]> cvs.zerfleddert.de Git - hmcfgusb/blob - flash-hmcfgusb.c
cleanup flasher: parse and validate firmware before flashing
[hmcfgusb] / flash-hmcfgusb.c
1 /* flasher for HM-CFG-USB
2 *
3 * Copyright (c) 2013 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 <poll.h>
31 #include <errno.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <sys/time.h>
36 #include <libusb-1.0/libusb.h>
37
38 #include "hexdump.h"
39 #include "version.h"
40 #include "hmcfgusb.h"
41
42 /* This might be wrong, but it works for current fw */
43 #define MAX_BLOCK_LENGTH 512
44
45 struct recv_data {
46 int ack;
47 };
48
49 static int parse_hmcfgusb(uint8_t *buf, int buf_len, void *data)
50 {
51 struct recv_data *rdata = data;
52
53 if (buf_len != 1)
54 return 1;
55
56 rdata->ack = buf[0];
57
58 return 1;
59 }
60
61 static uint8_t ascii_to_nibble(uint8_t a)
62 {
63 uint8_t c = 0x00;
64
65 if ((a >= '0') && (a <= '9')) {
66 c = a - '0';
67 } else if ((a >= 'A') && (a <= 'F')) {
68 c = (a - 'A') + 10;
69 } else if ((a >= 'a') && (a <= 'f')) {
70 c = (a - 'a') + 10;
71 }
72
73 return c;
74 }
75
76 static int validate_nibble(uint8_t a)
77 {
78 if (((a >= '0') && (a <= '9')) ||
79 ((a >= 'A') && (a <= 'F')) ||
80 ((a >= 'a') && (a <= 'f')))
81 return 1;
82
83 return 0;
84 }
85
86 int main(int argc, char **argv)
87 {
88 const char twiddlie[] = { '-', '\\', '|', '/' };
89 struct hmcfgusb_dev *dev;
90 struct recv_data rdata;
91 struct stat stat_buf;
92 uint8_t buf[4096];
93 uint16_t len;
94 uint8_t **fw = NULL;
95 int fw_blocks = 0;
96 int block;
97 int fd;
98 int pfd;
99 int r;
100 int i;
101 int debug = 0;
102
103 printf("HM-CFG-USB flasher version " VERSION "\n\n");
104
105 if (argc != 2) {
106 if (argc == 1)
107 fprintf(stderr, "Missing firmware filename!\n\n");
108
109 fprintf(stderr, "Syntax: %s hmusbif.enc\n\n", argv[0]);
110 exit(EXIT_FAILURE);
111 }
112
113 if (stat(argv[1], &stat_buf) == -1) {
114 fprintf(stderr, "Can't stat %s: %s\n", argv[1], strerror(errno));
115 exit(EXIT_FAILURE);
116 }
117
118 fd = open(argv[1], O_RDONLY);
119 if (fd < 0) {
120 fprintf(stderr, "Can't open %s: %s", argv[1], strerror(errno));
121 exit(EXIT_FAILURE);
122 }
123
124 printf("Reading firmware from %s...\n", argv[1]);
125 do {
126 memset(buf, 0, sizeof(buf));
127 r = read(fd, buf, 4);
128 if (r < 0) {
129 perror("read");
130 exit(EXIT_FAILURE);
131 } else if (r == 0) {
132 break;
133 } else if (r != 4) {
134 printf("can't get length information!\n");
135 exit(EXIT_FAILURE);
136 }
137
138 for (i = 0; i < r; i++) {
139 if (!validate_nibble(buf[i])) {
140 fprintf(stderr, "Firmware file not valid!\n");
141 exit(EXIT_FAILURE);
142 }
143 }
144
145 len = (ascii_to_nibble(buf[0]) & 0xf)<< 4;
146 len |= ascii_to_nibble(buf[1]) & 0xf;
147 len <<= 8;
148 len |= (ascii_to_nibble(buf[2]) & 0xf)<< 4;
149 len |= ascii_to_nibble(buf[3]) & 0xf;
150
151 /* This might be wrong, but it works for current fw */
152 if (len > MAX_BLOCK_LENGTH) {
153 fprintf(stderr, "Invalid block-length %u > %u for block %d!\n", len, MAX_BLOCK_LENGTH, fw_blocks+1);
154 exit(EXIT_FAILURE);
155 }
156
157 fw = realloc(fw, sizeof(uint8_t*) * (fw_blocks + 1));
158 if (fw == NULL) {
159 perror("Can't reallocate fw-blocklist");
160 exit(EXIT_FAILURE);
161 }
162
163 fw[fw_blocks] = malloc(len + 4);
164 if (fw[fw_blocks] == NULL) {
165 perror("Can't allocate memory for fw-block");
166 exit(EXIT_FAILURE);
167 }
168
169 fw[fw_blocks][0] = (fw_blocks >> 8) & 0xff;
170 fw[fw_blocks][1] = fw_blocks & 0xff;
171 fw[fw_blocks][2] = (len >> 8) & 0xff;
172 fw[fw_blocks][3] = len & 0xff;
173
174 r = read(fd, buf, len * 2);
175 if (r < 0) {
176 perror("read");
177 exit(EXIT_FAILURE);
178 } else if (r < len * 2) {
179 fprintf(stderr, "short read, aborting (%d < %d)\n", r, len * 2);
180 exit(EXIT_FAILURE);
181 }
182
183 for (i = 0; i < r; i+=2) {
184 if ((!validate_nibble(buf[i])) ||
185 (!validate_nibble(buf[i+1]))) {
186 fprintf(stderr, "Firmware file not valid!\n");
187 exit(EXIT_FAILURE);
188 }
189
190 fw[fw_blocks][(i/2) + 4] = (ascii_to_nibble(buf[i]) & 0xf)<< 4;
191 fw[fw_blocks][(i/2) + 4] |= ascii_to_nibble(buf[i+1]) & 0xf;
192 }
193
194 fw_blocks++;
195 if (debug)
196 printf("Firmware block %d with length %u read.\n", fw_blocks, len);
197 } while(r > 0);
198
199 if (fw_blocks == 0) {
200 fprintf(stderr, "Firmware file not valid!\n");
201 exit(EXIT_FAILURE);
202 }
203
204 printf("Firmware with %d blocks successfully read.\n", fw_blocks);
205
206 hmcfgusb_set_debug(debug);
207
208 memset(&rdata, 0, sizeof(rdata));
209
210 dev = hmcfgusb_init(parse_hmcfgusb, &rdata);
211 if (!dev) {
212 fprintf(stderr, "Can't initialize HM-CFG-USB\n");
213 exit(EXIT_FAILURE);
214 }
215
216 if (!dev->bootloader) {
217 fprintf(stderr, "\nHM-CFG-USB not in bootloader mode, entering bootloader.\n");
218 hmcfgusb_enter_bootloader(dev);
219 fprintf(stderr, "\nWaiting for device to reappear...\n");
220
221 do {
222 sleep(1);
223 } while ((dev = hmcfgusb_init(parse_hmcfgusb, &rdata)) == NULL);
224
225 if (!dev->bootloader) {
226 fprintf(stderr, "Can't enter bootloader, giving up!\n");
227 exit(EXIT_FAILURE);
228 }
229 }
230
231 printf("\nHM-CFG-USB opened.\n\n");
232
233
234 printf("Flasing %d blocks", fw_blocks);
235 if (debug) {
236 printf("\n");
237 } else {
238 printf(": %c", twiddlie[0]);
239 fflush(stdout);
240 }
241
242 for (block = 0; block < fw_blocks; block++) {
243 len = fw[block][2] << 8;
244 len |= fw[block][3];
245
246 len += 4; /* block nr., length */
247
248 if (debug)
249 hexdump(fw[block], len, "F> ");
250
251 rdata.ack = 0;
252 if (!hmcfgusb_send(dev, fw[block], len, 0)) {
253 perror("\n\nhmcfgusb_send");
254 exit(EXIT_FAILURE);
255 }
256
257 if (debug)
258 printf("Waiting for ack...\n");
259 do {
260 errno = 0;
261 pfd = hmcfgusb_poll(dev, 1);
262 if ((pfd < 0) && errno) {
263 perror("\n\nhmcfgusb_poll");
264 exit(EXIT_FAILURE);
265 }
266 if (rdata.ack) {
267 break;
268 }
269 } while (pfd < 0);
270
271 if (rdata.ack == 2) {
272 printf("\n\nFirmware update successfull!\n");
273 break;
274 }
275
276 if (rdata.ack != 1) {
277 fprintf(stderr, "\n\nError flashing block %d, status: %u\n", block, rdata.ack);
278 exit(EXIT_FAILURE);
279 }
280
281 if (!debug) {
282 printf("\b%c", twiddlie[block % sizeof(twiddlie)]);
283 fflush(stdout);
284 }
285 free(fw[block]);
286 }
287
288 free(fw);
289
290 hmcfgusb_close(dev);
291
292 return EXIT_SUCCESS;
293 }
Impressum, Datenschutz