Setting up repository
[linux-libre-firmware.git] / carl9170fw / tools / lib / carlfw.c
1 /*
2  * Copyright 2010-2011 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, found = 0;
190         struct carl9170fw_desc_head *tmp = NULL;
191
192         /*
193          * Note: the last desc also has atleast a full desc_head.
194          * There's no reason for looking beyond that point.
195          */
196         scan = (file->len - 1) - (sizeof(*tmp) - CARL9170FW_MAGIC_SIZE);
197         while (scan > 0) {
198                 if (file->data[scan] == descid[CARL9170FW_MAGIC_SIZE - found - 1])
199                         found++;
200                 else
201                         found = 0;
202
203                 if (found == CARL9170FW_MAGIC_SIZE)
204                         break;
205
206                 scan--;
207         }
208
209         if (found == CARL9170FW_MAGIC_SIZE) {
210                 u16 tmp_desc_len;
211
212                 tmp = (void *) &file->data[scan];
213                 tmp_desc_len = le16_to_cpu(tmp->length);
214
215                 if (!CHECK_HDR_VERSION(tmp, compatible_revision) &&
216                     (scan + tmp_desc_len <= file->len) && (tmp_desc_len >= len))
217                         return tmp;
218         }
219
220         return NULL;
221 }
222
223 void *carlfw_find_desc(struct carlfw *fw,
224                        const uint8_t descid[4],
225                        const unsigned int len,
226                        const uint8_t compatible_revision)
227 {
228         struct carlfw_list_entry *tmp;
229
230         tmp = carlfw_find_entry(fw, descid, len, compatible_revision);
231
232         return tmp ? carlfw_entry_to_desc(tmp) : NULL;
233 }
234
235 int carlfw_desc_add_tail(struct carlfw *fw,
236         const struct carl9170fw_desc_head *desc)
237 {
238         struct carlfw_list_entry *tmp;
239
240         tmp = __carlfw_entry_add_prepare(fw, desc);
241         if (IS_ERR(tmp))
242                 return PTR_ERR(tmp);
243
244         list_add_tail(&tmp->h.list, &fw->desc_list);
245         return 0;
246 }
247
248 int carlfw_desc_add(struct carlfw *fw,
249                     const struct carl9170fw_desc_head *desc,
250                     struct carl9170fw_desc_head *prev,
251                     struct carl9170fw_desc_head *next)
252 {
253         struct carlfw_list_entry *tmp;
254
255         tmp = __carlfw_entry_add_prepare(fw, desc);
256         if (IS_ERR(tmp))
257                 return PTR_ERR(tmp);
258
259         list_add(&tmp->h.list, &((carlfw_desc_to_entry(prev))->h.list),
260                  &((carlfw_desc_to_entry(next))->h.list));
261         return 0;
262 }
263
264 int carlfw_desc_add_before(struct carlfw *fw,
265                            const struct carl9170fw_desc_head *desc,
266                            struct carl9170fw_desc_head *pos)
267 {
268         struct carl9170fw_desc_head *prev;
269         struct carlfw_list_entry *prev_entry;
270
271         prev_entry = carlfw_desc_to_entry(pos);
272
273         prev = carlfw_entry_to_desc((struct carlfw_list_entry *) prev_entry->h.list.prev);
274
275         return carlfw_desc_add(fw, desc, prev, pos);
276 }
277
278 void carlfw_desc_unlink(struct carlfw *fw,
279         struct carl9170fw_desc_head *desc)
280 {
281         carlfw_entry_unlink(fw, carlfw_desc_to_entry(desc));
282 }
283
284 void carlfw_desc_del(struct carlfw *fw,
285         struct carl9170fw_desc_head *desc)
286 {
287         carlfw_entry_del(fw, carlfw_desc_to_entry(desc));
288 }
289
290 void *carlfw_desc_mod_len(struct carlfw *fw __unused,
291         struct carl9170fw_desc_head *desc, size_t len)
292 {
293         struct carlfw_list_entry *obj, tmp;
294         int new_len = le16_to_cpu(desc->length) + len;
295
296         if (new_len < (int)sizeof(*desc))
297                 return ERR_PTR(-EINVAL);
298
299         if (new_len > CARL9170FW_DESC_MAX_LENGTH)
300                 return ERR_PTR(-E2BIG);
301
302         obj = carlfw_desc_to_entry(desc);
303
304         memcpy(&tmp, obj, sizeof(tmp));
305         obj = realloc(obj, new_len + sizeof(struct carlfw_list_entry_head));
306         if (obj == NULL)
307                 return ERR_PTR(-ENOMEM);
308
309         list_replace(&tmp.h.list, &obj->h.list);
310
311         desc = carlfw_entry_to_desc(obj);
312         desc->length = le16_to_cpu(new_len);
313         fw->desc_list_len += len;
314
315         return desc;
316 }
317
318 void *carlfw_desc_next(struct carlfw *fw,
319                        struct carl9170fw_desc_head *pos)
320 {
321         struct carlfw_list_entry *entry;
322
323         if (!pos)
324                 entry = (struct carlfw_list_entry *) &fw->desc_list;
325         else
326                 entry = carlfw_desc_to_entry(pos);
327
328         if (list_at_tail(entry, &fw->desc_list, h.list))
329                 return NULL;
330
331         entry = (struct carlfw_list_entry *) entry->h.list.next;
332
333         return carlfw_entry_to_desc(entry);
334 }
335
336 static int carlfw_parse_descs(struct carlfw *fw,
337                               struct carl9170fw_otus_desc *otus_desc)
338 {
339         const struct carl9170fw_desc_head *iter = NULL;
340         int err;
341
342         carl9170fw_for_each_hdr(iter, &otus_desc->head) {
343                 err = carlfw_desc_add_tail(fw, iter);
344                 if (err)
345                         return err;
346         }
347         /* LAST is added automatically by carlfw_store */
348
349         return err;
350 }
351
352 #if BYTE_ORDER == LITTLE_ENDIAN
353 #define CRCPOLY_LE 0xedb88320
354
355 /* copied from the linux kernel  */
356 static uint32_t crc32_le(uint32_t crc, unsigned char const *p, size_t len)
357 {
358         int i;
359         while (len--) {
360                 crc ^= *p++;
361                 for (i = 0; i < 8; i++)
362                         crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
363         }
364         return crc;
365 }
366 #else
367 #error "this tool does not work with a big endian host yet!"
368 #endif
369
370 static int carlfw_check_crc32s(struct carlfw *fw)
371 {
372         struct carl9170fw_chk_desc *chk_desc;
373         struct carlfw_list_entry *iter;
374         unsigned int elen;
375         uint32_t crc32;
376
377         chk_desc = carlfw_find_desc(fw, (uint8_t *) CHK_MAGIC,
378                                     sizeof(*chk_desc),
379                                     CARL9170FW_CHK_DESC_CUR_VER);
380         if (!chk_desc)
381                 return -ENODATA;
382
383         crc32 = crc32_le(~0, (void *) fw->fw.data, fw->fw.len);
384         if (crc32 != le32_to_cpu(chk_desc->fw_crc32))
385                 return -EINVAL;
386
387         carlfw_walk_descs(iter, fw) {
388                 elen = le16_to_cpu(iter->head.length);
389
390                 if (carl9170fw_desc_cmp(&iter->head, (uint8_t *) CHK_MAGIC,
391                                         sizeof(*chk_desc),
392                                         CARL9170FW_CHK_DESC_CUR_VER))
393                         continue;
394
395                 crc32 = crc32_le(crc32, (void *) &iter->head, elen);
396         }
397
398         if (crc32 != le32_to_cpu(chk_desc->hdr_crc32))
399                 return -EINVAL;
400
401         return 0;
402 }
403
404 struct carlfw *carlfw_load(const char *basename)
405 {
406         char filename[256];
407         struct carlfw *fw;
408         struct carl9170fw_otus_desc *otus_desc;
409         struct carl9170fw_last_desc *last_desc;
410         struct carlfw_file *hdr_file;
411         unsigned long fin, diff, off, rem;
412         int err;
413
414         fw = calloc(1, sizeof(*fw));
415         if (!fw)
416                 return ERR_PTR(-ENOMEM);
417
418         init_list_head(&fw->desc_list);
419
420         err = __carlfw_load(&fw->fw, basename, "r");
421         if (err)
422                 goto err_out;
423
424         if (fw->hdr.name)
425                 hdr_file = &fw->hdr;
426         else
427                 hdr_file = &fw->fw;
428
429         otus_desc = __carlfw_find_desc(hdr_file, (uint8_t *) OTUS_MAGIC,
430                                        sizeof(*otus_desc),
431                                        CARL9170FW_OTUS_DESC_CUR_VER);
432         last_desc = __carlfw_find_desc(hdr_file, (uint8_t *) LAST_MAGIC,
433                                        sizeof(*last_desc),
434                                        CARL9170FW_LAST_DESC_CUR_VER);
435
436         if (!otus_desc || !last_desc ||
437             (unsigned long) otus_desc > (unsigned long) last_desc) {
438                 err = -ENODATA;
439                 goto err_out;
440         }
441
442         err = carlfw_parse_descs(fw, otus_desc);
443         if (err)
444                 goto err_out;
445
446         fin = (unsigned long)last_desc + sizeof(*last_desc);
447         diff = fin - (unsigned long)otus_desc;
448         rem = hdr_file->len - (fin - (unsigned long) hdr_file->data);
449
450         if (rem) {
451                 off = (unsigned long)otus_desc - (unsigned long)hdr_file->data;
452                 memmove(&hdr_file->data[off],
453                         ((uint8_t *)last_desc) + sizeof(*last_desc), rem);
454         }
455
456         hdr_file->len -= diff;
457         hdr_file->data = realloc(hdr_file->data, hdr_file->len);
458         if (!hdr_file->data && hdr_file->len) {
459                 err = -ENOMEM;
460                 goto err_out;
461         }
462
463         err = carlfw_check_crc32s(fw);
464         if (err && err != -ENODATA)
465                 goto err_out;
466
467         return fw;
468
469 err_out:
470         carlfw_release(fw);
471         return ERR_PTR(err);
472 }
473
474 static int carlfw_apply_checksums(struct carlfw *fw)
475 {
476         struct carlfw_list_entry *iter;
477         struct carl9170fw_chk_desc tmp = {
478                 CARL9170FW_FILL_DESC(CHK_MAGIC, sizeof(tmp),
479                                       CARL9170FW_CHK_DESC_MIN_VER,
480                                       CARL9170FW_CHK_DESC_CUR_VER) };
481         struct carl9170fw_chk_desc *chk_desc = NULL;
482         int err = 0;
483         unsigned int len = 0, elen, max_len;
484         uint32_t crc32;
485
486         chk_desc = carlfw_find_desc(fw, (uint8_t *) CHK_MAGIC,
487                                     sizeof(*chk_desc),
488                                     CARL9170FW_CHK_DESC_CUR_VER);
489         if (chk_desc) {
490                 carlfw_desc_del(fw, &chk_desc->head);
491                 chk_desc = NULL;
492         }
493
494         max_len = fw->desc_list_len;
495
496         crc32 = crc32_le(~0, (void *) fw->fw.data, fw->fw.len);
497         tmp.fw_crc32 = cpu_to_le32(crc32);
498
499         /*
500          * NOTE:
501          *
502          * The descriptor checksum is seeded with the firmware's crc32.
503          * This neat trick ensures that the driver can check whenever
504          * descriptor actually belongs to the firmware, or not.
505          */
506
507         carlfw_walk_descs(iter, fw) {
508                 elen = le16_to_cpu(iter->head.length);
509
510                 if (max_len < len + elen)
511                         return -EMSGSIZE;
512
513                 crc32 = crc32_le(crc32, (void *) &iter->head, elen);
514                 len += elen;
515         }
516
517         tmp.hdr_crc32 = cpu_to_le32(crc32);
518
519         err = carlfw_desc_add_tail(fw, &tmp.head);
520
521         return err;
522 }
523
524 int carlfw_store(struct carlfw *fw)
525 {
526         struct carl9170fw_last_desc last_desc = {
527                 CARL9170FW_FILL_DESC(LAST_MAGIC, sizeof(last_desc),
528                         CARL9170FW_LAST_DESC_MIN_VER,
529                         CARL9170FW_LAST_DESC_CUR_VER) };
530
531         struct carlfw_list_entry *iter;
532         FILE *fh;
533         int err, elen;
534
535         err = carlfw_apply_checksums(fw);
536         if (err)
537                 return err;
538
539         fh = fopen(fw->fw.name, "w");
540         if (!fh)
541                 return -errno;
542
543         err = fwrite(fw->fw.data, fw->fw.len, 1, fh);
544         if (err != 1) {
545                 err = -errno;
546                 goto close_out;
547         }
548
549         if (fw->hdr.name) {
550                 fclose(fh);
551
552                 fh = fopen(fw->hdr.name, "w");
553         }
554
555         carlfw_walk_descs(iter, fw) {
556                 elen = le16_to_cpu(iter->head.length);
557
558                 if (elen > CARL9170FW_DESC_MAX_LENGTH) {
559                         err = -E2BIG;
560                         goto close_out;
561                 }
562
563                 err = fwrite(iter->data, elen, 1, fh);
564                 if (err != 1) {
565                         err = -ferror(fh);
566                         goto close_out;
567                 }
568         }
569
570         err = fwrite(&last_desc, sizeof(last_desc), 1, fh);
571         if (err != 1) {
572                 err = -ferror(fh);
573                 goto close_out;
574         }
575
576         err = 0;
577
578 close_out:
579         fclose(fh);
580         return err;
581 }
582
583 void *carlfw_mod_tailroom(struct carlfw *fw, ssize_t len)
584 {
585         size_t new_len;
586         void *buf;
587
588         new_len = fw->fw.len + len;
589
590         if (!carl9170fw_size_check(new_len))
591                 return ERR_PTR(-EINVAL);
592
593         buf = realloc(fw->fw.data, new_len);
594         if (buf == NULL)
595                 return ERR_PTR(-ENOMEM);
596
597         fw->fw.len = new_len;
598         fw->fw.data = buf;
599         return &fw->fw.data[new_len - len];
600 }
601
602 void *carlfw_mod_headroom(struct carlfw *fw, ssize_t len)
603 {
604         size_t new_len;
605         void *ptr;
606
607         new_len = fw->fw.len + len;
608         if (!carl9170fw_size_check(new_len))
609                 return ERR_PTR(-EINVAL);
610
611         if (len < 0)
612                 memmove(fw->fw.data, &fw->fw.data[len], new_len);
613
614         ptr = carlfw_mod_tailroom(fw, len);
615         if (IS_ERR_OR_NULL(ptr))
616                 return ptr;
617
618         if (len > 0)
619                 memmove(&fw->fw.data[len], &fw->fw.data[0], new_len - len);
620
621         return fw->fw.data;
622 }
623
624 void *carlfw_get_fw(struct carlfw *fw, size_t *len)
625 {
626         *len = fw->fw.len;
627         return fw->fw.data;
628 }
629
630 unsigned int carlfw_get_descs_num(struct carlfw *fw)
631 {
632         return fw->desc_list_entries;
633 }
634
635 unsigned int carlfw_get_descs_size(struct carlfw *fw)
636 {
637         return fw->desc_list_len;
638 }