1 /* flasher for HomeMatic-devices supporting OTA updates
3 * Copyright (c) 2014 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>
36 #include <libusb-1.0/libusb.h>
56 enum message_type message_type
;
59 uint16_t hmcfgusb_version
;
62 static int parse_hmcfgusb(uint8_t *buf
, int buf_len
, void *data
)
64 struct recv_data
*rdata
= data
;
72 ((buf
[0x11] == ((hmid
>> 16) & 0xff)) &&
73 (buf
[0x12] == ((hmid
>> 8) & 0xff)) &&
74 (buf
[0x13] == (hmid
& 0xff)))) {
75 memset(rdata
->message
, 0, sizeof(rdata
->message
));
76 memcpy(rdata
->message
, buf
+ 0x0d, buf
[0x0d] + 1);
77 rdata
->message_type
= MESSAGE_TYPE_E
;
81 memset(rdata
->message
, 0, sizeof(rdata
->message
));
82 memcpy(rdata
->message
, buf
+ 0x0e, buf
[0x0e] + 1);
83 rdata
->status
= (buf
[5] << 8) | buf
[6];
84 rdata
->message_type
= MESSAGE_TYPE_R
;
87 rdata
->speed
= buf
[1];
90 rdata
->hmcfgusb_version
= (buf
[11] << 8) | buf
[12];
91 my_hmid
= (buf
[0x1b] << 16) | (buf
[0x1c] << 8) | buf
[0x1d];
103 int send_hm_message(struct hmcfgusb_dev
*dev
, struct recv_data
*rdata
, uint8_t *msg
)
105 static uint32_t id
= 1;
110 if (gettimeofday(&tv
, NULL
) == -1) {
111 perror("gettimeofay");
115 memset(out
, 0, sizeof(out
));
118 out
[1] = (id
>> 24) & 0xff;
119 out
[2] = (id
>> 16) & 0xff;
120 out
[3] = (id
>> 8) & 0xff;
123 out
[11] = (tv
.tv_usec
>> 24) & 0xff;
124 out
[12] = (tv
.tv_usec
>> 16) & 0xff;
125 out
[13] = (tv
.tv_usec
>> 8) & 0xff;
126 out
[14] = tv
.tv_usec
& 0xff;
129 memcpy(&out
[0x0f], msg
, msg
[0] + 1);
131 memset(rdata
, 0, sizeof(struct recv_data
));
132 hmcfgusb_send(dev
, out
, sizeof(out
), 1);
135 if (rdata
->message_type
== MESSAGE_TYPE_R
) {
136 if (((rdata
->status
& 0xff) == 0x01) ||
137 ((rdata
->status
& 0xff) == 0x02)) {
140 if ((rdata
->status
& 0xff00) == 0x0400) {
141 fprintf(stderr
, "\nOut of credits!\n");
142 } else if ((rdata
->status
& 0xff) == 0x08) {
143 fprintf(stderr
, "\nMissing ACK!\n");
145 fprintf(stderr
, "\nInvalid status: %04x\n", rdata
->status
);
151 pfd
= hmcfgusb_poll(dev
, 1);
152 if ((pfd
< 0) && errno
) {
153 if (errno
!= ETIMEDOUT
) {
154 perror("\n\nhmcfgusb_poll");
164 static int switch_speed(struct hmcfgusb_dev
*dev
, struct recv_data
*rdata
, uint8_t speed
)
169 printf("Entering %uk-mode\n", speed
);
171 memset(out
, 0, sizeof(out
));
175 hmcfgusb_send(dev
, out
, sizeof(out
), 1);
179 pfd
= hmcfgusb_poll(dev
, 1);
180 if ((pfd
< 0) && errno
) {
181 if (errno
!= ETIMEDOUT
) {
182 perror("\n\nhmcfgusb_poll");
186 if (rdata
->speed
== speed
)
193 int main(int argc
, char **argv
)
195 const char twiddlie
[] = { '-', '\\', '|', '/' };
196 const uint8_t switch_msg
[] = { 0x10, 0x5B, 0x11, 0xF8, 0x15, 0x47 };
197 struct hmcfgusb_dev
*dev
;
198 struct recv_data rdata
;
212 printf("HomeMatic OTA flasher version " VERSION
"\n\n");
216 fprintf(stderr
, "Missing firmware filename!\n\n");
219 fprintf(stderr
, "Missing serial!\n\n");
221 fprintf(stderr
, "Syntax: %s firmware.eq3 SERIALNUMBER\n\n", argv
[0]);
225 fw
= firmware_read_firmware(argv
[1], debug
);
229 hmcfgusb_set_debug(debug
);
231 memset(&rdata
, 0, sizeof(rdata
));
233 dev
= hmcfgusb_init(parse_hmcfgusb
, &rdata
);
235 fprintf(stderr
, "Can't initialize HM-CFG-USB\n");
239 printf("\nRebooting HM-CFG-USB to avoid running out of credits\n\n");
241 if (!dev
->bootloader
) {
242 printf("HM-CFG-USB not in bootloader mode, entering bootloader.\n");
243 hmcfgusb_enter_bootloader(dev
);
244 printf("Waiting for device to reappear...\n");
251 } while (((dev
= hmcfgusb_init(parse_hmcfgusb
, &rdata
)) == NULL
) || (!dev
->bootloader
));
254 if (dev
->bootloader
) {
255 printf("HM-CFG-USB in bootloader mode, rebooting\n");
256 hmcfgusb_leave_bootloader(dev
);
263 } while (((dev
= hmcfgusb_init(parse_hmcfgusb
, &rdata
)) == NULL
) || (dev
->bootloader
));
266 printf("\n\nHM-CFG-USB opened\n\n");
268 memset(out
, 0, sizeof(out
));
270 hmcfgusb_send(dev
, out
, sizeof(out
), 1);
274 pfd
= hmcfgusb_poll(dev
, 1);
275 if ((pfd
< 0) && errno
) {
276 if (errno
!= ETIMEDOUT
) {
277 perror("\n\nhmcfgusb_poll");
281 if (rdata
.hmcfgusb_version
)
285 if (rdata
.hmcfgusb_version
< 0x3c7) {
286 fprintf(stderr
, "HM-CFG-USB firmware too low: %u < 967\n", rdata
.hmcfgusb_version
);
290 printf("HM-CFG-USB firmware version: %u\n", rdata
.hmcfgusb_version
);
292 if (!switch_speed(dev
, &rdata
, 10)) {
293 fprintf(stderr
, "Can't switch speed!\n");
297 printf("Waiting for device with serial %s\n", argv
[2]);
301 pfd
= hmcfgusb_poll(dev
, 1);
302 if ((pfd
< 0) && errno
) {
303 if (errno
!= ETIMEDOUT
) {
304 perror("\n\nhmcfgusb_poll");
309 if ((rdata
.message
[LEN
] == 0x14) && /* Length */
310 (rdata
.message
[MSGID
] == 0x00) && /* Message ID */
311 (rdata
.message
[CTL
] == 0x00) && /* Control Byte */
312 (rdata
.message
[TYPE
] == 0x10) && /* Messagte type: Information */
313 (DST(rdata
.message
) == 0x000000) && /* Broadcast */
314 (rdata
.message
[PAYLOAD
] == 0x00) && /* FUP? */
315 (rdata
.message
[PAYLOAD
+2] == 'E') &&
316 (rdata
.message
[PAYLOAD
+3] == 'Q')) {
317 if (!strncmp((char*)&(rdata
.message
[0x0b]), argv
[2], 10)) {
318 hmid
= SRC(rdata
.message
);
324 printf("Device with serial %s (hmid: %06x) entered firmware-update-mode\n", argv
[2], hmid
);
326 printf("Adding HMID\n");
328 memset(out
, 0, sizeof(out
));
330 out
[1] = (hmid
>> 16) & 0xff;
331 out
[2] = (hmid
>> 8) & 0xff;
332 out
[3] = hmid
& 0xff;
334 hmcfgusb_send(dev
, out
, sizeof(out
), 1);
338 printf("Initiating remote switch to 100k\n");
340 memset(out
, 0, sizeof(out
));
342 out
[MSGID
] = msgid
++;
345 SET_SRC(out
, my_hmid
);
348 memcpy(&out
[PAYLOAD
], switch_msg
, sizeof(switch_msg
));
349 SET_LEN_FROM_PAYLOADLEN(out
, sizeof(switch_msg
));
351 if (!send_hm_message(dev
, &rdata
, out
)) {
355 if (!switch_speed(dev
, &rdata
, 100)) {
356 fprintf(stderr
, "Can't switch speed!\n");
360 printf("Has the device switched?\n");
362 memset(out
, 0, sizeof(out
));
364 out
[MSGID
] = msgid
++;
367 SET_SRC(out
, my_hmid
);
370 memcpy(&out
[PAYLOAD
], switch_msg
, sizeof(switch_msg
));
371 SET_LEN_FROM_PAYLOADLEN(out
, sizeof(switch_msg
));
375 if (send_hm_message(dev
, &rdata
, out
)) {
376 /* A0A02000221B9AD00000000 */
386 if (!switch_speed(dev
, &rdata
, 10)) {
387 fprintf(stderr
, "Can't switch speed!\n");
391 } while ((!switched
) && (switchcnt
--));
394 fprintf(stderr
, "Too many errors, giving up!\n");
400 printf("Flashing %d blocks", fw
->fw_blocks
);
404 printf(": %04u/%04u %c", 0, fw
->fw_blocks
, twiddlie
[0]);
408 for (block
= 0; block
< fw
->fw_blocks
; block
++) {
411 len
= fw
->fw
[block
][2] << 8;
412 len
|= fw
->fw
[block
][3];
414 pos
= &(fw
->fw
[block
][2]);
416 len
+= 2; /* length */
419 hexdump(pos
, len
, "F> ");
432 if ((len
- (pos
- &(fw
->fw
[block
][2]))) < payloadlen
)
433 payloadlen
= (len
- (pos
- &(fw
->fw
[block
][2])));
435 if (((pos
+ payloadlen
) - &(fw
->fw
[block
][2])) == len
)
438 memset(&rdata
, 0, sizeof(rdata
));
440 memset(out
, 0, sizeof(out
));
446 SET_SRC(out
, my_hmid
);
449 memcpy(&out
[PAYLOAD
], pos
, payloadlen
);
450 SET_LEN_FROM_PAYLOADLEN(out
, payloadlen
);
452 if (send_hm_message(dev
, &rdata
, out
)) {
455 pos
= &(fw
->fw
[block
][2]);
457 if (cnt
== MAX_RETRIES
) {
458 fprintf(stderr
, "\nToo many errors, giving up!\n");
461 printf("Flashing %d blocks: %04u/%04u %c", fw
->fw_blocks
, block
+ 1, fw
->fw_blocks
, twiddlie
[msgnum
% sizeof(twiddlie
)]);
468 printf("\b\b\b\b\b\b\b\b\b\b\b%04u/%04u %c",
469 block
+ 1, fw
->fw_blocks
, twiddlie
[msgnum
% sizeof(twiddlie
)]);
472 } while((pos
- &(fw
->fw
[block
][2])) < len
);
480 if (!switch_speed(dev
, &rdata
, 10)) {
481 fprintf(stderr
, "Can't switch speed!\n");
485 printf("Waiting for device to reboot\n");
490 pfd
= hmcfgusb_poll(dev
, 1);
491 if ((pfd
< 0) && errno
) {
492 if (errno
!= ETIMEDOUT
) {
493 perror("\n\nhmcfgusb_poll");
497 if (rdata
.message_type
== MESSAGE_TYPE_E
) {
502 if (rdata
.message_type
== MESSAGE_TYPE_E
) {
503 printf("Device rebooted\n");