]> cvs.zerfleddert.de Git - hmcfgusb/blob - firmware.c
flash-ota: add support for directly flashing AsksinPP .hex-files
[hmcfgusb] / firmware.c
1 /* generic firmware-functions for HomeMatic
2 *
3 * Copyright (c) 2014-20 Michael Gernoth <michael@gernoth.net>
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 <errno.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <sys/time.h>
35 #include <unistd.h>
36
37 #include "util.h"
38 #include "firmware.h"
39
40 #define CRC16_INIT 0xFFFF
41 #define CRC16_POLY 0x1021
42
43 /* This might be wrong, but it works for current fw */
44 #define MAX_BLOCK_LENGTH 2048
45
46 #define HEX_BLOCK_LENGTH_328P 128
47 #define HEX_BLOCK_LENGTH_644P 256
48 #define HEX_IMAGE_SIZE_328P 0x7000
49 #define HEX_IMAGE_SIZE_644P 0xF000
50 #define HEX_IMAGE_SIZE_MAX 0xFFFF
51
52 static uint16_t crc16(uint8_t* buf, int length, uint16_t crc)
53 {
54 int i;
55 uint16_t flag;
56
57 while (length--) {
58 for (i = 0; i < 8; i++) {
59 flag = crc & 0x8000;
60 crc <<= 1;
61 if (*buf & 0x80) {
62 crc |= 1;
63 }
64 if (flag) {
65 crc ^= CRC16_POLY;
66 }
67 *buf <<= 1;
68 }
69 buf++;
70 }
71
72 return crc;
73 }
74
75 static struct firmware* firmware_read_ihex(int fd, struct firmware *fw, int atmega, int debug)
76 {
77 uint8_t buf[2*MAX_BLOCK_LENGTH];
78 uint8_t image[HEX_IMAGE_SIZE_MAX];
79 uint16_t len = 0;
80 uint16_t addr = 0;
81 uint16_t type = 0;
82 uint32_t offset = 0;
83 uint32_t image_size = HEX_IMAGE_SIZE_328P;
84 uint32_t block_length = HEX_BLOCK_LENGTH_328P;
85 int r;
86 int i;
87
88 switch (atmega) {
89 case ATMEGA_644P:
90 printf("Using Atmega644P values for direct hex flashing\n");
91 image_size = HEX_IMAGE_SIZE_644P;
92 block_length = HEX_BLOCK_LENGTH_644P;
93 break;
94 case ATMEGA_328P:
95 printf("Using Atmega328P values for direct hex flashing\n");
96 image_size = HEX_IMAGE_SIZE_328P;
97 block_length = HEX_BLOCK_LENGTH_328P;
98 break;
99 default:
100 fprintf(stderr, "Atmega-type (328P/644P) not specified for flashing hex files\n");
101 exit(EXIT_FAILURE);
102 break;
103 }
104
105 memset(image, 0xff, sizeof(image));
106
107 while (1) {
108 memset(buf, 0, sizeof(buf));
109 len = 2 /* len */ + 4 /* len */ + 2 /* type */;
110 r = read(fd, buf, len);
111 if (r < 0) {
112 perror("read");
113 exit(EXIT_FAILURE);
114 } else if (r == 0) {
115 fprintf(stderr, "EOF without EOF record, Firmware file not valid!\n");
116 exit(EXIT_FAILURE);
117 } else if (r != len) {
118 printf("can't get record information!\n");
119 exit(EXIT_FAILURE);
120 }
121
122 for (i = 0; i < r; i++) {
123 if (!validate_nibble(buf[i])) {
124 fprintf(stderr, "Firmware file not valid!\n");
125 exit(EXIT_FAILURE);
126 }
127 }
128
129 len = (ascii_to_nibble(buf[0]) & 0xf)<< 4;
130 len |= ascii_to_nibble(buf[1]) & 0xf;
131
132 addr = (ascii_to_nibble(buf[2]) & 0xf)<< 4;
133 addr |= ascii_to_nibble(buf[3]) & 0xf;
134 addr <<= 8;
135 addr |= (ascii_to_nibble(buf[4]) & 0xf)<< 4;
136 addr |= ascii_to_nibble(buf[5]) & 0xf;
137
138 type = (ascii_to_nibble(buf[6]) & 0xf)<< 4;
139 type |= ascii_to_nibble(buf[7]) & 0xf;
140
141 if (debug)
142 printf("Length: %d, Address: 0x%04x, Type: 0x%02x\n", len, addr, type);
143
144 if (len > MAX_BLOCK_LENGTH) {
145 fprintf(stderr, "Invalid block-length %u > %u for block %d!\n", len, MAX_BLOCK_LENGTH, fw->fw_blocks+1);
146 exit(EXIT_FAILURE);
147 }
148
149 if (type == 0x00) {
150 r = read(fd, buf, (len * 2) + 2 /* crc */);
151 if (r < 0) {
152 perror("read");
153 exit(EXIT_FAILURE);
154 } else if (r == 0) {
155 break;
156 } else if (r < ((len * 2) + 2)) {
157 fprintf(stderr, "short read, aborting (%d < %d)\n", r, (len * 2) + 2);
158 exit(EXIT_FAILURE);
159 }
160
161 for (i = 0; i < len * 2; i+=2) {
162 if ((!validate_nibble(buf[i])) ||
163 (!validate_nibble(buf[i+1]))) {
164 fprintf(stderr, "Firmware file not valid!\n");
165 exit(EXIT_FAILURE);
166 }
167
168 image[addr + (i/2)] = (ascii_to_nibble(buf[i]) & 0xf)<< 4;
169 image[addr + (i/2)] |= ascii_to_nibble(buf[i+1]) & 0xf;
170 }
171
172 while (1) {
173 r = read(fd, buf, 1);
174 if (r < 0) {
175 perror("read");
176 exit(EXIT_FAILURE);
177 } else if (r == 0) {
178 break;
179 } else {
180 if (buf[0] == ':') {
181 break;
182 }
183 }
184 }
185 } else if (type == 0x01) {
186 break;
187 } else {
188 fprintf(stderr, "Can't handle iHex type 0x%02x\n", type);
189 exit(EXIT_FAILURE);
190 }
191 }
192
193 image[image_size-2] = 0x00;
194 image[image_size-1] = 0x00;
195
196 while (offset < image_size) {
197 fw->fw = realloc(fw->fw, sizeof(uint8_t*) * (fw->fw_blocks + 1));
198 if (fw->fw == NULL) {
199 perror("Can't reallocate fw->fw-blocklist");
200 exit(EXIT_FAILURE);
201 }
202
203 len = block_length;
204
205 fw->fw[fw->fw_blocks] = malloc(len + 4);
206 if (fw->fw[fw->fw_blocks] == NULL) {
207 perror("Can't allocate memory for fw->fw-block");
208 exit(EXIT_FAILURE);
209 }
210
211 fw->fw[fw->fw_blocks][0] = (fw->fw_blocks >> 8) & 0xff;
212 fw->fw[fw->fw_blocks][1] = fw->fw_blocks & 0xff;
213 fw->fw[fw->fw_blocks][2] = (len >> 8) & 0xff;
214 fw->fw[fw->fw_blocks][3] = len & 0xff;
215
216 memcpy(fw->fw[fw->fw_blocks] + 4, image + offset, len);
217
218 if ((len + offset) == image_size) {
219 uint16_t crc;
220
221 crc = crc16(image, image_size, CRC16_INIT);
222
223 if (debug)
224 printf("CRC: %04x\n", crc);
225
226 fw->fw[fw->fw_blocks][len+3] = (crc >> 8) & 0xff;
227 fw->fw[fw->fw_blocks][len+2] = crc & 0xff;
228 }
229
230 fw->fw_blocks++;
231 if (debug)
232 printf("Firmware block %d with length %u read.\n", fw->fw_blocks, len);
233
234 offset += len;
235 }
236
237 if (fw->fw_blocks == 0) {
238 fprintf(stderr, "Firmware file not valid!\n");
239 exit(EXIT_FAILURE);
240 }
241
242 printf("Firmware with %d blocks successfully read.\n", fw->fw_blocks);
243
244 return fw;
245 }
246
247 struct firmware* firmware_read_firmware(char *filename, int atmega, int debug)
248 {
249 struct firmware *fw;
250 struct stat stat_buf;
251 uint8_t buf[2*MAX_BLOCK_LENGTH];
252 uint16_t len;
253 int fd;
254 int r;
255 int i;
256
257 fw = malloc(sizeof(struct firmware));
258 if (!fw) {
259 perror("malloc(fw)");
260 return NULL;
261 }
262
263 memset(fw, 0, sizeof(struct firmware));
264
265 if (stat(filename, &stat_buf) == -1) {
266 fprintf(stderr, "Can't stat %s: %s\n", filename, strerror(errno));
267 exit(EXIT_FAILURE);
268 }
269
270 fd = open(filename, O_RDONLY);
271 if (fd < 0) {
272 fprintf(stderr, "Can't open %s: %s", filename, strerror(errno));
273 exit(EXIT_FAILURE);
274 }
275
276 printf("Reading firmware from %s...\n", filename);
277
278 memset(buf, 0, sizeof(buf));
279 r = read(fd, buf, 1);
280 if (r != 1) {
281 perror("read");
282 exit(EXIT_FAILURE);
283 }
284
285 //Intel hex?
286 if (buf[0] == ':') {
287 printf("HEX file detected (AsksinPP)\n");
288 return firmware_read_ihex(fd, fw, atmega, debug);
289 }
290
291 if (lseek(fd, 0, SEEK_SET) != 0) {
292 perror("lseek");
293 exit(EXIT_FAILURE);
294 }
295
296 do {
297 memset(buf, 0, sizeof(buf));
298 r = read(fd, buf, 4);
299 if (r < 0) {
300 perror("read");
301 exit(EXIT_FAILURE);
302 } else if (r == 0) {
303 break;
304 } else if (r != 4) {
305 printf("can't get length information!\n");
306 exit(EXIT_FAILURE);
307 }
308
309 for (i = 0; i < r; i++) {
310 if (!validate_nibble(buf[i])) {
311 fprintf(stderr, "Firmware file not valid!\n");
312 exit(EXIT_FAILURE);
313 }
314 }
315
316 len = (ascii_to_nibble(buf[0]) & 0xf)<< 4;
317 len |= ascii_to_nibble(buf[1]) & 0xf;
318 len <<= 8;
319 len |= (ascii_to_nibble(buf[2]) & 0xf)<< 4;
320 len |= ascii_to_nibble(buf[3]) & 0xf;
321
322 if (len > MAX_BLOCK_LENGTH) {
323 fprintf(stderr, "Invalid block-length %u > %u for block %d!\n", len, MAX_BLOCK_LENGTH, fw->fw_blocks+1);
324 exit(EXIT_FAILURE);
325 }
326
327 fw->fw = realloc(fw->fw, sizeof(uint8_t*) * (fw->fw_blocks + 1));
328 if (fw->fw == NULL) {
329 perror("Can't reallocate fw->fw-blocklist");
330 exit(EXIT_FAILURE);
331 }
332
333 fw->fw[fw->fw_blocks] = malloc(len + 4);
334 if (fw->fw[fw->fw_blocks] == NULL) {
335 perror("Can't allocate memory for fw->fw-block");
336 exit(EXIT_FAILURE);
337 }
338
339 fw->fw[fw->fw_blocks][0] = (fw->fw_blocks >> 8) & 0xff;
340 fw->fw[fw->fw_blocks][1] = fw->fw_blocks & 0xff;
341 fw->fw[fw->fw_blocks][2] = (len >> 8) & 0xff;
342 fw->fw[fw->fw_blocks][3] = len & 0xff;
343
344 r = read(fd, buf, len * 2);
345 if (r < 0) {
346 perror("read");
347 exit(EXIT_FAILURE);
348 } else if (r < len * 2) {
349 fprintf(stderr, "short read, aborting (%d < %d)\n", r, len * 2);
350 exit(EXIT_FAILURE);
351 }
352
353 for (i = 0; i < r; i+=2) {
354 if ((!validate_nibble(buf[i])) ||
355 (!validate_nibble(buf[i+1]))) {
356 fprintf(stderr, "Firmware file not valid!\n");
357 exit(EXIT_FAILURE);
358 }
359
360 fw->fw[fw->fw_blocks][(i/2) + 4] = (ascii_to_nibble(buf[i]) & 0xf)<< 4;
361 fw->fw[fw->fw_blocks][(i/2) + 4] |= ascii_to_nibble(buf[i+1]) & 0xf;
362 }
363
364 fw->fw_blocks++;
365 if (debug)
366 printf("Firmware block %d with length %u read.\n", fw->fw_blocks, len);
367 } while(r > 0);
368
369 if (fw->fw_blocks == 0) {
370 fprintf(stderr, "Firmware file not valid!\n");
371 exit(EXIT_FAILURE);
372 }
373
374 printf("Firmware with %d blocks successfully read.\n", fw->fw_blocks);
375
376 return fw;
377 }
378
379 void firmware_free(struct firmware *fw)
380 {
381 int i;
382
383 for (i = 0; i < fw->fw_blocks; i++)
384 free(fw->fw[i]);
385
386 free(fw->fw);
387 free(fw);
388 }
Impressum, Datenschutz