diff options
Diffstat (limited to 'drivers/staging/csr/csr_wifi_hip_download.c')
-rw-r--r-- | drivers/staging/csr/csr_wifi_hip_download.c | 835 |
1 files changed, 835 insertions, 0 deletions
diff --git a/drivers/staging/csr/csr_wifi_hip_download.c b/drivers/staging/csr/csr_wifi_hip_download.c new file mode 100644 index 000000000000..8e4a4608ba5c --- /dev/null +++ b/drivers/staging/csr/csr_wifi_hip_download.c @@ -0,0 +1,835 @@ +/***************************************************************************** + + (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_download.c + * + * PURPOSE: + * Routines for downloading firmware to UniFi. + * + * --------------------------------------------------------------------------- + */ +#include <linux/slab.h> +#include "csr_wifi_hip_unifi.h" +#include "csr_wifi_hip_unifiversion.h" +#include "csr_wifi_hip_card.h" +#include "csr_wifi_hip_xbv.h" + +#undef CSR_WIFI_IGNORE_PATCH_VERSION_MISMATCH + +static CsrResult do_patch_download(card_t *card, void *dlpriv, + xbv1_t *pfwinfo, u32 boot_ctrl_addr); + +static CsrResult do_patch_convert_download(card_t *card, + void *dlpriv, xbv1_t *pfwinfo); + +/* + * --------------------------------------------------------------------------- + * _find_in_slut + * + * Find the offset of the appropriate object in the SLUT of a card + * + * Arguments: + * card Pointer to card struct + * psym Pointer to symbol object. + * id set up by caller + * obj will be set up by this function + * pslut Pointer to SLUT address, if 0xffffffff then it must be + * read from the chip. + * Returns: + * CSR_RESULT_SUCCESS on success + * Non-zero on error, + * CSR_WIFI_HIP_RESULT_NOT_FOUND if not found + * --------------------------------------------------------------------------- + */ +static CsrResult _find_in_slut(card_t *card, symbol_t *psym, u32 *pslut) +{ + u32 slut_address; + u16 finger_print; + CsrResult r; + CsrResult csrResult; + + /* Get SLUT address */ + if (*pslut == 0xffffffff) + { + r = card_wait_for_firmware_to_start(card, &slut_address); + if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE) + { + return r; + } + if (r != CSR_RESULT_SUCCESS) + { + unifi_error(card->ospriv, "Firmware hasn't started\n"); + func_exit_r(r); + return r; + } + *pslut = slut_address; + + /* + * Firmware has started so set the SDIO bus clock to the initial speed, + * faster than UNIFI_SDIO_CLOCK_SAFE_HZ, to speed up the f/w download. + */ + csrResult = CsrSdioMaxBusClockFrequencySet(card->sdio_if, UNIFI_SDIO_CLOCK_INIT_HZ); + if (csrResult != CSR_RESULT_SUCCESS) + { + r = ConvertCsrSdioToCsrHipResult(card, csrResult); + func_exit_r(r); + return r; + } + card->sdio_clock_speed = UNIFI_SDIO_CLOCK_INIT_HZ; + } + else + { + slut_address = *pslut; /* Use previously discovered address */ + } + unifi_trace(card->ospriv, UDBG4, "SLUT addr: 0x%lX\n", slut_address); + + /* + * Check the SLUT fingerprint. + * The slut_address is a generic pointer so we must use unifi_card_read16(). + */ + unifi_trace(card->ospriv, UDBG4, "Looking for SLUT finger print\n"); + finger_print = 0; + r = unifi_card_read16(card, slut_address, &finger_print); + if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE) + { + return r; + } + if (r != CSR_RESULT_SUCCESS) + { + unifi_error(card->ospriv, "Failed to read SLUT finger print\n"); + func_exit_r(r); + return r; + } + + if (finger_print != SLUT_FINGERPRINT) + { + unifi_error(card->ospriv, "Failed to find SLUT fingerprint\n"); + func_exit_r(CSR_RESULT_FAILURE); + return CSR_RESULT_FAILURE; + } + + /* Symbol table starts imedately after the fingerprint */ + slut_address += 2; + + while (1) + { + u16 id; + u32 obj; + + r = unifi_card_read16(card, slut_address, &id); + if (r != CSR_RESULT_SUCCESS) + { + func_exit_r(r); + return r; + } + slut_address += 2; + + if (id == CSR_SLT_END) + { + /* End of table reached: not found */ + r = CSR_WIFI_HIP_RESULT_RANGE; + break; + } + + r = unifi_read32(card, slut_address, &obj); + if (r != CSR_RESULT_SUCCESS) + { + func_exit_r(r); + return r; + } + slut_address += 4; + + unifi_trace(card->ospriv, UDBG3, " found SLUT id %02d.%08lx\n", id, obj); + + r = CSR_WIFI_HIP_RESULT_NOT_FOUND; + /* Found search term? */ + if (id == psym->id) + { + unifi_trace(card->ospriv, UDBG1, " matched SLUT id %02d.%08lx\n", id, obj); + psym->obj = obj; + r = CSR_RESULT_SUCCESS; + break; + } + } + + func_exit_r(r); + return r; +} + + +/* + * --------------------------------------------------------------------------- + * do_patch_convert_download + * + * Download the given firmware image to the UniFi, converting from FWDL + * to PTDL XBV format. + * + * Arguments: + * card Pointer to card struct + * dlpriv Pointer to source firmware image + * fwinfo Pointer to source firmware info struct + * + * Returns: + * CSR_RESULT_SUCCESS on success, CSR error code on error + * + * Notes: + * --------------------------------------------------------------------------- + */ +static CsrResult do_patch_convert_download(card_t *card, void *dlpriv, xbv1_t *pfwinfo) +{ + CsrResult r; + u32 slut_base = 0xffffffff; + void *pfw; + u32 psize; + symbol_t sym; + + /* Reset the chip to guarantee that the ROM loader is running */ + r = unifi_init(card); + if (r != CSR_RESULT_SUCCESS) + { + unifi_error(card->ospriv, + "do_patch_convert_download: failed to re-init UniFi\n"); + return r; + } + + /* If no unifi_helper is running, the firmware version must be read */ + if (card->build_id == 0) + { + u32 ver = 0; + sym.id = CSR_SLT_BUILD_ID_NUMBER; + sym.obj = 0; /* To be updated by _find_in_slut() */ + + unifi_trace(card->ospriv, UDBG1, "Need f/w version\n"); + + /* Find chip build id entry in SLUT */ + r = _find_in_slut(card, &sym, &slut_base); + if (r != CSR_RESULT_SUCCESS) + { + unifi_error(card->ospriv, "Failed to find CSR_SLT_BUILD_ID_NUMBER\n"); + return CSR_RESULT_FAILURE; + } + + /* Read running f/w version */ + r = unifi_read32(card, sym.obj, &ver); + if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE) + { + return r; + } + if (r != CSR_RESULT_SUCCESS) + { + unifi_error(card->ospriv, "Failed to read f/w id\n"); + return CSR_RESULT_FAILURE; + } + card->build_id = ver; + } + + /* Convert the ptest firmware to a patch against the running firmware */ + pfw = xbv_to_patch(card, unifi_fw_read, dlpriv, pfwinfo, &psize); + if (!pfw) + { + unifi_error(card->ospriv, "Failed to convert f/w to patch"); + return CSR_WIFI_HIP_RESULT_NO_MEMORY; + } + else + { + void *desc; + sym.id = CSR_SLT_BOOT_LOADER_CONTROL; + sym.obj = 0; /* To be updated by _find_in_slut() */ + + /* Find boot loader control entry in SLUT */ + r = _find_in_slut(card, &sym, &slut_base); + if (r != CSR_RESULT_SUCCESS) + { + unifi_error(card->ospriv, "Failed to find BOOT_LOADER_CONTROL\n"); + return CSR_RESULT_FAILURE; + } + + r = unifi_set_host_state(card, UNIFI_HOST_STATE_AWAKE); + if (r != CSR_RESULT_SUCCESS) + { + unifi_error(card->ospriv, "Failed to wake UniFi\n"); + } + + /* Get a dlpriv for the patch buffer so that unifi_fw_read() can + * access it. + */ + desc = unifi_fw_open_buffer(card->ospriv, pfw, psize); + if (!desc) + { + return CSR_WIFI_HIP_RESULT_NO_MEMORY; + } + + /* Download the patch */ + unifi_info(card->ospriv, "Downloading converted f/w as patch\n"); + r = unifi_dl_patch(card, desc, sym.obj); + kfree(pfw); + unifi_fw_close_buffer(card->ospriv, desc); + + if (r != CSR_RESULT_SUCCESS) + { + unifi_error(card->ospriv, "Converted patch download failed\n"); + func_exit_r(r); + return r; + } + else + { + unifi_trace(card->ospriv, UDBG1, "Converted patch downloaded\n"); + } + + /* This command starts the firmware */ + r = unifi_do_loader_op(card, sym.obj + 6, UNIFI_BOOT_LOADER_RESTART); + if (r != CSR_RESULT_SUCCESS) + { + unifi_error(card->ospriv, "Failed to write loader restart cmd\n"); + } + + func_exit_r(r); + return r; + } +} + + +/* + * --------------------------------------------------------------------------- + * unifi_dl_firmware + * + * Download the given firmware image to the UniFi. + * + * Arguments: + * card Pointer to card struct + * dlpriv A context pointer from the calling function to be + * passed when calling unifi_fw_read(). + * + * Returns: + * CSR_RESULT_SUCCESS on success, + * CSR_WIFI_HIP_RESULT_NO_MEMORY memory allocation failed + * CSR_WIFI_HIP_RESULT_INVALID_VALUE error in XBV file + * CSR_RESULT_FAILURE SDIO error + * + * Notes: + * Stops and resets the chip, does the download and runs the new + * firmware. + * --------------------------------------------------------------------------- + */ +CsrResult unifi_dl_firmware(card_t *card, void *dlpriv) +{ + xbv1_t *fwinfo; + CsrResult r; + + func_enter(); + + fwinfo = kmalloc(sizeof(xbv1_t), GFP_KERNEL); + if (fwinfo == NULL) + { + unifi_error(card->ospriv, "Failed to allocate memory for firmware\n"); + return CSR_WIFI_HIP_RESULT_NO_MEMORY; + } + + /* + * Scan the firmware file to find the TLVs we are interested in. + * These are: + * - check we support the file format version in VERF + * - SLTP Symbol Lookup Table Pointer + * - FWDL firmware download segments + * - FWOV firmware overlay segment + * - VMEQ Register probe tests to verify matching h/w + */ + r = xbv1_parse(card, unifi_fw_read, dlpriv, fwinfo); + if (r != CSR_RESULT_SUCCESS || fwinfo->mode != xbv_firmware) + { + unifi_error(card->ospriv, "File type is %s, expected firmware.\n", + fwinfo->mode == xbv_patch?"patch" : "unknown"); + kfree(fwinfo); + return CSR_WIFI_HIP_RESULT_INVALID_VALUE; + } + + /* UF6xxx doesn't accept firmware, only patches. Therefore we convert + * the file to patch format with version numbers matching the current + * running firmware, and then download via the patch mechanism. + * The sole purpose of this is to support production test firmware across + * different ROM releases, the test firmware being provided in non-patch + * format. + */ + if (card->chip_id > SDIO_CARD_ID_UNIFI_2) + { + unifi_info(card->ospriv, "Must convert f/w to patch format\n"); + r = do_patch_convert_download(card, dlpriv, fwinfo); + } + else + { + /* Older UniFi chips allowed firmware to be directly loaded onto the + * chip, which is no longer supported. + */ + unifi_error(card->ospriv, "Only patch downloading supported\n"); + r = CSR_WIFI_HIP_RESULT_INVALID_VALUE; + } + + kfree(fwinfo); + func_exit_r(r); + return r; +} /* unifi_dl_firmware() */ + + +/* + * --------------------------------------------------------------------------- + * unifi_dl_patch + * + * Load the given patch set into UniFi. + * + * Arguments: + * card Pointer to card struct + * dlpriv The os specific handle to the firmware file. + * boot_ctrl The address of the boot loader control structure. + * + * Returns: + * CSR_RESULT_SUCCESS on success, + * CSR_WIFI_HIP_RESULT_NO_MEMORY memory allocation failed + * CSR_WIFI_HIP_RESULT_INVALID_VALUE error in XBV file + * CSR_RESULT_FAILURE SDIO error + * + * Notes: + * This ends up telling UniFi to restart. + * --------------------------------------------------------------------------- + */ +CsrResult unifi_dl_patch(card_t *card, void *dlpriv, u32 boot_ctrl) +{ + xbv1_t *fwinfo; + CsrResult r; + + func_enter(); + + unifi_info(card->ospriv, "unifi_dl_patch %p %08x\n", dlpriv, boot_ctrl); + + fwinfo = kmalloc(sizeof(xbv1_t), GFP_KERNEL); + if (fwinfo == NULL) + { + unifi_error(card->ospriv, "Failed to allocate memory for patches\n"); + func_exit(); + return CSR_WIFI_HIP_RESULT_NO_MEMORY; + } + + /* + * Scan the firmware file to find the TLVs we are interested in. + * These are: + * - check we support the file format version in VERF + * - FWID The build ID of the ROM that we can patch + * - PTDL patch download segments + */ + r = xbv1_parse(card, unifi_fw_read, dlpriv, fwinfo); + if (r != CSR_RESULT_SUCCESS || fwinfo->mode != xbv_patch) + { + kfree(fwinfo); + unifi_error(card->ospriv, "Failed to read in patch file\n"); + func_exit(); + return CSR_WIFI_HIP_RESULT_INVALID_VALUE; + } + + /* + * We have to check the build id read from the SLUT against that + * for the patch file. They have to match exactly. + * "card->build_id" == XBV1.PTCH.FWID + */ + if (card->build_id != fwinfo->build_id) + { + unifi_error(card->ospriv, "Wrong patch file for chip (chip = %lu, file = %lu)\n", + card->build_id, fwinfo->build_id); + kfree(fwinfo); +#ifndef CSR_WIFI_IGNORE_PATCH_VERSION_MISMATCH + func_exit(); + return CSR_WIFI_HIP_RESULT_INVALID_VALUE; +#else + fwinfo = NULL; + dlpriv = NULL; + return CSR_RESULT_SUCCESS; +#endif + } + + r = do_patch_download(card, dlpriv, fwinfo, boot_ctrl); + if (r != CSR_RESULT_SUCCESS) + { + unifi_error(card->ospriv, "Failed to patch image\n"); + } + + kfree(fwinfo); + + func_exit_r(r); + return r; +} /* unifi_dl_patch() */ + + +void* unifi_dl_fw_read_start(card_t *card, s8 is_fw) +{ + card_info_t card_info; + + unifi_card_info(card, &card_info); + unifi_trace(card->ospriv, UDBG5, + "id=%d, ver=0x%x, fw_build=%u, fw_hip=0x%x, block_size=%d\n", + card_info.chip_id, card_info.chip_version, + card_info.fw_build, card_info.fw_hip_version, + card_info.sdio_block_size); + + return unifi_fw_read_start(card->ospriv, is_fw, &card_info); +} + + +/* + * --------------------------------------------------------------------------- + * safe_read_shared_location + * + * Read a shared memory location repeatedly until we get two readings + * the same. + * + * Arguments: + * card Pointer to card context struct. + * unifi_addr UniFi shared-data-memory address to access. + * pdata Pointer to a byte variable for the value read. + * + * + * Returns: + * CSR_RESULT_SUCCESS on success, CSR error code on failure + * --------------------------------------------------------------------------- + */ +static CsrResult safe_read_shared_location(card_t *card, u32 address, u8 *pdata) +{ + CsrResult r; + u16 limit = 1000; + u8 b, b2; + + *pdata = 0; + + r = unifi_read_8_or_16(card, address, &b); + if (r != CSR_RESULT_SUCCESS) + { + return r; + } + + while (limit--) + { + r = unifi_read_8_or_16(card, address, &b2); + if (r != CSR_RESULT_SUCCESS) + { + return r; + } + + /* When we have a stable value, return it */ + if (b == b2) + { + *pdata = b; + return CSR_RESULT_SUCCESS; + } + + b = b2; + } + + return CSR_RESULT_FAILURE; +} /* safe_read_shared_location() */ + + +/* + * --------------------------------------------------------------------------- + * unifi_do_loader_op + * + * Send a loader / boot_loader command to the UniFi and wait for + * it to complete. + * + * Arguments: + * card Pointer to card context struct. + * op_addr The address of the loader operation control word. + * opcode The operation to perform. + * + * Returns: + * CSR_RESULT_SUCCESS on success + * CSR_RESULT_FAILURE SDIO error or SDIO/XAP timeout + * --------------------------------------------------------------------------- + */ + +/* + * Ideally instead of sleeping, we want to busy wait. + * Currently there is no framework API to do this. When it becomes available, + * we can use it to busy wait using usecs + */ +#define OPERATION_TIMEOUT_LOOPS (100) /* when OPERATION_TIMEOUT_DELAY==1, (500) otherwise */ +#define OPERATION_TIMEOUT_DELAY 1 /* msec, or 200usecs */ + +CsrResult unifi_do_loader_op(card_t *card, u32 op_addr, u8 opcode) +{ + CsrResult r; + s16 op_retries; + + unifi_trace(card->ospriv, UDBG4, "Loader cmd 0x%0x -> 0x%08x\n", opcode, op_addr); + + /* Set the Operation command byte to the opcode */ + r = unifi_write_8_or_16(card, op_addr, opcode); + if (r != CSR_RESULT_SUCCESS) + { + unifi_error(card->ospriv, "Failed to write loader copy command\n"); + return r; + } + + /* Wait for Operation command byte to be Idle */ + /* Typically takes ~100us */ + op_retries = 0; + r = CSR_RESULT_SUCCESS; + while (1) + { + u8 op; + + /* + * Read the memory location until two successive reads give + * the same value. + * Then handle it. + */ + r = safe_read_shared_location(card, op_addr, &op); + if (r != CSR_RESULT_SUCCESS) + { + unifi_error(card->ospriv, "Failed to read loader status\n"); + break; + } + + if (op == UNIFI_LOADER_IDLE) + { + /* Success */ + break; + } + + if (op != opcode) + { + unifi_error(card->ospriv, "Error reported by loader: 0x%X\n", op); + r = CSR_RESULT_FAILURE; + break; + } + + /* Allow 500us timeout */ + if (++op_retries >= OPERATION_TIMEOUT_LOOPS) + { + unifi_error(card->ospriv, "Timeout waiting for loader to ack transfer\n"); + /* Stop XAPs to aid post-mortem */ + r = unifi_card_stop_processor(card, UNIFI_PROC_BOTH); + if (r != CSR_RESULT_SUCCESS) + { + unifi_error(card->ospriv, "Failed to stop UniFi processors\n"); + } + else + { + r = CSR_RESULT_FAILURE; + } + break; + } + CsrThreadSleep(OPERATION_TIMEOUT_DELAY); + } /* Loop exits with r != CSR_RESULT_SUCCESS on error */ + + return r; +} /* unifi_do_loader_op() */ + + +/* + * --------------------------------------------------------------------------- + * send_ptdl_to_unifi + * + * Copy a patch block from userland to the UniFi. + * This function reads data, 2K at a time, from userland and writes + * it to the UniFi. + * + * Arguments: + * card A pointer to the card structure + * dlpriv The os specific handle for the firmware file + * ptdl A pointer ot the PTDL block + * handle The buffer handle to use for the xfer + * op_addr The address of the loader operation control word + * + * Returns: + * Number of bytes sent (Positive) or negative value indicating + * error code: + * CSR_WIFI_HIP_RESULT_NO_MEMORY memory allocation failed + * CSR_WIFI_HIP_RESULT_INVALID_VALUE error in XBV file + * CSR_RESULT_FAILURE SDIO error + * --------------------------------------------------------------------------- + */ +static CsrResult send_ptdl_to_unifi(card_t *card, void *dlpriv, + const struct PTDL *ptdl, u32 handle, + u32 op_addr) +{ + u32 offset; + u8 *buf; + s32 data_len; + u32 write_len; + CsrResult r; + const u16 buf_size = 2 * 1024; + + offset = ptdl->dl_offset; + data_len = ptdl->dl_size; + + if (data_len > buf_size) + { + unifi_error(card->ospriv, "PTDL block is too large (%u)\n", + ptdl->dl_size); + return CSR_WIFI_HIP_RESULT_INVALID_VALUE; + } + + buf = kmalloc(buf_size, GFP_KERNEL); + if (buf == NULL) + { + unifi_error(card->ospriv, "Failed to allocate transfer buffer for firmware download\n"); + return CSR_WIFI_HIP_RESULT_NO_MEMORY; + } + + r = CSR_RESULT_SUCCESS; + + if (unifi_fw_read(card->ospriv, dlpriv, offset, buf, data_len) != data_len) + { + unifi_error(card->ospriv, "Failed to read from file\n"); + } + else + { + /* We can always round these if the host wants to */ + if (card->sdio_io_block_pad) + { + write_len = (data_len + (card->sdio_io_block_size - 1)) & + ~(card->sdio_io_block_size - 1); + + /* Zero out the rest of the buffer (This isn't needed, but it + * makes debugging things later much easier). */ + memset(buf + data_len, 0, write_len - data_len); + } + else + { + write_len = data_len; + } + + r = unifi_bulk_rw_noretry(card, handle, buf, write_len, UNIFI_SDIO_WRITE); + if (r != CSR_RESULT_SUCCESS) + { + unifi_error(card->ospriv, "CMD53 failed writing %d bytes to handle %ld\n", + data_len, handle); + } + else + { + /* + * Can change the order of things to overlap read from file + * with copy to unifi + */ + r = unifi_do_loader_op(card, op_addr, UNIFI_BOOT_LOADER_PATCH); + } + } + + kfree(buf); + + if (r != CSR_RESULT_SUCCESS && r != CSR_WIFI_HIP_RESULT_NO_DEVICE) + { + unifi_error(card->ospriv, "Failed to copy block of %u bytes to UniFi\n", + ptdl->dl_size); + } + + return r; +} /* send_ptdl_to_unifi() */ + + +/* + * --------------------------------------------------------------------------- + * do_patch_download + * + * This function downloads a set of patches to UniFi and then + * causes it to restart. + * + * Arguments: + * card Pointer to card struct. + * dlpriv A context pointer from the calling function to be + * used when reading the XBV file. This can be NULL + * in which case not patches are applied. + * pfwinfo Pointer to a fwinfo struct describing the f/w + * XBV file. + * boot_ctrl_addr The address of the boot loader control structure. + * + * Returns: + * 0 on success, or an error code + * CSR_WIFI_HIP_RESULT_INVALID_VALUE for a bad laoader version number + * --------------------------------------------------------------------------- + */ +static CsrResult do_patch_download(card_t *card, void *dlpriv, xbv1_t *pfwinfo, u32 boot_ctrl_addr) +{ + CsrResult r; + s32 i; + u16 loader_version; + u16 handle; + u32 total_bytes; + + /* + * Read info from the SDIO Loader Control Data Structure + */ + /* Check the loader version */ + r = unifi_card_read16(card, boot_ctrl_addr, &loader_version); + if (r != CSR_RESULT_SUCCESS) + { + unifi_error(card->ospriv, "Patch download: Failed to read loader version\n"); + return r; + } + unifi_trace(card->ospriv, UDBG2, "Patch download: boot loader version 0x%04X\n", loader_version); + switch (loader_version) + { + case 0x0000: + break; + + default: + unifi_error(card->ospriv, "Patch loader version (0x%04X) is not supported by this driver\n", + loader_version); + return CSR_WIFI_HIP_RESULT_INVALID_VALUE; + } + + /* Retrieve the handle to use with CMD53 */ + r = unifi_card_read16(card, boot_ctrl_addr + 4, &handle); + if (r != CSR_RESULT_SUCCESS) + { + unifi_error(card->ospriv, "Patch download: Failed to read loader handle\n"); + return r; + } + + /* Set the mask of LEDs to flash */ + if (card->loader_led_mask) + { + r = unifi_card_write16(card, boot_ctrl_addr + 2, + (u16)card->loader_led_mask); + if (r != CSR_RESULT_SUCCESS) + { + unifi_error(card->ospriv, "Patch download: Failed to write LED mask\n"); + return r; + } + } + + total_bytes = 0; + + /* Copy download data to UniFi memory */ + for (i = 0; i < pfwinfo->num_ptdl; i++) + { + unifi_trace(card->ospriv, UDBG3, "Patch download: %d Downloading for %d from offset %d\n", + i, + pfwinfo->ptdl[i].dl_size, + pfwinfo->ptdl[i].dl_offset); + + r = send_ptdl_to_unifi(card, dlpriv, &pfwinfo->ptdl[i], + handle, boot_ctrl_addr + 6); + if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE) + { + return r; + } + if (r != CSR_RESULT_SUCCESS) + { + unifi_error(card->ospriv, "Patch failed after %u bytes\n", + total_bytes); + return r; + } + total_bytes += pfwinfo->ptdl[i].dl_size; + } + + return CSR_RESULT_SUCCESS; +} /* do_patch_download() */ + + |