diff options
Diffstat (limited to 'drivers/net/wireless/wl12xx')
-rw-r--r-- | drivers/net/wireless/wl12xx/acx.c | 190 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/acx.h | 103 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/boot.c | 6 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/cmd.c | 18 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/conf.h | 111 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/debugfs.c | 240 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/event.c | 70 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/event.h | 12 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/init.c | 110 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/init.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/main.c | 492 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/ps.c | 30 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/ps.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/rx.c | 36 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/scan.c | 243 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/scan.h | 114 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/sdio.c | 64 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/tx.c | 13 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/tx.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/wl12xx.h | 14 |
20 files changed, 1578 insertions, 294 deletions
diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index a5c9c0a..c6ee530 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -325,12 +325,19 @@ out: return ret; } -int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold) +int wl1271_acx_rts_threshold(struct wl1271 *wl, u32 rts_threshold) { struct acx_rts_threshold *rts; int ret; - wl1271_debug(DEBUG_ACX, "acx rts threshold"); + /* + * If the RTS threshold is not configured or out of range, use the + * default value. + */ + if (rts_threshold > IEEE80211_MAX_RTS_THRESHOLD) + rts_threshold = wl->conf.rx.rts_threshold; + + wl1271_debug(DEBUG_ACX, "acx rts threshold: %d", rts_threshold); rts = kzalloc(sizeof(*rts), GFP_KERNEL); if (!rts) { @@ -338,7 +345,7 @@ int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold) goto out; } - rts->threshold = cpu_to_le16(rts_threshold); + rts->threshold = cpu_to_le16((u16)rts_threshold); ret = wl1271_cmd_configure(wl, DOT11_RTS_THRESHOLD, rts, sizeof(*rts)); if (ret < 0) { @@ -540,13 +547,13 @@ out: return ret; } -int wl1271_acx_sg_cfg(struct wl1271 *wl) +int wl1271_acx_sta_sg_cfg(struct wl1271 *wl) { - struct acx_bt_wlan_coex_param *param; + struct acx_sta_bt_wlan_coex_param *param; struct conf_sg_settings *c = &wl->conf.sg; int i, ret; - wl1271_debug(DEBUG_ACX, "acx sg cfg"); + wl1271_debug(DEBUG_ACX, "acx sg sta cfg"); param = kzalloc(sizeof(*param), GFP_KERNEL); if (!param) { @@ -555,8 +562,38 @@ int wl1271_acx_sg_cfg(struct wl1271 *wl) } /* BT-WLAN coext parameters */ - for (i = 0; i < CONF_SG_PARAMS_MAX; i++) - param->params[i] = cpu_to_le32(c->params[i]); + for (i = 0; i < CONF_SG_STA_PARAMS_MAX; i++) + param->params[i] = cpu_to_le32(c->sta_params[i]); + param->param_idx = CONF_SG_PARAMS_ALL; + + ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param)); + if (ret < 0) { + wl1271_warning("failed to set sg config: %d", ret); + goto out; + } + +out: + kfree(param); + return ret; +} + +int wl1271_acx_ap_sg_cfg(struct wl1271 *wl) +{ + struct acx_ap_bt_wlan_coex_param *param; + struct conf_sg_settings *c = &wl->conf.sg; + int i, ret; + + wl1271_debug(DEBUG_ACX, "acx sg ap cfg"); + + param = kzalloc(sizeof(*param), GFP_KERNEL); + if (!param) { + ret = -ENOMEM; + goto out; + } + + /* BT-WLAN coext parameters */ + for (i = 0; i < CONF_SG_AP_PARAMS_MAX; i++) + param->params[i] = cpu_to_le32(c->ap_params[i]); param->param_idx = CONF_SG_PARAMS_ALL; ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param)); @@ -804,7 +841,8 @@ int wl1271_acx_ap_rate_policy(struct wl1271 *wl, struct conf_tx_rate_class *c, struct acx_ap_rate_policy *acx; int ret = 0; - wl1271_debug(DEBUG_ACX, "acx ap rate policy"); + wl1271_debug(DEBUG_ACX, "acx ap rate policy %d rates 0x%x", + idx, c->enabled_rates); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { @@ -898,12 +936,19 @@ out: return ret; } -int wl1271_acx_frag_threshold(struct wl1271 *wl, u16 frag_threshold) +int wl1271_acx_frag_threshold(struct wl1271 *wl, u32 frag_threshold) { struct acx_frag_threshold *acx; int ret = 0; - wl1271_debug(DEBUG_ACX, "acx frag threshold"); + /* + * If the fragmentation is not configured or out of range, use the + * default value. + */ + if (frag_threshold > IEEE80211_MAX_FRAG_THRESHOLD) + frag_threshold = wl->conf.tx.frag_threshold; + + wl1271_debug(DEBUG_ACX, "acx frag threshold: %d", frag_threshold); acx = kzalloc(sizeof(*acx), GFP_KERNEL); @@ -912,7 +957,7 @@ int wl1271_acx_frag_threshold(struct wl1271 *wl, u16 frag_threshold) goto out; } - acx->frag_threshold = cpu_to_le16(frag_threshold); + acx->frag_threshold = cpu_to_le16((u16)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); @@ -954,6 +999,7 @@ out: int wl1271_acx_ap_mem_cfg(struct wl1271 *wl) { struct wl1271_acx_ap_config_memory *mem_conf; + struct conf_memory_settings *mem; int ret; wl1271_debug(DEBUG_ACX, "wl1271 mem cfg"); @@ -964,14 +1010,21 @@ int wl1271_acx_ap_mem_cfg(struct wl1271 *wl) goto out; } + if (wl->chip.id == CHIP_ID_1283_PG20) + /* + * FIXME: The 128x AP FW does not yet support dynamic memory. + * Use the base memory configuration for 128x for now. This + * should be fine tuned in the future. + */ + mem = &wl->conf.mem_wl128x; + else + mem = &wl->conf.mem_wl127x; + /* memory config */ - /* FIXME: for now we always use mem_wl127x for AP, because it - * doesn't support dynamic memory and we don't have the - * optimal values for wl128x without dynamic memory yet */ - mem_conf->num_stations = wl->conf.mem_wl127x.num_stations; - mem_conf->rx_mem_block_num = wl->conf.mem_wl127x.rx_block_num; - mem_conf->tx_min_mem_block_num = wl->conf.mem_wl127x.tx_min_block_num; - mem_conf->num_ssid_profiles = wl->conf.mem_wl127x.ssid_profiles; + mem_conf->num_stations = mem->num_stations; + mem_conf->rx_mem_block_num = mem->rx_block_num; + mem_conf->tx_min_mem_block_num = mem->tx_min_block_num; + mem_conf->num_ssid_profiles = mem->ssid_profiles; mem_conf->total_tx_descriptors = cpu_to_le32(ACX_TX_DESCRIPTORS); ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf, @@ -1524,46 +1577,22 @@ out: return ret; } -int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl) +int wl1271_acx_max_tx_retry(struct wl1271 *wl) { - struct wl1271_acx_ap_max_tx_retry *acx = NULL; + struct wl1271_acx_max_tx_retry *acx = NULL; int ret; - wl1271_debug(DEBUG_ACX, "acx ap max tx retry"); + wl1271_debug(DEBUG_ACX, "acx max tx retry"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) return -ENOMEM; - acx->max_tx_retry = cpu_to_le16(wl->conf.tx.max_tx_retries); + acx->max_tx_retry = cpu_to_le16(wl->conf.tx.ap_max_tx_retries); ret = wl1271_cmd_configure(wl, ACX_MAX_TX_FAILURE, acx, sizeof(*acx)); if (ret < 0) { - wl1271_warning("acx ap max tx retry failed: %d", ret); - goto out; - } - -out: - kfree(acx); - return ret; -} - -int wl1271_acx_sta_max_tx_retry(struct wl1271 *wl) -{ - struct wl1271_acx_sta_max_tx_retry *acx = NULL; - int ret; - - wl1271_debug(DEBUG_ACX, "acx sta max tx retry"); - - acx = kzalloc(sizeof(*acx), GFP_KERNEL); - if (!acx) - return -ENOMEM; - - acx->max_tx_retry = wl->conf.tx.max_tx_retries; - - ret = wl1271_cmd_configure(wl, ACX_CONS_TX_FAILURE, acx, sizeof(*acx)); - if (ret < 0) { - wl1271_warning("acx sta max tx retry failed: %d", ret); + wl1271_warning("acx max tx retry failed: %d", ret); goto out; } @@ -1626,3 +1655,68 @@ out: kfree(acx); return ret; } + +int wl1271_acx_set_ap_beacon_filter(struct wl1271 *wl, bool enable) +{ + struct acx_ap_beacon_filter *acx = NULL; + int ret; + + wl1271_debug(DEBUG_ACX, "acx set ap beacon filter: %d", enable); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) + return -ENOMEM; + + acx->enable = enable ? 1 : 0; + + ret = wl1271_cmd_configure(wl, ACX_AP_BEACON_FILTER_OPT, + acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx set ap beacon filter failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + +int wl1271_acx_fm_coex(struct wl1271 *wl) +{ + struct wl1271_acx_fm_coex *acx; + int ret; + + wl1271_debug(DEBUG_ACX, "acx fm coex setting"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->enable = wl->conf.fm_coex.enable; + acx->swallow_period = wl->conf.fm_coex.swallow_period; + acx->n_divider_fref_set_1 = wl->conf.fm_coex.n_divider_fref_set_1; + acx->n_divider_fref_set_2 = wl->conf.fm_coex.n_divider_fref_set_2; + acx->m_divider_fref_set_1 = + cpu_to_le16(wl->conf.fm_coex.m_divider_fref_set_1); + acx->m_divider_fref_set_2 = + cpu_to_le16(wl->conf.fm_coex.m_divider_fref_set_2); + acx->coex_pll_stabilization_time = + cpu_to_le32(wl->conf.fm_coex.coex_pll_stabilization_time); + acx->ldo_stabilization_time = + cpu_to_le16(wl->conf.fm_coex.ldo_stabilization_time); + acx->fm_disturbed_band_margin = + wl->conf.fm_coex.fm_disturbed_band_margin; + acx->swallow_clk_diff = wl->conf.fm_coex.swallow_clk_diff; + + ret = wl1271_cmd_configure(wl, ACX_FM_COEX_CFG, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx fm coex setting failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index 942908c..9a895e3 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -303,7 +303,6 @@ struct acx_beacon_filter_option { struct acx_header header; u8 enable; - /* * The number of beacons without the unicast TIM * bit set that the firmware buffers before @@ -370,14 +369,23 @@ struct acx_bt_wlan_coex { u8 pad[3]; } __packed; -struct acx_bt_wlan_coex_param { +struct acx_sta_bt_wlan_coex_param { struct acx_header header; - __le32 params[CONF_SG_PARAMS_MAX]; + __le32 params[CONF_SG_STA_PARAMS_MAX]; u8 param_idx; u8 padding[3]; } __packed; +struct acx_ap_bt_wlan_coex_param { + struct acx_header header; + + __le32 params[CONF_SG_AP_PARAMS_MAX]; + u8 param_idx; + u8 padding[3]; +} __packed; + + struct acx_dco_itrim_params { struct acx_header header; @@ -1145,7 +1153,7 @@ struct wl1271_acx_fw_tsf_information { u8 padding[3]; } __packed; -struct wl1271_acx_ap_max_tx_retry { +struct wl1271_acx_max_tx_retry { struct acx_header header; /* @@ -1156,13 +1164,6 @@ struct wl1271_acx_ap_max_tx_retry { u8 padding_1[2]; } __packed; -struct wl1271_acx_sta_max_tx_retry { - struct acx_header header; - - u8 max_tx_retry; - u8 padding_1[3]; -} __packed; - struct wl1271_acx_config_ps { struct acx_header header; @@ -1179,6 +1180,72 @@ struct wl1271_acx_inconnection_sta { u8 padding1[2]; } __packed; +struct acx_ap_beacon_filter { + struct acx_header header; + + u8 enable; + u8 pad[3]; +} __packed; + +/* + * ACX_FM_COEX_CFG + * set the FM co-existence parameters. + */ +struct wl1271_acx_fm_coex { + struct acx_header header; + /* enable(1) / disable(0) the FM Coex feature */ + u8 enable; + /* + * Swallow period used in COEX PLL swallowing mechanism. + * 0xFF = use FW default + */ + u8 swallow_period; + /* + * The N divider used in COEX PLL swallowing mechanism for Fref of + * 38.4/19.2 Mhz. 0xFF = use FW default + */ + u8 n_divider_fref_set_1; + /* + * The N divider used in COEX PLL swallowing mechanism for Fref of + * 26/52 Mhz. 0xFF = use FW default + */ + u8 n_divider_fref_set_2; + /* + * The M divider used in COEX PLL swallowing mechanism for Fref of + * 38.4/19.2 Mhz. 0xFFFF = use FW default + */ + __le16 m_divider_fref_set_1; + /* + * The M divider used in COEX PLL swallowing mechanism for Fref of + * 26/52 Mhz. 0xFFFF = use FW default + */ + __le16 m_divider_fref_set_2; + /* + * The time duration in uSec required for COEX PLL to stabilize. + * 0xFFFFFFFF = use FW default + */ + __le32 coex_pll_stabilization_time; + /* + * The time duration in uSec required for LDO to stabilize. + * 0xFFFFFFFF = use FW default + */ + __le16 ldo_stabilization_time; + /* + * The disturbed frequency band margin around the disturbed frequency + * center (single sided). + * For example, if 2 is configured, the following channels will be + * considered disturbed channel: + * 80 +- 0.1 MHz, 91 +- 0.1 MHz, 98 +- 0.1 MHz, 102 +- 0.1 MH + * 0xFF = use FW default + */ + u8 fm_disturbed_band_margin; + /* + * The swallow clock difference of the swallowing mechanism. + * 0xFF = use FW default + */ + u8 swallow_clk_diff; +} __packed; + enum { ACX_WAKE_UP_CONDITIONS = 0x0002, ACX_MEM_CFG = 0x0003, @@ -1197,6 +1264,7 @@ enum { ACX_TID_CFG = 0x001A, ACX_PS_RX_STREAMING = 0x001B, ACX_BEACON_FILTER_OPT = 0x001F, + ACX_AP_BEACON_FILTER_OPT = 0x0020, ACX_NOISE_HIST = 0x0021, ACX_HDK_VERSION = 0x0022, /* ??? */ ACX_PD_THRESHOLD = 0x0023, @@ -1208,6 +1276,7 @@ enum { ACX_BCN_DTIM_OPTIONS = 0x0031, ACX_SG_ENABLE = 0x0032, ACX_SG_CFG = 0x0033, + ACX_FM_COEX_CFG = 0x0034, ACX_BEACON_FILTER_TABLE = 0x0038, ACX_ARP_IP_FILTER = 0x0039, ACX_ROAMING_STATISTICS_TBL = 0x003B, @@ -1264,13 +1333,14 @@ int wl1271_acx_slot(struct wl1271 *wl, enum acx_slot_type slot_time); 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_rts_threshold(struct wl1271 *wl, u32 rts_threshold); int wl1271_acx_dco_itrim_params(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, bool enable); int wl1271_acx_sg_enable(struct wl1271 *wl, bool enable); -int wl1271_acx_sg_cfg(struct wl1271 *wl); +int wl1271_acx_sta_sg_cfg(struct wl1271 *wl); +int wl1271_acx_ap_sg_cfg(struct wl1271 *wl); int wl1271_acx_cca_threshold(struct wl1271 *wl); int wl1271_acx_bcn_dtim_options(struct wl1271 *wl); int wl1271_acx_aid(struct wl1271 *wl, u16 aid); @@ -1287,7 +1357,7 @@ int wl1271_acx_ac_cfg(struct wl1271 *wl, u8 ac, u8 cw_min, u16 cw_max, int wl1271_acx_tid_cfg(struct wl1271 *wl, u8 queue_id, u8 channel_type, u8 tsid, u8 ps_scheme, u8 ack_policy, u32 apsd_conf0, u32 apsd_conf1); -int wl1271_acx_frag_threshold(struct wl1271 *wl, u16 frag_threshold); +int wl1271_acx_frag_threshold(struct wl1271 *wl, u32 frag_threshold); int wl1271_acx_tx_config_options(struct wl1271 *wl); int wl1271_acx_ap_mem_cfg(struct wl1271 *wl); int wl1271_acx_sta_mem_cfg(struct wl1271 *wl); @@ -1314,9 +1384,10 @@ int wl1271_acx_set_ba_session(struct wl1271 *wl, int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn, bool enable); int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime); -int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl); -int wl1271_acx_sta_max_tx_retry(struct wl1271 *wl); +int wl1271_acx_max_tx_retry(struct wl1271 *wl); int wl1271_acx_config_ps(struct wl1271 *wl); int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr); +int wl1271_acx_set_ap_beacon_filter(struct wl1271 *wl, bool enable); +int wl1271_acx_fm_coex(struct wl1271 *wl); #endif /* __WL1271_ACX_H__ */ diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index d263ebb..b07f8b7 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -479,11 +479,11 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) RSSI_SNR_TRIGGER_0_EVENT_ID | PSPOLL_DELIVERY_FAILURE_EVENT_ID | SOFT_GEMINI_SENSE_EVENT_ID | - MAX_TX_RETRY_EVENT_ID; + PERIODIC_SCAN_REPORT_EVENT_ID | + PERIODIC_SCAN_COMPLETE_EVENT_ID; if (wl->bss_type == BSS_TYPE_AP_BSS) - wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID | - INACTIVE_STA_EVENT_ID; + wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID; else wl->event_mask |= DUMMY_PACKET_EVENT_ID; diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 69d24f3..42935ac 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -76,7 +76,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, if (time_after(jiffies, timeout)) { wl1271_error("command complete timeout"); ret = -ETIMEDOUT; - goto out; + goto fail; } poll_count++; @@ -96,14 +96,17 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, status = le16_to_cpu(cmd->status); if (status != CMD_STATUS_SUCCESS) { wl1271_error("command execute failure %d", status); - ieee80211_queue_work(wl->hw, &wl->recovery_work); ret = -EIO; + goto fail; } wl1271_write32(wl, ACX_REG_INTERRUPT_ACK, WL1271_ACX_INTR_CMD_COMPLETE); + return 0; -out: +fail: + WARN_ON(1); + ieee80211_queue_work(wl->hw, &wl->recovery_work); return ret; } @@ -129,6 +132,9 @@ int wl1271_cmd_general_parms(struct wl1271 *wl) if (gp->tx_bip_fem_auto_detect) answer = true; + /* Override the REF CLK from the NVS with the one from platform data */ + gen_parms->general_params.ref_clock = wl->ref_clock; + ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer); if (ret < 0) { wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed"); @@ -168,6 +174,10 @@ int wl128x_cmd_general_parms(struct wl1271 *wl) if (gp->tx_bip_fem_auto_detect) answer = true; + /* Replace REF and TCXO CLKs with the ones from platform data */ + gen_parms->general_params.ref_clock = wl->ref_clock; + gen_parms->general_params.tcxo_ref_clock = wl->tcxo_clock; + ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer); if (ret < 0) { wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed"); @@ -1070,7 +1080,7 @@ int wl1271_cmd_start_bss(struct wl1271 *wl) memcpy(cmd->bssid, bss_conf->bssid, ETH_ALEN); - cmd->aging_period = cpu_to_le16(wl->conf.tx.ap_aging_period); + cmd->aging_period = cpu_to_le16(WL1271_AP_DEF_INACTIV_SEC); cmd->bss_index = WL1271_AP_BSS_INDEX; cmd->global_hlid = WL1271_AP_GLOBAL_HLID; cmd->broadcast_hlid = WL1271_AP_BROADCAST_HLID; diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index e3de915..1ab6c86 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -396,12 +396,43 @@ enum { CONF_SG_TEMP_PARAM_3, CONF_SG_TEMP_PARAM_4, CONF_SG_TEMP_PARAM_5, - CONF_SG_PARAMS_MAX, + + /* + * AP beacon miss + * + * Range: 0 - 255 + */ + CONF_SG_AP_BEACON_MISS_TX, + + /* + * AP RX window length + * + * Range: 0 - 50 + */ + CONF_SG_RX_WINDOW_LENGTH, + + /* + * AP connection protection time + * + * Range: 0 - 5000 + */ + CONF_SG_AP_CONNECTION_PROTECTION_TIME, + + CONF_SG_TEMP_PARAM_6, + CONF_SG_TEMP_PARAM_7, + CONF_SG_TEMP_PARAM_8, + CONF_SG_TEMP_PARAM_9, + CONF_SG_TEMP_PARAM_10, + + CONF_SG_STA_PARAMS_MAX = CONF_SG_TEMP_PARAM_5 + 1, + CONF_SG_AP_PARAMS_MAX = CONF_SG_TEMP_PARAM_10 + 1, + CONF_SG_PARAMS_ALL = 0xff }; struct conf_sg_settings { - u32 params[CONF_SG_PARAMS_MAX]; + u32 sta_params[CONF_SG_STA_PARAMS_MAX]; + u32 ap_params[CONF_SG_AP_PARAMS_MAX]; u8 state; }; @@ -509,6 +540,12 @@ struct conf_rx_settings { CONF_HW_BIT_RATE_36MBPS | CONF_HW_BIT_RATE_48MBPS | \ CONF_HW_BIT_RATE_54MBPS) +#define CONF_TX_OFDM_RATES (CONF_HW_BIT_RATE_6MBPS | \ + CONF_HW_BIT_RATE_12MBPS | CONF_HW_BIT_RATE_24MBPS | \ + CONF_HW_BIT_RATE_36MBPS | CONF_HW_BIT_RATE_48MBPS | \ + CONF_HW_BIT_RATE_54MBPS) + + /* * Default rates for management traffic when operating in AP mode. This * should be configured according to the basic rate set of the AP @@ -516,6 +553,13 @@ struct conf_rx_settings { #define CONF_TX_AP_DEFAULT_MGMT_RATES (CONF_HW_BIT_RATE_1MBPS | \ CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS) +/* + * Default rates for working as IBSS. use 11b rates + */ +#define CONF_TX_IBSS_DEFAULT_RATES (CONF_HW_BIT_RATE_1MBPS | \ + CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | \ + CONF_HW_BIT_RATE_11MBPS); + struct conf_tx_rate_class { /* @@ -667,34 +711,10 @@ struct conf_tx_settings { struct conf_tx_ac_category ac_conf[CONF_TX_MAX_AC_COUNT]; /* - * Configuration for rate classes in AP-mode. These rate classes - * are for the AC TX queues - */ - struct conf_tx_rate_class ap_rc_conf[CONF_TX_MAX_AC_COUNT]; - - /* - * Management TX rate class for AP-mode. - */ - struct conf_tx_rate_class ap_mgmt_conf; - - /* - * Broadcast TX rate class for AP-mode. - */ - struct conf_tx_rate_class ap_bcst_conf; - - /* - * Allow this number of TX retries to a connected station/AP before an + * AP-mode - allow this number of TX retries to a station before an * event is triggered from FW. - * In AP-mode the hlids of unreachable stations are given in the - * "sta_tx_retry_exceeded" member in the event mailbox. - */ - u8 max_tx_retries; - - /* - * AP-mode - after this number of seconds a connected station is - * considered inactive. */ - u16 ap_aging_period; + u16 ap_max_tx_retries; /* * Configuration for TID parameters. @@ -1127,6 +1147,26 @@ struct conf_scan_settings { }; +struct conf_sched_scan_settings { + /* minimum time to wait on the channel for active scans (in TUs) */ + u16 min_dwell_time_active; + + /* maximum time to wait on the channel for active scans (in TUs) */ + u16 max_dwell_time_active; + + /* time to wait on the channel for passive scans (in TUs) */ + u32 dwell_time_passive; + + /* number of probe requests to send on each channel in active scans */ + u8 num_probe_reqs; + + /* RSSI threshold to be used for filtering */ + s8 rssi_threshold; + + /* SNR threshold to be used for filtering */ + s8 snr_threshold; +}; + /* these are number of channels on the band divided by two, rounded up */ #define CONF_TX_PWR_COMPENSATION_LEN_2 7 #define CONF_TX_PWR_COMPENSATION_LEN_5 18 @@ -1192,6 +1232,19 @@ struct conf_memory_settings { u8 tx_min; }; +struct conf_fm_coex { + u8 enable; + u8 swallow_period; + u8 n_divider_fref_set_1; + u8 n_divider_fref_set_2; + u16 m_divider_fref_set_1; + u16 m_divider_fref_set_2; + u32 coex_pll_stabilization_time; + u16 ldo_stabilization_time; + u8 fm_disturbed_band_margin; + u8 swallow_clk_diff; +}; + struct conf_drv_settings { struct conf_sg_settings sg; struct conf_rx_settings rx; @@ -1201,10 +1254,12 @@ struct conf_drv_settings { struct conf_pm_config_settings pm_config; struct conf_roam_trigger_settings roam_trigger; struct conf_scan_settings scan; + struct conf_sched_scan_settings sched_scan; struct conf_rf_settings rf; struct conf_ht_setting ht; struct conf_memory_settings mem_wl127x; struct conf_memory_settings mem_wl128x; + struct conf_fm_coex fm_coex; u8 hci_io_ds; }; diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index 70ab198..f1f8df9 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -291,6 +291,242 @@ static const struct file_operations gpio_power_ops = { .llseek = default_llseek, }; +static ssize_t start_recovery_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + + mutex_lock(&wl->mutex); + ieee80211_queue_work(wl->hw, &wl->recovery_work); + mutex_unlock(&wl->mutex); + + return count; +} + +static const struct file_operations start_recovery_ops = { + .write = start_recovery_write, + .open = wl1271_open_file_generic, + .llseek = default_llseek, +}; + +static ssize_t driver_state_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + int res = 0; + char buf[1024]; + + mutex_lock(&wl->mutex); + +#define DRIVER_STATE_PRINT(x, fmt) \ + (res += scnprintf(buf + res, sizeof(buf) - res,\ + #x " = " fmt "\n", wl->x)) + +#define DRIVER_STATE_PRINT_LONG(x) DRIVER_STATE_PRINT(x, "%ld") +#define DRIVER_STATE_PRINT_INT(x) DRIVER_STATE_PRINT(x, "%d") +#define DRIVER_STATE_PRINT_STR(x) DRIVER_STATE_PRINT(x, "%s") +#define DRIVER_STATE_PRINT_LHEX(x) DRIVER_STATE_PRINT(x, "0x%lx") +#define DRIVER_STATE_PRINT_HEX(x) DRIVER_STATE_PRINT(x, "0x%x") + + DRIVER_STATE_PRINT_INT(tx_blocks_available); + DRIVER_STATE_PRINT_INT(tx_allocated_blocks); + DRIVER_STATE_PRINT_INT(tx_frames_cnt); + DRIVER_STATE_PRINT_LHEX(tx_frames_map[0]); + DRIVER_STATE_PRINT_INT(tx_queue_count); + DRIVER_STATE_PRINT_INT(tx_packets_count); + DRIVER_STATE_PRINT_INT(tx_results_count); + DRIVER_STATE_PRINT_LHEX(flags); + DRIVER_STATE_PRINT_INT(tx_blocks_freed[0]); + DRIVER_STATE_PRINT_INT(tx_blocks_freed[1]); + DRIVER_STATE_PRINT_INT(tx_blocks_freed[2]); + DRIVER_STATE_PRINT_INT(tx_blocks_freed[3]); + DRIVER_STATE_PRINT_INT(tx_security_last_seq); + DRIVER_STATE_PRINT_INT(rx_counter); + DRIVER_STATE_PRINT_INT(session_counter); + DRIVER_STATE_PRINT_INT(state); + DRIVER_STATE_PRINT_INT(bss_type); + DRIVER_STATE_PRINT_INT(channel); + DRIVER_STATE_PRINT_HEX(rate_set); + DRIVER_STATE_PRINT_HEX(basic_rate_set); + DRIVER_STATE_PRINT_HEX(basic_rate); + DRIVER_STATE_PRINT_INT(band); + DRIVER_STATE_PRINT_INT(beacon_int); + DRIVER_STATE_PRINT_INT(psm_entry_retry); + DRIVER_STATE_PRINT_INT(ps_poll_failures); + DRIVER_STATE_PRINT_HEX(filters); + DRIVER_STATE_PRINT_HEX(rx_config); + DRIVER_STATE_PRINT_HEX(rx_filter); + DRIVER_STATE_PRINT_INT(power_level); + DRIVER_STATE_PRINT_INT(rssi_thold); + DRIVER_STATE_PRINT_INT(last_rssi_event); + DRIVER_STATE_PRINT_INT(sg_enabled); + DRIVER_STATE_PRINT_INT(enable_11a); + DRIVER_STATE_PRINT_INT(noise); + DRIVER_STATE_PRINT_LHEX(ap_hlid_map[0]); + DRIVER_STATE_PRINT_INT(last_tx_hlid); + DRIVER_STATE_PRINT_INT(ba_support); + DRIVER_STATE_PRINT_HEX(ba_rx_bitmap); + DRIVER_STATE_PRINT_HEX(ap_fw_ps_map); + DRIVER_STATE_PRINT_LHEX(ap_ps_map); + DRIVER_STATE_PRINT_HEX(quirks); + DRIVER_STATE_PRINT_HEX(irq); + DRIVER_STATE_PRINT_HEX(ref_clock); + DRIVER_STATE_PRINT_HEX(tcxo_clock); + DRIVER_STATE_PRINT_HEX(hw_pg_ver); + DRIVER_STATE_PRINT_HEX(platform_quirks); + DRIVER_STATE_PRINT_HEX(chip.id); + DRIVER_STATE_PRINT_STR(chip.fw_ver_str); + DRIVER_STATE_PRINT_INT(sched_scanning); + +#undef DRIVER_STATE_PRINT_INT +#undef DRIVER_STATE_PRINT_LONG +#undef DRIVER_STATE_PRINT_HEX +#undef DRIVER_STATE_PRINT_LHEX +#undef DRIVER_STATE_PRINT_STR +#undef DRIVER_STATE_PRINT + + mutex_unlock(&wl->mutex); + + return simple_read_from_buffer(user_buf, count, ppos, buf, res); +} + +static const struct file_operations driver_state_ops = { + .read = driver_state_read, + .open = wl1271_open_file_generic, + .llseek = default_llseek, +}; + +static ssize_t dtim_interval_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + u8 value; + + if (wl->conf.conn.wake_up_event == CONF_WAKE_UP_EVENT_DTIM || + wl->conf.conn.wake_up_event == CONF_WAKE_UP_EVENT_N_DTIM) + value = wl->conf.conn.listen_interval; + else + value = 0; + + return wl1271_format_buffer(user_buf, count, ppos, "%d\n", value); +} + +static ssize_t dtim_interval_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + char buf[10]; + size_t len; + unsigned long value; + int ret; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + buf[len] = '\0'; + + ret = kstrtoul(buf, 0, &value); + if (ret < 0) { + wl1271_warning("illegal value for dtim_interval"); + return -EINVAL; + } + + if (value < 1 || value > 10) { + wl1271_warning("dtim value is not in valid range"); + return -ERANGE; + } + + mutex_lock(&wl->mutex); + + wl->conf.conn.listen_interval = value; + /* for some reason there are different event types for 1 and >1 */ + if (value == 1) + wl->conf.conn.wake_up_event = CONF_WAKE_UP_EVENT_DTIM; + else + wl->conf.conn.wake_up_event = CONF_WAKE_UP_EVENT_N_DTIM; + + /* + * we don't reconfigure ACX_WAKE_UP_CONDITIONS now, so it will only + * take effect on the next time we enter psm. + */ + mutex_unlock(&wl->mutex); + return count; +} + +static const struct file_operations dtim_interval_ops = { + .read = dtim_interval_read, + .write = dtim_interval_write, + .open = wl1271_open_file_generic, + .llseek = default_llseek, +}; + +static ssize_t beacon_interval_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + u8 value; + + if (wl->conf.conn.wake_up_event == CONF_WAKE_UP_EVENT_BEACON || + wl->conf.conn.wake_up_event == CONF_WAKE_UP_EVENT_N_BEACONS) + value = wl->conf.conn.listen_interval; + else + value = 0; + + return wl1271_format_buffer(user_buf, count, ppos, "%d\n", value); +} + +static ssize_t beacon_interval_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + char buf[10]; + size_t len; + unsigned long value; + int ret; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + buf[len] = '\0'; + + ret = kstrtoul(buf, 0, &value); + if (ret < 0) { + wl1271_warning("illegal value for beacon_interval"); + return -EINVAL; + } + + if (value < 1 || value > 255) { + wl1271_warning("beacon interval value is not in valid range"); + return -ERANGE; + } + + mutex_lock(&wl->mutex); + + wl->conf.conn.listen_interval = value; + /* for some reason there are different event types for 1 and >1 */ + if (value == 1) + wl->conf.conn.wake_up_event = CONF_WAKE_UP_EVENT_BEACON; + else + wl->conf.conn.wake_up_event = CONF_WAKE_UP_EVENT_N_BEACONS; + + /* + * we don't reconfigure ACX_WAKE_UP_CONDITIONS now, so it will only + * take effect on the next time we enter psm. + */ + mutex_unlock(&wl->mutex); + return count; +} + +static const struct file_operations beacon_interval_ops = { + .read = beacon_interval_read, + .write = beacon_interval_write, + .open = wl1271_open_file_generic, + .llseek = default_llseek, +}; + static int wl1271_debugfs_add_files(struct wl1271 *wl, struct dentry *rootdir) { @@ -399,6 +635,10 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl, DEBUGFS_ADD(excessive_retries, rootdir); DEBUGFS_ADD(gpio_power, rootdir); + DEBUGFS_ADD(start_recovery, rootdir); + DEBUGFS_ADD(driver_state, rootdir); + DEBUGFS_ADD(dtim_interval, rootdir); + DEBUGFS_ADD(beacon_interval, rootdir); return 0; diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c index d7be3ae..c3c554c 100644 --- a/drivers/net/wireless/wl12xx/event.c +++ b/drivers/net/wireless/wl12xx/event.c @@ -135,6 +135,13 @@ static int wl1271_event_ps_report(struct wl1271 *wl, /* enable beacon early termination */ ret = wl1271_acx_bet_enable(wl, true); + if (ret < 0) + break; + + if (wl->ps_compl) { + complete(wl->ps_compl); + wl->ps_compl = NULL; + } break; default: break; @@ -174,8 +181,6 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) u32 vector; bool beacon_loss = false; bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS); - bool disconnect_sta = false; - unsigned long sta_bitmap = 0; wl1271_event_mbox_dump(mbox); @@ -190,6 +195,22 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) wl1271_scan_stm(wl); } + if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) { + wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_REPORT_EVENT " + "(status 0x%0x)", mbox->scheduled_scan_status); + + wl1271_scan_sched_scan_results(wl); + } + + if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID) { + wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT " + "(status 0x%0x)", mbox->scheduled_scan_status); + if (wl->sched_scanning) { + wl1271_scan_sched_scan_stop(wl); + ieee80211_sched_scan_stopped(wl->hw); + } + } + /* disable dynamic PS when requested by the firmware */ if (vector & SOFT_GEMINI_SENSE_EVENT_ID && wl->bss_type == BSS_TYPE_STA_BSS) { @@ -237,54 +258,9 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) wl1271_tx_dummy_packet(wl); } - /* - * "TX retries exceeded" has a different meaning according to mode. - * In AP mode the offending station is disconnected. In STA mode we - * report connection loss. - */ - if (vector & MAX_TX_RETRY_EVENT_ID) { - wl1271_debug(DEBUG_EVENT, "MAX_TX_RETRY_EVENT_ID"); - if (is_ap) { - sta_bitmap |= le16_to_cpu(mbox->sta_tx_retry_exceeded); - disconnect_sta = true; - } else { - beacon_loss = true; - } - } - - if ((vector & INACTIVE_STA_EVENT_ID) && is_ap) { - wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID"); - sta_bitmap |= le16_to_cpu(mbox->sta_aging_status); - disconnect_sta = true; - } - if (wl->vif && beacon_loss) ieee80211_connection_loss(wl->vif); - if (is_ap && disconnect_sta) { - u32 num_packets = wl->conf.tx.max_tx_retries; - struct ieee80211_sta *sta; - const u8 *addr; - int h; - - for (h = find_first_bit(&sta_bitmap, AP_MAX_LINKS); - h < AP_MAX_LINKS; - h = find_next_bit(&sta_bitmap, AP_MAX_LINKS, h+1)) { - if (!wl1271_is_active_sta(wl, h)) - continue; - - addr = wl->links[h].addr; - - rcu_read_lock(); - sta = ieee80211_find_sta(wl->vif, addr); - if (sta) { - wl1271_debug(DEBUG_EVENT, "remove sta %d", h); - ieee80211_report_low_ack(sta, num_packets); - } - rcu_read_unlock(); - } - } - return 0; } diff --git a/drivers/net/wireless/wl12xx/event.h b/drivers/net/wireless/wl12xx/event.h index 7ae5a08..b6cf06e 100644 --- a/drivers/net/wireless/wl12xx/event.h +++ b/drivers/net/wireless/wl12xx/event.h @@ -58,16 +58,13 @@ enum { CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(17), BSS_LOSE_EVENT_ID = BIT(18), REGAINED_BSS_EVENT_ID = BIT(19), - MAX_TX_RETRY_EVENT_ID = BIT(20), + ROAMING_TRIGGER_MAX_TX_RETRY_EVENT_ID = BIT(20), /* STA: dummy paket for dynamic mem blocks */ DUMMY_PACKET_EVENT_ID = BIT(21), /* AP: STA remove complete */ STA_REMOVE_COMPLETE_EVENT_ID = BIT(21), SOFT_GEMINI_SENSE_EVENT_ID = BIT(22), - /* STA: SG prediction */ SOFT_GEMINI_PREDICTION_EVENT_ID = BIT(23), - /* AP: Inactive STA */ - INACTIVE_STA_EVENT_ID = BIT(23), SOFT_GEMINI_AVALANCHE_EVENT_ID = BIT(24), PLT_RX_CALIBRATION_COMPLETE_EVENT_ID = BIT(25), DBG_EVENT_ID = BIT(26), @@ -122,11 +119,7 @@ struct event_mailbox { /* AP FW only */ u8 hlid_removed; - - /* a bitmap of hlids for stations that have been inactive too long */ __le16 sta_aging_status; - - /* a bitmap of hlids for stations which didn't respond to TX */ __le16 sta_tx_retry_exceeded; u8 reserved_5[24]; @@ -137,7 +130,4 @@ void wl1271_event_mbox_config(struct wl1271 *wl); int wl1271_event_handle(struct wl1271 *wl, u8 mbox); void wl1271_pspoll_work(struct work_struct *work); -/* Functions from main.c */ -bool wl1271_is_active_sta(struct wl1271 *wl, u8 hlid); - #endif diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index ab3b1e2..a8f4f15 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -258,7 +258,7 @@ int wl1271_init_phy_config(struct wl1271 *wl) if (ret < 0) return ret; - ret = wl1271_acx_rts_threshold(wl, wl->conf.rx.rts_threshold); + ret = wl1271_acx_rts_threshold(wl, wl->hw->wiphy->rts_threshold); if (ret < 0) return ret; @@ -285,7 +285,10 @@ int wl1271_init_pta(struct wl1271 *wl) { int ret; - ret = wl1271_acx_sg_cfg(wl); + if (wl->bss_type == BSS_TYPE_AP_BSS) + ret = wl1271_acx_ap_sg_cfg(wl); + else + ret = wl1271_acx_sta_sg_cfg(wl); if (ret < 0) return ret; @@ -351,8 +354,8 @@ static int wl1271_sta_hw_init(struct wl1271 *wl) if (ret < 0) return ret; - /* Bluetooth WLAN coexistence */ - ret = wl1271_init_pta(wl); + /* FM WLAN coexistence */ + ret = wl1271_acx_fm_coex(wl); if (ret < 0) return ret; @@ -375,10 +378,6 @@ static int wl1271_sta_hw_init(struct wl1271 *wl) if (ret < 0) return ret; - ret = wl1271_acx_sta_max_tx_retry(wl); - if (ret < 0) - return ret; - ret = wl1271_acx_sta_mem_cfg(wl); if (ret < 0) return ret; @@ -414,7 +413,7 @@ static int wl1271_sta_hw_init_post_mem(struct wl1271 *wl) static int wl1271_ap_hw_init(struct wl1271 *wl) { - int ret, i; + int ret; ret = wl1271_ap_init_templates_config(wl); if (ret < 0) @@ -425,27 +424,11 @@ static int wl1271_ap_hw_init(struct wl1271 *wl) if (ret < 0) return ret; - /* Configure initial TX rate classes */ - for (i = 0; i < wl->conf.tx.ac_conf_count; i++) { - ret = wl1271_acx_ap_rate_policy(wl, - &wl->conf.tx.ap_rc_conf[i], i); - if (ret < 0) - return ret; - } - - ret = wl1271_acx_ap_rate_policy(wl, - &wl->conf.tx.ap_mgmt_conf, - ACX_TX_AP_MODE_MGMT_RATE); + ret = wl1271_init_ap_rates(wl); if (ret < 0) return ret; - ret = wl1271_acx_ap_rate_policy(wl, - &wl->conf.tx.ap_bcst_conf, - ACX_TX_AP_MODE_BCST_RATE); - if (ret < 0) - return ret; - - ret = wl1271_acx_ap_max_tx_retry(wl); + ret = wl1271_acx_max_tx_retry(wl); if (ret < 0) return ret; @@ -456,7 +439,7 @@ static int wl1271_ap_hw_init(struct wl1271 *wl) return 0; } -static int wl1271_ap_hw_init_post_mem(struct wl1271 *wl) +int wl1271_ap_init_templates(struct wl1271 *wl) { int ret; @@ -472,6 +455,70 @@ static int wl1271_ap_hw_init_post_mem(struct wl1271 *wl) if (ret < 0) return ret; + /* + * when operating as AP we want to receive external beacons for + * configuring ERP protection. + */ + ret = wl1271_acx_set_ap_beacon_filter(wl, false); + if (ret < 0) + return ret; + + return 0; +} + +static int wl1271_ap_hw_init_post_mem(struct wl1271 *wl) +{ + return wl1271_ap_init_templates(wl); +} + +int wl1271_init_ap_rates(struct wl1271 *wl) +{ + int i, ret; + struct conf_tx_rate_class rc; + u32 supported_rates; + + wl1271_debug(DEBUG_AP, "AP basic rate set: 0x%x", wl->basic_rate_set); + + if (wl->basic_rate_set == 0) + return -EINVAL; + + rc.enabled_rates = wl->basic_rate_set; + rc.long_retry_limit = 10; + rc.short_retry_limit = 10; + rc.aflags = 0; + ret = wl1271_acx_ap_rate_policy(wl, &rc, ACX_TX_AP_MODE_MGMT_RATE); + if (ret < 0) + return ret; + + /* use the min basic rate for AP broadcast/multicast */ + rc.enabled_rates = wl1271_tx_min_rate_get(wl); + rc.short_retry_limit = 10; + rc.long_retry_limit = 10; + rc.aflags = 0; + ret = wl1271_acx_ap_rate_policy(wl, &rc, ACX_TX_AP_MODE_BCST_RATE); + if (ret < 0) + return ret; + + /* + * If the basic rates contain OFDM rates, use OFDM only + * rates for unicast TX as well. Else use all supported rates. + */ + if ((wl->basic_rate_set & CONF_TX_OFDM_RATES)) + supported_rates = CONF_TX_OFDM_RATES; + else + supported_rates = CONF_TX_AP_ENABLED_RATES; + + /* configure unicast TX rate classes */ + for (i = 0; i < wl->conf.tx.ac_conf_count; i++) { + rc.enabled_rates = supported_rates; + rc.short_retry_limit = 10; + rc.long_retry_limit = 10; + rc.aflags = 0; + ret = wl1271_acx_ap_rate_policy(wl, &rc, i); + if (ret < 0) + return ret; + } + return 0; } @@ -567,6 +614,11 @@ int wl1271_hw_init(struct wl1271 *wl) if (ret < 0) return ret; + /* Bluetooth WLAN coexistence */ + ret = wl1271_init_pta(wl); + if (ret < 0) + return ret; + /* Default memory configuration */ ret = wl1271_acx_init_mem_config(wl); if (ret < 0) @@ -606,7 +658,7 @@ int wl1271_hw_init(struct wl1271 *wl) goto out_free_memmap; /* Default fragmentation threshold */ - ret = wl1271_acx_frag_threshold(wl, wl->conf.tx.frag_threshold); + ret = wl1271_acx_frag_threshold(wl, wl->hw->wiphy->frag_threshold); if (ret < 0) goto out_free_memmap; diff --git a/drivers/net/wireless/wl12xx/init.h b/drivers/net/wireless/wl12xx/init.h index 4975270..3a3c230 100644 --- a/drivers/net/wireless/wl12xx/init.h +++ b/drivers/net/wireless/wl12xx/init.h @@ -33,5 +33,7 @@ int wl1271_init_pta(struct wl1271 *wl); int wl1271_init_energy_detection(struct wl1271 *wl); int wl1271_chip_specific_init(struct wl1271 *wl); int wl1271_hw_init(struct wl1271 *wl); +int wl1271_init_ap_rates(struct wl1271 *wl); +int wl1271_ap_init_templates(struct wl1271 *wl); #endif diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 0c69e95..610be03 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -51,7 +51,7 @@ static struct conf_drv_settings default_conf = { .sg = { - .params = { + .sta_params = { [CONF_SG_BT_PER_THRESHOLD] = 7500, [CONF_SG_HV3_MAX_OVERRIDE] = 0, [CONF_SG_BT_NFS_SAMPLE_INTERVAL] = 400, @@ -101,6 +101,61 @@ static struct conf_drv_settings default_conf = { [CONF_SG_DHCP_TIME] = 5000, [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100, }, + .ap_params = { + [CONF_SG_BT_PER_THRESHOLD] = 7500, + [CONF_SG_HV3_MAX_OVERRIDE] = 0, + [CONF_SG_BT_NFS_SAMPLE_INTERVAL] = 400, + [CONF_SG_BT_LOAD_RATIO] = 50, + [CONF_SG_AUTO_PS_MODE] = 1, + [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170, + [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50, + [CONF_SG_ANTENNA_CONFIGURATION] = 0, + [CONF_SG_BEACON_MISS_PERCENT] = 60, + [CONF_SG_RATE_ADAPT_THRESH] = 64, + [CONF_SG_RATE_ADAPT_SNR] = 1, + [CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_BR] = 10, + [CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_BR] = 25, + [CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_BR] = 25, + [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_BR] = 20, + [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_BR] = 25, + [CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_BR] = 25, + [CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_EDR] = 7, + [CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_EDR] = 25, + [CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_EDR] = 25, + [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_EDR] = 8, + [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_EDR] = 25, + [CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_EDR] = 25, + [CONF_SG_RXT] = 1200, + [CONF_SG_TXT] = 1000, + [CONF_SG_ADAPTIVE_RXT_TXT] = 1, + [CONF_SG_PS_POLL_TIMEOUT] = 10, + [CONF_SG_UPSD_TIMEOUT] = 10, + [CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MIN_EDR] = 7, + [CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MAX_EDR] = 15, + [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_MASTER_EDR] = 15, + [CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MIN_EDR] = 8, + [CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MAX_EDR] = 20, + [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_SLAVE_EDR] = 15, + [CONF_SG_WLAN_ACTIVE_BT_ACL_MIN_BR] = 20, + [CONF_SG_WLAN_ACTIVE_BT_ACL_MAX_BR] = 50, + [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_BR] = 10, + [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200, + [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP] = 800, + [CONF_SG_PASSIVE_SCAN_A2DP_BT_TIME] = 75, + [CONF_SG_PASSIVE_SCAN_A2DP_WLAN_TIME] = 15, + [CONF_SG_HV3_MAX_SERVED] = 6, + [CONF_SG_DHCP_TIME] = 5000, + [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100, + [CONF_SG_TEMP_PARAM_1] = 0, + [CONF_SG_TEMP_PARAM_2] = 0, + [CONF_SG_TEMP_PARAM_3] = 0, + [CONF_SG_TEMP_PARAM_4] = 0, + [CONF_SG_TEMP_PARAM_5] = 0, + [CONF_SG_AP_BEACON_MISS_TX] = 3, + [CONF_SG_RX_WINDOW_LENGTH] = 6, + [CONF_SG_AP_CONNECTION_PROTECTION_TIME] = 50, + [CONF_SG_TEMP_PARAM_6] = 1, + }, .state = CONF_SG_PROTECTIVE, }, .rx = { @@ -108,7 +163,7 @@ static struct conf_drv_settings default_conf = { .packet_detection_threshold = 0, .ps_poll_timeout = 15, .upsd_timeout = 15, - .rts_threshold = 2347, + .rts_threshold = IEEE80211_MAX_RTS_THRESHOLD, .rx_cca_threshold = 0, .irq_blk_threshold = 0xFFFF, .irq_pkt_threshold = 0, @@ -154,46 +209,7 @@ static struct conf_drv_settings default_conf = { .tx_op_limit = 1504, }, }, - .ap_rc_conf = { - [0] = { - .enabled_rates = CONF_TX_AP_ENABLED_RATES, - .short_retry_limit = 10, - .long_retry_limit = 10, - .aflags = 0, - }, - [1] = { - .enabled_rates = CONF_TX_AP_ENABLED_RATES, - .short_retry_limit = 10, - .long_retry_limit = 10, - .aflags = 0, - }, - [2] = { - .enabled_rates = CONF_TX_AP_ENABLED_RATES, - .short_retry_limit = 10, - .long_retry_limit = 10, - .aflags = 0, - }, - [3] = { - .enabled_rates = CONF_TX_AP_ENABLED_RATES, - .short_retry_limit = 10, - .long_retry_limit = 10, - .aflags = 0, - }, - }, - .ap_mgmt_conf = { - .enabled_rates = CONF_TX_AP_DEFAULT_MGMT_RATES, - .short_retry_limit = 10, - .long_retry_limit = 10, - .aflags = 0, - }, - .ap_bcst_conf = { - .enabled_rates = CONF_HW_BIT_RATE_1MBPS, - .short_retry_limit = 10, - .long_retry_limit = 10, - .aflags = 0, - }, - .max_tx_retries = 100, - .ap_aging_period = 300, + .ap_max_tx_retries = 100, .tid_conf_count = 4, .tid_conf = { [CONF_TX_AC_BE] = { @@ -241,12 +257,16 @@ static struct conf_drv_settings default_conf = { .wake_up_event = CONF_WAKE_UP_EVENT_DTIM, .listen_interval = 1, .bcn_filt_mode = CONF_BCN_FILT_MODE_ENABLED, - .bcn_filt_ie_count = 1, + .bcn_filt_ie_count = 2, .bcn_filt_ie = { [0] = { .ie = WLAN_EID_CHANNEL_SWITCH, .rule = CONF_BCN_RULE_PASS_ON_APPEARANCE, - } + }, + [1] = { + .ie = WLAN_EID_HT_INFORMATION, + .rule = CONF_BCN_RULE_PASS_ON_CHANGE, + }, }, .synch_fail_thold = 10, .bss_lose_timeout = 100, @@ -258,7 +278,7 @@ static struct conf_drv_settings default_conf = { .bet_enable = CONF_BET_MODE_ENABLE, .bet_max_consecutive = 50, .psm_entry_retries = 5, - .psm_exit_retries = 255, + .psm_exit_retries = 16, .psm_entry_nullfunc_retries = 3, .psm_entry_hangover_period = 1, .keep_alive_interval = 55000, @@ -286,6 +306,15 @@ static struct conf_drv_settings default_conf = { .max_dwell_time_passive = 100000, .num_probe_reqs = 2, }, + .sched_scan = { + /* sched_scan requires dwell times in TU instead of TU/1000 */ + .min_dwell_time_active = 8, + .max_dwell_time_active = 30, + .dwell_time_passive = 100, + .num_probe_reqs = 2, + .rssi_threshold = -90, + .snr_threshold = 0, + }, .rf = { .tx_per_channel_power_compensation_2 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -305,7 +334,7 @@ static struct conf_drv_settings default_conf = { .ssid_profiles = 1, .rx_block_num = 70, .tx_min_block_num = 40, - .dynamic_memory = 0, + .dynamic_memory = 1, .min_req_tx_blocks = 100, .min_req_rx_blocks = 22, .tx_min = 27, @@ -320,10 +349,23 @@ static struct conf_drv_settings default_conf = { .min_req_rx_blocks = 22, .tx_min = 27, }, + .fm_coex = { + .enable = true, + .swallow_period = 5, + .n_divider_fref_set_1 = 0xff, /* default */ + .n_divider_fref_set_2 = 12, + .m_divider_fref_set_1 = 148, + .m_divider_fref_set_2 = 0xffff, /* default */ + .coex_pll_stabilization_time = 0xffffffff, /* default */ + .ldo_stabilization_time = 0xffff, /* default */ + .fm_disturbed_band_margin = 0xff, /* default */ + .swallow_clk_diff = 0xff, /* default */ + }, .hci_io_ds = HCI_IO_DS_6MA, }; -static void __wl1271_op_remove_interface(struct wl1271 *wl); +static void __wl1271_op_remove_interface(struct wl1271 *wl, + bool reset_tx_queues); static void wl1271_free_ap_keys(struct wl1271 *wl); @@ -508,6 +550,11 @@ static int wl1271_plt_init(struct wl1271 *wl) if (ret < 0) goto out_free_memmap; + /* FM WLAN coexistence */ + ret = wl1271_acx_fm_coex(wl); + if (ret < 0) + goto out_free_memmap; + /* Energy detection */ ret = wl1271_init_energy_detection(wl); if (ret < 0) @@ -932,15 +979,30 @@ static void wl1271_recovery_work(struct work_struct *work) if (wl->state != WL1271_STATE_ON) goto out; - wl1271_info("Hardware recovery in progress."); + wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x", + wl->chip.fw_ver_str, wl1271_read32(wl, SCR_PAD4)); if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) ieee80211_connection_loss(wl->vif); + /* Prevent spurious TX during FW restart */ + ieee80211_stop_queues(wl->hw); + + if (wl->sched_scanning) { + ieee80211_sched_scan_stopped(wl->hw); + wl->sched_scanning = false; + } + /* reboot the chipset */ - __wl1271_op_remove_interface(wl); + __wl1271_op_remove_interface(wl, false); ieee80211_restart_hw(wl->hw); + /* + * Its safe to enable TX now - the queues are stopped after a request + * to restart the HW. + */ + ieee80211_wake_queues(wl->hw); + out: mutex_unlock(&wl->mutex); } @@ -1011,6 +1073,10 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)", wl->chip.id); + /* end-of-transaction flag should be set in wl127x AP mode */ + if (wl->bss_type == BSS_TYPE_AP_BSS) + wl->quirks |= WL12XX_QUIRK_END_OF_TRANSACTION; + ret = wl1271_setup(wl); if (ret < 0) goto out; @@ -1273,7 +1339,7 @@ static struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl) skb->priority = WL1271_TID_MGMT; /* Initialize all fields that might be used */ - skb->queue_mapping = 0; + skb_set_queue_mapping(skb, 0); memset(IEEE80211_SKB_CB(skb), 0, sizeof(struct ieee80211_tx_info)); return skb; @@ -1284,6 +1350,150 @@ static struct notifier_block wl1271_dev_notifier = { .notifier_call = wl1271_dev_notify, }; +static int wl1271_configure_suspend(struct wl1271 *wl) +{ + int ret; + + if (wl->bss_type != BSS_TYPE_STA_BSS) + return 0; + + mutex_lock(&wl->mutex); + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out_unlock; + + /* enter psm if needed*/ + if (!test_bit(WL1271_FLAG_PSM, &wl->flags)) { + DECLARE_COMPLETION_ONSTACK(compl); + + wl->ps_compl = &compl; + ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE, + wl->basic_rate, true); + if (ret < 0) + goto out_sleep; + + /* we must unlock here so we will be able to get events */ + wl1271_ps_elp_sleep(wl); + mutex_unlock(&wl->mutex); + + ret = wait_for_completion_timeout( + &compl, msecs_to_jiffies(WL1271_PS_COMPLETE_TIMEOUT)); + if (ret <= 0) { + wl1271_warning("couldn't enter ps mode!"); + ret = -EBUSY; + goto out; + } + + /* take mutex again, and wakeup */ + mutex_lock(&wl->mutex); + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out_unlock; + } +out_sleep: + wl1271_ps_elp_sleep(wl); +out_unlock: + mutex_unlock(&wl->mutex); +out: + return ret; + +} + +static void wl1271_configure_resume(struct wl1271 *wl) +{ + int ret; + + if (wl->bss_type != BSS_TYPE_STA_BSS) + return; + + mutex_lock(&wl->mutex); + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + /* exit psm if it wasn't configured */ + if (!test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) + wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE, + wl->basic_rate, true); + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); +} + +static int wl1271_op_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wow) +{ + struct wl1271 *wl = hw->priv; + wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow); + wl->wow_enabled = !!wow; + if (wl->wow_enabled) { + int ret; + ret = wl1271_configure_suspend(wl); + if (ret < 0) { + wl1271_warning("couldn't prepare device to suspend"); + return ret; + } + /* flush any remaining work */ + wl1271_debug(DEBUG_MAC80211, "flushing remaining works"); + flush_delayed_work(&wl->scan_complete_work); + + /* + * disable and re-enable interrupts in order to flush + * the threaded_irq + */ + wl1271_disable_interrupts(wl); + + /* + * set suspended flag to avoid triggering a new threaded_irq + * work. no need for spinlock as interrupts are disabled. + */ + set_bit(WL1271_FLAG_SUSPENDED, &wl->flags); + + wl1271_enable_interrupts(wl); + flush_work(&wl->tx_work); + flush_delayed_work(&wl->pspoll_work); + flush_delayed_work(&wl->elp_work); + } + return 0; +} + +static int wl1271_op_resume(struct ieee80211_hw *hw) +{ + struct wl1271 *wl = hw->priv; + wl1271_debug(DEBUG_MAC80211, "mac80211 resume wow=%d", + wl->wow_enabled); + + /* + * re-enable irq_work enqueuing, and call irq_work directly if + * there is a pending work. + */ + if (wl->wow_enabled) { + struct wl1271 *wl = hw->priv; + unsigned long flags; + bool run_irq_work = false; + + spin_lock_irqsave(&wl->wl_lock, flags); + clear_bit(WL1271_FLAG_SUSPENDED, &wl->flags); + if (test_and_clear_bit(WL1271_FLAG_PENDING_WORK, &wl->flags)) + run_irq_work = true; + spin_unlock_irqrestore(&wl->wl_lock, flags); + + if (run_irq_work) { + wl1271_debug(DEBUG_MAC80211, + "run postponed irq_work directly"); + wl1271_irq(0, wl); + wl1271_enable_interrupts(wl); + } + + wl1271_configure_resume(wl); + } + + return 0; +} + static int wl1271_op_start(struct ieee80211_hw *hw) { wl1271_debug(DEBUG_MAC80211, "mac80211 start"); @@ -1440,7 +1650,8 @@ out: return ret; } -static void __wl1271_op_remove_interface(struct wl1271 *wl) +static void __wl1271_op_remove_interface(struct wl1271 *wl, + bool reset_tx_queues) { int i; @@ -1486,7 +1697,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) mutex_lock(&wl->mutex); /* let's notify MAC80211 about the remaining pending TX frames */ - wl1271_tx_reset(wl); + wl1271_tx_reset(wl, reset_tx_queues); wl1271_power_off(wl); memset(wl->bssid, 0, ETH_ALEN); @@ -1514,6 +1725,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map)); wl->ap_fw_ps_map = 0; wl->ap_ps_map = 0; + wl->sched_scanning = false; /* * this is performed after the cancel_work calls and the associated @@ -1547,7 +1759,7 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw, */ if (wl->vif) { WARN_ON(wl->vif != vif); - __wl1271_op_remove_interface(wl); + __wl1271_op_remove_interface(wl, true); } mutex_unlock(&wl->mutex); @@ -1716,6 +1928,13 @@ static int wl1271_sta_handle_idle(struct wl1271 *wl, bool idle) wl->session_counter++; if (wl->session_counter >= SESSION_COUNTER_MAX) wl->session_counter = 0; + + /* The current firmware only supports sched_scan in idle */ + if (wl->sched_scanning) { + wl1271_scan_sched_scan_stop(wl); + ieee80211_sched_scan_stopped(wl->hw); + } + ret = wl1271_dummy_join(wl); if (ret < 0) goto out; @@ -2268,6 +2487,60 @@ out: return ret; } +static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies) +{ + struct wl1271 *wl = hw->priv; + int ret; + + wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_start"); + + mutex_lock(&wl->mutex); + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + ret = wl1271_scan_sched_scan_config(wl, req, ies); + if (ret < 0) + goto out_sleep; + + ret = wl1271_scan_sched_scan_start(wl); + if (ret < 0) + goto out_sleep; + + wl->sched_scanning = true; + +out_sleep: + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); + return ret; +} + +static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct wl1271 *wl = hw->priv; + int ret; + + wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_stop"); + + mutex_lock(&wl->mutex); + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + wl1271_scan_sched_scan_stop(wl); + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); +} + static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value) { struct wl1271 *wl = hw->priv; @@ -2284,7 +2557,7 @@ static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value) if (ret < 0) goto out; - ret = wl1271_acx_frag_threshold(wl, (u16)value); + ret = wl1271_acx_frag_threshold(wl, value); if (ret < 0) wl1271_warning("wl1271_op_set_frag_threshold failed: %d", ret); @@ -2312,7 +2585,7 @@ static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value) if (ret < 0) goto out; - ret = wl1271_acx_rts_threshold(wl, (u16) value); + ret = wl1271_acx_rts_threshold(wl, value); if (ret < 0) wl1271_warning("wl1271_op_set_rts_threshold failed: %d", ret); @@ -2327,20 +2600,24 @@ out: static int wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb, int offset) { - u8 *ptr = skb->data + offset; + u8 ssid_len; + const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset, + skb->len - offset); - /* find the location of the ssid in the beacon */ - while (ptr < skb->data + skb->len) { - if (ptr[0] == WLAN_EID_SSID) { - wl->ssid_len = ptr[1]; - memcpy(wl->ssid, ptr+2, wl->ssid_len); - return 0; - } - ptr += (ptr[1] + 2); + if (!ptr) { + wl1271_error("No SSID in IEs!"); + return -ENOENT; } - wl1271_error("No SSID in IEs!\n"); - return -ENOENT; + ssid_len = ptr[1]; + if (ssid_len > IEEE80211_MAX_SSID_LEN) { + wl1271_error("SSID is too long!"); + return -EINVAL; + } + + wl->ssid_len = ssid_len; + memcpy(wl->ssid, ptr+2, ssid_len); + return 0; } static int wl1271_bss_erp_info_changed(struct wl1271 *wl, @@ -2455,24 +2732,19 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl, if ((changed & BSS_CHANGED_BASIC_RATES)) { u32 rates = bss_conf->basic_rates; - struct conf_tx_rate_class mgmt_rc; wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates); wl->basic_rate = wl1271_tx_min_rate_get(wl); - wl1271_debug(DEBUG_AP, "basic rates: 0x%x", - wl->basic_rate_set); - - /* update the AP management rate policy with the new rates */ - mgmt_rc.enabled_rates = wl->basic_rate_set; - mgmt_rc.long_retry_limit = 10; - mgmt_rc.short_retry_limit = 10; - mgmt_rc.aflags = 0; - ret = wl1271_acx_ap_rate_policy(wl, &mgmt_rc, - ACX_TX_AP_MODE_MGMT_RATE); + + ret = wl1271_init_ap_rates(wl); if (ret < 0) { - wl1271_error("AP mgmt policy change failed %d", ret); + wl1271_error("AP rate policy change failed %d", ret); goto out; } + + ret = wl1271_ap_init_templates(wl); + if (ret < 0) + goto out; } ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, changed); @@ -2505,6 +2777,24 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl, } } + if (changed & BSS_CHANGED_IBSS) { + wl1271_debug(DEBUG_ADHOC, "ibss_joined: %d", + bss_conf->ibss_joined); + + if (bss_conf->ibss_joined) { + u32 rates = bss_conf->basic_rates; + wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl, + rates); + wl->basic_rate = wl1271_tx_min_rate_get(wl); + + /* by default, use 11b rates */ + wl->rate_set = CONF_TX_IBSS_DEFAULT_RATES; + ret = wl1271_acx_sta_rate_policies(wl); + if (ret < 0) + goto out; + } + } + ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed); if (ret < 0) goto out; @@ -2694,8 +2984,10 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, } } else { /* use defaults when not associated */ + bool was_assoc = + !!test_and_clear_bit(WL1271_FLAG_STA_ASSOCIATED, + &wl->flags); clear_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags); - clear_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags); wl->aid = 0; /* free probe-request template */ @@ -2721,8 +3013,10 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, goto out; /* restore the bssid filter and go to dummy bssid */ - wl1271_unjoin(wl); - wl1271_dummy_join(wl); + if (was_assoc) { + wl1271_unjoin(wl); + wl1271_dummy_join(wl); + } } } @@ -2954,12 +3248,6 @@ static void wl1271_free_sta(struct wl1271 *wl, u8 hlid) __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); } -bool wl1271_is_active_sta(struct wl1271 *wl, u8 hlid) -{ - int id = hlid - WL1271_AP_STA_HLID_START; - return test_bit(id, wl->ap_hlid_map); -} - static int wl1271_op_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) @@ -3104,6 +3392,28 @@ out: return ret; } +static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw) +{ + struct wl1271 *wl = hw->priv; + bool ret = false; + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state == WL1271_STATE_OFF)) + goto out; + + /* packets are considered pending if in the TX queue or the FW */ + ret = (wl->tx_queue_count > 0) || (wl->tx_frames_cnt > 0); + + /* the above is appropriate for STA mode for PS purposes */ + WARN_ON(wl->bss_type != BSS_TYPE_STA_BSS); + +out: + mutex_unlock(&wl->mutex); + + return ret; +} + /* can't be const, mac80211 writes to this */ static struct ieee80211_rate wl1271_rates[] = { { .bitrate = 10, @@ -3340,12 +3650,16 @@ static const struct ieee80211_ops wl1271_ops = { .stop = wl1271_op_stop, .add_interface = wl1271_op_add_interface, .remove_interface = wl1271_op_remove_interface, + .suspend = wl1271_op_suspend, + .resume = wl1271_op_resume, .config = wl1271_op_config, .prepare_multicast = wl1271_op_prepare_multicast, .configure_filter = wl1271_op_configure_filter, .tx = wl1271_op_tx, .set_key = wl1271_op_set_key, .hw_scan = wl1271_op_hw_scan, + .sched_scan_start = wl1271_op_sched_scan_start, + .sched_scan_stop = wl1271_op_sched_scan_stop, .bss_info_changed = wl1271_op_bss_info_changed, .set_frag_threshold = wl1271_op_set_frag_threshold, .set_rts_threshold = wl1271_op_set_rts_threshold, @@ -3355,6 +3669,7 @@ static const struct ieee80211_ops wl1271_ops = { .sta_add = wl1271_op_sta_add, .sta_remove = wl1271_op_sta_remove, .ampdu_action = wl1271_op_ampdu_action, + .tx_frames_pending = wl1271_tx_frames_pending, CFG80211_TESTMODE_CMD(wl1271_tm_cmd) }; @@ -3542,6 +3857,8 @@ int wl1271_init_ieee80211(struct wl1271 *wl) IEEE80211_HW_HAS_RATE_CONTROL | IEEE80211_HW_CONNECTION_MONITOR | IEEE80211_HW_SUPPORTS_CQM_RSSI | + IEEE80211_HW_REPORTS_TX_ACK_STATUS | + IEEE80211_HW_SPECTRUM_MGMT | IEEE80211_HW_AP_LINK_PS; wl->hw->wiphy->cipher_suites = cipher_suites; @@ -3663,6 +3980,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->ap_fw_ps_map = 0; wl->quirks = 0; wl->platform_quirks = 0; + wl->sched_scanning = false; memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map)); for (i = 0; i < ACX_TX_DESCRIPTORS; i++) diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c index b8deada..b59b6771 100644 --- a/drivers/net/wireless/wl12xx/ps.c +++ b/drivers/net/wireless/wl12xx/ps.c @@ -43,6 +43,10 @@ void wl1271_elp_work(struct work_struct *work) if (unlikely(wl->state == WL1271_STATE_OFF)) goto out; + /* our work might have been already cancelled */ + if (unlikely(!test_bit(WL1271_FLAG_ELP_REQUESTED, &wl->flags))) + goto out; + if (test_bit(WL1271_FLAG_IN_ELP, &wl->flags) || (!test_bit(WL1271_FLAG_PSM, &wl->flags) && !test_bit(WL1271_FLAG_IDLE, &wl->flags))) @@ -61,12 +65,16 @@ out: /* Routines to toggle sleep mode while in ELP */ void wl1271_ps_elp_sleep(struct wl1271 *wl) { - if (test_bit(WL1271_FLAG_PSM, &wl->flags) || - test_bit(WL1271_FLAG_IDLE, &wl->flags)) { - cancel_delayed_work(&wl->elp_work); - ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, - msecs_to_jiffies(ELP_ENTRY_DELAY)); - } + /* we shouldn't get consecutive sleep requests */ + if (WARN_ON(test_and_set_bit(WL1271_FLAG_ELP_REQUESTED, &wl->flags))) + return; + + if (!test_bit(WL1271_FLAG_PSM, &wl->flags) && + !test_bit(WL1271_FLAG_IDLE, &wl->flags)) + return; + + ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, + msecs_to_jiffies(ELP_ENTRY_DELAY)); } int wl1271_ps_elp_wakeup(struct wl1271 *wl) @@ -77,6 +85,16 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl) u32 start_time = jiffies; bool pending = false; + /* + * we might try to wake up even if we didn't go to sleep + * before (e.g. on boot) + */ + if (!test_and_clear_bit(WL1271_FLAG_ELP_REQUESTED, &wl->flags)) + return 0; + + /* don't cancel_sync as it might contend for a mutex and deadlock */ + cancel_delayed_work(&wl->elp_work); + if (!test_bit(WL1271_FLAG_IN_ELP, &wl->flags)) return 0; diff --git a/drivers/net/wireless/wl12xx/ps.h b/drivers/net/wireless/wl12xx/ps.h index c41bd0a..25eb9bc 100644 --- a/drivers/net/wireless/wl12xx/ps.h +++ b/drivers/net/wireless/wl12xx/ps.h @@ -35,4 +35,6 @@ void wl1271_elp_work(struct work_struct *work); void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues); void wl1271_ps_link_end(struct wl1271 *wl, u8 hlid); +#define WL1271_PS_COMPLETE_TIMEOUT 500 + #endif /* __WL1271_PS_H__ */ diff --git a/drivers/net/wireless/wl12xx/rx.c b/drivers/net/wireless/wl12xx/rx.c index 2a58149..7009103 100644 --- a/drivers/net/wireless/wl12xx/rx.c +++ b/drivers/net/wireless/wl12xx/rx.c @@ -76,12 +76,15 @@ static void wl1271_rx_status(struct wl1271 *wl, status->band); if (desc->flags & WL1271_RX_DESC_ENCRYPT_MASK) { - status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED; + u8 desc_err_code = desc->status & WL1271_RX_DESC_STATUS_MASK; - if (likely(!(desc->status & WL1271_RX_DESC_DECRYPT_FAIL))) - status->flag |= RX_FLAG_DECRYPTED; - if (unlikely(desc->status & WL1271_RX_DESC_MIC_FAIL)) + status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED | + RX_FLAG_DECRYPTED; + + if (unlikely(desc_err_code == WL1271_RX_DESC_MIC_FAIL)) { status->flag |= RX_FLAG_MMIC_ERROR; + wl1271_warning("Michael MIC error"); + } } } @@ -100,6 +103,25 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) if (unlikely(wl->state == WL1271_STATE_PLT)) return -EINVAL; + /* the data read starts with the descriptor */ + desc = (struct wl1271_rx_descriptor *) data; + + switch (desc->status & WL1271_RX_DESC_STATUS_MASK) { + /* discard corrupted packets */ + case WL1271_RX_DESC_DRIVER_RX_Q_FAIL: + case WL1271_RX_DESC_DECRYPT_FAIL: + wl1271_warning("corrupted packet in RX with status: 0x%x", + desc->status & WL1271_RX_DESC_STATUS_MASK); + return -EINVAL; + case WL1271_RX_DESC_SUCCESS: + case WL1271_RX_DESC_MIC_FAIL: + break; + default: + wl1271_error("invalid RX descriptor status: 0x%x", + desc->status & WL1271_RX_DESC_STATUS_MASK); + return -EINVAL; + } + skb = __dev_alloc_skb(length, GFP_KERNEL); if (!skb) { wl1271_error("Couldn't allocate RX frame"); @@ -109,9 +131,6 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) buf = skb_put(skb, length); memcpy(buf, data, length); - /* the data read starts with the descriptor */ - desc = (struct wl1271_rx_descriptor *) buf; - /* now we pull the descriptor out of the buffer */ skb_pull(skb, sizeof(*desc)); @@ -121,7 +140,8 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) wl1271_rx_status(wl, desc, IEEE80211_SKB_RXCB(skb), beacon); - wl1271_debug(DEBUG_RX, "rx skb 0x%p: %d B %s", skb, skb->len, + wl1271_debug(DEBUG_RX, "rx skb 0x%p: %d B %s", skb, + skb->len - desc->pad_len, beacon ? "beacon" : ""); skb_trim(skb, skb->len - desc->pad_len); diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c index 5d0544c..f37e5a3 100644 --- a/drivers/net/wireless/wl12xx/scan.c +++ b/drivers/net/wireless/wl12xx/scan.c @@ -320,3 +320,246 @@ int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, return 0; } + +static int +wl1271_scan_get_sched_scan_channels(struct wl1271 *wl, + struct cfg80211_sched_scan_request *req, + struct conn_scan_ch_params *channels, + u32 band, bool radar, bool passive, + int start) +{ + struct conf_sched_scan_settings *c = &wl->conf.sched_scan; + int i, j; + u32 flags; + + for (i = 0, j = start; + i < req->n_channels && j < MAX_CHANNELS_ALL_BANDS; + i++) { + flags = req->channels[i]->flags; + + if (!(flags & IEEE80211_CHAN_DISABLED) && + ((flags & IEEE80211_CHAN_PASSIVE_SCAN) == passive) && + ((flags & IEEE80211_CHAN_RADAR) == radar) && + (req->channels[i]->band == band)) { + wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ", + req->channels[i]->band, + req->channels[i]->center_freq); + wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X", + req->channels[i]->hw_value, + req->channels[i]->flags); + wl1271_debug(DEBUG_SCAN, "max_power %d", + req->channels[i]->max_power); + + if (flags & IEEE80211_CHAN_PASSIVE_SCAN) { + channels[j].passive_duration = + cpu_to_le16(c->dwell_time_passive); + } else { + channels[j].min_duration = + cpu_to_le16(c->min_dwell_time_active); + channels[j].max_duration = + cpu_to_le16(c->max_dwell_time_active); + } + channels[j].tx_power_att = req->channels[j]->max_power; + channels[j].channel = req->channels[i]->hw_value; + + j++; + } + } + + return j - start; +} + +static int +wl1271_scan_sched_scan_channels(struct wl1271 *wl, + struct cfg80211_sched_scan_request *req, + struct wl1271_cmd_sched_scan_config *cfg) +{ + int idx = 0; + + cfg->passive[0] = + wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, + IEEE80211_BAND_2GHZ, + false, true, idx); + idx += cfg->passive[0]; + + cfg->active[0] = + wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, + IEEE80211_BAND_2GHZ, + false, false, idx); + idx += cfg->active[0]; + + cfg->passive[1] = + wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, + IEEE80211_BAND_5GHZ, + false, true, idx); + idx += cfg->passive[1]; + + cfg->active[1] = + wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, + IEEE80211_BAND_5GHZ, + false, false, 14); + idx += cfg->active[1]; + + cfg->dfs = + wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, + IEEE80211_BAND_5GHZ, + true, false, idx); + idx += cfg->dfs; + + wl1271_debug(DEBUG_SCAN, " 2.4GHz: active %d passive %d", + cfg->active[0], cfg->passive[0]); + wl1271_debug(DEBUG_SCAN, " 5GHz: active %d passive %d", + cfg->active[1], cfg->passive[1]); + + return idx; +} + +int wl1271_scan_sched_scan_config(struct wl1271 *wl, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies) +{ + struct wl1271_cmd_sched_scan_config *cfg = NULL; + struct conf_sched_scan_settings *c = &wl->conf.sched_scan; + int i, total_channels, ret; + + wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config"); + + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) + return -ENOMEM; + + cfg->rssi_threshold = c->rssi_threshold; + cfg->snr_threshold = c->snr_threshold; + cfg->n_probe_reqs = c->num_probe_reqs; + /* cycles set to 0 it means infinite (until manually stopped) */ + cfg->cycles = 0; + /* report APs when at least 1 is found */ + cfg->report_after = 1; + /* don't stop scanning automatically when something is found */ + cfg->terminate = 0; + cfg->tag = WL1271_SCAN_DEFAULT_TAG; + /* don't filter on BSS type */ + cfg->bss_type = SCAN_BSS_TYPE_ANY; + /* currently NL80211 supports only a single interval */ + for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++) + cfg->intervals[i] = cpu_to_le32(req->interval); + + if (req->ssids[0].ssid_len && req->ssids[0].ssid) { + cfg->filter_type = SCAN_SSID_FILTER_SPECIFIC; + cfg->ssid_len = req->ssids[0].ssid_len; + memcpy(cfg->ssid, req->ssids[0].ssid, + req->ssids[0].ssid_len); + } else { + cfg->filter_type = SCAN_SSID_FILTER_ANY; + cfg->ssid_len = 0; + } + + total_channels = wl1271_scan_sched_scan_channels(wl, req, cfg); + if (total_channels == 0) { + wl1271_error("scan channel list is empty"); + ret = -EINVAL; + goto out; + } + + if (cfg->active[0]) { + ret = wl1271_cmd_build_probe_req(wl, req->ssids[0].ssid, + req->ssids[0].ssid_len, + ies->ie[IEEE80211_BAND_2GHZ], + ies->len[IEEE80211_BAND_2GHZ], + IEEE80211_BAND_2GHZ); + if (ret < 0) { + wl1271_error("2.4GHz PROBE request template failed"); + goto out; + } + } + + if (cfg->active[1]) { + ret = wl1271_cmd_build_probe_req(wl, req->ssids[0].ssid, + req->ssids[0].ssid_len, + ies->ie[IEEE80211_BAND_5GHZ], + ies->len[IEEE80211_BAND_5GHZ], + IEEE80211_BAND_5GHZ); + if (ret < 0) { + wl1271_error("5GHz PROBE request template failed"); + goto out; + } + } + + wl1271_dump(DEBUG_SCAN, "SCAN_CFG: ", cfg, sizeof(*cfg)); + + ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_CFG, cfg, + sizeof(*cfg), 0); + if (ret < 0) { + wl1271_error("SCAN configuration failed"); + goto out; + } +out: + kfree(cfg); + return ret; +} + +int wl1271_scan_sched_scan_start(struct wl1271 *wl) +{ + struct wl1271_cmd_sched_scan_start *start; + int ret = 0; + + wl1271_debug(DEBUG_CMD, "cmd periodic scan start"); + + if (wl->bss_type != BSS_TYPE_STA_BSS) + return -EOPNOTSUPP; + + if (!test_bit(WL1271_FLAG_IDLE, &wl->flags)) + return -EBUSY; + + start = kzalloc(sizeof(*start), GFP_KERNEL); + if (!start) + return -ENOMEM; + + start->tag = WL1271_SCAN_DEFAULT_TAG; + + ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start, + sizeof(*start), 0); + if (ret < 0) { + wl1271_error("failed to send scan start command"); + goto out_free; + } + +out_free: + kfree(start); + return ret; +} + +void wl1271_scan_sched_scan_results(struct wl1271 *wl) +{ + wl1271_debug(DEBUG_SCAN, "got periodic scan results"); + + ieee80211_sched_scan_results(wl->hw); +} + +void wl1271_scan_sched_scan_stop(struct wl1271 *wl) +{ + struct wl1271_cmd_sched_scan_stop *stop; + int ret = 0; + + wl1271_debug(DEBUG_CMD, "cmd periodic scan stop"); + + /* FIXME: what to do if alloc'ing to stop fails? */ + stop = kzalloc(sizeof(*stop), GFP_KERNEL); + if (!stop) { + wl1271_error("failed to alloc memory to send sched scan stop"); + return; + } + + stop->tag = WL1271_SCAN_DEFAULT_TAG; + + ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop, + sizeof(*stop), 0); + if (ret < 0) { + wl1271_error("failed to send sched scan stop command"); + goto out_free; + } + wl->sched_scanning = false; + +out_free: + kfree(stop); +} diff --git a/drivers/net/wireless/wl12xx/scan.h b/drivers/net/wireless/wl12xx/scan.h index 421a750..c833195 100644 --- a/drivers/net/wireless/wl12xx/scan.h +++ b/drivers/net/wireless/wl12xx/scan.h @@ -33,6 +33,12 @@ int wl1271_scan_build_probe_req(struct wl1271 *wl, const u8 *ie, size_t ie_len, u8 band); void wl1271_scan_stm(struct wl1271 *wl); void wl1271_scan_complete_work(struct work_struct *work); +int wl1271_scan_sched_scan_config(struct wl1271 *wl, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies); +int wl1271_scan_sched_scan_start(struct wl1271 *wl); +void wl1271_scan_sched_scan_stop(struct wl1271 *wl); +void wl1271_scan_sched_scan_results(struct wl1271 *wl); #define WL1271_SCAN_MAX_CHANNELS 24 #define WL1271_SCAN_DEFAULT_TAG 1 @@ -106,4 +112,112 @@ struct wl1271_cmd_trigger_scan_to { __le32 timeout; } __packed; +#define MAX_CHANNELS_ALL_BANDS 41 +#define SCAN_MAX_CYCLE_INTERVALS 16 +#define SCAN_MAX_BANDS 3 + +enum { + SCAN_CHANNEL_TYPE_2GHZ_PASSIVE, + SCAN_CHANNEL_TYPE_2GHZ_ACTIVE, + SCAN_CHANNEL_TYPE_5GHZ_PASSIVE, + SCAN_CHANNEL_TYPE_5GHZ_ACTIVE, + SCAN_CHANNEL_TYPE_5GHZ_DFS, +}; + +enum { + SCAN_SSID_FILTER_ANY = 0, + SCAN_SSID_FILTER_SPECIFIC = 1, + SCAN_SSID_FILTER_LIST = 2, + SCAN_SSID_FILTER_DISABLED = 3 +}; + +enum { + SCAN_BSS_TYPE_INDEPENDENT, + SCAN_BSS_TYPE_INFRASTRUCTURE, + SCAN_BSS_TYPE_ANY, +}; + +struct conn_scan_ch_params { + __le16 min_duration; + __le16 max_duration; + __le16 passive_duration; + + u8 channel; + u8 tx_power_att; + + /* bit 0: DFS channel; bit 1: DFS enabled */ + u8 flags; + + u8 padding[3]; +} __packed; + +struct wl1271_cmd_sched_scan_config { + struct wl1271_cmd_header header; + + __le32 intervals[SCAN_MAX_CYCLE_INTERVALS]; + + s8 rssi_threshold; /* for filtering (in dBm) */ + s8 snr_threshold; /* for filtering (in dB) */ + + u8 cycles; /* maximum number of scan cycles */ + u8 report_after; /* report when this number of results are received */ + u8 terminate; /* stop scanning after reporting */ + + u8 tag; + u8 bss_type; /* for filtering */ + u8 filter_type; + + u8 ssid_len; /* For SCAN_SSID_FILTER_SPECIFIC */ + u8 ssid[IW_ESSID_MAX_SIZE]; + + u8 n_probe_reqs; /* Number of probes requests per channel */ + + u8 passive[SCAN_MAX_BANDS]; + u8 active[SCAN_MAX_BANDS]; + + u8 dfs; + + u8 padding[3]; + + struct conn_scan_ch_params channels[MAX_CHANNELS_ALL_BANDS]; +} __packed; + + +#define SCHED_SCAN_MAX_SSIDS 8 + +enum { + SCAN_SSID_TYPE_PUBLIC = 0, + SCAN_SSID_TYPE_HIDDEN = 1, +}; + +struct wl1271_ssid { + u8 type; + u8 len; + u8 ssid[IW_ESSID_MAX_SIZE]; + /* u8 padding[2]; */ +} __packed; + +struct wl1271_cmd_sched_scan_ssid_list { + struct wl1271_cmd_header header; + + u8 n_ssids; + struct wl1271_ssid ssids[SCHED_SCAN_MAX_SSIDS]; + u8 padding[3]; +} __packed; + +struct wl1271_cmd_sched_scan_start { + struct wl1271_cmd_header header; + + u8 tag; + u8 padding[3]; +} __packed; + +struct wl1271_cmd_sched_scan_stop { + struct wl1271_cmd_header header; + + u8 tag; + u8 padding[3]; +} __packed; + + #endif /* __WL1271_SCAN_H__ */ diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index bcd4ad7..92d29a8 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -82,6 +82,16 @@ static irqreturn_t wl1271_hardirq(int irq, void *cookie) complete(wl->elp_compl); wl->elp_compl = NULL; } + + if (test_bit(WL1271_FLAG_SUSPENDED, &wl->flags)) { + /* don't enqueue a work right now. mark it as pending */ + set_bit(WL1271_FLAG_PENDING_WORK, &wl->flags); + wl1271_debug(DEBUG_IRQ, "should not enqueue work"); + disable_irq_nosync(wl->irq); + pm_wakeup_event(wl1271_sdio_wl_to_dev(wl), 0); + spin_unlock_irqrestore(&wl->wl_lock, flags); + return IRQ_HANDLED; + } spin_unlock_irqrestore(&wl->wl_lock, flags); return IRQ_WAKE_THREAD; @@ -221,6 +231,7 @@ static int __devinit wl1271_probe(struct sdio_func *func, const struct wl12xx_platform_data *wlan_data; struct wl1271 *wl; unsigned long irqflags; + mmc_pm_flag_t mmcflags; int ret; /* We are only able to handle the wlan function */ @@ -267,8 +278,18 @@ static int __devinit wl1271_probe(struct sdio_func *func, goto out_free; } + enable_irq_wake(wl->irq); + device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 1); + disable_irq(wl->irq); + /* if sdio can keep power while host is suspended, enable wow */ + mmcflags = sdio_get_host_pm_caps(func); + wl1271_debug(DEBUG_SDIO, "sdio PM caps = 0x%x", mmcflags); + + if (mmcflags & MMC_PM_KEEP_POWER) + hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY; + ret = wl1271_init_ieee80211(wl); if (ret) goto out_irq; @@ -303,6 +324,8 @@ static void __devexit wl1271_remove(struct sdio_func *func) pm_runtime_get_noresume(&func->dev); wl1271_unregister_hw(wl); + device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 0); + disable_irq_wake(wl->irq); free_irq(wl->irq, wl); wl1271_free_hw(wl); } @@ -311,11 +334,50 @@ static int wl1271_suspend(struct device *dev) { /* Tell MMC/SDIO core it's OK to power down the card * (if it isn't already), but not to remove it completely */ - return 0; + struct sdio_func *func = dev_to_sdio_func(dev); + struct wl1271 *wl = sdio_get_drvdata(func); + mmc_pm_flag_t sdio_flags; + int ret = 0; + + wl1271_debug(DEBUG_MAC80211, "wl1271 suspend. wow_enabled: %d", + wl->wow_enabled); + + /* check whether sdio should keep power */ + if (wl->wow_enabled) { + sdio_flags = sdio_get_host_pm_caps(func); + + if (!(sdio_flags & MMC_PM_KEEP_POWER)) { + wl1271_error("can't keep power while host " + "is suspended"); + ret = -EINVAL; + goto out; + } + + /* keep power while host suspended */ + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + if (ret) { + wl1271_error("error while trying to keep power"); + goto out; + } + + /* release host */ + sdio_release_host(func); + } +out: + return ret; } static int wl1271_resume(struct device *dev) { + struct sdio_func *func = dev_to_sdio_func(dev); + struct wl1271 *wl = sdio_get_drvdata(func); + + wl1271_debug(DEBUG_MAC80211, "wl1271 resume"); + if (wl->wow_enabled) { + /* claim back host */ + sdio_claim_host(func); + } + return 0; } diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 7a3339f..ca3ab1c 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -65,6 +65,9 @@ static int wl1271_alloc_tx_id(struct wl1271 *wl, struct sk_buff *skb) static void wl1271_free_tx_id(struct wl1271 *wl, int id) { if (__test_and_clear_bit(id, wl->tx_frames_map)) { + if (unlikely(wl->tx_frames_cnt == ACX_TX_DESCRIPTORS)) + clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags); + wl->tx_frames[id] = NULL; wl->tx_frames_cnt--; } @@ -630,7 +633,7 @@ void wl1271_tx_work(struct work_struct *work) wl1271_tx_work_locked(wl); - wl1271_ps_elp_wakeup(wl); + wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); } @@ -766,8 +769,8 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid) wl1271_handle_tx_low_watermark(wl); } -/* caller must hold wl->mutex */ -void wl1271_tx_reset(struct wl1271 *wl) +/* caller must hold wl->mutex and TX must be stopped */ +void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues) { int i; struct sk_buff *skb; @@ -803,8 +806,10 @@ void wl1271_tx_reset(struct wl1271 *wl) /* * Make sure the driver is at a consistent state, in case this * function is called from a context other than interface removal. + * This call will always wake the TX queues. */ - wl1271_handle_tx_low_watermark(wl); + if (reset_tx_queues) + wl1271_handle_tx_low_watermark(wl); for (i = 0; i < ACX_TX_DESCRIPTORS; i++) { if (wl->tx_frames[i] == NULL) diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h index fc7835c..832f925 100644 --- a/drivers/net/wireless/wl12xx/tx.h +++ b/drivers/net/wireless/wl12xx/tx.h @@ -185,7 +185,7 @@ static inline int wl1271_tx_get_queue(int queue) void wl1271_tx_work(struct work_struct *work); void wl1271_tx_work_locked(struct wl1271 *wl); void wl1271_tx_complete(struct wl1271 *wl); -void wl1271_tx_reset(struct wl1271 *wl); +void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues); void wl1271_tx_flush(struct wl1271 *wl); u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band); u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set); diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 7c521af..fbe8f46 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -172,6 +172,7 @@ extern u32 wl12xx_debug_level; #define WL1271_PS_STA_MAX_BLOCKS (2 * 9) #define WL1271_AP_BSS_INDEX 0 +#define WL1271_AP_DEF_INACTIV_SEC 300 #define WL1271_AP_DEF_BEACON_EXP 20 #define ACX_TX_DESCRIPTORS 32 @@ -345,17 +346,19 @@ enum wl12xx_flags { WL1271_FLAG_TX_QUEUE_STOPPED, WL1271_FLAG_TX_PENDING, WL1271_FLAG_IN_ELP, + WL1271_FLAG_ELP_REQUESTED, WL1271_FLAG_PSM, WL1271_FLAG_PSM_REQUESTED, WL1271_FLAG_IRQ_RUNNING, WL1271_FLAG_IDLE, - WL1271_FLAG_IDLE_REQUESTED, WL1271_FLAG_PSPOLL_FAILURE, WL1271_FLAG_STA_STATE_SENT, WL1271_FLAG_FW_TX_BUSY, WL1271_FLAG_AP_STARTED, WL1271_FLAG_IF_INITIALIZED, WL1271_FLAG_DUMMY_PACKET_PENDING, + WL1271_FLAG_SUSPENDED, + WL1271_FLAG_PENDING_WORK, }; struct wl1271_link { @@ -478,6 +481,8 @@ struct wl1271 { struct wl1271_scan scan; struct delayed_work scan_complete_work; + bool sched_scanning; + /* probe-req template for the current AP */ struct sk_buff *probereq; @@ -508,6 +513,7 @@ struct wl1271 { unsigned int rx_filter; struct completion *elp_compl; + struct completion *ps_compl; struct delayed_work elp_work; struct delayed_work pspoll_work; @@ -562,6 +568,12 @@ struct wl1271 { int tcxo_clock; /* + * wowlan trigger was configured during suspend. + * (currently, only "ANY" trigger is supported) + */ + bool wow_enabled; + + /* * AP-mode - links indexed by HLID. The global and broadcast links * are always active. */ |