cleanup flasher: parse and validate firmware before flashing
[hmcfgusb] / flash-hmcfgusb.c
CommitLineData
19cb9745 1/* flasher for HM-CFG-USB
9fb0f4d2
MG
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"
19cb9745 39#include "version.h"
9fb0f4d2
MG
40#include "hmcfgusb.h"
41
19cb9745
MG
42/* This might be wrong, but it works for current fw */
43#define MAX_BLOCK_LENGTH 512
44
9fb0f4d2 45struct recv_data {
b5e57d26 46 int ack;
9fb0f4d2
MG
47};
48
49static int parse_hmcfgusb(uint8_t *buf, int buf_len, void *data)
50{
b5e57d26
MG
51 struct recv_data *rdata = data;
52
53 if (buf_len != 1)
9fb0f4d2
MG
54 return 1;
55
b5e57d26 56 rdata->ack = buf[0];
9fb0f4d2
MG
57
58 return 1;
59}
60
61static 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
19cb9745
MG
76static 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
9fb0f4d2
MG
86int main(int argc, char **argv)
87{
19cb9745 88 const char twiddlie[] = { '-', '\\', '|', '/' };
9fb0f4d2
MG
89 struct hmcfgusb_dev *dev;
90 struct recv_data rdata;
19cb9745 91 struct stat stat_buf;
04e76de6 92 uint8_t buf[4096];
19cb9745
MG
93 uint16_t len;
94 uint8_t **fw = NULL;
95 int fw_blocks = 0;
96 int block;
9fb0f4d2 97 int fd;
b5e57d26 98 int pfd;
9fb0f4d2
MG
99 int r;
100 int i;
b5e57d26 101 int debug = 0;
9fb0f4d2 102
19cb9745 103 printf("HM-CFG-USB flasher version " VERSION "\n\n");
9fb0f4d2 104
19cb9745
MG
105 if (argc != 2) {
106 if (argc == 1)
107 fprintf(stderr, "Missing firmware filename!\n\n");
9fb0f4d2 108
19cb9745 109 fprintf(stderr, "Syntax: %s hmusbif.enc\n\n", argv[0]);
b5e57d26
MG
110 exit(EXIT_FAILURE);
111 }
112
19cb9745
MG
113 if (stat(argv[1], &stat_buf) == -1) {
114 fprintf(stderr, "Can't stat %s: %s\n", argv[1], strerror(errno));
9fb0f4d2
MG
115 exit(EXIT_FAILURE);
116 }
9fb0f4d2 117
19cb9745
MG
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);
9fb0f4d2
MG
122 }
123
19cb9745 124 printf("Reading firmware from %s...\n", argv[1]);
9fb0f4d2
MG
125 do {
126 memset(buf, 0, sizeof(buf));
04e76de6 127 r = read(fd, buf, 4);
9fb0f4d2
MG
128 if (r < 0) {
129 perror("read");
130 exit(EXIT_FAILURE);
131 } else if (r == 0) {
132 break;
04e76de6
MG
133 } else if (r != 4) {
134 printf("can't get length information!\n");
135 exit(EXIT_FAILURE);
9fb0f4d2 136 }
04e76de6 137
19cb9745
MG
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
04e76de6
MG
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
19cb9745
MG
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
04e76de6
MG
174 r = read(fd, buf, len * 2);
175 if (r < 0) {
176 perror("read");
177 exit(EXIT_FAILURE);
178 } else if (r < len * 2) {
19cb9745
MG
179 fprintf(stderr, "short read, aborting (%d < %d)\n", r, len * 2);
180 exit(EXIT_FAILURE);
04e76de6
MG
181 }
182
9fb0f4d2 183 for (i = 0; i < r; i+=2) {
19cb9745
MG
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;
9fb0f4d2 192 }
19cb9745
MG
193
194 fw_blocks++;
b5e57d26 195 if (debug)
19cb9745
MG
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> ");
b5e57d26
MG
250
251 rdata.ack = 0;
19cb9745
MG
252 if (!hmcfgusb_send(dev, fw[block], len, 0)) {
253 perror("\n\nhmcfgusb_send");
b5e57d26
MG
254 exit(EXIT_FAILURE);
255 }
256
19cb9745
MG
257 if (debug)
258 printf("Waiting for ack...\n");
b5e57d26
MG
259 do {
260 errno = 0;
261 pfd = hmcfgusb_poll(dev, 1);
262 if ((pfd < 0) && errno) {
19cb9745 263 perror("\n\nhmcfgusb_poll");
b5e57d26
MG
264 exit(EXIT_FAILURE);
265 }
266 if (rdata.ack) {
267 break;
268 }
269 } while (pfd < 0);
270
271 if (rdata.ack == 2) {
19cb9745 272 printf("\n\nFirmware update successfull!\n");
b5e57d26
MG
273 break;
274 }
19cb9745
MG
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);
9fb0f4d2
MG
289
290 hmcfgusb_close(dev);
291
292 return EXIT_SUCCESS;
293}
Impressum, Datenschutz