void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status)
{
	struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map;
	u32 buf_size;
	u32 fw_rx_counter  = status->fw_rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
	u32 drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
	u32 mem_block;

	while (drv_rx_counter != fw_rx_counter) {
		mem_block = wl1271_rx_get_mem_block(status, drv_rx_counter);
		buf_size = wl1271_rx_get_buf_size(status, drv_rx_counter);

		if (buf_size == 0) {
			wl1271_warning("received empty data");
			break;
		}

		wl->rx_mem_pool_addr.addr = (mem_block << 8) +
			le32_to_cpu(wl_mem_map->packet_memory_pool_start);
		wl->rx_mem_pool_addr.addr_extra =
			wl->rx_mem_pool_addr.addr + 4;

		/* Choose the block we want to read */
		wl1271_write(wl, WL1271_SLV_REG_DATA, &wl->rx_mem_pool_addr,
			     sizeof(wl->rx_mem_pool_addr), false);

		wl1271_rx_handle_data(wl, buf_size);

		wl->rx_counter++;
		drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
	}

	wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter);
}
/*
 * send command to firmware
 *
 * @wl: wl struct
 * @id: command id
 * @buf: buffer containing the command, must work with dma
 * @len: length of the buffer
 */
int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
                    size_t res_len)
{
    struct wl1271_cmd_header *cmd;
    unsigned long timeout;
    u32 intr;
    int ret = 0;
    u16 status;
    u16 poll_count = 0;

    cmd = buf;
    cmd->id = cpu_to_le16(id);
    cmd->status = 0;

    WARN_ON(len % 4 != 0);

    wl1271_write(wl, wl->cmd_box_addr, buf, len, false);

    wl1271_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_CMD);

    timeout = jiffies + msecs_to_jiffies(WL1271_COMMAND_TIMEOUT);

    intr = wl1271_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR);
    while (!(intr & WL1271_ACX_INTR_CMD_COMPLETE)) {
        if (time_after(jiffies, timeout)) {
            wl1271_error("command complete timeout");
            ret = -ETIMEDOUT;
            goto out;
        }

        poll_count++;
        if (poll_count < WL1271_CMD_FAST_POLL_COUNT)
            udelay(10);
        else
            msleep(1);

        intr = wl1271_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR);
    }

    /* read back the status code of the command */
    if (res_len == 0)
        res_len = sizeof(struct wl1271_cmd_header);
    wl1271_read(wl, wl->cmd_box_addr, cmd, res_len, false);

    status = le16_to_cpu(cmd->status);
    if (status != CMD_STATUS_SUCCESS) {
        wl1271_error("command execute failure %d", status);
        ieee80211_queue_work(wl->hw, &wl->recovery_work);
        ret = -EIO;
    }

    wl1271_write32(wl, ACX_REG_INTERRUPT_ACK,
                   WL1271_ACX_INTR_CMD_COMPLETE);

out:
    return ret;
}
Exemple #3
0
static void wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len)
{
    if (wl->chip.id != CHIP_ID_1283_PG20) {
        struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map;
        struct wl1271_rx_mem_pool_addr rx_mem_addr;

        /*
         * Choose the block we want to read
         * For aggregated packets, only the first memory block
         * should be retrieved. The FW takes care of the rest.
         */
        u32 mem_block = rx_desc & RX_MEM_BLOCK_MASK;

        rx_mem_addr.addr = (mem_block << 8) +
                           le32_to_cpu(wl_mem_map->packet_memory_pool_start);

        rx_mem_addr.addr_extra = rx_mem_addr.addr + 4;

        wl1271_write(wl, WL1271_SLV_REG_DATA,
                     &rx_mem_addr, sizeof(rx_mem_addr), false);
    }
}
void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status)
{
	struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map;
	u32 buf_size;
	u32 fw_rx_counter  = status->fw_rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
	u32 drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
	u32 rx_counter;
	u32 mem_block;
	u32 pkt_length;
	u32 pkt_offset;

	while (drv_rx_counter != fw_rx_counter) {
		buf_size = 0;
		rx_counter = drv_rx_counter;
		while (rx_counter != fw_rx_counter) {
			pkt_length = wl1271_rx_get_buf_size(status, rx_counter);
			if (buf_size + pkt_length > WL1271_AGGR_BUFFER_SIZE)
				break;
			buf_size += pkt_length;
			rx_counter++;
			rx_counter &= NUM_RX_PKT_DESC_MOD_MASK;
		}

		if (buf_size == 0) {
			wl1271_warning("received empty data");
			break;
		}

		/*
		 * Choose the block we want to read
		 * For aggregated packets, only the first memory block should
		 * be retrieved. The FW takes care of the rest.
		 */
		mem_block = wl1271_rx_get_mem_block(status, drv_rx_counter);
		wl->rx_mem_pool_addr.addr = (mem_block << 8) +
			le32_to_cpu(wl_mem_map->packet_memory_pool_start);
		wl->rx_mem_pool_addr.addr_extra =
			wl->rx_mem_pool_addr.addr + 4;
		wl1271_write(wl, WL1271_SLV_REG_DATA, &wl->rx_mem_pool_addr,
				sizeof(wl->rx_mem_pool_addr), false);

		/* Read all available packets at once */
		wl1271_read(wl, WL1271_SLV_MEM_DATA, wl->aggr_buf,
				buf_size, true);

		/* Split data into separate packets */
		pkt_offset = 0;
		while (pkt_offset < buf_size) {
			pkt_length = wl1271_rx_get_buf_size(status,
					drv_rx_counter);
			/*
			 * the handle data call can only fail in memory-outage
			 * conditions, in that case the received frame will just
			 * be dropped.
			 */
			wl1271_rx_handle_data(wl,
					      wl->aggr_buf + pkt_offset,
					      pkt_length);
			wl->rx_counter++;
			drv_rx_counter++;
			drv_rx_counter &= NUM_RX_PKT_DESC_MOD_MASK;
			pkt_offset += pkt_length;
		}
	}
	wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter);
}
Exemple #5
0
void wl12xx_rx(struct wl1271 *wl, struct wl12xx_fw_status *status)
{
	struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map;
	unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0};
	u32 buf_size;
	u32 fw_rx_counter  = status->fw_rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
	u32 drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
	u32 rx_counter;
	u32 mem_block;
	u32 pkt_length;
	u32 pkt_offset;
	u8 hlid;
	bool unaligned = false;

	while (drv_rx_counter != fw_rx_counter) {
		buf_size = 0;
		rx_counter = drv_rx_counter;
		while (rx_counter != fw_rx_counter) {
			pkt_length = wl12xx_rx_get_buf_size(status, rx_counter);
			if (buf_size + pkt_length > WL1271_AGGR_BUFFER_SIZE)
				break;
			buf_size += pkt_length;
			rx_counter++;
			rx_counter &= NUM_RX_PKT_DESC_MOD_MASK;
		}

		if (buf_size == 0) {
			wl1271_warning("received empty data");
			break;
		}

		if (wl->chip.id != CHIP_ID_1283_PG20) {
			/*
                                      
                                                         
                                                         
    */
			mem_block = wl12xx_rx_get_mem_block(status,
							    drv_rx_counter);

			wl->rx_mem_pool_addr.addr = (mem_block << 8) +
			   le32_to_cpu(wl_mem_map->packet_memory_pool_start);

			wl->rx_mem_pool_addr.addr_extra =
				wl->rx_mem_pool_addr.addr + 4;

			wl1271_write(wl, WL1271_SLV_REG_DATA,
				     &wl->rx_mem_pool_addr,
				     sizeof(wl->rx_mem_pool_addr), false);
		}

		/*                                    */
		wl1271_read(wl, WL1271_SLV_MEM_DATA, wl->aggr_buf,
				buf_size, true);

		/*                                  */
		pkt_offset = 0;
		while (pkt_offset < buf_size) {
			pkt_length = wl12xx_rx_get_buf_size(status,
					drv_rx_counter);

			unaligned = wl12xx_rx_get_unaligned(status,
					drv_rx_counter);

			/*
                                                         
                                                           
                 
    */
			if (wl1271_rx_handle_data(wl,
						  wl->aggr_buf + pkt_offset,
						  pkt_length, unaligned,
						  &hlid) == 1) {
				if (hlid < WL12XX_MAX_LINKS)
					__set_bit(hlid, active_hlids);
				else
					WARN(1,
					     "hlid exceeded WL12XX_MAX_LINKS "
					     "(%d)\n", hlid);
			}

			wl->rx_counter++;
			drv_rx_counter++;
			drv_rx_counter &= NUM_RX_PKT_DESC_MOD_MASK;
			pkt_offset += pkt_length;
		}
	}

	/*
                                                                      
                                
  */
	if (wl->quirks & WL12XX_QUIRK_END_OF_TRANSACTION)
		wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter);

	wl12xx_rearm_rx_streaming(wl, active_hlids);
}
Exemple #6
0
static int wl1271_boot_upload_nvs(struct wl1271 *wl)
{
    size_t nvs_len, burst_len;
    int i;
    u32 dest_addr, val;
    u8 *nvs_ptr, *nvs_aligned;
    int ret;

    if (wl->nvs == NULL)
        return -ENODEV;

    if (wl->chip.id == CHIP_ID_1283_PG20) {
        struct wl128x_nvs_file *nvs = (struct wl128x_nvs_file *)wl->nvs;

        if (wl->nvs_len == sizeof(struct wl128x_nvs_file)) {
            if (nvs->general_params.dual_mode_select)
                wl->enable_11a = true;
        } else {
            wl1271_error("nvs size is not as expected: %zu != %zu",
                         wl->nvs_len,
                         sizeof(struct wl128x_nvs_file));
            kfree(wl->nvs);
            wl->nvs = NULL;
            wl->nvs_len = 0;
            return -EILSEQ;
        }

        /* only the first part of the NVS needs to be uploaded */
        nvs_len = sizeof(nvs->nvs);
        nvs_ptr = (u8 *)nvs->nvs;

    } else {
        struct wl1271_nvs_file *nvs =
            (struct wl1271_nvs_file *)wl->nvs;
        /*
         * FIXME: the LEGACY NVS image support (NVS's missing the 5GHz
         * band configurations) can be removed when those NVS files stop
         * floating around.
         */
        if (wl->nvs_len == sizeof(struct wl1271_nvs_file) ||
                wl->nvs_len == WL1271_INI_LEGACY_NVS_FILE_SIZE) {
            if (nvs->general_params.dual_mode_select)
                wl->enable_11a = true;
        }

        if (wl->nvs_len != sizeof(struct wl1271_nvs_file) &&
                (wl->nvs_len != WL1271_INI_LEGACY_NVS_FILE_SIZE ||
                 wl->enable_11a)) {
            wl1271_error("nvs size is not as expected: %zu != %zu",
                         wl->nvs_len, sizeof(struct wl1271_nvs_file));
            kfree(wl->nvs);
            wl->nvs = NULL;
            wl->nvs_len = 0;
            return -EILSEQ;
        }

        /* only the first part of the NVS needs to be uploaded */
        nvs_len = sizeof(nvs->nvs);
        nvs_ptr = (u8 *) nvs->nvs;
    }

    /* update current MAC address to NVS */
    nvs_ptr[11] = wl->addresses[0].addr[0];
    nvs_ptr[10] = wl->addresses[0].addr[1];
    nvs_ptr[6] = wl->addresses[0].addr[2];
    nvs_ptr[5] = wl->addresses[0].addr[3];
    nvs_ptr[4] = wl->addresses[0].addr[4];
    nvs_ptr[3] = wl->addresses[0].addr[5];

    /*
     * Layout before the actual NVS tables:
     * 1 byte : burst length.
     * 2 bytes: destination address.
     * n bytes: data to burst copy.
     *
     * This is ended by a 0 length, then the NVS tables.
     */

    /* FIXME: Do we need to check here whether the LSB is 1? */
    while (nvs_ptr[0]) {
        burst_len = nvs_ptr[0];
        dest_addr = (nvs_ptr[1] & 0xfe) | ((u32)(nvs_ptr[2] << 8));

        /*
         * Due to our new wl1271_translate_reg_addr function,
         * we need to add the REGISTER_BASE to the destination
         */
        dest_addr += REGISTERS_BASE;

        /* We move our pointer to the data */
        nvs_ptr += 3;

        for (i = 0; i < burst_len; i++) {
            if (nvs_ptr + 3 >= (u8 *) wl->nvs + nvs_len)
                goto out_badnvs;

            val = (nvs_ptr[0] | (nvs_ptr[1] << 8)
                   | (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24));

            wl1271_debug(DEBUG_BOOT,
                         "nvs burst write 0x%x: 0x%x",
                         dest_addr, val);
            ret = wl1271_write32(wl, dest_addr, val);
            if (ret < 0)
                return ret;

            nvs_ptr += 4;
            dest_addr += 4;
        }

        if (nvs_ptr >= (u8 *) wl->nvs + nvs_len)
            goto out_badnvs;
    }

    /*
     * We've reached the first zero length, the first NVS table
     * is located at an aligned offset which is at least 7 bytes further.
     * NOTE: The wl->nvs->nvs element must be first, in order to
     * simplify the casting, we assume it is at the beginning of
     * the wl->nvs structure.
     */
    nvs_ptr = (u8 *)wl->nvs +
              ALIGN(nvs_ptr - (u8 *)wl->nvs + 7, 4);

    if (nvs_ptr >= (u8 *) wl->nvs + nvs_len)
        goto out_badnvs;

    nvs_len -= nvs_ptr - (u8 *)wl->nvs;

    /* Now we must set the partition correctly */
    ret = wl1271_set_partition(wl, &wl12xx_part_table[PART_WORK]);
    if (ret < 0)
        return ret;

    /* Copy the NVS tables to a new block to ensure alignment */
    nvs_aligned = kmemdup(nvs_ptr, nvs_len, GFP_KERNEL);
    if (!nvs_aligned)
        return -ENOMEM;

    /* And finally we upload the NVS tables */
    ret = wl1271_write(wl, CMD_MBOX_ADDRESS, nvs_aligned, nvs_len, false);
    if (ret < 0)
        return ret;

    kfree(nvs_aligned);
    return 0;

out_badnvs:
    wl1271_error("nvs data is malformed");
    return -EILSEQ;
}
Exemple #7
0
static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
        size_t fw_data_len, u32 dest)
{
    struct wl1271_partition_set partition;
    int addr, chunk_num, partition_limit;
    u8 *p, *chunk;
    int ret;

    /* whal_FwCtrl_LoadFwImageSm() */

    wl1271_debug(DEBUG_BOOT, "starting firmware upload");

    wl1271_debug(DEBUG_BOOT, "fw_data_len %zd chunk_size %d",
                 fw_data_len, CHUNK_SIZE);

    if ((fw_data_len % 4) != 0) {
        wl1271_error("firmware length not multiple of four");
        return -EIO;
    }

    chunk = kmalloc(CHUNK_SIZE, GFP_KERNEL);
    if (!chunk) {
        wl1271_error("allocation for firmware upload chunk failed");
        return -ENOMEM;
    }

    memcpy(&partition, &wl12xx_part_table[PART_DOWN], sizeof(partition));
    partition.mem.start = dest;
    ret = wl1271_set_partition(wl, &partition);
    if (ret < 0)
        goto out;

    /* 10.1 set partition limit and chunk num */
    chunk_num = 0;
    partition_limit = wl12xx_part_table[PART_DOWN].mem.size;

    while (chunk_num < fw_data_len / CHUNK_SIZE) {
        /* 10.2 update partition, if needed */
        addr = dest + (chunk_num + 2) * CHUNK_SIZE;
        if (addr > partition_limit) {
            addr = dest + chunk_num * CHUNK_SIZE;
            partition_limit = chunk_num * CHUNK_SIZE +
                              wl12xx_part_table[PART_DOWN].mem.size;
            partition.mem.start = addr;
            ret = wl1271_set_partition(wl, &partition);
            if (ret < 0)
                goto out;
        }

        /* 10.3 upload the chunk */
        addr = dest + chunk_num * CHUNK_SIZE;
        p = buf + chunk_num * CHUNK_SIZE;
        memcpy(chunk, p, CHUNK_SIZE);
        wl1271_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x",
                     p, addr);
        ret = wl1271_write(wl, addr, chunk, CHUNK_SIZE, false);
        if (ret < 0)
            goto out;

        chunk_num++;
    }

    /* 10.4 upload the last chunk */
    addr = dest + chunk_num * CHUNK_SIZE;
    p = buf + chunk_num * CHUNK_SIZE;
    memcpy(chunk, p, fw_data_len % CHUNK_SIZE);
    wl1271_debug(DEBUG_BOOT, "uploading fw last chunk (%zd B) 0x%p to 0x%x",
                 fw_data_len % CHUNK_SIZE, p, addr);
    ret = wl1271_write(wl, addr, chunk, fw_data_len % CHUNK_SIZE, false);

out:
    kfree(chunk);
    return ret;
}
Exemple #8
0
void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status)
{
	struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map;
	u32 buf_size;
	u32 fw_rx_counter  = status->fw_rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
	u32 drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
	u32 rx_counter;
	u32 mem_block;
	u32 pkt_length;
	u32 pkt_offset;
	bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
	bool had_data = false;
	bool unaligned = false;

	while (drv_rx_counter != fw_rx_counter) {
		buf_size = 0;
		rx_counter = drv_rx_counter;
		while (rx_counter != fw_rx_counter) {
			pkt_length = wl1271_rx_get_buf_size(status, rx_counter);
			if (buf_size + pkt_length > WL1271_AGGR_BUFFER_SIZE)
				break;
			buf_size += pkt_length;
			rx_counter++;
			rx_counter &= NUM_RX_PKT_DESC_MOD_MASK;
		}

		if (buf_size == 0) {
			wl1271_warning("received empty data");
			break;
		}

		if (wl->chip.id != CHIP_ID_1283_PG20) {
			/*
			 * Choose the block we want to read
			 * For aggregated packets, only the first memory block
			 * should be retrieved. The FW takes care of the rest.
			 */
			mem_block = wl1271_rx_get_mem_block(status,
							    drv_rx_counter);

			wl->rx_mem_pool_addr.addr = (mem_block << 8) +
			   le32_to_cpu(wl_mem_map->packet_memory_pool_start);

			wl->rx_mem_pool_addr.addr_extra =
				wl->rx_mem_pool_addr.addr + 4;

			wl1271_write(wl, WL1271_SLV_REG_DATA,
				     &wl->rx_mem_pool_addr,
				     sizeof(wl->rx_mem_pool_addr), false);
		}

		/* Read all available packets at once */
		wl1271_read(wl, WL1271_SLV_MEM_DATA, wl->aggr_buf,
				buf_size, true);

		/* Split data into separate packets */
		pkt_offset = 0;
		while (pkt_offset < buf_size) {
			pkt_length = wl1271_rx_get_buf_size(status,
					drv_rx_counter);

			unaligned = wl1271_rx_get_unaligned(status,
					drv_rx_counter);

			/*
			 * the handle data call can only fail in memory-outage
			 * conditions, in that case the received frame will just
			 * be dropped.
			 */
			if (wl1271_rx_handle_data(wl,
						  wl->aggr_buf + pkt_offset,
						  pkt_length, unaligned) == 1)
				had_data = true;

			wl->rx_counter++;
			drv_rx_counter++;
			drv_rx_counter &= NUM_RX_PKT_DESC_MOD_MASK;
			pkt_offset += pkt_length;
		}
	}

	/*
	 * Write the driver's packet counter to the FW. This is only required
	 * for older hardware revisions
	 */
	if (wl->quirks & WL12XX_QUIRK_END_OF_TRANSACTION)
		wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter);

	if (!is_ap && wl->conf.rx_streaming.interval && had_data &&
	    (wl->conf.rx_streaming.always ||
	     test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags))) {
		u32 timeout = wl->conf.rx_streaming.duration;

		/* restart rx streaming */
		if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags))
			ieee80211_queue_work(wl->hw,
					     &wl->rx_streaming_enable_work);

		mod_timer(&wl->rx_streaming_timer,
			  jiffies + msecs_to_jiffies(timeout));
	}
}
Exemple #9
0
static int tester(void *data)
{
	struct wl1271 *wl = data;
	struct sdio_func *func = wl_to_func(wl);
	struct device *pdev = &func->dev;
	int ret = 0;
	bool rx_started = 0;
	bool tx_started = 0;
	uint8_t *tx_buf, *rx_buf;
	int test_size = PAGE_SIZE;
	u32 addr = 0;
	struct wl1271_partition_set partition;

	/* We assume chip is powered up and firmware fetched */

	memcpy(&partition, &part_down, sizeof(partition));
	partition.mem.start = addr;
	wl1271_set_partition(wl, &partition);

	tx_buf = kmalloc(test_size, GFP_KERNEL);
	rx_buf = kmalloc(test_size, GFP_KERNEL);
	if (!tx_buf || !rx_buf) {
		dev_err(pdev,
			"Could not allocate memory. Test will not run.\n");
		ret = -ENOMEM;
		goto free;
	}

	memset(tx_buf, 0x5a, test_size);

	/* write something in data area so we can read it back */
	wl1271_write(wl, addr, tx_buf, test_size, false);

	while (!kthread_should_stop()) {
		if (rx && !rx_started) {
			dev_info(pdev, "starting rx test\n");
			rx_started = 1;
		} else if (!rx && rx_started) {
			dev_info(pdev, "stopping rx test\n");
			rx_started = 0;
		}

		if (tx && !tx_started) {
			dev_info(pdev, "starting tx test\n");
			tx_started = 1;
		} else if (!tx && tx_started) {
			dev_info(pdev, "stopping tx test\n");
			tx_started = 0;
		}

		if (rx_started)
			wl1271_read(wl, addr, rx_buf, test_size, false);

		if (tx_started)
			wl1271_write(wl, addr, tx_buf, test_size, false);

		if (!rx_started && !tx_started)
			msleep(100);
	}

free:
	kfree(tx_buf);
	kfree(rx_buf);
	return ret;
}
Exemple #10
0
int wl12xx_rx(struct wl1271 *wl, struct wl12xx_fw_status *status)
{
	struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map;
	unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0};
	u32 buf_size;
	u32 fw_rx_counter  = status->fw_rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
	u32 drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
	u32 rx_counter;
	u32 mem_block;
	u32 pkt_length;
	u32 pkt_offset;
	u8 hlid;
	bool unaligned = false;
	int ret = 0;

	while (drv_rx_counter != fw_rx_counter) {
		buf_size = 0;
		rx_counter = drv_rx_counter;
		while (rx_counter != fw_rx_counter) {
			pkt_length = wl12xx_rx_get_buf_size(status, rx_counter);
			if (buf_size + pkt_length > WL1271_AGGR_BUFFER_SIZE)
				break;
			buf_size += pkt_length;
			rx_counter++;
			rx_counter &= NUM_RX_PKT_DESC_MOD_MASK;
		}

		if (buf_size == 0) {
			wl1271_warning("received empty data");
			break;
		}

		if (wl->chip.id != CHIP_ID_1283_PG20) {
			/*
			 * Choose the block we want to read
			 * For aggregated packets, only the first memory block
			 * should be retrieved. The FW takes care of the rest.
			 */
			mem_block = wl12xx_rx_get_mem_block(status,
							    drv_rx_counter);

			wl->rx_mem_pool_addr->addr = (mem_block << 8) +
			   le32_to_cpu(wl_mem_map->packet_memory_pool_start);

			wl->rx_mem_pool_addr->addr_extra =
				wl->rx_mem_pool_addr->addr + 4;

			ret = wl1271_write(wl, WL1271_SLV_REG_DATA,
					   wl->rx_mem_pool_addr,
					   sizeof(*wl->rx_mem_pool_addr),
					   false);
			if (ret < 0)
				goto out;
		}

		/* Read all available packets at once */
		ret = wl1271_read(wl, WL1271_SLV_MEM_DATA, wl->aggr_buf,
				  buf_size, true);
		if (ret < 0)
			goto out;

		/* Split data into separate packets */
		pkt_offset = 0;
		while (pkt_offset < buf_size) {
			pkt_length = wl12xx_rx_get_buf_size(status,
					drv_rx_counter);

			unaligned = wl12xx_rx_get_unaligned(status,
					drv_rx_counter);

			/*
			 * the handle data call can only fail in memory-outage
			 * conditions, in that case the received frame will just
			 * be dropped.
			 */
			if (wl1271_rx_handle_data(wl,
						  wl->aggr_buf + pkt_offset,
						  pkt_length, unaligned,
						  &hlid) == 1) {
				if (hlid < WL12XX_MAX_LINKS)
					__set_bit(hlid, active_hlids);
				else
					WARN(1,
					     "hlid exceeded WL12XX_MAX_LINKS "
					     "(%d)\n", hlid);
			}

			wl->rx_counter++;
			drv_rx_counter++;
			drv_rx_counter &= NUM_RX_PKT_DESC_MOD_MASK;
			pkt_offset += pkt_length;
		}
	}

	/*
	 * Write the driver's packet counter to the FW. This is only required
	 * for older hardware revisions
	 */
	if (wl->quirks & WL12XX_QUIRK_END_OF_TRANSACTION) {
		ret = wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS,
				     wl->rx_counter);
		if (ret < 0)
			goto out;
	}

	wl12xx_rearm_rx_streaming(wl, active_hlids);

out:
	return ret;
}
static int wl1271_boot_upload_nvs(struct wl1271 *wl)
{
	size_t nvs_len, burst_len;
	int i;
	u32 dest_addr, val;
	u8 *nvs_ptr, *nvs_aligned;

	if (wl->nvs == NULL)
		return -ENODEV;

	/* only the first part of the NVS needs to be uploaded */
	nvs_len = sizeof(wl->nvs->nvs);
	nvs_ptr = (u8 *)wl->nvs->nvs;

	/*
	 * Layout before the actual NVS tables:
	 * 1 byte : burst length.
	 * 2 bytes: destination address.
	 * n bytes: data to burst copy.
	 *
	 * This is ended by a 0 length, then the NVS tables.
	 */

	/* FIXME: Do we need to check here whether the LSB is 1? */
	while (nvs_ptr[0]) {
		burst_len = nvs_ptr[0];
		dest_addr = (nvs_ptr[1] & 0xfe) | ((u32)(nvs_ptr[2] << 8));

		/* FIXME: Due to our new wl1271_translate_reg_addr function,
		   we need to add the REGISTER_BASE to the destination */
		dest_addr += REGISTERS_BASE;

		/* We move our pointer to the data */
		nvs_ptr += 3;

		for (i = 0; i < burst_len; i++) {
			val = (nvs_ptr[0] | (nvs_ptr[1] << 8)
			       | (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24));

			wl1271_debug(DEBUG_BOOT,
				     "nvs burst write 0x%x: 0x%x",
				     dest_addr, val);
			wl1271_write32(wl, dest_addr, val);

			nvs_ptr += 4;
			dest_addr += 4;
		}
	}

	/*
	 * We've reached the first zero length, the first NVS table
	 * is 7 bytes further.
	 */
	nvs_ptr += 7;
	nvs_len -= nvs_ptr - (u8 *)wl->nvs->nvs;
	nvs_len = ALIGN(nvs_len, 4);

	/* FIXME: The driver sets the partition here, but this is not needed,
	   since it sets to the same one as currently in use */
	/* Now we must set the partition correctly */
	wl1271_set_partition(wl, &part_table[PART_WORK]);

	/* Copy the NVS tables to a new block to ensure alignment */
	/* FIXME: We jump 3 more bytes before uploading the NVS.  It seems
	that our NVS files have three extra zeros here.  I'm not sure whether
	the problem is in our NVS generation or we should really jumpt these
	3 bytes here */
	nvs_ptr += 3;

	nvs_aligned = kmemdup(nvs_ptr, nvs_len, GFP_KERNEL); if
	(!nvs_aligned) return -ENOMEM;

	/* And finally we upload the NVS tables */
	/* FIXME: In wl1271, we upload everything at once.
	   No endianness handling needed here?! The ref driver doesn't do
	   anything about it at this point */
	wl1271_write(wl, CMD_MBOX_ADDRESS, nvs_aligned, nvs_len, false);

	kfree(nvs_aligned);
	return 0;
}