1 /*****************************************************************************
3 (c) Cambridge Silicon Radio Limited 2012
4 All rights reserved and confidential information of CSR
6 Refer to LICENSE.txt included with this source for details
9 *****************************************************************************/
12 * ---------------------------------------------------------------------------
13 * FILE: csr_wifi_hip_xbv.c
16 * Routines for downloading firmware to UniFi.
18 * UniFi firmware files use a nested TLV (Tag-Length-Value) format.
20 * ---------------------------------------------------------------------------
23 #ifdef CSR_WIFI_XBV_TEST
24 /* Standalone test harness */
25 #include "unifi_xbv.h"
26 #include "csr_wifi_hip_unifihw.h"
28 /* Normal driver build */
29 #include "csr_wifi_hip_unifiversion.h"
30 #include "csr_wifi_hip_card.h"
34 #include "csr_wifi_hip_xbv.h"
36 #define STREAM_CHECKSUM 0x6d34 /* Sum of uint16s in each patch stream */
38 /* XBV sizes used in patch conversion
40 #define PTDL_MAX_SIZE 2048 /* Max bytes allowed per PTDL */
41 #define PTDL_HDR_SIZE (4 + 2 + 6 + 2) /* sizeof(fw_id, sec_len, patch_cmd, csum) */
43 /* Struct to represent a buffer for reading firmware file */
52 /* Struct to represent a TLV field */
55 CsrCharString t_name[4];
60 #define TAG_EQ(i, v) (((i)[0] == (v)[0]) && \
61 ((i)[1] == (v)[1]) && \
62 ((i)[2] == (v)[2]) && \
65 /* We create a small stack on the stack that contains an enum
66 * indicating the containing list segments, and the offset at which
67 * those lists end. This enables a lot more error checking. */
79 #define XBV_STACK_SIZE 6
80 #define XBV_MAX_OFFS 0x7fffffff
86 xbv_container container;
92 static CsrInt32 read_tag(card_t *card, ct_t *ct, tag_t *tag);
93 static CsrInt32 read_bytes(card_t *card, ct_t *ct, void *buf, CsrUint32 len);
94 static CsrInt32 read_uint(card_t *card, ct_t *ct, CsrUint32 *u, CsrUint32 len);
95 static CsrInt32 xbv_check(xbv1_t *fwinfo, const xbv_stack_t *stack,
96 xbv_mode new_mode, xbv_container old_cont);
97 static CsrInt32 xbv_push(xbv1_t *fwinfo, xbv_stack_t *stack,
98 xbv_mode new_mode, xbv_container old_cont,
99 xbv_container new_cont, CsrUint32 ioff);
101 static CsrUint32 write_uint16(void *buf, const CsrUint32 offset,
103 static CsrUint32 write_uint32(void *buf, const CsrUint32 offset,
104 const CsrUint32 val);
105 static CsrUint32 write_bytes(void *buf, const CsrUint32 offset,
106 const u8 *data, const CsrUint32 len);
107 static CsrUint32 write_tag(void *buf, const CsrUint32 offset,
108 const CsrCharString *tag_str);
109 static CsrUint32 write_chunk(void *buf, const CsrUint32 offset,
110 const CsrCharString *tag_str,
111 const CsrUint32 payload_len);
112 static u16 calc_checksum(void *buf, const CsrUint32 offset,
113 const CsrUint32 bytes_len);
114 static CsrUint32 calc_patch_size(const xbv1_t *fwinfo);
116 static CsrUint32 write_xbv_header(void *buf, const CsrUint32 offset,
117 const CsrUint32 file_payload_length);
118 static CsrUint32 write_ptch_header(void *buf, const CsrUint32 offset,
119 const CsrUint32 fw_id);
120 static CsrUint32 write_patchcmd(void *buf, const CsrUint32 offset,
121 const CsrUint32 dst_genaddr, const u16 len);
122 static CsrUint32 write_reset_ptdl(void *buf, const CsrUint32 offset,
123 const xbv1_t *fwinfo, CsrUint32 fw_id);
124 static CsrUint32 write_fwdl_to_ptdl(void *buf, const CsrUint32 offset,
125 fwreadfn_t readfn, const struct FWDL *fwdl,
126 const void *fw_buf, const CsrUint32 fw_id,
130 * ---------------------------------------------------------------------------
133 * Scan the firmware file to find the TLVs we are interested in.
135 * - check we support the file format version in VERF
136 * Store these TLVs if we have a firmware image:
137 * - SLTP Symbol Lookup Table Pointer
138 * - FWDL firmware download segments
139 * - FWOL firmware overlay segment
140 * - VMEQ Register probe tests to verify matching h/w
141 * Store these TLVs if we have a patch file:
142 * - FWID the firmware build ID that this file patches
143 * - PTDL The actual patches
145 * The structure pointed to by fwinfo is cleared and
146 * 'fwinfo->mode' is set to 'unknown'. The 'fwinfo->mode'
147 * variable is set to 'firmware' or 'patch' once we know which
148 * sort of XBV file we have.
151 * readfn Pointer to function to call to read from the file.
152 * dlpriv Opaque pointer arg to pass to readfn.
153 * fwinfo Pointer to fwinfo struct to fill in.
156 * CSR_RESULT_SUCCESS on success, CSR error code on failure
157 * ---------------------------------------------------------------------------
159 CsrResult xbv1_parse(card_t *card, fwreadfn_t readfn, void *dlpriv, xbv1_t *fwinfo)
169 CsrMemSet(fwinfo, 0, sizeof(xbv1_t));
170 fwinfo->mode = xbv_unknown;
172 /* File must start with XBV1 triplet */
173 if (read_tag(card, &ct, &tag) <= 0)
175 unifi_error(NULL, "File is not UniFi firmware\n");
176 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
181 if (!TAG_EQ(tag.t_name, "XBV1"))
183 unifi_error(NULL, "File is not UniFi firmware (%s)\n", tag.t_name);
184 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
188 stack.s[stack.ptr].container = xbv_xbv1;
189 stack.s[stack.ptr].ioffset_end = XBV_MAX_OFFS;
191 /* Now scan the file */
196 n = read_tag(card, &ct, &tag);
199 unifi_error(NULL, "No tag\n");
200 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
210 /* File format version */
211 if (TAG_EQ(tag.t_name, "VERF"))
215 if (xbv_check(fwinfo, &stack, xbv_unknown, xbv_xbv1) ||
217 read_uint(card, &ct, &version, 2))
219 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
223 unifi_error(NULL, "Unsupported firmware file version: %d.%d\n",
224 version >> 8, version & 0xFF);
225 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
228 else if (TAG_EQ(tag.t_name, "LIST"))
230 CsrCharString name[4];
233 list_end = ct.ioffset + tag.t_len;
235 if (read_bytes(card, &ct, name, 4))
237 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
241 if (TAG_EQ(name, "FW "))
243 if (xbv_push(fwinfo, &stack, xbv_firmware, xbv_xbv1, xbv_fw, list_end))
245 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
248 else if (TAG_EQ(name, "VERS"))
250 if (xbv_push(fwinfo, &stack, xbv_firmware, xbv_fw, xbv_vers, list_end) ||
251 (fwinfo->vers.num_vand != 0))
253 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
256 else if (TAG_EQ(name, "VAND"))
260 if (xbv_push(fwinfo, &stack, xbv_firmware, xbv_vers, xbv_vand, list_end) ||
261 (fwinfo->vers.num_vand >= MAX_VAND))
263 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
267 vand = fwinfo->vand + fwinfo->vers.num_vand++;
270 vand->first = fwinfo->num_vmeq;
273 else if (TAG_EQ(name, "PTCH"))
275 if (xbv_push(fwinfo, &stack, xbv_patch, xbv_xbv1, xbv_ptch, list_end))
277 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
282 /* Skip over any other lists. We dont bother to push
283 * the new list type now as we would only pop it at
284 * the end of the outer loop. */
285 ct.ioffset += tag.t_len - 4;
288 else if (TAG_EQ(tag.t_name, "SLTP"))
292 if (xbv_check(fwinfo, &stack, xbv_firmware, xbv_fw) ||
294 (fwinfo->slut_addr != 0) ||
295 read_uint(card, &ct, &addr, 4))
297 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
300 fwinfo->slut_addr = addr;
302 else if (TAG_EQ(tag.t_name, "FWDL"))
307 if (xbv_check(fwinfo, &stack, xbv_firmware, xbv_fw) ||
308 (fwinfo->num_fwdl >= MAX_FWDL) ||
309 (read_uint(card, &ct, &addr, 4)))
311 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
314 fwdl = fwinfo->fwdl + fwinfo->num_fwdl++;
316 fwdl->dl_size = tag.t_len - 4;
317 fwdl->dl_addr = addr;
318 fwdl->dl_offset = ct.ioffset;
320 ct.ioffset += tag.t_len - 4;
322 else if (TAG_EQ(tag.t_name, "FWOV"))
324 if (xbv_check(fwinfo, &stack, xbv_firmware, xbv_fw) ||
325 (fwinfo->fwov.dl_size != 0) ||
326 (fwinfo->fwov.dl_offset != 0))
328 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
331 fwinfo->fwov.dl_size = tag.t_len;
332 fwinfo->fwov.dl_offset = ct.ioffset;
334 ct.ioffset += tag.t_len;
336 else if (TAG_EQ(tag.t_name, "VMEQ"))
342 if (xbv_check(fwinfo, &stack, xbv_firmware, xbv_vand) ||
343 (fwinfo->num_vmeq >= MAX_VMEQ) ||
344 (fwinfo->vers.num_vand == 0) ||
346 read_uint(card, &ct, &temp[0], 4) ||
347 read_uint(card, &ct, &temp[1], 2) ||
348 read_uint(card, &ct, &temp[2], 2))
350 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
353 /* Get the last VAND */
354 vand = fwinfo->vand + (fwinfo->vers.num_vand - 1);
357 vmeq = fwinfo->vmeq + fwinfo->num_vmeq++;
359 /* Note that this VAND contains another VMEQ */
362 /* Fill in the VMEQ */
363 vmeq->addr = temp[0];
364 vmeq->mask = (u16)temp[1];
365 vmeq->value = (u16)temp[2];
367 else if (TAG_EQ(tag.t_name, "FWID"))
371 if (xbv_check(fwinfo, &stack, xbv_patch, xbv_ptch) ||
373 (fwinfo->build_id != 0) ||
374 read_uint(card, &ct, &build_id, 4))
376 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
379 fwinfo->build_id = build_id;
381 else if (TAG_EQ(tag.t_name, "PTDL"))
385 if (xbv_check(fwinfo, &stack, xbv_patch, xbv_ptch) ||
386 (fwinfo->num_ptdl >= MAX_PTDL))
388 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
391 /* Allocate a new PTDL */
392 ptdl = fwinfo->ptdl + fwinfo->num_ptdl++;
394 ptdl->dl_size = tag.t_len;
395 ptdl->dl_offset = ct.ioffset;
397 ct.ioffset += tag.t_len;
402 * If we get here it is a tag we are not interested in,
405 ct.ioffset += tag.t_len;
408 /* Check to see if we are at the end of the currently stacked
409 * segment. We could finish more than one list at a time. */
410 while (ct.ioffset >= stack.s[stack.ptr].ioffset_end)
412 if (ct.ioffset > stack.s[stack.ptr].ioffset_end)
415 "XBV file has overrun stack'd segment %d (%d > %d)\n",
416 stack.ptr, ct.ioffset, stack.s[stack.ptr].ioffset_end);
417 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
421 unifi_error(NULL, "XBV file has underrun stack pointer\n");
422 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
430 unifi_error(NULL, "Last list of XBV is not complete.\n");
431 return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
434 return CSR_RESULT_SUCCESS;
438 /* Check the the XBV file is of a consistant sort (either firmware or
439 * patch) and that we are in the correct containing list type. */
440 static CsrInt32 xbv_check(xbv1_t *fwinfo, const xbv_stack_t *stack,
441 xbv_mode new_mode, xbv_container old_cont)
443 /* If the new file mode is unknown the current packet could be in
444 * either (any) type of XBV file, and we cant make a decission at
446 if (new_mode != xbv_unknown)
448 if (fwinfo->mode == xbv_unknown)
450 fwinfo->mode = new_mode;
452 else if (fwinfo->mode != new_mode)
457 /* If the current stack top doesn't match what we expect then the
458 * file is corrupt. */
459 if (stack->s[stack->ptr].container != old_cont)
467 /* Make checks as above and then enter a new list */
468 static CsrInt32 xbv_push(xbv1_t *fwinfo, xbv_stack_t *stack,
469 xbv_mode new_mode, xbv_container old_cont,
470 xbv_container new_cont, CsrUint32 new_ioff)
472 if (xbv_check(fwinfo, stack, new_mode, old_cont))
477 /* Check that our stack won't overflow. */
478 if (stack->ptr >= (XBV_STACK_SIZE - 1))
483 /* Add the new list element to the top of the stack. */
485 stack->s[stack->ptr].container = new_cont;
486 stack->s[stack->ptr].ioffset_end = new_ioff;
492 static CsrUint32 xbv2uint(u8 *ptr, CsrInt32 len)
497 for (i = 0; i < len; i++)
507 static CsrInt32 read_tag(card_t *card, ct_t *ct, tag_t *tag)
512 n = (*ct->iread)(card->ospriv, ct->dlpriv, ct->ioffset, buf, 8);
518 /* read the tag and length */
524 /* get section tag */
525 CsrMemCpy(tag->t_name, buf, 4);
527 /* get section length */
528 tag->t_len = xbv2uint(buf + 4, 4);
536 static CsrInt32 read_bytes(card_t *card, ct_t *ct, void *buf, CsrUint32 len)
538 /* read the tag value */
539 if ((*ct->iread)(card->ospriv, ct->dlpriv, ct->ioffset, buf, len) != (CsrInt32)len)
550 static CsrInt32 read_uint(card_t *card, ct_t *ct, CsrUint32 *u, CsrUint32 len)
554 /* Integer cannot be more than 4 bytes */
560 if (read_bytes(card, ct, buf, len))
565 *u = xbv2uint(buf, len);
571 static CsrUint32 write_uint16(void *buf, const CsrUint32 offset, const u16 val)
573 u8 *dst = (u8 *)buf + offset;
574 *dst++ = (u8)(val & 0xff); /* LSB first */
575 *dst = (u8)(val >> 8);
580 static CsrUint32 write_uint32(void *buf, const CsrUint32 offset, const CsrUint32 val)
582 (void)write_uint16(buf, offset + 0, (u16)(val & 0xffff));
583 (void)write_uint16(buf, offset + 2, (u16)(val >> 16));
584 return sizeof(CsrUint32);
588 static CsrUint32 write_bytes(void *buf, const CsrUint32 offset, const u8 *data, const CsrUint32 len)
591 u8 *dst = (u8 *)buf + offset;
593 for (i = 0; i < len; i++)
595 *dst++ = *((u8 *)data + i);
601 static CsrUint32 write_tag(void *buf, const CsrUint32 offset, const CsrCharString *tag_str)
603 u8 *dst = (u8 *)buf + offset;
604 CsrMemCpy(dst, tag_str, 4);
609 static CsrUint32 write_chunk(void *buf, const CsrUint32 offset, const CsrCharString *tag_str, const CsrUint32 payload_len)
611 CsrUint32 written = 0;
612 written += write_tag(buf, offset, tag_str);
613 written += write_uint32(buf, written + offset, (CsrUint32)payload_len);
619 static u16 calc_checksum(void *buf, const CsrUint32 offset, const CsrUint32 bytes_len)
622 u8 *src = (u8 *)buf + offset;
626 for (i = 0; i < bytes_len / 2; i++)
628 /* Contents copied to file is LE, host might not be */
629 val = (u16) * src++; /* LSB */
630 val += (u16)(*src++) << 8; /* MSB */
634 /* Total of uint16s in the stream plus the stored check value
635 * should equal STREAM_CHECKSUM when decoded.
637 return (STREAM_CHECKSUM - sum);
641 #define PTDL_RESET_DATA_SIZE 20 /* Size of reset vectors PTDL */
643 static CsrUint32 calc_patch_size(const xbv1_t *fwinfo)
649 * Work out how big an equivalent patch format file must be for this image.
650 * This only needs to be approximate, so long as it's large enough.
652 if (fwinfo->mode != xbv_firmware)
657 /* Payload (which will get put into a series of PTDLs) */
658 for (i = 0; i < fwinfo->num_fwdl; i++)
660 size += fwinfo->fwdl[i].dl_size;
663 /* Another PTDL at the end containing reset vectors */
664 size += PTDL_RESET_DATA_SIZE;
666 /* PTDL headers. Add one for remainder, one for reset vectors */
667 size += ((fwinfo->num_fwdl / PTDL_MAX_SIZE) + 2) * PTDL_HDR_SIZE;
669 /* Another 1K sufficient to cover miscellaneous headers */
676 static CsrUint32 write_xbv_header(void *buf, const CsrUint32 offset, const CsrUint32 file_payload_length)
678 CsrUint32 written = 0;
680 /* The length value given to the XBV chunk is the length of all subsequent
681 * contents of the file, excluding the 8 byte size of the XBV1 header itself
682 * (The added 6 bytes thus accounts for the size of the VERF)
684 written += write_chunk(buf, offset + written, (CsrCharString *)"XBV1", file_payload_length + 6);
686 written += write_chunk(buf, offset + written, (CsrCharString *)"VERF", 2);
687 written += write_uint16(buf, offset + written, 0); /* File version */
693 static CsrUint32 write_ptch_header(void *buf, const CsrUint32 offset, const CsrUint32 fw_id)
695 CsrUint32 written = 0;
697 /* LIST is written with a zero length, to be updated later */
698 written += write_chunk(buf, offset + written, (CsrCharString *)"LIST", 0);
699 written += write_tag(buf, offset + written, (CsrCharString *)"PTCH"); /* List type */
701 written += write_chunk(buf, offset + written, (CsrCharString *)"FWID", 4);
702 written += write_uint32(buf, offset + written, fw_id);
709 #define UF_REGION_PHY 1
710 #define UF_REGION_MAC 2
711 #define UF_MEMPUT_MAC 0x0000
712 #define UF_MEMPUT_PHY 0x1000
714 static CsrUint32 write_patchcmd(void *buf, const CsrUint32 offset, const CsrUint32 dst_genaddr, const u16 len)
716 CsrUint32 written = 0;
717 CsrUint32 region = (dst_genaddr >> 28);
718 u16 cmd_and_len = UF_MEMPUT_MAC;
720 if (region == UF_REGION_PHY)
722 cmd_and_len = UF_MEMPUT_PHY;
724 else if (region != UF_REGION_MAC)
726 return 0; /* invalid */
729 /* Write the command and data length */
731 written += write_uint16(buf, offset + written, cmd_and_len);
733 /* Write the destination generic address */
734 written += write_uint16(buf, offset + written, (u16)(dst_genaddr >> 16));
735 written += write_uint16(buf, offset + written, (u16)(dst_genaddr & 0xffff));
737 /* The data payload should be appended to the command */
742 static CsrUint32 write_fwdl_to_ptdl(void *buf, const CsrUint32 offset, fwreadfn_t readfn,
743 const struct FWDL *fwdl, const void *dlpriv,
744 const CsrUint32 fw_id, void *fw_buf)
746 CsrUint32 written = 0;
748 CsrUint32 left = fwdl->dl_size; /* Bytes left in this fwdl */
749 CsrUint32 dl_addr = fwdl->dl_addr; /* Target address of fwdl image on XAP */
750 CsrUint32 dl_offs = fwdl->dl_offset; /* Offset of fwdl image data in source */
752 CsrUint32 csum_start_offs; /* first offset to include in checksum */
753 CsrUint32 sec_data_len; /* section data byte count */
754 CsrUint32 sec_len; /* section data + header byte count */
756 /* FWDL maps to one or more PTDLs, as max size for a PTDL is 1K words */
759 /* Calculate amount to be transferred */
760 sec_data_len = CSRMIN(left, PTDL_MAX_SIZE - PTDL_HDR_SIZE);
761 sec_len = sec_data_len + PTDL_HDR_SIZE;
763 /* Write PTDL header + entire PTDL size */
764 written += write_chunk(buf, offset + written, (CsrCharString *)"PTDL", sec_len);
765 /* bug digest implies 4 bytes of padding here, but that seems wrong */
767 /* Checksum starts here */
768 csum_start_offs = offset + written;
770 /* Patch-chunk header: fw_id. Note that this is in XAP word order */
771 written += write_uint16(buf, offset + written, (u16)(fw_id >> 16));
772 written += write_uint16(buf, offset + written, (u16)(fw_id & 0xffff));
774 /* Patch-chunk header: section length in uint16s */
775 written += write_uint16(buf, offset + written, (u16)(sec_len / 2));
778 /* Write the appropriate patch command for the data's destination ptr */
779 written += write_patchcmd(buf, offset + written, dl_addr, (u16)(sec_data_len / 2));
781 /* Write the data itself (limited to the max chunk length) */
782 if (readfn(NULL, (void *)dlpriv, dl_offs, fw_buf, sec_data_len) < 0)
787 written += write_bytes(buf,
792 /* u16 checksum calculated over data written */
793 csum = calc_checksum(buf, csum_start_offs, written - (csum_start_offs - offset));
794 written += write_uint16(buf, offset + written, csum);
796 left -= sec_data_len;
797 dl_addr += sec_data_len;
798 dl_offs += sec_data_len;
806 #define SEC_CMD_LEN ((4 + 2) * 2) /* sizeof(cmd, vector) per XAP */
807 #define PTDL_VEC_HDR_SIZE (4 + 2 + 2) /* sizeof(fw_id, sec_len, csum) */
808 #define UF_MAC_START_VEC 0x00c00000 /* Start address of image on MAC */
809 #define UF_PHY_START_VEC 0x00c00000 /* Start address of image on PHY */
810 #define UF_MAC_START_CMD 0x6000 /* MAC "Set start address" command */
811 #define UF_PHY_START_CMD 0x7000 /* PHY "Set start address" command */
813 static CsrUint32 write_reset_ptdl(void *buf, const CsrUint32 offset, const xbv1_t *fwinfo, CsrUint32 fw_id)
815 CsrUint32 written = 0;
817 CsrUint32 csum_start_offs; /* first offset to include in checksum */
818 CsrUint32 sec_len; /* section data + header byte count */
820 sec_len = SEC_CMD_LEN + PTDL_VEC_HDR_SIZE; /* Total section byte length */
822 /* Write PTDL header + entire PTDL size */
823 written += write_chunk(buf, offset + written, (CsrCharString *)"PTDL", sec_len);
825 /* Checksum starts here */
826 csum_start_offs = offset + written;
828 /* Patch-chunk header: fw_id. Note that this is in XAP word order */
829 written += write_uint16(buf, offset + written, (u16)(fw_id >> 16));
830 written += write_uint16(buf, offset + written, (u16)(fw_id & 0xffff));
832 /* Patch-chunk header: section length in uint16s */
833 written += write_uint16(buf, offset + written, (u16)(sec_len / 2));
836 * Restart addresses to be executed on subsequent loader restart command.
839 /* Setup the MAC start address, note word ordering */
840 written += write_uint16(buf, offset + written, UF_MAC_START_CMD);
841 written += write_uint16(buf, offset + written, (UF_MAC_START_VEC >> 16));
842 written += write_uint16(buf, offset + written, (UF_MAC_START_VEC & 0xffff));
844 /* Setup the PHY start address, note word ordering */
845 written += write_uint16(buf, offset + written, UF_PHY_START_CMD);
846 written += write_uint16(buf, offset + written, (UF_PHY_START_VEC >> 16));
847 written += write_uint16(buf, offset + written, (UF_PHY_START_VEC & 0xffff));
849 /* u16 checksum calculated over data written */
850 csum = calc_checksum(buf, csum_start_offs, written - (csum_start_offs - offset));
851 written += write_uint16(buf, offset + written, csum);
858 * ---------------------------------------------------------------------------
864 * readfn Pointer to function to call to read from the file.
865 * dlpriv Opaque pointer arg to pass to readfn.
866 * addr Offset into firmware image of SLUT.
867 * fwinfo Pointer to fwinfo struct to fill in.
870 * Number of SLUT entries in the f/w, or -1 if the image was corrupt.
871 * ---------------------------------------------------------------------------
873 CsrInt32 xbv1_read_slut(card_t *card, fwreadfn_t readfn, void *dlpriv, xbv1_t *fwinfo,
874 symbol_t *slut, CsrUint32 slut_len)
882 if (fwinfo->mode != xbv_firmware)
887 /* Find the d/l segment containing the SLUT */
888 /* This relies on the SLUT being entirely contained in one segment */
890 for (i = 0; i < fwinfo->num_fwdl; i++)
892 if ((fwinfo->slut_addr >= fwinfo->fwdl[i].dl_addr) &&
893 (fwinfo->slut_addr < (fwinfo->fwdl[i].dl_addr + fwinfo->fwdl[i].dl_size)))
895 offset = fwinfo->fwdl[i].dl_offset +
896 (fwinfo->slut_addr - fwinfo->fwdl[i].dl_addr);
908 if (read_uint(card, &ct, &magic, 2))
912 if (magic != SLUT_FINGERPRINT)
917 while (count < slut_len)
922 if (read_uint(card, &ct, &id, 2))
927 /* Check for end of table marker */
928 if (id == CSR_SLT_END)
933 /* Read Symbol Value */
934 if (read_uint(card, &ct, &obj, 4))
939 slut[count].id = (u16)id;
940 slut[count].obj = obj;
949 * ---------------------------------------------------------------------------
952 * Convert (the relevant parts of) a firmware xbv file into a patch xbv
956 * fw_buf - pointer to xbv firmware image
957 * fwinfo - structure describing the firmware image
958 * size - pointer to location into which size of f/w is written.
961 * Pointer to firmware image, or NULL on error. Caller must free this
962 * buffer via CsrMemFree() once it's finished with.
965 * The input fw_buf should have been checked via xbv1_parse prior to
966 * calling this function, so the input image is assumed valid.
967 * ---------------------------------------------------------------------------
969 #define PTCH_LIST_SIZE 16 /* sizeof PTCH+FWID chunk in LIST header */
971 void* xbv_to_patch(card_t *card, fwreadfn_t readfn,
972 const void *fw_buf, const xbv1_t *fwinfo, CsrUint32 *size)
974 void *patch_buf = NULL;
975 CsrUint32 patch_buf_size;
976 CsrUint32 payload_offs = 0; /* Start of XBV payload */
978 CsrUint32 patch_offs = 0;
979 CsrUint32 list_len_offs = 0; /* Offset of PTDL LIST length parameter */
980 CsrUint32 ptdl_start_offs = 0; /* Offset of first PTDL chunk */
984 if (!fw_buf || !fwinfo || !card)
989 if (fwinfo->mode != xbv_firmware)
991 unifi_error(NULL, "Not a firmware file\n");
995 /* Pre-allocate read buffer for chunk conversion */
996 rdbuf = CsrMemAlloc(PTDL_MAX_SIZE);
999 unifi_error(card, "Couldn't alloc conversion buffer\n");
1003 /* Loader requires patch file's build ID to match the running firmware's */
1004 fw_id = card->build_id;
1006 /* Firmware XBV1 contains VERF, optional INFO, SLUT(s), FWDL(s) */
1007 /* Other chunks should get skipped. */
1008 /* VERF should be sanity-checked against chip version */
1010 /* Patch XBV1 contains VERF, optional INFO, PTCH */
1011 /* PTCH contains FWID, optional INFO, PTDL(s), PTDL(start_vec) */
1012 /* Each FWDL is split into PTDLs (each is 1024 XAP words max) */
1013 /* Each PTDL contains running ROM f/w version, and checksum */
1014 /* MAC/PHY reset addresses (known) are added into a final PTDL */
1016 /* The input image has already been parsed, and loaded into fwinfo, so we
1017 * can use that to build the output image
1019 patch_buf_size = calc_patch_size(fwinfo);
1021 patch_buf = (void *)CsrMemAlloc(patch_buf_size);
1025 unifi_error(NULL, "Can't malloc buffer for patch conversion\n");
1029 CsrMemSet(patch_buf, 0xdd, patch_buf_size);
1031 /* Write XBV + VERF headers */
1032 patch_offs += write_xbv_header(patch_buf, patch_offs, 0);
1033 payload_offs = patch_offs;
1035 /* Write patch (LIST) header */
1036 list_len_offs = patch_offs + 4; /* Save LIST.length offset for later update */
1037 patch_offs += write_ptch_header(patch_buf, patch_offs, fw_id);
1039 /* Save start offset of the PTDL chunks */
1040 ptdl_start_offs = patch_offs;
1042 /* Write LIST of firmware PTDL blocks */
1043 for (i = 0; i < fwinfo->num_fwdl; i++)
1045 patch_offs += write_fwdl_to_ptdl(patch_buf,
1054 /* Write restart-vector PTDL last */
1055 patch_offs += write_reset_ptdl(patch_buf, patch_offs, fwinfo, fw_id);
1057 /* Now the length is known, update the LIST.length */
1058 (void)write_uint32(patch_buf, list_len_offs,
1059 (patch_offs - ptdl_start_offs) + PTCH_LIST_SIZE);
1061 /* Re write XBV headers just to fill in the correct file size */
1062 (void)write_xbv_header(patch_buf, 0, (patch_offs - payload_offs));
1064 unifi_trace(card->ospriv, UDBG1, "XBV:PTCH size %u, fw_id %u\n",