hmuartlgw: add initial support for HM-MOD-UART
[hmcfgusb] / hmsniff.c
CommitLineData
d57fdaf6
MG
1/* HM-sniffer for HM-CFG-USB
2 *
7ba4ea19 3 * Copyright (c) 2013-16 Michael Gernoth <michael@gernoth.net>
d57fdaf6
MG
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>
cd45e4af 32#include <time.h>
e2776af8 33#include <sys/time.h>
d57fdaf6
MG
34#include <libusb-1.0/libusb.h>
35
c44f15b8 36#include "version.h"
d57fdaf6
MG
37#include "hexdump.h"
38#include "hmcfgusb.h"
3e34d2ce
MG
39#include "hmuartlgw.h"
40#include "hm.h"
d57fdaf6 41
cd45e4af
MG
42static int verbose = 0;
43
d57fdaf6 44/* See HMConfig.pm */
cd45e4af 45char *hm_message_types(uint8_t type, uint8_t subtype)
d57fdaf6
MG
46{
47 switch(type) {
48 case 0x00:
49 return "Device Info";
50 break;
51 case 0x01:
52 return "Configuration";
53 break;
54 case 0x02:
cd45e4af
MG
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";
d57fdaf6
MG
63 break;
64 case 0x03:
cd45e4af 65 return "AESreply";
d57fdaf6
MG
66 break;
67 case 0x04:
cd45e4af 68 return "AESkey";
d57fdaf6
MG
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;
e728a6f8
MG
94 case 0x54:
95 return "Gas sensor";
96 break;
d57fdaf6
MG
97 case 0x58:
98 return "Climate event";
99 break;
cd45e4af
MG
100 case 0x5a:
101 return "Thermal control";
102 break;
920d34e0 103 case 0x5e:
e728a6f8 104 case 0x5f:
920d34e0
MG
105 return "Power event";
106 break;
d57fdaf6
MG
107 case 0x70:
108 return "Weather event";
109 break;
84daa92b
MG
110 case 0xca:
111 return "Firmware";
112 break;
113 case 0xcb:
114 return "Rf configuration";
115 break;
d57fdaf6
MG
116 default:
117 return "?";
118 break;
119 }
120}
121
122static void dissect_hm(uint8_t *buf, int len)
123{
e2776af8
MG
124 struct timeval tv;
125 struct tm *tmp;
126 char ts[32];
cd45e4af 127 static int count = 0;
d57fdaf6
MG
128 int i;
129
e2776af8
MG
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);
e2776af8 134
cd45e4af
MG
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");
920d34e0 169
cd45e4af
MG
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]));
d57fdaf6 180 }
d57fdaf6
MG
181}
182
885a84e3
MG
183struct recv_data {
184 int wrong_hmid;
185};
186
4371275b 187static int parse_hmcfgusb(uint8_t *buf, int buf_len, void *data)
d57fdaf6 188{
885a84e3
MG
189 struct recv_data *rdata = data;
190
d57fdaf6 191 if (buf_len < 1)
4371275b 192 return 1;
d57fdaf6
MG
193
194 switch(buf[0]) {
195 case 'E':
196 dissect_hm(buf + 13, buf[13] + 1);
197 break;
198 case 'H':
885a84e3
MG
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;
d57fdaf6
MG
206 case 'R':
207 case 'I':
84daa92b 208 case 'G':
d57fdaf6
MG
209 break;
210 default:
211 hexdump(buf, buf_len, "Unknown> ");
212 break;
213 }
4371275b
MG
214
215 return 1;
d57fdaf6
MG
216}
217
3e34d2ce
MG
218static 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
cd45e4af
MG
244void hmsniff_syntax(char *prog)
245{
246 fprintf(stderr, "Syntax: %s options\n\n", prog);
247 fprintf(stderr, "Possible options:\n");
84daa92b 248 fprintf(stderr, "\t-f\t\tfast (100k/firmware update) mode\n");
f51714be 249 fprintf(stderr, "\t-S serial\tuse HM-CFG-USB with given serial\n");
3e34d2ce 250 fprintf(stderr, "\t-U device\tuse HM-MOD-UART on given device\n");
cd45e4af
MG
251 fprintf(stderr, "\t-v\t\tverbose mode\n");
252 fprintf(stderr, "\t-V\t\tshow version (" VERSION ")\n");
253
254}
255
d57fdaf6
MG
256int main(int argc, char **argv)
257{
3e34d2ce 258 struct hm_dev dev = { 0 };
885a84e3 259 struct recv_data rdata;
f51714be 260 char *serial = NULL;
3e34d2ce 261 char *uart = NULL;
d57fdaf6 262 int quit = 0;
84daa92b 263 int speed = 10;
3e34d2ce 264 uint8_t buf[32];
cd45e4af
MG
265 int opt;
266
3e34d2ce
MG
267 dev.type = DEVICE_TYPE_HMCFGUSB;
268
269 while((opt = getopt(argc, argv, "fS:U:vV")) != -1) {
cd45e4af 270 switch (opt) {
84daa92b
MG
271 case 'f':
272 speed = 100;
273 break;
f51714be
MG
274 case 'S':
275 serial = optarg;
276 break;
3e34d2ce
MG
277 case 'U':
278 uart = optarg;
279 dev.type = DEVICE_TYPE_HMUARTLGW;
280 break;
cd45e4af
MG
281 case 'v':
282 verbose = 1;
283 break;
284 case 'V':
285 printf("hmsniff " VERSION "\n");
7ba4ea19 286 printf("Copyright (c) 2013-16 Michael Gernoth\n\n");
cd45e4af
MG
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 }
d57fdaf6 297
3e34d2ce
MG
298 if (dev.type == DEVICE_TYPE_HMCFGUSB) {
299 hmcfgusb_set_debug(0);
300 } else {
301 hmuartlgw_set_debug(0);
302 }
d57fdaf6 303
885a84e3
MG
304 do {
305 memset(&rdata, 0, sizeof(rdata));
306 rdata.wrong_hmid = 0;
d57fdaf6 307
3e34d2ce
MG
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");
d57fdaf6 316
3e34d2ce
MG
317 hmcfgusb_send_null_frame(dev.hmcfgusb, 1);
318 hmcfgusb_send(dev.hmcfgusb, (unsigned char*)"K", 1, 1);
d57fdaf6 319
3e34d2ce
MG
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 }
84daa92b 348
885a84e3
MG
349 while(!quit) {
350 int fd;
351
352 if (rdata.wrong_hmid) {
353 printf("changing hmId to 000000, this might reboot the device!\n");
3e34d2ce
MG
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);
885a84e3 370 }
885a84e3
MG
371 if (fd >= 0) {
372 fprintf(stderr, "activity on unknown fd %d!\n", fd);
373 continue;
374 } else if (fd == -1) {
375 if (errno) {
1e79d00a 376 if (errno != ETIMEDOUT) {
3e34d2ce 377 perror("hmsniff_poll");
1e79d00a
MG
378 break;
379 } else {
380 /* periodically wakeup the device */
3e34d2ce
MG
381 if (dev.type == DEVICE_TYPE_HMCFGUSB) {
382 hmcfgusb_send_null_frame(dev.hmcfgusb, 1);
383 }
1e79d00a 384 }
885a84e3 385 }
d57fdaf6
MG
386 }
387 }
d57fdaf6 388
3e34d2ce
MG
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 }
885a84e3
MG
396 } while (!quit);
397
3e34d2ce
MG
398 if (dev.type == DEVICE_TYPE_HMCFGUSB) {
399 hmcfgusb_exit();
400 }
018f85fa 401
d57fdaf6
MG
402 return EXIT_SUCCESS;
403}
Impressum, Datenschutz