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 };
292 fprintf(stderr
, "Entering bootloader\n");
295 dev
->cb
= hmuartlgw_init_parse
;
296 dev
->cb_data
= &rdata
;
299 rdata
.state
= HMUARTLGW_QUERY_APPSTATE
;
300 buf
[0] = HMUARTLGW_OS_GET_APP
;
301 hmuartlgw_send(dev
, buf
, 1, HMUARTLGW_OS
);
303 hmuartlgw_poll(dev
, HMUARTLGW_INIT_TIMEOUT
);
304 } while (rdata
.state
== HMUARTLGW_QUERY_APPSTATE
);
306 if (rdata
.state
!= HMUARTLGW_BOOTLOADER
) {
308 rdata
.state
= HMUARTLGW_ENTER_BOOTLOADER
;
309 buf
[0] = HMUARTLGW_OS_CHANGE_APP
;
310 hmuartlgw_send(dev
, buf
, 1, HMUARTLGW_OS
);
312 hmuartlgw_poll(dev
, HMUARTLGW_INIT_TIMEOUT
);
313 } while (rdata
.state
!= HMUARTLGW_BOOTLOADER
);
315 printf("Waiting for bootloader to settle...\n");
316 sleep(HMUARTLGW_SETTLE_TIME
);
320 dev
->cb_data
= cb_data_old
;
323 void hmuartlgw_enter_app(struct hmuartlgw_dev
*dev
)
325 hmuartlgw_cb_fn cb_old
= dev
->cb
;
326 void *cb_data_old
= dev
->cb_data
;
327 struct recv_data rdata
= { 0 };
328 uint8_t buf
[128] = { 0 };
331 fprintf(stderr
, "Entering application\n");
334 dev
->cb
= hmuartlgw_init_parse
;
335 dev
->cb_data
= &rdata
;
338 rdata
.state
= HMUARTLGW_QUERY_APPSTATE
;
339 buf
[0] = HMUARTLGW_OS_GET_APP
;
340 hmuartlgw_send(dev
, buf
, 1, HMUARTLGW_OS
);
342 hmuartlgw_poll(dev
, HMUARTLGW_INIT_TIMEOUT
);
343 } while (rdata
.state
== HMUARTLGW_QUERY_APPSTATE
);
345 if ((rdata
.state
!= HMUARTLGW_APPLICATION
) &&
346 (rdata
.state
!= HMUARTLGW_DUAL_APPLICATION
)) {
348 rdata
.state
= HMUARTLGW_ENTER_APPLICATION
;
349 buf
[0] = HMUARTLGW_OS_CHANGE_APP
;
350 hmuartlgw_send(dev
, buf
, 1, HMUARTLGW_OS
);
352 hmuartlgw_poll(dev
, HMUARTLGW_INIT_TIMEOUT
);
353 } while ((rdata
.state
!= HMUARTLGW_APPLICATION
) &&
354 (rdata
.state
!= HMUARTLGW_DUAL_APPLICATION
));
356 if (rdata
.state
== HMUARTLGW_APPLICATION
) {
357 printf("Waiting for application to settle...\n");
358 sleep(HMUARTLGW_SETTLE_TIME
);
362 if (rdata
.state
== HMUARTLGW_DUAL_APPLICATION
) {
363 fprintf(stderr
, "Unsupported firmware, please install HM-only firmware!\n");
369 dev
->cb_data
= cb_data_old
;
372 static int hmuartlgw_escape(uint8_t *frame
, int framelen
)
376 for (i
= 1; i
< framelen
; i
++) {
377 if (frame
[i
] == 0xfc || frame
[i
] == 0xfd) {
378 memmove(frame
+ i
+ 1, frame
+ i
, framelen
- i
);
387 int hmuartlgw_send_raw(struct hmuartlgw_dev
*dev
, uint8_t *frame
, int framelen
)
393 hexdump(frame
, framelen
, "UARTLGW < ");
396 framelen
= hmuartlgw_escape(frame
, framelen
);
399 ret
= write(dev
->fd
, frame
+ w
, framelen
- w
);
405 } while (w
< framelen
);
410 int hmuartlgw_send(struct hmuartlgw_dev
*dev
, uint8_t *cmd
, int cmdlen
, enum hmuartlgw_dst dst
)
412 static uint8_t cnt
= 0;
413 uint8_t frame
[4096] = { 0 };
417 frame
[1] = ((cmdlen
+ 2) >> 8) & 0xff;
418 frame
[2] = (cmdlen
+ 2) & 0xff;
420 dev
->last_send_cnt
= cnt
;
422 memcpy(&(frame
[5]), cmd
, cmdlen
);
423 crc
= crc16(frame
, cmdlen
+ 5);
424 frame
[cmdlen
+ 5] = (crc
>> 8) & 0xff;
425 frame
[cmdlen
+ 6] = crc
& 0xff;
427 return hmuartlgw_send_raw(dev
, frame
, cmdlen
+ 7);
430 int hmuartlgw_poll(struct hmuartlgw_dev
*dev
, int timeout
)
432 struct pollfd pfds
[1];
439 memset(pfds
, 0, sizeof(struct pollfd
) * 1);
441 pfds
[0].fd
= dev
->fd
;
442 pfds
[0].events
= POLLIN
;
444 ret
= poll(pfds
, 1, timeout
);
454 if (!(pfds
[0].revents
& POLLIN
)) {
459 r
= read(dev
->fd
, dev
->buf
+dev
->pos
, 1);
470 if (dev
->buf
[0] != 0xfd) {
471 memset(dev
->buf
, 0, sizeof(dev
->buf
));
473 dev
->unescape_next
= 0;
477 if (dev
->unescape_next
) {
478 dev
->buf
[dev
->pos
-1] |= 0x80;
479 dev
->unescape_next
= 0;
480 } else if (dev
->buf
[dev
->pos
-1] == 0xfc) {
481 dev
->unescape_next
= 1;
489 len
= ((dev
->buf
[1] << 8) & 0xff00) | (dev
->buf
[2] & 0xff);
491 if (dev
->pos
< len
+ 5)
497 crc
= crc16(dev
->buf
, dev
->pos
);
500 hexdump(dev
->buf
, dev
->pos
, "UARTLGW > ");
502 dev
->cb(dev
->buf
[3], dev
->buf
+ 5 , dev
->pos
- 7, dev
->cb_data
);
504 memset(dev
->buf
, 0, sizeof(dev
->buf
));
506 dev
->unescape_next
= 0;
508 fprintf(stderr
, "Invalid checksum received!\n");
509 hexdump(dev
->buf
, dev
->pos
, "ERR> ");
510 printf("calculated: %04x\n", crc
);
512 memset(dev
->buf
, 0, sizeof(dev
->buf
));
514 dev
->unescape_next
= 0;
521 void hmuartlgw_close(struct hmuartlgw_dev
*dev
)
526 void hmuartlgw_flush(struct hmuartlgw_dev
*dev
)
528 struct pollfd pfds
[1];
533 tcflush(dev
->fd
, TCIOFLUSH
);
536 memset(pfds
, 0, sizeof(struct pollfd
) * 1);
538 pfds
[0].fd
= dev
->fd
;
539 pfds
[0].events
= POLLIN
;
541 ret
= poll(pfds
, 1, 100);
545 if (!(pfds
[0].revents
& POLLIN
))
548 memset(buf
, 0, sizeof(buf
));
549 r
= read(dev
->fd
, buf
, sizeof(buf
));
557 void hmuartlgw_set_debug(int d
)