carl9170 firmware: import 1.7.0
[carl9170fw.git] / tools / lib / carlfw.c
1 /*
2  * Copyright 2010, Christian Lamparter <chunkeey@googlemail.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16  */
17
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <error.h>
21 #include <string.h>
22 #include <errno.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26
27 #include "carlfw.h"
28
29 struct carlfw_file {
30         char *name;
31         size_t len;
32         char *data;
33 };
34
35 struct carlfw {
36         struct carlfw_file fw;
37         struct carlfw_file hdr;
38
39         struct list_head desc_list;
40         unsigned int desc_list_entries,
41                      desc_list_len;
42 };
43
44 #define carlfw_walk_descs(iter, fw)                                     \
45         list_for_each_entry(iter, &fw->desc_list, h.list)
46
47 struct carlfw_list_entry_head {
48         struct list_head list;
49 };
50
51 struct carlfw_list_entry {
52         struct carlfw_list_entry_head h;
53         union {
54                 struct carl9170fw_desc_head head;
55                 uint32_t data[0];
56                 char text[0];
57         };
58 };
59
60 static inline struct carlfw_list_entry *carlfw_desc_to_entry(struct carl9170fw_desc_head *head)
61 {
62         return container_of(head, struct carlfw_list_entry, head);
63 }
64
65 static inline struct carl9170fw_desc_head *carlfw_entry_to_desc(struct carlfw_list_entry *entry)
66 {
67         return &entry->head;
68 }
69
70 static void carlfw_entry_unlink(struct carlfw *fw,
71         struct carlfw_list_entry *entry)
72 {
73         fw->desc_list_entries--;
74         fw->desc_list_len -= le16_to_cpu(entry->head.length);
75         list_del(&entry->h.list);
76 }
77
78 static void carlfw_entry_del(struct carlfw *fw,
79         struct carlfw_list_entry *entry)
80 {
81         carlfw_entry_unlink(fw, entry);
82         free(entry);
83 }
84
85 static struct carlfw_list_entry *carlfw_find_entry(struct carlfw *fw,
86                                                    const uint8_t descid[4],
87                                                    unsigned int len,
88                                                    uint8_t compatible_revision)
89 {
90         struct carlfw_list_entry *iter;
91
92         carlfw_walk_descs(iter, fw) {
93                 if (carl9170fw_desc_cmp(&iter->head, descid, len,
94                                          compatible_revision))
95                         return (void *)iter;
96         }
97
98         return NULL;
99 }
100
101 static struct carlfw_list_entry *__carlfw_entry_add_prepare(struct carlfw *fw,
102         const struct carl9170fw_desc_head *desc)
103 {
104         struct carlfw_list_entry *tmp;
105         unsigned int len;
106
107         len = le16_to_cpu(desc->length);
108
109         if (len < sizeof(struct carl9170fw_desc_head))
110                 return ERR_PTR(-EINVAL);
111
112         tmp = malloc(sizeof(*tmp) + len);
113         if (!tmp)
114                 return ERR_PTR(-ENOMEM);
115
116         fw->desc_list_entries++;
117         fw->desc_list_len += len;
118
119         memcpy(tmp->data, desc, len);
120         return tmp;
121 }
122
123 static void __carlfw_release(struct carlfw_file *f)
124 {
125         f->len = 0;
126         if (f->name)
127                 free(f->name);
128         f->name = NULL;
129
130         if (f->data)
131                 free(f->data);
132         f->data = NULL;
133 }
134
135 void carlfw_release(struct carlfw *fw)
136 {
137         struct carlfw_list_entry *entry;
138
139         if (!IS_ERR_OR_NULL(fw)) {
140                 while (!list_empty(&fw->desc_list)) {
141                         entry = list_entry(fw->desc_list.next,
142                                            struct carlfw_list_entry, h.list);
143                         carlfw_entry_del(fw, entry);
144                 }
145
146                 __carlfw_release(&fw->fw);
147                 __carlfw_release(&fw->hdr);
148                 free(fw);
149         }
150 }
151
152 static int __carlfw_load(struct carlfw_file *file, const char *name, const char *mode)
153 {
154         struct stat file_stat;
155         FILE *fh;
156         int err;
157
158         fh = fopen(name, mode);
159         if (fh == NULL)
160                 return errno ? -errno : -1;
161
162         err = fstat(fileno(fh), &file_stat);
163         if (err)
164                 return errno ? -errno : -1;
165
166         file->len = file_stat.st_size;
167         file->data = malloc(file->len);
168         if (file->data == NULL)
169                 return -ENOMEM;
170
171         err = fread(file->data, file->len, 1, fh);
172         if (err != 1)
173                 return ferror(fh);
174
175         file->name = strdup(name);
176         fclose(fh);
177
178         if (!file->name)
179                 return -ENOMEM;
180
181         return 0;
182 }
183
184 static void *__carlfw_find_desc(struct carlfw_file *file,
185                                 uint8_t descid[4],
186                                 unsigned int len,
187                                 uint8_t compatible_revision)
188 {
189         int scan = file->len, found = 0;
190         struct carl9170fw_desc_head *tmp = NULL;
191
192         while (scan >= 0) {
193                 if (file->data[scan] == descid[sizeof(descid) - found - 1])
194                         found++;
195                 else
196                         found = 0;
197
198                 if (found == sizeof(descid))
199                         break;
200
201                 scan--;
202         }
203
204         if (found == sizeof(descid)) {
205                 tmp = (void *) &file->data[scan];
206
207                 if (!CHECK_HDR_VERSION(tmp, compatible_revision) &&
208                     (le16_to_cpu(tmp->length) >= len))
209                         return tmp;
210         }
211
212         return NULL;
213 }
214
215 void *carlfw_find_desc(struct carlfw *fw,
216                        const uint8_t descid[4],
217                        const unsigned int len,
218                        const uint8_t compatible_revision)
219 {
220         struct carlfw_list_entry *tmp;
221
222         tmp = carlfw_find_entry(fw, descid, len, compatible_revision);
223
224         return tmp ? carlfw_entry_to_desc(tmp) : NULL;
225 }
226
227 int carlfw_desc_add_tail(struct carlfw *fw,
228         const struct carl9170fw_desc_head *desc)
229 {
230         struct carlfw_list_entry *tmp;
231
232         tmp = __carlfw_entry_add_prepare(fw, desc);
233         if (IS_ERR(tmp))
234                 return PTR_ERR(tmp);
235
236         list_add_tail(&tmp->h.list, &fw->desc_list);
237         return 0;
238 }
239
240 int carlfw_desc_add(struct carlfw *fw,
241                     const struct carl9170fw_desc_head *desc,
242                     struct carl9170fw_desc_head *prev,
243                     struct carl9170fw_desc_head *next)
244 {
245         struct carlfw_list_entry *tmp;
246
247         tmp = __carlfw_entry_add_prepare(fw, desc);
248         if (IS_ERR(tmp))
249                 return PTR_ERR(tmp);
250
251         list_add(&tmp->h.list, &((carlfw_desc_to_entry(prev))->h.list),
252                  &((carlfw_desc_to_entry(next))->h.list));
253         return 0;
254 }
255
256 int carlfw_desc_add_before(struct carlfw *fw,
257                            const struct carl9170fw_desc_head *desc,
258                            struct carl9170fw_desc_head *pos)
259 {
260         struct carl9170fw_desc_head *prev;
261         struct carlfw_list_entry *prev_entry;
262
263         prev_entry = carlfw_desc_to_entry(pos);
264
265         prev = carlfw_entry_to_desc((struct carlfw_list_entry *) prev_entry->h.list.prev);
266
267         return carlfw_desc_add(fw, desc, prev, pos);
268 }
269
270 void carlfw_desc_unlink(struct carlfw *fw,
271         struct carl9170fw_desc_head *desc)
272 {
273         carlfw_entry_unlink(fw, carlfw_desc_to_entry(desc));
274 }
275
276 void carlfw_desc_del(struct carlfw *fw,
277         struct carl9170fw_desc_head *desc)
278 {
279         carlfw_entry_del(fw, carlfw_desc_to_entry(desc));
280 }
281
282 void *carlfw_desc_mod_len(struct carlfw *fw __unused,
283         struct carl9170fw_desc_head *desc, int len)
284 {
285         struct carlfw_list_entry *obj, tmp;
286         int new_len = le16_to_cpu(desc->length) + len;
287
288         if (new_len < (int)sizeof(*desc))
289                 return ERR_PTR(-EINVAL);
290
291         if (new_len > CARL9170FW_DESC_MAX_LENGTH)
292                 return ERR_PTR(-E2BIG);
293
294         obj = carlfw_desc_to_entry(desc);
295
296         memcpy(&tmp, obj, sizeof(tmp));
297         obj = realloc(obj, new_len + sizeof(struct carlfw_list_entry_head));
298         if (obj == NULL)
299                 return ERR_PTR(-ENOMEM);
300
301         list_replace(&tmp.h.list, &obj->h.list);
302
303         desc = carlfw_entry_to_desc(obj);
304         desc->length = le16_to_cpu(new_len);
305         fw->desc_list_len += len;
306
307         return desc;
308 }
309
310 void *carlfw_desc_next(struct carlfw *fw,
311                        struct carl9170fw_desc_head *pos)
312 {
313         struct carlfw_list_entry *entry;
314
315         if (!pos)
316                 entry = (struct carlfw_list_entry *) &fw->desc_list;
317         else
318                 entry = carlfw_desc_to_entry(pos);
319
320         if (list_at_tail(entry, &fw->desc_list, h.list))
321                 return NULL;
322
323         entry = (struct carlfw_list_entry *) entry->h.list.next;
324
325         return carlfw_entry_to_desc(entry);
326 }
327
328 static int carlfw_parse_descs(struct carlfw *fw,
329                               struct carl9170fw_otus_desc *otus_desc)
330 {
331         const struct carl9170fw_desc_head *iter = NULL;
332         int err;
333
334         carl9170fw_for_each_hdr(iter, &otus_desc->head) {
335                 err = carlfw_desc_add_tail(fw, iter);
336                 if (err)
337                         return err;
338         }
339         /* LAST is added automatically by carlfw_store */
340
341         return err;
342 }
343
344 #if BYTE_ORDER == LITTLE_ENDIAN
345 #define CRCPOLY_LE 0xedb88320
346
347 /* copied from the linux kernel  */
348 static uint32_t crc32_le(uint32_t crc, unsigned char const *p, size_t len)
349 {
350         int i;
351         while (len--) {
352                 crc ^= *p++;
353                 for (i = 0; i < 8; i++)
354                         crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
355         }
356         return crc;
357 }
358 #else
359 #error "this tool does not work with a big endian host yet!"
360 #endif
361
362 static int carlfw_check_crc32s(struct carlfw *fw)
363 {
364         struct carl9170fw_chk_desc *chk_desc;
365         struct carlfw_list_entry *iter;
366         unsigned int elen;
367         uint32_t crc32;
368
369         chk_desc = carlfw_find_desc(fw, (uint8_t *) CHK_MAGIC,
370                                     sizeof(*chk_desc),
371                                     CARL9170FW_CHK_DESC_CUR_VER);
372         if (!chk_desc)
373                 return -ENODATA;
374
375         crc32 = crc32_le(~0, (void *) fw->fw.data, fw->fw.len);
376         if (crc32 != le32_to_cpu(chk_desc->fw_crc32))
377                 return -EINVAL;
378
379         carlfw_walk_descs(iter, fw) {
380                 elen = le16_to_cpu(iter->head.length);
381
382                 if (carl9170fw_desc_cmp(&iter->head, (uint8_t *) CHK_MAGIC,
383                                         sizeof(*chk_desc),
384                                         CARL9170FW_CHK_DESC_CUR_VER))
385                         continue;
386
387                 crc32 = crc32_le(crc32, (void *) &iter->head, elen);
388         }
389
390         if (crc32 != le32_to_cpu(chk_desc->hdr_crc32))
391                 return -EINVAL;
392
393         return 0;
394 }
395
396 struct carlfw *carlfw_load(const char *basename)
397 {
398         char filename[256];
399         struct carlfw *fw;
400         struct carl9170fw_otus_desc *otus_desc;
401         struct carl9170fw_last_desc *last_desc;
402         struct carlfw_file *hdr_file;
403         unsigned long fin, diff, off, rem;
404         int err;
405
406         fw = calloc(1, sizeof(*fw));
407         if (!fw)
408                 return ERR_PTR(-ENOMEM);
409
410         init_list_head(&fw->desc_list);
411
412         snprintf(filename, sizeof(filename), "%s.dsc", basename);
413         err = __carlfw_load(&fw->hdr, filename, "r");
414
415         snprintf(filename, sizeof(filename), "%s.fw", basename);
416         err = __carlfw_load(&fw->fw, filename, "r");
417         if (!err)
418                 goto found;
419
420         err = __carlfw_load(&fw->fw, basename, "r");
421         if (err)
422                 goto err_out;
423
424 found:
425         if (fw->hdr.name)
426                 hdr_file = &fw->hdr;
427         else
428                 hdr_file = &fw->fw;
429
430         otus_desc = __carlfw_find_desc(hdr_file, (uint8_t *) OTUS_MAGIC,
431                                        sizeof(*otus_desc),
432                                        CARL9170FW_OTUS_DESC_CUR_VER);
433         last_desc = __carlfw_find_desc(hdr_file, (uint8_t *) LAST_MAGIC,
434                                        sizeof(*last_desc),
435                                        CARL9170FW_LAST_DESC_CUR_VER);
436
437         if (!otus_desc || !last_desc ||
438             (unsigned long) otus_desc > (unsigned long) last_desc) {
439                 err = -ENODATA;
440                 goto err_out;
441         }
442
443         err = carlfw_parse_descs(fw, otus_desc);
444         if (err)
445                 goto err_out;
446
447         fin = (unsigned long)last_desc + sizeof(*last_desc);
448         diff = fin - (unsigned long)otus_desc;
449         rem = hdr_file->len - (fin - (unsigned long) hdr_file->data);
450
451         if (rem) {
452                 off = (unsigned long)otus_desc - (unsigned long)hdr_file->data;
453                 memmove(&hdr_file->data[off],
454                         ((uint8_t *)last_desc) + sizeof(*last_desc), rem);
455         }
456
457         hdr_file->len -= diff;
458         hdr_file->data = realloc(hdr_file->data, hdr_file->len);
459         if (!hdr_file->data && hdr_file->len) {
460                 err = -ENOMEM;
461                 goto err_out;
462         }
463
464         err = carlfw_check_crc32s(fw);
465         if (err && err != -ENODATA)
466                 goto err_out;
467
468         return fw;
469
470 err_out:
471         carlfw_release(fw);
472         return ERR_PTR(err);
473 }
474
475 static int carlfw_apply_checksums(struct carlfw *fw)
476 {
477         struct carlfw_list_entry *iter;
478         struct carl9170fw_chk_desc tmp = {
479                 CARL9170FW_FILL_DESC(CHK_MAGIC, sizeof(tmp),
480                                       CARL9170FW_CHK_DESC_MIN_VER,
481                                       CARL9170FW_CHK_DESC_CUR_VER) };
482         struct carl9170fw_chk_desc *chk_desc = NULL;
483         int err = 0;
484         unsigned int len = 0, elen, max_len;
485         uint32_t crc32;
486
487         chk_desc = carlfw_find_desc(fw, (uint8_t *) CHK_MAGIC,
488                                     sizeof(*chk_desc),
489                                     CARL9170FW_CHK_DESC_CUR_VER);
490         if (chk_desc) {
491                 carlfw_desc_del(fw, &chk_desc->head);
492                 chk_desc = NULL;
493         }
494
495         max_len = fw->desc_list_len;
496
497         crc32 = crc32_le(~0, (void *) fw->fw.data, fw->fw.len);
498         tmp.fw_crc32 = cpu_to_le32(crc32);
499
500         /*
501          * NOTE:
502          *
503          * The descriptor checksum is seeded with the firmware's crc32.
504          * This neat trick ensures that the driver can check whenever
505          * descriptor actually belongs to the firmware, or not.
506          */
507
508         carlfw_walk_descs(iter, fw) {
509                 elen = le16_to_cpu(iter->head.length);
510
511                 if (max_len < len + elen)
512                         return -EMSGSIZE;
513
514                 crc32 = crc32_le(crc32, (void *) &iter->head, elen);
515                 len += elen;
516         }
517
518         tmp.hdr_crc32 = cpu_to_le32(crc32);
519
520         err = carlfw_desc_add_tail(fw, &tmp.head);
521
522         return err;
523 }
524
525 int carlfw_store(struct carlfw *fw)
526 {
527         struct carl9170fw_last_desc last_desc = {
528                 CARL9170FW_FILL_DESC(LAST_MAGIC, sizeof(last_desc),
529                         CARL9170FW_LAST_DESC_MIN_VER,
530                         CARL9170FW_LAST_DESC_CUR_VER) };
531
532         struct carlfw_list_entry *iter;
533         FILE *fh;
534         int err, elen;
535
536         err = carlfw_apply_checksums(fw);
537         if (err)
538                 return err;
539
540         fh = fopen(fw->fw.name, "w");
541         if (!fh)
542                 return -errno;
543
544         err = fwrite(fw->fw.data, fw->fw.len, 1, fh);
545         if (err != 1) {
546                 err = -errno;
547                 goto close_out;
548         }
549
550         if (fw->hdr.name) {
551                 fclose(fh);
552
553                 fh = fopen(fw->hdr.name, "w");
554         }
555
556         carlfw_walk_descs(iter, fw) {
557                 elen = le16_to_cpu(iter->head.length);
558
559                 if (elen > CARL9170FW_DESC_MAX_LENGTH) {
560                         err = -E2BIG;
561                         goto close_out;
562                 }
563
564                 err = fwrite(iter->data, elen, 1, fh);
565                 if (err != 1) {
566                         err = ferror(fh);
567                         goto close_out;
568                 }
569         }
570
571         err = fwrite(&last_desc, sizeof(last_desc), 1, fh);
572         if (err != 1) {
573                 err = ferror(fh);
574                 goto close_out;
575         }
576
577         err = 0;
578
579 close_out:
580         fclose(fh);
581         return err;
582 }
583
584 void *carlfw_mod_tailroom(struct carlfw *fw, ssize_t len)
585 {
586         size_t new_len;
587         void *buf;
588
589         new_len = fw->fw.len + len;
590
591         if (!carl9170fw_size_check(new_len))
592                 return ERR_PTR(-EINVAL);
593
594         buf = realloc(fw->fw.data, new_len);
595         if (buf == NULL)
596                 return ERR_PTR(-ENOMEM);
597
598         fw->fw.len = new_len;
599         fw->fw.data = buf;
600         return &fw->fw.data[new_len - len];
601 }
602
603 void *carlfw_mod_headroom(struct carlfw *fw, ssize_t len)
604 {
605         size_t new_len;
606         void *ptr;
607
608         new_len = fw->fw.len + len;
609         if (!carl9170fw_size_check(new_len))
610                 return ERR_PTR(-EINVAL);
611
612         if (len < 0)
613                 memmove(fw->fw.data, &fw->fw.data[len], new_len);
614
615         ptr = carlfw_mod_tailroom(fw, len);
616         if (IS_ERR_OR_NULL(ptr))
617                 return ptr;
618
619         if (len > 0)
620                 memmove(&fw->fw.data[len], &fw->fw.data[0], new_len - len);
621
622         return fw->fw.data;
623 }
624
625 void *carlfw_get_fw(struct carlfw *fw, size_t *len)
626 {
627         *len = fw->fw.len;
628         return fw->fw.data;
629 }
630
631 unsigned int carlfw_get_descs_num(struct carlfw *fw)
632 {
633         return fw->desc_list_entries;
634 }
635
636 unsigned int carlfw_get_descs_size(struct carlfw *fw)
637 {
638         return fw->desc_list_len;
639 }