Rebased from upstream / out of band repository.
[librecmc/librecmc.git] / tools / firmware-utils / 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 }