diff options
Diffstat (limited to 'drivers/net/wireless/wl12xx')
37 files changed, 3773 insertions, 1625 deletions
diff --git a/drivers/net/wireless/wl12xx/Kconfig b/drivers/net/wireless/wl12xx/Kconfig index 88060e117541..785e0244e305 100644 --- a/drivers/net/wireless/wl12xx/Kconfig +++ b/drivers/net/wireless/wl12xx/Kconfig @@ -1,6 +1,6 @@ menuconfig WL12XX tristate "TI wl12xx driver support" - depends on MAC80211 && WLAN_80211 && EXPERIMENTAL + depends on MAC80211 && EXPERIMENTAL ---help--- This will enable TI wl12xx driver support. The drivers make use of the mac80211 stack. @@ -42,6 +42,7 @@ config WL1251_SDIO config WL1271 tristate "TI wl1271 support" depends on WL12XX && SPI_MASTER && GENERIC_HARDIRQS + depends on INET select FW_LOADER select CRC7 ---help--- diff --git a/drivers/net/wireless/wl12xx/wl1251.h b/drivers/net/wireless/wl12xx/wl1251.h index 998e4b6252bd..054533f7a124 100644 --- a/drivers/net/wireless/wl12xx/wl1251.h +++ b/drivers/net/wireless/wl12xx/wl1251.h @@ -269,6 +269,7 @@ struct wl1251 { void (*set_power)(bool enable); int irq; + bool use_eeprom; enum wl1251_state state; struct mutex mutex; @@ -354,6 +355,8 @@ struct wl1251 { /* is firmware in elp mode */ bool elp; + struct delayed_work elp_work; + /* we can be in psm, but not in elp, we have to differentiate */ bool psm; @@ -374,6 +377,8 @@ struct wl1251 { u8 buffer_busyword[WL1251_BUSY_WORD_LEN]; struct wl1251_rx_descriptor *rx_descriptor; + struct ieee80211_vif *vif; + u32 chip_id; char fw_ver[21]; }; diff --git a/drivers/net/wireless/wl12xx/wl1251_acx.c b/drivers/net/wireless/wl12xx/wl1251_acx.c index 10b26c4532c9..acfa086dbfc5 100644 --- a/drivers/net/wireless/wl12xx/wl1251_acx.c +++ b/drivers/net/wireless/wl12xx/wl1251_acx.c @@ -494,7 +494,7 @@ out: return ret; } -int wl1251_acx_beacon_filter_opt(struct wl1251 *wl) +int wl1251_acx_beacon_filter_opt(struct wl1251 *wl, bool enable_filter) { struct acx_beacon_filter_option *beacon_filter; int ret; @@ -507,7 +507,7 @@ int wl1251_acx_beacon_filter_opt(struct wl1251 *wl) goto out; } - beacon_filter->enable = 0; + beacon_filter->enable = enable_filter; beacon_filter->max_num_beacons = 0; ret = wl1251_cmd_configure(wl, ACX_BEACON_FILTER_OPT, @@ -525,6 +525,7 @@ out: int wl1251_acx_beacon_filter_table(struct wl1251 *wl) { struct acx_beacon_filter_ie_table *ie_table; + int idx = 0; int ret; wl1251_debug(DEBUG_ACX, "acx beacon filter table"); @@ -535,8 +536,10 @@ int wl1251_acx_beacon_filter_table(struct wl1251 *wl) goto out; } - ie_table->num_ie = 0; - memset(ie_table->table, 0, BEACON_FILTER_TABLE_MAX_SIZE); + /* configure default beacon pass-through rules */ + ie_table->num_ie = 1; + ie_table->table[idx++] = BEACON_FILTER_IE_ID_CHANNEL_SWITCH_ANN; + ie_table->table[idx++] = BEACON_RULE_PASS_ON_APPEARANCE; ret = wl1251_cmd_configure(wl, ACX_BEACON_FILTER_TABLE, ie_table, sizeof(*ie_table)); @@ -550,6 +553,35 @@ out: return ret; } +int wl1251_acx_conn_monit_params(struct wl1251 *wl) +{ + struct acx_conn_monit_params *acx; + int ret; + + wl1251_debug(DEBUG_ACX, "acx connection monitor parameters"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->synch_fail_thold = SYNCH_FAIL_DEFAULT_THRESHOLD; + acx->bss_lose_timeout = NO_BEACON_DEFAULT_TIMEOUT; + + ret = wl1251_cmd_configure(wl, ACX_CONN_MONIT_PARAMS, + acx, sizeof(*acx)); + if (ret < 0) { + wl1251_warning("failed to set connection monitor " + "parameters: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + int wl1251_acx_sg_enable(struct wl1251 *wl) { struct acx_bt_wlan_coex *pta; @@ -916,3 +948,31 @@ out: kfree(mem_conf); return ret; } + +int wl1251_acx_wr_tbtt_and_dtim(struct wl1251 *wl, u16 tbtt, u8 dtim) +{ + struct wl1251_acx_wr_tbtt_and_dtim *acx; + int ret; + + wl1251_debug(DEBUG_ACX, "acx tbtt and dtim"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->tbtt = tbtt; + acx->dtim = dtim; + + ret = wl1251_cmd_configure(wl, ACX_WR_TBTT_AND_DTIM, + acx, sizeof(*acx)); + if (ret < 0) { + wl1251_warning("failed to set tbtt and dtim: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} diff --git a/drivers/net/wireless/wl12xx/wl1251_acx.h b/drivers/net/wireless/wl12xx/wl1251_acx.h index cafb91459504..652371432cd8 100644 --- a/drivers/net/wireless/wl12xx/wl1251_acx.h +++ b/drivers/net/wireless/wl12xx/wl1251_acx.h @@ -450,6 +450,11 @@ struct acx_beacon_filter_option { (BEACON_FILTER_TABLE_MAX_VENDOR_SPECIFIC_IE_NUM * \ BEACON_FILTER_TABLE_EXTRA_VENDOR_SPECIFIC_IE_SIZE)) +#define BEACON_RULE_PASS_ON_CHANGE BIT(0) +#define BEACON_RULE_PASS_ON_APPEARANCE BIT(1) + +#define BEACON_FILTER_IE_ID_CHANNEL_SWITCH_ANN (37) + struct acx_beacon_filter_ie_table { struct acx_header header; @@ -458,6 +463,16 @@ struct acx_beacon_filter_ie_table { u8 pad[3]; } __attribute__ ((packed)); +#define SYNCH_FAIL_DEFAULT_THRESHOLD 10 /* number of beacons */ +#define NO_BEACON_DEFAULT_TIMEOUT (500) /* in microseconds */ + +struct acx_conn_monit_params { + struct acx_header header; + + u32 synch_fail_thold; /* number of beacons missed */ + u32 bss_lose_timeout; /* number of TU's from synch fail */ +}; + enum { SG_ENABLE = 0, SG_DISABLE, @@ -1134,6 +1149,23 @@ struct wl1251_acx_mem_map { u32 num_rx_mem_blocks; } __attribute__ ((packed)); + +struct wl1251_acx_wr_tbtt_and_dtim { + + struct acx_header header; + + /* Time in TUs between two consecutive beacons */ + u16 tbtt; + + /* + * DTIM period + * For BSS: Number of TBTTs in a DTIM period (range: 1-10) + * For IBSS: value shall be set to 1 + */ + u8 dtim; + u8 padding; +} __attribute__ ((packed)); + /************************************************************************* Host Interrupt Register (WiLink -> Host) @@ -1273,8 +1305,9 @@ int wl1251_acx_slot(struct wl1251 *wl, enum acx_slot_type slot_time); int wl1251_acx_group_address_tbl(struct wl1251 *wl); int wl1251_acx_service_period_timeout(struct wl1251 *wl); int wl1251_acx_rts_threshold(struct wl1251 *wl, u16 rts_threshold); -int wl1251_acx_beacon_filter_opt(struct wl1251 *wl); +int wl1251_acx_beacon_filter_opt(struct wl1251 *wl, bool enable_filter); int wl1251_acx_beacon_filter_table(struct wl1251 *wl); +int wl1251_acx_conn_monit_params(struct wl1251 *wl); int wl1251_acx_sg_enable(struct wl1251 *wl); int wl1251_acx_sg_cfg(struct wl1251 *wl); int wl1251_acx_cca_threshold(struct wl1251 *wl); @@ -1288,5 +1321,6 @@ int wl1251_acx_statistics(struct wl1251 *wl, struct acx_statistics *stats); int wl1251_acx_tsf_info(struct wl1251 *wl, u64 *mactime); int wl1251_acx_rate_policies(struct wl1251 *wl); int wl1251_acx_mem_cfg(struct wl1251 *wl); +int wl1251_acx_wr_tbtt_and_dtim(struct wl1251 *wl, u16 tbtt, u8 dtim); #endif /* __WL1251_ACX_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1251_boot.c b/drivers/net/wireless/wl12xx/wl1251_boot.c index 452d748e42c6..2e733e7bdfd4 100644 --- a/drivers/net/wireless/wl12xx/wl1251_boot.c +++ b/drivers/net/wireless/wl12xx/wl1251_boot.c @@ -296,8 +296,12 @@ int wl1251_boot_run_firmware(struct wl1251 *wl) WL1251_ACX_INTR_INIT_COMPLETE; wl1251_boot_target_enable_interrupts(wl); - /* unmask all mbox events */ - wl->event_mask = 0xffffffff; + wl->event_mask = SCAN_COMPLETE_EVENT_ID | BSS_LOSE_EVENT_ID | + SYNCHRONIZATION_TIMEOUT_EVENT_ID | + ROAMING_TRIGGER_LOW_RSSI_EVENT_ID | + ROAMING_TRIGGER_REGAINED_RSSI_EVENT_ID | + REGAINED_BSS_EVENT_ID | BT_PTA_SENSE_EVENT_ID | + BT_PTA_PREDICTION_EVENT_ID; ret = wl1251_event_unmask(wl); if (ret < 0) { @@ -314,8 +318,8 @@ int wl1251_boot_run_firmware(struct wl1251 *wl) static int wl1251_boot_upload_firmware(struct wl1251 *wl) { int addr, chunk_num, partition_limit; - size_t fw_data_len; - u8 *p; + size_t fw_data_len, len; + u8 *p, *buf; /* whal_FwCtrl_LoadFwImageSm() */ @@ -334,6 +338,12 @@ static int wl1251_boot_upload_firmware(struct wl1251 *wl) return -EIO; } + buf = kmalloc(CHUNK_SIZE, GFP_KERNEL); + if (!buf) { + wl1251_error("allocation for firmware upload chunk failed"); + return -ENOMEM; + } + wl1251_set_partition(wl, WL1251_PART_DOWN_MEM_START, WL1251_PART_DOWN_MEM_SIZE, WL1251_PART_DOWN_REG_START, @@ -364,7 +374,11 @@ static int wl1251_boot_upload_firmware(struct wl1251 *wl) p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE; wl1251_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x", p, addr); - wl1251_mem_write(wl, addr, p, CHUNK_SIZE); + + /* need to copy the chunk for dma */ + len = CHUNK_SIZE; + memcpy(buf, p, len); + wl1251_mem_write(wl, addr, buf, len); chunk_num++; } @@ -372,9 +386,16 @@ static int wl1251_boot_upload_firmware(struct wl1251 *wl) /* 10.4 upload the last chunk */ addr = WL1251_PART_DOWN_MEM_START + chunk_num * CHUNK_SIZE; p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE; + + /* need to copy the chunk for dma */ + len = fw_data_len % CHUNK_SIZE; + memcpy(buf, p, len); + wl1251_debug(DEBUG_BOOT, "uploading fw last chunk (%zu B) 0x%p to 0x%x", - fw_data_len % CHUNK_SIZE, p, addr); - wl1251_mem_write(wl, addr, p, fw_data_len % CHUNK_SIZE); + len, p, addr); + wl1251_mem_write(wl, addr, buf, len); + + kfree(buf); return 0; } @@ -473,13 +494,19 @@ int wl1251_boot(struct wl1251 *wl) goto out; /* 2. start processing NVS file */ - ret = wl1251_boot_upload_nvs(wl); - if (ret < 0) - goto out; - - /* write firmware's last address (ie. it's length) to - * ACX_EEPROMLESS_IND_REG */ - wl1251_reg_write32(wl, ACX_EEPROMLESS_IND_REG, wl->fw_len); + if (wl->use_eeprom) { + wl1251_reg_write32(wl, ACX_REG_EE_START, START_EEPROM_MGR); + msleep(4000); + wl1251_reg_write32(wl, ACX_EEPROMLESS_IND_REG, USE_EEPROM); + } else { + ret = wl1251_boot_upload_nvs(wl); + if (ret < 0) + goto out; + + /* write firmware's last address (ie. it's length) to + * ACX_EEPROMLESS_IND_REG */ + wl1251_reg_write32(wl, ACX_EEPROMLESS_IND_REG, wl->fw_len); + } /* 6. read the EEPROM parameters */ tmp = wl1251_reg_read32(wl, SCR_PAD2); diff --git a/drivers/net/wireless/wl12xx/wl1251_event.c b/drivers/net/wireless/wl12xx/wl1251_event.c index 00076c4a8a21..020d764f9c13 100644 --- a/drivers/net/wireless/wl12xx/wl1251_event.c +++ b/drivers/net/wireless/wl12xx/wl1251_event.c @@ -79,6 +79,21 @@ static int wl1251_event_process(struct wl1251 *wl, struct event_mailbox *mbox) } } + if (vector & SYNCHRONIZATION_TIMEOUT_EVENT_ID && wl->psm) { + wl1251_debug(DEBUG_EVENT, "SYNCHRONIZATION_TIMEOUT_EVENT"); + + /* indicate to the stack, that beacons have been lost */ + ieee80211_beacon_loss(wl->vif); + } + + if (vector & REGAINED_BSS_EVENT_ID) { + if (wl->psm_requested) { + ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE); + if (ret < 0) + return ret; + } + } + return 0; } diff --git a/drivers/net/wireless/wl12xx/wl1251_init.c b/drivers/net/wireless/wl12xx/wl1251_init.c index b2ee4f468fc4..5cb573383eeb 100644 --- a/drivers/net/wireless/wl12xx/wl1251_init.c +++ b/drivers/net/wireless/wl12xx/wl1251_init.c @@ -147,7 +147,8 @@ int wl1251_hw_init_beacon_filter(struct wl1251 *wl) { int ret; - ret = wl1251_acx_beacon_filter_opt(wl); + /* disable beacon filtering at this stage */ + ret = wl1251_acx_beacon_filter_opt(wl, false); if (ret < 0) return ret; @@ -364,6 +365,11 @@ int wl1251_hw_init(struct wl1251 *wl) if (ret < 0) goto out_free_data_path; + /* Initialize connection monitoring thresholds */ + ret = wl1251_acx_conn_monit_params(wl); + if (ret < 0) + goto out_free_data_path; + /* Beacon filtering */ ret = wl1251_hw_init_beacon_filter(wl); if (ret < 0) diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c index 1103256ad989..ff4be7bf5d36 100644 --- a/drivers/net/wireless/wl12xx/wl1251_main.c +++ b/drivers/net/wireless/wl12xx/wl1251_main.c @@ -28,6 +28,7 @@ #include <linux/irq.h> #include <linux/crc32.h> #include <linux/etherdevice.h> +#include <linux/vmalloc.h> #include "wl1251.h" #include "wl12xx_80211.h" @@ -83,7 +84,7 @@ static int wl1251_fetch_firmware(struct wl1251 *wl) } wl->fw_len = fw->size; - wl->fw = kmalloc(wl->fw_len, GFP_KERNEL); + wl->fw = vmalloc(wl->fw_len); if (!wl->fw) { wl1251_error("could not allocate memory for the firmware"); @@ -183,8 +184,11 @@ static int wl1251_chip_wakeup(struct wl1251 *wl) wl1251_debug(DEBUG_BOOT, "chip id 0x%x (1251 PG12)", wl->chip_id); break; - case CHIP_ID_1251_PG10: case CHIP_ID_1251_PG11: + wl1251_debug(DEBUG_BOOT, "chip id 0x%x (1251 PG11)", + wl->chip_id); + break; + case CHIP_ID_1251_PG10: default: wl1251_error("unsupported chip id: 0x%x", wl->chip_id); ret = -ENODEV; @@ -208,9 +212,10 @@ out: return ret; } +#define WL1251_IRQ_LOOP_COUNT 10 static void wl1251_irq_work(struct work_struct *work) { - u32 intr; + u32 intr, ctr = WL1251_IRQ_LOOP_COUNT; struct wl1251 *wl = container_of(work, struct wl1251, irq_work); int ret; @@ -231,78 +236,86 @@ static void wl1251_irq_work(struct work_struct *work) intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR); wl1251_debug(DEBUG_IRQ, "intr: 0x%x", intr); - if (wl->data_path) { - wl->rx_counter = - wl1251_mem_read32(wl, wl->data_path->rx_control_addr); - - /* We handle a frmware bug here */ - switch ((wl->rx_counter - wl->rx_handled) & 0xf) { - case 0: - wl1251_debug(DEBUG_IRQ, "RX: FW and host in sync"); - intr &= ~WL1251_ACX_INTR_RX0_DATA; - intr &= ~WL1251_ACX_INTR_RX1_DATA; - break; - case 1: - wl1251_debug(DEBUG_IRQ, "RX: FW +1"); - intr |= WL1251_ACX_INTR_RX0_DATA; - intr &= ~WL1251_ACX_INTR_RX1_DATA; - break; - case 2: - wl1251_debug(DEBUG_IRQ, "RX: FW +2"); - intr |= WL1251_ACX_INTR_RX0_DATA; - intr |= WL1251_ACX_INTR_RX1_DATA; - break; - default: - wl1251_warning("RX: FW and host out of sync: %d", - wl->rx_counter - wl->rx_handled); - break; - } - - wl->rx_handled = wl->rx_counter; + do { + if (wl->data_path) { + wl->rx_counter = wl1251_mem_read32( + wl, wl->data_path->rx_control_addr); + + /* We handle a frmware bug here */ + switch ((wl->rx_counter - wl->rx_handled) & 0xf) { + case 0: + wl1251_debug(DEBUG_IRQ, + "RX: FW and host in sync"); + intr &= ~WL1251_ACX_INTR_RX0_DATA; + intr &= ~WL1251_ACX_INTR_RX1_DATA; + break; + case 1: + wl1251_debug(DEBUG_IRQ, "RX: FW +1"); + intr |= WL1251_ACX_INTR_RX0_DATA; + intr &= ~WL1251_ACX_INTR_RX1_DATA; + break; + case 2: + wl1251_debug(DEBUG_IRQ, "RX: FW +2"); + intr |= WL1251_ACX_INTR_RX0_DATA; + intr |= WL1251_ACX_INTR_RX1_DATA; + break; + default: + wl1251_warning( + "RX: FW and host out of sync: %d", + wl->rx_counter - wl->rx_handled); + break; + } + wl->rx_handled = wl->rx_counter; - wl1251_debug(DEBUG_IRQ, "RX counter: %d", wl->rx_counter); - } + wl1251_debug(DEBUG_IRQ, "RX counter: %d", + wl->rx_counter); + } - intr &= wl->intr_mask; + intr &= wl->intr_mask; - if (intr == 0) { - wl1251_debug(DEBUG_IRQ, "INTR is 0"); - wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, - ~(wl->intr_mask)); + if (intr == 0) { + wl1251_debug(DEBUG_IRQ, "INTR is 0"); + goto out_sleep; + } - goto out_sleep; - } + if (intr & WL1251_ACX_INTR_RX0_DATA) { + wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX0_DATA"); + wl1251_rx(wl); + } - if (intr & WL1251_ACX_INTR_RX0_DATA) { - wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX0_DATA"); - wl1251_rx(wl); - } + if (intr & WL1251_ACX_INTR_RX1_DATA) { + wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX1_DATA"); + wl1251_rx(wl); + } - if (intr & WL1251_ACX_INTR_RX1_DATA) { - wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX1_DATA"); - wl1251_rx(wl); - } + if (intr & WL1251_ACX_INTR_TX_RESULT) { + wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT"); + wl1251_tx_complete(wl); + } - if (intr & WL1251_ACX_INTR_TX_RESULT) { - wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT"); - wl1251_tx_complete(wl); - } + if (intr & (WL1251_ACX_INTR_EVENT_A | + WL1251_ACX_INTR_EVENT_B)) { + wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT (0x%x)", + intr); + if (intr & WL1251_ACX_INTR_EVENT_A) + wl1251_event_handle(wl, 0); + else + wl1251_event_handle(wl, 1); + } - if (intr & (WL1251_ACX_INTR_EVENT_A | WL1251_ACX_INTR_EVENT_B)) { - wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT (0x%x)", intr); - if (intr & WL1251_ACX_INTR_EVENT_A) - wl1251_event_handle(wl, 0); - else - wl1251_event_handle(wl, 1); - } + if (intr & WL1251_ACX_INTR_INIT_COMPLETE) + wl1251_debug(DEBUG_IRQ, + "WL1251_ACX_INTR_INIT_COMPLETE"); - if (intr & WL1251_ACX_INTR_INIT_COMPLETE) - wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_INIT_COMPLETE"); + if (--ctr == 0) + break; - wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, ~(wl->intr_mask)); + intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR); + } while (intr); out_sleep: + wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, ~(wl->intr_mask)); wl1251_ps_elp_sleep(wl); out: @@ -506,6 +519,12 @@ static int wl1251_op_add_interface(struct ieee80211_hw *hw, conf->type, conf->mac_addr); mutex_lock(&wl->mutex); + if (wl->vif) { + ret = -EBUSY; + goto out; + } + + wl->vif = conf->vif; switch (conf->type) { case NL80211_IFTYPE_STATION: @@ -535,7 +554,12 @@ out: static void wl1251_op_remove_interface(struct ieee80211_hw *hw, struct ieee80211_if_init_conf *conf) { + struct wl1251 *wl = hw->priv; + + mutex_lock(&wl->mutex); wl1251_debug(DEBUG_MAC80211, "mac80211 remove interface"); + wl->vif = NULL; + mutex_unlock(&wl->mutex); } static int wl1251_build_null_data(struct wl1251 *wl) @@ -552,7 +576,8 @@ static int wl1251_build_null_data(struct wl1251 *wl) memcpy(template.header.sa, wl->mac_addr, ETH_ALEN); template.header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_DATA | - IEEE80211_STYPE_NULLFUNC); + IEEE80211_STYPE_NULLFUNC | + IEEE80211_FCTL_TODS); return wl1251_cmd_template_set(wl, CMD_NULL_DATA, &template, sizeof(template)); @@ -565,7 +590,10 @@ static int wl1251_build_ps_poll(struct wl1251 *wl, u16 aid) memcpy(template.bssid, wl->bssid, ETH_ALEN); memcpy(template.ta, wl->mac_addr, ETH_ALEN); - template.aid = aid; + + /* aid in PS-Poll has its two MSBs each set to 1 */ + template.aid = cpu_to_le16(1 << 15 | 1 << 14 | aid); + template.fc = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); return wl1251_cmd_template_set(wl, CMD_PS_POLL, &template, @@ -1087,8 +1115,8 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, wl->beacon_int = bss_conf->beacon_int; wl->dtim_period = bss_conf->dtim_period; - /* FIXME: call join */ - + ret = wl1251_acx_wr_tbtt_and_dtim(wl, wl->beacon_int, + wl->dtim_period); wl->aid = bss_conf->aid; ret = wl1251_build_ps_poll(wl, wl->aid); @@ -1308,7 +1336,9 @@ int wl1251_init_ieee80211(struct wl1251 *wl) wl->hw->channel_change_time = 10000; wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | - IEEE80211_HW_NOISE_DBM; + IEEE80211_HW_NOISE_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_BEACON_FILTER; wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); wl->hw->wiphy->max_scan_ssids = 1; @@ -1351,6 +1381,7 @@ struct ieee80211_hw *wl1251_alloc_hw(void) skb_queue_head_init(&wl->tx_queue); INIT_WORK(&wl->filter_work, wl1251_filter_work); + INIT_DELAYED_WORK(&wl->elp_work, wl1251_elp_work); wl->channel = WL1251_DEFAULT_CHANNEL; wl->scanning = false; wl->default_key = 0; @@ -1368,6 +1399,7 @@ struct ieee80211_hw *wl1251_alloc_hw(void) wl->power_level = WL1251_DEFAULT_POWER_LEVEL; wl->beacon_int = WL1251_DEFAULT_BEACON_INT; wl->dtim_period = WL1251_DEFAULT_DTIM_PERIOD; + wl->vif = NULL; for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) wl->tx_frames[i] = NULL; @@ -1409,7 +1441,7 @@ int wl1251_free_hw(struct wl1251 *wl) kfree(wl->target_mem_map); kfree(wl->data_path); - kfree(wl->fw); + vfree(wl->fw); wl->fw = NULL; kfree(wl->nvs); wl->nvs = NULL; @@ -1426,4 +1458,5 @@ EXPORT_SYMBOL_GPL(wl1251_free_hw); MODULE_DESCRIPTION("TI wl1251 Wireles LAN Driver Core"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kalle Valo <kalle.valo@nokia.com>"); -MODULE_ALIAS("spi:wl12xx"); +MODULE_ALIAS("spi:wl1251"); +MODULE_FIRMWARE(WL1251_FW_NAME); diff --git a/drivers/net/wireless/wl12xx/wl1251_netlink.h b/drivers/net/wireless/wl12xx/wl1251_netlink.h deleted file mode 100644 index ee36695e134e..000000000000 --- a/drivers/net/wireless/wl12xx/wl1251_netlink.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This file is part of wl1251 - * - * Copyright (C) 2009 Nokia Corporation - * - * Contact: Kalle Valo <kalle.valo@nokia.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#ifndef __WL1251_NETLINK_H__ -#define __WL1251_NETLINK_H__ - -int wl1251_nl_register(void); -void wl1251_nl_unregister(void); - -#endif /* __WL1251_NETLINK_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1251_ps.c b/drivers/net/wireless/wl12xx/wl1251_ps.c index c53e28727ed4..9931b197ff77 100644 --- a/drivers/net/wireless/wl12xx/wl1251_ps.c +++ b/drivers/net/wireless/wl12xx/wl1251_ps.c @@ -28,17 +28,41 @@ #define WL1251_WAKEUP_TIMEOUT 2000 -/* Routines to toggle sleep mode while in ELP */ -void wl1251_ps_elp_sleep(struct wl1251 *wl) +void wl1251_elp_work(struct work_struct *work) { + struct delayed_work *dwork; + struct wl1251 *wl; + + dwork = container_of(work, struct delayed_work, work); + wl = container_of(dwork, struct wl1251, elp_work); + + wl1251_debug(DEBUG_PSM, "elp work"); + + mutex_lock(&wl->mutex); + if (wl->elp || !wl->psm) - return; + goto out; wl1251_debug(DEBUG_PSM, "chip to elp"); - wl1251_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP); - wl->elp = true; + +out: + mutex_unlock(&wl->mutex); +} + +#define ELP_ENTRY_DELAY 5 + +/* Routines to toggle sleep mode while in ELP */ +void wl1251_ps_elp_sleep(struct wl1251 *wl) +{ + unsigned long delay; + + if (wl->psm) { + cancel_delayed_work(&wl->elp_work); + delay = msecs_to_jiffies(ELP_ENTRY_DELAY); + ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, delay); + } } int wl1251_ps_elp_wakeup(struct wl1251 *wl) @@ -119,6 +143,11 @@ int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode) case STATION_POWER_SAVE_MODE: wl1251_debug(DEBUG_PSM, "entering psm"); + /* enable beacon filtering */ + ret = wl1251_acx_beacon_filter_opt(wl, true); + if (ret < 0) + return ret; + ret = wl1251_acx_wake_up_conditions(wl, WAKE_UP_EVENT_DTIM_BITMAP, wl->listen_int); @@ -142,6 +171,11 @@ int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode) if (ret < 0) return ret; + /* disable beacon filtering */ + ret = wl1251_acx_beacon_filter_opt(wl, false); + if (ret < 0) + return ret; + ret = wl1251_acx_wake_up_conditions(wl, WAKE_UP_EVENT_DTIM_BITMAP, wl->listen_int); diff --git a/drivers/net/wireless/wl12xx/wl1251_ps.h b/drivers/net/wireless/wl12xx/wl1251_ps.h index db036fe12f25..c688ac57aee4 100644 --- a/drivers/net/wireless/wl12xx/wl1251_ps.h +++ b/drivers/net/wireless/wl12xx/wl1251_ps.h @@ -31,6 +31,7 @@ int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode); void wl1251_ps_elp_sleep(struct wl1251 *wl); int wl1251_ps_elp_wakeup(struct wl1251 *wl); +void wl1251_elp_work(struct work_struct *work); #endif /* __WL1251_PS_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1251_reg.h b/drivers/net/wireless/wl12xx/wl1251_reg.h index 06e1bd94a739..0ca3b4326056 100644 --- a/drivers/net/wireless/wl12xx/wl1251_reg.h +++ b/drivers/net/wireless/wl12xx/wl1251_reg.h @@ -370,6 +370,7 @@ enum wl12xx_acx_int_reg { EEPROM location specified in the EE_ADDR register. The Wlan hardware hardware clears this bit automatically. *===============================================*/ +#define EE_CTL (REGISTERS_BASE + 0x2000) #define ACX_EE_CTL_REG EE_CTL #define EE_WRITE 0x00000001ul #define EE_READ 0x00000002ul @@ -380,6 +381,7 @@ enum wl12xx_acx_int_reg { This register specifies the address within the EEPROM from/to which to read/write data. ===============================================*/ +#define EE_ADDR (REGISTERS_BASE + 0x2008) #define ACX_EE_ADDR_REG EE_ADDR /*=============================================== @@ -389,8 +391,12 @@ enum wl12xx_acx_int_reg { data from the EEPROM or the write data to be written to the EEPROM. ===============================================*/ +#define EE_DATA (REGISTERS_BASE + 0x2004) #define ACX_EE_DATA_REG EE_DATA +#define EEPROM_ACCESS_TO 10000 /* timeout counter */ +#define START_EEPROM_MGR 0x00000001 + /*=============================================== EEPROM Base Address - 32bit RW ------------------------------------------ diff --git a/drivers/net/wireless/wl12xx/wl1251_rx.c b/drivers/net/wireless/wl12xx/wl1251_rx.c index 17c54b59ef86..f84cc89cbffc 100644 --- a/drivers/net/wireless/wl12xx/wl1251_rx.c +++ b/drivers/net/wireless/wl12xx/wl1251_rx.c @@ -72,10 +72,6 @@ static void wl1251_rx_status(struct wl1251 *wl, } status->signal = desc->rssi; - status->qual = (desc->rssi - WL1251_RX_MIN_RSSI) * 100 / - (WL1251_RX_MAX_RSSI - WL1251_RX_MIN_RSSI); - status->qual = min(status->qual, 100); - status->qual = max(status->qual, 0); /* * FIXME: guessing that snr needs to be divided by two, otherwise @@ -153,7 +149,7 @@ static void wl1251_rx_body(struct wl1251 *wl, beacon ? "beacon" : ""); memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); - ieee80211_rx(wl->hw, skb); + ieee80211_rx_ni(wl->hw, skb); } static void wl1251_rx_ack(struct wl1251 *wl) diff --git a/drivers/net/wireless/wl12xx/wl1251_spi.c b/drivers/net/wireless/wl12xx/wl1251_spi.c index 14eff2b3d4c6..9cc8c323830f 100644 --- a/drivers/net/wireless/wl12xx/wl1251_spi.c +++ b/drivers/net/wireless/wl12xx/wl1251_spi.c @@ -270,6 +270,8 @@ static int __devinit wl1251_spi_probe(struct spi_device *spi) return -ENODEV; } + wl->use_eeprom = pdata->use_eeprom; + ret = request_irq(wl->irq, wl1251_irq, 0, DRIVER_NAME, wl); if (ret < 0) { wl1251_error("request_irq() failed: %d", ret); @@ -307,7 +309,7 @@ static int __devexit wl1251_spi_remove(struct spi_device *spi) static struct spi_driver wl1251_spi_driver = { .driver = { - .name = "wl12xx", + .name = "wl1251", .bus = &spi_bus_type, .owner = THIS_MODULE, }, diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index 55818f94017b..94359b1a861f 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -32,6 +32,8 @@ #include <linux/bitops.h> #include <net/mac80211.h> +#include "wl1271_conf.h" + #define DRIVER_NAME "wl1271" #define DRIVER_PREFIX DRIVER_NAME ": " @@ -97,21 +99,42 @@ enum { } while (0) #define WL1271_DEFAULT_RX_CONFIG (CFG_UNI_FILTER_EN | \ - CFG_BSSID_FILTER_EN) + CFG_BSSID_FILTER_EN | \ + CFG_MC_FILTER_EN) #define WL1271_DEFAULT_RX_FILTER (CFG_RX_RCTS_ACK | CFG_RX_PRSP_EN | \ CFG_RX_MGMT_EN | CFG_RX_DATA_EN | \ CFG_RX_CTL_EN | CFG_RX_BCN_EN | \ CFG_RX_AUTH_EN | CFG_RX_ASSOC_EN) +#define WL1271_DEFAULT_BASIC_RATE_SET (CONF_TX_RATE_MASK_ALL) + #define WL1271_FW_NAME "wl1271-fw.bin" #define WL1271_NVS_NAME "wl1271-nvs.bin" -#define WL1271_BUSY_WORD_LEN 8 +/* + * Enable/disable 802.11a support for WL1273 + */ +#undef WL1271_80211A_ENABLED + +/* + * FIXME: for the wl1271, a busy word count of 1 here will result in a more + * optimal SPI interface. There is some SPI bug however, causing RXS time outs + * with this mode occasionally on boot, so lets have three for now. A value of + * three should make sure, that the chipset will always be ready, though this + * will impact throughput and latencies slightly. + */ +#define WL1271_BUSY_WORD_CNT 3 +#define WL1271_BUSY_WORD_LEN (WL1271_BUSY_WORD_CNT * sizeof(u32)) #define WL1271_ELP_HW_STATE_ASLEEP 0 #define WL1271_ELP_HW_STATE_IRQ 1 +#define WL1271_DEFAULT_BEACON_INT 100 +#define WL1271_DEFAULT_DTIM_PERIOD 1 + +#define ACX_TX_DESCRIPTORS 32 + enum wl1271_state { WL1271_STATE_OFF, WL1271_STATE_ON, @@ -134,6 +157,8 @@ struct wl1271_partition { struct wl1271_partition_set { struct wl1271_partition mem; struct wl1271_partition reg; + struct wl1271_partition mem2; + struct wl1271_partition mem3; }; struct wl1271; @@ -258,15 +283,15 @@ struct wl1271_debugfs { /* FW status registers */ struct wl1271_fw_status { - u32 intr; + __le32 intr; u8 fw_rx_counter; u8 drv_rx_counter; u8 reserved; u8 tx_results_counter; - u32 rx_pkt_descs[NUM_RX_PKT_DESC]; - u32 tx_released_blks[NUM_TX_QUEUES]; - u32 fw_localtime; - u32 padding[2]; + __le32 rx_pkt_descs[NUM_RX_PKT_DESC]; + __le32 tx_released_blks[NUM_TX_QUEUES]; + __le32 fw_localtime; + __le32 padding[2]; } __attribute__ ((packed)); struct wl1271_rx_mem_pool_addr { @@ -274,6 +299,15 @@ struct wl1271_rx_mem_pool_addr { u32 addr_extra; }; +struct wl1271_scan { + u8 state; + u8 ssid[IW_ESSID_MAX_SIZE+1]; + size_t ssid_len; + u8 active; + u8 high_prio; + u8 probe_requests; +}; + struct wl1271 { struct ieee80211_hw *hw; bool mac80211_registered; @@ -288,10 +322,7 @@ struct wl1271 { enum wl1271_state state; struct mutex mutex; - int physical_mem_addr; - int physical_reg_addr; - int virtual_mem_addr; - int virtual_reg_addr; + struct wl1271_partition_set part; struct wl1271_chip chip; @@ -308,7 +339,6 @@ struct wl1271 { u8 bss_type; u8 ssid[IW_ESSID_MAX_SIZE + 1]; u8 ssid_len; - u8 listen_int; int channel; struct wl1271_acx_mem_map *target_mem_map; @@ -332,10 +362,14 @@ struct wl1271 { bool tx_queue_stopped; struct work_struct tx_work; - struct work_struct filter_work; /* Pending TX frames */ - struct sk_buff *tx_frames[16]; + struct sk_buff *tx_frames[ACX_TX_DESCRIPTORS]; + + /* Security sequence number counters */ + u8 tx_security_last_seq; + u16 tx_security_seq_16; + u32 tx_security_seq_32; /* FW Rx counter */ u32 rx_counter; @@ -354,10 +388,17 @@ struct wl1271 { /* Are we currently scanning */ bool scanning; + struct wl1271_scan scan; /* Our association ID */ u16 aid; + /* currently configured rate set */ + u32 basic_rate_set; + + /* The current band */ + enum ieee80211_band band; + /* Default key (for WEP) */ u32 default_key; @@ -368,6 +409,7 @@ struct wl1271 { bool elp; struct completion *elp_compl; + struct delayed_work elp_work; /* we can be in psm, but not in elp, we have to differentiate */ bool psm; @@ -375,6 +417,9 @@ struct wl1271 { /* PSM mode requested */ bool psm_requested; + /* retry counter for PSM entries */ + u8 psm_entry_retry; + /* in dBm */ int power_level; @@ -383,11 +428,20 @@ struct wl1271 { u32 buffer_32; u32 buffer_cmd; - u8 buffer_busyword[WL1271_BUSY_WORD_LEN]; - struct wl1271_rx_descriptor *rx_descriptor; + u32 buffer_busyword[WL1271_BUSY_WORD_CNT]; struct wl1271_fw_status *fw_status; struct wl1271_tx_hw_res_if *tx_res_if; + + struct ieee80211_vif *vif; + + /* Used for a workaround to send disconnect before rejoining */ + bool joined; + + /* Current chipset configuration */ + struct conf_drv_settings conf; + + struct list_head list; }; int wl1271_plt_start(struct wl1271 *wl); @@ -404,4 +458,13 @@ int wl1271_plt_stop(struct wl1271 *wl); /* WL1271 needs a 200ms sleep after power on */ #define WL1271_POWER_ON_SLEEP 200 /* in miliseconds */ +static inline bool wl1271_11a_enabled(void) +{ +#ifdef WL1271_80211A_ENABLED + return true; +#else + return false; +#endif +} + #endif diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c index f622a4092615..5cc89bbdac7a 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.c +++ b/drivers/net/wireless/wl12xx/wl1271_acx.c @@ -34,8 +34,7 @@ #include "wl1271_spi.h" #include "wl1271_ps.h" -int wl1271_acx_wake_up_conditions(struct wl1271 *wl, u8 wake_up_event, - u8 listen_interval) +int wl1271_acx_wake_up_conditions(struct wl1271 *wl) { struct acx_wake_up_condition *wake_up; int ret; @@ -48,8 +47,8 @@ int wl1271_acx_wake_up_conditions(struct wl1271 *wl, u8 wake_up_event, goto out; } - wake_up->wake_up_event = wake_up_event; - wake_up->listen_interval = listen_interval; + wake_up->wake_up_event = wl->conf.conn.wake_up_event; + wake_up->listen_interval = wl->conf.conn.listen_interval; ret = wl1271_cmd_configure(wl, ACX_WAKE_UP_CONDITIONS, wake_up, sizeof(*wake_up)); @@ -137,7 +136,12 @@ int wl1271_acx_tx_power(struct wl1271 *wl, int power) goto out; } - acx->current_tx_power = power * 10; + /* + * FIXME: This is a workaround needed while we don't the correct + * calibration, to avoid distortions + */ + /* acx->current_tx_power = power * 10; */ + acx->current_tx_power = 120; ret = wl1271_cmd_configure(wl, DOT11_CUR_TX_PWR, acx, sizeof(*acx)); if (ret < 0) { @@ -193,7 +197,7 @@ int wl1271_acx_mem_map(struct wl1271 *wl, struct acx_header *mem_map, return 0; } -int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl, u32 life_time) +int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl) { struct acx_rx_msdu_lifetime *acx; int ret; @@ -206,7 +210,7 @@ int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl, u32 life_time) goto out; } - acx->lifetime = life_time; + acx->lifetime = cpu_to_le32(wl->conf.rx.rx_msdu_life_time); ret = wl1271_cmd_configure(wl, DOT11_RX_MSDU_LIFE_TIME, acx, sizeof(*acx)); if (ret < 0) { @@ -232,8 +236,8 @@ int wl1271_acx_rx_config(struct wl1271 *wl, u32 config, u32 filter) goto out; } - rx_config->config_options = config; - rx_config->filter_options = filter; + rx_config->config_options = cpu_to_le32(config); + rx_config->filter_options = cpu_to_le32(filter); ret = wl1271_cmd_configure(wl, ACX_RX_CFG, rx_config, sizeof(*rx_config)); @@ -260,7 +264,7 @@ int wl1271_acx_pd_threshold(struct wl1271 *wl) goto out; } - /* FIXME: threshold value not set */ + pd->threshold = cpu_to_le32(wl->conf.rx.packet_detection_threshold); ret = wl1271_cmd_configure(wl, ACX_PD_THRESHOLD, pd, sizeof(*pd)); if (ret < 0) { @@ -300,7 +304,8 @@ out: return ret; } -int wl1271_acx_group_address_tbl(struct wl1271 *wl) +int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable, + void *mc_list, u32 mc_list_len) { struct acx_dot11_grp_addr_tbl *acx; int ret; @@ -314,9 +319,9 @@ int wl1271_acx_group_address_tbl(struct wl1271 *wl) } /* MAC filtering */ - acx->enabled = 0; - acx->num_groups = 0; - memset(acx->mac_table, 0, ADDRESS_GROUP_MAX_LEN); + acx->enabled = enable; + acx->num_groups = mc_list_len; + memcpy(acx->mac_table, mc_list, mc_list_len * ETH_ALEN); ret = wl1271_cmd_configure(wl, DOT11_GROUP_ADDRESS_TBL, acx, sizeof(*acx)); @@ -343,8 +348,8 @@ int wl1271_acx_service_period_timeout(struct wl1271 *wl) wl1271_debug(DEBUG_ACX, "acx service period timeout"); - rx_timeout->ps_poll_timeout = RX_TIMEOUT_PS_POLL_DEF; - rx_timeout->upsd_timeout = RX_TIMEOUT_UPSD_DEF; + rx_timeout->ps_poll_timeout = cpu_to_le16(wl->conf.rx.ps_poll_timeout); + rx_timeout->upsd_timeout = cpu_to_le16(wl->conf.rx.upsd_timeout); ret = wl1271_cmd_configure(wl, ACX_SERVICE_PERIOD_TIMEOUT, rx_timeout, sizeof(*rx_timeout)); @@ -372,7 +377,7 @@ int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold) goto out; } - rts->threshold = rts_threshold; + rts->threshold = cpu_to_le16(rts_threshold); ret = wl1271_cmd_configure(wl, DOT11_RTS_THRESHOLD, rts, sizeof(*rts)); if (ret < 0) { @@ -385,20 +390,29 @@ out: return ret; } -int wl1271_acx_beacon_filter_opt(struct wl1271 *wl) +int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter) { - struct acx_beacon_filter_option *beacon_filter; - int ret; + struct acx_beacon_filter_option *beacon_filter = NULL; + int ret = 0; wl1271_debug(DEBUG_ACX, "acx beacon filter opt"); + if (enable_filter && + wl->conf.conn.bcn_filt_mode == CONF_BCN_FILT_MODE_DISABLED) + goto out; + beacon_filter = kzalloc(sizeof(*beacon_filter), GFP_KERNEL); if (!beacon_filter) { ret = -ENOMEM; goto out; } - beacon_filter->enable = 0; + beacon_filter->enable = enable_filter; + + /* + * When set to zero, and the filter is enabled, beacons + * without the unicast TIM bit set are dropped. + */ beacon_filter->max_num_beacons = 0; ret = wl1271_cmd_configure(wl, ACX_BEACON_FILTER_OPT, @@ -416,7 +430,9 @@ out: int wl1271_acx_beacon_filter_table(struct wl1271 *wl) { struct acx_beacon_filter_ie_table *ie_table; + int i, idx = 0; int ret; + bool vendor_spec = false; wl1271_debug(DEBUG_ACX, "acx beacon filter table"); @@ -426,8 +442,32 @@ int wl1271_acx_beacon_filter_table(struct wl1271 *wl) goto out; } + /* configure default beacon pass-through rules */ ie_table->num_ie = 0; - memset(ie_table->table, 0, BEACON_FILTER_TABLE_MAX_SIZE); + for (i = 0; i < wl->conf.conn.bcn_filt_ie_count; i++) { + struct conf_bcn_filt_rule *r = &(wl->conf.conn.bcn_filt_ie[i]); + ie_table->table[idx++] = r->ie; + ie_table->table[idx++] = r->rule; + + if (r->ie == WLAN_EID_VENDOR_SPECIFIC) { + /* only one vendor specific ie allowed */ + if (vendor_spec) + continue; + + /* for vendor specific rules configure the + additional fields */ + memcpy(&(ie_table->table[idx]), r->oui, + CONF_BCN_IE_OUI_LEN); + idx += CONF_BCN_IE_OUI_LEN; + ie_table->table[idx++] = r->type; + memcpy(&(ie_table->table[idx]), r->version, + CONF_BCN_IE_VER_LEN); + idx += CONF_BCN_IE_VER_LEN; + vendor_spec = true; + } + + ie_table->num_ie++; + } ret = wl1271_cmd_configure(wl, ACX_BEACON_FILTER_TABLE, ie_table, sizeof(*ie_table)); @@ -441,6 +481,36 @@ out: return ret; } +int wl1271_acx_conn_monit_params(struct wl1271 *wl) +{ + struct acx_conn_monit_params *acx; + int ret; + + wl1271_debug(DEBUG_ACX, "acx connection monitor parameters"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->synch_fail_thold = cpu_to_le32(wl->conf.conn.synch_fail_thold); + acx->bss_lose_timeout = cpu_to_le32(wl->conf.conn.bss_lose_timeout); + + ret = wl1271_cmd_configure(wl, ACX_CONN_MONIT_PARAMS, + acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("failed to set connection monitor " + "parameters: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + + int wl1271_acx_sg_enable(struct wl1271 *wl) { struct acx_bt_wlan_coex *pta; @@ -470,6 +540,7 @@ out: int wl1271_acx_sg_cfg(struct wl1271 *wl) { struct acx_bt_wlan_coex_param *param; + struct conf_sg_settings *c = &wl->conf.sg; int ret; wl1271_debug(DEBUG_ACX, "acx sg cfg"); @@ -481,34 +552,19 @@ int wl1271_acx_sg_cfg(struct wl1271 *wl) } /* BT-WLAN coext parameters */ - param->min_rate = RATE_INDEX_24MBPS; - param->bt_hp_max_time = PTA_BT_HP_MAXTIME_DEF; - param->wlan_hp_max_time = PTA_WLAN_HP_MAX_TIME_DEF; - param->sense_disable_timer = PTA_SENSE_DISABLE_TIMER_DEF; - param->rx_time_bt_hp = PTA_PROTECTIVE_RX_TIME_DEF; - param->tx_time_bt_hp = PTA_PROTECTIVE_TX_TIME_DEF; - param->rx_time_bt_hp_fast = PTA_PROTECTIVE_RX_TIME_FAST_DEF; - param->tx_time_bt_hp_fast = PTA_PROTECTIVE_TX_TIME_FAST_DEF; - param->wlan_cycle_fast = PTA_CYCLE_TIME_FAST_DEF; - param->bt_anti_starvation_period = PTA_ANTI_STARVE_PERIOD_DEF; - param->next_bt_lp_packet = PTA_TIMEOUT_NEXT_BT_LP_PACKET_DEF; - param->wake_up_beacon = PTA_TIME_BEFORE_BEACON_DEF; - param->hp_dm_max_guard_time = PTA_HPDM_MAX_TIME_DEF; - param->next_wlan_packet = PTA_TIME_OUT_NEXT_WLAN_DEF; - param->antenna_type = PTA_ANTENNA_TYPE_DEF; - param->signal_type = PTA_SIGNALING_TYPE_DEF; - param->afh_leverage_on = PTA_AFH_LEVERAGE_ON_DEF; - param->quiet_cycle_num = PTA_NUMBER_QUIET_CYCLE_DEF; - param->max_cts = PTA_MAX_NUM_CTS_DEF; - param->wlan_packets_num = PTA_NUMBER_OF_WLAN_PACKETS_DEF; - param->bt_packets_num = PTA_NUMBER_OF_BT_PACKETS_DEF; - param->missed_rx_avalanche = PTA_RX_FOR_AVALANCHE_DEF; - param->wlan_elp_hp = PTA_ELP_HP_DEF; - param->bt_anti_starvation_cycles = PTA_ANTI_STARVE_NUM_CYCLE_DEF; - param->ack_mode_dual_ant = PTA_ACK_MODE_DEF; - param->pa_sd_enable = PTA_ALLOW_PA_SD_DEF; - param->pta_auto_mode_enable = PTA_AUTO_MODE_NO_CTS_DEF; - param->bt_hp_respected_num = PTA_BT_HP_RESPECTED_DEF; + param->per_threshold = cpu_to_le32(c->per_threshold); + param->max_scan_compensation_time = + cpu_to_le32(c->max_scan_compensation_time); + param->nfs_sample_interval = cpu_to_le16(c->nfs_sample_interval); + param->load_ratio = c->load_ratio; + param->auto_ps_mode = c->auto_ps_mode; + param->probe_req_compensation = c->probe_req_compensation; + param->scan_window_compensation = c->scan_window_compensation; + param->antenna_config = c->antenna_config; + param->beacon_miss_threshold = c->beacon_miss_threshold; + param->rate_adaptation_threshold = + cpu_to_le32(c->rate_adaptation_threshold); + param->rate_adaptation_snr = c->rate_adaptation_snr; ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param)); if (ret < 0) { @@ -534,8 +590,8 @@ int wl1271_acx_cca_threshold(struct wl1271 *wl) goto out; } - detection->rx_cca_threshold = CCA_THRSH_DISABLE_ENERGY_D; - detection->tx_energy_detection = 0; + detection->rx_cca_threshold = cpu_to_le16(wl->conf.rx.rx_cca_threshold); + detection->tx_energy_detection = wl->conf.tx.tx_energy_detection; ret = wl1271_cmd_configure(wl, ACX_CCA_THRESHOLD, detection, sizeof(*detection)); @@ -562,10 +618,10 @@ int wl1271_acx_bcn_dtim_options(struct wl1271 *wl) goto out; } - bb->beacon_rx_timeout = BCN_RX_TIMEOUT_DEF_VALUE; - bb->broadcast_timeout = BROADCAST_RX_TIMEOUT_DEF_VALUE; - bb->rx_broadcast_in_ps = RX_BROADCAST_IN_PS_DEF_VALUE; - bb->ps_poll_threshold = CONSECUTIVE_PS_POLL_FAILURE_DEF; + bb->beacon_rx_timeout = cpu_to_le16(wl->conf.conn.beacon_rx_timeout); + bb->broadcast_timeout = cpu_to_le16(wl->conf.conn.broadcast_timeout); + bb->rx_broadcast_in_ps = wl->conf.conn.rx_broadcast_in_ps; + bb->ps_poll_threshold = wl->conf.conn.ps_poll_threshold; ret = wl1271_cmd_configure(wl, ACX_BCN_DTIM_OPTIONS, bb, sizeof(*bb)); if (ret < 0) { @@ -591,7 +647,7 @@ int wl1271_acx_aid(struct wl1271 *wl, u16 aid) goto out; } - acx_aid->aid = aid; + acx_aid->aid = cpu_to_le16(aid); ret = wl1271_cmd_configure(wl, ACX_AID, acx_aid, sizeof(*acx_aid)); if (ret < 0) { @@ -618,9 +674,8 @@ int wl1271_acx_event_mbox_mask(struct wl1271 *wl, u32 event_mask) } /* high event mask is unused */ - mask->high_event_mask = 0xffffffff; - - mask->event_mask = event_mask; + mask->high_event_mask = cpu_to_le32(0xffffffff); + mask->event_mask = cpu_to_le32(event_mask); ret = wl1271_cmd_configure(wl, ACX_EVENT_MBOX_MASK, mask, sizeof(*mask)); @@ -703,9 +758,10 @@ int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats) return 0; } -int wl1271_acx_rate_policies(struct wl1271 *wl) +int wl1271_acx_rate_policies(struct wl1271 *wl, u32 enabled_rates) { struct acx_rate_policy *acx; + struct conf_tx_rate_class *c = &wl->conf.tx.rc_conf; int ret = 0; wl1271_debug(DEBUG_ACX, "acx rate policies"); @@ -718,11 +774,11 @@ int wl1271_acx_rate_policies(struct wl1271 *wl) } /* configure one default (one-size-fits-all) rate class */ - acx->rate_class_cnt = 1; - acx->rate_class[0].enabled_rates = ACX_RATE_MASK_ALL; - acx->rate_class[0].short_retry_limit = ACX_RATE_RETRY_LIMIT; - acx->rate_class[0].long_retry_limit = ACX_RATE_RETRY_LIMIT; - acx->rate_class[0].aflags = 0; + acx->rate_class_cnt = cpu_to_le32(1); + acx->rate_class[0].enabled_rates = cpu_to_le32(enabled_rates); + acx->rate_class[0].short_retry_limit = c->short_retry_limit; + acx->rate_class[0].long_retry_limit = c->long_retry_limit; + acx->rate_class[0].aflags = c->aflags; ret = wl1271_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx)); if (ret < 0) { @@ -749,22 +805,14 @@ int wl1271_acx_ac_cfg(struct wl1271 *wl) goto out; } - /* - * FIXME: Configure each AC with appropriate values (most suitable - * values will probably be different for each AC. - */ - for (i = 0; i < WL1271_ACX_AC_COUNT; i++) { - acx->ac = i; - - /* - * FIXME: The following default values originate from - * the TI reference driver. What do they mean? - */ - acx->cw_min = 15; - acx->cw_max = 63; - acx->aifsn = 3; + for (i = 0; i < wl->conf.tx.ac_conf_count; i++) { + struct conf_tx_ac_category *c = &(wl->conf.tx.ac_conf[i]); + acx->ac = c->ac; + acx->cw_min = c->cw_min; + acx->cw_max = cpu_to_le16(c->cw_max); + acx->aifsn = c->aifsn; acx->reserved = 0; - acx->tx_op_limit = 0; + acx->tx_op_limit = cpu_to_le16(c->tx_op_limit); ret = wl1271_cmd_configure(wl, ACX_AC_CFG, acx, sizeof(*acx)); if (ret < 0) { @@ -793,12 +841,15 @@ int wl1271_acx_tid_cfg(struct wl1271 *wl) goto out; } - /* FIXME: configure each TID with a different AC reference */ - for (i = 0; i < WL1271_ACX_TID_COUNT; i++) { - acx->queue_id = i; - acx->tsid = WL1271_ACX_AC_BE; - acx->ps_scheme = WL1271_ACX_PS_SCHEME_LEGACY; - acx->ack_policy = WL1271_ACX_ACK_POLICY_LEGACY; + for (i = 0; i < wl->conf.tx.tid_conf_count; i++) { + struct conf_tx_tid *c = &(wl->conf.tx.tid_conf[i]); + acx->queue_id = c->queue_id; + acx->channel_type = c->channel_type; + acx->tsid = c->tsid; + acx->ps_scheme = c->ps_scheme; + acx->ack_policy = c->ack_policy; + acx->apsd_conf[0] = cpu_to_le32(c->apsd_conf[0]); + acx->apsd_conf[1] = cpu_to_le32(c->apsd_conf[1]); ret = wl1271_cmd_configure(wl, ACX_TID_CFG, acx, sizeof(*acx)); if (ret < 0) { @@ -826,7 +877,7 @@ int wl1271_acx_frag_threshold(struct wl1271 *wl) goto out; } - acx->frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD; + acx->frag_threshold = cpu_to_le16(wl->conf.tx.frag_threshold); ret = wl1271_cmd_configure(wl, ACX_FRAG_CFG, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("Setting of frag threshold failed: %d", ret); @@ -852,8 +903,8 @@ int wl1271_acx_tx_config_options(struct wl1271 *wl) goto out; } - acx->tx_compl_timeout = WL1271_ACX_TX_COMPL_TIMEOUT; - acx->tx_compl_threshold = WL1271_ACX_TX_COMPL_THRESHOLD; + acx->tx_compl_timeout = cpu_to_le16(wl->conf.tx.tx_compl_timeout); + acx->tx_compl_threshold = cpu_to_le16(wl->conf.tx.tx_compl_threshold); ret = wl1271_cmd_configure(wl, ACX_TX_CONFIG_OPT, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("Setting of tx options failed: %d", ret); @@ -879,11 +930,11 @@ int wl1271_acx_mem_cfg(struct wl1271 *wl) } /* memory config */ - mem_conf->num_stations = cpu_to_le16(DEFAULT_NUM_STATIONS); + mem_conf->num_stations = DEFAULT_NUM_STATIONS; mem_conf->rx_mem_block_num = ACX_RX_MEM_BLOCKS; mem_conf->tx_min_mem_block_num = ACX_TX_MIN_MEM_BLOCKS; mem_conf->num_ssid_profiles = ACX_NUM_SSID_PROFILES; - mem_conf->total_tx_descriptors = ACX_TX_DESCRIPTORS; + mem_conf->total_tx_descriptors = cpu_to_le32(ACX_TX_DESCRIPTORS); ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf, sizeof(*mem_conf)); @@ -906,7 +957,7 @@ int wl1271_acx_init_mem_config(struct wl1271 *wl) return ret; wl->target_mem_map = kzalloc(sizeof(struct wl1271_acx_mem_map), - GFP_KERNEL); + GFP_KERNEL); if (!wl->target_mem_map) { wl1271_error("couldn't allocate target memory map"); return -ENOMEM; @@ -923,7 +974,8 @@ int wl1271_acx_init_mem_config(struct wl1271 *wl) } /* initialize TX block book keeping */ - wl->tx_blocks_available = wl->target_mem_map->num_tx_mem_blocks; + wl->tx_blocks_available = + le32_to_cpu(wl->target_mem_map->num_tx_mem_blocks); wl1271_debug(DEBUG_TX, "available tx blocks: %d", wl->tx_blocks_available); @@ -943,10 +995,10 @@ int wl1271_acx_init_rx_interrupt(struct wl1271 *wl) goto out; } - rx_conf->threshold = WL1271_RX_INTR_THRESHOLD_DEF; - rx_conf->timeout = WL1271_RX_INTR_TIMEOUT_DEF; - rx_conf->mblk_threshold = USHORT_MAX; /* Disabled */ - rx_conf->queue_type = RX_QUEUE_TYPE_RX_LOW_PRIORITY; + rx_conf->threshold = cpu_to_le16(wl->conf.rx.irq_pkt_threshold); + rx_conf->timeout = cpu_to_le16(wl->conf.rx.irq_timeout); + rx_conf->mblk_threshold = cpu_to_le16(wl->conf.rx.irq_blk_threshold); + rx_conf->queue_type = wl->conf.rx.queue_type; ret = wl1271_cmd_configure(wl, ACX_RX_CONFIG_OPT, rx_conf, sizeof(*rx_conf)); @@ -959,3 +1011,124 @@ out: kfree(rx_conf); return ret; } + +int wl1271_acx_smart_reflex(struct wl1271 *wl) +{ + struct acx_smart_reflex_state *sr_state = NULL; + struct acx_smart_reflex_config_params *sr_param = NULL; + int i, ret; + + wl1271_debug(DEBUG_ACX, "acx smart reflex"); + + sr_param = kzalloc(sizeof(*sr_param), GFP_KERNEL); + if (!sr_param) { + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < CONF_SR_ERR_TBL_COUNT; i++) { + struct conf_mart_reflex_err_table *e = + &(wl->conf.init.sr_err_tbl[i]); + + sr_param->error_table[i].len = e->len; + sr_param->error_table[i].upper_limit = e->upper_limit; + memcpy(sr_param->error_table[i].values, e->values, e->len); + } + + ret = wl1271_cmd_configure(wl, ACX_SET_SMART_REFLEX_PARAMS, + sr_param, sizeof(*sr_param)); + if (ret < 0) { + wl1271_warning("failed to set smart reflex params: %d", ret); + goto out; + } + + sr_state = kzalloc(sizeof(*sr_state), GFP_KERNEL); + if (!sr_state) { + ret = -ENOMEM; + goto out; + } + + /* enable smart reflex */ + sr_state->enable = wl->conf.init.sr_enable; + + ret = wl1271_cmd_configure(wl, ACX_SET_SMART_REFLEX_STATE, + sr_state, sizeof(*sr_state)); + if (ret < 0) { + wl1271_warning("failed to set smart reflex params: %d", ret); + goto out; + } + +out: + kfree(sr_state); + kfree(sr_param); + return ret; + +} + +int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable) +{ + struct wl1271_acx_bet_enable *acx = NULL; + int ret = 0; + + wl1271_debug(DEBUG_ACX, "acx bet enable"); + + if (enable && wl->conf.conn.bet_enable == CONF_BET_MODE_DISABLE) + goto out; + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->enable = enable ? CONF_BET_MODE_ENABLE : CONF_BET_MODE_DISABLE; + acx->max_consecutive = wl->conf.conn.bet_max_consecutive; + + ret = wl1271_cmd_configure(wl, ACX_BET_ENABLE, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx bet enable failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + +int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, u8 *address, + u8 version) +{ + struct wl1271_acx_arp_filter *acx; + int ret; + + wl1271_debug(DEBUG_ACX, "acx arp ip filter, enable: %d", enable); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->version = version; + acx->enable = enable; + + if (enable == true) { + if (version == ACX_IPV4_VERSION) + memcpy(acx->address, address, ACX_IPV4_ADDR_SIZE); + else if (version == ACX_IPV6_VERSION) + memcpy(acx->address, address, sizeof(acx->address)); + else + wl1271_error("Invalid IP version"); + } + + ret = wl1271_cmd_configure(wl, ACX_ARP_IP_FILTER, + acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("failed to set arp ip filter: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h index 9068daaf0ddf..2ce0a8128542 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.h +++ b/drivers/net/wireless/wl12xx/wl1271_acx.h @@ -61,8 +61,9 @@ WL1271_ACX_INTR_HW_AVAILABLE | \ WL1271_ACX_INTR_DATA) -#define WL1271_INTR_MASK (WL1271_ACX_INTR_EVENT_A | \ - WL1271_ACX_INTR_EVENT_B | \ +#define WL1271_INTR_MASK (WL1271_ACX_INTR_EVENT_A | \ + WL1271_ACX_INTR_EVENT_B | \ + WL1271_ACX_INTR_HW_AVAILABLE | \ WL1271_ACX_INTR_DATA) /* Target's information element */ @@ -70,11 +71,11 @@ struct acx_header { struct wl1271_cmd_header cmd; /* acx (or information element) header */ - u16 id; + __le16 id; /* payload length (not including headers */ - u16 len; -}; + __le16 len; +} __attribute__ ((packed)); struct acx_error_counter { struct acx_header header; @@ -82,21 +83,21 @@ struct acx_error_counter { /* The number of PLCP errors since the last time this */ /* information element was interrogated. This field is */ /* automatically cleared when it is interrogated.*/ - u32 PLCP_error; + __le32 PLCP_error; /* The number of FCS errors since the last time this */ /* information element was interrogated. This field is */ /* automatically cleared when it is interrogated.*/ - u32 FCS_error; + __le32 FCS_error; /* The number of MPDUs without PLCP header errors received*/ /* since the last time this information element was interrogated. */ /* This field is automatically cleared when it is interrogated.*/ - u32 valid_frame; + __le32 valid_frame; /* the number of missed sequence numbers in the squentially */ /* values of frames seq numbers */ - u32 seq_num_miss; + __le32 seq_num_miss; } __attribute__ ((packed)); struct acx_revision { @@ -125,7 +126,7 @@ struct acx_revision { * (1 = first spin, 2 = second spin, and so on). * bits 24 - 31: Chip ID - The WiLink chip ID. */ - u32 hw_version; + __le32 hw_version; } __attribute__ ((packed)); enum wl1271_psm_mode { @@ -170,7 +171,6 @@ enum { #define DP_RX_PACKET_RING_CHUNK_NUM 2 #define DP_TX_PACKET_RING_CHUNK_NUM 2 #define DP_TX_COMPLETE_TIME_OUT 20 -#define FW_TX_CMPLT_BLOCK_SIZE 16 #define TX_MSDU_LIFETIME_MIN 0 #define TX_MSDU_LIFETIME_MAX 3000 @@ -186,7 +186,7 @@ struct acx_rx_msdu_lifetime { * The maximum amount of time, in TU, before the * firmware discards the MSDU. */ - u32 lifetime; + __le32 lifetime; } __attribute__ ((packed)); /* @@ -273,14 +273,14 @@ struct acx_rx_msdu_lifetime { struct acx_rx_config { struct acx_header header; - u32 config_options; - u32 filter_options; + __le32 config_options; + __le32 filter_options; } __attribute__ ((packed)); struct acx_packet_detection { struct acx_header header; - u32 threshold; + __le32 threshold; } __attribute__ ((packed)); @@ -302,8 +302,8 @@ struct acx_slot { } __attribute__ ((packed)); -#define ADDRESS_GROUP_MAX (8) -#define ADDRESS_GROUP_MAX_LEN (ETH_ALEN * ADDRESS_GROUP_MAX) +#define ACX_MC_ADDRESS_GROUP_MAX (8) +#define ADDRESS_GROUP_MAX_LEN (ETH_ALEN * ACX_MC_ADDRESS_GROUP_MAX) struct acx_dot11_grp_addr_tbl { struct acx_header header; @@ -314,40 +314,17 @@ struct acx_dot11_grp_addr_tbl { u8 mac_table[ADDRESS_GROUP_MAX_LEN]; } __attribute__ ((packed)); - -#define RX_TIMEOUT_PS_POLL_MIN 0 -#define RX_TIMEOUT_PS_POLL_MAX (200000) -#define RX_TIMEOUT_PS_POLL_DEF (15) -#define RX_TIMEOUT_UPSD_MIN 0 -#define RX_TIMEOUT_UPSD_MAX (200000) -#define RX_TIMEOUT_UPSD_DEF (15) - struct acx_rx_timeout { struct acx_header header; - /* - * The longest time the STA will wait to receive - * traffic from the AP after a PS-poll has been - * transmitted. - */ - u16 ps_poll_timeout; - - /* - * The longest time the STA will wait to receive - * traffic from the AP after a frame has been sent - * from an UPSD enabled queue. - */ - u16 upsd_timeout; + __le16 ps_poll_timeout; + __le16 upsd_timeout; } __attribute__ ((packed)); -#define RTS_THRESHOLD_MIN 0 -#define RTS_THRESHOLD_MAX 4096 -#define RTS_THRESHOLD_DEF 2347 - struct acx_rts_threshold { struct acx_header header; - u16 threshold; + __le16 threshold; u8 pad[2]; } __attribute__ ((packed)); @@ -408,6 +385,13 @@ struct acx_beacon_filter_ie_table { u8 pad[3]; } __attribute__ ((packed)); +struct acx_conn_monit_params { + struct acx_header header; + + __le32 synch_fail_thold; /* number of beacons missed */ + __le32 bss_lose_timeout; /* number of TU's from synch fail */ +} __attribute__ ((packed)); + enum { SG_ENABLE = 0, SG_DISABLE, @@ -431,6 +415,25 @@ struct acx_bt_wlan_coex { u8 pad[3]; } __attribute__ ((packed)); +struct acx_smart_reflex_state { + struct acx_header header; + + u8 enable; + u8 padding[3]; +} __attribute__ ((packed)); + +struct smart_reflex_err_table { + u8 len; + s8 upper_limit; + s8 values[14]; +} __attribute__ ((packed)); + +struct acx_smart_reflex_config_params { + struct acx_header header; + + struct smart_reflex_err_table error_table[3]; +} __attribute__ ((packed)); + #define PTA_ANTENNA_TYPE_DEF (0) #define PTA_BT_HP_MAXTIME_DEF (2000) #define PTA_WLAN_HP_MAX_TIME_DEF (5000) @@ -463,150 +466,34 @@ struct acx_bt_wlan_coex { struct acx_bt_wlan_coex_param { struct acx_header header; - /* - * The minimum rate of a received WLAN packet in the STA, - * during protective mode, of which a new BT-HP request - * during this Rx will always be respected and gain the antenna. - */ - u32 min_rate; - - /* Max time the BT HP will be respected. */ - u16 bt_hp_max_time; - - /* Max time the WLAN HP will be respected. */ - u16 wlan_hp_max_time; - - /* - * The time between the last BT activity - * and the moment when the sense mode returns - * to SENSE_INACTIVE. - */ - u16 sense_disable_timer; - - /* Time before the next BT HP instance */ - u16 rx_time_bt_hp; - u16 tx_time_bt_hp; - - /* range: 10-20000 default: 1500 */ - u16 rx_time_bt_hp_fast; - u16 tx_time_bt_hp_fast; - - /* range: 2000-65535 default: 8700 */ - u16 wlan_cycle_fast; - - /* range: 0 - 15000 (Msec) default: 1000 */ - u16 bt_anti_starvation_period; - - /* range 400-10000(Usec) default: 3000 */ - u16 next_bt_lp_packet; - - /* Deafult: worst case for BT DH5 traffic */ - u16 wake_up_beacon; - - /* range: 0-50000(Usec) default: 1050 */ - u16 hp_dm_max_guard_time; - - /* - * This is to prevent both BT & WLAN antenna - * starvation. - * Range: 100-50000(Usec) default:2550 - */ - u16 next_wlan_packet; - - /* 0 -> shared antenna */ - u8 antenna_type; - - /* - * 0 -> TI legacy - * 1 -> Palau - */ - u8 signal_type; - - /* - * BT AFH status - * 0 -> no AFH - * 1 -> from dedicated GPIO - * 2 -> AFH on (from host) - */ - u8 afh_leverage_on; - - /* - * The number of cycles during which no - * TX will be sent after 1 cycle of RX - * transaction in protective mode - */ - u8 quiet_cycle_num; - - /* - * The maximum number of CTSs that will - * be sent for receiving RX packet in - * protective mode - */ - u8 max_cts; - - /* - * The number of WLAN packets - * transferred in common mode before - * switching to BT. - */ - u8 wlan_packets_num; - - /* - * The number of BT packets - * transferred in common mode before - * switching to WLAN. - */ - u8 bt_packets_num; - - /* range: 1-255 default: 5 */ - u8 missed_rx_avalanche; - - /* range: 0-1 default: 1 */ - u8 wlan_elp_hp; - - /* range: 0 - 15 default: 4 */ - u8 bt_anti_starvation_cycles; - - u8 ack_mode_dual_ant; - - /* - * Allow PA_SD assertion/de-assertion - * during enabled BT activity. - */ - u8 pa_sd_enable; - - /* - * Enable/Disable PTA in auto mode: - * Support Both Active & P.S modes - */ - u8 pta_auto_mode_enable; - - /* range: 0 - 20 default: 1 */ - u8 bt_hp_respected_num; + __le32 per_threshold; + __le32 max_scan_compensation_time; + __le16 nfs_sample_interval; + u8 load_ratio; + u8 auto_ps_mode; + u8 probe_req_compensation; + u8 scan_window_compensation; + u8 antenna_config; + u8 beacon_miss_threshold; + __le32 rate_adaptation_threshold; + s8 rate_adaptation_snr; + u8 padding[3]; } __attribute__ ((packed)); -#define CCA_THRSH_ENABLE_ENERGY_D 0x140A -#define CCA_THRSH_DISABLE_ENERGY_D 0xFFEF - struct acx_energy_detection { struct acx_header header; /* The RX Clear Channel Assessment threshold in the PHY */ - u16 rx_cca_threshold; + __le16 rx_cca_threshold; u8 tx_energy_detection; u8 pad; } __attribute__ ((packed)); -#define BCN_RX_TIMEOUT_DEF_VALUE 10000 -#define BROADCAST_RX_TIMEOUT_DEF_VALUE 20000 -#define RX_BROADCAST_IN_PS_DEF_VALUE 1 -#define CONSECUTIVE_PS_POLL_FAILURE_DEF 4 - struct acx_beacon_broadcast { struct acx_header header; - u16 beacon_rx_timeout; - u16 broadcast_timeout; + __le16 beacon_rx_timeout; + __le16 broadcast_timeout; /* Enables receiving of broadcast packets in PS mode */ u8 rx_broadcast_in_ps; @@ -619,8 +506,8 @@ struct acx_beacon_broadcast { struct acx_event_mask { struct acx_header header; - u32 event_mask; - u32 high_event_mask; /* Unused */ + __le32 event_mask; + __le32 high_event_mask; /* Unused */ } __attribute__ ((packed)); #define CFG_RX_FCS BIT(2) @@ -657,11 +544,15 @@ struct acx_event_mask { #define SCAN_TRIGGERED BIT(2) #define SCAN_PRIORITY_HIGH BIT(3) +/* When set, disable HW encryption */ +#define DF_ENCRYPTION_DISABLE 0x01 +#define DF_SNIFF_MODE_ENABLE 0x80 + struct acx_feature_config { struct acx_header header; - u32 options; - u32 data_flow_options; + __le32 options; + __le32 data_flow_options; } __attribute__ ((packed)); struct acx_current_tx_power { @@ -671,14 +562,6 @@ struct acx_current_tx_power { u8 padding[3]; } __attribute__ ((packed)); -enum acx_wake_up_event { - WAKE_UP_EVENT_BEACON_BITMAP = 0x01, /* Wake on every Beacon*/ - WAKE_UP_EVENT_DTIM_BITMAP = 0x02, /* Wake on every DTIM*/ - WAKE_UP_EVENT_N_DTIM_BITMAP = 0x04, /* Wake on every Nth DTIM */ - WAKE_UP_EVENT_N_BEACONS_BITMAP = 0x08, /* Wake on every Nth Beacon */ - WAKE_UP_EVENT_BITS_MASK = 0x0F -}; - struct acx_wake_up_condition { struct acx_header header; @@ -693,7 +576,7 @@ struct acx_aid { /* * To be set when associated with an AP. */ - u16 aid; + __le16 aid; u8 pad[2]; } __attribute__ ((packed)); @@ -725,152 +608,152 @@ struct acx_ctsprotect { } __attribute__ ((packed)); struct acx_tx_statistics { - u32 internal_desc_overflow; + __le32 internal_desc_overflow; } __attribute__ ((packed)); struct acx_rx_statistics { - u32 out_of_mem; - u32 hdr_overflow; - u32 hw_stuck; - u32 dropped; - u32 fcs_err; - u32 xfr_hint_trig; - u32 path_reset; - u32 reset_counter; + __le32 out_of_mem; + __le32 hdr_overflow; + __le32 hw_stuck; + __le32 dropped; + __le32 fcs_err; + __le32 xfr_hint_trig; + __le32 path_reset; + __le32 reset_counter; } __attribute__ ((packed)); struct acx_dma_statistics { - u32 rx_requested; - u32 rx_errors; - u32 tx_requested; - u32 tx_errors; + __le32 rx_requested; + __le32 rx_errors; + __le32 tx_requested; + __le32 tx_errors; } __attribute__ ((packed)); struct acx_isr_statistics { /* host command complete */ - u32 cmd_cmplt; + __le32 cmd_cmplt; /* fiqisr() */ - u32 fiqs; + __le32 fiqs; /* (INT_STS_ND & INT_TRIG_RX_HEADER) */ - u32 rx_headers; + __le32 rx_headers; /* (INT_STS_ND & INT_TRIG_RX_CMPLT) */ - u32 rx_completes; + __le32 rx_completes; /* (INT_STS_ND & INT_TRIG_NO_RX_BUF) */ - u32 rx_mem_overflow; + __le32 rx_mem_overflow; /* (INT_STS_ND & INT_TRIG_S_RX_RDY) */ - u32 rx_rdys; + __le32 rx_rdys; /* irqisr() */ - u32 irqs; + __le32 irqs; /* (INT_STS_ND & INT_TRIG_TX_PROC) */ - u32 tx_procs; + __le32 tx_procs; /* (INT_STS_ND & INT_TRIG_DECRYPT_DONE) */ - u32 decrypt_done; + __le32 decrypt_done; /* (INT_STS_ND & INT_TRIG_DMA0) */ - u32 dma0_done; + __le32 dma0_done; /* (INT_STS_ND & INT_TRIG_DMA1) */ - u32 dma1_done; + __le32 dma1_done; /* (INT_STS_ND & INT_TRIG_TX_EXC_CMPLT) */ - u32 tx_exch_complete; + __le32 tx_exch_complete; /* (INT_STS_ND & INT_TRIG_COMMAND) */ - u32 commands; + __le32 commands; /* (INT_STS_ND & INT_TRIG_RX_PROC) */ - u32 rx_procs; + __le32 rx_procs; /* (INT_STS_ND & INT_TRIG_PM_802) */ - u32 hw_pm_mode_changes; + __le32 hw_pm_mode_changes; /* (INT_STS_ND & INT_TRIG_ACKNOWLEDGE) */ - u32 host_acknowledges; + __le32 host_acknowledges; /* (INT_STS_ND & INT_TRIG_PM_PCI) */ - u32 pci_pm; + __le32 pci_pm; /* (INT_STS_ND & INT_TRIG_ACM_WAKEUP) */ - u32 wakeups; + __le32 wakeups; /* (INT_STS_ND & INT_TRIG_LOW_RSSI) */ - u32 low_rssi; + __le32 low_rssi; } __attribute__ ((packed)); struct acx_wep_statistics { /* WEP address keys configured */ - u32 addr_key_count; + __le32 addr_key_count; /* default keys configured */ - u32 default_key_count; + __le32 default_key_count; - u32 reserved; + __le32 reserved; /* number of times that WEP key not found on lookup */ - u32 key_not_found; + __le32 key_not_found; /* number of times that WEP key decryption failed */ - u32 decrypt_fail; + __le32 decrypt_fail; /* WEP packets decrypted */ - u32 packets; + __le32 packets; /* WEP decrypt interrupts */ - u32 interrupt; + __le32 interrupt; } __attribute__ ((packed)); #define ACX_MISSED_BEACONS_SPREAD 10 struct acx_pwr_statistics { /* the amount of enters into power save mode (both PD & ELP) */ - u32 ps_enter; + __le32 ps_enter; /* the amount of enters into ELP mode */ - u32 elp_enter; + __le32 elp_enter; /* the amount of missing beacon interrupts to the host */ - u32 missing_bcns; + __le32 missing_bcns; /* the amount of wake on host-access times */ - u32 wake_on_host; + __le32 wake_on_host; /* the amount of wake on timer-expire */ - u32 wake_on_timer_exp; + __le32 wake_on_timer_exp; /* the number of packets that were transmitted with PS bit set */ - u32 tx_with_ps; + __le32 tx_with_ps; /* the number of packets that were transmitted with PS bit clear */ - u32 tx_without_ps; + __le32 tx_without_ps; /* the number of received beacons */ - u32 rcvd_beacons; + __le32 rcvd_beacons; /* the number of entering into PowerOn (power save off) */ - u32 power_save_off; + __le32 power_save_off; /* the number of entries into power save mode */ - u16 enable_ps; + __le16 enable_ps; /* * the number of exits from power save, not including failed PS * transitions */ - u16 disable_ps; + __le16 disable_ps; /* * the number of times the TSF counter was adjusted because * of drift */ - u32 fix_tsf_ps; + __le32 fix_tsf_ps; /* Gives statistics about the spread continuous missed beacons. * The 16 LSB are dedicated for the PS mode. @@ -881,53 +764,53 @@ struct acx_pwr_statistics { * ... * cont_miss_bcns_spread[9] - ten and more continuous missed beacons. */ - u32 cont_miss_bcns_spread[ACX_MISSED_BEACONS_SPREAD]; + __le32 cont_miss_bcns_spread[ACX_MISSED_BEACONS_SPREAD]; /* the number of beacons in awake mode */ - u32 rcvd_awake_beacons; + __le32 rcvd_awake_beacons; } __attribute__ ((packed)); struct acx_mic_statistics { - u32 rx_pkts; - u32 calc_failure; + __le32 rx_pkts; + __le32 calc_failure; } __attribute__ ((packed)); struct acx_aes_statistics { - u32 encrypt_fail; - u32 decrypt_fail; - u32 encrypt_packets; - u32 decrypt_packets; - u32 encrypt_interrupt; - u32 decrypt_interrupt; + __le32 encrypt_fail; + __le32 decrypt_fail; + __le32 encrypt_packets; + __le32 decrypt_packets; + __le32 encrypt_interrupt; + __le32 decrypt_interrupt; } __attribute__ ((packed)); struct acx_event_statistics { - u32 heart_beat; - u32 calibration; - u32 rx_mismatch; - u32 rx_mem_empty; - u32 rx_pool; - u32 oom_late; - u32 phy_transmit_error; - u32 tx_stuck; + __le32 heart_beat; + __le32 calibration; + __le32 rx_mismatch; + __le32 rx_mem_empty; + __le32 rx_pool; + __le32 oom_late; + __le32 phy_transmit_error; + __le32 tx_stuck; } __attribute__ ((packed)); struct acx_ps_statistics { - u32 pspoll_timeouts; - u32 upsd_timeouts; - u32 upsd_max_sptime; - u32 upsd_max_apturn; - u32 pspoll_max_apturn; - u32 pspoll_utilization; - u32 upsd_utilization; + __le32 pspoll_timeouts; + __le32 upsd_timeouts; + __le32 upsd_max_sptime; + __le32 upsd_max_apturn; + __le32 pspoll_max_apturn; + __le32 pspoll_utilization; + __le32 upsd_utilization; } __attribute__ ((packed)); struct acx_rxpipe_statistics { - u32 rx_prep_beacon_drop; - u32 descr_host_int_trig_rx_data; - u32 beacon_buffer_thres_host_int_trig_rx_data; - u32 missed_beacon_host_int_trig_rx_data; - u32 tx_xfr_host_int_trig_rx_data; + __le32 rx_prep_beacon_drop; + __le32 descr_host_int_trig_rx_data; + __le32 beacon_buffer_thres_host_int_trig_rx_data; + __le32 missed_beacon_host_int_trig_rx_data; + __le32 tx_xfr_host_int_trig_rx_data; } __attribute__ ((packed)); struct acx_statistics { @@ -946,13 +829,8 @@ struct acx_statistics { struct acx_rxpipe_statistics rxpipe; } __attribute__ ((packed)); -#define ACX_MAX_RATE_CLASSES 8 -#define ACX_RATE_MASK_UNSPECIFIED 0 -#define ACX_RATE_MASK_ALL 0x1eff -#define ACX_RATE_RETRY_LIMIT 10 - struct acx_rate_class { - u32 enabled_rates; + __le32 enabled_rates; u8 short_retry_limit; u8 long_retry_limit; u8 aflags; @@ -962,47 +840,20 @@ struct acx_rate_class { struct acx_rate_policy { struct acx_header header; - u32 rate_class_cnt; - struct acx_rate_class rate_class[ACX_MAX_RATE_CLASSES]; + __le32 rate_class_cnt; + struct acx_rate_class rate_class[CONF_TX_MAX_RATE_CLASSES]; } __attribute__ ((packed)); -#define WL1271_ACX_AC_COUNT 4 - struct acx_ac_cfg { struct acx_header header; u8 ac; u8 cw_min; - u16 cw_max; + __le16 cw_max; u8 aifsn; u8 reserved; - u16 tx_op_limit; + __le16 tx_op_limit; } __attribute__ ((packed)); -enum wl1271_acx_ac { - WL1271_ACX_AC_BE = 0, - WL1271_ACX_AC_BK = 1, - WL1271_ACX_AC_VI = 2, - WL1271_ACX_AC_VO = 3, - WL1271_ACX_AC_CTS2SELF = 4, - WL1271_ACX_AC_ANY_TID = 0x1F, - WL1271_ACX_AC_INVALID = 0xFF, -}; - -enum wl1271_acx_ps_scheme { - WL1271_ACX_PS_SCHEME_LEGACY = 0, - WL1271_ACX_PS_SCHEME_UPSD_TRIGGER = 1, - WL1271_ACX_PS_SCHEME_LEGACY_PSPOLL = 2, - WL1271_ACX_PS_SCHEME_SAPSD = 3, -}; - -enum wl1271_acx_ack_policy { - WL1271_ACX_ACK_POLICY_LEGACY = 0, - WL1271_ACX_ACK_POLICY_NO_ACK = 1, - WL1271_ACX_ACK_POLICY_BLOCK = 2, -}; - -#define WL1271_ACX_TID_COUNT 7 - struct acx_tid_config { struct acx_header header; u8 queue_id; @@ -1011,22 +862,19 @@ struct acx_tid_config { u8 ps_scheme; u8 ack_policy; u8 padding[3]; - u32 apsd_conf[2]; + __le32 apsd_conf[2]; } __attribute__ ((packed)); struct acx_frag_threshold { struct acx_header header; - u16 frag_threshold; + __le16 frag_threshold; u8 padding[2]; } __attribute__ ((packed)); -#define WL1271_ACX_TX_COMPL_TIMEOUT 5 -#define WL1271_ACX_TX_COMPL_THRESHOLD 5 - struct acx_tx_config_options { struct acx_header header; - u16 tx_compl_timeout; /* msec */ - u16 tx_compl_threshold; /* number of packets */ + __le16 tx_compl_timeout; /* msec */ + __le16 tx_compl_threshold; /* number of packets */ } __attribute__ ((packed)); #define ACX_RX_MEM_BLOCKS 64 @@ -1041,79 +889,87 @@ struct wl1271_acx_config_memory { u8 tx_min_mem_block_num; u8 num_stations; u8 num_ssid_profiles; - u32 total_tx_descriptors; + __le32 total_tx_descriptors; } __attribute__ ((packed)); struct wl1271_acx_mem_map { struct acx_header header; - void *code_start; - void *code_end; + __le32 code_start; + __le32 code_end; - void *wep_defkey_start; - void *wep_defkey_end; + __le32 wep_defkey_start; + __le32 wep_defkey_end; - void *sta_table_start; - void *sta_table_end; + __le32 sta_table_start; + __le32 sta_table_end; - void *packet_template_start; - void *packet_template_end; + __le32 packet_template_start; + __le32 packet_template_end; /* Address of the TX result interface (control block) */ - u32 tx_result; - u32 tx_result_queue_start; + __le32 tx_result; + __le32 tx_result_queue_start; - void *queue_memory_start; - void *queue_memory_end; + __le32 queue_memory_start; + __le32 queue_memory_end; - u32 packet_memory_pool_start; - u32 packet_memory_pool_end; + __le32 packet_memory_pool_start; + __le32 packet_memory_pool_end; - void *debug_buffer1_start; - void *debug_buffer1_end; + __le32 debug_buffer1_start; + __le32 debug_buffer1_end; - void *debug_buffer2_start; - void *debug_buffer2_end; + __le32 debug_buffer2_start; + __le32 debug_buffer2_end; /* Number of blocks FW allocated for TX packets */ - u32 num_tx_mem_blocks; + __le32 num_tx_mem_blocks; /* Number of blocks FW allocated for RX packets */ - u32 num_rx_mem_blocks; + __le32 num_rx_mem_blocks; /* the following 4 fields are valid in SLAVE mode only */ u8 *tx_cbuf; u8 *rx_cbuf; - void *rx_ctrl; - void *tx_ctrl; + __le32 rx_ctrl; + __le32 tx_ctrl; } __attribute__ ((packed)); -enum wl1271_acx_rx_queue_type { - RX_QUEUE_TYPE_RX_LOW_PRIORITY, /* All except the high priority */ - RX_QUEUE_TYPE_RX_HIGH_PRIORITY, /* Management and voice packets */ - RX_QUEUE_TYPE_NUM, - RX_QUEUE_TYPE_MAX = USHORT_MAX -}; - -#define WL1271_RX_INTR_THRESHOLD_DEF 0 /* no pacing, send interrupt on - * every event */ -#define WL1271_RX_INTR_THRESHOLD_MIN 0 -#define WL1271_RX_INTR_THRESHOLD_MAX 15 - -#define WL1271_RX_INTR_TIMEOUT_DEF 5 -#define WL1271_RX_INTR_TIMEOUT_MIN 1 -#define WL1271_RX_INTR_TIMEOUT_MAX 100 - struct wl1271_acx_rx_config_opt { struct acx_header header; - u16 mblk_threshold; - u16 threshold; - u16 timeout; + __le16 mblk_threshold; + __le16 threshold; + __le16 timeout; u8 queue_type; u8 reserved; } __attribute__ ((packed)); + +struct wl1271_acx_bet_enable { + struct acx_header header; + + u8 enable; + u8 max_consecutive; + u8 padding[2]; +} __attribute__ ((packed)); + +#define ACX_IPV4_VERSION 4 +#define ACX_IPV6_VERSION 6 +#define ACX_IPV4_ADDR_SIZE 4 +struct wl1271_acx_arp_filter { + struct acx_header header; + u8 version; /* ACX_IPV4_VERSION, ACX_IPV6_VERSION */ + u8 enable; /* 1 to enable ARP filtering, 0 to disable */ + u8 padding[2]; + u8 address[16]; /* The configured device IP address - all ARP + requests directed to this IP address will pass + through. For IPv4, the first four bytes are + used. */ +} __attribute__((packed)); + + enum { ACX_WAKE_UP_CONDITIONS = 0x0002, ACX_MEM_CFG = 0x0003, @@ -1170,6 +1026,9 @@ enum { ACX_PEER_HT_CAP = 0x0057, ACX_HT_BSS_OPERATION = 0x0058, ACX_COEX_ACTIVITY = 0x0059, + ACX_SET_SMART_REFLEX_DEBUG = 0x005A, + ACX_SET_SMART_REFLEX_STATE = 0x005B, + ACX_SET_SMART_REFLEX_PARAMS = 0x005F, DOT11_RX_MSDU_LIFE_TIME = 0x1004, DOT11_CUR_TX_PWR = 0x100D, DOT11_RX_DOT11_MODE = 0x1012, @@ -1182,23 +1041,24 @@ enum { }; -int wl1271_acx_wake_up_conditions(struct wl1271 *wl, u8 wake_up_event, - u8 listen_interval); +int wl1271_acx_wake_up_conditions(struct wl1271 *wl); int wl1271_acx_sleep_auth(struct wl1271 *wl, u8 sleep_auth); int wl1271_acx_fw_version(struct wl1271 *wl, char *buf, size_t len); int wl1271_acx_tx_power(struct wl1271 *wl, int power); int wl1271_acx_feature_cfg(struct wl1271 *wl); int wl1271_acx_mem_map(struct wl1271 *wl, struct acx_header *mem_map, size_t len); -int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl, u32 life_time); +int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl); int wl1271_acx_rx_config(struct wl1271 *wl, u32 config, u32 filter); int wl1271_acx_pd_threshold(struct wl1271 *wl); int wl1271_acx_slot(struct wl1271 *wl, enum acx_slot_type slot_time); -int wl1271_acx_group_address_tbl(struct wl1271 *wl); +int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable, + void *mc_list, u32 mc_list_len); int wl1271_acx_service_period_timeout(struct wl1271 *wl); int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold); -int wl1271_acx_beacon_filter_opt(struct wl1271 *wl); +int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter); int wl1271_acx_beacon_filter_table(struct wl1271 *wl); +int wl1271_acx_conn_monit_params(struct wl1271 *wl); int wl1271_acx_sg_enable(struct wl1271 *wl); int wl1271_acx_sg_cfg(struct wl1271 *wl); int wl1271_acx_cca_threshold(struct wl1271 *wl); @@ -1207,9 +1067,9 @@ int wl1271_acx_aid(struct wl1271 *wl, u16 aid); int wl1271_acx_event_mbox_mask(struct wl1271 *wl, u32 event_mask); int wl1271_acx_set_preamble(struct wl1271 *wl, enum acx_preamble_type preamble); int wl1271_acx_cts_protect(struct wl1271 *wl, - enum acx_ctsprotect_type ctsprotect); + enum acx_ctsprotect_type ctsprotect); int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats); -int wl1271_acx_rate_policies(struct wl1271 *wl); +int wl1271_acx_rate_policies(struct wl1271 *wl, u32 enabled_rates); int wl1271_acx_ac_cfg(struct wl1271 *wl); int wl1271_acx_tid_cfg(struct wl1271 *wl); int wl1271_acx_frag_threshold(struct wl1271 *wl); @@ -1217,5 +1077,9 @@ int wl1271_acx_tx_config_options(struct wl1271 *wl); int wl1271_acx_mem_cfg(struct wl1271 *wl); int wl1271_acx_init_mem_config(struct wl1271 *wl); int wl1271_acx_init_rx_interrupt(struct wl1271 *wl); +int wl1271_acx_smart_reflex(struct wl1271 *wl); +int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable); +int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, u8 *address, + u8 version); #endif /* __WL1271_ACX_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.c b/drivers/net/wireless/wl12xx/wl1271_boot.c index 8228ef474a7e..b7c96454cca3 100644 --- a/drivers/net/wireless/wl12xx/wl1271_boot.c +++ b/drivers/net/wireless/wl12xx/wl1271_boot.c @@ -39,6 +39,14 @@ static struct wl1271_partition_set part_table[PART_TABLE_LEN] = { .start = REGISTERS_BASE, .size = 0x00008800 }, + .mem2 = { + .start = 0x00000000, + .size = 0x00000000 + }, + .mem3 = { + .start = 0x00000000, + .size = 0x00000000 + }, }, [PART_WORK] = { @@ -48,7 +56,15 @@ static struct wl1271_partition_set part_table[PART_TABLE_LEN] = { }, .reg = { .start = REGISTERS_BASE, - .size = 0x0000b000 + .size = 0x0000a000 + }, + .mem2 = { + .start = 0x003004f8, + .size = 0x00000004 + }, + .mem3 = { + .start = 0x00040404, + .size = 0x00000000 }, }, @@ -60,6 +76,14 @@ static struct wl1271_partition_set part_table[PART_TABLE_LEN] = { .reg = { .start = DRPW_BASE, .size = 0x00006000 + }, + .mem2 = { + .start = 0x00000000, + .size = 0x00000000 + }, + .mem3 = { + .start = 0x00000000, + .size = 0x00000000 } } }; @@ -69,19 +93,19 @@ static void wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag) u32 cpu_ctrl; /* 10.5.0 run the firmware (I) */ - cpu_ctrl = wl1271_reg_read32(wl, ACX_REG_ECPU_CONTROL); + cpu_ctrl = wl1271_spi_read32(wl, ACX_REG_ECPU_CONTROL); /* 10.5.1 run the firmware (II) */ cpu_ctrl |= flag; - wl1271_reg_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl); + wl1271_spi_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl); } static void wl1271_boot_fw_version(struct wl1271 *wl) { struct wl1271_static_data static_data; - wl1271_spi_mem_read(wl, wl->cmd_box_addr, - &static_data, sizeof(static_data)); + wl1271_spi_read(wl, wl->cmd_box_addr, + &static_data, sizeof(static_data), false); strncpy(wl->chip.fw_ver, static_data.fw_version, sizeof(wl->chip.fw_ver)); @@ -93,8 +117,9 @@ static void wl1271_boot_fw_version(struct wl1271 *wl) 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; + u8 *p, *chunk; /* whal_FwCtrl_LoadFwImageSm() */ @@ -103,16 +128,20 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, 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; } - wl1271_set_partition(wl, dest, - part_table[PART_DOWN].mem.size, - part_table[PART_DOWN].reg.start, - part_table[PART_DOWN].reg.size); + chunk = kmalloc(CHUNK_SIZE, GFP_KERNEL); + if (!chunk) { + wl1271_error("allocation for firmware upload chunk failed"); + return -ENOMEM; + } + + memcpy(&partition, &part_table[PART_DOWN], sizeof(partition)); + partition.mem.start = dest; + wl1271_set_partition(wl, &partition); /* 10.1 set partition limit and chunk num */ chunk_num = 0; @@ -125,21 +154,17 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, addr = dest + chunk_num * CHUNK_SIZE; partition_limit = chunk_num * CHUNK_SIZE + part_table[PART_DOWN].mem.size; - - /* FIXME: Over 80 chars! */ - wl1271_set_partition(wl, - addr, - part_table[PART_DOWN].mem.size, - part_table[PART_DOWN].reg.start, - part_table[PART_DOWN].reg.size); + partition.mem.start = addr; + wl1271_set_partition(wl, &partition); } /* 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); - wl1271_spi_mem_write(wl, addr, p, CHUNK_SIZE); + wl1271_spi_write(wl, addr, chunk, CHUNK_SIZE, false); chunk_num++; } @@ -147,28 +172,31 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, /* 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); - wl1271_spi_mem_write(wl, addr, p, fw_data_len % CHUNK_SIZE); + wl1271_spi_write(wl, addr, chunk, fw_data_len % CHUNK_SIZE, false); + kfree(chunk); return 0; } static int wl1271_boot_upload_firmware(struct wl1271 *wl) { u32 chunks, addr, len; + int ret = 0; u8 *fw; fw = wl->fw; - chunks = be32_to_cpup((u32 *) fw); + chunks = be32_to_cpup((__be32 *) fw); fw += sizeof(u32); wl1271_debug(DEBUG_BOOT, "firmware chunks to be uploaded: %u", chunks); while (chunks--) { - addr = be32_to_cpup((u32 *) fw); + addr = be32_to_cpup((__be32 *) fw); fw += sizeof(u32); - len = be32_to_cpup((u32 *) fw); + len = be32_to_cpup((__be32 *) fw); fw += sizeof(u32); if (len > 300000) { @@ -177,11 +205,13 @@ static int wl1271_boot_upload_firmware(struct wl1271 *wl) } wl1271_debug(DEBUG_BOOT, "chunk %d addr 0x%x len %u", chunks, addr, len); - wl1271_boot_upload_firmware_chunk(wl, fw, len, addr); + ret = wl1271_boot_upload_firmware_chunk(wl, fw, len, addr); + if (ret != 0) + break; fw += len; } - return 0; + return ret; } static int wl1271_boot_upload_nvs(struct wl1271 *wl) @@ -235,7 +265,7 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) wl1271_debug(DEBUG_BOOT, "nvs burst write 0x%x: 0x%x", dest_addr, val); - wl1271_reg_write32(wl, dest_addr, val); + wl1271_spi_write32(wl, dest_addr, val); nvs_ptr += 4; dest_addr += 4; @@ -253,20 +283,18 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) /* 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].mem.start, - part_table[PART_WORK].mem.size, - part_table[PART_WORK].reg.start, - part_table[PART_WORK].reg.size); + wl1271_set_partition(wl, &part_table[PART_WORK]); /* 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 */ /* 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_spi_mem_write(wl, CMD_MBOX_ADDRESS, nvs_aligned, nvs_len); + wl1271_spi_write(wl, CMD_MBOX_ADDRESS, nvs_aligned, nvs_len, false); kfree(nvs_aligned); return 0; @@ -275,9 +303,9 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) static void wl1271_boot_enable_interrupts(struct wl1271 *wl) { enable_irq(wl->irq); - wl1271_reg_write32(wl, ACX_REG_INTERRUPT_MASK, + wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK)); - wl1271_reg_write32(wl, HI_CFG, HI_CFG_DEF_VAL); + wl1271_spi_write32(wl, HI_CFG, HI_CFG_DEF_VAL); } static int wl1271_boot_soft_reset(struct wl1271 *wl) @@ -286,12 +314,13 @@ static int wl1271_boot_soft_reset(struct wl1271 *wl) u32 boot_data; /* perform soft reset */ - wl1271_reg_write32(wl, ACX_REG_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT); + wl1271_spi_write32(wl, ACX_REG_SLV_SOFT_RESET, + ACX_SLV_SOFT_RESET_BIT); /* SOFT_RESET is self clearing */ timeout = jiffies + usecs_to_jiffies(SOFT_RESET_MAX_TIME); while (1) { - boot_data = wl1271_reg_read32(wl, ACX_REG_SLV_SOFT_RESET); + boot_data = wl1271_spi_read32(wl, ACX_REG_SLV_SOFT_RESET); wl1271_debug(DEBUG_BOOT, "soft reset bootdata 0x%x", boot_data); if ((boot_data & ACX_SLV_SOFT_RESET_BIT) == 0) break; @@ -307,10 +336,10 @@ static int wl1271_boot_soft_reset(struct wl1271 *wl) } /* disable Rx/Tx */ - wl1271_reg_write32(wl, ENABLE, 0x0); + wl1271_spi_write32(wl, ENABLE, 0x0); /* disable auto calibration on start*/ - wl1271_reg_write32(wl, SPARE_A2, 0xffff); + wl1271_spi_write32(wl, SPARE_A2, 0xffff); return 0; } @@ -322,7 +351,7 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) wl1271_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT); - chip_id = wl1271_reg_read32(wl, CHIP_ID_B); + chip_id = wl1271_spi_read32(wl, CHIP_ID_B); wl1271_debug(DEBUG_BOOT, "chip id after firmware boot: 0x%x", chip_id); @@ -335,7 +364,8 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) loop = 0; while (loop++ < INIT_LOOP) { udelay(INIT_LOOP_DELAY); - interrupt = wl1271_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); + interrupt = wl1271_spi_read32(wl, + ACX_REG_INTERRUPT_NO_CLEAR); if (interrupt == 0xffffffff) { wl1271_error("error reading hardware complete " @@ -344,30 +374,26 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) } /* check that ACX_INTR_INIT_COMPLETE is enabled */ else if (interrupt & WL1271_ACX_INTR_INIT_COMPLETE) { - wl1271_reg_write32(wl, ACX_REG_INTERRUPT_ACK, + wl1271_spi_write32(wl, ACX_REG_INTERRUPT_ACK, WL1271_ACX_INTR_INIT_COMPLETE); break; } } - if (loop >= INIT_LOOP) { + if (loop > INIT_LOOP) { wl1271_error("timeout waiting for the hardware to " "complete initialization"); return -EIO; } /* get hardware config command mail box */ - wl->cmd_box_addr = wl1271_reg_read32(wl, REG_COMMAND_MAILBOX_PTR); + wl->cmd_box_addr = wl1271_spi_read32(wl, REG_COMMAND_MAILBOX_PTR); /* get hardware config event mail box */ - wl->event_box_addr = wl1271_reg_read32(wl, REG_EVENT_MAILBOX_PTR); + wl->event_box_addr = wl1271_spi_read32(wl, REG_EVENT_MAILBOX_PTR); /* set the working partition to its "running" mode offset */ - wl1271_set_partition(wl, - part_table[PART_WORK].mem.start, - part_table[PART_WORK].mem.size, - part_table[PART_WORK].reg.start, - part_table[PART_WORK].reg.size); + wl1271_set_partition(wl, &part_table[PART_WORK]); wl1271_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x event_box_addr 0x%x", wl->cmd_box_addr, wl->event_box_addr); @@ -379,11 +405,10 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) * ready to receive event from the command mailbox */ - /* enable gpio interrupts */ - wl1271_boot_enable_interrupts(wl); - - /* unmask all mbox events */ - wl->event_mask = 0xffffffff; + /* unmask required mbox events */ + wl->event_mask = BSS_LOSE_EVENT_ID | + SCAN_COMPLETE_EVENT_ID | + PS_REPORT_EVENT_ID; ret = wl1271_event_unmask(wl); if (ret < 0) { @@ -399,34 +424,13 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) static int wl1271_boot_write_irq_polarity(struct wl1271 *wl) { - u32 polarity, status, i; - - wl1271_reg_write32(wl, OCP_POR_CTR, OCP_REG_POLARITY); - wl1271_reg_write32(wl, OCP_CMD, OCP_CMD_READ); - - /* Wait until the command is complete (ie. bit 18 is set) */ - for (i = 0; i < OCP_CMD_LOOP; i++) { - polarity = wl1271_reg_read32(wl, OCP_DATA_READ); - if (polarity & OCP_READY_MASK) - break; - } - if (i == OCP_CMD_LOOP) { - wl1271_error("OCP command timeout!"); - return -EIO; - } + u32 polarity; - status = polarity & OCP_STATUS_MASK; - if (status != OCP_STATUS_OK) { - wl1271_error("OCP command failed (%d)", status); - return -EIO; - } + polarity = wl1271_top_reg_read(wl, OCP_REG_POLARITY); /* We use HIGH polarity, so unset the LOW bit */ polarity &= ~POLARITY_LOW; - - wl1271_reg_write32(wl, OCP_POR_CTR, OCP_REG_POLARITY); - wl1271_reg_write32(wl, OCP_DATA_WRITE, polarity); - wl1271_reg_write32(wl, OCP_CMD, OCP_CMD_WRITE); + wl1271_top_reg_write(wl, OCP_REG_POLARITY, polarity); return 0; } @@ -436,16 +440,32 @@ int wl1271_boot(struct wl1271 *wl) int ret = 0; u32 tmp, clk, pause; - if (REF_CLOCK == 0 || REF_CLOCK == 2) - /* ref clk: 19.2/38.4 */ + if (REF_CLOCK == 0 || REF_CLOCK == 2 || REF_CLOCK == 4) + /* ref clk: 19.2/38.4/38.4-XTAL */ clk = 0x3; else if (REF_CLOCK == 1 || REF_CLOCK == 3) /* ref clk: 26/52 */ clk = 0x5; - wl1271_reg_write32(wl, PLL_PARAMETERS, clk); + if (REF_CLOCK != 0) { + u16 val; + /* Set clock type */ + val = wl1271_top_reg_read(wl, OCP_REG_CLK_TYPE); + val &= FREF_CLK_TYPE_BITS; + val |= CLK_REQ_PRCM; + wl1271_top_reg_write(wl, OCP_REG_CLK_TYPE, val); + } else { + u16 val; + /* Set clock polarity */ + val = wl1271_top_reg_read(wl, OCP_REG_CLK_POLARITY); + val &= FREF_CLK_POLARITY_BITS; + val |= CLK_REQ_OUTN_SEL; + wl1271_top_reg_write(wl, OCP_REG_CLK_POLARITY, val); + } + + wl1271_spi_write32(wl, PLL_PARAMETERS, clk); - pause = wl1271_reg_read32(wl, PLL_PARAMETERS); + pause = wl1271_spi_read32(wl, PLL_PARAMETERS); wl1271_debug(DEBUG_BOOT, "pause1 0x%x", pause); @@ -454,39 +474,31 @@ int wl1271_boot(struct wl1271 *wl) * 0x3ff (magic number ). How does * this work?! */ pause |= WU_COUNTER_PAUSE_VAL; - wl1271_reg_write32(wl, WU_COUNTER_PAUSE, pause); + wl1271_spi_write32(wl, WU_COUNTER_PAUSE, pause); /* Continue the ELP wake up sequence */ - wl1271_reg_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); + wl1271_spi_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); udelay(500); - wl1271_set_partition(wl, - part_table[PART_DRPW].mem.start, - part_table[PART_DRPW].mem.size, - part_table[PART_DRPW].reg.start, - part_table[PART_DRPW].reg.size); + wl1271_set_partition(wl, &part_table[PART_DRPW]); /* Read-modify-write DRPW_SCRATCH_START register (see next state) to be used by DRPw FW. The RTRIM value will be added by the FW before taking DRPw out of reset */ wl1271_debug(DEBUG_BOOT, "DRPW_SCRATCH_START %08x", DRPW_SCRATCH_START); - clk = wl1271_reg_read32(wl, DRPW_SCRATCH_START); + clk = wl1271_spi_read32(wl, DRPW_SCRATCH_START); wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk); /* 2 */ clk |= (REF_CLOCK << 1) << 4; - wl1271_reg_write32(wl, DRPW_SCRATCH_START, clk); + wl1271_spi_write32(wl, DRPW_SCRATCH_START, clk); - wl1271_set_partition(wl, - part_table[PART_WORK].mem.start, - part_table[PART_WORK].mem.size, - part_table[PART_WORK].reg.start, - part_table[PART_WORK].reg.size); + wl1271_set_partition(wl, &part_table[PART_WORK]); /* Disable interrupts */ - wl1271_reg_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); + wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); ret = wl1271_boot_soft_reset(wl); if (ret < 0) @@ -501,21 +513,22 @@ int wl1271_boot(struct wl1271 *wl) * ACX_EEPROMLESS_IND_REG */ wl1271_debug(DEBUG_BOOT, "ACX_EEPROMLESS_IND_REG"); - wl1271_reg_write32(wl, ACX_EEPROMLESS_IND_REG, ACX_EEPROMLESS_IND_REG); + wl1271_spi_write32(wl, ACX_EEPROMLESS_IND_REG, + ACX_EEPROMLESS_IND_REG); - tmp = wl1271_reg_read32(wl, CHIP_ID_B); + tmp = wl1271_spi_read32(wl, CHIP_ID_B); wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp); /* 6. read the EEPROM parameters */ - tmp = wl1271_reg_read32(wl, SCR_PAD2); + tmp = wl1271_spi_read32(wl, SCR_PAD2); ret = wl1271_boot_write_irq_polarity(wl); if (ret < 0) goto out; /* FIXME: Need to check whether this is really what we want */ - wl1271_reg_write32(wl, ACX_REG_INTERRUPT_MASK, + wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_ALL_EVENTS_VECTOR); /* WL1271: The reference driver skips steps 7 to 10 (jumps directly @@ -530,6 +543,9 @@ int wl1271_boot(struct wl1271 *wl) if (ret < 0) goto out; + /* Enable firmware interrupts now */ + wl1271_boot_enable_interrupts(wl); + /* set the wl1271 default filters */ wl->rx_config = WL1271_DEFAULT_RX_CONFIG; wl->rx_filter = WL1271_DEFAULT_RX_FILTER; diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.h b/drivers/net/wireless/wl12xx/wl1271_boot.h index b0d8fb46a439..412443ee655a 100644 --- a/drivers/net/wireless/wl12xx/wl1271_boot.h +++ b/drivers/net/wireless/wl12xx/wl1271_boot.h @@ -50,23 +50,17 @@ struct wl1271_static_data { #define WU_COUNTER_PAUSE_VAL 0x3FF #define WELP_ARM_COMMAND_VAL 0x4 -#define OCP_CMD_LOOP 32 - -#define OCP_CMD_WRITE 0x1 -#define OCP_CMD_READ 0x2 - -#define OCP_READY_MASK BIT(18) -#define OCP_STATUS_MASK (BIT(16) | BIT(17)) - -#define OCP_STATUS_NO_RESP 0x00000 -#define OCP_STATUS_OK 0x10000 -#define OCP_STATUS_REQ_FAILED 0x20000 -#define OCP_STATUS_RESP_ERROR 0x30000 - -#define OCP_REG_POLARITY 0x30032 +#define OCP_REG_POLARITY 0x0064 +#define OCP_REG_CLK_TYPE 0x0448 +#define OCP_REG_CLK_POLARITY 0x0cb2 #define CMD_MBOX_ADDRESS 0x407B4 #define POLARITY_LOW BIT(1) +#define FREF_CLK_TYPE_BITS 0xfffffe7f +#define CLK_REQ_PRCM 0x100 +#define FREF_CLK_POLARITY_BITS 0xfffff8ff +#define CLK_REQ_OUTN_SEL 0x700 + #endif diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c index 2a4351ff54dc..886a9bc39cc1 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.c +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c @@ -42,26 +42,28 @@ * @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) +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; cmd = buf; - cmd->id = id; + cmd->id = cpu_to_le16(id); cmd->status = 0; WARN_ON(len % 4 != 0); - wl1271_spi_mem_write(wl, wl->cmd_box_addr, buf, len); + wl1271_spi_write(wl, wl->cmd_box_addr, buf, len, false); - wl1271_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_CMD); + wl1271_spi_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_CMD); timeout = jiffies + msecs_to_jiffies(WL1271_COMMAND_TIMEOUT); - intr = wl1271_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); + intr = wl1271_spi_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); while (!(intr & WL1271_ACX_INTR_CMD_COMPLETE)) { if (time_after(jiffies, timeout)) { wl1271_error("command complete timeout"); @@ -71,17 +73,28 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len) msleep(1); - intr = wl1271_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); + intr = wl1271_spi_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); } - wl1271_reg_write32(wl, ACX_REG_INTERRUPT_ACK, + /* read back the status code of the command */ + if (res_len == 0) + res_len = sizeof(struct wl1271_cmd_header); + wl1271_spi_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); + ret = -EIO; + } + + wl1271_spi_write32(wl, ACX_REG_INTERRUPT_ACK, WL1271_ACX_INTR_CMD_COMPLETE); out: return ret; } -int wl1271_cmd_cal_channel_tune(struct wl1271 *wl) +static int wl1271_cmd_cal_channel_tune(struct wl1271 *wl) { struct wl1271_cmd_cal_channel_tune *cmd; int ret = 0; @@ -104,7 +117,7 @@ int wl1271_cmd_cal_channel_tune(struct wl1271 *wl) return ret; } -int wl1271_cmd_cal_update_ref_point(struct wl1271 *wl) +static int wl1271_cmd_cal_update_ref_point(struct wl1271 *wl) { struct wl1271_cmd_cal_update_ref_point *cmd; int ret = 0; @@ -129,7 +142,7 @@ int wl1271_cmd_cal_update_ref_point(struct wl1271 *wl) return ret; } -int wl1271_cmd_cal_p2g(struct wl1271 *wl) +static int wl1271_cmd_cal_p2g(struct wl1271 *wl) { struct wl1271_cmd_cal_p2g *cmd; int ret = 0; @@ -150,7 +163,7 @@ int wl1271_cmd_cal_p2g(struct wl1271 *wl) return ret; } -int wl1271_cmd_cal(struct wl1271 *wl) +static int wl1271_cmd_cal(struct wl1271 *wl) { /* * FIXME: we must make sure that we're not sleeping when calibration @@ -175,11 +188,116 @@ int wl1271_cmd_cal(struct wl1271 *wl) return ret; } -int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type, u8 dtim_interval, - u16 beacon_interval, u8 wait) +int wl1271_cmd_general_parms(struct wl1271 *wl) +{ + struct wl1271_general_parms_cmd *gen_parms; + struct conf_general_parms *g = &wl->conf.init.genparam; + int ret; + + gen_parms = kzalloc(sizeof(*gen_parms), GFP_KERNEL); + if (!gen_parms) + return -ENOMEM; + + gen_parms->test.id = TEST_CMD_INI_FILE_GENERAL_PARAM; + + gen_parms->ref_clk = g->ref_clk; + gen_parms->settling_time = g->settling_time; + gen_parms->clk_valid_on_wakeup = g->clk_valid_on_wakeup; + gen_parms->dc2dcmode = g->dc2dcmode; + gen_parms->single_dual_band = g->single_dual_band; + gen_parms->tx_bip_fem_autodetect = g->tx_bip_fem_autodetect; + gen_parms->tx_bip_fem_manufacturer = g->tx_bip_fem_manufacturer; + gen_parms->settings = g->settings; + + ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), 0); + if (ret < 0) + wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed"); + + kfree(gen_parms); + return ret; +} + +int wl1271_cmd_radio_parms(struct wl1271 *wl) +{ + struct wl1271_radio_parms_cmd *radio_parms; + struct conf_radio_parms *r = &wl->conf.init.radioparam; + int i, ret; + + radio_parms = kzalloc(sizeof(*radio_parms), GFP_KERNEL); + if (!radio_parms) + return -ENOMEM; + + radio_parms->test.id = TEST_CMD_INI_FILE_RADIO_PARAM; + + /* Static radio parameters */ + radio_parms->rx_trace_loss = r->rx_trace_loss; + radio_parms->tx_trace_loss = r->tx_trace_loss; + memcpy(radio_parms->rx_rssi_and_proc_compens, + r->rx_rssi_and_proc_compens, + CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE); + + memcpy(radio_parms->rx_trace_loss_5, r->rx_trace_loss_5, + CONF_NUMBER_OF_SUB_BANDS_5); + memcpy(radio_parms->tx_trace_loss_5, r->tx_trace_loss_5, + CONF_NUMBER_OF_SUB_BANDS_5); + memcpy(radio_parms->rx_rssi_and_proc_compens_5, + r->rx_rssi_and_proc_compens_5, + CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE); + + /* Dynamic radio parameters */ + radio_parms->tx_ref_pd_voltage = cpu_to_le16(r->tx_ref_pd_voltage); + radio_parms->tx_ref_power = r->tx_ref_power; + radio_parms->tx_offset_db = r->tx_offset_db; + + memcpy(radio_parms->tx_rate_limits_normal, r->tx_rate_limits_normal, + CONF_NUMBER_OF_RATE_GROUPS); + memcpy(radio_parms->tx_rate_limits_degraded, r->tx_rate_limits_degraded, + CONF_NUMBER_OF_RATE_GROUPS); + + memcpy(radio_parms->tx_channel_limits_11b, r->tx_channel_limits_11b, + CONF_NUMBER_OF_CHANNELS_2_4); + memcpy(radio_parms->tx_channel_limits_ofdm, r->tx_channel_limits_ofdm, + CONF_NUMBER_OF_CHANNELS_2_4); + memcpy(radio_parms->tx_pdv_rate_offsets, r->tx_pdv_rate_offsets, + CONF_NUMBER_OF_RATE_GROUPS); + memcpy(radio_parms->tx_ibias, r->tx_ibias, CONF_NUMBER_OF_RATE_GROUPS); + + radio_parms->rx_fem_insertion_loss = r->rx_fem_insertion_loss; + + for (i = 0; i < CONF_NUMBER_OF_SUB_BANDS_5; i++) + radio_parms->tx_ref_pd_voltage_5[i] = + cpu_to_le16(r->tx_ref_pd_voltage_5[i]); + memcpy(radio_parms->tx_ref_power_5, r->tx_ref_power_5, + CONF_NUMBER_OF_SUB_BANDS_5); + memcpy(radio_parms->tx_offset_db_5, r->tx_offset_db_5, + CONF_NUMBER_OF_SUB_BANDS_5); + memcpy(radio_parms->tx_rate_limits_normal_5, + r->tx_rate_limits_normal_5, CONF_NUMBER_OF_RATE_GROUPS); + memcpy(radio_parms->tx_rate_limits_degraded_5, + r->tx_rate_limits_degraded_5, CONF_NUMBER_OF_RATE_GROUPS); + memcpy(radio_parms->tx_channel_limits_ofdm_5, + r->tx_channel_limits_ofdm_5, CONF_NUMBER_OF_CHANNELS_5); + memcpy(radio_parms->tx_pdv_rate_offsets_5, r->tx_pdv_rate_offsets_5, + CONF_NUMBER_OF_RATE_GROUPS); + memcpy(radio_parms->tx_ibias_5, r->tx_ibias_5, + CONF_NUMBER_OF_RATE_GROUPS); + memcpy(radio_parms->rx_fem_insertion_loss_5, + r->rx_fem_insertion_loss_5, CONF_NUMBER_OF_SUB_BANDS_5); + + wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_RADIO_PARAM: ", + radio_parms, sizeof(*radio_parms)); + + ret = wl1271_cmd_test(wl, radio_parms, sizeof(*radio_parms), 0); + if (ret < 0) + wl1271_warning("CMD_INI_FILE_RADIO_PARAM failed"); + + kfree(radio_parms); + return ret; +} + +int wl1271_cmd_join(struct wl1271 *wl) { static bool do_cal = true; - unsigned long timeout; struct wl1271_cmd_join *join; int ret, i; u8 *bssid; @@ -193,6 +311,18 @@ int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type, u8 dtim_interval, do_cal = false; } + /* FIXME: This is a workaround, because with the current stack, we + * cannot know when we have disassociated. So, if we have already + * joined, we disconnect before joining again. */ + if (wl->joined) { + ret = wl1271_cmd_disconnect(wl); + if (ret < 0) { + wl1271_error("failed to disconnect before rejoining"); + goto out; + } + + wl->joined = false; + } join = kzalloc(sizeof(*join), GFP_KERNEL); if (!join) { @@ -207,15 +337,34 @@ int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type, u8 dtim_interval, for (i = 0; i < ETH_ALEN; i++) bssid[i] = wl->bssid[ETH_ALEN - i - 1]; - join->rx_config_options = wl->rx_config; - join->rx_filter_options = wl->rx_filter; + join->rx_config_options = cpu_to_le32(wl->rx_config); + join->rx_filter_options = cpu_to_le32(wl->rx_filter); + join->bss_type = wl->bss_type; - join->basic_rate_set = RATE_MASK_1MBPS | RATE_MASK_2MBPS | - RATE_MASK_5_5MBPS | RATE_MASK_11MBPS; + /* + * FIXME: disable temporarily all filters because after commit + * 9cef8737 "mac80211: fix managed mode BSSID handling" broke + * association. The filter logic needs to be implemented properly + * and once that is done, this hack can be removed. + */ + join->rx_config_options = cpu_to_le32(0); + join->rx_filter_options = cpu_to_le32(WL1271_DEFAULT_RX_FILTER); + + if (wl->band == IEEE80211_BAND_2GHZ) + join->basic_rate_set = cpu_to_le32(CONF_HW_BIT_RATE_1MBPS | + CONF_HW_BIT_RATE_2MBPS | + CONF_HW_BIT_RATE_5_5MBPS | + CONF_HW_BIT_RATE_11MBPS); + else { + join->bss_type |= WL1271_JOIN_CMD_BSS_TYPE_5GHZ; + join->basic_rate_set = cpu_to_le32(CONF_HW_BIT_RATE_6MBPS | + CONF_HW_BIT_RATE_12MBPS | + CONF_HW_BIT_RATE_24MBPS); + } + + join->beacon_interval = cpu_to_le16(WL1271_DEFAULT_BEACON_INT); + join->dtim_interval = WL1271_DEFAULT_DTIM_PERIOD; - join->beacon_interval = beacon_interval; - join->dtim_interval = dtim_interval; - join->bss_type = bss_type; join->channel = wl->channel; join->ssid_len = wl->ssid_len; memcpy(join->ssid, wl->ssid, wl->ssid_len); @@ -228,21 +377,24 @@ int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type, u8 dtim_interval, join->ctrl |= wl->session_counter << WL1271_JOIN_CMD_TX_SESSION_OFFSET; + /* reset TX security counters */ + wl->tx_security_last_seq = 0; + wl->tx_security_seq_16 = 0; + wl->tx_security_seq_32 = 0; - ret = wl1271_cmd_send(wl, CMD_START_JOIN, join, sizeof(*join)); + ret = wl1271_cmd_send(wl, CMD_START_JOIN, join, sizeof(*join), 0); if (ret < 0) { wl1271_error("failed to initiate cmd join"); goto out_free; } - timeout = msecs_to_jiffies(JOIN_TIMEOUT); + wl->joined = true; /* * ugly hack: we should wait for JOIN_EVENT_COMPLETE_ID but to * simplify locking we just sleep instead, for now */ - if (wait) - msleep(10); + msleep(10); out_free: kfree(join); @@ -262,34 +414,21 @@ out: int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer) { int ret; + size_t res_len = 0; wl1271_debug(DEBUG_CMD, "cmd test"); - ret = wl1271_cmd_send(wl, CMD_TEST, buf, buf_len); + if (answer) + res_len = buf_len; + + ret = wl1271_cmd_send(wl, CMD_TEST, buf, buf_len, res_len); if (ret < 0) { wl1271_warning("TEST command failed"); return ret; } - if (answer) { - struct wl1271_command *cmd_answer; - - /* - * The test command got in, we can read the answer. - * The answer would be a wl1271_command, where the - * parameter array contains the actual answer. - */ - wl1271_spi_mem_read(wl, wl->cmd_box_addr, buf, buf_len); - - cmd_answer = buf; - - if (cmd_answer->header.status != CMD_STATUS_SUCCESS) - wl1271_error("TEST command answer error: %d", - cmd_answer->header.status); - } - - return 0; + return ret; } /** @@ -307,26 +446,15 @@ int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len) wl1271_debug(DEBUG_CMD, "cmd interrogate"); - acx->id = id; + acx->id = cpu_to_le16(id); /* payload length, does not include any headers */ - acx->len = len - sizeof(*acx); + acx->len = cpu_to_le16(len - sizeof(*acx)); - ret = wl1271_cmd_send(wl, CMD_INTERROGATE, acx, sizeof(*acx)); - if (ret < 0) { + ret = wl1271_cmd_send(wl, CMD_INTERROGATE, acx, sizeof(*acx), len); + if (ret < 0) wl1271_error("INTERROGATE command failed"); - goto out; - } - /* the interrogate command got in, we can read the answer */ - wl1271_spi_mem_read(wl, wl->cmd_box_addr, buf, len); - - acx = buf; - if (acx->cmd.status != CMD_STATUS_SUCCESS) - wl1271_error("INTERROGATE command error: %d", - acx->cmd.status); - -out: return ret; } @@ -345,12 +473,12 @@ int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len) wl1271_debug(DEBUG_CMD, "cmd configure"); - acx->id = id; + acx->id = cpu_to_le16(id); /* payload length, does not include any headers */ - acx->len = len - sizeof(*acx); + acx->len = cpu_to_le16(len - sizeof(*acx)); - ret = wl1271_cmd_send(wl, CMD_CONFIGURE, acx, len); + ret = wl1271_cmd_send(wl, CMD_CONFIGURE, acx, len, 0); if (ret < 0) { wl1271_warning("CONFIGURE command NOK"); return ret; @@ -383,7 +511,7 @@ int wl1271_cmd_data_path(struct wl1271 *wl, u8 channel, bool enable) cmd_tx = CMD_DISABLE_TX; } - ret = wl1271_cmd_send(wl, cmd_rx, cmd, sizeof(*cmd)); + ret = wl1271_cmd_send(wl, cmd_rx, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("rx %s cmd for channel %d failed", enable ? "start" : "stop", channel); @@ -393,7 +521,7 @@ int wl1271_cmd_data_path(struct wl1271 *wl, u8 channel, bool enable) wl1271_debug(DEBUG_BOOT, "rx %s cmd channel %d", enable ? "start" : "stop", channel); - ret = wl1271_cmd_send(wl, cmd_tx, cmd, sizeof(*cmd)); + ret = wl1271_cmd_send(wl, cmd_tx, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("tx %s cmd for channel %d failed", enable ? "start" : "stop", channel); @@ -414,8 +542,7 @@ int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode) int ret = 0; /* FIXME: this should be in ps.c */ - ret = wl1271_acx_wake_up_conditions(wl, WAKE_UP_EVENT_DTIM_BITMAP, - wl->listen_int); + ret = wl1271_acx_wake_up_conditions(wl); if (ret < 0) { wl1271_error("couldn't set wake up conditions"); goto out; @@ -433,10 +560,10 @@ int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode) ps_params->send_null_data = 1; ps_params->retries = 5; ps_params->hang_over_period = 128; - ps_params->null_data_rate = 1; /* 1 Mbps */ + ps_params->null_data_rate = cpu_to_le32(1); /* 1 Mbps */ ret = wl1271_cmd_send(wl, CMD_SET_PS_MODE, ps_params, - sizeof(*ps_params)); + sizeof(*ps_params), 0); if (ret < 0) { wl1271_error("cmd set_ps_mode failed"); goto out; @@ -464,22 +591,17 @@ int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer, WARN_ON(len > MAX_READ_SIZE); len = min_t(size_t, len, MAX_READ_SIZE); - cmd->addr = addr; - cmd->size = len; + cmd->addr = cpu_to_le32(addr); + cmd->size = cpu_to_le32(len); - ret = wl1271_cmd_send(wl, CMD_READ_MEMORY, cmd, sizeof(*cmd)); + ret = wl1271_cmd_send(wl, CMD_READ_MEMORY, cmd, sizeof(*cmd), + sizeof(*cmd)); if (ret < 0) { wl1271_error("read memory command failed: %d", ret); goto out; } - /* the read command got in, we can now read the answer */ - wl1271_spi_mem_read(wl, wl->cmd_box_addr, cmd, sizeof(*cmd)); - - if (cmd->header.status != CMD_STATUS_SUCCESS) - wl1271_error("error in read command result: %d", - cmd->header.status); - + /* the read command got in */ memcpy(answer, cmd->value, len); out: @@ -488,14 +610,31 @@ out: } int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, - u8 active_scan, u8 high_prio, u8 num_channels, + u8 active_scan, u8 high_prio, u8 band, u8 probe_requests) { struct wl1271_cmd_trigger_scan_to *trigger = NULL; struct wl1271_cmd_scan *params = NULL; - int i, ret; + struct ieee80211_channel *channels; + int i, j, n_ch, ret; u16 scan_options = 0; + u8 ieee_band; + + if (band == WL1271_SCAN_BAND_2_4_GHZ) + ieee_band = IEEE80211_BAND_2GHZ; + else if (band == WL1271_SCAN_BAND_DUAL && wl1271_11a_enabled()) + ieee_band = IEEE80211_BAND_2GHZ; + else if (band == WL1271_SCAN_BAND_5_GHZ && wl1271_11a_enabled()) + ieee_band = IEEE80211_BAND_5GHZ; + else + return -EINVAL; + + if (wl->hw->wiphy->bands[ieee_band]->channels == NULL) + return -EINVAL; + + channels = wl->hw->wiphy->bands[ieee_band]->channels; + n_ch = wl->hw->wiphy->bands[ieee_band]->n_channels; if (wl->scanning) return -EINVAL; @@ -512,32 +651,43 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, scan_options |= WL1271_SCAN_OPT_PASSIVE; if (high_prio) scan_options |= WL1271_SCAN_OPT_PRIORITY_HIGH; - params->params.scan_options = scan_options; + params->params.scan_options = cpu_to_le16(scan_options); - params->params.num_channels = num_channels; params->params.num_probe_requests = probe_requests; - params->params.tx_rate = cpu_to_le32(RATE_MASK_2MBPS); + /* Let the fw autodetect suitable tx_rate for probes */ + params->params.tx_rate = 0; params->params.tid_trigger = 0; params->params.scan_tag = WL1271_SCAN_DEFAULT_TAG; - for (i = 0; i < num_channels; i++) { - params->channels[i].min_duration = - cpu_to_le32(WL1271_SCAN_CHAN_MIN_DURATION); - params->channels[i].max_duration = - cpu_to_le32(WL1271_SCAN_CHAN_MAX_DURATION); - memset(¶ms->channels[i].bssid_lsb, 0xff, 4); - memset(¶ms->channels[i].bssid_msb, 0xff, 2); - params->channels[i].early_termination = 0; - params->channels[i].tx_power_att = WL1271_SCAN_CURRENT_TX_PWR; - params->channels[i].channel = i + 1; + if (band == WL1271_SCAN_BAND_DUAL) + params->params.band = WL1271_SCAN_BAND_2_4_GHZ; + else + params->params.band = band; + + for (i = 0, j = 0; i < n_ch && i < WL1271_SCAN_MAX_CHANNELS; i++) { + if (!(channels[i].flags & IEEE80211_CHAN_DISABLED)) { + params->channels[j].min_duration = + cpu_to_le32(WL1271_SCAN_CHAN_MIN_DURATION); + params->channels[j].max_duration = + cpu_to_le32(WL1271_SCAN_CHAN_MAX_DURATION); + memset(¶ms->channels[j].bssid_lsb, 0xff, 4); + memset(¶ms->channels[j].bssid_msb, 0xff, 2); + params->channels[j].early_termination = 0; + params->channels[j].tx_power_att = + WL1271_SCAN_CURRENT_TX_PWR; + params->channels[j].channel = channels[i].hw_value; + j++; + } } + params->params.num_channels = j; + if (len && ssid) { params->params.ssid_len = len; memcpy(params->params.ssid, ssid, len); } - ret = wl1271_cmd_build_probe_req(wl, ssid, len); + ret = wl1271_cmd_build_probe_req(wl, ssid, len, ieee_band); if (ret < 0) { wl1271_error("PROBE request template failed"); goto out; @@ -553,7 +703,7 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, trigger->timeout = 0; ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger, - sizeof(*trigger)); + sizeof(*trigger), 0); if (ret < 0) { wl1271_error("trigger scan to failed for hw scan"); goto out; @@ -562,20 +712,24 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, wl1271_dump(DEBUG_SCAN, "SCAN: ", params, sizeof(*params)); wl->scanning = true; + if (wl1271_11a_enabled()) { + wl->scan.state = band; + if (band == WL1271_SCAN_BAND_DUAL) { + wl->scan.active = active_scan; + wl->scan.high_prio = high_prio; + wl->scan.probe_requests = probe_requests; + if (len && ssid) { + wl->scan.ssid_len = len; + memcpy(wl->scan.ssid, ssid, len); + } else + wl->scan.ssid_len = 0; + } + } - ret = wl1271_cmd_send(wl, CMD_SCAN, params, sizeof(*params)); + ret = wl1271_cmd_send(wl, CMD_SCAN, params, sizeof(*params), 0); if (ret < 0) { wl1271_error("SCAN failed"); - goto out; - } - - wl1271_spi_mem_read(wl, wl->cmd_box_addr, params, sizeof(*params)); - - if (params->header.status != CMD_STATUS_SUCCESS) { - wl1271_error("Scan command error: %d", - params->header.status); wl->scanning = false; - ret = -EIO; goto out; } @@ -603,14 +757,14 @@ int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, cmd->len = cpu_to_le16(buf_len); cmd->template_type = template_id; - cmd->enabled_rates = ACX_RATE_MASK_UNSPECIFIED; - cmd->short_retry_limit = ACX_RATE_RETRY_LIMIT; - cmd->long_retry_limit = ACX_RATE_RETRY_LIMIT; + cmd->enabled_rates = cpu_to_le32(wl->conf.tx.rc_conf.enabled_rates); + cmd->short_retry_limit = wl->conf.tx.rc_conf.short_retry_limit; + cmd->long_retry_limit = wl->conf.tx.rc_conf.long_retry_limit; if (buf) memcpy(cmd->template_data, buf, buf_len); - ret = wl1271_cmd_send(wl, CMD_SET_TEMPLATE, cmd, sizeof(*cmd)); + ret = wl1271_cmd_send(wl, CMD_SET_TEMPLATE, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_warning("cmd set_template failed: %d", ret); goto out_free; @@ -623,30 +777,62 @@ out: return ret; } -static int wl1271_build_basic_rates(char *rates) +static int wl1271_build_basic_rates(char *rates, u8 band) { u8 index = 0; - rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB; - rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB; - rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_5MB; - rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_11MB; + if (band == IEEE80211_BAND_2GHZ) { + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB; + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB; + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_5MB; + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_11MB; + } else if (band == IEEE80211_BAND_5GHZ) { + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_6MB; + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_12MB; + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_24MB; + } else { + wl1271_error("build_basic_rates invalid band: %d", band); + } return index; } -static int wl1271_build_extended_rates(char *rates) +static int wl1271_build_extended_rates(char *rates, u8 band) { u8 index = 0; - rates[index++] = IEEE80211_OFDM_RATE_6MB; - rates[index++] = IEEE80211_OFDM_RATE_9MB; - rates[index++] = IEEE80211_OFDM_RATE_12MB; - rates[index++] = IEEE80211_OFDM_RATE_18MB; - rates[index++] = IEEE80211_OFDM_RATE_24MB; - rates[index++] = IEEE80211_OFDM_RATE_36MB; - rates[index++] = IEEE80211_OFDM_RATE_48MB; - rates[index++] = IEEE80211_OFDM_RATE_54MB; + if (band == IEEE80211_BAND_2GHZ) { + rates[index++] = IEEE80211_OFDM_RATE_6MB; + rates[index++] = IEEE80211_OFDM_RATE_9MB; + rates[index++] = IEEE80211_OFDM_RATE_12MB; + rates[index++] = IEEE80211_OFDM_RATE_18MB; + rates[index++] = IEEE80211_OFDM_RATE_24MB; + rates[index++] = IEEE80211_OFDM_RATE_36MB; + rates[index++] = IEEE80211_OFDM_RATE_48MB; + rates[index++] = IEEE80211_OFDM_RATE_54MB; + } else if (band == IEEE80211_BAND_5GHZ) { + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_9MB; + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_18MB; + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_24MB; + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_36MB; + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_48MB; + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_54MB; + } else { + wl1271_error("build_basic_rates invalid band: %d", band); + } return index; } @@ -665,7 +851,8 @@ int wl1271_cmd_build_null_data(struct wl1271 *wl) memcpy(template.header.sa, wl->mac_addr, ETH_ALEN); template.header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_DATA | - IEEE80211_STYPE_NULLFUNC); + IEEE80211_STYPE_NULLFUNC | + IEEE80211_FCTL_TODS); return wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, &template, sizeof(template)); @@ -678,7 +865,10 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid) memcpy(template.bssid, wl->bssid, ETH_ALEN); memcpy(template.ta, wl->mac_addr, ETH_ALEN); - template.aid = aid; + + /* aid in PS-Poll has its two MSBs each set to 1 */ + template.aid = cpu_to_le16(1 << 15 | 1 << 14 | aid); + template.fc = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); return wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, &template, @@ -686,12 +876,14 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid) } -int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len) +int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len, + u8 band) { struct wl12xx_probe_req_template template; struct wl12xx_ie_rates *rates; char *ptr; u16 size; + int ret; ptr = (char *)&template; size = sizeof(struct ieee80211_header); @@ -713,20 +905,25 @@ int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len) /* Basic Rates */ rates = (struct wl12xx_ie_rates *)ptr; rates->header.id = WLAN_EID_SUPP_RATES; - rates->header.len = wl1271_build_basic_rates(rates->rates); + rates->header.len = wl1271_build_basic_rates(rates->rates, band); size += sizeof(struct wl12xx_ie_header) + rates->header.len; ptr += sizeof(struct wl12xx_ie_header) + rates->header.len; /* Extended rates */ rates = (struct wl12xx_ie_rates *)ptr; rates->header.id = WLAN_EID_EXT_SUPP_RATES; - rates->header.len = wl1271_build_extended_rates(rates->rates); + rates->header.len = wl1271_build_extended_rates(rates->rates, band); size += sizeof(struct wl12xx_ie_header) + rates->header.len; wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", &template, size); - return wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, - &template, size); + if (band == IEEE80211_BAND_2GHZ) + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, + &template, size); + else + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, + &template, size); + return ret; } int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id) @@ -743,10 +940,10 @@ int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id) } cmd->id = id; - cmd->key_action = KEY_SET_ID; + cmd->key_action = cpu_to_le16(KEY_SET_ID); cmd->key_type = KEY_WEP; - ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd)); + ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_warning("cmd set_default_wep_key failed: %d", ret); goto out; @@ -759,7 +956,8 @@ out: } int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, - u8 key_size, const u8 *key, const u8 *addr) + u8 key_size, const u8 *key, const u8 *addr, + u32 tx_seq_32, u16 tx_seq_16) { struct wl1271_cmd_set_keys *cmd; int ret = 0; @@ -773,16 +971,18 @@ int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, if (key_type != KEY_WEP) memcpy(cmd->addr, addr, ETH_ALEN); - cmd->key_action = action; + cmd->key_action = cpu_to_le16(action); cmd->key_size = key_size; cmd->key_type = key_type; + cmd->ac_seq_num16[0] = cpu_to_le16(tx_seq_16); + cmd->ac_seq_num32[0] = cpu_to_le32(tx_seq_32); + /* we have only one SSID profile */ cmd->ssid_profile = 0; cmd->id = id; - /* FIXME: this is from wl1251, needs to be checked */ if (key_type == KEY_TKIP) { /* * We get the key in the following form: @@ -800,7 +1000,7 @@ int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, wl1271_dump(DEBUG_CRYPT, "TARGET KEY: ", cmd, sizeof(*cmd)); - ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd)); + ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_warning("could not set keys"); goto out; @@ -811,3 +1011,34 @@ out: return ret; } + +int wl1271_cmd_disconnect(struct wl1271 *wl) +{ + struct wl1271_cmd_disconnect *cmd; + int ret = 0; + + wl1271_debug(DEBUG_CMD, "cmd disconnect"); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + cmd->rx_config_options = cpu_to_le32(wl->rx_config); + cmd->rx_filter_options = cpu_to_le32(wl->rx_filter); + /* disconnect reason is not used in immediate disconnections */ + cmd->type = DISCONNECT_IMMEDIATE; + + ret = wl1271_cmd_send(wl, CMD_DISCONNECT, cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("failed to send disconnect command"); + goto out_free; + } + +out_free: + kfree(cmd); + +out: + return ret; +} diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.h b/drivers/net/wireless/wl12xx/wl1271_cmd.h index 951a8447a516..b4fa4acb9229 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.h +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.h @@ -29,9 +29,11 @@ struct acx_header; -int wl1271_cmd_send(struct wl1271 *wl, u16 type, void *buf, size_t buf_len); -int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type, u8 dtim_interval, - u16 beacon_interval, u8 wait); +int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, + size_t res_len); +int wl1271_cmd_general_parms(struct wl1271 *wl); +int wl1271_cmd_radio_parms(struct wl1271 *wl); +int wl1271_cmd_join(struct wl1271 *wl); int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer); int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len); int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len); @@ -40,16 +42,19 @@ int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode); int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer, size_t len); int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, - u8 active_scan, u8 high_prio, u8 num_channels, + u8 active_scan, u8 high_prio, u8 band, u8 probe_requests); int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, void *buf, size_t buf_len); int wl1271_cmd_build_null_data(struct wl1271 *wl); int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid); -int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len); +int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len, + u8 band); int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id); int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, - u8 key_size, const u8 *key, const u8 *addr); + u8 key_size, const u8 *key, const u8 *addr, + u32 tx_seq_32, u16 tx_seq_16); +int wl1271_cmd_disconnect(struct wl1271 *wl); enum wl1271_commands { CMD_INTERROGATE = 1, /*use this to read information elements*/ @@ -118,8 +123,8 @@ enum cmd_templ { #define WL1271_CMD_TEMPL_MAX_SIZE 252 struct wl1271_cmd_header { - u16 id; - u16 status; + __le16 id; + __le16 status; /* payload */ u8 data[0]; } __attribute__ ((packed)); @@ -172,17 +177,17 @@ struct cmd_read_write_memory { struct wl1271_cmd_header header; /* The address of the memory to read from or write to.*/ - u32 addr; + __le32 addr; /* The amount of data in bytes to read from or write to the WiLink * device.*/ - u32 size; + __le32 size; /* The actual value read from or written to the Wilink. The source of this field is the Host in WRITE command or the Wilink in READ command. */ u8 value[MAX_READ_SIZE]; -}; +} __attribute__ ((packed)); #define CMDMBOX_HEADER_LEN 4 #define CMDMBOX_INFO_ELEM_HEADER_LEN 4 @@ -196,22 +201,23 @@ enum { #define WL1271_JOIN_CMD_CTRL_TX_FLUSH 0x80 /* Firmware flushes all Tx */ #define WL1271_JOIN_CMD_TX_SESSION_OFFSET 1 +#define WL1271_JOIN_CMD_BSS_TYPE_5GHZ 0x10 struct wl1271_cmd_join { struct wl1271_cmd_header header; - u32 bssid_lsb; - u16 bssid_msb; - u16 beacon_interval; /* in TBTTs */ - u32 rx_config_options; - u32 rx_filter_options; + __le32 bssid_lsb; + __le16 bssid_msb; + __le16 beacon_interval; /* in TBTTs */ + __le32 rx_config_options; + __le32 rx_filter_options; /* * The target uses this field to determine the rate at * which to transmit control frame responses (such as * ACK or CTS frames). */ - u32 basic_rate_set; + __le32 basic_rate_set; u8 dtim_interval; /* * bits 0-2: This bitwise field specifies the type @@ -240,10 +246,10 @@ struct cmd_enabledisable_path { struct wl1271_cmd_template_set { struct wl1271_cmd_header header; - u16 len; + __le16 len; u8 template_type; u8 index; /* relevant only for KLV_TEMPLATE type */ - u32 enabled_rates; + __le32 enabled_rates; u8 short_retry_limit; u8 long_retry_limit; u8 aflags; @@ -280,18 +286,13 @@ struct wl1271_cmd_ps_params { * to power save mode. */ u8 hang_over_period; - u32 null_data_rate; + __le32 null_data_rate; } __attribute__ ((packed)); /* HW encryption keys */ #define NUM_ACCESS_CATEGORIES_COPY 4 #define MAX_KEY_SIZE 32 -/* When set, disable HW encryption */ -#define DF_ENCRYPTION_DISABLE 0x01 -/* When set, disable HW decryption */ -#define DF_SNIFF_MODE_ENABLE 0x80 - enum wl1271_cmd_key_action { KEY_ADD_OR_REPLACE = 1, KEY_REMOVE = 2, @@ -316,9 +317,9 @@ struct wl1271_cmd_set_keys { u8 addr[ETH_ALEN]; /* key_action_e */ - u16 key_action; + __le16 key_action; - u16 reserved_1; + __le16 reserved_1; /* key size in bytes */ u8 key_size; @@ -334,8 +335,8 @@ struct wl1271_cmd_set_keys { u8 id; u8 reserved_2[6]; u8 key[MAX_KEY_SIZE]; - u16 ac_seq_num16[NUM_ACCESS_CATEGORIES_COPY]; - u32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY]; + __le16 ac_seq_num16[NUM_ACCESS_CATEGORIES_COPY]; + __le32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY]; } __attribute__ ((packed)); @@ -347,19 +348,22 @@ struct wl1271_cmd_set_keys { #define WL1271_SCAN_OPT_PRIORITY_HIGH 4 #define WL1271_SCAN_CHAN_MIN_DURATION 30000 /* TU */ #define WL1271_SCAN_CHAN_MAX_DURATION 60000 /* TU */ +#define WL1271_SCAN_BAND_2_4_GHZ 0 +#define WL1271_SCAN_BAND_5_GHZ 1 +#define WL1271_SCAN_BAND_DUAL 2 struct basic_scan_params { - u32 rx_config_options; - u32 rx_filter_options; + __le32 rx_config_options; + __le32 rx_filter_options; /* Scan option flags (WL1271_SCAN_OPT_*) */ - u16 scan_options; + __le16 scan_options; /* Number of scan channels in the list (maximum 30) */ u8 num_channels; /* This field indicates the number of probe requests to send per channel for an active scan */ u8 num_probe_requests; /* Rate bit field for sending the probes */ - u32 tx_rate; + __le32 tx_rate; u8 tid_trigger; u8 ssid_len; /* in order to align */ @@ -374,10 +378,10 @@ struct basic_scan_params { struct basic_scan_channel_params { /* Duration in TU to wait for frames on a channel for active scan */ - u32 min_duration; - u32 max_duration; - u32 bssid_lsb; - u16 bssid_msb; + __le32 min_duration; + __le32 max_duration; + __le32 bssid_lsb; + __le16 bssid_msb; u8 early_termination; u8 tx_power_att; u8 channel; @@ -397,13 +401,13 @@ struct wl1271_cmd_scan { struct wl1271_cmd_trigger_scan_to { struct wl1271_cmd_header header; - u32 timeout; -}; + __le32 timeout; +} __attribute__ ((packed)); struct wl1271_cmd_test_header { u8 id; u8 padding[3]; -}; +} __attribute__ ((packed)); enum wl1271_channel_tune_bands { WL1271_CHANNEL_TUNE_BAND_2_4, @@ -416,6 +420,76 @@ enum wl1271_channel_tune_bands { #define TEST_CMD_P2G_CAL 0x02 #define TEST_CMD_CHANNEL_TUNE 0x0d #define TEST_CMD_UPDATE_PD_REFERENCE_POINT 0x1d +#define TEST_CMD_INI_FILE_RADIO_PARAM 0x19 +#define TEST_CMD_INI_FILE_GENERAL_PARAM 0x1E + +struct wl1271_general_parms_cmd { + struct wl1271_cmd_header header; + + struct wl1271_cmd_test_header test; + + u8 ref_clk; + u8 settling_time; + u8 clk_valid_on_wakeup; + u8 dc2dcmode; + u8 single_dual_band; + + u8 tx_bip_fem_autodetect; + u8 tx_bip_fem_manufacturer; + u8 settings; +} __attribute__ ((packed)); + +struct wl1271_radio_parms_cmd { + struct wl1271_cmd_header header; + + struct wl1271_cmd_test_header test; + + /* Static radio parameters */ + /* 2.4GHz */ + u8 rx_trace_loss; + u8 tx_trace_loss; + s8 rx_rssi_and_proc_compens[CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE]; + + /* 5GHz */ + u8 rx_trace_loss_5[CONF_NUMBER_OF_SUB_BANDS_5]; + u8 tx_trace_loss_5[CONF_NUMBER_OF_SUB_BANDS_5]; + s8 rx_rssi_and_proc_compens_5[CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE]; + + /* Dynamic radio parameters */ + /* 2.4GHz */ + __le16 tx_ref_pd_voltage; + s8 tx_ref_power; + s8 tx_offset_db; + + s8 tx_rate_limits_normal[CONF_NUMBER_OF_RATE_GROUPS]; + s8 tx_rate_limits_degraded[CONF_NUMBER_OF_RATE_GROUPS]; + + s8 tx_channel_limits_11b[CONF_NUMBER_OF_CHANNELS_2_4]; + s8 tx_channel_limits_ofdm[CONF_NUMBER_OF_CHANNELS_2_4]; + s8 tx_pdv_rate_offsets[CONF_NUMBER_OF_RATE_GROUPS]; + + u8 tx_ibias[CONF_NUMBER_OF_RATE_GROUPS]; + u8 rx_fem_insertion_loss; + + u8 padding2; + + /* 5GHz */ + __le16 tx_ref_pd_voltage_5[CONF_NUMBER_OF_SUB_BANDS_5]; + s8 tx_ref_power_5[CONF_NUMBER_OF_SUB_BANDS_5]; + s8 tx_offset_db_5[CONF_NUMBER_OF_SUB_BANDS_5]; + + s8 tx_rate_limits_normal_5[CONF_NUMBER_OF_RATE_GROUPS]; + s8 tx_rate_limits_degraded_5[CONF_NUMBER_OF_RATE_GROUPS]; + + s8 tx_channel_limits_ofdm_5[CONF_NUMBER_OF_CHANNELS_5]; + s8 tx_pdv_rate_offsets_5[CONF_NUMBER_OF_RATE_GROUPS]; + + /* FIXME: this is inconsistent with the types for 2.4GHz */ + s8 tx_ibias_5[CONF_NUMBER_OF_RATE_GROUPS]; + s8 rx_fem_insertion_loss_5[CONF_NUMBER_OF_SUB_BANDS_5]; + + u8 padding3[2]; +} __attribute__ ((packed)); struct wl1271_cmd_cal_channel_tune { struct wl1271_cmd_header header; @@ -425,7 +499,7 @@ struct wl1271_cmd_cal_channel_tune { u8 band; u8 channel; - u16 radio_status; + __le16 radio_status; } __attribute__ ((packed)); struct wl1271_cmd_cal_update_ref_point { @@ -433,8 +507,8 @@ struct wl1271_cmd_cal_update_ref_point { struct wl1271_cmd_test_header test; - s32 ref_power; - s32 ref_detector; + __le32 ref_power; + __le32 ref_detector; u8 sub_band; u8 padding[3]; } __attribute__ ((packed)); @@ -449,16 +523,42 @@ struct wl1271_cmd_cal_p2g { struct wl1271_cmd_test_header test; - u16 len; + __le16 len; u8 buf[MAX_TLV_LENGTH]; u8 type; u8 padding; - s16 radio_status; + __le16 radio_status; u8 nvs_version[MAX_NVS_VERSION_LENGTH]; u8 sub_band_mask; u8 padding2; } __attribute__ ((packed)); + +/* + * There are three types of disconnections: + * + * DISCONNECT_IMMEDIATE: the fw doesn't send any frames + * DISCONNECT_DEAUTH: the fw generates a DEAUTH request with the reason + * we have passed + * DISCONNECT_DISASSOC: the fw generates a DESASSOC request with the reason + * we have passed + */ +enum wl1271_disconnect_type { + DISCONNECT_IMMEDIATE, + DISCONNECT_DEAUTH, + DISCONNECT_DISASSOC +}; + +struct wl1271_cmd_disconnect { + __le32 rx_config_options; + __le32 rx_filter_options; + + __le16 reason; + u8 type; + + u8 padding; +} __attribute__ ((packed)); + #endif /* __WL1271_CMD_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1271_conf.h b/drivers/net/wireless/wl12xx/wl1271_conf.h new file mode 100644 index 000000000000..565373ede265 --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_conf.h @@ -0,0 +1,919 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 2009 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL1271_CONF_H__ +#define __WL1271_CONF_H__ + +enum { + CONF_HW_BIT_RATE_1MBPS = BIT(0), + CONF_HW_BIT_RATE_2MBPS = BIT(1), + CONF_HW_BIT_RATE_5_5MBPS = BIT(2), + CONF_HW_BIT_RATE_6MBPS = BIT(3), + CONF_HW_BIT_RATE_9MBPS = BIT(4), + CONF_HW_BIT_RATE_11MBPS = BIT(5), + CONF_HW_BIT_RATE_12MBPS = BIT(6), + CONF_HW_BIT_RATE_18MBPS = BIT(7), + CONF_HW_BIT_RATE_22MBPS = BIT(8), + CONF_HW_BIT_RATE_24MBPS = BIT(9), + CONF_HW_BIT_RATE_36MBPS = BIT(10), + CONF_HW_BIT_RATE_48MBPS = BIT(11), + CONF_HW_BIT_RATE_54MBPS = BIT(12), + CONF_HW_BIT_RATE_MCS_0 = BIT(13), + CONF_HW_BIT_RATE_MCS_1 = BIT(14), + CONF_HW_BIT_RATE_MCS_2 = BIT(15), + CONF_HW_BIT_RATE_MCS_3 = BIT(16), + CONF_HW_BIT_RATE_MCS_4 = BIT(17), + CONF_HW_BIT_RATE_MCS_5 = BIT(18), + CONF_HW_BIT_RATE_MCS_6 = BIT(19), + CONF_HW_BIT_RATE_MCS_7 = BIT(20) +}; + +enum { + CONF_HW_RATE_INDEX_1MBPS = 0, + CONF_HW_RATE_INDEX_2MBPS = 1, + CONF_HW_RATE_INDEX_5_5MBPS = 2, + CONF_HW_RATE_INDEX_6MBPS = 3, + CONF_HW_RATE_INDEX_9MBPS = 4, + CONF_HW_RATE_INDEX_11MBPS = 5, + CONF_HW_RATE_INDEX_12MBPS = 6, + CONF_HW_RATE_INDEX_18MBPS = 7, + CONF_HW_RATE_INDEX_22MBPS = 8, + CONF_HW_RATE_INDEX_24MBPS = 9, + CONF_HW_RATE_INDEX_36MBPS = 10, + CONF_HW_RATE_INDEX_48MBPS = 11, + CONF_HW_RATE_INDEX_54MBPS = 12, + CONF_HW_RATE_INDEX_MAX = CONF_HW_RATE_INDEX_54MBPS, +}; + +struct conf_sg_settings { + /* + * Defines the PER threshold in PPM of the BT voice of which reaching + * this value will trigger raising the priority of the BT voice by + * the BT IP until next NFS sample interval time as defined in + * nfs_sample_interval. + * + * Unit: PER value in PPM (parts per million) + * #Error_packets / #Total_packets + + * Range: u32 + */ + u32 per_threshold; + + /* + * This value is an absolute time in micro-seconds to limit the + * maximum scan duration compensation while in SG + */ + u32 max_scan_compensation_time; + + /* Defines the PER threshold of the BT voice of which reaching this + * value will trigger raising the priority of the BT voice until next + * NFS sample interval time as defined in sample_interval. + * + * Unit: msec + * Range: 1-65000 + */ + u16 nfs_sample_interval; + + /* + * Defines the load ratio for the BT. + * The WLAN ratio is: 100 - load_ratio + * + * Unit: Percent + * Range: 0-100 + */ + u8 load_ratio; + + /* + * true - Co-ex is allowed to enter/exit P.S automatically and + * transparently to the host + * + * false - Co-ex is disallowed to enter/exit P.S and will trigger an + * event to the host to notify for the need to enter/exit P.S + * due to BT change state + * + */ + u8 auto_ps_mode; + + /* + * This parameter defines the compensation percentage of num of probe + * requests in case scan is initiated during BT voice/BT ACL + * guaranteed link. + * + * Unit: Percent + * Range: 0-255 (0 - No compensation) + */ + u8 probe_req_compensation; + + /* + * This parameter defines the compensation percentage of scan window + * size in case scan is initiated during BT voice/BT ACL Guaranteed + * link. + * + * Unit: Percent + * Range: 0-255 (0 - No compensation) + */ + u8 scan_window_compensation; + + /* + * Defines the antenna configuration. + * + * Range: 0 - Single Antenna; 1 - Dual Antenna + */ + u8 antenna_config; + + /* + * The percent out of the Max consecutive beacon miss roaming trigger + * which is the threshold for raising the priority of beacon + * reception. + * + * Range: 1-100 + * N = MaxConsecutiveBeaconMiss + * P = coexMaxConsecutiveBeaconMissPrecent + * Threshold = MIN( N-1, round(N * P / 100)) + */ + u8 beacon_miss_threshold; + + /* + * The RX rate threshold below which rate adaptation is assumed to be + * occurring at the AP which will raise priority for ACTIVE_RX and RX + * SP. + * + * Range: HW_BIT_RATE_* + */ + u32 rate_adaptation_threshold; + + /* + * The SNR above which the RX rate threshold indicating AP rate + * adaptation is valid + * + * Range: -128 - 127 + */ + s8 rate_adaptation_snr; +}; + +enum conf_rx_queue_type { + CONF_RX_QUEUE_TYPE_LOW_PRIORITY, /* All except the high priority */ + CONF_RX_QUEUE_TYPE_HIGH_PRIORITY, /* Management and voice packets */ +}; + +struct conf_rx_settings { + /* + * The maximum amount of time, in TU, before the + * firmware discards the MSDU. + * + * Range: 0 - 0xFFFFFFFF + */ + u32 rx_msdu_life_time; + + /* + * Packet detection threshold in the PHY. + * + * FIXME: details unknown. + */ + u32 packet_detection_threshold; + + /* + * The longest time the STA will wait to receive traffic from the AP + * after a PS-poll has been transmitted. + * + * Range: 0 - 200000 + */ + u16 ps_poll_timeout; + /* + * The longest time the STA will wait to receive traffic from the AP + * after a frame has been sent from an UPSD enabled queue. + * + * Range: 0 - 200000 + */ + u16 upsd_timeout; + + /* + * The number of octets in an MPDU, below which an RTS/CTS + * handshake is not performed. + * + * Range: 0 - 4096 + */ + u16 rts_threshold; + + /* + * The RX Clear Channel Assessment threshold in the PHY + * (the energy threshold). + * + * Range: ENABLE_ENERGY_D == 0x140A + * DISABLE_ENERGY_D == 0xFFEF + */ + u16 rx_cca_threshold; + + /* + * Occupied Rx mem-blocks number which requires interrupting the host + * (0 = no buffering, 0xffff = disabled). + * + * Range: u16 + */ + u16 irq_blk_threshold; + + /* + * Rx packets number which requires interrupting the host + * (0 = no buffering). + * + * Range: u16 + */ + u16 irq_pkt_threshold; + + /* + * Max time in msec the FW may delay RX-Complete interrupt. + * + * Range: 1 - 100 + */ + u16 irq_timeout; + + /* + * The RX queue type. + * + * Range: RX_QUEUE_TYPE_RX_LOW_PRIORITY, RX_QUEUE_TYPE_RX_HIGH_PRIORITY, + */ + u8 queue_type; +}; + +#define CONF_TX_MAX_RATE_CLASSES 8 + +#define CONF_TX_RATE_MASK_UNSPECIFIED 0 +#define CONF_TX_RATE_MASK_ALL 0x1eff +#define CONF_TX_RATE_RETRY_LIMIT 10 + +struct conf_tx_rate_class { + + /* + * The rates enabled for this rate class. + * + * Range: CONF_HW_BIT_RATE_* bit mask + */ + u32 enabled_rates; + + /* + * The dot11 short retry limit used for TX retries. + * + * Range: u8 + */ + u8 short_retry_limit; + + /* + * The dot11 long retry limit used for TX retries. + * + * Range: u8 + */ + u8 long_retry_limit; + + /* + * Flags controlling the attributes of TX transmission. + * + * Range: bit 0: Truncate - when set, FW attempts to send a frame stop + * when the total valid per-rate attempts have + * been exhausted; otherwise transmissions + * will continue at the lowest available rate + * until the appropriate one of the + * short_retry_limit, long_retry_limit, + * dot11_max_transmit_msdu_life_time, or + * max_tx_life_time, is exhausted. + * 1: Preamble Override - indicates if the preamble type + * should be used in TX. + * 2: Preamble Type - the type of the preamble to be used by + * the policy (0 - long preamble, 1 - short preamble. + */ + u8 aflags; +}; + +#define CONF_TX_MAX_AC_COUNT 4 + +/* Slot number setting to start transmission at PIFS interval */ +#define CONF_TX_AIFS_PIFS 1 +/* Slot number setting to start transmission at DIFS interval normal + * DCF access */ +#define CONF_TX_AIFS_DIFS 2 + + +enum conf_tx_ac { + CONF_TX_AC_BE = 0, /* best effort / legacy */ + CONF_TX_AC_BK = 1, /* background */ + CONF_TX_AC_VI = 2, /* video */ + CONF_TX_AC_VO = 3, /* voice */ + CONF_TX_AC_CTS2SELF = 4, /* fictious AC, follows AC_VO */ + CONF_TX_AC_ANY_TID = 0x1f +}; + +struct conf_tx_ac_category { + /* + * The AC class identifier. + * + * Range: enum conf_tx_ac + */ + u8 ac; + + /* + * The contention window minimum size (in slots) for the access + * class. + * + * Range: u8 + */ + u8 cw_min; + + /* + * The contention window maximum size (in slots) for the access + * class. + * + * Range: u8 + */ + u16 cw_max; + + /* + * The AIF value (in slots) for the access class. + * + * Range: u8 + */ + u8 aifsn; + + /* + * The TX Op Limit (in microseconds) for the access class. + * + * Range: u16 + */ + u16 tx_op_limit; +}; + +#define CONF_TX_MAX_TID_COUNT 7 + +enum { + CONF_CHANNEL_TYPE_DCF = 0, /* DC/LEGACY*/ + CONF_CHANNEL_TYPE_EDCF = 1, /* EDCA*/ + CONF_CHANNEL_TYPE_HCCA = 2, /* HCCA*/ +}; + +enum { + CONF_PS_SCHEME_LEGACY = 0, + CONF_PS_SCHEME_UPSD_TRIGGER = 1, + CONF_PS_SCHEME_LEGACY_PSPOLL = 2, + CONF_PS_SCHEME_SAPSD = 3, +}; + +enum { + CONF_ACK_POLICY_LEGACY = 0, + CONF_ACK_POLICY_NO_ACK = 1, + CONF_ACK_POLICY_BLOCK = 2, +}; + + +struct conf_tx_tid { + u8 queue_id; + u8 channel_type; + u8 tsid; + u8 ps_scheme; + u8 ack_policy; + u32 apsd_conf[2]; +}; + +struct conf_tx_settings { + /* + * The TX ED value for TELEC Enable/Disable. + * + * Range: 0, 1 + */ + u8 tx_energy_detection; + + /* + * Configuration for rate classes for TX (currently only one + * rate class supported.) + */ + struct conf_tx_rate_class rc_conf; + + /* + * Configuration for access categories for TX rate control. + */ + u8 ac_conf_count; + struct conf_tx_ac_category ac_conf[CONF_TX_MAX_AC_COUNT]; + + /* + * Configuration for TID parameters. + */ + u8 tid_conf_count; + struct conf_tx_tid tid_conf[CONF_TX_MAX_TID_COUNT]; + + /* + * The TX fragmentation threshold. + * + * Range: u16 + */ + u16 frag_threshold; + + /* + * Max time in msec the FW may delay frame TX-Complete interrupt. + * + * Range: u16 + */ + u16 tx_compl_timeout; + + /* + * Completed TX packet count which requires to issue the TX-Complete + * interrupt. + * + * Range: u16 + */ + u16 tx_compl_threshold; + +}; + +enum { + CONF_WAKE_UP_EVENT_BEACON = 0x01, /* Wake on every Beacon*/ + CONF_WAKE_UP_EVENT_DTIM = 0x02, /* Wake on every DTIM*/ + CONF_WAKE_UP_EVENT_N_DTIM = 0x04, /* Wake every Nth DTIM */ + CONF_WAKE_UP_EVENT_N_BEACONS = 0x08, /* Wake every Nth beacon */ + CONF_WAKE_UP_EVENT_BITS_MASK = 0x0F +}; + +#define CONF_MAX_BCN_FILT_IE_COUNT 32 + +#define CONF_BCN_RULE_PASS_ON_CHANGE BIT(0) +#define CONF_BCN_RULE_PASS_ON_APPEARANCE BIT(1) + +#define CONF_BCN_IE_OUI_LEN 3 +#define CONF_BCN_IE_VER_LEN 2 + +struct conf_bcn_filt_rule { + /* + * IE number to which to associate a rule. + * + * Range: u8 + */ + u8 ie; + + /* + * Rule to associate with the specific ie. + * + * Range: CONF_BCN_RULE_PASS_ON_* + */ + u8 rule; + + /* + * OUI for the vendor specifie IE (221) + */ + u8 oui[CONF_BCN_IE_OUI_LEN]; + + /* + * Type for the vendor specifie IE (221) + */ + u8 type; + + /* + * Version for the vendor specifie IE (221) + */ + u8 version[CONF_BCN_IE_VER_LEN]; +}; + +#define CONF_MAX_RSSI_SNR_TRIGGERS 8 + +enum { + CONF_TRIG_METRIC_RSSI_BEACON = 0, + CONF_TRIG_METRIC_RSSI_DATA, + CONF_TRIG_METRIC_SNR_BEACON, + CONF_TRIG_METRIC_SNR_DATA +}; + +enum { + CONF_TRIG_EVENT_TYPE_LEVEL = 0, + CONF_TRIG_EVENT_TYPE_EDGE +}; + +enum { + CONF_TRIG_EVENT_DIR_LOW = 0, + CONF_TRIG_EVENT_DIR_HIGH, + CONF_TRIG_EVENT_DIR_BIDIR +}; + + +struct conf_sig_trigger { + /* + * The RSSI / SNR threshold value. + * + * FIXME: what is the range? + */ + s16 threshold; + + /* + * Minimum delay between two trigger events for this trigger in ms. + * + * Range: 0 - 60000 + */ + u16 pacing; + + /* + * The measurement data source for this trigger. + * + * Range: CONF_TRIG_METRIC_* + */ + u8 metric; + + /* + * The trigger type of this trigger. + * + * Range: CONF_TRIG_EVENT_TYPE_* + */ + u8 type; + + /* + * The direction of the trigger. + * + * Range: CONF_TRIG_EVENT_DIR_* + */ + u8 direction; + + /* + * Hysteresis range of the trigger around the threshold (in dB) + * + * Range: u8 + */ + u8 hysteresis; + + /* + * Index of the trigger rule. + * + * Range: 0 - CONF_MAX_RSSI_SNR_TRIGGERS-1 + */ + u8 index; + + /* + * Enable / disable this rule (to use for clearing rules.) + * + * Range: 1 - Enabled, 2 - Not enabled + */ + u8 enable; +}; + +struct conf_sig_weights { + + /* + * RSSI from beacons average weight. + * + * Range: u8 + */ + u8 rssi_bcn_avg_weight; + + /* + * RSSI from data average weight. + * + * Range: u8 + */ + u8 rssi_pkt_avg_weight; + + /* + * SNR from beacons average weight. + * + * Range: u8 + */ + u8 snr_bcn_avg_weight; + + /* + * SNR from data average weight. + * + * Range: u8 + */ + u8 snr_pkt_avg_weight; +}; + +enum conf_bcn_filt_mode { + CONF_BCN_FILT_MODE_DISABLED = 0, + CONF_BCN_FILT_MODE_ENABLED = 1 +}; + +enum conf_bet_mode { + CONF_BET_MODE_DISABLE = 0, + CONF_BET_MODE_ENABLE = 1, +}; + +struct conf_conn_settings { + /* + * Firmware wakeup conditions configuration. The host may set only + * one bit. + * + * Range: CONF_WAKE_UP_EVENT_* + */ + u8 wake_up_event; + + /* + * Listen interval for beacons or Dtims. + * + * Range: 0 for beacon and Dtim wakeup + * 1-10 for x Dtims + * 1-255 for x beacons + */ + u8 listen_interval; + + /* + * Enable or disable the beacon filtering. + * + * Range: CONF_BCN_FILT_MODE_* + */ + enum conf_bcn_filt_mode bcn_filt_mode; + + /* + * Configure Beacon filter pass-thru rules. + */ + u8 bcn_filt_ie_count; + struct conf_bcn_filt_rule bcn_filt_ie[CONF_MAX_BCN_FILT_IE_COUNT]; + + /* + * The number of consequtive beacons to lose, before the firmware + * becomes out of synch. + * + * Range: u32 + */ + u32 synch_fail_thold; + + /* + * After out-of-synch, the number of TU's to wait without a further + * received beacon (or probe response) before issuing the BSS_EVENT_LOSE + * event. + * + * Range: u32 + */ + u32 bss_lose_timeout; + + /* + * Beacon receive timeout. + * + * Range: u32 + */ + u32 beacon_rx_timeout; + + /* + * Broadcast receive timeout. + * + * Range: u32 + */ + u32 broadcast_timeout; + + /* + * Enable/disable reception of broadcast packets in power save mode + * + * Range: 1 - enable, 0 - disable + */ + u8 rx_broadcast_in_ps; + + /* + * Consequtive PS Poll failures before sending event to driver + * + * Range: u8 + */ + u8 ps_poll_threshold; + + /* + * Configuration of signal (rssi/snr) triggers. + */ + u8 sig_trigger_count; + struct conf_sig_trigger sig_trigger[CONF_MAX_RSSI_SNR_TRIGGERS]; + + /* + * Configuration of signal average weights. + */ + struct conf_sig_weights sig_weights; + + /* + * Specifies if beacon early termination procedure is enabled or + * disabled. + * + * Range: CONF_BET_MODE_* + */ + u8 bet_enable; + + /* + * Specifies the maximum number of consecutive beacons that may be + * early terminated. After this number is reached at least one full + * beacon must be correctly received in FW before beacon ET + * resumes. + * + * Range 0 - 255 + */ + u8 bet_max_consecutive; + + /* + * Specifies the maximum number of times to try PSM entry if it fails + * (if sending the appropriate null-func message fails.) + * + * Range 0 - 255 + */ + u8 psm_entry_retries; +}; + +#define CONF_SR_ERR_TBL_MAX_VALUES 14 + +struct conf_mart_reflex_err_table { + /* + * Length of the error table values table. + * + * Range: 0 - CONF_SR_ERR_TBL_MAX_VALUES + */ + u8 len; + + /* + * Smart Reflex error table upper limit. + * + * Range: s8 + */ + s8 upper_limit; + + /* + * Smart Reflex error table values. + * + * Range: s8 + */ + s8 values[CONF_SR_ERR_TBL_MAX_VALUES]; +}; + +enum { + CONF_REF_CLK_19_2_E, + CONF_REF_CLK_26_E, + CONF_REF_CLK_38_4_E, + CONF_REF_CLK_52_E +}; + +enum single_dual_band_enum { + CONF_SINGLE_BAND, + CONF_DUAL_BAND +}; + +struct conf_general_parms { + /* + * RF Reference Clock type / speed + * + * Range: CONF_REF_CLK_* + */ + u8 ref_clk; + + /* + * Settling time of the reference clock after boot. + * + * Range: u8 + */ + u8 settling_time; + + /* + * Flag defining whether clock is valid on wakeup. + * + * Range: 0 - not valid on wakeup, 1 - valid on wakeup + */ + u8 clk_valid_on_wakeup; + + /* + * DC-to-DC mode. + * + * Range: Unknown + */ + u8 dc2dcmode; + + /* + * Flag defining whether used as single or dual-band. + * + * Range: CONF_SINGLE_BAND, CONF_DUAL_BAND + */ + u8 single_dual_band; + + /* + * TX bip fem autodetect flag. + * + * Range: Unknown + */ + u8 tx_bip_fem_autodetect; + + /* + * TX bip gem manufacturer. + * + * Range: Unknown + */ + u8 tx_bip_fem_manufacturer; + + /* + * Settings flags. + * + * Range: Unknown + */ + u8 settings; +}; + +#define CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE 15 +#define CONF_NUMBER_OF_SUB_BANDS_5 7 +#define CONF_NUMBER_OF_RATE_GROUPS 6 +#define CONF_NUMBER_OF_CHANNELS_2_4 14 +#define CONF_NUMBER_OF_CHANNELS_5 35 + +struct conf_radio_parms { + /* + * Static radio parameters for 2.4GHz + * + * Range: unknown + */ + u8 rx_trace_loss; + u8 tx_trace_loss; + s8 rx_rssi_and_proc_compens[CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE]; + + /* + * Static radio parameters for 5GHz + * + * Range: unknown + */ + u8 rx_trace_loss_5[CONF_NUMBER_OF_SUB_BANDS_5]; + u8 tx_trace_loss_5[CONF_NUMBER_OF_SUB_BANDS_5]; + s8 rx_rssi_and_proc_compens_5[CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE]; + + /* + * Dynamic radio parameters for 2.4GHz + * + * Range: unknown + */ + s16 tx_ref_pd_voltage; + s8 tx_ref_power; + s8 tx_offset_db; + + s8 tx_rate_limits_normal[CONF_NUMBER_OF_RATE_GROUPS]; + s8 tx_rate_limits_degraded[CONF_NUMBER_OF_RATE_GROUPS]; + + s8 tx_channel_limits_11b[CONF_NUMBER_OF_CHANNELS_2_4]; + s8 tx_channel_limits_ofdm[CONF_NUMBER_OF_CHANNELS_2_4]; + s8 tx_pdv_rate_offsets[CONF_NUMBER_OF_RATE_GROUPS]; + + u8 tx_ibias[CONF_NUMBER_OF_RATE_GROUPS]; + u8 rx_fem_insertion_loss; + + /* + * Dynamic radio parameters for 5GHz + * + * Range: unknown + */ + s16 tx_ref_pd_voltage_5[CONF_NUMBER_OF_SUB_BANDS_5]; + s8 tx_ref_power_5[CONF_NUMBER_OF_SUB_BANDS_5]; + s8 tx_offset_db_5[CONF_NUMBER_OF_SUB_BANDS_5]; + + s8 tx_rate_limits_normal_5[CONF_NUMBER_OF_RATE_GROUPS]; + s8 tx_rate_limits_degraded_5[CONF_NUMBER_OF_RATE_GROUPS]; + + s8 tx_channel_limits_ofdm_5[CONF_NUMBER_OF_CHANNELS_5]; + s8 tx_pdv_rate_offsets_5[CONF_NUMBER_OF_RATE_GROUPS]; + + /* FIXME: this is inconsistent with the types for 2.4GHz */ + s8 tx_ibias_5[CONF_NUMBER_OF_RATE_GROUPS]; + s8 rx_fem_insertion_loss_5[CONF_NUMBER_OF_SUB_BANDS_5]; +}; + +#define CONF_SR_ERR_TBL_COUNT 3 + +struct conf_init_settings { + /* + * Configure Smart Reflex error table values. + */ + struct conf_mart_reflex_err_table sr_err_tbl[CONF_SR_ERR_TBL_COUNT]; + + /* + * Smart Reflex enable flag. + * + * Range: 1 - Smart Reflex enabled, 0 - Smart Reflex disabled + */ + u8 sr_enable; + + /* + * Configure general parameters. + */ + struct conf_general_parms genparam; + + /* + * Configure radio parameters. + */ + struct conf_radio_parms radioparam; + +}; + +struct conf_drv_settings { + struct conf_sg_settings sg; + struct conf_rx_settings rx; + struct conf_tx_settings tx; + struct conf_conn_settings conn; + struct conf_init_settings init; +}; + +#endif diff --git a/drivers/net/wireless/wl12xx/wl1271_event.c b/drivers/net/wireless/wl12xx/wl1271_event.c index f3afd4a6ff33..d13fdd99c85c 100644 --- a/drivers/net/wireless/wl12xx/wl1271_event.c +++ b/drivers/net/wireless/wl12xx/wl1271_event.c @@ -26,23 +26,86 @@ #include "wl1271_spi.h" #include "wl1271_event.h" #include "wl1271_ps.h" +#include "wl12xx_80211.h" static int wl1271_event_scan_complete(struct wl1271 *wl, struct event_mailbox *mbox) { + int size = sizeof(struct wl12xx_probe_req_template); wl1271_debug(DEBUG_EVENT, "status: 0x%x", mbox->scheduled_scan_status); if (wl->scanning) { - mutex_unlock(&wl->mutex); - ieee80211_scan_completed(wl->hw, false); - mutex_lock(&wl->mutex); - wl->scanning = false; + if (wl->scan.state == WL1271_SCAN_BAND_DUAL) { + wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, + NULL, size); + /* 2.4 GHz band scanned, scan 5 GHz band, pretend + * to the wl1271_cmd_scan function that we are not + * scanning as it checks that. + */ + wl->scanning = false; + wl1271_cmd_scan(wl, wl->scan.ssid, wl->scan.ssid_len, + wl->scan.active, + wl->scan.high_prio, + WL1271_SCAN_BAND_5_GHZ, + wl->scan.probe_requests); + } else { + if (wl->scan.state == WL1271_SCAN_BAND_2_4_GHZ) + wl1271_cmd_template_set(wl, + CMD_TEMPL_CFG_PROBE_REQ_2_4, + NULL, size); + else + wl1271_cmd_template_set(wl, + CMD_TEMPL_CFG_PROBE_REQ_5, + NULL, size); + + mutex_unlock(&wl->mutex); + ieee80211_scan_completed(wl->hw, false); + mutex_lock(&wl->mutex); + wl->scanning = false; + } } - return 0; } +static int wl1271_event_ps_report(struct wl1271 *wl, + struct event_mailbox *mbox, + bool *beacon_loss) +{ + int ret = 0; + + wl1271_debug(DEBUG_EVENT, "ps_status: 0x%x", mbox->ps_status); + + switch (mbox->ps_status) { + case EVENT_ENTER_POWER_SAVE_FAIL: + if (!wl->psm) { + wl->psm_entry_retry = 0; + break; + } + + if (wl->psm_entry_retry < wl->conf.conn.psm_entry_retries) { + wl->psm_entry_retry++; + ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE); + } else { + wl1271_error("PSM entry failed, giving up.\n"); + wl->psm_entry_retry = 0; + *beacon_loss = true; + } + break; + case EVENT_ENTER_POWER_SAVE_SUCCESS: + wl->psm_entry_retry = 0; + break; + case EVENT_EXIT_POWER_SAVE_FAIL: + wl1271_info("PSM exit failed"); + break; + case EVENT_EXIT_POWER_SAVE_SUCCESS: + default: + break; + } + + return ret; +} + static void wl1271_event_mbox_dump(struct event_mailbox *mbox) { wl1271_debug(DEBUG_EVENT, "MBOX DUMP:"); @@ -54,10 +117,12 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) { int ret; u32 vector; + bool beacon_loss = false; wl1271_event_mbox_dump(mbox); - vector = mbox->events_vector & ~(mbox->events_mask); + vector = le32_to_cpu(mbox->events_vector); + vector &= ~(le32_to_cpu(mbox->events_mask)); wl1271_debug(DEBUG_EVENT, "vector: 0x%x", vector); if (vector & SCAN_COMPLETE_EVENT_ID) { @@ -66,14 +131,34 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) return ret; } - if (vector & BSS_LOSE_EVENT_ID) { + /* + * The BSS_LOSE_EVENT_ID is only needed while psm (and hence beacon + * filtering) is enabled. Without PSM, the stack will receive all + * beacons and can detect beacon loss by itself. + */ + if (vector & BSS_LOSE_EVENT_ID && wl->psm) { wl1271_debug(DEBUG_EVENT, "BSS_LOSE_EVENT"); - if (wl->psm_requested && wl->psm) { - ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE); - if (ret < 0) - return ret; - } + /* indicate to the stack, that beacons have been lost */ + beacon_loss = true; + } + + if (vector & PS_REPORT_EVENT_ID) { + wl1271_debug(DEBUG_EVENT, "PS_REPORT_EVENT"); + ret = wl1271_event_ps_report(wl, mbox, &beacon_loss); + if (ret < 0) + return ret; + } + + if (beacon_loss) { + /* Obviously, it's dangerous to release the mutex while + we are holding many of the variables in the wl struct. + That's why it's done last in the function, and care must + be taken that nothing more is done after this function + returns. */ + mutex_unlock(&wl->mutex); + ieee80211_beacon_loss(wl->vif); + mutex_lock(&wl->mutex); } return 0; @@ -92,14 +177,14 @@ int wl1271_event_unmask(struct wl1271 *wl) void wl1271_event_mbox_config(struct wl1271 *wl) { - wl->mbox_ptr[0] = wl1271_reg_read32(wl, REG_EVENT_MAILBOX_PTR); + wl->mbox_ptr[0] = wl1271_spi_read32(wl, REG_EVENT_MAILBOX_PTR); wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox); wl1271_debug(DEBUG_EVENT, "MBOX ptrs: 0x%x 0x%x", wl->mbox_ptr[0], wl->mbox_ptr[1]); } -int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num) +int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num, bool do_ack) { struct event_mailbox mbox; int ret; @@ -110,8 +195,8 @@ int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num) return -EINVAL; /* first we read the mbox descriptor */ - wl1271_spi_mem_read(wl, wl->mbox_ptr[mbox_num], &mbox, - sizeof(struct event_mailbox)); + wl1271_spi_read(wl, wl->mbox_ptr[mbox_num], &mbox, + sizeof(struct event_mailbox), false); /* process the descriptor */ ret = wl1271_event_process(wl, &mbox); @@ -119,7 +204,9 @@ int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num) return ret; /* then we let the firmware know it can go on...*/ - wl1271_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_EVENT_ACK); + if (do_ack) + wl1271_spi_write32(wl, ACX_REG_INTERRUPT_TRIG, + INTR_TRIG_EVENT_ACK); return 0; } diff --git a/drivers/net/wireless/wl12xx/wl1271_event.h b/drivers/net/wireless/wl12xx/wl1271_event.h index 2cdce7c34bf0..4e3f55ebb1a8 100644 --- a/drivers/net/wireless/wl12xx/wl1271_event.h +++ b/drivers/net/wireless/wl12xx/wl1271_event.h @@ -63,36 +63,43 @@ enum { EVENT_MBOX_ALL_EVENT_ID = 0x7fffffff, }; +enum { + EVENT_ENTER_POWER_SAVE_FAIL = 0, + EVENT_ENTER_POWER_SAVE_SUCCESS, + EVENT_EXIT_POWER_SAVE_FAIL, + EVENT_EXIT_POWER_SAVE_SUCCESS, +}; + struct event_debug_report { u8 debug_event_id; u8 num_params; - u16 pad; - u32 report_1; - u32 report_2; - u32 report_3; + __le16 pad; + __le32 report_1; + __le32 report_2; + __le32 report_3; } __attribute__ ((packed)); #define NUM_OF_RSSI_SNR_TRIGGERS 8 struct event_mailbox { - u32 events_vector; - u32 events_mask; - u32 reserved_1; - u32 reserved_2; + __le32 events_vector; + __le32 events_mask; + __le32 reserved_1; + __le32 reserved_2; u8 dbg_event_id; u8 num_relevant_params; - u16 reserved_3; - u32 event_report_p1; - u32 event_report_p2; - u32 event_report_p3; + __le16 reserved_3; + __le32 event_report_p1; + __le32 event_report_p2; + __le32 event_report_p3; u8 number_of_scan_results; u8 scan_tag; u8 reserved_4[2]; - u32 compl_scheduled_scan_status; + __le32 compl_scheduled_scan_status; - u16 scheduled_scan_attended_channels; + __le16 scheduled_scan_attended_channels; u8 soft_gemini_sense_info; u8 soft_gemini_protective_info; s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS]; @@ -105,6 +112,6 @@ struct event_mailbox { int wl1271_event_unmask(struct wl1271 *wl); void wl1271_event_mbox_config(struct wl1271 *wl); -int wl1271_event_handle(struct wl1271 *wl, u8 mbox); +int wl1271_event_handle(struct wl1271 *wl, u8 mbox, bool do_ack); #endif diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c index 490df217605a..11249b436cf1 100644 --- a/drivers/net/wireless/wl12xx/wl1271_init.c +++ b/drivers/net/wireless/wl12xx/wl1271_init.c @@ -59,6 +59,14 @@ static int wl1271_init_templates_config(struct wl1271 *wl) if (ret < 0) return ret; + if (wl1271_11a_enabled()) { + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, + NULL, + sizeof(struct wl12xx_probe_req_template)); + if (ret < 0) + return ret; + } + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, NULL, sizeof(struct wl12xx_null_data_template)); if (ret < 0) @@ -94,7 +102,7 @@ static int wl1271_init_rx_config(struct wl1271 *wl, u32 config, u32 filter) { int ret; - ret = wl1271_acx_rx_msdu_life_time(wl, RX_MSDU_LIFETIME_DEF); + ret = wl1271_acx_rx_msdu_life_time(wl); if (ret < 0) return ret; @@ -117,7 +125,7 @@ static int wl1271_init_phy_config(struct wl1271 *wl) if (ret < 0) return ret; - ret = wl1271_acx_group_address_tbl(wl); + ret = wl1271_acx_group_address_tbl(wl, true, NULL, 0); if (ret < 0) return ret; @@ -125,7 +133,7 @@ static int wl1271_init_phy_config(struct wl1271 *wl) if (ret < 0) return ret; - ret = wl1271_acx_rts_threshold(wl, RTS_THRESHOLD_DEF); + ret = wl1271_acx_rts_threshold(wl, wl->conf.rx.rts_threshold); if (ret < 0) return ret; @@ -136,7 +144,8 @@ static int wl1271_init_beacon_filter(struct wl1271 *wl) { int ret; - ret = wl1271_acx_beacon_filter_opt(wl); + /* disable beacon filtering at this stage */ + ret = wl1271_acx_beacon_filter_opt(wl, false); if (ret < 0) return ret; @@ -184,118 +193,15 @@ static int wl1271_init_beacon_broadcast(struct wl1271 *wl) return 0; } -static int wl1271_init_general_parms(struct wl1271 *wl) -{ - struct wl1271_general_parms *gen_parms; - int ret; - - gen_parms = kzalloc(sizeof(*gen_parms), GFP_KERNEL); - if (!gen_parms) - return -ENOMEM; - - gen_parms->id = TEST_CMD_INI_FILE_GENERAL_PARAM; - - gen_parms->ref_clk = REF_CLK_38_4_E; - /* FIXME: magic numbers */ - gen_parms->settling_time = 5; - gen_parms->clk_valid_on_wakeup = 0; - gen_parms->dc2dcmode = 0; - gen_parms->single_dual_band = 0; - gen_parms->tx_bip_fem_autodetect = 1; - gen_parms->tx_bip_fem_manufacturer = 1; - gen_parms->settings = 1; - - ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), 0); - if (ret < 0) { - wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed"); - return ret; - } - - kfree(gen_parms); - return 0; -} - -static int wl1271_init_radio_parms(struct wl1271 *wl) -{ - /* - * FIXME: All these magic numbers should be moved to some place where - * they can be configured (separate file?) - */ - - struct wl1271_radio_parms *radio_parms; - int ret; - u8 compensation[] = { 0xec, 0xf6, 0x00, 0x0c, 0x18, 0xf8, 0xfc, 0x00, - 0x08, 0x10, 0xf0, 0xf8, 0x00, 0x0a, 0x14 }; - - u8 tx_rate_limits_normal[] = { 0x1e, 0x1f, 0x22, 0x24, 0x28, 0x29 }; - u8 tx_rate_limits_degraded[] = { 0x1b, 0x1c, 0x1e, 0x20, 0x24, 0x25 }; - - u8 tx_channel_limits_11b[] = { 0x22, 0x50, 0x50, 0x50, - 0x50, 0x50, 0x50, 0x50, - 0x50, 0x50, 0x22, 0x50, - 0x22, 0x50 }; - - u8 tx_channel_limits_ofdm[] = { 0x20, 0x50, 0x50, 0x50, - 0x50, 0x50, 0x50, 0x50, - 0x50, 0x50, 0x20, 0x50, - 0x20, 0x50 }; - - u8 tx_pdv_rate_offsets[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - - u8 tx_ibias[] = { 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x27 }; - - radio_parms = kzalloc(sizeof(*radio_parms), GFP_KERNEL); - if (!radio_parms) - return -ENOMEM; - - radio_parms->id = TEST_CMD_INI_FILE_RADIO_PARAM; - - /* Static radio parameters */ - radio_parms->rx_trace_loss = 10; - radio_parms->tx_trace_loss = 10; - memcpy(radio_parms->rx_rssi_and_proc_compens, compensation, - sizeof(compensation)); - - /* We don't set the 5GHz -- N/A */ - - /* Dynamic radio parameters */ - radio_parms->tx_ref_pd_voltage = cpu_to_le16(0x24e); - radio_parms->tx_ref_power = 0x78; - radio_parms->tx_offset_db = 0x0; - - memcpy(radio_parms->tx_rate_limits_normal, tx_rate_limits_normal, - sizeof(tx_rate_limits_normal)); - memcpy(radio_parms->tx_rate_limits_degraded, tx_rate_limits_degraded, - sizeof(tx_rate_limits_degraded)); - - memcpy(radio_parms->tx_channel_limits_11b, tx_channel_limits_11b, - sizeof(tx_channel_limits_11b)); - memcpy(radio_parms->tx_channel_limits_ofdm, tx_channel_limits_ofdm, - sizeof(tx_channel_limits_ofdm)); - memcpy(radio_parms->tx_pdv_rate_offsets, tx_pdv_rate_offsets, - sizeof(tx_pdv_rate_offsets)); - memcpy(radio_parms->tx_ibias, tx_ibias, - sizeof(tx_ibias)); - - radio_parms->rx_fem_insertion_loss = 0x14; - - ret = wl1271_cmd_test(wl, radio_parms, sizeof(*radio_parms), 0); - if (ret < 0) - wl1271_warning("CMD_INI_FILE_RADIO_PARAM failed"); - - kfree(radio_parms); - return ret; -} - int wl1271_hw_init(struct wl1271 *wl) { int ret; - ret = wl1271_init_general_parms(wl); + ret = wl1271_cmd_general_parms(wl); if (ret < 0) return ret; - ret = wl1271_init_radio_parms(wl); + ret = wl1271_cmd_radio_parms(wl); if (ret < 0) return ret; @@ -311,8 +217,8 @@ int wl1271_hw_init(struct wl1271 *wl) /* RX config */ ret = wl1271_init_rx_config(wl, - RX_CFG_PROMISCUOUS | RX_CFG_TSF, - RX_FILTER_OPTION_DEF); + RX_CFG_PROMISCUOUS | RX_CFG_TSF, + RX_FILTER_OPTION_DEF); /* RX_CONFIG_OPTION_ANY_DST_ANY_BSS, RX_FILTER_OPTION_FILTER_ALL); */ if (ret < 0) @@ -323,6 +229,11 @@ int wl1271_hw_init(struct wl1271 *wl) if (ret < 0) goto out_free_memmap; + /* Initialize connection monitoring thresholds */ + ret = wl1271_acx_conn_monit_params(wl); + if (ret < 0) + goto out_free_memmap; + /* Beacon filtering */ ret = wl1271_init_beacon_filter(wl); if (ret < 0) @@ -369,7 +280,7 @@ int wl1271_hw_init(struct wl1271 *wl) goto out_free_memmap; /* Configure TX rate classes */ - ret = wl1271_acx_rate_policies(wl); + ret = wl1271_acx_rate_policies(wl, CONF_TX_RATE_MASK_ALL); if (ret < 0) goto out_free_memmap; @@ -388,10 +299,16 @@ int wl1271_hw_init(struct wl1271 *wl) if (ret < 0) goto out_free_memmap; + /* Configure smart reflex */ + ret = wl1271_acx_smart_reflex(wl); + if (ret < 0) + goto out_free_memmap; + return 0; out_free_memmap: kfree(wl->target_mem_map); + wl->target_mem_map = NULL; return ret; } diff --git a/drivers/net/wireless/wl12xx/wl1271_init.h b/drivers/net/wireless/wl12xx/wl1271_init.h index bd8ff0fa2272..930677fbe852 100644 --- a/drivers/net/wireless/wl12xx/wl1271_init.h +++ b/drivers/net/wireless/wl12xx/wl1271_init.h @@ -29,87 +29,4 @@ int wl1271_hw_init_power_auth(struct wl1271 *wl); int wl1271_hw_init(struct wl1271 *wl); -/* These are not really a TEST_CMD, but the ref driver uses them as such */ -#define TEST_CMD_INI_FILE_RADIO_PARAM 0x19 -#define TEST_CMD_INI_FILE_GENERAL_PARAM 0x1E - -struct wl1271_general_parms { - u8 id; - u8 padding[3]; - - u8 ref_clk; - u8 settling_time; - u8 clk_valid_on_wakeup; - u8 dc2dcmode; - u8 single_dual_band; - - u8 tx_bip_fem_autodetect; - u8 tx_bip_fem_manufacturer; - u8 settings; -} __attribute__ ((packed)); - -enum ref_clk_enum { - REF_CLK_19_2_E, - REF_CLK_26_E, - REF_CLK_38_4_E, - REF_CLK_52_E -}; - -#define RSSI_AND_PROCESS_COMPENSATION_SIZE 15 -#define NUMBER_OF_SUB_BANDS_5 7 -#define NUMBER_OF_RATE_GROUPS 6 -#define NUMBER_OF_CHANNELS_2_4 14 -#define NUMBER_OF_CHANNELS_5 35 - -struct wl1271_radio_parms { - u8 id; - u8 padding[3]; - - /* Static radio parameters */ - /* 2.4GHz */ - u8 rx_trace_loss; - u8 tx_trace_loss; - s8 rx_rssi_and_proc_compens[RSSI_AND_PROCESS_COMPENSATION_SIZE]; - - /* 5GHz */ - u8 rx_trace_loss_5[NUMBER_OF_SUB_BANDS_5]; - u8 tx_trace_loss_5[NUMBER_OF_SUB_BANDS_5]; - s8 rx_rssi_and_proc_compens_5[RSSI_AND_PROCESS_COMPENSATION_SIZE]; - - /* Dynamic radio parameters */ - /* 2.4GHz */ - s16 tx_ref_pd_voltage; - s8 tx_ref_power; - s8 tx_offset_db; - - s8 tx_rate_limits_normal[NUMBER_OF_RATE_GROUPS]; - s8 tx_rate_limits_degraded[NUMBER_OF_RATE_GROUPS]; - - s8 tx_channel_limits_11b[NUMBER_OF_CHANNELS_2_4]; - s8 tx_channel_limits_ofdm[NUMBER_OF_CHANNELS_2_4]; - s8 tx_pdv_rate_offsets[NUMBER_OF_RATE_GROUPS]; - - u8 tx_ibias[NUMBER_OF_RATE_GROUPS]; - u8 rx_fem_insertion_loss; - - u8 padding2; - - /* 5GHz */ - s16 tx_ref_pd_voltage_5[NUMBER_OF_SUB_BANDS_5]; - s8 tx_ref_power_5[NUMBER_OF_SUB_BANDS_5]; - s8 tx_offset_db_5[NUMBER_OF_SUB_BANDS_5]; - - s8 tx_rate_limits_normal_5[NUMBER_OF_RATE_GROUPS]; - s8 tx_rate_limits_degraded_5[NUMBER_OF_RATE_GROUPS]; - - s8 tx_channel_limits_ofdm_5[NUMBER_OF_CHANNELS_5]; - s8 tx_pdv_rate_offsets_5[NUMBER_OF_RATE_GROUPS]; - - /* FIXME: this is inconsistent with the types for 2.4GHz */ - s8 tx_ibias_5[NUMBER_OF_RATE_GROUPS]; - s8 rx_fem_insertion_loss_5[NUMBER_OF_SUB_BANDS_5]; - - u8 padding3[2]; -} __attribute__ ((packed)); - #endif diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index 27298b19d5bd..b62c00ff42fe 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -30,7 +30,9 @@ #include <linux/spi/spi.h> #include <linux/crc32.h> #include <linux/etherdevice.h> +#include <linux/vmalloc.h> #include <linux/spi/wl12xx.h> +#include <linux/inetdevice.h> #include "wl1271.h" #include "wl12xx_80211.h" @@ -45,10 +47,314 @@ #include "wl1271_cmd.h" #include "wl1271_boot.h" +static struct conf_drv_settings default_conf = { + .sg = { + .per_threshold = 7500, + .max_scan_compensation_time = 120000, + .nfs_sample_interval = 400, + .load_ratio = 50, + .auto_ps_mode = 0, + .probe_req_compensation = 170, + .scan_window_compensation = 50, + .antenna_config = 0, + .beacon_miss_threshold = 60, + .rate_adaptation_threshold = CONF_HW_BIT_RATE_12MBPS, + .rate_adaptation_snr = 0 + }, + .rx = { + .rx_msdu_life_time = 512000, + .packet_detection_threshold = 0, + .ps_poll_timeout = 15, + .upsd_timeout = 15, + .rts_threshold = 2347, + .rx_cca_threshold = 0xFFEF, + .irq_blk_threshold = 0, + .irq_pkt_threshold = USHORT_MAX, + .irq_timeout = 5, + .queue_type = CONF_RX_QUEUE_TYPE_LOW_PRIORITY, + }, + .tx = { + .tx_energy_detection = 0, + .rc_conf = { + .enabled_rates = CONF_TX_RATE_MASK_UNSPECIFIED, + .short_retry_limit = 10, + .long_retry_limit = 10, + .aflags = 0 + }, + .ac_conf_count = 4, + .ac_conf = { + [0] = { + .ac = CONF_TX_AC_BE, + .cw_min = 15, + .cw_max = 63, + .aifsn = 3, + .tx_op_limit = 0, + }, + [1] = { + .ac = CONF_TX_AC_BK, + .cw_min = 15, + .cw_max = 63, + .aifsn = 7, + .tx_op_limit = 0, + }, + [2] = { + .ac = CONF_TX_AC_VI, + .cw_min = 15, + .cw_max = 63, + .aifsn = CONF_TX_AIFS_PIFS, + .tx_op_limit = 3008, + }, + [3] = { + .ac = CONF_TX_AC_VO, + .cw_min = 15, + .cw_max = 63, + .aifsn = CONF_TX_AIFS_PIFS, + .tx_op_limit = 1504, + }, + }, + .tid_conf_count = 7, + .tid_conf = { + [0] = { + .queue_id = 0, + .channel_type = CONF_CHANNEL_TYPE_DCF, + .tsid = CONF_TX_AC_BE, + .ps_scheme = CONF_PS_SCHEME_LEGACY, + .ack_policy = CONF_ACK_POLICY_LEGACY, + .apsd_conf = {0, 0}, + }, + [1] = { + .queue_id = 1, + .channel_type = CONF_CHANNEL_TYPE_DCF, + .tsid = CONF_TX_AC_BE, + .ps_scheme = CONF_PS_SCHEME_LEGACY, + .ack_policy = CONF_ACK_POLICY_LEGACY, + .apsd_conf = {0, 0}, + }, + [2] = { + .queue_id = 2, + .channel_type = CONF_CHANNEL_TYPE_DCF, + .tsid = CONF_TX_AC_BE, + .ps_scheme = CONF_PS_SCHEME_LEGACY, + .ack_policy = CONF_ACK_POLICY_LEGACY, + .apsd_conf = {0, 0}, + }, + [3] = { + .queue_id = 3, + .channel_type = CONF_CHANNEL_TYPE_DCF, + .tsid = CONF_TX_AC_BE, + .ps_scheme = CONF_PS_SCHEME_LEGACY, + .ack_policy = CONF_ACK_POLICY_LEGACY, + .apsd_conf = {0, 0}, + }, + [4] = { + .queue_id = 4, + .channel_type = CONF_CHANNEL_TYPE_DCF, + .tsid = CONF_TX_AC_BE, + .ps_scheme = CONF_PS_SCHEME_LEGACY, + .ack_policy = CONF_ACK_POLICY_LEGACY, + .apsd_conf = {0, 0}, + }, + [5] = { + .queue_id = 5, + .channel_type = CONF_CHANNEL_TYPE_DCF, + .tsid = CONF_TX_AC_BE, + .ps_scheme = CONF_PS_SCHEME_LEGACY, + .ack_policy = CONF_ACK_POLICY_LEGACY, + .apsd_conf = {0, 0}, + }, + [6] = { + .queue_id = 6, + .channel_type = CONF_CHANNEL_TYPE_DCF, + .tsid = CONF_TX_AC_BE, + .ps_scheme = CONF_PS_SCHEME_LEGACY, + .ack_policy = CONF_ACK_POLICY_LEGACY, + .apsd_conf = {0, 0}, + } + }, + .frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD, + .tx_compl_timeout = 5, + .tx_compl_threshold = 5 + }, + .conn = { + .wake_up_event = CONF_WAKE_UP_EVENT_DTIM, + .listen_interval = 0, + .bcn_filt_mode = CONF_BCN_FILT_MODE_ENABLED, + .bcn_filt_ie_count = 1, + .bcn_filt_ie = { + [0] = { + .ie = WLAN_EID_CHANNEL_SWITCH, + .rule = CONF_BCN_RULE_PASS_ON_APPEARANCE, + } + }, + .synch_fail_thold = 5, + .bss_lose_timeout = 100, + .beacon_rx_timeout = 10000, + .broadcast_timeout = 20000, + .rx_broadcast_in_ps = 1, + .ps_poll_threshold = 4, + .sig_trigger_count = 2, + .sig_trigger = { + [0] = { + .threshold = -75, + .pacing = 500, + .metric = CONF_TRIG_METRIC_RSSI_BEACON, + .type = CONF_TRIG_EVENT_TYPE_EDGE, + .direction = CONF_TRIG_EVENT_DIR_LOW, + .hysteresis = 2, + .index = 0, + .enable = 1 + }, + [1] = { + .threshold = -75, + .pacing = 500, + .metric = CONF_TRIG_METRIC_RSSI_BEACON, + .type = CONF_TRIG_EVENT_TYPE_EDGE, + .direction = CONF_TRIG_EVENT_DIR_HIGH, + .hysteresis = 2, + .index = 1, + .enable = 1 + } + }, + .sig_weights = { + .rssi_bcn_avg_weight = 10, + .rssi_pkt_avg_weight = 10, + .snr_bcn_avg_weight = 10, + .snr_pkt_avg_weight = 10 + }, + .bet_enable = CONF_BET_MODE_ENABLE, + .bet_max_consecutive = 10, + .psm_entry_retries = 3 + }, + .init = { + .sr_err_tbl = { + [0] = { + .len = 7, + .upper_limit = 0x03, + .values = { + 0x18, 0x10, 0x05, 0xfb, 0xf0, 0xe8, + 0x00 } + }, + [1] = { + .len = 7, + .upper_limit = 0x03, + .values = { + 0x18, 0x10, 0x05, 0xf6, 0xf0, 0xe8, + 0x00 } + }, + [2] = { + .len = 7, + .upper_limit = 0x03, + .values = { + 0x18, 0x10, 0x05, 0xfb, 0xf0, 0xe8, + 0x00 } + } + }, + .sr_enable = 1, + .genparam = { + .ref_clk = CONF_REF_CLK_38_4_E, + .settling_time = 5, + .clk_valid_on_wakeup = 0, + .dc2dcmode = 0, + .single_dual_band = CONF_SINGLE_BAND, + .tx_bip_fem_autodetect = 0, + .tx_bip_fem_manufacturer = 1, + .settings = 1, + }, + .radioparam = { + .rx_trace_loss = 10, + .tx_trace_loss = 10, + .rx_rssi_and_proc_compens = { + 0xec, 0xf6, 0x00, 0x0c, 0x18, 0xf8, + 0xfc, 0x00, 0x08, 0x10, 0xf0, 0xf8, + 0x00, 0x0a, 0x14 }, + .rx_trace_loss_5 = { 0, 0, 0, 0, 0, 0, 0 }, + .tx_trace_loss_5 = { 0, 0, 0, 0, 0, 0, 0 }, + .rx_rssi_and_proc_compens_5 = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 }, + .tx_ref_pd_voltage = 0x24e, + .tx_ref_power = 0x78, + .tx_offset_db = 0x0, + .tx_rate_limits_normal = { + 0x1e, 0x1f, 0x22, 0x24, 0x28, 0x29 }, + .tx_rate_limits_degraded = { + 0x1b, 0x1c, 0x1e, 0x20, 0x24, 0x25 }, + .tx_channel_limits_11b = { + 0x22, 0x50, 0x50, 0x50, 0x50, 0x50, + 0x50, 0x50, 0x50, 0x50, 0x22, 0x50, + 0x22, 0x50 }, + .tx_channel_limits_ofdm = { + 0x20, 0x50, 0x50, 0x50, 0x50, 0x50, + 0x50, 0x50, 0x50, 0x50, 0x20, 0x50, + 0x20, 0x50 }, + .tx_pdv_rate_offsets = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + .tx_ibias = { + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x27 }, + .rx_fem_insertion_loss = 0x14, + .tx_ref_pd_voltage_5 = { + 0x0190, 0x01a4, 0x01c3, 0x01d8, + 0x020a, 0x021c }, + .tx_ref_power_5 = { + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + .tx_offset_db_5 = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + .tx_rate_limits_normal_5 = { + 0x1b, 0x1e, 0x21, 0x23, 0x27, 0x00 }, + .tx_rate_limits_degraded_5 = { + 0x1b, 0x1e, 0x21, 0x23, 0x27, 0x00 }, + .tx_channel_limits_ofdm_5 = { + 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, + 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, + 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, + 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, + 0x50, 0x50, 0x50 }, + .tx_pdv_rate_offsets_5 = { + 0x01, 0x02, 0x02, 0x02, 0x02, 0x00 }, + .tx_ibias_5 = { + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 }, + .rx_fem_insertion_loss_5 = { + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 } + } + } +}; + +static LIST_HEAD(wl_list); + +static void wl1271_conf_init(struct wl1271 *wl) +{ + + /* + * This function applies the default configuration to the driver. This + * function is invoked upon driver load (spi probe.) + * + * The configuration is stored in a run-time structure in order to + * facilitate for run-time adjustment of any of the parameters. Making + * changes to the configuration structure will apply the new values on + * the next interface up (wl1271_op_start.) + */ + + /* apply driver default configuration */ + memcpy(&wl->conf, &default_conf, sizeof(default_conf)); + + if (wl1271_11a_enabled()) + wl->conf.init.genparam.single_dual_band = CONF_DUAL_BAND; +} + + static int wl1271_plt_init(struct wl1271 *wl) { int ret; + ret = wl1271_cmd_general_parms(wl); + if (ret < 0) + return ret; + + ret = wl1271_cmd_radio_parms(wl); + if (ret < 0) + return ret; + ret = wl1271_acx_init_mem_config(wl); if (ret < 0) return ret; @@ -75,20 +381,14 @@ static void wl1271_power_on(struct wl1271 *wl) wl->set_power(true); } -static void wl1271_fw_status(struct wl1271 *wl, struct wl1271_fw_status *status) +static void wl1271_fw_status(struct wl1271 *wl, + struct wl1271_fw_status *status) { u32 total = 0; int i; - /* - * FIXME: Reading the FW status directly from the registers seems to - * be the right thing to do, but it doesn't work. And in the - * reference driver, there is a workaround called - * USE_SDIO_24M_WORKAROUND, which reads the status from memory - * instead, so we do the same here. - */ - - wl1271_spi_mem_read(wl, STATUS_MEM_ADDRESS, status, sizeof(*status)); + wl1271_spi_read(wl, FW_STATUS_ADDR, status, + sizeof(*status), false); wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, " "drv_rx_counter = %d, tx_results_counter = %d)", @@ -99,25 +399,28 @@ static void wl1271_fw_status(struct wl1271 *wl, struct wl1271_fw_status *status) /* update number of available TX blocks */ for (i = 0; i < NUM_TX_QUEUES; i++) { - u32 cnt = status->tx_released_blks[i] - wl->tx_blocks_freed[i]; - wl->tx_blocks_freed[i] = status->tx_released_blks[i]; + u32 cnt = le32_to_cpu(status->tx_released_blks[i]) - + wl->tx_blocks_freed[i]; + + wl->tx_blocks_freed[i] = + le32_to_cpu(status->tx_released_blks[i]); wl->tx_blocks_available += cnt; total += cnt; } /* if more blocks are available now, schedule some tx work */ if (total && !skb_queue_empty(&wl->tx_queue)) - schedule_work(&wl->tx_work); + ieee80211_queue_work(wl->hw, &wl->tx_work); /* update the host-chipset time offset */ - wl->time_offset = jiffies_to_usecs(jiffies) - status->fw_localtime; + wl->time_offset = jiffies_to_usecs(jiffies) - + le32_to_cpu(status->fw_localtime); } -#define WL1271_IRQ_MAX_LOOPS 10 static void wl1271_irq_work(struct work_struct *work) { - u32 intr, ctr = WL1271_IRQ_MAX_LOOPS; int ret; + u32 intr; struct wl1271 *wl = container_of(work, struct wl1271, irq_work); @@ -132,9 +435,10 @@ static void wl1271_irq_work(struct work_struct *work) if (ret < 0) goto out; - wl1271_reg_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); + wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); - intr = wl1271_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR); + wl1271_fw_status(wl, wl->fw_status); + intr = le32_to_cpu(wl->fw_status->intr); if (!intr) { wl1271_debug(DEBUG_IRQ, "Zero interrupt received."); goto out_sleep; @@ -142,46 +446,39 @@ static void wl1271_irq_work(struct work_struct *work) intr &= WL1271_INTR_MASK; - do { - wl1271_fw_status(wl, wl->fw_status); - - - if (intr & (WL1271_ACX_INTR_EVENT_A | - WL1271_ACX_INTR_EVENT_B)) { - wl1271_debug(DEBUG_IRQ, - "WL1271_ACX_INTR_EVENT (0x%x)", intr); - if (intr & WL1271_ACX_INTR_EVENT_A) - wl1271_event_handle(wl, 0); - else - wl1271_event_handle(wl, 1); - } + if (intr & WL1271_ACX_INTR_EVENT_A) { + bool do_ack = (intr & WL1271_ACX_INTR_EVENT_B) ? false : true; + wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A"); + wl1271_event_handle(wl, 0, do_ack); + } - if (intr & WL1271_ACX_INTR_INIT_COMPLETE) - wl1271_debug(DEBUG_IRQ, - "WL1271_ACX_INTR_INIT_COMPLETE"); + if (intr & WL1271_ACX_INTR_EVENT_B) { + wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B"); + wl1271_event_handle(wl, 1, true); + } - if (intr & WL1271_ACX_INTR_HW_AVAILABLE) - wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE"); + if (intr & WL1271_ACX_INTR_INIT_COMPLETE) + wl1271_debug(DEBUG_IRQ, + "WL1271_ACX_INTR_INIT_COMPLETE"); - if (intr & WL1271_ACX_INTR_DATA) { - u8 tx_res_cnt = wl->fw_status->tx_results_counter - - wl->tx_results_count; + if (intr & WL1271_ACX_INTR_HW_AVAILABLE) + wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE"); - wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA"); + if (intr & WL1271_ACX_INTR_DATA) { + u8 tx_res_cnt = wl->fw_status->tx_results_counter - + wl->tx_results_count; - /* check for tx results */ - if (tx_res_cnt) - wl1271_tx_complete(wl, tx_res_cnt); + wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA"); - wl1271_rx(wl, wl->fw_status); - } + /* check for tx results */ + if (tx_res_cnt) + wl1271_tx_complete(wl, tx_res_cnt); - intr = wl1271_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR); - intr &= WL1271_INTR_MASK; - } while (intr && --ctr); + wl1271_rx(wl, wl->fw_status); + } out_sleep: - wl1271_reg_write32(wl, ACX_REG_INTERRUPT_MASK, + wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK)); wl1271_ps_elp_sleep(wl); @@ -205,7 +502,7 @@ static irqreturn_t wl1271_irq(int irq, void *cookie) wl->elp_compl = NULL; } - schedule_work(&wl->irq_work); + ieee80211_queue_work(wl->hw, &wl->irq_work); spin_unlock_irqrestore(&wl->wl_lock, flags); return IRQ_HANDLED; @@ -231,7 +528,7 @@ static int wl1271_fetch_firmware(struct wl1271 *wl) } wl->fw_len = fw->size; - wl->fw = kmalloc(wl->fw_len, GFP_KERNEL); + wl->fw = vmalloc(wl->fw_len); if (!wl->fw) { wl1271_error("could not allocate memory for the firmware"); @@ -292,7 +589,7 @@ static void wl1271_fw_wakeup(struct wl1271 *wl) u32 elp_reg; elp_reg = ELPCTRL_WAKE_UP; - wl1271_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg); + wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg); } static int wl1271_setup(struct wl1271 *wl) @@ -314,6 +611,7 @@ static int wl1271_setup(struct wl1271 *wl) static int wl1271_chip_wakeup(struct wl1271 *wl) { + struct wl1271_partition_set partition; int ret = 0; wl1271_power_on(wl); @@ -323,11 +621,10 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) /* We don't need a real memory partition here, because we only want * to use the registers at this point. */ - wl1271_set_partition(wl, - 0x00000000, - 0x00000000, - REGISTERS_BASE, - REGISTERS_DOWN_SIZE); + memset(&partition, 0, sizeof(partition)); + partition.reg.start = REGISTERS_BASE; + partition.reg.size = REGISTERS_DOWN_SIZE; + wl1271_set_partition(wl, &partition); /* ELP module wake up */ wl1271_fw_wakeup(wl); @@ -335,7 +632,7 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) /* whal_FwCtrl_BootSm() */ /* 0. read chip id from CHIP_ID */ - wl->chip.id = wl1271_reg_read32(wl, CHIP_ID_B); + wl->chip.id = wl1271_spi_read32(wl, CHIP_ID_B); /* 1. check if chip id is valid */ @@ -346,7 +643,7 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) ret = wl1271_setup(wl); if (ret < 0) - goto out; + goto out_power_off; break; case CHIP_ID_1271_PG20: wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)", @@ -354,56 +651,34 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) ret = wl1271_setup(wl); if (ret < 0) - goto out; + goto out_power_off; break; default: wl1271_error("unsupported chip id: 0x%x", wl->chip.id); ret = -ENODEV; - goto out; + goto out_power_off; } if (wl->fw == NULL) { ret = wl1271_fetch_firmware(wl); if (ret < 0) - goto out; + goto out_power_off; } /* No NVS from netlink, try to get it from the filesystem */ if (wl->nvs == NULL) { ret = wl1271_fetch_nvs(wl); if (ret < 0) - goto out; + goto out_power_off; } -out: - return ret; -} - -static void wl1271_filter_work(struct work_struct *work) -{ - struct wl1271 *wl = - container_of(work, struct wl1271, filter_work); - int ret; - - mutex_lock(&wl->mutex); - - if (wl->state == WL1271_STATE_OFF) - goto out; - - ret = wl1271_ps_elp_wakeup(wl, false); - if (ret < 0) - goto out; - - /* FIXME: replace the magic numbers with proper definitions */ - ret = wl1271_cmd_join(wl, wl->bss_type, 1, 100, 0); - if (ret < 0) - goto out_sleep; + goto out; -out_sleep: - wl1271_ps_elp_sleep(wl); +out_power_off: + wl1271_power_off(wl); out: - mutex_unlock(&wl->mutex); + return ret; } int wl1271_plt_start(struct wl1271 *wl) @@ -429,13 +704,26 @@ int wl1271_plt_start(struct wl1271 *wl) ret = wl1271_boot(wl); if (ret < 0) - goto out; + goto out_power_off; wl1271_notice("firmware booted in PLT mode (%s)", wl->chip.fw_ver); ret = wl1271_plt_init(wl); if (ret < 0) - goto out; + goto out_irq_disable; + + /* Make sure power saving is disabled */ + ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); + if (ret < 0) + goto out_irq_disable; + + goto out; + +out_irq_disable: + wl1271_disable_interrupts(wl); + +out_power_off: + wl1271_power_off(wl); out: mutex_unlock(&wl->mutex); @@ -462,6 +750,7 @@ int wl1271_plt_stop(struct wl1271 *wl) wl1271_power_off(wl); wl->state = WL1271_STATE_OFF; + wl->rx_counter = 0; out: mutex_unlock(&wl->mutex); @@ -481,7 +770,7 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) * before that, the tx_work will not be initialized! */ - schedule_work(&wl->tx_work); + ieee80211_queue_work(wl->hw, &wl->tx_work); /* * The workqueue is slow to process the tx_queue and we need stop @@ -501,6 +790,93 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) return NETDEV_TX_OK; } +static int wl1271_dev_notify(struct notifier_block *me, unsigned long what, + void *arg) +{ + struct net_device *dev; + struct wireless_dev *wdev; + struct wiphy *wiphy; + struct ieee80211_hw *hw; + struct wl1271 *wl; + struct wl1271 *wl_temp; + struct in_device *idev; + struct in_ifaddr *ifa = arg; + int ret = 0; + + /* FIXME: this ugly function should probably be implemented in the + * mac80211, and here should only be a simple callback handling actual + * setting of the filters. Now we need to dig up references to + * various structures to gain access to what we need. + * Also, because of this, there is no "initial" setting of the filter + * in "op_start", because we don't want to dig up struct net_device + * there - the filter will be set upon first change of the interface + * IP address. */ + + dev = ifa->ifa_dev->dev; + + wdev = dev->ieee80211_ptr; + if (wdev == NULL) + return NOTIFY_DONE; + + wiphy = wdev->wiphy; + if (wiphy == NULL) + return NOTIFY_DONE; + + hw = wiphy_priv(wiphy); + if (hw == NULL) + return NOTIFY_DONE; + + /* Check that the interface is one supported by this driver. */ + wl_temp = hw->priv; + list_for_each_entry(wl, &wl_list, list) { + if (wl == wl_temp) + break; + } + if (wl == NULL) + return NOTIFY_DONE; + + /* Get the interface IP address for the device. "ifa" will become + NULL if: + - there is no IPV4 protocol address configured + - there are multiple (virtual) IPV4 addresses configured + When "ifa" is NULL, filtering will be disabled. + */ + ifa = NULL; + idev = dev->ip_ptr; + if (idev) + ifa = idev->ifa_list; + + if (ifa && ifa->ifa_next) + ifa = NULL; + + mutex_lock(&wl->mutex); + + if (wl->state == WL1271_STATE_OFF) + goto out; + + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + goto out; + if (ifa) + ret = wl1271_acx_arp_ip_filter(wl, true, + (u8 *)&ifa->ifa_address, + ACX_IPV4_VERSION); + else + ret = wl1271_acx_arp_ip_filter(wl, false, NULL, + ACX_IPV4_VERSION); + wl1271_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); + + return NOTIFY_OK; +} + +static struct notifier_block wl1271_dev_notifier = { + .notifier_call = wl1271_dev_notify, +}; + + static int wl1271_op_start(struct ieee80211_hw *hw) { struct wl1271 *wl = hw->priv; @@ -523,22 +899,32 @@ static int wl1271_op_start(struct ieee80211_hw *hw) ret = wl1271_boot(wl); if (ret < 0) - goto out; + goto out_power_off; ret = wl1271_hw_init(wl); if (ret < 0) - goto out; + goto out_irq_disable; wl->state = WL1271_STATE_ON; wl1271_info("firmware booted (%s)", wl->chip.fw_ver); -out: - if (ret < 0) - wl1271_power_off(wl); + goto out; + +out_irq_disable: + wl1271_disable_interrupts(wl); + +out_power_off: + wl1271_power_off(wl); +out: mutex_unlock(&wl->mutex); + if (!ret) { + list_add(&wl->list, &wl_list); + register_inetaddr_notifier(&wl1271_dev_notifier); + } + return ret; } @@ -551,6 +937,9 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) wl1271_debug(DEBUG_MAC80211, "mac80211 stop"); + unregister_inetaddr_notifier(&wl1271_dev_notifier); + list_del(&wl->list); + mutex_lock(&wl->mutex); WARN_ON(wl->state != WL1271_STATE_ON); @@ -570,7 +959,6 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) cancel_work_sync(&wl->irq_work); cancel_work_sync(&wl->tx_work); - cancel_work_sync(&wl->filter_work); mutex_lock(&wl->mutex); @@ -581,19 +969,25 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) memset(wl->bssid, 0, ETH_ALEN); memset(wl->ssid, 0, IW_ESSID_MAX_SIZE + 1); wl->ssid_len = 0; - wl->listen_int = 1; wl->bss_type = MAX_BSS_TYPE; + wl->band = IEEE80211_BAND_2GHZ; wl->rx_counter = 0; wl->elp = false; wl->psm = 0; + wl->psm_entry_retry = 0; wl->tx_queue_stopped = false; wl->power_level = WL1271_DEFAULT_POWER_LEVEL; wl->tx_blocks_available = 0; wl->tx_results_count = 0; wl->tx_packets_count = 0; + wl->tx_security_last_seq = 0; + wl->tx_security_seq_16 = 0; + wl->tx_security_seq_32 = 0; wl->time_offset = 0; wl->session_counter = 0; + wl->joined = false; + for (i = 0; i < NUM_TX_QUEUES; i++) wl->tx_blocks_freed[i] = 0; @@ -611,6 +1005,12 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, conf->type, conf->mac_addr); mutex_lock(&wl->mutex); + if (wl->vif) { + ret = -EBUSY; + goto out; + } + + wl->vif = conf->vif; switch (conf->type) { case NL80211_IFTYPE_STATION: @@ -634,7 +1034,12 @@ out: static void wl1271_op_remove_interface(struct ieee80211_hw *hw, struct ieee80211_if_init_conf *conf) { + struct wl1271 *wl = hw->priv; + + mutex_lock(&wl->mutex); wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface"); + wl->vif = NULL; + mutex_unlock(&wl->mutex); } #if 0 @@ -657,23 +1062,24 @@ static int wl1271_op_config_interface(struct ieee80211_hw *hw, if (ret < 0) goto out; - memcpy(wl->bssid, conf->bssid, ETH_ALEN); + if (memcmp(wl->bssid, conf->bssid, ETH_ALEN)) { + wl1271_debug(DEBUG_MAC80211, "bssid changed"); - ret = wl1271_cmd_build_null_data(wl); - if (ret < 0) - goto out_sleep; + memcpy(wl->bssid, conf->bssid, ETH_ALEN); - wl->ssid_len = conf->ssid_len; - if (wl->ssid_len) - memcpy(wl->ssid, conf->ssid, wl->ssid_len); + ret = wl1271_cmd_join(wl); + if (ret < 0) + goto out_sleep; - if (wl->bss_type != BSS_TYPE_IBSS) { - /* FIXME: replace the magic numbers with proper definitions */ - ret = wl1271_cmd_join(wl, wl->bss_type, 5, 100, 1); + ret = wl1271_cmd_build_null_data(wl); if (ret < 0) goto out_sleep; } + wl->ssid_len = conf->ssid_len; + if (wl->ssid_len) + memcpy(wl->ssid, conf->ssid, wl->ssid_len); + if (conf->changed & IEEE80211_IFCC_BEACON) { beacon = ieee80211_beacon_get(hw, vif); ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON, @@ -691,12 +1097,6 @@ static int wl1271_op_config_interface(struct ieee80211_hw *hw, if (ret < 0) goto out_sleep; - - /* FIXME: replace the magic numbers with proper definitions */ - ret = wl1271_cmd_join(wl, wl->bss_type, 1, 100, 0); - - if (ret < 0) - goto out_sleep; } out_sleep: @@ -724,26 +1124,22 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) mutex_lock(&wl->mutex); + wl->band = conf->channel->band; + ret = wl1271_ps_elp_wakeup(wl, false); if (ret < 0) goto out; if (channel != wl->channel) { - u8 old_channel = wl->channel; + /* + * We assume that the stack will configure the right channel + * before associating, so we don't need to send a join + * command here. We will join the right channel when the + * BSSID changes + */ wl->channel = channel; - - /* FIXME: use beacon interval provided by mac80211 */ - ret = wl1271_cmd_join(wl, wl->bss_type, 1, 100, 0); - if (ret < 0) { - wl->channel = old_channel; - goto out_sleep; - } } - ret = wl1271_cmd_build_null_data(wl); - if (ret < 0) - goto out_sleep; - if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) { wl1271_info("psm enabled"); @@ -768,7 +1164,7 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) if (conf->power_level != wl->power_level) { ret = wl1271_acx_tx_power(wl, conf->power_level); if (ret < 0) - goto out; + goto out_sleep; wl->power_level = conf->power_level; } @@ -782,6 +1178,45 @@ out: return ret; } +struct wl1271_filter_params { + bool enabled; + int mc_list_length; + u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN]; +}; + +static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count, + struct dev_addr_list *mc_list) +{ + struct wl1271_filter_params *fp; + int i; + + fp = kzalloc(sizeof(*fp), GFP_ATOMIC); + if (!fp) { + wl1271_error("Out of memory setting filters."); + return 0; + } + + /* update multicast filtering parameters */ + fp->enabled = true; + if (mc_count > ACX_MC_ADDRESS_GROUP_MAX) { + mc_count = 0; + fp->enabled = false; + } + + fp->mc_list_length = 0; + for (i = 0; i < mc_count; i++) { + if (mc_list->da_addrlen == ETH_ALEN) { + memcpy(fp->mc_list[fp->mc_list_length], + mc_list->da_addr, ETH_ALEN); + fp->mc_list_length++; + } else + wl1271_warning("Unknown mc address length."); + mc_list = mc_list->next; + } + + return (u64)(unsigned long)fp; +} + #define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \ FIF_ALLMULTI | \ FIF_FCSFAIL | \ @@ -791,28 +1226,53 @@ out: static void wl1271_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed, - unsigned int *total,u64 multicast) + unsigned int *total, u64 multicast) { + struct wl1271_filter_params *fp = (void *)(unsigned long)multicast; struct wl1271 *wl = hw->priv; + int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter"); + mutex_lock(&wl->mutex); + + if (wl->state == WL1271_STATE_OFF) + goto out; + + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + goto out; + *total &= WL1271_SUPPORTED_FILTERS; changed &= WL1271_SUPPORTED_FILTERS; + if (*total & FIF_ALLMULTI) + ret = wl1271_acx_group_address_tbl(wl, false, NULL, 0); + else if (fp) + ret = wl1271_acx_group_address_tbl(wl, fp->enabled, + fp->mc_list, + fp->mc_list_length); + if (ret < 0) + goto out_sleep; + + kfree(fp); + + /* FIXME: We still need to set our filters properly */ + + /* determine, whether supported filter values have changed */ if (changed == 0) - return; + goto out_sleep; - /* FIXME: wl->rx_config and wl->rx_filter are not protected */ - wl->rx_config = WL1271_DEFAULT_RX_CONFIG; - wl->rx_filter = WL1271_DEFAULT_RX_FILTER; + /* apply configured filters */ + ret = wl1271_acx_rx_config(wl, wl->rx_config, wl->rx_filter); + if (ret < 0) + goto out_sleep; - /* - * FIXME: workqueues need to be properly cancelled on stop(), for - * now let's just disable changing the filter settings. They will - * be updated any on config(). - */ - /* schedule_work(&wl->filter_work); */ +out_sleep: + wl1271_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); } static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, @@ -823,6 +1283,8 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct wl1271 *wl = hw->priv; const u8 *addr; int ret; + u32 tx_seq_32 = 0; + u16 tx_seq_16 = 0; u8 key_type; static const u8 bcast_addr[ETH_ALEN] = @@ -861,11 +1323,15 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, key_type = KEY_TKIP; key_conf->hw_key_idx = key_conf->keyidx; + tx_seq_32 = wl->tx_security_seq_32; + tx_seq_16 = wl->tx_security_seq_16; break; case ALG_CCMP: key_type = KEY_AES; key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + tx_seq_32 = wl->tx_security_seq_32; + tx_seq_16 = wl->tx_security_seq_16; break; default: wl1271_error("Unknown key algo 0x%x", key_conf->alg); @@ -879,7 +1345,7 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ret = wl1271_cmd_set_key(wl, KEY_ADD_OR_REPLACE, key_conf->keyidx, key_type, key_conf->keylen, key_conf->key, - addr); + addr, tx_seq_32, tx_seq_16); if (ret < 0) { wl1271_error("Could not add or replace key"); goto out_sleep; @@ -890,7 +1356,7 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ret = wl1271_cmd_set_key(wl, KEY_REMOVE, key_conf->keyidx, key_type, key_conf->keylen, key_conf->key, - addr); + addr, 0, 0); if (ret < 0) { wl1271_error("Could not remove key"); goto out_sleep; @@ -921,13 +1387,13 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw, struct wl1271 *wl = hw->priv; int ret; u8 *ssid = NULL; - size_t ssid_len = 0; + size_t len = 0; wl1271_debug(DEBUG_MAC80211, "mac80211 hw scan"); if (req->n_ssids) { ssid = req->ssids[0].ssid; - ssid_len = req->ssids[0].ssid_len; + len = req->ssids[0].ssid_len; } mutex_lock(&wl->mutex); @@ -936,7 +1402,12 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw, if (ret < 0) goto out; - ret = wl1271_cmd_scan(hw->priv, ssid, ssid_len, 1, 0, 13, 3); + if (wl1271_11a_enabled()) + ret = wl1271_cmd_scan(hw->priv, ssid, len, 1, 0, + WL1271_SCAN_BAND_DUAL, 3); + else + ret = wl1271_cmd_scan(hw->priv, ssid, len, 1, 0, + WL1271_SCAN_BAND_2_4_GHZ, 3); wl1271_ps_elp_sleep(wl); @@ -969,6 +1440,22 @@ out: return ret; } +static u32 wl1271_enabled_rates_get(struct wl1271 *wl, u64 basic_rate_set) +{ + struct ieee80211_supported_band *band; + u32 enabled_rates = 0; + int bit; + + band = wl->hw->wiphy->bands[wl->band]; + for (bit = 0; bit < band->n_bitrates; bit++) { + if (basic_rate_set & 0x1) + enabled_rates |= band->bitrates[bit].hw_value; + basic_rate_set >>= 1; + } + + return enabled_rates; +} + static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, @@ -990,6 +1477,12 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, if (bss_conf->assoc) { wl->aid = bss_conf->aid; + /* + * with wl1271, we don't need to update the + * beacon_int and dtim_period, because the firmware + * updates it by itself when the first beacon is + * received after a join. + */ ret = wl1271_cmd_build_ps_poll(wl, wl->aid); if (ret < 0) goto out_sleep; @@ -1005,8 +1498,14 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, if (ret < 0) goto out_sleep; } + } else { + /* use defaults when not associated */ + wl->basic_rate_set = WL1271_DEFAULT_BASIC_RATE_SET; + wl->aid = 0; } + } + if (changed & BSS_CHANGED_ERP_SLOT) { if (bss_conf->use_short_slot) ret = wl1271_acx_slot(wl, SLOT_TIME_SHORT); @@ -1036,6 +1535,17 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, } } + if (changed & BSS_CHANGED_BASIC_RATES) { + wl->basic_rate_set = wl1271_enabled_rates_get( + wl, bss_conf->basic_rates); + + ret = wl1271_acx_rate_policies(wl, wl->basic_rate_set); + if (ret < 0) { + wl1271_warning("Set rate policies failed %d", ret); + goto out_sleep; + } + } + out_sleep: wl1271_ps_elp_sleep(wl); @@ -1047,44 +1557,44 @@ out: /* can't be const, mac80211 writes to this */ static struct ieee80211_rate wl1271_rates[] = { { .bitrate = 10, - .hw_value = 0x1, - .hw_value_short = 0x1, }, + .hw_value = CONF_HW_BIT_RATE_1MBPS, + .hw_value_short = CONF_HW_BIT_RATE_1MBPS, }, { .bitrate = 20, - .hw_value = 0x2, - .hw_value_short = 0x2, + .hw_value = CONF_HW_BIT_RATE_2MBPS, + .hw_value_short = CONF_HW_BIT_RATE_2MBPS, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 55, - .hw_value = 0x4, - .hw_value_short = 0x4, + .hw_value = CONF_HW_BIT_RATE_5_5MBPS, + .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 110, - .hw_value = 0x20, - .hw_value_short = 0x20, + .hw_value = CONF_HW_BIT_RATE_11MBPS, + .hw_value_short = CONF_HW_BIT_RATE_11MBPS, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 60, - .hw_value = 0x8, - .hw_value_short = 0x8, }, + .hw_value = CONF_HW_BIT_RATE_6MBPS, + .hw_value_short = CONF_HW_BIT_RATE_6MBPS, }, { .bitrate = 90, - .hw_value = 0x10, - .hw_value_short = 0x10, }, + .hw_value = CONF_HW_BIT_RATE_9MBPS, + .hw_value_short = CONF_HW_BIT_RATE_9MBPS, }, { .bitrate = 120, - .hw_value = 0x40, - .hw_value_short = 0x40, }, + .hw_value = CONF_HW_BIT_RATE_12MBPS, + .hw_value_short = CONF_HW_BIT_RATE_12MBPS, }, { .bitrate = 180, - .hw_value = 0x80, - .hw_value_short = 0x80, }, + .hw_value = CONF_HW_BIT_RATE_18MBPS, + .hw_value_short = CONF_HW_BIT_RATE_18MBPS, }, { .bitrate = 240, - .hw_value = 0x200, - .hw_value_short = 0x200, }, + .hw_value = CONF_HW_BIT_RATE_24MBPS, + .hw_value_short = CONF_HW_BIT_RATE_24MBPS, }, { .bitrate = 360, - .hw_value = 0x400, - .hw_value_short = 0x400, }, + .hw_value = CONF_HW_BIT_RATE_36MBPS, + .hw_value_short = CONF_HW_BIT_RATE_36MBPS, }, { .bitrate = 480, - .hw_value = 0x800, - .hw_value_short = 0x800, }, + .hw_value = CONF_HW_BIT_RATE_48MBPS, + .hw_value_short = CONF_HW_BIT_RATE_48MBPS, }, { .bitrate = 540, - .hw_value = 0x1000, - .hw_value_short = 0x1000, }, + .hw_value = CONF_HW_BIT_RATE_54MBPS, + .hw_value_short = CONF_HW_BIT_RATE_54MBPS, }, }; /* can't be const, mac80211 writes to this */ @@ -1112,6 +1622,88 @@ static struct ieee80211_supported_band wl1271_band_2ghz = { .n_bitrates = ARRAY_SIZE(wl1271_rates), }; +/* 5 GHz data rates for WL1273 */ +static struct ieee80211_rate wl1271_rates_5ghz[] = { + { .bitrate = 60, + .hw_value = CONF_HW_BIT_RATE_6MBPS, + .hw_value_short = CONF_HW_BIT_RATE_6MBPS, }, + { .bitrate = 90, + .hw_value = CONF_HW_BIT_RATE_9MBPS, + .hw_value_short = CONF_HW_BIT_RATE_9MBPS, }, + { .bitrate = 120, + .hw_value = CONF_HW_BIT_RATE_12MBPS, + .hw_value_short = CONF_HW_BIT_RATE_12MBPS, }, + { .bitrate = 180, + .hw_value = CONF_HW_BIT_RATE_18MBPS, + .hw_value_short = CONF_HW_BIT_RATE_18MBPS, }, + { .bitrate = 240, + .hw_value = CONF_HW_BIT_RATE_24MBPS, + .hw_value_short = CONF_HW_BIT_RATE_24MBPS, }, + { .bitrate = 360, + .hw_value = CONF_HW_BIT_RATE_36MBPS, + .hw_value_short = CONF_HW_BIT_RATE_36MBPS, }, + { .bitrate = 480, + .hw_value = CONF_HW_BIT_RATE_48MBPS, + .hw_value_short = CONF_HW_BIT_RATE_48MBPS, }, + { .bitrate = 540, + .hw_value = CONF_HW_BIT_RATE_54MBPS, + .hw_value_short = CONF_HW_BIT_RATE_54MBPS, }, +}; + +/* 5 GHz band channels for WL1273 */ +static struct ieee80211_channel wl1271_channels_5ghz[] = { + { .hw_value = 183, .center_freq = 4915}, + { .hw_value = 184, .center_freq = 4920}, + { .hw_value = 185, .center_freq = 4925}, + { .hw_value = 187, .center_freq = 4935}, + { .hw_value = 188, .center_freq = 4940}, + { .hw_value = 189, .center_freq = 4945}, + { .hw_value = 192, .center_freq = 4960}, + { .hw_value = 196, .center_freq = 4980}, + { .hw_value = 7, .center_freq = 5035}, + { .hw_value = 8, .center_freq = 5040}, + { .hw_value = 9, .center_freq = 5045}, + { .hw_value = 11, .center_freq = 5055}, + { .hw_value = 12, .center_freq = 5060}, + { .hw_value = 16, .center_freq = 5080}, + { .hw_value = 34, .center_freq = 5170}, + { .hw_value = 36, .center_freq = 5180}, + { .hw_value = 38, .center_freq = 5190}, + { .hw_value = 40, .center_freq = 5200}, + { .hw_value = 42, .center_freq = 5210}, + { .hw_value = 44, .center_freq = 5220}, + { .hw_value = 46, .center_freq = 5230}, + { .hw_value = 48, .center_freq = 5240}, + { .hw_value = 52, .center_freq = 5260}, + { .hw_value = 56, .center_freq = 5280}, + { .hw_value = 60, .center_freq = 5300}, + { .hw_value = 64, .center_freq = 5320}, + { .hw_value = 100, .center_freq = 5500}, + { .hw_value = 104, .center_freq = 5520}, + { .hw_value = 108, .center_freq = 5540}, + { .hw_value = 112, .center_freq = 5560}, + { .hw_value = 116, .center_freq = 5580}, + { .hw_value = 120, .center_freq = 5600}, + { .hw_value = 124, .center_freq = 5620}, + { .hw_value = 128, .center_freq = 5640}, + { .hw_value = 132, .center_freq = 5660}, + { .hw_value = 136, .center_freq = 5680}, + { .hw_value = 140, .center_freq = 5700}, + { .hw_value = 149, .center_freq = 5745}, + { .hw_value = 153, .center_freq = 5765}, + { .hw_value = 157, .center_freq = 5785}, + { .hw_value = 161, .center_freq = 5805}, + { .hw_value = 165, .center_freq = 5825}, +}; + + +static struct ieee80211_supported_band wl1271_band_5ghz = { + .channels = wl1271_channels_5ghz, + .n_channels = ARRAY_SIZE(wl1271_channels_5ghz), + .bitrates = wl1271_rates_5ghz, + .n_bitrates = ARRAY_SIZE(wl1271_rates_5ghz), +}; + static const struct ieee80211_ops wl1271_ops = { .start = wl1271_op_start, .stop = wl1271_op_stop, @@ -1119,6 +1711,7 @@ static const struct ieee80211_ops wl1271_ops = { .remove_interface = wl1271_op_remove_interface, .config = wl1271_op_config, /* .config_interface = wl1271_op_config_interface, */ + .prepare_multicast = wl1271_op_prepare_multicast, .configure_filter = wl1271_op_configure_filter, .tx = wl1271_op_tx, .set_key = wl1271_op_set_key, @@ -1151,24 +1744,26 @@ static int wl1271_register_hw(struct wl1271 *wl) static int wl1271_init_ieee80211(struct wl1271 *wl) { - /* - * The tx descriptor buffer and the TKIP space. - * - * FIXME: add correct 1271 descriptor size - */ - wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE; + /* The tx descriptor buffer and the TKIP space. */ + wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE + + sizeof(struct wl1271_tx_hw_descr); /* unit us */ /* FIXME: find a proper value */ wl->hw->channel_change_time = 10000; wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | - IEEE80211_HW_NOISE_DBM; + IEEE80211_HW_NOISE_DBM | + IEEE80211_HW_BEACON_FILTER | + IEEE80211_HW_SUPPORTS_PS; wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); wl->hw->wiphy->max_scan_ssids = 1; wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1271_band_2ghz; + if (wl1271_11a_enabled()) + wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl1271_band_5ghz; + SET_IEEE80211_DEV(wl->hw, &wl->spi->dev); return 0; @@ -1213,29 +1808,33 @@ static int __devinit wl1271_probe(struct spi_device *spi) wl = hw->priv; memset(wl, 0, sizeof(*wl)); + INIT_LIST_HEAD(&wl->list); + wl->hw = hw; dev_set_drvdata(&spi->dev, wl); wl->spi = spi; skb_queue_head_init(&wl->tx_queue); - INIT_WORK(&wl->filter_work, wl1271_filter_work); + INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work); wl->channel = WL1271_DEFAULT_CHANNEL; wl->scanning = false; wl->default_key = 0; - wl->listen_int = 1; wl->rx_counter = 0; wl->rx_config = WL1271_DEFAULT_RX_CONFIG; wl->rx_filter = WL1271_DEFAULT_RX_FILTER; wl->elp = false; wl->psm = 0; wl->psm_requested = false; + wl->psm_entry_retry = 0; wl->tx_queue_stopped = false; wl->power_level = WL1271_DEFAULT_POWER_LEVEL; + wl->basic_rate_set = WL1271_DEFAULT_BASIC_RATE_SET; + wl->band = IEEE80211_BAND_2GHZ; + wl->vif = NULL; + wl->joined = false; - /* We use the default power on sleep time until we know which chip - * we're using */ - for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) + for (i = 0; i < ACX_TX_DESCRIPTORS; i++) wl->tx_frames[i] = NULL; spin_lock_init(&wl->wl_lock); @@ -1250,13 +1849,6 @@ static int __devinit wl1271_probe(struct spi_device *spi) wl->state = WL1271_STATE_OFF; mutex_init(&wl->mutex); - wl->rx_descriptor = kmalloc(sizeof(*wl->rx_descriptor), GFP_KERNEL); - if (!wl->rx_descriptor) { - wl1271_error("could not allocate memory for rx descriptor"); - ret = -ENOMEM; - goto out_free; - } - /* This is the only SPI value that we need to set here, the rest * comes from the board-peripherals file */ spi->bits_per_word = 32; @@ -1298,6 +1890,9 @@ static int __devinit wl1271_probe(struct spi_device *spi) } dev_set_drvdata(&wl1271_device.dev, wl); + /* Apply default driver configuration. */ + wl1271_conf_init(wl); + ret = wl1271_init_ieee80211(wl); if (ret) goto out_platform; @@ -1319,9 +1914,6 @@ static int __devinit wl1271_probe(struct spi_device *spi) free_irq(wl->irq, wl); out_free: - kfree(wl->rx_descriptor); - wl->rx_descriptor = NULL; - ieee80211_free_hw(hw); return ret; @@ -1337,14 +1929,11 @@ static int __devexit wl1271_remove(struct spi_device *spi) platform_device_unregister(&wl1271_device); free_irq(wl->irq, wl); kfree(wl->target_mem_map); - kfree(wl->fw); + vfree(wl->fw); wl->fw = NULL; kfree(wl->nvs); wl->nvs = NULL; - kfree(wl->rx_descriptor); - wl->rx_descriptor = NULL; - kfree(wl->fw_status); kfree(wl->tx_res_if); @@ -1391,3 +1980,5 @@ module_exit(wl1271_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>"); +MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); +MODULE_FIRMWARE(WL1271_FW_NAME); diff --git a/drivers/net/wireless/wl12xx/wl1271_ps.c b/drivers/net/wireless/wl12xx/wl1271_ps.c index 1dc74b0c7736..507cd91d7eed 100644 --- a/drivers/net/wireless/wl12xx/wl1271_ps.c +++ b/drivers/net/wireless/wl12xx/wl1271_ps.c @@ -27,25 +27,38 @@ #define WL1271_WAKEUP_TIMEOUT 500 +void wl1271_elp_work(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wl1271 *wl; + + dwork = container_of(work, struct delayed_work, work); + wl = container_of(dwork, struct wl1271, elp_work); + + wl1271_debug(DEBUG_PSM, "elp work"); + + mutex_lock(&wl->mutex); + + if (wl->elp || !wl->psm) + goto out; + + wl1271_debug(DEBUG_PSM, "chip to elp"); + wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP); + wl->elp = true; + +out: + mutex_unlock(&wl->mutex); +} + +#define ELP_ENTRY_DELAY 5 + /* Routines to toggle sleep mode while in ELP */ void wl1271_ps_elp_sleep(struct wl1271 *wl) { - /* - * FIXME: due to a problem in the firmware (causing a firmware - * crash), ELP entry is prevented below. Remove the "true" to - * re-enable ELP entry. - */ - if (true || wl->elp || !wl->psm) - return; - - /* - * Go to ELP unless there is work already pending - pending work - * will immediately wakeup the chipset anyway. - */ - if (!work_pending(&wl->irq_work) && !work_pending(&wl->tx_work)) { - wl1271_debug(DEBUG_PSM, "chip to elp"); - wl1271_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP); - wl->elp = true; + if (wl->psm) { + cancel_delayed_work(&wl->elp_work); + ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, + msecs_to_jiffies(ELP_ENTRY_DELAY)); } } @@ -73,7 +86,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake) wl->elp_compl = &compl; spin_unlock_irqrestore(&wl->wl_lock, flags); - wl1271_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP); + wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP); if (!pending) { ret = wait_for_completion_timeout( @@ -111,6 +124,17 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode) switch (mode) { case STATION_POWER_SAVE_MODE: wl1271_debug(DEBUG_PSM, "entering psm"); + + /* enable beacon filtering */ + ret = wl1271_acx_beacon_filter_opt(wl, true); + if (ret < 0) + return ret; + + /* enable beacon early termination */ + ret = wl1271_acx_bet_enable(wl, true); + if (ret < 0) + return ret; + ret = wl1271_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE); if (ret < 0) return ret; @@ -128,6 +152,16 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode) if (ret < 0) return ret; + /* disable beacon early termination */ + ret = wl1271_acx_bet_enable(wl, false); + if (ret < 0) + return ret; + + /* disable beacon filtering */ + ret = wl1271_acx_beacon_filter_opt(wl, false); + if (ret < 0) + return ret; + ret = wl1271_cmd_ps_mode(wl, STATION_ACTIVE_MODE); if (ret < 0) return ret; diff --git a/drivers/net/wireless/wl12xx/wl1271_ps.h b/drivers/net/wireless/wl12xx/wl1271_ps.h index de2bd3c7dc9c..779653d0ae85 100644 --- a/drivers/net/wireless/wl12xx/wl1271_ps.h +++ b/drivers/net/wireless/wl12xx/wl1271_ps.h @@ -30,6 +30,6 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode); void wl1271_ps_elp_sleep(struct wl1271 *wl); int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake); - +void wl1271_elp_work(struct work_struct *work); #endif /* __WL1271_PS_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1271_reg.h b/drivers/net/wireless/wl12xx/wl1271_reg.h index f8ed4a4fc691..1f237389d1c7 100644 --- a/drivers/net/wireless/wl12xx/wl1271_reg.h +++ b/drivers/net/wireless/wl12xx/wl1271_reg.h @@ -34,7 +34,7 @@ #define REGISTERS_WORK_SIZE 0x0000b000 #define HW_ACCESS_ELP_CTRL_REG_ADDR 0x1FFFC -#define STATUS_MEM_ADDRESS 0x40400 +#define FW_STATUS_ADDR (0x14FC0 + 0xA000) /* ELP register commands */ #define ELPCTRL_WAKE_UP 0x1 @@ -213,7 +213,6 @@ ==============================================*/ #define ACX_REG_INTERRUPT_ACK (REGISTERS_BASE + 0x04F0) -#define RX_DRIVER_DUMMY_WRITE_ADDRESS (REGISTERS_BASE + 0x0534) #define RX_DRIVER_COUNTER_ADDRESS (REGISTERS_BASE + 0x0538) /* Device Configuration registers*/ @@ -614,50 +613,6 @@ enum { MAX_RADIO_BANDS = 0xFF }; -enum { - NO_RATE = 0, - RATE_1MBPS = 0x0A, - RATE_2MBPS = 0x14, - RATE_5_5MBPS = 0x37, - RATE_6MBPS = 0x0B, - RATE_9MBPS = 0x0F, - RATE_11MBPS = 0x6E, - RATE_12MBPS = 0x0A, - RATE_18MBPS = 0x0E, - RATE_22MBPS = 0xDC, - RATE_24MBPS = 0x09, - RATE_36MBPS = 0x0D, - RATE_48MBPS = 0x08, - RATE_54MBPS = 0x0C -}; - -enum { - RATE_INDEX_1MBPS = 0, - RATE_INDEX_2MBPS = 1, - RATE_INDEX_5_5MBPS = 2, - RATE_INDEX_6MBPS = 3, - RATE_INDEX_9MBPS = 4, - RATE_INDEX_11MBPS = 5, - RATE_INDEX_12MBPS = 6, - RATE_INDEX_18MBPS = 7, - RATE_INDEX_22MBPS = 8, - RATE_INDEX_24MBPS = 9, - RATE_INDEX_36MBPS = 10, - RATE_INDEX_48MBPS = 11, - RATE_INDEX_54MBPS = 12, - RATE_INDEX_MAX = RATE_INDEX_54MBPS, - MAX_RATE_INDEX, - INVALID_RATE_INDEX = MAX_RATE_INDEX, - RATE_INDEX_ENUM_MAX_SIZE = 0x7FFFFFFF -}; - -enum { - RATE_MASK_1MBPS = 0x1, - RATE_MASK_2MBPS = 0x2, - RATE_MASK_5_5MBPS = 0x4, - RATE_MASK_11MBPS = 0x20, -}; - #define SHORT_PREAMBLE_BIT BIT(0) /* CCK or Barker depending on the rate */ #define OFDM_RATE_BIT BIT(6) #define PBCC_RATE_BIT BIT(7) diff --git a/drivers/net/wireless/wl12xx/wl1271_rx.c b/drivers/net/wireless/wl12xx/wl1271_rx.c index ad8b6904c5eb..ca645f38109b 100644 --- a/drivers/net/wireless/wl12xx/wl1271_rx.c +++ b/drivers/net/wireless/wl12xx/wl1271_rx.c @@ -30,14 +30,15 @@ static u8 wl1271_rx_get_mem_block(struct wl1271_fw_status *status, u32 drv_rx_counter) { - return status->rx_pkt_descs[drv_rx_counter] & RX_MEM_BLOCK_MASK; + return le32_to_cpu(status->rx_pkt_descs[drv_rx_counter]) & + RX_MEM_BLOCK_MASK; } static u32 wl1271_rx_get_buf_size(struct wl1271_fw_status *status, u32 drv_rx_counter) { - return (status->rx_pkt_descs[drv_rx_counter] & RX_BUF_SIZE_MASK) >> - RX_BUF_SIZE_SHIFT_DIV; + return (le32_to_cpu(status->rx_pkt_descs[drv_rx_counter]) & + RX_BUF_SIZE_MASK) >> RX_BUF_SIZE_SHIFT_DIV; } /* The values of this table must match the wl1271_rates[] array */ @@ -70,6 +71,36 @@ static u8 wl1271_rx_rate_to_idx[] = { 0 /* WL1271_RATE_1 */ }; +/* The values of this table must match the wl1271_rates[] array */ +static u8 wl1271_5_ghz_rx_rate_to_idx[] = { + /* MCS rates are used only with 11n */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS7 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS6 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS5 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS4 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS3 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS2 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS1 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS0 */ + + 7, /* WL1271_RATE_54 */ + 6, /* WL1271_RATE_48 */ + 5, /* WL1271_RATE_36 */ + 4, /* WL1271_RATE_24 */ + + /* TI-specific rate */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_22 */ + + 3, /* WL1271_RATE_18 */ + 2, /* WL1271_RATE_12 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_11 */ + 1, /* WL1271_RATE_9 */ + 0, /* WL1271_RATE_6 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_5_5 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_2 */ + WL1271_RX_RATE_UNSUPPORTED /* WL1271_RATE_1 */ +}; + static void wl1271_rx_status(struct wl1271 *wl, struct wl1271_rx_descriptor *desc, struct ieee80211_rx_status *status, @@ -77,12 +108,21 @@ static void wl1271_rx_status(struct wl1271 *wl, { memset(status, 0, sizeof(struct ieee80211_rx_status)); - if ((desc->flags & WL1271_RX_DESC_BAND_MASK) == WL1271_RX_DESC_BAND_BG) + if ((desc->flags & WL1271_RX_DESC_BAND_MASK) == + WL1271_RX_DESC_BAND_BG) { status->band = IEEE80211_BAND_2GHZ; - else + status->rate_idx = wl1271_rx_rate_to_idx[desc->rate]; + } else if ((desc->flags & WL1271_RX_DESC_BAND_MASK) == + WL1271_RX_DESC_BAND_A) { + status->band = IEEE80211_BAND_5GHZ; + status->rate_idx = wl1271_5_ghz_rx_rate_to_idx[desc->rate]; + } else wl1271_warning("unsupported band 0x%x", desc->flags & WL1271_RX_DESC_BAND_MASK); + if (unlikely(status->rate_idx == WL1271_RX_RATE_UNSUPPORTED)) + wl1271_warning("unsupported rate"); + /* * FIXME: Add mactime handling. For IBSS (ad-hoc) we need to get the * timestamp from the beacon (acx_tsf_info). In BSS mode (infra) we @@ -91,12 +131,6 @@ static void wl1271_rx_status(struct wl1271 *wl, */ status->signal = desc->rssi; - /* FIXME: Should this be optimized? */ - status->qual = (desc->rssi - WL1271_RX_MIN_RSSI) * 100 / - (WL1271_RX_MAX_RSSI - WL1271_RX_MIN_RSSI); - status->qual = min(status->qual, 100); - status->qual = max(status->qual, 0); - /* * FIXME: In wl1251, the SNR should be divided by two. In wl1271 we * need to divide by two for now, but TI has been discussing about @@ -109,17 +143,11 @@ static void wl1271_rx_status(struct wl1271 *wl, if (desc->flags & WL1271_RX_DESC_ENCRYPT_MASK) { status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED; - if (likely(!(desc->flags & WL1271_RX_DESC_DECRYPT_FAIL))) + if (likely(!(desc->status & WL1271_RX_DESC_DECRYPT_FAIL))) status->flag |= RX_FLAG_DECRYPTED; - - if (unlikely(desc->flags & WL1271_RX_DESC_MIC_FAIL)) + if (unlikely(desc->status & WL1271_RX_DESC_MIC_FAIL)) status->flag |= RX_FLAG_MMIC_ERROR; } - - status->rate_idx = wl1271_rx_rate_to_idx[desc->rate]; - - if (status->rate_idx == WL1271_RX_RATE_UNSUPPORTED) - wl1271_warning("unsupported rate"); } static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length) @@ -131,14 +159,14 @@ static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length) u8 *buf; u8 beacon = 0; - skb = dev_alloc_skb(length); + skb = __dev_alloc_skb(length, GFP_KERNEL); if (!skb) { wl1271_error("Couldn't allocate RX frame"); return; } buf = skb_put(skb, length); - wl1271_spi_reg_read(wl, WL1271_SLV_MEM_DATA, buf, length, true); + wl1271_spi_read(wl, WL1271_SLV_MEM_DATA, buf, length, true); /* the data read starts with the descriptor */ desc = (struct wl1271_rx_descriptor *) buf; @@ -156,7 +184,7 @@ static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length) beacon ? "beacon" : ""); memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); - ieee80211_rx(wl->hw, skb); + ieee80211_rx_ni(wl->hw, skb); } void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status) @@ -176,15 +204,15 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status) break; } - wl->rx_mem_pool_addr.addr = - (mem_block << 8) + wl_mem_map->packet_memory_pool_start; + 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_spi_reg_write(wl, WL1271_SLV_REG_DATA, - &wl->rx_mem_pool_addr, - sizeof(wl->rx_mem_pool_addr), false); + wl1271_spi_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); @@ -192,9 +220,5 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status) drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK; } - wl1271_reg_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter); - - /* This is a workaround for some problems in the chip */ - wl1271_reg_write32(wl, RX_DRIVER_DUMMY_WRITE_ADDRESS, 0x1); - + wl1271_spi_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter); } diff --git a/drivers/net/wireless/wl12xx/wl1271_rx.h b/drivers/net/wireless/wl12xx/wl1271_rx.h index d1ca60e43a25..1ae6d1783ed4 100644 --- a/drivers/net/wireless/wl12xx/wl1271_rx.h +++ b/drivers/net/wireless/wl12xx/wl1271_rx.h @@ -102,14 +102,14 @@ #define RX_BUF_SIZE_SHIFT_DIV 6 struct wl1271_rx_descriptor { - u16 length; + __le16 length; u8 status; u8 flags; u8 rate; u8 channel; s8 rssi; u8 snr; - u32 timestamp; + __le32 timestamp; u8 packet_class; u8 process_id; u8 pad_len; diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.c b/drivers/net/wireless/wl12xx/wl1271_spi.c index 4a12880c16a8..02978a16e732 100644 --- a/drivers/net/wireless/wl12xx/wl1271_spi.c +++ b/drivers/net/wireless/wl12xx/wl1271_spi.c @@ -30,17 +30,29 @@ #include "wl12xx_80211.h" #include "wl1271_spi.h" -static int wl1271_translate_reg_addr(struct wl1271 *wl, int addr) +static int wl1271_translate_addr(struct wl1271 *wl, int addr) { - return addr - wl->physical_reg_addr + wl->virtual_reg_addr; -} - -static int wl1271_translate_mem_addr(struct wl1271 *wl, int addr) -{ - return addr - wl->physical_mem_addr + wl->virtual_mem_addr; + /* + * To translate, first check to which window of addresses the + * particular address belongs. Then subtract the starting address + * of that window from the address. Then, add offset of the + * translated region. + * + * The translated regions occur next to each other in physical device + * memory, so just add the sizes of the preceeding address regions to + * get the offset to the new region. + * + * Currently, only the two first regions are addressed, and the + * assumption is that all addresses will fall into either of those + * two. + */ + if ((addr >= wl->part.reg.start) && + (addr < wl->part.reg.start + wl->part.reg.size)) + return addr - wl->part.reg.start + wl->part.mem.size; + else + return addr - wl->part.mem.start; } - void wl1271_spi_reset(struct wl1271 *wl) { u8 *cmd; @@ -123,133 +135,137 @@ void wl1271_spi_init(struct wl1271 *wl) /* Set the SPI partitions to access the chip addresses * - * There are two VIRTUAL (SPI) partitions (the memory partition and the - * registers partition), which are mapped to two different areas of the - * PHYSICAL (hardware) memory. This function also makes other checks to - * ensure that the partitions are not overlapping. In the diagram below, the - * memory partition comes before the register partition, but the opposite is - * also supported. + * To simplify driver code, a fixed (virtual) memory map is defined for + * register and memory addresses. Because in the chipset, in different stages + * of operation, those addresses will move around, an address translation + * mechanism is required. * - * PHYSICAL address + * There are four partitions (three memory and one register partition), + * which are mapped to two different areas of the hardware memory. + * + * Virtual address * space * * | | - * ...+----+--> mem_start - * VIRTUAL address ... | | + * ...+----+--> mem.start + * Physical address ... | | * space ... | | [PART_0] * ... | | - * 0x00000000 <--+----+... ...+----+--> mem_start + mem_size + * 00000000 <--+----+... ...+----+--> mem.start + mem.size * | | ... | | * |MEM | ... | | * | | ... | | - * part_size <--+----+... | | {unused area) + * mem.size <--+----+... | | {unused area) * | | ... | | * |REG | ... | | - * part_size | | ... | | - * + <--+----+... ...+----+--> reg_start - * reg_size ... | | - * ... | | [PART_1] - * ... | | - * ...+----+--> reg_start + reg_size + * mem.size | | ... | | + * + <--+----+... ...+----+--> reg.start + * reg.size | | ... | | + * |MEM2| ... | | [PART_1] + * | | ... | | + * ...+----+--> reg.start + reg.size * | | * */ int wl1271_set_partition(struct wl1271 *wl, - u32 mem_start, u32 mem_size, - u32 reg_start, u32 reg_size) + struct wl1271_partition_set *p) { - struct wl1271_partition *partition; - struct spi_transfer t; - struct spi_message m; - size_t len, cmd_len; - u32 *cmd; - int addr; - - cmd_len = sizeof(u32) + 2 * sizeof(struct wl1271_partition); - cmd = kzalloc(cmd_len, GFP_KERNEL); - if (!cmd) - return -ENOMEM; - - spi_message_init(&m); - memset(&t, 0, sizeof(t)); - - partition = (struct wl1271_partition *) (cmd + 1); - addr = HW_ACCESS_PART0_SIZE_ADDR; - len = 2 * sizeof(struct wl1271_partition); - - *cmd |= WSPI_CMD_WRITE; - *cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH; - *cmd |= addr & WSPI_CMD_BYTE_ADDR; + /* copy partition info */ + memcpy(&wl->part, p, sizeof(*p)); wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", - mem_start, mem_size); + p->mem.start, p->mem.size); wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", - reg_start, reg_size); - - /* Make sure that the two partitions together don't exceed the - * address range */ - if ((mem_size + reg_size) > HW_ACCESS_MEMORY_MAX_RANGE) { - wl1271_debug(DEBUG_SPI, "Total size exceeds maximum virtual" - " address range. Truncating partition[0]."); - mem_size = HW_ACCESS_MEMORY_MAX_RANGE - reg_size; - wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", - mem_start, mem_size); - wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", - reg_start, reg_size); - } + p->reg.start, p->reg.size); + wl1271_debug(DEBUG_SPI, "mem2_start %08X mem2_size %08X", + p->mem2.start, p->mem2.size); + wl1271_debug(DEBUG_SPI, "mem3_start %08X mem3_size %08X", + p->mem3.start, p->mem3.size); + + /* write partition info to the chipset */ + wl1271_raw_write32(wl, HW_PART0_START_ADDR, p->mem.start); + wl1271_raw_write32(wl, HW_PART0_SIZE_ADDR, p->mem.size); + wl1271_raw_write32(wl, HW_PART1_START_ADDR, p->reg.start); + wl1271_raw_write32(wl, HW_PART1_SIZE_ADDR, p->reg.size); + wl1271_raw_write32(wl, HW_PART2_START_ADDR, p->mem2.start); + wl1271_raw_write32(wl, HW_PART2_SIZE_ADDR, p->mem2.size); + wl1271_raw_write32(wl, HW_PART3_START_ADDR, p->mem3.start); - if ((mem_start < reg_start) && - ((mem_start + mem_size) > reg_start)) { - /* Guarantee that the memory partition doesn't overlap the - * registers partition */ - wl1271_debug(DEBUG_SPI, "End of partition[0] is " - "overlapping partition[1]. Adjusted."); - mem_size = reg_start - mem_start; - wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", - mem_start, mem_size); - wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", - reg_start, reg_size); - } else if ((reg_start < mem_start) && - ((reg_start + reg_size) > mem_start)) { - /* Guarantee that the register partition doesn't overlap the - * memory partition */ - wl1271_debug(DEBUG_SPI, "End of partition[1] is" - " overlapping partition[0]. Adjusted."); - reg_size = mem_start - reg_start; - wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", - mem_start, mem_size); - wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", - reg_start, reg_size); - } + return 0; +} - partition[0].start = mem_start; - partition[0].size = mem_size; - partition[1].start = reg_start; - partition[1].size = reg_size; +#define WL1271_BUSY_WORD_TIMEOUT 1000 - wl->physical_mem_addr = mem_start; - wl->physical_reg_addr = reg_start; +/* FIXME: Check busy words, removed due to SPI bug */ +#if 0 +static void wl1271_spi_read_busy(struct wl1271 *wl, void *buf, size_t len) +{ + struct spi_transfer t[1]; + struct spi_message m; + u32 *busy_buf; + int num_busy_bytes = 0; - wl->virtual_mem_addr = 0; - wl->virtual_reg_addr = mem_size; + wl1271_info("spi read BUSY!"); - t.tx_buf = cmd; - t.len = cmd_len; - spi_message_add_tail(&t, &m); + /* + * Look for the non-busy word in the read buffer, and if found, + * read in the remaining data into the buffer. + */ + busy_buf = (u32 *)buf; + for (; (u32)busy_buf < (u32)buf + len; busy_buf++) { + num_busy_bytes += sizeof(u32); + if (*busy_buf & 0x1) { + spi_message_init(&m); + memset(t, 0, sizeof(t)); + memmove(buf, busy_buf, len - num_busy_bytes); + t[0].rx_buf = buf + (len - num_busy_bytes); + t[0].len = num_busy_bytes; + spi_message_add_tail(&t[0], &m); + spi_sync(wl->spi, &m); + return; + } + } - spi_sync(wl->spi, &m); + /* + * Read further busy words from SPI until a non-busy word is + * encountered, then read the data itself into the buffer. + */ + wl1271_info("spi read BUSY-polling needed!"); - kfree(cmd); + num_busy_bytes = WL1271_BUSY_WORD_TIMEOUT; + busy_buf = wl->buffer_busyword; + while (num_busy_bytes) { + num_busy_bytes--; + spi_message_init(&m); + memset(t, 0, sizeof(t)); + t[0].rx_buf = busy_buf; + t[0].len = sizeof(u32); + spi_message_add_tail(&t[0], &m); + spi_sync(wl->spi, &m); + + if (*busy_buf & 0x1) { + spi_message_init(&m); + memset(t, 0, sizeof(t)); + t[0].rx_buf = buf; + t[0].len = len; + spi_message_add_tail(&t[0], &m); + spi_sync(wl->spi, &m); + return; + } + } - return 0; + /* The SPI bus is unresponsive, the read failed. */ + memset(buf, 0, len); + wl1271_error("SPI read busy-word timeout!\n"); } +#endif -void wl1271_spi_read(struct wl1271 *wl, int addr, void *buf, - size_t len, bool fixed) +void wl1271_spi_raw_read(struct wl1271 *wl, int addr, void *buf, + size_t len, bool fixed) { struct spi_transfer t[3]; struct spi_message m; - u8 *busy_buf; + u32 *busy_buf; u32 *cmd; cmd = &wl->buffer_cmd; @@ -281,14 +297,16 @@ void wl1271_spi_read(struct wl1271 *wl, int addr, void *buf, spi_sync(wl->spi, &m); - /* FIXME: check busy words */ + /* FIXME: Check busy words, removed due to SPI bug */ + /* if (!(busy_buf[WL1271_BUSY_WORD_CNT - 1] & 0x1)) + wl1271_spi_read_busy(wl, buf, len); */ wl1271_dump(DEBUG_SPI, "spi_read cmd -> ", cmd, sizeof(*cmd)); wl1271_dump(DEBUG_SPI, "spi_read buf <- ", buf, len); } -void wl1271_spi_write(struct wl1271 *wl, int addr, void *buf, - size_t len, bool fixed) +void wl1271_spi_raw_write(struct wl1271 *wl, int addr, void *buf, + size_t len, bool fixed) { struct spi_transfer t[2]; struct spi_message m; @@ -321,62 +339,77 @@ void wl1271_spi_write(struct wl1271 *wl, int addr, void *buf, wl1271_dump(DEBUG_SPI, "spi_write buf -> ", buf, len); } -void wl1271_spi_mem_read(struct wl1271 *wl, int addr, void *buf, - size_t len) +void wl1271_spi_read(struct wl1271 *wl, int addr, void *buf, size_t len, + bool fixed) { int physical; - physical = wl1271_translate_mem_addr(wl, addr); + physical = wl1271_translate_addr(wl, addr); - wl1271_spi_read(wl, physical, buf, len, false); + wl1271_spi_raw_read(wl, physical, buf, len, fixed); } -void wl1271_spi_mem_write(struct wl1271 *wl, int addr, void *buf, - size_t len) +void wl1271_spi_write(struct wl1271 *wl, int addr, void *buf, size_t len, + bool fixed) { int physical; - physical = wl1271_translate_mem_addr(wl, addr); + physical = wl1271_translate_addr(wl, addr); - wl1271_spi_write(wl, physical, buf, len, false); + wl1271_spi_raw_write(wl, physical, buf, len, fixed); } -void wl1271_spi_reg_read(struct wl1271 *wl, int addr, void *buf, size_t len, - bool fixed) +u32 wl1271_spi_read32(struct wl1271 *wl, int addr) { - int physical; - - physical = wl1271_translate_reg_addr(wl, addr); + return wl1271_raw_read32(wl, wl1271_translate_addr(wl, addr)); +} - wl1271_spi_read(wl, physical, buf, len, fixed); +void wl1271_spi_write32(struct wl1271 *wl, int addr, u32 val) +{ + wl1271_raw_write32(wl, wl1271_translate_addr(wl, addr), val); } -void wl1271_spi_reg_write(struct wl1271 *wl, int addr, void *buf, size_t len, - bool fixed) +void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val) { - int physical; + /* write address >> 1 + 0x30000 to OCP_POR_CTR */ + addr = (addr >> 1) + 0x30000; + wl1271_spi_write32(wl, OCP_POR_CTR, addr); - physical = wl1271_translate_reg_addr(wl, addr); + /* write value to OCP_POR_WDATA */ + wl1271_spi_write32(wl, OCP_DATA_WRITE, val); - wl1271_spi_write(wl, physical, buf, len, fixed); + /* write 1 to OCP_CMD */ + wl1271_spi_write32(wl, OCP_CMD, OCP_CMD_WRITE); } -u32 wl1271_mem_read32(struct wl1271 *wl, int addr) +u16 wl1271_top_reg_read(struct wl1271 *wl, int addr) { - return wl1271_read32(wl, wl1271_translate_mem_addr(wl, addr)); -} + u32 val; + int timeout = OCP_CMD_LOOP; -void wl1271_mem_write32(struct wl1271 *wl, int addr, u32 val) -{ - wl1271_write32(wl, wl1271_translate_mem_addr(wl, addr), val); -} + /* write address >> 1 + 0x30000 to OCP_POR_CTR */ + addr = (addr >> 1) + 0x30000; + wl1271_spi_write32(wl, OCP_POR_CTR, addr); -u32 wl1271_reg_read32(struct wl1271 *wl, int addr) -{ - return wl1271_read32(wl, wl1271_translate_reg_addr(wl, addr)); -} + /* write 2 to OCP_CMD */ + wl1271_spi_write32(wl, OCP_CMD, OCP_CMD_READ); -void wl1271_reg_write32(struct wl1271 *wl, int addr, u32 val) -{ - wl1271_write32(wl, wl1271_translate_reg_addr(wl, addr), val); + /* poll for data ready */ + do { + val = wl1271_spi_read32(wl, OCP_DATA_READ); + timeout--; + } while (!(val & OCP_READY_MASK) && timeout); + + if (!timeout) { + wl1271_warning("Top register access timed out."); + return 0xffff; + } + + /* check data status and return if OK */ + if ((val & OCP_STATUS_MASK) == OCP_STATUS_OK) + return val & 0xffff; + else { + wl1271_warning("Top register access returned error."); + return 0xffff; + } } diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.h b/drivers/net/wireless/wl12xx/wl1271_spi.h index 2c9968458646..cb7df1c56314 100644 --- a/drivers/net/wireless/wl12xx/wl1271_spi.h +++ b/drivers/net/wireless/wl12xx/wl1271_spi.h @@ -29,10 +29,14 @@ #define HW_ACCESS_MEMORY_MAX_RANGE 0x1FFC0 -#define HW_ACCESS_PART0_SIZE_ADDR 0x1FFC0 -#define HW_ACCESS_PART0_START_ADDR 0x1FFC4 -#define HW_ACCESS_PART1_SIZE_ADDR 0x1FFC8 -#define HW_ACCESS_PART1_START_ADDR 0x1FFCC +#define HW_PARTITION_REGISTERS_ADDR 0x1ffc0 +#define HW_PART0_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR) +#define HW_PART0_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 4) +#define HW_PART1_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR + 8) +#define HW_PART1_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 12) +#define HW_PART2_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR + 16) +#define HW_PART2_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 20) +#define HW_PART3_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 24) #define HW_ACCESS_REGISTER_SIZE 4 @@ -67,47 +71,56 @@ ((WL1271_BUSY_WORD_LEN - 4) / sizeof(u32)) #define HW_ACCESS_WSPI_INIT_CMD_MASK 0 +#define OCP_CMD_LOOP 32 + +#define OCP_CMD_WRITE 0x1 +#define OCP_CMD_READ 0x2 + +#define OCP_READY_MASK BIT(18) +#define OCP_STATUS_MASK (BIT(16) | BIT(17)) + +#define OCP_STATUS_NO_RESP 0x00000 +#define OCP_STATUS_OK 0x10000 +#define OCP_STATUS_REQ_FAILED 0x20000 +#define OCP_STATUS_RESP_ERROR 0x30000 /* Raw target IO, address is not translated */ -void wl1271_spi_write(struct wl1271 *wl, int addr, void *buf, +void wl1271_spi_raw_write(struct wl1271 *wl, int addr, void *buf, size_t len, bool fixed); -void wl1271_spi_read(struct wl1271 *wl, int addr, void *buf, +void wl1271_spi_raw_read(struct wl1271 *wl, int addr, void *buf, size_t len, bool fixed); -/* Memory target IO, address is tranlated to partition 0 */ -void wl1271_spi_mem_read(struct wl1271 *wl, int addr, void *buf, size_t len); -void wl1271_spi_mem_write(struct wl1271 *wl, int addr, void *buf, size_t len); -u32 wl1271_mem_read32(struct wl1271 *wl, int addr); -void wl1271_mem_write32(struct wl1271 *wl, int addr, u32 val); +/* Translated target IO */ +void wl1271_spi_read(struct wl1271 *wl, int addr, void *buf, size_t len, + bool fixed); +void wl1271_spi_write(struct wl1271 *wl, int addr, void *buf, size_t len, + bool fixed); +u32 wl1271_spi_read32(struct wl1271 *wl, int addr); +void wl1271_spi_write32(struct wl1271 *wl, int addr, u32 val); -/* Registers IO */ -void wl1271_spi_reg_read(struct wl1271 *wl, int addr, void *buf, size_t len, - bool fixed); -void wl1271_spi_reg_write(struct wl1271 *wl, int addr, void *buf, size_t len, - bool fixed); -u32 wl1271_reg_read32(struct wl1271 *wl, int addr); -void wl1271_reg_write32(struct wl1271 *wl, int addr, u32 val); +/* Top Register IO */ +void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val); +u16 wl1271_top_reg_read(struct wl1271 *wl, int addr); /* INIT and RESET words */ void wl1271_spi_reset(struct wl1271 *wl); void wl1271_spi_init(struct wl1271 *wl); int wl1271_set_partition(struct wl1271 *wl, - u32 part_start, u32 part_size, - u32 reg_start, u32 reg_size); + struct wl1271_partition_set *p); -static inline u32 wl1271_read32(struct wl1271 *wl, int addr) +static inline u32 wl1271_raw_read32(struct wl1271 *wl, int addr) { - wl1271_spi_read(wl, addr, &wl->buffer_32, - sizeof(wl->buffer_32), false); + wl1271_spi_raw_read(wl, addr, &wl->buffer_32, + sizeof(wl->buffer_32), false); return wl->buffer_32; } -static inline void wl1271_write32(struct wl1271 *wl, int addr, u32 val) +static inline void wl1271_raw_write32(struct wl1271 *wl, int addr, u32 val) { wl->buffer_32 = val; - wl1271_spi_write(wl, addr, &wl->buffer_32, - sizeof(wl->buffer_32), false); + wl1271_spi_raw_write(wl, addr, &wl->buffer_32, + sizeof(wl->buffer_32), false); } #endif /* __WL1271_SPI_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.c b/drivers/net/wireless/wl12xx/wl1271_tx.c index ff221258b941..00af065c77c2 100644 --- a/drivers/net/wireless/wl12xx/wl1271_tx.c +++ b/drivers/net/wireless/wl12xx/wl1271_tx.c @@ -33,8 +33,7 @@ static int wl1271_tx_id(struct wl1271 *wl, struct sk_buff *skb) { int i; - - for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) + for (i = 0; i < ACX_TX_DESCRIPTORS; i++) if (wl->tx_frames[i] == NULL) { wl->tx_frames[i] = skb; return i; @@ -58,8 +57,8 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra) /* approximate the number of blocks required for this packet in the firmware */ /* FIXME: try to figure out what is done here and make it cleaner */ - total_blocks = (skb->len) >> TX_HW_BLOCK_SHIFT_DIV; - excluded = (total_blocks << 2) + (skb->len & 0xff) + 34; + total_blocks = (total_len + 20) >> TX_HW_BLOCK_SHIFT_DIV; + excluded = (total_blocks << 2) + ((total_len + 20) & 0xff) + 34; total_blocks += (excluded > 252) ? 2 : 1; total_blocks += TX_HW_BLOCK_SPARE; @@ -89,15 +88,25 @@ static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, { struct wl1271_tx_hw_descr *desc; int pad; + u16 tx_attr; desc = (struct wl1271_tx_hw_descr *) skb->data; + /* relocate space for security header */ + if (extra) { + void *framestart = skb->data + sizeof(*desc); + u16 fc = *(u16 *)(framestart + extra); + int hdrlen = ieee80211_hdrlen(cpu_to_le16(fc)); + memmove(framestart, framestart + extra, hdrlen); + } + /* configure packet life time */ - desc->start_time = jiffies_to_usecs(jiffies) - wl->time_offset; - desc->life_time = TX_HW_MGMT_PKT_LIFETIME_TU; + desc->start_time = cpu_to_le32(jiffies_to_usecs(jiffies) - + wl->time_offset); + desc->life_time = cpu_to_le16(TX_HW_MGMT_PKT_LIFETIME_TU); /* configure the tx attributes */ - desc->tx_attr = wl->session_counter << TX_HW_ATTR_OFST_SESSION_COUNTER; + tx_attr = wl->session_counter << TX_HW_ATTR_OFST_SESSION_COUNTER; /* FIXME: do we know the packet priority? can we identify mgmt packets, and use max prio for them at least? */ desc->tid = 0; @@ -106,11 +115,13 @@ static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, /* align the length (and store in terms of words) */ pad = WL1271_TX_ALIGN(skb->len); - desc->length = pad >> 2; + desc->length = cpu_to_le16(pad >> 2); /* calculate number of padding bytes */ pad = pad - skb->len; - desc->tx_attr |= pad << TX_HW_ATTR_OFST_LAST_WORD_PAD; + tx_attr |= pad << TX_HW_ATTR_OFST_LAST_WORD_PAD; + + desc->tx_attr = cpu_to_le16(tx_attr); wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d", pad); return 0; @@ -147,11 +158,11 @@ static int wl1271_tx_send_packet(struct wl1271 *wl, struct sk_buff *skb, len = WL1271_TX_ALIGN(skb->len); /* perform a fixed address block write with the packet */ - wl1271_spi_reg_write(wl, WL1271_SLV_MEM_DATA, skb->data, len, true); + wl1271_spi_write(wl, WL1271_SLV_MEM_DATA, skb->data, len, true); /* write packet new counter into the write access register */ wl->tx_packets_count++; - wl1271_reg_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count); + wl1271_spi_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count); desc = (struct wl1271_tx_hw_descr *) skb->data; wl1271_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u (%u words)", @@ -254,14 +265,13 @@ out: static void wl1271_tx_complete_packet(struct wl1271 *wl, struct wl1271_tx_hw_res_descr *result) { - struct ieee80211_tx_info *info; struct sk_buff *skb; - u32 header_len; + u16 seq; int id = result->id; /* check for id legality */ - if (id >= TX_HW_RESULT_QUEUE_LEN || wl->tx_frames[id] == NULL) { + if (id >= ACX_TX_DESCRIPTORS || wl->tx_frames[id] == NULL) { wl1271_warning("TX result illegal id: %d", id); return; } @@ -284,22 +294,32 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, /* info->status.retry_count = result->ack_failures; */ wl->stats.retry_count += result->ack_failures; - /* get header len */ + /* update security sequence number */ + seq = wl->tx_security_seq_16 + + (result->lsb_security_sequence_number - + wl->tx_security_last_seq); + wl->tx_security_last_seq = result->lsb_security_sequence_number; + + if (seq < wl->tx_security_seq_16) + wl->tx_security_seq_32++; + wl->tx_security_seq_16 = seq; + + /* remove private header from packet */ + skb_pull(skb, sizeof(struct wl1271_tx_hw_descr)); + + /* remove TKIP header space if present */ if (info->control.hw_key && - info->control.hw_key->alg == ALG_TKIP) - header_len = WL1271_TKIP_IV_SPACE + - sizeof(struct wl1271_tx_hw_descr); - else - header_len = sizeof(struct wl1271_tx_hw_descr); + info->control.hw_key->alg == ALG_TKIP) { + int hdrlen = ieee80211_get_hdrlen_from_skb(skb); + memmove(skb->data + WL1271_TKIP_IV_SPACE, skb->data, hdrlen); + skb_pull(skb, WL1271_TKIP_IV_SPACE); + } wl1271_debug(DEBUG_TX, "tx status id %u skb 0x%p failures %u rate 0x%x" " status 0x%x", result->id, skb, result->ack_failures, result->rate_class_index, result->status); - /* remove private header from packet */ - skb_pull(skb, header_len); - /* return the packet to the stack */ ieee80211_tx_status(wl->hw, skb); wl->tx_frames[result->id] = NULL; @@ -315,8 +335,8 @@ void wl1271_tx_complete(struct wl1271 *wl, u32 count) wl1271_debug(DEBUG_TX, "tx_complete received, packets: %d", count); /* read the tx results from the chipset */ - wl1271_spi_mem_read(wl, memmap->tx_result, - wl->tx_res_if, sizeof(*wl->tx_res_if)); + wl1271_spi_read(wl, le32_to_cpu(memmap->tx_result), + wl->tx_res_if, sizeof(*wl->tx_res_if), false); /* verify that the result buffer is not getting overrun */ if (count > TX_HW_RESULT_QUEUE_LEN) { @@ -337,10 +357,10 @@ void wl1271_tx_complete(struct wl1271 *wl, u32 count) } /* write host counter to chipset (to ack) */ - wl1271_mem_write32(wl, memmap->tx_result + + wl1271_spi_write32(wl, le32_to_cpu(memmap->tx_result) + offsetof(struct wl1271_tx_hw_res_if, tx_result_host_counter), - wl->tx_res_if->tx_result_fw_counter); + le32_to_cpu(wl->tx_res_if->tx_result_fw_counter)); } /* caller must hold wl->mutex */ @@ -364,7 +384,7 @@ void wl1271_tx_flush(struct wl1271 *wl) ieee80211_tx_status(wl->hw, skb); } - for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) + for (i = 0; i < ACX_TX_DESCRIPTORS; i++) if (wl->tx_frames[i] != NULL) { skb = wl->tx_frames[i]; info = IEEE80211_SKB_CB(skb); diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.h b/drivers/net/wireless/wl12xx/wl1271_tx.h index 4a614067ddba..416396caf0a0 100644 --- a/drivers/net/wireless/wl12xx/wl1271_tx.h +++ b/drivers/net/wireless/wl12xx/wl1271_tx.h @@ -58,7 +58,7 @@ struct wl1271_tx_hw_descr { /* Length of packet in words, including descriptor+header+data */ - u16 length; + __le16 length; /* Number of extra memory blocks to allocate for this packet in addition to the number of blocks derived from the packet length */ u8 extra_mem_blocks; @@ -67,12 +67,12 @@ struct wl1271_tx_hw_descr { HW!! */ u8 total_mem_blocks; /* Device time (in us) when the packet arrived to the driver */ - u32 start_time; + __le32 start_time; /* Max delay in TUs until transmission. The last device time the packet can be transmitted is: startTime+(1024*LifeTime) */ - u16 life_time; + __le16 life_time; /* Bitwise fields - see TX_ATTR... definitions above. */ - u16 tx_attr; + __le16 tx_attr; /* Packet identifier used also in the Tx-Result. */ u8 id; /* The packet TID value (as User-Priority) */ @@ -100,12 +100,12 @@ struct wl1271_tx_hw_res_descr { several possible reasons for failure. */ u8 status; /* Total air access duration including all retrys and overheads.*/ - u16 medium_usage; + __le16 medium_usage; /* The time passed from host xfer to Tx-complete.*/ - u32 fw_handling_time; + __le32 fw_handling_time; /* Total media delay (from 1st EDCA AIFS counter until TX Complete). */ - u32 medium_delay; + __le32 medium_delay; /* LS-byte of last TKIP seq-num (saved per AC for recovery). */ u8 lsb_security_sequence_number; /* Retry count - number of transmissions without successful ACK.*/ @@ -118,8 +118,8 @@ struct wl1271_tx_hw_res_descr { } __attribute__ ((packed)); struct wl1271_tx_hw_res_if { - u32 tx_result_fw_counter; - u32 tx_result_host_counter; + __le32 tx_result_fw_counter; + __le32 tx_result_host_counter; struct wl1271_tx_hw_res_descr tx_results_queue[TX_HW_RESULT_QUEUE_LEN]; } __attribute__ ((packed)); diff --git a/drivers/net/wireless/wl12xx/wl12xx_80211.h b/drivers/net/wireless/wl12xx/wl12xx_80211.h index 657c2dbcb7d3..055d7bc6f592 100644 --- a/drivers/net/wireless/wl12xx/wl12xx_80211.h +++ b/drivers/net/wireless/wl12xx/wl12xx_80211.h @@ -122,8 +122,8 @@ struct wl12xx_null_data_template { } __attribute__ ((packed)); struct wl12xx_ps_poll_template { - u16 fc; - u16 aid; + __le16 fc; + __le16 aid; u8 bssid[ETH_ALEN]; u8 ta[ETH_ALEN]; } __attribute__ ((packed)); |