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