1 /* HM-MOD-UART/HM-LGW-O-TW-W-EU driver
3 * Copyright (c) 2016-17 Michael Gernoth <michael@gernoth.net>
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:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
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
32 #include <sys/types.h>
38 #include "hmuartlgw.h"
40 #define HMUARTLGW_INIT_TIMEOUT 10000
42 #define HMUARTLGW_SETTLE_TIME 1
46 enum hmuartlgw_state
{
47 HMUARTLGW_QUERY_APPSTATE
,
48 HMUARTLGW_ENTER_BOOTLOADER
,
49 HMUARTLGW_ENTER_BOOTLOADER_ACK
,
51 HMUARTLGW_ENTER_APPLICATION
,
52 HMUARTLGW_ENTER_APPLICATION_ACK
,
53 HMUARTLGW_APPLICATION
,
54 HMUARTLGW_DUAL_APPLICATION
,
58 enum hmuartlgw_state state
;
59 struct hmuartlgw_dev
*dev
;
63 #define CRC16_POLY 0x8005
65 static uint16_t crc16(uint8_t* buf
, int length
)
67 uint16_t crc
= 0xd77f;
72 for (i
= 0; i
< 8; i
++) {
85 static int hmuartlgw_init_parse(enum hmuartlgw_dst dst
, uint8_t *buf
, int buf_len
, void *data
)
87 struct recv_data
*rdata
= data
;
91 printf("Length: %d\n", buf_len
);
92 hexdump(buf
, buf_len
, "INIT > ");
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
;
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
;
112 case HMUARTLGW_ENTER_BOOTLOADER
:
113 if ((buf_len
== 2) &&
116 rdata
->state
= HMUARTLGW_ENTER_BOOTLOADER_ACK
;
121 fprintf(stderr
, "Don't know how to handle this state (%d) for unsupported firmware, giving up!\n", rdata
->state
);
129 /* Re-send commands for DualCopro Firmware */
130 if (dst
== HMUARTLGW_DUAL_ERR
) {
131 uint8_t buf
[128] = { 0 };
133 switch(rdata
->state
) {
134 case HMUARTLGW_QUERY_APPSTATE
:
136 printf("Re-sending appstate-query for new firmare\n");
139 buf
[0] = HMUARTLGW_DUAL_GET_APP
;
140 hmuartlgw_send(rdata
->dev
, buf
, 1, HMUARTLGW_DUAL
);
142 case HMUARTLGW_ENTER_BOOTLOADER
:
144 printf("Re-sending switch to bootloader for new firmare\n");
147 buf
[0] = HMUARTLGW_DUAL_CHANGE_APP
;
148 hmuartlgw_send(rdata
->dev
, buf
, 1, HMUARTLGW_DUAL
);
151 fprintf(stderr
, "Don't know how to handle this error-state (%d) for unsupported firmware, giving up!\n", rdata
->state
);
159 if (dst
!= HMUARTLGW_OS
)
162 if ((buf_len
== 10) && (buf
[0] == 0x00) && !strncmp(((char*)buf
)+1, "Co_CPU_BL", 9)) {
163 rdata
->state
= HMUARTLGW_BOOTLOADER
;
167 if ((buf_len
== 11) && (buf
[0] == 0x00) && !strncmp(((char*)buf
)+1, "Co_CPU_App", 10)) {
168 rdata
->state
= HMUARTLGW_APPLICATION
;
172 switch(rdata
->state
) {
173 case HMUARTLGW_QUERY_APPSTATE
:
174 if ((buf
[0] == HMUARTLGW_OS_ACK
) && (buf
[1] == 0x02)) {
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
;
182 case HMUARTLGW_ENTER_BOOTLOADER
:
183 if ((buf_len
== 2) &&
184 (buf
[0] == HMUARTLGW_OS_ACK
) &&
186 rdata
->state
= HMUARTLGW_ENTER_BOOTLOADER_ACK
;
189 case HMUARTLGW_ENTER_BOOTLOADER_ACK
:
190 rdata
->state
= HMUARTLGW_ENTER_BOOTLOADER
;
192 case HMUARTLGW_ENTER_APPLICATION
:
193 if ((buf_len
== 2) &&
194 (buf
[0] == HMUARTLGW_OS_ACK
) &&
196 rdata
->state
= HMUARTLGW_ENTER_APPLICATION_ACK
;
199 case HMUARTLGW_ENTER_APPLICATION_ACK
:
200 rdata
->state
= HMUARTLGW_ENTER_APPLICATION
;
210 struct hmuartlgw_dev
*hmuart_init(char *device
, hmuartlgw_cb_fn cb
, void *data
, int app
)
212 struct hmuartlgw_dev
*dev
= NULL
;
213 struct termios oldtio
, tio
;
215 dev
= malloc(sizeof(struct hmuartlgw_dev
));
217 perror("malloc(struct hmuartlgw_dev)");
221 memset(dev
, 0, sizeof(struct hmuartlgw_dev
));
223 dev
->fd
= open(device
, O_RDWR
| O_NOCTTY
);
225 perror("open(hmuartlgw)");
230 fprintf(stderr
, "%s opened\n", device
);
233 if (tcgetattr(dev
->fd
, &oldtio
) == -1) {
238 memset(&tio
, 0, sizeof(tio
));
240 tio
.c_cflag
= B115200
| CS8
| CLOCAL
| CREAD
;
241 tio
.c_iflag
= IGNPAR
;
247 tcflush(dev
->fd
, TCIFLUSH
);
248 if (tcsetattr(dev
->fd
, TCSANOW
, &tio
) == -1) {
254 fprintf(stderr
, "serial parameters set\n");
257 hmuartlgw_flush(dev
);
260 hmuartlgw_enter_app(dev
);
262 hmuartlgw_enter_bootloader(dev
);
277 struct hmuartlgw_dev
*hmlgw_init(char *device
, hmuartlgw_cb_fn cb
, void *data
)
279 struct hmuartlgw_dev
*dev
= NULL
;
284 void hmuartlgw_enter_bootloader(struct hmuartlgw_dev
*dev
)
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 };
293 fprintf(stderr
, "Entering bootloader\n");
296 dev
->cb
= hmuartlgw_init_parse
;
297 dev
->cb_data
= &rdata
;
300 rdata
.state
= HMUARTLGW_QUERY_APPSTATE
;
301 buf
[0] = HMUARTLGW_OS_GET_APP
;
302 hmuartlgw_send(dev
, buf
, 1, HMUARTLGW_OS
);
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");
310 } while (rdata
.state
== HMUARTLGW_QUERY_APPSTATE
);
312 if (rdata
.state
!= HMUARTLGW_BOOTLOADER
) {
314 rdata
.state
= HMUARTLGW_ENTER_BOOTLOADER
;
315 buf
[0] = HMUARTLGW_OS_CHANGE_APP
;
316 hmuartlgw_send(dev
, buf
, 1, HMUARTLGW_OS
);
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");
324 } while (rdata
.state
!= HMUARTLGW_BOOTLOADER
);
326 printf("Waiting for bootloader to settle...\n");
327 sleep(HMUARTLGW_SETTLE_TIME
);
331 dev
->cb_data
= cb_data_old
;
334 void hmuartlgw_enter_app(struct hmuartlgw_dev
*dev
)
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 };
343 fprintf(stderr
, "Entering application\n");
346 dev
->cb
= hmuartlgw_init_parse
;
347 dev
->cb_data
= &rdata
;
350 rdata
.state
= HMUARTLGW_QUERY_APPSTATE
;
351 buf
[0] = HMUARTLGW_OS_GET_APP
;
352 hmuartlgw_send(dev
, buf
, 1, HMUARTLGW_OS
);
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");
360 } while (rdata
.state
== HMUARTLGW_QUERY_APPSTATE
);
362 if ((rdata
.state
!= HMUARTLGW_APPLICATION
) &&
363 (rdata
.state
!= HMUARTLGW_DUAL_APPLICATION
)) {
365 rdata
.state
= HMUARTLGW_ENTER_APPLICATION
;
366 buf
[0] = HMUARTLGW_OS_CHANGE_APP
;
367 hmuartlgw_send(dev
, buf
, 1, HMUARTLGW_OS
);
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");
375 } while ((rdata
.state
!= HMUARTLGW_APPLICATION
) &&
376 (rdata
.state
!= HMUARTLGW_DUAL_APPLICATION
));
378 if (rdata
.state
== HMUARTLGW_APPLICATION
) {
379 printf("Waiting for application to settle...\n");
380 sleep(HMUARTLGW_SETTLE_TIME
);
384 if (rdata
.state
== HMUARTLGW_DUAL_APPLICATION
) {
385 fprintf(stderr
, "Unsupported firmware, please install HM-only firmware!\n");
391 dev
->cb_data
= cb_data_old
;
394 static int hmuartlgw_escape(uint8_t *frame
, int framelen
)
398 for (i
= 1; i
< framelen
; i
++) {
399 if (frame
[i
] == 0xfc || frame
[i
] == 0xfd) {
400 memmove(frame
+ i
+ 1, frame
+ i
, framelen
- i
);
409 int hmuartlgw_send_raw(struct hmuartlgw_dev
*dev
, uint8_t *frame
, int framelen
)
415 hexdump(frame
, framelen
, "UARTLGW < ");
418 framelen
= hmuartlgw_escape(frame
, framelen
);
421 ret
= write(dev
->fd
, frame
+ w
, framelen
- w
);
427 } while (w
< framelen
);
432 int hmuartlgw_send(struct hmuartlgw_dev
*dev
, uint8_t *cmd
, int cmdlen
, enum hmuartlgw_dst dst
)
434 static uint8_t cnt
= 0;
435 uint8_t frame
[4096] = { 0 };
439 frame
[1] = ((cmdlen
+ 2) >> 8) & 0xff;
440 frame
[2] = (cmdlen
+ 2) & 0xff;
442 dev
->last_send_cnt
= 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;
449 return hmuartlgw_send_raw(dev
, frame
, cmdlen
+ 7);
452 int hmuartlgw_poll(struct hmuartlgw_dev
*dev
, int timeout
)
454 struct pollfd pfds
[1];
461 memset(pfds
, 0, sizeof(struct pollfd
) * 1);
463 pfds
[0].fd
= dev
->fd
;
464 pfds
[0].events
= POLLIN
;
466 ret
= poll(pfds
, 1, timeout
);
476 if (!(pfds
[0].revents
& POLLIN
)) {
481 r
= read(dev
->fd
, dev
->buf
+dev
->pos
, 1);
492 if (dev
->buf
[0] != 0xfd) {
493 memset(dev
->buf
, 0, sizeof(dev
->buf
));
495 dev
->unescape_next
= 0;
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;
511 len
= ((dev
->buf
[1] << 8) & 0xff00) | (dev
->buf
[2] & 0xff);
513 if (dev
->pos
< len
+ 5)
519 crc
= crc16(dev
->buf
, dev
->pos
);
522 hexdump(dev
->buf
, dev
->pos
, "UARTLGW > ");
524 dev
->cb(dev
->buf
[3], dev
->buf
+ 5 , dev
->pos
- 7, dev
->cb_data
);
526 memset(dev
->buf
, 0, sizeof(dev
->buf
));
528 dev
->unescape_next
= 0;
530 fprintf(stderr
, "Invalid checksum received!\n");
531 hexdump(dev
->buf
, dev
->pos
, "ERR> ");
532 printf("calculated: %04x\n", crc
);
534 memset(dev
->buf
, 0, sizeof(dev
->buf
));
536 dev
->unescape_next
= 0;
543 void hmuartlgw_close(struct hmuartlgw_dev
*dev
)
548 void hmuartlgw_flush(struct hmuartlgw_dev
*dev
)
550 struct pollfd pfds
[1];
555 tcflush(dev
->fd
, TCIOFLUSH
);
558 memset(pfds
, 0, sizeof(struct pollfd
) * 1);
560 pfds
[0].fd
= dev
->fd
;
561 pfds
[0].events
= POLLIN
;
563 ret
= poll(pfds
, 1, 100);
567 if (!(pfds
[0].revents
& POLLIN
))
570 memset(buf
, 0, sizeof(buf
));
571 r
= read(dev
->fd
, buf
, sizeof(buf
));
579 void hmuartlgw_set_debug(int d
)