firmware-utils/mksercommfw: fix build with clang/macOS
[openwrt/openwrt.git] / tools / firmware-utils / src / edimax_fw_header.c
1 /*
2 * Copyright (C) 2014 Gabor Juhos <juhosg@openwrt.org>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 as published
6 * by the Free Software Foundation.
7 *
8 */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <stdint.h>
13 #include <string.h>
14 #include <unistd.h> /* for unlink() */
15 #include <libgen.h>
16 #include <getopt.h> /* for getopt() */
17 #include <stdarg.h>
18 #include <errno.h>
19 #include <sys/stat.h>
20
21 #include <arpa/inet.h>
22 #include <netinet/in.h>
23
24 #define MAX_MAGIC_LEN 16
25 #define MAX_MODEL_LEN 32
26 #define MAX_VERSION_LEN 14
27 #define MAX_MTD_NAME_LEN 16
28
29 #define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f))
30
31 struct edimax_header {
32 char magic[MAX_MAGIC_LEN];
33 char model[MAX_MODEL_LEN];
34 unsigned char force;
35 unsigned char header_csum;
36 unsigned char data_csum;
37 uint32_t data_size;
38 uint32_t start_addr;
39 uint32_t end_addr;
40 char fw_version[MAX_VERSION_LEN];
41 unsigned char type;
42 char mtd_name[MAX_MTD_NAME_LEN];
43 } __attribute__ ((packed));
44
45 /*
46 * Globals
47 */
48 static char *ofname;
49 static char *ifname;
50 static char *progname;
51
52 static char *model;
53 static char *magic = "eDiMaX";
54 static char *fw_version = "";
55 static char *mtd_name;
56 static int force;
57 static uint32_t start_addr;
58 static uint32_t end_addr;
59 static uint8_t image_type;
60 static int data_size;
61
62 /*
63 * Message macros
64 */
65 #define ERR(fmt, ...) do { \
66 fflush(0); \
67 fprintf(stderr, "[%s] *** error: " fmt "\n", \
68 progname, ## __VA_ARGS__ ); \
69 } while (0)
70
71 #define ERRS(fmt, ...) do { \
72 int save = errno; \
73 fflush(0); \
74 fprintf(stderr, "[%s] *** error: " fmt " (%s)\n", \
75 progname, ## __VA_ARGS__, strerror(save)); \
76 } while (0)
77
78 #define DBG(fmt, ...) do { \
79 fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__ ); \
80 } while (0)
81
82 static void usage(int status)
83 {
84 FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout;
85
86 fprintf(stream, "Usage: %s [OPTIONS...]\n", progname);
87 fprintf(stream,
88 "\n"
89 "Options:\n"
90 " -e <addr> set end addr to <addr>\n"
91 " -f set force flag\n"
92 " -h show this screen\n"
93 " -i <file> read input data from the file <file>\n"
94 " -o <file> write output to the file <file>\n"
95 " -m <model> set model to <model>\n"
96 " -M <magic> set image magic to <magic>\n"
97 " -n <name> set MTD device name to <name>\n"
98 " -s <addr> set start address to <addr>\n"
99 " -t <type> set image type to <type>\n"
100 " -v <version> set firmware version to <version>\n"
101 );
102
103 exit(status);
104 }
105
106 int
107 str2u32(char *arg, uint32_t *val)
108 {
109 char *err = NULL;
110 uint32_t t;
111
112 errno=0;
113 t = strtoul(arg, &err, 0);
114 if (errno || (err==arg) || ((err != NULL) && *err)) {
115 return -1;
116 }
117
118 *val = t;
119 return 0;
120 }
121
122 int
123 str2u8(char *arg, uint8_t *val)
124 {
125 char *err = NULL;
126 uint32_t t;
127
128 errno=0;
129 t = strtoul(arg, &err, 0);
130 if (errno || (err==arg) || ((err != NULL) && *err) || (t >= 0x100)) {
131 return -1;
132 }
133
134 *val = t & 0xFF;
135 return 0;
136 }
137
138 static int get_file_size(char *name)
139 {
140 struct stat st;
141 int res;
142
143 res = stat(name, &st);
144 if (res){
145 ERRS("stat failed on %s", name);
146 return -1;
147 }
148
149 return st.st_size;
150 }
151
152 static int read_to_buf(char *name, char *buf, int buflen)
153 {
154 FILE *f;
155 int ret = EXIT_FAILURE;
156
157 f = fopen(name, "r");
158 if (f == NULL) {
159 ERRS("could not open \"%s\" for reading", name);
160 goto out;
161 }
162
163 errno = 0;
164 fread(buf, buflen, 1, f);
165 if (errno != 0) {
166 ERRS("unable to read from file \"%s\"", name);
167 goto out_close;
168 }
169
170 ret = EXIT_SUCCESS;
171
172 out_close:
173 fclose(f);
174 out:
175 return ret;
176 }
177
178 static int check_options(void)
179 {
180 #define CHKSTR(_name, _msg) \
181 do { \
182 if (_name == NULL) { \
183 ERR("no %s specified", _msg); \
184 return -1; \
185 } \
186 } while (0)
187
188 #define CHKSTRLEN(_name, _msg) \
189 do { \
190 int field_len; \
191 CHKSTR(_name, _msg); \
192 field_len = FIELD_SIZEOF(struct edimax_header, _name) - 1; \
193 if (strlen(_name) > field_len) { \
194 ERR("'%s' is too long, max %s length is %d", \
195 _name, _msg, field_len); \
196 return -1; \
197 } \
198 } while (0)
199
200 CHKSTR(ofname, "output file");
201 CHKSTR(ifname, "input file");
202
203 CHKSTRLEN(magic, "magic");
204 CHKSTRLEN(model, "model");
205 CHKSTRLEN(mtd_name, "MTD device name");
206 CHKSTRLEN(fw_version, "firware version");
207
208 data_size = get_file_size(ifname);
209 if (data_size < 0)
210 return -1;
211
212 return 0;
213 }
214
215 static int write_fw(char *data, int len)
216 {
217 FILE *f;
218 int ret = EXIT_FAILURE;
219
220 f = fopen(ofname, "w");
221 if (f == NULL) {
222 ERRS("could not open \"%s\" for writing", ofname);
223 goto out;
224 }
225
226 errno = 0;
227 fwrite(data, len, 1, f);
228 if (errno) {
229 ERRS("unable to write output file");
230 goto out_flush;
231 }
232
233 DBG("firmware file \"%s\" completed", ofname);
234
235 ret = EXIT_SUCCESS;
236
237 out_flush:
238 fflush(f);
239 fclose(f);
240 if (ret != EXIT_SUCCESS) {
241 unlink(ofname);
242 }
243 out:
244 return ret;
245 }
246
247 static unsigned char checksum(unsigned char *p, unsigned len)
248 {
249 unsigned char csum = 0;
250
251 while (len--)
252 csum += *p++;
253
254 csum ^= 0xb9;
255
256 return csum;
257 }
258
259 static int build_fw(void)
260 {
261 int buflen;
262 char *buf;
263 char *data;
264 struct edimax_header *hdr;
265 int ret = EXIT_FAILURE;
266
267 buflen = sizeof(struct edimax_header) + data_size;
268
269 buf = malloc(buflen);
270 if (!buf) {
271 ERR("no memory for buffer\n");
272 goto out;
273 }
274
275 data = buf + sizeof(struct edimax_header);
276
277 /* read input file */
278 ret = read_to_buf(ifname, data, data_size);
279 if (ret)
280 goto out_free_buf;
281
282 /* fill firmware header */
283 hdr = (struct edimax_header *)buf;
284 memset(hdr, 0, sizeof(struct edimax_header));
285
286 strncpy(hdr->model, model, sizeof(hdr->model));
287 strncpy(hdr->magic, magic, sizeof(hdr->magic));
288 strncpy(hdr->fw_version, fw_version, sizeof(hdr->fw_version));
289 strncpy(hdr->mtd_name, mtd_name, sizeof(hdr->mtd_name));
290
291 hdr->force = force;
292 hdr->start_addr = htonl(start_addr);
293 hdr->end_addr = htonl(end_addr);
294 hdr->data_size = htonl(data_size);
295 hdr->type = image_type;
296
297 hdr->data_csum = checksum((unsigned char *)data, data_size);
298 hdr->header_csum = checksum((unsigned char *)hdr,
299 sizeof(struct edimax_header));
300
301 ret = write_fw(buf, buflen);
302 if (ret)
303 goto out_free_buf;
304
305 ret = EXIT_SUCCESS;
306
307 out_free_buf:
308 free(buf);
309 out:
310 return ret;
311 }
312
313 int main(int argc, char *argv[])
314 {
315 int ret = EXIT_FAILURE;
316
317 progname = basename(argv[0]);
318
319 while (1) {
320 int c;
321
322 c = getopt(argc, argv, "e:fhi:o:m:M:n:s:t:v:");
323 if (c == -1)
324 break;
325
326 switch (c) {
327 case 'e':
328 if (str2u32(optarg, &end_addr)) {
329 ERR("%s is invalid '%s'",
330 "end address", optarg);
331 goto out;
332 }
333 break;
334 case 'f':
335 force = 1;
336 break;
337 case 'i':
338 ifname = optarg;
339 break;
340 case 'h':
341 usage(EXIT_SUCCESS);
342 break;
343 case 'o':
344 ofname = optarg;
345 break;
346 case 'm':
347 model = optarg;
348 break;
349 case 'M':
350 magic = optarg;
351 break;
352 case 'n':
353 mtd_name = optarg;
354 break;
355 case 's':
356 if (str2u32(optarg, &start_addr)) {
357 ERR("%s is invalid '%s'",
358 "start address", optarg);
359 goto out;
360 }
361 break;
362 case 't':
363 if (str2u8(optarg, &image_type)) {
364 ERR("%s is invalid '%s'",
365 "image type", optarg);
366 goto out;
367 }
368 break;
369 case 'v':
370 fw_version = optarg;
371 break;
372 default:
373 usage(EXIT_FAILURE);
374 break;
375 }
376 }
377
378 ret = check_options();
379 if (ret)
380 goto out;
381
382 ret = build_fw();
383
384 out:
385 return ret;
386 }