diff options
Diffstat (limited to 'drivers/net/wireless/ti/wlcore')
24 files changed, 2290 insertions, 1895 deletions
diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c index ce108a7..c796543 100644 --- a/drivers/net/wireless/ti/wlcore/acx.c +++ b/drivers/net/wireless/ti/wlcore/acx.c @@ -1340,6 +1340,8 @@ out: kfree(acx); return ret; } +EXPORT_SYMBOL_GPL(wl1271_acx_set_ht_capabilities); + int wl1271_acx_set_ht_information(struct wl1271 *wl, struct wl12xx_vif *wlvif, @@ -1433,13 +1435,22 @@ int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, acx->win_size = wl->conf.ht.rx_ba_win_size; acx->ssn = ssn; - ret = wl1271_cmd_configure(wl, ACX_BA_SESSION_RX_SETUP, acx, - sizeof(*acx)); + ret = wlcore_cmd_configure_failsafe(wl, ACX_BA_SESSION_RX_SETUP, acx, + sizeof(*acx), + BIT(CMD_STATUS_NO_RX_BA_SESSION)); if (ret < 0) { wl1271_warning("acx ba receiver session failed: %d", ret); goto out; } + /* sometimes we can't start the session */ + if (ret == CMD_STATUS_NO_RX_BA_SESSION) { + wl1271_warning("no fw rx ba on tid %d", tid_index); + ret = -EBUSY; + goto out; + } + + ret = 0; out: kfree(acx); return ret; diff --git a/drivers/net/wireless/ti/wlcore/acx.h b/drivers/net/wireless/ti/wlcore/acx.h index d03215d..126536c 100644 --- a/drivers/net/wireless/ti/wlcore/acx.h +++ b/drivers/net/wireless/ti/wlcore/acx.h @@ -1025,7 +1025,6 @@ enum { ACX_CONFIG_HANGOVER = 0x0042, ACX_FEATURE_CFG = 0x0043, ACX_PROTECTION_CFG = 0x0044, - ACX_CHECKSUM_CONFIG = 0x0045, }; diff --git a/drivers/net/wireless/ti/wlcore/boot.c b/drivers/net/wireless/ti/wlcore/boot.c index 375ea57..b58ae5f 100644 --- a/drivers/net/wireless/ti/wlcore/boot.c +++ b/drivers/net/wireless/ti/wlcore/boot.c @@ -84,47 +84,57 @@ out: static int wlcore_validate_fw_ver(struct wl1271 *wl) { unsigned int *fw_ver = wl->chip.fw_ver; - unsigned int *min_ver = wl->min_fw_ver; + unsigned int *min_ver = (wl->fw_type == WL12XX_FW_TYPE_NORMAL) ? + wl->min_sr_fw_ver : wl->min_mr_fw_ver; + char min_fw_str[32] = ""; + int i; /* the chip must be exactly equal */ - if (min_ver[FW_VER_CHIP] != fw_ver[FW_VER_CHIP]) + if ((min_ver[FW_VER_CHIP] != WLCORE_FW_VER_IGNORE) && + (min_ver[FW_VER_CHIP] != fw_ver[FW_VER_CHIP])) goto fail; - /* always check the next digit if all previous ones are equal */ - - if (min_ver[FW_VER_IF_TYPE] < fw_ver[FW_VER_IF_TYPE]) - goto out; - else if (min_ver[FW_VER_IF_TYPE] > fw_ver[FW_VER_IF_TYPE]) + /* the firmware type must be equal */ + if ((min_ver[FW_VER_IF_TYPE] != WLCORE_FW_VER_IGNORE) && + (min_ver[FW_VER_IF_TYPE] != fw_ver[FW_VER_IF_TYPE])) goto fail; - if (min_ver[FW_VER_MAJOR] < fw_ver[FW_VER_MAJOR]) - goto out; - else if (min_ver[FW_VER_MAJOR] > fw_ver[FW_VER_MAJOR]) + /* the project number must be equal */ + if ((min_ver[FW_VER_SUBTYPE] != WLCORE_FW_VER_IGNORE) && + (min_ver[FW_VER_SUBTYPE] != fw_ver[FW_VER_SUBTYPE])) goto fail; - if (min_ver[FW_VER_SUBTYPE] < fw_ver[FW_VER_SUBTYPE]) - goto out; - else if (min_ver[FW_VER_SUBTYPE] > fw_ver[FW_VER_SUBTYPE]) + /* the API version must be greater or equal */ + if ((min_ver[FW_VER_MAJOR] != WLCORE_FW_VER_IGNORE) && + (min_ver[FW_VER_MAJOR] > fw_ver[FW_VER_MAJOR])) goto fail; - if (min_ver[FW_VER_MINOR] < fw_ver[FW_VER_MINOR]) - goto out; - else if (min_ver[FW_VER_MINOR] > fw_ver[FW_VER_MINOR]) + /* if the API version is equal... */ + if (((min_ver[FW_VER_MAJOR] == WLCORE_FW_VER_IGNORE) || + (min_ver[FW_VER_MAJOR] == fw_ver[FW_VER_MAJOR])) && + /* ...the minor must be greater or equal */ + ((min_ver[FW_VER_MINOR] != WLCORE_FW_VER_IGNORE) && + (min_ver[FW_VER_MINOR] > fw_ver[FW_VER_MINOR]))) goto fail; -out: return 0; fail: - wl1271_error("Your WiFi FW version (%u.%u.%u.%u.%u) is outdated.\n" - "Please use at least FW %u.%u.%u.%u.%u.\n" - "You can get more information at:\n" - "http://wireless.kernel.org/en/users/Drivers/wl12xx", + for (i = 0; i < NUM_FW_VER; i++) + if (min_ver[i] == WLCORE_FW_VER_IGNORE) + snprintf(min_fw_str, sizeof(min_fw_str), + "%s*.", min_fw_str); + else + snprintf(min_fw_str, sizeof(min_fw_str), + "%s%u.", min_fw_str, min_ver[i]); + + wl1271_error("Your WiFi FW version (%u.%u.%u.%u.%u) is invalid.\n" + "Please use at least FW %s\n" + "You can get the latest firmwares at:\n" + "git://github.com/TI-OpenLink/firmwares.git", fw_ver[FW_VER_CHIP], fw_ver[FW_VER_IF_TYPE], fw_ver[FW_VER_MAJOR], fw_ver[FW_VER_SUBTYPE], - fw_ver[FW_VER_MINOR], min_ver[FW_VER_CHIP], - min_ver[FW_VER_IF_TYPE], min_ver[FW_VER_MAJOR], - min_ver[FW_VER_SUBTYPE], min_ver[FW_VER_MINOR]); + fw_ver[FW_VER_MINOR], min_fw_str); return -EINVAL; } @@ -491,7 +501,7 @@ int wlcore_boot_run_firmware(struct wl1271 *wl) if (ret < 0) return ret; - wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox); + wl->mbox_ptr[1] = wl->mbox_ptr[0] + wl->mbox_size; wl1271_debug(DEBUG_MAILBOX, "MBOX ptrs: 0x%x 0x%x", wl->mbox_ptr[0], wl->mbox_ptr[1]); @@ -508,23 +518,6 @@ int wlcore_boot_run_firmware(struct wl1271 *wl) */ /* unmask required mbox events */ - wl->event_mask = BSS_LOSE_EVENT_ID | - REGAINED_BSS_EVENT_ID | - SCAN_COMPLETE_EVENT_ID | - ROLE_STOP_COMPLETE_EVENT_ID | - RSSI_SNR_TRIGGER_0_EVENT_ID | - PSPOLL_DELIVERY_FAILURE_EVENT_ID | - SOFT_GEMINI_SENSE_EVENT_ID | - PERIODIC_SCAN_REPORT_EVENT_ID | - PERIODIC_SCAN_COMPLETE_EVENT_ID | - DUMMY_PACKET_EVENT_ID | - PEER_REMOVE_COMPLETE_EVENT_ID | - BA_SESSION_RX_CONSTRAINT_EVENT_ID | - REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID | - INACTIVE_STA_EVENT_ID | - MAX_TX_RETRY_EVENT_ID | - CHANNEL_SWITCH_COMPLETE_EVENT_ID; - ret = wl1271_event_unmask(wl); if (ret < 0) { wl1271_error("EVENT mask setting failed"); diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 27f83f7..1201aca 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -48,14 +48,15 @@ * @id: command id * @buf: buffer containing the command, must work with dma * @len: length of the buffer + * return the cmd status code on success. */ -int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, - size_t res_len) +static int __wlcore_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; + int ret; u16 status; u16 poll_count = 0; @@ -71,7 +72,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, ret = wlcore_write(wl, wl->cmd_box_addr, buf, len, false); if (ret < 0) - goto fail; + return ret; /* * TODO: we just need this because one bit is in a different @@ -79,19 +80,18 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, */ ret = wl->ops->trigger_cmd(wl, wl->cmd_box_addr, buf, len); if (ret < 0) - goto fail; + return ret; timeout = jiffies + msecs_to_jiffies(WL1271_COMMAND_TIMEOUT); ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &intr); if (ret < 0) - goto fail; + return ret; while (!(intr & WL1271_ACX_INTR_CMD_COMPLETE)) { if (time_after(jiffies, timeout)) { wl1271_error("command complete timeout"); - ret = -ETIMEDOUT; - goto fail; + return -ETIMEDOUT; } poll_count++; @@ -102,7 +102,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &intr); if (ret < 0) - goto fail; + return ret; } /* read back the status code of the command */ @@ -111,33 +111,66 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, ret = wlcore_read(wl, wl->cmd_box_addr, cmd, res_len, false); if (ret < 0) - goto fail; + return ret; status = le16_to_cpu(cmd->status); - if (status != CMD_STATUS_SUCCESS) { - wl1271_error("command execute failure %d", status); - ret = -EIO; - goto fail; - } ret = wlcore_write_reg(wl, REG_INTERRUPT_ACK, WL1271_ACX_INTR_CMD_COMPLETE); if (ret < 0) + return ret; + + return status; +} + +/* + * send command to fw and return cmd status on success + * valid_rets contains a bitmap of allowed error codes + */ +int wlcore_cmd_send_failsafe(struct wl1271 *wl, u16 id, void *buf, size_t len, + size_t res_len, unsigned long valid_rets) +{ + int ret = __wlcore_cmd_send(wl, id, buf, len, res_len); + + if (ret < 0) goto fail; - return 0; + /* success is always a valid status */ + valid_rets |= BIT(CMD_STATUS_SUCCESS); + if (ret >= MAX_COMMAND_STATUS || + !test_bit(ret, &valid_rets)) { + wl1271_error("command execute failure %d", ret); + ret = -EIO; + goto fail; + } + return ret; fail: wl12xx_queue_recovery_work(wl); return ret; } +EXPORT_SYMBOL_GPL(wl1271_cmd_send); + +/* + * wrapper for wlcore_cmd_send that accept only CMD_STATUS_SUCCESS + * return 0 on success. + */ +int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, + size_t res_len) +{ + int ret = wlcore_cmd_send_failsafe(wl, id, buf, len, res_len, 0); + + if (ret < 0) + return ret; + return 0; +} /* * Poll the mailbox event field until any of the bits in the mask is set or a * timeout occurs (WL1271_EVENT_TIMEOUT in msecs) */ -static int wl1271_cmd_wait_for_event_or_timeout(struct wl1271 *wl, - u32 mask, bool *timeout) +int wlcore_cmd_wait_for_event_or_timeout(struct wl1271 *wl, + u32 mask, bool *timeout) { u32 *events_vector; u32 event; @@ -187,20 +220,7 @@ out: kfree(events_vector); return ret; } - -static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask) -{ - int ret; - bool timeout = false; - - ret = wl1271_cmd_wait_for_event_or_timeout(wl, mask, &timeout); - if (ret != 0 || timeout) { - wl12xx_queue_recovery_work(wl); - return ret; - } - - return 0; -} +EXPORT_SYMBOL_GPL(wlcore_cmd_wait_for_event_or_timeout); int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 *addr, u8 role_type, u8 *role_id) @@ -278,6 +298,16 @@ out: return ret; } +static int wlcore_get_new_session_id(struct wl1271 *wl, u8 hlid) +{ + if (wl->session_ids[hlid] >= SESSION_COUNTER_MAX) + wl->session_ids[hlid] = 0; + + wl->session_ids[hlid]++; + + return wl->session_ids[hlid]; +} + int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) { unsigned long flags; @@ -285,12 +315,21 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) if (link >= WL12XX_MAX_LINKS) return -EBUSY; + wl->session_ids[link] = wlcore_get_new_session_id(wl, link); + /* these bits are used by op_tx */ spin_lock_irqsave(&wl->wl_lock, flags); __set_bit(link, wl->links_map); __set_bit(link, wlvif->links_map); spin_unlock_irqrestore(&wl->wl_lock, flags); + + /* take the last "freed packets" value from the current FW status */ + wl->links[link].prev_freed_pkts = + wl->fw_status_2->counters.tx_lnk_free_pkts[link]; + wl->links[link].wlvif = wlvif; *hlid = link; + + wl->active_link_count++; return 0; } @@ -307,24 +346,21 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) __clear_bit(*hlid, wlvif->links_map); spin_unlock_irqrestore(&wl->wl_lock, flags); + wl->links[*hlid].allocated_pkts = 0; + wl->links[*hlid].prev_freed_pkts = 0; + wl->links[*hlid].ba_bitmap = 0; + memset(wl->links[*hlid].addr, 0, ETH_ALEN); + /* * At this point op_tx() will not add more packets to the queues. We * can purge them. */ wl1271_tx_reset_link_queues(wl, *hlid); + wl->links[*hlid].wlvif = NULL; *hlid = WL12XX_INVALID_LINK_ID; -} - -static int wl12xx_get_new_session_id(struct wl1271 *wl, - struct wl12xx_vif *wlvif) -{ - if (wlvif->session_counter >= SESSION_COUNTER_MAX) - wlvif->session_counter = 0; - - wlvif->session_counter++; - - return wlvif->session_counter; + wl->active_link_count--; + WARN_ON_ONCE(wl->active_link_count < 0); } static u8 wlcore_get_native_channel_type(u8 nl_channel_type) @@ -345,7 +381,9 @@ static u8 wlcore_get_native_channel_type(u8 nl_channel_type) } static int wl12xx_cmd_role_start_dev(struct wl1271 *wl, - struct wl12xx_vif *wlvif) + struct wl12xx_vif *wlvif, + enum ieee80211_band band, + int channel) { struct wl12xx_cmd_role_start *cmd; int ret; @@ -359,9 +397,9 @@ static int wl12xx_cmd_role_start_dev(struct wl1271 *wl, wl1271_debug(DEBUG_CMD, "cmd role start dev %d", wlvif->dev_role_id); cmd->role_id = wlvif->dev_role_id; - if (wlvif->band == IEEE80211_BAND_5GHZ) + if (band == IEEE80211_BAND_5GHZ) cmd->band = WLCORE_BAND_5GHZ; - cmd->channel = wlvif->channel; + cmd->channel = channel; if (wlvif->dev_hlid == WL12XX_INVALID_LINK_ID) { ret = wl12xx_allocate_link(wl, wlvif, &wlvif->dev_hlid); @@ -369,7 +407,7 @@ static int wl12xx_cmd_role_start_dev(struct wl1271 *wl, goto out_free; } cmd->device.hlid = wlvif->dev_hlid; - cmd->device.session = wl12xx_get_new_session_id(wl, wlvif); + cmd->device.session = wl->session_ids[wlvif->dev_hlid]; wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d", cmd->role_id, cmd->device.hlid, cmd->device.session); @@ -420,12 +458,6 @@ static int wl12xx_cmd_role_stop_dev(struct wl1271 *wl, goto out_free; } - ret = wl1271_cmd_wait_for_event(wl, ROLE_STOP_COMPLETE_EVENT_ID); - if (ret < 0) { - wl1271_error("cmd role stop dev event completion error"); - goto out_free; - } - wl12xx_free_link(wl, wlvif, &wlvif->dev_hlid); out_free: @@ -439,6 +471,7 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); struct wl12xx_cmd_role_start *cmd; + u32 supported_rates; int ret; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); @@ -459,7 +492,14 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif) cmd->sta.ssid_len = wlvif->ssid_len; memcpy(cmd->sta.ssid, wlvif->ssid, wlvif->ssid_len); memcpy(cmd->sta.bssid, vif->bss_conf.bssid, ETH_ALEN); - cmd->sta.local_rates = cpu_to_le32(wlvif->rate_set); + + supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES | + wlcore_hw_sta_get_ap_rate_mask(wl, wlvif); + if (wlvif->p2p) + supported_rates &= ~CONF_TX_CCK_RATES; + + cmd->sta.local_rates = cpu_to_le32(supported_rates); + cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type); if (wlvif->sta.hlid == WL12XX_INVALID_LINK_ID) { @@ -468,7 +508,11 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif) goto out_free; } cmd->sta.hlid = wlvif->sta.hlid; - cmd->sta.session = wl12xx_get_new_session_id(wl, wlvif); + cmd->sta.session = wl->session_ids[wlvif->sta.hlid]; + /* + * We don't have the correct remote rates in this stage. the rates + * will be reconfigured later, after authorization. + */ cmd->sta.remote_rates = cpu_to_le32(wlvif->rate_set); wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d " @@ -482,6 +526,7 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif) goto err_hlid; } + wlvif->sta.role_chan_type = wlvif->channel_type; goto out_free; err_hlid: @@ -500,7 +545,6 @@ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct wl12xx_cmd_role_stop *cmd; int ret; - bool timeout = false; if (WARN_ON(wlvif->sta.hlid == WL12XX_INVALID_LINK_ID)) return -EINVAL; @@ -523,17 +567,6 @@ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif) goto out_free; } - /* - * Sometimes the firmware doesn't send this event, so we just - * time out without failing. Queue recovery for other - * failures. - */ - ret = wl1271_cmd_wait_for_event_or_timeout(wl, - ROLE_STOP_COMPLETE_EVENT_ID, - &timeout); - if (ret) - wl12xx_queue_recovery_work(wl); - wl12xx_free_link(wl, wlvif, &wlvif->sta.hlid); out_free: @@ -579,12 +612,15 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif) cmd->ap.bss_index = WL1271_AP_BSS_INDEX; cmd->ap.global_hlid = wlvif->ap.global_hlid; cmd->ap.broadcast_hlid = wlvif->ap.bcast_hlid; + cmd->ap.global_session_id = wl->session_ids[wlvif->ap.global_hlid]; + cmd->ap.bcast_session_id = wl->session_ids[wlvif->ap.bcast_hlid]; cmd->ap.basic_rate_set = cpu_to_le32(wlvif->basic_rate_set); cmd->ap.beacon_interval = cpu_to_le16(wlvif->beacon_int); cmd->ap.dtim_interval = bss_conf->dtim_period; cmd->ap.beacon_expiry = WL1271_AP_DEF_BEACON_EXP; /* FIXME: Change when adding DFS */ cmd->ap.reset_tsf = 1; /* By default reset AP TSF */ + cmd->ap.wmm = wlvif->wmm_enabled; cmd->channel = wlvif->channel; cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type); @@ -599,8 +635,10 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif) memcpy(cmd->ap.ssid, bss_conf->ssid, bss_conf->ssid_len); } - supported_rates = CONF_TX_AP_ENABLED_RATES | CONF_TX_MCS_RATES | + supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES | wlcore_hw_ap_get_mimo_wide_rate_mask(wl, wlvif); + if (wlvif->p2p) + supported_rates &= ~CONF_TX_CCK_RATES; wl1271_debug(DEBUG_CMD, "cmd role start ap with supported_rates 0x%08x", supported_rates); @@ -799,8 +837,11 @@ int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len) * @id: acx id * @buf: buffer containing acx, including all headers, must work with dma * @len: length of buf + * @valid_rets: bitmap of valid cmd status codes (i.e. return values). + * return the cmd status on success. */ -int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len) +int wlcore_cmd_configure_failsafe(struct wl1271 *wl, u16 id, void *buf, + size_t len, unsigned long valid_rets) { struct acx_header *acx = buf; int ret; @@ -812,12 +853,26 @@ int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len) /* payload length, does not include any headers */ acx->len = cpu_to_le16(len - sizeof(*acx)); - ret = wl1271_cmd_send(wl, CMD_CONFIGURE, acx, len, 0); + ret = wlcore_cmd_send_failsafe(wl, CMD_CONFIGURE, acx, len, 0, + valid_rets); if (ret < 0) { wl1271_warning("CONFIGURE command NOK"); return ret; } + return ret; +} + +/* + * wrapper for wlcore_cmd_configure that accepts only success status. + * return 0 on success + */ +int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len) +{ + int ret = wlcore_cmd_configure_failsafe(wl, id, buf, len, 0); + + if (ret < 0) + return ret; return 0; } EXPORT_SYMBOL_GPL(wl1271_cmd_configure); @@ -1034,8 +1089,8 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct sk_buff *skb; int ret; u32 rate; - u16 template_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4; - u16 template_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5; + u16 template_id_2_4 = wl->scan_templ_id_2_4; + u16 template_id_5 = wl->scan_templ_id_5; skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len, ie_len); @@ -1048,10 +1103,10 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len); - if (!sched_scan && + if (sched_scan && (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL)) { - template_id_2_4 = CMD_TEMPL_APP_PROBE_REQ_2_4; - template_id_5 = CMD_TEMPL_APP_PROBE_REQ_5; + template_id_2_4 = wl->sched_scan_templ_id_2_4; + template_id_5 = wl->sched_scan_templ_id_5; } rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]); @@ -1068,6 +1123,7 @@ out: dev_kfree_skb(skb); return ret; } +EXPORT_SYMBOL_GPL(wl12xx_cmd_build_probe_req); struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, @@ -1379,7 +1435,8 @@ out: return ret; } -int wl12xx_cmd_set_peer_state(struct wl1271 *wl, u8 hlid) +int wl12xx_cmd_set_peer_state(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 hlid) { struct wl12xx_cmd_set_peer_state *cmd; int ret = 0; @@ -1395,6 +1452,10 @@ int wl12xx_cmd_set_peer_state(struct wl1271 *wl, u8 hlid) cmd->hlid = hlid; cmd->state = WL1271_CMD_STA_STATE_CONNECTED; + /* wmm param is valid only for station role */ + if (wlvif->bss_type == BSS_TYPE_STA_BSS) + cmd->wmm = wlvif->wmm_enabled; + ret = wl1271_cmd_send(wl, CMD_SET_PEER_STATE, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("failed to send set peer state command"); @@ -1429,6 +1490,7 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif, cmd->hlid = hlid; cmd->sp_len = sta->max_sp; cmd->wmm = sta->wme ? 1 : 0; + cmd->session_id = wl->session_ids[hlid]; for (i = 0; i < NUM_ACCESS_CATEGORIES_COPY; i++) if (sta->wme && (sta->uapsd_queues & BIT(i))) @@ -1490,9 +1552,10 @@ int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid) goto out_free; } - ret = wl1271_cmd_wait_for_event_or_timeout(wl, - PEER_REMOVE_COMPLETE_EVENT_ID, - &timeout); + ret = wl->ops->wait_for_event(wl, + WLCORE_EVENT_PEER_REMOVE_COMPLETE, + &timeout); + /* * We are ok with a timeout here. The event is sometimes not sent * due to a firmware bug. In case of another error (like SDIO timeout) @@ -1508,6 +1571,131 @@ out: return ret; } +static int wlcore_get_reg_conf_ch_idx(enum ieee80211_band band, u16 ch) +{ + int idx = -1; + + switch (band) { + case IEEE80211_BAND_5GHZ: + if (ch >= 8 && ch <= 16) + idx = ((ch-8)/4 + 18); + else if (ch >= 34 && ch <= 64) + idx = ((ch-34)/2 + 3 + 18); + else if (ch >= 100 && ch <= 140) + idx = ((ch-100)/4 + 15 + 18); + else if (ch >= 149 && ch <= 165) + idx = ((ch-149)/4 + 26 + 18); + else + idx = -1; + break; + case IEEE80211_BAND_2GHZ: + if (ch >= 1 && ch <= 14) + idx = ch - 1; + else + idx = -1; + break; + default: + wl1271_error("get reg conf ch idx - unknown band: %d", + (int)band); + } + + return idx; +} + +void wlcore_set_pending_regdomain_ch(struct wl1271 *wl, u16 channel, + enum ieee80211_band band) +{ + int ch_bit_idx = 0; + + if (!(wl->quirks & WLCORE_QUIRK_REGDOMAIN_CONF)) + return; + + ch_bit_idx = wlcore_get_reg_conf_ch_idx(band, channel); + + if (ch_bit_idx > 0 && ch_bit_idx <= WL1271_MAX_CHANNELS) + set_bit(ch_bit_idx, (long *)wl->reg_ch_conf_pending); +} + +int wlcore_cmd_regdomain_config_locked(struct wl1271 *wl) +{ + struct wl12xx_cmd_regdomain_dfs_config *cmd = NULL; + int ret = 0, i, b, ch_bit_idx; + struct ieee80211_channel *channel; + u32 tmp_ch_bitmap[2]; + u16 ch; + struct wiphy *wiphy = wl->hw->wiphy; + struct ieee80211_supported_band *band; + bool timeout = false; + + if (!(wl->quirks & WLCORE_QUIRK_REGDOMAIN_CONF)) + return 0; + + wl1271_debug(DEBUG_CMD, "cmd reg domain config"); + + memset(tmp_ch_bitmap, 0, sizeof(tmp_ch_bitmap)); + + for (b = IEEE80211_BAND_2GHZ; b <= IEEE80211_BAND_5GHZ; b++) { + band = wiphy->bands[b]; + for (i = 0; i < band->n_channels; i++) { + channel = &band->channels[i]; + ch = channel->hw_value; + + if (channel->flags & (IEEE80211_CHAN_DISABLED | + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_PASSIVE_SCAN)) + continue; + + ch_bit_idx = wlcore_get_reg_conf_ch_idx(b, ch); + if (ch_bit_idx < 0) + continue; + + set_bit(ch_bit_idx, (long *)tmp_ch_bitmap); + } + } + + tmp_ch_bitmap[0] |= wl->reg_ch_conf_pending[0]; + tmp_ch_bitmap[1] |= wl->reg_ch_conf_pending[1]; + + if (!memcmp(tmp_ch_bitmap, wl->reg_ch_conf_last, sizeof(tmp_ch_bitmap))) + goto out; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + cmd->ch_bit_map1 = cpu_to_le32(tmp_ch_bitmap[0]); + cmd->ch_bit_map2 = cpu_to_le32(tmp_ch_bitmap[1]); + + wl1271_debug(DEBUG_CMD, + "cmd reg domain bitmap1: 0x%08x, bitmap2: 0x%08x", + cmd->ch_bit_map1, cmd->ch_bit_map2); + + ret = wl1271_cmd_send(wl, CMD_DFS_CHANNEL_CONFIG, cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("failed to send reg domain dfs config"); + goto out; + } + + ret = wl->ops->wait_for_event(wl, + WLCORE_EVENT_DFS_CONFIG_COMPLETE, + &timeout); + if (ret < 0 || timeout) { + wl1271_error("reg domain conf %serror", + timeout ? "completion " : ""); + ret = timeout ? -ETIMEDOUT : ret; + goto out; + } + + memcpy(wl->reg_ch_conf_last, tmp_ch_bitmap, sizeof(tmp_ch_bitmap)); + memset(wl->reg_ch_conf_pending, 0, sizeof(wl->reg_ch_conf_pending)); + +out: + kfree(cmd); + return ret; +} + int wl12xx_cmd_config_fwlog(struct wl1271 *wl) { struct wl12xx_cmd_config_fwlog *cmd; @@ -1593,12 +1781,12 @@ out: } static int wl12xx_cmd_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, - u8 role_id) + u8 role_id, enum ieee80211_band band, u8 channel) { struct wl12xx_cmd_roc *cmd; int ret = 0; - wl1271_debug(DEBUG_CMD, "cmd roc %d (%d)", wlvif->channel, role_id); + wl1271_debug(DEBUG_CMD, "cmd roc %d (%d)", channel, role_id); if (WARN_ON(role_id == WL12XX_INVALID_ROLE_ID)) return -EINVAL; @@ -1610,8 +1798,8 @@ static int wl12xx_cmd_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, } cmd->role_id = role_id; - cmd->channel = wlvif->channel; - switch (wlvif->band) { + cmd->channel = channel; + switch (band) { case IEEE80211_BAND_2GHZ: cmd->band = WLCORE_BAND_2_4GHZ; break; @@ -1666,30 +1854,18 @@ out: return ret; } -int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id) +int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id, + enum ieee80211_band band, u8 channel) { int ret = 0; - bool is_first_roc; if (WARN_ON(test_bit(role_id, wl->roc_map))) return 0; - is_first_roc = (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) >= - WL12XX_MAX_ROLES); - - ret = wl12xx_cmd_roc(wl, wlvif, role_id); + ret = wl12xx_cmd_roc(wl, wlvif, role_id, band, channel); if (ret < 0) goto out; - if (is_first_roc) { - ret = wl1271_cmd_wait_for_event(wl, - REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID); - if (ret < 0) { - wl1271_error("cmd roc event completion error"); - goto out; - } - } - __set_bit(role_id, wl->roc_map); out: return ret; @@ -1719,43 +1895,7 @@ out: return ret; } -int wl12xx_cmd_channel_switch(struct wl1271 *wl, - struct wl12xx_vif *wlvif, - struct ieee80211_channel_switch *ch_switch) -{ - struct wl12xx_cmd_channel_switch *cmd; - int ret; - - wl1271_debug(DEBUG_ACX, "cmd channel switch"); - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (!cmd) { - ret = -ENOMEM; - goto out; - } - - cmd->role_id = wlvif->role_id; - cmd->channel = ch_switch->channel->hw_value; - cmd->switch_time = ch_switch->count; - cmd->stop_tx = ch_switch->block_tx; - - /* FIXME: control from mac80211 in the future */ - cmd->post_switch_tx_disable = 0; /* Enable TX on the target channel */ - - ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0); - if (ret < 0) { - wl1271_error("failed to send channel switch command"); - goto out_free; - } - -out_free: - kfree(cmd); - -out: - return ret; -} - -int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl) +int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct wl12xx_cmd_stop_channel_switch *cmd; int ret; @@ -1768,6 +1908,8 @@ int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl) goto out; } + cmd->role_id = wlvif->role_id; + ret = wl1271_cmd_send(wl, CMD_STOP_CHANNEL_SWICTH, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("failed to stop channel switch command"); @@ -1782,7 +1924,8 @@ out: } /* start dev role and roc on its channel */ -int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif) +int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif, + enum ieee80211_band band, int channel) { int ret; @@ -1797,11 +1940,11 @@ int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif) if (ret < 0) goto out; - ret = wl12xx_cmd_role_start_dev(wl, wlvif); + ret = wl12xx_cmd_role_start_dev(wl, wlvif, band, channel); if (ret < 0) goto out_disable; - ret = wl12xx_roc(wl, wlvif, wlvif->dev_role_id); + ret = wl12xx_roc(wl, wlvif, wlvif->dev_role_id, band, channel); if (ret < 0) goto out_stop; diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h index 2409f3d..fd34123 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.h +++ b/drivers/net/wireless/ti/wlcore/cmd.h @@ -31,6 +31,8 @@ struct acx_header; int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, size_t res_len); +int wlcore_cmd_send_failsafe(struct wl1271 *wl, u16 id, void *buf, size_t len, + size_t res_len, unsigned long valid_rets); int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 *addr, u8 role_type, u8 *role_id); int wl12xx_cmd_role_disable(struct wl1271 *wl, u8 *role_id); @@ -39,11 +41,14 @@ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_cmd_role_stop_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_cmd_role_start_ibss(struct wl1271 *wl, struct wl12xx_vif *wlvif); -int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif); +int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif, + enum ieee80211_band band, int channel); int wl12xx_stop_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif); 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); +int wlcore_cmd_configure_failsafe(struct wl1271 *wl, u16 id, void *buf, + size_t len, unsigned long valid_rets); int wl1271_cmd_data_path(struct wl1271 *wl, bool enable); int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 ps_mode, u16 auto_ps_timeout); @@ -75,22 +80,30 @@ int wl1271_cmd_set_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, u16 action, u8 id, u8 key_type, u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32, u16 tx_seq_16); -int wl12xx_cmd_set_peer_state(struct wl1271 *wl, u8 hlid); -int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id); +int wl12xx_cmd_set_peer_state(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 hlid); +int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id, + enum ieee80211_band band, u8 channel); int wl12xx_croc(struct wl1271 *wl, u8 role_id); int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_sta *sta, u8 hlid); int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid); +void wlcore_set_pending_regdomain_ch(struct wl1271 *wl, u16 channel, + enum ieee80211_band band); +int wlcore_cmd_regdomain_config_locked(struct wl1271 *wl); int wl12xx_cmd_config_fwlog(struct wl1271 *wl); int wl12xx_cmd_start_fwlog(struct wl1271 *wl); int wl12xx_cmd_stop_fwlog(struct wl1271 *wl); int wl12xx_cmd_channel_switch(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_channel_switch *ch_switch); -int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl); +int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl, + struct wl12xx_vif *wlvif); int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid); void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid); +int wlcore_cmd_wait_for_event_or_timeout(struct wl1271 *wl, + u32 mask, bool *timeout); enum wl1271_commands { CMD_INTERROGATE = 1, /* use this to read information elements */ @@ -149,8 +162,11 @@ enum wl1271_commands { CMD_WFD_START_DISCOVERY = 45, CMD_WFD_STOP_DISCOVERY = 46, CMD_WFD_ATTRIBUTE_CONFIG = 47, - CMD_NOP = 48, - CMD_LAST_COMMAND, + CMD_GENERIC_CFG = 48, + CMD_NOP = 49, + + /* start of 18xx specific commands */ + CMD_DFS_CHANNEL_CONFIG = 60, MAX_COMMAND_ID = 0xFFFF, }; @@ -167,8 +183,8 @@ enum cmd_templ { CMD_TEMPL_PS_POLL, CMD_TEMPL_KLV, CMD_TEMPL_DISCONNECT, - CMD_TEMPL_APP_PROBE_REQ_2_4, - CMD_TEMPL_APP_PROBE_REQ_5, + CMD_TEMPL_APP_PROBE_REQ_2_4_LEGACY, + CMD_TEMPL_APP_PROBE_REQ_5_LEGACY, CMD_TEMPL_BAR, /* for firmware internal use only */ CMD_TEMPL_CTS, /* * For CTS-to-self (FastCTS) mechanism @@ -179,6 +195,8 @@ enum cmd_templ { CMD_TEMPL_DEAUTH_AP, CMD_TEMPL_TEMPORARY, CMD_TEMPL_LINK_MEASUREMENT_REPORT, + CMD_TEMPL_PROBE_REQ_2_4_PERIODIC, + CMD_TEMPL_PROBE_REQ_5_PERIODIC, CMD_TEMPL_MAX = 0xff }; @@ -220,7 +238,8 @@ enum { CMD_STATUS_FW_RESET = 22, /* Driver internal use.*/ CMD_STATUS_TEMPLATE_OOM = 23, CMD_STATUS_NO_RX_BA_SESSION = 24, - MAX_COMMAND_STATUS = 0xff + + MAX_COMMAND_STATUS }; #define CMDMBOX_HEADER_LEN 4 @@ -345,7 +364,15 @@ struct wl12xx_cmd_role_start { u8 reset_tsf; - u8 padding_1[4]; + /* + * ap supports wmm (note that there is additional + * per-sta wmm configuration) + */ + u8 wmm; + + u8 bcast_session_id; + u8 global_session_id; + u8 padding_1[1]; } __packed ap; }; } __packed; @@ -515,7 +542,14 @@ struct wl12xx_cmd_set_peer_state { u8 hlid; u8 state; - u8 padding[2]; + + /* + * wmm is relevant for sta role only. + * ap role configures the per-sta wmm params in + * the add_peer command. + */ + u8 wmm; + u8 padding[1]; } __packed; struct wl12xx_cmd_roc { @@ -558,7 +592,7 @@ struct wl12xx_cmd_add_peer { u8 bss_index; u8 sp_len; u8 wmm; - u8 padding1; + u8 session_id; } __packed; struct wl12xx_cmd_remove_peer { @@ -597,6 +631,13 @@ enum wl12xx_fwlogger_output { WL12XX_FWLOG_OUTPUT_HOST, }; +struct wl12xx_cmd_regdomain_dfs_config { + struct wl1271_cmd_header header; + + __le32 ch_bit_map1; + __le32 ch_bit_map2; +} __packed; + struct wl12xx_cmd_config_fwlog { struct wl1271_cmd_header header; @@ -626,27 +667,13 @@ struct wl12xx_cmd_stop_fwlog { struct wl1271_cmd_header header; } __packed; -struct wl12xx_cmd_channel_switch { +struct wl12xx_cmd_stop_channel_switch { struct wl1271_cmd_header header; u8 role_id; - - /* The new serving channel */ - u8 channel; - /* Relative time of the serving channel switch in TBTT units */ - u8 switch_time; - /* Stop the role TX, should expect it after radar detection */ - u8 stop_tx; - /* The target channel tx status 1-stopped 0-open*/ - u8 post_switch_tx_disable; - u8 padding[3]; } __packed; -struct wl12xx_cmd_stop_channel_switch { - struct wl1271_cmd_header header; -} __packed; - /* Used to check radio status after calibration */ #define MAX_TLV_LENGTH 500 #define TEST_CMD_P2G_CAL 2 /* TX BiP */ diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h index 9e40760..2b96ff8 100644 --- a/drivers/net/wireless/ti/wlcore/conf.h +++ b/drivers/net/wireless/ti/wlcore/conf.h @@ -57,20 +57,49 @@ enum { }; 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, + CONF_HW_RATE_INDEX_1MBPS = 0, + CONF_HW_RATE_INDEX_2MBPS = 1, + CONF_HW_RATE_INDEX_5_5MBPS = 2, + CONF_HW_RATE_INDEX_11MBPS = 3, + CONF_HW_RATE_INDEX_6MBPS = 4, + CONF_HW_RATE_INDEX_9MBPS = 5, + CONF_HW_RATE_INDEX_12MBPS = 6, + CONF_HW_RATE_INDEX_18MBPS = 7, + CONF_HW_RATE_INDEX_24MBPS = 8, + CONF_HW_RATE_INDEX_36MBPS = 9, + CONF_HW_RATE_INDEX_48MBPS = 10, + CONF_HW_RATE_INDEX_54MBPS = 11, + CONF_HW_RATE_INDEX_MCS0 = 12, + CONF_HW_RATE_INDEX_MCS1 = 13, + CONF_HW_RATE_INDEX_MCS2 = 14, + CONF_HW_RATE_INDEX_MCS3 = 15, + CONF_HW_RATE_INDEX_MCS4 = 16, + CONF_HW_RATE_INDEX_MCS5 = 17, + CONF_HW_RATE_INDEX_MCS6 = 18, + CONF_HW_RATE_INDEX_MCS7 = 19, + CONF_HW_RATE_INDEX_MCS7_SGI = 20, + CONF_HW_RATE_INDEX_MCS0_40MHZ = 21, + CONF_HW_RATE_INDEX_MCS1_40MHZ = 22, + CONF_HW_RATE_INDEX_MCS2_40MHZ = 23, + CONF_HW_RATE_INDEX_MCS3_40MHZ = 24, + CONF_HW_RATE_INDEX_MCS4_40MHZ = 25, + CONF_HW_RATE_INDEX_MCS5_40MHZ = 26, + CONF_HW_RATE_INDEX_MCS6_40MHZ = 27, + CONF_HW_RATE_INDEX_MCS7_40MHZ = 28, + CONF_HW_RATE_INDEX_MCS7_40MHZ_SGI = 29, + + /* MCS8+ rates overlap with 40Mhz rates */ + CONF_HW_RATE_INDEX_MCS8 = 21, + CONF_HW_RATE_INDEX_MCS9 = 22, + CONF_HW_RATE_INDEX_MCS10 = 23, + CONF_HW_RATE_INDEX_MCS11 = 24, + CONF_HW_RATE_INDEX_MCS12 = 25, + CONF_HW_RATE_INDEX_MCS13 = 26, + CONF_HW_RATE_INDEX_MCS14 = 27, + CONF_HW_RATE_INDEX_MCS15 = 28, + CONF_HW_RATE_INDEX_MCS15_SGI = 29, + + CONF_HW_RATE_INDEX_MAX = CONF_HW_RATE_INDEX_MCS7_40MHZ_SGI, }; #define CONF_HW_RXTX_RATE_UNSUPPORTED 0xff @@ -415,11 +444,11 @@ struct conf_rx_settings { #define CONF_TX_RATE_MASK_BASIC_P2P CONF_HW_BIT_RATE_6MBPS /* - * Rates supported for data packets when operating as AP. Note the absence + * Rates supported for data packets when operating as STA/AP. Note the absence * of the 22Mbps rate. There is a FW limitation on 12 rates so we must drop * one. The rate dropped is not mandatory under any operating mode. */ -#define CONF_TX_AP_ENABLED_RATES (CONF_HW_BIT_RATE_1MBPS | \ +#define CONF_TX_ENABLED_RATES (CONF_HW_BIT_RATE_1MBPS | \ CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | \ CONF_HW_BIT_RATE_6MBPS | CONF_HW_BIT_RATE_9MBPS | \ CONF_HW_BIT_RATE_11MBPS | CONF_HW_BIT_RATE_12MBPS | \ @@ -677,6 +706,18 @@ struct conf_tx_settings { /* Time in ms for Tx watchdog timer to expire */ u32 tx_watchdog_timeout; + + /* + * when a slow link has this much packets pending, it becomes a low + * priority link, scheduling-wise + */ + u8 slow_link_thold; + + /* + * when a fast link has this much packets pending, it becomes a low + * priority link, scheduling-wise + */ + u8 fast_link_thold; } __packed; enum { @@ -1047,6 +1088,7 @@ struct conf_roam_trigger_settings { struct conf_scan_settings { /* * The minimum time to wait on each channel for active scans + * This value will be used whenever there's a connected interface. * * Range: u32 tu/1000 */ @@ -1054,24 +1096,37 @@ struct conf_scan_settings { /* * The maximum time to wait on each channel for active scans + * This value will be currently used whenever there's a + * connected interface. It shouldn't exceed 30000 (~30ms) to avoid + * possible interference of voip traffic going on while scanning. * * Range: u32 tu/1000 */ u32 max_dwell_time_active; - /* - * The minimum time to wait on each channel for passive scans + /* The minimum time to wait on each channel for active scans + * when it's possible to have longer scan dwell times. + * Currently this is used whenever we're idle on all interfaces. + * Longer dwell times improve detection of networks within a + * single scan. * * Range: u32 tu/1000 */ - u32 min_dwell_time_passive; + u32 min_dwell_time_active_long; - /* - * The maximum time to wait on each channel for passive scans + /* The maximum time to wait on each channel for active scans + * when it's possible to have longer scan dwell times. + * See min_dwell_time_active_long * * Range: u32 tu/1000 */ - u32 max_dwell_time_passive; + u32 max_dwell_time_active_long; + + /* time to wait on the channel for passive scans (in TU/1000) */ + u32 dwell_time_passive; + + /* time to wait on the channel for DFS scans (in TU/1000) */ + u32 dwell_time_dfs; /* * Number of probe requests to transmit on each active scan channel @@ -1276,12 +1331,20 @@ struct conf_hangover_settings { u8 window_size; } __packed; +struct conf_recovery_settings { + /* BUG() on fw recovery */ + u8 bug_on_recovery; + + /* Prevent HW recovery. FW will remain stuck. */ + u8 no_recovery; +} __packed; + /* * The conf version consists of 4 bytes. The two MSB are the wlcore * version, the two LSB are the lower driver's private conf * version. */ -#define WLCORE_CONF_VERSION (0x0002 << 16) +#define WLCORE_CONF_VERSION (0x0005 << 16) #define WLCORE_CONF_MASK 0xffff0000 #define WLCORE_CONF_SIZE (sizeof(struct wlcore_conf_header) + \ sizeof(struct wlcore_conf)) @@ -1309,6 +1372,7 @@ struct wlcore_conf { struct conf_fwlog fwlog; struct conf_rate_policy_settings rate; struct conf_hangover_settings hangover; + struct conf_recovery_settings recovery; } __packed; struct wlcore_conf_file { diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index c86bb00..e70a7c8 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -490,7 +490,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf, DRIVER_STATE_PRINT_HEX(chip.id); DRIVER_STATE_PRINT_STR(chip.fw_ver_str); DRIVER_STATE_PRINT_STR(chip.phy_fw_ver_str); - DRIVER_STATE_PRINT_INT(sched_scanning); + DRIVER_STATE_PRINT_INT(recovery_count); #undef DRIVER_STATE_PRINT_INT #undef DRIVER_STATE_PRINT_LONG @@ -560,7 +560,6 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf, if (wlvif->bss_type == BSS_TYPE_STA_BSS || wlvif->bss_type == BSS_TYPE_IBSS) { VIF_STATE_PRINT_INT(sta.hlid); - VIF_STATE_PRINT_INT(sta.ba_rx_bitmap); VIF_STATE_PRINT_INT(sta.basic_rate_idx); VIF_STATE_PRINT_INT(sta.ap_rate_idx); VIF_STATE_PRINT_INT(sta.p2p_rate_idx); @@ -577,6 +576,10 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf, VIF_STATE_PRINT_INT(ap.ucast_rate_idx[3]); } VIF_STATE_PRINT_INT(last_tx_hlid); + VIF_STATE_PRINT_INT(tx_queue_count[0]); + VIF_STATE_PRINT_INT(tx_queue_count[1]); + VIF_STATE_PRINT_INT(tx_queue_count[2]); + VIF_STATE_PRINT_INT(tx_queue_count[3]); VIF_STATE_PRINT_LHEX(links_map[0]); VIF_STATE_PRINT_NSTR(ssid, wlvif->ssid_len); VIF_STATE_PRINT_INT(band); @@ -589,7 +592,6 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf, VIF_STATE_PRINT_INT(beacon_int); VIF_STATE_PRINT_INT(default_key); VIF_STATE_PRINT_INT(aid); - VIF_STATE_PRINT_INT(session_counter); VIF_STATE_PRINT_INT(psm_entry_retry); VIF_STATE_PRINT_INT(power_level); VIF_STATE_PRINT_INT(rssi_thold); @@ -993,7 +995,7 @@ static ssize_t sleep_auth_write(struct file *file, return -EINVAL; } - if (value < 0 || value > WL1271_PSM_MAX) { + if (value > WL1271_PSM_MAX) { wl1271_warning("sleep_auth must be between 0 and %d", WL1271_PSM_MAX); return -ERANGE; diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c index 4890705..70f289a 100644 --- a/drivers/net/wireless/ti/wlcore/event.c +++ b/drivers/net/wireless/ti/wlcore/event.c @@ -29,34 +29,39 @@ #include "scan.h" #include "wl12xx_80211.h" -static void wl1271_event_rssi_trigger(struct wl1271 *wl, - struct wl12xx_vif *wlvif, - struct event_mailbox *mbox) +void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr) { - struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); + struct wl12xx_vif *wlvif; + struct ieee80211_vif *vif; enum nl80211_cqm_rssi_threshold_event event; - s8 metric = mbox->rssi_snr_trigger_metric[0]; + s8 metric = metric_arr[0]; wl1271_debug(DEBUG_EVENT, "RSSI trigger metric: %d", metric); - if (metric <= wlvif->rssi_thold) - event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW; - else - event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; - - if (event != wlvif->last_rssi_event) - ieee80211_cqm_rssi_notify(vif, event, GFP_KERNEL); - wlvif->last_rssi_event = event; + /* TODO: check actual multi-role support */ + wl12xx_for_each_wlvif_sta(wl, wlvif) { + if (metric <= wlvif->rssi_thold) + event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW; + else + event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; + + vif = wl12xx_wlvif_to_vif(wlvif); + if (event != wlvif->last_rssi_event) + ieee80211_cqm_rssi_notify(vif, event, GFP_KERNEL); + wlvif->last_rssi_event = event; + } } +EXPORT_SYMBOL_GPL(wlcore_event_rssi_trigger); static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); if (wlvif->bss_type != BSS_TYPE_AP_BSS) { - if (!wlvif->sta.ba_rx_bitmap) + u8 hlid = wlvif->sta.hlid; + if (!wl->links[hlid].ba_bitmap) return; - ieee80211_stop_rx_ba_session(vif, wlvif->sta.ba_rx_bitmap, + ieee80211_stop_rx_ba_session(vif, wl->links[hlid].ba_bitmap, vif->bss_conf.bssid); } else { u8 hlid; @@ -74,8 +79,7 @@ static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif) } } -static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl, - u8 enable) +void wlcore_event_soft_gemini_sense(struct wl1271 *wl, u8 enable) { struct wl12xx_vif *wlvif; @@ -87,201 +91,169 @@ static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl, wl1271_recalc_rx_streaming(wl, wlvif); } } - } +EXPORT_SYMBOL_GPL(wlcore_event_soft_gemini_sense); -static void wl1271_event_mbox_dump(struct event_mailbox *mbox) +void wlcore_event_sched_scan_completed(struct wl1271 *wl, + u8 status) { - wl1271_debug(DEBUG_EVENT, "MBOX DUMP:"); - wl1271_debug(DEBUG_EVENT, "\tvector: 0x%x", mbox->events_vector); - wl1271_debug(DEBUG_EVENT, "\tmask: 0x%x", mbox->events_mask); + wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT (status 0x%0x)", + status); + + if (wl->sched_vif) { + ieee80211_sched_scan_stopped(wl->hw); + wl->sched_vif = NULL; + } } +EXPORT_SYMBOL_GPL(wlcore_event_sched_scan_completed); -static int wl1271_event_process(struct wl1271 *wl) +void wlcore_event_ba_rx_constraint(struct wl1271 *wl, + unsigned long roles_bitmap, + unsigned long allowed_bitmap) { - struct event_mailbox *mbox = wl->mbox; - struct ieee80211_vif *vif; struct wl12xx_vif *wlvif; - u32 vector; - bool disconnect_sta = false; - unsigned long sta_bitmap = 0; - int ret; - - wl1271_event_mbox_dump(mbox); - - 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) { - wl1271_debug(DEBUG_EVENT, "status: 0x%x", - mbox->scheduled_scan_status); - - wl1271_scan_stm(wl, wl->scan_vif); - } + wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx allowed=0x%lx", + __func__, roles_bitmap, allowed_bitmap); - if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) { - wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_REPORT_EVENT " - "(status 0x%0x)", mbox->scheduled_scan_status); + wl12xx_for_each_wlvif(wl, wlvif) { + if (wlvif->role_id == WL12XX_INVALID_ROLE_ID || + !test_bit(wlvif->role_id , &roles_bitmap)) + continue; - wl1271_scan_sched_scan_results(wl); + wlvif->ba_allowed = !!test_bit(wlvif->role_id, + &allowed_bitmap); + if (!wlvif->ba_allowed) + wl1271_stop_ba_event(wl, wlvif); } +} +EXPORT_SYMBOL_GPL(wlcore_event_ba_rx_constraint); - 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) { - ieee80211_sched_scan_stopped(wl->hw); - wl->sched_scanning = false; - } - } +void wlcore_event_channel_switch(struct wl1271 *wl, + unsigned long roles_bitmap, + bool success) +{ + struct wl12xx_vif *wlvif; + struct ieee80211_vif *vif; - if (vector & SOFT_GEMINI_SENSE_EVENT_ID) - wl12xx_event_soft_gemini_sense(wl, - mbox->soft_gemini_sense_info); + wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx success=%d", + __func__, roles_bitmap, success); - /* - * We are HW_MONITOR device. On beacon loss - queue - * connection loss work. Cancel it on REGAINED event. - */ - if (vector & BSS_LOSE_EVENT_ID) { - /* TODO: check for multi-role */ - int delay = wl->conf.conn.synch_fail_thold * - wl->conf.conn.bss_lose_timeout; - wl1271_info("Beacon loss detected."); + wl12xx_for_each_wlvif_sta(wl, wlvif) { + if (wlvif->role_id == WL12XX_INVALID_ROLE_ID || + !test_bit(wlvif->role_id , &roles_bitmap)) + continue; - /* - * if the work is already queued, it should take place. We - * don't want to delay the connection loss indication - * any more. - */ - ieee80211_queue_delayed_work(wl->hw, &wl->connection_loss_work, - msecs_to_jiffies(delay)); + if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, + &wlvif->flags)) + continue; - wl12xx_for_each_wlvif_sta(wl, wlvif) { - vif = wl12xx_wlvif_to_vif(wlvif); + vif = wl12xx_wlvif_to_vif(wlvif); - ieee80211_cqm_rssi_notify( - vif, - NL80211_CQM_RSSI_BEACON_LOSS_EVENT, - GFP_KERNEL); - } + ieee80211_chswitch_done(vif, success); + cancel_delayed_work(&wlvif->channel_switch_work); } +} +EXPORT_SYMBOL_GPL(wlcore_event_channel_switch); - if (vector & REGAINED_BSS_EVENT_ID) { - /* TODO: check for multi-role */ - wl1271_info("Beacon regained."); - cancel_delayed_work(&wl->connection_loss_work); - - /* sanity check - we can't lose and gain the beacon together */ - WARN(vector & BSS_LOSE_EVENT_ID, - "Concurrent beacon loss and gain from FW"); - } +void wlcore_event_dummy_packet(struct wl1271 *wl) +{ + wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID"); + wl1271_tx_dummy_packet(wl); +} +EXPORT_SYMBOL_GPL(wlcore_event_dummy_packet); - if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) { - /* TODO: check actual multi-role support */ - wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT"); - wl12xx_for_each_wlvif_sta(wl, wlvif) { - wl1271_event_rssi_trigger(wl, wlvif, mbox); +static void wlcore_disconnect_sta(struct wl1271 *wl, unsigned long sta_bitmap) +{ + u32 num_packets = wl->conf.tx.max_tx_retries; + struct wl12xx_vif *wlvif; + struct ieee80211_vif *vif; + struct ieee80211_sta *sta; + const u8 *addr; + int h; + + for_each_set_bit(h, &sta_bitmap, WL12XX_MAX_LINKS) { + bool found = false; + /* find the ap vif connected to this sta */ + wl12xx_for_each_wlvif_ap(wl, wlvif) { + if (!test_bit(h, wlvif->ap.sta_hlid_map)) + continue; + found = true; + break; } - } + if (!found) + continue; - if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID) { - u8 role_id = mbox->role_id; - wl1271_debug(DEBUG_EVENT, "BA_SESSION_RX_CONSTRAINT_EVENT_ID. " - "ba_allowed = 0x%x, role_id=%d", - mbox->rx_ba_allowed, role_id); + vif = wl12xx_wlvif_to_vif(wlvif); + addr = wl->links[h].addr; - wl12xx_for_each_wlvif(wl, wlvif) { - if (role_id != 0xff && role_id != wlvif->role_id) - continue; - - wlvif->ba_allowed = !!mbox->rx_ba_allowed; - if (!wlvif->ba_allowed) - wl1271_stop_ba_event(wl, wlvif); + rcu_read_lock(); + sta = ieee80211_find_sta(vif, addr); + if (sta) { + wl1271_debug(DEBUG_EVENT, "remove sta %d", h); + ieee80211_report_low_ack(sta, num_packets); } + rcu_read_unlock(); } +} - if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID) { - wl1271_debug(DEBUG_EVENT, "CHANNEL_SWITCH_COMPLETE_EVENT_ID. " - "status = 0x%x", - mbox->channel_switch_status); - /* - * That event uses for two cases: - * 1) channel switch complete with status=0 - * 2) channel switch failed status=1 - */ - - /* TODO: configure only the relevant vif */ - wl12xx_for_each_wlvif_sta(wl, wlvif) { - bool success; - - if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, - &wlvif->flags)) - continue; - - success = mbox->channel_switch_status ? false : true; - vif = wl12xx_wlvif_to_vif(wlvif); +void wlcore_event_max_tx_failure(struct wl1271 *wl, unsigned long sta_bitmap) +{ + wl1271_debug(DEBUG_EVENT, "MAX_TX_FAILURE_EVENT_ID"); + wlcore_disconnect_sta(wl, sta_bitmap); +} +EXPORT_SYMBOL_GPL(wlcore_event_max_tx_failure); - ieee80211_chswitch_done(vif, success); - } - } +void wlcore_event_inactive_sta(struct wl1271 *wl, unsigned long sta_bitmap) +{ + wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID"); + wlcore_disconnect_sta(wl, sta_bitmap); +} +EXPORT_SYMBOL_GPL(wlcore_event_inactive_sta); - if ((vector & DUMMY_PACKET_EVENT_ID)) { - wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID"); - ret = wl1271_tx_dummy_packet(wl); - if (ret < 0) - return ret; - } +void wlcore_event_roc_complete(struct wl1271 *wl) +{ + wl1271_debug(DEBUG_EVENT, "REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID"); + if (wl->roc_vif) + ieee80211_ready_on_channel(wl->hw); +} +EXPORT_SYMBOL_GPL(wlcore_event_roc_complete); +void wlcore_event_beacon_loss(struct wl1271 *wl, unsigned long roles_bitmap) +{ /* - * "TX retries exceeded" has a different meaning according to mode. - * In AP mode the offending station is disconnected. + * We are HW_MONITOR device. On beacon loss - queue + * connection loss work. Cancel it on REGAINED event. */ - if (vector & MAX_TX_RETRY_EVENT_ID) { - wl1271_debug(DEBUG_EVENT, "MAX_TX_RETRY_EVENT_ID"); - sta_bitmap |= le16_to_cpu(mbox->sta_tx_retry_exceeded); - disconnect_sta = true; - } + struct wl12xx_vif *wlvif; + struct ieee80211_vif *vif; + int delay = wl->conf.conn.synch_fail_thold * + wl->conf.conn.bss_lose_timeout; - if (vector & INACTIVE_STA_EVENT_ID) { - wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID"); - sta_bitmap |= le16_to_cpu(mbox->sta_aging_status); - disconnect_sta = true; - } + wl1271_info("Beacon loss detected. roles:0x%lx", roles_bitmap); - if (disconnect_sta) { - u32 num_packets = wl->conf.tx.max_tx_retries; - struct ieee80211_sta *sta; - const u8 *addr; - int h; - - for_each_set_bit(h, &sta_bitmap, WL12XX_MAX_LINKS) { - bool found = false; - /* find the ap vif connected to this sta */ - wl12xx_for_each_wlvif_ap(wl, wlvif) { - if (!test_bit(h, wlvif->ap.sta_hlid_map)) - continue; - found = true; - break; - } - if (!found) - continue; + wl12xx_for_each_wlvif_sta(wl, wlvif) { + if (wlvif->role_id == WL12XX_INVALID_ROLE_ID || + !test_bit(wlvif->role_id , &roles_bitmap)) + continue; - vif = wl12xx_wlvif_to_vif(wlvif); - addr = wl->links[h].addr; + /* + * if the work is already queued, it should take place. + * We don't want to delay the connection loss + * indication any more. + */ + ieee80211_queue_delayed_work(wl->hw, + &wlvif->connection_loss_work, + msecs_to_jiffies(delay)); - rcu_read_lock(); - sta = ieee80211_find_sta(vif, addr); - if (sta) { - wl1271_debug(DEBUG_EVENT, "remove sta %d", h); - ieee80211_report_low_ack(sta, num_packets); - } - rcu_read_unlock(); - } + vif = wl12xx_wlvif_to_vif(wlvif); + ieee80211_cqm_rssi_notify( + vif, + NL80211_CQM_RSSI_BEACON_LOSS_EVENT, + GFP_KERNEL); } - return 0; } +EXPORT_SYMBOL_GPL(wlcore_event_beacon_loss); int wl1271_event_unmask(struct wl1271 *wl) { @@ -305,12 +277,12 @@ int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num) /* first we read the mbox descriptor */ ret = wlcore_read(wl, wl->mbox_ptr[mbox_num], wl->mbox, - sizeof(*wl->mbox), false); + wl->mbox_size, false); if (ret < 0) return ret; /* process the descriptor */ - ret = wl1271_event_process(wl); + ret = wl->ops->process_mailbox_events(wl); if (ret < 0) return ret; diff --git a/drivers/net/wireless/ti/wlcore/event.h b/drivers/net/wireless/ti/wlcore/event.h index 8adf18d..acc7a59 100644 --- a/drivers/net/wireless/ti/wlcore/event.h +++ b/drivers/net/wireless/ti/wlcore/event.h @@ -46,33 +46,17 @@ enum { RSSI_SNR_TRIGGER_5_EVENT_ID = BIT(5), RSSI_SNR_TRIGGER_6_EVENT_ID = BIT(6), RSSI_SNR_TRIGGER_7_EVENT_ID = BIT(7), - MEASUREMENT_START_EVENT_ID = BIT(8), - MEASUREMENT_COMPLETE_EVENT_ID = BIT(9), - SCAN_COMPLETE_EVENT_ID = BIT(10), - WFD_DISCOVERY_COMPLETE_EVENT_ID = BIT(11), - AP_DISCOVERY_COMPLETE_EVENT_ID = BIT(12), - RESERVED1 = BIT(13), - PSPOLL_DELIVERY_FAILURE_EVENT_ID = BIT(14), - ROLE_STOP_COMPLETE_EVENT_ID = BIT(15), - RADAR_DETECTED_EVENT_ID = BIT(16), - 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), - DUMMY_PACKET_EVENT_ID = BIT(21), - SOFT_GEMINI_SENSE_EVENT_ID = BIT(22), - CHANGE_AUTO_MODE_TIMEOUT_EVENT_ID = BIT(23), - SOFT_GEMINI_AVALANCHE_EVENT_ID = BIT(24), - PLT_RX_CALIBRATION_COMPLETE_EVENT_ID = BIT(25), - INACTIVE_STA_EVENT_ID = BIT(26), - PEER_REMOVE_COMPLETE_EVENT_ID = BIT(27), - PERIODIC_SCAN_COMPLETE_EVENT_ID = BIT(28), - PERIODIC_SCAN_REPORT_EVENT_ID = BIT(29), - BA_SESSION_RX_CONSTRAINT_EVENT_ID = BIT(30), - REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID = BIT(31), + EVENT_MBOX_ALL_EVENT_ID = 0x7fffffff, }; +/* events the driver might want to wait for */ +enum wlcore_wait_event { + WLCORE_EVENT_ROLE_STOP_COMPLETE, + WLCORE_EVENT_PEER_REMOVE_COMPLETE, + WLCORE_EVENT_DFS_CONFIG_COMPLETE +}; + enum { EVENT_ENTER_POWER_SAVE_FAIL = 0, EVENT_ENTER_POWER_SAVE_SUCCESS, @@ -80,61 +64,24 @@ enum { #define NUM_OF_RSSI_SNR_TRIGGERS 8 -struct event_mailbox { - __le32 events_vector; - __le32 events_mask; - __le32 reserved_1; - __le32 reserved_2; - - u8 number_of_scan_results; - u8 scan_tag; - u8 completed_scan_status; - u8 reserved_3; - - u8 soft_gemini_sense_info; - u8 soft_gemini_protective_info; - s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS]; - u8 change_auto_mode_timeout; - u8 scheduled_scan_status; - u8 reserved4; - /* tuned channel (roc) */ - u8 roc_channel; - - __le16 hlid_removed_bitmap; - - /* bitmap of aged stations (by HLID) */ - __le16 sta_aging_status; - - /* bitmap of stations (by HLID) which exceeded max tx retries */ - __le16 sta_tx_retry_exceeded; - - /* discovery completed results */ - u8 discovery_tag; - u8 number_of_preq_results; - u8 number_of_prsp_results; - u8 reserved_5; - - /* rx ba constraint */ - u8 role_id; /* 0xFF means any role. */ - u8 rx_ba_allowed; - u8 reserved_6[2]; - - /* Channel switch results */ - - u8 channel_switch_role_id; - u8 channel_switch_status; - u8 reserved_7[2]; - - u8 ps_poll_delivery_failure_role_ids; - u8 stopped_role_ids; - u8 started_role_ids; - - u8 reserved_8[9]; -} __packed; - struct wl1271; int wl1271_event_unmask(struct wl1271 *wl); int wl1271_event_handle(struct wl1271 *wl, u8 mbox); +void wlcore_event_soft_gemini_sense(struct wl1271 *wl, u8 enable); +void wlcore_event_sched_scan_completed(struct wl1271 *wl, + u8 status); +void wlcore_event_ba_rx_constraint(struct wl1271 *wl, + unsigned long roles_bitmap, + unsigned long allowed_bitmap); +void wlcore_event_channel_switch(struct wl1271 *wl, + unsigned long roles_bitmap, + bool success); +void wlcore_event_beacon_loss(struct wl1271 *wl, unsigned long roles_bitmap); +void wlcore_event_dummy_packet(struct wl1271 *wl); +void wlcore_event_max_tx_failure(struct wl1271 *wl, unsigned long sta_bitmap); +void wlcore_event_inactive_sta(struct wl1271 *wl, unsigned long sta_bitmap); +void wlcore_event_roc_complete(struct wl1271 *wl); +void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr); #endif diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h index 2673d78..7fd260c 100644 --- a/drivers/net/wireless/ti/wlcore/hw_ops.h +++ b/drivers/net/wireless/ti/wlcore/hw_ops.h @@ -201,4 +201,45 @@ wlcore_hw_pre_pkt_send(struct wl1271 *wl, u32 buf_offset, u32 last_len) return buf_offset; } +static inline void +wlcore_hw_sta_rc_update(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct ieee80211_sta *sta, u32 changed) +{ + if (wl->ops->sta_rc_update) + wl->ops->sta_rc_update(wl, wlvif, sta, changed); +} + +static inline int +wlcore_hw_set_peer_cap(struct wl1271 *wl, + struct ieee80211_sta_ht_cap *ht_cap, + bool allow_ht_operation, + u32 rate_set, u8 hlid) +{ + if (wl->ops->set_peer_cap) + return wl->ops->set_peer_cap(wl, ht_cap, allow_ht_operation, + rate_set, hlid); + + return 0; +} + +static inline bool +wlcore_hw_lnk_high_prio(struct wl1271 *wl, u8 hlid, + struct wl1271_link *lnk) +{ + if (!wl->ops->lnk_high_prio) + BUG_ON(1); + + return wl->ops->lnk_high_prio(wl, hlid, lnk); +} + +static inline bool +wlcore_hw_lnk_low_prio(struct wl1271 *wl, u8 hlid, + struct wl1271_link *lnk) +{ + if (!wl->ops->lnk_low_prio) + BUG_ON(1); + + return wl->ops->lnk_low_prio(wl, hlid, lnk); +} + #endif diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c index 32d157f..5c6f11e 100644 --- a/drivers/net/wireless/ti/wlcore/init.c +++ b/drivers/net/wireless/ti/wlcore/init.c @@ -41,14 +41,14 @@ int wl1271_init_templates_config(struct wl1271 *wl) /* send empty templates for fw memory reservation */ ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, - CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL, + wl->scan_templ_id_2_4, NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, - CMD_TEMPL_CFG_PROBE_REQ_5, + wl->scan_templ_id_5, NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) @@ -56,14 +56,16 @@ int wl1271_init_templates_config(struct wl1271 *wl) if (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL) { ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, - CMD_TEMPL_APP_PROBE_REQ_2_4, NULL, + wl->sched_scan_templ_id_2_4, + NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, - CMD_TEMPL_APP_PROBE_REQ_5, NULL, + wl->sched_scan_templ_id_5, + NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) @@ -463,7 +465,7 @@ int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif) if ((wlvif->basic_rate_set & CONF_TX_OFDM_RATES)) supported_rates = CONF_TX_OFDM_RATES; else - supported_rates = CONF_TX_AP_ENABLED_RATES; + supported_rates = CONF_TX_ENABLED_RATES; /* unconditionally enable HT rates */ supported_rates |= CONF_TX_MCS_RATES; @@ -575,9 +577,6 @@ int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif) /* Configure for power according to debugfs */ if (sta_auth != WL1271_PSM_ILLEGAL) ret = wl1271_acx_sleep_auth(wl, sta_auth); - /* Configure for power always on */ - else if (wl->quirks & WLCORE_QUIRK_NO_ELP) - ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); /* Configure for ELP power saving */ else ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP); @@ -679,6 +678,10 @@ int wl1271_hw_init(struct wl1271 *wl) if (ret < 0) return ret; + ret = wlcore_cmd_regdomain_config_locked(wl); + if (ret < 0) + return ret; + /* Bluetooth WLAN coexistence */ ret = wl1271_init_pta(wl); if (ret < 0) diff --git a/drivers/net/wireless/ti/wlcore/io.h b/drivers/net/wireless/ti/wlcore/io.h index f48530f..af7d9f9 100644 --- a/drivers/net/wireless/ti/wlcore/io.h +++ b/drivers/net/wireless/ti/wlcore/io.h @@ -105,13 +105,13 @@ static inline int __must_check wlcore_raw_read32(struct wl1271 *wl, int addr, { int ret; - ret = wlcore_raw_read(wl, addr, &wl->buffer_32, - sizeof(wl->buffer_32), false); + ret = wlcore_raw_read(wl, addr, wl->buffer_32, + sizeof(*wl->buffer_32), false); if (ret < 0) return ret; if (val) - *val = le32_to_cpu(wl->buffer_32); + *val = le32_to_cpu(*wl->buffer_32); return 0; } @@ -119,9 +119,9 @@ static inline int __must_check wlcore_raw_read32(struct wl1271 *wl, int addr, static inline int __must_check wlcore_raw_write32(struct wl1271 *wl, int addr, u32 val) { - wl->buffer_32 = cpu_to_le32(val); - return wlcore_raw_write(wl, addr, &wl->buffer_32, - sizeof(wl->buffer_32), false); + *wl->buffer_32 = cpu_to_le32(val); + return wlcore_raw_write(wl, addr, wl->buffer_32, + sizeof(*wl->buffer_32), false); } static inline int __must_check wlcore_read(struct wl1271 *wl, int addr, diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index ea9d8e0..e1dfdf9 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -56,8 +56,8 @@ #define WL1271_BOOT_RETRIES 3 static char *fwlog_param; -static bool bug_on_recovery; -static bool no_recovery; +static int bug_on_recovery = -1; +static int no_recovery = -1; static void __wl1271_op_remove_interface(struct wl1271 *wl, struct ieee80211_vif *vif, @@ -79,22 +79,22 @@ static int wl12xx_set_authorized(struct wl1271 *wl, if (test_and_set_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags)) return 0; - ret = wl12xx_cmd_set_peer_state(wl, wlvif->sta.hlid); + ret = wl12xx_cmd_set_peer_state(wl, wlvif, wlvif->sta.hlid); if (ret < 0) return ret; - wl12xx_croc(wl, wlvif->role_id); - wl1271_info("Association completed."); return 0; } -static int wl1271_reg_notify(struct wiphy *wiphy, - struct regulatory_request *request) +static void wl1271_reg_notify(struct wiphy *wiphy, + struct regulatory_request *request) { struct ieee80211_supported_band *band; struct ieee80211_channel *ch; int i; + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct wl1271 *wl = hw->priv; band = wiphy->bands[IEEE80211_BAND_5GHZ]; for (i = 0; i < band->n_channels; i++) { @@ -108,7 +108,8 @@ static int wl1271_reg_notify(struct wiphy *wiphy, } - return 0; + if (likely(wl->state == WLCORE_STATE_ON)) + wlcore_regdomain_config(wl); } static int wl1271_set_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif, @@ -303,6 +304,7 @@ out: static void wlcore_adjust_conf(struct wl1271 *wl) { /* Adjust settings according to optional module parameters */ + if (fwlog_param) { if (!strcmp(fwlog_param, "continuous")) { wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS; @@ -318,16 +320,22 @@ static void wlcore_adjust_conf(struct wl1271 *wl) wl1271_error("Unknown fwlog parameter %s", fwlog_param); } } + + if (bug_on_recovery != -1) + wl->conf.recovery.bug_on_recovery = (u8) bug_on_recovery; + + if (no_recovery != -1) + wl->conf.recovery.no_recovery = (u8) no_recovery; } static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid, u8 tx_pkts) { - bool fw_ps, single_sta; + bool fw_ps, single_link; fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); - single_sta = (wl->active_sta_count == 1); + single_link = (wl->active_link_count == 1); /* * Wake up from high level PS if the STA is asleep with too little @@ -338,10 +346,10 @@ static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl, /* * Start high-level PS if the STA is asleep with enough blocks in FW. - * Make an exception if this is the only connected station. In this - * case FW-memory congestion is not a problem. + * Make an exception if this is the only connected link. In this + * case FW-memory congestion is less of a problem. */ - else if (!single_sta && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS) + else if (!single_link && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS) wl12xx_ps_link_start(wl, wlvif, hlid, true); } @@ -349,11 +357,8 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct wl_fw_status_2 *status) { - struct wl1271_link *lnk; u32 cur_fw_ps_map; - u8 hlid, cnt; - - /* TODO: also use link_fast_bitmap here */ + u8 hlid; cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap); if (wl->ap_fw_ps_map != cur_fw_ps_map) { @@ -365,17 +370,9 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl, wl->ap_fw_ps_map = cur_fw_ps_map; } - for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, WL12XX_MAX_LINKS) { - lnk = &wl->links[hlid]; - cnt = status->counters.tx_lnk_free_pkts[hlid] - - lnk->prev_freed_pkts; - - lnk->prev_freed_pkts = status->counters.tx_lnk_free_pkts[hlid]; - lnk->allocated_pkts -= cnt; - + for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, WL12XX_MAX_LINKS) wl12xx_irq_ps_regulate_link(wl, wlvif, hlid, - lnk->allocated_pkts); - } + wl->links[hlid].allocated_pkts); } static int wlcore_fw_status(struct wl1271 *wl, @@ -389,6 +386,7 @@ static int wlcore_fw_status(struct wl1271 *wl, int i; size_t status_len; int ret; + struct wl1271_link *lnk; status_len = WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) + sizeof(*status_2) + wl->fw_status_priv_len; @@ -414,6 +412,17 @@ static int wlcore_fw_status(struct wl1271 *wl, wl->tx_pkts_freed[i] = status_2->counters.tx_released_pkts[i]; } + + for_each_set_bit(i, wl->links_map, WL12XX_MAX_LINKS) { + lnk = &wl->links[i]; + /* prevent wrap-around in freed-packets counter */ + lnk->allocated_pkts -= + (status_2->counters.tx_lnk_free_pkts[i] - + lnk->prev_freed_pkts) & 0xff; + + lnk->prev_freed_pkts = status_2->counters.tx_lnk_free_pkts[i]; + } + /* prevent wrap-around in total blocks counter */ if (likely(wl->tx_blocks_freed <= le32_to_cpu(status_2->total_released_blks))) @@ -466,6 +475,8 @@ static int wlcore_fw_status(struct wl1271 *wl, wl->time_offset = (timespec_to_ns(&ts) >> 10) - (s64)le32_to_cpu(status_2->fw_localtime); + wl->fw_fast_lnk_map = le32_to_cpu(status_2->link_fast_bitmap); + return 0; } @@ -802,11 +813,13 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl) /* * Make sure the chip is awake and the logger isn't active. - * Do not send a stop fwlog command if the fw is hanged. + * Do not send a stop fwlog command if the fw is hanged or if + * dbgpins are used (due to some fw bug). */ if (wl1271_ps_elp_wakeup(wl)) goto out; - if (!wl->watchdog_recovery) + if (!wl->watchdog_recovery && + wl->conf.fwlog.output != WL12XX_FWLOG_OUTPUT_DBG_PINS) wl12xx_cmd_stop_fwlog(wl); /* Read the first memory block address */ @@ -874,7 +887,8 @@ static void wlcore_print_recovery(struct wl1271 *wl) if (ret < 0) return; - wl1271_info("pc: 0x%x, hint_sts: 0x%08x", pc, hint_sts); + wl1271_info("pc: 0x%x, hint_sts: 0x%08x count: %d", + pc, hint_sts, ++wl->recovery_count); wlcore_set_partition(wl, &wl->ptable[PART_WORK]); } @@ -897,10 +911,10 @@ static void wl1271_recovery_work(struct work_struct *work) wlcore_print_recovery(wl); } - BUG_ON(bug_on_recovery && + BUG_ON(wl->conf.recovery.bug_on_recovery && !test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags)); - if (no_recovery) { + if (wl->conf.recovery.no_recovery) { wl1271_info("No recovery (chosen on module load). Fw will remain stuck."); goto out_unlock; } @@ -920,11 +934,6 @@ static void wl1271_recovery_work(struct work_struct *work) /* Prevent spurious TX during FW restart */ wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART); - if (wl->sched_scanning) { - ieee80211_sched_scan_stopped(wl->hw); - wl->sched_scanning = false; - } - /* reboot the chipset */ while (!list_empty(&wl->wlvif_list)) { wlvif = list_first_entry(&wl->wlvif_list, @@ -1141,7 +1150,6 @@ int wl1271_plt_stop(struct wl1271 *wl) cancel_work_sync(&wl->recovery_work); cancel_delayed_work_sync(&wl->elp_work); cancel_delayed_work_sync(&wl->tx_watchdog_work); - cancel_delayed_work_sync(&wl->connection_loss_work); mutex_lock(&wl->mutex); wl1271_power_off(wl); @@ -1169,9 +1177,13 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, int q, mapping; u8 hlid; - if (vif) - wlvif = wl12xx_vif_to_data(vif); + if (!vif) { + wl1271_debug(DEBUG_TX, "DROP skb with no vif"); + ieee80211_free_txskb(hw, skb); + return; + } + wlvif = wl12xx_vif_to_data(vif); mapping = skb_get_queue_mapping(skb); q = wl1271_tx_get_queue(mapping); @@ -1185,9 +1197,9 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, * allow these packets through. */ if (hlid == WL12XX_INVALID_LINK_ID || - (wlvif && !test_bit(hlid, wlvif->links_map)) || - (wlcore_is_queue_stopped(wl, q) && - !wlcore_is_queue_stopped_by_reason(wl, q, + (!test_bit(hlid, wlvif->links_map)) || + (wlcore_is_queue_stopped_locked(wl, wlvif, q) && + !wlcore_is_queue_stopped_by_reason_locked(wl, wlvif, q, WLCORE_QUEUE_STOP_REASON_WATERMARK))) { wl1271_debug(DEBUG_TX, "DROP skb hlid %d q %d", hlid, q); ieee80211_free_txskb(hw, skb); @@ -1199,16 +1211,17 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, skb_queue_tail(&wl->links[hlid].tx_queue[q], skb); wl->tx_queue_count[q]++; + wlvif->tx_queue_count[q]++; /* * The workqueue is slow to process the tx_queue and we need stop * the queue here, otherwise the queue will get too long. */ - if (wl->tx_queue_count[q] >= WL1271_TX_QUEUE_HIGH_WATERMARK && - !wlcore_is_queue_stopped_by_reason(wl, q, + if (wlvif->tx_queue_count[q] >= WL1271_TX_QUEUE_HIGH_WATERMARK && + !wlcore_is_queue_stopped_by_reason_locked(wl, wlvif, q, WLCORE_QUEUE_STOP_REASON_WATERMARK)) { wl1271_debug(DEBUG_TX, "op_tx: stopping queues for q %d", q); - wlcore_stop_queue_locked(wl, q, + wlcore_stop_queue_locked(wl, wlvif, q, WLCORE_QUEUE_STOP_REASON_WATERMARK); } @@ -1843,11 +1856,10 @@ static void wlcore_op_stop_locked(struct wl1271 *wl) cancel_work_sync(&wl->tx_work); cancel_delayed_work_sync(&wl->elp_work); cancel_delayed_work_sync(&wl->tx_watchdog_work); - cancel_delayed_work_sync(&wl->connection_loss_work); /* let's notify MAC80211 about the remaining pending TX frames */ - wl12xx_tx_reset(wl); mutex_lock(&wl->mutex); + wl12xx_tx_reset(wl); wl1271_power_off(wl); /* @@ -1870,14 +1882,17 @@ static void wlcore_op_stop_locked(struct wl1271 *wl) wl->time_offset = 0; wl->ap_fw_ps_map = 0; wl->ap_ps_map = 0; - wl->sched_scanning = false; wl->sleep_auth = WL1271_PSM_ILLEGAL; memset(wl->roles_map, 0, sizeof(wl->roles_map)); memset(wl->links_map, 0, sizeof(wl->links_map)); memset(wl->roc_map, 0, sizeof(wl->roc_map)); + memset(wl->session_ids, 0, sizeof(wl->session_ids)); wl->active_sta_count = 0; + wl->active_link_count = 0; /* The system link is always allocated */ + wl->links[WL12XX_SYSTEM_HLID].allocated_pkts = 0; + wl->links[WL12XX_SYSTEM_HLID].prev_freed_pkts = 0; __set_bit(WL12XX_SYSTEM_HLID, wl->links_map); /* @@ -1903,6 +1918,12 @@ static void wlcore_op_stop_locked(struct wl1271 *wl) wl->tx_res_if = NULL; kfree(wl->target_mem_map); wl->target_mem_map = NULL; + + /* + * FW channels must be re-calibrated after recovery, + * clear the last Reg-Domain channel configuration. + */ + memset(wl->reg_ch_conf_last, 0, sizeof(wl->reg_ch_conf_last)); } static void wlcore_op_stop(struct ieee80211_hw *hw) @@ -1918,6 +1939,71 @@ static void wlcore_op_stop(struct ieee80211_hw *hw) mutex_unlock(&wl->mutex); } +static void wlcore_channel_switch_work(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wl1271 *wl; + struct ieee80211_vif *vif; + struct wl12xx_vif *wlvif; + int ret; + + dwork = container_of(work, struct delayed_work, work); + wlvif = container_of(dwork, struct wl12xx_vif, channel_switch_work); + wl = wlvif->wl; + + wl1271_info("channel switch failed (role_id: %d).", wlvif->role_id); + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state != WLCORE_STATE_ON)) + goto out; + + /* check the channel switch is still ongoing */ + if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags)) + goto out; + + vif = wl12xx_wlvif_to_vif(wlvif); + ieee80211_chswitch_done(vif, false); + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + wl12xx_cmd_stop_channel_switch(wl, wlvif); + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); +} + +static void wlcore_connection_loss_work(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wl1271 *wl; + struct ieee80211_vif *vif; + struct wl12xx_vif *wlvif; + + dwork = container_of(work, struct delayed_work, work); + wlvif = container_of(dwork, struct wl12xx_vif, connection_loss_work); + wl = wlvif->wl; + + wl1271_info("Connection loss work (role_id: %d).", wlvif->role_id); + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state != WLCORE_STATE_ON)) + goto out; + + /* Call mac80211 connection loss */ + if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) + goto out; + + vif = wl12xx_wlvif_to_vif(wlvif); + ieee80211_connection_loss(vif); +out: + mutex_unlock(&wl->mutex); +} + static int wl12xx_allocate_rate_policy(struct wl1271 *wl, u8 *idx) { u8 policy = find_first_zero_bit(wl->rate_policies_map, @@ -2037,15 +2123,15 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif) for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++) wl12xx_allocate_rate_policy(wl, &wlvif->ap.ucast_rate_idx[i]); - wlvif->basic_rate_set = CONF_TX_AP_ENABLED_RATES; + wlvif->basic_rate_set = CONF_TX_ENABLED_RATES; /* * TODO: check if basic_rate shouldn't be * wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); * instead (the same thing for STA above). */ - wlvif->basic_rate = CONF_TX_AP_ENABLED_RATES; + wlvif->basic_rate = CONF_TX_ENABLED_RATES; /* TODO: this seems to be used only for STA, check it */ - wlvif->rate_set = CONF_TX_AP_ENABLED_RATES; + wlvif->rate_set = CONF_TX_ENABLED_RATES; } wlvif->bitrate_masks[IEEE80211_BAND_2GHZ] = wl->conf.tx.basic_rate; @@ -2065,6 +2151,10 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif) wl1271_rx_streaming_enable_work); INIT_WORK(&wlvif->rx_streaming_disable_work, wl1271_rx_streaming_disable_work); + INIT_DELAYED_WORK(&wlvif->channel_switch_work, + wlcore_channel_switch_work); + INIT_DELAYED_WORK(&wlvif->connection_loss_work, + wlcore_connection_loss_work); INIT_LIST_HEAD(&wlvif->list); setup_timer(&wlvif->rx_streaming_timer, wl1271_rx_streaming_timer, @@ -2198,6 +2288,81 @@ static void wl12xx_force_active_psm(struct wl1271 *wl) } } +struct wlcore_hw_queue_iter_data { + unsigned long hw_queue_map[BITS_TO_LONGS(WLCORE_NUM_MAC_ADDRESSES)]; + /* current vif */ + struct ieee80211_vif *vif; + /* is the current vif among those iterated */ + bool cur_running; +}; + +static void wlcore_hw_queue_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct wlcore_hw_queue_iter_data *iter_data = data; + + if (WARN_ON_ONCE(vif->hw_queue[0] == IEEE80211_INVAL_HW_QUEUE)) + return; + + if (iter_data->cur_running || vif == iter_data->vif) { + iter_data->cur_running = true; + return; + } + + __set_bit(vif->hw_queue[0] / NUM_TX_QUEUES, iter_data->hw_queue_map); +} + +static int wlcore_allocate_hw_queue_base(struct wl1271 *wl, + struct wl12xx_vif *wlvif) +{ + struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); + struct wlcore_hw_queue_iter_data iter_data = {}; + int i, q_base; + + iter_data.vif = vif; + + /* mark all bits taken by active interfaces */ + ieee80211_iterate_active_interfaces_atomic(wl->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + wlcore_hw_queue_iter, &iter_data); + + /* the current vif is already running in mac80211 (resume/recovery) */ + if (iter_data.cur_running) { + wlvif->hw_queue_base = vif->hw_queue[0]; + wl1271_debug(DEBUG_MAC80211, + "using pre-allocated hw queue base %d", + wlvif->hw_queue_base); + + /* interface type might have changed type */ + goto adjust_cab_queue; + } + + q_base = find_first_zero_bit(iter_data.hw_queue_map, + WLCORE_NUM_MAC_ADDRESSES); + if (q_base >= WLCORE_NUM_MAC_ADDRESSES) + return -EBUSY; + + wlvif->hw_queue_base = q_base * NUM_TX_QUEUES; + wl1271_debug(DEBUG_MAC80211, "allocating hw queue base: %d", + wlvif->hw_queue_base); + + for (i = 0; i < NUM_TX_QUEUES; i++) { + wl->queue_stop_reasons[wlvif->hw_queue_base + i] = 0; + /* register hw queues in mac80211 */ + vif->hw_queue[i] = wlvif->hw_queue_base + i; + } + +adjust_cab_queue: + /* the last places are reserved for cab queues per interface */ + if (wlvif->bss_type == BSS_TYPE_AP_BSS) + vif->cab_queue = NUM_TX_QUEUES * WLCORE_NUM_MAC_ADDRESSES + + wlvif->hw_queue_base / NUM_TX_QUEUES; + else + vif->cab_queue = IEEE80211_INVAL_HW_QUEUE; + + return 0; +} + static int wl1271_op_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -2244,6 +2409,10 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, goto out; } + ret = wlcore_allocate_hw_queue_base(wl, wlvif); + if (ret < 0) + goto out; + if (wl12xx_need_fw_change(wl, vif_count, true)) { wl12xx_force_active_psm(wl); set_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags); @@ -2314,7 +2483,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, wl1271_info("down"); if (wl->scan.state != WL1271_SCAN_STATE_IDLE && - wl->scan_vif == vif) { + wl->scan_wlvif == wlvif) { /* * Rearm the tx watchdog just before idling scan. This * prevents just-finished scans from triggering the watchdog @@ -2323,11 +2492,21 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, wl->scan.state = WL1271_SCAN_STATE_IDLE; memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); - wl->scan_vif = NULL; + wl->scan_wlvif = NULL; wl->scan.req = NULL; ieee80211_scan_completed(wl->hw, true); } + if (wl->sched_vif == wlvif) { + ieee80211_sched_scan_stopped(wl->hw); + wl->sched_vif = NULL; + } + + if (wl->roc_vif == vif) { + wl->roc_vif = NULL; + ieee80211_remain_on_channel_expired(wl->hw); + } + if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) { /* disable active roles */ ret = wl1271_ps_elp_wakeup(wl); @@ -2396,9 +2575,6 @@ deinit: /* Configure for power according to debugfs */ if (sta_auth != WL1271_PSM_ILLEGAL) wl1271_acx_sleep_auth(wl, sta_auth); - /* Configure for power always on */ - else if (wl->quirks & WLCORE_QUIRK_NO_ELP) - wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); /* Configure for ELP power saving */ else wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP); @@ -2410,6 +2586,7 @@ unlock: del_timer_sync(&wlvif->rx_streaming_timer); cancel_work_sync(&wlvif->rx_streaming_enable_work); cancel_work_sync(&wlvif->rx_streaming_disable_work); + cancel_delayed_work_sync(&wlvif->connection_loss_work); mutex_lock(&wl->mutex); } @@ -2468,8 +2645,7 @@ static int wl12xx_op_change_interface(struct ieee80211_hw *hw, return ret; } -static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif, - bool set_assoc) +static int wlcore_join(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret; bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS); @@ -2489,18 +2665,111 @@ static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif, /* clear encryption type */ wlvif->encryption_type = KEY_NONE; - if (set_assoc) - set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags); - if (is_ibss) ret = wl12xx_cmd_role_start_ibss(wl, wlvif); - else + else { + if (wl->quirks & WLCORE_QUIRK_START_STA_FAILS) { + /* + * TODO: this is an ugly workaround for wl12xx fw + * bug - we are not able to tx/rx after the first + * start_sta, so make dummy start+stop calls, + * and then call start_sta again. + * this should be fixed in the fw. + */ + wl12xx_cmd_role_start_sta(wl, wlvif); + wl12xx_cmd_role_stop_sta(wl, wlvif); + } + ret = wl12xx_cmd_role_start_sta(wl, wlvif); + } + + return ret; +} + +static int wl1271_ssid_set(struct wl12xx_vif *wlvif, struct sk_buff *skb, + int offset) +{ + u8 ssid_len; + const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset, + skb->len - offset); + + if (!ptr) { + wl1271_error("No SSID in IEs!"); + return -ENOENT; + } + + ssid_len = ptr[1]; + if (ssid_len > IEEE80211_MAX_SSID_LEN) { + wl1271_error("SSID is too long!"); + return -EINVAL; + } + + wlvif->ssid_len = ssid_len; + memcpy(wlvif->ssid, ptr+2, ssid_len); + return 0; +} + +static int wlcore_set_ssid(struct wl1271 *wl, struct wl12xx_vif *wlvif) +{ + struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); + struct sk_buff *skb; + int ieoffset; + + /* we currently only support setting the ssid from the ap probe req */ + if (wlvif->bss_type != BSS_TYPE_STA_BSS) + return -EINVAL; + + skb = ieee80211_ap_probereq_get(wl->hw, vif); + if (!skb) + return -EINVAL; + + ieoffset = offsetof(struct ieee80211_mgmt, + u.probe_req.variable); + wl1271_ssid_set(wlvif, skb, ieoffset); + dev_kfree_skb(skb); + + return 0; +} + +static int wlcore_set_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct ieee80211_bss_conf *bss_conf, + u32 sta_rate_set) +{ + int ieoffset; + int ret; + + wlvif->aid = bss_conf->aid; + wlvif->channel_type = cfg80211_get_chandef_type(&bss_conf->chandef); + wlvif->beacon_int = bss_conf->beacon_int; + wlvif->wmm_enabled = bss_conf->qos; + + set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags); + + /* + * 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, wlvif, wlvif->aid); if (ret < 0) - goto out; + return ret; - if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) - goto out; + /* + * Get a template for hardware connection maintenance + */ + dev_kfree_skb(wlvif->probereq); + wlvif->probereq = wl1271_cmd_build_ap_probe_req(wl, + wlvif, + NULL); + ieoffset = offsetof(struct ieee80211_mgmt, + u.probe_req.variable); + wl1271_ssid_set(wlvif, wlvif->probereq, ieoffset); + + /* enable the connection monitoring feature */ + ret = wl1271_acx_conn_monit_params(wl, wlvif, true); + if (ret < 0) + return ret; /* * The join command disable the keep-alive mode, shut down its process, @@ -2510,35 +2779,83 @@ static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif, */ ret = wl1271_acx_keep_alive_mode(wl, wlvif, true); if (ret < 0) - goto out; + return ret; ret = wl1271_acx_aid(wl, wlvif, wlvif->aid); if (ret < 0) - goto out; + return ret; ret = wl12xx_cmd_build_klv_null_data(wl, wlvif); if (ret < 0) - goto out; + return ret; ret = wl1271_acx_keep_alive_config(wl, wlvif, wlvif->sta.klv_template_id, ACX_KEEP_ALIVE_TPL_VALID); if (ret < 0) - goto out; + return ret; + + /* + * The default fw psm configuration is AUTO, while mac80211 default + * setting is off (ACTIVE), so sync the fw with the correct value. + */ + ret = wl1271_ps_set_mode(wl, wlvif, STATION_ACTIVE_MODE); + if (ret < 0) + return ret; + + if (sta_rate_set) { + wlvif->rate_set = + wl1271_tx_enabled_rates_get(wl, + sta_rate_set, + wlvif->band); + ret = wl1271_acx_sta_rate_policies(wl, wlvif); + if (ret < 0) + return ret; + } -out: return ret; } -static int wl1271_unjoin(struct wl1271 *wl, struct wl12xx_vif *wlvif) +static int wlcore_unset_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret; + bool sta = wlvif->bss_type == BSS_TYPE_STA_BSS; + + /* make sure we are connected (sta) joined */ + if (sta && + !test_and_clear_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) + return false; + + /* make sure we are joined (ibss) */ + if (!sta && + test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags)) + return false; + + if (sta) { + /* use defaults when not associated */ + wlvif->aid = 0; + + /* free probe-request template */ + dev_kfree_skb(wlvif->probereq); + wlvif->probereq = NULL; + + /* disable connection monitor features */ + ret = wl1271_acx_conn_monit_params(wl, wlvif, false); + if (ret < 0) + return ret; + + /* Disable the keep-alive feature */ + ret = wl1271_acx_keep_alive_mode(wl, wlvif, false); + if (ret < 0) + return ret; + } if (test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags)) { struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); - wl12xx_cmd_stop_channel_switch(wl); + wl12xx_cmd_stop_channel_switch(wl, wlvif); ieee80211_chswitch_done(vif, false); + cancel_delayed_work(&wlvif->channel_switch_work); } /* invalidate keep-alive template */ @@ -2546,17 +2863,11 @@ static int wl1271_unjoin(struct wl1271 *wl, struct wl12xx_vif *wlvif) wlvif->sta.klv_template_id, ACX_KEEP_ALIVE_TPL_INVALID); - /* to stop listening to a channel, we disconnect */ - ret = wl12xx_cmd_role_stop_sta(wl, wlvif); - if (ret < 0) - goto out; - /* reset TX security counters on a clean disconnect */ wlvif->tx_security_last_seq_lsb = 0; wlvif->tx_security_seq = 0; -out: - return ret; + return 0; } static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif) @@ -2565,147 +2876,10 @@ static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif) wlvif->rate_set = wlvif->basic_rate_set; } -static int wl1271_sta_handle_idle(struct wl1271 *wl, struct wl12xx_vif *wlvif, - bool idle) -{ - int ret; - bool cur_idle = !test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags); - - if (idle == cur_idle) - return 0; - - if (idle) { - /* no need to croc if we weren't busy (e.g. during boot) */ - if (wl12xx_dev_role_started(wlvif)) { - ret = wl12xx_stop_dev(wl, wlvif); - if (ret < 0) - goto out; - } - wlvif->rate_set = - wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); - ret = wl1271_acx_sta_rate_policies(wl, wlvif); - if (ret < 0) - goto out; - clear_bit(WLVIF_FLAG_IN_USE, &wlvif->flags); - } else { - /* The current firmware only supports sched_scan in idle */ - if (wl->sched_scanning) { - wl1271_scan_sched_scan_stop(wl, wlvif); - ieee80211_sched_scan_stopped(wl->hw); - } - - ret = wl12xx_start_dev(wl, wlvif); - if (ret < 0) - goto out; - set_bit(WLVIF_FLAG_IN_USE, &wlvif->flags); - } - -out: - return ret; -} - static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_conf *conf, u32 changed) { - bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS); - int channel, ret; - - channel = ieee80211_frequency_to_channel(conf->channel->center_freq); - - /* if the channel changes while joined, join again */ - if (changed & IEEE80211_CONF_CHANGE_CHANNEL && - ((wlvif->band != conf->channel->band) || - (wlvif->channel != channel) || - (wlvif->channel_type != conf->channel_type))) { - /* send all pending packets */ - ret = wlcore_tx_work_locked(wl); - if (ret < 0) - return ret; - - wlvif->band = conf->channel->band; - wlvif->channel = channel; - wlvif->channel_type = conf->channel_type; - - if (is_ap) { - wl1271_set_band_rate(wl, wlvif); - ret = wl1271_init_ap_rates(wl, wlvif); - if (ret < 0) - wl1271_error("AP rate policy change failed %d", - ret); - } else { - /* - * FIXME: the mac80211 should really provide a fixed - * rate to use here. for now, just use the smallest - * possible rate for the band as a fixed rate for - * association frames and other control messages. - */ - if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) - wl1271_set_band_rate(wl, wlvif); - - wlvif->basic_rate = - wl1271_tx_min_rate_get(wl, - wlvif->basic_rate_set); - ret = wl1271_acx_sta_rate_policies(wl, wlvif); - if (ret < 0) - wl1271_warning("rate policy for channel " - "failed %d", ret); - - /* - * change the ROC channel. do it only if we are - * not idle. otherwise, CROC will be called - * anyway. - */ - if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, - &wlvif->flags) && - wl12xx_dev_role_started(wlvif) && - !(conf->flags & IEEE80211_CONF_IDLE)) { - ret = wl12xx_stop_dev(wl, wlvif); - if (ret < 0) - return ret; - - ret = wl12xx_start_dev(wl, wlvif); - if (ret < 0) - return ret; - } - } - } - - if ((changed & IEEE80211_CONF_CHANGE_PS) && !is_ap) { - - if ((conf->flags & IEEE80211_CONF_PS) && - test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) && - !test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) { - - int ps_mode; - char *ps_mode_str; - - if (wl->conf.conn.forced_ps) { - ps_mode = STATION_POWER_SAVE_MODE; - ps_mode_str = "forced"; - } else { - ps_mode = STATION_AUTO_PS_MODE; - ps_mode_str = "auto"; - } - - wl1271_debug(DEBUG_PSM, "%s ps enabled", ps_mode_str); - - ret = wl1271_ps_set_mode(wl, wlvif, ps_mode); - - if (ret < 0) - wl1271_warning("enter %s ps failed %d", - ps_mode_str, ret); - - } else if (!(conf->flags & IEEE80211_CONF_PS) && - test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) { - - wl1271_debug(DEBUG_PSM, "auto ps disabled"); - - ret = wl1271_ps_set_mode(wl, wlvif, - STATION_ACTIVE_MODE); - if (ret < 0) - wl1271_warning("exit auto ps failed %d", ret); - } - } + int ret; if (conf->power_level != wlvif->power_level) { ret = wl1271_acx_tx_power(wl, wlvif, conf->power_level); @@ -2723,37 +2897,17 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif; struct ieee80211_conf *conf = &hw->conf; - int channel, ret = 0; - - channel = ieee80211_frequency_to_channel(conf->channel->center_freq); + int ret = 0; - wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s" + wl1271_debug(DEBUG_MAC80211, "mac80211 config psm %s power %d %s" " changed 0x%x", - channel, conf->flags & IEEE80211_CONF_PS ? "on" : "off", conf->power_level, conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use", changed); - /* - * mac80211 will go to idle nearly immediately after transmitting some - * frames, such as the deauth. To make sure those frames reach the air, - * wait here until the TX queue is fully flushed. - */ - if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || - ((changed & IEEE80211_CONF_CHANGE_IDLE) && - (conf->flags & IEEE80211_CONF_IDLE))) - wl1271_tx_flush(wl); - mutex_lock(&wl->mutex); - /* we support configuring the channel and band even while off */ - if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { - wl->band = conf->channel->band; - wl->channel = channel; - wl->channel_type = conf->channel_type; - } - if (changed & IEEE80211_CONF_CHANGE_POWER) wl->power_level = conf->power_level; @@ -3073,10 +3227,7 @@ static int wlcore_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, * stop the queues and flush to ensure the next packets are * in sync with FW spare block accounting */ - mutex_lock(&wl->mutex); wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_SPARE_BLK); - mutex_unlock(&wl->mutex); - wl1271_tx_flush(wl); } @@ -3202,6 +3353,29 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd, } EXPORT_SYMBOL_GPL(wlcore_set_key); +void wlcore_regdomain_config(struct wl1271 *wl) +{ + int ret; + + if (!(wl->quirks & WLCORE_QUIRK_REGDOMAIN_CONF)) + return; + + mutex_lock(&wl->mutex); + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + ret = wlcore_cmd_regdomain_config_locked(wl); + if (ret < 0) { + wl12xx_queue_recovery_work(wl); + goto out; + } + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); +} + static int wl1271_op_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_scan_request *req) @@ -3241,7 +3415,7 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw, goto out_sleep; } - ret = wl1271_scan(hw->priv, vif, ssid, len, req); + ret = wlcore_scan(hw->priv, vif, ssid, len, req); out_sleep: wl1271_ps_elp_sleep(wl); out: @@ -3254,6 +3428,7 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct wl1271 *wl = hw->priv; + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 cancel hw scan"); @@ -3271,7 +3446,7 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw, goto out; if (wl->scan.state != WL1271_SCAN_STATE_DONE) { - ret = wl1271_scan_stop(wl); + ret = wl->ops->scan_stop(wl, wlvif); if (ret < 0) goto out_sleep; } @@ -3284,7 +3459,7 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw, wl->scan.state = WL1271_SCAN_STATE_IDLE; memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); - wl->scan_vif = NULL; + wl->scan_wlvif = NULL; wl->scan.req = NULL; ieee80211_scan_completed(wl->hw, true); @@ -3318,15 +3493,11 @@ static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw, if (ret < 0) goto out; - ret = wl1271_scan_sched_scan_config(wl, wlvif, req, ies); + ret = wl->ops->sched_scan_start(wl, wlvif, req, ies); if (ret < 0) goto out_sleep; - ret = wl1271_scan_sched_scan_start(wl, wlvif); - if (ret < 0) - goto out_sleep; - - wl->sched_scanning = true; + wl->sched_vif = wlvif; out_sleep: wl1271_ps_elp_sleep(wl); @@ -3353,7 +3524,7 @@ static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw, if (ret < 0) goto out; - wl1271_scan_sched_scan_stop(wl, wlvif); + wl->ops->sched_scan_stop(wl, wlvif); wl1271_ps_elp_sleep(wl); out: @@ -3418,30 +3589,6 @@ out: return ret; } -static int wl1271_ssid_set(struct ieee80211_vif *vif, struct sk_buff *skb, - int offset) -{ - struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); - u8 ssid_len; - const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset, - skb->len - offset); - - if (!ptr) { - wl1271_error("No SSID in IEs!"); - return -ENOENT; - } - - ssid_len = ptr[1]; - if (ssid_len > IEEE80211_MAX_SSID_LEN) { - wl1271_error("SSID is too long!"); - return -EINVAL; - } - - wlvif->ssid_len = ssid_len; - memcpy(wlvif->ssid, ptr+2, ssid_len); - return 0; -} - static void wl12xx_remove_ie(struct sk_buff *skb, u8 eid, int ieoffset) { int len; @@ -3622,7 +3769,7 @@ static int wlcore_set_beacon_template(struct wl1271 *wl, wl1271_debug(DEBUG_MASTER, "beacon updated"); - ret = wl1271_ssid_set(vif, beacon, ieoffset); + ret = wl1271_ssid_set(wlvif, beacon, ieoffset); if (ret < 0) { dev_kfree_skb(beacon); goto out; @@ -3639,6 +3786,12 @@ static int wlcore_set_beacon_template(struct wl1271 *wl, goto out; } + wlvif->wmm_enabled = + cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WMM, + beacon->data + ieoffset, + beacon->len - ieoffset); + /* * In case we already have a probe-resp beacon set explicitly * by usermode, don't use the beacon data. @@ -3692,7 +3845,7 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl, bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS); int ret = 0; - if ((changed & BSS_CHANGED_BEACON_INT)) { + if (changed & BSS_CHANGED_BEACON_INT) { wl1271_debug(DEBUG_MASTER, "beacon interval updated: %d", bss_conf->beacon_int); @@ -3705,7 +3858,7 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl, wl1271_ap_set_probe_resp_tmpl(wl, rate, vif); } - if ((changed & BSS_CHANGED_BEACON)) { + if (changed & BSS_CHANGED_BEACON) { ret = wlcore_set_beacon_template(wl, vif, is_ap); if (ret < 0) goto out; @@ -3726,7 +3879,7 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int ret = 0; - if ((changed & BSS_CHANGED_BASIC_RATES)) { + if (changed & BSS_CHANGED_BASIC_RATES) { u32 rates = bss_conf->basic_rates; wlvif->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates, @@ -3757,7 +3910,7 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl, if (ret < 0) goto out; - if ((changed & BSS_CHANGED_BEACON_ENABLED)) { + if (changed & BSS_CHANGED_BEACON_ENABLED) { if (bss_conf->enable_beacon) { if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) { ret = wl12xx_cmd_role_start_ap(wl, wlvif); @@ -3804,6 +3957,79 @@ out: return; } +static int wlcore_set_bssid(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct ieee80211_bss_conf *bss_conf, + u32 sta_rate_set) +{ + u32 rates; + int ret; + + wl1271_debug(DEBUG_MAC80211, + "changed_bssid: %pM, aid: %d, bcn_int: %d, brates: 0x%x sta_rate_set: 0x%x", + bss_conf->bssid, bss_conf->aid, + bss_conf->beacon_int, + bss_conf->basic_rates, sta_rate_set); + + wlvif->beacon_int = bss_conf->beacon_int; + rates = bss_conf->basic_rates; + wlvif->basic_rate_set = + wl1271_tx_enabled_rates_get(wl, rates, + wlvif->band); + wlvif->basic_rate = + wl1271_tx_min_rate_get(wl, + wlvif->basic_rate_set); + + if (sta_rate_set) + wlvif->rate_set = + wl1271_tx_enabled_rates_get(wl, + sta_rate_set, + wlvif->band); + + /* we only support sched_scan while not connected */ + if (wl->sched_vif == wlvif) + wl->ops->sched_scan_stop(wl, wlvif); + + ret = wl1271_acx_sta_rate_policies(wl, wlvif); + if (ret < 0) + return ret; + + ret = wl12xx_cmd_build_null_data(wl, wlvif); + if (ret < 0) + return ret; + + ret = wl1271_build_qos_null_data(wl, wl12xx_wlvif_to_vif(wlvif)); + if (ret < 0) + return ret; + + wlcore_set_ssid(wl, wlvif); + + set_bit(WLVIF_FLAG_IN_USE, &wlvif->flags); + + return 0; +} + +static int wlcore_clear_bssid(struct wl1271 *wl, struct wl12xx_vif *wlvif) +{ + int ret; + + /* revert back to minimum rates for the current band */ + wl1271_set_band_rate(wl, wlvif); + wlvif->basic_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); + + ret = wl1271_acx_sta_rate_policies(wl, wlvif); + if (ret < 0) + return ret; + + if (wlvif->bss_type == BSS_TYPE_STA_BSS && + test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) { + ret = wl12xx_cmd_role_stop_sta(wl, wlvif); + if (ret < 0) + return ret; + } + + clear_bit(WLVIF_FLAG_IN_USE, &wlvif->flags); + return 0; +} /* STA/IBSS mode changes */ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, struct ieee80211_vif *vif, @@ -3811,7 +4037,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, u32 changed) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); - bool do_join = false, set_assoc = false; + bool do_join = false; bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS); bool ibss_joined = false; u32 sta_rate_set = 0; @@ -3832,9 +4058,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, set_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags); ibss_joined = true; } else { - if (test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED, - &wlvif->flags)) - wl1271_unjoin(wl, wlvif); + wlcore_unset_assoc(wl, wlvif); + wl12xx_cmd_role_stop_sta(wl, wlvif); } } @@ -3852,13 +4077,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, do_join = true; } - if (changed & BSS_CHANGED_IDLE && !is_ibss) { - ret = wl1271_sta_handle_idle(wl, wlvif, bss_conf->idle); - if (ret < 0) - wl1271_warning("idle mode change failed %d", ret); - } - - if ((changed & BSS_CHANGED_CQM)) { + if (changed & BSS_CHANGED_CQM) { bool enable = false; if (bss_conf->cqm_rssi_thold) enable = true; @@ -3870,150 +4089,39 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, wlvif->rssi_thold = bss_conf->cqm_rssi_thold; } - if (changed & BSS_CHANGED_BSSID) - if (!is_zero_ether_addr(bss_conf->bssid)) { - ret = wl12xx_cmd_build_null_data(wl, wlvif); - if (ret < 0) - goto out; - - ret = wl1271_build_qos_null_data(wl, vif); - if (ret < 0) - goto out; - } - - if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_HT)) { + if (changed & (BSS_CHANGED_BSSID | BSS_CHANGED_HT | + BSS_CHANGED_ASSOC)) { rcu_read_lock(); sta = ieee80211_find_sta(vif, bss_conf->bssid); - if (!sta) - goto sta_not_found; - - /* save the supp_rates of the ap */ - sta_rate_set = sta->supp_rates[wl->hw->conf.channel->band]; - if (sta->ht_cap.ht_supported) - sta_rate_set |= - (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET) | - (sta->ht_cap.mcs.rx_mask[1] << HW_MIMO_RATES_OFFSET); - sta_ht_cap = sta->ht_cap; - sta_exists = true; - -sta_not_found: + if (sta) { + u8 *rx_mask = sta->ht_cap.mcs.rx_mask; + + /* save the supp_rates of the ap */ + sta_rate_set = sta->supp_rates[wlvif->band]; + if (sta->ht_cap.ht_supported) + sta_rate_set |= + (rx_mask[0] << HW_HT_RATES_OFFSET) | + (rx_mask[1] << HW_MIMO_RATES_OFFSET); + sta_ht_cap = sta->ht_cap; + sta_exists = true; + } + rcu_read_unlock(); } - if ((changed & BSS_CHANGED_ASSOC)) { - if (bss_conf->assoc) { - u32 rates; - int ieoffset; - wlvif->aid = bss_conf->aid; - wlvif->channel_type = - cfg80211_get_chandef_type(&bss_conf->chandef); - wlvif->beacon_int = bss_conf->beacon_int; - do_join = true; - set_assoc = true; - - /* - * use basic rates from AP, and determine lowest rate - * to use with control frames. - */ - rates = bss_conf->basic_rates; - wlvif->basic_rate_set = - wl1271_tx_enabled_rates_get(wl, rates, - wlvif->band); - wlvif->basic_rate = - wl1271_tx_min_rate_get(wl, - wlvif->basic_rate_set); - if (sta_rate_set) - wlvif->rate_set = - wl1271_tx_enabled_rates_get(wl, - sta_rate_set, - wlvif->band); - ret = wl1271_acx_sta_rate_policies(wl, wlvif); - if (ret < 0) - goto out; - - /* - * 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, wlvif, wlvif->aid); + if (changed & BSS_CHANGED_BSSID) { + if (!is_zero_ether_addr(bss_conf->bssid)) { + ret = wlcore_set_bssid(wl, wlvif, bss_conf, + sta_rate_set); if (ret < 0) goto out; - /* - * Get a template for hardware connection maintenance - */ - dev_kfree_skb(wlvif->probereq); - wlvif->probereq = wl1271_cmd_build_ap_probe_req(wl, - wlvif, - NULL); - ieoffset = offsetof(struct ieee80211_mgmt, - u.probe_req.variable); - wl1271_ssid_set(vif, wlvif->probereq, ieoffset); - - /* enable the connection monitoring feature */ - ret = wl1271_acx_conn_monit_params(wl, wlvif, true); - if (ret < 0) - goto out; + /* Need to update the BSSID (for filtering etc) */ + do_join = true; } else { - /* use defaults when not associated */ - bool was_assoc = - !!test_and_clear_bit(WLVIF_FLAG_STA_ASSOCIATED, - &wlvif->flags); - bool was_ifup = - !!test_and_clear_bit(WLVIF_FLAG_STA_STATE_SENT, - &wlvif->flags); - wlvif->aid = 0; - - /* free probe-request template */ - dev_kfree_skb(wlvif->probereq); - wlvif->probereq = NULL; - - /* revert back to minimum rates for the current band */ - wl1271_set_band_rate(wl, wlvif); - wlvif->basic_rate = - wl1271_tx_min_rate_get(wl, - wlvif->basic_rate_set); - ret = wl1271_acx_sta_rate_policies(wl, wlvif); - if (ret < 0) - goto out; - - /* disable connection monitor features */ - ret = wl1271_acx_conn_monit_params(wl, wlvif, false); - - /* Disable the keep-alive feature */ - ret = wl1271_acx_keep_alive_mode(wl, wlvif, false); + ret = wlcore_clear_bssid(wl, wlvif); if (ret < 0) goto out; - - /* restore the bssid filter and go to dummy bssid */ - if (was_assoc) { - /* - * we might have to disable roc, if there was - * no IF_OPER_UP notification. - */ - if (!was_ifup) { - ret = wl12xx_croc(wl, wlvif->role_id); - if (ret < 0) - goto out; - } - /* - * (we also need to disable roc in case of - * roaming on the same channel. until we will - * have a better flow...) - */ - if (test_bit(wlvif->dev_role_id, wl->roc_map)) { - ret = wl12xx_croc(wl, - wlvif->dev_role_id); - if (ret < 0) - goto out; - } - - wl1271_unjoin(wl, wlvif); - if (!bss_conf->idle) - wl12xx_start_dev(wl, wlvif); - } } } @@ -4043,71 +4151,87 @@ sta_not_found: goto out; if (do_join) { - ret = wl1271_join(wl, wlvif, set_assoc); + ret = wlcore_join(wl, wlvif); if (ret < 0) { wl1271_warning("cmd join failed %d", ret); goto out; } + } - /* ROC until connected (after EAPOL exchange) */ - if (!is_ibss) { - ret = wl12xx_roc(wl, wlvif, wlvif->role_id); + if (changed & BSS_CHANGED_ASSOC) { + if (bss_conf->assoc) { + ret = wlcore_set_assoc(wl, wlvif, bss_conf, + sta_rate_set); if (ret < 0) goto out; if (test_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags)) wl12xx_set_authorized(wl, wlvif); + } else { + wlcore_unset_assoc(wl, wlvif); } - /* - * stop device role if started (we might already be in - * STA/IBSS role). - */ - if (wl12xx_dev_role_started(wlvif)) { - ret = wl12xx_stop_dev(wl, wlvif); + } + + if (changed & BSS_CHANGED_PS) { + if ((bss_conf->ps) && + test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) && + !test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) { + int ps_mode; + char *ps_mode_str; + + if (wl->conf.conn.forced_ps) { + ps_mode = STATION_POWER_SAVE_MODE; + ps_mode_str = "forced"; + } else { + ps_mode = STATION_AUTO_PS_MODE; + ps_mode_str = "auto"; + } + + wl1271_debug(DEBUG_PSM, "%s ps enabled", ps_mode_str); + + ret = wl1271_ps_set_mode(wl, wlvif, ps_mode); if (ret < 0) - goto out; + wl1271_warning("enter %s ps failed %d", + ps_mode_str, ret); + } else if (!bss_conf->ps && + test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) { + wl1271_debug(DEBUG_PSM, "auto ps disabled"); + + ret = wl1271_ps_set_mode(wl, wlvif, + STATION_ACTIVE_MODE); + if (ret < 0) + wl1271_warning("exit auto ps failed %d", ret); } } /* Handle new association with HT. Do this after join. */ - if (sta_exists) { - if ((changed & BSS_CHANGED_HT) && - (bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT)) { - ret = wl1271_acx_set_ht_capabilities(wl, - &sta_ht_cap, - true, - wlvif->sta.hlid); - if (ret < 0) { - wl1271_warning("Set ht cap true failed %d", - ret); - goto out; - } + if (sta_exists && + (changed & BSS_CHANGED_HT)) { + bool enabled = + bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT; + + ret = wlcore_hw_set_peer_cap(wl, + &sta_ht_cap, + enabled, + wlvif->rate_set, + wlvif->sta.hlid); + if (ret < 0) { + wl1271_warning("Set ht cap failed %d", ret); + goto out; + } - /* handle new association without HT and disassociation */ - else if (changed & BSS_CHANGED_ASSOC) { - ret = wl1271_acx_set_ht_capabilities(wl, - &sta_ht_cap, - false, - wlvif->sta.hlid); + + if (enabled) { + ret = wl1271_acx_set_ht_information(wl, wlvif, + bss_conf->ht_operation_mode); if (ret < 0) { - wl1271_warning("Set ht cap false failed %d", + wl1271_warning("Set ht information failed %d", ret); goto out; } } } - /* Handle HT information change. Done after join. */ - if ((changed & BSS_CHANGED_HT) && - (bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT)) { - ret = wl1271_acx_set_ht_information(wl, wlvif, - bss_conf->ht_operation_mode); - if (ret < 0) { - wl1271_warning("Set ht information failed %d", ret); - goto out; - } - } - /* Handle arp filtering. Done after join. */ if ((changed & BSS_CHANGED_ARP_FILTER) || (!is_ibss && (changed & BSS_CHANGED_QOS))) { @@ -4115,8 +4239,7 @@ sta_not_found: wlvif->sta.qos = bss_conf->qos; WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS); - if (bss_conf->arp_addr_cnt == 1 && - bss_conf->arp_filter_enabled) { + if (bss_conf->arp_addr_cnt == 1 && bss_conf->assoc) { wlvif->ip_addr = addr; /* * The template should have been configured only upon @@ -4157,15 +4280,15 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS); int ret; - wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed 0x%x", - (int)changed); + wl1271_debug(DEBUG_MAC80211, "mac80211 bss info role %d changed 0x%x", + wlvif->role_id, (int)changed); /* * make sure to cancel pending disconnections if our association * state changed */ if (!is_ap && (changed & BSS_CHANGED_ASSOC)) - cancel_delayed_work_sync(&wl->connection_loss_work); + cancel_delayed_work_sync(&wlvif->connection_loss_work); if (is_ap && (changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) @@ -4194,6 +4317,76 @@ out: mutex_unlock(&wl->mutex); } +static int wlcore_op_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + wl1271_debug(DEBUG_MAC80211, "mac80211 add chanctx %d (type %d)", + ieee80211_frequency_to_channel(ctx->def.chan->center_freq), + cfg80211_get_chandef_type(&ctx->def)); + return 0; +} + +static void wlcore_op_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + wl1271_debug(DEBUG_MAC80211, "mac80211 remove chanctx %d (type %d)", + ieee80211_frequency_to_channel(ctx->def.chan->center_freq), + cfg80211_get_chandef_type(&ctx->def)); +} + +static void wlcore_op_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx, + u32 changed) +{ + wl1271_debug(DEBUG_MAC80211, + "mac80211 change chanctx %d (type %d) changed 0x%x", + ieee80211_frequency_to_channel(ctx->def.chan->center_freq), + cfg80211_get_chandef_type(&ctx->def), changed); +} + +static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) +{ + struct wl1271 *wl = hw->priv; + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); + int channel = ieee80211_frequency_to_channel( + ctx->def.chan->center_freq); + + wl1271_debug(DEBUG_MAC80211, + "mac80211 assign chanctx (role %d) %d (type %d)", + wlvif->role_id, channel, cfg80211_get_chandef_type(&ctx->def)); + + mutex_lock(&wl->mutex); + + wlvif->band = ctx->def.chan->band; + wlvif->channel = channel; + wlvif->channel_type = cfg80211_get_chandef_type(&ctx->def); + + /* update default rates according to the band */ + wl1271_set_band_rate(wl, wlvif); + + mutex_unlock(&wl->mutex); + + return 0; +} + +static void wlcore_op_unassign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) +{ + struct wl1271 *wl = hw->priv; + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); + + wl1271_debug(DEBUG_MAC80211, + "mac80211 unassign chanctx (role %d) %d (type %d)", + wlvif->role_id, + ieee80211_frequency_to_channel(ctx->def.chan->center_freq), + cfg80211_get_chandef_type(&ctx->def)); + + wl1271_tx_flush(wl); +} + static int wl1271_op_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) @@ -4321,8 +4514,6 @@ void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid) return; clear_bit(hlid, wlvif->ap.sta_hlid_map); - memset(wl->links[hlid].addr, 0, ETH_ALEN); - wl->links[hlid].ba_bitmap = 0; __clear_bit(hlid, &wl->ap_ps_map); __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); wl12xx_free_link(wl, wlvif, &hlid); @@ -4382,6 +4573,45 @@ static int wl12xx_sta_remove(struct wl1271 *wl, return ret; } +static void wlcore_roc_if_possible(struct wl1271 *wl, + struct wl12xx_vif *wlvif) +{ + if (find_first_bit(wl->roc_map, + WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES) + return; + + if (WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID)) + return; + + wl12xx_roc(wl, wlvif, wlvif->role_id, wlvif->band, wlvif->channel); +} + +static void wlcore_update_inconn_sta(struct wl1271 *wl, + struct wl12xx_vif *wlvif, + struct wl1271_station *wl_sta, + bool in_connection) +{ + if (in_connection) { + if (WARN_ON(wl_sta->in_connection)) + return; + wl_sta->in_connection = true; + if (!wlvif->inconn_count++) + wlcore_roc_if_possible(wl, wlvif); + } else { + if (!wl_sta->in_connection) + return; + + wl_sta->in_connection = false; + wlvif->inconn_count--; + if (WARN_ON(wlvif->inconn_count < 0)) + return; + + if (!wlvif->inconn_count) + if (test_bit(wlvif->role_id, wl->roc_map)) + wl12xx_croc(wl, wlvif->role_id); + } +} + static int wl12xx_update_sta_state(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_sta *sta, @@ -4400,8 +4630,13 @@ static int wl12xx_update_sta_state(struct wl1271 *wl, /* Add station (AP mode) */ if (is_ap && old_state == IEEE80211_STA_NOTEXIST && - new_state == IEEE80211_STA_NONE) - return wl12xx_sta_add(wl, wlvif, sta); + new_state == IEEE80211_STA_NONE) { + ret = wl12xx_sta_add(wl, wlvif, sta); + if (ret) + return ret; + + wlcore_update_inconn_sta(wl, wlvif, wl_sta, true); + } /* Remove station (AP mode) */ if (is_ap && @@ -4409,35 +4644,59 @@ static int wl12xx_update_sta_state(struct wl1271 *wl, new_state == IEEE80211_STA_NOTEXIST) { /* must not fail */ wl12xx_sta_remove(wl, wlvif, sta); - return 0; + + wlcore_update_inconn_sta(wl, wlvif, wl_sta, false); } /* Authorize station (AP mode) */ if (is_ap && new_state == IEEE80211_STA_AUTHORIZED) { - ret = wl12xx_cmd_set_peer_state(wl, hlid); + ret = wl12xx_cmd_set_peer_state(wl, wlvif, hlid); if (ret < 0) return ret; ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true, hlid); - return ret; + if (ret) + return ret; + + wlcore_update_inconn_sta(wl, wlvif, wl_sta, false); } /* Authorize station */ if (is_sta && new_state == IEEE80211_STA_AUTHORIZED) { set_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags); - return wl12xx_set_authorized(wl, wlvif); + ret = wl12xx_set_authorized(wl, wlvif); + if (ret) + return ret; } if (is_sta && old_state == IEEE80211_STA_AUTHORIZED && new_state == IEEE80211_STA_ASSOC) { clear_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags); - return 0; + clear_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags); } + /* clear ROCs on failure or authorization */ + if (is_sta && + (new_state == IEEE80211_STA_AUTHORIZED || + new_state == IEEE80211_STA_NOTEXIST)) { + if (test_bit(wlvif->role_id, wl->roc_map)) + wl12xx_croc(wl, wlvif->role_id); + } + + if (is_sta && + old_state == IEEE80211_STA_NOTEXIST && + new_state == IEEE80211_STA_NONE) { + if (find_first_bit(wl->roc_map, + WL12XX_MAX_ROLES) >= WL12XX_MAX_ROLES) { + WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID); + wl12xx_roc(wl, wlvif, wlvif->role_id, + wlvif->band, wlvif->channel); + } + } return 0; } @@ -4502,18 +4761,18 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw, if (wlvif->bss_type == BSS_TYPE_STA_BSS) { hlid = wlvif->sta.hlid; - ba_bitmap = &wlvif->sta.ba_rx_bitmap; } else if (wlvif->bss_type == BSS_TYPE_AP_BSS) { struct wl1271_station *wl_sta; wl_sta = (struct wl1271_station *)sta->drv_priv; hlid = wl_sta->hlid; - ba_bitmap = &wl->links[hlid].ba_bitmap; } else { ret = -EINVAL; goto out; } + ba_bitmap = &wl->links[hlid].ba_bitmap; + ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; @@ -4575,7 +4834,9 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw, * Falling break here on purpose for all TX APDU commands. */ case IEEE80211_AMPDU_TX_START: - case IEEE80211_AMPDU_TX_STOP: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: case IEEE80211_AMPDU_TX_OPERATIONAL: ret = -EINVAL; break; @@ -4665,12 +4926,23 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw, /* TODO: change mac80211 to pass vif as param */ wl12xx_for_each_wlvif_sta(wl, wlvif) { - ret = wl12xx_cmd_channel_switch(wl, wlvif, ch_switch); + unsigned long delay_usec; + + ret = wl->ops->channel_switch(wl, wlvif, ch_switch); + if (ret) + goto out_sleep; + + set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags); - if (!ret) - set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags); + /* indicate failure 5 seconds after channel switch time */ + delay_usec = ieee80211_tu_to_usec(wlvif->beacon_int) * + ch_switch->count; + ieee80211_queue_delayed_work(hw, &wlvif->channel_switch_work, + usecs_to_jiffies(delay_usec) + + msecs_to_jiffies(5000)); } +out_sleep: wl1271_ps_elp_sleep(wl); out: @@ -4684,6 +4956,144 @@ static void wlcore_op_flush(struct ieee80211_hw *hw, bool drop) wl1271_tx_flush(wl); } +static int wlcore_op_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel *chan, + int duration) +{ + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); + struct wl1271 *wl = hw->priv; + int channel, ret = 0; + + channel = ieee80211_frequency_to_channel(chan->center_freq); + + wl1271_debug(DEBUG_MAC80211, "mac80211 roc %d (%d)", + channel, wlvif->role_id); + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state != WLCORE_STATE_ON)) + goto out; + + /* return EBUSY if we can't ROC right now */ + if (WARN_ON(wl->roc_vif || + find_first_bit(wl->roc_map, + WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES)) { + ret = -EBUSY; + goto out; + } + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + ret = wl12xx_start_dev(wl, wlvif, chan->band, channel); + if (ret < 0) + goto out_sleep; + + wl->roc_vif = vif; + ieee80211_queue_delayed_work(hw, &wl->roc_complete_work, + msecs_to_jiffies(duration)); +out_sleep: + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); + return ret; +} + +static int __wlcore_roc_completed(struct wl1271 *wl) +{ + struct wl12xx_vif *wlvif; + int ret; + + /* already completed */ + if (unlikely(!wl->roc_vif)) + return 0; + + wlvif = wl12xx_vif_to_data(wl->roc_vif); + + if (!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)) + return -EBUSY; + + ret = wl12xx_stop_dev(wl, wlvif); + if (ret < 0) + return ret; + + wl->roc_vif = NULL; + + return 0; +} + +static int wlcore_roc_completed(struct wl1271 *wl) +{ + int ret; + + wl1271_debug(DEBUG_MAC80211, "roc complete"); + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state != WLCORE_STATE_ON)) { + ret = -EBUSY; + goto out; + } + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + ret = __wlcore_roc_completed(wl); + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); + + return ret; +} + +static void wlcore_roc_complete_work(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wl1271 *wl; + int ret; + + dwork = container_of(work, struct delayed_work, work); + wl = container_of(dwork, struct wl1271, roc_complete_work); + + ret = wlcore_roc_completed(wl); + if (!ret) + ieee80211_remain_on_channel_expired(wl->hw); +} + +static int wlcore_op_cancel_remain_on_channel(struct ieee80211_hw *hw) +{ + struct wl1271 *wl = hw->priv; + + wl1271_debug(DEBUG_MAC80211, "mac80211 croc"); + + /* TODO: per-vif */ + wl1271_tx_flush(wl); + + /* + * we can't just flush_work here, because it might deadlock + * (as we might get called from the same workqueue) + */ + cancel_delayed_work_sync(&wl->roc_complete_work); + wlcore_roc_completed(wl); + + return 0; +} + +static void wlcore_op_sta_rc_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u32 changed) +{ + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); + struct wl1271 *wl = hw->priv; + + wlcore_hw_sta_rc_update(wl, wlvif, sta, changed); +} + static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw) { struct wl1271 *wl = hw->priv; @@ -4747,20 +5157,20 @@ static struct ieee80211_rate wl1271_rates[] = { /* can't be const, mac80211 writes to this */ static struct ieee80211_channel wl1271_channels[] = { - { .hw_value = 1, .center_freq = 2412, .max_power = 25 }, - { .hw_value = 2, .center_freq = 2417, .max_power = 25 }, - { .hw_value = 3, .center_freq = 2422, .max_power = 25 }, - { .hw_value = 4, .center_freq = 2427, .max_power = 25 }, - { .hw_value = 5, .center_freq = 2432, .max_power = 25 }, - { .hw_value = 6, .center_freq = 2437, .max_power = 25 }, - { .hw_value = 7, .center_freq = 2442, .max_power = 25 }, - { .hw_value = 8, .center_freq = 2447, .max_power = 25 }, - { .hw_value = 9, .center_freq = 2452, .max_power = 25 }, - { .hw_value = 10, .center_freq = 2457, .max_power = 25 }, - { .hw_value = 11, .center_freq = 2462, .max_power = 25 }, - { .hw_value = 12, .center_freq = 2467, .max_power = 25 }, - { .hw_value = 13, .center_freq = 2472, .max_power = 25 }, - { .hw_value = 14, .center_freq = 2484, .max_power = 25 }, + { .hw_value = 1, .center_freq = 2412, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 2, .center_freq = 2417, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 3, .center_freq = 2422, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 4, .center_freq = 2427, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 5, .center_freq = 2432, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 6, .center_freq = 2437, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 7, .center_freq = 2442, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 8, .center_freq = 2447, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 9, .center_freq = 2452, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 10, .center_freq = 2457, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 11, .center_freq = 2462, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 12, .center_freq = 2467, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 13, .center_freq = 2472, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 14, .center_freq = 2484, .max_power = WLCORE_MAX_TXPWR }, }; /* can't be const, mac80211 writes to this */ @@ -4801,40 +5211,40 @@ static struct ieee80211_rate wl1271_rates_5ghz[] = { /* 5 GHz band channels for WL1273 */ static struct ieee80211_channel wl1271_channels_5ghz[] = { - { .hw_value = 7, .center_freq = 5035, .max_power = 25 }, - { .hw_value = 8, .center_freq = 5040, .max_power = 25 }, - { .hw_value = 9, .center_freq = 5045, .max_power = 25 }, - { .hw_value = 11, .center_freq = 5055, .max_power = 25 }, - { .hw_value = 12, .center_freq = 5060, .max_power = 25 }, - { .hw_value = 16, .center_freq = 5080, .max_power = 25 }, - { .hw_value = 34, .center_freq = 5170, .max_power = 25 }, - { .hw_value = 36, .center_freq = 5180, .max_power = 25 }, - { .hw_value = 38, .center_freq = 5190, .max_power = 25 }, - { .hw_value = 40, .center_freq = 5200, .max_power = 25 }, - { .hw_value = 42, .center_freq = 5210, .max_power = 25 }, - { .hw_value = 44, .center_freq = 5220, .max_power = 25 }, - { .hw_value = 46, .center_freq = 5230, .max_power = 25 }, - { .hw_value = 48, .center_freq = 5240, .max_power = 25 }, - { .hw_value = 52, .center_freq = 5260, .max_power = 25 }, - { .hw_value = 56, .center_freq = 5280, .max_power = 25 }, - { .hw_value = 60, .center_freq = 5300, .max_power = 25 }, - { .hw_value = 64, .center_freq = 5320, .max_power = 25 }, - { .hw_value = 100, .center_freq = 5500, .max_power = 25 }, - { .hw_value = 104, .center_freq = 5520, .max_power = 25 }, - { .hw_value = 108, .center_freq = 5540, .max_power = 25 }, - { .hw_value = 112, .center_freq = 5560, .max_power = 25 }, - { .hw_value = 116, .center_freq = 5580, .max_power = 25 }, - { .hw_value = 120, .center_freq = 5600, .max_power = 25 }, - { .hw_value = 124, .center_freq = 5620, .max_power = 25 }, - { .hw_value = 128, .center_freq = 5640, .max_power = 25 }, - { .hw_value = 132, .center_freq = 5660, .max_power = 25 }, - { .hw_value = 136, .center_freq = 5680, .max_power = 25 }, - { .hw_value = 140, .center_freq = 5700, .max_power = 25 }, - { .hw_value = 149, .center_freq = 5745, .max_power = 25 }, - { .hw_value = 153, .center_freq = 5765, .max_power = 25 }, - { .hw_value = 157, .center_freq = 5785, .max_power = 25 }, - { .hw_value = 161, .center_freq = 5805, .max_power = 25 }, - { .hw_value = 165, .center_freq = 5825, .max_power = 25 }, + { .hw_value = 7, .center_freq = 5035, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 8, .center_freq = 5040, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 9, .center_freq = 5045, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 11, .center_freq = 5055, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 12, .center_freq = 5060, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 16, .center_freq = 5080, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 34, .center_freq = 5170, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 36, .center_freq = 5180, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 38, .center_freq = 5190, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 40, .center_freq = 5200, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 42, .center_freq = 5210, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 44, .center_freq = 5220, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 46, .center_freq = 5230, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 48, .center_freq = 5240, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 52, .center_freq = 5260, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 56, .center_freq = 5280, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 60, .center_freq = 5300, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 64, .center_freq = 5320, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 100, .center_freq = 5500, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 104, .center_freq = 5520, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 108, .center_freq = 5540, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 112, .center_freq = 5560, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 116, .center_freq = 5580, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 120, .center_freq = 5600, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 124, .center_freq = 5620, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 128, .center_freq = 5640, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 132, .center_freq = 5660, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 136, .center_freq = 5680, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 140, .center_freq = 5700, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 149, .center_freq = 5745, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 153, .center_freq = 5765, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 157, .center_freq = 5785, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 161, .center_freq = 5805, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 165, .center_freq = 5825, .max_power = WLCORE_MAX_TXPWR }, }; static struct ieee80211_supported_band wl1271_band_5ghz = { @@ -4875,6 +5285,14 @@ static const struct ieee80211_ops wl1271_ops = { .set_bitrate_mask = wl12xx_set_bitrate_mask, .channel_switch = wl12xx_op_channel_switch, .flush = wlcore_op_flush, + .remain_on_channel = wlcore_op_remain_on_channel, + .cancel_remain_on_channel = wlcore_op_cancel_remain_on_channel, + .add_chanctx = wlcore_op_add_chanctx, + .remove_chanctx = wlcore_op_remove_chanctx, + .change_chanctx = wlcore_op_change_chanctx, + .assign_vif_chanctx = wlcore_op_assign_vif_chanctx, + .unassign_vif_chanctx = wlcore_op_unassign_vif_chanctx, + .sta_rc_update = wlcore_op_sta_rc_update, CFG80211_TESTMODE_CMD(wl1271_tm_cmd) }; @@ -5044,34 +5462,6 @@ static struct bin_attribute fwlog_attr = { .read = wl1271_sysfs_read_fwlog, }; -static void wl1271_connection_loss_work(struct work_struct *work) -{ - struct delayed_work *dwork; - struct wl1271 *wl; - struct ieee80211_vif *vif; - struct wl12xx_vif *wlvif; - - dwork = container_of(work, struct delayed_work, work); - wl = container_of(dwork, struct wl1271, connection_loss_work); - - wl1271_info("Connection loss work."); - - mutex_lock(&wl->mutex); - - if (unlikely(wl->state != WLCORE_STATE_ON)) - goto out; - - /* Call mac80211 connection loss */ - wl12xx_for_each_wlvif_sta(wl, wlvif) { - if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) - goto out; - vif = wl12xx_wlvif_to_vif(wlvif); - ieee80211_connection_loss(vif); - } -out: - mutex_unlock(&wl->mutex); -} - static void wl12xx_derive_mac_addresses(struct wl1271 *wl, u32 oui, u32 nic) { int i; @@ -5117,7 +5507,7 @@ static int wl12xx_get_hw_info(struct wl1271 *wl) ret = wl12xx_set_power_on(wl); if (ret < 0) - goto out; + return ret; ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &wl->chip.id); if (ret < 0) @@ -5207,10 +5597,9 @@ static const struct ieee80211_iface_limit wlcore_iface_limits[] = { }, }; -static const struct ieee80211_iface_combination +static struct ieee80211_iface_combination wlcore_iface_combinations[] = { { - .num_different_channels = 1, .max_interfaces = 3, .limits = wlcore_iface_limits, .n_limits = ARRAY_SIZE(wlcore_iface_limits), @@ -5219,6 +5608,7 @@ wlcore_iface_combinations[] = { static int wl1271_init_ieee80211(struct wl1271 *wl) { + int i; static const u32 cipher_suites[] = { WLAN_CIPHER_SUITE_WEP40, WLAN_CIPHER_SUITE_WEP104, @@ -5249,7 +5639,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) IEEE80211_HW_AP_LINK_PS | IEEE80211_HW_AMPDU_AGGREGATION | IEEE80211_HW_TX_AMPDU_SETUP_IN_HW | - IEEE80211_HW_SCAN_WHILE_IDLE; + IEEE80211_HW_SCAN_WHILE_IDLE | + IEEE80211_HW_QUEUE_CONTROL; wl->hw->wiphy->cipher_suites = cipher_suites; wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); @@ -5271,6 +5662,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE - sizeof(struct ieee80211_header); + wl->hw->wiphy->max_remain_on_channel_duration = 5000; + wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; @@ -5279,6 +5672,22 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) ARRAY_SIZE(wl1271_channels_5ghz) > WL1271_MAX_CHANNELS); /* + * clear channel flags from the previous usage + * and restore max_power & max_antenna_gain values. + */ + for (i = 0; i < ARRAY_SIZE(wl1271_channels); i++) { + wl1271_band_2ghz.channels[i].flags = 0; + wl1271_band_2ghz.channels[i].max_power = WLCORE_MAX_TXPWR; + wl1271_band_2ghz.channels[i].max_antenna_gain = 0; + } + + for (i = 0; i < ARRAY_SIZE(wl1271_channels_5ghz); i++) { + wl1271_band_5ghz.channels[i].flags = 0; + wl1271_band_5ghz.channels[i].max_power = WLCORE_MAX_TXPWR; + wl1271_band_5ghz.channels[i].max_antenna_gain = 0; + } + + /* * We keep local copies of the band structs because we need to * modify them on a per-device basis. */ @@ -5298,7 +5707,14 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl->bands[IEEE80211_BAND_5GHZ]; - wl->hw->queues = 4; + /* + * allow 4 queues per mac address we support + + * 1 cab queue per mac + one global offchannel Tx queue + */ + wl->hw->queues = (NUM_TX_QUEUES + 1) * WLCORE_NUM_MAC_ADDRESSES + 1; + + /* the last queue is the offchannel queue */ + wl->hw->offchannel_tx_hw_queue = wl->hw->queues - 1; wl->hw->max_rates = 1; wl->hw->wiphy->reg_notifier = wl1271_reg_notify; @@ -5311,6 +5727,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; /* allowed interface combinations */ + wlcore_iface_combinations[0].num_different_channels = wl->num_channels; wl->hw->wiphy->iface_combinations = wlcore_iface_combinations; wl->hw->wiphy->n_iface_combinations = ARRAY_SIZE(wlcore_iface_combinations); @@ -5327,7 +5744,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) #define WL1271_DEFAULT_CHANNEL 0 -struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size) +struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size, + u32 mbox_size) { struct ieee80211_hw *hw; struct wl1271 *wl; @@ -5369,9 +5787,8 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size) INIT_WORK(&wl->tx_work, wl1271_tx_work); INIT_WORK(&wl->recovery_work, wl1271_recovery_work); INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work); + INIT_DELAYED_WORK(&wl->roc_complete_work, wlcore_roc_complete_work); INIT_DELAYED_WORK(&wl->tx_watchdog_work, wl12xx_tx_watchdog_work); - INIT_DELAYED_WORK(&wl->connection_loss_work, - wl1271_connection_loss_work); wl->freezable_wq = create_freezable_workqueue("wl12xx_wq"); if (!wl->freezable_wq) { @@ -5387,14 +5804,15 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size) wl->flags = 0; wl->sg_enabled = true; wl->sleep_auth = WL1271_PSM_ILLEGAL; + wl->recovery_count = 0; wl->hw_pg_ver = -1; wl->ap_ps_map = 0; wl->ap_fw_ps_map = 0; wl->quirks = 0; wl->platform_quirks = 0; - wl->sched_scanning = false; wl->system_hlid = WL12XX_SYSTEM_HLID; wl->active_sta_count = 0; + wl->active_link_count = 0; wl->fwlog_size = 0; init_waitqueue_head(&wl->fwlog_waitq); @@ -5434,14 +5852,24 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size) goto err_dummy_packet; } - wl->mbox = kmalloc(sizeof(*wl->mbox), GFP_KERNEL | GFP_DMA); + wl->mbox_size = mbox_size; + wl->mbox = kmalloc(wl->mbox_size, GFP_KERNEL | GFP_DMA); if (!wl->mbox) { ret = -ENOMEM; goto err_fwlog; } + wl->buffer_32 = kmalloc(sizeof(*wl->buffer_32), GFP_KERNEL); + if (!wl->buffer_32) { + ret = -ENOMEM; + goto err_mbox; + } + return hw; +err_mbox: + kfree(wl->mbox); + err_fwlog: free_page((unsigned long)wl->fwlog); @@ -5480,6 +5908,8 @@ int wlcore_free_hw(struct wl1271 *wl) device_remove_file(wl->dev, &dev_attr_hw_pg_ver); device_remove_file(wl->dev, &dev_attr_bt_coex_state); + kfree(wl->buffer_32); + kfree(wl->mbox); free_page((unsigned long)wl->fwlog); dev_kfree_skb(wl->dummy_packet); free_pages((unsigned long)wl->aggr_buf, get_order(wl->aggr_buf_size)); @@ -5712,10 +6142,10 @@ module_param_named(fwlog, fwlog_param, charp, 0); MODULE_PARM_DESC(fwlog, "FW logger options: continuous, ondemand, dbgpins or disable"); -module_param(bug_on_recovery, bool, S_IRUSR | S_IWUSR); +module_param(bug_on_recovery, int, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(bug_on_recovery, "BUG() on fw recovery"); -module_param(no_recovery, bool, S_IRUSR | S_IWUSR); +module_param(no_recovery, int, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(no_recovery, "Prevent HW recovery. FW will remain stuck."); MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/ti/wlcore/ps.c b/drivers/net/wireless/ti/wlcore/ps.c index 4d1414a..9b7b6e2 100644 --- a/drivers/net/wireless/ti/wlcore/ps.c +++ b/drivers/net/wireless/ti/wlcore/ps.c @@ -151,9 +151,6 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl) wl12xx_queue_recovery_work(wl); ret = -ETIMEDOUT; goto err; - } else if (ret < 0) { - wl1271_error("ELP wakeup completion error."); - goto err; } } @@ -242,11 +239,12 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid) struct ieee80211_tx_info *info; unsigned long flags; int filtered[NUM_TX_QUEUES]; + struct wl1271_link *lnk = &wl->links[hlid]; /* filter all frames currently in the low level queues for this hlid */ for (i = 0; i < NUM_TX_QUEUES; i++) { filtered[i] = 0; - while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) { + while ((skb = skb_dequeue(&lnk->tx_queue[i]))) { filtered[i]++; if (WARN_ON(wl12xx_is_dummy_packet(wl, skb))) @@ -260,8 +258,11 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid) } spin_lock_irqsave(&wl->wl_lock, flags); - for (i = 0; i < NUM_TX_QUEUES; i++) + for (i = 0; i < NUM_TX_QUEUES; i++) { wl->tx_queue_count[i] -= filtered[i]; + if (lnk->wlvif) + lnk->wlvif->tx_queue_count[i] -= filtered[i]; + } spin_unlock_irqrestore(&wl->wl_lock, flags); wl1271_handle_tx_low_watermark(wl); diff --git a/drivers/net/wireless/ti/wlcore/rx.c b/drivers/net/wireless/ti/wlcore/rx.c index 9ee0ec6..6791a1a 100644 --- a/drivers/net/wireless/ti/wlcore/rx.c +++ b/drivers/net/wireless/ti/wlcore/rx.c @@ -92,11 +92,16 @@ static void wl1271_rx_status(struct wl1271 *wl, status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED | RX_FLAG_DECRYPTED; - if (unlikely(desc_err_code == WL1271_RX_DESC_MIC_FAIL)) { + if (unlikely(desc_err_code & WL1271_RX_DESC_MIC_FAIL)) { status->flag |= RX_FLAG_MMIC_ERROR; - wl1271_warning("Michael MIC error"); + wl1271_warning("Michael MIC error. Desc: 0x%x", + desc_err_code); } } + + if (beacon) + wlcore_set_pending_regdomain_ch(wl, (u16)desc->channel, + status->band); } static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length, @@ -108,7 +113,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length, u8 *buf; u8 beacon = 0; u8 is_data = 0; - u8 reserved = 0; + u8 reserved = 0, offset_to_data = 0; u16 seq_num; u32 pkt_data_len; @@ -128,6 +133,8 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length, if (rx_align == WLCORE_RX_BUF_UNALIGNED) reserved = RX_BUF_ALIGN; + else if (rx_align == WLCORE_RX_BUF_PADDED) + offset_to_data = RX_BUF_ALIGN; /* the data read starts with the descriptor */ desc = (struct wl1271_rx_descriptor *) data; @@ -139,19 +146,15 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length, return 0; } - 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); + if (desc->status & WL1271_RX_DESC_DECRYPT_FAIL) { + hdr = (void *)(data + sizeof(*desc) + offset_to_data); + wl1271_warning("corrupted packet in RX: status: 0x%x len: %d", + desc->status & WL1271_RX_DESC_STATUS_MASK, + pkt_data_len); + wl1271_dump((DEBUG_RX|DEBUG_CMD), "PKT: ", data + sizeof(*desc), + min(pkt_data_len, + ieee80211_hdrlen(hdr->frame_control))); return -EINVAL; } diff --git a/drivers/net/wireless/ti/wlcore/rx.h b/drivers/net/wireless/ti/wlcore/rx.h index 71eba18..3363f60 100644 --- a/drivers/net/wireless/ti/wlcore/rx.h +++ b/drivers/net/wireless/ti/wlcore/rx.h @@ -84,12 +84,11 @@ * Bits 3-5 - process_id tag (AP mode FW) * Bits 6-7 - reserved */ -#define WL1271_RX_DESC_STATUS_MASK 0x03 +#define WL1271_RX_DESC_STATUS_MASK 0x07 #define WL1271_RX_DESC_SUCCESS 0x00 #define WL1271_RX_DESC_DECRYPT_FAIL 0x01 #define WL1271_RX_DESC_MIC_FAIL 0x02 -#define WL1271_RX_DESC_DRIVER_RX_Q_FAIL 0x03 #define RX_MEM_BLOCK_MASK 0xFF #define RX_BUF_SIZE_MASK 0xFFF00 diff --git a/drivers/net/wireless/ti/wlcore/scan.c b/drivers/net/wireless/ti/wlcore/scan.c index d005014..f407101 100644 --- a/drivers/net/wireless/ti/wlcore/scan.c +++ b/drivers/net/wireless/ti/wlcore/scan.c @@ -35,7 +35,6 @@ void wl1271_scan_complete_work(struct work_struct *work) { struct delayed_work *dwork; struct wl1271 *wl; - struct ieee80211_vif *vif; struct wl12xx_vif *wlvif; int ret; @@ -52,8 +51,7 @@ void wl1271_scan_complete_work(struct work_struct *work) if (wl->scan.state == WL1271_SCAN_STATE_IDLE) goto out; - vif = wl->scan_vif; - wlvif = wl12xx_vif_to_data(vif); + wlvif = wl->scan_wlvif; /* * Rearm the tx watchdog just before idling scan. This @@ -64,7 +62,7 @@ void wl1271_scan_complete_work(struct work_struct *work) wl->scan.state = WL1271_SCAN_STATE_IDLE; memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); wl->scan.req = NULL; - wl->scan_vif = NULL; + wl->scan_wlvif = NULL; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) @@ -82,6 +80,8 @@ void wl1271_scan_complete_work(struct work_struct *work) wl12xx_queue_recovery_work(wl); } + wlcore_cmd_regdomain_config_locked(wl); + ieee80211_scan_completed(wl->hw, false); out: @@ -89,371 +89,99 @@ out: } - -static int wl1271_get_scan_channels(struct wl1271 *wl, - struct cfg80211_scan_request *req, - struct basic_scan_channel_params *channels, - enum ieee80211_band band, bool passive) -{ - struct conf_scan_settings *c = &wl->conf.scan; - int i, j; - u32 flags; - - for (i = 0, j = 0; - i < req->n_channels && j < WL1271_SCAN_MAX_CHANNELS; - i++) { - flags = req->channels[i]->flags; - - if (!test_bit(i, wl->scan.scanned_ch) && - !(flags & IEEE80211_CHAN_DISABLED) && - (req->channels[i]->band == band) && - /* - * In passive scans, we scan all remaining - * channels, even if not marked as such. - * In active scans, we only scan channels not - * marked as passive. - */ - (passive || !(flags & IEEE80211_CHAN_PASSIVE_SCAN))) { - 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_antenna_gain %d, max_power %d", - req->channels[i]->max_antenna_gain, - req->channels[i]->max_power); - wl1271_debug(DEBUG_SCAN, "beacon_found %d", - req->channels[i]->beacon_found); - - if (!passive) { - channels[j].min_duration = - cpu_to_le32(c->min_dwell_time_active); - channels[j].max_duration = - cpu_to_le32(c->max_dwell_time_active); - } else { - channels[j].min_duration = - cpu_to_le32(c->min_dwell_time_passive); - channels[j].max_duration = - cpu_to_le32(c->max_dwell_time_passive); - } - channels[j].early_termination = 0; - channels[j].tx_power_att = req->channels[i]->max_power; - channels[j].channel = req->channels[i]->hw_value; - - memset(&channels[j].bssid_lsb, 0xff, 4); - memset(&channels[j].bssid_msb, 0xff, 2); - - /* Mark the channels we already used */ - set_bit(i, wl->scan.scanned_ch); - - j++; - } - } - - return j; -} - -#define WL1271_NOTHING_TO_SCAN 1 - -static int wl1271_scan_send(struct wl1271 *wl, struct ieee80211_vif *vif, - enum ieee80211_band band, - bool passive, u32 basic_rate) +static void wlcore_started_vifs_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) { - struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); - struct wl1271_cmd_scan *cmd; - struct wl1271_cmd_trigger_scan_to *trigger; - int ret; - u16 scan_options = 0; - - /* skip active scans if we don't have SSIDs */ - if (!passive && wl->scan.req->n_ssids == 0) - return WL1271_NOTHING_TO_SCAN; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - trigger = kzalloc(sizeof(*trigger), GFP_KERNEL); - if (!cmd || !trigger) { - ret = -ENOMEM; - goto out; - } - - if (wl->conf.scan.split_scan_timeout) - scan_options |= WL1271_SCAN_OPT_SPLIT_SCAN; - - if (passive) - scan_options |= WL1271_SCAN_OPT_PASSIVE; - - cmd->params.role_id = wlvif->role_id; - - if (WARN_ON(cmd->params.role_id == WL12XX_INVALID_ROLE_ID)) { - ret = -EINVAL; - goto out; - } - - cmd->params.scan_options = cpu_to_le16(scan_options); - - cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req, - cmd->channels, - band, passive); - if (cmd->params.n_ch == 0) { - ret = WL1271_NOTHING_TO_SCAN; - goto out; - } - - cmd->params.tx_rate = cpu_to_le32(basic_rate); - cmd->params.n_probe_reqs = wl->conf.scan.num_probe_reqs; - cmd->params.tid_trigger = CONF_TX_AC_ANY_TID; - cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG; - - if (band == IEEE80211_BAND_2GHZ) - cmd->params.band = WL1271_SCAN_BAND_2_4_GHZ; - else - cmd->params.band = WL1271_SCAN_BAND_5_GHZ; - - if (wl->scan.ssid_len && wl->scan.ssid) { - cmd->params.ssid_len = wl->scan.ssid_len; - memcpy(cmd->params.ssid, wl->scan.ssid, wl->scan.ssid_len); - } - - memcpy(cmd->addr, vif->addr, ETH_ALEN); - - ret = wl12xx_cmd_build_probe_req(wl, wlvif, - cmd->params.role_id, band, - wl->scan.ssid, wl->scan.ssid_len, - wl->scan.req->ie, - wl->scan.req->ie_len, false); - if (ret < 0) { - wl1271_error("PROBE request template failed"); - goto out; - } - - trigger->timeout = cpu_to_le32(wl->conf.scan.split_scan_timeout); - ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger, - sizeof(*trigger), 0); - if (ret < 0) { - wl1271_error("trigger scan to failed for hw scan"); - goto out; - } - - wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd)); + int *count = (int *)data; - ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0); - if (ret < 0) { - wl1271_error("SCAN failed"); - goto out; - } - -out: - kfree(cmd); - kfree(trigger); - return ret; + if (!vif->bss_conf.idle) + (*count)++; } -void wl1271_scan_stm(struct wl1271 *wl, struct ieee80211_vif *vif) +static int wlcore_count_started_vifs(struct wl1271 *wl) { - struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); - int ret = 0; - enum ieee80211_band band; - u32 rate, mask; - - switch (wl->scan.state) { - case WL1271_SCAN_STATE_IDLE: - break; - - case WL1271_SCAN_STATE_2GHZ_ACTIVE: - band = IEEE80211_BAND_2GHZ; - mask = wlvif->bitrate_masks[band]; - if (wl->scan.req->no_cck) { - mask &= ~CONF_TX_CCK_RATES; - if (!mask) - mask = CONF_TX_RATE_MASK_BASIC_P2P; - } - rate = wl1271_tx_min_rate_get(wl, mask); - ret = wl1271_scan_send(wl, vif, band, false, rate); - if (ret == WL1271_NOTHING_TO_SCAN) { - wl->scan.state = WL1271_SCAN_STATE_2GHZ_PASSIVE; - wl1271_scan_stm(wl, vif); - } - - break; - - case WL1271_SCAN_STATE_2GHZ_PASSIVE: - band = IEEE80211_BAND_2GHZ; - mask = wlvif->bitrate_masks[band]; - if (wl->scan.req->no_cck) { - mask &= ~CONF_TX_CCK_RATES; - if (!mask) - mask = CONF_TX_RATE_MASK_BASIC_P2P; - } - rate = wl1271_tx_min_rate_get(wl, mask); - ret = wl1271_scan_send(wl, vif, band, true, rate); - if (ret == WL1271_NOTHING_TO_SCAN) { - if (wl->enable_11a) - wl->scan.state = WL1271_SCAN_STATE_5GHZ_ACTIVE; - else - wl->scan.state = WL1271_SCAN_STATE_DONE; - wl1271_scan_stm(wl, vif); - } - - break; + int count = 0; - case WL1271_SCAN_STATE_5GHZ_ACTIVE: - band = IEEE80211_BAND_5GHZ; - rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]); - ret = wl1271_scan_send(wl, vif, band, false, rate); - if (ret == WL1271_NOTHING_TO_SCAN) { - wl->scan.state = WL1271_SCAN_STATE_5GHZ_PASSIVE; - wl1271_scan_stm(wl, vif); - } - - break; - - case WL1271_SCAN_STATE_5GHZ_PASSIVE: - band = IEEE80211_BAND_5GHZ; - rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]); - ret = wl1271_scan_send(wl, vif, band, true, rate); - if (ret == WL1271_NOTHING_TO_SCAN) { - wl->scan.state = WL1271_SCAN_STATE_DONE; - wl1271_scan_stm(wl, vif); - } - - break; - - case WL1271_SCAN_STATE_DONE: - wl->scan.failed = false; - cancel_delayed_work(&wl->scan_complete_work); - ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, - msecs_to_jiffies(0)); - break; - - default: - wl1271_error("invalid scan state"); - break; - } - - if (ret < 0) { - cancel_delayed_work(&wl->scan_complete_work); - ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, - msecs_to_jiffies(0)); - } -} - -int wl1271_scan(struct wl1271 *wl, struct ieee80211_vif *vif, - const u8 *ssid, size_t ssid_len, - struct cfg80211_scan_request *req) -{ - /* - * cfg80211 should guarantee that we don't get more channels - * than what we have registered. - */ - BUG_ON(req->n_channels > WL1271_MAX_CHANNELS); - - if (wl->scan.state != WL1271_SCAN_STATE_IDLE) - return -EBUSY; - - wl->scan.state = WL1271_SCAN_STATE_2GHZ_ACTIVE; - - if (ssid_len && ssid) { - wl->scan.ssid_len = ssid_len; - memcpy(wl->scan.ssid, ssid, ssid_len); - } else { - wl->scan.ssid_len = 0; - } - - wl->scan_vif = vif; - wl->scan.req = req; - memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); - - /* we assume failure so that timeout scenarios are handled correctly */ - wl->scan.failed = true; - ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, - msecs_to_jiffies(WL1271_SCAN_TIMEOUT)); - - wl1271_scan_stm(wl, vif); - - return 0; -} - -int wl1271_scan_stop(struct wl1271 *wl) -{ - struct wl1271_cmd_header *cmd = NULL; - int ret = 0; - - if (WARN_ON(wl->scan.state == WL1271_SCAN_STATE_IDLE)) - return -EINVAL; - - wl1271_debug(DEBUG_CMD, "cmd scan stop"); - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (!cmd) { - ret = -ENOMEM; - goto out; - } - - ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, cmd, - sizeof(*cmd), 0); - if (ret < 0) { - wl1271_error("cmd stop_scan failed"); - goto out; - } -out: - kfree(cmd); - return ret; + ieee80211_iterate_active_interfaces_atomic(wl->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + wlcore_started_vifs_iter, &count); + return count; } 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, int max_channels, - u8 *n_pactive_ch) +wlcore_scan_get_channels(struct wl1271 *wl, + struct ieee80211_channel *req_channels[], + u32 n_channels, + u32 n_ssids, + struct conn_scan_ch_params *channels, + u32 band, bool radar, bool passive, + int start, int max_channels, + u8 *n_pactive_ch, + int scan_type) { - struct conf_sched_scan_settings *c = &wl->conf.sched_scan; int i, j; u32 flags; - bool force_passive = !req->n_ssids; - u32 min_dwell_time_active, max_dwell_time_active, delta_per_probe; + bool force_passive = !n_ssids; + u32 min_dwell_time_active, max_dwell_time_active; u32 dwell_time_passive, dwell_time_dfs; - if (band == IEEE80211_BAND_5GHZ) - delta_per_probe = c->dwell_time_delta_per_probe_5; - else - delta_per_probe = c->dwell_time_delta_per_probe; + /* configure dwell times according to scan type */ + if (scan_type == SCAN_TYPE_SEARCH) { + struct conf_scan_settings *c = &wl->conf.scan; + bool active_vif_exists = !!wlcore_count_started_vifs(wl); + + min_dwell_time_active = active_vif_exists ? + c->min_dwell_time_active : + c->min_dwell_time_active_long; + max_dwell_time_active = active_vif_exists ? + c->max_dwell_time_active : + c->max_dwell_time_active_long; + dwell_time_passive = c->dwell_time_passive; + dwell_time_dfs = c->dwell_time_dfs; + } else { + struct conf_sched_scan_settings *c = &wl->conf.sched_scan; + u32 delta_per_probe; - min_dwell_time_active = c->base_dwell_time + - req->n_ssids * c->num_probe_reqs * delta_per_probe; + if (band == IEEE80211_BAND_5GHZ) + delta_per_probe = c->dwell_time_delta_per_probe_5; + else + delta_per_probe = c->dwell_time_delta_per_probe; - max_dwell_time_active = min_dwell_time_active + c->max_dwell_time_delta; + min_dwell_time_active = c->base_dwell_time + + n_ssids * c->num_probe_reqs * delta_per_probe; + max_dwell_time_active = min_dwell_time_active + + c->max_dwell_time_delta; + dwell_time_passive = c->dwell_time_passive; + dwell_time_dfs = c->dwell_time_dfs; + } min_dwell_time_active = DIV_ROUND_UP(min_dwell_time_active, 1000); max_dwell_time_active = DIV_ROUND_UP(max_dwell_time_active, 1000); - dwell_time_passive = DIV_ROUND_UP(c->dwell_time_passive, 1000); - dwell_time_dfs = DIV_ROUND_UP(c->dwell_time_dfs, 1000); + dwell_time_passive = DIV_ROUND_UP(dwell_time_passive, 1000); + dwell_time_dfs = DIV_ROUND_UP(dwell_time_dfs, 1000); for (i = 0, j = start; - i < req->n_channels && j < max_channels; + i < n_channels && j < max_channels; i++) { - flags = req->channels[i]->flags; + flags = req_channels[i]->flags; if (force_passive) flags |= IEEE80211_CHAN_PASSIVE_SCAN; - if ((req->channels[i]->band == band) && + if ((req_channels[i]->band == band) && !(flags & IEEE80211_CHAN_DISABLED) && (!!(flags & IEEE80211_CHAN_RADAR) == radar) && /* if radar is set, we ignore the passive flag */ (radar || !!(flags & IEEE80211_CHAN_PASSIVE_SCAN) == passive)) { wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ", - req->channels[i]->band, - req->channels[i]->center_freq); + 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); + req_channels[i]->hw_value, + req_channels[i]->flags); wl1271_debug(DEBUG_SCAN, "max_power %d", - req->channels[i]->max_power); + req_channels[i]->max_power); wl1271_debug(DEBUG_SCAN, "min_dwell_time %d max dwell time %d", min_dwell_time_active, max_dwell_time_active); @@ -473,10 +201,11 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl, channels[j].max_duration = cpu_to_le16(max_dwell_time_active); - channels[j].tx_power_att = req->channels[i]->max_power; - channels[j].channel = req->channels[i]->hw_value; + channels[j].tx_power_att = req_channels[i]->max_power; + channels[j].channel = req_channels[i]->hw_value; - if ((band == IEEE80211_BAND_2GHZ) && + if (n_pactive_ch && + (band == IEEE80211_BAND_2GHZ) && (channels[j].channel >= 12) && (channels[j].channel <= 14) && (flags & IEEE80211_CHAN_PASSIVE_SCAN) && @@ -500,51 +229,80 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl, return j - start; } -static bool -wl1271_scan_sched_scan_channels(struct wl1271 *wl, - struct cfg80211_sched_scan_request *req, - struct wl1271_cmd_sched_scan_config *cfg) +bool +wlcore_set_scan_chan_params(struct wl1271 *wl, + struct wlcore_scan_channels *cfg, + struct ieee80211_channel *channels[], + u32 n_channels, + u32 n_ssids, + int scan_type) { u8 n_pactive_ch = 0; cfg->passive[0] = - wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2, - IEEE80211_BAND_2GHZ, - false, true, 0, - MAX_CHANNELS_2GHZ, - &n_pactive_ch); + wlcore_scan_get_channels(wl, + channels, + n_channels, + n_ssids, + cfg->channels_2, + IEEE80211_BAND_2GHZ, + false, true, 0, + MAX_CHANNELS_2GHZ, + &n_pactive_ch, + scan_type); cfg->active[0] = - wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2, - IEEE80211_BAND_2GHZ, - false, false, - cfg->passive[0], - MAX_CHANNELS_2GHZ, - &n_pactive_ch); + wlcore_scan_get_channels(wl, + channels, + n_channels, + n_ssids, + cfg->channels_2, + IEEE80211_BAND_2GHZ, + false, false, + cfg->passive[0], + MAX_CHANNELS_2GHZ, + &n_pactive_ch, + scan_type); cfg->passive[1] = - wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5, - IEEE80211_BAND_5GHZ, - false, true, 0, - MAX_CHANNELS_5GHZ, - &n_pactive_ch); + wlcore_scan_get_channels(wl, + channels, + n_channels, + n_ssids, + cfg->channels_5, + IEEE80211_BAND_5GHZ, + false, true, 0, + wl->max_channels_5, + &n_pactive_ch, + scan_type); cfg->dfs = - wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5, - IEEE80211_BAND_5GHZ, - true, true, - cfg->passive[1], - MAX_CHANNELS_5GHZ, - &n_pactive_ch); + wlcore_scan_get_channels(wl, + channels, + n_channels, + n_ssids, + cfg->channels_5, + IEEE80211_BAND_5GHZ, + true, true, + cfg->passive[1], + wl->max_channels_5, + &n_pactive_ch, + scan_type); cfg->active[1] = - wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5, - IEEE80211_BAND_5GHZ, - false, false, - cfg->passive[1] + cfg->dfs, - MAX_CHANNELS_5GHZ, - &n_pactive_ch); + wlcore_scan_get_channels(wl, + channels, + n_channels, + n_ssids, + cfg->channels_5, + IEEE80211_BAND_5GHZ, + false, false, + cfg->passive[1] + cfg->dfs, + wl->max_channels_5, + &n_pactive_ch, + scan_type); + /* 802.11j channels are not supported yet */ cfg->passive[2] = 0; cfg->active[2] = 0; - cfg->n_pactive_ch = n_pactive_ch; + cfg->passive_active = n_pactive_ch; wl1271_debug(DEBUG_SCAN, " 2.4GHz: active %d passive %d", cfg->active[0], cfg->passive[0]); @@ -556,10 +314,48 @@ wl1271_scan_sched_scan_channels(struct wl1271 *wl, cfg->passive[1] || cfg->active[1] || cfg->dfs || cfg->passive[2] || cfg->active[2]; } +EXPORT_SYMBOL_GPL(wlcore_set_scan_chan_params); + +int wlcore_scan(struct wl1271 *wl, struct ieee80211_vif *vif, + const u8 *ssid, size_t ssid_len, + struct cfg80211_scan_request *req) +{ + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); + + /* + * cfg80211 should guarantee that we don't get more channels + * than what we have registered. + */ + BUG_ON(req->n_channels > WL1271_MAX_CHANNELS); + + if (wl->scan.state != WL1271_SCAN_STATE_IDLE) + return -EBUSY; + + wl->scan.state = WL1271_SCAN_STATE_2GHZ_ACTIVE; + + if (ssid_len && ssid) { + wl->scan.ssid_len = ssid_len; + memcpy(wl->scan.ssid, ssid, ssid_len); + } else { + wl->scan.ssid_len = 0; + } + + wl->scan_wlvif = wlvif; + wl->scan.req = req; + memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); + + /* we assume failure so that timeout scenarios are handled correctly */ + wl->scan.failed = true; + ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, + msecs_to_jiffies(WL1271_SCAN_TIMEOUT)); + wl->ops->scan_start(wl, wlvif, req); + + return 0; +} /* Returns the scan type to be used or a negative value on error */ -static int -wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl, +int +wlcore_scan_sched_scan_ssid_list(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req) { @@ -662,160 +458,12 @@ out: return ret; return type; } +EXPORT_SYMBOL_GPL(wlcore_scan_sched_scan_ssid_list); -int wl1271_scan_sched_scan_config(struct wl1271 *wl, - struct wl12xx_vif *wlvif, - 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, ret; - bool force_passive = !req->n_ssids; - - wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config"); - - cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); - if (!cfg) - return -ENOMEM; - - cfg->role_id = wlvif->role_id; - 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); - - cfg->ssid_len = 0; - ret = wl12xx_scan_sched_scan_ssid_list(wl, wlvif, req); - if (ret < 0) - goto out; - - cfg->filter_type = ret; - - wl1271_debug(DEBUG_SCAN, "filter_type = %d", cfg->filter_type); - - if (!wl1271_scan_sched_scan_channels(wl, req, cfg)) { - wl1271_error("scan channel list is empty"); - ret = -EINVAL; - goto out; - } - - if (!force_passive && cfg->active[0]) { - u8 band = IEEE80211_BAND_2GHZ; - ret = wl12xx_cmd_build_probe_req(wl, wlvif, - wlvif->role_id, band, - req->ssids[0].ssid, - req->ssids[0].ssid_len, - ies->ie[band], - ies->len[band], true); - if (ret < 0) { - wl1271_error("2.4GHz PROBE request template failed"); - goto out; - } - } - - if (!force_passive && cfg->active[1]) { - u8 band = IEEE80211_BAND_5GHZ; - ret = wl12xx_cmd_build_probe_req(wl, wlvif, - wlvif->role_id, band, - req->ssids[0].ssid, - req->ssids[0].ssid_len, - ies->ie[band], - ies->len[band], true); - 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 wl12xx_vif *wlvif) -{ - struct wl1271_cmd_sched_scan_start *start; - int ret = 0; - - wl1271_debug(DEBUG_CMD, "cmd periodic scan start"); - - if (wlvif->bss_type != BSS_TYPE_STA_BSS) - return -EOPNOTSUPP; - - if ((wl->quirks & WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN) && - test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) - return -EBUSY; - - start = kzalloc(sizeof(*start), GFP_KERNEL); - if (!start) - return -ENOMEM; - - start->role_id = wlvif->role_id; - 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) +void wlcore_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 wl12xx_vif *wlvif) -{ - 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->role_id = wlvif->role_id; - 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; - } - -out_free: - kfree(stop); -} +EXPORT_SYMBOL_GPL(wlcore_scan_sched_scan_results); diff --git a/drivers/net/wireless/ti/wlcore/scan.h b/drivers/net/wireless/ti/wlcore/scan.h index 29f3c8d6..a6ab24b 100644 --- a/drivers/net/wireless/ti/wlcore/scan.h +++ b/drivers/net/wireless/ti/wlcore/scan.h @@ -26,22 +26,20 @@ #include "wlcore.h" -int wl1271_scan(struct wl1271 *wl, struct ieee80211_vif *vif, +int wlcore_scan(struct wl1271 *wl, struct ieee80211_vif *vif, const u8 *ssid, size_t ssid_len, struct cfg80211_scan_request *req); -int wl1271_scan_stop(struct wl1271 *wl); int wl1271_scan_build_probe_req(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, u8 band); -void wl1271_scan_stm(struct wl1271 *wl, struct ieee80211_vif *vif); +void wl1271_scan_stm(struct wl1271 *wl, struct wl12xx_vif *wlvif); void wl1271_scan_complete_work(struct work_struct *work); int wl1271_scan_sched_scan_config(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req, struct ieee80211_sched_scan_ies *ies); int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif); -void wl1271_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif); -void wl1271_scan_sched_scan_results(struct wl1271 *wl); +void wlcore_scan_sched_scan_results(struct wl1271 *wl); #define WL1271_SCAN_MAX_CHANNELS 24 #define WL1271_SCAN_DEFAULT_TAG 1 @@ -66,56 +64,6 @@ enum { WL1271_SCAN_STATE_DONE }; -struct basic_scan_params { - /* Scan option flags (WL1271_SCAN_OPT_*) */ - __le16 scan_options; - u8 role_id; - /* Number of scan channels in the list (maximum 30) */ - u8 n_ch; - /* This field indicates the number of probe requests to send - per channel for an active scan */ - u8 n_probe_reqs; - u8 tid_trigger; - u8 ssid_len; - u8 use_ssid_list; - - /* Rate bit field for sending the probes */ - __le32 tx_rate; - - u8 ssid[IEEE80211_MAX_SSID_LEN]; - /* Band to scan */ - u8 band; - - u8 scan_tag; - u8 padding2[2]; -} __packed; - -struct basic_scan_channel_params { - /* Duration in TU to wait for frames on a channel for active scan */ - __le32 min_duration; - __le32 max_duration; - __le32 bssid_lsb; - __le16 bssid_msb; - u8 early_termination; - u8 tx_power_att; - u8 channel; - /* FW internal use only! */ - u8 dfs_candidate; - u8 activity_detected; - u8 pad; -} __packed; - -struct wl1271_cmd_scan { - struct wl1271_cmd_header header; - - struct basic_scan_params params; - struct basic_scan_channel_params channels[WL1271_SCAN_MAX_CHANNELS]; - - /* src mac address */ - u8 addr[ETH_ALEN]; - u8 padding[2]; -} __packed; - struct wl1271_cmd_trigger_scan_to { struct wl1271_cmd_header header; @@ -123,9 +71,17 @@ struct wl1271_cmd_trigger_scan_to { } __packed; #define MAX_CHANNELS_2GHZ 14 -#define MAX_CHANNELS_5GHZ 23 #define MAX_CHANNELS_4GHZ 4 +/* + * This max value here is used only for the struct definition of + * wlcore_scan_channels. This struct is used by both 12xx + * and 18xx (which have different max 5ghz channels value). + * In order to make sure this is large enough, just use the + * max possible 5ghz channels. + */ +#define MAX_CHANNELS_5GHZ 42 + #define SCAN_MAX_CYCLE_INTERVALS 16 #define SCAN_MAX_BANDS 3 @@ -160,43 +116,6 @@ struct conn_scan_ch_params { 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[IEEE80211_MAX_SSID_LEN]; - - u8 n_probe_reqs; /* Number of probes requests per channel */ - - u8 passive[SCAN_MAX_BANDS]; - u8 active[SCAN_MAX_BANDS]; - - u8 dfs; - - u8 n_pactive_ch; /* number of pactive (passive until fw detects energy) - channels in BG band */ - u8 role_id; - u8 padding[1]; - - struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ]; - struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ]; - struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ]; -} __packed; - - #define SCHED_SCAN_MAX_SSIDS 16 enum { @@ -220,21 +139,34 @@ struct wl1271_cmd_sched_scan_ssid_list { u8 padding[2]; } __packed; -struct wl1271_cmd_sched_scan_start { - struct wl1271_cmd_header header; +struct wlcore_scan_channels { + u8 passive[SCAN_MAX_BANDS]; /* number of passive scan channels */ + u8 active[SCAN_MAX_BANDS]; /* number of active scan channels */ + u8 dfs; /* number of dfs channels in 5ghz */ + u8 passive_active; /* number of passive before active channels 2.4ghz */ - u8 tag; - u8 role_id; - u8 padding[2]; -} __packed; - -struct wl1271_cmd_sched_scan_stop { - struct wl1271_cmd_header header; + struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ]; + struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ]; + struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ]; +}; - u8 tag; - u8 role_id; - u8 padding[2]; -} __packed; +enum { + SCAN_TYPE_SEARCH = 0, + SCAN_TYPE_PERIODIC = 1, + SCAN_TYPE_TRACKING = 2, +}; +bool +wlcore_set_scan_chan_params(struct wl1271 *wl, + struct wlcore_scan_channels *cfg, + struct ieee80211_channel *channels[], + u32 n_channels, + u32 n_ssids, + int scan_type); + +int +wlcore_scan_sched_scan_ssid_list(struct wl1271 *wl, + struct wl12xx_vif *wlvif, + struct cfg80211_sched_scan_request *req); #endif /* __WL1271_SCAN_H__ */ diff --git a/drivers/net/wireless/ti/wlcore/sdio.c b/drivers/net/wireless/ti/wlcore/sdio.c index 646f703..d4f184e 100644 --- a/drivers/net/wireless/ti/wlcore/sdio.c +++ b/drivers/net/wireless/ti/wlcore/sdio.c @@ -326,8 +326,7 @@ static void wl1271_remove(struct sdio_func *func) /* Undo decrement done above in wl1271_probe */ pm_runtime_get_noresume(&func->dev); - platform_device_del(glue->core); - platform_device_put(glue->core); + platform_device_unregister(glue->core); kfree(glue); } diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c index f06f477..2d700b7 100644 --- a/drivers/net/wireless/ti/wlcore/spi.c +++ b/drivers/net/wireless/ti/wlcore/spi.c @@ -270,7 +270,7 @@ static int __must_check wl12xx_spi_raw_write(struct device *child, int addr, void *buf, size_t len, bool fixed) { struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent); - struct spi_transfer t[2 * WSPI_MAX_NUM_OF_CHUNKS]; + struct spi_transfer t[2 * (WSPI_MAX_NUM_OF_CHUNKS + 1)]; struct spi_message m; u32 commands[WSPI_MAX_NUM_OF_CHUNKS]; u32 *cmd; @@ -407,8 +407,7 @@ static int wl1271_remove(struct spi_device *spi) { struct wl12xx_spi_glue *glue = spi_get_drvdata(spi); - platform_device_del(glue->core); - platform_device_put(glue->core); + platform_device_unregister(glue->core); kfree(glue); return 0; diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index a90d3cd..ece392c 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -104,7 +104,7 @@ static void wl1271_tx_regulate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid) { - bool fw_ps, single_sta; + bool fw_ps, single_link; u8 tx_pkts; if (WARN_ON(!test_bit(hlid, wlvif->links_map))) @@ -112,15 +112,15 @@ static void wl1271_tx_regulate_link(struct wl1271 *wl, fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); tx_pkts = wl->links[hlid].allocated_pkts; - single_sta = (wl->active_sta_count == 1); + single_link = (wl->active_link_count == 1); /* * if in FW PS and there is enough data in FW we can put the link * into high-level PS and clean out its TX queues. - * Make an exception if this is the only connected station. In this - * case FW-memory congestion is not a problem. + * Make an exception if this is the only connected link. In this + * case FW-memory congestion is less of a problem. */ - if (!single_sta && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS) + if (!single_link && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS) wl12xx_ps_link_start(wl, wlvif, hlid, true); } @@ -155,21 +155,18 @@ static u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct sk_buff *skb, struct ieee80211_sta *sta) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - - if (!wlvif || wl12xx_is_dummy_packet(wl, skb)) - return wl->system_hlid; + struct ieee80211_tx_info *control; if (wlvif->bss_type == BSS_TYPE_AP_BSS) return wl12xx_tx_get_hlid_ap(wl, wlvif, skb, sta); - if ((test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) || - test_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags)) && - !ieee80211_is_auth(hdr->frame_control) && - !ieee80211_is_assoc_req(hdr->frame_control)) - return wlvif->sta.hlid; - else + control = IEEE80211_SKB_CB(skb); + if (control->flags & IEEE80211_TX_CTL_TX_OFFCHAN) { + wl1271_debug(DEBUG_TX, "tx offchannel"); return wlvif->dev_hlid; + } + + return wlvif->sta.hlid; } unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl, @@ -224,9 +221,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif, ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); wl->tx_allocated_pkts[ac]++; - if (!wl12xx_is_dummy_packet(wl, skb) && wlvif && - wlvif->bss_type == BSS_TYPE_AP_BSS && - test_bit(hlid, wlvif->ap.sta_hlid_map)) + if (test_bit(hlid, wl->links_map)) wl->links[hlid].allocated_pkts++; ret = 0; @@ -293,9 +288,14 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif, tx_attr |= TX_HW_ATTR_TX_DUMMY_REQ; } else if (wlvif) { + u8 session_id = wl->session_ids[hlid]; + + if ((wl->quirks & WLCORE_QUIRK_AP_ZERO_SESSION_ID) && + (wlvif->bss_type == BSS_TYPE_AP_BSS)) + session_id = 0; + /* configure the tx attributes */ - tx_attr = wlvif->session_counter << - TX_HW_ATTR_OFST_SESSION_COUNTER; + tx_attr = session_id << TX_HW_ATTR_OFST_SESSION_COUNTER; } desc->hlid = hlid; @@ -452,20 +452,22 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set, void wl1271_handle_tx_low_watermark(struct wl1271 *wl) { int i; + struct wl12xx_vif *wlvif; - for (i = 0; i < NUM_TX_QUEUES; i++) { - if (wlcore_is_queue_stopped_by_reason(wl, i, - WLCORE_QUEUE_STOP_REASON_WATERMARK) && - wl->tx_queue_count[i] <= WL1271_TX_QUEUE_LOW_WATERMARK) { - /* firmware buffer has space, restart queues */ - wlcore_wake_queue(wl, i, - WLCORE_QUEUE_STOP_REASON_WATERMARK); + wl12xx_for_each_wlvif(wl, wlvif) { + for (i = 0; i < NUM_TX_QUEUES; i++) { + if (wlcore_is_queue_stopped_by_reason(wl, wlvif, i, + WLCORE_QUEUE_STOP_REASON_WATERMARK) && + wlvif->tx_queue_count[i] <= + WL1271_TX_QUEUE_LOW_WATERMARK) + /* firmware buffer has space, restart queues */ + wlcore_wake_queue(wl, wlvif, i, + WLCORE_QUEUE_STOP_REASON_WATERMARK); } } } -static struct sk_buff_head *wl1271_select_queue(struct wl1271 *wl, - struct sk_buff_head *queues) +static int wlcore_select_ac(struct wl1271 *wl) { int i, q = -1, ac; u32 min_pkts = 0xffffffff; @@ -479,45 +481,60 @@ static struct sk_buff_head *wl1271_select_queue(struct wl1271 *wl, */ for (i = 0; i < NUM_TX_QUEUES; i++) { ac = wl1271_tx_get_queue(i); - if (!skb_queue_empty(&queues[ac]) && - (wl->tx_allocated_pkts[ac] < min_pkts)) { + if (wl->tx_queue_count[ac] && + wl->tx_allocated_pkts[ac] < min_pkts) { q = ac; min_pkts = wl->tx_allocated_pkts[q]; } } - if (q == -1) - return NULL; - - return &queues[q]; + return q; } -static struct sk_buff *wl12xx_lnk_skb_dequeue(struct wl1271 *wl, - struct wl1271_link *lnk) +static struct sk_buff *wlcore_lnk_dequeue(struct wl1271 *wl, + struct wl1271_link *lnk, u8 q) { struct sk_buff *skb; unsigned long flags; - struct sk_buff_head *queue; - queue = wl1271_select_queue(wl, lnk->tx_queue); - if (!queue) - return NULL; - - skb = skb_dequeue(queue); + skb = skb_dequeue(&lnk->tx_queue[q]); if (skb) { - int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); spin_lock_irqsave(&wl->wl_lock, flags); WARN_ON_ONCE(wl->tx_queue_count[q] <= 0); wl->tx_queue_count[q]--; + if (lnk->wlvif) { + WARN_ON_ONCE(lnk->wlvif->tx_queue_count[q] <= 0); + lnk->wlvif->tx_queue_count[q]--; + } spin_unlock_irqrestore(&wl->wl_lock, flags); } return skb; } -static struct sk_buff *wl12xx_vif_skb_dequeue(struct wl1271 *wl, - struct wl12xx_vif *wlvif, - u8 *hlid) +static struct sk_buff *wlcore_lnk_dequeue_high_prio(struct wl1271 *wl, + u8 hlid, u8 ac, + u8 *low_prio_hlid) +{ + struct wl1271_link *lnk = &wl->links[hlid]; + + if (!wlcore_hw_lnk_high_prio(wl, hlid, lnk)) { + if (*low_prio_hlid == WL12XX_INVALID_LINK_ID && + !skb_queue_empty(&lnk->tx_queue[ac]) && + wlcore_hw_lnk_low_prio(wl, hlid, lnk)) + /* we found the first non-empty low priority queue */ + *low_prio_hlid = hlid; + + return NULL; + } + + return wlcore_lnk_dequeue(wl, lnk, ac); +} + +static struct sk_buff *wlcore_vif_dequeue_high_prio(struct wl1271 *wl, + struct wl12xx_vif *wlvif, + u8 ac, u8 *hlid, + u8 *low_prio_hlid) { struct sk_buff *skb = NULL; int i, h, start_hlid; @@ -533,7 +550,8 @@ static struct sk_buff *wl12xx_vif_skb_dequeue(struct wl1271 *wl, if (!test_bit(h, wlvif->links_map)) continue; - skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[h]); + skb = wlcore_lnk_dequeue_high_prio(wl, h, ac, + low_prio_hlid); if (!skb) continue; @@ -553,42 +571,74 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl, u8 *hlid) unsigned long flags; struct wl12xx_vif *wlvif = wl->last_wlvif; struct sk_buff *skb = NULL; + int ac; + u8 low_prio_hlid = WL12XX_INVALID_LINK_ID; + + ac = wlcore_select_ac(wl); + if (ac < 0) + goto out; /* continue from last wlvif (round robin) */ if (wlvif) { wl12xx_for_each_wlvif_continue(wl, wlvif) { - skb = wl12xx_vif_skb_dequeue(wl, wlvif, hlid); - if (skb) { - wl->last_wlvif = wlvif; - break; - } + if (!wlvif->tx_queue_count[ac]) + continue; + + skb = wlcore_vif_dequeue_high_prio(wl, wlvif, ac, hlid, + &low_prio_hlid); + if (!skb) + continue; + + wl->last_wlvif = wlvif; + break; } } /* dequeue from the system HLID before the restarting wlvif list */ if (!skb) { - skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[wl->system_hlid]); - *hlid = wl->system_hlid; + skb = wlcore_lnk_dequeue_high_prio(wl, wl->system_hlid, + ac, &low_prio_hlid); + if (skb) { + *hlid = wl->system_hlid; + wl->last_wlvif = NULL; + } } - /* do a new pass over the wlvif list */ + /* Do a new pass over the wlvif list. But no need to continue + * after last_wlvif. The previous pass should have found it. */ if (!skb) { wl12xx_for_each_wlvif(wl, wlvif) { - skb = wl12xx_vif_skb_dequeue(wl, wlvif, hlid); + if (!wlvif->tx_queue_count[ac]) + goto next; + + skb = wlcore_vif_dequeue_high_prio(wl, wlvif, ac, hlid, + &low_prio_hlid); if (skb) { wl->last_wlvif = wlvif; break; } - /* - * No need to continue after last_wlvif. The previous - * pass should have found it. - */ +next: if (wlvif == wl->last_wlvif) break; } } + /* no high priority skbs found - but maybe a low priority one? */ + if (!skb && low_prio_hlid != WL12XX_INVALID_LINK_ID) { + struct wl1271_link *lnk = &wl->links[low_prio_hlid]; + skb = wlcore_lnk_dequeue(wl, lnk, ac); + + WARN_ON(!skb); /* we checked this before */ + *hlid = low_prio_hlid; + + /* ensure proper round robin in the vif/link levels */ + wl->last_wlvif = lnk->wlvif; + if (lnk->wlvif) + lnk->wlvif->last_tx_hlid = low_prio_hlid; + + } + if (!skb && test_and_clear_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags)) { int q; @@ -602,6 +652,7 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl, u8 *hlid) spin_unlock_irqrestore(&wl->wl_lock, flags); } +out: return skb; } @@ -623,6 +674,8 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct wl12xx_vif *wlvif, spin_lock_irqsave(&wl->wl_lock, flags); wl->tx_queue_count[q]++; + if (wlvif) + wlvif->tx_queue_count[q]++; spin_unlock_irqrestore(&wl->wl_lock, flags); } @@ -699,7 +752,7 @@ int wlcore_tx_work_locked(struct wl1271 *wl) bool has_data = false; wlvif = NULL; - if (!wl12xx_is_dummy_packet(wl, skb) && info->control.vif) + if (!wl12xx_is_dummy_packet(wl, skb)) wlvif = wl12xx_vif_to_data(info->control.vif); else hlid = wl->system_hlid; @@ -972,10 +1025,11 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid) unsigned long flags; struct ieee80211_tx_info *info; int total[NUM_TX_QUEUES]; + struct wl1271_link *lnk = &wl->links[hlid]; for (i = 0; i < NUM_TX_QUEUES; i++) { total[i] = 0; - while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) { + while ((skb = skb_dequeue(&lnk->tx_queue[i]))) { wl1271_debug(DEBUG_TX, "link freeing skb 0x%p", skb); if (!wl12xx_is_dummy_packet(wl, skb)) { @@ -990,8 +1044,11 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid) } spin_lock_irqsave(&wl->wl_lock, flags); - for (i = 0; i < NUM_TX_QUEUES; i++) + for (i = 0; i < NUM_TX_QUEUES; i++) { wl->tx_queue_count[i] -= total[i]; + if (lnk->wlvif) + lnk->wlvif->tx_queue_count[i] -= total[i]; + } spin_unlock_irqrestore(&wl->wl_lock, flags); wl1271_handle_tx_low_watermark(wl); @@ -1004,16 +1061,18 @@ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif) /* TX failure */ for_each_set_bit(i, wlvif->links_map, WL12XX_MAX_LINKS) { - if (wlvif->bss_type == BSS_TYPE_AP_BSS) + if (wlvif->bss_type == BSS_TYPE_AP_BSS) { + /* this calls wl12xx_free_link */ wl1271_free_sta(wl, wlvif, i); - else - wlvif->sta.ba_rx_bitmap = 0; - - wl->links[i].allocated_pkts = 0; - wl->links[i].prev_freed_pkts = 0; + } else { + u8 hlid = i; + wl12xx_free_link(wl, wlvif, &hlid); + } } wlvif->last_tx_hlid = 0; + for (i = 0; i < NUM_TX_QUEUES; i++) + wlvif->tx_queue_count[i] = 0; } /* caller must hold wl->mutex and TX must be stopped */ void wl12xx_tx_reset(struct wl1271 *wl) @@ -1023,7 +1082,7 @@ void wl12xx_tx_reset(struct wl1271 *wl) struct ieee80211_tx_info *info; /* only reset the queues if something bad happened */ - if (WARN_ON_ONCE(wl1271_tx_total_queue_count(wl) != 0)) { + if (wl1271_tx_total_queue_count(wl) != 0) { for (i = 0; i < WL12XX_MAX_LINKS; i++) wl1271_tx_reset_link_queues(wl, i); @@ -1135,45 +1194,48 @@ u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set) return BIT(__ffs(rate_set)); } +EXPORT_SYMBOL_GPL(wl1271_tx_min_rate_get); -void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue, - enum wlcore_queue_stop_reason reason) +void wlcore_stop_queue_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 queue, enum wlcore_queue_stop_reason reason) { - bool stopped = !!wl->queue_stop_reasons[queue]; + int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue); + bool stopped = !!wl->queue_stop_reasons[hwq]; /* queue should not be stopped for this reason */ - WARN_ON(test_and_set_bit(reason, &wl->queue_stop_reasons[queue])); + WARN_ON_ONCE(test_and_set_bit(reason, &wl->queue_stop_reasons[hwq])); if (stopped) return; - ieee80211_stop_queue(wl->hw, wl1271_tx_get_mac80211_queue(queue)); + ieee80211_stop_queue(wl->hw, hwq); } -void wlcore_stop_queue(struct wl1271 *wl, u8 queue, +void wlcore_stop_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue, enum wlcore_queue_stop_reason reason) { unsigned long flags; spin_lock_irqsave(&wl->wl_lock, flags); - wlcore_stop_queue_locked(wl, queue, reason); + wlcore_stop_queue_locked(wl, wlvif, queue, reason); spin_unlock_irqrestore(&wl->wl_lock, flags); } -void wlcore_wake_queue(struct wl1271 *wl, u8 queue, +void wlcore_wake_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue, enum wlcore_queue_stop_reason reason) { unsigned long flags; + int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue); spin_lock_irqsave(&wl->wl_lock, flags); /* queue should not be clear for this reason */ - WARN_ON(!test_and_clear_bit(reason, &wl->queue_stop_reasons[queue])); + WARN_ON_ONCE(!test_and_clear_bit(reason, &wl->queue_stop_reasons[hwq])); - if (wl->queue_stop_reasons[queue]) + if (wl->queue_stop_reasons[hwq]) goto out; - ieee80211_wake_queue(wl->hw, wl1271_tx_get_mac80211_queue(queue)); + ieee80211_wake_queue(wl->hw, hwq); out: spin_unlock_irqrestore(&wl->wl_lock, flags); @@ -1183,48 +1245,74 @@ void wlcore_stop_queues(struct wl1271 *wl, enum wlcore_queue_stop_reason reason) { int i; + unsigned long flags; - for (i = 0; i < NUM_TX_QUEUES; i++) - wlcore_stop_queue(wl, i, reason); + spin_lock_irqsave(&wl->wl_lock, flags); + + /* mark all possible queues as stopped */ + for (i = 0; i < WLCORE_NUM_MAC_ADDRESSES * NUM_TX_QUEUES; i++) + WARN_ON_ONCE(test_and_set_bit(reason, + &wl->queue_stop_reasons[i])); + + /* use the global version to make sure all vifs in mac80211 we don't + * know are stopped. + */ + ieee80211_stop_queues(wl->hw); + + spin_unlock_irqrestore(&wl->wl_lock, flags); } -EXPORT_SYMBOL_GPL(wlcore_stop_queues); void wlcore_wake_queues(struct wl1271 *wl, enum wlcore_queue_stop_reason reason) { int i; + unsigned long flags; - for (i = 0; i < NUM_TX_QUEUES; i++) - wlcore_wake_queue(wl, i, reason); + spin_lock_irqsave(&wl->wl_lock, flags); + + /* mark all possible queues as awake */ + for (i = 0; i < WLCORE_NUM_MAC_ADDRESSES * NUM_TX_QUEUES; i++) + WARN_ON_ONCE(!test_and_clear_bit(reason, + &wl->queue_stop_reasons[i])); + + /* use the global version to make sure all vifs in mac80211 we don't + * know are woken up. + */ + ieee80211_wake_queues(wl->hw); + + spin_unlock_irqrestore(&wl->wl_lock, flags); } -EXPORT_SYMBOL_GPL(wlcore_wake_queues); -void wlcore_reset_stopped_queues(struct wl1271 *wl) +bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, + struct wl12xx_vif *wlvif, u8 queue, + enum wlcore_queue_stop_reason reason) { - int i; unsigned long flags; + bool stopped; spin_lock_irqsave(&wl->wl_lock, flags); - - for (i = 0; i < NUM_TX_QUEUES; i++) { - if (!wl->queue_stop_reasons[i]) - continue; - - wl->queue_stop_reasons[i] = 0; - ieee80211_wake_queue(wl->hw, - wl1271_tx_get_mac80211_queue(i)); - } - + stopped = wlcore_is_queue_stopped_by_reason_locked(wl, wlvif, queue, + reason); spin_unlock_irqrestore(&wl->wl_lock, flags); + + return stopped; } -bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, u8 queue, - enum wlcore_queue_stop_reason reason) +bool wlcore_is_queue_stopped_by_reason_locked(struct wl1271 *wl, + struct wl12xx_vif *wlvif, u8 queue, + enum wlcore_queue_stop_reason reason) { - return test_bit(reason, &wl->queue_stop_reasons[queue]); + int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue); + + WARN_ON_ONCE(!spin_is_locked(&wl->wl_lock)); + return test_bit(reason, &wl->queue_stop_reasons[hwq]); } -bool wlcore_is_queue_stopped(struct wl1271 *wl, u8 queue) +bool wlcore_is_queue_stopped_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 queue) { - return !!wl->queue_stop_reasons[queue]; + int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue); + + WARN_ON_ONCE(!spin_is_locked(&wl->wl_lock)); + return !!wl->queue_stop_reasons[hwq]; } diff --git a/drivers/net/wireless/ti/wlcore/tx.h b/drivers/net/wireless/ti/wlcore/tx.h index 349520d..55aa4ac 100644 --- a/drivers/net/wireless/ti/wlcore/tx.h +++ b/drivers/net/wireless/ti/wlcore/tx.h @@ -207,19 +207,22 @@ static inline int wl1271_tx_get_queue(int queue) } } -static inline int wl1271_tx_get_mac80211_queue(int queue) +static inline +int wlcore_tx_get_mac80211_queue(struct wl12xx_vif *wlvif, int queue) { + int mac_queue = wlvif->hw_queue_base; + switch (queue) { case CONF_TX_AC_VO: - return 0; + return mac_queue + 0; case CONF_TX_AC_VI: - return 1; + return mac_queue + 1; case CONF_TX_AC_BE: - return 2; + return mac_queue + 2; case CONF_TX_AC_BK: - return 3; + return mac_queue + 3; default: - return 2; + return mac_queue + 2; } } @@ -252,20 +255,26 @@ void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids); unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl, unsigned int packet_length); void wl1271_free_tx_id(struct wl1271 *wl, int id); -void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue, - enum wlcore_queue_stop_reason reason); -void wlcore_stop_queue(struct wl1271 *wl, u8 queue, +void wlcore_stop_queue_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 queue, enum wlcore_queue_stop_reason reason); +void wlcore_stop_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue, enum wlcore_queue_stop_reason reason); -void wlcore_wake_queue(struct wl1271 *wl, u8 queue, +void wlcore_wake_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue, enum wlcore_queue_stop_reason reason); void wlcore_stop_queues(struct wl1271 *wl, enum wlcore_queue_stop_reason reason); void wlcore_wake_queues(struct wl1271 *wl, enum wlcore_queue_stop_reason reason); -void wlcore_reset_stopped_queues(struct wl1271 *wl); -bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, u8 queue, +bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, + struct wl12xx_vif *wlvif, u8 queue, enum wlcore_queue_stop_reason reason); -bool wlcore_is_queue_stopped(struct wl1271 *wl, u8 queue); +bool +wlcore_is_queue_stopped_by_reason_locked(struct wl1271 *wl, + struct wl12xx_vif *wlvif, + u8 queue, + enum wlcore_queue_stop_reason reason); +bool wlcore_is_queue_stopped_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 queue); /* from main.c */ void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid); diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index c388493..ebd8c6f 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -37,6 +37,9 @@ */ #define WLCORE_NUM_MAC_ADDRESSES 3 +/* wl12xx/wl18xx maximum transmission power (in dBm) */ +#define WLCORE_MAX_TXPWR 25 + /* forward declaration */ struct wl1271_tx_hw_descr; enum wl_rx_buf_align; @@ -51,6 +54,9 @@ struct wlcore_ops { int (*trigger_cmd)(struct wl1271 *wl, int cmd_box_addr, void *buf, size_t len); int (*ack_event)(struct wl1271 *wl); + int (*wait_for_event)(struct wl1271 *wl, enum wlcore_wait_event event, + bool *timeout); + int (*process_mailbox_events)(struct wl1271 *wl); u32 (*calc_tx_blocks)(struct wl1271 *wl, u32 len, u32 spare_blks); void (*set_tx_desc_blocks)(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, @@ -82,12 +88,32 @@ struct wlcore_ops { int (*debugfs_init)(struct wl1271 *wl, struct dentry *rootdir); int (*handle_static_data)(struct wl1271 *wl, struct wl1271_static_data *static_data); + int (*scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct cfg80211_scan_request *req); + int (*scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif); + int (*sched_scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies); + void (*sched_scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif); int (*get_spare_blocks)(struct wl1271 *wl, bool is_gem); int (*set_key)(struct wl1271 *wl, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key_conf); + int (*channel_switch)(struct wl1271 *wl, + struct wl12xx_vif *wlvif, + struct ieee80211_channel_switch *ch_switch); u32 (*pre_pkt_send)(struct wl1271 *wl, u32 buf_offset, u32 last_len); + void (*sta_rc_update)(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct ieee80211_sta *sta, u32 changed); + int (*set_peer_cap)(struct wl1271 *wl, + struct ieee80211_sta_ht_cap *ht_cap, + bool allow_ht_operation, + u32 rate_set, u8 hlid); + bool (*lnk_high_prio)(struct wl1271 *wl, u8 hlid, + struct wl1271_link *lnk); + bool (*lnk_low_prio)(struct wl1271 *wl, u8 hlid, + struct wl1271_link *lnk); }; enum wlcore_partitions { @@ -202,6 +228,8 @@ struct wl1271 { unsigned long klv_templates_map[ BITS_TO_LONGS(WLCORE_MAX_KLV_TEMPLATES)]; + u8 session_ids[WL12XX_MAX_LINKS]; + struct list_head wlvif_list; u8 sta_count; @@ -227,7 +255,8 @@ struct wl1271 { /* Frames scheduled for transmission, not handled yet */ int tx_queue_count[NUM_TX_QUEUES]; - unsigned long queue_stop_reasons[NUM_TX_QUEUES]; + unsigned long queue_stop_reasons[ + NUM_TX_QUEUES * WLCORE_NUM_MAC_ADDRESSES]; /* Frames received, not handled yet by mac80211 */ struct sk_buff_head deferred_rx_queue; @@ -269,24 +298,30 @@ struct wl1271 { struct work_struct recovery_work; bool watchdog_recovery; + /* Reg domain last configuration */ + u32 reg_ch_conf_last[2]; + /* Reg domain pending configuration */ + u32 reg_ch_conf_pending[2]; + /* Pointer that holds DMA-friendly block for the mailbox */ - struct event_mailbox *mbox; + void *mbox; /* The mbox event mask */ u32 event_mask; /* Mailbox pointers */ + u32 mbox_size; u32 mbox_ptr[2]; /* Are we currently scanning */ - struct ieee80211_vif *scan_vif; + struct wl12xx_vif *scan_wlvif; struct wl1271_scan scan; struct delayed_work scan_complete_work; - /* Connection loss work */ - struct delayed_work connection_loss_work; + struct ieee80211_vif *roc_vif; + struct delayed_work roc_complete_work; - bool sched_scanning; + struct wl12xx_vif *sched_vif; /* The current band */ enum ieee80211_band band; @@ -299,7 +334,7 @@ struct wl1271 { struct wl1271_stats stats; - __le32 buffer_32; + __le32 *buffer_32; u32 buffer_cmd; u32 buffer_busyword[WL1271_BUSY_WORD_CNT]; @@ -314,6 +349,8 @@ struct wl1271 { bool enable_11a; + int recovery_count; + /* Most recently reported noise in dBm */ s8 noise; @@ -333,6 +370,12 @@ struct wl1271 { */ struct wl1271_link links[WL12XX_MAX_LINKS]; + /* number of currently active links */ + int active_link_count; + + /* Fast/slow links bitmap according to FW */ + u32 fw_fast_lnk_map; + /* AP-mode - a bitmap of links currently in PS mode according to FW */ u32 ap_fw_ps_map; @@ -367,6 +410,12 @@ struct wl1271 { const char *sr_fw_name; const char *mr_fw_name; + u8 scan_templ_id_2_4; + u8 scan_templ_id_5; + u8 sched_scan_templ_id_2_4; + u8 sched_scan_templ_id_5; + u8 max_channels_5; + /* per-chip-family private structure */ void *priv; @@ -408,20 +457,28 @@ struct wl1271 { /* the number of allocated MAC addresses in this chip */ int num_mac_addr; - /* the minimum FW version required for the driver to work */ - unsigned int min_fw_ver[NUM_FW_VER]; + /* minimum FW version required for the driver to work in single-role */ + unsigned int min_sr_fw_ver[NUM_FW_VER]; + + /* minimum FW version required for the driver to work in multi-role */ + unsigned int min_mr_fw_ver[NUM_FW_VER]; struct completion nvs_loading_complete; + + /* number of concurrent channels the HW supports */ + u32 num_channels; }; int wlcore_probe(struct wl1271 *wl, struct platform_device *pdev); int wlcore_remove(struct platform_device *pdev); -struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size); +struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size, + u32 mbox_size); int wlcore_free_hw(struct wl1271 *wl); int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key_conf); +void wlcore_regdomain_config(struct wl1271 *wl); static inline void wlcore_set_ht_cap(struct wl1271 *wl, enum ieee80211_band band, @@ -430,16 +487,27 @@ wlcore_set_ht_cap(struct wl1271 *wl, enum ieee80211_band band, memcpy(&wl->ht_cap[band], ht_cap, sizeof(*ht_cap)); } +/* Tell wlcore not to care about this element when checking the version */ +#define WLCORE_FW_VER_IGNORE -1 + static inline void wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip, - unsigned int iftype, unsigned int major, - unsigned int subtype, unsigned int minor) + unsigned int iftype_sr, unsigned int major_sr, + unsigned int subtype_sr, unsigned int minor_sr, + unsigned int iftype_mr, unsigned int major_mr, + unsigned int subtype_mr, unsigned int minor_mr) { - wl->min_fw_ver[FW_VER_CHIP] = chip; - wl->min_fw_ver[FW_VER_IF_TYPE] = iftype; - wl->min_fw_ver[FW_VER_MAJOR] = major; - wl->min_fw_ver[FW_VER_SUBTYPE] = subtype; - wl->min_fw_ver[FW_VER_MINOR] = minor; + wl->min_sr_fw_ver[FW_VER_CHIP] = chip; + wl->min_sr_fw_ver[FW_VER_IF_TYPE] = iftype_sr; + wl->min_sr_fw_ver[FW_VER_MAJOR] = major_sr; + wl->min_sr_fw_ver[FW_VER_SUBTYPE] = subtype_sr; + wl->min_sr_fw_ver[FW_VER_MINOR] = minor_sr; + + wl->min_mr_fw_ver[FW_VER_CHIP] = chip; + wl->min_mr_fw_ver[FW_VER_IF_TYPE] = iftype_mr; + wl->min_mr_fw_ver[FW_VER_MAJOR] = major_mr; + wl->min_mr_fw_ver[FW_VER_SUBTYPE] = subtype_mr; + wl->min_mr_fw_ver[FW_VER_MINOR] = minor_mr; } /* Firmware image load chunk size */ @@ -450,6 +518,9 @@ wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip, /* Each RX/TX transaction requires an end-of-transaction transfer */ #define WLCORE_QUIRK_END_OF_TRANSACTION BIT(0) +/* the first start_role(sta) sometimes doesn't work on wl12xx */ +#define WLCORE_QUIRK_START_STA_FAILS BIT(1) + /* wl127x and SPI don't support SDIO block size alignment */ #define WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN BIT(2) @@ -462,9 +533,6 @@ wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip, /* Older firmwares use an old NVS format */ #define WLCORE_QUIRK_LEGACY_NVS BIT(5) -/* Some firmwares may not support ELP */ -#define WLCORE_QUIRK_NO_ELP BIT(6) - /* pad only the last frame in the aggregate buffer */ #define WLCORE_QUIRK_TX_PAD_LAST_FRAME BIT(7) @@ -477,11 +545,11 @@ wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip, /* separate probe response templates for one-shot and sched scans */ #define WLCORE_QUIRK_DUAL_PROBE_TMPL BIT(10) -/* TODO: move to the lower drivers when all usages are abstracted */ -#define CHIP_ID_1271_PG10 (0x4030101) -#define CHIP_ID_1271_PG20 (0x4030111) -#define CHIP_ID_1283_PG10 (0x05030101) -#define CHIP_ID_1283_PG20 (0x05030111) +/* Firmware requires reg domain configuration for active calibration */ +#define WLCORE_QUIRK_REGDOMAIN_CONF BIT(11) + +/* The FW only support a zero session id for AP */ +#define WLCORE_QUIRK_AP_ZERO_SESSION_ID BIT(12) /* TODO: move all these common registers and values elsewhere */ #define HW_ACCESS_ELP_CTRL_REG 0x1FFFC diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index 6678d4b..20316ac 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -109,17 +109,6 @@ enum { NUM_FW_VER }; -#define FW_VER_CHIP_WL127X 6 -#define FW_VER_CHIP_WL128X 7 - -#define FW_VER_IF_TYPE_STA 1 -#define FW_VER_IF_TYPE_AP 2 - -#define FW_VER_MINOR_1_SPARE_STA_MIN 58 -#define FW_VER_MINOR_1_SPARE_AP_MIN 47 - -#define FW_VER_MINOR_FWLOG_STA_MIN 70 - struct wl1271_chip { u32 id; char fw_ver_str[ETHTOOL_BUSINFO_LEN]; @@ -141,7 +130,10 @@ struct wl_fw_packet_counters { /* Cumulative counter of released Voice memory blocks */ u8 tx_voice_released_blks; - u8 padding[3]; + /* Tx rate of the last transmitted packet */ + u8 tx_last_rate; + + u8 padding[2]; } __packed; /* FW status registers */ @@ -260,6 +252,8 @@ enum wl12xx_vif_flags { WLVIF_FLAG_IN_USE, }; +struct wl12xx_vif; + struct wl1271_link { /* AP-mode - TX queue per AC in link */ struct sk_buff_head tx_queue[NUM_TX_QUEUES]; @@ -272,6 +266,9 @@ struct wl1271_link { /* bitmap of TIDs where RX BA sessions are active for this link */ u8 ba_bitmap; + + /* The wlvif this link belongs to. Might be null for global links */ + struct wl12xx_vif *wlvif; }; #define WL1271_MAX_RX_FILTERS 5 @@ -315,6 +312,7 @@ struct wl12xx_rx_filter { struct wl1271_station { u8 hlid; + bool in_connection; }; struct wl12xx_vif { @@ -332,7 +330,6 @@ struct wl12xx_vif { union { struct { u8 hlid; - u8 ba_rx_bitmap; u8 basic_rate_idx; u8 ap_rate_idx; @@ -341,6 +338,8 @@ struct wl12xx_vif { u8 klv_template_id; bool qos; + /* channel type we started the STA role with */ + enum nl80211_channel_type role_chan_type; } sta; struct { u8 global_hlid; @@ -362,6 +361,9 @@ struct wl12xx_vif { /* the hlid of the last transmitted skb */ int last_tx_hlid; + /* counters of packets per AC, across all links in the vif */ + int tx_queue_count[NUM_TX_QUEUES]; + unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)]; u8 ssid[IEEE80211_MAX_SSID_LEN + 1]; @@ -396,9 +398,6 @@ struct wl12xx_vif { /* Our association ID */ u16 aid; - /* Session counter for the chipset */ - int session_counter; - /* retry counter for PSM entries */ u8 psm_entry_retry; @@ -416,11 +415,28 @@ struct wl12xx_vif { bool ba_support; bool ba_allowed; + bool wmm_enabled; + /* Rx Streaming */ struct work_struct rx_streaming_enable_work; struct work_struct rx_streaming_disable_work; struct timer_list rx_streaming_timer; + struct delayed_work channel_switch_work; + struct delayed_work connection_loss_work; + + /* number of in connection stations */ + int inconn_count; + + /* + * This vif's queues are mapped to mac80211 HW queues as: + * VO - hw_queue_base + * VI - hw_queue_base + 1 + * BE - hw_queue_base + 2 + * BK - hw_queue_base + 3 + */ + int hw_queue_base; + /* * This struct must be last! * data that has to be saved acrossed reconfigs (e.g. recovery) @@ -443,6 +459,7 @@ struct wl12xx_vif { static inline struct wl12xx_vif *wl12xx_vif_to_data(struct ieee80211_vif *vif) { + WARN_ON(!vif); return (struct wl12xx_vif *)vif->drv_priv; } |