int wlcore_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) { wl1271_error("NVS file is needed during boot"); return -ENODEV; } if (wl->quirks & WLCORE_QUIRK_LEGACY_NVS) { 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; } else { 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; } /* 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 partition start address * to the destination */ dest_addr += wl->curr_part.reg.start; /* 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 = wlcore_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 = wlcore_set_partition(wl, &wl->ptable[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 = wlcore_write_data(wl, REG_CMD_MBOX_ADDRESS, nvs_aligned, nvs_len, false); kfree(nvs_aligned); return ret; out_badnvs: wl1271_error("nvs data is malformed"); return -EILSEQ; }
int wlcore_rx(struct wl1271 *wl, struct wl_fw_status_1 *status) { unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0}; u32 buf_size; u32 fw_rx_counter = status->fw_rx_counter % wl->num_rx_desc; u32 drv_rx_counter = wl->rx_counter % wl->num_rx_desc; u32 rx_counter; u32 pkt_len, align_pkt_len; u32 pkt_offset, des; u8 hlid; enum wl_rx_buf_align rx_align; int ret = 0; while (drv_rx_counter != fw_rx_counter) { buf_size = 0; rx_counter = drv_rx_counter; while (rx_counter != fw_rx_counter) { des = le32_to_cpu(status->rx_pkt_descs[rx_counter]); pkt_len = wlcore_rx_get_buf_size(wl, des); align_pkt_len = wlcore_rx_get_align_buf_size(wl, pkt_len); if (buf_size + align_pkt_len > wl->aggr_buf_size) break; buf_size += align_pkt_len; rx_counter++; rx_counter %= wl->num_rx_desc; } if (buf_size == 0) { wl1271_warning("received empty data"); break; } /* Read all available packets at once */ des = le32_to_cpu(status->rx_pkt_descs[drv_rx_counter]); ret = wlcore_hw_prepare_read(wl, des, buf_size); if (ret < 0) goto out; ret = wlcore_read_data(wl, REG_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) { des = le32_to_cpu(status->rx_pkt_descs[drv_rx_counter]); pkt_len = wlcore_rx_get_buf_size(wl, des); rx_align = wlcore_hw_get_rx_buf_align(wl, des); /* * 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_len, rx_align, &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 %= wl->num_rx_desc; pkt_offset += wlcore_rx_get_align_buf_size(wl, pkt_len); } } /* * Write the driver's packet counter to the FW. This is only required * for older hardware revisions */ if (wl->quirks & WLCORE_QUIRK_END_OF_TRANSACTION) { ret = wlcore_write32(wl, WL12XX_REG_RX_DRIVER_COUNTER, wl->rx_counter); if (ret < 0) goto out; } wl12xx_rearm_rx_streaming(wl, active_hlids); out: return ret; }