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 * ---------------------------------------------------------------------------
22 #include <linux/slab.h>
24 #ifdef CSR_WIFI_XBV_TEST
25 /* Standalone test harness */
26 #include "unifi_xbv.h"
27 #include "csr_wifi_hip_unifihw.h"
29 /* Normal driver build */
30 #include "csr_wifi_hip_unifiversion.h"
31 #include "csr_wifi_hip_card.h"
35 #include "csr_wifi_hip_xbv.h"
37 #define STREAM_CHECKSUM 0x6d34 /* Sum of uint16s in each patch stream */
39 /* XBV sizes used in patch conversion
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) */
44 /* Struct to represent a buffer for reading firmware file */
53 /* Struct to represent a TLV field */
61 #define TAG_EQ(i, v) (((i)[0] == (v)[0]) && \
62 ((i)[1] == (v)[1]) && \
63 ((i)[2] == (v)[2]) && \
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. */
80 #define XBV_STACK_SIZE 6
81 #define XBV_MAX_OFFS 0x7fffffff
87 xbv_container container
;
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
);
102 static u32
write_uint16(void *buf
, const u32 offset
,
104 static u32
write_uint32(void *buf
, const u32 offset
,
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
,
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
);
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
,
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
,
131 * ---------------------------------------------------------------------------
134 * Scan the firmware file to find the TLVs we are interested in.
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
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.
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.
157 * CSR_RESULT_SUCCESS on success, CSR error code on failure
158 * ---------------------------------------------------------------------------
160 CsrResult
xbv1_parse(card_t
*card
, fwreadfn_t readfn
, void *dlpriv
, xbv1_t
*fwinfo
)
170 memset(fwinfo
, 0, sizeof(xbv1_t
));
171 fwinfo
->mode
= xbv_unknown
;
173 /* File must start with XBV1 triplet */
174 if (read_tag(card
, &ct
, &tag
) <= 0)
176 unifi_error(NULL
, "File is not UniFi firmware\n");
177 return CSR_WIFI_HIP_RESULT_INVALID_VALUE
;
182 if (!TAG_EQ(tag
.t_name
, "XBV1"))
184 unifi_error(NULL
, "File is not UniFi firmware (%s)\n", tag
.t_name
);
185 return CSR_WIFI_HIP_RESULT_INVALID_VALUE
;
189 stack
.s
[stack
.ptr
].container
= xbv_xbv1
;
190 stack
.s
[stack
.ptr
].ioffset_end
= XBV_MAX_OFFS
;
192 /* Now scan the file */
197 n
= read_tag(card
, &ct
, &tag
);
200 unifi_error(NULL
, "No tag\n");
201 return CSR_WIFI_HIP_RESULT_INVALID_VALUE
;
211 /* File format version */
212 if (TAG_EQ(tag
.t_name
, "VERF"))
216 if (xbv_check(fwinfo
, &stack
, xbv_unknown
, xbv_xbv1
) ||
218 read_uint(card
, &ct
, &version
, 2))
220 return CSR_WIFI_HIP_RESULT_INVALID_VALUE
;
224 unifi_error(NULL
, "Unsupported firmware file version: %d.%d\n",
225 version
>> 8, version
& 0xFF);
226 return CSR_WIFI_HIP_RESULT_INVALID_VALUE
;
229 else if (TAG_EQ(tag
.t_name
, "LIST"))
234 list_end
= ct
.ioffset
+ tag
.t_len
;
236 if (read_bytes(card
, &ct
, name
, 4))
238 return CSR_WIFI_HIP_RESULT_INVALID_VALUE
;
242 if (TAG_EQ(name
, "FW "))
244 if (xbv_push(fwinfo
, &stack
, xbv_firmware
, xbv_xbv1
, xbv_fw
, list_end
))
246 return CSR_WIFI_HIP_RESULT_INVALID_VALUE
;
249 else if (TAG_EQ(name
, "VERS"))
251 if (xbv_push(fwinfo
, &stack
, xbv_firmware
, xbv_fw
, xbv_vers
, list_end
) ||
252 (fwinfo
->vers
.num_vand
!= 0))
254 return CSR_WIFI_HIP_RESULT_INVALID_VALUE
;
257 else if (TAG_EQ(name
, "VAND"))
261 if (xbv_push(fwinfo
, &stack
, xbv_firmware
, xbv_vers
, xbv_vand
, list_end
) ||
262 (fwinfo
->vers
.num_vand
>= MAX_VAND
))
264 return CSR_WIFI_HIP_RESULT_INVALID_VALUE
;
268 vand
= fwinfo
->vand
+ fwinfo
->vers
.num_vand
++;
271 vand
->first
= fwinfo
->num_vmeq
;
274 else if (TAG_EQ(name
, "PTCH"))
276 if (xbv_push(fwinfo
, &stack
, xbv_patch
, xbv_xbv1
, xbv_ptch
, list_end
))
278 return CSR_WIFI_HIP_RESULT_INVALID_VALUE
;
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;
289 else if (TAG_EQ(tag
.t_name
, "SLTP"))
293 if (xbv_check(fwinfo
, &stack
, xbv_firmware
, xbv_fw
) ||
295 (fwinfo
->slut_addr
!= 0) ||
296 read_uint(card
, &ct
, &addr
, 4))
298 return CSR_WIFI_HIP_RESULT_INVALID_VALUE
;
301 fwinfo
->slut_addr
= addr
;
303 else if (TAG_EQ(tag
.t_name
, "FWDL"))
308 if (xbv_check(fwinfo
, &stack
, xbv_firmware
, xbv_fw
) ||
309 (fwinfo
->num_fwdl
>= MAX_FWDL
) ||
310 (read_uint(card
, &ct
, &addr
, 4)))
312 return CSR_WIFI_HIP_RESULT_INVALID_VALUE
;
315 fwdl
= fwinfo
->fwdl
+ fwinfo
->num_fwdl
++;
317 fwdl
->dl_size
= tag
.t_len
- 4;
318 fwdl
->dl_addr
= addr
;
319 fwdl
->dl_offset
= ct
.ioffset
;
321 ct
.ioffset
+= tag
.t_len
- 4;
323 else if (TAG_EQ(tag
.t_name
, "FWOV"))
325 if (xbv_check(fwinfo
, &stack
, xbv_firmware
, xbv_fw
) ||
326 (fwinfo
->fwov
.dl_size
!= 0) ||
327 (fwinfo
->fwov
.dl_offset
!= 0))
329 return CSR_WIFI_HIP_RESULT_INVALID_VALUE
;
332 fwinfo
->fwov
.dl_size
= tag
.t_len
;
333 fwinfo
->fwov
.dl_offset
= ct
.ioffset
;
335 ct
.ioffset
+= tag
.t_len
;
337 else if (TAG_EQ(tag
.t_name
, "VMEQ"))
343 if (xbv_check(fwinfo
, &stack
, xbv_firmware
, xbv_vand
) ||
344 (fwinfo
->num_vmeq
>= MAX_VMEQ
) ||
345 (fwinfo
->vers
.num_vand
== 0) ||
347 read_uint(card
, &ct
, &temp
[0], 4) ||
348 read_uint(card
, &ct
, &temp
[1], 2) ||
349 read_uint(card
, &ct
, &temp
[2], 2))
351 return CSR_WIFI_HIP_RESULT_INVALID_VALUE
;
354 /* Get the last VAND */
355 vand
= fwinfo
->vand
+ (fwinfo
->vers
.num_vand
- 1);
358 vmeq
= fwinfo
->vmeq
+ fwinfo
->num_vmeq
++;
360 /* Note that this VAND contains another VMEQ */
363 /* Fill in the VMEQ */
364 vmeq
->addr
= temp
[0];
365 vmeq
->mask
= (u16
)temp
[1];
366 vmeq
->value
= (u16
)temp
[2];
368 else if (TAG_EQ(tag
.t_name
, "FWID"))
372 if (xbv_check(fwinfo
, &stack
, xbv_patch
, xbv_ptch
) ||
374 (fwinfo
->build_id
!= 0) ||
375 read_uint(card
, &ct
, &build_id
, 4))
377 return CSR_WIFI_HIP_RESULT_INVALID_VALUE
;
380 fwinfo
->build_id
= build_id
;
382 else if (TAG_EQ(tag
.t_name
, "PTDL"))
386 if (xbv_check(fwinfo
, &stack
, xbv_patch
, xbv_ptch
) ||
387 (fwinfo
->num_ptdl
>= MAX_PTDL
))
389 return CSR_WIFI_HIP_RESULT_INVALID_VALUE
;
392 /* Allocate a new PTDL */
393 ptdl
= fwinfo
->ptdl
+ fwinfo
->num_ptdl
++;
395 ptdl
->dl_size
= tag
.t_len
;
396 ptdl
->dl_offset
= ct
.ioffset
;
398 ct
.ioffset
+= tag
.t_len
;
403 * If we get here it is a tag we are not interested in,
406 ct
.ioffset
+= tag
.t_len
;
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
)
413 if (ct
.ioffset
> stack
.s
[stack
.ptr
].ioffset_end
)
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
;
422 unifi_error(NULL
, "XBV file has underrun stack pointer\n");
423 return CSR_WIFI_HIP_RESULT_INVALID_VALUE
;
431 unifi_error(NULL
, "Last list of XBV is not complete.\n");
432 return CSR_WIFI_HIP_RESULT_INVALID_VALUE
;
435 return CSR_RESULT_SUCCESS
;
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
)
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
447 if (new_mode
!= xbv_unknown
)
449 if (fwinfo
->mode
== xbv_unknown
)
451 fwinfo
->mode
= new_mode
;
453 else if (fwinfo
->mode
!= new_mode
)
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
)
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
)
473 if (xbv_check(fwinfo
, stack
, new_mode
, old_cont
))
478 /* Check that our stack won't overflow. */
479 if (stack
->ptr
>= (XBV_STACK_SIZE
- 1))
484 /* Add the new list element to the top of the stack. */
486 stack
->s
[stack
->ptr
].container
= new_cont
;
487 stack
->s
[stack
->ptr
].ioffset_end
= new_ioff
;
493 static u32
xbv2uint(u8
*ptr
, s32 len
)
498 for (i
= 0; i
< len
; i
++)
508 static s32
read_tag(card_t
*card
, ct_t
*ct
, tag_t
*tag
)
513 n
= (*ct
->iread
)(card
->ospriv
, ct
->dlpriv
, ct
->ioffset
, buf
, 8);
519 /* read the tag and length */
525 /* get section tag */
526 memcpy(tag
->t_name
, buf
, 4);
528 /* get section length */
529 tag
->t_len
= xbv2uint(buf
+ 4, 4);
537 static s32
read_bytes(card_t
*card
, ct_t
*ct
, void *buf
, u32 len
)
539 /* read the tag value */
540 if ((*ct
->iread
)(card
->ospriv
, ct
->dlpriv
, ct
->ioffset
, buf
, len
) != (s32
)len
)
551 static s32
read_uint(card_t
*card
, ct_t
*ct
, u32
*u
, u32 len
)
555 /* Integer cannot be more than 4 bytes */
561 if (read_bytes(card
, ct
, buf
, len
))
566 *u
= xbv2uint(buf
, len
);
572 static u32
write_uint16(void *buf
, const u32 offset
, const u16 val
)
574 u8
*dst
= (u8
*)buf
+ offset
;
575 *dst
++ = (u8
)(val
& 0xff); /* LSB first */
576 *dst
= (u8
)(val
>> 8);
581 static u32
write_uint32(void *buf
, const u32 offset
, const u32 val
)
583 (void)write_uint16(buf
, offset
+ 0, (u16
)(val
& 0xffff));
584 (void)write_uint16(buf
, offset
+ 2, (u16
)(val
>> 16));
589 static u32
write_bytes(void *buf
, const u32 offset
, const u8
*data
, const u32 len
)
592 u8
*dst
= (u8
*)buf
+ offset
;
594 for (i
= 0; i
< len
; i
++)
596 *dst
++ = *((u8
*)data
+ i
);
602 static u32
write_tag(void *buf
, const u32 offset
, const char *tag_str
)
604 u8
*dst
= (u8
*)buf
+ offset
;
605 memcpy(dst
, tag_str
, 4);
610 static u32
write_chunk(void *buf
, const u32 offset
, const char *tag_str
, const u32 payload_len
)
613 written
+= write_tag(buf
, offset
, tag_str
);
614 written
+= write_uint32(buf
, written
+ offset
, (u32
)payload_len
);
620 static u16
calc_checksum(void *buf
, const u32 offset
, const u32 bytes_len
)
623 u8
*src
= (u8
*)buf
+ offset
;
627 for (i
= 0; i
< bytes_len
/ 2; i
++)
629 /* Contents copied to file is LE, host might not be */
630 val
= (u16
) * src
++; /* LSB */
631 val
+= (u16
)(*src
++) << 8; /* MSB */
635 /* Total of uint16s in the stream plus the stored check value
636 * should equal STREAM_CHECKSUM when decoded.
638 return (STREAM_CHECKSUM
- sum
);
642 #define PTDL_RESET_DATA_SIZE 20 /* Size of reset vectors PTDL */
644 static u32
calc_patch_size(const xbv1_t
*fwinfo
)
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.
653 if (fwinfo
->mode
!= xbv_firmware
)
658 /* Payload (which will get put into a series of PTDLs) */
659 for (i
= 0; i
< fwinfo
->num_fwdl
; i
++)
661 size
+= fwinfo
->fwdl
[i
].dl_size
;
664 /* Another PTDL at the end containing reset vectors */
665 size
+= PTDL_RESET_DATA_SIZE
;
667 /* PTDL headers. Add one for remainder, one for reset vectors */
668 size
+= ((fwinfo
->num_fwdl
/ PTDL_MAX_SIZE
) + 2) * PTDL_HDR_SIZE
;
670 /* Another 1K sufficient to cover miscellaneous headers */
677 static u32
write_xbv_header(void *buf
, const u32 offset
, const u32 file_payload_length
)
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)
685 written
+= write_chunk(buf
, offset
+ written
, (char *)"XBV1", file_payload_length
+ 6);
687 written
+= write_chunk(buf
, offset
+ written
, (char *)"VERF", 2);
688 written
+= write_uint16(buf
, offset
+ written
, 0); /* File version */
694 static u32
write_ptch_header(void *buf
, const u32 offset
, const u32 fw_id
)
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 */
702 written
+= write_chunk(buf
, offset
+ written
, (char *)"FWID", 4);
703 written
+= write_uint32(buf
, offset
+ written
, fw_id
);
710 #define UF_REGION_PHY 1
711 #define UF_REGION_MAC 2
712 #define UF_MEMPUT_MAC 0x0000
713 #define UF_MEMPUT_PHY 0x1000
715 static u32
write_patchcmd(void *buf
, const u32 offset
, const u32 dst_genaddr
, const u16 len
)
718 u32 region
= (dst_genaddr
>> 28);
719 u16 cmd_and_len
= UF_MEMPUT_MAC
;
721 if (region
== UF_REGION_PHY
)
723 cmd_and_len
= UF_MEMPUT_PHY
;
725 else if (region
!= UF_REGION_MAC
)
727 return 0; /* invalid */
730 /* Write the command and data length */
732 written
+= write_uint16(buf
, offset
+ written
, cmd_and_len
);
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));
738 /* The data payload should be appended to the command */
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
)
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 */
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 */
757 /* FWDL maps to one or more PTDLs, as max size for a PTDL is 1K words */
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
;
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 */
768 /* Checksum starts here */
769 csum_start_offs
= offset
+ written
;
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));
775 /* Patch-chunk header: section length in uint16s */
776 written
+= write_uint16(buf
, offset
+ written
, (u16
)(sec_len
/ 2));
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));
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)
788 written
+= write_bytes(buf
,
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
);
797 left
-= sec_data_len
;
798 dl_addr
+= sec_data_len
;
799 dl_offs
+= sec_data_len
;
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 */
814 static u32
write_reset_ptdl(void *buf
, const u32 offset
, const xbv1_t
*fwinfo
, u32 fw_id
)
818 u32 csum_start_offs
; /* first offset to include in checksum */
819 u32 sec_len
; /* section data + header byte count */
821 sec_len
= SEC_CMD_LEN
+ PTDL_VEC_HDR_SIZE
; /* Total section byte length */
823 /* Write PTDL header + entire PTDL size */
824 written
+= write_chunk(buf
, offset
+ written
, (char *)"PTDL", sec_len
);
826 /* Checksum starts here */
827 csum_start_offs
= offset
+ written
;
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));
833 /* Patch-chunk header: section length in uint16s */
834 written
+= write_uint16(buf
, offset
+ written
, (u16
)(sec_len
/ 2));
837 * Restart addresses to be executed on subsequent loader restart command.
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));
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));
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
);
859 * ---------------------------------------------------------------------------
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.
871 * Number of SLUT entries in the f/w, or -1 if the image was corrupt.
872 * ---------------------------------------------------------------------------
874 s32
xbv1_read_slut(card_t
*card
, fwreadfn_t readfn
, void *dlpriv
, xbv1_t
*fwinfo
,
875 symbol_t
*slut
, u32 slut_len
)
883 if (fwinfo
->mode
!= xbv_firmware
)
888 /* Find the d/l segment containing the SLUT */
889 /* This relies on the SLUT being entirely contained in one segment */
891 for (i
= 0; i
< fwinfo
->num_fwdl
; i
++)
893 if ((fwinfo
->slut_addr
>= fwinfo
->fwdl
[i
].dl_addr
) &&
894 (fwinfo
->slut_addr
< (fwinfo
->fwdl
[i
].dl_addr
+ fwinfo
->fwdl
[i
].dl_size
)))
896 offset
= fwinfo
->fwdl
[i
].dl_offset
+
897 (fwinfo
->slut_addr
- fwinfo
->fwdl
[i
].dl_addr
);
909 if (read_uint(card
, &ct
, &magic
, 2))
913 if (magic
!= SLUT_FINGERPRINT
)
918 while (count
< slut_len
)
923 if (read_uint(card
, &ct
, &id
, 2))
928 /* Check for end of table marker */
929 if (id
== CSR_SLT_END
)
934 /* Read Symbol Value */
935 if (read_uint(card
, &ct
, &obj
, 4))
940 slut
[count
].id
= (u16
)id
;
941 slut
[count
].obj
= obj
;
950 * ---------------------------------------------------------------------------
953 * Convert (the relevant parts of) a firmware xbv file into a patch xbv
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.
962 * Pointer to firmware image, or NULL on error. Caller must free this
963 * buffer via kfree() once it's finished with.
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 * ---------------------------------------------------------------------------
970 #define PTCH_LIST_SIZE 16 /* sizeof PTCH+FWID chunk in LIST header */
972 void* xbv_to_patch(card_t
*card
, fwreadfn_t readfn
,
973 const void *fw_buf
, const xbv1_t
*fwinfo
, u32
*size
)
975 void *patch_buf
= NULL
;
977 u32 payload_offs
= 0; /* Start of XBV payload */
980 u32 list_len_offs
= 0; /* Offset of PTDL LIST length parameter */
981 u32 ptdl_start_offs
= 0; /* Offset of first PTDL chunk */
985 if (!fw_buf
|| !fwinfo
|| !card
)
990 if (fwinfo
->mode
!= xbv_firmware
)
992 unifi_error(NULL
, "Not a firmware file\n");
996 /* Pre-allocate read buffer for chunk conversion */
997 rdbuf
= kmalloc(PTDL_MAX_SIZE
, GFP_KERNEL
);
1000 unifi_error(card
, "Couldn't alloc conversion buffer\n");
1004 /* Loader requires patch file's build ID to match the running firmware's */
1005 fw_id
= card
->build_id
;
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 */
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 */
1017 /* The input image has already been parsed, and loaded into fwinfo, so we
1018 * can use that to build the output image
1020 patch_buf_size
= calc_patch_size(fwinfo
);
1022 patch_buf
= kmalloc(patch_buf_size
, GFP_KERNEL
);
1026 unifi_error(NULL
, "Can't malloc buffer for patch conversion\n");
1030 memset(patch_buf
, 0xdd, patch_buf_size
);
1032 /* Write XBV + VERF headers */
1033 patch_offs
+= write_xbv_header(patch_buf
, patch_offs
, 0);
1034 payload_offs
= patch_offs
;
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
);
1040 /* Save start offset of the PTDL chunks */
1041 ptdl_start_offs
= patch_offs
;
1043 /* Write LIST of firmware PTDL blocks */
1044 for (i
= 0; i
< fwinfo
->num_fwdl
; i
++)
1046 patch_offs
+= write_fwdl_to_ptdl(patch_buf
,
1055 /* Write restart-vector PTDL last */
1056 patch_offs
+= write_reset_ptdl(patch_buf
, patch_offs
, fwinfo
, fw_id
);
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
);
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
));
1065 unifi_trace(card
->ospriv
, UDBG1
, "XBV:PTCH size %u, fw_id %u\n",