Cleanup: Improve const correctness
[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 */
b8d1d0c3 45const char *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:
86fad104 234 buf[3] = buf_len - 4;
3e34d2ce
MG
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
cd45e4af
MG
245void hmsniff_syntax(char *prog)
246{
247 fprintf(stderr, "Syntax: %s options\n\n", prog);
248 fprintf(stderr, "Possible options:\n");
84daa92b 249 fprintf(stderr, "\t-f\t\tfast (100k/firmware update) mode\n");
f51714be 250 fprintf(stderr, "\t-S serial\tuse HM-CFG-USB with given serial\n");
3e34d2ce 251 fprintf(stderr, "\t-U device\tuse HM-MOD-UART on given device\n");
cd45e4af
MG
252 fprintf(stderr, "\t-v\t\tverbose mode\n");
253 fprintf(stderr, "\t-V\t\tshow version (" VERSION ")\n");
254
255}
256
d57fdaf6
MG
257int main(int argc, char **argv)
258{
3e34d2ce 259 struct hm_dev dev = { 0 };
885a84e3 260 struct recv_data rdata;
f51714be 261 char *serial = NULL;
3e34d2ce 262 char *uart = NULL;
d57fdaf6 263 int quit = 0;
84daa92b 264 int speed = 10;
3e34d2ce 265 uint8_t buf[32];
cd45e4af
MG
266 int opt;
267
3e34d2ce
MG
268 dev.type = DEVICE_TYPE_HMCFGUSB;
269
270 while((opt = getopt(argc, argv, "fS:U:vV")) != -1) {
cd45e4af 271 switch (opt) {
84daa92b
MG
272 case 'f':
273 speed = 100;
274 break;
f51714be
MG
275 case 'S':
276 serial = optarg;
277 break;
3e34d2ce
MG
278 case 'U':
279 uart = optarg;
280 dev.type = DEVICE_TYPE_HMUARTLGW;
281 break;
cd45e4af
MG
282 case 'v':
283 verbose = 1;
284 break;
285 case 'V':
286 printf("hmsniff " VERSION "\n");
7ba4ea19 287 printf("Copyright (c) 2013-16 Michael Gernoth\n\n");
cd45e4af
MG
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 }
d57fdaf6 298
3e34d2ce
MG
299 if (dev.type == DEVICE_TYPE_HMCFGUSB) {
300 hmcfgusb_set_debug(0);
301 } else {
302 hmuartlgw_set_debug(0);
303 }
d57fdaf6 304
885a84e3
MG
305 do {
306 memset(&rdata, 0, sizeof(rdata));
307 rdata.wrong_hmid = 0;
d57fdaf6 308
3e34d2ce
MG
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");
d57fdaf6 317
3e34d2ce
MG
318 hmcfgusb_send_null_frame(dev.hmcfgusb, 1);
319 hmcfgusb_send(dev.hmcfgusb, (unsigned char*)"K", 1, 1);
d57fdaf6 320
3e34d2ce
MG
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 {
853cbce9 326 dev.hmuartlgw = hmuart_init(uart, parse_hmuartlgw, &rdata, 1);
3e34d2ce
MG
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 }
84daa92b 349
885a84e3
MG
350 while(!quit) {
351 int fd;
352
353 if (rdata.wrong_hmid) {
354 printf("changing hmId to 000000, this might reboot the device!\n");
3e34d2ce
MG
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);
885a84e3 371 }
885a84e3
MG
372 if (fd >= 0) {
373 fprintf(stderr, "activity on unknown fd %d!\n", fd);
374 continue;
375 } else if (fd == -1) {
376 if (errno) {
1e79d00a 377 if (errno != ETIMEDOUT) {
3e34d2ce 378 perror("hmsniff_poll");
1e79d00a
MG
379 break;
380 } else {
381 /* periodically wakeup the device */
3e34d2ce
MG
382 if (dev.type == DEVICE_TYPE_HMCFGUSB) {
383 hmcfgusb_send_null_frame(dev.hmcfgusb, 1);
384 }
1e79d00a 385 }
885a84e3 386 }
d57fdaf6
MG
387 }
388 }
d57fdaf6 389
3e34d2ce
MG
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 }
885a84e3
MG
397 } while (!quit);
398
3e34d2ce
MG
399 if (dev.type == DEVICE_TYPE_HMCFGUSB) {
400 hmcfgusb_exit();
401 }
018f85fa 402
d57fdaf6
MG
403 return EXIT_SUCCESS;
404}
Impressum, Datenschutz