]> cvs.zerfleddert.de Git - hmcfgusb/blame - hmuartlgw.c
hmland.init: try hmland-location from Fhem-wiki, too
[hmcfgusb] / hmuartlgw.c
CommitLineData
3e34d2ce
MG
1/* HM-MOD-UART/HM-LGW-O-TW-W-EU driver
2 *
70faa273 3 * Copyright (c) 2016-17 Michael Gernoth <michael@gernoth.net>
3e34d2ce
MG
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24#include <errno.h>
25#include <fcntl.h>
26#include <inttypes.h>
27#include <poll.h>
28#include <stdlib.h>
29#include <stdio.h>
30#include <string.h>
31#include <strings.h>
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <termios.h>
35#include <unistd.h>
36
37#include "hexdump.h"
38#include "hmuartlgw.h"
39
40#define HMUARTLGW_INIT_TIMEOUT 10000
41
9248b988 42#define HMUARTLGW_SETTLE_TIME 1
3e34d2ce
MG
43
44static int debug = 0;
45
46enum hmuartlgw_state {
47 HMUARTLGW_QUERY_APPSTATE,
48 HMUARTLGW_ENTER_BOOTLOADER,
49 HMUARTLGW_ENTER_BOOTLOADER_ACK,
50 HMUARTLGW_BOOTLOADER,
51 HMUARTLGW_ENTER_APPLICATION,
52 HMUARTLGW_ENTER_APPLICATION_ACK,
53 HMUARTLGW_APPLICATION,
70faa273 54 HMUARTLGW_DUAL_APPLICATION,
3e34d2ce
MG
55};
56
57struct recv_data {
58 enum hmuartlgw_state state;
70faa273 59 struct hmuartlgw_dev *dev;
3e34d2ce
MG
60};
61
62
63#define CRC16_POLY 0x8005
64
65static uint16_t crc16(uint8_t* buf, int length)
66{
67 uint16_t crc = 0xd77f;
68 int i;
69
70 while (length--) {
71 crc ^= *buf++ << 8;
72 for (i = 0; i < 8; i++) {
73 if (crc & 0x8000) {
74 crc <<= 1;
75 crc ^= CRC16_POLY;
76 } else {
77 crc <<= 1;
78 }
79 }
80 }
81
82 return crc;
83}
84
85static int hmuartlgw_init_parse(enum hmuartlgw_dst dst, uint8_t *buf, int buf_len, void *data)
86{
87 struct recv_data *rdata = data;
88
89#if 0
90 if (debug) {
91 printf("Length: %d\n", buf_len);
92 hexdump(buf, buf_len, "INIT > ");
93 }
94#endif
95
70faa273
MG
96 /* Minimally handle DualCopro-Firmware */
97 if (dst == HMUARTLGW_DUAL) {
98 if ((buf_len == 14) && (buf[0] == 0x00) && !strncmp(((char*)buf)+1, "DualCoPro_App", 13)) {
99 rdata->state = HMUARTLGW_DUAL_APPLICATION;
100 return 1;
101 }
102
103 switch (rdata->state) {
104 case HMUARTLGW_QUERY_APPSTATE:
105 if ((buf[0] == 0x05) && (buf[1] == 0x01)) {
106 if (!strncmp(((char*)buf)+2, "DualCoPro_App", 13)) {
107 rdata->state = HMUARTLGW_DUAL_APPLICATION;
108 return 1;
109 }
110 }
111 break;
112 case HMUARTLGW_ENTER_BOOTLOADER:
113 if ((buf_len == 2) &&
114 (buf[0] == 0x05) &&
115 (buf[1] == 0x01)) {
116 rdata->state = HMUARTLGW_ENTER_BOOTLOADER_ACK;
117 return 1;
118 }
119 break;
120 default:
121 fprintf(stderr, "Don't know how to handle this state (%d) for unsupported firmware, giving up!\n", rdata->state);
122 exit(1);
123 break;
124 }
125
126 return 0;
127 }
128
129 /* Re-send commands for DualCopro Firmware */
130 if (dst == HMUARTLGW_DUAL_ERR) {
131 uint8_t buf[128] = { 0 };
132
133 switch(rdata->state) {
134 case HMUARTLGW_QUERY_APPSTATE:
135 if (debug) {
136 printf("Re-sending appstate-query for new firmare\n");
137 }
138
139 buf[0] = HMUARTLGW_DUAL_GET_APP;
140 hmuartlgw_send(rdata->dev, buf, 1, HMUARTLGW_DUAL);
141 break;
142 case HMUARTLGW_ENTER_BOOTLOADER:
143 if (debug) {
144 printf("Re-sending switch to bootloader for new firmare\n");
145 }
146
147 buf[0] = HMUARTLGW_DUAL_CHANGE_APP;
148 hmuartlgw_send(rdata->dev, buf, 1, HMUARTLGW_DUAL);
149 break;
150 default:
151 fprintf(stderr, "Don't know how to handle this error-state (%d) for unsupported firmware, giving up!\n", rdata->state);
152 exit(1);
153 break;
154 }
155
156 return 0;
157 }
158
3e34d2ce
MG
159 if (dst != HMUARTLGW_OS)
160 return 0;
161
162 if ((buf_len == 10) && (buf[0] == 0x00) && !strncmp(((char*)buf)+1, "Co_CPU_BL", 9)) {
163 rdata->state = HMUARTLGW_BOOTLOADER;
164 return 1;
165 }
166
167 if ((buf_len == 11) && (buf[0] == 0x00) && !strncmp(((char*)buf)+1, "Co_CPU_App", 10)) {
168 rdata->state = HMUARTLGW_APPLICATION;
169 return 1;
170 }
171
172 switch(rdata->state) {
173 case HMUARTLGW_QUERY_APPSTATE:
9248b988 174 if ((buf[0] == HMUARTLGW_OS_ACK) && (buf[1] == 0x02)) {
3e34d2ce
MG
175 if (!strncmp(((char*)buf)+2, "Co_CPU_BL", 9)) {
176 rdata->state = HMUARTLGW_BOOTLOADER;
177 } else if (!strncmp(((char*)buf)+2, "Co_CPU_App", 10)) {
178 rdata->state = HMUARTLGW_APPLICATION;
179 }
180 }
181 break;
182 case HMUARTLGW_ENTER_BOOTLOADER:
183 if ((buf_len == 2) &&
9248b988 184 (buf[0] == HMUARTLGW_OS_ACK) &&
3e34d2ce
MG
185 (buf[1] == 0x01)) {
186 rdata->state = HMUARTLGW_ENTER_BOOTLOADER_ACK;
187 }
188 break;
189 case HMUARTLGW_ENTER_BOOTLOADER_ACK:
190 rdata->state = HMUARTLGW_ENTER_BOOTLOADER;
191 break;
192 case HMUARTLGW_ENTER_APPLICATION:
193 if ((buf_len == 2) &&
9248b988 194 (buf[0] == HMUARTLGW_OS_ACK) &&
3e34d2ce
MG
195 (buf[1] == 0x01)) {
196 rdata->state = HMUARTLGW_ENTER_APPLICATION_ACK;
197 }
198 break;
199 case HMUARTLGW_ENTER_APPLICATION_ACK:
200 rdata->state = HMUARTLGW_ENTER_APPLICATION;
201 break;
202 default:
203 return 0;
204 break;
205 }
206
207 return 1;
208}
209
853cbce9 210struct hmuartlgw_dev *hmuart_init(char *device, hmuartlgw_cb_fn cb, void *data, int app)
3e34d2ce
MG
211{
212 struct hmuartlgw_dev *dev = NULL;
213 struct termios oldtio, tio;
214
215 dev = malloc(sizeof(struct hmuartlgw_dev));
216 if (dev == NULL) {
217 perror("malloc(struct hmuartlgw_dev)");
218 return NULL;
219 }
220
221 memset(dev, 0, sizeof(struct hmuartlgw_dev));
222
223 dev->fd = open(device, O_RDWR | O_NOCTTY);
224 if (dev->fd < 0) {
225 perror("open(hmuartlgw)");
226 goto out;
227 }
228
229 if (debug) {
230 fprintf(stderr, "%s opened\n", device);
231 }
232
233 if (tcgetattr(dev->fd, &oldtio) == -1) {
234 perror("tcgetattr");
235 goto out2;
236 }
237
238 memset(&tio, 0, sizeof(tio));
239
240 tio.c_cflag = B115200 | CS8 | CLOCAL | CREAD;
241 tio.c_iflag = IGNPAR;
242 tio.c_oflag = 0;
243 tio.c_lflag = 0;
244 tio.c_cc[VTIME] = 0;
245 tio.c_cc[VMIN] = 1;
246
247 tcflush(dev->fd, TCIFLUSH);
248 if (tcsetattr(dev->fd, TCSANOW, &tio) == -1) {
249 perror("tcsetattr");
250 goto out2;
251 }
252
253 if (debug) {
254 fprintf(stderr, "serial parameters set\n");
255 }
256
257 hmuartlgw_flush(dev);
258
853cbce9
MG
259 if (app) {
260 hmuartlgw_enter_app(dev);
261 } else {
262 hmuartlgw_enter_bootloader(dev);
263 }
3e34d2ce
MG
264
265 dev->cb = cb;
266 dev->cb_data = data;
267
268 return dev;
269
270out2:
271 close(dev->fd);
272out:
273 free(dev);
274 return NULL;
275}
276
277struct hmuartlgw_dev *hmlgw_init(char *device, hmuartlgw_cb_fn cb, void *data)
278{
279 struct hmuartlgw_dev *dev = NULL;
280
281 return dev;
282}
283
284void hmuartlgw_enter_bootloader(struct hmuartlgw_dev *dev)
285{
286 hmuartlgw_cb_fn cb_old = dev->cb;
287 void *cb_data_old = dev->cb_data;
288 struct recv_data rdata = { 0 };
289 uint8_t buf[128] = { 0 };
8eb01624 290 int ret;
3e34d2ce
MG
291
292 if (debug) {
293 fprintf(stderr, "Entering bootloader\n");
294 }
295
3e34d2ce
MG
296 dev->cb = hmuartlgw_init_parse;
297 dev->cb_data = &rdata;
298
70faa273 299 rdata.dev = dev;
3e34d2ce
MG
300 rdata.state = HMUARTLGW_QUERY_APPSTATE;
301 buf[0] = HMUARTLGW_OS_GET_APP;
302 hmuartlgw_send(dev, buf, 1, HMUARTLGW_OS);
303 do {
8eb01624
MG
304 errno = 0;
305 ret = hmuartlgw_poll(dev, HMUARTLGW_INIT_TIMEOUT);
306 if (ret == -1 && errno == ETIMEDOUT) {
307 fprintf(stderr, "Communication with the module timed out, is the serial port configured correctly?\n");
308 exit(1);
309 }
3e34d2ce
MG
310 } while (rdata.state == HMUARTLGW_QUERY_APPSTATE);
311
312 if (rdata.state != HMUARTLGW_BOOTLOADER) {
70faa273 313 rdata.dev = dev;
3e34d2ce
MG
314 rdata.state = HMUARTLGW_ENTER_BOOTLOADER;
315 buf[0] = HMUARTLGW_OS_CHANGE_APP;
316 hmuartlgw_send(dev, buf, 1, HMUARTLGW_OS);
317 do {
8eb01624
MG
318 errno = 0;
319 ret = hmuartlgw_poll(dev, HMUARTLGW_INIT_TIMEOUT);
320 if (ret == -1 && errno == ETIMEDOUT) {
321 fprintf(stderr, "Communication with the module timed out, is the serial port configured correctly?\n");
322 exit(1);
323 }
3e34d2ce
MG
324 } while (rdata.state != HMUARTLGW_BOOTLOADER);
325
326 printf("Waiting for bootloader to settle...\n");
327 sleep(HMUARTLGW_SETTLE_TIME);
328 }
329
330 dev->cb = cb_old;
331 dev->cb_data = cb_data_old;
332}
333
334void hmuartlgw_enter_app(struct hmuartlgw_dev *dev)
335{
336 hmuartlgw_cb_fn cb_old = dev->cb;
337 void *cb_data_old = dev->cb_data;
338 struct recv_data rdata = { 0 };
339 uint8_t buf[128] = { 0 };
8eb01624 340 int ret;
3e34d2ce
MG
341
342 if (debug) {
343 fprintf(stderr, "Entering application\n");
344 }
345
346 dev->cb = hmuartlgw_init_parse;
347 dev->cb_data = &rdata;
348
70faa273 349 rdata.dev = dev;
3e34d2ce
MG
350 rdata.state = HMUARTLGW_QUERY_APPSTATE;
351 buf[0] = HMUARTLGW_OS_GET_APP;
352 hmuartlgw_send(dev, buf, 1, HMUARTLGW_OS);
353 do {
8eb01624
MG
354 errno = 0;
355 ret = hmuartlgw_poll(dev, HMUARTLGW_INIT_TIMEOUT);
356 if (ret == -1 && errno == ETIMEDOUT) {
357 fprintf(stderr, "Communication with the module timed out, is the serial port configured correctly?\n");
358 exit(1);
359 }
3e34d2ce
MG
360 } while (rdata.state == HMUARTLGW_QUERY_APPSTATE);
361
70faa273
MG
362 if ((rdata.state != HMUARTLGW_APPLICATION) &&
363 (rdata.state != HMUARTLGW_DUAL_APPLICATION)) {
364 rdata.dev = dev;
3e34d2ce
MG
365 rdata.state = HMUARTLGW_ENTER_APPLICATION;
366 buf[0] = HMUARTLGW_OS_CHANGE_APP;
367 hmuartlgw_send(dev, buf, 1, HMUARTLGW_OS);
368 do {
8eb01624
MG
369 errno = 0;
370 ret = hmuartlgw_poll(dev, HMUARTLGW_INIT_TIMEOUT);
371 if (ret == -1 && errno == ETIMEDOUT) {
372 fprintf(stderr, "Communication with the module timed out, is the serial port configured correctly?\n");
373 exit(1);
374 }
70faa273
MG
375 } while ((rdata.state != HMUARTLGW_APPLICATION) &&
376 (rdata.state != HMUARTLGW_DUAL_APPLICATION));
3e34d2ce 377
70faa273
MG
378 if (rdata.state == HMUARTLGW_APPLICATION) {
379 printf("Waiting for application to settle...\n");
380 sleep(HMUARTLGW_SETTLE_TIME);
381 }
3e34d2ce
MG
382 }
383
70faa273
MG
384 if (rdata.state == HMUARTLGW_DUAL_APPLICATION) {
385 fprintf(stderr, "Unsupported firmware, please install HM-only firmware!\n");
386 exit(1);
387 }
388
389
3e34d2ce
MG
390 dev->cb = cb_old;
391 dev->cb_data = cb_data_old;
392}
393
394static int hmuartlgw_escape(uint8_t *frame, int framelen)
395{
396 int i;
397
398 for (i = 1; i < framelen; i++) {
399 if (frame[i] == 0xfc || frame[i] == 0xfd) {
400 memmove(frame + i + 1, frame + i, framelen - i);
401 frame[i++] = 0xfc;
402 frame[i] &= 0x7f;
403 framelen++;
404 }
405 }
406 return framelen;
407}
408
409int hmuartlgw_send_raw(struct hmuartlgw_dev *dev, uint8_t *frame, int framelen)
410{
411 int w = 0;
412 int ret;
413
414 if (debug) {
415 hexdump(frame, framelen, "UARTLGW < ");
416 }
417
418 framelen = hmuartlgw_escape(frame, framelen);
419
420 do {
421 ret = write(dev->fd, frame + w, framelen - w);
422 if (ret < 0) {
423 perror("write");
424 return 0;
425 }
426 w += ret;
427 } while (w < framelen);
428
429 return 1;
430}
431
432int hmuartlgw_send(struct hmuartlgw_dev *dev, uint8_t *cmd, int cmdlen, enum hmuartlgw_dst dst)
433{
434 static uint8_t cnt = 0;
853cbce9 435 uint8_t frame[4096] = { 0 };
3e34d2ce
MG
436 uint16_t crc;
437
438 frame[0] = 0xfd;
439 frame[1] = ((cmdlen + 2) >> 8) & 0xff;
440 frame[2] = (cmdlen + 2) & 0xff;
441 frame[3] = dst;
442 dev->last_send_cnt = cnt;
443 frame[4] = cnt++;
444 memcpy(&(frame[5]), cmd, cmdlen);
445 crc = crc16(frame, cmdlen + 5);
446 frame[cmdlen + 5] = (crc >> 8) & 0xff;
447 frame[cmdlen + 6] = crc & 0xff;
448
449 return hmuartlgw_send_raw(dev, frame, cmdlen + 7);
450}
451
452int hmuartlgw_poll(struct hmuartlgw_dev *dev, int timeout)
453{
454 struct pollfd pfds[1];
455 int ret;
456 int r = 0;
457 uint16_t crc;
458
459 errno = 0;
460
461 memset(pfds, 0, sizeof(struct pollfd) * 1);
462
463 pfds[0].fd = dev->fd;
464 pfds[0].events = POLLIN;
465
466 ret = poll(pfds, 1, timeout);
467 if (ret == -1)
468 return -1;
469
470 errno = 0;
471 if (ret == 0) {
472 errno = ETIMEDOUT;
473 return -1;
474 }
475
476 if (!(pfds[0].revents & POLLIN)) {
477 errno = EIO;
478 return -1;
479 }
480
481 r = read(dev->fd, dev->buf+dev->pos, 1);
482 if (r < 0)
483 return -1;
484
485 if (r == 0) {
486 errno = EOF;
487 return -1;
488 }
489
490 dev->pos += r;
491
492 if (dev->buf[0] != 0xfd) {
493 memset(dev->buf, 0, sizeof(dev->buf));
494 dev->pos = 0;
495 dev->unescape_next = 0;
496 return -1;
497 }
498
499 if (dev->unescape_next) {
500 dev->buf[dev->pos-1] |= 0x80;
501 dev->unescape_next = 0;
502 } else if (dev->buf[dev->pos-1] == 0xfc) {
503 dev->unescape_next = 1;
504 dev->pos--;
505 return -1;
506 }
507
508 if (dev->pos >= 3) {
509 uint16_t len;
510
511 len = ((dev->buf[1] << 8) & 0xff00) | (dev->buf[2] & 0xff);
512
513 if (dev->pos < len + 5)
514 return -1;
515 } else {
516 return -1;
517 }
518
b1d2ccc2
MG
519 crc = crc16(dev->buf, dev->pos);
520 if (crc == 0x0000) {
3e34d2ce
MG
521 if (debug)
522 hexdump(dev->buf, dev->pos, "UARTLGW > ");
523
524 dev->cb(dev->buf[3], dev->buf + 5 , dev->pos - 7, dev->cb_data);
525
526 memset(dev->buf, 0, sizeof(dev->buf));
527 dev->pos = 0;
528 dev->unescape_next = 0;
529 } else {
530 fprintf(stderr, "Invalid checksum received!\n");
531 hexdump(dev->buf, dev->pos, "ERR> ");
532 printf("calculated: %04x\n", crc);
533
534 memset(dev->buf, 0, sizeof(dev->buf));
535 dev->pos = 0;
536 dev->unescape_next = 0;
537 }
538
539 errno = 0;
540 return -1;
541}
542
543void hmuartlgw_close(struct hmuartlgw_dev *dev)
544{
545 close(dev->fd);
546}
547
548void hmuartlgw_flush(struct hmuartlgw_dev *dev)
549{
550 struct pollfd pfds[1];
551 int ret;
552 int r = 0;
553 uint8_t buf[1024];
554
555 tcflush(dev->fd, TCIOFLUSH);
556
557 while(1) {
558 memset(pfds, 0, sizeof(struct pollfd) * 1);
559
560 pfds[0].fd = dev->fd;
561 pfds[0].events = POLLIN;
562
563 ret = poll(pfds, 1, 100);
564 if (ret <= 0)
565 break;
566
567 if (!(pfds[0].revents & POLLIN))
568 break;
569
570 memset(buf, 0, sizeof(buf));
571 r = read(dev->fd, buf, sizeof(buf));
572 if (r <= 0)
573 break;
574 }
575
576 return;
577}
578
579void hmuartlgw_set_debug(int d)
580{
581 debug = d;
582}
Impressum, Datenschutz