tools: otrx: allow own magic
[project/firmware-utils.git] / src / mksercommfw.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <sys/types.h>
5 #include <unistd.h>
6 #include <byteswap.h>
7 #include <endian.h>
8 #include <getopt.h>
9
10 #if !defined(__BYTE_ORDER)
11 #error "Unknown byte order"
12 #endif
13
14 #if __BYTE_ORDER == __BIG_ENDIAN
15 #define cpu_to_be32(x) (x)
16 #elif __BYTE_ORDER == __LITTLE_ENDIAN
17 #define cpu_to_be32(x) bswap_32(x)
18 #else
19 #error "Unsupported endianness"
20 #endif
21
22 /* #define DEBUG 1 */
23
24 #ifdef DEBUG
25 #define DBG(...) {printf(__VA_ARGS__); }
26 #else
27 #define DBG(...) {}
28 #endif
29
30 #define ERR(...) {printf(__VA_ARGS__); }
31
32 /*
33 * Fw Header Layout for Netgear / Sercomm devices (bytes)
34 *
35 * Size : 512 bytes + zipped image size
36 *
37 * Locations:
38 * magic : 0-6 ASCII
39 * version: 7-11 fixed
40 * hwID : 11-44 ASCII
41 * hwVer : 45-54 ASCII
42 * swVer : 55-62 uint32_t in BE
43 * magic : 63-69 ASCII
44 * ChkSum : 511 Inverse value of the full image checksum while this location is 0x00
45 */
46 static const char* magic = "sErCoMm"; /* 7 */
47 static const unsigned char version[4] = { 0x00, 0x01, 0x00, 0x00 };
48 static const int header_sz = 512;
49 static const int footer_sz = 71;
50
51 static int is_header = 1;
52
53 struct file_info {
54 char* file_name; /* name of the file */
55 char* file_data; /* data of the file in memory */
56 u_int32_t file_size; /* length of the file */
57 };
58
59 static u_int8_t getCheckSum(char* data, int len) {
60 u_int8_t new = 0;
61 int i;
62
63 if (!data) {
64 ERR("Invalid pointer provided!\n");
65 return 0;
66 }
67
68 for (i = 0; i < len; i++) {
69 new += data[i];
70 }
71
72 return new;
73 }
74
75 /*
76 * read file into buffer
77 * add space for header/footer
78 */
79 static int copyToOutputBuf(struct file_info* finfo) {
80 FILE* fp = NULL;
81
82 int file_sz = 0;
83 int extra_sz;
84 int hdr_pos;
85 int img_pos;
86
87 if (!finfo || !finfo->file_name) {
88 ERR("Invalid pointer provided!\n");
89 return -1;
90 }
91
92 DBG("Opening file: %s\n", finfo->file_name);
93
94 if (!(fp = fopen(finfo->file_name, "rb"))) {
95 ERR("Error opening file: %s\n", finfo->file_name);
96 return -1;
97 }
98
99 /* Get filesize */
100 rewind(fp);
101 fseek(fp, 0L, SEEK_END);
102 file_sz = ftell(fp);
103 rewind(fp);
104
105 if (file_sz < 1) {
106 ERR("Error getting filesize: %s\n", finfo->file_name);
107 fclose(fp);
108 return -1;
109 }
110
111 if (is_header) {
112 extra_sz = header_sz;
113 hdr_pos = 0;
114 img_pos = header_sz;
115 } else {
116 extra_sz = footer_sz;
117 hdr_pos = file_sz;
118 img_pos = 0;
119 }
120
121 DBG("Filesize: %i\n", file_sz);
122 finfo->file_size = file_sz + extra_sz;
123
124 if (!(finfo->file_data = malloc(finfo->file_size))) {
125 ERR("Out of memory!\n");
126 fclose(fp);
127 return -1;
128 }
129
130 /* init header/footer bytes */
131 memset(finfo->file_data + hdr_pos, 0, extra_sz);
132
133 /* read file and take care of leading header if exists */
134 if (fread(finfo->file_data + img_pos, 1, file_sz, fp) != file_sz) {
135 ERR("Error reading file %s\n", finfo->file_name);
136 fclose(fp);
137 return -1;
138 }
139
140 DBG("File: read successful\n");
141 fclose(fp);
142
143 return hdr_pos;
144 }
145
146 static int writeFile(struct file_info* finfo) {
147 FILE* fp;
148
149 if (!finfo || !finfo->file_name) {
150 ERR("Invalid pointer provided!\n");
151 return -1;
152 }
153
154 DBG("Opening file: %s\n", finfo->file_name);
155
156 if (!(fp = fopen(finfo->file_name, "w"))) {
157 ERR("Error opening file: %s\n", finfo->file_name);
158 return -1;
159 }
160
161 DBG("Writing file: %s\n", finfo->file_name);
162
163 if (fwrite(finfo->file_data, 1, finfo->file_size, fp) != finfo->file_size) {
164 ERR("Wanted to write, but something went wrong!\n");
165 fclose(fp);
166 return -1;
167 }
168
169 fclose(fp);
170 return 0;
171 }
172
173 static void usage(char* argv[]) {
174 printf("Usage: %s [OPTIONS...]\n"
175 "\n"
176 "Options:\n"
177 " -f add sercom footer (if absent, header)\n"
178 " -b <hwid> use hardware id specified with <hwid> (ASCII)\n"
179 " -r <hwrev> use hardware revision specified with <hwrev> (ASCII)\n"
180 " -v <version> set image version to <version> (decimal, hex or octal notation)\n"
181 " -i <file> input file\n"
182 , argv[0]);
183 }
184
185 int main(int argc, char* argv[]) {
186 struct file_info image = { 0 };
187
188 char* hwID = NULL;
189 char* hwVer = NULL;
190 u_int32_t swVer = 0;
191 u_int8_t chkSum;
192 int hdr_offset;
193
194 while ( 1 ) {
195 int c;
196
197 c = getopt(argc, argv, "b:i:r:v:f");
198 if (c == -1)
199 break;
200
201 switch (c) {
202 case 'b':
203 hwID = optarg;
204 break;
205 case 'f':
206 is_header = 0;
207 break;
208 case 'i':
209 image.file_name = optarg;
210 break;
211 case 'r':
212 hwVer = optarg;
213 break;
214 case 'v':
215 swVer = (u_int32_t) strtol(optarg, NULL, 0);
216 swVer = cpu_to_be32(swVer);
217 break;
218 default:
219 usage(argv);
220 return EXIT_FAILURE;
221 }
222 }
223
224 if (!hwID || !hwVer || !image.file_name) {
225 usage(argv);
226 return EXIT_FAILURE;
227 }
228
229 /*
230 * copy input to buffer, add extra space for header/footer and return
231 * header position
232 */
233 hdr_offset = copyToOutputBuf(&image);
234 if (hdr_offset < 0)
235 return EXIT_FAILURE;
236
237 DBG("Filling header: %s %s %2X %s\n", hwID, hwVer, swVer, magic);
238
239 strncpy(image.file_data + hdr_offset + 0, magic, 7);
240 memcpy(image.file_data + hdr_offset + 7, version, sizeof(version));
241 strncpy(image.file_data + hdr_offset + 11, hwID, 34);
242 strncpy(image.file_data + hdr_offset + 45, hwVer, 10);
243 memcpy(image.file_data + hdr_offset + 55, &swVer, sizeof(swVer));
244 strncpy(image.file_data + hdr_offset + 63, magic, 7);
245
246 /* calculate checksum and invert checksum */
247 if (is_header) {
248 chkSum = getCheckSum(image.file_data, image.file_size);
249 chkSum = (chkSum ^ 0xFF) + 1;
250 DBG("Checksum for Image: %hhX\n", chkSum);
251
252 /* write checksum to header */
253 image.file_data[511] = (char) chkSum;
254 }
255
256 /* overwrite input file */
257 if (writeFile(&image))
258 return EXIT_FAILURE;
259
260 return EXIT_SUCCESS;
261 }