edimax_fw_header.c 7.1 KB

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