1 /* HM-MOD-UART/HM-LGW-O-TW-W-EU driver
3 * Copyright (c) 2016 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 2
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
,
57 enum hmuartlgw_state state
;
61 #define CRC16_POLY 0x8005
63 static uint16_t crc16(uint8_t* buf
, int length
)
65 uint16_t crc
= 0xd77f;
70 for (i
= 0; i
< 8; i
++) {
83 static int hmuartlgw_init_parse(enum hmuartlgw_dst dst
, uint8_t *buf
, int buf_len
, void *data
)
85 struct recv_data
*rdata
= data
;
89 printf("Length: %d\n", buf_len
);
90 hexdump(buf
, buf_len
, "INIT > ");
94 if (dst
!= HMUARTLGW_OS
)
97 if ((buf_len
== 10) && (buf
[0] == 0x00) && !strncmp(((char*)buf
)+1, "Co_CPU_BL", 9)) {
98 rdata
->state
= HMUARTLGW_BOOTLOADER
;
102 if ((buf_len
== 11) && (buf
[0] == 0x00) && !strncmp(((char*)buf
)+1, "Co_CPU_App", 10)) {
103 rdata
->state
= HMUARTLGW_APPLICATION
;
107 switch(rdata
->state
) {
108 case HMUARTLGW_QUERY_APPSTATE
:
109 if ((buf
[0] == 0x04) && (buf
[1] == 0x02)) {
110 if (!strncmp(((char*)buf
)+2, "Co_CPU_BL", 9)) {
111 rdata
->state
= HMUARTLGW_BOOTLOADER
;
112 } else if (!strncmp(((char*)buf
)+2, "Co_CPU_App", 10)) {
113 rdata
->state
= HMUARTLGW_APPLICATION
;
117 case HMUARTLGW_ENTER_BOOTLOADER
:
118 if ((buf_len
== 2) &&
121 rdata
->state
= HMUARTLGW_ENTER_BOOTLOADER_ACK
;
124 case HMUARTLGW_ENTER_BOOTLOADER_ACK
:
125 rdata
->state
= HMUARTLGW_ENTER_BOOTLOADER
;
127 case HMUARTLGW_ENTER_APPLICATION
:
128 if ((buf_len
== 2) &&
131 rdata
->state
= HMUARTLGW_ENTER_APPLICATION_ACK
;
134 case HMUARTLGW_ENTER_APPLICATION_ACK
:
135 rdata
->state
= HMUARTLGW_ENTER_APPLICATION
;
145 struct hmuartlgw_dev
*hmuart_init(char *device
, hmuartlgw_cb_fn cb
, void *data
)
147 struct hmuartlgw_dev
*dev
= NULL
;
148 struct termios oldtio
, tio
;
150 dev
= malloc(sizeof(struct hmuartlgw_dev
));
152 perror("malloc(struct hmuartlgw_dev)");
156 memset(dev
, 0, sizeof(struct hmuartlgw_dev
));
158 dev
->fd
= open(device
, O_RDWR
| O_NOCTTY
);
160 perror("open(hmuartlgw)");
165 fprintf(stderr
, "%s opened\n", device
);
168 if (tcgetattr(dev
->fd
, &oldtio
) == -1) {
173 memset(&tio
, 0, sizeof(tio
));
175 tio
.c_cflag
= B115200
| CS8
| CLOCAL
| CREAD
;
176 tio
.c_iflag
= IGNPAR
;
182 tcflush(dev
->fd
, TCIFLUSH
);
183 if (tcsetattr(dev
->fd
, TCSANOW
, &tio
) == -1) {
189 fprintf(stderr
, "serial parameters set\n");
192 hmuartlgw_flush(dev
);
194 hmuartlgw_enter_app(dev
);
208 struct hmuartlgw_dev
*hmlgw_init(char *device
, hmuartlgw_cb_fn cb
, void *data
)
210 struct hmuartlgw_dev
*dev
= NULL
;
215 void hmuartlgw_enter_bootloader(struct hmuartlgw_dev
*dev
)
217 hmuartlgw_cb_fn cb_old
= dev
->cb
;
218 void *cb_data_old
= dev
->cb_data
;
219 struct recv_data rdata
= { 0 };
220 uint8_t buf
[128] = { 0 };
223 fprintf(stderr
, "Entering bootloader\n");
226 buf
[0] = HMUARTLGW_OS_CHANGE_APP
;
228 dev
->cb
= hmuartlgw_init_parse
;
229 dev
->cb_data
= &rdata
;
231 rdata
.state
= HMUARTLGW_QUERY_APPSTATE
;
232 buf
[0] = HMUARTLGW_OS_GET_APP
;
233 hmuartlgw_send(dev
, buf
, 1, HMUARTLGW_OS
);
235 hmuartlgw_poll(dev
, HMUARTLGW_INIT_TIMEOUT
);
236 } while (rdata
.state
== HMUARTLGW_QUERY_APPSTATE
);
238 if (rdata
.state
!= HMUARTLGW_BOOTLOADER
) {
239 rdata
.state
= HMUARTLGW_ENTER_BOOTLOADER
;
240 buf
[0] = HMUARTLGW_OS_CHANGE_APP
;
241 hmuartlgw_send(dev
, buf
, 1, HMUARTLGW_OS
);
243 hmuartlgw_poll(dev
, HMUARTLGW_INIT_TIMEOUT
);
244 } while (rdata
.state
!= HMUARTLGW_BOOTLOADER
);
246 printf("Waiting for bootloader to settle...\n");
247 sleep(HMUARTLGW_SETTLE_TIME
);
251 dev
->cb_data
= cb_data_old
;
254 void hmuartlgw_enter_app(struct hmuartlgw_dev
*dev
)
256 hmuartlgw_cb_fn cb_old
= dev
->cb
;
257 void *cb_data_old
= dev
->cb_data
;
258 struct recv_data rdata
= { 0 };
259 uint8_t buf
[128] = { 0 };
262 fprintf(stderr
, "Entering application\n");
265 dev
->cb
= hmuartlgw_init_parse
;
266 dev
->cb_data
= &rdata
;
268 rdata
.state
= HMUARTLGW_QUERY_APPSTATE
;
269 buf
[0] = HMUARTLGW_OS_GET_APP
;
270 hmuartlgw_send(dev
, buf
, 1, HMUARTLGW_OS
);
272 hmuartlgw_poll(dev
, HMUARTLGW_INIT_TIMEOUT
);
273 } while (rdata
.state
== HMUARTLGW_QUERY_APPSTATE
);
275 if (rdata
.state
!= HMUARTLGW_APPLICATION
) {
276 rdata
.state
= HMUARTLGW_ENTER_APPLICATION
;
277 buf
[0] = HMUARTLGW_OS_CHANGE_APP
;
278 hmuartlgw_send(dev
, buf
, 1, HMUARTLGW_OS
);
280 hmuartlgw_poll(dev
, HMUARTLGW_INIT_TIMEOUT
);
281 } while (rdata
.state
!= HMUARTLGW_APPLICATION
);
283 printf("Waiting for application to settle...\n");
284 sleep(HMUARTLGW_SETTLE_TIME
);
288 dev
->cb_data
= cb_data_old
;
291 static int hmuartlgw_escape(uint8_t *frame
, int framelen
)
295 for (i
= 1; i
< framelen
; i
++) {
296 if (frame
[i
] == 0xfc || frame
[i
] == 0xfd) {
297 memmove(frame
+ i
+ 1, frame
+ i
, framelen
- i
);
306 int hmuartlgw_send_raw(struct hmuartlgw_dev
*dev
, uint8_t *frame
, int framelen
)
312 hexdump(frame
, framelen
, "UARTLGW < ");
315 framelen
= hmuartlgw_escape(frame
, framelen
);
318 ret
= write(dev
->fd
, frame
+ w
, framelen
- w
);
324 } while (w
< framelen
);
329 int hmuartlgw_send(struct hmuartlgw_dev
*dev
, uint8_t *cmd
, int cmdlen
, enum hmuartlgw_dst dst
)
331 static uint8_t cnt
= 0;
332 uint8_t frame
[1024] = { 0 };
336 frame
[1] = ((cmdlen
+ 2) >> 8) & 0xff;
337 frame
[2] = (cmdlen
+ 2) & 0xff;
339 dev
->last_send_cnt
= cnt
;
341 memcpy(&(frame
[5]), cmd
, cmdlen
);
342 crc
= crc16(frame
, cmdlen
+ 5);
343 frame
[cmdlen
+ 5] = (crc
>> 8) & 0xff;
344 frame
[cmdlen
+ 6] = crc
& 0xff;
346 return hmuartlgw_send_raw(dev
, frame
, cmdlen
+ 7);
349 int hmuartlgw_poll(struct hmuartlgw_dev
*dev
, int timeout
)
351 struct pollfd pfds
[1];
358 memset(pfds
, 0, sizeof(struct pollfd
) * 1);
360 pfds
[0].fd
= dev
->fd
;
361 pfds
[0].events
= POLLIN
;
363 ret
= poll(pfds
, 1, timeout
);
373 if (!(pfds
[0].revents
& POLLIN
)) {
378 r
= read(dev
->fd
, dev
->buf
+dev
->pos
, 1);
389 if (dev
->buf
[0] != 0xfd) {
390 memset(dev
->buf
, 0, sizeof(dev
->buf
));
392 dev
->unescape_next
= 0;
396 if (dev
->unescape_next
) {
397 dev
->buf
[dev
->pos
-1] |= 0x80;
398 dev
->unescape_next
= 0;
399 } else if (dev
->buf
[dev
->pos
-1] == 0xfc) {
400 dev
->unescape_next
= 1;
408 len
= ((dev
->buf
[1] << 8) & 0xff00) | (dev
->buf
[2] & 0xff);
410 if (dev
->pos
< len
+ 5)
416 crc
= crc16(dev
->buf
, dev
->pos
- 2);
417 if ((((crc
>> 8) & 0xff) == dev
->buf
[dev
->pos
- 2]) &&
418 ((crc
& 0xff) == dev
->buf
[dev
->pos
- 1])) {
421 hexdump(dev
->buf
, dev
->pos
, "UARTLGW > ");
423 dev
->cb(dev
->buf
[3], dev
->buf
+ 5 , dev
->pos
- 7, dev
->cb_data
);
425 memset(dev
->buf
, 0, sizeof(dev
->buf
));
427 dev
->unescape_next
= 0;
429 fprintf(stderr
, "Invalid checksum received!\n");
430 hexdump(dev
->buf
, dev
->pos
, "ERR> ");
431 printf("calculated: %04x\n", crc
);
433 memset(dev
->buf
, 0, sizeof(dev
->buf
));
435 dev
->unescape_next
= 0;
442 void hmuartlgw_close(struct hmuartlgw_dev
*dev
)
447 void hmuartlgw_flush(struct hmuartlgw_dev
*dev
)
449 struct pollfd pfds
[1];
454 tcflush(dev
->fd
, TCIOFLUSH
);
457 memset(pfds
, 0, sizeof(struct pollfd
) * 1);
459 pfds
[0].fd
= dev
->fd
;
460 pfds
[0].events
= POLLIN
;
462 ret
= poll(pfds
, 1, 100);
466 if (!(pfds
[0].revents
& POLLIN
))
469 memset(buf
, 0, sizeof(buf
));
470 r
= read(dev
->fd
, buf
, sizeof(buf
));
478 void hmuartlgw_set_debug(int d
)