From fa56dddd6720c8d4b9fa4c942377d2a019cf3708 Mon Sep 17 00:00:00 2001 From: Alina Friedrichsen Date: Tue, 10 Mar 2009 00:49:46 +0100 Subject: mac80211: ieee80211_ibss_commit() cleanup Don't call ieee80211_sta_find_ibss() directly, like it's done in STA mode, so that the commit() call is more harmless respectively has less site-effects. Signed-off-by: Alina Friedrichsen Signed-off-by: John W. Linville --- net/mac80211/ibss.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index f4becc12904..3201e1f9636 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -812,8 +812,9 @@ int ieee80211_ibss_commit(struct ieee80211_sub_if_data *sdata) ifibss->ibss_join_req = jiffies; ifibss->state = IEEE80211_IBSS_MLME_SEARCH; + set_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request); - return ieee80211_sta_find_ibss(sdata); + return 0; } int ieee80211_ibss_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t len) -- cgit v1.2.3 From 633e24ed95b6c87b42f201ecb65c12a75a5a7eef Mon Sep 17 00:00:00 2001 From: Reinette Chatre Date: Thu, 12 Mar 2009 09:20:40 -0700 Subject: cfg80211/nl80211: remove usage of CONFIG_NL80211 The scan capability added to cfg80211/nl80211 introduced a dependency on nl80211 by cfg80211. We can thus no longer have just cfg80211 without nl80211. Specifically, cfg80211_scan_done() calls nl80211_send_scan_aborted() or nl80211_send_scan_done(). Now we remove the option for user to select nl80211. It will always be compiled if user selects cfg80211. Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville --- net/wireless/Kconfig | 13 ------------- net/wireless/Makefile | 3 +-- net/wireless/nl80211.h | 26 -------------------------- 3 files changed, 1 insertion(+), 41 deletions(-) diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index 092ae6faccc..d1d18f34d27 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -10,19 +10,6 @@ config CFG80211_REG_DEBUG If unsure, say N. -config NL80211 - bool "nl80211 new netlink interface support" - depends on CFG80211 - default y - ---help--- - This option turns on the new netlink interface - (nl80211) support in cfg80211. - - If =n, drivers using mac80211 will be configured via - wireless extension support provided by that subsystem. - - If unsure, say Y. - config WIRELESS_OLD_REGULATORY bool "Old wireless static regulatory definitions" default y diff --git a/net/wireless/Makefile b/net/wireless/Makefile index dad43c24f69..c157b4d8014 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -5,8 +5,7 @@ obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o -cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o +cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o -cfg80211-$(CONFIG_NL80211) += nl80211.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index e65a3c38c52..5b5fe1339de 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -3,7 +3,6 @@ #include "core.h" -#ifdef CONFIG_NL80211 extern int nl80211_init(void); extern void nl80211_exit(void); extern void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev); @@ -12,30 +11,5 @@ extern void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, extern void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, struct net_device *netdev); extern void nl80211_send_reg_change_event(struct regulatory_request *request); -#else -static inline int nl80211_init(void) -{ - return 0; -} -static inline void nl80211_exit(void) -{ -} -static inline void nl80211_notify_dev_rename( - struct cfg80211_registered_device *rdev) -{ -} -static inline void -nl80211_send_scan_done(struct cfg80211_registered_device *rdev, - struct net_device *netdev) -{} -static inline void nl80211_send_scan_aborted( - struct cfg80211_registered_device *rdev, - struct net_device *netdev) -{} -static inline void -nl80211_send_reg_change_event(struct regulatory_request *request) -{ -} -#endif /* CONFIG_NL80211 */ #endif /* __NET_WIRELESS_NL80211_H */ -- cgit v1.2.3 From 14587ce2a8898de959f32dfd505b4871f09930d5 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Thu, 12 Mar 2009 15:32:54 +0530 Subject: ath9k: Set IEEE80211_TX_CTL_RATE_CTRL_PROBE in rate control for probe rate Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/rc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ath9k/rc.c b/drivers/net/wireless/ath9k/rc.c index 832735677a4..28d69da342c 100644 --- a/drivers/net/wireless/ath9k/rc.c +++ b/drivers/net/wireless/ath9k/rc.c @@ -864,6 +864,8 @@ static void ath_rc_ratefind(struct ath_softc *sc, rate_table, nrix, 1, 0); ath_rc_rate_set_series(rate_table, &rates[i++], txrc, try_per_rate, nrix, 0); + + tx_info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; } else { try_per_rate = (ATH_11N_TXMAXTRY/4); /* Set the choosen rate. No RTS for first series entry. */ -- cgit v1.2.3 From 176be728ee7d32cfd33702d82c0733e51f66ab5b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 12 Mar 2009 23:49:28 +0100 Subject: mac80211: remove ieee80211_num_regular_queues This inline is useless and actually makes the code _longer_ rather than shorter. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 5 ----- net/mac80211/mlme.c | 2 +- net/mac80211/tx.c | 7 +++---- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 12a52efcd0d..3bfc6c6c8c4 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1017,11 +1017,6 @@ static inline void SET_IEEE80211_PERM_ADDR(struct ieee80211_hw *hw, u8 *addr) memcpy(hw->wiphy->perm_addr, addr, ETH_ALEN); } -static inline int ieee80211_num_regular_queues(struct ieee80211_hw *hw) -{ - return hw->queues; -} - static inline struct ieee80211_rate * ieee80211_get_tx_rate(const struct ieee80211_hw *hw, const struct ieee80211_tx_info *c) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 841b8450b3d..aaf7793583a 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1834,7 +1834,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) ifmgd->flags |= IEEE80211_STA_CREATE_IBSS | IEEE80211_STA_AUTO_BSSID_SEL | IEEE80211_STA_AUTO_CHANNEL_SEL; - if (ieee80211_num_regular_queues(&sdata->local->hw) >= 4) + if (sdata->local->hw.queues >= 4) ifmgd->flags |= IEEE80211_STA_WMM_ENABLED; } diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 457238a2f3f..038460b0a48 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1666,8 +1666,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, } /* receiver and we are QoS enabled, use a QoS type frame */ - if (sta_flags & WLAN_STA_WME && - ieee80211_num_regular_queues(&local->hw) >= 4) { + if ((sta_flags & WLAN_STA_WME) && local->hw.queues >= 4) { fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA); hdrlen += 2; } @@ -1802,7 +1801,7 @@ void ieee80211_clear_tx_pending(struct ieee80211_local *local) int i, j; struct ieee80211_tx_stored_packet *store; - for (i = 0; i < ieee80211_num_regular_queues(&local->hw); i++) { + for (i = 0; i < local->hw.queues; i++) { if (!test_bit(i, local->queues_pending)) continue; store = &local->pending_packet[i]; @@ -1827,7 +1826,7 @@ void ieee80211_tx_pending(unsigned long data) int i, ret; netif_tx_lock_bh(dev); - for (i = 0; i < ieee80211_num_regular_queues(&local->hw); i++) { + for (i = 0; i < local->hw.queues; i++) { /* Check that this queue is ok */ if (__netif_subqueue_stopped(local->mdev, i) && !test_bit(i, local->queues_pending_run)) -- cgit v1.2.3 From 51b381479ff5bc9b8c49ce15fd8bc35c6b695ca4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 12 Mar 2009 11:16:48 +0100 Subject: mac80211: reduce max number of queues No hw/driver actually supports more than four queues right now, and we allocate a number of things per queue which means we waste a bit of memory. Reduce the maximum number to four to accurately reflect what we do (and need for QoS). Even if we had hardware supporting more queues we couldn't take advantage of that right now anyway. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 3bfc6c6c8c4..a38a82c785d 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -97,7 +97,7 @@ struct ieee80211_ht_bss_info { * for A-MPDU operation. */ enum ieee80211_max_queues { - IEEE80211_MAX_QUEUES = 16, + IEEE80211_MAX_QUEUES = 4, IEEE80211_MAX_AMPDU_QUEUES = 16, }; -- cgit v1.2.3 From 11432379fd2a3854a3408424d8dcd99afd811573 Mon Sep 17 00:00:00 2001 From: Helmut Schaa Date: Thu, 12 Mar 2009 14:04:34 +0100 Subject: mac80211: start pending scan after probe/auth/assoc timed out If a scan is queued in STA mode while the interface is in state direct probe, authenticate or associate the scan is delayed until the interface enters disabled or associated state. But in case of direct probe-, authentication- or association- timeout sta_work will not be scheduled anymore (without external trigger) and thus the pending scan is not executed and prevents a new scan from being triggered (-EBUSY). Fix this by queueing the sta work again after direct probe-, authentication- and association- timeout. Signed-off-by: Helmut Schaa Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index aaf7793583a..a55879663b3 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -682,6 +682,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_local *local = sdata->local; ifmgd->direct_probe_tries++; if (ifmgd->direct_probe_tries > IEEE80211_AUTH_MAX_TRIES) { @@ -697,6 +698,13 @@ static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata) ieee80211_rx_bss_remove(sdata, ifmgd->bssid, sdata->local->hw.conf.channel->center_freq, ifmgd->ssid, ifmgd->ssid_len); + + /* + * We might have a pending scan which had no chance to run yet + * due to state == IEEE80211_STA_MLME_DIRECT_PROBE. + * Hence, queue the STAs work again + */ + queue_work(local->hw.workqueue, &ifmgd->work); return; } @@ -721,6 +729,7 @@ static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata) static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_local *local = sdata->local; ifmgd->auth_tries++; if (ifmgd->auth_tries > IEEE80211_AUTH_MAX_TRIES) { @@ -732,6 +741,13 @@ static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata) ieee80211_rx_bss_remove(sdata, ifmgd->bssid, sdata->local->hw.conf.channel->center_freq, ifmgd->ssid, ifmgd->ssid_len); + + /* + * We might have a pending scan which had no chance to run yet + * due to state == IEEE80211_STA_MLME_AUTHENTICATE. + * Hence, queue the STAs work again + */ + queue_work(local->hw.workqueue, &ifmgd->work); return; } @@ -878,6 +894,7 @@ static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata) static void ieee80211_associate(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_local *local = sdata->local; ifmgd->assoc_tries++; if (ifmgd->assoc_tries > IEEE80211_ASSOC_MAX_TRIES) { @@ -889,6 +906,12 @@ static void ieee80211_associate(struct ieee80211_sub_if_data *sdata) ieee80211_rx_bss_remove(sdata, ifmgd->bssid, sdata->local->hw.conf.channel->center_freq, ifmgd->ssid, ifmgd->ssid_len); + /* + * We might have a pending scan which had no chance to run yet + * due to state == IEEE80211_STA_MLME_ASSOCIATE. + * Hence, queue the STAs work again + */ + queue_work(local->hw.workqueue, &ifmgd->work); return; } -- cgit v1.2.3 From 4ed96f04f8a1869757f4dd4a9283a18ec63c442f Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 12 Mar 2009 21:53:23 +0200 Subject: ath9k: Add support for multiple virtual AP interfaces This patch fixes the TSF offset calculation for staggered Beacon frames and sets ATH_BCBUF back to the earlier value 4 to enable multi-BSS configurations of up to four BSSes. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/ath9k.h | 3 ++- drivers/net/wireless/ath9k/beacon.c | 50 ++++++++++++++++++------------------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/drivers/net/wireless/ath9k/ath9k.h b/drivers/net/wireless/ath9k/ath9k.h index b64be8e9a69..5afd244ea6a 100644 --- a/drivers/net/wireless/ath9k/ath9k.h +++ b/drivers/net/wireless/ath9k/ath9k.h @@ -390,6 +390,7 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid struct ath_vif { int av_bslot; + __le64 tsf_adjust; /* TSF adjustment for staggered beacons */ enum nl80211_iftype av_opmode; struct ath_buf *av_bcbuf; struct ath_tx_control av_btxctl; @@ -406,7 +407,7 @@ struct ath_vif { * number of beacon intervals, the game's up. */ #define BSTUCK_THRESH (9 * ATH_BCBUF) -#define ATH_BCBUF 1 +#define ATH_BCBUF 4 #define ATH_DEFAULT_BINTVAL 100 /* TU */ #define ATH_DEFAULT_BMISS_LIMIT 10 #define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024) diff --git a/drivers/net/wireless/ath9k/beacon.c b/drivers/net/wireless/ath9k/beacon.c index 039c78136c5..3fd1b86a9b3 100644 --- a/drivers/net/wireless/ath9k/beacon.c +++ b/drivers/net/wireless/ath9k/beacon.c @@ -153,6 +153,8 @@ static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw, bf->bf_mpdu = skb; if (skb == NULL) return NULL; + ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp = + avp->tsf_adjust; info = IEEE80211_SKB_CB(skb); if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { @@ -253,7 +255,6 @@ int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif) { struct ath_softc *sc = aphy->sc; struct ath_vif *avp; - struct ieee80211_hdr *hdr; struct ath_buf *bf; struct sk_buff *skb; __le64 tstamp; @@ -316,42 +317,33 @@ int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif) tstamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp; sc->beacon.bc_tstamp = le64_to_cpu(tstamp); - - /* - * Calculate a TSF adjustment factor required for - * staggered beacons. Note that we assume the format - * of the beacon frame leaves the tstamp field immediately - * following the header. - */ + /* Calculate a TSF adjustment factor required for staggered beacons. */ if (avp->av_bslot > 0) { u64 tsfadjust; - __le64 val; int intval; intval = sc->hw->conf.beacon_int ? sc->hw->conf.beacon_int : ATH_DEFAULT_BINTVAL; /* - * The beacon interval is in TU's; the TSF in usecs. - * We figure out how many TU's to add to align the - * timestamp then convert to TSF units and handle - * byte swapping before writing it in the frame. - * The hardware will then add this each time a beacon - * frame is sent. Note that we align vif's 1..N - * and leave vif 0 untouched. This means vap 0 - * has a timestamp in one beacon interval while the - * others get a timestamp aligned to the next interval. + * Calculate the TSF offset for this beacon slot, i.e., the + * number of usecs that need to be added to the timestamp field + * in Beacon and Probe Response frames. Beacon slot 0 is + * processed at the correct offset, so it does not require TSF + * adjustment. Other slots are adjusted to get the timestamp + * close to the TBTT for the BSS. */ - tsfadjust = (intval * (ATH_BCBUF - avp->av_bslot)) / ATH_BCBUF; - val = cpu_to_le64(tsfadjust << 10); /* TU->TSF */ + tsfadjust = intval * avp->av_bslot / ATH_BCBUF; + avp->tsf_adjust = cpu_to_le64(TU_TO_USEC(tsfadjust)); DPRINTF(sc, ATH_DBG_BEACON, "stagger beacons, bslot %d intval %u tsfadjust %llu\n", avp->av_bslot, intval, (unsigned long long)tsfadjust); - hdr = (struct ieee80211_hdr *)skb->data; - memcpy(&hdr[1], &val, sizeof(val)); - } + ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp = + avp->tsf_adjust; + } else + avp->tsf_adjust = cpu_to_le64(0); bf->bf_mpdu = skb; bf->bf_buf_addr = bf->bf_dmacontext = @@ -447,8 +439,16 @@ void ath_beacon_tasklet(unsigned long data) tsf = ath9k_hw_gettsf64(ah); tsftu = TSF_TO_TU(tsf>>32, tsf); slot = ((tsftu % intval) * ATH_BCBUF) / intval; - vif = sc->beacon.bslot[(slot + 1) % ATH_BCBUF]; - aphy = sc->beacon.bslot_aphy[(slot + 1) % ATH_BCBUF]; + /* + * Reverse the slot order to get slot 0 on the TBTT offset that does + * not require TSF adjustment and other slots adding + * slot/ATH_BCBUF * beacon_int to timestamp. For example, with + * ATH_BCBUF = 4, we process beacon slots as follows: 3 2 1 0 3 2 1 .. + * and slot 0 is at correct offset to TBTT. + */ + slot = ATH_BCBUF - slot - 1; + vif = sc->beacon.bslot[slot]; + aphy = sc->beacon.bslot_aphy[slot]; DPRINTF(sc, ATH_DBG_BEACON, "slot %d [tsf %llu tsftu %u intval %u] vif %p\n", -- cgit v1.2.3 From b572b24c578ab1be9d1fcb11d2d8244878757a66 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 12 Mar 2009 18:18:51 -0400 Subject: ath9k: remove dummy PCI "retry timeout" fix Remove the PCI retry timeout code as that was just taken from ipw2100 due to historical reasons but in reality its a no-op, additionally its simply incorrect as each PCI devices has its own custom PCI configuration space on PCI config space >= 0x40. Not to mention we were trying to write 0 to a place that already has 0 on it. Cc: Matthew Garrett Cc: Ben Cahill Cc: Inaky Perez-Gonzalez Tested-by: Adel Gadllah Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/pci.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/drivers/net/wireless/ath9k/pci.c b/drivers/net/wireless/ath9k/pci.c index 9a58baabb9c..53572d96cdb 100644 --- a/drivers/net/wireless/ath9k/pci.c +++ b/drivers/net/wireless/ath9k/pci.c @@ -87,7 +87,6 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) struct ath_softc *sc; struct ieee80211_hw *hw; u8 csz; - u32 val; int ret = 0; struct ath_hw *ah; @@ -134,14 +133,6 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_master(pdev); - /* - * Disable the RETRY_TIMEOUT register (0x41) to keep - * PCI Tx retries from interfering with C3 CPU state. - */ - pci_read_config_dword(pdev, 0x40, &val); - if ((val & 0x0000ff00) != 0) - pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); - ret = pci_request_region(pdev, 0, "ath9k"); if (ret) { dev_err(&pdev->dev, "PCI memory region reserve error\n"); @@ -253,21 +244,12 @@ static int ath_pci_resume(struct pci_dev *pdev) struct ieee80211_hw *hw = pci_get_drvdata(pdev); struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; - u32 val; int err; err = pci_enable_device(pdev); if (err) return err; pci_restore_state(pdev); - /* - * Suspend/Resume resets the PCI configuration space, so we have to - * re-disable the RETRY_TIMEOUT register (0x41) to keep - * PCI Tx retries from interfering with C3 CPU state - */ - pci_read_config_dword(pdev, 0x40, &val); - if ((val & 0x0000ff00) != 0) - pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); /* Enable LED */ ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN, -- cgit v1.2.3 From 7d01b221b338ef2a5b1ffa9c60645c0bdcce191e Mon Sep 17 00:00:00 2001 From: Sujith Date: Fri, 13 Mar 2009 08:55:55 +0530 Subject: ath9k: Miscellaneous EEPROM handling cleanup Print the EEPROM version/revision on init. Choose appropriate debug masks on error conditions, and remove useless print messages. Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/eeprom.c | 75 ++++++++++++------------------------- drivers/net/wireless/ath9k/hw.c | 4 ++ 2 files changed, 28 insertions(+), 51 deletions(-) diff --git a/drivers/net/wireless/ath9k/eeprom.c b/drivers/net/wireless/ath9k/eeprom.c index 183c949bcca..6c9bc5fdb04 100644 --- a/drivers/net/wireless/ath9k/eeprom.c +++ b/drivers/net/wireless/ath9k/eeprom.c @@ -342,8 +342,7 @@ static int ath9k_hw_4k_get_eeprom_rev(struct ath_hw *ah) static bool ath9k_hw_4k_fill_eeprom(struct ath_hw *ah) { #define SIZE_EEPROM_4K (sizeof(struct ar5416_eeprom_4k) / sizeof(u16)) - struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; - u16 *eep_data; + u16 *eep_data = (u16 *)&ah->eeprom.map4k; int addr, eep_start_loc = 0; eep_start_loc = 64; @@ -353,8 +352,6 @@ static bool ath9k_hw_4k_fill_eeprom(struct ath_hw *ah) "Reading from EEPROM, not flash\n"); } - eep_data = (u16 *)eep; - for (addr = 0; addr < SIZE_EEPROM_4K; addr++) { if (!ath9k_hw_nvram_read(ah, addr + eep_start_loc, eep_data)) { DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, @@ -363,6 +360,7 @@ static bool ath9k_hw_4k_fill_eeprom(struct ath_hw *ah) } eep_data++; } + return true; #undef SIZE_EEPROM_4K } @@ -379,16 +377,15 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah) if (!ath9k_hw_use_flash(ah)) { - if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) { - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Reading Magic # failed\n"); return false; } DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "Read Magic = 0x%04X\n", magic); + "Read Magic = 0x%04X\n", magic); if (magic != AR5416_EEPROM_MAGIC) { magic2 = swab16(magic); @@ -401,16 +398,9 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah) temp = swab16(*eepdata); *eepdata = temp; eepdata++; - - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "0x%04X ", *eepdata); - - if (((addr + 1) % 6) == 0) - DPRINTF(ah->ah_sc, - ATH_DBG_EEPROM, "\n"); } } else { - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Invalid EEPROM Magic. " "endianness mismatch.\n"); return -EINVAL; @@ -441,7 +431,7 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah) u16 word; DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "EEPROM Endianness is not native.. Changing \n"); + "EEPROM Endianness is not native.. Changing\n"); word = swab16(eep->baseEepHeader.length); eep->baseEepHeader.length = word; @@ -483,7 +473,7 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah) if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER || ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) { - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Bad EEPROM checksum 0x%x or revision 0x%04x\n", sum, ah->eep_ops->get_eeprom_ver(ah)); return -EINVAL; @@ -1295,9 +1285,6 @@ static bool ath9k_hw_4k_set_board_values(struct ath_hw *ah, db2[4] = ((pModal->db2_234 >> 8) & 0xf); } else if (pModal->version == 1) { - - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "EEPROM Model version is set to 1 \n"); ob[0] = (pModal->ob_01 & 0xf); ob[1] = ob[2] = ob[3] = ob[4] = (pModal->ob_01 >> 4) & 0xf; db1[0] = (pModal->db1_01 & 0xf); @@ -1464,16 +1451,13 @@ static int ath9k_hw_def_get_eeprom_rev(struct ath_hw *ah) static bool ath9k_hw_def_fill_eeprom(struct ath_hw *ah) { #define SIZE_EEPROM_DEF (sizeof(struct ar5416_eeprom_def) / sizeof(u16)) - struct ar5416_eeprom_def *eep = &ah->eeprom.def; - u16 *eep_data; + u16 *eep_data = (u16 *)&ah->eeprom.def; int addr, ar5416_eep_start_loc = 0x100; - eep_data = (u16 *)eep; - for (addr = 0; addr < SIZE_EEPROM_DEF; addr++) { if (!ath9k_hw_nvram_read(ah, addr + ar5416_eep_start_loc, eep_data)) { - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Unable to read eeprom region\n"); return false; } @@ -1492,17 +1476,14 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) bool need_swap = false; int i, addr, size; - if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, - &magic)) { - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "Reading Magic # failed\n"); + if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Reading Magic # failed\n"); return false; } if (!ath9k_hw_use_flash(ah)) { - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "Read Magic = 0x%04X\n", magic); + "Read Magic = 0x%04X\n", magic); if (magic != AR5416_EEPROM_MAGIC) { magic2 = swab16(magic); @@ -1516,18 +1497,11 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) temp = swab16(*eepdata); *eepdata = temp; eepdata++; - - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "0x%04X ", *eepdata); - - if (((addr + 1) % 6) == 0) - DPRINTF(ah->ah_sc, - ATH_DBG_EEPROM, "\n"); } } else { - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Invalid EEPROM Magic. " - "endianness mismatch.\n"); + "Endianness mismatch.\n"); return -EINVAL; } } @@ -1556,7 +1530,7 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) u16 word; DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "EEPROM Endianness is not native.. Changing \n"); + "EEPROM Endianness is not native.. Changing.\n"); word = swab16(eep->baseEepHeader.length); eep->baseEepHeader.length = word; @@ -1602,7 +1576,7 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER || ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) { - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Bad EEPROM checksum 0x%x or revision 0x%04x\n", sum, ah->eep_ops->get_eeprom_ver(ah)); return -EINVAL; @@ -1855,8 +1829,6 @@ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah, AR_AN_TOP2_LOCALBIAS, AR_AN_TOP2_LOCALBIAS_S, pModal->local_bias); - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "ForceXPAon: %d\n", - pModal->force_xpaon); REG_RMW_FIELD(ah, AR_PHY_XPA_CFG, AR_PHY_FORCE_XPA_CFG, pModal->force_xpaon); } @@ -1882,6 +1854,7 @@ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah, REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON, pModal->txEndToRxOn); + if (AR_SREV_9280_10_OR_LATER(ah)) { REG_RMW_FIELD(ah, AR_PHY_CCA, AR9280_PHY_CCA_THRESH62, pModal->thresh62); @@ -1912,10 +1885,10 @@ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah, } if (AR_SREV_9280_20_OR_LATER(ah) && - AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_19) + AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_19) REG_RMW_FIELD(ah, AR_PHY_CCK_TX_CTRL, - AR_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK, - pModal->miscBits); + AR_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK, + pModal->miscBits); if (AR_SREV_9280_20(ah) && AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_20) { @@ -1926,14 +1899,14 @@ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah, REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE, 0); else REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE, - eep->baseEepHeader.dacLpMode); + eep->baseEepHeader.dacLpMode); REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL, AR_PHY_FRAME_CTL_TX_CLIP, - pModal->miscBits >> 2); + pModal->miscBits >> 2); REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL9, - AR_PHY_TX_DESIRED_SCALE_CCK, - eep->baseEepHeader.desiredScaleCCK); + AR_PHY_TX_DESIRED_SCALE_CCK, + eep->baseEepHeader.desiredScaleCCK); } return true; diff --git a/drivers/net/wireless/ath9k/hw.c b/drivers/net/wireless/ath9k/hw.c index d494e98ba97..3dd054b21f5 100644 --- a/drivers/net/wireless/ath9k/hw.c +++ b/drivers/net/wireless/ath9k/hw.c @@ -588,6 +588,10 @@ static int ath9k_hw_post_attach(struct ath_hw *ah) ecode = ath9k_hw_eeprom_attach(ah); if (ecode != 0) return ecode; + + DPRINTF(ah->ah_sc, ATH_DBG_CONFIG, "Eeprom VER: %d, REV: %d\n", + ah->eep_ops->get_eeprom_ver(ah), ah->eep_ops->get_eeprom_rev(ah)); + ecode = ath9k_hw_rfattach(ah); if (ecode != 0) return ecode; -- cgit v1.2.3 From 355363fcf7daded4a48308b54d6b86bea4f8bb6d Mon Sep 17 00:00:00 2001 From: Sujith Date: Fri, 13 Mar 2009 08:56:02 +0530 Subject: ath9k: Move AR5416_VER_MASK to a common location Signed-off-by: Sujith Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/eeprom.c | 4 ---- drivers/net/wireless/ath9k/eeprom.h | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath9k/eeprom.c b/drivers/net/wireless/ath9k/eeprom.c index 6c9bc5fdb04..49dee238c68 100644 --- a/drivers/net/wireless/ath9k/eeprom.c +++ b/drivers/net/wireless/ath9k/eeprom.c @@ -1588,7 +1588,6 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) static u32 ath9k_hw_def_get_eeprom(struct ath_hw *ah, enum eeprom_param param) { -#define AR5416_VER_MASK (pBase->version & AR5416_EEP_VER_MINOR_MASK) struct ar5416_eeprom_def *eep = &ah->eeprom.def; struct modal_eep_header *pModal = eep->modalHeader; struct base_eep_header *pBase = &eep->baseEepHeader; @@ -1655,14 +1654,12 @@ static u32 ath9k_hw_def_get_eeprom(struct ath_hw *ah, default: return 0; } -#undef AR5416_VER_MASK } /* XXX: Clean me up, make me more legible */ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah, struct ath9k_channel *chan) { -#define AR5416_VER_MASK (eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) struct modal_eep_header *pModal; struct ar5416_eeprom_def *eep = &ah->eeprom.def; int i, regChainOffset; @@ -1910,7 +1907,6 @@ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah, } return true; -#undef AR5416_VER_MASK } static void ath9k_hw_def_set_addac(struct ath_hw *ah, diff --git a/drivers/net/wireless/ath9k/eeprom.h b/drivers/net/wireless/ath9k/eeprom.h index d6f6108f63c..0a4b22a554d 100644 --- a/drivers/net/wireless/ath9k/eeprom.h +++ b/drivers/net/wireless/ath9k/eeprom.h @@ -95,6 +95,7 @@ #define FREQ2FBIN(x, y) ((y) ? ((x) - 2300) : (((x) - 4800) / 5)) #define ath9k_hw_use_flash(_ah) (!(_ah->ah_flags & AH_USE_EEPROM)) +#define AR5416_VER_MASK (eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) #define OLC_FOR_AR9280_20_LATER (AR_SREV_9280_20_OR_LATER(ah) && \ ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL)) -- cgit v1.2.3 From a83615d74d9b952f24c904baad58610ed9d76838 Mon Sep 17 00:00:00 2001 From: Sujith Date: Fri, 13 Mar 2009 08:56:04 +0530 Subject: ath9k: Introduce a helper function for setting board gain values This improves readability. Handle both 4K/non-4K EEPROM in this patch. Signed-off-by: Sujith Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/eeprom.c | 219 ++++++++++++++++-------------------- 1 file changed, 99 insertions(+), 120 deletions(-) diff --git a/drivers/net/wireless/ath9k/eeprom.c b/drivers/net/wireless/ath9k/eeprom.c index 49dee238c68..c3bb1968bb5 100644 --- a/drivers/net/wireless/ath9k/eeprom.c +++ b/drivers/net/wireless/ath9k/eeprom.c @@ -1193,57 +1193,63 @@ static void ath9k_hw_4k_set_addac(struct ath_hw *ah, } } -static bool ath9k_hw_4k_set_board_values(struct ath_hw *ah, - struct ath9k_channel *chan) +static void ath9k_hw_4k_set_gain(struct ath_hw *ah, + struct modal_eep_4k_header *pModal, + struct ar5416_eeprom_4k *eep, + u8 txRxAttenLocal, int regChainOffset) { - struct modal_eep_4k_header *pModal; - struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; - int regChainOffset; - u8 txRxAttenLocal; - u8 ob[5], db1[5], db2[5]; - u8 ant_div_control1, ant_div_control2; - u32 regVal; - - - pModal = &eep->modalHeader; - - txRxAttenLocal = 23; - - REG_WRITE(ah, AR_PHY_SWITCH_COM, - ah->eep_ops->get_eeprom_antenna_cfg(ah, chan)); - - regChainOffset = 0; REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0 + regChainOffset, pModal->antCtrlChain[0]); REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset, - (REG_READ(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset) & - ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF | - AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) | - SM(pModal->iqCalICh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) | - SM(pModal->iqCalQCh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF)); + (REG_READ(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset) & + ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF | + AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) | + SM(pModal->iqCalICh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) | + SM(pModal->iqCalQCh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF)); if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= - AR5416_EEP_MINOR_VER_3) { + AR5416_EEP_MINOR_VER_3) { txRxAttenLocal = pModal->txRxAttenCh[0]; + REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, - AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, pModal->bswMargin[0]); + AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, pModal->bswMargin[0]); REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, - AR_PHY_GAIN_2GHZ_XATTEN1_DB, pModal->bswAtten[0]); + AR_PHY_GAIN_2GHZ_XATTEN1_DB, pModal->bswAtten[0]); REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, - AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN, - pModal->xatten2Margin[0]); + AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN, + pModal->xatten2Margin[0]); REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, - AR_PHY_GAIN_2GHZ_XATTEN2_DB, pModal->xatten2Db[0]); + AR_PHY_GAIN_2GHZ_XATTEN2_DB, pModal->xatten2Db[0]); } REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset, - AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal); + AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal); REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset, - AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[0]); + AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[0]); if (AR_SREV_9285_11(ah)) REG_WRITE(ah, AR9285_AN_TOP4, (AR9285_AN_TOP4_DEFAULT | 0x14)); +} + +static bool ath9k_hw_4k_set_board_values(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + struct modal_eep_4k_header *pModal; + struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; + u8 txRxAttenLocal; + u8 ob[5], db1[5], db2[5]; + u8 ant_div_control1, ant_div_control2; + u32 regVal; + + pModal = &eep->modalHeader; + txRxAttenLocal = 23; + + REG_WRITE(ah, AR_PHY_SWITCH_COM, + ah->eep_ops->get_eeprom_antenna_cfg(ah, chan)); + + /* Single chain for 4K EEPROM*/ + ath9k_hw_4k_set_gain(ah, pModal, eep, txRxAttenLocal, 0); /* Initialize Ant Diversity settings from EEPROM */ if (pModal->version == 3) { @@ -1656,7 +1662,62 @@ static u32 ath9k_hw_def_get_eeprom(struct ath_hw *ah, } } -/* XXX: Clean me up, make me more legible */ +static void ath9k_hw_def_set_gain(struct ath_hw *ah, + struct modal_eep_header *pModal, + struct ar5416_eeprom_def *eep, + u8 txRxAttenLocal, int regChainOffset, int i) +{ + if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_3) { + txRxAttenLocal = pModal->txRxAttenCh[i]; + + if (AR_SREV_9280_10_OR_LATER(ah)) { + REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, + AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, + pModal->bswMargin[i]); + REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, + AR_PHY_GAIN_2GHZ_XATTEN1_DB, + pModal->bswAtten[i]); + REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, + AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN, + pModal->xatten2Margin[i]); + REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, + AR_PHY_GAIN_2GHZ_XATTEN2_DB, + pModal->xatten2Db[i]); + } else { + REG_WRITE(ah, AR_PHY_GAIN_2GHZ + regChainOffset, + (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) & + ~AR_PHY_GAIN_2GHZ_BSW_MARGIN) + | SM(pModal-> bswMargin[i], + AR_PHY_GAIN_2GHZ_BSW_MARGIN)); + REG_WRITE(ah, AR_PHY_GAIN_2GHZ + regChainOffset, + (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) & + ~AR_PHY_GAIN_2GHZ_BSW_ATTEN) + | SM(pModal->bswAtten[i], + AR_PHY_GAIN_2GHZ_BSW_ATTEN)); + } + } + + if (AR_SREV_9280_10_OR_LATER(ah)) { + REG_RMW_FIELD(ah, + AR_PHY_RXGAIN + regChainOffset, + AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal); + REG_RMW_FIELD(ah, + AR_PHY_RXGAIN + regChainOffset, + AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[i]); + } else { + REG_WRITE(ah, + AR_PHY_RXGAIN + regChainOffset, + (REG_READ(ah, AR_PHY_RXGAIN + regChainOffset) & + ~AR_PHY_RXGAIN_TXRX_ATTEN) + | SM(txRxAttenLocal, AR_PHY_RXGAIN_TXRX_ATTEN)); + REG_WRITE(ah, + AR_PHY_GAIN_2GHZ + regChainOffset, + (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) & + ~AR_PHY_GAIN_2GHZ_RXTX_MARGIN) | + SM(pModal->rxTxMarginCh[i], AR_PHY_GAIN_2GHZ_RXTX_MARGIN)); + } +} + static bool ath9k_hw_def_set_board_values(struct ath_hw *ah, struct ath9k_channel *chan) { @@ -1666,7 +1727,6 @@ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah, u8 txRxAttenLocal; pModal = &(eep->modalHeader[IS_CHAN_2GHZ(chan)]); - txRxAttenLocal = IS_CHAN_2GHZ(chan) ? 23 : 44; REG_WRITE(ah, AR_PHY_SWITCH_COM, @@ -1679,8 +1739,7 @@ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah, } if (AR_SREV_5416_20_OR_LATER(ah) && - (ah->rxchainmask == 5 || ah->txchainmask == 5) - && (i != 0)) + (ah->rxchainmask == 5 || ah->txchainmask == 5) && (i != 0)) regChainOffset = (i == 1) ? 0x2000 : 0x1000; else regChainOffset = i * 0x1000; @@ -1689,9 +1748,7 @@ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah, pModal->antCtrlChain[i]); REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset, - (REG_READ(ah, - AR_PHY_TIMING_CTRL4(0) + - regChainOffset) & + (REG_READ(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset) & ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF | AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) | SM(pModal->iqCalICh[i], @@ -1699,87 +1756,9 @@ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah, SM(pModal->iqCalQCh[i], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF)); - if ((i == 0) || AR_SREV_5416_20_OR_LATER(ah)) { - if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_3) { - txRxAttenLocal = pModal->txRxAttenCh[i]; - if (AR_SREV_9280_10_OR_LATER(ah)) { - REG_RMW_FIELD(ah, - AR_PHY_GAIN_2GHZ + - regChainOffset, - AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, - pModal-> - bswMargin[i]); - REG_RMW_FIELD(ah, - AR_PHY_GAIN_2GHZ + - regChainOffset, - AR_PHY_GAIN_2GHZ_XATTEN1_DB, - pModal-> - bswAtten[i]); - REG_RMW_FIELD(ah, - AR_PHY_GAIN_2GHZ + - regChainOffset, - AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN, - pModal-> - xatten2Margin[i]); - REG_RMW_FIELD(ah, - AR_PHY_GAIN_2GHZ + - regChainOffset, - AR_PHY_GAIN_2GHZ_XATTEN2_DB, - pModal-> - xatten2Db[i]); - } else { - REG_WRITE(ah, - AR_PHY_GAIN_2GHZ + - regChainOffset, - (REG_READ(ah, - AR_PHY_GAIN_2GHZ + - regChainOffset) & - ~AR_PHY_GAIN_2GHZ_BSW_MARGIN) - | SM(pModal-> - bswMargin[i], - AR_PHY_GAIN_2GHZ_BSW_MARGIN)); - REG_WRITE(ah, - AR_PHY_GAIN_2GHZ + - regChainOffset, - (REG_READ(ah, - AR_PHY_GAIN_2GHZ + - regChainOffset) & - ~AR_PHY_GAIN_2GHZ_BSW_ATTEN) - | SM(pModal->bswAtten[i], - AR_PHY_GAIN_2GHZ_BSW_ATTEN)); - } - } - if (AR_SREV_9280_10_OR_LATER(ah)) { - REG_RMW_FIELD(ah, - AR_PHY_RXGAIN + - regChainOffset, - AR9280_PHY_RXGAIN_TXRX_ATTEN, - txRxAttenLocal); - REG_RMW_FIELD(ah, - AR_PHY_RXGAIN + - regChainOffset, - AR9280_PHY_RXGAIN_TXRX_MARGIN, - pModal->rxTxMarginCh[i]); - } else { - REG_WRITE(ah, - AR_PHY_RXGAIN + regChainOffset, - (REG_READ(ah, - AR_PHY_RXGAIN + - regChainOffset) & - ~AR_PHY_RXGAIN_TXRX_ATTEN) | - SM(txRxAttenLocal, - AR_PHY_RXGAIN_TXRX_ATTEN)); - REG_WRITE(ah, - AR_PHY_GAIN_2GHZ + - regChainOffset, - (REG_READ(ah, - AR_PHY_GAIN_2GHZ + - regChainOffset) & - ~AR_PHY_GAIN_2GHZ_RXTX_MARGIN) | - SM(pModal->rxTxMarginCh[i], - AR_PHY_GAIN_2GHZ_RXTX_MARGIN)); - } - } + if ((i == 0) || AR_SREV_5416_20_OR_LATER(ah)) + ath9k_hw_def_set_gain(ah, pModal, eep, txRxAttenLocal, + regChainOffset, i); } if (AR_SREV_9280_10_OR_LATER(ah)) { -- cgit v1.2.3 From d6509151bd3e952b7d157ea4dbae23279d427e95 Mon Sep 17 00:00:00 2001 From: Sujith Date: Fri, 13 Mar 2009 08:56:05 +0530 Subject: ath9k: Change return type for set_board_values() We always return true, checking for 'false' return value is bogus anyway, so fix this. Signed-off-by: Sujith Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/eeprom.c | 8 ++------ drivers/net/wireless/ath9k/eeprom.h | 2 +- drivers/net/wireless/ath9k/hw.c | 6 +----- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ath9k/eeprom.c b/drivers/net/wireless/ath9k/eeprom.c index c3bb1968bb5..973b576c0ad 100644 --- a/drivers/net/wireless/ath9k/eeprom.c +++ b/drivers/net/wireless/ath9k/eeprom.c @@ -1232,7 +1232,7 @@ static void ath9k_hw_4k_set_gain(struct ath_hw *ah, REG_WRITE(ah, AR9285_AN_TOP4, (AR9285_AN_TOP4_DEFAULT | 0x14)); } -static bool ath9k_hw_4k_set_board_values(struct ath_hw *ah, +static void ath9k_hw_4k_set_board_values(struct ath_hw *ah, struct ath9k_channel *chan) { struct modal_eep_4k_header *pModal; @@ -1378,8 +1378,6 @@ static bool ath9k_hw_4k_set_board_values(struct ath_hw *ah, AR_PHY_SETTLING_SWITCH, pModal->swSettleHt40); } - - return true; } static u16 ath9k_hw_4k_get_eeprom_antenna_cfg(struct ath_hw *ah, @@ -1718,7 +1716,7 @@ static void ath9k_hw_def_set_gain(struct ath_hw *ah, } } -static bool ath9k_hw_def_set_board_values(struct ath_hw *ah, +static void ath9k_hw_def_set_board_values(struct ath_hw *ah, struct ath9k_channel *chan) { struct modal_eep_header *pModal; @@ -1884,8 +1882,6 @@ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah, AR_PHY_TX_DESIRED_SCALE_CCK, eep->baseEepHeader.desiredScaleCCK); } - - return true; } static void ath9k_hw_def_set_addac(struct ath_hw *ah, diff --git a/drivers/net/wireless/ath9k/eeprom.h b/drivers/net/wireless/ath9k/eeprom.h index 0a4b22a554d..8e5b880d445 100644 --- a/drivers/net/wireless/ath9k/eeprom.h +++ b/drivers/net/wireless/ath9k/eeprom.h @@ -490,7 +490,7 @@ struct eeprom_ops { u8 (*get_num_ant_config)(struct ath_hw *hw, enum ieee80211_band band); u16 (*get_eeprom_antenna_cfg)(struct ath_hw *hw, struct ath9k_channel *chan); - bool (*set_board_values)(struct ath_hw *hw, struct ath9k_channel *chan); + void (*set_board_values)(struct ath_hw *hw, struct ath9k_channel *chan); void (*set_addac)(struct ath_hw *hw, struct ath9k_channel *chan); int (*set_txpower)(struct ath_hw *hw, struct ath9k_channel *chan, u16 cfgCtl, u8 twiceAntennaReduction, diff --git a/drivers/net/wireless/ath9k/hw.c b/drivers/net/wireless/ath9k/hw.c index 3dd054b21f5..78e5763f7c1 100644 --- a/drivers/net/wireless/ath9k/hw.c +++ b/drivers/net/wireless/ath9k/hw.c @@ -2277,11 +2277,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, else ath9k_hw_spur_mitigate(ah, chan); - if (!ah->eep_ops->set_board_values(ah, chan)) { - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "error setting board options\n"); - return -EIO; - } + ah->eep_ops->set_board_values(ah, chan); ath9k_hw_decrease_chain_power(ah, chan); -- cgit v1.2.3 From e71cef37f1f4cb7e9c919cbaabe23438f10a7080 Mon Sep 17 00:00:00 2001 From: Sujith Date: Fri, 13 Mar 2009 08:56:07 +0530 Subject: ath9k: Fix bug in 4K EEPROM size calculation We should be checking with the 4K header and not the non-4K header size. Signed-off-by: Sujith Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/eeprom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath9k/eeprom.c b/drivers/net/wireless/ath9k/eeprom.c index 973b576c0ad..d7b9cf4e8eb 100644 --- a/drivers/net/wireless/ath9k/eeprom.c +++ b/drivers/net/wireless/ath9k/eeprom.c @@ -416,7 +416,7 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah) else el = ah->eeprom.map4k.baseEepHeader.length; - if (el > sizeof(struct ar5416_eeprom_def)) + if (el > sizeof(struct ar5416_eeprom_4k)) el = sizeof(struct ar5416_eeprom_4k) / sizeof(u16); else el = el / sizeof(u16); -- cgit v1.2.3 From 95e4acb7331722236b9f11492ae2e96473210ebc Mon Sep 17 00:00:00 2001 From: Sujith Date: Fri, 13 Mar 2009 08:56:09 +0530 Subject: ath9k: Fill in ack signal in TX status This patch fills the ack_signal field in TX status with an appropriate value from the TX descriptor. Signed-off-by: Sujith Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/xmit.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath9k/xmit.c b/drivers/net/wireless/ath9k/xmit.c index e3f376611f8..0aae8f349ff 100644 --- a/drivers/net/wireless/ath9k/xmit.c +++ b/drivers/net/wireless/ath9k/xmit.c @@ -1852,13 +1852,17 @@ static int ath_tx_num_badfrms(struct ath_softc *sc, struct ath_buf *bf, return nbad; } -static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds, int nbad) +static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds, + int nbad, int txok) { struct sk_buff *skb = (struct sk_buff *)bf->bf_mpdu; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info); + if (txok) + tx_info->status.ack_signal = ds->ds_txstat.ts_rssi; + tx_info_priv->update_rc = false; if (ds->ds_txstat.ts_status & ATH9K_TXERR_FILT) tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED; @@ -1996,7 +2000,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) nbad = ath_tx_num_badfrms(sc, bf, txok); } - ath_tx_rc_status(bf, ds, nbad); + ath_tx_rc_status(bf, ds, nbad, txok); if (bf_isampdu(bf)) ath_tx_complete_aggr(sc, txq, bf, &bf_head, txok); -- cgit v1.2.3 From c2da50e5837f92c9f16a611efd1bb0754036691b Mon Sep 17 00:00:00 2001 From: Sujith Date: Fri, 13 Mar 2009 08:56:11 +0530 Subject: ath9k: Fix bug in handling single stream stations AP mode currently sets up the dual stream capability for all stations. This patch fixes it by checking if the associated station supports dual stream MCS rates (8-15). We would disregard any MCS rates above 15, since Atheros HW supports only 0..15 rates currently, and can't receive at rates > 15 anyway. Signed-off-by: Sujith Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/rc.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath9k/rc.c b/drivers/net/wireless/ath9k/rc.c index 28d69da342c..74bc4e64b03 100644 --- a/drivers/net/wireless/ath9k/rc.c +++ b/drivers/net/wireless/ath9k/rc.c @@ -1470,16 +1470,18 @@ static void ath_rc_init(struct ath_softc *sc, ath_rc_priv->ht_cap); } -static u8 ath_rc_build_ht_caps(struct ath_softc *sc, bool is_ht, bool is_cw40, - bool is_sgi40) +static u8 ath_rc_build_ht_caps(struct ath_softc *sc, struct ieee80211_sta *sta, + bool is_cw40, bool is_sgi40) { u8 caps = 0; - if (is_ht) { + if (sta->ht_cap.ht_supported) { caps = WLAN_RC_HT_FLAG; if (sc->sc_ah->caps.tx_chainmask != 1 && - ath9k_hw_getcapability(sc->sc_ah, ATH9K_CAP_DS, 0, NULL)) - caps |= WLAN_RC_DS_FLAG; + ath9k_hw_getcapability(sc->sc_ah, ATH9K_CAP_DS, 0, NULL)) { + if (sta->ht_cap.mcs.rx_mask[1]) + caps |= WLAN_RC_DS_FLAG; + } if (is_cw40) caps |= WLAN_RC_40_FLAG; if (is_sgi40) @@ -1626,8 +1628,7 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband, rate_table = sc->cur_rate_table; } - ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta->ht_cap.ht_supported, - is_cw40, is_sgi40); + ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta, is_cw40, is_sgi40); ath_rc_init(sc, priv_sta, sband, sta, rate_table); } @@ -1661,8 +1662,7 @@ static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband, rate_table = ath_choose_rate_table(sc, sband->band, sta->ht_cap.ht_supported, oper_cw40); - ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, - sta->ht_cap.ht_supported, + ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta, oper_cw40, oper_sgi40); ath_rc_init(sc, priv_sta, sband, sta, rate_table); -- cgit v1.2.3 From cee075a24eec64f1f5b2b3b14753b2d4b8ecce55 Mon Sep 17 00:00:00 2001 From: Sujith Date: Fri, 13 Mar 2009 09:07:23 +0530 Subject: ath9k: Update copyright in all the files How time flies. Signed-off-by: Sujith Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/ahb.c | 2 +- drivers/net/wireless/ath9k/ani.c | 2 +- drivers/net/wireless/ath9k/ani.h | 2 +- drivers/net/wireless/ath9k/ath9k.h | 2 +- drivers/net/wireless/ath9k/beacon.c | 2 +- drivers/net/wireless/ath9k/calib.c | 2 +- drivers/net/wireless/ath9k/calib.h | 2 +- drivers/net/wireless/ath9k/debug.c | 2 +- drivers/net/wireless/ath9k/debug.h | 2 +- drivers/net/wireless/ath9k/eeprom.c | 2 +- drivers/net/wireless/ath9k/eeprom.h | 2 +- drivers/net/wireless/ath9k/hw.c | 2 +- drivers/net/wireless/ath9k/hw.h | 2 +- drivers/net/wireless/ath9k/initvals.h | 2 +- drivers/net/wireless/ath9k/mac.c | 2 +- drivers/net/wireless/ath9k/mac.h | 2 +- drivers/net/wireless/ath9k/main.c | 2 +- drivers/net/wireless/ath9k/pci.c | 2 +- drivers/net/wireless/ath9k/phy.c | 2 +- drivers/net/wireless/ath9k/phy.h | 2 +- drivers/net/wireless/ath9k/rc.c | 2 +- drivers/net/wireless/ath9k/rc.h | 2 +- drivers/net/wireless/ath9k/recv.c | 2 +- drivers/net/wireless/ath9k/reg.h | 2 +- drivers/net/wireless/ath9k/regd.c | 2 +- drivers/net/wireless/ath9k/regd.h | 2 +- drivers/net/wireless/ath9k/regd_common.h | 2 +- drivers/net/wireless/ath9k/xmit.c | 2 +- 28 files changed, 28 insertions(+), 28 deletions(-) diff --git a/drivers/net/wireless/ath9k/ahb.c b/drivers/net/wireless/ath9k/ahb.c index 00cc7bb01f2..0e65c51ba17 100644 --- a/drivers/net/wireless/ath9k/ahb.c +++ b/drivers/net/wireless/ath9k/ahb.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 Atheros Communications Inc. * Copyright (c) 2009 Gabor Juhos * Copyright (c) 2009 Imre Kaloz * diff --git a/drivers/net/wireless/ath9k/ani.c b/drivers/net/wireless/ath9k/ani.c index a39eb760cbb..6c5e887d50d 100644 --- a/drivers/net/wireless/ath9k/ani.c +++ b/drivers/net/wireless/ath9k/ani.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/ani.h b/drivers/net/wireless/ath9k/ani.h index 7315761f6d7..08b4e7ed5ff 100644 --- a/drivers/net/wireless/ath9k/ani.h +++ b/drivers/net/wireless/ath9k/ani.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/ath9k.h b/drivers/net/wireless/ath9k/ath9k.h index 5afd244ea6a..2b025645511 100644 --- a/drivers/net/wireless/ath9k/ath9k.h +++ b/drivers/net/wireless/ath9k/ath9k.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/beacon.c b/drivers/net/wireless/ath9k/beacon.c index 3fd1b86a9b3..e5b007196ca 100644 --- a/drivers/net/wireless/ath9k/beacon.c +++ b/drivers/net/wireless/ath9k/beacon.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/calib.c b/drivers/net/wireless/ath9k/calib.c index c9446fb6b15..e2d62e97131 100644 --- a/drivers/net/wireless/ath9k/calib.c +++ b/drivers/net/wireless/ath9k/calib.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/calib.h b/drivers/net/wireless/ath9k/calib.h index 32589e0c501..1c74bd50700 100644 --- a/drivers/net/wireless/ath9k/calib.h +++ b/drivers/net/wireless/ath9k/calib.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/debug.c b/drivers/net/wireless/ath9k/debug.c index 82573cadb1a..fdf9528fa49 100644 --- a/drivers/net/wireless/ath9k/debug.c +++ b/drivers/net/wireless/ath9k/debug.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/debug.h b/drivers/net/wireless/ath9k/debug.h index 065268b8568..7b0e5419d2b 100644 --- a/drivers/net/wireless/ath9k/debug.h +++ b/drivers/net/wireless/ath9k/debug.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/eeprom.c b/drivers/net/wireless/ath9k/eeprom.c index d7b9cf4e8eb..ffc36b0361c 100644 --- a/drivers/net/wireless/ath9k/eeprom.c +++ b/drivers/net/wireless/ath9k/eeprom.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/eeprom.h b/drivers/net/wireless/ath9k/eeprom.h index 8e5b880d445..25b68c881ff 100644 --- a/drivers/net/wireless/ath9k/eeprom.h +++ b/drivers/net/wireless/ath9k/eeprom.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/hw.c b/drivers/net/wireless/ath9k/hw.c index 78e5763f7c1..15e4d422cad 100644 --- a/drivers/net/wireless/ath9k/hw.c +++ b/drivers/net/wireless/ath9k/hw.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/hw.h b/drivers/net/wireless/ath9k/hw.h index dc681f011fd..0b594e0ee26 100644 --- a/drivers/net/wireless/ath9k/hw.h +++ b/drivers/net/wireless/ath9k/hw.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/initvals.h b/drivers/net/wireless/ath9k/initvals.h index 1d60c3706f1..e2f0a34b79a 100644 --- a/drivers/net/wireless/ath9k/initvals.h +++ b/drivers/net/wireless/ath9k/initvals.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/mac.c b/drivers/net/wireless/ath9k/mac.c index f757bc7eec6..e0a6dee4583 100644 --- a/drivers/net/wireless/ath9k/mac.c +++ b/drivers/net/wireless/ath9k/mac.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/mac.h b/drivers/net/wireless/ath9k/mac.h index a75f65dae1d..1176bce8b76 100644 --- a/drivers/net/wireless/ath9k/mac.h +++ b/drivers/net/wireless/ath9k/mac.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index 8db75f6de53..7d27eed78af 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/pci.c b/drivers/net/wireless/ath9k/pci.c index 53572d96cdb..6dbc58580ab 100644 --- a/drivers/net/wireless/ath9k/pci.c +++ b/drivers/net/wireless/ath9k/pci.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/phy.c b/drivers/net/wireless/ath9k/phy.c index e1494bae0f9..8bcba906929 100644 --- a/drivers/net/wireless/ath9k/phy.c +++ b/drivers/net/wireless/ath9k/phy.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/phy.h b/drivers/net/wireless/ath9k/phy.h index 1eac8c70734..0f7f8e0c9c9 100644 --- a/drivers/net/wireless/ath9k/phy.h +++ b/drivers/net/wireless/ath9k/phy.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/rc.c b/drivers/net/wireless/ath9k/rc.c index 74bc4e64b03..6c2fd395bc3 100644 --- a/drivers/net/wireless/ath9k/rc.c +++ b/drivers/net/wireless/ath9k/rc.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2004 Video54 Technologies, Inc. - * Copyright (c) 2004-2008 Atheros Communications, Inc. + * Copyright (c) 2004-2009 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 diff --git a/drivers/net/wireless/ath9k/rc.h b/drivers/net/wireless/ath9k/rc.h index db9b0b9a343..199a3ce57d6 100644 --- a/drivers/net/wireless/ath9k/rc.h +++ b/drivers/net/wireless/ath9k/rc.h @@ -1,7 +1,7 @@ /* * Copyright (c) 2004 Sam Leffler, Errno Consulting * Copyright (c) 2004 Video54 Technologies, Inc. - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/recv.c b/drivers/net/wireless/ath9k/recv.c index 0bba17662a1..917bac7af6f 100644 --- a/drivers/net/wireless/ath9k/recv.c +++ b/drivers/net/wireless/ath9k/recv.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/reg.h b/drivers/net/wireless/ath9k/reg.h index d86e90e3817..52605246679 100644 --- a/drivers/net/wireless/ath9k/reg.h +++ b/drivers/net/wireless/ath9k/reg.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/regd.c b/drivers/net/wireless/ath9k/regd.c index b8f9b6d6bec..4ca62510229 100644 --- a/drivers/net/wireless/ath9k/regd.c +++ b/drivers/net/wireless/ath9k/regd.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/regd.h b/drivers/net/wireless/ath9k/regd.h index 8f885f3bc8d..9f5fbd4eea7 100644 --- a/drivers/net/wireless/ath9k/regd.h +++ b/drivers/net/wireless/ath9k/regd.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/regd_common.h b/drivers/net/wireless/ath9k/regd_common.h index b41d0002f3f..4d0e298cd1c 100644 --- a/drivers/net/wireless/ath9k/regd_common.h +++ b/drivers/net/wireless/ath9k/regd_common.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 diff --git a/drivers/net/wireless/ath9k/xmit.c b/drivers/net/wireless/ath9k/xmit.c index 0aae8f349ff..8968abe7f48 100644 --- a/drivers/net/wireless/ath9k/xmit.c +++ b/drivers/net/wireless/ath9k/xmit.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 -- cgit v1.2.3 From b5bde374f0f61f5d97114d400ade8fc96bf6f10d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 13 Mar 2009 11:19:45 +0100 Subject: mac80211: fix warnings in ieee80211_if_config The last warning can never trigger, and the explicit AP_VLAN check is pointless if we move the config_interface check down, in practice config_interface is required anyway. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/main.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/net/mac80211/main.c b/net/mac80211/main.c index f38db4d37e5..dac68d476bf 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -161,12 +161,6 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed) if (WARN_ON(!netif_running(sdata->dev))) return 0; - if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) - return -EINVAL; - - if (!local->ops->config_interface) - return 0; - memset(&conf, 0, sizeof(conf)); if (sdata->vif.type == NL80211_IFTYPE_STATION) @@ -183,6 +177,9 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed) return -EINVAL; } + if (!local->ops->config_interface) + return 0; + switch (sdata->vif.type) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_ADHOC: @@ -224,9 +221,6 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed) } } - if (WARN_ON(!conf.bssid && (changed & IEEE80211_IFCC_BSSID))) - return -EINVAL; - conf.changed = changed; return local->ops->config_interface(local_to_hw(local), -- cgit v1.2.3 From 25420604c8967ff24f087dd7b9cd4b278567d39a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 13 Mar 2009 11:43:36 +0100 Subject: mac80211: stop queues across suspend/resume Even though userland probably cannot submit packets, there might still be some coming, and that's no good when the driver doesn't expect them. Stop the queues across suspend/resume. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 1 + net/mac80211/pm.c | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index fbb91f1aebb..ad12c2a03a9 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -598,6 +598,7 @@ enum queue_stop_reason { IEEE80211_QUEUE_STOP_REASON_PS, IEEE80211_QUEUE_STOP_REASON_CSA, IEEE80211_QUEUE_STOP_REASON_AGGREGATION, + IEEE80211_QUEUE_STOP_REASON_SUSPEND, }; struct ieee80211_master_priv { diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 44525f51707..c923ceb089a 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -11,6 +11,9 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) struct ieee80211_if_init_conf conf; struct sta_info *sta; + ieee80211_stop_queues_by_reason(hw, + IEEE80211_QUEUE_STOP_REASON_SUSPEND); + flush_workqueue(local->hw.workqueue); /* disable keys */ @@ -113,5 +116,8 @@ int __ieee80211_resume(struct ieee80211_hw *hw) ieee80211_configure_filter(local); netif_addr_unlock_bh(local->mdev); + ieee80211_wake_queues_by_reason(hw, + IEEE80211_QUEUE_STOP_REASON_SUSPEND); + return 0; } -- cgit v1.2.3 From aae89831df03e5282a8f5c0ee46432cfb677fc5c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 13 Mar 2009 12:52:10 +0100 Subject: wireless: radiotap updates Radiotap was updated to include a "bad PLCP" flag and standardise the "bad FCS" flag in the "flags" rather than "RX flags" field, this patch updates Linux to that standard. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/radiotap.h | 10 ---------- drivers/net/wireless/libertas/rx.c | 12 ++---------- include/net/ieee80211_radiotap.h | 4 +++- net/mac80211/rx.c | 7 ++++--- 4 files changed, 9 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/libertas/radiotap.h b/drivers/net/wireless/libertas/radiotap.h index f8eb9097ff0..d16b26416e8 100644 --- a/drivers/net/wireless/libertas/radiotap.h +++ b/drivers/net/wireless/libertas/radiotap.h @@ -33,22 +33,12 @@ struct rx_radiotap_hdr { struct ieee80211_radiotap_header hdr; u8 flags; u8 rate; - u16 chan_freq; - u16 chan_flags; - u8 antenna; u8 antsignal; - u16 rx_flags; -#if 0 - u8 pad[IEEE80211_RADIOTAP_HDRLEN - 18]; -#endif } __attribute__ ((packed)); #define RX_RADIOTAP_PRESENT ( \ (1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ - (1 << IEEE80211_RADIOTAP_CHANNEL) | \ - (1 << IEEE80211_RADIOTAP_ANTENNA) | \ (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |\ - (1 << IEEE80211_RADIOTAP_RX_FLAGS) | \ 0) diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/libertas/rx.c index 4f60948dde9..63d7e19ce9b 100644 --- a/drivers/net/wireless/libertas/rx.c +++ b/drivers/net/wireless/libertas/rx.c @@ -351,19 +351,11 @@ static int process_rxed_802_11_packet(struct lbs_private *priv, radiotap_hdr.hdr.it_pad = 0; radiotap_hdr.hdr.it_len = cpu_to_le16 (sizeof(struct rx_radiotap_hdr)); radiotap_hdr.hdr.it_present = cpu_to_le32 (RX_RADIOTAP_PRESENT); - /* unknown values */ - radiotap_hdr.flags = 0; - radiotap_hdr.chan_freq = 0; - radiotap_hdr.chan_flags = 0; - radiotap_hdr.antenna = 0; - /* known values */ + if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK))) + radiotap_hdr.flags |= IEEE80211_RADIOTAP_F_BADFCS; radiotap_hdr.rate = convert_mv_rate_to_radiotap(prxpd->rx_rate); /* XXX must check no carryout */ radiotap_hdr.antsignal = prxpd->snr + prxpd->nf; - radiotap_hdr.rx_flags = 0; - if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK))) - radiotap_hdr.rx_flags |= IEEE80211_RADIOTAP_F_RX_BADFCS; - //memset(radiotap_hdr.pad, 0x11, IEEE80211_RADIOTAP_HDRLEN - 18); /* chop the rxpd */ skb_pull(skb, sizeof(struct rxpd)); diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h index 384698cb773..23c3f3d9777 100644 --- a/include/net/ieee80211_radiotap.h +++ b/include/net/ieee80211_radiotap.h @@ -230,8 +230,10 @@ enum ieee80211_radiotap_type { * 802.11 header and payload * (to 32-bit boundary) */ +#define IEEE80211_RADIOTAP_F_BADFCS 0x40 /* bad FCS */ + /* For IEEE80211_RADIOTAP_RX_FLAGS */ -#define IEEE80211_RADIOTAP_F_RX_BADFCS 0x0001 /* frame failed crc check */ +#define IEEE80211_RADIOTAP_F_RX_BADPLCP 0x0002 /* frame has bad PLCP */ /* For IEEE80211_RADIOTAP_TX_FLAGS */ #define IEEE80211_RADIOTAP_F_TX_FAIL 0x0001 /* failed due to excessive diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 66f7ecf51b9..fcc0a599579 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -142,6 +142,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, /* IEEE80211_RADIOTAP_FLAGS */ if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) *pos |= IEEE80211_RADIOTAP_F_FCS; + if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC)) + *pos |= IEEE80211_RADIOTAP_F_BADFCS; if (status->flag & RX_FLAG_SHORTPRE) *pos |= IEEE80211_RADIOTAP_F_SHORTPRE; pos++; @@ -204,9 +206,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, /* ensure 2 byte alignment for the 2 byte field as required */ if ((pos - (unsigned char *)rthdr) & 1) pos++; - /* FIXME: when radiotap gets a 'bad PLCP' flag use it here */ - if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC)) - *(__le16 *)pos |= cpu_to_le16(IEEE80211_RADIOTAP_F_RX_BADFCS); + if (status->flag & RX_FLAG_FAILED_PLCP_CRC) + *(__le16 *)pos |= cpu_to_le16(IEEE80211_RADIOTAP_F_RX_BADPLCP); pos += 2; } -- cgit v1.2.3 From ec30415f7935f0ff92f93a4ac87233ca3007a78a Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Fri, 13 Mar 2009 20:26:52 +0530 Subject: mac80211: Populate HT limitation with TKIP/WEP to the handler for SIOCSIWENCODE too Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: John W. Linville --- net/mac80211/wext.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 935c63ed3df..e55d2834764 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -630,7 +630,7 @@ static int ieee80211_ioctl_siwencode(struct net_device *dev, struct ieee80211_sub_if_data *sdata; int idx, i, alg = ALG_WEP; u8 bcaddr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - int remove = 0; + int remove = 0, ret; sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -656,11 +656,20 @@ static int ieee80211_ioctl_siwencode(struct net_device *dev, return 0; } - return ieee80211_set_encryption( + ret = ieee80211_set_encryption( sdata, bcaddr, idx, alg, remove, !sdata->default_key, keybuf, erq->length); + + if (!ret) { + if (remove) + sdata->u.mgd.flags &= ~IEEE80211_STA_TKIP_WEP_USED; + else + sdata->u.mgd.flags |= IEEE80211_STA_TKIP_WEP_USED; + } + + return ret; } -- cgit v1.2.3 From 8fdc621dc743b87879ccf0177969864b09388d9a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 14 Mar 2009 09:34:01 +0100 Subject: nl80211: export supported commands This makes nl80211 export the supported commands (command groups) per wiphy so userspace has an idea what it can do -- this will be required reading for userspace when we introduce auth/assoc /or/ connect for older hardware that cannot separate auth and assoc. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 6 ++++++ net/wireless/nl80211.c | 27 +++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index f33aa08dd9b..3700d927e24 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -349,6 +349,10 @@ enum nl80211_commands { * @NL80211_ATTR_REG_TYPE: indicates the type of the regulatory domain currently * set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*) * + * @NL80211_ATTR_SUPPORTED_COMMANDS: wiphy attribute that specifies + * an array of command numbers (i.e. a mapping index to command number) + * that the driver for the given wiphy supports. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -426,6 +430,8 @@ enum nl80211_attrs { NL80211_ATTR_REG_INITIATOR, NL80211_ATTR_REG_TYPE, + NL80211_ATTR_SUPPORTED_COMMANDS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ab9d8f14e15..58ee1b1aff8 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -131,6 +131,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, struct nlattr *nl_freqs, *nl_freq; struct nlattr *nl_rates, *nl_rate; struct nlattr *nl_modes; + struct nlattr *nl_cmds; enum ieee80211_band band; struct ieee80211_channel *chan; struct ieee80211_rate *rate; @@ -242,6 +243,32 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, } nla_nest_end(msg, nl_bands); + nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS); + if (!nl_cmds) + goto nla_put_failure; + + i = 0; +#define CMD(op, n) \ + do { \ + if (dev->ops->op) { \ + i++; \ + NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \ + } \ + } while (0) + + CMD(add_virtual_intf, NEW_INTERFACE); + CMD(change_virtual_intf, SET_INTERFACE); + CMD(add_key, NEW_KEY); + CMD(add_beacon, NEW_BEACON); + CMD(add_station, NEW_STATION); + CMD(add_mpath, NEW_MPATH); + CMD(set_mesh_params, SET_MESH_PARAMS); + CMD(change_bss, SET_BSS); + CMD(set_mgmt_extra_ie, SET_MGMT_EXTRA_IE); + +#undef CMD + nla_nest_end(msg, nl_cmds); + return genlmsg_end(msg, hdr); nla_put_failure: -- cgit v1.2.3 From 7f0216a49bea717b9606b81c60f2f0b6152123eb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 14 Mar 2009 09:42:49 +0100 Subject: mac80211: acquire sta_lock for station suspend/resume To avoid concurrent manipulations of the sta list (which shouldn't be possible at this point, but anyway) we need to hold the sta_lock around iterating the list. At the same time, we do not need to iterate the list at all if the driver doesn't want to be notified. Signed-off-by: Johannes Berg Acked-by: Bob Copeland Signed-off-by: John W. Linville --- net/mac80211/pm.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index c923ceb089a..ef7be1ce2c8 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -10,6 +10,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) struct ieee80211_sub_if_data *sdata; struct ieee80211_if_init_conf conf; struct sta_info *sta; + unsigned long flags; ieee80211_stop_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_SUSPEND); @@ -21,9 +22,9 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) ieee80211_disable_keys(sdata); /* remove STAs */ - list_for_each_entry(sta, &local->sta_list, list) { - - if (local->ops->sta_notify) { + if (local->ops->sta_notify) { + spin_lock_irqsave(&local->sta_lock, flags); + list_for_each_entry(sta, &local->sta_list, list) { if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, @@ -32,11 +33,11 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) local->ops->sta_notify(hw, &sdata->vif, STA_NOTIFY_REMOVE, &sta->sta); } + spin_unlock_irqrestore(&local->sta_lock, flags); } /* remove all interfaces */ list_for_each_entry(sdata, &local->interfaces, list) { - if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && sdata->vif.type != NL80211_IFTYPE_MONITOR && netif_running(sdata->dev)) { @@ -64,6 +65,7 @@ int __ieee80211_resume(struct ieee80211_hw *hw) struct ieee80211_sub_if_data *sdata; struct ieee80211_if_init_conf conf; struct sta_info *sta; + unsigned long flags; int res; /* restart hardware */ @@ -75,7 +77,6 @@ int __ieee80211_resume(struct ieee80211_hw *hw) /* add interfaces */ list_for_each_entry(sdata, &local->interfaces, list) { - if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && sdata->vif.type != NL80211_IFTYPE_MONITOR && netif_running(sdata->dev)) { @@ -87,9 +88,9 @@ int __ieee80211_resume(struct ieee80211_hw *hw) } /* add STAs back */ - list_for_each_entry(sta, &local->sta_list, list) { - - if (local->ops->sta_notify) { + if (local->ops->sta_notify) { + spin_lock_irqsave(&local->sta_lock, flags); + list_for_each_entry(sta, &local->sta_list, list) { if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, @@ -98,6 +99,7 @@ int __ieee80211_resume(struct ieee80211_hw *hw) local->ops->sta_notify(hw, &sdata->vif, STA_NOTIFY_ADD, &sta->sta); } + spin_unlock_irqrestore(&local->sta_lock, flags); } /* add back keys */ -- cgit v1.2.3 From 85067c06ba0329c37d5a357ced1f39a5583ccc98 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Sat, 14 Mar 2009 19:59:41 +0530 Subject: ath9k: Keep LED on in idle state after association Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/main.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index 7d27eed78af..4c29cef66a6 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c @@ -940,18 +940,25 @@ static void ath_led_blink_work(struct work_struct *work) if (!(sc->sc_flags & SC_OP_LED_ASSOCIATED)) return; - ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, - (sc->sc_flags & SC_OP_LED_ON) ? 1 : 0); + + if ((sc->led_on_duration == ATH_LED_ON_DURATION_IDLE) || + (sc->led_off_duration == ATH_LED_OFF_DURATION_IDLE)) + ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 0); + else + ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, + (sc->sc_flags & SC_OP_LED_ON) ? 1 : 0); queue_delayed_work(sc->hw->workqueue, &sc->ath_led_blink_work, (sc->sc_flags & SC_OP_LED_ON) ? msecs_to_jiffies(sc->led_off_duration) : msecs_to_jiffies(sc->led_on_duration)); - sc->led_on_duration = - max((ATH_LED_ON_DURATION_IDLE - sc->led_on_cnt), 25); - sc->led_off_duration = - max((ATH_LED_OFF_DURATION_IDLE - sc->led_off_cnt), 10); + sc->led_on_duration = sc->led_on_cnt ? + max((ATH_LED_ON_DURATION_IDLE - sc->led_on_cnt), 25) : + ATH_LED_ON_DURATION_IDLE; + sc->led_off_duration = sc->led_off_cnt ? + max((ATH_LED_OFF_DURATION_IDLE - sc->led_off_cnt), 10) : + ATH_LED_OFF_DURATION_IDLE; sc->led_on_cnt = sc->led_off_cnt = 0; if (sc->sc_flags & SC_OP_LED_ON) sc->sc_flags &= ~SC_OP_LED_ON; -- cgit v1.2.3 From 3f46b29cd8caa35fcbc46e254a5abeee4e0e9e2f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 14 Mar 2009 19:10:51 +0100 Subject: ieee80211: document DS bit usage I keep needing this because I'm too stupid to remember it. Everybody else can probably remember, but who knows :) Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index b1bb817d142..382387e75b8 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -18,6 +18,22 @@ #include #include +/* + * DS bit usage + * + * TA = transmitter address + * RA = receiver address + * DA = destination address + * SA = source address + * + * ToDS FromDS A1(RA) A2(TA) A3 A4 Use + * ----------------------------------------------------------------- + * 0 0 DA SA BSSID - IBSS/DLS + * 0 1 DA BSSID SA - AP -> STA + * 1 0 BSSID SA DA - AP <- STA + * 1 1 RA TA DA SA unspecified (WDS) + */ + #define FCS_LEN 4 #define IEEE80211_FCTL_VERS 0x0003 -- cgit v1.2.3 From 504f365554a7f543fcd706878ff9edf785be7614 Mon Sep 17 00:00:00 2001 From: Nick Kossifidis Date: Sun, 15 Mar 2009 22:13:39 +0200 Subject: ath5k: Choose the right initvals for RF2425 * Fix a typo in initvals.c so that we use the RF2425 array for RF2425 and not RF2413 Note: This also fixes incorect pd gain overlap since RF2425 has different pd gain overlap from RF2413 Signed-off-by: Nick Kossifidis Acked-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/initvals.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath5k/initvals.c b/drivers/net/wireless/ath5k/initvals.c index 44886434187..61fb621ed20 100644 --- a/drivers/net/wireless/ath5k/initvals.c +++ b/drivers/net/wireless/ath5k/initvals.c @@ -1510,8 +1510,8 @@ int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool change_channel) rf2425_ini_mode_end, mode); ath5k_hw_ini_registers(ah, - ARRAY_SIZE(rf2413_ini_common_end), - rf2413_ini_common_end, change_channel); + ARRAY_SIZE(rf2425_ini_common_end), + rf2425_ini_common_end, change_channel); ath5k_hw_ini_registers(ah, ARRAY_SIZE(rf5112_ini_bbgain), -- cgit v1.2.3 From 8e218fb24faef0bfe95bc91b3c05261e20439527 Mon Sep 17 00:00:00 2001 From: Nick Kossifidis Date: Sun, 15 Mar 2009 22:17:04 +0200 Subject: ath5k: Convert chip specific calibration data to a generic format * Convert chip specific calibration data to a generic format common for all chips Note: We scale up power to be in 0.25dB units for all chips for compatibility with RF5112 v2: Address Bob's and Jiri's comments Signed-off-by: Nick Kossifidis Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/eeprom.c | 774 +++++++++++++++++++++++++----------- drivers/net/wireless/ath5k/eeprom.h | 128 ++++-- 2 files changed, 629 insertions(+), 273 deletions(-) diff --git a/drivers/net/wireless/ath5k/eeprom.c b/drivers/net/wireless/ath5k/eeprom.c index ac45ca47ca8..c0fb3b09ba4 100644 --- a/drivers/net/wireless/ath5k/eeprom.c +++ b/drivers/net/wireless/ath5k/eeprom.c @@ -1,7 +1,7 @@ /* * Copyright (c) 2004-2008 Reyk Floeter - * Copyright (c) 2006-2008 Nick Kossifidis - * Copyright (c) 2008 Felix Fietkau + * Copyright (c) 2006-2009 Nick Kossifidis + * Copyright (c) 2008-2009 Felix Fietkau * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -98,11 +98,6 @@ ath5k_eeprom_init_header(struct ath5k_hw *ah) int ret; u16 val; - /* Initial TX thermal adjustment values */ - ee->ee_tx_clip = 4; - ee->ee_pwd_84 = ee->ee_pwd_90 = 1; - ee->ee_gain_select = 1; - /* * Read values from EEPROM and store them in the capability structure */ @@ -241,22 +236,22 @@ static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset, ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff); switch(mode) { case AR5K_EEPROM_MODE_11A: - ee->ee_ob[mode][3] = (val >> 5) & 0x7; - ee->ee_db[mode][3] = (val >> 2) & 0x7; - ee->ee_ob[mode][2] = (val << 1) & 0x7; + ee->ee_ob[mode][3] = (val >> 5) & 0x7; + ee->ee_db[mode][3] = (val >> 2) & 0x7; + ee->ee_ob[mode][2] = (val << 1) & 0x7; AR5K_EEPROM_READ(o++, val); - ee->ee_ob[mode][2] |= (val >> 15) & 0x1; - ee->ee_db[mode][2] = (val >> 12) & 0x7; - ee->ee_ob[mode][1] = (val >> 9) & 0x7; - ee->ee_db[mode][1] = (val >> 6) & 0x7; - ee->ee_ob[mode][0] = (val >> 3) & 0x7; - ee->ee_db[mode][0] = val & 0x7; + ee->ee_ob[mode][2] |= (val >> 15) & 0x1; + ee->ee_db[mode][2] = (val >> 12) & 0x7; + ee->ee_ob[mode][1] = (val >> 9) & 0x7; + ee->ee_db[mode][1] = (val >> 6) & 0x7; + ee->ee_ob[mode][0] = (val >> 3) & 0x7; + ee->ee_db[mode][0] = val & 0x7; break; case AR5K_EEPROM_MODE_11G: case AR5K_EEPROM_MODE_11B: - ee->ee_ob[mode][1] = (val >> 4) & 0x7; - ee->ee_db[mode][1] = val & 0x7; + ee->ee_ob[mode][1] = (val >> 4) & 0x7; + ee->ee_db[mode][1] = val & 0x7; break; } @@ -504,35 +499,6 @@ ath5k_eeprom_init_modes(struct ath5k_hw *ah) return 0; } -/* Used to match PCDAC steps with power values on RF5111 chips - * (eeprom versions < 4). For RF5111 we have 10 pre-defined PCDAC - * steps that match with the power values we read from eeprom. On - * older eeprom versions (< 3.2) these steps are equaly spaced at - * 10% of the pcdac curve -until the curve reaches it's maximum- - * (10 steps from 0 to 100%) but on newer eeprom versions (>= 3.2) - * these 10 steps are spaced in a different way. This function returns - * the pcdac steps based on eeprom version and curve min/max so that we - * can have pcdac/pwr points. - */ -static inline void -ath5k_get_pcdac_intercepts(struct ath5k_hw *ah, u8 min, u8 max, u8 *vp) -{ - static const u16 intercepts3[] = - { 0, 5, 10, 20, 30, 50, 70, 85, 90, 95, 100 }; - static const u16 intercepts3_2[] = - { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 }; - const u16 *ip; - int i; - - if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_3_2) - ip = intercepts3_2; - else - ip = intercepts3; - - for (i = 0; i < ARRAY_SIZE(intercepts3); i++) - *vp++ = (ip[i] * max + (100 - ip[i]) * min) / 100; -} - /* Read the frequency piers for each mode (mostly used on newer eeproms with 0xff * frequency mask) */ static inline int @@ -546,26 +512,25 @@ ath5k_eeprom_read_freq_list(struct ath5k_hw *ah, int *offset, int max, int ret; u16 val; + ee->ee_n_piers[mode] = 0; while(i < max) { AR5K_EEPROM_READ(o++, val); - freq1 = (val >> 8) & 0xff; - freq2 = val & 0xff; - - if (freq1) { - pc[i++].freq = ath5k_eeprom_bin2freq(ee, - freq1, mode); - ee->ee_n_piers[mode]++; - } + freq1 = val & 0xff; + if (!freq1) + break; - if (freq2) { - pc[i++].freq = ath5k_eeprom_bin2freq(ee, - freq2, mode); - ee->ee_n_piers[mode]++; - } + pc[i++].freq = ath5k_eeprom_bin2freq(ee, + freq1, mode); + ee->ee_n_piers[mode]++; - if (!freq1 || !freq2) + freq2 = (val >> 8) & 0xff; + if (!freq2) break; + + pc[i++].freq = ath5k_eeprom_bin2freq(ee, + freq2, mode); + ee->ee_n_piers[mode]++; } /* return new offset */ @@ -652,13 +617,122 @@ ath5k_eeprom_init_11bg_2413(struct ath5k_hw *ah, unsigned int mode, int offset) return 0; } -/* Read power calibration for RF5111 chips +/* + * Read power calibration for RF5111 chips + * * For RF5111 we have an XPD -eXternal Power Detector- curve - * for each calibrated channel. Each curve has PCDAC steps on - * x axis and power on y axis and looks like a logarithmic - * function. To recreate the curve and pass the power values - * on the pcdac table, we read 10 points here and interpolate later. + * for each calibrated channel. Each curve has 0,5dB Power steps + * on x axis and PCDAC steps (offsets) on y axis and looks like an + * exponential function. To recreate the curve we read 11 points + * here and interpolate later. */ + +/* Used to match PCDAC steps with power values on RF5111 chips + * (eeprom versions < 4). For RF5111 we have 11 pre-defined PCDAC + * steps that match with the power values we read from eeprom. On + * older eeprom versions (< 3.2) these steps are equaly spaced at + * 10% of the pcdac curve -until the curve reaches it's maximum- + * (11 steps from 0 to 100%) but on newer eeprom versions (>= 3.2) + * these 11 steps are spaced in a different way. This function returns + * the pcdac steps based on eeprom version and curve min/max so that we + * can have pcdac/pwr points. + */ +static inline void +ath5k_get_pcdac_intercepts(struct ath5k_hw *ah, u8 min, u8 max, u8 *vp) +{ + const static u16 intercepts3[] = + { 0, 5, 10, 20, 30, 50, 70, 85, 90, 95, 100 }; + const static u16 intercepts3_2[] = + { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 }; + const u16 *ip; + int i; + + if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_3_2) + ip = intercepts3_2; + else + ip = intercepts3; + + for (i = 0; i < ARRAY_SIZE(intercepts3); i++) + vp[i] = (ip[i] * max + (100 - ip[i]) * min) / 100; +} + +/* Convert RF5111 specific data to generic raw data + * used by interpolation code */ +static int +ath5k_eeprom_convert_pcal_info_5111(struct ath5k_hw *ah, int mode, + struct ath5k_chan_pcal_info *chinfo) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_chan_pcal_info_rf5111 *pcinfo; + struct ath5k_pdgain_info *pd; + u8 pier, point, idx; + u8 *pdgain_idx = ee->ee_pdc_to_idx[mode]; + + /* Fill raw data for each calibration pier */ + for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) { + + pcinfo = &chinfo[pier].rf5111_info; + + /* Allocate pd_curves for this cal pier */ + chinfo[pier].pd_curves = + kcalloc(AR5K_EEPROM_N_PD_CURVES, + sizeof(struct ath5k_pdgain_info), + GFP_KERNEL); + + if (!chinfo[pier].pd_curves) + return -ENOMEM; + + /* Only one curve for RF5111 + * find out which one and place + * in in pd_curves. + * Note: ee_x_gain is reversed here */ + for (idx = 0; idx < AR5K_EEPROM_N_PD_CURVES; idx++) { + + if (!((ee->ee_x_gain[mode] >> idx) & 0x1)) { + pdgain_idx[0] = idx; + break; + } + } + + ee->ee_pd_gains[mode] = 1; + + pd = &chinfo[pier].pd_curves[idx]; + + pd->pd_points = AR5K_EEPROM_N_PWR_POINTS_5111; + + /* Allocate pd points for this curve */ + pd->pd_step = kcalloc(AR5K_EEPROM_N_PWR_POINTS_5111, + sizeof(u8), GFP_KERNEL); + if (!pd->pd_step) + return -ENOMEM; + + pd->pd_pwr = kcalloc(AR5K_EEPROM_N_PWR_POINTS_5111, + sizeof(s16), GFP_KERNEL); + if (!pd->pd_pwr) + return -ENOMEM; + + /* Fill raw dataset + * (convert power to 0.25dB units + * for RF5112 combatibility) */ + for (point = 0; point < pd->pd_points; point++) { + + /* Absolute values */ + pd->pd_pwr[point] = 2 * pcinfo->pwr[point]; + + /* Already sorted */ + pd->pd_step[point] = pcinfo->pcdac[point]; + } + + /* Set min/max pwr */ + chinfo[pier].min_pwr = pd->pd_pwr[0]; + chinfo[pier].max_pwr = pd->pd_pwr[10]; + + } + + return 0; +} + +/* Parse EEPROM data */ static int ath5k_eeprom_read_pcal_info_5111(struct ath5k_hw *ah, int mode) { @@ -747,30 +821,165 @@ ath5k_eeprom_read_pcal_info_5111(struct ath5k_hw *ah, int mode) cdata->pcdac_max, cdata->pcdac); } - return 0; + return ath5k_eeprom_convert_pcal_info_5111(ah, mode, pcal); } -/* Read power calibration for RF5112 chips + +/* + * Read power calibration for RF5112 chips + * * For RF5112 we have 4 XPD -eXternal Power Detector- curves * for each calibrated channel on 0, -6, -12 and -18dbm but we only - * use the higher (3) and the lower (0) curves. Each curve has PCDAC - * steps on x axis and power on y axis and looks like a linear - * function. To recreate the curve and pass the power values - * on the pcdac table, we read 4 points for xpd 0 and 3 points - * for xpd 3 here and interpolate later. + * use the higher (3) and the lower (0) curves. Each curve has 0.5dB + * power steps on x axis and PCDAC steps on y axis and looks like a + * linear function. To recreate the curve and pass the power values + * on hw, we read 4 points for xpd 0 (lower gain -> max power) + * and 3 points for xpd 3 (higher gain -> lower power) here and + * interpolate later. * * Note: Many vendors just use xpd 0 so xpd 3 is zeroed. */ + +/* Convert RF5112 specific data to generic raw data + * used by interpolation code */ +static int +ath5k_eeprom_convert_pcal_info_5112(struct ath5k_hw *ah, int mode, + struct ath5k_chan_pcal_info *chinfo) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_chan_pcal_info_rf5112 *pcinfo; + u8 *pdgain_idx = ee->ee_pdc_to_idx[mode]; + unsigned int pier, pdg, point; + + /* Fill raw data for each calibration pier */ + for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) { + + pcinfo = &chinfo[pier].rf5112_info; + + /* Allocate pd_curves for this cal pier */ + chinfo[pier].pd_curves = + kcalloc(AR5K_EEPROM_N_PD_CURVES, + sizeof(struct ath5k_pdgain_info), + GFP_KERNEL); + + if (!chinfo[pier].pd_curves) + return -ENOMEM; + + /* Fill pd_curves */ + for (pdg = 0; pdg < ee->ee_pd_gains[mode]; pdg++) { + + u8 idx = pdgain_idx[pdg]; + struct ath5k_pdgain_info *pd = + &chinfo[pier].pd_curves[idx]; + + /* Lowest gain curve (max power) */ + if (pdg == 0) { + /* One more point for better accuracy */ + pd->pd_points = AR5K_EEPROM_N_XPD0_POINTS; + + /* Allocate pd points for this curve */ + pd->pd_step = kcalloc(pd->pd_points, + sizeof(u8), GFP_KERNEL); + + if (!pd->pd_step) + return -ENOMEM; + + pd->pd_pwr = kcalloc(pd->pd_points, + sizeof(s16), GFP_KERNEL); + + if (!pd->pd_pwr) + return -ENOMEM; + + + /* Fill raw dataset + * (all power levels are in 0.25dB units) */ + pd->pd_step[0] = pcinfo->pcdac_x0[0]; + pd->pd_pwr[0] = pcinfo->pwr_x0[0]; + + for (point = 1; point < pd->pd_points; + point++) { + /* Absolute values */ + pd->pd_pwr[point] = + pcinfo->pwr_x0[point]; + + /* Deltas */ + pd->pd_step[point] = + pd->pd_step[point - 1] + + pcinfo->pcdac_x0[point]; + } + + /* Set min power for this frequency */ + chinfo[pier].min_pwr = pd->pd_pwr[0]; + + /* Highest gain curve (min power) */ + } else if (pdg == 1) { + + pd->pd_points = AR5K_EEPROM_N_XPD3_POINTS; + + /* Allocate pd points for this curve */ + pd->pd_step = kcalloc(pd->pd_points, + sizeof(u8), GFP_KERNEL); + + if (!pd->pd_step) + return -ENOMEM; + + pd->pd_pwr = kcalloc(pd->pd_points, + sizeof(s16), GFP_KERNEL); + + if (!pd->pd_pwr) + return -ENOMEM; + + /* Fill raw dataset + * (all power levels are in 0.25dB units) */ + for (point = 0; point < pd->pd_points; + point++) { + /* Absolute values */ + pd->pd_pwr[point] = + pcinfo->pwr_x3[point]; + + /* Fixed points */ + pd->pd_step[point] = + pcinfo->pcdac_x3[point]; + } + + /* Since we have a higher gain curve + * override min power */ + chinfo[pier].min_pwr = pd->pd_pwr[0]; + } + } + } + + return 0; +} + +/* Parse EEPROM data */ static int ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; struct ath5k_chan_pcal_info_rf5112 *chan_pcal_info; struct ath5k_chan_pcal_info *gen_chan_info; + u8 *pdgain_idx = ee->ee_pdc_to_idx[mode]; u32 offset; - unsigned int i, c; + u8 i, c; u16 val; int ret; + u8 pd_gains = 0; + + /* Count how many curves we have and + * identify them (which one of the 4 + * available curves we have on each count). + * Curves are stored from lower (x0) to + * higher (x3) gain */ + for (i = 0; i < AR5K_EEPROM_N_PD_CURVES; i++) { + /* ee_x_gain[mode] is x gain mask */ + if ((ee->ee_x_gain[mode] >> i) & 0x1) + pdgain_idx[pd_gains++] = i; + } + ee->ee_pd_gains[mode] = pd_gains; + + if (pd_gains == 0 || pd_gains > 2) + return -EINVAL; switch (mode) { case AR5K_EEPROM_MODE_11A: @@ -808,13 +1017,13 @@ ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode) for (i = 0; i < ee->ee_n_piers[mode]; i++) { chan_pcal_info = &gen_chan_info[i].rf5112_info; - /* Power values in dBm * 4 + /* Power values in quarter dB * for the lower xpd gain curve * (0 dBm -> higher output power) */ for (c = 0; c < AR5K_EEPROM_N_XPD0_POINTS; c++) { AR5K_EEPROM_READ(offset++, val); - chan_pcal_info->pwr_x0[c] = (val & 0xff); - chan_pcal_info->pwr_x0[++c] = ((val >> 8) & 0xff); + chan_pcal_info->pwr_x0[c] = (s8) (val & 0xff); + chan_pcal_info->pwr_x0[++c] = (s8) ((val >> 8) & 0xff); } /* PCDAC steps @@ -825,12 +1034,12 @@ ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode) chan_pcal_info->pcdac_x0[2] = ((val >> 5) & 0x1f); chan_pcal_info->pcdac_x0[3] = ((val >> 10) & 0x1f); - /* Power values in dBm * 4 + /* Power values in quarter dB * for the higher xpd gain curve * (18 dBm -> lower output power) */ AR5K_EEPROM_READ(offset++, val); - chan_pcal_info->pwr_x3[0] = (val & 0xff); - chan_pcal_info->pwr_x3[1] = ((val >> 8) & 0xff); + chan_pcal_info->pwr_x3[0] = (s8) (val & 0xff); + chan_pcal_info->pwr_x3[1] = (s8) ((val >> 8) & 0xff); AR5K_EEPROM_READ(offset++, val); chan_pcal_info->pwr_x3[2] = (val & 0xff); @@ -843,24 +1052,36 @@ ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode) chan_pcal_info->pcdac_x3[2] = 63; if (ee->ee_version >= AR5K_EEPROM_VERSION_4_3) { - chan_pcal_info->pcdac_x0[0] = ((val >> 8) & 0xff); + chan_pcal_info->pcdac_x0[0] = ((val >> 8) & 0x3f); /* Last xpd0 power level is also channel maximum */ gen_chan_info[i].max_pwr = chan_pcal_info->pwr_x0[3]; } else { chan_pcal_info->pcdac_x0[0] = 1; - gen_chan_info[i].max_pwr = ((val >> 8) & 0xff); + gen_chan_info[i].max_pwr = (s8) ((val >> 8) & 0xff); } - /* Recreate pcdac_x0 table for this channel using pcdac steps */ - chan_pcal_info->pcdac_x0[1] += chan_pcal_info->pcdac_x0[0]; - chan_pcal_info->pcdac_x0[2] += chan_pcal_info->pcdac_x0[1]; - chan_pcal_info->pcdac_x0[3] += chan_pcal_info->pcdac_x0[2]; } - return 0; + return ath5k_eeprom_convert_pcal_info_5112(ah, mode, gen_chan_info); } + +/* + * Read power calibration for RF2413 chips + * + * For RF2413 we have a Power to PDDAC table (Power Detector) + * instead of a PCDAC and 4 pd gain curves for each calibrated channel. + * Each curve has power on x axis in 0.5 db steps and PDDADC steps on y + * axis and looks like an exponential function like the RF5111 curve. + * + * To recreate the curves we read here the points and interpolate + * later. Note that in most cases only 2 (higher and lower) curves are + * used (like RF5112) but vendors have the oportunity to include all + * 4 curves on eeprom. The final curve (higher power) has an extra + * point for better accuracy like RF5112. + */ + /* For RF2413 power calibration data doesn't start on a fixed location and * if a mode is not supported, it's section is missing -not zeroed-. * So we need to calculate the starting offset for each section by using @@ -890,13 +1111,15 @@ ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode) switch(mode) { case AR5K_EEPROM_MODE_11G: if (AR5K_EEPROM_HDR_11B(ee->ee_header)) - offset += ath5k_pdgains_size_2413(ee, AR5K_EEPROM_MODE_11B) + - AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2; + offset += ath5k_pdgains_size_2413(ee, + AR5K_EEPROM_MODE_11B) + + AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2; /* fall through */ case AR5K_EEPROM_MODE_11B: if (AR5K_EEPROM_HDR_11A(ee->ee_header)) - offset += ath5k_pdgains_size_2413(ee, AR5K_EEPROM_MODE_11A) + - AR5K_EEPROM_N_5GHZ_CHAN / 2; + offset += ath5k_pdgains_size_2413(ee, + AR5K_EEPROM_MODE_11A) + + AR5K_EEPROM_N_5GHZ_CHAN / 2; /* fall through */ case AR5K_EEPROM_MODE_11A: break; @@ -907,37 +1130,118 @@ ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode) return offset; } -/* Read power calibration for RF2413 chips - * For RF2413 we have a PDDAC table (Power Detector) instead - * of a PCDAC and 4 pd gain curves for each calibrated channel. - * Each curve has PDDAC steps on x axis and power on y axis and - * looks like an exponential function. To recreate the curves - * we read here the points and interpolate later. Note that - * in most cases only higher and lower curves are used (like - * RF5112) but vendors have the oportunity to include all 4 - * curves on eeprom. The final curve (higher power) has an extra - * point for better accuracy like RF5112. - */ +/* Convert RF2413 specific data to generic raw data + * used by interpolation code */ +static int +ath5k_eeprom_convert_pcal_info_2413(struct ath5k_hw *ah, int mode, + struct ath5k_chan_pcal_info *chinfo) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_chan_pcal_info_rf2413 *pcinfo; + u8 *pdgain_idx = ee->ee_pdc_to_idx[mode]; + unsigned int pier, pdg, point; + + /* Fill raw data for each calibration pier */ + for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) { + + pcinfo = &chinfo[pier].rf2413_info; + + /* Allocate pd_curves for this cal pier */ + chinfo[pier].pd_curves = + kcalloc(AR5K_EEPROM_N_PD_CURVES, + sizeof(struct ath5k_pdgain_info), + GFP_KERNEL); + + if (!chinfo[pier].pd_curves) + return -ENOMEM; + + /* Fill pd_curves */ + for (pdg = 0; pdg < ee->ee_pd_gains[mode]; pdg++) { + + u8 idx = pdgain_idx[pdg]; + struct ath5k_pdgain_info *pd = + &chinfo[pier].pd_curves[idx]; + + /* One more point for the highest power + * curve (lowest gain) */ + if (pdg == ee->ee_pd_gains[mode] - 1) + pd->pd_points = AR5K_EEPROM_N_PD_POINTS; + else + pd->pd_points = AR5K_EEPROM_N_PD_POINTS - 1; + + /* Allocate pd points for this curve */ + pd->pd_step = kcalloc(pd->pd_points, + sizeof(u8), GFP_KERNEL); + + if (!pd->pd_step) + return -ENOMEM; + + pd->pd_pwr = kcalloc(pd->pd_points, + sizeof(s16), GFP_KERNEL); + + if (!pd->pd_pwr) + return -ENOMEM; + + /* Fill raw dataset + * convert all pwr levels to + * quarter dB for RF5112 combatibility */ + pd->pd_step[0] = pcinfo->pddac_i[pdg]; + pd->pd_pwr[0] = 4 * pcinfo->pwr_i[pdg]; + + for (point = 1; point < pd->pd_points; point++) { + + pd->pd_pwr[point] = pd->pd_pwr[point - 1] + + 2 * pcinfo->pwr[pdg][point - 1]; + + pd->pd_step[point] = pd->pd_step[point - 1] + + pcinfo->pddac[pdg][point - 1]; + + } + + /* Highest gain curve -> min power */ + if (pdg == 0) + chinfo[pier].min_pwr = pd->pd_pwr[0]; + + /* Lowest gain curve -> max power */ + if (pdg == ee->ee_pd_gains[mode] - 1) + chinfo[pier].max_pwr = + pd->pd_pwr[pd->pd_points - 1]; + } + } + + return 0; +} + +/* Parse EEPROM data */ static int ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; - struct ath5k_chan_pcal_info_rf2413 *chan_pcal_info; - struct ath5k_chan_pcal_info *gen_chan_info; - unsigned int i, c; + struct ath5k_chan_pcal_info_rf2413 *pcinfo; + struct ath5k_chan_pcal_info *chinfo; + u8 *pdgain_idx = ee->ee_pdc_to_idx[mode]; u32 offset; - int ret; + int idx, i, ret; u16 val; u8 pd_gains = 0; - if (ee->ee_x_gain[mode] & 0x1) pd_gains++; - if ((ee->ee_x_gain[mode] >> 1) & 0x1) pd_gains++; - if ((ee->ee_x_gain[mode] >> 2) & 0x1) pd_gains++; - if ((ee->ee_x_gain[mode] >> 3) & 0x1) pd_gains++; + /* Count how many curves we have and + * identify them (which one of the 4 + * available curves we have on each count). + * Curves are stored from higher to + * lower gain so we go backwards */ + for (idx = AR5K_EEPROM_N_PD_CURVES - 1; idx >= 0; idx--) { + /* ee_x_gain[mode] is x gain mask */ + if ((ee->ee_x_gain[mode] >> idx) & 0x1) + pdgain_idx[pd_gains++] = idx; + + } ee->ee_pd_gains[mode] = pd_gains; + if (pd_gains == 0) + return -EINVAL; + offset = ath5k_cal_data_offset_2413(ee, mode); - ee->ee_n_piers[mode] = 0; switch (mode) { case AR5K_EEPROM_MODE_11A: if (!AR5K_EEPROM_HDR_11A(ee->ee_header)) @@ -945,7 +1249,7 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode) ath5k_eeprom_init_11a_pcal_freq(ah, offset); offset += AR5K_EEPROM_N_5GHZ_CHAN / 2; - gen_chan_info = ee->ee_pwr_cal_a; + chinfo = ee->ee_pwr_cal_a; break; case AR5K_EEPROM_MODE_11B: if (!AR5K_EEPROM_HDR_11B(ee->ee_header)) @@ -953,7 +1257,7 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode) ath5k_eeprom_init_11bg_2413(ah, mode, offset); offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2; - gen_chan_info = ee->ee_pwr_cal_b; + chinfo = ee->ee_pwr_cal_b; break; case AR5K_EEPROM_MODE_11G: if (!AR5K_EEPROM_HDR_11G(ee->ee_header)) @@ -961,41 +1265,35 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode) ath5k_eeprom_init_11bg_2413(ah, mode, offset); offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2; - gen_chan_info = ee->ee_pwr_cal_g; + chinfo = ee->ee_pwr_cal_g; break; default: return -EINVAL; } - if (pd_gains == 0) - return 0; - for (i = 0; i < ee->ee_n_piers[mode]; i++) { - chan_pcal_info = &gen_chan_info[i].rf2413_info; + pcinfo = &chinfo[i].rf2413_info; /* * Read pwr_i, pddac_i and the first * 2 pd points (pwr, pddac) */ AR5K_EEPROM_READ(offset++, val); - chan_pcal_info->pwr_i[0] = val & 0x1f; - chan_pcal_info->pddac_i[0] = (val >> 5) & 0x7f; - chan_pcal_info->pwr[0][0] = - (val >> 12) & 0xf; + pcinfo->pwr_i[0] = val & 0x1f; + pcinfo->pddac_i[0] = (val >> 5) & 0x7f; + pcinfo->pwr[0][0] = (val >> 12) & 0xf; AR5K_EEPROM_READ(offset++, val); - chan_pcal_info->pddac[0][0] = val & 0x3f; - chan_pcal_info->pwr[0][1] = (val >> 6) & 0xf; - chan_pcal_info->pddac[0][1] = - (val >> 10) & 0x3f; + pcinfo->pddac[0][0] = val & 0x3f; + pcinfo->pwr[0][1] = (val >> 6) & 0xf; + pcinfo->pddac[0][1] = (val >> 10) & 0x3f; AR5K_EEPROM_READ(offset++, val); - chan_pcal_info->pwr[0][2] = val & 0xf; - chan_pcal_info->pddac[0][2] = - (val >> 4) & 0x3f; + pcinfo->pwr[0][2] = val & 0xf; + pcinfo->pddac[0][2] = (val >> 4) & 0x3f; - chan_pcal_info->pwr[0][3] = 0; - chan_pcal_info->pddac[0][3] = 0; + pcinfo->pwr[0][3] = 0; + pcinfo->pddac[0][3] = 0; if (pd_gains > 1) { /* @@ -1003,44 +1301,36 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode) * so it only has 2 pd points. * Continue wih pd gain 1. */ - chan_pcal_info->pwr_i[1] = (val >> 10) & 0x1f; + pcinfo->pwr_i[1] = (val >> 10) & 0x1f; - chan_pcal_info->pddac_i[1] = (val >> 15) & 0x1; + pcinfo->pddac_i[1] = (val >> 15) & 0x1; AR5K_EEPROM_READ(offset++, val); - chan_pcal_info->pddac_i[1] |= (val & 0x3F) << 1; + pcinfo->pddac_i[1] |= (val & 0x3F) << 1; - chan_pcal_info->pwr[1][0] = (val >> 6) & 0xf; - chan_pcal_info->pddac[1][0] = - (val >> 10) & 0x3f; + pcinfo->pwr[1][0] = (val >> 6) & 0xf; + pcinfo->pddac[1][0] = (val >> 10) & 0x3f; AR5K_EEPROM_READ(offset++, val); - chan_pcal_info->pwr[1][1] = val & 0xf; - chan_pcal_info->pddac[1][1] = - (val >> 4) & 0x3f; - chan_pcal_info->pwr[1][2] = - (val >> 10) & 0xf; - - chan_pcal_info->pddac[1][2] = - (val >> 14) & 0x3; + pcinfo->pwr[1][1] = val & 0xf; + pcinfo->pddac[1][1] = (val >> 4) & 0x3f; + pcinfo->pwr[1][2] = (val >> 10) & 0xf; + + pcinfo->pddac[1][2] = (val >> 14) & 0x3; AR5K_EEPROM_READ(offset++, val); - chan_pcal_info->pddac[1][2] |= - (val & 0xF) << 2; + pcinfo->pddac[1][2] |= (val & 0xF) << 2; - chan_pcal_info->pwr[1][3] = 0; - chan_pcal_info->pddac[1][3] = 0; + pcinfo->pwr[1][3] = 0; + pcinfo->pddac[1][3] = 0; } else if (pd_gains == 1) { /* * Pd gain 0 is the last one so * read the extra point. */ - chan_pcal_info->pwr[0][3] = - (val >> 10) & 0xf; + pcinfo->pwr[0][3] = (val >> 10) & 0xf; - chan_pcal_info->pddac[0][3] = - (val >> 14) & 0x3; + pcinfo->pddac[0][3] = (val >> 14) & 0x3; AR5K_EEPROM_READ(offset++, val); - chan_pcal_info->pddac[0][3] |= - (val & 0xF) << 2; + pcinfo->pddac[0][3] |= (val & 0xF) << 2; } /* @@ -1048,105 +1338,65 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode) * as above. */ if (pd_gains > 2) { - chan_pcal_info->pwr_i[2] = (val >> 4) & 0x1f; - chan_pcal_info->pddac_i[2] = (val >> 9) & 0x7f; + pcinfo->pwr_i[2] = (val >> 4) & 0x1f; + pcinfo->pddac_i[2] = (val >> 9) & 0x7f; AR5K_EEPROM_READ(offset++, val); - chan_pcal_info->pwr[2][0] = - (val >> 0) & 0xf; - chan_pcal_info->pddac[2][0] = - (val >> 4) & 0x3f; - chan_pcal_info->pwr[2][1] = - (val >> 10) & 0xf; - - chan_pcal_info->pddac[2][1] = - (val >> 14) & 0x3; + pcinfo->pwr[2][0] = (val >> 0) & 0xf; + pcinfo->pddac[2][0] = (val >> 4) & 0x3f; + pcinfo->pwr[2][1] = (val >> 10) & 0xf; + + pcinfo->pddac[2][1] = (val >> 14) & 0x3; AR5K_EEPROM_READ(offset++, val); - chan_pcal_info->pddac[2][1] |= - (val & 0xF) << 2; + pcinfo->pddac[2][1] |= (val & 0xF) << 2; - chan_pcal_info->pwr[2][2] = - (val >> 4) & 0xf; - chan_pcal_info->pddac[2][2] = - (val >> 8) & 0x3f; + pcinfo->pwr[2][2] = (val >> 4) & 0xf; + pcinfo->pddac[2][2] = (val >> 8) & 0x3f; - chan_pcal_info->pwr[2][3] = 0; - chan_pcal_info->pddac[2][3] = 0; + pcinfo->pwr[2][3] = 0; + pcinfo->pddac[2][3] = 0; } else if (pd_gains == 2) { - chan_pcal_info->pwr[1][3] = - (val >> 4) & 0xf; - chan_pcal_info->pddac[1][3] = - (val >> 8) & 0x3f; + pcinfo->pwr[1][3] = (val >> 4) & 0xf; + pcinfo->pddac[1][3] = (val >> 8) & 0x3f; } if (pd_gains > 3) { - chan_pcal_info->pwr_i[3] = (val >> 14) & 0x3; + pcinfo->pwr_i[3] = (val >> 14) & 0x3; AR5K_EEPROM_READ(offset++, val); - chan_pcal_info->pwr_i[3] |= ((val >> 0) & 0x7) << 2; + pcinfo->pwr_i[3] |= ((val >> 0) & 0x7) << 2; - chan_pcal_info->pddac_i[3] = (val >> 3) & 0x7f; - chan_pcal_info->pwr[3][0] = - (val >> 10) & 0xf; - chan_pcal_info->pddac[3][0] = - (val >> 14) & 0x3; + pcinfo->pddac_i[3] = (val >> 3) & 0x7f; + pcinfo->pwr[3][0] = (val >> 10) & 0xf; + pcinfo->pddac[3][0] = (val >> 14) & 0x3; AR5K_EEPROM_READ(offset++, val); - chan_pcal_info->pddac[3][0] |= - (val & 0xF) << 2; - chan_pcal_info->pwr[3][1] = - (val >> 4) & 0xf; - chan_pcal_info->pddac[3][1] = - (val >> 8) & 0x3f; - - chan_pcal_info->pwr[3][2] = - (val >> 14) & 0x3; + pcinfo->pddac[3][0] |= (val & 0xF) << 2; + pcinfo->pwr[3][1] = (val >> 4) & 0xf; + pcinfo->pddac[3][1] = (val >> 8) & 0x3f; + + pcinfo->pwr[3][2] = (val >> 14) & 0x3; AR5K_EEPROM_READ(offset++, val); - chan_pcal_info->pwr[3][2] |= - ((val >> 0) & 0x3) << 2; + pcinfo->pwr[3][2] |= ((val >> 0) & 0x3) << 2; - chan_pcal_info->pddac[3][2] = - (val >> 2) & 0x3f; - chan_pcal_info->pwr[3][3] = - (val >> 8) & 0xf; + pcinfo->pddac[3][2] = (val >> 2) & 0x3f; + pcinfo->pwr[3][3] = (val >> 8) & 0xf; - chan_pcal_info->pddac[3][3] = - (val >> 12) & 0xF; + pcinfo->pddac[3][3] = (val >> 12) & 0xF; AR5K_EEPROM_READ(offset++, val); - chan_pcal_info->pddac[3][3] |= - ((val >> 0) & 0x3) << 4; + pcinfo->pddac[3][3] |= ((val >> 0) & 0x3) << 4; } else if (pd_gains == 3) { - chan_pcal_info->pwr[2][3] = - (val >> 14) & 0x3; + pcinfo->pwr[2][3] = (val >> 14) & 0x3; AR5K_EEPROM_READ(offset++, val); - chan_pcal_info->pwr[2][3] |= - ((val >> 0) & 0x3) << 2; - - chan_pcal_info->pddac[2][3] = - (val >> 2) & 0x3f; - } + pcinfo->pwr[2][3] |= ((val >> 0) & 0x3) << 2; - for (c = 0; c < pd_gains; c++) { - /* Recreate pwr table for this channel using pwr steps */ - chan_pcal_info->pwr[c][0] += chan_pcal_info->pwr_i[c] * 2; - chan_pcal_info->pwr[c][1] += chan_pcal_info->pwr[c][0]; - chan_pcal_info->pwr[c][2] += chan_pcal_info->pwr[c][1]; - chan_pcal_info->pwr[c][3] += chan_pcal_info->pwr[c][2]; - if (chan_pcal_info->pwr[c][3] == chan_pcal_info->pwr[c][2]) - chan_pcal_info->pwr[c][3] = 0; - - /* Recreate pddac table for this channel using pddac steps */ - chan_pcal_info->pddac[c][0] += chan_pcal_info->pddac_i[c]; - chan_pcal_info->pddac[c][1] += chan_pcal_info->pddac[c][0]; - chan_pcal_info->pddac[c][2] += chan_pcal_info->pddac[c][1]; - chan_pcal_info->pddac[c][3] += chan_pcal_info->pddac[c][2]; - if (chan_pcal_info->pddac[c][3] == chan_pcal_info->pddac[c][2]) - chan_pcal_info->pddac[c][3] = 0; + pcinfo->pddac[2][3] = (val >> 2) & 0x3f; } } - return 0; + return ath5k_eeprom_convert_pcal_info_2413(ah, mode, chinfo); } + /* * Read per rate target power (this is the maximum tx power * supported by the card). This info is used when setting @@ -1154,11 +1404,12 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode) * * This also works for v5 EEPROMs. */ -static int ath5k_eeprom_read_target_rate_pwr_info(struct ath5k_hw *ah, unsigned int mode) +static int +ath5k_eeprom_read_target_rate_pwr_info(struct ath5k_hw *ah, unsigned int mode) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; struct ath5k_rate_pcal_info *rate_pcal_info; - u16 *rate_target_pwr_num; + u8 *rate_target_pwr_num; u32 offset; u16 val; int ret, i; @@ -1264,7 +1515,9 @@ ath5k_eeprom_read_pcal_info(struct ath5k_hw *ah) else read_pcal = ath5k_eeprom_read_pcal_info_5111; - for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; mode++) { + + for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; + mode++) { err = read_pcal(ah, mode); if (err) return err; @@ -1277,6 +1530,62 @@ ath5k_eeprom_read_pcal_info(struct ath5k_hw *ah) return 0; } +static int +ath5k_eeprom_free_pcal_info(struct ath5k_hw *ah, int mode) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_chan_pcal_info *chinfo; + u8 pier, pdg; + + switch (mode) { + case AR5K_EEPROM_MODE_11A: + if (!AR5K_EEPROM_HDR_11A(ee->ee_header)) + return 0; + chinfo = ee->ee_pwr_cal_a; + break; + case AR5K_EEPROM_MODE_11B: + if (!AR5K_EEPROM_HDR_11B(ee->ee_header)) + return 0; + chinfo = ee->ee_pwr_cal_b; + break; + case AR5K_EEPROM_MODE_11G: + if (!AR5K_EEPROM_HDR_11G(ee->ee_header)) + return 0; + chinfo = ee->ee_pwr_cal_g; + break; + default: + return -EINVAL; + } + + for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) { + if (!chinfo[pier].pd_curves) + continue; + + for (pdg = 0; pdg < ee->ee_pd_gains[mode]; pdg++) { + struct ath5k_pdgain_info *pd = + &chinfo[pier].pd_curves[pdg]; + + if (pd != NULL) { + kfree(pd->pd_step); + kfree(pd->pd_pwr); + } + } + + kfree(chinfo[pier].pd_curves); + } + + return 0; +} + +void +ath5k_eeprom_detach(struct ath5k_hw *ah) +{ + u8 mode; + + for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; mode++) + ath5k_eeprom_free_pcal_info(ah, mode); +} + /* Read conformance test limits used for regulatory control */ static int ath5k_eeprom_read_ctl_info(struct ath5k_hw *ah) @@ -1457,3 +1766,4 @@ bool ath5k_eeprom_is_hb63(struct ath5k_hw *ah) else return false; } + diff --git a/drivers/net/wireless/ath5k/eeprom.h b/drivers/net/wireless/ath5k/eeprom.h index 1deebc0257d..b0c0606dea0 100644 --- a/drivers/net/wireless/ath5k/eeprom.h +++ b/drivers/net/wireless/ath5k/eeprom.h @@ -173,6 +173,7 @@ #define AR5K_EEPROM_N_5GHZ_CHAN 10 #define AR5K_EEPROM_N_2GHZ_CHAN 3 #define AR5K_EEPROM_N_2GHZ_CHAN_2413 4 +#define AR5K_EEPROM_N_2GHZ_CHAN_MAX 4 #define AR5K_EEPROM_MAX_CHAN 10 #define AR5K_EEPROM_N_PWR_POINTS_5111 11 #define AR5K_EEPROM_N_PCDAC 11 @@ -193,7 +194,7 @@ #define AR5K_EEPROM_SCALE_OC_DELTA(_x) (((_x) * 2) / 10) #define AR5K_EEPROM_N_CTLS(_v) AR5K_EEPROM_OFF(_v, 16, 32) #define AR5K_EEPROM_MAX_CTLS 32 -#define AR5K_EEPROM_N_XPD_PER_CHANNEL 4 +#define AR5K_EEPROM_N_PD_CURVES 4 #define AR5K_EEPROM_N_XPD0_POINTS 4 #define AR5K_EEPROM_N_XPD3_POINTS 3 #define AR5K_EEPROM_N_PD_GAINS 4 @@ -232,7 +233,7 @@ enum ath5k_ctl_mode { AR5K_CTL_11B = 1, AR5K_CTL_11G = 2, AR5K_CTL_TURBO = 3, - AR5K_CTL_108G = 4, + AR5K_CTL_TURBOG = 4, AR5K_CTL_2GHT20 = 5, AR5K_CTL_5GHT20 = 6, AR5K_CTL_2GHT40 = 7, @@ -240,65 +241,114 @@ enum ath5k_ctl_mode { AR5K_CTL_MODE_M = 15, }; +/* Default CTL ids for the 3 main reg domains. + * Atheros only uses these by default but vendors + * can have up to 32 different CTLs for different + * scenarios. Note that theese values are ORed with + * the mode id (above) so we can have up to 24 CTL + * datasets out of these 3 main regdomains. That leaves + * 8 ids that can be used by vendors and since 0x20 is + * missing from HAL sources i guess this is the set of + * custom CTLs vendors can use. */ +#define AR5K_CTL_FCC 0x10 +#define AR5K_CTL_CUSTOM 0x20 +#define AR5K_CTL_ETSI 0x30 +#define AR5K_CTL_MKK 0x40 + +/* Indicates a CTL with only mode set and + * no reg domain mapping, such CTLs are used + * for world roaming domains or simply when + * a reg domain is not set */ +#define AR5K_CTL_NO_REGDOMAIN 0xf0 + +/* Indicates an empty (invalid) CTL */ +#define AR5K_CTL_NO_CTL 0xff + /* Per channel calibration data, used for power table setup */ struct ath5k_chan_pcal_info_rf5111 { /* Power levels in half dbm units * for one power curve. */ - u8 pwr[AR5K_EEPROM_N_PWR_POINTS_5111]; + u8 pwr[AR5K_EEPROM_N_PWR_POINTS_5111]; /* PCDAC table steps * for the above values */ - u8 pcdac[AR5K_EEPROM_N_PWR_POINTS_5111]; + u8 pcdac[AR5K_EEPROM_N_PWR_POINTS_5111]; /* Starting PCDAC step */ - u8 pcdac_min; + u8 pcdac_min; /* Final PCDAC step */ - u8 pcdac_max; + u8 pcdac_max; }; struct ath5k_chan_pcal_info_rf5112 { /* Power levels in quarter dBm units * for lower (0) and higher (3) - * level curves */ - s8 pwr_x0[AR5K_EEPROM_N_XPD0_POINTS]; - s8 pwr_x3[AR5K_EEPROM_N_XPD3_POINTS]; + * level curves in 0.25dB units */ + s8 pwr_x0[AR5K_EEPROM_N_XPD0_POINTS]; + s8 pwr_x3[AR5K_EEPROM_N_XPD3_POINTS]; /* PCDAC table steps * for the above values */ - u8 pcdac_x0[AR5K_EEPROM_N_XPD0_POINTS]; - u8 pcdac_x3[AR5K_EEPROM_N_XPD3_POINTS]; + u8 pcdac_x0[AR5K_EEPROM_N_XPD0_POINTS]; + u8 pcdac_x3[AR5K_EEPROM_N_XPD3_POINTS]; }; struct ath5k_chan_pcal_info_rf2413 { /* Starting pwr/pddac values */ - s8 pwr_i[AR5K_EEPROM_N_PD_GAINS]; - u8 pddac_i[AR5K_EEPROM_N_PD_GAINS]; - /* (pwr,pddac) points */ - s8 pwr[AR5K_EEPROM_N_PD_GAINS] - [AR5K_EEPROM_N_PD_POINTS]; - u8 pddac[AR5K_EEPROM_N_PD_GAINS] - [AR5K_EEPROM_N_PD_POINTS]; + s8 pwr_i[AR5K_EEPROM_N_PD_GAINS]; + u8 pddac_i[AR5K_EEPROM_N_PD_GAINS]; + /* (pwr,pddac) points + * power levels in 0.5dB units */ + s8 pwr[AR5K_EEPROM_N_PD_GAINS] + [AR5K_EEPROM_N_PD_POINTS]; + u8 pddac[AR5K_EEPROM_N_PD_GAINS] + [AR5K_EEPROM_N_PD_POINTS]; +}; + +enum ath5k_powertable_type { + AR5K_PWRTABLE_PWR_TO_PCDAC = 0, + AR5K_PWRTABLE_LINEAR_PCDAC = 1, + AR5K_PWRTABLE_PWR_TO_PDADC = 2, +}; + +struct ath5k_pdgain_info { + u8 pd_points; + u8 *pd_step; + /* Power values are in + * 0.25dB units */ + s16 *pd_pwr; }; struct ath5k_chan_pcal_info { /* Frequency */ u16 freq; - /* Max available power */ - s8 max_pwr; + /* Tx power boundaries */ + s16 max_pwr; + s16 min_pwr; union { struct ath5k_chan_pcal_info_rf5111 rf5111_info; struct ath5k_chan_pcal_info_rf5112 rf5112_info; struct ath5k_chan_pcal_info_rf2413 rf2413_info; }; + /* Raw values used by phy code + * Curves are stored in order from lower + * gain to higher gain (max txpower -> min txpower) */ + struct ath5k_pdgain_info *pd_curves; }; -/* Per rate calibration data for each mode, used for power table setup */ +/* Per rate calibration data for each mode, + * used for rate power table setup. + * Note: Values in 0.5dB units */ struct ath5k_rate_pcal_info { u16 freq; /* Frequency */ - /* Power level for 6-24Mbit/s rates */ + /* Power level for 6-24Mbit/s rates or + * 1Mb rate */ u16 target_power_6to24; - /* Power level for 36Mbit rate */ + /* Power level for 36Mbit rate or + * 2Mb rate */ u16 target_power_36; - /* Power level for 48Mbit rate */ + /* Power level for 48Mbit rate or + * 5.5Mbit rate */ u16 target_power_48; - /* Power level for 54Mbit rate */ + /* Power level for 54Mbit rate or + * 11Mbit rate */ u16 target_power_54; }; @@ -330,12 +380,6 @@ struct ath5k_eeprom_info { u16 ee_cck_ofdm_power_delta; u16 ee_scaled_cck_delta; - /* Used for tx thermal adjustment (eeprom_init, rfregs) */ - u16 ee_tx_clip; - u16 ee_pwd_84; - u16 ee_pwd_90; - u16 ee_gain_select; - /* RF Calibration settings (reset, rfregs) */ u16 ee_i_cal[AR5K_EEPROM_N_MODES]; u16 ee_q_cal[AR5K_EEPROM_N_MODES]; @@ -363,23 +407,25 @@ struct ath5k_eeprom_info { /* Power calibration data */ u16 ee_false_detect[AR5K_EEPROM_N_MODES]; - /* Number of pd gain curves per mode (RF2413) */ - u8 ee_pd_gains[AR5K_EEPROM_N_MODES]; + /* Number of pd gain curves per mode */ + u8 ee_pd_gains[AR5K_EEPROM_N_MODES]; + /* Back mapping pdcurve number -> pdcurve index in pd->pd_curves */ + u8 ee_pdc_to_idx[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_PD_GAINS]; - u8 ee_n_piers[AR5K_EEPROM_N_MODES]; + u8 ee_n_piers[AR5K_EEPROM_N_MODES]; struct ath5k_chan_pcal_info ee_pwr_cal_a[AR5K_EEPROM_N_5GHZ_CHAN]; - struct ath5k_chan_pcal_info ee_pwr_cal_b[AR5K_EEPROM_N_2GHZ_CHAN]; - struct ath5k_chan_pcal_info ee_pwr_cal_g[AR5K_EEPROM_N_2GHZ_CHAN]; + struct ath5k_chan_pcal_info ee_pwr_cal_b[AR5K_EEPROM_N_2GHZ_CHAN_MAX]; + struct ath5k_chan_pcal_info ee_pwr_cal_g[AR5K_EEPROM_N_2GHZ_CHAN_MAX]; /* Per rate target power levels */ - u16 ee_rate_target_pwr_num[AR5K_EEPROM_N_MODES]; + u8 ee_rate_target_pwr_num[AR5K_EEPROM_N_MODES]; struct ath5k_rate_pcal_info ee_rate_tpwr_a[AR5K_EEPROM_N_5GHZ_CHAN]; - struct ath5k_rate_pcal_info ee_rate_tpwr_b[AR5K_EEPROM_N_2GHZ_CHAN]; - struct ath5k_rate_pcal_info ee_rate_tpwr_g[AR5K_EEPROM_N_2GHZ_CHAN]; + struct ath5k_rate_pcal_info ee_rate_tpwr_b[AR5K_EEPROM_N_2GHZ_CHAN_MAX]; + struct ath5k_rate_pcal_info ee_rate_tpwr_g[AR5K_EEPROM_N_2GHZ_CHAN_MAX]; /* Conformance test limits (Unused) */ - u16 ee_ctls; - u16 ee_ctl[AR5K_EEPROM_MAX_CTLS]; + u8 ee_ctls; + u8 ee_ctl[AR5K_EEPROM_MAX_CTLS]; struct ath5k_edge_power ee_ctl_pwr[AR5K_EEPROM_N_EDGES * AR5K_EEPROM_MAX_CTLS]; /* Noise Floor Calibration settings */ -- cgit v1.2.3 From 9ca9fb8aa8422595956af9681518cdb8b167055e Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Mon, 16 Mar 2009 22:34:02 -0400 Subject: ath5k: disable MIB interrupts The MIB interrupt fires whenever counters overflow; however without support for automatic noise immunity, we can sometimes get an interrupt storm. The get_stats() callback reads the counters anyway so we can disable the interrupt for now until ANI is implemented. This fixes the issue reported in http://bugzilla.kernel.org/show_bug.cgi?id=12647. Changes-licensed-under: 3-Clause-BSD Cc: stable@kernel.org Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index cad3ccf61b0..8eec155ed0c 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -2305,7 +2305,7 @@ ath5k_init(struct ath5k_softc *sc) sc->curband = &sc->sbands[sc->curchan->band]; sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL | AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL | - AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_MIB; + AR5K_INT_FATAL | AR5K_INT_GLOBAL; ret = ath5k_reset(sc, false, false); if (ret) goto done; -- cgit v1.2.3 From e23a9014fd4d502a419255a83e2479ab804c6f16 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Mon, 16 Mar 2009 22:34:03 -0400 Subject: ath5k: remove dummy PCI "retry timeout" fix Remove the PCI retry timeout code, for all the same reasons that Luis Rodriguez removed it for ath9k. Changes-licensed-under: 3-Clause-BSD Cc: Luis Rodriguez Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 8eec155ed0c..ef9825fa4ec 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -685,13 +685,6 @@ ath5k_pci_resume(struct pci_dev *pdev) if (err) return err; - /* - * Suspend/Resume resets the PCI configuration space, so we have to - * re-disable the RETRY_TIMEOUT register (0x41) to keep - * PCI Tx retries from interfering with C3 CPU state - */ - pci_write_config_byte(pdev, 0x41, 0); - err = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc); if (err) { ATH5K_ERR(sc, "request_irq failed\n"); -- cgit v1.2.3 From 722f069a6dc95d7c6c2cdfbe3413899a3b768f9c Mon Sep 17 00:00:00 2001 From: Sujith Date: Tue, 17 Mar 2009 08:50:06 +0530 Subject: mac80211: Tear down aggregation sessions for suspend/resume When the driver has been notified with a STA_REMOVE, it tears down the internal ADDBA state. On resume, trying to initiate aggregation would fail because mac80211 has not cleared the operational state for that . This can be fixed by tearing down the existing sessions on a suspend. Also, the driver can initiate a new BA session when suspend is in progress. This is fixed by marking the station as being in suspend state and denying ADDBA requests for such STAs. Signed-off-by: Sujith Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/agg-rx.c | 8 ++++++++ net/mac80211/agg-tx.c | 9 +++++++++ net/mac80211/pm.c | 25 +++++++++++++++++++++++++ net/mac80211/sta_info.h | 3 +++ 4 files changed, 45 insertions(+) diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index a95affc9462..07656d830bc 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -197,6 +197,14 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, status = WLAN_STATUS_REQUEST_DECLINED; + if (test_sta_flags(sta, WLAN_STA_SUSPEND)) { +#ifdef CONFIG_MAC80211_HT_DEBUG + printk(KERN_DEBUG "Suspend in progress. " + "Denying ADDBA request\n"); +#endif + goto end_no_lock; + } + /* sanity check for incoming parameters: * check if configuration can support the BA policy * and if buffer size does not exceeds max value */ diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 1df116d4d6e..e5776ef1717 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -257,6 +257,15 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) goto unlock; } + if (test_sta_flags(sta, WLAN_STA_SUSPEND)) { +#ifdef CONFIG_MAC80211_HT_DEBUG + printk(KERN_DEBUG "Suspend in progress. " + "Denying BA session request\n"); +#endif + ret = -EINVAL; + goto unlock; + } + spin_lock_bh(&sta->lock); sdata = sta->sdata; diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index ef7be1ce2c8..1e6152ac677 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -21,6 +21,19 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) list_for_each_entry(sdata, &local->interfaces, list) ieee80211_disable_keys(sdata); + /* Tear down aggregation sessions */ + + rcu_read_lock(); + + if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { + list_for_each_entry_rcu(sta, &local->sta_list, list) { + set_sta_flags(sta, WLAN_STA_SUSPEND); + ieee80211_sta_tear_down_BA_sessions(sta); + } + } + + rcu_read_unlock(); + /* remove STAs */ if (local->ops->sta_notify) { spin_lock_irqsave(&local->sta_lock, flags); @@ -102,6 +115,18 @@ int __ieee80211_resume(struct ieee80211_hw *hw) spin_unlock_irqrestore(&local->sta_lock, flags); } + /* Clear Suspend state so that ADDBA requests can be processed */ + + rcu_read_lock(); + + if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { + list_for_each_entry_rcu(sta, &local->sta_list, list) { + clear_sta_flags(sta, WLAN_STA_SUSPEND); + } + } + + rcu_read_unlock(); + /* add back keys */ list_for_each_entry(sdata, &local->interfaces, list) if (netif_running(sdata->dev)) diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 1f45573c580..5b223b216e5 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -35,6 +35,8 @@ * IEEE80211_TX_CTL_CLEAR_PS_FILT control flag) when the next * frame to this station is transmitted. * @WLAN_STA_MFP: Management frame protection is used with this STA. + * @WLAN_STA_SUSPEND: Set/cleared during a suspend/resume cycle. + * Used to deny ADDBA requests (both TX and RX). */ enum ieee80211_sta_info_flags { WLAN_STA_AUTH = 1<<0, @@ -48,6 +50,7 @@ enum ieee80211_sta_info_flags { WLAN_STA_PSPOLL = 1<<8, WLAN_STA_CLEAR_PS_FILT = 1<<9, WLAN_STA_MFP = 1<<10, + WLAN_STA_SUSPEND = 1<<11 }; #define STA_TID_NUM 16 -- cgit v1.2.3 From 6d5eaafa558783a669bb46c3dba902370e8f0ffc Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Tue, 17 Mar 2009 11:29:19 +0100 Subject: rt2x00: Update MAINTAINERS entry: new mailinglist The rt2400-devel mailinglist will be replaced by the new mailinglist rt2x00-users. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index fa7be04b0cf..bdadcb3c3f4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3602,7 +3602,7 @@ S: Maintained RALINK RT2X00 WIRELESS LAN DRIVER P: rt2x00 project L: linux-wireless@vger.kernel.org -L: rt2400-devel@lists.sourceforge.net +L: users@rt2x00.serialmonkey.com W: http://rt2x00.serialmonkey.com/ S: Maintained T: git kernel.org:/pub/scm/linux/kernel/git/ivd/rt2x00.git -- cgit v1.2.3 From 8f655dde240293f3b82313cae91c64ffd7b64c50 Mon Sep 17 00:00:00 2001 From: Nick Kossifidis Date: Sun, 15 Mar 2009 22:20:35 +0200 Subject: ath5k: Add tx power calibration support * Add tx power calibration support * Add a few tx power limits * Hardcode default power to 12.5dB * Disable TPC for now v2: Address Jiri's comments Signed-off-by: Nick Kossifidis Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/ath5k.h | 35 +- drivers/net/wireless/ath5k/attach.c | 2 + drivers/net/wireless/ath5k/base.c | 12 +- drivers/net/wireless/ath5k/desc.c | 4 + drivers/net/wireless/ath5k/phy.c | 1170 +++++++++++++++++++++++++++++++++-- drivers/net/wireless/ath5k/reg.h | 19 + drivers/net/wireless/ath5k/reset.c | 35 +- 7 files changed, 1180 insertions(+), 97 deletions(-) diff --git a/drivers/net/wireless/ath5k/ath5k.h b/drivers/net/wireless/ath5k/ath5k.h index 0dc2c7321c8..0b616e72fe0 100644 --- a/drivers/net/wireless/ath5k/ath5k.h +++ b/drivers/net/wireless/ath5k/ath5k.h @@ -204,9 +204,9 @@ #define AR5K_TUNE_CWMAX_11B 1023 #define AR5K_TUNE_CWMAX_XR 7 #define AR5K_TUNE_NOISE_FLOOR -72 -#define AR5K_TUNE_MAX_TXPOWER 60 -#define AR5K_TUNE_DEFAULT_TXPOWER 30 -#define AR5K_TUNE_TPC_TXPOWER true +#define AR5K_TUNE_MAX_TXPOWER 63 +#define AR5K_TUNE_DEFAULT_TXPOWER 25 +#define AR5K_TUNE_TPC_TXPOWER false #define AR5K_TUNE_ANT_DIVERSITY true #define AR5K_TUNE_HWTXTRIES 4 @@ -551,11 +551,11 @@ enum ath5k_pkt_type { */ #define AR5K_TXPOWER_OFDM(_r, _v) ( \ ((0 & 1) << ((_v) + 6)) | \ - (((ah->ah_txpower.txp_rates[(_r)]) & 0x3f) << (_v)) \ + (((ah->ah_txpower.txp_rates_power_table[(_r)]) & 0x3f) << (_v)) \ ) #define AR5K_TXPOWER_CCK(_r, _v) ( \ - (ah->ah_txpower.txp_rates[(_r)] & 0x3f) << (_v) \ + (ah->ah_txpower.txp_rates_power_table[(_r)] & 0x3f) << (_v) \ ) /* @@ -1085,13 +1085,25 @@ struct ath5k_hw { struct ath5k_gain ah_gain; u8 ah_offset[AR5K_MAX_RF_BANKS]; + struct { - u16 txp_pcdac[AR5K_EEPROM_POWER_TABLE_SIZE]; - u16 txp_rates[AR5K_MAX_RATES]; - s16 txp_min; - s16 txp_max; + /* Temporary tables used for interpolation */ + u8 tmpL[AR5K_EEPROM_N_PD_GAINS] + [AR5K_EEPROM_POWER_TABLE_SIZE]; + u8 tmpR[AR5K_EEPROM_N_PD_GAINS] + [AR5K_EEPROM_POWER_TABLE_SIZE]; + u8 txp_pd_table[AR5K_EEPROM_POWER_TABLE_SIZE * 2]; + u16 txp_rates_power_table[AR5K_MAX_RATES]; + u8 txp_min_idx; bool txp_tpc; + /* Values in 0.25dB units */ + s16 txp_min_pwr; + s16 txp_max_pwr; + s16 txp_offset; s16 txp_ofdm; + /* Values in dB units */ + s16 txp_cck_ofdm_pwr_delta; + s16 txp_cck_ofdm_gainf_delta; } ah_txpower; struct { @@ -1161,6 +1173,7 @@ extern void ath5k_hw_update_mib_counters(struct ath5k_hw *ah, struct ieee80211_l /* EEPROM access functions */ extern int ath5k_eeprom_init(struct ath5k_hw *ah); +extern void ath5k_eeprom_detach(struct ath5k_hw *ah); extern int ath5k_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac); extern bool ath5k_eeprom_is_hb63(struct ath5k_hw *ah); @@ -1256,8 +1269,8 @@ extern void ath5k_hw_set_def_antenna(struct ath5k_hw *ah, unsigned int ant); extern unsigned int ath5k_hw_get_def_antenna(struct ath5k_hw *ah); extern int ath5k_hw_phy_disable(struct ath5k_hw *ah); /* TX power setup */ -extern int ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, unsigned int txpower); -extern int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, unsigned int power); +extern int ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, u8 ee_mode, u8 txpower); +extern int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 ee_mode, u8 txpower); /* * Functions used internaly diff --git a/drivers/net/wireless/ath5k/attach.c b/drivers/net/wireless/ath5k/attach.c index 656cb9dc833..70d376c63aa 100644 --- a/drivers/net/wireless/ath5k/attach.c +++ b/drivers/net/wireless/ath5k/attach.c @@ -341,6 +341,8 @@ void ath5k_hw_detach(struct ath5k_hw *ah) if (ah->ah_rf_banks != NULL) kfree(ah->ah_rf_banks); + ath5k_eeprom_detach(ah); + /* assume interrupts are down */ kfree(ah); } diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index ef9825fa4ec..f28b86c5d34 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -1209,6 +1209,9 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) pktlen = skb->len; + /* FIXME: If we are in g mode and rate is a CCK rate + * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta + * from tx power (value is in dB units already) */ if (info->control.hw_key) { keyidx = info->control.hw_key->hw_key_idx; pktlen += info->control.hw_key->icv_len; @@ -2037,6 +2040,9 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) antenna = sc->bsent & 4 ? 2 : 1; } + /* FIXME: If we are in g mode and rate is a CCK rate + * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta + * from tx power (value is in dB units already) */ ds->ds_data = bf->skbaddr; ret = ah->ah_setup_tx_desc(ah, ds, skb->len, ieee80211_get_hdrlen_from_skb(skb), @@ -2601,12 +2607,6 @@ ath5k_reset(struct ath5k_softc *sc, bool stop, bool change_channel) goto err; } - /* - * This is needed only to setup initial state - * but it's best done after a reset. - */ - ath5k_hw_set_txpower_limit(sc->ah, 0); - ret = ath5k_rx_start(sc); if (ret) { ATH5K_ERR(sc, "can't start recv logic\n"); diff --git a/drivers/net/wireless/ath5k/desc.c b/drivers/net/wireless/ath5k/desc.c index b40a9287a39..dc30a2b70a6 100644 --- a/drivers/net/wireless/ath5k/desc.c +++ b/drivers/net/wireless/ath5k/desc.c @@ -194,6 +194,10 @@ static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah, return -EINVAL; } + tx_power += ah->ah_txpower.txp_offset; + if (tx_power > AR5K_TUNE_MAX_TXPOWER) + tx_power = AR5K_TUNE_MAX_TXPOWER; + /* Clear descriptor */ memset(&desc->ud.ds_tx5212, 0, sizeof(struct ath5k_hw_5212_tx_desc)); diff --git a/drivers/net/wireless/ath5k/phy.c b/drivers/net/wireless/ath5k/phy.c index 81f5bebc48b..9e2faae5ae9 100644 --- a/drivers/net/wireless/ath5k/phy.c +++ b/drivers/net/wireless/ath5k/phy.c @@ -4,6 +4,7 @@ * Copyright (c) 2004-2007 Reyk Floeter * Copyright (c) 2006-2009 Nick Kossifidis * Copyright (c) 2007-2008 Jiri Slaby + * Copyright (c) 2008-2009 Felix Fietkau * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -183,7 +184,9 @@ static void ath5k_hw_request_rfgain_probe(struct ath5k_hw *ah) if (ah->ah_gain.g_state != AR5K_RFGAIN_ACTIVE) return; - ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txpower.txp_max, + /* Send the packet with 2dB below max power as + * patent doc suggest */ + ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txpower.txp_max_pwr - 4, AR5K_PHY_PAPD_PROBE_TXPOWER) | AR5K_PHY_PAPD_PROBE_TX_NEXT, AR5K_PHY_PAPD_PROBE); @@ -1433,93 +1436,1120 @@ unsigned int ath5k_hw_get_def_antenna(struct ath5k_hw *ah) return false; /*XXX: What do we return for 5210 ?*/ } + +/****************\ +* TX power setup * +\****************/ + +/* + * Helper functions + */ + +/* + * Do linear interpolation between two given (x, y) points + */ +static s16 +ath5k_get_interpolated_value(s16 target, s16 x_left, s16 x_right, + s16 y_left, s16 y_right) +{ + s16 ratio, result; + + /* Avoid divide by zero and skip interpolation + * if we have the same point */ + if ((x_left == x_right) || (y_left == y_right)) + return y_left; + + /* + * Since we use ints and not fps, we need to scale up in + * order to get a sane ratio value (or else we 'll eg. get + * always 1 instead of 1.25, 1.75 etc). We scale up by 100 + * to have some accuracy both for 0.5 and 0.25 steps. + */ + ratio = ((100 * y_right - 100 * y_left)/(x_right - x_left)); + + /* Now scale down to be in range */ + result = y_left + (ratio * (target - x_left) / 100); + + return result; +} + +/* + * Find vertical boundary (min pwr) for the linear PCDAC curve. + * + * Since we have the top of the curve and we draw the line below + * until we reach 1 (1 pcdac step) we need to know which point + * (x value) that is so that we don't go below y axis and have negative + * pcdac values when creating the curve, or fill the table with zeroes. + */ +static s16 +ath5k_get_linear_pcdac_min(const u8 *stepL, const u8 *stepR, + const s16 *pwrL, const s16 *pwrR) +{ + s8 tmp; + s16 min_pwrL, min_pwrR; + s16 pwr_i = pwrL[0]; + + do { + pwr_i--; + tmp = (s8) ath5k_get_interpolated_value(pwr_i, + pwrL[0], pwrL[1], + stepL[0], stepL[1]); + + } while (tmp > 1); + + min_pwrL = pwr_i; + + pwr_i = pwrR[0]; + do { + pwr_i--; + tmp = (s8) ath5k_get_interpolated_value(pwr_i, + pwrR[0], pwrR[1], + stepR[0], stepR[1]); + + } while (tmp > 1); + + min_pwrR = pwr_i; + + /* Keep the right boundary so that it works for both curves */ + return max(min_pwrL, min_pwrR); +} + +/* + * Interpolate (pwr,vpd) points to create a Power to PDADC or a + * Power to PCDAC curve. + * + * Each curve has power on x axis (in 0.5dB units) and PCDAC/PDADC + * steps (offsets) on y axis. Power can go up to 31.5dB and max + * PCDAC/PDADC step for each curve is 64 but we can write more than + * one curves on hw so we can go up to 128 (which is the max step we + * can write on the final table). + * + * We write y values (PCDAC/PDADC steps) on hw. + */ +static void +ath5k_create_power_curve(s16 pmin, s16 pmax, + const s16 *pwr, const u8 *vpd, + u8 num_points, + u8 *vpd_table, u8 type) +{ + u8 idx[2] = { 0, 1 }; + s16 pwr_i = 2*pmin; + int i; + + if (num_points < 2) + return; + + /* We want the whole line, so adjust boundaries + * to cover the entire power range. Note that + * power values are already 0.25dB so no need + * to multiply pwr_i by 2 */ + if (type == AR5K_PWRTABLE_LINEAR_PCDAC) { + pwr_i = pmin; + pmin = 0; + pmax = 63; + } + + /* Find surrounding turning points (TPs) + * and interpolate between them */ + for (i = 0; (i <= (u16) (pmax - pmin)) && + (i < AR5K_EEPROM_POWER_TABLE_SIZE); i++) { + + /* We passed the right TP, move to the next set of TPs + * if we pass the last TP, extrapolate above using the last + * two TPs for ratio */ + if ((pwr_i > pwr[idx[1]]) && (idx[1] < num_points - 1)) { + idx[0]++; + idx[1]++; + } + + vpd_table[i] = (u8) ath5k_get_interpolated_value(pwr_i, + pwr[idx[0]], pwr[idx[1]], + vpd[idx[0]], vpd[idx[1]]); + + /* Increase by 0.5dB + * (0.25 dB units) */ + pwr_i += 2; + } +} + +/* + * Get the surrounding per-channel power calibration piers + * for a given frequency so that we can interpolate between + * them and come up with an apropriate dataset for our current + * channel. + */ +static void +ath5k_get_chan_pcal_surrounding_piers(struct ath5k_hw *ah, + struct ieee80211_channel *channel, + struct ath5k_chan_pcal_info **pcinfo_l, + struct ath5k_chan_pcal_info **pcinfo_r) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_chan_pcal_info *pcinfo; + u8 idx_l, idx_r; + u8 mode, max, i; + u32 target = channel->center_freq; + + idx_l = 0; + idx_r = 0; + + if (!(channel->hw_value & CHANNEL_OFDM)) { + pcinfo = ee->ee_pwr_cal_b; + mode = AR5K_EEPROM_MODE_11B; + } else if (channel->hw_value & CHANNEL_2GHZ) { + pcinfo = ee->ee_pwr_cal_g; + mode = AR5K_EEPROM_MODE_11G; + } else { + pcinfo = ee->ee_pwr_cal_a; + mode = AR5K_EEPROM_MODE_11A; + } + max = ee->ee_n_piers[mode] - 1; + + /* Frequency is below our calibrated + * range. Use the lowest power curve + * we have */ + if (target < pcinfo[0].freq) { + idx_l = idx_r = 0; + goto done; + } + + /* Frequency is above our calibrated + * range. Use the highest power curve + * we have */ + if (target > pcinfo[max].freq) { + idx_l = idx_r = max; + goto done; + } + + /* Frequency is inside our calibrated + * channel range. Pick the surrounding + * calibration piers so that we can + * interpolate */ + for (i = 0; i <= max; i++) { + + /* Frequency matches one of our calibration + * piers, no need to interpolate, just use + * that calibration pier */ + if (pcinfo[i].freq == target) { + idx_l = idx_r = i; + goto done; + } + + /* We found a calibration pier that's above + * frequency, use this pier and the previous + * one to interpolate */ + if (target < pcinfo[i].freq) { + idx_r = i; + idx_l = idx_r - 1; + goto done; + } + } + +done: + *pcinfo_l = &pcinfo[idx_l]; + *pcinfo_r = &pcinfo[idx_r]; + + return; +} + +/* + * Get the surrounding per-rate power calibration data + * for a given frequency and interpolate between power + * values to set max target power supported by hw for + * each rate. + */ +static void +ath5k_get_rate_pcal_data(struct ath5k_hw *ah, + struct ieee80211_channel *channel, + struct ath5k_rate_pcal_info *rates) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_rate_pcal_info *rpinfo; + u8 idx_l, idx_r; + u8 mode, max, i; + u32 target = channel->center_freq; + + idx_l = 0; + idx_r = 0; + + if (!(channel->hw_value & CHANNEL_OFDM)) { + rpinfo = ee->ee_rate_tpwr_b; + mode = AR5K_EEPROM_MODE_11B; + } else if (channel->hw_value & CHANNEL_2GHZ) { + rpinfo = ee->ee_rate_tpwr_g; + mode = AR5K_EEPROM_MODE_11G; + } else { + rpinfo = ee->ee_rate_tpwr_a; + mode = AR5K_EEPROM_MODE_11A; + } + max = ee->ee_rate_target_pwr_num[mode] - 1; + + /* Get the surrounding calibration + * piers - same as above */ + if (target < rpinfo[0].freq) { + idx_l = idx_r = 0; + goto done; + } + + if (target > rpinfo[max].freq) { + idx_l = idx_r = max; + goto done; + } + + for (i = 0; i <= max; i++) { + + if (rpinfo[i].freq == target) { + idx_l = idx_r = i; + goto done; + } + + if (target < rpinfo[i].freq) { + idx_r = i; + idx_l = idx_r - 1; + goto done; + } + } + +done: + /* Now interpolate power value, based on the frequency */ + rates->freq = target; + + rates->target_power_6to24 = + ath5k_get_interpolated_value(target, rpinfo[idx_l].freq, + rpinfo[idx_r].freq, + rpinfo[idx_l].target_power_6to24, + rpinfo[idx_r].target_power_6to24); + + rates->target_power_36 = + ath5k_get_interpolated_value(target, rpinfo[idx_l].freq, + rpinfo[idx_r].freq, + rpinfo[idx_l].target_power_36, + rpinfo[idx_r].target_power_36); + + rates->target_power_48 = + ath5k_get_interpolated_value(target, rpinfo[idx_l].freq, + rpinfo[idx_r].freq, + rpinfo[idx_l].target_power_48, + rpinfo[idx_r].target_power_48); + + rates->target_power_54 = + ath5k_get_interpolated_value(target, rpinfo[idx_l].freq, + rpinfo[idx_r].freq, + rpinfo[idx_l].target_power_54, + rpinfo[idx_r].target_power_54); +} + +/* + * Get the max edge power for this channel if + * we have such data from EEPROM's Conformance Test + * Limits (CTL), and limit max power if needed. + * + * FIXME: Only works for world regulatory domains + */ +static void +ath5k_get_max_ctl_power(struct ath5k_hw *ah, + struct ieee80211_channel *channel) +{ + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + struct ath5k_edge_power *rep = ee->ee_ctl_pwr; + u8 *ctl_val = ee->ee_ctl; + s16 max_chan_pwr = ah->ah_txpower.txp_max_pwr / 4; + s16 edge_pwr = 0; + u8 rep_idx; + u8 i, ctl_mode; + u8 ctl_idx = 0xFF; + u32 target = channel->center_freq; + + /* Find out a CTL for our mode that's not mapped + * on a specific reg domain. + * + * TODO: Map our current reg domain to one of the 3 available + * reg domain ids so that we can support more CTLs. */ + switch (channel->hw_value & CHANNEL_MODES) { + case CHANNEL_A: + ctl_mode = AR5K_CTL_11A | AR5K_CTL_NO_REGDOMAIN; + break; + case CHANNEL_G: + ctl_mode = AR5K_CTL_11G | AR5K_CTL_NO_REGDOMAIN; + break; + case CHANNEL_B: + ctl_mode = AR5K_CTL_11B | AR5K_CTL_NO_REGDOMAIN; + break; + case CHANNEL_T: + ctl_mode = AR5K_CTL_TURBO | AR5K_CTL_NO_REGDOMAIN; + break; + case CHANNEL_TG: + ctl_mode = AR5K_CTL_TURBOG | AR5K_CTL_NO_REGDOMAIN; + break; + case CHANNEL_XR: + /* Fall through */ + default: + return; + } + + for (i = 0; i < ee->ee_ctls; i++) { + if (ctl_val[i] == ctl_mode) { + ctl_idx = i; + break; + } + } + + /* If we have a CTL dataset available grab it and find the + * edge power for our frequency */ + if (ctl_idx == 0xFF) + return; + + /* Edge powers are sorted by frequency from lower + * to higher. Each CTL corresponds to 8 edge power + * measurements. */ + rep_idx = ctl_idx * AR5K_EEPROM_N_EDGES; + + /* Don't do boundaries check because we + * might have more that one bands defined + * for this mode */ + + /* Get the edge power that's closer to our + * frequency */ + for (i = 0; i < AR5K_EEPROM_N_EDGES; i++) { + rep_idx += i; + if (target <= rep[rep_idx].freq) + edge_pwr = (s16) rep[rep_idx].edge; + } + + if (edge_pwr) + ah->ah_txpower.txp_max_pwr = 4*min(edge_pwr, max_chan_pwr); +} + + +/* + * Power to PCDAC table functions + */ + /* - * TX power setup + * Fill Power to PCDAC table on RF5111 + * + * No further processing is needed for RF5111, the only thing we have to + * do is fill the values below and above calibration range since eeprom data + * may not cover the entire PCDAC table. */ +static void +ath5k_fill_pwr_to_pcdac_table(struct ath5k_hw *ah, s16* table_min, + s16 *table_max) +{ + u8 *pcdac_out = ah->ah_txpower.txp_pd_table; + u8 *pcdac_tmp = ah->ah_txpower.tmpL[0]; + u8 pcdac_0, pcdac_n, pcdac_i, pwr_idx, i; + s16 min_pwr, max_pwr; + + /* Get table boundaries */ + min_pwr = table_min[0]; + pcdac_0 = pcdac_tmp[0]; + + max_pwr = table_max[0]; + pcdac_n = pcdac_tmp[table_max[0] - table_min[0]]; + + /* Extrapolate below minimum using pcdac_0 */ + pcdac_i = 0; + for (i = 0; i < min_pwr; i++) + pcdac_out[pcdac_i++] = pcdac_0; + + /* Copy values from pcdac_tmp */ + pwr_idx = min_pwr; + for (i = 0 ; pwr_idx <= max_pwr && + pcdac_i < AR5K_EEPROM_POWER_TABLE_SIZE; i++) { + pcdac_out[pcdac_i++] = pcdac_tmp[i]; + pwr_idx++; + } + + /* Extrapolate above maximum */ + while (pcdac_i < AR5K_EEPROM_POWER_TABLE_SIZE) + pcdac_out[pcdac_i++] = pcdac_n; + +} /* - * Initialize the tx power table (not fully implemented) + * Combine available XPD Curves and fill Linear Power to PCDAC table + * on RF5112 + * + * RFX112 can have up to 2 curves (one for low txpower range and one for + * higher txpower range). We need to put them both on pcdac_out and place + * them in the correct location. In case we only have one curve available + * just fit it on pcdac_out (it's supposed to cover the entire range of + * available pwr levels since it's always the higher power curve). Extrapolate + * below and above final table if needed. */ -static void ath5k_txpower_table(struct ath5k_hw *ah, - struct ieee80211_channel *channel, s16 max_power) +static void +ath5k_combine_linear_pcdac_curves(struct ath5k_hw *ah, s16* table_min, + s16 *table_max, u8 pdcurves) { - unsigned int i, min, max, n; - u16 txpower, *rates; - - rates = ah->ah_txpower.txp_rates; - - txpower = AR5K_TUNE_DEFAULT_TXPOWER * 2; - if (max_power > txpower) - txpower = max_power > AR5K_TUNE_MAX_TXPOWER ? - AR5K_TUNE_MAX_TXPOWER : max_power; - - for (i = 0; i < AR5K_MAX_RATES; i++) - rates[i] = txpower; - - /* XXX setup target powers by rate */ - - ah->ah_txpower.txp_min = rates[7]; - ah->ah_txpower.txp_max = rates[0]; - ah->ah_txpower.txp_ofdm = rates[0]; - - /* Calculate the power table */ - n = ARRAY_SIZE(ah->ah_txpower.txp_pcdac); - min = AR5K_EEPROM_PCDAC_START; - max = AR5K_EEPROM_PCDAC_STOP; - for (i = 0; i < n; i += AR5K_EEPROM_PCDAC_STEP) - ah->ah_txpower.txp_pcdac[i] = -#ifdef notyet - min + ((i * (max - min)) / n); -#else - min; + u8 *pcdac_out = ah->ah_txpower.txp_pd_table; + u8 *pcdac_low_pwr; + u8 *pcdac_high_pwr; + u8 *pcdac_tmp; + u8 pwr; + s16 max_pwr_idx; + s16 min_pwr_idx; + s16 mid_pwr_idx = 0; + /* Edge flag turs on the 7nth bit on the PCDAC + * to delcare the higher power curve (force values + * to be greater than 64). If we only have one curve + * we don't need to set this, if we have 2 curves and + * fill the table backwards this can also be used to + * switch from higher power curve to lower power curve */ + u8 edge_flag; + int i; + + /* When we have only one curve available + * that's the higher power curve. If we have + * two curves the first is the high power curve + * and the next is the low power curve. */ + if (pdcurves > 1) { + pcdac_low_pwr = ah->ah_txpower.tmpL[1]; + pcdac_high_pwr = ah->ah_txpower.tmpL[0]; + mid_pwr_idx = table_max[1] - table_min[1] - 1; + max_pwr_idx = (table_max[0] - table_min[0]) / 2; + + /* If table size goes beyond 31.5dB, keep the + * upper 31.5dB range when setting tx power. + * Note: 126 = 31.5 dB in quarter dB steps */ + if (table_max[0] - table_min[1] > 126) + min_pwr_idx = table_max[0] - 126; + else + min_pwr_idx = table_min[1]; + + /* Since we fill table backwards + * start from high power curve */ + pcdac_tmp = pcdac_high_pwr; + + edge_flag = 0x40; +#if 0 + /* If both min and max power limits are in lower + * power curve's range, only use the low power curve. + * TODO: min/max levels are related to target + * power values requested from driver/user + * XXX: Is this really needed ? */ + if (min_pwr < table_max[1] && + max_pwr < table_max[1]) { + edge_flag = 0; + pcdac_tmp = pcdac_low_pwr; + max_pwr_idx = (table_max[1] - table_min[1])/2; + } #endif + } else { + pcdac_low_pwr = ah->ah_txpower.tmpL[1]; /* Zeroed */ + pcdac_high_pwr = ah->ah_txpower.tmpL[0]; + min_pwr_idx = table_min[0]; + max_pwr_idx = (table_max[0] - table_min[0]) / 2; + pcdac_tmp = pcdac_high_pwr; + edge_flag = 0; + } + + /* This is used when setting tx power*/ + ah->ah_txpower.txp_min_idx = min_pwr_idx/2; + + /* Fill Power to PCDAC table backwards */ + pwr = max_pwr_idx; + for (i = 63; i >= 0; i--) { + /* Entering lower power range, reset + * edge flag and set pcdac_tmp to lower + * power curve.*/ + if (edge_flag == 0x40 && + (2*pwr <= (table_max[1] - table_min[0]) || pwr == 0)) { + edge_flag = 0x00; + pcdac_tmp = pcdac_low_pwr; + pwr = mid_pwr_idx/2; + } + + /* Don't go below 1, extrapolate below if we have + * already swithced to the lower power curve -or + * we only have one curve and edge_flag is zero + * anyway */ + if (pcdac_tmp[pwr] < 1 && (edge_flag == 0x00)) { + while (i >= 0) { + pcdac_out[i] = pcdac_out[i + 1]; + i--; + } + break; + } + + pcdac_out[i] = pcdac_tmp[pwr] | edge_flag; + + /* Extrapolate above if pcdac is greater than + * 126 -this can happen because we OR pcdac_out + * value with edge_flag on high power curve */ + if (pcdac_out[i] > 126) + pcdac_out[i] = 126; + + /* Decrease by a 0.5dB step */ + pwr--; + } } +/* Write PCDAC values on hw */ +static void +ath5k_setup_pcdac_table(struct ath5k_hw *ah) +{ + u8 *pcdac_out = ah->ah_txpower.txp_pd_table; + int i; + + /* + * Write TX power values + */ + for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) { + ath5k_hw_reg_write(ah, + (((pcdac_out[2*i + 0] << 8 | 0xff) & 0xffff) << 0) | + (((pcdac_out[2*i + 1] << 8 | 0xff) & 0xffff) << 16), + AR5K_PHY_PCDAC_TXPOWER(i)); + } +} + + /* - * Set transmition power + * Power to PDADC table functions */ -int /*O.K. - txpower_table is unimplemented so this doesn't work*/ -ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, - unsigned int txpower) + +/* + * Set the gain boundaries and create final Power to PDADC table + * + * We can have up to 4 pd curves, we need to do a simmilar process + * as we do for RF5112. This time we don't have an edge_flag but we + * set the gain boundaries on a separate register. + */ +static void +ath5k_combine_pwr_to_pdadc_curves(struct ath5k_hw *ah, + s16 *pwr_min, s16 *pwr_max, u8 pdcurves) { - bool tpc = ah->ah_txpower.txp_tpc; - unsigned int i; + u8 gain_boundaries[AR5K_EEPROM_N_PD_GAINS]; + u8 *pdadc_out = ah->ah_txpower.txp_pd_table; + u8 *pdadc_tmp; + s16 pdadc_0; + u8 pdadc_i, pdadc_n, pwr_step, pdg, max_idx, table_size; + u8 pd_gain_overlap; + + /* Note: Register value is initialized on initvals + * there is no feedback from hw. + * XXX: What about pd_gain_overlap from EEPROM ? */ + pd_gain_overlap = (u8) ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG5) & + AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP; + + /* Create final PDADC table */ + for (pdg = 0, pdadc_i = 0; pdg < pdcurves; pdg++) { + pdadc_tmp = ah->ah_txpower.tmpL[pdg]; + + if (pdg == pdcurves - 1) + /* 2 dB boundary stretch for last + * (higher power) curve */ + gain_boundaries[pdg] = pwr_max[pdg] + 4; + else + /* Set gain boundary in the middle + * between this curve and the next one */ + gain_boundaries[pdg] = + (pwr_max[pdg] + pwr_min[pdg + 1]) / 2; + + /* Sanity check in case our 2 db stretch got out of + * range. */ + if (gain_boundaries[pdg] > AR5K_TUNE_MAX_TXPOWER) + gain_boundaries[pdg] = AR5K_TUNE_MAX_TXPOWER; + + /* For the first curve (lower power) + * start from 0 dB */ + if (pdg == 0) + pdadc_0 = 0; + else + /* For the other curves use the gain overlap */ + pdadc_0 = (gain_boundaries[pdg - 1] - pwr_min[pdg]) - + pd_gain_overlap; - ATH5K_TRACE(ah->ah_sc); - if (txpower > AR5K_TUNE_MAX_TXPOWER) { - ATH5K_ERR(ah->ah_sc, "invalid tx power: %u\n", txpower); - return -EINVAL; + /* Force each power step to be at least 0.5 dB */ + if ((pdadc_tmp[1] - pdadc_tmp[0]) > 1) + pwr_step = pdadc_tmp[1] - pdadc_tmp[0]; + else + pwr_step = 1; + + /* If pdadc_0 is negative, we need to extrapolate + * below this pdgain by a number of pwr_steps */ + while ((pdadc_0 < 0) && (pdadc_i < 128)) { + s16 tmp = pdadc_tmp[0] + pdadc_0 * pwr_step; + pdadc_out[pdadc_i++] = (tmp < 0) ? 0 : (u8) tmp; + pdadc_0++; + } + + /* Set last pwr level, using gain boundaries */ + pdadc_n = gain_boundaries[pdg] + pd_gain_overlap - pwr_min[pdg]; + /* Limit it to be inside pwr range */ + table_size = pwr_max[pdg] - pwr_min[pdg]; + max_idx = (pdadc_n < table_size) ? pdadc_n : table_size; + + /* Fill pdadc_out table */ + while (pdadc_0 < max_idx) + pdadc_out[pdadc_i++] = pdadc_tmp[pdadc_0++]; + + /* Need to extrapolate above this pdgain? */ + if (pdadc_n <= max_idx) + continue; + + /* Force each power step to be at least 0.5 dB */ + if ((pdadc_tmp[table_size - 1] - pdadc_tmp[table_size - 2]) > 1) + pwr_step = pdadc_tmp[table_size - 1] - + pdadc_tmp[table_size - 2]; + else + pwr_step = 1; + + /* Extrapolate above */ + while ((pdadc_0 < (s16) pdadc_n) && + (pdadc_i < AR5K_EEPROM_POWER_TABLE_SIZE * 2)) { + s16 tmp = pdadc_tmp[table_size - 1] + + (pdadc_0 - max_idx) * pwr_step; + pdadc_out[pdadc_i++] = (tmp > 127) ? 127 : (u8) tmp; + pdadc_0++; + } } + while (pdg < AR5K_EEPROM_N_PD_GAINS) { + gain_boundaries[pdg] = gain_boundaries[pdg - 1]; + pdg++; + } + + while (pdadc_i < AR5K_EEPROM_POWER_TABLE_SIZE * 2) { + pdadc_out[pdadc_i] = pdadc_out[pdadc_i - 1]; + pdadc_i++; + } + + /* Set gain boundaries */ + ath5k_hw_reg_write(ah, + AR5K_REG_SM(pd_gain_overlap, + AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP) | + AR5K_REG_SM(gain_boundaries[0], + AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_1) | + AR5K_REG_SM(gain_boundaries[1], + AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_2) | + AR5K_REG_SM(gain_boundaries[2], + AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_3) | + AR5K_REG_SM(gain_boundaries[3], + AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_4), + AR5K_PHY_TPC_RG5); + + /* Used for setting rate power table */ + ah->ah_txpower.txp_min_idx = pwr_min[0]; + +} + +/* Write PDADC values on hw */ +static void +ath5k_setup_pwr_to_pdadc_table(struct ath5k_hw *ah, + u8 pdcurves, u8 *pdg_to_idx) +{ + u8 *pdadc_out = ah->ah_txpower.txp_pd_table; + u32 reg; + u8 i; + + /* Select the right pdgain curves */ + + /* Clear current settings */ + reg = ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG1); + reg &= ~(AR5K_PHY_TPC_RG1_PDGAIN_1 | + AR5K_PHY_TPC_RG1_PDGAIN_2 | + AR5K_PHY_TPC_RG1_PDGAIN_3 | + AR5K_PHY_TPC_RG1_NUM_PD_GAIN); + /* - * RF2413 for some reason can't - * transmit anything if we call - * this funtion, so we skip it - * until we fix txpower. + * Use pd_gains curve from eeprom * - * XXX: Assume same for RF2425 - * to be safe. + * This overrides the default setting from initvals + * in case some vendors (e.g. Zcomax) don't use the default + * curves. If we don't honor their settings we 'll get a + * 5dB (1 * gain overlap ?) drop. */ - if ((ah->ah_radio == AR5K_RF2413) || (ah->ah_radio == AR5K_RF2425)) - return 0; + reg |= AR5K_REG_SM(pdcurves, AR5K_PHY_TPC_RG1_NUM_PD_GAIN); - /* Reset TX power values */ - memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower)); - ah->ah_txpower.txp_tpc = tpc; - - /* Initialize TX power table */ - ath5k_txpower_table(ah, channel, txpower); + switch (pdcurves) { + case 3: + reg |= AR5K_REG_SM(pdg_to_idx[2], AR5K_PHY_TPC_RG1_PDGAIN_3); + /* Fall through */ + case 2: + reg |= AR5K_REG_SM(pdg_to_idx[1], AR5K_PHY_TPC_RG1_PDGAIN_2); + /* Fall through */ + case 1: + reg |= AR5K_REG_SM(pdg_to_idx[0], AR5K_PHY_TPC_RG1_PDGAIN_1); + break; + } + ath5k_hw_reg_write(ah, reg, AR5K_PHY_TPC_RG1); /* * Write TX power values */ for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) { ath5k_hw_reg_write(ah, - ((((ah->ah_txpower.txp_pcdac[(i << 1) + 1] << 8) | 0xff) & 0xffff) << 16) | - (((ah->ah_txpower.txp_pcdac[(i << 1) ] << 8) | 0xff) & 0xffff), - AR5K_PHY_PCDAC_TXPOWER(i)); + ((pdadc_out[4*i + 0] & 0xff) << 0) | + ((pdadc_out[4*i + 1] & 0xff) << 8) | + ((pdadc_out[4*i + 2] & 0xff) << 16) | + ((pdadc_out[4*i + 3] & 0xff) << 24), + AR5K_PHY_PDADC_TXPOWER(i)); + } +} + + +/* + * Common code for PCDAC/PDADC tables + */ + +/* + * This is the main function that uses all of the above + * to set PCDAC/PDADC table on hw for the current channel. + * This table is used for tx power calibration on the basband, + * without it we get weird tx power levels and in some cases + * distorted spectral mask + */ +static int +ath5k_setup_channel_powertable(struct ath5k_hw *ah, + struct ieee80211_channel *channel, + u8 ee_mode, u8 type) +{ + struct ath5k_pdgain_info *pdg_L, *pdg_R; + struct ath5k_chan_pcal_info *pcinfo_L; + struct ath5k_chan_pcal_info *pcinfo_R; + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + u8 *pdg_curve_to_idx = ee->ee_pdc_to_idx[ee_mode]; + s16 table_min[AR5K_EEPROM_N_PD_GAINS]; + s16 table_max[AR5K_EEPROM_N_PD_GAINS]; + u8 *tmpL; + u8 *tmpR; + u32 target = channel->center_freq; + int pdg, i; + + /* Get surounding freq piers for this channel */ + ath5k_get_chan_pcal_surrounding_piers(ah, channel, + &pcinfo_L, + &pcinfo_R); + + /* Loop over pd gain curves on + * surounding freq piers by index */ + for (pdg = 0; pdg < ee->ee_pd_gains[ee_mode]; pdg++) { + + /* Fill curves in reverse order + * from lower power (max gain) + * to higher power. Use curve -> idx + * backmaping we did on eeprom init */ + u8 idx = pdg_curve_to_idx[pdg]; + + /* Grab the needed curves by index */ + pdg_L = &pcinfo_L->pd_curves[idx]; + pdg_R = &pcinfo_R->pd_curves[idx]; + + /* Initialize the temp tables */ + tmpL = ah->ah_txpower.tmpL[pdg]; + tmpR = ah->ah_txpower.tmpR[pdg]; + + /* Set curve's x boundaries and create + * curves so that they cover the same + * range (if we don't do that one table + * will have values on some range and the + * other one won't have any so interpolation + * will fail) */ + table_min[pdg] = min(pdg_L->pd_pwr[0], + pdg_R->pd_pwr[0]) / 2; + + table_max[pdg] = max(pdg_L->pd_pwr[pdg_L->pd_points - 1], + pdg_R->pd_pwr[pdg_R->pd_points - 1]) / 2; + + /* Now create the curves on surrounding channels + * and interpolate if needed to get the final + * curve for this gain on this channel */ + switch (type) { + case AR5K_PWRTABLE_LINEAR_PCDAC: + /* Override min/max so that we don't loose + * accuracy (don't divide by 2) */ + table_min[pdg] = min(pdg_L->pd_pwr[0], + pdg_R->pd_pwr[0]); + + table_max[pdg] = + max(pdg_L->pd_pwr[pdg_L->pd_points - 1], + pdg_R->pd_pwr[pdg_R->pd_points - 1]); + + /* Override minimum so that we don't get + * out of bounds while extrapolating + * below. Don't do this when we have 2 + * curves and we are on the high power curve + * because table_min is ok in this case */ + if (!(ee->ee_pd_gains[ee_mode] > 1 && pdg == 0)) { + + table_min[pdg] = + ath5k_get_linear_pcdac_min(pdg_L->pd_step, + pdg_R->pd_step, + pdg_L->pd_pwr, + pdg_R->pd_pwr); + + /* Don't go too low because we will + * miss the upper part of the curve. + * Note: 126 = 31.5dB (max power supported) + * in 0.25dB units */ + if (table_max[pdg] - table_min[pdg] > 126) + table_min[pdg] = table_max[pdg] - 126; + } + + /* Fall through */ + case AR5K_PWRTABLE_PWR_TO_PCDAC: + case AR5K_PWRTABLE_PWR_TO_PDADC: + + ath5k_create_power_curve(table_min[pdg], + table_max[pdg], + pdg_L->pd_pwr, + pdg_L->pd_step, + pdg_L->pd_points, tmpL, type); + + /* We are in a calibration + * pier, no need to interpolate + * between freq piers */ + if (pcinfo_L == pcinfo_R) + continue; + + ath5k_create_power_curve(table_min[pdg], + table_max[pdg], + pdg_R->pd_pwr, + pdg_R->pd_step, + pdg_R->pd_points, tmpR, type); + break; + default: + return -EINVAL; + } + + /* Interpolate between curves + * of surounding freq piers to + * get the final curve for this + * pd gain. Re-use tmpL for interpolation + * output */ + for (i = 0; (i < (u16) (table_max[pdg] - table_min[pdg])) && + (i < AR5K_EEPROM_POWER_TABLE_SIZE); i++) { + tmpL[i] = (u8) ath5k_get_interpolated_value(target, + (s16) pcinfo_L->freq, + (s16) pcinfo_R->freq, + (s16) tmpL[i], + (s16) tmpR[i]); + } } + /* Now we have a set of curves for this + * channel on tmpL (x range is table_max - table_min + * and y values are tmpL[pdg][]) sorted in the same + * order as EEPROM (because we've used the backmaping). + * So for RF5112 it's from higher power to lower power + * and for RF2413 it's from lower power to higher power. + * For RF5111 we only have one curve. */ + + /* Fill min and max power levels for this + * channel by interpolating the values on + * surounding channels to complete the dataset */ + ah->ah_txpower.txp_min_pwr = ath5k_get_interpolated_value(target, + (s16) pcinfo_L->freq, + (s16) pcinfo_R->freq, + pcinfo_L->min_pwr, pcinfo_R->min_pwr); + + ah->ah_txpower.txp_max_pwr = ath5k_get_interpolated_value(target, + (s16) pcinfo_L->freq, + (s16) pcinfo_R->freq, + pcinfo_L->max_pwr, pcinfo_R->max_pwr); + + /* We are ready to go, fill PCDAC/PDADC + * table and write settings on hardware */ + switch (type) { + case AR5K_PWRTABLE_LINEAR_PCDAC: + /* For RF5112 we can have one or two curves + * and each curve covers a certain power lvl + * range so we need to do some more processing */ + ath5k_combine_linear_pcdac_curves(ah, table_min, table_max, + ee->ee_pd_gains[ee_mode]); + + /* Set txp.offset so that we can + * match max power value with max + * table index */ + ah->ah_txpower.txp_offset = 64 - (table_max[0] / 2); + + /* Write settings on hw */ + ath5k_setup_pcdac_table(ah); + break; + case AR5K_PWRTABLE_PWR_TO_PCDAC: + /* We are done for RF5111 since it has only + * one curve, just fit the curve on the table */ + ath5k_fill_pwr_to_pcdac_table(ah, table_min, table_max); + + /* No rate powertable adjustment for RF5111 */ + ah->ah_txpower.txp_min_idx = 0; + ah->ah_txpower.txp_offset = 0; + + /* Write settings on hw */ + ath5k_setup_pcdac_table(ah); + break; + case AR5K_PWRTABLE_PWR_TO_PDADC: + /* Set PDADC boundaries and fill + * final PDADC table */ + ath5k_combine_pwr_to_pdadc_curves(ah, table_min, table_max, + ee->ee_pd_gains[ee_mode]); + + /* Write settings on hw */ + ath5k_setup_pwr_to_pdadc_table(ah, pdg, pdg_curve_to_idx); + + /* Set txp.offset, note that table_min + * can be negative */ + ah->ah_txpower.txp_offset = table_min[0]; + break; + default: + return -EINVAL; + } + + return 0; +} + + +/* + * Per-rate tx power setting + * + * This is the code that sets the desired tx power (below + * maximum) on hw for each rate (we also have TPC that sets + * power per packet). We do that by providing an index on the + * PCDAC/PDADC table we set up. + */ + +/* + * Set rate power table + * + * For now we only limit txpower based on maximum tx power + * supported by hw (what's inside rate_info). We need to limit + * this even more, based on regulatory domain etc. + * + * Rate power table contains indices to PCDAC/PDADC table (0.5dB steps) + * and is indexed as follows: + * rates[0] - rates[7] -> OFDM rates + * rates[8] - rates[14] -> CCK rates + * rates[15] -> XR rates (they all have the same power) + */ +static void +ath5k_setup_rate_powertable(struct ath5k_hw *ah, u16 max_pwr, + struct ath5k_rate_pcal_info *rate_info, + u8 ee_mode) +{ + unsigned int i; + u16 *rates; + + /* max_pwr is power level we got from driver/user in 0.5dB + * units, switch to 0.25dB units so we can compare */ + max_pwr *= 2; + max_pwr = min(max_pwr, (u16) ah->ah_txpower.txp_max_pwr) / 2; + + /* apply rate limits */ + rates = ah->ah_txpower.txp_rates_power_table; + + /* OFDM rates 6 to 24Mb/s */ + for (i = 0; i < 5; i++) + rates[i] = min(max_pwr, rate_info->target_power_6to24); + + /* Rest OFDM rates */ + rates[5] = min(rates[0], rate_info->target_power_36); + rates[6] = min(rates[0], rate_info->target_power_48); + rates[7] = min(rates[0], rate_info->target_power_54); + + /* CCK rates */ + /* 1L */ + rates[8] = min(rates[0], rate_info->target_power_6to24); + /* 2L */ + rates[9] = min(rates[0], rate_info->target_power_36); + /* 2S */ + rates[10] = min(rates[0], rate_info->target_power_36); + /* 5L */ + rates[11] = min(rates[0], rate_info->target_power_48); + /* 5S */ + rates[12] = min(rates[0], rate_info->target_power_48); + /* 11L */ + rates[13] = min(rates[0], rate_info->target_power_54); + /* 11S */ + rates[14] = min(rates[0], rate_info->target_power_54); + + /* XR rates */ + rates[15] = min(rates[0], rate_info->target_power_6to24); + + /* CCK rates have different peak to average ratio + * so we have to tweak their power so that gainf + * correction works ok. For this we use OFDM to + * CCK delta from eeprom */ + if ((ee_mode == AR5K_EEPROM_MODE_11G) && + (ah->ah_phy_revision < AR5K_SREV_PHY_5212A)) + for (i = 8; i <= 15; i++) + rates[i] -= ah->ah_txpower.txp_cck_ofdm_gainf_delta; + + ah->ah_txpower.txp_min_pwr = rates[7]; + ah->ah_txpower.txp_max_pwr = rates[0]; + ah->ah_txpower.txp_ofdm = rates[7]; +} + + +/* + * Set transmition power + */ +int +ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, + u8 ee_mode, u8 txpower) +{ + struct ath5k_rate_pcal_info rate_info; + u8 type; + int ret; + + ATH5K_TRACE(ah->ah_sc); + if (txpower > AR5K_TUNE_MAX_TXPOWER) { + ATH5K_ERR(ah->ah_sc, "invalid tx power: %u\n", txpower); + return -EINVAL; + } + if (txpower == 0) + txpower = AR5K_TUNE_DEFAULT_TXPOWER; + + /* Reset TX power values */ + memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower)); + ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER; + ah->ah_txpower.txp_min_pwr = 0; + ah->ah_txpower.txp_max_pwr = AR5K_TUNE_MAX_TXPOWER; + + /* Initialize TX power table */ + switch (ah->ah_radio) { + case AR5K_RF5111: + type = AR5K_PWRTABLE_PWR_TO_PCDAC; + break; + case AR5K_RF5112: + type = AR5K_PWRTABLE_LINEAR_PCDAC; + break; + case AR5K_RF2413: + case AR5K_RF5413: + case AR5K_RF2316: + case AR5K_RF2317: + case AR5K_RF2425: + type = AR5K_PWRTABLE_PWR_TO_PDADC; + break; + default: + return -EINVAL; + } + + /* FIXME: Only on channel/mode change */ + ret = ath5k_setup_channel_powertable(ah, channel, ee_mode, type); + if (ret) + return ret; + + /* Limit max power if we have a CTL available */ + ath5k_get_max_ctl_power(ah, channel); + + /* FIXME: Tx power limit for this regdomain + * XXX: Mac80211/CRDA will do that anyway ? */ + + /* FIXME: Antenna reduction stuff */ + + /* FIXME: Limit power on turbo modes */ + + /* FIXME: TPC scale reduction */ + + /* Get surounding channels for per-rate power table + * calibration */ + ath5k_get_rate_pcal_data(ah, channel, &rate_info); + + /* Setup rate power table */ + ath5k_setup_rate_powertable(ah, txpower, &rate_info, ee_mode); + + /* Write rate power table on hw */ ath5k_hw_reg_write(ah, AR5K_TXPOWER_OFDM(3, 24) | AR5K_TXPOWER_OFDM(2, 16) | AR5K_TXPOWER_OFDM(1, 8) | AR5K_TXPOWER_OFDM(0, 0), AR5K_PHY_TXPOWER_RATE1); @@ -1536,26 +2566,34 @@ ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, AR5K_TXPOWER_CCK(13, 16) | AR5K_TXPOWER_CCK(12, 8) | AR5K_TXPOWER_CCK(11, 0), AR5K_PHY_TXPOWER_RATE4); - if (ah->ah_txpower.txp_tpc) + /* FIXME: TPC support */ + if (ah->ah_txpower.txp_tpc) { ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX_TPC_ENABLE | AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX); - else + + ath5k_hw_reg_write(ah, + AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_ACK) | + AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CTS) | + AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CHIRP), + AR5K_TPC); + } else { ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX | AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX); + } return 0; } -int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, unsigned int power) +int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 mode, u8 txpower) { /*Just a try M.F.*/ struct ieee80211_channel *channel = &ah->ah_current_channel; ATH5K_TRACE(ah->ah_sc); ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_TXPOWER, - "changing txpower to %d\n", power); + "changing txpower to %d\n", txpower); - return ath5k_hw_txpower(ah, channel, power); + return ath5k_hw_txpower(ah, channel, mode, txpower); } #undef _ATH5K_PHY diff --git a/drivers/net/wireless/ath5k/reg.h b/drivers/net/wireless/ath5k/reg.h index 2dc008e1022..7070d1543cd 100644 --- a/drivers/net/wireless/ath5k/reg.h +++ b/drivers/net/wireless/ath5k/reg.h @@ -1553,6 +1553,19 @@ /*===5212 Specific PCU registers===*/ +/* + * Transmit power control register + */ +#define AR5K_TPC 0x80e8 +#define AR5K_TPC_ACK 0x0000003f /* ack frames */ +#define AR5K_TPC_ACK_S 0 +#define AR5K_TPC_CTS 0x00003f00 /* cts frames */ +#define AR5K_TPC_CTS_S 8 +#define AR5K_TPC_CHIRP 0x003f0000 /* chirp frames */ +#define AR5K_TPC_CHIRP_S 16 +#define AR5K_TPC_DOPPLER 0x0f000000 /* doppler chirp span */ +#define AR5K_TPC_DOPPLER_S 24 + /* * XR (eXtended Range) mode register */ @@ -2550,6 +2563,12 @@ #define AR5K_PHY_TPC_RG1 0xa258 #define AR5K_PHY_TPC_RG1_NUM_PD_GAIN 0x0000c000 #define AR5K_PHY_TPC_RG1_NUM_PD_GAIN_S 14 +#define AR5K_PHY_TPC_RG1_PDGAIN_1 0x00030000 +#define AR5K_PHY_TPC_RG1_PDGAIN_1_S 16 +#define AR5K_PHY_TPC_RG1_PDGAIN_2 0x000c0000 +#define AR5K_PHY_TPC_RG1_PDGAIN_2_S 18 +#define AR5K_PHY_TPC_RG1_PDGAIN_3 0x00300000 +#define AR5K_PHY_TPC_RG1_PDGAIN_3_S 20 #define AR5K_PHY_TPC_RG5 0xa26C #define AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP 0x0000000F diff --git a/drivers/net/wireless/ath5k/reset.c b/drivers/net/wireless/ath5k/reset.c index 685dc213eda..7a17d31b2fd 100644 --- a/drivers/net/wireless/ath5k/reset.c +++ b/drivers/net/wireless/ath5k/reset.c @@ -664,29 +664,35 @@ static void ath5k_hw_commit_eeprom_settings(struct ath5k_hw *ah, struct ieee80211_channel *channel, u8 *ant, u8 ee_mode) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + s16 cck_ofdm_pwr_delta; - /* Set CCK to OFDM power delta */ - if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) { - int16_t cck_ofdm_pwr_delta; - - /* Adjust power delta for channel 14 */ - if (channel->center_freq == 2484) - cck_ofdm_pwr_delta = - ((ee->ee_cck_ofdm_power_delta - - ee->ee_scaled_cck_delta) * 2) / 10; - else - cck_ofdm_pwr_delta = - (ee->ee_cck_ofdm_power_delta * 2) / 10; + /* Adjust power delta for channel 14 */ + if (channel->center_freq == 2484) + cck_ofdm_pwr_delta = + ((ee->ee_cck_ofdm_power_delta - + ee->ee_scaled_cck_delta) * 2) / 10; + else + cck_ofdm_pwr_delta = + (ee->ee_cck_ofdm_power_delta * 2) / 10; + /* Set CCK to OFDM power delta on tx power + * adjustment register */ + if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) { if (channel->hw_value == CHANNEL_G) ath5k_hw_reg_write(ah, - AR5K_REG_SM((ee->ee_cck_ofdm_power_delta * -1), + AR5K_REG_SM((ee->ee_cck_ofdm_gain_delta * -1), AR5K_PHY_TX_PWR_ADJ_CCK_GAIN_DELTA) | AR5K_REG_SM((cck_ofdm_pwr_delta * -1), AR5K_PHY_TX_PWR_ADJ_CCK_PCDAC_INDEX), AR5K_PHY_TX_PWR_ADJ); else ath5k_hw_reg_write(ah, 0, AR5K_PHY_TX_PWR_ADJ); + } else { + /* For older revs we scale power on sw during tx power + * setup */ + ah->ah_txpower.txp_cck_ofdm_pwr_delta = cck_ofdm_pwr_delta; + ah->ah_txpower.txp_cck_ofdm_gainf_delta = + ee->ee_cck_ofdm_gain_delta; } /* Set antenna idle switch table */ @@ -994,7 +1000,8 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, /* * Set TX power (FIXME) */ - ret = ath5k_hw_txpower(ah, channel, AR5K_TUNE_DEFAULT_TXPOWER); + ret = ath5k_hw_txpower(ah, channel, ee_mode, + AR5K_TUNE_DEFAULT_TXPOWER); if (ret) return ret; -- cgit v1.2.3 From 3b85875a252dbbd95c2e04d73639719a0a79634e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 12 Mar 2009 09:55:09 +0100 Subject: nl80211: rework locking When I added scanning to cfg80211, we got a lock dependency like this: rtnl --> cfg80211_mtx nl80211, on the other hand, has the reverse lock dependency: cfg80211_mtx --> rtnl which clearly is a bad idea. This patch reworks nl80211 to take these two locks in the other order to fix the possible, and easily triggerable, deadlock. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 271 ++++++++++++++++++++++++++++++------------------- 1 file changed, 166 insertions(+), 105 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 58ee1b1aff8..a3ecf8d7389 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -602,9 +602,12 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) memset(¶ms, 0, sizeof(params)); + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto unlock_rtnl; + ifindex = dev->ifindex; type = dev->ieee80211_ptr->iftype; dev_put(dev); @@ -641,17 +644,17 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) if (!err) flags = &_flags; } - rtnl_lock(); + err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, type, flags, ¶ms); dev = __dev_get_by_index(&init_net, ifindex); WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != type)); - rtnl_unlock(); - unlock: cfg80211_put_dev(drv); + unlock_rtnl: + rtnl_unlock(); return err; } @@ -674,9 +677,13 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } + rtnl_lock(); + drv = cfg80211_get_dev_from_info(info); - if (IS_ERR(drv)) - return PTR_ERR(drv); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto unlock_rtnl; + } if (!drv->ops->add_virtual_intf || !(drv->wiphy.interface_modes & (1 << type))) { @@ -690,18 +697,17 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); } - rtnl_lock(); err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, &flags); err = drv->ops->add_virtual_intf(&drv->wiphy, nla_data(info->attrs[NL80211_ATTR_IFNAME]), type, err ? NULL : &flags, ¶ms); - rtnl_unlock(); - unlock: cfg80211_put_dev(drv); + unlock_rtnl: + rtnl_unlock(); return err; } @@ -711,9 +717,11 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) int ifindex, err; struct net_device *dev; + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto unlock_rtnl; ifindex = dev->ifindex; dev_put(dev); @@ -722,12 +730,12 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) goto out; } - rtnl_lock(); err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex); - rtnl_unlock(); out: cfg80211_put_dev(drv); + unlock_rtnl: + rtnl_unlock(); return err; } @@ -779,9 +787,11 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_MAC]) mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto unlock_rtnl; if (!drv->ops->get_key) { err = -EOPNOTSUPP; @@ -809,10 +819,8 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) if (mac_addr) NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); - rtnl_lock(); err = drv->ops->get_key(&drv->wiphy, dev, key_idx, mac_addr, &cookie, get_key_callback); - rtnl_unlock(); if (err) goto out; @@ -830,6 +838,9 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) out: cfg80211_put_dev(drv); dev_put(dev); + unlock_rtnl: + rtnl_unlock(); + return err; } @@ -858,9 +869,11 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) !info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) return -EINVAL; + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto unlock_rtnl; if (info->attrs[NL80211_ATTR_KEY_DEFAULT]) func = drv->ops->set_default_key; @@ -872,13 +885,15 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) goto out; } - rtnl_lock(); err = func(&drv->wiphy, dev, key_idx); - rtnl_unlock(); out: cfg80211_put_dev(drv); dev_put(dev); + + unlock_rtnl: + rtnl_unlock(); + return err; } @@ -948,22 +963,25 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto unlock_rtnl; if (!drv->ops->add_key) { err = -EOPNOTSUPP; goto out; } - rtnl_lock(); err = drv->ops->add_key(&drv->wiphy, dev, key_idx, mac_addr, ¶ms); - rtnl_unlock(); out: cfg80211_put_dev(drv); dev_put(dev); + unlock_rtnl: + rtnl_unlock(); + return err; } @@ -984,22 +1002,26 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_MAC]) mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto unlock_rtnl; if (!drv->ops->del_key) { err = -EOPNOTSUPP; goto out; } - rtnl_lock(); err = drv->ops->del_key(&drv->wiphy, dev, key_idx, mac_addr); - rtnl_unlock(); out: cfg80211_put_dev(drv); dev_put(dev); + + unlock_rtnl: + rtnl_unlock(); + return err; } @@ -1013,9 +1035,11 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) struct beacon_parameters params; int haveinfo = 0; + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto unlock_rtnl; switch (info->genlhdr->cmd) { case NL80211_CMD_NEW_BEACON: @@ -1076,13 +1100,14 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) goto out; } - rtnl_lock(); err = call(&drv->wiphy, dev, ¶ms); - rtnl_unlock(); out: cfg80211_put_dev(drv); dev_put(dev); + unlock_rtnl: + rtnl_unlock(); + return err; } @@ -1092,22 +1117,25 @@ static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) int err; struct net_device *dev; + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto unlock_rtnl; if (!drv->ops->del_beacon) { err = -EOPNOTSUPP; goto out; } - rtnl_lock(); err = drv->ops->del_beacon(&drv->wiphy, dev); - rtnl_unlock(); out: cfg80211_put_dev(drv); dev_put(dev); + unlock_rtnl: + rtnl_unlock(); + return err; } @@ -1273,14 +1301,18 @@ static int nl80211_dump_station(struct sk_buff *skb, return -EINVAL; } - netdev = dev_get_by_index(&init_net, ifidx); - if (!netdev) - return -ENODEV; + rtnl_lock(); + + netdev = __dev_get_by_index(&init_net, ifidx); + if (!netdev) { + err = -ENODEV; + goto out_rtnl; + } dev = cfg80211_get_dev_from_ifindex(ifidx); if (IS_ERR(dev)) { err = PTR_ERR(dev); - goto out_put_netdev; + goto out_rtnl; } if (!dev->ops->dump_station) { @@ -1288,15 +1320,13 @@ static int nl80211_dump_station(struct sk_buff *skb, goto out_err; } - rtnl_lock(); - while (1) { err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx, mac_addr, &sinfo); if (err == -ENOENT) break; if (err) - goto out_err_rtnl; + goto out_err; if (nl80211_send_station(skb, NETLINK_CB(cb->skb).pid, @@ -1312,12 +1342,10 @@ static int nl80211_dump_station(struct sk_buff *skb, out: cb->args[1] = sta_idx; err = skb->len; - out_err_rtnl: - rtnl_unlock(); out_err: cfg80211_put_dev(dev); - out_put_netdev: - dev_put(netdev); + out_rtnl: + rtnl_unlock(); return err; } @@ -1338,19 +1366,18 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; if (!drv->ops->get_station) { err = -EOPNOTSUPP; goto out; } - rtnl_lock(); err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &sinfo); - rtnl_unlock(); - if (err) goto out; @@ -1367,10 +1394,12 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) out_free: nlmsg_free(msg); - out: cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } @@ -1438,9 +1467,11 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) params.plink_action = nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, ¶ms.vlan); if (err) @@ -1451,15 +1482,16 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) goto out; } - rtnl_lock(); err = drv->ops->change_station(&drv->wiphy, dev, mac_addr, ¶ms); - rtnl_unlock(); out: if (params.vlan) dev_put(params.vlan); cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } @@ -1501,9 +1533,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) ¶ms.station_flags)) return -EINVAL; + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, ¶ms.vlan); if (err) @@ -1514,15 +1548,16 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) goto out; } - rtnl_lock(); err = drv->ops->add_station(&drv->wiphy, dev, mac_addr, ¶ms); - rtnl_unlock(); out: if (params.vlan) dev_put(params.vlan); cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } @@ -1536,22 +1571,25 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_MAC]) mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; if (!drv->ops->del_station) { err = -EOPNOTSUPP; goto out; } - rtnl_lock(); err = drv->ops->del_station(&drv->wiphy, dev, mac_addr); - rtnl_unlock(); out: cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } @@ -1632,14 +1670,18 @@ static int nl80211_dump_mpath(struct sk_buff *skb, return -EINVAL; } - netdev = dev_get_by_index(&init_net, ifidx); - if (!netdev) - return -ENODEV; + rtnl_lock(); + + netdev = __dev_get_by_index(&init_net, ifidx); + if (!netdev) { + err = -ENODEV; + goto out_rtnl; + } dev = cfg80211_get_dev_from_ifindex(ifidx); if (IS_ERR(dev)) { err = PTR_ERR(dev); - goto out_put_netdev; + goto out_rtnl; } if (!dev->ops->dump_mpath) { @@ -1647,15 +1689,13 @@ static int nl80211_dump_mpath(struct sk_buff *skb, goto out_err; } - rtnl_lock(); - while (1) { err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx, dst, next_hop, &pinfo); if (err == -ENOENT) break; if (err) - goto out_err_rtnl; + goto out_err; if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, NLM_F_MULTI, @@ -1670,12 +1710,10 @@ static int nl80211_dump_mpath(struct sk_buff *skb, out: cb->args[1] = path_idx; err = skb->len; - out_err_rtnl: - rtnl_unlock(); out_err: cfg80211_put_dev(dev); - out_put_netdev: - dev_put(netdev); + out_rtnl: + rtnl_unlock(); return err; } @@ -1697,19 +1735,18 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) dst = nla_data(info->attrs[NL80211_ATTR_MAC]); + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; if (!drv->ops->get_mpath) { err = -EOPNOTSUPP; goto out; } - rtnl_lock(); err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo); - rtnl_unlock(); - if (err) goto out; @@ -1726,10 +1763,12 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) out_free: nlmsg_free(msg); - out: cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } @@ -1750,22 +1789,25 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) dst = nla_data(info->attrs[NL80211_ATTR_MAC]); next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]); + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; if (!drv->ops->change_mpath) { err = -EOPNOTSUPP; goto out; } - rtnl_lock(); err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop); - rtnl_unlock(); out: cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) @@ -1785,22 +1827,25 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) dst = nla_data(info->attrs[NL80211_ATTR_MAC]); next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]); + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; if (!drv->ops->add_mpath) { err = -EOPNOTSUPP; goto out; } - rtnl_lock(); err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop); - rtnl_unlock(); out: cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } @@ -1814,22 +1859,25 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_MAC]) dst = nla_data(info->attrs[NL80211_ATTR_MAC]); + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; if (!drv->ops->del_mpath) { err = -EOPNOTSUPP; goto out; } - rtnl_lock(); err = drv->ops->del_mpath(&drv->wiphy, dev, dst); - rtnl_unlock(); out: cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } @@ -1862,22 +1910,25 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); } + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; if (!drv->ops->change_bss) { err = -EOPNOTSUPP; goto out; } - rtnl_lock(); err = drv->ops->change_bss(&drv->wiphy, dev, ¶ms); - rtnl_unlock(); out: cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } @@ -1972,10 +2023,12 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, struct nlattr *pinfoattr; struct sk_buff *msg; + rtnl_lock(); + /* Look up our device */ err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; if (!drv->ops->get_mesh_params) { err = -EOPNOTSUPP; @@ -1983,9 +2036,7 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, } /* Get the mesh params */ - rtnl_lock(); err = drv->ops->get_mesh_params(&drv->wiphy, dev, &cur_params); - rtnl_unlock(); if (err) goto out; @@ -2034,13 +2085,16 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, err = genlmsg_unicast(msg, info->snd_pid); goto out; -nla_put_failure: + nla_put_failure: genlmsg_cancel(msg, hdr); err = -EMSGSIZE; -out: + out: /* Cleanup */ cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } @@ -2087,9 +2141,11 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) parent_attr, nl80211_meshconf_params_policy)) return -EINVAL; + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; if (!drv->ops->set_mesh_params) { err = -EOPNOTSUPP; @@ -2136,14 +2192,15 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) nla_get_u16); /* Apply changes */ - rtnl_lock(); err = drv->ops->set_mesh_params(&drv->wiphy, dev, &cfg, mask); - rtnl_unlock(); out: /* cleanup */ cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } @@ -2310,19 +2367,22 @@ static int nl80211_set_mgmt_extra_ie(struct sk_buff *skb, params.ies_len = nla_len(info->attrs[NL80211_ATTR_IE]); } + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; - if (drv->ops->set_mgmt_extra_ie) { - rtnl_lock(); + if (drv->ops->set_mgmt_extra_ie) err = drv->ops->set_mgmt_extra_ie(&drv->wiphy, dev, ¶ms); - rtnl_unlock(); - } else + else err = -EOPNOTSUPP; cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } @@ -2339,9 +2399,11 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) enum ieee80211_band band; size_t ie_len; + rtnl_lock(); + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) - return err; + goto out_rtnl; wiphy = &drv->wiphy; @@ -2350,11 +2412,9 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) goto out; } - rtnl_lock(); - if (drv->scan_req) { err = -EBUSY; - goto out_unlock; + goto out; } if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { @@ -2362,7 +2422,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) n_channels++; if (!n_channels) { err = -EINVAL; - goto out_unlock; + goto out; } } else { for (band = 0; band < IEEE80211_NUM_BANDS; band++) @@ -2376,7 +2436,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) if (n_ssids > wiphy->max_scan_ssids) { err = -EINVAL; - goto out_unlock; + goto out; } if (info->attrs[NL80211_ATTR_IE]) @@ -2390,7 +2450,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) + ie_len, GFP_KERNEL); if (!request) { err = -ENOMEM; - goto out_unlock; + goto out; } request->channels = (void *)((char *)request + sizeof(*request)); @@ -2461,11 +2521,12 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) drv->scan_req = NULL; kfree(request); } - out_unlock: - rtnl_unlock(); out: cfg80211_put_dev(drv); dev_put(dev); + out_rtnl: + rtnl_unlock(); + return err; } -- cgit v1.2.3 From 019fb97d47896c0ead4a77f55e5350c2750f675f Mon Sep 17 00:00:00 2001 From: Mohamed Abbas Date: Tue, 17 Mar 2009 21:59:18 -0700 Subject: iwlagn: use changed in mac_config In function iwl_mac_config use changed flag to call only the affected functions. This patch also allow user to cache channel, txpower and power value when the interface is not ready and apply the changes once the interface ready. Signed-off-by: Mohamed Abbas Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn.c | 161 ++++++++++++++++---------------- drivers/net/wireless/iwlwifi/iwl-core.c | 4 + 2 files changed, 85 insertions(+), 80 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 0db3bc011ac..4d2ed52af16 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -1567,9 +1567,8 @@ static void iwl_alive_start(struct iwl_priv *priv) if (iwl_is_associated(priv)) { struct iwl_rxon_cmd *active_rxon = (struct iwl_rxon_cmd *)&priv->active_rxon; - - memcpy(&priv->staging_rxon, &priv->active_rxon, - sizeof(priv->staging_rxon)); + /* apply any changes in staging */ + priv->staging_rxon.filter_flags |= RXON_FILTER_ASSOC_MSK; active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; } else { /* Initialize our rx_config data */ @@ -2184,110 +2183,112 @@ static int iwl_mac_config(struct ieee80211_hw *hw, u32 changed) struct iwl_priv *priv = hw->priv; const struct iwl_channel_info *ch_info; struct ieee80211_conf *conf = &hw->conf; - unsigned long flags; + unsigned long flags = 0; int ret = 0; - u16 channel; + u16 ch; + int scan_active = 0; mutex_lock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "enter to channel %d\n", conf->channel->hw_value); - - priv->current_ht_config.is_ht = conf_is_ht(conf); + IWL_DEBUG_MAC80211(priv, "enter to channel %d changed 0x%X\n", + conf->channel->hw_value, changed); - if (conf->radio_enabled && iwl_radio_kill_sw_enable_radio(priv)) { - IWL_DEBUG_MAC80211(priv, "leave - RF-KILL - waiting for uCode\n"); - goto out; + if (unlikely(!priv->cfg->mod_params->disable_hw_scan && + test_bit(STATUS_SCANNING, &priv->status))) { + scan_active = 1; + IWL_DEBUG_MAC80211(priv, "leave - scanning\n"); } - if (!conf->radio_enabled) - iwl_radio_kill_sw_disable_radio(priv); - if (!iwl_is_ready(priv)) { - IWL_DEBUG_MAC80211(priv, "leave - not ready\n"); - ret = -EIO; - goto out; - } + /* during scanning mac80211 will delay channel setting until + * scan finish with changed = 0 + */ + if (!changed || (changed & IEEE80211_CONF_CHANGE_CHANNEL)) { + if (scan_active) + goto set_ch_out; + + ch = ieee80211_frequency_to_channel(conf->channel->center_freq); + ch_info = iwl_get_channel_info(priv, conf->channel->band, ch); + if (!is_channel_valid(ch_info)) { + IWL_DEBUG_MAC80211(priv, "leave - invalid channel\n"); + ret = -EINVAL; + goto set_ch_out; + } - if (unlikely(!priv->cfg->mod_params->disable_hw_scan && - test_bit(STATUS_SCANNING, &priv->status))) { - IWL_DEBUG_MAC80211(priv, "leave - scanning\n"); - mutex_unlock(&priv->mutex); - return 0; - } + if (priv->iw_mode == NL80211_IFTYPE_ADHOC && + !is_channel_ibss(ch_info)) { + IWL_ERR(priv, "channel %d in band %d not " + "IBSS channel\n", + conf->channel->hw_value, conf->channel->band); + ret = -EINVAL; + goto set_ch_out; + } - channel = ieee80211_frequency_to_channel(conf->channel->center_freq); - ch_info = iwl_get_channel_info(priv, conf->channel->band, channel); - if (!is_channel_valid(ch_info)) { - IWL_DEBUG_MAC80211(priv, "leave - invalid channel\n"); - ret = -EINVAL; - goto out; - } + priv->current_ht_config.is_ht = conf_is_ht(conf); - if (priv->iw_mode == NL80211_IFTYPE_ADHOC && - !is_channel_ibss(ch_info)) { - IWL_ERR(priv, "channel %d in band %d not IBSS channel\n", - conf->channel->hw_value, conf->channel->band); - ret = -EINVAL; - goto out; - } + spin_lock_irqsave(&priv->lock, flags); - spin_lock_irqsave(&priv->lock, flags); + /* if we are switching from ht to 2.4 clear flags + * from any ht related info since 2.4 does not + * support ht */ + if ((le16_to_cpu(priv->staging_rxon.channel) != ch)) + priv->staging_rxon.flags = 0; - /* if we are switching from ht to 2.4 clear flags - * from any ht related info since 2.4 does not - * support ht */ - if ((le16_to_cpu(priv->staging_rxon.channel) != channel) -#ifdef IEEE80211_CONF_CHANNEL_SWITCH - && !(conf->flags & IEEE80211_CONF_CHANNEL_SWITCH) -#endif - ) - priv->staging_rxon.flags = 0; + iwl_set_rxon_channel(priv, conf->channel); + + iwl_set_flags_for_band(priv, conf->channel->band); + spin_unlock_irqrestore(&priv->lock, flags); + set_ch_out: + /* The list of supported rates and rate mask can be different + * for each band; since the band may have changed, reset + * the rate mask to what mac80211 lists */ + iwl_set_rate(priv); + } - iwl_set_rxon_channel(priv, conf->channel); + if (changed & IEEE80211_CONF_CHANGE_PS) { + if (conf->flags & IEEE80211_CONF_PS) + ret = iwl_power_set_user_mode(priv, IWL_POWER_INDEX_3); + else + ret = iwl_power_set_user_mode(priv, IWL_POWER_MODE_CAM); + if (ret) + IWL_DEBUG_MAC80211(priv, "Error setting power level\n"); - iwl_set_flags_for_band(priv, conf->channel->band); + } - /* The list of supported rates and rate mask can be different - * for each band; since the band may have changed, reset - * the rate mask to what mac80211 lists */ - iwl_set_rate(priv); + if (changed & IEEE80211_CONF_CHANGE_POWER) { + IWL_DEBUG_MAC80211(priv, "TX Power old=%d new=%d\n", + priv->tx_power_user_lmt, conf->power_level); - spin_unlock_irqrestore(&priv->lock, flags); + iwl_set_tx_power(priv, conf->power_level, false); + } -#ifdef IEEE80211_CONF_CHANNEL_SWITCH - if (conf->flags & IEEE80211_CONF_CHANNEL_SWITCH) { - iwl_hw_channel_switch(priv, conf->channel); - goto out; + /* call to ensure that 4965 rx_chain is set properly in monitor mode */ + iwl_set_rxon_chain(priv); + + if (changed & IEEE80211_CONF_CHANGE_RADIO_ENABLED) { + if (conf->radio_enabled && + iwl_radio_kill_sw_enable_radio(priv)) { + IWL_DEBUG_MAC80211(priv, "leave - RF-KILL - " + "waiting for uCode\n"); + goto out; + } + + if (!conf->radio_enabled) + iwl_radio_kill_sw_disable_radio(priv); } -#endif if (!conf->radio_enabled) { IWL_DEBUG_MAC80211(priv, "leave - radio disabled\n"); goto out; } - if (iwl_is_rfkill(priv)) { - IWL_DEBUG_MAC80211(priv, "leave - RF kill\n"); - ret = -EIO; + if (!iwl_is_ready(priv)) { + IWL_DEBUG_MAC80211(priv, "leave - not ready\n"); goto out; } - if (conf->flags & IEEE80211_CONF_PS) - ret = iwl_power_set_user_mode(priv, IWL_POWER_INDEX_3); - else - ret = iwl_power_set_user_mode(priv, IWL_POWER_MODE_CAM); - if (ret) - IWL_DEBUG_MAC80211(priv, "Error setting power level\n"); - - IWL_DEBUG_MAC80211(priv, "TX Power old=%d new=%d\n", - priv->tx_power_user_lmt, conf->power_level); - - iwl_set_tx_power(priv, conf->power_level, false); - - iwl_set_rate(priv); - - /* call to ensure that 4965 rx_chain is set properly in monitor mode */ - iwl_set_rxon_chain(priv); + if (scan_active) + goto out; if (memcmp(&priv->active_rxon, &priv->staging_rxon, sizeof(priv->staging_rxon))) @@ -2295,9 +2296,9 @@ static int iwl_mac_config(struct ieee80211_hw *hw, u32 changed) else IWL_DEBUG_INFO(priv, "No re-sending same RXON configuration.\n"); - IWL_DEBUG_MAC80211(priv, "leave\n"); out: + IWL_DEBUG_MAC80211(priv, "leave\n"); mutex_unlock(&priv->mutex); return ret; } diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 085e9cf1cac..bcdecb11080 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -1437,6 +1437,10 @@ int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force) priv->tx_power_user_lmt = tx_power; + /* if nic is not up don't send command */ + if (!iwl_is_ready_rf(priv)) + return ret; + if (force && priv->cfg->ops->lib->send_tx_power) ret = priv->cfg->ops->lib->send_tx_power(priv); -- cgit v1.2.3 From 37fec3846a5a8b098e35c44ee858407bab0df43f Mon Sep 17 00:00:00 2001 From: Mohamed Abbas Date: Tue, 17 Mar 2009 21:51:42 -0700 Subject: iwl3945: use changed in iwl3945_mac_config In function iwl3945_mac_config use changed flag to call only the affected functions. Signed-off-by: Mohamed Abbas Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl3945-base.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 4465320f273..16ecf03a155 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -3773,15 +3773,19 @@ static int iwl3945_mac_config(struct ieee80211_hw *hw, u32 changed) } #endif - if (conf->radio_enabled && iwl_radio_kill_sw_enable_radio(priv)) { - IWL_DEBUG_MAC80211(priv, "leave - RF-KILL - waiting for uCode\n"); - goto out; - } + if (changed & IEEE80211_CONF_CHANGE_RADIO_ENABLED) { + if (conf->radio_enabled && + iwl_radio_kill_sw_enable_radio(priv)) { + IWL_DEBUG_MAC80211(priv, "leave - RF-KILL - " + "waiting for uCode\n"); + goto out; + } - if (!conf->radio_enabled) { - iwl_radio_kill_sw_disable_radio(priv); - IWL_DEBUG_MAC80211(priv, "leave - radio disabled\n"); - goto out; + if (!conf->radio_enabled) { + iwl_radio_kill_sw_disable_radio(priv); + IWL_DEBUG_MAC80211(priv, "leave - radio disabled\n"); + goto out; + } } if (iwl_is_rfkill(priv)) { -- cgit v1.2.3 From 5c2207c64209be2fe0d6b43ada2e41b28a948015 Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Tue, 17 Mar 2009 21:51:43 -0700 Subject: iwlwifi: return 0 for AMPDU_TX/RX_STOP request if NIC is going down When receive IEEE80211_AMPDU_RX_STOP or IEEE80211_AMPDU_TX_STOP request in iwl_mac_ampdu_action() from mac80211; check STATUS_EXIT_PENDING bit, if NIC is on the way out, then return 0 back to mac80211, this can prevent mac80211 report HW error incorrectly. Signed-off-by: Wey-Yi Guy Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 4d2ed52af16..c1482852ea4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -2683,6 +2683,7 @@ static int iwl_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u16 tid, u16 *ssn) { struct iwl_priv *priv = hw->priv; + int ret; IWL_DEBUG_HT(priv, "A-MPDU action on addr %pM tid %d\n", sta->addr, tid); @@ -2696,13 +2697,21 @@ static int iwl_mac_ampdu_action(struct ieee80211_hw *hw, return iwl_sta_rx_agg_start(priv, sta->addr, tid, *ssn); case IEEE80211_AMPDU_RX_STOP: IWL_DEBUG_HT(priv, "stop Rx\n"); - return iwl_sta_rx_agg_stop(priv, sta->addr, tid); + ret = iwl_sta_rx_agg_stop(priv, sta->addr, tid); + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return 0; + else + return ret; case IEEE80211_AMPDU_TX_START: IWL_DEBUG_HT(priv, "start Tx\n"); return iwl_tx_agg_start(priv, sta->addr, tid, ssn); case IEEE80211_AMPDU_TX_STOP: IWL_DEBUG_HT(priv, "stop Tx\n"); - return iwl_tx_agg_stop(priv, sta->addr, tid); + ret = iwl_tx_agg_stop(priv, sta->addr, tid); + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return 0; + else + return ret; default: IWL_DEBUG_HT(priv, "unknown\n"); return -EINVAL; -- cgit v1.2.3 From 4f01ac01539d83709d6ae314fc172da7b7e70456 Mon Sep 17 00:00:00 2001 From: Mohamed Abbas Date: Tue, 17 Mar 2009 21:51:44 -0700 Subject: iwlagn: allow power level setting all the times allow user to set power level at all times Signed-off-by: Mohamed Abbas Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn.c | 5 ----- drivers/net/wireless/iwlwifi/iwl3945-base.c | 5 ----- 2 files changed, 10 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index c1482852ea4..663dc83be50 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -3093,11 +3093,6 @@ static ssize_t store_power_level(struct device *d, mutex_lock(&priv->mutex); - if (!iwl_is_ready(priv)) { - ret = -EAGAIN; - goto out; - } - ret = strict_strtoul(buf, 10, &mode); if (ret) goto out; diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 16ecf03a155..bd59ed4dae2 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -4550,11 +4550,6 @@ static ssize_t store_power_level(struct device *d, mutex_lock(&priv->mutex); - if (!iwl_is_ready(priv)) { - ret = -EAGAIN; - goto out; - } - ret = strict_strtoul(buf, 10, &mode); if (ret) goto out; -- cgit v1.2.3 From 28c608750f5f72e3c4139f7a51358eccd58c80a9 Mon Sep 17 00:00:00 2001 From: Mohamed Abbas Date: Tue, 17 Mar 2009 21:51:45 -0700 Subject: iwlcore: dont commit power command if interface is not up If user set new power level, accept the new power level and only send command to host if the interface is up and radio on. Signed-off-by: Mohamed Abbas Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-power.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-power.c b/drivers/net/wireless/iwlwifi/iwl-power.c index 18b7e4195ea..47c894530eb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-power.c +++ b/drivers/net/wireless/iwlwifi/iwl-power.c @@ -273,7 +273,7 @@ int iwl_power_update_mode(struct iwl_priv *priv, bool force) if (priv->iw_mode != NL80211_IFTYPE_STATION) final_mode = IWL_POWER_MODE_CAM; - if (!iwl_is_rfkill(priv) && !setting->power_disabled && + if (iwl_is_ready_rf(priv) && !setting->power_disabled && ((setting->power_mode != final_mode) || force)) { struct iwl_powertable_cmd cmd; -- cgit v1.2.3 From b1c6019bc0fe829309258d888f47d9ae54353039 Mon Sep 17 00:00:00 2001 From: Mohamed Abbas Date: Tue, 17 Mar 2009 21:51:47 -0700 Subject: iwlwifi: support 11h Set IEEE80211_HW_SPECTRUM_MGMT bit in hw->flags, this tell mac80211 we support spectrum mgmt. Signed-off-by: Mohamed Abbas Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-core.c | 1 + drivers/net/wireless/iwlwifi/iwl3945-base.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index bcdecb11080..4b1298c2b0d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -1298,6 +1298,7 @@ int iwl_setup_mac(struct iwl_priv *priv) hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM | IEEE80211_HW_AMPDU_AGGREGATION | + IEEE80211_HW_SPECTRUM_MGMT | IEEE80211_HW_SUPPORTS_PS; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index bd59ed4dae2..ec446451d9e 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -4904,7 +4904,8 @@ static int iwl3945_setup_mac(struct iwl_priv *priv) /* Tell mac80211 our characteristics */ hw->flags = IEEE80211_HW_SIGNAL_DBM | - IEEE80211_HW_NOISE_DBM; + IEEE80211_HW_NOISE_DBM | + IEEE80211_HW_SPECTRUM_MGMT; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | -- cgit v1.2.3 From 21c02a1ab2d4b4a439461140e1ac355db32c3f2b Mon Sep 17 00:00:00 2001 From: Abhijeet Kolekar Date: Tue, 17 Mar 2009 21:51:48 -0700 Subject: iwl3945: set TFD_QUEUE_MAX to correct value Total number of queues is 8 but only 7 of them are TX queues. 4 AC(Data) queue ,1 CMD and 2 HCCA. The HCCA queues are not used. max_txq_num is set to maximum usable TX queues. Signed-off-by: Abhijeet Kolekar Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-3945-hw.h | 2 +- drivers/net/wireless/iwlwifi/iwl-3945.c | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-hw.h b/drivers/net/wireless/iwlwifi/iwl-3945-hw.h index 205603d082a..73f93a0ff2d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-hw.h +++ b/drivers/net/wireless/iwlwifi/iwl-3945-hw.h @@ -233,7 +233,7 @@ struct iwl3945_eeprom { #define PCI_CFG_REV_ID_BIT_RTP (0x80) /* bit 7 */ #define TFD_QUEUE_MIN 0 -#define TFD_QUEUE_MAX 6 +#define TFD_QUEUE_MAX 5 /* 4 DATA + 1 CMD */ #define IWL_NUM_SCAN_RATES (2) diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index ba7e720e73c..99bb48e0336 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -1046,7 +1046,7 @@ static int iwl3945_txq_ctx_reset(struct iwl_priv *priv) goto error; /* Tx queue(s) */ - for (txq_id = 0; txq_id < TFD_QUEUE_MAX; txq_id++) { + for (txq_id = 0; txq_id <= priv->hw_params.max_txq_num; txq_id++) { slots_num = (txq_id == IWL_CMD_QUEUE_NUM) ? TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; rc = iwl_tx_queue_init(priv, &priv->txq[txq_id], slots_num, @@ -1239,7 +1239,7 @@ void iwl3945_hw_txq_ctx_free(struct iwl_priv *priv) int txq_id; /* Tx queues */ - for (txq_id = 0; txq_id < TFD_QUEUE_MAX; txq_id++) + for (txq_id = 0; txq_id <= priv->hw_params.max_txq_num; txq_id++) iwl_tx_queue_free(priv, txq_id); } @@ -1259,7 +1259,7 @@ void iwl3945_hw_txq_ctx_stop(struct iwl_priv *priv) iwl_write_prph(priv, ALM_SCD_MODE_REG, 0); /* reset TFD queues */ - for (txq_id = 0; txq_id < TFD_QUEUE_MAX; txq_id++) { + for (txq_id = 0; txq_id <= priv->hw_params.max_txq_num; txq_id++) { iwl_write_direct32(priv, FH39_TCSR_CONFIG(txq_id), 0x0); iwl_poll_direct_bit(priv, FH39_TSSR_TX_STATUS, FH39_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(txq_id), @@ -2488,6 +2488,9 @@ int iwl3945_hw_set_hw_params(struct iwl_priv *priv) return -ENOMEM; } + /* Assign number of Usable TX queues */ + priv->hw_params.max_txq_num = TFD_QUEUE_MAX; + priv->hw_params.tfd_size = sizeof(struct iwl3945_tfd); priv->hw_params.rx_buf_size = IWL_RX_BUF_SIZE_3K; priv->hw_params.max_pkt_size = 2342; -- cgit v1.2.3 From 3e5d238fa75783e1080e7413c7e36dd5203950eb Mon Sep 17 00:00:00 2001 From: Abhijeet Kolekar Date: Tue, 17 Mar 2009 21:51:49 -0700 Subject: iwl3945: use iwl_cmd_queue_free iwl_cmd_queue_free needs to be used to free up the cmd_queue, as TFD slots for cmd_queue and tx_queue are different. Signed-off-by: Abhijeet Kolekar Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-3945.c | 11 +++++------ drivers/net/wireless/iwlwifi/iwl-core.h | 1 + drivers/net/wireless/iwlwifi/iwl-tx.c | 6 ++++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index 99bb48e0336..50c61ed2352 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -747,11 +747,6 @@ void iwl3945_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq) int i; int counter; - /* classify bd */ - if (txq->q.id == IWL_CMD_QUEUE_NUM) - /* nothing to cleanup after for host commands */ - return; - /* sanity check */ counter = TFD_CTL_COUNT_GET(le32_to_cpu(tfd->control_flags)); if (counter > NUM_TFD_CHUNKS) { @@ -1240,7 +1235,11 @@ void iwl3945_hw_txq_ctx_free(struct iwl_priv *priv) /* Tx queues */ for (txq_id = 0; txq_id <= priv->hw_params.max_txq_num; txq_id++) - iwl_tx_queue_free(priv, txq_id); + if (txq_id == IWL_CMD_QUEUE_NUM) + iwl_cmd_queue_free(priv); + else + iwl_tx_queue_free(priv, txq_id); + } void iwl3945_hw_txq_ctx_stop(struct iwl_priv *priv) diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index 27310fec2e4..a8eac8c3c1f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h @@ -264,6 +264,7 @@ void iwl_rx_reply_error(struct iwl_priv *priv, * RX ******************************************************/ void iwl_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rxq); +void iwl_cmd_queue_free(struct iwl_priv *priv); int iwl_rx_queue_alloc(struct iwl_priv *priv); void iwl_rx_handle(struct iwl_priv *priv); int iwl_rx_queue_update_write_ptr(struct iwl_priv *priv, diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index dff60fb7021..9e83ee24ea8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -174,7 +174,7 @@ EXPORT_SYMBOL(iwl_tx_queue_free); * Free all buffers. * 0-fill, but do not free "txq" descriptor structure. */ -static void iwl_cmd_queue_free(struct iwl_priv *priv) +void iwl_cmd_queue_free(struct iwl_priv *priv) { struct iwl_tx_queue *txq = &priv->txq[IWL_CMD_QUEUE_NUM]; struct iwl_queue *q = &txq->q; @@ -193,12 +193,14 @@ static void iwl_cmd_queue_free(struct iwl_priv *priv) /* De-alloc circular buffer of TFDs */ if (txq->q.n_bd) - pci_free_consistent(dev, sizeof(struct iwl_tfd) * + pci_free_consistent(dev, priv->hw_params.tfd_size * txq->q.n_bd, txq->tfds, txq->q.dma_addr); /* 0-fill queue descriptor structure */ memset(txq, 0, sizeof(*txq)); } +EXPORT_SYMBOL(iwl_cmd_queue_free); + /*************** DMA-QUEUE-GENERAL-FUNCTIONS ***** * DMA services * -- cgit v1.2.3 From 1e680233e7edfd081ebf9ec54e118547d5de7a8c Mon Sep 17 00:00:00 2001 From: Abhijeet Kolekar Date: Tue, 17 Mar 2009 21:51:50 -0700 Subject: iwl3945: fix checkpatch.pl errors Patch fixes two checkpatch.pl errors. Signed-off-by: Abhijeet Kolekar Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-3945.c | 2 +- drivers/net/wireless/iwlwifi/iwl3945-base.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index 50c61ed2352..d03f5534afe 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -1179,7 +1179,7 @@ int iwl3945_hw_nic_init(struct iwl_priv *priv) IWL_DEBUG_INFO(priv, "HW Revision ID = 0x%X\n", rev_id); rc = priv->cfg->ops->lib->apm_ops.set_pwr_src(priv, IWL_PWR_SRC_VMAIN); - if(rc) + if (rc) return rc; priv->cfg->ops->lib->apm_ops.config(priv); diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index ec446451d9e..d61f9a0701e 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -560,7 +560,7 @@ static int iwl3945_set_dynamic_key(struct iwl_priv *priv, ret = iwl3945_set_wep_dynamic_key_info(priv, keyconf, sta_id); break; default: - IWL_ERR(priv,"Unknown alg: %s alg = %d\n", __func__, keyconf->alg); + IWL_ERR(priv, "Unknown alg: %s alg = %d\n", __func__, keyconf->alg); ret = -EINVAL; } -- cgit v1.2.3 From 82127493a656f6293ffb1566410b5753f29991ef Mon Sep 17 00:00:00 2001 From: Abhijeet Kolekar Date: Tue, 17 Mar 2009 21:51:51 -0700 Subject: iwl3945: control rate decrease Control the rate decrease. Do not decrease the rate fast. Use success_ratio for rate scaling :) Signed-off-by: Abhijeet Kolekar Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-3945-rs.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c index f65c308a671..af6b9d44477 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c @@ -124,7 +124,7 @@ static struct iwl3945_tpt_entry iwl3945_tpt_table_g[] = { #define IWL39_RATE_HIGH_TH 11520 #define IWL_SUCCESS_UP_TH 8960 #define IWL_SUCCESS_DOWN_TH 10880 -#define IWL_RATE_MIN_FAILURE_TH 8 +#define IWL_RATE_MIN_FAILURE_TH 6 #define IWL_RATE_MIN_SUCCESS_TH 8 #define IWL_RATE_DECREASE_TH 1920 #define IWL_RATE_RETRY_TH 15 @@ -488,7 +488,7 @@ static void rs_tx_status(void *priv_rate, struct ieee80211_supported_band *sband IWL_DEBUG_RATE(priv, "enter\n"); - retries = info->status.rates[0].count - 1; + retries = info->status.rates[0].count; /* Sanity Check for retries */ if (retries > IWL_RATE_RETRY_TH) retries = IWL_RATE_RETRY_TH; @@ -791,16 +791,15 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, if ((window->success_ratio < IWL_RATE_DECREASE_TH) || !current_tpt) { IWL_DEBUG_RATE(priv, "decrease rate because of low success_ratio\n"); scale_action = -1; - /* No throughput measured yet for adjacent rates, * try increase */ } else if ((low_tpt == IWL_INVALID_VALUE) && (high_tpt == IWL_INVALID_VALUE)) { - if (high != IWL_RATE_INVALID && window->success_counter >= IWL_RATE_INCREASE_TH) + if (high != IWL_RATE_INVALID && window->success_ratio >= IWL_RATE_INCREASE_TH) scale_action = 1; else if (low != IWL_RATE_INVALID) - scale_action = -1; + scale_action = 0; /* Both adjacent throughputs are measured, but neither one has * better throughput; we're using the best rate, don't change @@ -826,14 +825,14 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, else { IWL_DEBUG_RATE(priv, "decrease rate because of high tpt\n"); - scale_action = -1; + scale_action = 0; } } else if (low_tpt != IWL_INVALID_VALUE) { if (low_tpt > current_tpt) { IWL_DEBUG_RATE(priv, "decrease rate because of low tpt\n"); scale_action = -1; - } else if (window->success_counter >= IWL_RATE_INCREASE_TH) { + } else if (window->success_ratio >= IWL_RATE_INCREASE_TH) { /* Lower rate has better * throughput,decrease rate */ scale_action = 1; -- cgit v1.2.3 From a2f1cbebdccc866d6c7da9eb655d35b5c60d33a0 Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Tue, 17 Mar 2009 21:51:52 -0700 Subject: iwlwifi: report error when detect failure during stop agg queue This fix related to bug 1921 at http://www.intellinuxwireless.org/bugzilla/show_bug.cgi?id=1921 when detect error during stopping tx aggregation queue, report the error to help identify the problem. Signed-off-by: Wey-Yi Guy Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-5000.c | 2 +- drivers/net/wireless/iwlwifi/iwl-sta.c | 4 +++- drivers/net/wireless/iwlwifi/iwl-tx.c | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 08c19bea71e..a3d9a95a9b3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -1077,7 +1077,7 @@ static int iwl5000_txq_agg_disable(struct iwl_priv *priv, u16 txq_id, if ((IWL50_FIRST_AMPDU_QUEUE > txq_id) || (IWL50_FIRST_AMPDU_QUEUE + IWL50_NUM_AMPDU_QUEUES <= txq_id)) { - IWL_WARN(priv, + IWL_ERR(priv, "queue number out of range: %d, must be %d to %d\n", txq_id, IWL50_FIRST_AMPDU_QUEUE, IWL50_FIRST_AMPDU_QUEUE + IWL50_NUM_AMPDU_QUEUES - 1); diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c index 1684490d93c..5798fe49c77 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sta.c +++ b/drivers/net/wireless/iwlwifi/iwl-sta.c @@ -1138,8 +1138,10 @@ int iwl_sta_rx_agg_stop(struct iwl_priv *priv, const u8 *addr, int tid) int sta_id; sta_id = iwl_find_station(priv, addr); - if (sta_id == IWL_INVALID_STATION) + if (sta_id == IWL_INVALID_STATION) { + IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid); return -ENXIO; + } spin_lock_irqsave(&priv->sta_lock, flags); priv->stations[sta_id].sta.station_flags_msk = 0; diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index 9e83ee24ea8..b13862a598e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -1223,8 +1223,10 @@ int iwl_tx_agg_stop(struct iwl_priv *priv , const u8 *ra, u16 tid) sta_id = iwl_find_station(priv, ra); - if (sta_id == IWL_INVALID_STATION) + if (sta_id == IWL_INVALID_STATION) { + IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid); return -ENXIO; + } if (priv->stations[sta_id].tid[tid].agg.state != IWL_AGG_ON) IWL_WARN(priv, "Stopping AGG while state not IWL_AGG_ON\n"); -- cgit v1.2.3 From 43da9192326a4499b5faf737c3636f25b56b53e0 Mon Sep 17 00:00:00 2001 From: Abhijeet Kolekar Date: Tue, 17 Mar 2009 21:51:53 -0700 Subject: iwl3945: replace stations with stations_39 A *leftover* stations is replaced with stations_39. Signed-off-by: Abhijeet Kolekar Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl3945-base.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index d61f9a0701e..ede29b6c4dc 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -485,14 +485,14 @@ static int iwl3945_set_ccmp_dynamic_key_info(struct iwl_priv *priv, memcpy(priv->stations_39[sta_id].sta.key.key, keyconf->key, keyconf->keylen); - if ((priv->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_ENCRYPT_MSK) + if ((priv->stations_39[sta_id].sta.key.key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC) - priv->stations[sta_id].sta.key.key_offset = + priv->stations_39[sta_id].sta.key.key_offset = iwl_get_free_ucode_key_index(priv); /* else, we are overriding an existing key => no need to allocated room * in uCode. */ - WARN(priv->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET, + WARN(priv->stations_39[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET, "no space for a new key"); priv->stations_39[sta_id].sta.key.key_flags = key_flags; -- cgit v1.2.3 From a9a6ffffd05f97e6acbdeafc595e269855829751 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 18 Mar 2009 14:06:44 +0200 Subject: mac80211: don't drop nullfunc frames during software scan ieee80211_tx_h_check_assoc() was dropping everything else than probe requests during software scan. So the nullfunc frame with the power save bit was dropped and AP never received it. This meant that AP never buffered any frames for the station during software scan. Fix this by allowing to transmit both probe request and nullfunc frames during software scan. Tested with stlc45xx. Signed-off-by: Kalle Valo Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/scan.c | 13 +++++++++++++ net/mac80211/tx.c | 14 +++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 5030a3c8750..46f35dc6acc 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -409,6 +409,19 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, return 0; } + /* + * Hardware/driver doesn't support hw_scan, so use software + * scanning instead. First send a nullfunc frame with power save + * bit on so that AP will buffer the frames for us while we are not + * listening, then send probe requests to each channel and wait for + * the responses. After all channels are scanned, tune back to the + * original channel and send a nullfunc frame with power save bit + * off to trigger the AP to send us all the buffered frames. + * + * Note that while local->sw_scanning is true everything else but + * nullfunc frames and probe requests will be dropped in + * ieee80211_tx_h_check_assoc(). + */ local->sw_scanning = true; if (local->ops->sw_scan_start) local->ops->sw_scan_start(local_to_hw(local)); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 038460b0a48..f3f240c6901 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -193,7 +193,19 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx) return TX_CONTINUE; if (unlikely(tx->local->sw_scanning) && - !ieee80211_is_probe_req(hdr->frame_control)) + !ieee80211_is_probe_req(hdr->frame_control) && + !ieee80211_is_nullfunc(hdr->frame_control)) + /* + * When software scanning only nullfunc frames (to notify + * the sleep state to the AP) and probe requests (for the + * active scan) are allowed, all other frames should not be + * sent and we should not get here, but if we do + * nonetheless, drop them to avoid sending them + * off-channel. See the link below and + * ieee80211_start_scan() for more. + * + * http://article.gmane.org/gmane.linux.kernel.wireless.general/30089 + */ return TX_DROP; if (tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT) -- cgit v1.2.3 From b3a902850a8f5bc11a660051faae707f928d4bd6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 18 Mar 2009 14:29:38 +0100 Subject: mac80211: kill IEEE80211_CONF_SHORT_SLOT_TIME No drivers use it any more, so it can now be removed safely. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index a38a82c785d..daa539a4287 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -520,12 +520,6 @@ enum ieee80211_conf_flags { IEEE80211_CONF_PS = (1<<1), }; -/* XXX: remove all this once drivers stop trying to use it */ -static inline int __deprecated __IEEE80211_CONF_SHORT_SLOT_TIME(void) -{ - return 0; -} -#define IEEE80211_CONF_SHORT_SLOT_TIME (__IEEE80211_CONF_SHORT_SLOT_TIME()) /** * enum ieee80211_conf_changed - denotes which configuration changed -- cgit v1.2.3 From 0934af2340caf3c9f247ae650bf0c6faa4203dba Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Wed, 18 Mar 2009 20:22:00 +0530 Subject: ath9k: Fix rate control update for aggregated frames We will miss rate control update if first A-MPDU of an aggregation is not Block Acked as we always tell if the rate control needs to updated through update_rc of first A-MPDU. This patch does rate control update for the first A-MPDU which notifies it's tx status (which is not necessarily the first A-MPDU of an aggregation) to mac80211. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/xmit.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath9k/xmit.c b/drivers/net/wireless/ath9k/xmit.c index 8968abe7f48..f2877193e95 100644 --- a/drivers/net/wireless/ath9k/xmit.c +++ b/drivers/net/wireless/ath9k/xmit.c @@ -64,6 +64,10 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq, struct list_head *head); static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf); +static int ath_tx_num_badfrms(struct ath_softc *sc, struct ath_buf *bf, + int txok); +static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds, + int nbad, int txok); /*********************/ /* Aggregation logic */ @@ -274,9 +278,10 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf_next, *bf_last = bf->bf_lastbf; struct ath_desc *ds = bf_last->bf_desc; struct list_head bf_head, bf_pending; - u16 seq_st = 0; + u16 seq_st = 0, acked_cnt = 0, txfail_cnt = 0; u32 ba[WME_BA_BMP_SIZE >> 5]; - int isaggr, txfail, txpending, sendbar = 0, needreset = 0; + int isaggr, txfail, txpending, sendbar = 0, needreset = 0, nbad = 0; + bool rc_update = true; skb = (struct sk_buff *)bf->bf_mpdu; hdr = (struct ieee80211_hdr *)skb->data; @@ -316,6 +321,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, INIT_LIST_HEAD(&bf_pending); INIT_LIST_HEAD(&bf_head); + nbad = ath_tx_num_badfrms(sc, bf, txok); while (bf) { txfail = txpending = 0; bf_next = bf->bf_next; @@ -323,8 +329,10 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, if (ATH_BA_ISSET(ba, ATH_BA_INDEX(seq_st, bf->bf_seqno))) { /* transmit completion, subframe is * acked by block ack */ + acked_cnt++; } else if (!isaggr && txok) { /* transmit completion */ + acked_cnt++; } else { if (!(tid->state & AGGR_CLEANUP) && ds->ds_txstat.ts_flags != ATH9K_TX_SW_ABORTED) { @@ -335,6 +343,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, bf->bf_state.bf_type |= BUF_XRETRY; txfail = 1; sendbar = 1; + txfail_cnt++; } } else { /* @@ -361,6 +370,11 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, ath_tx_update_baw(sc, tid, bf->bf_seqno); spin_unlock_bh(&txq->axq_lock); + if (rc_update) + if (acked_cnt == 1 || txfail_cnt == 1) { + ath_tx_rc_status(bf, ds, nbad, txok); + rc_update = false; + } ath_tx_complete_buf(sc, bf, &bf_head, !txfail, sendbar); } else { /* retry the un-acked ones */ @@ -1901,7 +1915,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) struct ath_buf *bf, *lastbf, *bf_held = NULL; struct list_head bf_head; struct ath_desc *ds; - int txok, nbad = 0; + int txok; int status; DPRINTF(sc, ATH_DBG_QUEUE, "tx queue %d (%x), link %p\n", @@ -1995,13 +2009,9 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) bf->bf_retries = ds->ds_txstat.ts_longretry; if (ds->ds_txstat.ts_status & ATH9K_TXERR_XRETRY) bf->bf_state.bf_type |= BUF_XRETRY; - nbad = 0; - } else { - nbad = ath_tx_num_badfrms(sc, bf, txok); + ath_tx_rc_status(bf, ds, 0, txok); } - ath_tx_rc_status(bf, ds, nbad, txok); - if (bf_isampdu(bf)) ath_tx_complete_aggr(sc, txq, bf, &bf_head, txok); else -- cgit v1.2.3 From 4b4698c443c9db62b220c41a1793872d6ebe82e1 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 19 Mar 2009 13:39:19 +0200 Subject: mac80211: Fix a typo in assoc vs. reassoc check Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index a55879663b3..4c753bb43ba 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -103,7 +103,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) u32 rates = 0; size_t e_ies_len; - if (ifmgd->flags & IEEE80211_IBSS_PREV_BSSID_SET) { + if (ifmgd->flags & IEEE80211_STA_PREV_BSSID_SET) { e_ies = sdata->u.mgd.ie_reassocreq; e_ies_len = sdata->u.mgd.ie_reassocreq_len; } else { -- cgit v1.2.3 From a299542e97ec1939fdca7db6d3d82c0aa9bf8b9a Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 19 Mar 2009 13:39:20 +0200 Subject: mac80211: Fix reassociation by not clearing previous BSSID We must not clear the previous BSSID when roaming to another AP within the same ESS for reassociation to be used properly. It is fine to clear this when the SSID changes, so let's move the code into ieee80211_sta_set_ssid(). Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 4c753bb43ba..1f49b63d8dd 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1888,8 +1888,6 @@ int ieee80211_sta_commit(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - ifmgd->flags &= ~IEEE80211_STA_PREV_BSSID_SET; - if (ifmgd->ssid_len) ifmgd->flags |= IEEE80211_STA_SSID_SET; else @@ -1908,6 +1906,10 @@ int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size ifmgd = &sdata->u.mgd; if (ifmgd->ssid_len != len || memcmp(ifmgd->ssid, ssid, len) != 0) { + /* + * Do not use reassociation if SSID is changed (different ESS). + */ + ifmgd->flags &= ~IEEE80211_STA_PREV_BSSID_SET; memset(ifmgd->ssid, 0, sizeof(ifmgd->ssid)); memcpy(ifmgd->ssid, ssid, len); ifmgd->ssid_len = len; -- cgit v1.2.3 From 6039f6d23fe792d615da5449e9fa1c6b43caacf6 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 19 Mar 2009 13:39:21 +0200 Subject: nl80211: Event notifications for MLME events Add new nl80211 event notifications (and a new multicast group, "mlme") for informing user space about received and processed Authentication, (Re)Association Response, Deauthentication, and Disassociation frames in station and IBSS modes (i.e., MLME SAP interface primitives MLME-AUTHENTICATE.confirm, MLME-ASSOCIATE.confirm, MLME-REASSOCIATE.confirm, MLME-DEAUTHENTICATE.indicate, and MLME-DISASSOCIATE.indication). The event data is encapsulated as the 802.11 management frame since we already have the frame in that format and it includes all the needed information. This is the initial step in providing MLME SAP interface for authentication and association with nl80211. In other words, kernel code will act as the MLME and a user space application can control it as the SME. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- include/linux/nl80211.h | 36 ++++++++++++++++++++++++- include/net/cfg80211.h | 46 +++++++++++++++++++++++++++++++ net/mac80211/mlme.c | 9 +++++-- net/wireless/Makefile | 2 +- net/wireless/mlme.c | 46 +++++++++++++++++++++++++++++++ net/wireless/nl80211.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++ net/wireless/nl80211.h | 12 +++++++++ 7 files changed, 219 insertions(+), 4 deletions(-) create mode 100644 net/wireless/mlme.c diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 3700d927e24..5ce68ae8314 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -161,6 +161,25 @@ * %NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on * to (%NL80211_ATTR_REG_ALPHA2). * + * @NL80211_CMD_AUTHENTICATE: authentication notification (on the "mlme" + * multicast group). This event reports reception of an Authentication + * frame in station and IBSS modes when the local MLME processed the + * frame, i.e., it was for the local STA and was received in correct + * state. This is similar to MLME-AUTHENTICATE.confirm primitive in the + * MLME SAP interface (kernel providing MLME, userspace SME). The + * included NL80211_ATTR_FRAME attribute contains the management frame + * (including both the header and frame body, but not FCS). + * @NL80211_CMD_ASSOCIATE: association notification; like + * NL80211_CMD_AUTHENTICATE but for Association Response and Reassociation + * Response frames (similar to MLME-ASSOCIATE.confirm or + * MLME-REASSOCIATE.confirm primitives). + * @NL80211_CMD_DEAUTHENTICATE: deauthentication notification; like + * NL80211_CMD_AUTHENTICATE but for Deauthentication frames (similar to + * MLME-DEAUTHENTICATE.indication primitive). + * @NL80211_CMD_DISASSOCIATE: disassociation notification; like + * NL80211_CMD_AUTHENTICATE but for Disassociation frames (similar to + * MLME-DISASSOCIATE.indication primitive). + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -217,6 +236,11 @@ enum nl80211_commands { NL80211_CMD_REG_CHANGE, + NL80211_CMD_AUTHENTICATE, + NL80211_CMD_ASSOCIATE, + NL80211_CMD_DEAUTHENTICATE, + NL80211_CMD_DISASSOCIATE, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -230,8 +254,11 @@ enum nl80211_commands { */ #define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS #define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE - #define NL80211_CMD_REG_CHANGE NL80211_CMD_REG_CHANGE +#define NL80211_CMD_AUTHENTICATE NL80211_CMD_AUTHENTICATE +#define NL80211_CMD_ASSOCIATE NL80211_CMD_ASSOCIATE +#define NL80211_CMD_DEAUTHENTICATE NL80211_CMD_DEAUTHENTICATE +#define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE /** * enum nl80211_attrs - nl80211 netlink attributes @@ -353,6 +380,10 @@ enum nl80211_commands { * an array of command numbers (i.e. a mapping index to command number) * that the driver for the given wiphy supports. * + * @NL80211_ATTR_FRAME: frame data (binary attribute), including frame header + * and body, but not FCS; used, e.g., with NL80211_CMD_AUTHENTICATE and + * NL80211_CMD_ASSOCIATE events + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -432,6 +463,8 @@ enum nl80211_attrs { NL80211_ATTR_SUPPORTED_COMMANDS, + NL80211_ATTR_FRAME, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -451,6 +484,7 @@ enum nl80211_attrs { #define NL80211_ATTR_IE NL80211_ATTR_IE #define NL80211_ATTR_REG_INITIATOR NL80211_ATTR_REG_INITIATOR #define NL80211_ATTR_REG_TYPE NL80211_ATTR_REG_TYPE +#define NL80211_ATTR_FRAME NL80211_ATTR_FRAME #define NL80211_MAX_SUPP_RATES 32 #define NL80211_MAX_SUPP_REG_RULES 32 diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 50f3fd9ff52..ad44016021b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -807,4 +807,50 @@ void cfg80211_put_bss(struct cfg80211_bss *bss); */ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *bss); +/** + * cfg80211_send_rx_auth - notification of processed authentication + * @dev: network device + * @buf: authentication frame (header + body) + * @len: length of the frame data + * + * This function is called whenever an authentication has been processed in + * station mode. + */ +void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len); + +/** + * cfg80211_send_rx_assoc - notification of processed association + * @dev: network device + * @buf: (re)association response frame (header + body) + * @len: length of the frame data + * + * This function is called whenever a (re)association response has been + * processed in station mode. + */ +void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len); + +/** + * cfg80211_send_rx_deauth - notification of processed deauthentication + * @dev: network device + * @buf: deauthentication frame (header + body) + * @len: length of the frame data + * + * This function is called whenever deauthentication has been processed in + * station mode. + */ +void cfg80211_send_rx_deauth(struct net_device *dev, const u8 *buf, + size_t len); + +/** + * cfg80211_send_rx_disassoc - notification of processed disassociation + * @dev: network device + * @buf: disassociation response frame (header + body) + * @len: length of the frame data + * + * This function is called whenever disassociation has been processed in + * station mode. + */ +void cfg80211_send_rx_disassoc(struct net_device *dev, const u8 *buf, + size_t len); + #endif /* __NET_CFG80211_H */ diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 1f49b63d8dd..6dc7a61bc18 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1085,11 +1085,13 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, case WLAN_AUTH_OPEN: case WLAN_AUTH_LEAP: ieee80211_auth_completed(sdata); + cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len); break; case WLAN_AUTH_SHARED_KEY: - if (ifmgd->auth_transaction == 4) + if (ifmgd->auth_transaction == 4) { ieee80211_auth_completed(sdata); - else + cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len); + } else ieee80211_auth_challenge(sdata, mgmt, len); break; } @@ -1125,6 +1127,7 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, ieee80211_set_disassoc(sdata, true, false, 0); ifmgd->flags &= ~IEEE80211_STA_AUTHENTICATED; + cfg80211_send_rx_deauth(sdata->dev, (u8 *) mgmt, len); } @@ -1154,6 +1157,7 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, } ieee80211_set_disassoc(sdata, false, false, reason_code); + cfg80211_send_rx_disassoc(sdata->dev, (u8 *) mgmt, len); } @@ -1370,6 +1374,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, ieee80211_set_associated(sdata, changed); ieee80211_associated(sdata); + cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, len); } diff --git a/net/wireless/Makefile b/net/wireless/Makefile index c157b4d8014..6d1e7b27b75 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o -cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o +cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c new file mode 100644 index 00000000000..bec5721b6f9 --- /dev/null +++ b/net/wireless/mlme.c @@ -0,0 +1,46 @@ +/* + * cfg80211 MLME SAP interface + * + * Copyright (c) 2009, Jouni Malinen + */ + +#include +#include +#include +#include +#include +#include "core.h" +#include "nl80211.h" + +void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len) +{ + struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + nl80211_send_rx_auth(rdev, dev, buf, len); +} +EXPORT_SYMBOL(cfg80211_send_rx_auth); + +void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) +{ + struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + nl80211_send_rx_assoc(rdev, dev, buf, len); +} +EXPORT_SYMBOL(cfg80211_send_rx_assoc); + +void cfg80211_send_rx_deauth(struct net_device *dev, const u8 *buf, size_t len) +{ + struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + nl80211_send_rx_deauth(rdev, dev, buf, len); +} +EXPORT_SYMBOL(cfg80211_send_rx_deauth); + +void cfg80211_send_rx_disassoc(struct net_device *dev, const u8 *buf, + size_t len) +{ + struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + nl80211_send_rx_disassoc(rdev, dev, buf, len); +} +EXPORT_SYMBOL(cfg80211_send_rx_disassoc); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a3ecf8d7389..c034c2418cb 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2830,6 +2830,9 @@ static struct genl_ops nl80211_ops[] = { .dumpit = nl80211_dump_scan, }, }; +static struct genl_multicast_group nl80211_mlme_mcgrp = { + .name = "mlme", +}; /* multicast groups */ static struct genl_multicast_group nl80211_config_mcgrp = { @@ -2975,6 +2978,71 @@ nla_put_failure: nlmsg_free(msg); } +static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len, + enum nl80211_commands cmd) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); + 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); + NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + +void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *buf, size_t len) +{ + nl80211_send_mlme_event(rdev, netdev, buf, len, + NL80211_CMD_AUTHENTICATE); +} + +void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *buf, + size_t len) +{ + nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_ASSOCIATE); +} + +void nl80211_send_rx_deauth(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *buf, + size_t len) +{ + nl80211_send_mlme_event(rdev, netdev, buf, len, + NL80211_CMD_DEAUTHENTICATE); +} + +void nl80211_send_rx_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *buf, + size_t len) +{ + nl80211_send_mlme_event(rdev, netdev, buf, len, + NL80211_CMD_DISASSOCIATE); +} + /* initialisation/exit functions */ int nl80211_init(void) @@ -3003,6 +3071,10 @@ int nl80211_init(void) if (err) goto err_out; + err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp); + if (err) + goto err_out; + return 0; err_out: genl_unregister_family(&nl80211_fam); diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 5b5fe1339de..b77af4ab80b 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -11,5 +11,17 @@ extern void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, extern void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, struct net_device *netdev); extern void nl80211_send_reg_change_event(struct regulatory_request *request); +extern void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len); +extern void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len); +extern void nl80211_send_rx_deauth(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len); +extern void nl80211_send_rx_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len); #endif /* __NET_WIRELESS_NL80211_H */ -- cgit v1.2.3 From 636a5d3625993c5ca59abc81794b9ded93cdb740 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 19 Mar 2009 13:39:22 +0200 Subject: nl80211: Add MLME primitives to support external SME This patch adds new nl80211 commands to allow user space to request authentication and association (and also deauthentication and disassociation). The commands are structured to allow separate authentication and association steps, i.e., the interface between kernel and user space is similar to the MLME SAP interface in IEEE 802.11 standard and an user space application takes the role of the SME. The patch introduces MLME-AUTHENTICATE.request, MLME-{,RE}ASSOCIATE.request, MLME-DEAUTHENTICATE.request, and MLME-DISASSOCIATE.request primitives. The authentication and association commands request the actual operations in two steps (assuming the driver supports this; if not, separate authentication step is skipped; this could end up being a separate "connect" command). The initial implementation for mac80211 uses the current net/mac80211/mlme.c for actual sending and processing of management frames and the new nl80211 commands will just stop the current state machine from moving automatically from authentication to association. Future cleanup may move more of the MLME operations into cfg80211. The goal of this design is to provide more control of authentication and association process to user space without having to move the full MLME implementation. This should be enough to allow IEEE 802.11r FT protocol and 802.11s SAE authentication to be implemented. Obviously, this will also bring the extra benefit of not having to use WEXT for association requests with mac80211. An example implementation of a user space SME using the new nl80211 commands is available for wpa_supplicant. This patch is enough to get IEEE 802.11r FT protocol working with over-the-air mechanism (over-the-DS will need additional MLME primitives for handling the FT Action frames). Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 1 + include/linux/nl80211.h | 58 +++++++++-- include/net/cfg80211.h | 113 ++++++++++++++++++++ net/mac80211/cfg.c | 140 +++++++++++++++++++++++++ net/mac80211/ieee80211_i.h | 7 +- net/mac80211/mlme.c | 45 ++++++-- net/mac80211/wext.c | 3 + net/wireless/nl80211.c | 255 +++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 601 insertions(+), 21 deletions(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 382387e75b8..4b501b48ce8 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -867,6 +867,7 @@ struct ieee80211_ht_info { /* Authentication algorithms */ #define WLAN_AUTH_OPEN 0 #define WLAN_AUTH_SHARED_KEY 1 +#define WLAN_AUTH_FT 2 #define WLAN_AUTH_LEAP 128 #define WLAN_AUTH_CHALLENGE_LEN 128 diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 5ce68ae8314..9685eaab40a 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -161,24 +161,37 @@ * %NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on * to (%NL80211_ATTR_REG_ALPHA2). * - * @NL80211_CMD_AUTHENTICATE: authentication notification (on the "mlme" - * multicast group). This event reports reception of an Authentication + * @NL80211_CMD_AUTHENTICATE: authentication request and notification. + * This command is used both as a command (request to authenticate) and + * as an event on the "mlme" multicast group indicating completion of the + * authentication process. + * When used as a command, %NL80211_ATTR_IFINDEX is used to identify the + * interface. %NL80211_ATTR_MAC is used to specify PeerSTAAddress (and + * BSSID in case of station mode). %NL80211_ATTR_SSID is used to specify + * the SSID (mainly for association, but is included in authentication + * request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ is used + * to specify the frequence of the channel in MHz. %NL80211_ATTR_AUTH_TYPE + * is used to specify the authentication type. %NL80211_ATTR_IE is used to + * define IEs (VendorSpecificInfo, but also including RSN IE and FT IEs) + * to be added to the frame. + * When used as an event, this reports reception of an Authentication * frame in station and IBSS modes when the local MLME processed the * frame, i.e., it was for the local STA and was received in correct * state. This is similar to MLME-AUTHENTICATE.confirm primitive in the * MLME SAP interface (kernel providing MLME, userspace SME). The * included NL80211_ATTR_FRAME attribute contains the management frame * (including both the header and frame body, but not FCS). - * @NL80211_CMD_ASSOCIATE: association notification; like - * NL80211_CMD_AUTHENTICATE but for Association Response and Reassociation - * Response frames (similar to MLME-ASSOCIATE.confirm or - * MLME-REASSOCIATE.confirm primitives). - * @NL80211_CMD_DEAUTHENTICATE: deauthentication notification; like + * @NL80211_CMD_ASSOCIATE: association request and notification; like + * NL80211_CMD_AUTHENTICATE but for Association and Reassociation + * (similar to MLME-ASSOCIATE.request, MLME-REASSOCIATE.request, + * MLME-ASSOCIATE.confirm or MLME-REASSOCIATE.confirm primitives). + * @NL80211_CMD_DEAUTHENTICATE: deauthentication request and notification; like * NL80211_CMD_AUTHENTICATE but for Deauthentication frames (similar to - * MLME-DEAUTHENTICATE.indication primitive). - * @NL80211_CMD_DISASSOCIATE: disassociation notification; like + * MLME-DEAUTHENTICATION.request and MLME-DEAUTHENTICATE.indication + * primitives). + * @NL80211_CMD_DISASSOCIATE: disassociation request and notification; like * NL80211_CMD_AUTHENTICATE but for Disassociation frames (similar to - * MLME-DISASSOCIATE.indication primitive). + * MLME-DISASSOCIATE.request and MLME-DISASSOCIATE.indication primitives). * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use @@ -383,6 +396,11 @@ enum nl80211_commands { * @NL80211_ATTR_FRAME: frame data (binary attribute), including frame header * and body, but not FCS; used, e.g., with NL80211_CMD_AUTHENTICATE and * NL80211_CMD_ASSOCIATE events + * @NL80211_ATTR_SSID: SSID (binary attribute, 0..32 octets) + * @NL80211_ATTR_AUTH_TYPE: AuthenticationType, see &enum nl80211_auth_type, + * represented as a u32 + * @NL80211_ATTR_REASON_CODE: ReasonCode for %NL80211_CMD_DEAUTHENTICATE and + * %NL80211_CMD_DISASSOCIATE, u16 * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -464,6 +482,9 @@ enum nl80211_attrs { NL80211_ATTR_SUPPORTED_COMMANDS, NL80211_ATTR_FRAME, + NL80211_ATTR_SSID, + NL80211_ATTR_AUTH_TYPE, + NL80211_ATTR_REASON_CODE, /* add attributes here, update the policy in nl80211.c */ @@ -485,6 +506,9 @@ enum nl80211_attrs { #define NL80211_ATTR_REG_INITIATOR NL80211_ATTR_REG_INITIATOR #define NL80211_ATTR_REG_TYPE NL80211_ATTR_REG_TYPE #define NL80211_ATTR_FRAME NL80211_ATTR_FRAME +#define NL80211_ATTR_SSID NL80211_ATTR_SSID +#define NL80211_ATTR_AUTH_TYPE NL80211_ATTR_AUTH_TYPE +#define NL80211_ATTR_REASON_CODE NL80211_ATTR_REASON_CODE #define NL80211_MAX_SUPP_RATES 32 #define NL80211_MAX_SUPP_REG_RULES 32 @@ -1018,4 +1042,18 @@ enum nl80211_bss { NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1 }; +/** + * enum nl80211_auth_type - AuthenticationType + * + * @NL80211_AUTHTYPE_OPEN_SYSTEM: Open System authentication + * @NL80211_AUTHTYPE_SHARED_KEY: Shared Key authentication (WEP only) + * @NL80211_AUTHTYPE_FT: Fast BSS Transition (IEEE 802.11r) + * @NL80211_AUTHTYPE_NETWORK_EAP: Network EAP (some Cisco APs and mainly LEAP) + */ +enum nl80211_auth_type { + NL80211_AUTHTYPE_OPEN_SYSTEM, + NL80211_AUTHTYPE_SHARED_KEY, + NL80211_AUTHTYPE_FT, + NL80211_AUTHTYPE_NETWORK_EAP, +}; #endif /* __LINUX_NL80211_H */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ad44016021b..0da9a55881a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -578,6 +578,105 @@ struct cfg80211_bss { u8 priv[0] __attribute__((__aligned__(sizeof(void *)))); }; +/** + * struct cfg80211_auth_request - Authentication request data + * + * This structure provides information needed to complete IEEE 802.11 + * authentication. + * NOTE: This structure will likely change when more code from mac80211 is + * moved into cfg80211 so that non-mac80211 drivers can benefit from it, too. + * Before using this in a driver that does not use mac80211, it would be better + * to check the status of that work and better yet, volunteer to work on it. + * + * @chan: The channel to use or %NULL if not specified (auto-select based on + * scan results) + * @peer_addr: The address of the peer STA (AP BSSID in infrastructure case); + * this field is required to be present; if the driver wants to help with + * BSS selection, it should use (yet to be added) MLME event to allow user + * space SME to be notified of roaming candidate, so that the SME can then + * use the authentication request with the recommended BSSID and whatever + * other data may be needed for authentication/association + * @ssid: SSID or %NULL if not yet available + * @ssid_len: Length of ssid in octets + * @auth_type: Authentication type (algorithm) + * @ie: Extra IEs to add to Authentication frame or %NULL + * @ie_len: Length of ie buffer in octets + */ +struct cfg80211_auth_request { + struct ieee80211_channel *chan; + u8 *peer_addr; + const u8 *ssid; + size_t ssid_len; + enum nl80211_auth_type auth_type; + const u8 *ie; + size_t ie_len; +}; + +/** + * struct cfg80211_assoc_request - (Re)Association request data + * + * This structure provides information needed to complete IEEE 802.11 + * (re)association. + * NOTE: This structure will likely change when more code from mac80211 is + * moved into cfg80211 so that non-mac80211 drivers can benefit from it, too. + * Before using this in a driver that does not use mac80211, it would be better + * to check the status of that work and better yet, volunteer to work on it. + * + * @chan: The channel to use or %NULL if not specified (auto-select based on + * scan results) + * @peer_addr: The address of the peer STA (AP BSSID); this field is required + * to be present and the STA must be in State 2 (authenticated) with the + * peer STA + * @ssid: SSID + * @ssid_len: Length of ssid in octets + * @ie: Extra IEs to add to (Re)Association Request frame or %NULL + * @ie_len: Length of ie buffer in octets + */ +struct cfg80211_assoc_request { + struct ieee80211_channel *chan; + u8 *peer_addr; + const u8 *ssid; + size_t ssid_len; + const u8 *ie; + size_t ie_len; +}; + +/** + * struct cfg80211_deauth_request - Deauthentication request data + * + * This structure provides information needed to complete IEEE 802.11 + * deauthentication. + * + * @peer_addr: The address of the peer STA (AP BSSID); this field is required + * to be present and the STA must be authenticated with the peer STA + * @ie: Extra IEs to add to Deauthentication frame or %NULL + * @ie_len: Length of ie buffer in octets + */ +struct cfg80211_deauth_request { + u8 *peer_addr; + u16 reason_code; + const u8 *ie; + size_t ie_len; +}; + +/** + * struct cfg80211_disassoc_request - Disassociation request data + * + * This structure provides information needed to complete IEEE 802.11 + * disassocation. + * + * @peer_addr: The address of the peer STA (AP BSSID); this field is required + * to be present and the STA must be associated with the peer STA + * @ie: Extra IEs to add to Disassociation frame or %NULL + * @ie_len: Length of ie buffer in octets + */ +struct cfg80211_disassoc_request { + u8 *peer_addr; + u16 reason_code; + const u8 *ie; + size_t ie_len; +}; + /** * struct cfg80211_ops - backend description for wireless configuration * @@ -650,6 +749,11 @@ struct cfg80211_bss { * the driver, and will be valid until passed to cfg80211_scan_done(). * For scan results, call cfg80211_inform_bss(); you can call this outside * the scan/scan_done bracket too. + * + * @auth: Request to authenticate with the specified peer + * @assoc: Request to (re)associate with the specified peer + * @deauth: Request to deauthenticate from the specified peer + * @disassoc: Request to disassociate from the specified peer */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy); @@ -730,6 +834,15 @@ struct cfg80211_ops { int (*scan)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_scan_request *request); + + int (*auth)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_auth_request *req); + int (*assoc)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_assoc_request *req); + int (*deauth)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_deauth_request *req); + int (*disassoc)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_disassoc_request *req); }; /* temporary wext handlers */ diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 58693e52d45..223e536e842 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1300,6 +1300,142 @@ static int ieee80211_scan(struct wiphy *wiphy, return ieee80211_request_scan(sdata, req); } +static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_auth_request *req) +{ + struct ieee80211_sub_if_data *sdata; + + if (!netif_running(dev)) + return -ENETDOWN; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (sdata->vif.type != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + switch (req->auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_OPEN; + break; + case NL80211_AUTHTYPE_SHARED_KEY: + sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_SHARED_KEY; + break; + case NL80211_AUTHTYPE_FT: + sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_FT; + break; + case NL80211_AUTHTYPE_NETWORK_EAP: + sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_LEAP; + break; + default: + return -EOPNOTSUPP; + } + + memcpy(sdata->u.mgd.bssid, req->peer_addr, ETH_ALEN); + sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL; + sdata->u.mgd.flags |= IEEE80211_STA_BSSID_SET; + + /* TODO: req->chan */ + sdata->u.mgd.flags |= IEEE80211_STA_AUTO_CHANNEL_SEL; + + if (req->ssid) { + sdata->u.mgd.flags |= IEEE80211_STA_SSID_SET; + memcpy(sdata->u.mgd.ssid, req->ssid, req->ssid_len); + sdata->u.mgd.ssid_len = req->ssid_len; + sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL; + } + + kfree(sdata->u.mgd.sme_auth_ie); + sdata->u.mgd.sme_auth_ie = NULL; + sdata->u.mgd.sme_auth_ie_len = 0; + if (req->ie) { + sdata->u.mgd.sme_auth_ie = kmalloc(req->ie_len, GFP_KERNEL); + if (sdata->u.mgd.sme_auth_ie == NULL) + return -ENOMEM; + memcpy(sdata->u.mgd.sme_auth_ie, req->ie, req->ie_len); + sdata->u.mgd.sme_auth_ie_len = req->ie_len; + } + + sdata->u.mgd.flags |= IEEE80211_STA_EXT_SME; + sdata->u.mgd.state = IEEE80211_STA_MLME_DIRECT_PROBE; + ieee80211_sta_req_auth(sdata); + return 0; +} + +static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_assoc_request *req) +{ + struct ieee80211_sub_if_data *sdata; + int ret; + + if (!netif_running(dev)) + return -ENETDOWN; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (sdata->vif.type != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + if (memcmp(sdata->u.mgd.bssid, req->peer_addr, ETH_ALEN) != 0 || + !(sdata->u.mgd.flags & IEEE80211_STA_AUTHENTICATED)) + return -ENOLINK; /* not authenticated */ + + sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL; + sdata->u.mgd.flags |= IEEE80211_STA_BSSID_SET; + + /* TODO: req->chan */ + sdata->u.mgd.flags |= IEEE80211_STA_AUTO_CHANNEL_SEL; + + if (req->ssid) { + sdata->u.mgd.flags |= IEEE80211_STA_SSID_SET; + memcpy(sdata->u.mgd.ssid, req->ssid, req->ssid_len); + sdata->u.mgd.ssid_len = req->ssid_len; + sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL; + } else + sdata->u.mgd.flags |= IEEE80211_STA_AUTO_SSID_SEL; + + ret = ieee80211_sta_set_extra_ie(sdata, req->ie, req->ie_len); + if (ret) + return ret; + + sdata->u.mgd.flags |= IEEE80211_STA_EXT_SME; + sdata->u.mgd.state = IEEE80211_STA_MLME_ASSOCIATE; + ieee80211_sta_req_auth(sdata); + return 0; +} + +static int ieee80211_deauth(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_deauth_request *req) +{ + struct ieee80211_sub_if_data *sdata; + + if (!netif_running(dev)) + return -ENETDOWN; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (sdata->vif.type != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + /* TODO: req->ie */ + return ieee80211_sta_deauthenticate(sdata, req->reason_code); +} + +static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_disassoc_request *req) +{ + struct ieee80211_sub_if_data *sdata; + + if (!netif_running(dev)) + return -ENETDOWN; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (sdata->vif.type != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + /* TODO: req->ie */ + return ieee80211_sta_disassociate(sdata, req->reason_code); +} + struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -1333,4 +1469,8 @@ struct cfg80211_ops mac80211_config_ops = { .suspend = ieee80211_suspend, .resume = ieee80211_resume, .scan = ieee80211_scan, + .auth = ieee80211_auth, + .assoc = ieee80211_assoc, + .deauth = ieee80211_deauth, + .disassoc = ieee80211_disassoc, }; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index ad12c2a03a9..7b96d95f48b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -256,6 +256,7 @@ struct mesh_preq_queue { #define IEEE80211_STA_TKIP_WEP_USED BIT(14) #define IEEE80211_STA_CSA_RECEIVED BIT(15) #define IEEE80211_STA_MFP_ENABLED BIT(16) +#define IEEE80211_STA_EXT_SME BIT(17) /* flags for MLME request */ #define IEEE80211_STA_REQ_SCAN 0 #define IEEE80211_STA_REQ_DIRECT_PROBE 1 @@ -266,6 +267,7 @@ struct mesh_preq_queue { #define IEEE80211_AUTH_ALG_OPEN BIT(0) #define IEEE80211_AUTH_ALG_SHARED_KEY BIT(1) #define IEEE80211_AUTH_ALG_LEAP BIT(2) +#define IEEE80211_AUTH_ALG_FT BIT(3) struct ieee80211_if_managed { struct timer_list timer; @@ -335,6 +337,9 @@ struct ieee80211_if_managed { size_t ie_deauth_len; u8 *ie_disassoc; size_t ie_disassoc_len; + + u8 *sme_auth_ie; + size_t sme_auth_ie_len; }; enum ieee80211_ibss_flags { @@ -970,7 +975,7 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, struct ieee80211_rx_status *rx_status); int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata, - char *ie, size_t len); + const char *ie, size_t len); void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local); void ieee80211_scan_failed(struct ieee80211_local *local); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 6dc7a61bc18..d1bcc843877 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -730,6 +730,8 @@ static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; + u8 *ies; + size_t ies_len; ifmgd->auth_tries++; if (ifmgd->auth_tries > IEEE80211_AUTH_MAX_TRIES) { @@ -755,7 +757,14 @@ static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata) printk(KERN_DEBUG "%s: authenticate with AP %pM\n", sdata->dev->name, ifmgd->bssid); - ieee80211_send_auth(sdata, 1, ifmgd->auth_alg, NULL, 0, + if (ifmgd->flags & IEEE80211_STA_EXT_SME) { + ies = ifmgd->sme_auth_ie; + ies_len = ifmgd->sme_auth_ie_len; + } else { + ies = NULL; + ies_len = 0; + } + ieee80211_send_auth(sdata, 1, ifmgd->auth_alg, ies, ies_len, ifmgd->bssid, 0); ifmgd->auth_transaction = 2; @@ -870,7 +879,8 @@ static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata) int wep_privacy; int privacy_invoked; - if (!ifmgd || (ifmgd->flags & IEEE80211_STA_MIXED_CELL)) + if (!ifmgd || (ifmgd->flags & (IEEE80211_STA_MIXED_CELL | + IEEE80211_STA_EXT_SME))) return 0; bss = ieee80211_rx_bss_get(local, ifmgd->bssid, @@ -998,7 +1008,11 @@ static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata) printk(KERN_DEBUG "%s: authenticated\n", sdata->dev->name); ifmgd->flags |= IEEE80211_STA_AUTHENTICATED; - ieee80211_associate(sdata); + if (ifmgd->flags & IEEE80211_STA_EXT_SME) { + /* Wait for SME to request association */ + ifmgd->state = IEEE80211_STA_MLME_DISABLED; + } else + ieee80211_associate(sdata); } @@ -1084,6 +1098,7 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, switch (ifmgd->auth_alg) { case WLAN_AUTH_OPEN: case WLAN_AUTH_LEAP: + case WLAN_AUTH_FT: ieee80211_auth_completed(sdata); cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len); break; @@ -1117,9 +1132,10 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: deauthenticated (Reason: %u)\n", sdata->dev->name, reason_code); - if (ifmgd->state == IEEE80211_STA_MLME_AUTHENTICATE || - ifmgd->state == IEEE80211_STA_MLME_ASSOCIATE || - ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) { + if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) && + (ifmgd->state == IEEE80211_STA_MLME_AUTHENTICATE || + ifmgd->state == IEEE80211_STA_MLME_ASSOCIATE || + ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED)) { ifmgd->state = IEEE80211_STA_MLME_DIRECT_PROBE; mod_timer(&ifmgd->timer, jiffies + IEEE80211_RETRY_AUTH_INTERVAL); @@ -1150,7 +1166,8 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: disassociated (Reason: %u)\n", sdata->dev->name, reason_code); - if (ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) { + if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) && + ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) { ifmgd->state = IEEE80211_STA_MLME_ASSOCIATE; mod_timer(&ifmgd->timer, jiffies + IEEE80211_RETRY_AUTH_INTERVAL); @@ -1664,6 +1681,8 @@ static void ieee80211_sta_reset_auth(struct ieee80211_sub_if_data *sdata) ifmgd->auth_alg = WLAN_AUTH_SHARED_KEY; else if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_LEAP) ifmgd->auth_alg = WLAN_AUTH_LEAP; + else if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_FT) + ifmgd->auth_alg = WLAN_AUTH_FT; else ifmgd->auth_alg = WLAN_AUTH_OPEN; ifmgd->auth_transaction = -1; @@ -1687,7 +1706,8 @@ static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata) u16 capa_val = WLAN_CAPABILITY_ESS; struct ieee80211_channel *chan = local->oper_channel; - if (ifmgd->flags & (IEEE80211_STA_AUTO_SSID_SEL | + if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) && + ifmgd->flags & (IEEE80211_STA_AUTO_SSID_SEL | IEEE80211_STA_AUTO_BSSID_SEL | IEEE80211_STA_AUTO_CHANNEL_SEL)) { capa_mask |= WLAN_CAPABILITY_PRIVACY; @@ -1884,7 +1904,11 @@ void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata) ieee80211_set_disassoc(sdata, true, true, WLAN_REASON_DEAUTH_LEAVING); - set_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request); + if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) || + ifmgd->state != IEEE80211_STA_MLME_ASSOCIATE) + set_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request); + else if (ifmgd->flags & IEEE80211_STA_EXT_SME) + set_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request); queue_work(local->hw.workqueue, &ifmgd->work); } } @@ -1953,7 +1977,8 @@ int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid) return ieee80211_sta_commit(sdata); } -int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata, char *ie, size_t len) +int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata, + const char *ie, size_t len) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index e55d2834764..ce21d66b102 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -137,6 +137,7 @@ static int ieee80211_ioctl_siwgenie(struct net_device *dev, if (ret) return ret; sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL; + sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME; ieee80211_sta_req_auth(sdata); return 0; } @@ -224,6 +225,7 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev, if (ret) return ret; + sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME; ieee80211_sta_req_auth(sdata); return 0; } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) @@ -287,6 +289,7 @@ static int ieee80211_ioctl_siwap(struct net_device *dev, ret = ieee80211_sta_set_bssid(sdata, (u8 *) &ap_addr->sa_data); if (ret) return ret; + sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME; ieee80211_sta_req_auth(sdata); return 0; } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index c034c2418cb..9e1318d1d4b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -111,6 +111,11 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { .len = IEEE80211_MAX_DATA_LEN }, [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED }, [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED }, + + [NL80211_ATTR_SSID] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_SSID_LEN }, + [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 }, + [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 }, }; /* message building helper */ @@ -265,6 +270,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, CMD(set_mesh_params, SET_MESH_PARAMS); CMD(change_bss, SET_BSS); CMD(set_mgmt_extra_ie, SET_MGMT_EXTRA_IE); + CMD(auth, AUTHENTICATE); + CMD(assoc, ASSOCIATE); + CMD(deauth, DEAUTHENTICATE); + CMD(disassoc, DISASSOCIATE); #undef CMD nla_nest_end(msg, nl_cmds); @@ -2646,6 +2655,228 @@ static int nl80211_dump_scan(struct sk_buff *skb, return err; } +static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + struct net_device *dev; + struct cfg80211_auth_request req; + struct wiphy *wiphy; + int err; + + rtnl_lock(); + + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + if (err) + goto unlock_rtnl; + + if (!drv->ops->auth) { + err = -EOPNOTSUPP; + goto out; + } + + if (!info->attrs[NL80211_ATTR_MAC]) { + err = -EINVAL; + goto out; + } + + wiphy = &drv->wiphy; + memset(&req, 0, sizeof(req)); + + req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { + req.chan = ieee80211_get_channel( + wiphy, + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); + if (!req.chan) { + err = -EINVAL; + goto out; + } + } + + if (info->attrs[NL80211_ATTR_SSID]) { + req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); + } + + if (info->attrs[NL80211_ATTR_IE]) { + req.ie = nla_data(info->attrs[NL80211_ATTR_IE]); + req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { + req.auth_type = + nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); + } + + err = drv->ops->auth(&drv->wiphy, dev, &req); + +out: + cfg80211_put_dev(drv); + dev_put(dev); +unlock_rtnl: + rtnl_unlock(); + return err; +} + +static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + struct net_device *dev; + struct cfg80211_assoc_request req; + struct wiphy *wiphy; + int err; + + rtnl_lock(); + + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + if (err) + goto unlock_rtnl; + + if (!drv->ops->assoc) { + err = -EOPNOTSUPP; + goto out; + } + + if (!info->attrs[NL80211_ATTR_MAC] || + !info->attrs[NL80211_ATTR_SSID]) { + err = -EINVAL; + goto out; + } + + wiphy = &drv->wiphy; + memset(&req, 0, sizeof(req)); + + req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { + req.chan = ieee80211_get_channel( + wiphy, + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); + if (!req.chan) { + err = -EINVAL; + goto out; + } + } + + if (nla_len(info->attrs[NL80211_ATTR_SSID]) > IEEE80211_MAX_SSID_LEN) { + err = -EINVAL; + goto out; + } + req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); + + if (info->attrs[NL80211_ATTR_IE]) { + req.ie = nla_data(info->attrs[NL80211_ATTR_IE]); + req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + err = drv->ops->assoc(&drv->wiphy, dev, &req); + +out: + cfg80211_put_dev(drv); + dev_put(dev); +unlock_rtnl: + rtnl_unlock(); + return err; +} + +static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + struct net_device *dev; + struct cfg80211_deauth_request req; + struct wiphy *wiphy; + int err; + + rtnl_lock(); + + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + if (err) + goto unlock_rtnl; + + if (!drv->ops->deauth) { + err = -EOPNOTSUPP; + goto out; + } + + if (!info->attrs[NL80211_ATTR_MAC]) { + err = -EINVAL; + goto out; + } + + wiphy = &drv->wiphy; + memset(&req, 0, sizeof(req)); + + req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (info->attrs[NL80211_ATTR_REASON_CODE]) + req.reason_code = + nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); + + if (info->attrs[NL80211_ATTR_IE]) { + req.ie = nla_data(info->attrs[NL80211_ATTR_IE]); + req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + err = drv->ops->deauth(&drv->wiphy, dev, &req); + +out: + cfg80211_put_dev(drv); + dev_put(dev); +unlock_rtnl: + rtnl_unlock(); + return err; +} + +static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + struct net_device *dev; + struct cfg80211_disassoc_request req; + struct wiphy *wiphy; + int err; + + rtnl_lock(); + + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + if (err) + goto unlock_rtnl; + + if (!drv->ops->disassoc) { + err = -EOPNOTSUPP; + goto out; + } + + if (!info->attrs[NL80211_ATTR_MAC]) { + err = -EINVAL; + goto out; + } + + wiphy = &drv->wiphy; + memset(&req, 0, sizeof(req)); + + req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (info->attrs[NL80211_ATTR_REASON_CODE]) + req.reason_code = + nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); + + if (info->attrs[NL80211_ATTR_IE]) { + req.ie = nla_data(info->attrs[NL80211_ATTR_IE]); + req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + err = drv->ops->disassoc(&drv->wiphy, dev, &req); + +out: + cfg80211_put_dev(drv); + dev_put(dev); +unlock_rtnl: + rtnl_unlock(); + return err; +} + static struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_GET_WIPHY, @@ -2829,6 +3060,30 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .dumpit = nl80211_dump_scan, }, + { + .cmd = NL80211_CMD_AUTHENTICATE, + .doit = nl80211_authenticate, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_ASSOCIATE, + .doit = nl80211_associate, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_DEAUTHENTICATE, + .doit = nl80211_deauthenticate, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_DISASSOCIATE, + .doit = nl80211_disassociate, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { .name = "mlme", -- cgit v1.2.3 From 1bf68e5cda40eaa26b186f043340fd283a4fb718 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 19 Mar 2009 13:33:52 +0100 Subject: wireless/p54: P54_SPI should depend on GENERIC_HARDIRQS m68k allmodconfig: | drivers/net/wireless/p54/p54spi.c: In function 'p54spi_probe': | drivers/net/wireless/p54/p54spi.c:675: error: implicit declaration of function | 'set_irq_type' | make[4]: *** [drivers/net/wireless/p54/p54spi.o] Error 1 Signed-off-by: Geert Uytterhoeven Signed-off-by: John W. Linville --- drivers/net/wireless/p54/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/p54/Kconfig b/drivers/net/wireless/p54/Kconfig index cfc5f41aa13..0e344ac10d2 100644 --- a/drivers/net/wireless/p54/Kconfig +++ b/drivers/net/wireless/p54/Kconfig @@ -64,7 +64,7 @@ config P54_PCI config P54_SPI tristate "Prism54 SPI (stlc45xx) support" - depends on P54_COMMON && SPI_MASTER + depends on P54_COMMON && SPI_MASTER && GENERIC_HARDIRQS ---help--- This driver is for stlc4550 or stlc4560 based wireless chips. This driver is experimental, untested and will probably only work on -- cgit v1.2.3 From 3e3ccb3d9b8d5a1b65b34e1be2decf213ba3bebb Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Thu, 19 Mar 2009 19:27:21 +0100 Subject: b43: Mask PHY TX error interrupt, if not debugging This masks the PHY TX error interrupt, if debugging is disabled. Currently we have a bug somewhere which triggers this interrupt once in a while. (Depends on the network noise/quality). While this is nonfatal, it scares the hell out of users and we frequently receive bugreports that incorrectly identify this error message as the reason. There's another problem with this. The PHY TX error interrupt is protected with a watchdog that will restart the device if it keeps triggering very often. This is used to fix interrupt storms from completely broken devices. However, this watchdog might trigger in completely normal operation. If the TX capacity of the card is saturated, the likeliness of the watchdog triggering increases, as more TX errors occur. The current threshold for the watchdog is 1000 errors in 15 seconds. This patch adds a workaround for the issue by just enabling the interrupt if debugging is disabled (by Kconfig or by modparam). This has the downside that real fatal PHY TX errors are not caught anymore. But this is nonfatal due to the following reasons: * If the card is not able to transmit anymore, MLME will notice anyway. * I did _never_ see a real fatal PHY TX error in a mainline b43 driver. * It does _not_ result in interrupt storms or something like that. It will simply result in a stalled card. It can be debugged by enabling the debugging module parameter. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index b72ef3fd315..4896e083111 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3993,6 +3993,8 @@ static void setup_struct_wldev_for_init(struct b43_wldev *dev) dev->irq_reason = 0; memset(dev->dma_reason, 0, sizeof(dev->dma_reason)); dev->irq_savedstate = B43_IRQ_MASKTEMPLATE; + if (b43_modparam_verbose < B43_VERBOSITY_DEBUG) + dev->irq_savedstate &= ~B43_IRQ_PHY_TXERR; dev->mac_suspended = 1; -- cgit v1.2.3 From 827b1fb44b7e41377a5498b9d070a11dfae2c283 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 13 Mar 2009 11:44:18 +0100 Subject: mac80211: resume properly, add suspend/resume test When mac80211 resumes, it currently doesn't reconfigure the interfaces entirely and also doesn't reconfigure BSS information -- fix this. Also, to be able to test this, add a debugfs file that just calls the suspend/resume code to see what happens when we go through that, without needing the time-consuming suspend/resume cycle. (Original version broke the build for CONFIG_PM=n. Define alternative functions for that situation. -- JWL) Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/debugfs.c | 24 ++++++++++++++++++++++++ net/mac80211/ieee80211_i.h | 12 ++++++++++++ net/mac80211/pm.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index e37f557de3f..210b9b6fecd 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -40,6 +40,10 @@ static const struct file_operations name## _ops = { \ local->debugfs.name = debugfs_create_file(#name, 0400, phyd, \ local, &name## _ops); +#define DEBUGFS_ADD_MODE(name, mode) \ + local->debugfs.name = debugfs_create_file(#name, mode, phyd, \ + local, &name## _ops); + #define DEBUGFS_DEL(name) \ debugfs_remove(local->debugfs.name); \ local->debugfs.name = NULL; @@ -113,6 +117,24 @@ static const struct file_operations tsf_ops = { .open = mac80211_open_file_generic }; +static ssize_t reset_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_local *local = file->private_data; + + rtnl_lock(); + __ieee80211_suspend(&local->hw); + __ieee80211_resume(&local->hw); + rtnl_unlock(); + + return count; +} + +static const struct file_operations reset_ops = { + .write = reset_write, + .open = mac80211_open_file_generic, +}; + /* statistics stuff */ #define DEBUGFS_STATS_FILE(name, buflen, fmt, value...) \ @@ -254,6 +276,7 @@ void debugfs_hw_add(struct ieee80211_local *local) DEBUGFS_ADD(total_ps_buffered); DEBUGFS_ADD(wep_iv); DEBUGFS_ADD(tsf); + DEBUGFS_ADD_MODE(reset, 0200); statsd = debugfs_create_dir("statistics", phyd); local->debugfs.statistics = statsd; @@ -308,6 +331,7 @@ void debugfs_hw_del(struct ieee80211_local *local) DEBUGFS_DEL(total_ps_buffered); DEBUGFS_DEL(wep_iv); DEBUGFS_DEL(tsf); + DEBUGFS_DEL(reset); DEBUGFS_STATS_DEL(transmitted_fragment_count); DEBUGFS_STATS_DEL(multicast_transmitted_frame_count); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 7b96d95f48b..547cfac218e 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -780,6 +780,7 @@ struct ieee80211_local { struct dentry *total_ps_buffered; struct dentry *wep_iv; struct dentry *tsf; + struct dentry *reset; struct dentry *statistics; struct local_debugfsdentries_statsdentries { struct dentry *transmitted_fragment_count; @@ -1059,8 +1060,19 @@ void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, u8 pwr_constr_elem_len); /* Suspend/resume */ +#ifdef CONFIG_PM int __ieee80211_suspend(struct ieee80211_hw *hw); int __ieee80211_resume(struct ieee80211_hw *hw); +#else +static inline int __ieee80211_suspend(struct ieee80211_hw *hw) +{ + return 0; +} +static inline int __ieee80211_resume(struct ieee80211_hw *hw) +{ + return 0; +} +#endif /* utility functions/constants */ extern void *mac80211_wiphy_privid; /* for wiphy privid */ diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 1e6152ac677..02730232649 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -143,6 +143,35 @@ int __ieee80211_resume(struct ieee80211_hw *hw) ieee80211_configure_filter(local); netif_addr_unlock_bh(local->mdev); + /* Finally also reconfigure all the BSS information */ + list_for_each_entry(sdata, &local->interfaces, list) { + u32 changed = ~0; + if (!netif_running(sdata->dev)) + continue; + switch (sdata->vif.type) { + case NL80211_IFTYPE_STATION: + /* disable beacon change bits */ + changed &= ~IEEE80211_IFCC_BEACON; + /* fall through */ + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MESH_POINT: + WARN_ON(ieee80211_if_config(sdata, changed)); + ieee80211_bss_info_change_notify(sdata, ~0); + break; + case NL80211_IFTYPE_WDS: + break; + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_MONITOR: + /* ignore virtual */ + break; + case NL80211_IFTYPE_UNSPECIFIED: + case __NL80211_IFTYPE_AFTER_LAST: + WARN_ON(1); + break; + } + } + ieee80211_wake_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_SUSPEND); -- cgit v1.2.3 From 8a92e2ee02dee127d309c73969aeb2a56567c9a0 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Fri, 20 Mar 2009 15:27:49 +0530 Subject: ath9k: Fix bug in reporting status of tx rate This patch updates count of every hw tried rate with appropriate tries before reporting tx status of a frame. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/xmit.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/ath9k/xmit.c b/drivers/net/wireless/ath9k/xmit.c index f2877193e95..21e90bca350 100644 --- a/drivers/net/wireless/ath9k/xmit.c +++ b/drivers/net/wireless/ath9k/xmit.c @@ -67,7 +67,7 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf); static int ath_tx_num_badfrms(struct ath_softc *sc, struct ath_buf *bf, int txok); static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds, - int nbad, int txok); + int nbad, int txok, bool update_rc); /*********************/ /* Aggregation logic */ @@ -370,11 +370,13 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, ath_tx_update_baw(sc, tid, bf->bf_seqno); spin_unlock_bh(&txq->axq_lock); - if (rc_update) - if (acked_cnt == 1 || txfail_cnt == 1) { - ath_tx_rc_status(bf, ds, nbad, txok); - rc_update = false; - } + if (rc_update && (acked_cnt == 1 || txfail_cnt == 1)) { + ath_tx_rc_status(bf, ds, nbad, txok, true); + rc_update = false; + } else { + ath_tx_rc_status(bf, ds, nbad, txok, false); + } + ath_tx_complete_buf(sc, bf, &bf_head, !txfail, sendbar); } else { /* retry the un-acked ones */ @@ -1779,8 +1781,6 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, tx_info->flags |= IEEE80211_TX_STAT_ACK; } - tx_info->status.rates[0].count = tx_status->retries + 1; - hdrlen = ieee80211_get_hdrlen_from_skb(skb); padsize = hdrlen & 3; if (padsize && hdrlen >= 24) { @@ -1867,30 +1867,39 @@ static int ath_tx_num_badfrms(struct ath_softc *sc, struct ath_buf *bf, } static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds, - int nbad, int txok) + int nbad, int txok, bool update_rc) { struct sk_buff *skb = (struct sk_buff *)bf->bf_mpdu; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info); + struct ieee80211_hw *hw = tx_info_priv->aphy->hw; + u8 i, tx_rateindex; if (txok) tx_info->status.ack_signal = ds->ds_txstat.ts_rssi; - tx_info_priv->update_rc = false; + tx_rateindex = ds->ds_txstat.ts_rateindex; + WARN_ON(tx_rateindex >= hw->max_rates); + + tx_info_priv->update_rc = update_rc; if (ds->ds_txstat.ts_status & ATH9K_TXERR_FILT) tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED; if ((ds->ds_txstat.ts_status & ATH9K_TXERR_FILT) == 0 && - (bf->bf_flags & ATH9K_TXDESC_NOACK) == 0) { + (bf->bf_flags & ATH9K_TXDESC_NOACK) == 0 && update_rc) { if (ieee80211_is_data(hdr->frame_control)) { memcpy(&tx_info_priv->tx, &ds->ds_txstat, sizeof(tx_info_priv->tx)); tx_info_priv->n_frames = bf->bf_nframes; tx_info_priv->n_bad_frames = nbad; - tx_info_priv->update_rc = true; } } + + for (i = tx_rateindex + 1; i < hw->max_rates; i++) + tx_info->status.rates[i].count = 0; + + tx_info->status.rates[tx_rateindex].count = bf->bf_retries + 1; } static void ath_wake_mac80211_queue(struct ath_softc *sc, struct ath_txq *txq) @@ -2009,7 +2018,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) bf->bf_retries = ds->ds_txstat.ts_longretry; if (ds->ds_txstat.ts_status & ATH9K_TXERR_XRETRY) bf->bf_state.bf_type |= BUF_XRETRY; - ath_tx_rc_status(bf, ds, 0, txok); + ath_tx_rc_status(bf, ds, 0, txok, true); } if (bf_isampdu(bf)) -- cgit v1.2.3 From 6b2c40326f9569283444d483448bcaadeca903e9 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Fri, 20 Mar 2009 15:27:50 +0530 Subject: ath9k: Nuke struct ath_xmit_status Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/ath9k.h | 4 ---- drivers/net/wireless/ath9k/xmit.c | 25 ++++++++----------------- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/ath9k/ath9k.h b/drivers/net/wireless/ath9k/ath9k.h index 2b025645511..2689a08a284 100644 --- a/drivers/net/wireless/ath9k/ath9k.h +++ b/drivers/net/wireless/ath9k/ath9k.h @@ -295,13 +295,9 @@ struct ath_tx_control { enum ath9k_internal_frame_type frame_type; }; -struct ath_xmit_status { - int retries; - int flags; #define ATH_TX_ERROR 0x01 #define ATH_TX_XRETRY 0x02 #define ATH_TX_BAR 0x04 -}; /* All RSSI values are noise floor adjusted */ struct ath_tx_stat { diff --git a/drivers/net/wireless/ath9k/xmit.c b/drivers/net/wireless/ath9k/xmit.c index 21e90bca350..689bdbf7880 100644 --- a/drivers/net/wireless/ath9k/xmit.c +++ b/drivers/net/wireless/ath9k/xmit.c @@ -1750,7 +1750,7 @@ exit: /*****************/ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, - struct ath_xmit_status *tx_status) + int tx_flags) { struct ieee80211_hw *hw = sc->hw; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); @@ -1771,12 +1771,10 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, tx_info->rate_driver_data[0] = NULL; } - if (tx_status->flags & ATH_TX_BAR) { + if (tx_flags & ATH_TX_BAR) tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; - tx_status->flags &= ~ATH_TX_BAR; - } - if (!(tx_status->flags & (ATH_TX_ERROR | ATH_TX_XRETRY))) { + if (!(tx_flags & (ATH_TX_ERROR | ATH_TX_XRETRY))) { /* Frame was ACKed */ tx_info->flags |= IEEE80211_TX_STAT_ACK; } @@ -1803,29 +1801,22 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, int txok, int sendbar) { struct sk_buff *skb = bf->bf_mpdu; - struct ath_xmit_status tx_status; unsigned long flags; + int tx_flags = 0; - /* - * Set retry information. - * NB: Don't use the information in the descriptor, because the frame - * could be software retried. - */ - tx_status.retries = bf->bf_retries; - tx_status.flags = 0; if (sendbar) - tx_status.flags = ATH_TX_BAR; + tx_flags = ATH_TX_BAR; if (!txok) { - tx_status.flags |= ATH_TX_ERROR; + tx_flags |= ATH_TX_ERROR; if (bf_isxretried(bf)) - tx_status.flags |= ATH_TX_XRETRY; + tx_flags |= ATH_TX_XRETRY; } dma_unmap_single(sc->dev, bf->bf_dmacontext, skb->len, DMA_TO_DEVICE); - ath_tx_complete(sc, skb, &tx_status); + ath_tx_complete(sc, skb, tx_flags); /* * Return the list of ath_buf of this mpdu to free queue -- cgit v1.2.3 From d7873cb9abb5d8b4b9f7f5749af06e4e03798733 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 20 Mar 2009 15:53:16 +0200 Subject: mac80211: Fix memleak in nl80211 authentication on deinit This file was forgotten from the quilt patch that added MLME primitives, so the kfree on interface removal is missing. Fix this potential memleak by freeing the temporary Authentication frame IEs from SME when the interface is being removed. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- net/mac80211/iface.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index f9f27b9cadb..6b56dc2208e 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -660,6 +660,7 @@ static void ieee80211_teardown_sdata(struct net_device *dev) kfree(sdata->u.mgd.ie_reassocreq); kfree(sdata->u.mgd.ie_deauth); kfree(sdata->u.mgd.ie_disassoc); + kfree(sdata->u.mgd.sme_auth_ie); break; case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_AP_VLAN: -- cgit v1.2.3 From feeb44454996cf5b375fad21697bf6202fe30dd2 Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Fri, 20 Mar 2009 16:43:20 +0100 Subject: ssb: remove EXPERIMENTAL dependencies. ssb is not experimental anymore. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/ssb/Kconfig | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig index b1b947edcf0..540a2948596 100644 --- a/drivers/ssb/Kconfig +++ b/drivers/ssb/Kconfig @@ -53,11 +53,11 @@ config SSB_B43_PCI_BRIDGE config SSB_PCMCIAHOST_POSSIBLE bool - depends on SSB && (PCMCIA = y || PCMCIA = SSB) && EXPERIMENTAL + depends on SSB && (PCMCIA = y || PCMCIA = SSB) default y config SSB_PCMCIAHOST - bool "Support for SSB on PCMCIA-bus host (EXPERIMENTAL)" + bool "Support for SSB on PCMCIA-bus host" depends on SSB_PCMCIAHOST_POSSIBLE select SSB_SPROM help @@ -107,14 +107,14 @@ config SSB_DRIVER_PCICORE If unsure, say Y config SSB_PCICORE_HOSTMODE - bool "Hostmode support for SSB PCI core (EXPERIMENTAL)" - depends on SSB_DRIVER_PCICORE && SSB_DRIVER_MIPS && EXPERIMENTAL + bool "Hostmode support for SSB PCI core" + depends on SSB_DRIVER_PCICORE && SSB_DRIVER_MIPS help PCIcore hostmode operation (external PCI bus). config SSB_DRIVER_MIPS - bool "SSB Broadcom MIPS core driver (EXPERIMENTAL)" - depends on SSB && MIPS && EXPERIMENTAL + bool "SSB Broadcom MIPS core driver" + depends on SSB && MIPS select SSB_SERIAL help Driver for the Sonics Silicon Backplane attached @@ -129,8 +129,8 @@ config SSB_EMBEDDED default y config SSB_DRIVER_EXTIF - bool "SSB Broadcom EXTIF core driver (EXPERIMENTAL)" - depends on SSB_DRIVER_MIPS && EXPERIMENTAL + bool "SSB Broadcom EXTIF core driver" + depends on SSB_DRIVER_MIPS help Driver for the Sonics Silicon Backplane attached Broadcom EXTIF core. -- cgit v1.2.3 From 65fc73ac4a310945dfeceac961726c2765ad2ec0 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 20 Mar 2009 21:21:16 +0200 Subject: nl80211: Remove NL80211_CMD_SET_MGMT_EXTRA_IE The functionality that NL80211_CMD_SET_MGMT_EXTRA_IE provided can now be achieved with cleaner design by adding IE(s) into NL80211_CMD_TRIGGER_SCAN, NL80211_CMD_AUTHENTICATE, NL80211_CMD_ASSOCIATE, NL80211_CMD_DEAUTHENTICATE, and NL80211_CMD_DISASSOCIATE. Since this is a very recently added command and there are no known (or known planned) applications using NL80211_CMD_SET_MGMT_EXTRA_IE and taken into account how much extra complexity it adds to the IE processing we have now (and need to add in the future to fix IE order in couple of frames), it looks like the best option is to just remove the implementation of this command for now. The enum values themselves are left to avoid changing the nl80211 command or attribute numbers. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- include/linux/nl80211.h | 8 ++++- include/net/cfg80211.h | 26 -------------- net/mac80211/cfg.c | 86 ---------------------------------------------- net/mac80211/ieee80211_i.h | 15 -------- net/mac80211/iface.c | 7 ---- net/mac80211/mlme.c | 36 ++----------------- net/mac80211/util.c | 29 +++------------- net/wireless/nl80211.c | 47 ------------------------- 8 files changed, 14 insertions(+), 240 deletions(-) diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 9685eaab40a..cbe8ce3bf48 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -142,6 +142,12 @@ * %NL80211_ATTR_IE. If the command succeeds, the requested data will be * added to all specified management frames generated by * kernel/firmware/driver. + * Note: This command has been removed and it is only reserved at this + * point to avoid re-using existing command number. The functionality this + * command was planned for has been provided with cleaner design with the + * option to specify additional IEs in NL80211_CMD_TRIGGER_SCAN, + * NL80211_CMD_AUTHENTICATE, NL80211_CMD_ASSOCIATE, + * NL80211_CMD_DEAUTHENTICATE, and NL80211_CMD_DISASSOCIATE. * * @NL80211_CMD_GET_SCAN: get scan results * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters @@ -238,7 +244,7 @@ enum nl80211_commands { NL80211_CMD_GET_MESH_PARAMS, NL80211_CMD_SET_MESH_PARAMS, - NL80211_CMD_SET_MGMT_EXTRA_IE, + NL80211_CMD_SET_MGMT_EXTRA_IE /* reserved; not used */, NL80211_CMD_GET_REG, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 0da9a55881a..dca4a6b0461 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -471,26 +471,6 @@ struct ieee80211_txq_params { u8 aifs; }; -/** - * struct mgmt_extra_ie_params - Extra management frame IE parameters - * - * Used to add extra IE(s) into management frames. If the driver cannot add the - * requested data into all management frames of the specified subtype that are - * generated in kernel or firmware/hardware, it must reject the configuration - * call. The IE data buffer is added to the end of the specified management - * frame body after all other IEs. This addition is not applied to frames that - * are injected through a monitor interface. - * - * @subtype: Management frame subtype - * @ies: IE data buffer or %NULL to remove previous data - * @ies_len: Length of @ies in octets - */ -struct mgmt_extra_ie_params { - u8 subtype; - u8 *ies; - int ies_len; -}; - /* from net/wireless.h */ struct wiphy; @@ -743,8 +723,6 @@ struct cfg80211_disassoc_request { * * @set_channel: Set channel * - * @set_mgmt_extra_ie: Set extra IE data for management frames - * * @scan: Request to do a scan. If returning zero, the scan request is given * the driver, and will be valid until passed to cfg80211_scan_done(). * For scan results, call cfg80211_inform_bss(); you can call this outside @@ -828,10 +806,6 @@ struct cfg80211_ops { struct ieee80211_channel *chan, enum nl80211_channel_type channel_type); - int (*set_mgmt_extra_ie)(struct wiphy *wiphy, - struct net_device *dev, - struct mgmt_extra_ie_params *params); - int (*scan)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_scan_request *request); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 223e536e842..f5c15c9a00c 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1181,91 +1181,6 @@ static int ieee80211_set_channel(struct wiphy *wiphy, return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); } -static int set_mgmt_extra_ie_sta(struct ieee80211_sub_if_data *sdata, - u8 subtype, u8 *ies, size_t ies_len) -{ - struct ieee80211_local *local = sdata->local; - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - - switch (subtype) { - case IEEE80211_STYPE_PROBE_REQ >> 4: - if (local->ops->hw_scan) - break; - kfree(ifmgd->ie_probereq); - ifmgd->ie_probereq = ies; - ifmgd->ie_probereq_len = ies_len; - return 0; - case IEEE80211_STYPE_PROBE_RESP >> 4: - kfree(ifmgd->ie_proberesp); - ifmgd->ie_proberesp = ies; - ifmgd->ie_proberesp_len = ies_len; - return 0; - case IEEE80211_STYPE_AUTH >> 4: - kfree(ifmgd->ie_auth); - ifmgd->ie_auth = ies; - ifmgd->ie_auth_len = ies_len; - return 0; - case IEEE80211_STYPE_ASSOC_REQ >> 4: - kfree(ifmgd->ie_assocreq); - ifmgd->ie_assocreq = ies; - ifmgd->ie_assocreq_len = ies_len; - return 0; - case IEEE80211_STYPE_REASSOC_REQ >> 4: - kfree(ifmgd->ie_reassocreq); - ifmgd->ie_reassocreq = ies; - ifmgd->ie_reassocreq_len = ies_len; - return 0; - case IEEE80211_STYPE_DEAUTH >> 4: - kfree(ifmgd->ie_deauth); - ifmgd->ie_deauth = ies; - ifmgd->ie_deauth_len = ies_len; - return 0; - case IEEE80211_STYPE_DISASSOC >> 4: - kfree(ifmgd->ie_disassoc); - ifmgd->ie_disassoc = ies; - ifmgd->ie_disassoc_len = ies_len; - return 0; - } - - return -EOPNOTSUPP; -} - -static int ieee80211_set_mgmt_extra_ie(struct wiphy *wiphy, - struct net_device *dev, - struct mgmt_extra_ie_params *params) -{ - struct ieee80211_sub_if_data *sdata; - u8 *ies; - size_t ies_len; - int ret = -EOPNOTSUPP; - - if (params->ies) { - ies = kmemdup(params->ies, params->ies_len, GFP_KERNEL); - if (ies == NULL) - return -ENOMEM; - ies_len = params->ies_len; - } else { - ies = NULL; - ies_len = 0; - } - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - switch (sdata->vif.type) { - case NL80211_IFTYPE_STATION: - ret = set_mgmt_extra_ie_sta(sdata, params->subtype, - ies, ies_len); - break; - default: - ret = -EOPNOTSUPP; - break; - } - - if (ret) - kfree(ies); - return ret; -} - #ifdef CONFIG_PM static int ieee80211_suspend(struct wiphy *wiphy) { @@ -1465,7 +1380,6 @@ struct cfg80211_ops mac80211_config_ops = { .change_bss = ieee80211_change_bss, .set_txq_params = ieee80211_set_txq_params, .set_channel = ieee80211_set_channel, - .set_mgmt_extra_ie = ieee80211_set_mgmt_extra_ie, .suspend = ieee80211_suspend, .resume = ieee80211_resume, .scan = ieee80211_scan, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 547cfac218e..f69e84ab961 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -323,21 +323,6 @@ struct ieee80211_if_managed { int wmm_last_param_set; /* Extra IE data for management frames */ - u8 *ie_probereq; - size_t ie_probereq_len; - u8 *ie_proberesp; - size_t ie_proberesp_len; - u8 *ie_auth; - size_t ie_auth_len; - u8 *ie_assocreq; - size_t ie_assocreq_len; - u8 *ie_reassocreq; - size_t ie_reassocreq_len; - u8 *ie_deauth; - size_t ie_deauth_len; - u8 *ie_disassoc; - size_t ie_disassoc_len; - u8 *sme_auth_ie; size_t sme_auth_ie_len; }; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 6b56dc2208e..34f4798a98f 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -653,13 +653,6 @@ static void ieee80211_teardown_sdata(struct net_device *dev) kfree(sdata->u.mgd.extra_ie); kfree(sdata->u.mgd.assocreq_ies); kfree(sdata->u.mgd.assocresp_ies); - kfree(sdata->u.mgd.ie_probereq); - kfree(sdata->u.mgd.ie_proberesp); - kfree(sdata->u.mgd.ie_auth); - kfree(sdata->u.mgd.ie_assocreq); - kfree(sdata->u.mgd.ie_reassocreq); - kfree(sdata->u.mgd.ie_deauth); - kfree(sdata->u.mgd.ie_disassoc); kfree(sdata->u.mgd.sme_auth_ie); break; case NL80211_IFTYPE_WDS: diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d1bcc843877..b0808efcedf 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -82,38 +82,23 @@ static int ieee80211_compatible_rates(struct ieee80211_bss *bss, /* frame sending functions */ -static void add_extra_ies(struct sk_buff *skb, u8 *ies, size_t ies_len) -{ - if (ies) - memcpy(skb_put(skb, ies_len), ies, ies_len); -} - static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; - u8 *pos, *ies, *ht_ie, *e_ies; + u8 *pos, *ies, *ht_ie; int i, len, count, rates_len, supp_rates_len; u16 capab; struct ieee80211_bss *bss; int wmm = 0; struct ieee80211_supported_band *sband; u32 rates = 0; - size_t e_ies_len; - - if (ifmgd->flags & IEEE80211_STA_PREV_BSSID_SET) { - e_ies = sdata->u.mgd.ie_reassocreq; - e_ies_len = sdata->u.mgd.ie_reassocreq_len; - } else { - e_ies = sdata->u.mgd.ie_assocreq; - e_ies_len = sdata->u.mgd.ie_assocreq_len; - } skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 + ifmgd->extra_ie_len + - ifmgd->ssid_len + e_ies_len); + ifmgd->ssid_len); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for assoc " "frame\n", sdata->dev->name); @@ -304,8 +289,6 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); } - add_extra_ies(skb, e_ies, e_ies_len); - kfree(ifmgd->assocreq_ies); ifmgd->assocreq_ies_len = (skb->data + skb->len) - ies; ifmgd->assocreq_ies = kmalloc(ifmgd->assocreq_ies_len, GFP_KERNEL); @@ -323,19 +306,8 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; - u8 *ies; - size_t ies_len; - if (stype == IEEE80211_STYPE_DEAUTH) { - ies = sdata->u.mgd.ie_deauth; - ies_len = sdata->u.mgd.ie_deauth_len; - } else { - ies = sdata->u.mgd.ie_disassoc; - ies_len = sdata->u.mgd.ie_disassoc_len; - } - - skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + - ies_len); + skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt)); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for " "deauth/disassoc frame\n", sdata->dev->name); @@ -353,8 +325,6 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, /* u.deauth.reason_code == u.disassoc.reason_code */ mgmt->u.deauth.reason_code = cpu_to_le16(reason); - add_extra_ies(skb, ies, ies_len); - ieee80211_tx_skb(sdata, skb, ifmgd->flags & IEEE80211_STA_MFP_ENABLED); } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index e0431a1d218..444bb14c95e 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -846,16 +846,9 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; - const u8 *ie_auth = NULL; - int ie_auth_len = 0; - - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - ie_auth_len = sdata->u.mgd.ie_auth_len; - ie_auth = sdata->u.mgd.ie_auth; - } skb = dev_alloc_skb(local->hw.extra_tx_headroom + - sizeof(*mgmt) + 6 + extra_len + ie_auth_len); + sizeof(*mgmt) + 6 + extra_len); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for auth " "frame\n", sdata->dev->name); @@ -877,8 +870,6 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, mgmt->u.auth.status_code = cpu_to_le16(0); if (extra) memcpy(skb_put(skb, extra_len), extra, extra_len); - if (ie_auth) - memcpy(skb_put(skb, ie_auth_len), ie_auth, ie_auth_len); ieee80211_tx_skb(sdata, skb, encrypt); } @@ -891,20 +882,11 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, struct ieee80211_supported_band *sband; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; - u8 *pos, *supp_rates, *esupp_rates = NULL, *extra_preq_ie = NULL; - int i, extra_preq_ie_len = 0; - - switch (sdata->vif.type) { - case NL80211_IFTYPE_STATION: - extra_preq_ie_len = sdata->u.mgd.ie_probereq_len; - extra_preq_ie = sdata->u.mgd.ie_probereq; - break; - default: - break; - } + u8 *pos, *supp_rates, *esupp_rates = NULL; + int i; skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 + - ie_len + extra_preq_ie_len); + ie_len); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for probe " "request\n", sdata->dev->name); @@ -953,9 +935,6 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, if (ie) memcpy(skb_put(skb, ie_len), ie, ie_len); - if (extra_preq_ie) - memcpy(skb_put(skb, extra_preq_ie_len), extra_preq_ie, - extra_preq_ie_len); ieee80211_tx_skb(sdata, skb, 0); } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9e1318d1d4b..44c79972be5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -269,7 +269,6 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, CMD(add_mpath, NEW_MPATH); CMD(set_mesh_params, SET_MESH_PARAMS); CMD(change_bss, SET_BSS); - CMD(set_mgmt_extra_ie, SET_MGMT_EXTRA_IE); CMD(auth, AUTHENTICATE); CMD(assoc, ASSOCIATE); CMD(deauth, DEAUTHENTICATE); @@ -2355,46 +2354,6 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } -static int nl80211_set_mgmt_extra_ie(struct sk_buff *skb, - struct genl_info *info) -{ - struct cfg80211_registered_device *drv; - int err; - struct net_device *dev; - struct mgmt_extra_ie_params params; - - memset(¶ms, 0, sizeof(params)); - - if (!info->attrs[NL80211_ATTR_MGMT_SUBTYPE]) - return -EINVAL; - params.subtype = nla_get_u8(info->attrs[NL80211_ATTR_MGMT_SUBTYPE]); - if (params.subtype > 15) - return -EINVAL; /* FC Subtype field is 4 bits (0..15) */ - - if (info->attrs[NL80211_ATTR_IE]) { - params.ies = nla_data(info->attrs[NL80211_ATTR_IE]); - params.ies_len = nla_len(info->attrs[NL80211_ATTR_IE]); - } - - rtnl_lock(); - - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); - if (err) - goto out_rtnl; - - if (drv->ops->set_mgmt_extra_ie) - err = drv->ops->set_mgmt_extra_ie(&drv->wiphy, dev, ¶ms); - else - err = -EOPNOTSUPP; - - cfg80211_put_dev(drv); - dev_put(dev); - out_rtnl: - rtnl_unlock(); - - return err; -} - static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *drv; @@ -3043,12 +3002,6 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, }, - { - .cmd = NL80211_CMD_SET_MGMT_EXTRA_IE, - .doit = nl80211_set_mgmt_extra_ie, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - }, { .cmd = NL80211_CMD_TRIGGER_SCAN, .doit = nl80211_trigger_scan, -- cgit v1.2.3 From 255e737eab645ec6037baeca04a5e0a7c3b1f459 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 20 Mar 2009 21:21:17 +0200 Subject: nl80211: Add more through validation of MLME command parameters Check that the used authentication type and reason code are valid here so that drivers/mac80211 do not need to care about this. In addition, remove the unnecessary validation of SSID attribute length which is taken care of by netlink policy. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 44c79972be5..6f38ee7a3c9 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2614,6 +2614,14 @@ static int nl80211_dump_scan(struct sk_buff *skb, return err; } +static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type) +{ + return auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM || + auth_type == NL80211_AUTHTYPE_SHARED_KEY || + auth_type == NL80211_AUTHTYPE_FT || + auth_type == NL80211_AUTHTYPE_NETWORK_EAP; +} + static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *drv; @@ -2666,6 +2674,10 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { req.auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); + if (!nl80211_valid_auth_type(req.auth_type)) { + err = -EINVAL; + goto out; + } } err = drv->ops->auth(&drv->wiphy, dev, &req); @@ -2718,10 +2730,6 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) } } - if (nla_len(info->attrs[NL80211_ATTR_SSID]) > IEEE80211_MAX_SSID_LEN) { - err = -EINVAL; - goto out; - } req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); @@ -2769,9 +2777,15 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - if (info->attrs[NL80211_ATTR_REASON_CODE]) + if (info->attrs[NL80211_ATTR_REASON_CODE]) { req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); + if (req.reason_code == 0) { + /* Reason Code 0 is reserved */ + err = -EINVAL; + goto out; + } + } if (info->attrs[NL80211_ATTR_IE]) { req.ie = nla_data(info->attrs[NL80211_ATTR_IE]); @@ -2817,9 +2831,15 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - if (info->attrs[NL80211_ATTR_REASON_CODE]) + if (info->attrs[NL80211_ATTR_REASON_CODE]) { req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); + if (req.reason_code == 0) { + /* Reason Code 0 is reserved */ + err = -EINVAL; + goto out; + } + } if (info->attrs[NL80211_ATTR_IE]) { req.ie = nla_data(info->attrs[NL80211_ATTR_IE]); -- cgit v1.2.3 From 35a8efe1a67ba5d7bb7492f67f52ed2aa4925892 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 20 Mar 2009 21:21:18 +0200 Subject: nl80211: Check that netif_runnin is true in cfg80211 code We do not want to require all the drivers using cfg80211 to need to do this or to be prepared to handle these commands when the interface is down. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 25 ------------------------- net/wireless/nl80211.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 25 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index f5c15c9a00c..b5810b4c79a 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -728,10 +728,6 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, int err; int layer2_update; - /* Prevent a race with changing the rate control algorithm */ - if (!netif_running(dev)) - return -ENETDOWN; - if (params->vlan) { sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); @@ -860,9 +856,6 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev, struct sta_info *sta; int err; - if (!netif_running(dev)) - return -ENETDOWN; - sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT) @@ -913,9 +906,6 @@ static int ieee80211_change_mpath(struct wiphy *wiphy, struct mesh_path *mpath; struct sta_info *sta; - if (!netif_running(dev)) - return -ENETDOWN; - sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT) @@ -1202,9 +1192,6 @@ static int ieee80211_scan(struct wiphy *wiphy, { struct ieee80211_sub_if_data *sdata; - if (!netif_running(dev)) - return -ENETDOWN; - sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->vif.type != NL80211_IFTYPE_STATION && @@ -1220,9 +1207,6 @@ static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev, { struct ieee80211_sub_if_data *sdata; - if (!netif_running(dev)) - return -ENETDOWN; - sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->vif.type != NL80211_IFTYPE_STATION) @@ -1282,9 +1266,6 @@ static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_sub_if_data *sdata; int ret; - if (!netif_running(dev)) - return -ENETDOWN; - sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->vif.type != NL80211_IFTYPE_STATION) @@ -1323,9 +1304,6 @@ static int ieee80211_deauth(struct wiphy *wiphy, struct net_device *dev, { struct ieee80211_sub_if_data *sdata; - if (!netif_running(dev)) - return -ENETDOWN; - sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->vif.type != NL80211_IFTYPE_STATION) return -EOPNOTSUPP; @@ -1339,9 +1317,6 @@ static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev, { struct ieee80211_sub_if_data *sdata; - if (!netif_running(dev)) - return -ENETDOWN; - sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->vif.type != NL80211_IFTYPE_STATION) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 6f38ee7a3c9..6bb73a3a339 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1556,6 +1556,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) goto out; } + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } + err = drv->ops->add_station(&drv->wiphy, dev, mac_addr, ¶ms); out: @@ -1808,6 +1813,11 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) goto out; } + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } + err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop); out: @@ -1846,6 +1856,11 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) goto out; } + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } + err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop); out: @@ -2380,6 +2395,11 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) goto out; } + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } + if (drv->scan_req) { err = -EBUSY; goto out; @@ -2641,6 +2661,11 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) goto out; } + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } + if (!info->attrs[NL80211_ATTR_MAC]) { err = -EINVAL; goto out; @@ -2709,6 +2734,11 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) goto out; } + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } + if (!info->attrs[NL80211_ATTR_MAC] || !info->attrs[NL80211_ATTR_SSID]) { err = -EINVAL; @@ -2767,6 +2797,11 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) goto out; } + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } + if (!info->attrs[NL80211_ATTR_MAC]) { err = -EINVAL; goto out; @@ -2821,6 +2856,11 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) goto out; } + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } + if (!info->attrs[NL80211_ATTR_MAC]) { err = -EINVAL; goto out; -- cgit v1.2.3 From eec60b037a875513d9715dcdb90b13ed81fc5f26 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 20 Mar 2009 21:21:19 +0200 Subject: nl80211: Check iftype in cfg80211 code We do not want to require all the drivers using cfg80211 to need to do this. In addition, make the error values consistent by using EOPNOTSUPP instead of semi-random assortment of errno values. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 40 ---------------------------------- net/wireless/nl80211.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 42 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index b5810b4c79a..e677b751d46 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -540,9 +540,6 @@ static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_AP) - return -EINVAL; - old = sdata->u.ap.beacon; if (old) @@ -559,9 +556,6 @@ static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_AP) - return -EINVAL; - old = sdata->u.ap.beacon; if (!old) @@ -577,9 +571,6 @@ static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev) sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_AP) - return -EINVAL; - old = sdata->u.ap.beacon; if (!old) @@ -858,9 +849,6 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT) - return -ENOTSUPP; - rcu_read_lock(); sta = sta_info_get(local, next_hop); if (!sta) { @@ -908,9 +896,6 @@ static int ieee80211_change_mpath(struct wiphy *wiphy, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT) - return -ENOTSUPP; - rcu_read_lock(); sta = sta_info_get(local, next_hop); @@ -979,9 +964,6 @@ static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT) - return -ENOTSUPP; - rcu_read_lock(); mpath = mesh_path_lookup(dst, sdata); if (!mpath) { @@ -1003,9 +985,6 @@ static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT) - return -ENOTSUPP; - rcu_read_lock(); mpath = mesh_path_lookup_by_idx(idx, sdata); if (!mpath) { @@ -1025,8 +1004,6 @@ static int ieee80211_get_mesh_params(struct wiphy *wiphy, struct ieee80211_sub_if_data *sdata; sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT) - return -ENOTSUPP; memcpy(conf, &(sdata->u.mesh.mshcfg), sizeof(struct mesh_config)); return 0; } @@ -1044,9 +1021,6 @@ static int ieee80211_set_mesh_params(struct wiphy *wiphy, struct ieee80211_sub_if_data *sdata; sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT) - return -ENOTSUPP; - /* Set the config options which we are interested in setting */ conf = &(sdata->u.mesh.mshcfg); if (_chg_mesh_attr(NL80211_MESHCONF_RETRY_TIMEOUT, mask)) @@ -1094,9 +1068,6 @@ static int ieee80211_change_bss(struct wiphy *wiphy, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_AP) - return -EINVAL; - if (params->use_cts_prot >= 0) { sdata->vif.bss_conf.use_cts_prot = params->use_cts_prot; changed |= BSS_CHANGED_ERP_CTS_PROT; @@ -1209,9 +1180,6 @@ static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_STATION) - return -EOPNOTSUPP; - switch (req->auth_type) { case NL80211_AUTHTYPE_OPEN_SYSTEM: sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_OPEN; @@ -1268,9 +1236,6 @@ static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_STATION) - return -EOPNOTSUPP; - if (memcmp(sdata->u.mgd.bssid, req->peer_addr, ETH_ALEN) != 0 || !(sdata->u.mgd.flags & IEEE80211_STA_AUTHENTICATED)) return -ENOLINK; /* not authenticated */ @@ -1305,8 +1270,6 @@ static int ieee80211_deauth(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_sub_if_data *sdata; sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_STATION) - return -EOPNOTSUPP; /* TODO: req->ie */ return ieee80211_sta_deauthenticate(sdata, req->reason_code); @@ -1319,9 +1282,6 @@ static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->vif.type != NL80211_IFTYPE_STATION) - return -EOPNOTSUPP; - /* TODO: req->ie */ return ieee80211_sta_disassociate(sdata, req->reason_code); } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 6bb73a3a339..a7d0b94f6b5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1049,6 +1049,11 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) if (err) goto unlock_rtnl; + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) { + err = -EOPNOTSUPP; + goto out; + } + switch (info->genlhdr->cmd) { case NL80211_CMD_NEW_BEACON: /* these are required for NEW_BEACON */ @@ -1136,6 +1141,10 @@ static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) goto out; } + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) { + err = -EOPNOTSUPP; + goto out; + } err = drv->ops->del_beacon(&drv->wiphy, dev); out: @@ -1324,7 +1333,7 @@ static int nl80211_dump_station(struct sk_buff *skb, } if (!dev->ops->dump_station) { - err = -ENOSYS; + err = -EOPNOTSUPP; goto out_err; } @@ -1698,10 +1707,15 @@ static int nl80211_dump_mpath(struct sk_buff *skb, } if (!dev->ops->dump_mpath) { - err = -ENOSYS; + err = -EOPNOTSUPP; goto out_err; } + if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) { + err = -EOPNOTSUPP; + goto out; + } + while (1) { err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx, dst, next_hop, &pinfo); @@ -1759,6 +1773,11 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) goto out; } + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) { + err = -EOPNOTSUPP; + goto out; + } + err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo); if (err) goto out; @@ -1813,6 +1832,11 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) goto out; } + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) { + err = -EOPNOTSUPP; + goto out; + } + if (!netif_running(dev)) { err = -ENETDOWN; goto out; @@ -1856,6 +1880,11 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) goto out; } + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) { + err = -EOPNOTSUPP; + goto out; + } + if (!netif_running(dev)) { err = -ENETDOWN; goto out; @@ -1944,6 +1973,11 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) goto out; } + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) { + err = -EOPNOTSUPP; + goto out; + } + err = drv->ops->change_bss(&drv->wiphy, dev, ¶ms); out: @@ -2661,6 +2695,11 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) goto out; } + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { + err = -EOPNOTSUPP; + goto out; + } + if (!netif_running(dev)) { err = -ENETDOWN; goto out; @@ -2734,6 +2773,11 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) goto out; } + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { + err = -EOPNOTSUPP; + goto out; + } + if (!netif_running(dev)) { err = -ENETDOWN; goto out; @@ -2797,6 +2841,11 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) goto out; } + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { + err = -EOPNOTSUPP; + goto out; + } + if (!netif_running(dev)) { err = -ENETDOWN; goto out; @@ -2856,6 +2905,11 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) goto out; } + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { + err = -EOPNOTSUPP; + goto out; + } + if (!netif_running(dev)) { err = -ENETDOWN; goto out; -- cgit v1.2.3 From d8cd7effc20027c313d4086b123046ff9f9a5814 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Mon, 23 Mar 2009 15:37:45 +0100 Subject: p54: fix SoftLED compile dependencies This patch fixes a compile problem when the MAC80211_LEDS triggers are enabled but not LED class itself. (which is sort of pointless, but anyway...) Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/Kconfig | 5 +++++ drivers/net/wireless/p54/p54common.c | 16 ++++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/p54/Kconfig b/drivers/net/wireless/p54/Kconfig index 0e344ac10d2..7d6e9d10820 100644 --- a/drivers/net/wireless/p54/Kconfig +++ b/drivers/net/wireless/p54/Kconfig @@ -71,3 +71,8 @@ config P54_SPI Nokia's N800/N810 Portable Internet Tablet. If you choose to build a module, it'll be called p54spi. + +config P54_LEDS + bool + depends on P54_COMMON && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = P54_COMMON) + default y diff --git a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c index 0a989834b70..0c1b0577d4e 100644 --- a/drivers/net/wireless/p54/p54common.c +++ b/drivers/net/wireless/p54/p54common.c @@ -21,9 +21,9 @@ #include #include -#ifdef CONFIG_MAC80211_LEDS +#ifdef CONFIG_P54_LEDS #include -#endif /* CONFIG_MAC80211_LEDS */ +#endif /* CONFIG_P54_LEDS */ #include "p54.h" #include "p54common.h" @@ -2420,7 +2420,7 @@ static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, return 0; } -#ifdef CONFIG_MAC80211_LEDS +#ifdef CONFIG_P54_LEDS static void p54_led_brightness_set(struct led_classdev *led_dev, enum led_brightness brightness) { @@ -2508,7 +2508,7 @@ static void p54_unregister_leds(struct ieee80211_hw *dev) if (priv->assoc_led.registered) led_classdev_unregister(&priv->assoc_led.led_dev); } -#endif /* CONFIG_MAC80211_LEDS */ +#endif /* CONFIG_P54_LEDS */ static const struct ieee80211_ops p54_ops = { .tx = p54_tx, @@ -2592,11 +2592,11 @@ int p54_register_common(struct ieee80211_hw *dev, struct device *pdev) return err; } - #ifdef CONFIG_MAC80211_LEDS +#ifdef CONFIG_P54_LEDS err = p54_init_leds(dev); if (err) return err; - #endif /* CONFIG_MAC80211_LEDS */ +#endif /* CONFIG_P54_LEDS */ dev_info(pdev, "is registered as '%s'\n", wiphy_name(dev->wiphy)); return 0; @@ -2610,9 +2610,9 @@ void p54_free_common(struct ieee80211_hw *dev) kfree(priv->output_limit); kfree(priv->curve_data); - #ifdef CONFIG_MAC80211_LEDS +#ifdef CONFIG_P54_LEDS p54_unregister_leds(dev); - #endif /* CONFIG_MAC80211_LEDS */ +#endif /* CONFIG_P54_LEDS */ } EXPORT_SYMBOL_GPL(p54_free_common); -- cgit v1.2.3 From 9cb5412b0760981d43ac3e612992c90cea690e72 Mon Sep 17 00:00:00 2001 From: Pat Erley Date: Fri, 20 Mar 2009 22:59:59 -0400 Subject: Add mesh point functionality to ath9k This patch enables mesh point operation for ath9k. Tested with b43, ath9k, rt2500usb, and ath5k as peers. Signed-off-by: Pat Erley Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/beacon.c | 4 +++- drivers/net/wireless/ath9k/hw.c | 2 ++ drivers/net/wireless/ath9k/main.c | 24 ++++++++++++------------ drivers/net/wireless/ath9k/rc.c | 1 + 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/ath9k/beacon.c b/drivers/net/wireless/ath9k/beacon.c index e5b007196ca..ec995730632 100644 --- a/drivers/net/wireless/ath9k/beacon.c +++ b/drivers/net/wireless/ath9k/beacon.c @@ -70,7 +70,8 @@ static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp, ds = bf->bf_desc; flags = ATH9K_TXDESC_NOACK; - if (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC && + if (((sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) || + (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT)) && (ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)) { ds->ds_link = bf->bf_daddr; /* self-linked */ flags |= ATH9K_TXDESC_VEOL; @@ -728,6 +729,7 @@ void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif) ath_beacon_config_ap(sc, &conf, avp); break; case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: ath_beacon_config_adhoc(sc, &conf, avp, vif); break; case NL80211_IFTYPE_STATION: diff --git a/drivers/net/wireless/ath9k/hw.c b/drivers/net/wireless/ath9k/hw.c index 15e4d422cad..b15eaf8417f 100644 --- a/drivers/net/wireless/ath9k/hw.c +++ b/drivers/net/wireless/ath9k/hw.c @@ -1448,6 +1448,7 @@ static void ath9k_hw_set_operating_mode(struct ath_hw *ah, int opmode) REG_CLR_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); break; case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_ADHOC | AR_STA_ID1_KSRCH_MODE); REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); @@ -3149,6 +3150,7 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period) flags |= AR_TBTT_TIMER_EN; break; case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: REG_SET_BIT(ah, AR_TXCFG, AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY); REG_WRITE(ah, AR_NEXT_NDP_TIMER, diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index 4c29cef66a6..c13e4e53634 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c @@ -1599,7 +1599,8 @@ void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC); + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_MESH_POINT); hw->wiphy->reg_notifier = ath9k_reg_notifier; hw->wiphy->strict_regulatory = true; @@ -2207,18 +2208,13 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, ic_opmode = NL80211_IFTYPE_STATION; break; case NL80211_IFTYPE_ADHOC: - if (sc->nbcnvifs >= ATH_BCBUF) { - ret = -ENOBUFS; - goto out; - } - ic_opmode = NL80211_IFTYPE_ADHOC; - break; case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MESH_POINT: if (sc->nbcnvifs >= ATH_BCBUF) { ret = -ENOBUFS; goto out; } - ic_opmode = NL80211_IFTYPE_AP; + ic_opmode = conf->type; break; default: DPRINTF(sc, ATH_DBG_FATAL, @@ -2254,7 +2250,8 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, * Note we only do this (at the moment) for station mode. */ if ((conf->type == NL80211_IFTYPE_STATION) || - (conf->type == NL80211_IFTYPE_ADHOC)) { + (conf->type == NL80211_IFTYPE_ADHOC) || + (conf->type == NL80211_IFTYPE_MESH_POINT)) { if (ath9k_hw_phycounters(sc->sc_ah)) sc->imask |= ATH9K_INT_MIB; sc->imask |= ATH9K_INT_TSFOOR; @@ -2301,8 +2298,9 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, del_timer_sync(&sc->ani.timer); /* Reclaim beacon resources */ - if (sc->sc_ah->opmode == NL80211_IFTYPE_AP || - sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) { + if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) || + (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) || + (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT)) { ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq); ath_beacon_return(sc, avp); } @@ -2435,6 +2433,7 @@ static int ath9k_config_interface(struct ieee80211_hw *hw, switch (vif->type) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: /* Set BSSID */ memcpy(sc->curbssid, conf->bssid, ETH_ALEN); memcpy(avp->bssid, conf->bssid, ETH_ALEN); @@ -2458,7 +2457,8 @@ static int ath9k_config_interface(struct ieee80211_hw *hw, } if ((vif->type == NL80211_IFTYPE_ADHOC) || - (vif->type == NL80211_IFTYPE_AP)) { + (vif->type == NL80211_IFTYPE_AP) || + (vif->type == NL80211_IFTYPE_MESH_POINT)) { if ((conf->changed & IEEE80211_IFCC_BEACON) || (conf->changed & IEEE80211_IFCC_BEACON_ENABLED && conf->enable_beacon)) { diff --git a/drivers/net/wireless/ath9k/rc.c b/drivers/net/wireless/ath9k/rc.c index 6c2fd395bc3..824ccbb8b7b 100644 --- a/drivers/net/wireless/ath9k/rc.c +++ b/drivers/net/wireless/ath9k/rc.c @@ -1619,6 +1619,7 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband, /* Choose rate table first */ if ((sc->sc_ah->opmode == NL80211_IFTYPE_STATION) || + (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT) || (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC)) { rate_table = ath_choose_rate_table(sc, sband->band, sta->ht_cap.ht_supported, -- cgit v1.2.3 From 98dfaa577855a551e798e3a99b934386698d2026 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Fri, 20 Mar 2009 23:46:11 -0400 Subject: mac80211_hwsim: let the reg workqueue breathe when regtest is set Without this the regulatory domain isn't seen and we end up intersecting for each request (each radio). Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/mac80211_hwsim.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 2368b7f825a..55116102475 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1041,6 +1041,9 @@ static int __init init_mac80211_hwsim(void) break; } + /* give the regulatory workqueue a chance to run */ + if (regtest) + schedule_timeout_interruptible(1); err = ieee80211_register_hw(hw); if (err < 0) { printk(KERN_DEBUG "mac80211_hwsim: " -- cgit v1.2.3 From 2e097dc65673ed421bbc2e49f52c125aa43a8ee6 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Fri, 20 Mar 2009 23:53:04 -0400 Subject: cfg80211: force last_request to be set for OLD_REG if regdom is EU Although EU is a bogus alpha2 we need to process the send request as our code depends on last_request being set. Cc: stable@kernel.org Reported-by: Quentin Armitage Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/reg.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index eb8b8ed1615..ead9dccb547 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2135,11 +2135,14 @@ int regulatory_init(void) /* * The old code still requests for a new regdomain and if * you have CRDA you get it updated, otherwise you get - * stuck with the static values. We ignore "EU" code as - * that is not a valid ISO / IEC 3166 alpha2 + * stuck with the static values. Since "EU" is not a valid + * ISO / IEC 3166 alpha2 code we can't expect userpace to + * give us a regulatory domain for it. We need last_request + * iniitalized though so lets just send a request which we + * know will be ignored... this crap will be removed once + * OLD_REG dies. */ - if (ieee80211_regdom[0] != 'E' || ieee80211_regdom[1] != 'U') - err = regulatory_hint_core(ieee80211_regdom); + err = regulatory_hint_core(ieee80211_regdom); #else cfg80211_regdomain = cfg80211_world_regdom; -- cgit v1.2.3 From cc0b6fe88e99096868bdbacbf486c97299533b5a Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Fri, 20 Mar 2009 23:53:05 -0400 Subject: cfg80211: fix incorrect assumption on last_request for 11d The incorrect assumption is the last regulatory request (last_request) is always a country IE when processing country IEs. Although this is true 99% of the time the first time this happens this could not be true. This fixes an oops in the branch check for the last_request when accessing drv_last_ie. The access was done under the assumption the struct won't be null. Note to stable: to port to 29 replace as follows, only 29 has country IE code: s|NL80211_REGDOM_SET_BY_COUNTRY_IE|REGDOM_SET_BY_COUNTRY_IE Cc: stable@kernel.org Reported-by: Quentin Armitage Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/reg.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index ead9dccb547..9afc9168748 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1601,6 +1601,10 @@ static bool reg_same_country_ie_hint(struct wiphy *wiphy, assert_cfg80211_lock(); + if (unlikely(last_request->initiator != + NL80211_REGDOM_SET_BY_COUNTRY_IE)) + return false; + request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); if (!request_wiphy) @@ -1663,7 +1667,9 @@ void regulatory_hint_11d(struct wiphy *wiphy, * we optimize an early check to exit out early if we don't have to * do anything */ - if (likely(wiphy_idx_valid(last_request->wiphy_idx))) { + if (likely(last_request->initiator == + NL80211_REGDOM_SET_BY_COUNTRY_IE && + wiphy_idx_valid(last_request->wiphy_idx))) { struct cfg80211_registered_device *drv_last_ie; drv_last_ie = -- cgit v1.2.3 From 6ee7d33056f6e6fc7437d980dcc741816deedd0f Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Fri, 20 Mar 2009 23:53:06 -0400 Subject: cfg80211: make regdom module parameter available oustide of OLD_REG It seems a few users are using this module parameter although its not recommended. People are finding it useful despite there being utilities for setting this in userspace. I'm not aware of any distribution using this though. Until userspace and distributions catch up with a default userspace automatic replacement (GeoClue integration would be nirvana) we copy the ieee80211_regdom module parameter from OLD_REG to the new reg code to help these users migrate. Users who are using the non-valid ISO / IEC 3166 alpha "EU" in their ieee80211_regdom module parameter and migrate to non-OLD_REG enabled system will world roam. This also schedules removal of this same ieee80211_regdom module parameter circa March 2010. Hope is by then nirvana is reached and users will abandoned the module parameter completely. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- Documentation/feature-removal-schedule.txt | 30 ++++++++++++++++++++++++++---- net/wireless/reg.c | 7 ++++++- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index e47c0ff8ba7..8365f52a354 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -6,7 +6,31 @@ be removed from this file. --------------------------- -What: old static regulatory information and ieee80211_regdom module parameter +What: The ieee80211_regdom module parameter +When: March 2010 + +Why: This was inherited by the CONFIG_WIRELESS_OLD_REGULATORY code, + and currently serves as an option for users to define an + ISO / IEC 3166 alpha2 code for the country they are currently + present in. Although there are userspace API replacements for this + through nl80211 distributions haven't yet caught up with implementing + decent alternatives through standard GUIs. Although available as an + option through iw or wpa_supplicant its just a matter of time before + distributions pick up good GUI options for this. The ideal solution + would actually consist of intelligent designs which would do this for + the user automatically even when travelling through different countries. + Until then we leave this module parameter as a compromise. + + When userspace improves with reasonable widely-available alternatives for + this we will no longer need this module parameter. This entry hopes that + by the super-futuristically looking date of "March 2010" we will have + such replacements widely available. + +Who: Luis R. Rodriguez + +--------------------------- + +What: old static regulatory information When: 2.6.29 Why: The old regulatory infrastructure has been replaced with a new one which does not require statically defined regulatory domains. We do @@ -17,9 +41,7 @@ Why: The old regulatory infrastructure has been replaced with a new one * JP * EU and used by default the US when CONFIG_WIRELESS_OLD_REGULATORY was - set. We also kept around the ieee80211_regdom module parameter in case - some applications were relying on it. Changing regulatory domains - can now be done instead by using nl80211, as is done with iw. + set. Who: Luis R. Rodriguez --------------------------- diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 9afc9168748..ac048a158d8 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -122,9 +122,14 @@ static const struct ieee80211_regdomain *cfg80211_world_regdom = #ifdef CONFIG_WIRELESS_OLD_REGULATORY static char *ieee80211_regdom = "US"; +#else +static char *ieee80211_regdom = "00"; +#endif + module_param(ieee80211_regdom, charp, 0444); MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); +#ifdef CONFIG_WIRELESS_OLD_REGULATORY /* * We assume 40 MHz bandwidth for the old regulatory work. * We make emphasis we are using the exact same frequencies @@ -2152,7 +2157,7 @@ int regulatory_init(void) #else cfg80211_regdomain = cfg80211_world_regdom; - err = regulatory_hint_core("00"); + err = regulatory_hint_core(ieee80211_regdom); #endif if (err) { if (err == -ENOMEM) -- cgit v1.2.3 From 86f04680df4a136a4a90501572dc2f31f8426581 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Fri, 20 Mar 2009 23:53:07 -0400 Subject: cfg80211: remove code about country IE support with OLD_REG We had left in code to allow interested developers to add support for parsing country IEs when OLD_REG was enabled. This never happened and since we're going to remove OLD_REG lets just remove these comments and code for it. This code path was never being entered so this has no functional change. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/reg.c | 39 +++++++++++---------------------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index ac048a158d8..6327e1617ac 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1420,16 +1420,6 @@ new_request: return r; } - /* - * Note: When CONFIG_WIRELESS_OLD_REGULATORY is enabled - * AND if CRDA is NOT present nothing will happen, if someone - * wants to bother with 11d with OLD_REG you can add a timer. - * If after x amount of time nothing happens you can call: - * - * return set_regdom(country_ie_regdomain); - * - * to intersect with the static rd - */ return call_crda(last_request->alpha2); } @@ -2033,28 +2023,21 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) */ BUG_ON(!country_ie_regdomain); + BUG_ON(rd == country_ie_regdomain); - if (rd != country_ie_regdomain) { - /* - * Intersect what CRDA returned and our what we - * had built from the Country IE received - */ + /* + * Intersect what CRDA returned and our what we + * had built from the Country IE received + */ - intersected_rd = regdom_intersect(rd, country_ie_regdomain); + intersected_rd = regdom_intersect(rd, country_ie_regdomain); - reg_country_ie_process_debug(rd, country_ie_regdomain, - intersected_rd); + reg_country_ie_process_debug(rd, + country_ie_regdomain, + intersected_rd); - kfree(country_ie_regdomain); - country_ie_regdomain = NULL; - } else { - /* - * This would happen when CRDA was not present and - * OLD_REGULATORY was enabled. We intersect our Country - * IE rd and what was set on cfg80211 originally - */ - intersected_rd = regdom_intersect(rd, cfg80211_regdomain); - } + kfree(country_ie_regdomain); + country_ie_regdomain = NULL; if (!intersected_rd) return -EINVAL; -- cgit v1.2.3 From ac7f9cfa2c3b810e0adfb889ad407a8c79a84dbe Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 21 Mar 2009 17:07:59 +0100 Subject: cfg80211: accept no-op interface mode changes When somebody tries to set the interface mode to the existing mode, don't ask the driver but silently accept the setting. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 28 +++++++++++++++++++++------- net/wireless/wext-compat.c | 11 +++++++++-- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a7d0b94f6b5..8808431bd58 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -607,6 +607,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) enum nl80211_iftype type; struct net_device *dev; u32 _flags, *flags = NULL; + bool change = false; memset(¶ms, 0, sizeof(params)); @@ -620,11 +621,17 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) type = dev->ieee80211_ptr->iftype; dev_put(dev); - err = -EINVAL; if (info->attrs[NL80211_ATTR_IFTYPE]) { - type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); - if (type > NL80211_IFTYPE_MAX) + enum nl80211_iftype ntype; + + ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); + if (type != ntype) + change = true; + type = ntype; + if (type > NL80211_IFTYPE_MAX) { + err = -EINVAL; goto unlock; + } } if (!drv->ops->change_virtual_intf || @@ -640,6 +647,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) } params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]); params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); + change = true; } if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { @@ -649,12 +657,18 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) } err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS], &_flags); - if (!err) - flags = &_flags; + if (err) + goto unlock; + + flags = &_flags; + change = true; } - err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, - type, flags, ¶ms); + if (change) + err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, + type, flags, ¶ms); + else + err = 0; dev = __dev_get_by_index(&init_net, ifindex); WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != type)); diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index b84a9b4fe96..0fd1db6e95b 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -66,6 +66,7 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, struct cfg80211_registered_device *rdev; struct vif_params vifparams; enum nl80211_iftype type; + int ret; if (!wdev) return -EOPNOTSUPP; @@ -96,10 +97,16 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, return -EINVAL; } + if (type == wdev->iftype) + return 0; + memset(&vifparams, 0, sizeof(vifparams)); - return rdev->ops->change_virtual_intf(wdev->wiphy, dev->ifindex, type, - NULL, &vifparams); + ret = rdev->ops->change_virtual_intf(wdev->wiphy, dev->ifindex, type, + NULL, &vifparams); + WARN_ON(!ret && wdev->iftype != type); + + return ret; } EXPORT_SYMBOL(cfg80211_wext_siwmode); -- cgit v1.2.3 From 7986cf9581767d250ca0e5a554541bb276e08d21 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 21 Mar 2009 17:08:43 +0100 Subject: mac80211: remove mixed-cell and userspace MLME code Neither can currently be set from userspace, so there's no regression potential, and neither will be supported from userspace since the new userspace APIs allow the SME, which is in userspace, to control all we need. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 9 ++++----- net/mac80211/iface.c | 3 +-- net/mac80211/mlme.c | 3 +-- net/mac80211/rx.c | 13 ++++--------- net/mac80211/wext.c | 17 +---------------- 5 files changed, 11 insertions(+), 34 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index f69e84ab961..564167fbb9a 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -247,8 +247,9 @@ struct mesh_preq_queue { #define IEEE80211_STA_ASSOCIATED BIT(4) #define IEEE80211_STA_PROBEREQ_POLL BIT(5) #define IEEE80211_STA_CREATE_IBSS BIT(6) -#define IEEE80211_STA_MIXED_CELL BIT(7) +/* hole at 7, please re-use */ #define IEEE80211_STA_WMM_ENABLED BIT(8) +/* hole at 9, please re-use */ #define IEEE80211_STA_AUTO_SSID_SEL BIT(10) #define IEEE80211_STA_AUTO_BSSID_SEL BIT(11) #define IEEE80211_STA_AUTO_CHANNEL_SEL BIT(12) @@ -411,7 +412,6 @@ struct ieee80211_if_mesh { * * @IEEE80211_SDATA_ALLMULTI: interface wants all multicast packets * @IEEE80211_SDATA_PROMISC: interface is promisc - * @IEEE80211_SDATA_USERSPACE_MLME: userspace MLME is active * @IEEE80211_SDATA_OPERATING_GMODE: operating in G-only mode * @IEEE80211_SDATA_DONT_BRIDGE_PACKETS: bridge packets between * associated stations and deliver multicast frames both @@ -420,9 +420,8 @@ struct ieee80211_if_mesh { enum ieee80211_sub_if_data_flags { IEEE80211_SDATA_ALLMULTI = BIT(0), IEEE80211_SDATA_PROMISC = BIT(1), - IEEE80211_SDATA_USERSPACE_MLME = BIT(2), - IEEE80211_SDATA_OPERATING_GMODE = BIT(3), - IEEE80211_SDATA_DONT_BRIDGE_PACKETS = BIT(4), + IEEE80211_SDATA_OPERATING_GMODE = BIT(2), + IEEE80211_SDATA_DONT_BRIDGE_PACKETS = BIT(3), }; struct ieee80211_sub_if_data { diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 34f4798a98f..dd2a276fa8c 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -261,8 +261,7 @@ static int ieee80211_open(struct net_device *dev) ieee80211_bss_info_change_notify(sdata, changed); ieee80211_enable_keys(sdata); - if (sdata->vif.type == NL80211_IFTYPE_STATION && - !(sdata->flags & IEEE80211_SDATA_USERSPACE_MLME)) + if (sdata->vif.type == NL80211_IFTYPE_STATION) netif_carrier_off(dev); else netif_carrier_on(dev); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index b0808efcedf..c05be09b9c6 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -849,8 +849,7 @@ static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata) int wep_privacy; int privacy_invoked; - if (!ifmgd || (ifmgd->flags & (IEEE80211_STA_MIXED_CELL | - IEEE80211_STA_EXT_SME))) + if (!ifmgd || (ifmgd->flags & IEEE80211_STA_EXT_SME)) return 0; bss = ieee80211_rx_bss_get(local, ifmgd->bssid, diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index fcc0a599579..47d395a5192 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1877,18 +1877,13 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) if (ieee80211_vif_is_mesh(&sdata->vif)) return ieee80211_mesh_rx_mgmt(sdata, rx->skb, rx->status); - if (sdata->vif.type != NL80211_IFTYPE_STATION && - sdata->vif.type != NL80211_IFTYPE_ADHOC) - return RX_DROP_MONITOR; - + if (sdata->vif.type != NL80211_IFTYPE_ADHOC) + return ieee80211_ibss_rx_mgmt(sdata, rx->skb, rx->status); - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) - return RX_DROP_MONITOR; + if (sdata->vif.type == NL80211_IFTYPE_STATION) return ieee80211_sta_rx_mgmt(sdata, rx->skb, rx->status); - } - return ieee80211_ibss_rx_mgmt(sdata, rx->skb, rx->status); + return RX_DROP_MONITOR; } static void ieee80211_rx_michael_mic_report(struct net_device *dev, diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index ce21d66b102..deb4ecec122 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -129,9 +129,6 @@ static int ieee80211_ioctl_siwgenie(struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) - return -EOPNOTSUPP; - if (sdata->vif.type == NL80211_IFTYPE_STATION) { int ret = ieee80211_sta_set_extra_ie(sdata, extra, data->length); if (ret) @@ -208,14 +205,6 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->vif.type == NL80211_IFTYPE_STATION) { - if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) { - if (len > IEEE80211_MAX_SSID_LEN) - return -EINVAL; - memcpy(sdata->u.mgd.ssid, ssid, len); - sdata->u.mgd.ssid_len = len; - return 0; - } - if (data->flags) sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL; else @@ -274,11 +263,7 @@ static int ieee80211_ioctl_siwap(struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->vif.type == NL80211_IFTYPE_STATION) { int ret; - if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) { - memcpy(sdata->u.mgd.bssid, (u8 *) &ap_addr->sa_data, - ETH_ALEN); - return 0; - } + if (is_zero_ether_addr((u8 *) &ap_addr->sa_data)) sdata->u.mgd.flags |= IEEE80211_STA_AUTO_BSSID_SEL | IEEE80211_STA_AUTO_CHANNEL_SEL; -- cgit v1.2.3 From 23b53f4f55d833ecc5a11b5fba646c78d3876927 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 21 Mar 2009 23:04:48 +0100 Subject: ar9170: hardware and eeprom header files hardware / firmware interface definitions for Atheros' AR9170 based devices. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ar9170/eeprom.h | 179 +++++++++++++++ drivers/net/wireless/ar9170/hw.h | 406 +++++++++++++++++++++++++++++++++++ 2 files changed, 585 insertions(+) create mode 100644 drivers/net/wireless/ar9170/eeprom.h create mode 100644 drivers/net/wireless/ar9170/hw.h diff --git a/drivers/net/wireless/ar9170/eeprom.h b/drivers/net/wireless/ar9170/eeprom.h new file mode 100644 index 00000000000..d2c8cc83f1d --- /dev/null +++ b/drivers/net/wireless/ar9170/eeprom.h @@ -0,0 +1,179 @@ +/* + * Atheros AR9170 driver + * + * EEPROM layout + * + * Copyright 2008, Johannes Berg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 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 __AR9170_EEPROM_H +#define __AR9170_EEPROM_H + +#define AR5416_MAX_CHAINS 2 +#define AR5416_MODAL_SPURS 5 + +struct ar9170_eeprom_modal { + __le32 antCtrlChain[AR5416_MAX_CHAINS]; + __le32 antCtrlCommon; + s8 antennaGainCh[AR5416_MAX_CHAINS]; + u8 switchSettling; + u8 txRxAttenCh[AR5416_MAX_CHAINS]; + u8 rxTxMarginCh[AR5416_MAX_CHAINS]; + s8 adcDesiredSize; + s8 pgaDesiredSize; + u8 xlnaGainCh[AR5416_MAX_CHAINS]; + u8 txEndToXpaOff; + u8 txEndToRxOn; + u8 txFrameToXpaOn; + u8 thresh62; + s8 noiseFloorThreshCh[AR5416_MAX_CHAINS]; + u8 xpdGain; + u8 xpd; + s8 iqCalICh[AR5416_MAX_CHAINS]; + s8 iqCalQCh[AR5416_MAX_CHAINS]; + u8 pdGainOverlap; + u8 ob; + u8 db; + u8 xpaBiasLvl; + u8 pwrDecreaseFor2Chain; + u8 pwrDecreaseFor3Chain; + u8 txFrameToDataStart; + u8 txFrameToPaOn; + u8 ht40PowerIncForPdadc; + u8 bswAtten[AR5416_MAX_CHAINS]; + u8 bswMargin[AR5416_MAX_CHAINS]; + u8 swSettleHt40; + u8 reserved[22]; + struct spur_channel { + __le16 spurChan; + u8 spurRangeLow; + u8 spurRangeHigh; + } __packed spur_channels[AR5416_MODAL_SPURS]; +} __packed; + +#define AR5416_NUM_PD_GAINS 4 +#define AR5416_PD_GAIN_ICEPTS 5 + +struct ar9170_calibration_data_per_freq { + u8 pwr_pdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS]; + u8 vpd_pdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS]; +} __packed; + +#define AR5416_NUM_5G_CAL_PIERS 8 +#define AR5416_NUM_2G_CAL_PIERS 4 + +#define AR5416_NUM_5G_TARGET_PWRS 8 +#define AR5416_NUM_2G_CCK_TARGET_PWRS 3 +#define AR5416_NUM_2G_OFDM_TARGET_PWRS 4 +#define AR5416_MAX_NUM_TGT_PWRS 8 + +struct ar9170_calibration_target_power_legacy { + u8 freq; + u8 power[4]; +} __packed; + +struct ar9170_calibration_target_power_ht { + u8 freq; + u8 power[8]; +} __packed; + +#define AR5416_NUM_CTLS 24 + +struct ar9170_calctl_edges { + u8 channel; +#define AR9170_CALCTL_EDGE_FLAGS 0xC0 + u8 power_flags; +} __packed; + +#define AR5416_NUM_BAND_EDGES 8 + +struct ar9170_calctl_data { + struct ar9170_calctl_edges + control_edges[AR5416_MAX_CHAINS][AR5416_NUM_BAND_EDGES]; +} __packed; + + +struct ar9170_eeprom { + __le16 length; + __le16 checksum; + __le16 version; + u8 operating_flags; +#define AR9170_OPFLAG_5GHZ 1 +#define AR9170_OPFLAG_2GHZ 2 + u8 misc; + __le16 reg_domain[2]; + u8 mac_address[6]; + u8 rx_mask; + u8 tx_mask; + __le16 rf_silent; + __le16 bluetooth_options; + __le16 device_capabilities; + __le32 build_number; + u8 deviceType; + u8 reserved[33]; + + u8 customer_data[64]; + + struct ar9170_eeprom_modal + modal_header[2]; + + u8 cal_freq_pier_5G[AR5416_NUM_5G_CAL_PIERS]; + u8 cal_freq_pier_2G[AR5416_NUM_2G_CAL_PIERS]; + + struct ar9170_calibration_data_per_freq + cal_pier_data_5G[AR5416_MAX_CHAINS][AR5416_NUM_5G_CAL_PIERS], + cal_pier_data_2G[AR5416_MAX_CHAINS][AR5416_NUM_2G_CAL_PIERS]; + + /* power calibration data */ + struct ar9170_calibration_target_power_legacy + cal_tgt_pwr_5G[AR5416_NUM_5G_TARGET_PWRS]; + struct ar9170_calibration_target_power_ht + cal_tgt_pwr_5G_ht20[AR5416_NUM_5G_TARGET_PWRS], + cal_tgt_pwr_5G_ht40[AR5416_NUM_5G_TARGET_PWRS]; + + struct ar9170_calibration_target_power_legacy + cal_tgt_pwr_2G_cck[AR5416_NUM_2G_CCK_TARGET_PWRS], + cal_tgt_pwr_2G_ofdm[AR5416_NUM_2G_OFDM_TARGET_PWRS]; + struct ar9170_calibration_target_power_ht + cal_tgt_pwr_2G_ht20[AR5416_NUM_2G_OFDM_TARGET_PWRS], + cal_tgt_pwr_2G_ht40[AR5416_NUM_2G_OFDM_TARGET_PWRS]; + + /* conformance testing limits */ + u8 ctl_index[AR5416_NUM_CTLS]; + struct ar9170_calctl_data + ctl_data[AR5416_NUM_CTLS]; + + u8 pad; + __le16 subsystem_id; +} __packed; + +#endif /* __AR9170_EEPROM_H */ diff --git a/drivers/net/wireless/ar9170/hw.h b/drivers/net/wireless/ar9170/hw.h new file mode 100644 index 00000000000..ad205f82f60 --- /dev/null +++ b/drivers/net/wireless/ar9170/hw.h @@ -0,0 +1,406 @@ +/* + * Atheros AR9170 driver + * + * Hardware-specific definitions + * + * Copyright 2008, Johannes Berg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 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 __AR9170_HW_H +#define __AR9170_HW_H + +#define AR9170_MAX_CMD_LEN 64 + +enum ar9170_cmd { + AR9170_CMD_RREG = 0x00, + AR9170_CMD_WREG = 0x01, + AR9170_CMD_RMEM = 0x02, + AR9170_CMD_WMEM = 0x03, + AR9170_CMD_BITAND = 0x04, + AR9170_CMD_BITOR = 0x05, + AR9170_CMD_EKEY = 0x28, + AR9170_CMD_DKEY = 0x29, + AR9170_CMD_FREQUENCY = 0x30, + AR9170_CMD_RF_INIT = 0x31, + AR9170_CMD_SYNTH = 0x32, + AR9170_CMD_FREQ_START = 0x33, + AR9170_CMD_ECHO = 0x80, + AR9170_CMD_TALLY = 0x81, + AR9170_CMD_TALLY_APD = 0x82, + AR9170_CMD_CONFIG = 0x83, + AR9170_CMD_RESET = 0x90, + AR9170_CMD_DKRESET = 0x91, + AR9170_CMD_DKTX_STATUS = 0x92, + AR9170_CMD_FDC = 0xA0, + AR9170_CMD_WREEPROM = 0xB0, + AR9170_CMD_WFLASH = 0xB0, + AR9170_CMD_FLASH_ERASE = 0xB1, + AR9170_CMD_FLASH_PROG = 0xB2, + AR9170_CMD_FLASH_CHKSUM = 0xB3, + AR9170_CMD_FLASH_READ = 0xB4, + AR9170_CMD_FW_DL_INIT = 0xB5, + AR9170_CMD_MEM_WREEPROM = 0xBB, +}; + +/* endpoints */ +#define AR9170_EP_TX 1 +#define AR9170_EP_RX 2 +#define AR9170_EP_IRQ 3 +#define AR9170_EP_CMD 4 + +#define AR9170_EEPROM_START 0x1600 + +#define AR9170_GPIO_REG_BASE 0x1d0100 +#define AR9170_GPIO_REG_PORT_TYPE AR9170_GPIO_REG_BASE +#define AR9170_GPIO_REG_DATA (AR9170_GPIO_REG_BASE + 4) +#define AR9170_NUM_LEDS 2 + + +#define AR9170_USB_REG_BASE 0x1e1000 +#define AR9170_USB_REG_DMA_CTL (AR9170_USB_REG_BASE + 0x108) +#define AR9170_DMA_CTL_ENABLE_TO_DEVICE 0x1 +#define AR9170_DMA_CTL_ENABLE_FROM_DEVICE 0x2 +#define AR9170_DMA_CTL_HIGH_SPEED 0x4 +#define AR9170_DMA_CTL_PACKET_MODE 0x8 + +#define AR9170_USB_REG_MAX_AGG_UPLOAD (AR9170_USB_REG_BASE + 0x110) +#define AR9170_USB_REG_UPLOAD_TIME_CTL (AR9170_USB_REG_BASE + 0x114) + + + +#define AR9170_MAC_REG_BASE 0x1c3000 + +#define AR9170_MAC_REG_TSF_L (AR9170_MAC_REG_BASE + 0x514) +#define AR9170_MAC_REG_TSF_H (AR9170_MAC_REG_BASE + 0x518) + +#define AR9170_MAC_REG_ATIM_WINDOW (AR9170_MAC_REG_BASE + 0x51C) +#define AR9170_MAC_REG_BCN_PERIOD (AR9170_MAC_REG_BASE + 0x520) +#define AR9170_MAC_REG_PRETBTT (AR9170_MAC_REG_BASE + 0x524) + +#define AR9170_MAC_REG_MAC_ADDR_L (AR9170_MAC_REG_BASE + 0x610) +#define AR9170_MAC_REG_MAC_ADDR_H (AR9170_MAC_REG_BASE + 0x614) +#define AR9170_MAC_REG_BSSID_L (AR9170_MAC_REG_BASE + 0x618) +#define AR9170_MAC_REG_BSSID_H (AR9170_MAC_REG_BASE + 0x61c) + +#define AR9170_MAC_REG_GROUP_HASH_TBL_L (AR9170_MAC_REG_BASE + 0x624) +#define AR9170_MAC_REG_GROUP_HASH_TBL_H (AR9170_MAC_REG_BASE + 0x628) + +#define AR9170_MAC_REG_RX_TIMEOUT (AR9170_MAC_REG_BASE + 0x62C) + +#define AR9170_MAC_REG_BASIC_RATE (AR9170_MAC_REG_BASE + 0x630) +#define AR9170_MAC_REG_MANDATORY_RATE (AR9170_MAC_REG_BASE + 0x634) +#define AR9170_MAC_REG_RTS_CTS_RATE (AR9170_MAC_REG_BASE + 0x638) +#define AR9170_MAC_REG_BACKOFF_PROTECT (AR9170_MAC_REG_BASE + 0x63c) +#define AR9170_MAC_REG_RX_THRESHOLD (AR9170_MAC_REG_BASE + 0x640) +#define AR9170_MAC_REG_RX_PE_DELAY (AR9170_MAC_REG_BASE + 0x64C) + +#define AR9170_MAC_REG_DYNAMIC_SIFS_ACK (AR9170_MAC_REG_BASE + 0x658) +#define AR9170_MAC_REG_SNIFFER (AR9170_MAC_REG_BASE + 0x674) +#define AR9170_MAC_REG_SNIFFER_ENABLE_PROMISC BIT(0) +#define AR9170_MAC_REG_SNIFFER_DEFAULTS 0x02000000 +#define AR9170_MAC_REG_ENCRYPTION (AR9170_MAC_REG_BASE + 0x678) +#define AR9170_MAC_REG_ENCRYPTION_RX_SOFTWARE BIT(3) +#define AR9170_MAC_REG_ENCRYPTION_DEFAULTS 0x70 + +#define AR9170_MAC_REG_MISC_680 (AR9170_MAC_REG_BASE + 0x680) +#define AR9170_MAC_REG_TX_UNDERRUN (AR9170_MAC_REG_BASE + 0x688) + +#define AR9170_MAC_REG_FRAMETYPE_FILTER (AR9170_MAC_REG_BASE + 0x68c) +#define AR9170_MAC_REG_FTF_ASSOC_REQ BIT(0) +#define AR9170_MAC_REG_FTF_ASSOC_RESP BIT(1) +#define AR9170_MAC_REG_FTF_REASSOC_REQ BIT(2) +#define AR9170_MAC_REG_FTF_REASSOC_RESP BIT(3) +#define AR9170_MAC_REG_FTF_PRB_REQ BIT(4) +#define AR9170_MAC_REG_FTF_PRB_RESP BIT(5) +#define AR9170_MAC_REG_FTF_BIT6 BIT(6) +#define AR9170_MAC_REG_FTF_BIT7 BIT(7) +#define AR9170_MAC_REG_FTF_BEACON BIT(8) +#define AR9170_MAC_REG_FTF_ATIM BIT(9) +#define AR9170_MAC_REG_FTF_DEASSOC BIT(10) +#define AR9170_MAC_REG_FTF_AUTH BIT(11) +#define AR9170_MAC_REG_FTF_DEAUTH BIT(12) +#define AR9170_MAC_REG_FTF_BIT13 BIT(13) +#define AR9170_MAC_REG_FTF_BIT14 BIT(14) +#define AR9170_MAC_REG_FTF_BIT15 BIT(15) +#define AR9170_MAC_REG_FTF_BAR BIT(24) +#define AR9170_MAC_REG_FTF_BIT25 BIT(25) +#define AR9170_MAC_REG_FTF_PSPOLL BIT(26) +#define AR9170_MAC_REG_FTF_RTS BIT(27) +#define AR9170_MAC_REG_FTF_CTS BIT(28) +#define AR9170_MAC_REG_FTF_ACK BIT(29) +#define AR9170_MAC_REG_FTF_CFE BIT(30) +#define AR9170_MAC_REG_FTF_CFE_ACK BIT(31) +#define AR9170_MAC_REG_FTF_DEFAULTS 0x0500ffff +#define AR9170_MAC_REG_FTF_MONITOR 0xfd00ffff + +#define AR9170_MAC_REG_RX_TOTAL (AR9170_MAC_REG_BASE + 0x6A0) +#define AR9170_MAC_REG_RX_CRC32 (AR9170_MAC_REG_BASE + 0x6A4) +#define AR9170_MAC_REG_RX_CRC16 (AR9170_MAC_REG_BASE + 0x6A8) +#define AR9170_MAC_REG_RX_ERR_DECRYPTION_UNI (AR9170_MAC_REG_BASE + 0x6AC) +#define AR9170_MAC_REG_RX_OVERRUN (AR9170_MAC_REG_BASE + 0x6B0) +#define AR9170_MAC_REG_RX_ERR_DECRYPTION_MUL (AR9170_MAC_REG_BASE + 0x6BC) +#define AR9170_MAC_REG_TX_RETRY (AR9170_MAC_REG_BASE + 0x6CC) +#define AR9170_MAC_REG_TX_TOTAL (AR9170_MAC_REG_BASE + 0x6F4) + + +#define AR9170_MAC_REG_ACK_EXTENSION (AR9170_MAC_REG_BASE + 0x690) +#define AR9170_MAC_REG_EIFS_AND_SIFS (AR9170_MAC_REG_BASE + 0x698) + +#define AR9170_MAC_REG_SLOT_TIME (AR9170_MAC_REG_BASE + 0x6F0) + +#define AR9170_MAC_REG_POWERMANAGEMENT (AR9170_MAC_REG_BASE + 0x700) +#define AR9170_MAC_REG_POWERMGT_IBSS 0xe0 +#define AR9170_MAC_REG_POWERMGT_AP 0xa1 +#define AR9170_MAC_REG_POWERMGT_STA 0x2 +#define AR9170_MAC_REG_POWERMGT_AP_WDS 0x3 +#define AR9170_MAC_REG_POWERMGT_DEFAULTS (0xf << 24) + +#define AR9170_MAC_REG_ROLL_CALL_TBL_L (AR9170_MAC_REG_BASE + 0x704) +#define AR9170_MAC_REG_ROLL_CALL_TBL_H (AR9170_MAC_REG_BASE + 0x708) + +#define AR9170_MAC_REG_AC0_CW (AR9170_MAC_REG_BASE + 0xB00) +#define AR9170_MAC_REG_AC1_CW (AR9170_MAC_REG_BASE + 0xB04) +#define AR9170_MAC_REG_AC2_CW (AR9170_MAC_REG_BASE + 0xB08) +#define AR9170_MAC_REG_AC3_CW (AR9170_MAC_REG_BASE + 0xB0C) +#define AR9170_MAC_REG_AC4_CW (AR9170_MAC_REG_BASE + 0xB10) +#define AR9170_MAC_REG_AC1_AC0_AIFS (AR9170_MAC_REG_BASE + 0xB14) +#define AR9170_MAC_REG_AC3_AC2_AIFS (AR9170_MAC_REG_BASE + 0xB18) + +#define AR9170_MAC_REG_RETRY_MAX (AR9170_MAC_REG_BASE + 0xB28) + +#define AR9170_MAC_REG_FCS_SELECT (AR9170_MAC_REG_BASE + 0xBB0) +#define AR9170_MAC_FCS_SWFCS 0x1 +#define AR9170_MAC_FCS_FIFO_PROT 0x4 + + +#define AR9170_MAC_REG_TXOP_NOT_ENOUGH_IND (AR9170_MAC_REG_BASE + 0xB30) + +#define AR9170_MAC_REG_AC1_AC0_TXOP (AR9170_MAC_REG_BASE + 0xB44) +#define AR9170_MAC_REG_AC3_AC2_TXOP (AR9170_MAC_REG_BASE + 0xB48) + +#define AR9170_MAC_REG_ACK_TABLE (AR9170_MAC_REG_BASE + 0xC00) +#define AR9170_MAC_REG_AMPDU_RX_THRESH (AR9170_MAC_REG_BASE + 0xC50) + +#define AR9170_MAC_REG_TXRX_MPI (AR9170_MAC_REG_BASE + 0xD7C) +#define AR9170_MAC_TXRX_MPI_TX_MPI_MASK 0x0000000f +#define AR9170_MAC_TXRX_MPI_TX_TO_MASK 0x0000fff0 +#define AR9170_MAC_TXRX_MPI_RX_MPI_MASK 0x000f0000 +#define AR9170_MAC_TXRX_MPI_RX_TO_MASK 0xfff00000 + +#define AR9170_MAC_REG_BCN_ADDR (AR9170_MAC_REG_BASE + 0xD84) +#define AR9170_MAC_REG_BCN_LENGTH (AR9170_MAC_REG_BASE + 0xD88) +#define AR9170_MAC_REG_BCN_PLCP (AR9170_MAC_REG_BASE + 0xD90) +#define AR9170_MAC_REG_BCN_CTRL (AR9170_MAC_REG_BASE + 0xD94) +#define AR9170_MAC_REG_BCN_HT1 (AR9170_MAC_REG_BASE + 0xDA0) +#define AR9170_MAC_REG_BCN_HT2 (AR9170_MAC_REG_BASE + 0xDA4) + + +#define AR9170_PWR_REG_BASE 0x1D4000 + +#define AR9170_PWR_REG_CLOCK_SEL (AR9170_PWR_REG_BASE + 0x008) +#define AR9170_PWR_CLK_AHB_40MHZ 0 +#define AR9170_PWR_CLK_AHB_20_22MHZ 1 +#define AR9170_PWR_CLK_AHB_40_44MHZ 2 +#define AR9170_PWR_CLK_AHB_80_88MHZ 3 +#define AR9170_PWR_CLK_DAC_160_INV_DLY 0x70 + + +/* put beacon here in memory */ +#define AR9170_BEACON_BUFFER_ADDRESS 0x117900 + + +struct ar9170_tx_control { + __le16 length; + __le16 mac_control; + __le32 phy_control; + u8 frame_data[0]; +} __packed; + +/* these are either-or */ +#define AR9170_TX_MAC_PROT_RTS 0x0001 +#define AR9170_TX_MAC_PROT_CTS 0x0002 + +#define AR9170_TX_MAC_NO_ACK 0x0004 +/* if unset, MAC will only do SIFS space before frame */ +#define AR9170_TX_MAC_BACKOFF 0x0008 +#define AR9170_TX_MAC_BURST 0x0010 +#define AR9170_TX_MAC_AGGR 0x0020 + +/* encryption is a two-bit field */ +#define AR9170_TX_MAC_ENCR_NONE 0x0000 +#define AR9170_TX_MAC_ENCR_RC4 0x0040 +#define AR9170_TX_MAC_ENCR_CENC 0x0080 +#define AR9170_TX_MAC_ENCR_AES 0x00c0 + +#define AR9170_TX_MAC_MMIC 0x0100 +#define AR9170_TX_MAC_HW_DURATION 0x0200 +#define AR9170_TX_MAC_QOS_SHIFT 10 +#define AR9170_TX_MAC_QOS_MASK (3 << AR9170_TX_MAC_QOS_SHIFT) +#define AR9170_TX_MAC_AGGR_QOS_BIT1 0x0400 +#define AR9170_TX_MAC_AGGR_QOS_BIT2 0x0800 +#define AR9170_TX_MAC_DISABLE_TXOP 0x1000 +#define AR9170_TX_MAC_TXOP_RIFS 0x2000 +#define AR9170_TX_MAC_IMM_AMPDU 0x4000 +#define AR9170_TX_MAC_RATE_PROBE 0x8000 + +/* either-or */ +#define AR9170_TX_PHY_MOD_CCK 0x00000000 +#define AR9170_TX_PHY_MOD_OFDM 0x00000001 +#define AR9170_TX_PHY_MOD_HT 0x00000002 + +/* depends on modulation */ +#define AR9170_TX_PHY_SHORT_PREAMBLE 0x00000004 +#define AR9170_TX_PHY_GREENFIELD 0x00000004 + +#define AR9170_TX_PHY_BW_SHIFT 3 +#define AR9170_TX_PHY_BW_MASK (3 << AR9170_TX_PHY_BW_SHIFT) +#define AR9170_TX_PHY_BW_20MHZ 0 +#define AR9170_TX_PHY_BW_40MHZ 2 +#define AR9170_TX_PHY_BW_40MHZ_DUP 3 + +#define AR9170_TX_PHY_TX_HEAVY_CLIP_SHIFT 6 +#define AR9170_TX_PHY_TX_HEAVY_CLIP_MASK (7 << AR9170_TX_PHY_TX_HEAVY_CLIP_SHIFT) + +#define AR9170_TX_PHY_TX_PWR_SHIFT 9 +#define AR9170_TX_PHY_TX_PWR_MASK (0x3f << AR9170_TX_PHY_TX_PWR_SHIFT) + +/* not part of the hw-spec */ +#define AR9170_TX_PHY_QOS_SHIFT 25 +#define AR9170_TX_PHY_QOS_MASK (3 << AR9170_TX_PHY_QOS_SHIFT) + +#define AR9170_TX_PHY_TXCHAIN_SHIFT 15 +#define AR9170_TX_PHY_TXCHAIN_MASK (7 << AR9170_TX_PHY_TXCHAIN_SHIFT) +#define AR9170_TX_PHY_TXCHAIN_1 1 +/* use for cck, ofdm 6/9/12/18/24 and HT if capable */ +#define AR9170_TX_PHY_TXCHAIN_2 5 + +#define AR9170_TX_PHY_MCS_SHIFT 18 +#define AR9170_TX_PHY_MCS_MASK (0x7f << AR9170_TX_PHY_MCS_SHIFT) + +#define AR9170_TX_PHY_SHORT_GI 0x80000000 + +struct ar9170_rx_head { + u8 plcp[12]; +}; + +struct ar9170_rx_tail { + union { + struct { + u8 rssi_ant0, rssi_ant1, rssi_ant2, + rssi_ant0x, rssi_ant1x, rssi_ant2x, + rssi_combined; + }; + u8 rssi[7]; + }; + + u8 evm_stream0[6], evm_stream1[6]; + u8 phy_err; + u8 SAidx, DAidx; + u8 error; + u8 status; +}; + +#define AR9170_ENC_ALG_NONE 0x0 +#define AR9170_ENC_ALG_WEP64 0x1 +#define AR9170_ENC_ALG_TKIP 0x2 +#define AR9170_ENC_ALG_AESCCMP 0x4 +#define AR9170_ENC_ALG_WEP128 0x5 +#define AR9170_ENC_ALG_WEP256 0x6 +#define AR9170_ENC_ALG_CENC 0x7 + +#define AR9170_RX_ENC_SOFTWARE 0x8 + +static inline u8 ar9170_get_decrypt_type(struct ar9170_rx_tail *t) +{ + return (t->SAidx & 0xc0) >> 4 | + (t->DAidx & 0xc0) >> 6; +} + +#define AR9170_RX_STATUS_MODULATION_MASK 0x03 +#define AR9170_RX_STATUS_MODULATION_CCK 0x00 +#define AR9170_RX_STATUS_MODULATION_OFDM 0x01 +#define AR9170_RX_STATUS_MODULATION_HT 0x02 +#define AR9170_RX_STATUS_MODULATION_DUPOFDM 0x03 + +/* depends on modulation */ +#define AR9170_RX_STATUS_SHORT_PREAMBLE 0x08 +#define AR9170_RX_STATUS_GREENFIELD 0x08 + +#define AR9170_RX_STATUS_MPDU_MASK 0x30 +#define AR9170_RX_STATUS_MPDU_SINGLE 0x00 +#define AR9170_RX_STATUS_MPDU_FIRST 0x10 +#define AR9170_RX_STATUS_MPDU_MIDDLE 0x20 +#define AR9170_RX_STATUS_MPDU_LAST 0x30 + + +#define AR9170_RX_ERROR_RXTO 0x01 +#define AR9170_RX_ERROR_OVERRUN 0x02 +#define AR9170_RX_ERROR_DECRYPT 0x04 +#define AR9170_RX_ERROR_FCS 0x08 +#define AR9170_RX_ERROR_WRONG_RA 0x10 +#define AR9170_RX_ERROR_PLCP 0x20 +#define AR9170_RX_ERROR_MMIC 0x40 + +struct ar9170_cmd_tx_status { + __le16 unkn; + u8 dst[ETH_ALEN]; + __le32 rate; + __le16 status; +} __packed; + +#define AR9170_TX_STATUS_COMPLETE 0x00 +#define AR9170_TX_STATUS_RETRY 0x01 +#define AR9170_TX_STATUS_FAILED 0x02 + +struct ar9170_cmd_ba_failed_count { + __le16 failed; + __le16 rate; +} __packed; + +struct ar9170_cmd_response { + u8 flag; + u8 type; + + union { + struct ar9170_cmd_tx_status tx_status; + struct ar9170_cmd_ba_failed_count ba_fail_cnt; + u8 data[0]; + }; +} __packed; + +/* mac80211 queue to HW/FW map */ +static const u8 ar9170_qos_hwmap[4] = { 3, 2, 0, 1 }; + +/* HW/FW queue to mac80211 map */ +static const u8 ar9170_qos_mac80211map[4] = { 2, 3, 1, 0 }; + +#endif /* __AR9170_HW_H */ -- cgit v1.2.3 From e9348cdd280eb6a1d6d38fef513b578dc9ead363 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 21 Mar 2009 23:05:13 +0100 Subject: ar9170: ar9170: mac80211 interaction code This patch contains almost all mac80211 interaction code of AR9170. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ar9170/ar9170.h | 209 ++++ drivers/net/wireless/ar9170/cmd.c | 130 +++ drivers/net/wireless/ar9170/cmd.h | 91 ++ drivers/net/wireless/ar9170/led.c | 171 ++++ drivers/net/wireless/ar9170/main.c | 1776 ++++++++++++++++++++++++++++++++++ 5 files changed, 2377 insertions(+) create mode 100644 drivers/net/wireless/ar9170/ar9170.h create mode 100644 drivers/net/wireless/ar9170/cmd.c create mode 100644 drivers/net/wireless/ar9170/cmd.h create mode 100644 drivers/net/wireless/ar9170/led.c create mode 100644 drivers/net/wireless/ar9170/main.c diff --git a/drivers/net/wireless/ar9170/ar9170.h b/drivers/net/wireless/ar9170/ar9170.h new file mode 100644 index 00000000000..c49d7138d57 --- /dev/null +++ b/drivers/net/wireless/ar9170/ar9170.h @@ -0,0 +1,209 @@ +/* + * Atheros AR9170 driver + * + * Driver specific definitions + * + * Copyright 2008, Johannes Berg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 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 __AR9170_H +#define __AR9170_H + +#include +#include +#include +#include +#ifdef CONFIG_AR9170_LEDS +#include +#endif /* CONFIG_AR9170_LEDS */ +#include "eeprom.h" +#include "hw.h" + +#define PAYLOAD_MAX (AR9170_MAX_CMD_LEN/4 - 1) + +enum ar9170_bw { + AR9170_BW_20, + AR9170_BW_40_BELOW, + AR9170_BW_40_ABOVE, + + __AR9170_NUM_BW, +}; + +enum ar9170_rf_init_mode { + AR9170_RFI_NONE, + AR9170_RFI_WARM, + AR9170_RFI_COLD, +}; + +#define AR9170_MAX_RX_BUFFER_SIZE 8192 + +#ifdef CONFIG_AR9170_LEDS +struct ar9170; + +struct ar9170_led { + struct ar9170 *ar; + struct led_classdev l; + char name[32]; + unsigned int toggled; + bool registered; +}; + +#endif /* CONFIG_AR9170_LEDS */ + +enum ar9170_device_state { + AR9170_UNKNOWN_STATE, + AR9170_STOPPED, + AR9170_IDLE, + AR9170_STARTED, + AR9170_ASSOCIATED, +}; + +struct ar9170 { + struct ieee80211_hw *hw; + struct mutex mutex; + enum ar9170_device_state state; + + int (*open)(struct ar9170 *); + void (*stop)(struct ar9170 *); + int (*tx)(struct ar9170 *, struct sk_buff *, bool, unsigned int); + int (*exec_cmd)(struct ar9170 *, enum ar9170_cmd, u32 , + void *, u32 , void *); + void (*callback_cmd)(struct ar9170 *, u32 , void *); + + /* interface mode settings */ + struct ieee80211_vif *vif; + u8 mac_addr[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + + /* beaconing */ + struct sk_buff *beacon; + struct work_struct beacon_work; + + /* cryptographic engine */ + u64 usedkeys; + bool rx_software_decryption; + bool disable_offload; + + /* filter settings */ + struct work_struct filter_config_work; + u64 cur_mc_hash, want_mc_hash; + u32 cur_filter, want_filter; + unsigned int filter_changed; + bool sniffer_enabled; + + /* PHY */ + struct ieee80211_channel *channel; + int noise[4]; + + /* power calibration data */ + u8 power_5G_leg[4]; + u8 power_2G_cck[4]; + u8 power_2G_ofdm[4]; + u8 power_5G_ht20[8]; + u8 power_5G_ht40[8]; + u8 power_2G_ht20[8]; + u8 power_2G_ht40[8]; + +#ifdef CONFIG_AR9170_LEDS + struct delayed_work led_work; + struct ar9170_led leds[AR9170_NUM_LEDS]; +#endif /* CONFIG_AR9170_LEDS */ + + /* qos queue settings */ + spinlock_t tx_stats_lock; + struct ieee80211_tx_queue_stats tx_stats[5]; + struct ieee80211_tx_queue_params edcf[5]; + + spinlock_t cmdlock; + __le32 cmdbuf[PAYLOAD_MAX + 1]; + + /* MAC statistics */ + struct ieee80211_low_level_stats stats; + + /* EEPROM */ + struct ar9170_eeprom eeprom; + + /* global tx status for unregistered Stations. */ + struct sk_buff_head global_tx_status; + struct sk_buff_head global_tx_status_waste; + struct delayed_work tx_status_janitor; +}; + +struct ar9170_sta_info { + struct sk_buff_head tx_status; +}; + +#define IS_STARTED(a) (a->state >= AR9170_STARTED) +#define IS_ACCEPTING_CMD(a) (a->state >= AR9170_IDLE) + +#define AR9170_FILTER_CHANGED_PROMISC BIT(0) +#define AR9170_FILTER_CHANGED_MULTICAST BIT(1) +#define AR9170_FILTER_CHANGED_FRAMEFILTER BIT(2) + +/* exported interface */ +void *ar9170_alloc(size_t priv_size); +int ar9170_register(struct ar9170 *ar, struct device *pdev); +void ar9170_rx(struct ar9170 *ar, struct sk_buff *skb); +void ar9170_unregister(struct ar9170 *ar); +void ar9170_handle_tx_status(struct ar9170 *ar, struct sk_buff *skb, + bool update_statistics, u16 tx_status); + +/* MAC */ +int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb); +int ar9170_init_mac(struct ar9170 *ar); +int ar9170_set_qos(struct ar9170 *ar); +int ar9170_update_multicast(struct ar9170 *ar); +int ar9170_update_frame_filter(struct ar9170 *ar); +int ar9170_set_operating_mode(struct ar9170 *ar); +int ar9170_set_beacon_timers(struct ar9170 *ar); +int ar9170_set_hwretry_limit(struct ar9170 *ar, u32 max_retry); +int ar9170_update_beacon(struct ar9170 *ar); +void ar9170_new_beacon(struct work_struct *work); +int ar9170_upload_key(struct ar9170 *ar, u8 id, const u8 *mac, u8 ktype, + u8 keyidx, u8 *keydata, int keylen); +int ar9170_disable_key(struct ar9170 *ar, u8 id); + +/* LEDs */ +#ifdef CONFIG_AR9170_LEDS +int ar9170_register_leds(struct ar9170 *ar); +void ar9170_unregister_leds(struct ar9170 *ar); +#endif /* CONFIG_AR9170_LEDS */ +int ar9170_init_leds(struct ar9170 *ar); +int ar9170_set_leds_state(struct ar9170 *ar, u32 led_state); + +/* PHY / RF */ +int ar9170_init_phy(struct ar9170 *ar, enum ieee80211_band band); +int ar9170_init_rf(struct ar9170 *ar); +int ar9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel, + enum ar9170_rf_init_mode rfi, enum ar9170_bw bw); + +#endif /* __AR9170_H */ diff --git a/drivers/net/wireless/ar9170/cmd.c b/drivers/net/wireless/ar9170/cmd.c new file mode 100644 index 00000000000..fd5625c4e9d --- /dev/null +++ b/drivers/net/wireless/ar9170/cmd.c @@ -0,0 +1,130 @@ +/* + * Atheros AR9170 driver + * + * Basic HW register/memory/command access functions + * + * Copyright 2008, Johannes Berg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 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 "ar9170.h" +#include "cmd.h" + +int ar9170_write_mem(struct ar9170 *ar, const __le32 *data, size_t len) +{ + int err; + + if (unlikely(!IS_ACCEPTING_CMD(ar))) + return 0; + + err = ar->exec_cmd(ar, AR9170_CMD_WMEM, len, (u8 *) data, 0, NULL); + if (err) + printk(KERN_DEBUG "%s: writing memory failed\n", + wiphy_name(ar->hw->wiphy)); + return err; +} + +int ar9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val) +{ + __le32 buf[2] = { + cpu_to_le32(reg), + cpu_to_le32(val), + }; + int err; + + if (unlikely(!IS_ACCEPTING_CMD(ar))) + return 0; + + err = ar->exec_cmd(ar, AR9170_CMD_WREG, sizeof(buf), + (u8 *) buf, 0, NULL); + if (err) + printk(KERN_DEBUG "%s: writing reg %#x (val %#x) failed\n", + wiphy_name(ar->hw->wiphy), reg, val); + return err; +} + +static int ar9170_read_mreg(struct ar9170 *ar, int nregs, + const u32 *regs, u32 *out) +{ + int i, err; + __le32 *offs, *res; + + if (unlikely(!IS_ACCEPTING_CMD(ar))) + return 0; + + /* abuse "out" for the register offsets, must be same length */ + offs = (__le32 *)out; + for (i = 0; i < nregs; i++) + offs[i] = cpu_to_le32(regs[i]); + + /* also use the same buffer for the input */ + res = (__le32 *)out; + + err = ar->exec_cmd(ar, AR9170_CMD_RREG, + 4 * nregs, (u8 *)offs, + 4 * nregs, (u8 *)res); + if (err) + return err; + + /* convert result to cpu endian */ + for (i = 0; i < nregs; i++) + out[i] = le32_to_cpu(res[i]); + + return 0; +} + +int ar9170_read_reg(struct ar9170 *ar, u32 reg, u32 *val) +{ + return ar9170_read_mreg(ar, 1, ®, val); +} + +int ar9170_echo_test(struct ar9170 *ar, u32 v) +{ + __le32 echobuf = cpu_to_le32(v); + __le32 echores; + int err; + + if (unlikely(!IS_ACCEPTING_CMD(ar))) + return -ENODEV; + + err = ar->exec_cmd(ar, AR9170_CMD_ECHO, + 4, (u8 *)&echobuf, + 4, (u8 *)&echores); + if (err) + return err; + + if (echobuf != echores) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_GPL(ar9170_echo_test); diff --git a/drivers/net/wireless/ar9170/cmd.h b/drivers/net/wireless/ar9170/cmd.h new file mode 100644 index 00000000000..a4f0e50e52b --- /dev/null +++ b/drivers/net/wireless/ar9170/cmd.h @@ -0,0 +1,91 @@ +/* + * Atheros AR9170 driver + * + * Basic HW register/memory/command access functions + * + * Copyright 2008, Johannes Berg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 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 __CMD_H +#define __CMD_H + +#include "ar9170.h" + +/* basic HW access */ +int ar9170_write_mem(struct ar9170 *ar, const __le32 *data, size_t len); +int ar9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val); +int ar9170_read_reg(struct ar9170 *ar, u32 reg, u32 *val); +int ar9170_echo_test(struct ar9170 *ar, u32 v); + +/* + * Macros to facilitate writing multiple registers in a single + * write-combining USB command. Note that when the first group + * fails the whole thing will fail without any others attempted, + * but you won't know which write in the group failed. + */ +#define ar9170_regwrite_begin(ar) \ +do { \ + int __nreg = 0, __err = 0; \ + struct ar9170 *__ar = ar; + +#define ar9170_regwrite(r, v) do { \ + __ar->cmdbuf[2 * __nreg + 1] = cpu_to_le32(r); \ + __ar->cmdbuf[2 * __nreg + 2] = cpu_to_le32(v); \ + __nreg++; \ + if ((__nreg >= PAYLOAD_MAX/2)) { \ + if (IS_ACCEPTING_CMD(__ar)) \ + __err = ar->exec_cmd(__ar, AR9170_CMD_WREG, \ + 8 * __nreg, \ + (u8 *) &__ar->cmdbuf[1], \ + 0, NULL); \ + __nreg = 0; \ + if (__err) \ + goto __regwrite_out; \ + } \ +} while (0) + +#define ar9170_regwrite_finish() \ +__regwrite_out : \ + if (__nreg) { \ + if (IS_ACCEPTING_CMD(__ar)) \ + __err = ar->exec_cmd(__ar, AR9170_CMD_WREG, \ + 8 * __nreg, \ + (u8 *) &__ar->cmdbuf[1], \ + 0, NULL); \ + __nreg = 0; \ + } + +#define ar9170_regwrite_result() \ + __err; \ +} while (0); + +#endif /* __CMD_H */ diff --git a/drivers/net/wireless/ar9170/led.c b/drivers/net/wireless/ar9170/led.c new file mode 100644 index 00000000000..341cead7f60 --- /dev/null +++ b/drivers/net/wireless/ar9170/led.c @@ -0,0 +1,171 @@ +/* + * Atheros AR9170 driver + * + * LED handling + * + * Copyright 2008, Johannes Berg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 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 "ar9170.h" +#include "cmd.h" + +int ar9170_set_leds_state(struct ar9170 *ar, u32 led_state) +{ + return ar9170_write_reg(ar, AR9170_GPIO_REG_DATA, led_state); +} + +int ar9170_init_leds(struct ar9170 *ar) +{ + int err; + + /* disable LEDs */ + /* GPIO [0/1 mode: output, 2/3: input] */ + err = ar9170_write_reg(ar, AR9170_GPIO_REG_PORT_TYPE, 3); + if (err) + goto out; + + /* GPIO 0/1 value: off */ + err = ar9170_set_leds_state(ar, 0); + +out: + return err; +} + +#ifdef CONFIG_AR9170_LEDS +static void ar9170_update_leds(struct work_struct *work) +{ + struct ar9170 *ar = container_of(work, struct ar9170, led_work.work); + int i, tmp, blink_delay = 1000; + u32 led_val = 0; + bool rerun = false; + + if (unlikely(!IS_ACCEPTING_CMD(ar))) + return ; + + mutex_lock(&ar->mutex); + for (i = 0; i < AR9170_NUM_LEDS; i++) + if (ar->leds[i].toggled) { + led_val |= 1 << i; + + tmp = 70 + 200 / (ar->leds[i].toggled); + if (tmp < blink_delay) + blink_delay = tmp; + + if (ar->leds[i].toggled > 1) + ar->leds[i].toggled = 0; + + rerun = true; + } + + ar9170_set_leds_state(ar, led_val); + mutex_unlock(&ar->mutex); + + if (rerun) + queue_delayed_work(ar->hw->workqueue, &ar->led_work, + msecs_to_jiffies(blink_delay)); +} + +static void ar9170_led_brightness_set(struct led_classdev *led, + enum led_brightness brightness) +{ + struct ar9170_led *arl = container_of(led, struct ar9170_led, l); + struct ar9170 *ar = arl->ar; + + arl->toggled++; + + if (likely(IS_ACCEPTING_CMD(ar) && brightness)) + queue_delayed_work(ar->hw->workqueue, &ar->led_work, HZ/10); +} + +static int ar9170_register_led(struct ar9170 *ar, int i, char *name, + char *trigger) +{ + int err; + + snprintf(ar->leds[i].name, sizeof(ar->leds[i].name), + "ar9170-%s::%s", wiphy_name(ar->hw->wiphy), name); + + ar->leds[i].ar = ar; + ar->leds[i].l.name = ar->leds[i].name; + ar->leds[i].l.brightness_set = ar9170_led_brightness_set; + ar->leds[i].l.brightness = 0; + ar->leds[i].l.default_trigger = trigger; + + err = led_classdev_register(wiphy_dev(ar->hw->wiphy), + &ar->leds[i].l); + if (err) + printk(KERN_ERR "%s: failed to register %s LED (%d).\n", + wiphy_name(ar->hw->wiphy), ar->leds[i].name, err); + else + ar->leds[i].registered = true; + + return err; +} + +void ar9170_unregister_leds(struct ar9170 *ar) +{ + int i; + + cancel_delayed_work_sync(&ar->led_work); + + for (i = 0; i < AR9170_NUM_LEDS; i++) + if (ar->leds[i].registered) { + led_classdev_unregister(&ar->leds[i].l); + ar->leds[i].registered = false; + } +} + +int ar9170_register_leds(struct ar9170 *ar) +{ + int err; + + INIT_DELAYED_WORK(&ar->led_work, ar9170_update_leds); + + err = ar9170_register_led(ar, 0, "tx", + ieee80211_get_tx_led_name(ar->hw)); + if (err) + goto fail; + + err = ar9170_register_led(ar, 1, "assoc", + ieee80211_get_assoc_led_name(ar->hw)); + if (err) + goto fail; + + return 0; + +fail: + ar9170_unregister_leds(ar); + return err; +} + +#endif /* CONFIG_AR9170_LEDS */ diff --git a/drivers/net/wireless/ar9170/main.c b/drivers/net/wireless/ar9170/main.c new file mode 100644 index 00000000000..a3b5323743c --- /dev/null +++ b/drivers/net/wireless/ar9170/main.c @@ -0,0 +1,1776 @@ +/* + * Atheros AR9170 driver + * + * mac80211 interaction code + * + * Copyright 2008, Johannes Berg + * Copyright 2009, Christian Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 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. + */ + +/* + * BIG FAT TODO: + * + * By the looks of things: these devices share a lot of things like + * EEPROM layout/design and PHY code with other Atheros WIFI products. + * So this driver/library will eventually become ath9k code... or vice versa ;-) + */ + +#include +#include +#include +#include +#include "ar9170.h" +#include "hw.h" +#include "cmd.h" + +static int modparam_nohwcrypt; +module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); +MODULE_AUTHOR("Johannes Berg "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Atheros shared code for AR9170 wireless devices"); + +#define RATE(_bitrate, _hw_rate, _txpidx, _flags) { \ + .bitrate = (_bitrate), \ + .flags = (_flags), \ + .hw_value = (_hw_rate) | (_txpidx) << 4, \ +} + +static struct ieee80211_rate __ar9170_ratetable[] = { + RATE(10, 0, 0, 0), + RATE(20, 1, 1, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(55, 2, 2, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(110, 3, 3, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(60, 0xb, 0, 0), + RATE(90, 0xf, 0, 0), + RATE(120, 0xa, 0, 0), + RATE(180, 0xe, 0, 0), + RATE(240, 0x9, 0, 0), + RATE(360, 0xd, 1, 0), + RATE(480, 0x8, 2, 0), + RATE(540, 0xc, 3, 0), +}; +#undef RATE + +#define ar9170_g_ratetable (__ar9170_ratetable + 0) +#define ar9170_g_ratetable_size 12 +#define ar9170_a_ratetable (__ar9170_ratetable + 4) +#define ar9170_a_ratetable_size 8 + +/* + * NB: The hw_value is used as an index into the ar9170_phy_freq_params + * array in phy.c so that we don't have to do frequency lookups! + */ +#define CHAN(_freq, _idx) { \ + .center_freq = (_freq), \ + .hw_value = (_idx), \ + .max_power = 18, /* XXX */ \ +} + +static struct ieee80211_channel ar9170_2ghz_chantable[] = { + CHAN(2412, 0), + CHAN(2417, 1), + CHAN(2422, 2), + CHAN(2427, 3), + CHAN(2432, 4), + CHAN(2437, 5), + CHAN(2442, 6), + CHAN(2447, 7), + CHAN(2452, 8), + CHAN(2457, 9), + CHAN(2462, 10), + CHAN(2467, 11), + CHAN(2472, 12), + CHAN(2484, 13), +}; + +static struct ieee80211_channel ar9170_5ghz_chantable[] = { + CHAN(4920, 14), + CHAN(4940, 15), + CHAN(4960, 16), + CHAN(4980, 17), + CHAN(5040, 18), + CHAN(5060, 19), + CHAN(5080, 20), + CHAN(5180, 21), + CHAN(5200, 22), + CHAN(5220, 23), + CHAN(5240, 24), + CHAN(5260, 25), + CHAN(5280, 26), + CHAN(5300, 27), + CHAN(5320, 28), + CHAN(5500, 29), + CHAN(5520, 30), + CHAN(5540, 31), + CHAN(5560, 32), + CHAN(5580, 33), + CHAN(5600, 34), + CHAN(5620, 35), + CHAN(5640, 36), + CHAN(5660, 37), + CHAN(5680, 38), + CHAN(5700, 39), + CHAN(5745, 40), + CHAN(5765, 41), + CHAN(5785, 42), + CHAN(5805, 43), + CHAN(5825, 44), + CHAN(5170, 45), + CHAN(5190, 46), + CHAN(5210, 47), + CHAN(5230, 48), +}; +#undef CHAN + +static struct ieee80211_supported_band ar9170_band_2GHz = { + .channels = ar9170_2ghz_chantable, + .n_channels = ARRAY_SIZE(ar9170_2ghz_chantable), + .bitrates = ar9170_g_ratetable, + .n_bitrates = ar9170_g_ratetable_size, +}; + +#ifdef AR9170_QUEUE_DEBUG +/* + * In case some wants works with AR9170's crazy tx_status queueing techniques. + * He might need this rather useful probing function. + * + * NOTE: caller must hold the queue's spinlock! + */ + +static void ar9170_print_txheader(struct ar9170 *ar, struct sk_buff *skb) +{ + struct ar9170_tx_control *txc = (void *) skb->data; + struct ieee80211_hdr *hdr = (void *)txc->frame_data; + + printk(KERN_DEBUG "%s: => FRAME [skb:%p, queue:%d, DA:[%pM] " + "mac_control:%04x, phy_control:%08x]\n", + wiphy_name(ar->hw->wiphy), skb, skb_get_queue_mapping(skb), + ieee80211_get_DA(hdr), le16_to_cpu(txc->mac_control), + le32_to_cpu(txc->phy_control)); +} + +static void ar9170_dump_station_tx_status_queue(struct ar9170 *ar, + struct sk_buff_head *queue) +{ + struct sk_buff *skb; + int i = 0; + + printk(KERN_DEBUG "---[ cut here ]---\n"); + printk(KERN_DEBUG "%s: %d entries in tx_status queue.\n", + wiphy_name(ar->hw->wiphy), skb_queue_len(queue)); + + skb_queue_walk(queue, skb) { + struct ar9170_tx_control *txc = (void *) skb->data; + struct ieee80211_hdr *hdr = (void *)txc->frame_data; + + printk(KERN_DEBUG "index:%d => \n", i); + ar9170_print_txheader(ar, skb); + } + printk(KERN_DEBUG "---[ end ]---\n"); +} +#endif /* AR9170_QUEUE_DEBUG */ + +static struct ieee80211_supported_band ar9170_band_5GHz = { + .channels = ar9170_5ghz_chantable, + .n_channels = ARRAY_SIZE(ar9170_5ghz_chantable), + .bitrates = ar9170_a_ratetable, + .n_bitrates = ar9170_a_ratetable_size, +}; + +void ar9170_handle_tx_status(struct ar9170 *ar, struct sk_buff *skb, + bool valid_status, u16 tx_status) +{ + struct ieee80211_tx_info *txinfo; + unsigned int retries = 0, queue = skb_get_queue_mapping(skb); + unsigned long flags; + + spin_lock_irqsave(&ar->tx_stats_lock, flags); + ar->tx_stats[queue].len--; + if (ieee80211_queue_stopped(ar->hw, queue)) + ieee80211_wake_queue(ar->hw, queue); + spin_unlock_irqrestore(&ar->tx_stats_lock, flags); + + txinfo = IEEE80211_SKB_CB(skb); + ieee80211_tx_info_clear_status(txinfo); + + switch (tx_status) { + case AR9170_TX_STATUS_RETRY: + retries = 2; + case AR9170_TX_STATUS_COMPLETE: + txinfo->flags |= IEEE80211_TX_STAT_ACK; + break; + + case AR9170_TX_STATUS_FAILED: + retries = ar->hw->conf.long_frame_max_tx_count; + break; + + default: + printk(KERN_ERR "%s: invalid tx_status response (%x).\n", + wiphy_name(ar->hw->wiphy), tx_status); + break; + } + + if (valid_status) + txinfo->status.rates[0].count = retries + 1; + + skb_pull(skb, sizeof(struct ar9170_tx_control)); + ieee80211_tx_status_irqsafe(ar->hw, skb); +} +EXPORT_SYMBOL_GPL(ar9170_handle_tx_status); + +static struct sk_buff *ar9170_find_skb_in_queue(struct ar9170 *ar, + const u8 *mac, + const u32 queue, + struct sk_buff_head *q) +{ + unsigned long flags; + struct sk_buff *skb; + + spin_lock_irqsave(&q->lock, flags); + skb_queue_walk(q, skb) { + struct ar9170_tx_control *txc = (void *) skb->data; + struct ieee80211_hdr *hdr = (void *) txc->frame_data; + u32 txc_queue = (le32_to_cpu(txc->phy_control) & + AR9170_TX_PHY_QOS_MASK) >> + AR9170_TX_PHY_QOS_SHIFT; + + if ((queue != txc_queue) || + (compare_ether_addr(ieee80211_get_DA(hdr), mac))) + continue; + + __skb_unlink(skb, q); + spin_unlock_irqrestore(&q->lock, flags); + return skb; + } + spin_unlock_irqrestore(&q->lock, flags); + return NULL; +} + +static struct sk_buff *ar9170_find_skb_in_queue_by_mac(struct ar9170 *ar, + const u8 *mac, + struct sk_buff_head *q) +{ + unsigned long flags; + struct sk_buff *skb; + + spin_lock_irqsave(&q->lock, flags); + skb_queue_walk(q, skb) { + struct ar9170_tx_control *txc = (void *) skb->data; + struct ieee80211_hdr *hdr = (void *) txc->frame_data; + + if (compare_ether_addr(ieee80211_get_DA(hdr), mac)) + continue; + + __skb_unlink(skb, q); + spin_unlock_irqrestore(&q->lock, flags); + return skb; + } + spin_unlock_irqrestore(&q->lock, flags); + return NULL; +} + +static struct sk_buff *ar9170_find_skb_in_queue_by_txcq(struct ar9170 *ar, + const u32 queue, + struct sk_buff_head *q) +{ + unsigned long flags; + struct sk_buff *skb; + + spin_lock_irqsave(&q->lock, flags); + skb_queue_walk(q, skb) { + struct ar9170_tx_control *txc = (void *) skb->data; + u32 txc_queue = (le32_to_cpu(txc->phy_control) & + AR9170_TX_PHY_QOS_MASK) >> + AR9170_TX_PHY_QOS_SHIFT; + + if (queue != txc_queue) + continue; + + __skb_unlink(skb, q); + spin_unlock_irqrestore(&q->lock, flags); + return skb; + } + spin_unlock_irqrestore(&q->lock, flags); + return NULL; +} + +static struct sk_buff *ar9170_find_queued_skb(struct ar9170 *ar, const u8 *mac, + const u32 queue) +{ + struct ieee80211_sta *sta; + struct sk_buff *skb; + + /* + * Unfortunately, the firmware does not tell to which (queued) frame + * this transmission status report belongs to. + * + * So we have to make risky guesses - with the scarce information + * the firmware provided (-> destination MAC, and phy_control) - + * and hope that we picked the right one... + */ + rcu_read_lock(); + sta = ieee80211_find_sta(ar->hw, mac); + + if (likely(sta)) { + struct ar9170_sta_info *sta_priv = (void *) sta->drv_priv; + skb = ar9170_find_skb_in_queue_by_txcq(ar, queue, + &sta_priv->tx_status); + } else { + /* STA is not in database (yet/anymore). */ + + /* scan the waste bin for likely candidates */ + skb = ar9170_find_skb_in_queue(ar, mac, queue, + &ar->global_tx_status_waste); + if (!skb) { + /* so it still _must_ be in the global list. */ + skb = ar9170_find_skb_in_queue(ar, mac, queue, + &ar->global_tx_status); + } + } + rcu_read_unlock(); + +#ifdef AR9170_QUEUE_DEBUG + if (unlikely((!skb) && net_ratelimit())) { + printk(KERN_ERR "%s: ESS:[%pM] does not have any " + "outstanding frames in this queue (%d).\n", + wiphy_name(ar->hw->wiphy), mac, queue); + } +#endif /* AR9170_QUEUE_DEBUG */ + + return skb; +} + +/* + * This worker tries to keep the global tx_status queue empty. + * So we can guarantee that incoming tx_status reports for + * unregistered stations are always synced with the actual + * frame - which we think - belongs to. + */ + +static void ar9170_tx_status_janitor(struct work_struct *work) +{ + struct ar9170 *ar = container_of(work, struct ar9170, + filter_config_work); + struct sk_buff *skb; + + /* recycle the garbage back to mac80211... one by one. */ + while ((skb = skb_dequeue(&ar->global_tx_status_waste))) { +#ifdef AR9170_QUEUE_DEBUG + printk(KERN_DEBUG "%s: dispose queued frame =>\n", + wiphy_name(ar->hw->wiphy)); + ar9170_print_txheader(ar, skb); +#endif /* AR9170_QUEUE_DEBUG */ + ar9170_handle_tx_status(ar, skb, false, + AR9170_TX_STATUS_FAILED); + } + + + + while ((skb = skb_dequeue(&ar->global_tx_status))) { +#ifdef AR9170_QUEUE_DEBUG + printk(KERN_DEBUG "%s: moving frame into waste queue =>\n", + wiphy_name(ar->hw->wiphy)); + + ar9170_print_txheader(ar, skb); +#endif /* AR9170_QUEUE_DEBUG */ + skb_queue_tail(&ar->global_tx_status_waste, skb); + } + + /* recall the janitor in 100ms - if there's garbage in the can. */ + if (skb_queue_len(&ar->global_tx_status_waste) > 0) + queue_delayed_work(ar->hw->workqueue, &ar->tx_status_janitor, + msecs_to_jiffies(100)); +} + +static void ar9170_handle_command_response(struct ar9170 *ar, + void *buf, u32 len) +{ + struct ar9170_cmd_response *cmd = (void *) buf; + + if ((cmd->type & 0xc0) != 0xc0) { + ar->callback_cmd(ar, len, buf); + return; + } + + /* hardware event handlers */ + switch (cmd->type) { + case 0xc1: { + /* + * TX status notification: + * bytes: 0c c1 XX YY M1 M2 M3 M4 M5 M6 R4 R3 R2 R1 S2 S1 + * + * XX always 81 + * YY always 00 + * M1-M6 is the MAC address + * R1-R4 is the transmit rate + * S1-S2 is the transmit status + */ + + struct sk_buff *skb; + u32 queue = (le32_to_cpu(cmd->tx_status.rate) & + AR9170_TX_PHY_QOS_MASK) >> AR9170_TX_PHY_QOS_SHIFT; + + skb = ar9170_find_queued_skb(ar, cmd->tx_status.dst, queue); + if (unlikely(!skb)) + return ; + + ar9170_handle_tx_status(ar, skb, true, + le16_to_cpu(cmd->tx_status.status)); + break; + } + + case 0xc0: + /* + * pre-TBTT event + */ + if (ar->vif && ar->vif->type == NL80211_IFTYPE_AP) + queue_work(ar->hw->workqueue, &ar->beacon_work); + break; + + case 0xc2: + /* + * (IBSS) beacon send notification + * bytes: 04 c2 XX YY B4 B3 B2 B1 + * + * XX always 80 + * YY always 00 + * B1-B4 "should" be the number of send out beacons. + */ + break; + + case 0xc3: + /* End of Atim Window */ + break; + + case 0xc4: + case 0xc5: + /* BlockACK events */ + break; + + case 0xc6: + /* Watchdog Interrupt */ + break; + + case 0xc9: + /* retransmission issue / SIFS/EIFS collision ?! */ + break; + + default: + printk(KERN_INFO "received unhandled event %x\n", cmd->type); + print_hex_dump_bytes("dump:", DUMP_PREFIX_NONE, buf, len); + break; + } +} + +/* + * If the frame alignment is right (or the kernel has + * CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS), and there + * is only a single MPDU in the USB frame, then we can + * submit to mac80211 the SKB directly. However, since + * there may be multiple packets in one SKB in stream + * mode, and we need to observe the proper ordering, + * this is non-trivial. + */ +static void ar9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len) +{ + struct sk_buff *skb; + struct ar9170_rx_head *head = (void *)buf; + struct ar9170_rx_tail *tail; + struct ieee80211_rx_status status; + int mpdu_len, i; + u8 error, antennas = 0, decrypt; + __le16 fc; + int reserved; + + if (unlikely(!IS_STARTED(ar))) + return ; + + /* Received MPDU */ + mpdu_len = len; + mpdu_len -= sizeof(struct ar9170_rx_head); + mpdu_len -= sizeof(struct ar9170_rx_tail); + BUILD_BUG_ON(sizeof(struct ar9170_rx_head) != 12); + BUILD_BUG_ON(sizeof(struct ar9170_rx_tail) != 24); + + if (mpdu_len <= FCS_LEN) + return; + + tail = (void *)(buf + sizeof(struct ar9170_rx_head) + mpdu_len); + + for (i = 0; i < 3; i++) + if (tail->rssi[i] != 0x80) + antennas |= BIT(i); + + /* post-process RSSI */ + for (i = 0; i < 7; i++) + if (tail->rssi[i] & 0x80) + tail->rssi[i] = ((tail->rssi[i] & 0x7f) + 1) & 0x7f; + + memset(&status, 0, sizeof(status)); + + status.band = ar->channel->band; + status.freq = ar->channel->center_freq; + status.signal = ar->noise[0] + tail->rssi_combined; + status.noise = ar->noise[0]; + status.antenna = antennas; + + switch (tail->status & AR9170_RX_STATUS_MODULATION_MASK) { + case AR9170_RX_STATUS_MODULATION_CCK: + if (tail->status & AR9170_RX_STATUS_SHORT_PREAMBLE) + status.flag |= RX_FLAG_SHORTPRE; + switch (head->plcp[0]) { + case 0x0a: + status.rate_idx = 0; + break; + case 0x14: + status.rate_idx = 1; + break; + case 0x37: + status.rate_idx = 2; + break; + case 0x6e: + status.rate_idx = 3; + break; + default: + if ((!ar->sniffer_enabled) && (net_ratelimit())) + printk(KERN_ERR "%s: invalid plcp cck rate " + "(%x).\n", wiphy_name(ar->hw->wiphy), + head->plcp[0]); + return; + } + break; + case AR9170_RX_STATUS_MODULATION_OFDM: + switch (head->plcp[0] & 0xF) { + case 0xB: + status.rate_idx = 0; + break; + case 0xF: + status.rate_idx = 1; + break; + case 0xA: + status.rate_idx = 2; + break; + case 0xE: + status.rate_idx = 3; + break; + case 0x9: + status.rate_idx = 4; + break; + case 0xD: + status.rate_idx = 5; + break; + case 0x8: + status.rate_idx = 6; + break; + case 0xC: + status.rate_idx = 7; + break; + default: + if ((!ar->sniffer_enabled) && (net_ratelimit())) + printk(KERN_ERR "%s: invalid plcp ofdm rate " + "(%x).\n", wiphy_name(ar->hw->wiphy), + head->plcp[0]); + return; + } + if (status.band == IEEE80211_BAND_2GHZ) + status.rate_idx += 4; + break; + case AR9170_RX_STATUS_MODULATION_HT: + case AR9170_RX_STATUS_MODULATION_DUPOFDM: + /* XXX */ + + if (net_ratelimit()) + printk(KERN_ERR "%s: invalid modulation\n", + wiphy_name(ar->hw->wiphy)); + return; + } + + error = tail->error; + + if (error & AR9170_RX_ERROR_MMIC) { + status.flag |= RX_FLAG_MMIC_ERROR; + error &= ~AR9170_RX_ERROR_MMIC; + } + + if (error & AR9170_RX_ERROR_PLCP) { + status.flag |= RX_FLAG_FAILED_PLCP_CRC; + error &= ~AR9170_RX_ERROR_PLCP; + } + + if (error & AR9170_RX_ERROR_FCS) { + status.flag |= RX_FLAG_FAILED_FCS_CRC; + error &= ~AR9170_RX_ERROR_FCS; + } + + decrypt = ar9170_get_decrypt_type(tail); + if (!(decrypt & AR9170_RX_ENC_SOFTWARE) && + decrypt != AR9170_ENC_ALG_NONE) + status.flag |= RX_FLAG_DECRYPTED; + + /* ignore wrong RA errors */ + error &= ~AR9170_RX_ERROR_WRONG_RA; + + if (error & AR9170_RX_ERROR_DECRYPT) { + error &= ~AR9170_RX_ERROR_DECRYPT; + + /* + * Rx decryption is done in place, + * the original data is lost anyway. + */ + return ; + } + + /* drop any other error frames */ + if ((error) && (net_ratelimit())) { + printk(KERN_DEBUG "%s: errors: %#x\n", + wiphy_name(ar->hw->wiphy), error); + return; + } + + buf += sizeof(struct ar9170_rx_head); + fc = *(__le16 *)buf; + + if (ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc)) + reserved = 32 + 2; + else + reserved = 32; + + skb = dev_alloc_skb(mpdu_len + reserved); + if (!skb) + return; + + skb_reserve(skb, reserved); + memcpy(skb_put(skb, mpdu_len), buf, mpdu_len); + ieee80211_rx_irqsafe(ar->hw, skb, &status); +} + +/* + * TODO: + * It looks like AR9170 supports more than just the USB transport interface. + * Unfortunately, there is no available information what parts of the + * precendent and following code fragments is device specific and what not. + * For now, everything stays here, until some SPI chips pop up. + */ +void ar9170_rx(struct ar9170 *ar, struct sk_buff *skb) +{ + unsigned int i, tlen, resplen; + u8 *tbuf, *respbuf; + + tbuf = skb->data; + tlen = skb->len; + + while (tlen >= 4) { + int clen = tbuf[1] << 8 | tbuf[0]; + int wlen = (clen + 3) & ~3; + + /* + * parse stream (if any) + */ + if (tbuf[2] != 0 || tbuf[3] != 0x4e) { + printk(KERN_ERR "%s: missing tag!\n", + wiphy_name(ar->hw->wiphy)); + return ; + } + if (wlen > tlen - 4) { + printk(KERN_ERR "%s: invalid RX (%d, %d, %d)\n", + wiphy_name(ar->hw->wiphy), clen, wlen, tlen); + print_hex_dump(KERN_DEBUG, "data: ", + DUMP_PREFIX_OFFSET, + 16, 1, tbuf, tlen, true); + return ; + } + resplen = clen; + respbuf = tbuf + 4; + tbuf += wlen + 4; + tlen -= wlen + 4; + + i = 0; + + /* weird thing, but this is the same in the original driver */ + while (resplen > 2 && i < 12 && + respbuf[0] == 0xff && respbuf[1] == 0xff) { + i += 2; + resplen -= 2; + respbuf += 2; + } + + if (resplen < 4) + continue; + + /* found the 6 * 0xffff marker? */ + if (i == 12) + ar9170_handle_command_response(ar, respbuf, resplen); + else + ar9170_handle_mpdu(ar, respbuf, resplen); + } + + if (tlen) + printk(KERN_ERR "%s: buffer remains!\n", + wiphy_name(ar->hw->wiphy)); +} +EXPORT_SYMBOL_GPL(ar9170_rx); + +#define AR9170_FILL_QUEUE(queue, ai_fs, cwmin, cwmax, _txop) \ +do { \ + queue.aifs = ai_fs; \ + queue.cw_min = cwmin; \ + queue.cw_max = cwmax; \ + queue.txop = _txop; \ +} while (0) + +static int ar9170_op_start(struct ieee80211_hw *hw) +{ + struct ar9170 *ar = hw->priv; + int err, i; + + mutex_lock(&ar->mutex); + + /* reinitialize queues statistics */ + memset(&ar->tx_stats, 0, sizeof(ar->tx_stats)); + for (i = 0; i < ARRAY_SIZE(ar->tx_stats); i++) + ar->tx_stats[i].limit = 8; + + /* reset QoS defaults */ + AR9170_FILL_QUEUE(ar->edcf[0], 3, 15, 1023, 0); /* BEST EFFORT*/ + AR9170_FILL_QUEUE(ar->edcf[1], 7, 15, 1023, 0); /* BACKGROUND */ + AR9170_FILL_QUEUE(ar->edcf[2], 2, 7, 15, 94); /* VIDEO */ + AR9170_FILL_QUEUE(ar->edcf[3], 2, 3, 7, 47); /* VOICE */ + AR9170_FILL_QUEUE(ar->edcf[4], 2, 3, 7, 0); /* SPECIAL */ + + err = ar->open(ar); + if (err) + goto out; + + err = ar9170_init_mac(ar); + if (err) + goto out; + + err = ar9170_set_qos(ar); + if (err) + goto out; + + err = ar9170_init_phy(ar, IEEE80211_BAND_2GHZ); + if (err) + goto out; + + err = ar9170_init_rf(ar); + if (err) + goto out; + + /* start DMA */ + err = ar9170_write_reg(ar, 0x1c3d30, 0x100); + if (err) + goto out; + + ar->state = AR9170_STARTED; + +out: + mutex_unlock(&ar->mutex); + return err; +} + +static void ar9170_op_stop(struct ieee80211_hw *hw) +{ + struct ar9170 *ar = hw->priv; + + if (IS_STARTED(ar)) + ar->state = AR9170_IDLE; + + mutex_lock(&ar->mutex); + + cancel_delayed_work_sync(&ar->tx_status_janitor); + cancel_work_sync(&ar->filter_config_work); + cancel_work_sync(&ar->beacon_work); + skb_queue_purge(&ar->global_tx_status_waste); + skb_queue_purge(&ar->global_tx_status); + + if (IS_ACCEPTING_CMD(ar)) { + ar9170_set_leds_state(ar, 0); + + /* stop DMA */ + ar9170_write_reg(ar, 0x1c3d30, 0); + ar->stop(ar); + } + + mutex_unlock(&ar->mutex); +} + +int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct ar9170 *ar = hw->priv; + struct ieee80211_hdr *hdr; + struct ar9170_tx_control *txc; + struct ieee80211_tx_info *info; + struct ieee80211_rate *rate = NULL; + struct ieee80211_tx_rate *txrate; + unsigned int queue = skb_get_queue_mapping(skb); + unsigned long flags = 0; + struct ar9170_sta_info *sta_info = NULL; + u32 power, chains; + u16 keytype = 0; + u16 len, icv = 0; + int err; + bool tx_status; + + if (unlikely(!IS_STARTED(ar))) + goto err_free; + + hdr = (void *)skb->data; + info = IEEE80211_SKB_CB(skb); + len = skb->len; + + spin_lock_irqsave(&ar->tx_stats_lock, flags); + if (ar->tx_stats[queue].limit < ar->tx_stats[queue].len) { + spin_unlock_irqrestore(&ar->tx_stats_lock, flags); + return NETDEV_TX_OK; + } + + ar->tx_stats[queue].len++; + ar->tx_stats[queue].count++; + if (ar->tx_stats[queue].limit == ar->tx_stats[queue].len) + ieee80211_stop_queue(hw, queue); + + spin_unlock_irqrestore(&ar->tx_stats_lock, flags); + + txc = (void *)skb_push(skb, sizeof(*txc)); + + tx_status = (((info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) != 0) || + ((info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) != 0)); + + if (info->control.hw_key) { + icv = info->control.hw_key->icv_len; + + switch (info->control.hw_key->alg) { + case ALG_WEP: + keytype = AR9170_TX_MAC_ENCR_RC4; + break; + case ALG_TKIP: + keytype = AR9170_TX_MAC_ENCR_RC4; + break; + case ALG_CCMP: + keytype = AR9170_TX_MAC_ENCR_AES; + break; + default: + WARN_ON(1); + goto err_dequeue; + } + } + + /* Length */ + txc->length = cpu_to_le16(len + icv + 4); + + txc->mac_control = cpu_to_le16(AR9170_TX_MAC_HW_DURATION | + AR9170_TX_MAC_BACKOFF); + txc->mac_control |= cpu_to_le16(ar9170_qos_hwmap[queue] << + AR9170_TX_MAC_QOS_SHIFT); + txc->mac_control |= cpu_to_le16(keytype); + txc->phy_control = cpu_to_le32(0); + + if (info->flags & IEEE80211_TX_CTL_NO_ACK) + txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_NO_ACK); + + if (info->flags & IEEE80211_TX_CTL_AMPDU) + txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_AGGR); + + txrate = &info->control.rates[0]; + + if (txrate->flags & IEEE80211_TX_RC_USE_CTS_PROTECT) + txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_PROT_CTS); + else if (txrate->flags & IEEE80211_TX_RC_USE_RTS_CTS) + txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_PROT_RTS); + + if (txrate->flags & IEEE80211_TX_RC_GREEN_FIELD) + txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_GREENFIELD); + + if (txrate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) + txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_SHORT_PREAMBLE); + + if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_BW_40MHZ); + /* this works because 40 MHz is 2 and dup is 3 */ + if (txrate->flags & IEEE80211_TX_RC_DUP_DATA) + txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_BW_40MHZ_DUP); + + if (txrate->flags & IEEE80211_TX_RC_SHORT_GI) + txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_SHORT_GI); + + if (txrate->flags & IEEE80211_TX_RC_MCS) { + u32 r = txrate->idx; + u8 *txpower; + + r <<= AR9170_TX_PHY_MCS_SHIFT; + if (WARN_ON(r & ~AR9170_TX_PHY_MCS_MASK)) + goto err_dequeue; + txc->phy_control |= cpu_to_le32(r & AR9170_TX_PHY_MCS_MASK); + txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_MOD_HT); + + if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) { + if (info->band == IEEE80211_BAND_5GHZ) + txpower = ar->power_5G_ht40; + else + txpower = ar->power_2G_ht40; + } else { + if (info->band == IEEE80211_BAND_5GHZ) + txpower = ar->power_5G_ht20; + else + txpower = ar->power_2G_ht20; + } + + power = txpower[(txrate->idx) & 7]; + } else { + u8 *txpower; + u32 mod; + u32 phyrate; + u8 idx = txrate->idx; + + if (info->band != IEEE80211_BAND_2GHZ) { + idx += 4; + txpower = ar->power_5G_leg; + mod = AR9170_TX_PHY_MOD_OFDM; + } else { + if (idx < 4) { + txpower = ar->power_2G_cck; + mod = AR9170_TX_PHY_MOD_CCK; + } else { + mod = AR9170_TX_PHY_MOD_OFDM; + txpower = ar->power_2G_ofdm; + } + } + + rate = &__ar9170_ratetable[idx]; + + phyrate = rate->hw_value & 0xF; + power = txpower[(rate->hw_value & 0x30) >> 4]; + phyrate <<= AR9170_TX_PHY_MCS_SHIFT; + + txc->phy_control |= cpu_to_le32(mod); + txc->phy_control |= cpu_to_le32(phyrate); + } + + power <<= AR9170_TX_PHY_TX_PWR_SHIFT; + power &= AR9170_TX_PHY_TX_PWR_MASK; + txc->phy_control |= cpu_to_le32(power); + + /* set TX chains */ + if (ar->eeprom.tx_mask == 1) { + chains = AR9170_TX_PHY_TXCHAIN_1; + } else { + chains = AR9170_TX_PHY_TXCHAIN_2; + + /* >= 36M legacy OFDM - use only one chain */ + if (rate && rate->bitrate >= 360) + chains = AR9170_TX_PHY_TXCHAIN_1; + } + txc->phy_control |= cpu_to_le32(chains << AR9170_TX_PHY_TXCHAIN_SHIFT); + + if (tx_status) { + txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_RATE_PROBE); + /* + * WARNING: + * Putting the QoS queue bits into an unexplored territory is + * certainly not elegant. + * + * In my defense: This idea provides a reasonable way to + * smuggle valuable information to the tx_status callback. + * Also, the idea behind this bit-abuse came straight from + * the original driver code. + */ + + txc->phy_control |= + cpu_to_le32(queue << AR9170_TX_PHY_QOS_SHIFT); + + if (info->control.sta) { + sta_info = (void *) info->control.sta->drv_priv; + skb_queue_tail(&sta_info->tx_status, skb); + } else { + skb_queue_tail(&ar->global_tx_status, skb); + + queue_delayed_work(ar->hw->workqueue, + &ar->tx_status_janitor, + msecs_to_jiffies(100)); + } + } + + err = ar->tx(ar, skb, tx_status, 0); + if (unlikely(tx_status && err)) { + if (info->control.sta) + skb_unlink(skb, &sta_info->tx_status); + else + skb_unlink(skb, &ar->global_tx_status); + } + + return NETDEV_TX_OK; + +err_dequeue: + spin_lock_irqsave(&ar->tx_stats_lock, flags); + ar->tx_stats[queue].len--; + ar->tx_stats[queue].count--; + spin_unlock_irqrestore(&ar->tx_stats_lock, flags); + +err_free: + dev_kfree_skb(skb); + return NETDEV_TX_OK; +} + +static int ar9170_op_add_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct ar9170 *ar = hw->priv; + int err = 0; + + mutex_lock(&ar->mutex); + + if (ar->vif) { + err = -EBUSY; + goto unlock; + } + + ar->vif = conf->vif; + memcpy(ar->mac_addr, conf->mac_addr, ETH_ALEN); + + if (modparam_nohwcrypt || (ar->vif->type != NL80211_IFTYPE_STATION)) { + ar->rx_software_decryption = true; + ar->disable_offload = true; + } + + ar->cur_filter = 0; + ar->want_filter = AR9170_MAC_REG_FTF_DEFAULTS; + err = ar9170_update_frame_filter(ar); + if (err) + goto unlock; + + err = ar9170_set_operating_mode(ar); + +unlock: + mutex_unlock(&ar->mutex); + return err; +} + +static void ar9170_op_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct ar9170 *ar = hw->priv; + + mutex_lock(&ar->mutex); + ar->vif = NULL; + ar->want_filter = 0; + ar9170_update_frame_filter(ar); + ar9170_set_beacon_timers(ar); + dev_kfree_skb(ar->beacon); + ar->beacon = NULL; + ar->sniffer_enabled = false; + ar->rx_software_decryption = false; + ar9170_set_operating_mode(ar); + mutex_unlock(&ar->mutex); +} + +static int ar9170_op_config(struct ieee80211_hw *hw, u32 changed) +{ + struct ar9170 *ar = hw->priv; + int err = 0; + + mutex_lock(&ar->mutex); + + if (changed & IEEE80211_CONF_CHANGE_RADIO_ENABLED) { + /* TODO */ + err = 0; + } + + if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) { + /* TODO */ + err = 0; + } + + if (changed & IEEE80211_CONF_CHANGE_PS) { + /* TODO */ + err = 0; + } + + if (changed & IEEE80211_CONF_CHANGE_POWER) { + /* TODO */ + err = 0; + } + + if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) { + /* + * is it long_frame_max_tx_count or short_frame_max_tx_count? + */ + + err = ar9170_set_hwretry_limit(ar, + ar->hw->conf.long_frame_max_tx_count); + if (err) + goto out; + } + + if (changed & IEEE80211_CONF_CHANGE_BEACON_INTERVAL) { + err = ar9170_set_beacon_timers(ar); + if (err) + goto out; + } + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + err = ar9170_set_channel(ar, hw->conf.channel, + AR9170_RFI_NONE, AR9170_BW_20); + if (err) + goto out; + /* adjust slot time for 5 GHz */ + if (hw->conf.channel->band == IEEE80211_BAND_5GHZ) + err = ar9170_write_reg(ar, AR9170_MAC_REG_SLOT_TIME, + 9 << 10); + } + +out: + mutex_unlock(&ar->mutex); + return err; +} + +static int ar9170_op_config_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_if_conf *conf) +{ + struct ar9170 *ar = hw->priv; + int err = 0; + + mutex_lock(&ar->mutex); + + if (conf->changed & IEEE80211_IFCC_BSSID) { + memcpy(ar->bssid, conf->bssid, ETH_ALEN); + err = ar9170_set_operating_mode(ar); + } + + if (conf->changed & IEEE80211_IFCC_BEACON) { + err = ar9170_update_beacon(ar); + + if (err) + goto out; + err = ar9170_set_beacon_timers(ar); + } + +out: + mutex_unlock(&ar->mutex); + return err; +} + +static void ar9170_set_filters(struct work_struct *work) +{ + struct ar9170 *ar = container_of(work, struct ar9170, + filter_config_work); + int err; + + mutex_lock(&ar->mutex); + if (unlikely(!IS_STARTED(ar))) + goto unlock; + + if (ar->filter_changed & AR9170_FILTER_CHANGED_PROMISC) { + err = ar9170_set_operating_mode(ar); + if (err) + goto unlock; + } + + if (ar->filter_changed & AR9170_FILTER_CHANGED_MULTICAST) { + err = ar9170_update_multicast(ar); + if (err) + goto unlock; + } + + if (ar->filter_changed & AR9170_FILTER_CHANGED_FRAMEFILTER) + err = ar9170_update_frame_filter(ar); + +unlock: + mutex_unlock(&ar->mutex); +} + +static void ar9170_op_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *new_flags, + int mc_count, struct dev_mc_list *mclist) +{ + struct ar9170 *ar = hw->priv; + + /* mask supported flags */ + *new_flags &= FIF_ALLMULTI | FIF_CONTROL | FIF_BCN_PRBRESP_PROMISC | + FIF_PROMISC_IN_BSS; + + /* + * We can support more by setting the sniffer bit and + * then checking the error flags, later. + */ + + if (changed_flags & FIF_ALLMULTI) { + if (*new_flags & FIF_ALLMULTI) { + ar->want_mc_hash = ~0ULL; + } else { + u64 mchash; + int i; + + /* always get broadcast frames */ + mchash = 1ULL << (0xff>>2); + + for (i = 0; i < mc_count; i++) { + if (WARN_ON(!mclist)) + break; + mchash |= 1ULL << (mclist->dmi_addr[5] >> 2); + mclist = mclist->next; + } + ar->want_mc_hash = mchash; + } + ar->filter_changed |= AR9170_FILTER_CHANGED_MULTICAST; + } + + if (changed_flags & FIF_CONTROL) { + u32 filter = AR9170_MAC_REG_FTF_PSPOLL | + AR9170_MAC_REG_FTF_RTS | + AR9170_MAC_REG_FTF_CTS | + AR9170_MAC_REG_FTF_ACK | + AR9170_MAC_REG_FTF_CFE | + AR9170_MAC_REG_FTF_CFE_ACK; + + if (*new_flags & FIF_CONTROL) + ar->want_filter = ar->cur_filter | filter; + else + ar->want_filter = ar->cur_filter & ~filter; + + ar->filter_changed |= AR9170_FILTER_CHANGED_FRAMEFILTER; + } + + if (changed_flags & FIF_PROMISC_IN_BSS) { + ar->sniffer_enabled = ((*new_flags) & FIF_PROMISC_IN_BSS) != 0; + ar->filter_changed |= AR9170_FILTER_CHANGED_PROMISC; + } + + if (likely(IS_STARTED(ar))) + queue_work(ar->hw->workqueue, &ar->filter_config_work); +} + +static void ar9170_op_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changed) +{ + struct ar9170 *ar = hw->priv; + int err = 0; + + mutex_lock(&ar->mutex); + + ar9170_regwrite_begin(ar); + + if (changed & BSS_CHANGED_ASSOC) { + ar->state = bss_conf->assoc ? AR9170_ASSOCIATED : ar->state; + +#ifndef CONFIG_AR9170_LEDS + /* enable assoc LED. */ + err = ar9170_set_leds_state(ar, bss_conf->assoc ? 2 : 0); +#endif /* CONFIG_AR9170_LEDS */ + } + + if (changed & BSS_CHANGED_HT) { + /* TODO */ + err = 0; + } + + if (changed & BSS_CHANGED_ERP_SLOT) { + u32 slottime = 20; + + if (bss_conf->use_short_slot) + slottime = 9; + + ar9170_regwrite(AR9170_MAC_REG_SLOT_TIME, slottime << 10); + } + + if (changed & BSS_CHANGED_BASIC_RATES) { + u32 cck, ofdm; + + if (hw->conf.channel->band == IEEE80211_BAND_5GHZ) { + ofdm = bss_conf->basic_rates; + cck = 0; + } else { + /* four cck rates */ + cck = bss_conf->basic_rates & 0xf; + ofdm = bss_conf->basic_rates >> 4; + } + ar9170_regwrite(AR9170_MAC_REG_BASIC_RATE, + ofdm << 8 | cck); + } + + ar9170_regwrite_finish(); + err = ar9170_regwrite_result(); + mutex_unlock(&ar->mutex); +} + +static u64 ar9170_op_get_tsf(struct ieee80211_hw *hw) +{ + struct ar9170 *ar = hw->priv; + int err; + u32 tsf_low; + u32 tsf_high; + u64 tsf; + + mutex_lock(&ar->mutex); + err = ar9170_read_reg(ar, AR9170_MAC_REG_TSF_L, &tsf_low); + if (!err) + err = ar9170_read_reg(ar, AR9170_MAC_REG_TSF_H, &tsf_high); + mutex_unlock(&ar->mutex); + + if (WARN_ON(err)) + return 0; + + tsf = tsf_high; + tsf = (tsf << 32) | tsf_low; + return tsf; +} + +static int ar9170_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 ar9170 *ar = hw->priv; + int err = 0, i; + u8 ktype; + + if ((!ar->vif) || (ar->disable_offload)) + return -EOPNOTSUPP; + + switch (key->alg) { + case ALG_WEP: + if (key->keylen == LEN_WEP40) + ktype = AR9170_ENC_ALG_WEP64; + else + ktype = AR9170_ENC_ALG_WEP128; + break; + case ALG_TKIP: + ktype = AR9170_ENC_ALG_TKIP; + break; + case ALG_CCMP: + ktype = AR9170_ENC_ALG_AESCCMP; + break; + default: + return -EOPNOTSUPP; + } + + mutex_lock(&ar->mutex); + if (cmd == SET_KEY) { + if (unlikely(!IS_STARTED(ar))) { + err = -EOPNOTSUPP; + goto out; + } + + /* group keys need all-zeroes address */ + if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + sta = NULL; + + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) { + for (i = 0; i < 64; i++) + if (!(ar->usedkeys & BIT(i))) + break; + if (i == 64) { + ar->rx_software_decryption = true; + ar9170_set_operating_mode(ar); + err = -ENOSPC; + goto out; + } + } else { + i = 64 + key->keyidx; + } + + key->hw_key_idx = i; + + err = ar9170_upload_key(ar, i, sta ? sta->addr : NULL, ktype, 0, + key->key, min_t(u8, 16, key->keylen)); + if (err) + goto out; + + if (key->alg == ALG_TKIP) { + err = ar9170_upload_key(ar, i, sta ? sta->addr : NULL, + ktype, 1, key->key + 16, 16); + if (err) + goto out; + + /* + * hardware is not capable generating the MMIC + * for fragmented frames! + */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; + } + + if (i < 64) + ar->usedkeys |= BIT(i); + + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + } else { + if (unlikely(!IS_STARTED(ar))) { + /* The device is gone... together with the key ;-) */ + err = 0; + goto out; + } + + err = ar9170_disable_key(ar, key->hw_key_idx); + if (err) + goto out; + + if (key->hw_key_idx < 64) { + ar->usedkeys &= ~BIT(key->hw_key_idx); + } else { + err = ar9170_upload_key(ar, key->hw_key_idx, NULL, + AR9170_ENC_ALG_NONE, 0, + NULL, 0); + if (err) + goto out; + + if (key->alg == ALG_TKIP) { + err = ar9170_upload_key(ar, key->hw_key_idx, + NULL, + AR9170_ENC_ALG_NONE, 1, + NULL, 0); + if (err) + goto out; + } + + } + } + + ar9170_regwrite_begin(ar); + ar9170_regwrite(AR9170_MAC_REG_ROLL_CALL_TBL_L, ar->usedkeys); + ar9170_regwrite(AR9170_MAC_REG_ROLL_CALL_TBL_H, ar->usedkeys >> 32); + ar9170_regwrite_finish(); + err = ar9170_regwrite_result(); + +out: + mutex_unlock(&ar->mutex); + + return err; +} + +static void ar9170_sta_notify(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum sta_notify_cmd cmd, + struct ieee80211_sta *sta) +{ + struct ar9170 *ar = hw->priv; + struct ar9170_sta_info *info = (void *) sta->drv_priv; + struct sk_buff *skb; + + switch (cmd) { + case STA_NOTIFY_ADD: + skb_queue_head_init(&info->tx_status); + + /* + * do we already have a frame that needs tx_status + * from this station in our queue? + * If so then transfer them to the new station queue. + */ + + /* preserve the correct order - check the waste bin first */ + while ((skb = ar9170_find_skb_in_queue_by_mac(ar, sta->addr, + &ar->global_tx_status_waste))) + skb_queue_tail(&info->tx_status, skb); + + /* now the still pending frames */ + while ((skb = ar9170_find_skb_in_queue_by_mac(ar, sta->addr, + &ar->global_tx_status))) + skb_queue_tail(&info->tx_status, skb); + +#ifdef AR9170_QUEUE_DEBUG + printk(KERN_DEBUG "%s: STA[%pM] has %d queued frames =>\n", + wiphy_name(ar->hw->wiphy), sta->addr, + skb_queue_len(&info->tx_status)); + + ar9170_dump_station_tx_status_queue(ar, &info->tx_status); +#endif /* AR9170_QUEUE_DEBUG */ + + + break; + + case STA_NOTIFY_REMOVE: + + /* + * transfer all outstanding frames that need a tx_status + * reports to the fallback queue + */ + + while ((skb = skb_dequeue(&ar->global_tx_status))) { +#ifdef AR9170_QUEUE_DEBUG + printk(KERN_DEBUG "%s: queueing frame in global " + "tx_status queue =>\n", + wiphy_name(ar->hw->wiphy)); + + ar9170_print_txheader(ar, skb); +#endif /* AR9170_QUEUE_DEBUG */ + skb_queue_tail(&ar->global_tx_status_waste, skb); + } + break; + + default: + break; + } +} + +static int ar9170_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + struct ar9170 *ar = hw->priv; + u32 val; + int err; + + mutex_lock(&ar->mutex); + err = ar9170_read_reg(ar, AR9170_MAC_REG_TX_RETRY, &val); + ar->stats.dot11ACKFailureCount += val; + + memcpy(stats, &ar->stats, sizeof(*stats)); + mutex_unlock(&ar->mutex); + + return 0; +} + +static int ar9170_get_tx_stats(struct ieee80211_hw *hw, + struct ieee80211_tx_queue_stats *tx_stats) +{ + struct ar9170 *ar = hw->priv; + + spin_lock_bh(&ar->tx_stats_lock); + memcpy(tx_stats, ar->tx_stats, sizeof(tx_stats[0]) * hw->queues); + spin_unlock_bh(&ar->tx_stats_lock); + + return 0; +} + +static int ar9170_conf_tx(struct ieee80211_hw *hw, u16 queue, + const struct ieee80211_tx_queue_params *param) +{ + struct ar9170 *ar = hw->priv; + int ret; + + mutex_lock(&ar->mutex); + if ((param) && !(queue > 4)) { + memcpy(&ar->edcf[ar9170_qos_hwmap[queue]], + param, sizeof(*param)); + + ret = ar9170_set_qos(ar); + } else + ret = -EINVAL; + + mutex_unlock(&ar->mutex); + return ret; +} + +static const struct ieee80211_ops ar9170_ops = { + .start = ar9170_op_start, + .stop = ar9170_op_stop, + .tx = ar9170_op_tx, + .add_interface = ar9170_op_add_interface, + .remove_interface = ar9170_op_remove_interface, + .config = ar9170_op_config, + .config_interface = ar9170_op_config_interface, + .configure_filter = ar9170_op_configure_filter, + .conf_tx = ar9170_conf_tx, + .bss_info_changed = ar9170_op_bss_info_changed, + .get_tsf = ar9170_op_get_tsf, + .set_key = ar9170_set_key, + .sta_notify = ar9170_sta_notify, + .get_stats = ar9170_get_stats, + .get_tx_stats = ar9170_get_tx_stats, +}; + +void *ar9170_alloc(size_t priv_size) +{ + struct ieee80211_hw *hw; + struct ar9170 *ar; + int i; + + hw = ieee80211_alloc_hw(priv_size, &ar9170_ops); + if (!hw) + return ERR_PTR(-ENOMEM); + + ar = hw->priv; + ar->hw = hw; + + mutex_init(&ar->mutex); + spin_lock_init(&ar->cmdlock); + spin_lock_init(&ar->tx_stats_lock); + skb_queue_head_init(&ar->global_tx_status); + skb_queue_head_init(&ar->global_tx_status_waste); + INIT_WORK(&ar->filter_config_work, ar9170_set_filters); + INIT_WORK(&ar->beacon_work, ar9170_new_beacon); + INIT_DELAYED_WORK(&ar->tx_status_janitor, ar9170_tx_status_janitor); + + /* all hw supports 2.4 GHz, so set channel to 1 by default */ + ar->channel = &ar9170_2ghz_chantable[0]; + + /* first part of wiphy init */ + ar->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_WDS) | + BIT(NL80211_IFTYPE_ADHOC); + ar->hw->flags |= IEEE80211_HW_RX_INCLUDES_FCS | + IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_NOISE_DBM; + + ar->hw->queues = 4; + ar->hw->extra_tx_headroom = 8; + ar->hw->sta_data_size = sizeof(struct ar9170_sta_info); + + ar->hw->max_rates = 1; + ar->hw->max_rate_tries = 3; + + for (i = 0; i < ARRAY_SIZE(ar->noise); i++) + ar->noise[i] = -95; /* ATH_DEFAULT_NOISE_FLOOR */ + + return ar; +} +EXPORT_SYMBOL_GPL(ar9170_alloc); + +static int ar9170_read_eeprom(struct ar9170 *ar) +{ +#define RW 8 /* number of words to read at once */ +#define RB (sizeof(u32) * RW) + DECLARE_MAC_BUF(mbuf); + u8 *eeprom = (void *)&ar->eeprom; + u8 *addr = ar->eeprom.mac_address; + __le32 offsets[RW]; + int i, j, err, bands = 0; + + BUILD_BUG_ON(sizeof(ar->eeprom) & 3); + + BUILD_BUG_ON(RB > AR9170_MAX_CMD_LEN - 4); +#ifndef __CHECKER__ + /* don't want to handle trailing remains */ + BUILD_BUG_ON(sizeof(ar->eeprom) % RB); +#endif + + for (i = 0; i < sizeof(ar->eeprom)/RB; i++) { + for (j = 0; j < RW; j++) + offsets[j] = cpu_to_le32(AR9170_EEPROM_START + + RB * i + 4 * j); + + err = ar->exec_cmd(ar, AR9170_CMD_RREG, + RB, (u8 *) &offsets, + RB, eeprom + RB * i); + if (err) + return err; + } + +#undef RW +#undef RB + + if (ar->eeprom.length == cpu_to_le16(0xFFFF)) + return -ENODATA; + + if (ar->eeprom.operating_flags & AR9170_OPFLAG_2GHZ) { + ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &ar9170_band_2GHz; + bands++; + } + if (ar->eeprom.operating_flags & AR9170_OPFLAG_5GHZ) { + ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &ar9170_band_5GHz; + bands++; + } + /* + * I measured this, a bandswitch takes roughly + * 135 ms and a frequency switch about 80. + * + * FIXME: measure these values again once EEPROM settings + * are used, that will influence them! + */ + if (bands == 2) + ar->hw->channel_change_time = 135 * 1000; + else + ar->hw->channel_change_time = 80 * 1000; + + /* second part of wiphy init */ + SET_IEEE80211_PERM_ADDR(ar->hw, addr); + + return bands ? 0 : -EINVAL; +} + +int ar9170_register(struct ar9170 *ar, struct device *pdev) +{ + int err; + + /* try to read EEPROM, init MAC addr */ + err = ar9170_read_eeprom(ar); + if (err) + goto err_out; + + err = ieee80211_register_hw(ar->hw); + if (err) + goto err_out; + + err = ar9170_init_leds(ar); + if (err) + goto err_unreg; + +#ifdef CONFIG_AR9170_LEDS + err = ar9170_register_leds(ar); + if (err) + goto err_unreg; +#endif /* CONFIG_AR9170_LEDS */ + + dev_info(pdev, "Atheros AR9170 is registered as '%s'\n", + wiphy_name(ar->hw->wiphy)); + + return err; + +err_unreg: + ieee80211_unregister_hw(ar->hw); + +err_out: + return err; +} +EXPORT_SYMBOL_GPL(ar9170_register); + +void ar9170_unregister(struct ar9170 *ar) +{ +#ifdef CONFIG_AR9170_LEDS + ar9170_unregister_leds(ar); +#endif /* CONFIG_AR9170_LEDS */ + + ieee80211_unregister_hw(ar->hw); + mutex_destroy(&ar->mutex); +} +EXPORT_SYMBOL_GPL(ar9170_unregister); + +static int __init ar9170_init(void) +{ + if (modparam_nohwcrypt) + printk(KERN_INFO "ar9170: cryptographic acceleration " + "disabled.\n"); + + return 0; +} + +static void __exit ar9170_exit(void) +{ + +} + +module_init(ar9170_init); +module_exit(ar9170_exit); -- cgit v1.2.3 From 6cb19353535f9f02fc2a753e3261a255406ba8fa Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sun, 22 Mar 2009 02:36:06 +0100 Subject: ar9170: mac/bbp and phy code MAC/BBP and PHY programming code Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ar9170/mac.c | 452 ++++++++++++++ drivers/net/wireless/ar9170/phy.c | 1240 +++++++++++++++++++++++++++++++++++++ 2 files changed, 1692 insertions(+) create mode 100644 drivers/net/wireless/ar9170/mac.c create mode 100644 drivers/net/wireless/ar9170/phy.c diff --git a/drivers/net/wireless/ar9170/mac.c b/drivers/net/wireless/ar9170/mac.c new file mode 100644 index 00000000000..c8fa3073169 --- /dev/null +++ b/drivers/net/wireless/ar9170/mac.c @@ -0,0 +1,452 @@ +/* + * Atheros AR9170 driver + * + * MAC programming + * + * Copyright 2008, Johannes Berg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 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 "ar9170.h" +#include "cmd.h" + +int ar9170_set_qos(struct ar9170 *ar) +{ + ar9170_regwrite_begin(ar); + + ar9170_regwrite(AR9170_MAC_REG_AC0_CW, ar->edcf[0].cw_min | + (ar->edcf[0].cw_max << 16)); + ar9170_regwrite(AR9170_MAC_REG_AC1_CW, ar->edcf[1].cw_min | + (ar->edcf[1].cw_max << 16)); + ar9170_regwrite(AR9170_MAC_REG_AC2_CW, ar->edcf[2].cw_min | + (ar->edcf[2].cw_max << 16)); + ar9170_regwrite(AR9170_MAC_REG_AC3_CW, ar->edcf[3].cw_min | + (ar->edcf[3].cw_max << 16)); + ar9170_regwrite(AR9170_MAC_REG_AC4_CW, ar->edcf[4].cw_min | + (ar->edcf[4].cw_max << 16)); + + ar9170_regwrite(AR9170_MAC_REG_AC1_AC0_AIFS, + ((ar->edcf[0].aifs * 9 + 10)) | + ((ar->edcf[1].aifs * 9 + 10) << 12) | + ((ar->edcf[2].aifs * 9 + 10) << 24)); + ar9170_regwrite(AR9170_MAC_REG_AC3_AC2_AIFS, + ((ar->edcf[2].aifs * 9 + 10) >> 8) | + ((ar->edcf[3].aifs * 9 + 10) << 4) | + ((ar->edcf[4].aifs * 9 + 10) << 16)); + + ar9170_regwrite(AR9170_MAC_REG_AC1_AC0_TXOP, + ar->edcf[0].txop | ar->edcf[1].txop << 16); + ar9170_regwrite(AR9170_MAC_REG_AC3_AC2_TXOP, + ar->edcf[1].txop | ar->edcf[3].txop << 16); + + ar9170_regwrite_finish(); + + return ar9170_regwrite_result(); +} + +int ar9170_init_mac(struct ar9170 *ar) +{ + ar9170_regwrite_begin(ar); + + ar9170_regwrite(AR9170_MAC_REG_ACK_EXTENSION, 0x40); + + ar9170_regwrite(AR9170_MAC_REG_RETRY_MAX, 0); + + /* enable MMIC */ + ar9170_regwrite(AR9170_MAC_REG_SNIFFER, + AR9170_MAC_REG_SNIFFER_DEFAULTS); + + ar9170_regwrite(AR9170_MAC_REG_RX_THRESHOLD, 0xc1f80); + + ar9170_regwrite(AR9170_MAC_REG_RX_PE_DELAY, 0x70); + ar9170_regwrite(AR9170_MAC_REG_EIFS_AND_SIFS, 0xa144000); + ar9170_regwrite(AR9170_MAC_REG_SLOT_TIME, 9 << 10); + + /* CF-END mode */ + ar9170_regwrite(0x1c3b2c, 0x19000000); + + /* NAV protects ACK only (in TXOP) */ + ar9170_regwrite(0x1c3b38, 0x201); + + /* Set Beacon PHY CTRL's TPC to 0x7, TA1=1 */ + /* OTUS set AM to 0x1 */ + ar9170_regwrite(AR9170_MAC_REG_BCN_HT1, 0x8000170); + + ar9170_regwrite(AR9170_MAC_REG_BACKOFF_PROTECT, 0x105); + + /* AGG test code*/ + /* Aggregation MAX number and timeout */ + ar9170_regwrite(0x1c3b9c, 0x10000a); + + ar9170_regwrite(AR9170_MAC_REG_FRAMETYPE_FILTER, + AR9170_MAC_REG_FTF_DEFAULTS); + + /* Enable deaggregator, response in sniffer mode */ + ar9170_regwrite(0x1c3c40, 0x1 | 1<<30); + + /* rate sets */ + ar9170_regwrite(AR9170_MAC_REG_BASIC_RATE, 0x150f); + ar9170_regwrite(AR9170_MAC_REG_MANDATORY_RATE, 0x150f); + ar9170_regwrite(AR9170_MAC_REG_RTS_CTS_RATE, 0x10b01bb); + + /* MIMO response control */ + ar9170_regwrite(0x1c3694, 0x4003C1E);/* bit 26~28 otus-AM */ + + /* switch MAC to OTUS interface */ + ar9170_regwrite(0x1c3600, 0x3); + + ar9170_regwrite(AR9170_MAC_REG_AMPDU_RX_THRESH, 0xffff); + + /* set PHY register read timeout (??) */ + ar9170_regwrite(AR9170_MAC_REG_MISC_680, 0xf00008); + + /* Disable Rx TimeOut, workaround for BB. */ + ar9170_regwrite(AR9170_MAC_REG_RX_TIMEOUT, 0x0); + + /* Set CPU clock frequency to 88/80MHz */ + ar9170_regwrite(AR9170_PWR_REG_CLOCK_SEL, + AR9170_PWR_CLK_AHB_80_88MHZ | + AR9170_PWR_CLK_DAC_160_INV_DLY); + + /* Set WLAN DMA interrupt mode: generate int per packet */ + ar9170_regwrite(AR9170_MAC_REG_TXRX_MPI, 0x110011); + + ar9170_regwrite(AR9170_MAC_REG_FCS_SELECT, + AR9170_MAC_FCS_FIFO_PROT); + + /* Disables the CF_END frame, undocumented register */ + ar9170_regwrite(AR9170_MAC_REG_TXOP_NOT_ENOUGH_IND, + 0x141E0F48); + + ar9170_regwrite_finish(); + + return ar9170_regwrite_result(); +} + +static int ar9170_set_mac_reg(struct ar9170 *ar, const u32 reg, const u8 *mac) +{ + static const u8 zero[ETH_ALEN] = { 0 }; + + if (!mac) + mac = zero; + + ar9170_regwrite_begin(ar); + + ar9170_regwrite(reg, + (mac[3] << 24) | (mac[2] << 16) | + (mac[1] << 8) | mac[0]); + + ar9170_regwrite(reg + 4, (mac[5] << 8) | mac[4]); + + ar9170_regwrite_finish(); + + return ar9170_regwrite_result(); +} + +int ar9170_update_multicast(struct ar9170 *ar) +{ + int err; + + ar9170_regwrite_begin(ar); + ar9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_H, + ar->want_mc_hash >> 32); + ar9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_L, + ar->want_mc_hash); + + ar9170_regwrite_finish(); + err = ar9170_regwrite_result(); + + if (err) + return err; + + ar->cur_mc_hash = ar->want_mc_hash; + + return 0; +} + +int ar9170_update_frame_filter(struct ar9170 *ar) +{ + int err; + + err = ar9170_write_reg(ar, AR9170_MAC_REG_FRAMETYPE_FILTER, + ar->want_filter); + + if (err) + return err; + + ar->cur_filter = ar->want_filter; + + return 0; +} + +static int ar9170_set_promiscouous(struct ar9170 *ar) +{ + u32 encr_mode, sniffer; + int err; + + err = ar9170_read_reg(ar, AR9170_MAC_REG_SNIFFER, &sniffer); + if (err) + return err; + + err = ar9170_read_reg(ar, AR9170_MAC_REG_ENCRYPTION, &encr_mode); + if (err) + return err; + + if (ar->sniffer_enabled) { + sniffer |= AR9170_MAC_REG_SNIFFER_ENABLE_PROMISC; + + /* + * Rx decryption works in place. + * + * If we don't disable it, the hardware will render all + * encrypted frames which are encrypted with an unknown + * key useless. + */ + + encr_mode |= AR9170_MAC_REG_ENCRYPTION_RX_SOFTWARE; + ar->sniffer_enabled = true; + } else { + sniffer &= ~AR9170_MAC_REG_SNIFFER_ENABLE_PROMISC; + + if (ar->rx_software_decryption) + encr_mode |= AR9170_MAC_REG_ENCRYPTION_RX_SOFTWARE; + else + encr_mode &= ~AR9170_MAC_REG_ENCRYPTION_RX_SOFTWARE; + } + + ar9170_regwrite_begin(ar); + ar9170_regwrite(AR9170_MAC_REG_ENCRYPTION, encr_mode); + ar9170_regwrite(AR9170_MAC_REG_SNIFFER, sniffer); + ar9170_regwrite_finish(); + + return ar9170_regwrite_result(); +} + +int ar9170_set_operating_mode(struct ar9170 *ar) +{ + u32 pm_mode = AR9170_MAC_REG_POWERMGT_DEFAULTS; + u8 *mac_addr, *bssid; + int err; + + if (ar->vif) { + mac_addr = ar->mac_addr; + bssid = ar->bssid; + + switch (ar->vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_ADHOC: + pm_mode |= AR9170_MAC_REG_POWERMGT_IBSS; + break; +/* case NL80211_IFTYPE_AP: + pm_mode |= AR9170_MAC_REG_POWERMGT_AP; + break;*/ + case NL80211_IFTYPE_WDS: + pm_mode |= AR9170_MAC_REG_POWERMGT_AP_WDS; + break; + case NL80211_IFTYPE_MONITOR: + ar->sniffer_enabled = true; + ar->rx_software_decryption = true; + break; + default: + pm_mode |= AR9170_MAC_REG_POWERMGT_STA; + break; + } + } else { + mac_addr = NULL; + bssid = NULL; + } + + err = ar9170_set_mac_reg(ar, AR9170_MAC_REG_MAC_ADDR_L, mac_addr); + if (err) + return err; + + err = ar9170_set_mac_reg(ar, AR9170_MAC_REG_BSSID_L, bssid); + if (err) + return err; + + err = ar9170_set_promiscouous(ar); + if (err) + return err; + + ar9170_regwrite_begin(ar); + + ar9170_regwrite(AR9170_MAC_REG_POWERMANAGEMENT, pm_mode); + ar9170_regwrite_finish(); + + return ar9170_regwrite_result(); +} + +int ar9170_set_hwretry_limit(struct ar9170 *ar, unsigned int max_retry) +{ + u32 tmp = min_t(u32, 0x33333, max_retry * 0x11111); + + return ar9170_write_reg(ar, AR9170_MAC_REG_RETRY_MAX, tmp); +} + +int ar9170_set_beacon_timers(struct ar9170 *ar) +{ + u32 v = 0; + u32 pretbtt = 0; + + v |= ar->hw->conf.beacon_int; + + if (ar->vif) { + switch (ar->vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_ADHOC: + v |= BIT(25); + break; + case NL80211_IFTYPE_AP: + v |= BIT(24); + pretbtt = (ar->hw->conf.beacon_int - 6) << 16; + break; + default: + break; + } + + v |= ar->vif->bss_conf.dtim_period << 16; + } + + ar9170_regwrite_begin(ar); + + ar9170_regwrite(AR9170_MAC_REG_PRETBTT, pretbtt); + ar9170_regwrite(AR9170_MAC_REG_BCN_PERIOD, v); + ar9170_regwrite_finish(); + return ar9170_regwrite_result(); +} + +int ar9170_update_beacon(struct ar9170 *ar) +{ + struct sk_buff *skb; + __le32 *data, *old = NULL; + u32 word; + int i; + + skb = ieee80211_beacon_get(ar->hw, ar->vif); + if (!skb) + return -ENOMEM; + + data = (__le32 *)skb->data; + if (ar->beacon) + old = (__le32 *)ar->beacon->data; + + ar9170_regwrite_begin(ar); + for (i = 0; i < DIV_ROUND_UP(skb->len, 4); i++) { + /* + * XXX: This accesses beyond skb data for up + * to the last 3 bytes!! + */ + + if (old && (data[i] == old[i])) + continue; + + word = le32_to_cpu(data[i]); + ar9170_regwrite(AR9170_BEACON_BUFFER_ADDRESS + 4 * i, word); + } + + /* XXX: use skb->cb info */ + if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ) + ar9170_regwrite(AR9170_MAC_REG_BCN_PLCP, + ((skb->len + 4) << (3+16)) + 0x0400); + else + ar9170_regwrite(AR9170_MAC_REG_BCN_PLCP, + ((skb->len + 4) << (3+16)) + 0x0400); + + ar9170_regwrite(AR9170_MAC_REG_BCN_LENGTH, skb->len + 4); + ar9170_regwrite(AR9170_MAC_REG_BCN_ADDR, AR9170_BEACON_BUFFER_ADDRESS); + ar9170_regwrite(AR9170_MAC_REG_BCN_CTRL, 1); + + ar9170_regwrite_finish(); + + dev_kfree_skb(ar->beacon); + ar->beacon = skb; + + return ar9170_regwrite_result(); +} + +void ar9170_new_beacon(struct work_struct *work) +{ + struct ar9170 *ar = container_of(work, struct ar9170, + beacon_work); + struct sk_buff *skb; + + if (unlikely(!IS_STARTED(ar))) + return ; + + mutex_lock(&ar->mutex); + + if (!ar->vif) + goto out; + + ar9170_update_beacon(ar); + + rcu_read_lock(); + while ((skb = ieee80211_get_buffered_bc(ar->hw, ar->vif))) + ar9170_op_tx(ar->hw, skb); + + rcu_read_unlock(); + + out: + mutex_unlock(&ar->mutex); +} + +int ar9170_upload_key(struct ar9170 *ar, u8 id, const u8 *mac, u8 ktype, + u8 keyidx, u8 *keydata, int keylen) +{ + __le32 vals[7]; + static const u8 bcast[ETH_ALEN] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + u8 dummy; + + mac = mac ? : bcast; + + vals[0] = cpu_to_le32((keyidx << 16) + id); + vals[1] = cpu_to_le32(mac[1] << 24 | mac[0] << 16 | ktype); + vals[2] = cpu_to_le32(mac[5] << 24 | mac[4] << 16 | + mac[3] << 8 | mac[2]); + memset(&vals[3], 0, 16); + if (keydata) + memcpy(&vals[3], keydata, keylen); + + return ar->exec_cmd(ar, AR9170_CMD_EKEY, + sizeof(vals), (u8 *)vals, + 1, &dummy); +} + +int ar9170_disable_key(struct ar9170 *ar, u8 id) +{ + __le32 val = cpu_to_le32(id); + u8 dummy; + + return ar->exec_cmd(ar, AR9170_CMD_EKEY, + sizeof(val), (u8 *)&val, + 1, &dummy); +} diff --git a/drivers/net/wireless/ar9170/phy.c b/drivers/net/wireless/ar9170/phy.c new file mode 100644 index 00000000000..6ce20754b8e --- /dev/null +++ b/drivers/net/wireless/ar9170/phy.c @@ -0,0 +1,1240 @@ +/* + * Atheros AR9170 driver + * + * PHY and RF code + * + * Copyright 2008, Johannes Berg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 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 +#include "ar9170.h" +#include "cmd.h" + +static int ar9170_init_power_cal(struct ar9170 *ar) +{ + ar9170_regwrite_begin(ar); + + ar9170_regwrite(0x1bc000 + 0x993c, 0x7f); + ar9170_regwrite(0x1bc000 + 0x9934, 0x3f3f3f3f); + ar9170_regwrite(0x1bc000 + 0x9938, 0x3f3f3f3f); + ar9170_regwrite(0x1bc000 + 0xa234, 0x3f3f3f3f); + ar9170_regwrite(0x1bc000 + 0xa238, 0x3f3f3f3f); + ar9170_regwrite(0x1bc000 + 0xa38c, 0x3f3f3f3f); + ar9170_regwrite(0x1bc000 + 0xa390, 0x3f3f3f3f); + ar9170_regwrite(0x1bc000 + 0xa3cc, 0x3f3f3f3f); + ar9170_regwrite(0x1bc000 + 0xa3d0, 0x3f3f3f3f); + ar9170_regwrite(0x1bc000 + 0xa3d4, 0x3f3f3f3f); + + ar9170_regwrite_finish(); + return ar9170_regwrite_result(); +} + +struct ar9170_phy_init { + u32 reg, _5ghz_20, _5ghz_40, _2ghz_40, _2ghz_20; +}; + +static struct ar9170_phy_init ar5416_phy_init[] = { + { 0x1c5800, 0x00000007, 0x00000007, 0x00000007, 0x00000007, }, + { 0x1c5804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, }, + { 0x1c5808, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c580c, 0xad848e19, 0xad848e19, 0xad848e19, 0xad848e19, }, + { 0x1c5810, 0x7d14e000, 0x7d14e000, 0x7d14e000, 0x7d14e000, }, + { 0x1c5814, 0x9c0a9f6b, 0x9c0a9f6b, 0x9c0a9f6b, 0x9c0a9f6b, }, + { 0x1c5818, 0x00000090, 0x00000090, 0x00000090, 0x00000090, }, + { 0x1c581c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, }, + { 0x1c5824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, }, + { 0x1c5828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, }, + { 0x1c582c, 0x0000a000, 0x0000a000, 0x0000a000, 0x0000a000, }, + { 0x1c5830, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, }, + { 0x1c5838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, }, + { 0x1c583c, 0x00200400, 0x00200400, 0x00200400, 0x00200400, }, + { 0x1c5840, 0x206a002e, 0x206a002e, 0x206a002e, 0x206a002e, }, + { 0x1c5844, 0x1372161e, 0x13721c1e, 0x13721c24, 0x137216a4, }, + { 0x1c5848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, }, + { 0x1c584c, 0x1284233c, 0x1284233c, 0x1284233c, 0x1284233c, }, + { 0x1c5850, 0x6c48b4e4, 0x6c48b4e4, 0x6c48b0e4, 0x6c48b0e4, }, + { 0x1c5854, 0x00000859, 0x00000859, 0x00000859, 0x00000859, }, + { 0x1c5858, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, }, + { 0x1c585c, 0x31395c5e, 0x31395c5e, 0x31395c5e, 0x31395c5e, }, + { 0x1c5860, 0x0004dd10, 0x0004dd10, 0x0004dd20, 0x0004dd20, }, + { 0x1c5868, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190, }, + { 0x1c586c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, }, + { 0x1c5900, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5904, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5908, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c590c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5914, 0x000007d0, 0x000007d0, 0x00000898, 0x00000898, }, + { 0x1c5918, 0x00000118, 0x00000230, 0x00000268, 0x00000134, }, + { 0x1c591c, 0x10000fff, 0x10000fff, 0x10000fff, 0x10000fff, }, + { 0x1c5920, 0x0510081c, 0x0510081c, 0x0510001c, 0x0510001c, }, + { 0x1c5924, 0xd0058a15, 0xd0058a15, 0xd0058a15, 0xd0058a15, }, + { 0x1c5928, 0x00000001, 0x00000001, 0x00000001, 0x00000001, }, + { 0x1c592c, 0x00000004, 0x00000004, 0x00000004, 0x00000004, }, + { 0x1c5934, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c5938, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c593c, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, }, + { 0x1c5944, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020, }, + { 0x1c5948, 0x9280b212, 0x9280b212, 0x9280b212, 0x9280b212, }, + { 0x1c594c, 0x00020028, 0x00020028, 0x00020028, 0x00020028, }, + { 0x1c5954, 0x5d50e188, 0x5d50e188, 0x5d50e188, 0x5d50e188, }, + { 0x1c5958, 0x00081fff, 0x00081fff, 0x00081fff, 0x00081fff, }, + { 0x1c5960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, }, + { 0x1c5964, 0x00001120, 0x00001120, 0x00001120, 0x00001120, }, + { 0x1c5970, 0x190fb515, 0x190fb515, 0x190fb515, 0x190fb515, }, + { 0x1c5974, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5978, 0x00000001, 0x00000001, 0x00000001, 0x00000001, }, + { 0x1c597c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5980, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5984, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5988, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c598c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5990, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5994, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5998, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c599c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c59a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c59a4, 0x00000007, 0x00000007, 0x00000007, 0x00000007, }, + { 0x1c59a8, 0x001fff00, 0x001fff00, 0x001fff00, 0x001fff00, }, + { 0x1c59ac, 0x006f00c4, 0x006f00c4, 0x006f00c4, 0x006f00c4, }, + { 0x1c59b0, 0x03051000, 0x03051000, 0x03051000, 0x03051000, }, + { 0x1c59b4, 0x00000820, 0x00000820, 0x00000820, 0x00000820, }, + { 0x1c59c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be, }, + { 0x1c59c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, }, + { 0x1c59c8, 0x60f6532c, 0x60f6532c, 0x60f6532c, 0x60f6532c, }, + { 0x1c59cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, }, + { 0x1c59d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, }, + { 0x1c59d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c59d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c59dc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c59e0, 0x00000200, 0x00000200, 0x00000200, 0x00000200, }, + { 0x1c59e4, 0x64646464, 0x64646464, 0x64646464, 0x64646464, }, + { 0x1c59e8, 0x3c787878, 0x3c787878, 0x3c787878, 0x3c787878, }, + { 0x1c59ec, 0x000000aa, 0x000000aa, 0x000000aa, 0x000000aa, }, + { 0x1c59f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c59fc, 0x00001042, 0x00001042, 0x00001042, 0x00001042, }, + { 0x1c5a00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5a04, 0x00000040, 0x00000040, 0x00000040, 0x00000040, }, + { 0x1c5a08, 0x00000080, 0x00000080, 0x00000080, 0x00000080, }, + { 0x1c5a0c, 0x000001a1, 0x000001a1, 0x00000141, 0x00000141, }, + { 0x1c5a10, 0x000001e1, 0x000001e1, 0x00000181, 0x00000181, }, + { 0x1c5a14, 0x00000021, 0x00000021, 0x000001c1, 0x000001c1, }, + { 0x1c5a18, 0x00000061, 0x00000061, 0x00000001, 0x00000001, }, + { 0x1c5a1c, 0x00000168, 0x00000168, 0x00000041, 0x00000041, }, + { 0x1c5a20, 0x000001a8, 0x000001a8, 0x000001a8, 0x000001a8, }, + { 0x1c5a24, 0x000001e8, 0x000001e8, 0x000001e8, 0x000001e8, }, + { 0x1c5a28, 0x00000028, 0x00000028, 0x00000028, 0x00000028, }, + { 0x1c5a2c, 0x00000068, 0x00000068, 0x00000068, 0x00000068, }, + { 0x1c5a30, 0x00000189, 0x00000189, 0x000000a8, 0x000000a8, }, + { 0x1c5a34, 0x000001c9, 0x000001c9, 0x00000169, 0x00000169, }, + { 0x1c5a38, 0x00000009, 0x00000009, 0x000001a9, 0x000001a9, }, + { 0x1c5a3c, 0x00000049, 0x00000049, 0x000001e9, 0x000001e9, }, + { 0x1c5a40, 0x00000089, 0x00000089, 0x00000029, 0x00000029, }, + { 0x1c5a44, 0x00000170, 0x00000170, 0x00000069, 0x00000069, }, + { 0x1c5a48, 0x000001b0, 0x000001b0, 0x00000190, 0x00000190, }, + { 0x1c5a4c, 0x000001f0, 0x000001f0, 0x000001d0, 0x000001d0, }, + { 0x1c5a50, 0x00000030, 0x00000030, 0x00000010, 0x00000010, }, + { 0x1c5a54, 0x00000070, 0x00000070, 0x00000050, 0x00000050, }, + { 0x1c5a58, 0x00000191, 0x00000191, 0x00000090, 0x00000090, }, + { 0x1c5a5c, 0x000001d1, 0x000001d1, 0x00000151, 0x00000151, }, + { 0x1c5a60, 0x00000011, 0x00000011, 0x00000191, 0x00000191, }, + { 0x1c5a64, 0x00000051, 0x00000051, 0x000001d1, 0x000001d1, }, + { 0x1c5a68, 0x00000091, 0x00000091, 0x00000011, 0x00000011, }, + { 0x1c5a6c, 0x000001b8, 0x000001b8, 0x00000051, 0x00000051, }, + { 0x1c5a70, 0x000001f8, 0x000001f8, 0x00000198, 0x00000198, }, + { 0x1c5a74, 0x00000038, 0x00000038, 0x000001d8, 0x000001d8, }, + { 0x1c5a78, 0x00000078, 0x00000078, 0x00000018, 0x00000018, }, + { 0x1c5a7c, 0x00000199, 0x00000199, 0x00000058, 0x00000058, }, + { 0x1c5a80, 0x000001d9, 0x000001d9, 0x00000098, 0x00000098, }, + { 0x1c5a84, 0x00000019, 0x00000019, 0x00000159, 0x00000159, }, + { 0x1c5a88, 0x00000059, 0x00000059, 0x00000199, 0x00000199, }, + { 0x1c5a8c, 0x00000099, 0x00000099, 0x000001d9, 0x000001d9, }, + { 0x1c5a90, 0x000000d9, 0x000000d9, 0x00000019, 0x00000019, }, + { 0x1c5a94, 0x000000f9, 0x000000f9, 0x00000059, 0x00000059, }, + { 0x1c5a98, 0x000000f9, 0x000000f9, 0x00000099, 0x00000099, }, + { 0x1c5a9c, 0x000000f9, 0x000000f9, 0x000000d9, 0x000000d9, }, + { 0x1c5aa0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5aa4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5aa8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5aac, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ab0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ab4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ab8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5abc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ac0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ac4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ac8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5acc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ad0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ad4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ad8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5adc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ae0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ae4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5ae8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5aec, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5af0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5af4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5af8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5afc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, + { 0x1c5b00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5b04, 0x00000001, 0x00000001, 0x00000001, 0x00000001, }, + { 0x1c5b08, 0x00000002, 0x00000002, 0x00000002, 0x00000002, }, + { 0x1c5b0c, 0x00000003, 0x00000003, 0x00000003, 0x00000003, }, + { 0x1c5b10, 0x00000004, 0x00000004, 0x00000004, 0x00000004, }, + { 0x1c5b14, 0x00000005, 0x00000005, 0x00000005, 0x00000005, }, + { 0x1c5b18, 0x00000008, 0x00000008, 0x00000008, 0x00000008, }, + { 0x1c5b1c, 0x00000009, 0x00000009, 0x00000009, 0x00000009, }, + { 0x1c5b20, 0x0000000a, 0x0000000a, 0x0000000a, 0x0000000a, }, + { 0x1c5b24, 0x0000000b, 0x0000000b, 0x0000000b, 0x0000000b, }, + { 0x1c5b28, 0x0000000c, 0x0000000c, 0x0000000c, 0x0000000c, }, + { 0x1c5b2c, 0x0000000d, 0x0000000d, 0x0000000d, 0x0000000d, }, + { 0x1c5b30, 0x00000010, 0x00000010, 0x00000010, 0x00000010, }, + { 0x1c5b34, 0x00000011, 0x00000011, 0x00000011, 0x00000011, }, + { 0x1c5b38, 0x00000012, 0x00000012, 0x00000012, 0x00000012, }, + { 0x1c5b3c, 0x00000013, 0x00000013, 0x00000013, 0x00000013, }, + { 0x1c5b40, 0x00000014, 0x00000014, 0x00000014, 0x00000014, }, + { 0x1c5b44, 0x00000015, 0x00000015, 0x00000015, 0x00000015, }, + { 0x1c5b48, 0x00000018, 0x00000018, 0x00000018, 0x00000018, }, + { 0x1c5b4c, 0x00000019, 0x00000019, 0x00000019, 0x00000019, }, + { 0x1c5b50, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, }, + { 0x1c5b54, 0x0000001b, 0x0000001b, 0x0000001b, 0x0000001b, }, + { 0x1c5b58, 0x0000001c, 0x0000001c, 0x0000001c, 0x0000001c, }, + { 0x1c5b5c, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, }, + { 0x1c5b60, 0x00000020, 0x00000020, 0x00000020, 0x00000020, }, + { 0x1c5b64, 0x00000021, 0x00000021, 0x00000021, 0x00000021, }, + { 0x1c5b68, 0x00000022, 0x00000022, 0x00000022, 0x00000022, }, + { 0x1c5b6c, 0x00000023, 0x00000023, 0x00000023, 0x00000023, }, + { 0x1c5b70, 0x00000024, 0x00000024, 0x00000024, 0x00000024, }, + { 0x1c5b74, 0x00000025, 0x00000025, 0x00000025, 0x00000025, }, + { 0x1c5b78, 0x00000028, 0x00000028, 0x00000028, 0x00000028, }, + { 0x1c5b7c, 0x00000029, 0x00000029, 0x00000029, 0x00000029, }, + { 0x1c5b80, 0x0000002a, 0x0000002a, 0x0000002a, 0x0000002a, }, + { 0x1c5b84, 0x0000002b, 0x0000002b, 0x0000002b, 0x0000002b, }, + { 0x1c5b88, 0x0000002c, 0x0000002c, 0x0000002c, 0x0000002c, }, + { 0x1c5b8c, 0x0000002d, 0x0000002d, 0x0000002d, 0x0000002d, }, + { 0x1c5b90, 0x00000030, 0x00000030, 0x00000030, 0x00000030, }, + { 0x1c5b94, 0x00000031, 0x00000031, 0x00000031, 0x00000031, }, + { 0x1c5b98, 0x00000032, 0x00000032, 0x00000032, 0x00000032, }, + { 0x1c5b9c, 0x00000033, 0x00000033, 0x00000033, 0x00000033, }, + { 0x1c5ba0, 0x00000034, 0x00000034, 0x00000034, 0x00000034, }, + { 0x1c5ba4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5ba8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bac, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bb0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bb4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bb8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bbc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bc0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bc4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bc8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bcc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bd0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bd4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bd8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bdc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5be0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5be4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5be8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bec, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bf0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bf4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, + { 0x1c5bf8, 0x00000010, 0x00000010, 0x00000010, 0x00000010, }, + { 0x1c5bfc, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, }, + { 0x1c5c00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c0c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c10, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c14, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c18, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c1c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c24, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c28, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c2c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c30, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c34, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c38, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5c3c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5cf0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5cf4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5cf8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c5cfc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6200, 0x00000008, 0x00000008, 0x0000000e, 0x0000000e, }, + { 0x1c6204, 0x00000440, 0x00000440, 0x00000440, 0x00000440, }, + { 0x1c6208, 0xd6be4788, 0xd6be4788, 0xd03e4788, 0xd03e4788, }, + { 0x1c620c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, }, + { 0x1c6210, 0x40806333, 0x40806333, 0x40806333, 0x40806333, }, + { 0x1c6214, 0x00106c10, 0x00106c10, 0x00106c10, 0x00106c10, }, + { 0x1c6218, 0x009c4060, 0x009c4060, 0x009c4060, 0x009c4060, }, + { 0x1c621c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, }, + { 0x1c6220, 0x018830c6, 0x018830c6, 0x018830c6, 0x018830c6, }, + { 0x1c6224, 0x00000400, 0x00000400, 0x00000400, 0x00000400, }, + { 0x1c6228, 0x000009b5, 0x000009b5, 0x000009b5, 0x000009b5, }, + { 0x1c622c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6230, 0x00000108, 0x00000210, 0x00000210, 0x00000108, }, + { 0x1c6234, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c6238, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c623c, 0x13c889af, 0x13c889af, 0x13c889af, 0x13c889af, }, + { 0x1c6240, 0x38490a20, 0x38490a20, 0x38490a20, 0x38490a20, }, + { 0x1c6244, 0x00007bb6, 0x00007bb6, 0x00007bb6, 0x00007bb6, }, + { 0x1c6248, 0x0fff3ffc, 0x0fff3ffc, 0x0fff3ffc, 0x0fff3ffc, }, + { 0x1c624c, 0x00000001, 0x00000001, 0x00000001, 0x00000001, }, + { 0x1c6250, 0x0000a000, 0x0000a000, 0x0000a000, 0x0000a000, }, + { 0x1c6254, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6258, 0x0cc75380, 0x0cc75380, 0x0cc75380, 0x0cc75380, }, + { 0x1c625c, 0x0f0f0f01, 0x0f0f0f01, 0x0f0f0f01, 0x0f0f0f01, }, + { 0x1c6260, 0xdfa91f01, 0xdfa91f01, 0xdfa91f01, 0xdfa91f01, }, + { 0x1c6264, 0x00418a11, 0x00418a11, 0x00418a11, 0x00418a11, }, + { 0x1c6268, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c626c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, }, + { 0x1c6274, 0x0a1a9caa, 0x0a1a9caa, 0x0a1a7caa, 0x0a1a7caa, }, + { 0x1c6278, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, }, + { 0x1c627c, 0x051701ce, 0x051701ce, 0x051701ce, 0x051701ce, }, + { 0x1c6300, 0x18010000, 0x18010000, 0x18010000, 0x18010000, }, + { 0x1c6304, 0x30032602, 0x30032602, 0x2e032402, 0x2e032402, }, + { 0x1c6308, 0x48073e06, 0x48073e06, 0x4a0a3c06, 0x4a0a3c06, }, + { 0x1c630c, 0x560b4c0a, 0x560b4c0a, 0x621a540b, 0x621a540b, }, + { 0x1c6310, 0x641a600f, 0x641a600f, 0x764f6c1b, 0x764f6c1b, }, + { 0x1c6314, 0x7a4f6e1b, 0x7a4f6e1b, 0x845b7a5a, 0x845b7a5a, }, + { 0x1c6318, 0x8c5b7e5a, 0x8c5b7e5a, 0x950f8ccf, 0x950f8ccf, }, + { 0x1c631c, 0x9d0f96cf, 0x9d0f96cf, 0xa5cf9b4f, 0xa5cf9b4f, }, + { 0x1c6320, 0xb51fa69f, 0xb51fa69f, 0xbddfaf1f, 0xbddfaf1f, }, + { 0x1c6324, 0xcb3fbd07, 0xcb3fbcbf, 0xd1ffc93f, 0xd1ffc93f, }, + { 0x1c6328, 0x0000d7bf, 0x0000d7bf, 0x00000000, 0x00000000, }, + { 0x1c632c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6330, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6334, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6338, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c633c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6340, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6344, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c6348, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, }, + { 0x1c634c, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, }, + { 0x1c6350, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, }, + { 0x1c6354, 0x0003ffff, 0x0003ffff, 0x0003ffff, 0x0003ffff, }, + { 0x1c6358, 0x79a8aa1f, 0x79a8aa1f, 0x79a8aa1f, 0x79a8aa1f, }, + { 0x1c6388, 0x08000000, 0x08000000, 0x08000000, 0x08000000, }, + { 0x1c638c, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c6390, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c6394, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, }, + { 0x1c6398, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce, }, + { 0x1c639c, 0x00000007, 0x00000007, 0x00000007, 0x00000007, }, + { 0x1c63a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63a4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63a8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63ac, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63b0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63b4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63b8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63bc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63c0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63c4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63c8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63cc, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c63d0, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c63d4, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, + { 0x1c63d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, + { 0x1c63dc, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, }, + { 0x1c63e0, 0x000000c0, 0x000000c0, 0x000000c0, 0x000000c0, }, + { 0x1c6848, 0x00180a65, 0x00180a65, 0x00180a68, 0x00180a68, }, + { 0x1c6920, 0x0510001c, 0x0510001c, 0x0510001c, 0x0510001c, }, + { 0x1c6960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, }, + { 0x1c720c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, }, + { 0x1c726c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, }, + { 0x1c7848, 0x00180a65, 0x00180a65, 0x00180a68, 0x00180a68, }, + { 0x1c7920, 0x0510001c, 0x0510001c, 0x0510001c, 0x0510001c, }, + { 0x1c7960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, }, + { 0x1c820c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, }, + { 0x1c826c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, }, +/* { 0x1c8864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, }, */ + { 0x1c8864, 0x0001c600, 0x0001c600, 0x0001c600, 0x0001c600, }, + { 0x1c895c, 0x004b6a8e, 0x004b6a8e, 0x004b6a8e, 0x004b6a8e, }, + { 0x1c8968, 0x000003ce, 0x000003ce, 0x000003ce, 0x000003ce, }, + { 0x1c89bc, 0x00181400, 0x00181400, 0x00181400, 0x00181400, }, + { 0x1c9270, 0x00820820, 0x00820820, 0x00820820, 0x00820820, }, + { 0x1c935c, 0x066c420f, 0x066c420f, 0x066c420f, 0x066c420f, }, + { 0x1c9360, 0x0f282207, 0x0f282207, 0x0f282207, 0x0f282207, }, + { 0x1c9364, 0x17601685, 0x17601685, 0x17601685, 0x17601685, }, + { 0x1c9368, 0x1f801104, 0x1f801104, 0x1f801104, 0x1f801104, }, + { 0x1c936c, 0x37a00c03, 0x37a00c03, 0x37a00c03, 0x37a00c03, }, + { 0x1c9370, 0x3fc40883, 0x3fc40883, 0x3fc40883, 0x3fc40883, }, + { 0x1c9374, 0x57c00803, 0x57c00803, 0x57c00803, 0x57c00803, }, + { 0x1c9378, 0x5fd80682, 0x5fd80682, 0x5fd80682, 0x5fd80682, }, + { 0x1c937c, 0x7fe00482, 0x7fe00482, 0x7fe00482, 0x7fe00482, }, + { 0x1c9380, 0x7f3c7bba, 0x7f3c7bba, 0x7f3c7bba, 0x7f3c7bba, }, + { 0x1c9384, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, } +}; + +int ar9170_init_phy(struct ar9170 *ar, enum ieee80211_band band) +{ + int i, err; + u32 val; + bool is_2ghz = band == IEEE80211_BAND_2GHZ; + bool is_40mhz = false; /* XXX: for now */ + + ar9170_regwrite_begin(ar); + + for (i = 0; i < ARRAY_SIZE(ar5416_phy_init); i++) { + if (is_40mhz) { + if (is_2ghz) + val = ar5416_phy_init[i]._2ghz_40; + else + val = ar5416_phy_init[i]._5ghz_40; + } else { + if (is_2ghz) + val = ar5416_phy_init[i]._2ghz_20; + else + val = ar5416_phy_init[i]._5ghz_20; + } + + ar9170_regwrite(ar5416_phy_init[i].reg, val); + } + + ar9170_regwrite_finish(); + err = ar9170_regwrite_result(); + if (err) + return err; + + /* XXX: use EEPROM data here! */ + + err = ar9170_init_power_cal(ar); + if (err) + return err; + + /* XXX: remove magic! */ + if (is_2ghz) + err = ar9170_write_reg(ar, 0x1d4014, 0x5163); + else + err = ar9170_write_reg(ar, 0x1d4014, 0x5143); + + return err; +} + +struct ar9170_rf_init { + u32 reg, _5ghz, _2ghz; +}; + +static struct ar9170_rf_init ar9170_rf_init[] = { + /* bank 0 */ + { 0x1c58b0, 0x1e5795e5, 0x1e5795e5}, + { 0x1c58e0, 0x02008020, 0x02008020}, + /* bank 1 */ + { 0x1c58b0, 0x02108421, 0x02108421}, + { 0x1c58ec, 0x00000008, 0x00000008}, + /* bank 2 */ + { 0x1c58b0, 0x0e73ff17, 0x0e73ff17}, + { 0x1c58e0, 0x00000420, 0x00000420}, + /* bank 3 */ + { 0x1c58f0, 0x01400018, 0x01c00018}, + /* bank 4 */ + { 0x1c58b0, 0x000001a1, 0x000001a1}, + { 0x1c58e8, 0x00000001, 0x00000001}, + /* bank 5 */ + { 0x1c58b0, 0x00000013, 0x00000013}, + { 0x1c58e4, 0x00000002, 0x00000002}, + /* bank 6 */ + { 0x1c58b0, 0x00000000, 0x00000000}, + { 0x1c58b0, 0x00000000, 0x00000000}, + { 0x1c58b0, 0x00000000, 0x00000000}, + { 0x1c58b0, 0x00000000, 0x00000000}, + { 0x1c58b0, 0x00000000, 0x00000000}, + { 0x1c58b0, 0x00004000, 0x00004000}, + { 0x1c58b0, 0x00006c00, 0x00006c00}, + { 0x1c58b0, 0x00002c00, 0x00002c00}, + { 0x1c58b0, 0x00004800, 0x00004800}, + { 0x1c58b0, 0x00004000, 0x00004000}, + { 0x1c58b0, 0x00006000, 0x00006000}, + { 0x1c58b0, 0x00001000, 0x00001000}, + { 0x1c58b0, 0x00004000, 0x00004000}, + { 0x1c58b0, 0x00007c00, 0x00007c00}, + { 0x1c58b0, 0x00007c00, 0x00007c00}, + { 0x1c58b0, 0x00007c00, 0x00007c00}, + { 0x1c58b0, 0x00007c00, 0x00007c00}, + { 0x1c58b0, 0x00007c00, 0x00007c00}, + { 0x1c58b0, 0x00087c00, 0x00087c00}, + { 0x1c58b0, 0x00007c00, 0x00007c00}, + { 0x1c58b0, 0x00005400, 0x00005400}, + { 0x1c58b0, 0x00000c00, 0x00000c00}, + { 0x1c58b0, 0x00001800, 0x00001800}, + { 0x1c58b0, 0x00007c00, 0x00007c00}, + { 0x1c58b0, 0x00006c00, 0x00006c00}, + { 0x1c58b0, 0x00006c00, 0x00006c00}, + { 0x1c58b0, 0x00007c00, 0x00007c00}, + { 0x1c58b0, 0x00002c00, 0x00002c00}, + { 0x1c58b0, 0x00003c00, 0x00003c00}, + { 0x1c58b0, 0x00003800, 0x00003800}, + { 0x1c58b0, 0x00001c00, 0x00001c00}, + { 0x1c58b0, 0x00000800, 0x00000800}, + { 0x1c58b0, 0x00000408, 0x00000408}, + { 0x1c58b0, 0x00004c15, 0x00004c15}, + { 0x1c58b0, 0x00004188, 0x00004188}, + { 0x1c58b0, 0x0000201e, 0x0000201e}, + { 0x1c58b0, 0x00010408, 0x00010408}, + { 0x1c58b0, 0x00000801, 0x00000801}, + { 0x1c58b0, 0x00000c08, 0x00000c08}, + { 0x1c58b0, 0x0000181e, 0x0000181e}, + { 0x1c58b0, 0x00001016, 0x00001016}, + { 0x1c58b0, 0x00002800, 0x00002800}, + { 0x1c58b0, 0x00004010, 0x00004010}, + { 0x1c58b0, 0x0000081c, 0x0000081c}, + { 0x1c58b0, 0x00000115, 0x00000115}, + { 0x1c58b0, 0x00000015, 0x00000015}, + { 0x1c58b0, 0x00000066, 0x00000066}, + { 0x1c58b0, 0x0000001c, 0x0000001c}, + { 0x1c58b0, 0x00000000, 0x00000000}, + { 0x1c58b0, 0x00000004, 0x00000004}, + { 0x1c58b0, 0x00000015, 0x00000015}, + { 0x1c58b0, 0x0000001f, 0x0000001f}, + { 0x1c58e0, 0x00000000, 0x00000400}, + /* bank 7 */ + { 0x1c58b0, 0x000000a0, 0x000000a0}, + { 0x1c58b0, 0x00000000, 0x00000000}, + { 0x1c58b0, 0x00000040, 0x00000040}, + { 0x1c58f0, 0x0000001c, 0x0000001c}, +}; + +static int ar9170_init_rf_banks_0_7(struct ar9170 *ar, bool band5ghz) +{ + int err, i; + + ar9170_regwrite_begin(ar); + + for (i = 0; i < ARRAY_SIZE(ar9170_rf_init); i++) + ar9170_regwrite(ar9170_rf_init[i].reg, + band5ghz ? ar9170_rf_init[i]._5ghz + : ar9170_rf_init[i]._2ghz); + + ar9170_regwrite_finish(); + err = ar9170_regwrite_result(); + if (err) + printk(KERN_ERR "%s: rf init failed\n", + wiphy_name(ar->hw->wiphy)); + return err; +} + +static int ar9170_init_rf_bank4_pwr(struct ar9170 *ar, bool band5ghz, + u32 freq, enum ar9170_bw bw) +{ + int err; + u32 d0, d1, td0, td1, fd0, fd1; + u8 chansel; + u8 refsel0 = 1, refsel1 = 0; + u8 lf_synth = 0; + + switch (bw) { + case AR9170_BW_40_ABOVE: + freq += 10; + break; + case AR9170_BW_40_BELOW: + freq -= 10; + break; + case AR9170_BW_20: + break; + case __AR9170_NUM_BW: + BUG(); + } + + if (band5ghz) { + if (freq % 10) { + chansel = (freq - 4800) / 5; + } else { + chansel = ((freq - 4800) / 10) * 2; + refsel0 = 0; + refsel1 = 1; + } + chansel = byte_rev_table[chansel]; + } else { + if (freq == 2484) { + chansel = 10 + (freq - 2274) / 5; + lf_synth = 1; + } else + chansel = 16 + (freq - 2272) / 5; + chansel *= 4; + chansel = byte_rev_table[chansel]; + } + + d1 = chansel; + d0 = 0x21 | + refsel0 << 3 | + refsel1 << 2 | + lf_synth << 1; + td0 = d0 & 0x1f; + td1 = d1 & 0x1f; + fd0 = td1 << 5 | td0; + + td0 = (d0 >> 5) & 0x7; + td1 = (d1 >> 5) & 0x7; + fd1 = td1 << 5 | td0; + + ar9170_regwrite_begin(ar); + + ar9170_regwrite(0x1c58b0, fd0); + ar9170_regwrite(0x1c58e8, fd1); + + ar9170_regwrite_finish(); + err = ar9170_regwrite_result(); + if (err) + return err; + + msleep(10); + + return 0; +} + +struct ar9170_phy_freq_params { + u8 coeff_exp; + u16 coeff_man; + u8 coeff_exp_shgi; + u16 coeff_man_shgi; +}; + +struct ar9170_phy_freq_entry { + u16 freq; + struct ar9170_phy_freq_params params[__AR9170_NUM_BW]; +}; + +/* NB: must be in sync with channel tables in main! */ +static const struct ar9170_phy_freq_entry ar9170_phy_freq_params[] = { +/* + * freq, + * 20MHz, + * 40MHz (below), + * 40Mhz (above), + */ + { 2412, { + { 3, 21737, 3, 19563, }, + { 3, 21827, 3, 19644, }, + { 3, 21647, 3, 19482, }, + } }, + { 2417, { + { 3, 21692, 3, 19523, }, + { 3, 21782, 3, 19604, }, + { 3, 21602, 3, 19442, }, + } }, + { 2422, { + { 3, 21647, 3, 19482, }, + { 3, 21737, 3, 19563, }, + { 3, 21558, 3, 19402, }, + } }, + { 2427, { + { 3, 21602, 3, 19442, }, + { 3, 21692, 3, 19523, }, + { 3, 21514, 3, 19362, }, + } }, + { 2432, { + { 3, 21558, 3, 19402, }, + { 3, 21647, 3, 19482, }, + { 3, 21470, 3, 19323, }, + } }, + { 2437, { + { 3, 21514, 3, 19362, }, + { 3, 21602, 3, 19442, }, + { 3, 21426, 3, 19283, }, + } }, + { 2442, { + { 3, 21470, 3, 19323, }, + { 3, 21558, 3, 19402, }, + { 3, 21382, 3, 19244, }, + } }, + { 2447, { + { 3, 21426, 3, 19283, }, + { 3, 21514, 3, 19362, }, + { 3, 21339, 3, 19205, }, + } }, + { 2452, { + { 3, 21382, 3, 19244, }, + { 3, 21470, 3, 19323, }, + { 3, 21295, 3, 19166, }, + } }, + { 2457, { + { 3, 21339, 3, 19205, }, + { 3, 21426, 3, 19283, }, + { 3, 21252, 3, 19127, }, + } }, + { 2462, { + { 3, 21295, 3, 19166, }, + { 3, 21382, 3, 19244, }, + { 3, 21209, 3, 19088, }, + } }, + { 2467, { + { 3, 21252, 3, 19127, }, + { 3, 21339, 3, 19205, }, + { 3, 21166, 3, 19050, }, + } }, + { 2472, { + { 3, 21209, 3, 19088, }, + { 3, 21295, 3, 19166, }, + { 3, 21124, 3, 19011, }, + } }, + { 2484, { + { 3, 21107, 3, 18996, }, + { 3, 21192, 3, 19073, }, + { 3, 21022, 3, 18920, }, + } }, + { 4920, { + { 4, 21313, 4, 19181, }, + { 4, 21356, 4, 19220, }, + { 4, 21269, 4, 19142, }, + } }, + { 4940, { + { 4, 21226, 4, 19104, }, + { 4, 21269, 4, 19142, }, + { 4, 21183, 4, 19065, }, + } }, + { 4960, { + { 4, 21141, 4, 19027, }, + { 4, 21183, 4, 19065, }, + { 4, 21098, 4, 18988, }, + } }, + { 4980, { + { 4, 21056, 4, 18950, }, + { 4, 21098, 4, 18988, }, + { 4, 21014, 4, 18912, }, + } }, + { 5040, { + { 4, 20805, 4, 18725, }, + { 4, 20846, 4, 18762, }, + { 4, 20764, 4, 18687, }, + } }, + { 5060, { + { 4, 20723, 4, 18651, }, + { 4, 20764, 4, 18687, }, + { 4, 20682, 4, 18614, }, + } }, + { 5080, { + { 4, 20641, 4, 18577, }, + { 4, 20682, 4, 18614, }, + { 4, 20601, 4, 18541, }, + } }, + { 5180, { + { 4, 20243, 4, 18219, }, + { 4, 20282, 4, 18254, }, + { 4, 20204, 4, 18183, }, + } }, + { 5200, { + { 4, 20165, 4, 18148, }, + { 4, 20204, 4, 18183, }, + { 4, 20126, 4, 18114, }, + } }, + { 5220, { + { 4, 20088, 4, 18079, }, + { 4, 20126, 4, 18114, }, + { 4, 20049, 4, 18044, }, + } }, + { 5240, { + { 4, 20011, 4, 18010, }, + { 4, 20049, 4, 18044, }, + { 4, 19973, 4, 17976, }, + } }, + { 5260, { + { 4, 19935, 4, 17941, }, + { 4, 19973, 4, 17976, }, + { 4, 19897, 4, 17907, }, + } }, + { 5280, { + { 4, 19859, 4, 17873, }, + { 4, 19897, 4, 17907, }, + { 4, 19822, 4, 17840, }, + } }, + { 5300, { + { 4, 19784, 4, 17806, }, + { 4, 19822, 4, 17840, }, + { 4, 19747, 4, 17772, }, + } }, + { 5320, { + { 4, 19710, 4, 17739, }, + { 4, 19747, 4, 17772, }, + { 4, 19673, 4, 17706, }, + } }, + { 5500, { + { 4, 19065, 4, 17159, }, + { 4, 19100, 4, 17190, }, + { 4, 19030, 4, 17127, }, + } }, + { 5520, { + { 4, 18996, 4, 17096, }, + { 4, 19030, 4, 17127, }, + { 4, 18962, 4, 17065, }, + } }, + { 5540, { + { 4, 18927, 4, 17035, }, + { 4, 18962, 4, 17065, }, + { 4, 18893, 4, 17004, }, + } }, + { 5560, { + { 4, 18859, 4, 16973, }, + { 4, 18893, 4, 17004, }, + { 4, 18825, 4, 16943, }, + } }, + { 5580, { + { 4, 18792, 4, 16913, }, + { 4, 18825, 4, 16943, }, + { 4, 18758, 4, 16882, }, + } }, + { 5600, { + { 4, 18725, 4, 16852, }, + { 4, 18758, 4, 16882, }, + { 4, 18691, 4, 16822, }, + } }, + { 5620, { + { 4, 18658, 4, 16792, }, + { 4, 18691, 4, 16822, }, + { 4, 18625, 4, 16762, }, + } }, + { 5640, { + { 4, 18592, 4, 16733, }, + { 4, 18625, 4, 16762, }, + { 4, 18559, 4, 16703, }, + } }, + { 5660, { + { 4, 18526, 4, 16673, }, + { 4, 18559, 4, 16703, }, + { 4, 18493, 4, 16644, }, + } }, + { 5680, { + { 4, 18461, 4, 16615, }, + { 4, 18493, 4, 16644, }, + { 4, 18428, 4, 16586, }, + } }, + { 5700, { + { 4, 18396, 4, 16556, }, + { 4, 18428, 4, 16586, }, + { 4, 18364, 4, 16527, }, + } }, + { 5745, { + { 4, 18252, 4, 16427, }, + { 4, 18284, 4, 16455, }, + { 4, 18220, 4, 16398, }, + } }, + { 5765, { + { 4, 18189, 5, 32740, }, + { 4, 18220, 4, 16398, }, + { 4, 18157, 5, 32683, }, + } }, + { 5785, { + { 4, 18126, 5, 32626, }, + { 4, 18157, 5, 32683, }, + { 4, 18094, 5, 32570, }, + } }, + { 5805, { + { 4, 18063, 5, 32514, }, + { 4, 18094, 5, 32570, }, + { 4, 18032, 5, 32458, }, + } }, + { 5825, { + { 4, 18001, 5, 32402, }, + { 4, 18032, 5, 32458, }, + { 4, 17970, 5, 32347, }, + } }, + { 5170, { + { 4, 20282, 4, 18254, }, + { 4, 20321, 4, 18289, }, + { 4, 20243, 4, 18219, }, + } }, + { 5190, { + { 4, 20204, 4, 18183, }, + { 4, 20243, 4, 18219, }, + { 4, 20165, 4, 18148, }, + } }, + { 5210, { + { 4, 20126, 4, 18114, }, + { 4, 20165, 4, 18148, }, + { 4, 20088, 4, 18079, }, + } }, + { 5230, { + { 4, 20049, 4, 18044, }, + { 4, 20088, 4, 18079, }, + { 4, 20011, 4, 18010, }, + } }, +}; + +static const struct ar9170_phy_freq_params * +ar9170_get_hw_dyn_params(struct ieee80211_channel *channel, + enum ar9170_bw bw) +{ + unsigned int chanidx = 0; + u16 freq = 2412; + + if (channel) { + chanidx = channel->hw_value; + freq = channel->center_freq; + } + + BUG_ON(chanidx >= ARRAY_SIZE(ar9170_phy_freq_params)); + + BUILD_BUG_ON(__AR9170_NUM_BW != 3); + + WARN_ON(ar9170_phy_freq_params[chanidx].freq != freq); + + return &ar9170_phy_freq_params[chanidx].params[bw]; +} + + +int ar9170_init_rf(struct ar9170 *ar) +{ + const struct ar9170_phy_freq_params *freqpar; + __le32 cmd[7]; + int err; + + err = ar9170_init_rf_banks_0_7(ar, false); + if (err) + return err; + + err = ar9170_init_rf_bank4_pwr(ar, false, 2412, AR9170_BW_20); + if (err) + return err; + + freqpar = ar9170_get_hw_dyn_params(NULL, AR9170_BW_20); + + cmd[0] = cpu_to_le32(2412 * 1000); + cmd[1] = cpu_to_le32(0); + cmd[2] = cpu_to_le32(1); + cmd[3] = cpu_to_le32(freqpar->coeff_exp); + cmd[4] = cpu_to_le32(freqpar->coeff_man); + cmd[5] = cpu_to_le32(freqpar->coeff_exp_shgi); + cmd[6] = cpu_to_le32(freqpar->coeff_man_shgi); + + /* RF_INIT echoes the command back to us */ + err = ar->exec_cmd(ar, AR9170_CMD_RF_INIT, + sizeof(cmd), (u8 *)cmd, + sizeof(cmd), (u8 *)cmd); + if (err) + return err; + + msleep(1000); + + return ar9170_echo_test(ar, 0xaabbccdd); +} + +static int ar9170_find_freq_idx(int nfreqs, u8 *freqs, u8 f) +{ + int idx = nfreqs - 2; + + while (idx >= 0) { + if (f >= freqs[idx]) + return idx; + idx--; + } + + return 0; +} + +static s32 ar9170_interpolate_s32(s32 x, s32 x1, s32 y1, s32 x2, s32 y2) +{ + /* nothing to interpolate, it's horizontal */ + if (y2 == y1) + return y1; + + /* check if we hit one of the edges */ + if (x == x1) + return y1; + if (x == x2) + return y2; + + /* x1 == x2 is bad, hopefully == x */ + if (x2 == x1) + return y1; + + return y1 + (((y2 - y1) * (x - x1)) / (x2 - x1)); +} + +static u8 ar9170_interpolate_u8(u8 x, u8 x1, u8 y1, u8 x2, u8 y2) +{ +#define SHIFT 8 + s32 y; + + y = ar9170_interpolate_s32(x << SHIFT, + x1 << SHIFT, y1 << SHIFT, + x2 << SHIFT, y2 << SHIFT); + + /* + * XXX: unwrap this expression + * Isn't it just DIV_ROUND_UP(y, 1<> SHIFT) + ((y & (1<<(SHIFT-1))) >> (SHIFT - 1)); +#undef SHIFT +} + +static int ar9170_set_power_cal(struct ar9170 *ar, u32 freq, enum ar9170_bw bw) +{ + struct ar9170_calibration_target_power_legacy *ctpl; + struct ar9170_calibration_target_power_ht *ctph; + u8 *ctpres; + int ntargets; + int idx, i, n; + u8 ackpower, ackchains, f; + u8 pwr_freqs[AR5416_MAX_NUM_TGT_PWRS]; + + if (freq < 3000) + f = freq - 2300; + else + f = (freq - 4800)/5; + + /* + * cycle through the various modes + * + * legacy modes first: 5G, 2G CCK, 2G OFDM + */ + for (i = 0; i < 3; i++) { + switch (i) { + case 0: /* 5 GHz legacy */ + ctpl = &ar->eeprom.cal_tgt_pwr_5G[0]; + ntargets = AR5416_NUM_5G_TARGET_PWRS; + ctpres = ar->power_5G_leg; + break; + case 1: /* 2.4 GHz CCK */ + ctpl = &ar->eeprom.cal_tgt_pwr_2G_cck[0]; + ntargets = AR5416_NUM_2G_CCK_TARGET_PWRS; + ctpres = ar->power_2G_cck; + break; + case 2: /* 2.4 GHz OFDM */ + ctpl = &ar->eeprom.cal_tgt_pwr_2G_ofdm[0]; + ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS; + ctpres = ar->power_2G_ofdm; + break; + default: + BUG(); + } + + for (n = 0; n < ntargets; n++) { + if (ctpl[n].freq == 0xff) + break; + pwr_freqs[n] = ctpl[n].freq; + } + ntargets = n; + idx = ar9170_find_freq_idx(ntargets, pwr_freqs, f); + for (n = 0; n < 4; n++) + ctpres[n] = ar9170_interpolate_u8( + f, + ctpl[idx + 0].freq, + ctpl[idx + 0].power[n], + ctpl[idx + 1].freq, + ctpl[idx + 1].power[n]); + } + + /* + * HT modes now: 5G HT20, 5G HT40, 2G CCK, 2G OFDM, 2G HT20, 2G HT40 + */ + for (i = 0; i < 4; i++) { + switch (i) { + case 0: /* 5 GHz HT 20 */ + ctph = &ar->eeprom.cal_tgt_pwr_5G_ht20[0]; + ntargets = AR5416_NUM_5G_TARGET_PWRS; + ctpres = ar->power_5G_ht20; + break; + case 1: /* 5 GHz HT 40 */ + ctph = &ar->eeprom.cal_tgt_pwr_5G_ht40[0]; + ntargets = AR5416_NUM_5G_TARGET_PWRS; + ctpres = ar->power_5G_ht40; + break; + case 2: /* 2.4 GHz HT 20 */ + ctph = &ar->eeprom.cal_tgt_pwr_2G_ht20[0]; + ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS; + ctpres = ar->power_2G_ht20; + break; + case 3: /* 2.4 GHz HT 40 */ + ctph = &ar->eeprom.cal_tgt_pwr_2G_ht40[0]; + ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS; + ctpres = ar->power_2G_ht40; + break; + default: + BUG(); + } + + for (n = 0; n < ntargets; n++) { + if (ctph[n].freq == 0xff) + break; + pwr_freqs[n] = ctph[n].freq; + } + ntargets = n; + idx = ar9170_find_freq_idx(ntargets, pwr_freqs, f); + for (n = 0; n < 8; n++) + ctpres[n] = ar9170_interpolate_u8( + f, + ctph[idx + 0].freq, + ctph[idx + 0].power[n], + ctph[idx + 1].freq, + ctph[idx + 1].power[n]); + } + + /* set ACK/CTS TX power */ + ar9170_regwrite_begin(ar); + + if (ar->eeprom.tx_mask != 1) + ackchains = AR9170_TX_PHY_TXCHAIN_2; + else + ackchains = AR9170_TX_PHY_TXCHAIN_1; + + if (freq < 3000) + ackpower = ar->power_2G_ofdm[0] & 0x3f; + else + ackpower = ar->power_5G_leg[0] & 0x3f; + + ar9170_regwrite(0x1c3694, ackpower << 20 | ackchains << 26); + ar9170_regwrite(0x1c3bb4, ackpower << 5 | ackchains << 11 | + ackpower << 21 | ackchains << 27); + + ar9170_regwrite_finish(); + return ar9170_regwrite_result(); +} + +static int ar9170_calc_noise_dbm(u32 raw_noise) +{ + if (raw_noise & 0x100) + return ~((raw_noise & 0x0ff) >> 1); + else + return (raw_noise & 0xff) >> 1; +} + +int ar9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel, + enum ar9170_rf_init_mode rfi, enum ar9170_bw bw) +{ + const struct ar9170_phy_freq_params *freqpar; + u32 cmd, tmp, offs; + __le32 vals[8]; + int i, err; + bool bandswitch; + + /* clear BB heavy clip enable */ + err = ar9170_write_reg(ar, 0x1c59e0, 0x200); + if (err) + return err; + + /* may be NULL at first setup */ + if (ar->channel) + bandswitch = ar->channel->band != channel->band; + else + bandswitch = true; + + /* HW workaround */ + if (!ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] && + channel->center_freq <= 2417) + bandswitch = true; + + err = ar->exec_cmd(ar, AR9170_CMD_FREQ_START, 0, NULL, 0, NULL); + if (err) + return err; + + if (rfi != AR9170_RFI_NONE || bandswitch) { + u32 val = 0x400; + + if (rfi == AR9170_RFI_COLD) + val = 0x800; + + /* warm/cold reset BB/ADDA */ + err = ar9170_write_reg(ar, 0x1d4004, val); + if (err) + return err; + + err = ar9170_write_reg(ar, 0x1d4004, 0x0); + if (err) + return err; + + err = ar9170_init_phy(ar, channel->band); + if (err) + return err; + + err = ar9170_init_rf_banks_0_7(ar, + channel->band == IEEE80211_BAND_5GHZ); + if (err) + return err; + + cmd = AR9170_CMD_RF_INIT; + } else { + cmd = AR9170_CMD_FREQUENCY; + } + + err = ar9170_init_rf_bank4_pwr(ar, + channel->band == IEEE80211_BAND_5GHZ, + channel->center_freq, bw); + if (err) + return err; + + switch (bw) { + case AR9170_BW_20: + tmp = 0x240; + offs = 0; + break; + case AR9170_BW_40_BELOW: + tmp = 0x2c4; + offs = 3; + break; + case AR9170_BW_40_ABOVE: + tmp = 0x2d4; + offs = 1; + break; + default: + BUG(); + return -ENOSYS; + } + + if (0 /* 2 streams capable */) + tmp |= 0x100; + + err = ar9170_write_reg(ar, 0x1c5804, tmp); + if (err) + return err; + + err = ar9170_set_power_cal(ar, channel->center_freq, bw); + if (err) + return err; + + freqpar = ar9170_get_hw_dyn_params(channel, bw); + + vals[0] = cpu_to_le32(channel->center_freq * 1000); + vals[1] = cpu_to_le32(bw == AR9170_BW_20 ? 0 : 1); + vals[2] = cpu_to_le32(offs << 2 | 1); + vals[3] = cpu_to_le32(freqpar->coeff_exp); + vals[4] = cpu_to_le32(freqpar->coeff_man); + vals[5] = cpu_to_le32(freqpar->coeff_exp_shgi); + vals[6] = cpu_to_le32(freqpar->coeff_man_shgi); + vals[7] = cpu_to_le32(1000); + + err = ar->exec_cmd(ar, cmd, sizeof(vals), (u8 *)vals, + sizeof(vals), (u8 *)vals); + if (err) + return err; + + for (i = 0; i < 2; i++) { + ar->noise[i] = ar9170_calc_noise_dbm( + (le32_to_cpu(vals[2 + i]) >> 19) & 0x1ff); + + ar->noise[i + 2] = ar9170_calc_noise_dbm( + (le32_to_cpu(vals[5 + i]) >> 23) & 0x1ff); + } + + ar->channel = channel; + return 0; +} -- cgit v1.2.3 From b63a2cb30405777033d58045c562a3b04d87d702 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 21 Mar 2009 23:05:48 +0100 Subject: ar9170: ar9170: USB frontend driver USB frontend driver code for Atheros' AR9170 modules. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ar9170/usb.c | 747 ++++++++++++++++++++++++++++++++++++++ drivers/net/wireless/ar9170/usb.h | 74 ++++ 2 files changed, 821 insertions(+) create mode 100644 drivers/net/wireless/ar9170/usb.c create mode 100644 drivers/net/wireless/ar9170/usb.h diff --git a/drivers/net/wireless/ar9170/usb.c b/drivers/net/wireless/ar9170/usb.c new file mode 100644 index 00000000000..ede511e4047 --- /dev/null +++ b/drivers/net/wireless/ar9170/usb.c @@ -0,0 +1,747 @@ +/* + * Atheros AR9170 driver + * + * USB - frontend + * + * Copyright 2008, Johannes Berg + * Copyright 2009, Christian Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 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 +#include +#include +#include +#include +#include "ar9170.h" +#include "cmd.h" +#include "hw.h" +#include "usb.h" + +MODULE_AUTHOR("Christian Lamparter "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("USB Driver for Atheros AR9170 based devices"); +MODULE_FIRMWARE("ar9170-1.fw"); +MODULE_FIRMWARE("ar9170-2.fw"); + +static struct usb_device_id ar9170_usb_ids[] = { + /* Atheros 9170 */ + { USB_DEVICE(0x0cf3, 0x9170) }, + /* Atheros TG121N */ + { USB_DEVICE(0x0cf3, 0x1001) }, + /* D-Link DWA 160A */ + { USB_DEVICE(0x07d1, 0x3c10) }, + /* Netgear WNDA3100 */ + { USB_DEVICE(0x0846, 0x9010) }, + /* Netgear WN111 v2 */ + { USB_DEVICE(0x0846, 0x9001) }, + /* Zydas ZD1221 */ + { USB_DEVICE(0x0ace, 0x1221) }, + /* Z-Com UB81 BG */ + { USB_DEVICE(0x0cde, 0x0023) }, + /* Z-Com UB82 ABG */ + { USB_DEVICE(0x0cde, 0x0026) }, + /* Arcadyan WN7512 */ + { USB_DEVICE(0x083a, 0xf522) }, + /* Planex GWUS300 */ + { USB_DEVICE(0x2019, 0x5304) }, + /* IO-Data WNGDNUS2 */ + { USB_DEVICE(0x04bb, 0x093f) }, + + /* terminate */ + {} +}; +MODULE_DEVICE_TABLE(usb, ar9170_usb_ids); + +static void ar9170_usb_tx_urb_complete_free(struct urb *urb) +{ + struct sk_buff *skb = urb->context; + struct ar9170_usb *aru = (struct ar9170_usb *) + usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)); + + if (!aru) { + dev_kfree_skb_irq(skb); + return ; + } + + ar9170_handle_tx_status(&aru->common, skb, false, + AR9170_TX_STATUS_COMPLETE); +} + +static void ar9170_usb_tx_urb_complete(struct urb *urb) +{ +} + +static void ar9170_usb_irq_completed(struct urb *urb) +{ + struct ar9170_usb *aru = urb->context; + + switch (urb->status) { + /* everything is fine */ + case 0: + break; + + /* disconnect */ + case -ENOENT: + case -ECONNRESET: + case -ENODEV: + case -ESHUTDOWN: + goto free; + + default: + goto resubmit; + } + + print_hex_dump_bytes("ar9170 irq: ", DUMP_PREFIX_OFFSET, + urb->transfer_buffer, urb->actual_length); + +resubmit: + usb_anchor_urb(urb, &aru->rx_submitted); + if (usb_submit_urb(urb, GFP_ATOMIC)) { + usb_unanchor_urb(urb); + goto free; + } + + return; + +free: + usb_buffer_free(aru->udev, 64, urb->transfer_buffer, urb->transfer_dma); +} + +static void ar9170_usb_rx_completed(struct urb *urb) +{ + struct sk_buff *skb = urb->context; + struct ar9170_usb *aru = (struct ar9170_usb *) + usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)); + int err; + + if (!aru) + goto free; + + switch (urb->status) { + /* everything is fine */ + case 0: + break; + + /* disconnect */ + case -ENOENT: + case -ECONNRESET: + case -ENODEV: + case -ESHUTDOWN: + goto free; + + default: + goto resubmit; + } + + skb_put(skb, urb->actual_length); + ar9170_rx(&aru->common, skb); + +resubmit: + skb_reset_tail_pointer(skb); + skb_trim(skb, 0); + + usb_anchor_urb(urb, &aru->rx_submitted); + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + usb_unanchor_urb(urb); + dev_kfree_skb_irq(skb); + } + + return ; + +free: + dev_kfree_skb_irq(skb); + return; +} + +static int ar9170_usb_prep_rx_urb(struct ar9170_usb *aru, + struct urb *urb, gfp_t gfp) +{ + struct sk_buff *skb; + + skb = __dev_alloc_skb(AR9170_MAX_RX_BUFFER_SIZE + 32, gfp); + if (!skb) + return -ENOMEM; + + /* reserve some space for mac80211's radiotap */ + skb_reserve(skb, 32); + + usb_fill_bulk_urb(urb, aru->udev, + usb_rcvbulkpipe(aru->udev, AR9170_EP_RX), + skb->data, min(skb_tailroom(skb), + AR9170_MAX_RX_BUFFER_SIZE), + ar9170_usb_rx_completed, skb); + + return 0; +} + +static int ar9170_usb_alloc_rx_irq_urb(struct ar9170_usb *aru) +{ + struct urb *urb = NULL; + void *ibuf; + int err = -ENOMEM; + + /* initialize interrupt endpoint */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + goto out; + + ibuf = usb_buffer_alloc(aru->udev, 64, GFP_KERNEL, &urb->transfer_dma); + if (!ibuf) + goto out; + + usb_fill_int_urb(urb, aru->udev, + usb_rcvintpipe(aru->udev, AR9170_EP_IRQ), ibuf, + 64, ar9170_usb_irq_completed, aru, 1); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + usb_anchor_urb(urb, &aru->rx_submitted); + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + usb_unanchor_urb(urb); + usb_buffer_free(aru->udev, 64, urb->transfer_buffer, + urb->transfer_dma); + } + +out: + usb_free_urb(urb); + return err; +} + +static int ar9170_usb_alloc_rx_bulk_urbs(struct ar9170_usb *aru) +{ + struct urb *urb; + int i; + int err = -EINVAL; + + for (i = 0; i < AR9170_NUM_RX_URBS; i++) { + err = -ENOMEM; + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + goto err_out; + + err = ar9170_usb_prep_rx_urb(aru, urb, GFP_KERNEL); + if (err) { + usb_free_urb(urb); + goto err_out; + } + + usb_anchor_urb(urb, &aru->rx_submitted); + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + usb_unanchor_urb(urb); + dev_kfree_skb_any((void *) urb->transfer_buffer); + usb_free_urb(urb); + goto err_out; + } + usb_free_urb(urb); + } + + /* the device now waiting for a firmware. */ + aru->common.state = AR9170_IDLE; + return 0; + +err_out: + + usb_kill_anchored_urbs(&aru->rx_submitted); + return err; +} + +static void ar9170_usb_cancel_urbs(struct ar9170_usb *aru) +{ + int ret; + + aru->common.state = AR9170_UNKNOWN_STATE; + + usb_unlink_anchored_urbs(&aru->tx_submitted); + + /* give the LED OFF command and the deauth frame a chance to air. */ + ret = usb_wait_anchor_empty_timeout(&aru->tx_submitted, + msecs_to_jiffies(100)); + if (ret == 0) + dev_err(&aru->udev->dev, "kill pending tx urbs.\n"); + usb_poison_anchored_urbs(&aru->tx_submitted); + + usb_poison_anchored_urbs(&aru->rx_submitted); +} + +static int ar9170_usb_exec_cmd(struct ar9170 *ar, enum ar9170_cmd cmd, + unsigned int plen, void *payload, + unsigned int outlen, void *out) +{ + struct ar9170_usb *aru = (void *) ar; + struct urb *urb = NULL; + unsigned long flags; + int err = -ENOMEM; + + if (unlikely(!IS_ACCEPTING_CMD(ar))) + return -EPERM; + + if (WARN_ON(plen > AR9170_MAX_CMD_LEN - 4)) + return -EINVAL; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (unlikely(!urb)) + goto err_free; + + ar->cmdbuf[0] = cpu_to_le32(plen); + ar->cmdbuf[0] |= cpu_to_le32(cmd << 8); + /* writing multiple regs fills this buffer already */ + if (plen && payload != (u8 *)(&ar->cmdbuf[1])) + memcpy(&ar->cmdbuf[1], payload, plen); + + spin_lock_irqsave(&aru->common.cmdlock, flags); + aru->readbuf = (u8 *)out; + aru->readlen = outlen; + spin_unlock_irqrestore(&aru->common.cmdlock, flags); + + usb_fill_int_urb(urb, aru->udev, + usb_sndbulkpipe(aru->udev, AR9170_EP_CMD), + aru->common.cmdbuf, plen + 4, + ar9170_usb_tx_urb_complete, NULL, 1); + + usb_anchor_urb(urb, &aru->tx_submitted); + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + usb_unanchor_urb(urb); + usb_free_urb(urb); + goto err_unbuf; + } + usb_free_urb(urb); + + err = wait_for_completion_timeout(&aru->cmd_wait, HZ); + if (err == 0) { + err = -ETIMEDOUT; + goto err_unbuf; + } + + if (outlen >= 0 && aru->readlen != outlen) { + err = -EMSGSIZE; + goto err_unbuf; + } + + return 0; + +err_unbuf: + /* Maybe the device was removed in the second we were waiting? */ + if (IS_STARTED(ar)) { + dev_err(&aru->udev->dev, "no command feedback " + "received (%d).\n", err); + + /* provide some maybe useful debug information */ + print_hex_dump_bytes("ar9170 cmd: ", DUMP_PREFIX_NONE, + aru->common.cmdbuf, plen + 4); + dump_stack(); + } + + /* invalidate to avoid completing the next prematurely */ + spin_lock_irqsave(&aru->common.cmdlock, flags); + aru->readbuf = NULL; + aru->readlen = 0; + spin_unlock_irqrestore(&aru->common.cmdlock, flags); + +err_free: + + return err; +} + +static int ar9170_usb_tx(struct ar9170 *ar, struct sk_buff *skb, + bool txstatus_needed, unsigned int extra_len) +{ + struct ar9170_usb *aru = (struct ar9170_usb *) ar; + struct urb *urb; + int err; + + if (unlikely(!IS_STARTED(ar))) { + /* Seriously, what were you drink... err... thinking!? */ + return -EPERM; + } + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (unlikely(!urb)) + return -ENOMEM; + + usb_fill_bulk_urb(urb, aru->udev, + usb_sndbulkpipe(aru->udev, AR9170_EP_TX), + skb->data, skb->len + extra_len, (txstatus_needed ? + ar9170_usb_tx_urb_complete : + ar9170_usb_tx_urb_complete_free), skb); + urb->transfer_flags |= URB_ZERO_PACKET; + + usb_anchor_urb(urb, &aru->tx_submitted); + err = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(err)) + usb_unanchor_urb(urb); + + usb_free_urb(urb); + return err; +} + +static void ar9170_usb_callback_cmd(struct ar9170 *ar, u32 len , void *buffer) +{ + struct ar9170_usb *aru = (void *) ar; + unsigned long flags; + u32 in, out; + + if (!buffer) + return ; + + in = le32_to_cpup((__le32 *)buffer); + out = le32_to_cpu(ar->cmdbuf[0]); + + /* mask off length byte */ + out &= ~0xFF; + + if (aru->readlen >= 0) { + /* add expected length */ + out |= aru->readlen; + } else { + /* add obtained length */ + out |= in & 0xFF; + } + + /* + * Some commands (e.g: AR9170_CMD_FREQUENCY) have a variable response + * length and we cannot predict the correct length in advance. + * So we only check if we provided enough space for the data. + */ + if (unlikely(out < in)) { + dev_warn(&aru->udev->dev, "received invalid command response " + "got %d bytes, instead of %d bytes " + "and the resp length is %d bytes\n", + in, out, len); + print_hex_dump_bytes("ar9170 invalid resp: ", + DUMP_PREFIX_OFFSET, buffer, len); + /* + * Do not complete, then the command times out, + * and we get a stack trace from there. + */ + return ; + } + + spin_lock_irqsave(&aru->common.cmdlock, flags); + if (aru->readbuf && len > 0) { + memcpy(aru->readbuf, buffer + 4, len - 4); + aru->readbuf = NULL; + } + complete(&aru->cmd_wait); + spin_unlock_irqrestore(&aru->common.cmdlock, flags); +} + +static int ar9170_usb_upload(struct ar9170_usb *aru, const void *data, + size_t len, u32 addr, bool complete) +{ + int transfer, err; + u8 *buf = kmalloc(4096, GFP_KERNEL); + + if (!buf) + return -ENOMEM; + + while (len) { + transfer = min_t(int, len, 4096); + memcpy(buf, data, transfer); + + err = usb_control_msg(aru->udev, usb_sndctrlpipe(aru->udev, 0), + 0x30 /* FW DL */, 0x40 | USB_DIR_OUT, + addr >> 8, 0, buf, transfer, 1000); + + if (err < 0) { + kfree(buf); + return err; + } + + len -= transfer; + data += transfer; + addr += transfer; + } + kfree(buf); + + if (complete) { + err = usb_control_msg(aru->udev, usb_sndctrlpipe(aru->udev, 0), + 0x31 /* FW DL COMPLETE */, + 0x40 | USB_DIR_OUT, 0, 0, NULL, 0, 5000); + } + + return 0; +} + +static int ar9170_usb_request_firmware(struct ar9170_usb *aru) +{ + int err = 0; + + err = request_firmware(&aru->init_values, "ar9170-1.fw", + &aru->udev->dev); + if (err) { + dev_err(&aru->udev->dev, "file with init values not found.\n"); + return err; + } + + err = request_firmware(&aru->firmware, "ar9170-2.fw", &aru->udev->dev); + if (err) { + release_firmware(aru->init_values); + dev_err(&aru->udev->dev, "firmware file not found.\n"); + return err; + } + + return err; +} + +static int ar9170_usb_reset(struct ar9170_usb *aru) +{ + int ret, lock = (aru->intf->condition != USB_INTERFACE_BINDING); + + if (lock) { + ret = usb_lock_device_for_reset(aru->udev, aru->intf); + if (ret < 0) { + dev_err(&aru->udev->dev, "unable to lock device " + "for reset (%d).\n", ret); + return ret; + } + } + + ret = usb_reset_device(aru->udev); + if (lock) + usb_unlock_device(aru->udev); + + /* let it rest - for a second - */ + msleep(1000); + + return ret; +} + +static int ar9170_usb_upload_firmware(struct ar9170_usb *aru) +{ + int err; + + /* First, upload initial values to device RAM */ + err = ar9170_usb_upload(aru, aru->init_values->data, + aru->init_values->size, 0x102800, false); + if (err) { + dev_err(&aru->udev->dev, "firmware part 1 " + "upload failed (%d).\n", err); + return err; + } + + /* Then, upload the firmware itself and start it */ + return ar9170_usb_upload(aru, aru->firmware->data, aru->firmware->size, + 0x200000, true); +} + +static int ar9170_usb_init_transport(struct ar9170_usb *aru) +{ + struct ar9170 *ar = (void *) &aru->common; + int err; + + ar9170_regwrite_begin(ar); + + /* Set USB Rx stream mode MAX packet number to 2 */ + ar9170_regwrite(AR9170_USB_REG_MAX_AGG_UPLOAD, 0x4); + + /* Set USB Rx stream mode timeout to 10us */ + ar9170_regwrite(AR9170_USB_REG_UPLOAD_TIME_CTL, 0x80); + + ar9170_regwrite_finish(); + + err = ar9170_regwrite_result(); + if (err) + dev_err(&aru->udev->dev, "USB setup failed (%d).\n", err); + + return err; +} + +static void ar9170_usb_stop(struct ar9170 *ar) +{ + struct ar9170_usb *aru = (void *) ar; + int ret; + + if (IS_ACCEPTING_CMD(ar)) + aru->common.state = AR9170_STOPPED; + + /* lets wait a while until the tx - queues are dried out */ + ret = usb_wait_anchor_empty_timeout(&aru->tx_submitted, + msecs_to_jiffies(1000)); + if (ret == 0) + dev_err(&aru->udev->dev, "kill pending tx urbs.\n"); + + usb_poison_anchored_urbs(&aru->tx_submitted); + + /* + * Note: + * So far we freed all tx urbs, but we won't dare to touch any rx urbs. + * Else we would end up with a unresponsive device... + */ +} + +static int ar9170_usb_open(struct ar9170 *ar) +{ + struct ar9170_usb *aru = (void *) ar; + int err; + + usb_unpoison_anchored_urbs(&aru->tx_submitted); + err = ar9170_usb_init_transport(aru); + if (err) { + usb_poison_anchored_urbs(&aru->tx_submitted); + return err; + } + + aru->common.state = AR9170_IDLE; + return 0; +} + +static int ar9170_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct ar9170_usb *aru; + struct ar9170 *ar; + struct usb_device *udev; + int err; + + aru = ar9170_alloc(sizeof(*aru)); + if (IS_ERR(aru)) { + err = PTR_ERR(aru); + goto out; + } + + udev = interface_to_usbdev(intf); + usb_get_dev(udev); + aru->udev = udev; + aru->intf = intf; + ar = &aru->common; + + usb_set_intfdata(intf, aru); + SET_IEEE80211_DEV(ar->hw, &udev->dev); + + init_usb_anchor(&aru->rx_submitted); + init_usb_anchor(&aru->tx_submitted); + init_completion(&aru->cmd_wait); + + aru->common.stop = ar9170_usb_stop; + aru->common.open = ar9170_usb_open; + aru->common.tx = ar9170_usb_tx; + aru->common.exec_cmd = ar9170_usb_exec_cmd; + aru->common.callback_cmd = ar9170_usb_callback_cmd; + + err = ar9170_usb_reset(aru); + if (err) + goto err_unlock; + + err = ar9170_usb_request_firmware(aru); + if (err) + goto err_unlock; + + err = ar9170_usb_alloc_rx_irq_urb(aru); + if (err) + goto err_freefw; + + err = ar9170_usb_alloc_rx_bulk_urbs(aru); + if (err) + goto err_unrx; + + err = ar9170_usb_upload_firmware(aru); + if (err) { + err = ar9170_echo_test(&aru->common, 0x60d43110); + if (err) { + /* force user invention, by disabling the device */ + err = usb_driver_set_configuration(aru->udev, -1); + dev_err(&aru->udev->dev, "device is in a bad state. " + "please reconnect it!\n"); + goto err_unrx; + } + } + + err = ar9170_usb_open(ar); + if (err) + goto err_unrx; + + err = ar9170_register(ar, &udev->dev); + + ar9170_usb_stop(ar); + if (err) + goto err_unrx; + + return 0; + +err_unrx: + ar9170_usb_cancel_urbs(aru); + +err_freefw: + release_firmware(aru->init_values); + release_firmware(aru->firmware); + +err_unlock: + usb_set_intfdata(intf, NULL); + usb_put_dev(udev); + ieee80211_free_hw(ar->hw); +out: + return err; +} + +static void ar9170_usb_disconnect(struct usb_interface *intf) +{ + struct ar9170_usb *aru = usb_get_intfdata(intf); + + if (!aru) + return; + + aru->common.state = AR9170_IDLE; + ar9170_unregister(&aru->common); + ar9170_usb_cancel_urbs(aru); + + release_firmware(aru->init_values); + release_firmware(aru->firmware); + + usb_put_dev(aru->udev); + usb_set_intfdata(intf, NULL); + ieee80211_free_hw(aru->common.hw); +} + +static struct usb_driver ar9170_driver = { + .name = "ar9170usb", + .probe = ar9170_usb_probe, + .disconnect = ar9170_usb_disconnect, + .id_table = ar9170_usb_ids, + .soft_unbind = 1, +}; + +static int __init ar9170_init(void) +{ + return usb_register(&ar9170_driver); +} + +static void __exit ar9170_exit(void) +{ + usb_deregister(&ar9170_driver); +} + +module_init(ar9170_init); +module_exit(ar9170_exit); diff --git a/drivers/net/wireless/ar9170/usb.h b/drivers/net/wireless/ar9170/usb.h new file mode 100644 index 00000000000..f5852924cd6 --- /dev/null +++ b/drivers/net/wireless/ar9170/usb.h @@ -0,0 +1,74 @@ +/* + * Atheros AR9170 USB driver + * + * Driver specific definitions + * + * Copyright 2008, Johannes Berg + * Copyright 2009, Christian Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, see + * http://www.gnu.org/licenses/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * Copyright (c) 2007-2008 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 __USB_H +#define __USB_H + +#include +#include +#include +#include +#include +#include +#include +#include "eeprom.h" +#include "hw.h" +#include "ar9170.h" + +#define AR9170_NUM_RX_URBS 16 + +struct firmware; + +struct ar9170_usb { + struct ar9170 common; + struct usb_device *udev; + struct usb_interface *intf; + + struct usb_anchor rx_submitted; + struct usb_anchor tx_submitted; + + spinlock_t cmdlock; + struct completion cmd_wait; + int readlen; + u8 *readbuf; + + const struct firmware *init_values; + const struct firmware *firmware; +}; + +#endif /* __USB_H */ -- cgit v1.2.3 From 75ca88c9701bf8f65fc1f009f10dd64b0378b977 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 21 Mar 2009 23:11:49 +0100 Subject: ar9170: update Makefile, Kconfig and MAINTAINERS This patch update all necessary Makefile and Kconfig files. Now you can then enable ar9170 in the kernel configuration under: Device Drivers ---> [*] Network device support ---> Wireless LAN ---> [*] Wireless LAN (IEEE 802.11) Atheros AR9170 support Atheros AR9170 USB support Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- MAINTAINERS | 8 ++++++++ drivers/net/wireless/Kconfig | 1 + drivers/net/wireless/Makefile | 1 + drivers/net/wireless/ar9170/Kconfig | 28 ++++++++++++++++++++++++++++ drivers/net/wireless/ar9170/Makefile | 5 +++++ 5 files changed, 43 insertions(+) create mode 100644 drivers/net/wireless/ar9170/Kconfig create mode 100644 drivers/net/wireless/ar9170/Makefile diff --git a/MAINTAINERS b/MAINTAINERS index bdadcb3c3f4..64c89c215b0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -765,6 +765,14 @@ L: linux-wireless@vger.kernel.org L: ath9k-devel@lists.ath9k.org S: Supported +ATHEROS AR9170 WIRELESS DRIVER +P: Christian Lamparter +M: chunkeey@web.de +L: linux-wireless@vger.kernel.org +W: http://wireless.kernel.org/en/users/Drivers/ar9170 +S: Maintained +F: drivers/net/wireless/ar9170/ + ATI_REMOTE2 DRIVER P: Ville Syrjala M: syrjala@sci.fi diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 612fffe100a..8a0823588c5 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -485,6 +485,7 @@ config MWL8K source "drivers/net/wireless/p54/Kconfig" source "drivers/net/wireless/ath5k/Kconfig" source "drivers/net/wireless/ath9k/Kconfig" +source "drivers/net/wireless/ar9170/Kconfig" source "drivers/net/wireless/ipw2x00/Kconfig" source "drivers/net/wireless/iwlwifi/Kconfig" source "drivers/net/wireless/hostap/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index d780487c420..5e7c9acb6be 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -57,5 +57,6 @@ obj-$(CONFIG_P54_COMMON) += p54/ obj-$(CONFIG_ATH5K) += ath5k/ obj-$(CONFIG_ATH9K) += ath9k/ +obj-$(CONFIG_AR9170_COMMON) += ar9170/ obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o diff --git a/drivers/net/wireless/ar9170/Kconfig b/drivers/net/wireless/ar9170/Kconfig new file mode 100644 index 00000000000..f6611876e28 --- /dev/null +++ b/drivers/net/wireless/ar9170/Kconfig @@ -0,0 +1,28 @@ +config AR9170_COMMON + tristate "Atheros AR9170 support" + depends on WLAN_80211 && MAC80211 && EXPERIMENTAL + help + This is common code for AR9170 based devices. + This module does nothing by itself - the USB/(SPI) frontends + also need to be enabled in order to support any devices. + + Say Y if you have the hardware, or M to build a module called + ar9170common. + +config AR9170_USB + tristate "Atheros AR9170 USB support" + depends on AR9170_COMMON && USB + select FW_LOADER + help + This is a driver for the Atheros "otus" 802.11n USB devices. + + These devices require additional firmware (2 files). + For now, these files can be downloaded from here: + http://wireless.kernel.org/en/users/Drivers/ar9170 + + If you choose to build a module, it'll be called ar9170usb. + +config AR9170_LEDS + bool + depends on AR9170_COMMON && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = AR9170_COMMON) + default y diff --git a/drivers/net/wireless/ar9170/Makefile b/drivers/net/wireless/ar9170/Makefile new file mode 100644 index 00000000000..59b174dd466 --- /dev/null +++ b/drivers/net/wireless/ar9170/Makefile @@ -0,0 +1,5 @@ +ar9170common-objs += main.o cmd.o mac.o phy.o led.o +ar9170usb-objs += usb.o + +obj-$(CONFIG_AR9170_COMMON) += ar9170common.o +obj-$(CONFIG_AR9170_USB) += ar9170usb.o -- cgit v1.2.3 From af83debf5bb44257082d4489ac86123a0cadf6d3 Mon Sep 17 00:00:00 2001 From: Tulio Magno Quites Machado Filho Date: Sun, 22 Mar 2009 01:41:13 +0100 Subject: ath5k: Support LED's on Acer Extensa 5620z Add vendor ID for Quanta Microsystems and update the led table with the reported device. Reported-by: Scott Barnes Signed-off-by: Tulio Magno Quites Machado Filho Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/led.c | 2 ++ include/linux/pci_ids.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath5k/led.c b/drivers/net/wireless/ath5k/led.c index 0686e12738b..19555fb79c9 100644 --- a/drivers/net/wireless/ath5k/led.c +++ b/drivers/net/wireless/ath5k/led.c @@ -65,6 +65,8 @@ static const struct pci_device_id ath5k_led_devices[] = { { ATH_SDEVICE(PCI_VENDOR_ID_AMBIT, 0x0422), ATH_LED(1, 1) }, /* E-machines E510 (tuliom@gmail.com) */ { ATH_SDEVICE(PCI_VENDOR_ID_AMBIT, 0x0428), ATH_LED(3, 0) }, + /* Acer Extensa 5620z (nekoreeve@gmail.com) */ + { ATH_SDEVICE(PCI_VENDOR_ID_QMI, 0x0105), ATH_LED(3, 0) }, { } }; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 097f410edef..05dfa7c4fb6 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2271,6 +2271,8 @@ #define PCI_DEVICE_ID_KORENIX_JETCARDF0 0x1600 #define PCI_DEVICE_ID_KORENIX_JETCARDF1 0x16ff +#define PCI_VENDOR_ID_QMI 0x1a32 + #define PCI_VENDOR_ID_TEKRAM 0x1de1 #define PCI_DEVICE_ID_TEKRAM_DC290 0xdc29 -- cgit v1.2.3 From 3cf335d527ba6af80f4143f3c9e5136afdb143af Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Sun, 22 Mar 2009 21:57:06 +0200 Subject: mac80211: decrease execution of the associated timer Currently the timer is triggering every two seconds (IEEE80211_MONITORING_INTERVAL). Decrease the timer to only trigger during data idle periods to avoid waking up CPU unnecessary. The timer will still trigger during idle periods, that needs to be fixed later. There's also a functional change that probe requests are sent only when the data path is idle, earlier they were sent also while there was activity on the data path. This is also preparation for the beacon filtering support. Thanks to Johannes Berg for the idea. Signed-off-by: Kalle Valo Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/mlme.c | 15 +++++++++++++++ net/mac80211/rx.c | 3 +++ 3 files changed, 20 insertions(+) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 564167fbb9a..055bb776408 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1083,6 +1083,8 @@ void ieee80211_dynamic_ps_timer(unsigned long data); void ieee80211_send_nullfunc(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, int powersave); +void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, + struct ieee80211_hdr *hdr); void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, enum queue_stop_reason reason); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index c05be09b9c6..209abb073df 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -909,6 +909,21 @@ static void ieee80211_associate(struct ieee80211_sub_if_data *sdata) mod_timer(&ifmgd->timer, jiffies + IEEE80211_ASSOC_TIMEOUT); } +void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, + struct ieee80211_hdr *hdr) +{ + /* + * We can postpone the mgd.timer whenever receiving unicast frames + * from AP because we know that the connection is working both ways + * at that time. But multicast frames (and hence also beacons) must + * be ignored here, because we need to trigger the timer during + * data idle periods for sending the periodical probe request to + * the AP. + */ + if (!is_multicast_ether_addr(hdr->addr1)) + mod_timer(&sdata->u.mgd.timer, + jiffies + IEEE80211_MONITORING_INTERVAL); +} static void ieee80211_associated(struct ieee80211_sub_if_data *sdata) { diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 47d395a5192..dbfb2846535 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -856,6 +856,9 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) if (!(rx->flags & IEEE80211_RX_RA_MATCH)) return RX_CONTINUE; + if (rx->sdata->vif.type == NL80211_IFTYPE_STATION) + ieee80211_sta_rx_notify(rx->sdata, hdr); + sta->rx_fragments++; sta->rx_bytes += rx->skb->len; sta->last_signal = rx->status->signal; -- cgit v1.2.3 From 15b7b0629c8213905926394dc73d600e0ca250ce Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Sun, 22 Mar 2009 21:57:14 +0200 Subject: mac80211: track beacons separately from the rx path activity Separate beacon and rx path tracking in preparation for the beacon filtering support. At the same time change ieee80211_associated() to look a bit simpler. Probe requests are now sent only after IEEE80211_PROBE_IDLE_TIME, which is now set to 60 seconds. Signed-off-by: Kalle Valo Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 1 + net/mac80211/mlme.c | 77 +++++++++++++++++++++++++++------------------- net/mac80211/rx.c | 6 +++- 3 files changed, 52 insertions(+), 32 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 055bb776408..8a617a7fc09 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -308,6 +308,7 @@ struct ieee80211_if_managed { unsigned long request; unsigned long last_probe; + unsigned long last_beacon; unsigned int flags; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 209abb073df..8f30f4d19da 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -30,7 +30,7 @@ #define IEEE80211_ASSOC_TIMEOUT (HZ / 5) #define IEEE80211_ASSOC_MAX_TRIES 3 #define IEEE80211_MONITORING_INTERVAL (2 * HZ) -#define IEEE80211_PROBE_INTERVAL (60 * HZ) +#define IEEE80211_PROBE_IDLE_TIME (60 * HZ) #define IEEE80211_RETRY_AUTH_INTERVAL (1 * HZ) /* utils */ @@ -930,7 +930,7 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata) struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; struct sta_info *sta; - int disassoc; + bool disassoc = false; /* TODO: start monitoring current AP signal quality and number of * missed beacons. Scan other channels every now and then and search @@ -945,36 +945,39 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata) if (!sta) { printk(KERN_DEBUG "%s: No STA entry for own AP %pM\n", sdata->dev->name, ifmgd->bssid); - disassoc = 1; - } else { - disassoc = 0; - if (time_after(jiffies, - sta->last_rx + IEEE80211_MONITORING_INTERVAL)) { - if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) { - printk(KERN_DEBUG "%s: No ProbeResp from " - "current AP %pM - assume out of " - "range\n", - sdata->dev->name, ifmgd->bssid); - disassoc = 1; - } else - ieee80211_send_probe_req(sdata, ifmgd->bssid, - ifmgd->ssid, - ifmgd->ssid_len, - NULL, 0); - ifmgd->flags ^= IEEE80211_STA_PROBEREQ_POLL; - } else { - ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL; - if (time_after(jiffies, ifmgd->last_probe + - IEEE80211_PROBE_INTERVAL)) { - ifmgd->last_probe = jiffies; - ieee80211_send_probe_req(sdata, ifmgd->bssid, - ifmgd->ssid, - ifmgd->ssid_len, - NULL, 0); - } - } + disassoc = true; + goto unlock; + } + + if ((ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) && + time_after(jiffies, sta->last_rx + IEEE80211_MONITORING_INTERVAL)) { + printk(KERN_DEBUG "%s: no probe response from AP %pM " + "- disassociating\n", + sdata->dev->name, ifmgd->bssid); + disassoc = true; + ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL; + goto unlock; + } + + if (time_after(jiffies, + ifmgd->last_beacon + IEEE80211_MONITORING_INTERVAL)) { + printk(KERN_DEBUG "%s: beacon loss from AP %pM " + "- sending probe request\n", + sdata->dev->name, ifmgd->bssid); + ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL; + ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid, + ifmgd->ssid_len, NULL, 0); + goto unlock; + + } + + if (time_after(jiffies, sta->last_rx + IEEE80211_PROBE_IDLE_TIME)) { + ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL; + ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid, + ifmgd->ssid_len, NULL, 0); } + unlock: rcu_read_unlock(); if (disassoc) @@ -1374,6 +1377,12 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, bss_conf->assoc_capability = capab_info; ieee80211_set_associated(sdata, changed); + /* + * initialise the time of last beacon to be the association time, + * otherwise beacon loss check will trigger immediately + */ + ifmgd->last_beacon = jiffies; + ieee80211_associated(sdata); cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, len); } @@ -1422,9 +1431,12 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, size_t len, struct ieee80211_rx_status *rx_status) { + struct ieee80211_if_managed *ifmgd; size_t baselen; struct ieee802_11_elems elems; + ifmgd = &sdata->u.mgd; + if (memcmp(mgmt->da, sdata->dev->dev_addr, ETH_ALEN)) return; /* ignore ProbeResp to foreign address */ @@ -1439,11 +1451,14 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, /* direct probe may be part of the association flow */ if (test_and_clear_bit(IEEE80211_STA_REQ_DIRECT_PROBE, - &sdata->u.mgd.request)) { + &ifmgd->request)) { printk(KERN_DEBUG "%s direct probe responded\n", sdata->dev->name); ieee80211_authenticate(sdata); } + + if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) + ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL; } static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index dbfb2846535..eff59f36e8e 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -850,7 +850,11 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) * Mesh beacons will update last_rx when if they are found to * match the current local configuration when processed. */ - sta->last_rx = jiffies; + if (rx->sdata->vif.type == NL80211_IFTYPE_STATION && + ieee80211_is_beacon(hdr->frame_control)) { + rx->sdata->u.mgd.last_beacon = jiffies; + } else + sta->last_rx = jiffies; } if (!(rx->flags & IEEE80211_RX_RA_MATCH)) -- cgit v1.2.3 From 9050bdd8589c373e01e41ddbd9a192de2ff01ef0 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Sun, 22 Mar 2009 21:57:21 +0200 Subject: mac80211: disable power save when scanning When software scanning we need to disable power save so that all possible probe responses and beacons are received. For hardware scanning assume that hardware will take care of that and document that assumption. Signed-off-by: Kalle Valo Signed-off-by: John W. Linville --- include/net/mac80211.h | 12 ++++++---- net/mac80211/scan.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 69 insertions(+), 7 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index daa539a4287..174dc1d7526 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1307,11 +1307,13 @@ enum ieee80211_ampdu_mlme_action { * * @hw_scan: Ask the hardware to service the scan request, no need to start * the scan state machine in stack. The scan must honour the channel - * configuration done by the regulatory agent in the wiphy's registered - * bands. When the scan finishes, ieee80211_scan_completed() must be - * called; note that it also must be called when the scan cannot finish - * because the hardware is turned off! Anything else is a bug! - * Returns a negative error code which will be seen in userspace. + * configuration done by the regulatory agent in the wiphy's + * registered bands. The hardware (or the driver) needs to make sure + * that power save is disabled. When the scan finishes, + * ieee80211_scan_completed() must be called; note that it also must + * be called when the scan cannot finish because the hardware is + * turned off! Anything else is a bug! Returns a negative error code + * which will be seen in userspace. * * @sw_scan_start: Notifier function that is called just before a software scan * is started. Can be NULL, if the driver doesn't need this notification. diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 46f35dc6acc..3bf9839f591 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -214,6 +214,66 @@ void ieee80211_scan_failed(struct ieee80211_local *local) local->scan_req = NULL; } +/* + * inform AP that we will go to sleep so that it will buffer the frames + * while we scan + */ +static void ieee80211_scan_ps_enable(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + bool ps = false; + + /* FIXME: what to do when local->pspolling is true? */ + + del_timer_sync(&local->dynamic_ps_timer); + cancel_work_sync(&local->dynamic_ps_enable_work); + + if (local->hw.conf.flags & IEEE80211_CONF_PS) { + ps = true; + local->hw.conf.flags &= ~IEEE80211_CONF_PS; + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); + } + + if (!ps || !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)) + /* + * If power save was enabled, no need to send a nullfunc + * frame because AP knows that we are sleeping. But if the + * hardware is creating the nullfunc frame for power save + * status (ie. IEEE80211_HW_PS_NULLFUNC_STACK is not + * enabled) and power save was enabled, the firmware just + * sent a null frame with power save disabled. So we need + * to send a new nullfunc frame to inform the AP that we + * are again sleeping. + */ + ieee80211_send_nullfunc(local, sdata, 1); +} + +/* inform AP that we are awake again, unless power save is enabled */ +static void ieee80211_scan_ps_disable(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + + if (!local->powersave) + ieee80211_send_nullfunc(local, sdata, 0); + else { + /* + * In !IEEE80211_HW_PS_NULLFUNC_STACK case the hardware + * will send a nullfunc frame with the powersave bit set + * even though the AP already knows that we are sleeping. + * This could be avoided by sending a null frame with power + * save bit disabled before enabling the power save, but + * this doesn't gain anything. + * + * When IEEE80211_HW_PS_NULLFUNC_STACK is enabled, no need + * to send a nullfunc frame because AP already knows that + * we are sleeping, let's just enable power save mode in + * hardware. + */ + local->hw.conf.flags |= IEEE80211_CONF_PS; + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); + } +} + void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) { struct ieee80211_local *local = hw_to_local(hw); @@ -268,7 +328,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) /* Tell AP we're back */ if (sdata->vif.type == NL80211_IFTYPE_STATION) { if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED) { - ieee80211_send_nullfunc(local, sdata, 0); + ieee80211_scan_ps_disable(sdata); netif_tx_wake_all_queues(sdata->dev); } } else @@ -441,7 +501,7 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, if (sdata->vif.type == NL80211_IFTYPE_STATION) { if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED) { netif_tx_stop_all_queues(sdata->dev); - ieee80211_send_nullfunc(local, sdata, 1); + ieee80211_scan_ps_enable(sdata); } } else netif_tx_stop_all_queues(sdata->dev); -- cgit v1.2.3 From a08c1c1ac0c26229ca1ca45d554b209a56edc8be Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Sun, 22 Mar 2009 21:57:28 +0200 Subject: cfg80211: add feature to hold bss In beacon filtering there needs to be a way to not expire the BSS even when no beacons are received. Add an interface to cfg80211 to hold BSS and make sure that it's not expired. Signed-off-by: Kalle Valo Signed-off-by: John W. Linville --- include/net/cfg80211.h | 18 ++++++++++++++++++ net/wireless/core.h | 2 ++ net/wireless/scan.c | 27 ++++++++++++++++++++++++++- 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index dca4a6b0461..5389afdc129 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -539,6 +539,7 @@ enum cfg80211_signal_type { * is no guarantee that these are well-formed!) * @len_information_elements: total length of the information elements * @signal: signal strength value (type depends on the wiphy's signal_type) + * @hold: BSS should not expire * @free_priv: function pointer to free private data * @priv: private area for driver use, has at least wiphy->bss_priv_size bytes */ @@ -940,4 +941,21 @@ void cfg80211_send_rx_deauth(struct net_device *dev, const u8 *buf, void cfg80211_send_rx_disassoc(struct net_device *dev, const u8 *buf, size_t len); +/** + * cfg80211_hold_bss - exclude bss from expiration + * @bss: bss which should not expire + * + * In a case when the BSS is not updated but it shouldn't expire this + * function can be used to mark the BSS to be excluded from expiration. + */ +void cfg80211_hold_bss(struct cfg80211_bss *bss); + +/** + * cfg80211_unhold_bss - remove expiration exception from the BSS + * @bss: bss which can expire again + * + * This function marks the BSS to be expirable again. + */ +void cfg80211_unhold_bss(struct cfg80211_bss *bss); + #endif /* __NET_CFG80211_H */ diff --git a/net/wireless/core.h b/net/wireless/core.h index 6acd483a61f..97a6fd8b2b0 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -90,6 +90,8 @@ struct cfg80211_internal_bss { struct rb_node rbn; unsigned long ts; struct kref ref; + bool hold; + /* must be last because of priv member */ struct cfg80211_bss pub; }; diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 280dbcd02c1..2a00e362f5f 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -80,7 +80,8 @@ void cfg80211_bss_expire(struct cfg80211_registered_device *dev) bool expired = false; list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { - if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE)) + if (bss->hold || + !time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE)) continue; list_del(&bss->list); rb_erase(&bss->rbn, &dev->bss_tree); @@ -471,6 +472,30 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) } EXPORT_SYMBOL(cfg80211_unlink_bss); +void cfg80211_hold_bss(struct cfg80211_bss *pub) +{ + struct cfg80211_internal_bss *bss; + + if (!pub) + return; + + bss = container_of(pub, struct cfg80211_internal_bss, pub); + bss->hold = true; +} +EXPORT_SYMBOL(cfg80211_hold_bss); + +void cfg80211_unhold_bss(struct cfg80211_bss *pub) +{ + struct cfg80211_internal_bss *bss; + + if (!pub) + return; + + bss = container_of(pub, struct cfg80211_internal_bss, pub); + bss->hold = false; +} +EXPORT_SYMBOL(cfg80211_unhold_bss); + #ifdef CONFIG_WIRELESS_EXT int cfg80211_wext_siwscan(struct net_device *dev, struct iw_request_info *info, -- cgit v1.2.3 From 04de83815993714a7ba2618f637fa1092a5f664b Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Sun, 22 Mar 2009 21:57:35 +0200 Subject: mac80211: add beacon filtering support Add IEEE80211_HW_BEACON_FILTERING flag so that driver inform that it supports beacon filtering. Drivers need to call the new function ieee80211_beacon_loss() to notify about beacon loss. Signed-off-by: Kalle Valo Signed-off-by: John W. Linville --- Documentation/DocBook/mac80211.tmpl | 6 +++++ include/net/mac80211.h | 33 +++++++++++++++++++++++++ net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/iface.c | 3 +++ net/mac80211/mlme.c | 49 ++++++++++++++++++++++++++++++++++++- 5 files changed, 92 insertions(+), 1 deletion(-) diff --git a/Documentation/DocBook/mac80211.tmpl b/Documentation/DocBook/mac80211.tmpl index 8af6d962687..fbeaffc1dcc 100644 --- a/Documentation/DocBook/mac80211.tmpl +++ b/Documentation/DocBook/mac80211.tmpl @@ -227,6 +227,12 @@ usage should require reading the full document. !Pinclude/net/mac80211.h Powersave support + + Beacon filter support +!Pinclude/net/mac80211.h Beacon filter support +!Finclude/net/mac80211.h ieee80211_beacon_loss + + Multiple queues and QoS support TBD diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 174dc1d7526..d881ed8ad2c 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -882,6 +882,10 @@ enum ieee80211_tkip_key_type { * * @IEEE80211_HW_MFP_CAPABLE: * Hardware supports management frame protection (MFP, IEEE 802.11w). + * + * @IEEE80211_HW_BEACON_FILTER: + * Hardware supports dropping of irrelevant beacon frames to + * avoid waking up cpu. */ enum ieee80211_hw_flags { IEEE80211_HW_RX_INCLUDES_FCS = 1<<1, @@ -897,6 +901,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_PS_NULLFUNC_STACK = 1<<11, IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<12, IEEE80211_HW_MFP_CAPABLE = 1<<13, + IEEE80211_HW_BEACON_FILTER = 1<<14, }; /** @@ -1120,6 +1125,24 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw, * value, or by the stack if all nullfunc handling is in the stack. */ +/** + * DOC: Beacon filter support + * + * Some hardware have beacon filter support to reduce host cpu wakeups + * which will reduce system power consumption. It usuallly works so that + * the firmware creates a checksum of the beacon but omits all constantly + * changing elements (TSF, TIM etc). Whenever the checksum changes the + * beacon is forwarded to the host, otherwise it will be just dropped. That + * way the host will only receive beacons where some relevant information + * (for example ERP protection or WMM settings) have changed. + * + * Beacon filter support is informed with %IEEE80211_HW_BEACON_FILTER flag. + * The driver needs to enable beacon filter support whenever power save is + * enabled, that is %IEEE80211_CONF_PS is set. When power save is enabled, + * the stack will not check for beacon miss at all and the driver needs to + * notify about complete loss of beacons with ieee80211_beacon_loss(). + */ + /** * DOC: Frame filtering * @@ -1970,6 +1993,16 @@ void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_hw *hw, const u8 *ra, struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_hw *hw, const u8 *addr); +/** + * ieee80211_beacon_loss - inform hardware does not receive beacons + * + * @vif: &struct ieee80211_vif pointer from &struct ieee80211_if_init_conf. + * + * 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); /* Rate control API */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8a617a7fc09..acba78e1a5c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -275,6 +275,7 @@ struct ieee80211_if_managed { struct timer_list chswitch_timer; struct work_struct work; struct work_struct chswitch_work; + struct work_struct beacon_loss_work; u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; @@ -1086,6 +1087,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_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 dd2a276fa8c..91e8e1bacaa 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -477,6 +477,9 @@ 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.beacon_loss_work); + /* * When we get here, the interface is marked down. * Call synchronize_rcu() to wait for the RX path diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 8f30f4d19da..7ecda9d59d8 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -610,6 +610,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, bss_info_changed |= ieee80211_handle_bss_capability(sdata, bss->cbss.capability, bss->has_erp_value, bss->erp_value); + cfg80211_hold_bss(&bss->cbss); + ieee80211_rx_bss_put(local, bss); } @@ -751,6 +753,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; + struct ieee80211_conf *conf = &local_to_hw(local)->conf; + struct ieee80211_bss *bss; struct sta_info *sta; u32 changed = 0, config_changed = 0; @@ -774,6 +778,15 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ieee80211_sta_tear_down_BA_sessions(sta); + bss = ieee80211_rx_bss_get(local, ifmgd->bssid, + conf->channel->center_freq, + ifmgd->ssid, ifmgd->ssid_len); + + if (bss) { + cfg80211_unhold_bss(&bss->cbss); + ieee80211_rx_bss_put(local, bss); + } + if (self_disconnected) { if (deauth) ieee80211_send_deauth_disassoc(sdata, @@ -925,6 +938,33 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, jiffies + IEEE80211_MONITORING_INTERVAL); } +void ieee80211_beacon_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); + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + + printk(KERN_DEBUG "%s: driver reports beacon loss from AP %pM " + "- sending probe request\n", sdata->dev->name, + sdata->u.mgd.bssid); + + ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL; + ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid, + ifmgd->ssid_len, NULL, 0); + + mod_timer(&ifmgd->timer, jiffies + IEEE80211_MONITORING_INTERVAL); +} + +void ieee80211_beacon_loss(struct ieee80211_vif *vif) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + + queue_work(sdata->local->hw.workqueue, + &sdata->u.mgd.beacon_loss_work); +} +EXPORT_SYMBOL(ieee80211_beacon_loss); + static void ieee80211_associated(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; @@ -959,7 +999,13 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata) goto unlock; } - if (time_after(jiffies, + /* + * Beacon filtering is only enabled with power save and then the + * stack should not check for beacon loss. + */ + if (!((local->hw.flags & IEEE80211_HW_BEACON_FILTER) && + (local->hw.conf.flags & IEEE80211_CONF_PS)) && + time_after(jiffies, ifmgd->last_beacon + IEEE80211_MONITORING_INTERVAL)) { printk(KERN_DEBUG "%s: beacon loss from AP %pM " "- sending probe request\n", @@ -1869,6 +1915,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) ifmgd = &sdata->u.mgd; INIT_WORK(&ifmgd->work, ieee80211_sta_work); INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work); + INIT_WORK(&ifmgd->beacon_loss_work, ieee80211_beacon_loss_work); setup_timer(&ifmgd->timer, ieee80211_sta_timer, (unsigned long) sdata); setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer, -- cgit v1.2.3 From 4a48e2a484e5cf99da4795cf2d6916e057d533ad Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Mon, 23 Mar 2009 12:15:43 +0100 Subject: ar9170: simplify & deBUG tx_status queueing and reporting This patch simplifies the tx_status report code by using four tx_queues per station instead of only one. (the skb lookup should be in O(1) now :-p ). Also, it fixes a really obvious copy&paste bug in the janitor work code and adds back a few spilled bits to the hardware definition header about QoS. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ar9170/ar9170.h | 2 +- drivers/net/wireless/ar9170/hw.h | 11 +++ drivers/net/wireless/ar9170/main.c | 139 ++++++++++------------------------- 3 files changed, 49 insertions(+), 103 deletions(-) diff --git a/drivers/net/wireless/ar9170/ar9170.h b/drivers/net/wireless/ar9170/ar9170.h index c49d7138d57..f4fb2e94aea 100644 --- a/drivers/net/wireless/ar9170/ar9170.h +++ b/drivers/net/wireless/ar9170/ar9170.h @@ -159,7 +159,7 @@ struct ar9170 { }; struct ar9170_sta_info { - struct sk_buff_head tx_status; + struct sk_buff_head tx_status[__AR9170_NUM_TXQ]; }; #define IS_STARTED(a) (a->state >= AR9170_STARTED) diff --git a/drivers/net/wireless/ar9170/hw.h b/drivers/net/wireless/ar9170/hw.h index ad205f82f60..13091bd9d81 100644 --- a/drivers/net/wireless/ar9170/hw.h +++ b/drivers/net/wireless/ar9170/hw.h @@ -397,10 +397,21 @@ struct ar9170_cmd_response { }; } __packed; +/* QoS */ + /* mac80211 queue to HW/FW map */ static const u8 ar9170_qos_hwmap[4] = { 3, 2, 0, 1 }; /* HW/FW queue to mac80211 map */ static const u8 ar9170_qos_mac80211map[4] = { 2, 3, 1, 0 }; +enum ar9170_txq { + AR9170_TXQ_BE, + AR9170_TXQ_BK, + AR9170_TXQ_VI, + AR9170_TXQ_VO, + + __AR9170_NUM_TXQ, +}; + #endif /* __AR9170_HW_H */ diff --git a/drivers/net/wireless/ar9170/main.c b/drivers/net/wireless/ar9170/main.c index a3b5323743c..f8c2357a173 100644 --- a/drivers/net/wireless/ar9170/main.c +++ b/drivers/net/wireless/ar9170/main.c @@ -277,54 +277,6 @@ static struct sk_buff *ar9170_find_skb_in_queue(struct ar9170 *ar, return NULL; } -static struct sk_buff *ar9170_find_skb_in_queue_by_mac(struct ar9170 *ar, - const u8 *mac, - struct sk_buff_head *q) -{ - unsigned long flags; - struct sk_buff *skb; - - spin_lock_irqsave(&q->lock, flags); - skb_queue_walk(q, skb) { - struct ar9170_tx_control *txc = (void *) skb->data; - struct ieee80211_hdr *hdr = (void *) txc->frame_data; - - if (compare_ether_addr(ieee80211_get_DA(hdr), mac)) - continue; - - __skb_unlink(skb, q); - spin_unlock_irqrestore(&q->lock, flags); - return skb; - } - spin_unlock_irqrestore(&q->lock, flags); - return NULL; -} - -static struct sk_buff *ar9170_find_skb_in_queue_by_txcq(struct ar9170 *ar, - const u32 queue, - struct sk_buff_head *q) -{ - unsigned long flags; - struct sk_buff *skb; - - spin_lock_irqsave(&q->lock, flags); - skb_queue_walk(q, skb) { - struct ar9170_tx_control *txc = (void *) skb->data; - u32 txc_queue = (le32_to_cpu(txc->phy_control) & - AR9170_TX_PHY_QOS_MASK) >> - AR9170_TX_PHY_QOS_SHIFT; - - if (queue != txc_queue) - continue; - - __skb_unlink(skb, q); - spin_unlock_irqrestore(&q->lock, flags); - return skb; - } - spin_unlock_irqrestore(&q->lock, flags); - return NULL; -} - static struct sk_buff *ar9170_find_queued_skb(struct ar9170 *ar, const u8 *mac, const u32 queue) { @@ -344,21 +296,21 @@ static struct sk_buff *ar9170_find_queued_skb(struct ar9170 *ar, const u8 *mac, if (likely(sta)) { struct ar9170_sta_info *sta_priv = (void *) sta->drv_priv; - skb = ar9170_find_skb_in_queue_by_txcq(ar, queue, - &sta_priv->tx_status); - } else { - /* STA is not in database (yet/anymore). */ + skb = skb_dequeue(&sta_priv->tx_status[queue]); + rcu_read_unlock(); + if (likely(skb)) + return skb; + } else + rcu_read_unlock(); - /* scan the waste bin for likely candidates */ + /* scan the waste queue for candidates */ + skb = ar9170_find_skb_in_queue(ar, mac, queue, + &ar->global_tx_status_waste); + if (!skb) { + /* so it still _must_ be in the global list. */ skb = ar9170_find_skb_in_queue(ar, mac, queue, - &ar->global_tx_status_waste); - if (!skb) { - /* so it still _must_ be in the global list. */ - skb = ar9170_find_skb_in_queue(ar, mac, queue, - &ar->global_tx_status); - } + &ar->global_tx_status); } - rcu_read_unlock(); #ifdef AR9170_QUEUE_DEBUG if (unlikely((!skb) && net_ratelimit())) { @@ -367,7 +319,6 @@ static struct sk_buff *ar9170_find_queued_skb(struct ar9170 *ar, const u8 *mac, wiphy_name(ar->hw->wiphy), mac, queue); } #endif /* AR9170_QUEUE_DEBUG */ - return skb; } @@ -381,9 +332,13 @@ static struct sk_buff *ar9170_find_queued_skb(struct ar9170 *ar, const u8 *mac, static void ar9170_tx_status_janitor(struct work_struct *work) { struct ar9170 *ar = container_of(work, struct ar9170, - filter_config_work); + tx_status_janitor.work); struct sk_buff *skb; + if (unlikely(!IS_STARTED(ar))) + return ; + + mutex_lock(&ar->mutex); /* recycle the garbage back to mac80211... one by one. */ while ((skb = skb_dequeue(&ar->global_tx_status_waste))) { #ifdef AR9170_QUEUE_DEBUG @@ -395,8 +350,6 @@ static void ar9170_tx_status_janitor(struct work_struct *work) AR9170_TX_STATUS_FAILED); } - - while ((skb = skb_dequeue(&ar->global_tx_status))) { #ifdef AR9170_QUEUE_DEBUG printk(KERN_DEBUG "%s: moving frame into waste queue =>\n", @@ -411,6 +364,8 @@ static void ar9170_tx_status_janitor(struct work_struct *work) if (skb_queue_len(&ar->global_tx_status_waste) > 0) queue_delayed_work(ar->hw->workqueue, &ar->tx_status_janitor, msecs_to_jiffies(100)); + + mutex_unlock(&ar->mutex); } static void ar9170_handle_command_response(struct ar9170 *ar, @@ -1012,7 +967,7 @@ int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (info->control.sta) { sta_info = (void *) info->control.sta->drv_priv; - skb_queue_tail(&sta_info->tx_status, skb); + skb_queue_tail(&sta_info->tx_status[queue], skb); } else { skb_queue_tail(&ar->global_tx_status, skb); @@ -1025,7 +980,7 @@ int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) err = ar->tx(ar, skb, tx_status, 0); if (unlikely(tx_status && err)) { if (info->control.sta) - skb_unlink(skb, &sta_info->tx_status); + skb_unlink(skb, &sta_info->tx_status[queue]); else skb_unlink(skb, &ar->global_tx_status); } @@ -1479,55 +1434,35 @@ static void ar9170_sta_notify(struct ieee80211_hw *hw, struct ar9170 *ar = hw->priv; struct ar9170_sta_info *info = (void *) sta->drv_priv; struct sk_buff *skb; + unsigned int i; switch (cmd) { case STA_NOTIFY_ADD: - skb_queue_head_init(&info->tx_status); - - /* - * do we already have a frame that needs tx_status - * from this station in our queue? - * If so then transfer them to the new station queue. - */ - - /* preserve the correct order - check the waste bin first */ - while ((skb = ar9170_find_skb_in_queue_by_mac(ar, sta->addr, - &ar->global_tx_status_waste))) - skb_queue_tail(&info->tx_status, skb); - - /* now the still pending frames */ - while ((skb = ar9170_find_skb_in_queue_by_mac(ar, sta->addr, - &ar->global_tx_status))) - skb_queue_tail(&info->tx_status, skb); - -#ifdef AR9170_QUEUE_DEBUG - printk(KERN_DEBUG "%s: STA[%pM] has %d queued frames =>\n", - wiphy_name(ar->hw->wiphy), sta->addr, - skb_queue_len(&info->tx_status)); - - ar9170_dump_station_tx_status_queue(ar, &info->tx_status); -#endif /* AR9170_QUEUE_DEBUG */ - - + for (i = 0; i < ar->hw->queues; i++) + skb_queue_head_init(&info->tx_status[i]); break; case STA_NOTIFY_REMOVE: /* * transfer all outstanding frames that need a tx_status - * reports to the fallback queue + * reports to the global tx_status queue */ - while ((skb = skb_dequeue(&ar->global_tx_status))) { + for (i = 0; i < ar->hw->queues; i++) { + while ((skb = skb_dequeue(&info->tx_status[i]))) { #ifdef AR9170_QUEUE_DEBUG - printk(KERN_DEBUG "%s: queueing frame in global " - "tx_status queue =>\n", - wiphy_name(ar->hw->wiphy)); + printk(KERN_DEBUG "%s: queueing frame in " + "global tx_status queue =>\n", + wiphy_name(ar->hw->wiphy)); - ar9170_print_txheader(ar, skb); + ar9170_print_txheader(ar, skb); #endif /* AR9170_QUEUE_DEBUG */ - skb_queue_tail(&ar->global_tx_status_waste, skb); + skb_queue_tail(&ar->global_tx_status, skb); + } } + queue_delayed_work(ar->hw->workqueue, &ar->tx_status_janitor, + msecs_to_jiffies(100)); break; default: @@ -1571,7 +1506,7 @@ static int ar9170_conf_tx(struct ieee80211_hw *hw, u16 queue, int ret; mutex_lock(&ar->mutex); - if ((param) && !(queue > 4)) { + if ((param) && !(queue > ar->hw->queues)) { memcpy(&ar->edcf[ar9170_qos_hwmap[queue]], param, sizeof(*param)); @@ -1635,7 +1570,7 @@ void *ar9170_alloc(size_t priv_size) IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM; - ar->hw->queues = 4; + ar->hw->queues = __AR9170_NUM_TXQ; ar->hw->extra_tx_headroom = 8; ar->hw->sta_data_size = sizeof(struct ar9170_sta_info); -- cgit v1.2.3 From 2b874e83c970b45c328ab12239b066a43505454c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Mar 2009 14:10:22 +0100 Subject: mac80211: rate control status only for controlled packets This patch changes mac80211 to not notify the rate control algorithm's tx_status() method when reporting status for a packet that didn't go through the rate control algorithm's get_rate() method. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 4 ++++ net/mac80211/rate.c | 6 ++++-- net/mac80211/rate.h | 4 +++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index d881ed8ad2c..6f3bc4cc53e 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -245,6 +245,9 @@ struct ieee80211_bss_conf { * @IEEE80211_TX_CTL_RATE_CTRL_PROBE: internal to mac80211, can be * set by rate control algorithms to indicate probe rate, will * be cleared for fragmented frames (except on the last fragment) + * @IEEE80211_TX_INTFL_RCALGO: mac80211 internal flag, do not test or + * set this flag in the driver; indicates that the rate control + * algorithm was used and should be notified of TX status */ enum mac80211_tx_control_flags { IEEE80211_TX_CTL_REQ_TX_STATUS = BIT(0), @@ -260,6 +263,7 @@ enum mac80211_tx_control_flags { IEEE80211_TX_STAT_AMPDU = BIT(10), IEEE80211_TX_STAT_AMPDU_NO_BACK = BIT(11), IEEE80211_TX_CTL_RATE_CTRL_PROBE = BIT(12), + IEEE80211_TX_INTFL_RCALGO = BIT(13), }; /** diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 3fa7ab28506..4641f00a1e5 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -219,10 +219,12 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata, info->control.rates[i].count = 1; } - if (sta && sdata->force_unicast_rateidx > -1) + if (sta && sdata->force_unicast_rateidx > -1) { info->control.rates[0].idx = sdata->force_unicast_rateidx; - else + } else { ref->ops->get_rate(ref->priv, ista, priv_sta, txrc); + info->flags |= IEEE80211_TX_INTFL_RCALGO; + } /* * try to enforce the maximum rate the user wanted diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h index b9164c9a956..2ab5ad9e71c 100644 --- a/net/mac80211/rate.h +++ b/net/mac80211/rate.h @@ -44,8 +44,10 @@ static inline void rate_control_tx_status(struct ieee80211_local *local, struct rate_control_ref *ref = local->rate_ctrl; struct ieee80211_sta *ista = &sta->sta; void *priv_sta = sta->rate_ctrl_priv; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb); + if (likely(info->flags & IEEE80211_TX_INTFL_RCALGO)) + ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb); } -- cgit v1.2.3 From a1bfa0eb98e2fedd05a64a4a8943ea8f6f7c5469 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Mon, 23 Mar 2009 15:49:33 +0100 Subject: p54: Kconfig maintenance This patch updates p54's Kconfig entry and removes the out-dated device list. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/Kconfig | 32 ++++---------------------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/drivers/net/wireless/p54/Kconfig b/drivers/net/wireless/p54/Kconfig index 7d6e9d10820..b45d6a4ed1e 100644 --- a/drivers/net/wireless/p54/Kconfig +++ b/drivers/net/wireless/p54/Kconfig @@ -1,9 +1,10 @@ config P54_COMMON tristate "Softmac Prism54 support" - depends on MAC80211 && WLAN_80211 && FW_LOADER && EXPERIMENTAL + depends on MAC80211 && WLAN_80211 && EXPERIMENTAL + select FW_LOADER ---help--- - This is common code for isl38xx based cards. - This module does nothing by itself - the USB/PCI frontends + This is common code for isl38xx/stlc45xx based modules. + This module does nothing by itself - the USB/PCI/SPI front-ends also need to be enabled in order to support any devices. These devices require softmac firmware which can be found at @@ -17,31 +18,6 @@ config P54_USB select CRC32 ---help--- This driver is for USB isl38xx based wireless cards. - These are USB based adapters found in devices such as: - - 3COM 3CRWE254G72 - SMC 2862W-G - Accton 802.11g WN4501 USB - Siemens Gigaset USB - Netgear WG121 - Netgear WG111 - Medion 40900, Roper Europe - Shuttle PN15, Airvast WM168g, IOGear GWU513 - Linksys WUSB54G - Linksys WUSB54G Portable - DLink DWL-G120 Spinnaker - DLink DWL-G122 - Belkin F5D7050 ver 1000 - Cohiba Proto board - SMC 2862W-G version 2 - U.S. Robotics U5 802.11g Adapter - FUJITSU E-5400 USB D1700 - Sagem XG703A - DLink DWL-G120 Cohiba - Spinnaker Proto board - Linksys WUSB54AG - Inventel UR054G - Spinnaker DUT These devices require softmac firmware which can be found at http://prism54.org/ -- cgit v1.2.3 From a3c0b87c4f21911fb7185902dd13f0e3cd7f33f7 Mon Sep 17 00:00:00 2001 From: Lorenzo Nava Date: Sun, 22 Mar 2009 19:15:41 +0100 Subject: b43: fix b43_plcp_get_bitrate_idx_ofdm return type This patch fixes the return type of b43_plcp_get_bitrate_idx_ofdm. If the plcp contains an error, the function return value is 255 instead of -1, and the packet was not dropped. This causes a warning in __ieee80211_rx function because rate idx is out of range. Cc: stable@kernel.org Signed-off-by: Lorenzo Nava Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/net/wireless/b43/xmit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c index 0f53c7e5e01..a63d88841df 100644 --- a/drivers/net/wireless/b43/xmit.c +++ b/drivers/net/wireless/b43/xmit.c @@ -50,7 +50,7 @@ static int b43_plcp_get_bitrate_idx_cck(struct b43_plcp_hdr6 *plcp) } /* Extract the bitrate index out of an OFDM PLCP header. */ -static u8 b43_plcp_get_bitrate_idx_ofdm(struct b43_plcp_hdr6 *plcp, bool aphy) +static int b43_plcp_get_bitrate_idx_ofdm(struct b43_plcp_hdr6 *plcp, bool aphy) { int base = aphy ? 0 : 4; -- cgit v1.2.3 From b726604706ad88d8b28bc487e45e710f58cc19ee Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Mon, 2 Mar 2009 21:55:18 -0500 Subject: ath5k: warn and correct rate for unknown hw rate indexes ath5k sets up a mapping table from the hardware rate index to the rate index used by mac80211; however, we have seen some received frames with incorrect rate indexes. Such frames normally get dropped with a warning in __ieee80211_rx(), but it doesn't include enough information to track down the error. This patch adds a warning to hw_to_driver_rix for any lookups that result in a rate index of -1, then returns a valid rate so the frame can be processed. Changes-licensed-under: 3-Clause-BSD Signed-off-by: Bob Copeland Cc: stable@kernel.org Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 15 ++++++++++++--- drivers/net/wireless/ath5k/base.h | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index f28b86c5d34..6580df2f744 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -1088,9 +1088,18 @@ ath5k_mode_setup(struct ath5k_softc *sc) static inline int ath5k_hw_to_driver_rix(struct ath5k_softc *sc, int hw_rix) { - WARN(hw_rix < 0 || hw_rix >= AR5K_MAX_RATES, - "hw_rix out of bounds: %x\n", hw_rix); - return sc->rate_idx[sc->curband->band][hw_rix]; + int rix; + + /* return base rate on errors */ + if (WARN(hw_rix < 0 || hw_rix >= AR5K_MAX_RATES, + "hw_rix out of bounds: %x\n", hw_rix)) + return 0; + + rix = sc->rate_idx[sc->curband->band][hw_rix]; + if (WARN(rix < 0, "invalid hw_rix: %x\n", hw_rix)) + rix = 0; + + return rix; } /***************\ diff --git a/drivers/net/wireless/ath5k/base.h b/drivers/net/wireless/ath5k/base.h index 20e0d14b41e..822956114cd 100644 --- a/drivers/net/wireless/ath5k/base.h +++ b/drivers/net/wireless/ath5k/base.h @@ -112,7 +112,7 @@ struct ath5k_softc { struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; struct ieee80211_channel channels[ATH_CHAN_MAX]; struct ieee80211_rate rates[IEEE80211_NUM_BANDS][AR5K_MAX_RATES]; - u8 rate_idx[IEEE80211_NUM_BANDS][AR5K_MAX_RATES]; + s8 rate_idx[IEEE80211_NUM_BANDS][AR5K_MAX_RATES]; enum nl80211_iftype opmode; struct ath5k_hw *ah; /* Atheros HW */ -- cgit v1.2.3 From 14344b81ec264efbe59de0183f5ba38650a479a6 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Sat, 21 Mar 2009 00:00:57 +0100 Subject: rt2x00: New USB ID for rt73usb Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt73usb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index 24fdfdfee3d..420fff42c0d 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -2425,6 +2425,8 @@ static struct usb_device_id rt73usb_device_table[] = { { USB_DEVICE(0x0df6, 0x9712), USB_DEVICE_DATA(&rt73usb_ops) }, /* Surecom */ { USB_DEVICE(0x0769, 0x31f3), USB_DEVICE_DATA(&rt73usb_ops) }, + /* Tilgin */ + { USB_DEVICE(0x6933, 0x5001), USB_DEVICE_DATA(&rt73usb_ops) }, /* Philips */ { USB_DEVICE(0x0471, 0x200a), USB_DEVICE_DATA(&rt73usb_ops) }, /* Planex */ -- cgit v1.2.3 From 051b919188650fe4c93ca8701183ae88439388f6 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 23 Mar 2009 18:25:01 -0400 Subject: ath9k: fix dma mapping leak of rx buffer upon rmmod We were claiming DMA buffers on the RX tasklet but never upon a simple module removal. Cc: stable@kernel.org Signed-off-by: FUJITA Tomonori Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/recv.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath9k/recv.c b/drivers/net/wireless/ath9k/recv.c index 917bac7af6f..71cb18d6757 100644 --- a/drivers/net/wireless/ath9k/recv.c +++ b/drivers/net/wireless/ath9k/recv.c @@ -344,8 +344,13 @@ void ath_rx_cleanup(struct ath_softc *sc) list_for_each_entry(bf, &sc->rx.rxbuf, list) { skb = bf->bf_mpdu; - if (skb) + if (skb) { + dma_unmap_single(sc->dev, + bf->bf_buf_addr, + sc->rx.bufsize, + DMA_FROM_DEVICE); dev_kfree_skb(skb); + } } if (sc->rx.rxdma.dd_desc_len != 0) -- cgit v1.2.3 From de00c04ecbb482507ace2197782123446a1cfdca Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Tue, 24 Mar 2009 16:21:55 +0100 Subject: ar9170: single module build This patch restores all-in-one module build procedure for ar9170. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/Makefile | 2 +- drivers/net/wireless/ar9170/Kconfig | 17 +++------------ drivers/net/wireless/ar9170/Makefile | 4 +--- drivers/net/wireless/ar9170/cmd.c | 1 - drivers/net/wireless/ar9170/main.c | 40 ------------------------------------ drivers/net/wireless/ar9170/usb.c | 3 ++- 6 files changed, 7 insertions(+), 60 deletions(-) diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 5e7c9acb6be..50e7fba7f0e 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -57,6 +57,6 @@ obj-$(CONFIG_P54_COMMON) += p54/ obj-$(CONFIG_ATH5K) += ath5k/ obj-$(CONFIG_ATH9K) += ath9k/ -obj-$(CONFIG_AR9170_COMMON) += ar9170/ +obj-$(CONFIG_AR9170_USB) += ar9170/ obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o diff --git a/drivers/net/wireless/ar9170/Kconfig b/drivers/net/wireless/ar9170/Kconfig index f6611876e28..de4281fda12 100644 --- a/drivers/net/wireless/ar9170/Kconfig +++ b/drivers/net/wireless/ar9170/Kconfig @@ -1,17 +1,6 @@ -config AR9170_COMMON - tristate "Atheros AR9170 support" - depends on WLAN_80211 && MAC80211 && EXPERIMENTAL - help - This is common code for AR9170 based devices. - This module does nothing by itself - the USB/(SPI) frontends - also need to be enabled in order to support any devices. - - Say Y if you have the hardware, or M to build a module called - ar9170common. - config AR9170_USB - tristate "Atheros AR9170 USB support" - depends on AR9170_COMMON && USB + tristate "Atheros AR9170 802.11n USB support" + depends on USB && MAC80211 && WLAN_80211 && EXPERIMENTAL select FW_LOADER help This is a driver for the Atheros "otus" 802.11n USB devices. @@ -24,5 +13,5 @@ config AR9170_USB config AR9170_LEDS bool - depends on AR9170_COMMON && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = AR9170_COMMON) + depends on AR9170_USB && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = AR9170_USB) default y diff --git a/drivers/net/wireless/ar9170/Makefile b/drivers/net/wireless/ar9170/Makefile index 59b174dd466..8d91c7ee321 100644 --- a/drivers/net/wireless/ar9170/Makefile +++ b/drivers/net/wireless/ar9170/Makefile @@ -1,5 +1,3 @@ -ar9170common-objs += main.o cmd.o mac.o phy.o led.o -ar9170usb-objs += usb.o +ar9170usb-objs := usb.o main.o cmd.o mac.o phy.o led.o -obj-$(CONFIG_AR9170_COMMON) += ar9170common.o obj-$(CONFIG_AR9170_USB) += ar9170usb.o diff --git a/drivers/net/wireless/ar9170/cmd.c b/drivers/net/wireless/ar9170/cmd.c index fd5625c4e9d..f57a6200167 100644 --- a/drivers/net/wireless/ar9170/cmd.c +++ b/drivers/net/wireless/ar9170/cmd.c @@ -127,4 +127,3 @@ int ar9170_echo_test(struct ar9170 *ar, u32 v) return 0; } -EXPORT_SYMBOL_GPL(ar9170_echo_test); diff --git a/drivers/net/wireless/ar9170/main.c b/drivers/net/wireless/ar9170/main.c index f8c2357a173..5996ff9f7f4 100644 --- a/drivers/net/wireless/ar9170/main.c +++ b/drivers/net/wireless/ar9170/main.c @@ -37,14 +37,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* - * BIG FAT TODO: - * - * By the looks of things: these devices share a lot of things like - * EEPROM layout/design and PHY code with other Atheros WIFI products. - * So this driver/library will eventually become ath9k code... or vice versa ;-) - */ - #include #include #include @@ -56,9 +48,6 @@ static int modparam_nohwcrypt; module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); -MODULE_AUTHOR("Johannes Berg "); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Atheros shared code for AR9170 wireless devices"); #define RATE(_bitrate, _hw_rate, _txpidx, _flags) { \ .bitrate = (_bitrate), \ @@ -247,7 +236,6 @@ void ar9170_handle_tx_status(struct ar9170 *ar, struct sk_buff *skb, skb_pull(skb, sizeof(struct ar9170_tx_control)); ieee80211_tx_status_irqsafe(ar->hw, skb); } -EXPORT_SYMBOL_GPL(ar9170_handle_tx_status); static struct sk_buff *ar9170_find_skb_in_queue(struct ar9170 *ar, const u8 *mac, @@ -630,13 +618,6 @@ static void ar9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len) ieee80211_rx_irqsafe(ar->hw, skb, &status); } -/* - * TODO: - * It looks like AR9170 supports more than just the USB transport interface. - * Unfortunately, there is no available information what parts of the - * precendent and following code fragments is device specific and what not. - * For now, everything stays here, until some SPI chips pop up. - */ void ar9170_rx(struct ar9170 *ar, struct sk_buff *skb) { unsigned int i, tlen, resplen; @@ -694,7 +675,6 @@ void ar9170_rx(struct ar9170 *ar, struct sk_buff *skb) printk(KERN_ERR "%s: buffer remains!\n", wiphy_name(ar->hw->wiphy)); } -EXPORT_SYMBOL_GPL(ar9170_rx); #define AR9170_FILL_QUEUE(queue, ai_fs, cwmin, cwmax, _txop) \ do { \ @@ -1582,7 +1562,6 @@ void *ar9170_alloc(size_t priv_size) return ar; } -EXPORT_SYMBOL_GPL(ar9170_alloc); static int ar9170_read_eeprom(struct ar9170 *ar) { @@ -1680,7 +1659,6 @@ err_unreg: err_out: return err; } -EXPORT_SYMBOL_GPL(ar9170_register); void ar9170_unregister(struct ar9170 *ar) { @@ -1691,21 +1669,3 @@ void ar9170_unregister(struct ar9170 *ar) ieee80211_unregister_hw(ar->hw); mutex_destroy(&ar->mutex); } -EXPORT_SYMBOL_GPL(ar9170_unregister); - -static int __init ar9170_init(void) -{ - if (modparam_nohwcrypt) - printk(KERN_INFO "ar9170: cryptographic acceleration " - "disabled.\n"); - - return 0; -} - -static void __exit ar9170_exit(void) -{ - -} - -module_init(ar9170_init); -module_exit(ar9170_exit); diff --git a/drivers/net/wireless/ar9170/usb.c b/drivers/net/wireless/ar9170/usb.c index ede511e4047..ad296840893 100644 --- a/drivers/net/wireless/ar9170/usb.c +++ b/drivers/net/wireless/ar9170/usb.c @@ -47,9 +47,10 @@ #include "hw.h" #include "usb.h" +MODULE_AUTHOR("Johannes Berg "); MODULE_AUTHOR("Christian Lamparter "); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("USB Driver for Atheros AR9170 based devices"); +MODULE_DESCRIPTION("Atheros AR9170 802.11n USB wireless"); MODULE_FIRMWARE("ar9170-1.fw"); MODULE_FIRMWARE("ar9170-2.fw"); -- cgit v1.2.3 From 5a0fe8ac70f81b5b91156736066e6445d0dcc61f Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Mon, 23 Mar 2009 23:35:37 -0400 Subject: ath5k: properly drop packets from ops->tx We shouldn't return NETDEV_TX_BUSY from the TX callback, especially after we've mucked with the sk_buffs. Drop the packets and return NETDEV_TX_OK. Changes-licensed-under: 3-Clause-BSD Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 6580df2f744..5d57d774e46 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -2562,7 +2562,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (skb_headroom(skb) < padsize) { ATH5K_ERR(sc, "tx hdrlen not %%4: %d not enough" " headroom to pad %d\n", hdrlen, padsize); - return NETDEV_TX_BUSY; + goto drop_packet; } skb_push(skb, padsize); memmove(skb->data, skb->data+padsize, hdrlen); @@ -2573,7 +2573,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) ATH5K_ERR(sc, "no further txbuf available, dropping packet\n"); spin_unlock_irqrestore(&sc->txbuflock, flags); ieee80211_stop_queue(hw, skb_get_queue_mapping(skb)); - return NETDEV_TX_BUSY; + goto drop_packet; } bf = list_first_entry(&sc->txbuf, struct ath5k_buf, list); list_del(&bf->list); @@ -2590,10 +2590,12 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) list_add_tail(&bf->list, &sc->txbuf); sc->txbuf_len++; spin_unlock_irqrestore(&sc->txbuflock, flags); - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; + goto drop_packet; } + return NETDEV_TX_OK; +drop_packet: + dev_kfree_skb_any(skb); return NETDEV_TX_OK; } -- cgit v1.2.3 From 3832c287f11ba001bbe48e9be8c59cb9f71f6b43 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 24 Mar 2009 08:46:57 +0100 Subject: mac80211: fix RX path My previous patch ("mac80211: remove mixed-cell and userspace MLME code") was too obvious to me, so obvious that a stupid bug crept in. The IBSS RX function must be invoked for IBSS, of course, not anything != IBSS. Reported-by: Larry Finger Signed-off-by: Johannes Berg Tested-by: Larry Finger Signed-off-by: John W. Linville --- net/mac80211/rx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index eff59f36e8e..64ebe664eff 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1884,7 +1884,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) if (ieee80211_vif_is_mesh(&sdata->vif)) return ieee80211_mesh_rx_mgmt(sdata, rx->skb, rx->status); - if (sdata->vif.type != NL80211_IFTYPE_ADHOC) + if (sdata->vif.type == NL80211_IFTYPE_ADHOC) return ieee80211_ibss_rx_mgmt(sdata, rx->skb, rx->status); if (sdata->vif.type == NL80211_IFTYPE_STATION) -- cgit v1.2.3 From 4bbf4d56583dd52c429d88f43cb614bdbe5deea6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 24 Mar 2009 09:35:46 +0100 Subject: cfg80211: fix locking in nl80211_set_wiphy Luis reports that there's a circular locking dependency; this is because cfg80211_dev_rename() will acquire the cfg80211_mutex while the device mutex is held, while this normally is done the other way around. The solution is to open-code the device-getting in nl80211_set_wiphy and require holding the mutex around cfg80211_dev_rename rather than acquiring it within. Also fix a bug -- rtnl locking is expected by drivers so we need to provide it. Reported-by: Luis R. Rodriguez Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 30 ++++++++++-------------------- net/wireless/core.h | 3 +++ net/wireless/nl80211.c | 28 ++++++++++++++++++++-------- 3 files changed, 33 insertions(+), 28 deletions(-) diff --git a/net/wireless/core.c b/net/wireless/core.c index 17fe3904974..d1f556535f6 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -87,7 +87,7 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx) } /* requires cfg80211_mutex to be held! */ -static struct cfg80211_registered_device * +struct cfg80211_registered_device * __cfg80211_drv_from_info(struct genl_info *info) { int ifindex; @@ -176,13 +176,14 @@ void cfg80211_put_dev(struct cfg80211_registered_device *drv) mutex_unlock(&drv->mtx); } +/* requires cfg80211_mutex to be held */ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, char *newname) { struct cfg80211_registered_device *drv; int wiphy_idx, taken = -1, result, digits; - mutex_lock(&cfg80211_mutex); + assert_cfg80211_lock(); /* prohibit calling the thing phy%d when %d is not its number */ sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken); @@ -195,30 +196,23 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, * deny the name if it is phy where is printed * without leading zeroes. taken == strlen(newname) here */ - result = -EINVAL; if (taken == strlen(PHY_NAME) + digits) - goto out_unlock; + return -EINVAL; } /* Ignore nop renames */ - result = 0; if (strcmp(newname, dev_name(&rdev->wiphy.dev)) == 0) - goto out_unlock; + return 0; /* Ensure another device does not already have this name. */ - list_for_each_entry(drv, &cfg80211_drv_list, list) { - result = -EINVAL; + list_for_each_entry(drv, &cfg80211_drv_list, list) if (strcmp(newname, dev_name(&drv->wiphy.dev)) == 0) - goto out_unlock; - } + return -EINVAL; - /* this will only check for collisions in sysfs - * which is not even always compiled in. - */ result = device_rename(&rdev->wiphy.dev, newname); if (result) - goto out_unlock; + return result; if (rdev->wiphy.debugfsdir && !debugfs_rename(rdev->wiphy.debugfsdir->d_parent, @@ -228,13 +222,9 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, printk(KERN_ERR "cfg80211: failed to rename debugfs dir to %s!\n", newname); - result = 0; -out_unlock: - mutex_unlock(&cfg80211_mutex); - if (result == 0) - nl80211_notify_dev_rename(rdev); + nl80211_notify_dev_rename(rdev); - return result; + return 0; } /* exported functions */ diff --git a/net/wireless/core.h b/net/wireless/core.h index 97a6fd8b2b0..d43daa236ef 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -99,6 +99,9 @@ struct cfg80211_internal_bss { struct cfg80211_registered_device *cfg80211_drv_by_wiphy_idx(int wiphy_idx); int get_wiphy_idx(struct wiphy *wiphy); +struct cfg80211_registered_device * +__cfg80211_drv_from_info(struct genl_info *info); + /* * This function returns a pointer to the driver * that the genl_info item that is passed refers to. diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 8808431bd58..353e1a4ece8 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -366,16 +366,26 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) int result = 0, rem_txq_params = 0; struct nlattr *nl_txq_params; - rdev = cfg80211_get_dev_from_info(info); - if (IS_ERR(rdev)) - return PTR_ERR(rdev); + rtnl_lock(); + + mutex_lock(&cfg80211_mutex); + + rdev = __cfg80211_drv_from_info(info); + if (IS_ERR(rdev)) { + result = PTR_ERR(rdev); + goto unlock; + } - if (info->attrs[NL80211_ATTR_WIPHY_NAME]) { + mutex_lock(&rdev->mtx); + + if (info->attrs[NL80211_ATTR_WIPHY_NAME]) result = cfg80211_dev_rename( rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME])); - if (result) - goto bad_res; - } + + mutex_unlock(&cfg80211_mutex); + + if (result) + goto bad_res; if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) { struct ieee80211_txq_params txq_params; @@ -471,7 +481,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) bad_res: - cfg80211_put_dev(rdev); + mutex_unlock(&rdev->mtx); + unlock: + rtnl_unlock(); return result; } -- cgit v1.2.3 From dd970e43d86c253ff159d9668499aaf42d175722 Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Tue, 24 Mar 2009 10:36:48 +0100 Subject: b43: Add BCM4307 PCI-ID 00:09.0 Network controller [0280]: Broadcom Corporation BCM4307 Ethernet Controller [14e4:4306] (rev 03) Subsystem: Broadcom Corporation BCM4307 Ethernet Controller [14e4:4306] Flags: bus master, fast devsel, latency 32, IRQ 10 Memory at d7000000 (32-bit, non-prefetchable) [size=8K] Kernel driver in use: b43-pci-bridge Kernel modules: ssb Reported-by: yoann Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/ssb/b43_pci_bridge.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/ssb/b43_pci_bridge.c b/drivers/ssb/b43_pci_bridge.c index 27a677584a4..ef9c6a04ad8 100644 --- a/drivers/ssb/b43_pci_bridge.c +++ b/drivers/ssb/b43_pci_bridge.c @@ -18,6 +18,7 @@ static const struct pci_device_id b43_pci_bridge_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4301) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4306) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4307) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4311) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4312) }, -- cgit v1.2.3 From 08df05aa9b25f3079585855506022bb33a011183 Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Tue, 24 Mar 2009 10:02:54 -0700 Subject: iwlwifi: show current driver status in user readable format change the display of current driver status bit to user readable format for better and easier debugging Signed-off-by: Wey-Yi Guy Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-debugfs.c | 54 +++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c index 36cfeccfafb..64eb585f157 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c +++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c @@ -425,6 +425,56 @@ static ssize_t iwl_dbgfs_channels_read(struct file *file, char __user *user_buf, return ret; } +static ssize_t iwl_dbgfs_status_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + char buf[512]; + int pos = 0; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_HCMD_ACTIVE:\t %d\n", + test_bit(STATUS_HCMD_ACTIVE, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_HCMD_SYNC_ACTIVE: %d\n", + test_bit(STATUS_HCMD_SYNC_ACTIVE, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_INT_ENABLED:\t %d\n", + test_bit(STATUS_INT_ENABLED, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_RF_KILL_HW:\t %d\n", + test_bit(STATUS_RF_KILL_HW, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_RF_KILL_SW:\t %d\n", + test_bit(STATUS_RF_KILL_SW, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_INIT:\t\t %d\n", + test_bit(STATUS_INIT, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_ALIVE:\t\t %d\n", + test_bit(STATUS_ALIVE, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_READY:\t\t %d\n", + test_bit(STATUS_READY, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_TEMPERATURE:\t %d\n", + test_bit(STATUS_TEMPERATURE, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_GEO_CONFIGURED:\t %d\n", + test_bit(STATUS_GEO_CONFIGURED, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_EXIT_PENDING:\t %d\n", + test_bit(STATUS_EXIT_PENDING, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_IN_SUSPEND:\t %d\n", + test_bit(STATUS_IN_SUSPEND, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_STATISTICS:\t %d\n", + test_bit(STATUS_STATISTICS, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCANNING:\t %d\n", + test_bit(STATUS_SCANNING, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_ABORTING:\t %d\n", + test_bit(STATUS_SCAN_ABORTING, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_HW:\t\t %d\n", + test_bit(STATUS_SCAN_HW, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_POWER_PMI:\t %d\n", + test_bit(STATUS_POWER_PMI, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_FW_ERROR:\t %d\n", + test_bit(STATUS_FW_ERROR, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_MODE_PENDING:\t %d\n", + test_bit(STATUS_MODE_PENDING, &priv->status)); + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + DEBUGFS_READ_WRITE_FILE_OPS(sram); DEBUGFS_WRITE_FILE_OPS(log_event); DEBUGFS_READ_FILE_OPS(eeprom); @@ -432,6 +482,7 @@ DEBUGFS_READ_FILE_OPS(stations); DEBUGFS_READ_FILE_OPS(rx_statistics); DEBUGFS_READ_FILE_OPS(tx_statistics); DEBUGFS_READ_FILE_OPS(channels); +DEBUGFS_READ_FILE_OPS(status); /* * Create the debugfs files and directories @@ -466,7 +517,7 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name) DEBUGFS_ADD_FILE(rx_statistics, data); DEBUGFS_ADD_FILE(tx_statistics, data); DEBUGFS_ADD_FILE(channels, data); - DEBUGFS_ADD_X32(status, data, (u32 *)&priv->status); + DEBUGFS_ADD_FILE(status, data); DEBUGFS_ADD_BOOL(disable_sensitivity, rf, &priv->disable_sens_cal); DEBUGFS_ADD_BOOL(disable_chain_noise, rf, &priv->disable_chain_noise_cal); @@ -496,6 +547,7 @@ void iwl_dbgfs_unregister(struct iwl_priv *priv) DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_log_event); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_stations); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_channels); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_status); DEBUGFS_REMOVE(priv->dbgfs->dir_data); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_rf_files.file_disable_sensitivity); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_rf_files.file_disable_chain_noise); -- cgit v1.2.3 From 2de8e0d999b8790861cd3749bec2236ccc1c8110 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Mar 2009 17:28:35 +0100 Subject: mac80211: rewrite fragmentation Fragmentation currently uses an allocated array to store the fragment skbs, and then keeps track of which have been sent and which are still pending etc. This is rather complicated; make it simpler by just chaining the fragments into skb->next and removing from that list when sent. Also simplifies all code that needs to touch fragments, since it now only needs to walk the skb->next list. This is a prerequisite for fixing the stored packet code, which I need to do for proper aggregation packet storing. Signed-off-by: Johannes Berg Reviewed-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 7 - net/mac80211/tx.c | 322 +++++++++++++++++++++------------------------ net/mac80211/util.c | 19 +-- net/mac80211/wep.c | 21 +-- net/mac80211/wpa.c | 28 ++-- 5 files changed, 173 insertions(+), 224 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index acba78e1a5c..785f6363a6f 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -149,11 +149,6 @@ struct ieee80211_tx_data { struct ieee80211_channel *channel; - /* Extra fragments (in addition to the first fragment - * in skb) */ - struct sk_buff **extra_frag; - int num_extra_frag; - u16 ethertype; unsigned int flags; }; @@ -191,8 +186,6 @@ struct ieee80211_rx_data { struct ieee80211_tx_stored_packet { struct sk_buff *skb; - struct sk_buff **extra_frag; - int num_extra_frag; }; struct beacon_data { diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index f3f240c6901..51bf49cc75b 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -34,8 +34,7 @@ #define IEEE80211_TX_OK 0 #define IEEE80211_TX_AGAIN 1 -#define IEEE80211_TX_FRAG_AGAIN 2 -#define IEEE80211_TX_PENDING 3 +#define IEEE80211_TX_PENDING 2 /* misc utils */ @@ -702,17 +701,62 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx) return TX_CONTINUE; } +static int ieee80211_fragment(struct ieee80211_local *local, + struct sk_buff *skb, int hdrlen, + int frag_threshold) +{ + struct sk_buff *tail = skb, *tmp; + int per_fragm = frag_threshold - hdrlen - FCS_LEN; + int pos = hdrlen + per_fragm; + int rem = skb->len - hdrlen - per_fragm; + + if (WARN_ON(rem < 0)) + return -EINVAL; + + while (rem) { + int fraglen = per_fragm; + + if (fraglen > rem) + fraglen = rem; + rem -= fraglen; + tmp = dev_alloc_skb(local->tx_headroom + + frag_threshold + + IEEE80211_ENCRYPT_HEADROOM + + IEEE80211_ENCRYPT_TAILROOM); + if (!tmp) + return -ENOMEM; + tail->next = tmp; + tail = tmp; + skb_reserve(tmp, local->tx_headroom + + IEEE80211_ENCRYPT_HEADROOM); + /* copy control information */ + memcpy(tmp->cb, skb->cb, sizeof(tmp->cb)); + skb_copy_queue_mapping(tmp, skb); + tmp->priority = skb->priority; + tmp->do_not_encrypt = skb->do_not_encrypt; + tmp->dev = skb->dev; + tmp->iif = skb->iif; + + /* copy header and data */ + memcpy(skb_put(tmp, hdrlen), skb->data, hdrlen); + memcpy(skb_put(tmp, fraglen), skb->data + pos, fraglen); + + pos += fraglen; + } + + skb->len = hdrlen + per_fragm; + return 0; +} + static ieee80211_tx_result debug_noinline ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) { - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; - size_t hdrlen, per_fragm, num_fragm, payload_len, left; - struct sk_buff **frags, *first, *frag; - int i; - u16 seq; - u8 *pos; + struct sk_buff *skb = tx->skb; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *)skb->data; int frag_threshold = tx->local->fragmentation_threshold; + int hdrlen; + int fragnum; if (!(tx->flags & IEEE80211_TX_FRAGMENTED)) return TX_CONTINUE; @@ -725,58 +769,35 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU)) return TX_DROP; - first = tx->skb; - hdrlen = ieee80211_hdrlen(hdr->frame_control); - payload_len = first->len - hdrlen; - per_fragm = frag_threshold - hdrlen - FCS_LEN; - num_fragm = DIV_ROUND_UP(payload_len, per_fragm); - - frags = kzalloc(num_fragm * sizeof(struct sk_buff *), GFP_ATOMIC); - if (!frags) - goto fail; - - hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREFRAGS); - seq = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ; - pos = first->data + hdrlen + per_fragm; - left = payload_len - per_fragm; - for (i = 0; i < num_fragm - 1; i++) { - struct ieee80211_hdr *fhdr; - size_t copylen; - - if (left <= 0) - goto fail; - /* reserve enough extra head and tail room for possible - * encryption */ - frag = frags[i] = - dev_alloc_skb(tx->local->tx_headroom + - frag_threshold + - IEEE80211_ENCRYPT_HEADROOM + - IEEE80211_ENCRYPT_TAILROOM); - if (!frag) - goto fail; - - /* Make sure that all fragments use the same priority so - * that they end up using the same TX queue */ - frag->priority = first->priority; + /* internal error, why is TX_FRAGMENTED set? */ + if (WARN_ON(skb->len <= frag_threshold)) + return TX_DROP; - skb_reserve(frag, tx->local->tx_headroom + - IEEE80211_ENCRYPT_HEADROOM); + /* + * Now fragment the frame. This will allocate all the fragments and + * chain them (using skb as the first fragment) to skb->next. + * During transmission, we will remove the successfully transmitted + * fragments from this list. When the low-level driver rejects one + * of the fragments then we will simply pretend to accept the skb + * but store it away as pending. + */ + if (ieee80211_fragment(tx->local, skb, hdrlen, frag_threshold)) + return TX_DROP; - /* copy TX information */ - info = IEEE80211_SKB_CB(frag); - memcpy(info, first->cb, sizeof(frag->cb)); + /* update duration/seq/flags of fragments */ + fragnum = 0; + do { + int next_len; + const __le16 morefrags = cpu_to_le16(IEEE80211_FCTL_MOREFRAGS); - /* copy/fill in 802.11 header */ - fhdr = (struct ieee80211_hdr *) skb_put(frag, hdrlen); - memcpy(fhdr, first->data, hdrlen); - fhdr->seq_ctrl = cpu_to_le16(seq | ((i + 1) & IEEE80211_SCTL_FRAG)); + hdr = (void *)skb->data; + info = IEEE80211_SKB_CB(skb); - if (i == num_fragm - 2) { - /* clear MOREFRAGS bit for the last fragment */ - fhdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREFRAGS); - } else { + if (skb->next) { + hdr->frame_control |= morefrags; + next_len = skb->next->len; /* * No multi-rate retries for fragmented frames, that * would completely throw off the NAV at other STAs. @@ -787,37 +808,16 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) info->control.rates[4].idx = -1; BUILD_BUG_ON(IEEE80211_TX_MAX_RATES != 5); info->flags &= ~IEEE80211_TX_CTL_RATE_CTRL_PROBE; + } else { + hdr->frame_control &= ~morefrags; + next_len = 0; } - - /* copy data */ - copylen = left > per_fragm ? per_fragm : left; - memcpy(skb_put(frag, copylen), pos, copylen); - - skb_copy_queue_mapping(frag, first); - - frag->do_not_encrypt = first->do_not_encrypt; - frag->dev = first->dev; - frag->iif = first->iif; - - pos += copylen; - left -= copylen; - } - skb_trim(first, hdrlen + per_fragm); - - tx->num_extra_frag = num_fragm - 1; - tx->extra_frag = frags; + hdr->duration_id = ieee80211_duration(tx, 0, next_len); + hdr->seq_ctrl |= cpu_to_le16(fragnum & IEEE80211_SCTL_FRAG); + fragnum++; + } while ((skb = skb->next)); return TX_CONTINUE; - - fail: - if (frags) { - for (i = 0; i < num_fragm - 1; i++) - if (frags[i]) - dev_kfree_skb(frags[i]); - kfree(frags); - } - I802_DEBUG_INC(tx->local->tx_handlers_drop_fragment); - return TX_DROP; } static ieee80211_tx_result debug_noinline @@ -845,27 +845,19 @@ ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx) static ieee80211_tx_result debug_noinline ieee80211_tx_h_calculate_duration(struct ieee80211_tx_data *tx) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; - int next_len, i; - int group_addr = is_multicast_ether_addr(hdr->addr1); - - if (!(tx->flags & IEEE80211_TX_FRAGMENTED)) { - hdr->duration_id = ieee80211_duration(tx, group_addr, 0); - return TX_CONTINUE; - } - - hdr->duration_id = ieee80211_duration(tx, group_addr, - tx->extra_frag[0]->len); + struct sk_buff *skb = tx->skb; + struct ieee80211_hdr *hdr; + int next_len; + bool group_addr; - for (i = 0; i < tx->num_extra_frag; i++) { - if (i + 1 < tx->num_extra_frag) - next_len = tx->extra_frag[i + 1]->len; - else - next_len = 0; + do { + hdr = (void *) skb->data; + next_len = skb->next ? skb->next->len : 0; + group_addr = is_multicast_ether_addr(hdr->addr1); - hdr = (struct ieee80211_hdr *)tx->extra_frag[i]->data; - hdr->duration_id = ieee80211_duration(tx, 0, next_len); - } + hdr->duration_id = + ieee80211_duration(tx, group_addr, next_len); + } while ((skb = skb->next)); return TX_CONTINUE; } @@ -873,19 +865,16 @@ ieee80211_tx_h_calculate_duration(struct ieee80211_tx_data *tx) static ieee80211_tx_result debug_noinline ieee80211_tx_h_stats(struct ieee80211_tx_data *tx) { - int i; + struct sk_buff *skb = tx->skb; if (!tx->sta) return TX_CONTINUE; tx->sta->tx_packets++; - tx->sta->tx_fragments++; - tx->sta->tx_bytes += tx->skb->len; - if (tx->extra_frag) { - tx->sta->tx_fragments += tx->num_extra_frag; - for (i = 0; i < tx->num_extra_frag; i++) - tx->sta->tx_bytes += tx->extra_frag[i]->len; - } + do { + tx->sta->tx_fragments++; + tx->sta->tx_bytes += skb->len; + } while ((skb = skb->next)); return TX_CONTINUE; } @@ -1099,45 +1088,36 @@ static int ieee80211_tx_prepare(struct ieee80211_local *local, return 0; } -static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb, +static int __ieee80211_tx(struct ieee80211_local *local, struct ieee80211_tx_data *tx) { + struct sk_buff *skb = tx->skb, *next; struct ieee80211_tx_info *info; - int ret, i; + int ret; + bool fragm = false; - if (skb) { + local->mdev->trans_start = jiffies; + + while (skb) { if (ieee80211_queue_stopped(&local->hw, skb_get_queue_mapping(skb))) return IEEE80211_TX_PENDING; - ret = local->ops->tx(local_to_hw(local), skb); - if (ret) - return IEEE80211_TX_AGAIN; - local->mdev->trans_start = jiffies; - ieee80211_led_tx(local, 1); - } - if (tx->extra_frag) { - for (i = 0; i < tx->num_extra_frag; i++) { - if (!tx->extra_frag[i]) - continue; - info = IEEE80211_SKB_CB(tx->extra_frag[i]); + if (fragm) { + info = IEEE80211_SKB_CB(skb); info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT | IEEE80211_TX_CTL_FIRST_FRAGMENT); - if (ieee80211_queue_stopped(&local->hw, - skb_get_queue_mapping(tx->extra_frag[i]))) - return IEEE80211_TX_FRAG_AGAIN; - - ret = local->ops->tx(local_to_hw(local), - tx->extra_frag[i]); - if (ret) - return IEEE80211_TX_FRAG_AGAIN; - local->mdev->trans_start = jiffies; - ieee80211_led_tx(local, 1); - tx->extra_frag[i] = NULL; } - kfree(tx->extra_frag); - tx->extra_frag = NULL; + + next = skb->next; + ret = local->ops->tx(local_to_hw(local), skb); + if (ret != NETDEV_TX_OK) + return IEEE80211_TX_AGAIN; + tx->skb = skb = next; + ieee80211_led_tx(local, 1); + fragm = true; } + return IEEE80211_TX_OK; } @@ -1149,7 +1129,6 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx) { struct sk_buff *skb = tx->skb; ieee80211_tx_result res = TX_DROP; - int i; #define CALL_TXH(txh) \ res = txh(tx); \ @@ -1173,11 +1152,13 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx) txh_done: if (unlikely(res == TX_DROP)) { I802_DEBUG_INC(tx->local->tx_handlers_drop); - dev_kfree_skb(skb); - for (i = 0; i < tx->num_extra_frag; i++) - if (tx->extra_frag[i]) - dev_kfree_skb(tx->extra_frag[i]); - kfree(tx->extra_frag); + while (skb) { + struct sk_buff *next; + + next = skb->next; + dev_kfree_skb(skb); + skb = next; + } return -1; } else if (unlikely(res == TX_QUEUED)) { I802_DEBUG_INC(tx->local->tx_handlers_queued); @@ -1194,7 +1175,7 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) struct ieee80211_tx_data tx; ieee80211_tx_result res_prepare; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - int ret, i; + int ret; u16 queue; queue = skb_get_queue_mapping(skb); @@ -1225,7 +1206,7 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) goto out; retry: - ret = __ieee80211_tx(local, skb, &tx); + ret = __ieee80211_tx(local, &tx); if (ret) { struct ieee80211_tx_stored_packet *store; @@ -1240,9 +1221,6 @@ retry: store = &local->pending_packet[queue]; - if (ret == IEEE80211_TX_FRAG_AGAIN) - skb = NULL; - set_bit(queue, local->queues_pending); smp_mb(); /* @@ -1260,22 +1238,23 @@ retry: clear_bit(queue, local->queues_pending); goto retry; } - store->skb = skb; - store->extra_frag = tx.extra_frag; - store->num_extra_frag = tx.num_extra_frag; + store->skb = tx.skb; } out: rcu_read_unlock(); return 0; drop: - if (skb) - dev_kfree_skb(skb); - for (i = 0; i < tx.num_extra_frag; i++) - if (tx.extra_frag[i]) - dev_kfree_skb(tx.extra_frag[i]); - kfree(tx.extra_frag); rcu_read_unlock(); + + skb = tx.skb; + while (skb) { + struct sk_buff *next; + + next = skb->next; + dev_kfree_skb(skb); + skb = next; + } return 0; } @@ -1810,17 +1789,21 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, */ void ieee80211_clear_tx_pending(struct ieee80211_local *local) { - int i, j; - struct ieee80211_tx_stored_packet *store; + struct sk_buff *skb; + int i; for (i = 0; i < local->hw.queues; i++) { if (!test_bit(i, local->queues_pending)) continue; - store = &local->pending_packet[i]; - kfree_skb(store->skb); - for (j = 0; j < store->num_extra_frag; j++) - kfree_skb(store->extra_frag[j]); - kfree(store->extra_frag); + + skb = local->pending_packet[i].skb; + while (skb) { + struct sk_buff *next; + + next = skb->next; + dev_kfree_skb(skb); + skb = next; + } clear_bit(i, local->queues_pending); } } @@ -1854,14 +1837,11 @@ void ieee80211_tx_pending(unsigned long data) netif_start_subqueue(local->mdev, i); store = &local->pending_packet[i]; - tx.extra_frag = store->extra_frag; - tx.num_extra_frag = store->num_extra_frag; tx.flags = 0; - ret = __ieee80211_tx(local, store->skb, &tx); - if (ret) { - if (ret == IEEE80211_TX_FRAG_AGAIN) - store->skb = NULL; - } else { + tx.skb = store->skb; + ret = __ieee80211_tx(local, &tx); + store->skb = tx.skb; + if (!ret) { clear_bit(i, local->queues_pending); ieee80211_wake_queue(&local->hw, i); } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 444bb14c95e..021166c8cce 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -166,18 +166,13 @@ int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data; - - hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); - if (tx->extra_frag) { - struct ieee80211_hdr *fhdr; - int i; - for (i = 0; i < tx->num_extra_frag; i++) { - fhdr = (struct ieee80211_hdr *) - tx->extra_frag[i]->data; - fhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); - } - } + struct sk_buff *skb = tx->skb; + struct ieee80211_hdr *hdr; + + do { + hdr = (struct ieee80211_hdr *) skb->data; + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); + } while ((skb = skb->next)); } int ieee80211_frame_duration(struct ieee80211_local *local, size_t len, diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c index 7043ddc7549..ef73105b306 100644 --- a/net/mac80211/wep.c +++ b/net/mac80211/wep.c @@ -329,24 +329,17 @@ static int wep_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) ieee80211_tx_result ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx) { - int i; + struct sk_buff *skb; ieee80211_tx_set_protected(tx); - if (wep_encrypt_skb(tx, tx->skb) < 0) { - I802_DEBUG_INC(tx->local->tx_handlers_drop_wep); - return TX_DROP; - } - - if (tx->extra_frag) { - for (i = 0; i < tx->num_extra_frag; i++) { - if (wep_encrypt_skb(tx, tx->extra_frag[i])) { - I802_DEBUG_INC(tx->local-> - tx_handlers_drop_wep); - return TX_DROP; - } + skb = tx->skb; + do { + if (wep_encrypt_skb(tx, skb) < 0) { + I802_DEBUG_INC(tx->local->tx_handlers_drop_wep); + return TX_DROP; } - } + } while ((skb = skb->next)); return TX_CONTINUE; } diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index 9101b48ec2a..4f8bfea278f 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -196,19 +196,13 @@ ieee80211_tx_result ieee80211_crypto_tkip_encrypt(struct ieee80211_tx_data *tx) { struct sk_buff *skb = tx->skb; - int i; ieee80211_tx_set_protected(tx); - if (tkip_encrypt_skb(tx, skb) < 0) - return TX_DROP; - - if (tx->extra_frag) { - for (i = 0; i < tx->num_extra_frag; i++) { - if (tkip_encrypt_skb(tx, tx->extra_frag[i])) - return TX_DROP; - } - } + do { + if (tkip_encrypt_skb(tx, skb) < 0) + return TX_DROP; + } while ((skb = skb->next)); return TX_CONTINUE; } @@ -428,19 +422,13 @@ ieee80211_tx_result ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx) { struct sk_buff *skb = tx->skb; - int i; ieee80211_tx_set_protected(tx); - if (ccmp_encrypt_skb(tx, skb) < 0) - return TX_DROP; - - if (tx->extra_frag) { - for (i = 0; i < tx->num_extra_frag; i++) { - if (ccmp_encrypt_skb(tx, tx->extra_frag[i])) - return TX_DROP; - } - } + do { + if (ccmp_encrypt_skb(tx, skb) < 0) + return TX_DROP; + } while ((skb = skb->next)); return TX_CONTINUE; } -- cgit v1.2.3 From f0e72851f7ad108fed20426b46a18ab5fcd5729f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Mar 2009 17:28:36 +0100 Subject: mac80211: fix A-MPDU queue assignment Internally, mac80211 requires the skb's queue mapping to be set to the AC queue, not the virtual A-MPDU queue. This is not done correctly currently, this patch moves the code down to directly before the driver is invoked and adds a comment that it will be moved into the driver later. Since this requires __ieee80211_tx() to have the sta pointer, make sure to provide it in ieee80211_tx_pending(). Signed-off-by: Johannes Berg Reviewed-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/mac80211/tx.c | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 51bf49cc75b..0d97cad84b1 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1024,13 +1024,8 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx, spin_lock_irqsave(&tx->sta->lock, flags); state = &tx->sta->ampdu_mlme.tid_state_tx[tid]; - if (*state == HT_AGG_STATE_OPERATIONAL) { + if (*state == HT_AGG_STATE_OPERATIONAL) info->flags |= IEEE80211_TX_CTL_AMPDU; - if (local->hw.ampdu_queues) - skb_set_queue_mapping( - skb, tx->local->hw.queues + - tx->sta->tid_to_tx_q[tid]); - } spin_unlock_irqrestore(&tx->sta->lock, flags); } @@ -1103,10 +1098,29 @@ static int __ieee80211_tx(struct ieee80211_local *local, skb_get_queue_mapping(skb))) return IEEE80211_TX_PENDING; - if (fragm) { - info = IEEE80211_SKB_CB(skb); + info = IEEE80211_SKB_CB(skb); + + if (fragm) info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT | IEEE80211_TX_CTL_FIRST_FRAGMENT); + + /* + * Internally, we need to have the queue mapping point to + * the real AC queue, not the virtual A-MPDU queue. This + * now finally sets the queue to what the driver wants. + * We will later move this down into the only driver that + * needs it, iwlwifi. + */ + if (tx->sta && local->hw.ampdu_queues && + info->flags & IEEE80211_TX_CTL_AMPDU) { + unsigned long flags; + u8 *qc = ieee80211_get_qos_ctl((void *) skb->data); + int tid = *qc & IEEE80211_QOS_CTL_TID_MASK; + + spin_lock_irqsave(&tx->sta->lock, flags); + skb_set_queue_mapping(skb, local->hw.queues + + tx->sta->tid_to_tx_q[tid]); + spin_unlock_irqrestore(&tx->sta->lock, flags); } next = skb->next; @@ -1817,9 +1831,11 @@ void ieee80211_tx_pending(unsigned long data) struct ieee80211_local *local = (struct ieee80211_local *)data; struct net_device *dev = local->mdev; struct ieee80211_tx_stored_packet *store; + struct ieee80211_hdr *hdr; struct ieee80211_tx_data tx; int i, ret; + rcu_read_lock(); netif_tx_lock_bh(dev); for (i = 0; i < local->hw.queues; i++) { /* Check that this queue is ok */ @@ -1839,6 +1855,8 @@ void ieee80211_tx_pending(unsigned long data) store = &local->pending_packet[i]; tx.flags = 0; tx.skb = store->skb; + hdr = (struct ieee80211_hdr *)tx.skb->data; + tx.sta = sta_info_get(local, hdr->addr1); ret = __ieee80211_tx(local, &tx); store->skb = tx.skb; if (!ret) { @@ -1847,6 +1865,7 @@ void ieee80211_tx_pending(unsigned long data) } } netif_tx_unlock_bh(dev); + rcu_read_unlock(); } /* functions for drivers to get certain frames */ -- cgit v1.2.3 From 2a577d98712a284a612dd51d69db5cb989810dc2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Mar 2009 17:28:37 +0100 Subject: mac80211: rework the pending packets code The pending packets code is quite incomprehensible, uses memory barriers nobody really understands, etc. This patch reworks it entirely, using the queue spinlock, proper stop bits and the skb queues themselves to indicate whether packets are pending or not (rather than a separate variable like before). Signed-off-by: Johannes Berg Reviewed-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 9 +-- net/mac80211/main.c | 2 + net/mac80211/tx.c | 144 +++++++++++++++++++++++++-------------------- net/mac80211/util.c | 22 ++++--- 4 files changed, 98 insertions(+), 79 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 785f6363a6f..6ce62e553dc 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -184,10 +184,6 @@ struct ieee80211_rx_data { u16 tkip_iv16; }; -struct ieee80211_tx_stored_packet { - struct sk_buff *skb; -}; - struct beacon_data { u8 *head, *tail; int head_len, tail_len; @@ -583,6 +579,7 @@ enum queue_stop_reason { IEEE80211_QUEUE_STOP_REASON_CSA, IEEE80211_QUEUE_STOP_REASON_AGGREGATION, IEEE80211_QUEUE_STOP_REASON_SUSPEND, + IEEE80211_QUEUE_STOP_REASON_PENDING, }; struct ieee80211_master_priv { @@ -639,9 +636,7 @@ struct ieee80211_local { struct sta_info *sta_hash[STA_HASH_SIZE]; struct timer_list sta_cleanup; - unsigned long queues_pending[BITS_TO_LONGS(IEEE80211_MAX_QUEUES)]; - unsigned long queues_pending_run[BITS_TO_LONGS(IEEE80211_MAX_QUEUES)]; - struct ieee80211_tx_stored_packet pending_packet[IEEE80211_MAX_QUEUES]; + struct sk_buff_head pending[IEEE80211_MAX_QUEUES]; struct tasklet_struct tx_pending_tasklet; /* number of interfaces with corresponding IFF_ flags */ diff --git a/net/mac80211/main.c b/net/mac80211/main.c index dac68d476bf..a7430e98c53 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -781,6 +781,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, sta_info_init(local); + for (i = 0; i < IEEE80211_MAX_QUEUES; i++) + skb_queue_head_init(&local->pending[i]); tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending, (unsigned long)local); tasklet_disable(&local->tx_pending_tasklet); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 0d97cad84b1..ee1b77f8a80 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1189,12 +1189,14 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) struct ieee80211_tx_data tx; ieee80211_tx_result res_prepare; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - int ret; + struct sk_buff *next; + unsigned long flags; + int ret, retries; u16 queue; queue = skb_get_queue_mapping(skb); - WARN_ON(test_bit(queue, local->queues_pending)); + WARN_ON(!skb_queue_empty(&local->pending[queue])); if (unlikely(skb->len < 10)) { dev_kfree_skb(skb); @@ -1219,40 +1221,52 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) if (invoke_tx_handlers(&tx)) goto out; -retry: + retries = 0; + retry: ret = __ieee80211_tx(local, &tx); - if (ret) { - struct ieee80211_tx_stored_packet *store; - + switch (ret) { + case IEEE80211_TX_OK: + break; + case IEEE80211_TX_AGAIN: /* * Since there are no fragmented frames on A-MPDU * queues, there's no reason for a driver to reject * a frame there, warn and drop it. */ - if (ret != IEEE80211_TX_PENDING) - if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU)) - goto drop; + if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU)) + goto drop; + /* fall through */ + case IEEE80211_TX_PENDING: + skb = tx.skb; - store = &local->pending_packet[queue]; + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - set_bit(queue, local->queues_pending); - smp_mb(); - /* - * When the driver gets out of buffers during sending of - * fragments and calls ieee80211_stop_queue, the netif - * subqueue is stopped. There is, however, a small window - * in which the PENDING bit is not yet set. If a buffer - * gets available in that window (i.e. driver calls - * ieee80211_wake_queue), we would end up with ieee80211_tx - * called with the PENDING bit still set. Prevent this by - * continuing transmitting here when that situation is - * possible to have happened. - */ - if (!__netif_subqueue_stopped(local->mdev, queue)) { - clear_bit(queue, local->queues_pending); + if (__netif_subqueue_stopped(local->mdev, queue)) { + do { + next = skb->next; + skb->next = NULL; + skb_queue_tail(&local->pending[queue], skb); + } while ((skb = next)); + + /* + * Make sure nobody will enable the queue on us + * (without going through the tasklet) nor disable the + * netdev queue underneath the pending handling code. + */ + __set_bit(IEEE80211_QUEUE_STOP_REASON_PENDING, + &local->queue_stop_reasons[queue]); + + spin_unlock_irqrestore(&local->queue_stop_reason_lock, + flags); + } else { + spin_unlock_irqrestore(&local->queue_stop_reason_lock, + flags); + + retries++; + if (WARN(retries > 10, "tx refused but queue active")) + goto drop; goto retry; } - store->skb = tx.skb; } out: rcu_read_unlock(); @@ -1263,8 +1277,6 @@ retry: skb = tx.skb; while (skb) { - struct sk_buff *next; - next = skb->next; dev_kfree_skb(skb); skb = next; @@ -1803,23 +1815,10 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, */ void ieee80211_clear_tx_pending(struct ieee80211_local *local) { - struct sk_buff *skb; int i; - for (i = 0; i < local->hw.queues; i++) { - if (!test_bit(i, local->queues_pending)) - continue; - - skb = local->pending_packet[i].skb; - while (skb) { - struct sk_buff *next; - - next = skb->next; - dev_kfree_skb(skb); - skb = next; - } - clear_bit(i, local->queues_pending); - } + for (i = 0; i < local->hw.queues; i++) + skb_queue_purge(&local->pending[i]); } /* @@ -1830,40 +1829,57 @@ void ieee80211_tx_pending(unsigned long data) { struct ieee80211_local *local = (struct ieee80211_local *)data; struct net_device *dev = local->mdev; - struct ieee80211_tx_stored_packet *store; struct ieee80211_hdr *hdr; + unsigned long flags; struct ieee80211_tx_data tx; int i, ret; + bool next; rcu_read_lock(); netif_tx_lock_bh(dev); - for (i = 0; i < local->hw.queues; i++) { - /* Check that this queue is ok */ - if (__netif_subqueue_stopped(local->mdev, i) && - !test_bit(i, local->queues_pending_run)) - continue; - if (!test_bit(i, local->queues_pending)) { - clear_bit(i, local->queues_pending_run); - ieee80211_wake_queue(&local->hw, i); + for (i = 0; i < local->hw.queues; i++) { + /* + * If queue is stopped by something other than due to pending + * frames, or we have no pending frames, proceed to next queue. + */ + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); + next = false; + if (local->queue_stop_reasons[i] != + BIT(IEEE80211_QUEUE_STOP_REASON_PENDING) || + skb_queue_empty(&local->pending[i])) + next = true; + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); + + if (next) continue; - } - clear_bit(i, local->queues_pending_run); + /* + * start the queue now to allow processing our packets, + * we're under the tx lock here anyway so nothing will + * happen as a result of this + */ netif_start_subqueue(local->mdev, i); - store = &local->pending_packet[i]; - tx.flags = 0; - tx.skb = store->skb; - hdr = (struct ieee80211_hdr *)tx.skb->data; - tx.sta = sta_info_get(local, hdr->addr1); - ret = __ieee80211_tx(local, &tx); - store->skb = tx.skb; - if (!ret) { - clear_bit(i, local->queues_pending); - ieee80211_wake_queue(&local->hw, i); + while (!skb_queue_empty(&local->pending[i])) { + tx.flags = 0; + tx.skb = skb_dequeue(&local->pending[i]); + hdr = (struct ieee80211_hdr *)tx.skb->data; + tx.sta = sta_info_get(local, hdr->addr1); + + ret = __ieee80211_tx(local, &tx); + if (ret != IEEE80211_TX_OK) { + skb_queue_head(&local->pending[i], tx.skb); + break; + } } + + /* Start regular packet processing again. */ + if (skb_queue_empty(&local->pending[i])) + ieee80211_wake_queue_by_reason(&local->hw, i, + IEEE80211_QUEUE_STOP_REASON_PENDING); } + netif_tx_unlock_bh(dev); rcu_read_unlock(); } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 021166c8cce..0247d8022f5 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -365,16 +365,16 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, __clear_bit(reason, &local->queue_stop_reasons[queue]); + if (!skb_queue_empty(&local->pending[queue]) && + local->queue_stop_reasons[queue] == + BIT(IEEE80211_QUEUE_STOP_REASON_PENDING)) + tasklet_schedule(&local->tx_pending_tasklet); + if (local->queue_stop_reasons[queue] != 0) /* someone still has this queue stopped */ return; - if (test_bit(queue, local->queues_pending)) { - set_bit(queue, local->queues_pending_run); - tasklet_schedule(&local->tx_pending_tasklet); - } else { - netif_wake_subqueue(local->mdev, queue); - } + netif_wake_subqueue(local->mdev, queue); } void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, @@ -420,9 +420,15 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, reason = IEEE80211_QUEUE_STOP_REASON_AGGREGATION; } - __set_bit(reason, &local->queue_stop_reasons[queue]); + /* + * Only stop if it was previously running, this is necessary + * for correct pending packets handling because there we may + * start (but not wake) the queue and rely on that. + */ + if (!local->queue_stop_reasons[queue]) + netif_stop_subqueue(local->mdev, queue); - netif_stop_subqueue(local->mdev, queue); + __set_bit(reason, &local->queue_stop_reasons[queue]); } void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue, -- cgit v1.2.3 From 1870cd71e87da1a1afb904f2c84086f487a07135 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Mar 2009 17:28:38 +0100 Subject: mac80211: clean up __ieee80211_tx args __ieee80211_tx takes a struct ieee80211_tx_data argument, but only uses a few of its members, namely 'skb' and 'sta'. Make that explicit, so that less internal knowledge is required in ieee80211_tx_pending and the possibility of introducing errors here is removed. Signed-off-by: Johannes Berg Reviewed-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/mac80211/tx.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index ee1b77f8a80..b909e4090e9 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1084,9 +1084,10 @@ static int ieee80211_tx_prepare(struct ieee80211_local *local, } static int __ieee80211_tx(struct ieee80211_local *local, - struct ieee80211_tx_data *tx) + struct sk_buff **skbp, + struct sta_info *sta) { - struct sk_buff *skb = tx->skb, *next; + struct sk_buff *skb = *skbp, *next; struct ieee80211_tx_info *info; int ret; bool fragm = false; @@ -1111,23 +1112,23 @@ static int __ieee80211_tx(struct ieee80211_local *local, * We will later move this down into the only driver that * needs it, iwlwifi. */ - if (tx->sta && local->hw.ampdu_queues && + if (sta && local->hw.ampdu_queues && info->flags & IEEE80211_TX_CTL_AMPDU) { unsigned long flags; u8 *qc = ieee80211_get_qos_ctl((void *) skb->data); int tid = *qc & IEEE80211_QOS_CTL_TID_MASK; - spin_lock_irqsave(&tx->sta->lock, flags); + spin_lock_irqsave(&sta->lock, flags); skb_set_queue_mapping(skb, local->hw.queues + - tx->sta->tid_to_tx_q[tid]); - spin_unlock_irqrestore(&tx->sta->lock, flags); + sta->tid_to_tx_q[tid]); + spin_unlock_irqrestore(&sta->lock, flags); } next = skb->next; ret = local->ops->tx(local_to_hw(local), skb); if (ret != NETDEV_TX_OK) return IEEE80211_TX_AGAIN; - tx->skb = skb = next; + *skbp = skb = next; ieee80211_led_tx(local, 1); fragm = true; } @@ -1223,7 +1224,7 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) retries = 0; retry: - ret = __ieee80211_tx(local, &tx); + ret = __ieee80211_tx(local, &tx.skb, tx.sta); switch (ret) { case IEEE80211_TX_OK: break; @@ -1831,7 +1832,6 @@ void ieee80211_tx_pending(unsigned long data) struct net_device *dev = local->mdev; struct ieee80211_hdr *hdr; unsigned long flags; - struct ieee80211_tx_data tx; int i, ret; bool next; @@ -1862,14 +1862,15 @@ void ieee80211_tx_pending(unsigned long data) netif_start_subqueue(local->mdev, i); while (!skb_queue_empty(&local->pending[i])) { - tx.flags = 0; - tx.skb = skb_dequeue(&local->pending[i]); - hdr = (struct ieee80211_hdr *)tx.skb->data; - tx.sta = sta_info_get(local, hdr->addr1); + struct sk_buff *skb = skb_dequeue(&local->pending[i]); + struct sta_info *sta; + + hdr = (struct ieee80211_hdr *)skb->data; + sta = sta_info_get(local, hdr->addr1); - ret = __ieee80211_tx(local, &tx); + ret = __ieee80211_tx(local, &skb, sta); if (ret != IEEE80211_TX_OK) { - skb_queue_head(&local->pending[i], tx.skb); + skb_queue_head(&local->pending[i], skb); break; } } -- cgit v1.2.3 From b1720231ca07dee3382980f3b25e6581bd2e54e9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Mar 2009 17:28:39 +0100 Subject: mac80211: unify and fix TX aggregation start When TX aggregation becomes operational, we do a number of steps: 1) print a debug message 2) wake the virtual queue 3) notify the driver Unfortunately, 1) and 3) are only done if the driver is first to reply to the aggregation request, it is, however, possible that the remote station replies before the driver! Thus, unify the code for this and call the new function ieee80211_agg_tx_operational in both places where TX aggregation can become operational. Additionally, rename the driver notification from IEEE80211_AMPDU_TX_RESUME to IEEE80211_AMPDU_TX_OPERATIONAL. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/main.c | 2 +- include/net/mac80211.h | 4 +-- net/mac80211/agg-tx.c | 63 +++++++++++++++++---------------------- 3 files changed, 30 insertions(+), 39 deletions(-) diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index c13e4e53634..13d4e6756c9 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c @@ -2730,7 +2730,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, ieee80211_stop_tx_ba_cb_irqsafe(hw, sta->addr, tid); break; - case IEEE80211_AMPDU_TX_RESUME: + case IEEE80211_AMPDU_TX_OPERATIONAL: ath_tx_aggr_resume(sc, sta, tid); break; default: diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 6f3bc4cc53e..07fe9875506 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1236,14 +1236,14 @@ enum ieee80211_filter_flags { * @IEEE80211_AMPDU_RX_STOP: stop Rx aggregation * @IEEE80211_AMPDU_TX_START: start Tx aggregation * @IEEE80211_AMPDU_TX_STOP: stop Tx aggregation - * @IEEE80211_AMPDU_TX_RESUME: resume TX aggregation + * @IEEE80211_AMPDU_TX_OPERATIONAL: TX aggregation has become operational */ enum ieee80211_ampdu_mlme_action { IEEE80211_AMPDU_RX_START, IEEE80211_AMPDU_RX_STOP, IEEE80211_AMPDU_TX_START, IEEE80211_AMPDU_TX_STOP, - IEEE80211_AMPDU_TX_RESUME, + IEEE80211_AMPDU_TX_OPERATIONAL, }; /** diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index e5776ef1717..fd718e2b29f 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -404,6 +404,27 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) } EXPORT_SYMBOL(ieee80211_start_tx_ba_session); +static void ieee80211_agg_tx_operational(struct ieee80211_local *local, + struct sta_info *sta, u16 tid) +{ +#ifdef CONFIG_MAC80211_HT_DEBUG + printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid); +#endif + + if (local->hw.ampdu_queues) { + /* + * Wake up the A-MPDU queue, we stopped it earlier, + * this will in turn wake the entire AC. + */ + ieee80211_wake_queue_by_reason(&local->hw, + local->hw.queues + sta->tid_to_tx_q[tid], + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + } + + local->ops->ampdu_action(&local->hw, IEEE80211_AMPDU_TX_OPERATIONAL, + &sta->sta, tid, NULL); +} + void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) { struct ieee80211_local *local = hw_to_local(hw); @@ -446,20 +467,8 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) *state |= HT_ADDBA_DRV_READY_MSK; - if (*state == HT_AGG_STATE_OPERATIONAL) { -#ifdef CONFIG_MAC80211_HT_DEBUG - printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid); -#endif - if (hw->ampdu_queues) { - /* - * Wake up this queue, we stopped it earlier, - * this will in turn wake the entire AC. - */ - ieee80211_wake_queue_by_reason(hw, - hw->queues + sta->tid_to_tx_q[tid], - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); - } - } + if (*state == HT_AGG_STATE_OPERATIONAL) + ieee80211_agg_tx_operational(local, sta, tid); out: spin_unlock_bh(&sta->lock); @@ -646,9 +655,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, struct ieee80211_mgmt *mgmt, size_t len) { - struct ieee80211_hw *hw = &local->hw; - u16 capab; - u16 tid, start_seq_num; + u16 capab, tid; u8 *state; capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab); @@ -682,26 +689,10 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, *state |= HT_ADDBA_RECEIVED_MSK; - if (hw->ampdu_queues && *state != curstate && - *state == HT_AGG_STATE_OPERATIONAL) { - /* - * Wake up this queue, we stopped it earlier, - * this will in turn wake the entire AC. - */ - ieee80211_wake_queue_by_reason(hw, - hw->queues + sta->tid_to_tx_q[tid], - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); - } - sta->ampdu_mlme.addba_req_num[tid] = 0; + if (*state != curstate && *state == HT_AGG_STATE_OPERATIONAL) + ieee80211_agg_tx_operational(local, sta, tid); - if (local->ops->ampdu_action) { - (void)local->ops->ampdu_action(hw, - IEEE80211_AMPDU_TX_RESUME, - &sta->sta, tid, &start_seq_num); - } -#ifdef CONFIG_MAC80211_HT_DEBUG - printk(KERN_DEBUG "Resuming TX aggregation for tid %d\n", tid); -#endif /* CONFIG_MAC80211_HT_DEBUG */ + sta->ampdu_mlme.addba_req_num[tid] = 0; } else { sta->ampdu_mlme.addba_req_num[tid]++; ___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR); -- cgit v1.2.3 From a220858d30604902f650074bfac5a7598bc97ea4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Mar 2009 17:28:40 +0100 Subject: mac80211: add skb length sanity checking We just found a bug in zd1211rw where it would reject packets in the ->tx() method but leave them modified, which would cause retransmit attempts with completely bogus skbs, eventually leading to a panic due to not having enough headroom in those. This patch adds a sanity check to mac80211 to catch such driver mistakes; in this case we warn and drop the skb. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/tx.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index b909e4090e9..a0e00c6339c 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1089,7 +1089,7 @@ static int __ieee80211_tx(struct ieee80211_local *local, { struct sk_buff *skb = *skbp, *next; struct ieee80211_tx_info *info; - int ret; + int ret, len; bool fragm = false; local->mdev->trans_start = jiffies; @@ -1125,7 +1125,12 @@ static int __ieee80211_tx(struct ieee80211_local *local, } next = skb->next; + len = skb->len; ret = local->ops->tx(local_to_hw(local), skb); + if (WARN_ON(ret != NETDEV_TX_OK && skb->len != len)) { + dev_kfree_skb(skb); + ret = NETDEV_TX_OK; + } if (ret != NETDEV_TX_OK) return IEEE80211_TX_AGAIN; *skbp = skb = next; -- cgit v1.2.3 From cd8ffc800ce18e558335c4946b2217864fc16045 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Mar 2009 17:28:41 +0100 Subject: mac80211: fix aggregation to not require queue stop Instead of stopping the entire AC queue when enabling aggregation (which was only done for hardware with aggregation queues) buffer the packets for each station, and release them to the pending skb queue once aggregation is turned on successfully. We get a little more code, but it becomes conceptually simpler and we can remove the entire virtual queue mechanism from mac80211 in a follow-up patch. This changes how mac80211 behaves towards drivers that support aggregation but have no hardware queues -- those drivers will now not be handed packets while the aggregation session is being established, but only after it has been fully established. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 4 ++ net/mac80211/agg-tx.c | 136 ++++++++++++++++++++++++++----------------- net/mac80211/ieee80211_i.h | 8 +++ net/mac80211/main.c | 2 + net/mac80211/sta_info.c | 5 ++ net/mac80211/sta_info.h | 2 + net/mac80211/tx.c | 142 ++++++++++++++++++++++++++++++++++++--------- 7 files changed, 221 insertions(+), 78 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 07fe9875506..841f7f804bb 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -248,6 +248,9 @@ struct ieee80211_bss_conf { * @IEEE80211_TX_INTFL_RCALGO: mac80211 internal flag, do not test or * set this flag in the driver; indicates that the rate control * algorithm was used and should be notified of TX status + * @IEEE80211_TX_INTFL_NEED_TXPROCESSING: completely internal to mac80211, + * used to indicate that a pending frame requires TX processing before + * it can be sent out. */ enum mac80211_tx_control_flags { IEEE80211_TX_CTL_REQ_TX_STATUS = BIT(0), @@ -264,6 +267,7 @@ enum mac80211_tx_control_flags { IEEE80211_TX_STAT_AMPDU_NO_BACK = BIT(11), IEEE80211_TX_CTL_RATE_CTRL_PROBE = BIT(12), IEEE80211_TX_INTFL_RCALGO = BIT(13), + IEEE80211_TX_INTFL_NEED_TXPROCESSING = BIT(14), }; /** diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index fd718e2b29f..64b839bfbf1 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -132,16 +132,6 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, state = &sta->ampdu_mlme.tid_state_tx[tid]; if (local->hw.ampdu_queues) { - if (initiator) { - /* - * Stop the AC queue to avoid issues where we send - * unaggregated frames already before the delba. - */ - ieee80211_stop_queue_by_reason(&local->hw, - local->hw.queues + sta->tid_to_tx_q[tid], - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); - } - /* * Pretend the driver woke the queue, just in case * it disabled it before the session was stopped. @@ -158,6 +148,10 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, /* HW shall not deny going back to legacy */ if (WARN_ON(ret)) { *state = HT_AGG_STATE_OPERATIONAL; + /* + * We may have pending packets get stuck in this case... + * Not bothering with a workaround for now. + */ } return ret; @@ -226,13 +220,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) ra, tid); #endif /* CONFIG_MAC80211_HT_DEBUG */ - if (hw->ampdu_queues && ieee80211_ac_from_tid(tid) == 0) { -#ifdef CONFIG_MAC80211_HT_DEBUG - printk(KERN_DEBUG "rejecting on voice AC\n"); -#endif - return -EINVAL; - } - rcu_read_lock(); sta = sta_info_get(local, ra); @@ -267,6 +254,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) } spin_lock_bh(&sta->lock); + spin_lock(&local->ampdu_lock); sdata = sta->sdata; @@ -308,21 +296,19 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) ret = -ENOSPC; goto err_unlock_sta; } - - /* - * If we successfully allocate the session, we can't have - * anything going on on the queue this TID maps into, so - * stop it for now. This is a "virtual" stop using the same - * mechanism that drivers will use. - * - * XXX: queue up frames for this session in the sta_info - * struct instead to avoid hitting all other STAs. - */ - ieee80211_stop_queue_by_reason( - &local->hw, hw->queues + qn, - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); } + /* + * While we're asking the driver about the aggregation, + * stop the AC queue so that we don't have to worry + * about frames that came in while we were doing that, + * which would require us to put them to the AC pending + * afterwards which just makes the code more complex. + */ + ieee80211_stop_queue_by_reason( + &local->hw, ieee80211_ac_from_tid(tid), + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + /* prepare A-MPDU MLME for Tx aggregation */ sta->ampdu_mlme.tid_tx[tid] = kmalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC); @@ -336,6 +322,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) goto err_return_queue; } + skb_queue_head_init(&sta->ampdu_mlme.tid_tx[tid]->pending); + /* Tx timer */ sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.function = sta_addba_resp_timer_expired; @@ -362,6 +350,12 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) } sta->tid_to_tx_q[tid] = qn; + /* Driver vetoed or OKed, but we can take packets again now */ + ieee80211_wake_queue_by_reason( + &local->hw, ieee80211_ac_from_tid(tid), + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + + spin_unlock(&local->ampdu_lock); spin_unlock_bh(&sta->lock); /* send an addBA request */ @@ -388,15 +382,16 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) sta->ampdu_mlme.tid_tx[tid] = NULL; err_return_queue: if (qn >= 0) { - /* We failed, so start queue again right away. */ - ieee80211_wake_queue_by_reason(hw, hw->queues + qn, - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); /* give queue back to pool */ spin_lock(&local->queue_stop_reason_lock); local->ampdu_ac_queue[qn] = -1; spin_unlock(&local->queue_stop_reason_lock); } + ieee80211_wake_queue_by_reason( + &local->hw, ieee80211_ac_from_tid(tid), + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); err_unlock_sta: + spin_unlock(&local->ampdu_lock); spin_unlock_bh(&sta->lock); unlock: rcu_read_unlock(); @@ -404,6 +399,45 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) } EXPORT_SYMBOL(ieee80211_start_tx_ba_session); +/* + * splice packets from the STA's pending to the local pending, + * requires a call to ieee80211_agg_splice_finish and holding + * local->ampdu_lock across both calls. + */ +static void ieee80211_agg_splice_packets(struct ieee80211_local *local, + struct sta_info *sta, u16 tid) +{ + unsigned long flags; + u16 queue = ieee80211_ac_from_tid(tid); + + ieee80211_stop_queue_by_reason( + &local->hw, queue, + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + + if (!skb_queue_empty(&sta->ampdu_mlme.tid_tx[tid]->pending)) { + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); + /* mark queue as pending, it is stopped already */ + __set_bit(IEEE80211_QUEUE_STOP_REASON_PENDING, + &local->queue_stop_reasons[queue]); + /* copy over remaining packets */ + skb_queue_splice_tail_init( + &sta->ampdu_mlme.tid_tx[tid]->pending, + &local->pending[queue]); + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); + } +} + +static void ieee80211_agg_splice_finish(struct ieee80211_local *local, + struct sta_info *sta, u16 tid) +{ + u16 queue = ieee80211_ac_from_tid(tid); + + ieee80211_wake_queue_by_reason( + &local->hw, queue, + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); +} + +/* caller must hold sta->lock */ static void ieee80211_agg_tx_operational(struct ieee80211_local *local, struct sta_info *sta, u16 tid) { @@ -411,15 +445,16 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local, printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid); #endif - if (local->hw.ampdu_queues) { - /* - * Wake up the A-MPDU queue, we stopped it earlier, - * this will in turn wake the entire AC. - */ - ieee80211_wake_queue_by_reason(&local->hw, - local->hw.queues + sta->tid_to_tx_q[tid], - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); - } + spin_lock(&local->ampdu_lock); + ieee80211_agg_splice_packets(local, sta, tid); + /* + * NB: we rely on sta->lock being taken in the TX + * processing here when adding to the pending queue, + * otherwise we could only change the state of the + * session to OPERATIONAL _here_. + */ + ieee80211_agg_splice_finish(local, sta, tid); + spin_unlock(&local->ampdu_lock); local->ops->ampdu_action(&local->hw, IEEE80211_AMPDU_TX_OPERATIONAL, &sta->sta, tid, NULL); @@ -602,22 +637,19 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE); spin_lock_bh(&sta->lock); + spin_lock(&local->ampdu_lock); - if (*state & HT_AGG_STATE_INITIATOR_MSK && - hw->ampdu_queues) { - /* - * Wake up this queue, we stopped it earlier, - * this will in turn wake the entire AC. - */ - ieee80211_wake_queue_by_reason(hw, - hw->queues + sta->tid_to_tx_q[tid], - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); - } + ieee80211_agg_splice_packets(local, sta, tid); *state = HT_AGG_STATE_IDLE; + /* from now on packets are no longer put onto sta->pending */ sta->ampdu_mlme.addba_req_num[tid] = 0; kfree(sta->ampdu_mlme.tid_tx[tid]); sta->ampdu_mlme.tid_tx[tid] = NULL; + + ieee80211_agg_splice_finish(local, sta, tid); + + spin_unlock(&local->ampdu_lock); spin_unlock_bh(&sta->lock); rcu_read_unlock(); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 6ce62e553dc..32345b479ad 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -639,6 +639,14 @@ struct ieee80211_local { struct sk_buff_head pending[IEEE80211_MAX_QUEUES]; struct tasklet_struct tx_pending_tasklet; + /* + * This lock is used to prevent concurrent A-MPDU + * session start/stop processing, this thus also + * synchronises the ->ampdu_action() callback to + * drivers and limits it to one at a time. + */ + spinlock_t ampdu_lock; + /* number of interfaces with corresponding IFF_ flags */ atomic_t iff_allmultis, iff_promiscs; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index a7430e98c53..756284e0bbd 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -795,6 +795,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, skb_queue_head_init(&local->skb_queue); skb_queue_head_init(&local->skb_queue_unreliable); + spin_lock_init(&local->ampdu_lock); + return local_to_hw(local); } EXPORT_SYMBOL(ieee80211_alloc_hw); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 4ba3c540fcf..dd3593c1fd2 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -239,6 +239,11 @@ void sta_info_destroy(struct sta_info *sta) tid_tx = sta->ampdu_mlme.tid_tx[i]; if (tid_tx) { del_timer_sync(&tid_tx->addba_resp_timer); + /* + * STA removed while aggregation session being + * started? Bit odd, but purge frames anyway. + */ + skb_queue_purge(&tid_tx->pending); kfree(tid_tx); } } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 5b223b216e5..18fd5d1a442 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -73,11 +73,13 @@ enum ieee80211_sta_info_flags { * struct tid_ampdu_tx - TID aggregation information (Tx). * * @addba_resp_timer: timer for peer's response to addba request + * @pending: pending frames queue -- use sta's spinlock to protect * @ssn: Starting Sequence Number expected to be aggregated. * @dialog_token: dialog token for aggregation session */ struct tid_ampdu_tx { struct timer_list addba_resp_timer; + struct sk_buff_head pending; u16 ssn; u8 dialog_token; }; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index a0e00c6339c..906ab785db4 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -984,9 +984,9 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx, struct ieee80211_hdr *hdr; struct ieee80211_sub_if_data *sdata; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - int hdrlen, tid; u8 *qc, *state; + bool queued = false; memset(tx, 0, sizeof(*tx)); tx->skb = skb; @@ -1013,20 +1013,53 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx, */ } + /* + * If this flag is set to true anywhere, and we get here, + * we are doing the needed processing, so remove the flag + * now. + */ + info->flags &= ~IEEE80211_TX_INTFL_NEED_TXPROCESSING; + hdr = (struct ieee80211_hdr *) skb->data; tx->sta = sta_info_get(local, hdr->addr1); - if (tx->sta && ieee80211_is_data_qos(hdr->frame_control)) { + if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) && + (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)) { unsigned long flags; + struct tid_ampdu_tx *tid_tx; + qc = ieee80211_get_qos_ctl(hdr); tid = *qc & IEEE80211_QOS_CTL_TID_MASK; spin_lock_irqsave(&tx->sta->lock, flags); + /* + * XXX: This spinlock could be fairly expensive, but see the + * comment in agg-tx.c:ieee80211_agg_tx_operational(). + * One way to solve this would be to do something RCU-like + * for managing the tid_tx struct and using atomic bitops + * for the actual state -- by introducing an actual + * 'operational' bit that would be possible. It would + * require changing ieee80211_agg_tx_operational() to + * set that bit, and changing the way tid_tx is managed + * everywhere, including races between that bit and + * tid_tx going away (tid_tx being added can be easily + * committed to memory before the 'operational' bit). + */ + tid_tx = tx->sta->ampdu_mlme.tid_tx[tid]; state = &tx->sta->ampdu_mlme.tid_state_tx[tid]; - if (*state == HT_AGG_STATE_OPERATIONAL) + if (*state == HT_AGG_STATE_OPERATIONAL) { info->flags |= IEEE80211_TX_CTL_AMPDU; + } else if (*state != HT_AGG_STATE_IDLE) { + /* in progress */ + queued = true; + info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; + __skb_queue_tail(&tid_tx->pending, skb); + } spin_unlock_irqrestore(&tx->sta->lock, flags); + + if (unlikely(queued)) + return TX_QUEUED; } if (is_multicast_ether_addr(hdr->addr1)) { @@ -1077,7 +1110,14 @@ static int ieee80211_tx_prepare(struct ieee80211_local *local, } if (unlikely(!dev)) return -ENODEV; - /* initialises tx with control */ + /* + * initialises tx with control + * + * return value is safe to ignore here because this function + * can only be invoked for multicast frames + * + * XXX: clean up + */ __ieee80211_tx_prepare(tx, skb, dev); dev_put(dev); return 0; @@ -1188,7 +1228,8 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx) return 0; } -static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) +static void ieee80211_tx(struct net_device *dev, struct sk_buff *skb, + bool txpending) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct sta_info *sta; @@ -1202,11 +1243,11 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) queue = skb_get_queue_mapping(skb); - WARN_ON(!skb_queue_empty(&local->pending[queue])); + WARN_ON(!txpending && !skb_queue_empty(&local->pending[queue])); if (unlikely(skb->len < 10)) { dev_kfree_skb(skb); - return 0; + return; } rcu_read_lock(); @@ -1214,10 +1255,13 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) /* initialises tx */ res_prepare = __ieee80211_tx_prepare(&tx, skb, dev); - if (res_prepare == TX_DROP) { + if (unlikely(res_prepare == TX_DROP)) { dev_kfree_skb(skb); rcu_read_unlock(); - return 0; + return; + } else if (unlikely(res_prepare == TX_QUEUED)) { + rcu_read_unlock(); + return; } sta = tx.sta; @@ -1251,7 +1295,12 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) do { next = skb->next; skb->next = NULL; - skb_queue_tail(&local->pending[queue], skb); + if (unlikely(txpending)) + skb_queue_head(&local->pending[queue], + skb); + else + skb_queue_tail(&local->pending[queue], + skb); } while ((skb = next)); /* @@ -1276,7 +1325,7 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) } out: rcu_read_unlock(); - return 0; + return; drop: rcu_read_unlock(); @@ -1287,7 +1336,6 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) dev_kfree_skb(skb); skb = next; } - return 0; } /* device xmit handlers */ @@ -1346,7 +1394,6 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) FOUND_SDATA, UNKNOWN_ADDRESS, } monitor_iface = NOT_MONITOR; - int ret; if (skb->iif) odev = dev_get_by_index(&init_net, skb->iif); @@ -1360,7 +1407,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) "originating device\n", dev->name); #endif dev_kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) && @@ -1389,7 +1436,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) else if (mesh_nexthop_lookup(skb, osdata)) { dev_put(odev); - return 0; + return NETDEV_TX_OK; } if (memcmp(odev->dev_addr, hdr->addr4, ETH_ALEN) != 0) IEEE80211_IFSTA_MESH_CTR_INC(&osdata->u.mesh, @@ -1451,7 +1498,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) if (ieee80211_skb_resize(osdata->local, skb, headroom, may_encrypt)) { dev_kfree_skb(skb); dev_put(odev); - return 0; + return NETDEV_TX_OK; } if (osdata->vif.type == NL80211_IFTYPE_AP_VLAN) @@ -1460,10 +1507,11 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) u.ap); if (likely(monitor_iface != UNKNOWN_ADDRESS)) info->control.vif = &osdata->vif; - ret = ieee80211_tx(odev, skb); + + ieee80211_tx(odev, skb, false); dev_put(odev); - return ret; + return NETDEV_TX_OK; } int ieee80211_monitor_start_xmit(struct sk_buff *skb, @@ -1827,6 +1875,54 @@ void ieee80211_clear_tx_pending(struct ieee80211_local *local) skb_queue_purge(&local->pending[i]); } +static bool ieee80211_tx_pending_skb(struct ieee80211_local *local, + struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_sub_if_data *sdata; + struct sta_info *sta; + struct ieee80211_hdr *hdr; + struct net_device *dev; + int ret; + bool result = true; + + /* does interface still exist? */ + dev = dev_get_by_index(&init_net, skb->iif); + if (!dev) { + dev_kfree_skb(skb); + return true; + } + + /* validate info->control.vif against skb->iif */ + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + sdata = container_of(sdata->bss, + struct ieee80211_sub_if_data, + u.ap); + + if (unlikely(info->control.vif && info->control.vif != &sdata->vif)) { + dev_kfree_skb(skb); + result = true; + goto out; + } + + if (info->flags & IEEE80211_TX_INTFL_NEED_TXPROCESSING) { + ieee80211_tx(dev, skb, true); + } else { + hdr = (struct ieee80211_hdr *)skb->data; + sta = sta_info_get(local, hdr->addr1); + + ret = __ieee80211_tx(local, &skb, sta); + if (ret != IEEE80211_TX_OK) + result = false; + } + + out: + dev_put(dev); + + return result; +} + /* * Transmit all pending packets. Called from tasklet, locks master device * TX lock so that no new packets can come in. @@ -1835,9 +1931,8 @@ void ieee80211_tx_pending(unsigned long data) { struct ieee80211_local *local = (struct ieee80211_local *)data; struct net_device *dev = local->mdev; - struct ieee80211_hdr *hdr; unsigned long flags; - int i, ret; + int i; bool next; rcu_read_lock(); @@ -1868,13 +1963,8 @@ void ieee80211_tx_pending(unsigned long data) while (!skb_queue_empty(&local->pending[i])) { struct sk_buff *skb = skb_dequeue(&local->pending[i]); - struct sta_info *sta; - - hdr = (struct ieee80211_hdr *)skb->data; - sta = sta_info_get(local, hdr->addr1); - ret = __ieee80211_tx(local, &skb, sta); - if (ret != IEEE80211_TX_OK) { + if (!ieee80211_tx_pending_skb(local, skb)) { skb_queue_head(&local->pending[i], skb); break; } -- cgit v1.2.3 From e4e72fb4de93e3d4047a4ee3f08778422e17ed0d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Mar 2009 17:28:42 +0100 Subject: mac80211/iwlwifi: move virtual A-MDPU queue bookkeeping to iwlwifi This patch removes all the virtual A-MPDU-queue bookkeeping from mac80211. Curiously, iwlwifi already does its own bookkeeping, so it doesn't require much changes except where it needs to handle starting and stopping the queues in mac80211. To handle the queue stop/wake properly, we rewrite the software queue number for aggregation frames and internally to iwlwifi keep track of the queues that map into the same AC queue, and only talk to mac80211 about the AC queue. The implementation requires calling two new functions, iwl_stop_queue and iwl_wake_queue instead of the mac80211 counterparts. Signed-off-by: Johannes Berg Cc: Reinette Chattre Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-3945.c | 2 +- drivers/net/wireless/iwlwifi/iwl-4965.c | 7 ++-- drivers/net/wireless/iwlwifi/iwl-5000.c | 7 ++-- drivers/net/wireless/iwlwifi/iwl-core.c | 3 -- drivers/net/wireless/iwlwifi/iwl-dev.h | 6 +++ drivers/net/wireless/iwlwifi/iwl-helpers.h | 52 ++++++++++++++++++++++++++ drivers/net/wireless/iwlwifi/iwl-tx.c | 8 ++-- drivers/net/wireless/iwlwifi/iwl3945-base.c | 2 +- drivers/net/wireless/mac80211_hwsim.c | 1 - include/net/mac80211.h | 14 +------ net/mac80211/agg-tx.c | 44 ++-------------------- net/mac80211/ieee80211_i.h | 7 +--- net/mac80211/main.c | 9 ----- net/mac80211/sta_info.c | 12 ------ net/mac80211/sta_info.h | 2 - net/mac80211/tx.c | 19 ---------- net/mac80211/util.c | 58 ++++------------------------- 17 files changed, 84 insertions(+), 169 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index d03f5534afe..2399328e8de 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -293,7 +293,7 @@ static void iwl3945_tx_queue_reclaim(struct iwl_priv *priv, if (iwl_queue_space(q) > q->low_mark && (txq_id >= 0) && (txq_id != IWL_CMD_QUEUE_NUM) && priv->mac80211_registered) - ieee80211_wake_queue(priv->hw, txq_id); + iwl_wake_queue(priv, txq_id); } /** diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index bd0140be774..847a6220c5e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -2178,10 +2178,9 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv, (iwl_queue_space(&txq->q) > txq->q.low_mark) && (agg->state != IWL_EMPTYING_HW_QUEUE_DELBA)) { if (agg->state == IWL_AGG_OFF) - ieee80211_wake_queue(priv->hw, txq_id); + iwl_wake_queue(priv, txq_id); else - ieee80211_wake_queue(priv->hw, - txq->swq_id); + iwl_wake_queue(priv, txq->swq_id); } } } else { @@ -2205,7 +2204,7 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv, if (priv->mac80211_registered && (iwl_queue_space(&txq->q) > txq->q.low_mark)) - ieee80211_wake_queue(priv->hw, txq_id); + iwl_wake_queue(priv, txq_id); } if (qc && likely(sta_id != IWL_INVALID_STATION)) diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index a3d9a95a9b3..e5ca2511a81 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -1295,10 +1295,9 @@ static void iwl5000_rx_reply_tx(struct iwl_priv *priv, (iwl_queue_space(&txq->q) > txq->q.low_mark) && (agg->state != IWL_EMPTYING_HW_QUEUE_DELBA)) { if (agg->state == IWL_AGG_OFF) - ieee80211_wake_queue(priv->hw, txq_id); + iwl_wake_queue(priv, txq_id); else - ieee80211_wake_queue(priv->hw, - txq->swq_id); + iwl_wake_queue(priv, txq->swq_id); } } } else { @@ -1324,7 +1323,7 @@ static void iwl5000_rx_reply_tx(struct iwl_priv *priv, if (priv->mac80211_registered && (iwl_queue_space(&txq->q) > txq->q.low_mark)) - ieee80211_wake_queue(priv->hw, txq_id); + iwl_wake_queue(priv, txq_id); } if (ieee80211_is_data_qos(tx_resp->frame_ctrl)) diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 4b1298c2b0d..c54fb93e9d7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -1309,9 +1309,6 @@ int iwl_setup_mac(struct iwl_priv *priv) /* Default value; 4 EDCA QOS priorities */ hw->queues = 4; - /* queues to support 11n aggregation */ - if (priv->cfg->sku & IWL_SKU_N) - hw->ampdu_queues = priv->cfg->mod_params->num_of_ampdu_queues; hw->conf.beacon_int = 100; hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 0baae802282..ec9a13846ed 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -996,6 +996,12 @@ struct iwl_priv { u8 key_mapping_key; unsigned long ucode_key_table; + /* queue refcounts */ +#define IWL_MAX_HW_QUEUES 32 + unsigned long queue_stopped[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)]; + /* for each AC */ + atomic_t queue_stop_count[4]; + /* Indication if ieee80211_ops->open has been called */ u8 is_open; diff --git a/drivers/net/wireless/iwlwifi/iwl-helpers.h b/drivers/net/wireless/iwlwifi/iwl-helpers.h index fb64d297dd4..a1328c3c81a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-helpers.h +++ b/drivers/net/wireless/iwlwifi/iwl-helpers.h @@ -93,4 +93,56 @@ static inline int iwl_alloc_fw_desc(struct pci_dev *pci_dev, return (desc->v_addr != NULL) ? 0 : -ENOMEM; } +/* + * we have 8 bits used like this: + * + * 7 6 5 4 3 2 1 0 + * | | | | | | | | + * | | | | | | +-+-------- AC queue (0-3) + * | | | | | | + * | +-+-+-+-+------------ HW A-MPDU queue + * | + * +---------------------- indicates agg queue + */ +static inline u8 iwl_virtual_agg_queue_num(u8 ac, u8 hwq) +{ + BUG_ON(ac > 3); /* only have 2 bits */ + BUG_ON(hwq > 31); /* only have 5 bits */ + + return 0x80 | (hwq << 2) | ac; +} + +static inline void iwl_wake_queue(struct iwl_priv *priv, u8 queue) +{ + u8 ac = queue; + u8 hwq = queue; + + if (queue & 0x80) { + ac = queue & 3; + hwq = (queue >> 2) & 0x1f; + } + + if (test_and_clear_bit(hwq, priv->queue_stopped)) + if (atomic_dec_return(&priv->queue_stop_count[ac]) <= 0) + ieee80211_wake_queue(priv->hw, ac); +} + +static inline void iwl_stop_queue(struct iwl_priv *priv, u8 queue) +{ + u8 ac = queue; + u8 hwq = queue; + + if (queue & 0x80) { + ac = queue & 3; + hwq = (queue >> 2) & 0x1f; + } + + if (!test_and_set_bit(hwq, priv->queue_stopped)) + if (atomic_inc_return(&priv->queue_stop_count[ac]) > 0) + ieee80211_stop_queue(priv->hw, ac); +} + +#define ieee80211_stop_queue DO_NOT_USE_ieee80211_stop_queue +#define ieee80211_wake_queue DO_NOT_USE_ieee80211_wake_queue + #endif /* __iwl_helpers_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index b13862a598e..1f117a49c56 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -763,8 +763,10 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) hdr->seq_ctrl |= cpu_to_le16(seq_number); seq_number += 0x10; /* aggregation is on for this */ - if (info->flags & IEEE80211_TX_CTL_AMPDU) + if (info->flags & IEEE80211_TX_CTL_AMPDU) { txq_id = priv->stations[sta_id].tid[tid].agg.txq_id; + swq_id = iwl_virtual_agg_queue_num(swq_id, txq_id); + } priv->stations[sta_id].tid[tid].tfds_in_queue++; } @@ -895,7 +897,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) iwl_txq_update_write_ptr(priv, txq); spin_unlock_irqrestore(&priv->lock, flags); } else { - ieee80211_stop_queue(priv->hw, txq->swq_id); + iwl_stop_queue(priv, txq->swq_id); } } @@ -1433,7 +1435,7 @@ void iwl_rx_reply_compressed_ba(struct iwl_priv *priv, if ((iwl_queue_space(&txq->q) > txq->q.low_mark) && priv->mac80211_registered && (agg->state != IWL_EMPTYING_HW_QUEUE_DELBA)) - ieee80211_wake_queue(priv->hw, txq->swq_id); + iwl_wake_queue(priv, txq->swq_id); iwl_txq_check_empty(priv, sta_id, tid, scd_flow); } diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index ede29b6c4dc..a71b08ca7c7 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -1168,7 +1168,7 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) spin_unlock_irqrestore(&priv->lock, flags); } - ieee80211_stop_queue(priv->hw, skb_get_queue_mapping(skb)); + iwl_stop_queue(priv, skb_get_queue_mapping(skb)); } return 0; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 55116102475..d4fdc8b7d7d 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -933,7 +933,6 @@ static int __init init_mac80211_hwsim(void) BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_MESH_POINT); - hw->ampdu_queues = 1; hw->flags = IEEE80211_HW_MFP_CAPABLE; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 841f7f804bb..3b83a80e3fe 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -93,12 +93,9 @@ struct ieee80211_ht_bss_info { * enum ieee80211_max_queues - maximum number of queues * * @IEEE80211_MAX_QUEUES: Maximum number of regular device queues. - * @IEEE80211_MAX_AMPDU_QUEUES: Maximum number of queues usable - * for A-MPDU operation. */ enum ieee80211_max_queues { IEEE80211_MAX_QUEUES = 4, - IEEE80211_MAX_AMPDU_QUEUES = 16, }; /** @@ -952,12 +949,6 @@ enum ieee80211_hw_flags { * data packets. WMM/QoS requires at least four, these * queues need to have configurable access parameters. * - * @ampdu_queues: number of available hardware transmit queues - * for A-MPDU packets, these have no access parameters - * because they're used only for A-MPDU frames. Note that - * mac80211 will not currently use any of the regular queues - * for aggregation. - * * @rate_control_algorithm: rate control algorithm for this hardware. * If unset (NULL), the default algorithm will be used. Must be * set before calling ieee80211_register_hw(). @@ -982,7 +973,6 @@ struct ieee80211_hw { int vif_data_size; int sta_data_size; u16 queues; - u16 ampdu_queues; u16 max_listen_interval; s8 max_signal; u8 max_rates; @@ -1372,8 +1362,8 @@ enum ieee80211_ampdu_mlme_action { * @get_tx_stats: Get statistics of the current TX queue status. This is used * to get number of currently queued packets (queue length), maximum queue * size (limit), and total number of packets sent using each TX queue - * (count). The 'stats' pointer points to an array that has hw->queues + - * hw->ampdu_queues items. + * (count). The 'stats' pointer points to an array that has hw->queues + * items. * * @get_tsf: Get the current TSF timer value from firmware/hardware. Currently, * this is only used for IBSS mode BSSID merging and debugging. Is not a diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 64b839bfbf1..947aaaad35d 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -131,14 +131,6 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, state = &sta->ampdu_mlme.tid_state_tx[tid]; - if (local->hw.ampdu_queues) { - /* - * Pretend the driver woke the queue, just in case - * it disabled it before the session was stopped. - */ - ieee80211_wake_queue( - &local->hw, local->hw.queues + sta->tid_to_tx_q[tid]); - } *state = HT_AGG_STATE_REQ_STOP_BA_MSK | (initiator << HT_AGG_STATE_INITIATOR_SHIFT); @@ -206,7 +198,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) struct sta_info *sta; struct ieee80211_sub_if_data *sdata; u8 *state; - int i, qn = -1, ret = 0; + int ret = 0; u16 start_seq_num; if (WARN_ON(!local->ops->ampdu_action)) @@ -275,29 +267,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) goto err_unlock_sta; } - if (hw->ampdu_queues) { - spin_lock(&local->queue_stop_reason_lock); - /* reserve a new queue for this session */ - for (i = 0; i < local->hw.ampdu_queues; i++) { - if (local->ampdu_ac_queue[i] < 0) { - qn = i; - local->ampdu_ac_queue[qn] = - ieee80211_ac_from_tid(tid); - break; - } - } - spin_unlock(&local->queue_stop_reason_lock); - - if (qn < 0) { -#ifdef CONFIG_MAC80211_HT_DEBUG - printk(KERN_DEBUG "BA request denied - " - "queue unavailable for tid %d\n", tid); -#endif /* CONFIG_MAC80211_HT_DEBUG */ - ret = -ENOSPC; - goto err_unlock_sta; - } - } - /* * While we're asking the driver about the aggregation, * stop the AC queue so that we don't have to worry @@ -319,7 +288,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) tid); #endif ret = -ENOMEM; - goto err_return_queue; + goto err_wake_queue; } skb_queue_head_init(&sta->ampdu_mlme.tid_tx[tid]->pending); @@ -348,7 +317,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) *state = HT_AGG_STATE_IDLE; goto err_free; } - sta->tid_to_tx_q[tid] = qn; /* Driver vetoed or OKed, but we can take packets again now */ ieee80211_wake_queue_by_reason( @@ -380,13 +348,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) err_free: kfree(sta->ampdu_mlme.tid_tx[tid]); sta->ampdu_mlme.tid_tx[tid] = NULL; - err_return_queue: - if (qn >= 0) { - /* give queue back to pool */ - spin_lock(&local->queue_stop_reason_lock); - local->ampdu_ac_queue[qn] = -1; - spin_unlock(&local->queue_stop_reason_lock); - } + err_wake_queue: ieee80211_wake_queue_by_reason( &local->hw, ieee80211_ac_from_tid(tid), IEEE80211_QUEUE_STOP_REASON_AGGREGATION); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 32345b479ad..e6ed78cb16b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -594,12 +594,7 @@ struct ieee80211_local { const struct ieee80211_ops *ops; - /* AC queue corresponding to each AMPDU queue */ - s8 ampdu_ac_queue[IEEE80211_MAX_AMPDU_QUEUES]; - unsigned int amdpu_ac_stop_refcnt[IEEE80211_MAX_AMPDU_QUEUES]; - - unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES + - IEEE80211_MAX_AMPDU_QUEUES]; + unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES]; /* also used to protect ampdu_ac_queue and amdpu_ac_stop_refcnt */ spinlock_t queue_stop_reason_lock; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 756284e0bbd..a6f1d8a869b 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -774,11 +774,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, setup_timer(&local->dynamic_ps_timer, ieee80211_dynamic_ps_timer, (unsigned long) local); - for (i = 0; i < IEEE80211_MAX_AMPDU_QUEUES; i++) - local->ampdu_ac_queue[i] = -1; - /* using an s8 won't work with more than that */ - BUILD_BUG_ON(IEEE80211_MAX_AMPDU_QUEUES > 127); - sta_info_init(local); for (i = 0; i < IEEE80211_MAX_QUEUES; i++) @@ -874,10 +869,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) */ if (hw->queues > IEEE80211_MAX_QUEUES) hw->queues = IEEE80211_MAX_QUEUES; - if (hw->ampdu_queues > IEEE80211_MAX_AMPDU_QUEUES) - hw->ampdu_queues = IEEE80211_MAX_AMPDU_QUEUES; - if (hw->queues < 4) - hw->ampdu_queues = 0; mdev = alloc_netdev_mq(sizeof(struct ieee80211_master_priv), "wmaster%d", ieee80211_master_setup, diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index dd3593c1fd2..c5f14e6bbde 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -203,17 +203,6 @@ void sta_info_destroy(struct sta_info *sta) if (tid_rx) tid_rx->shutdown = true; - /* - * The stop callback cannot find this station any more, but - * it didn't complete its work -- start the queue if necessary - */ - if (sta->ampdu_mlme.tid_state_tx[i] & HT_AGG_STATE_INITIATOR_MSK && - sta->ampdu_mlme.tid_state_tx[i] & HT_AGG_STATE_REQ_STOP_BA_MSK && - local->hw.ampdu_queues) - ieee80211_wake_queue_by_reason(&local->hw, - local->hw.queues + sta->tid_to_tx_q[i], - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); - spin_unlock_bh(&sta->lock); /* @@ -292,7 +281,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, * enable session_timer's data differentiation. refer to * sta_rx_agg_session_timer_expired for useage */ sta->timer_to_tid[i] = i; - sta->tid_to_tx_q[i] = -1; /* rx */ sta->ampdu_mlme.tid_state_rx[i] = HT_AGG_STATE_IDLE; sta->ampdu_mlme.tid_rx[i] = NULL; diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 18fd5d1a442..5534d489f50 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -206,7 +206,6 @@ struct sta_ampdu_mlme { * @tid_seq: per-TID sequence numbers for sending to this STA * @ampdu_mlme: A-MPDU state machine state * @timer_to_tid: identity mapping to ID timers - * @tid_to_tx_q: map tid to tx queue (invalid == negative values) * @llid: Local link ID * @plid: Peer link ID * @reason: Cancel reason on PLINK_HOLDING state @@ -281,7 +280,6 @@ struct sta_info { */ struct sta_ampdu_mlme ampdu_mlme; u8 timer_to_tid[STA_TID_NUM]; - s8 tid_to_tx_q[STA_TID_NUM]; #ifdef CONFIG_MAC80211_MESH /* diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 906ab785db4..3fb04a86444 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1145,25 +1145,6 @@ static int __ieee80211_tx(struct ieee80211_local *local, info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT | IEEE80211_TX_CTL_FIRST_FRAGMENT); - /* - * Internally, we need to have the queue mapping point to - * the real AC queue, not the virtual A-MPDU queue. This - * now finally sets the queue to what the driver wants. - * We will later move this down into the only driver that - * needs it, iwlwifi. - */ - if (sta && local->hw.ampdu_queues && - info->flags & IEEE80211_TX_CTL_AMPDU) { - unsigned long flags; - u8 *qc = ieee80211_get_qos_ctl((void *) skb->data); - int tid = *qc & IEEE80211_QOS_CTL_TID_MASK; - - spin_lock_irqsave(&sta->lock, flags); - skb_set_queue_mapping(skb, local->hw.queues + - sta->tid_to_tx_q[tid]); - spin_unlock_irqrestore(&sta->lock, flags); - } - next = skb->next; len = skb->len; ret = local->ops->tx(local_to_hw(local), skb); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 0247d8022f5..fdf432f1455 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -339,29 +339,8 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, { struct ieee80211_local *local = hw_to_local(hw); - if (queue >= hw->queues) { - if (local->ampdu_ac_queue[queue - hw->queues] < 0) - return; - - /* - * for virtual aggregation queues, we need to refcount the - * internal mac80211 disable (multiple times!), keep track of - * driver disable _and_ make sure the regular queue is - * actually enabled. - */ - if (reason == IEEE80211_QUEUE_STOP_REASON_AGGREGATION) - local->amdpu_ac_stop_refcnt[queue - hw->queues]--; - else - __clear_bit(reason, &local->queue_stop_reasons[queue]); - - if (local->queue_stop_reasons[queue] || - local->amdpu_ac_stop_refcnt[queue - hw->queues]) - return; - - /* now go on to treat the corresponding regular queue */ - queue = local->ampdu_ac_queue[queue - hw->queues]; - reason = IEEE80211_QUEUE_STOP_REASON_AGGREGATION; - } + if (WARN_ON(queue >= hw->queues)) + return; __clear_bit(reason, &local->queue_stop_reasons[queue]); @@ -400,25 +379,8 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, { struct ieee80211_local *local = hw_to_local(hw); - if (queue >= hw->queues) { - if (local->ampdu_ac_queue[queue - hw->queues] < 0) - return; - - /* - * for virtual aggregation queues, we need to refcount the - * internal mac80211 disable (multiple times!), keep track of - * driver disable _and_ make sure the regular queue is - * actually enabled. - */ - if (reason == IEEE80211_QUEUE_STOP_REASON_AGGREGATION) - local->amdpu_ac_stop_refcnt[queue - hw->queues]++; - else - __set_bit(reason, &local->queue_stop_reasons[queue]); - - /* now go on to treat the corresponding regular queue */ - queue = local->ampdu_ac_queue[queue - hw->queues]; - reason = IEEE80211_QUEUE_STOP_REASON_AGGREGATION; - } + if (WARN_ON(queue >= hw->queues)) + return; /* * Only stop if it was previously running, this is necessary @@ -474,15 +436,9 @@ EXPORT_SYMBOL(ieee80211_stop_queues); int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue) { struct ieee80211_local *local = hw_to_local(hw); - unsigned long flags; - if (queue >= hw->queues) { - spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - queue = local->ampdu_ac_queue[queue - hw->queues]; - spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); - if (queue < 0) - return true; - } + if (WARN_ON(queue >= hw->queues)) + return true; return __netif_subqueue_stopped(local->mdev, queue); } @@ -497,7 +453,7 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - for (i = 0; i < hw->queues + hw->ampdu_queues; i++) + for (i = 0; i < hw->queues; i++) __ieee80211_wake_queue(hw, i, reason); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); -- cgit v1.2.3 From 8a5117d80fe93de5df5b56480054f7df1fd20755 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 24 Mar 2009 21:21:07 -0400 Subject: cfg80211: default CONFIG_WIRELESS_OLD_REGULATORY to n And update description and feature-removal schedule according to the new plan. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- Documentation/feature-removal-schedule.txt | 13 +++++++---- net/wireless/Kconfig | 37 ++++++++---------------------- 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 8365f52a354..02ea3773535 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -7,7 +7,7 @@ be removed from this file. --------------------------- What: The ieee80211_regdom module parameter -When: March 2010 +When: March 2010 / desktop catchup Why: This was inherited by the CONFIG_WIRELESS_OLD_REGULATORY code, and currently serves as an option for users to define an @@ -30,18 +30,23 @@ Who: Luis R. Rodriguez --------------------------- -What: old static regulatory information -When: 2.6.29 +What: CONFIG_WIRELESS_OLD_REGULATORY - old static regulatory information +When: March 2010 / desktop catchup + Why: The old regulatory infrastructure has been replaced with a new one which does not require statically defined regulatory domains. We do not want to keep static regulatory domains in the kernel due to the the dynamic nature of regulatory law and localization. We kept around the old static definitions for the regulatory domains of: + * US * JP * EU + and used by default the US when CONFIG_WIRELESS_OLD_REGULATORY was - set. + set. We will remove this option once the standard Linux desktop catches + up with the new userspace APIs we have implemented. + Who: Luis R. Rodriguez --------------------------- diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index d1d18f34d27..3c3bc9e579e 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -12,36 +12,17 @@ config CFG80211_REG_DEBUG config WIRELESS_OLD_REGULATORY bool "Old wireless static regulatory definitions" - default y + default n ---help--- This option enables the old static regulatory information - and uses it within the new framework. This is available - temporarily as an option to help prevent immediate issues - due to the switch to the new regulatory framework which - does require a new userspace application which has the - database of regulatory information (CRDA) and another for - setting regulatory domains (iw). - - For more information see: - - http://wireless.kernel.org/en/developers/Regulatory/CRDA - http://wireless.kernel.org/en/users/Documentation/iw - - It is important to note though that if you *do* have CRDA present - and if this option is enabled CRDA *will* be called to update the - regulatory domain (for US and JP only). Support for letting the user - set the regulatory domain through iw is also supported. This option - mainly exists to leave around for a kernel release some old static - regulatory domains that were defined and to keep around the old - ieee80211_regdom module parameter. This is being phased out and you - should stop using them ASAP. - - Note: You will need CRDA if you want 802.11d support - - Say Y unless you have installed a new userspace application. - Also say Y if have one currently depending on the ieee80211_regdom - module parameter and cannot port it to use the new userspace - interfaces. + and uses it within the new framework. This option is available + for historical reasons and it is advised to leave it off. + + For details see: + + http://wireless.kernel.org/en/developers/Regulatory + + Say N and if you say Y, please tell us why. The default is N. config WIRELESS_EXT bool "Wireless extensions" -- cgit v1.2.3