firmware-utils: mkdlinkfw: fix error handling
[openwrt/staging/mkresin.git] / tools / firmware-utils / src / asustrx.c
1 /*
2 * asustrx
3 *
4 * Copyright (C) 2015 Rafał Miłecki <zajec5@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
10 */
11
12 #include <byteswap.h>
13 #include <errno.h>
14 #include <stdint.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19
20 #if __BYTE_ORDER == __BIG_ENDIAN
21 #define cpu_to_le32(x) bswap_32(x)
22 #define le32_to_cpu(x) bswap_32(x)
23 #elif __BYTE_ORDER == __LITTLE_ENDIAN
24 #define cpu_to_le32(x) (x)
25 #define le32_to_cpu(x) (x)
26 #else
27 #error "Unsupported endianness"
28 #endif
29
30 #define TRX_MAGIC 0x30524448
31 #define TRX_FLAGS_OFFSET 12
32
33 struct trx_header {
34 uint32_t magic;
35 uint32_t length;
36 uint32_t crc32;
37 uint16_t flags;
38 uint16_t version;
39 uint32_t offset[3];
40 };
41
42 struct asustrx_tail {
43 uint8_t version[4];
44 char productid[12];
45 uint8_t unused[48];
46 };
47
48 char *in_path = NULL;
49 char *out_path = NULL;
50 char *productid = NULL;
51 uint8_t version[4] = { };
52
53 static const uint32_t crc32_tbl[] = {
54 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
55 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
56 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
57 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
58 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
59 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
60 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
61 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
62 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
63 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
64 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
65 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
66 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
67 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
68 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
69 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
70 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
71 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
72 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
73 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
74 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
75 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
76 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
77 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
78 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
79 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
80 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
81 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
82 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
83 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
84 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
85 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
86 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
87 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
88 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
89 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
90 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
91 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
92 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
93 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
94 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
95 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
96 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
97 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
98 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
99 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
100 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
101 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
102 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
103 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
104 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
105 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
106 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
107 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
108 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
109 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
110 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
111 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
112 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
113 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
114 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
115 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
116 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
117 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
118 };
119
120 static void parse_options(int argc, char **argv) {
121 int c;
122
123 while ((c = getopt(argc, argv, "i:o:p:v:")) != -1) {
124 switch (c) {
125 case 'i':
126 in_path = optarg;
127 break;
128 case 'o':
129 out_path = optarg;
130 break;
131 case 'p':
132 productid = optarg;
133 break;
134 case 'v':
135 if (sscanf(optarg, "%hu.%hu.%hu.%hu", &version[0], &version[1], &version[2], &version[3]) != 4)
136 fprintf(stderr, "Version %s doesn't match suppored 4-digits format\n", optarg);
137 break;
138 }
139 }
140 }
141
142 static void usage() {
143 printf("Usage:\n");
144 printf("\t-i file\t\t\t\tinput TRX file\n");
145 printf("\t-o file\t\t\t\toutput Asus TRX file\n");
146 printf("\t-p productid\t\t\tproduct (device) ID\n");
147 printf("\t-v version\t\t\tfirmware version formatted with 4 digits like: 1.2.3.4\n");
148 }
149
150 int main(int argc, char **argv) {
151 struct trx_header hdr;
152 struct asustrx_tail tail = { };
153 FILE *in, *out;
154 uint8_t buf[1024];
155 size_t bytes;
156 size_t length = 0;
157 uint32_t crc32 = 0xffffffff;
158 int i;
159 int err = 0;
160
161 /* Parse & validate arguments */
162 parse_options(argc, argv);
163 if (!in_path || !out_path || !productid) {
164 usage();
165 err = -EINVAL;
166 goto err;
167 }
168
169 /* Fill Asus tail */
170 tail.version[0] = version[0];
171 tail.version[1] = version[1];
172 tail.version[2] = version[2];
173 tail.version[3] = version[3];
174 strncpy(tail.productid, productid, sizeof(tail.productid));
175
176 /* Open files */
177 in = fopen(in_path, "r");
178 if (!in) {
179 fprintf(stderr, "Couldn't open %s\n", in_path);
180 err = -EIO;
181 goto err;
182 }
183 out = fopen(out_path, "w+");
184 if (!out) {
185 fprintf(stderr, "Couldn't open %s\n", out_path);
186 err = -EIO;
187 goto err;
188 }
189
190 /* Check is there is empty place for Asus tail */
191 bytes = sizeof(struct asustrx_tail);
192 fseek(in, -bytes, SEEK_END);
193 if (fread(buf, 1, bytes, in) != bytes) {
194 fprintf(stderr, "Couldn't read %zu B from %s\n", bytes, in_path);
195 err = -EIO;
196 goto err;
197 }
198 for (i = 0; i < bytes; i++) {
199 if (buf[i]) {
200 fprintf(stderr, "Input TRX doesn't have last 64 B empty %s\n", out_path);
201 err = -ENOSPC;
202 goto err;
203 }
204 }
205
206 /* Copy whole TRX */
207 rewind(in);
208 while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) {
209 if (fwrite(buf, 1, bytes, out) != bytes) {
210 fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, out_path);
211 err = -EIO;
212 goto err;
213 }
214 }
215
216 /* Overwrite last 64 B with Asus tail */
217 bytes = sizeof(tail);
218 fseek(out, -bytes, SEEK_CUR);
219 if (fwrite(&tail, 1, bytes, out) != bytes) {
220 fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, out_path);
221 err = -EIO;
222 goto err;
223 }
224
225 /* Calculate crc32 */
226 fseek(out, TRX_FLAGS_OFFSET, SEEK_SET);
227 length = TRX_FLAGS_OFFSET;
228 while ((bytes = fread(buf, 1, sizeof(buf), out )) > 0) {
229 length += bytes;
230 for (i = 0; i < bytes; i++)
231 crc32 = crc32_tbl[(crc32 ^ buf[i]) & 0xff] ^ (crc32 >> 8);
232 }
233
234 /* Update header */
235 bytes = sizeof(hdr);
236 rewind(out);
237 if (fread(&hdr, 1, sizeof(hdr), out) != bytes) {
238 fprintf(stderr, "Couldn't read %zu B from %s\n", bytes, in_path);
239 err = -EIO;
240 goto err;
241 }
242 hdr.crc32 = cpu_to_le32(crc32);
243 rewind(out);
244 if (fwrite(&hdr, 1, bytes, out) != bytes) {
245 fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, out_path);
246 err = -EIO;
247 goto err;
248 }
249
250 err:
251 if (out)
252 fclose(out);
253 if (in)
254 fclose(in);
255 return err;
256 }