ASoC: fsl: Add S/PDIF CPU DAI driver
[deliverable/linux.git] / drivers / staging / csr / csr_wifi_hip_xbv.c
1 /*****************************************************************************
2
3 (c) Cambridge Silicon Radio Limited 2012
4 All rights reserved and confidential information of CSR
5
6 Refer to LICENSE.txt included with this source for details
7 on the license terms.
8
9 *****************************************************************************/
10
11 /*
12 * ---------------------------------------------------------------------------
13 * FILE: csr_wifi_hip_xbv.c
14 *
15 * PURPOSE:
16 * Routines for downloading firmware to UniFi.
17 *
18 * UniFi firmware files use a nested TLV (Tag-Length-Value) format.
19 *
20 * ---------------------------------------------------------------------------
21 */
22 #include <linux/slab.h>
23
24 #ifdef CSR_WIFI_XBV_TEST
25 /* Standalone test harness */
26 #include "unifi_xbv.h"
27 #include "csr_wifi_hip_unifihw.h"
28 #else
29 /* Normal driver build */
30 #include "csr_wifi_hip_unifiversion.h"
31 #include "csr_wifi_hip_card.h"
32 #define DBG_TAG(t)
33 #endif
34
35 #include "csr_wifi_hip_xbv.h"
36
37 #define STREAM_CHECKSUM 0x6d34 /* Sum of uint16s in each patch stream */
38
39 /* XBV sizes used in patch conversion
40 */
41 #define PTDL_MAX_SIZE 2048 /* Max bytes allowed per PTDL */
42 #define PTDL_HDR_SIZE (4 + 2 + 6 + 2) /* sizeof(fw_id, sec_len, patch_cmd, csum) */
43
44 /* Struct to represent a buffer for reading firmware file */
45
46 typedef struct
47 {
48 void *dlpriv;
49 s32 ioffset;
50 fwreadfn_t iread;
51 } ct_t;
52
53 /* Struct to represent a TLV field */
54 typedef struct
55 {
56 char t_name[4];
57 u32 t_len;
58 } tag_t;
59
60
61 #define TAG_EQ(i, v) (((i)[0] == (v)[0]) && \
62 ((i)[1] == (v)[1]) && \
63 ((i)[2] == (v)[2]) && \
64 ((i)[3] == (v)[3]))
65
66 /* We create a small stack on the stack that contains an enum
67 * indicating the containing list segments, and the offset at which
68 * those lists end. This enables a lot more error checking. */
69 typedef enum
70 {
71 xbv_xbv1,
72 /*xbv_info,*/
73 xbv_fw,
74 xbv_vers,
75 xbv_vand,
76 xbv_ptch,
77 xbv_other
78 } xbv_container;
79
80 #define XBV_STACK_SIZE 6
81 #define XBV_MAX_OFFS 0x7fffffff
82
83 typedef struct
84 {
85 struct
86 {
87 xbv_container container;
88 s32 ioffset_end;
89 } s[XBV_STACK_SIZE];
90 u32 ptr;
91 } xbv_stack_t;
92
93 static s32 read_tag(card_t *card, ct_t *ct, tag_t *tag);
94 static s32 read_bytes(card_t *card, ct_t *ct, void *buf, u32 len);
95 static s32 read_uint(card_t *card, ct_t *ct, u32 *u, u32 len);
96 static s32 xbv_check(xbv1_t *fwinfo, const xbv_stack_t *stack,
97 xbv_mode new_mode, xbv_container old_cont);
98 static s32 xbv_push(xbv1_t *fwinfo, xbv_stack_t *stack,
99 xbv_mode new_mode, xbv_container old_cont,
100 xbv_container new_cont, u32 ioff);
101
102 static u32 write_uint16(void *buf, const u32 offset,
103 const u16 val);
104 static u32 write_uint32(void *buf, const u32 offset,
105 const u32 val);
106 static u32 write_bytes(void *buf, const u32 offset,
107 const u8 *data, const u32 len);
108 static u32 write_tag(void *buf, const u32 offset,
109 const char *tag_str);
110 static u32 write_chunk(void *buf, const u32 offset,
111 const char *tag_str,
112 const u32 payload_len);
113 static u16 calc_checksum(void *buf, const u32 offset,
114 const u32 bytes_len);
115 static u32 calc_patch_size(const xbv1_t *fwinfo);
116
117 static u32 write_xbv_header(void *buf, const u32 offset,
118 const u32 file_payload_length);
119 static u32 write_ptch_header(void *buf, const u32 offset,
120 const u32 fw_id);
121 static u32 write_patchcmd(void *buf, const u32 offset,
122 const u32 dst_genaddr, const u16 len);
123 static u32 write_reset_ptdl(void *buf, const u32 offset,
124 const xbv1_t *fwinfo, u32 fw_id);
125 static u32 write_fwdl_to_ptdl(void *buf, const u32 offset,
126 fwreadfn_t readfn, const struct FWDL *fwdl,
127 const void *fw_buf, const u32 fw_id,
128 void *rdbuf);
129
130 /*
131 * ---------------------------------------------------------------------------
132 * parse_xbv1
133 *
134 * Scan the firmware file to find the TLVs we are interested in.
135 * Actions performed:
136 * - check we support the file format version in VERF
137 * Store these TLVs if we have a firmware image:
138 * - SLTP Symbol Lookup Table Pointer
139 * - FWDL firmware download segments
140 * - FWOL firmware overlay segment
141 * - VMEQ Register probe tests to verify matching h/w
142 * Store these TLVs if we have a patch file:
143 * - FWID the firmware build ID that this file patches
144 * - PTDL The actual patches
145 *
146 * The structure pointed to by fwinfo is cleared and
147 * 'fwinfo->mode' is set to 'unknown'. The 'fwinfo->mode'
148 * variable is set to 'firmware' or 'patch' once we know which
149 * sort of XBV file we have.
150 *
151 * Arguments:
152 * readfn Pointer to function to call to read from the file.
153 * dlpriv Opaque pointer arg to pass to readfn.
154 * fwinfo Pointer to fwinfo struct to fill in.
155 *
156 * Returns:
157 * CSR_RESULT_SUCCESS on success, CSR error code on failure
158 * ---------------------------------------------------------------------------
159 */
160 CsrResult xbv1_parse(card_t *card, fwreadfn_t readfn, void *dlpriv, xbv1_t *fwinfo)
161 {
162 ct_t ct;
163 tag_t tag;
164 xbv_stack_t stack;
165
166 ct.dlpriv = dlpriv;
167 ct.ioffset = 0;
168 ct.iread = readfn;
169
170 memset(fwinfo, 0, sizeof(xbv1_t));
171 fwinfo->mode = xbv_unknown;
172
173 /* File must start with XBV1 triplet */
174 if (read_tag(card, &ct, &tag) <= 0)
175 {
176 unifi_error(NULL, "File is not UniFi firmware\n");
177 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
178 }
179
180 DBG_TAG(tag.t_name);
181
182 if (!TAG_EQ(tag.t_name, "XBV1"))
183 {
184 unifi_error(NULL, "File is not UniFi firmware (%s)\n", tag.t_name);
185 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
186 }
187
188 stack.ptr = 0;
189 stack.s[stack.ptr].container = xbv_xbv1;
190 stack.s[stack.ptr].ioffset_end = XBV_MAX_OFFS;
191
192 /* Now scan the file */
193 while (1)
194 {
195 s32 n;
196
197 n = read_tag(card, &ct, &tag);
198 if (n < 0)
199 {
200 unifi_error(NULL, "No tag\n");
201 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
202 }
203 if (n == 0)
204 {
205 /* End of file */
206 break;
207 }
208
209 DBG_TAG(tag.t_name);
210
211 /* File format version */
212 if (TAG_EQ(tag.t_name, "VERF"))
213 {
214 u32 version;
215
216 if (xbv_check(fwinfo, &stack, xbv_unknown, xbv_xbv1) ||
217 (tag.t_len != 2) ||
218 read_uint(card, &ct, &version, 2))
219 {
220 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
221 }
222 if (version != 0)
223 {
224 unifi_error(NULL, "Unsupported firmware file version: %d.%d\n",
225 version >> 8, version & 0xFF);
226 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
227 }
228 }
229 else if (TAG_EQ(tag.t_name, "LIST"))
230 {
231 char name[4];
232 u32 list_end;
233
234 list_end = ct.ioffset + tag.t_len;
235
236 if (read_bytes(card, &ct, name, 4))
237 {
238 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
239 }
240
241 DBG_TAG(name);
242 if (TAG_EQ(name, "FW "))
243 {
244 if (xbv_push(fwinfo, &stack, xbv_firmware, xbv_xbv1, xbv_fw, list_end))
245 {
246 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
247 }
248 }
249 else if (TAG_EQ(name, "VERS"))
250 {
251 if (xbv_push(fwinfo, &stack, xbv_firmware, xbv_fw, xbv_vers, list_end) ||
252 (fwinfo->vers.num_vand != 0))
253 {
254 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
255 }
256 }
257 else if (TAG_EQ(name, "VAND"))
258 {
259 struct VAND *vand;
260
261 if (xbv_push(fwinfo, &stack, xbv_firmware, xbv_vers, xbv_vand, list_end) ||
262 (fwinfo->vers.num_vand >= MAX_VAND))
263 {
264 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
265 }
266
267 /* Get a new VAND */
268 vand = fwinfo->vand + fwinfo->vers.num_vand++;
269
270 /* Fill it in */
271 vand->first = fwinfo->num_vmeq;
272 vand->count = 0;
273 }
274 else if (TAG_EQ(name, "PTCH"))
275 {
276 if (xbv_push(fwinfo, &stack, xbv_patch, xbv_xbv1, xbv_ptch, list_end))
277 {
278 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
279 }
280 }
281 else
282 {
283 /* Skip over any other lists. We dont bother to push
284 * the new list type now as we would only pop it at
285 * the end of the outer loop. */
286 ct.ioffset += tag.t_len - 4;
287 }
288 }
289 else if (TAG_EQ(tag.t_name, "SLTP"))
290 {
291 u32 addr;
292
293 if (xbv_check(fwinfo, &stack, xbv_firmware, xbv_fw) ||
294 (tag.t_len != 4) ||
295 (fwinfo->slut_addr != 0) ||
296 read_uint(card, &ct, &addr, 4))
297 {
298 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
299 }
300
301 fwinfo->slut_addr = addr;
302 }
303 else if (TAG_EQ(tag.t_name, "FWDL"))
304 {
305 u32 addr;
306 struct FWDL *fwdl;
307
308 if (xbv_check(fwinfo, &stack, xbv_firmware, xbv_fw) ||
309 (fwinfo->num_fwdl >= MAX_FWDL) ||
310 (read_uint(card, &ct, &addr, 4)))
311 {
312 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
313 }
314
315 fwdl = fwinfo->fwdl + fwinfo->num_fwdl++;
316
317 fwdl->dl_size = tag.t_len - 4;
318 fwdl->dl_addr = addr;
319 fwdl->dl_offset = ct.ioffset;
320
321 ct.ioffset += tag.t_len - 4;
322 }
323 else if (TAG_EQ(tag.t_name, "FWOV"))
324 {
325 if (xbv_check(fwinfo, &stack, xbv_firmware, xbv_fw) ||
326 (fwinfo->fwov.dl_size != 0) ||
327 (fwinfo->fwov.dl_offset != 0))
328 {
329 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
330 }
331
332 fwinfo->fwov.dl_size = tag.t_len;
333 fwinfo->fwov.dl_offset = ct.ioffset;
334
335 ct.ioffset += tag.t_len;
336 }
337 else if (TAG_EQ(tag.t_name, "VMEQ"))
338 {
339 u32 temp[3];
340 struct VAND *vand;
341 struct VMEQ *vmeq;
342
343 if (xbv_check(fwinfo, &stack, xbv_firmware, xbv_vand) ||
344 (fwinfo->num_vmeq >= MAX_VMEQ) ||
345 (fwinfo->vers.num_vand == 0) ||
346 (tag.t_len != 8) ||
347 read_uint(card, &ct, &temp[0], 4) ||
348 read_uint(card, &ct, &temp[1], 2) ||
349 read_uint(card, &ct, &temp[2], 2))
350 {
351 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
352 }
353
354 /* Get the last VAND */
355 vand = fwinfo->vand + (fwinfo->vers.num_vand - 1);
356
357 /* Get a new VMEQ */
358 vmeq = fwinfo->vmeq + fwinfo->num_vmeq++;
359
360 /* Note that this VAND contains another VMEQ */
361 vand->count++;
362
363 /* Fill in the VMEQ */
364 vmeq->addr = temp[0];
365 vmeq->mask = (u16)temp[1];
366 vmeq->value = (u16)temp[2];
367 }
368 else if (TAG_EQ(tag.t_name, "FWID"))
369 {
370 u32 build_id;
371
372 if (xbv_check(fwinfo, &stack, xbv_patch, xbv_ptch) ||
373 (tag.t_len != 4) ||
374 (fwinfo->build_id != 0) ||
375 read_uint(card, &ct, &build_id, 4))
376 {
377 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
378 }
379
380 fwinfo->build_id = build_id;
381 }
382 else if (TAG_EQ(tag.t_name, "PTDL"))
383 {
384 struct PTDL *ptdl;
385
386 if (xbv_check(fwinfo, &stack, xbv_patch, xbv_ptch) ||
387 (fwinfo->num_ptdl >= MAX_PTDL))
388 {
389 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
390 }
391
392 /* Allocate a new PTDL */
393 ptdl = fwinfo->ptdl + fwinfo->num_ptdl++;
394
395 ptdl->dl_size = tag.t_len;
396 ptdl->dl_offset = ct.ioffset;
397
398 ct.ioffset += tag.t_len;
399 }
400 else
401 {
402 /*
403 * If we get here it is a tag we are not interested in,
404 * just skip over it.
405 */
406 ct.ioffset += tag.t_len;
407 }
408
409 /* Check to see if we are at the end of the currently stacked
410 * segment. We could finish more than one list at a time. */
411 while (ct.ioffset >= stack.s[stack.ptr].ioffset_end)
412 {
413 if (ct.ioffset > stack.s[stack.ptr].ioffset_end)
414 {
415 unifi_error(NULL,
416 "XBV file has overrun stack'd segment %d (%d > %d)\n",
417 stack.ptr, ct.ioffset, stack.s[stack.ptr].ioffset_end);
418 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
419 }
420 if (stack.ptr <= 0)
421 {
422 unifi_error(NULL, "XBV file has underrun stack pointer\n");
423 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
424 }
425 stack.ptr--;
426 }
427 }
428
429 if (stack.ptr != 0)
430 {
431 unifi_error(NULL, "Last list of XBV is not complete.\n");
432 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
433 }
434
435 return CSR_RESULT_SUCCESS;
436 } /* xbv1_parse() */
437
438
439 /* Check the the XBV file is of a consistant sort (either firmware or
440 * patch) and that we are in the correct containing list type. */
441 static s32 xbv_check(xbv1_t *fwinfo, const xbv_stack_t *stack,
442 xbv_mode new_mode, xbv_container old_cont)
443 {
444 /* If the new file mode is unknown the current packet could be in
445 * either (any) type of XBV file, and we cant make a decission at
446 * this time. */
447 if (new_mode != xbv_unknown)
448 {
449 if (fwinfo->mode == xbv_unknown)
450 {
451 fwinfo->mode = new_mode;
452 }
453 else if (fwinfo->mode != new_mode)
454 {
455 return -1;
456 }
457 }
458 /* If the current stack top doesn't match what we expect then the
459 * file is corrupt. */
460 if (stack->s[stack->ptr].container != old_cont)
461 {
462 return -1;
463 }
464 return 0;
465 }
466
467
468 /* Make checks as above and then enter a new list */
469 static s32 xbv_push(xbv1_t *fwinfo, xbv_stack_t *stack,
470 xbv_mode new_mode, xbv_container old_cont,
471 xbv_container new_cont, u32 new_ioff)
472 {
473 if (xbv_check(fwinfo, stack, new_mode, old_cont))
474 {
475 return -1;
476 }
477
478 /* Check that our stack won't overflow. */
479 if (stack->ptr >= (XBV_STACK_SIZE - 1))
480 {
481 return -1;
482 }
483
484 /* Add the new list element to the top of the stack. */
485 stack->ptr++;
486 stack->s[stack->ptr].container = new_cont;
487 stack->s[stack->ptr].ioffset_end = new_ioff;
488
489 return 0;
490 }
491
492
493 static u32 xbv2uint(u8 *ptr, s32 len)
494 {
495 u32 u = 0;
496 s16 i;
497
498 for (i = 0; i < len; i++)
499 {
500 u32 b;
501 b = ptr[i];
502 u += b << (i * 8);
503 }
504 return u;
505 }
506
507
508 static s32 read_tag(card_t *card, ct_t *ct, tag_t *tag)
509 {
510 u8 buf[8];
511 s32 n;
512
513 n = (*ct->iread)(card->ospriv, ct->dlpriv, ct->ioffset, buf, 8);
514 if (n <= 0)
515 {
516 return n;
517 }
518
519 /* read the tag and length */
520 if (n != 8)
521 {
522 return -1;
523 }
524
525 /* get section tag */
526 memcpy(tag->t_name, buf, 4);
527
528 /* get section length */
529 tag->t_len = xbv2uint(buf + 4, 4);
530
531 ct->ioffset += 8;
532
533 return 8;
534 } /* read_tag() */
535
536
537 static s32 read_bytes(card_t *card, ct_t *ct, void *buf, u32 len)
538 {
539 /* read the tag value */
540 if ((*ct->iread)(card->ospriv, ct->dlpriv, ct->ioffset, buf, len) != (s32)len)
541 {
542 return -1;
543 }
544
545 ct->ioffset += len;
546
547 return 0;
548 } /* read_bytes() */
549
550
551 static s32 read_uint(card_t *card, ct_t *ct, u32 *u, u32 len)
552 {
553 u8 buf[4];
554
555 /* Integer cannot be more than 4 bytes */
556 if (len > 4)
557 {
558 return -1;
559 }
560
561 if (read_bytes(card, ct, buf, len))
562 {
563 return -1;
564 }
565
566 *u = xbv2uint(buf, len);
567
568 return 0;
569 } /* read_uint() */
570
571
572 static u32 write_uint16(void *buf, const u32 offset, const u16 val)
573 {
574 u8 *dst = (u8 *)buf + offset;
575 *dst++ = (u8)(val & 0xff); /* LSB first */
576 *dst = (u8)(val >> 8);
577 return sizeof(u16);
578 }
579
580
581 static u32 write_uint32(void *buf, const u32 offset, const u32 val)
582 {
583 (void)write_uint16(buf, offset + 0, (u16)(val & 0xffff));
584 (void)write_uint16(buf, offset + 2, (u16)(val >> 16));
585 return sizeof(u32);
586 }
587
588
589 static u32 write_bytes(void *buf, const u32 offset, const u8 *data, const u32 len)
590 {
591 u32 i;
592 u8 *dst = (u8 *)buf + offset;
593
594 for (i = 0; i < len; i++)
595 {
596 *dst++ = *((u8 *)data + i);
597 }
598 return len;
599 }
600
601
602 static u32 write_tag(void *buf, const u32 offset, const char *tag_str)
603 {
604 u8 *dst = (u8 *)buf + offset;
605 memcpy(dst, tag_str, 4);
606 return 4;
607 }
608
609
610 static u32 write_chunk(void *buf, const u32 offset, const char *tag_str, const u32 payload_len)
611 {
612 u32 written = 0;
613 written += write_tag(buf, offset, tag_str);
614 written += write_uint32(buf, written + offset, (u32)payload_len);
615
616 return written;
617 }
618
619
620 static u16 calc_checksum(void *buf, const u32 offset, const u32 bytes_len)
621 {
622 u32 i;
623 u8 *src = (u8 *)buf + offset;
624 u16 sum = 0;
625 u16 val;
626
627 for (i = 0; i < bytes_len / 2; i++)
628 {
629 /* Contents copied to file is LE, host might not be */
630 val = (u16) * src++; /* LSB */
631 val += (u16)(*src++) << 8; /* MSB */
632 sum += val;
633 }
634
635 /* Total of uint16s in the stream plus the stored check value
636 * should equal STREAM_CHECKSUM when decoded.
637 */
638 return (STREAM_CHECKSUM - sum);
639 }
640
641
642 #define PTDL_RESET_DATA_SIZE 20 /* Size of reset vectors PTDL */
643
644 static u32 calc_patch_size(const xbv1_t *fwinfo)
645 {
646 s16 i;
647 u32 size = 0;
648
649 /*
650 * Work out how big an equivalent patch format file must be for this image.
651 * This only needs to be approximate, so long as it's large enough.
652 */
653 if (fwinfo->mode != xbv_firmware)
654 {
655 return 0;
656 }
657
658 /* Payload (which will get put into a series of PTDLs) */
659 for (i = 0; i < fwinfo->num_fwdl; i++)
660 {
661 size += fwinfo->fwdl[i].dl_size;
662 }
663
664 /* Another PTDL at the end containing reset vectors */
665 size += PTDL_RESET_DATA_SIZE;
666
667 /* PTDL headers. Add one for remainder, one for reset vectors */
668 size += ((fwinfo->num_fwdl / PTDL_MAX_SIZE) + 2) * PTDL_HDR_SIZE;
669
670 /* Another 1K sufficient to cover miscellaneous headers */
671 size += 1024;
672
673 return size;
674 }
675
676
677 static u32 write_xbv_header(void *buf, const u32 offset, const u32 file_payload_length)
678 {
679 u32 written = 0;
680
681 /* The length value given to the XBV chunk is the length of all subsequent
682 * contents of the file, excluding the 8 byte size of the XBV1 header itself
683 * (The added 6 bytes thus accounts for the size of the VERF)
684 */
685 written += write_chunk(buf, offset + written, (char *)"XBV1", file_payload_length + 6);
686
687 written += write_chunk(buf, offset + written, (char *)"VERF", 2);
688 written += write_uint16(buf, offset + written, 0); /* File version */
689
690 return written;
691 }
692
693
694 static u32 write_ptch_header(void *buf, const u32 offset, const u32 fw_id)
695 {
696 u32 written = 0;
697
698 /* LIST is written with a zero length, to be updated later */
699 written += write_chunk(buf, offset + written, (char *)"LIST", 0);
700 written += write_tag(buf, offset + written, (char *)"PTCH"); /* List type */
701
702 written += write_chunk(buf, offset + written, (char *)"FWID", 4);
703 written += write_uint32(buf, offset + written, fw_id);
704
705
706 return written;
707 }
708
709
710 #define UF_REGION_PHY 1
711 #define UF_REGION_MAC 2
712 #define UF_MEMPUT_MAC 0x0000
713 #define UF_MEMPUT_PHY 0x1000
714
715 static u32 write_patchcmd(void *buf, const u32 offset, const u32 dst_genaddr, const u16 len)
716 {
717 u32 written = 0;
718 u32 region = (dst_genaddr >> 28);
719 u16 cmd_and_len = UF_MEMPUT_MAC;
720
721 if (region == UF_REGION_PHY)
722 {
723 cmd_and_len = UF_MEMPUT_PHY;
724 }
725 else if (region != UF_REGION_MAC)
726 {
727 return 0; /* invalid */
728 }
729
730 /* Write the command and data length */
731 cmd_and_len |= len;
732 written += write_uint16(buf, offset + written, cmd_and_len);
733
734 /* Write the destination generic address */
735 written += write_uint16(buf, offset + written, (u16)(dst_genaddr >> 16));
736 written += write_uint16(buf, offset + written, (u16)(dst_genaddr & 0xffff));
737
738 /* The data payload should be appended to the command */
739 return written;
740 }
741
742
743 static u32 write_fwdl_to_ptdl(void *buf, const u32 offset, fwreadfn_t readfn,
744 const struct FWDL *fwdl, const void *dlpriv,
745 const u32 fw_id, void *fw_buf)
746 {
747 u32 written = 0;
748 s16 chunks = 0;
749 u32 left = fwdl->dl_size; /* Bytes left in this fwdl */
750 u32 dl_addr = fwdl->dl_addr; /* Target address of fwdl image on XAP */
751 u32 dl_offs = fwdl->dl_offset; /* Offset of fwdl image data in source */
752 u16 csum;
753 u32 csum_start_offs; /* first offset to include in checksum */
754 u32 sec_data_len; /* section data byte count */
755 u32 sec_len; /* section data + header byte count */
756
757 /* FWDL maps to one or more PTDLs, as max size for a PTDL is 1K words */
758 while (left)
759 {
760 /* Calculate amount to be transferred */
761 sec_data_len = min_t(u32, left, PTDL_MAX_SIZE - PTDL_HDR_SIZE);
762 sec_len = sec_data_len + PTDL_HDR_SIZE;
763
764 /* Write PTDL header + entire PTDL size */
765 written += write_chunk(buf, offset + written, (char *)"PTDL", sec_len);
766 /* bug digest implies 4 bytes of padding here, but that seems wrong */
767
768 /* Checksum starts here */
769 csum_start_offs = offset + written;
770
771 /* Patch-chunk header: fw_id. Note that this is in XAP word order */
772 written += write_uint16(buf, offset + written, (u16)(fw_id >> 16));
773 written += write_uint16(buf, offset + written, (u16)(fw_id & 0xffff));
774
775 /* Patch-chunk header: section length in uint16s */
776 written += write_uint16(buf, offset + written, (u16)(sec_len / 2));
777
778
779 /* Write the appropriate patch command for the data's destination ptr */
780 written += write_patchcmd(buf, offset + written, dl_addr, (u16)(sec_data_len / 2));
781
782 /* Write the data itself (limited to the max chunk length) */
783 if (readfn(NULL, (void *)dlpriv, dl_offs, fw_buf, sec_data_len) < 0)
784 {
785 return 0;
786 }
787
788 written += write_bytes(buf,
789 offset + written,
790 fw_buf,
791 sec_data_len);
792
793 /* u16 checksum calculated over data written */
794 csum = calc_checksum(buf, csum_start_offs, written - (csum_start_offs - offset));
795 written += write_uint16(buf, offset + written, csum);
796
797 left -= sec_data_len;
798 dl_addr += sec_data_len;
799 dl_offs += sec_data_len;
800 chunks++;
801 }
802
803 return written;
804 }
805
806
807 #define SEC_CMD_LEN ((4 + 2) * 2) /* sizeof(cmd, vector) per XAP */
808 #define PTDL_VEC_HDR_SIZE (4 + 2 + 2) /* sizeof(fw_id, sec_len, csum) */
809 #define UF_MAC_START_VEC 0x00c00000 /* Start address of image on MAC */
810 #define UF_PHY_START_VEC 0x00c00000 /* Start address of image on PHY */
811 #define UF_MAC_START_CMD 0x6000 /* MAC "Set start address" command */
812 #define UF_PHY_START_CMD 0x7000 /* PHY "Set start address" command */
813
814 static u32 write_reset_ptdl(void *buf, const u32 offset, const xbv1_t *fwinfo, u32 fw_id)
815 {
816 u32 written = 0;
817 u16 csum;
818 u32 csum_start_offs; /* first offset to include in checksum */
819 u32 sec_len; /* section data + header byte count */
820
821 sec_len = SEC_CMD_LEN + PTDL_VEC_HDR_SIZE; /* Total section byte length */
822
823 /* Write PTDL header + entire PTDL size */
824 written += write_chunk(buf, offset + written, (char *)"PTDL", sec_len);
825
826 /* Checksum starts here */
827 csum_start_offs = offset + written;
828
829 /* Patch-chunk header: fw_id. Note that this is in XAP word order */
830 written += write_uint16(buf, offset + written, (u16)(fw_id >> 16));
831 written += write_uint16(buf, offset + written, (u16)(fw_id & 0xffff));
832
833 /* Patch-chunk header: section length in uint16s */
834 written += write_uint16(buf, offset + written, (u16)(sec_len / 2));
835
836 /*
837 * Restart addresses to be executed on subsequent loader restart command.
838 */
839
840 /* Setup the MAC start address, note word ordering */
841 written += write_uint16(buf, offset + written, UF_MAC_START_CMD);
842 written += write_uint16(buf, offset + written, (UF_MAC_START_VEC >> 16));
843 written += write_uint16(buf, offset + written, (UF_MAC_START_VEC & 0xffff));
844
845 /* Setup the PHY start address, note word ordering */
846 written += write_uint16(buf, offset + written, UF_PHY_START_CMD);
847 written += write_uint16(buf, offset + written, (UF_PHY_START_VEC >> 16));
848 written += write_uint16(buf, offset + written, (UF_PHY_START_VEC & 0xffff));
849
850 /* u16 checksum calculated over data written */
851 csum = calc_checksum(buf, csum_start_offs, written - (csum_start_offs - offset));
852 written += write_uint16(buf, offset + written, csum);
853
854 return written;
855 }
856
857
858 /*
859 * ---------------------------------------------------------------------------
860 * read_slut
861 *
862 * desc
863 *
864 * Arguments:
865 * readfn Pointer to function to call to read from the file.
866 * dlpriv Opaque pointer arg to pass to readfn.
867 * addr Offset into firmware image of SLUT.
868 * fwinfo Pointer to fwinfo struct to fill in.
869 *
870 * Returns:
871 * Number of SLUT entries in the f/w, or -1 if the image was corrupt.
872 * ---------------------------------------------------------------------------
873 */
874 s32 xbv1_read_slut(card_t *card, fwreadfn_t readfn, void *dlpriv, xbv1_t *fwinfo,
875 symbol_t *slut, u32 slut_len)
876 {
877 s16 i;
878 s32 offset;
879 u32 magic;
880 u32 count = 0;
881 ct_t ct;
882
883 if (fwinfo->mode != xbv_firmware)
884 {
885 return -1;
886 }
887
888 /* Find the d/l segment containing the SLUT */
889 /* This relies on the SLUT being entirely contained in one segment */
890 offset = -1;
891 for (i = 0; i < fwinfo->num_fwdl; i++)
892 {
893 if ((fwinfo->slut_addr >= fwinfo->fwdl[i].dl_addr) &&
894 (fwinfo->slut_addr < (fwinfo->fwdl[i].dl_addr + fwinfo->fwdl[i].dl_size)))
895 {
896 offset = fwinfo->fwdl[i].dl_offset +
897 (fwinfo->slut_addr - fwinfo->fwdl[i].dl_addr);
898 }
899 }
900 if (offset < 0)
901 {
902 return -1;
903 }
904
905 ct.dlpriv = dlpriv;
906 ct.ioffset = offset;
907 ct.iread = readfn;
908
909 if (read_uint(card, &ct, &magic, 2))
910 {
911 return -1;
912 }
913 if (magic != SLUT_FINGERPRINT)
914 {
915 return -1;
916 }
917
918 while (count < slut_len)
919 {
920 u32 id, obj;
921
922 /* Read Symbol Id */
923 if (read_uint(card, &ct, &id, 2))
924 {
925 return -1;
926 }
927
928 /* Check for end of table marker */
929 if (id == CSR_SLT_END)
930 {
931 break;
932 }
933
934 /* Read Symbol Value */
935 if (read_uint(card, &ct, &obj, 4))
936 {
937 return -1;
938 }
939
940 slut[count].id = (u16)id;
941 slut[count].obj = obj;
942 count++;
943 }
944
945 return count;
946 } /* read_slut() */
947
948
949 /*
950 * ---------------------------------------------------------------------------
951 * xbv_to_patch
952 *
953 * Convert (the relevant parts of) a firmware xbv file into a patch xbv
954 *
955 * Arguments:
956 * card
957 * fw_buf - pointer to xbv firmware image
958 * fwinfo - structure describing the firmware image
959 * size - pointer to location into which size of f/w is written.
960 *
961 * Returns:
962 * Pointer to firmware image, or NULL on error. Caller must free this
963 * buffer via kfree() once it's finished with.
964 *
965 * Notes:
966 * The input fw_buf should have been checked via xbv1_parse prior to
967 * calling this function, so the input image is assumed valid.
968 * ---------------------------------------------------------------------------
969 */
970 #define PTCH_LIST_SIZE 16 /* sizeof PTCH+FWID chunk in LIST header */
971
972 void* xbv_to_patch(card_t *card, fwreadfn_t readfn,
973 const void *fw_buf, const xbv1_t *fwinfo, u32 *size)
974 {
975 void *patch_buf = NULL;
976 u32 patch_buf_size;
977 u32 payload_offs = 0; /* Start of XBV payload */
978 s16 i;
979 u32 patch_offs = 0;
980 u32 list_len_offs = 0; /* Offset of PTDL LIST length parameter */
981 u32 ptdl_start_offs = 0; /* Offset of first PTDL chunk */
982 u32 fw_id;
983 void *rdbuf;
984
985 if (!fw_buf || !fwinfo || !card)
986 {
987 return NULL;
988 }
989
990 if (fwinfo->mode != xbv_firmware)
991 {
992 unifi_error(NULL, "Not a firmware file\n");
993 return NULL;
994 }
995
996 /* Pre-allocate read buffer for chunk conversion */
997 rdbuf = kmalloc(PTDL_MAX_SIZE, GFP_KERNEL);
998 if (!rdbuf)
999 {
1000 unifi_error(card, "Couldn't alloc conversion buffer\n");
1001 return NULL;
1002 }
1003
1004 /* Loader requires patch file's build ID to match the running firmware's */
1005 fw_id = card->build_id;
1006
1007 /* Firmware XBV1 contains VERF, optional INFO, SLUT(s), FWDL(s) */
1008 /* Other chunks should get skipped. */
1009 /* VERF should be sanity-checked against chip version */
1010
1011 /* Patch XBV1 contains VERF, optional INFO, PTCH */
1012 /* PTCH contains FWID, optional INFO, PTDL(s), PTDL(start_vec) */
1013 /* Each FWDL is split into PTDLs (each is 1024 XAP words max) */
1014 /* Each PTDL contains running ROM f/w version, and checksum */
1015 /* MAC/PHY reset addresses (known) are added into a final PTDL */
1016
1017 /* The input image has already been parsed, and loaded into fwinfo, so we
1018 * can use that to build the output image
1019 */
1020 patch_buf_size = calc_patch_size(fwinfo);
1021
1022 patch_buf = kmalloc(patch_buf_size, GFP_KERNEL);
1023 if (!patch_buf)
1024 {
1025 kfree(rdbuf);
1026 unifi_error(NULL, "Can't malloc buffer for patch conversion\n");
1027 return NULL;
1028 }
1029
1030 memset(patch_buf, 0xdd, patch_buf_size);
1031
1032 /* Write XBV + VERF headers */
1033 patch_offs += write_xbv_header(patch_buf, patch_offs, 0);
1034 payload_offs = patch_offs;
1035
1036 /* Write patch (LIST) header */
1037 list_len_offs = patch_offs + 4; /* Save LIST.length offset for later update */
1038 patch_offs += write_ptch_header(patch_buf, patch_offs, fw_id);
1039
1040 /* Save start offset of the PTDL chunks */
1041 ptdl_start_offs = patch_offs;
1042
1043 /* Write LIST of firmware PTDL blocks */
1044 for (i = 0; i < fwinfo->num_fwdl; i++)
1045 {
1046 patch_offs += write_fwdl_to_ptdl(patch_buf,
1047 patch_offs,
1048 readfn,
1049 &fwinfo->fwdl[i],
1050 fw_buf,
1051 fw_id,
1052 rdbuf);
1053 }
1054
1055 /* Write restart-vector PTDL last */
1056 patch_offs += write_reset_ptdl(patch_buf, patch_offs, fwinfo, fw_id);
1057
1058 /* Now the length is known, update the LIST.length */
1059 (void)write_uint32(patch_buf, list_len_offs,
1060 (patch_offs - ptdl_start_offs) + PTCH_LIST_SIZE);
1061
1062 /* Re write XBV headers just to fill in the correct file size */
1063 (void)write_xbv_header(patch_buf, 0, (patch_offs - payload_offs));
1064
1065 unifi_trace(card->ospriv, UDBG1, "XBV:PTCH size %u, fw_id %u\n",
1066 patch_offs, fw_id);
1067 if (size)
1068 {
1069 *size = patch_offs;
1070 }
1071 kfree(rdbuf);
1072
1073 return patch_buf;
1074 }
1075
1076
This page took 0.055514 seconds and 5 git commands to generate.