]> git.zerfleddert.de Git - hmcfgusb/blob - hmsniff.c
c35b29dd0ef0cc8458d31fff023626aceeeb0c77
[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 buf[3] = buf_len - 4;
235 dissect_hm(buf + 3, buf_len - 3);
236 case HMUARTLGW_APP_ACK:
237 break;
238 default:
239 hexdump(buf, buf_len, "Unknown> ");
240 }
241
242 return 1;
243 }
244
245 void hmsniff_syntax(char *prog)
246 {
247 fprintf(stderr, "Syntax: %s options\n\n", prog);
248 fprintf(stderr, "Possible options:\n");
249 fprintf(stderr, "\t-f\t\tfast (100k/firmware update) mode\n");
250 fprintf(stderr, "\t-S serial\tuse HM-CFG-USB with given serial\n");
251 fprintf(stderr, "\t-U device\tuse HM-MOD-UART on given device\n");
252 fprintf(stderr, "\t-v\t\tverbose mode\n");
253 fprintf(stderr, "\t-V\t\tshow version (" VERSION ")\n");
254
255 }
256
257 int main(int argc, char **argv)
258 {
259 struct hm_dev dev = { 0 };
260 struct recv_data rdata;
261 char *serial = NULL;
262 char *uart = NULL;
263 int quit = 0;
264 int speed = 10;
265 uint8_t buf[32];
266 int opt;
267
268 dev.type = DEVICE_TYPE_HMCFGUSB;
269
270 while((opt = getopt(argc, argv, "fS:U:vV")) != -1) {
271 switch (opt) {
272 case 'f':
273 speed = 100;
274 break;
275 case 'S':
276 serial = optarg;
277 break;
278 case 'U':
279 uart = optarg;
280 dev.type = DEVICE_TYPE_HMUARTLGW;
281 break;
282 case 'v':
283 verbose = 1;
284 break;
285 case 'V':
286 printf("hmsniff " VERSION "\n");
287 printf("Copyright (c) 2013-16 Michael Gernoth\n\n");
288 exit(EXIT_SUCCESS);
289 case 'h':
290 case ':':
291 case '?':
292 default:
293 hmsniff_syntax(argv[0]);
294 exit(EXIT_FAILURE);
295 break;
296 }
297 }
298
299 if (dev.type == DEVICE_TYPE_HMCFGUSB) {
300 hmcfgusb_set_debug(0);
301 } else {
302 hmuartlgw_set_debug(0);
303 }
304
305 do {
306 memset(&rdata, 0, sizeof(rdata));
307 rdata.wrong_hmid = 0;
308
309 if (dev.type == DEVICE_TYPE_HMCFGUSB) {
310 dev.hmcfgusb = hmcfgusb_init(parse_hmcfgusb, &rdata, serial);
311 if (!dev.hmcfgusb) {
312 fprintf(stderr, "Can't initialize HM-CFG-USB, retrying in 1s...\n");
313 sleep(1);
314 continue;
315 }
316 printf("HM-CFG-USB opened!\n");
317
318 hmcfgusb_send_null_frame(dev.hmcfgusb, 1);
319 hmcfgusb_send(dev.hmcfgusb, (unsigned char*)"K", 1, 1);
320
321 hmcfgusb_send_null_frame(dev.hmcfgusb, 1);
322 buf[0] = 'G';
323 buf[1] = speed;
324 hmcfgusb_send(dev.hmcfgusb, buf, 2, 1);
325 } else {
326 dev.hmuartlgw = hmuart_init(uart, parse_hmuartlgw, &rdata);
327 if (!dev.hmuartlgw) {
328 fprintf(stderr, "Can't initialize HM-MOD-UART!\n");
329 exit(1);
330 }
331 printf("HM-MOD-UART opened!\n");
332
333 buf[0] = HMUARTLGW_APP_SET_HMID;
334 buf[1] = 0x00;
335 buf[2] = 0x00;
336 buf[3] = 0x00;
337 hmuartlgw_send(dev.hmuartlgw, buf, 4, HMUARTLGW_APP);
338 do { hmuartlgw_poll(dev.hmuartlgw, 500); } while (errno != ETIMEDOUT);
339 if (speed == 100) {
340 buf[0] = HMUARTLGW_OS_UPDATE_MODE;
341 buf[1] = 0xe9;
342 buf[2] = 0xca;
343 hmuartlgw_send(dev.hmuartlgw, buf, 3, HMUARTLGW_OS);
344 } else {
345 buf[0] = HMUARTLGW_OS_NORMAL_MODE;
346 hmuartlgw_send(dev.hmuartlgw, buf, 1, HMUARTLGW_OS);
347 }
348 }
349
350 while(!quit) {
351 int fd;
352
353 if (rdata.wrong_hmid) {
354 printf("changing hmId to 000000, this might reboot the device!\n");
355 if (dev.type == DEVICE_TYPE_HMCFGUSB) {
356 hmcfgusb_send(dev.hmcfgusb, (unsigned char*)"A\00\00\00", 4, 1);
357 rdata.wrong_hmid = 0;
358 hmcfgusb_send(dev.hmcfgusb, (unsigned char*)"K", 1, 1);
359 } else {
360 buf[0] = HMUARTLGW_APP_SET_HMID;
361 buf[1] = 0x00;
362 buf[2] = 0x00;
363 buf[3] = 0x00;
364 hmuartlgw_send(dev.hmuartlgw, buf, 4, HMUARTLGW_APP);
365 }
366 }
367 if (dev.type == DEVICE_TYPE_HMCFGUSB) {
368 fd = hmcfgusb_poll(dev.hmcfgusb, 1000);
369 } else {
370 fd = hmuartlgw_poll(dev.hmuartlgw, 60000);
371 }
372 if (fd >= 0) {
373 fprintf(stderr, "activity on unknown fd %d!\n", fd);
374 continue;
375 } else if (fd == -1) {
376 if (errno) {
377 if (errno != ETIMEDOUT) {
378 perror("hmsniff_poll");
379 break;
380 } else {
381 /* periodically wakeup the device */
382 if (dev.type == DEVICE_TYPE_HMCFGUSB) {
383 hmcfgusb_send_null_frame(dev.hmcfgusb, 1);
384 }
385 }
386 }
387 }
388 }
389
390 if (dev.type == DEVICE_TYPE_HMCFGUSB) {
391 if (dev.hmcfgusb)
392 hmcfgusb_close(dev.hmcfgusb);
393 } else {
394 if (dev.hmuartlgw)
395 hmuartlgw_close(dev.hmuartlgw);
396 }
397 } while (!quit);
398
399 if (dev.type == DEVICE_TYPE_HMCFGUSB) {
400 hmcfgusb_exit();
401 }
402
403 return EXIT_SUCCESS;
404 }
Impressum, Datenschutz