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 31struct 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 */ 48static char *ofname; 49static char *ifname; 50static char *progname; 51 52static char *model; 53static char *magic = "eDiMaX"; 54static char *fw_version = ""; 55static char *mtd_name; 56static int force; 57static uint32_t start_addr; 58static uint32_t end_addr; 59static uint8_t image_type; 60static 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 82static 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 106int 107str2u32(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 122int 123str2u8(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 138static 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 152static 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 172out_close: 173 fclose(f); 174out: 175 return ret; 176} 177 178static 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 215static 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 237out_flush: 238 fflush(f); 239 fclose(f); 240 if (ret != EXIT_SUCCESS) { 241 unlink(ofname); 242 } 243out: 244 return ret; 245} 246 247static 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 259static 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 307out_free_buf: 308 free(buf); 309out: 310 return ret; 311} 312 313int 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 384out: 385 return ret; 386} 387