/**
 *  @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;
}
Exemplo n.º 2
0
/** 
 *  @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;
}