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>
53 enum message_type message_type
;
56 uint16_t hmcfgusb_version
;
59 static int parse_hmcfgusb(uint8_t *buf
, int buf_len
, void *data
)
61 struct recv_data
*rdata
= data
;
69 ((buf
[0x11] == ((hmid
>> 16) & 0xff)) &&
70 (buf
[0x12] == ((hmid
>> 8) & 0xff)) &&
71 (buf
[0x13] == (hmid
& 0xff)))) {
72 memset(rdata
->message
, 0, sizeof(rdata
->message
));
73 memcpy(rdata
->message
, buf
+ 0x0d, buf
[0x0d] + 1);
74 rdata
->message_type
= MESSAGE_TYPE_E
;
78 memset(rdata
->message
, 0, sizeof(rdata
->message
));
79 memcpy(rdata
->message
, buf
+ 0x0e, buf
[0x0e] + 1);
80 rdata
->status
= (buf
[5] << 8) | buf
[6];
81 rdata
->message_type
= MESSAGE_TYPE_R
;
84 rdata
->speed
= buf
[1];
87 rdata
->hmcfgusb_version
= (buf
[11] << 8) | buf
[12];
99 int send_hm_message(struct hmcfgusb_dev
*dev
, struct recv_data
*rdata
, uint8_t *msg
)
101 static uint32_t id
= 1;
106 if (gettimeofday(&tv
, NULL
) == -1) {
107 perror("gettimeofay");
111 memset(out
, 0, sizeof(out
));
114 out
[1] = (id
>> 24) & 0xff;
115 out
[2] = (id
>> 16) & 0xff;
116 out
[3] = (id
>> 8) & 0xff;
119 out
[11] = (tv
.tv_usec
>> 24) & 0xff;
120 out
[12] = (tv
.tv_usec
>> 16) & 0xff;
121 out
[13] = (tv
.tv_usec
>> 8) & 0xff;
122 out
[14] = tv
.tv_usec
& 0xff;
125 memcpy(&out
[0x0f], msg
, msg
[0] + 1);
127 memset(rdata
, 0, sizeof(struct recv_data
));
128 hmcfgusb_send(dev
, out
, sizeof(out
), 1);
131 if (rdata
->message_type
== MESSAGE_TYPE_R
) {
132 if (((rdata
->status
& 0xff) == 0x01) ||
133 ((rdata
->status
& 0xff) == 0x02)) {
136 fprintf(stderr
, "\nInvalid status: %04x\n", rdata
->status
);
141 pfd
= hmcfgusb_poll(dev
, 1);
142 if ((pfd
< 0) && errno
) {
143 if (errno
!= ETIMEDOUT
) {
144 perror("\n\nhmcfgusb_poll");
154 static int switch_speed(struct hmcfgusb_dev
*dev
, struct recv_data
*rdata
, uint8_t speed
)
159 printf("Entering %uk-mode\n", speed
);
161 memset(out
, 0, sizeof(out
));
165 hmcfgusb_send(dev
, out
, sizeof(out
), 1);
169 pfd
= hmcfgusb_poll(dev
, 1);
170 if ((pfd
< 0) && errno
) {
171 if (errno
!= ETIMEDOUT
) {
172 perror("\n\nhmcfgusb_poll");
176 if (rdata
->speed
== speed
)
183 int main(int argc
, char **argv
)
185 const char twiddlie
[] = { '-', '\\', '|', '/' };
186 const uint8_t switch_msg
[] = { 0x10, 0x5B, 0x11, 0xF8, 0x15, 0x47 };
187 struct hmcfgusb_dev
*dev
;
188 struct recv_data rdata
;
202 printf("HomeMatic OTA flasher version " VERSION
"\n\n");
206 fprintf(stderr
, "Missing firmware filename!\n\n");
209 fprintf(stderr
, "Missing serial!\n\n");
211 fprintf(stderr
, "Syntax: %s firmware.eq3 SERIALNUMBER\n\n", argv
[0]);
215 fw
= firmware_read_firmware(argv
[1], debug
);
219 hmcfgusb_set_debug(debug
);
221 memset(&rdata
, 0, sizeof(rdata
));
223 dev
= hmcfgusb_init(parse_hmcfgusb
, &rdata
);
225 fprintf(stderr
, "Can't initialize HM-CFG-USB\n");
229 if (dev
->bootloader
) {
230 fprintf(stderr
, "\nHM-CFG-USB not in bootloader mode, aborting!\n");
234 printf("\nHM-CFG-USB opened\n\n");
236 memset(out
, 0, sizeof(out
));
238 hmcfgusb_send(dev
, out
, sizeof(out
), 1);
242 pfd
= hmcfgusb_poll(dev
, 1);
243 if ((pfd
< 0) && errno
) {
244 if (errno
!= ETIMEDOUT
) {
245 perror("\n\nhmcfgusb_poll");
249 if (rdata
.hmcfgusb_version
)
253 if (rdata
.hmcfgusb_version
< 0x3c7) {
254 fprintf(stderr
, "HM-CFG-USB firmware too low: %u < 967\n", rdata
.hmcfgusb_version
);
258 printf("HM-CFG-USB firmware version: %u\n", rdata
.hmcfgusb_version
);
260 if (!switch_speed(dev
, &rdata
, 10)) {
261 fprintf(stderr
, "Can't switch speed!\n");
265 printf("Waiting for device with serial %s\n", argv
[2]);
269 pfd
= hmcfgusb_poll(dev
, 1);
270 if ((pfd
< 0) && errno
) {
271 if (errno
!= ETIMEDOUT
) {
272 perror("\n\nhmcfgusb_poll");
277 if ((rdata
.message
[LEN
] == 0x14) && /* Length */
278 (rdata
.message
[MSGID
] == 0x00) && /* Message ID */
279 (rdata
.message
[CTL
] == 0x00) && /* Control Byte */
280 (rdata
.message
[TYPE
] == 0x10) && /* Messagte type: Information */
281 (DST(rdata
.message
) == 0x000000) && /* Broadcast */
282 (rdata
.message
[PAYLOAD
] == 0x00) && /* FUP? */
283 (rdata
.message
[PAYLOAD
+2] == 'E') &&
284 (rdata
.message
[PAYLOAD
+3] == 'Q')) {
285 if (!strncmp((char*)&(rdata
.message
[0x0b]), argv
[2], 10)) {
286 hmid
= SRC(rdata
.message
);
292 printf("Device with serial %s (hmid: %06x) entered firmware-update-mode\n", argv
[2], hmid
);
294 printf("Adding HMID\n");
296 memset(out
, 0, sizeof(out
));
298 out
[1] = (hmid
>> 16) & 0xff;
299 out
[2] = (hmid
>> 8) & 0xff;
300 out
[3] = hmid
& 0xff;
302 hmcfgusb_send(dev
, out
, sizeof(out
), 1);
306 printf("Initiating remote switch to 100k\n");
308 memset(out
, 0, sizeof(out
));
310 out
[MSGID
] = msgid
++;
313 SET_SRC(out
, 0x000000);
316 memcpy(&out
[PAYLOAD
], switch_msg
, sizeof(switch_msg
));
317 SET_LEN_FROM_PAYLOADLEN(out
, sizeof(switch_msg
));
319 if (!send_hm_message(dev
, &rdata
, out
)) {
323 if (!switch_speed(dev
, &rdata
, 100)) {
324 fprintf(stderr
, "Can't switch speed!\n");
328 printf("Has the device switched?\n");
330 memset(out
, 0, sizeof(out
));
332 out
[MSGID
] = msgid
++;
335 SET_SRC(out
, 0x000000);
338 memcpy(&out
[PAYLOAD
], switch_msg
, sizeof(switch_msg
));
339 SET_LEN_FROM_PAYLOADLEN(out
, sizeof(switch_msg
));
343 if (send_hm_message(dev
, &rdata
, out
)) {
344 /* A0A02000221B9AD00000000 */
354 if (!switch_speed(dev
, &rdata
, 10)) {
355 fprintf(stderr
, "Can't switch speed!\n");
359 } while ((!switched
) && (switchcnt
--));
362 fprintf(stderr
, "Too many errors, giving up!\n");
368 printf("Flashing %d blocks", fw
->fw_blocks
);
372 printf(": %04u/%04u %c", 0, fw
->fw_blocks
, twiddlie
[0]);
376 for (block
= 0; block
< fw
->fw_blocks
; block
++) {
379 len
= fw
->fw
[block
][2] << 8;
380 len
|= fw
->fw
[block
][3];
382 pos
= &(fw
->fw
[block
][2]);
384 len
+= 2; /* length */
387 hexdump(pos
, len
, "F> ");
400 if ((len
- (pos
- &(fw
->fw
[block
][2]))) < payloadlen
)
401 payloadlen
= (len
- (pos
- &(fw
->fw
[block
][2])));
403 if (((pos
+ payloadlen
) - &(fw
->fw
[block
][2])) == len
)
406 memset(&rdata
, 0, sizeof(rdata
));
408 memset(out
, 0, sizeof(out
));
414 SET_SRC(out
, 0x000000);
417 memcpy(&out
[PAYLOAD
], pos
, payloadlen
);
418 SET_LEN_FROM_PAYLOADLEN(out
, payloadlen
);
420 if (send_hm_message(dev
, &rdata
, out
)) {
423 pos
= &(fw
->fw
[block
][2]);
426 fprintf(stderr
, "\nToo many errors, giving up!\n");
429 printf("Flashing %d blocks: %04u/%04u %c", fw
->fw_blocks
, block
+ 1, fw
->fw_blocks
, twiddlie
[msgnum
% sizeof(twiddlie
)]);
436 printf("\b\b\b\b\b\b\b\b\b\b\b%04u/%04u %c",
437 block
+ 1, fw
->fw_blocks
, twiddlie
[msgnum
% sizeof(twiddlie
)]);
440 } while((pos
- &(fw
->fw
[block
][2])) < len
);
448 if (!switch_speed(dev
, &rdata
, 10)) {
449 fprintf(stderr
, "Can't switch speed!\n");
453 printf("Waiting for device to reboot\n");
458 pfd
= hmcfgusb_poll(dev
, 1);
459 if ((pfd
< 0) && errno
) {
460 if (errno
!= ETIMEDOUT
) {
461 perror("\n\nhmcfgusb_poll");
465 if (rdata
.message_type
== MESSAGE_TYPE_E
) {
470 if (rdata
.message_type
== MESSAGE_TYPE_E
) {
471 printf("Device rebooted\n");