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
;
58 static int parse_hmcfgusb(uint8_t *buf
, int buf_len
, void *data
)
60 struct recv_data
*rdata
= data
;
68 ((buf
[0x11] == ((hmid
>> 16) & 0xff)) &&
69 (buf
[0x12] == ((hmid
>> 8) & 0xff)) &&
70 (buf
[0x13] == (hmid
& 0xff)))) {
71 memset(rdata
->message
, 0, sizeof(rdata
->message
));
72 memcpy(rdata
->message
, buf
+ 0x0d, buf
[0x0d] + 1);
73 rdata
->message_type
= MESSAGE_TYPE_E
;
77 memset(rdata
->message
, 0, sizeof(rdata
->message
));
78 memcpy(rdata
->message
, buf
+ 0x0e, buf
[0x0e] + 1);
79 rdata
->status
= (buf
[5] << 8) | buf
[6];
80 rdata
->message_type
= MESSAGE_TYPE_R
;
83 rdata
->speed
= buf
[1];
95 int send_hm_message(struct hmcfgusb_dev
*dev
, struct recv_data
*rdata
, uint8_t *msg
)
97 static uint32_t id
= 1;
102 if (gettimeofday(&tv
, NULL
) == -1) {
103 perror("gettimeofay");
107 memset(out
, 0, sizeof(out
));
110 out
[1] = (id
>> 24) & 0xff;
111 out
[2] = (id
>> 16) & 0xff;
112 out
[3] = (id
>> 8) & 0xff;
115 out
[11] = (tv
.tv_usec
>> 24) & 0xff;
116 out
[12] = (tv
.tv_usec
>> 16) & 0xff;
117 out
[13] = (tv
.tv_usec
>> 8) & 0xff;
118 out
[14] = tv
.tv_usec
& 0xff;
121 memcpy(&out
[0x0f], msg
, msg
[0] + 1);
123 memset(rdata
, 0, sizeof(struct recv_data
));
124 hmcfgusb_send(dev
, out
, sizeof(out
), 2);
127 if (rdata
->message_type
== MESSAGE_TYPE_R
) {
128 if (((rdata
->status
& 0xff) == 0x01) ||
129 ((rdata
->status
& 0xff) == 0x02)) {
132 fprintf(stderr
, "\n\nInvalid status: %04x\n\n", rdata
->status
);
137 pfd
= hmcfgusb_poll(dev
, 1);
138 if ((pfd
< 0) && errno
) {
139 if (errno
!= ETIMEDOUT
) {
140 perror("\n\nhmcfgusb_poll");
150 int main(int argc
, char **argv
)
152 const char twiddlie
[] = { '-', '\\', '|', '/' };
153 const uint8_t switch_msg
[] = { 0x10, 0x5B, 0x11, 0xF8, 0x15, 0x47 };
154 struct hmcfgusb_dev
*dev
;
155 struct recv_data rdata
;
168 printf("HomeMatic OTA flasher version " VERSION
"\n\n");
172 fprintf(stderr
, "Missing firmware filename!\n\n");
175 fprintf(stderr
, "Missing serial!\n\n");
177 fprintf(stderr
, "Syntax: %s firmware.eq3 SERIALNUMBER\n\n", argv
[0]);
181 fw
= firmware_read_firmware(argv
[1], debug
);
185 hmcfgusb_set_debug(debug
);
187 memset(&rdata
, 0, sizeof(rdata
));
189 dev
= hmcfgusb_init(parse_hmcfgusb
, &rdata
);
191 fprintf(stderr
, "Can't initialize HM-CFG-USB\n");
195 if (dev
->bootloader
) {
196 fprintf(stderr
, "\nHM-CFG-USB not in bootloader mode, aborting!\n");
200 printf("\nHM-CFG-USB opened\n\n");
202 printf("Entering 10k-mode\n");
204 memset(out
, 0, sizeof(out
));
207 hmcfgusb_send(dev
, out
, sizeof(out
), 1);
211 pfd
= hmcfgusb_poll(dev
, 1);
212 if ((pfd
< 0) && errno
) {
213 if (errno
!= ETIMEDOUT
) {
214 perror("\n\nhmcfgusb_poll");
218 if (rdata
.speed
== 10)
222 printf("Waiting for device with serial %s\n", argv
[2]);
226 pfd
= hmcfgusb_poll(dev
, 1);
227 if ((pfd
< 0) && errno
) {
228 if (errno
!= ETIMEDOUT
) {
229 perror("\n\nhmcfgusb_poll");
234 if ((rdata
.message
[LEN
] == 0x14) && /* Length */
235 (rdata
.message
[MSGID
] == 0x00) && /* Message ID */
236 (rdata
.message
[CTL
] == 0x00) && /* Control Byte */
237 (rdata
.message
[TYPE
] == 0x10) && /* Messagte type: Information */
238 (DST(rdata
.message
) == 0x000000) && /* Broadcast */
239 (rdata
.message
[PAYLOAD
] == 0x00) && /* FUP? */
240 (rdata
.message
[PAYLOAD
+2] == 'E') &&
241 (rdata
.message
[PAYLOAD
+3] == 'Q')) {
242 if (!strncmp((char*)&(rdata
.message
[0x0b]), argv
[2], 10)) {
243 hmid
= SRC(rdata
.message
);
249 printf("Device with serial %s (hmid: %06x) entered firmware-update-mode\n", argv
[2], hmid
);
251 printf("Adding HMID\n");
253 memset(out
, 0, sizeof(out
));
255 out
[1] = (hmid
>> 16) & 0xff;
256 out
[2] = (hmid
>> 8) & 0xff;
257 out
[3] = hmid
& 0xff;
259 hmcfgusb_send(dev
, out
, sizeof(out
), 2);
262 printf("Initiating remote switch to 100k\n");
264 memset(out
, 0, sizeof(out
));
266 out
[MSGID
] = msgid
++;
269 SET_SRC(out
, 0x000000);
272 memcpy(&out
[PAYLOAD
], switch_msg
, sizeof(switch_msg
));
273 SET_LEN_FROM_PAYLOADLEN(out
, sizeof(switch_msg
));
275 if (!send_hm_message(dev
, &rdata
, out
)) {
279 printf("Entering 100k-mode\n");
281 memset(out
, 0, sizeof(out
));
285 hmcfgusb_send(dev
, out
, sizeof(out
), 2);
289 pfd
= hmcfgusb_poll(dev
, 1);
290 if ((pfd
< 0) && errno
) {
291 if (errno
!= ETIMEDOUT
) {
292 perror("\n\nhmcfgusb_poll");
296 if (rdata
.speed
== 100)
300 printf("Has the device switched?\n");
302 memset(out
, 0, sizeof(out
));
304 out
[MSGID
] = msgid
++;
307 SET_SRC(out
, 0x000000);
310 memcpy(&out
[PAYLOAD
], switch_msg
, sizeof(switch_msg
));
311 SET_LEN_FROM_PAYLOADLEN(out
, sizeof(switch_msg
));
315 if (send_hm_message(dev
, &rdata
, out
)) {
316 /* A0A02000221B9AD00000000 */
324 printf("Entering 10k-mode\n");
326 memset(out
, 0, sizeof(out
));
329 hmcfgusb_send(dev
, out
, sizeof(out
), 1);
333 pfd
= hmcfgusb_poll(dev
, 1);
334 if ((pfd
< 0) && errno
) {
335 if (errno
!= ETIMEDOUT
) {
336 perror("\n\nhmcfgusb_poll");
340 if (rdata
.speed
== 10)
347 printf("Initiating firmware upload!\n");
349 printf("Flashing %d blocks", fw
->fw_blocks
);
353 printf(": %04u/%04u %c", 0, fw
->fw_blocks
, twiddlie
[0]);
357 for (block
= 0; block
< fw
->fw_blocks
; block
++) {
360 len
= fw
->fw
[block
][2] << 8;
361 len
|= fw
->fw
[block
][3];
363 pos
= &(fw
->fw
[block
][2]);
365 len
+= 2; /* length */
368 hexdump(pos
, len
, "F> ");
381 if ((len
- (pos
- &(fw
->fw
[block
][2]))) < payloadlen
)
382 payloadlen
= (len
- (pos
- &(fw
->fw
[block
][2])));
384 if (((pos
+ payloadlen
) - &(fw
->fw
[block
][2])) == len
)
387 memset(&rdata
, 0, sizeof(rdata
));
389 memset(out
, 0, sizeof(out
));
391 out
[MSGID
] = msgid
++;
395 SET_SRC(out
, 0x000000);
398 memcpy(&out
[PAYLOAD
], pos
, payloadlen
);
399 SET_LEN_FROM_PAYLOADLEN(out
, payloadlen
);
401 if (send_hm_message(dev
, &rdata
, out
)) {
404 pos
= &(fw
->fw
[block
][2]);
407 fprintf(stderr
, "\nToo many errors, giving up!\n");
410 printf("Flashing %d blocks: %04u/%04u %c", fw
->fw_blocks
, block
+ 1, fw
->fw_blocks
, twiddlie
[msgnum
% sizeof(twiddlie
)]);
417 printf("\b\b\b\b\b\b\b\b\b\b\b%04u/%04u %c",
418 block
+ 1, fw
->fw_blocks
, twiddlie
[msgnum
% sizeof(twiddlie
)]);
421 } while((pos
- &(fw
->fw
[block
][2])) < len
);
426 printf("Entering 10k-mode\n");
428 memset(out
, 0, sizeof(out
));
431 hmcfgusb_send(dev
, out
, sizeof(out
), 1);
435 pfd
= hmcfgusb_poll(dev
, 1);
436 if ((pfd
< 0) && errno
) {
437 if (errno
!= ETIMEDOUT
) {
438 perror("\n\nhmcfgusb_poll");
442 if (rdata
.speed
== 10)
446 printf("Waiting for device to reboot\n");
451 pfd
= hmcfgusb_poll(dev
, 1);
452 if ((pfd
< 0) && errno
) {
453 if (errno
!= ETIMEDOUT
) {
454 perror("\n\nhmcfgusb_poll");
458 if (rdata
.message_type
== MESSAGE_TYPE_E
) {
463 if (rdata
.message_type
== MESSAGE_TYPE_E
) {
464 printf("Device rebooted\n");