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