/** * @brief This function polls the card status register. * * @param pmadapter A pointer to mlan_adapter structure * @param bits the bit mask * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_sdio_poll_card_status(mlan_adapter * pmadapter, t_u8 bits) { pmlan_callbacks pcb = &pmadapter->callbacks; t_u32 tries; t_u32 cs = 0; ENTER(); for (tries = 0; tries < MAX_POLL_TRIES; tries++) { if (pcb-> moal_read_reg(pmadapter->pmoal_handle, CARD_TO_HOST_EVENT_REG, &cs) != MLAN_STATUS_SUCCESS) break; else if ((cs & bits) == bits) { LEAVE(); return MLAN_STATUS_SUCCESS; } wlan_udelay(pmadapter, 10); } PRINTM(MERROR, "wlan_sdio_poll_card_status failed, tries = %d, cs = 0x%x\n", tries, cs); LEAVE(); return MLAN_STATUS_FAILURE; }
/** * @brief This function polls the card status register. * * @param pmadapter A pointer to mlan_adapter structure * @param bits the bit mask * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_sdio_poll_card_status(mlan_adapter * pmadapter, t_u8 bits) { pmlan_callbacks pcb = &pmadapter->callbacks; t_u32 tries; t_u32 cs = 0; for (tries = 0; tries < MAX_POLL_TRIES; tries++) { if (pcb->moal_read_reg(pmadapter->pmoal_handle, CARD_STATUS_REG, &cs) != MLAN_STATUS_SUCCESS) break; else if ((cs & bits) == bits) return MLAN_STATUS_SUCCESS; wlan_udelay(pmadapter, 10); } PRINTM(MWARN, "wlan_sdio_poll_card_status failed, tries = %d\n", tries); return MLAN_STATUS_FAILURE; }
/** * @brief This function downloads FW blocks to device * * @param pmadapter A pointer to mlan_adapter * @param pmfw A pointer to firmware image * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_prog_fw_w_helper(IN pmlan_adapter pmadapter, IN pmlan_fw_image pmfw) { mlan_status ret = MLAN_STATUS_SUCCESS; pmlan_callbacks pcb = &pmadapter->callbacks; t_u8 *firmware = pmfw->pfw_buf; t_u32 firmwarelen = pmfw->fw_len; t_u32 offset = 0; t_u32 base0, base1; t_void *tmpfwbuf = MNULL; t_u32 tmpfwbufsz; t_u8 *fwbuf; mlan_buffer mbuf; t_u16 len = 0; t_u32 txlen = 0, tx_blocks = 0, tries = 0; t_u32 i = 0; t_u8 crc_buffer = 0; t_u8 *header_crc_fw; t_u8 header_crc_fw_len = 0; header_crc_fw = fw_crc_header_rb; header_crc_fw_len = FW_CRC_HEADER_RB; ENTER(); if (!firmware && !pcb->moal_get_fw_data) { PRINTM(MMSG, "No firmware image found! Terminating download\n"); LEAVE(); return MLAN_STATUS_FAILURE; } PRINTM(MINFO, "WLAN: Downloading FW image (%d bytes)\n", firmwarelen); tmpfwbufsz = ALIGN_SZ(WLAN_UPLD_SIZE, DMA_ALIGNMENT); ret = pcb->moal_malloc(pmadapter->pmoal_handle, tmpfwbufsz, MLAN_MEM_DEF | MLAN_MEM_DMA, (t_u8 **) & tmpfwbuf); if ((ret != MLAN_STATUS_SUCCESS) || !tmpfwbuf) { PRINTM(MERROR, "Unable to allocate buffer for firmware. Terminating download\n"); ret = MLAN_STATUS_FAILURE; goto done; } memset(pmadapter, tmpfwbuf, 0, tmpfwbufsz); /* Ensure 8-byte aligned firmware buffer */ fwbuf = (t_u8 *) ALIGN_ADDR(tmpfwbuf, DMA_ALIGNMENT); if (!pmadapter->init_para.fw_crc_check) { /* CRC check not required, use custom header first */ firmware = header_crc_fw; firmwarelen = header_crc_fw_len; crc_buffer = 1; } /* Perform firmware data transfer */ do { /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY bits */ ret = wlan_sdio_poll_card_status(pmadapter, CARD_IO_READY | DN_LD_CARD_RDY); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MFATAL, "WLAN: FW download with helper poll status timeout @ %d\n", offset); goto done; } if (!crc_buffer) /* More data? */ if (firmwarelen && offset >= firmwarelen) break; for (tries = 0; tries < MAX_POLL_TRIES; tries++) { ret = pcb->moal_read_reg(pmadapter->pmoal_handle, READ_BASE_0_REG, &base0); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MERROR, "Dev BASE0 register read failed:" " base0=0x%04X(%d). Terminating download\n", base0, base0); goto done; } ret = pcb->moal_read_reg(pmadapter->pmoal_handle, READ_BASE_1_REG, &base1); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MERROR, "Dev BASE1 register read failed:" " base1=0x%04X(%d). Terminating download\n", base1, base1); goto done; } len = (t_u16) (((base1 & 0xff) << 8) | (base0 & 0xff)); if (len) break; wlan_udelay(pmadapter, 10); } if (!len) break; else if (len > WLAN_UPLD_SIZE) { PRINTM(MFATAL, "WLAN: FW download failure @ %d, invalid length %d\n", offset, len); ret = MLAN_STATUS_FAILURE; goto done; } txlen = len; if (len & MBIT(0)) { i++; if (i > MAX_WRITE_IOMEM_RETRY) { PRINTM(MFATAL, "WLAN: FW download failure @ %d, over max retry count\n", offset); ret = MLAN_STATUS_FAILURE; goto done; } PRINTM(MERROR, "WLAN: FW CRC error indicated by the helper:" " len = 0x%04X, txlen = %d\n", len, txlen); len &= ~MBIT(0); PRINTM(MERROR, "WLAN: retry: %d, offset %d\n", i, offset); DBG_HEXDUMP(MERROR, "WLAN: FW block:", mbuf.pbuf, len); /* Setting this to 0 to resend from same offset */ txlen = 0; } else { i = 0; /* Set blocksize to transfer - checking for last block */ if (firmwarelen && firmwarelen - offset < txlen) { txlen = firmwarelen - offset; } PRINTM(MINFO, "."); tx_blocks = (txlen + MLAN_SDIO_BLOCK_SIZE_FW_DNLD - 1) / MLAN_SDIO_BLOCK_SIZE_FW_DNLD; /* Copy payload to buffer */ if (firmware) memmove(pmadapter, fwbuf, &firmware[offset], txlen); else pcb->moal_get_fw_data(pmadapter->pmoal_handle, offset, txlen, fwbuf); } /* Send data */ memset(pmadapter, &mbuf, 0, sizeof(mlan_buffer)); mbuf.pbuf = (t_u8 *) fwbuf; mbuf.data_len = tx_blocks * MLAN_SDIO_BLOCK_SIZE_FW_DNLD; ret = pcb->moal_write_data_sync(pmadapter->pmoal_handle, &mbuf, pmadapter->ioport, 0); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MERROR, "WLAN: FW download, write iomem (%d) failed @ %d\n", i, offset); if (pcb-> moal_write_reg(pmadapter->pmoal_handle, HOST_TO_CARD_EVENT_REG, HOST_TERM_CMD53) != MLAN_STATUS_SUCCESS) { PRINTM(MERROR, "write CFG reg failed\n"); } ret = MLAN_STATUS_FAILURE; goto done; } offset += txlen; if (crc_buffer && offset >= header_crc_fw_len) { /* Custom header download complete, restore original FW */ offset = 0; firmware = pmfw->pfw_buf; firmwarelen = pmfw->fw_len; crc_buffer = 0; } } while (MTRUE); PRINTM(MMSG, "Wlan: FW download over, firmwarelen=%d downloaded %d\n", firmwarelen, offset); ret = MLAN_STATUS_SUCCESS; done: if (tmpfwbuf) pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *) tmpfwbuf); LEAVE(); return ret; }