aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/csr/csr_wifi_hip_xbv.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/csr/csr_wifi_hip_xbv.c')
-rw-r--r--drivers/staging/csr/csr_wifi_hip_xbv.c1076
1 files changed, 0 insertions, 1076 deletions
diff --git a/drivers/staging/csr/csr_wifi_hip_xbv.c b/drivers/staging/csr/csr_wifi_hip_xbv.c
deleted file mode 100644
index 050a15fbadf9..000000000000
--- a/drivers/staging/csr/csr_wifi_hip_xbv.c
+++ /dev/null
@@ -1,1076 +0,0 @@
-/*****************************************************************************
-
- (c) Cambridge Silicon Radio Limited 2012
- All rights reserved and confidential information of CSR
-
- Refer to LICENSE.txt included with this source for details
- on the license terms.
-
-*****************************************************************************/
-
-/*
- * ---------------------------------------------------------------------------
- * FILE: csr_wifi_hip_xbv.c
- *
- * PURPOSE:
- * Routines for downloading firmware to UniFi.
- *
- * UniFi firmware files use a nested TLV (Tag-Length-Value) format.
- *
- * ---------------------------------------------------------------------------
- */
-#include <linux/slab.h>
-
-#ifdef CSR_WIFI_XBV_TEST
-/* Standalone test harness */
-#include "unifi_xbv.h"
-#include "csr_wifi_hip_unifihw.h"
-#else
-/* Normal driver build */
-#include "csr_wifi_hip_unifiversion.h"
-#include "csr_wifi_hip_card.h"
-#define DBG_TAG(t)
-#endif
-
-#include "csr_wifi_hip_xbv.h"
-
-#define STREAM_CHECKSUM 0x6d34 /* Sum of uint16s in each patch stream */
-
-/* XBV sizes used in patch conversion
- */
-#define PTDL_MAX_SIZE 2048 /* Max bytes allowed per PTDL */
-#define PTDL_HDR_SIZE (4 + 2 + 6 + 2) /* sizeof(fw_id, sec_len, patch_cmd, csum) */
-
-/* Struct to represent a buffer for reading firmware file */
-
-typedef struct
-{
- void *dlpriv;
- s32 ioffset;
- fwreadfn_t iread;
-} ct_t;
-
-/* Struct to represent a TLV field */
-typedef struct
-{
- char t_name[4];
- u32 t_len;
-} tag_t;
-
-
-#define TAG_EQ(i, v) (((i)[0] == (v)[0]) && \
- ((i)[1] == (v)[1]) && \
- ((i)[2] == (v)[2]) && \
- ((i)[3] == (v)[3]))
-
-/* We create a small stack on the stack that contains an enum
- * indicating the containing list segments, and the offset at which
- * those lists end. This enables a lot more error checking. */
-typedef enum
-{
- xbv_xbv1,
- /*xbv_info,*/
- xbv_fw,
- xbv_vers,
- xbv_vand,
- xbv_ptch,
- xbv_other
-} xbv_container;
-
-#define XBV_STACK_SIZE 6
-#define XBV_MAX_OFFS 0x7fffffff
-
-typedef struct
-{
- struct
- {
- xbv_container container;
- s32 ioffset_end;
- } s[XBV_STACK_SIZE];
- u32 ptr;
-} xbv_stack_t;
-
-static s32 read_tag(card_t *card, ct_t *ct, tag_t *tag);
-static s32 read_bytes(card_t *card, ct_t *ct, void *buf, u32 len);
-static s32 read_uint(card_t *card, ct_t *ct, u32 *u, u32 len);
-static s32 xbv_check(xbv1_t *fwinfo, const xbv_stack_t *stack,
- xbv_mode new_mode, xbv_container old_cont);
-static s32 xbv_push(xbv1_t *fwinfo, xbv_stack_t *stack,
- xbv_mode new_mode, xbv_container old_cont,
- xbv_container new_cont, u32 ioff);
-
-static u32 write_uint16(void *buf, const u32 offset,
- const u16 val);
-static u32 write_uint32(void *buf, const u32 offset,
- const u32 val);
-static u32 write_bytes(void *buf, const u32 offset,
- const u8 *data, const u32 len);
-static u32 write_tag(void *buf, const u32 offset,
- const char *tag_str);
-static u32 write_chunk(void *buf, const u32 offset,
- const char *tag_str,
- const u32 payload_len);
-static u16 calc_checksum(void *buf, const u32 offset,
- const u32 bytes_len);
-static u32 calc_patch_size(const xbv1_t *fwinfo);
-
-static u32 write_xbv_header(void *buf, const u32 offset,
- const u32 file_payload_length);
-static u32 write_ptch_header(void *buf, const u32 offset,
- const u32 fw_id);
-static u32 write_patchcmd(void *buf, const u32 offset,
- const u32 dst_genaddr, const u16 len);
-static u32 write_reset_ptdl(void *buf, const u32 offset,
- const xbv1_t *fwinfo, u32 fw_id);
-static u32 write_fwdl_to_ptdl(void *buf, const u32 offset,
- fwreadfn_t readfn, const struct FWDL *fwdl,
- const void *fw_buf, const u32 fw_id,
- void *rdbuf);
-
-/*
- * ---------------------------------------------------------------------------
- * parse_xbv1
- *
- * Scan the firmware file to find the TLVs we are interested in.
- * Actions performed:
- * - check we support the file format version in VERF
- * Store these TLVs if we have a firmware image:
- * - SLTP Symbol Lookup Table Pointer
- * - FWDL firmware download segments
- * - FWOL firmware overlay segment
- * - VMEQ Register probe tests to verify matching h/w
- * Store these TLVs if we have a patch file:
- * - FWID the firmware build ID that this file patches
- * - PTDL The actual patches
- *
- * The structure pointed to by fwinfo is cleared and
- * 'fwinfo->mode' is set to 'unknown'. The 'fwinfo->mode'
- * variable is set to 'firmware' or 'patch' once we know which
- * sort of XBV file we have.
- *
- * Arguments:
- * readfn Pointer to function to call to read from the file.
- * dlpriv Opaque pointer arg to pass to readfn.
- * fwinfo Pointer to fwinfo struct to fill in.
- *
- * Returns:
- * CSR_RESULT_SUCCESS on success, CSR error code on failure
- * ---------------------------------------------------------------------------
- */
-CsrResult xbv1_parse(card_t *card, fwreadfn_t readfn, void *dlpriv, xbv1_t *fwinfo)
-{
- ct_t ct;
- tag_t tag;
- xbv_stack_t stack;
-
- ct.dlpriv = dlpriv;
- ct.ioffset = 0;
- ct.iread = readfn;
-
- memset(fwinfo, 0, sizeof(xbv1_t));
- fwinfo->mode = xbv_unknown;
-
- /* File must start with XBV1 triplet */
- if (read_tag(card, &ct, &tag) <= 0)
- {
- unifi_error(NULL, "File is not UniFi firmware\n");
- return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
- }
-
- DBG_TAG(tag.t_name);
-
- if (!TAG_EQ(tag.t_name, "XBV1"))
- {
- unifi_error(NULL, "File is not UniFi firmware (%s)\n", tag.t_name);
- return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
- }
-
- stack.ptr = 0;
- stack.s[stack.ptr].container = xbv_xbv1;
- stack.s[stack.ptr].ioffset_end = XBV_MAX_OFFS;
-
- /* Now scan the file */
- while (1)
- {
- s32 n;
-
- n = read_tag(card, &ct, &tag);
- if (n < 0)
- {
- unifi_error(NULL, "No tag\n");
- return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
- }
- if (n == 0)
- {
- /* End of file */
- break;
- }
-
- DBG_TAG(tag.t_name);
-
- /* File format version */
- if (TAG_EQ(tag.t_name, "VERF"))
- {
- u32 version;
-
- if (xbv_check(fwinfo, &stack, xbv_unknown, xbv_xbv1) ||
- (tag.t_len != 2) ||
- read_uint(card, &ct, &version, 2))
- {
- return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
- }
- if (version != 0)
- {
- unifi_error(NULL, "Unsupported firmware file version: %d.%d\n",
- version >> 8, version & 0xFF);
- return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
- }
- }
- else if (TAG_EQ(tag.t_name, "LIST"))
- {
- char name[4];
- u32 list_end;
-
- list_end = ct.ioffset + tag.t_len;
-
- if (read_bytes(card, &ct, name, 4))
- {
- return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
- }
-
- DBG_TAG(name);
- if (TAG_EQ(name, "FW "))
- {
- if (xbv_push(fwinfo, &stack, xbv_firmware, xbv_xbv1, xbv_fw, list_end))
- {
- return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
- }
- }
- else if (TAG_EQ(name, "VERS"))
- {
- if (xbv_push(fwinfo, &stack, xbv_firmware, xbv_fw, xbv_vers, list_end) ||
- (fwinfo->vers.num_vand != 0))
- {
- return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
- }
- }
- else if (TAG_EQ(name, "VAND"))
- {
- struct VAND *vand;
-
- if (xbv_push(fwinfo, &stack, xbv_firmware, xbv_vers, xbv_vand, list_end) ||
- (fwinfo->vers.num_vand >= MAX_VAND))
- {
- return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
- }
-
- /* Get a new VAND */
- vand = fwinfo->vand + fwinfo->vers.num_vand++;
-
- /* Fill it in */
- vand->first = fwinfo->num_vmeq;
- vand->count = 0;
- }
- else if (TAG_EQ(name, "PTCH"))
- {
- if (xbv_push(fwinfo, &stack, xbv_patch, xbv_xbv1, xbv_ptch, list_end))
- {
- return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
- }
- }
- else
- {
- /* Skip over any other lists. We dont bother to push
- * the new list type now as we would only pop it at
- * the end of the outer loop. */
- ct.ioffset += tag.t_len - 4;
- }
- }
- else if (TAG_EQ(tag.t_name, "SLTP"))
- {
- u32 addr;
-
- if (xbv_check(fwinfo, &stack, xbv_firmware, xbv_fw) ||
- (tag.t_len != 4) ||
- (fwinfo->slut_addr != 0) ||
- read_uint(card, &ct, &addr, 4))
- {
- return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
- }
-
- fwinfo->slut_addr = addr;
- }
- else if (TAG_EQ(tag.t_name, "FWDL"))
- {
- u32 addr;
- struct FWDL *fwdl;
-
- if (xbv_check(fwinfo, &stack, xbv_firmware, xbv_fw) ||
- (fwinfo->num_fwdl >= MAX_FWDL) ||
- (read_uint(card, &ct, &addr, 4)))
- {
- return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
- }
-
- fwdl = fwinfo->fwdl + fwinfo->num_fwdl++;
-
- fwdl->dl_size = tag.t_len - 4;
- fwdl->dl_addr = addr;
- fwdl->dl_offset = ct.ioffset;
-
- ct.ioffset += tag.t_len - 4;
- }
- else if (TAG_EQ(tag.t_name, "FWOV"))
- {
- if (xbv_check(fwinfo, &stack, xbv_firmware, xbv_fw) ||
- (fwinfo->fwov.dl_size != 0) ||
- (fwinfo->fwov.dl_offset != 0))
- {
- return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
- }
-
- fwinfo->fwov.dl_size = tag.t_len;
- fwinfo->fwov.dl_offset = ct.ioffset;
-
- ct.ioffset += tag.t_len;
- }
- else if (TAG_EQ(tag.t_name, "VMEQ"))
- {
- u32 temp[3];
- struct VAND *vand;
- struct VMEQ *vmeq;
-
- if (xbv_check(fwinfo, &stack, xbv_firmware, xbv_vand) ||
- (fwinfo->num_vmeq >= MAX_VMEQ) ||
- (fwinfo->vers.num_vand == 0) ||
- (tag.t_len != 8) ||
- read_uint(card, &ct, &temp[0], 4) ||
- read_uint(card, &ct, &temp[1], 2) ||
- read_uint(card, &ct, &temp[2], 2))
- {
- return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
- }
-
- /* Get the last VAND */
- vand = fwinfo->vand + (fwinfo->vers.num_vand - 1);
-
- /* Get a new VMEQ */
- vmeq = fwinfo->vmeq + fwinfo->num_vmeq++;
-
- /* Note that this VAND contains another VMEQ */
- vand->count++;
-
- /* Fill in the VMEQ */
- vmeq->addr = temp[0];
- vmeq->mask = (u16)temp[1];
- vmeq->value = (u16)temp[2];
- }
- else if (TAG_EQ(tag.t_name, "FWID"))
- {
- u32 build_id;
-
- if (xbv_check(fwinfo, &stack, xbv_patch, xbv_ptch) ||
- (tag.t_len != 4) ||
- (fwinfo->build_id != 0) ||
- read_uint(card, &ct, &build_id, 4))
- {
- return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
- }
-
- fwinfo->build_id = build_id;
- }
- else if (TAG_EQ(tag.t_name, "PTDL"))
- {
- struct PTDL *ptdl;
-
- if (xbv_check(fwinfo, &stack, xbv_patch, xbv_ptch) ||
- (fwinfo->num_ptdl >= MAX_PTDL))
- {
- return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
- }
-
- /* Allocate a new PTDL */
- ptdl = fwinfo->ptdl + fwinfo->num_ptdl++;
-
- ptdl->dl_size = tag.t_len;
- ptdl->dl_offset = ct.ioffset;
-
- ct.ioffset += tag.t_len;
- }
- else
- {
- /*
- * If we get here it is a tag we are not interested in,
- * just skip over it.
- */
- ct.ioffset += tag.t_len;
- }
-
- /* Check to see if we are at the end of the currently stacked
- * segment. We could finish more than one list at a time. */
- while (ct.ioffset >= stack.s[stack.ptr].ioffset_end)
- {
- if (ct.ioffset > stack.s[stack.ptr].ioffset_end)
- {
- unifi_error(NULL,
- "XBV file has overrun stack'd segment %d (%d > %d)\n",
- stack.ptr, ct.ioffset, stack.s[stack.ptr].ioffset_end);
- return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
- }
- if (stack.ptr <= 0)
- {
- unifi_error(NULL, "XBV file has underrun stack pointer\n");
- return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
- }
- stack.ptr--;
- }
- }
-
- if (stack.ptr != 0)
- {
- unifi_error(NULL, "Last list of XBV is not complete.\n");
- return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
- }
-
- return CSR_RESULT_SUCCESS;
-} /* xbv1_parse() */
-
-
-/* Check the the XBV file is of a consistant sort (either firmware or
- * patch) and that we are in the correct containing list type. */
-static s32 xbv_check(xbv1_t *fwinfo, const xbv_stack_t *stack,
- xbv_mode new_mode, xbv_container old_cont)
-{
- /* If the new file mode is unknown the current packet could be in
- * either (any) type of XBV file, and we cant make a decission at
- * this time. */
- if (new_mode != xbv_unknown)
- {
- if (fwinfo->mode == xbv_unknown)
- {
- fwinfo->mode = new_mode;
- }
- else if (fwinfo->mode != new_mode)
- {
- return -1;
- }
- }
- /* If the current stack top doesn't match what we expect then the
- * file is corrupt. */
- if (stack->s[stack->ptr].container != old_cont)
- {
- return -1;
- }
- return 0;
-}
-
-
-/* Make checks as above and then enter a new list */
-static s32 xbv_push(xbv1_t *fwinfo, xbv_stack_t *stack,
- xbv_mode new_mode, xbv_container old_cont,
- xbv_container new_cont, u32 new_ioff)
-{
- if (xbv_check(fwinfo, stack, new_mode, old_cont))
- {
- return -1;
- }
-
- /* Check that our stack won't overflow. */
- if (stack->ptr >= (XBV_STACK_SIZE - 1))
- {
- return -1;
- }
-
- /* Add the new list element to the top of the stack. */
- stack->ptr++;
- stack->s[stack->ptr].container = new_cont;
- stack->s[stack->ptr].ioffset_end = new_ioff;
-
- return 0;
-}
-
-
-static u32 xbv2uint(u8 *ptr, s32 len)
-{
- u32 u = 0;
- s16 i;
-
- for (i = 0; i < len; i++)
- {
- u32 b;
- b = ptr[i];
- u += b << (i * 8);
- }
- return u;
-}
-
-
-static s32 read_tag(card_t *card, ct_t *ct, tag_t *tag)
-{
- u8 buf[8];
- s32 n;
-
- n = (*ct->iread)(card->ospriv, ct->dlpriv, ct->ioffset, buf, 8);
- if (n <= 0)
- {
- return n;
- }
-
- /* read the tag and length */
- if (n != 8)
- {
- return -1;
- }
-
- /* get section tag */
- memcpy(tag->t_name, buf, 4);
-
- /* get section length */
- tag->t_len = xbv2uint(buf + 4, 4);
-
- ct->ioffset += 8;
-
- return 8;
-} /* read_tag() */
-
-
-static s32 read_bytes(card_t *card, ct_t *ct, void *buf, u32 len)
-{
- /* read the tag value */
- if ((*ct->iread)(card->ospriv, ct->dlpriv, ct->ioffset, buf, len) != (s32)len)
- {
- return -1;
- }
-
- ct->ioffset += len;
-
- return 0;
-} /* read_bytes() */
-
-
-static s32 read_uint(card_t *card, ct_t *ct, u32 *u, u32 len)
-{
- u8 buf[4];
-
- /* Integer cannot be more than 4 bytes */
- if (len > 4)
- {
- return -1;
- }
-
- if (read_bytes(card, ct, buf, len))
- {
- return -1;
- }
-
- *u = xbv2uint(buf, len);
-
- return 0;
-} /* read_uint() */
-
-
-static u32 write_uint16(void *buf, const u32 offset, const u16 val)
-{
- u8 *dst = (u8 *)buf + offset;
- *dst++ = (u8)(val & 0xff); /* LSB first */
- *dst = (u8)(val >> 8);
- return sizeof(u16);
-}
-
-
-static u32 write_uint32(void *buf, const u32 offset, const u32 val)
-{
- (void)write_uint16(buf, offset + 0, (u16)(val & 0xffff));
- (void)write_uint16(buf, offset + 2, (u16)(val >> 16));
- return sizeof(u32);
-}
-
-
-static u32 write_bytes(void *buf, const u32 offset, const u8 *data, const u32 len)
-{
- u32 i;
- u8 *dst = (u8 *)buf + offset;
-
- for (i = 0; i < len; i++)
- {
- *dst++ = *((u8 *)data + i);
- }
- return len;
-}
-
-
-static u32 write_tag(void *buf, const u32 offset, const char *tag_str)
-{
- u8 *dst = (u8 *)buf + offset;
- memcpy(dst, tag_str, 4);
- return 4;
-}
-
-
-static u32 write_chunk(void *buf, const u32 offset, const char *tag_str, const u32 payload_len)
-{
- u32 written = 0;
- written += write_tag(buf, offset, tag_str);
- written += write_uint32(buf, written + offset, (u32)payload_len);
-
- return written;
-}
-
-
-static u16 calc_checksum(void *buf, const u32 offset, const u32 bytes_len)
-{
- u32 i;
- u8 *src = (u8 *)buf + offset;
- u16 sum = 0;
- u16 val;
-
- for (i = 0; i < bytes_len / 2; i++)
- {
- /* Contents copied to file is LE, host might not be */
- val = (u16) * src++; /* LSB */
- val += (u16)(*src++) << 8; /* MSB */
- sum += val;
- }
-
- /* Total of uint16s in the stream plus the stored check value
- * should equal STREAM_CHECKSUM when decoded.
- */
- return (STREAM_CHECKSUM - sum);
-}
-
-
-#define PTDL_RESET_DATA_SIZE 20 /* Size of reset vectors PTDL */
-
-static u32 calc_patch_size(const xbv1_t *fwinfo)
-{
- s16 i;
- u32 size = 0;
-
- /*
- * Work out how big an equivalent patch format file must be for this image.
- * This only needs to be approximate, so long as it's large enough.
- */
- if (fwinfo->mode != xbv_firmware)
- {
- return 0;
- }
-
- /* Payload (which will get put into a series of PTDLs) */
- for (i = 0; i < fwinfo->num_fwdl; i++)
- {
- size += fwinfo->fwdl[i].dl_size;
- }
-
- /* Another PTDL at the end containing reset vectors */
- size += PTDL_RESET_DATA_SIZE;
-
- /* PTDL headers. Add one for remainder, one for reset vectors */
- size += ((fwinfo->num_fwdl / PTDL_MAX_SIZE) + 2) * PTDL_HDR_SIZE;
-
- /* Another 1K sufficient to cover miscellaneous headers */
- size += 1024;
-
- return size;
-}
-
-
-static u32 write_xbv_header(void *buf, const u32 offset, const u32 file_payload_length)
-{
- u32 written = 0;
-
- /* The length value given to the XBV chunk is the length of all subsequent
- * contents of the file, excluding the 8 byte size of the XBV1 header itself
- * (The added 6 bytes thus accounts for the size of the VERF)
- */
- written += write_chunk(buf, offset + written, (char *)"XBV1", file_payload_length + 6);
-
- written += write_chunk(buf, offset + written, (char *)"VERF", 2);
- written += write_uint16(buf, offset + written, 0); /* File version */
-
- return written;
-}
-
-
-static u32 write_ptch_header(void *buf, const u32 offset, const u32 fw_id)
-{
- u32 written = 0;
-
- /* LIST is written with a zero length, to be updated later */
- written += write_chunk(buf, offset + written, (char *)"LIST", 0);
- written += write_tag(buf, offset + written, (char *)"PTCH"); /* List type */
-
- written += write_chunk(buf, offset + written, (char *)"FWID", 4);
- written += write_uint32(buf, offset + written, fw_id);
-
-
- return written;
-}
-
-
-#define UF_REGION_PHY 1
-#define UF_REGION_MAC 2
-#define UF_MEMPUT_MAC 0x0000
-#define UF_MEMPUT_PHY 0x1000
-
-static u32 write_patchcmd(void *buf, const u32 offset, const u32 dst_genaddr, const u16 len)
-{
- u32 written = 0;
- u32 region = (dst_genaddr >> 28);
- u16 cmd_and_len = UF_MEMPUT_MAC;
-
- if (region == UF_REGION_PHY)
- {
- cmd_and_len = UF_MEMPUT_PHY;
- }
- else if (region != UF_REGION_MAC)
- {
- return 0; /* invalid */
- }
-
- /* Write the command and data length */
- cmd_and_len |= len;
- written += write_uint16(buf, offset + written, cmd_and_len);
-
- /* Write the destination generic address */
- written += write_uint16(buf, offset + written, (u16)(dst_genaddr >> 16));
- written += write_uint16(buf, offset + written, (u16)(dst_genaddr & 0xffff));
-
- /* The data payload should be appended to the command */
- return written;
-}
-
-
-static u32 write_fwdl_to_ptdl(void *buf, const u32 offset, fwreadfn_t readfn,
- const struct FWDL *fwdl, const void *dlpriv,
- const u32 fw_id, void *fw_buf)
-{
- u32 written = 0;
- s16 chunks = 0;
- u32 left = fwdl->dl_size; /* Bytes left in this fwdl */
- u32 dl_addr = fwdl->dl_addr; /* Target address of fwdl image on XAP */
- u32 dl_offs = fwdl->dl_offset; /* Offset of fwdl image data in source */
- u16 csum;
- u32 csum_start_offs; /* first offset to include in checksum */
- u32 sec_data_len; /* section data byte count */
- u32 sec_len; /* section data + header byte count */
-
- /* FWDL maps to one or more PTDLs, as max size for a PTDL is 1K words */
- while (left)
- {
- /* Calculate amount to be transferred */
- sec_data_len = min_t(u32, left, PTDL_MAX_SIZE - PTDL_HDR_SIZE);
- sec_len = sec_data_len + PTDL_HDR_SIZE;
-
- /* Write PTDL header + entire PTDL size */
- written += write_chunk(buf, offset + written, (char *)"PTDL", sec_len);
- /* bug digest implies 4 bytes of padding here, but that seems wrong */
-
- /* Checksum starts here */
- csum_start_offs = offset + written;
-
- /* Patch-chunk header: fw_id. Note that this is in XAP word order */
- written += write_uint16(buf, offset + written, (u16)(fw_id >> 16));
- written += write_uint16(buf, offset + written, (u16)(fw_id & 0xffff));
-
- /* Patch-chunk header: section length in uint16s */
- written += write_uint16(buf, offset + written, (u16)(sec_len / 2));
-
-
- /* Write the appropriate patch command for the data's destination ptr */
- written += write_patchcmd(buf, offset + written, dl_addr, (u16)(sec_data_len / 2));
-
- /* Write the data itself (limited to the max chunk length) */
- if (readfn(NULL, (void *)dlpriv, dl_offs, fw_buf, sec_data_len) < 0)
- {
- return 0;
- }
-
- written += write_bytes(buf,
- offset + written,
- fw_buf,
- sec_data_len);
-
- /* u16 checksum calculated over data written */
- csum = calc_checksum(buf, csum_start_offs, written - (csum_start_offs - offset));
- written += write_uint16(buf, offset + written, csum);
-
- left -= sec_data_len;
- dl_addr += sec_data_len;
- dl_offs += sec_data_len;
- chunks++;
- }
-
- return written;
-}
-
-
-#define SEC_CMD_LEN ((4 + 2) * 2) /* sizeof(cmd, vector) per XAP */
-#define PTDL_VEC_HDR_SIZE (4 + 2 + 2) /* sizeof(fw_id, sec_len, csum) */
-#define UF_MAC_START_VEC 0x00c00000 /* Start address of image on MAC */
-#define UF_PHY_START_VEC 0x00c00000 /* Start address of image on PHY */
-#define UF_MAC_START_CMD 0x6000 /* MAC "Set start address" command */
-#define UF_PHY_START_CMD 0x7000 /* PHY "Set start address" command */
-
-static u32 write_reset_ptdl(void *buf, const u32 offset, const xbv1_t *fwinfo, u32 fw_id)
-{
- u32 written = 0;
- u16 csum;
- u32 csum_start_offs; /* first offset to include in checksum */
- u32 sec_len; /* section data + header byte count */
-
- sec_len = SEC_CMD_LEN + PTDL_VEC_HDR_SIZE; /* Total section byte length */
-
- /* Write PTDL header + entire PTDL size */
- written += write_chunk(buf, offset + written, (char *)"PTDL", sec_len);
-
- /* Checksum starts here */
- csum_start_offs = offset + written;
-
- /* Patch-chunk header: fw_id. Note that this is in XAP word order */
- written += write_uint16(buf, offset + written, (u16)(fw_id >> 16));
- written += write_uint16(buf, offset + written, (u16)(fw_id & 0xffff));
-
- /* Patch-chunk header: section length in uint16s */
- written += write_uint16(buf, offset + written, (u16)(sec_len / 2));
-
- /*
- * Restart addresses to be executed on subsequent loader restart command.
- */
-
- /* Setup the MAC start address, note word ordering */
- written += write_uint16(buf, offset + written, UF_MAC_START_CMD);
- written += write_uint16(buf, offset + written, (UF_MAC_START_VEC >> 16));
- written += write_uint16(buf, offset + written, (UF_MAC_START_VEC & 0xffff));
-
- /* Setup the PHY start address, note word ordering */
- written += write_uint16(buf, offset + written, UF_PHY_START_CMD);
- written += write_uint16(buf, offset + written, (UF_PHY_START_VEC >> 16));
- written += write_uint16(buf, offset + written, (UF_PHY_START_VEC & 0xffff));
-
- /* u16 checksum calculated over data written */
- csum = calc_checksum(buf, csum_start_offs, written - (csum_start_offs - offset));
- written += write_uint16(buf, offset + written, csum);
-
- return written;
-}
-
-
-/*
- * ---------------------------------------------------------------------------
- * read_slut
- *
- * desc
- *
- * Arguments:
- * readfn Pointer to function to call to read from the file.
- * dlpriv Opaque pointer arg to pass to readfn.
- * addr Offset into firmware image of SLUT.
- * fwinfo Pointer to fwinfo struct to fill in.
- *
- * Returns:
- * Number of SLUT entries in the f/w, or -1 if the image was corrupt.
- * ---------------------------------------------------------------------------
- */
-s32 xbv1_read_slut(card_t *card, fwreadfn_t readfn, void *dlpriv, xbv1_t *fwinfo,
- symbol_t *slut, u32 slut_len)
-{
- s16 i;
- s32 offset;
- u32 magic;
- u32 count = 0;
- ct_t ct;
-
- if (fwinfo->mode != xbv_firmware)
- {
- return -1;
- }
-
- /* Find the d/l segment containing the SLUT */
- /* This relies on the SLUT being entirely contained in one segment */
- offset = -1;
- for (i = 0; i < fwinfo->num_fwdl; i++)
- {
- if ((fwinfo->slut_addr >= fwinfo->fwdl[i].dl_addr) &&
- (fwinfo->slut_addr < (fwinfo->fwdl[i].dl_addr + fwinfo->fwdl[i].dl_size)))
- {
- offset = fwinfo->fwdl[i].dl_offset +
- (fwinfo->slut_addr - fwinfo->fwdl[i].dl_addr);
- }
- }
- if (offset < 0)
- {
- return -1;
- }
-
- ct.dlpriv = dlpriv;
- ct.ioffset = offset;
- ct.iread = readfn;
-
- if (read_uint(card, &ct, &magic, 2))
- {
- return -1;
- }
- if (magic != SLUT_FINGERPRINT)
- {
- return -1;
- }
-
- while (count < slut_len)
- {
- u32 id, obj;
-
- /* Read Symbol Id */
- if (read_uint(card, &ct, &id, 2))
- {
- return -1;
- }
-
- /* Check for end of table marker */
- if (id == CSR_SLT_END)
- {
- break;
- }
-
- /* Read Symbol Value */
- if (read_uint(card, &ct, &obj, 4))
- {
- return -1;
- }
-
- slut[count].id = (u16)id;
- slut[count].obj = obj;
- count++;
- }
-
- return count;
-} /* read_slut() */
-
-
-/*
- * ---------------------------------------------------------------------------
- * xbv_to_patch
- *
- * Convert (the relevant parts of) a firmware xbv file into a patch xbv
- *
- * Arguments:
- * card
- * fw_buf - pointer to xbv firmware image
- * fwinfo - structure describing the firmware image
- * size - pointer to location into which size of f/w is written.
- *
- * Returns:
- * Pointer to firmware image, or NULL on error. Caller must free this
- * buffer via kfree() once it's finished with.
- *
- * Notes:
- * The input fw_buf should have been checked via xbv1_parse prior to
- * calling this function, so the input image is assumed valid.
- * ---------------------------------------------------------------------------
- */
-#define PTCH_LIST_SIZE 16 /* sizeof PTCH+FWID chunk in LIST header */
-
-void* xbv_to_patch(card_t *card, fwreadfn_t readfn,
- const void *fw_buf, const xbv1_t *fwinfo, u32 *size)
-{
- void *patch_buf = NULL;
- u32 patch_buf_size;
- u32 payload_offs = 0; /* Start of XBV payload */
- s16 i;
- u32 patch_offs = 0;
- u32 list_len_offs = 0; /* Offset of PTDL LIST length parameter */
- u32 ptdl_start_offs = 0; /* Offset of first PTDL chunk */
- u32 fw_id;
- void *rdbuf;
-
- if (!fw_buf || !fwinfo || !card)
- {
- return NULL;
- }
-
- if (fwinfo->mode != xbv_firmware)
- {
- unifi_error(NULL, "Not a firmware file\n");
- return NULL;
- }
-
- /* Pre-allocate read buffer for chunk conversion */
- rdbuf = kmalloc(PTDL_MAX_SIZE, GFP_KERNEL);
- if (!rdbuf)
- {
- unifi_error(card, "Couldn't alloc conversion buffer\n");
- return NULL;
- }
-
- /* Loader requires patch file's build ID to match the running firmware's */
- fw_id = card->build_id;
-
- /* Firmware XBV1 contains VERF, optional INFO, SLUT(s), FWDL(s) */
- /* Other chunks should get skipped. */
- /* VERF should be sanity-checked against chip version */
-
- /* Patch XBV1 contains VERF, optional INFO, PTCH */
- /* PTCH contains FWID, optional INFO, PTDL(s), PTDL(start_vec) */
- /* Each FWDL is split into PTDLs (each is 1024 XAP words max) */
- /* Each PTDL contains running ROM f/w version, and checksum */
- /* MAC/PHY reset addresses (known) are added into a final PTDL */
-
- /* The input image has already been parsed, and loaded into fwinfo, so we
- * can use that to build the output image
- */
- patch_buf_size = calc_patch_size(fwinfo);
-
- patch_buf = kmalloc(patch_buf_size, GFP_KERNEL);
- if (!patch_buf)
- {
- kfree(rdbuf);
- unifi_error(NULL, "Can't malloc buffer for patch conversion\n");
- return NULL;
- }
-
- memset(patch_buf, 0xdd, patch_buf_size);
-
- /* Write XBV + VERF headers */
- patch_offs += write_xbv_header(patch_buf, patch_offs, 0);
- payload_offs = patch_offs;
-
- /* Write patch (LIST) header */
- list_len_offs = patch_offs + 4; /* Save LIST.length offset for later update */
- patch_offs += write_ptch_header(patch_buf, patch_offs, fw_id);
-
- /* Save start offset of the PTDL chunks */
- ptdl_start_offs = patch_offs;
-
- /* Write LIST of firmware PTDL blocks */
- for (i = 0; i < fwinfo->num_fwdl; i++)
- {
- patch_offs += write_fwdl_to_ptdl(patch_buf,
- patch_offs,
- readfn,
- &fwinfo->fwdl[i],
- fw_buf,
- fw_id,
- rdbuf);
- }
-
- /* Write restart-vector PTDL last */
- patch_offs += write_reset_ptdl(patch_buf, patch_offs, fwinfo, fw_id);
-
- /* Now the length is known, update the LIST.length */
- (void)write_uint32(patch_buf, list_len_offs,
- (patch_offs - ptdl_start_offs) + PTCH_LIST_SIZE);
-
- /* Re write XBV headers just to fill in the correct file size */
- (void)write_xbv_header(patch_buf, 0, (patch_offs - payload_offs));
-
- unifi_trace(card->ospriv, UDBG1, "XBV:PTCH size %u, fw_id %u\n",
- patch_offs, fw_id);
- if (size)
- {
- *size = patch_offs;
- }
- kfree(rdbuf);
-
- return patch_buf;
-}
-
-