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>
54 enum message_type message_type
;
57 uint16_t hmcfgusb_version
;
60 static int parse_hmcfgusb(uint8_t *buf
, int buf_len
, void *data
)
62 struct recv_data
*rdata
= data
;
70 ((buf
[0x11] == ((hmid
>> 16) & 0xff)) &&
71 (buf
[0x12] == ((hmid
>> 8) & 0xff)) &&
72 (buf
[0x13] == (hmid
& 0xff)))) {
73 memset(rdata
->message
, 0, sizeof(rdata
->message
));
74 memcpy(rdata
->message
, buf
+ 0x0d, buf
[0x0d] + 1);
75 rdata
->message_type
= MESSAGE_TYPE_E
;
79 memset(rdata
->message
, 0, sizeof(rdata
->message
));
80 memcpy(rdata
->message
, buf
+ 0x0e, buf
[0x0e] + 1);
81 rdata
->status
= (buf
[5] << 8) | buf
[6];
82 rdata
->message_type
= MESSAGE_TYPE_R
;
85 rdata
->speed
= buf
[1];
88 rdata
->hmcfgusb_version
= (buf
[11] << 8) | buf
[12];
89 my_hmid
= (buf
[0x1b] << 16) | (buf
[0x1c] << 8) | buf
[0x1d];
101 int send_hm_message(struct hmcfgusb_dev
*dev
, struct recv_data
*rdata
, uint8_t *msg
)
103 static uint32_t id
= 1;
108 if (gettimeofday(&tv
, NULL
) == -1) {
109 perror("gettimeofay");
113 memset(out
, 0, sizeof(out
));
116 out
[1] = (id
>> 24) & 0xff;
117 out
[2] = (id
>> 16) & 0xff;
118 out
[3] = (id
>> 8) & 0xff;
121 out
[11] = (tv
.tv_usec
>> 24) & 0xff;
122 out
[12] = (tv
.tv_usec
>> 16) & 0xff;
123 out
[13] = (tv
.tv_usec
>> 8) & 0xff;
124 out
[14] = tv
.tv_usec
& 0xff;
127 memcpy(&out
[0x0f], msg
, msg
[0] + 1);
129 memset(rdata
, 0, sizeof(struct recv_data
));
130 hmcfgusb_send(dev
, out
, sizeof(out
), 1);
133 if (rdata
->message_type
== MESSAGE_TYPE_R
) {
134 if (((rdata
->status
& 0xff) == 0x01) ||
135 ((rdata
->status
& 0xff) == 0x02)) {
138 fprintf(stderr
, "\nInvalid status: %04x\n", rdata
->status
);
143 pfd
= hmcfgusb_poll(dev
, 1);
144 if ((pfd
< 0) && errno
) {
145 if (errno
!= ETIMEDOUT
) {
146 perror("\n\nhmcfgusb_poll");
156 static int switch_speed(struct hmcfgusb_dev
*dev
, struct recv_data
*rdata
, uint8_t speed
)
161 printf("Entering %uk-mode\n", speed
);
163 memset(out
, 0, sizeof(out
));
167 hmcfgusb_send(dev
, out
, sizeof(out
), 1);
171 pfd
= hmcfgusb_poll(dev
, 1);
172 if ((pfd
< 0) && errno
) {
173 if (errno
!= ETIMEDOUT
) {
174 perror("\n\nhmcfgusb_poll");
178 if (rdata
->speed
== speed
)
185 int main(int argc
, char **argv
)
187 const char twiddlie
[] = { '-', '\\', '|', '/' };
188 const uint8_t switch_msg
[] = { 0x10, 0x5B, 0x11, 0xF8, 0x15, 0x47 };
189 struct hmcfgusb_dev
*dev
;
190 struct recv_data rdata
;
204 printf("HomeMatic OTA flasher version " VERSION
"\n\n");
208 fprintf(stderr
, "Missing firmware filename!\n\n");
211 fprintf(stderr
, "Missing serial!\n\n");
213 fprintf(stderr
, "Syntax: %s firmware.eq3 SERIALNUMBER\n\n", argv
[0]);
217 fw
= firmware_read_firmware(argv
[1], debug
);
221 hmcfgusb_set_debug(debug
);
223 memset(&rdata
, 0, sizeof(rdata
));
225 dev
= hmcfgusb_init(parse_hmcfgusb
, &rdata
);
227 fprintf(stderr
, "Can't initialize HM-CFG-USB\n");
231 if (dev
->bootloader
) {
232 fprintf(stderr
, "\nHM-CFG-USB in bootloader mode, aborting!\n");
236 printf("\nHM-CFG-USB opened\n\n");
238 memset(out
, 0, sizeof(out
));
240 hmcfgusb_send(dev
, out
, sizeof(out
), 1);
244 pfd
= hmcfgusb_poll(dev
, 1);
245 if ((pfd
< 0) && errno
) {
246 if (errno
!= ETIMEDOUT
) {
247 perror("\n\nhmcfgusb_poll");
251 if (rdata
.hmcfgusb_version
)
255 if (rdata
.hmcfgusb_version
< 0x3c7) {
256 fprintf(stderr
, "HM-CFG-USB firmware too low: %u < 967\n", rdata
.hmcfgusb_version
);
260 printf("HM-CFG-USB firmware version: %u\n", rdata
.hmcfgusb_version
);
262 if (!switch_speed(dev
, &rdata
, 10)) {
263 fprintf(stderr
, "Can't switch speed!\n");
267 printf("Waiting for device with serial %s\n", argv
[2]);
271 pfd
= hmcfgusb_poll(dev
, 1);
272 if ((pfd
< 0) && errno
) {
273 if (errno
!= ETIMEDOUT
) {
274 perror("\n\nhmcfgusb_poll");
279 if ((rdata
.message
[LEN
] == 0x14) && /* Length */
280 (rdata
.message
[MSGID
] == 0x00) && /* Message ID */
281 (rdata
.message
[CTL
] == 0x00) && /* Control Byte */
282 (rdata
.message
[TYPE
] == 0x10) && /* Messagte type: Information */
283 (DST(rdata
.message
) == 0x000000) && /* Broadcast */
284 (rdata
.message
[PAYLOAD
] == 0x00) && /* FUP? */
285 (rdata
.message
[PAYLOAD
+2] == 'E') &&
286 (rdata
.message
[PAYLOAD
+3] == 'Q')) {
287 if (!strncmp((char*)&(rdata
.message
[0x0b]), argv
[2], 10)) {
288 hmid
= SRC(rdata
.message
);
294 printf("Device with serial %s (hmid: %06x) entered firmware-update-mode\n", argv
[2], hmid
);
296 printf("Adding HMID\n");
298 memset(out
, 0, sizeof(out
));
300 out
[1] = (hmid
>> 16) & 0xff;
301 out
[2] = (hmid
>> 8) & 0xff;
302 out
[3] = hmid
& 0xff;
304 hmcfgusb_send(dev
, out
, sizeof(out
), 1);
308 printf("Initiating remote switch to 100k\n");
310 memset(out
, 0, sizeof(out
));
312 out
[MSGID
] = msgid
++;
315 SET_SRC(out
, my_hmid
);
318 memcpy(&out
[PAYLOAD
], switch_msg
, sizeof(switch_msg
));
319 SET_LEN_FROM_PAYLOADLEN(out
, sizeof(switch_msg
));
321 if (!send_hm_message(dev
, &rdata
, out
)) {
325 if (!switch_speed(dev
, &rdata
, 100)) {
326 fprintf(stderr
, "Can't switch speed!\n");
330 printf("Has the device switched?\n");
332 memset(out
, 0, sizeof(out
));
334 out
[MSGID
] = msgid
++;
337 SET_SRC(out
, my_hmid
);
340 memcpy(&out
[PAYLOAD
], switch_msg
, sizeof(switch_msg
));
341 SET_LEN_FROM_PAYLOADLEN(out
, sizeof(switch_msg
));
345 if (send_hm_message(dev
, &rdata
, out
)) {
346 /* A0A02000221B9AD00000000 */
356 if (!switch_speed(dev
, &rdata
, 10)) {
357 fprintf(stderr
, "Can't switch speed!\n");
361 } while ((!switched
) && (switchcnt
--));
364 fprintf(stderr
, "Too many errors, giving up!\n");
370 printf("Flashing %d blocks", fw
->fw_blocks
);
374 printf(": %04u/%04u %c", 0, fw
->fw_blocks
, twiddlie
[0]);
378 for (block
= 0; block
< fw
->fw_blocks
; block
++) {
381 len
= fw
->fw
[block
][2] << 8;
382 len
|= fw
->fw
[block
][3];
384 pos
= &(fw
->fw
[block
][2]);
386 len
+= 2; /* length */
389 hexdump(pos
, len
, "F> ");
402 if ((len
- (pos
- &(fw
->fw
[block
][2]))) < payloadlen
)
403 payloadlen
= (len
- (pos
- &(fw
->fw
[block
][2])));
405 if (((pos
+ payloadlen
) - &(fw
->fw
[block
][2])) == len
)
408 memset(&rdata
, 0, sizeof(rdata
));
410 memset(out
, 0, sizeof(out
));
416 SET_SRC(out
, my_hmid
);
419 memcpy(&out
[PAYLOAD
], pos
, payloadlen
);
420 SET_LEN_FROM_PAYLOADLEN(out
, payloadlen
);
422 if (send_hm_message(dev
, &rdata
, out
)) {
425 pos
= &(fw
->fw
[block
][2]);
428 fprintf(stderr
, "\nToo many errors, giving up!\n");
431 printf("Flashing %d blocks: %04u/%04u %c", fw
->fw_blocks
, block
+ 1, fw
->fw_blocks
, twiddlie
[msgnum
% sizeof(twiddlie
)]);
438 printf("\b\b\b\b\b\b\b\b\b\b\b%04u/%04u %c",
439 block
+ 1, fw
->fw_blocks
, twiddlie
[msgnum
% sizeof(twiddlie
)]);
442 } while((pos
- &(fw
->fw
[block
][2])) < len
);
450 if (!switch_speed(dev
, &rdata
, 10)) {
451 fprintf(stderr
, "Can't switch speed!\n");
455 printf("Waiting for device to reboot\n");
460 pfd
= hmcfgusb_poll(dev
, 1);
461 if ((pfd
< 0) && errno
) {
462 if (errno
!= ETIMEDOUT
) {
463 perror("\n\nhmcfgusb_poll");
467 if (rdata
.message_type
== MESSAGE_TYPE_E
) {
472 if (rdata
.message_type
== MESSAGE_TYPE_E
) {
473 printf("Device rebooted\n");