hmuartlgw: add initial support for HM-MOD-UART
[hmcfgusb] / hmsniff.c
1 /* HM-sniffer for HM-CFG-USB
2 *
3 * Copyright (c) 2013-16 Michael Gernoth <michael@gernoth.net>
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 <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <stdint.h>
28 #include <string.h>
29 #include <strings.h>
30 #include <poll.h>
31 #include <errno.h>
32 #include <time.h>
33 #include <sys/time.h>
34 #include <libusb-1.0/libusb.h>
35
36 #include "version.h"
37 #include "hexdump.h"
38 #include "hmcfgusb.h"
39 #include "hmuartlgw.h"
40 #include "hm.h"
41
42 static int verbose = 0;
43
44 /* See HMConfig.pm */
45 char *hm_message_types(uint8_t type, uint8_t subtype)
46 {
47 switch(type) {
48 case 0x00:
49 return "Device Info";
50 break;
51 case 0x01:
52 return "Configuration";
53 break;
54 case 0x02:
55 if (subtype >= 0x80 && subtype <= 0x8f) {
56 return "NACK";
57 } else if (subtype == 0x01) {
58 return "ACKinfo";
59 } else if (subtype == 0x04) {
60 return "AESrequest";
61 }
62 return "ACK";
63 break;
64 case 0x03:
65 return "AESreply";
66 break;
67 case 0x04:
68 return "AESkey";
69 break;
70 case 0x10:
71 return "Information";
72 break;
73 case 0x11:
74 return "SET";
75 break;
76 case 0x12:
77 return "HAVE_DATA";
78 break;
79 case 0x3e:
80 return "Switch";
81 break;
82 case 0x3f:
83 return "Timestamp";
84 break;
85 case 0x40:
86 return "Remote";
87 break;
88 case 0x41:
89 return "Sensor";
90 break;
91 case 0x53:
92 return "Water sensor";
93 break;
94 case 0x54:
95 return "Gas sensor";
96 break;
97 case 0x58:
98 return "Climate event";
99 break;
100 case 0x5a:
101 return "Thermal control";
102 break;
103 case 0x5e:
104 case 0x5f:
105 return "Power event";
106 break;
107 case 0x70:
108 return "Weather event";
109 break;
110 case 0xca:
111 return "Firmware";
112 break;
113 case 0xcb:
114 return "Rf configuration";
115 break;
116 default:
117 return "?";
118 break;
119 }
120 }
121
122 static void dissect_hm(uint8_t *buf, int len)
123 {
124 struct timeval tv;
125 struct tm *tmp;
126 char ts[32];
127 static int count = 0;
128 int i;
129
130 gettimeofday(&tv, NULL);
131 tmp = localtime(&tv.tv_sec);
132 memset(ts, 0, sizeof(ts));
133 strftime(ts, sizeof(ts)-1, "%Y-%m-%d %H:%M:%S", tmp);
134
135 if (verbose) {
136 printf("%s.%06ld: ", ts, tv.tv_usec);
137
138 for (i = 0; i < len; i++) {
139 printf("%02X", buf[i]);
140 }
141 printf("\n");
142 printf("Packet information:\n");
143 printf("\tLength: %u\n", buf[0]);
144 printf("\tMessage ID: %u\n", buf[1]);
145 printf("\tSender: %02x%02x%02x\n", buf[4], buf[5], buf[6]);
146 printf("\tReceiver: %02x%02x%02x\n", buf[7], buf[8], buf[9]);
147 printf("\tControl Byte: 0x%02x\n", buf[2]);
148 printf("\t\tFlags: ");
149 if (buf[2] & (1 << 0)) printf("WAKEUP ");
150 if (buf[2] & (1 << 1)) printf("WAKEMEUP ");
151 if (buf[2] & (1 << 2)) printf("CFG ");
152 if (buf[2] & (1 << 3)) printf("? ");
153 if (buf[2] & (1 << 4)) printf("BURST ");
154 if (buf[2] & (1 << 5)) printf("BIDI ");
155 if (buf[2] & (1 << 6)) printf("RPTED ");
156 if (buf[2] & (1 << 7)) printf("RPTEN ");
157 printf("\n");
158 printf("\tMessage type: %s (0x%02x 0x%02x)\n", hm_message_types(buf[3], buf[10]), buf[3], buf[10]);
159 printf("\tMessage: ");
160 for (i = 10; i < len; i++) {
161 printf("%02X", buf[i]);
162 }
163 printf("\n");
164
165 printf("\n");
166 } else {
167 if (!(count++ % 20))
168 printf(" LL NR FL CM sender recvr payload\n");
169
170 printf("%s.%03ld: %02X %02X %02X %02X %02X%02X%02X %02X%02X%02X ",
171 ts, tv.tv_usec/1000,
172 buf[0], buf[1], buf[2], buf[3],
173 buf[4], buf[5], buf[6],
174 buf[7], buf[8], buf[9]);
175
176 for (i = 10; i < len; i++) {
177 printf("%02X", buf[i]);
178 }
179 printf("%s(%s)\n", (i>10)?" ":"", hm_message_types(buf[3], buf[10]));
180 }
181 }
182
183 struct recv_data {
184 int wrong_hmid;
185 };
186
187 static int parse_hmcfgusb(uint8_t *buf, int buf_len, void *data)
188 {
189 struct recv_data *rdata = data;
190
191 if (buf_len < 1)
192 return 1;
193
194 switch(buf[0]) {
195 case 'E':
196 dissect_hm(buf + 13, buf[13] + 1);
197 break;
198 case 'H':
199 if ((buf[27] != 0x00) ||
200 (buf[28] != 0x00) ||
201 (buf[29] != 0x00)) {
202 printf("hmId is currently set to: %02x%02x%02x\n", buf[27], buf[28], buf[29]);
203 rdata->wrong_hmid = 1;
204 }
205 break;
206 case 'R':
207 case 'I':
208 case 'G':
209 break;
210 default:
211 hexdump(buf, buf_len, "Unknown> ");
212 break;
213 }
214
215 return 1;
216 }
217
218 static int parse_hmuartlgw(enum hmuartlgw_dst dst, uint8_t *buf, int buf_len, void *data)
219 {
220 if (dst == HMUARTLGW_OS) {
221 if ((buf[0] != HMUARTLGW_OS_ACK) ||
222 (buf[1] != 0x01)) {
223 hexdump(buf, buf_len, "OS> ");
224 }
225 return 0;
226 }
227
228 if (dst != HMUARTLGW_APP) {
229 return 0;
230 }
231
232 switch(buf[0]) {
233 case HMUARTLGW_APP_RECV:
234 dissect_hm(buf + 3, buf_len - 3);
235 case HMUARTLGW_APP_ACK:
236 break;
237 default:
238 hexdump(buf, buf_len, "Unknown> ");
239 }
240
241 return 1;
242 }
243
244 void hmsniff_syntax(char *prog)
245 {
246 fprintf(stderr, "Syntax: %s options\n\n", prog);
247 fprintf(stderr, "Possible options:\n");
248 fprintf(stderr, "\t-f\t\tfast (100k/firmware update) mode\n");
249 fprintf(stderr, "\t-S serial\tuse HM-CFG-USB with given serial\n");
250 fprintf(stderr, "\t-U device\tuse HM-MOD-UART on given device\n");
251 fprintf(stderr, "\t-v\t\tverbose mode\n");
252 fprintf(stderr, "\t-V\t\tshow version (" VERSION ")\n");
253
254 }
255
256 int main(int argc, char **argv)
257 {
258 struct hm_dev dev = { 0 };
259 struct recv_data rdata;
260 char *serial = NULL;
261 char *uart = NULL;
262 int quit = 0;
263 int speed = 10;
264 uint8_t buf[32];
265 int opt;
266
267 dev.type = DEVICE_TYPE_HMCFGUSB;
268
269 while((opt = getopt(argc, argv, "fS:U:vV")) != -1) {
270 switch (opt) {
271 case 'f':
272 speed = 100;
273 break;
274 case 'S':
275 serial = optarg;
276 break;
277 case 'U':
278 uart = optarg;
279 dev.type = DEVICE_TYPE_HMUARTLGW;
280 break;
281 case 'v':
282 verbose = 1;
283 break;
284 case 'V':
285 printf("hmsniff " VERSION "\n");
286 printf("Copyright (c) 2013-16 Michael Gernoth\n\n");
287 exit(EXIT_SUCCESS);
288 case 'h':
289 case ':':
290 case '?':
291 default:
292 hmsniff_syntax(argv[0]);
293 exit(EXIT_FAILURE);
294 break;
295 }
296 }
297
298 if (dev.type == DEVICE_TYPE_HMCFGUSB) {
299 hmcfgusb_set_debug(0);
300 } else {
301 hmuartlgw_set_debug(0);
302 }
303
304 do {
305 memset(&rdata, 0, sizeof(rdata));
306 rdata.wrong_hmid = 0;
307
308 if (dev.type == DEVICE_TYPE_HMCFGUSB) {
309 dev.hmcfgusb = hmcfgusb_init(parse_hmcfgusb, &rdata, serial);
310 if (!dev.hmcfgusb) {
311 fprintf(stderr, "Can't initialize HM-CFG-USB, retrying in 1s...\n");
312 sleep(1);
313 continue;
314 }
315 printf("HM-CFG-USB opened!\n");
316
317 hmcfgusb_send_null_frame(dev.hmcfgusb, 1);
318 hmcfgusb_send(dev.hmcfgusb, (unsigned char*)"K", 1, 1);
319
320 hmcfgusb_send_null_frame(dev.hmcfgusb, 1);
321 buf[0] = 'G';
322 buf[1] = speed;
323 hmcfgusb_send(dev.hmcfgusb, buf, 2, 1);
324 } else {
325 dev.hmuartlgw = hmuart_init(uart, parse_hmuartlgw, &rdata);
326 if (!dev.hmuartlgw) {
327 fprintf(stderr, "Can't initialize HM-MOD-UART!\n");
328 exit(1);
329 }
330 printf("HM-MOD-UART opened!\n");
331
332 buf[0] = HMUARTLGW_APP_SET_HMID;
333 buf[1] = 0x00;
334 buf[2] = 0x00;
335 buf[3] = 0x00;
336 hmuartlgw_send(dev.hmuartlgw, buf, 4, HMUARTLGW_APP);
337 do { hmuartlgw_poll(dev.hmuartlgw, 500); } while (errno != ETIMEDOUT);
338 if (speed == 100) {
339 buf[0] = HMUARTLGW_OS_UPDATE_MODE;
340 buf[1] = 0xe9;
341 buf[2] = 0xca;
342 hmuartlgw_send(dev.hmuartlgw, buf, 3, HMUARTLGW_OS);
343 } else {
344 buf[0] = HMUARTLGW_OS_NORMAL_MODE;
345 hmuartlgw_send(dev.hmuartlgw, buf, 1, HMUARTLGW_OS);
346 }
347 }
348
349 while(!quit) {
350 int fd;
351
352 if (rdata.wrong_hmid) {
353 printf("changing hmId to 000000, this might reboot the device!\n");
354 if (dev.type == DEVICE_TYPE_HMCFGUSB) {
355 hmcfgusb_send(dev.hmcfgusb, (unsigned char*)"A\00\00\00", 4, 1);
356 rdata.wrong_hmid = 0;
357 hmcfgusb_send(dev.hmcfgusb, (unsigned char*)"K", 1, 1);
358 } else {
359 buf[0] = HMUARTLGW_APP_SET_HMID;
360 buf[1] = 0x00;
361 buf[2] = 0x00;
362 buf[3] = 0x00;
363 hmuartlgw_send(dev.hmuartlgw, buf, 4, HMUARTLGW_APP);
364 }
365 }
366 if (dev.type == DEVICE_TYPE_HMCFGUSB) {
367 fd = hmcfgusb_poll(dev.hmcfgusb, 1000);
368 } else {
369 fd = hmuartlgw_poll(dev.hmuartlgw, 60000);
370 }
371 if (fd >= 0) {
372 fprintf(stderr, "activity on unknown fd %d!\n", fd);
373 continue;
374 } else if (fd == -1) {
375 if (errno) {
376 if (errno != ETIMEDOUT) {
377 perror("hmsniff_poll");
378 break;
379 } else {
380 /* periodically wakeup the device */
381 if (dev.type == DEVICE_TYPE_HMCFGUSB) {
382 hmcfgusb_send_null_frame(dev.hmcfgusb, 1);
383 }
384 }
385 }
386 }
387 }
388
389 if (dev.type == DEVICE_TYPE_HMCFGUSB) {
390 if (dev.hmcfgusb)
391 hmcfgusb_close(dev.hmcfgusb);
392 } else {
393 if (dev.hmuartlgw)
394 hmuartlgw_close(dev.hmuartlgw);
395 }
396 } while (!quit);
397
398 if (dev.type == DEVICE_TYPE_HMCFGUSB) {
399 hmcfgusb_exit();
400 }
401
402 return EXIT_SUCCESS;
403 }
Impressum, Datenschutz