From 647291f5c1596839eb69d6c1f231b2249a703c27 Mon Sep 17 00:00:00 2001 From: Ben Cahill Date: Tue, 2 Mar 2010 12:48:25 -0800 Subject: [PATCH 01/60] iwlagn: Add Flow-handler (FH) register dump for 4965. FH register dump debug feature was recently added for 5000 and later; 4965 can use it also. Signed-off-by: Ben Cahill Signed-off-by: Reinette Chatre --- drivers/net/wireless/iwlwifi/iwl-4965.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index 644aacfbd7df..271576ab5747 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -2187,6 +2187,7 @@ static struct iwl_lib_ops iwl4965_lib = { .load_ucode = iwl4965_load_bsm, .dump_nic_event_log = iwl_dump_nic_event_log, .dump_nic_error_log = iwl_dump_nic_error_log, + .dump_fh = iwl_dump_fh, .set_channel_switch = iwl4965_hw_channel_switch, .apm_ops = { .init = iwl_apm_init, From 7e2461910e9115c9964975f77584baf8c2f76bfe Mon Sep 17 00:00:00 2001 From: Reinette Chatre Date: Thu, 18 Feb 2010 22:58:32 -0800 Subject: [PATCH 02/60] iwlwifi: only add broadcast station once Currently the broadcast station is added after every RXON command. Change this to only add the broadcast station when interface is added by mac80211. With this we need some extra work to ensure broadcast station is always present since station table is cleared when RXON without ASSOC bit set is sent. To deal with this we re-add all driver known stations to uCode after such an RXON command is sent. We also do some cleanup and remove the various calls to clear the station table. We now only clear the station table in two scenarios: - only clear uCode portion of station table when RXON command without ASSOC bit is sent - clear uCode and driver portion when interface goes down or is removed. We need to do this clearing when interface goes down to deal with the device restart/reconfigure routines which do not remove the interface, but do add the interface during reconfiguration. Previously the keys were also cleared when station table in driver is cleared, this is not done anymore since mac80211 will take care that keys are set and cleared correctly. There is a known issue with this change. Associating with different AP without bringing interface down fails with a firmware error. This is because of the lack of full station notification support and the later patches in this series that complete the station notification support will fix this. Signed-off-by: Reinette Chatre --- drivers/net/wireless/iwlwifi/iwl-3945.c | 10 +- drivers/net/wireless/iwlwifi/iwl-5000.c | 1 - drivers/net/wireless/iwlwifi/iwl-agn.c | 27 ++--- drivers/net/wireless/iwlwifi/iwl-core.c | 8 +- drivers/net/wireless/iwlwifi/iwl-sta.c | 114 +++++++++++++++----- drivers/net/wireless/iwlwifi/iwl-sta.h | 9 +- drivers/net/wireless/iwlwifi/iwl3945-base.c | 11 +- 7 files changed, 120 insertions(+), 60 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index ed59e2744ca4..7f95f9019093 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -1911,6 +1911,8 @@ static int iwl3945_commit_rxon(struct iwl_priv *priv) "configuration (%d).\n", rc); return rc; } + iwl_clear_ucode_stations(priv, false); + iwl_restore_stations(priv); } IWL_DEBUG_INFO(priv, "Sending RXON\n" @@ -1941,7 +1943,10 @@ static int iwl3945_commit_rxon(struct iwl_priv *priv) memcpy(active_rxon, staging_rxon, sizeof(*active_rxon)); - iwl_clear_stations_table(priv); + if (!new_assoc) { + iwl_clear_ucode_stations(priv, false); + iwl_restore_stations(priv); + } /* If we issue a new RXON command which required a tune then we must * send a new TXPOWER command or we won't be able to Tx any frames */ @@ -1951,9 +1956,6 @@ static int iwl3945_commit_rxon(struct iwl_priv *priv) return rc; } - /* Add the broadcast address so we can send broadcast frames */ - priv->cfg->ops->lib->add_bcast_station(priv); - /* If we have set the ASSOC_MSK and we are in BSS mode then * add the IWL_AP_ID to the station rate table */ if (iwl_is_associated(priv) && diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 37e1e77f513d..046bdd96e5a0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -544,7 +544,6 @@ void iwl5000_init_alive_start(struct iwl_priv *priv) goto restart; } - iwl_clear_stations_table(priv); ret = priv->cfg->ops->lib->alive_notify(priv); if (ret) { IWL_WARN(priv, diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index d6e1a059b9b0..85ff1552481f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -166,6 +166,8 @@ int iwl_commit_rxon(struct iwl_priv *priv) IWL_ERR(priv, "Error clearing ASSOC_MSK (%d)\n", ret); return ret; } + iwl_clear_ucode_stations(priv, false); + iwl_restore_stations(priv); } IWL_DEBUG_INFO(priv, "Sending RXON\n" @@ -179,9 +181,8 @@ int iwl_commit_rxon(struct iwl_priv *priv) iwl_set_rxon_hwcrypto(priv, !priv->cfg->mod_params->sw_crypto); /* Apply the new configuration - * RXON unassoc clears the station table in uCode, send it before - * we add the bcast station. If assoc bit is set, we will send RXON - * after having added the bcast and bssid station. + * RXON unassoc clears the station table in uCode so restoration of + * stations is needed after it (the RXON command) completes */ if (!new_assoc) { ret = iwl_send_cmd_pdu(priv, REPLY_RXON, @@ -190,17 +191,14 @@ int iwl_commit_rxon(struct iwl_priv *priv) IWL_ERR(priv, "Error setting new RXON (%d)\n", ret); return ret; } + IWL_DEBUG_INFO(priv, "Return from !new_assoc RXON. \n"); memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon)); + iwl_clear_ucode_stations(priv, false); + iwl_restore_stations(priv); } - iwl_clear_stations_table(priv); - priv->start_calib = 0; - /* Add the broadcast address so we can send broadcast frames */ - priv->cfg->ops->lib->add_bcast_station(priv); - - /* If we have set the ASSOC_MSK and we are in BSS mode then * add the IWL_AP_ID to the station rate table */ if (new_assoc) { @@ -2087,7 +2085,6 @@ static void iwl_alive_start(struct iwl_priv *priv) goto restart; } - iwl_clear_stations_table(priv); ret = priv->cfg->ops->lib->alive_notify(priv); if (ret) { IWL_WARN(priv, @@ -2143,6 +2140,8 @@ static void iwl_alive_start(struct iwl_priv *priv) wake_up_interruptible(&priv->wait_command_queue); iwl_power_update_mode(priv, true); + IWL_DEBUG_INFO(priv, "Updated power mode\n"); + return; @@ -2162,7 +2161,7 @@ static void __iwl_down(struct iwl_priv *priv) if (!exit_pending) set_bit(STATUS_EXIT_PENDING, &priv->status); - iwl_clear_stations_table(priv); + iwl_clear_ucode_stations(priv, true); /* Unblock any waiting calls */ wake_up_interruptible_all(&priv->wait_command_queue); @@ -2359,8 +2358,6 @@ static int __iwl_up(struct iwl_priv *priv) for (i = 0; i < MAX_HW_RESTARTS; i++) { - iwl_clear_stations_table(priv); - /* load bootstrap state machine, * load bootstrap program into processor's memory, * prepare to load the "initialize" uCode */ @@ -3270,9 +3267,6 @@ static int iwl_init_drv(struct iwl_priv *priv) mutex_init(&priv->mutex); mutex_init(&priv->sync_cmd_mutex); - /* Clear the driver's (not device's) station table */ - iwl_clear_stations_table(priv); - priv->ieee_channels = NULL; priv->ieee_rates = NULL; priv->band = IEEE80211_BAND_2GHZ; @@ -3649,7 +3643,6 @@ static void __devexit iwl_pci_remove(struct pci_dev *pdev) iwl_rx_queue_free(priv, &priv->rxq); iwl_hw_txq_ctx_free(priv); - iwl_clear_stations_table(priv); iwl_eeprom_free(priv); diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index ec435e5491d9..a33e4536657a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -2283,8 +2283,6 @@ static int iwl_set_mode(struct iwl_priv *priv, struct ieee80211_vif *vif) memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN); - iwl_clear_stations_table(priv); - return iwlcore_commit_rxon(priv); } @@ -2317,6 +2315,10 @@ int iwl_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) err = iwl_set_mode(priv, vif); if (err) goto out_err; + + /* Add the broadcast address so we can send broadcast frames */ + priv->cfg->ops->lib->add_bcast_station(priv); + goto out; out_err: @@ -2339,6 +2341,8 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw, mutex_lock(&priv->mutex); + iwl_clear_ucode_stations(priv, true); + if (iwl_is_ready_rf(priv)) { iwl_scan_cancel_timeout(priv, 100); priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c index b1aad306efa9..312099099ed3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sta.c +++ b/drivers/net/wireless/iwlwifi/iwl-sta.c @@ -34,9 +34,6 @@ #include "iwl-core.h" #include "iwl-sta.h" -#define IWL_STA_DRIVER_ACTIVE BIT(0) /* driver entry is active */ -#define IWL_STA_UCODE_ACTIVE BIT(1) /* ucode entry is active */ - u8 iwl_find_station(struct iwl_priv *priv, const u8 *addr) { int i; @@ -495,37 +492,102 @@ out: } /** - * iwl_clear_stations_table - Clear the driver's station table - * - * NOTE: This does not clear or otherwise alter the device's station table. + * iwl_clear_ucode_stations() - clear entire station table driver and/or ucode + * @priv: + * @force: If set then the uCode station table needs to be cleared here. If + * not set then the uCode station table has already been cleared, + * for example after sending it a RXON command without ASSOC bit + * set, and we just need to change driver state here. */ -void iwl_clear_stations_table(struct iwl_priv *priv) +void iwl_clear_ucode_stations(struct iwl_priv *priv, bool force) { - unsigned long flags; int i; + unsigned long flags_spin; + bool cleared = false; - spin_lock_irqsave(&priv->sta_lock, flags); + IWL_DEBUG_INFO(priv, "Clearing ucode stations in driver%s\n", + force ? " and ucode" : ""); - if (iwl_is_alive(priv) && - !test_bit(STATUS_EXIT_PENDING, &priv->status) && - iwl_send_cmd_pdu_async(priv, REPLY_REMOVE_ALL_STA, 0, NULL, NULL)) - IWL_ERR(priv, "Couldn't clear the station table\n"); - - priv->num_stations = 0; - memset(priv->stations, 0, sizeof(priv->stations)); - - /* clean ucode key table bit map */ - priv->ucode_key_table = 0; - - /* keep track of static keys */ - for (i = 0; i < WEP_KEYS_MAX ; i++) { - if (priv->wep_keys[i].key_size) - set_bit(i, &priv->ucode_key_table); + if (force) { + if (!iwl_is_ready(priv)) { + /* + * If device is not ready at this point the station + * table is likely already empty (uCode not ready + * to receive station requests) or will soon be + * due to interface going down. + */ + IWL_DEBUG_INFO(priv, "Unable to remove stations from device - device not ready\n"); + } else { + iwl_send_cmd_pdu_async(priv, REPLY_REMOVE_ALL_STA, 0, NULL, NULL); + } } - spin_unlock_irqrestore(&priv->sta_lock, flags); + spin_lock_irqsave(&priv->sta_lock, flags_spin); + if (force) { + IWL_DEBUG_INFO(priv, "Clearing all station information in driver\n"); + priv->num_stations = 0; + memset(priv->stations, 0, sizeof(priv->stations)); + } else { + for (i = 0; i < priv->hw_params.max_stations; i++) { + if (priv->stations[i].used & IWL_STA_UCODE_ACTIVE) { + IWL_DEBUG_INFO(priv, "Clearing ucode active for station %d \n", i); + priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE; + cleared = true; + } + } + } + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + + if (!cleared) + IWL_DEBUG_INFO(priv, "No active stations found to be cleared\n"); } -EXPORT_SYMBOL(iwl_clear_stations_table); +EXPORT_SYMBOL(iwl_clear_ucode_stations); + +/** + * iwl_restore_stations() - Restore driver known stations to device + * + * All stations considered active by driver, but not present in ucode, is + * restored. + */ +void iwl_restore_stations(struct iwl_priv *priv) +{ + unsigned long flags_spin; + int i; + bool found = false; + + if (!iwl_is_ready(priv)) { + IWL_DEBUG_INFO(priv, "Not ready yet, not restoring any stations.\n"); + return; + } + + IWL_DEBUG_ASSOC(priv, "Restoring all known stations ... start.\n"); + spin_lock_irqsave(&priv->sta_lock, flags_spin); + for (i = 0; i < priv->hw_params.max_stations; i++) { + if ((priv->stations[i].used & IWL_STA_DRIVER_ACTIVE) && + !(priv->stations[i].used & IWL_STA_UCODE_ACTIVE)) { + IWL_DEBUG_ASSOC(priv, "Restoring sta %pM\n", + priv->stations[i].sta.sta.addr); + priv->stations[i].sta.mode = 0; + priv->stations[i].used |= IWL_STA_UCODE_INPROGRESS; + found = true; + } + } + + for (i = 0; i < priv->hw_params.max_stations; i++) { + if ((priv->stations[i].used & IWL_STA_UCODE_INPROGRESS)) { + iwl_send_add_sta(priv, &priv->stations[i].sta, + CMD_ASYNC); + priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS; + } + } + + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + if (!found) + IWL_DEBUG_INFO(priv, "Restoring all known stations .... no stations to be restored.\n"); + else + IWL_DEBUG_INFO(priv, "Restoring all known stations .... in progress.\n"); +} +EXPORT_SYMBOL(iwl_restore_stations); int iwl_get_free_ucode_key_index(struct iwl_priv *priv) { diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.h b/drivers/net/wireless/iwlwifi/iwl-sta.h index 2dc35fe28f56..50367e83defd 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sta.h +++ b/drivers/net/wireless/iwlwifi/iwl-sta.h @@ -32,6 +32,12 @@ #define HW_KEY_DYNAMIC 0 #define HW_KEY_DEFAULT 1 +#define IWL_STA_DRIVER_ACTIVE BIT(0) /* driver entry is active */ +#define IWL_STA_UCODE_ACTIVE BIT(1) /* ucode entry is active */ +#define IWL_STA_UCODE_INPROGRESS BIT(2) /* ucode entry is in process of + being activated */ + + /** * iwl_find_station - Find station id for a given BSSID * @bssid: MAC address of station ID to find @@ -55,7 +61,8 @@ int iwl_rxon_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap); void iwl_add_bcast_station(struct iwl_priv *priv); void iwl3945_add_bcast_station(struct iwl_priv *priv); int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, bool is_ap); -void iwl_clear_stations_table(struct iwl_priv *priv); +void iwl_restore_stations(struct iwl_priv *priv); +void iwl_clear_ucode_stations(struct iwl_priv *priv, bool force); int iwl_get_free_ucode_key_index(struct iwl_priv *priv); int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr); int iwl_get_ra_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr); diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 2579bbcaab36..6d3e6a85f932 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -2480,8 +2480,6 @@ static void iwl3945_alive_start(struct iwl_priv *priv) goto restart; } - iwl_clear_stations_table(priv); - rfkill = iwl_read_prph(priv, APMG_RFKILL_REG); IWL_DEBUG_INFO(priv, "RFKILL status: 0x%x\n", rfkill); @@ -2558,7 +2556,8 @@ static void __iwl3945_down(struct iwl_priv *priv) if (!exit_pending) set_bit(STATUS_EXIT_PENDING, &priv->status); - iwl_clear_stations_table(priv); + /* Station information will now be cleared in device */ + iwl_clear_ucode_stations(priv, true); /* Unblock any waiting calls */ wake_up_interruptible_all(&priv->wait_command_queue); @@ -2692,8 +2691,6 @@ static int __iwl3945_up(struct iwl_priv *priv) for (i = 0; i < MAX_HW_RESTARTS; i++) { - iwl_clear_stations_table(priv); - /* load bootstrap state machine, * load bootstrap program into processor's memory, * prepare to load the "initialize" uCode */ @@ -3834,9 +3831,6 @@ static int iwl3945_init_drv(struct iwl_priv *priv) mutex_init(&priv->mutex); mutex_init(&priv->sync_cmd_mutex); - /* Clear the driver's (not device's) station table */ - iwl_clear_stations_table(priv); - priv->ieee_channels = NULL; priv->ieee_rates = NULL; priv->band = IEEE80211_BAND_2GHZ; @@ -4196,7 +4190,6 @@ static void __devexit iwl3945_pci_remove(struct pci_dev *pdev) iwl3945_hw_txq_ctx_free(priv); iwl3945_unset_hw_params(priv); - iwl_clear_stations_table(priv); /*netif_stop_queue(dev); */ flush_workqueue(priv->workqueue); From fe6b23dd361199bfbc50b0cbce6bed37c5797c75 Mon Sep 17 00:00:00 2001 From: Reinette Chatre Date: Mon, 22 Feb 2010 16:24:47 -0800 Subject: [PATCH 03/60] iwlwifi: implement new mac80211 station add/remove calls mac80211 recently implemented two new callbacks that are used to request station add/remove from the driver. The benefot from these new callbacks are that they enable the driver to sleep while performing this work. This is a big patch since a few things need to be coordinated in this move. First we need to decouple station management from rate scaling, which caused a lot of code to be moved and/or deleted. Next we needed to tie in with mac80211's station management callback and let it direct our station management as well as trigger the rate scaling initialization. Signed-off-by: Reinette Chatre --- drivers/net/wireless/iwlwifi/iwl-3945-rs.c | 83 +-- drivers/net/wireless/iwlwifi/iwl-3945.c | 10 - drivers/net/wireless/iwlwifi/iwl-3945.h | 1 - drivers/net/wireless/iwlwifi/iwl-agn-rs.c | 120 ++--- drivers/net/wireless/iwlwifi/iwl-agn-rs.h | 7 +- drivers/net/wireless/iwlwifi/iwl-agn.c | 95 ++-- drivers/net/wireless/iwlwifi/iwl-core.c | 1 - drivers/net/wireless/iwlwifi/iwl-core.h | 2 +- drivers/net/wireless/iwlwifi/iwl-dev.h | 8 +- drivers/net/wireless/iwlwifi/iwl-sta.c | 534 +++++++++++--------- drivers/net/wireless/iwlwifi/iwl-sta.h | 11 +- drivers/net/wireless/iwlwifi/iwl3945-base.c | 45 +- 12 files changed, 525 insertions(+), 392 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c index b588cb69536a..605aca4c78c8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c @@ -329,16 +329,25 @@ static void iwl3945_collect_tx_data(struct iwl3945_rs_sta *rs_sta, } -static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, void *priv_sta) +/* + * Called after adding a new station to initialize rate scaling + */ +void iwl3945_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_id) { - struct iwl3945_rs_sta *rs_sta = priv_sta; - struct iwl_priv *priv = (struct iwl_priv *)priv_r; + struct ieee80211_hw *hw = priv->hw; + struct ieee80211_conf *conf = &priv->hw->conf; + struct iwl3945_sta_priv *psta; + struct iwl3945_rs_sta *rs_sta; + struct ieee80211_supported_band *sband; int i; - IWL_DEBUG_RATE(priv, "enter\n"); + IWL_DEBUG_INFO(priv, "enter \n"); + if (sta_id == priv->hw_params.bcast_sta_id) + goto out; - spin_lock_init(&rs_sta->lock); + psta = (struct iwl3945_sta_priv *) sta->drv_priv; + rs_sta = &psta->rs_sta; + sband = hw->wiphy->bands[conf->channel->band]; rs_sta->priv = priv; @@ -351,9 +360,7 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband, rs_sta->last_flush = jiffies; rs_sta->flush_time = IWL_RATE_FLUSH; rs_sta->last_tx_packets = 0; - rs_sta->ibss_sta_added = 0; - init_timer(&rs_sta->rate_scale_flush); rs_sta->rate_scale_flush.data = (unsigned long)rs_sta; rs_sta->rate_scale_flush.function = iwl3945_bg_rate_scale_flush; @@ -380,8 +387,10 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband, IWL_FIRST_OFDM_RATE; } +out: + priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; - IWL_DEBUG_RATE(priv, "leave\n"); + IWL_DEBUG_INFO(priv, "leave\n"); } static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) @@ -405,6 +414,9 @@ static void *rs_alloc_sta(void *iwl_priv, struct ieee80211_sta *sta, gfp_t gfp) rs_sta = &psta->rs_sta; + spin_lock_init(&rs_sta->lock); + init_timer(&rs_sta->rate_scale_flush); + IWL_DEBUG_RATE(priv, "leave\n"); return rs_sta; @@ -413,13 +425,14 @@ static void *rs_alloc_sta(void *iwl_priv, struct ieee80211_sta *sta, gfp_t gfp) static void rs_free_sta(void *iwl_priv, struct ieee80211_sta *sta, void *priv_sta) { - struct iwl3945_sta_priv *psta = (void *) sta->drv_priv; - struct iwl3945_rs_sta *rs_sta = &psta->rs_sta; - struct iwl_priv *priv __maybe_unused = rs_sta->priv; + struct iwl3945_rs_sta *rs_sta = priv_sta; - IWL_DEBUG_RATE(priv, "enter\n"); + /* + * Be careful not to use any members of iwl3945_rs_sta (like trying + * to use iwl_priv to print out debugging) since it may not be fully + * initialized at this point. + */ del_timer_sync(&rs_sta->rate_scale_flush); - IWL_DEBUG_RATE(priv, "leave\n"); } @@ -458,6 +471,13 @@ static void rs_tx_status(void *priv_rate, struct ieee80211_supported_band *sband return; } + /* Treat uninitialized rate scaling data same as non-existing. */ + if (!rs_sta->priv) { + IWL_DEBUG_RATE(priv, "leave: STA priv data uninitialized!\n"); + return; + } + + rs_sta->tx_packets++; scale_rate_index = first_index; @@ -625,7 +645,6 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, u32 fail_count; s8 scale_action = 0; unsigned long flags; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; u16 rate_mask = sta ? sta->supp_rates[sband->band] : 0; s8 max_rate_idx = -1; struct iwl_priv *priv = (struct iwl_priv *)priv_r; @@ -633,6 +652,12 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, IWL_DEBUG_RATE(priv, "enter\n"); + /* Treat uninitialized rate scaling data same as non-existing. */ + if (rs_sta && !rs_sta->priv) { + IWL_DEBUG_RATE(priv, "Rate scaling information not initialized yet.\n"); + priv_sta = NULL; + } + if (rate_control_send_low(sta, priv_sta, txrc)) return; @@ -650,20 +675,6 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, if (sband->band == IEEE80211_BAND_5GHZ) rate_mask = rate_mask << IWL_FIRST_OFDM_RATE; - if ((priv->iw_mode == NL80211_IFTYPE_ADHOC) && - !rs_sta->ibss_sta_added) { - u8 sta_id = iwl_find_station(priv, hdr->addr1); - - if (sta_id == IWL_INVALID_STATION) { - IWL_DEBUG_RATE(priv, "LQ: ADD station %pM\n", - hdr->addr1); - sta_id = iwl_add_station(priv, hdr->addr1, false, - CMD_ASYNC, NULL); - } - if (sta_id != IWL_INVALID_STATION) - rs_sta->ibss_sta_added = 1; - } - spin_lock_irqsave(&rs_sta->lock, flags); /* for recent assoc, choose best rate regarding @@ -883,12 +894,22 @@ static void iwl3945_remove_debugfs(void *priv, void *priv_sta) } #endif +/* + * Initialization of rate scaling information is done by driver after + * the station is added. Since mac80211 calls this function before a + * station is added we ignore it. + */ +static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta, void *priv_sta) +{ +} + static struct rate_control_ops rs_ops = { .module = NULL, .name = RS_NAME, .tx_status = rs_tx_status, .get_rate = rs_get_rate, - .rate_init = rs_rate_init, + .rate_init = rs_rate_init_stub, .alloc = rs_alloc, .free = rs_free, .alloc_sta = rs_alloc_sta, @@ -899,7 +920,6 @@ static struct rate_control_ops rs_ops = { #endif }; - void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id) { struct iwl_priv *priv = hw->priv; @@ -916,6 +936,7 @@ void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id) sta = ieee80211_find_sta(priv->vif, priv->stations[sta_id].sta.sta.addr); if (!sta) { + IWL_DEBUG_RATE(priv, "Unable to find station to initialize rate scaling.\n"); rcu_read_unlock(); return; } diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index 7f95f9019093..11785e2c767a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -1956,16 +1956,6 @@ static int iwl3945_commit_rxon(struct iwl_priv *priv) return rc; } - /* If we have set the ASSOC_MSK and we are in BSS mode then - * add the IWL_AP_ID to the station rate table */ - if (iwl_is_associated(priv) && - (priv->iw_mode == NL80211_IFTYPE_STATION)) - if (iwl_add_station(priv, priv->active_rxon.bssid_addr, - true, CMD_SYNC, NULL) == IWL_INVALID_STATION) { - IWL_ERR(priv, "Error adding AP address for transmit\n"); - return -EIO; - } - /* Init the hardware's rate fallback order based on the band */ rc = iwl3945_init_hw_rate_table(priv); if (rc) { diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.h b/drivers/net/wireless/iwlwifi/iwl-3945.h index 452dfd5456c6..b89219573b91 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.h +++ b/drivers/net/wireless/iwlwifi/iwl-3945.h @@ -95,7 +95,6 @@ struct iwl3945_rs_sta { u8 tgg; u8 flush_pending; u8 start_rate; - u8 ibss_sta_added; struct timer_list rate_scale_flush; struct iwl3945_rate_scale_data win[IWL_RATE_COUNT_3945]; #ifdef CONFIG_MAC80211_DEBUGFS diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c index 84271cc62afa..970eec82d106 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c @@ -769,6 +769,15 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, IWL_DEBUG_RATE_LIMIT(priv, "get frame ack response, update rate scale window\n"); + /* Treat uninitialized rate scaling data same as non-existing. */ + if (!lq_sta) { + IWL_DEBUG_RATE(priv, "Station rate scaling not created yet.\n"); + return; + } else if (!lq_sta->drv) { + IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n"); + return; + } + if (!ieee80211_is_data(hdr->frame_control) || info->flags & IEEE80211_TX_CTL_NO_ACK) return; @@ -778,10 +787,6 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, !(info->flags & IEEE80211_TX_STAT_AMPDU)) return; - if ((priv->iw_mode == NL80211_IFTYPE_ADHOC) && - !lq_sta->ibss_sta_added) - return; - /* * Ignore this Tx frame response if its initial rate doesn't match * that of latest Link Quality command. There may be stragglers @@ -827,7 +832,7 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, lq_sta->missed_rate_counter++; if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) { lq_sta->missed_rate_counter = 0; - iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC); + iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC, false); } /* Regardless, ignore this status info for outdated rate */ return; @@ -1915,7 +1920,7 @@ static u32 rs_update_rate_tbl(struct iwl_priv *priv, /* Update uCode's rate table. */ rate = rate_n_flags_from_tbl(priv, tbl, index, is_green); rs_fill_link_cmd(priv, lq_sta, rate); - iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC); + iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC, false); return rate; } @@ -2291,7 +2296,7 @@ lq_update: IWL_DEBUG_RATE(priv, "Switch current mcs: %X index: %d\n", tbl->current_rate, index); rs_fill_link_cmd(priv, lq_sta, tbl->current_rate); - iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC); + iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC, false); } else done_search = 1; } @@ -2340,7 +2345,20 @@ out: return; } - +/** + * rs_initialize_lq - Initialize a station's hardware rate table + * + * The uCode's station table contains a table of fallback rates + * for automatic fallback during transmission. + * + * NOTE: This sets up a default set of values. These will be replaced later + * if the driver's iwl-agn-rs rate scaling algorithm is used, instead of + * rc80211_simple. + * + * NOTE: Run REPLY_ADD_STA command to set up station table entry, before + * calling this function (which runs REPLY_TX_LINK_QUALITY_CMD, + * which requires station table entry to exist). + */ static void rs_initialize_lq(struct iwl_priv *priv, struct ieee80211_conf *conf, struct ieee80211_sta *sta, @@ -2390,7 +2408,8 @@ static void rs_initialize_lq(struct iwl_priv *priv, tbl->current_rate = rate; rs_set_expected_tpt_table(lq_sta, tbl); rs_fill_link_cmd(NULL, lq_sta, rate); - iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC); + priv->stations[lq_sta->lq.sta_id].lq = &lq_sta->lq; + iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_SYNC, true); out: return; } @@ -2402,9 +2421,6 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, struct sk_buff *skb = txrc->skb; struct ieee80211_supported_band *sband = txrc->sband; struct iwl_priv *priv = (struct iwl_priv *)priv_r; - struct ieee80211_conf *conf = &priv->hw->conf; - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct iwl_lq_sta *lq_sta = priv_sta; int rate_idx; @@ -2422,30 +2438,18 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, lq_sta->max_rate_idx = -1; } + /* Treat uninitialized rate scaling data same as non-existing. */ + if (lq_sta && !lq_sta->drv) { + IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n"); + priv_sta = NULL; + } + /* Send management frames and NO_ACK data using lowest rate. */ if (rate_control_send_low(sta, priv_sta, txrc)) return; rate_idx = lq_sta->last_txrate_idx; - if ((priv->iw_mode == NL80211_IFTYPE_ADHOC) && - !lq_sta->ibss_sta_added) { - u8 sta_id = iwl_find_station(priv, hdr->addr1); - - if (sta_id == IWL_INVALID_STATION) { - IWL_DEBUG_RATE(priv, "LQ: ADD station %pM\n", - hdr->addr1); - sta_id = iwl_add_station(priv, hdr->addr1, - false, CMD_ASYNC, ht_cap); - } - if ((sta_id != IWL_INVALID_STATION)) { - lq_sta->lq.sta_id = sta_id; - lq_sta->lq.rs_table[0].rate_n_flags = 0; - lq_sta->ibss_sta_added = 1; - rs_initialize_lq(priv, conf, sta, lq_sta); - } - } - if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) { rate_idx -= IWL_FIRST_OFDM_RATE; /* 6M and 9M shared same MCS index */ @@ -2495,16 +2499,25 @@ static void *rs_alloc_sta(void *priv_rate, struct ieee80211_sta *sta, return lq_sta; } -static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, void *priv_sta) +/* + * Called after adding a new station to initialize rate scaling + */ +void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_id) { int i, j; - struct iwl_priv *priv = (struct iwl_priv *)priv_r; + struct ieee80211_hw *hw = priv->hw; struct ieee80211_conf *conf = &priv->hw->conf; struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - struct iwl_lq_sta *lq_sta = priv_sta; + struct iwl_station_priv *sta_priv; + struct iwl_lq_sta *lq_sta; + struct ieee80211_supported_band *sband; - lq_sta->lq.sta_id = 0xff; + sta_priv = (struct iwl_station_priv *) sta->drv_priv; + lq_sta = &sta_priv->lq_sta; + sband = hw->wiphy->bands[conf->channel->band]; + + + lq_sta->lq.sta_id = sta_id; for (j = 0; j < LQ_SIZE; j++) for (i = 0; i < IWL_RATE_COUNT; i++) @@ -2516,33 +2529,13 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband, for (i = 0; i < IWL_RATE_COUNT; i++) rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]); - IWL_DEBUG_RATE(priv, "LQ: *** rate scale station global init ***\n"); + IWL_DEBUG_RATE(priv, "LQ: *** rate scale station global init for station %d ***\n", + sta_id); /* TODO: what is a good starting rate for STA? About middle? Maybe not * the lowest or the highest rate.. Could consider using RSSI from * previous packets? Need to have IEEE 802.1X auth succeed immediately * after assoc.. */ - lq_sta->ibss_sta_added = 0; - if (priv->iw_mode == NL80211_IFTYPE_AP) { - u8 sta_id = iwl_find_station(priv, - sta->addr); - - /* for IBSS the call are from tasklet */ - IWL_DEBUG_RATE(priv, "LQ: ADD station %pM\n", sta->addr); - - if (sta_id == IWL_INVALID_STATION) { - IWL_DEBUG_RATE(priv, "LQ: ADD station %pM\n", sta->addr); - sta_id = iwl_add_station(priv, sta->addr, false, - CMD_ASYNC, ht_cap); - } - if ((sta_id != IWL_INVALID_STATION)) { - lq_sta->lq.sta_id = sta_id; - lq_sta->lq.rs_table[0].rate_n_flags = 0; - } - /* FIXME: this is w/a remove it later */ - priv->assoc_station_added = 1; - } - lq_sta->is_dup = 0; lq_sta->max_rate_idx = -1; lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX; @@ -2795,7 +2788,7 @@ static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file, if (lq_sta->dbg_fixed_rate) { rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate); - iwl_send_lq_cmd(lq_sta->drv, &lq_sta->lq, CMD_ASYNC); + iwl_send_lq_cmd(lq_sta->drv, &lq_sta->lq, CMD_ASYNC, false); } return count; @@ -2992,12 +2985,21 @@ static void rs_remove_debugfs(void *priv, void *priv_sta) } #endif +/* + * Initialization of rate scaling information is done by driver after + * the station is added. Since mac80211 calls this function before a + * station is added we ignore it. + */ +static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta, void *priv_sta) +{ +} static struct rate_control_ops rs_ops = { .module = NULL, .name = RS_NAME, .tx_status = rs_tx_status, .get_rate = rs_get_rate, - .rate_init = rs_rate_init, + .rate_init = rs_rate_init_stub, .alloc = rs_alloc, .free = rs_free, .alloc_sta = rs_alloc_sta, diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.h b/drivers/net/wireless/iwlwifi/iwl-agn-rs.h index e182f5a0f736..8292f6d48ec6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.h +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.h @@ -403,7 +403,6 @@ struct iwl_lq_sta { u8 is_green; u8 is_dup; enum ieee80211_band band; - u8 ibss_sta_added; /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */ u32 supp_rates; @@ -478,6 +477,12 @@ static inline u8 iwl3945_get_prev_ieee_rate(u8 rate_index) */ extern void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id); +/* Initialize station's rate scaling information after adding station */ +extern void iwl_rs_rate_init(struct iwl_priv *priv, + struct ieee80211_sta *sta, u8 sta_id); +extern void iwl3945_rs_rate_init(struct iwl_priv *priv, + struct ieee80211_sta *sta, u8 sta_id); + /** * iwl_rate_control_register - Register the rate control algorithm callbacks * diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 85ff1552481f..4b4b47676ad8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -144,9 +144,6 @@ int iwl_commit_rxon(struct iwl_priv *priv) return 0; } - /* station table will be cleared */ - priv->assoc_station_added = 0; - /* If we are currently associated and the new config requires * an RXON_ASSOC and the new config wants the associated mask enabled, * we must clear the associated from the active configuration @@ -198,25 +195,7 @@ int iwl_commit_rxon(struct iwl_priv *priv) } priv->start_calib = 0; - - /* If we have set the ASSOC_MSK and we are in BSS mode then - * add the IWL_AP_ID to the station rate table */ if (new_assoc) { - if (priv->iw_mode == NL80211_IFTYPE_STATION) { - ret = iwl_rxon_add_station(priv, - priv->active_rxon.bssid_addr, 1); - if (ret == IWL_INVALID_STATION) { - IWL_ERR(priv, - "Error adding AP address for TX.\n"); - return -EIO; - } - priv->assoc_station_added = 1; - if (priv->default_wep_key && - iwl_send_static_wepkey_cmd(priv, 0)) - IWL_ERR(priv, - "Could not send WEP static key.\n"); - } - /* * allow CTS-to-self if possible for new association. * this is relevant only for 5000 series and up, @@ -2498,10 +2477,6 @@ void iwl_post_associate(struct iwl_priv *priv) return; } - IWL_DEBUG_ASSOC(priv, "Associated as %d to: %pM\n", - priv->assoc_id, priv->active_rxon.bssid_addr); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; @@ -2553,6 +2528,9 @@ void iwl_post_associate(struct iwl_priv *priv) iwlcore_commit_rxon(priv); + IWL_DEBUG_ASSOC(priv, "Associated as %d to: %pM\n", + priv->assoc_id, priv->active_rxon.bssid_addr); + switch (priv->iw_mode) { case NL80211_IFTYPE_STATION: break; @@ -2562,7 +2540,7 @@ void iwl_post_associate(struct iwl_priv *priv) /* assume default assoc id */ priv->assoc_id = 1; - iwl_rxon_add_station(priv, priv->bssid, 0); + iwl_add_local_station(priv, priv->bssid, true); iwl_send_beacon_cmd(priv); break; @@ -2573,9 +2551,6 @@ void iwl_post_associate(struct iwl_priv *priv) break; } - if (priv->iw_mode == NL80211_IFTYPE_ADHOC) - priv->assoc_station_added = 1; - spin_lock_irqsave(&priv->lock, flags); iwl_activate_qos(priv, 0); spin_unlock_irqrestore(&priv->lock, flags); @@ -2974,18 +2949,7 @@ static void iwl_mac_sta_notify(struct ieee80211_hw *hw, struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; int sta_id; - /* - * TODO: We really should use this callback to - * actually maintain the station table in - * the device. - */ - switch (cmd) { - case STA_NOTIFY_ADD: - atomic_set(&sta_priv->pending_frames, 0); - if (vif->type == NL80211_IFTYPE_AP) - sta_priv->client = true; - break; case STA_NOTIFY_SLEEP: WARN_ON(!sta_priv->client); sta_priv->asleep = true; @@ -3006,6 +2970,55 @@ static void iwl_mac_sta_notify(struct ieee80211_hw *hw, } } +/** + * iwl_restore_wepkeys - Restore WEP keys to device + */ +static void iwl_restore_wepkeys(struct iwl_priv *priv) +{ + mutex_lock(&priv->mutex); + if (priv->iw_mode == NL80211_IFTYPE_STATION && + priv->default_wep_key && + iwl_send_static_wepkey_cmd(priv, 0)) + IWL_ERR(priv, "Could not send WEP static key\n"); + mutex_unlock(&priv->mutex); +} + +static int iwlagn_mac_sta_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + bool is_ap = priv->iw_mode == NL80211_IFTYPE_STATION; + int ret; + u8 sta_id; + + IWL_DEBUG_INFO(priv, "received request to add station %pM\n", + sta->addr); + + atomic_set(&sta_priv->pending_frames, 0); + if (vif->type == NL80211_IFTYPE_AP) + sta_priv->client = true; + + ret = iwl_add_station_common(priv, sta->addr, is_ap, &sta->ht_cap, + &sta_id); + if (ret) { + IWL_ERR(priv, "Unable to add station %pM (%d)\n", + sta->addr, ret); + /* Should we return success if return code is EEXIST ? */ + return ret; + } + + iwl_restore_wepkeys(priv); + + /* Initialize rate scaling */ + IWL_DEBUG_INFO(priv, "Initializing rate scaling for station %pM \n", + sta->addr); + iwl_rs_rate_init(priv, sta, sta_id); + + return ret; +} + /***************************************************************************** * * sysfs attributes @@ -3359,6 +3372,8 @@ static struct ieee80211_ops iwl_hw_ops = { .ampdu_action = iwl_mac_ampdu_action, .hw_scan = iwl_mac_hw_scan, .sta_notify = iwl_mac_sta_notify, + .sta_add = iwlagn_mac_sta_add, + .sta_remove = iwl_mac_sta_remove, }; static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index a33e4536657a..d4c2a3e17c53 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -2530,7 +2530,6 @@ void iwl_mac_reset_tsf(struct ieee80211_hw *hw) spin_lock_irqsave(&priv->lock, flags); priv->assoc_id = 0; priv->assoc_capability = 0; - priv->assoc_station_added = 0; /* new association get rid of ibss beacon skb */ if (priv->ibss_beacon) diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index aced12f1611e..2482a25d543f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h @@ -667,7 +667,7 @@ extern int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear); extern int iwl_verify_ucode(struct iwl_priv *priv); extern int iwl_send_lq_cmd(struct iwl_priv *priv, - struct iwl_link_quality_cmd *lq, u8 flags); + struct iwl_link_quality_cmd *lq, u8 flags, bool init); extern void iwl_rx_reply_rx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); extern void iwl_rx_reply_rx_phy(struct iwl_priv *priv, diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 2e4d47c7139b..9c676ea420b9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -544,11 +544,18 @@ struct iwl_qos_info { struct iwl_qosparam_cmd def_qos_parm; }; +/* + * Structure should be accessed with sta_lock held. When station addition + * is in progress (IWL_STA_UCODE_INPROGRESS) it is possible to access only + * the commands (iwl_addsta_cmd and iwl_link_quality_cmd) without sta_lock + * held. + */ struct iwl_station_entry { struct iwl_addsta_cmd sta; struct iwl_tid_data tid[MAX_TID_COUNT]; u8 used; struct iwl_hw_key keyinfo; + struct iwl_link_quality_cmd *lq; }; /* @@ -1163,7 +1170,6 @@ struct iwl_priv { u16 active_rate; - u8 assoc_station_added; u8 start_calib; struct iwl_sensitivity_data sensitivity_data; struct iwl_chain_noise_data chain_noise_data; diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c index 312099099ed3..d401b6f226f9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sta.c +++ b/drivers/net/wireless/iwlwifi/iwl-sta.c @@ -29,6 +29,7 @@ #include #include +#include #include "iwl-dev.h" #include "iwl-core.h" @@ -61,6 +62,19 @@ u8 iwl_find_station(struct iwl_priv *priv, const u8 *addr) addr, priv->num_stations); out: + /* + * It may be possible that more commands interacting with stations + * arrive before we completed processing the adding of + * station + */ + if (ret != IWL_INVALID_STATION && + (!(priv->stations[ret].used & IWL_STA_UCODE_ACTIVE) || + ((priv->stations[ret].used & IWL_STA_UCODE_ACTIVE) && + (priv->stations[ret].used & IWL_STA_UCODE_INPROGRESS)))) { + IWL_ERR(priv, "Requested station info for sta %d before ready. \n", + ret); + ret = IWL_INVALID_STATION; + } spin_unlock_irqrestore(&priv->sta_lock, flags); return ret; } @@ -155,13 +169,6 @@ static void iwl_process_add_sta_resp(struct iwl_priv *priv, priv->stations[sta_id].sta.mode == STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", addsta->sta.addr); - - /* - * Determine if we wanted to modify or add a station, - * if adding a station succeeded we have some more initialization - * to do when using station notification. TODO - */ - spin_unlock_irqrestore(&priv->sta_lock, flags); } @@ -187,6 +194,10 @@ int iwl_send_add_sta(struct iwl_priv *priv, .flags = flags, .data = data, }; + u8 sta_id = sta->sta.sta_id; + + IWL_DEBUG_INFO(priv, "Adding sta %u (%pM) %ssynchronously\n", + sta_id, sta->sta.addr, flags & CMD_ASYNC ? "a" : ""); if (flags & CMD_ASYNC) cmd.callback = iwl_add_sta_callback; @@ -260,18 +271,19 @@ static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index, } /** - * iwl_add_station - Add station to tables in driver and device + * iwl_prep_station - Prepare station information for addition + * + * should be called with sta_lock held */ -u8 iwl_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap, u8 flags, - struct ieee80211_sta_ht_cap *ht_info) +static u8 iwl_prep_station(struct iwl_priv *priv, const u8 *addr, + bool is_ap, + struct ieee80211_sta_ht_cap *ht_info) { struct iwl_station_entry *station; - unsigned long flags_spin; int i; - int sta_id = IWL_INVALID_STATION; + u8 sta_id = IWL_INVALID_STATION; u16 rate; - spin_lock_irqsave(&priv->sta_lock, flags_spin); if (is_ap) sta_id = IWL_AP_ID; else if (is_broadcast_ether_addr(addr)) @@ -289,20 +301,32 @@ u8 iwl_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap, u8 flags, sta_id = i; } - /* These two conditions have the same outcome, but keep them separate - since they have different meanings */ - if (unlikely(sta_id == IWL_INVALID_STATION)) { - spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + /* + * These two conditions have the same outcome, but keep them + * separate + */ + if (unlikely(sta_id == IWL_INVALID_STATION)) + return sta_id; + + /* + * uCode is not able to deal with multiple requests to add a + * station. Keep track if one is in progress so that we do not send + * another. + */ + if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) { + IWL_DEBUG_INFO(priv, "STA %d already in process of being added.\n", + sta_id); return sta_id; } - if (priv->stations[sta_id].used && + if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) && + (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) && !compare_ether_addr(priv->stations[sta_id].sta.sta.addr, addr)) { - spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not adding again.\n", + sta_id, addr); return sta_id; } - station = &priv->stations[sta_id]; station->used = IWL_STA_DRIVER_ACTIVE; IWL_DEBUG_ASSOC(priv, "Add STA to driver ID %d: %pM\n", @@ -327,86 +351,185 @@ u8 iwl_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap, u8 flags, /* Turn on both antennas for the station... */ station->sta.rate_n_flags = cpu_to_le16(rate | RATE_MCS_ANT_AB_MSK); - spin_unlock_irqrestore(&priv->sta_lock, flags_spin); - - /* Add station to device's station table */ - iwl_send_add_sta(priv, &station->sta, flags); return sta_id; } -EXPORT_SYMBOL(iwl_add_station); -static void iwl_sta_ucode_deactivate(struct iwl_priv *priv, const u8 *addr) +#define STA_WAIT_TIMEOUT (HZ/2) + +/** + * iwl_add_station_common - + */ +int iwl_add_station_common(struct iwl_priv *priv, const u8 *addr, + bool is_ap, + struct ieee80211_sta_ht_cap *ht_info, + u8 *sta_id_r) { - unsigned long flags; - u8 sta_id = iwl_find_station(priv, addr); + struct iwl_station_entry *station; + unsigned long flags_spin; + int ret = 0; + u8 sta_id; - BUG_ON(sta_id == IWL_INVALID_STATION); + *sta_id_r = 0; + spin_lock_irqsave(&priv->sta_lock, flags_spin); + sta_id = iwl_prep_station(priv, addr, is_ap, ht_info); + if (sta_id == IWL_INVALID_STATION) { + IWL_ERR(priv, "Unable to prepare station %pM for addition\n", + addr); + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + return -EINVAL; + } - IWL_DEBUG_ASSOC(priv, "Removed STA from Ucode: %pM\n", addr); + /* + * uCode is not able to deal with multiple requests to add a + * station. Keep track if one is in progress so that we do not send + * another. + */ + if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) { + IWL_DEBUG_INFO(priv, "STA %d already in process of being added.\n", + sta_id); + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + return -EEXIST; + } - spin_lock_irqsave(&priv->sta_lock, flags); + if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) && + (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { + IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not adding again.\n", + sta_id, addr); + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + return -EEXIST; + } + priv->stations[sta_id].used |= IWL_STA_UCODE_INPROGRESS; + station = &priv->stations[sta_id]; + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + + /* Add station to device's station table */ + ret = iwl_send_add_sta(priv, &station->sta, CMD_SYNC); + if (ret) { + IWL_ERR(priv, "Adding station %pM failed.\n", station->sta.sta.addr); + spin_lock_irqsave(&priv->sta_lock, flags_spin); + priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; + priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + } + *sta_id_r = sta_id; + return ret; +} +EXPORT_SYMBOL(iwl_add_station_common); + +static void iwl_sta_init_lq(struct iwl_priv *priv, const u8 *addr, bool is_ap) +{ + int i, r; + struct iwl_link_quality_cmd link_cmd = { + .reserved1 = 0, + }; + u32 rate_flags; + + /* Set up the rate scaling to start at selected rate, fall back + * all the way down to 1M in IEEE order, and then spin on 1M */ + if (is_ap) + r = IWL_RATE_54M_INDEX; + else if (priv->band == IEEE80211_BAND_5GHZ) + r = IWL_RATE_6M_INDEX; + else + r = IWL_RATE_1M_INDEX; + + for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { + rate_flags = 0; + if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE) + rate_flags |= RATE_MCS_CCK_MSK; + + rate_flags |= first_antenna(priv->hw_params.valid_tx_ant) << + RATE_MCS_ANT_POS; + + link_cmd.rs_table[i].rate_n_flags = + iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags); + r = iwl_get_prev_ieee_rate(r); + } + + link_cmd.general_params.single_stream_ant_msk = + first_antenna(priv->hw_params.valid_tx_ant); + link_cmd.general_params.dual_stream_ant_msk = 3; + link_cmd.agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; + link_cmd.agg_params.agg_time_limit = + cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); + + /* Update the rate scaling for control frame Tx to AP */ + link_cmd.sta_id = is_ap ? IWL_AP_ID : priv->hw_params.bcast_sta_id; + + iwl_send_cmd_pdu(priv, REPLY_TX_LINK_QUALITY_CMD, + sizeof(link_cmd), &link_cmd); +} + +/* + * iwl_add_local_stations - Add stations not requested by mac80211 + * + * This will be either the broadcast station or the bssid station needed by + * ad-hoc. + * + * Function sleeps. + */ +int iwl_add_local_station(struct iwl_priv *priv, const u8 *addr, bool init_rs) +{ + int ret; + u8 sta_id; + + ret = iwl_add_station_common(priv, addr, 0, NULL, &sta_id); + if (ret) { + IWL_ERR(priv, "Unable to add station %pM\n", addr); + return ret; + } + + if (init_rs) + /* Set up default rate scaling table in device's station table */ + iwl_sta_init_lq(priv, addr, false); + return 0; +} +EXPORT_SYMBOL(iwl_add_local_station); + +/** + * iwl_sta_ucode_deactivate - deactivate ucode status for a station + * + * priv->sta_lock must be held + */ +static void iwl_sta_ucode_deactivate(struct iwl_priv *priv, u8 sta_id) +{ /* Ucode must be active and driver must be non active */ if (priv->stations[sta_id].used != IWL_STA_UCODE_ACTIVE) - IWL_ERR(priv, "removed non active STA %d\n", sta_id); + IWL_ERR(priv, "removed non active STA %u\n", sta_id); priv->stations[sta_id].used &= ~IWL_STA_UCODE_ACTIVE; memset(&priv->stations[sta_id], 0, sizeof(struct iwl_station_entry)); - spin_unlock_irqrestore(&priv->sta_lock, flags); + IWL_DEBUG_ASSOC(priv, "Removed STA %u\n", sta_id); } -static void iwl_remove_sta_callback(struct iwl_priv *priv, - struct iwl_device_cmd *cmd, - struct iwl_rx_packet *pkt) -{ - struct iwl_rem_sta_cmd *rm_sta = - (struct iwl_rem_sta_cmd *)cmd->cmd.payload; - const u8 *addr = rm_sta->addr; - - if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { - IWL_ERR(priv, "Bad return from REPLY_REMOVE_STA (0x%08X)\n", - pkt->hdr.flags); - return; - } - - switch (pkt->u.rem_sta.status) { - case REM_STA_SUCCESS_MSK: - iwl_sta_ucode_deactivate(priv, addr); - break; - default: - IWL_ERR(priv, "REPLY_REMOVE_STA failed\n"); - break; - } -} - -static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr, - u8 flags) +static int iwl_send_remove_station(struct iwl_priv *priv, + struct iwl_station_entry *station) { struct iwl_rx_packet *pkt; int ret; + unsigned long flags_spin; struct iwl_rem_sta_cmd rm_sta_cmd; struct iwl_host_cmd cmd = { .id = REPLY_REMOVE_STA, .len = sizeof(struct iwl_rem_sta_cmd), - .flags = flags, + .flags = CMD_SYNC, .data = &rm_sta_cmd, }; memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd)); rm_sta_cmd.num_sta = 1; - memcpy(&rm_sta_cmd.addr, addr , ETH_ALEN); + memcpy(&rm_sta_cmd.addr, &station->sta.sta.addr , ETH_ALEN); + + cmd.flags |= CMD_WANT_SKB; - if (flags & CMD_ASYNC) - cmd.callback = iwl_remove_sta_callback; - else - cmd.flags |= CMD_WANT_SKB; ret = iwl_send_cmd(priv, &cmd); - if (ret || (flags & CMD_ASYNC)) + if (ret) return ret; pkt = (struct iwl_rx_packet *)cmd.reply_page; @@ -419,7 +542,9 @@ static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr, if (!ret) { switch (pkt->u.rem_sta.status) { case REM_STA_SUCCESS_MSK: - iwl_sta_ucode_deactivate(priv, addr); + spin_lock_irqsave(&priv->sta_lock, flags_spin); + iwl_sta_ucode_deactivate(priv, station->sta.sta.sta_id); + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n"); break; default: @@ -436,23 +561,35 @@ static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr, /** * iwl_remove_station - Remove driver's knowledge of station. */ -int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, bool is_ap) +static int iwl_remove_station(struct iwl_priv *priv, struct ieee80211_sta *sta) { int sta_id = IWL_INVALID_STATION; int i, ret = -EINVAL; unsigned long flags; + bool is_ap = priv->iw_mode == NL80211_IFTYPE_STATION; + struct iwl_station_entry *station; + + if (!iwl_is_ready(priv)) { + IWL_DEBUG_INFO(priv, + "Unable to remove station %pM, device not ready. \n", + sta->addr); + /* + * It is typical for stations to be removed when we are + * going down. Return success since device will be down + * soon anyway + */ + return 0; + } spin_lock_irqsave(&priv->sta_lock, flags); if (is_ap) sta_id = IWL_AP_ID; - else if (is_broadcast_ether_addr(addr)) - sta_id = priv->hw_params.bcast_sta_id; else for (i = IWL_STA_ID; i < priv->hw_params.max_stations; i++) if (priv->stations[i].used && !compare_ether_addr(priv->stations[i].sta.sta.addr, - addr)) { + sta->addr)) { sta_id = i; break; } @@ -461,17 +598,17 @@ int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, bool is_ap) goto out; IWL_DEBUG_ASSOC(priv, "Removing STA from driver:%d %pM\n", - sta_id, addr); + sta_id, sta->addr); if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) { - IWL_ERR(priv, "Removing %pM but non DRIVER active\n", - addr); + IWL_DEBUG_INFO(priv, "Removing %pM but non DRIVER active\n", + sta->addr); goto out; } if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { - IWL_ERR(priv, "Removing %pM but non UCODE active\n", - addr); + IWL_DEBUG_INFO(priv, "Removing %pM but non UCODE active\n", + sta->addr); goto out; } @@ -482,9 +619,10 @@ int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, bool is_ap) BUG_ON(priv->num_stations < 0); + station = &priv->stations[sta_id]; spin_unlock_irqrestore(&priv->sta_lock, flags); - ret = iwl_send_remove_station(priv, addr, CMD_ASYNC); + ret = iwl_send_remove_station(priv, station); return ret; out: spin_unlock_irqrestore(&priv->sta_lock, flags); @@ -548,12 +686,16 @@ EXPORT_SYMBOL(iwl_clear_ucode_stations); * * All stations considered active by driver, but not present in ucode, is * restored. + * + * Function sleeps. */ void iwl_restore_stations(struct iwl_priv *priv) { + struct iwl_station_entry *station; unsigned long flags_spin; int i; bool found = false; + int ret; if (!iwl_is_ready(priv)) { IWL_DEBUG_INFO(priv, "Not ready yet, not restoring any stations.\n"); @@ -575,8 +717,24 @@ void iwl_restore_stations(struct iwl_priv *priv) for (i = 0; i < priv->hw_params.max_stations; i++) { if ((priv->stations[i].used & IWL_STA_UCODE_INPROGRESS)) { - iwl_send_add_sta(priv, &priv->stations[i].sta, - CMD_ASYNC); + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + station = &priv->stations[i]; + ret = iwl_send_add_sta(priv, &priv->stations[i].sta, CMD_SYNC); + if (ret) { + IWL_ERR(priv, "Adding station %pM failed.\n", + station->sta.sta.addr); + spin_lock_irqsave(&priv->sta_lock, flags_spin); + priv->stations[i].used &= ~IWL_STA_DRIVER_ACTIVE; + priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS; + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + } + /* + * Rate scaling has already been initialized, send + * current LQ command + */ + if (station->lq) + iwl_send_lq_cmd(priv, station->lq, CMD_SYNC, true); + spin_lock_irqsave(&priv->sta_lock, flags_spin); priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS; } } @@ -585,7 +743,7 @@ void iwl_restore_stations(struct iwl_priv *priv) if (!found) IWL_DEBUG_INFO(priv, "Restoring all known stations .... no stations to be restored.\n"); else - IWL_DEBUG_INFO(priv, "Restoring all known stations .... in progress.\n"); + IWL_DEBUG_INFO(priv, "Restoring all known stations .... complete.\n"); } EXPORT_SYMBOL(iwl_restore_stations); @@ -1010,9 +1168,22 @@ static inline void iwl_dump_lq_cmd(struct iwl_priv *priv, } #endif +/** + * iwl_send_lq_cmd() - Send link quality command + * @init: This command is sent as part of station initialization right + * after station has been added. + * + * The link quality command is sent as the last step of station creation. + * This is the special case in which init is set and we call a callback in + * this case to clear the state indicating that station creation is in + * progress. + */ int iwl_send_lq_cmd(struct iwl_priv *priv, - struct iwl_link_quality_cmd *lq, u8 flags) + struct iwl_link_quality_cmd *lq, u8 flags, bool init) { + int ret = 0; + unsigned long flags_spin; + struct iwl_host_cmd cmd = { .id = REPLY_TX_LINK_QUALITY_CMD, .len = sizeof(struct iwl_link_quality_cmd), @@ -1028,167 +1199,31 @@ int iwl_send_lq_cmd(struct iwl_priv *priv, lq->sta_id = IWL_AP_ID; iwl_dump_lq_cmd(priv, lq); + BUG_ON(init && (cmd.flags & CMD_ASYNC)); - if (iwl_is_associated(priv) && priv->assoc_station_added) - return iwl_send_cmd(priv, &cmd); + iwl_dump_lq_cmd(priv, lq); + ret = iwl_send_cmd(priv, &cmd); + if (ret || (cmd.flags & CMD_ASYNC)) + return ret; + if (init) { + IWL_DEBUG_INFO(priv, "init LQ command complete, clearing sta addition status for sta %d \n", + lq->sta_id); + spin_lock_irqsave(&priv->sta_lock, flags_spin); + priv->stations[lq->sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + } return 0; } EXPORT_SYMBOL(iwl_send_lq_cmd); -/** - * iwl_sta_init_lq - Initialize a station's hardware rate table - * - * The uCode's station table contains a table of fallback rates - * for automatic fallback during transmission. - * - * NOTE: This sets up a default set of values. These will be replaced later - * if the driver's iwl-agn-rs rate scaling algorithm is used, instead of - * rc80211_simple. - * - * NOTE: Run REPLY_ADD_STA command to set up station table entry, before - * calling this function (which runs REPLY_TX_LINK_QUALITY_CMD, - * which requires station table entry to exist). - */ -static void iwl_sta_init_lq(struct iwl_priv *priv, const u8 *addr, bool is_ap) -{ - int i, r; - struct iwl_link_quality_cmd link_cmd = { - .reserved1 = 0, - }; - u32 rate_flags; - - /* Set up the rate scaling to start at selected rate, fall back - * all the way down to 1M in IEEE order, and then spin on 1M */ - if (is_ap) - r = IWL_RATE_54M_INDEX; - else if (priv->band == IEEE80211_BAND_5GHZ) - r = IWL_RATE_6M_INDEX; - else - r = IWL_RATE_1M_INDEX; - - for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { - rate_flags = 0; - if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE) - rate_flags |= RATE_MCS_CCK_MSK; - - rate_flags |= first_antenna(priv->hw_params.valid_tx_ant) << - RATE_MCS_ANT_POS; - - link_cmd.rs_table[i].rate_n_flags = - iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags); - r = iwl_get_prev_ieee_rate(r); - } - - link_cmd.general_params.single_stream_ant_msk = - first_antenna(priv->hw_params.valid_tx_ant); - link_cmd.general_params.dual_stream_ant_msk = 3; - link_cmd.agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; - link_cmd.agg_params.agg_time_limit = - cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); - - /* Update the rate scaling for control frame Tx to AP */ - link_cmd.sta_id = is_ap ? IWL_AP_ID : priv->hw_params.bcast_sta_id; - - iwl_send_cmd_pdu_async(priv, REPLY_TX_LINK_QUALITY_CMD, - sizeof(link_cmd), &link_cmd, NULL); -} - -/** - * iwl_rxon_add_station - add station into station table. - * - * there is only one AP station with id= IWL_AP_ID - * NOTE: mutex must be held before calling this function - */ -int iwl_rxon_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap) -{ - struct ieee80211_sta *sta; - struct ieee80211_sta_ht_cap ht_config; - struct ieee80211_sta_ht_cap *cur_ht_config = NULL; - u8 sta_id; - - /* - * Set HT capabilities. It is ok to set this struct even if not using - * HT config: the priv->current_ht_config.is_ht flag will just be false - */ - rcu_read_lock(); - sta = ieee80211_find_sta(priv->vif, addr); - if (sta) { - memcpy(&ht_config, &sta->ht_cap, sizeof(ht_config)); - cur_ht_config = &ht_config; - } - rcu_read_unlock(); - - /* Add station to device's station table */ - sta_id = iwl_add_station(priv, addr, is_ap, CMD_SYNC, cur_ht_config); - - /* Set up default rate scaling table in device's station table */ - iwl_sta_init_lq(priv, addr, is_ap); - - return sta_id; -} -EXPORT_SYMBOL(iwl_rxon_add_station); - -/** - * iwl_sta_init_bcast_lq - Initialize a bcast station's hardware rate table - * - * NOTE: Run REPLY_ADD_STA command to set up station table entry, before - * calling this function (which runs REPLY_TX_LINK_QUALITY_CMD, - * which requires station table entry to exist). - */ -static void iwl_sta_init_bcast_lq(struct iwl_priv *priv) -{ - int i, r; - struct iwl_link_quality_cmd link_cmd = { - .reserved1 = 0, - }; - u32 rate_flags; - - /* Set up the rate scaling to start at selected rate, fall back - * all the way down to 1M in IEEE order, and then spin on 1M */ - if (priv->band == IEEE80211_BAND_5GHZ) - r = IWL_RATE_6M_INDEX; - else - r = IWL_RATE_1M_INDEX; - - for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { - rate_flags = 0; - if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE) - rate_flags |= RATE_MCS_CCK_MSK; - - rate_flags |= first_antenna(priv->hw_params.valid_tx_ant) << - RATE_MCS_ANT_POS; - - link_cmd.rs_table[i].rate_n_flags = - iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags); - r = iwl_get_prev_ieee_rate(r); - } - - link_cmd.general_params.single_stream_ant_msk = - first_antenna(priv->hw_params.valid_tx_ant); - link_cmd.general_params.dual_stream_ant_msk = 3; - link_cmd.agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; - link_cmd.agg_params.agg_time_limit = - cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); - - /* Update the rate scaling for control frame Tx to AP */ - link_cmd.sta_id = priv->hw_params.bcast_sta_id; - - iwl_send_cmd_pdu_async(priv, REPLY_TX_LINK_QUALITY_CMD, - sizeof(link_cmd), &link_cmd, NULL); -} - - /** * iwl_add_bcast_station - add broadcast station into station table. */ void iwl_add_bcast_station(struct iwl_priv *priv) { IWL_DEBUG_INFO(priv, "Adding broadcast station to station table\n"); - iwl_add_station(priv, iwl_bcast_addr, false, CMD_SYNC, NULL); - - /* Set up default rate scaling table in device's station table */ - iwl_sta_init_bcast_lq(priv); + iwl_add_local_station(priv, iwl_bcast_addr, true); } EXPORT_SYMBOL(iwl_add_bcast_station); @@ -1198,7 +1233,14 @@ EXPORT_SYMBOL(iwl_add_bcast_station); void iwl3945_add_bcast_station(struct iwl_priv *priv) { IWL_DEBUG_INFO(priv, "Adding broadcast station to station table\n"); - iwl_add_station(priv, iwl_bcast_addr, false, CMD_SYNC, NULL); + iwl_add_local_station(priv, iwl_bcast_addr, false); + /* + * It is assumed that when station is added more initialization + * needs to be done, but for 3945 it is not the case and we can + * just release station table access right here. + */ + priv->stations[priv->hw_params.bcast_sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; + } EXPORT_SYMBOL(iwl3945_add_bcast_station); @@ -1221,6 +1263,13 @@ int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr) /* If we are a client station in a BSS network, use the special * AP station entry (that's the only station we communicate with) */ case NL80211_IFTYPE_STATION: + /* + * If addition of station not complete yet, which means + * that rate scaling has not been initialized, then return + * the broadcast station. + */ + if (!(priv->stations[IWL_AP_ID].used & IWL_STA_UCODE_ACTIVE)) + return priv->hw_params.bcast_sta_id; return IWL_AP_ID; /* If we are an AP, then find the station, or use BCAST */ @@ -1237,13 +1286,6 @@ int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr) if (sta_id != IWL_INVALID_STATION) return sta_id; - /* Create new station table entry */ - sta_id = iwl_add_station(priv, hdr->addr1, false, - CMD_ASYNC, NULL); - - if (sta_id != IWL_INVALID_STATION) - return sta_id; - IWL_DEBUG_DROP(priv, "Station %pM not in station map. " "Defaulting to broadcast...\n", hdr->addr1); @@ -1353,3 +1395,19 @@ void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt) iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC); } + +int iwl_mac_sta_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + int ret; + struct iwl_priv *priv = hw->priv; + IWL_DEBUG_INFO(priv, "received request to remove station %pM\n", + sta->addr); + ret = iwl_remove_station(priv, sta); + if (ret) + IWL_ERR(priv, "Error removing station %pM\n", + sta->addr); + return ret; +} +EXPORT_SYMBOL(iwl_mac_sta_remove); diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.h b/drivers/net/wireless/iwlwifi/iwl-sta.h index 50367e83defd..87a34997a758 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sta.h +++ b/drivers/net/wireless/iwlwifi/iwl-sta.h @@ -57,10 +57,8 @@ void iwl_update_tkip_key(struct iwl_priv *priv, struct ieee80211_key_conf *keyconf, const u8 *addr, u32 iv32, u16 *phase1key); -int iwl_rxon_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap); void iwl_add_bcast_station(struct iwl_priv *priv); void iwl3945_add_bcast_station(struct iwl_priv *priv); -int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, bool is_ap); void iwl_restore_stations(struct iwl_priv *priv); void iwl_clear_ucode_stations(struct iwl_priv *priv, bool force); int iwl_get_free_ucode_key_index(struct iwl_priv *priv); @@ -68,8 +66,13 @@ int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr); int iwl_get_ra_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr); int iwl_send_add_sta(struct iwl_priv *priv, struct iwl_addsta_cmd *sta, u8 flags); -u8 iwl_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap, u8 flags, - struct ieee80211_sta_ht_cap *ht_info); +int iwl_add_local_station(struct iwl_priv *priv, const u8 *addr, bool init_rs); +int iwl_add_station_common(struct iwl_priv *priv, const u8 *addr, + bool is_ap, + struct ieee80211_sta_ht_cap *ht_info, + u8 *sta_id_r); +int iwl_mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); void iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid); int iwl_sta_rx_agg_start(struct iwl_priv *priv, const u8 *addr, int tid, u16 ssn); diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 6d3e6a85f932..6687b9456554 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -3116,12 +3116,13 @@ void iwl3945_post_associate(struct iwl_priv *priv) case NL80211_IFTYPE_ADHOC: priv->assoc_id = 1; - iwl_add_station(priv, priv->bssid, 0, CMD_SYNC, NULL); + iwl_add_local_station(priv, priv->bssid, false); iwl3945_sync_sta(priv, IWL_STA_ID, - (priv->band == IEEE80211_BAND_5GHZ) ? - IWL_RATE_6M_PLCP : IWL_RATE_1M_PLCP, + (priv->band == IEEE80211_BAND_5GHZ) ? + IWL_RATE_6M_PLCP : IWL_RATE_1M_PLCP, CMD_ASYNC); iwl3945_rate_scale_init(priv->hw, IWL_STA_ID); + iwl3945_send_beacon_cmd(priv); break; @@ -3306,7 +3307,7 @@ void iwl3945_config_ap(struct iwl_priv *priv) /* restore RXON assoc */ priv->staging_rxon.filter_flags |= RXON_FILTER_ASSOC_MSK; iwlcore_commit_rxon(priv); - iwl_add_station(priv, iwl_bcast_addr, 0, CMD_SYNC, NULL); + iwl_add_local_station(priv, iwl_bcast_addr, false); } iwl3945_send_beacon_cmd(priv); @@ -3373,6 +3374,38 @@ static int iwl3945_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, return ret; } +static int iwl3945_mac_sta_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_priv *priv = hw->priv; + int ret; + bool is_ap = priv->iw_mode == NL80211_IFTYPE_STATION; + u8 sta_id; + + IWL_DEBUG_INFO(priv, "received request to add station %pM\n", + sta->addr); + + ret = iwl_add_station_common(priv, sta->addr, is_ap, &sta->ht_cap, + &sta_id); + if (ret) { + IWL_ERR(priv, "Unable to add station %pM (%d)\n", + sta->addr, ret); + /* Should we return success if return code is EEXIST ? */ + return ret; + } + + /* Initialize rate scaling */ + IWL_DEBUG_INFO(priv, "Initializing rate scaling for station %pM \n", + sta->addr); + iwl3945_rs_rate_init(priv, sta, sta_id); + + return 0; + + + + return ret; +} /***************************************************************************** * * sysfs attributes @@ -3812,7 +3845,9 @@ static struct ieee80211_ops iwl3945_hw_ops = { .conf_tx = iwl_mac_conf_tx, .reset_tsf = iwl_mac_reset_tsf, .bss_info_changed = iwl_bss_info_changed, - .hw_scan = iwl_mac_hw_scan + .hw_scan = iwl_mac_hw_scan, + .sta_add = iwl3945_mac_sta_add, + .sta_remove = iwl_mac_sta_remove, }; static int iwl3945_init_drv(struct iwl_priv *priv) From fe0b3b96ab28b7e5fb6c5aea68e6dd88115873da Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 21 Jan 2010 07:07:44 -0800 Subject: [PATCH 04/60] iwlwifi: sta_id cannot be invalid in rs_initialize_lq The only caller of this is iwl_rs_rate_init which is only called with a valid sta_id. Signed-off-by: Johannes Berg Signed-off-by: Reinette Chatre --- drivers/net/wireless/iwlwifi/iwl-agn-rs.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c index 970eec82d106..5155b1a027eb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c @@ -2377,10 +2377,6 @@ static void rs_initialize_lq(struct iwl_priv *priv, i = lq_sta->last_txrate_idx; - if ((lq_sta->lq.sta_id == 0xff) && - (priv->iw_mode == NL80211_IFTYPE_ADHOC)) - goto out; - valid_tx_ant = priv->hw_params.valid_tx_ant; if (!lq_sta->search_better_tbl) From c11362c01b280f8b2c728bc64793d484282b8734 Mon Sep 17 00:00:00 2001 From: Shanyu Zhao Date: Fri, 5 Mar 2010 17:05:20 -0800 Subject: [PATCH 05/60] iwlwifi: clean up driver names for 1000/5000/6000 Align the driver names with official product names for 1000/5000/6000 series. This change mainly affects the debug messages show up in system log. No functional changes. Signed-off-by: Shanyu Zhao Signed-off-by: Reinette Chatre --- drivers/net/wireless/iwlwifi/iwl-1000.c | 4 ++-- drivers/net/wireless/iwlwifi/iwl-4965.c | 2 +- drivers/net/wireless/iwlwifi/iwl-5000.c | 14 +++++++------- drivers/net/wireless/iwlwifi/iwl-6000.c | 12 ++++++------ drivers/net/wireless/iwlwifi/iwl-agn.c | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c index 59b092eaa829..e31e8a3459b5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-1000.c +++ b/drivers/net/wireless/iwlwifi/iwl-1000.c @@ -223,7 +223,7 @@ static const struct iwl_ops iwl1000_ops = { }; struct iwl_cfg iwl1000_bgn_cfg = { - .name = "1000 Series BGN", + .name = "Intel(R) Centrino(R) Wireless-N 1000 BGN", .fw_name_pre = IWL1000_FW_PRE, .ucode_api_max = IWL1000_UCODE_API_MAX, .ucode_api_min = IWL1000_UCODE_API_MIN, @@ -252,7 +252,7 @@ struct iwl_cfg iwl1000_bgn_cfg = { }; struct iwl_cfg iwl1000_bg_cfg = { - .name = "1000 Series BG", + .name = "Intel(R) Centrino(R) Wireless-N 1000 BG", .fw_name_pre = IWL1000_FW_PRE, .ucode_api_max = IWL1000_UCODE_API_MAX, .ucode_api_min = IWL1000_UCODE_API_MIN, diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index 271576ab5747..7f9e448fe5e1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -2232,7 +2232,7 @@ static const struct iwl_ops iwl4965_ops = { }; struct iwl_cfg iwl4965_agn_cfg = { - .name = "4965AGN", + .name = "Intel(R) Wireless WiFi Link 4965AGN", .fw_name_pre = IWL4965_FW_PRE, .ucode_api_max = IWL4965_UCODE_API_MAX, .ucode_api_min = IWL4965_UCODE_API_MIN, diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 046bdd96e5a0..8ab28a760157 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -1579,7 +1579,7 @@ struct iwl_mod_params iwl50_mod_params = { struct iwl_cfg iwl5300_agn_cfg = { - .name = "5300AGN", + .name = "Intel(R) Ultimate N WiFi Link 5300 AGN", .fw_name_pre = IWL5000_FW_PRE, .ucode_api_max = IWL5000_UCODE_API_MAX, .ucode_api_min = IWL5000_UCODE_API_MIN, @@ -1605,7 +1605,7 @@ struct iwl_cfg iwl5300_agn_cfg = { }; struct iwl_cfg iwl5100_bgn_cfg = { - .name = "5100BGN", + .name = "Intel(R) WiFi Link 5100 BGN", .fw_name_pre = IWL5000_FW_PRE, .ucode_api_max = IWL5000_UCODE_API_MAX, .ucode_api_min = IWL5000_UCODE_API_MIN, @@ -1631,7 +1631,7 @@ struct iwl_cfg iwl5100_bgn_cfg = { }; struct iwl_cfg iwl5100_abg_cfg = { - .name = "5100ABG", + .name = "Intel(R) WiFi Link 5100 ABG", .fw_name_pre = IWL5000_FW_PRE, .ucode_api_max = IWL5000_UCODE_API_MAX, .ucode_api_min = IWL5000_UCODE_API_MIN, @@ -1655,7 +1655,7 @@ struct iwl_cfg iwl5100_abg_cfg = { }; struct iwl_cfg iwl5100_agn_cfg = { - .name = "5100AGN", + .name = "Intel(R) WiFi Link 5100 AGN", .fw_name_pre = IWL5000_FW_PRE, .ucode_api_max = IWL5000_UCODE_API_MAX, .ucode_api_min = IWL5000_UCODE_API_MIN, @@ -1681,7 +1681,7 @@ struct iwl_cfg iwl5100_agn_cfg = { }; struct iwl_cfg iwl5350_agn_cfg = { - .name = "5350AGN", + .name = "Intel(R) WiMAX/WiFi Link 5350 AGN", .fw_name_pre = IWL5000_FW_PRE, .ucode_api_max = IWL5000_UCODE_API_MAX, .ucode_api_min = IWL5000_UCODE_API_MIN, @@ -1707,7 +1707,7 @@ struct iwl_cfg iwl5350_agn_cfg = { }; struct iwl_cfg iwl5150_agn_cfg = { - .name = "5150AGN", + .name = "Intel(R) WiMAX/WiFi Link 5150 AGN", .fw_name_pre = IWL5150_FW_PRE, .ucode_api_max = IWL5150_UCODE_API_MAX, .ucode_api_min = IWL5150_UCODE_API_MIN, @@ -1733,7 +1733,7 @@ struct iwl_cfg iwl5150_agn_cfg = { }; struct iwl_cfg iwl5150_abg_cfg = { - .name = "5150ABG", + .name = "Intel(R) WiMAX/WiFi Link 5150 ABG", .fw_name_pre = IWL5150_FW_PRE, .ucode_api_max = IWL5150_UCODE_API_MAX, .ucode_api_min = IWL5150_UCODE_API_MIN, diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c index 4b7bc008220f..fb7012289c3d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ b/drivers/net/wireless/iwlwifi/iwl-6000.c @@ -357,7 +357,7 @@ static const struct iwl_ops iwl6050_ops = { * "i": Internal configuration, use internal Power Amplifier */ struct iwl_cfg iwl6000i_2agn_cfg = { - .name = "6000 Series 2x2 AGN", + .name = "Intel(R) Centrino(R) Advanced-N 6200 AGN", .fw_name_pre = IWL6000_FW_PRE, .ucode_api_max = IWL6000_UCODE_API_MAX, .ucode_api_min = IWL6000_UCODE_API_MIN, @@ -389,7 +389,7 @@ struct iwl_cfg iwl6000i_2agn_cfg = { }; struct iwl_cfg iwl6000i_2abg_cfg = { - .name = "6000 Series 2x2 ABG", + .name = "Intel(R) Centrino(R) Advanced-N 6200 ABG", .fw_name_pre = IWL6000_FW_PRE, .ucode_api_max = IWL6000_UCODE_API_MAX, .ucode_api_min = IWL6000_UCODE_API_MIN, @@ -420,7 +420,7 @@ struct iwl_cfg iwl6000i_2abg_cfg = { }; struct iwl_cfg iwl6000i_2bg_cfg = { - .name = "6000 Series 2x2 BG", + .name = "Intel(R) Centrino(R) Advanced-N 6200 BG", .fw_name_pre = IWL6000_FW_PRE, .ucode_api_max = IWL6000_UCODE_API_MAX, .ucode_api_min = IWL6000_UCODE_API_MIN, @@ -451,7 +451,7 @@ struct iwl_cfg iwl6000i_2bg_cfg = { }; struct iwl_cfg iwl6050_2agn_cfg = { - .name = "6050 Series 2x2 AGN", + .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 AGN", .fw_name_pre = IWL6050_FW_PRE, .ucode_api_max = IWL6050_UCODE_API_MAX, .ucode_api_min = IWL6050_UCODE_API_MIN, @@ -483,7 +483,7 @@ struct iwl_cfg iwl6050_2agn_cfg = { }; struct iwl_cfg iwl6050_2abg_cfg = { - .name = "6050 Series 2x2 ABG", + .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 ABG", .fw_name_pre = IWL6050_FW_PRE, .ucode_api_max = IWL6050_UCODE_API_MAX, .ucode_api_min = IWL6050_UCODE_API_MIN, @@ -514,7 +514,7 @@ struct iwl_cfg iwl6050_2abg_cfg = { }; struct iwl_cfg iwl6000_3agn_cfg = { - .name = "6000 Series 3x3 AGN", + .name = "Intel(R) Centrino(R) Ultimate-N 6300 AGN", .fw_name_pre = IWL6000_FW_PRE, .ucode_api_max = IWL6000_UCODE_API_MAX, .ucode_api_min = IWL6000_UCODE_API_MIN, diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 4b4b47676ad8..93931b61304b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -3477,7 +3477,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) iwl_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); iwl_hw_detect(priv); - IWL_INFO(priv, "Detected Intel Wireless WiFi Link %s REV=0x%X\n", + IWL_INFO(priv, "Detected %s, REV=0x%X\n", priv->cfg->name, priv->hw_rev); /* We disable the RETRY_TIMEOUT register (0x41) to keep From b74e31a9bc1013e69b85b139072485dc153453dd Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Mon, 1 Mar 2010 17:23:50 -0800 Subject: [PATCH 06/60] iwlwifi: Recover TX flow stall due to stuck queue Monitors the internal TX queues periodically. When a queue is stuck for some unknown conditions causing the throughput to drop and the transfer is stop, the driver will force firmware reload and bring the system back to normal operational state. The iwlwifi devices behave differently in this regard so this feature is made part of the ops infrastructure so we can have more control on how to monitor and recover from tx queue stall case per device. Signed-off-by: Trieu 'Andrew' Nguyen Signed-off-by: Wey-Yi Guy Signed-off-by: Reinette Chatre --- drivers/net/wireless/iwlwifi/iwl-1000.c | 3 + drivers/net/wireless/iwlwifi/iwl-3945.c | 2 + drivers/net/wireless/iwlwifi/iwl-4965.c | 1 + drivers/net/wireless/iwlwifi/iwl-5000.c | 9 ++ drivers/net/wireless/iwlwifi/iwl-6000.c | 8 ++ drivers/net/wireless/iwlwifi/iwl-agn.c | 16 ++++ drivers/net/wireless/iwlwifi/iwl-core.c | 93 +++++++++++++++++++++ drivers/net/wireless/iwlwifi/iwl-core.h | 7 ++ drivers/net/wireless/iwlwifi/iwl-dev.h | 10 +++ drivers/net/wireless/iwlwifi/iwl-tx.c | 2 + drivers/net/wireless/iwlwifi/iwl3945-base.c | 16 ++++ 11 files changed, 167 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c index e31e8a3459b5..5a9cb70160f6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-1000.c +++ b/drivers/net/wireless/iwlwifi/iwl-1000.c @@ -212,6 +212,7 @@ static struct iwl_lib_ops iwl1000_lib = { .set_ct_kill = iwl1000_set_ct_threshold, }, .add_bcast_station = iwl_add_bcast_station, + .recover_from_tx_stall = iwl_bg_monitor_recover, }; static const struct iwl_ops iwl1000_ops = { @@ -249,6 +250,7 @@ struct iwl_cfg iwl1000_bgn_cfg = { .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; struct iwl_cfg iwl1000_bg_cfg = { @@ -277,6 +279,7 @@ struct iwl_cfg iwl1000_bg_cfg = { .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index 11785e2c767a..21ae61dd3d51 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -2820,6 +2820,7 @@ static struct iwl_cfg iwl3945_bg_cfg = { .led_compensation = 64, .broken_powersave = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; static struct iwl_cfg iwl3945_abg_cfg = { @@ -2838,6 +2839,7 @@ static struct iwl_cfg iwl3945_abg_cfg = { .led_compensation = 64, .broken_powersave = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; DEFINE_PCI_DEVICE_TABLE(iwl3945_hw_card_ids) = { diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index 7f9e448fe5e1..acca89a635a5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -2255,6 +2255,7 @@ struct iwl_cfg iwl4965_agn_cfg = { .led_compensation = 61, .chain_noise_num_beacons = IWL4965_CAL_NUM_BEACONS, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; /* Module firmware */ diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 8ab28a760157..a01b3c59a5a7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -1499,6 +1499,7 @@ struct iwl_lib_ops iwl5000_lib = { .set_ct_kill = iwl5000_set_ct_threshold, }, .add_bcast_station = iwl_add_bcast_station, + .recover_from_tx_stall = iwl_bg_monitor_recover, }; static struct iwl_lib_ops iwl5150_lib = { @@ -1553,6 +1554,7 @@ static struct iwl_lib_ops iwl5150_lib = { .set_ct_kill = iwl5150_set_ct_threshold, }, .add_bcast_station = iwl_add_bcast_station, + .recover_from_tx_stall = iwl_bg_monitor_recover, }; static const struct iwl_ops iwl5000_ops = { @@ -1602,6 +1604,7 @@ struct iwl_cfg iwl5300_agn_cfg = { .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; struct iwl_cfg iwl5100_bgn_cfg = { @@ -1628,6 +1631,7 @@ struct iwl_cfg iwl5100_bgn_cfg = { .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; struct iwl_cfg iwl5100_abg_cfg = { @@ -1652,6 +1656,7 @@ struct iwl_cfg iwl5100_abg_cfg = { .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; struct iwl_cfg iwl5100_agn_cfg = { @@ -1678,6 +1683,7 @@ struct iwl_cfg iwl5100_agn_cfg = { .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; struct iwl_cfg iwl5350_agn_cfg = { @@ -1704,6 +1710,7 @@ struct iwl_cfg iwl5350_agn_cfg = { .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; struct iwl_cfg iwl5150_agn_cfg = { @@ -1730,6 +1737,7 @@ struct iwl_cfg iwl5150_agn_cfg = { .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; struct iwl_cfg iwl5150_abg_cfg = { @@ -1754,6 +1762,7 @@ struct iwl_cfg iwl5150_abg_cfg = { .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c index fb7012289c3d..4fbc38cfd911 100644 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ b/drivers/net/wireless/iwlwifi/iwl-6000.c @@ -278,6 +278,7 @@ static struct iwl_lib_ops iwl6000_lib = { .set_ct_kill = iwl6000_set_ct_threshold, }, .add_bcast_station = iwl_add_bcast_station, + .recover_from_tx_stall = iwl_bg_monitor_recover, }; static const struct iwl_ops iwl6000_ops = { @@ -343,6 +344,7 @@ static struct iwl_lib_ops iwl6050_lib = { .set_calib_version = iwl6050_set_calib_version, }, .add_bcast_station = iwl_add_bcast_station, + .recover_from_tx_stall = iwl_bg_monitor_recover, }; static const struct iwl_ops iwl6050_ops = { @@ -386,6 +388,7 @@ struct iwl_cfg iwl6000i_2agn_cfg = { .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; struct iwl_cfg iwl6000i_2abg_cfg = { @@ -417,6 +420,7 @@ struct iwl_cfg iwl6000i_2abg_cfg = { .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; struct iwl_cfg iwl6000i_2bg_cfg = { @@ -448,6 +452,7 @@ struct iwl_cfg iwl6000i_2bg_cfg = { .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; struct iwl_cfg iwl6050_2agn_cfg = { @@ -480,6 +485,7 @@ struct iwl_cfg iwl6050_2agn_cfg = { .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .chain_noise_scale = 1500, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; struct iwl_cfg iwl6050_2abg_cfg = { @@ -511,6 +517,7 @@ struct iwl_cfg iwl6050_2abg_cfg = { .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .chain_noise_scale = 1500, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; struct iwl_cfg iwl6000_3agn_cfg = { @@ -543,6 +550,7 @@ struct iwl_cfg iwl6000_3agn_cfg = { .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 93931b61304b..82b1a3fb54ef 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -2074,6 +2074,13 @@ static void iwl_alive_start(struct iwl_priv *priv) /* After the ALIVE response, we can send host commands to the uCode */ set_bit(STATUS_ALIVE, &priv->status); + if (priv->cfg->ops->lib->recover_from_tx_stall) { + /* Enable timer to monitor the driver queues */ + mod_timer(&priv->monitor_recover, + jiffies + + msecs_to_jiffies(priv->cfg->monitor_recover_period)); + } + if (iwl_is_rfkill(priv)) return; @@ -3224,6 +3231,13 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv) priv->ucode_trace.data = (unsigned long)priv; priv->ucode_trace.function = iwl_bg_ucode_trace; + if (priv->cfg->ops->lib->recover_from_tx_stall) { + init_timer(&priv->monitor_recover); + priv->monitor_recover.data = (unsigned long)priv; + priv->monitor_recover.function = + priv->cfg->ops->lib->recover_from_tx_stall; + } + if (!priv->cfg->use_isr_legacy) tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) iwl_irq_tasklet, (unsigned long)priv); @@ -3243,6 +3257,8 @@ static void iwl_cancel_deferred_work(struct iwl_priv *priv) cancel_work_sync(&priv->beacon_update); del_timer_sync(&priv->statistics_periodic); del_timer_sync(&priv->ucode_trace); + if (priv->cfg->ops->lib->recover_from_tx_stall) + del_timer_sync(&priv->monitor_recover); } static void iwl_init_hw_rates(struct iwl_priv *priv, diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index d4c2a3e17c53..5180fb24cd38 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -3051,6 +3051,99 @@ int iwl_force_reset(struct iwl_priv *priv, int mode) } return 0; } +EXPORT_SYMBOL(iwl_force_reset); + +/** + * iwl_bg_monitor_recover - Timer callback to check for stuck queue and recover + * + * During normal condition (no queue is stuck), the timer is continually set to + * execute every monitor_recover_period milliseconds after the last timer + * expired. When the queue read_ptr is at the same place, the timer is + * shorten to 100mSecs. This is + * 1) to reduce the chance that the read_ptr may wrap around (not stuck) + * 2) to detect the stuck queues quicker before the station and AP can + * disassociate each other. + * + * This function monitors all the tx queues and recover from it if any + * of the queues are stuck. + * 1. It first check the cmd queue for stuck conditions. If it is stuck, + * it will recover by resetting the firmware and return. + * 2. Then, it checks for station association. If it associates it will check + * other queues. If any queue is stuck, it will recover by resetting + * the firmware. + * Note: It the number of times the queue read_ptr to be at the same place to + * be MAX_REPEAT+1 in order to consider to be stuck. + */ +/* + * The maximum number of times the read pointer of the tx queue at the + * same place without considering to be stuck. + */ +#define MAX_REPEAT (2) +static int iwl_check_stuck_queue(struct iwl_priv *priv, int cnt) +{ + struct iwl_tx_queue *txq; + struct iwl_queue *q; + + txq = &priv->txq[cnt]; + q = &txq->q; + /* queue is empty, skip */ + if (q->read_ptr != q->write_ptr) { + if (q->read_ptr == q->last_read_ptr) { + /* a queue has not been read from last time */ + if (q->repeat_same_read_ptr > MAX_REPEAT) { + IWL_ERR(priv, + "queue %d stuck %d time. Fw reload.\n", + q->id, q->repeat_same_read_ptr); + q->repeat_same_read_ptr = 0; + iwl_force_reset(priv, IWL_FW_RESET); + } else { + q->repeat_same_read_ptr++; + IWL_DEBUG_RADIO(priv, + "queue %d, not read %d time\n", + q->id, + q->repeat_same_read_ptr); + mod_timer(&priv->monitor_recover, jiffies + + msecs_to_jiffies(IWL_ONE_HUNDRED_MSECS)); + } + return 1; + } else { + q->last_read_ptr = q->read_ptr; + q->repeat_same_read_ptr = 0; + } + } + return 0; +} + +void iwl_bg_monitor_recover(unsigned long data) +{ + struct iwl_priv *priv = (struct iwl_priv *)data; + int cnt; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + /* monitor and check for stuck cmd queue */ + if (iwl_check_stuck_queue(priv, IWL_CMD_QUEUE_NUM)) + return; + + /* monitor and check for other stuck queues */ + if (iwl_is_associated(priv)) { + for (cnt = 0; cnt < priv->hw_params.max_txq_num; cnt++) { + /* skip as we already checked the command queue */ + if (cnt == IWL_CMD_QUEUE_NUM) + continue; + if (iwl_check_stuck_queue(priv, cnt)) + return; + } + } + /* + * Reschedule the timer to occur in + * priv->cfg->monitor_recover_period + */ + mod_timer(&priv->monitor_recover, + jiffies + msecs_to_jiffies(priv->cfg->monitor_recover_period)); +} +EXPORT_SYMBOL(iwl_bg_monitor_recover); #ifdef CONFIG_PM diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index 2482a25d543f..c4cd1deb3b3e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h @@ -191,6 +191,8 @@ struct iwl_lib_ops { struct iwl_temp_ops temp_ops; /* station management */ void (*add_bcast_station)(struct iwl_priv *priv); + /* recover from tx queue stall */ + void (*recover_from_tx_stall)(unsigned long data); }; struct iwl_led_ops { @@ -295,6 +297,8 @@ struct iwl_cfg { const bool support_wimax_coexist; u8 plcp_delta_threshold; s32 chain_noise_scale; + /* timer period for monitor the driver queues */ + u32 monitor_recover_period; }; /*************************** @@ -568,6 +572,9 @@ static inline u16 iwl_pcie_link_ctl(struct iwl_priv *priv) pci_read_config_word(priv->pci_dev, pos + PCI_EXP_LNKCTL, &pci_lnk_ctl); return pci_lnk_ctl; } + +void iwl_bg_monitor_recover(unsigned long data); + #ifdef CONFIG_PM int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state); int iwl_pci_resume(struct pci_dev *pdev); diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 9c676ea420b9..bb4cba508acf 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -183,6 +183,10 @@ struct iwl_queue { int n_bd; /* number of BDs in this queue */ int write_ptr; /* 1-st empty entry (index) host_w*/ int read_ptr; /* last used entry (index) host_r*/ + /* use for monitoring and recovering the stuck queue */ + int last_read_ptr; /* storing the last read_ptr */ + /* number of time read_ptr and last_read_ptr are the same */ + u8 repeat_same_read_ptr; dma_addr_t dma_addr; /* physical addr for BD's */ int n_window; /* safe queue window */ u32 id; @@ -1044,6 +1048,11 @@ struct iwl_event_log { #define IWL_DELAY_NEXT_FORCE_RF_RESET (HZ*3) #define IWL_DELAY_NEXT_FORCE_FW_RELOAD (HZ*5) +/* timer constants use to monitor and recover stuck tx queues in mSecs */ +#define IWL_MONITORING_PERIOD (1000) +#define IWL_ONE_HUNDRED_MSECS (100) +#define IWL_SIXTY_SECS (60000) + enum iwl_reset { IWL_RF_RESET = 0, IWL_FW_RESET, @@ -1354,6 +1363,7 @@ struct iwl_priv { struct work_struct run_time_calib_work; struct timer_list statistics_periodic; struct timer_list ucode_trace; + struct timer_list monitor_recover; bool hw_ready; struct iwl_event_log event_log; diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index 045e4a67344b..34c983833a8f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -322,6 +322,8 @@ static int iwl_queue_init(struct iwl_priv *priv, struct iwl_queue *q, q->high_mark = 2; q->write_ptr = q->read_ptr = 0; + q->last_read_ptr = 0; + q->repeat_same_read_ptr = 0; return 0; } diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 6687b9456554..4995134d7e4a 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -2501,6 +2501,13 @@ static void iwl3945_alive_start(struct iwl_priv *priv) /* After the ALIVE response, we can send commands to 3945 uCode */ set_bit(STATUS_ALIVE, &priv->status); + if (priv->cfg->ops->lib->recover_from_tx_stall) { + /* Enable timer to monitor the driver queues */ + mod_timer(&priv->monitor_recover, + jiffies + + msecs_to_jiffies(priv->cfg->monitor_recover_period)); + } + if (iwl_is_rfkill(priv)) return; @@ -3796,6 +3803,13 @@ static void iwl3945_setup_deferred_work(struct iwl_priv *priv) iwl3945_hw_setup_deferred_work(priv); + if (priv->cfg->ops->lib->recover_from_tx_stall) { + init_timer(&priv->monitor_recover); + priv->monitor_recover.data = (unsigned long)priv; + priv->monitor_recover.function = + priv->cfg->ops->lib->recover_from_tx_stall; + } + tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) iwl3945_irq_tasklet, (unsigned long)priv); } @@ -3808,6 +3822,8 @@ static void iwl3945_cancel_deferred_work(struct iwl_priv *priv) cancel_delayed_work(&priv->scan_check); cancel_delayed_work(&priv->alive_start); cancel_work_sync(&priv->beacon_update); + if (priv->cfg->ops->lib->recover_from_tx_stall) + del_timer_sync(&priv->monitor_recover); } static struct attribute *iwl3945_sysfs_entries[] = { From beac5498b792ed8420885ee23e8d4f2885ee2d13 Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Thu, 4 Mar 2010 13:38:58 -0800 Subject: [PATCH 07/60] iwlwifi: move plcp check to separated function Move the plcp error checking into stand alone function and pointed by ops to accommodate devices not needing this recovery. Signed-off-by: Trieu 'Andrew' Nguyen Signed-off-by: Wey-Yi Guy Signed-off-by: Reinette Chatre --- drivers/net/wireless/iwlwifi/iwl-1000.c | 1 + drivers/net/wireless/iwlwifi/iwl-4965.c | 1 + drivers/net/wireless/iwlwifi/iwl-5000.c | 2 + drivers/net/wireless/iwlwifi/iwl-6000.c | 2 + drivers/net/wireless/iwlwifi/iwl-core.h | 5 +++ drivers/net/wireless/iwlwifi/iwl-rx.c | 58 +++++++++++++++---------- 6 files changed, 47 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c index 5a9cb70160f6..c6d6a0c2cd3c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-1000.c +++ b/drivers/net/wireless/iwlwifi/iwl-1000.c @@ -213,6 +213,7 @@ static struct iwl_lib_ops iwl1000_lib = { }, .add_bcast_station = iwl_add_bcast_station, .recover_from_tx_stall = iwl_bg_monitor_recover, + .recover_from_statistics = iwl_recover_from_statistics, }; static const struct iwl_ops iwl1000_ops = { diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index acca89a635a5..5efd60a5b988 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -2221,6 +2221,7 @@ static struct iwl_lib_ops iwl4965_lib = { .set_ct_kill = iwl4965_set_ct_threshold, }, .add_bcast_station = iwl_add_bcast_station, + .recover_from_statistics = iwl_recover_from_statistics, }; static const struct iwl_ops iwl4965_ops = { diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index a01b3c59a5a7..75f2a5156beb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -1500,6 +1500,7 @@ struct iwl_lib_ops iwl5000_lib = { }, .add_bcast_station = iwl_add_bcast_station, .recover_from_tx_stall = iwl_bg_monitor_recover, + .recover_from_statistics = iwl_recover_from_statistics, }; static struct iwl_lib_ops iwl5150_lib = { @@ -1555,6 +1556,7 @@ static struct iwl_lib_ops iwl5150_lib = { }, .add_bcast_station = iwl_add_bcast_station, .recover_from_tx_stall = iwl_bg_monitor_recover, + .recover_from_statistics = iwl_recover_from_statistics, }; static const struct iwl_ops iwl5000_ops = { diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c index 4fbc38cfd911..b1f0e5c7e287 100644 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ b/drivers/net/wireless/iwlwifi/iwl-6000.c @@ -279,6 +279,7 @@ static struct iwl_lib_ops iwl6000_lib = { }, .add_bcast_station = iwl_add_bcast_station, .recover_from_tx_stall = iwl_bg_monitor_recover, + .recover_from_statistics = iwl_recover_from_statistics, }; static const struct iwl_ops iwl6000_ops = { @@ -345,6 +346,7 @@ static struct iwl_lib_ops iwl6050_lib = { }, .add_bcast_station = iwl_add_bcast_station, .recover_from_tx_stall = iwl_bg_monitor_recover, + .recover_from_statistics = iwl_recover_from_statistics, }; static const struct iwl_ops iwl6050_ops = { diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index c4cd1deb3b3e..4251afb8bf5e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h @@ -193,6 +193,9 @@ struct iwl_lib_ops { void (*add_bcast_station)(struct iwl_priv *priv); /* recover from tx queue stall */ void (*recover_from_tx_stall)(unsigned long data); + /* recover from errors showed in statistics */ + void (*recover_from_statistics)(struct iwl_priv *priv, + struct iwl_rx_packet *pkt); }; struct iwl_led_ops { @@ -434,6 +437,8 @@ void iwl_rx_missed_beacon_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); +void iwl_recover_from_statistics(struct iwl_priv *priv, + struct iwl_rx_packet *pkt); void iwl_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); void iwl_reply_statistics(struct iwl_priv *priv, diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c index 8116aa0d7678..d368b8df8d87 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-rx.c @@ -617,28 +617,18 @@ static void iwl_accumulative_statistics(struct iwl_priv *priv, #define REG_RECALIB_PERIOD (60) #define PLCP_MSG "plcp_err exceeded %u, %u, %u, %u, %u, %d, %u mSecs\n" -void iwl_rx_statistics(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) +/* + * This function checks for plcp error. + * - When the plcp error is exceeding the thresholds, it will reset the radio + * to improve the throughput. + */ +void iwl_recover_from_statistics(struct iwl_priv *priv, + struct iwl_rx_packet *pkt) { - int change; - struct iwl_rx_packet *pkt = rxb_addr(rxb); int combined_plcp_delta; unsigned int plcp_msec; unsigned long plcp_received_jiffies; - IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n", - (int)sizeof(priv->statistics), - le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK); - - change = ((priv->statistics.general.temperature != - pkt->u.stats.general.temperature) || - ((priv->statistics.flag & - STATISTICS_REPLY_FLG_HT40_MODE_MSK) != - (pkt->u.stats.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK))); - -#ifdef CONFIG_IWLWIFI_DEBUG - iwl_accumulative_statistics(priv, (__le32 *)&pkt->u.stats); -#endif /* * check for plcp_err and trigger radio reset if it exceeds * the plcp error threshold plcp_delta. @@ -659,11 +649,11 @@ void iwl_rx_statistics(struct iwl_priv *priv, le32_to_cpu(priv->statistics.rx.ofdm_ht.plcp_err)); if ((combined_plcp_delta > 0) && - ((combined_plcp_delta * 100) / plcp_msec) > + ((combined_plcp_delta * 100) / plcp_msec) > priv->cfg->plcp_delta_threshold) { /* - * if plcp_err exceed the threshold, the following - * data is printed in csv format: + * if plcp_err exceed the threshold, + * the following data is printed in csv format: * Text: plcp_err exceeded %d, * Received ofdm.plcp_err, * Current ofdm.plcp_err, @@ -678,9 +668,8 @@ void iwl_rx_statistics(struct iwl_priv *priv, le32_to_cpu(priv->statistics.rx.ofdm.plcp_err), le32_to_cpu(pkt->u.stats.rx.ofdm_ht.plcp_err), le32_to_cpu( - priv->statistics.rx.ofdm_ht.plcp_err), + priv->statistics.rx.ofdm_ht.plcp_err), combined_plcp_delta, plcp_msec); - /* * Reset the RF radio due to the high plcp * error rate @@ -688,6 +677,31 @@ void iwl_rx_statistics(struct iwl_priv *priv, iwl_force_reset(priv, IWL_RF_RESET); } } +} +EXPORT_SYMBOL(iwl_recover_from_statistics); + +void iwl_rx_statistics(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ + int change; + struct iwl_rx_packet *pkt = rxb_addr(rxb); + + + IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n", + (int)sizeof(priv->statistics), + le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK); + + change = ((priv->statistics.general.temperature != + pkt->u.stats.general.temperature) || + ((priv->statistics.flag & + STATISTICS_REPLY_FLG_HT40_MODE_MSK) != + (pkt->u.stats.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK))); + +#ifdef CONFIG_IWLWIFI_DEBUG + iwl_accumulative_statistics(priv, (__le32 *)&pkt->u.stats); +#endif + if (priv->cfg->ops->lib->recover_from_statistics) + priv->cfg->ops->lib->recover_from_statistics(priv, pkt); memcpy(&priv->statistics, &pkt->u.stats, sizeof(priv->statistics)); From d5a0ffa3eaf9e898f25a925813f1a723be7808f8 Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Thu, 4 Mar 2010 13:38:59 -0800 Subject: [PATCH 08/60] iwlwifi: Recover TX flow failure Monitors the tx statistics to detect the drop in throughput. When the throughput drops, the ratio of the actual_ack_count and the expected_ack_count also drops. At the same time, the aggregated ba_timeout (the number of ba timeout retries) also rises. If the actual_ack_count/expected_ack_count ratio is 0 and the number of ba timeout retries rises to BA_TIMEOUT_MAX, no tx packets can be delivered. Reloading the uCode and bring the system back to normal operational state. Signed-off-by: Trieu 'Andrew' Nguyen Signed-off-by: Wey-Yi Guy Signed-off-by: Reinette Chatre --- drivers/net/wireless/iwlwifi/iwl-agn.c | 14 +++++++- drivers/net/wireless/iwlwifi/iwl-dev.h | 5 +++ drivers/net/wireless/iwlwifi/iwl-rx.c | 50 +++++++++++++++++++++++++- 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 82b1a3fb54ef..e69e6256a41b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -2916,10 +2916,21 @@ static int iwl_mac_ampdu_action(struct ieee80211_hw *hw, return ret; case IEEE80211_AMPDU_TX_START: IWL_DEBUG_HT(priv, "start Tx\n"); - return iwl_tx_agg_start(priv, sta->addr, tid, ssn); + ret = iwl_tx_agg_start(priv, sta->addr, tid, ssn); + if (ret == 0) { + priv->_agn.agg_tids_count++; + IWL_DEBUG_HT(priv, "priv->_agn.agg_tids_count = %u\n", + priv->_agn.agg_tids_count); + } + return ret; case IEEE80211_AMPDU_TX_STOP: IWL_DEBUG_HT(priv, "stop Tx\n"); ret = iwl_tx_agg_stop(priv, sta->addr, tid); + if ((ret == 0) && (priv->_agn.agg_tids_count > 0)) { + priv->_agn.agg_tids_count--; + IWL_DEBUG_HT(priv, "priv->_agn.agg_tids_count = %u\n", + priv->_agn.agg_tids_count); + } if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return 0; else @@ -3303,6 +3314,7 @@ static int iwl_init_drv(struct iwl_priv *priv) priv->iw_mode = NL80211_IFTYPE_STATION; priv->current_ht_config.smps = IEEE80211_SMPS_STATIC; priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF; + priv->_agn.agg_tids_count = 0; /* initialize force reset */ priv->force_reset[IWL_RF_RESET].reset_duration = diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index bb4cba508acf..e847e6197a3d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -1300,6 +1300,11 @@ struct iwl_priv { int ict_index; u32 inta; bool use_ict; + /* + * reporting the number of tids has AGG on. 0 means + * no AGGREGATION + */ + u8 agg_tids_count; } _agn; #endif }; diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c index d368b8df8d87..7aef285d2818 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-rx.c @@ -616,9 +616,18 @@ static void iwl_accumulative_statistics(struct iwl_priv *priv, #define REG_RECALIB_PERIOD (60) +/* the threshold ratio of actual_ack_cnt to expected_ack_cnt in percent */ +#define ACK_CNT_RATIO (50) +#define BA_TIMEOUT_CNT (5) +#define BA_TIMEOUT_MAX (16) + #define PLCP_MSG "plcp_err exceeded %u, %u, %u, %u, %u, %d, %u mSecs\n" /* - * This function checks for plcp error. + * This function checks for plcp error, ACK count ratios, aggregated BA + * timeout retries. + * - When the ACK count ratio is 0 and aggregated BA timeout retries is + * exceeding the BA_TIMEOUT_MAX, it will recover the failure by resetting + * the firmware. * - When the plcp error is exceeding the thresholds, it will reset the radio * to improve the throughput. */ @@ -628,6 +637,45 @@ void iwl_recover_from_statistics(struct iwl_priv *priv, int combined_plcp_delta; unsigned int plcp_msec; unsigned long plcp_received_jiffies; + int actual_ack_cnt_delta; + int expected_ack_cnt_delta; + int ba_timeout_delta; + + actual_ack_cnt_delta = + le32_to_cpu(pkt->u.stats.tx.actual_ack_cnt) - + le32_to_cpu(priv->statistics.tx.actual_ack_cnt); + expected_ack_cnt_delta = + le32_to_cpu(pkt->u.stats.tx.expected_ack_cnt) - + le32_to_cpu(priv->statistics.tx.expected_ack_cnt); + ba_timeout_delta = + le32_to_cpu(pkt->u.stats.tx.agg.ba_timeout) - + le32_to_cpu(priv->statistics.tx.agg.ba_timeout); + if ((priv->_agn.agg_tids_count > 0) && + (expected_ack_cnt_delta > 0) && + (((actual_ack_cnt_delta * 100) / expected_ack_cnt_delta) + < ACK_CNT_RATIO) && + (ba_timeout_delta > BA_TIMEOUT_CNT)) { + IWL_DEBUG_RADIO(priv, "actual_ack_cnt delta = %d," + " expected_ack_cnt = %d\n", + actual_ack_cnt_delta, expected_ack_cnt_delta); + +#ifdef CONFIG_IWLWIFI_DEBUG + IWL_DEBUG_RADIO(priv, "rx_detected_cnt delta = %d\n", + priv->delta_statistics.tx.rx_detected_cnt); + IWL_DEBUG_RADIO(priv, + "ack_or_ba_timeout_collision delta = %d\n", + priv->delta_statistics.tx. + ack_or_ba_timeout_collision); +#endif + IWL_DEBUG_RADIO(priv, "agg ba_timeout delta = %d\n", + ba_timeout_delta); + if ((actual_ack_cnt_delta == 0) && + (ba_timeout_delta >= BA_TIMEOUT_MAX)) { + IWL_DEBUG_RADIO(priv, + "call iwl_force_reset(IWL_FW_RESET)\n"); + iwl_force_reset(priv, IWL_FW_RESET); + } + } /* * check for plcp_err and trigger radio reset if it exceeds From fa8f130c504223d25c116b3d23787f465dfb1317 Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Fri, 5 Mar 2010 14:22:46 -0800 Subject: [PATCH 09/60] iwlwifi: code cleanup for connectivity recovery Split the connectivity check and recovery routine into separated functions based on the types 1. iwl_good_ack_health() - check for ack count 2. iwl_good_plcp_health() - check for plcp error Based on the type of errors being detected, different recovery methods will be used to bring the system back to normal operational state. Because different NIC has different HW and uCode, the behavior is also different; these functions thus now form part of the ops infrastructure, so we can have more control on how to monitor and recover from error condition case per device. Signed-off-by: Wey-Yi Guy Signed-off-by: Reinette Chatre --- drivers/net/wireless/iwlwifi/iwl-1000.c | 3 +- drivers/net/wireless/iwlwifi/iwl-4965.c | 2 +- drivers/net/wireless/iwlwifi/iwl-5000.c | 6 +- drivers/net/wireless/iwlwifi/iwl-6000.c | 6 +- drivers/net/wireless/iwlwifi/iwl-core.h | 11 ++- drivers/net/wireless/iwlwifi/iwl-rx.c | 97 +++++++++++++++++-------- 6 files changed, 85 insertions(+), 40 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c index c6d6a0c2cd3c..9e392896005d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-1000.c +++ b/drivers/net/wireless/iwlwifi/iwl-1000.c @@ -213,7 +213,8 @@ static struct iwl_lib_ops iwl1000_lib = { }, .add_bcast_station = iwl_add_bcast_station, .recover_from_tx_stall = iwl_bg_monitor_recover, - .recover_from_statistics = iwl_recover_from_statistics, + .check_plcp_health = iwl_good_plcp_health, + .check_ack_health = iwl_good_ack_health, }; static const struct iwl_ops iwl1000_ops = { diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index 5efd60a5b988..3949133d9ee2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -2221,7 +2221,7 @@ static struct iwl_lib_ops iwl4965_lib = { .set_ct_kill = iwl4965_set_ct_threshold, }, .add_bcast_station = iwl_add_bcast_station, - .recover_from_statistics = iwl_recover_from_statistics, + .check_plcp_health = iwl_good_plcp_health, }; static const struct iwl_ops iwl4965_ops = { diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 75f2a5156beb..2267cad49cbf 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -1500,7 +1500,8 @@ struct iwl_lib_ops iwl5000_lib = { }, .add_bcast_station = iwl_add_bcast_station, .recover_from_tx_stall = iwl_bg_monitor_recover, - .recover_from_statistics = iwl_recover_from_statistics, + .check_plcp_health = iwl_good_plcp_health, + .check_ack_health = iwl_good_ack_health, }; static struct iwl_lib_ops iwl5150_lib = { @@ -1556,7 +1557,8 @@ static struct iwl_lib_ops iwl5150_lib = { }, .add_bcast_station = iwl_add_bcast_station, .recover_from_tx_stall = iwl_bg_monitor_recover, - .recover_from_statistics = iwl_recover_from_statistics, + .check_plcp_health = iwl_good_plcp_health, + .check_ack_health = iwl_good_ack_health, }; static const struct iwl_ops iwl5000_ops = { diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c index b1f0e5c7e287..d75799946a7e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ b/drivers/net/wireless/iwlwifi/iwl-6000.c @@ -279,7 +279,8 @@ static struct iwl_lib_ops iwl6000_lib = { }, .add_bcast_station = iwl_add_bcast_station, .recover_from_tx_stall = iwl_bg_monitor_recover, - .recover_from_statistics = iwl_recover_from_statistics, + .check_plcp_health = iwl_good_plcp_health, + .check_ack_health = iwl_good_ack_health, }; static const struct iwl_ops iwl6000_ops = { @@ -346,7 +347,8 @@ static struct iwl_lib_ops iwl6050_lib = { }, .add_bcast_station = iwl_add_bcast_station, .recover_from_tx_stall = iwl_bg_monitor_recover, - .recover_from_statistics = iwl_recover_from_statistics, + .check_plcp_health = iwl_good_plcp_health, + .check_ack_health = iwl_good_ack_health, }; static const struct iwl_ops iwl6050_ops = { diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index 4251afb8bf5e..b3e698b576e1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h @@ -193,8 +193,11 @@ struct iwl_lib_ops { void (*add_bcast_station)(struct iwl_priv *priv); /* recover from tx queue stall */ void (*recover_from_tx_stall)(unsigned long data); - /* recover from errors showed in statistics */ - void (*recover_from_statistics)(struct iwl_priv *priv, + /* check for plcp health */ + bool (*check_plcp_health)(struct iwl_priv *priv, + struct iwl_rx_packet *pkt); + /* check for ack health */ + bool (*check_ack_health)(struct iwl_priv *priv, struct iwl_rx_packet *pkt); }; @@ -437,7 +440,9 @@ void iwl_rx_missed_beacon_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); -void iwl_recover_from_statistics(struct iwl_priv *priv, +bool iwl_good_plcp_health(struct iwl_priv *priv, + struct iwl_rx_packet *pkt); +bool iwl_good_ack_health(struct iwl_priv *priv, struct iwl_rx_packet *pkt); void iwl_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c index 7aef285d2818..b6a64d83718a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-rx.c @@ -621,24 +621,18 @@ static void iwl_accumulative_statistics(struct iwl_priv *priv, #define BA_TIMEOUT_CNT (5) #define BA_TIMEOUT_MAX (16) -#define PLCP_MSG "plcp_err exceeded %u, %u, %u, %u, %u, %d, %u mSecs\n" -/* - * This function checks for plcp error, ACK count ratios, aggregated BA - * timeout retries. - * - When the ACK count ratio is 0 and aggregated BA timeout retries is - * exceeding the BA_TIMEOUT_MAX, it will recover the failure by resetting - * the firmware. - * - When the plcp error is exceeding the thresholds, it will reset the radio - * to improve the throughput. +/** + * iwl_good_ack_health - checks for ACK count ratios, BA timeout retries. + * + * When the ACK count ratio is 0 and aggregated BA timeout retries exceeding + * the BA_TIMEOUT_MAX, reload firmware and bring system back to normal + * operation state. */ -void iwl_recover_from_statistics(struct iwl_priv *priv, - struct iwl_rx_packet *pkt) +bool iwl_good_ack_health(struct iwl_priv *priv, + struct iwl_rx_packet *pkt) { - int combined_plcp_delta; - unsigned int plcp_msec; - unsigned long plcp_received_jiffies; - int actual_ack_cnt_delta; - int expected_ack_cnt_delta; + bool rc = true; + int actual_ack_cnt_delta, expected_ack_cnt_delta; int ba_timeout_delta; actual_ack_cnt_delta = @@ -669,13 +663,27 @@ void iwl_recover_from_statistics(struct iwl_priv *priv, #endif IWL_DEBUG_RADIO(priv, "agg ba_timeout delta = %d\n", ba_timeout_delta); - if ((actual_ack_cnt_delta == 0) && - (ba_timeout_delta >= BA_TIMEOUT_MAX)) { - IWL_DEBUG_RADIO(priv, - "call iwl_force_reset(IWL_FW_RESET)\n"); - iwl_force_reset(priv, IWL_FW_RESET); - } + if (!actual_ack_cnt_delta && + (ba_timeout_delta >= BA_TIMEOUT_MAX)) + rc = false; } + return rc; +} +EXPORT_SYMBOL(iwl_good_ack_health); + +/** + * iwl_good_plcp_health - checks for plcp error. + * + * When the plcp error is exceeding the thresholds, reset the radio + * to improve the throughput. + */ +bool iwl_good_plcp_health(struct iwl_priv *priv, + struct iwl_rx_packet *pkt) +{ + bool rc = true; + int combined_plcp_delta; + unsigned int plcp_msec; + unsigned long plcp_received_jiffies; /* * check for plcp_err and trigger radio reset if it exceeds @@ -710,7 +718,8 @@ void iwl_recover_from_statistics(struct iwl_priv *priv, * combined_plcp_delta, * plcp_msec */ - IWL_DEBUG_RADIO(priv, PLCP_MSG, + IWL_DEBUG_RADIO(priv, "plcp_err exceeded %u, " + "%u, %u, %u, %u, %d, %u mSecs\n", priv->cfg->plcp_delta_threshold, le32_to_cpu(pkt->u.stats.rx.ofdm.plcp_err), le32_to_cpu(priv->statistics.rx.ofdm.plcp_err), @@ -718,15 +727,42 @@ void iwl_recover_from_statistics(struct iwl_priv *priv, le32_to_cpu( priv->statistics.rx.ofdm_ht.plcp_err), combined_plcp_delta, plcp_msec); - /* - * Reset the RF radio due to the high plcp - * error rate - */ - iwl_force_reset(priv, IWL_RF_RESET); + rc = false; + } + } + return rc; +} +EXPORT_SYMBOL(iwl_good_plcp_health); + +static void iwl_recover_from_statistics(struct iwl_priv *priv, + struct iwl_rx_packet *pkt) +{ + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + if (iwl_is_associated(priv)) { + if (priv->cfg->ops->lib->check_ack_health) { + if (!priv->cfg->ops->lib->check_ack_health( + priv, pkt)) { + /* + * low ack count detected + * restart Firmware + */ + IWL_ERR(priv, "low ack count detected, " + "restart firmware\n"); + iwl_force_reset(priv, IWL_FW_RESET); + } + } else if (priv->cfg->ops->lib->check_plcp_health) { + if (!priv->cfg->ops->lib->check_plcp_health( + priv, pkt)) { + /* + * high plcp error detected + * reset Radio + */ + iwl_force_reset(priv, IWL_RF_RESET); + } } } } -EXPORT_SYMBOL(iwl_recover_from_statistics); void iwl_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) @@ -748,8 +784,7 @@ void iwl_rx_statistics(struct iwl_priv *priv, #ifdef CONFIG_IWLWIFI_DEBUG iwl_accumulative_statistics(priv, (__le32 *)&pkt->u.stats); #endif - if (priv->cfg->ops->lib->recover_from_statistics) - priv->cfg->ops->lib->recover_from_statistics(priv, pkt); + iwl_recover_from_statistics(priv, pkt); memcpy(&priv->statistics, &pkt->u.stats, sizeof(priv->statistics)); From e21247cc4a37296f57281a472e961ff09de0e98d Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Mon, 1 Mar 2010 23:13:29 +0800 Subject: [PATCH 10/60] ath9k: decrease size of ath9k.ko From e74b075cdb143d45be9b371ee8a8e2dcfc15ab34 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sat, 27 Feb 2010 23:50:54 +0800 Subject: [PATCH] ath9k: decrease size of ath9k.ko The patch defines the fields of 'valid_single_stream' and 'valid' in struct ath_rate_table as char type, so decrease the size of ath9k.ko about 2KB. old ath9k.ko [tom@tom-lei ath9k]$ size ath9k.ko text data bss dec hex filename 69344 3080 168 72592 11b90 ath9k.ko new ath9k.ko [tom@tom-lei ath9k]$ size ath9k.ko text data bss dec hex filename 67304 3080 168 70552 11398 ath9k.ko Signed-off-by: Larry Finger Signed-off-by: Ming Lei Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/rc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/rc.h b/drivers/net/wireless/ath/ath9k/rc.h index f4818e4fa4b0..36083dde863d 100644 --- a/drivers/net/wireless/ath/ath9k/rc.h +++ b/drivers/net/wireless/ath/ath9k/rc.h @@ -110,8 +110,8 @@ struct ath_rate_table { int rate_cnt; int mcs_start; struct { - int valid; - int valid_single_stream; + u8 valid; + u8 valid_single_stream; u8 phy; u32 ratekbps; u32 user_ratekbps; From 70807e99dbb0c5847668b617caf0ad7f876a23d9 Mon Sep 17 00:00:00 2001 From: Sujith Date: Wed, 17 Mar 2010 14:25:14 +0530 Subject: [PATCH 11/60] ath9k_hw: update initialization values for AR9271 Update the register initialization values for AR9271. This is based on our last review from our systems team. Signed-off-by: Sujith Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 65 ++++++---- drivers/net/wireless/ath/ath9k/hw.h | 5 + drivers/net/wireless/ath/ath9k/initvals.h | 141 ++++++++++++++++------ 3 files changed, 146 insertions(+), 65 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 1fb14edfcb2a..9de46036db0e 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -603,9 +603,23 @@ static void ath9k_hw_init_mode_regs(struct ath_hw *ah) ARRAY_SIZE(ar9271Modes_9271), 6); INIT_INI_ARRAY(&ah->iniCommon, ar9271Common_9271, ARRAY_SIZE(ar9271Common_9271), 2); + INIT_INI_ARRAY(&ah->iniCommon_normal_cck_fir_coeff_9271, + ar9271Common_normal_cck_fir_coeff_9271, + ARRAY_SIZE(ar9271Common_normal_cck_fir_coeff_9271), 2); + INIT_INI_ARRAY(&ah->iniCommon_japan_2484_cck_fir_coeff_9271, + ar9271Common_japan_2484_cck_fir_coeff_9271, + ARRAY_SIZE(ar9271Common_japan_2484_cck_fir_coeff_9271), 2); INIT_INI_ARRAY(&ah->iniModes_9271_1_0_only, ar9271Modes_9271_1_0_only, ARRAY_SIZE(ar9271Modes_9271_1_0_only), 6); + INIT_INI_ARRAY(&ah->iniModes_9271_ANI_reg, ar9271Modes_9271_ANI_reg, + ARRAY_SIZE(ar9271Modes_9271_ANI_reg), 6); + INIT_INI_ARRAY(&ah->iniModes_high_power_tx_gain_9271, + ar9271Modes_high_power_tx_gain_9271, + ARRAY_SIZE(ar9271Modes_high_power_tx_gain_9271), 6); + INIT_INI_ARRAY(&ah->iniModes_normal_power_tx_gain_9271, + ar9271Modes_normal_power_tx_gain_9271, + ARRAY_SIZE(ar9271Modes_normal_power_tx_gain_9271), 6); return; } @@ -1266,26 +1280,6 @@ static void ath9k_hw_override_ini(struct ath_hw *ah, { u32 val; - if (AR_SREV_9271(ah)) { - /* - * Enable spectral scan to solution for issues with stuck - * beacons on AR9271 1.0. The beacon stuck issue is not seeon on - * AR9271 1.1 - */ - if (AR_SREV_9271_10(ah)) { - val = REG_READ(ah, AR_PHY_SPECTRAL_SCAN) | - AR_PHY_SPECTRAL_SCAN_ENABLE; - REG_WRITE(ah, AR_PHY_SPECTRAL_SCAN, val); - } - else if (AR_SREV_9271_11(ah)) - /* - * change AR_PHY_RF_CTL3 setting to fix MAC issue - * present on AR9271 1.1 - */ - REG_WRITE(ah, AR_PHY_RF_CTL3, 0x3a020001); - return; - } - /* * Set the RX_ABORT and RX_DIS and clear if off only after * RXE is set for MAC. This prevents frames with corrupted @@ -1294,8 +1288,10 @@ static void ath9k_hw_override_ini(struct ath_hw *ah, REG_SET_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); if (AR_SREV_9280_10_OR_LATER(ah)) { - val = REG_READ(ah, AR_PCU_MISC_MODE2) & - (~AR_PCU_MISC_MODE2_HWWAR1); + val = REG_READ(ah, AR_PCU_MISC_MODE2); + + if (!AR_SREV_9271(ah)) + val &= ~AR_PCU_MISC_MODE2_HWWAR1; if (AR_SREV_9287_10_OR_LATER(ah)) val = val & (~AR_PCU_MISC_MODE2_HWWAR2); @@ -1439,7 +1435,10 @@ static int ath9k_hw_process_ini(struct ath_hw *ah, return -EINVAL; } + /* Set correct baseband to analog shift setting to access analog chips */ REG_WRITE(ah, AR_PHY(0), 0x00000007); + + /* Write ADDAC shifts */ REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_EXTERNAL_RADIO); ah->eep_ops->set_addac(ah, chan); @@ -1451,9 +1450,11 @@ static int ath9k_hw_process_ini(struct ath_hw *ah, sizeof(u32) * ah->iniAddac.ia_rows * ah->iniAddac.ia_columns; + /* For AR5416 2.0/2.1 */ memcpy(ah->addac5416_21, ah->iniAddac.ia_array, addacSize); + /* override CLKDRV value at [row, column] = [31, 1] */ (ah->addac5416_21)[31 * ah->iniAddac.ia_columns + 1] = 0; temp.ia_array = ah->addac5416_21; @@ -1485,6 +1486,11 @@ static int ath9k_hw_process_ini(struct ath_hw *ah, AR_SREV_9287_10_OR_LATER(ah)) REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites); + if (AR_SREV_9271_10(ah)) + REG_WRITE_ARRAY(&ah->iniModes_9271_1_0_only, + modesIndex, regWrites); + + /* Write common array parameters */ for (i = 0; i < ah->iniCommon.ia_rows; i++) { u32 reg = INI_RA(&ah->iniCommon, i, 0); u32 val = INI_RA(&ah->iniCommon, i, 1); @@ -1499,11 +1505,16 @@ static int ath9k_hw_process_ini(struct ath_hw *ah, DO_DELAY(regWrites); } - ath9k_hw_write_regs(ah, freqIndex, regWrites); + if (AR_SREV_9271(ah)) { + if (ah->eep_ops->get_eeprom(ah, EEP_TXGAIN_TYPE) == 1) + REG_WRITE_ARRAY(&ah->iniModes_high_power_tx_gain_9271, + modesIndex, regWrites); + else + REG_WRITE_ARRAY(&ah->iniModes_normal_power_tx_gain_9271, + modesIndex, regWrites); + } - if (AR_SREV_9271_10(ah)) - REG_WRITE_ARRAY(&ah->iniModes_9271_1_0_only, - modesIndex, regWrites); + ath9k_hw_write_regs(ah, freqIndex, regWrites); if (AR_SREV_9280_20(ah) && IS_CHAN_A_5MHZ_SPACED(chan)) { REG_WRITE_ARRAY(&ah->iniModesAdditional, modesIndex, @@ -1517,6 +1528,7 @@ static int ath9k_hw_process_ini(struct ath_hw *ah, if (OLC_FOR_AR9280_20_LATER) ath9k_olc_init(ah); + /* Set TX power */ ah->eep_ops->set_txpower(ah, chan, ath9k_regd_get_ctl(regulatory, chan), channel->max_antenna_gain * 2, @@ -1524,6 +1536,7 @@ static int ath9k_hw_process_ini(struct ath_hw *ah, min((u32) MAX_RATE_POWER, (u32) regulatory->power_limit)); + /* Write analog registers */ if (!ath9k_hw_set_rf_regs(ah, chan, freqIndex)) { ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL, "ar5416SetRfRegs failed\n"); diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 20d90268ce31..e63edfb91590 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -599,6 +599,11 @@ struct ath_hw { struct ar5416IniArray iniModes_9271_1_0_only; struct ar5416IniArray iniCckfirNormal; struct ar5416IniArray iniCckfirJapan2484; + struct ar5416IniArray iniCommon_normal_cck_fir_coeff_9271; + struct ar5416IniArray iniCommon_japan_2484_cck_fir_coeff_9271; + struct ar5416IniArray iniModes_9271_ANI_reg; + struct ar5416IniArray iniModes_high_power_tx_gain_9271; + struct ar5416IniArray iniModes_normal_power_tx_gain_9271; u32 intr_gen_timer_trigger; u32 intr_gen_timer_thresh; diff --git a/drivers/net/wireless/ath/ath9k/initvals.h b/drivers/net/wireless/ath/ath9k/initvals.h index 8a3bf3ab998d..177bdeb84ad7 100644 --- a/drivers/net/wireless/ath/ath9k/initvals.h +++ b/drivers/net/wireless/ath/ath9k/initvals.h @@ -6441,7 +6441,7 @@ static const u_int32_t ar9271Modes_9271[][6] = { { 0x00009a44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8, 0x00000000 }, { 0x00009a48, 0x00000000, 0x00000000, 0x00058284, 0x00058284, 0x00000000 }, { 0x00009a4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288, 0x00000000 }, - { 0x00009a50, 0x00000000, 0x00000000, 0x00058220, 0x00058220, 0x00000000 }, + { 0x00009a50, 0x00000000, 0x00000000, 0x00058224, 0x00058224, 0x00000000 }, { 0x00009a54, 0x00000000, 0x00000000, 0x00058290, 0x00058290, 0x00000000 }, { 0x00009a58, 0x00000000, 0x00000000, 0x00058300, 0x00058300, 0x00000000 }, { 0x00009a5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304, 0x00000000 }, @@ -6455,8 +6455,8 @@ static const u_int32_t ar9271Modes_9271[][6] = { { 0x00009a7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c, 0x00000000 }, { 0x00009a80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000 }, { 0x00009a84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000 }, - { 0x00009a88, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000 }, - { 0x00009a8c, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 }, + { 0x00009a88, 0x00000000, 0x00000000, 0x00078b00, 0x00078b00, 0x00000000 }, + { 0x00009a8c, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000 }, { 0x00009a90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 }, { 0x00009a94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c, 0x00000000 }, { 0x00009a98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80, 0x00000000 }, @@ -6569,7 +6569,7 @@ static const u_int32_t ar9271Modes_9271[][6] = { { 0x0000aa44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8, 0x00000000 }, { 0x0000aa48, 0x00000000, 0x00000000, 0x00058284, 0x00058284, 0x00000000 }, { 0x0000aa4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288, 0x00000000 }, - { 0x0000aa50, 0x00000000, 0x00000000, 0x00058220, 0x00058220, 0x00000000 }, + { 0x0000aa50, 0x00000000, 0x00000000, 0x00058224, 0x00058224, 0x00000000 }, { 0x0000aa54, 0x00000000, 0x00000000, 0x00058290, 0x00058290, 0x00000000 }, { 0x0000aa58, 0x00000000, 0x00000000, 0x00058300, 0x00058300, 0x00000000 }, { 0x0000aa5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304, 0x00000000 }, @@ -6583,8 +6583,8 @@ static const u_int32_t ar9271Modes_9271[][6] = { { 0x0000aa7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c, 0x00000000 }, { 0x0000aa80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000 }, { 0x0000aa84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000 }, - { 0x0000aa88, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000 }, - { 0x0000aa8c, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 }, + { 0x0000aa88, 0x00000000, 0x00000000, 0x00078b00, 0x00078b00, 0x00000000 }, + { 0x0000aa8c, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000 }, { 0x0000aa90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 }, { 0x0000aa94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c, 0x00000000 }, { 0x0000aa98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80, 0x00000000 }, @@ -6683,25 +6683,6 @@ static const u_int32_t ar9271Modes_9271[][6] = { { 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a }, { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 }, { 0x0000a250, 0x0004f000, 0x0004f000, 0x0004a000, 0x0004a000, 0x0004a000 }, - { 0x0000a274, 0x0a21c652, 0x0a21c652, 0x0a218652, 0x0a218652, 0x0a22a652 }, - { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, - { 0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200, 0x00000000 }, - { 0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208, 0x00000000 }, - { 0x0000a30c, 0x00000000, 0x00000000, 0x00019608, 0x00019608, 0x00000000 }, - { 0x0000a310, 0x00000000, 0x00000000, 0x0001e610, 0x0001e610, 0x00000000 }, - { 0x0000a314, 0x00000000, 0x00000000, 0x0002d6d0, 0x0002d6d0, 0x00000000 }, - { 0x0000a318, 0x00000000, 0x00000000, 0x00039758, 0x00039758, 0x00000000 }, - { 0x0000a31c, 0x00000000, 0x00000000, 0x0003b759, 0x0003b759, 0x00000000 }, - { 0x0000a320, 0x00000000, 0x00000000, 0x0003d75a, 0x0003d75a, 0x00000000 }, - { 0x0000a324, 0x00000000, 0x00000000, 0x0004175c, 0x0004175c, 0x00000000 }, - { 0x0000a328, 0x00000000, 0x00000000, 0x0004575e, 0x0004575e, 0x00000000 }, - { 0x0000a32c, 0x00000000, 0x00000000, 0x0004979f, 0x0004979f, 0x00000000 }, - { 0x0000a330, 0x00000000, 0x00000000, 0x0004d7df, 0x0004d7df, 0x00000000 }, - { 0x0000a334, 0x000368de, 0x000368de, 0x000368de, 0x000368de, 0x00000000 }, - { 0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x00000000 }, - { 0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x00000000 }, - { 0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, - { 0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, { 0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e }, }; @@ -6879,7 +6860,7 @@ static const u_int32_t ar9271Common_9271[][2] = { { 0x00008258, 0x00000000 }, { 0x0000825c, 0x400000ff }, { 0x00008260, 0x00080922 }, - { 0x00008264, 0x88a00010 }, + { 0x00008264, 0xa8a00010 }, { 0x00008270, 0x00000000 }, { 0x00008274, 0x40000000 }, { 0x00008278, 0x003e4180 }, @@ -6910,13 +6891,10 @@ static const u_int32_t ar9271Common_9271[][2] = { { 0x00007810, 0x71c0d388 }, { 0x00007814, 0x924934a8 }, { 0x0000781c, 0x00000000 }, - { 0x00007820, 0x00000c04 }, - { 0x00007824, 0x00d8abff }, { 0x00007828, 0x66964300 }, { 0x0000782c, 0x8db6d961 }, { 0x00007830, 0x8db6d96c }, { 0x00007834, 0x6140008b }, - { 0x00007838, 0x00000029 }, { 0x0000783c, 0x72ee0a72 }, { 0x00007840, 0xbbfffffc }, { 0x00007844, 0x000c0db6 }, @@ -6929,7 +6907,6 @@ static const u_int32_t ar9271Common_9271[][2] = { { 0x00007860, 0x21084210 }, { 0x00007864, 0xf7d7ffde }, { 0x00007868, 0xc2034080 }, - { 0x0000786c, 0x48609eb4 }, { 0x00007870, 0x10142c00 }, { 0x00009808, 0x00000000 }, { 0x0000980c, 0xafe68e30 }, @@ -6982,9 +6959,6 @@ static const u_int32_t ar9271Common_9271[][2] = { { 0x000099e8, 0x3c466478 }, { 0x000099ec, 0x0cc80caa }, { 0x000099f0, 0x00000000 }, - { 0x0000a1f4, 0x00000000 }, - { 0x0000a1f8, 0x71733d01 }, - { 0x0000a1fc, 0xd0ad5c12 }, { 0x0000a208, 0x803e68c8 }, { 0x0000a210, 0x4080a333 }, { 0x0000a214, 0x00206c10 }, @@ -7004,13 +6978,9 @@ static const u_int32_t ar9271Common_9271[][2] = { { 0x0000a260, 0xdfa90f01 }, { 0x0000a268, 0x00000000 }, { 0x0000a26c, 0x0ebae9e6 }, - { 0x0000a278, 0x3bdef7bd }, - { 0x0000a27c, 0x050e83bd }, { 0x0000a388, 0x0c000000 }, { 0x0000a38c, 0x20202020 }, { 0x0000a390, 0x20202020 }, - { 0x0000a394, 0x3bdef7bd }, - { 0x0000a398, 0x000003bd }, { 0x0000a39c, 0x00000001 }, { 0x0000a3a0, 0x00000000 }, { 0x0000a3a4, 0x00000000 }, @@ -7025,8 +6995,6 @@ static const u_int32_t ar9271Common_9271[][2] = { { 0x0000a3cc, 0x20202020 }, { 0x0000a3d0, 0x20202020 }, { 0x0000a3d4, 0x20202020 }, - { 0x0000a3dc, 0x3bdef7bd }, - { 0x0000a3e0, 0x000003bd }, { 0x0000a3e4, 0x00000000 }, { 0x0000a3e8, 0x18c43433 }, { 0x0000a3ec, 0x00f70081 }, @@ -7046,7 +7014,102 @@ static const u_int32_t ar9271Common_9271[][2] = { { 0x0000d384, 0xf3307ff0 }, }; +static const u_int32_t ar9271Common_normal_cck_fir_coeff_9271[][2] = { + { 0x0000a1f4, 0x00fffeff }, + { 0x0000a1f8, 0x00f5f9ff }, + { 0x0000a1fc, 0xb79f6427 }, +}; + +static const u_int32_t ar9271Common_japan_2484_cck_fir_coeff_9271[][2] = { + { 0x0000a1f4, 0x00000000 }, + { 0x0000a1f8, 0xefff0301 }, + { 0x0000a1fc, 0xca9228ee }, +}; + static const u_int32_t ar9271Modes_9271_1_0_only[][6] = { { 0x00009910, 0x30002311, 0x30002311, 0x30002311, 0x30002311, 0x30002311 }, { 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 }, }; + +static const u_int32_t ar9271Modes_9271_ANI_reg[][6] = { + { 0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2 }, + { 0x0000985c, 0x3139605e, 0x3139605e, 0x3137605e, 0x3137605e, 0x3139605e }, + { 0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e }, + { 0x0000986c, 0x06903881, 0x06903881, 0x06903881, 0x06903881, 0x06903881 }, + { 0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0 }, + { 0x0000a208, 0x803e68c8, 0x803e68c8, 0x803e68c8, 0x803e68c8, 0x803e68c8 }, + { 0x00009924, 0xd00a8007, 0xd00a8007, 0xd00a800d, 0xd00a800d, 0xd00a800d }, + { 0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4 }, +}; + +static const u_int32_t ar9271Modes_normal_power_tx_gain_9271[][6] = { + { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200, 0x00000000 }, + { 0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208, 0x00000000 }, + { 0x0000a30c, 0x00000000, 0x00000000, 0x00019608, 0x00019608, 0x00000000 }, + { 0x0000a310, 0x00000000, 0x00000000, 0x0001e610, 0x0001e610, 0x00000000 }, + { 0x0000a314, 0x00000000, 0x00000000, 0x0002d6d0, 0x0002d6d0, 0x00000000 }, + { 0x0000a318, 0x00000000, 0x00000000, 0x00039758, 0x00039758, 0x00000000 }, + { 0x0000a31c, 0x00000000, 0x00000000, 0x0003b759, 0x0003b759, 0x00000000 }, + { 0x0000a320, 0x00000000, 0x00000000, 0x0003d75a, 0x0003d75a, 0x00000000 }, + { 0x0000a324, 0x00000000, 0x00000000, 0x0004175c, 0x0004175c, 0x00000000 }, + { 0x0000a328, 0x00000000, 0x00000000, 0x0004575e, 0x0004575e, 0x00000000 }, + { 0x0000a32c, 0x00000000, 0x00000000, 0x0004979f, 0x0004979f, 0x00000000 }, + { 0x0000a330, 0x00000000, 0x00000000, 0x0004d7df, 0x0004d7df, 0x00000000 }, + { 0x0000a334, 0x000368de, 0x000368de, 0x000368de, 0x000368de, 0x00000000 }, + { 0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x00000000 }, + { 0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x00000000 }, + { 0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x00007838, 0x00000029, 0x00000029, 0x00000029, 0x00000029, 0x00000029 }, + { 0x00007824, 0x00d8abff, 0x00d8abff, 0x00d8abff, 0x00d8abff, 0x00d8abff }, + { 0x0000786c, 0x48609eb4, 0x48609eb4, 0x48609eb4, 0x48609eb4, 0x48609eb4 }, + { 0x00007820, 0x00000c04, 0x00000c04, 0x00000c04, 0x00000c04, 0x00000c04 }, + { 0x0000a274, 0x0a21c652, 0x0a21c652, 0x0a218652, 0x0a218652, 0x0a22a652 }, + { 0x0000a278, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd }, + { 0x0000a27c, 0x050e83bd, 0x050e83bd, 0x050e83bd, 0x050e83bd, 0x050e83bd }, + { 0x0000a394, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd }, + { 0x0000a398, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd }, + { 0x0000a3dc, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd }, + { 0x0000a3e0, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd }, +}; + +static const u_int32_t ar9271Modes_high_power_tx_gain_9271[][6] = { + { 0x0000a300, 0x00000000, 0x00000000, 0x00010000, 0x00010000, 0x00000000 }, + { 0x0000a304, 0x00000000, 0x00000000, 0x00016200, 0x00016200, 0x00000000 }, + { 0x0000a308, 0x00000000, 0x00000000, 0x00018201, 0x00018201, 0x00000000 }, + { 0x0000a30c, 0x00000000, 0x00000000, 0x0001b240, 0x0001b240, 0x00000000 }, + { 0x0000a310, 0x00000000, 0x00000000, 0x0001d241, 0x0001d241, 0x00000000 }, + { 0x0000a314, 0x00000000, 0x00000000, 0x0001f600, 0x0001f600, 0x00000000 }, + { 0x0000a318, 0x00000000, 0x00000000, 0x00022800, 0x00022800, 0x00000000 }, + { 0x0000a31c, 0x00000000, 0x00000000, 0x00026802, 0x00026802, 0x00000000 }, + { 0x0000a320, 0x00000000, 0x00000000, 0x0002b805, 0x0002b805, 0x00000000 }, + { 0x0000a324, 0x00000000, 0x00000000, 0x0002ea41, 0x0002ea41, 0x00000000 }, + { 0x0000a328, 0x00000000, 0x00000000, 0x00038b00, 0x00038b00, 0x00000000 }, + { 0x0000a32c, 0x00000000, 0x00000000, 0x0003ab40, 0x0003ab40, 0x00000000 }, + { 0x0000a330, 0x00000000, 0x00000000, 0x0003cd80, 0x0003cd80, 0x00000000 }, + { 0x0000a334, 0x000368de, 0x000368de, 0x000368de, 0x000368de, 0x00000000 }, + { 0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x00000000 }, + { 0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x00000000 }, + { 0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x00007838, 0x0000002b, 0x0000002b, 0x0000002b, 0x0000002b, 0x0000002b }, + { 0x00007824, 0x00d8a7ff, 0x00d8a7ff, 0x00d8a7ff, 0x00d8a7ff, 0x00d8a7ff }, + { 0x0000786c, 0x08609eb6, 0x08609eb6, 0x08609eba, 0x08609eba, 0x08609eb6 }, + { 0x00007820, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00 }, + { 0x0000a274, 0x0a22a652, 0x0a22a652, 0x0a212652, 0x0a212652, 0x0a22a652 }, + { 0x0000a278, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7 }, + { 0x0000a27c, 0x05018063, 0x05038063, 0x05018063, 0x05018063, 0x05018063 }, + { 0x0000a394, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63 }, + { 0x0000a398, 0x00000063, 0x00000063, 0x00000063, 0x00000063, 0x00000063 }, + { 0x0000a3dc, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63 }, + { 0x0000a3e0, 0x00000063, 0x00000063, 0x00000063, 0x00000063, 0x00000063 }, +}; From 5b5fa3558db7451f4744ef92f2b1830d05b9307b Mon Sep 17 00:00:00 2001 From: Sujith Date: Wed, 17 Mar 2010 14:25:15 +0530 Subject: [PATCH 12/60] ath9k_hw: add GPIO setup code for AR9271 Assign the proper number of GPIO pins for AR9271. Signed-off-by: Sujith Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 11 +++++++++-- drivers/net/wireless/ath/ath9k/reg.h | 3 +++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 9de46036db0e..7b1ba6fa4bec 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -3229,7 +3229,9 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) else pCap->tx_triglevel_max = MAX_TX_FIFO_THRESHOLD; - if (AR_SREV_9285_10_OR_LATER(ah)) + if (AR_SREV_9271(ah)) + pCap->num_gpio_pins = AR9271_NUM_GPIO; + else if (AR_SREV_9285_10_OR_LATER(ah)) pCap->num_gpio_pins = AR9285_NUM_GPIO; else if (AR_SREV_9280_10_OR_LATER(ah)) pCap->num_gpio_pins = AR928X_NUM_GPIO; @@ -3465,7 +3467,9 @@ u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio) if (gpio >= ah->caps.num_gpio_pins) return 0xffffffff; - if (AR_SREV_9287_10_OR_LATER(ah)) + if (AR_SREV_9271(ah)) + return MS_REG_READ(AR9271, gpio) != 0; + else if (AR_SREV_9287_10_OR_LATER(ah)) return MS_REG_READ(AR9287, gpio) != 0; else if (AR_SREV_9285_10_OR_LATER(ah)) return MS_REG_READ(AR9285, gpio) != 0; @@ -3494,6 +3498,9 @@ EXPORT_SYMBOL(ath9k_hw_cfg_output); void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val) { + if (AR_SREV_9271(ah)) + val = ~val; + REG_RMW(ah, AR_GPIO_IN_OUT, ((val & 1) << gpio), AR_GPIO_BIT(gpio)); } diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index 72cfa8ebd9ae..198e41dd38a6 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -940,6 +940,7 @@ enum { #define AR928X_NUM_GPIO 10 #define AR9285_NUM_GPIO 12 #define AR9287_NUM_GPIO 11 +#define AR9271_NUM_GPIO 16 #define AR_GPIO_IN_OUT 0x4048 #define AR_GPIO_IN_VAL 0x0FFFC000 @@ -950,6 +951,8 @@ enum { #define AR9285_GPIO_IN_VAL_S 12 #define AR9287_GPIO_IN_VAL 0x003FF800 #define AR9287_GPIO_IN_VAL_S 11 +#define AR9271_GPIO_IN_VAL 0xFFFF0000 +#define AR9271_GPIO_IN_VAL_S 16 #define AR_GPIO_OE_OUT 0x404c #define AR_GPIO_OE_OUT_DRV 0x3 From 527d485fd8765e1df1dd9114f5c623253d6349c3 Mon Sep 17 00:00:00 2001 From: Sujith Date: Wed, 17 Mar 2010 14:25:16 +0530 Subject: [PATCH 13/60] ath9k_hw: skip chip tests for AR9271 The chip test is not required for AR9271 on the host driver code as the firmware will do the test internally on its own. Signed-off-by: Sujith Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 7b1ba6fa4bec..b1a4a8352f40 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -499,8 +499,10 @@ static int ath9k_hw_post_init(struct ath_hw *ah) { int ecode; - if (!ath9k_hw_chip_test(ah)) - return -ENODEV; + if (!AR_SREV_9271(ah)) { + if (!ath9k_hw_chip_test(ah)) + return -ENODEV; + } ecode = ath9k_hw_rf_claim(ah); if (ecode != 0) From e492d7cfdbb49f364529bc1642b978d85654c398 Mon Sep 17 00:00:00 2001 From: Sujith Date: Wed, 17 Mar 2010 14:25:17 +0530 Subject: [PATCH 14/60] ath9k_hw: fix TX descriptor setup for AR9271 The TX descriptors setup for AR971 requires the same setup as AR9285, so use that. Signed-off-by: Sujith Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index 589490b69ddc..7af823a1527d 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -351,7 +351,7 @@ void ath9k_hw_set11n_txdesc(struct ath_hw *ah, struct ath_desc *ds, ads->ds_ctl6 = SM(keyType, AR_EncrType); - if (AR_SREV_9285(ah)) { + if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) { ads->ds_ctl8 = 0; ads->ds_ctl9 = 0; ads->ds_ctl10 = 0; From 14b3af381df6359b15416df350e4e11892a52ab3 Mon Sep 17 00:00:00 2001 From: Sujith Date: Wed, 17 Mar 2010 14:25:18 +0530 Subject: [PATCH 15/60] ath9k_hw: Fix full sleep setup for AR9271 After telling the AR9271 to go into full sleep we do not need to clear the RTC reset signal. Signed-off-by: Sujith Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index b1a4a8352f40..023c38bb846b 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2453,7 +2453,7 @@ static void ath9k_set_power_sleep(struct ath_hw *ah, int setChip) if (!AR_SREV_9100(ah)) REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF); - if(!AR_SREV_5416(ah)) + if (!AR_SREV_5416(ah) && !AR_SREV_9271(ah)) REG_CLR_BIT(ah, (AR_RTC_RESET), AR_RTC_RESET_EN); } From 6398dc039d92b12805a900b3f82a033007a5b465 Mon Sep 17 00:00:00 2001 From: Sujith Date: Wed, 17 Mar 2010 14:25:19 +0530 Subject: [PATCH 16/60] ath9k_hw: fix noisefloor history buffer usage on AR9271 Noisefloor calibration involves querying hardware for samples and storing this information on a history buffer in hardware for actual noisefloor calibration processing in hardware. The history buffer supports collecting information for all Atheros hardware, one history buffer slot for each chain on each channel used for MIMO operation. For current hardware this means one history buffer slot for each chain on both the control (or primary) channel and the extension (or secondary) channel. We know which noisefloor registers to poke for collecting noisefloor data through the chainmask. For AR9285 and AR9271 devices, both 1x1, the chaimmask is defined as 0x9 = 0b0001001. The first four bits represent each chain out of a maximum of 4 chains [0-3] on the primary channel. The last four bits represent each chain on the extension channel. A chainmask of 0x9 therefore indicates chain 1 is active on both the primary and the extension channel. AR9271 only requires collecting and storing noisefloor history buffer data for the first chain on both the control and extension channel (nfarray[0] and nfarray[3]) so fix the code and avoid which reads and writes to the history buffer for the other chains. Since the noisefloor varies depending on the number of chains your device supports also initialize the noisefloor history buffer with reasonable values seen on 1x1 devices such as AR9285. Signed-off-by: Sujith Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/calib.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 238a5744d8e9..0c3e9c8fa017 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -103,7 +103,7 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah, "NF calibrated [ctl] [chain 0] is %d\n", nf); nfarray[0] = nf; - if (!AR_SREV_9285(ah)) { + if (!AR_SREV_9285(ah) && !AR_SREV_9271(ah)) { if (AR_SREV_9280_10_OR_LATER(ah)) nf = MS(REG_READ(ah, AR_PHY_CH1_CCA), AR9280_PHY_CH1_MINCCA_PWR); @@ -141,7 +141,7 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah, "NF calibrated [ext] [chain 0] is %d\n", nf); nfarray[3] = nf; - if (!AR_SREV_9285(ah)) { + if (!AR_SREV_9285(ah) && !AR_SREV_9271(ah)) { if (AR_SREV_9280_10_OR_LATER(ah)) nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA), AR9280_PHY_CH1_EXT_MINCCA_PWR); @@ -621,7 +621,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) u8 chainmask, rx_chain_status; rx_chain_status = REG_READ(ah, AR_PHY_RX_CHAINMASK); - if (AR_SREV_9285(ah)) + if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) chainmask = 0x9; else if (AR_SREV_9280(ah) || AR_SREV_9287(ah)) { if ((rx_chain_status & 0x2) || (rx_chain_status & 0x4)) @@ -715,7 +715,7 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah) if (AR_SREV_9280(ah)) noise_floor = AR_PHY_CCA_MAX_AR9280_GOOD_VALUE; - else if (AR_SREV_9285(ah)) + else if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) noise_floor = AR_PHY_CCA_MAX_AR9285_GOOD_VALUE; else if (AR_SREV_9287(ah)) noise_floor = AR_PHY_CCA_MAX_AR9287_GOOD_VALUE; From 2cbfaea48500ce98588e31bd806bbfb524b36b31 Mon Sep 17 00:00:00 2001 From: Sujith Date: Wed, 17 Mar 2010 14:25:20 +0530 Subject: [PATCH 17/60] ath9k_hw: restrict valid nf readings for AR9271 to -114 Noisefloor values read on AR9271 are unreliable if they are less than -114, set those statically to -116. Signed-off-by: Sujith Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/calib.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 0c3e9c8fa017..e76dd7498653 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -101,6 +101,10 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah, nf = 0 - ((nf ^ 0x1ff) + 1); ath_print(common, ATH_DBG_CALIBRATE, "NF calibrated [ctl] [chain 0] is %d\n", nf); + + if (AR_SREV_9271(ah) && (nf >= -114)) + nf = -116; + nfarray[0] = nf; if (!AR_SREV_9285(ah) && !AR_SREV_9271(ah)) { @@ -139,6 +143,10 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah, nf = 0 - ((nf ^ 0x1ff) + 1); ath_print(common, ATH_DBG_CALIBRATE, "NF calibrated [ext] [chain 0] is %d\n", nf); + + if (AR_SREV_9271(ah) && (nf >= -114)) + nf = -116; + nfarray[3] = nf; if (!AR_SREV_9285(ah) && !AR_SREV_9271(ah)) { From 02afa2a01b74ed3e8f3a85be11919b33f4ad4f02 Mon Sep 17 00:00:00 2001 From: Sujith Date: Wed, 17 Mar 2010 14:25:21 +0530 Subject: [PATCH 18/60] ath9k_hw: use the skip count for PA calibration on AR9271 Periodic power amplifier offset calibration is skipped on ath9k algorithmically, this is required on AR9271. Signed-off-by: Sujith Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/calib.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index e76dd7498653..d5026e4f484b 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -1059,9 +1059,12 @@ bool ath9k_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan, /* Do NF cal only at longer intervals */ if (longcal) { /* Do periodic PAOffset Cal */ - if (AR_SREV_9271(ah)) - ath9k_hw_9271_pa_cal(ah, false); - else if (AR_SREV_9285_11_OR_LATER(ah)) { + if (AR_SREV_9271(ah)) { + if (!ah->pacal_info.skipcount) + ath9k_hw_9271_pa_cal(ah, false); + else + ah->pacal_info.skipcount--; + } else if (AR_SREV_9285_11_OR_LATER(ah)) { if (!ah->pacal_info.skipcount) ath9k_hw_9285_pa_cal(ah, false); else From 25e2ab17fd77e752597050980cec4efae7f87854 Mon Sep 17 00:00:00 2001 From: Sujith Date: Wed, 17 Mar 2010 14:25:22 +0530 Subject: [PATCH 19/60] ath9k_hw: always set the core clock for AR9271 When initializing the PLL on AR9271 we always need to set the core clock to 117MHz. While at it remove the baud rate settings for the serial device on the AR9271, the default settings work well unless you want to customize it. Signed-off-by: Sujith Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 34 ++--------------------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 023c38bb846b..5bc5f5fdff57 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1006,22 +1006,6 @@ static void ath9k_hw_init_qos(struct ath_hw *ah) REG_WRITE(ah, AR_TXOP_12_15, 0xFFFFFFFF); } -static void ath9k_hw_change_target_baud(struct ath_hw *ah, u32 freq, u32 baud) -{ - u32 lcr; - u32 baud_divider = freq * 1000 * 1000 / 16 / baud; - - lcr = REG_READ(ah , 0x5100c); - lcr |= 0x80; - - REG_WRITE(ah, 0x5100c, lcr); - REG_WRITE(ah, 0x51004, (baud_divider >> 8)); - REG_WRITE(ah, 0x51000, (baud_divider & 0xff)); - - lcr &= ~0x80; - REG_WRITE(ah, 0x5100c, lcr); -} - static void ath9k_hw_init_pll(struct ath_hw *ah, struct ath9k_channel *chan) { @@ -1087,22 +1071,8 @@ static void ath9k_hw_init_pll(struct ath_hw *ah, /* Switch the core clock for ar9271 to 117Mhz */ if (AR_SREV_9271(ah)) { - if ((pll == 0x142c) || (pll == 0x2850) ) { - udelay(500); - /* set CLKOBS to output AHB clock */ - REG_WRITE(ah, 0x7020, 0xe); - /* - * 0x304: 117Mhz, ahb_ratio: 1x1 - * 0x306: 40Mhz, ahb_ratio: 1x1 - */ - REG_WRITE(ah, 0x50040, 0x304); - /* - * makes adjustments for the baud dividor to keep the - * targetted baud rate based on the used core clock. - */ - ath9k_hw_change_target_baud(ah, AR9271_CORE_CLOCK, - AR9271_TARGET_BAUD_RATE); - } + udelay(500); + REG_WRITE(ah, 0x50040, 0x304); } udelay(RTC_PLL_SETTLE_DELAY); From 05020d236e91ddab1cb42483843a8995b3be1adf Mon Sep 17 00:00:00 2001 From: Sujith Date: Wed, 17 Mar 2010 14:25:23 +0530 Subject: [PATCH 20/60] ath9k_hw: add HTC init hardware call for special resets for AR9271 AR9271 needs a full reset only upon the first reset, add a call for the driver to enable these special resets. We can optimize this out later without an export. Signed-off-by: Sujith Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 12 ++++++++++++ drivers/net/wireless/ath/ath9k/hw.h | 3 +++ 2 files changed, 15 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 5bc5f5fdff57..05aeb511c686 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1951,6 +1951,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, ath9k_hw_mark_phy_inactive(ah); + /* Only required on the first reset */ if (AR_SREV_9271(ah) && ah->htc_reset_init) { REG_WRITE(ah, AR9271_RESET_POWER_DOWN_CONTROL, @@ -1963,6 +1964,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, return -EINVAL; } + /* Only required on the first reset */ if (AR_SREV_9271(ah) && ah->htc_reset_init) { ah->htc_reset_init = false; REG_WRITE(ah, @@ -3857,6 +3859,16 @@ void ath_gen_timer_isr(struct ath_hw *ah) } EXPORT_SYMBOL(ath_gen_timer_isr); +/********/ +/* HTC */ +/********/ + +void ath9k_hw_htc_resetinit(struct ath_hw *ah) +{ + ah->htc_reset_init = true; +} +EXPORT_SYMBOL(ath9k_hw_htc_resetinit); + static struct { u32 version; const char * name; diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index e63edfb91590..6b03e1688b22 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -707,6 +707,9 @@ u32 ath9k_hw_gettsf32(struct ath_hw *ah); void ath9k_hw_name(struct ath_hw *ah, char *hw_name, size_t len); +/* HTC */ +void ath9k_hw_htc_resetinit(struct ath_hw *ah); + #define ATH_PCIE_CAP_LINK_CTRL 0x70 #define ATH_PCIE_CAP_LINK_L0S 1 #define ATH_PCIE_CAP_LINK_L1 2 From 736b3a27b3c50c4a23717b802240435a69e8d0ff Mon Sep 17 00:00:00 2001 From: Sujith Date: Wed, 17 Mar 2010 14:25:24 +0530 Subject: [PATCH 21/60] ath9k_hw: fix hardware deinit Without this you will get a panic if the device initialization fails. Also, free ath_hw instance properly. ath9k_hw_deinit() shouldn't do it. Cc: stable@kernel.org Signed-off-by: Sujith Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 4 +--- drivers/net/wireless/ath/ath9k/init.c | 3 +++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 05aeb511c686..77db932c3137 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1227,7 +1227,7 @@ void ath9k_hw_deinit(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); - if (common->state <= ATH_HW_INITIALIZED) + if (common->state < ATH_HW_INITIALIZED) goto free_hw; if (!AR_SREV_9100(ah)) @@ -1238,8 +1238,6 @@ void ath9k_hw_deinit(struct ath_hw *ah) free_hw: if (!AR_SREV_9280_10_OR_LATER(ah)) ath9k_hw_rf_free_ext_banks(ah); - kfree(ah); - ah = NULL; } EXPORT_SYMBOL(ath9k_hw_deinit); diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 623c2f884987..6063f5463708 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -758,6 +758,9 @@ static void ath9k_deinit_softc(struct ath_softc *sc) tasklet_kill(&sc->intr_tq); tasklet_kill(&sc->bcon_tasklet); + + kfree(sc->sc_ah); + sc->sc_ah = NULL; } void ath9k_deinit_device(struct ath_softc *sc) From fb9987d0f748c983bb795a86f47522313f701a08 Mon Sep 17 00:00:00 2001 From: Sujith Date: Wed, 17 Mar 2010 14:25:25 +0530 Subject: [PATCH 22/60] ath9k_htc: Support for AR9271 chipset. Features: * Station mode * IBSS mode * Monitor mode * Legacy support * HT support * TX/RX 11n Aggregation * HW encryption * LED * Suspend/Resume For more information: http://wireless.kernel.org/en/users/Drivers/ath9k_htc Signed-off-by: Sujith Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Senthil Balasubramanian Signed-off-by: John W. Linville --- drivers/net/wireless/ath/Kconfig | 2 +- drivers/net/wireless/ath/ath9k/Kconfig | 21 + drivers/net/wireless/ath/ath9k/Makefile | 10 + drivers/net/wireless/ath/ath9k/common.c | 421 +++++ drivers/net/wireless/ath/ath9k/common.h | 17 + drivers/net/wireless/ath/ath9k/hif_usb.c | 993 ++++++++++ drivers/net/wireless/ath/ath9k/hif_usb.h | 105 ++ drivers/net/wireless/ath/ath9k/htc.h | 441 +++++ .../net/wireless/ath/ath9k/htc_drv_beacon.c | 260 +++ drivers/net/wireless/ath/ath9k/htc_drv_init.c | 713 ++++++++ drivers/net/wireless/ath/ath9k/htc_drv_main.c | 1626 +++++++++++++++++ drivers/net/wireless/ath/ath9k/htc_drv_txrx.c | 604 ++++++ drivers/net/wireless/ath/ath9k/htc_hst.c | 463 +++++ drivers/net/wireless/ath/ath9k/htc_hst.h | 246 +++ drivers/net/wireless/ath/ath9k/mac.h | 26 + drivers/net/wireless/ath/ath9k/wmi.c | 319 ++++ drivers/net/wireless/ath/ath9k/wmi.h | 126 ++ drivers/net/wireless/ath/debug.h | 1 + 18 files changed, 6393 insertions(+), 1 deletion(-) create mode 100644 drivers/net/wireless/ath/ath9k/hif_usb.c create mode 100644 drivers/net/wireless/ath/ath9k/hif_usb.h create mode 100644 drivers/net/wireless/ath/ath9k/htc.h create mode 100644 drivers/net/wireless/ath/ath9k/htc_drv_beacon.c create mode 100644 drivers/net/wireless/ath/ath9k/htc_drv_init.c create mode 100644 drivers/net/wireless/ath/ath9k/htc_drv_main.c create mode 100644 drivers/net/wireless/ath/ath9k/htc_drv_txrx.c create mode 100644 drivers/net/wireless/ath/ath9k/htc_hst.c create mode 100644 drivers/net/wireless/ath/ath9k/htc_hst.h create mode 100644 drivers/net/wireless/ath/ath9k/wmi.c create mode 100644 drivers/net/wireless/ath/ath9k/wmi.h diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index 4e7a7fd695c8..0a75be027afa 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -3,7 +3,7 @@ menuconfig ATH_COMMON depends on CFG80211 ---help--- This will enable the support for the Atheros wireless drivers. - ath5k, ath9k and ar9170 drivers share some common code, this option + ath5k, ath9k, ath9k_htc and ar9170 drivers share some common code, this option enables the common ath.ko module which shares common helpers. For more information and documentation on this module you can visit: diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index 5774cea23a3b..35f23bdc442f 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -32,3 +32,24 @@ config ATH9K_DEBUGFS Also required for changing debug message flags at run time. +config ATH9K_HTC + tristate "Atheros HTC based wireless cards support" + depends on USB && MAC80211 + select ATH9K_HW + select MAC80211_LEDS + select LEDS_CLASS + select NEW_LEDS + select ATH9K_COMMON + ---help--- + Support for Atheros HTC based cards. + Chipsets supported: AR9271 + + For more information: http://wireless.kernel.org/en/users/Drivers/ath9k_htc + + The built module will be ath9k_htc. + +config ATH9K_HTC_DEBUGFS + bool "Atheros ath9k_htc debugging" + depends on ATH9K_HTC && DEBUG_FS + ---help--- + Say Y, if you need access to ath9k_htc's statistics. diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 6b50d5eb9ec3..97133beda269 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -28,3 +28,13 @@ obj-$(CONFIG_ATH9K_HW) += ath9k_hw.o obj-$(CONFIG_ATH9K_COMMON) += ath9k_common.o ath9k_common-y:= common.o + +ath9k_htc-y += htc_hst.o \ + hif_usb.o \ + wmi.o \ + htc_drv_txrx.o \ + htc_drv_main.o \ + htc_drv_beacon.o \ + htc_drv_init.o + +obj-$(CONFIG_ATH9K_HTC) += ath9k_htc.o diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c index 4d775ae141db..7902d287f671 100644 --- a/drivers/net/wireless/ath/ath9k/common.c +++ b/drivers/net/wireless/ath/ath9k/common.c @@ -286,6 +286,427 @@ int ath9k_cmn_padpos(__le16 frame_control) } EXPORT_SYMBOL(ath9k_cmn_padpos); +int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb) +{ + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + + if (tx_info->control.hw_key) { + if (tx_info->control.hw_key->alg == ALG_WEP) + return ATH9K_KEY_TYPE_WEP; + else if (tx_info->control.hw_key->alg == ALG_TKIP) + return ATH9K_KEY_TYPE_TKIP; + else if (tx_info->control.hw_key->alg == ALG_CCMP) + return ATH9K_KEY_TYPE_AES; + } + + return ATH9K_KEY_TYPE_CLEAR; +} +EXPORT_SYMBOL(ath9k_cmn_get_hw_crypto_keytype); + +/* + * Calculate the RX filter to be set in the HW. + */ +u32 ath9k_cmn_calcrxfilter(struct ieee80211_hw *hw, struct ath_hw *ah, + unsigned int rxfilter) +{ +#define RX_FILTER_PRESERVE (ATH9K_RX_FILTER_PHYERR | ATH9K_RX_FILTER_PHYRADAR) + + u32 rfilt; + + rfilt = (ath9k_hw_getrxfilter(ah) & RX_FILTER_PRESERVE) + | ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST + | ATH9K_RX_FILTER_MCAST; + + /* If not a STA, enable processing of Probe Requests */ + if (ah->opmode != NL80211_IFTYPE_STATION) + rfilt |= ATH9K_RX_FILTER_PROBEREQ; + + /* + * Set promiscuous mode when FIF_PROMISC_IN_BSS is enabled for station + * mode interface or when in monitor mode. AP mode does not need this + * since it receives all in-BSS frames anyway. + */ + if (((ah->opmode != NL80211_IFTYPE_AP) && + (rxfilter & FIF_PROMISC_IN_BSS)) || + (ah->opmode == NL80211_IFTYPE_MONITOR)) + rfilt |= ATH9K_RX_FILTER_PROM; + + if (rxfilter & FIF_CONTROL) + rfilt |= ATH9K_RX_FILTER_CONTROL; + + if ((ah->opmode == NL80211_IFTYPE_STATION) && + !(rxfilter & FIF_BCN_PRBRESP_PROMISC)) + rfilt |= ATH9K_RX_FILTER_MYBEACON; + else + rfilt |= ATH9K_RX_FILTER_BEACON; + + if ((AR_SREV_9280_10_OR_LATER(ah) || + AR_SREV_9285_10_OR_LATER(ah)) && + (ah->opmode == NL80211_IFTYPE_AP) && + (rxfilter & FIF_PSPOLL)) + rfilt |= ATH9K_RX_FILTER_PSPOLL; + + if (conf_is_ht(&hw->conf)) + rfilt |= ATH9K_RX_FILTER_COMP_BAR; + + return rfilt; + +#undef RX_FILTER_PRESERVE +} +EXPORT_SYMBOL(ath9k_cmn_calcrxfilter); + +/* + * Recv initialization for opmode change. + */ +void ath9k_cmn_opmode_init(struct ieee80211_hw *hw, struct ath_hw *ah, + unsigned int rxfilter) +{ + struct ath_common *common = ath9k_hw_common(ah); + + u32 rfilt, mfilt[2]; + + /* configure rx filter */ + rfilt = ath9k_cmn_calcrxfilter(hw, ah, rxfilter); + ath9k_hw_setrxfilter(ah, rfilt); + + /* configure bssid mask */ + if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) + ath_hw_setbssidmask(common); + + /* configure operational mode */ + ath9k_hw_setopmode(ah); + + /* Handle any link-level address change. */ + ath9k_hw_setmac(ah, common->macaddr); + + /* calculate and install multicast filter */ + mfilt[0] = mfilt[1] = ~0; + ath9k_hw_setmcastfilter(ah, mfilt[0], mfilt[1]); +} +EXPORT_SYMBOL(ath9k_cmn_opmode_init); + +static u32 ath9k_get_extchanmode(struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) +{ + u32 chanmode = 0; + + switch (chan->band) { + case IEEE80211_BAND_2GHZ: + switch (channel_type) { + case NL80211_CHAN_NO_HT: + case NL80211_CHAN_HT20: + chanmode = CHANNEL_G_HT20; + break; + case NL80211_CHAN_HT40PLUS: + chanmode = CHANNEL_G_HT40PLUS; + break; + case NL80211_CHAN_HT40MINUS: + chanmode = CHANNEL_G_HT40MINUS; + break; + } + break; + case IEEE80211_BAND_5GHZ: + switch (channel_type) { + case NL80211_CHAN_NO_HT: + case NL80211_CHAN_HT20: + chanmode = CHANNEL_A_HT20; + break; + case NL80211_CHAN_HT40PLUS: + chanmode = CHANNEL_A_HT40PLUS; + break; + case NL80211_CHAN_HT40MINUS: + chanmode = CHANNEL_A_HT40MINUS; + break; + } + break; + default: + break; + } + + return chanmode; +} + +/* + * Update internal channel flags. + */ +void ath9k_cmn_update_ichannel(struct ieee80211_hw *hw, + struct ath9k_channel *ichan) +{ + struct ieee80211_channel *chan = hw->conf.channel; + struct ieee80211_conf *conf = &hw->conf; + + ichan->channel = chan->center_freq; + ichan->chan = chan; + + if (chan->band == IEEE80211_BAND_2GHZ) { + ichan->chanmode = CHANNEL_G; + ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM | CHANNEL_G; + } else { + ichan->chanmode = CHANNEL_A; + ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM; + } + + if (conf_is_ht(conf)) + ichan->chanmode = ath9k_get_extchanmode(chan, + conf->channel_type); +} +EXPORT_SYMBOL(ath9k_cmn_update_ichannel); + +/* + * Get the internal channel reference. + */ +struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw, + struct ath_hw *ah) +{ + struct ieee80211_channel *curchan = hw->conf.channel; + struct ath9k_channel *channel; + u8 chan_idx; + + chan_idx = curchan->hw_value; + channel = &ah->channels[chan_idx]; + ath9k_cmn_update_ichannel(hw, channel); + + return channel; +} +EXPORT_SYMBOL(ath9k_cmn_get_curchannel); + +static int ath_setkey_tkip(struct ath_common *common, u16 keyix, const u8 *key, + struct ath9k_keyval *hk, const u8 *addr, + bool authenticator) +{ + struct ath_hw *ah = common->ah; + const u8 *key_rxmic; + const u8 *key_txmic; + + key_txmic = key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY; + key_rxmic = key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY; + + if (addr == NULL) { + /* + * Group key installation - only two key cache entries are used + * regardless of splitmic capability since group key is only + * used either for TX or RX. + */ + if (authenticator) { + memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic)); + memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_mic)); + } else { + memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); + memcpy(hk->kv_txmic, key_rxmic, sizeof(hk->kv_mic)); + } + return ath9k_hw_set_keycache_entry(ah, keyix, hk, addr); + } + if (!common->splitmic) { + /* TX and RX keys share the same key cache entry. */ + memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); + memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic)); + return ath9k_hw_set_keycache_entry(ah, keyix, hk, addr); + } + + /* Separate key cache entries for TX and RX */ + + /* TX key goes at first index, RX key at +32. */ + memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic)); + if (!ath9k_hw_set_keycache_entry(ah, keyix, hk, NULL)) { + /* TX MIC entry failed. No need to proceed further */ + ath_print(common, ATH_DBG_FATAL, + "Setting TX MIC Key Failed\n"); + return 0; + } + + memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); + /* XXX delete tx key on failure? */ + return ath9k_hw_set_keycache_entry(ah, keyix + 32, hk, addr); +} + +static int ath_reserve_key_cache_slot_tkip(struct ath_common *common) +{ + int i; + + for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) { + if (test_bit(i, common->keymap) || + test_bit(i + 64, common->keymap)) + continue; /* At least one part of TKIP key allocated */ + if (common->splitmic && + (test_bit(i + 32, common->keymap) || + test_bit(i + 64 + 32, common->keymap))) + continue; /* At least one part of TKIP key allocated */ + + /* Found a free slot for a TKIP key */ + return i; + } + return -1; +} + +static int ath_reserve_key_cache_slot(struct ath_common *common) +{ + int i; + + /* First, try to find slots that would not be available for TKIP. */ + if (common->splitmic) { + for (i = IEEE80211_WEP_NKID; i < common->keymax / 4; i++) { + if (!test_bit(i, common->keymap) && + (test_bit(i + 32, common->keymap) || + test_bit(i + 64, common->keymap) || + test_bit(i + 64 + 32, common->keymap))) + return i; + if (!test_bit(i + 32, common->keymap) && + (test_bit(i, common->keymap) || + test_bit(i + 64, common->keymap) || + test_bit(i + 64 + 32, common->keymap))) + return i + 32; + if (!test_bit(i + 64, common->keymap) && + (test_bit(i , common->keymap) || + test_bit(i + 32, common->keymap) || + test_bit(i + 64 + 32, common->keymap))) + return i + 64; + if (!test_bit(i + 64 + 32, common->keymap) && + (test_bit(i, common->keymap) || + test_bit(i + 32, common->keymap) || + test_bit(i + 64, common->keymap))) + return i + 64 + 32; + } + } else { + for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) { + if (!test_bit(i, common->keymap) && + test_bit(i + 64, common->keymap)) + return i; + if (test_bit(i, common->keymap) && + !test_bit(i + 64, common->keymap)) + return i + 64; + } + } + + /* No partially used TKIP slots, pick any available slot */ + for (i = IEEE80211_WEP_NKID; i < common->keymax; i++) { + /* Do not allow slots that could be needed for TKIP group keys + * to be used. This limitation could be removed if we know that + * TKIP will not be used. */ + if (i >= 64 && i < 64 + IEEE80211_WEP_NKID) + continue; + if (common->splitmic) { + if (i >= 32 && i < 32 + IEEE80211_WEP_NKID) + continue; + if (i >= 64 + 32 && i < 64 + 32 + IEEE80211_WEP_NKID) + continue; + } + + if (!test_bit(i, common->keymap)) + return i; /* Found a free slot for a key */ + } + + /* No free slot found */ + return -1; +} + +/* + * Configure encryption in the HW. + */ +int ath9k_cmn_key_config(struct ath_common *common, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct ath_hw *ah = common->ah; + struct ath9k_keyval hk; + const u8 *mac = NULL; + int ret = 0; + int idx; + + memset(&hk, 0, sizeof(hk)); + + switch (key->alg) { + case ALG_WEP: + hk.kv_type = ATH9K_CIPHER_WEP; + break; + case ALG_TKIP: + hk.kv_type = ATH9K_CIPHER_TKIP; + break; + case ALG_CCMP: + hk.kv_type = ATH9K_CIPHER_AES_CCM; + break; + default: + return -EOPNOTSUPP; + } + + hk.kv_len = key->keylen; + memcpy(hk.kv_val, key->key, key->keylen); + + if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { + /* For now, use the default keys for broadcast keys. This may + * need to change with virtual interfaces. */ + idx = key->keyidx; + } else if (key->keyidx) { + if (WARN_ON(!sta)) + return -EOPNOTSUPP; + mac = sta->addr; + + if (vif->type != NL80211_IFTYPE_AP) { + /* Only keyidx 0 should be used with unicast key, but + * allow this for client mode for now. */ + idx = key->keyidx; + } else + return -EIO; + } else { + if (WARN_ON(!sta)) + return -EOPNOTSUPP; + mac = sta->addr; + + if (key->alg == ALG_TKIP) + idx = ath_reserve_key_cache_slot_tkip(common); + else + idx = ath_reserve_key_cache_slot(common); + if (idx < 0) + return -ENOSPC; /* no free key cache entries */ + } + + if (key->alg == ALG_TKIP) + ret = ath_setkey_tkip(common, idx, key->key, &hk, mac, + vif->type == NL80211_IFTYPE_AP); + else + ret = ath9k_hw_set_keycache_entry(ah, idx, &hk, mac); + + if (!ret) + return -EIO; + + set_bit(idx, common->keymap); + if (key->alg == ALG_TKIP) { + set_bit(idx + 64, common->keymap); + if (common->splitmic) { + set_bit(idx + 32, common->keymap); + set_bit(idx + 64 + 32, common->keymap); + } + } + + return idx; +} +EXPORT_SYMBOL(ath9k_cmn_key_config); + +/* + * Delete Key. + */ +void ath9k_cmn_key_delete(struct ath_common *common, + struct ieee80211_key_conf *key) +{ + struct ath_hw *ah = common->ah; + + ath9k_hw_keyreset(ah, key->hw_key_idx); + if (key->hw_key_idx < IEEE80211_WEP_NKID) + return; + + clear_bit(key->hw_key_idx, common->keymap); + if (key->alg != ALG_TKIP) + return; + + clear_bit(key->hw_key_idx + 64, common->keymap); + if (common->splitmic) { + ath9k_hw_keyreset(ah, key->hw_key_idx + 32); + clear_bit(key->hw_key_idx + 32, common->keymap); + clear_bit(key->hw_key_idx + 64 + 32, common->keymap); + } +} +EXPORT_SYMBOL(ath9k_cmn_key_delete); + static int __init ath9k_cmn_init(void) { return 0; diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h index 042999c2fe9c..bbcc57f6eba3 100644 --- a/drivers/net/wireless/ath/ath9k/common.h +++ b/drivers/net/wireless/ath/ath9k/common.h @@ -23,6 +23,8 @@ /* Common header for Atheros 802.11n base driver cores */ +#define IEEE80211_WEP_NKID 4 + #define WME_NUM_TID 16 #define WME_BA_BMP_SIZE 64 #define WME_MAX_BA WME_BA_BMP_SIZE @@ -125,3 +127,18 @@ void ath9k_cmn_rx_skb_postprocess(struct ath_common *common, bool decrypt_error); int ath9k_cmn_padpos(__le16 frame_control); +int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb); +u32 ath9k_cmn_calcrxfilter(struct ieee80211_hw *hw, struct ath_hw *ah, + unsigned int rxfilter); +void ath9k_cmn_opmode_init(struct ieee80211_hw *hw, struct ath_hw *ah, + unsigned int rxfilter); +void ath9k_cmn_update_ichannel(struct ieee80211_hw *hw, + struct ath9k_channel *ichan); +struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw, + struct ath_hw *ah); +int ath9k_cmn_key_config(struct ath_common *common, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); +void ath9k_cmn_key_delete(struct ath_common *common, + struct ieee80211_key_conf *key); diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c new file mode 100644 index 000000000000..fc4f6e8c9ef3 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -0,0 +1,993 @@ +/* + * Copyright (c) 2010 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "htc.h" + +#define ATH9K_FW_USB_DEV(devid, fw) \ + { USB_DEVICE(0x0cf3, devid), .driver_info = (unsigned long) fw } + +static struct usb_device_id ath9k_hif_usb_ids[] = { + ATH9K_FW_USB_DEV(0x9271, "ar9271.fw"), + { }, +}; + +MODULE_DEVICE_TABLE(usb, ath9k_hif_usb_ids); + +static int __hif_usb_tx(struct hif_device_usb *hif_dev); + +static void hif_usb_regout_cb(struct urb *urb) +{ + struct cmd_buf *cmd = (struct cmd_buf *)urb->context; + struct hif_device_usb *hif_dev = cmd->hif_dev; + + if (!hif_dev) { + usb_free_urb(urb); + if (cmd) { + if (cmd->skb) + dev_kfree_skb_any(cmd->skb); + kfree(cmd); + } + return; + } + + switch (urb->status) { + case 0: + break; + case -ENOENT: + case -ECONNRESET: + break; + case -ENODEV: + case -ESHUTDOWN: + return; + default: + break; + } + + if (cmd) { + ath9k_htc_txcompletion_cb(cmd->hif_dev->htc_handle, + cmd->skb, 1); + kfree(cmd); + usb_free_urb(urb); + } +} + +static int hif_usb_send_regout(struct hif_device_usb *hif_dev, + struct sk_buff *skb) +{ + struct urb *urb; + struct cmd_buf *cmd; + int ret = 0; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (urb == NULL) + return -ENOMEM; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) { + usb_free_urb(urb); + return -ENOMEM; + } + + cmd->skb = skb; + cmd->hif_dev = hif_dev; + + usb_fill_int_urb(urb, hif_dev->udev, + usb_sndintpipe(hif_dev->udev, USB_REG_OUT_PIPE), + skb->data, skb->len, + hif_usb_regout_cb, cmd, 1); + + ret = usb_submit_urb(urb, GFP_KERNEL); + if (ret) { + usb_free_urb(urb); + kfree(cmd); + } + + return ret; +} + +static void hif_usb_tx_cb(struct urb *urb) +{ + struct tx_buf *tx_buf = (struct tx_buf *) urb->context; + struct hif_device_usb *hif_dev = tx_buf->hif_dev; + struct sk_buff *skb; + bool drop, flush; + + if (!hif_dev) + return; + + switch (urb->status) { + case 0: + break; + case -ENOENT: + case -ECONNRESET: + break; + case -ENODEV: + case -ESHUTDOWN: + return; + default: + break; + } + + if (tx_buf) { + spin_lock(&hif_dev->tx.tx_lock); + drop = !!(hif_dev->tx.flags & HIF_USB_TX_STOP); + flush = !!(hif_dev->tx.flags & HIF_USB_TX_FLUSH); + spin_unlock(&hif_dev->tx.tx_lock); + + while ((skb = __skb_dequeue(&tx_buf->skb_queue)) != NULL) { + if (!drop && !flush) { + ath9k_htc_txcompletion_cb(hif_dev->htc_handle, + skb, 1); + TX_STAT_INC(skb_completed); + } else { + dev_kfree_skb_any(skb); + } + } + + if (flush) + return; + + tx_buf->len = tx_buf->offset = 0; + __skb_queue_head_init(&tx_buf->skb_queue); + + spin_lock(&hif_dev->tx.tx_lock); + list_del(&tx_buf->list); + list_add_tail(&tx_buf->list, &hif_dev->tx.tx_buf); + hif_dev->tx.tx_buf_cnt++; + if (!drop) + __hif_usb_tx(hif_dev); /* Check for pending SKBs */ + TX_STAT_INC(buf_completed); + spin_unlock(&hif_dev->tx.tx_lock); + } +} + +/* TX lock has to be taken */ +static int __hif_usb_tx(struct hif_device_usb *hif_dev) +{ + struct tx_buf *tx_buf = NULL; + struct sk_buff *nskb = NULL; + int ret = 0, i; + u16 *hdr, tx_skb_cnt = 0; + u8 *buf; + + if (hif_dev->tx.tx_skb_cnt == 0) + return 0; + + /* Check if a free TX buffer is available */ + if (list_empty(&hif_dev->tx.tx_buf)) + return 0; + + tx_buf = list_first_entry(&hif_dev->tx.tx_buf, struct tx_buf, list); + list_del(&tx_buf->list); + list_add_tail(&tx_buf->list, &hif_dev->tx.tx_pending); + hif_dev->tx.tx_buf_cnt--; + + tx_skb_cnt = min_t(u16, hif_dev->tx.tx_skb_cnt, MAX_TX_AGGR_NUM); + + for (i = 0; i < tx_skb_cnt; i++) { + nskb = __skb_dequeue(&hif_dev->tx.tx_skb_queue); + + /* Should never be NULL */ + BUG_ON(!nskb); + + hif_dev->tx.tx_skb_cnt--; + + buf = tx_buf->buf; + buf += tx_buf->offset; + hdr = (u16 *)buf; + *hdr++ = nskb->len; + *hdr++ = ATH_USB_TX_STREAM_MODE_TAG; + buf += 4; + memcpy(buf, nskb->data, nskb->len); + tx_buf->len = nskb->len + 4; + + if (i < (tx_skb_cnt - 1)) + tx_buf->offset += (((tx_buf->len - 1) / 4) + 1) * 4; + + if (i == (tx_skb_cnt - 1)) + tx_buf->len += tx_buf->offset; + + __skb_queue_tail(&tx_buf->skb_queue, nskb); + TX_STAT_INC(skb_queued); + } + + usb_fill_bulk_urb(tx_buf->urb, hif_dev->udev, + usb_sndbulkpipe(hif_dev->udev, USB_WLAN_TX_PIPE), + tx_buf->buf, tx_buf->len, + hif_usb_tx_cb, tx_buf); + + ret = usb_submit_urb(tx_buf->urb, GFP_ATOMIC); + if (ret) { + tx_buf->len = tx_buf->offset = 0; + __skb_queue_purge(&tx_buf->skb_queue); + __skb_queue_head_init(&tx_buf->skb_queue); + list_move_tail(&tx_buf->list, &hif_dev->tx.tx_buf); + hif_dev->tx.tx_buf_cnt++; + } + + if (!ret) + TX_STAT_INC(buf_queued); + + return ret; +} + +static int hif_usb_send_tx(struct hif_device_usb *hif_dev, struct sk_buff *skb, + struct ath9k_htc_tx_ctl *tx_ctl) +{ + unsigned long flags; + + spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); + + if (hif_dev->tx.flags & HIF_USB_TX_STOP) { + spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); + return -ENODEV; + } + + /* Check if the max queue count has been reached */ + if (hif_dev->tx.tx_skb_cnt > MAX_TX_BUF_NUM) { + spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); + return -ENOMEM; + } + + __skb_queue_tail(&hif_dev->tx.tx_skb_queue, skb); + hif_dev->tx.tx_skb_cnt++; + + /* Send normal frames immediately */ + if (!tx_ctl || (tx_ctl && (tx_ctl->type == ATH9K_HTC_NORMAL))) + __hif_usb_tx(hif_dev); + + /* Check if AMPDUs have to be sent immediately */ + if (tx_ctl && (tx_ctl->type == ATH9K_HTC_AMPDU) && + (hif_dev->tx.tx_buf_cnt == MAX_TX_URB_NUM) && + (hif_dev->tx.tx_skb_cnt < 2)) { + __hif_usb_tx(hif_dev); + } + + spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); + + return 0; +} + +static void hif_usb_start(void *hif_handle, u8 pipe_id) +{ + struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle; + unsigned long flags; + + hif_dev->flags |= HIF_USB_START; + + spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); + hif_dev->tx.flags &= ~HIF_USB_TX_STOP; + spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); +} + +static void hif_usb_stop(void *hif_handle, u8 pipe_id) +{ + struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle; + unsigned long flags; + + spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); + __skb_queue_purge(&hif_dev->tx.tx_skb_queue); + hif_dev->tx.tx_skb_cnt = 0; + hif_dev->tx.flags |= HIF_USB_TX_STOP; + spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); +} + +static int hif_usb_send(void *hif_handle, u8 pipe_id, struct sk_buff *skb, + struct ath9k_htc_tx_ctl *tx_ctl) +{ + struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle; + int ret = 0; + + switch (pipe_id) { + case USB_WLAN_TX_PIPE: + ret = hif_usb_send_tx(hif_dev, skb, tx_ctl); + break; + case USB_REG_OUT_PIPE: + ret = hif_usb_send_regout(hif_dev, skb); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static struct ath9k_htc_hif hif_usb = { + .transport = ATH9K_HIF_USB, + .name = "ath9k_hif_usb", + + .control_ul_pipe = USB_REG_OUT_PIPE, + .control_dl_pipe = USB_REG_IN_PIPE, + + .start = hif_usb_start, + .stop = hif_usb_stop, + .send = hif_usb_send, +}; + +static void ath9k_hif_usb_rx_stream(struct hif_device_usb *hif_dev, + struct sk_buff *skb) +{ + struct sk_buff *nskb, *skb_pool[8]; + int index = 0, i = 0, chk_idx, len = skb->len; + int rx_remain_len = 0, rx_pkt_len = 0; + u16 pkt_len, pkt_tag, pool_index = 0; + u8 *ptr; + + rx_remain_len = hif_dev->rx_remain_len; + rx_pkt_len = hif_dev->rx_transfer_len; + + if (rx_remain_len != 0) { + struct sk_buff *remain_skb = hif_dev->remain_skb; + + if (remain_skb) { + ptr = (u8 *) remain_skb->data; + + index = rx_remain_len; + rx_remain_len -= hif_dev->rx_pad_len; + ptr += rx_pkt_len; + + memcpy(ptr, skb->data, rx_remain_len); + + rx_pkt_len += rx_remain_len; + hif_dev->rx_remain_len = 0; + skb_put(remain_skb, rx_pkt_len); + + skb_pool[pool_index++] = remain_skb; + + } else { + index = rx_remain_len; + } + } + + while (index < len) { + ptr = (u8 *) skb->data; + + pkt_len = ptr[index] + (ptr[index+1] << 8); + pkt_tag = ptr[index+2] + (ptr[index+3] << 8); + + if (pkt_tag == ATH_USB_RX_STREAM_MODE_TAG) { + u16 pad_len; + + pad_len = 4 - (pkt_len & 0x3); + if (pad_len == 4) + pad_len = 0; + + chk_idx = index; + index = index + 4 + pkt_len + pad_len; + + if (index > MAX_RX_BUF_SIZE) { + hif_dev->rx_remain_len = index - MAX_RX_BUF_SIZE; + hif_dev->rx_transfer_len = + MAX_RX_BUF_SIZE - chk_idx - 4; + hif_dev->rx_pad_len = pad_len; + + nskb = __dev_alloc_skb(pkt_len + 32, + GFP_ATOMIC); + if (!nskb) { + dev_err(&hif_dev->udev->dev, + "ath9k_htc: RX memory allocation" + " error\n"); + goto err; + } + skb_reserve(nskb, 32); + RX_STAT_INC(skb_allocated); + + memcpy(nskb->data, &(skb->data[chk_idx+4]), + hif_dev->rx_transfer_len); + + /* Record the buffer pointer */ + hif_dev->remain_skb = nskb; + } else { + nskb = __dev_alloc_skb(pkt_len + 32, GFP_ATOMIC); + if (!nskb) { + dev_err(&hif_dev->udev->dev, + "ath9k_htc: RX memory allocation" + " error\n"); + goto err; + } + skb_reserve(nskb, 32); + RX_STAT_INC(skb_allocated); + + memcpy(nskb->data, &(skb->data[chk_idx+4]), pkt_len); + skb_put(nskb, pkt_len); + skb_pool[pool_index++] = nskb; + } + } else { + RX_STAT_INC(skb_dropped); + dev_kfree_skb_any(skb); + return; + } + } + +err: + dev_kfree_skb_any(skb); + + for (i = 0; i < pool_index; i++) { + ath9k_htc_rx_msg(hif_dev->htc_handle, skb_pool[i], + skb_pool[i]->len, USB_WLAN_RX_PIPE); + RX_STAT_INC(skb_completed); + } +} + +static void ath9k_hif_usb_rx_cb(struct urb *urb) +{ + struct sk_buff *skb = (struct sk_buff *) urb->context; + struct sk_buff *nskb; + struct hif_device_usb *hif_dev = (struct hif_device_usb *) + usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)); + int ret; + + if (!hif_dev) + goto free; + + switch (urb->status) { + case 0: + break; + case -ENOENT: + case -ECONNRESET: + case -ENODEV: + case -ESHUTDOWN: + goto free; + default: + goto resubmit; + } + + if (likely(urb->actual_length != 0)) { + skb_put(skb, urb->actual_length); + + nskb = __dev_alloc_skb(MAX_RX_BUF_SIZE, GFP_ATOMIC); + if (!nskb) + goto resubmit; + + usb_fill_bulk_urb(urb, hif_dev->udev, + usb_rcvbulkpipe(hif_dev->udev, + USB_WLAN_RX_PIPE), + nskb->data, MAX_RX_BUF_SIZE, + ath9k_hif_usb_rx_cb, nskb); + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) { + dev_kfree_skb_any(nskb); + goto free; + } + + ath9k_hif_usb_rx_stream(hif_dev, skb); + return; + } + +resubmit: + skb_reset_tail_pointer(skb); + skb_trim(skb, 0); + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) + goto free; + + return; +free: + dev_kfree_skb_any(skb); +} + +static void ath9k_hif_usb_reg_in_cb(struct urb *urb) +{ + struct sk_buff *skb = (struct sk_buff *) urb->context; + struct sk_buff *nskb; + struct hif_device_usb *hif_dev = (struct hif_device_usb *) + usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)); + int ret; + + if (!hif_dev) + goto free; + + switch (urb->status) { + case 0: + break; + case -ENOENT: + case -ECONNRESET: + case -ENODEV: + case -ESHUTDOWN: + goto free; + default: + goto resubmit; + } + + if (likely(urb->actual_length != 0)) { + skb_put(skb, urb->actual_length); + + nskb = __dev_alloc_skb(MAX_REG_IN_BUF_SIZE, GFP_ATOMIC); + if (!nskb) + goto resubmit; + + usb_fill_int_urb(urb, hif_dev->udev, + usb_rcvintpipe(hif_dev->udev, USB_REG_IN_PIPE), + nskb->data, MAX_REG_IN_BUF_SIZE, + ath9k_hif_usb_reg_in_cb, nskb, 1); + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) { + dev_kfree_skb_any(nskb); + goto free; + } + + ath9k_htc_rx_msg(hif_dev->htc_handle, skb, + skb->len, USB_REG_IN_PIPE); + + return; + } + +resubmit: + skb_reset_tail_pointer(skb); + skb_trim(skb, 0); + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) + goto free; + + return; +free: + dev_kfree_skb_any(skb); +} + +static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev) +{ + unsigned long flags; + struct tx_buf *tx_buf = NULL, *tx_buf_tmp = NULL; + + list_for_each_entry_safe(tx_buf, tx_buf_tmp, &hif_dev->tx.tx_buf, list) { + list_del(&tx_buf->list); + usb_free_urb(tx_buf->urb); + kfree(tx_buf->buf); + kfree(tx_buf); + } + + spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); + hif_dev->tx.flags |= HIF_USB_TX_FLUSH; + spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); + + list_for_each_entry_safe(tx_buf, tx_buf_tmp, + &hif_dev->tx.tx_pending, list) { + usb_kill_urb(tx_buf->urb); + list_del(&tx_buf->list); + usb_free_urb(tx_buf->urb); + kfree(tx_buf->buf); + kfree(tx_buf); + } + + spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); + hif_dev->tx.flags &= ~HIF_USB_TX_FLUSH; + spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); +} + +static int ath9k_hif_usb_alloc_tx_urbs(struct hif_device_usb *hif_dev) +{ + struct tx_buf *tx_buf; + int i; + + INIT_LIST_HEAD(&hif_dev->tx.tx_buf); + INIT_LIST_HEAD(&hif_dev->tx.tx_pending); + spin_lock_init(&hif_dev->tx.tx_lock); + __skb_queue_head_init(&hif_dev->tx.tx_skb_queue); + + for (i = 0; i < MAX_TX_URB_NUM; i++) { + tx_buf = kzalloc(sizeof(struct tx_buf), GFP_KERNEL); + if (!tx_buf) + goto err; + + tx_buf->buf = kzalloc(MAX_TX_BUF_SIZE, GFP_KERNEL); + if (!tx_buf->buf) + goto err; + + tx_buf->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!tx_buf->urb) + goto err; + + tx_buf->hif_dev = hif_dev; + __skb_queue_head_init(&tx_buf->skb_queue); + + list_add_tail(&tx_buf->list, &hif_dev->tx.tx_buf); + } + + hif_dev->tx.tx_buf_cnt = MAX_TX_URB_NUM; + + return 0; +err: + ath9k_hif_usb_dealloc_tx_urbs(hif_dev); + return -ENOMEM; +} + +static void ath9k_hif_usb_dealloc_rx_skbs(struct hif_device_usb *hif_dev) +{ + int i; + + for (i = 0; i < MAX_RX_URB_NUM; i++) { + if (hif_dev->wlan_rx_data_urb[i]) { + if (hif_dev->wlan_rx_data_urb[i]->transfer_buffer) + dev_kfree_skb_any((void *) + hif_dev->wlan_rx_data_urb[i]->context); + } + } +} + +static void ath9k_hif_usb_dealloc_rx_urbs(struct hif_device_usb *hif_dev) +{ + int i; + + for (i = 0; i < MAX_RX_URB_NUM; i++) { + if (hif_dev->wlan_rx_data_urb[i]) { + usb_kill_urb(hif_dev->wlan_rx_data_urb[i]); + usb_free_urb(hif_dev->wlan_rx_data_urb[i]); + hif_dev->wlan_rx_data_urb[i] = NULL; + } + } +} + +static int ath9k_hif_usb_prep_rx_urb(struct hif_device_usb *hif_dev, + struct urb *urb) +{ + struct sk_buff *skb; + + skb = __dev_alloc_skb(MAX_RX_BUF_SIZE, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + usb_fill_bulk_urb(urb, hif_dev->udev, + usb_rcvbulkpipe(hif_dev->udev, USB_WLAN_RX_PIPE), + skb->data, MAX_RX_BUF_SIZE, + ath9k_hif_usb_rx_cb, skb); + return 0; +} + +static int ath9k_hif_usb_alloc_rx_urbs(struct hif_device_usb *hif_dev) +{ + int i, ret; + + for (i = 0; i < MAX_RX_URB_NUM; i++) { + + /* Allocate URB */ + hif_dev->wlan_rx_data_urb[i] = usb_alloc_urb(0, GFP_KERNEL); + if (hif_dev->wlan_rx_data_urb[i] == NULL) { + ret = -ENOMEM; + goto err_rx_urb; + } + + /* Allocate buffer */ + ret = ath9k_hif_usb_prep_rx_urb(hif_dev, + hif_dev->wlan_rx_data_urb[i]); + if (ret) + goto err_rx_urb; + + /* Submit URB */ + ret = usb_submit_urb(hif_dev->wlan_rx_data_urb[i], GFP_KERNEL); + if (ret) + goto err_rx_urb; + + } + + return 0; + +err_rx_urb: + ath9k_hif_usb_dealloc_rx_skbs(hif_dev); + ath9k_hif_usb_dealloc_rx_urbs(hif_dev); + return ret; +} + +static void ath9k_hif_usb_dealloc_reg_in_urb(struct hif_device_usb *hif_dev) +{ + if (hif_dev->reg_in_urb) { + usb_kill_urb(hif_dev->reg_in_urb); + usb_free_urb(hif_dev->reg_in_urb); + hif_dev->reg_in_urb = NULL; + } +} + +static int ath9k_hif_usb_alloc_reg_in_urb(struct hif_device_usb *hif_dev) +{ + struct sk_buff *skb; + + hif_dev->reg_in_urb = usb_alloc_urb(0, GFP_KERNEL); + if (hif_dev->reg_in_urb == NULL) + return -ENOMEM; + + skb = __dev_alloc_skb(MAX_REG_IN_BUF_SIZE, GFP_KERNEL); + if (!skb) + goto err; + + usb_fill_int_urb(hif_dev->reg_in_urb, hif_dev->udev, + usb_rcvintpipe(hif_dev->udev, USB_REG_IN_PIPE), + skb->data, MAX_REG_IN_BUF_SIZE, + ath9k_hif_usb_reg_in_cb, skb, 1); + + if (usb_submit_urb(hif_dev->reg_in_urb, GFP_KERNEL) != 0) + goto err_skb; + + return 0; + +err_skb: + dev_kfree_skb_any(skb); +err: + ath9k_hif_usb_dealloc_reg_in_urb(hif_dev); + return -ENOMEM; +} + +static int ath9k_hif_usb_alloc_urbs(struct hif_device_usb *hif_dev) +{ + /* TX */ + if (ath9k_hif_usb_alloc_tx_urbs(hif_dev) < 0) + goto err; + + /* RX */ + if (ath9k_hif_usb_alloc_rx_urbs(hif_dev) < 0) + goto err; + + /* Register Read/Write */ + if (ath9k_hif_usb_alloc_reg_in_urb(hif_dev) < 0) + goto err; + + return 0; +err: + return -ENOMEM; +} + +static int ath9k_hif_usb_download_fw(struct hif_device_usb *hif_dev) +{ + int transfer, err; + const void *data = hif_dev->firmware->data; + size_t len = hif_dev->firmware->size; + u32 addr = AR9271_FIRMWARE; + u8 *buf = kzalloc(4096, GFP_KERNEL); + + if (!buf) + return -ENOMEM; + + while (len) { + transfer = min_t(int, len, 4096); + memcpy(buf, data, transfer); + + err = usb_control_msg(hif_dev->udev, + usb_sndctrlpipe(hif_dev->udev, 0), + FIRMWARE_DOWNLOAD, 0x40 | USB_DIR_OUT, + addr >> 8, 0, buf, transfer, HZ); + if (err < 0) { + kfree(buf); + return err; + } + + len -= transfer; + data += transfer; + addr += transfer; + } + kfree(buf); + + /* + * Issue FW download complete command to firmware. + */ + err = usb_control_msg(hif_dev->udev, usb_sndctrlpipe(hif_dev->udev, 0), + FIRMWARE_DOWNLOAD_COMP, + 0x40 | USB_DIR_OUT, + AR9271_FIRMWARE_TEXT >> 8, 0, NULL, 0, HZ); + if (err) + return -EIO; + + dev_info(&hif_dev->udev->dev, "ath9k_htc: Transferred FW: %s, size: %ld\n", + "ar9271.fw", (unsigned long) hif_dev->firmware->size); + + return 0; +} + +static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev, + const char *fw_name) +{ + int ret; + + /* Request firmware */ + ret = request_firmware(&hif_dev->firmware, fw_name, &hif_dev->udev->dev); + if (ret) { + dev_err(&hif_dev->udev->dev, + "ath9k_htc: Firmware - %s not found\n", fw_name); + goto err_fw_req; + } + + /* Download firmware */ + ret = ath9k_hif_usb_download_fw(hif_dev); + if (ret) { + dev_err(&hif_dev->udev->dev, + "ath9k_htc: Firmware - %s download failed\n", fw_name); + goto err_fw_download; + } + + /* Alloc URBs */ + ret = ath9k_hif_usb_alloc_urbs(hif_dev); + if (ret) { + dev_err(&hif_dev->udev->dev, + "ath9k_htc: Unable to allocate URBs\n"); + goto err_urb; + } + + return 0; + +err_urb: + /* Nothing */ +err_fw_download: + release_firmware(hif_dev->firmware); +err_fw_req: + hif_dev->firmware = NULL; + return ret; +} + +static void ath9k_hif_usb_dealloc_urbs(struct hif_device_usb *hif_dev) +{ + ath9k_hif_usb_dealloc_reg_in_urb(hif_dev); + ath9k_hif_usb_dealloc_tx_urbs(hif_dev); + ath9k_hif_usb_dealloc_rx_urbs(hif_dev); +} + +static void ath9k_hif_usb_dev_deinit(struct hif_device_usb *hif_dev) +{ + ath9k_hif_usb_dealloc_urbs(hif_dev); + if (hif_dev->firmware) + release_firmware(hif_dev->firmware); +} + +static int ath9k_hif_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct hif_device_usb *hif_dev; + const char *fw_name = (const char *) id->driver_info; + int ret = 0; + + hif_dev = kzalloc(sizeof(struct hif_device_usb), GFP_KERNEL); + if (!hif_dev) { + ret = -ENOMEM; + goto err_alloc; + } + + usb_get_dev(udev); + hif_dev->udev = udev; + hif_dev->interface = interface; + hif_dev->device_id = id->idProduct; +#ifdef CONFIG_PM + udev->reset_resume = 1; +#endif + usb_set_intfdata(interface, hif_dev); + + ret = ath9k_hif_usb_dev_init(hif_dev, fw_name); + if (ret) { + ret = -EINVAL; + goto err_hif_init_usb; + } + + hif_dev->htc_handle = ath9k_htc_hw_alloc(hif_dev); + if (hif_dev->htc_handle == NULL) { + ret = -ENOMEM; + goto err_htc_hw_alloc; + } + + ret = ath9k_htc_hw_init(&hif_usb, hif_dev->htc_handle, hif_dev, + &hif_dev->udev->dev, hif_dev->device_id, + ATH9K_HIF_USB); + if (ret) { + ret = -EINVAL; + goto err_htc_hw_init; + } + + dev_info(&hif_dev->udev->dev, "ath9k_htc: USB layer initialized\n"); + + return 0; + +err_htc_hw_init: + ath9k_htc_hw_free(hif_dev->htc_handle); +err_htc_hw_alloc: + ath9k_hif_usb_dev_deinit(hif_dev); +err_hif_init_usb: + usb_set_intfdata(interface, NULL); + kfree(hif_dev); + usb_put_dev(udev); +err_alloc: + return ret; +} + +static void ath9k_hif_usb_disconnect(struct usb_interface *interface) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct hif_device_usb *hif_dev = + (struct hif_device_usb *) usb_get_intfdata(interface); + + if (hif_dev) { + ath9k_htc_hw_deinit(hif_dev->htc_handle, true); + ath9k_htc_hw_free(hif_dev->htc_handle); + ath9k_hif_usb_dev_deinit(hif_dev); + usb_set_intfdata(interface, NULL); + } + + if (hif_dev->flags & HIF_USB_START) + usb_reset_device(udev); + + kfree(hif_dev); + dev_info(&udev->dev, "ath9k_htc: USB layer deinitialized\n"); + usb_put_dev(udev); +} + +#ifdef CONFIG_PM +static int ath9k_hif_usb_suspend(struct usb_interface *interface, + pm_message_t message) +{ + struct hif_device_usb *hif_dev = + (struct hif_device_usb *) usb_get_intfdata(interface); + + ath9k_hif_usb_dealloc_urbs(hif_dev); + + return 0; +} + +static int ath9k_hif_usb_resume(struct usb_interface *interface) +{ + struct hif_device_usb *hif_dev = + (struct hif_device_usb *) usb_get_intfdata(interface); + int ret; + + ret = ath9k_hif_usb_alloc_urbs(hif_dev); + if (ret) + return ret; + + if (hif_dev->firmware) { + ret = ath9k_hif_usb_download_fw(hif_dev); + if (ret) + goto fail_resume; + } else { + ath9k_hif_usb_dealloc_urbs(hif_dev); + return -EIO; + } + + mdelay(100); + + ret = ath9k_htc_resume(hif_dev->htc_handle); + + if (ret) + goto fail_resume; + + return 0; + +fail_resume: + ath9k_hif_usb_dealloc_urbs(hif_dev); + + return ret; +} +#endif + +static struct usb_driver ath9k_hif_usb_driver = { + .name = "ath9k_hif_usb", + .probe = ath9k_hif_usb_probe, + .disconnect = ath9k_hif_usb_disconnect, +#ifdef CONFIG_PM + .suspend = ath9k_hif_usb_suspend, + .resume = ath9k_hif_usb_resume, + .reset_resume = ath9k_hif_usb_resume, +#endif + .id_table = ath9k_hif_usb_ids, + .soft_unbind = 1, +}; + +int ath9k_hif_usb_init(void) +{ + return usb_register(&ath9k_hif_usb_driver); +} + +void ath9k_hif_usb_exit(void) +{ + usb_deregister(&ath9k_hif_usb_driver); +} diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.h b/drivers/net/wireless/ath/ath9k/hif_usb.h new file mode 100644 index 000000000000..7cc3762a6789 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/hif_usb.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2010 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef HTC_USB_H +#define HTC_USB_H + +#define AR9271_FIRMWARE 0x501000 +#define AR9271_FIRMWARE_TEXT 0x903000 + +#define FIRMWARE_DOWNLOAD 0x30 +#define FIRMWARE_DOWNLOAD_COMP 0x31 + +#define ATH_USB_RX_STREAM_MODE_TAG 0x4e00 +#define ATH_USB_TX_STREAM_MODE_TAG 0x697e + +/* FIXME: Verify these numbers (with Windows) */ +#define MAX_TX_URB_NUM 8 +#define MAX_TX_BUF_NUM 1024 +#define MAX_TX_BUF_SIZE 32768 +#define MAX_TX_AGGR_NUM 20 + +#define MAX_RX_URB_NUM 8 +#define MAX_RX_BUF_SIZE 16384 + +#define MAX_REG_OUT_URB_NUM 1 +#define MAX_REG_OUT_BUF_NUM 8 + +#define MAX_REG_IN_BUF_SIZE 64 + +/* USB Endpoint definition */ +#define USB_WLAN_TX_PIPE 1 +#define USB_WLAN_RX_PIPE 2 +#define USB_REG_IN_PIPE 3 +#define USB_REG_OUT_PIPE 4 + +#define HIF_USB_MAX_RXPIPES 2 +#define HIF_USB_MAX_TXPIPES 4 + +struct tx_buf { + u8 *buf; + u16 len; + u16 offset; + struct urb *urb; + struct sk_buff_head skb_queue; + struct hif_device_usb *hif_dev; + struct list_head list; +}; + +#define HIF_USB_TX_STOP BIT(0) +#define HIF_USB_TX_FLUSH BIT(1) + +struct hif_usb_tx { + u8 flags; + u8 tx_buf_cnt; + u16 tx_skb_cnt; + struct sk_buff_head tx_skb_queue; + struct list_head tx_buf; + struct list_head tx_pending; + spinlock_t tx_lock; +}; + +struct cmd_buf { + struct sk_buff *skb; + struct hif_device_usb *hif_dev; +}; + +#define HIF_USB_START BIT(0) + +struct hif_device_usb { + u16 device_id; + struct usb_device *udev; + struct usb_interface *interface; + const struct firmware *firmware; + struct htc_target *htc_handle; + u8 flags; + + struct hif_usb_tx tx; + + struct urb *wlan_rx_data_urb[MAX_RX_URB_NUM]; + struct urb *reg_in_urb; + + struct sk_buff *remain_skb; + int rx_remain_len; + int rx_pkt_len; + int rx_transfer_len; + int rx_pad_len; +}; + +int ath9k_hif_usb_init(void); +void ath9k_hif_usb_exit(void); + +#endif /* HTC_USB_H */ diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h new file mode 100644 index 000000000000..ab09fe3416c7 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/htc.h @@ -0,0 +1,441 @@ +/* + * Copyright (c) 2010 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef HTC_H +#define HTC_H + +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "htc_hst.h" +#include "hif_usb.h" +#include "wmi.h" + +#define ATH_STA_SHORT_CALINTERVAL 1000 /* 1 second */ +#define ATH_ANI_POLLINTERVAL 100 /* 100 ms */ +#define ATH_LONG_CALINTERVAL 30000 /* 30 seconds */ +#define ATH_RESTART_CALINTERVAL 1200000 /* 20 minutes */ + +#define ATH_DEFAULT_BMISS_LIMIT 10 +#define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024) +#define TSF_TO_TU(_h, _l) \ + ((((u32)(_h)) << 22) | (((u32)(_l)) >> 10)) + +extern struct ieee80211_ops ath9k_htc_ops; +extern int modparam_nohwcrypt; + +enum htc_phymode { + HTC_MODE_AUTO = 0, + HTC_MODE_11A = 1, + HTC_MODE_11B = 2, + HTC_MODE_11G = 3, + HTC_MODE_FH = 4, + HTC_MODE_TURBO_A = 5, + HTC_MODE_TURBO_G = 6, + HTC_MODE_11NA = 7, + HTC_MODE_11NG = 8 +}; + +enum htc_opmode { + HTC_M_STA = 1, + HTC_M_IBSS = 0, + HTC_M_AHDEMO = 3, + HTC_M_HOSTAP = 6, + HTC_M_MONITOR = 8, + HTC_M_WDS = 2 +}; + +#define ATH9K_HTC_HDRSPACE sizeof(struct htc_frame_hdr) +#define ATH9K_HTC_AMPDU 1 +#define ATH9K_HTC_NORMAL 2 + +#define ATH9K_HTC_TX_CTSONLY 0x1 +#define ATH9K_HTC_TX_RTSCTS 0x2 +#define ATH9K_HTC_TX_USE_MIN_RATE 0x100 + +struct tx_frame_hdr { + u8 data_type; + u8 node_idx; + u8 vif_idx; + u8 tidno; + u32 flags; /* ATH9K_HTC_TX_* */ + u8 key_type; + u8 keyix; + u8 reserved[26]; +} __packed; + +struct tx_mgmt_hdr { + u8 node_idx; + u8 vif_idx; + u8 tidno; + u8 flags; + u8 key_type; + u8 keyix; + u16 reserved; +} __packed; + +struct tx_beacon_header { + u8 len_changed; + u8 vif_index; + u16 rev; +} __packed; + +struct ath9k_htc_target_hw { + u32 flags; + u32 flags_ext; + u32 ampdu_limit; + u8 ampdu_subframes; + u8 tx_chainmask; + u8 tx_chainmask_legacy; + u8 rtscts_ratecode; + u8 protmode; +} __packed; + +struct ath9k_htc_cap_target { + u32 flags; + u32 flags_ext; + u32 ampdu_limit; + u8 ampdu_subframes; + u8 tx_chainmask; + u8 tx_chainmask_legacy; + u8 rtscts_ratecode; + u8 protmode; +} __packed; + +struct ath9k_htc_target_vif { + u8 index; + u8 des_bssid[ETH_ALEN]; + enum htc_opmode opmode; + u8 myaddr[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + u32 flags; + u32 flags_ext; + u16 ps_sta; + u16 rtsthreshold; + u8 ath_cap; + u8 node; + s8 mcast_rate; +} __packed; + +#define ATH_HTC_STA_AUTH 0x0001 +#define ATH_HTC_STA_QOS 0x0002 +#define ATH_HTC_STA_ERP 0x0004 +#define ATH_HTC_STA_HT 0x0008 + +/* FIXME: UAPSD variables */ +struct ath9k_htc_target_sta { + u16 associd; + u16 txpower; + u32 ucastkey; + u8 macaddr[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + u8 sta_index; + u8 vif_index; + u8 vif_sta; + u16 flags; /* ATH_HTC_STA_* */ + u16 htcap; + u8 valid; + u16 capinfo; + struct ath9k_htc_target_hw *hw; + struct ath9k_htc_target_vif *vif; + u16 txseqmgmt; + u8 is_vif_sta; + u16 maxampdu; + u16 iv16; + u32 iv32; +} __packed; + +struct ath9k_htc_target_aggr { + u8 sta_index; + u8 tidno; + u8 aggr_enable; + u8 padding; +} __packed; + +#define ATH_HTC_RATE_MAX 30 + +#define WLAN_RC_DS_FLAG 0x01 +#define WLAN_RC_40_FLAG 0x02 +#define WLAN_RC_SGI_FLAG 0x04 +#define WLAN_RC_HT_FLAG 0x08 + +struct ath9k_htc_rateset { + u8 rs_nrates; + u8 rs_rates[ATH_HTC_RATE_MAX]; +}; + +struct ath9k_htc_rate { + struct ath9k_htc_rateset legacy_rates; + struct ath9k_htc_rateset ht_rates; +} __packed; + +struct ath9k_htc_target_rate { + u8 sta_index; + u8 isnew; + u32 capflags; + struct ath9k_htc_rate rates; +}; + +struct ath9k_htc_target_stats { + u32 tx_shortretry; + u32 tx_longretry; + u32 tx_xretries; + u32 ht_txunaggr_xretry; + u32 ht_tx_xretries; +} __packed; + +struct ath9k_htc_vif { + u8 index; +}; + +#define ATH9K_HTC_MAX_STA 8 +#define ATH9K_HTC_MAX_TID 8 + +enum tid_aggr_state { + AGGR_STOP = 0, + AGGR_PROGRESS, + AGGR_START, + AGGR_OPERATIONAL +}; + +struct ath9k_htc_sta { + u8 index; + enum tid_aggr_state tid_state[ATH9K_HTC_MAX_TID]; +}; + +struct ath9k_htc_aggr_work { + u16 tid; + u8 sta_addr[ETH_ALEN]; + struct ieee80211_hw *hw; + struct ieee80211_vif *vif; + enum ieee80211_ampdu_mlme_action action; + struct mutex mutex; +}; + +#define ATH9K_HTC_RXBUF 256 +#define HTC_RX_FRAME_HEADER_SIZE 40 + +struct ath9k_htc_rxbuf { + bool in_process; + struct sk_buff *skb; + struct ath_htc_rx_status rxstatus; + struct list_head list; +}; + +struct ath9k_htc_rx { + int last_rssi; /* FIXME: per-STA */ + struct list_head rxbuf; + spinlock_t rxbuflock; +}; + +struct ath9k_htc_tx_ctl { + u8 type; /* ATH9K_HTC_* */ +}; + +#ifdef CONFIG_ATH9K_HTC_DEBUGFS + +#define TX_STAT_INC(c) (hif_dev->htc_handle->drv_priv->debug.tx_stats.c++) +#define RX_STAT_INC(c) (hif_dev->htc_handle->drv_priv->debug.rx_stats.c++) + +struct ath_tx_stats { + u32 buf_queued; + u32 buf_completed; + u32 skb_queued; + u32 skb_completed; +}; + +struct ath_rx_stats { + u32 skb_allocated; + u32 skb_completed; + u32 skb_dropped; +}; + +struct ath9k_debug { + struct dentry *debugfs_phy; + struct dentry *debugfs_tgt_stats; + struct dentry *debugfs_xmit; + struct dentry *debugfs_recv; + struct ath_tx_stats tx_stats; + struct ath_rx_stats rx_stats; + u32 txrate; +}; + +#else + +#define TX_STAT_INC(c) do { } while (0) +#define RX_STAT_INC(c) do { } while (0) + +#endif /* CONFIG_ATH9K_HTC_DEBUGFS */ + +#define ATH_LED_PIN_DEF 1 +#define ATH_LED_PIN_9287 8 +#define ATH_LED_PIN_9271 15 +#define ATH_LED_ON_DURATION_IDLE 350 /* in msecs */ +#define ATH_LED_OFF_DURATION_IDLE 250 /* in msecs */ + +enum ath_led_type { + ATH_LED_RADIO, + ATH_LED_ASSOC, + ATH_LED_TX, + ATH_LED_RX +}; + +struct ath_led { + struct ath9k_htc_priv *priv; + struct led_classdev led_cdev; + enum ath_led_type led_type; + struct delayed_work brightness_work; + char name[32]; + bool registered; + int brightness; +}; + +#define OP_INVALID BIT(0) +#define OP_SCANNING BIT(1) +#define OP_FULL_RESET BIT(2) +#define OP_LED_ASSOCIATED BIT(3) +#define OP_LED_ON BIT(4) +#define OP_PREAMBLE_SHORT BIT(5) +#define OP_PROTECT_ENABLE BIT(6) +#define OP_TXAGGR BIT(7) +#define OP_ASSOCIATED BIT(8) +#define OP_ENABLE_BEACON BIT(9) +#define OP_LED_DEINIT BIT(10) + +struct ath9k_htc_priv { + struct device *dev; + struct ieee80211_hw *hw; + struct ath_hw *ah; + struct htc_target *htc; + struct wmi *wmi; + + enum htc_endpoint_id wmi_cmd_ep; + enum htc_endpoint_id beacon_ep; + enum htc_endpoint_id cab_ep; + enum htc_endpoint_id uapsd_ep; + enum htc_endpoint_id mgmt_ep; + enum htc_endpoint_id data_be_ep; + enum htc_endpoint_id data_bk_ep; + enum htc_endpoint_id data_vi_ep; + enum htc_endpoint_id data_vo_ep; + + u16 op_flags; + u16 curtxpow; + u16 txpowlimit; + u16 nvifs; + u16 nstations; + u16 seq_no; + u32 bmiss_cnt; + + struct sk_buff *beacon; + spinlock_t beacon_lock; + + struct ieee80211_vif *vif; + unsigned int rxfilter; + struct tasklet_struct wmi_tasklet; + struct tasklet_struct rx_tasklet; + struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; + struct ath9k_htc_rx rx; + struct tasklet_struct tx_tasklet; + struct sk_buff_head tx_queue; + struct ath9k_htc_aggr_work aggr_work; + struct delayed_work ath9k_aggr_work; + struct delayed_work ath9k_ani_work; + + struct ath_led radio_led; + struct ath_led assoc_led; + struct ath_led tx_led; + struct ath_led rx_led; + struct delayed_work ath9k_led_blink_work; + int led_on_duration; + int led_off_duration; + int led_on_cnt; + int led_off_cnt; + int hwq_map[ATH9K_WME_AC_VO+1]; + +#ifdef CONFIG_ATH9K_HTC_DEBUGFS + struct ath9k_debug debug; +#endif + struct ath9k_htc_target_rate tgt_rate; + + struct mutex mutex; +}; + +static inline void ath_read_cachesize(struct ath_common *common, int *csz) +{ + common->bus_ops->read_cachesize(common, csz); +} + +void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf); +void ath9k_htc_swba(struct ath9k_htc_priv *priv, u8 beacon_pending); +void ath9k_htc_beacon_update(struct ath9k_htc_priv *priv, + struct ieee80211_vif *vif); + +void ath9k_htc_rxep(void *priv, struct sk_buff *skb, + enum htc_endpoint_id ep_id); +void ath9k_htc_txep(void *priv, struct sk_buff *skb, enum htc_endpoint_id ep_id, + bool txok); + +void ath9k_htc_station_work(struct work_struct *work); +void ath9k_htc_aggr_work(struct work_struct *work); +void ath9k_ani_work(struct work_struct *work);; + +int ath9k_tx_init(struct ath9k_htc_priv *priv); +void ath9k_tx_tasklet(unsigned long data); +int ath9k_htc_tx_start(struct ath9k_htc_priv *priv, struct sk_buff *skb); +void ath9k_tx_cleanup(struct ath9k_htc_priv *priv); +bool ath9k_htc_txq_setup(struct ath9k_htc_priv *priv, + enum ath9k_tx_queue_subtype qtype); +int get_hw_qnum(u16 queue, int *hwq_map); +int ath_txq_update(struct ath9k_htc_priv *priv, int qnum, + struct ath9k_tx_queue_info *qinfo); + +int ath9k_rx_init(struct ath9k_htc_priv *priv); +void ath9k_rx_cleanup(struct ath9k_htc_priv *priv); +void ath9k_host_rx_init(struct ath9k_htc_priv *priv); +void ath9k_rx_tasklet(unsigned long data); + +void ath9k_start_rfkill_poll(struct ath9k_htc_priv *priv); +void ath9k_init_leds(struct ath9k_htc_priv *priv); +void ath9k_deinit_leds(struct ath9k_htc_priv *priv); + +int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev, + u16 devid); +void ath9k_htc_disconnect_device(struct htc_target *htc_handle, bool hotunplug); +#ifdef CONFIG_PM +int ath9k_htc_resume(struct htc_target *htc_handle); +#endif +#ifdef CONFIG_ATH9K_HTC_DEBUGFS +int ath9k_debug_create_root(void); +void ath9k_debug_remove_root(void); +int ath9k_init_debug(struct ath_hw *ah); +void ath9k_exit_debug(struct ath_hw *ah); +#else +static inline int ath9k_debug_create_root(void) { return 0; }; +static inline void ath9k_debug_remove_root(void) {}; +static inline int ath9k_init_debug(struct ath_hw *ah) { return 0; }; +static inline void ath9k_exit_debug(struct ath_hw *ah) {}; +#endif /* CONFIG_ATH9K_HTC_DEBUGFS */ + +#endif /* HTC_H */ diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c new file mode 100644 index 000000000000..25f5b5377bac --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2010 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "htc.h" + +#define FUDGE 2 + +static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv, + struct ieee80211_bss_conf *bss_conf) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_beacon_state bs; + enum ath9k_int imask = 0; + int dtimperiod, dtimcount, sleepduration; + int cfpperiod, cfpcount, bmiss_timeout; + u32 nexttbtt = 0, intval, tsftu, htc_imask = 0; + u64 tsf; + int num_beacons, offset, dtim_dec_count, cfp_dec_count; + int ret; + u8 cmd_rsp; + + memset(&bs, 0, sizeof(bs)); + + intval = bss_conf->beacon_int & ATH9K_BEACON_PERIOD; + bmiss_timeout = (ATH_DEFAULT_BMISS_LIMIT * bss_conf->beacon_int); + + /* + * Setup dtim and cfp parameters according to + * last beacon we received (which may be none). + */ + dtimperiod = bss_conf->dtim_period; + if (dtimperiod <= 0) /* NB: 0 if not known */ + dtimperiod = 1; + dtimcount = 1; + if (dtimcount >= dtimperiod) /* NB: sanity check */ + dtimcount = 0; + cfpperiod = 1; /* NB: no PCF support yet */ + cfpcount = 0; + + sleepduration = intval; + if (sleepduration <= 0) + sleepduration = intval; + + /* + * Pull nexttbtt forward to reflect the current + * TSF and calculate dtim+cfp state for the result. + */ + tsf = ath9k_hw_gettsf64(priv->ah); + tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; + + num_beacons = tsftu / intval + 1; + offset = tsftu % intval; + nexttbtt = tsftu - offset; + if (offset) + nexttbtt += intval; + + /* DTIM Beacon every dtimperiod Beacon */ + dtim_dec_count = num_beacons % dtimperiod; + /* CFP every cfpperiod DTIM Beacon */ + cfp_dec_count = (num_beacons / dtimperiod) % cfpperiod; + if (dtim_dec_count) + cfp_dec_count++; + + dtimcount -= dtim_dec_count; + if (dtimcount < 0) + dtimcount += dtimperiod; + + cfpcount -= cfp_dec_count; + if (cfpcount < 0) + cfpcount += cfpperiod; + + bs.bs_intval = intval; + bs.bs_nexttbtt = nexttbtt; + bs.bs_dtimperiod = dtimperiod*intval; + bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; + bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; + bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; + bs.bs_cfpmaxduration = 0; + + /* + * Calculate the number of consecutive beacons to miss* before taking + * a BMISS interrupt. The configuration is specified in TU so we only + * need calculate based on the beacon interval. Note that we clamp the + * result to at most 15 beacons. + */ + if (sleepduration > intval) { + bs.bs_bmissthreshold = ATH_DEFAULT_BMISS_LIMIT / 2; + } else { + bs.bs_bmissthreshold = DIV_ROUND_UP(bmiss_timeout, intval); + if (bs.bs_bmissthreshold > 15) + bs.bs_bmissthreshold = 15; + else if (bs.bs_bmissthreshold <= 0) + bs.bs_bmissthreshold = 1; + } + + /* + * Calculate sleep duration. The configuration is given in ms. + * We ensure a multiple of the beacon period is used. Also, if the sleep + * duration is greater than the DTIM period then it makes senses + * to make it a multiple of that. + * + * XXX fixed at 100ms + */ + + bs.bs_sleepduration = roundup(IEEE80211_MS_TO_TU(100), sleepduration); + if (bs.bs_sleepduration > bs.bs_dtimperiod) + bs.bs_sleepduration = bs.bs_dtimperiod; + + /* TSF out of range threshold fixed at 1 second */ + bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD; + + ath_print(common, ATH_DBG_BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu); + ath_print(common, ATH_DBG_BEACON, + "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n", + bs.bs_bmissthreshold, bs.bs_sleepduration, + bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext); + + /* Set the computed STA beacon timers */ + + WMI_CMD(WMI_DISABLE_INTR_CMDID); + ath9k_hw_set_sta_beacon_timers(priv->ah, &bs); + imask |= ATH9K_INT_BMISS; + htc_imask = cpu_to_be32(imask); + WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask); +} + +static void ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv *priv, + struct ieee80211_bss_conf *bss_conf) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + enum ath9k_int imask = 0; + u32 nexttbtt, intval, htc_imask = 0; + int ret; + u8 cmd_rsp; + + intval = bss_conf->beacon_int & ATH9K_BEACON_PERIOD; + nexttbtt = intval; + intval |= ATH9K_BEACON_ENA; + if (priv->op_flags & OP_ENABLE_BEACON) + imask |= ATH9K_INT_SWBA; + + ath_print(common, ATH_DBG_BEACON, + "IBSS Beacon config, intval: %d, imask: 0x%x\n", + bss_conf->beacon_int, imask); + + WMI_CMD(WMI_DISABLE_INTR_CMDID); + ath9k_hw_beaconinit(priv->ah, nexttbtt, intval); + priv->bmiss_cnt = 0; + htc_imask = cpu_to_be32(imask); + WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask); +} + +void ath9k_htc_beacon_update(struct ath9k_htc_priv *priv, + struct ieee80211_vif *vif) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + + spin_lock_bh(&priv->beacon_lock); + + if (priv->beacon) + dev_kfree_skb_any(priv->beacon); + + priv->beacon = ieee80211_beacon_get(priv->hw, vif); + if (!priv->beacon) + ath_print(common, ATH_DBG_BEACON, + "Unable to allocate beacon\n"); + + spin_unlock_bh(&priv->beacon_lock); +} + +void ath9k_htc_swba(struct ath9k_htc_priv *priv, u8 beacon_pending) +{ + struct ath9k_htc_vif *avp = (void *)priv->vif->drv_priv; + struct tx_beacon_header beacon_hdr; + struct ath9k_htc_tx_ctl tx_ctl; + struct ieee80211_tx_info *info; + u8 *tx_fhdr; + + memset(&beacon_hdr, 0, sizeof(struct tx_beacon_header)); + memset(&tx_ctl, 0, sizeof(struct ath9k_htc_tx_ctl)); + + /* FIXME: Handle BMISS */ + if (beacon_pending != 0) { + priv->bmiss_cnt++; + return; + } + + spin_lock_bh(&priv->beacon_lock); + + if (unlikely(priv->op_flags & OP_SCANNING)) { + spin_unlock_bh(&priv->beacon_lock); + return; + } + + if (unlikely(priv->beacon == NULL)) { + spin_unlock_bh(&priv->beacon_lock); + return; + } + + /* Free the old SKB first */ + dev_kfree_skb_any(priv->beacon); + + /* Get a new beacon */ + priv->beacon = ieee80211_beacon_get(priv->hw, priv->vif); + if (!priv->beacon) { + spin_unlock_bh(&priv->beacon_lock); + return; + } + + info = IEEE80211_SKB_CB(priv->beacon); + if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { + struct ieee80211_hdr *hdr = + (struct ieee80211_hdr *) priv->beacon->data; + priv->seq_no += 0x10; + hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); + hdr->seq_ctrl |= cpu_to_le16(priv->seq_no); + } + + tx_ctl.type = ATH9K_HTC_NORMAL; + beacon_hdr.vif_index = avp->index; + tx_fhdr = skb_push(priv->beacon, sizeof(beacon_hdr)); + memcpy(tx_fhdr, (u8 *) &beacon_hdr, sizeof(beacon_hdr)); + + htc_send(priv->htc, priv->beacon, priv->beacon_ep, &tx_ctl); + + spin_unlock_bh(&priv->beacon_lock); +} + +void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + ath9k_htc_beacon_config_sta(priv, bss_conf); + break; + case NL80211_IFTYPE_ADHOC: + ath9k_htc_beacon_config_adhoc(priv, bss_conf); + break; + default: + ath_print(common, ATH_DBG_CONFIG, + "Unsupported beaconing mode\n"); + return; + } +} diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c new file mode 100644 index 000000000000..a653dec140b9 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -0,0 +1,713 @@ +/* + * Copyright (c) 2010 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "htc.h" + +MODULE_AUTHOR("Atheros Communications"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("Atheros driver 802.11n HTC based wireless devices"); + +static unsigned int ath9k_debug = ATH_DBG_DEFAULT; +module_param_named(debug, ath9k_debug, uint, 0); +MODULE_PARM_DESC(debug, "Debugging mask"); + +int modparam_nohwcrypt; +module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption"); + +#define CHAN2G(_freq, _idx) { \ + .center_freq = (_freq), \ + .hw_value = (_idx), \ + .max_power = 20, \ +} + +static struct ieee80211_channel ath9k_2ghz_channels[] = { + CHAN2G(2412, 0), /* Channel 1 */ + CHAN2G(2417, 1), /* Channel 2 */ + CHAN2G(2422, 2), /* Channel 3 */ + CHAN2G(2427, 3), /* Channel 4 */ + CHAN2G(2432, 4), /* Channel 5 */ + CHAN2G(2437, 5), /* Channel 6 */ + CHAN2G(2442, 6), /* Channel 7 */ + CHAN2G(2447, 7), /* Channel 8 */ + CHAN2G(2452, 8), /* Channel 9 */ + CHAN2G(2457, 9), /* Channel 10 */ + CHAN2G(2462, 10), /* Channel 11 */ + CHAN2G(2467, 11), /* Channel 12 */ + CHAN2G(2472, 12), /* Channel 13 */ + CHAN2G(2484, 13), /* Channel 14 */ +}; + +/* Atheros hardware rate code addition for short premble */ +#define SHPCHECK(__hw_rate, __flags) \ + ((__flags & IEEE80211_RATE_SHORT_PREAMBLE) ? (__hw_rate | 0x04) : 0) + +#define RATE(_bitrate, _hw_rate, _flags) { \ + .bitrate = (_bitrate), \ + .flags = (_flags), \ + .hw_value = (_hw_rate), \ + .hw_value_short = (SHPCHECK(_hw_rate, _flags)) \ +} + +static struct ieee80211_rate ath9k_legacy_rates[] = { + RATE(10, 0x1b, 0), + RATE(20, 0x1a, IEEE80211_RATE_SHORT_PREAMBLE), /* shortp : 0x1e */ + RATE(55, 0x19, IEEE80211_RATE_SHORT_PREAMBLE), /* shortp: 0x1d */ + RATE(110, 0x18, IEEE80211_RATE_SHORT_PREAMBLE), /* short: 0x1c */ + RATE(60, 0x0b, 0), + RATE(90, 0x0f, 0), + RATE(120, 0x0a, 0), + RATE(180, 0x0e, 0), + RATE(240, 0x09, 0), + RATE(360, 0x0d, 0), + RATE(480, 0x08, 0), + RATE(540, 0x0c, 0), +}; + +static int ath9k_htc_wait_for_target(struct ath9k_htc_priv *priv) +{ + int time_left; + + /* Firmware can take up to 50ms to get ready, to be safe use 1 second */ + time_left = wait_for_completion_timeout(&priv->htc->target_wait, HZ); + if (!time_left) { + dev_err(priv->dev, "ath9k_htc: Target is unresponsive\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static void ath9k_deinit_priv(struct ath9k_htc_priv *priv) +{ + ath9k_exit_debug(priv->ah); + ath9k_hw_deinit(priv->ah); + tasklet_kill(&priv->wmi_tasklet); + tasklet_kill(&priv->rx_tasklet); + tasklet_kill(&priv->tx_tasklet); + kfree(priv->ah); + priv->ah = NULL; +} + +static void ath9k_deinit_device(struct ath9k_htc_priv *priv) +{ + struct ieee80211_hw *hw = priv->hw; + + wiphy_rfkill_stop_polling(hw->wiphy); + ath9k_deinit_leds(priv); + ieee80211_unregister_hw(hw); + ath9k_rx_cleanup(priv); + ath9k_tx_cleanup(priv); + ath9k_deinit_priv(priv); +} + +static inline int ath9k_htc_connect_svc(struct ath9k_htc_priv *priv, + u16 service_id, + void (*tx) (void *, + struct sk_buff *, + enum htc_endpoint_id, + bool txok), + enum htc_endpoint_id *ep_id) +{ + struct htc_service_connreq req; + + memset(&req, 0, sizeof(struct htc_service_connreq)); + + req.service_id = service_id; + req.ep_callbacks.priv = priv; + req.ep_callbacks.rx = ath9k_htc_rxep; + req.ep_callbacks.tx = tx; + + return htc_connect_service(priv->htc, &req, ep_id); +} + +static int ath9k_init_htc_services(struct ath9k_htc_priv *priv) +{ + int ret; + + /* WMI CMD*/ + ret = ath9k_wmi_connect(priv->htc, priv->wmi, &priv->wmi_cmd_ep); + if (ret) + goto err; + + /* Beacon */ + ret = ath9k_htc_connect_svc(priv, WMI_BEACON_SVC, NULL, + &priv->beacon_ep); + if (ret) + goto err; + + /* CAB */ + ret = ath9k_htc_connect_svc(priv, WMI_CAB_SVC, ath9k_htc_txep, + &priv->cab_ep); + if (ret) + goto err; + + + /* UAPSD */ + ret = ath9k_htc_connect_svc(priv, WMI_UAPSD_SVC, ath9k_htc_txep, + &priv->uapsd_ep); + if (ret) + goto err; + + /* MGMT */ + ret = ath9k_htc_connect_svc(priv, WMI_MGMT_SVC, ath9k_htc_txep, + &priv->mgmt_ep); + if (ret) + goto err; + + /* DATA BE */ + ret = ath9k_htc_connect_svc(priv, WMI_DATA_BE_SVC, ath9k_htc_txep, + &priv->data_be_ep); + if (ret) + goto err; + + /* DATA BK */ + ret = ath9k_htc_connect_svc(priv, WMI_DATA_BK_SVC, ath9k_htc_txep, + &priv->data_bk_ep); + if (ret) + goto err; + + /* DATA VI */ + ret = ath9k_htc_connect_svc(priv, WMI_DATA_VI_SVC, ath9k_htc_txep, + &priv->data_vi_ep); + if (ret) + goto err; + + /* DATA VO */ + ret = ath9k_htc_connect_svc(priv, WMI_DATA_VO_SVC, ath9k_htc_txep, + &priv->data_vo_ep); + if (ret) + goto err; + + ret = htc_init(priv->htc); + if (ret) + goto err; + + return 0; + +err: + dev_err(priv->dev, "ath9k_htc: Unable to initialize HTC services\n"); + return ret; +} + +static int ath9k_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct ath9k_htc_priv *priv = hw->priv; + + return ath_reg_notifier_apply(wiphy, request, + ath9k_hw_regulatory(priv->ah)); +} + +static unsigned int ath9k_ioread32(void *hw_priv, u32 reg_offset) +{ + struct ath_hw *ah = (struct ath_hw *) hw_priv; + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; + __be32 val, reg = cpu_to_be32(reg_offset); + int r; + + r = ath9k_wmi_cmd(priv->wmi, WMI_REG_READ_CMDID, + (u8 *) ®, sizeof(reg), + (u8 *) &val, sizeof(val), + 100); + if (unlikely(r)) { + ath_print(common, ATH_DBG_WMI, + "REGISTER READ FAILED: (0x%04x, %d)\n", + reg_offset, r); + return -EIO; + } + + return be32_to_cpu(val); +} + +static void ath9k_iowrite32(void *hw_priv, u32 val, u32 reg_offset) +{ + struct ath_hw *ah = (struct ath_hw *) hw_priv; + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; + __be32 buf[2] = { + cpu_to_be32(reg_offset), + cpu_to_be32(val), + }; + int r; + + r = ath9k_wmi_cmd(priv->wmi, WMI_REG_WRITE_CMDID, + (u8 *) &buf, sizeof(buf), + (u8 *) &val, sizeof(val), + 100); + if (unlikely(r)) { + ath_print(common, ATH_DBG_WMI, + "REGISTER WRITE FAILED:(0x%04x, %d)\n", + reg_offset, r); + } +} + +static const struct ath_ops ath9k_common_ops = { + .read = ath9k_ioread32, + .write = ath9k_iowrite32, +}; + +static void ath_usb_read_cachesize(struct ath_common *common, int *csz) +{ + *csz = L1_CACHE_BYTES >> 2; +} + +static bool ath_usb_eeprom_read(struct ath_common *common, u32 off, u16 *data) +{ + struct ath_hw *ah = (struct ath_hw *) common->ah; + + (void)REG_READ(ah, AR5416_EEPROM_OFFSET + (off << AR5416_EEPROM_S)); + + if (!ath9k_hw_wait(ah, + AR_EEPROM_STATUS_DATA, + AR_EEPROM_STATUS_DATA_BUSY | + AR_EEPROM_STATUS_DATA_PROT_ACCESS, 0, + AH_WAIT_TIMEOUT)) + return false; + + *data = MS(REG_READ(ah, AR_EEPROM_STATUS_DATA), + AR_EEPROM_STATUS_DATA_VAL); + + return true; +} + +static const struct ath_bus_ops ath9k_usb_bus_ops = { + .read_cachesize = ath_usb_read_cachesize, + .eeprom_read = ath_usb_eeprom_read, +}; + +static void setup_ht_cap(struct ath9k_htc_priv *priv, + struct ieee80211_sta_ht_cap *ht_info) +{ + ht_info->ht_supported = true; + ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_SM_PS | + IEEE80211_HT_CAP_SGI_40 | + IEEE80211_HT_CAP_DSSSCCK40; + + ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_8; + + memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); + ht_info->mcs.rx_mask[0] = 0xff; + ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED; +} + +static int ath9k_init_queues(struct ath9k_htc_priv *priv) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + int i; + + for (i = 0; i < ARRAY_SIZE(priv->hwq_map); i++) + priv->hwq_map[i] = -1; + + if (!ath9k_htc_txq_setup(priv, ATH9K_WME_AC_BE)) { + ath_print(common, ATH_DBG_FATAL, + "Unable to setup xmit queue for BE traffic\n"); + goto err; + } + + if (!ath9k_htc_txq_setup(priv, ATH9K_WME_AC_BK)) { + ath_print(common, ATH_DBG_FATAL, + "Unable to setup xmit queue for BK traffic\n"); + goto err; + } + if (!ath9k_htc_txq_setup(priv, ATH9K_WME_AC_VI)) { + ath_print(common, ATH_DBG_FATAL, + "Unable to setup xmit queue for VI traffic\n"); + goto err; + } + if (!ath9k_htc_txq_setup(priv, ATH9K_WME_AC_VO)) { + ath_print(common, ATH_DBG_FATAL, + "Unable to setup xmit queue for VO traffic\n"); + goto err; + } + + return 0; + +err: + return -EINVAL; +} + +static void ath9k_init_crypto(struct ath9k_htc_priv *priv) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + int i = 0; + + /* Get the hardware key cache size. */ + common->keymax = priv->ah->caps.keycache_size; + if (common->keymax > ATH_KEYMAX) { + ath_print(common, ATH_DBG_ANY, + "Warning, using only %u entries in %u key cache\n", + ATH_KEYMAX, common->keymax); + common->keymax = ATH_KEYMAX; + } + + /* + * Reset the key cache since some parts do not + * reset the contents on initial power up. + */ + for (i = 0; i < common->keymax; i++) + ath9k_hw_keyreset(priv->ah, (u16) i); + + if (ath9k_hw_getcapability(priv->ah, ATH9K_CAP_CIPHER, + ATH9K_CIPHER_TKIP, NULL)) { + /* + * Whether we should enable h/w TKIP MIC. + * XXX: if we don't support WME TKIP MIC, then we wouldn't + * report WMM capable, so it's always safe to turn on + * TKIP MIC in this case. + */ + ath9k_hw_setcapability(priv->ah, ATH9K_CAP_TKIP_MIC, 0, 1, NULL); + } + + /* + * Check whether the separate key cache entries + * are required to handle both tx+rx MIC keys. + * With split mic keys the number of stations is limited + * to 27 otherwise 59. + */ + if (ath9k_hw_getcapability(priv->ah, ATH9K_CAP_CIPHER, + ATH9K_CIPHER_TKIP, NULL) + && ath9k_hw_getcapability(priv->ah, ATH9K_CAP_CIPHER, + ATH9K_CIPHER_MIC, NULL) + && ath9k_hw_getcapability(priv->ah, ATH9K_CAP_TKIP_SPLIT, + 0, NULL)) + common->splitmic = 1; + + /* turn on mcast key search if possible */ + if (!ath9k_hw_getcapability(priv->ah, ATH9K_CAP_MCAST_KEYSRCH, 0, NULL)) + (void)ath9k_hw_setcapability(priv->ah, ATH9K_CAP_MCAST_KEYSRCH, + 1, 1, NULL); +} + +static void ath9k_init_channels_rates(struct ath9k_htc_priv *priv) +{ + if (test_bit(ATH9K_MODE_11G, priv->ah->caps.wireless_modes)) { + priv->sbands[IEEE80211_BAND_2GHZ].channels = + ath9k_2ghz_channels; + priv->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; + priv->sbands[IEEE80211_BAND_2GHZ].n_channels = + ARRAY_SIZE(ath9k_2ghz_channels); + priv->sbands[IEEE80211_BAND_2GHZ].bitrates = ath9k_legacy_rates; + priv->sbands[IEEE80211_BAND_2GHZ].n_bitrates = + ARRAY_SIZE(ath9k_legacy_rates); + } +} + +static void ath9k_init_misc(struct ath9k_htc_priv *priv) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + + common->tx_chainmask = priv->ah->caps.tx_chainmask; + common->rx_chainmask = priv->ah->caps.rx_chainmask; + + if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) + memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); + + priv->op_flags |= OP_TXAGGR; +} + +static int ath9k_init_priv(struct ath9k_htc_priv *priv, u16 devid) +{ + struct ath_hw *ah = NULL; + struct ath_common *common; + int ret = 0, csz = 0; + + priv->op_flags |= OP_INVALID; + + ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL); + if (!ah) + return -ENOMEM; + + ah->hw_version.devid = devid; + ah->hw_version.subsysid = 0; /* FIXME */ + priv->ah = ah; + + common = ath9k_hw_common(ah); + common->ops = &ath9k_common_ops; + common->bus_ops = &ath9k_usb_bus_ops; + common->ah = ah; + common->hw = priv->hw; + common->priv = priv; + common->debug_mask = ath9k_debug; + + spin_lock_init(&priv->wmi->wmi_lock); + spin_lock_init(&priv->beacon_lock); + mutex_init(&priv->mutex); + mutex_init(&priv->aggr_work.mutex); + tasklet_init(&priv->wmi_tasklet, ath9k_wmi_tasklet, + (unsigned long)priv); + tasklet_init(&priv->rx_tasklet, ath9k_rx_tasklet, + (unsigned long)priv); + tasklet_init(&priv->tx_tasklet, ath9k_tx_tasklet, (unsigned long)priv); + INIT_DELAYED_WORK(&priv->ath9k_aggr_work, ath9k_htc_aggr_work); + INIT_DELAYED_WORK(&priv->ath9k_ani_work, ath9k_ani_work); + + /* + * Cache line size is used to size and align various + * structures used to communicate with the hardware. + */ + ath_read_cachesize(common, &csz); + common->cachelsz = csz << 2; /* convert to bytes */ + + ret = ath9k_hw_init(ah); + if (ret) { + ath_print(common, ATH_DBG_FATAL, + "Unable to initialize hardware; " + "initialization status: %d\n", ret); + goto err_hw; + } + + ret = ath9k_init_debug(ah); + if (ret) { + ath_print(common, ATH_DBG_FATAL, + "Unable to create debugfs files\n"); + goto err_debug; + } + + ret = ath9k_init_queues(priv); + if (ret) + goto err_queues; + + ath9k_init_crypto(priv); + ath9k_init_channels_rates(priv); + ath9k_init_misc(priv); + + return 0; + +err_queues: + ath9k_exit_debug(ah); +err_debug: + ath9k_hw_deinit(ah); +err_hw: + + kfree(ah); + priv->ah = NULL; + + return ret; +} + +static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, + struct ieee80211_hw *hw) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + + hw->flags = IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_AMPDU_AGGREGATION | + IEEE80211_HW_SPECTRUM_MGMT | + IEEE80211_HW_HAS_RATE_CONTROL; + + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC); + + hw->queues = 4; + hw->channel_change_time = 5000; + hw->max_listen_interval = 10; + hw->vif_data_size = sizeof(struct ath9k_htc_vif); + hw->sta_data_size = sizeof(struct ath9k_htc_sta); + + /* tx_frame_hdr is larger than tx_mgmt_hdr anyway */ + hw->extra_tx_headroom = sizeof(struct tx_frame_hdr) + + sizeof(struct htc_frame_hdr) + 4; + + if (test_bit(ATH9K_MODE_11G, priv->ah->caps.wireless_modes)) + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = + &priv->sbands[IEEE80211_BAND_2GHZ]; + + if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_HT) { + if (test_bit(ATH9K_MODE_11G, priv->ah->caps.wireless_modes)) + setup_ht_cap(priv, + &priv->sbands[IEEE80211_BAND_2GHZ].ht_cap); + } + + SET_IEEE80211_PERM_ADDR(hw, common->macaddr); +} + +static int ath9k_init_device(struct ath9k_htc_priv *priv, u16 devid) +{ + struct ieee80211_hw *hw = priv->hw; + struct ath_common *common; + struct ath_hw *ah; + int error = 0; + struct ath_regulatory *reg; + + /* Bring up device */ + error = ath9k_init_priv(priv, devid); + if (error != 0) + goto err_init; + + ah = priv->ah; + common = ath9k_hw_common(ah); + ath9k_set_hw_capab(priv, hw); + + /* Initialize regulatory */ + error = ath_regd_init(&common->regulatory, priv->hw->wiphy, + ath9k_reg_notifier); + if (error) + goto err_regd; + + reg = &common->regulatory; + + /* Setup TX */ + error = ath9k_tx_init(priv); + if (error != 0) + goto err_tx; + + /* Setup RX */ + error = ath9k_rx_init(priv); + if (error != 0) + goto err_rx; + + /* Register with mac80211 */ + error = ieee80211_register_hw(hw); + if (error) + goto err_register; + + /* Handle world regulatory */ + if (!ath_is_world_regd(reg)) { + error = regulatory_hint(hw->wiphy, reg->alpha2); + if (error) + goto err_world; + } + + ath9k_init_leds(priv); + ath9k_start_rfkill_poll(priv); + + return 0; + +err_world: + ieee80211_unregister_hw(hw); +err_register: + ath9k_rx_cleanup(priv); +err_rx: + ath9k_tx_cleanup(priv); +err_tx: + /* Nothing */ +err_regd: + ath9k_deinit_priv(priv); +err_init: + return error; +} + +int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev, + u16 devid) +{ + struct ieee80211_hw *hw; + struct ath9k_htc_priv *priv; + int ret; + + hw = ieee80211_alloc_hw(sizeof(struct ath9k_htc_priv), &ath9k_htc_ops); + if (!hw) + return -ENOMEM; + + priv = hw->priv; + priv->hw = hw; + priv->htc = htc_handle; + priv->dev = dev; + htc_handle->drv_priv = priv; + SET_IEEE80211_DEV(hw, priv->dev); + + ret = ath9k_htc_wait_for_target(priv); + if (ret) + goto err_free; + + priv->wmi = ath9k_init_wmi(priv); + if (!priv->wmi) { + ret = -EINVAL; + goto err_free; + } + + ret = ath9k_init_htc_services(priv); + if (ret) + goto err_init; + + ret = ath9k_init_device(priv, devid); + if (ret) + goto err_init; + + return 0; + +err_init: + ath9k_deinit_wmi(priv); +err_free: + ieee80211_free_hw(hw); + return ret; +} + +void ath9k_htc_disconnect_device(struct htc_target *htc_handle, bool hotunplug) +{ + if (htc_handle->drv_priv) { + ath9k_deinit_device(htc_handle->drv_priv); + ath9k_deinit_wmi(htc_handle->drv_priv); + ieee80211_free_hw(htc_handle->drv_priv->hw); + } +} + +#ifdef CONFIG_PM +int ath9k_htc_resume(struct htc_target *htc_handle) +{ + int ret; + + ret = ath9k_htc_wait_for_target(htc_handle->drv_priv); + if (ret) + return ret; + + ret = ath9k_init_htc_services(htc_handle->drv_priv); + return ret; +} +#endif + +static int __init ath9k_htc_init(void) +{ + int error; + + error = ath9k_debug_create_root(); + if (error < 0) { + printk(KERN_ERR + "ath9k_htc: Unable to create debugfs root: %d\n", + error); + goto err_dbg; + } + + error = ath9k_hif_usb_init(); + if (error < 0) { + printk(KERN_ERR + "ath9k_htc: No USB devices found," + " driver not installed.\n"); + error = -ENODEV; + goto err_usb; + } + + return 0; + +err_usb: + ath9k_debug_remove_root(); +err_dbg: + return error; +} +module_init(ath9k_htc_init); + +static void __exit ath9k_htc_exit(void) +{ + ath9k_hif_usb_exit(); + ath9k_debug_remove_root(); + printk(KERN_INFO "ath9k_htc: Driver unloaded\n"); +} +module_exit(ath9k_htc_exit); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c new file mode 100644 index 000000000000..3184a2ac7b88 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -0,0 +1,1626 @@ +/* + * Copyright (c) 2010 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "htc.h" + +#ifdef CONFIG_ATH9K_HTC_DEBUGFS +static struct dentry *ath9k_debugfs_root; +#endif + +/*************/ +/* Utilities */ +/*************/ + +static void ath_update_txpow(struct ath9k_htc_priv *priv) +{ + struct ath_hw *ah = priv->ah; + u32 txpow; + + if (priv->curtxpow != priv->txpowlimit) { + ath9k_hw_set_txpowerlimit(ah, priv->txpowlimit); + /* read back in case value is clamped */ + ath9k_hw_getcapability(ah, ATH9K_CAP_TXPOW, 1, &txpow); + priv->curtxpow = txpow; + } +} + +/* HACK Alert: Use 11NG for 2.4, use 11NA for 5 */ +static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv, + struct ath9k_channel *ichan) +{ + enum htc_phymode mode; + + mode = HTC_MODE_AUTO; + + switch (ichan->chanmode) { + case CHANNEL_G: + case CHANNEL_G_HT20: + case CHANNEL_G_HT40PLUS: + case CHANNEL_G_HT40MINUS: + mode = HTC_MODE_11NG; + break; + case CHANNEL_A: + case CHANNEL_A_HT20: + case CHANNEL_A_HT40PLUS: + case CHANNEL_A_HT40MINUS: + mode = HTC_MODE_11NA; + break; + default: + break; + } + + return mode; +} + +static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, + struct ieee80211_hw *hw, + struct ath9k_channel *hchan) +{ + struct ath_hw *ah = priv->ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ieee80211_conf *conf = &common->hw->conf; + bool fastcc = true; + struct ieee80211_channel *channel = hw->conf.channel; + enum htc_phymode mode; + u16 htc_mode; + u8 cmd_rsp; + int ret; + + if (priv->op_flags & OP_INVALID) + return -EIO; + + if (priv->op_flags & OP_FULL_RESET) + fastcc = false; + + /* Fiddle around with fastcc later on, for now just use full reset */ + fastcc = false; + + htc_stop(priv->htc); + WMI_CMD(WMI_DISABLE_INTR_CMDID); + WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); + WMI_CMD(WMI_STOP_RECV_CMDID); + + ath_print(common, ATH_DBG_CONFIG, + "(%u MHz) -> (%u MHz), HT: %d, HT40: %d\n", + priv->ah->curchan->channel, + channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf)); + + ret = ath9k_hw_reset(ah, hchan, fastcc); + if (ret) { + ath_print(common, ATH_DBG_FATAL, + "Unable to reset channel (%u Mhz) " + "reset status %d\n", channel->center_freq, ret); + goto err; + } + + ath_update_txpow(priv); + + WMI_CMD(WMI_START_RECV_CMDID); + if (ret) + goto err; + + ath9k_host_rx_init(priv); + + mode = ath9k_htc_get_curmode(priv, hchan); + htc_mode = cpu_to_be16(mode); + WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode); + if (ret) + goto err; + + WMI_CMD(WMI_ENABLE_INTR_CMDID); + if (ret) + goto err; + + htc_start(priv->htc); + + priv->op_flags &= ~OP_FULL_RESET; +err: + return ret; +} + +static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_htc_target_vif hvif; + int ret = 0; + u8 cmd_rsp; + + if (priv->nvifs > 0) + return -ENOBUFS; + + memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); + memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN); + + hvif.opmode = cpu_to_be32(HTC_M_MONITOR); + priv->ah->opmode = NL80211_IFTYPE_MONITOR; + hvif.index = priv->nvifs; + + WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif); + if (ret) + return ret; + + priv->nvifs++; + return 0; +} + +static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_htc_target_vif hvif; + int ret = 0; + u8 cmd_rsp; + + memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); + memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN); + hvif.index = 0; /* Should do for now */ + WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); + priv->nvifs--; + + return ret; +} + +static int ath9k_htc_add_station(struct ath9k_htc_priv *priv, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_htc_target_sta tsta; + struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv; + struct ath9k_htc_sta *ista; + int ret; + u8 cmd_rsp; + + if (priv->nstations >= ATH9K_HTC_MAX_STA) + return -ENOBUFS; + + memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta)); + + if (sta) { + ista = (struct ath9k_htc_sta *) sta->drv_priv; + memcpy(&tsta.macaddr, sta->addr, ETH_ALEN); + memcpy(&tsta.bssid, common->curbssid, ETH_ALEN); + tsta.associd = common->curaid; + tsta.is_vif_sta = 0; + tsta.valid = true; + ista->index = priv->nstations; + } else { + memcpy(&tsta.macaddr, vif->addr, ETH_ALEN); + tsta.is_vif_sta = 1; + } + + tsta.sta_index = priv->nstations; + tsta.vif_index = avp->index; + tsta.maxampdu = 0xffff; + if (sta && sta->ht_cap.ht_supported) + tsta.flags = cpu_to_be16(ATH_HTC_STA_HT); + + WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta); + if (ret) { + if (sta) + ath_print(common, ATH_DBG_FATAL, + "Unable to add station entry for: %pM\n", sta->addr); + return ret; + } + + if (sta) + ath_print(common, ATH_DBG_CONFIG, + "Added a station entry for: %pM (idx: %d)\n", + sta->addr, tsta.sta_index); + + priv->nstations++; + return 0; +} + +static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_htc_sta *ista; + int ret; + u8 cmd_rsp, sta_idx; + + if (sta) { + ista = (struct ath9k_htc_sta *) sta->drv_priv; + sta_idx = ista->index; + } else { + sta_idx = 0; + } + + WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx); + if (ret) { + if (sta) + ath_print(common, ATH_DBG_FATAL, + "Unable to remove station entry for: %pM\n", + sta->addr); + return ret; + } + + if (sta) + ath_print(common, ATH_DBG_CONFIG, + "Removed a station entry for: %pM (idx: %d)\n", + sta->addr, sta_idx); + + priv->nstations--; + return 0; +} + +static int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv) +{ + struct ath9k_htc_cap_target tcap; + int ret; + u8 cmd_rsp; + + memset(&tcap, 0, sizeof(struct ath9k_htc_cap_target)); + + /* FIXME: Values are hardcoded */ + tcap.flags = 0x240c40; + tcap.flags_ext = 0x80601000; + tcap.ampdu_limit = 0xffff0000; + tcap.ampdu_subframes = 20; + tcap.tx_chainmask_legacy = 1; + tcap.protmode = 1; + tcap.tx_chainmask = 1; + + WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap); + + return ret; +} + +static int ath9k_htc_init_rate(struct ath9k_htc_priv *priv, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv; + struct ieee80211_supported_band *sband; + struct ath9k_htc_target_rate trate; + u32 caps = 0; + u8 cmd_rsp; + int i, j, ret; + + memset(&trate, 0, sizeof(trate)); + + /* Only 2GHz is supported */ + sband = priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ]; + + for (i = 0, j = 0; i < sband->n_bitrates; i++) { + if (sta->supp_rates[sband->band] & BIT(i)) { + priv->tgt_rate.rates.legacy_rates.rs_rates[j] + = (sband->bitrates[i].bitrate * 2) / 10; + j++; + } + } + priv->tgt_rate.rates.legacy_rates.rs_nrates = j; + + if (sta->ht_cap.ht_supported) { + for (i = 0, j = 0; i < 77; i++) { + if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8))) + priv->tgt_rate.rates.ht_rates.rs_rates[j++] = i; + if (j == ATH_HTC_RATE_MAX) + break; + } + priv->tgt_rate.rates.ht_rates.rs_nrates = j; + + caps = WLAN_RC_HT_FLAG; + if (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) + caps |= WLAN_RC_40_FLAG; + if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) + caps |= WLAN_RC_SGI_FLAG; + + } + + priv->tgt_rate.sta_index = ista->index; + priv->tgt_rate.isnew = 1; + trate = priv->tgt_rate; + priv->tgt_rate.capflags = caps; + trate.capflags = cpu_to_be32(caps); + + WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, &trate); + if (ret) { + ath_print(common, ATH_DBG_FATAL, + "Unable to initialize Rate information on target\n"); + return ret; + } + + ath_print(common, ATH_DBG_CONFIG, + "Updated target STA: %pM (caps: 0x%x)\n", sta->addr, caps); + return 0; +} + +static bool check_rc_update(struct ieee80211_hw *hw, bool *cw40) +{ + struct ath9k_htc_priv *priv = hw->priv; + struct ieee80211_conf *conf = &hw->conf; + + if (!conf_is_ht(conf)) + return false; + + if (!(priv->op_flags & OP_ASSOCIATED) || + (priv->op_flags & OP_SCANNING)) + return false; + + if (conf_is_ht40(conf)) { + if (priv->ah->curchan->chanmode & + (CHANNEL_HT40PLUS | CHANNEL_HT40MINUS)) { + return false; + } else { + *cw40 = true; + return true; + } + } else { /* ht20 */ + if (priv->ah->curchan->chanmode & CHANNEL_HT20) + return false; + else + return true; + } +} + +static void ath9k_htc_rc_update(struct ath9k_htc_priv *priv, bool is_cw40) +{ + struct ath9k_htc_target_rate trate; + struct ath_common *common = ath9k_hw_common(priv->ah); + int ret; + u8 cmd_rsp; + + memset(&trate, 0, sizeof(trate)); + + trate = priv->tgt_rate; + + if (is_cw40) + priv->tgt_rate.capflags |= WLAN_RC_40_FLAG; + else + priv->tgt_rate.capflags &= ~WLAN_RC_40_FLAG; + + trate.capflags = cpu_to_be32(priv->tgt_rate.capflags); + + WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, &trate); + if (ret) { + ath_print(common, ATH_DBG_FATAL, + "Unable to update Rate information on target\n"); + return; + } + + ath_print(common, ATH_DBG_CONFIG, "Rate control updated with " + "caps:0x%x on target\n", priv->tgt_rate.capflags); +} + +static int ath9k_htc_aggr_oper(struct ath9k_htc_priv *priv, + struct ieee80211_vif *vif, + u8 *sta_addr, u8 tid, bool oper) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_htc_target_aggr aggr; + struct ieee80211_sta *sta = NULL; + struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv; + int ret = 0; + u8 cmd_rsp; + + if (tid > ATH9K_HTC_MAX_TID) + return -EINVAL; + + rcu_read_lock(); + sta = ieee80211_find_sta(vif, sta_addr); + if (sta) { + ista = (struct ath9k_htc_sta *) sta->drv_priv; + } else { + rcu_read_unlock(); + return -EINVAL; + } + + if (!ista) { + rcu_read_unlock(); + return -EINVAL; + } + + memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr)); + + aggr.sta_index = ista->index; + rcu_read_unlock(); + aggr.tidno = tid; + aggr.aggr_enable = oper; + + if (oper) + ista->tid_state[tid] = AGGR_START; + else + ista->tid_state[tid] = AGGR_STOP; + + WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr); + if (ret) + ath_print(common, ATH_DBG_CONFIG, + "Unable to %s TX aggregation for (%pM, %d)\n", + (oper) ? "start" : "stop", sta->addr, tid); + else + ath_print(common, ATH_DBG_CONFIG, + "%s aggregation for (%pM, %d)\n", + (oper) ? "Starting" : "Stopping", sta->addr, tid); + + return ret; +} + +void ath9k_htc_aggr_work(struct work_struct *work) +{ + int ret = 0; + struct ath9k_htc_priv *priv = + container_of(work, struct ath9k_htc_priv, + ath9k_aggr_work.work); + struct ath9k_htc_aggr_work *wk = &priv->aggr_work; + + mutex_lock(&wk->mutex); + + switch (wk->action) { + case IEEE80211_AMPDU_TX_START: + ret = ath9k_htc_aggr_oper(priv, wk->vif, wk->sta_addr, + wk->tid, true); + if (!ret) + ieee80211_start_tx_ba_cb(wk->vif, wk->sta_addr, + wk->tid); + break; + case IEEE80211_AMPDU_TX_STOP: + ath9k_htc_aggr_oper(priv, wk->vif, wk->sta_addr, + wk->tid, false); + ieee80211_stop_tx_ba_cb(wk->vif, wk->sta_addr, wk->tid); + break; + default: + ath_print(ath9k_hw_common(priv->ah), ATH_DBG_FATAL, + "Unknown AMPDU action\n"); + } + + mutex_unlock(&wk->mutex); +} + +/*********/ +/* DEBUG */ +/*********/ + +#ifdef CONFIG_ATH9K_HTC_DEBUGFS + +static int ath9k_debugfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath9k_htc_priv *priv = + (struct ath9k_htc_priv *) file->private_data; + struct ath9k_htc_target_stats cmd_rsp; + char buf[512]; + unsigned int len = 0; + int ret = 0; + + memset(&cmd_rsp, 0, sizeof(cmd_rsp)); + + WMI_CMD(WMI_TGT_STATS_CMDID); + if (ret) + return -EINVAL; + + + len += snprintf(buf + len, sizeof(buf) - len, + "%19s : %10u\n", "TX Short Retries", + be32_to_cpu(cmd_rsp.tx_shortretry)); + len += snprintf(buf + len, sizeof(buf) - len, + "%19s : %10u\n", "TX Long Retries", + be32_to_cpu(cmd_rsp.tx_longretry)); + len += snprintf(buf + len, sizeof(buf) - len, + "%19s : %10u\n", "TX Xretries", + be32_to_cpu(cmd_rsp.tx_xretries)); + len += snprintf(buf + len, sizeof(buf) - len, + "%19s : %10u\n", "TX Unaggr. Xretries", + be32_to_cpu(cmd_rsp.ht_txunaggr_xretry)); + len += snprintf(buf + len, sizeof(buf) - len, + "%19s : %10u\n", "TX Xretries (HT)", + be32_to_cpu(cmd_rsp.ht_tx_xretries)); + len += snprintf(buf + len, sizeof(buf) - len, + "%19s : %10u\n", "TX Rate", priv->debug.txrate); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_tgt_stats = { + .read = read_file_tgt_stats, + .open = ath9k_debugfs_open, + .owner = THIS_MODULE +}; + +static ssize_t read_file_xmit(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath9k_htc_priv *priv = + (struct ath9k_htc_priv *) file->private_data; + char buf[512]; + unsigned int len = 0; + + len += snprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "Buffers queued", + priv->debug.tx_stats.buf_queued); + len += snprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "Buffers completed", + priv->debug.tx_stats.buf_completed); + len += snprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "SKBs queued", + priv->debug.tx_stats.skb_queued); + len += snprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "SKBs completed", + priv->debug.tx_stats.skb_completed); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_xmit = { + .read = read_file_xmit, + .open = ath9k_debugfs_open, + .owner = THIS_MODULE +}; + +static ssize_t read_file_recv(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath9k_htc_priv *priv = + (struct ath9k_htc_priv *) file->private_data; + char buf[512]; + unsigned int len = 0; + + len += snprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "SKBs allocated", + priv->debug.rx_stats.skb_allocated); + len += snprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "SKBs completed", + priv->debug.rx_stats.skb_completed); + len += snprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "SKBs Dropped", + priv->debug.rx_stats.skb_dropped); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_recv = { + .read = read_file_recv, + .open = ath9k_debugfs_open, + .owner = THIS_MODULE +}; + +int ath9k_init_debug(struct ath_hw *ah) +{ + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; + + if (!ath9k_debugfs_root) + return -ENOENT; + + priv->debug.debugfs_phy = debugfs_create_dir(wiphy_name(priv->hw->wiphy), + ath9k_debugfs_root); + if (!priv->debug.debugfs_phy) + goto err; + + priv->debug.debugfs_tgt_stats = debugfs_create_file("tgt_stats", S_IRUSR, + priv->debug.debugfs_phy, + priv, &fops_tgt_stats); + if (!priv->debug.debugfs_tgt_stats) + goto err; + + + priv->debug.debugfs_xmit = debugfs_create_file("xmit", S_IRUSR, + priv->debug.debugfs_phy, + priv, &fops_xmit); + if (!priv->debug.debugfs_xmit) + goto err; + + priv->debug.debugfs_recv = debugfs_create_file("recv", S_IRUSR, + priv->debug.debugfs_phy, + priv, &fops_recv); + if (!priv->debug.debugfs_recv) + goto err; + + return 0; + +err: + ath9k_exit_debug(ah); + return -ENOMEM; +} + +void ath9k_exit_debug(struct ath_hw *ah) +{ + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; + + debugfs_remove(priv->debug.debugfs_recv); + debugfs_remove(priv->debug.debugfs_xmit); + debugfs_remove(priv->debug.debugfs_tgt_stats); + debugfs_remove(priv->debug.debugfs_phy); +} + +int ath9k_debug_create_root(void) +{ + ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL); + if (!ath9k_debugfs_root) + return -ENOENT; + + return 0; +} + +void ath9k_debug_remove_root(void) +{ + debugfs_remove(ath9k_debugfs_root); + ath9k_debugfs_root = NULL; +} + +#endif /* CONFIG_ATH9K_HTC_DEBUGFS */ + +/*******/ +/* ANI */ +/*******/ + +static void ath_start_ani(struct ath9k_htc_priv *priv) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + unsigned long timestamp = jiffies_to_msecs(jiffies); + + common->ani.longcal_timer = timestamp; + common->ani.shortcal_timer = timestamp; + common->ani.checkani_timer = timestamp; + + ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work, + msecs_to_jiffies(ATH_ANI_POLLINTERVAL)); +} + +void ath9k_ani_work(struct work_struct *work) +{ + struct ath9k_htc_priv *priv = + container_of(work, struct ath9k_htc_priv, + ath9k_ani_work.work); + struct ath_hw *ah = priv->ah; + struct ath_common *common = ath9k_hw_common(ah); + bool longcal = false; + bool shortcal = false; + bool aniflag = false; + unsigned int timestamp = jiffies_to_msecs(jiffies); + u32 cal_interval, short_cal_interval; + + short_cal_interval = ATH_STA_SHORT_CALINTERVAL; + + /* Long calibration runs independently of short calibration. */ + if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) { + longcal = true; + ath_print(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies); + common->ani.longcal_timer = timestamp; + } + + /* Short calibration applies only while caldone is false */ + if (!common->ani.caldone) { + if ((timestamp - common->ani.shortcal_timer) >= + short_cal_interval) { + shortcal = true; + ath_print(common, ATH_DBG_ANI, + "shortcal @%lu\n", jiffies); + common->ani.shortcal_timer = timestamp; + common->ani.resetcal_timer = timestamp; + } + } else { + if ((timestamp - common->ani.resetcal_timer) >= + ATH_RESTART_CALINTERVAL) { + common->ani.caldone = ath9k_hw_reset_calvalid(ah); + if (common->ani.caldone) + common->ani.resetcal_timer = timestamp; + } + } + + /* Verify whether we must check ANI */ + if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) { + aniflag = true; + common->ani.checkani_timer = timestamp; + } + + /* Skip all processing if there's nothing to do. */ + if (longcal || shortcal || aniflag) { + /* Call ANI routine if necessary */ + if (aniflag) + ath9k_hw_ani_monitor(ah, ah->curchan); + + /* Perform calibration if necessary */ + if (longcal || shortcal) { + common->ani.caldone = + ath9k_hw_calibrate(ah, ah->curchan, + common->rx_chainmask, + longcal); + + if (longcal) + common->ani.noise_floor = + ath9k_hw_getchan_noise(ah, ah->curchan); + + ath_print(common, ATH_DBG_ANI, + " calibrate chan %u/%x nf: %d\n", + ah->curchan->channel, + ah->curchan->channelFlags, + common->ani.noise_floor); + } + } + + /* + * Set timer interval based on previous results. + * The interval must be the shortest necessary to satisfy ANI, + * short calibration and long calibration. + */ + cal_interval = ATH_LONG_CALINTERVAL; + if (priv->ah->config.enable_ani) + cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL); + if (!common->ani.caldone) + cal_interval = min(cal_interval, (u32)short_cal_interval); + + ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work, + msecs_to_jiffies(cal_interval)); +} + +/*******/ +/* LED */ +/*******/ + +static void ath9k_led_blink_work(struct work_struct *work) +{ + struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv, + ath9k_led_blink_work.work); + + if (!(priv->op_flags & OP_LED_ASSOCIATED)) + return; + + if ((priv->led_on_duration == ATH_LED_ON_DURATION_IDLE) || + (priv->led_off_duration == ATH_LED_OFF_DURATION_IDLE)) + ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 0); + else + ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, + (priv->op_flags & OP_LED_ON) ? 1 : 0); + + ieee80211_queue_delayed_work(priv->hw, + &priv->ath9k_led_blink_work, + (priv->op_flags & OP_LED_ON) ? + msecs_to_jiffies(priv->led_off_duration) : + msecs_to_jiffies(priv->led_on_duration)); + + priv->led_on_duration = priv->led_on_cnt ? + max((ATH_LED_ON_DURATION_IDLE - priv->led_on_cnt), 25) : + ATH_LED_ON_DURATION_IDLE; + priv->led_off_duration = priv->led_off_cnt ? + max((ATH_LED_OFF_DURATION_IDLE - priv->led_off_cnt), 10) : + ATH_LED_OFF_DURATION_IDLE; + priv->led_on_cnt = priv->led_off_cnt = 0; + + if (priv->op_flags & OP_LED_ON) + priv->op_flags &= ~OP_LED_ON; + else + priv->op_flags |= OP_LED_ON; +} + +static void ath9k_led_brightness_work(struct work_struct *work) +{ + struct ath_led *led = container_of(work, struct ath_led, + brightness_work.work); + struct ath9k_htc_priv *priv = led->priv; + + switch (led->brightness) { + case LED_OFF: + if (led->led_type == ATH_LED_ASSOC || + led->led_type == ATH_LED_RADIO) { + ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, + (led->led_type == ATH_LED_RADIO)); + priv->op_flags &= ~OP_LED_ASSOCIATED; + if (led->led_type == ATH_LED_RADIO) + priv->op_flags &= ~OP_LED_ON; + } else { + priv->led_off_cnt++; + } + break; + case LED_FULL: + if (led->led_type == ATH_LED_ASSOC) { + priv->op_flags |= OP_LED_ASSOCIATED; + ieee80211_queue_delayed_work(priv->hw, + &priv->ath9k_led_blink_work, 0); + } else if (led->led_type == ATH_LED_RADIO) { + ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 0); + priv->op_flags |= OP_LED_ON; + } else { + priv->led_on_cnt++; + } + break; + default: + break; + } +} + +static void ath9k_led_brightness(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev); + struct ath9k_htc_priv *priv = led->priv; + + led->brightness = brightness; + if (!(priv->op_flags & OP_LED_DEINIT)) + ieee80211_queue_delayed_work(priv->hw, + &led->brightness_work, 0); +} + +static void ath9k_led_stop_brightness(struct ath9k_htc_priv *priv) +{ + cancel_delayed_work_sync(&priv->radio_led.brightness_work); + cancel_delayed_work_sync(&priv->assoc_led.brightness_work); + cancel_delayed_work_sync(&priv->tx_led.brightness_work); + cancel_delayed_work_sync(&priv->rx_led.brightness_work); +} + +static int ath9k_register_led(struct ath9k_htc_priv *priv, struct ath_led *led, + char *trigger) +{ + int ret; + + led->priv = priv; + led->led_cdev.name = led->name; + led->led_cdev.default_trigger = trigger; + led->led_cdev.brightness_set = ath9k_led_brightness; + + ret = led_classdev_register(wiphy_dev(priv->hw->wiphy), &led->led_cdev); + if (ret) + ath_print(ath9k_hw_common(priv->ah), ATH_DBG_FATAL, + "Failed to register led:%s", led->name); + else + led->registered = 1; + + INIT_DELAYED_WORK(&led->brightness_work, ath9k_led_brightness_work); + + return ret; +} + +static void ath9k_unregister_led(struct ath_led *led) +{ + if (led->registered) { + led_classdev_unregister(&led->led_cdev); + led->registered = 0; + } +} + +void ath9k_deinit_leds(struct ath9k_htc_priv *priv) +{ + priv->op_flags |= OP_LED_DEINIT; + ath9k_unregister_led(&priv->assoc_led); + priv->op_flags &= ~OP_LED_ASSOCIATED; + ath9k_unregister_led(&priv->tx_led); + ath9k_unregister_led(&priv->rx_led); + ath9k_unregister_led(&priv->radio_led); + ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 1); +} + +void ath9k_init_leds(struct ath9k_htc_priv *priv) +{ + char *trigger; + int ret; + + if (AR_SREV_9287(priv->ah)) + priv->ah->led_pin = ATH_LED_PIN_9287; + else if (AR_SREV_9271(priv->ah)) + priv->ah->led_pin = ATH_LED_PIN_9271; + else + priv->ah->led_pin = ATH_LED_PIN_DEF; + + /* Configure gpio 1 for output */ + ath9k_hw_cfg_output(priv->ah, priv->ah->led_pin, + AR_GPIO_OUTPUT_MUX_AS_OUTPUT); + /* LED off, active low */ + ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 1); + + INIT_DELAYED_WORK(&priv->ath9k_led_blink_work, ath9k_led_blink_work); + + trigger = ieee80211_get_radio_led_name(priv->hw); + snprintf(priv->radio_led.name, sizeof(priv->radio_led.name), + "ath9k-%s::radio", wiphy_name(priv->hw->wiphy)); + ret = ath9k_register_led(priv, &priv->radio_led, trigger); + priv->radio_led.led_type = ATH_LED_RADIO; + if (ret) + goto fail; + + trigger = ieee80211_get_assoc_led_name(priv->hw); + snprintf(priv->assoc_led.name, sizeof(priv->assoc_led.name), + "ath9k-%s::assoc", wiphy_name(priv->hw->wiphy)); + ret = ath9k_register_led(priv, &priv->assoc_led, trigger); + priv->assoc_led.led_type = ATH_LED_ASSOC; + if (ret) + goto fail; + + trigger = ieee80211_get_tx_led_name(priv->hw); + snprintf(priv->tx_led.name, sizeof(priv->tx_led.name), + "ath9k-%s::tx", wiphy_name(priv->hw->wiphy)); + ret = ath9k_register_led(priv, &priv->tx_led, trigger); + priv->tx_led.led_type = ATH_LED_TX; + if (ret) + goto fail; + + trigger = ieee80211_get_rx_led_name(priv->hw); + snprintf(priv->rx_led.name, sizeof(priv->rx_led.name), + "ath9k-%s::rx", wiphy_name(priv->hw->wiphy)); + ret = ath9k_register_led(priv, &priv->rx_led, trigger); + priv->rx_led.led_type = ATH_LED_RX; + if (ret) + goto fail; + + priv->op_flags &= ~OP_LED_DEINIT; + + return; + +fail: + cancel_delayed_work_sync(&priv->ath9k_led_blink_work); + ath9k_deinit_leds(priv); +} + +/*******************/ +/* Rfkill */ +/*******************/ + +static bool ath_is_rfkill_set(struct ath9k_htc_priv *priv) +{ + return ath9k_hw_gpio_get(priv->ah, priv->ah->rfkill_gpio) == + priv->ah->rfkill_polarity; +} + +static void ath9k_htc_rfkill_poll_state(struct ieee80211_hw *hw) +{ + struct ath9k_htc_priv *priv = hw->priv; + bool blocked = !!ath_is_rfkill_set(priv); + + wiphy_rfkill_set_hw_state(hw->wiphy, blocked); +} + +void ath9k_start_rfkill_poll(struct ath9k_htc_priv *priv) +{ + if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) + wiphy_rfkill_start_polling(priv->hw->wiphy); +} + +/**********************/ +/* mac80211 Callbacks */ +/**********************/ + +static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr; + struct ath9k_htc_priv *priv = hw->priv; + int padpos, padsize; + + hdr = (struct ieee80211_hdr *) skb->data; + + /* Add the padding after the header if this is not already done */ + padpos = ath9k_cmn_padpos(hdr->frame_control); + padsize = padpos & 3; + if (padsize && skb->len > padpos) { + if (skb_headroom(skb) < padsize) + return -1; + skb_push(skb, padsize); + memmove(skb->data, skb->data + padsize, padpos); + } + + if (ath9k_htc_tx_start(priv, skb) != 0) { + ath_print(ath9k_hw_common(priv->ah), ATH_DBG_XMIT, "Tx failed"); + goto fail_tx; + } + + return 0; + +fail_tx: + dev_kfree_skb_any(skb); + return 0; +} + +static int ath9k_htc_start(struct ieee80211_hw *hw) +{ + struct ath9k_htc_priv *priv = hw->priv; + struct ath_hw *ah = priv->ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ieee80211_channel *curchan = hw->conf.channel; + struct ath9k_channel *init_channel; + int ret = 0; + enum htc_phymode mode; + u16 htc_mode; + u8 cmd_rsp; + + ath_print(common, ATH_DBG_CONFIG, + "Starting driver with initial channel: %d MHz\n", + curchan->center_freq); + + mutex_lock(&priv->mutex); + + /* setup initial channel */ + init_channel = ath9k_cmn_get_curchannel(hw, ah); + + /* Reset SERDES registers */ + ath9k_hw_configpcipowersave(ah, 0, 0); + + ath9k_hw_htc_resetinit(ah); + ret = ath9k_hw_reset(ah, init_channel, false); + if (ret) { + ath_print(common, ATH_DBG_FATAL, + "Unable to reset hardware; reset status %d " + "(freq %u MHz)\n", ret, curchan->center_freq); + goto mutex_unlock; + } + + ath_update_txpow(priv); + + mode = ath9k_htc_get_curmode(priv, init_channel); + htc_mode = cpu_to_be16(mode); + WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode); + if (ret) + goto mutex_unlock; + + WMI_CMD(WMI_ATH_INIT_CMDID); + if (ret) + goto mutex_unlock; + + WMI_CMD(WMI_START_RECV_CMDID); + if (ret) + goto mutex_unlock; + + ath9k_host_rx_init(priv); + + priv->op_flags &= ~OP_INVALID; + htc_start(priv->htc); + +mutex_unlock: + mutex_unlock(&priv->mutex); + return ret; +} + +static void ath9k_htc_stop(struct ieee80211_hw *hw) +{ + struct ath9k_htc_priv *priv = hw->priv; + struct ath_hw *ah = priv->ah; + struct ath_common *common = ath9k_hw_common(ah); + int ret = 0; + u8 cmd_rsp; + + mutex_lock(&priv->mutex); + + if (priv->op_flags & OP_INVALID) { + ath_print(common, ATH_DBG_ANY, "Device not present\n"); + mutex_unlock(&priv->mutex); + return; + } + + htc_stop(priv->htc); + WMI_CMD(WMI_DISABLE_INTR_CMDID); + WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); + WMI_CMD(WMI_STOP_RECV_CMDID); + ath9k_hw_phy_disable(ah); + ath9k_hw_disable(ah); + ath9k_hw_configpcipowersave(ah, 1, 1); + ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); + + cancel_delayed_work_sync(&priv->ath9k_ani_work); + cancel_delayed_work_sync(&priv->ath9k_aggr_work); + cancel_delayed_work_sync(&priv->ath9k_led_blink_work); + ath9k_led_stop_brightness(priv); + skb_queue_purge(&priv->tx_queue); + + /* Remove monitor interface here */ + if (ah->opmode == NL80211_IFTYPE_MONITOR) { + if (ath9k_htc_remove_monitor_interface(priv)) + ath_print(common, ATH_DBG_FATAL, + "Unable to remove monitor interface\n"); + else + ath_print(common, ATH_DBG_CONFIG, + "Monitor interface removed\n"); + } + + priv->op_flags |= OP_INVALID; + mutex_unlock(&priv->mutex); + + ath_print(common, ATH_DBG_CONFIG, "Driver halt\n"); +} + +static int ath9k_htc_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath9k_htc_priv *priv = hw->priv; + struct ath9k_htc_vif *avp = (void *)vif->drv_priv; + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_htc_target_vif hvif; + int ret = 0; + u8 cmd_rsp; + + mutex_lock(&priv->mutex); + + /* Only one interface for now */ + if (priv->nvifs > 0) { + ret = -ENOBUFS; + goto out; + } + + memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); + memcpy(&hvif.myaddr, vif->addr, ETH_ALEN); + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + hvif.opmode = cpu_to_be32(HTC_M_STA); + break; + case NL80211_IFTYPE_ADHOC: + hvif.opmode = cpu_to_be32(HTC_M_IBSS); + break; + default: + ath_print(common, ATH_DBG_FATAL, + "Interface type %d not yet supported\n", vif->type); + ret = -EOPNOTSUPP; + goto out; + } + + ath_print(common, ATH_DBG_CONFIG, + "Attach a VIF of type: %d\n", vif->type); + + priv->ah->opmode = vif->type; + + /* Index starts from zero on the target */ + avp->index = hvif.index = priv->nvifs; + hvif.rtsthreshold = cpu_to_be16(2304); + WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif); + if (ret) + goto out; + + priv->nvifs++; + + /* + * We need a node in target to tx mgmt frames + * before association. + */ + ret = ath9k_htc_add_station(priv, vif, NULL); + if (ret) + goto out; + + ret = ath9k_htc_update_cap_target(priv); + if (ret) + ath_print(common, ATH_DBG_CONFIG, "Failed to update" + " capability in target \n"); + + priv->vif = vif; +out: + mutex_unlock(&priv->mutex); + return ret; +} + +static void ath9k_htc_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath9k_htc_priv *priv = hw->priv; + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_htc_vif *avp = (void *)vif->drv_priv; + struct ath9k_htc_target_vif hvif; + int ret = 0; + u8 cmd_rsp; + + ath_print(common, ATH_DBG_CONFIG, "Detach Interface\n"); + + mutex_lock(&priv->mutex); + + memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); + memcpy(&hvif.myaddr, vif->addr, ETH_ALEN); + hvif.index = avp->index; + WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); + priv->nvifs--; + + ath9k_htc_remove_station(priv, vif, NULL); + + if (vif->type == NL80211_IFTYPE_ADHOC) { + spin_lock_bh(&priv->beacon_lock); + if (priv->beacon) + dev_kfree_skb_any(priv->beacon); + priv->beacon = NULL; + spin_unlock_bh(&priv->beacon_lock); + } + + priv->vif = NULL; + + mutex_unlock(&priv->mutex); +} + +static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed) +{ + struct ath9k_htc_priv *priv = hw->priv; + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ieee80211_conf *conf = &hw->conf; + + mutex_lock(&priv->mutex); + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + struct ieee80211_channel *curchan = hw->conf.channel; + int pos = curchan->hw_value; + bool is_cw40 = false; + + ath_print(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n", + curchan->center_freq); + + if (check_rc_update(hw, &is_cw40)) + ath9k_htc_rc_update(priv, is_cw40); + + ath9k_cmn_update_ichannel(hw, &priv->ah->channels[pos]); + + if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) { + ath_print(common, ATH_DBG_FATAL, + "Unable to set channel\n"); + mutex_unlock(&priv->mutex); + return -EINVAL; + } + + } + + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { + if (conf->flags & IEEE80211_CONF_MONITOR) { + if (ath9k_htc_add_monitor_interface(priv)) + ath_print(common, ATH_DBG_FATAL, + "Failed to set monitor mode\n"); + else + ath_print(common, ATH_DBG_CONFIG, + "HW opmode set to Monitor mode\n"); + } + } + + mutex_unlock(&priv->mutex); + + return 0; +} + +#define SUPPORTED_FILTERS \ + (FIF_PROMISC_IN_BSS | \ + FIF_ALLMULTI | \ + FIF_CONTROL | \ + FIF_PSPOLL | \ + FIF_OTHER_BSS | \ + FIF_BCN_PRBRESP_PROMISC | \ + FIF_FCSFAIL) + +static void ath9k_htc_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct ath9k_htc_priv *priv = hw->priv; + u32 rfilt; + + mutex_lock(&priv->mutex); + + changed_flags &= SUPPORTED_FILTERS; + *total_flags &= SUPPORTED_FILTERS; + + priv->rxfilter = *total_flags; + rfilt = ath9k_cmn_calcrxfilter(hw, priv->ah, priv->rxfilter); + ath9k_hw_setrxfilter(priv->ah, rfilt); + + ath_print(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG, + "Set HW RX filter: 0x%x\n", rfilt); + + mutex_unlock(&priv->mutex); +} + +static void ath9k_htc_sta_notify(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum sta_notify_cmd cmd, + struct ieee80211_sta *sta) +{ + struct ath9k_htc_priv *priv = hw->priv; + int ret; + + switch (cmd) { + case STA_NOTIFY_ADD: + ret = ath9k_htc_add_station(priv, vif, sta); + if (!ret) + ath9k_htc_init_rate(priv, vif, sta); + break; + case STA_NOTIFY_REMOVE: + ath9k_htc_remove_station(priv, vif, sta); + break; + default: + break; + } +} + +static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct ath9k_htc_priv *priv = hw->priv; + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_tx_queue_info qi; + int ret = 0, qnum; + + if (queue >= WME_NUM_AC) + return 0; + + mutex_lock(&priv->mutex); + + memset(&qi, 0, sizeof(struct ath9k_tx_queue_info)); + + qi.tqi_aifs = params->aifs; + qi.tqi_cwmin = params->cw_min; + qi.tqi_cwmax = params->cw_max; + qi.tqi_burstTime = params->txop; + + qnum = get_hw_qnum(queue, priv->hwq_map); + + ath_print(common, ATH_DBG_CONFIG, + "Configure tx [queue/hwq] [%d/%d], " + "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n", + queue, qnum, params->aifs, params->cw_min, + params->cw_max, params->txop); + + ret = ath_txq_update(priv, qnum, &qi); + if (ret) + ath_print(common, ATH_DBG_FATAL, "TXQ Update failed\n"); + + mutex_unlock(&priv->mutex); + + return ret; +} + +static int ath9k_htc_set_key(struct ieee80211_hw *hw, + enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct ath9k_htc_priv *priv = hw->priv; + struct ath_common *common = ath9k_hw_common(priv->ah); + int ret = 0; + + if (modparam_nohwcrypt) + return -ENOSPC; + + mutex_lock(&priv->mutex); + ath_print(common, ATH_DBG_CONFIG, "Set HW Key\n"); + + switch (cmd) { + case SET_KEY: + ret = ath9k_cmn_key_config(common, vif, sta, key); + if (ret >= 0) { + key->hw_key_idx = ret; + /* push IV and Michael MIC generation to stack */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + if (key->alg == ALG_TKIP) + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; + if (priv->ah->sw_mgmt_crypto && key->alg == ALG_CCMP) + key->flags |= IEEE80211_KEY_FLAG_SW_MGMT; + ret = 0; + } + break; + case DISABLE_KEY: + ath9k_cmn_key_delete(common, key); + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&priv->mutex); + + return ret; +} + +static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changed) +{ + struct ath9k_htc_priv *priv = hw->priv; + struct ath_hw *ah = priv->ah; + struct ath_common *common = ath9k_hw_common(ah); + + mutex_lock(&priv->mutex); + + if (changed & BSS_CHANGED_ASSOC) { + common->curaid = bss_conf->assoc ? + bss_conf->aid : 0; + ath_print(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n", + bss_conf->assoc); + + if (bss_conf->assoc) { + priv->op_flags |= OP_ASSOCIATED; + ath_start_ani(priv); + } else { + priv->op_flags &= ~OP_ASSOCIATED; + cancel_delayed_work_sync(&priv->ath9k_ani_work); + } + } + + if (changed & BSS_CHANGED_BSSID) { + /* Set BSSID */ + memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); + ath9k_hw_write_associd(ah); + + ath_print(common, ATH_DBG_CONFIG, + "BSSID: %pM aid: 0x%x\n", + common->curbssid, common->curaid); + } + + if ((changed & BSS_CHANGED_BEACON_INT) || + (changed & BSS_CHANGED_BEACON) || + ((changed & BSS_CHANGED_BEACON_ENABLED) && + bss_conf->enable_beacon)) { + priv->op_flags |= OP_ENABLE_BEACON; + ath9k_htc_beacon_config(priv, vif, bss_conf); + } + + if (changed & BSS_CHANGED_BEACON) + ath9k_htc_beacon_update(priv, vif); + + if ((changed & BSS_CHANGED_BEACON_ENABLED) && + !bss_conf->enable_beacon) { + priv->op_flags &= ~OP_ENABLE_BEACON; + ath9k_htc_beacon_config(priv, vif, bss_conf); + } + + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + ath_print(common, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n", + bss_conf->use_short_preamble); + if (bss_conf->use_short_preamble) + priv->op_flags |= OP_PREAMBLE_SHORT; + else + priv->op_flags &= ~OP_PREAMBLE_SHORT; + } + + if (changed & BSS_CHANGED_ERP_CTS_PROT) { + ath_print(common, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n", + bss_conf->use_cts_prot); + if (bss_conf->use_cts_prot && + hw->conf.channel->band != IEEE80211_BAND_5GHZ) + priv->op_flags |= OP_PROTECT_ENABLE; + else + priv->op_flags &= ~OP_PROTECT_ENABLE; + } + + if (changed & BSS_CHANGED_ERP_SLOT) { + if (bss_conf->use_short_slot) + ah->slottime = 9; + else + ah->slottime = 20; + + ath9k_hw_init_global_settings(ah); + } + + mutex_unlock(&priv->mutex); +} + +static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw) +{ + struct ath9k_htc_priv *priv = hw->priv; + u64 tsf; + + mutex_lock(&priv->mutex); + tsf = ath9k_hw_gettsf64(priv->ah); + mutex_unlock(&priv->mutex); + + return tsf; +} + +static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf) +{ + struct ath9k_htc_priv *priv = hw->priv; + + mutex_lock(&priv->mutex); + ath9k_hw_settsf64(priv->ah, tsf); + mutex_unlock(&priv->mutex); +} + +static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw) +{ + struct ath9k_htc_priv *priv = hw->priv; + + mutex_lock(&priv->mutex); + ath9k_hw_reset_tsf(priv->ah); + mutex_unlock(&priv->mutex); +} + +static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, + u16 tid, u16 *ssn) +{ + struct ath9k_htc_priv *priv = hw->priv; + struct ath9k_htc_aggr_work *work = &priv->aggr_work; + struct ath9k_htc_sta *ista; + + switch (action) { + case IEEE80211_AMPDU_RX_START: + break; + case IEEE80211_AMPDU_RX_STOP: + break; + case IEEE80211_AMPDU_TX_START: + case IEEE80211_AMPDU_TX_STOP: + if (!(priv->op_flags & OP_TXAGGR)) + return -ENOTSUPP; + memcpy(work->sta_addr, sta->addr, ETH_ALEN); + work->hw = hw; + work->vif = vif; + work->action = action; + work->tid = tid; + ieee80211_queue_delayed_work(hw, &priv->ath9k_aggr_work, 0); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + ista = (struct ath9k_htc_sta *) sta->drv_priv; + ista->tid_state[tid] = AGGR_OPERATIONAL; + break; + default: + ath_print(ath9k_hw_common(priv->ah), ATH_DBG_FATAL, + "Unknown AMPDU action\n"); + } + + return 0; +} + +static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw) +{ + struct ath9k_htc_priv *priv = hw->priv; + + mutex_lock(&priv->mutex); + spin_lock_bh(&priv->beacon_lock); + priv->op_flags |= OP_SCANNING; + spin_unlock_bh(&priv->beacon_lock); + cancel_delayed_work_sync(&priv->ath9k_ani_work); + mutex_unlock(&priv->mutex); +} + +static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw) +{ + struct ath9k_htc_priv *priv = hw->priv; + + mutex_lock(&priv->mutex); + spin_lock_bh(&priv->beacon_lock); + priv->op_flags &= ~OP_SCANNING; + spin_unlock_bh(&priv->beacon_lock); + priv->op_flags |= OP_FULL_RESET; + ath_start_ani(priv); + mutex_unlock(&priv->mutex); +} + +static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + return 0; +} + +static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw, + u8 coverage_class) +{ + struct ath9k_htc_priv *priv = hw->priv; + + mutex_lock(&priv->mutex); + priv->ah->coverage_class = coverage_class; + ath9k_hw_init_global_settings(priv->ah); + mutex_unlock(&priv->mutex); +} + +struct ieee80211_ops ath9k_htc_ops = { + .tx = ath9k_htc_tx, + .start = ath9k_htc_start, + .stop = ath9k_htc_stop, + .add_interface = ath9k_htc_add_interface, + .remove_interface = ath9k_htc_remove_interface, + .config = ath9k_htc_config, + .configure_filter = ath9k_htc_configure_filter, + .sta_notify = ath9k_htc_sta_notify, + .conf_tx = ath9k_htc_conf_tx, + .bss_info_changed = ath9k_htc_bss_info_changed, + .set_key = ath9k_htc_set_key, + .get_tsf = ath9k_htc_get_tsf, + .set_tsf = ath9k_htc_set_tsf, + .reset_tsf = ath9k_htc_reset_tsf, + .ampdu_action = ath9k_htc_ampdu_action, + .sw_scan_start = ath9k_htc_sw_scan_start, + .sw_scan_complete = ath9k_htc_sw_scan_complete, + .set_rts_threshold = ath9k_htc_set_rts_threshold, + .rfkill_poll = ath9k_htc_rfkill_poll_state, + .set_coverage_class = ath9k_htc_set_coverage_class, +}; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c new file mode 100644 index 000000000000..dba22d3f87c3 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -0,0 +1,604 @@ +/* + * Copyright (c) 2010 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "htc.h" + +/******/ +/* TX */ +/******/ + +int get_hw_qnum(u16 queue, int *hwq_map) +{ + switch (queue) { + case 0: + return hwq_map[ATH9K_WME_AC_VO]; + case 1: + return hwq_map[ATH9K_WME_AC_VI]; + case 2: + return hwq_map[ATH9K_WME_AC_BE]; + case 3: + return hwq_map[ATH9K_WME_AC_BK]; + default: + return hwq_map[ATH9K_WME_AC_BE]; + } +} + +int ath_txq_update(struct ath9k_htc_priv *priv, int qnum, + struct ath9k_tx_queue_info *qinfo) +{ + struct ath_hw *ah = priv->ah; + int error = 0; + struct ath9k_tx_queue_info qi; + + ath9k_hw_get_txq_props(ah, qnum, &qi); + + qi.tqi_aifs = qinfo->tqi_aifs; + qi.tqi_cwmin = qinfo->tqi_cwmin / 2; /* XXX */ + qi.tqi_cwmax = qinfo->tqi_cwmax; + qi.tqi_burstTime = qinfo->tqi_burstTime; + qi.tqi_readyTime = qinfo->tqi_readyTime; + + if (!ath9k_hw_set_txq_props(ah, qnum, &qi)) { + ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL, + "Unable to update hardware queue %u!\n", qnum); + error = -EIO; + } else { + ath9k_hw_resettxqueue(ah, qnum); + } + + return error; +} + +int ath9k_htc_tx_start(struct ath9k_htc_priv *priv, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_sta *sta = tx_info->control.sta; + struct ath9k_htc_sta *ista; + struct ath9k_htc_vif *avp; + struct ath9k_htc_tx_ctl tx_ctl; + enum htc_endpoint_id epid; + u16 qnum, hw_qnum; + __le16 fc; + u8 *tx_fhdr; + u8 sta_idx; + + hdr = (struct ieee80211_hdr *) skb->data; + fc = hdr->frame_control; + + avp = (struct ath9k_htc_vif *) tx_info->control.vif->drv_priv; + if (sta) { + ista = (struct ath9k_htc_sta *) sta->drv_priv; + sta_idx = ista->index; + } else { + sta_idx = 0; + } + + memset(&tx_ctl, 0, sizeof(struct ath9k_htc_tx_ctl)); + + if (ieee80211_is_data(fc)) { + struct tx_frame_hdr tx_hdr; + u8 *qc; + + memset(&tx_hdr, 0, sizeof(struct tx_frame_hdr)); + + tx_hdr.node_idx = sta_idx; + tx_hdr.vif_idx = avp->index; + + if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) { + tx_ctl.type = ATH9K_HTC_AMPDU; + tx_hdr.data_type = ATH9K_HTC_AMPDU; + } else { + tx_ctl.type = ATH9K_HTC_NORMAL; + tx_hdr.data_type = ATH9K_HTC_NORMAL; + } + + if (ieee80211_is_data(fc)) { + qc = ieee80211_get_qos_ctl(hdr); + tx_hdr.tidno = qc[0] & IEEE80211_QOS_CTL_TID_MASK; + } + + /* Check for RTS protection */ + if (priv->hw->wiphy->rts_threshold != (u32) -1) + if (skb->len > priv->hw->wiphy->rts_threshold) + tx_hdr.flags |= ATH9K_HTC_TX_RTSCTS; + + /* CTS-to-self */ + if (!(tx_hdr.flags & ATH9K_HTC_TX_RTSCTS) && + (priv->op_flags & OP_PROTECT_ENABLE)) + tx_hdr.flags |= ATH9K_HTC_TX_CTSONLY; + + tx_hdr.key_type = ath9k_cmn_get_hw_crypto_keytype(skb); + if (tx_hdr.key_type == ATH9K_KEY_TYPE_CLEAR) + tx_hdr.keyix = (u8) ATH9K_TXKEYIX_INVALID; + else + tx_hdr.keyix = tx_info->control.hw_key->hw_key_idx; + + tx_fhdr = skb_push(skb, sizeof(tx_hdr)); + memcpy(tx_fhdr, (u8 *) &tx_hdr, sizeof(tx_hdr)); + + qnum = skb_get_queue_mapping(skb); + hw_qnum = get_hw_qnum(qnum, priv->hwq_map); + + switch (hw_qnum) { + case 0: + epid = priv->data_be_ep; + break; + case 2: + epid = priv->data_vi_ep; + break; + case 3: + epid = priv->data_vo_ep; + break; + case 1: + default: + epid = priv->data_bk_ep; + break; + } + } else { + struct tx_mgmt_hdr mgmt_hdr; + + memset(&mgmt_hdr, 0, sizeof(struct tx_mgmt_hdr)); + + tx_ctl.type = ATH9K_HTC_NORMAL; + + mgmt_hdr.node_idx = sta_idx; + mgmt_hdr.vif_idx = avp->index; + mgmt_hdr.tidno = 0; + mgmt_hdr.flags = 0; + + mgmt_hdr.key_type = ath9k_cmn_get_hw_crypto_keytype(skb); + if (mgmt_hdr.key_type == ATH9K_KEY_TYPE_CLEAR) + mgmt_hdr.keyix = (u8) ATH9K_TXKEYIX_INVALID; + else + mgmt_hdr.keyix = tx_info->control.hw_key->hw_key_idx; + + tx_fhdr = skb_push(skb, sizeof(mgmt_hdr)); + memcpy(tx_fhdr, (u8 *) &mgmt_hdr, sizeof(mgmt_hdr)); + epid = priv->mgmt_ep; + } + + return htc_send(priv->htc, skb, epid, &tx_ctl); +} + +void ath9k_tx_tasklet(unsigned long data) +{ + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data; + struct ieee80211_sta *sta; + struct ieee80211_hdr *hdr; + struct ieee80211_tx_info *tx_info; + struct sk_buff *skb = NULL; + __le16 fc; + + while ((skb = skb_dequeue(&priv->tx_queue)) != NULL) { + + hdr = (struct ieee80211_hdr *) skb->data; + fc = hdr->frame_control; + tx_info = IEEE80211_SKB_CB(skb); + sta = tx_info->control.sta; + + rcu_read_lock(); + + if (sta && conf_is_ht(&priv->hw->conf) && + (priv->op_flags & OP_TXAGGR) + && !(skb->protocol == cpu_to_be16(ETH_P_PAE))) { + if (ieee80211_is_data_qos(fc)) { + u8 *qc, tid; + struct ath9k_htc_sta *ista; + + qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & 0xf; + ista = (struct ath9k_htc_sta *)sta->drv_priv; + + if ((tid < ATH9K_HTC_MAX_TID) && + ista->tid_state[tid] == AGGR_STOP) { + ieee80211_start_tx_ba_session(sta, tid); + ista->tid_state[tid] = AGGR_PROGRESS; + } + } + } + + rcu_read_unlock(); + + memset(&tx_info->status, 0, sizeof(tx_info->status)); + ieee80211_tx_status(priv->hw, skb); + } +} + +void ath9k_htc_txep(void *drv_priv, struct sk_buff *skb, + enum htc_endpoint_id ep_id, bool txok) +{ + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) drv_priv; + struct ieee80211_tx_info *tx_info; + + if (!skb) + return; + + if (ep_id == priv->mgmt_ep) + skb_pull(skb, sizeof(struct tx_mgmt_hdr)); + else + /* TODO: Check for cab/uapsd/data */ + skb_pull(skb, sizeof(struct tx_frame_hdr)); + + tx_info = IEEE80211_SKB_CB(skb); + + if (txok) + tx_info->flags |= IEEE80211_TX_STAT_ACK; + + skb_queue_tail(&priv->tx_queue, skb); + tasklet_schedule(&priv->tx_tasklet); +} + +int ath9k_tx_init(struct ath9k_htc_priv *priv) +{ + skb_queue_head_init(&priv->tx_queue); + return 0; +} + +void ath9k_tx_cleanup(struct ath9k_htc_priv *priv) +{ + +} + +bool ath9k_htc_txq_setup(struct ath9k_htc_priv *priv, + enum ath9k_tx_queue_subtype subtype) +{ + struct ath_hw *ah = priv->ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_tx_queue_info qi; + int qnum; + + memset(&qi, 0, sizeof(qi)); + + qi.tqi_subtype = subtype; + qi.tqi_aifs = ATH9K_TXQ_USEDEFAULT; + qi.tqi_cwmin = ATH9K_TXQ_USEDEFAULT; + qi.tqi_cwmax = ATH9K_TXQ_USEDEFAULT; + qi.tqi_physCompBuf = 0; + qi.tqi_qflags = TXQ_FLAG_TXEOLINT_ENABLE | TXQ_FLAG_TXDESCINT_ENABLE; + + qnum = ath9k_hw_setuptxqueue(priv->ah, ATH9K_TX_QUEUE_DATA, &qi); + if (qnum == -1) + return false; + + if (qnum >= ARRAY_SIZE(priv->hwq_map)) { + ath_print(common, ATH_DBG_FATAL, + "qnum %u out of range, max %u!\n", + qnum, (unsigned int)ARRAY_SIZE(priv->hwq_map)); + ath9k_hw_releasetxqueue(ah, qnum); + return false; + } + + priv->hwq_map[subtype] = qnum; + return true; +} + +/******/ +/* RX */ +/******/ + +void ath9k_host_rx_init(struct ath9k_htc_priv *priv) +{ + ath9k_hw_rxena(priv->ah); + ath9k_cmn_opmode_init(priv->hw, priv->ah, priv->rxfilter); + ath9k_hw_startpcureceive(priv->ah); + priv->rx.last_rssi = ATH_RSSI_DUMMY_MARKER; +} + +static void ath9k_process_rate(struct ieee80211_hw *hw, + struct ieee80211_rx_status *rxs, + u8 rx_rate, u8 rs_flags) +{ + struct ieee80211_supported_band *sband; + enum ieee80211_band band; + unsigned int i = 0; + + if (rx_rate & 0x80) { + /* HT rate */ + rxs->flag |= RX_FLAG_HT; + if (rs_flags & ATH9K_RX_2040) + rxs->flag |= RX_FLAG_40MHZ; + if (rs_flags & ATH9K_RX_GI) + rxs->flag |= RX_FLAG_SHORT_GI; + rxs->rate_idx = rx_rate & 0x7f; + return; + } + + band = hw->conf.channel->band; + sband = hw->wiphy->bands[band]; + + for (i = 0; i < sband->n_bitrates; i++) { + if (sband->bitrates[i].hw_value == rx_rate) { + rxs->rate_idx = i; + return; + } + if (sband->bitrates[i].hw_value_short == rx_rate) { + rxs->rate_idx = i; + rxs->flag |= RX_FLAG_SHORTPRE; + return; + } + } + +} + +static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv, + struct ath9k_htc_rxbuf *rxbuf, + struct ieee80211_rx_status *rx_status) + +{ + struct ieee80211_hdr *hdr; + struct ieee80211_hw *hw = priv->hw; + struct sk_buff *skb = rxbuf->skb; + struct ath_common *common = ath9k_hw_common(priv->ah); + int hdrlen, padpos, padsize; + int last_rssi = ATH_RSSI_DUMMY_MARKER; + __le16 fc; + + hdr = (struct ieee80211_hdr *)skb->data; + fc = hdr->frame_control; + hdrlen = ieee80211_get_hdrlen_from_skb(skb); + + padpos = ath9k_cmn_padpos(fc); + + padsize = padpos & 3; + if (padsize && skb->len >= padpos+padsize) { + memmove(skb->data + padsize, skb->data, padpos); + skb_pull(skb, padsize); + } + + memset(rx_status, 0, sizeof(struct ieee80211_rx_status)); + + if (rxbuf->rxstatus.rs_status != 0) { + if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_CRC) + rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; + if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_PHY) + goto rx_next; + + if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_DECRYPT) { + /* FIXME */ + } else if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_MIC) { + if (ieee80211_is_ctl(fc)) + /* + * Sometimes, we get invalid + * MIC failures on valid control frames. + * Remove these mic errors. + */ + rxbuf->rxstatus.rs_status &= ~ATH9K_RXERR_MIC; + else + rx_status->flag |= RX_FLAG_MMIC_ERROR; + } + + /* + * Reject error frames with the exception of + * decryption and MIC failures. For monitor mode, + * we also ignore the CRC error. + */ + if (priv->ah->opmode == NL80211_IFTYPE_MONITOR) { + if (rxbuf->rxstatus.rs_status & + ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC | + ATH9K_RXERR_CRC)) + goto rx_next; + } else { + if (rxbuf->rxstatus.rs_status & + ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC)) { + goto rx_next; + } + } + } + + if (!(rxbuf->rxstatus.rs_status & ATH9K_RXERR_DECRYPT)) { + u8 keyix; + keyix = rxbuf->rxstatus.rs_keyix; + if (keyix != ATH9K_RXKEYIX_INVALID) { + rx_status->flag |= RX_FLAG_DECRYPTED; + } else if (ieee80211_has_protected(fc) && + skb->len >= hdrlen + 4) { + keyix = skb->data[hdrlen + 3] >> 6; + if (test_bit(keyix, common->keymap)) + rx_status->flag |= RX_FLAG_DECRYPTED; + } + } + + ath9k_process_rate(hw, rx_status, rxbuf->rxstatus.rs_rate, + rxbuf->rxstatus.rs_flags); + + if (priv->op_flags & OP_ASSOCIATED) { + if (rxbuf->rxstatus.rs_rssi != ATH9K_RSSI_BAD && + !rxbuf->rxstatus.rs_moreaggr) + ATH_RSSI_LPF(priv->rx.last_rssi, + rxbuf->rxstatus.rs_rssi); + + last_rssi = priv->rx.last_rssi; + + if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER)) + rxbuf->rxstatus.rs_rssi = ATH_EP_RND(last_rssi, + ATH_RSSI_EP_MULTIPLIER); + + if (rxbuf->rxstatus.rs_rssi < 0) + rxbuf->rxstatus.rs_rssi = 0; + + if (ieee80211_is_beacon(fc)) + priv->ah->stats.avgbrssi = rxbuf->rxstatus.rs_rssi; + } + + rx_status->mactime = rxbuf->rxstatus.rs_tstamp; + rx_status->band = hw->conf.channel->band; + rx_status->freq = hw->conf.channel->center_freq; + rx_status->signal = rxbuf->rxstatus.rs_rssi + ATH_DEFAULT_NOISE_FLOOR; + rx_status->antenna = rxbuf->rxstatus.rs_antenna; + rx_status->flag |= RX_FLAG_TSFT; + + return true; + +rx_next: + return false; +} + +/* + * FIXME: Handle FLUSH later on. + */ +void ath9k_rx_tasklet(unsigned long data) +{ + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data; + struct ath9k_htc_rxbuf *rxbuf = NULL, *tmp_buf = NULL; + struct ieee80211_rx_status rx_status; + struct sk_buff *skb; + unsigned long flags; + + + do { + spin_lock_irqsave(&priv->rx.rxbuflock, flags); + list_for_each_entry(tmp_buf, &priv->rx.rxbuf, list) { + if (tmp_buf->in_process) { + rxbuf = tmp_buf; + break; + } + } + + if (rxbuf == NULL) { + spin_unlock_irqrestore(&priv->rx.rxbuflock, flags); + break; + } + + if (!rxbuf->skb) + goto requeue; + + if (!ath9k_rx_prepare(priv, rxbuf, &rx_status)) { + dev_kfree_skb_any(rxbuf->skb); + goto requeue; + } + + memcpy(IEEE80211_SKB_RXCB(rxbuf->skb), &rx_status, + sizeof(struct ieee80211_rx_status)); + skb = rxbuf->skb; + spin_unlock_irqrestore(&priv->rx.rxbuflock, flags); + + ieee80211_rx(priv->hw, skb); + + spin_lock_irqsave(&priv->rx.rxbuflock, flags); +requeue: + rxbuf->in_process = false; + rxbuf->skb = NULL; + list_move_tail(&rxbuf->list, &priv->rx.rxbuf); + rxbuf = NULL; + spin_unlock_irqrestore(&priv->rx.rxbuflock, flags); + } while (1); + +} + +void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb, + enum htc_endpoint_id ep_id) +{ + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)drv_priv; + struct ath_hw *ah = priv->ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_htc_rxbuf *rxbuf = NULL, *tmp_buf = NULL; + struct ath_htc_rx_status *rxstatus; + u32 len = 0; + + spin_lock(&priv->rx.rxbuflock); + list_for_each_entry(tmp_buf, &priv->rx.rxbuf, list) { + if (!tmp_buf->in_process) { + rxbuf = tmp_buf; + break; + } + } + spin_unlock(&priv->rx.rxbuflock); + + if (rxbuf == NULL) { + ath_print(common, ATH_DBG_ANY, + "No free RX buffer\n"); + goto err; + } + + len = skb->len; + if (len <= HTC_RX_FRAME_HEADER_SIZE) { + ath_print(common, ATH_DBG_FATAL, + "Corrupted RX frame, dropping\n"); + goto err; + } + + rxstatus = (struct ath_htc_rx_status *)skb->data; + + rxstatus->rs_tstamp = be64_to_cpu(rxstatus->rs_tstamp); + rxstatus->rs_datalen = be16_to_cpu(rxstatus->rs_datalen); + rxstatus->evm0 = be32_to_cpu(rxstatus->evm0); + rxstatus->evm1 = be32_to_cpu(rxstatus->evm1); + rxstatus->evm2 = be32_to_cpu(rxstatus->evm2); + + if (rxstatus->rs_datalen - (len - HTC_RX_FRAME_HEADER_SIZE) != 0) { + ath_print(common, ATH_DBG_FATAL, + "Corrupted RX data len, dropping " + "(epid: %d, dlen: %d, skblen: %d)\n", + ep_id, rxstatus->rs_datalen, len); + goto err; + } + + spin_lock(&priv->rx.rxbuflock); + memcpy(&rxbuf->rxstatus, rxstatus, HTC_RX_FRAME_HEADER_SIZE); + skb_pull(skb, HTC_RX_FRAME_HEADER_SIZE); + skb->len = rxstatus->rs_datalen; + rxbuf->skb = skb; + rxbuf->in_process = true; + spin_unlock(&priv->rx.rxbuflock); + + tasklet_schedule(&priv->rx_tasklet); + return; +err: + dev_kfree_skb_any(skb); + return; +} + +/* FIXME: Locking for cleanup/init */ + +void ath9k_rx_cleanup(struct ath9k_htc_priv *priv) +{ + struct ath9k_htc_rxbuf *rxbuf, *tbuf; + + list_for_each_entry_safe(rxbuf, tbuf, &priv->rx.rxbuf, list) { + list_del(&rxbuf->list); + if (rxbuf->skb) + dev_kfree_skb_any(rxbuf->skb); + kfree(rxbuf); + } +} + +int ath9k_rx_init(struct ath9k_htc_priv *priv) +{ + struct ath_hw *ah = priv->ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_htc_rxbuf *rxbuf; + int i = 0; + + INIT_LIST_HEAD(&priv->rx.rxbuf); + spin_lock_init(&priv->rx.rxbuflock); + + for (i = 0; i < ATH9K_HTC_RXBUF; i++) { + rxbuf = kzalloc(sizeof(struct ath9k_htc_rxbuf), GFP_KERNEL); + if (rxbuf == NULL) { + ath_print(common, ATH_DBG_FATAL, + "Unable to allocate RX buffers\n"); + goto err; + } + list_add_tail(&rxbuf->list, &priv->rx.rxbuf); + } + + return 0; + +err: + ath9k_rx_cleanup(priv); + return -ENOMEM; +} diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c new file mode 100644 index 000000000000..9a48999d0979 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/htc_hst.c @@ -0,0 +1,463 @@ +/* + * Copyright (c) 2010 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "htc.h" + +static int htc_issue_send(struct htc_target *target, struct sk_buff* skb, + u16 len, u8 flags, u8 epid, + struct ath9k_htc_tx_ctl *tx_ctl) +{ + struct htc_frame_hdr *hdr; + struct htc_endpoint *endpoint = &target->endpoint[epid]; + int status; + + hdr = (struct htc_frame_hdr *) + skb_push(skb, sizeof(struct htc_frame_hdr)); + hdr->endpoint_id = epid; + hdr->flags = flags; + hdr->payload_len = cpu_to_be16(len); + + status = target->hif->send(target->hif_dev, endpoint->ul_pipeid, skb, + tx_ctl); + return status; +} + +static struct htc_endpoint *get_next_avail_ep(struct htc_endpoint *endpoint) +{ + enum htc_endpoint_id avail_epid; + + for (avail_epid = ENDPOINT_MAX; avail_epid > ENDPOINT0; avail_epid--) + if (endpoint[avail_epid].service_id == 0) + return &endpoint[avail_epid]; + return NULL; +} + +static u8 service_to_ulpipe(u16 service_id) +{ + switch (service_id) { + case WMI_CONTROL_SVC: + return 4; + case WMI_BEACON_SVC: + case WMI_CAB_SVC: + case WMI_UAPSD_SVC: + case WMI_MGMT_SVC: + case WMI_DATA_VO_SVC: + case WMI_DATA_VI_SVC: + case WMI_DATA_BE_SVC: + case WMI_DATA_BK_SVC: + return 1; + default: + return 0; + } +} + +static u8 service_to_dlpipe(u16 service_id) +{ + switch (service_id) { + case WMI_CONTROL_SVC: + return 3; + case WMI_BEACON_SVC: + case WMI_CAB_SVC: + case WMI_UAPSD_SVC: + case WMI_MGMT_SVC: + case WMI_DATA_VO_SVC: + case WMI_DATA_VI_SVC: + case WMI_DATA_BE_SVC: + case WMI_DATA_BK_SVC: + return 2; + default: + return 0; + } +} + +static void htc_process_target_rdy(struct htc_target *target, + void *buf) +{ + struct htc_endpoint *endpoint; + struct htc_ready_msg *htc_ready_msg = (struct htc_ready_msg *) buf; + + target->credits = be16_to_cpu(htc_ready_msg->credits); + target->credit_size = be16_to_cpu(htc_ready_msg->credit_size); + + endpoint = &target->endpoint[ENDPOINT0]; + endpoint->service_id = HTC_CTRL_RSVD_SVC; + endpoint->max_msglen = HTC_MAX_CONTROL_MESSAGE_LENGTH; + complete(&target->target_wait); +} + +static void htc_process_conn_rsp(struct htc_target *target, + struct htc_frame_hdr *htc_hdr) +{ + struct htc_conn_svc_rspmsg *svc_rspmsg; + struct htc_endpoint *endpoint, *tmp_endpoint = NULL; + u16 service_id; + u16 max_msglen; + enum htc_endpoint_id epid, tepid; + + svc_rspmsg = (struct htc_conn_svc_rspmsg *) + ((void *) htc_hdr + sizeof(struct htc_frame_hdr)); + + if (svc_rspmsg->status == HTC_SERVICE_SUCCESS) { + epid = svc_rspmsg->endpoint_id; + service_id = be16_to_cpu(svc_rspmsg->service_id); + max_msglen = be16_to_cpu(svc_rspmsg->max_msg_len); + endpoint = &target->endpoint[epid]; + + for (tepid = ENDPOINT_MAX; tepid > ENDPOINT0; tepid--) { + tmp_endpoint = &target->endpoint[tepid]; + if (tmp_endpoint->service_id == service_id) { + tmp_endpoint->service_id = 0; + break; + } + } + + if (!tmp_endpoint) + return; + + endpoint->service_id = service_id; + endpoint->max_txqdepth = tmp_endpoint->max_txqdepth; + endpoint->ep_callbacks = tmp_endpoint->ep_callbacks; + endpoint->ul_pipeid = tmp_endpoint->ul_pipeid; + endpoint->dl_pipeid = tmp_endpoint->dl_pipeid; + endpoint->max_msglen = max_msglen; + target->conn_rsp_epid = epid; + complete(&target->cmd_wait); + } else { + target->conn_rsp_epid = ENDPOINT_UNUSED; + } +} + +static int htc_config_pipe_credits(struct htc_target *target) +{ + struct sk_buff *skb; + struct htc_config_pipe_msg *cp_msg; + int ret, time_left; + + skb = dev_alloc_skb(50 + sizeof(struct htc_frame_hdr)); + if (!skb) { + dev_err(target->dev, "failed to allocate send buffer\n"); + return -ENOMEM; + } + skb_reserve(skb, sizeof(struct htc_frame_hdr)); + + cp_msg = (struct htc_config_pipe_msg *) + skb_put(skb, sizeof(struct htc_config_pipe_msg)); + + cp_msg->message_id = cpu_to_be16(HTC_MSG_CONFIG_PIPE_ID); + cp_msg->pipe_id = USB_WLAN_TX_PIPE; + cp_msg->credits = 28; + + target->htc_flags |= HTC_OP_CONFIG_PIPE_CREDITS; + + ret = htc_issue_send(target, skb, skb->len, 0, ENDPOINT0, NULL); + if (ret) + goto err; + + time_left = wait_for_completion_timeout(&target->cmd_wait, HZ); + if (!time_left) { + dev_err(target->dev, "HTC credit config timeout\n"); + return -ETIMEDOUT; + } + + return 0; +err: + dev_kfree_skb(skb); + return -EINVAL; +} + +static int htc_setup_complete(struct htc_target *target) +{ + struct sk_buff *skb; + struct htc_comp_msg *comp_msg; + int ret = 0, time_left; + + skb = dev_alloc_skb(50 + sizeof(struct htc_frame_hdr)); + if (!skb) { + dev_err(target->dev, "failed to allocate send buffer\n"); + return -ENOMEM; + } + skb_reserve(skb, sizeof(struct htc_frame_hdr)); + + comp_msg = (struct htc_comp_msg *) + skb_put(skb, sizeof(struct htc_comp_msg)); + comp_msg->msg_id = cpu_to_be16(HTC_MSG_SETUP_COMPLETE_ID); + + target->htc_flags |= HTC_OP_START_WAIT; + + ret = htc_issue_send(target, skb, skb->len, 0, ENDPOINT0, NULL); + if (ret) + goto err; + + time_left = wait_for_completion_timeout(&target->cmd_wait, HZ); + if (!time_left) { + dev_err(target->dev, "HTC start timeout\n"); + return -ETIMEDOUT; + } + + return 0; + +err: + dev_kfree_skb(skb); + return -EINVAL; +} + +/* HTC APIs */ + +int htc_init(struct htc_target *target) +{ + int ret; + + ret = htc_config_pipe_credits(target); + if (ret) + return ret; + + return htc_setup_complete(target); +} + +int htc_connect_service(struct htc_target *target, + struct htc_service_connreq *service_connreq, + enum htc_endpoint_id *conn_rsp_epid) +{ + struct sk_buff *skb; + struct htc_endpoint *endpoint; + struct htc_conn_svc_msg *conn_msg; + int ret, time_left; + + /* Find an available endpoint */ + endpoint = get_next_avail_ep(target->endpoint); + if (!endpoint) { + dev_err(target->dev, "Endpoint is not available for" + "service %d\n", service_connreq->service_id); + return -EINVAL; + } + + endpoint->service_id = service_connreq->service_id; + endpoint->max_txqdepth = service_connreq->max_send_qdepth; + endpoint->ul_pipeid = service_to_ulpipe(service_connreq->service_id); + endpoint->dl_pipeid = service_to_dlpipe(service_connreq->service_id); + endpoint->ep_callbacks = service_connreq->ep_callbacks; + + skb = dev_alloc_skb(sizeof(struct htc_conn_svc_msg) + + sizeof(struct htc_frame_hdr)); + if (!skb) { + dev_err(target->dev, "Failed to allocate buf to send" + "service connect req\n"); + return -ENOMEM; + } + + skb_reserve(skb, sizeof(struct htc_frame_hdr)); + + conn_msg = (struct htc_conn_svc_msg *) + skb_put(skb, sizeof(struct htc_conn_svc_msg)); + conn_msg->service_id = cpu_to_be16(service_connreq->service_id); + conn_msg->msg_id = cpu_to_be16(HTC_MSG_CONNECT_SERVICE_ID); + conn_msg->con_flags = cpu_to_be16(service_connreq->con_flags); + conn_msg->dl_pipeid = endpoint->dl_pipeid; + conn_msg->ul_pipeid = endpoint->ul_pipeid; + + ret = htc_issue_send(target, skb, skb->len, 0, ENDPOINT0, NULL); + if (ret) + goto err; + + time_left = wait_for_completion_timeout(&target->cmd_wait, HZ); + if (!time_left) { + dev_err(target->dev, "Service connection timeout for: %d\n", + service_connreq->service_id); + return -ETIMEDOUT; + } + + *conn_rsp_epid = target->conn_rsp_epid; + return 0; +err: + dev_kfree_skb(skb); + return ret; +} + +int htc_send(struct htc_target *target, struct sk_buff *skb, + enum htc_endpoint_id epid, struct ath9k_htc_tx_ctl *tx_ctl) +{ + return htc_issue_send(target, skb, skb->len, 0, epid, tx_ctl); +} + +void htc_stop(struct htc_target *target) +{ + enum htc_endpoint_id epid; + struct htc_endpoint *endpoint; + + for (epid = ENDPOINT0; epid <= ENDPOINT_MAX; epid++) { + endpoint = &target->endpoint[epid]; + if (endpoint->service_id != 0) + target->hif->stop(target->hif_dev, endpoint->ul_pipeid); + } +} + +void htc_start(struct htc_target *target) +{ + enum htc_endpoint_id epid; + struct htc_endpoint *endpoint; + + for (epid = ENDPOINT0; epid <= ENDPOINT_MAX; epid++) { + endpoint = &target->endpoint[epid]; + if (endpoint->service_id != 0) + target->hif->start(target->hif_dev, + endpoint->ul_pipeid); + } +} + +void ath9k_htc_txcompletion_cb(struct htc_target *htc_handle, + struct sk_buff *skb, bool txok) +{ + struct htc_endpoint *endpoint; + struct htc_frame_hdr *htc_hdr; + + if (htc_handle->htc_flags & HTC_OP_CONFIG_PIPE_CREDITS) { + complete(&htc_handle->cmd_wait); + htc_handle->htc_flags &= ~HTC_OP_CONFIG_PIPE_CREDITS; + } + + if (htc_handle->htc_flags & HTC_OP_START_WAIT) { + complete(&htc_handle->cmd_wait); + htc_handle->htc_flags &= ~HTC_OP_START_WAIT; + } + + if (skb) { + htc_hdr = (struct htc_frame_hdr *) skb->data; + endpoint = &htc_handle->endpoint[htc_hdr->endpoint_id]; + skb_pull(skb, sizeof(struct htc_frame_hdr)); + + if (endpoint->ep_callbacks.tx) { + endpoint->ep_callbacks.tx(htc_handle->drv_priv, skb, + htc_hdr->endpoint_id, txok); + } + } +} + +/* + * HTC Messages are handled directly here and the obtained SKB + * is freed. + * + * Sevice messages (Data, WMI) passed to the corresponding + * endpoint RX handlers, which have to free the SKB. + */ +void ath9k_htc_rx_msg(struct htc_target *htc_handle, + struct sk_buff *skb, u32 len, u8 pipe_id) +{ + struct htc_frame_hdr *htc_hdr; + enum htc_endpoint_id epid; + struct htc_endpoint *endpoint; + u16 *msg_id; + + if (!htc_handle || !skb) + return; + + htc_hdr = (struct htc_frame_hdr *) skb->data; + epid = htc_hdr->endpoint_id; + + if (epid >= ENDPOINT_MAX) { + dev_kfree_skb_any(skb); + return; + } + + if (epid == ENDPOINT0) { + + /* Handle trailer */ + if (htc_hdr->flags & HTC_FLAGS_RECV_TRAILER) { + if (be32_to_cpu(*(u32 *) skb->data) == 0x00C60000) + /* Move past the Watchdog pattern */ + htc_hdr = (struct htc_frame_hdr *) skb->data + 4; + } + + /* Get the message ID */ + msg_id = (u16 *) ((void *) htc_hdr + + sizeof(struct htc_frame_hdr)); + + /* Now process HTC messages */ + switch (be16_to_cpu(*msg_id)) { + case HTC_MSG_READY_ID: + htc_process_target_rdy(htc_handle, htc_hdr); + break; + case HTC_MSG_CONNECT_SERVICE_RESPONSE_ID: + htc_process_conn_rsp(htc_handle, htc_hdr); + break; + default: + break; + } + + dev_kfree_skb_any(skb); + + } else { + if (htc_hdr->flags & HTC_FLAGS_RECV_TRAILER) + skb_trim(skb, len - htc_hdr->control[0]); + + skb_pull(skb, sizeof(struct htc_frame_hdr)); + + endpoint = &htc_handle->endpoint[epid]; + if (endpoint->ep_callbacks.rx) + endpoint->ep_callbacks.rx(endpoint->ep_callbacks.priv, + skb, epid); + } +} + +struct htc_target *ath9k_htc_hw_alloc(void *hif_handle) +{ + struct htc_target *target; + + target = kzalloc(sizeof(struct htc_target), GFP_KERNEL); + if (!target) + printk(KERN_ERR "Unable to allocate memory for" + "target device\n"); + + return target; +} + +void ath9k_htc_hw_free(struct htc_target *htc) +{ + kfree(htc); +} + +int ath9k_htc_hw_init(struct ath9k_htc_hif *hif, struct htc_target *target, + void *hif_handle, struct device *dev, u16 devid, + enum ath9k_hif_transports transport) +{ + struct htc_endpoint *endpoint; + int err = 0; + + init_completion(&target->target_wait); + init_completion(&target->cmd_wait); + + target->hif = hif; + target->hif_dev = hif_handle; + target->dev = dev; + + /* Assign control endpoint pipe IDs */ + endpoint = &target->endpoint[ENDPOINT0]; + endpoint->ul_pipeid = hif->control_ul_pipe; + endpoint->dl_pipeid = hif->control_dl_pipe; + + err = ath9k_htc_probe_device(target, dev, devid); + if (err) { + printk(KERN_ERR "Failed to initialize the device\n"); + return -ENODEV; + } + + return 0; +} + +void ath9k_htc_hw_deinit(struct htc_target *target, bool hot_unplug) +{ + if (target) + ath9k_htc_disconnect_device(target, hot_unplug); +} diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.h b/drivers/net/wireless/ath/ath9k/htc_hst.h new file mode 100644 index 000000000000..cd7048ffd239 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/htc_hst.h @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2010 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef HTC_HST_H +#define HTC_HST_H + +struct ath9k_htc_priv; +struct htc_target; +struct ath9k_htc_tx_ctl; + +enum ath9k_hif_transports { + ATH9K_HIF_USB, +}; + +struct ath9k_htc_hif { + struct list_head list; + const enum ath9k_hif_transports transport; + const char *name; + + u8 control_dl_pipe; + u8 control_ul_pipe; + + void (*start) (void *hif_handle, u8 pipe); + void (*stop) (void *hif_handle, u8 pipe); + int (*send) (void *hif_handle, u8 pipe, struct sk_buff *buf, + struct ath9k_htc_tx_ctl *tx_ctl); +}; + +enum htc_endpoint_id { + ENDPOINT_UNUSED = -1, + ENDPOINT0 = 0, + ENDPOINT1 = 1, + ENDPOINT2 = 2, + ENDPOINT3 = 3, + ENDPOINT4 = 4, + ENDPOINT5 = 5, + ENDPOINT6 = 6, + ENDPOINT7 = 7, + ENDPOINT8 = 8, + ENDPOINT_MAX = 22 +}; + +/* Htc frame hdr flags */ +#define HTC_FLAGS_RECV_TRAILER (1 << 1) + +struct htc_frame_hdr { + u8 endpoint_id; + u8 flags; + u16 payload_len; + u8 control[4]; +} __packed; + +struct htc_ready_msg { + u16 message_id; + u16 credits; + u16 credit_size; + u8 max_endpoints; + u8 pad; +} __packed; + +struct htc_config_pipe_msg { + u16 message_id; + u8 pipe_id; + u8 credits; +} __packed; + +struct htc_packet { + void *pktcontext; + u8 *buf; + u8 *buf_payload; + u32 buflen; + u32 payload_len; + + int endpoint; + int status; + + void *context; + u32 reserved; +}; + +struct htc_ep_callbacks { + void *priv; + void (*tx) (void *, struct sk_buff *, enum htc_endpoint_id, bool txok); + void (*rx) (void *, struct sk_buff *, enum htc_endpoint_id); +}; + +#define HTC_TX_QUEUE_SIZE 256 + +struct htc_txq { + struct sk_buff *buf[HTC_TX_QUEUE_SIZE]; + u32 txqdepth; + u16 txbuf_cnt; + u16 txq_head; + u16 txq_tail; +}; + +struct htc_endpoint { + u16 service_id; + + struct htc_ep_callbacks ep_callbacks; + struct htc_txq htc_txq; + u32 max_txqdepth; + int max_msglen; + + u8 ul_pipeid; + u8 dl_pipeid; +}; + +#define HTC_MAX_CONTROL_MESSAGE_LENGTH 255 +#define HTC_CONTROL_BUFFER_SIZE \ + (HTC_MAX_CONTROL_MESSAGE_LENGTH + sizeof(struct htc_frame_hdr)) + +#define NUM_CONTROL_BUFFERS 8 +#define HST_ENDPOINT_MAX 8 + +struct htc_control_buf { + struct htc_packet htc_pkt; + u8 buf[HTC_CONTROL_BUFFER_SIZE]; +}; + +#define HTC_OP_START_WAIT BIT(0) +#define HTC_OP_CONFIG_PIPE_CREDITS BIT(1) + +struct htc_target { + void *hif_dev; + struct ath9k_htc_priv *drv_priv; + struct device *dev; + struct ath9k_htc_hif *hif; + struct htc_endpoint endpoint[HST_ENDPOINT_MAX]; + struct completion target_wait; + struct completion cmd_wait; + struct list_head list; + enum htc_endpoint_id conn_rsp_epid; + u16 credits; + u16 credit_size; + u8 htc_flags; +}; + +enum htc_msg_id { + HTC_MSG_READY_ID = 1, + HTC_MSG_CONNECT_SERVICE_ID, + HTC_MSG_CONNECT_SERVICE_RESPONSE_ID, + HTC_MSG_SETUP_COMPLETE_ID, + HTC_MSG_CONFIG_PIPE_ID, + HTC_MSG_CONFIG_PIPE_RESPONSE_ID, +}; + +struct htc_service_connreq { + u16 service_id; + u16 con_flags; + u32 max_send_qdepth; + struct htc_ep_callbacks ep_callbacks; +}; + +/* Current service IDs */ + +enum htc_service_group_ids{ + RSVD_SERVICE_GROUP = 0, + WMI_SERVICE_GROUP = 1, + + HTC_SERVICE_GROUP_LAST = 255 +}; + +#define MAKE_SERVICE_ID(group, index) \ + (int)(((int)group << 8) | (int)(index)) + +/* NOTE: service ID of 0x0000 is reserved and should never be used */ +#define HTC_CTRL_RSVD_SVC MAKE_SERVICE_ID(RSVD_SERVICE_GROUP, 1) +#define HTC_LOOPBACK_RSVD_SVC MAKE_SERVICE_ID(RSVD_SERVICE_GROUP, 2) + +#define WMI_CONTROL_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 0) +#define WMI_BEACON_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 1) +#define WMI_CAB_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 2) +#define WMI_UAPSD_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 3) +#define WMI_MGMT_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 4) +#define WMI_DATA_VO_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 5) +#define WMI_DATA_VI_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 6) +#define WMI_DATA_BE_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 7) +#define WMI_DATA_BK_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 8) + +struct htc_conn_svc_msg { + u16 msg_id; + u16 service_id; + u16 con_flags; + u8 dl_pipeid; + u8 ul_pipeid; + u8 svc_meta_len; + u8 pad; +} __packed; + +/* connect response status codes */ +#define HTC_SERVICE_SUCCESS 0 +#define HTC_SERVICE_NOT_FOUND 1 +#define HTC_SERVICE_FAILED 2 +#define HTC_SERVICE_NO_RESOURCES 3 +#define HTC_SERVICE_NO_MORE_EP 4 + +struct htc_conn_svc_rspmsg { + u16 msg_id; + u16 service_id; + u8 status; + u8 endpoint_id; + u16 max_msg_len; + u8 svc_meta_len; + u8 pad; +} __packed; + +struct htc_comp_msg { + u16 msg_id; +} __packed; + +int htc_init(struct htc_target *target); +int htc_connect_service(struct htc_target *target, + struct htc_service_connreq *service_connreq, + enum htc_endpoint_id *conn_rsp_eid); +int htc_send(struct htc_target *target, struct sk_buff *skb, + enum htc_endpoint_id eid, struct ath9k_htc_tx_ctl *tx_ctl); +void htc_stop(struct htc_target *target); +void htc_start(struct htc_target *target); + +void ath9k_htc_rx_msg(struct htc_target *htc_handle, + struct sk_buff *skb, u32 len, u8 pipe_id); +void ath9k_htc_txcompletion_cb(struct htc_target *htc_handle, + struct sk_buff *skb, bool txok); + +struct htc_target *ath9k_htc_hw_alloc(void *hif_handle); +void ath9k_htc_hw_free(struct htc_target *htc); +int ath9k_htc_hw_init(struct ath9k_htc_hif *hif, struct htc_target *target, + void *hif_handle, struct device *dev, u16 devid, + enum ath9k_hif_transports transport); +void ath9k_htc_hw_deinit(struct htc_target *target, bool hot_unplug); + +#endif /* HTC_HST_H */ diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index 29851e6376a9..a5e543bd2271 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -150,6 +150,32 @@ struct ath_rx_status { u32 evm2; }; +struct ath_htc_rx_status { + u64 rs_tstamp; + u16 rs_datalen; + u8 rs_status; + u8 rs_phyerr; + int8_t rs_rssi; + int8_t rs_rssi_ctl0; + int8_t rs_rssi_ctl1; + int8_t rs_rssi_ctl2; + int8_t rs_rssi_ext0; + int8_t rs_rssi_ext1; + int8_t rs_rssi_ext2; + u8 rs_keyix; + u8 rs_rate; + u8 rs_antenna; + u8 rs_more; + u8 rs_isaggr; + u8 rs_moreaggr; + u8 rs_num_delims; + u8 rs_flags; + u8 rs_dummy; + u32 evm0; + u32 evm1; + u32 evm2; +}; + #define ATH9K_RXERR_CRC 0x01 #define ATH9K_RXERR_PHY 0x02 #define ATH9K_RXERR_FIFO 0x04 diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c new file mode 100644 index 000000000000..818dea0164ec --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/wmi.c @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2010 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "htc.h" + +static const char *wmi_cmd_to_name(enum wmi_cmd_id wmi_cmd) +{ + switch (wmi_cmd) { + case WMI_ECHO_CMDID: + return "WMI_ECHO_CMDID"; + case WMI_ACCESS_MEMORY_CMDID: + return "WMI_ACCESS_MEMORY_CMDID"; + case WMI_DISABLE_INTR_CMDID: + return "WMI_DISABLE_INTR_CMDID"; + case WMI_ENABLE_INTR_CMDID: + return "WMI_ENABLE_INTR_CMDID"; + case WMI_RX_LINK_CMDID: + return "WMI_RX_LINK_CMDID"; + case WMI_ATH_INIT_CMDID: + return "WMI_ATH_INIT_CMDID"; + case WMI_ABORT_TXQ_CMDID: + return "WMI_ABORT_TXQ_CMDID"; + case WMI_STOP_TX_DMA_CMDID: + return "WMI_STOP_TX_DMA_CMDID"; + case WMI_STOP_DMA_RECV_CMDID: + return "WMI_STOP_DMA_RECV_CMDID"; + case WMI_ABORT_TX_DMA_CMDID: + return "WMI_ABORT_TX_DMA_CMDID"; + case WMI_DRAIN_TXQ_CMDID: + return "WMI_DRAIN_TXQ_CMDID"; + case WMI_DRAIN_TXQ_ALL_CMDID: + return "WMI_DRAIN_TXQ_ALL_CMDID"; + case WMI_START_RECV_CMDID: + return "WMI_START_RECV_CMDID"; + case WMI_STOP_RECV_CMDID: + return "WMI_STOP_RECV_CMDID"; + case WMI_FLUSH_RECV_CMDID: + return "WMI_FLUSH_RECV_CMDID"; + case WMI_SET_MODE_CMDID: + return "WMI_SET_MODE_CMDID"; + case WMI_RESET_CMDID: + return "WMI_RESET_CMDID"; + case WMI_NODE_CREATE_CMDID: + return "WMI_NODE_CREATE_CMDID"; + case WMI_NODE_REMOVE_CMDID: + return "WMI_NODE_REMOVE_CMDID"; + case WMI_VAP_REMOVE_CMDID: + return "WMI_VAP_REMOVE_CMDID"; + case WMI_VAP_CREATE_CMDID: + return "WMI_VAP_CREATE_CMDID"; + case WMI_BEACON_UPDATE_CMDID: + return "WMI_BEACON_UPDATE_CMDID"; + case WMI_REG_READ_CMDID: + return "WMI_REG_READ_CMDID"; + case WMI_REG_WRITE_CMDID: + return "WMI_REG_WRITE_CMDID"; + case WMI_RC_STATE_CHANGE_CMDID: + return "WMI_RC_STATE_CHANGE_CMDID"; + case WMI_RC_RATE_UPDATE_CMDID: + return "WMI_RC_RATE_UPDATE_CMDID"; + case WMI_DEBUG_INFO_CMDID: + return "WMI_DEBUG_INFO_CMDID"; + case WMI_HOST_ATTACH: + return "WMI_HOST_ATTACH"; + case WMI_TARGET_IC_UPDATE_CMDID: + return "WMI_TARGET_IC_UPDATE_CMDID"; + case WMI_TGT_STATS_CMDID: + return "WMI_TGT_STATS_CMDID"; + case WMI_TX_AGGR_ENABLE_CMDID: + return "WMI_TX_AGGR_ENABLE_CMDID"; + case WMI_TGT_DETACH_CMDID: + return "WMI_TGT_DETACH_CMDID"; + case WMI_TGT_TXQ_ENABLE_CMDID: + return "WMI_TGT_TXQ_ENABLE_CMDID"; + } + + return "Bogus"; +} + +struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv) +{ + struct wmi *wmi; + + wmi = kzalloc(sizeof(struct wmi), GFP_KERNEL); + if (!wmi) + return NULL; + + wmi->drv_priv = priv; + wmi->stopped = false; + mutex_init(&wmi->op_mutex); + init_completion(&wmi->cmd_wait); + + return wmi; +} + +void ath9k_deinit_wmi(struct ath9k_htc_priv *priv) +{ + struct wmi *wmi = priv->wmi; + + mutex_lock(&wmi->op_mutex); + wmi->stopped = true; + mutex_unlock(&wmi->op_mutex); + + kfree(priv->wmi); +} + +void ath9k_wmi_tasklet(unsigned long data) +{ + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data; + struct ath_common *common = ath9k_hw_common(priv->ah); + struct wmi_cmd_hdr *hdr; + struct wmi_swba *swba_hdr; + enum wmi_event_id event; + struct sk_buff *skb; + void *wmi_event; + unsigned long flags; +#ifdef CONFIG_ATH9K_HTC_DEBUGFS + u32 txrate; +#endif + + spin_lock_irqsave(&priv->wmi->wmi_lock, flags); + skb = priv->wmi->wmi_skb; + spin_unlock_irqrestore(&priv->wmi->wmi_lock, flags); + + hdr = (struct wmi_cmd_hdr *) skb->data; + event = be16_to_cpu(hdr->command_id); + wmi_event = skb_pull(skb, sizeof(struct wmi_cmd_hdr)); + + ath_print(common, ATH_DBG_WMI, + "WMI Event: 0x%x\n", event); + + switch (event) { + case WMI_TGT_RDY_EVENTID: + break; + case WMI_SWBA_EVENTID: + swba_hdr = (struct wmi_swba *) wmi_event; + ath9k_htc_swba(priv, swba_hdr->beacon_pending); + break; + case WMI_FATAL_EVENTID: + break; + case WMI_TXTO_EVENTID: + break; + case WMI_BMISS_EVENTID: + break; + case WMI_WLAN_TXCOMP_EVENTID: + break; + case WMI_DELBA_EVENTID: + break; + case WMI_TXRATE_EVENTID: +#ifdef CONFIG_ATH9K_HTC_DEBUGFS + txrate = ((struct wmi_event_txrate *)wmi_event)->txrate; + priv->debug.txrate = be32_to_cpu(txrate); +#endif + break; + default: + break; + } + + dev_kfree_skb_any(skb); +} + +static void ath9k_wmi_rsp_callback(struct wmi *wmi, struct sk_buff *skb) +{ + skb_pull(skb, sizeof(struct wmi_cmd_hdr)); + + if (wmi->cmd_rsp_buf != NULL && wmi->cmd_rsp_len != 0) + memcpy(wmi->cmd_rsp_buf, skb->data, wmi->cmd_rsp_len); + + complete(&wmi->cmd_wait); +} + +static void ath9k_wmi_ctrl_rx(void *priv, struct sk_buff *skb, + enum htc_endpoint_id epid) +{ + struct wmi *wmi = (struct wmi *) priv; + struct wmi_cmd_hdr *hdr; + u16 cmd_id; + + if (unlikely(wmi->stopped)) + goto free_skb; + + hdr = (struct wmi_cmd_hdr *) skb->data; + cmd_id = be16_to_cpu(hdr->command_id); + + if (cmd_id & 0x1000) { + spin_lock(&wmi->wmi_lock); + wmi->wmi_skb = skb; + spin_unlock(&wmi->wmi_lock); + tasklet_schedule(&wmi->drv_priv->wmi_tasklet); + return; + } + + /* WMI command response */ + ath9k_wmi_rsp_callback(wmi, skb); + +free_skb: + dev_kfree_skb_any(skb); +} + +static void ath9k_wmi_ctrl_tx(void *priv, struct sk_buff *skb, + enum htc_endpoint_id epid, bool txok) +{ + dev_kfree_skb_any(skb); +} + +int ath9k_wmi_connect(struct htc_target *htc, struct wmi *wmi, + enum htc_endpoint_id *wmi_ctrl_epid) +{ + struct htc_service_connreq connect; + int ret; + + wmi->htc = htc; + + memset(&connect, 0, sizeof(connect)); + + connect.ep_callbacks.priv = wmi; + connect.ep_callbacks.tx = ath9k_wmi_ctrl_tx; + connect.ep_callbacks.rx = ath9k_wmi_ctrl_rx; + connect.service_id = WMI_CONTROL_SVC; + + ret = htc_connect_service(htc, &connect, &wmi->ctrl_epid); + if (ret) + return ret; + + *wmi_ctrl_epid = wmi->ctrl_epid; + + return 0; +} + +static int ath9k_wmi_cmd_issue(struct wmi *wmi, + struct sk_buff *skb, + enum wmi_cmd_id cmd, u16 len) +{ + struct wmi_cmd_hdr *hdr; + + hdr = (struct wmi_cmd_hdr *) skb_push(skb, sizeof(struct wmi_cmd_hdr)); + hdr->command_id = cpu_to_be16(cmd); + hdr->seq_no = cpu_to_be16(++wmi->tx_seq_id); + + return htc_send(wmi->htc, skb, wmi->ctrl_epid, NULL); +} + +int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id, + u8 *cmd_buf, u32 cmd_len, + u8 *rsp_buf, u32 rsp_len, + u32 timeout) +{ + struct ath_hw *ah = wmi->drv_priv->ah; + struct ath_common *common = ath9k_hw_common(ah); + u16 headroom = sizeof(struct htc_frame_hdr) + + sizeof(struct wmi_cmd_hdr); + struct sk_buff *skb; + u8 *data; + int time_left, ret = 0; + + if (!wmi) + return -EINVAL; + + skb = dev_alloc_skb(headroom + cmd_len); + if (!skb) + return -ENOMEM; + + skb_reserve(skb, headroom); + + if (cmd_len != 0 && cmd_buf != NULL) { + data = (u8 *) skb_put(skb, cmd_len); + memcpy(data, cmd_buf, cmd_len); + } + + mutex_lock(&wmi->op_mutex); + + /* check if wmi stopped flag is set */ + if (unlikely(wmi->stopped)) { + ret = -EPROTO; + goto out; + } + + /* record the rsp buffer and length */ + wmi->cmd_rsp_buf = rsp_buf; + wmi->cmd_rsp_len = rsp_len; + + ret = ath9k_wmi_cmd_issue(wmi, skb, cmd_id, cmd_len); + if (ret) + goto out; + + time_left = wait_for_completion_timeout(&wmi->cmd_wait, timeout); + if (!time_left) { + ath_print(common, ATH_DBG_WMI, + "Timeout waiting for WMI command: %s\n", + wmi_cmd_to_name(cmd_id)); + mutex_unlock(&wmi->op_mutex); + return -ETIMEDOUT; + } + + mutex_unlock(&wmi->op_mutex); + + return 0; + +out: + ath_print(common, ATH_DBG_WMI, + "WMI failure for: %s\n", wmi_cmd_to_name(cmd_id)); + mutex_unlock(&wmi->op_mutex); + dev_kfree_skb_any(skb); + + return ret; +} diff --git a/drivers/net/wireless/ath/ath9k/wmi.h b/drivers/net/wireless/ath/ath9k/wmi.h new file mode 100644 index 000000000000..39ef926f27c2 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/wmi.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2010 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WMI_H +#define WMI_H + + +struct wmi_event_txrate { + u32 txrate; + struct { + u8 rssi_thresh; + u8 per; + } rc_stats; +} __packed; + +struct wmi_cmd_hdr { + u16 command_id; + u16 seq_no; +} __packed; + +struct wmi_swba { + u8 beacon_pending; +} __packed; + +enum wmi_cmd_id { + WMI_ECHO_CMDID = 0x0001, + WMI_ACCESS_MEMORY_CMDID, + + /* Commands to Target */ + WMI_DISABLE_INTR_CMDID, + WMI_ENABLE_INTR_CMDID, + WMI_RX_LINK_CMDID, + WMI_ATH_INIT_CMDID, + WMI_ABORT_TXQ_CMDID, + WMI_STOP_TX_DMA_CMDID, + WMI_STOP_DMA_RECV_CMDID, + WMI_ABORT_TX_DMA_CMDID, + WMI_DRAIN_TXQ_CMDID, + WMI_DRAIN_TXQ_ALL_CMDID, + WMI_START_RECV_CMDID, + WMI_STOP_RECV_CMDID, + WMI_FLUSH_RECV_CMDID, + WMI_SET_MODE_CMDID, + WMI_RESET_CMDID, + WMI_NODE_CREATE_CMDID, + WMI_NODE_REMOVE_CMDID, + WMI_VAP_REMOVE_CMDID, + WMI_VAP_CREATE_CMDID, + WMI_BEACON_UPDATE_CMDID, + WMI_REG_READ_CMDID, + WMI_REG_WRITE_CMDID, + WMI_RC_STATE_CHANGE_CMDID, + WMI_RC_RATE_UPDATE_CMDID, + WMI_DEBUG_INFO_CMDID, + WMI_HOST_ATTACH, + WMI_TARGET_IC_UPDATE_CMDID, + WMI_TGT_STATS_CMDID, + WMI_TX_AGGR_ENABLE_CMDID, + WMI_TGT_DETACH_CMDID, + WMI_TGT_TXQ_ENABLE_CMDID, +}; + +enum wmi_event_id { + WMI_TGT_RDY_EVENTID = 0x1001, + WMI_SWBA_EVENTID, + WMI_FATAL_EVENTID, + WMI_TXTO_EVENTID, + WMI_BMISS_EVENTID, + WMI_WLAN_TXCOMP_EVENTID, + WMI_DELBA_EVENTID, + WMI_TXRATE_EVENTID, +}; + +struct wmi { + struct ath9k_htc_priv *drv_priv; + struct htc_target *htc; + enum htc_endpoint_id ctrl_epid; + struct mutex op_mutex; + struct completion cmd_wait; + u16 tx_seq_id; + u8 *cmd_rsp_buf; + u32 cmd_rsp_len; + bool stopped; + + struct sk_buff *wmi_skb; + spinlock_t wmi_lock; +}; + +struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv); +void ath9k_deinit_wmi(struct ath9k_htc_priv *priv); +int ath9k_wmi_connect(struct htc_target *htc, struct wmi *wmi, + enum htc_endpoint_id *wmi_ctrl_epid); +int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id, + u8 *cmd_buf, u32 cmd_len, + u8 *rsp_buf, u32 rsp_len, + u32 timeout); +void ath9k_wmi_tasklet(unsigned long data); + +#define WMI_CMD(_wmi_cmd) \ + do { \ + ret = ath9k_wmi_cmd(priv->wmi, _wmi_cmd, NULL, 0, \ + (u8 *) &cmd_rsp, \ + sizeof(cmd_rsp), HZ); \ + } while (0) + +#define WMI_CMD_BUF(_wmi_cmd, _buf) \ + do { \ + ret = ath9k_wmi_cmd(priv->wmi, _wmi_cmd, \ + (u8 *) _buf, sizeof(*_buf), \ + &cmd_rsp, sizeof(cmd_rsp), HZ); \ + } while (0) + +#endif /* WMI_H */ diff --git a/drivers/net/wireless/ath/debug.h b/drivers/net/wireless/ath/debug.h index 8263633c003c..873bf526e11f 100644 --- a/drivers/net/wireless/ath/debug.h +++ b/drivers/net/wireless/ath/debug.h @@ -59,6 +59,7 @@ enum ATH_DEBUG { ATH_DBG_PS = 0x00000800, ATH_DBG_HWTIMER = 0x00001000, ATH_DBG_BTCOEX = 0x00002000, + ATH_DBG_WMI = 0x00004000, ATH_DBG_ANY = 0xffffffff }; From b43316dbf9c6b01f3ac6dc23bcb94ae66eb3a721 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Thu, 18 Mar 2010 12:26:26 +0200 Subject: [PATCH 23/60] wl1271: Fix configuration of the TX opportunity value The per-queue TX opportunity value is configured by the mac80211 in units of 32us. The firmware however wants it in us, so add the conversion. Signed-off-by: Juuso Oikarinen Reviewed-by: Kalle Valo Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index 0a4ff7b02f59..0de337f9e50f 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -1745,9 +1745,10 @@ static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue, if (ret < 0) goto out; + /* the txop is confed in units of 32us by the mac80211, we need us */ ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue), params->cw_min, params->cw_max, - params->aifs, params->txop); + params->aifs, params->txop << 5); if (ret < 0) goto out_sleep; From becd551cac0d6868819083c59fc32d1072ba1804 Mon Sep 17 00:00:00 2001 From: Teemu Paasikivi Date: Thu, 18 Mar 2010 12:26:27 +0200 Subject: [PATCH 24/60] wl1271: Cleaned up wlan power on/off functions Added method for wlan power control to io_ops struct and moved wl1271_power_on and wl1271_power_off functions to wl1271_io.h. Signed-off-by: Teemu Paasikivi Reviewed-by: Juuso Oikarinen Signed-off-by: Juuso Oikarinen Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271.h | 1 + drivers/net/wireless/wl12xx/wl1271_debugfs.c | 12 +++++------- drivers/net/wireless/wl12xx/wl1271_io.h | 12 ++++++++++++ drivers/net/wireless/wl12xx/wl1271_main.c | 12 ------------ drivers/net/wireless/wl12xx/wl1271_sdio.c | 11 +++++------ drivers/net/wireless/wl12xx/wl1271_spi.c | 7 +++++++ 6 files changed, 30 insertions(+), 25 deletions(-) diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index 0deb4fdf916b..7397999deb19 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -344,6 +344,7 @@ struct wl1271_if_operations { bool fixed); void (*reset)(struct wl1271 *wl); void (*init)(struct wl1271 *wl); + void (*power)(struct wl1271 *wl, bool enable); struct device* (*dev)(struct wl1271 *wl); void (*enable_irq)(struct wl1271 *wl); void (*disable_irq)(struct wl1271 *wl); diff --git a/drivers/net/wireless/wl12xx/wl1271_debugfs.c b/drivers/net/wireless/wl12xx/wl1271_debugfs.c index 8d7588ca68fd..3c0f5b1ac272 100644 --- a/drivers/net/wireless/wl12xx/wl1271_debugfs.c +++ b/drivers/net/wireless/wl12xx/wl1271_debugfs.c @@ -28,6 +28,7 @@ #include "wl1271.h" #include "wl1271_acx.h" #include "wl1271_ps.h" +#include "wl1271_io.h" /* ms */ #define WL1271_DEBUGFS_STATS_LIFETIME 1000 @@ -276,13 +277,10 @@ static ssize_t gpio_power_write(struct file *file, goto out; } - if (value) { - wl->set_power(true); - set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); - } else { - wl->set_power(false); - clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); - } + if (value) + wl1271_power_on(wl); + else + wl1271_power_off(wl); out: mutex_unlock(&wl->mutex); diff --git a/drivers/net/wireless/wl12xx/wl1271_io.h b/drivers/net/wireless/wl12xx/wl1271_io.h index 95d2168f8af4..3ff88d971308 100644 --- a/drivers/net/wireless/wl12xx/wl1271_io.h +++ b/drivers/net/wireless/wl12xx/wl1271_io.h @@ -138,6 +138,18 @@ static inline void wl1271_write32(struct wl1271 *wl, int addr, u32 val) wl1271_raw_write32(wl, wl1271_translate_addr(wl, addr), val); } +static inline void wl1271_power_off(struct wl1271 *wl) +{ + wl->if_ops->power(wl, false); + clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); +} + +static inline void wl1271_power_on(struct wl1271 *wl) +{ + wl->if_ops->power(wl, true); + set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); +} + /* Top Register IO */ void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val); diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index 0de337f9e50f..b3abb455bd70 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -359,18 +359,6 @@ static int wl1271_plt_init(struct wl1271 *wl) return ret; } -static void wl1271_power_off(struct wl1271 *wl) -{ - wl->set_power(false); - clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); -} - -static void wl1271_power_on(struct wl1271 *wl) -{ - wl->set_power(true); - set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); -} - static void wl1271_fw_status(struct wl1271 *wl, struct wl1271_fw_status *status) { diff --git a/drivers/net/wireless/wl12xx/wl1271_sdio.c b/drivers/net/wireless/wl12xx/wl1271_sdio.c index 1f204db30c27..d43e0d41d439 100644 --- a/drivers/net/wireless/wl12xx/wl1271_sdio.c +++ b/drivers/net/wireless/wl12xx/wl1271_sdio.c @@ -156,20 +156,21 @@ static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf, sdio_release_host(func); } +static void wl1271_sdio_set_power(struct wl1271 *wl, bool enable) +{ +} + static struct wl1271_if_operations sdio_ops = { .read = wl1271_sdio_raw_read, .write = wl1271_sdio_raw_write, .reset = wl1271_sdio_reset, .init = wl1271_sdio_init, + .power = wl1271_sdio_set_power, .dev = wl1271_sdio_wl_to_dev, .enable_irq = wl1271_sdio_enable_interrupts, .disable_irq = wl1271_sdio_disable_interrupts }; -static void wl1271_sdio_set_power(bool enable) -{ -} - static int __devinit wl1271_probe(struct sdio_func *func, const struct sdio_device_id *id) { @@ -190,8 +191,6 @@ static int __devinit wl1271_probe(struct sdio_func *func, wl->if_priv = func; wl->if_ops = &sdio_ops; - wl->set_power = wl1271_sdio_set_power; - /* Grab access to FN0 for ELP reg. */ func->card->quirks |= MMC_QUIRK_LENIENT_FN0; diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.c b/drivers/net/wireless/wl12xx/wl1271_spi.c index ed285fec2a08..0eea73392ea3 100644 --- a/drivers/net/wireless/wl12xx/wl1271_spi.c +++ b/drivers/net/wireless/wl12xx/wl1271_spi.c @@ -347,11 +347,18 @@ static struct platform_device wl1271_device = { }, }; +static void wl1271_spi_set_power(struct wl1271 *wl, bool enable) +{ + if (wl->set_power) + wl->set_power(enable); +} + static struct wl1271_if_operations spi_ops = { .read = wl1271_spi_raw_read, .write = wl1271_spi_raw_write, .reset = wl1271_spi_reset, .init = wl1271_spi_init, + .power = wl1271_spi_set_power, .dev = wl1271_spi_wl_to_dev, .enable_irq = wl1271_spi_enable_interrupts, .disable_irq = wl1271_spi_disable_interrupts From 93f56be146a7be4d3243fc64d9b6313d96eaa62b Mon Sep 17 00:00:00 2001 From: Teemu Paasikivi Date: Thu, 18 Mar 2010 12:26:28 +0200 Subject: [PATCH 25/60] wl1271: Changed SDIO MMC host claiming Changed driver to keep MMC host claimed while wlan interface is up. This keeps MMC stack from powering the SDIO device down. Signed-off-by: Teemu Paasikivi Reviewed-by: Juuso Oikarinen Signed-off-by: Juuso Oikarinen Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271_sdio.c | 27 ++++++++--------------- 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/wl12xx/wl1271_sdio.c b/drivers/net/wireless/wl12xx/wl1271_sdio.c index d43e0d41d439..d9183b64e723 100644 --- a/drivers/net/wireless/wl12xx/wl1271_sdio.c +++ b/drivers/net/wireless/wl12xx/wl1271_sdio.c @@ -107,7 +107,6 @@ static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf, int ret; struct sdio_func *func = wl_to_func(wl); - sdio_claim_host(func); if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) { ((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret); wl1271_debug(DEBUG_SPI, "sdio read 52 addr 0x%x, byte 0x%02x", @@ -126,7 +125,6 @@ static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf, if (ret) wl1271_error("sdio read failed (%d)", ret); - sdio_release_host(func); } static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf, @@ -135,7 +133,6 @@ static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf, int ret; struct sdio_func *func = wl_to_func(wl); - sdio_claim_host(func); if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) { sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret); wl1271_debug(DEBUG_SPI, "sdio write 52 addr 0x%x, byte 0x%02x", @@ -153,11 +150,19 @@ static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf, if (ret) wl1271_error("sdio write failed (%d)", ret); - sdio_release_host(func); } static void wl1271_sdio_set_power(struct wl1271 *wl, bool enable) { + struct sdio_func *func = wl_to_func(wl); + + if (enable) { + sdio_claim_host(func); + sdio_enable_func(func); + } else { + sdio_disable_func(func); + sdio_release_host(func); + } } static struct wl1271_if_operations sdio_ops = { @@ -219,22 +224,12 @@ static int __devinit wl1271_probe(struct sdio_func *func, if (ret) goto out_irq; - sdio_claim_host(func); sdio_set_drvdata(func, wl); - ret = sdio_enable_func(func); - if (ret) - goto out_release; - - sdio_release_host(func); - wl1271_notice("initialized"); return 0; - out_release: - sdio_release_host(func); - out_irq: free_irq(wl->irq, wl); @@ -251,10 +246,6 @@ static void __devexit wl1271_remove(struct sdio_func *func) ieee80211_unregister_hw(wl->hw); - sdio_claim_host(func); - sdio_disable_func(func); - sdio_release_host(func); - free_irq(wl->irq, wl); kfree(wl->target_mem_map); From 885c9907440947a4f50d4c7a93b1a45266b00b84 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Thu, 18 Mar 2010 12:26:29 +0200 Subject: [PATCH 26/60] wl1271: Fix SG configuration message structures The bluetooth coexistence (SG) configuration messages have changed and were completely wrong. For instance, intending to enable the SG, it was instead disabled. Signed-off-by: Juuso Oikarinen Reviewed-by: Teemu Paasikivi Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271_acx.c | 17 +--- drivers/net/wireless/wl12xx/wl1271_acx.h | 124 ++++++++++++----------- 2 files changed, 68 insertions(+), 73 deletions(-) diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c index 60e20876e6d8..405ae1bf464f 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.c +++ b/drivers/net/wireless/wl12xx/wl1271_acx.c @@ -547,7 +547,7 @@ int wl1271_acx_sg_enable(struct wl1271 *wl) goto out; } - pta->enable = SG_ENABLE; + pta->enable = ACX_SG_DISABLE; ret = wl1271_cmd_configure(wl, ACX_SG_ENABLE, pta, sizeof(*pta)); if (ret < 0) { @@ -575,19 +575,8 @@ int wl1271_acx_sg_cfg(struct wl1271 *wl) } /* BT-WLAN coext parameters */ - param->per_threshold = cpu_to_le32(c->per_threshold); - param->max_scan_compensation_time = - cpu_to_le32(c->max_scan_compensation_time); - param->nfs_sample_interval = cpu_to_le16(c->nfs_sample_interval); - param->load_ratio = c->load_ratio; - param->auto_ps_mode = c->auto_ps_mode; - param->probe_req_compensation = c->probe_req_compensation; - param->scan_window_compensation = c->scan_window_compensation; - param->antenna_config = c->antenna_config; - param->beacon_miss_threshold = c->beacon_miss_threshold; - param->rate_adaptation_threshold = - cpu_to_le32(c->rate_adaptation_threshold); - param->rate_adaptation_snr = c->rate_adaptation_snr; + param->params[ACX_SG_BT_PER_THRESHOLD] = c->per_threshold; + param->param_idx = ACX_SG_BT_PER_THRESHOLD; ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param)); if (ret < 0) { diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h index aeccc98581eb..88a05cbd3457 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.h +++ b/drivers/net/wireless/wl12xx/wl1271_acx.h @@ -393,28 +393,80 @@ struct acx_conn_monit_params { } __attribute__ ((packed)); enum { - SG_ENABLE = 0, - SG_DISABLE, - SG_SENSE_NO_ACTIVITY, - SG_SENSE_ACTIVE + ACX_SG_DISABLE = 0, + ACX_SG_PROTECTIVE, + ACX_SG_OPPORTUNISTIC }; struct acx_bt_wlan_coex { struct acx_header header; - /* - * 0 -> PTA enabled - * 1 -> PTA disabled - * 2 -> sense no active mode, i.e. - * an interrupt is sent upon - * BT activity. - * 3 -> PTA is switched on in response - * to the interrupt sending. - */ u8 enable; u8 pad[3]; } __attribute__ ((packed)); +enum { + ACX_SG_BT_PER_THRESHOLD = 0, + ACX_SG_HV3_MAX_OVERRIDE, + ACX_SG_BT_NFS_SAMPLE_INTERVAL, + ACX_SG_BT_LOAD_RATIO, + ACX_SG_AUTO_PS_MODE, + ACX_SG_AUTO_SCAN_PROBE_REQ, + ACX_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3, + ACX_SG_ANTENNA_CONFIGURATION, + ACX_SG_BEACON_MISS_PERCENT, + ACX_SG_RATE_ADAPT_THRESH, + ACX_SG_RATE_ADAPT_SNR, + ACX_SG_WLAN_PS_BT_ACL_MASTER_MIN_BR, + ACX_SG_WLAN_PS_BT_ACL_MASTER_MAX_BR, + ACX_SG_WLAN_PS_MAX_BT_ACL_MASTER_BR, + ACX_SG_WLAN_PS_BT_ACL_SLAVE_MIN_BR, + ACX_SG_WLAN_PS_BT_ACL_SLAVE_MAX_BR, + ACX_SG_WLAN_PS_MAX_BT_ACL_SLAVE_BR, + ACX_SG_WLAN_PS_BT_ACL_MASTER_MIN_EDR, + ACX_SG_WLAN_PS_BT_ACL_MASTER_MAX_EDR, + ACX_SG_WLAN_PS_MAX_BT_ACL_MASTER_EDR, + ACX_SG_WLAN_PS_BT_ACL_SLAVE_MIN_EDR, + ACX_SG_WLAN_PS_BT_ACL_SLAVE_MAX_EDR, + ACX_SG_WLAN_PS_MAX_BT_ACL_SLAVE_EDR, + ACX_SG_RXT, + ACX_SG_TXT, + ACX_SG_ADAPTIVE_RXT_TXT, + ACX_SG_PS_POLL_TIMEOUT, + ACX_SG_UPSD_TIMEOUT, + ACX_SG_WLAN_ACTIVE_BT_ACL_MASTER_MIN_EDR, + ACX_SG_WLAN_ACTIVE_BT_ACL_MASTER_MAX_EDR, + ACX_SG_WLAN_ACTIVE_MAX_BT_ACL_MASTER_EDR, + ACX_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MIN_EDR, + ACX_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MAX_EDR, + ACX_SG_WLAN_ACTIVE_MAX_BT_ACL_SLAVE_EDR, + ACX_SG_WLAN_ACTIVE_BT_ACL_MIN_BR, + ACX_SG_WLAN_ACTIVE_BT_ACL_MAX_BR, + ACX_SG_WLAN_ACTIVE_MAX_BT_ACL_BR, + ACX_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3, + ACX_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP, + ACX_SG_PASSIVE_SCAN_A2DP_BT_TIME, + ACX_SG_PASSIVE_SCAN_A2DP_WLAN_TIME, + ACX_SG_HV3_MAX_SERVED, + ACX_SG_DHCP_TIME, + ACX_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP, + ACX_SG_TEMP_PARAM_1, + ACX_SG_TEMP_PARAM_2, + ACX_SG_TEMP_PARAM_3, + ACX_SG_TEMP_PARAM_4, + ACX_SG_TEMP_PARAM_5, + ACX_SG_PARAMS_MAX, + ACX_SG_PARAMS_ALL = 0xff +}; + +struct acx_bt_wlan_coex_param { + struct acx_header header; + + __le32 params[ACX_SG_PARAMS_MAX]; + u8 param_idx; + u8 padding[3]; +} __attribute__ ((packed)); + struct acx_dco_itrim_params { struct acx_header header; @@ -423,52 +475,6 @@ struct acx_dco_itrim_params { __le32 timeout; } __attribute__ ((packed)); -#define PTA_ANTENNA_TYPE_DEF (0) -#define PTA_BT_HP_MAXTIME_DEF (2000) -#define PTA_WLAN_HP_MAX_TIME_DEF (5000) -#define PTA_SENSE_DISABLE_TIMER_DEF (1350) -#define PTA_PROTECTIVE_RX_TIME_DEF (1500) -#define PTA_PROTECTIVE_TX_TIME_DEF (1500) -#define PTA_TIMEOUT_NEXT_BT_LP_PACKET_DEF (3000) -#define PTA_SIGNALING_TYPE_DEF (1) -#define PTA_AFH_LEVERAGE_ON_DEF (0) -#define PTA_NUMBER_QUIET_CYCLE_DEF (0) -#define PTA_MAX_NUM_CTS_DEF (3) -#define PTA_NUMBER_OF_WLAN_PACKETS_DEF (2) -#define PTA_NUMBER_OF_BT_PACKETS_DEF (2) -#define PTA_PROTECTIVE_RX_TIME_FAST_DEF (1500) -#define PTA_PROTECTIVE_TX_TIME_FAST_DEF (3000) -#define PTA_CYCLE_TIME_FAST_DEF (8700) -#define PTA_RX_FOR_AVALANCHE_DEF (5) -#define PTA_ELP_HP_DEF (0) -#define PTA_ANTI_STARVE_PERIOD_DEF (500) -#define PTA_ANTI_STARVE_NUM_CYCLE_DEF (4) -#define PTA_ALLOW_PA_SD_DEF (1) -#define PTA_TIME_BEFORE_BEACON_DEF (6300) -#define PTA_HPDM_MAX_TIME_DEF (1600) -#define PTA_TIME_OUT_NEXT_WLAN_DEF (2550) -#define PTA_AUTO_MODE_NO_CTS_DEF (0) -#define PTA_BT_HP_RESPECTED_DEF (3) -#define PTA_WLAN_RX_MIN_RATE_DEF (24) -#define PTA_ACK_MODE_DEF (1) - -struct acx_bt_wlan_coex_param { - struct acx_header header; - - __le32 per_threshold; - __le32 max_scan_compensation_time; - __le16 nfs_sample_interval; - u8 load_ratio; - u8 auto_ps_mode; - u8 probe_req_compensation; - u8 scan_window_compensation; - u8 antenna_config; - u8 beacon_miss_threshold; - __le32 rate_adaptation_threshold; - s8 rate_adaptation_snr; - u8 padding[3]; -} __attribute__ ((packed)); - struct acx_energy_detection { struct acx_header header; From 1b00f546fc4271d94c1bccb1955ce64d9ace1000 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Thu, 18 Mar 2010 12:26:30 +0200 Subject: [PATCH 27/60] wl1271: Add proper WLAN-BT co-ex configuration, and enable co-ex. Add configuration values for the varous WLAN-BT co-ex configuration parameters, and finally enable WLAN-BT co-ex. Based on preliminary measurements, it appears the co-ex feature is not increasing WLAN power consumption, if BT is not activated. Signed-off-by: Juuso Oikarinen Reviewed-by: Teemu Paasikivi Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271_acx.c | 9 +- drivers/net/wireless/wl12xx/wl1271_acx.h | 62 +--- drivers/net/wireless/wl12xx/wl1271_conf.h | 414 ++++++++++++++++------ drivers/net/wireless/wl12xx/wl1271_init.c | 4 +- drivers/net/wireless/wl12xx/wl1271_main.c | 62 +++- 5 files changed, 370 insertions(+), 181 deletions(-) diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c index 405ae1bf464f..e7c22d3c75ac 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.c +++ b/drivers/net/wireless/wl12xx/wl1271_acx.c @@ -547,7 +547,7 @@ int wl1271_acx_sg_enable(struct wl1271 *wl) goto out; } - pta->enable = ACX_SG_DISABLE; + pta->enable = wl->conf.sg.state; ret = wl1271_cmd_configure(wl, ACX_SG_ENABLE, pta, sizeof(*pta)); if (ret < 0) { @@ -564,7 +564,7 @@ int wl1271_acx_sg_cfg(struct wl1271 *wl) { struct acx_bt_wlan_coex_param *param; struct conf_sg_settings *c = &wl->conf.sg; - int ret; + int i, ret; wl1271_debug(DEBUG_ACX, "acx sg cfg"); @@ -575,8 +575,9 @@ int wl1271_acx_sg_cfg(struct wl1271 *wl) } /* BT-WLAN coext parameters */ - param->params[ACX_SG_BT_PER_THRESHOLD] = c->per_threshold; - param->param_idx = ACX_SG_BT_PER_THRESHOLD; + for (i = 0; i < CONF_SG_PARAMS_MAX; i++) + param->params[i] = c->params[i]; + param->param_idx = CONF_SG_PARAMS_ALL; ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param)); if (ret < 0) { diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h index 88a05cbd3457..dee3c06a717c 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.h +++ b/drivers/net/wireless/wl12xx/wl1271_acx.h @@ -392,12 +392,6 @@ struct acx_conn_monit_params { __le32 bss_lose_timeout; /* number of TU's from synch fail */ } __attribute__ ((packed)); -enum { - ACX_SG_DISABLE = 0, - ACX_SG_PROTECTIVE, - ACX_SG_OPPORTUNISTIC -}; - struct acx_bt_wlan_coex { struct acx_header header; @@ -405,64 +399,10 @@ struct acx_bt_wlan_coex { u8 pad[3]; } __attribute__ ((packed)); -enum { - ACX_SG_BT_PER_THRESHOLD = 0, - ACX_SG_HV3_MAX_OVERRIDE, - ACX_SG_BT_NFS_SAMPLE_INTERVAL, - ACX_SG_BT_LOAD_RATIO, - ACX_SG_AUTO_PS_MODE, - ACX_SG_AUTO_SCAN_PROBE_REQ, - ACX_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3, - ACX_SG_ANTENNA_CONFIGURATION, - ACX_SG_BEACON_MISS_PERCENT, - ACX_SG_RATE_ADAPT_THRESH, - ACX_SG_RATE_ADAPT_SNR, - ACX_SG_WLAN_PS_BT_ACL_MASTER_MIN_BR, - ACX_SG_WLAN_PS_BT_ACL_MASTER_MAX_BR, - ACX_SG_WLAN_PS_MAX_BT_ACL_MASTER_BR, - ACX_SG_WLAN_PS_BT_ACL_SLAVE_MIN_BR, - ACX_SG_WLAN_PS_BT_ACL_SLAVE_MAX_BR, - ACX_SG_WLAN_PS_MAX_BT_ACL_SLAVE_BR, - ACX_SG_WLAN_PS_BT_ACL_MASTER_MIN_EDR, - ACX_SG_WLAN_PS_BT_ACL_MASTER_MAX_EDR, - ACX_SG_WLAN_PS_MAX_BT_ACL_MASTER_EDR, - ACX_SG_WLAN_PS_BT_ACL_SLAVE_MIN_EDR, - ACX_SG_WLAN_PS_BT_ACL_SLAVE_MAX_EDR, - ACX_SG_WLAN_PS_MAX_BT_ACL_SLAVE_EDR, - ACX_SG_RXT, - ACX_SG_TXT, - ACX_SG_ADAPTIVE_RXT_TXT, - ACX_SG_PS_POLL_TIMEOUT, - ACX_SG_UPSD_TIMEOUT, - ACX_SG_WLAN_ACTIVE_BT_ACL_MASTER_MIN_EDR, - ACX_SG_WLAN_ACTIVE_BT_ACL_MASTER_MAX_EDR, - ACX_SG_WLAN_ACTIVE_MAX_BT_ACL_MASTER_EDR, - ACX_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MIN_EDR, - ACX_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MAX_EDR, - ACX_SG_WLAN_ACTIVE_MAX_BT_ACL_SLAVE_EDR, - ACX_SG_WLAN_ACTIVE_BT_ACL_MIN_BR, - ACX_SG_WLAN_ACTIVE_BT_ACL_MAX_BR, - ACX_SG_WLAN_ACTIVE_MAX_BT_ACL_BR, - ACX_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3, - ACX_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP, - ACX_SG_PASSIVE_SCAN_A2DP_BT_TIME, - ACX_SG_PASSIVE_SCAN_A2DP_WLAN_TIME, - ACX_SG_HV3_MAX_SERVED, - ACX_SG_DHCP_TIME, - ACX_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP, - ACX_SG_TEMP_PARAM_1, - ACX_SG_TEMP_PARAM_2, - ACX_SG_TEMP_PARAM_3, - ACX_SG_TEMP_PARAM_4, - ACX_SG_TEMP_PARAM_5, - ACX_SG_PARAMS_MAX, - ACX_SG_PARAMS_ALL = 0xff -}; - struct acx_bt_wlan_coex_param { struct acx_header header; - __le32 params[ACX_SG_PARAMS_MAX]; + __le32 params[CONF_SG_PARAMS_MAX]; u8 param_idx; u8 padding[3]; } __attribute__ ((packed)); diff --git a/drivers/net/wireless/wl12xx/wl1271_conf.h b/drivers/net/wireless/wl12xx/wl1271_conf.h index 6f9e75cc5640..7fcfe06b1412 100644 --- a/drivers/net/wireless/wl12xx/wl1271_conf.h +++ b/drivers/net/wireless/wl12xx/wl1271_conf.h @@ -65,110 +65,318 @@ enum { CONF_HW_RATE_INDEX_MAX = CONF_HW_RATE_INDEX_54MBPS, }; +enum { + CONF_SG_DISABLE = 0, + CONF_SG_PROTECTIVE, + CONF_SG_OPPORTUNISTIC +}; + +enum { + /* + * PER threshold in PPM of the BT voice + * + * Range: 0 - 10000000 + */ + CONF_SG_BT_PER_THRESHOLD = 0, + + /* + * Number of consequent RX_ACTIVE activities to override BT voice + * frames to ensure WLAN connection + * + * Range: 0 - 100 + */ + CONF_SG_HV3_MAX_OVERRIDE, + + /* + * Defines the PER threshold of the BT voice + * + * Range: 0 - 65000 + */ + CONF_SG_BT_NFS_SAMPLE_INTERVAL, + + /* + * Defines the load ratio of BT + * + * Range: 0 - 100 (%) + */ + CONF_SG_BT_LOAD_RATIO, + + /* + * Defines whether the SG will force WLAN host to enter/exit PSM + * + * Range: 1 - SG can force, 0 - host handles PSM + */ + CONF_SG_AUTO_PS_MODE, + + /* + * Compensation percentage of probe requests when scan initiated + * during BT voice/ACL link. + * + * Range: 0 - 255 (%) + */ + CONF_SG_AUTO_SCAN_PROBE_REQ, + + /* + * Compensation percentage of probe requests when active scan initiated + * during BT voice + * + * Range: 0 - 255 (%) + */ + CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3, + + /* + * Defines antenna configuration (single/dual antenna) + * + * Range: 0 - single antenna, 1 - dual antenna + */ + CONF_SG_ANTENNA_CONFIGURATION, + + /* + * The threshold (percent) of max consequtive beacon misses before + * increasing priority of beacon reception. + * + * Range: 0 - 100 (%) + */ + CONF_SG_BEACON_MISS_PERCENT, + + /* + * The rate threshold below which receiving a data frame from the AP + * will increase the priority of the data frame above BT traffic. + * + * Range: 0,2, 5(=5.5), 6, 9, 11, 12, 18, 24, 36, 48, 54 + */ + CONF_SG_RATE_ADAPT_THRESH, + + /* + * Not used currently. + * + * Range: 0 + */ + CONF_SG_RATE_ADAPT_SNR, + + /* + * Configure the min and max time BT gains the antenna + * in WLAN PSM / BT master basic rate + * + * Range: 0 - 255 (ms) + */ + CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_BR, + CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_BR, + + /* + * The time after it expires no new WLAN trigger frame is trasmitted + * in WLAN PSM / BT master basic rate + * + * Range: 0 - 255 (ms) + */ + CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_BR, + + /* + * Configure the min and max time BT gains the antenna + * in WLAN PSM / BT slave basic rate + * + * Range: 0 - 255 (ms) + */ + CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_BR, + CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_BR, + + /* + * The time after it expires no new WLAN trigger frame is trasmitted + * in WLAN PSM / BT slave basic rate + * + * Range: 0 - 255 (ms) + */ + CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_BR, + + /* + * Configure the min and max time BT gains the antenna + * in WLAN PSM / BT master EDR + * + * Range: 0 - 255 (ms) + */ + CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_EDR, + CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_EDR, + + /* + * The time after it expires no new WLAN trigger frame is trasmitted + * in WLAN PSM / BT master EDR + * + * Range: 0 - 255 (ms) + */ + CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_EDR, + + /* + * Configure the min and max time BT gains the antenna + * in WLAN PSM / BT slave EDR + * + * Range: 0 - 255 (ms) + */ + CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_EDR, + CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_EDR, + + /* + * The time after it expires no new WLAN trigger frame is trasmitted + * in WLAN PSM / BT slave EDR + * + * Range: 0 - 255 (ms) + */ + CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_EDR, + + /* + * RX guard time before the beginning of a new BT voice frame during + * which no new WLAN trigger frame is transmitted. + * + * Range: 0 - 100000 (us) + */ + CONF_SG_RXT, + + /* + * TX guard time before the beginning of a new BT voice frame during + * which no new WLAN frame is transmitted. + * + * Range: 0 - 100000 (us) + */ + + CONF_SG_TXT, + + /* + * Enable adaptive RXT/TXT algorithm. If disabled, the host values + * will be utilized. + * + * Range: 0 - disable, 1 - enable + */ + CONF_SG_ADAPTIVE_RXT_TXT, + + /* + * The used WLAN legacy service period during active BT ACL link + * + * Range: 0 - 255 (ms) + */ + CONF_SG_PS_POLL_TIMEOUT, + + /* + * The used WLAN UPSD service period during active BT ACL link + * + * Range: 0 - 255 (ms) + */ + CONF_SG_UPSD_TIMEOUT, + + /* + * Configure the min and max time BT gains the antenna + * in WLAN Active / BT master EDR + * + * Range: 0 - 255 (ms) + */ + CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MIN_EDR, + CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MAX_EDR, + + /* + * The maximum time WLAN can gain the antenna for + * in WLAN Active / BT master EDR + * + * Range: 0 - 255 (ms) + */ + CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_MASTER_EDR, + + /* + * Configure the min and max time BT gains the antenna + * in WLAN Active / BT slave EDR + * + * Range: 0 - 255 (ms) + */ + CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MIN_EDR, + CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MAX_EDR, + + /* + * The maximum time WLAN can gain the antenna for + * in WLAN Active / BT slave EDR + * + * Range: 0 - 255 (ms) + */ + CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_SLAVE_EDR, + + /* + * Configure the min and max time BT gains the antenna + * in WLAN Active / BT basic rate + * + * Range: 0 - 255 (ms) + */ + CONF_SG_WLAN_ACTIVE_BT_ACL_MIN_BR, + CONF_SG_WLAN_ACTIVE_BT_ACL_MAX_BR, + + /* + * The maximum time WLAN can gain the antenna for + * in WLAN Active / BT basic rate + * + * Range: 0 - 255 (ms) + */ + CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_BR, + + /* + * Compensation percentage of WLAN passive scan window if initiated + * during BT voice + * + * Range: 0 - 1000 (%) + */ + CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3, + + /* + * Compensation percentage of WLAN passive scan window if initiated + * during BT A2DP + * + * Range: 0 - 1000 (%) + */ + CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP, + + /* + * Fixed time ensured for BT traffic to gain the antenna during WLAN + * passive scan. + * + * Range: 0 - 1000 ms + */ + CONF_SG_PASSIVE_SCAN_A2DP_BT_TIME, + + /* + * Fixed time ensured for WLAN traffic to gain the antenna during WLAN + * passive scan. + * + * Range: 0 - 1000 ms + */ + CONF_SG_PASSIVE_SCAN_A2DP_WLAN_TIME, + + /* + * Number of consequent BT voice frames not interrupted by WLAN + * + * Range: 0 - 100 + */ + CONF_SG_HV3_MAX_SERVED, + + /* + * Protection time of the DHCP procedure. + * + * Range: 0 - 100000 (ms) + */ + CONF_SG_DHCP_TIME, + + /* + * Compensation percentage of WLAN active scan window if initiated + * during BT A2DP + * + * Range: 0 - 1000 (%) + */ + CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP, + CONF_SG_TEMP_PARAM_1, + CONF_SG_TEMP_PARAM_2, + CONF_SG_TEMP_PARAM_3, + CONF_SG_TEMP_PARAM_4, + CONF_SG_TEMP_PARAM_5, + CONF_SG_PARAMS_MAX, + CONF_SG_PARAMS_ALL = 0xff +}; + struct conf_sg_settings { - /* - * Defines the PER threshold in PPM of the BT voice of which reaching - * this value will trigger raising the priority of the BT voice by - * the BT IP until next NFS sample interval time as defined in - * nfs_sample_interval. - * - * Unit: PER value in PPM (parts per million) - * #Error_packets / #Total_packets - - * Range: u32 - */ - u32 per_threshold; - - /* - * This value is an absolute time in micro-seconds to limit the - * maximum scan duration compensation while in SG - */ - u32 max_scan_compensation_time; - - /* Defines the PER threshold of the BT voice of which reaching this - * value will trigger raising the priority of the BT voice until next - * NFS sample interval time as defined in sample_interval. - * - * Unit: msec - * Range: 1-65000 - */ - u16 nfs_sample_interval; - - /* - * Defines the load ratio for the BT. - * The WLAN ratio is: 100 - load_ratio - * - * Unit: Percent - * Range: 0-100 - */ - u8 load_ratio; - - /* - * true - Co-ex is allowed to enter/exit P.S automatically and - * transparently to the host - * - * false - Co-ex is disallowed to enter/exit P.S and will trigger an - * event to the host to notify for the need to enter/exit P.S - * due to BT change state - * - */ - u8 auto_ps_mode; - - /* - * This parameter defines the compensation percentage of num of probe - * requests in case scan is initiated during BT voice/BT ACL - * guaranteed link. - * - * Unit: Percent - * Range: 0-255 (0 - No compensation) - */ - u8 probe_req_compensation; - - /* - * This parameter defines the compensation percentage of scan window - * size in case scan is initiated during BT voice/BT ACL Guaranteed - * link. - * - * Unit: Percent - * Range: 0-255 (0 - No compensation) - */ - u8 scan_window_compensation; - - /* - * Defines the antenna configuration. - * - * Range: 0 - Single Antenna; 1 - Dual Antenna - */ - u8 antenna_config; - - /* - * The percent out of the Max consecutive beacon miss roaming trigger - * which is the threshold for raising the priority of beacon - * reception. - * - * Range: 1-100 - * N = MaxConsecutiveBeaconMiss - * P = coexMaxConsecutiveBeaconMissPrecent - * Threshold = MIN( N-1, round(N * P / 100)) - */ - u8 beacon_miss_threshold; - - /* - * The RX rate threshold below which rate adaptation is assumed to be - * occurring at the AP which will raise priority for ACTIVE_RX and RX - * SP. - * - * Range: HW_BIT_RATE_* - */ - u32 rate_adaptation_threshold; - - /* - * The SNR above which the RX rate threshold indicating AP rate - * adaptation is valid - * - * Range: -128 - 127 - */ - s8 rate_adaptation_snr; + __le32 params[CONF_SG_PARAMS_MAX]; + u8 state; }; enum conf_rx_queue_type { diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c index 86c30a86a456..d68445c9d333 100644 --- a/drivers/net/wireless/wl12xx/wl1271_init.c +++ b/drivers/net/wireless/wl12xx/wl1271_init.c @@ -160,11 +160,11 @@ int wl1271_init_pta(struct wl1271 *wl) { int ret; - ret = wl1271_acx_sg_enable(wl); + ret = wl1271_acx_sg_cfg(wl); if (ret < 0) return ret; - ret = wl1271_acx_sg_cfg(wl); + ret = wl1271_acx_sg_enable(wl); if (ret < 0) return ret; diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index b3abb455bd70..6501d6e2d3b2 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -48,17 +48,57 @@ static struct conf_drv_settings default_conf = { .sg = { - .per_threshold = 7500, - .max_scan_compensation_time = 120000, - .nfs_sample_interval = 400, - .load_ratio = 50, - .auto_ps_mode = 0, - .probe_req_compensation = 170, - .scan_window_compensation = 50, - .antenna_config = 0, - .beacon_miss_threshold = 60, - .rate_adaptation_threshold = CONF_HW_BIT_RATE_12MBPS, - .rate_adaptation_snr = 0 + .params = { + [CONF_SG_BT_PER_THRESHOLD] = 7500, + [CONF_SG_HV3_MAX_OVERRIDE] = 0, + [CONF_SG_BT_NFS_SAMPLE_INTERVAL] = 400, + [CONF_SG_BT_LOAD_RATIO] = 50, + [CONF_SG_AUTO_PS_MODE] = 0, + [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170, + [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50, + [CONF_SG_ANTENNA_CONFIGURATION] = 0, + [CONF_SG_BEACON_MISS_PERCENT] = 60, + [CONF_SG_RATE_ADAPT_THRESH] = 12, + [CONF_SG_RATE_ADAPT_SNR] = 0, + [CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_BR] = 10, + [CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_BR] = 30, + [CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_BR] = 8, + [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_BR] = 20, + [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_BR] = 50, + /* Note: with UPSD, this should be 4 */ + [CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_BR] = 8, + [CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_EDR] = 7, + [CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_EDR] = 25, + [CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_EDR] = 20, + /* Note: with UPDS, this should be 15 */ + [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_EDR] = 8, + /* Note: with UPDS, this should be 50 */ + [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_EDR] = 40, + /* Note: with UPDS, this should be 10 */ + [CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_EDR] = 20, + [CONF_SG_RXT] = 1200, + [CONF_SG_TXT] = 1000, + [CONF_SG_ADAPTIVE_RXT_TXT] = 1, + [CONF_SG_PS_POLL_TIMEOUT] = 10, + [CONF_SG_UPSD_TIMEOUT] = 10, + [CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MIN_EDR] = 7, + [CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MAX_EDR] = 15, + [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_MASTER_EDR] = 15, + [CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MIN_EDR] = 8, + [CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MAX_EDR] = 20, + [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_SLAVE_EDR] = 15, + [CONF_SG_WLAN_ACTIVE_BT_ACL_MIN_BR] = 20, + [CONF_SG_WLAN_ACTIVE_BT_ACL_MAX_BR] = 50, + [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_BR] = 10, + [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200, + [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP] = 800, + [CONF_SG_PASSIVE_SCAN_A2DP_BT_TIME] = 75, + [CONF_SG_PASSIVE_SCAN_A2DP_WLAN_TIME] = 15, + [CONF_SG_HV3_MAX_SERVED] = 6, + [CONF_SG_DHCP_TIME] = 5000, + [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100, + }, + .state = CONF_SG_PROTECTIVE, }, .rx = { .rx_msdu_life_time = 512000, From a1dd8187d8d8f565976c9e55374dee520cdc2fa3 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Thu, 18 Mar 2010 12:26:31 +0200 Subject: [PATCH 28/60] wl1271: Move platform device registration from _spi to _main In order to get the platform device for both SPI and SDIO, move the platform device registration to wl1271_main.c from wl1271_spi.c. Signed-off-by: Juuso Oikarinen Reviewed-by: Teemu Paasikivi Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271_main.c | 37 +++++++++++++++++++++-- drivers/net/wireless/wl12xx/wl1271_spi.c | 31 ++----------------- 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index 6501d6e2d3b2..ad9c49111678 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "wl1271.h" #include "wl12xx_80211.h" @@ -280,6 +281,21 @@ static struct conf_drv_settings default_conf = { } }; +static void wl1271_device_release(struct device *dev) +{ + +} + +static struct platform_device wl1271_device = { + .name = "wl1271", + .id = -1, + + /* device model insists to have a release function */ + .dev = { + .release = wl1271_device_release, + }, +}; + static LIST_HEAD(wl_list); static void wl1271_conf_init(struct wl1271 *wl) @@ -2025,12 +2041,13 @@ struct ieee80211_hw *wl1271_alloc_hw(void) { struct ieee80211_hw *hw; struct wl1271 *wl; - int i; + int i, ret; hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops); if (!hw) { wl1271_error("could not alloc ieee80211_hw"); - return ERR_PTR(-ENOMEM); + ret = -ENOMEM; + goto err; } wl = hw->priv; @@ -2070,12 +2087,28 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl1271_debugfs_init(wl); + /* Register platform device */ + ret = platform_device_register(&wl1271_device); + if (ret) { + wl1271_error("couldn't register platform device"); + goto err_hw; + } + dev_set_drvdata(&wl1271_device.dev, wl); + + return hw; + +err_hw: + ieee80211_unregister_hw(wl->hw); + +err: + return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(wl1271_alloc_hw); int wl1271_free_hw(struct wl1271 *wl) { + platform_device_unregister(&wl1271_device); ieee80211_unregister_hw(wl->hw); wl1271_debugfs_exit(wl); diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.c b/drivers/net/wireless/wl12xx/wl1271_spi.c index 0eea73392ea3..4c129c76c29d 100644 --- a/drivers/net/wireless/wl12xx/wl1271_spi.c +++ b/drivers/net/wireless/wl12xx/wl1271_spi.c @@ -23,7 +23,6 @@ #include #include -#include #include #include #include @@ -332,21 +331,6 @@ static irqreturn_t wl1271_irq(int irq, void *cookie) return IRQ_HANDLED; } -static void wl1271_device_release(struct device *dev) -{ - -} - -static struct platform_device wl1271_device = { - .name = "wl1271", - .id = -1, - - /* device model insists to have a release function */ - .dev = { - .release = wl1271_device_release, - }, -}; - static void wl1271_spi_set_power(struct wl1271 *wl, bool enable) { if (wl->set_power) @@ -422,28 +406,18 @@ static int __devinit wl1271_probe(struct spi_device *spi) disable_irq(wl->irq); - ret = platform_device_register(&wl1271_device); - if (ret) { - wl1271_error("couldn't register platform device"); - goto out_irq; - } - dev_set_drvdata(&wl1271_device.dev, wl); - ret = wl1271_init_ieee80211(wl); if (ret) - goto out_platform; + goto out_irq; ret = wl1271_register_hw(wl); if (ret) - goto out_platform; + goto out_irq; wl1271_notice("initialized"); return 0; - out_platform: - platform_device_unregister(&wl1271_device); - out_irq: free_irq(wl->irq, wl); @@ -457,7 +431,6 @@ static int __devexit wl1271_remove(struct spi_device *spi) { struct wl1271 *wl = dev_get_drvdata(&spi->dev); - platform_device_unregister(&wl1271_device); free_irq(wl->irq, wl); wl1271_free_hw(wl); From 7fc3a8647d2eaa8bc2f7ac7e9baff55199da7be6 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Thu, 18 Mar 2010 12:26:32 +0200 Subject: [PATCH 29/60] wl1271: Add sysfs file to control BT co-ex state Add a sysfs file to control the state of the BT co-ex (enable or disable it.) By default, the BT co-ex is enabled. Signed-off-by: Juuso Oikarinen Reviewed-by: Teemu Paasikivi Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271.h | 2 + drivers/net/wireless/wl12xx/wl1271_acx.c | 7 ++- drivers/net/wireless/wl12xx/wl1271_acx.h | 2 +- drivers/net/wireless/wl12xx/wl1271_init.c | 2 +- drivers/net/wireless/wl12xx/wl1271_main.c | 72 +++++++++++++++++++++++ 5 files changed, 81 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index 7397999deb19..23a0c7e22939 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -484,6 +484,8 @@ struct wl1271 { /* Current chipset configuration */ struct conf_drv_settings conf; + bool sg_enabled; + struct list_head list; }; diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c index e7c22d3c75ac..7e337cea9905 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.c +++ b/drivers/net/wireless/wl12xx/wl1271_acx.c @@ -534,7 +534,7 @@ out: } -int wl1271_acx_sg_enable(struct wl1271 *wl) +int wl1271_acx_sg_enable(struct wl1271 *wl, bool enable) { struct acx_bt_wlan_coex *pta; int ret; @@ -547,7 +547,10 @@ int wl1271_acx_sg_enable(struct wl1271 *wl) goto out; } - pta->enable = wl->conf.sg.state; + if (enable) + pta->enable = wl->conf.sg.state; + else + pta->enable = CONF_SG_DISABLE; ret = wl1271_cmd_configure(wl, ACX_SG_ENABLE, pta, sizeof(*pta)); if (ret < 0) { diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h index dee3c06a717c..8e5870fa9609 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.h +++ b/drivers/net/wireless/wl12xx/wl1271_acx.h @@ -1005,7 +1005,7 @@ int wl1271_acx_dco_itrim_params(struct wl1271 *wl); int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter); int wl1271_acx_beacon_filter_table(struct wl1271 *wl); int wl1271_acx_conn_monit_params(struct wl1271 *wl); -int wl1271_acx_sg_enable(struct wl1271 *wl); +int wl1271_acx_sg_enable(struct wl1271 *wl, bool enable); int wl1271_acx_sg_cfg(struct wl1271 *wl); int wl1271_acx_cca_threshold(struct wl1271 *wl); int wl1271_acx_bcn_dtim_options(struct wl1271 *wl); diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c index d68445c9d333..d9335fc2a575 100644 --- a/drivers/net/wireless/wl12xx/wl1271_init.c +++ b/drivers/net/wireless/wl12xx/wl1271_init.c @@ -164,7 +164,7 @@ int wl1271_init_pta(struct wl1271 *wl) if (ret < 0) return ret; - ret = wl1271_acx_sg_enable(wl); + ret = wl1271_acx_sg_enable(wl, wl->sg_enabled); if (ret < 0) return ret; diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index ad9c49111678..abf9f22ee968 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -1982,6 +1982,68 @@ static const struct ieee80211_ops wl1271_ops = { CFG80211_TESTMODE_CMD(wl1271_tm_cmd) }; +static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct wl1271 *wl = dev_get_drvdata(dev); + ssize_t len; + + /* FIXME: what's the maximum length of buf? page size?*/ + len = 500; + + mutex_lock(&wl->mutex); + len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n", + wl->sg_enabled); + mutex_unlock(&wl->mutex); + + return len; + +} + +static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct wl1271 *wl = dev_get_drvdata(dev); + unsigned long res; + int ret; + + ret = strict_strtoul(buf, 10, &res); + + if (ret < 0) { + wl1271_warning("incorrect value written to bt_coex_mode"); + return count; + } + + mutex_lock(&wl->mutex); + + res = !!res; + + if (res == wl->sg_enabled) + goto out; + + wl->sg_enabled = res; + + if (wl->state == WL1271_STATE_OFF) + goto out; + + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + goto out; + + wl1271_acx_sg_enable(wl, wl->sg_enabled); + wl1271_ps_elp_sleep(wl); + + out: + mutex_unlock(&wl->mutex); + return count; +} + +static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR, + wl1271_sysfs_show_bt_coex_state, + wl1271_sysfs_store_bt_coex_state); + int wl1271_register_hw(struct wl1271 *wl) { int ret; @@ -2073,6 +2135,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->band = IEEE80211_BAND_2GHZ; wl->vif = NULL; wl->flags = 0; + wl->sg_enabled = true; for (i = 0; i < ACX_TX_DESCRIPTORS; i++) wl->tx_frames[i] = NULL; @@ -2095,9 +2158,18 @@ struct ieee80211_hw *wl1271_alloc_hw(void) } dev_set_drvdata(&wl1271_device.dev, wl); + /* Create sysfs file to control bt coex state */ + ret = device_create_file(&wl1271_device.dev, &dev_attr_bt_coex_state); + if (ret < 0) { + wl1271_error("failed to create sysfs file bt_coex_state"); + goto err_platform; + } return hw; +err_platform: + platform_device_unregister(&wl1271_device); + err_hw: ieee80211_unregister_hw(wl->hw); From 12bd89499f22ec01bbff21fd04a347ee82726bf6 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Thu, 18 Mar 2010 12:26:33 +0200 Subject: [PATCH 30/60] wl1271: enable WMM Everything is ready now and we can enable WMM in wl1271. Signed-off-by: Kalle Valo Reviewed-by: Juuso Oikarinen Signed-off-by: Juuso Oikarinen Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271_main.c | 2 ++ drivers/net/wireless/wl12xx/wl1271_tx.h | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index abf9f22ee968..d21bfe29531d 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -2091,6 +2091,8 @@ int wl1271_init_ieee80211(struct wl1271 *wl) if (wl1271_11a_enabled()) wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl1271_band_5ghz; + wl->hw->queues = 4; + SET_IEEE80211_DEV(wl->hw, wl1271_wl_to_dev(wl)); return 0; diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.h b/drivers/net/wireless/wl12xx/wl1271_tx.h index 8b9f6b4f5652..5e6c27a57415 100644 --- a/drivers/net/wireless/wl12xx/wl1271_tx.h +++ b/drivers/net/wireless/wl12xx/wl1271_tx.h @@ -125,9 +125,6 @@ struct wl1271_tx_hw_res_if { static inline int wl1271_tx_get_queue(int queue) { - /* FIXME: use best effort until WMM is enabled */ - return CONF_TX_AC_BE; - switch (queue) { case 0: return CONF_TX_AC_VO; From 899e6e65c39990a76c17940625dbe6001f618734 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Thu, 18 Mar 2010 12:26:34 +0200 Subject: [PATCH 31/60] wl1271: get pspoll and nullfunc templates from mac80211 mac80211 now can create pspoll and nullfunc templates, better to use those. Signed-off-by: Kalle Valo Reviewed-by: Juuso Oikarinen Signed-off-by: Juuso Oikarinen Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271_cmd.c | 44 +++++++++++------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c index d59b3830a6a5..3a615fac54e8 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.c +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c @@ -776,41 +776,37 @@ static int wl1271_build_extended_rates(u8 *rates, u8 band) int wl1271_cmd_build_null_data(struct wl1271 *wl) { - struct wl12xx_null_data_template template; + struct sk_buff *skb; + int ret = 0; - if (!is_zero_ether_addr(wl->bssid)) { - memcpy(template.header.da, wl->bssid, ETH_ALEN); - memcpy(template.header.bssid, wl->bssid, ETH_ALEN); - } else { - memset(template.header.da, 0xff, ETH_ALEN); - memset(template.header.bssid, 0xff, ETH_ALEN); - } + skb = ieee80211_nullfunc_get(wl->hw, wl->vif); + if (!skb) + goto out; - memcpy(template.header.sa, wl->mac_addr, ETH_ALEN); - template.header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_DATA | - IEEE80211_STYPE_NULLFUNC | - IEEE80211_FCTL_TODS); + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, skb->data, + skb->len); - return wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, &template, - sizeof(template)); +out: + dev_kfree_skb(skb); + return ret; } int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid) { - struct wl12xx_ps_poll_template template; + struct sk_buff *skb; + int ret = 0; - memcpy(template.bssid, wl->bssid, ETH_ALEN); - memcpy(template.ta, wl->mac_addr, ETH_ALEN); + skb = ieee80211_pspoll_get(wl->hw, wl->vif); + if (!skb) + goto out; - /* aid in PS-Poll has its two MSBs each set to 1 */ - template.aid = cpu_to_le16(1 << 15 | 1 << 14 | aid); - - template.fc = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); - - return wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, &template, - sizeof(template)); + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, skb->data, + skb->len); +out: + dev_kfree_skb(skb); + return ret; } int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len, From 818e3063a9546fcd826155dd47582a6e2f4d1c37 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Thu, 18 Mar 2010 12:26:35 +0200 Subject: [PATCH 32/60] wl1271: get probe request template from mac80211 If we get probe request from mac80211, we can remove two functions. Signed-off-by: Kalle Valo Reviewed-by: Juuso Oikarinen Signed-off-by: Juuso Oikarinen Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271_cmd.c | 135 +++++---------------- drivers/net/wireless/wl12xx/wl1271_cmd.h | 11 +- drivers/net/wireless/wl12xx/wl1271_event.c | 2 + drivers/net/wireless/wl12xx/wl1271_main.c | 6 +- 4 files changed, 39 insertions(+), 115 deletions(-) diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c index 3a615fac54e8..8364270b2d5c 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.c +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c @@ -546,9 +546,9 @@ out: return ret; } -int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, - u8 active_scan, u8 high_prio, u8 band, - u8 probe_requests) +int wl1271_cmd_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, + const u8 *ie, size_t ie_len, u8 active_scan, + u8 high_prio, u8 band, u8 probe_requests) { struct wl1271_cmd_trigger_scan_to *trigger = NULL; @@ -619,12 +619,13 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, params->params.num_channels = j; - if (len && ssid) { - params->params.ssid_len = len; - memcpy(params->params.ssid, ssid, len); + if (ssid_len && ssid) { + params->params.ssid_len = ssid_len; + memcpy(params->params.ssid, ssid, ssid_len); } - ret = wl1271_cmd_build_probe_req(wl, ssid, len, ieee_band); + ret = wl1271_cmd_build_probe_req(wl, ssid, ssid_len, + ie, ie_len, ieee_band); if (ret < 0) { wl1271_error("PROBE request template failed"); goto out; @@ -655,9 +656,9 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, wl->scan.active = active_scan; wl->scan.high_prio = high_prio; wl->scan.probe_requests = probe_requests; - if (len && ssid) { - wl->scan.ssid_len = len; - memcpy(wl->scan.ssid, ssid, len); + if (ssid_len && ssid) { + wl->scan.ssid_len = ssid_len; + memcpy(wl->scan.ssid, ssid, ssid_len); } else wl->scan.ssid_len = 0; } @@ -714,66 +715,6 @@ out: return ret; } -static int wl1271_build_basic_rates(u8 *rates, u8 band) -{ - u8 index = 0; - - if (band == IEEE80211_BAND_2GHZ) { - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB; - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB; - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_5MB; - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_11MB; - } else if (band == IEEE80211_BAND_5GHZ) { - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_6MB; - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_12MB; - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_24MB; - } else { - wl1271_error("build_basic_rates invalid band: %d", band); - } - - return index; -} - -static int wl1271_build_extended_rates(u8 *rates, u8 band) -{ - u8 index = 0; - - if (band == IEEE80211_BAND_2GHZ) { - rates[index++] = IEEE80211_OFDM_RATE_6MB; - rates[index++] = IEEE80211_OFDM_RATE_9MB; - rates[index++] = IEEE80211_OFDM_RATE_12MB; - rates[index++] = IEEE80211_OFDM_RATE_18MB; - rates[index++] = IEEE80211_OFDM_RATE_24MB; - rates[index++] = IEEE80211_OFDM_RATE_36MB; - rates[index++] = IEEE80211_OFDM_RATE_48MB; - rates[index++] = IEEE80211_OFDM_RATE_54MB; - } else if (band == IEEE80211_BAND_5GHZ) { - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_9MB; - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_18MB; - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_24MB; - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_36MB; - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_48MB; - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_54MB; - } else { - wl1271_error("build_basic_rates invalid band: %d", band); - } - - return index; -} - int wl1271_cmd_build_null_data(struct wl1271 *wl) { struct sk_buff *skb; @@ -809,53 +750,31 @@ out: return ret; } -int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len, - u8 band) +int wl1271_cmd_build_probe_req(struct wl1271 *wl, + const u8 *ssid, size_t ssid_len, + const u8 *ie, size_t ie_len, u8 band) { - struct wl12xx_probe_req_template template; - struct wl12xx_ie_rates *rates; - char *ptr; - u16 size; + struct sk_buff *skb; int ret; - ptr = (char *)&template; - size = sizeof(struct ieee80211_header); + skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len, + ie, ie_len); + if (!skb) { + ret = -ENOMEM; + goto out; + } - memset(template.header.da, 0xff, ETH_ALEN); - memset(template.header.bssid, 0xff, ETH_ALEN); - memcpy(template.header.sa, wl->mac_addr, ETH_ALEN); - template.header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); - - /* IEs */ - /* SSID */ - template.ssid.header.id = WLAN_EID_SSID; - template.ssid.header.len = ssid_len; - if (ssid_len && ssid) - memcpy(template.ssid.ssid, ssid, ssid_len); - size += sizeof(struct wl12xx_ie_header) + ssid_len; - ptr += size; - - /* Basic Rates */ - rates = (struct wl12xx_ie_rates *)ptr; - rates->header.id = WLAN_EID_SUPP_RATES; - rates->header.len = wl1271_build_basic_rates(rates->rates, band); - size += sizeof(struct wl12xx_ie_header) + rates->header.len; - ptr += sizeof(struct wl12xx_ie_header) + rates->header.len; - - /* Extended rates */ - rates = (struct wl12xx_ie_rates *)ptr; - rates->header.id = WLAN_EID_EXT_SUPP_RATES; - rates->header.len = wl1271_build_extended_rates(rates->rates, band); - size += sizeof(struct wl12xx_ie_header) + rates->header.len; - - wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", &template, size); + wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len); if (band == IEEE80211_BAND_2GHZ) ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, - &template, size); + skb->data, skb->len); else ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, - &template, size); + skb->data, skb->len); + +out: + dev_kfree_skb(skb); return ret; } diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.h b/drivers/net/wireless/wl12xx/wl1271_cmd.h index 4297205b8d6d..e936d3ace7b8 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.h +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.h @@ -41,15 +41,16 @@ int wl1271_cmd_data_path(struct wl1271 *wl, bool enable); int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode, bool send); int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer, size_t len); -int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, - u8 active_scan, u8 high_prio, u8 band, - u8 probe_requests); +int wl1271_cmd_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, + const u8 *ie, size_t ie_len, u8 active_scan, + u8 high_prio, u8 band, u8 probe_requests); int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, void *buf, size_t buf_len); int wl1271_cmd_build_null_data(struct wl1271 *wl); int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid); -int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len, - u8 band); +int wl1271_cmd_build_probe_req(struct wl1271 *wl, + const u8 *ssid, size_t ssid_len, + const u8 *ie, size_t ie_len, u8 band); int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id); int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, u8 key_size, const u8 *key, const u8 *addr, diff --git a/drivers/net/wireless/wl12xx/wl1271_event.c b/drivers/net/wireless/wl12xx/wl1271_event.c index 5533519a1418..4d35af96c597 100644 --- a/drivers/net/wireless/wl12xx/wl1271_event.c +++ b/drivers/net/wireless/wl12xx/wl1271_event.c @@ -44,7 +44,9 @@ static int wl1271_event_scan_complete(struct wl1271 *wl, * scanning as it checks that. */ clear_bit(WL1271_FLAG_SCANNING, &wl->flags); + /* FIXME: ie missing! */ wl1271_cmd_scan(wl, wl->scan.ssid, wl->scan.ssid_len, + NULL, 0, wl->scan.active, wl->scan.high_prio, WL1271_SCAN_BAND_5_GHZ, diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index d21bfe29531d..fbc42ceb39d8 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -1566,10 +1566,12 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw, goto out; if (wl1271_11a_enabled()) - ret = wl1271_cmd_scan(hw->priv, ssid, len, 1, 0, + ret = wl1271_cmd_scan(hw->priv, ssid, len, + req->ie, req->ie_len, 1, 0, WL1271_SCAN_BAND_DUAL, 3); else - ret = wl1271_cmd_scan(hw->priv, ssid, len, 1, 0, + ret = wl1271_cmd_scan(hw->priv, ssid, len, + req->ie, req->ie_len, 1, 0, WL1271_SCAN_BAND_2_4_GHZ, 3); wl1271_ps_elp_sleep(wl); From 023e082609ba3225dbd5c33933a90156d2201d7f Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Thu, 18 Mar 2010 12:26:36 +0200 Subject: [PATCH 33/60] wl1271: create qos nullfunc template Needed for U-APSD. Signed-off-by: Kalle Valo Reviewed-by: Juuso Oikarinen Signed-off-by: Juuso Oikarinen Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271_cmd.c | 22 ++++++++++++++++++++++ drivers/net/wireless/wl12xx/wl1271_cmd.h | 1 + 2 files changed, 23 insertions(+) diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c index 8364270b2d5c..0cbb0e1e3d40 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.c +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "wl1271.h" #include "wl1271_reg.h" @@ -778,6 +779,27 @@ out: return ret; } +int wl1271_build_qos_null_data(struct wl1271 *wl) +{ + struct ieee80211_qos_hdr template; + + memset(&template, 0, sizeof(template)); + + memcpy(template.addr1, wl->bssid, ETH_ALEN); + memcpy(template.addr2, wl->mac_addr, ETH_ALEN); + memcpy(template.addr3, wl->bssid, ETH_ALEN); + + template.frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_NULLFUNC | + IEEE80211_FCTL_TODS); + + /* FIXME: not sure what priority to use here */ + template.qos_ctrl = cpu_to_le16(0); + + return wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, &template, + sizeof(template)); +} + int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id) { struct wl1271_cmd_set_keys *cmd; diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.h b/drivers/net/wireless/wl12xx/wl1271_cmd.h index e936d3ace7b8..6324bbf36843 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.h +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.h @@ -51,6 +51,7 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid); int wl1271_cmd_build_probe_req(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, u8 band); +int wl1271_build_qos_null_data(struct wl1271 *wl); int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id); int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, u8 key_size, const u8 *key, const u8 *addr, From 522ac25048676b2141d0560841654323339c0cbb Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Thu, 18 Mar 2010 12:26:37 +0200 Subject: [PATCH 34/60] wl1271: fix ps scheme in wl1271_op_conf_tx() Commit "wl1271: implement WMM" accidentally used CONF_PS_SCHEME_LEGACY_PSPOLL, but instead CONF_PS_SCHEME_LEGACY should be used as earlier. Signed-off-by: Kalle Valo Reviewed-by: Juuso Oikarinen Signed-off-by: Juuso Oikarinen Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index fbc42ceb39d8..2a2e5841a0e0 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -1801,7 +1801,7 @@ static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue, ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue), CONF_CHANNEL_TYPE_EDCF, wl1271_tx_get_queue(queue), - CONF_PS_SCHEME_LEGACY_PSPOLL, + CONF_PS_SCHEME_LEGACY, CONF_ACK_POLICY_LEGACY, 0, 0); if (ret < 0) goto out_sleep; From 4695dc917d9ec7a38cb4f91fceb2b78aaec1f945 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Thu, 18 Mar 2010 12:26:38 +0200 Subject: [PATCH 35/60] wl1271: enable U-APSD wl1271 supports U-APSD similarly as wl1251, so let's enable it. Signed-off-by: Kalle Valo Reviewed-by: Juuso Oikarinen Signed-off-by: Juuso Oikarinen Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271_main.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index 2a2e5841a0e0..037a4f4036fd 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -1781,6 +1781,7 @@ static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue, const struct ieee80211_tx_queue_params *params) { struct wl1271 *wl = hw->priv; + u8 ps_scheme; int ret; mutex_lock(&wl->mutex); @@ -1798,11 +1799,15 @@ static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue, if (ret < 0) goto out_sleep; + if (params->uapsd) + ps_scheme = CONF_PS_SCHEME_UPSD_TRIGGER; + else + ps_scheme = CONF_PS_SCHEME_LEGACY; + ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue), CONF_CHANNEL_TYPE_EDCF, wl1271_tx_get_queue(queue), - CONF_PS_SCHEME_LEGACY, - CONF_ACK_POLICY_LEGACY, 0, 0); + ps_scheme, CONF_ACK_POLICY_LEGACY, 0, 0); if (ret < 0) goto out_sleep; @@ -2083,6 +2088,7 @@ int wl1271_init_ieee80211(struct wl1271 *wl) IEEE80211_HW_NOISE_DBM | IEEE80211_HW_BEACON_FILTER | IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_HAS_RATE_CONTROL; wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | From 1b72aecd950c4c3cec2d66dbe5436c9e25a487b7 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Thu, 18 Mar 2010 12:26:39 +0200 Subject: [PATCH 36/60] wl1271: Fix MAC address handling This patch fixes MAC address handling. To achieve this, firmware booting had to be delayed from the previous op_start to op_add_interface, which is the point when the driver gets to know the configured MAC address. As the wl1271 only supports one virtual interface, this even seems quite logical. Signed-off-by: Juuso Oikarinen Reviewed-by: Kalle Valo Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271_boot.c | 8 ++ drivers/net/wireless/wl12xx/wl1271_main.c | 149 +++++++++------------- 2 files changed, 68 insertions(+), 89 deletions(-) diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.c b/drivers/net/wireless/wl12xx/wl1271_boot.c index f88d52e87e82..41c6affbee29 100644 --- a/drivers/net/wireless/wl12xx/wl1271_boot.c +++ b/drivers/net/wireless/wl12xx/wl1271_boot.c @@ -228,6 +228,14 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) nvs_len = sizeof(wl->nvs->nvs); nvs_ptr = (u8 *)wl->nvs->nvs; + /* update current MAC address to NVS */ + nvs_ptr[11] = wl->mac_addr[0]; + nvs_ptr[10] = wl->mac_addr[1]; + nvs_ptr[6] = wl->mac_addr[2]; + nvs_ptr[5] = wl->mac_addr[3]; + nvs_ptr[4] = wl->mac_addr[4]; + nvs_ptr[3] = wl->mac_addr[5]; + /* * Layout before the actual NVS tables: * 1 byte : burst length. diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index 037a4f4036fd..d8cb51410b2f 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -570,40 +570,6 @@ out: return ret; } -static int wl1271_update_mac_addr(struct wl1271 *wl) -{ - int ret = 0; - u8 *nvs_ptr = (u8 *)wl->nvs->nvs; - - /* get mac address from the NVS */ - wl->mac_addr[0] = nvs_ptr[11]; - wl->mac_addr[1] = nvs_ptr[10]; - wl->mac_addr[2] = nvs_ptr[6]; - wl->mac_addr[3] = nvs_ptr[5]; - wl->mac_addr[4] = nvs_ptr[4]; - wl->mac_addr[5] = nvs_ptr[3]; - - /* FIXME: if it is a zero-address, we should bail out. Now, instead, - we randomize an address */ - if (is_zero_ether_addr(wl->mac_addr)) { - static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf}; - memcpy(wl->mac_addr, nokia_oui, 3); - get_random_bytes(wl->mac_addr + 3, 3); - - /* update this address to the NVS */ - nvs_ptr[11] = wl->mac_addr[0]; - nvs_ptr[10] = wl->mac_addr[1]; - nvs_ptr[6] = wl->mac_addr[2]; - nvs_ptr[5] = wl->mac_addr[3]; - nvs_ptr[4] = wl->mac_addr[4]; - nvs_ptr[3] = wl->mac_addr[5]; - } - - SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr); - - return ret; -} - static int wl1271_fetch_nvs(struct wl1271 *wl) { const struct firmware *fw; @@ -633,8 +599,6 @@ static int wl1271_fetch_nvs(struct wl1271 *wl) memcpy(wl->nvs, fw->data, sizeof(struct wl1271_nvs_file)); - ret = wl1271_update_mac_addr(wl); - out: release_firmware(fw); @@ -951,14 +915,59 @@ static struct notifier_block wl1271_dev_notifier = { static int wl1271_op_start(struct ieee80211_hw *hw) +{ + wl1271_debug(DEBUG_MAC80211, "mac80211 start"); + + /* + * We have to delay the booting of the hardware because + * we need to know the local MAC address before downloading and + * initializing the firmware. The MAC address cannot be changed + * after boot, and without the proper MAC address, the firmware + * will not function properly. + * + * The MAC address is first known when the corresponding interface + * is added. That is where we will initialize the hardware. + */ + + return 0; +} + +static void wl1271_op_stop(struct ieee80211_hw *hw) +{ + wl1271_debug(DEBUG_MAC80211, "mac80211 stop"); +} + +static int wl1271_op_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct wl1271 *wl = hw->priv; int retries = WL1271_BOOT_RETRIES; int ret = 0; - wl1271_debug(DEBUG_MAC80211, "mac80211 start"); + wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", + vif->type, vif->addr); mutex_lock(&wl->mutex); + if (wl->vif) { + ret = -EBUSY; + goto out; + } + + wl->vif = vif; + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + wl->bss_type = BSS_TYPE_STA_BSS; + break; + case NL80211_IFTYPE_ADHOC: + wl->bss_type = BSS_TYPE_IBSS; + break; + default: + ret = -EOPNOTSUPP; + goto out; + } + + memcpy(wl->mac_addr, vif->addr, ETH_ALEN); if (wl->state != WL1271_STATE_OFF) { wl1271_error("cannot start because not in off state: %d", @@ -1014,20 +1023,20 @@ out: return ret; } -static void wl1271_op_stop(struct ieee80211_hw *hw) +static void wl1271_op_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct wl1271 *wl = hw->priv; int i; - wl1271_info("down"); + mutex_lock(&wl->mutex); + wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface"); - wl1271_debug(DEBUG_MAC80211, "mac80211 stop"); + wl1271_info("down"); unregister_inetaddr_notifier(&wl1271_dev_notifier); list_del(&wl->list); - mutex_lock(&wl->mutex); - WARN_ON(wl->state != WL1271_STATE_ON); if (test_and_clear_bit(WL1271_FLAG_SCANNING, &wl->flags)) { @@ -1070,6 +1079,7 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) wl->rate_set = CONF_TX_RATE_MASK_BASIC; wl->sta_rate_set = 0; wl->flags = 0; + wl->vif = NULL; for (i = 0; i < NUM_TX_QUEUES; i++) wl->tx_blocks_freed[i] = 0; @@ -1078,53 +1088,6 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) mutex_unlock(&wl->mutex); } -static int wl1271_op_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct wl1271 *wl = hw->priv; - int ret = 0; - - wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", - vif->type, vif->addr); - - mutex_lock(&wl->mutex); - if (wl->vif) { - ret = -EBUSY; - goto out; - } - - wl->vif = vif; - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - wl->bss_type = BSS_TYPE_STA_BSS; - break; - case NL80211_IFTYPE_ADHOC: - wl->bss_type = BSS_TYPE_IBSS; - break; - default: - ret = -EOPNOTSUPP; - goto out; - } - - /* FIXME: what if conf->mac_addr changes? */ - -out: - mutex_unlock(&wl->mutex); - return ret; -} - -static void wl1271_op_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct wl1271 *wl = hw->priv; - - mutex_lock(&wl->mutex); - wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface"); - wl->vif = NULL; - mutex_unlock(&wl->mutex); -} - #if 0 static int wl1271_op_config_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -2114,6 +2077,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void) struct ieee80211_hw *hw; struct wl1271 *wl; int i, ret; + static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf}; hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops); if (!hw) { @@ -2155,6 +2119,13 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->state = WL1271_STATE_OFF; mutex_init(&wl->mutex); + /* + * FIXME: we should use a zero MAC address here, but for now we + * generate a random Nokia address. + */ + memcpy(wl->mac_addr, nokia_oui, 3); + get_random_bytes(wl->mac_addr + 3, 3); + /* Apply default driver configuration. */ wl1271_conf_init(wl); From c76a0d6c9357d3e6ac32793b89a0a303966e1c4c Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Thu, 18 Mar 2010 12:26:40 +0200 Subject: [PATCH 37/60] wl1271: Remove deprecated interface config function It's no longer needed. Signed-off-by: Juuso Oikarinen Reviewed-by: Kalle Valo Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271_main.c | 68 ----------------------- 1 file changed, 68 deletions(-) diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index d8cb51410b2f..688009ba94fa 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -1088,73 +1088,6 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw, mutex_unlock(&wl->mutex); } -#if 0 -static int wl1271_op_config_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf) -{ - struct wl1271 *wl = hw->priv; - struct sk_buff *beacon; - int ret; - - wl1271_debug(DEBUG_MAC80211, "mac80211 config_interface bssid %pM", - conf->bssid); - wl1271_dump_ascii(DEBUG_MAC80211, "ssid: ", conf->ssid, - conf->ssid_len); - - mutex_lock(&wl->mutex); - - ret = wl1271_ps_elp_wakeup(wl, false); - if (ret < 0) - goto out; - - if (memcmp(wl->bssid, conf->bssid, ETH_ALEN)) { - wl1271_debug(DEBUG_MAC80211, "bssid changed"); - - memcpy(wl->bssid, conf->bssid, ETH_ALEN); - - ret = wl1271_cmd_join(wl, wl->bss_type); - if (ret < 0) - goto out_sleep; - - ret = wl1271_cmd_build_null_data(wl); - if (ret < 0) - goto out_sleep; - } - - wl->ssid_len = conf->ssid_len; - if (wl->ssid_len) - memcpy(wl->ssid, conf->ssid, wl->ssid_len); - - if (conf->changed & IEEE80211_IFCC_BEACON) { - beacon = ieee80211_beacon_get(hw, vif); - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON, - beacon->data, beacon->len); - - if (ret < 0) { - dev_kfree_skb(beacon); - goto out_sleep; - } - - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PROBE_RESPONSE, - beacon->data, beacon->len); - - dev_kfree_skb(beacon); - - if (ret < 0) - goto out_sleep; - } - -out_sleep: - wl1271_ps_elp_sleep(wl); - -out: - mutex_unlock(&wl->mutex); - - return ret; -} -#endif - static int wl1271_join_channel(struct wl1271 *wl, int channel) { int ret = 0; @@ -1940,7 +1873,6 @@ static const struct ieee80211_ops wl1271_ops = { .add_interface = wl1271_op_add_interface, .remove_interface = wl1271_op_remove_interface, .config = wl1271_op_config, -/* .config_interface = wl1271_op_config_interface, */ .prepare_multicast = wl1271_op_prepare_multicast, .configure_filter = wl1271_op_configure_filter, .tx = wl1271_op_tx, From a3b8ea7554862e80a14b83233b37ddd2506d1d70 Mon Sep 17 00:00:00 2001 From: Teemu Paasikivi Date: Thu, 18 Mar 2010 12:26:41 +0200 Subject: [PATCH 38/60] wl1271: Added DEBUG_SDIO flag Added separate flag for SDIO debugging. Previously it has used DEBUG_SPI flag. This patch also includes couple indentation fixes. Signed-off-by: Teemu Paasikivi Reviewed-by: Juuso Oikarinen Signed-off-by: Juuso Oikarinen Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271.h | 1 + drivers/net/wireless/wl12xx/wl1271_sdio.c | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index 23a0c7e22939..4e608067253e 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -53,6 +53,7 @@ enum { DEBUG_MAC80211 = BIT(11), DEBUG_CMD = BIT(12), DEBUG_ACX = BIT(13), + DEBUG_SDIO = BIT(14), DEBUG_ALL = ~0, }; diff --git a/drivers/net/wireless/wl12xx/wl1271_sdio.c b/drivers/net/wireless/wl12xx/wl1271_sdio.c index d9183b64e723..0478919138d4 100644 --- a/drivers/net/wireless/wl12xx/wl1271_sdio.c +++ b/drivers/net/wireless/wl12xx/wl1271_sdio.c @@ -102,14 +102,14 @@ static void wl1271_sdio_init(struct wl1271 *wl) } static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf, - size_t len, bool fixed) + size_t len, bool fixed) { int ret; struct sdio_func *func = wl_to_func(wl); if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) { ((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret); - wl1271_debug(DEBUG_SPI, "sdio read 52 addr 0x%x, byte 0x%02x", + wl1271_debug(DEBUG_SDIO, "sdio read 52 addr 0x%x, byte 0x%02x", addr, ((u8 *)buf)[0]); } else { if (fixed) @@ -117,9 +117,9 @@ static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf, else ret = sdio_memcpy_fromio(func, buf, addr, len); - wl1271_debug(DEBUG_SPI, "sdio read 53 addr 0x%x, %d bytes", + wl1271_debug(DEBUG_SDIO, "sdio read 53 addr 0x%x, %d bytes", addr, len); - wl1271_dump_ascii(DEBUG_SPI, "data: ", buf, len); + wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len); } if (ret) @@ -128,19 +128,19 @@ static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf, } static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf, - size_t len, bool fixed) + size_t len, bool fixed) { int ret; struct sdio_func *func = wl_to_func(wl); if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) { sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret); - wl1271_debug(DEBUG_SPI, "sdio write 52 addr 0x%x, byte 0x%02x", + wl1271_debug(DEBUG_SDIO, "sdio write 52 addr 0x%x, byte 0x%02x", addr, ((u8 *)buf)[0]); } else { - wl1271_debug(DEBUG_SPI, "sdio write 53 addr 0x%x, %d bytes", + wl1271_debug(DEBUG_SDIO, "sdio write 53 addr 0x%x, %d bytes", addr, len); - wl1271_dump_ascii(DEBUG_SPI, "data: ", buf, len); + wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len); if (fixed) ret = sdio_writesb(func, addr, buf, len); @@ -156,6 +156,10 @@ static void wl1271_sdio_set_power(struct wl1271 *wl, bool enable) { struct sdio_func *func = wl_to_func(wl); + /* Let the SDIO stack handle wlan_enable control, so we + * keep host claimed while wlan is in use to keep wl1271 + * alive. + */ if (enable) { sdio_claim_host(func); sdio_enable_func(func); From 801a673ed1b36fc0e7dd92d8bb0090a4dc26a7d0 Mon Sep 17 00:00:00 2001 From: Teemu Paasikivi Date: Thu, 18 Mar 2010 12:26:42 +0200 Subject: [PATCH 39/60] wl1271: Removed duplicate code from module remove function. Removed duplicate code from wl1271_sdio module remove function. For freeing stuff in the remove function wl1271_free_hw function is now used. Signed-off-by: Teemu Paasikivi Reviewed-by: Juuso Oikarinen Signed-off-by: Juuso Oikarinen Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271_sdio.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/net/wireless/wl12xx/wl1271_sdio.c b/drivers/net/wireless/wl12xx/wl1271_sdio.c index 0478919138d4..99b5b3f7a92c 100644 --- a/drivers/net/wireless/wl12xx/wl1271_sdio.c +++ b/drivers/net/wireless/wl12xx/wl1271_sdio.c @@ -248,20 +248,9 @@ static void __devexit wl1271_remove(struct sdio_func *func) { struct wl1271 *wl = sdio_get_drvdata(func); - ieee80211_unregister_hw(wl->hw); - free_irq(wl->irq, wl); - kfree(wl->target_mem_map); - vfree(wl->fw); - wl->fw = NULL; - kfree(wl->nvs); - wl->nvs = NULL; - - kfree(wl->fw_status); - kfree(wl->tx_res_if); - - ieee80211_free_hw(wl->hw); + wl1271_free_hw(wl); } static struct sdio_driver wl1271_sdio_driver = { From 14b228a0d4234e4a2d4e683052da78760e8abf0f Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Thu, 18 Mar 2010 12:26:43 +0200 Subject: [PATCH 40/60] wl1271: Update filters properly This patch adds support for the filters configured by the mac80211 stack. Signed-off-by: Juuso Oikarinen Reviewed-by: Teemu Paasikivi Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271.h | 2 + drivers/net/wireless/wl12xx/wl1271_cmd.c | 9 ---- drivers/net/wireless/wl12xx/wl1271_main.c | 58 +++++++++++++++++++---- 3 files changed, 51 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index 4e608067253e..a0262b7723c3 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -54,6 +54,7 @@ enum { DEBUG_CMD = BIT(12), DEBUG_ACX = BIT(13), DEBUG_SDIO = BIT(14), + DEBUG_FILTERS = BIT(15), DEBUG_ALL = ~0, }; @@ -458,6 +459,7 @@ struct wl1271 { /* Default key (for WEP) */ u32 default_key; + unsigned int filters; unsigned int rx_config; unsigned int rx_filter; diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c index 0cbb0e1e3d40..035ddc06034c 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.c +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c @@ -281,15 +281,6 @@ int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type) join->rx_filter_options = cpu_to_le32(wl->rx_filter); join->bss_type = bss_type; - /* - * FIXME: disable temporarily all filters because after commit - * 9cef8737 "mac80211: fix managed mode BSSID handling" broke - * association. The filter logic needs to be implemented properly - * and once that is done, this hack can be removed. - */ - join->rx_config_options = cpu_to_le32(0); - join->rx_filter_options = cpu_to_le32(WL1271_DEFAULT_RX_FILTER); - if (wl->band == IEEE80211_BAND_2GHZ) join->basic_rate_set = cpu_to_le32(CONF_HW_BIT_RATE_1MBPS | CONF_HW_BIT_RATE_2MBPS | diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index 688009ba94fa..befc2e9b1842 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -1080,6 +1080,7 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw, wl->sta_rate_set = 0; wl->flags = 0; wl->vif = NULL; + wl->filters = 0; for (i = 0; i < NUM_TX_QUEUES; i++) wl->tx_blocks_freed[i] = 0; @@ -1088,6 +1089,40 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw, mutex_unlock(&wl->mutex); } +static void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters) +{ + wl->rx_config = WL1271_DEFAULT_RX_CONFIG; + wl->rx_filter = WL1271_DEFAULT_RX_FILTER; + + /* combine requested filters with current filter config */ + filters = wl->filters | filters; + + wl1271_debug(DEBUG_FILTERS, "RX filters set: "); + + if (filters & FIF_PROMISC_IN_BSS) { + wl1271_debug(DEBUG_FILTERS, " - FIF_PROMISC_IN_BSS"); + wl->rx_config &= ~CFG_UNI_FILTER_EN; + wl->rx_config |= CFG_BSSID_FILTER_EN; + } + if (filters & FIF_BCN_PRBRESP_PROMISC) { + wl1271_debug(DEBUG_FILTERS, " - FIF_BCN_PRBRESP_PROMISC"); + wl->rx_config &= ~CFG_BSSID_FILTER_EN; + wl->rx_config &= ~CFG_SSID_FILTER_EN; + } + if (filters & FIF_OTHER_BSS) { + wl1271_debug(DEBUG_FILTERS, " - FIF_OTHER_BSS"); + wl->rx_config &= ~CFG_BSSID_FILTER_EN; + } + if (filters & FIF_CONTROL) { + wl1271_debug(DEBUG_FILTERS, " - FIF_CONTROL"); + wl->rx_filter |= CFG_RX_CTL_EN; + } + if (filters & FIF_FCSFAIL) { + wl1271_debug(DEBUG_FILTERS, " - FIF_FCSFAIL"); + wl->rx_filter |= CFG_RX_FCS_ERROR; + } +} + static int wl1271_join_channel(struct wl1271 *wl, int channel) { int ret = 0; @@ -1095,12 +1130,12 @@ static int wl1271_join_channel(struct wl1271 *wl, int channel) static const u8 dummy_bssid[ETH_ALEN] = { 0x0b, 0xad, 0xde, 0xad, 0xbe, 0xef }; - /* disable mac filter, so we hear everything */ - wl->rx_config &= ~CFG_BSSID_FILTER_EN; - wl->channel = channel; memcpy(wl->bssid, dummy_bssid, ETH_ALEN); + /* pass through frames from all BSS */ + wl1271_configure_filters(wl, FIF_OTHER_BSS); + /* the dummy join is performed always with STATION BSS type to allow also ad-hoc mode to listen to the surroundings without sending any beacons yet. */ @@ -1126,7 +1161,9 @@ static int wl1271_unjoin_channel(struct wl1271 *wl) clear_bit(WL1271_FLAG_JOINED, &wl->flags); wl->channel = 0; memset(wl->bssid, 0, ETH_ALEN); - wl->rx_config = WL1271_DEFAULT_RX_CONFIG; + + /* stop filterting packets based on bssid */ + wl1271_configure_filters(wl, FIF_OTHER_BSS); out: return ret; @@ -1299,14 +1336,14 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw, if (ret < 0) goto out_sleep; - kfree(fp); - - /* FIXME: We still need to set our filters properly */ - /* determine, whether supported filter values have changed */ if (changed == 0) goto out_sleep; + /* configure filters */ + wl->filters = *total; + wl1271_configure_filters(wl, 0); + /* apply configured filters */ ret = wl1271_acx_rx_config(wl, wl->rx_config, wl->rx_filter); if (ret < 0) @@ -1317,6 +1354,7 @@ out_sleep: out: mutex_unlock(&wl->mutex); + kfree(fp); } static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, @@ -1580,7 +1618,6 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, * and enable the BSSID filter */ memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) { - wl->rx_config |= CFG_BSSID_FILTER_EN; memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN); ret = wl1271_cmd_build_null_data(wl); if (ret < 0) { @@ -1589,6 +1626,9 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, goto out_sleep; } + /* filter out all packets not from this BSSID */ + wl1271_configure_filters(wl, 0); + /* Need to update the BSSID (for filtering etc) */ do_join = true; } From a0cb7be4f4fa765dcfa82675811cd7e7713b5610 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Thu, 18 Mar 2010 12:26:44 +0200 Subject: [PATCH 41/60] wl1271: Don't generate null func template for ad-hoc The null func template is not needed for ad-hoc, and it's generation is not supported for ad-hoc (mac80211 will WARN about it.) Signed-off-by: Juuso Oikarinen Reviewed-by: Teemu Paasikivi Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271_cmd.c | 26 +++++++++++++++++------ drivers/net/wireless/wl12xx/wl1271_main.c | 6 ++---- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c index 035ddc06034c..d005729e0312 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.c +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c @@ -709,18 +709,30 @@ out: int wl1271_cmd_build_null_data(struct wl1271 *wl) { - struct sk_buff *skb; - int ret = 0; + struct sk_buff *skb = NULL; + int size; + void *ptr; + int ret = -ENOMEM; - skb = ieee80211_nullfunc_get(wl->hw, wl->vif); - if (!skb) - goto out; - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, skb->data, - skb->len); + if (wl->bss_type == BSS_TYPE_IBSS) { + size = sizeof(struct wl12xx_null_data_template); + ptr = NULL; + } else { + skb = ieee80211_nullfunc_get(wl->hw, wl->vif); + if (!skb) + goto out; + size = skb->len; + ptr = skb->data; + } + + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, ptr, size); out: dev_kfree_skb(skb); + if (ret) + wl1271_warning("cmd buld null data failed %d", ret); + return ret; } diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index befc2e9b1842..6f6d366491a3 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -1619,12 +1619,10 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, */ memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) { memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN); + ret = wl1271_cmd_build_null_data(wl); - if (ret < 0) { - wl1271_warning("cmd buld null data failed %d", - ret); + if (ret < 0) goto out_sleep; - } /* filter out all packets not from this BSSID */ wl1271_configure_filters(wl, 0); From 2ea9fb3d79aac371e5bb0c4b0f5be7195e8fcd33 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Thu, 18 Mar 2010 12:26:45 +0200 Subject: [PATCH 42/60] wl1271: Remove circular interlocking related to the inetaddr notifier chain Removing the wl1271 from the inet addr notifier chain sometimes causes the registered handler to be called - causing locking problems if the removing function is called from within the mutex. Signed-off-by: Juuso Oikarinen Reviewed-by: Teemu Paasikivi Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index 6f6d366491a3..0bebc456ec0d 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -1029,12 +1029,13 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw, struct wl1271 *wl = hw->priv; int i; + unregister_inetaddr_notifier(&wl1271_dev_notifier); + mutex_lock(&wl->mutex); wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface"); wl1271_info("down"); - unregister_inetaddr_notifier(&wl1271_dev_notifier); list_del(&wl->list); WARN_ON(wl->state != WL1271_STATE_ON); From 3b56dd6a090e905eece023f690298013da4b6b67 Mon Sep 17 00:00:00 2001 From: Teemu Paasikivi Date: Thu, 18 Mar 2010 12:26:46 +0200 Subject: [PATCH 43/60] wl1271: Changed platform_device to be dynamically allocated Changed platform_device to be allocated dynamically from the wl1271_alloc_hw function. Also cleaned up error handling in the wl1271_alloc_hw function and module probe functions. Signed-off-by: Teemu Paasikivi Reviewed-by: Juuso Oikarinen Signed-off-by: Juuso Oikarinen Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271.h | 1 + drivers/net/wireless/wl12xx/wl1271_io.h | 1 + drivers/net/wireless/wl12xx/wl1271_main.c | 42 ++++++++++++++++++----- drivers/net/wireless/wl12xx/wl1271_sdio.c | 3 +- drivers/net/wireless/wl12xx/wl1271_spi.c | 3 +- 5 files changed, 39 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index a0262b7723c3..8f11506f8310 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -353,6 +353,7 @@ struct wl1271_if_operations { }; struct wl1271 { + struct platform_device *plat_dev; struct ieee80211_hw *hw; bool mac80211_registered; diff --git a/drivers/net/wireless/wl12xx/wl1271_io.h b/drivers/net/wireless/wl12xx/wl1271_io.h index 3ff88d971308..d8837ef0bb40 100644 --- a/drivers/net/wireless/wl12xx/wl1271_io.h +++ b/drivers/net/wireless/wl12xx/wl1271_io.h @@ -161,6 +161,7 @@ int wl1271_set_partition(struct wl1271 *wl, /* Functions from wl1271_main.c */ int wl1271_register_hw(struct wl1271 *wl); +void wl1271_unregister_hw(struct wl1271 *wl); int wl1271_init_ieee80211(struct wl1271 *wl); struct ieee80211_hw *wl1271_alloc_hw(void); int wl1271_free_hw(struct wl1271 *wl); diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index 0bebc456ec0d..3daba6c0c77f 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -2008,6 +2008,14 @@ int wl1271_register_hw(struct wl1271 *wl) } EXPORT_SYMBOL_GPL(wl1271_register_hw); +void wl1271_unregister_hw(struct wl1271 *wl) +{ + ieee80211_unregister_hw(wl->hw); + wl->mac80211_registered = false; + +} +EXPORT_SYMBOL_GPL(wl1271_unregister_hw); + int wl1271_init_ieee80211(struct wl1271 *wl) { /* The tx descriptor buffer and the TKIP space. */ @@ -2046,6 +2054,7 @@ EXPORT_SYMBOL_GPL(wl1271_init_ieee80211); struct ieee80211_hw *wl1271_alloc_hw(void) { struct ieee80211_hw *hw; + struct platform_device *plat_dev = NULL; struct wl1271 *wl; int i, ret; static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf}; @@ -2054,15 +2063,25 @@ struct ieee80211_hw *wl1271_alloc_hw(void) if (!hw) { wl1271_error("could not alloc ieee80211_hw"); ret = -ENOMEM; - goto err; + goto err_hw_alloc; } + plat_dev = kmalloc(sizeof(wl1271_device), GFP_KERNEL); + if (!plat_dev) { + wl1271_error("could not allocate platform_device"); + ret = -ENOMEM; + goto err_plat_alloc; + } + + memcpy(plat_dev, &wl1271_device, sizeof(wl1271_device)); + wl = hw->priv; memset(wl, 0, sizeof(*wl)); INIT_LIST_HEAD(&wl->list); wl->hw = hw; + wl->plat_dev = plat_dev; skb_queue_head_init(&wl->tx_queue); @@ -2103,15 +2122,15 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl1271_debugfs_init(wl); /* Register platform device */ - ret = platform_device_register(&wl1271_device); + ret = platform_device_register(wl->plat_dev); if (ret) { wl1271_error("couldn't register platform device"); goto err_hw; } - dev_set_drvdata(&wl1271_device.dev, wl); + dev_set_drvdata(&wl->plat_dev->dev, wl); /* Create sysfs file to control bt coex state */ - ret = device_create_file(&wl1271_device.dev, &dev_attr_bt_coex_state); + ret = device_create_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state); if (ret < 0) { wl1271_error("failed to create sysfs file bt_coex_state"); goto err_platform; @@ -2120,20 +2139,25 @@ struct ieee80211_hw *wl1271_alloc_hw(void) return hw; err_platform: - platform_device_unregister(&wl1271_device); + platform_device_unregister(wl->plat_dev); err_hw: - ieee80211_unregister_hw(wl->hw); + wl1271_debugfs_exit(wl); + kfree(plat_dev); + +err_plat_alloc: + ieee80211_free_hw(hw); + +err_hw_alloc: -err: return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(wl1271_alloc_hw); int wl1271_free_hw(struct wl1271 *wl) { - platform_device_unregister(&wl1271_device); - ieee80211_unregister_hw(wl->hw); + platform_device_unregister(wl->plat_dev); + kfree(wl->plat_dev); wl1271_debugfs_exit(wl); diff --git a/drivers/net/wireless/wl12xx/wl1271_sdio.c b/drivers/net/wireless/wl12xx/wl1271_sdio.c index 99b5b3f7a92c..3c03de74dbfc 100644 --- a/drivers/net/wireless/wl12xx/wl1271_sdio.c +++ b/drivers/net/wireless/wl12xx/wl1271_sdio.c @@ -239,7 +239,7 @@ static int __devinit wl1271_probe(struct sdio_func *func, out_free: - ieee80211_free_hw(hw); + wl1271_free_hw(wl); return ret; } @@ -250,6 +250,7 @@ static void __devexit wl1271_remove(struct sdio_func *func) free_irq(wl->irq, wl); + wl1271_unregister_hw(wl); wl1271_free_hw(wl); } diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.c b/drivers/net/wireless/wl12xx/wl1271_spi.c index 4c129c76c29d..f44b05a32b0d 100644 --- a/drivers/net/wireless/wl12xx/wl1271_spi.c +++ b/drivers/net/wireless/wl12xx/wl1271_spi.c @@ -422,7 +422,7 @@ static int __devinit wl1271_probe(struct spi_device *spi) free_irq(wl->irq, wl); out_free: - ieee80211_free_hw(hw); + wl1271_free_hw(wl); return ret; } @@ -433,6 +433,7 @@ static int __devexit wl1271_remove(struct spi_device *spi) free_irq(wl->irq, wl); + wl1271_unregister_hw(wl); wl1271_free_hw(wl); return 0; From f6e0bb56d43d1f3b2ad54d51b65c07ef3bdead16 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 18 Mar 2010 18:29:32 -0700 Subject: [PATCH 44/60] include/linux/wireless.h: Add IW_HANDLER macro to initialize array entry Copied the idea from orinoco Signed-off-by: Joe Perches Signed-off-by: John W. Linville --- include/linux/wireless.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/wireless.h b/include/linux/wireless.h index 5b4c6c772a9b..0955b67616d2 100644 --- a/include/linux/wireless.h +++ b/include/linux/wireless.h @@ -346,6 +346,8 @@ #define SIOCIWFIRST 0x8B00 #define SIOCIWLAST SIOCIWLASTPRIV /* 0x8BFF */ #define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST) +#define IW_HANDLER(id, func) \ + [IW_IOCTL_IDX(id)] = func /* Odd : get (world access), even : set (root access) */ #define IW_IS_SET(cmd) (!((cmd) & 0x1)) From 2ae2332ed11687325096e68e326ec57f0294cff9 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 18 Mar 2010 18:29:33 -0700 Subject: [PATCH 45/60] wireless.h: Use SIOCIWFIRST not SIOCSIWCOMMIT for range check These two #defines use the same value, but SIOCIWFIRST makes more sense in this use. Signed-off-by: Joe Perches Signed-off-by: John W. Linville --- include/linux/wireless.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/wireless.h b/include/linux/wireless.h index 0955b67616d2..e6827eedf18b 100644 --- a/include/linux/wireless.h +++ b/include/linux/wireless.h @@ -650,7 +650,7 @@ * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */ #define IW_EVENT_CAPA_BASE(cmd) ((cmd >= SIOCIWFIRSTPRIV) ? \ (cmd - SIOCIWFIRSTPRIV + 0x60) : \ - (cmd - SIOCSIWCOMMIT)) + (cmd - SIOCIWFIRST)) #define IW_EVENT_CAPA_INDEX(cmd) (IW_EVENT_CAPA_BASE(cmd) >> 5) #define IW_EVENT_CAPA_MASK(cmd) (1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F)) /* Event capability constants - event autogenerated by the kernel From 44608f801283f0f69d8a04d9976837748e410084 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 18 Mar 2010 18:29:35 -0700 Subject: [PATCH 46/60] net/wireless/wext_core.c: Use IW_IOCTL_IDX macro There's a wireless.h macro for this, might as well use it. Signed-off-by: Joe Perches Signed-off-by: John W. Linville --- net/wireless/wext-core.c | 112 +++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c index 5e1656bdf23b..dbde22b8f30f 100644 --- a/net/wireless/wext-core.c +++ b/net/wireless/wext-core.c @@ -28,226 +28,226 @@ typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *, * know about. */ static const struct iw_ioctl_description standard_ioctl[] = { - [SIOCSIWCOMMIT - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWCOMMIT)] = { .header_type = IW_HEADER_TYPE_NULL, }, - [SIOCGIWNAME - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWNAME)] = { .header_type = IW_HEADER_TYPE_CHAR, .flags = IW_DESCR_FLAG_DUMP, }, - [SIOCSIWNWID - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWNWID)] = { .header_type = IW_HEADER_TYPE_PARAM, .flags = IW_DESCR_FLAG_EVENT, }, - [SIOCGIWNWID - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWNWID)] = { .header_type = IW_HEADER_TYPE_PARAM, .flags = IW_DESCR_FLAG_DUMP, }, - [SIOCSIWFREQ - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWFREQ)] = { .header_type = IW_HEADER_TYPE_FREQ, .flags = IW_DESCR_FLAG_EVENT, }, - [SIOCGIWFREQ - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWFREQ)] = { .header_type = IW_HEADER_TYPE_FREQ, .flags = IW_DESCR_FLAG_DUMP, }, - [SIOCSIWMODE - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWMODE)] = { .header_type = IW_HEADER_TYPE_UINT, .flags = IW_DESCR_FLAG_EVENT, }, - [SIOCGIWMODE - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWMODE)] = { .header_type = IW_HEADER_TYPE_UINT, .flags = IW_DESCR_FLAG_DUMP, }, - [SIOCSIWSENS - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWSENS)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCGIWSENS - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWSENS)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCSIWRANGE - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWRANGE)] = { .header_type = IW_HEADER_TYPE_NULL, }, - [SIOCGIWRANGE - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWRANGE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = sizeof(struct iw_range), .flags = IW_DESCR_FLAG_DUMP, }, - [SIOCSIWPRIV - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWPRIV)] = { .header_type = IW_HEADER_TYPE_NULL, }, - [SIOCGIWPRIV - SIOCIWFIRST] = { /* (handled directly by us) */ + [IW_IOCTL_IDX(SIOCGIWPRIV)] = { /* (handled directly by us) */ .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct iw_priv_args), .max_tokens = 16, .flags = IW_DESCR_FLAG_NOMAX, }, - [SIOCSIWSTATS - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWSTATS)] = { .header_type = IW_HEADER_TYPE_NULL, }, - [SIOCGIWSTATS - SIOCIWFIRST] = { /* (handled directly by us) */ + [IW_IOCTL_IDX(SIOCGIWSTATS)] = { /* (handled directly by us) */ .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = sizeof(struct iw_statistics), .flags = IW_DESCR_FLAG_DUMP, }, - [SIOCSIWSPY - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWSPY)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct sockaddr), .max_tokens = IW_MAX_SPY, }, - [SIOCGIWSPY - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWSPY)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality), .max_tokens = IW_MAX_SPY, }, - [SIOCSIWTHRSPY - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWTHRSPY)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct iw_thrspy), .min_tokens = 1, .max_tokens = 1, }, - [SIOCGIWTHRSPY - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWTHRSPY)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct iw_thrspy), .min_tokens = 1, .max_tokens = 1, }, - [SIOCSIWAP - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWAP)] = { .header_type = IW_HEADER_TYPE_ADDR, }, - [SIOCGIWAP - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWAP)] = { .header_type = IW_HEADER_TYPE_ADDR, .flags = IW_DESCR_FLAG_DUMP, }, - [SIOCSIWMLME - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWMLME)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .min_tokens = sizeof(struct iw_mlme), .max_tokens = sizeof(struct iw_mlme), }, - [SIOCGIWAPLIST - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWAPLIST)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality), .max_tokens = IW_MAX_AP, .flags = IW_DESCR_FLAG_NOMAX, }, - [SIOCSIWSCAN - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWSCAN)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .min_tokens = 0, .max_tokens = sizeof(struct iw_scan_req), }, - [SIOCGIWSCAN - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWSCAN)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_SCAN_MAX_DATA, .flags = IW_DESCR_FLAG_NOMAX, }, - [SIOCSIWESSID - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWESSID)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ESSID_MAX_SIZE, .flags = IW_DESCR_FLAG_EVENT, }, - [SIOCGIWESSID - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWESSID)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ESSID_MAX_SIZE, .flags = IW_DESCR_FLAG_DUMP, }, - [SIOCSIWNICKN - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWNICKN)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ESSID_MAX_SIZE, }, - [SIOCGIWNICKN - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWNICKN)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ESSID_MAX_SIZE, }, - [SIOCSIWRATE - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWRATE)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCGIWRATE - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWRATE)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCSIWRTS - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWRTS)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCGIWRTS - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWRTS)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCSIWFRAG - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWFRAG)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCGIWFRAG - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWFRAG)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCSIWTXPOW - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWTXPOW)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCGIWTXPOW - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWTXPOW)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCSIWRETRY - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWRETRY)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCGIWRETRY - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWRETRY)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCSIWENCODE - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWENCODE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ENCODING_TOKEN_MAX, .flags = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT, }, - [SIOCGIWENCODE - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWENCODE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ENCODING_TOKEN_MAX, .flags = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT, }, - [SIOCSIWPOWER - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWPOWER)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCGIWPOWER - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWPOWER)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCSIWGENIE - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWGENIE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_GENERIC_IE_MAX, }, - [SIOCGIWGENIE - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWGENIE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_GENERIC_IE_MAX, }, - [SIOCSIWAUTH - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWAUTH)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCGIWAUTH - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWAUTH)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCSIWENCODEEXT - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWENCODEEXT)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .min_tokens = sizeof(struct iw_encode_ext), .max_tokens = sizeof(struct iw_encode_ext) + IW_ENCODING_TOKEN_MAX, }, - [SIOCGIWENCODEEXT - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWENCODEEXT)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .min_tokens = sizeof(struct iw_encode_ext), .max_tokens = sizeof(struct iw_encode_ext) + IW_ENCODING_TOKEN_MAX, }, - [SIOCSIWPMKSA - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWPMKSA)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .min_tokens = sizeof(struct iw_pmksa), @@ -449,7 +449,7 @@ void wireless_send_event(struct net_device * dev, /* Get the description of the Event */ if (cmd <= SIOCIWLAST) { - cmd_index = cmd - SIOCIWFIRST; + cmd_index = IW_IOCTL_IDX(cmd); if (cmd_index < standard_ioctl_num) descr = &(standard_ioctl[cmd_index]); } else { @@ -662,7 +662,7 @@ static iw_handler get_handler(struct net_device *dev, unsigned int cmd) return NULL; /* Try as a standard command */ - index = cmd - SIOCIWFIRST; + index = IW_IOCTL_IDX(cmd); if (index < handlers->num_standard) return handlers->standard[index]; @@ -954,9 +954,9 @@ static int ioctl_standard_call(struct net_device * dev, int ret = -EINVAL; /* Get the description of the IOCTL */ - if ((cmd - SIOCIWFIRST) >= standard_ioctl_num) + if (IW_IOCTL_IDX(cmd) >= standard_ioctl_num) return -EOPNOTSUPP; - descr = &(standard_ioctl[cmd - SIOCIWFIRST]); + descr = &(standard_ioctl[IW_IOCTL_IDX(cmd)]); /* Check if we have a pointer to user space data or not */ if (descr->header_type != IW_HEADER_TYPE_POINT) { @@ -1012,7 +1012,7 @@ static int compat_standard_call(struct net_device *dev, struct iw_point iwp; int err; - descr = standard_ioctl + (cmd - SIOCIWFIRST); + descr = standard_ioctl + IW_IOCTL_IDX(cmd); if (descr->header_type != IW_HEADER_TYPE_POINT) return ioctl_standard_call(dev, iwr, cmd, info, handler); From 76326f1d4c98fe01daf363e3d07f84bafed1feec Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 18 Mar 2010 18:29:36 -0700 Subject: [PATCH 47/60] net/wireless/wext-core.c: Use IW_EVENT_IDX macro There's a wireless.h macro for this, might as well use it. Signed-off-by: Joe Perches Signed-off-by: John W. Linville --- net/wireless/wext-core.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c index dbde22b8f30f..bfcbeee23f9c 100644 --- a/net/wireless/wext-core.c +++ b/net/wireless/wext-core.c @@ -261,44 +261,44 @@ static const unsigned standard_ioctl_num = ARRAY_SIZE(standard_ioctl); * we know about. */ static const struct iw_ioctl_description standard_event[] = { - [IWEVTXDROP - IWEVFIRST] = { + [IW_EVENT_IDX(IWEVTXDROP)] = { .header_type = IW_HEADER_TYPE_ADDR, }, - [IWEVQUAL - IWEVFIRST] = { + [IW_EVENT_IDX(IWEVQUAL)] = { .header_type = IW_HEADER_TYPE_QUAL, }, - [IWEVCUSTOM - IWEVFIRST] = { + [IW_EVENT_IDX(IWEVCUSTOM)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_CUSTOM_MAX, }, - [IWEVREGISTERED - IWEVFIRST] = { + [IW_EVENT_IDX(IWEVREGISTERED)] = { .header_type = IW_HEADER_TYPE_ADDR, }, - [IWEVEXPIRED - IWEVFIRST] = { + [IW_EVENT_IDX(IWEVEXPIRED)] = { .header_type = IW_HEADER_TYPE_ADDR, }, - [IWEVGENIE - IWEVFIRST] = { + [IW_EVENT_IDX(IWEVGENIE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_GENERIC_IE_MAX, }, - [IWEVMICHAELMICFAILURE - IWEVFIRST] = { + [IW_EVENT_IDX(IWEVMICHAELMICFAILURE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = sizeof(struct iw_michaelmicfailure), }, - [IWEVASSOCREQIE - IWEVFIRST] = { + [IW_EVENT_IDX(IWEVASSOCREQIE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_GENERIC_IE_MAX, }, - [IWEVASSOCRESPIE - IWEVFIRST] = { + [IW_EVENT_IDX(IWEVASSOCRESPIE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_GENERIC_IE_MAX, }, - [IWEVPMKIDCAND - IWEVFIRST] = { + [IW_EVENT_IDX(IWEVPMKIDCAND)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = sizeof(struct iw_pmkid_cand), @@ -453,7 +453,7 @@ void wireless_send_event(struct net_device * dev, if (cmd_index < standard_ioctl_num) descr = &(standard_ioctl[cmd_index]); } else { - cmd_index = cmd - IWEVFIRST; + cmd_index = IW_EVENT_IDX(cmd); if (cmd_index < standard_event_num) descr = &(standard_event[cmd_index]); } From 270020e85ec88a832d663fff4489523169641c2b Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 18 Mar 2010 18:29:37 -0700 Subject: [PATCH 48/60] drivers/net/wireless: Use IW_HANDLER macro Convert direct uses of [ (foo - SIOCIWFIRST) , fn) Signed-off-by: Joe Perches Signed-off-by: John W. Linville --- drivers/net/wireless/ray_cs.c | 40 ++++++++++++------------ drivers/net/wireless/wl3501_cs.c | 52 ++++++++++++++++---------------- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c index 84c530aa52f9..a42ee01e7b14 100644 --- a/drivers/net/wireless/ray_cs.c +++ b/drivers/net/wireless/ray_cs.c @@ -1531,28 +1531,28 @@ static iw_stats *ray_get_wireless_stats(struct net_device *dev) */ static const iw_handler ray_handler[] = { - [SIOCSIWCOMMIT - SIOCIWFIRST] = (iw_handler) ray_commit, - [SIOCGIWNAME - SIOCIWFIRST] = (iw_handler) ray_get_name, - [SIOCSIWFREQ - SIOCIWFIRST] = (iw_handler) ray_set_freq, - [SIOCGIWFREQ - SIOCIWFIRST] = (iw_handler) ray_get_freq, - [SIOCSIWMODE - SIOCIWFIRST] = (iw_handler) ray_set_mode, - [SIOCGIWMODE - SIOCIWFIRST] = (iw_handler) ray_get_mode, - [SIOCGIWRANGE - SIOCIWFIRST] = (iw_handler) ray_get_range, + IW_HANDLER(SIOCSIWCOMMIT, (iw_handler)ray_commit), + IW_HANDLER(SIOCGIWNAME, (iw_handler)ray_get_name), + IW_HANDLER(SIOCSIWFREQ, (iw_handler)ray_set_freq), + IW_HANDLER(SIOCGIWFREQ, (iw_handler)ray_get_freq), + IW_HANDLER(SIOCSIWMODE, (iw_handler)ray_set_mode), + IW_HANDLER(SIOCGIWMODE, (iw_handler)ray_get_mode), + IW_HANDLER(SIOCGIWRANGE, (iw_handler)ray_get_range), #ifdef WIRELESS_SPY - [SIOCSIWSPY - SIOCIWFIRST] = (iw_handler) iw_handler_set_spy, - [SIOCGIWSPY - SIOCIWFIRST] = (iw_handler) iw_handler_get_spy, - [SIOCSIWTHRSPY - SIOCIWFIRST] = (iw_handler) iw_handler_set_thrspy, - [SIOCGIWTHRSPY - SIOCIWFIRST] = (iw_handler) iw_handler_get_thrspy, + IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), + IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), + IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), + IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), #endif /* WIRELESS_SPY */ - [SIOCGIWAP - SIOCIWFIRST] = (iw_handler) ray_get_wap, - [SIOCSIWESSID - SIOCIWFIRST] = (iw_handler) ray_set_essid, - [SIOCGIWESSID - SIOCIWFIRST] = (iw_handler) ray_get_essid, - [SIOCSIWRATE - SIOCIWFIRST] = (iw_handler) ray_set_rate, - [SIOCGIWRATE - SIOCIWFIRST] = (iw_handler) ray_get_rate, - [SIOCSIWRTS - SIOCIWFIRST] = (iw_handler) ray_set_rts, - [SIOCGIWRTS - SIOCIWFIRST] = (iw_handler) ray_get_rts, - [SIOCSIWFRAG - SIOCIWFIRST] = (iw_handler) ray_set_frag, - [SIOCGIWFRAG - SIOCIWFIRST] = (iw_handler) ray_get_frag, + IW_HANDLER(SIOCGIWAP, (iw_handler)ray_get_wap), + IW_HANDLER(SIOCSIWESSID, (iw_handler)ray_set_essid), + IW_HANDLER(SIOCGIWESSID, (iw_handler)ray_get_essid), + IW_HANDLER(SIOCSIWRATE, (iw_handler)ray_set_rate), + IW_HANDLER(SIOCGIWRATE, (iw_handler)ray_get_rate), + IW_HANDLER(SIOCSIWRTS, (iw_handler)ray_set_rts), + IW_HANDLER(SIOCGIWRTS, (iw_handler)ray_get_rts), + IW_HANDLER(SIOCSIWFRAG, (iw_handler)ray_set_frag), + IW_HANDLER(SIOCGIWFRAG, (iw_handler)ray_get_frag), }; #define SIOCSIPFRAMING SIOCIWFIRSTPRIV /* Set framing mode */ diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c index 7b9621de239f..65dd502eab0d 100644 --- a/drivers/net/wireless/wl3501_cs.c +++ b/drivers/net/wireless/wl3501_cs.c @@ -1834,32 +1834,32 @@ out: } static const iw_handler wl3501_handler[] = { - [SIOCGIWNAME - SIOCIWFIRST] = wl3501_get_name, - [SIOCSIWFREQ - SIOCIWFIRST] = wl3501_set_freq, - [SIOCGIWFREQ - SIOCIWFIRST] = wl3501_get_freq, - [SIOCSIWMODE - SIOCIWFIRST] = wl3501_set_mode, - [SIOCGIWMODE - SIOCIWFIRST] = wl3501_get_mode, - [SIOCGIWSENS - SIOCIWFIRST] = wl3501_get_sens, - [SIOCGIWRANGE - SIOCIWFIRST] = wl3501_get_range, - [SIOCSIWSPY - SIOCIWFIRST] = iw_handler_set_spy, - [SIOCGIWSPY - SIOCIWFIRST] = iw_handler_get_spy, - [SIOCSIWTHRSPY - SIOCIWFIRST] = iw_handler_set_thrspy, - [SIOCGIWTHRSPY - SIOCIWFIRST] = iw_handler_get_thrspy, - [SIOCSIWAP - SIOCIWFIRST] = wl3501_set_wap, - [SIOCGIWAP - SIOCIWFIRST] = wl3501_get_wap, - [SIOCSIWSCAN - SIOCIWFIRST] = wl3501_set_scan, - [SIOCGIWSCAN - SIOCIWFIRST] = wl3501_get_scan, - [SIOCSIWESSID - SIOCIWFIRST] = wl3501_set_essid, - [SIOCGIWESSID - SIOCIWFIRST] = wl3501_get_essid, - [SIOCSIWNICKN - SIOCIWFIRST] = wl3501_set_nick, - [SIOCGIWNICKN - SIOCIWFIRST] = wl3501_get_nick, - [SIOCGIWRATE - SIOCIWFIRST] = wl3501_get_rate, - [SIOCGIWRTS - SIOCIWFIRST] = wl3501_get_rts_threshold, - [SIOCGIWFRAG - SIOCIWFIRST] = wl3501_get_frag_threshold, - [SIOCGIWTXPOW - SIOCIWFIRST] = wl3501_get_txpow, - [SIOCGIWRETRY - SIOCIWFIRST] = wl3501_get_retry, - [SIOCGIWENCODE - SIOCIWFIRST] = wl3501_get_encode, - [SIOCGIWPOWER - SIOCIWFIRST] = wl3501_get_power, + IW_HANDLER(SIOCGIWNAME, wl3501_get_name), + IW_HANDLER(SIOCSIWFREQ, wl3501_set_freq), + IW_HANDLER(SIOCGIWFREQ, wl3501_get_freq), + IW_HANDLER(SIOCSIWMODE, wl3501_set_mode), + IW_HANDLER(SIOCGIWMODE, wl3501_get_mode), + IW_HANDLER(SIOCGIWSENS, wl3501_get_sens), + IW_HANDLER(SIOCGIWRANGE, wl3501_get_range), + IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), + IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), + IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), + IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), + IW_HANDLER(SIOCSIWAP, wl3501_set_wap), + IW_HANDLER(SIOCGIWAP, wl3501_get_wap), + IW_HANDLER(SIOCSIWSCAN, wl3501_set_scan), + IW_HANDLER(SIOCGIWSCAN, wl3501_get_scan), + IW_HANDLER(SIOCSIWESSID, wl3501_set_essid), + IW_HANDLER(SIOCGIWESSID, wl3501_get_essid), + IW_HANDLER(SIOCSIWNICKN, wl3501_set_nick), + IW_HANDLER(SIOCGIWNICKN, wl3501_get_nick), + IW_HANDLER(SIOCGIWRATE, wl3501_get_rate), + IW_HANDLER(SIOCGIWRTS, wl3501_get_rts_threshold), + IW_HANDLER(SIOCGIWFRAG, wl3501_get_frag_threshold), + IW_HANDLER(SIOCGIWTXPOW, wl3501_get_txpow), + IW_HANDLER(SIOCGIWRETRY, wl3501_get_retry), + IW_HANDLER(SIOCGIWENCODE, wl3501_get_encode), + IW_HANDLER(SIOCGIWPOWER, wl3501_get_power), }; static const struct iw_handler_def wl3501_handler_def = { From 56b632e8cc7a13cece861d890deb2843116f9372 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 18 Mar 2010 18:29:38 -0700 Subject: [PATCH 49/60] drivers/net: Remove local #define IW_IOCTL, use IW_HANDLER Use #define IW_HANDLER from wireless.h instead Signed-off-by: Joe Perches Signed-off-by: John W. Linville --- drivers/net/ps3_gelic_wireless.c | 35 ++++++----- drivers/net/wireless/ipw2x00/ipw2200.c | 83 +++++++++++++------------- 2 files changed, 58 insertions(+), 60 deletions(-) diff --git a/drivers/net/ps3_gelic_wireless.c b/drivers/net/ps3_gelic_wireless.c index 2663b2fdc0bb..f5fc0f78fc21 100644 --- a/drivers/net/ps3_gelic_wireless.c +++ b/drivers/net/ps3_gelic_wireless.c @@ -2279,26 +2279,25 @@ void gelic_wl_interrupt(struct net_device *netdev, u64 status) /* * driver helpers */ -#define IW_IOCTL(n) [(n) - SIOCSIWCOMMIT] static const iw_handler gelic_wl_wext_handler[] = { - IW_IOCTL(SIOCGIWNAME) = gelic_wl_get_name, - IW_IOCTL(SIOCGIWRANGE) = gelic_wl_get_range, - IW_IOCTL(SIOCSIWSCAN) = gelic_wl_set_scan, - IW_IOCTL(SIOCGIWSCAN) = gelic_wl_get_scan, - IW_IOCTL(SIOCSIWAUTH) = gelic_wl_set_auth, - IW_IOCTL(SIOCGIWAUTH) = gelic_wl_get_auth, - IW_IOCTL(SIOCSIWESSID) = gelic_wl_set_essid, - IW_IOCTL(SIOCGIWESSID) = gelic_wl_get_essid, - IW_IOCTL(SIOCSIWENCODE) = gelic_wl_set_encode, - IW_IOCTL(SIOCGIWENCODE) = gelic_wl_get_encode, - IW_IOCTL(SIOCSIWAP) = gelic_wl_set_ap, - IW_IOCTL(SIOCGIWAP) = gelic_wl_get_ap, - IW_IOCTL(SIOCSIWENCODEEXT) = gelic_wl_set_encodeext, - IW_IOCTL(SIOCGIWENCODEEXT) = gelic_wl_get_encodeext, - IW_IOCTL(SIOCSIWMODE) = gelic_wl_set_mode, - IW_IOCTL(SIOCGIWMODE) = gelic_wl_get_mode, - IW_IOCTL(SIOCGIWNICKN) = gelic_wl_get_nick, + IW_HANDLER(SIOCGIWNAME, gelic_wl_get_name), + IW_HANDLER(SIOCGIWRANGE, gelic_wl_get_range), + IW_HANDLER(SIOCSIWSCAN, gelic_wl_set_scan), + IW_HANDLER(SIOCGIWSCAN, gelic_wl_get_scan), + IW_HANDLER(SIOCSIWAUTH, gelic_wl_set_auth), + IW_HANDLER(SIOCGIWAUTH, gelic_wl_get_auth), + IW_HANDLER(SIOCSIWESSID, gelic_wl_set_essid), + IW_HANDLER(SIOCGIWESSID, gelic_wl_get_essid), + IW_HANDLER(SIOCSIWENCODE, gelic_wl_set_encode), + IW_HANDLER(SIOCGIWENCODE, gelic_wl_get_encode), + IW_HANDLER(SIOCSIWAP, gelic_wl_set_ap), + IW_HANDLER(SIOCGIWAP, gelic_wl_get_ap), + IW_HANDLER(SIOCSIWENCODEEXT, gelic_wl_set_encodeext), + IW_HANDLER(SIOCGIWENCODEEXT, gelic_wl_get_encodeext), + IW_HANDLER(SIOCSIWMODE, gelic_wl_set_mode), + IW_HANDLER(SIOCGIWMODE, gelic_wl_get_mode), + IW_HANDLER(SIOCGIWNICKN, gelic_wl_get_nick), }; static const struct iw_handler_def gelic_wl_wext_handler_def = { diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index d6edc2440128..fcdbe8b00551 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -9980,49 +9980,48 @@ static int ipw_wx_sw_reset(struct net_device *dev, } /* Rebase the WE IOCTLs to zero for the handler array */ -#define IW_IOCTL(x) [(x)-SIOCSIWCOMMIT] static iw_handler ipw_wx_handlers[] = { - IW_IOCTL(SIOCGIWNAME) = (iw_handler) cfg80211_wext_giwname, - IW_IOCTL(SIOCSIWFREQ) = ipw_wx_set_freq, - IW_IOCTL(SIOCGIWFREQ) = ipw_wx_get_freq, - IW_IOCTL(SIOCSIWMODE) = ipw_wx_set_mode, - IW_IOCTL(SIOCGIWMODE) = ipw_wx_get_mode, - IW_IOCTL(SIOCSIWSENS) = ipw_wx_set_sens, - IW_IOCTL(SIOCGIWSENS) = ipw_wx_get_sens, - IW_IOCTL(SIOCGIWRANGE) = ipw_wx_get_range, - IW_IOCTL(SIOCSIWAP) = ipw_wx_set_wap, - IW_IOCTL(SIOCGIWAP) = ipw_wx_get_wap, - IW_IOCTL(SIOCSIWSCAN) = ipw_wx_set_scan, - IW_IOCTL(SIOCGIWSCAN) = ipw_wx_get_scan, - IW_IOCTL(SIOCSIWESSID) = ipw_wx_set_essid, - IW_IOCTL(SIOCGIWESSID) = ipw_wx_get_essid, - IW_IOCTL(SIOCSIWNICKN) = ipw_wx_set_nick, - IW_IOCTL(SIOCGIWNICKN) = ipw_wx_get_nick, - IW_IOCTL(SIOCSIWRATE) = ipw_wx_set_rate, - IW_IOCTL(SIOCGIWRATE) = ipw_wx_get_rate, - IW_IOCTL(SIOCSIWRTS) = ipw_wx_set_rts, - IW_IOCTL(SIOCGIWRTS) = ipw_wx_get_rts, - IW_IOCTL(SIOCSIWFRAG) = ipw_wx_set_frag, - IW_IOCTL(SIOCGIWFRAG) = ipw_wx_get_frag, - IW_IOCTL(SIOCSIWTXPOW) = ipw_wx_set_txpow, - IW_IOCTL(SIOCGIWTXPOW) = ipw_wx_get_txpow, - IW_IOCTL(SIOCSIWRETRY) = ipw_wx_set_retry, - IW_IOCTL(SIOCGIWRETRY) = ipw_wx_get_retry, - IW_IOCTL(SIOCSIWENCODE) = ipw_wx_set_encode, - IW_IOCTL(SIOCGIWENCODE) = ipw_wx_get_encode, - IW_IOCTL(SIOCSIWPOWER) = ipw_wx_set_power, - IW_IOCTL(SIOCGIWPOWER) = ipw_wx_get_power, - IW_IOCTL(SIOCSIWSPY) = iw_handler_set_spy, - IW_IOCTL(SIOCGIWSPY) = iw_handler_get_spy, - IW_IOCTL(SIOCSIWTHRSPY) = iw_handler_set_thrspy, - IW_IOCTL(SIOCGIWTHRSPY) = iw_handler_get_thrspy, - IW_IOCTL(SIOCSIWGENIE) = ipw_wx_set_genie, - IW_IOCTL(SIOCGIWGENIE) = ipw_wx_get_genie, - IW_IOCTL(SIOCSIWMLME) = ipw_wx_set_mlme, - IW_IOCTL(SIOCSIWAUTH) = ipw_wx_set_auth, - IW_IOCTL(SIOCGIWAUTH) = ipw_wx_get_auth, - IW_IOCTL(SIOCSIWENCODEEXT) = ipw_wx_set_encodeext, - IW_IOCTL(SIOCGIWENCODEEXT) = ipw_wx_get_encodeext, + IW_HANDLER(SIOCGIWNAME, (iw_handler)cfg80211_wext_giwname), + IW_HANDLER(SIOCSIWFREQ, ipw_wx_set_freq), + IW_HANDLER(SIOCGIWFREQ, ipw_wx_get_freq), + IW_HANDLER(SIOCSIWMODE, ipw_wx_set_mode), + IW_HANDLER(SIOCGIWMODE, ipw_wx_get_mode), + IW_HANDLER(SIOCSIWSENS, ipw_wx_set_sens), + IW_HANDLER(SIOCGIWSENS, ipw_wx_get_sens), + IW_HANDLER(SIOCGIWRANGE, ipw_wx_get_range), + IW_HANDLER(SIOCSIWAP, ipw_wx_set_wap), + IW_HANDLER(SIOCGIWAP, ipw_wx_get_wap), + IW_HANDLER(SIOCSIWSCAN, ipw_wx_set_scan), + IW_HANDLER(SIOCGIWSCAN, ipw_wx_get_scan), + IW_HANDLER(SIOCSIWESSID, ipw_wx_set_essid), + IW_HANDLER(SIOCGIWESSID, ipw_wx_get_essid), + IW_HANDLER(SIOCSIWNICKN, ipw_wx_set_nick), + IW_HANDLER(SIOCGIWNICKN, ipw_wx_get_nick), + IW_HANDLER(SIOCSIWRATE, ipw_wx_set_rate), + IW_HANDLER(SIOCGIWRATE, ipw_wx_get_rate), + IW_HANDLER(SIOCSIWRTS, ipw_wx_set_rts), + IW_HANDLER(SIOCGIWRTS, ipw_wx_get_rts), + IW_HANDLER(SIOCSIWFRAG, ipw_wx_set_frag), + IW_HANDLER(SIOCGIWFRAG, ipw_wx_get_frag), + IW_HANDLER(SIOCSIWTXPOW, ipw_wx_set_txpow), + IW_HANDLER(SIOCGIWTXPOW, ipw_wx_get_txpow), + IW_HANDLER(SIOCSIWRETRY, ipw_wx_set_retry), + IW_HANDLER(SIOCGIWRETRY, ipw_wx_get_retry), + IW_HANDLER(SIOCSIWENCODE, ipw_wx_set_encode), + IW_HANDLER(SIOCGIWENCODE, ipw_wx_get_encode), + IW_HANDLER(SIOCSIWPOWER, ipw_wx_set_power), + IW_HANDLER(SIOCGIWPOWER, ipw_wx_get_power), + IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), + IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), + IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), + IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), + IW_HANDLER(SIOCSIWGENIE, ipw_wx_set_genie), + IW_HANDLER(SIOCGIWGENIE, ipw_wx_get_genie), + IW_HANDLER(SIOCSIWMLME, ipw_wx_set_mlme), + IW_HANDLER(SIOCSIWAUTH, ipw_wx_set_auth), + IW_HANDLER(SIOCGIWAUTH, ipw_wx_get_auth), + IW_HANDLER(SIOCSIWENCODEEXT, ipw_wx_set_encodeext), + IW_HANDLER(SIOCGIWENCODEEXT, ipw_wx_get_encodeext), }; enum { From adc009e2e76b222006beb7f9df6d0aee051509d2 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 18 Mar 2010 18:29:39 -0700 Subject: [PATCH 50/60] orinoco/wext.c: Remove local #define STD_IW_HANDLER Use IW_HANDLER from wireless.h instead Signed-off-by: Joe Perches Signed-off-by: John W. Linville --- drivers/net/wireless/orinoco/wext.c | 94 ++++++++++++++--------------- 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/drivers/net/wireless/orinoco/wext.c b/drivers/net/wireless/orinoco/wext.c index 31ca241f7753..29f9bc03190a 100644 --- a/drivers/net/wireless/orinoco/wext.c +++ b/drivers/net/wireless/orinoco/wext.c @@ -1505,46 +1505,44 @@ static const struct iw_priv_args orinoco_privtab[] = { * Structures to export the Wireless Handlers */ -#define STD_IW_HANDLER(id, func) \ - [IW_IOCTL_IDX(id)] = (iw_handler) func static const iw_handler orinoco_handler[] = { - STD_IW_HANDLER(SIOCSIWCOMMIT, orinoco_ioctl_commit), - STD_IW_HANDLER(SIOCGIWNAME, cfg80211_wext_giwname), - STD_IW_HANDLER(SIOCSIWFREQ, orinoco_ioctl_setfreq), - STD_IW_HANDLER(SIOCGIWFREQ, orinoco_ioctl_getfreq), - STD_IW_HANDLER(SIOCSIWMODE, cfg80211_wext_siwmode), - STD_IW_HANDLER(SIOCGIWMODE, cfg80211_wext_giwmode), - STD_IW_HANDLER(SIOCSIWSENS, orinoco_ioctl_setsens), - STD_IW_HANDLER(SIOCGIWSENS, orinoco_ioctl_getsens), - STD_IW_HANDLER(SIOCGIWRANGE, cfg80211_wext_giwrange), - STD_IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), - STD_IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), - STD_IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), - STD_IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), - STD_IW_HANDLER(SIOCSIWAP, orinoco_ioctl_setwap), - STD_IW_HANDLER(SIOCGIWAP, orinoco_ioctl_getwap), - STD_IW_HANDLER(SIOCSIWSCAN, cfg80211_wext_siwscan), - STD_IW_HANDLER(SIOCGIWSCAN, cfg80211_wext_giwscan), - STD_IW_HANDLER(SIOCSIWESSID, orinoco_ioctl_setessid), - STD_IW_HANDLER(SIOCGIWESSID, orinoco_ioctl_getessid), - STD_IW_HANDLER(SIOCSIWRATE, orinoco_ioctl_setrate), - STD_IW_HANDLER(SIOCGIWRATE, orinoco_ioctl_getrate), - STD_IW_HANDLER(SIOCSIWRTS, orinoco_ioctl_setrts), - STD_IW_HANDLER(SIOCGIWRTS, orinoco_ioctl_getrts), - STD_IW_HANDLER(SIOCSIWFRAG, orinoco_ioctl_setfrag), - STD_IW_HANDLER(SIOCGIWFRAG, orinoco_ioctl_getfrag), - STD_IW_HANDLER(SIOCGIWRETRY, orinoco_ioctl_getretry), - STD_IW_HANDLER(SIOCSIWENCODE, orinoco_ioctl_setiwencode), - STD_IW_HANDLER(SIOCGIWENCODE, orinoco_ioctl_getiwencode), - STD_IW_HANDLER(SIOCSIWPOWER, orinoco_ioctl_setpower), - STD_IW_HANDLER(SIOCGIWPOWER, orinoco_ioctl_getpower), - STD_IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie), - STD_IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie), - STD_IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme), - STD_IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth), - STD_IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth), - STD_IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext), - STD_IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_encodeext), + IW_HANDLER(SIOCSIWCOMMIT, (iw_handler)orinoco_ioctl_commit), + IW_HANDLER(SIOCGIWNAME, (iw_handler)cfg80211_wext_giwname), + IW_HANDLER(SIOCSIWFREQ, (iw_handler)orinoco_ioctl_setfreq), + IW_HANDLER(SIOCGIWFREQ, (iw_handler)orinoco_ioctl_getfreq), + IW_HANDLER(SIOCSIWMODE, (iw_handler)cfg80211_wext_siwmode), + IW_HANDLER(SIOCGIWMODE, (iw_handler)cfg80211_wext_giwmode), + IW_HANDLER(SIOCSIWSENS, (iw_handler)orinoco_ioctl_setsens), + IW_HANDLER(SIOCGIWSENS, (iw_handler)orinoco_ioctl_getsens), + IW_HANDLER(SIOCGIWRANGE, (iw_handler)cfg80211_wext_giwrange), + IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), + IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), + IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), + IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), + IW_HANDLER(SIOCSIWAP, (iw_handler)orinoco_ioctl_setwap), + IW_HANDLER(SIOCGIWAP, (iw_handler)orinoco_ioctl_getwap), + IW_HANDLER(SIOCSIWSCAN, (iw_handler)cfg80211_wext_siwscan), + IW_HANDLER(SIOCGIWSCAN, (iw_handler)cfg80211_wext_giwscan), + IW_HANDLER(SIOCSIWESSID, (iw_handler)orinoco_ioctl_setessid), + IW_HANDLER(SIOCGIWESSID, (iw_handler)orinoco_ioctl_getessid), + IW_HANDLER(SIOCSIWRATE, (iw_handler)orinoco_ioctl_setrate), + IW_HANDLER(SIOCGIWRATE, (iw_handler)orinoco_ioctl_getrate), + IW_HANDLER(SIOCSIWRTS, (iw_handler)orinoco_ioctl_setrts), + IW_HANDLER(SIOCGIWRTS, (iw_handler)orinoco_ioctl_getrts), + IW_HANDLER(SIOCSIWFRAG, (iw_handler)orinoco_ioctl_setfrag), + IW_HANDLER(SIOCGIWFRAG, (iw_handler)orinoco_ioctl_getfrag), + IW_HANDLER(SIOCGIWRETRY, (iw_handler)orinoco_ioctl_getretry), + IW_HANDLER(SIOCSIWENCODE, (iw_handler)orinoco_ioctl_setiwencode), + IW_HANDLER(SIOCGIWENCODE, (iw_handler)orinoco_ioctl_getiwencode), + IW_HANDLER(SIOCSIWPOWER, (iw_handler)orinoco_ioctl_setpower), + IW_HANDLER(SIOCGIWPOWER, (iw_handler)orinoco_ioctl_getpower), + IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie), + IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie), + IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme), + IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth), + IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth), + IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext), + IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_encodeext), }; @@ -1552,15 +1550,15 @@ static const iw_handler orinoco_handler[] = { Added typecasting since we no longer use iwreq_data -- Moustafa */ static const iw_handler orinoco_private_handler[] = { - [0] = (iw_handler) orinoco_ioctl_reset, - [1] = (iw_handler) orinoco_ioctl_reset, - [2] = (iw_handler) orinoco_ioctl_setport3, - [3] = (iw_handler) orinoco_ioctl_getport3, - [4] = (iw_handler) orinoco_ioctl_setpreamble, - [5] = (iw_handler) orinoco_ioctl_getpreamble, - [6] = (iw_handler) orinoco_ioctl_setibssport, - [7] = (iw_handler) orinoco_ioctl_getibssport, - [9] = (iw_handler) orinoco_ioctl_getrid, + [0] = (iw_handler)orinoco_ioctl_reset, + [1] = (iw_handler)orinoco_ioctl_reset, + [2] = (iw_handler)orinoco_ioctl_setport3, + [3] = (iw_handler)orinoco_ioctl_getport3, + [4] = (iw_handler)orinoco_ioctl_setpreamble, + [5] = (iw_handler)orinoco_ioctl_getpreamble, + [6] = (iw_handler)orinoco_ioctl_setibssport, + [7] = (iw_handler)orinoco_ioctl_getibssport, + [9] = (iw_handler)orinoco_ioctl_getrid, }; const struct iw_handler_def orinoco_handler_def = { From 43ead78ac48b75aaf47de96fcf10cbf5962f32a6 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 18 Mar 2010 18:29:40 -0700 Subject: [PATCH 51/60] drivers/net/wireless/ray_cs.c: Use iw_handler function prototypes Change local functions that are cast to iw_handler to the more standard use with "union iwreq_data *wrqu" so the iw_handler array no longer needs the casts. Signed-off-by: Joe Perches Signed-off-by: John W. Linville --- drivers/net/wireless/ray_cs.c | 204 ++++++++++++++++------------------ 1 file changed, 93 insertions(+), 111 deletions(-) diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c index a42ee01e7b14..4f5bdb528ef7 100644 --- a/drivers/net/wireless/ray_cs.c +++ b/drivers/net/wireless/ray_cs.c @@ -1113,10 +1113,10 @@ static const struct ethtool_ops netdev_ethtool_ops = { /* * Wireless Handler : get protocol name */ -static int ray_get_name(struct net_device *dev, - struct iw_request_info *info, char *cwrq, char *extra) +static int ray_get_name(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { - strcpy(cwrq, "IEEE 802.11-FH"); + strcpy(wrqu->name, "IEEE 802.11-FH"); return 0; } @@ -1124,9 +1124,8 @@ static int ray_get_name(struct net_device *dev, /* * Wireless Handler : set frequency */ -static int ray_set_freq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *fwrq, char *extra) +static int ray_set_freq(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); int err = -EINPROGRESS; /* Call commit handler */ @@ -1136,10 +1135,10 @@ static int ray_set_freq(struct net_device *dev, return -EBUSY; /* Setting by channel number */ - if ((fwrq->m > USA_HOP_MOD) || (fwrq->e > 0)) + if ((wrqu->freq.m > USA_HOP_MOD) || (wrqu->freq.e > 0)) err = -EOPNOTSUPP; else - local->sparm.b5.a_hop_pattern = fwrq->m; + local->sparm.b5.a_hop_pattern = wrqu->freq.m; return err; } @@ -1148,14 +1147,13 @@ static int ray_set_freq(struct net_device *dev, /* * Wireless Handler : get frequency */ -static int ray_get_freq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *fwrq, char *extra) +static int ray_get_freq(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); - fwrq->m = local->sparm.b5.a_hop_pattern; - fwrq->e = 0; + wrqu->freq.m = local->sparm.b5.a_hop_pattern; + wrqu->freq.e = 0; return 0; } @@ -1163,9 +1161,8 @@ static int ray_get_freq(struct net_device *dev, /* * Wireless Handler : set ESSID */ -static int ray_set_essid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, char *extra) +static int ray_set_essid(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); @@ -1174,19 +1171,17 @@ static int ray_set_essid(struct net_device *dev, return -EBUSY; /* Check if we asked for `any' */ - if (dwrq->flags == 0) { + if (wrqu->essid.flags == 0) /* Corey : can you do that ? */ return -EOPNOTSUPP; - } else { - /* Check the size of the string */ - if (dwrq->length > IW_ESSID_MAX_SIZE) { - return -E2BIG; - } - /* Set the ESSID in the card */ - memset(local->sparm.b5.a_current_ess_id, 0, IW_ESSID_MAX_SIZE); - memcpy(local->sparm.b5.a_current_ess_id, extra, dwrq->length); - } + /* Check the size of the string */ + if (wrqu->essid.length > IW_ESSID_MAX_SIZE) + return -E2BIG; + + /* Set the ESSID in the card */ + memset(local->sparm.b5.a_current_ess_id, 0, IW_ESSID_MAX_SIZE); + memcpy(local->sparm.b5.a_current_ess_id, extra, wrqu->essid.length); return -EINPROGRESS; /* Call commit handler */ } @@ -1195,9 +1190,8 @@ static int ray_set_essid(struct net_device *dev, /* * Wireless Handler : get ESSID */ -static int ray_get_essid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, char *extra) +static int ray_get_essid(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); @@ -1205,8 +1199,8 @@ static int ray_get_essid(struct net_device *dev, memcpy(extra, local->sparm.b5.a_current_ess_id, IW_ESSID_MAX_SIZE); /* Push it out ! */ - dwrq->length = strlen(extra); - dwrq->flags = 1; /* active */ + wrqu->essid.length = strlen(extra); + wrqu->essid.flags = 1; /* active */ return 0; } @@ -1215,14 +1209,13 @@ static int ray_get_essid(struct net_device *dev, /* * Wireless Handler : get AP address */ -static int ray_get_wap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *awrq, char *extra) +static int ray_get_wap(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); - memcpy(awrq->sa_data, local->bss_id, ETH_ALEN); - awrq->sa_family = ARPHRD_ETHER; + memcpy(wrqu->ap_addr.sa_data, local->bss_id, ETH_ALEN); + wrqu->ap_addr.sa_family = ARPHRD_ETHER; return 0; } @@ -1231,9 +1224,8 @@ static int ray_get_wap(struct net_device *dev, /* * Wireless Handler : set Bit-Rate */ -static int ray_set_rate(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, char *extra) +static int ray_set_rate(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); @@ -1242,15 +1234,15 @@ static int ray_set_rate(struct net_device *dev, return -EBUSY; /* Check if rate is in range */ - if ((vwrq->value != 1000000) && (vwrq->value != 2000000)) + if ((wrqu->bitrate.value != 1000000) && (wrqu->bitrate.value != 2000000)) return -EINVAL; /* Hack for 1.5 Mb/s instead of 2 Mb/s */ if ((local->fw_ver == 0x55) && /* Please check */ - (vwrq->value == 2000000)) + (wrqu->bitrate.value == 2000000)) local->net_default_tx_rate = 3; else - local->net_default_tx_rate = vwrq->value / 500000; + local->net_default_tx_rate = wrqu->bitrate.value / 500000; return 0; } @@ -1259,17 +1251,16 @@ static int ray_set_rate(struct net_device *dev, /* * Wireless Handler : get Bit-Rate */ -static int ray_get_rate(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, char *extra) +static int ray_get_rate(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); if (local->net_default_tx_rate == 3) - vwrq->value = 2000000; /* Hum... */ + wrqu->bitrate.value = 2000000; /* Hum... */ else - vwrq->value = local->net_default_tx_rate * 500000; - vwrq->fixed = 0; /* We are in auto mode */ + wrqu->bitrate.value = local->net_default_tx_rate * 500000; + wrqu->bitrate.fixed = 0; /* We are in auto mode */ return 0; } @@ -1278,19 +1269,18 @@ static int ray_get_rate(struct net_device *dev, /* * Wireless Handler : set RTS threshold */ -static int ray_set_rts(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, char *extra) +static int ray_set_rts(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); - int rthr = vwrq->value; + int rthr = wrqu->rts.value; /* Reject if card is already initialised */ if (local->card_status != CARD_AWAITING_PARAM) return -EBUSY; /* if(wrq->u.rts.fixed == 0) we should complain */ - if (vwrq->disabled) + if (wrqu->rts.disabled) rthr = 32767; else { if ((rthr < 0) || (rthr > 2347)) /* What's the max packet size ??? */ @@ -1306,16 +1296,15 @@ static int ray_set_rts(struct net_device *dev, /* * Wireless Handler : get RTS threshold */ -static int ray_get_rts(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, char *extra) +static int ray_get_rts(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); - vwrq->value = (local->sparm.b5.a_rts_threshold[0] << 8) + wrqu->rts.value = (local->sparm.b5.a_rts_threshold[0] << 8) + local->sparm.b5.a_rts_threshold[1]; - vwrq->disabled = (vwrq->value == 32767); - vwrq->fixed = 1; + wrqu->rts.disabled = (wrqu->rts.value == 32767); + wrqu->rts.fixed = 1; return 0; } @@ -1324,19 +1313,18 @@ static int ray_get_rts(struct net_device *dev, /* * Wireless Handler : set Fragmentation threshold */ -static int ray_set_frag(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, char *extra) +static int ray_set_frag(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); - int fthr = vwrq->value; + int fthr = wrqu->frag.value; /* Reject if card is already initialised */ if (local->card_status != CARD_AWAITING_PARAM) return -EBUSY; /* if(wrq->u.frag.fixed == 0) should complain */ - if (vwrq->disabled) + if (wrqu->frag.disabled) fthr = 32767; else { if ((fthr < 256) || (fthr > 2347)) /* To check out ! */ @@ -1352,16 +1340,15 @@ static int ray_set_frag(struct net_device *dev, /* * Wireless Handler : get Fragmentation threshold */ -static int ray_get_frag(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, char *extra) +static int ray_get_frag(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); - vwrq->value = (local->sparm.b5.a_frag_threshold[0] << 8) + wrqu->frag.value = (local->sparm.b5.a_frag_threshold[0] << 8) + local->sparm.b5.a_frag_threshold[1]; - vwrq->disabled = (vwrq->value == 32767); - vwrq->fixed = 1; + wrqu->frag.disabled = (wrqu->frag.value == 32767); + wrqu->frag.fixed = 1; return 0; } @@ -1370,8 +1357,8 @@ static int ray_get_frag(struct net_device *dev, /* * Wireless Handler : set Mode of Operation */ -static int ray_set_mode(struct net_device *dev, - struct iw_request_info *info, __u32 *uwrq, char *extra) +static int ray_set_mode(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); int err = -EINPROGRESS; /* Call commit handler */ @@ -1381,7 +1368,7 @@ static int ray_set_mode(struct net_device *dev, if (local->card_status != CARD_AWAITING_PARAM) return -EBUSY; - switch (*uwrq) { + switch (wrqu->mode) { case IW_MODE_ADHOC: card_mode = 0; /* Fall through */ @@ -1399,15 +1386,15 @@ static int ray_set_mode(struct net_device *dev, /* * Wireless Handler : get Mode of Operation */ -static int ray_get_mode(struct net_device *dev, - struct iw_request_info *info, __u32 *uwrq, char *extra) +static int ray_get_mode(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); if (local->sparm.b5.a_network_type) - *uwrq = IW_MODE_INFRA; + wrqu->mode = IW_MODE_INFRA; else - *uwrq = IW_MODE_ADHOC; + wrqu->mode = IW_MODE_ADHOC; return 0; } @@ -1416,16 +1403,15 @@ static int ray_get_mode(struct net_device *dev, /* * Wireless Handler : get range info */ -static int ray_get_range(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, char *extra) +static int ray_get_range(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { struct iw_range *range = (struct iw_range *)extra; - memset((char *)range, 0, sizeof(struct iw_range)); + memset(range, 0, sizeof(struct iw_range)); /* Set the length (very important for backward compatibility) */ - dwrq->length = sizeof(struct iw_range); + wrqu->data.length = sizeof(struct iw_range); /* Set the Wireless Extension versions */ range->we_version_compiled = WIRELESS_EXT; @@ -1448,8 +1434,7 @@ static int ray_get_range(struct net_device *dev, /* * Wireless Private Handler : set framing mode */ -static int ray_set_framing(struct net_device *dev, - struct iw_request_info *info, +static int ray_set_framing(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { translate = *(extra); /* Set framing mode */ @@ -1461,8 +1446,7 @@ static int ray_set_framing(struct net_device *dev, /* * Wireless Private Handler : get framing mode */ -static int ray_get_framing(struct net_device *dev, - struct iw_request_info *info, +static int ray_get_framing(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { *(extra) = translate; @@ -1474,8 +1458,7 @@ static int ray_get_framing(struct net_device *dev, /* * Wireless Private Handler : get country */ -static int ray_get_country(struct net_device *dev, - struct iw_request_info *info, +static int ray_get_country(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { *(extra) = country; @@ -1487,10 +1470,9 @@ static int ray_get_country(struct net_device *dev, /* * Commit handler : called after a bunch of SET operations */ -static int ray_commit(struct net_device *dev, struct iw_request_info *info, /* NULL */ - void *zwrq, /* NULL */ - char *extra) -{ /* NULL */ +static int ray_commit(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ return 0; } @@ -1531,28 +1513,28 @@ static iw_stats *ray_get_wireless_stats(struct net_device *dev) */ static const iw_handler ray_handler[] = { - IW_HANDLER(SIOCSIWCOMMIT, (iw_handler)ray_commit), - IW_HANDLER(SIOCGIWNAME, (iw_handler)ray_get_name), - IW_HANDLER(SIOCSIWFREQ, (iw_handler)ray_set_freq), - IW_HANDLER(SIOCGIWFREQ, (iw_handler)ray_get_freq), - IW_HANDLER(SIOCSIWMODE, (iw_handler)ray_set_mode), - IW_HANDLER(SIOCGIWMODE, (iw_handler)ray_get_mode), - IW_HANDLER(SIOCGIWRANGE, (iw_handler)ray_get_range), + IW_HANDLER(SIOCSIWCOMMIT, ray_commit), + IW_HANDLER(SIOCGIWNAME, ray_get_name), + IW_HANDLER(SIOCSIWFREQ, ray_set_freq), + IW_HANDLER(SIOCGIWFREQ, ray_get_freq), + IW_HANDLER(SIOCSIWMODE, ray_set_mode), + IW_HANDLER(SIOCGIWMODE, ray_get_mode), + IW_HANDLER(SIOCGIWRANGE, ray_get_range), #ifdef WIRELESS_SPY IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), #endif /* WIRELESS_SPY */ - IW_HANDLER(SIOCGIWAP, (iw_handler)ray_get_wap), - IW_HANDLER(SIOCSIWESSID, (iw_handler)ray_set_essid), - IW_HANDLER(SIOCGIWESSID, (iw_handler)ray_get_essid), - IW_HANDLER(SIOCSIWRATE, (iw_handler)ray_set_rate), - IW_HANDLER(SIOCGIWRATE, (iw_handler)ray_get_rate), - IW_HANDLER(SIOCSIWRTS, (iw_handler)ray_set_rts), - IW_HANDLER(SIOCGIWRTS, (iw_handler)ray_get_rts), - IW_HANDLER(SIOCSIWFRAG, (iw_handler)ray_set_frag), - IW_HANDLER(SIOCGIWFRAG, (iw_handler)ray_get_frag), + IW_HANDLER(SIOCGIWAP, ray_get_wap), + IW_HANDLER(SIOCSIWESSID, ray_set_essid), + IW_HANDLER(SIOCGIWESSID, ray_get_essid), + IW_HANDLER(SIOCSIWRATE, ray_set_rate), + IW_HANDLER(SIOCGIWRATE, ray_get_rate), + IW_HANDLER(SIOCSIWRTS, ray_set_rts), + IW_HANDLER(SIOCGIWRTS, ray_get_rts), + IW_HANDLER(SIOCSIWFRAG, ray_set_frag), + IW_HANDLER(SIOCGIWFRAG, ray_get_frag), }; #define SIOCSIPFRAMING SIOCIWFIRSTPRIV /* Set framing mode */ @@ -1560,9 +1542,9 @@ static const iw_handler ray_handler[] = { #define SIOCGIPCOUNTRY SIOCIWFIRSTPRIV + 3 /* Get country code */ static const iw_handler ray_private_handler[] = { - [0] = (iw_handler) ray_set_framing, - [1] = (iw_handler) ray_get_framing, - [3] = (iw_handler) ray_get_country, + [0] = ray_set_framing, + [1] = ray_get_framing, + [3] = ray_get_country, }; static const struct iw_priv_args ray_private_args[] = { From 1e4dcd012423b6a28f968a55886d2b27896a1586 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Fri, 19 Mar 2010 07:14:53 +0200 Subject: [PATCH 52/60] mac80211: Add support for connection monitor in hardware This patch is based on a RFC patch by Kalle Valo. The wl1271 has a feature which handles the connection monitor logic in hardware, basically sending periodically nullfunc frames and reporting to the host if AP is lost, after attempting to recover by sending probe-requests to the AP. Add support to mac80211 by adding a new flag IEEE80211_HW_CONNECTION_MONITOR which prevents conn_mon_timer from triggering during idle periods, and prevents sending probe-requests to the AP if beacon-loss is indicated by the hardware. Cc: Kalle Valo Signed-off-by: Juuso Oikarinen Reviewed-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 24 ++++++++++++-- net/mac80211/ieee80211_i.h | 4 +-- net/mac80211/iface.c | 2 +- net/mac80211/mlme.c | 64 +++++++++++++++++++++++++++++++++----- 4 files changed, 82 insertions(+), 12 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 936bc410d061..d14226f29ffc 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -954,6 +954,11 @@ enum ieee80211_tkip_key_type { * Hardware can provide ack status reports of Tx frames to * the stack. * + * @IEEE80211_HW_CONNECTION_MONITOR: + * The hardware performs its own connection monitoring, including + * periodic keep-alives to the AP and probing the AP on beacon loss. + * When this flag is set, signaling beacon-loss will cause an immediate + * change to disassociated state. */ enum ieee80211_hw_flags { IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, @@ -975,6 +980,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16, IEEE80211_HW_SUPPORTS_UAPSD = 1<<17, IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18, + IEEE80211_HW_CONNECTION_MONITOR = 1<<19, }; /** @@ -2364,12 +2370,26 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw, * * @vif: &struct ieee80211_vif pointer from the add_interface callback. * - * When beacon filtering is enabled with IEEE80211_HW_BEACON_FILTERING and - * IEEE80211_CONF_PS is set, the driver needs to inform whenever the + * When beacon filtering is enabled with %IEEE80211_HW_BEACON_FILTERING and + * %IEEE80211_CONF_PS is set, the driver needs to inform whenever the * hardware is not receiving beacons with this function. */ void ieee80211_beacon_loss(struct ieee80211_vif *vif); +/** + * ieee80211_connection_loss - inform hardware has lost connection to the AP + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * When beacon filtering is enabled with %IEEE80211_HW_BEACON_FILTERING, and + * %IEEE80211_CONF_PS and %IEEE80211_HW_CONNECTION_MONITOR are set, the driver + * needs to inform if the connection to the AP has been lost. + * + * This function will cause immediate change to disassociated state, + * without connection recovery attempts. + */ +void ieee80211_connection_loss(struct ieee80211_vif *vif); + /* Rate control API */ /** diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index b84126491ab1..ab369e2a5282 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -327,7 +327,7 @@ struct ieee80211_if_managed { struct work_struct work; struct work_struct monitor_work; struct work_struct chswitch_work; - struct work_struct beacon_loss_work; + struct work_struct beacon_connection_loss_work; unsigned long probe_timeout; int probe_send_count; @@ -1156,7 +1156,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, int powersave); void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, struct ieee80211_hdr *hdr); -void ieee80211_beacon_loss_work(struct work_struct *work); +void ieee80211_beacon_connection_loss_work(struct work_struct *work); void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, enum queue_stop_reason reason); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index d5571b9420cd..b4ec59a8dc03 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -486,7 +486,7 @@ static int ieee80211_stop(struct net_device *dev) cancel_work_sync(&sdata->u.mgd.work); cancel_work_sync(&sdata->u.mgd.chswitch_work); cancel_work_sync(&sdata->u.mgd.monitor_work); - cancel_work_sync(&sdata->u.mgd.beacon_loss_work); + cancel_work_sync(&sdata->u.mgd.beacon_connection_loss_work); /* * When we get here, the interface is marked down. diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 0ab284c32135..865ea1cfb7bb 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -851,6 +851,9 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, if (is_multicast_ether_addr(hdr->addr1)) return; + if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) + return; + mod_timer(&sdata->u.mgd.conn_mon_timer, round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME)); } @@ -928,23 +931,68 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, mutex_unlock(&ifmgd->mtx); } -void ieee80211_beacon_loss_work(struct work_struct *work) +static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_local *local = sdata->local; + u8 bssid[ETH_ALEN]; + + mutex_lock(&ifmgd->mtx); + if (!ifmgd->associated) { + mutex_unlock(&ifmgd->mtx); + return; + } + + memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN); + + printk(KERN_DEBUG "Connection to AP %pM lost.\n", bssid); + + ieee80211_set_disassoc(sdata); + ieee80211_recalc_idle(local); + mutex_unlock(&ifmgd->mtx); + /* + * must be outside lock due to cfg80211, + * but that's not a problem. + */ + ieee80211_send_deauth_disassoc(sdata, bssid, + IEEE80211_STYPE_DEAUTH, + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, + NULL); +} + +void ieee80211_beacon_connection_loss_work(struct work_struct *work) { struct ieee80211_sub_if_data *sdata = container_of(work, struct ieee80211_sub_if_data, - u.mgd.beacon_loss_work); + u.mgd.beacon_connection_loss_work); - ieee80211_mgd_probe_ap(sdata, true); + if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) + __ieee80211_connection_loss(sdata); + else + ieee80211_mgd_probe_ap(sdata, true); } void ieee80211_beacon_loss(struct ieee80211_vif *vif) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_hw *hw = &sdata->local->hw; - ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work); + WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR); + ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work); } EXPORT_SYMBOL(ieee80211_beacon_loss); +void ieee80211_connection_loss(struct ieee80211_vif *vif) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_hw *hw = &sdata->local->hw; + + WARN_ON(!(hw->flags & IEEE80211_HW_CONNECTION_MONITOR)); + ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work); +} +EXPORT_SYMBOL(ieee80211_connection_loss); + + static enum rx_mgmt_action __must_check ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len) @@ -1634,7 +1682,8 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data) if (local->quiescing) return; - ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work); + ieee80211_queue_work(&sdata->local->hw, + &sdata->u.mgd.beacon_connection_loss_work); } static void ieee80211_sta_conn_mon_timer(unsigned long data) @@ -1686,7 +1735,7 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata) */ cancel_work_sync(&ifmgd->work); - cancel_work_sync(&ifmgd->beacon_loss_work); + cancel_work_sync(&ifmgd->beacon_connection_loss_work); if (del_timer_sync(&ifmgd->timer)) set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running); @@ -1720,7 +1769,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) INIT_WORK(&ifmgd->work, ieee80211_sta_work); INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work); INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work); - INIT_WORK(&ifmgd->beacon_loss_work, ieee80211_beacon_loss_work); + INIT_WORK(&ifmgd->beacon_connection_loss_work, + ieee80211_beacon_connection_loss_work); setup_timer(&ifmgd->timer, ieee80211_sta_timer, (unsigned long) sdata); setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, From c5a079f46aabff026b2acaba0a2579a74c3c3731 Mon Sep 17 00:00:00 2001 From: Daniel Ngu Date: Tue, 23 Mar 2010 00:52:44 +1300 Subject: [PATCH 53/60] drivers/net/wireless/b43/main.c:4351: Fixed coding style WARNING: braces {} are not necessary for any arm of this statement Signed-off-by: Daniel Ngu Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 1521b1e78d21..14cf3bd7ea51 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4348,11 +4348,10 @@ static int b43_wireless_core_init(struct b43_wldev *dev) b43_set_phytxctl_defaults(dev); /* Minimum Contention Window */ - if (phy->type == B43_PHYTYPE_B) { + if (phy->type == B43_PHYTYPE_B) b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MINCONT, 0x1F); - } else { + else b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MINCONT, 0xF); - } /* Maximum Contention Window */ b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MAXCONT, 0x3FF); From e1572c5eeca8ef87a250322364584458b2dadb35 Mon Sep 17 00:00:00 2001 From: Sujith Date: Wed, 24 Mar 2010 13:42:13 +0530 Subject: [PATCH 54/60] ath9k_htc: Fix symbol collision with ath9k Signed-off-by: Sujith Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/htc.h | 22 +++++++++---------- drivers/net/wireless/ath/ath9k/htc_drv_init.c | 16 +++++++------- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 14 ++++++------ drivers/net/wireless/ath/ath9k/htc_drv_txrx.c | 4 ++-- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h index ab09fe3416c7..777064945fca 100644 --- a/drivers/net/wireless/ath/ath9k/htc.h +++ b/drivers/net/wireless/ath/ath9k/htc.h @@ -41,7 +41,7 @@ ((((u32)(_h)) << 22) | (((u32)(_l)) >> 10)) extern struct ieee80211_ops ath9k_htc_ops; -extern int modparam_nohwcrypt; +extern int htc_modparam_nohwcrypt; enum htc_phymode { HTC_MODE_AUTO = 0, @@ -408,8 +408,8 @@ void ath9k_tx_cleanup(struct ath9k_htc_priv *priv); bool ath9k_htc_txq_setup(struct ath9k_htc_priv *priv, enum ath9k_tx_queue_subtype qtype); int get_hw_qnum(u16 queue, int *hwq_map); -int ath_txq_update(struct ath9k_htc_priv *priv, int qnum, - struct ath9k_tx_queue_info *qinfo); +int ath_htc_txq_update(struct ath9k_htc_priv *priv, int qnum, + struct ath9k_tx_queue_info *qinfo); int ath9k_rx_init(struct ath9k_htc_priv *priv); void ath9k_rx_cleanup(struct ath9k_htc_priv *priv); @@ -427,15 +427,15 @@ void ath9k_htc_disconnect_device(struct htc_target *htc_handle, bool hotunplug); int ath9k_htc_resume(struct htc_target *htc_handle); #endif #ifdef CONFIG_ATH9K_HTC_DEBUGFS -int ath9k_debug_create_root(void); -void ath9k_debug_remove_root(void); -int ath9k_init_debug(struct ath_hw *ah); -void ath9k_exit_debug(struct ath_hw *ah); +int ath9k_htc_debug_create_root(void); +void ath9k_htc_debug_remove_root(void); +int ath9k_htc_init_debug(struct ath_hw *ah); +void ath9k_htc_exit_debug(struct ath_hw *ah); #else -static inline int ath9k_debug_create_root(void) { return 0; }; -static inline void ath9k_debug_remove_root(void) {}; -static inline int ath9k_init_debug(struct ath_hw *ah) { return 0; }; -static inline void ath9k_exit_debug(struct ath_hw *ah) {}; +static inline int ath9k_htc_debug_create_root(void) { return 0; }; +static inline void ath9k_htc_debug_remove_root(void) {}; +static inline int ath9k_htc_init_debug(struct ath_hw *ah) { return 0; }; +static inline void ath9k_htc_exit_debug(struct ath_hw *ah) {}; #endif /* CONFIG_ATH9K_HTC_DEBUGFS */ #endif /* HTC_H */ diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index a653dec140b9..10c87605d2c4 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -24,8 +24,8 @@ static unsigned int ath9k_debug = ATH_DBG_DEFAULT; module_param_named(debug, ath9k_debug, uint, 0); MODULE_PARM_DESC(debug, "Debugging mask"); -int modparam_nohwcrypt; -module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444); +int htc_modparam_nohwcrypt; +module_param_named(nohwcrypt, htc_modparam_nohwcrypt, int, 0444); MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption"); #define CHAN2G(_freq, _idx) { \ @@ -93,7 +93,7 @@ static int ath9k_htc_wait_for_target(struct ath9k_htc_priv *priv) static void ath9k_deinit_priv(struct ath9k_htc_priv *priv) { - ath9k_exit_debug(priv->ah); + ath9k_htc_exit_debug(priv->ah); ath9k_hw_deinit(priv->ah); tasklet_kill(&priv->wmi_tasklet); tasklet_kill(&priv->rx_tasklet); @@ -474,7 +474,7 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv, u16 devid) goto err_hw; } - ret = ath9k_init_debug(ah); + ret = ath9k_htc_init_debug(ah); if (ret) { ath_print(common, ATH_DBG_FATAL, "Unable to create debugfs files\n"); @@ -492,7 +492,7 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv, u16 devid) return 0; err_queues: - ath9k_exit_debug(ah); + ath9k_htc_exit_debug(ah); err_debug: ath9k_hw_deinit(ah); err_hw: @@ -678,7 +678,7 @@ static int __init ath9k_htc_init(void) { int error; - error = ath9k_debug_create_root(); + error = ath9k_htc_debug_create_root(); if (error < 0) { printk(KERN_ERR "ath9k_htc: Unable to create debugfs root: %d\n", @@ -698,7 +698,7 @@ static int __init ath9k_htc_init(void) return 0; err_usb: - ath9k_debug_remove_root(); + ath9k_htc_debug_remove_root(); err_dbg: return error; } @@ -707,7 +707,7 @@ module_init(ath9k_htc_init); static void __exit ath9k_htc_exit(void) { ath9k_hif_usb_exit(); - ath9k_debug_remove_root(); + ath9k_htc_debug_remove_root(); printk(KERN_INFO "ath9k_htc: Driver unloaded\n"); } module_exit(ath9k_htc_exit); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 3184a2ac7b88..20a2c1341e20 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -595,7 +595,7 @@ static const struct file_operations fops_recv = { .owner = THIS_MODULE }; -int ath9k_init_debug(struct ath_hw *ah) +int ath9k_htc_init_debug(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; @@ -630,11 +630,11 @@ int ath9k_init_debug(struct ath_hw *ah) return 0; err: - ath9k_exit_debug(ah); + ath9k_htc_exit_debug(ah); return -ENOMEM; } -void ath9k_exit_debug(struct ath_hw *ah) +void ath9k_htc_exit_debug(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; @@ -645,7 +645,7 @@ void ath9k_exit_debug(struct ath_hw *ah) debugfs_remove(priv->debug.debugfs_phy); } -int ath9k_debug_create_root(void) +int ath9k_htc_debug_create_root(void) { ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL); if (!ath9k_debugfs_root) @@ -654,7 +654,7 @@ int ath9k_debug_create_root(void) return 0; } -void ath9k_debug_remove_root(void) +void ath9k_htc_debug_remove_root(void) { debugfs_remove(ath9k_debugfs_root); ath9k_debugfs_root = NULL; @@ -1358,7 +1358,7 @@ static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue, queue, qnum, params->aifs, params->cw_min, params->cw_max, params->txop); - ret = ath_txq_update(priv, qnum, &qi); + ret = ath_htc_txq_update(priv, qnum, &qi); if (ret) ath_print(common, ATH_DBG_FATAL, "TXQ Update failed\n"); @@ -1377,7 +1377,7 @@ static int ath9k_htc_set_key(struct ieee80211_hw *hw, struct ath_common *common = ath9k_hw_common(priv->ah); int ret = 0; - if (modparam_nohwcrypt) + if (htc_modparam_nohwcrypt) return -ENOSPC; mutex_lock(&priv->mutex); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index dba22d3f87c3..ac66cf0b2d53 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -36,8 +36,8 @@ int get_hw_qnum(u16 queue, int *hwq_map) } } -int ath_txq_update(struct ath9k_htc_priv *priv, int qnum, - struct ath9k_tx_queue_info *qinfo) +int ath_htc_txq_update(struct ath9k_htc_priv *priv, int qnum, + struct ath9k_tx_queue_info *qinfo) { struct ath_hw *ah = priv->ah; int error = 0; From 921ca03c8f8e982f27fc406bc301caf2196b99f7 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Thu, 25 Feb 2010 17:16:36 -0800 Subject: [PATCH 55/60] libertas: add auto auth mode feature Auto auth mode is enabled by default. If user doesn't specify the auth mode, while association driver will first try with open mode and then with shared key mode. If user specifies an auth mode, auto auth is disabled and driver will not try association with another auth mode. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/assoc.c | 22 +++++++++++++++++++++- drivers/net/wireless/libertas/dev.h | 1 + drivers/net/wireless/libertas/main.c | 1 + drivers/net/wireless/libertas/wext.c | 4 ++++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/libertas/assoc.c b/drivers/net/wireless/libertas/assoc.c index f03d5e4e59c3..95d3d4c5e08b 100644 --- a/drivers/net/wireless/libertas/assoc.c +++ b/drivers/net/wireless/libertas/assoc.c @@ -31,6 +31,9 @@ u8 lbs_bg_rates[MAX_RATES] = 0x00, 0x00 }; +static int assoc_helper_wep_keys(struct lbs_private *priv, + struct assoc_request *assoc_req); + /** * @brief This function finds common rates between rates and card rates. * @@ -610,7 +613,7 @@ static int lbs_assoc_post(struct lbs_private *priv, if (status_code) { lbs_mac_event_disconnected(priv); - ret = -1; + ret = status_code; goto done; } @@ -813,7 +816,24 @@ static int lbs_try_associate(struct lbs_private *priv, goto out; ret = lbs_associate(priv, assoc_req, CMD_802_11_ASSOCIATE); + /* If the association fails with current auth mode, let's + * try by changing the auth mode + */ + if ((priv->authtype_auto) && + (ret == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) && + (assoc_req->secinfo.wep_enabled) && + (priv->connect_status != LBS_CONNECTED)) { + if (priv->secinfo.auth_mode == IW_AUTH_ALG_OPEN_SYSTEM) + priv->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY; + else + priv->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; + if (!assoc_helper_wep_keys(priv, assoc_req)) + ret = lbs_associate(priv, assoc_req, + CMD_802_11_ASSOCIATE); + } + if (ret) + ret = -1; out: lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); return ret; diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h index 6977ee820214..058d1720242e 100644 --- a/drivers/net/wireless/libertas/dev.h +++ b/drivers/net/wireless/libertas/dev.h @@ -133,6 +133,7 @@ struct lbs_private { u8 wpa_ie_len; u16 wep_tx_keyidx; struct enc_key wep_keys[4]; + u8 authtype_auto; /* Wake On LAN */ uint32_t wol_criteria; diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index 28a1c9d1627a..3c889f43d909 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c @@ -835,6 +835,7 @@ static int lbs_init_adapter(struct lbs_private *priv) priv->is_auto_deep_sleep_enabled = 0; priv->wakeup_dev_required = 0; init_waitqueue_head(&priv->ds_awake_q); + priv->authtype_auto = 1; mutex_init(&priv->lock); diff --git a/drivers/net/wireless/libertas/wext.c b/drivers/net/wireless/libertas/wext.c index 71f88a08e090..aad6263dee6d 100644 --- a/drivers/net/wireless/libertas/wext.c +++ b/drivers/net/wireless/libertas/wext.c @@ -1440,8 +1440,10 @@ static int lbs_set_encode(struct net_device *dev, set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags); if (dwrq->flags & IW_ENCODE_RESTRICTED) { + priv->authtype_auto = 0; assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY; } else if (dwrq->flags & IW_ENCODE_OPEN) { + priv->authtype_auto = 0; assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; } @@ -1620,8 +1622,10 @@ static int lbs_set_encodeext(struct net_device *dev, goto out; if (dwrq->flags & IW_ENCODE_RESTRICTED) { + priv->authtype_auto = 0; assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY; } else if (dwrq->flags & IW_ENCODE_OPEN) { + priv->authtype_auto = 0; assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; } From d6dc1a386358979e12366d1f35eeb68fc181e101 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Tue, 23 Mar 2010 09:02:33 +0200 Subject: [PATCH 56/60] cfg80211: Add connection quality monitoring support to nl80211 Add support for basic configuration of a connection quality monitoring to the nl80211 interface, and basic support for notifying about triggered monitoring events. Via this interface a user-space connection manager may configure and receive pre-warning events of deteriorating WLAN connection quality, and start preparing for roaming in advance, before the connection is already lost. An example usage of such a trigger is starting scanning for nearby AP's in an attempt to find one with better connection quality, and associate to it before the connection characteristics of the existing connection become too bad or the association is even lost, leading in a prolonged delay in connectivity. The interface currently supports only RSSI, but it could be later extended to include other parameters, such as signal-to-noise ratio, if need for that arises. Signed-off-by: Juuso Oikarinen Reviewed-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 46 ++++++++++++++ include/net/cfg80211.h | 19 ++++++ net/wireless/mlme.c | 13 ++++ net/wireless/nl80211.c | 131 ++++++++++++++++++++++++++++++++++++++++ net/wireless/nl80211.h | 6 ++ 5 files changed, 215 insertions(+) diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 28ba20fda3e2..89947597b9ce 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -323,6 +323,12 @@ * the TX command and %NL80211_ATTR_FRAME includes the contents of the * frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged * the frame. + * @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command + * is used to configure connection quality monitoring notification trigger + * levels. + * @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This + * command is used as an event to indicate the that a trigger level was + * reached. * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use @@ -419,6 +425,9 @@ enum nl80211_commands { NL80211_CMD_SET_POWER_SAVE, NL80211_CMD_GET_POWER_SAVE, + NL80211_CMD_SET_CQM, + NL80211_CMD_NOTIFY_CQM, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -691,6 +700,9 @@ enum nl80211_commands { * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was * acknowledged by the recipient. * + * @NL80211_ATTR_CQM: connection quality monitor configuration in a + * nested attribute with %NL80211_ATTR_CQM_* sub-attributes. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -842,6 +854,8 @@ enum nl80211_attrs { NL80211_ATTR_PS_STATE, + NL80211_ATTR_CQM, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -1583,4 +1597,36 @@ enum nl80211_ps_state { NL80211_PS_ENABLED, }; +/** + * enum nl80211_attr_cqm - connection quality monitor attributes + * @__NL80211_ATTR_CQM_INVALID: invalid + * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm (zero to disable) + * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm + * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event + * @__NL80211_ATTR_CQM_AFTER_LAST: internal + * @NL80211_ATTR_CQM_MAX: highest key attribute + */ +enum nl80211_attr_cqm { + __NL80211_ATTR_CQM_INVALID, + NL80211_ATTR_CQM_RSSI_THOLD, + NL80211_ATTR_CQM_RSSI_HYST, + NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, + + /* keep last */ + __NL80211_ATTR_CQM_AFTER_LAST, + NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1 +}; + +/** + * enum nl80211_cqm_rssi_threshold_event - RSSI threshold event + * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW - The RSSI level is lower than the + * configured threshold + * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH - The RSSI is higher than the + * configured threshold + */ +enum nl80211_cqm_rssi_threshold_event { + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, +}; + #endif /* __LINUX_NL80211_H */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 3d134a1fb96b..868cfd3b9724 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1007,6 +1007,7 @@ struct cfg80211_pmksa { * RSN IE. It allows for faster roaming between WPA2 BSSIDs. * @del_pmksa: Delete a cached PMKID. * @flush_pmksa: Flush all cached PMKIDs. + * @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold. * */ struct cfg80211_ops { @@ -1152,6 +1153,10 @@ struct cfg80211_ops { int (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev, bool enabled, int timeout); + + int (*set_cqm_rssi_config)(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_thold, u32 rssi_hyst); }; /* @@ -2337,4 +2342,18 @@ bool cfg80211_rx_action(struct net_device *dev, int freq, const u8 *buf, void cfg80211_action_tx_status(struct net_device *dev, u64 cookie, const u8 *buf, size_t len, bool ack, gfp_t gfp); + +/** + * cfg80211_cqm_rssi_notify - connection quality monitoring rssi event + * @dev: network device + * @rssi_event: the triggered RSSI event + * @gfp: context flags + * + * This function is called when a configured connection quality monitoring + * rssi threshold reached event occurs. + */ +void cfg80211_cqm_rssi_notify(struct net_device *dev, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp); + #endif /* __NET_CFG80211_H */ diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 62bc8855e123..0855f0d32349 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -894,3 +894,16 @@ void cfg80211_action_tx_status(struct net_device *dev, u64 cookie, nl80211_send_action_tx_status(rdev, dev, cookie, buf, len, ack, gfp); } EXPORT_SYMBOL(cfg80211_action_tx_status); + +void cfg80211_cqm_rssi_notify(struct net_device *dev, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + /* Indicate roaming trigger event to user space */ + nl80211_send_cqm_rssi_notify(rdev, dev, rssi_event, gfp); +} +EXPORT_SYMBOL(cfg80211_cqm_rssi_notify); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e447db04cf76..a7fc3d83f5f6 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -149,6 +149,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { .len = IEEE80211_MAX_DATA_LEN }, [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, }, [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM] = { .type = NLA_NESTED, }, }; /* policy for the attributes */ @@ -4778,6 +4779,84 @@ unlock_rtnl: return err; } +static struct nla_policy +nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = { + [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, +}; + +static int nl80211_set_cqm_rssi(struct genl_info *info, + s32 threshold, u32 hysteresis) +{ + struct cfg80211_registered_device *rdev; + struct wireless_dev *wdev; + struct net_device *dev; + int err; + + if (threshold > 0) + return -EINVAL; + + rtnl_lock(); + + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); + if (err) + goto unlock_rdev; + + wdev = dev->ieee80211_ptr; + + if (!rdev->ops->set_cqm_rssi_config) { + err = -EOPNOTSUPP; + goto unlock_rdev; + } + + if (wdev->iftype != NL80211_IFTYPE_STATION) { + err = -EOPNOTSUPP; + goto unlock_rdev; + } + + err = rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev, + threshold, hysteresis); + +unlock_rdev: + cfg80211_unlock_rdev(rdev); + dev_put(dev); + rtnl_unlock(); + + return err; +} + +static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) +{ + struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1]; + struct nlattr *cqm; + int err; + + cqm = info->attrs[NL80211_ATTR_CQM]; + if (!cqm) { + err = -EINVAL; + goto out; + } + + err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm, + nl80211_attr_cqm_policy); + if (err) + goto out; + + if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] && + attrs[NL80211_ATTR_CQM_RSSI_HYST]) { + s32 threshold; + u32 hysteresis; + threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]); + hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]); + err = nl80211_set_cqm_rssi(info, threshold, hysteresis); + } else + err = -EINVAL; + +out: + return err; +} + static struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_GET_WIPHY, @@ -5082,6 +5161,12 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, /* can be retrieved by unprivileged users */ }, + { + .cmd = NL80211_CMD_SET_CQM, + .doit = nl80211_set_cqm, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { @@ -5832,6 +5917,52 @@ void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev, nlmsg_free(msg); } +void +nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp) +{ + struct sk_buff *msg; + struct nlattr *pinfoattr; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + + pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM); + if (!pinfoattr) + goto nla_put_failure; + + NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, + rssi_event); + + nla_nest_end(msg, pinfoattr); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + static int nl80211_netlink_notify(struct notifier_block * nb, unsigned long state, void *_notify) diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 4ca511102c6c..2ad7fbc7d9f1 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -82,4 +82,10 @@ void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev, const u8 *buf, size_t len, bool ack, gfp_t gfp); +void +nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp); + #endif /* __NET_WIRELESS_NL80211_H */ From a97c13c34509be460dea23c86f31c02daa2428b5 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Tue, 23 Mar 2010 09:02:34 +0200 Subject: [PATCH 57/60] mac80211: Add support for connection quality monitoring Add support for the set_cqm_config op. This op function configures the requested connection quality monitor rssi threshold and rssi hysteresis values to the hardware if the hardware supports IEEE80211_HW_SUPPORTS_CQM. For unsupported hardware, currently -EOPNOTSUPP is returned, so the mac80211 is currently not doing connection quality monitoring on the host. This could be added later, if needed. Signed-off-by: Juuso Oikarinen Reviewed-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 30 ++++++++++++++++++++++++++++++ net/mac80211/cfg.c | 27 +++++++++++++++++++++++++++ net/mac80211/mlme.c | 15 +++++++++++++++ 3 files changed, 72 insertions(+) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index d14226f29ffc..1a8f50af49a0 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -144,6 +144,7 @@ struct ieee80211_low_level_stats { * new beacon (beaconing modes) * @BSS_CHANGED_BEACON_ENABLED: Beaconing should be * enabled/disabled (beaconing modes) + * @BSS_CHANGED_CQM: Connection quality monitor config changed */ enum ieee80211_bss_change { BSS_CHANGED_ASSOC = 1<<0, @@ -156,6 +157,7 @@ enum ieee80211_bss_change { BSS_CHANGED_BSSID = 1<<7, BSS_CHANGED_BEACON = 1<<8, BSS_CHANGED_BEACON_ENABLED = 1<<9, + BSS_CHANGED_CQM = 1<<10, }; /** @@ -185,6 +187,9 @@ enum ieee80211_bss_change { * @enable_beacon: whether beaconing should be enabled or not * @ht_operation_mode: HT operation mode (like in &struct ieee80211_ht_info). * This field is only valid when the channel type is one of the HT types. + * @cqm_rssi_thold: Connection quality monitor RSSI threshold, a zero value + * implies disabled + * @cqm_rssi_hyst: Connection quality monitor RSSI hysteresis */ struct ieee80211_bss_conf { const u8 *bssid; @@ -202,6 +207,8 @@ struct ieee80211_bss_conf { u64 timestamp; u32 basic_rates; u16 ht_operation_mode; + s32 cqm_rssi_thold; + u32 cqm_rssi_hyst; }; /** @@ -959,6 +966,12 @@ enum ieee80211_tkip_key_type { * periodic keep-alives to the AP and probing the AP on beacon loss. * When this flag is set, signaling beacon-loss will cause an immediate * change to disassociated state. + * + * @IEEE80211_HW_SUPPORTS_CQM_RSSI: + * Hardware can do connection quality monitoring - i.e. it can monitor + * connection quality related parameters, such as the RSSI level and + * provide notifications if configured trigger levels are reached. + * */ enum ieee80211_hw_flags { IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, @@ -981,6 +994,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_SUPPORTS_UAPSD = 1<<17, IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18, IEEE80211_HW_CONNECTION_MONITOR = 1<<19, + IEEE80211_HW_SUPPORTS_CQM_RSSI = 1<<20, }; /** @@ -2390,6 +2404,22 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif); */ void ieee80211_connection_loss(struct ieee80211_vif *vif); +/** + * ieee80211_cqm_rssi_notify - inform a configured connection quality monitoring + * rssi threshold triggered + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @rssi_event: the RSSI trigger event type + * @gfp: context flags + * + * When the %IEEE80211_HW_SUPPORTS_CQM_RSSI is set, and a connection quality + * monitoring is configured with an rssi threshold, the driver will inform + * whenever the rssi level reaches the threshold. + */ +void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp); + /* Rate control API */ /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index b7116ef84a3b..c8f520529eec 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1402,6 +1402,32 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, return 0; } +static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_thold, u32 rssi_hyst) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_vif *vif = &sdata->vif; + struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; + + if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)) + return -EOPNOTSUPP; + + if (rssi_thold == bss_conf->cqm_rssi_thold && + rssi_hyst == bss_conf->cqm_rssi_hyst) + return 0; + + bss_conf->cqm_rssi_thold = rssi_thold; + bss_conf->cqm_rssi_hyst = rssi_hyst; + + /* tell the driver upon association, unless already associated */ + if (sdata->u.mgd.associated) + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_CQM); + + return 0; +} + static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *dev, const u8 *addr, @@ -1506,4 +1532,5 @@ struct cfg80211_ops mac80211_config_ops = { .remain_on_channel = ieee80211_remain_on_channel, .cancel_remain_on_channel = ieee80211_cancel_remain_on_channel, .action = ieee80211_action, + .set_cqm_rssi_config = ieee80211_set_cqm_rssi_config, }; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 865ea1cfb7bb..65eafda5738a 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -750,6 +750,11 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, /* And the BSSID changed - we're associated now */ bss_info_changed |= BSS_CHANGED_BSSID; + /* Tell the driver to monitor connection quality (if supported) */ + if ((local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI) && + sdata->vif.bss_conf.cqm_rssi_thold) + bss_info_changed |= BSS_CHANGED_CQM; + ieee80211_bss_info_change_notify(sdata, bss_info_changed); mutex_lock(&local->iflist_mtx); @@ -2182,3 +2187,13 @@ int ieee80211_mgd_action(struct ieee80211_sub_if_data *sdata, *cookie = (unsigned long) skb; return 0; } + +void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + + cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp); +} +EXPORT_SYMBOL(ieee80211_cqm_rssi_notify); From 4ac5541bdaea4d8e93710c58775cfa48ac82e09b Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Wed, 24 Mar 2010 10:11:14 +0200 Subject: [PATCH 58/60] cfg80211: Improve connection quality maintenance docs in nl80211.h In nl80211.h, be a little more elaborate in the docs for the definitions NL80211_ATTR_CQM_RSSI_THOLD and NL80211_ATTR_CQM_RSSI_HYST. Reported-by: Luis R. Rodriguez Signed-off-by: Juuso Oikarinen Signed-off-by: John W. Linville --- include/linux/nl80211.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 89947597b9ce..daf6a3432b92 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1600,8 +1600,12 @@ enum nl80211_ps_state { /** * enum nl80211_attr_cqm - connection quality monitor attributes * @__NL80211_ATTR_CQM_INVALID: invalid - * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm (zero to disable) - * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm + * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies + * the threshold for the RSSI level at which an event will be sent. Zero + * to disable. + * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies + * the minimum amount the RSSI level must change after an event before a + * new event may be issued (to reduce effects of RSSI oscillation). * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event * @__NL80211_ATTR_CQM_AFTER_LAST: internal * @NL80211_ATTR_CQM_MAX: highest key attribute From 14b44974d5a3c1ca59f6809b7313d7229eb55fd8 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 24 Mar 2010 14:56:41 +0300 Subject: [PATCH 59/60] mac80211: remove unneed variable from ieee80211_tx_pending() We don't need "sdata" any more after: d84f323477260e773d5317ad7cbe50f76115cb47 mac80211: remove dev_hold/put calls Signed-off-by: Dan Carpenter Signed-off-by: John W. Linville --- net/mac80211/tx.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index cbe53ed4fb0b..08e1f17a4226 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2010,14 +2010,12 @@ void ieee80211_tx_pending(unsigned long data) while (!skb_queue_empty(&local->pending[i])) { struct sk_buff *skb = __skb_dequeue(&local->pending[i]); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_sub_if_data *sdata; if (WARN_ON(!info->control.vif)) { kfree_skb(skb); continue; } - sdata = vif_to_sdata(info->control.vif); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); From 76232ebf898c4d5e657f2b663fbf7108bca80ded Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Wed, 24 Mar 2010 17:21:08 -0400 Subject: [PATCH 60/60] iwlwifi: fix build error for CONFIG_IWLAGN=n drivers/net/wireless/iwlwifi/iwl-rx.c: In function 'iwl_good_ack_health': drivers/net/wireless/iwlwifi/iwl-rx.c:647: error: 'struct iwl_priv' has no member named '_agn' Reported-by: Randy Dunlap Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-rx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c index b6a64d83718a..2fa30dfb7c59 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-rx.c @@ -621,6 +621,7 @@ static void iwl_accumulative_statistics(struct iwl_priv *priv, #define BA_TIMEOUT_CNT (5) #define BA_TIMEOUT_MAX (16) +#if defined(CONFIG_IWLAGN) || defined(CONFIG_IWLAGN_MODULE) /** * iwl_good_ack_health - checks for ACK count ratios, BA timeout retries. * @@ -670,6 +671,7 @@ bool iwl_good_ack_health(struct iwl_priv *priv, return rc; } EXPORT_SYMBOL(iwl_good_ack_health); +#endif /** * iwl_good_plcp_health - checks for plcp error.