From 12f32370068d539bdc2fc9cf22c37dbdc2bae158 Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Sat, 31 May 2014 10:14:06 -0300 Subject: [PATCH 001/274] net: wireless: Remove useless return variables This patch remove variables that are initialized with a constant, are never updated, and are only used as parameter of return. Return the constant instead of using a variable. wl_cfg80211.c verified by compilation only. phy/phy_cmn.c unverified. The coccinelle script that find and fixes this issue is: // @@ type T; constant C; identifier ret; @@ - T ret = C; ... when != ret when strict return - ret + C ; // Signed-off-by: Peter Senna Tschudin Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 18 ++++++------------ .../wireless/brcm80211/brcmsmac/phy/phy_cmn.c | 6 +----- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index d8fa276e368b..db3d8487dc42 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -1331,7 +1331,6 @@ static s32 brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev) { struct brcmf_if *ifp = netdev_priv(ndev); - s32 err = 0; brcmf_dbg(TRACE, "Enter\n"); if (!check_vif_up(ifp->vif)) @@ -1341,7 +1340,7 @@ brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev) brcmf_dbg(TRACE, "Exit\n"); - return err; + return 0; } static s32 brcmf_set_wpa_version(struct net_device *ndev, @@ -2388,7 +2387,6 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg, struct cfg80211_bss *bss; struct ieee80211_supported_band *band; struct brcmu_chan ch; - s32 err = 0; u16 channel; u32 freq; u16 notify_capability; @@ -2438,7 +2436,7 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg, cfg80211_put_bss(wiphy, bss); - return err; + return 0; } static struct brcmf_bss_info_le * @@ -2690,7 +2688,6 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp, { struct brcmf_cfg80211_info *cfg = ifp->drvr->config; s32 status; - s32 err = 0; struct brcmf_escan_result_le *escan_result_le; struct brcmf_bss_info_le *bss_info_le; struct brcmf_bss_info_le *bss = NULL; @@ -2781,7 +2778,7 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp, status); } exit: - return err; + return 0; } static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg) @@ -3507,7 +3504,6 @@ static s32 brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len, struct parsed_vndr_ies *vndr_ies) { - s32 err = 0; struct brcmf_vs_tlv *vndrie; struct brcmf_tlv *ie; struct parsed_vndr_ie_info *parsed_info; @@ -3560,7 +3556,7 @@ next: ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len + TLV_HDR_LEN); } - return err; + return 0; } static u32 @@ -4650,7 +4646,6 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); - s32 err = 0; brcmf_dbg(TRACE, "Enter\n"); @@ -4676,7 +4671,7 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg, completed ? "succeeded" : "failed"); } brcmf_dbg(TRACE, "Exit\n"); - return err; + return 0; } static s32 @@ -4768,7 +4763,6 @@ brcmf_notify_roaming_status(struct brcmf_if *ifp, const struct brcmf_event_msg *e, void *data) { struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - s32 err = 0; u32 event = e->event_code; u32 status = e->status; @@ -4779,7 +4773,7 @@ brcmf_notify_roaming_status(struct brcmf_if *ifp, brcmf_bss_connect_done(cfg, ifp->ndev, e, true); } - return err; + return 0; } static s32 diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c index b0fd807f2b2b..57ecc05802e9 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c @@ -1538,11 +1538,7 @@ static s8 wlc_user_txpwr_antport_to_rfport(struct brcms_phy *pi, uint chan, u32 band, u8 rate) { - s8 offset = 0; - - if (!pi->user_txpwr_at_rfport) - return offset; - return offset; + return 0; } void wlc_phy_txpower_recalc_target(struct brcms_phy *pi) From 69253b6108102834c7cac826d4c34a0414b83a3e Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Sat, 31 May 2014 13:12:26 -0300 Subject: [PATCH 002/274] cw1200: Remove useless return variables This patch remove variables that are initialized with a constant, are never updated, and are only used as parameter of return. Return the constant instead of using a variable. Verified by compilation only. The coccinelle script that find and fixes this issue is: // @@ type T; constant C; identifier ret; @@ - T ret = C; ... when != ret when strict return - ret + C ; // Signed-off-by: Peter Senna Tschudin Signed-off-by: John W. Linville --- drivers/net/wireless/cw1200/sta.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c index cd0cad7f7759..5b84664db13b 100644 --- a/drivers/net/wireless/cw1200/sta.c +++ b/drivers/net/wireless/cw1200/sta.c @@ -2289,7 +2289,6 @@ static int cw1200_upload_null(struct cw1200_common *priv) static int cw1200_upload_qosnull(struct cw1200_common *priv) { - int ret = 0; /* TODO: This needs to be implemented struct wsm_template_frame frame = { @@ -2306,7 +2305,7 @@ static int cw1200_upload_qosnull(struct cw1200_common *priv) dev_kfree_skb(frame.skb); */ - return ret; + return 0; } static int cw1200_enable_beaconing(struct cw1200_common *priv, From afbedbf7dfc0ebff0ea3280530fe0609fd307486 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Sat, 31 May 2014 18:29:46 +0200 Subject: [PATCH 003/274] rtl818x_pci: make RSSI code more readable remove the if-else chains and use switch-case to make code more readable and avoiding long lines that broke in several lines Signed-off-by: Andrea Merello Signed-off-by: John W. Linville --- drivers/net/wireless/rtl818x/rtl8180/dev.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index 2c1c02bafa10..c2dd5e636d28 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -209,7 +209,7 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev) struct rtl8180_priv *priv = dev->priv; struct rtl818x_rx_cmd_desc *cmd_desc; unsigned int count = 32; - u8 signal, agc, sq; + u8 agc, sq, signal = 1; dma_addr_t mapping; while (count--) { @@ -266,18 +266,21 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev) rx_status.rate_idx = (flags >> 20) & 0xF; agc = (flags2 >> 17) & 0x7F; - if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8185) { + switch (priv->chip_family) { + case RTL818X_CHIP_FAMILY_RTL8185: if (rx_status.rate_idx > 3) signal = 90 - clamp_t(u8, agc, 25, 90); else signal = 95 - clamp_t(u8, agc, 30, 95); - } else if (priv->chip_family == - RTL818X_CHIP_FAMILY_RTL8180) { + break; + case RTL818X_CHIP_FAMILY_RTL8180: sq = flags2 & 0xff; signal = priv->rf->calc_rssi(agc, sq); - } else { + break; + case RTL818X_CHIP_FAMILY_RTL8187SE: /* TODO: rtl8187se rssi */ signal = 10; + break; } rx_status.signal = signal; rx_status.freq = dev->conf.chandef.chan->center_freq; From 7049327a686d2ba888b28a0456eddd3c71fb4b55 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Sat, 31 May 2014 18:30:13 +0200 Subject: [PATCH 004/274] rtl818x_pci: provide dBm signal information for rtl8185 This patch makes the driver report signal information for rtl8185 boards using dBm instead of unspecified unit. Rtl8180 remains untouched. I did some tests to confirm the correctness of the measure performed by the board and it seems reasonably correct. The test setup has been made by connecting an AP with coax and RF attenuators to the card antenna port. In order to get a reference measure I tried with several cards with different chipset I own. I found that many gave different results, and I finally selected two cards that gave me consistent results to use as reference: AR9271 and Prism54-usb (isl3887 with Frisbee radio). Using this references I compared the RSSI information with my rtl8185 and I repeated tests with three different attenuation values, increasing attenuation by 10dB each step. I made only relative measures, making NO assumption about source power. CCK measures seem very close to my references, OFDM are a little bit less precise but, considering that these cards are not measuring instrumentation, IMHO this is still fairly good. CCK measures (1Mbps beacons) ATTENUATOR 1 p54usb: -58dBm ath9k_htc: -59dBm rtl8185: -59dBm ATTENUATOR 2 p54usb: -67dBm ath9k_htc: -68dBm rtl8185: -70dBm ATTENUATOR 3 p54usb: -78dBm ath9k_htc: -79dBm rtl8185: -79dBm OFDM measures (54Mbps ping) ATTENUATOR 1 p54usb: -58dBm ath9k_htc: -57dBm rtl8185: -62dBm ATTENUATOR 2 p54usb: -68dBm rtl8185: -71dBm Signed-off-by: Andrea Merello Signed-off-by: John W. Linville --- drivers/net/wireless/rtl818x/rtl8180/dev.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index c2dd5e636d28..cd8c09076b52 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -269,9 +269,9 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev) switch (priv->chip_family) { case RTL818X_CHIP_FAMILY_RTL8185: if (rx_status.rate_idx > 3) - signal = 90 - clamp_t(u8, agc, 25, 90); + signal = -clamp_t(u8, agc, 25, 90) - 9; else - signal = 95 - clamp_t(u8, agc, 30, 95); + signal = -clamp_t(u8, agc, 30, 95); break; case RTL818X_CHIP_FAMILY_RTL8180: sq = flags2 & 0xff; @@ -1754,8 +1754,7 @@ static int rtl8180_probe(struct pci_dev *pdev, dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | - IEEE80211_HW_RX_INCLUDES_FCS | - IEEE80211_HW_SIGNAL_UNSPEC; + IEEE80211_HW_RX_INCLUDES_FCS; dev->vif_data_size = sizeof(struct rtl8180_vif); dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); @@ -1812,6 +1811,11 @@ static int rtl8180_probe(struct pci_dev *pdev, pci_try_set_mwi(pdev); } + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8185) + dev->flags |= IEEE80211_HW_SIGNAL_DBM; + else + dev->flags |= IEEE80211_HW_SIGNAL_UNSPEC; + rtl8180_eeprom_read(priv); switch (priv->rf_type) { From 325ed9ff14d2c726725324280ce3a56d6258b0ea Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Sat, 31 May 2014 18:30:26 +0200 Subject: [PATCH 005/274] rtl818x_pci: fix possible RX descriptor invalid data read RX descriptor data must be read only if the descriptor has been fully updated by HW. There is a "ownership" flag in the descriptor itself to test this. The driver code contains a read for the "ownership" flag and, after it, other read access for descriptor data. This is in DMA coherent memory, that is _not_ guaranteed to be immune to instruction reordering, thus it is possible that the descriptor data is read _before_ the "ownership" flag. This can theoretically lead to a DMA/CPU race that may end up with the driver reading the data when it is still not valid, and the "ownership" bit just after enough time that the HW make the whole descriptor valid. The driver will in this case believe the data is valid, but it will use the invalid data read earlier. In order to avoid this, this patch adds a rmb() to force the "ownership" bit read to be issued before other descriptor data reads are attempted. Signed-off-by: Andrea Merello Signed-off-by: John W. Linville --- drivers/net/wireless/rtl818x/rtl8180/dev.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index cd8c09076b52..1e2592918fc6 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -222,12 +222,20 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev) struct rtl8187se_rx_desc *desc = entry; flags = le32_to_cpu(desc->flags); + /* if ownership flag is set, then we can trust the + * HW has written other fields. We must not trust + * other descriptor data read before we checked (read) + * the ownership flag + */ + rmb(); flags2 = le32_to_cpu(desc->flags2); tsft = le64_to_cpu(desc->tsft); } else { struct rtl8180_rx_desc *desc = entry; flags = le32_to_cpu(desc->flags); + /* same as above */ + rmb(); flags2 = le32_to_cpu(desc->flags2); tsft = le64_to_cpu(desc->tsft); } From c5ce4874d1f303dc851849d05191d79c3f798acb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sat, 31 May 2014 20:49:33 +0200 Subject: [PATCH 006/274] b43: drop B43_DEFAULT_CHANNEL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was never used, b43_switch_channel is always called with hw_value (from mac80211) or whatever get_default_chan returns. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/phy_common.c | 3 --- drivers/net/wireless/b43/phy_common.h | 4 ---- 2 files changed, 7 deletions(-) diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c index 08244b3b327e..ac4cf2b8aca8 100644 --- a/drivers/net/wireless/b43/phy_common.c +++ b/drivers/net/wireless/b43/phy_common.c @@ -408,9 +408,6 @@ int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel) u16 channelcookie, savedcookie; int err; - if (new_channel == B43_DEFAULT_CHANNEL) - new_channel = phy->ops->get_default_chan(dev); - /* First we set the channel radio code to prevent the * firmware from sending ghost packets. */ diff --git a/drivers/net/wireless/b43/phy_common.h b/drivers/net/wireless/b43/phy_common.h index 4ad6240d9ff4..9a92df46ae84 100644 --- a/drivers/net/wireless/b43/phy_common.h +++ b/drivers/net/wireless/b43/phy_common.h @@ -400,10 +400,6 @@ void b43_phy_take_out_of_reset(struct b43_wldev *dev); * b43_switch_channel - Switch to another channel */ int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel); -/** - * B43_DEFAULT_CHANNEL - Switch to the default channel. - */ -#define B43_DEFAULT_CHANNEL UINT_MAX /** * b43_software_rfkill - Turn the radio ON or OFF in software. From 53256511010b2ee226ded9155728862d90ac2b1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sat, 31 May 2014 20:49:34 +0200 Subject: [PATCH 007/274] b43: b43_op_config: drop check for core change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There aren't devices with multiple 802.11 cores supported by b43. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 32538ac5f7e4..a9e50ee125fa 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3798,17 +3798,13 @@ static void b43_set_retry_limits(struct b43_wldev *dev, static int b43_op_config(struct ieee80211_hw *hw, u32 changed) { struct b43_wl *wl = hw_to_b43_wl(hw); - struct b43_wldev *dev; - struct b43_phy *phy; + struct b43_wldev *dev = wl->current_dev; + struct b43_phy *phy = &dev->phy; struct ieee80211_conf *conf = &hw->conf; int antenna; int err = 0; - bool reload_bss = false; mutex_lock(&wl->mutex); - - dev = wl->current_dev; - b43_mac_suspend(dev); /* Switch the band (if necessary). This might change the active core. */ @@ -3816,15 +3812,6 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) if (err) goto out_unlock_mutex; - /* Need to reload all settings if the core changed */ - if (dev != wl->current_dev) { - dev = wl->current_dev; - changed = ~0; - reload_bss = true; - } - - phy = &dev->phy; - if (conf_is_ht(conf)) phy->is_40mhz = (conf_is_ht40_minus(conf) || conf_is_ht40_plus(conf)); @@ -3881,9 +3868,6 @@ out_mac_enable: out_unlock_mutex: mutex_unlock(&wl->mutex); - if (wl->vif && reload_bss) - b43_op_bss_info_changed(hw, wl->vif, &wl->vif->bss_conf, ~0); - return err; } From 8c79e5ee033798bc1147ec29f222c2ace6e9e897 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sat, 31 May 2014 20:49:35 +0200 Subject: [PATCH 008/274] b43: b43_op_config: use IEEE80211_CONF_CHANGE_CHANNEL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is tiny optimization and grouping band/channel ops. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index a9e50ee125fa..59aa4fdb2aeb 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3807,16 +3807,23 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) mutex_lock(&wl->mutex); b43_mac_suspend(dev); - /* Switch the band (if necessary). This might change the active core. */ - err = b43_switch_band(dev, conf->chandef.chan); - if (err) - goto out_unlock_mutex; + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + if (conf_is_ht(conf)) + phy->is_40mhz = conf_is_ht40_minus(conf) || + conf_is_ht40_plus(conf); + else + phy->is_40mhz = false; - if (conf_is_ht(conf)) - phy->is_40mhz = - (conf_is_ht40_minus(conf) || conf_is_ht40_plus(conf)); - else - phy->is_40mhz = false; + /* Switch the band (if necessary). */ + err = b43_switch_band(dev, conf->chandef.chan); + if (err) + goto out_mac_enable; + + /* Switch to the requested channel. + * The firmware takes care of races with the TX handler. + */ + b43_switch_channel(dev, conf->chandef.chan->hw_value); + } if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) b43_set_retry_limits(dev, conf->short_frame_max_tx_count, @@ -3825,11 +3832,6 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) if (!changed) goto out_mac_enable; - /* Switch to the requested channel. - * The firmware takes care of races with the TX handler. */ - if (conf->chandef.chan->hw_value != phy->channel) - b43_switch_channel(dev, conf->chandef.chan->hw_value); - dev->wl->radiotap_enabled = !!(conf->flags & IEEE80211_CONF_MONITOR); /* Adjust the desired TX power level. */ @@ -3865,7 +3867,6 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) out_mac_enable: b43_mac_enable(dev); -out_unlock_mutex: mutex_unlock(&wl->mutex); return err; From eb530b0fed7ec8ca435ec421cbff773b318a6a00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sat, 31 May 2014 20:49:36 +0200 Subject: [PATCH 009/274] b43: PHY: don't force default channel during init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PHY may need to be re-initialized during runtime (e.g. on band switch). Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/phy_common.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c index ac4cf2b8aca8..3bfb795f6a6d 100644 --- a/drivers/net/wireless/b43/phy_common.c +++ b/drivers/net/wireless/b43/phy_common.c @@ -94,7 +94,8 @@ int b43_phy_init(struct b43_wldev *dev) const struct b43_phy_operations *ops = phy->ops; int err; - phy->channel = ops->get_default_chan(dev); + if (!phy->channel) + phy->channel = ops->get_default_chan(dev); phy->ops->switch_analog(dev, true); b43_software_rfkill(dev, false); @@ -106,9 +107,7 @@ int b43_phy_init(struct b43_wldev *dev) } phy->do_full_init = false; - /* Make sure to switch hardware and firmware (SHM) to - * the default channel. */ - err = b43_switch_channel(dev, ops->get_default_chan(dev)); + err = b43_switch_channel(dev, phy->channel); if (err) { b43err(dev->wl, "PHY init: Channel switch to default failed\n"); goto err_phy_exit; From f9471e9973fd887b8af888b98860182b6c534ea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sat, 31 May 2014 20:49:37 +0200 Subject: [PATCH 010/274] b43: b43_op_config: set channel info before switching band MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Band switching code needs to know what channel we switch to. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 3 ++- drivers/net/wireless/b43/phy_common.c | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 59aa4fdb2aeb..5e4eed37e3ce 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3808,6 +3808,7 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) b43_mac_suspend(dev); if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + phy->channel = conf->chandef.chan->hw_value; if (conf_is_ht(conf)) phy->is_40mhz = conf_is_ht40_minus(conf) || conf_is_ht40_plus(conf); @@ -3822,7 +3823,7 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) /* Switch to the requested channel. * The firmware takes care of races with the TX handler. */ - b43_switch_channel(dev, conf->chandef.chan->hw_value); + b43_switch_channel(dev, phy->channel); } if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c index 3bfb795f6a6d..b465011c14ea 100644 --- a/drivers/net/wireless/b43/phy_common.c +++ b/drivers/net/wireless/b43/phy_common.c @@ -424,7 +424,6 @@ int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel) if (err) goto err_restore_cookie; - dev->phy.channel = new_channel; /* Wait for the radio to tune to the channel and stabilize. */ msleep(8); From ea42e71c79068daa8bfd04f5e3c4a19b5e62f7da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sat, 31 May 2014 20:49:38 +0200 Subject: [PATCH 011/274] b43: store current channel using struct cfg80211_chan_def MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 1 + drivers/net/wireless/b43/phy_common.c | 9 +++++++-- drivers/net/wireless/b43/phy_common.h | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 5e4eed37e3ce..2b99ed31ab08 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3808,6 +3808,7 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) b43_mac_suspend(dev); if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + phy->chandef = &conf->chandef; phy->channel = conf->chandef.chan->hw_value; if (conf_is_ht(conf)) phy->is_40mhz = conf_is_ht40_minus(conf) || diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c index b465011c14ea..9aa6c9c57393 100644 --- a/drivers/net/wireless/b43/phy_common.c +++ b/drivers/net/wireless/b43/phy_common.c @@ -94,8 +94,13 @@ int b43_phy_init(struct b43_wldev *dev) const struct b43_phy_operations *ops = phy->ops; int err; - if (!phy->channel) - phy->channel = ops->get_default_chan(dev); + /* During PHY init we need to use some channel. On the first init this + * function is called *before* b43_op_config, so our pointer is NULL. + */ + if (!phy->chandef) { + phy->chandef = &dev->wl->hw->conf.chandef; + phy->channel = phy->chandef->chan->hw_value; + } phy->ops->switch_analog(dev, true); b43_software_rfkill(dev, false); diff --git a/drivers/net/wireless/b43/phy_common.h b/drivers/net/wireless/b43/phy_common.h index 9a92df46ae84..56dfe7aa50a7 100644 --- a/drivers/net/wireless/b43/phy_common.h +++ b/drivers/net/wireless/b43/phy_common.h @@ -267,6 +267,7 @@ struct b43_phy { unsigned long next_txpwr_check_time; /* Current channel */ + struct cfg80211_chan_def *chandef; unsigned int channel; u16 channel_freq; enum nl80211_channel_type channel_type; From 39e971ef1b0ced72b6504429296551bbf14ac965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sat, 31 May 2014 20:49:39 +0200 Subject: [PATCH 012/274] b43: PHY: drop own channel_freq (get it from chandef when needed) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/phy_common.h | 1 - drivers/net/wireless/b43/phy_ht.c | 2 +- drivers/net/wireless/b43/phy_n.c | 24 ++++++++++++++---------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/b43/phy_common.h b/drivers/net/wireless/b43/phy_common.h index 56dfe7aa50a7..399082026b03 100644 --- a/drivers/net/wireless/b43/phy_common.h +++ b/drivers/net/wireless/b43/phy_common.h @@ -269,7 +269,6 @@ struct b43_phy { /* Current channel */ struct cfg80211_chan_def *chandef; unsigned int channel; - u16 channel_freq; enum nl80211_channel_type channel_type; /* PHY TX errors counter. */ diff --git a/drivers/net/wireless/b43/phy_ht.c b/drivers/net/wireless/b43/phy_ht.c index 5d6833f18498..f2974c6b1c01 100644 --- a/drivers/net/wireless/b43/phy_ht.c +++ b/drivers/net/wireless/b43/phy_ht.c @@ -596,7 +596,7 @@ static void b43_phy_ht_tx_power_ctl_setup(struct b43_wldev *dev) u8 target[3]; s16 a1[3], b0[3], b1[3]; - u16 freq = dev->phy.channel_freq; + u16 freq = dev->phy.chandef->chan->center_freq; int i, c; if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index 86569f6a8705..dc62f024f776 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -798,6 +798,7 @@ static void b43_chantab_radio_2056_upload(struct b43_wldev *dev, static void b43_radio_2056_setup(struct b43_wldev *dev, const struct b43_nphy_channeltab_entry_rev3 *e) { + struct b43_phy *phy = &dev->phy; struct ssb_sprom *sprom = dev->dev->bus_sprom; enum ieee80211_band band = b43_current_band(dev->wl); u16 offset; @@ -909,7 +910,7 @@ static void b43_radio_2056_setup(struct b43_wldev *dev, b43_radio_write(dev, offset | B2056_TX_PA_SPARE1, 0xee); } } else if (dev->phy.n->ipa5g_on && band == IEEE80211_BAND_5GHZ) { - u16 freq = dev->phy.channel_freq; + u16 freq = phy->chandef->chan->center_freq; if (freq < 5100) { paa_boost = 0xA; pada_boost = 0x77; @@ -1675,6 +1676,7 @@ static int b43_nphy_poll_rssi(struct b43_wldev *dev, enum n_rssi_type rssi_type, /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICalRev3 */ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev) { + struct b43_phy *phy = &dev->phy; struct b43_phy_n *nphy = dev->phy.n; u16 saved_regs_phy_rfctl[2]; @@ -1897,9 +1899,9 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev) /* Remember for which channel we store configuration */ if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - nphy->rssical_chanspec_2G.center_freq = dev->phy.channel_freq; + nphy->rssical_chanspec_2G.center_freq = phy->chandef->chan->center_freq; else - nphy->rssical_chanspec_5G.center_freq = dev->phy.channel_freq; + nphy->rssical_chanspec_5G.center_freq = phy->chandef->chan->center_freq; /* End of calibration, restore configuration */ b43_nphy_classifier(dev, 7, class); @@ -2528,7 +2530,7 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev) } } } else { - u16 freq = phy->channel_freq; + u16 freq = phy->chandef->chan->center_freq; if ((freq >= 5180 && freq <= 5230) || (freq >= 5745 && freq <= 5805)) { b43_radio_write(dev, 0x7D, 0xFF); @@ -3184,12 +3186,13 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable) /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrFix */ static void b43_nphy_tx_power_fix(struct b43_wldev *dev) { + struct b43_phy *phy = &dev->phy; struct b43_phy_n *nphy = dev->phy.n; struct ssb_sprom *sprom = dev->dev->bus_sprom; u8 txpi[2], bbmult, i; u16 tmp, radio_gain, dac_gain; - u16 freq = dev->phy.channel_freq; + u16 freq = phy->chandef->chan->center_freq; u32 txgain; /* u32 gaintbl; rev3+ */ @@ -3474,6 +3477,7 @@ static void b43_nphy_tx_prepare_adjusted_power_table(struct b43_wldev *dev) /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlSetup */ static void b43_nphy_tx_power_ctl_setup(struct b43_wldev *dev) { + struct b43_phy *phy = &dev->phy; struct b43_phy_n *nphy = dev->phy.n; struct ssb_sprom *sprom = dev->dev->bus_sprom; @@ -3483,7 +3487,7 @@ static void b43_nphy_tx_power_ctl_setup(struct b43_wldev *dev) s32 num, den, pwr; u32 regval[64]; - u16 freq = dev->phy.channel_freq; + u16 freq = phy->chandef->chan->center_freq; u16 tmp; u16 r; /* routing */ u8 i, c; @@ -4500,7 +4504,7 @@ static void b43_nphy_save_cal(struct b43_wldev *dev) txcal_radio_regs[2] = b43_radio_read(dev, 0x8D); txcal_radio_regs[3] = b43_radio_read(dev, 0xBC); } - iqcal_chanspec->center_freq = dev->phy.channel_freq; + iqcal_chanspec->center_freq = dev->phy.chandef->chan->center_freq; iqcal_chanspec->channel_type = dev->phy.channel_type; b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 8, table); @@ -4581,6 +4585,7 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev, struct nphy_txgains target, bool full, bool mphase) { + struct b43_phy *phy = &dev->phy; struct b43_phy_n *nphy = dev->phy.n; int i; int error = 0; @@ -4773,7 +4778,7 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev, nphy->txiqlocal_bestc); nphy->txiqlocal_coeffsvalid = true; nphy->txiqlocal_chanspec.center_freq = - dev->phy.channel_freq; + phy->chandef->chan->center_freq; nphy->txiqlocal_chanspec.channel_type = dev->phy.channel_type; } else { @@ -4811,7 +4816,7 @@ static void b43_nphy_reapply_tx_cal_coeffs(struct b43_wldev *dev) bool equal = true; if (!nphy->txiqlocal_coeffsvalid || - nphy->txiqlocal_chanspec.center_freq != dev->phy.channel_freq || + nphy->txiqlocal_chanspec.center_freq != dev->phy.chandef->chan->center_freq || nphy->txiqlocal_chanspec.channel_type != dev->phy.channel_type) return; @@ -5502,7 +5507,6 @@ static int b43_nphy_set_channel(struct b43_wldev *dev, /* Channel is set later in common code, but we need to set it on our own to let this function's subcalls work properly. */ phy->channel = channel->hw_value; - phy->channel_freq = channel->center_freq; if (b43_channel_type_is_40mhz(phy->channel_type) != b43_channel_type_is_40mhz(channel_type)) From bee6d4b272ba6e668f0c12d8bb66d76e1826f406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sat, 31 May 2014 20:49:40 +0200 Subject: [PATCH 013/274] b43: PHY: drop is_40mhz (get width info from chandef) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 5 --- drivers/net/wireless/b43/phy_common.c | 5 +++ drivers/net/wireless/b43/phy_common.h | 5 +-- drivers/net/wireless/b43/phy_n.c | 53 +++++++++++++-------------- 4 files changed, 33 insertions(+), 35 deletions(-) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 2b99ed31ab08..4b662d0abdd2 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3810,11 +3810,6 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { phy->chandef = &conf->chandef; phy->channel = conf->chandef.chan->hw_value; - if (conf_is_ht(conf)) - phy->is_40mhz = conf_is_ht40_minus(conf) || - conf_is_ht40_plus(conf); - else - phy->is_40mhz = false; /* Switch the band (if necessary). */ err = b43_switch_band(dev, conf->chandef.chan); diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c index 9aa6c9c57393..e7e83835f725 100644 --- a/drivers/net/wireless/b43/phy_common.c +++ b/drivers/net/wireless/b43/phy_common.c @@ -553,6 +553,11 @@ bool b43_channel_type_is_40mhz(enum nl80211_channel_type channel_type) channel_type == NL80211_CHAN_HT40PLUS); } +bool b43_is_40mhz(struct b43_wldev *dev) +{ + return dev->phy.chandef->width == NL80211_CHAN_WIDTH_40; +} + /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/BmacPhyClkFgc */ void b43_phy_force_clock(struct b43_wldev *dev, bool force) { diff --git a/drivers/net/wireless/b43/phy_common.h b/drivers/net/wireless/b43/phy_common.h index 399082026b03..674422fd22e6 100644 --- a/drivers/net/wireless/b43/phy_common.h +++ b/drivers/net/wireless/b43/phy_common.h @@ -228,9 +228,6 @@ struct b43_phy { bool supports_2ghz; bool supports_5ghz; - /* HT info */ - bool is_40mhz; - /* Is GMODE (2 GHz mode) bit enabled? */ bool gmode; @@ -452,6 +449,8 @@ void b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on); bool b43_channel_type_is_40mhz(enum nl80211_channel_type channel_type); +bool b43_is_40mhz(struct b43_wldev *dev); + void b43_phy_force_clock(struct b43_wldev *dev, bool force); struct b43_c32 b43_cordic(int theta); diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index dc62f024f776..dc1249381275 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -896,7 +896,7 @@ static void b43_radio_2056_setup(struct b43_wldev *dev, offset | B2056_TX_MIXG_BOOST_TUNE, mixg_boost); } else { - bias = dev->phy.is_40mhz ? 0x40 : 0x20; + bias = b43_is_40mhz(dev) ? 0x40 : 0x20; b43_radio_write(dev, offset | B2056_TX_INTPAG_IMAIN_STAT, bias); @@ -1211,8 +1211,7 @@ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max, u16 bw, len, rot, angle; struct b43_c32 *samples; - - bw = (dev->phy.is_40mhz) ? 40 : 20; + bw = b43_is_40mhz(dev) ? 40 : 20; len = bw << 3; if (test) { @@ -1221,7 +1220,7 @@ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max, else bw = 80; - if (dev->phy.is_40mhz) + if (b43_is_40mhz(dev)) bw <<= 1; len = bw << 1; @@ -1264,7 +1263,7 @@ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops, } /* TODO: add modify_bbmult argument */ - if (!dev->phy.is_40mhz) + if (!b43_is_40mhz(dev)) tmp = 0x6464; else tmp = 0x4747; @@ -2194,7 +2193,7 @@ static void b43_nphy_gain_ctl_workarounds_rev1_2(struct b43_wldev *dev) b43_phy_write(dev, B43_NPHY_C1_NBCLIPTHRES, 0x84); b43_phy_write(dev, B43_NPHY_C2_NBCLIPTHRES, 0x84); - if (!dev->phy.is_40mhz) { + if (!b43_is_40mhz(dev)) { /* Set dwell lengths */ b43_phy_write(dev, B43_NPHY_CLIP1_NBDWELL_LEN, 0x002B); b43_phy_write(dev, B43_NPHY_CLIP2_NBDWELL_LEN, 0x002B); @@ -2208,7 +2207,7 @@ static void b43_nphy_gain_ctl_workarounds_rev1_2(struct b43_wldev *dev) b43_phy_maskset(dev, B43_NPHY_C2_CLIPWBTHRES, ~B43_NPHY_C2_CLIPWBTHRES_CLIP2, 21); - if (!dev->phy.is_40mhz) { + if (!b43_is_40mhz(dev)) { b43_phy_maskset(dev, B43_NPHY_C1_CGAINI, ~B43_NPHY_C1_CGAINI_GAINBKOFF, 0x1); b43_phy_maskset(dev, B43_NPHY_C2_CGAINI, @@ -2223,12 +2222,12 @@ static void b43_nphy_gain_ctl_workarounds_rev1_2(struct b43_wldev *dev) if (nphy->gain_boost) { if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ && - dev->phy.is_40mhz) + b43_is_40mhz(dev)) code = 4; else code = 5; } else { - code = dev->phy.is_40mhz ? 6 : 7; + code = b43_is_40mhz(dev) ? 6 : 7; } /* Set HPVGA2 index */ @@ -2300,7 +2299,7 @@ static void b43_nphy_gain_ctl_workarounds(struct b43_wldev *dev) static u16 b43_nphy_read_lpf_ctl(struct b43_wldev *dev, u16 offset) { if (!offset) - offset = (dev->phy.is_40mhz) ? 0x159 : 0x154; + offset = b43_is_40mhz(dev) ? 0x159 : 0x154; return b43_ntab_read(dev, B43_NTAB16(7, offset)) & 0x7; } @@ -2373,13 +2372,13 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev) lpf_40 = b43_nphy_read_lpf_ctl(dev, 0x159); lpf_11b = b43_nphy_read_lpf_ctl(dev, 0x152); if (b43_nphy_ipa(dev)) { - if ((phy->radio_rev == 5 && phy->is_40mhz) || + if ((phy->radio_rev == 5 && b43_is_40mhz(dev)) || phy->radio_rev == 7 || phy->radio_rev == 8) { bcap_val = b43_radio_read(dev, 0x16b); scap_val = b43_radio_read(dev, 0x16a); scap_val_11b = scap_val; bcap_val_11b = bcap_val; - if (phy->radio_rev == 5 && phy->is_40mhz) { + if (phy->radio_rev == 5 && b43_is_40mhz(dev)) { scap_val_11n_20 = scap_val; bcap_val_11n_20 = bcap_val; scap_val_11n_40 = bcap_val_11n_40 = 0xc; @@ -2521,7 +2520,7 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev) } } } else if (phy->radio_rev == 7 || phy->radio_rev == 8) { - if (!phy->is_40mhz) { + if (!b43_is_40mhz(dev)) { b43_radio_write(dev, 0x5F, 0x14); b43_radio_write(dev, 0xE8, 0x12); } else { @@ -2594,7 +2593,7 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev) b43_ntab_write(dev, B43_NTAB16(7, 0x123), 0x77); b43_ntab_write(dev, B43_NTAB16(7, 0x12A), 0x77); - if (!phy->is_40mhz) { + if (!b43_is_40mhz(dev)) { b43_ntab_write(dev, B43_NTAB32(16, 0x03), 0x18D); b43_ntab_write(dev, B43_NTAB32(16, 0x7F), 0x18D); } else { @@ -2693,7 +2692,7 @@ static void b43_nphy_workarounds_rev3plus(struct b43_wldev *dev) b43_phy_maskset(dev, B43_NPHY_SGILTRNOFFSET, 0xF0FF, 0x0700); - if (!dev->phy.is_40mhz) { + if (!b43_is_40mhz(dev)) { b43_ntab_write(dev, B43_NTAB32(16, 3), 0x18D); b43_ntab_write(dev, B43_NTAB32(16, 127), 0x18D); } else { @@ -3116,7 +3115,7 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable) b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, ~B43_NPHY_BPHY_CTL3_SCALE, 0x5A); - if (dev->phy.rev < 2 && dev->phy.is_40mhz) + if (dev->phy.rev < 2 && b43_is_40mhz(dev)) b43_hf_write(dev, b43_hf_read(dev) | B43_HF_TSSIRPSMW); } else { b43_ntab_write_bulk(dev, B43_NTAB16(26, 64), 84, @@ -3170,7 +3169,7 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable) else if (dev->phy.rev < 2) b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, ~0xFF, 0x40); - if (dev->phy.rev < 2 && dev->phy.is_40mhz) + if (dev->phy.rev < 2 && b43_is_40mhz(dev)) b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_TSSIRPSMW); if (b43_nphy_ipa(dev)) { @@ -3442,21 +3441,21 @@ static void b43_nphy_tx_prepare_adjusted_power_table(struct b43_wldev *dev) delta = 0; switch (stf_mode) { case 0: - if (dev->phy.is_40mhz && dev->phy.rev >= 5) { + if (b43_is_40mhz(dev) && dev->phy.rev >= 5) { idx = 68; } else { delta = 1; - idx = dev->phy.is_40mhz ? 52 : 4; + idx = b43_is_40mhz(dev) ? 52 : 4; } break; case 1: - idx = dev->phy.is_40mhz ? 76 : 28; + idx = b43_is_40mhz(dev) ? 76 : 28; break; case 2: - idx = dev->phy.is_40mhz ? 84 : 36; + idx = b43_is_40mhz(dev) ? 84 : 36; break; case 3: - idx = dev->phy.is_40mhz ? 92 : 44; + idx = b43_is_40mhz(dev) ? 92 : 44; break; } @@ -3996,7 +3995,7 @@ static void b43_nphy_spur_workaround(struct b43_wldev *dev) if (nphy->gband_spurwar_en) { /* TODO: N PHY Adjust Analog Pfbw (7) */ - if (channel == 11 && dev->phy.is_40mhz) + if (channel == 11 && b43_is_40mhz(dev)) ; /* TODO: N PHY Adjust Min Noise Var(2, tone, noise)*/ else ; /* TODO: N PHY Adjust Min Noise Var(0, NULL, NULL)*/ @@ -4290,7 +4289,7 @@ static void b43_nphy_int_pa_set_tx_dig_filters(struct b43_wldev *dev) b43_phy_write(dev, B43_PHY_N(offset[i] + j), tbl_tx_filter_coef_rev4[i][j]); - if (dev->phy.is_40mhz) { + if (b43_is_40mhz(dev)) { for (j = 0; j < 15; j++) b43_phy_write(dev, B43_PHY_N(offset[0] + j), tbl_tx_filter_coef_rev4[3][j]); @@ -4626,7 +4625,7 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev, (dev->phy.rev == 5 && nphy->ipa2g_on && b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ); if (phy6or5x) { - if (dev->phy.is_40mhz) { + if (b43_is_40mhz(dev)) { b43_ntab_write_bulk(dev, B43_NTAB16(15, 0), 18, tbl_tx_iqlo_cal_loft_ladder_40); b43_ntab_write_bulk(dev, B43_NTAB16(15, 32), 18, @@ -4641,13 +4640,13 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev, b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8AA9); - if (!dev->phy.is_40mhz) + if (!b43_is_40mhz(dev)) freq = 2500; else freq = 5000; if (nphy->mphase_cal_phase_id > 2) - b43_nphy_run_samples(dev, (dev->phy.is_40mhz ? 40 : 20) * 8, + b43_nphy_run_samples(dev, (b43_is_40mhz(dev) ? 40 : 20) * 8, 0xFFFF, 0, true, false); else error = b43_nphy_tx_tone(dev, freq, 250, true, false); From 427fa00b8953b4cc428737af2b062e8ab4de3e21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sat, 31 May 2014 20:49:41 +0200 Subject: [PATCH 014/274] b43: PHY: drop channel_type (we can get this info from chandef) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/phy_common.c | 6 ------ drivers/net/wireless/b43/phy_common.h | 3 --- drivers/net/wireless/b43/phy_n.c | 11 +++++++---- drivers/net/wireless/b43/tables_nphy.c | 2 +- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c index e7e83835f725..2d05b5987168 100644 --- a/drivers/net/wireless/b43/phy_common.c +++ b/drivers/net/wireless/b43/phy_common.c @@ -547,12 +547,6 @@ void b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on) } -bool b43_channel_type_is_40mhz(enum nl80211_channel_type channel_type) -{ - return (channel_type == NL80211_CHAN_HT40MINUS || - channel_type == NL80211_CHAN_HT40PLUS); -} - bool b43_is_40mhz(struct b43_wldev *dev) { return dev->phy.chandef->width == NL80211_CHAN_WIDTH_40; diff --git a/drivers/net/wireless/b43/phy_common.h b/drivers/net/wireless/b43/phy_common.h index 674422fd22e6..3912274f71e3 100644 --- a/drivers/net/wireless/b43/phy_common.h +++ b/drivers/net/wireless/b43/phy_common.h @@ -266,7 +266,6 @@ struct b43_phy { /* Current channel */ struct cfg80211_chan_def *chandef; unsigned int channel; - enum nl80211_channel_type channel_type; /* PHY TX errors counter. */ atomic_t txerr_cnt; @@ -447,8 +446,6 @@ int b43_phy_shm_tssi_read(struct b43_wldev *dev, u16 shm_offset); */ void b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on); -bool b43_channel_type_is_40mhz(enum nl80211_channel_type channel_type); - bool b43_is_40mhz(struct b43_wldev *dev); void b43_phy_force_clock(struct b43_wldev *dev, bool force); diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index dc1249381275..6398c7e45ab7 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -4504,7 +4504,8 @@ static void b43_nphy_save_cal(struct b43_wldev *dev) txcal_radio_regs[3] = b43_radio_read(dev, 0xBC); } iqcal_chanspec->center_freq = dev->phy.chandef->chan->center_freq; - iqcal_chanspec->channel_type = dev->phy.channel_type; + iqcal_chanspec->channel_type = + cfg80211_get_chandef_type(dev->phy.chandef); b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 8, table); if (nphy->hang_avoid) @@ -4779,7 +4780,7 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev, nphy->txiqlocal_chanspec.center_freq = phy->chandef->chan->center_freq; nphy->txiqlocal_chanspec.channel_type = - dev->phy.channel_type; + cfg80211_get_chandef_type(phy->chandef); } else { length = 11; if (dev->phy.rev < 3) @@ -4816,7 +4817,7 @@ static void b43_nphy_reapply_tx_cal_coeffs(struct b43_wldev *dev) if (!nphy->txiqlocal_coeffsvalid || nphy->txiqlocal_chanspec.center_freq != dev->phy.chandef->chan->center_freq || - nphy->txiqlocal_chanspec.channel_type != dev->phy.channel_type) + nphy->txiqlocal_chanspec.channel_type != cfg80211_get_chandef_type(dev->phy.chandef)) return; b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 7, buffer); @@ -5441,7 +5442,7 @@ static void b43_nphy_channel_setup(struct b43_wldev *dev, bool avoid = false; if (dev->phy.n->spur_avoid == B43_SPUR_AVOID_FORCE) { avoid = true; - } else if (!b43_channel_type_is_40mhz(phy->channel_type)) { + } else if (!b43_is_40mhz(dev)) { if ((ch >= 5 && ch <= 8) || ch == 13 || ch == 14) avoid = true; } else { /* 40MHz */ @@ -5507,9 +5508,11 @@ static int b43_nphy_set_channel(struct b43_wldev *dev, own to let this function's subcalls work properly. */ phy->channel = channel->hw_value; +#if 0 if (b43_channel_type_is_40mhz(phy->channel_type) != b43_channel_type_is_40mhz(channel_type)) ; /* TODO: BMAC BW Set (channel_type) */ +#endif if (channel_type == NL80211_CHAN_HT40PLUS) b43_phy_set(dev, B43_NPHY_RXCTL, diff --git a/drivers/net/wireless/b43/tables_nphy.c b/drivers/net/wireless/b43/tables_nphy.c index 4047c05e3807..b22171592926 100644 --- a/drivers/net/wireless/b43/tables_nphy.c +++ b/drivers/net/wireless/b43/tables_nphy.c @@ -3191,7 +3191,7 @@ struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent( /* Some workarounds to the workarounds... */ if (ghz5 && dev->phy.rev >= 6) { if (dev->phy.radio_rev == 11 && - !b43_channel_type_is_40mhz(dev->phy.channel_type)) + !b43_is_40mhz(dev)) e->cliplo_gain = 0x2d; } else if (!ghz5 && dev->phy.rev >= 5) { static const int gain_data[] = {0x0062, 0x0064, 0x006a, 0x106a, From c0f36ebf9f5e92d38a5859e88527109388fc398b Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Sun, 1 Jun 2014 14:39:20 +0200 Subject: [PATCH 015/274] net: wireless: libertas: cmd.c: Cleaning up uninitialized variables There is a risk that the variable will be used without being initialized. This was largely found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/cmd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c index aaa297315c47..0387a5b380c8 100644 --- a/drivers/net/wireless/libertas/cmd.c +++ b/drivers/net/wireless/libertas/cmd.c @@ -1111,6 +1111,7 @@ int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on) cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_ACT_SET); + cmd.control = 0; /* Only v8 and below support setting the preamble */ if (priv->fwrelease < 0x09000000) { From 57eaeb6efa1f732ecf61130d7e0bb67c3ad9e4af Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Sun, 1 Jun 2014 14:44:13 +0200 Subject: [PATCH 016/274] net: wireless: rt2x00: rt2x00mac.c: Cleaning up uninitialized variables There is a risk that the variable will be used without being initialized. This was largely found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00mac.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index 212ac4842c16..671836210744 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -799,6 +799,8 @@ int rt2x00mac_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) setup.tx = tx_ant; setup.rx = rx_ant; + setup.rx_chain_num = 0; + setup.tx_chain_num = 0; rt2x00lib_config_antenna(rt2x00dev, setup); From 7949513b315ac70abcd292170ba1099a9a56dbfe Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Sun, 1 Jun 2014 22:14:38 +0200 Subject: [PATCH 017/274] net: wireless: rtlwifi: rtl8192de: phy.c: Cleaning up uninitialized variable There is a risk that the variables will be used without being initialized. Have also moved variable to the part of the code where it is used. This was largely found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/rtl8192de/phy.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/phy.c b/drivers/net/wireless/rtlwifi/rtl8192de/phy.c index 3d1f0dd4e52d..592125a5f19c 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/phy.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/phy.c @@ -203,11 +203,12 @@ u32 rtl92d_phy_query_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask) struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtlpriv); u32 returnvalue, originalvalue, bitshift; - u8 dbi_direct; RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), bitmask(%#x)\n", regaddr, bitmask); if (rtlhal->during_mac1init_radioa || rtlhal->during_mac0init_radiob) { + u8 dbi_direct = 0; + /* mac1 use phy0 read radio_b. */ /* mac0 use phy1 read radio_b. */ if (rtlhal->during_mac1init_radioa) From 7d8831bb1bfbf8db896bfc1de9d0b3bc7a8e60f0 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Wed, 4 Jun 2014 15:32:32 +0200 Subject: [PATCH 018/274] mwifiex: Remove custom world regulatory domain A custom regulatory domain was introduced in this commit: cc0ba0d mwifiex: support custom world regulatory domain The commit description says that it was introduced because the world regulatory domain does not include channels 52-64 and 100-140. These channels are described in the world regulatory domain now, so we can drop this custom regulatory domain. Signed-off-by: Markus Pargmann Acked-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 36 ------------------------- 1 file changed, 36 deletions(-) diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index e95dec91a561..201edbf76c81 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -42,36 +42,6 @@ static const struct ieee80211_iface_combination mwifiex_iface_comb_ap_sta = { .beacon_int_infra_match = true, }; -static const struct ieee80211_regdomain mwifiex_world_regdom_custom = { - .n_reg_rules = 7, - .alpha2 = "99", - .reg_rules = { - /* Channel 1 - 11 */ - REG_RULE(2412-10, 2462+10, 40, 3, 20, 0), - /* Channel 12 - 13 */ - REG_RULE(2467-10, 2472+10, 20, 3, 20, - NL80211_RRF_NO_IR), - /* Channel 14 */ - REG_RULE(2484-10, 2484+10, 20, 3, 20, - NL80211_RRF_NO_IR | - NL80211_RRF_NO_OFDM), - /* Channel 36 - 48 */ - REG_RULE(5180-10, 5240+10, 40, 3, 20, - NL80211_RRF_NO_IR), - /* Channel 149 - 165 */ - REG_RULE(5745-10, 5825+10, 40, 3, 20, - NL80211_RRF_NO_IR), - /* Channel 52 - 64 */ - REG_RULE(5260-10, 5320+10, 40, 3, 30, - NL80211_RRF_NO_IR | - NL80211_RRF_DFS), - /* Channel 100 - 140 */ - REG_RULE(5500-10, 5700+10, 40, 3, 30, - NL80211_RRF_NO_IR | - NL80211_RRF_DFS), - } -}; - /* * This function maps the nl802.11 channel type into driver channel type. * @@ -2916,12 +2886,6 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | WIPHY_FLAG_TDLS_EXTERNAL_SETUP; - wiphy->regulatory_flags |= - REGULATORY_CUSTOM_REG | - REGULATORY_STRICT_REG; - - wiphy_apply_custom_regulatory(wiphy, &mwifiex_world_regdom_custom); - #ifdef CONFIG_PM wiphy->wowlan = &mwifiex_wowlan_support; #endif From 283dafa1c69475596701da7767df471c0a71d8fb Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Thu, 5 Jun 2014 13:52:23 +0200 Subject: [PATCH 019/274] rt2x00: change beaconing locking This patch is needed for further changes to keep global variables consistent when changing beaconing on diffrent vif's. Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00dev.c | 7 +++++-- drivers/net/wireless/rt2x00/rt2x00mac.c | 6 ++---- drivers/net/wireless/rt2x00/rt2x00queue.c | 21 ++------------------- 3 files changed, 9 insertions(+), 25 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 2bde6729f5e6..72e3e8138111 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -141,8 +141,11 @@ static void rt2x00lib_intf_scheduled_iter(void *data, u8 *mac, if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return; - if (test_and_clear_bit(DELAYED_UPDATE_BEACON, &intf->delayed_flags)) + if (test_and_clear_bit(DELAYED_UPDATE_BEACON, &intf->delayed_flags)) { + mutex_lock(&intf->beacon_skb_mutex); rt2x00queue_update_beacon(rt2x00dev, vif); + mutex_unlock(&intf->beacon_skb_mutex); + } } static void rt2x00lib_intf_scheduled(struct work_struct *work) @@ -216,7 +219,7 @@ static void rt2x00lib_beaconupdate_iter(void *data, u8 *mac, * never be called for USB devices. */ WARN_ON(rt2x00_is_usb(rt2x00dev)); - rt2x00queue_update_beacon_locked(rt2x00dev, vif); + rt2x00queue_update_beacon(rt2x00dev, vif); } void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev) diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index 671836210744..d63636bbb9d7 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -624,6 +624,7 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, * Start/stop beaconing. */ if (changes & BSS_CHANGED_BEACON_ENABLED) { + mutex_lock(&intf->beacon_skb_mutex); if (!bss_conf->enable_beacon && intf->enable_beacon) { rt2x00dev->intf_beaconing--; intf->enable_beacon = false; @@ -639,9 +640,7 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, * Last beaconing interface disabled * -> stop beacon queue. */ - mutex_lock(&intf->beacon_skb_mutex); rt2x00queue_stop_queue(rt2x00dev->bcn); - mutex_unlock(&intf->beacon_skb_mutex); } } else if (bss_conf->enable_beacon && !intf->enable_beacon) { rt2x00dev->intf_beaconing++; @@ -658,11 +657,10 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, * First beaconing interface enabled * -> start beacon queue. */ - mutex_lock(&intf->beacon_skb_mutex); rt2x00queue_start_queue(rt2x00dev->bcn); - mutex_unlock(&intf->beacon_skb_mutex); } } + mutex_unlock(&intf->beacon_skb_mutex); } /* diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 5642ccceca7c..8e68f87ab13c 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -754,8 +754,6 @@ int rt2x00queue_clear_beacon(struct rt2x00_dev *rt2x00dev, if (unlikely(!intf->beacon)) return -ENOBUFS; - mutex_lock(&intf->beacon_skb_mutex); - /* * Clean up the beacon skb. */ @@ -768,13 +766,11 @@ int rt2x00queue_clear_beacon(struct rt2x00_dev *rt2x00dev, if (rt2x00dev->ops->lib->clear_beacon) rt2x00dev->ops->lib->clear_beacon(intf->beacon); - mutex_unlock(&intf->beacon_skb_mutex); - return 0; } -int rt2x00queue_update_beacon_locked(struct rt2x00_dev *rt2x00dev, - struct ieee80211_vif *vif) +int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev, + struct ieee80211_vif *vif) { struct rt2x00_intf *intf = vif_to_intf(vif); struct skb_frame_desc *skbdesc; @@ -815,19 +811,6 @@ int rt2x00queue_update_beacon_locked(struct rt2x00_dev *rt2x00dev, } -int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev, - struct ieee80211_vif *vif) -{ - struct rt2x00_intf *intf = vif_to_intf(vif); - int ret; - - mutex_lock(&intf->beacon_skb_mutex); - ret = rt2x00queue_update_beacon_locked(rt2x00dev, vif); - mutex_unlock(&intf->beacon_skb_mutex); - - return ret; -} - bool rt2x00queue_for_each_entry(struct data_queue *queue, enum queue_index start, enum queue_index end, From ba08910e04e09088cac42ab6d5ceedd1239a3208 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Thu, 5 Jun 2014 13:52:24 +0200 Subject: [PATCH 020/274] rt2x00: change beaconing setup on RT2800 As reported by Matthias, on 5572 chip, even if we clear up TXWI of corresponding beacon, hardware still try to send it or do other action that increase power consumption peak up to 1A. To avoid the issue, setup beaconing dynamically by configuring offsets of currently active beacons and MAC_BSSID_DW1_BSS_BCN_NUM variable, which limit number of beacons that hardware will try to send. Reported-by: Matthias Fend Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 45 +++++++++++++++++++++++ drivers/net/wireless/rt2x00/rt2x00queue.h | 1 + 2 files changed, 46 insertions(+) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index c17fcf272728..c45b2d31ea65 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -947,6 +947,40 @@ static inline u8 rt2800_get_beacon_offset(struct rt2x00_dev *rt2x00dev, return BEACON_BASE_TO_OFFSET(rt2800_hw_beacon_base(rt2x00dev, index)); } +static void rt2800_update_beacons_setup(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue = rt2x00dev->bcn; + struct queue_entry *entry; + int i, bcn_num = 0; + u64 off, reg = 0; + u32 bssid_dw1; + + /* + * Setup offsets of all active beacons in BCN_OFFSET{0,1} registers. + */ + for (i = 0; i < queue->limit; i++) { + entry = &queue->entries[i]; + if (!test_bit(ENTRY_BCN_ENABLED, &entry->flags)) + continue; + off = rt2800_get_beacon_offset(rt2x00dev, entry->entry_idx); + reg |= off << (8 * bcn_num); + bcn_num++; + } + + WARN_ON_ONCE(bcn_num != rt2x00dev->intf_beaconing); + + rt2800_register_write(rt2x00dev, BCN_OFFSET0, (u32) reg); + rt2800_register_write(rt2x00dev, BCN_OFFSET1, (u32) (reg >> 32)); + + /* + * H/W sends up to MAC_BSSID_DW1_BSS_BCN_NUM + 1 consecutive beacons. + */ + rt2800_register_read(rt2x00dev, MAC_BSSID_DW1, &bssid_dw1); + rt2x00_set_field32(&bssid_dw1, MAC_BSSID_DW1_BSS_BCN_NUM, + bcn_num > 0 ? bcn_num - 1 : 0); + rt2800_register_write(rt2x00dev, MAC_BSSID_DW1, bssid_dw1); +} + void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; @@ -1003,6 +1037,12 @@ void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc) rt2800_register_multiwrite(rt2x00dev, beacon_base, entry->skb->data, entry->skb->len + padding_len); + __set_bit(ENTRY_BCN_ENABLED, &entry->flags); + + /* + * Change global beacons settings. + */ + rt2800_update_beacons_setup(rt2x00dev); /* * Restore beaconing state. @@ -1053,7 +1093,12 @@ void rt2800_clear_beacon(struct queue_entry *entry) * Clear beacon. */ rt2800_clear_beacon_register(rt2x00dev, entry->entry_idx); + __clear_bit(ENTRY_BCN_ENABLED, &entry->flags); + /* + * Change global beacons settings. + */ + rt2800_update_beacons_setup(rt2x00dev); /* * Restore beaconing state. */ diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h index c48125be0e34..2233b911a1d7 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.h +++ b/drivers/net/wireless/rt2x00/rt2x00queue.h @@ -353,6 +353,7 @@ struct txentry_desc { */ enum queue_entry_flags { ENTRY_BCN_ASSIGNED, + ENTRY_BCN_ENABLED, ENTRY_OWNER_DEVICE_DATA, ENTRY_DATA_PENDING, ENTRY_DATA_IO_FAILED, From 88ff2f45f23eb0f0e262251b34edb4582ce8e188 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Thu, 5 Jun 2014 13:52:25 +0200 Subject: [PATCH 021/274] rt2x00: change default MAC_BSSID_DW1_BSS_BCN_NUM We setup MAC_BSSID_DW1_BSS_BCN_NUM dynamically when numbers of active beacons increase. Change default to 0 to tell hardware that we want to send only one beacon as default. Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index c45b2d31ea65..a9cdddd2ab1a 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -1601,7 +1601,7 @@ void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, if (!is_zero_ether_addr((const u8 *)conf->bssid)) { reg = le32_to_cpu(conf->bssid[1]); rt2x00_set_field32(®, MAC_BSSID_DW1_BSS_ID_MASK, 3); - rt2x00_set_field32(®, MAC_BSSID_DW1_BSS_BCN_NUM, 7); + rt2x00_set_field32(®, MAC_BSSID_DW1_BSS_BCN_NUM, 0); conf->bssid[1] = cpu_to_le32(reg); } From ddb405506cc2273e2a367383831b50053464929b Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Thu, 5 Jun 2014 13:52:26 +0200 Subject: [PATCH 022/274] rt2x00: change order when stop beaconing When no beaconing is needed, first stop beacon queue (disable beaconing globally) to avoid possible sending of not prepared beacon on short period after clearing beacon and before stop of BCN queue. Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00mac.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index d63636bbb9d7..e5935ea3719f 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -628,12 +628,6 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, if (!bss_conf->enable_beacon && intf->enable_beacon) { rt2x00dev->intf_beaconing--; intf->enable_beacon = false; - /* - * Clear beacon in the H/W for this vif. This is needed - * to disable beaconing on this particular interface - * and keep it running on other interfaces. - */ - rt2x00queue_clear_beacon(rt2x00dev, vif); if (rt2x00dev->intf_beaconing == 0) { /* @@ -642,6 +636,12 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, */ rt2x00queue_stop_queue(rt2x00dev->bcn); } + /* + * Clear beacon in the H/W for this vif. This is needed + * to disable beaconing on this particular interface + * and keep it running on other interfaces. + */ + rt2x00queue_clear_beacon(rt2x00dev, vif); } else if (bss_conf->enable_beacon && !intf->enable_beacon) { rt2x00dev->intf_beaconing++; intf->enable_beacon = true; From 19dcb76842d6d51690188c49c5c3142077f8ec5a Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Thu, 5 Jun 2014 13:52:27 +0200 Subject: [PATCH 023/274] rt2x00: do not initialize BCN_OFFSET registers We setup BCN_OFFSET{0,1} registers dynamically, don't have to initialize them. Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index a9cdddd2ab1a..893c9d5f3d6f 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -4562,28 +4562,6 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) if (ret) return ret; - rt2800_register_read(rt2x00dev, BCN_OFFSET0, ®); - rt2x00_set_field32(®, BCN_OFFSET0_BCN0, - rt2800_get_beacon_offset(rt2x00dev, 0)); - rt2x00_set_field32(®, BCN_OFFSET0_BCN1, - rt2800_get_beacon_offset(rt2x00dev, 1)); - rt2x00_set_field32(®, BCN_OFFSET0_BCN2, - rt2800_get_beacon_offset(rt2x00dev, 2)); - rt2x00_set_field32(®, BCN_OFFSET0_BCN3, - rt2800_get_beacon_offset(rt2x00dev, 3)); - rt2800_register_write(rt2x00dev, BCN_OFFSET0, reg); - - rt2800_register_read(rt2x00dev, BCN_OFFSET1, ®); - rt2x00_set_field32(®, BCN_OFFSET1_BCN4, - rt2800_get_beacon_offset(rt2x00dev, 4)); - rt2x00_set_field32(®, BCN_OFFSET1_BCN5, - rt2800_get_beacon_offset(rt2x00dev, 5)); - rt2x00_set_field32(®, BCN_OFFSET1_BCN6, - rt2800_get_beacon_offset(rt2x00dev, 6)); - rt2x00_set_field32(®, BCN_OFFSET1_BCN7, - rt2800_get_beacon_offset(rt2x00dev, 7)); - rt2800_register_write(rt2x00dev, BCN_OFFSET1, reg); - rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE, 0x0000013f); rt2800_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003); From f0db59e1c491e921e412148ce05558bfcf9aaa64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Thu, 5 Jun 2014 20:20:44 +0200 Subject: [PATCH 024/274] bcma: gpio: register all 32 GPIOs on BCM53572 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I've here a device detected as: bcma: bus0: Found chip with id 0xD144, rev 0x01 and package 0x08 I couldn't find GPIO handling hw button until trying GPIO 20. It seems BCM53572 also has 32 GPIOs. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/bcma/driver_gpio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/bcma/driver_gpio.c b/drivers/bcma/driver_gpio.c index d7f81ad56b8a..aec9f850b4a8 100644 --- a/drivers/bcma/driver_gpio.c +++ b/drivers/bcma/driver_gpio.c @@ -220,6 +220,7 @@ int bcma_gpio_init(struct bcma_drv_cc *cc) #endif switch (cc->core->bus->chipinfo.id) { case BCMA_CHIP_ID_BCM5357: + case BCMA_CHIP_ID_BCM53572: chip->ngpio = 32; break; default: From 4dbc13fae485c2519e995b2e0d8580641f14cad0 Mon Sep 17 00:00:00 2001 From: Zhiyuan Yang Date: Fri, 6 Jun 2014 19:47:42 -0700 Subject: [PATCH 025/274] mwifiex: support wowlan magic-packet encapsulated as UDP packet When magic-packet is generated as a UDP packet the offset should be 20+8 more bytes to cover IPv4 header and UDP header. So the total offset become 56. Add a new MEF entry to support both magic-packet patterns generated by different tools. Cc: Andreas Fenkart Signed-off-by: Zhiyuan Yang Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 201edbf76c81..e8981afb208b 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -2453,6 +2453,16 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, mef_entry->filter[filt_num].filt_type = TYPE_EQ; if (filt_num) mef_entry->filter[filt_num].filt_action = TYPE_OR; + + filt_num++; + mef_entry->filter[filt_num].repeat = 16; + memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr, + ETH_ALEN); + mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = + ETH_ALEN; + mef_entry->filter[filt_num].offset = 56; + mef_entry->filter[filt_num].filt_type = TYPE_EQ; + mef_entry->filter[filt_num].filt_action = TYPE_OR; } if (!mef_cfg.criteria) From ef0a68a832cccfc8400aa32b2a3d65e16d29ae9d Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Fri, 6 Jun 2014 19:47:44 -0700 Subject: [PATCH 026/274] mwifiex: wowlan: do not disconnect on suspend For users who do not need wowlan, load mwifiex.ko with disconnect_on_suspend = 1; or iw phy0 wowlan disable. Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/sta_ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index 536c14aa71f3..229526356d9b 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -26,7 +26,7 @@ #include "11n.h" #include "cfg80211.h" -static int disconnect_on_suspend = 1; +static int disconnect_on_suspend; module_param(disconnect_on_suspend, int, 0644); /* From b101426aae371b91ad10c7553e54725cc5844e4a Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 7 Jun 2014 07:18:30 +0400 Subject: [PATCH 027/274] rsi: Use module_usb_driver module_usb_driver eliminates the boilerplate and makes the code simpler, in addition to the fact currently rsi_module_init() ignores usb_deregister() error. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: John W. Linville --- drivers/net/wireless/rsi/rsi_91x_usb.c | 28 +------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index 4c46e5631e2f..3226f19989e7 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -550,33 +550,7 @@ static struct usb_driver rsi_driver = { #endif }; -/** - * rsi_module_init() - This function registers the client driver. - * @void: Void. - * - * Return: 0 on success. - */ -static int rsi_module_init(void) -{ - usb_register(&rsi_driver); - rsi_dbg(INIT_ZONE, "%s: Registering driver\n", __func__); - return 0; -} - -/** - * rsi_module_exit() - This function unregisters the client driver. - * @void: Void. - * - * Return: None. - */ -static void rsi_module_exit(void) -{ - usb_deregister(&rsi_driver); - rsi_dbg(INFO_ZONE, "%s: Unregistering driver\n", __func__); -} - -module_init(rsi_module_init); -module_exit(rsi_module_exit); +module_usb_driver(rsi_driver); MODULE_AUTHOR("Redpine Signals Inc"); MODULE_DESCRIPTION("Common USB layer for RSI drivers"); From e860c33f7d05907bb21ea6f0c2b085b75b028e3d Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 7 Jun 2014 07:18:31 +0400 Subject: [PATCH 028/274] rsi_91x_sdio: add error handling into rsi_module_init() Fix rsi_module_init() to propagate sdio_register_driver() errors. Signed-off-by: Alexey Khoroshilov Signed-off-by: John W. Linville --- drivers/net/wireless/rsi/rsi_91x_sdio.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio.c b/drivers/net/wireless/rsi/rsi_91x_sdio.c index 46e7af446f01..8428858204a6 100644 --- a/drivers/net/wireless/rsi/rsi_91x_sdio.c +++ b/drivers/net/wireless/rsi/rsi_91x_sdio.c @@ -820,9 +820,11 @@ static struct sdio_driver rsi_driver = { */ static int rsi_module_init(void) { - sdio_register_driver(&rsi_driver); + int ret; + + ret = sdio_register_driver(&rsi_driver); rsi_dbg(INIT_ZONE, "%s: Registering driver\n", __func__); - return 0; + return ret; } /** From 6437f51ec36af8ef1e3e2659439b35c37e5498e2 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 10 Jun 2014 10:37:24 -0700 Subject: [PATCH 029/274] rtlwifi: btcoexist: avoid format string in printk Since CL_PRINTF only ever takes a single argument, make sure a format string cannot leak into printk. Signed-off-by: Kees Cook Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.h b/drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.h index 871fc3c6d559..049f4c8d98a8 100644 --- a/drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.h +++ b/drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.h @@ -114,7 +114,7 @@ extern u32 btc_dbg_type[]; #define CL_SPRINTF snprintf -#define CL_PRINTF printk +#define CL_PRINTF(buf) printk("%s", buf) #define BTC_PRINT(dbgtype, dbgflag, printstr, ...) \ do { \ From fbbcd14690d3c42b664740d58a22af50a77d5689 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 11 Jun 2014 16:17:49 +0530 Subject: [PATCH 030/274] ath9k: Add channel context structure The channel context structure is defined to enable multi-channel concurrency support. Signed-off-by: Felix Fietkau Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/Makefile | 3 +- drivers/net/wireless/ath/ath9k/ath9k.h | 18 +++ drivers/net/wireless/ath/ath9k/channel.c | 133 +++++++++++++++++++++++ drivers/net/wireless/ath/ath9k/init.c | 1 + drivers/net/wireless/ath/ath9k/link.c | 2 +- drivers/net/wireless/ath/ath9k/main.c | 111 ++----------------- drivers/net/wireless/ath/ath9k/recv.c | 4 +- 7 files changed, 169 insertions(+), 103 deletions(-) create mode 100644 drivers/net/wireless/ath/ath9k/channel.c diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 8fcd586d1c39..6b4020a57984 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -5,7 +5,8 @@ ath9k-y += beacon.o \ recv.o \ xmit.o \ link.o \ - antenna.o + antenna.o \ + channel.o ath9k-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += mci.o ath9k-$(CONFIG_ATH9K_PCI) += pci.o diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 2ca8f7e06174..8c87eb7fb6de 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -325,6 +325,16 @@ struct ath_rx { u32 ampdu_ref; }; +struct ath_chanctx { + struct cfg80211_chan_def chandef; + struct list_head vifs; + bool offchannel; +}; + +void ath_chanctx_init(struct ath_softc *sc); +int ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx, + struct cfg80211_chan_def *chandef); +int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan); int ath_startrecv(struct ath_softc *sc); bool ath_stoprecv(struct ath_softc *sc); u32 ath_calcrxfilter(struct ath_softc *sc); @@ -370,12 +380,15 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw, /********/ struct ath_vif { + struct list_head list; + struct ieee80211_vif *vif; struct ath_node mcast_node; int av_bslot; bool primary_sta_vif; __le64 tsf_adjust; /* TSF adjustment for staggered beacons */ struct ath_buf *av_bcbuf; + struct ath_chanctx *chanctx; /* P2P Client */ struct ieee80211_noa_data noa; @@ -702,6 +715,8 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs); #define PS_BEACON_SYNC BIT(4) #define PS_WAIT_FOR_ANI BIT(5) +#define ATH9K_NUM_CHANCTX 2 /* supports 2 operating channels */ + struct ath_softc { struct ieee80211_hw *hw; struct device *dev; @@ -743,6 +758,9 @@ struct ath_softc { struct ath_tx tx; struct ath_beacon beacon; + struct ath_chanctx chanctx[ATH9K_NUM_CHANCTX]; + struct ath_chanctx *cur_chan; + #ifdef CONFIG_MAC80211_LEDS bool led_registered; char led_name[32]; diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c new file mode 100644 index 000000000000..aee6cdb4975b --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2014 Qualcomm Atheros, 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 "ath9k.h" + +/* Set/change channels. If the channel is really being changed, it's done + * by reseting the chip. To accomplish this we must first cleanup any pending + * DMA, then restart stuff. + */ +static int ath_set_channel(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ieee80211_hw *hw = sc->hw; + struct ath9k_channel *hchan; + struct cfg80211_chan_def *chandef = &sc->cur_chan->chandef; + struct ieee80211_channel *chan = chandef->chan; + int pos = chan->hw_value; + int old_pos = -1; + int r; + + if (test_bit(ATH_OP_INVALID, &common->op_flags)) + return -EIO; + + if (ah->curchan) + old_pos = ah->curchan - &ah->channels[0]; + + ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n", + chan->center_freq, chandef->width); + + /* update survey stats for the old channel before switching */ + spin_lock_bh(&common->cc_lock); + ath_update_survey_stats(sc); + spin_unlock_bh(&common->cc_lock); + + ath9k_cmn_get_channel(hw, ah, chandef); + + /* If the operating channel changes, change the survey in-use flags + * along with it. + * Reset the survey data for the new channel, unless we're switching + * back to the operating channel from an off-channel operation. + */ + if (!sc->cur_chan->offchannel && sc->cur_survey != &sc->survey[pos]) { + if (sc->cur_survey) + sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE; + + sc->cur_survey = &sc->survey[pos]; + + memset(sc->cur_survey, 0, sizeof(struct survey_info)); + sc->cur_survey->filled |= SURVEY_INFO_IN_USE; + } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) { + memset(&sc->survey[pos], 0, sizeof(struct survey_info)); + } + + hchan = &sc->sc_ah->channels[pos]; + r = ath_reset_internal(sc, hchan); + if (r) + return r; + + /* The most recent snapshot of channel->noisefloor for the old + * channel is only available after the hardware reset. Copy it to + * the survey stats now. + */ + if (old_pos >= 0) + ath_update_survey_nf(sc, old_pos); + + /* Enable radar pulse detection if on a DFS channel. Spectral + * scanning and radar detection can not be used concurrently. + */ + if (hw->conf.radar_enabled) { + u32 rxfilter; + + /* set HW specific DFS configuration */ + ath9k_hw_set_radar_params(ah); + rxfilter = ath9k_hw_getrxfilter(ah); + rxfilter |= ATH9K_RX_FILTER_PHYRADAR | + ATH9K_RX_FILTER_PHYERR; + ath9k_hw_setrxfilter(ah, rxfilter); + ath_dbg(common, DFS, "DFS enabled at freq %d\n", + chan->center_freq); + } else { + /* perform spectral scan if requested. */ + if (test_bit(ATH_OP_SCANNING, &common->op_flags) && + sc->spectral_mode == SPECTRAL_CHANSCAN) + ath9k_spectral_scan_trigger(hw); + } + + return 0; +} + +void ath_chanctx_init(struct ath_softc *sc) +{ + struct ath_chanctx *ctx; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ieee80211_supported_band *sband; + struct ieee80211_channel *chan; + int i; + + sband = &common->sbands[IEEE80211_BAND_2GHZ]; + if (!sband->n_channels) + sband = &common->sbands[IEEE80211_BAND_5GHZ]; + + chan = &sband->channels[0]; + for (i = 0; i < ATH9K_NUM_CHANCTX; i++) { + ctx = &sc->chanctx[i]; + cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20); + INIT_LIST_HEAD(&ctx->vifs); + } + sc->cur_chan = &sc->chanctx[0]; +} + +int ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx, + struct cfg80211_chan_def *chandef) +{ + memcpy(&ctx->chandef, chandef, sizeof(ctx->chandef)); + if (ctx != sc->cur_chan) + return 0; + + return ath_set_channel(sc); +} diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 0246b990fe87..32d954275d47 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -599,6 +599,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, ath9k_cmn_init_crypto(sc->sc_ah); ath9k_init_misc(sc); ath_fill_led_pin(sc); + ath_chanctx_init(sc); if (common->bus_ops->aspm_init) common->bus_ops->aspm_init(common); diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index 72a715fe8f24..6f91974c7b08 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -416,7 +416,7 @@ void ath_start_ani(struct ath_softc *sc) if (common->disable_ani || !test_bit(ATH_OP_ANI_RUN, &common->op_flags) || - (sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) + sc->cur_chan->offchannel) return; common->ani.longcal_timer = timestamp; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 62ac95d6bb9d..2e7cce7b1238 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -233,7 +233,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) ath9k_hw_set_interrupts(ah); ath9k_hw_enable_interrupts(ah); - if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) && start) { + if (!sc->cur_chan->offchannel && start) { if (!test_bit(ATH_OP_BEACONS, &common->op_flags)) goto work; @@ -266,7 +266,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) return true; } -static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan) +int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); @@ -279,7 +279,7 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan) tasklet_disable(&sc->intr_tq); spin_lock_bh(&sc->sc_pcu_lock); - if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) { + if (!sc->cur_chan->offchannel) { fastcc = false; caldata = &sc->caldata; } @@ -307,7 +307,7 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan) } if (ath9k_hw_mci_is_enabled(sc->sc_ah) && - (sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) + sc->cur_chan->offchannel) ath9k_mci_set_txpower(sc, true, false); if (!ath_complete_reset(sc, true)) @@ -320,98 +320,6 @@ out: return r; } - -/* - * Set/change channels. If the channel is really being changed, it's done - * by reseting the chip. To accomplish this we must first cleanup any pending - * DMA, then restart stuff. -*/ -static int ath_set_channel(struct ath_softc *sc, struct cfg80211_chan_def *chandef) -{ - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - struct ieee80211_hw *hw = sc->hw; - struct ath9k_channel *hchan; - struct ieee80211_channel *chan = chandef->chan; - bool offchannel; - int pos = chan->hw_value; - int old_pos = -1; - int r; - - if (test_bit(ATH_OP_INVALID, &common->op_flags)) - return -EIO; - - offchannel = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL); - - if (ah->curchan) - old_pos = ah->curchan - &ah->channels[0]; - - ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n", - chan->center_freq, chandef->width); - - /* update survey stats for the old channel before switching */ - spin_lock_bh(&common->cc_lock); - ath_update_survey_stats(sc); - spin_unlock_bh(&common->cc_lock); - - ath9k_cmn_get_channel(hw, ah, chandef); - - /* - * If the operating channel changes, change the survey in-use flags - * along with it. - * Reset the survey data for the new channel, unless we're switching - * back to the operating channel from an off-channel operation. - */ - if (!offchannel && sc->cur_survey != &sc->survey[pos]) { - if (sc->cur_survey) - sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE; - - sc->cur_survey = &sc->survey[pos]; - - memset(sc->cur_survey, 0, sizeof(struct survey_info)); - sc->cur_survey->filled |= SURVEY_INFO_IN_USE; - } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) { - memset(&sc->survey[pos], 0, sizeof(struct survey_info)); - } - - hchan = &sc->sc_ah->channels[pos]; - r = ath_reset_internal(sc, hchan); - if (r) - return r; - - /* - * The most recent snapshot of channel->noisefloor for the old - * channel is only available after the hardware reset. Copy it to - * the survey stats now. - */ - if (old_pos >= 0) - ath_update_survey_nf(sc, old_pos); - - /* - * Enable radar pulse detection if on a DFS channel. Spectral - * scanning and radar detection can not be used concurrently. - */ - if (hw->conf.radar_enabled) { - u32 rxfilter; - - /* set HW specific DFS configuration */ - ath9k_hw_set_radar_params(ah); - rxfilter = ath9k_hw_getrxfilter(ah); - rxfilter |= ATH9K_RX_FILTER_PHYRADAR | - ATH9K_RX_FILTER_PHYERR; - ath9k_hw_setrxfilter(ah, rxfilter); - ath_dbg(common, DFS, "DFS enabled at freq %d\n", - chan->center_freq); - } else { - /* perform spectral scan if requested. */ - if (test_bit(ATH_OP_SCANNING, &common->op_flags) && - sc->spectral_mode == SPECTRAL_CHANSCAN) - ath9k_spectral_scan_trigger(hw); - } - - return 0; -} - static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta, struct ieee80211_vif *vif) { @@ -713,6 +621,7 @@ static int ath9k_start(struct ieee80211_hw *hw) struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_channel *curchan = hw->conf.chandef.chan; + struct ath_chanctx *ctx = sc->cur_chan; struct ath9k_channel *init_channel; int r; @@ -723,7 +632,8 @@ static int ath9k_start(struct ieee80211_hw *hw) ath9k_ps_wakeup(sc); mutex_lock(&sc->mutex); - init_channel = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef); + memcpy(&ctx->chandef, &hw->conf.chandef, sizeof(ctx->chandef)); + init_channel = ath9k_cmn_get_channel(hw, ah, &ctx->chandef); /* Reset SERDES registers */ ath9k_hw_configpcipowersave(ah, false); @@ -934,7 +844,8 @@ static void ath9k_stop(struct ieee80211_hw *hw) } if (!ah->curchan) - ah->curchan = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef); + ah->curchan = ath9k_cmn_get_channel(hw, ah, + &sc->cur_chan->chandef); ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); ath9k_hw_phy_disable(ah); @@ -1345,6 +1256,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &hw->conf; + struct ath_chanctx *ctx = sc->cur_chan; bool reset_channel = false; ath9k_ps_wakeup(sc); @@ -1392,7 +1304,8 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) } if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || reset_channel) { - if (ath_set_channel(sc, &hw->conf.chandef) < 0) { + ctx->offchannel = !!(conf->flags & IEEE80211_CONF_OFFCHANNEL); + if (ath_chanctx_set_channel(sc, ctx, &hw->conf.chandef) < 0) { ath_err(common, "Unable to set channel\n"); mutex_unlock(&sc->mutex); ath9k_ps_restore(sc); diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 9105a92364f7..de5684a33dd7 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -259,7 +259,7 @@ static void ath_edma_start_recv(struct ath_softc *sc) ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_HP); ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_LP); ath_opmode_init(sc); - ath9k_hw_startpcureceive(sc->sc_ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)); + ath9k_hw_startpcureceive(sc->sc_ah, sc->cur_chan->offchannel); } static void ath_edma_stop_recv(struct ath_softc *sc) @@ -457,7 +457,7 @@ int ath_startrecv(struct ath_softc *sc) start_recv: ath_opmode_init(sc); - ath9k_hw_startpcureceive(ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)); + ath9k_hw_startpcureceive(ah, sc->cur_chan->offchannel); return 0; } From bc7e1be70c9f1c6de622aa14baa62003342034bb Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 11 Jun 2014 16:17:50 +0530 Subject: [PATCH 031/274] ath9k: Move txpower limit to channel context Signed-off-by: Felix Fietkau Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 6 +----- drivers/net/wireless/ath/ath9k/channel.c | 1 + drivers/net/wireless/ath/ath9k/init.c | 5 ++--- drivers/net/wireless/ath/ath9k/main.c | 6 +++--- drivers/net/wireless/ath/ath9k/mci.c | 2 +- 5 files changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 8c87eb7fb6de..b6f6444d109e 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -36,10 +36,6 @@ extern int ath9k_modparam_nohwcrypt; extern int led_blink; extern bool is_ath9k_unloaded; -struct ath_config { - u16 txpowlimit; -}; - /*************************/ /* Descriptor Management */ /*************************/ @@ -328,6 +324,7 @@ struct ath_rx { struct ath_chanctx { struct cfg80211_chan_def chandef; struct list_head vifs; + u16 txpower; bool offchannel; }; @@ -753,7 +750,6 @@ struct ath_softc { short nvifs; unsigned long ps_usecount; - struct ath_config config; struct ath_rx rx; struct ath_tx tx; struct ath_beacon beacon; diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index aee6cdb4975b..1b40cf5aa955 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -118,6 +118,7 @@ void ath_chanctx_init(struct ath_softc *sc) ctx = &sc->chanctx[i]; cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20); INIT_LIST_HEAD(&ctx->vifs); + ctx->txpower = ATH_TXPOWER_MAX; } sc->cur_chan = &sc->chanctx[0]; } diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 32d954275d47..ffd42bfb74b4 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -169,9 +169,9 @@ static void ath9k_reg_notifier(struct wiphy *wiphy, /* Set tx power */ if (ah->curchan) { - sc->config.txpowlimit = 2 * ah->curchan->chan->max_power; + sc->cur_chan->txpower = 2 * ah->curchan->chan->max_power; ath9k_ps_wakeup(sc); - ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false); + ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false); sc->curtxpow = ath9k_hw_regulatory(ah)->power_limit; /* synchronize DFS detector if regulatory domain changed */ if (sc->dfs_detector != NULL) @@ -335,7 +335,6 @@ static void ath9k_init_misc(struct ath_softc *sc) setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc); common->last_rssi = ATH_RSSI_DUMMY_MARKER; - sc->config.txpowlimit = ATH_TXPOWER_MAX; memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); sc->beacon.slottime = ATH9K_SLOT_TIME_9; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 2e7cce7b1238..5a9cee4a8935 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -227,7 +227,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) } ath9k_cmn_update_txpow(ah, sc->curtxpow, - sc->config.txpowlimit, &sc->curtxpow); + sc->cur_chan->txpower, &sc->curtxpow); clear_bit(ATH_OP_HW_RESET, &common->op_flags); ath9k_hw_set_interrupts(ah); @@ -1315,9 +1315,9 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) if (changed & IEEE80211_CONF_CHANGE_POWER) { ath_dbg(common, CONFIG, "Set power: %d\n", conf->power_level); - sc->config.txpowlimit = 2 * conf->power_level; + sc->cur_chan->txpower = 2 * conf->power_level; ath9k_cmn_update_txpow(ah, sc->curtxpow, - sc->config.txpowlimit, &sc->curtxpow); + sc->cur_chan->txpower, &sc->curtxpow); } mutex_unlock(&sc->mutex); diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index a0dbcc412384..313ed995585a 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -720,7 +720,7 @@ void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel, mci_hw->concur_tx = concur_tx; if (old_concur_tx != mci_hw->concur_tx) - ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false); + ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false); } static void ath9k_mci_stomp_audio(struct ath_softc *sc) From 0453531e2eae61c5c0a2af7b67cdafd19c0dce68 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 11 Jun 2014 16:17:51 +0530 Subject: [PATCH 032/274] ath9k: Move acq to channel context Add support to maintain per-channel ACs list. Signed-off-by: Felix Fietkau Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 4 +- drivers/net/wireless/ath/ath9k/channel.c | 4 +- drivers/net/wireless/ath/ath9k/main.c | 25 +++++----- drivers/net/wireless/ath/ath9k/xmit.c | 58 ++++++++++++++++++------ 4 files changed, 63 insertions(+), 28 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index b6f6444d109e..198cca38c24b 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -163,7 +163,6 @@ struct ath_txq { u32 axq_ampdu_depth; bool stopped; bool axq_tx_inprogress; - struct list_head axq_acq; struct list_head txq_fifo[ATH_TXFIFO_DEPTH]; u8 txq_headidx; u8 txq_tailidx; @@ -324,6 +323,8 @@ struct ath_rx { struct ath_chanctx { struct cfg80211_chan_def chandef; struct list_head vifs; + struct list_head acq[IEEE80211_NUM_ACS]; + u16 txpower; bool offchannel; }; @@ -348,6 +349,7 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq); void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an); void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an); void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq); +void ath_txq_schedule_all(struct ath_softc *sc); int ath_tx_init(struct ath_softc *sc, int nbufs); int ath_txq_update(struct ath_softc *sc, int qnum, struct ath9k_tx_queue_info *q); diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 1b40cf5aa955..c8d91dfcb3b9 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -107,7 +107,7 @@ void ath_chanctx_init(struct ath_softc *sc) struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; - int i; + int i, j; sband = &common->sbands[IEEE80211_BAND_2GHZ]; if (!sband->n_channels) @@ -119,6 +119,8 @@ void ath_chanctx_init(struct ath_softc *sc) cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20); INIT_LIST_HEAD(&ctx->vifs); ctx->txpower = ATH_TXPOWER_MAX; + for (j = 0; j < ARRAY_SIZE(ctx->acq); j++) + INIT_LIST_HEAD(&ctx->acq[j]); } sc->cur_chan = &sc->chanctx[0]; } diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 5a9cee4a8935..5f7e0bf4c986 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -63,9 +63,16 @@ static bool ath9k_has_pending_frames(struct ath_softc *sc, struct ath_txq *txq) spin_lock_bh(&txq->axq_lock); - if (txq->axq_depth || !list_empty(&txq->axq_acq)) + if (txq->axq_depth) pending = true; + if (txq->mac80211_qnum >= 0) { + struct list_head *list; + + list = &sc->cur_chan->acq[txq->mac80211_qnum]; + if (!list_empty(list)) + pending = true; + } spin_unlock_bh(&txq->axq_lock); return pending; } @@ -219,7 +226,6 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); unsigned long flags; - int i; if (ath_startrecv(sc) != 0) { ath_err(common, "Unable to restart recv logic\n"); @@ -247,15 +253,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) } work: ath_restart_work(sc); - - for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { - if (!ATH_TXQ_SETUP(sc, i)) - continue; - - spin_lock_bh(&sc->tx.txq[i].axq_lock); - ath_txq_schedule(sc, &sc->tx.txq[i]); - spin_unlock_bh(&sc->tx.txq[i].axq_lock); - } + ath_txq_schedule_all(sc); } sc->gtt_cnt = 0; @@ -1035,6 +1033,10 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, avp->vif = vif; + /* XXX - will be removed once chanctx ops are added */ + avp->chanctx = sc->cur_chan; + list_add_tail(&avp->list, &sc->cur_chan->vifs); + an->sc = sc; an->sta = NULL; an->vif = vif; @@ -1120,6 +1122,7 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, } spin_unlock_bh(&sc->sc_pcu_lock); + list_del(&avp->list); sc->nvifs--; sc->tx99_vif = NULL; diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 66acb2cbd9df..b2e66d21af1c 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -103,9 +103,16 @@ void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq) ieee80211_tx_status(sc->hw, skb); } -static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid) +static void ath_tx_queue_tid(struct ath_softc *sc, struct ath_txq *txq, + struct ath_atx_tid *tid) { struct ath_atx_ac *ac = tid->ac; + struct list_head *list; + struct ath_vif *avp = (struct ath_vif *) tid->an->vif->drv_priv; + struct ath_chanctx *ctx = avp->chanctx; + + if (!ctx) + return; if (tid->sched) return; @@ -117,7 +124,9 @@ static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid) return; ac->sched = true; - list_add_tail(&ac->list, &txq->axq_acq); + + list = &ctx->acq[TID_TO_WME_AC(tid->tidno)]; + list_add_tail(&ac->list, list); } static struct ath_frame_info *get_frame_info(struct sk_buff *skb) @@ -626,7 +635,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, skb_queue_splice_tail(&bf_pending, &tid->retry_q); if (!an->sleeping) { - ath_tx_queue_tid(txq, tid); + ath_tx_queue_tid(sc, txq, tid); if (ts->ts_status & (ATH9K_TXERR_FILT | ATH9K_TXERR_XRETRY)) tid->ac->clear_ps_filter = true; @@ -1483,7 +1492,7 @@ void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an) ac->clear_ps_filter = true; if (ath_tid_has_buffered(tid)) { - ath_tx_queue_tid(txq, tid); + ath_tx_queue_tid(sc, txq, tid); ath_txq_schedule(sc, txq); } @@ -1507,7 +1516,7 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, tid->baw_size = IEEE80211_MIN_AMPDU_BUF << sta->ht_cap.ampdu_factor; if (ath_tid_has_buffered(tid)) { - ath_tx_queue_tid(txq, tid); + ath_tx_queue_tid(sc, txq, tid); ath_txq_schedule(sc, txq); } @@ -1642,7 +1651,6 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) txq->axq_link = NULL; __skb_queue_head_init(&txq->complete_q); INIT_LIST_HEAD(&txq->axq_q); - INIT_LIST_HEAD(&txq->axq_acq); spin_lock_init(&txq->axq_lock); txq->axq_depth = 0; txq->axq_ampdu_depth = 0; @@ -1804,7 +1812,7 @@ void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq) sc->tx.txqsetup &= ~(1<axq_qnum); } -/* For each axq_acq entry, for each tid, try to schedule packets +/* For each acq entry, for each tid, try to schedule packets * for transmit until ampdu_depth has reached min Q depth. */ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) @@ -1812,19 +1820,25 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_atx_ac *ac, *last_ac; struct ath_atx_tid *tid, *last_tid; + struct list_head *ac_list; bool sent = false; + if (txq->mac80211_qnum < 0) + return; + + ac_list = &sc->cur_chan->acq[txq->mac80211_qnum]; + if (test_bit(ATH_OP_HW_RESET, &common->op_flags) || - list_empty(&txq->axq_acq)) + list_empty(ac_list)) return; rcu_read_lock(); - last_ac = list_entry(txq->axq_acq.prev, struct ath_atx_ac, list); - while (!list_empty(&txq->axq_acq)) { + last_ac = list_entry(ac_list->prev, struct ath_atx_ac, list); + while (!list_empty(ac_list)) { bool stop = false; - ac = list_first_entry(&txq->axq_acq, struct ath_atx_ac, list); + ac = list_first_entry(ac_list, struct ath_atx_ac, list); last_tid = list_entry(ac->tid_q.prev, struct ath_atx_tid, list); list_del(&ac->list); ac->sched = false; @@ -1844,7 +1858,7 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) * are pending for the tid */ if (ath_tid_has_buffered(tid)) - ath_tx_queue_tid(txq, tid); + ath_tx_queue_tid(sc, txq, tid); if (stop || tid == last_tid) break; @@ -1852,7 +1866,7 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) if (!list_empty(&ac->tid_q) && !ac->sched) { ac->sched = true; - list_add_tail(&ac->list, &txq->axq_acq); + list_add_tail(&ac->list, ac_list); } if (stop) @@ -1863,7 +1877,7 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) break; sent = false; - last_ac = list_entry(txq->axq_acq.prev, + last_ac = list_entry(ac_list->prev, struct ath_atx_ac, list); } } @@ -1871,6 +1885,20 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) rcu_read_unlock(); } +void ath_txq_schedule_all(struct ath_softc *sc) +{ + struct ath_txq *txq; + int i; + + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + txq = sc->tx.txq_map[i]; + + spin_lock_bh(&txq->axq_lock); + ath_txq_schedule(sc, txq); + spin_unlock_bh(&txq->axq_lock); + } +} + /***********/ /* TX, DMA */ /***********/ @@ -2198,7 +2226,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, TX_STAT_INC(txq->axq_qnum, a_queued_sw); __skb_queue_tail(&tid->buf_q, skb); if (!txctl->an->sleeping) - ath_tx_queue_tid(txq, tid); + ath_tx_queue_tid(sc, txq, tid); ath_txq_schedule(sc, txq); goto out; From bff117669841c04d08bd1d23617818e0030b3299 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 11 Jun 2014 16:17:52 +0530 Subject: [PATCH 033/274] ath9k: Add channel context worker thread The channel context worker is used to switch to next requested channel context. Signed-off-by: Felix Fietkau Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 13 ++++- drivers/net/wireless/ath/ath9k/beacon.c | 2 +- drivers/net/wireless/ath/ath9k/channel.c | 64 +++++++++++++++++++++--- drivers/net/wireless/ath/ath9k/init.c | 2 + drivers/net/wireless/ath/ath9k/link.c | 2 +- drivers/net/wireless/ath/ath9k/main.c | 29 +++++++---- drivers/net/wireless/ath/ath9k/wow.c | 1 + drivers/net/wireless/ath/ath9k/xmit.c | 7 +++ 8 files changed, 99 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 198cca38c24b..8f59cea33fd6 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -327,11 +327,14 @@ struct ath_chanctx { u16 txpower; bool offchannel; + bool stopped; }; void ath_chanctx_init(struct ath_softc *sc); -int ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx, - struct cfg80211_chan_def *chandef); +void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx, + struct cfg80211_chan_def *chandef); +void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx, + struct cfg80211_chan_def *chandef); int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan); int ath_startrecv(struct ath_softc *sc); bool ath_stoprecv(struct ath_softc *sc); @@ -470,6 +473,7 @@ void ath9k_csa_update(struct ath_softc *sc); #define ATH_PAPRD_TIMEOUT 100 /* msecs */ #define ATH_PLL_WORK_INTERVAL 100 +void ath_chanctx_work(struct work_struct *work); void ath_tx_complete_poll_work(struct work_struct *work); void ath_reset_work(struct work_struct *work); bool ath_hw_check(struct ath_softc *sc); @@ -485,6 +489,7 @@ void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type); void ath_ps_full_sleep(unsigned long data); void ath9k_p2p_ps_timer(void *priv); void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif); +void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop); /**********/ /* BTCOEX */ @@ -734,6 +739,7 @@ struct ath_softc { struct mutex mutex; struct work_struct paprd_work; struct work_struct hw_reset_work; + struct work_struct chanctx_work; struct completion paprd_complete; wait_queue_head_t tx_wait; @@ -756,8 +762,11 @@ struct ath_softc { struct ath_tx tx; struct ath_beacon beacon; + struct cfg80211_chan_def cur_chandef; struct ath_chanctx chanctx[ATH9K_NUM_CHANCTX]; struct ath_chanctx *cur_chan; + struct ath_chanctx *next_chan; + spinlock_t chan_lock; #ifdef CONFIG_MAC80211_LEDS bool led_registered; diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index e387f0b2954a..eae8f686f125 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -80,7 +80,7 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif, u8 chainmask = ah->txchainmask; u8 rate = 0; - sband = &common->sbands[common->hw->conf.chandef.chan->band]; + sband = &common->sbands[sc->cur_chandef.chan->band]; rate = sband->bitrates[rateidx].hw_value; if (vif->bss_conf.use_short_preamble) rate |= sband->bitrates[rateidx].hw_value_short; diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index c8d91dfcb3b9..86404d38901e 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -101,6 +101,39 @@ static int ath_set_channel(struct ath_softc *sc) return 0; } +void ath_chanctx_work(struct work_struct *work) +{ + struct ath_softc *sc = container_of(work, struct ath_softc, + chanctx_work); + + mutex_lock(&sc->mutex); + spin_lock_bh(&sc->chan_lock); + if (!sc->next_chan) { + spin_unlock_bh(&sc->chan_lock); + mutex_unlock(&sc->mutex); + return; + } + + if (sc->cur_chan != sc->next_chan) { + sc->cur_chan->stopped = true; + spin_unlock_bh(&sc->chan_lock); + + __ath9k_flush(sc->hw, ~0, true); + + spin_lock_bh(&sc->chan_lock); + } + sc->cur_chan = sc->next_chan; + sc->cur_chan->stopped = false; + sc->next_chan = NULL; + spin_unlock_bh(&sc->chan_lock); + + if (sc->sc_ah->chip_fullsleep || + memcmp(&sc->cur_chandef, &sc->cur_chan->chandef, + sizeof(sc->cur_chandef))) + ath_set_channel(sc); + mutex_unlock(&sc->mutex); +} + void ath_chanctx_init(struct ath_softc *sc) { struct ath_chanctx *ctx; @@ -125,12 +158,31 @@ void ath_chanctx_init(struct ath_softc *sc) sc->cur_chan = &sc->chanctx[0]; } -int ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx, - struct cfg80211_chan_def *chandef) +void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx, + struct cfg80211_chan_def *chandef) { - memcpy(&ctx->chandef, chandef, sizeof(ctx->chandef)); - if (ctx != sc->cur_chan) - return 0; - return ath_set_channel(sc); + spin_lock_bh(&sc->chan_lock); + sc->next_chan = ctx; + if (chandef) + ctx->chandef = *chandef; + spin_unlock_bh(&sc->chan_lock); + ieee80211_queue_work(sc->hw, &sc->chanctx_work); +} + +void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx, + struct cfg80211_chan_def *chandef) +{ + bool cur_chan; + + spin_lock_bh(&sc->chan_lock); + if (chandef) + memcpy(&ctx->chandef, chandef, sizeof(*chandef)); + cur_chan = sc->cur_chan == ctx; + spin_unlock_bh(&sc->chan_lock); + + if (!cur_chan) + return; + + ath_set_channel(sc); } diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index ffd42bfb74b4..8bd3e422b82b 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -555,6 +555,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, spin_lock_init(&common->cc_lock); spin_lock_init(&sc->sc_serial_rw); spin_lock_init(&sc->sc_pm_lock); + spin_lock_init(&sc->chan_lock); mutex_init(&sc->mutex); tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc); tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet, @@ -563,6 +564,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, setup_timer(&sc->sleep_timer, ath_ps_full_sleep, (unsigned long)sc); INIT_WORK(&sc->hw_reset_work, ath_reset_work); INIT_WORK(&sc->paprd_work, ath_paprd_calibrate); + INIT_WORK(&sc->chanctx_work, ath_chanctx_work); INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work); /* diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index 6f91974c7b08..d9c01d563cc2 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -178,7 +178,7 @@ static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int txctl.txq = sc->tx.txq_map[IEEE80211_AC_BE]; memset(tx_info, 0, sizeof(*tx_info)); - tx_info->band = hw->conf.chandef.chan->band; + tx_info->band = sc->cur_chandef.chan->band; tx_info->flags |= IEEE80211_TX_CTL_NO_ACK; tx_info->control.rates[0].idx = 0; tx_info->control.rates[0].count = 1; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 5f7e0bf4c986..bb73a23f19f8 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -290,6 +290,12 @@ int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan) if (!ath_prepare_reset(sc)) fastcc = false; + if (hchan) { + spin_lock_bh(&sc->chan_lock); + sc->cur_chandef = sc->cur_chan->chandef; + spin_unlock_bh(&sc->chan_lock); + } + ath_dbg(common, CONFIG, "Reset to %u MHz, HT40: %d fastcc: %d\n", hchan->channel, IS_CHAN_HT40(hchan), fastcc); @@ -630,8 +636,8 @@ static int ath9k_start(struct ieee80211_hw *hw) ath9k_ps_wakeup(sc); mutex_lock(&sc->mutex); - memcpy(&ctx->chandef, &hw->conf.chandef, sizeof(ctx->chandef)); init_channel = ath9k_cmn_get_channel(hw, ah, &ctx->chandef); + sc->cur_chandef = hw->conf.chandef; /* Reset SERDES registers */ ath9k_hw_configpcipowersave(ah, false); @@ -794,6 +800,7 @@ static void ath9k_stop(struct ieee80211_hw *hw) struct ath_common *common = ath9k_hw_common(ah); bool prev_idle; + cancel_work_sync(&sc->chanctx_work); mutex_lock(&sc->mutex); ath_cancel_work(sc); @@ -1308,12 +1315,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || reset_channel) { ctx->offchannel = !!(conf->flags & IEEE80211_CONF_OFFCHANNEL); - if (ath_chanctx_set_channel(sc, ctx, &hw->conf.chandef) < 0) { - ath_err(common, "Unable to set channel\n"); - mutex_unlock(&sc->mutex); - ath9k_ps_restore(sc); - return -EINVAL; - } + ath_chanctx_set_channel(sc, ctx, &hw->conf.chandef); } if (changed & IEEE80211_CONF_CHANGE_POWER) { @@ -1946,6 +1948,15 @@ static bool ath9k_has_tx_pending(struct ath_softc *sc) static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 queues, bool drop) +{ + struct ath_softc *sc = hw->priv; + + mutex_lock(&sc->mutex); + __ath9k_flush(hw, queues, drop); + mutex_unlock(&sc->mutex); +} + +void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) { struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; @@ -1953,18 +1964,15 @@ static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int timeout = HZ / 5; /* 200 ms */ bool drain_txq; - mutex_lock(&sc->mutex); cancel_delayed_work_sync(&sc->tx_complete_work); if (ah->ah_flags & AH_UNPLUGGED) { ath_dbg(common, ANY, "Device has been unplugged!\n"); - mutex_unlock(&sc->mutex); return; } if (test_bit(ATH_OP_INVALID, &common->op_flags)) { ath_dbg(common, ANY, "Device not present\n"); - mutex_unlock(&sc->mutex); return; } @@ -1986,7 +1994,6 @@ static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } ieee80211_queue_delayed_work(hw, &sc->tx_complete_work, 0); - mutex_unlock(&sc->mutex); } static bool ath9k_tx_frames_pending(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/ath/ath9k/wow.c b/drivers/net/wireless/ath/ath9k/wow.c index 2879887f5691..a4f4f0da81f6 100644 --- a/drivers/net/wireless/ath/ath9k/wow.c +++ b/drivers/net/wireless/ath/ath9k/wow.c @@ -193,6 +193,7 @@ int ath9k_suspend(struct ieee80211_hw *hw, u32 wow_triggers_enabled = 0; int ret = 0; + cancel_work_sync(&sc->chanctx_work); mutex_lock(&sc->mutex); ath_cancel_work(sc); diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index b2e66d21af1c..5aaed39459d2 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1826,18 +1826,24 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) if (txq->mac80211_qnum < 0) return; + spin_lock_bh(&sc->chan_lock); ac_list = &sc->cur_chan->acq[txq->mac80211_qnum]; + spin_unlock_bh(&sc->chan_lock); if (test_bit(ATH_OP_HW_RESET, &common->op_flags) || list_empty(ac_list)) return; + spin_lock_bh(&sc->chan_lock); rcu_read_lock(); last_ac = list_entry(ac_list->prev, struct ath_atx_ac, list); while (!list_empty(ac_list)) { bool stop = false; + if (sc->cur_chan->stopped) + break; + ac = list_first_entry(ac_list, struct ath_atx_ac, list); last_tid = list_entry(ac->tid_q.prev, struct ath_atx_tid, list); list_del(&ac->list); @@ -1883,6 +1889,7 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) } rcu_read_unlock(); + spin_unlock_bh(&sc->chan_lock); } void ath_txq_schedule_all(struct ath_softc *sc) From befcf7e70e899db62307408259c51e0435bd9b3f Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 11 Jun 2014 16:17:53 +0530 Subject: [PATCH 034/274] ath9k: channel context based transmission Force queueing of all frames that belong to a virtual interface on a different channel context, to ensure that they are sent on the correct channel. Signed-off-by: Felix Fietkau Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 3 ++- drivers/net/wireless/ath/ath9k/xmit.c | 24 +++++++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 8f59cea33fd6..ddd2e81ccd58 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -275,8 +275,9 @@ struct ath_node { struct ath_tx_control { struct ath_txq *txq; struct ath_node *an; - u8 paprd; struct ieee80211_sta *sta; + u8 paprd; + bool force_channel; }; diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 5aaed39459d2..7972e1e24dd2 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2185,13 +2185,18 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_sta *sta = txctl->sta; struct ieee80211_vif *vif = info->control.vif; + struct ath_vif *avp = NULL; struct ath_softc *sc = hw->priv; struct ath_txq *txq = txctl->txq; struct ath_atx_tid *tid = NULL; struct ath_buf *bf; + bool queue; int q; int ret; + if (vif) + avp = (void *)vif->drv_priv; + ret = ath_tx_prepare(hw, skb, txctl); if (ret) return ret; @@ -2212,15 +2217,28 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, txq->stopped = true; } - if (txctl->an && ieee80211_is_data_present(hdr->frame_control)) + queue = ieee80211_is_data_present(hdr->frame_control); + + /* Force queueing of all frames that belong to a virtual interface on + * a different channel context, to ensure that they are sent on the + * correct channel. + */ + if (((avp && avp->chanctx != sc->cur_chan) || + sc->cur_chan->stopped) && !txctl->force_channel) { + if (!txctl->an) + txctl->an = &avp->mcast_node; + info->flags &= ~IEEE80211_TX_CTL_PS_RESPONSE; + queue = true; + } + + if (txctl->an && queue) tid = ath_get_skb_tid(sc, txctl->an, skb); if (info->flags & IEEE80211_TX_CTL_PS_RESPONSE) { ath_txq_unlock(sc, txq); txq = sc->tx.uapsdq; ath_txq_lock(sc, txq); - } else if (txctl->an && - ieee80211_is_data_present(hdr->frame_control)) { + } else if (txctl->an && queue) { WARN_ON(tid->ac->txq != txctl->txq); if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) From c083ce9980109065297aa2171d18980a0ac92bb9 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 11 Jun 2014 16:17:54 +0530 Subject: [PATCH 035/274] ath9k: send powersave frame on channel switch While leaving from or entering to active channel context, send out nullfunc frame to inform to the AP about the presence of station. Signed-off-by: Felix Fietkau Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 3 + drivers/net/wireless/ath/ath9k/channel.c | 97 ++++++++++++++++++++++++ drivers/net/wireless/ath/ath9k/main.c | 6 ++ 3 files changed, 106 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index ddd2e81ccd58..d5a586bbab7c 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -329,6 +329,7 @@ struct ath_chanctx { u16 txpower; bool offchannel; bool stopped; + bool active; }; void ath_chanctx_init(struct ath_softc *sc); @@ -336,6 +337,8 @@ void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx, struct cfg80211_chan_def *chandef); void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx, struct cfg80211_chan_def *chandef); +void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx); + int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan); int ath_startrecv(struct ath_softc *sc); bool ath_stoprecv(struct ath_softc *sc); diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 86404d38901e..26fc98b3495b 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -101,10 +101,99 @@ static int ath_set_channel(struct ath_softc *sc) return 0; } +static bool +ath_chanctx_send_vif_ps_frame(struct ath_softc *sc, struct ath_vif *avp, + bool powersave) +{ + struct ieee80211_vif *vif = avp->vif; + struct ieee80211_sta *sta = NULL; + struct ieee80211_hdr_3addr *nullfunc; + struct ath_tx_control txctl; + struct sk_buff *skb; + int band = sc->cur_chan->chandef.chan->band; + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + if (!vif->bss_conf.assoc) + return false; + + skb = ieee80211_nullfunc_get(sc->hw, vif); + if (!skb) + return false; + + nullfunc = (struct ieee80211_hdr_3addr *) skb->data; + if (powersave) + nullfunc->frame_control |= + cpu_to_le16(IEEE80211_FCTL_PM); + + skb_set_queue_mapping(skb, IEEE80211_AC_VO); + if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, &sta)) { + dev_kfree_skb_any(skb); + return false; + } + break; + default: + return false; + } + + memset(&txctl, 0, sizeof(txctl)); + txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO]; + txctl.sta = sta; + txctl.force_channel = true; + if (ath_tx_start(sc->hw, skb, &txctl)) { + ieee80211_free_txskb(sc->hw, skb); + return false; + } + + return true; +} + +void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx) +{ + struct ath_vif *avp; + bool active = false; + + if (!ctx) + return; + + list_for_each_entry(avp, &ctx->vifs, list) { + struct ieee80211_vif *vif = avp->vif; + + switch (vif->type) { + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_STATION: + if (vif->bss_conf.assoc) + active = true; + break; + default: + active = true; + break; + } + } + ctx->active = active; +} + +static bool +ath_chanctx_send_ps_frame(struct ath_softc *sc, bool powersave) +{ + struct ath_vif *avp; + bool sent = false; + + rcu_read_lock(); + list_for_each_entry(avp, &sc->cur_chan->vifs, list) { + if (ath_chanctx_send_vif_ps_frame(sc, avp, powersave)) + sent = true; + } + rcu_read_unlock(); + + return sent; +} + void ath_chanctx_work(struct work_struct *work) { struct ath_softc *sc = container_of(work, struct ath_softc, chanctx_work); + bool send_ps = false; mutex_lock(&sc->mutex); spin_lock_bh(&sc->chan_lock); @@ -120,6 +209,10 @@ void ath_chanctx_work(struct work_struct *work) __ath9k_flush(sc->hw, ~0, true); + if (ath_chanctx_send_ps_frame(sc, true)) + __ath9k_flush(sc->hw, BIT(IEEE80211_AC_VO), false); + + send_ps = true; spin_lock_bh(&sc->chan_lock); } sc->cur_chan = sc->next_chan; @@ -131,6 +224,10 @@ void ath_chanctx_work(struct work_struct *work) memcmp(&sc->cur_chandef, &sc->cur_chan->chandef, sizeof(sc->cur_chandef))) ath_set_channel(sc); + + if (send_ps) + ath_chanctx_send_ps_frame(sc, false); + mutex_unlock(&sc->mutex); } diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index bb73a23f19f8..d8a4510f963a 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1043,6 +1043,7 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, /* XXX - will be removed once chanctx ops are added */ avp->chanctx = sc->cur_chan; list_add_tail(&avp->list, &sc->cur_chan->vifs); + ath_chanctx_check_active(sc, avp->chanctx); an->sc = sc; an->sta = NULL; @@ -1061,6 +1062,7 @@ static int ath9k_change_interface(struct ieee80211_hw *hw, { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_vif *avp = (void *)vif->drv_priv; mutex_lock(&sc->mutex); @@ -1083,6 +1085,7 @@ static int ath9k_change_interface(struct ieee80211_hw *hw, if (ath9k_uses_beacons(vif->type)) ath9k_beacon_assign_slot(sc, vif); + ath_chanctx_check_active(sc, avp->chanctx); mutex_unlock(&sc->mutex); return 0; @@ -1141,6 +1144,7 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, ath9k_ps_restore(sc); ath_tx_node_cleanup(sc, &avp->mcast_node); + ath_chanctx_check_active(sc, avp->chanctx); mutex_unlock(&sc->mutex); } @@ -1733,6 +1737,8 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, if (ath9k_hw_mci_is_enabled(sc->sc_ah)) ath9k_mci_update_wlan_channels(sc, true); } + + ath_chanctx_check_active(sc, avp->chanctx); } if (changed & BSS_CHANGED_IBSS) { From 78b21949711ee3c877f1aab5b51abe1981e1161d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 11 Jun 2014 16:17:55 +0530 Subject: [PATCH 036/274] ath9k: Implement hw_scan support Implement hw_scan support for enabling multi-channel cuncurrency. Signed-off-by: Felix Fietkau Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 24 +++ drivers/net/wireless/ath/ath9k/channel.c | 31 ++++ drivers/net/wireless/ath/ath9k/init.c | 9 +- drivers/net/wireless/ath/ath9k/main.c | 187 +++++++++++++++++++++++ drivers/net/wireless/ath/ath9k/pci.c | 1 + drivers/net/wireless/ath/ath9k/recv.c | 5 + 6 files changed, 255 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index d5a586bbab7c..d6b0c4e55c95 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -35,6 +35,7 @@ extern struct ieee80211_ops ath9k_ops; extern int ath9k_modparam_nohwcrypt; extern int led_blink; extern bool is_ath9k_unloaded; +extern int ath9k_use_chanctx; /*************************/ /* Descriptor Management */ @@ -332,12 +333,34 @@ struct ath_chanctx { bool active; }; +enum ath_offchannel_state { + ATH_OFFCHANNEL_IDLE, + ATH_OFFCHANNEL_PROBE_SEND, + ATH_OFFCHANNEL_PROBE_WAIT, + ATH_OFFCHANNEL_SUSPEND, +}; + +struct ath_offchannel { + struct ath_chanctx chan; + struct timer_list timer; + struct cfg80211_scan_request *scan_req; + struct ieee80211_vif *scan_vif; + int scan_idx; + enum ath_offchannel_state state; +}; + +void ath9k_fill_chanctx_ops(void); void ath_chanctx_init(struct ath_softc *sc); void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx, struct cfg80211_chan_def *chandef); void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx, struct cfg80211_chan_def *chandef); void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx); +void ath_offchannel_timer(unsigned long data); +void ath_offchannel_channel_change(struct ath_softc *sc); +void ath_chanctx_offchan_switch(struct ath_softc *sc, + struct ieee80211_channel *chan); +struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc); int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan); int ath_startrecv(struct ath_softc *sc); @@ -771,6 +794,7 @@ struct ath_softc { struct ath_chanctx *cur_chan; struct ath_chanctx *next_chan; spinlock_t chan_lock; + struct ath_offchannel offchannel; #ifdef CONFIG_MAC80211_LEDS bool led_registered; diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 26fc98b3495b..c679a26045ac 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -228,6 +228,7 @@ void ath_chanctx_work(struct work_struct *work) if (send_ps) ath_chanctx_send_ps_frame(sc, false); + ath_offchannel_channel_change(sc); mutex_unlock(&sc->mutex); } @@ -253,6 +254,14 @@ void ath_chanctx_init(struct ath_softc *sc) INIT_LIST_HEAD(&ctx->acq[j]); } sc->cur_chan = &sc->chanctx[0]; + ctx = &sc->offchannel.chan; + cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20); + INIT_LIST_HEAD(&ctx->vifs); + ctx->txpower = ATH_TXPOWER_MAX; + for (j = 0; j < ARRAY_SIZE(ctx->acq); j++) + INIT_LIST_HEAD(&ctx->acq[j]); + sc->offchannel.chan.offchannel = true; + } void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx, @@ -283,3 +292,25 @@ void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx, ath_set_channel(sc); } + +struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc) +{ + u8 i; + + for (i = 0; i < ARRAY_SIZE(sc->chanctx); i++) { + if (!list_empty(&sc->chanctx[i].vifs)) + return &sc->chanctx[i]; + } + + return &sc->chanctx[0]; +} + +void ath_chanctx_offchan_switch(struct ath_softc *sc, + struct ieee80211_channel *chan) +{ + struct cfg80211_chan_def chandef; + + cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); + + ath_chanctx_switch(sc, &sc->offchannel.chan, &chandef); +} diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 8bd3e422b82b..9f5e1e4931af 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -61,7 +61,7 @@ static int ath9k_ps_enable; module_param_named(ps_enable, ath9k_ps_enable, int, 0444); MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave"); -static int ath9k_use_chanctx; +int ath9k_use_chanctx; module_param_named(use_chanctx, ath9k_use_chanctx, int, 0444); MODULE_PARM_DESC(use_chanctx, "Enable channel context for concurrency"); @@ -566,6 +566,8 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, INIT_WORK(&sc->paprd_work, ath_paprd_calibrate); INIT_WORK(&sc->chanctx_work, ath_chanctx_work); INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work); + setup_timer(&sc->offchannel.timer, ath_offchannel_timer, + (unsigned long)sc); /* * Cache line size is used to size and align various @@ -745,8 +747,11 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) if (!ath9k_use_chanctx) { hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_WDS); - } else + } else { hw->wiphy->n_iface_combinations = 1; + hw->wiphy->max_scan_ssids = 255; + hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; + } } hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index d8a4510f963a..5da62ef1fc26 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2159,6 +2159,193 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw) clear_bit(ATH_OP_SCANNING, &common->op_flags); } +static void +ath_scan_next_channel(struct ath_softc *sc) +{ + struct cfg80211_scan_request *req = sc->offchannel.scan_req; + struct ieee80211_channel *chan; + + if (sc->offchannel.scan_idx >= req->n_channels) { + sc->offchannel.state = ATH_OFFCHANNEL_IDLE; + ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc), NULL); + return; + } + + chan = req->channels[sc->offchannel.scan_idx++]; + sc->offchannel.state = ATH_OFFCHANNEL_PROBE_SEND; + ath_chanctx_offchan_switch(sc, chan); +} + +static void ath_scan_complete(struct ath_softc *sc, bool abort) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + + ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc), NULL); + sc->offchannel.scan_req = NULL; + sc->offchannel.scan_vif = NULL; + sc->offchannel.state = ATH_OFFCHANNEL_IDLE; + ieee80211_scan_completed(sc->hw, abort); + clear_bit(ATH_OP_SCANNING, &common->op_flags); + ath9k_ps_restore(sc); + + if (!sc->ps_idle) + return; + + ath_cancel_work(sc); +} + +static void ath_scan_send_probe(struct ath_softc *sc, + struct cfg80211_ssid *ssid) +{ + struct cfg80211_scan_request *req = sc->offchannel.scan_req; + struct ieee80211_vif *vif = sc->offchannel.scan_vif; + struct ath_tx_control txctl = {}; + struct sk_buff *skb; + struct ieee80211_tx_info *info; + int band = sc->offchannel.chan.chandef.chan->band; + + skb = ieee80211_probereq_get(sc->hw, vif, + ssid->ssid, ssid->ssid_len, req->ie_len); + if (!skb) + return; + + info = IEEE80211_SKB_CB(skb); + if (req->no_cck) + info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE; + + if (req->ie_len) + memcpy(skb_put(skb, req->ie_len), req->ie, req->ie_len); + + skb_set_queue_mapping(skb, IEEE80211_AC_VO); + + if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, NULL)) + goto error; + + txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO]; + txctl.force_channel = true; + if (ath_tx_start(sc->hw, skb, &txctl)) + goto error; + + return; + +error: + ieee80211_free_txskb(sc->hw, skb); +} + +static void ath_scan_channel_start(struct ath_softc *sc) +{ + struct cfg80211_scan_request *req = sc->offchannel.scan_req; + int i, dwell; + + if ((sc->cur_chan->chandef.chan->flags & IEEE80211_CHAN_NO_IR) || + !req->n_ssids) { + dwell = HZ / 9; /* ~110 ms */ + } else { + dwell = HZ / 16; /* ~60 ms */ + + for (i = 0; i < req->n_ssids; i++) + ath_scan_send_probe(sc, &req->ssids[i]); + } + + sc->offchannel.state = ATH_OFFCHANNEL_PROBE_WAIT; + mod_timer(&sc->offchannel.timer, jiffies + dwell); +} + +void ath_offchannel_channel_change(struct ath_softc *sc) +{ + if (!sc->offchannel.scan_req) + return; + + switch (sc->offchannel.state) { + case ATH_OFFCHANNEL_PROBE_SEND: + if (sc->cur_chan->chandef.chan != + sc->offchannel.chan.chandef.chan) + return; + + ath_scan_channel_start(sc); + break; + case ATH_OFFCHANNEL_IDLE: + ath_scan_complete(sc, false); + break; + default: + break; + } +} + +void ath_offchannel_timer(unsigned long data) +{ + struct ath_softc *sc = (struct ath_softc *)data; + struct ath_chanctx *ctx = ath_chanctx_get_oper_chan(sc); + + if (!sc->offchannel.scan_req) + return; + + switch (sc->offchannel.state) { + case ATH_OFFCHANNEL_PROBE_WAIT: + if (ctx->active) { + sc->offchannel.state = ATH_OFFCHANNEL_SUSPEND; + ath_chanctx_switch(sc, ctx, NULL); + mod_timer(&sc->offchannel.timer, jiffies + HZ / 10); + break; + } + /* fall through */ + case ATH_OFFCHANNEL_SUSPEND: + ath_scan_next_channel(sc); + break; + default: + break; + } +} + +static int ath9k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct cfg80211_scan_request *req) +{ + struct ath_softc *sc = hw->priv; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + int ret = 0; + + mutex_lock(&sc->mutex); + + if (WARN_ON(sc->offchannel.scan_req)) { + ret = -EBUSY; + goto out; + } + + ath9k_ps_wakeup(sc); + set_bit(ATH_OP_SCANNING, &common->op_flags); + sc->offchannel.scan_vif = vif; + sc->offchannel.scan_req = req; + sc->offchannel.scan_idx = 0; + sc->offchannel.chan.txpower = vif->bss_conf.txpower; + + ath_scan_next_channel(sc); + +out: + mutex_unlock(&sc->mutex); + + return ret; +} + +static void ath9k_cancel_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath_softc *sc = hw->priv; + + mutex_lock(&sc->mutex); + del_timer_sync(&sc->offchannel.timer); + ath_scan_complete(sc, true); + mutex_unlock(&sc->mutex); +} + +void ath9k_fill_chanctx_ops(void) +{ + if (!ath9k_use_chanctx) + return; + + ath9k_ops.hw_scan = ath9k_hw_scan; + ath9k_ops.cancel_hw_scan = ath9k_cancel_hw_scan; +} + struct ieee80211_ops ath9k_ops = { .tx = ath9k_tx, .start = ath9k_start, diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index 4dec09e565ed..7a2b2c5caced 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -843,6 +843,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) return -ENODEV; } + ath9k_fill_chanctx_ops(); hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops); if (!hw) { dev_err(&pdev->dev, "No memory for ieee80211_hw\n"); diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index de5684a33dd7..fec9e0b42f5d 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -374,6 +374,7 @@ void ath_rx_cleanup(struct ath_softc *sc) u32 ath_calcrxfilter(struct ath_softc *sc) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); u32 rfilt; if (config_enabled(CONFIG_ATH9K_TX99)) @@ -424,6 +425,10 @@ u32 ath_calcrxfilter(struct ath_softc *sc) if (AR_SREV_9550(sc->sc_ah) || AR_SREV_9531(sc->sc_ah)) rfilt |= ATH9K_RX_FILTER_4ADDRESS; + if (ath9k_use_chanctx && + test_bit(ATH_OP_SCANNING, &common->op_flags)) + rfilt |= ATH9K_RX_FILTER_BEACON; + return rfilt; } From 405393cfde07781c21cdee28b145919d6dfe382e Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 11 Jun 2014 16:17:56 +0530 Subject: [PATCH 037/274] ath9k: Implement remain-on-channal support Add remain on channel support in order to enable multi-channel concurrency. Signed-off-by: Felix Fietkau Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 6 ++ drivers/net/wireless/ath/ath9k/init.c | 1 + drivers/net/wireless/ath/ath9k/main.c | 126 ++++++++++++++++++++++--- drivers/net/wireless/ath/ath9k/xmit.c | 6 +- 4 files changed, 124 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index d6b0c4e55c95..a8ae3e9b49fa 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -338,6 +338,9 @@ enum ath_offchannel_state { ATH_OFFCHANNEL_PROBE_SEND, ATH_OFFCHANNEL_PROBE_WAIT, ATH_OFFCHANNEL_SUSPEND, + ATH_OFFCHANNEL_ROC_START, + ATH_OFFCHANNEL_ROC_WAIT, + ATH_OFFCHANNEL_ROC_DONE, }; struct ath_offchannel { @@ -347,6 +350,9 @@ struct ath_offchannel { struct ieee80211_vif *scan_vif; int scan_idx; enum ath_offchannel_state state; + struct ieee80211_channel *roc_chan; + struct ieee80211_vif *roc_vif; + int roc_duration; }; void ath9k_fill_chanctx_ops(void); diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 9f5e1e4931af..001b7d02345c 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -751,6 +751,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->wiphy->n_iface_combinations = 1; hw->wiphy->max_scan_ssids = 255; hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; + hw->wiphy->max_remain_on_channel_duration = 10000; } } diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 5da62ef1fc26..9e10434dbfa5 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2176,22 +2176,48 @@ ath_scan_next_channel(struct ath_softc *sc) ath_chanctx_offchan_switch(sc, chan); } +static void ath_offchannel_next(struct ath_softc *sc) +{ + struct ieee80211_vif *vif; + + if (sc->offchannel.scan_req) { + vif = sc->offchannel.scan_vif; + sc->offchannel.chan.txpower = vif->bss_conf.txpower; + ath_scan_next_channel(sc); + } else if (sc->offchannel.roc_vif) { + vif = sc->offchannel.roc_vif; + sc->offchannel.chan.txpower = vif->bss_conf.txpower; + sc->offchannel.state = ATH_OFFCHANNEL_ROC_START; + ath_chanctx_offchan_switch(sc, sc->offchannel.roc_chan); + } else { + ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc), NULL); + sc->offchannel.state = ATH_OFFCHANNEL_IDLE; + if (sc->ps_idle) + ath_cancel_work(sc); + } +} + +static void ath_roc_complete(struct ath_softc *sc, bool abort) +{ + sc->offchannel.roc_vif = NULL; + sc->offchannel.roc_chan = NULL; + if (!abort) + ieee80211_remain_on_channel_expired(sc->hw); + ath_offchannel_next(sc); + ath9k_ps_restore(sc); +} + static void ath_scan_complete(struct ath_softc *sc, bool abort) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); - ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc), NULL); sc->offchannel.scan_req = NULL; sc->offchannel.scan_vif = NULL; sc->offchannel.state = ATH_OFFCHANNEL_IDLE; ieee80211_scan_completed(sc->hw, abort); clear_bit(ATH_OP_SCANNING, &common->op_flags); + ath_offchannel_next(sc); ath9k_ps_restore(sc); - - if (!sc->ps_idle) - return; - - ath_cancel_work(sc); } static void ath_scan_send_probe(struct ath_softc *sc, @@ -2253,11 +2279,11 @@ static void ath_scan_channel_start(struct ath_softc *sc) void ath_offchannel_channel_change(struct ath_softc *sc) { - if (!sc->offchannel.scan_req) - return; - switch (sc->offchannel.state) { case ATH_OFFCHANNEL_PROBE_SEND: + if (!sc->offchannel.scan_req) + return; + if (sc->cur_chan->chandef.chan != sc->offchannel.chan.chandef.chan) return; @@ -2265,8 +2291,23 @@ void ath_offchannel_channel_change(struct ath_softc *sc) ath_scan_channel_start(sc); break; case ATH_OFFCHANNEL_IDLE: + if (!sc->offchannel.scan_req) + return; + ath_scan_complete(sc, false); break; + case ATH_OFFCHANNEL_ROC_START: + if (sc->cur_chan != &sc->offchannel.chan) + break; + + sc->offchannel.state = ATH_OFFCHANNEL_ROC_WAIT; + mod_timer(&sc->offchannel.timer, jiffies + + msecs_to_jiffies(sc->offchannel.roc_duration)); + ieee80211_ready_on_channel(sc->hw); + break; + case ATH_OFFCHANNEL_ROC_DONE: + ath_roc_complete(sc, false); + break; default: break; } @@ -2277,11 +2318,11 @@ void ath_offchannel_timer(unsigned long data) struct ath_softc *sc = (struct ath_softc *)data; struct ath_chanctx *ctx = ath_chanctx_get_oper_chan(sc); - if (!sc->offchannel.scan_req) - return; - switch (sc->offchannel.state) { case ATH_OFFCHANNEL_PROBE_WAIT: + if (!sc->offchannel.scan_req) + return; + if (ctx->active) { sc->offchannel.state = ATH_OFFCHANNEL_SUSPEND; ath_chanctx_switch(sc, ctx, NULL); @@ -2290,8 +2331,16 @@ void ath_offchannel_timer(unsigned long data) } /* fall through */ case ATH_OFFCHANNEL_SUSPEND: + if (!sc->offchannel.scan_req) + return; + ath_scan_next_channel(sc); break; + case ATH_OFFCHANNEL_ROC_START: + case ATH_OFFCHANNEL_ROC_WAIT: + sc->offchannel.state = ATH_OFFCHANNEL_ROC_DONE; + ath_chanctx_switch(sc, ctx, NULL); + break; default: break; } @@ -2316,9 +2365,9 @@ static int ath9k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, sc->offchannel.scan_vif = vif; sc->offchannel.scan_req = req; sc->offchannel.scan_idx = 0; - sc->offchannel.chan.txpower = vif->bss_conf.txpower; - ath_scan_next_channel(sc); + if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE) + ath_offchannel_next(sc); out: mutex_unlock(&sc->mutex); @@ -2337,6 +2386,53 @@ static void ath9k_cancel_hw_scan(struct ieee80211_hw *hw, mutex_unlock(&sc->mutex); } +static int ath9k_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel *chan, int duration, + enum ieee80211_roc_type type) +{ + struct ath_softc *sc = hw->priv; + int ret = 0; + + mutex_lock(&sc->mutex); + + if (WARN_ON(sc->offchannel.roc_vif)) { + ret = -EBUSY; + goto out; + } + + ath9k_ps_wakeup(sc); + sc->offchannel.roc_vif = vif; + sc->offchannel.roc_chan = chan; + sc->offchannel.roc_duration = duration; + + if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE) + ath_offchannel_next(sc); + +out: + mutex_unlock(&sc->mutex); + + return ret; +} + +static int ath9k_cancel_remain_on_channel(struct ieee80211_hw *hw) +{ + struct ath_softc *sc = hw->priv; + + mutex_lock(&sc->mutex); + + del_timer_sync(&sc->offchannel.timer); + + if (sc->offchannel.roc_vif) { + if (sc->offchannel.state >= ATH_OFFCHANNEL_ROC_START) + ath_roc_complete(sc, true); + } + + mutex_unlock(&sc->mutex); + + return 0; +} + void ath9k_fill_chanctx_ops(void) { if (!ath9k_use_chanctx) @@ -2344,6 +2440,8 @@ void ath9k_fill_chanctx_ops(void) ath9k_ops.hw_scan = ath9k_hw_scan; ath9k_ops.cancel_hw_scan = ath9k_cancel_hw_scan; + ath9k_ops.remain_on_channel = ath9k_remain_on_channel; + ath9k_ops.cancel_remain_on_channel = ath9k_cancel_remain_on_channel; } struct ieee80211_ops ath9k_ops = { diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 7972e1e24dd2..22460a1e033c 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2197,6 +2197,9 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, if (vif) avp = (void *)vif->drv_priv; + if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) + txctl->force_channel = true; + ret = ath_tx_prepare(hw, skb, txctl); if (ret) return ret; @@ -2234,7 +2237,8 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, if (txctl->an && queue) tid = ath_get_skb_tid(sc, txctl->an, skb); - if (info->flags & IEEE80211_TX_CTL_PS_RESPONSE) { + if (info->flags & (IEEE80211_TX_CTL_PS_RESPONSE | + IEEE80211_TX_CTL_TX_OFFCHAN)) { ath_txq_unlock(sc, txq); txq = sc->tx.uapsdq; ath_txq_lock(sc, txq); From 3930563570d3714420a2ebe0324a917ff64e0422 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 11 Jun 2014 16:17:57 +0530 Subject: [PATCH 038/274] ath9k: Implement channel context ops Add channel context operations (add, remove, change, assign and unassign) to enable support for multiple channels. Signed-off-by: Felix Fietkau Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 7 ++ drivers/net/wireless/ath/ath9k/init.c | 1 + drivers/net/wireless/ath/ath9k/main.c | 104 ++++++++++++++++++++++--- drivers/net/wireless/ath/ath9k/tx99.c | 2 +- 4 files changed, 102 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index a8ae3e9b49fa..eae1830ad33f 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -331,6 +331,7 @@ struct ath_chanctx { bool offchannel; bool stopped; bool active; + bool assigned; }; enum ath_offchannel_state { @@ -356,6 +357,12 @@ struct ath_offchannel { }; void ath9k_fill_chanctx_ops(void); +static inline struct ath_chanctx * +ath_chanctx_get(struct ieee80211_chanctx_conf *ctx) +{ + struct ath_chanctx **ptr = (void *) ctx->drv_priv; + return *ptr; +} void ath_chanctx_init(struct ath_softc *sc); void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx, struct cfg80211_chan_def *chandef); diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 001b7d02345c..45f198ff6e0e 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -752,6 +752,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->wiphy->max_scan_ssids = 255; hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; hw->wiphy->max_remain_on_channel_duration = 10000; + hw->chanctx_data_size = sizeof(void *); } } diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 9e10434dbfa5..e0cb2af51767 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -624,7 +624,7 @@ static int ath9k_start(struct ieee80211_hw *hw) struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); - struct ieee80211_channel *curchan = hw->conf.chandef.chan; + struct ieee80211_channel *curchan = sc->cur_chan->chandef.chan; struct ath_chanctx *ctx = sc->cur_chan; struct ath9k_channel *init_channel; int r; @@ -1039,11 +1039,8 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, ath9k_beacon_assign_slot(sc, vif); avp->vif = vif; - - /* XXX - will be removed once chanctx ops are added */ - avp->chanctx = sc->cur_chan; - list_add_tail(&avp->list, &sc->cur_chan->vifs); - ath_chanctx_check_active(sc, avp->chanctx); + if (!ath9k_use_chanctx) + avp->chanctx = sc->cur_chan; an->sc = sc; an->sta = NULL; @@ -1132,7 +1129,6 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, } spin_unlock_bh(&sc->sc_pcu_lock); - list_del(&avp->list); sc->nvifs--; sc->tx99_vif = NULL; @@ -1144,7 +1140,6 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, ath9k_ps_restore(sc); ath_tx_node_cleanup(sc, &avp->mcast_node); - ath_chanctx_check_active(sc, avp->chanctx); mutex_unlock(&sc->mutex); } @@ -1271,7 +1266,6 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &hw->conf; struct ath_chanctx *ctx = sc->cur_chan; - bool reset_channel = false; ath9k_ps_wakeup(sc); mutex_lock(&sc->mutex); @@ -1287,7 +1281,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) * The chip needs a reset to properly wake up from * full sleep */ - reset_channel = ah->chip_fullsleep; + ath_chanctx_set_channel(sc, ctx, &ctx->chandef); } } @@ -1317,7 +1311,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) } } - if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || reset_channel) { + if (!ath9k_use_chanctx && (changed & IEEE80211_CONF_CHANGE_CHANNEL)) { ctx->offchannel = !!(conf->flags & IEEE80211_CONF_OFFCHANNEL); ath_chanctx_set_channel(sc, ctx, &hw->conf.chandef); } @@ -2433,6 +2427,89 @@ static int ath9k_cancel_remain_on_channel(struct ieee80211_hw *hw) return 0; } +static int ath9k_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf) +{ + struct ath_softc *sc = hw->priv; + struct ath_chanctx *ctx, **ptr; + int i; + + mutex_lock(&sc->mutex); + for (i = 0; i < ATH9K_NUM_CHANCTX; i++) { + if (!sc->chanctx[i].assigned) + break; + } + if (i == ATH9K_NUM_CHANCTX) { + mutex_unlock(&sc->mutex); + return -ENOSPC; + } + + ctx = &sc->chanctx[i]; + ptr = (void *) conf->drv_priv; + *ptr = ctx; + ctx->assigned = true; + ath_chanctx_set_channel(sc, ctx, &conf->def); + mutex_unlock(&sc->mutex); + + return 0; +} + + +static void ath9k_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf) +{ + struct ath_softc *sc = hw->priv; + struct ath_chanctx *ctx = ath_chanctx_get(conf); + + mutex_lock(&sc->mutex); + ctx->assigned = false; + mutex_unlock(&sc->mutex); +} + +static void ath9k_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf, + u32 changed) +{ + struct ath_softc *sc = hw->priv; + struct ath_chanctx *ctx = ath_chanctx_get(conf); + + mutex_lock(&sc->mutex); + ath_chanctx_set_channel(sc, ctx, &conf->def); + mutex_unlock(&sc->mutex); +} + +static int ath9k_assign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *conf) +{ + struct ath_softc *sc = hw->priv; + struct ath_vif *avp = (void *)vif->drv_priv; + struct ath_chanctx *ctx = ath_chanctx_get(conf); + + mutex_lock(&sc->mutex); + avp->chanctx = ctx; + list_add_tail(&avp->list, &ctx->vifs); + ath_chanctx_check_active(sc, ctx); + mutex_unlock(&sc->mutex); + + return 0; +} + +static void ath9k_unassign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *conf) +{ + struct ath_softc *sc = hw->priv; + struct ath_vif *avp = (void *)vif->drv_priv; + struct ath_chanctx *ctx = ath_chanctx_get(conf); + + mutex_lock(&sc->mutex); + avp->chanctx = NULL; + list_del(&avp->list); + ath_chanctx_check_active(sc, ctx); + mutex_unlock(&sc->mutex); +} + void ath9k_fill_chanctx_ops(void) { if (!ath9k_use_chanctx) @@ -2442,6 +2519,11 @@ void ath9k_fill_chanctx_ops(void) ath9k_ops.cancel_hw_scan = ath9k_cancel_hw_scan; ath9k_ops.remain_on_channel = ath9k_remain_on_channel; ath9k_ops.cancel_remain_on_channel = ath9k_cancel_remain_on_channel; + ath9k_ops.add_chanctx = ath9k_add_chanctx; + ath9k_ops.remove_chanctx = ath9k_remove_chanctx; + ath9k_ops.change_chanctx = ath9k_change_chanctx; + ath9k_ops.assign_vif_chanctx = ath9k_assign_vif_chanctx; + ath9k_ops.unassign_vif_chanctx = ath9k_unassign_vif_chanctx; } struct ieee80211_ops ath9k_ops = { diff --git a/drivers/net/wireless/ath/ath9k/tx99.c b/drivers/net/wireless/ath/ath9k/tx99.c index a65cfb91adca..23972924c774 100644 --- a/drivers/net/wireless/ath/ath9k/tx99.c +++ b/drivers/net/wireless/ath/ath9k/tx99.c @@ -76,7 +76,7 @@ static struct sk_buff *ath9k_build_tx99_skb(struct ath_softc *sc) tx_info = IEEE80211_SKB_CB(skb); memset(tx_info, 0, sizeof(*tx_info)); rate = &tx_info->control.rates[0]; - tx_info->band = hw->conf.chandef.chan->band; + tx_info->band = sc->cur_chan->chandef.chan->band; tx_info->flags = IEEE80211_TX_CTL_NO_ACK; tx_info->control.vif = sc->tx99_vif; rate->count = 1; From c4dc0d040e356efc0263e0b27181b05f9ef33e5f Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Wed, 11 Jun 2014 16:17:58 +0530 Subject: [PATCH 039/274] ath9k: Fetch appropriate operating channel context Retrieve appropriate operating channel context while switching between operating and off channels. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 7 ++++- drivers/net/wireless/ath/ath9k/channel.c | 14 +++++---- drivers/net/wireless/ath/ath9k/main.c | 37 ++++++++++++------------ 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index eae1830ad33f..3db489824261 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -355,6 +355,10 @@ struct ath_offchannel { struct ieee80211_vif *roc_vif; int roc_duration; }; +#define ath_for_each_chanctx(_sc, _ctx) \ + for (ctx = &sc->chanctx[0]; \ + ctx <= &sc->chanctx[ARRAY_SIZE(sc->chanctx) - 1]; \ + ctx++) void ath9k_fill_chanctx_ops(void); static inline struct ath_chanctx * @@ -373,7 +377,8 @@ void ath_offchannel_timer(unsigned long data); void ath_offchannel_channel_change(struct ath_softc *sc); void ath_chanctx_offchan_switch(struct ath_softc *sc, struct ieee80211_channel *chan); -struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc); +struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc, + bool active); int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan); int ath_startrecv(struct ath_softc *sc); diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index c679a26045ac..097207073cd4 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -293,13 +293,17 @@ void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx, ath_set_channel(sc); } -struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc) +struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc, bool active) { - u8 i; + struct ath_chanctx *ctx; - for (i = 0; i < ARRAY_SIZE(sc->chanctx); i++) { - if (!list_empty(&sc->chanctx[i].vifs)) - return &sc->chanctx[i]; + ath_for_each_chanctx(sc, ctx) { + if (!ctx->assigned || list_empty(&ctx->vifs)) + continue; + if (active && !ctx->active) + continue; + + return ctx; } return &sc->chanctx[0]; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index e0cb2af51767..92bf9079184f 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2161,7 +2161,8 @@ ath_scan_next_channel(struct ath_softc *sc) if (sc->offchannel.scan_idx >= req->n_channels) { sc->offchannel.state = ATH_OFFCHANNEL_IDLE; - ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc), NULL); + ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false), + NULL); return; } @@ -2184,7 +2185,8 @@ static void ath_offchannel_next(struct ath_softc *sc) sc->offchannel.state = ATH_OFFCHANNEL_ROC_START; ath_chanctx_offchan_switch(sc, sc->offchannel.roc_chan); } else { - ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc), NULL); + ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false), + NULL); sc->offchannel.state = ATH_OFFCHANNEL_IDLE; if (sc->ps_idle) ath_cancel_work(sc); @@ -2310,13 +2312,15 @@ void ath_offchannel_channel_change(struct ath_softc *sc) void ath_offchannel_timer(unsigned long data) { struct ath_softc *sc = (struct ath_softc *)data; - struct ath_chanctx *ctx = ath_chanctx_get_oper_chan(sc); + struct ath_chanctx *ctx; switch (sc->offchannel.state) { case ATH_OFFCHANNEL_PROBE_WAIT: if (!sc->offchannel.scan_req) return; + /* get first active channel context */ + ctx = ath_chanctx_get_oper_chan(sc, true); if (ctx->active) { sc->offchannel.state = ATH_OFFCHANNEL_SUSPEND; ath_chanctx_switch(sc, ctx, NULL); @@ -2332,6 +2336,7 @@ void ath_offchannel_timer(unsigned long data) break; case ATH_OFFCHANNEL_ROC_START: case ATH_OFFCHANNEL_ROC_WAIT: + ctx = ath_chanctx_get_oper_chan(sc, false); sc->offchannel.state = ATH_OFFCHANNEL_ROC_DONE; ath_chanctx_switch(sc, ctx, NULL); break; @@ -2432,26 +2437,22 @@ static int ath9k_add_chanctx(struct ieee80211_hw *hw, { struct ath_softc *sc = hw->priv; struct ath_chanctx *ctx, **ptr; - int i; mutex_lock(&sc->mutex); - for (i = 0; i < ATH9K_NUM_CHANCTX; i++) { - if (!sc->chanctx[i].assigned) - break; - } - if (i == ATH9K_NUM_CHANCTX) { + + ath_for_each_chanctx(sc, ctx) { + if (ctx->assigned) + continue; + + ptr = (void *) conf->drv_priv; + *ptr = ctx; + ctx->assigned = true; + ath_chanctx_set_channel(sc, ctx, &conf->def); mutex_unlock(&sc->mutex); - return -ENOSPC; + return 0; } - - ctx = &sc->chanctx[i]; - ptr = (void *) conf->drv_priv; - *ptr = ctx; - ctx->assigned = true; - ath_chanctx_set_channel(sc, ctx, &conf->def); mutex_unlock(&sc->mutex); - - return 0; + return -ENOSPC; } From b01459e856cbe9ccf64dde251aec02eae60094ce Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 11 Jun 2014 16:17:59 +0530 Subject: [PATCH 040/274] ath9k: Move caldata into channel context Signed-off-by: Felix Fietkau Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 4 ++-- drivers/net/wireless/ath/ath9k/debug.c | 2 +- drivers/net/wireless/ath/ath9k/main.c | 2 +- drivers/net/wireless/ath/ath9k/mci.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 3db489824261..ffacbf6e9f52 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -327,6 +327,8 @@ struct ath_chanctx { struct list_head vifs; struct list_head acq[IEEE80211_NUM_ACS]; + struct ath9k_hw_cal_data caldata; + u16 txpower; bool offchannel; bool stopped; @@ -820,8 +822,6 @@ struct ath_softc { struct led_classdev led_cdev; #endif - struct ath9k_hw_cal_data caldata; - #ifdef CONFIG_ATH9K_DEBUGFS struct ath9k_debug debug; #endif diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 6cc42be48d4e..f230e2e9824e 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -1080,7 +1080,7 @@ static ssize_t read_file_dump_nfcal(struct file *file, char __user *user_buf, { struct ath_softc *sc = file->private_data; struct ath_hw *ah = sc->sc_ah; - struct ath9k_nfcal_hist *h = sc->caldata.nfCalHist; + struct ath9k_nfcal_hist *h = sc->cur_chan->caldata.nfCalHist; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &common->hw->conf; u32 len = 0, size = 1500; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 92bf9079184f..85db24be8eec 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -279,7 +279,7 @@ int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan) if (!sc->cur_chan->offchannel) { fastcc = false; - caldata = &sc->caldata; + caldata = &sc->cur_chan->caldata; } if (!hchan) { diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 313ed995585a..3f7a11edb82a 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -706,7 +706,7 @@ void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel, return; if (setchannel) { - struct ath9k_hw_cal_data *caldata = &sc->caldata; + struct ath9k_hw_cal_data *caldata = &sc->cur_chan->caldata; if (IS_CHAN_HT40PLUS(ah->curchan) && (ah->curchan->channel > caldata->channel) && (ah->curchan->channel <= caldata->channel + 20)) From 26f16c246cea41f30ab2f63214a5529678677d0d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 11 Jun 2014 16:18:00 +0530 Subject: [PATCH 041/274] ath9k: Add ATH_OP_MULTI_CHANNEL Signed-off-by: Felix Fietkau Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath.h | 1 + drivers/net/wireless/ath/ath9k/channel.c | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index a889fd66fc63..fd9e5305e77f 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -63,6 +63,7 @@ enum ath_op_flags { ATH_OP_PRIM_STA_VIF, ATH_OP_HW_RESET, ATH_OP_SCANNING, + ATH_OP_MULTI_CHANNEL, }; enum ath_bus_type { diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 097207073cd4..e3127b52e1ee 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -150,8 +150,10 @@ ath_chanctx_send_vif_ps_frame(struct ath_softc *sc, struct ath_vif *avp, void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_vif *avp; bool active = false; + u8 n_active = 0; if (!ctx) return; @@ -171,6 +173,17 @@ void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx) } } ctx->active = active; + + ath_for_each_chanctx(sc, ctx) { + if (!ctx->assigned || list_empty(&ctx->vifs)) + continue; + n_active++; + } + + if (n_active > 1) + set_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags); + else + clear_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags); } static bool From 8d7e09dda8214e4154f45238b4c85ab1ecb5d89a Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 11 Jun 2014 16:18:01 +0530 Subject: [PATCH 042/274] ath9k: save tsf in channel context Save TSF in channel context for multiple operating channels. Signed-off-by: Felix Fietkau Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 3 +++ drivers/net/wireless/ath/ath9k/channel.c | 5 +++++ drivers/net/wireless/ath/ath9k/hw.c | 23 ++++++++++++++++++----- drivers/net/wireless/ath/ath9k/hw.h | 1 + drivers/net/wireless/ath/ath9k/main.c | 10 ++++++++++ 5 files changed, 37 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index ffacbf6e9f52..4df412b71680 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -22,6 +22,7 @@ #include #include #include +#include #include "common.h" #include "debug.h" @@ -328,6 +329,8 @@ struct ath_chanctx { struct list_head acq[IEEE80211_NUM_ACS]; struct ath9k_hw_cal_data caldata; + struct timespec tsf_ts; + u64 tsf_val; u16 txpower; bool offchannel; diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index e3127b52e1ee..4a7691eecdb4 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -227,6 +227,11 @@ void ath_chanctx_work(struct work_struct *work) send_ps = true; spin_lock_bh(&sc->chan_lock); + + if (sc->cur_chan != &sc->offchannel.chan) { + getrawmonotonic(&sc->cur_chan->tsf_ts); + sc->cur_chan->tsf_val = ath9k_hw_gettsf64(sc->sc_ah); + } } sc->cur_chan = sc->next_chan; sc->cur_chan->stopped = false; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 2a8ed8375ec0..ace4fe2740d4 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1730,6 +1730,23 @@ fail: return -EINVAL; } +u32 ath9k_hw_get_tsf_offset(struct timespec *last, struct timespec *cur) +{ + struct timespec ts; + s64 usec; + + if (!cur) { + getrawmonotonic(&ts); + cur = &ts; + } + + usec = cur->tv_sec * 1000000ULL + cur->tv_nsec / 1000; + usec -= last->tv_sec * 1000000ULL + last->tv_nsec / 1000; + + return (u32) usec; +} +EXPORT_SYMBOL(ath9k_hw_get_tsf_offset); + int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, struct ath9k_hw_cal_data *caldata, bool fastcc) { @@ -1739,7 +1756,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, u32 saveDefAntenna; u32 macStaId1; u64 tsf = 0; - s64 usec = 0; int r; bool start_mci_reset = false; bool save_fullsleep = ah->chip_fullsleep; @@ -1785,7 +1801,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, /* Save TSF before chip reset, a cold reset clears it */ tsf = ath9k_hw_gettsf64(ah); getrawmonotonic(&ts); - usec = ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000; saveLedState = REG_READ(ah, AR_CFG_LED) & (AR_CFG_LED_ASSOC_CTL | AR_CFG_LED_MODE_SEL | @@ -1818,9 +1833,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, } /* Restore TSF */ - getrawmonotonic(&ts); - usec = ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000 - usec; - ath9k_hw_settsf64(ah, tsf + usec); + ath9k_hw_settsf64(ah, tsf + ath9k_hw_get_tsf_offset(&ts, NULL)); if (AR_SREV_9280_20_OR_LATER(ah)) REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_JTAG_DISABLE); diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 0acd4b5a4892..51b4ebe04c04 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -1000,6 +1000,7 @@ u32 ath9k_hw_gettsf32(struct ath_hw *ah); u64 ath9k_hw_gettsf64(struct ath_hw *ah); void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64); void ath9k_hw_reset_tsf(struct ath_hw *ah); +u32 ath9k_hw_get_tsf_offset(struct timespec *last, struct timespec *cur); void ath9k_hw_set_tsfadjust(struct ath_hw *ah, bool set); void ath9k_hw_init_global_settings(struct ath_hw *ah); u32 ar9003_get_pll_sqsum_dvc(struct ath_hw *ah); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 85db24be8eec..6abdf99ffae4 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -240,6 +240,16 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) ath9k_hw_enable_interrupts(ah); if (!sc->cur_chan->offchannel && start) { + /* restore per chanctx TSF timer */ + if (sc->cur_chan->tsf_val) { + u32 offset; + + offset = ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts, + NULL); + ath9k_hw_settsf64(ah, sc->cur_chan->tsf_val + offset); + } + + if (!test_bit(ATH_OP_BEACONS, &common->op_flags)) goto work; From ca900ac9d9f0e38782f5a24e64b05f607fd6eb4c Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Wed, 11 Jun 2014 16:18:02 +0530 Subject: [PATCH 043/274] ath9k: Move beacon config to channel context Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 2 +- drivers/net/wireless/ath/ath9k/beacon.c | 10 +++++----- drivers/net/wireless/ath/ath9k/channel.c | 1 - drivers/net/wireless/ath/ath9k/init.c | 1 + drivers/net/wireless/ath/ath9k/link.c | 2 +- drivers/net/wireless/ath/ath9k/recv.c | 2 +- drivers/net/wireless/ath/ath9k/xmit.c | 6 +++--- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 4df412b71680..d632f8f2a72c 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -328,6 +328,7 @@ struct ath_chanctx { struct list_head vifs; struct list_head acq[IEEE80211_NUM_ACS]; + struct ath_beacon_config beacon; struct ath9k_hw_cal_data caldata; struct timespec tsf_ts; u64 tsf_val; @@ -828,7 +829,6 @@ struct ath_softc { #ifdef CONFIG_ATH9K_DEBUGFS struct ath9k_debug debug; #endif - struct ath_beacon_config cur_beacon_conf; struct delayed_work tx_complete_work; struct delayed_work hw_pll_work; struct timer_list sleep_timer; diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index eae8f686f125..c0ff01599ed3 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -249,7 +249,7 @@ void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif) static int ath9k_beacon_choose_slot(struct ath_softc *sc) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; + struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; u16 intval; u32 tsftu; u64 tsf; @@ -277,7 +277,7 @@ static int ath9k_beacon_choose_slot(struct ath_softc *sc) static void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; + struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; struct ath_vif *avp = (void *)vif->drv_priv; u32 tsfadjust; @@ -528,7 +528,7 @@ static void ath9k_cache_beacon_config(struct ath_softc *sc, struct ieee80211_bss_conf *bss_conf) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; + struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; ath_dbg(common, BEACON, "Caching beacon data for BSS: %pM\n", bss_conf->bssid); @@ -564,7 +564,7 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif, u32 changed) { struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; - struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; + struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); unsigned long flags; @@ -631,7 +631,7 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif, void ath9k_set_beacon(struct ath_softc *sc) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; + struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; switch (sc->sc_ah->opmode) { case NL80211_IFTYPE_AP: diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 4a7691eecdb4..156625318d04 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -271,7 +271,6 @@ void ath_chanctx_init(struct ath_softc *sc) for (j = 0; j < ARRAY_SIZE(ctx->acq); j++) INIT_LIST_HEAD(&ctx->acq[j]); } - sc->cur_chan = &sc->chanctx[0]; ctx = &sc->offchannel.chan; cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20); INIT_LIST_HEAD(&ctx->vifs); diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 45f198ff6e0e..1ff1a75f4fed 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -510,6 +510,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, sc->dfs_detector = dfs_pattern_detector_init(common, NL80211_DFS_UNSET); sc->tx99_power = MAX_RATE_POWER + 1; init_waitqueue_head(&sc->tx_wait); + sc->cur_chan = &sc->chanctx[0]; if (!pdata || pdata->use_eeprom) { ah->ah_flags |= AH_USE_EEPROM; diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index d9c01d563cc2..2343f56e6498 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -440,7 +440,7 @@ void ath_check_ani(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; + struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; /* * Check for the various conditions in which ANI has to diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index fec9e0b42f5d..7b9c7e53caaa 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -545,7 +545,7 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) sc->ps_flags &= ~PS_BEACON_SYNC; ath_dbg(common, PS, "Reconfigure beacon timers based on synchronized timestamp\n"); - if (!(WARN_ON_ONCE(sc->cur_beacon_conf.beacon_interval == 0))) + if (!(WARN_ON_ONCE(sc->cur_chan->beacon.beacon_interval == 0))) ath9k_set_beacon(sc); if (sc->p2p_ps_vif) ath9k_update_p2p_ps(sc, sc->p2p_ps_vif->vif); diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 22460a1e033c..7dd6187761c1 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1694,7 +1694,7 @@ int ath_txq_update(struct ath_softc *sc, int qnum, int ath_cabq_update(struct ath_softc *sc) { struct ath9k_tx_queue_info qi; - struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; + struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; int qnum = sc->beacon.cabq->axq_qnum; ath9k_hw_get_txq_props(sc->sc_ah, qnum, &qi); @@ -2301,8 +2301,8 @@ void ath_tx_cabq(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int max_duration; max_duration = - sc->cur_beacon_conf.beacon_interval * 1000 * - sc->cur_beacon_conf.dtim_period / ATH_BCBUF; + sc->cur_chan->beacon.beacon_interval * 1000 * + sc->cur_chan->beacon.dtim_period / ATH_BCBUF; do { struct ath_frame_info *fi = get_frame_info(skb); From 9a9c4fbc3fcabc0d510600743204f890ebdbb141 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Wed, 11 Jun 2014 16:18:03 +0530 Subject: [PATCH 044/274] ath9k: Summarize hw state per channel context Group and set hw state (opmode, primary_sta, beacon conf) per channel context instead of whole list of vifs. This would allow each channel context to run in different mode (STA/AP). Signed-off-by: Felix Fietkau Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 13 +- drivers/net/wireless/ath/ath9k/beacon.c | 28 ++- drivers/net/wireless/ath/ath9k/debug.c | 24 ++- drivers/net/wireless/ath/ath9k/main.c | 275 ++++++++++++------------ 4 files changed, 183 insertions(+), 157 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index d632f8f2a72c..a0c7279e4364 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -328,6 +328,9 @@ struct ath_chanctx { struct list_head vifs; struct list_head acq[IEEE80211_NUM_ACS]; + /* do not dereference, use for comparison only */ + struct ieee80211_vif *primary_sta; + struct ath_beacon_config beacon; struct ath9k_hw_cal_data caldata; struct timespec tsf_ts; @@ -438,7 +441,6 @@ struct ath_vif { struct ieee80211_vif *vif; struct ath_node mcast_node; int av_bslot; - bool primary_sta_vif; __le64 tsf_adjust; /* TSF adjustment for staggered beacons */ struct ath_buf *av_bcbuf; struct ath_chanctx *chanctx; @@ -451,17 +453,22 @@ struct ath9k_vif_iter_data { u8 hw_macaddr[ETH_ALEN]; /* address of the first vif */ u8 mask[ETH_ALEN]; /* bssid mask */ bool has_hw_macaddr; + u8 slottime; + bool beacons; int naps; /* number of AP vifs */ int nmeshes; /* number of mesh vifs */ int nstations; /* number of station vifs */ int nwds; /* number of WDS vifs */ int nadhocs; /* number of adhoc vifs */ + struct ieee80211_vif *primary_sta; }; -void ath9k_calculate_iter_data(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, +void ath9k_calculate_iter_data(struct ath_softc *sc, + struct ath_chanctx *ctx, struct ath9k_vif_iter_data *iter_data); +void ath9k_calculate_summary_state(struct ath_softc *sc, + struct ath_chanctx *ctx); /*******************/ /* Beacon Handling */ diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index c0ff01599ed3..d3553927c6dd 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -277,8 +277,8 @@ static int ath9k_beacon_choose_slot(struct ath_softc *sc) static void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; struct ath_vif *avp = (void *)vif->drv_priv; + struct ath_beacon_config *cur_conf = &avp->chanctx->beacon; u32 tsfadjust; if (avp->av_bslot == 0) @@ -500,7 +500,6 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_vif *avp = (void *)vif->drv_priv; if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) { if ((vif->type != NL80211_IFTYPE_AP) || @@ -514,7 +513,7 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc, if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) { if ((vif->type == NL80211_IFTYPE_STATION) && test_bit(ATH_OP_BEACONS, &common->op_flags) && - !avp->primary_sta_vif) { + vif != sc->cur_chan->primary_sta) { ath_dbg(common, CONFIG, "Beacon already configured for a station interface\n"); return false; @@ -525,10 +524,11 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc, } static void ath9k_cache_beacon_config(struct ath_softc *sc, + struct ath_chanctx *ctx, struct ieee80211_bss_conf *bss_conf) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; + struct ath_beacon_config *cur_conf = &ctx->beacon; ath_dbg(common, BEACON, "Caching beacon data for BSS: %pM\n", bss_conf->bssid); @@ -564,20 +564,29 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif, u32 changed) { struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; - struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); + struct ath_vif *avp = (void *)vif->drv_priv; + struct ath_chanctx *ctx = avp->chanctx; + struct ath_beacon_config *cur_conf; unsigned long flags; bool skip_beacon = false; + if (!ctx) + return; + + cur_conf = &avp->chanctx->beacon; if (vif->type == NL80211_IFTYPE_AP) ath9k_set_tsfadjust(sc, vif); if (!ath9k_allow_beacon_config(sc, vif)) return; - if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) { - ath9k_cache_beacon_config(sc, bss_conf); + if (vif->type == NL80211_IFTYPE_STATION) { + ath9k_cache_beacon_config(sc, ctx, bss_conf); + if (ctx != sc->cur_chan) + return; + ath9k_set_beacon(sc); set_bit(ATH_OP_BEACONS, &common->op_flags); return; @@ -593,10 +602,13 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif, cur_conf->enable_beacon = false; } else if (bss_conf->enable_beacon) { cur_conf->enable_beacon = true; - ath9k_cache_beacon_config(sc, bss_conf); + ath9k_cache_beacon_config(sc, ctx, bss_conf); } } + if (ctx != sc->cur_chan) + return; + /* * Configure the HW beacon registers only when we have a valid * beacon interval. diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index f230e2e9824e..ce073e995dfe 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -750,13 +750,13 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf, { struct ath_softc *sc = file->private_data; struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ieee80211_hw *hw = sc->hw; struct ath9k_vif_iter_data iter_data; + struct ath_chanctx *ctx; char buf[512]; unsigned int len = 0; ssize_t retval = 0; unsigned int reg; - u32 rxfilter; + u32 rxfilter, i; len += scnprintf(buf + len, sizeof(buf) - len, "BSSID: %pM\n", common->curbssid); @@ -826,14 +826,20 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf, len += scnprintf(buf + len, sizeof(buf) - len, "\n"); - ath9k_calculate_iter_data(hw, NULL, &iter_data); + i = 0; + ath_for_each_chanctx(sc, ctx) { + if (!ctx->assigned || list_empty(&ctx->vifs)) + continue; + ath9k_calculate_iter_data(sc, ctx, &iter_data); - len += scnprintf(buf + len, sizeof(buf) - len, - "VIF-COUNTS: AP: %i STA: %i MESH: %i WDS: %i" - " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n", - iter_data.naps, iter_data.nstations, iter_data.nmeshes, - iter_data.nwds, iter_data.nadhocs, - sc->nvifs, sc->nbcnvifs); + len += scnprintf(buf + len, sizeof(buf) - len, + "VIF-COUNTS: CTX %i AP: %i STA: %i MESH: %i WDS: %i", + i++, iter_data.naps, iter_data.nstations, + iter_data.nmeshes, iter_data.nwds); + len += scnprintf(buf + len, sizeof(buf) - len, + " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n", + iter_data.nadhocs, sc->nvifs, sc->nbcnvifs); + } if (len > sizeof(buf)) len = sizeof(buf); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 6abdf99ffae4..9dfb82077016 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -19,9 +19,6 @@ #include "ath9k.h" #include "btcoex.h" -static void ath9k_set_assoc_state(struct ath_softc *sc, - struct ieee80211_vif *vif); - u8 ath9k_parse_mpdudensity(u8 mpdudensity) { /* @@ -236,8 +233,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) sc->cur_chan->txpower, &sc->curtxpow); clear_bit(ATH_OP_HW_RESET, &common->op_flags); - ath9k_hw_set_interrupts(ah); - ath9k_hw_enable_interrupts(ah); + ath9k_calculate_summary_state(sc, sc->cur_chan); if (!sc->cur_chan->offchannel && start) { /* restore per chanctx TSF timer */ @@ -267,6 +263,10 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) } sc->gtt_cnt = 0; + + ath9k_hw_set_interrupts(ah); + ath9k_hw_enable_interrupts(ah); + ieee80211_wake_queues(sc->hw); ath9k_p2p_ps_timer(sc); @@ -905,18 +905,29 @@ static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) iter_data->has_hw_macaddr = true; } + if (!vif->bss_conf.use_short_slot) + iter_data->slottime = ATH9K_SLOT_TIME_20; + switch (vif->type) { case NL80211_IFTYPE_AP: iter_data->naps++; + if (vif->bss_conf.enable_beacon) + iter_data->beacons = true; break; case NL80211_IFTYPE_STATION: iter_data->nstations++; + if (vif->bss_conf.assoc && !iter_data->primary_sta) + iter_data->primary_sta = vif; break; case NL80211_IFTYPE_ADHOC: iter_data->nadhocs++; + if (vif->bss_conf.enable_beacon) + iter_data->beacons = true; break; case NL80211_IFTYPE_MESH_POINT: iter_data->nmeshes++; + if (vif->bss_conf.enable_beacon) + iter_data->beacons = true; break; case NL80211_IFTYPE_WDS: iter_data->nwds++; @@ -926,26 +937,12 @@ static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) } } -static void ath9k_sta_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) -{ - struct ath_softc *sc = data; - struct ath_vif *avp = (void *)vif->drv_priv; - - if (vif->type != NL80211_IFTYPE_STATION) - return; - - if (avp->primary_sta_vif) - ath9k_set_assoc_state(sc, vif); -} - /* Called with sc->mutex held. */ -void ath9k_calculate_iter_data(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, +void ath9k_calculate_iter_data(struct ath_softc *sc, + struct ath_chanctx *ctx, struct ath9k_vif_iter_data *iter_data) { - struct ath_softc *sc = hw->priv; - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); + struct ath_vif *avp; /* * Pick the MAC address of the first interface as the new hardware @@ -954,29 +951,80 @@ void ath9k_calculate_iter_data(struct ieee80211_hw *hw, */ memset(iter_data, 0, sizeof(*iter_data)); memset(&iter_data->mask, 0xff, ETH_ALEN); + iter_data->slottime = ATH9K_SLOT_TIME_9; - if (vif) - ath9k_vif_iter(iter_data, vif->addr, vif); + list_for_each_entry(avp, &ctx->vifs, list) + ath9k_vif_iter(iter_data, avp->vif->addr, avp->vif); - /* Get list of all active MAC addresses */ - ieee80211_iterate_active_interfaces_atomic( - sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL, - ath9k_vif_iter, iter_data); + if (ctx == &sc->offchannel.chan) { + struct ieee80211_vif *vif; - memcpy(common->macaddr, iter_data->hw_macaddr, ETH_ALEN); + if (sc->offchannel.state < ATH_OFFCHANNEL_ROC_START) + vif = sc->offchannel.scan_vif; + else + vif = sc->offchannel.roc_vif; + + if (vif) + ath9k_vif_iter(iter_data, vif->addr, vif); + iter_data->beacons = false; + } +} + +static void ath9k_set_assoc_state(struct ath_softc *sc, + struct ieee80211_vif *vif, bool changed) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; + unsigned long flags; + + set_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags); + /* Set the AID, BSSID and do beacon-sync only when + * the HW opmode is STATION. + * + * But the primary bit is set above in any case. + */ + if (sc->sc_ah->opmode != NL80211_IFTYPE_STATION) + return; + + ether_addr_copy(common->curbssid, bss_conf->bssid); + common->curaid = bss_conf->aid; + ath9k_hw_write_associd(sc->sc_ah); + + if (changed) { + common->last_rssi = ATH_RSSI_DUMMY_MARKER; + sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER; + + spin_lock_irqsave(&sc->sc_pm_lock, flags); + sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON; + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); + } + + if (ath9k_hw_mci_is_enabled(sc->sc_ah)) + ath9k_mci_update_wlan_channels(sc, false); + + ath_dbg(common, CONFIG, + "Primary Station interface: %pM, BSSID: %pM\n", + vif->addr, common->curbssid); } /* Called with sc->mutex held. */ -static void ath9k_calculate_summary_state(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) +void ath9k_calculate_summary_state(struct ath_softc *sc, + struct ath_chanctx *ctx) { - struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_vif_iter_data iter_data; - enum nl80211_iftype old_opmode = ah->opmode; - ath9k_calculate_iter_data(hw, vif, &iter_data); + ath_chanctx_check_active(sc, ctx); + + if (ctx != sc->cur_chan) + return; + + ath9k_ps_wakeup(sc); + ath9k_calculate_iter_data(sc, ctx, &iter_data); + + if (iter_data.has_hw_macaddr) + ether_addr_copy(common->macaddr, iter_data.hw_macaddr); memcpy(common->bssidmask, iter_data.mask, ETH_ALEN); ath_hw_setbssidmask(common); @@ -1004,19 +1052,48 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw, else ah->imask &= ~ATH9K_INT_TSFOOR; + ah->imask &= ~ATH9K_INT_SWBA; + if (ah->opmode == NL80211_IFTYPE_STATION) { + bool changed = (iter_data.primary_sta != ctx->primary_sta); + + iter_data.beacons = true; + if (iter_data.primary_sta) { + ath9k_set_assoc_state(sc, iter_data.primary_sta, + changed); + if (!ctx->primary_sta || + !ctx->primary_sta->bss_conf.assoc) + ctx->primary_sta = iter_data.primary_sta; + } else { + ctx->primary_sta = NULL; + memset(common->curbssid, 0, ETH_ALEN); + common->curaid = 0; + ath9k_hw_write_associd(sc->sc_ah); + if (ath9k_hw_mci_is_enabled(sc->sc_ah)) + ath9k_mci_update_wlan_channels(sc, true); + } + } else if (iter_data.beacons) { + ah->imask |= ATH9K_INT_SWBA; + } ath9k_hw_set_interrupts(ah); - /* - * If we are changing the opmode to STATION, - * a beacon sync needs to be done. - */ - if (ah->opmode == NL80211_IFTYPE_STATION && - old_opmode == NL80211_IFTYPE_AP && - test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) { - ieee80211_iterate_active_interfaces_atomic( - sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL, - ath9k_sta_vif_iter, sc); + if (iter_data.beacons) + set_bit(ATH_OP_BEACONS, &common->op_flags); + else + clear_bit(ATH_OP_BEACONS, &common->op_flags); + + if (ah->slottime != iter_data.slottime) { + ah->slottime = iter_data.slottime; + ath9k_hw_init_global_settings(ah); } + + if (iter_data.primary_sta) + set_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags); + else + clear_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags); + + ctx->primary_sta = iter_data.primary_sta; + + ath9k_ps_restore(sc); } static int ath9k_add_interface(struct ieee80211_hw *hw, @@ -1041,16 +1118,14 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, ath_dbg(common, CONFIG, "Attach a VIF of type: %d\n", vif->type); sc->nvifs++; - ath9k_ps_wakeup(sc); - ath9k_calculate_summary_state(hw, vif); - ath9k_ps_restore(sc); - if (ath9k_uses_beacons(vif->type)) ath9k_beacon_assign_slot(sc, vif); avp->vif = vif; - if (!ath9k_use_chanctx) + if (!ath9k_use_chanctx) { avp->chanctx = sc->cur_chan; + list_add_tail(&avp->list, &avp->chanctx->vifs); + } an->sc = sc; an->sta = NULL; @@ -1086,13 +1161,10 @@ static int ath9k_change_interface(struct ieee80211_hw *hw, vif->type = new_type; vif->p2p = p2p; - ath9k_ps_wakeup(sc); - ath9k_calculate_summary_state(hw, vif); - ath9k_ps_restore(sc); - if (ath9k_uses_beacons(vif->type)) ath9k_beacon_assign_slot(sc, vif); - ath_chanctx_check_active(sc, avp->chanctx); + + ath9k_calculate_summary_state(sc, avp->chanctx); mutex_unlock(&sc->mutex); return 0; @@ -1141,14 +1213,12 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, sc->nvifs--; sc->tx99_vif = NULL; + if (!ath9k_use_chanctx) + list_del(&avp->list); if (ath9k_uses_beacons(vif->type)) ath9k_beacon_remove_slot(sc, vif); - ath9k_ps_wakeup(sc); - ath9k_calculate_summary_state(hw, NULL); - ath9k_ps_restore(sc); - ath_tx_node_cleanup(sc, &avp->mcast_node); mutex_unlock(&sc->mutex); @@ -1585,58 +1655,6 @@ static int ath9k_set_key(struct ieee80211_hw *hw, return ret; } -static void ath9k_set_assoc_state(struct ath_softc *sc, - struct ieee80211_vif *vif) -{ - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_vif *avp = (void *)vif->drv_priv; - struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; - unsigned long flags; - - set_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags); - avp->primary_sta_vif = true; - - /* - * Set the AID, BSSID and do beacon-sync only when - * the HW opmode is STATION. - * - * But the primary bit is set above in any case. - */ - if (sc->sc_ah->opmode != NL80211_IFTYPE_STATION) - return; - - memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); - common->curaid = bss_conf->aid; - ath9k_hw_write_associd(sc->sc_ah); - - common->last_rssi = ATH_RSSI_DUMMY_MARKER; - sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER; - - spin_lock_irqsave(&sc->sc_pm_lock, flags); - sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON; - spin_unlock_irqrestore(&sc->sc_pm_lock, flags); - - if (ath9k_hw_mci_is_enabled(sc->sc_ah)) - ath9k_mci_update_wlan_channels(sc, false); - - ath_dbg(common, CONFIG, - "Primary Station interface: %pM, BSSID: %pM\n", - vif->addr, common->curbssid); -} - -static void ath9k_bss_assoc_iter(void *data, u8 *mac, struct ieee80211_vif *vif) -{ - struct ath_softc *sc = data; - struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - - if (test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) - return; - - if (bss_conf->assoc) - ath9k_set_assoc_state(sc, vif); -} - void ath9k_p2p_ps_timer(void *priv) { struct ath_softc *sc = priv; @@ -1646,7 +1664,7 @@ void ath9k_p2p_ps_timer(void *priv) struct ath_node *an; u32 tsf; - if (!avp) + if (!avp || avp->chanctx != sc->cur_chan) return; tsf = ath9k_hw_gettsf32(sc->sc_ah); @@ -1721,28 +1739,7 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, ath_dbg(common, CONFIG, "BSSID %pM Changed ASSOC %d\n", bss_conf->bssid, bss_conf->assoc); - if (avp->primary_sta_vif && !bss_conf->assoc) { - clear_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags); - avp->primary_sta_vif = false; - - if (ah->opmode == NL80211_IFTYPE_STATION) - clear_bit(ATH_OP_BEACONS, &common->op_flags); - } - - ieee80211_iterate_active_interfaces_atomic( - sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL, - ath9k_bss_assoc_iter, sc); - - if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags) && - ah->opmode == NL80211_IFTYPE_STATION) { - memset(common->curbssid, 0, ETH_ALEN); - common->curaid = 0; - ath9k_hw_write_associd(sc->sc_ah); - if (ath9k_hw_mci_is_enabled(sc->sc_ah)) - ath9k_mci_update_wlan_channels(sc, true); - } - - ath_chanctx_check_active(sc, avp->chanctx); + ath9k_calculate_summary_state(sc, avp->chanctx); } if (changed & BSS_CHANGED_IBSS) { @@ -1752,10 +1749,14 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, } if ((changed & BSS_CHANGED_BEACON_ENABLED) || - (changed & BSS_CHANGED_BEACON_INT)) + (changed & BSS_CHANGED_BEACON_INT)) { + if (changed & BSS_CHANGED_BEACON_ENABLED) + ath9k_calculate_summary_state(sc, avp->chanctx); ath9k_beacon_config(sc, vif, changed); + } - if (changed & BSS_CHANGED_ERP_SLOT) { + if ((avp->chanctx == sc->cur_chan) && + (changed & BSS_CHANGED_ERP_SLOT)) { if (bss_conf->use_short_slot) slottime = 9; else @@ -2500,7 +2501,7 @@ static int ath9k_assign_vif_chanctx(struct ieee80211_hw *hw, mutex_lock(&sc->mutex); avp->chanctx = ctx; list_add_tail(&avp->list, &ctx->vifs); - ath_chanctx_check_active(sc, ctx); + ath9k_calculate_summary_state(sc, ctx); mutex_unlock(&sc->mutex); return 0; @@ -2517,7 +2518,7 @@ static void ath9k_unassign_vif_chanctx(struct ieee80211_hw *hw, mutex_lock(&sc->mutex); avp->chanctx = NULL; list_del(&avp->list); - ath_chanctx_check_active(sc, ctx); + ath9k_calculate_summary_state(sc, ctx); mutex_unlock(&sc->mutex); } From 748299f27b21c23ba963df4768abb2344fe6e9a7 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 11 Jun 2014 16:18:04 +0530 Subject: [PATCH 045/274] ath9k: switch channel context for beaconing Add a basic state machine for switch channel context for beacon transmission. Signed-off-by: Felix Fietkau Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 25 +++++++ drivers/net/wireless/ath/ath9k/beacon.c | 11 +++- drivers/net/wireless/ath/ath9k/channel.c | 84 +++++++++++++++++++++++- drivers/net/wireless/ath/ath9k/main.c | 9 ++- drivers/net/wireless/ath/ath9k/xmit.c | 2 + 5 files changed, 126 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index a0c7279e4364..b657115d1eb7 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -341,6 +341,28 @@ struct ath_chanctx { bool stopped; bool active; bool assigned; + bool switch_after_beacon; +}; + +enum ath_chanctx_event { + ATH_CHANCTX_EVENT_BEACON_PREPARE, + ATH_CHANCTX_EVENT_BEACON_SENT, + ATH_CHANCTX_EVENT_TSF_TIMER, +}; + +enum ath_chanctx_state { + ATH_CHANCTX_STATE_IDLE, + ATH_CHANCTX_STATE_WAIT_FOR_BEACON, + ATH_CHANCTX_STATE_WAIT_FOR_TIMER, + ATH_CHANCTX_STATE_SWITCH, +}; + +struct ath_chanctx_sched { + bool beacon_pending; + enum ath_chanctx_state state; + + u32 next_tbtt; + unsigned int channel_switch_time; }; enum ath_offchannel_state { @@ -388,6 +410,8 @@ void ath_chanctx_offchan_switch(struct ath_softc *sc, struct ieee80211_channel *chan); struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc, bool active); +void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, + enum ath_chanctx_event ev); int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan); int ath_startrecv(struct ath_softc *sc); @@ -826,6 +850,7 @@ struct ath_softc { struct ath_chanctx *next_chan; spinlock_t chan_lock; struct ath_offchannel offchannel; + struct ath_chanctx_sched sched; #ifdef CONFIG_MAC80211_LEDS bool led_registered; diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index d3553927c6dd..5b1689cf029a 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -374,12 +374,19 @@ void ath9k_beacon_tasklet(unsigned long data) vif = sc->beacon.bslot[slot]; /* EDMA devices check that in the tx completion function. */ - if (!edma && ath9k_csa_is_finished(sc, vif)) - return; + if (!edma) { + if (sc->sched.beacon_pending) + ath_chanctx_event(sc, NULL, + ATH_CHANCTX_EVENT_BEACON_SENT); + + if (ath9k_csa_is_finished(sc, vif)) + return; + } if (!vif || !vif->bss_conf.enable_beacon) return; + ath_chanctx_event(sc, vif, ATH_CHANCTX_EVENT_BEACON_PREPARE); bf = ath9k_beacon_generate(sc->hw, vif); if (sc->beacon.bmisscnt != 0) { diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 156625318d04..503b7766e12e 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -202,10 +202,33 @@ ath_chanctx_send_ps_frame(struct ath_softc *sc, bool powersave) return sent; } +static bool ath_chanctx_defer_switch(struct ath_softc *sc) +{ + if (sc->cur_chan == &sc->offchannel.chan) + return false; + + switch (sc->sched.state) { + case ATH_CHANCTX_STATE_SWITCH: + return false; + case ATH_CHANCTX_STATE_IDLE: + if (!sc->cur_chan->switch_after_beacon) + return false; + + sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; + break; + default: + break; + } + + return true; +} + void ath_chanctx_work(struct work_struct *work) { struct ath_softc *sc = container_of(work, struct ath_softc, chanctx_work); + struct timespec ts; + bool measure_time = false; bool send_ps = false; mutex_lock(&sc->mutex); @@ -216,10 +239,20 @@ void ath_chanctx_work(struct work_struct *work) return; } + if (ath_chanctx_defer_switch(sc)) { + spin_unlock_bh(&sc->chan_lock); + mutex_unlock(&sc->mutex); + return; + } + if (sc->cur_chan != sc->next_chan) { sc->cur_chan->stopped = true; spin_unlock_bh(&sc->chan_lock); + if (sc->next_chan == &sc->offchannel.chan) { + getrawmonotonic(&ts); + measure_time = true; + } __ath9k_flush(sc->hw, ~0, true); if (ath_chanctx_send_ps_frame(sc, true)) @@ -236,13 +269,17 @@ void ath_chanctx_work(struct work_struct *work) sc->cur_chan = sc->next_chan; sc->cur_chan->stopped = false; sc->next_chan = NULL; + sc->sched.state = ATH_CHANCTX_STATE_IDLE; spin_unlock_bh(&sc->chan_lock); if (sc->sc_ah->chip_fullsleep || memcmp(&sc->cur_chandef, &sc->cur_chan->chandef, - sizeof(sc->cur_chandef))) + sizeof(sc->cur_chandef))) { ath_set_channel(sc); - + if (measure_time) + sc->sched.channel_switch_time = + ath9k_hw_get_tsf_offset(&ts, NULL); + } if (send_ps) ath_chanctx_send_ps_frame(sc, false); @@ -335,3 +372,46 @@ void ath_chanctx_offchan_switch(struct ath_softc *sc, ath_chanctx_switch(sc, &sc->offchannel.chan, &chandef); } + +void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, + enum ath_chanctx_event ev) +{ + struct ath_hw *ah = sc->sc_ah; + u32 tsf_time; + + spin_lock_bh(&sc->chan_lock); + + switch (ev) { + case ATH_CHANCTX_EVENT_BEACON_PREPARE: + if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON) + break; + + sc->sched.beacon_pending = true; + sc->sched.next_tbtt = REG_READ(ah, AR_NEXT_TBTT_TIMER); + break; + case ATH_CHANCTX_EVENT_BEACON_SENT: + if (!sc->sched.beacon_pending) + break; + + sc->sched.beacon_pending = false; + if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON) + break; + + /* defer channel switch by a quarter beacon interval */ + tsf_time = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval); + tsf_time = sc->sched.next_tbtt + tsf_time / 4; + sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER; + ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, tsf_time, + 1000000); + break; + case ATH_CHANCTX_EVENT_TSF_TIMER: + if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_TIMER) + break; + + sc->sched.state = ATH_CHANCTX_STATE_SWITCH; + ieee80211_queue_work(sc->hw, &sc->chanctx_work); + break; + } + + spin_unlock_bh(&sc->chan_lock); +} diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 9dfb82077016..2393af058afe 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1047,10 +1047,14 @@ void ath9k_calculate_summary_state(struct ath_softc *sc, ath9k_hw_setopmode(ah); + ctx->switch_after_beacon = false; if ((iter_data.nstations + iter_data.nadhocs + iter_data.nmeshes) > 0) ah->imask |= ATH9K_INT_TSFOOR; - else + else { ah->imask &= ~ATH9K_INT_TSFOOR; + if (iter_data.naps == 1 && iter_data.beacons) + ctx->switch_after_beacon = true; + } ah->imask &= ~ATH9K_INT_SWBA; if (ah->opmode == NL80211_IFTYPE_STATION) { @@ -1664,6 +1668,9 @@ void ath9k_p2p_ps_timer(void *priv) struct ath_node *an; u32 tsf; + ath9k_hw_gen_timer_stop(sc->sc_ah, sc->p2p_ps_timer); + ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER); + if (!avp || avp->chanctx != sc->cur_chan) return; diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 7dd6187761c1..a422c20fe065 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2617,6 +2617,8 @@ void ath_tx_edma_tasklet(struct ath_softc *sc) sc->beacon.tx_processed = true; sc->beacon.tx_last = !(ts.ts_status & ATH9K_TXERR_MASK); + ath_chanctx_event(sc, NULL, + ATH_CHANCTX_EVENT_BEACON_SENT); ath9k_csa_update(sc); continue; } From ea6ff2de5c56f6b0c08717f6cc47281b52504e81 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Wed, 11 Jun 2014 16:18:05 +0530 Subject: [PATCH 046/274] ath9k: Store current offchannel duration Update current offchannel duration during scan or roc request. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 + drivers/net/wireless/ath/ath9k/main.c | 28 +++++++++++++++++--------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index b657115d1eb7..6487c4769af4 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -385,6 +385,7 @@ struct ath_offchannel { struct ieee80211_channel *roc_chan; struct ieee80211_vif *roc_vif; int roc_duration; + int duration; }; #define ath_for_each_chanctx(_sc, _ctx) \ for (ctx = &sc->chanctx[0]; \ diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 2393af058afe..0ba496d400d0 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2171,6 +2171,17 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw) clear_bit(ATH_OP_SCANNING, &common->op_flags); } +static int ath_scan_channel_duration(struct ath_softc *sc, + struct ieee80211_channel *chan) +{ + struct cfg80211_scan_request *req = sc->offchannel.scan_req; + + if (!req->n_ssids || (chan->flags & IEEE80211_CHAN_NO_IR)) + return (HZ / 9); /* ~110 ms */ + + return (HZ / 16); /* ~60 ms */ +} + static void ath_scan_next_channel(struct ath_softc *sc) { @@ -2185,6 +2196,7 @@ ath_scan_next_channel(struct ath_softc *sc) } chan = req->channels[sc->offchannel.scan_idx++]; + sc->offchannel.duration = ath_scan_channel_duration(sc, chan); sc->offchannel.state = ATH_OFFCHANNEL_PROBE_SEND; ath_chanctx_offchan_switch(sc, chan); } @@ -2200,6 +2212,7 @@ static void ath_offchannel_next(struct ath_softc *sc) } else if (sc->offchannel.roc_vif) { vif = sc->offchannel.roc_vif; sc->offchannel.chan.txpower = vif->bss_conf.txpower; + sc->offchannel.duration = sc->offchannel.roc_duration; sc->offchannel.state = ATH_OFFCHANNEL_ROC_START; ath_chanctx_offchan_switch(sc, sc->offchannel.roc_chan); } else { @@ -2275,20 +2288,17 @@ error: static void ath_scan_channel_start(struct ath_softc *sc) { struct cfg80211_scan_request *req = sc->offchannel.scan_req; - int i, dwell; - - if ((sc->cur_chan->chandef.chan->flags & IEEE80211_CHAN_NO_IR) || - !req->n_ssids) { - dwell = HZ / 9; /* ~110 ms */ - } else { - dwell = HZ / 16; /* ~60 ms */ + int i; + if (!(sc->cur_chan->chandef.chan->flags & IEEE80211_CHAN_NO_IR) && + req->n_ssids) { for (i = 0; i < req->n_ssids; i++) ath_scan_send_probe(sc, &req->ssids[i]); + } sc->offchannel.state = ATH_OFFCHANNEL_PROBE_WAIT; - mod_timer(&sc->offchannel.timer, jiffies + dwell); + mod_timer(&sc->offchannel.timer, jiffies + sc->offchannel.duration); } void ath_offchannel_channel_change(struct ath_softc *sc) @@ -2316,7 +2326,7 @@ void ath_offchannel_channel_change(struct ath_softc *sc) sc->offchannel.state = ATH_OFFCHANNEL_ROC_WAIT; mod_timer(&sc->offchannel.timer, jiffies + - msecs_to_jiffies(sc->offchannel.roc_duration)); + msecs_to_jiffies(sc->offchannel.duration)); ieee80211_ready_on_channel(sc->hw); break; case ATH_OFFCHANNEL_ROC_DONE: From 3ae07d39ea81440768427e7786c5422f3af38a94 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 11 Jun 2014 16:18:06 +0530 Subject: [PATCH 047/274] ath9k: Add p2p go NoA attribute Signed-off-by: Felix Fietkau Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 7 +++++ drivers/net/wireless/ath/ath9k/beacon.c | 37 ++++++++++++++++++++++ drivers/net/wireless/ath/ath9k/channel.c | 39 +++++++++++++++++++++--- drivers/net/wireless/ath/ath9k/init.c | 2 ++ 4 files changed, 80 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 6487c4769af4..02f30980c31a 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -362,6 +362,8 @@ struct ath_chanctx_sched { enum ath_chanctx_state state; u32 next_tbtt; + u32 switch_start_time; + unsigned int offchannel_duration; unsigned int channel_switch_time; }; @@ -472,6 +474,11 @@ struct ath_vif { /* P2P Client */ struct ieee80211_noa_data noa; + + /* P2P GO */ + u8 noa_index; + u32 offchannel_start; + u32 offchannel_duration; }; struct ath9k_vif_iter_data { diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 5b1689cf029a..85a40d749e20 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -108,6 +108,40 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif, ath9k_hw_set_txdesc(ah, bf->bf_desc, &info); } +static void ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp, + struct sk_buff *skb) +{ + static const u8 noa_ie_hdr[] = { + WLAN_EID_VENDOR_SPECIFIC, /* type */ + 0, /* length */ + 0x50, 0x6f, 0x9a, /* WFA OUI */ + 0x09, /* P2P subtype */ + 0x0c, /* Notice of Absence */ + 0x00, /* LSB of little-endian len */ + 0x00, /* MSB of little-endian len */ + }; + + struct ieee80211_p2p_noa_attr *noa; + int noa_len = 2 + sizeof(struct ieee80211_p2p_noa_desc); + u8 *hdr; + + if (!avp->offchannel_duration) + return; + + hdr = skb_put(skb, sizeof(noa_ie_hdr)); + memcpy(hdr, noa_ie_hdr, sizeof(noa_ie_hdr)); + hdr[1] = sizeof(noa_ie_hdr) + noa_len - 2; + hdr[7] = noa_len; + + noa = (void *) skb_put(skb, noa_len); + memset(noa, 0, noa_len); + + noa->index = avp->noa_index; + noa->desc[0].count = 1; + noa->desc[0].duration = cpu_to_le32(avp->offchannel_duration); + noa->desc[0].start_time = cpu_to_le32(avp->offchannel_start); +} + static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -155,6 +189,9 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw, hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no); } + if (vif->p2p) + ath9k_beacon_add_noa(sc, avp, skb); + bf->bf_buf_addr = dma_map_single(sc->dev, skb->data, skb->len, DMA_TO_DEVICE); if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 503b7766e12e..364a55502b7d 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -270,6 +270,8 @@ void ath_chanctx_work(struct work_struct *work) sc->cur_chan->stopped = false; sc->next_chan = NULL; sc->sched.state = ATH_CHANCTX_STATE_IDLE; + sc->sched.offchannel_duration = 0; + spin_unlock_bh(&sc->chan_lock); if (sc->sc_ah->chip_fullsleep || @@ -326,6 +328,12 @@ void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx, sc->next_chan = ctx; if (chandef) ctx->chandef = *chandef; + + if (sc->next_chan == &sc->offchannel.chan) { + sc->sched.offchannel_duration = + TU_TO_USEC(sc->offchannel.duration) + + sc->sched.channel_switch_time; + } spin_unlock_bh(&sc->chan_lock); ieee80211_queue_work(sc->hw, &sc->chanctx_work); } @@ -377,17 +385,40 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, enum ath_chanctx_event ev) { struct ath_hw *ah = sc->sc_ah; + struct ath_vif *avp = NULL; u32 tsf_time; + bool noa_changed = false; + + if (vif) + avp = (struct ath_vif *) vif->drv_priv; spin_lock_bh(&sc->chan_lock); switch (ev) { case ATH_CHANCTX_EVENT_BEACON_PREPARE: + if (avp->offchannel_duration) + avp->offchannel_duration = 0; + if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON) break; sc->sched.beacon_pending = true; sc->sched.next_tbtt = REG_READ(ah, AR_NEXT_TBTT_TIMER); + + /* defer channel switch by a quarter beacon interval */ + tsf_time = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval); + tsf_time = sc->sched.next_tbtt + tsf_time / 4; + sc->sched.switch_start_time = tsf_time; + + if (sc->sched.offchannel_duration) { + noa_changed = true; + avp->offchannel_start = tsf_time; + avp->offchannel_duration = + sc->sched.offchannel_duration; + } + + if (noa_changed) + avp->noa_index++; break; case ATH_CHANCTX_EVENT_BEACON_SENT: if (!sc->sched.beacon_pending) @@ -397,12 +428,10 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON) break; - /* defer channel switch by a quarter beacon interval */ - tsf_time = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval); - tsf_time = sc->sched.next_tbtt + tsf_time / 4; sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER; - ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, tsf_time, - 1000000); + ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, + sc->sched.switch_start_time, + 1000000); break; case ATH_CHANCTX_EVENT_TSF_TIMER: if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_TIMER) diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 1ff1a75f4fed..66a9dc3a5369 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -754,6 +754,8 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; hw->wiphy->max_remain_on_channel_duration = 10000; hw->chanctx_data_size = sizeof(void *); + hw->extra_beacon_tailroom = + sizeof(struct ieee80211_p2p_noa_attr) + 9; } } From 8eab25108e374403f759dd7de01084a1f3ba6d68 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 11 Jun 2014 16:18:07 +0530 Subject: [PATCH 048/274] ath9k: switch channel after sending beacon Signed-off-by: Felix Fietkau Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/channel.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 364a55502b7d..4793de079f77 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -365,7 +365,8 @@ struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc, bool active) if (active && !ctx->active) continue; - return ctx; + if (ctx->switch_after_beacon) + return ctx; } return &sc->chanctx[0]; From 58b57375285223badddebdf8d905a864c271b87d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 11 Jun 2014 16:18:08 +0530 Subject: [PATCH 049/274] ath9k: Adjust AP beacon tsf based on station context In multi channel context (AP + STA case), adjust the TSF time of the AP chanctx to keep its beacons at half beacon interval offset relative to the STA chanctx. Signed-off-by: Felix Fietkau Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 2 + drivers/net/wireless/ath/ath9k/channel.c | 48 ++++++++++++++++++++++++ drivers/net/wireless/ath/ath9k/recv.c | 5 +++ 3 files changed, 55 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 02f30980c31a..a4646a076099 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -335,6 +335,7 @@ struct ath_chanctx { struct ath9k_hw_cal_data caldata; struct timespec tsf_ts; u64 tsf_val; + u32 last_beacon; u16 txpower; bool offchannel; @@ -348,6 +349,7 @@ enum ath_chanctx_event { ATH_CHANCTX_EVENT_BEACON_PREPARE, ATH_CHANCTX_EVENT_BEACON_SENT, ATH_CHANCTX_EVENT_TSF_TIMER, + ATH_CHANCTX_EVENT_BEACON_RECEIVED, }; enum ath_chanctx_state { diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 4793de079f77..6ea806cc68fe 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -382,10 +382,51 @@ void ath_chanctx_offchan_switch(struct ath_softc *sc, ath_chanctx_switch(sc, &sc->offchannel.chan, &chandef); } +static struct ath_chanctx * +ath_chanctx_get_next(struct ath_softc *sc, struct ath_chanctx *ctx) +{ + int idx = ctx - &sc->chanctx[0]; + + return &sc->chanctx[!idx]; +} + +static void ath_chanctx_adjust_tbtt_delta(struct ath_softc *sc) +{ + struct ath_chanctx *prev, *cur; + struct timespec ts; + u32 cur_tsf, prev_tsf, beacon_int; + s32 offset; + + beacon_int = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval); + + cur = sc->cur_chan; + prev = ath_chanctx_get_next(sc, cur); + + getrawmonotonic(&ts); + cur_tsf = (u32) cur->tsf_val + + ath9k_hw_get_tsf_offset(&cur->tsf_ts, &ts); + + prev_tsf = prev->last_beacon - (u32) prev->tsf_val + cur_tsf; + prev_tsf -= ath9k_hw_get_tsf_offset(&prev->tsf_ts, &ts); + + /* Adjust the TSF time of the AP chanctx to keep its beacons + * at half beacon interval offset relative to the STA chanctx. + */ + offset = cur_tsf - prev_tsf; + + /* Ignore stale data or spurious timestamps */ + if (offset < 0 || offset > 3 * beacon_int) + return; + + offset = beacon_int / 2 - (offset % beacon_int); + prev->tsf_val += offset; +} + void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, enum ath_chanctx_event ev) { struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); struct ath_vif *avp = NULL; u32 tsf_time; bool noa_changed = false; @@ -410,6 +451,7 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, tsf_time = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval); tsf_time = sc->sched.next_tbtt + tsf_time / 4; sc->sched.switch_start_time = tsf_time; + sc->cur_chan->last_beacon = sc->sched.next_tbtt; if (sc->sched.offchannel_duration) { noa_changed = true; @@ -441,6 +483,12 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, sc->sched.state = ATH_CHANCTX_STATE_SWITCH; ieee80211_queue_work(sc->hw, &sc->chanctx_work); break; + case ATH_CHANCTX_EVENT_BEACON_RECEIVED: + if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) + break; + + ath_chanctx_adjust_tbtt_delta(sc); + break; } spin_unlock_bh(&sc->chan_lock); diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 7b9c7e53caaa..74ab1d02013b 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -892,6 +892,11 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc, return -EINVAL; } + if (rx_stats->is_mybeacon) { + sc->sched.next_tbtt = rx_stats->rs_tstamp; + ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_BEACON_RECEIVED); + } + ath9k_cmn_process_rssi(common, hw, rx_stats, rx_status); rx_status->band = ah->curchan->chan->band; From 6036c2845650d26a15b44498f8fb8f8f4518847a Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 11 Jun 2014 16:18:09 +0530 Subject: [PATCH 050/274] ath9k: Implement mgd_prepare_tx Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 3 ++ drivers/net/wireless/ath/ath9k/channel.c | 50 ++++++++++++++++++++---- drivers/net/wireless/ath/ath9k/main.c | 1 + 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index a4646a076099..7947909918bd 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -357,6 +357,7 @@ enum ath_chanctx_state { ATH_CHANCTX_STATE_WAIT_FOR_BEACON, ATH_CHANCTX_STATE_WAIT_FOR_TIMER, ATH_CHANCTX_STATE_SWITCH, + ATH_CHANCTX_STATE_FORCE_ACTIVE, }; struct ath_chanctx_sched { @@ -397,6 +398,8 @@ struct ath_offchannel { ctx++) void ath9k_fill_chanctx_ops(void); +void ath9k_chanctx_force_active(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); static inline struct ath_chanctx * ath_chanctx_get(struct ieee80211_chanctx_conf *ctx) { diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 6ea806cc68fe..8d56b7961d7b 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -223,25 +223,20 @@ static bool ath_chanctx_defer_switch(struct ath_softc *sc) return true; } -void ath_chanctx_work(struct work_struct *work) +static void ath_chanctx_set_next(struct ath_softc *sc, bool force) { - struct ath_softc *sc = container_of(work, struct ath_softc, - chanctx_work); struct timespec ts; bool measure_time = false; bool send_ps = false; - mutex_lock(&sc->mutex); spin_lock_bh(&sc->chan_lock); if (!sc->next_chan) { spin_unlock_bh(&sc->chan_lock); - mutex_unlock(&sc->mutex); return; } - if (ath_chanctx_defer_switch(sc)) { + if (!force && ath_chanctx_defer_switch(sc)) { spin_unlock_bh(&sc->chan_lock); - mutex_unlock(&sc->mutex); return; } @@ -269,8 +264,9 @@ void ath_chanctx_work(struct work_struct *work) sc->cur_chan = sc->next_chan; sc->cur_chan->stopped = false; sc->next_chan = NULL; - sc->sched.state = ATH_CHANCTX_STATE_IDLE; sc->sched.offchannel_duration = 0; + if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE) + sc->sched.state = ATH_CHANCTX_STATE_IDLE; spin_unlock_bh(&sc->chan_lock); @@ -286,6 +282,14 @@ void ath_chanctx_work(struct work_struct *work) ath_chanctx_send_ps_frame(sc, false); ath_offchannel_channel_change(sc); +} + +void ath_chanctx_work(struct work_struct *work) +{ + struct ath_softc *sc = container_of(work, struct ath_softc, + chanctx_work); + mutex_lock(&sc->mutex); + ath_chanctx_set_next(sc, false); mutex_unlock(&sc->mutex); } @@ -320,6 +324,36 @@ void ath_chanctx_init(struct ath_softc *sc) } +void ath9k_chanctx_force_active(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath_softc *sc = hw->priv; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_vif *avp = (struct ath_vif *) vif->drv_priv; + bool changed = false; + + if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) + return; + + if (!avp->chanctx) + return; + + mutex_lock(&sc->mutex); + + spin_lock_bh(&sc->chan_lock); + if (sc->next_chan || (sc->cur_chan != avp->chanctx)) { + sc->next_chan = avp->chanctx; + changed = true; + } + sc->sched.state = ATH_CHANCTX_STATE_FORCE_ACTIVE; + spin_unlock_bh(&sc->chan_lock); + + if (changed) + ath_chanctx_set_next(sc, true); + + mutex_unlock(&sc->mutex); +} + void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx, struct cfg80211_chan_def *chandef) { diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 0ba496d400d0..b8975f0700bf 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2553,6 +2553,7 @@ void ath9k_fill_chanctx_ops(void) ath9k_ops.change_chanctx = ath9k_change_chanctx; ath9k_ops.assign_vif_chanctx = ath9k_assign_vif_chanctx; ath9k_ops.unassign_vif_chanctx = ath9k_unassign_vif_chanctx; + ath9k_ops.mgd_prepare_tx = ath9k_chanctx_force_active; } struct ieee80211_ops ath9k_ops = { From 73fa2f26d35a37034fdff9fd702887909e138926 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 11 Jun 2014 16:18:10 +0530 Subject: [PATCH 051/274] ath9k: Add multi-channel scheduling support Signed-off-by: Felix Fietkau Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 5 ++ drivers/net/wireless/ath/ath9k/channel.c | 95 +++++++++++++++++++++++- drivers/net/wireless/ath/ath9k/main.c | 3 + 3 files changed, 99 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 7947909918bd..0bc63bd4ec26 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -350,6 +350,10 @@ enum ath_chanctx_event { ATH_CHANCTX_EVENT_BEACON_SENT, ATH_CHANCTX_EVENT_TSF_TIMER, ATH_CHANCTX_EVENT_BEACON_RECEIVED, + ATH_CHANCTX_EVENT_ASSOC, + ATH_CHANCTX_EVENT_SWITCH, + ATH_CHANCTX_EVENT_UNASSIGN, + ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL, }; enum ath_chanctx_state { @@ -362,6 +366,7 @@ enum ath_chanctx_state { struct ath_chanctx_sched { bool beacon_pending; + bool offchannel_pending; enum ath_chanctx_state state; u32 next_tbtt; diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 8d56b7961d7b..1cb2909a114c 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -180,10 +180,13 @@ void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx) n_active++; } - if (n_active > 1) - set_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags); - else + if (n_active <= 1) { clear_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags); + return; + } + if (test_and_set_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) + return; + ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL); } static bool @@ -282,6 +285,7 @@ static void ath_chanctx_set_next(struct ath_softc *sc, bool force) ath_chanctx_send_ps_frame(sc, false); ath_offchannel_channel_change(sc); + ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_SWITCH); } void ath_chanctx_work(struct work_struct *work) @@ -357,8 +361,17 @@ void ath9k_chanctx_force_active(struct ieee80211_hw *hw, void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx, struct cfg80211_chan_def *chandef) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); spin_lock_bh(&sc->chan_lock); + + if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) && + (sc->cur_chan != ctx) && (ctx == &sc->offchannel.chan)) { + sc->sched.offchannel_pending = true; + spin_unlock_bh(&sc->chan_lock); + return; + } + sc->next_chan = ctx; if (chandef) ctx->chandef = *chandef; @@ -462,6 +475,7 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath_vif *avp = NULL; + struct ath_chanctx *ctx; u32 tsf_time; bool noa_changed = false; @@ -475,6 +489,25 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, if (avp->offchannel_duration) avp->offchannel_duration = 0; + if (avp->chanctx != sc->cur_chan) + break; + + if (sc->sched.offchannel_pending) { + sc->sched.offchannel_pending = false; + sc->next_chan = &sc->offchannel.chan; + sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; + } + + ctx = ath_chanctx_get_next(sc, sc->cur_chan); + if (ctx->active && sc->sched.state == ATH_CHANCTX_STATE_IDLE) { + sc->next_chan = ctx; + sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; + } + + /* if the timer missed its window, use the next interval */ + if (sc->sched.state == ATH_CHANCTX_STATE_WAIT_FOR_TIMER) + sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; + if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON) break; @@ -518,11 +551,65 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, ieee80211_queue_work(sc->hw, &sc->chanctx_work); break; case ATH_CHANCTX_EVENT_BEACON_RECEIVED: - if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) + if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) || + sc->cur_chan == &sc->offchannel.chan) break; ath_chanctx_adjust_tbtt_delta(sc); break; + case ATH_CHANCTX_EVENT_ASSOC: + if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE || + avp->chanctx != sc->cur_chan) + break; + + sc->sched.state = ATH_CHANCTX_STATE_IDLE; + /* fall through */ + case ATH_CHANCTX_EVENT_SWITCH: + if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) || + sc->sched.state == ATH_CHANCTX_STATE_FORCE_ACTIVE || + sc->cur_chan->switch_after_beacon || + sc->cur_chan == &sc->offchannel.chan) + break; + + /* If this is a station chanctx, stay active for a half + * beacon period (minus channel switch time) + */ + sc->next_chan = ath_chanctx_get_next(sc, sc->cur_chan); + + sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER; + tsf_time = ath9k_hw_gettsf32(sc->sc_ah); + tsf_time += + TU_TO_USEC(sc->cur_chan->beacon.beacon_interval) / 2; + tsf_time -= sc->sched.channel_switch_time; + sc->sched.switch_start_time = tsf_time; + + ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, + tsf_time, 1000000); + break; + case ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL: + if (sc->cur_chan == &sc->offchannel.chan || + sc->cur_chan->switch_after_beacon) + break; + + sc->next_chan = ath_chanctx_get_next(sc, sc->cur_chan); + ieee80211_queue_work(sc->hw, &sc->chanctx_work); + break; + case ATH_CHANCTX_EVENT_UNASSIGN: + if (sc->cur_chan->assigned) { + if (sc->next_chan && !sc->next_chan->assigned && + sc->next_chan != &sc->offchannel.chan) + sc->sched.state = ATH_CHANCTX_STATE_IDLE; + break; + } + + ctx = ath_chanctx_get_next(sc, sc->cur_chan); + sc->sched.state = ATH_CHANCTX_STATE_IDLE; + if (!ctx->assigned) + break; + + sc->next_chan = ctx; + ieee80211_queue_work(sc->hw, &sc->chanctx_work); + break; } spin_unlock_bh(&sc->chan_lock); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index b8975f0700bf..f7d8ddae216e 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1747,6 +1747,8 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, bss_conf->bssid, bss_conf->assoc); ath9k_calculate_summary_state(sc, avp->chanctx); + if (bss_conf->assoc) + ath_chanctx_event(sc, vif, ATH_CHANCTX_EVENT_ASSOC); } if (changed & BSS_CHANGED_IBSS) { @@ -2492,6 +2494,7 @@ static void ath9k_remove_chanctx(struct ieee80211_hw *hw, mutex_lock(&sc->mutex); ctx->assigned = false; + ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_UNASSIGN); mutex_unlock(&sc->mutex); } From 7414863ed3dfa407006c92616c1e0efda481738c Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 11 Jun 2014 16:18:11 +0530 Subject: [PATCH 052/274] ath9k: Add periodic NoA support Signed-off-by: Felix Fietkau Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 3 +++ drivers/net/wireless/ath/ath9k/beacon.c | 25 +++++++++++++++++++----- drivers/net/wireless/ath/ath9k/channel.c | 24 ++++++++++++++++++++--- 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 0bc63bd4ec26..a5afd4a7df9f 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -489,6 +489,9 @@ struct ath_vif { u8 noa_index; u32 offchannel_start; u32 offchannel_duration; + + u32 periodic_noa_start; + u32 periodic_noa_duration; }; struct ath9k_vif_iter_data { diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 85a40d749e20..eaf8f058c151 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -122,12 +122,15 @@ static void ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp, }; struct ieee80211_p2p_noa_attr *noa; - int noa_len = 2 + sizeof(struct ieee80211_p2p_noa_desc); + int noa_len, noa_desc, i = 0; u8 *hdr; - if (!avp->offchannel_duration) + if (!avp->offchannel_duration && !avp->periodic_noa_duration) return; + noa_desc = !!avp->offchannel_duration + !!avp->periodic_noa_duration; + noa_len = 2 + sizeof(struct ieee80211_p2p_noa_desc) * noa_desc; + hdr = skb_put(skb, sizeof(noa_ie_hdr)); memcpy(hdr, noa_ie_hdr, sizeof(noa_ie_hdr)); hdr[1] = sizeof(noa_ie_hdr) + noa_len - 2; @@ -137,9 +140,21 @@ static void ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp, memset(noa, 0, noa_len); noa->index = avp->noa_index; - noa->desc[0].count = 1; - noa->desc[0].duration = cpu_to_le32(avp->offchannel_duration); - noa->desc[0].start_time = cpu_to_le32(avp->offchannel_start); + if (avp->periodic_noa_duration) { + u32 interval = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval); + + noa->desc[i].count = 255; + noa->desc[i].start_time = cpu_to_le32(avp->periodic_noa_start); + noa->desc[i].duration = cpu_to_le32(avp->periodic_noa_duration); + noa->desc[i].interval = cpu_to_le32(interval); + i++; + } + + if (avp->offchannel_duration) { + noa->desc[i].count = 1; + noa->desc[i].start_time = cpu_to_le32(avp->offchannel_start); + noa->desc[i].duration = cpu_to_le32(avp->offchannel_duration); + } } static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 1cb2909a114c..3d9776c4c909 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -474,6 +474,7 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); + struct ath_beacon_config *cur_conf; struct ath_vif *avp = NULL; struct ath_chanctx *ctx; u32 tsf_time; @@ -514,12 +515,29 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, sc->sched.beacon_pending = true; sc->sched.next_tbtt = REG_READ(ah, AR_NEXT_TBTT_TIMER); + cur_conf = &sc->cur_chan->beacon; /* defer channel switch by a quarter beacon interval */ - tsf_time = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval); + tsf_time = TU_TO_USEC(cur_conf->beacon_interval); tsf_time = sc->sched.next_tbtt + tsf_time / 4; sc->sched.switch_start_time = tsf_time; sc->cur_chan->last_beacon = sc->sched.next_tbtt; + /* Prevent wrap-around issues */ + if (avp->periodic_noa_duration && + tsf_time - avp->periodic_noa_start > BIT(30)) + avp->periodic_noa_duration = 0; + + if (ctx->active && !avp->periodic_noa_duration) { + avp->periodic_noa_start = tsf_time; + avp->periodic_noa_duration = + TU_TO_USEC(cur_conf->beacon_interval) / 2 - + sc->sched.channel_switch_time; + noa_changed = true; + } else if (!ctx->active && avp->periodic_noa_duration) { + avp->periodic_noa_duration = 0; + noa_changed = true; + } + if (sc->sched.offchannel_duration) { noa_changed = true; avp->offchannel_start = tsf_time; @@ -575,11 +593,11 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, * beacon period (minus channel switch time) */ sc->next_chan = ath_chanctx_get_next(sc, sc->cur_chan); + cur_conf = &sc->cur_chan->beacon; sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER; tsf_time = ath9k_hw_gettsf32(sc->sc_ah); - tsf_time += - TU_TO_USEC(sc->cur_chan->beacon.beacon_interval) / 2; + tsf_time += TU_TO_USEC(cur_conf->beacon_interval) / 2; tsf_time -= sc->sched.channel_switch_time; sc->sched.switch_start_time = tsf_time; From ec70abe1f62099f8cdd5453e20098e15435706bd Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 11 Jun 2014 16:18:12 +0530 Subject: [PATCH 053/274] ath9k: Handle beacon miss on multi channel context Signed-off-by: Felix Fietkau Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 + drivers/net/wireless/ath/ath9k/channel.c | 30 ++++++++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index a5afd4a7df9f..ee00ddb8885d 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -368,6 +368,7 @@ struct ath_chanctx_sched { bool beacon_pending; bool offchannel_pending; enum ath_chanctx_state state; + u8 beacon_miss; u32 next_tbtt; u32 switch_start_time; diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 3d9776c4c909..55165d5a7ed1 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -478,6 +478,7 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, struct ath_vif *avp = NULL; struct ath_chanctx *ctx; u32 tsf_time; + u32 beacon_int; bool noa_changed = false; if (vif) @@ -516,9 +517,10 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, sc->sched.next_tbtt = REG_READ(ah, AR_NEXT_TBTT_TIMER); cur_conf = &sc->cur_chan->beacon; + beacon_int = TU_TO_USEC(cur_conf->beacon_interval); + /* defer channel switch by a quarter beacon interval */ - tsf_time = TU_TO_USEC(cur_conf->beacon_interval); - tsf_time = sc->sched.next_tbtt + tsf_time / 4; + tsf_time = sc->sched.next_tbtt + beacon_int / 4; sc->sched.switch_start_time = tsf_time; sc->cur_chan->last_beacon = sc->sched.next_tbtt; @@ -538,6 +540,13 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, noa_changed = true; } + /* If at least two consecutive beacons were missed on the STA + * chanctx, stay on the STA channel for one extra beacon period, + * to resync the timer properly. + */ + if (ctx->active && sc->sched.beacon_miss >= 2) + sc->sched.offchannel_duration = 3 * beacon_int / 2; + if (sc->sched.offchannel_duration) { noa_changed = true; avp->offchannel_start = tsf_time; @@ -565,6 +574,10 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_TIMER) break; + if (!sc->cur_chan->switch_after_beacon && + sc->sched.beacon_pending) + sc->sched.beacon_miss++; + sc->sched.state = ATH_CHANCTX_STATE_SWITCH; ieee80211_queue_work(sc->hw, &sc->chanctx_work); break; @@ -574,6 +587,8 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, break; ath_chanctx_adjust_tbtt_delta(sc); + sc->sched.beacon_pending = false; + sc->sched.beacon_miss = 0; break; case ATH_CHANCTX_EVENT_ASSOC: if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE || @@ -596,13 +611,20 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, cur_conf = &sc->cur_chan->beacon; sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER; - tsf_time = ath9k_hw_gettsf32(sc->sc_ah); - tsf_time += TU_TO_USEC(cur_conf->beacon_interval) / 2; + + tsf_time = TU_TO_USEC(cur_conf->beacon_interval) / 2; + if (sc->sched.beacon_miss >= 2) { + sc->sched.beacon_miss = 0; + tsf_time *= 3; + } + tsf_time -= sc->sched.channel_switch_time; + tsf_time += ath9k_hw_gettsf32(sc->sc_ah); sc->sched.switch_start_time = tsf_time; ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, tsf_time, 1000000); + sc->sched.beacon_pending = true; break; case ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL: if (sc->cur_chan == &sc->offchannel.chan || From a899b678d92fcd25215192dae26913cdb8a9b96d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 11 Jun 2014 16:18:13 +0530 Subject: [PATCH 054/274] ath9k: Update channel switch timer TSF time might have been updated by the incoming beacon, need update the channel switch timer to reflect the change. Signed-off-by: Felix Fietkau Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/channel.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 55165d5a7ed1..0a38eea27870 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -589,6 +589,17 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, ath_chanctx_adjust_tbtt_delta(sc); sc->sched.beacon_pending = false; sc->sched.beacon_miss = 0; + + /* TSF time might have been updated by the incoming beacon, + * need update the channel switch timer to reflect the change. + */ + tsf_time = sc->sched.switch_start_time; + tsf_time -= (u32) sc->cur_chan->tsf_val + + ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts, NULL); + tsf_time += ath9k_hw_gettsf32(ah); + + ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, + tsf_time, 1000000); break; case ATH_CHANCTX_EVENT_ASSOC: if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE || From 42eda11558559104c09f0d0924bbb210edfcf487 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 11 Jun 2014 16:18:14 +0530 Subject: [PATCH 055/274] ath9k: Add recovery mechanism for hw TSF timer Configure the TSF based hardware timer for a channel switch. Also set up backup software timer, in case the gen timer fails. This could be caused by a hardware reset. Signed-off-by: Felix Fietkau Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 4 +++ drivers/net/wireless/ath/ath9k/channel.c | 32 ++++++++++++++++++------ drivers/net/wireless/ath/ath9k/init.c | 1 + drivers/net/wireless/ath/ath9k/main.c | 1 + 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index ee00ddb8885d..757dd602daaa 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -374,6 +374,9 @@ struct ath_chanctx_sched { u32 switch_start_time; unsigned int offchannel_duration; unsigned int channel_switch_time; + + /* backup, in case the hardware timer fails */ + struct timer_list timer; }; enum ath_offchannel_state { @@ -426,6 +429,7 @@ struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc, bool active); void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, enum ath_chanctx_event ev); +void ath_chanctx_timer(unsigned long data); int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan); int ath_startrecv(struct ath_softc *sc); diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 0a38eea27870..ba214ebdcd16 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -469,6 +469,27 @@ static void ath_chanctx_adjust_tbtt_delta(struct ath_softc *sc) prev->tsf_val += offset; } +void ath_chanctx_timer(unsigned long data) +{ + struct ath_softc *sc = (struct ath_softc *) data; + + ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER); +} + +/* Configure the TSF based hardware timer for a channel switch. + * Also set up backup software timer, in case the gen timer fails. + * This could be caused by a hardware reset. + */ +static void ath_chanctx_setup_timer(struct ath_softc *sc, u32 tsf_time) +{ + struct ath_hw *ah = sc->sc_ah; + + ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, tsf_time, 1000000); + tsf_time -= ath9k_hw_gettsf32(ah); + tsf_time = msecs_to_jiffies(tsf_time / 1000) + 1; + mod_timer(&sc->sched.timer, tsf_time); +} + void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, enum ath_chanctx_event ev) { @@ -566,9 +587,7 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, break; sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER; - ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, - sc->sched.switch_start_time, - 1000000); + ath_chanctx_setup_timer(sc, sc->sched.switch_start_time); break; case ATH_CHANCTX_EVENT_TSF_TIMER: if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_TIMER) @@ -598,8 +617,8 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts, NULL); tsf_time += ath9k_hw_gettsf32(ah); - ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, - tsf_time, 1000000); + + ath_chanctx_setup_timer(sc, tsf_time); break; case ATH_CHANCTX_EVENT_ASSOC: if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE || @@ -633,8 +652,7 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, tsf_time += ath9k_hw_gettsf32(sc->sc_ah); sc->sched.switch_start_time = tsf_time; - ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, - tsf_time, 1000000); + ath_chanctx_setup_timer(sc, tsf_time); sc->sched.beacon_pending = true; break; case ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL: diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 66a9dc3a5369..a4afcb19af2a 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -569,6 +569,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work); setup_timer(&sc->offchannel.timer, ath_offchannel_timer, (unsigned long)sc); + setup_timer(&sc->sched.timer, ath_chanctx_timer, (unsigned long)sc); /* * Cache line size is used to size and align various diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index f7d8ddae216e..b307e6e2c0be 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1668,6 +1668,7 @@ void ath9k_p2p_ps_timer(void *priv) struct ath_node *an; u32 tsf; + del_timer_sync(&sc->sched.timer); ath9k_hw_gen_timer_stop(sc->sc_ah, sc->p2p_ps_timer); ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER); From 3ad9c3861acef2343b232d733aa288e71cc07d44 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Wed, 11 Jun 2014 16:18:15 +0530 Subject: [PATCH 056/274] ath9k: use separate HW queue for each channel context Use seperate tx queue for each AC in each channel context and expose these information to mac80211 to avoid stopping one channel context leads to stopping the entire traffic for that AC even on other contexts. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 + drivers/net/wireless/ath/ath9k/init.c | 10 +++++- drivers/net/wireless/ath/ath9k/main.c | 48 ++++++++++++++++++++++++-- drivers/net/wireless/ath/ath9k/xmit.c | 11 +++--- 4 files changed, 63 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 757dd602daaa..11b5e4dd6294 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -327,6 +327,7 @@ struct ath_chanctx { struct cfg80211_chan_def chandef; struct list_head vifs; struct list_head acq[IEEE80211_NUM_ACS]; + int hw_queue_base; /* do not dereference, use for comparison only */ struct ieee80211_vif *primary_sta; diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index a4afcb19af2a..7afb40572ed0 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -511,6 +511,8 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, sc->tx99_power = MAX_RATE_POWER + 1; init_waitqueue_head(&sc->tx_wait); sc->cur_chan = &sc->chanctx[0]; + if (!ath9k_use_chanctx) + sc->cur_chan->hw_queue_base = 0; if (!pdata || pdata->use_eeprom) { ah->ah_flags |= AH_USE_EEPROM; @@ -718,6 +720,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) IEEE80211_HW_SPECTRUM_MGMT | IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SUPPORTS_RC_TABLE | + IEEE80211_HW_QUEUE_CONTROL | IEEE80211_HW_SUPPORTS_HT_CCK_RATES; if (ath9k_ps_enable) @@ -769,7 +772,12 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; - hw->queues = 4; + /* allow 4 queues per channel context + + * 1 cab queue + 1 offchannel tx queue + */ + hw->queues = 10; + /* last queue for offchannel */ + hw->offchannel_tx_hw_queue = hw->queues - 1; hw->max_rates = 4; hw->max_listen_interval = 1; hw->max_rate_tries = 10; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index b307e6e2c0be..cf21652835c1 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -223,6 +223,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); unsigned long flags; + int i; if (ath_startrecv(sc) != 0) { ath_err(common, "Unable to restart recv logic\n"); @@ -267,7 +268,20 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) ath9k_hw_set_interrupts(ah); ath9k_hw_enable_interrupts(ah); - ieee80211_wake_queues(sc->hw); + if (!ath9k_use_chanctx) + ieee80211_wake_queues(sc->hw); + else { + if (sc->cur_chan == &sc->offchannel.chan) + ieee80211_wake_queue(sc->hw, + sc->hw->offchannel_tx_hw_queue); + else { + for (i = 0; i < IEEE80211_NUM_ACS; i++) + ieee80211_wake_queue(sc->hw, + sc->cur_chan->hw_queue_base + i); + } + if (ah->opmode == NL80211_IFTYPE_AP) + ieee80211_wake_queue(sc->hw, sc->hw->queues - 2); + } ath9k_p2p_ps_timer(sc); @@ -1108,6 +1122,7 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, struct ath_common *common = ath9k_hw_common(ah); struct ath_vif *avp = (void *)vif->drv_priv; struct ath_node *an = &avp->mcast_node; + int i; mutex_lock(&sc->mutex); @@ -1130,6 +1145,12 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, avp->chanctx = sc->cur_chan; list_add_tail(&avp->list, &avp->chanctx->vifs); } + for (i = 0; i < IEEE80211_NUM_ACS; i++) + vif->hw_queue[i] = i; + if (vif->type == NL80211_IFTYPE_AP) + vif->cab_queue = hw->queues - 2; + else + vif->cab_queue = IEEE80211_INVAL_HW_QUEUE; an->sc = sc; an->sta = NULL; @@ -1149,6 +1170,7 @@ static int ath9k_change_interface(struct ieee80211_hw *hw, struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_vif *avp = (void *)vif->drv_priv; + int i; mutex_lock(&sc->mutex); @@ -1168,6 +1190,14 @@ static int ath9k_change_interface(struct ieee80211_hw *hw, if (ath9k_uses_beacons(vif->type)) ath9k_beacon_assign_slot(sc, vif); + for (i = 0; i < IEEE80211_NUM_ACS; i++) + vif->hw_queue[i] = i; + + if (vif->type == NL80211_IFTYPE_AP) + vif->cab_queue = hw->queues - 2; + else + vif->cab_queue = IEEE80211_INVAL_HW_QUEUE; + ath9k_calculate_summary_state(sc, avp->chanctx); mutex_unlock(&sc->mutex); @@ -1984,6 +2014,7 @@ void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) struct ath_common *common = ath9k_hw_common(ah); int timeout = HZ / 5; /* 200 ms */ bool drain_txq; + int i; cancel_delayed_work_sync(&sc->tx_complete_work); @@ -2011,7 +2042,10 @@ void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) ath_reset(sc); ath9k_ps_restore(sc); - ieee80211_wake_queues(hw); + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + ieee80211_wake_queue(sc->hw, + sc->cur_chan->hw_queue_base + i); + } } ieee80211_queue_delayed_work(hw, &sc->tx_complete_work, 0); @@ -2468,6 +2502,7 @@ static int ath9k_add_chanctx(struct ieee80211_hw *hw, { struct ath_softc *sc = hw->priv; struct ath_chanctx *ctx, **ptr; + int pos; mutex_lock(&sc->mutex); @@ -2478,6 +2513,8 @@ static int ath9k_add_chanctx(struct ieee80211_hw *hw, ptr = (void *) conf->drv_priv; *ptr = ctx; ctx->assigned = true; + pos = ctx - &sc->chanctx[0]; + ctx->hw_queue_base = pos * IEEE80211_NUM_ACS; ath_chanctx_set_channel(sc, ctx, &conf->def); mutex_unlock(&sc->mutex); return 0; @@ -2495,6 +2532,7 @@ static void ath9k_remove_chanctx(struct ieee80211_hw *hw, mutex_lock(&sc->mutex); ctx->assigned = false; + ctx->hw_queue_base = -1; ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_UNASSIGN); mutex_unlock(&sc->mutex); } @@ -2518,11 +2556,14 @@ static int ath9k_assign_vif_chanctx(struct ieee80211_hw *hw, struct ath_softc *sc = hw->priv; struct ath_vif *avp = (void *)vif->drv_priv; struct ath_chanctx *ctx = ath_chanctx_get(conf); + int i; mutex_lock(&sc->mutex); avp->chanctx = ctx; list_add_tail(&avp->list, &ctx->vifs); ath9k_calculate_summary_state(sc, ctx); + for (i = 0; i < IEEE80211_NUM_ACS; i++) + vif->hw_queue[i] = ctx->hw_queue_base + i; mutex_unlock(&sc->mutex); return 0; @@ -2535,11 +2576,14 @@ static void ath9k_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ath_softc *sc = hw->priv; struct ath_vif *avp = (void *)vif->drv_priv; struct ath_chanctx *ctx = ath_chanctx_get(conf); + int ac; mutex_lock(&sc->mutex); avp->chanctx = NULL; list_del(&avp->list); ath9k_calculate_summary_state(sc, ctx); + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) + vif->hw_queue[ac] = IEEE80211_INVAL_HW_QUEUE; mutex_unlock(&sc->mutex); } diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index a422c20fe065..d4927c9a6bae 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -156,7 +156,8 @@ static void ath_set_rates(struct ieee80211_vif *vif, struct ieee80211_sta *sta, static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq, struct sk_buff *skb) { - int q; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + int q, hw_queue; q = skb_get_queue_mapping(skb); if (txq == sc->tx.uapsdq) @@ -168,9 +169,10 @@ static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq, if (WARN_ON(--txq->pending_frames < 0)) txq->pending_frames = 0; + hw_queue = (info->hw_queue >= sc->hw->queues - 2) ? q : info->hw_queue; if (txq->stopped && txq->pending_frames < sc->tx.txq_max_pending[q]) { - ieee80211_wake_queue(sc->hw, q); + ieee80211_wake_queue(sc->hw, hw_queue); txq->stopped = false; } } @@ -2191,7 +2193,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, struct ath_atx_tid *tid = NULL; struct ath_buf *bf; bool queue; - int q; + int q, hw_queue; int ret; if (vif) @@ -2211,12 +2213,13 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, */ q = skb_get_queue_mapping(skb); + hw_queue = (info->hw_queue >= sc->hw->queues - 2) ? q : info->hw_queue; ath_txq_lock(sc, txq); if (txq == sc->tx.txq_map[q] && ++txq->pending_frames > sc->tx.txq_max_pending[q] && !txq->stopped) { - ieee80211_stop_queue(sc->hw, q); + ieee80211_stop_queue(sc->hw, hw_queue); txq->stopped = true; } From a4068323d5775877e89d87fc2fdaebd6e9e6a33c Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Wed, 11 Jun 2014 16:18:16 +0530 Subject: [PATCH 057/274] ath9k: Advertise multichannel support Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/init.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 7afb40572ed0..79fdab6a4003 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -672,6 +672,12 @@ static const struct ieee80211_iface_limit wds_limits[] = { { .max = 2048, .types = BIT(NL80211_IFTYPE_WDS) }, }; +static const struct ieee80211_iface_limit if_limits_multi[] = { + { .max = 1, .types = BIT(NL80211_IFTYPE_STATION) }, + { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) }, +}; + static const struct ieee80211_iface_limit if_dfs_limits[] = { { .max = 1, .types = BIT(NL80211_IFTYPE_AP) | #ifdef CONFIG_MAC80211_MESH @@ -680,6 +686,16 @@ static const struct ieee80211_iface_limit if_dfs_limits[] = { BIT(NL80211_IFTYPE_ADHOC) }, }; +static const struct ieee80211_iface_combination if_comb_multi[] = { + { + .limits = if_limits_multi, + .n_limits = ARRAY_SIZE(if_limits_multi), + .max_interfaces = 2, + .num_different_channels = 2, + .beacon_int_infra_match = true, + }, +}; + static const struct ieee80211_iface_combination if_comb[] = { { .limits = if_limits, @@ -748,12 +764,14 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MESH_POINT); - hw->wiphy->iface_combinations = if_comb; if (!ath9k_use_chanctx) { + hw->wiphy->iface_combinations = if_comb; hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_WDS); } else { - hw->wiphy->n_iface_combinations = 1; + hw->wiphy->iface_combinations = if_comb_multi; + hw->wiphy->n_iface_combinations = + ARRAY_SIZE(if_comb_multi); hw->wiphy->max_scan_ssids = 255; hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; hw->wiphy->max_remain_on_channel_duration = 10000; From df6e6333237dd1e55571e1e8c61354ae51b26d82 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sun, 15 Jun 2014 13:37:43 -0700 Subject: [PATCH 058/274] rt2x00: Use dma_zalloc_coherent Use the zeroing function instead of dma_alloc_coherent & memset(,0,) Signed-off-by: Joe Perches Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00mmio.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2x00mmio.c b/drivers/net/wireless/rt2x00/rt2x00mmio.c index 6f236ea180aa..f0178fd4fe5f 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mmio.c +++ b/drivers/net/wireless/rt2x00/rt2x00mmio.c @@ -119,14 +119,12 @@ static int rt2x00mmio_alloc_queue_dma(struct rt2x00_dev *rt2x00dev, /* * Allocate DMA memory for descriptor and buffer. */ - addr = dma_alloc_coherent(rt2x00dev->dev, - queue->limit * queue->desc_size, - &dma, GFP_KERNEL); + addr = dma_zalloc_coherent(rt2x00dev->dev, + queue->limit * queue->desc_size, &dma, + GFP_KERNEL); if (!addr) return -ENOMEM; - memset(addr, 0, queue->limit * queue->desc_size); - /* * Initialize all queue entries to contain valid addresses. */ From 51b4a86abd088431e09a01fd335ad385a3ddfe21 Mon Sep 17 00:00:00 2001 From: Jahnavi Meher Date: Mon, 16 Jun 2014 19:39:40 +0530 Subject: [PATCH 059/274] rsi: Mapping the debugfs stats to the correct s/w queues. Changed the queue numbers to macros, and corrected the mappings. Signed-off-by: Jahnavi Meher Signed-off-by: John W. Linville --- drivers/net/wireless/rsi/rsi_91x_debugfs.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_debugfs.c b/drivers/net/wireless/rsi/rsi_91x_debugfs.c index c466246a323f..828a042f903f 100644 --- a/drivers/net/wireless/rsi/rsi_91x_debugfs.c +++ b/drivers/net/wireless/rsi/rsi_91x_debugfs.c @@ -145,7 +145,7 @@ static int rsi_stats_read(struct seq_file *seq, void *data) seq_printf(seq, "total_mgmt_pkt_send : %d\n", common->tx_stats.total_tx_pkt_send[MGMT_SOFT_Q]); seq_printf(seq, "total_mgmt_pkt_queued : %d\n", - skb_queue_len(&common->tx_queue[4])); + skb_queue_len(&common->tx_queue[MGMT_SOFT_Q])); seq_printf(seq, "total_mgmt_pkt_freed : %d\n", common->tx_stats.total_tx_pkt_freed[MGMT_SOFT_Q]); @@ -153,25 +153,25 @@ static int rsi_stats_read(struct seq_file *seq, void *data) seq_printf(seq, "total_data_vo_pkt_send: %8d\t", common->tx_stats.total_tx_pkt_send[VO_Q]); seq_printf(seq, "total_data_vo_pkt_queued: %8d\t", - skb_queue_len(&common->tx_queue[0])); + skb_queue_len(&common->tx_queue[VO_Q])); seq_printf(seq, "total_vo_pkt_freed: %8d\n", common->tx_stats.total_tx_pkt_freed[VO_Q]); seq_printf(seq, "total_data_vi_pkt_send: %8d\t", common->tx_stats.total_tx_pkt_send[VI_Q]); seq_printf(seq, "total_data_vi_pkt_queued: %8d\t", - skb_queue_len(&common->tx_queue[1])); + skb_queue_len(&common->tx_queue[VI_Q])); seq_printf(seq, "total_vi_pkt_freed: %8d\n", common->tx_stats.total_tx_pkt_freed[VI_Q]); seq_printf(seq, "total_data_be_pkt_send: %8d\t", common->tx_stats.total_tx_pkt_send[BE_Q]); seq_printf(seq, "total_data_be_pkt_queued: %8d\t", - skb_queue_len(&common->tx_queue[2])); + skb_queue_len(&common->tx_queue[BE_Q])); seq_printf(seq, "total_be_pkt_freed: %8d\n", common->tx_stats.total_tx_pkt_freed[BE_Q]); seq_printf(seq, "total_data_bk_pkt_send: %8d\t", common->tx_stats.total_tx_pkt_send[BK_Q]); seq_printf(seq, "total_data_bk_pkt_queued: %8d\t", - skb_queue_len(&common->tx_queue[3])); + skb_queue_len(&common->tx_queue[BK_Q])); seq_printf(seq, "total_bk_pkt_freed: %8d\n", common->tx_stats.total_tx_pkt_freed[BK_Q]); From 19d2e619e75459291ad053ab3e1a97619ae0704e Mon Sep 17 00:00:00 2001 From: Jahnavi Meher Date: Mon, 16 Jun 2014 19:40:02 +0530 Subject: [PATCH 060/274] rsi: Fixed the kernel doc Changed the function header to match the function name. Signed-off-by: Jahnavi Meher Signed-off-by: John W. Linville --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 54aaeb09debf..a1bdea126151 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -185,7 +185,7 @@ static void rsi_register_rates_channels(struct rsi_hw *adapter, int band) } /** - * rsi_mac80211_attach() - This function is used to de-initialize the + * rsi_mac80211_detach() - This function is used to de-initialize the * Mac80211 stack. * @adapter: Pointer to the adapter structure. * From aabd3ad41a00651fa7c81c3bcb9a7138cf1bb224 Mon Sep 17 00:00:00 2001 From: Jahnavi Meher Date: Mon, 16 Jun 2014 19:40:55 +0530 Subject: [PATCH 061/274] rsi: Using band from rsi_common to fill in ieee80211_rx_status Filling in band from common->band. Signed-off-by: Jahnavi Meher Signed-off-by: John W. Linville --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index a1bdea126151..45bdb9953755 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -770,10 +770,7 @@ static void rsi_fill_rx_status(struct ieee80211_hw *hw, rxs->signal = -(rssi); - if (channel <= 14) - rxs->band = IEEE80211_BAND_2GHZ; - else - rxs->band = IEEE80211_BAND_5GHZ; + rxs->band = common->band; freq = ieee80211_channel_to_frequency(channel, rxs->band); From f870a340f12ea7889fff40dbd05c0910ef3ecbd4 Mon Sep 17 00:00:00 2001 From: Jahnavi Meher Date: Mon, 16 Jun 2014 19:41:22 +0530 Subject: [PATCH 062/274] rsi: Add macros for endpoints and set default value of endpoint. Added macros for the endpoints and set the default value of endpoint to 2.4GHz and 20MHz. Signed-off-by: Jahnavi Meher Signed-off-by: John W. Linville --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 1 + drivers/net/wireless/rsi/rsi_mgmt.h | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 2eefbf159bc0..2cdb36a4239e 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -217,6 +217,7 @@ static void rsi_set_default_parameters(struct rsi_common *common) common->min_rate = 0xffff; common->fsm_state = FSM_CARD_NOT_READY; common->iface_down = true; + common->endpoint = EP_2GHZ_20MHZ; } /** diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index 225215a3b8bb..0641068d7fec 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -123,6 +123,11 @@ #define BW_20MHZ 0 #define BW_40MHZ 1 +#define EP_2GHZ_20MHZ 0 +#define EP_2GHZ_40MHZ 1 +#define EP_5GHZ_20MHZ 2 +#define EP_5GHZ_40MHZ 3 + #define RSI_SUPP_FILTERS (FIF_ALLMULTI | FIF_PROBE_REQ |\ FIF_BCN_PRBRESP_PROMISC) enum opmode { From 4550faac36f532b5600eea8a13f655f4fe39484a Mon Sep 17 00:00:00 2001 From: Jahnavi Meher Date: Mon, 16 Jun 2014 19:41:41 +0530 Subject: [PATCH 063/274] rsi: Changed the radio caps frame. Changed the radio caps frame and added the required fields. Signed-off-by: Jahnavi Meher Signed-off-by: John W. Linville --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 7 +++++++ drivers/net/wireless/rsi/rsi_mgmt.h | 15 +++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 2cdb36a4239e..c3d8da9b544a 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -331,6 +331,13 @@ static int rsi_load_radio_caps(struct rsi_common *common) } } + radio_caps->sifs_tx_11n = cpu_to_le16(SIFS_TX_11N_VALUE); + radio_caps->sifs_tx_11b = cpu_to_le16(SIFS_TX_11B_VALUE); + radio_caps->slot_rx_11n = cpu_to_le16(SHORT_SLOT_VALUE); + radio_caps->ofdm_ack_tout = cpu_to_le16(OFDM_ACK_TOUT_VALUE); + radio_caps->cck_ack_tout = cpu_to_le16(CCK_ACK_TOUT_VALUE); + radio_caps->preamble_type = cpu_to_le16(LONG_PREAMBLE); + radio_caps->desc_word[7] |= cpu_to_le16(radio_id << 8); for (ii = 0; ii < MAX_HW_QUEUES; ii++) { diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index 0641068d7fec..6ccf9d935e4b 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -128,6 +128,15 @@ #define EP_5GHZ_20MHZ 2 #define EP_5GHZ_40MHZ 3 +#define SIFS_TX_11N_VALUE 580 +#define SIFS_TX_11B_VALUE 346 +#define SHORT_SLOT_VALUE 360 +#define LONG_SLOT_VALUE 640 +#define OFDM_ACK_TOUT_VALUE 2720 +#define CCK_ACK_TOUT_VALUE 9440 +#define LONG_PREAMBLE 0x0000 +#define SHORT_PREAMBLE 0x0001 + #define RSI_SUPP_FILTERS (FIF_ALLMULTI | FIF_PROBE_REQ |\ FIF_BCN_PRBRESP_PROMISC) enum opmode { @@ -243,6 +252,12 @@ struct rsi_radio_caps { u8 num_11n_rates; u8 num_11ac_rates; __le16 gcpd_per_rate[20]; + __le16 sifs_tx_11n; + __le16 sifs_tx_11b; + __le16 slot_rx_11n; + __le16 ofdm_ack_tout; + __le16 cck_ack_tout; + __le16 preamble_type; } __packed; static inline u32 rsi_get_queueno(u8 *addr, u16 offset) From 8701d0312880fcddff757d153c7591d8caad4217 Mon Sep 17 00:00:00 2001 From: Jahnavi Meher Date: Mon, 16 Jun 2014 19:41:58 +0530 Subject: [PATCH 064/274] rsi: Changed the rsi_set_channel() and rsi_program_bb_rf(). Made required changes to rsi_set_channel() and rsi_program_bb_rf() functions. Signed-off-by: Jahnavi Meher Signed-off-by: John W. Linville --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index c3d8da9b544a..5c122e8cd526 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -596,7 +596,7 @@ static int rsi_program_bb_rf(struct rsi_common *common) mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); mgmt_frame->desc_word[1] = cpu_to_le16(BBP_PROG_IN_TA); - mgmt_frame->desc_word[4] = cpu_to_le16(common->endpoint << 8); + mgmt_frame->desc_word[4] = cpu_to_le16(common->endpoint); if (common->rf_reset) { mgmt_frame->desc_word[7] = cpu_to_le16(RF_RESET_ENABLE); @@ -849,23 +849,6 @@ int rsi_set_channel(struct rsi_common *common, u16 channel) rsi_dbg(MGMT_TX_ZONE, "%s: Sending scan req frame\n", __func__); - if (common->band == IEEE80211_BAND_5GHZ) { - if ((channel >= 36) && (channel <= 64)) - channel = ((channel - 32) / 4); - else if ((channel > 64) && (channel <= 140)) - channel = ((channel - 102) / 4) + 8; - else if (channel >= 149) - channel = ((channel - 151) / 4) + 18; - else - return -EINVAL; - } else { - if (channel > 14) { - rsi_dbg(ERR_ZONE, "%s: Invalid chno %d, band = %d\n", - __func__, channel, common->band); - return -EINVAL; - } - } - skb = dev_alloc_skb(FRAME_DESC_SZ); if (!skb) { rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", @@ -885,6 +868,7 @@ int rsi_set_channel(struct rsi_common *common, u16 channel) (RSI_RF_TYPE << 4)); mgmt_frame->desc_word[5] = cpu_to_le16(0x01); + mgmt_frame->desc_word[6] = cpu_to_le16(0x12); if (common->channel_width == BW_40MHZ) mgmt_frame->desc_word[5] |= cpu_to_le16(0x1 << 8); From 0d59f5267d93f71b3d0c3c80f4948065e77ee75c Mon Sep 17 00:00:00 2001 From: Jahnavi Meher Date: Mon, 16 Jun 2014 19:43:09 +0530 Subject: [PATCH 065/274] rsi: Changed rate handling. Changed rate handling. Signed-off-by: Jahnavi Meher Signed-off-by: John W. Linville --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 35 +++++++++++-------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 5c122e8cd526..f5182cd67f53 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -942,7 +942,7 @@ static int rsi_send_auto_rate_request(struct rsi_common *common) struct ieee80211_hw *hw = common->priv->hw; u8 band = hw->conf.chandef.chan->band; u8 num_supported_rates = 0; - u8 rate_offset = 0; + u8 rate_table_offset, rate_offset = 0; u32 rate_bitmap = common->bitrate_mask[band]; u16 *selected_rates, min_rate; @@ -978,14 +978,18 @@ static int rsi_send_auto_rate_request(struct rsi_common *common) if (common->channel_width == BW_40MHZ) auto_rate->desc_word[7] |= cpu_to_le16(1); - if (band == IEEE80211_BAND_2GHZ) - min_rate = STD_RATE_01; - else - min_rate = STD_RATE_06; + if (band == IEEE80211_BAND_2GHZ) { + min_rate = RSI_RATE_1; + rate_table_offset = 0; + } else { + min_rate = RSI_RATE_6; + rate_table_offset = 4; + } for (ii = 0, jj = 0; ii < ARRAY_SIZE(rsi_rates); ii++) { if (rate_bitmap & BIT(ii)) { - selected_rates[jj++] = (rsi_rates[ii].bitrate / 5); + selected_rates[jj++] = + (rsi_rates[ii + rate_table_offset].bitrate / 5); rate_offset++; } } @@ -998,13 +1002,6 @@ static int rsi_send_auto_rate_request(struct rsi_common *common) rate_offset += ARRAY_SIZE(mcs); } - if (rate_offset < (RSI_TBL_SZ / 2) - 1) { - for (ii = jj; ii < (RSI_TBL_SZ / 2); ii++) { - selected_rates[jj++] = min_rate; - rate_offset++; - } - } - sort(selected_rates, jj, sizeof(u16), &rsi_compare, NULL); /* mapping the rates to RSI rates */ @@ -1020,25 +1017,25 @@ static int rsi_send_auto_rate_request(struct rsi_common *common) /* loading HT rates in the bottom half of the auto rate table */ if (common->vif_info[0].is_ht) { - if (common->vif_info[0].sgi) - auto_rate->supported_rates[rate_offset++] = - cpu_to_le16(RSI_RATE_MCS7_SG); - for (ii = rate_offset, kk = ARRAY_SIZE(rsi_mcsrates) - 1; ii < rate_offset + 2 * ARRAY_SIZE(rsi_mcsrates); ii++) { - if (common->vif_info[0].sgi) + if (common->vif_info[0].sgi || + conf_is_ht40(&common->priv->hw->conf)) auto_rate->supported_rates[ii++] = cpu_to_le16(rsi_mcsrates[kk] | BIT(9)); auto_rate->supported_rates[ii] = cpu_to_le16(rsi_mcsrates[kk--]); } - for (; ii < RSI_TBL_SZ; ii++) { + for (; ii < (RSI_TBL_SZ - 1); ii++) { auto_rate->supported_rates[ii] = cpu_to_le16(rsi_mcsrates[0]); } } + for (; ii < RSI_TBL_SZ; ii++) + auto_rate->supported_rates[ii] = min_rate; + auto_rate->num_supported_rates = cpu_to_le16(num_supported_rates * 2); auto_rate->moderate_rate_inx = cpu_to_le16(num_supported_rates / 2); auto_rate->desc_word[7] |= cpu_to_le16(0 << 8); From be876b299e12bae2b38cba3f48324fe5913a250b Mon Sep 17 00:00:00 2001 From: Jahnavi Meher Date: Mon, 16 Jun 2014 19:43:35 +0530 Subject: [PATCH 066/274] rsi: Lower level debug messages and changed handling of confirm received for rsi_program_bb_rf(). Lower level debug messages for some frames and changed confirm received for rsi_program_bb_rf() to a valid case. Signed-off-by: Jahnavi Meher Signed-off-by: John W. Linville --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index f5182cd67f53..92f584e636d4 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -1153,7 +1153,7 @@ static int rsi_handle_ta_confirm_type(struct rsi_common *common, common->fsm_state = FSM_EEPROM_READ_MAC_ADDR; } } else { - rsi_dbg(ERR_ZONE, + rsi_dbg(INFO_ZONE, "%s: Received bootup params cfm in %d state\n", __func__, common->fsm_state); return 0; @@ -1216,7 +1216,7 @@ static int rsi_handle_ta_confirm_type(struct rsi_common *common, __func__); } } else { - rsi_dbg(ERR_ZONE, + rsi_dbg(INFO_ZONE, "%s: Received radio caps cfm in %d state\n", __func__, common->fsm_state); return 0; @@ -1234,7 +1234,10 @@ static int rsi_handle_ta_confirm_type(struct rsi_common *common, return rsi_mac80211_attach(common); } } else { - goto out; + rsi_dbg(INFO_ZONE, + "%s: Received bbb_rf cfm in %d state\n", + __func__, common->fsm_state); + return 0; } break; From 2bfa6969d9b332ce43d03d679bf178ecb19b23c0 Mon Sep 17 00:00:00 2001 From: Jahnavi Meher Date: Mon, 16 Jun 2014 19:43:54 +0530 Subject: [PATCH 067/274] rsi: Use SGI if configured for fixed rate transmission. Use SGI if configured while sending data packets at a fixed rate. Signed-off-by: Jahnavi Meher Signed-off-by: John W. Linville --- drivers/net/wireless/rsi/rsi_91x_pkt.c | 6 ++++++ drivers/net/wireless/rsi/rsi_mgmt.h | 1 + 2 files changed, 7 insertions(+) diff --git a/drivers/net/wireless/rsi/rsi_91x_pkt.c b/drivers/net/wireless/rsi/rsi_91x_pkt.c index 8e48e72bae20..229ef3aeaaaf 100644 --- a/drivers/net/wireless/rsi/rsi_91x_pkt.c +++ b/drivers/net/wireless/rsi/rsi_91x_pkt.c @@ -81,6 +81,12 @@ int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb) /* Send fixed rate */ frame_desc[3] = cpu_to_le16(RATE_INFO_ENABLE); frame_desc[4] = cpu_to_le16(common->min_rate); + if (common->vif_info[0].sgi) { + if (common->min_rate & 0x100) /* Only MCS rates */ + frame_desc[4] |= + cpu_to_le16(ENABLE_SHORTGI_RATE); + } + } frame_desc[6] |= cpu_to_le16(seq_num & 0xfff); diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index 6ccf9d935e4b..8bff6640dd10 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -69,6 +69,7 @@ #define RSI_LMAC_CLOCK_80MHZ 0x1 #define RSI_ENABLE_40MHZ (0x1 << 3) +#define ENABLE_SHORTGI_RATE BIT(9) #define RX_BA_INDICATION 1 #define RSI_TBL_SZ 40 From f75d3419ec2579929a29c4b3b0a7b790c6f6ae24 Mon Sep 17 00:00:00 2001 From: Jahnavi Meher Date: Mon, 16 Jun 2014 19:44:12 +0530 Subject: [PATCH 068/274] rsi: Changed the SDIO interrupt variables and some clean up. Changed the SDIO interrupt variables and some clean ups. Signed-off-by: Jahnavi Meher Signed-off-by: John W. Linville --- drivers/net/wireless/rsi/rsi_91x_sdio_ops.c | 8 +++++--- drivers/net/wireless/rsi/rsi_sdio.h | 8 ++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c index 20d11ccfffe3..4834a9abc171 100644 --- a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c +++ b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c @@ -401,14 +401,16 @@ void rsi_interrupt_handler(struct rsi_hw *adapter) case BUFFER_AVAILABLE: dev->rx_info.watch_bufferfull_count = 0; dev->rx_info.buffer_full = false; + dev->rx_info.semi_buffer_full = false; dev->rx_info.mgmt_buffer_full = false; rsi_sdio_ack_intr(common->priv, (1 << PKT_BUFF_AVAILABLE)); - rsi_set_event((&common->tx_thread.event)); + rsi_set_event(&common->tx_thread.event); + rsi_dbg(ISR_ZONE, - "%s: ==> BUFFER_AVILABLE <==\n", + "%s: ==> BUFFER_AVAILABLE <==\n", __func__); - dev->rx_info.buf_avilable_counter++; + dev->rx_info.buf_available_counter++; break; case FIRMWARE_ASSERT_IND: diff --git a/drivers/net/wireless/rsi/rsi_sdio.h b/drivers/net/wireless/rsi/rsi_sdio.h index df4b5e20e05f..c7e8f2be7901 100644 --- a/drivers/net/wireless/rsi/rsi_sdio.h +++ b/drivers/net/wireless/rsi/rsi_sdio.h @@ -30,7 +30,7 @@ enum sdio_interrupt_type { BUFFER_FULL = 0x0, - BUFFER_AVAILABLE = 0x1, + BUFFER_AVAILABLE = 0x2, FIRMWARE_ASSERT_IND = 0x3, MSDU_PACKET_PENDING = 0x4, UNKNOWN_INT = 0XE @@ -42,7 +42,7 @@ enum sdio_interrupt_type { #define PKT_MGMT_BUFF_FULL 2 #define MSDU_PKT_PENDING 3 /* Interrupt Bit Related Macros */ -#define PKT_BUFF_AVAILABLE 0 +#define PKT_BUFF_AVAILABLE 1 #define FW_ASSERT_IND 2 #define RSI_DEVICE_BUFFER_STATUS_REGISTER 0xf3 @@ -84,7 +84,7 @@ enum sdio_interrupt_type { #define TA_HOLD_THREAD_VALUE cpu_to_le32(0xF) #define TA_RELEASE_THREAD_VALUE cpu_to_le32(0xF) #define TA_BASE_ADDR 0x2200 -#define MISC_CFG_BASE_ADDR 0x4150 +#define MISC_CFG_BASE_ADDR 0x4105 struct receive_info { bool buffer_full; @@ -98,7 +98,7 @@ struct receive_info { u32 total_sdio_msdu_pending_intr; u32 total_sdio_unknown_intr; u32 buf_full_counter; - u32 buf_avilable_counter; + u32 buf_available_counter; }; struct rsi_91x_sdiodev { From 360accb0dba1777a4ea85a637c816b7987fa947b Mon Sep 17 00:00:00 2001 From: Jahnavi Meher Date: Mon, 16 Jun 2014 19:45:03 +0530 Subject: [PATCH 069/274] rsi: Changed the logic of dequeuing packets from hal queues. The number of packets being dequeued from s/w queues was fixed - changed it to a dynamic calculation based on txop. There are also some fixes to the dequeuing algorithm. Signed-off-by: Jahnavi Meher Signed-off-by: John W. Linville --- drivers/net/wireless/rsi/rsi_91x_core.c | 80 +++++++++++++++++-------- drivers/net/wireless/rsi/rsi_main.h | 3 + 2 files changed, 59 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_core.c b/drivers/net/wireless/rsi/rsi_91x_core.c index cf61d6e3eaa7..264e8fac83ff 100644 --- a/drivers/net/wireless/rsi/rsi_91x_core.c +++ b/drivers/net/wireless/rsi/rsi_91x_core.c @@ -76,6 +76,50 @@ static bool rsi_recalculate_weights(struct rsi_common *common) return recontend_queue; } +/** + * rsi_get_num_pkts_dequeue() - This function determines the number of + * packets to be dequeued based on the number + * of bytes calculated using txop. + * + * @common: Pointer to the driver private structure. + * @q_num: the queue from which pkts have to be dequeued + * + * Return: pkt_num: Number of pkts to be dequeued. + */ +static u32 rsi_get_num_pkts_dequeue(struct rsi_common *common, u8 q_num) +{ + struct rsi_hw *adapter = common->priv; + struct sk_buff *skb; + u32 pkt_cnt = 0; + s16 txop = common->tx_qinfo[q_num].txop * 32; + struct ieee80211_rate rate; + + rate.bitrate = RSI_RATE_MCS0 * 5 * 10; /* Convert to Kbps */ + if (q_num == VI_Q) + txop = ((txop << 5) / 80); + + if (skb_queue_len(&common->tx_queue[q_num])) + skb = skb_peek(&common->tx_queue[q_num]); + else + return 0; + + do { + txop -= ieee80211_generic_frame_duration(adapter->hw, + adapter->vifs[0], + common->band, + skb->len, &rate); + pkt_cnt += 1; + /*checking if pkts are still there*/ + if (skb_queue_len(&common->tx_queue[q_num]) - pkt_cnt) + skb = skb->next; + else + break; + + } while (txop > 0); + + return pkt_cnt; +} + /** * rsi_core_determine_hal_queue() - This function determines the queue from * which packet has to be dequeued. @@ -88,7 +132,7 @@ static u8 rsi_core_determine_hal_queue(struct rsi_common *common) bool recontend_queue = false; u32 q_len = 0; u8 q_num = INVALID_QUEUE; - u8 ii = 0, min = 0; + u8 ii = 0; if (skb_queue_len(&common->tx_queue[MGMT_SOFT_Q])) { if (!common->mgmt_q_block) @@ -96,6 +140,9 @@ static u8 rsi_core_determine_hal_queue(struct rsi_common *common) return q_num; } + if (common->hw_data_qs_blocked) + return q_num; + if (common->pkt_cnt != 0) { --common->pkt_cnt; return common->selected_qnum; @@ -106,14 +153,15 @@ get_queue_num: q_num = rsi_determine_min_weight_queue(common); - q_len = skb_queue_len(&common->tx_queue[ii]); ii = q_num; /* Selecting the queue with least back off */ for (; ii < NUM_EDCA_QUEUES; ii++) { + q_len = skb_queue_len(&common->tx_queue[ii]); if (((common->tx_qinfo[ii].pkt_contended) && - (common->tx_qinfo[ii].weight < min)) && q_len) { - min = common->tx_qinfo[ii].weight; + (common->tx_qinfo[ii].weight < common->min_weight)) && + q_len) { + common->min_weight = common->tx_qinfo[ii].weight; q_num = ii; } } @@ -140,26 +188,10 @@ get_queue_num: common->selected_qnum = q_num; q_len = skb_queue_len(&common->tx_queue[q_num]); - switch (common->selected_qnum) { - case VO_Q: - if (q_len > MAX_CONTINUOUS_VO_PKTS) - common->pkt_cnt = (MAX_CONTINUOUS_VO_PKTS - 1); - else - common->pkt_cnt = --q_len; - break; - - case VI_Q: - if (q_len > MAX_CONTINUOUS_VI_PKTS) - common->pkt_cnt = (MAX_CONTINUOUS_VI_PKTS - 1); - else - common->pkt_cnt = --q_len; - - break; - - default: - common->pkt_cnt = 0; - break; - } + if (q_num == VO_Q || q_num == VI_Q) { + common->pkt_cnt = rsi_get_num_pkts_dequeue(common, q_num); + common->pkt_cnt -= 1; + }; return q_num; } diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h index 2cb73e7edb98..86291310e293 100644 --- a/drivers/net/wireless/rsi/rsi_main.h +++ b/drivers/net/wireless/rsi/rsi_main.h @@ -115,6 +115,7 @@ struct wmm_qinfo { s32 weight; s32 wme_params; s32 pkt_contended; + s32 txop; }; struct transmit_q_stats { @@ -192,6 +193,8 @@ struct rsi_common { u8 selected_qnum; u32 pkt_cnt; u8 min_weight; + + bool hw_data_qs_blocked; }; struct rsi_hw { From 258587f913f48f45b7e810d700ce1300afdb97da Mon Sep 17 00:00:00 2001 From: Jahnavi Meher Date: Mon, 16 Jun 2014 19:45:45 +0530 Subject: [PATCH 070/274] rsi: Added debug messages. Added some debug messages. Signed-off-by: Jahnavi Meher Signed-off-by: John W. Linville --- drivers/net/wireless/rsi/rsi_91x_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/rsi/rsi_91x_core.c b/drivers/net/wireless/rsi/rsi_91x_core.c index 264e8fac83ff..6f6a4d0b5b1f 100644 --- a/drivers/net/wireless/rsi/rsi_91x_core.c +++ b/drivers/net/wireless/rsi/rsi_91x_core.c @@ -284,6 +284,7 @@ void rsi_core_qos_processor(struct rsi_common *common) skb = rsi_core_dequeue_pkt(common, q_num); if (skb == NULL) { + rsi_dbg(ERR_ZONE, "skb null\n"); mutex_unlock(&common->tx_rxlock); break; } @@ -357,6 +358,7 @@ void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb) if ((q_num != MGMT_SOFT_Q) && ((skb_queue_len(&common->tx_queue[q_num]) + 1) >= DATA_QUEUE_WATER_MARK)) { + rsi_dbg(ERR_ZONE, "%s: sw queue full\n", __func__); if (!ieee80211_queue_stopped(adapter->hw, WME_AC(q_num))) ieee80211_stop_queue(adapter->hw, WME_AC(q_num)); rsi_set_event(&common->tx_thread.event); From 7b748dc0ed95cfee7962c5359299e4f305989769 Mon Sep 17 00:00:00 2001 From: Jahnavi Meher Date: Mon, 16 Jun 2014 19:46:13 +0530 Subject: [PATCH 071/274] rsi: Sending QoS null packet via the mgmt queue. Send the QoS null packet via mgmt queue. Signed-off-by: Jahnavi Meher Signed-off-by: John W. Linville --- drivers/net/wireless/rsi/rsi_91x_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_core.c b/drivers/net/wireless/rsi/rsi_91x_core.c index 6f6a4d0b5b1f..1cd0914db270 100644 --- a/drivers/net/wireless/rsi/rsi_91x_core.c +++ b/drivers/net/wireless/rsi/rsi_91x_core.c @@ -339,7 +339,8 @@ void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb) } if ((ieee80211_is_mgmt(tmp_hdr->frame_control)) || - (ieee80211_is_ctl(tmp_hdr->frame_control))) { + (ieee80211_is_ctl(tmp_hdr->frame_control)) || + (ieee80211_is_qos_nullfunc(tmp_hdr->frame_control))) { q_num = MGMT_SOFT_Q; skb->priority = q_num; } else { From 85af5bf829813df5571779e795b73ee498173945 Mon Sep 17 00:00:00 2001 From: Jahnavi Meher Date: Mon, 16 Jun 2014 19:46:31 +0530 Subject: [PATCH 072/274] rsi: Adding support for 5GHz Adding support for 5GHz. Signed-off-by: Jahnavi Meher Signed-off-by: John W. Linville --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 17 +++-- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 81 +++++++++++++++++++-- drivers/net/wireless/rsi/rsi_mgmt.h | 1 + 3 files changed, 87 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 45bdb9953755..4700714e600f 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -723,17 +723,17 @@ static int rsi_mac80211_set_rate_mask(struct ieee80211_hw *hw, { struct rsi_hw *adapter = hw->priv; struct rsi_common *common = adapter->priv; + enum ieee80211_band band = hw->conf.chandef.chan->band; mutex_lock(&common->mutex); + common->fixedrate_mask[band] = 0; - common->fixedrate_mask[IEEE80211_BAND_2GHZ] = 0; - - if (mask->control[IEEE80211_BAND_2GHZ].legacy == 0xfff) { - common->fixedrate_mask[IEEE80211_BAND_2GHZ] = - (mask->control[IEEE80211_BAND_2GHZ].ht_mcs[0] << 12); + if (mask->control[band].legacy == 0xfff) { + common->fixedrate_mask[band] = + (mask->control[band].ht_mcs[0] << 12); } else { - common->fixedrate_mask[IEEE80211_BAND_2GHZ] = - mask->control[IEEE80211_BAND_2GHZ].legacy; + common->fixedrate_mask[band] = + mask->control[band].legacy; } mutex_unlock(&common->mutex); @@ -980,6 +980,7 @@ int rsi_mac80211_attach(struct rsi_common *common) hw->max_tx_aggregation_subframes = 6; rsi_register_rates_channels(adapter, IEEE80211_BAND_2GHZ); + rsi_register_rates_channels(adapter, IEEE80211_BAND_5GHZ); hw->rate_control_algorithm = "AARF"; SET_IEEE80211_PERM_ADDR(hw, common->mac_addr); @@ -997,6 +998,8 @@ int rsi_mac80211_attach(struct rsi_common *common) wiphy->available_antennas_tx = 1; wiphy->bands[IEEE80211_BAND_2GHZ] = &adapter->sbands[IEEE80211_BAND_2GHZ]; + wiphy->bands[IEEE80211_BAND_5GHZ] = + &adapter->sbands[IEEE80211_BAND_5GHZ]; status = ieee80211_register_hw(hw); if (status) diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 92f584e636d4..cbd5a7e7c73c 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -623,6 +623,9 @@ int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode) { struct sk_buff *skb = NULL; struct rsi_vap_caps *vap_caps; + struct rsi_hw *adapter = common->priv; + struct ieee80211_hw *hw = adapter->hw; + struct ieee80211_conf *conf = &hw->conf; u16 vap_id = 0; rsi_dbg(MGMT_TX_ZONE, "%s: Sending VAP capabilities frame\n", __func__); @@ -652,13 +655,24 @@ int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode) vap_caps->frag_threshold = cpu_to_le16(IEEE80211_MAX_FRAG_THRESHOLD); vap_caps->rts_threshold = cpu_to_le16(common->rts_threshold); - vap_caps->default_mgmt_rate = 0; - if (conf_is_ht40(&common->priv->hw->conf)) { - vap_caps->default_ctrl_rate = - cpu_to_le32(RSI_RATE_6 | FULL40M_ENABLE << 16); - } else { + vap_caps->default_mgmt_rate = cpu_to_le32(RSI_RATE_6); + + if (common->band == IEEE80211_BAND_5GHZ) { vap_caps->default_ctrl_rate = cpu_to_le32(RSI_RATE_6); + if (conf_is_ht40(&common->priv->hw->conf)) { + vap_caps->default_ctrl_rate |= + cpu_to_le32(FULL40M_ENABLE << 16); + } + } else { + vap_caps->default_ctrl_rate = cpu_to_le32(RSI_RATE_1); + if (conf_is_ht40_minus(conf)) + vap_caps->default_ctrl_rate |= + cpu_to_le32(UPPER_20_ENABLE << 16); + else if (conf_is_ht40_plus(conf)) + vap_caps->default_ctrl_rate |= + cpu_to_le32(LOWER_20_ENABLE << 16); } + vap_caps->default_data_rate = 0; vap_caps->beacon_interval = cpu_to_le16(200); vap_caps->dtim_period = cpu_to_le16(4); @@ -834,6 +848,63 @@ static int rsi_send_reset_mac(struct rsi_common *common) return rsi_send_internal_mgmt_frame(common, skb); } +/** + * rsi_band_check() - This function programs the band + * @common: Pointer to the driver private structure. + * + * Return: 0 on success, corresponding error code on failure. + */ +int rsi_band_check(struct rsi_common *common) +{ + struct rsi_hw *adapter = common->priv; + struct ieee80211_hw *hw = adapter->hw; + u8 prev_bw = common->channel_width; + u8 prev_ep = common->endpoint; + struct ieee80211_channel *curchan = hw->conf.chandef.chan; + int status = 0; + + if (common->band != curchan->band) { + common->rf_reset = 1; + common->band = curchan->band; + } + + if ((hw->conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) || + (hw->conf.chandef.width == NL80211_CHAN_WIDTH_20)) + common->channel_width = BW_20MHZ; + else + common->channel_width = BW_40MHZ; + + if (common->band == IEEE80211_BAND_2GHZ) { + if (common->channel_width) + common->endpoint = EP_2GHZ_40MHZ; + else + common->endpoint = EP_2GHZ_20MHZ; + } else { + if (common->channel_width) + common->endpoint = EP_5GHZ_40MHZ; + else + common->endpoint = EP_5GHZ_20MHZ; + } + + if (common->endpoint != prev_ep) { + status = rsi_program_bb_rf(common); + if (status) + return status; + } + + if (common->channel_width != prev_bw) { + status = rsi_load_bootup_params(common); + if (status) + return status; + + status = rsi_load_radio_caps(common); + if (status) + return status; + } + + return status; +} + /** * rsi_set_channel() - This function programs the channel. * @common: Pointer to the driver private structure. diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index 8bff6640dd10..f10d6c67e82f 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -304,4 +304,5 @@ void rsi_core_qos_processor(struct rsi_common *common); void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb); int rsi_send_mgmt_pkt(struct rsi_common *common, struct sk_buff *skb); int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb); +int rsi_band_check(struct rsi_common *common); #endif From 686a254177929cf82bc34af0944906e6866e393a Mon Sep 17 00:00:00 2001 From: Jahnavi Meher Date: Mon, 16 Jun 2014 19:46:48 +0530 Subject: [PATCH 073/274] rsi: Adding support for host based bgscan. Added support for host based bgscan. The h/w queues are blocked while bgscan is being performed and after coming to the connected channel, the queues are unblocked. Signed-off-by: Jahnavi Meher Signed-off-by: John W. Linville --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 115 ++++++++++++++++++-- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 43 ++++++++ drivers/net/wireless/rsi/rsi_main.h | 9 ++ drivers/net/wireless/rsi/rsi_mgmt.h | 3 +- 4 files changed, 159 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 4700714e600f..1cb316417887 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -340,6 +340,59 @@ static void rsi_mac80211_remove_interface(struct ieee80211_hw *hw, mutex_unlock(&common->mutex); } +/** + * rsi_channel_change() - This function is a performs the checks + * required for changing a channel and sets + * the channel accordingly. + * @hw: Pointer to the ieee80211_hw structure. + * + * Return: 0 on success, negative error code on failure. + */ +static int rsi_channel_change(struct ieee80211_hw *hw) +{ + struct rsi_hw *adapter = hw->priv; + struct rsi_common *common = adapter->priv; + int status = -EOPNOTSUPP; + struct ieee80211_channel *curchan = hw->conf.chandef.chan; + u16 channel = curchan->hw_value; + struct ieee80211_bss_conf *bss = &adapter->vifs[0]->bss_conf; + + rsi_dbg(INFO_ZONE, + "%s: Set channel: %d MHz type: %d channel_no %d\n", + __func__, curchan->center_freq, + curchan->flags, channel); + + if (bss->assoc) { + if (!common->hw_data_qs_blocked && + (rsi_get_connected_channel(adapter) != channel)) { + rsi_dbg(INFO_ZONE, "blk data q %d\n", channel); + if (!rsi_send_block_unblock_frame(common, true)) + common->hw_data_qs_blocked = true; + } + } + + status = rsi_band_check(common); + if (!status) + status = rsi_set_channel(adapter->priv, channel); + + if (bss->assoc) { + if (common->hw_data_qs_blocked && + (rsi_get_connected_channel(adapter) == channel)) { + rsi_dbg(INFO_ZONE, "unblk data q %d\n", channel); + if (!rsi_send_block_unblock_frame(common, false)) + common->hw_data_qs_blocked = false; + } + } else { + if (common->hw_data_qs_blocked) { + rsi_dbg(INFO_ZONE, "unblk data q %d\n", channel); + if (!rsi_send_block_unblock_frame(common, false)) + common->hw_data_qs_blocked = false; + } + } + + return status; +} + /** * rsi_mac80211_config() - This function is a handler for configuration * requests. The stack calls this function to @@ -357,17 +410,10 @@ static int rsi_mac80211_config(struct ieee80211_hw *hw, int status = -EOPNOTSUPP; mutex_lock(&common->mutex); - if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { - struct ieee80211_channel *curchan = hw->conf.chandef.chan; - u16 channel = curchan->hw_value; - rsi_dbg(INFO_ZONE, - "%s: Set channel: %d MHz type: %d channel_no %d\n", - __func__, curchan->center_freq, - curchan->flags, channel); - common->band = curchan->band; - status = rsi_set_channel(adapter->priv, channel); - } + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) + status = rsi_channel_change(hw); + mutex_unlock(&common->mutex); return status; @@ -421,6 +467,15 @@ static void rsi_mac80211_bss_info_changed(struct ieee80211_hw *hw, bss_conf->qos, bss_conf->aid); } + + if (changed & BSS_CHANGED_CQM) { + common->cqm_info.last_cqm_event_rssi = 0; + common->cqm_info.rssi_thold = bss_conf->cqm_rssi_thold; + common->cqm_info.rssi_hyst = bss_conf->cqm_rssi_hyst; + rsi_dbg(INFO_ZONE, "RSSI throld & hysteresis are: %d %d\n", + common->cqm_info.rssi_thold, + common->cqm_info.rssi_hyst); + } mutex_unlock(&common->mutex); } @@ -740,6 +795,37 @@ static int rsi_mac80211_set_rate_mask(struct ieee80211_hw *hw, return 0; } +/** + * rsi_perform_cqm() - This function performs cqm. + * @common: Pointer to the driver private structure. + * @bssid: pointer to the bssid. + * @rssi: RSSI value. + */ +static void rsi_perform_cqm(struct rsi_common *common, + u8 *bssid, + s8 rssi) +{ + struct rsi_hw *adapter = common->priv; + s8 last_event = common->cqm_info.last_cqm_event_rssi; + int thold = common->cqm_info.rssi_thold; + u32 hyst = common->cqm_info.rssi_hyst; + enum nl80211_cqm_rssi_threshold_event event; + + if (rssi < thold && (last_event == 0 || rssi < (last_event - hyst))) + event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW; + else if (rssi > thold && + (last_event == 0 || rssi > (last_event + hyst))) + event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; + else + return; + + common->cqm_info.last_cqm_event_rssi = rssi; + rsi_dbg(INFO_ZONE, "CQM: Notifying event: %d\n", event); + ieee80211_cqm_rssi_notify(adapter->vifs[0], event, GFP_KERNEL); + + return; +} + /** * rsi_fill_rx_status() - This function fills rx status in * ieee80211_rx_status structure. @@ -755,6 +841,7 @@ static void rsi_fill_rx_status(struct ieee80211_hw *hw, struct rsi_common *common, struct ieee80211_rx_status *rxs) { + struct ieee80211_bss_conf *bss = &common->priv->vifs[0]->bss_conf; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct skb_info *rx_params = (struct skb_info *)info->driver_data; struct ieee80211_hdr *hdr; @@ -789,6 +876,14 @@ static void rsi_fill_rx_status(struct ieee80211_hw *hw, rxs->flag |= RX_FLAG_DECRYPTED; rxs->flag |= RX_FLAG_IV_STRIPPED; } + + /* CQM only for connected AP beacons, the RSSI is a weighted avg */ + if (bss->assoc && !(memcmp(bss->bssid, hdr->addr2, ETH_ALEN))) { + if (ieee80211_is_beacon(hdr->frame_control)) + rsi_perform_cqm(common, hdr->addr2, rxs->signal); + } + + return; } /** diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index cbd5a7e7c73c..3ef343469c4e 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -1200,6 +1200,49 @@ static int rsi_eeprom_read(struct rsi_common *common) return rsi_send_internal_mgmt_frame(common, skb); } +/** + * This function sends a frame to block/unblock + * data queues in the firmware + * + * @param common Pointer to the driver private structure. + * @param block event - block if true, unblock if false + * @return 0 on success, -1 on failure. + */ +int rsi_send_block_unblock_frame(struct rsi_common *common, bool block_event) +{ + struct rsi_mac_frame *mgmt_frame; + struct sk_buff *skb; + + rsi_dbg(MGMT_TX_ZONE, "%s: Sending block/unblock frame\n", __func__); + + skb = dev_alloc_skb(FRAME_DESC_SZ); + if (!skb) { + rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", + __func__); + return -ENOMEM; + } + + memset(skb->data, 0, FRAME_DESC_SZ); + mgmt_frame = (struct rsi_mac_frame *)skb->data; + + mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); + mgmt_frame->desc_word[1] = cpu_to_le16(BLOCK_HW_QUEUE); + + if (block_event == true) { + rsi_dbg(INFO_ZONE, "blocking the data qs\n"); + mgmt_frame->desc_word[4] = cpu_to_le16(0xf); + } else { + rsi_dbg(INFO_ZONE, "unblocking the data qs\n"); + mgmt_frame->desc_word[5] = cpu_to_le16(0xf); + } + + skb_put(skb, FRAME_DESC_SZ); + + return rsi_send_internal_mgmt_frame(common, skb); + +} + + /** * rsi_handle_ta_confirm_type() - This function handles the confirm frames. * @common: Pointer to the driver private structure. diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h index 86291310e293..5baed945f60e 100644 --- a/drivers/net/wireless/rsi/rsi_main.h +++ b/drivers/net/wireless/rsi/rsi_main.h @@ -142,6 +142,12 @@ struct rsi_thread { atomic_t thread_done; }; +struct cqm_info { + s8 last_cqm_event_rssi; + int rssi_thold; + u32 rssi_hyst; +}; + struct rsi_hw; struct rsi_common { @@ -194,6 +200,9 @@ struct rsi_common { u32 pkt_cnt; u8 min_weight; + /* bgscan related */ + struct cqm_info cqm_info; + bool hw_data_qs_blocked; }; diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index f10d6c67e82f..3741173fd3ac 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -168,7 +168,7 @@ enum cmd_frame_type { SCAN_REQUEST, TSF_UPDATE, PEER_NOTIFY, - BLOCK_UNBLOCK, + BLOCK_HW_QUEUE, SET_KEY_REQ, AUTO_RATE_IND, BOOTUP_PARAMS_REQUEST, @@ -293,6 +293,7 @@ int rsi_send_aggregation_params_frame(struct rsi_common *common, u16 tid, int rsi_hal_load_key(struct rsi_common *common, u8 *data, u16 key_len, u8 key_type, u8 key_id, u32 cipher); int rsi_set_channel(struct rsi_common *common, u16 chno); +int rsi_send_block_unblock_frame(struct rsi_common *common, bool event); void rsi_inform_bss_status(struct rsi_common *common, u8 status, const u8 *bssid, u8 qos_enable, u16 aid); void rsi_indicate_pkt_to_os(struct rsi_common *common, struct sk_buff *skb); From e8c58e7a5a106c3d557fccd01cd4d1128f9bab38 Mon Sep 17 00:00:00 2001 From: Jahnavi Meher Date: Mon, 16 Jun 2014 19:47:03 +0530 Subject: [PATCH 074/274] rsi: Changes for 40MHz Added code required for 40MHz. Signed-off-by: Jahnavi Meher Signed-off-by: John W. Linville --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 2 +- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 40 ++++++++++----------- drivers/net/wireless/rsi/rsi_91x_pkt.c | 11 ++++++ 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 1cb316417887..aeaf87bb5518 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -177,7 +177,7 @@ static void rsi_register_rates_channels(struct rsi_hw *adapter, int band) sbands->ht_cap.cap = (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40); - sbands->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K; + sbands->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K; sbands->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; sbands->ht_cap.mcs.rx_mask[0] = 0xff; sbands->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 3ef343469c4e..83abe580cf5f 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -277,7 +277,6 @@ static int rsi_load_radio_caps(struct rsi_common *common) { struct rsi_radio_caps *radio_caps; struct rsi_hw *adapter = common->priv; - struct ieee80211_hw *hw = adapter->hw; u16 inx = 0; u8 ii; u8 radio_id = 0; @@ -286,7 +285,6 @@ static int rsi_load_radio_caps(struct rsi_common *common) 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0}; - struct ieee80211_conf *conf = &hw->conf; struct sk_buff *skb; rsi_dbg(INFO_ZONE, "%s: Sending rate symbol req frame\n", __func__); @@ -308,26 +306,26 @@ static int rsi_load_radio_caps(struct rsi_common *common) if (common->channel_width == BW_40MHZ) { radio_caps->desc_word[7] |= cpu_to_le16(RSI_LMAC_CLOCK_80MHZ); radio_caps->desc_word[7] |= cpu_to_le16(RSI_ENABLE_40MHZ); - if (common->channel_width) { - radio_caps->desc_word[5] = - cpu_to_le16(common->channel_width << 12); - radio_caps->desc_word[5] |= cpu_to_le16(FULL40M_ENABLE); - } - if (conf_is_ht40_minus(conf)) { - radio_caps->desc_word[5] = 0; - radio_caps->desc_word[5] |= - cpu_to_le16(LOWER_20_ENABLE); - radio_caps->desc_word[5] |= - cpu_to_le16(LOWER_20_ENABLE >> 12); - } - - if (conf_is_ht40_plus(conf)) { - radio_caps->desc_word[5] = 0; - radio_caps->desc_word[5] |= - cpu_to_le16(UPPER_20_ENABLE); - radio_caps->desc_word[5] |= - cpu_to_le16(UPPER_20_ENABLE >> 12); + if (common->fsm_state == FSM_MAC_INIT_DONE) { + struct ieee80211_hw *hw = adapter->hw; + struct ieee80211_conf *conf = &hw->conf; + if (conf_is_ht40_plus(conf)) { + radio_caps->desc_word[5] = + cpu_to_le16(LOWER_20_ENABLE); + radio_caps->desc_word[5] |= + cpu_to_le16(LOWER_20_ENABLE >> 12); + } else if (conf_is_ht40_minus(conf)) { + radio_caps->desc_word[5] = + cpu_to_le16(UPPER_20_ENABLE); + radio_caps->desc_word[5] |= + cpu_to_le16(UPPER_20_ENABLE >> 12); + } else { + radio_caps->desc_word[5] = + cpu_to_le16(BW_40MHZ << 12); + radio_caps->desc_word[5] |= + cpu_to_le16(FULL40M_ENABLE); + } } } diff --git a/drivers/net/wireless/rsi/rsi_91x_pkt.c b/drivers/net/wireless/rsi/rsi_91x_pkt.c index 229ef3aeaaaf..702593f19997 100644 --- a/drivers/net/wireless/rsi/rsi_91x_pkt.c +++ b/drivers/net/wireless/rsi/rsi_91x_pkt.c @@ -81,6 +81,10 @@ int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb) /* Send fixed rate */ frame_desc[3] = cpu_to_le16(RATE_INFO_ENABLE); frame_desc[4] = cpu_to_le16(common->min_rate); + + if (conf_is_ht40(&common->priv->hw->conf)) + frame_desc[5] = cpu_to_le16(FULL40M_ENABLE); + if (common->vif_info[0].sgi) { if (common->min_rate & 0x100) /* Only MCS rates */ frame_desc[4] |= @@ -122,6 +126,8 @@ int rsi_send_mgmt_pkt(struct rsi_common *common, struct ieee80211_hdr *wh = NULL; struct ieee80211_tx_info *info; struct ieee80211_bss_conf *bss = NULL; + struct ieee80211_hw *hw = adapter->hw; + struct ieee80211_conf *conf = &hw->conf; struct skb_info *tx_params; int status = -E2BIG; __le16 *msg = NULL; @@ -181,6 +187,11 @@ int rsi_send_mgmt_pkt(struct rsi_common *common, else msg[4] = cpu_to_le16((RSI_RATE_6 & 0x0f) | RSI_11G_MODE); + if (conf_is_ht40(conf)) { + msg[4] = cpu_to_le16(0xB | RSI_11G_MODE); + msg[5] = cpu_to_le16(0x6); + } + /* Indicate to firmware to give cfm */ if ((skb->data[16] == IEEE80211_STYPE_PROBE_REQ) && (!bss->assoc)) { msg[1] |= cpu_to_le16(BIT(10)); From 0b39aaf2f2035b1c42b805a786a8b42f7501b82f Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:36:59 +0300 Subject: [PATCH 075/274] wil6210: Tx mgmt frame from debugfs Provide 2 files on the debugfs: - "rxon": write channel (1..4) to open Rx on it, 0 to rxoff - "tx_mgmt": write binary frame, starting from MAC header one need to care about turning receiver on/off before/after tx_mgmt Correct sequence is: echo $channel > rxon cat mfmt_frame > tx_mgmt echo 0 > rxon Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/cfg80211.c | 7 +- drivers/net/wireless/ath/wil6210/debugfs.c | 80 +++++++++++++++++++++ drivers/net/wireless/ath/wil6210/wil6210.h | 3 + 3 files changed, 86 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 820d4ebd9322..1725dfca1d8c 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -443,10 +443,9 @@ static int wil_cfg80211_disconnect(struct wiphy *wiphy, return rc; } -static int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, - struct wireless_dev *wdev, - struct cfg80211_mgmt_tx_params *params, - u64 *cookie) +int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_mgmt_tx_params *params, + u64 *cookie) { const u8 *buf = params->buf; size_t len = params->len; diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 8d4bc4bfb664..9d5db0472f4b 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -397,6 +397,84 @@ static const struct file_operations fops_reset = { .write = wil_write_file_reset, .open = simple_open, }; +/*---write channel 1..4 to rxon for it, 0 to rxoff---*/ +static ssize_t wil_write_file_rxon(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + struct wil6210_priv *wil = file->private_data; + int rc; + long channel; + bool on; + + char *kbuf = kmalloc(len + 1, GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + if (copy_from_user(kbuf, buf, len)) + return -EIO; + + kbuf[len] = '\0'; + rc = kstrtol(kbuf, 0, &channel); + kfree(kbuf); + if (rc) + return rc; + + if ((channel < 0) || (channel > 4)) { + wil_err(wil, "Invalid channel %ld\n", channel); + return -EINVAL; + } + on = !!channel; + + if (on) { + rc = wmi_set_channel(wil, (int)channel); + if (rc) + return rc; + } + + rc = wmi_rxon(wil, on); + if (rc) + return rc; + + return len; +} + +static const struct file_operations fops_rxon = { + .write = wil_write_file_rxon, + .open = simple_open, +}; +/*---tx_mgmt---*/ +/* Write mgmt frame to this file to send it */ +static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + struct wil6210_priv *wil = file->private_data; + struct wiphy *wiphy = wil_to_wiphy(wil); + struct wireless_dev *wdev = wil_to_wdev(wil); + struct cfg80211_mgmt_tx_params params; + int rc; + + void *frame = kmalloc(len, GFP_KERNEL); + if (!frame) + return -ENOMEM; + + if (copy_from_user(frame, buf, len)) + return -EIO; + + params.buf = frame; + params.len = len; + params.chan = wdev->preset_chandef.chan; + + rc = wil_cfg80211_mgmt_tx(wiphy, wdev, ¶ms, NULL); + + kfree(frame); + wil_info(wil, "%s() -> %d\n", __func__, rc); + + return len; +} + +static const struct file_operations fops_txmgmt = { + .write = wil_write_file_txmgmt, + .open = simple_open, +}; static void wil_seq_hexdump(struct seq_file *s, void *p, int len, const char *prefix) @@ -719,6 +797,8 @@ int wil6210_debugfs_init(struct wil6210_priv *wil) debugfs_create_file("mem_val", S_IRUGO, dbg, wil, &fops_memread); debugfs_create_file("reset", S_IWUSR, dbg, wil, &fops_reset); + debugfs_create_file("rxon", S_IWUSR, dbg, wil, &fops_rxon); + debugfs_create_file("tx_mgmt", S_IWUSR, dbg, wil, &fops_txmgmt); debugfs_create_file("temp", S_IRUGO, dbg, wil, &fops_temp); wil->rgf_blob.data = (void * __force)wil->csr + 0; diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index e25edc52398f..793675ee69a9 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -504,6 +504,9 @@ int wil6210_init_irq(struct wil6210_priv *wil, int irq); void wil6210_fini_irq(struct wil6210_priv *wil, int irq); void wil6210_disable_irq(struct wil6210_priv *wil); void wil6210_enable_irq(struct wil6210_priv *wil); +int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_mgmt_tx_params *params, + u64 *cookie); int wil6210_debugfs_init(struct wil6210_priv *wil); void wil6210_debugfs_remove(struct wil6210_priv *wil); From 304464f482272d761bf4c479738d3183fc36857b Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:37:00 +0300 Subject: [PATCH 076/274] wil6210: indicate mgmt_tx status Inform cfg80211 about Tx result Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/cfg80211.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 1725dfca1d8c..cfdf1a273e8f 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -451,6 +451,7 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, size_t len = params->len; struct wil6210_priv *wil = wiphy_to_wil(wiphy); int rc; + bool tx_status = false; struct ieee80211_mgmt *mgmt_frame = (void *)buf; struct wmi_sw_tx_req_cmd *cmd; struct { @@ -459,8 +460,10 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, } __packed evt; cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL); - if (!cmd) - return -ENOMEM; + if (!cmd) { + rc = -ENOMEM; + goto out; + } memcpy(cmd->dst_mac, mgmt_frame->da, WMI_MAC_LEN); cmd->len = cpu_to_le16(len); @@ -469,10 +472,12 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, cmd, sizeof(*cmd) + len, WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000); if (rc == 0) - rc = evt.evt.status; + tx_status = !evt.evt.status; kfree(cmd); - + out: + cfg80211_mgmt_tx_status(wdev, cookie ? *cookie : 0, buf, len, + tx_status, GFP_KERNEL); return rc; } From ca959773f00128defd7b87815b7d38ca318e21d9 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:37:01 +0300 Subject: [PATCH 077/274] wil6210: print debug info when starting AP In the wil_cfg80211_start_ap(), debug print selected data: - beacon (before and after fix-up) - crypto parameters - mark start/stop AP invocation Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/cfg80211.c | 43 ++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index cfdf1a273e8f..a3042cd3d308 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -566,6 +566,34 @@ static int wil_cancel_remain_on_channel(struct wiphy *wiphy, return rc; } +static void wil_print_bcon_data(struct cfg80211_beacon_data *b) +{ + print_hex_dump_bytes("head ", DUMP_PREFIX_OFFSET, + b->head, b->head_len); + print_hex_dump_bytes("tail ", DUMP_PREFIX_OFFSET, + b->tail, b->tail_len); + print_hex_dump_bytes("BCON IE ", DUMP_PREFIX_OFFSET, + b->beacon_ies, b->beacon_ies_len); + print_hex_dump_bytes("PROBE ", DUMP_PREFIX_OFFSET, + b->probe_resp, b->probe_resp_len); + print_hex_dump_bytes("PROBE IE ", DUMP_PREFIX_OFFSET, + b->proberesp_ies, b->proberesp_ies_len); + print_hex_dump_bytes("ASSOC IE ", DUMP_PREFIX_OFFSET, + b->assocresp_ies, b->assocresp_ies_len); +} + +static void wil_print_crypto(struct wil6210_priv *wil, + struct cfg80211_crypto_settings *c) +{ + wil_dbg_misc(wil, "WPA versions: 0x%08x cipher group 0x%08x\n", + c->wpa_versions, c->cipher_group); + wil_dbg_misc(wil, "Pairwise ciphers [%d]\n", c->n_ciphers_pairwise); + wil_dbg_misc(wil, "AKM suites [%d]\n", c->n_akm_suites); + wil_dbg_misc(wil, "Control port : %d, eth_type 0x%04x no_encrypt %d\n", + c->control_port, be16_to_cpu(c->control_port_ethertype), + c->control_port_no_encrypt); +} + static int wil_fix_bcon(struct wil6210_priv *wil, struct cfg80211_beacon_data *bcon) { @@ -599,8 +627,11 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy, struct wireless_dev *wdev = ndev->ieee80211_ptr; struct ieee80211_channel *channel = info->chandef.chan; struct cfg80211_beacon_data *bcon = &info->beacon; + struct cfg80211_crypto_settings *crypto = &info->crypto; u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); + wil_dbg_misc(wil, "%s()\n", __func__); + if (!channel) { wil_err(wil, "AP: No channel???\n"); return -EINVAL; @@ -608,11 +639,19 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy, wil_dbg_misc(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value, channel->center_freq, info->privacy ? "secure" : "open"); + wil_dbg_misc(wil, "Privacy: %d auth_type %d\n", + info->privacy, info->auth_type); + wil_dbg_misc(wil, "BI %d DTIM %d\n", info->beacon_interval, + info->dtim_period); print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET, info->ssid, info->ssid_len); + wil_print_bcon_data(bcon); + wil_print_crypto(wil, crypto); - if (wil_fix_bcon(wil, bcon)) + if (wil_fix_bcon(wil, bcon)) { wil_dbg_misc(wil, "Fixed bcon\n"); + wil_print_bcon_data(bcon); + } mutex_lock(&wil->mutex); @@ -667,6 +706,8 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy, int rc = 0; struct wil6210_priv *wil = wiphy_to_wil(wiphy); + wil_dbg_misc(wil, "%s()\n", __func__); + mutex_lock(&wil->mutex); rc = wmi_pcp_stop(wil); From 3de6cf204d540d9cf52ad6669d49425579562d2a Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:37:02 +0300 Subject: [PATCH 078/274] wil6210: trace wil->status on debugfs For debug purposes Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/debugfs.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 9d5db0472f4b..4fb33750505c 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -231,6 +231,26 @@ static struct dentry *wil_debugfs_create_iomem_x32(const char *name, &fops_iomem_x32); } +static int wil_debugfs_ulong_set(void *data, u64 val) +{ + *(ulong *)data = val; + return 0; +} +static int wil_debugfs_ulong_get(void *data, u64 *val) +{ + *val = *(ulong *)data; + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(wil_fops_ulong, wil_debugfs_ulong_get, + wil_debugfs_ulong_set, "%llu\n"); + +static struct dentry *wil_debugfs_create_ulong(const char *name, umode_t mode, + struct dentry *parent, + ulong *value) +{ + return debugfs_create_file(name, mode, parent, value, &wil_fops_ulong); +} + static int wil6210_debugfs_create_ISR(struct wil6210_priv *wil, const char *name, struct dentry *parent, u32 off) @@ -781,6 +801,8 @@ int wil6210_debugfs_init(struct wil6210_priv *wil) debugfs_create_file("ssid", S_IRUGO | S_IWUSR, dbg, wil, &fops_ssid); debugfs_create_u32("secure_pcp", S_IRUGO | S_IWUSR, dbg, &wil->secure_pcp); + wil_debugfs_create_ulong("status", S_IRUGO | S_IWUSR, dbg, + &wil->status); wil6210_debugfs_create_ISR(wil, "USER_ICR", dbg, HOSTADDR(RGF_USER_USER_ICR)); From 8eea944af0efcdba09dac1ad220c9bfa68293279 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:37:03 +0300 Subject: [PATCH 079/274] wil6210: print more info about BSS found print essential info to dmesg Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/wmi.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 6cc0e182cc70..1c5e6e995437 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -327,6 +327,17 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) if (ieee80211_is_beacon(fc) || ieee80211_is_probe_resp(fc)) { struct cfg80211_bss *bss; + u64 tsf = le64_to_cpu(rx_mgmt_frame->u.beacon.timestamp); + u16 cap = le16_to_cpu(rx_mgmt_frame->u.beacon.capab_info); + u16 bi = le16_to_cpu(rx_mgmt_frame->u.beacon.beacon_int); + const u8 *ie_buf = rx_mgmt_frame->u.beacon.variable; + size_t ie_len = d_len - offsetof(struct ieee80211_mgmt, + u.beacon.variable); + wil_dbg_wmi(wil, "Capability info : 0x%04x\n", cap); + wil_dbg_wmi(wil, "TSF : 0x%016llx\n", tsf); + wil_dbg_wmi(wil, "Beacon interval : %d\n", bi); + wil_hex_dump_wmi("IE ", DUMP_PREFIX_OFFSET, 16, 1, ie_buf, + ie_len, true); bss = cfg80211_inform_bss_frame(wiphy, channel, rx_mgmt_frame, d_len, signal, GFP_KERNEL); From 67c3e1b41efe4dd400f444c6dccc4538b627758c Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:37:04 +0300 Subject: [PATCH 080/274] wil6210: more debug info for vring print used/available counters on debugfs; print to dmesg when Tx vring becomes empty This aids with performance investigation Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/debugfs.c | 9 +++++++-- drivers/net/wireless/ath/wil6210/txrx.c | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 4fb33750505c..d90aa28ec7bd 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -72,11 +72,16 @@ static int wil_vring_debugfs_show(struct seq_file *s, void *data) if (vring->va) { int cid = wil->vring2cid_tid[i][0]; int tid = wil->vring2cid_tid[i][1]; + u32 swhead = vring->swhead; + u32 swtail = vring->swtail; + int used = (vring->size + swhead - swtail) + % vring->size; + int avail = vring->size - used - 1; char name[10]; snprintf(name, sizeof(name), "tx_%2d", i); - seq_printf(s, "\n%pM CID %d TID %d\n", - wil->sta[cid].addr, cid, tid); + seq_printf(s, "\n%pM CID %d TID %d [%3d|%3d]\n", + wil->sta[cid].addr, cid, tid, used, avail); wil_print_vring(s, wil, name, vring, '_', 'H'); } } diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 0784ef3d4ce2..c08d041fbe74 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -1132,6 +1132,10 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid) done++; } } + + if (wil_vring_is_empty(vring)) + wil_dbg_txrx(wil, "Ring[%2d] empty\n", ringid); + if (wil_vring_avail_tx(vring) > wil_vring_wmark_high(vring)) netif_tx_wake_all_queues(wil_to_ndev(wil)); From 7c0acf868d2e470c9d6a40091acf8d6444c01b57 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:37:05 +0300 Subject: [PATCH 081/274] wil6210: Tx performance monitoring For performance monitoring, trace time intervals when Tx vring is idle/not idle. Use CPU cycle counter for this, because jiffies is too rough, and other precise time measurement methods involve overhead while get_cycles() should be fast. This used to provide some estimation for percentage when Tx vring was idle, i.e. when hardware is under-utilized. Estimation is not precise because of many reasons - CPU frequency scaling, grt_cycles() may be per core etc. But still, it is good estimation Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/debugfs.c | 16 ++++++++++++++-- drivers/net/wireless/ath/wil6210/txrx.c | 8 +++++++- drivers/net/wireless/ath/wil6210/wil6210.h | 3 ++- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index d90aa28ec7bd..9c1102304b93 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -69,6 +69,8 @@ static int wil_vring_debugfs_show(struct seq_file *s, void *data) for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { struct vring *vring = &(wil->vring_tx[i]); + struct vring_tx_data *txdata = &wil->vring_tx_data[i]; + if (vring->va) { int cid = wil->vring2cid_tid[i][0]; int tid = wil->vring2cid_tid[i][1]; @@ -78,10 +80,20 @@ static int wil_vring_debugfs_show(struct seq_file *s, void *data) % vring->size; int avail = vring->size - used - 1; char name[10]; + /* performance monitoring */ + cycles_t now = get_cycles(); + cycles_t idle = txdata->idle; + cycles_t total = now - txdata->begin; + + txdata->begin = now; + txdata->idle = 0ULL; + snprintf(name, sizeof(name), "tx_%2d", i); - seq_printf(s, "\n%pM CID %d TID %d [%3d|%3d]\n", - wil->sta[cid].addr, cid, tid, used, avail); + seq_printf(s, "\n%pM CID %d TID %d [%3d|%3d] idle %3d%%\n", + wil->sta[cid].addr, cid, tid, used, avail, + (int)((idle*100)/total)); + wil_print_vring(s, wil, name, vring, '_', 'H'); } } diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index c08d041fbe74..0318cd2650ce 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -881,6 +881,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, int nr_frags = skb_shinfo(skb)->nr_frags; uint f = 0; int vring_index = vring - wil->vring_tx; + struct vring_tx_data *txdata = &wil->vring_tx_data[vring_index]; uint i = swhead; dma_addr_t pa; @@ -953,6 +954,9 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4, (const void *)d, sizeof(*d), false); + if (wil_vring_is_empty(vring)) /* performance monitoring */ + txdata->idle += get_cycles() - txdata->last_idle; + /* advance swhead */ wil_vring_advance_head(vring, nr_frags + 1); wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead); @@ -1133,8 +1137,10 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid) } } - if (wil_vring_is_empty(vring)) + if (wil_vring_is_empty(vring)) { /* performance monitoring */ wil_dbg_txrx(wil, "Ring[%2d] empty\n", ringid); + txdata->last_idle = get_cycles(); + } if (wil_vring_avail_tx(vring) > wil_vring_wmark_high(vring)) netif_tx_wake_all_queues(wil_to_ndev(wil)); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 793675ee69a9..ede4c9f5f5c2 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -20,6 +20,7 @@ #include #include #include +#include #define WIL_NAME "wil6210" @@ -251,7 +252,7 @@ struct vring { */ struct vring_tx_data { int enabled; - + cycles_t idle, last_idle, begin; }; enum { /* for wil6210_priv.status */ From 94b7b64c73515bc7689bd9d80e6d2fea536a3cef Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:37:06 +0300 Subject: [PATCH 082/274] wil6210: Allow driver load if FW not ready Usable for debugging, to be able to obtain FW traces Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/pcie_bus.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 1e2e07b9d13d..e5d7ffe7d4e6 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -27,6 +27,10 @@ MODULE_PARM_DESC(use_msi, " Use MSI interrupt: " "0 - don't, 1 - (default) - single, or 3"); +static bool debug_fw; /* = false; */ +module_param(debug_fw, bool, S_IRUGO); +MODULE_PARM_DESC(debug_fw, " load driver if FW not ready. For FW debug"); + /* Bus ops */ static int wil_if_pcie_enable(struct wil6210_priv *wil) { @@ -71,6 +75,8 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil) mutex_lock(&wil->mutex); rc = wil_reset(wil); mutex_unlock(&wil->mutex); + if (debug_fw) + rc = 0; if (rc) goto release_irq; From d5b1c32f51fc27f4fff77878e83949d665124f7d Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:37:07 +0300 Subject: [PATCH 083/274] wil6210: BACK: track last dropped SSN Track and print on debugfs Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/debugfs.c | 2 +- drivers/net/wireless/ath/wil6210/rx_reorder.c | 1 + drivers/net/wireless/ath/wil6210/wil6210.h | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 9c1102304b93..3c3abb6f30fe 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -745,7 +745,7 @@ static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r) else seq_printf(s, "%c", r->reorder_buf[i] ? '*' : '_'); } - seq_puts(s, "]\n"); + seq_printf(s, "] last drop 0x%03x\n", r->ssn_last_drop); } static int wil_sta_debugfs_show(struct seq_file *s, void *data) diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c index 747ae1275877..180ca4793904 100644 --- a/drivers/net/wireless/ath/wil6210/rx_reorder.c +++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c @@ -116,6 +116,7 @@ void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb) /* frame with out of date sequence number */ if (seq_less(seq, r->head_seq_num)) { + r->ssn_last_drop = seq; dev_kfree_skb(skb); goto out; } diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index ede4c9f5f5c2..4cbb8cec29c6 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -304,6 +304,7 @@ struct wil_tid_ampdu_rx { u16 ssn; u16 buf_size; u16 timeout; + u16 ssn_last_drop; u8 dialog_token; bool first_time; /* is it 1-st time this buffer used? */ }; From ff974e4083341383d3dd4079e52ed30f57f376f0 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:37:08 +0300 Subject: [PATCH 084/274] wil6210: debugfs interface to send raw WMI command Debug aid Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/debugfs.c | 41 ++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 3c3abb6f30fe..8767f4ce33f4 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -513,6 +513,46 @@ static const struct file_operations fops_txmgmt = { .open = simple_open, }; +/* Write WMI command (w/o mbox header) to this file to send it + * WMI starts from wil6210_mbox_hdr_wmi header + */ +static ssize_t wil_write_file_wmi(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + struct wil6210_priv *wil = file->private_data; + struct wil6210_mbox_hdr_wmi *wmi; + void *cmd; + int cmdlen = len - sizeof(struct wil6210_mbox_hdr_wmi); + u16 cmdid; + int rc, rc1; + + if (cmdlen <= 0) + return -EINVAL; + + wmi = kmalloc(len, GFP_KERNEL); + if (!wmi) + return -ENOMEM; + + rc = simple_write_to_buffer(wmi, len, ppos, buf, len); + if (rc < 0) + return rc; + + cmd = &wmi[1]; + cmdid = le16_to_cpu(wmi->id); + + rc1 = wmi_send(wil, cmdid, cmd, cmdlen); + kfree(wmi); + + wil_info(wil, "%s(0x%04x[%d]) -> %d\n", __func__, cmdid, cmdlen, rc1); + + return rc; +} + +static const struct file_operations fops_wmi = { + .write = wil_write_file_wmi, + .open = simple_open, +}; + static void wil_seq_hexdump(struct seq_file *s, void *p, int len, const char *prefix) { @@ -838,6 +878,7 @@ int wil6210_debugfs_init(struct wil6210_priv *wil) debugfs_create_file("reset", S_IWUSR, dbg, wil, &fops_reset); debugfs_create_file("rxon", S_IWUSR, dbg, wil, &fops_rxon); debugfs_create_file("tx_mgmt", S_IWUSR, dbg, wil, &fops_txmgmt); + debugfs_create_file("wmi_send", S_IWUSR, dbg, wil, &fops_wmi); debugfs_create_file("temp", S_IRUGO, dbg, wil, &fops_temp); wil->rgf_blob.data = (void * __force)wil->csr + 0; From 4b63261c7d203472a1670d4199ee7754d52150e9 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:37:09 +0300 Subject: [PATCH 085/274] wil6210: writeable ITR registers Interrupt threshold registers may be written to. Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/debugfs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 8767f4ce33f4..4cb54eb6c8fa 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -321,11 +321,11 @@ static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil, if (IS_ERR_OR_NULL(d)) return -ENODEV; - wil_debugfs_create_iomem_x32("TRSH", S_IRUGO, d, wil->csr + + wil_debugfs_create_iomem_x32("TRSH", S_IRUGO | S_IWUSR, d, wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_TRSH)); - wil_debugfs_create_iomem_x32("DATA", S_IRUGO, d, wil->csr + + wil_debugfs_create_iomem_x32("DATA", S_IRUGO | S_IWUSR, d, wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_DATA)); - wil_debugfs_create_iomem_x32("CTL", S_IRUGO, d, wil->csr + + wil_debugfs_create_iomem_x32("CTL", S_IRUGO | S_IWUSR, d, wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_CRL)); return 0; From 92b6747eedc300680de5063c642789d239dcbfbc Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:37:10 +0300 Subject: [PATCH 086/274] wil6210: print error when notifying about FW error Print to dmesg when FW error notification is about to be sent Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/interrupt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index 73593aa3cd98..e4aba53c20e4 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -257,6 +257,7 @@ static void wil_notify_fw_error(struct wil6210_priv *wil) [1] = "EVENT=FW_ERROR", [2] = NULL, }; + wil_err(wil, "Notify about firmware error\n"); kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); } From 2a91d7d06bae371c13ce09e7976cb1470ed67be7 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:37:11 +0300 Subject: [PATCH 087/274] wil6210: debug print when scan request state changes Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/cfg80211.c | 1 + drivers/net/wireless/ath/wil6210/main.c | 2 ++ drivers/net/wireless/ath/wil6210/wmi.c | 3 +++ 3 files changed, 6 insertions(+) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index a3042cd3d308..850a2f11e0f9 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -287,6 +287,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, return -EBUSY; } + wil_dbg_misc(wil, "Start scan_request 0x%p\n", request); wil->scan_request = request; mod_timer(&wil->scan_timer, jiffies + WIL6210_SCAN_TO); diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 11e6d9d22eae..33b89d1e4570 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -552,6 +552,8 @@ static int __wil_down(struct wil6210_priv *wil) napi_disable(&wil->napi_tx); if (wil->scan_request) { + wil_dbg_misc(wil, "Abort scan_request 0x%p\n", + wil->scan_request); del_timer_sync(&wil->scan_timer); cfg80211_scan_done(wil->scan_request, true); wil->scan_request = NULL; diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 1c5e6e995437..281a28f24be6 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -362,6 +362,9 @@ static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id, bool aborted = (data->status != WMI_SCAN_SUCCESS); wil_dbg_wmi(wil, "SCAN_COMPLETE(0x%08x)\n", data->status); + wil_dbg_misc(wil, "Complete scan_request 0x%p aborted %d\n", + wil->scan_request, aborted); + del_timer_sync(&wil->scan_timer); cfg80211_scan_done(wil->scan_request, aborted); wil->scan_request = NULL; From d45cff9f6151bf40006a97804a83e55abccbc21b Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:37:12 +0300 Subject: [PATCH 088/274] wil6210: Use "name = value" format in the debugfs Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/debugfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 4cb54eb6c8fa..89f0d094c5a2 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -755,8 +755,8 @@ static int wil_temp_debugfs_show(struct seq_file *s, void *data) return 0; } - print_temp(s, "MAC temperature :", t_m); - print_temp(s, "Radio temperature :", t_r); + print_temp(s, "T_mac =", t_m); + print_temp(s, "T_radio =", t_r); return 0; } From 9eb82d43da0618f6bab78de0f18e7405085dd955 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:37:13 +0300 Subject: [PATCH 089/274] wil6210: add 'freq' and 'link' debugfs entries Expose operational frequency and link info Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/cfg80211.c | 4 +- drivers/net/wireless/ath/wil6210/debugfs.c | 76 +++++++++++++++++++++ drivers/net/wireless/ath/wil6210/wil6210.h | 2 + 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 850a2f11e0f9..4ac2c208c9ba 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -104,8 +104,8 @@ int wil_iftype_nl2wmi(enum nl80211_iftype type) return -EOPNOTSUPP; } -static int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid, - struct station_info *sinfo) +int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid, + struct station_info *sinfo) { struct wmi_notify_req_cmd cmd = { .cid = cid, diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 89f0d094c5a2..d6acb309dd16 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -773,6 +773,80 @@ static const struct file_operations fops_temp = { .llseek = seq_lseek, }; +/*---------freq------------*/ +static int wil_freq_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + struct wireless_dev *wdev = wil_to_wdev(wil); + u16 freq = wdev->chandef.chan ? wdev->chandef.chan->center_freq : 0; + + seq_printf(s, "Freq = %d\n", freq); + + return 0; +} + +static int wil_freq_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_freq_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_freq = { + .open = wil_freq_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + +/*---------link------------*/ +static int wil_link_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + struct station_info sinfo; + int i, rc; + + for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { + struct wil_sta_info *p = &wil->sta[i]; + char *status = "unknown"; + switch (p->status) { + case wil_sta_unused: + status = "unused "; + break; + case wil_sta_conn_pending: + status = "pending "; + break; + case wil_sta_connected: + status = "connected"; + break; + } + seq_printf(s, "[%d] %pM %s%s\n", i, p->addr, status, + (p->data_port_open ? " data_port_open" : "")); + + if (p->status == wil_sta_connected) { + rc = wil_cid_fill_sinfo(wil, i, &sinfo); + if (rc) + return rc; + + seq_printf(s, " Tx_mcs = %d\n", sinfo.txrate.mcs); + seq_printf(s, " Rx_mcs = %d\n", sinfo.rxrate.mcs); + seq_printf(s, " SQ = %d\n", sinfo.signal); + } + } + + return 0; +} + +static int wil_link_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_link_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_link = { + .open = wil_link_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + /*---------Station matrix------------*/ static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r) { @@ -880,6 +954,8 @@ int wil6210_debugfs_init(struct wil6210_priv *wil) debugfs_create_file("tx_mgmt", S_IWUSR, dbg, wil, &fops_txmgmt); debugfs_create_file("wmi_send", S_IWUSR, dbg, wil, &fops_wmi); debugfs_create_file("temp", S_IRUGO, dbg, wil, &fops_temp); + debugfs_create_file("freq", S_IRUGO, dbg, wil, &fops_freq); + debugfs_create_file("link", S_IRUGO, dbg, wil, &fops_link); wil->rgf_blob.data = (void * __force)wil->csr + 0; wil->rgf_blob.size = 0xa000; diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 4cbb8cec29c6..fd6ff0926f3b 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -512,6 +512,8 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, int wil6210_debugfs_init(struct wil6210_priv *wil); void wil6210_debugfs_remove(struct wil6210_priv *wil); +int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid, + struct station_info *sinfo); struct wireless_dev *wil_cfg80211_init(struct device *dev); void wil_wdev_free(struct wil6210_priv *wil); From 194b482b5055d50b481e0c5651bab90623deb611 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:37:14 +0300 Subject: [PATCH 090/274] wil6210: Debug print GRO Rx result Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/txrx.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 0318cd2650ce..a89a5c68e006 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -525,6 +525,17 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) ndev->stats.rx_bytes += len; stats->rx_bytes += len; } + { + static const char * const gro_res_str[] = { + [GRO_MERGED] = "GRO_MERGED", + [GRO_MERGED_FREE] = "GRO_MERGED_FREE", + [GRO_HELD] = "GRO_HELD", + [GRO_NORMAL] = "GRO_NORMAL", + [GRO_DROP] = "GRO_DROP", + }; + wil_dbg_txrx(wil, "Rx complete %d bytes => %s,\n", + len, gro_res_str[rc]); + } } /** From 5aed13932a36a4c12aad4d84b98d439f5b7af34e Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:37:15 +0300 Subject: [PATCH 091/274] wil6210: avoid dmesg pollution by Tx errors On Tx path, when vring for the destination not found, it was considered as error and message was printed unconditionally. However, this situation is normal after disconnect. If disconnect was while heavy traffic load, lots of Tx packets will be dropped and this would cause significant amount of prints in dmesg. Demote messages priority from 'error' to 'debug'. Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/txrx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index a89a5c68e006..c06d74adb3fc 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -771,7 +771,7 @@ static struct vring *wil_tx_bcast(struct wil6210_priv *wil, goto found; } - wil_err(wil, "Tx while no vrings active?\n"); + wil_dbg_txrx(wil, "Tx while no vrings active?\n"); return NULL; @@ -1031,7 +1031,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) vring = wil_tx_bcast(wil, skb); } if (!vring) { - wil_err(wil, "No Tx VRING found for %pM\n", eth->h_dest); + wil_dbg_txrx(wil, "No Tx VRING found for %pM\n", eth->h_dest); goto drop; } /* set up vring entry */ From fc58f6811a9edae55f64538bd361945572c366f6 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:37:16 +0300 Subject: [PATCH 092/274] wil6210: fix disconnect handling for AP For the AP-like interface, if "disconnect all" requested, every station should be deleted with cfg80211_del_sta(). Implement this behavior. Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/main.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 33b89d1e4570..6cc4791d6022 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -61,11 +61,24 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, static void wil_disconnect_cid(struct wil6210_priv *wil, int cid) { uint i; + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; struct wil_sta_info *sta = &wil->sta[cid]; + wil_dbg_misc(wil, "%s(CID %d, status %d)\n", __func__, cid, + sta->status); sta->data_port_open = false; if (sta->status != wil_sta_unused) { wmi_disconnect_sta(wil, sta->addr, WLAN_REASON_DEAUTH_LEAVING); + switch (wdev->iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + /* AP-like interface */ + cfg80211_del_sta(ndev, sta->addr, GFP_KERNEL); + break; + default: + break; + } sta->status = wil_sta_unused; } @@ -119,11 +132,6 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid) clear_bit(wil_status_fwconnecting, &wil->status); break; default: - /* AP-like interface and monitor: - * never scan, always connected - */ - if (bssid) - cfg80211_del_sta(ndev, bssid, GFP_KERNEL); break; } } From 84d94d520495d443a6e8202d7f1e190106edbef6 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:37:17 +0300 Subject: [PATCH 093/274] wil6210: remove unused #include In the pcie_bus.c, knowledge about debugfs is not necessary Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/pcie_bus.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index e5d7ffe7d4e6..05c1c9d6f9c2 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -15,7 +15,6 @@ */ #include -#include #include #include From f8cd9f8b327aaf74ab15b8e8146e8102fe90dbb6 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:37:18 +0300 Subject: [PATCH 094/274] wil6210: map additional registers on target New registers area introduced, mark corresponded address range as valid Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/wmi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 281a28f24be6..b92a042af2c3 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -75,6 +75,7 @@ static const struct { {0x800000, 0x808000, 0x900000}, /* FW data RAM 32k */ {0x840000, 0x860000, 0x908000}, /* peripheral data RAM 128k/96k used */ {0x880000, 0x88a000, 0x880000}, /* various RGF */ + {0x88b000, 0x88c000, 0x88b000}, /* Pcie_ext_rgf */ {0x8c0000, 0x949000, 0x8c0000}, /* trivial mapping for upper area */ /* * 920000..930000 ucode code RAM From 95266dc07d52b28d7cedb755e2ff4254bb2d7eec Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:37:19 +0300 Subject: [PATCH 095/274] wil6210: fix for unreachable code in wmi_recv_cmd As reported by Dan Carpenter : The patch a715c7ddd65a: "wil6210: improve debug for WMI receive" from May 29, 2014, leads to the following static checker warning: drivers/net/wireless/ath/wil6210/wmi.c:746 wmi_recv_cmd() info: ignoring unreachable code. drivers/net/wireless/ath/wil6210/wmi.c 739 spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); 740 { 741 int q = queue_work(wil->wmi_wq, 742 &wil->wmi_event_worker); 743 wil_dbg_wmi(wil, "queue_work -> %d\n", q); 744 } 745 } 746 if (n > 1) ^^^^^^^^^^ We never reach this if statemtent. 747 wil_dbg_wmi(wil, "%s -> %d events processed\n", __func__, n); 748 } Exit loop with "break", not "return". Reported-by: Dan Carpenter Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/wmi.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index b92a042af2c3..a136dab560e2 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -683,14 +683,12 @@ void wmi_recv_cmd(struct wil6210_priv *wil) for (n = 0;; n++) { u16 len; + bool q; r->head = ioread32(wil->csr + HOST_MBOX + offsetof(struct wil6210_mbox_ctl, rx.head)); - if (r->tail == r->head) { - if (n == 0) - wil_dbg_wmi(wil, "No events?\n"); - return; - } + if (r->tail == r->head) + break; wil_dbg_wmi(wil, "Mbox head %08x tail %08x\n", r->head, r->tail); @@ -699,14 +697,14 @@ void wmi_recv_cmd(struct wil6210_priv *wil) sizeof(struct wil6210_mbox_ring_desc)); if (d_tail.sync == 0) { wil_err(wil, "Mbox evt not owned by FW?\n"); - return; + break; } /* read cmd header from descriptor */ if (0 != wmi_read_hdr(wil, d_tail.addr, &hdr)) { wil_err(wil, "Mbox evt at 0x%08x?\n", le32_to_cpu(d_tail.addr)); - return; + break; } len = le16_to_cpu(hdr.len); wil_dbg_wmi(wil, "Mbox evt %04x %04x %04x %02x\n", @@ -720,7 +718,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil) event.wmi) + len, 4), GFP_KERNEL); if (!evt) - return; + break; evt->event.hdr = hdr; cmd = (void *)&evt->event.wmi; @@ -752,14 +750,11 @@ void wmi_recv_cmd(struct wil6210_priv *wil) spin_lock_irqsave(&wil->wmi_ev_lock, flags); list_add_tail(&evt->list, &wil->pending_wmi_ev); spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); - { - int q = queue_work(wil->wmi_wq, - &wil->wmi_event_worker); - wil_dbg_wmi(wil, "queue_work -> %d\n", q); - } + q = queue_work(wil->wmi_wq, &wil->wmi_event_worker); + wil_dbg_wmi(wil, "queue_work -> %d\n", q); } - if (n > 1) - wil_dbg_wmi(wil, "%s -> %d events processed\n", __func__, n); + /* normally, 1 event per IRQ should be processed */ + wil_dbg_wmi(wil, "%s -> %d events queued\n", __func__, n); } int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, From 2bdc0700263ff2c557fa566881721394abfc2ea4 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:37:20 +0300 Subject: [PATCH 096/274] wil6210: work around for platforms with broken INTx There are platforms where INTx can't be routed by ACPI, this leads to pci_enable_device failure. Re-try pretending we have MSI already configured; in this case pci_enable_device do not try to configure INTx. However, MSI could still work. Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/pcie_bus.c | 24 +++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 05c1c9d6f9c2..77b6272d93fb 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -35,6 +35,13 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil) { struct pci_dev *pdev = wil->pdev; int rc; + /* on platforms with buggy ACPI, pdev->msi_enabled may be set to + * allow pci_enable_device to work. This indicates INTx was not routed + * and only MSI should be used + */ + int msi_only = pdev->msi_enabled; + + pdev->msi_enabled = 0; pci_set_master(pdev); @@ -66,6 +73,12 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil) wil->n_msi = use_msi; + if ((wil->n_msi == 0) && msi_only) { + wil_err(wil, "Interrupt pin not routed, unable to use INTx\n"); + rc = -ENODEV; + goto stop_master; + } + rc = wil6210_init_irq(wil, pdev->irq); if (rc) goto stop_master; @@ -124,9 +137,16 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) rc = pci_enable_device(pdev); if (rc) { - dev_err(&pdev->dev, "pci_enable_device failed\n"); - return -ENODEV; + dev_err(&pdev->dev, + "pci_enable_device failed, retry with MSI only\n"); + /* Work around for platforms that can't allocate IRQ: + * retry with MSI only + */ + pdev->msi_enabled = 1; + rc = pci_enable_device(pdev); } + if (rc) + return -ENODEV; /* rollback to err_disable_pdev */ rc = pci_request_region(pdev, 0, WIL_NAME); From 84bb29b7ab71daed9318073dd33632274373a1ea Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:37:21 +0300 Subject: [PATCH 097/274] wil6210: add 'info' debugfs entry Use 'info' debugfs entry for misc. assorted information. Start with indication whether platform is AC-powered; will use it later for power related decisions Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/debugfs.c | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index d6acb309dd16..8bf00aca321b 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "wil6210.h" #include "txrx.h" @@ -847,6 +848,29 @@ static const struct file_operations fops_link = { .llseek = seq_lseek, }; +/*---------info------------*/ +static int wil_info_debugfs_show(struct seq_file *s, void *data) +{ + int is_ac = power_supply_is_system_supplied(); + + /* >0 : AC; 0 : battery; <0 : error */ + seq_printf(s, "AC powered : %d\n", is_ac); + + return 0; +} + +static int wil_info_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_info_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_info = { + .open = wil_info_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + /*---------Station matrix------------*/ static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r) { @@ -956,6 +980,7 @@ int wil6210_debugfs_init(struct wil6210_priv *wil) debugfs_create_file("temp", S_IRUGO, dbg, wil, &fops_temp); debugfs_create_file("freq", S_IRUGO, dbg, wil, &fops_freq); debugfs_create_file("link", S_IRUGO, dbg, wil, &fops_link); + debugfs_create_file("info", S_IRUGO, dbg, wil, &fops_info); wil->rgf_blob.data = (void * __force)wil->csr + 0; wil->rgf_blob.size = 0xa000; From be299858d0611ee9f43fab7fa6dbea8671748267 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:37:22 +0300 Subject: [PATCH 098/274] wil6210: interrupt statistics Track number of interrupts and Tx/Rx packets; expose through debugfs 'info'. Reset upon read. Used to analyse effectivness of interrupt coalescing and NAPI. Read twice with some interval like cat info > /dev/null; sleep 1; cat info Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/debugfs.c | 11 +++++++++++ drivers/net/wireless/ath/wil6210/interrupt.c | 2 ++ drivers/net/wireless/ath/wil6210/wil6210.h | 1 + 3 files changed, 14 insertions(+) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 8bf00aca321b..94ac69a380b8 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -851,10 +851,21 @@ static const struct file_operations fops_link = { /*---------info------------*/ static int wil_info_debugfs_show(struct seq_file *s, void *data) { + struct wil6210_priv *wil = s->private; + struct net_device *ndev = wil_to_ndev(wil); int is_ac = power_supply_is_system_supplied(); + int rx = atomic_xchg(&wil->isr_count_rx, 0); + int tx = atomic_xchg(&wil->isr_count_tx, 0); + static ulong rxf_old, txf_old; + ulong rxf = ndev->stats.rx_packets; + ulong txf = ndev->stats.tx_packets; /* >0 : AC; 0 : battery; <0 : error */ seq_printf(s, "AC powered : %d\n", is_ac); + seq_printf(s, "Rx irqs:packets : %8d : %8ld\n", rx, rxf - rxf_old); + seq_printf(s, "Tx irqs:packets : %8d : %8ld\n", tx, txf - txf_old); + rxf_old = rxf; + txf_old = txf; return 0; } diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index e4aba53c20e4..67f1002a03a1 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -208,6 +208,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie) /* Rx IRQ will be enabled when NAPI processing finished */ + atomic_inc(&wil->isr_count_rx); return IRQ_HANDLED; } @@ -246,6 +247,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie) /* Tx IRQ will be enabled when NAPI processing finished */ + atomic_inc(&wil->isr_count_tx); return IRQ_HANDLED; } diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index fd6ff0926f3b..424906635f05 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -412,6 +412,7 @@ struct wil6210_priv { struct mutex mutex; /* for wil6210_priv access in wil_{up|down} */ /* statistics */ struct wil6210_stats stats; + atomic_t isr_count_rx, isr_count_tx; /* debugfs */ struct dentry *debug; struct debugfs_blob_wrapper fw_code_blob; From 55f8f68017a2d1f4836d50ac5c6473f10c22e557 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 16 Jun 2014 19:37:23 +0300 Subject: [PATCH 099/274] wil6210: track Tx queue state Provide both event (netif_tx_[stop|wake]) tracking via printk; and state via debugfs 'info' Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/debugfs.c | 16 ++++++++++++++++ drivers/net/wireless/ath/wil6210/main.c | 2 ++ drivers/net/wireless/ath/wil6210/txrx.c | 8 ++++++-- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 94ac69a380b8..7d1ef4eea0d8 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -859,6 +859,7 @@ static int wil_info_debugfs_show(struct seq_file *s, void *data) static ulong rxf_old, txf_old; ulong rxf = ndev->stats.rx_packets; ulong txf = ndev->stats.tx_packets; + unsigned int i; /* >0 : AC; 0 : battery; <0 : error */ seq_printf(s, "AC powered : %d\n", is_ac); @@ -867,6 +868,21 @@ static int wil_info_debugfs_show(struct seq_file *s, void *data) rxf_old = rxf; txf_old = txf; + +#define CHECK_QSTATE(x) (state & BIT(__QUEUE_STATE_ ## x)) ? \ + " " __stringify(x) : "" + + for (i = 0; i < ndev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(ndev, i); + unsigned long state = txq->state; + + seq_printf(s, "Tx queue[%i] state : 0x%lx%s%s%s\n", i, state, + CHECK_QSTATE(DRV_XOFF), + CHECK_QSTATE(STACK_XOFF), + CHECK_QSTATE(FROZEN) + ); + } +#undef CHECK_QSTATE return 0; } diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 6cc4791d6022..53a689ed7c7d 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -473,6 +473,7 @@ void wil_link_on(struct wil6210_priv *wil) wil_dbg_misc(wil, "%s()\n", __func__); netif_carrier_on(ndev); + wil_dbg_misc(wil, "netif_tx_wake : link on\n"); netif_tx_wake_all_queues(ndev); } @@ -483,6 +484,7 @@ void wil_link_off(struct wil6210_priv *wil) wil_dbg_misc(wil, "%s()\n", __func__); netif_tx_stop_all_queues(ndev); + wil_dbg_misc(wil, "netif_tx_stop : link off\n"); netif_carrier_off(ndev); } diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index c06d74adb3fc..af4b93e4beb5 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -1038,8 +1038,10 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) rc = wil_tx_vring(wil, vring, skb); /* do we still have enough room in the vring? */ - if (wil_vring_avail_tx(vring) < wil_vring_wmark_low(vring)) + if (wil_vring_avail_tx(vring) < wil_vring_wmark_low(vring)) { netif_tx_stop_all_queues(wil_to_ndev(wil)); + wil_dbg_txrx(wil, "netif_tx_stop : ring full\n"); + } switch (rc) { case 0: @@ -1153,8 +1155,10 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid) txdata->last_idle = get_cycles(); } - if (wil_vring_avail_tx(vring) > wil_vring_wmark_high(vring)) + if (wil_vring_avail_tx(vring) > wil_vring_wmark_high(vring)) { + wil_dbg_txrx(wil, "netif_tx_wake : ring not full\n"); netif_tx_wake_all_queues(wil_to_ndev(wil)); + } return done; } From d4150246eb403eee756516c98e7ebc5792cb34f7 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Wed, 18 Jun 2014 16:51:29 +0200 Subject: [PATCH 100/274] drivers/net/wireless/rt2x00/rt2x00dev.c: remove null test before kfree Fix checkpatch warning: WARNING: kfree(NULL) is safe this check is probably not required Cc: Ivo van Doorn Cc: Helmut Schaa Signed-off-by: Fabian Frederick Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00dev.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 72e3e8138111..c6ae9a495b77 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -1455,8 +1455,7 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) /* * Free the driver data. */ - if (rt2x00dev->drv_data) - kfree(rt2x00dev->drv_data); + kfree(rt2x00dev->drv_data); } EXPORT_SYMBOL_GPL(rt2x00lib_remove_dev); From dff6277658fc3609ccd6bf0a6dc487e7bf1ac6d6 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Wed, 18 Jun 2014 19:43:31 +0200 Subject: [PATCH 101/274] SPECTRUM_CS: ioport_map/unmap relies on HAS_IOPORT_MAP Fixing following sh-allmodconfig errors reported on kisskb " drivers/net/wireless/orinoco/spectrum_cs.c:216:2: error: implicit declaration of function 'ioport_map' [-Werror=implicit-function-declaration] drivers/net/wireless/orinoco/spectrum_cs.c:273:3: error: implicit declaration of function 'ioport_unmap' [-Werror=implicit-function-declaration] " Cc: "John W. Linville" Cc: Paul Gortmaker Cc: linux-wireless@vger.kernel.org Signed-off-by: Fabian Frederick Signed-off-by: John W. Linville --- drivers/net/wireless/orinoco/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/orinoco/Kconfig b/drivers/net/wireless/orinoco/Kconfig index 60819bcf4377..a8aab9f99d79 100644 --- a/drivers/net/wireless/orinoco/Kconfig +++ b/drivers/net/wireless/orinoco/Kconfig @@ -122,7 +122,7 @@ config PCMCIA_HERMES config PCMCIA_SPECTRUM tristate "Symbol Spectrum24 Trilogy PCMCIA card support" - depends on PCMCIA && HERMES + depends on PCMCIA && HERMES && HAS_IOPORT_MAP ---help--- This is a driver for 802.11b cards using RAM-loadable Symbol From 11c761c4816c2b2d7704a94e5ab3f010d816e601 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Wed, 18 Jun 2014 19:48:02 +0200 Subject: [PATCH 102/274] LIBERTAS_CS: ioport_map/unmap relies on HAS_IOPORT_MAP Fixing following sh-allmodconfig errors reported on kisskb " drivers/net/wireless/libertas/if_cs.c:826:3: error: implicit declaration of function 'ioport_unmap' [-Werror=implicit-function-declaration] drivers/net/wireless/libertas/if_cs.c:878:2: error: implicit declaration of function 'ioport_map' [-Werror=implicit-function-declaration] " Cc: "John W. Linville" Cc: Dan Williams Cc: linux-wireless@vger.kernel.org Signed-off-by: Fabian Frederick Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/libertas/Kconfig b/drivers/net/wireless/libertas/Kconfig index 0485c9957575..e6268ceacbf1 100644 --- a/drivers/net/wireless/libertas/Kconfig +++ b/drivers/net/wireless/libertas/Kconfig @@ -16,7 +16,7 @@ config LIBERTAS_USB config LIBERTAS_CS tristate "Marvell Libertas 8385 CompactFlash 802.11b/g cards" - depends on LIBERTAS && PCMCIA + depends on LIBERTAS && PCMCIA && HAS_IOPORT_MAP ---help--- A driver for Marvell Libertas 8385 CompactFlash devices. From 2e91606f5e1ec7329557dfc0e298c4c021acbb80 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Wed, 18 Jun 2014 19:55:38 +0200 Subject: [PATCH 103/274] PCMCIA_HERMES: ioport_map/unmap relies on HAS_IOPORT_MAP Fixing following sh-allmodconfig errors reported on kisskb " drivers/net/wireless/orinoco/orinoco_cs.c:153:2: error: implicit declaration of function 'ioport_map' [-Werror=implicit-function-declaration] drivers/net/wireless/orinoco/orinoco_cs.c:205:3: error: implicit declaration of function 'ioport_unmap' [-Werror=implicit-function-declaration] " Cc: "John W. Linville" Cc: Paul Gortmaker Cc: linux-wireless@vger.kernel.org Signed-off-by: Fabian Frederick Signed-off-by: John W. Linville --- drivers/net/wireless/orinoco/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/orinoco/Kconfig b/drivers/net/wireless/orinoco/Kconfig index a8aab9f99d79..60698b020851 100644 --- a/drivers/net/wireless/orinoco/Kconfig +++ b/drivers/net/wireless/orinoco/Kconfig @@ -107,7 +107,7 @@ config PCI_HERMES config PCMCIA_HERMES tristate "Hermes PCMCIA card support" - depends on PCMCIA && HERMES + depends on PCMCIA && HERMES && HAS_IOPORT_MAP ---help--- A driver for "Hermes" chipset based PCMCIA wireless adaptors, such as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/ From 6451acdc4f716610043442016f76a0b849c06ea6 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 19 Jun 2014 21:48:12 -0700 Subject: [PATCH 104/274] mwifiex: Use the proper interfaces Why is converting time formats so desired if there are proper interfaces for this? Signed-off-by: Thomas Gleixner Acked-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 4 +--- drivers/net/wireless/mwifiex/main.c | 4 +--- drivers/net/wireless/mwifiex/tdls.c | 8 ++------ drivers/net/wireless/mwifiex/uap_txrx.c | 4 +--- drivers/net/wireless/mwifiex/wmm.c | 9 +-------- 5 files changed, 6 insertions(+), 23 deletions(-) diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index e8981afb208b..8e7b9c9696f6 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -121,7 +121,6 @@ mwifiex_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len) u8 addr[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; u16 pkt_len; u32 tx_control = 0, pkt_type = PKT_TYPE_MGMT; - struct timeval tv; pkt_len = len + ETH_ALEN; @@ -143,8 +142,7 @@ mwifiex_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len) len - sizeof(struct ieee80211_hdr_3addr)); skb->priority = LOW_PRIO_TID; - do_gettimeofday(&tv); - skb->tstamp = timeval_to_ktime(tv); + __net_timestamp(skb); return 0; } diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index cbabc12fbda3..40e4dbd5b89d 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -609,7 +609,6 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); struct sk_buff *new_skb; struct mwifiex_txinfo *tx_info; - struct timeval tv; dev_dbg(priv->adapter->dev, "data: %lu BSS(%d-%d): Data <= kernel\n", jiffies, priv->bss_type, priv->bss_num); @@ -656,8 +655,7 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) * firmware for aggregate delay calculation for stats and * MSDU lifetime expiry. */ - do_gettimeofday(&tv); - skb->tstamp = timeval_to_ktime(tv); + __net_timestamp(skb); mwifiex_queue_tx_pkt(priv, skb); diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c index e73034fbbde9..3efbcbe7e891 100644 --- a/drivers/net/wireless/mwifiex/tdls.c +++ b/drivers/net/wireless/mwifiex/tdls.c @@ -530,7 +530,6 @@ int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, { struct sk_buff *skb; struct mwifiex_txinfo *tx_info; - struct timeval tv; int ret; u16 skb_len; @@ -608,8 +607,7 @@ int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, tx_info->bss_num = priv->bss_num; tx_info->bss_type = priv->bss_type; - do_gettimeofday(&tv); - skb->tstamp = timeval_to_ktime(tv); + __net_timestamp(skb); mwifiex_queue_tx_pkt(priv, skb); return 0; @@ -702,7 +700,6 @@ int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer, { struct sk_buff *skb; struct mwifiex_txinfo *tx_info; - struct timeval tv; u8 *pos; u32 pkt_type, tx_control; u16 pkt_len, skb_len; @@ -767,8 +764,7 @@ int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer, pkt_len = skb->len - MWIFIEX_MGMT_FRAME_HEADER_SIZE - sizeof(pkt_len); memcpy(skb->data + MWIFIEX_MGMT_FRAME_HEADER_SIZE, &pkt_len, sizeof(pkt_len)); - do_gettimeofday(&tv); - skb->tstamp = timeval_to_ktime(tv); + __net_timestamp(skb); mwifiex_queue_tx_pkt(priv, skb); return 0; diff --git a/drivers/net/wireless/mwifiex/uap_txrx.c b/drivers/net/wireless/mwifiex/uap_txrx.c index 9a56bc61cb1d..0c87f8f9113a 100644 --- a/drivers/net/wireless/mwifiex/uap_txrx.c +++ b/drivers/net/wireless/mwifiex/uap_txrx.c @@ -96,7 +96,6 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv, struct sk_buff *new_skb; struct mwifiex_txinfo *tx_info; int hdr_chop; - struct timeval tv; struct ethhdr *p_ethhdr; uap_rx_pd = (struct uap_rxpd *)(skb->data); @@ -192,8 +191,7 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv, tx_info->pkt_len = skb->len; } - do_gettimeofday(&tv); - skb->tstamp = timeval_to_ktime(tv); + __net_timestamp(skb); mwifiex_wmm_add_buf_txqueue(priv, skb); atomic_inc(&adapter->tx_pending); atomic_inc(&adapter->pending_bridged_pkts); diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index d3671d009f6c..6e86d1e17d3e 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -878,15 +878,8 @@ u8 mwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv, const struct sk_buff *skb) { + u32 queue_delay = ktime_to_ms(ktime_sub(ktime_get(), skb->tstamp)); u8 ret_val; - struct timeval out_tstamp, in_tstamp; - u32 queue_delay; - - do_gettimeofday(&out_tstamp); - in_tstamp = ktime_to_timeval(skb->tstamp); - - queue_delay = (out_tstamp.tv_sec - in_tstamp.tv_sec) * 1000; - queue_delay += (out_tstamp.tv_usec - in_tstamp.tv_usec) / 1000; /* * Queue delay is passed as a uint8 in units of 2ms (ms shifted From e48b1790907c960b9d4f28cf7da9e0c25c5d02df Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Fri, 20 Jun 2014 10:05:07 +0300 Subject: [PATCH 105/274] wil6210: fix for 64-bit integer division On some platforms, cycles_t is 64-bit, and gcc generates call to __udivdi3 for straight division of cycles_t/cycles_t. This leads to compilation failure, as this function is not exist in the kernel runtime. do_div() to rescue Original report: tree: git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next.git master head: 2e91606f5e1ec7329557dfc0e298c4c021acbb80 commit: 7c0acf868d2e470c9d6a40091acf8d6444c01b57 [81/103] wil6210: Tx performance monitoring config: i386-randconfig-ha3-0620 (attached as .config) All error/warnings: drivers/built-in.o: In function `wil_vring_debugfs_show': >> debugfs.c:(.text+0x39b9be): undefined reference to `__udivdi3' Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/debugfs.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 7d1ef4eea0d8..a868c5eebe37 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -83,9 +83,10 @@ static int wil_vring_debugfs_show(struct seq_file *s, void *data) char name[10]; /* performance monitoring */ cycles_t now = get_cycles(); - cycles_t idle = txdata->idle; + cycles_t idle = txdata->idle * 100; cycles_t total = now - txdata->begin; + do_div(idle, total); txdata->begin = now; txdata->idle = 0ULL; @@ -93,7 +94,7 @@ static int wil_vring_debugfs_show(struct seq_file *s, void *data) seq_printf(s, "\n%pM CID %d TID %d [%3d|%3d] idle %3d%%\n", wil->sta[cid].addr, cid, tid, used, avail, - (int)((idle*100)/total)); + (int)idle); wil_print_vring(s, wil, name, vring, '_', 'H'); } From bba4d409cbd322a0b78768a6dda013baed2b5a36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Fri, 20 Jun 2014 17:22:00 +0200 Subject: [PATCH 106/274] b43: remove leftover code from old devices support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Old devices (A-PHY or B-PHY) are supposed to be supported by b43legacy. We keep phy_a.c as it's needed for G-PHY which shares some design. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 9 --------- drivers/net/wireless/b43/phy_a.h | 4 ---- drivers/net/wireless/b43/phy_common.c | 3 --- 3 files changed, 16 deletions(-) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 4b662d0abdd2..4164afa843e9 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4306,15 +4306,6 @@ static int b43_phy_versioning(struct b43_wldev *dev) phy_type = (tmp & B43_PHYVER_TYPE) >> B43_PHYVER_TYPE_SHIFT; phy_rev = (tmp & B43_PHYVER_VERSION); switch (phy_type) { - case B43_PHYTYPE_A: - if (phy_rev >= 4) - unsupported = 1; - break; - case B43_PHYTYPE_B: - if (phy_rev != 2 && phy_rev != 4 && phy_rev != 6 - && phy_rev != 7) - unsupported = 1; - break; case B43_PHYTYPE_G: if (phy_rev > 9) unsupported = 1; diff --git a/drivers/net/wireless/b43/phy_a.h b/drivers/net/wireless/b43/phy_a.h index 5cfaab7b16ee..f7d0d929a374 100644 --- a/drivers/net/wireless/b43/phy_a.h +++ b/drivers/net/wireless/b43/phy_a.h @@ -123,8 +123,4 @@ struct b43_phy_a { */ void b43_phy_inita(struct b43_wldev *dev); - -struct b43_phy_operations; -extern const struct b43_phy_operations b43_phyops_a; - #endif /* LINUX_B43_PHY_A_H_ */ diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c index 2d05b5987168..ce486eeaf8e1 100644 --- a/drivers/net/wireless/b43/phy_common.c +++ b/drivers/net/wireless/b43/phy_common.c @@ -45,9 +45,6 @@ int b43_phy_allocate(struct b43_wldev *dev) phy->ops = NULL; switch (phy->type) { - case B43_PHYTYPE_A: - phy->ops = &b43_phyops_a; - break; case B43_PHYTYPE_G: phy->ops = &b43_phyops_g; break; From 418378fed0506b5ec0b43c03bc11929ec1f88073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Fri, 20 Jun 2014 17:22:01 +0200 Subject: [PATCH 107/274] b43: add config for (en|dis)abling G-PHY support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows new devices users to save some space. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/Kconfig | 9 +++++++++ drivers/net/wireless/b43/Makefile | 6 +----- drivers/net/wireless/b43/main.c | 2 ++ drivers/net/wireless/b43/phy_common.c | 2 ++ 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig index e3f67b8d3f80..037a4e304d14 100644 --- a/drivers/net/wireless/b43/Kconfig +++ b/drivers/net/wireless/b43/Kconfig @@ -122,6 +122,15 @@ config B43_PIO select SSB_BLOCKIO default y +config B43_PHY_G + bool "Support for G-PHY (802.11g) devices" + depends on B43 && B43_SSB + default y + ---help--- + This PHY type can be found in the following chipsets: + PCI: BCM4306, BCM4311, BCM4318 + SoC: BCM4712, BCM5352E + config B43_PHY_N bool "Support for 802.11n (N-PHY) devices" depends on B43 diff --git a/drivers/net/wireless/b43/Makefile b/drivers/net/wireless/b43/Makefile index 098fe9ee7096..6e00b8804ada 100644 --- a/drivers/net/wireless/b43/Makefile +++ b/drivers/net/wireless/b43/Makefile @@ -1,13 +1,11 @@ b43-y += main.o b43-y += bus.o -b43-y += tables.o +b43-$(CONFIG_B43_PHY_G) += phy_a.o phy_g.o tables.o lo.o wa.o b43-$(CONFIG_B43_PHY_N) += tables_nphy.o b43-$(CONFIG_B43_PHY_N) += radio_2055.o b43-$(CONFIG_B43_PHY_N) += radio_2056.o b43-$(CONFIG_B43_PHY_N) += radio_2057.o b43-y += phy_common.o -b43-y += phy_g.o -b43-y += phy_a.o b43-$(CONFIG_B43_PHY_N) += phy_n.o b43-$(CONFIG_B43_PHY_LP) += phy_lp.o b43-$(CONFIG_B43_PHY_LP) += tables_lpphy.o @@ -17,8 +15,6 @@ b43-$(CONFIG_B43_PHY_HT) += radio_2059.o b43-$(CONFIG_B43_PHY_LCN) += phy_lcn.o tables_phy_lcn.o b43-y += sysfs.o b43-y += xmit.o -b43-y += lo.o -b43-y += wa.o b43-y += dma.o b43-y += pio.o b43-y += rfkill.o diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 4164afa843e9..9cf07bb7adf8 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4306,10 +4306,12 @@ static int b43_phy_versioning(struct b43_wldev *dev) phy_type = (tmp & B43_PHYVER_TYPE) >> B43_PHYVER_TYPE_SHIFT; phy_rev = (tmp & B43_PHYVER_VERSION); switch (phy_type) { +#ifdef CONFIG_B43_PHY_G case B43_PHYTYPE_G: if (phy_rev > 9) unsupported = 1; break; +#endif #ifdef CONFIG_B43_PHY_N case B43_PHYTYPE_N: if (phy_rev > 9) diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c index ce486eeaf8e1..3cbef21b4726 100644 --- a/drivers/net/wireless/b43/phy_common.c +++ b/drivers/net/wireless/b43/phy_common.c @@ -46,7 +46,9 @@ int b43_phy_allocate(struct b43_wldev *dev) switch (phy->type) { case B43_PHYTYPE_G: +#ifdef CONFIG_B43_PHY_G phy->ops = &b43_phyops_g; +#endif break; case B43_PHYTYPE_N: #ifdef CONFIG_B43_PHY_N From d6067f0e17eb1de7d9b1d792f67d17c6e894b770 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Fri, 20 Jun 2014 22:47:49 +0530 Subject: [PATCH 108/274] ath9k: Fix build error in ath_reset_internal drivers/net/wireless/ath/ath9k/main.c:299 ath_reset_internal() error: we previously assumed 'hchan' could be null (see line 293) Cc: Felix Fietkau Reported-by: Dan Carpenter Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index cf21652835c1..83cb39efb636 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -314,11 +314,9 @@ int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan) if (!ath_prepare_reset(sc)) fastcc = false; - if (hchan) { - spin_lock_bh(&sc->chan_lock); - sc->cur_chandef = sc->cur_chan->chandef; - spin_unlock_bh(&sc->chan_lock); - } + spin_lock_bh(&sc->chan_lock); + sc->cur_chandef = sc->cur_chan->chandef; + spin_unlock_bh(&sc->chan_lock); ath_dbg(common, CONFIG, "Reset to %u MHz, HT40: %d fastcc: %d\n", hchan->channel, IS_CHAN_HT40(hchan), fastcc); From 20edb50e593dca7522b4f3a95b801dbf99f24c52 Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Fri, 30 May 2014 10:47:28 -0400 Subject: [PATCH 109/274] mac80211: remove PID rate control Minstrel has long since proven its worth. Signed-off-by: John W. Linville Signed-off-by: Johannes Berg --- MAINTAINERS | 10 - net/mac80211/Kconfig | 17 - net/mac80211/Makefile | 5 - net/mac80211/main.c | 7 - net/mac80211/rate.h | 13 - net/mac80211/rc80211_pid.h | 278 ----------------- net/mac80211/rc80211_pid_algo.c | 478 ----------------------------- net/mac80211/rc80211_pid_debugfs.c | 228 -------------- 8 files changed, 1036 deletions(-) delete mode 100644 net/mac80211/rc80211_pid.h delete mode 100644 net/mac80211/rc80211_pid_algo.c delete mode 100644 net/mac80211/rc80211_pid_debugfs.c diff --git a/MAINTAINERS b/MAINTAINERS index 134483f206e4..54ba0e643c39 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5629,16 +5629,6 @@ F: Documentation/networking/mac80211-injection.txt F: include/net/mac80211.h F: net/mac80211/ -MAC80211 PID RATE CONTROL -M: Stefano Brivio -M: Mattias Nissler -L: linux-wireless@vger.kernel.org -W: http://wireless.kernel.org/en/developers/Documentation/mac80211/RateControl/PID -T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git -T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git -S: Maintained -F: net/mac80211/rc80211_pid* - MACVLAN DRIVER M: Patrick McHardy L: netdev@vger.kernel.org diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 97b5dcad5025..aeb6a483b3bc 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -19,14 +19,6 @@ if MAC80211 != n config MAC80211_HAS_RC bool -config MAC80211_RC_PID - bool "PID controller based rate control algorithm" if EXPERT - select MAC80211_HAS_RC - ---help--- - This option enables a TX rate control algorithm for - mac80211 that uses a PID controller to select the TX - rate. - config MAC80211_RC_MINSTREL bool "Minstrel" if EXPERT select MAC80211_HAS_RC @@ -51,14 +43,6 @@ choice overridden through the ieee80211_default_rc_algo module parameter if different algorithms are available. -config MAC80211_RC_DEFAULT_PID - bool "PID controller based rate control algorithm" - depends on MAC80211_RC_PID - ---help--- - Select the PID controller based rate control as the - default rate control algorithm. You should choose - this unless you know what you are doing. - config MAC80211_RC_DEFAULT_MINSTREL bool "Minstrel" depends on MAC80211_RC_MINSTREL @@ -72,7 +56,6 @@ config MAC80211_RC_DEFAULT string default "minstrel_ht" if MAC80211_RC_DEFAULT_MINSTREL && MAC80211_RC_MINSTREL_HT default "minstrel" if MAC80211_RC_DEFAULT_MINSTREL - default "pid" if MAC80211_RC_DEFAULT_PID default "" endif diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 1e46ffa69167..4409bf506594 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -47,17 +47,12 @@ mac80211-$(CONFIG_PM) += pm.o CFLAGS_trace.o := -I$(src) -# objects for PID algorithm -rc80211_pid-y := rc80211_pid_algo.o -rc80211_pid-$(CONFIG_MAC80211_DEBUGFS) += rc80211_pid_debugfs.o - rc80211_minstrel-y := rc80211_minstrel.o rc80211_minstrel-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_debugfs.o rc80211_minstrel_ht-y := rc80211_minstrel_ht.o rc80211_minstrel_ht-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_ht_debugfs.o -mac80211-$(CONFIG_MAC80211_RC_PID) += $(rc80211_pid-y) mac80211-$(CONFIG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y) mac80211-$(CONFIG_MAC80211_RC_MINSTREL_HT) += $(rc80211_minstrel_ht-y) diff --git a/net/mac80211/main.c b/net/mac80211/main.c index d17c26d6e369..0512a5096f0f 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1187,18 +1187,12 @@ static int __init ieee80211_init(void) if (ret) goto err_minstrel; - ret = rc80211_pid_init(); - if (ret) - goto err_pid; - ret = ieee80211_iface_init(); if (ret) goto err_netdev; return 0; err_netdev: - rc80211_pid_exit(); - err_pid: rc80211_minstrel_ht_exit(); err_minstrel: rc80211_minstrel_exit(); @@ -1208,7 +1202,6 @@ static int __init ieee80211_init(void) static void __exit ieee80211_exit(void) { - rc80211_pid_exit(); rc80211_minstrel_ht_exit(); rc80211_minstrel_exit(); diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h index 9aa2a1190a86..18babe302832 100644 --- a/net/mac80211/rate.h +++ b/net/mac80211/rate.h @@ -143,19 +143,6 @@ void rate_control_deinitialize(struct ieee80211_local *local); /* Rate control algorithms */ -#ifdef CONFIG_MAC80211_RC_PID -int rc80211_pid_init(void); -void rc80211_pid_exit(void); -#else -static inline int rc80211_pid_init(void) -{ - return 0; -} -static inline void rc80211_pid_exit(void) -{ -} -#endif - #ifdef CONFIG_MAC80211_RC_MINSTREL int rc80211_minstrel_init(void); void rc80211_minstrel_exit(void); diff --git a/net/mac80211/rc80211_pid.h b/net/mac80211/rc80211_pid.h deleted file mode 100644 index 19111c7bf454..000000000000 --- a/net/mac80211/rc80211_pid.h +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright 2007, Mattias Nissler - * Copyright 2007, Stefano Brivio - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef RC80211_PID_H -#define RC80211_PID_H - -/* Sampling period for measuring percentage of failed frames in ms. */ -#define RC_PID_INTERVAL 125 - -/* Exponential averaging smoothness (used for I part of PID controller) */ -#define RC_PID_SMOOTHING_SHIFT 3 -#define RC_PID_SMOOTHING (1 << RC_PID_SMOOTHING_SHIFT) - -/* Sharpening factor (used for D part of PID controller) */ -#define RC_PID_SHARPENING_FACTOR 0 -#define RC_PID_SHARPENING_DURATION 0 - -/* Fixed point arithmetic shifting amount. */ -#define RC_PID_ARITH_SHIFT 8 - -/* Proportional PID component coefficient. */ -#define RC_PID_COEFF_P 15 -/* Integral PID component coefficient. */ -#define RC_PID_COEFF_I 9 -/* Derivative PID component coefficient. */ -#define RC_PID_COEFF_D 15 - -/* Target failed frames rate for the PID controller. NB: This effectively gives - * maximum failed frames percentage we're willing to accept. If the wireless - * link quality is good, the controller will fail to adjust failed frames - * percentage to the target. This is intentional. - */ -#define RC_PID_TARGET_PF 14 - -/* Rate behaviour normalization quantity over time. */ -#define RC_PID_NORM_OFFSET 3 - -/* Push high rates right after loading. */ -#define RC_PID_FAST_START 0 - -/* Arithmetic right shift for positive and negative values for ISO C. */ -#define RC_PID_DO_ARITH_RIGHT_SHIFT(x, y) \ - ((x) < 0 ? -((-(x)) >> (y)) : (x) >> (y)) - -enum rc_pid_event_type { - RC_PID_EVENT_TYPE_TX_STATUS, - RC_PID_EVENT_TYPE_RATE_CHANGE, - RC_PID_EVENT_TYPE_TX_RATE, - RC_PID_EVENT_TYPE_PF_SAMPLE, -}; - -union rc_pid_event_data { - /* RC_PID_EVENT_TX_STATUS */ - struct { - u32 flags; - struct ieee80211_tx_info tx_status; - }; - /* RC_PID_EVENT_TYPE_RATE_CHANGE */ - /* RC_PID_EVENT_TYPE_TX_RATE */ - struct { - int index; - int rate; - }; - /* RC_PID_EVENT_TYPE_PF_SAMPLE */ - struct { - s32 pf_sample; - s32 prop_err; - s32 int_err; - s32 der_err; - }; -}; - -struct rc_pid_event { - /* The time when the event occurred */ - unsigned long timestamp; - - /* Event ID number */ - unsigned int id; - - /* Type of event */ - enum rc_pid_event_type type; - - /* type specific data */ - union rc_pid_event_data data; -}; - -/* Size of the event ring buffer. */ -#define RC_PID_EVENT_RING_SIZE 32 - -struct rc_pid_event_buffer { - /* Counter that generates event IDs */ - unsigned int ev_count; - - /* Ring buffer of events */ - struct rc_pid_event ring[RC_PID_EVENT_RING_SIZE]; - - /* Index to the entry in events_buf to be reused */ - unsigned int next_entry; - - /* Lock that guards against concurrent access to this buffer struct */ - spinlock_t lock; - - /* Wait queue for poll/select and blocking I/O */ - wait_queue_head_t waitqueue; -}; - -struct rc_pid_events_file_info { - /* The event buffer we read */ - struct rc_pid_event_buffer *events; - - /* The entry we have should read next */ - unsigned int next_entry; -}; - -/** - * struct rc_pid_debugfs_entries - tunable parameters - * - * Algorithm parameters, tunable via debugfs. - * @target: target percentage for failed frames - * @sampling_period: error sampling interval in milliseconds - * @coeff_p: absolute value of the proportional coefficient - * @coeff_i: absolute value of the integral coefficient - * @coeff_d: absolute value of the derivative coefficient - * @smoothing_shift: absolute value of the integral smoothing factor (i.e. - * amount of smoothing introduced by the exponential moving average) - * @sharpen_factor: absolute value of the derivative sharpening factor (i.e. - * amount of emphasis given to the derivative term after low activity - * events) - * @sharpen_duration: duration of the sharpening effect after the detected low - * activity event, relative to sampling_period - * @norm_offset: amount of normalization periodically performed on the learnt - * rate behaviour values (lower means we should trust more what we learnt - * about behaviour of rates, higher means we should trust more the natural - * ordering of rates) - */ -struct rc_pid_debugfs_entries { - struct dentry *target; - struct dentry *sampling_period; - struct dentry *coeff_p; - struct dentry *coeff_i; - struct dentry *coeff_d; - struct dentry *smoothing_shift; - struct dentry *sharpen_factor; - struct dentry *sharpen_duration; - struct dentry *norm_offset; -}; - -void rate_control_pid_event_tx_status(struct rc_pid_event_buffer *buf, - struct ieee80211_tx_info *stat); - -void rate_control_pid_event_rate_change(struct rc_pid_event_buffer *buf, - int index, int rate); - -void rate_control_pid_event_tx_rate(struct rc_pid_event_buffer *buf, - int index, int rate); - -void rate_control_pid_event_pf_sample(struct rc_pid_event_buffer *buf, - s32 pf_sample, s32 prop_err, - s32 int_err, s32 der_err); - -void rate_control_pid_add_sta_debugfs(void *priv, void *priv_sta, - struct dentry *dir); - -void rate_control_pid_remove_sta_debugfs(void *priv, void *priv_sta); - -struct rc_pid_sta_info { - unsigned long last_change; - unsigned long last_sample; - - u32 tx_num_failed; - u32 tx_num_xmit; - - int txrate_idx; - - /* Average failed frames percentage error (i.e. actual vs. target - * percentage), scaled by RC_PID_SMOOTHING. This value is computed - * using using an exponential weighted average technique: - * - * (RC_PID_SMOOTHING - 1) * err_avg_old + err - * err_avg = ------------------------------------------ - * RC_PID_SMOOTHING - * - * where err_avg is the new approximation, err_avg_old the previous one - * and err is the error w.r.t. to the current failed frames percentage - * sample. Note that the bigger RC_PID_SMOOTHING the more weight is - * given to the previous estimate, resulting in smoother behavior (i.e. - * corresponding to a longer integration window). - * - * For computation, we actually don't use the above formula, but this - * one: - * - * err_avg_scaled = err_avg_old_scaled - err_avg_old + err - * - * where: - * err_avg_scaled = err * RC_PID_SMOOTHING - * err_avg_old_scaled = err_avg_old * RC_PID_SMOOTHING - * - * This avoids floating point numbers and the per_failed_old value can - * easily be obtained by shifting per_failed_old_scaled right by - * RC_PID_SMOOTHING_SHIFT. - */ - s32 err_avg_sc; - - /* Last framed failes percentage sample. */ - u32 last_pf; - - /* Sharpening needed. */ - u8 sharp_cnt; - -#ifdef CONFIG_MAC80211_DEBUGFS - /* Event buffer */ - struct rc_pid_event_buffer events; - - /* Events debugfs file entry */ - struct dentry *events_entry; -#endif -}; - -/* Algorithm parameters. We keep them on a per-algorithm approach, so they can - * be tuned individually for each interface. - */ -struct rc_pid_rateinfo { - - /* Map sorted rates to rates in ieee80211_hw_mode. */ - int index; - - /* Map rates in ieee80211_hw_mode to sorted rates. */ - int rev_index; - - /* Did we do any measurement on this rate? */ - bool valid; - - /* Comparison with the lowest rate. */ - int diff; -}; - -struct rc_pid_info { - - /* The failed frames percentage target. */ - unsigned int target; - - /* Rate at which failed frames percentage is sampled in 0.001s. */ - unsigned int sampling_period; - - /* P, I and D coefficients. */ - int coeff_p; - int coeff_i; - int coeff_d; - - /* Exponential averaging shift. */ - unsigned int smoothing_shift; - - /* Sharpening factor and duration. */ - unsigned int sharpen_factor; - unsigned int sharpen_duration; - - /* Normalization offset. */ - unsigned int norm_offset; - - /* Rates information. */ - struct rc_pid_rateinfo *rinfo; - - /* Index of the last used rate. */ - int oldrate; - -#ifdef CONFIG_MAC80211_DEBUGFS - /* Debugfs entries created for the parameters above. */ - struct rc_pid_debugfs_entries dentries; -#endif -}; - -#endif /* RC80211_PID_H */ diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c deleted file mode 100644 index d0da2a70fe68..000000000000 --- a/net/mac80211/rc80211_pid_algo.c +++ /dev/null @@ -1,478 +0,0 @@ -/* - * Copyright 2002-2005, Instant802 Networks, Inc. - * Copyright 2005, Devicescape Software, Inc. - * Copyright 2007, Mattias Nissler - * Copyright 2007-2008, Stefano Brivio - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include "rate.h" -#include "mesh.h" -#include "rc80211_pid.h" - - -/* This is an implementation of a TX rate control algorithm that uses a PID - * controller. Given a target failed frames rate, the controller decides about - * TX rate changes to meet the target failed frames rate. - * - * The controller basically computes the following: - * - * adj = CP * err + CI * err_avg + CD * (err - last_err) * (1 + sharpening) - * - * where - * adj adjustment value that is used to switch TX rate (see below) - * err current error: target vs. current failed frames percentage - * last_err last error - * err_avg average (i.e. poor man's integral) of recent errors - * sharpening non-zero when fast response is needed (i.e. right after - * association or no frames sent for a long time), heading - * to zero over time - * CP Proportional coefficient - * CI Integral coefficient - * CD Derivative coefficient - * - * CP, CI, CD are subject to careful tuning. - * - * The integral component uses a exponential moving average approach instead of - * an actual sliding window. The advantage is that we don't need to keep an - * array of the last N error values and computation is easier. - * - * Once we have the adj value, we map it to a rate by means of a learning - * algorithm. This algorithm keeps the state of the percentual failed frames - * difference between rates. The behaviour of the lowest available rate is kept - * as a reference value, and every time we switch between two rates, we compute - * the difference between the failed frames each rate exhibited. By doing so, - * we compare behaviours which different rates exhibited in adjacent timeslices, - * thus the comparison is minimally affected by external conditions. This - * difference gets propagated to the whole set of measurements, so that the - * reference is always the same. Periodically, we normalize this set so that - * recent events weigh the most. By comparing the adj value with this set, we - * avoid pejorative switches to lower rates and allow for switches to higher - * rates if they behaved well. - * - * Note that for the computations we use a fixed-point representation to avoid - * floating point arithmetic. Hence, all values are shifted left by - * RC_PID_ARITH_SHIFT. - */ - - -/* Adjust the rate while ensuring that we won't switch to a lower rate if it - * exhibited a worse failed frames behaviour and we'll choose the highest rate - * whose failed frames behaviour is not worse than the one of the original rate - * target. While at it, check that the new rate is valid. */ -static void rate_control_pid_adjust_rate(struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, - struct rc_pid_sta_info *spinfo, int adj, - struct rc_pid_rateinfo *rinfo) -{ - int cur_sorted, new_sorted, probe, tmp, n_bitrates, band; - int cur = spinfo->txrate_idx; - - band = sband->band; - n_bitrates = sband->n_bitrates; - - /* Map passed arguments to sorted values. */ - cur_sorted = rinfo[cur].rev_index; - new_sorted = cur_sorted + adj; - - /* Check limits. */ - if (new_sorted < 0) - new_sorted = rinfo[0].rev_index; - else if (new_sorted >= n_bitrates) - new_sorted = rinfo[n_bitrates - 1].rev_index; - - tmp = new_sorted; - - if (adj < 0) { - /* Ensure that the rate decrease isn't disadvantageous. */ - for (probe = cur_sorted; probe >= new_sorted; probe--) - if (rinfo[probe].diff <= rinfo[cur_sorted].diff && - rate_supported(sta, band, rinfo[probe].index)) - tmp = probe; - } else { - /* Look for rate increase with zero (or below) cost. */ - for (probe = new_sorted + 1; probe < n_bitrates; probe++) - if (rinfo[probe].diff <= rinfo[new_sorted].diff && - rate_supported(sta, band, rinfo[probe].index)) - tmp = probe; - } - - /* Fit the rate found to the nearest supported rate. */ - do { - if (rate_supported(sta, band, rinfo[tmp].index)) { - spinfo->txrate_idx = rinfo[tmp].index; - break; - } - if (adj < 0) - tmp--; - else - tmp++; - } while (tmp < n_bitrates && tmp >= 0); - -#ifdef CONFIG_MAC80211_DEBUGFS - rate_control_pid_event_rate_change(&spinfo->events, - spinfo->txrate_idx, - sband->bitrates[spinfo->txrate_idx].bitrate); -#endif -} - -/* Normalize the failed frames per-rate differences. */ -static void rate_control_pid_normalize(struct rc_pid_info *pinfo, int l) -{ - int i, norm_offset = pinfo->norm_offset; - struct rc_pid_rateinfo *r = pinfo->rinfo; - - if (r[0].diff > norm_offset) - r[0].diff -= norm_offset; - else if (r[0].diff < -norm_offset) - r[0].diff += norm_offset; - for (i = 0; i < l - 1; i++) - if (r[i + 1].diff > r[i].diff + norm_offset) - r[i + 1].diff -= norm_offset; - else if (r[i + 1].diff <= r[i].diff) - r[i + 1].diff += norm_offset; -} - -static void rate_control_pid_sample(struct rc_pid_info *pinfo, - struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, - struct rc_pid_sta_info *spinfo) -{ - struct rc_pid_rateinfo *rinfo = pinfo->rinfo; - u32 pf; - s32 err_avg; - u32 err_prop; - u32 err_int; - u32 err_der; - int adj, i, j, tmp; - unsigned long period; - - /* In case nothing happened during the previous control interval, turn - * the sharpening factor on. */ - period = msecs_to_jiffies(pinfo->sampling_period); - if (jiffies - spinfo->last_sample > 2 * period) - spinfo->sharp_cnt = pinfo->sharpen_duration; - - spinfo->last_sample = jiffies; - - /* This should never happen, but in case, we assume the old sample is - * still a good measurement and copy it. */ - if (unlikely(spinfo->tx_num_xmit == 0)) - pf = spinfo->last_pf; - else - pf = spinfo->tx_num_failed * 100 / spinfo->tx_num_xmit; - - spinfo->tx_num_xmit = 0; - spinfo->tx_num_failed = 0; - - /* If we just switched rate, update the rate behaviour info. */ - if (pinfo->oldrate != spinfo->txrate_idx) { - - i = rinfo[pinfo->oldrate].rev_index; - j = rinfo[spinfo->txrate_idx].rev_index; - - tmp = (pf - spinfo->last_pf); - tmp = RC_PID_DO_ARITH_RIGHT_SHIFT(tmp, RC_PID_ARITH_SHIFT); - - rinfo[j].diff = rinfo[i].diff + tmp; - pinfo->oldrate = spinfo->txrate_idx; - } - rate_control_pid_normalize(pinfo, sband->n_bitrates); - - /* Compute the proportional, integral and derivative errors. */ - err_prop = (pinfo->target - pf) << RC_PID_ARITH_SHIFT; - - err_avg = spinfo->err_avg_sc >> pinfo->smoothing_shift; - spinfo->err_avg_sc = spinfo->err_avg_sc - err_avg + err_prop; - err_int = spinfo->err_avg_sc >> pinfo->smoothing_shift; - - err_der = (pf - spinfo->last_pf) * - (1 + pinfo->sharpen_factor * spinfo->sharp_cnt); - spinfo->last_pf = pf; - if (spinfo->sharp_cnt) - spinfo->sharp_cnt--; - -#ifdef CONFIG_MAC80211_DEBUGFS - rate_control_pid_event_pf_sample(&spinfo->events, pf, err_prop, err_int, - err_der); -#endif - - /* Compute the controller output. */ - adj = (err_prop * pinfo->coeff_p + err_int * pinfo->coeff_i - + err_der * pinfo->coeff_d); - adj = RC_PID_DO_ARITH_RIGHT_SHIFT(adj, 2 * RC_PID_ARITH_SHIFT); - - /* Change rate. */ - if (adj) - rate_control_pid_adjust_rate(sband, sta, spinfo, adj, rinfo); -} - -static void rate_control_pid_tx_status(void *priv, struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, void *priv_sta, - struct sk_buff *skb) -{ - struct rc_pid_info *pinfo = priv; - struct rc_pid_sta_info *spinfo = priv_sta; - unsigned long period; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - - if (!spinfo) - return; - - /* Ignore all frames that were sent with a different rate than the rate - * we currently advise mac80211 to use. */ - if (info->status.rates[0].idx != spinfo->txrate_idx) - return; - - spinfo->tx_num_xmit++; - -#ifdef CONFIG_MAC80211_DEBUGFS - rate_control_pid_event_tx_status(&spinfo->events, info); -#endif - - /* We count frames that totally failed to be transmitted as two bad - * frames, those that made it out but had some retries as one good and - * one bad frame. */ - if (!(info->flags & IEEE80211_TX_STAT_ACK)) { - spinfo->tx_num_failed += 2; - spinfo->tx_num_xmit++; - } else if (info->status.rates[0].count > 1) { - spinfo->tx_num_failed++; - spinfo->tx_num_xmit++; - } - - /* Update PID controller state. */ - period = msecs_to_jiffies(pinfo->sampling_period); - if (time_after(jiffies, spinfo->last_sample + period)) - rate_control_pid_sample(pinfo, sband, sta, spinfo); -} - -static void -rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta, - void *priv_sta, - struct ieee80211_tx_rate_control *txrc) -{ - struct sk_buff *skb = txrc->skb; - struct ieee80211_supported_band *sband = txrc->sband; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct rc_pid_sta_info *spinfo = priv_sta; - int rateidx; - - if (txrc->rts) - info->control.rates[0].count = - txrc->hw->conf.long_frame_max_tx_count; - else - info->control.rates[0].count = - txrc->hw->conf.short_frame_max_tx_count; - - /* Send management frames and NO_ACK data using lowest rate. */ - if (rate_control_send_low(sta, priv_sta, txrc)) - return; - - rateidx = spinfo->txrate_idx; - - if (rateidx >= sband->n_bitrates) - rateidx = sband->n_bitrates - 1; - - info->control.rates[0].idx = rateidx; - -#ifdef CONFIG_MAC80211_DEBUGFS - rate_control_pid_event_tx_rate(&spinfo->events, - rateidx, sband->bitrates[rateidx].bitrate); -#endif -} - -static void -rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband, - struct cfg80211_chan_def *chandef, - struct ieee80211_sta *sta, void *priv_sta) -{ - struct rc_pid_sta_info *spinfo = priv_sta; - struct rc_pid_info *pinfo = priv; - struct rc_pid_rateinfo *rinfo = pinfo->rinfo; - int i, j, tmp; - bool s; - - /* TODO: This routine should consider using RSSI from previous packets - * as we need to have IEEE 802.1X auth succeed immediately after assoc.. - * Until that method is implemented, we will use the lowest supported - * rate as a workaround. */ - - /* Sort the rates. This is optimized for the most common case (i.e. - * almost-sorted CCK+OFDM rates). Kind of bubble-sort with reversed - * mapping too. */ - for (i = 0; i < sband->n_bitrates; i++) { - rinfo[i].index = i; - rinfo[i].rev_index = i; - if (RC_PID_FAST_START) - rinfo[i].diff = 0; - else - rinfo[i].diff = i * pinfo->norm_offset; - } - for (i = 1; i < sband->n_bitrates; i++) { - s = false; - for (j = 0; j < sband->n_bitrates - i; j++) - if (unlikely(sband->bitrates[rinfo[j].index].bitrate > - sband->bitrates[rinfo[j + 1].index].bitrate)) { - tmp = rinfo[j].index; - rinfo[j].index = rinfo[j + 1].index; - rinfo[j + 1].index = tmp; - rinfo[rinfo[j].index].rev_index = j; - rinfo[rinfo[j + 1].index].rev_index = j + 1; - s = true; - } - if (!s) - break; - } - - spinfo->txrate_idx = rate_lowest_index(sband, sta); -} - -static void *rate_control_pid_alloc(struct ieee80211_hw *hw, - struct dentry *debugfsdir) -{ - struct rc_pid_info *pinfo; - struct rc_pid_rateinfo *rinfo; - struct ieee80211_supported_band *sband; - int i, max_rates = 0; -#ifdef CONFIG_MAC80211_DEBUGFS - struct rc_pid_debugfs_entries *de; -#endif - - pinfo = kmalloc(sizeof(*pinfo), GFP_ATOMIC); - if (!pinfo) - return NULL; - - for (i = 0; i < IEEE80211_NUM_BANDS; i++) { - sband = hw->wiphy->bands[i]; - if (sband && sband->n_bitrates > max_rates) - max_rates = sband->n_bitrates; - } - - rinfo = kmalloc(sizeof(*rinfo) * max_rates, GFP_ATOMIC); - if (!rinfo) { - kfree(pinfo); - return NULL; - } - - pinfo->target = RC_PID_TARGET_PF; - pinfo->sampling_period = RC_PID_INTERVAL; - pinfo->coeff_p = RC_PID_COEFF_P; - pinfo->coeff_i = RC_PID_COEFF_I; - pinfo->coeff_d = RC_PID_COEFF_D; - pinfo->smoothing_shift = RC_PID_SMOOTHING_SHIFT; - pinfo->sharpen_factor = RC_PID_SHARPENING_FACTOR; - pinfo->sharpen_duration = RC_PID_SHARPENING_DURATION; - pinfo->norm_offset = RC_PID_NORM_OFFSET; - pinfo->rinfo = rinfo; - pinfo->oldrate = 0; - -#ifdef CONFIG_MAC80211_DEBUGFS - de = &pinfo->dentries; - de->target = debugfs_create_u32("target_pf", S_IRUSR | S_IWUSR, - debugfsdir, &pinfo->target); - de->sampling_period = debugfs_create_u32("sampling_period", - S_IRUSR | S_IWUSR, debugfsdir, - &pinfo->sampling_period); - de->coeff_p = debugfs_create_u32("coeff_p", S_IRUSR | S_IWUSR, - debugfsdir, (u32 *)&pinfo->coeff_p); - de->coeff_i = debugfs_create_u32("coeff_i", S_IRUSR | S_IWUSR, - debugfsdir, (u32 *)&pinfo->coeff_i); - de->coeff_d = debugfs_create_u32("coeff_d", S_IRUSR | S_IWUSR, - debugfsdir, (u32 *)&pinfo->coeff_d); - de->smoothing_shift = debugfs_create_u32("smoothing_shift", - S_IRUSR | S_IWUSR, debugfsdir, - &pinfo->smoothing_shift); - de->sharpen_factor = debugfs_create_u32("sharpen_factor", - S_IRUSR | S_IWUSR, debugfsdir, - &pinfo->sharpen_factor); - de->sharpen_duration = debugfs_create_u32("sharpen_duration", - S_IRUSR | S_IWUSR, debugfsdir, - &pinfo->sharpen_duration); - de->norm_offset = debugfs_create_u32("norm_offset", - S_IRUSR | S_IWUSR, debugfsdir, - &pinfo->norm_offset); -#endif - - return pinfo; -} - -static void rate_control_pid_free(void *priv) -{ - struct rc_pid_info *pinfo = priv; -#ifdef CONFIG_MAC80211_DEBUGFS - struct rc_pid_debugfs_entries *de = &pinfo->dentries; - - debugfs_remove(de->norm_offset); - debugfs_remove(de->sharpen_duration); - debugfs_remove(de->sharpen_factor); - debugfs_remove(de->smoothing_shift); - debugfs_remove(de->coeff_d); - debugfs_remove(de->coeff_i); - debugfs_remove(de->coeff_p); - debugfs_remove(de->sampling_period); - debugfs_remove(de->target); -#endif - - kfree(pinfo->rinfo); - kfree(pinfo); -} - -static void *rate_control_pid_alloc_sta(void *priv, struct ieee80211_sta *sta, - gfp_t gfp) -{ - struct rc_pid_sta_info *spinfo; - - spinfo = kzalloc(sizeof(*spinfo), gfp); - if (spinfo == NULL) - return NULL; - - spinfo->last_sample = jiffies; - -#ifdef CONFIG_MAC80211_DEBUGFS - spin_lock_init(&spinfo->events.lock); - init_waitqueue_head(&spinfo->events.waitqueue); -#endif - - return spinfo; -} - -static void rate_control_pid_free_sta(void *priv, struct ieee80211_sta *sta, - void *priv_sta) -{ - kfree(priv_sta); -} - -static const struct rate_control_ops mac80211_rcpid = { - .name = "pid", - .tx_status = rate_control_pid_tx_status, - .get_rate = rate_control_pid_get_rate, - .rate_init = rate_control_pid_rate_init, - .alloc = rate_control_pid_alloc, - .free = rate_control_pid_free, - .alloc_sta = rate_control_pid_alloc_sta, - .free_sta = rate_control_pid_free_sta, -#ifdef CONFIG_MAC80211_DEBUGFS - .add_sta_debugfs = rate_control_pid_add_sta_debugfs, - .remove_sta_debugfs = rate_control_pid_remove_sta_debugfs, -#endif -}; - -int __init rc80211_pid_init(void) -{ - return ieee80211_rate_control_register(&mac80211_rcpid); -} - -void rc80211_pid_exit(void) -{ - ieee80211_rate_control_unregister(&mac80211_rcpid); -} diff --git a/net/mac80211/rc80211_pid_debugfs.c b/net/mac80211/rc80211_pid_debugfs.c deleted file mode 100644 index 6ff134650a84..000000000000 --- a/net/mac80211/rc80211_pid_debugfs.c +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright 2007, Mattias Nissler - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "rate.h" - -#include "rc80211_pid.h" - -static void rate_control_pid_event(struct rc_pid_event_buffer *buf, - enum rc_pid_event_type type, - union rc_pid_event_data *data) -{ - struct rc_pid_event *ev; - unsigned long status; - - spin_lock_irqsave(&buf->lock, status); - ev = &(buf->ring[buf->next_entry]); - buf->next_entry = (buf->next_entry + 1) % RC_PID_EVENT_RING_SIZE; - - ev->timestamp = jiffies; - ev->id = buf->ev_count++; - ev->type = type; - ev->data = *data; - - spin_unlock_irqrestore(&buf->lock, status); - - wake_up_all(&buf->waitqueue); -} - -void rate_control_pid_event_tx_status(struct rc_pid_event_buffer *buf, - struct ieee80211_tx_info *stat) -{ - union rc_pid_event_data evd; - - evd.flags = stat->flags; - memcpy(&evd.tx_status, stat, sizeof(struct ieee80211_tx_info)); - rate_control_pid_event(buf, RC_PID_EVENT_TYPE_TX_STATUS, &evd); -} - -void rate_control_pid_event_rate_change(struct rc_pid_event_buffer *buf, - int index, int rate) -{ - union rc_pid_event_data evd; - - evd.index = index; - evd.rate = rate; - rate_control_pid_event(buf, RC_PID_EVENT_TYPE_RATE_CHANGE, &evd); -} - -void rate_control_pid_event_tx_rate(struct rc_pid_event_buffer *buf, - int index, int rate) -{ - union rc_pid_event_data evd; - - evd.index = index; - evd.rate = rate; - rate_control_pid_event(buf, RC_PID_EVENT_TYPE_TX_RATE, &evd); -} - -void rate_control_pid_event_pf_sample(struct rc_pid_event_buffer *buf, - s32 pf_sample, s32 prop_err, - s32 int_err, s32 der_err) -{ - union rc_pid_event_data evd; - - evd.pf_sample = pf_sample; - evd.prop_err = prop_err; - evd.int_err = int_err; - evd.der_err = der_err; - rate_control_pid_event(buf, RC_PID_EVENT_TYPE_PF_SAMPLE, &evd); -} - -static int rate_control_pid_events_open(struct inode *inode, struct file *file) -{ - struct rc_pid_sta_info *sinfo = inode->i_private; - struct rc_pid_event_buffer *events = &sinfo->events; - struct rc_pid_events_file_info *file_info; - unsigned long status; - - /* Allocate a state struct */ - file_info = kmalloc(sizeof(*file_info), GFP_KERNEL); - if (file_info == NULL) - return -ENOMEM; - - spin_lock_irqsave(&events->lock, status); - - file_info->next_entry = events->next_entry; - file_info->events = events; - - spin_unlock_irqrestore(&events->lock, status); - - file->private_data = file_info; - - return 0; -} - -static int rate_control_pid_events_release(struct inode *inode, - struct file *file) -{ - struct rc_pid_events_file_info *file_info = file->private_data; - - kfree(file_info); - - return 0; -} - -static unsigned int rate_control_pid_events_poll(struct file *file, - poll_table *wait) -{ - struct rc_pid_events_file_info *file_info = file->private_data; - - poll_wait(file, &file_info->events->waitqueue, wait); - - return POLLIN | POLLRDNORM; -} - -#define RC_PID_PRINT_BUF_SIZE 64 - -static ssize_t rate_control_pid_events_read(struct file *file, char __user *buf, - size_t length, loff_t *offset) -{ - struct rc_pid_events_file_info *file_info = file->private_data; - struct rc_pid_event_buffer *events = file_info->events; - struct rc_pid_event *ev; - char pb[RC_PID_PRINT_BUF_SIZE]; - int ret; - int p; - unsigned long status; - - /* Check if there is something to read. */ - if (events->next_entry == file_info->next_entry) { - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; - - /* Wait */ - ret = wait_event_interruptible(events->waitqueue, - events->next_entry != file_info->next_entry); - - if (ret) - return ret; - } - - /* Write out one event per call. I don't care whether it's a little - * inefficient, this is debugging code anyway. */ - spin_lock_irqsave(&events->lock, status); - - /* Get an event */ - ev = &(events->ring[file_info->next_entry]); - file_info->next_entry = (file_info->next_entry + 1) % - RC_PID_EVENT_RING_SIZE; - - /* Print information about the event. Note that userspace needs to - * provide large enough buffers. */ - length = length < RC_PID_PRINT_BUF_SIZE ? - length : RC_PID_PRINT_BUF_SIZE; - p = scnprintf(pb, length, "%u %lu ", ev->id, ev->timestamp); - switch (ev->type) { - case RC_PID_EVENT_TYPE_TX_STATUS: - p += scnprintf(pb + p, length - p, "tx_status %u %u", - !(ev->data.flags & IEEE80211_TX_STAT_ACK), - ev->data.tx_status.status.rates[0].idx); - break; - case RC_PID_EVENT_TYPE_RATE_CHANGE: - p += scnprintf(pb + p, length - p, "rate_change %d %d", - ev->data.index, ev->data.rate); - break; - case RC_PID_EVENT_TYPE_TX_RATE: - p += scnprintf(pb + p, length - p, "tx_rate %d %d", - ev->data.index, ev->data.rate); - break; - case RC_PID_EVENT_TYPE_PF_SAMPLE: - p += scnprintf(pb + p, length - p, - "pf_sample %d %d %d %d", - ev->data.pf_sample, ev->data.prop_err, - ev->data.int_err, ev->data.der_err); - break; - } - p += scnprintf(pb + p, length - p, "\n"); - - spin_unlock_irqrestore(&events->lock, status); - - if (copy_to_user(buf, pb, p)) - return -EFAULT; - - return p; -} - -#undef RC_PID_PRINT_BUF_SIZE - -static const struct file_operations rc_pid_fop_events = { - .owner = THIS_MODULE, - .read = rate_control_pid_events_read, - .poll = rate_control_pid_events_poll, - .open = rate_control_pid_events_open, - .release = rate_control_pid_events_release, - .llseek = noop_llseek, -}; - -void rate_control_pid_add_sta_debugfs(void *priv, void *priv_sta, - struct dentry *dir) -{ - struct rc_pid_sta_info *spinfo = priv_sta; - - spinfo->events_entry = debugfs_create_file("rc_pid_events", S_IRUGO, - dir, spinfo, - &rc_pid_fop_events); -} - -void rate_control_pid_remove_sta_debugfs(void *priv, void *priv_sta) -{ - struct rc_pid_sta_info *spinfo = priv_sta; - - debugfs_remove(spinfo->events_entry); -} From 5ac2e35030113ed881ce9ad413d80f13ffe5b5a0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 27 May 2014 16:32:27 +0200 Subject: [PATCH 110/274] mac80211: fix station/driver powersave race It is currently possible to have a race due to the station PS unblock work like this: * station goes to sleep with frames buffered in the driver * driver blocks wakeup * station wakes up again * driver flushes/returns frames, and unblocks, which schedules the unblock work * unblock work starts to run, and checks that the station is awake (i.e. that the WLAN_STA_PS_STA flag isn't set) * we process a received frame with PM=1, setting the flag again * ieee80211_sta_ps_deliver_wakeup() runs, delivering all frames to the driver, and then clearing the WLAN_STA_PS_DRIVER and WLAN_STA_PS_STA flags In this scenario, mac80211 will think that the station is awake, while it really is asleep, and any TX'ed frames should be filtered by the device (it will know that the station is sleeping) but then passed to mac80211 again, which will not buffer it either as it thinks the station is awake, and eventually the packets will be dropped. Fix this by moving the clearing of the flags to exactly where we learn about the situation. This creates a problem of reordering, so introduce another flag indicating that delivery is being done, this new flag also queues frames and is cleared only while the spinlock is held (which the queuing code also holds) so that any concurrent delivery/TX is handled correctly. Reported-by: Andrei Otcheretianski Signed-off-by: Johannes Berg --- net/mac80211/rx.c | 2 ++ net/mac80211/sta_info.c | 65 +++++++++++++++++++++++++---------------- net/mac80211/sta_info.h | 7 +++-- net/mac80211/tx.c | 6 ++-- 4 files changed, 51 insertions(+), 29 deletions(-) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 394e201cde6d..5f572bed1761 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1107,6 +1107,8 @@ static void sta_ps_end(struct sta_info *sta) return; } + set_sta_flag(sta, WLAN_STA_PS_DELIVER); + clear_sta_flag(sta, WLAN_STA_PS_STA); ieee80211_sta_ps_deliver_wakeup(sta); } diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index a9b46d8ea22f..ae7c16ad5f22 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -100,7 +100,8 @@ static void __cleanup_single_sta(struct sta_info *sta) struct ps_data *ps; if (test_sta_flag(sta, WLAN_STA_PS_STA) || - test_sta_flag(sta, WLAN_STA_PS_DRIVER)) { + test_sta_flag(sta, WLAN_STA_PS_DRIVER) || + test_sta_flag(sta, WLAN_STA_PS_DELIVER)) { if (sta->sdata->vif.type == NL80211_IFTYPE_AP || sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) ps = &sdata->bss->ps; @@ -111,6 +112,7 @@ static void __cleanup_single_sta(struct sta_info *sta) clear_sta_flag(sta, WLAN_STA_PS_STA); clear_sta_flag(sta, WLAN_STA_PS_DRIVER); + clear_sta_flag(sta, WLAN_STA_PS_DELIVER); atomic_dec(&ps->num_sta_ps); sta_info_recalc_tim(sta); @@ -125,7 +127,7 @@ static void __cleanup_single_sta(struct sta_info *sta) if (ieee80211_vif_is_mesh(&sdata->vif)) mesh_sta_cleanup(sta); - cancel_work_sync(&sta->drv_unblock_wk); + cancel_work_sync(&sta->drv_deliver_wk); /* * Destroy aggregation state here. It would be nice to wait for the @@ -253,33 +255,23 @@ static void sta_info_hash_add(struct ieee80211_local *local, rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta); } -static void sta_unblock(struct work_struct *wk) +static void sta_deliver_ps_frames(struct work_struct *wk) { struct sta_info *sta; - sta = container_of(wk, struct sta_info, drv_unblock_wk); + sta = container_of(wk, struct sta_info, drv_deliver_wk); if (sta->dead) return; - if (!test_sta_flag(sta, WLAN_STA_PS_STA)) { - local_bh_disable(); + local_bh_disable(); + if (!test_sta_flag(sta, WLAN_STA_PS_STA)) ieee80211_sta_ps_deliver_wakeup(sta); - local_bh_enable(); - } else if (test_and_clear_sta_flag(sta, WLAN_STA_PSPOLL)) { - clear_sta_flag(sta, WLAN_STA_PS_DRIVER); - - local_bh_disable(); + else if (test_and_clear_sta_flag(sta, WLAN_STA_PSPOLL)) ieee80211_sta_ps_deliver_poll_response(sta); - local_bh_enable(); - } else if (test_and_clear_sta_flag(sta, WLAN_STA_UAPSD)) { - clear_sta_flag(sta, WLAN_STA_PS_DRIVER); - - local_bh_disable(); + else if (test_and_clear_sta_flag(sta, WLAN_STA_UAPSD)) ieee80211_sta_ps_deliver_uapsd(sta); - local_bh_enable(); - } else - clear_sta_flag(sta, WLAN_STA_PS_DRIVER); + local_bh_enable(); } static int sta_prepare_rate_control(struct ieee80211_local *local, @@ -341,7 +333,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, spin_lock_init(&sta->lock); spin_lock_init(&sta->ps_lock); - INIT_WORK(&sta->drv_unblock_wk, sta_unblock); + INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames); INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work); mutex_init(&sta->ampdu_mlme.mtx); #ifdef CONFIG_MAC80211_MESH @@ -1141,8 +1133,15 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) } ieee80211_add_pending_skbs(local, &pending); - clear_sta_flag(sta, WLAN_STA_PS_DRIVER); - clear_sta_flag(sta, WLAN_STA_PS_STA); + + /* now we're no longer in the deliver code */ + clear_sta_flag(sta, WLAN_STA_PS_DELIVER); + + /* The station might have polled and then woken up before we responded, + * so clear these flags now to avoid them sticking around. + */ + clear_sta_flag(sta, WLAN_STA_PSPOLL); + clear_sta_flag(sta, WLAN_STA_UAPSD); spin_unlock(&sta->ps_lock); atomic_dec(&ps->num_sta_ps); @@ -1543,10 +1542,26 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw, trace_api_sta_block_awake(sta->local, pubsta, block); - if (block) + if (block) { set_sta_flag(sta, WLAN_STA_PS_DRIVER); - else if (test_sta_flag(sta, WLAN_STA_PS_DRIVER)) - ieee80211_queue_work(hw, &sta->drv_unblock_wk); + return; + } + + if (!test_sta_flag(sta, WLAN_STA_PS_DRIVER)) + return; + + if (!test_sta_flag(sta, WLAN_STA_PS_STA)) { + set_sta_flag(sta, WLAN_STA_PS_DELIVER); + clear_sta_flag(sta, WLAN_STA_PS_DRIVER); + ieee80211_queue_work(hw, &sta->drv_deliver_wk); + } else if (test_sta_flag(sta, WLAN_STA_PSPOLL) || + test_sta_flag(sta, WLAN_STA_UAPSD)) { + /* must be asleep in this case */ + clear_sta_flag(sta, WLAN_STA_PS_DRIVER); + ieee80211_queue_work(hw, &sta->drv_deliver_wk); + } else { + clear_sta_flag(sta, WLAN_STA_PS_DRIVER); + } } EXPORT_SYMBOL(ieee80211_sta_block_awake); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 4acc5fc402fa..dee0b645b34c 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -58,6 +58,8 @@ * @WLAN_STA_TOFFSET_KNOWN: toffset calculated for this station is valid. * @WLAN_STA_MPSP_OWNER: local STA is owner of a mesh Peer Service Period. * @WLAN_STA_MPSP_RECIPIENT: local STA is recipient of a MPSP. + * @WLAN_STA_PS_DELIVER: station woke up, but we're still blocking TX + * until pending frames are delivered */ enum ieee80211_sta_info_flags { WLAN_STA_AUTH, @@ -82,6 +84,7 @@ enum ieee80211_sta_info_flags { WLAN_STA_TOFFSET_KNOWN, WLAN_STA_MPSP_OWNER, WLAN_STA_MPSP_RECIPIENT, + WLAN_STA_PS_DELIVER, }; #define ADDBA_RESP_INTERVAL HZ @@ -265,7 +268,7 @@ struct ieee80211_tx_latency_stat { * @last_rx_rate_vht_nss: rx status nss of last data packet * @lock: used for locking all fields that require locking, see comments * in the header file. - * @drv_unblock_wk: used for driver PS unblocking + * @drv_deliver_wk: used for delivering frames after driver PS unblocking * @listen_interval: listen interval of this station, when we're acting as AP * @_flags: STA flags, see &enum ieee80211_sta_info_flags, do not use directly * @ps_lock: used for powersave (when mac80211 is the AP) related locking @@ -345,7 +348,7 @@ struct sta_info { void *rate_ctrl_priv; spinlock_t lock; - struct work_struct drv_unblock_wk; + struct work_struct drv_deliver_wk; u16 listen_interval; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 5214686d9fd1..8170d9945d6d 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -469,7 +469,8 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) return TX_CONTINUE; if (unlikely((test_sta_flag(sta, WLAN_STA_PS_STA) || - test_sta_flag(sta, WLAN_STA_PS_DRIVER)) && + test_sta_flag(sta, WLAN_STA_PS_DRIVER) || + test_sta_flag(sta, WLAN_STA_PS_DELIVER)) && !(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER))) { int ac = skb_get_queue_mapping(tx->skb); @@ -486,7 +487,8 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) * ahead and Tx the packet. */ if (!test_sta_flag(sta, WLAN_STA_PS_STA) && - !test_sta_flag(sta, WLAN_STA_PS_DRIVER)) { + !test_sta_flag(sta, WLAN_STA_PS_DRIVER) && + !test_sta_flag(sta, WLAN_STA_PS_DELIVER)) { spin_unlock(&sta->ps_lock); return TX_CONTINUE; } From 2b470c39e895dfde5413b0a85fac928a353de48d Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Wed, 4 Jun 2014 09:27:31 -0400 Subject: [PATCH 111/274] mac80211: remove ignore_plink_timer flag The mesh_plink code is doing some interesting things with the ignore_plink_timer flag. It seems the original intent was to handle this race: cpu 0 cpu 1 ----- ----- start timer handler for state X acquire sta_lock change state from X to Y mod_timer() / del_timer() release sta_lock acquire sta_lock execute state Y timer too soon However, using the mod_timer()/del_timer() return values to detect these cases is broken. As a result, timers get ignored unnecessarily, and stations can get stuck in the peering state machine. Instead, we can detect the case by looking at the timer expiration. In the case of del_timer, just ignore the timers in the following (LISTEN/ESTAB) states since they won't have timers anyway. Signed-off-by: Bob Copeland Signed-off-by: Johannes Berg --- net/mac80211/mesh_plink.c | 30 +++++++++++++++++++++++------- net/mac80211/sta_info.h | 2 -- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index e8f60aa2e848..63b874101b27 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -551,11 +551,30 @@ static void mesh_plink_timer(unsigned long data) return; spin_lock_bh(&sta->lock); - if (sta->ignore_plink_timer) { - sta->ignore_plink_timer = false; + + /* If a timer fires just before a state transition on another CPU, + * we may have already extended the timeout and changed state by the + * time we've acquired the lock and arrived here. In that case, + * skip this timer and wait for the new one. + */ + if (time_before(jiffies, sta->plink_timer.expires)) { + mpl_dbg(sta->sdata, + "Ignoring timer for %pM in state %s (timer adjusted)", + sta->sta.addr, mplstates[sta->plink_state]); spin_unlock_bh(&sta->lock); return; } + + /* del_timer() and handler may race when entering these states */ + if (sta->plink_state == NL80211_PLINK_LISTEN || + sta->plink_state == NL80211_PLINK_ESTAB) { + mpl_dbg(sta->sdata, + "Ignoring timer for %pM in state %s (timer deleted)", + sta->sta.addr, mplstates[sta->plink_state]); + spin_unlock_bh(&sta->lock); + return; + } + mpl_dbg(sta->sdata, "Mesh plink timer for %pM fired on state %s\n", sta->sta.addr, mplstates[sta->plink_state]); @@ -773,9 +792,7 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata, break; case CNF_ACPT: sta->plink_state = NL80211_PLINK_CNF_RCVD; - if (!mod_plink_timer(sta, - mshcfg->dot11MeshConfirmTimeout)) - sta->ignore_plink_timer = true; + mod_plink_timer(sta, mshcfg->dot11MeshConfirmTimeout); break; default: break; @@ -834,8 +851,7 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata, case NL80211_PLINK_HOLDING: switch (event) { case CLS_ACPT: - if (del_timer(&sta->plink_timer)) - sta->ignore_plink_timer = 1; + del_timer(&sta->plink_timer); mesh_plink_fsm_restart(sta); break; case OPN_ACPT: diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index dee0b645b34c..159cac903ce8 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -306,7 +306,6 @@ struct ieee80211_tx_latency_stat { * @plid: Peer link ID * @reason: Cancel reason on PLINK_HOLDING state * @plink_retries: Retries in establishment - * @ignore_plink_timer: ignore the peer-link timer (used internally) * @plink_state: peer link state * @plink_timeout: timeout of peer link * @plink_timer: peer link watch timer @@ -421,7 +420,6 @@ struct sta_info { u16 plid; u16 reason; u8 plink_retries; - bool ignore_plink_timer; enum nl80211_plink_state plink_state; u32 plink_timeout; struct timer_list plink_timer; From b314c669905cbef00fed33028c61c96efeea08f5 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Wed, 4 Jun 2014 14:03:49 +0200 Subject: [PATCH 112/274] trivial: net/mac80211/mesh.c: fix typo s/Substract/Subtract/ Signed-off-by: Antonio Ospite Cc: Luis Carlos Cobo Cc: Johannes Berg Cc: linux-wireless@vger.kernel.org Signed-off-by: Johannes Berg --- net/mac80211/mesh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 6495a3f0428d..da164685b524 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -1122,7 +1122,7 @@ static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata, mgmt_fwd = (struct ieee80211_mgmt *) skb_put(skb, len); /* offset_ttl is based on whether the secondary channel - * offset is available or not. Substract 1 from the mesh TTL + * offset is available or not. Subtract 1 from the mesh TTL * and disable the initiator flag before forwarding. */ offset_ttl = (len < 42) ? 7 : 10; From ba9030c20a2def223d9b993cb3dfdd3aab3d2b31 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 4 Jun 2014 17:06:23 +0200 Subject: [PATCH 113/274] mac80211: remove weak WEP IV accounting Since WEP is practically dead, there seems very little point in keeping WEP weak IV accounting. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 3 +-- net/mac80211/debugfs_sta.c | 1 - net/mac80211/sta_info.h | 2 -- net/mac80211/wep.c | 20 -------------------- 4 files changed, 1 insertion(+), 25 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index d7513a503be1..f8d065480ba9 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -603,7 +603,7 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) } static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = { - "rx_packets", "rx_bytes", "wep_weak_iv_count", + "rx_packets", "rx_bytes", "rx_duplicates", "rx_fragments", "rx_dropped", "tx_packets", "tx_bytes", "tx_fragments", "tx_filtered", "tx_retry_failed", "tx_retries", @@ -651,7 +651,6 @@ static void ieee80211_get_et_stats(struct wiphy *wiphy, do { \ data[i++] += sta->rx_packets; \ data[i++] += sta->rx_bytes; \ - data[i++] += sta->wep_weak_iv_count; \ data[i++] += sta->num_duplicates; \ data[i++] += sta->rx_fragments; \ data[i++] += sta->rx_dropped; \ diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 2ecb4deddb5d..ee82e1f0a9e7 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -587,7 +587,6 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta) DEBUGFS_ADD_COUNTER(tx_filtered, tx_filtered_count); DEBUGFS_ADD_COUNTER(tx_retry_failed, tx_retry_failed); DEBUGFS_ADD_COUNTER(tx_retry_count, tx_retry_count); - DEBUGFS_ADD_COUNTER(wep_weak_iv_count, wep_weak_iv_count); if (sizeof(sta->driver_buffered_tids) == sizeof(u32)) debugfs_create_x32("driver_buffered_tids", 0400, diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 159cac903ce8..fa7ce6f8593b 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -281,7 +281,6 @@ struct ieee80211_tx_latency_stat { * @driver_buffered_tids: bitmap of TIDs the driver has data buffered on * @rx_packets: Number of MSDUs received from this STA * @rx_bytes: Number of bytes received from this STA - * @wep_weak_iv_count: number of weak WEP IVs received from this station * @last_rx: time (in jiffies) when last frame was received from this STA * @last_connected: time (in seconds) when a station got connected * @num_duplicates: number of duplicate frames received from this STA @@ -369,7 +368,6 @@ struct sta_info { /* Updated from RX path only, no locking requirements */ unsigned long rx_packets; u64 rx_bytes; - unsigned long wep_weak_iv_count; unsigned long last_rx; long last_connected; unsigned long num_duplicates; diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c index 6ee2b5863572..9181fb6d6437 100644 --- a/net/mac80211/wep.c +++ b/net/mac80211/wep.c @@ -271,22 +271,6 @@ static int ieee80211_wep_decrypt(struct ieee80211_local *local, return ret; } - -static bool ieee80211_wep_is_weak_iv(struct sk_buff *skb, - struct ieee80211_key *key) -{ - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - unsigned int hdrlen; - u8 *ivpos; - u32 iv; - - hdrlen = ieee80211_hdrlen(hdr->frame_control); - ivpos = skb->data + hdrlen; - iv = (ivpos[0] << 16) | (ivpos[1] << 8) | ivpos[2]; - - return ieee80211_wep_weak_iv(iv, key->conf.keylen); -} - ieee80211_rx_result ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx) { @@ -301,16 +285,12 @@ ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx) if (!(status->flag & RX_FLAG_DECRYPTED)) { if (skb_linearize(rx->skb)) return RX_DROP_UNUSABLE; - if (rx->sta && ieee80211_wep_is_weak_iv(rx->skb, rx->key)) - rx->sta->wep_weak_iv_count++; if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key)) return RX_DROP_UNUSABLE; } else if (!(status->flag & RX_FLAG_IV_STRIPPED)) { if (!pskb_may_pull(rx->skb, ieee80211_hdrlen(fc) + IEEE80211_WEP_IV_LEN)) return RX_DROP_UNUSABLE; - if (rx->sta && ieee80211_wep_is_weak_iv(rx->skb, rx->key)) - rx->sta->wep_weak_iv_count++; ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key); /* remove ICV */ if (pskb_trim(rx->skb, rx->skb->len - IEEE80211_WEP_ICV_LEN)) From b7ffbd7ef6751f6cde73082346e365738daf00d2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 4 Jun 2014 17:31:56 +0200 Subject: [PATCH 114/274] cfg80211: make ethtool the driver's responsibility Currently, cfg80211 tries to implement ethtool, but that doesn't really scale well, with all the different operations. Make the lower-level driver responsible for it, which currently only has an effect on mac80211. It will similarly not scale well at that level though, since mac80211 also has many drivers. To cleanly implement this in mac80211, introduce a new file and move some code to appropriate places. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 29 +--- net/mac80211/Makefile | 1 + net/mac80211/cfg.c | 340 ------------------------------------- net/mac80211/ethtool.c | 244 ++++++++++++++++++++++++++ net/mac80211/ieee80211_i.h | 2 + net/mac80211/iface.c | 2 + net/mac80211/sta_info.c | 134 +++++++++++++++ net/mac80211/sta_info.h | 2 + net/wireless/core.c | 3 - net/wireless/ethtool.c | 86 +--------- net/wireless/ethtool.h | 6 - net/wireless/rdev-ops.h | 48 ------ net/wireless/trace.h | 49 ------ 13 files changed, 391 insertions(+), 555 deletions(-) create mode 100644 net/mac80211/ethtool.c delete mode 100644 net/wireless/ethtool.h diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e46c437944f7..29cb4b2bee5a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2266,10 +2266,6 @@ struct cfg80211_qos_map { * * @get_antenna: Get current antenna configuration from device (tx_ant, rx_ant). * - * @set_ringparam: Set tx and rx ring sizes. - * - * @get_ringparam: Get tx and rx ring current and maximum sizes. - * * @tdls_mgmt: Transmit a TDLS management frame. * @tdls_oper: Perform a high-level TDLS operation (e.g. TDLS link setup). * @@ -2278,16 +2274,6 @@ struct cfg80211_qos_map { * * @set_noack_map: Set the NoAck Map for the TIDs. * - * @get_et_sset_count: Ethtool API to get string-set count. - * See @ethtool_ops.get_sset_count - * - * @get_et_stats: Ethtool API to get a set of u64 stats. - * See @ethtool_ops.get_ethtool_stats - * - * @get_et_strings: Ethtool API to get a set of strings to describe stats - * and perhaps other supported types of ethtool data-sets. - * See @ethtool_ops.get_strings - * * @get_channel: Get the current operating channel for the virtual interface. * For monitor interfaces, it should return %NULL unless there's a single * current monitoring channel. @@ -2503,10 +2489,6 @@ struct cfg80211_ops { int (*set_antenna)(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant); int (*get_antenna)(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant); - int (*set_ringparam)(struct wiphy *wiphy, u32 tx, u32 rx); - void (*get_ringparam)(struct wiphy *wiphy, - u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max); - int (*sched_scan_start)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_sched_scan_request *request); @@ -2529,13 +2511,6 @@ struct cfg80211_ops { struct net_device *dev, u16 noack_map); - int (*get_et_sset_count)(struct wiphy *wiphy, - struct net_device *dev, int sset); - void (*get_et_stats)(struct wiphy *wiphy, struct net_device *dev, - struct ethtool_stats *stats, u64 *data); - void (*get_et_strings)(struct wiphy *wiphy, struct net_device *dev, - u32 sset, u8 *data); - int (*get_channel)(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_chan_def *chandef); @@ -4843,6 +4818,10 @@ void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev, */ void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy); + +/* ethtool helper */ +void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info); + /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 4409bf506594..7273d2796dd1 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -17,6 +17,7 @@ mac80211-y := \ aes_ccm.o \ aes_cmac.o \ cfg.o \ + ethtool.o \ rx.o \ spectmgmt.o \ tx.o \ diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index f8d065480ba9..b6d73c14e1ae 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -468,326 +468,6 @@ void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo) rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH; } -static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) -{ - struct ieee80211_sub_if_data *sdata = sta->sdata; - struct ieee80211_local *local = sdata->local; - struct rate_control_ref *ref = local->rate_ctrl; - struct timespec uptime; - u64 packets = 0; - u32 thr = 0; - int i, ac; - - sinfo->generation = sdata->local->sta_generation; - - sinfo->filled = STATION_INFO_INACTIVE_TIME | - STATION_INFO_RX_BYTES64 | - STATION_INFO_TX_BYTES64 | - STATION_INFO_RX_PACKETS | - STATION_INFO_TX_PACKETS | - STATION_INFO_TX_RETRIES | - STATION_INFO_TX_FAILED | - STATION_INFO_TX_BITRATE | - STATION_INFO_RX_BITRATE | - STATION_INFO_RX_DROP_MISC | - STATION_INFO_BSS_PARAM | - STATION_INFO_CONNECTED_TIME | - STATION_INFO_STA_FLAGS | - STATION_INFO_BEACON_LOSS_COUNT; - - do_posix_clock_monotonic_gettime(&uptime); - sinfo->connected_time = uptime.tv_sec - sta->last_connected; - - sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); - sinfo->tx_bytes = 0; - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { - sinfo->tx_bytes += sta->tx_bytes[ac]; - packets += sta->tx_packets[ac]; - } - sinfo->tx_packets = packets; - sinfo->rx_bytes = sta->rx_bytes; - sinfo->rx_packets = sta->rx_packets; - sinfo->tx_retries = sta->tx_retry_count; - sinfo->tx_failed = sta->tx_retry_failed; - sinfo->rx_dropped_misc = sta->rx_dropped; - sinfo->beacon_loss_count = sta->beacon_loss_count; - - if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) || - (sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) { - sinfo->filled |= STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG; - if (!local->ops->get_rssi || - drv_get_rssi(local, sdata, &sta->sta, &sinfo->signal)) - sinfo->signal = (s8)sta->last_signal; - sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal); - } - if (sta->chains) { - sinfo->filled |= STATION_INFO_CHAIN_SIGNAL | - STATION_INFO_CHAIN_SIGNAL_AVG; - - sinfo->chains = sta->chains; - for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) { - sinfo->chain_signal[i] = sta->chain_signal_last[i]; - sinfo->chain_signal_avg[i] = - (s8) -ewma_read(&sta->chain_signal_avg[i]); - } - } - - sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate); - sta_set_rate_info_rx(sta, &sinfo->rxrate); - - if (ieee80211_vif_is_mesh(&sdata->vif)) { -#ifdef CONFIG_MAC80211_MESH - sinfo->filled |= STATION_INFO_LLID | - STATION_INFO_PLID | - STATION_INFO_PLINK_STATE | - STATION_INFO_LOCAL_PM | - STATION_INFO_PEER_PM | - STATION_INFO_NONPEER_PM; - - sinfo->llid = sta->llid; - sinfo->plid = sta->plid; - sinfo->plink_state = sta->plink_state; - if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) { - sinfo->filled |= STATION_INFO_T_OFFSET; - sinfo->t_offset = sta->t_offset; - } - sinfo->local_pm = sta->local_pm; - sinfo->peer_pm = sta->peer_pm; - sinfo->nonpeer_pm = sta->nonpeer_pm; -#endif - } - - sinfo->bss_param.flags = 0; - if (sdata->vif.bss_conf.use_cts_prot) - sinfo->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT; - if (sdata->vif.bss_conf.use_short_preamble) - sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE; - if (sdata->vif.bss_conf.use_short_slot) - sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME; - sinfo->bss_param.dtim_period = sdata->local->hw.conf.ps_dtim_period; - sinfo->bss_param.beacon_interval = sdata->vif.bss_conf.beacon_int; - - sinfo->sta_flags.set = 0; - sinfo->sta_flags.mask = BIT(NL80211_STA_FLAG_AUTHORIZED) | - BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | - BIT(NL80211_STA_FLAG_WME) | - BIT(NL80211_STA_FLAG_MFP) | - BIT(NL80211_STA_FLAG_AUTHENTICATED) | - BIT(NL80211_STA_FLAG_ASSOCIATED) | - BIT(NL80211_STA_FLAG_TDLS_PEER); - if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) - sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED); - if (test_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE)) - sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE); - if (test_sta_flag(sta, WLAN_STA_WME)) - sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_WME); - if (test_sta_flag(sta, WLAN_STA_MFP)) - sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP); - if (test_sta_flag(sta, WLAN_STA_AUTH)) - sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED); - if (test_sta_flag(sta, WLAN_STA_ASSOC)) - sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED); - if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) - sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER); - - /* check if the driver has a SW RC implementation */ - if (ref && ref->ops->get_expected_throughput) - thr = ref->ops->get_expected_throughput(sta->rate_ctrl_priv); - else - thr = drv_get_expected_throughput(local, &sta->sta); - - if (thr != 0) { - sinfo->filled |= STATION_INFO_EXPECTED_THROUGHPUT; - sinfo->expected_throughput = thr; - } -} - -static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = { - "rx_packets", "rx_bytes", - "rx_duplicates", "rx_fragments", "rx_dropped", - "tx_packets", "tx_bytes", "tx_fragments", - "tx_filtered", "tx_retry_failed", "tx_retries", - "beacon_loss", "sta_state", "txrate", "rxrate", "signal", - "channel", "noise", "ch_time", "ch_time_busy", - "ch_time_ext_busy", "ch_time_rx", "ch_time_tx" -}; -#define STA_STATS_LEN ARRAY_SIZE(ieee80211_gstrings_sta_stats) - -static int ieee80211_get_et_sset_count(struct wiphy *wiphy, - struct net_device *dev, - int sset) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - int rv = 0; - - if (sset == ETH_SS_STATS) - rv += STA_STATS_LEN; - - rv += drv_get_et_sset_count(sdata, sset); - - if (rv == 0) - return -EOPNOTSUPP; - return rv; -} - -static void ieee80211_get_et_stats(struct wiphy *wiphy, - struct net_device *dev, - struct ethtool_stats *stats, - u64 *data) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - struct ieee80211_chanctx_conf *chanctx_conf; - struct ieee80211_channel *channel; - struct sta_info *sta; - struct ieee80211_local *local = sdata->local; - struct station_info sinfo; - struct survey_info survey; - int i, q; -#define STA_STATS_SURVEY_LEN 7 - - memset(data, 0, sizeof(u64) * STA_STATS_LEN); - -#define ADD_STA_STATS(sta) \ - do { \ - data[i++] += sta->rx_packets; \ - data[i++] += sta->rx_bytes; \ - data[i++] += sta->num_duplicates; \ - data[i++] += sta->rx_fragments; \ - data[i++] += sta->rx_dropped; \ - \ - data[i++] += sinfo.tx_packets; \ - data[i++] += sinfo.tx_bytes; \ - data[i++] += sta->tx_fragments; \ - data[i++] += sta->tx_filtered_count; \ - data[i++] += sta->tx_retry_failed; \ - data[i++] += sta->tx_retry_count; \ - data[i++] += sta->beacon_loss_count; \ - } while (0) - - /* For Managed stations, find the single station based on BSSID - * and use that. For interface types, iterate through all available - * stations and add stats for any station that is assigned to this - * network device. - */ - - mutex_lock(&local->sta_mtx); - - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - sta = sta_info_get_bss(sdata, sdata->u.mgd.bssid); - - if (!(sta && !WARN_ON(sta->sdata->dev != dev))) - goto do_survey; - - sinfo.filled = 0; - sta_set_sinfo(sta, &sinfo); - - i = 0; - ADD_STA_STATS(sta); - - data[i++] = sta->sta_state; - - - if (sinfo.filled & STATION_INFO_TX_BITRATE) - data[i] = 100000 * - cfg80211_calculate_bitrate(&sinfo.txrate); - i++; - if (sinfo.filled & STATION_INFO_RX_BITRATE) - data[i] = 100000 * - cfg80211_calculate_bitrate(&sinfo.rxrate); - i++; - - if (sinfo.filled & STATION_INFO_SIGNAL_AVG) - data[i] = (u8)sinfo.signal_avg; - i++; - } else { - list_for_each_entry(sta, &local->sta_list, list) { - /* Make sure this station belongs to the proper dev */ - if (sta->sdata->dev != dev) - continue; - - sinfo.filled = 0; - sta_set_sinfo(sta, &sinfo); - i = 0; - ADD_STA_STATS(sta); - } - } - -do_survey: - i = STA_STATS_LEN - STA_STATS_SURVEY_LEN; - /* Get survey stats for current channel */ - survey.filled = 0; - - rcu_read_lock(); - chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); - if (chanctx_conf) - channel = chanctx_conf->def.chan; - else - channel = NULL; - rcu_read_unlock(); - - if (channel) { - q = 0; - do { - survey.filled = 0; - if (drv_get_survey(local, q, &survey) != 0) { - survey.filled = 0; - break; - } - q++; - } while (channel != survey.channel); - } - - if (survey.filled) - data[i++] = survey.channel->center_freq; - else - data[i++] = 0; - if (survey.filled & SURVEY_INFO_NOISE_DBM) - data[i++] = (u8)survey.noise; - else - data[i++] = -1LL; - if (survey.filled & SURVEY_INFO_CHANNEL_TIME) - data[i++] = survey.channel_time; - else - data[i++] = -1LL; - if (survey.filled & SURVEY_INFO_CHANNEL_TIME_BUSY) - data[i++] = survey.channel_time_busy; - else - data[i++] = -1LL; - if (survey.filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) - data[i++] = survey.channel_time_ext_busy; - else - data[i++] = -1LL; - if (survey.filled & SURVEY_INFO_CHANNEL_TIME_RX) - data[i++] = survey.channel_time_rx; - else - data[i++] = -1LL; - if (survey.filled & SURVEY_INFO_CHANNEL_TIME_TX) - data[i++] = survey.channel_time_tx; - else - data[i++] = -1LL; - - mutex_unlock(&local->sta_mtx); - - if (WARN_ON(i != STA_STATS_LEN)) - return; - - drv_get_et_stats(sdata, stats, &(data[STA_STATS_LEN])); -} - -static void ieee80211_get_et_strings(struct wiphy *wiphy, - struct net_device *dev, - u32 sset, u8 *data) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - int sz_sta_stats = 0; - - if (sset == ETH_SS_STATS) { - sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats); - memcpy(data, ieee80211_gstrings_sta_stats, sz_sta_stats); - } - drv_get_et_strings(sdata, sset, &(data[sz_sta_stats])); -} - static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev, int idx, u8 *mac, struct station_info *sinfo) { @@ -3597,21 +3277,6 @@ static int ieee80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant) return drv_get_antenna(local, tx_ant, rx_ant); } -static int ieee80211_set_ringparam(struct wiphy *wiphy, u32 tx, u32 rx) -{ - struct ieee80211_local *local = wiphy_priv(wiphy); - - return drv_set_ringparam(local, tx, rx); -} - -static void ieee80211_get_ringparam(struct wiphy *wiphy, - u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max) -{ - struct ieee80211_local *local = wiphy_priv(wiphy); - - drv_get_ringparam(local, tx, tx_max, rx, rx_max); -} - static int ieee80211_set_rekey_data(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_gtk_rekey_data *data) @@ -3843,8 +3508,6 @@ const struct cfg80211_ops mac80211_config_ops = { .mgmt_frame_register = ieee80211_mgmt_frame_register, .set_antenna = ieee80211_set_antenna, .get_antenna = ieee80211_get_antenna, - .set_ringparam = ieee80211_set_ringparam, - .get_ringparam = ieee80211_get_ringparam, .set_rekey_data = ieee80211_set_rekey_data, .tdls_oper = ieee80211_tdls_oper, .tdls_mgmt = ieee80211_tdls_mgmt, @@ -3853,9 +3516,6 @@ const struct cfg80211_ops mac80211_config_ops = { #ifdef CONFIG_PM .set_wakeup = ieee80211_set_wakeup, #endif - .get_et_sset_count = ieee80211_get_et_sset_count, - .get_et_stats = ieee80211_get_et_stats, - .get_et_strings = ieee80211_get_et_strings, .get_channel = ieee80211_cfg_get_channel, .start_radar_detection = ieee80211_start_radar_detection, .channel_switch = ieee80211_channel_switch, diff --git a/net/mac80211/ethtool.c b/net/mac80211/ethtool.c new file mode 100644 index 000000000000..ebfc8091557b --- /dev/null +++ b/net/mac80211/ethtool.c @@ -0,0 +1,244 @@ +/* + * mac80211 ethtool hooks for cfg80211 + * + * Copied from cfg.c - originally + * Copyright 2006-2010 Johannes Berg + * Copyright 2014 Intel Corporation (Author: Johannes Berg) + * + * This file is GPLv2 as found in COPYING. + */ +#include +#include +#include "ieee80211_i.h" +#include "sta_info.h" +#include "driver-ops.h" + +static int ieee80211_set_ringparam(struct net_device *dev, + struct ethtool_ringparam *rp) +{ + struct ieee80211_local *local = wiphy_priv(dev->ieee80211_ptr->wiphy); + + if (rp->rx_mini_pending != 0 || rp->rx_jumbo_pending != 0) + return -EINVAL; + + return drv_set_ringparam(local, rp->tx_pending, rp->rx_pending); +} + +static void ieee80211_get_ringparam(struct net_device *dev, + struct ethtool_ringparam *rp) +{ + struct ieee80211_local *local = wiphy_priv(dev->ieee80211_ptr->wiphy); + + memset(rp, 0, sizeof(*rp)); + + drv_get_ringparam(local, &rp->tx_pending, &rp->tx_max_pending, + &rp->rx_pending, &rp->rx_max_pending); +} + +static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = { + "rx_packets", "rx_bytes", + "rx_duplicates", "rx_fragments", "rx_dropped", + "tx_packets", "tx_bytes", "tx_fragments", + "tx_filtered", "tx_retry_failed", "tx_retries", + "beacon_loss", "sta_state", "txrate", "rxrate", "signal", + "channel", "noise", "ch_time", "ch_time_busy", + "ch_time_ext_busy", "ch_time_rx", "ch_time_tx" +}; +#define STA_STATS_LEN ARRAY_SIZE(ieee80211_gstrings_sta_stats) + +static int ieee80211_get_sset_count(struct net_device *dev, int sset) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + int rv = 0; + + if (sset == ETH_SS_STATS) + rv += STA_STATS_LEN; + + rv += drv_get_et_sset_count(sdata, sset); + + if (rv == 0) + return -EOPNOTSUPP; + return rv; +} + +static void ieee80211_get_stats(struct net_device *dev, + struct ethtool_stats *stats, + u64 *data) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_channel *channel; + struct sta_info *sta; + struct ieee80211_local *local = sdata->local; + struct station_info sinfo; + struct survey_info survey; + int i, q; +#define STA_STATS_SURVEY_LEN 7 + + memset(data, 0, sizeof(u64) * STA_STATS_LEN); + +#define ADD_STA_STATS(sta) \ + do { \ + data[i++] += sta->rx_packets; \ + data[i++] += sta->rx_bytes; \ + data[i++] += sta->num_duplicates; \ + data[i++] += sta->rx_fragments; \ + data[i++] += sta->rx_dropped; \ + \ + data[i++] += sinfo.tx_packets; \ + data[i++] += sinfo.tx_bytes; \ + data[i++] += sta->tx_fragments; \ + data[i++] += sta->tx_filtered_count; \ + data[i++] += sta->tx_retry_failed; \ + data[i++] += sta->tx_retry_count; \ + data[i++] += sta->beacon_loss_count; \ + } while (0) + + /* For Managed stations, find the single station based on BSSID + * and use that. For interface types, iterate through all available + * stations and add stats for any station that is assigned to this + * network device. + */ + + mutex_lock(&local->sta_mtx); + + if (sdata->vif.type == NL80211_IFTYPE_STATION) { + sta = sta_info_get_bss(sdata, sdata->u.mgd.bssid); + + if (!(sta && !WARN_ON(sta->sdata->dev != dev))) + goto do_survey; + + sinfo.filled = 0; + sta_set_sinfo(sta, &sinfo); + + i = 0; + ADD_STA_STATS(sta); + + data[i++] = sta->sta_state; + + + if (sinfo.filled & STATION_INFO_TX_BITRATE) + data[i] = 100000 * + cfg80211_calculate_bitrate(&sinfo.txrate); + i++; + if (sinfo.filled & STATION_INFO_RX_BITRATE) + data[i] = 100000 * + cfg80211_calculate_bitrate(&sinfo.rxrate); + i++; + + if (sinfo.filled & STATION_INFO_SIGNAL_AVG) + data[i] = (u8)sinfo.signal_avg; + i++; + } else { + list_for_each_entry(sta, &local->sta_list, list) { + /* Make sure this station belongs to the proper dev */ + if (sta->sdata->dev != dev) + continue; + + sinfo.filled = 0; + sta_set_sinfo(sta, &sinfo); + i = 0; + ADD_STA_STATS(sta); + } + } + +do_survey: + i = STA_STATS_LEN - STA_STATS_SURVEY_LEN; + /* Get survey stats for current channel */ + survey.filled = 0; + + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (chanctx_conf) + channel = chanctx_conf->def.chan; + else + channel = NULL; + rcu_read_unlock(); + + if (channel) { + q = 0; + do { + survey.filled = 0; + if (drv_get_survey(local, q, &survey) != 0) { + survey.filled = 0; + break; + } + q++; + } while (channel != survey.channel); + } + + if (survey.filled) + data[i++] = survey.channel->center_freq; + else + data[i++] = 0; + if (survey.filled & SURVEY_INFO_NOISE_DBM) + data[i++] = (u8)survey.noise; + else + data[i++] = -1LL; + if (survey.filled & SURVEY_INFO_CHANNEL_TIME) + data[i++] = survey.channel_time; + else + data[i++] = -1LL; + if (survey.filled & SURVEY_INFO_CHANNEL_TIME_BUSY) + data[i++] = survey.channel_time_busy; + else + data[i++] = -1LL; + if (survey.filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) + data[i++] = survey.channel_time_ext_busy; + else + data[i++] = -1LL; + if (survey.filled & SURVEY_INFO_CHANNEL_TIME_RX) + data[i++] = survey.channel_time_rx; + else + data[i++] = -1LL; + if (survey.filled & SURVEY_INFO_CHANNEL_TIME_TX) + data[i++] = survey.channel_time_tx; + else + data[i++] = -1LL; + + mutex_unlock(&local->sta_mtx); + + if (WARN_ON(i != STA_STATS_LEN)) + return; + + drv_get_et_stats(sdata, stats, &(data[STA_STATS_LEN])); +} + +static void ieee80211_get_strings(struct net_device *dev, u32 sset, u8 *data) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + int sz_sta_stats = 0; + + if (sset == ETH_SS_STATS) { + sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats); + memcpy(data, ieee80211_gstrings_sta_stats, sz_sta_stats); + } + drv_get_et_strings(sdata, sset, &(data[sz_sta_stats])); +} + +static int ieee80211_get_regs_len(struct net_device *dev) +{ + return 0; +} + +static void ieee80211_get_regs(struct net_device *dev, + struct ethtool_regs *regs, + void *data) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + regs->version = wdev->wiphy->hw_version; + regs->len = 0; +} + +const struct ethtool_ops ieee80211_ethtool_ops = { + .get_drvinfo = cfg80211_get_drvinfo, + .get_regs_len = ieee80211_get_regs_len, + .get_regs = ieee80211_get_regs, + .get_link = ethtool_op_get_link, + .get_ringparam = ieee80211_get_ringparam, + .set_ringparam = ieee80211_set_ringparam, + .get_strings = ieee80211_get_strings, + .get_ethtool_stats = ieee80211_get_stats, + .get_sset_count = ieee80211_get_sset_count, +}; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index ac9836e0aab3..4372d48b718f 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1847,6 +1847,8 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, enum nl80211_tdls_operation oper); +extern const struct ethtool_ops ieee80211_ethtool_ops; + #ifdef CONFIG_MAC80211_NOINLINE #define debug_noinline noinline #else diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 388b863e821c..db5afc7faa22 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1705,6 +1705,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, ndev->features |= local->hw.netdev_features; + netdev_set_default_ethtool_ops(ndev, &ieee80211_ethtool_ops); + ret = register_netdevice(ndev); if (ret) { free_netdev(ndev); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index ae7c16ad5f22..6fe48f64d0e4 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1719,3 +1719,137 @@ u8 sta_info_tx_streams(struct sta_info *sta) return ((ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK) >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1; } + +void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) +{ + struct ieee80211_sub_if_data *sdata = sta->sdata; + struct ieee80211_local *local = sdata->local; + struct rate_control_ref *ref = local->rate_ctrl; + struct timespec uptime; + u64 packets = 0; + u32 thr = 0; + int i, ac; + + sinfo->generation = sdata->local->sta_generation; + + sinfo->filled = STATION_INFO_INACTIVE_TIME | + STATION_INFO_RX_BYTES64 | + STATION_INFO_TX_BYTES64 | + STATION_INFO_RX_PACKETS | + STATION_INFO_TX_PACKETS | + STATION_INFO_TX_RETRIES | + STATION_INFO_TX_FAILED | + STATION_INFO_TX_BITRATE | + STATION_INFO_RX_BITRATE | + STATION_INFO_RX_DROP_MISC | + STATION_INFO_BSS_PARAM | + STATION_INFO_CONNECTED_TIME | + STATION_INFO_STA_FLAGS | + STATION_INFO_BEACON_LOSS_COUNT; + + do_posix_clock_monotonic_gettime(&uptime); + sinfo->connected_time = uptime.tv_sec - sta->last_connected; + + sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); + sinfo->tx_bytes = 0; + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + sinfo->tx_bytes += sta->tx_bytes[ac]; + packets += sta->tx_packets[ac]; + } + sinfo->tx_packets = packets; + sinfo->rx_bytes = sta->rx_bytes; + sinfo->rx_packets = sta->rx_packets; + sinfo->tx_retries = sta->tx_retry_count; + sinfo->tx_failed = sta->tx_retry_failed; + sinfo->rx_dropped_misc = sta->rx_dropped; + sinfo->beacon_loss_count = sta->beacon_loss_count; + + if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) || + (sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) { + sinfo->filled |= STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG; + if (!local->ops->get_rssi || + drv_get_rssi(local, sdata, &sta->sta, &sinfo->signal)) + sinfo->signal = (s8)sta->last_signal; + sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal); + } + if (sta->chains) { + sinfo->filled |= STATION_INFO_CHAIN_SIGNAL | + STATION_INFO_CHAIN_SIGNAL_AVG; + + sinfo->chains = sta->chains; + for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) { + sinfo->chain_signal[i] = sta->chain_signal_last[i]; + sinfo->chain_signal_avg[i] = + (s8) -ewma_read(&sta->chain_signal_avg[i]); + } + } + + sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate); + sta_set_rate_info_rx(sta, &sinfo->rxrate); + + if (ieee80211_vif_is_mesh(&sdata->vif)) { +#ifdef CONFIG_MAC80211_MESH + sinfo->filled |= STATION_INFO_LLID | + STATION_INFO_PLID | + STATION_INFO_PLINK_STATE | + STATION_INFO_LOCAL_PM | + STATION_INFO_PEER_PM | + STATION_INFO_NONPEER_PM; + + sinfo->llid = sta->llid; + sinfo->plid = sta->plid; + sinfo->plink_state = sta->plink_state; + if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) { + sinfo->filled |= STATION_INFO_T_OFFSET; + sinfo->t_offset = sta->t_offset; + } + sinfo->local_pm = sta->local_pm; + sinfo->peer_pm = sta->peer_pm; + sinfo->nonpeer_pm = sta->nonpeer_pm; +#endif + } + + sinfo->bss_param.flags = 0; + if (sdata->vif.bss_conf.use_cts_prot) + sinfo->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT; + if (sdata->vif.bss_conf.use_short_preamble) + sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE; + if (sdata->vif.bss_conf.use_short_slot) + sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME; + sinfo->bss_param.dtim_period = sdata->local->hw.conf.ps_dtim_period; + sinfo->bss_param.beacon_interval = sdata->vif.bss_conf.beacon_int; + + sinfo->sta_flags.set = 0; + sinfo->sta_flags.mask = BIT(NL80211_STA_FLAG_AUTHORIZED) | + BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | + BIT(NL80211_STA_FLAG_WME) | + BIT(NL80211_STA_FLAG_MFP) | + BIT(NL80211_STA_FLAG_AUTHENTICATED) | + BIT(NL80211_STA_FLAG_ASSOCIATED) | + BIT(NL80211_STA_FLAG_TDLS_PEER); + if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) + sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED); + if (test_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE)) + sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE); + if (test_sta_flag(sta, WLAN_STA_WME)) + sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_WME); + if (test_sta_flag(sta, WLAN_STA_MFP)) + sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP); + if (test_sta_flag(sta, WLAN_STA_AUTH)) + sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED); + if (test_sta_flag(sta, WLAN_STA_ASSOC)) + sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED); + if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) + sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER); + + /* check if the driver has a SW RC implementation */ + if (ref && ref->ops->get_expected_throughput) + thr = ref->ops->get_expected_throughput(sta->rate_ctrl_priv); + else + thr = drv_get_expected_throughput(local, &sta->sta); + + if (thr != 0) { + sinfo->filled |= STATION_INFO_EXPECTED_THROUGHPUT; + sinfo->expected_throughput = thr; + } +} diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index fa7ce6f8593b..2a04361b2162 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -627,6 +627,8 @@ void sta_set_rate_info_tx(struct sta_info *sta, struct rate_info *rinfo); void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo); +void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo); + void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, unsigned long exp_time); u8 sta_info_tx_streams(struct sta_info *sta); diff --git a/net/wireless/core.c b/net/wireless/core.c index a1c40654dd9b..afee5e0455ea 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -25,7 +25,6 @@ #include "sysfs.h" #include "debugfs.h" #include "wext-compat.h" -#include "ethtool.h" #include "rdev-ops.h" /* name for sysfs, %d is appended */ @@ -927,8 +926,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, /* allow mac80211 to determine the timeout */ wdev->ps_timeout = -1; - netdev_set_default_ethtool_ops(dev, &cfg80211_ethtool_ops); - if ((wdev->iftype == NL80211_IFTYPE_STATION || wdev->iftype == NL80211_IFTYPE_P2P_CLIENT || wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr) diff --git a/net/wireless/ethtool.c b/net/wireless/ethtool.c index d4860bfc020e..e9e91298c70d 100644 --- a/net/wireless/ethtool.c +++ b/net/wireless/ethtool.c @@ -1,11 +1,9 @@ #include #include #include "core.h" -#include "ethtool.h" #include "rdev-ops.h" -static void cfg80211_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) +void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct wireless_dev *wdev = dev->ieee80211_ptr; @@ -23,84 +21,4 @@ static void cfg80211_get_drvinfo(struct net_device *dev, strlcpy(info->bus_info, dev_name(wiphy_dev(wdev->wiphy)), sizeof(info->bus_info)); } - -static int cfg80211_get_regs_len(struct net_device *dev) -{ - /* For now, return 0... */ - return 0; -} - -static void cfg80211_get_regs(struct net_device *dev, struct ethtool_regs *regs, - void *data) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - regs->version = wdev->wiphy->hw_version; - regs->len = 0; -} - -static void cfg80211_get_ringparam(struct net_device *dev, - struct ethtool_ringparam *rp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); - - memset(rp, 0, sizeof(*rp)); - - if (rdev->ops->get_ringparam) - rdev_get_ringparam(rdev, &rp->tx_pending, &rp->tx_max_pending, - &rp->rx_pending, &rp->rx_max_pending); -} - -static int cfg80211_set_ringparam(struct net_device *dev, - struct ethtool_ringparam *rp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); - - if (rp->rx_mini_pending != 0 || rp->rx_jumbo_pending != 0) - return -EINVAL; - - if (rdev->ops->set_ringparam) - return rdev_set_ringparam(rdev, rp->tx_pending, rp->rx_pending); - - return -ENOTSUPP; -} - -static int cfg80211_get_sset_count(struct net_device *dev, int sset) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); - if (rdev->ops->get_et_sset_count) - return rdev_get_et_sset_count(rdev, dev, sset); - return -EOPNOTSUPP; -} - -static void cfg80211_get_stats(struct net_device *dev, - struct ethtool_stats *stats, u64 *data) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); - if (rdev->ops->get_et_stats) - rdev_get_et_stats(rdev, dev, stats, data); -} - -static void cfg80211_get_strings(struct net_device *dev, u32 sset, u8 *data) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); - if (rdev->ops->get_et_strings) - rdev_get_et_strings(rdev, dev, sset, data); -} - -const struct ethtool_ops cfg80211_ethtool_ops = { - .get_drvinfo = cfg80211_get_drvinfo, - .get_regs_len = cfg80211_get_regs_len, - .get_regs = cfg80211_get_regs, - .get_link = ethtool_op_get_link, - .get_ringparam = cfg80211_get_ringparam, - .set_ringparam = cfg80211_set_ringparam, - .get_strings = cfg80211_get_strings, - .get_ethtool_stats = cfg80211_get_stats, - .get_sset_count = cfg80211_get_sset_count, -}; +EXPORT_SYMBOL(cfg80211_get_drvinfo); diff --git a/net/wireless/ethtool.h b/net/wireless/ethtool.h deleted file mode 100644 index 695ecad20bd6..000000000000 --- a/net/wireless/ethtool.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __CFG80211_ETHTOOL__ -#define __CFG80211_ETHTOOL__ - -extern const struct ethtool_ops cfg80211_ethtool_ops; - -#endif /* __CFG80211_ETHTOOL__ */ diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index d95bbe348138..f552b0abbd70 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -714,25 +714,6 @@ static inline int rdev_get_antenna(struct cfg80211_registered_device *rdev, return ret; } -static inline int rdev_set_ringparam(struct cfg80211_registered_device *rdev, - u32 tx, u32 rx) -{ - int ret; - trace_rdev_set_ringparam(&rdev->wiphy, tx, rx); - ret = rdev->ops->set_ringparam(&rdev->wiphy, tx, rx); - trace_rdev_return_int(&rdev->wiphy, ret); - return ret; -} - -static inline void rdev_get_ringparam(struct cfg80211_registered_device *rdev, - u32 *tx, u32 *tx_max, u32 *rx, - u32 *rx_max) -{ - trace_rdev_get_ringparam(&rdev->wiphy); - rdev->ops->get_ringparam(&rdev->wiphy, tx, tx_max, rx, rx_max); - trace_rdev_return_void_tx_rx(&rdev->wiphy, *tx, *tx_max, *rx, *rx_max); -} - static inline int rdev_sched_scan_start(struct cfg80211_registered_device *rdev, struct net_device *dev, @@ -815,35 +796,6 @@ static inline int rdev_set_noack_map(struct cfg80211_registered_device *rdev, return ret; } -static inline int -rdev_get_et_sset_count(struct cfg80211_registered_device *rdev, - struct net_device *dev, int sset) -{ - int ret; - trace_rdev_get_et_sset_count(&rdev->wiphy, dev, sset); - ret = rdev->ops->get_et_sset_count(&rdev->wiphy, dev, sset); - trace_rdev_return_int(&rdev->wiphy, ret); - return ret; -} - -static inline void rdev_get_et_stats(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct ethtool_stats *stats, u64 *data) -{ - trace_rdev_get_et_stats(&rdev->wiphy, dev); - rdev->ops->get_et_stats(&rdev->wiphy, dev, stats, data); - trace_rdev_return_void(&rdev->wiphy); -} - -static inline void rdev_get_et_strings(struct cfg80211_registered_device *rdev, - struct net_device *dev, u32 sset, - u8 *data) -{ - trace_rdev_get_et_strings(&rdev->wiphy, dev, sset); - rdev->ops->get_et_strings(&rdev->wiphy, dev, sset, data); - trace_rdev_return_void(&rdev->wiphy); -} - static inline int rdev_get_channel(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 560ed77084e9..174559aade57 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -298,11 +298,6 @@ DEFINE_EVENT(wiphy_only_evt, rdev_return_void, TP_ARGS(wiphy) ); -DEFINE_EVENT(wiphy_only_evt, rdev_get_ringparam, - TP_PROTO(struct wiphy *wiphy), - TP_ARGS(wiphy) -); - DEFINE_EVENT(wiphy_only_evt, rdev_get_antenna, TP_PROTO(struct wiphy *wiphy), TP_ARGS(wiphy) @@ -580,11 +575,6 @@ DEFINE_EVENT(wiphy_netdev_evt, rdev_stop_ap, TP_ARGS(wiphy, netdev) ); -DEFINE_EVENT(wiphy_netdev_evt, rdev_get_et_stats, - TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), - TP_ARGS(wiphy, netdev) -); - DEFINE_EVENT(wiphy_netdev_evt, rdev_sched_scan_stop, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), TP_ARGS(wiphy, netdev) @@ -1439,11 +1429,6 @@ DECLARE_EVENT_CLASS(tx_rx_evt, WIPHY_PR_ARG, __entry->tx, __entry->rx) ); -DEFINE_EVENT(tx_rx_evt, rdev_set_ringparam, - TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx), - TP_ARGS(wiphy, rx, tx) -); - DEFINE_EVENT(tx_rx_evt, rdev_set_antenna, TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx), TP_ARGS(wiphy, rx, tx) @@ -1725,40 +1710,6 @@ TRACE_EVENT(rdev_set_noack_map, WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->noack_map) ); -TRACE_EVENT(rdev_get_et_sset_count, - TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int sset), - TP_ARGS(wiphy, netdev, sset), - TP_STRUCT__entry( - WIPHY_ENTRY - NETDEV_ENTRY - __field(int, sset) - ), - TP_fast_assign( - WIPHY_ASSIGN; - NETDEV_ASSIGN; - __entry->sset = sset; - ), - TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", sset: %d", - WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->sset) -); - -TRACE_EVENT(rdev_get_et_strings, - TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u32 sset), - TP_ARGS(wiphy, netdev, sset), - TP_STRUCT__entry( - WIPHY_ENTRY - NETDEV_ENTRY - __field(u32, sset) - ), - TP_fast_assign( - WIPHY_ASSIGN; - NETDEV_ASSIGN; - __entry->sset = sset; - ), - TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", sset: %u", - WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->sset) -); - DEFINE_EVENT(wiphy_wdev_evt, rdev_get_channel, TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), TP_ARGS(wiphy, wdev) From b49328361bab10d786e321aeae79b4429fdff38c Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Thu, 5 Jun 2014 08:12:57 +0200 Subject: [PATCH 115/274] mac80211: allow tx via monitor iface when DFS Allow send frames using monitor interface when DFS chandef and we pass CAC (beaconing allowed). This fix problem when old kernel and new backports used, in such case hostapd create/use also monitor interface. Before this patch all frames hostapd send using monitor iface were dropped when AP was configured on DFS channel. Signed-off-by: Janusz Dziedzic Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 8170d9945d6d..3c80bf29b050 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1620,12 +1620,12 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_chanctx_conf *chanctx_conf; - struct ieee80211_channel *chan; struct ieee80211_radiotap_header *prthdr = (struct ieee80211_radiotap_header *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr; struct ieee80211_sub_if_data *tmp_sdata, *sdata; + struct cfg80211_chan_def *chandef; u16 len_rthdr; int hdrlen; @@ -1723,9 +1723,9 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, } if (chanctx_conf) - chan = chanctx_conf->def.chan; + chandef = &chanctx_conf->def; else if (!local->use_chanctx) - chan = local->_oper_chandef.chan; + chandef = &local->_oper_chandef; else goto fail_rcu; @@ -1745,10 +1745,11 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, * radar detection by itself. We can do that later by adding a * monitor flag interfaces used for AP support. */ - if ((chan->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR))) + if (!cfg80211_reg_can_beacon(local->hw.wiphy, chandef, + sdata->vif.type)) goto fail_rcu; - ieee80211_xmit(sdata, skb, chan->band); + ieee80211_xmit(sdata, skb, chandef->chan->band); rcu_read_unlock(); return NETDEV_TX_OK; From af296bdb8da4d0a4284de10fc4a61497272ddf11 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Thu, 5 Jun 2014 14:21:36 +0200 Subject: [PATCH 116/274] mac80211: move csa counters from sdata to beacon/presp Having csa counters part of beacon and probe_resp structures makes it easier to get rid of possible races between setting a beacon and updating counters on SMP systems by guaranteeing counters are always consistent against given beacon struct. While at it relax WARN_ON into WARN_ON_ONCE to prevent spamming logs and racing. Signed-off-by: Michal Kazior [remove pointless array check] Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 67 +++++++++++++++--------- net/mac80211/ibss.c | 2 +- net/mac80211/ieee80211_i.h | 16 ++++-- net/mac80211/mesh.c | 2 +- net/mac80211/tx.c | 103 ++++++++++++++++++++----------------- 5 files changed, 115 insertions(+), 75 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index b6d73c14e1ae..af3eac482acd 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -554,7 +554,8 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy, } static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata, - const u8 *resp, size_t resp_len) + const u8 *resp, size_t resp_len, + const struct ieee80211_csa_settings *csa) { struct probe_resp *new, *old; @@ -570,6 +571,11 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata, new->len = resp_len; memcpy(new->data, resp, resp_len); + if (csa) + memcpy(new->csa_counter_offsets, csa->counter_offsets_presp, + csa->n_counter_offsets_presp * + sizeof(new->csa_counter_offsets[0])); + rcu_assign_pointer(sdata->u.ap.probe_resp, new); if (old) kfree_rcu(old, rcu_head); @@ -578,7 +584,8 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata, } static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, - struct cfg80211_beacon_data *params) + struct cfg80211_beacon_data *params, + const struct ieee80211_csa_settings *csa) { struct beacon_data *new, *old; int new_head_len, new_tail_len; @@ -622,6 +629,13 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, new->head_len = new_head_len; new->tail_len = new_tail_len; + if (csa) { + new->csa_current_counter = csa->count; + memcpy(new->csa_counter_offsets, csa->counter_offsets_beacon, + csa->n_counter_offsets_beacon * + sizeof(new->csa_counter_offsets[0])); + } + /* copy in head */ if (params->head) memcpy(new->head, params->head, new_head_len); @@ -636,7 +650,7 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, memcpy(new->tail, old->tail, new_tail_len); err = ieee80211_set_probe_resp(sdata, params->probe_resp, - params->probe_resp_len); + params->probe_resp_len, csa); if (err < 0) return err; if (err == 0) @@ -721,7 +735,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow |= IEEE80211_P2P_OPPPS_ENABLE_BIT; - err = ieee80211_assign_beacon(sdata, ¶ms->beacon); + err = ieee80211_assign_beacon(sdata, ¶ms->beacon, NULL); if (err < 0) { ieee80211_vif_release_channel(sdata); return err; @@ -769,7 +783,7 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev, if (!old) return -ENOENT; - err = ieee80211_assign_beacon(sdata, params); + err = ieee80211_assign_beacon(sdata, params, NULL); if (err < 0) return err; ieee80211_bss_info_change_notify(sdata, err); @@ -2752,7 +2766,8 @@ static int ieee80211_set_after_csa_beacon(struct ieee80211_sub_if_data *sdata, switch (sdata->vif.type) { case NL80211_IFTYPE_AP: - err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon); + err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon, + NULL); kfree(sdata->u.ap.next_beacon); sdata->u.ap.next_beacon = NULL; @@ -2855,6 +2870,7 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, struct cfg80211_csa_settings *params, u32 *changed) { + struct ieee80211_csa_settings csa = {}; int err; switch (sdata->vif.type) { @@ -2889,20 +2905,13 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, IEEE80211_MAX_CSA_COUNTERS_NUM)) return -EINVAL; - /* make sure we don't have garbage in other counters */ - memset(sdata->csa_counter_offset_beacon, 0, - sizeof(sdata->csa_counter_offset_beacon)); - memset(sdata->csa_counter_offset_presp, 0, - sizeof(sdata->csa_counter_offset_presp)); + csa.counter_offsets_beacon = params->counter_offsets_beacon; + csa.counter_offsets_presp = params->counter_offsets_presp; + csa.n_counter_offsets_beacon = params->n_counter_offsets_beacon; + csa.n_counter_offsets_presp = params->n_counter_offsets_presp; + csa.count = params->count; - memcpy(sdata->csa_counter_offset_beacon, - params->counter_offsets_beacon, - params->n_counter_offsets_beacon * sizeof(u16)); - memcpy(sdata->csa_counter_offset_presp, - params->counter_offsets_presp, - params->n_counter_offsets_presp * sizeof(u16)); - - err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa); + err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa, &csa); if (err < 0) { kfree(sdata->u.ap.next_beacon); return err; @@ -3046,7 +3055,6 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, sdata->csa_radar_required = params->radar_required; sdata->csa_chandef = params->chandef; sdata->csa_block_tx = params->block_tx; - sdata->csa_current_counter = params->count; sdata->vif.csa_active = true; if (sdata->csa_block_tx) @@ -3194,10 +3202,23 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, sdata->vif.type == NL80211_IFTYPE_ADHOC) && params->n_csa_offsets) { int i; - u8 c = sdata->csa_current_counter; + struct beacon_data *beacon = NULL; - for (i = 0; i < params->n_csa_offsets; i++) - data[params->csa_offsets[i]] = c; + rcu_read_lock(); + + if (sdata->vif.type == NL80211_IFTYPE_AP) + beacon = rcu_dereference(sdata->u.ap.beacon); + else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) + beacon = rcu_dereference(sdata->u.ibss.presp); + else if (ieee80211_vif_is_mesh(&sdata->vif)) + beacon = rcu_dereference(sdata->u.mesh.beacon); + + if (beacon) + for (i = 0; i < params->n_csa_offsets; i++) + data[params->csa_offsets[i]] = + beacon->csa_current_counter; + + rcu_read_unlock(); } IEEE80211_SKB_CB(skb)->flags = flags; diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 18ee0a256b1e..713485f9effc 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -143,7 +143,7 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata, *pos++ = csa_settings->block_tx ? 1 : 0; *pos++ = ieee80211_frequency_to_channel( csa_settings->chandef.chan->center_freq); - sdata->csa_counter_offset_beacon[0] = (pos - presp->head); + presp->csa_counter_offsets[0] = (pos - presp->head); *pos++ = csa_settings->count; } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 4372d48b718f..d9af7ef3c11a 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -229,16 +229,29 @@ struct ieee80211_rx_data { u16 tkip_iv16; }; +struct ieee80211_csa_settings { + const u16 *counter_offsets_beacon; + const u16 *counter_offsets_presp; + + int n_counter_offsets_beacon; + int n_counter_offsets_presp; + + u8 count; +}; + struct beacon_data { u8 *head, *tail; int head_len, tail_len; struct ieee80211_meshconf_ie *meshconf; + u16 csa_counter_offsets[IEEE80211_MAX_CSA_COUNTERS_NUM]; + u8 csa_current_counter; struct rcu_head rcu_head; }; struct probe_resp { struct rcu_head rcu_head; int len; + u16 csa_counter_offsets[IEEE80211_MAX_CSA_COUNTERS_NUM]; u8 data[0]; }; @@ -754,8 +767,6 @@ struct ieee80211_sub_if_data { struct mac80211_qos_map __rcu *qos_map; struct work_struct csa_finalize_work; - u16 csa_counter_offset_beacon[IEEE80211_MAX_CSA_COUNTERS_NUM]; - u16 csa_counter_offset_presp[IEEE80211_MAX_CSA_COUNTERS_NUM]; bool csa_radar_required; bool csa_block_tx; /* write-protected by sdata_lock and local->mtx */ struct cfg80211_chan_def csa_chandef; @@ -767,7 +778,6 @@ struct ieee80211_sub_if_data { struct ieee80211_chanctx *reserved_chanctx; struct cfg80211_chan_def reserved_chandef; bool reserved_radar_required; - u8 csa_current_counter; /* used to reconfigure hardware SM PS */ struct work_struct recalc_smps; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index da164685b524..e9f99c1e3fad 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -679,7 +679,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) *pos++ = 0x0; *pos++ = ieee80211_frequency_to_channel( csa->settings.chandef.chan->center_freq); - sdata->csa_counter_offset_beacon[0] = hdr_len + 6; + bcn->csa_counter_offsets[0] = hdr_len + 6; *pos++ = csa->settings.count; *pos++ = WLAN_EID_CHAN_SWITCH_PARAM; *pos++ = 6; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 3c80bf29b050..ed56f0091663 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2426,7 +2426,7 @@ static void ieee80211_set_csa(struct ieee80211_sub_if_data *sdata, u8 *beacon_data; size_t beacon_data_len; int i; - u8 count = sdata->csa_current_counter; + u8 count = beacon->csa_current_counter; switch (sdata->vif.type) { case NL80211_IFTYPE_AP: @@ -2445,46 +2445,53 @@ static void ieee80211_set_csa(struct ieee80211_sub_if_data *sdata, return; } + rcu_read_lock(); for (i = 0; i < IEEE80211_MAX_CSA_COUNTERS_NUM; ++i) { - u16 counter_offset_beacon = - sdata->csa_counter_offset_beacon[i]; - u16 counter_offset_presp = sdata->csa_counter_offset_presp[i]; + resp = rcu_dereference(sdata->u.ap.probe_resp); - if (counter_offset_beacon) { - if (WARN_ON(counter_offset_beacon >= beacon_data_len)) - return; - - beacon_data[counter_offset_beacon] = count; - } - - if (sdata->vif.type == NL80211_IFTYPE_AP && - counter_offset_presp) { - rcu_read_lock(); - resp = rcu_dereference(sdata->u.ap.probe_resp); - - /* If nl80211 accepted the offset, this should - * not happen. - */ - if (WARN_ON(!resp)) { + if (beacon->csa_counter_offsets[i]) { + if (WARN_ON_ONCE(beacon->csa_counter_offsets[i] >= + beacon_data_len)) { rcu_read_unlock(); return; } - resp->data[counter_offset_presp] = count; - rcu_read_unlock(); + + beacon_data[beacon->csa_counter_offsets[i]] = count; } + + if (sdata->vif.type == NL80211_IFTYPE_AP && resp) + resp->data[resp->csa_counter_offsets[i]] = count; } + rcu_read_unlock(); } u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct beacon_data *beacon = NULL; + u8 count = 0; - sdata->csa_current_counter--; + rcu_read_lock(); + + if (sdata->vif.type == NL80211_IFTYPE_AP) + beacon = rcu_dereference(sdata->u.ap.beacon); + else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) + beacon = rcu_dereference(sdata->u.ibss.presp); + else if (ieee80211_vif_is_mesh(&sdata->vif)) + beacon = rcu_dereference(sdata->u.mesh.beacon); + + if (!beacon) + goto unlock; + + beacon->csa_current_counter--; /* the counter should never reach 0 */ - WARN_ON(!sdata->csa_current_counter); + WARN_ON_ONCE(!beacon->csa_current_counter); + count = beacon->csa_current_counter; - return sdata->csa_current_counter; +unlock: + rcu_read_unlock(); + return count; } EXPORT_SYMBOL(ieee80211_csa_update_counter); @@ -2494,7 +2501,6 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif) struct beacon_data *beacon = NULL; u8 *beacon_data; size_t beacon_data_len; - int counter_beacon = sdata->csa_counter_offset_beacon[0]; int ret = false; if (!ieee80211_sdata_running(sdata)) @@ -2532,10 +2538,10 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif) goto out; } - if (WARN_ON(counter_beacon > beacon_data_len)) + if (WARN_ON_ONCE(beacon->csa_counter_offsets[0] > beacon_data_len)) goto out; - if (beacon_data[counter_beacon] == 1) + if (beacon_data[beacon->csa_counter_offsets[0]] == 1) ret = true; out: rcu_read_unlock(); @@ -2551,6 +2557,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw, bool is_template) { struct ieee80211_local *local = hw_to_local(hw); + struct beacon_data *beacon = NULL; struct sk_buff *skb = NULL; struct ieee80211_tx_info *info; struct ieee80211_sub_if_data *sdata = NULL; @@ -2572,8 +2579,8 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw, if (sdata->vif.type == NL80211_IFTYPE_AP) { struct ieee80211_if_ap *ap = &sdata->u.ap; - struct beacon_data *beacon = rcu_dereference(ap->beacon); + beacon = rcu_dereference(ap->beacon); if (beacon) { if (sdata->vif.csa_active) { if (!is_template) @@ -2616,34 +2623,34 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw, } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_hdr *hdr; - struct beacon_data *presp = rcu_dereference(ifibss->presp); - if (!presp) + beacon = rcu_dereference(ifibss->presp); + if (!beacon) goto out; if (sdata->vif.csa_active) { if (!is_template) ieee80211_csa_update_counter(vif); - ieee80211_set_csa(sdata, presp); + ieee80211_set_csa(sdata, beacon); } - skb = dev_alloc_skb(local->tx_headroom + presp->head_len + + skb = dev_alloc_skb(local->tx_headroom + beacon->head_len + local->hw.extra_beacon_tailroom); if (!skb) goto out; skb_reserve(skb, local->tx_headroom); - memcpy(skb_put(skb, presp->head_len), presp->head, - presp->head_len); + memcpy(skb_put(skb, beacon->head_len), beacon->head, + beacon->head_len); hdr = (struct ieee80211_hdr *) skb->data; hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON); } else if (ieee80211_vif_is_mesh(&sdata->vif)) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; - struct beacon_data *bcn = rcu_dereference(ifmsh->beacon); - if (!bcn) + beacon = rcu_dereference(ifmsh->beacon); + if (!beacon) goto out; if (sdata->vif.csa_active) { @@ -2655,40 +2662,42 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw, */ ieee80211_csa_update_counter(vif); - ieee80211_set_csa(sdata, bcn); + ieee80211_set_csa(sdata, beacon); } if (ifmsh->sync_ops) - ifmsh->sync_ops->adjust_tbtt(sdata, bcn); + ifmsh->sync_ops->adjust_tbtt(sdata, beacon); skb = dev_alloc_skb(local->tx_headroom + - bcn->head_len + + beacon->head_len + 256 + /* TIM IE */ - bcn->tail_len + + beacon->tail_len + local->hw.extra_beacon_tailroom); if (!skb) goto out; skb_reserve(skb, local->tx_headroom); - memcpy(skb_put(skb, bcn->head_len), bcn->head, bcn->head_len); + memcpy(skb_put(skb, beacon->head_len), beacon->head, + beacon->head_len); ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb, is_template); if (offs) { - offs->tim_offset = bcn->head_len; - offs->tim_length = skb->len - bcn->head_len; + offs->tim_offset = beacon->head_len; + offs->tim_length = skb->len - beacon->head_len; } - memcpy(skb_put(skb, bcn->tail_len), bcn->tail, bcn->tail_len); + memcpy(skb_put(skb, beacon->tail_len), beacon->tail, + beacon->tail_len); } else { WARN_ON(1); goto out; } /* CSA offsets */ - if (offs) { + if (offs && beacon) { int i; for (i = 0; i < IEEE80211_MAX_CSA_COUNTERS_NUM; i++) { - u16 csa_off = sdata->csa_counter_offset_beacon[i]; + u16 csa_off = beacon->csa_counter_offsets[i]; if (!csa_off) continue; From 10d78f278214bd7c8a15d09ce2304728114786e7 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Thu, 5 Jun 2014 14:21:37 +0200 Subject: [PATCH 117/274] mac80211: use csa counter offsets instead of csa_active vif->csa_active is protected by mutexes only. This means it is unreliable to depend on it on codeflow in non-sleepable beacon and CSA code. There was no guarantee to have vif->csa_active update be visible before beacons are updated on SMP systems. Using csa counter offsets which are embedded in beacon struct (and thus are protected with single RCU assignment) is much safer. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index ed56f0091663..d741b7369dc4 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2538,6 +2538,9 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif) goto out; } + if (!beacon->csa_counter_offsets[0]) + goto out; + if (WARN_ON_ONCE(beacon->csa_counter_offsets[0] > beacon_data_len)) goto out; @@ -2582,7 +2585,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw, beacon = rcu_dereference(ap->beacon); if (beacon) { - if (sdata->vif.csa_active) { + if (beacon->csa_counter_offsets[0]) { if (!is_template) ieee80211_csa_update_counter(vif); @@ -2628,7 +2631,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw, if (!beacon) goto out; - if (sdata->vif.csa_active) { + if (beacon->csa_counter_offsets[0]) { if (!is_template) ieee80211_csa_update_counter(vif); @@ -2653,7 +2656,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw, if (!beacon) goto out; - if (sdata->vif.csa_active) { + if (beacon->csa_counter_offsets[0]) { if (!is_template) /* TODO: For mesh csa_counter is in TU, so * decrementing it by one isn't correct, but From 181715203bc0e309b827f99baf44f5a97b19c90c Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 11 Jun 2014 23:59:14 +0000 Subject: [PATCH 118/274] mac80211: Use ktime_get_ts() do_posix_clock_monotonic_gettime() is a leftover from the initial posix timer implementation which maps to ktime_get_ts(). Signed-off-by: Thomas Gleixner Signed-off-by: Johannes Berg --- net/mac80211/debugfs_sta.c | 2 +- net/mac80211/sta_info.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index ee82e1f0a9e7..3db96648b45a 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -124,7 +124,7 @@ static ssize_t sta_connected_time_read(struct file *file, char __user *userbuf, long connected_time_secs; char buf[100]; int res; - do_posix_clock_monotonic_gettime(&uptime); + ktime_get_ts(&uptime); connected_time_secs = uptime.tv_sec - sta->last_connected; time_to_tm(connected_time_secs, 0, &result); result.tm_year -= 70; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 6fe48f64d0e4..f41177f58b30 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -350,7 +350,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, sta->sta_state = IEEE80211_STA_NONE; - do_posix_clock_monotonic_gettime(&uptime); + ktime_get_ts(&uptime); sta->last_connected = uptime.tv_sec; ewma_init(&sta->avg_signal, 1024, 8); for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++) @@ -1747,7 +1747,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) STATION_INFO_STA_FLAGS | STATION_INFO_BEACON_LOSS_COUNT; - do_posix_clock_monotonic_gettime(&uptime); + ktime_get_ts(&uptime); sinfo->connected_time = uptime.tv_sec - sta->last_connected; sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); From 3283e286b859e9214867b2c855db065e41101b03 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 12 Jun 2014 22:34:49 +0200 Subject: [PATCH 119/274] mac80211_hwsim: clean up own address matching Using perm_addr is always wrong, it may be reassigned by anyone using standard netdev APIs. Remove that from the match function and also use the match function where only the perm_addr was used now. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 66 +++++++++++++-------------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index a312c653d116..06a0722164da 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -781,6 +781,36 @@ static void mac80211_hwsim_monitor_ack(struct ieee80211_channel *chan, netif_rx(skb); } +struct mac80211_hwsim_addr_match_data { + u8 addr[ETH_ALEN]; + bool ret; +}; + +static void mac80211_hwsim_addr_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mac80211_hwsim_addr_match_data *md = data; + + if (memcmp(mac, md->addr, ETH_ALEN) == 0) + md->ret = true; +} + +static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data, + const u8 *addr) +{ + struct mac80211_hwsim_addr_match_data md = { + .ret = false, + }; + + memcpy(md.addr, addr, ETH_ALEN); + + ieee80211_iterate_active_interfaces_atomic(data->hw, + IEEE80211_IFACE_ITER_NORMAL, + mac80211_hwsim_addr_iter, + &md); + + return md.ret; +} static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data, struct sk_buff *skb) @@ -798,8 +828,7 @@ static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data, /* Allow unicast frames to own address if there is a pending * PS-Poll */ if (data->ps_poll_pending && - memcmp(data->hw->wiphy->perm_addr, skb->data + 4, - ETH_ALEN) == 0) { + mac80211_hwsim_addr_match(data, skb->data + 4)) { data->ps_poll_pending = false; return true; } @@ -809,39 +838,6 @@ static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data, return true; } - -struct mac80211_hwsim_addr_match_data { - bool ret; - const u8 *addr; -}; - -static void mac80211_hwsim_addr_iter(void *data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct mac80211_hwsim_addr_match_data *md = data; - if (memcmp(mac, md->addr, ETH_ALEN) == 0) - md->ret = true; -} - - -static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data, - const u8 *addr) -{ - struct mac80211_hwsim_addr_match_data md; - - if (memcmp(addr, data->hw->wiphy->perm_addr, ETH_ALEN) == 0) - return true; - - md.ret = false; - md.addr = addr; - ieee80211_iterate_active_interfaces_atomic(data->hw, - IEEE80211_IFACE_ITER_NORMAL, - mac80211_hwsim_addr_iter, - &md); - - return md.ret; -} - static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, struct sk_buff *my_skb, int dst_portid) From 8d7b70fb7bf8963634052f455db86074d48bab2c Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 11 Jun 2014 23:59:18 +0000 Subject: [PATCH 120/274] net: Mac80211: Remove silly timespec dance Converting time from one format to another seems to give coders a warm and fuzzy feeling. Use the proper interfaces. Signed-off-by: Thomas Gleixner Cc: John Stultz Cc: Peter Zijlstra Cc: John W. Linville [fix compile error] Signed-off-by: Johannes Berg --- net/mac80211/status.c | 7 ++----- net/mac80211/tx.c | 5 +---- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/net/mac80211/status.c b/net/mac80211/status.c index ba29ebc86141..aa06dcad336e 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -473,8 +473,6 @@ static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local, struct sta_info *sta, struct ieee80211_hdr *hdr) { - ktime_t skb_dprt; - struct timespec dprt_time; u32 msrmnt; u16 tid; u8 *qc; @@ -506,9 +504,8 @@ static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local, tx_lat = &sta->tx_lat[tid]; - ktime_get_ts(&dprt_time); /* time stamp completion time */ - skb_dprt = ktime_set(dprt_time.tv_sec, dprt_time.tv_nsec); - msrmnt = ktime_to_ms(ktime_sub(skb_dprt, skb_arv)); + /* Calculate the latency */ + msrmnt = ktime_to_ms(ktime_sub(ktime_get(), skb_arv)); if (tx_lat->max < msrmnt) /* update stats */ tx_lat->max = msrmnt; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index d741b7369dc4..9b3d94e7c4be 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1770,15 +1770,12 @@ fail: static void ieee80211_tx_latency_start_msrmnt(struct ieee80211_local *local, struct sk_buff *skb) { - struct timespec skb_arv; struct ieee80211_tx_latency_bin_ranges *tx_latency; tx_latency = rcu_dereference(local->tx_latency); if (!tx_latency) return; - - ktime_get_ts(&skb_arv); - skb->tstamp = ktime_set(skb_arv.tv_sec, skb_arv.tv_nsec); + skb->tstamp = ktime_get(); } /** From 59f48fe22fe817df1ced42dee28b31de88b478b6 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Fri, 13 Jun 2014 16:30:04 +0300 Subject: [PATCH 121/274] mac80211: don't stop all queues when flushing There is no need to stop all queues when we want to flush specific queues, so stop only the queues that will be flushed. Signed-off-by: Luciano Coelho Signed-off-by: Johannes Berg --- net/mac80211/util.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 6886601afe1c..6d29e40538ad 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -551,12 +551,12 @@ void ieee80211_flush_queues(struct ieee80211_local *local, queues = BIT(local->hw.queues) - 1; } - ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, + ieee80211_stop_queues_by_reason(&local->hw, queues, IEEE80211_QUEUE_STOP_REASON_FLUSH); drv_flush(local, sdata, queues, false); - ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, + ieee80211_wake_queues_by_reason(&local->hw, queues, IEEE80211_QUEUE_STOP_REASON_FLUSH); } From cca07b00a56d6ddd339e457dfd1a229222b9acf5 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Fri, 13 Jun 2014 16:30:05 +0300 Subject: [PATCH 122/274] mac80211: introduce refcount for queue_stop_reasons Sometimes different vifs may be stopping the queues for the same reason (e.g. when several interfaces are performing a channel switch). Instead of using a bitmask for the reasons, use an integer that holds a refcount instead. In order to keep it backwards compatible, introduce a boolean in some functions that tell us whether the queue stopping should be refcounted or not. For now, use not refcounted for all calls to keep it functionally the same as before. Signed-off-by: Luciano Coelho Signed-off-by: Johannes Berg --- net/mac80211/agg-tx.c | 8 +++- net/mac80211/cfg.c | 9 +++-- net/mac80211/ieee80211_i.h | 15 ++++++-- net/mac80211/iface.c | 3 +- net/mac80211/main.c | 3 +- net/mac80211/mlme.c | 15 +++++--- net/mac80211/offchannel.c | 6 ++- net/mac80211/pm.c | 6 ++- net/mac80211/tx.c | 3 +- net/mac80211/util.c | 76 +++++++++++++++++++++++++------------- 10 files changed, 98 insertions(+), 46 deletions(-) diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index ce9633a3cfb0..d6986f3aa5c4 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -170,10 +170,13 @@ ieee80211_stop_queue_agg(struct ieee80211_sub_if_data *sdata, int tid) { int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)]; + /* we do refcounting here, so don't use the queue reason refcounting */ + if (atomic_inc_return(&sdata->local->agg_queue_stop[queue]) == 1) ieee80211_stop_queue_by_reason( &sdata->local->hw, queue, - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + IEEE80211_QUEUE_STOP_REASON_AGGREGATION, + false); __acquire(agg_queue); } @@ -185,7 +188,8 @@ ieee80211_wake_queue_agg(struct ieee80211_sub_if_data *sdata, int tid) if (atomic_dec_return(&sdata->local->agg_queue_stop[queue]) == 0) ieee80211_wake_queue_by_reason( &sdata->local->hw, queue, - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + IEEE80211_QUEUE_STOP_REASON_AGGREGATION, + false); __release(agg_queue); } diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index af3eac482acd..e920d48f0209 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -837,7 +837,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) if (!ieee80211_csa_needs_block_tx(local)) ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_CSA); + IEEE80211_QUEUE_STOP_REASON_CSA, + false); mutex_unlock(&local->mtx); kfree(sdata->u.ap.next_beacon); @@ -2828,7 +2829,8 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) if (!ieee80211_csa_needs_block_tx(local)) ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_CSA); + IEEE80211_QUEUE_STOP_REASON_CSA, + false); return 0; } @@ -3060,7 +3062,8 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, if (sdata->csa_block_tx) ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_CSA); + IEEE80211_QUEUE_STOP_REASON_CSA, + false); if (changed) { ieee80211_bss_info_change_notify(sdata, changed); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index d9af7ef3c11a..a0c7da809744 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -922,6 +922,8 @@ enum queue_stop_reason { IEEE80211_QUEUE_STOP_REASON_SKB_ADD, IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL, IEEE80211_QUEUE_STOP_REASON_FLUSH, + + IEEE80211_QUEUE_STOP_REASONS, }; #ifdef CONFIG_MAC80211_LEDS @@ -1018,6 +1020,7 @@ struct ieee80211_local { struct workqueue_struct *workqueue; unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES]; + int q_stop_reasons[IEEE80211_MAX_QUEUES][IEEE80211_QUEUE_STOP_REASONS]; /* also used to protect ampdu_ac_queue and amdpu_ac_stop_refcnt */ spinlock_t queue_stop_reason_lock; @@ -1715,14 +1718,18 @@ void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata, void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, unsigned long queues, - enum queue_stop_reason reason); + enum queue_stop_reason reason, + bool refcounted); void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, unsigned long queues, - enum queue_stop_reason reason); + enum queue_stop_reason reason, + bool refcounted); void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, - enum queue_stop_reason reason); + enum queue_stop_reason reason, + bool refcounted); void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue, - enum queue_stop_reason reason); + enum queue_stop_reason reason, + bool refcounted); void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue); void ieee80211_add_pending_skb(struct ieee80211_local *local, struct sk_buff *skb); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index db5afc7faa22..1971d2418d44 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -844,7 +844,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, if (!ieee80211_csa_needs_block_tx(local)) ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_CSA); + IEEE80211_QUEUE_STOP_REASON_CSA, + false); mutex_unlock(&local->mtx); sdata_unlock(sdata); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 0512a5096f0f..e0ab4320a078 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -272,7 +272,8 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw) /* use this reason, ieee80211_reconfig will unblock it */ ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_SUSPEND); + IEEE80211_QUEUE_STOP_REASON_SUSPEND, + false); /* * Stop all Rx during the reconfig. We don't want state changes diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 3345401be1b3..1ab1884eddbf 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -983,7 +983,8 @@ static void ieee80211_chswitch_work(struct work_struct *work) if (!ieee80211_csa_needs_block_tx(local)) ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_CSA); + IEEE80211_QUEUE_STOP_REASON_CSA, + false); mutex_unlock(&local->mtx); ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; @@ -1115,7 +1116,8 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, if (sdata->csa_block_tx) ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_CSA); + IEEE80211_QUEUE_STOP_REASON_CSA, + false); mutex_unlock(&local->mtx); if (local->ops->channel_switch) { @@ -1385,7 +1387,8 @@ void ieee80211_dynamic_ps_disable_work(struct work_struct *work) ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_PS); + IEEE80211_QUEUE_STOP_REASON_PS, + false); } void ieee80211_dynamic_ps_enable_work(struct work_struct *work) @@ -1833,7 +1836,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, if (!ieee80211_csa_needs_block_tx(local)) ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_CSA); + IEEE80211_QUEUE_STOP_REASON_CSA, + false); mutex_unlock(&local->mtx); sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM; @@ -2082,7 +2086,8 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) if (!ieee80211_csa_needs_block_tx(local)) ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_CSA); + IEEE80211_QUEUE_STOP_REASON_CSA, + false); mutex_unlock(&local->mtx); cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index 7a17decd27f9..ff20b2ebdb30 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -119,7 +119,8 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local) * before sending nullfunc to enable powersave at the AP. */ ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL); + IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL, + false); ieee80211_flush_queues(local, NULL); mutex_lock(&local->iflist_mtx); @@ -182,7 +183,8 @@ void ieee80211_offchannel_return(struct ieee80211_local *local) mutex_unlock(&local->iflist_mtx); ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL); + IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL, + false); } void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc) diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index d478b880a0af..4c5192e0d66c 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -35,7 +35,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_SUSPEND); + IEEE80211_QUEUE_STOP_REASON_SUSPEND, + false); /* flush out all packets */ synchronize_net(); @@ -74,7 +75,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) } ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_SUSPEND); + IEEE80211_QUEUE_STOP_REASON_SUSPEND, + false); return err; } else if (err > 0) { WARN_ON(err != 1); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 9b3d94e7c4be..f6018178f33c 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -250,7 +250,8 @@ ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx) if (local->hw.conf.flags & IEEE80211_CONF_PS) { ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_PS); + IEEE80211_QUEUE_STOP_REASON_PS, + false); ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED; ieee80211_queue_work(&local->hw, &local->dynamic_ps_disable_work); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 6d29e40538ad..4e8513cfdae5 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -317,7 +317,8 @@ void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue) } static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, - enum queue_stop_reason reason) + enum queue_stop_reason reason, + bool refcounted) { struct ieee80211_local *local = hw_to_local(hw); @@ -329,7 +330,13 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, if (!test_bit(reason, &local->queue_stop_reasons[queue])) return; - __clear_bit(reason, &local->queue_stop_reasons[queue]); + if (!refcounted) + local->q_stop_reasons[queue][reason] = 0; + else + local->q_stop_reasons[queue][reason]--; + + if (local->q_stop_reasons[queue][reason] == 0) + __clear_bit(reason, &local->queue_stop_reasons[queue]); if (local->queue_stop_reasons[queue] != 0) /* someone still has this queue stopped */ @@ -344,25 +351,28 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, } void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, - enum queue_stop_reason reason) + enum queue_stop_reason reason, + bool refcounted) { struct ieee80211_local *local = hw_to_local(hw); unsigned long flags; spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - __ieee80211_wake_queue(hw, queue, reason); + __ieee80211_wake_queue(hw, queue, reason, refcounted); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue) { ieee80211_wake_queue_by_reason(hw, queue, - IEEE80211_QUEUE_STOP_REASON_DRIVER); + IEEE80211_QUEUE_STOP_REASON_DRIVER, + false); } EXPORT_SYMBOL(ieee80211_wake_queue); static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, - enum queue_stop_reason reason) + enum queue_stop_reason reason, + bool refcounted) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_sub_if_data *sdata; @@ -373,10 +383,13 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, if (WARN_ON(queue >= hw->queues)) return; - if (test_bit(reason, &local->queue_stop_reasons[queue])) - return; + if (!refcounted) + local->q_stop_reasons[queue][reason] = 1; + else + local->q_stop_reasons[queue][reason]++; - __set_bit(reason, &local->queue_stop_reasons[queue]); + if (__test_and_set_bit(reason, &local->queue_stop_reasons[queue])) + return; if (local->hw.queues < IEEE80211_NUM_ACS) n_acs = 1; @@ -398,20 +411,22 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, } void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue, - enum queue_stop_reason reason) + enum queue_stop_reason reason, + bool refcounted) { struct ieee80211_local *local = hw_to_local(hw); unsigned long flags; spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - __ieee80211_stop_queue(hw, queue, reason); + __ieee80211_stop_queue(hw, queue, reason, refcounted); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue) { ieee80211_stop_queue_by_reason(hw, queue, - IEEE80211_QUEUE_STOP_REASON_DRIVER); + IEEE80211_QUEUE_STOP_REASON_DRIVER, + false); } EXPORT_SYMBOL(ieee80211_stop_queue); @@ -429,9 +444,11 @@ void ieee80211_add_pending_skb(struct ieee80211_local *local, } spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - __ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD); + __ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD, + false); __skb_queue_tail(&local->pending[queue], skb); - __ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD); + __ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD, + false); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } @@ -455,20 +472,23 @@ void ieee80211_add_pending_skbs(struct ieee80211_local *local, queue = info->hw_queue; __ieee80211_stop_queue(hw, queue, - IEEE80211_QUEUE_STOP_REASON_SKB_ADD); + IEEE80211_QUEUE_STOP_REASON_SKB_ADD, + false); __skb_queue_tail(&local->pending[queue], skb); } for (i = 0; i < hw->queues; i++) __ieee80211_wake_queue(hw, i, - IEEE80211_QUEUE_STOP_REASON_SKB_ADD); + IEEE80211_QUEUE_STOP_REASON_SKB_ADD, + false); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, unsigned long queues, - enum queue_stop_reason reason) + enum queue_stop_reason reason, + bool refcounted) { struct ieee80211_local *local = hw_to_local(hw); unsigned long flags; @@ -477,7 +497,7 @@ void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, spin_lock_irqsave(&local->queue_stop_reason_lock, flags); for_each_set_bit(i, &queues, hw->queues) - __ieee80211_stop_queue(hw, i, reason); + __ieee80211_stop_queue(hw, i, reason, refcounted); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } @@ -485,7 +505,8 @@ void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, void ieee80211_stop_queues(struct ieee80211_hw *hw) { ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_DRIVER); + IEEE80211_QUEUE_STOP_REASON_DRIVER, + false); } EXPORT_SYMBOL(ieee80211_stop_queues); @@ -508,7 +529,8 @@ EXPORT_SYMBOL(ieee80211_queue_stopped); void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, unsigned long queues, - enum queue_stop_reason reason) + enum queue_stop_reason reason, + bool refcounted) { struct ieee80211_local *local = hw_to_local(hw); unsigned long flags; @@ -517,7 +539,7 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, spin_lock_irqsave(&local->queue_stop_reason_lock, flags); for_each_set_bit(i, &queues, hw->queues) - __ieee80211_wake_queue(hw, i, reason); + __ieee80211_wake_queue(hw, i, reason, refcounted); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } @@ -525,7 +547,8 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, void ieee80211_wake_queues(struct ieee80211_hw *hw) { ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_DRIVER); + IEEE80211_QUEUE_STOP_REASON_DRIVER, + false); } EXPORT_SYMBOL(ieee80211_wake_queues); @@ -552,12 +575,14 @@ void ieee80211_flush_queues(struct ieee80211_local *local, } ieee80211_stop_queues_by_reason(&local->hw, queues, - IEEE80211_QUEUE_STOP_REASON_FLUSH); + IEEE80211_QUEUE_STOP_REASON_FLUSH, + false); drv_flush(local, sdata, queues, false); ieee80211_wake_queues_by_reason(&local->hw, queues, - IEEE80211_QUEUE_STOP_REASON_FLUSH); + IEEE80211_QUEUE_STOP_REASON_FLUSH, + false); } static void __iterate_active_interfaces(struct ieee80211_local *local, @@ -1797,7 +1822,8 @@ int ieee80211_reconfig(struct ieee80211_local *local) } ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_SUSPEND); + IEEE80211_QUEUE_STOP_REASON_SUSPEND, + false); /* * Reconfigure sched scan if it was interrupted by FW restart or From 26da23b6950cd1aaae86caa541eb4befc9e96e1d Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Fri, 13 Jun 2014 16:30:06 +0300 Subject: [PATCH 123/274] mac80211: add functions to stop and wake all queues assigned to a vif In some cases we may want to stop the queues of a single vif (for instance during a channel-switch). Add a function that stops all the queues that are assigned to a vif. If a queue is assigned to more than one vif, the corresponding netdev subqueue of the other vif(s) will also be stopped. If the HW doesn't set the IEEE80211_HW_QUEUE_CONTROL flag, then all queues are stopped. Also add a corresponding function to wake the queues of a vif back. Signed-off-by: Luciano Coelho Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 6 ++++++ net/mac80211/util.c | 41 ++++++++++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a0c7da809744..b202df2aebe8 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1720,6 +1720,12 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, unsigned long queues, enum queue_stop_reason reason, bool refcounted); +void ieee80211_stop_vif_queues(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + enum queue_stop_reason reason); +void ieee80211_wake_vif_queues(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + enum queue_stop_reason reason); void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, unsigned long queues, enum queue_stop_reason reason, diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 4e8513cfdae5..42d448d765b4 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -552,13 +552,11 @@ void ieee80211_wake_queues(struct ieee80211_hw *hw) } EXPORT_SYMBOL(ieee80211_wake_queues); -void ieee80211_flush_queues(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata) +static unsigned int +ieee80211_get_vif_queues(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) { - u32 queues; - - if (!local->ops->flush) - return; + unsigned int queues; if (sdata && local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) { int ac; @@ -574,6 +572,19 @@ void ieee80211_flush_queues(struct ieee80211_local *local, queues = BIT(local->hw.queues) - 1; } + return queues; +} + +void ieee80211_flush_queues(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + unsigned int queues; + + if (!local->ops->flush) + return; + + queues = ieee80211_get_vif_queues(local, sdata); + ieee80211_stop_queues_by_reason(&local->hw, queues, IEEE80211_QUEUE_STOP_REASON_FLUSH, false); @@ -585,6 +596,24 @@ void ieee80211_flush_queues(struct ieee80211_local *local, false); } +void ieee80211_stop_vif_queues(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + enum queue_stop_reason reason) +{ + ieee80211_stop_queues_by_reason(&local->hw, + ieee80211_get_vif_queues(local, sdata), + reason, true); +} + +void ieee80211_wake_vif_queues(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + enum queue_stop_reason reason) +{ + ieee80211_wake_queues_by_reason(&local->hw, + ieee80211_get_vif_queues(local, sdata), + reason, true); +} + static void __iterate_active_interfaces(struct ieee80211_local *local, u32 iter_flags, void (*iterator)(void *data, u8 *mac, From a46992b441f097a971cca39f49d07a0d16a1c0d8 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Fri, 13 Jun 2014 16:30:07 +0300 Subject: [PATCH 124/274] mac80211: stop only the queues assigned to the vif during channel switch Instead of stopping all the hardware queues during channel switch, which is especially bad when we have large CSA counts, stop only the queues that are assigned to the vif that is performing the channel switch. Additionally, check for (sdata->csa_block_tx) instead of calling ieee80211_csa_needs_block_tx(), which can now be removed. Signed-off-by: Luciano Coelho Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 52 ++++++++++---------------------------- net/mac80211/ieee80211_i.h | 1 - net/mac80211/iface.c | 10 ++++---- net/mac80211/mlme.c | 36 +++++++++++++------------- 4 files changed, 35 insertions(+), 64 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index e920d48f0209..a0d7a0362f1f 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -790,31 +790,6 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev, return 0; } -bool ieee80211_csa_needs_block_tx(struct ieee80211_local *local) -{ - struct ieee80211_sub_if_data *sdata; - - lockdep_assert_held(&local->mtx); - - rcu_read_lock(); - list_for_each_entry_rcu(sdata, &local->interfaces, list) { - if (!ieee80211_sdata_running(sdata)) - continue; - - if (!sdata->vif.csa_active) - continue; - - if (!sdata->csa_block_tx) - continue; - - rcu_read_unlock(); - return true; - } - rcu_read_unlock(); - - return false; -} - static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -834,11 +809,12 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) /* abort any running channel switch */ mutex_lock(&local->mtx); sdata->vif.csa_active = false; - if (!ieee80211_csa_needs_block_tx(local)) - ieee80211_wake_queues_by_reason(&local->hw, - IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_CSA, - false); + if (sdata->csa_block_tx) { + ieee80211_wake_vif_queues(local, sdata, + IEEE80211_QUEUE_STOP_REASON_CSA); + sdata->csa_block_tx = false; + } + mutex_unlock(&local->mtx); kfree(sdata->u.ap.next_beacon); @@ -2826,11 +2802,11 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) ieee80211_bss_info_change_notify(sdata, changed); cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef); - if (!ieee80211_csa_needs_block_tx(local)) - ieee80211_wake_queues_by_reason(&local->hw, - IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_CSA, - false); + if (sdata->csa_block_tx) { + ieee80211_wake_vif_queues(local, sdata, + IEEE80211_QUEUE_STOP_REASON_CSA); + sdata->csa_block_tx = false; + } return 0; } @@ -3060,10 +3036,8 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, sdata->vif.csa_active = true; if (sdata->csa_block_tx) - ieee80211_stop_queues_by_reason(&local->hw, - IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_CSA, - false); + ieee80211_stop_vif_queues(local, sdata, + IEEE80211_QUEUE_STOP_REASON_CSA); if (changed) { ieee80211_bss_info_change_notify(sdata, changed); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index b202df2aebe8..1fd50f173653 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1489,7 +1489,6 @@ void ieee80211_sw_roc_work(struct work_struct *work); void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc); /* channel switch handling */ -bool ieee80211_csa_needs_block_tx(struct ieee80211_local *local); void ieee80211_csa_finalize_work(struct work_struct *work); int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_csa_settings *params); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 1971d2418d44..2a12b8aa6aad 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -841,11 +841,11 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, sdata_lock(sdata); mutex_lock(&local->mtx); sdata->vif.csa_active = false; - if (!ieee80211_csa_needs_block_tx(local)) - ieee80211_wake_queues_by_reason(&local->hw, - IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_CSA, - false); + if (sdata->csa_block_tx) { + ieee80211_wake_vif_queues(local, sdata, + IEEE80211_QUEUE_STOP_REASON_CSA); + sdata->csa_block_tx = false; + } mutex_unlock(&local->mtx); sdata_unlock(sdata); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 1ab1884eddbf..eccc8492a59c 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -980,11 +980,11 @@ static void ieee80211_chswitch_work(struct work_struct *work) mutex_lock(&local->mtx); sdata->vif.csa_active = false; /* XXX: wait for a beacon first? */ - if (!ieee80211_csa_needs_block_tx(local)) - ieee80211_wake_queues_by_reason(&local->hw, - IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_CSA, - false); + if (sdata->csa_block_tx) { + ieee80211_wake_vif_queues(local, sdata, + IEEE80211_QUEUE_STOP_REASON_CSA); + sdata->csa_block_tx = false; + } mutex_unlock(&local->mtx); ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; @@ -1114,10 +1114,8 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, sdata->csa_block_tx = csa_ie.mode; if (sdata->csa_block_tx) - ieee80211_stop_queues_by_reason(&local->hw, - IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_CSA, - false); + ieee80211_stop_vif_queues(local, sdata, + IEEE80211_QUEUE_STOP_REASON_CSA); mutex_unlock(&local->mtx); if (local->ops->channel_switch) { @@ -1833,11 +1831,11 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ieee80211_vif_release_channel(sdata); sdata->vif.csa_active = false; - if (!ieee80211_csa_needs_block_tx(local)) - ieee80211_wake_queues_by_reason(&local->hw, - IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_CSA, - false); + if (sdata->csa_block_tx) { + ieee80211_wake_vif_queues(local, sdata, + IEEE80211_QUEUE_STOP_REASON_CSA); + sdata->csa_block_tx = false; + } mutex_unlock(&local->mtx); sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM; @@ -2083,11 +2081,11 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) mutex_lock(&local->mtx); sdata->vif.csa_active = false; - if (!ieee80211_csa_needs_block_tx(local)) - ieee80211_wake_queues_by_reason(&local->hw, - IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_CSA, - false); + if (sdata->csa_block_tx) { + ieee80211_wake_vif_queues(local, sdata, + IEEE80211_QUEUE_STOP_REASON_CSA); + sdata->csa_block_tx = false; + } mutex_unlock(&local->mtx); cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, From 9deba04d0f0b43ca60a867f006e06625ad85f8c2 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 11 Jun 2014 17:18:18 +0300 Subject: [PATCH 125/274] mac80211: clarify TDLS Tx handling Rename the flags used in the Tx path and add an explanation for the reasons to drop, send directly or through the AP. Signed-off-by: Arik Nemtsov Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index f6018178f33c..865bdaf06ff1 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1811,7 +1811,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, int nh_pos, h_pos; struct sta_info *sta = NULL; bool wme_sta = false, authorized = false, tdls_auth = false; - bool tdls_direct = false; + bool tdls_peer = false, tdls_setup_frame = false; bool multicast; u32 info_flags = 0; u16 info_id = 0; @@ -1953,34 +1953,35 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, #endif case NL80211_IFTYPE_STATION: if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) { - bool tdls_peer = false; - sta = sta_info_get(sdata, skb->data); if (sta) { authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); wme_sta = test_sta_flag(sta, WLAN_STA_WME); tdls_peer = test_sta_flag(sta, - WLAN_STA_TDLS_PEER); + WLAN_STA_TDLS_PEER); tdls_auth = test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); } - /* - * If the TDLS link is enabled, send everything - * directly. Otherwise, allow TDLS setup frames - * to be transmitted indirectly. - */ - tdls_direct = tdls_peer && (tdls_auth || - !(ethertype == ETH_P_TDLS && skb->len > 14 && - skb->data[14] == WLAN_TDLS_SNAP_RFTYPE)); + if (tdls_peer) + tdls_setup_frame = + ethertype == ETH_P_TDLS && + skb->len > 14 && + skb->data[14] == WLAN_TDLS_SNAP_RFTYPE; } - if (tdls_direct) { - /* link during setup - throw out frames to peer */ - if (!tdls_auth) - goto fail_rcu; + /* + * TDLS link during setup - throw out frames to peer. We allow + * TDLS-setup frames to unauthorized peers for the special case + * of a link teardown after a TDLS sta is removed due to being + * unreachable. + */ + if (tdls_peer && !tdls_auth && !tdls_setup_frame) + goto fail_rcu; + /* send direct packets to authorized TDLS peers */ + if (tdls_peer && tdls_auth) { /* DA SA BSSID */ memcpy(hdr.addr1, skb->data, ETH_ALEN); memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); From 68885a54cd04742a2e6e10e9d3ff5976d046832e Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 11 Jun 2014 17:18:19 +0300 Subject: [PATCH 126/274] mac80211: set auth flags after other station info For TDLS, the AUTHORIZED flag arrives with all other important station info (supported rates, HT/VHT caps, ...). Make sure to set the station state in the low-level driver after transferring this information to the mac80211 STA entry. This aligns the STA information during sta_state callbacks with the non-TDLS case. Signed-off-by: Arik Nemtsov Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index a0d7a0362f1f..73c9e1003e25 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -997,9 +997,12 @@ static int sta_apply_parameters(struct ieee80211_local *local, } } - ret = sta_apply_auth_flags(local, sta, mask, set); - if (ret) - return ret; + /* auth flags will be set later for TDLS stations */ + if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { + ret = sta_apply_auth_flags(local, sta, mask, set); + if (ret) + return ret; + } if (mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) { if (set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) @@ -1136,6 +1139,13 @@ static int sta_apply_parameters(struct ieee80211_local *local, #endif } + /* set the STA state after all sta info from usermode has been set */ + if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { + ret = sta_apply_auth_flags(local, sta, mask, set); + if (ret) + return ret; + } + return 0; } From 17e6a59a365ac993c18f762d3a259635957f1026 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 11 Jun 2014 17:18:20 +0300 Subject: [PATCH 127/274] mac80211: cleanup TDLS state during failed setup When setting up a TDLS session, register a delayed work to remove the peer if setup times out. Prevent concurrent setups to support this capacity. Signed-off-by: Arik Nemtsov Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 4 ++ net/mac80211/iface.c | 2 + net/mac80211/tdls.c | 133 ++++++++++++++++++++++++++++++------- 3 files changed, 115 insertions(+), 24 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 1fd50f173653..fc687d2a7518 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -794,6 +794,9 @@ struct ieee80211_sub_if_data { bool radar_required; struct delayed_work dfs_cac_timer_work; + u8 tdls_peer[ETH_ALEN] __aligned(2); + struct delayed_work tdls_peer_del_work; + /* * AP this belongs to: self in AP mode and * corresponding AP in VLAN mode, NULL for @@ -1878,3 +1881,4 @@ extern const struct ethtool_ops ieee80211_ethtool_ops; #endif #endif /* IEEE80211_I_H */ +void ieee80211_tdls_peer_del_work(struct work_struct *wk); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 2a12b8aa6aad..bbf51b2f0651 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1672,6 +1672,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, ieee80211_dfs_cac_timer_work); INIT_DELAYED_WORK(&sdata->dec_tailroom_needed_wk, ieee80211_delayed_tailroom_dec); + INIT_DELAYED_WORK(&sdata->tdls_peer_del_work, + ieee80211_tdls_peer_del_work); for (i = 0; i < IEEE80211_NUM_BANDS; i++) { struct ieee80211_supported_band *sband; diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 652813b2d3df..cafcbde70018 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -10,6 +10,27 @@ #include #include "ieee80211_i.h" +/* give usermode some time for retries in setting up the TDLS session */ +#define TDLS_PEER_SETUP_TIMEOUT (15 * HZ) + +void ieee80211_tdls_peer_del_work(struct work_struct *wk) +{ + struct ieee80211_sub_if_data *sdata; + struct ieee80211_local *local; + + sdata = container_of(wk, struct ieee80211_sub_if_data, + tdls_peer_del_work.work); + local = sdata->local; + + mutex_lock(&local->mtx); + if (!is_zero_ether_addr(sdata->tdls_peer)) { + tdls_dbg(sdata, "TDLS del peer %pM\n", sdata->tdls_peer); + sta_info_destroy_addr(sdata, sdata->tdls_peer); + eth_zero_addr(sdata->tdls_peer); + } + mutex_unlock(&local->mtx); +} + static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb) { u8 *pos = (void *)skb_put(skb, 7); @@ -168,10 +189,12 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, return 0; } -int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, - const u8 *peer, u8 action_code, u8 dialog_token, - u16 status_code, u32 peer_capability, - const u8 *extra_ies, size_t extra_ies_len) +static int +ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u8 action_code, + u8 dialog_token, u16 status_code, + u32 peer_capability, const u8 *extra_ies, + size_t extra_ies_len) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; @@ -179,17 +202,6 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, bool send_direct; int ret; - if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) - return -ENOTSUPP; - - /* make sure we are in managed mode, and associated */ - if (sdata->vif.type != NL80211_IFTYPE_STATION || - !sdata->u.mgd.associated) - return -EINVAL; - - tdls_dbg(sdata, "TDLS mgmt action %d peer %pM\n", - action_code, peer); - skb = dev_alloc_skb(local->hw.extra_tx_headroom + max(sizeof(struct ieee80211_mgmt), sizeof(struct ieee80211_tdls_data)) + @@ -284,11 +296,64 @@ fail: return ret; } +int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, u32 peer_capability, + const u8 *extra_ies, size_t extra_ies_len) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; + int ret; + + if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) + return -ENOTSUPP; + + /* make sure we are in managed mode, and associated */ + if (sdata->vif.type != NL80211_IFTYPE_STATION || + !sdata->u.mgd.associated) + return -EINVAL; + + mutex_lock(&local->mtx); + + /* we don't support concurrent TDLS peer setups */ + if (!is_zero_ether_addr(sdata->tdls_peer) && + !ether_addr_equal(sdata->tdls_peer, peer) && + (action_code == WLAN_TDLS_SETUP_REQUEST || + action_code == WLAN_TDLS_SETUP_RESPONSE)) { + ret = -EBUSY; + goto exit; + } + + ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code, + dialog_token, status_code, + peer_capability, extra_ies, + extra_ies_len); + if (ret < 0) + goto exit; + + if (action_code == WLAN_TDLS_SETUP_REQUEST || + action_code == WLAN_TDLS_SETUP_RESPONSE) { + memcpy(sdata->tdls_peer, peer, ETH_ALEN); + ieee80211_queue_delayed_work(&sdata->local->hw, + &sdata->tdls_peer_del_work, + TDLS_PEER_SETUP_TIMEOUT); + } + +exit: + mutex_unlock(&local->mtx); + + tdls_dbg(sdata, "TDLS mgmt action %d peer %pM status %d\n", + action_code, peer, ret); + return ret; +} + int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, enum nl80211_tdls_operation oper) { struct sta_info *sta; struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; + int ret; if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) return -ENOTSUPP; @@ -296,6 +361,18 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, if (sdata->vif.type != NL80211_IFTYPE_STATION) return -EINVAL; + switch (oper) { + case NL80211_TDLS_ENABLE_LINK: + case NL80211_TDLS_DISABLE_LINK: + break; + case NL80211_TDLS_TEARDOWN: + case NL80211_TDLS_SETUP: + case NL80211_TDLS_DISCOVERY_REQ: + /* We don't support in-driver setup/teardown/discovery */ + return -ENOTSUPP; + } + + mutex_lock(&local->mtx); tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer); switch (oper) { @@ -304,22 +381,30 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, sta = sta_info_get(sdata, peer); if (!sta) { rcu_read_unlock(); - return -ENOLINK; + ret = -ENOLINK; + break; } set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); rcu_read_unlock(); + + WARN_ON_ONCE(is_zero_ether_addr(sdata->tdls_peer) || + !ether_addr_equal(sdata->tdls_peer, peer)); + ret = 0; break; case NL80211_TDLS_DISABLE_LINK: - return sta_info_destroy_addr(sdata, peer); - case NL80211_TDLS_TEARDOWN: - case NL80211_TDLS_SETUP: - case NL80211_TDLS_DISCOVERY_REQ: - /* We don't support in-driver setup/teardown/discovery */ - return -ENOTSUPP; + ret = sta_info_destroy_addr(sdata, peer); + break; default: - return -ENOTSUPP; + ret = -ENOTSUPP; + break; } - return 0; + if (ret == 0 && ether_addr_equal(sdata->tdls_peer, peer)) { + cancel_delayed_work(&sdata->tdls_peer_del_work); + eth_zero_addr(sdata->tdls_peer); + } + + mutex_unlock(&local->mtx); + return ret; } From 31fa97c5defca3895dc6c823096d7ba59df76125 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 11 Jun 2014 17:18:21 +0300 Subject: [PATCH 128/274] cfg80211: pass TDLS initiator in tdls_mgmt operations The TDLS initiator is set once during link setup. If determines the address ordering in the link identifier IE. Fix dependent drivers - mwifiex and mac80211. Signed-off-by: Arik Nemtsov Signed-off-by: Johannes Berg --- drivers/net/wireless/mwifiex/cfg80211.c | 3 ++- include/net/cfg80211.h | 2 +- include/uapi/linux/nl80211.h | 5 +++++ net/mac80211/ieee80211_i.h | 3 ++- net/mac80211/tdls.c | 3 ++- net/wireless/nl80211.c | 4 ++++ net/wireless/rdev-ops.h | 6 +++--- net/wireless/trace.h | 10 +++++++--- 8 files changed, 26 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index e95dec91a561..149d2e693bdd 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -2631,7 +2631,8 @@ static int mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capability, - const u8 *extra_ies, size_t extra_ies_len) + bool initiator, const u8 *extra_ies, + size_t extra_ies_len) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); int ret; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 29cb4b2bee5a..b9eeae3990cf 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2500,7 +2500,7 @@ struct cfg80211_ops { int (*tdls_mgmt)(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capability, - const u8 *buf, size_t len); + bool initiator, const u8 *buf, size_t len); int (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, enum nl80211_tdls_operation oper); diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index be9519b52bb1..f1db15b9c041 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1591,6 +1591,9 @@ enum nl80211_commands { * creation then the new interface will be owned by the netlink socket * that created it and will be destroyed when the socket is closed * + * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is + * the TDLS link initiator. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1931,6 +1934,8 @@ enum nl80211_attrs { NL80211_ATTR_CSA_C_OFFSETS_TX, NL80211_ATTR_MAX_CSA_COUNTERS, + NL80211_ATTR_TDLS_INITIATOR, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index fc687d2a7518..75f79c168e90 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1867,7 +1867,8 @@ int ieee80211_max_num_channels(struct ieee80211_local *local); int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capability, - const u8 *extra_ies, size_t extra_ies_len); + bool initiator, const u8 *extra_ies, + size_t extra_ies_len); int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, enum nl80211_tdls_operation oper); diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index cafcbde70018..0b3ca2ce7ea4 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -299,7 +299,8 @@ fail: int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capability, - const u8 *extra_ies, size_t extra_ies_len) + bool initiator, const u8 *extra_ies, + size_t extra_ies_len) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ba4f1723c83a..8f46b8ffbcf6 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -337,6 +337,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 }, [NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG }, [NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG }, + [NL80211_ATTR_TDLS_INITIATOR] = { .type = NLA_FLAG }, [NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG }, [NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN }, @@ -7365,6 +7366,7 @@ static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info) u32 peer_capability = 0; u16 status_code; u8 *peer; + bool initiator; if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) || !rdev->ops->tdls_mgmt) @@ -7381,12 +7383,14 @@ static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info) action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]); status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]); dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]); + initiator = nla_get_flag(info->attrs[NL80211_ATTR_TDLS_INITIATOR]); if (info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY]) peer_capability = nla_get_u32(info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY]); return rdev_tdls_mgmt(rdev, dev, peer, action_code, dialog_token, status_code, peer_capability, + initiator, nla_data(info->attrs[NL80211_ATTR_IE]), nla_len(info->attrs[NL80211_ATTR_IE])); } diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index f552b0abbd70..56c2240c30ce 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -751,15 +751,15 @@ static inline int rdev_tdls_mgmt(struct cfg80211_registered_device *rdev, struct net_device *dev, u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capability, - const u8 *buf, size_t len) + bool initiator, const u8 *buf, size_t len) { int ret; trace_rdev_tdls_mgmt(&rdev->wiphy, dev, peer, action_code, dialog_token, status_code, peer_capability, - buf, len); + initiator, buf, len); ret = rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code, dialog_token, status_code, peer_capability, - buf, len); + initiator, buf, len); trace_rdev_return_int(&rdev->wiphy, ret); return ret; } diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 174559aade57..85474ee501eb 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1454,9 +1454,9 @@ TRACE_EVENT(rdev_tdls_mgmt, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capability, - const u8 *buf, size_t len), + bool initiator, const u8 *buf, size_t len), TP_ARGS(wiphy, netdev, peer, action_code, dialog_token, status_code, - peer_capability, buf, len), + peer_capability, initiator, buf, len), TP_STRUCT__entry( WIPHY_ENTRY NETDEV_ENTRY @@ -1465,6 +1465,7 @@ TRACE_EVENT(rdev_tdls_mgmt, __field(u8, dialog_token) __field(u16, status_code) __field(u32, peer_capability) + __field(bool, initiator) __dynamic_array(u8, buf, len) ), TP_fast_assign( @@ -1475,13 +1476,16 @@ TRACE_EVENT(rdev_tdls_mgmt, __entry->dialog_token = dialog_token; __entry->status_code = status_code; __entry->peer_capability = peer_capability; + __entry->initiator = initiator; memcpy(__get_dynamic_array(buf), buf, len); ), TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", action_code: %u, " - "dialog_token: %u, status_code: %u, peer_capability: %u buf: %#.2x ", + "dialog_token: %u, status_code: %u, peer_capability: %u " + "initiator: %s buf: %#.2x ", WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->action_code, __entry->dialog_token, __entry->status_code, __entry->peer_capability, + BOOL_TO_STR(__entry->initiator), ((u8 *)__get_dynamic_array(buf))[0]) ); From 2fb6b9b8e5f08df31bc4081b4d9f38701c59cfd3 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 11 Jun 2014 17:18:22 +0300 Subject: [PATCH 129/274] mac80211: use TDLS initiator in tdls_mgmt operations The TDLS initiator is set once during link setup. If determines the address ordering in the link identifier IE. Use the value from userspace in order to have a correct teardown packet. With the current code, a teardown from the responder side fails the TDLS MIC check because of a bad link identifier IE. Signed-off-by: Arik Nemtsov Signed-off-by: Johannes Berg --- net/mac80211/tdls.c | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 0b3ca2ce7ea4..c4a9af3b75e5 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -193,13 +193,14 @@ static int ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, - u32 peer_capability, const u8 *extra_ies, - size_t extra_ies_len) + u32 peer_capability, bool initiator, + const u8 *extra_ies, size_t extra_ies_len) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; struct sk_buff *skb = NULL; bool send_direct; + const u8 *init_addr, *rsp_addr; int ret; skb = dev_alloc_skb(local->hw.extra_tx_headroom + @@ -242,27 +243,42 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, if (extra_ies_len) memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); - /* the TDLS link IE is always added last */ + /* sanity check for initiator */ switch (action_code) { case WLAN_TDLS_SETUP_REQUEST: case WLAN_TDLS_SETUP_CONFIRM: - case WLAN_TDLS_TEARDOWN: case WLAN_TDLS_DISCOVERY_REQUEST: - /* we are the initiator */ - ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer, - sdata->u.mgd.bssid); + if (!initiator) { + ret = -EINVAL; + goto fail; + } break; case WLAN_TDLS_SETUP_RESPONSE: case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: - /* we are the responder */ - ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr, - sdata->u.mgd.bssid); + if (initiator) { + ret = -EINVAL; + goto fail; + } + break; + case WLAN_TDLS_TEARDOWN: + /* any value is ok */ break; default: ret = -ENOTSUPP; goto fail; } + if (initiator) { + init_addr = sdata->vif.addr; + rsp_addr = peer; + } else { + init_addr = peer; + rsp_addr = sdata->vif.addr; + } + + ieee80211_tdls_add_link_ie(skb, init_addr, rsp_addr, + sdata->u.mgd.bssid); + if (send_direct) { ieee80211_tx_skb(sdata, skb); return 0; @@ -327,8 +343,8 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code, dialog_token, status_code, - peer_capability, extra_ies, - extra_ies_len); + peer_capability, initiator, + extra_ies, extra_ies_len); if (ret < 0) goto exit; From 191dd46905ce9a25174dd9eea4ff3029802ac37c Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 11 Jun 2014 17:18:23 +0300 Subject: [PATCH 130/274] mac80211: split tdls_mgmt function There are setup/teardown specific actions to be done that accompany the sending of a TDLS management packet. Split the main function to simplify future additions. Signed-off-by: Arik Nemtsov Signed-off-by: Johannes Berg --- net/mac80211/tdls.c | 78 ++++++++++++++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 23 deletions(-) diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index c4a9af3b75e5..92d203a3be07 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -312,31 +312,21 @@ fail: return ret; } -int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, - const u8 *peer, u8 action_code, u8 dialog_token, - u16 status_code, u32 peer_capability, - bool initiator, const u8 *extra_ies, - size_t extra_ies_len) +static int +ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, u32 peer_capability, bool initiator, + const u8 *extra_ies, size_t extra_ies_len) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; int ret; - if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) - return -ENOTSUPP; - - /* make sure we are in managed mode, and associated */ - if (sdata->vif.type != NL80211_IFTYPE_STATION || - !sdata->u.mgd.associated) - return -EINVAL; - mutex_lock(&local->mtx); /* we don't support concurrent TDLS peer setups */ if (!is_zero_ether_addr(sdata->tdls_peer) && - !ether_addr_equal(sdata->tdls_peer, peer) && - (action_code == WLAN_TDLS_SETUP_REQUEST || - action_code == WLAN_TDLS_SETUP_RESPONSE)) { + !ether_addr_equal(sdata->tdls_peer, peer)) { ret = -EBUSY; goto exit; } @@ -348,16 +338,58 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, if (ret < 0) goto exit; - if (action_code == WLAN_TDLS_SETUP_REQUEST || - action_code == WLAN_TDLS_SETUP_RESPONSE) { - memcpy(sdata->tdls_peer, peer, ETH_ALEN); - ieee80211_queue_delayed_work(&sdata->local->hw, - &sdata->tdls_peer_del_work, - TDLS_PEER_SETUP_TIMEOUT); - } + memcpy(sdata->tdls_peer, peer, ETH_ALEN); + ieee80211_queue_delayed_work(&sdata->local->hw, + &sdata->tdls_peer_del_work, + TDLS_PEER_SETUP_TIMEOUT); exit: mutex_unlock(&local->mtx); + return ret; +} + +int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, u32 peer_capability, + bool initiator, const u8 *extra_ies, + size_t extra_ies_len) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + int ret; + + if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) + return -ENOTSUPP; + + /* make sure we are in managed mode, and associated */ + if (sdata->vif.type != NL80211_IFTYPE_STATION || + !sdata->u.mgd.associated) + return -EINVAL; + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_RESPONSE: + ret = ieee80211_tdls_mgmt_setup(wiphy, dev, peer, action_code, + dialog_token, status_code, + peer_capability, initiator, + extra_ies, extra_ies_len); + break; + case WLAN_TDLS_TEARDOWN: + case WLAN_TDLS_SETUP_CONFIRM: + case WLAN_TDLS_DISCOVERY_REQUEST: + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + /* no special handling */ + ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, + action_code, + dialog_token, + status_code, + peer_capability, + initiator, extra_ies, + extra_ies_len); + break; + default: + ret = -EOPNOTSUPP; + break; + } tdls_dbg(sdata, "TDLS mgmt action %d peer %pM status %d\n", action_code, peer, ret); From db67d661e82da22f751585a7f284a9251e8a2a51 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 11 Jun 2014 17:18:24 +0300 Subject: [PATCH 131/274] mac80211: implement proper Tx path flushing for TDLS As the spec mandates, flush data in the AP path before transmitting the first setup frame. Data packets transmitted during setup are already dropped in the Tx path. For the teardown flow, flush all packets in the direct path before transmitting the teardown frame. Un-authorize the peer sta after teardown is sent, forcing all subsequent Tx to the peer through the AP. Make sure to flush the queues when disabling the link to get the teardown packet out. Signed-off-by: Arik Nemtsov [adjust to Luca's new quuee API and stop only vif queues] Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 1 + net/mac80211/tdls.c | 57 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 75f79c168e90..6c8089429892 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -925,6 +925,7 @@ enum queue_stop_reason { IEEE80211_QUEUE_STOP_REASON_SKB_ADD, IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL, IEEE80211_QUEUE_STOP_REASON_FLUSH, + IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN, IEEE80211_QUEUE_STOP_REASONS, }; diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 92d203a3be07..0ba7e4c029c8 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -331,6 +331,8 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev, goto exit; } + ieee80211_flush_queues(local, sdata); + ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code, dialog_token, status_code, peer_capability, initiator, @@ -348,6 +350,52 @@ exit: return ret; } +static int +ieee80211_tdls_mgmt_teardown(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, u32 peer_capability, + bool initiator, const u8 *extra_ies, + size_t extra_ies_len) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; + struct sta_info *sta; + int ret; + + /* + * No packets can be transmitted to the peer via the AP during setup - + * the STA is set as a TDLS peer, but is not authorized. + * During teardown, we prevent direct transmissions by stopping the + * queues and flushing all direct packets. + */ + ieee80211_stop_vif_queues(local, sdata, + IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN); + ieee80211_flush_queues(local, sdata); + + ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code, + dialog_token, status_code, + peer_capability, initiator, + extra_ies, extra_ies_len); + if (ret < 0) + sdata_err(sdata, "Failed sending TDLS teardown packet %d\n", + ret); + + /* + * Remove the STA AUTH flag to force further traffic through the AP. If + * the STA was unreachable, it was already removed. + */ + rcu_read_lock(); + sta = sta_info_get(sdata, peer); + if (sta) + clear_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); + rcu_read_unlock(); + + ieee80211_wake_vif_queues(local, sdata, + IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN); + + return 0; +} + int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capability, @@ -374,6 +422,12 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, extra_ies, extra_ies_len); break; case WLAN_TDLS_TEARDOWN: + ret = ieee80211_tdls_mgmt_teardown(wiphy, dev, peer, + action_code, dialog_token, + status_code, + peer_capability, initiator, + extra_ies, extra_ies_len); + break; case WLAN_TDLS_SETUP_CONFIRM: case WLAN_TDLS_DISCOVERY_REQUEST: case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: @@ -442,6 +496,9 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, ret = 0; break; case NL80211_TDLS_DISABLE_LINK: + /* flush a potentially queued teardown packet */ + ieee80211_flush_queues(local, sdata); + ret = sta_info_destroy_addr(sdata, peer); break; default: From c887f0d3a03283cb6fe2c32aae62229bebd3fa32 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 11 Jun 2014 17:18:25 +0300 Subject: [PATCH 132/274] mac80211: add API to request TDLS operation from userspace Write a mac80211 to the cfg80211 API for requesting a userspace TDLS operation. Define TDLS specific reason codes that can be used here. Signed-off-by: Arik Nemtsov Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 3 +++ include/net/mac80211.h | 13 +++++++++++++ net/mac80211/tdls.c | 17 +++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 6bff13f74050..75d17e15da33 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1621,6 +1621,9 @@ enum ieee80211_reasoncode { WLAN_REASON_INVALID_RSN_IE_CAP = 22, WLAN_REASON_IEEE8021X_FAILED = 23, WLAN_REASON_CIPHER_SUITE_REJECTED = 24, + /* TDLS (802.11z) */ + WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE = 25, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED = 26, /* 802.11e */ WLAN_REASON_DISASSOC_UNSPECIFIED_QOS = 32, WLAN_REASON_DISASSOC_QAP_NO_BANDWIDTH = 33, diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 421b6ecb4b2c..8d876dc8b299 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -4815,4 +4815,17 @@ int ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr *attr, */ void ieee80211_update_p2p_noa(struct ieee80211_noa_data *data, u32 tsf); +/** + * ieee80211_tdls_oper - request userspace to perform a TDLS operation + * @vif: virtual interface + * @peer: the peer's destination address + * @oper: the requested TDLS operation + * @reason_code: reason code for the operation, valid for TDLS teardown + * @gfp: allocation flags + * + * See cfg80211_tdls_oper_request(). + */ +void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer, + enum nl80211_tdls_operation oper, + u16 reason_code, gfp_t gfp); #endif /* MAC80211_H */ diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 0ba7e4c029c8..6f3a3ad0cb7c 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -8,6 +8,7 @@ */ #include +#include #include "ieee80211_i.h" /* give usermode some time for retries in setting up the TDLS session */ @@ -514,3 +515,19 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, mutex_unlock(&local->mtx); return ret; } + +void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer, + enum nl80211_tdls_operation oper, + u16 reason_code, gfp_t gfp) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + + if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc) { + sdata_err(sdata, "Discarding TDLS oper %d - not STA or disconnected\n", + oper); + return; + } + + cfg80211_tdls_oper_request(sdata->dev, peer, oper, reason_code, gfp); +} +EXPORT_SYMBOL(ieee80211_tdls_oper_request); From 7adc3e4664bc5bd382fa181e6a4506f5b5b583d8 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 11 Jun 2014 17:18:26 +0300 Subject: [PATCH 133/274] mac80211: make sure TDLS peer STA exists during setup Make sure userspace added a TDLS peer station before invoking the transmission of the first setup frame. This ensures packets to the peer won't go through the AP path. Signed-off-by: Arik Nemtsov Signed-off-by: Johannes Berg --- net/mac80211/tdls.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 6f3a3ad0cb7c..b87e369561f4 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -332,6 +332,19 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev, goto exit; } + /* + * make sure we have a STA representing the peer so we drop or buffer + * non-TDLS-setup frames to the peer. We can't send other packets + * during setup through the AP path + */ + rcu_read_lock(); + if (!sta_info_get(sdata, peer)) { + rcu_read_unlock(); + ret = -ENOLINK; + goto exit; + } + rcu_read_unlock(); + ieee80211_flush_queues(local, sdata); ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code, From ee10f2c779b28c1d6e87ac3e1bbb1aa8b62fa891 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 11 Jun 2014 17:18:27 +0300 Subject: [PATCH 134/274] mac80211: protect TDLS discovery session After sending a TDLS discovery-request, we expect a reply to arrive on the AP's channel. We must stay on the channel (no PSM, scan, etc.), since a TDLS setup-response is a direct packet not buffered by the AP. Add a new mac80211 driver callback to allow discovery session protection. Signed-off-by: Arik Nemtsov Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- include/net/mac80211.h | 12 ++++++++++++ net/mac80211/driver-ops.h | 16 ++++++++++++++++ net/mac80211/tdls.c | 10 +++++++++- net/mac80211/trace.h | 7 +++++++ 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 8d876dc8b299..18c2bdbaf988 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2764,6 +2764,15 @@ enum ieee80211_roc_type { * mac80211 will transmit the frame right away. * The callback is optional and can (should!) sleep. * + * @mgd_protect_tdls_discover: Protect a TDLS discovery session. After sending + * a TDLS discovery-request, we expect a reply to arrive on the AP's + * channel. We must stay on the channel (no PSM, scan, etc.), since a TDLS + * setup-response is a direct packet not buffered by the AP. + * mac80211 will call this function just before the transmission of a TDLS + * discovery-request. The recommended period of protection is at least + * 2 * (DTIM period). + * The callback is optional and can sleep. + * * @add_chanctx: Notifies device driver about new channel context creation. * @remove_chanctx: Notifies device driver about channel context destruction. * @change_chanctx: Notifies device driver about channel context changes that @@ -2981,6 +2990,9 @@ struct ieee80211_ops { void (*mgd_prepare_tx)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); + void (*mgd_protect_tdls_discover)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); + int (*add_chanctx)(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx); void (*remove_chanctx)(struct ieee80211_hw *hw, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index bd782dcffcc7..2265bd7a44ba 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -970,6 +970,22 @@ static inline void drv_mgd_prepare_tx(struct ieee80211_local *local, trace_drv_return_void(local); } +static inline void +drv_mgd_protect_tdls_discover(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + might_sleep(); + + if (!check_sdata_in_driver(sdata)) + return; + WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION); + + trace_drv_mgd_protect_tdls_discover(local, sdata); + if (local->ops->mgd_protect_tdls_discover) + local->ops->mgd_protect_tdls_discover(&local->hw, &sdata->vif); + trace_drv_return_void(local); +} + static inline int drv_add_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *ctx) { diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index b87e369561f4..f7185338a0fa 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -10,6 +10,7 @@ #include #include #include "ieee80211_i.h" +#include "driver-ops.h" /* give usermode some time for retries in setting up the TDLS session */ #define TDLS_PEER_SETUP_TIMEOUT (15 * HZ) @@ -442,8 +443,15 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, peer_capability, initiator, extra_ies, extra_ies_len); break; - case WLAN_TDLS_SETUP_CONFIRM: case WLAN_TDLS_DISCOVERY_REQUEST: + /* + * Protect the discovery so we can hear the TDLS discovery + * response frame. It is transmitted directly and not buffered + * by the AP. + */ + drv_mgd_protect_tdls_discover(sdata->local, sdata); + /* fall-through */ + case WLAN_TDLS_SETUP_CONFIRM: case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: /* no special handling */ ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index cfe1a0688b5c..02ac535d1274 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -1330,6 +1330,13 @@ DEFINE_EVENT(local_sdata_evt, drv_mgd_prepare_tx, TP_ARGS(local, sdata) ); +DEFINE_EVENT(local_sdata_evt, drv_mgd_protect_tdls_discover, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + + TP_ARGS(local, sdata) +); + DECLARE_EVENT_CLASS(local_chanctx, TP_PROTO(struct ieee80211_local *local, struct ieee80211_chanctx *ctx), From 89879413eba4d9f57b69f84a32fd085d54057df3 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 26 May 2014 18:44:35 +0300 Subject: [PATCH 135/274] iwlwifi: mvm: rework sched scan channel configuration The current sched scan channel configuration code configures all the supported channels for scanning. However, this can result in SYSASSERT in some cases, when the configured channel is disabled. Instead, configure only the channels given in the req struct, and set the channel_count field appropriately. While on it, change the code to use channel->hw_value instead of recalculating the channel number. Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/scan.c | 63 +++++++------------------ 1 file changed, 18 insertions(+), 45 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 4b6c7d4bd199..eac2b424f6a0 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -588,9 +588,7 @@ static void iwl_build_scan_cmd(struct iwl_mvm *mvm, struct iwl_scan_offload_cmd *scan, struct iwl_mvm_scan_params *params) { - scan->channel_count = - mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels + - mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels; + scan->channel_count = req->n_channels; scan->quiet_time = cpu_to_le16(IWL_ACTIVE_QUIET_TIME); scan->quiet_plcp_th = cpu_to_le16(IWL_PLCP_QUIET_THRESH); scan->good_CRC_th = IWL_GOOD_CRC_TH_DEFAULT; @@ -669,61 +667,37 @@ static void iwl_build_channel_cfg(struct iwl_mvm *mvm, struct cfg80211_sched_scan_request *req, struct iwl_scan_channel_cfg *channels, enum ieee80211_band band, - int *head, int *tail, + int *head, u32 ssid_bitmap, struct iwl_mvm_scan_params *params) { - struct ieee80211_supported_band *s_band; - int n_channels = req->n_channels; - int i, j, index = 0; - bool partial; + int i, index = 0; - /* - * We have to configure all supported channels, even if we don't want to - * scan on them, but we have to send channels in the order that we want - * to scan. So add requested channels to head of the list and others to - * the end. - */ - s_band = &mvm->nvm_data->bands[band]; + for (i = 0; i < req->n_channels; i++) { + struct ieee80211_channel *chan = req->channels[i]; - for (i = 0; i < s_band->n_channels && *head <= *tail; i++) { - partial = false; - for (j = 0; j < n_channels; j++) - if (s_band->channels[i].center_freq == - req->channels[j]->center_freq) { - index = *head; - (*head)++; - /* - * Channels that came with the request will be - * in partial scan . - */ - partial = true; - break; - } - if (!partial) { - index = *tail; - (*tail)--; - } - channels->channel_number[index] = - cpu_to_le16(ieee80211_frequency_to_channel( - s_band->channels[i].center_freq)); + if (chan->band != band) + continue; + + index = *head; + (*head)++; + + channels->channel_number[index] = cpu_to_le16(chan->hw_value); channels->dwell_time[index][0] = params->dwell[band].active; channels->dwell_time[index][1] = params->dwell[band].passive; channels->iter_count[index] = cpu_to_le16(1); channels->iter_interval[index] = 0; - if (!(s_band->channels[i].flags & IEEE80211_CHAN_NO_IR)) + if (!(chan->flags & IEEE80211_CHAN_NO_IR)) channels->type[index] |= cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_ACTIVE); channels->type[index] |= - cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_FULL); - if (partial) - channels->type[index] |= - cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_PARTIAL); + cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_FULL | + IWL_SCAN_OFFLOAD_CHANNEL_PARTIAL); - if (s_band->channels[i].flags & IEEE80211_CHAN_NO_HT40) + if (chan->flags & IEEE80211_CHAN_NO_HT40) channels->type[index] |= cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_NARROW); @@ -740,7 +714,6 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, int band_2ghz = mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels; int band_5ghz = mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels; int head = 0; - int tail = band_2ghz + band_5ghz - 1; u32 ssid_bitmap; int cmd_len; int ret; @@ -772,7 +745,7 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, &scan_cfg->scan_cmd.tx_cmd[0], scan_cfg->data); iwl_build_channel_cfg(mvm, req, &scan_cfg->channel_cfg, - IEEE80211_BAND_2GHZ, &head, &tail, + IEEE80211_BAND_2GHZ, &head, ssid_bitmap, ¶ms); } if (band_5ghz) { @@ -782,7 +755,7 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, scan_cfg->data + SCAN_OFFLOAD_PROBE_REQ_SIZE); iwl_build_channel_cfg(mvm, req, &scan_cfg->channel_cfg, - IEEE80211_BAND_5GHZ, &head, &tail, + IEEE80211_BAND_5GHZ, &head, ssid_bitmap, ¶ms); } From 14ca0f389c11f9e845a3e10a834c6412af279ccd Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 24 Jun 2014 14:12:43 +0300 Subject: [PATCH 136/274] iwlwifi: bump API version for 8000 devices Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-8000.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-8000.c b/drivers/net/wireless/iwlwifi/iwl-8000.c index 51c41531d81d..0a1bb5f564ba 100644 --- a/drivers/net/wireless/iwlwifi/iwl-8000.c +++ b/drivers/net/wireless/iwlwifi/iwl-8000.c @@ -67,7 +67,7 @@ #include "iwl-agn-hw.h" /* Highest firmware API version supported */ -#define IWL8000_UCODE_API_MAX 8 +#define IWL8000_UCODE_API_MAX 9 /* Oldest version we won't warn about */ #define IWL8000_UCODE_API_OK 8 From 0564679bb6e750dc6d441f6d67e95c0590fa3f80 Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Mon, 2 Jun 2014 09:59:49 +0300 Subject: [PATCH 137/274] iwlwifi: mvm: use ksize to memset scan_command Use ksize to get scan command size instead of calculating it, to avoid nasty bugs. Signed-off-by: David Spinadel Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/scan.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 4b6c7d4bd199..df8f3ebea84a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -325,9 +325,7 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm, IWL_DEBUG_SCAN(mvm, "Handling mac80211 scan request\n"); mvm->scan_status = IWL_MVM_SCAN_OS; - memset(cmd, 0, sizeof(struct iwl_scan_cmd) + - mvm->fw->ucode_capa.max_probe_length + - (MAX_NUM_SCAN_CHANNELS * sizeof(struct iwl_scan_channel))); + memset(cmd, 0, ksize(cmd)); cmd->channel_count = (u8)req->n_channels; cmd->quiet_time = cpu_to_le16(IWL_ACTIVE_QUIET_TIME); From ab4800303299e9a3b844e8256bf3c8fd9f095060 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 4 Jun 2014 10:13:50 +0200 Subject: [PATCH 138/274] iwlwifi: mvm: add back support for low-priority scan The low-priority scan feature can be useful, e.g. for OBSS scans (if those are required by the AP); add back support for it, restoring the maximum out time to the value it was for low-priority scan before that was removed. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 1 + drivers/net/wireless/iwlwifi/mvm/scan.c | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 7215f5980186..41c0aace3e84 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -374,6 +374,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->wiphy->max_sched_scan_ie_len = SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2; hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN | + NL80211_FEATURE_LOW_PRIORITY_SCAN | NL80211_FEATURE_P2P_GO_OPPPS; mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index df8f3ebea84a..896c21d4fc7c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -267,7 +267,7 @@ static void iwl_mvm_scan_condition_iterator(void *data, u8 *mac, static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - int n_ssids, + int n_ssids, u32 flags, struct iwl_mvm_scan_params *params) { bool global_bound = false; @@ -289,6 +289,9 @@ static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm, params->max_out_time = 250; } + if (flags & NL80211_SCAN_FLAG_LOW_PRIORITY) + params->max_out_time = 200; + not_bound: for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) { @@ -332,7 +335,7 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm, cmd->quiet_plcp_th = cpu_to_le16(IWL_PLCP_QUIET_THRESH); cmd->rxchain_sel_flags = iwl_mvm_scan_rx_chain(mvm); - iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, ¶ms); + iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, req->flags, ¶ms); cmd->max_out_time = cpu_to_le32(params.max_out_time); cmd->suspend_time = cpu_to_le32(params.suspend_time); if (params.passive_fragmented) @@ -758,7 +761,7 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, if (!scan_cfg) return -ENOMEM; - iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, ¶ms); + iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, 0, ¶ms); iwl_build_scan_cmd(mvm, vif, req, &scan_cfg->scan_cmd, ¶ms); scan_cfg->scan_cmd.len = cpu_to_le16(cmd_len); From b3c063ae7279981f7161e63b44f214c62f122b32 Mon Sep 17 00:00:00 2001 From: Oren Givon Date: Sun, 25 May 2014 16:31:58 +0300 Subject: [PATCH 139/274] iwlwifi: update the 7265 series HW IDs Add one more 7265 series HW ID. Edit one existing 7265 series HW ID. CC: [3.13+] Signed-off-by: Oren Givon Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/pcie/drv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c index 7091a18d5a72..98950e45c7b0 100644 --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -367,6 +367,7 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { {IWL_PCI_DEVICE(0x095A, 0x5012, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x5412, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x5410, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5510, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x5400, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x1010, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x5000, iwl7265_2n_cfg)}, @@ -380,7 +381,7 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { {IWL_PCI_DEVICE(0x095A, 0x9110, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x9112, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x9210, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x9200, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x9200, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x9510, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x9310, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x9410, iwl7265_2ac_cfg)}, From 762533ba9c0306b97e8587fdcafc500a3d7115ae Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Thu, 5 Jun 2014 11:20:43 +0300 Subject: [PATCH 140/274] iwlwifi: mvm: don't use hardcoded num of scan channels Use num of scan channels as advertised by fw TLV. Signed-off-by: David Spinadel Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-drv.c | 9 ++++ drivers/net/wireless/iwlwifi/iwl-fw-file.h | 1 + drivers/net/wireless/iwlwifi/iwl-fw.h | 1 + .../net/wireless/iwlwifi/mvm/fw-api-scan.h | 47 ++++++------------- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 3 +- drivers/net/wireless/iwlwifi/mvm/ops.c | 3 +- drivers/net/wireless/iwlwifi/mvm/scan.c | 43 ++++++++++------- 7 files changed, 56 insertions(+), 51 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index f2a5c12269a3..bf584d3fe8ec 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -155,6 +155,8 @@ static struct iwlwifi_opmode_table { [MVM_OP_MODE] = { .name = "iwlmvm", .ops = NULL }, }; +#define IWL_DEFAULT_SCAN_CHANNELS 40 + /* * struct fw_sec: Just for the image parsing proccess. * For the fw storage we are using struct fw_desc. @@ -819,6 +821,12 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, if (iwl_store_cscheme(&drv->fw, tlv_data, tlv_len)) goto invalid_tlv_len; break; + case IWL_UCODE_TLV_N_SCAN_CHANNELS: + if (tlv_len != sizeof(u32)) + goto invalid_tlv_len; + capa->n_scan_channels = + le32_to_cpup((__le32 *)tlv_data); + break; default: IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type); break; @@ -973,6 +981,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) fw->ucode_capa.max_probe_length = IWL_DEFAULT_MAX_PROBE_LENGTH; fw->ucode_capa.standard_phy_calibration_size = IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE; + fw->ucode_capa.n_scan_channels = IWL_DEFAULT_SCAN_CHANNELS; if (!api_ok) api_ok = api_max; diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h index b45e576a4b57..9e6ba1e7fd21 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h @@ -128,6 +128,7 @@ enum iwl_ucode_tlv_type { IWL_UCODE_TLV_CSCHEME = 28, IWL_UCODE_TLV_API_CHANGES_SET = 29, IWL_UCODE_TLV_ENABLED_CAPABILITIES = 30, + IWL_UCODE_TLV_N_SCAN_CHANNELS = 31, }; struct iwl_ucode_tlv { diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index 0aa7c0085c9f..de975cd4d73a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -178,6 +178,7 @@ enum iwl_ucode_sec { struct iwl_ucode_capabilities { u32 max_probe_length; + u32 n_scan_channels; u32 standard_phy_calibration_size; u32 flags; u32 api[IWL_API_ARRAY_SIZE]; diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h index 6959fda3fe09..1d586923d5b7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h @@ -169,19 +169,13 @@ enum iwl_scan_type { SCAN_TYPE_DISCOVERY_FORCED = 6, }; /* SCAN_ACTIVITY_TYPE_E_VER_1 */ -/** - * Maximal number of channels to scan - * it should be equal to: - * max(IWL_NUM_CHANNELS, IWL_NUM_CHANNELS_FAMILY_8000) - */ -#define MAX_NUM_SCAN_CHANNELS 50 - /** * struct iwl_scan_cmd - scan request command * ( SCAN_REQUEST_CMD = 0x80 ) * @len: command length in bytes * @scan_flags: scan flags from SCAN_FLAGS_* - * @channel_count: num of channels in channel list (1 - MAX_NUM_SCAN_CHANNELS) + * @channel_count: num of channels in channel list + * (1 - ucode_capa.n_scan_channels) * @quiet_time: in msecs, dwell this time for active scan on quiet channels * @quiet_plcp_th: quiet PLCP threshold (channel is quiet if less than * this number of packets were received (typically 1) @@ -345,7 +339,7 @@ struct iwl_scan_results_notif { * @last_channel: last channel that was scanned * @tsf_low: TSF timer (lower half) in usecs * @tsf_high: TSF timer (higher half) in usecs - * @results: all scan results, only "scanned_channels" of them are valid + * @results: array of scan results, only "scanned_channels" of them are valid */ struct iwl_scan_complete_notif { u8 scanned_channels; @@ -354,11 +348,10 @@ struct iwl_scan_complete_notif { u8 last_channel; __le32 tsf_low; __le32 tsf_high; - struct iwl_scan_results_notif results[MAX_NUM_SCAN_CHANNELS]; + struct iwl_scan_results_notif results[]; } __packed; /* SCAN_COMPLETE_NTF_API_S_VER_2 */ /* scan offload */ -#define IWL_MAX_SCAN_CHANNELS 40 #define IWL_SCAN_MAX_BLACKLIST_LEN 64 #define IWL_SCAN_SHORT_BLACKLIST_LEN 16 #define IWL_SCAN_MAX_PROFILES 11 @@ -423,36 +416,24 @@ enum iwl_scan_offload_channel_flags { IWL_SCAN_OFFLOAD_CHANNEL_PARTIAL = BIT(25), }; -/** - * iwl_scan_channel_cfg - SCAN_CHANNEL_CFG_S - * @type: bitmap - see enum iwl_scan_offload_channel_flags. - * 0: passive (0) or active (1) scan. - * 1-20: directed scan to i'th ssid. - * 22: channel width configuation - 1 for narrow. - * 24: full scan. - * 25: partial scan. - * @channel_number: channel number 1-13 etc. - * @iter_count: repetition count for the channel. - * @iter_interval: interval between two innteration on one channel. - * @dwell_time: entry 0 - active scan, entry 1 - passive scan. +/* channel configuration for struct iwl_scan_offload_cfg. Each channels needs: + * __le32 type: bitmap; bits 1-20 are for directed scan to i'th ssid and + * see enum iwl_scan_offload_channel_flags. + * __le16 channel_number: channel number 1-13 etc. + * __le16 iter_count: repetition count for the channel. + * __le32 iter_interval: interval between two innteration on one channel. + * u8 active_dwell. + * u8 passive_dwell. */ -struct iwl_scan_channel_cfg { - __le32 type[IWL_MAX_SCAN_CHANNELS]; - __le16 channel_number[IWL_MAX_SCAN_CHANNELS]; - __le16 iter_count[IWL_MAX_SCAN_CHANNELS]; - __le32 iter_interval[IWL_MAX_SCAN_CHANNELS]; - u8 dwell_time[IWL_MAX_SCAN_CHANNELS][2]; -} __packed; +#define IWL_SCAN_CHAN_SIZE 14 /** * iwl_scan_offload_cfg - SCAN_OFFLOAD_CONFIG_API_S * @scan_cmd: scan command fixed part - * @channel_cfg: scan channel configuration - * @data: probe request frames (one per band) + * @data: scan channel configuration and probe request frames */ struct iwl_scan_offload_cfg { struct iwl_scan_offload_cmd scan_cmd; - struct iwl_scan_channel_cfg channel_cfg; u8 data[0]; } __packed; diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 41c0aace3e84..f22be88c0387 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -1543,7 +1543,8 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); int ret; - if (req->n_channels == 0 || req->n_channels > MAX_NUM_SCAN_CHANNELS) + if (req->n_channels == 0 || + req->n_channels > mvm->fw->ucode_capa.n_scan_channels) return -EINVAL; mutex_lock(&mvm->mutex); diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index cc2f7de396de..a46f0289a3c1 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -504,7 +504,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, scan_size = sizeof(struct iwl_scan_cmd) + mvm->fw->ucode_capa.max_probe_length + - (MAX_NUM_SCAN_CHANNELS * sizeof(struct iwl_scan_channel)); + (mvm->fw->ucode_capa.n_scan_channels * + sizeof(struct iwl_scan_channel)); mvm->scan_cmd = kmalloc(scan_size, GFP_KERNEL); if (!mvm->scan_cmd) goto out_free; diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 40c112f136dc..349d8e67118b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -666,12 +666,19 @@ static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req, static void iwl_build_channel_cfg(struct iwl_mvm *mvm, struct cfg80211_sched_scan_request *req, - struct iwl_scan_channel_cfg *channels, + u8 *channels_buffer, enum ieee80211_band band, int *head, u32 ssid_bitmap, struct iwl_mvm_scan_params *params) { + u32 n_channels = mvm->fw->ucode_capa.n_scan_channels; + __le32 *type = (__le32 *)channels_buffer; + __le16 *channel_number = (__le16 *)(type + n_channels); + __le16 *iter_count = channel_number + n_channels; + __le32 *iter_interval = (__le32 *)(iter_count + n_channels); + u8 *active_dwell = (u8 *)(iter_interval + n_channels); + u8 *passive_dwell = active_dwell + n_channels; int i, index = 0; for (i = 0; i < req->n_channels; i++) { @@ -683,27 +690,26 @@ static void iwl_build_channel_cfg(struct iwl_mvm *mvm, index = *head; (*head)++; - channels->channel_number[index] = cpu_to_le16(chan->hw_value); - channels->dwell_time[index][0] = params->dwell[band].active; - channels->dwell_time[index][1] = params->dwell[band].passive; + channel_number[index] = cpu_to_le16(chan->hw_value); + active_dwell[index] = params->dwell[band].active; + passive_dwell[index] = params->dwell[band].passive; - channels->iter_count[index] = cpu_to_le16(1); - channels->iter_interval[index] = 0; + iter_count[index] = cpu_to_le16(1); + iter_interval[index] = 0; if (!(chan->flags & IEEE80211_CHAN_NO_IR)) - channels->type[index] |= + type[index] |= cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_ACTIVE); - channels->type[index] |= - cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_FULL | - IWL_SCAN_OFFLOAD_CHANNEL_PARTIAL); + type[index] |= cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_FULL | + IWL_SCAN_OFFLOAD_CHANNEL_PARTIAL); if (chan->flags & IEEE80211_CHAN_NO_HT40) - channels->type[index] |= + type[index] |= cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_NARROW); /* scan for all SSIDs from req->ssids */ - channels->type[index] |= cpu_to_le32(ssid_bitmap); + type[index] |= cpu_to_le32(ssid_bitmap); } } @@ -718,6 +724,7 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, u32 ssid_bitmap; int cmd_len; int ret; + u8 *probes; struct iwl_scan_offload_cfg *scan_cfg; struct iwl_host_cmd cmd = { @@ -728,12 +735,16 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); cmd_len = sizeof(struct iwl_scan_offload_cfg) + + mvm->fw->ucode_capa.n_scan_channels * IWL_SCAN_CHAN_SIZE + 2 * SCAN_OFFLOAD_PROBE_REQ_SIZE; scan_cfg = kzalloc(cmd_len, GFP_KERNEL); if (!scan_cfg) return -ENOMEM; + probes = scan_cfg->data + + mvm->fw->ucode_capa.n_scan_channels * IWL_SCAN_CHAN_SIZE; + iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, 0, ¶ms); iwl_build_scan_cmd(mvm, vif, req, &scan_cfg->scan_cmd, ¶ms); scan_cfg->scan_cmd.len = cpu_to_le16(cmd_len); @@ -744,8 +755,8 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, iwl_scan_offload_build_tx_cmd(mvm, vif, ies, IEEE80211_BAND_2GHZ, &scan_cfg->scan_cmd.tx_cmd[0], - scan_cfg->data); - iwl_build_channel_cfg(mvm, req, &scan_cfg->channel_cfg, + probes); + iwl_build_channel_cfg(mvm, req, scan_cfg->data, IEEE80211_BAND_2GHZ, &head, ssid_bitmap, ¶ms); } @@ -753,9 +764,9 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, iwl_scan_offload_build_tx_cmd(mvm, vif, ies, IEEE80211_BAND_5GHZ, &scan_cfg->scan_cmd.tx_cmd[1], - scan_cfg->data + + probes + SCAN_OFFLOAD_PROBE_REQ_SIZE); - iwl_build_channel_cfg(mvm, req, &scan_cfg->channel_cfg, + iwl_build_channel_cfg(mvm, req, scan_cfg->data, IEEE80211_BAND_5GHZ, &head, ssid_bitmap, ¶ms); } From 5788ab75eccd5783889ad848282a160e5dd365cb Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 15 Jun 2014 10:40:48 +0300 Subject: [PATCH 141/274] iwlwifi: remove MCS32 support declaration MCS32 is currently not supported, so don't declare support for it. Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c index c44cf1149648..07ff7e0028ee 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c @@ -779,7 +779,6 @@ void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg, if (cfg->ht_params->ht40_bands & BIT(band)) { ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; ht_info->cap |= IEEE80211_HT_CAP_SGI_40; - ht_info->mcs.rx_mask[4] = 0x01; max_bit_rate = MAX_BIT_RATE_40_MHZ; } From 06ddbf5adac1fd2a031eade8a92239abfa6db93a Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 2 Jun 2014 08:34:53 +0300 Subject: [PATCH 142/274] iwlwifi: add device / firmware to fw-error-dump file This can be useful later for parsing since the parsing may differ based on the device's family / bus. Also add the human readable version of the firmware. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-drv.c | 2 ++ .../net/wireless/iwlwifi/iwl-fw-error-dump.h | 24 +++++++++++++++++++ drivers/net/wireless/iwlwifi/iwl-fw-file.h | 5 ++-- drivers/net/wireless/iwlwifi/iwl-fw.h | 3 +++ drivers/net/wireless/iwlwifi/mvm/ops.c | 24 ++++++++++++++++--- 5 files changed, 53 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index bf584d3fe8ec..01a426871ffb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -567,6 +567,8 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, } drv->fw.ucode_ver = le32_to_cpu(ucode->ver); + memcpy(drv->fw.human_readable, ucode->human_readable, + sizeof(drv->fw.human_readable)); build = le32_to_cpu(ucode->build); if (build) diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h index 2953ffceda38..aa0f85ee65b5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h @@ -74,12 +74,15 @@ * @IWL_FW_ERROR_DUMP_RXF: * @IWL_FW_ERROR_DUMP_TXCMD: last TX command data, structured as * &struct iwl_fw_error_dump_txcmd packets + * @IWL_FW_ERROR_DUMP_DEV_FW_INFO: struct %iwl_fw_error_dump_info + * info on the device / firmware. */ enum iwl_fw_error_dump_type { IWL_FW_ERROR_DUMP_SRAM = 0, IWL_FW_ERROR_DUMP_REG = 1, IWL_FW_ERROR_DUMP_RXF = 2, IWL_FW_ERROR_DUMP_TXCMD = 3, + IWL_FW_ERROR_DUMP_DEV_FW_INFO = 4, IWL_FW_ERROR_DUMP_MAX, }; @@ -120,6 +123,27 @@ struct iwl_fw_error_dump_txcmd { u8 data[]; } __packed; +enum iwl_fw_error_dump_family { + IWL_FW_ERROR_DUMP_FAMILY_7 = 7, + IWL_FW_ERROR_DUMP_FAMILY_8 = 8, +}; + +/** + * struct iwl_fw_error_dump_info - info on the device / firmware + * @device_family: the family of the device (7 / 8) + * @hw_step: the step of the device + * @fw_human_readable: human readable FW version + * @dev_human_readable: name of the device + * @bus_human_readable: name of the bus used + */ +struct iwl_fw_error_dump_info { + __le32 device_family; + __le32 hw_step; + u8 fw_human_readable[FW_VER_HUMAN_READABLE_SZ]; + u8 dev_human_readable[64]; + u8 bus_human_readable[8]; +} __packed; + /** * iwl_mvm_fw_error_next_data - advance fw error dump data pointer * @data: previous data block diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h index 9e6ba1e7fd21..929a8063354c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h @@ -137,7 +137,8 @@ struct iwl_ucode_tlv { u8 data[0]; }; -#define IWL_TLV_UCODE_MAGIC 0x0a4c5749 +#define IWL_TLV_UCODE_MAGIC 0x0a4c5749 +#define FW_VER_HUMAN_READABLE_SZ 64 struct iwl_tlv_ucode_header { /* @@ -148,7 +149,7 @@ struct iwl_tlv_ucode_header { */ __le32 zero; __le32 magic; - u8 human_readable[64]; + u8 human_readable[FW_VER_HUMAN_READABLE_SZ]; __le32 ver; /* major/minor/API/serial */ __le32 build; __le64 ignore; diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index de975cd4d73a..287b949c95f6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -65,6 +65,8 @@ #include #include +#include "iwl-fw-file.h" + /** * enum iwl_ucode_tlv_flag - ucode API flags * @IWL_UCODE_TLV_FLAGS_PAN: This is PAN capable microcode; this previously @@ -312,6 +314,7 @@ struct iwl_fw { bool mvm_fw; struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS]; + u8 human_readable[FW_VER_HUMAN_READABLE_SZ]; }; #endif /* __iwl_fw_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index a46f0289a3c1..3ab33e296686 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -827,6 +827,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) { struct iwl_fw_error_dump_file *dump_file; struct iwl_fw_error_dump_data *dump_data; + struct iwl_fw_error_dump_info *dump_info; u32 file_len; u32 trans_len; @@ -835,10 +836,11 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) if (mvm->fw_error_dump) return; - file_len = mvm->fw_error_sram_len + + file_len = sizeof(*dump_file) + + sizeof(*dump_data) * 3 + + mvm->fw_error_sram_len + mvm->fw_error_rxf_len + - sizeof(*dump_file) + - sizeof(*dump_data) * 2; + sizeof(*dump_info); trans_len = iwl_trans_dump_data(mvm->trans, NULL, 0); if (trans_len) @@ -853,6 +855,22 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER); dump_file->file_len = cpu_to_le32(file_len); dump_data = (void *)dump_file->data; + + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO); + dump_data->len = cpu_to_le32(sizeof(*dump_info)); + dump_info = (void *) dump_data->data; + dump_info->device_family = + mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ? + cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) : + cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8); + memcpy(dump_info->fw_human_readable, mvm->fw->human_readable, + sizeof(dump_info->fw_human_readable)); + strncpy(dump_info->dev_human_readable, mvm->cfg->name, + sizeof(dump_info->dev_human_readable)); + strncpy(dump_info->bus_human_readable, mvm->dev->bus->name, + sizeof(dump_info->bus_human_readable)); + + dump_data = iwl_mvm_fw_error_next_data(dump_data); dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); dump_data->len = cpu_to_le32(mvm->fw_error_rxf_len); memcpy(dump_data->data, mvm->fw_error_rxf, mvm->fw_error_rxf_len); From 1fa1605648d15d42f350807279b6c6e8d33b6382 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 5 Jun 2014 13:50:31 +0300 Subject: [PATCH 143/274] iwlwifi: rename iwl_mvm_fw_error_next_data This is not related to mvm. Rename to iwl_fw_error_next_data. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h | 4 ++-- drivers/net/wireless/iwlwifi/mvm/ops.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h index aa0f85ee65b5..3584a75833fe 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h @@ -145,12 +145,12 @@ struct iwl_fw_error_dump_info { } __packed; /** - * iwl_mvm_fw_error_next_data - advance fw error dump data pointer + * iwl_fw_error_next_data - advance fw error dump data pointer * @data: previous data block * Returns: next data block */ static inline struct iwl_fw_error_dump_data * -iwl_mvm_fw_error_next_data(struct iwl_fw_error_dump_data *data) +iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data) { return (void *)(data->data + le32_to_cpu(data->len)); } diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 3ab33e296686..7bb763f3052b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -870,12 +870,12 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) strncpy(dump_info->bus_human_readable, mvm->dev->bus->name, sizeof(dump_info->bus_human_readable)); - dump_data = iwl_mvm_fw_error_next_data(dump_data); + dump_data = iwl_fw_error_next_data(dump_data); dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); dump_data->len = cpu_to_le32(mvm->fw_error_rxf_len); memcpy(dump_data->data, mvm->fw_error_rxf, mvm->fw_error_rxf_len); - dump_data = iwl_mvm_fw_error_next_data(dump_data); + dump_data = iwl_fw_error_next_data(dump_data); dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM); dump_data->len = cpu_to_le32(mvm->fw_error_sram_len); @@ -895,7 +895,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) mvm->fw_error_sram_len = 0; if (trans_len) { - void *buf = iwl_mvm_fw_error_next_data(dump_data); + void *buf = iwl_fw_error_next_data(dump_data); u32 real_trans_len = iwl_trans_dump_data(mvm->trans, buf, trans_len); dump_data = (void *)((u8 *)buf + real_trans_len); From c2d202017da18ebd6567862bd9a50392970f048f Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 1 Jun 2014 08:05:52 +0300 Subject: [PATCH 144/274] iwlwifi: pcie: add firmware monitor capabilities This allows to use the firmware monitor. This capability uses a lot of contiguous memory (up to 64MB), so make its usage module parameter dependent. The driver will try to allocate as much contiguous memory as possible downgrading its requirements until the allocation succeeds. Dump this data into the fw-error dump file when an error happens. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-drv.c | 4 + .../net/wireless/iwlwifi/iwl-fw-error-dump.h | 18 +++ drivers/net/wireless/iwlwifi/iwl-modparams.h | 2 + drivers/net/wireless/iwlwifi/iwl-prph.h | 6 + drivers/net/wireless/iwlwifi/pcie/internal.h | 7 + drivers/net/wireless/iwlwifi/pcie/trans.c | 125 +++++++++++++++++- 6 files changed, 158 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index 01a426871ffb..bb842f4732dd 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -1405,3 +1405,7 @@ module_param_named(power_level, iwlwifi_mod_params.power_level, int, S_IRUGO); MODULE_PARM_DESC(power_level, "default power save level (range from 1 - 5, default: 1)"); + +module_param_named(fw_monitor, iwlwifi_mod_params.fw_monitor, bool, S_IRUGO); +MODULE_PARM_DESC(dbgm, + "firmware monitor - to debug FW (default: false - needs lots of memory)"); diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h index 3584a75833fe..ced5ba95c23d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h @@ -76,6 +76,7 @@ * &struct iwl_fw_error_dump_txcmd packets * @IWL_FW_ERROR_DUMP_DEV_FW_INFO: struct %iwl_fw_error_dump_info * info on the device / firmware. + * @IWL_FW_ERROR_DUMP_FW_MONITOR: firmware monitor */ enum iwl_fw_error_dump_type { IWL_FW_ERROR_DUMP_SRAM = 0, @@ -83,6 +84,7 @@ enum iwl_fw_error_dump_type { IWL_FW_ERROR_DUMP_RXF = 2, IWL_FW_ERROR_DUMP_TXCMD = 3, IWL_FW_ERROR_DUMP_DEV_FW_INFO = 4, + IWL_FW_ERROR_DUMP_FW_MONITOR = 5, IWL_FW_ERROR_DUMP_MAX, }; @@ -144,6 +146,22 @@ struct iwl_fw_error_dump_info { u8 bus_human_readable[8]; } __packed; +/** + * struct iwl_fw_error_fw_mon - FW monitor data + * @fw_mon_wr_ptr: the position of the write pointer in the cyclic buffer + * @fw_mon_base_ptr: base pointer of the data + * @fw_mon_cycle_cnt: number of wrap arounds + * @reserved: for future use + * @data: captured data + */ +struct iwl_fw_error_fw_mon { + __le32 fw_mon_wr_ptr; + __le32 fw_mon_base_ptr; + __le32 fw_mon_cycle_cnt; + __le32 reserved[3]; + u8 data[]; +} __packed; + /** * iwl_fw_error_next_data - advance fw error dump data pointer * @data: previous data block diff --git a/drivers/net/wireless/iwlwifi/iwl-modparams.h b/drivers/net/wireless/iwlwifi/iwl-modparams.h index d051857729ab..f2d39cb011fc 100644 --- a/drivers/net/wireless/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/iwlwifi/iwl-modparams.h @@ -103,6 +103,7 @@ enum iwl_disable_11n { * @power_level: power level, default = 1 * @debug_level: levels are IWL_DL_* * @ant_coupling: antenna coupling in dB, default = 0 + * @fw_monitor: allow to use firmware monitor */ struct iwl_mod_params { int sw_crypto; @@ -120,6 +121,7 @@ struct iwl_mod_params { int ant_coupling; char *nvm_file; bool uapsd_disable; + bool fw_monitor; }; #endif /* #__iwl_modparams_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index 4997e27672b3..47033a35a402 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -359,4 +359,10 @@ enum secure_load_status_reg { #define RXF_LD_FENCE_OFFSET_ADDR (0xa00c10) #define RXF_FIFO_RD_FENCE_ADDR (0xa00c0c) +/* FW monitor */ +#define MON_BUFF_BASE_ADDR (0xa03c3c) +#define MON_BUFF_END_ADDR (0xa03c40) +#define MON_BUFF_WRPTR (0xa03c44) +#define MON_BUFF_CYCLE_CNT (0xa03c48) + #endif /* __iwl_prph_h__ */ diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index 6c22b23a2845..78f72c34438a 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -260,6 +260,9 @@ iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx) * @wd_timeout: queue watchdog timeout (jiffies) * @reg_lock: protect hw register access * @cmd_in_flight: true when we have a host command in flight + * @fw_mon_phys: physical address of the buffer for the firmware monitor + * @fw_mon_page: points to the first page of the buffer for the firmware monitor + * @fw_mon_size: size of the buffer for the firmware monitor */ struct iwl_trans_pcie { struct iwl_rxq rxq; @@ -312,6 +315,10 @@ struct iwl_trans_pcie { /*protect hw register */ spinlock_t reg_lock; bool cmd_in_flight; + + dma_addr_t fw_mon_phys; + struct page *fw_mon_page; + u32 fw_mon_size; }; #define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \ diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 788085bc65d7..3ffac4888c12 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -76,6 +76,68 @@ #include "iwl-fw-error-dump.h" #include "internal.h" +static void iwl_pcie_free_fw_monitor(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + if (!trans_pcie->fw_mon_page) + return; + + dma_unmap_page(trans->dev, trans_pcie->fw_mon_phys, + trans_pcie->fw_mon_size, DMA_FROM_DEVICE); + __free_pages(trans_pcie->fw_mon_page, + get_order(trans_pcie->fw_mon_size)); + trans_pcie->fw_mon_page = NULL; + trans_pcie->fw_mon_phys = 0; + trans_pcie->fw_mon_size = 0; +} + +static void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct page *page; + dma_addr_t phys; + u32 size; + u8 power; + + if (trans_pcie->fw_mon_page) { + dma_sync_single_for_device(trans->dev, trans_pcie->fw_mon_phys, + trans_pcie->fw_mon_size, + DMA_FROM_DEVICE); + return; + } + + phys = 0; + for (power = 26; power >= 11; power--) { + int order; + + size = BIT(power); + order = get_order(size); + page = alloc_pages(__GFP_COMP | __GFP_NOWARN | __GFP_ZERO, + order); + if (!page) + continue; + + phys = dma_map_page(trans->dev, page, 0, PAGE_SIZE << order, + DMA_FROM_DEVICE); + if (dma_mapping_error(trans->dev, phys)) { + __free_pages(page, order); + continue; + } + IWL_INFO(trans, + "Allocated 0x%08x bytes (order %d) for firmware monitor.\n", + size, order); + break; + } + + if (!page) + return; + + trans_pcie->fw_mon_page = page; + trans_pcie->fw_mon_phys = phys; + trans_pcie->fw_mon_size = size; +} + static u32 iwl_trans_pcie_read_shr(struct iwl_trans *trans, u32 reg) { iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG, @@ -675,6 +737,7 @@ static int iwl_pcie_load_cpu_sections(struct iwl_trans *trans, static int iwl_pcie_load_given_ucode(struct iwl_trans *trans, const struct fw_img *image) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret = 0; int first_ucode_section; @@ -733,6 +796,20 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans, return ret; } + /* supported for 7000 only for the moment */ + if (iwlwifi_mod_params.fw_monitor && + trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) { + iwl_pcie_alloc_fw_monitor(trans); + + if (trans_pcie->fw_mon_size) { + iwl_write_prph(trans, MON_BUFF_BASE_ADDR, + trans_pcie->fw_mon_phys >> 4); + iwl_write_prph(trans, MON_BUFF_END_ADDR, + (trans_pcie->fw_mon_phys + + trans_pcie->fw_mon_size) >> 4); + } + } + /* release CPU reset */ if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT); @@ -1126,6 +1203,8 @@ void iwl_trans_pcie_free(struct iwl_trans *trans) if (trans_pcie->napi.poll) netif_napi_del(&trans_pcie->napi); + iwl_pcie_free_fw_monitor(trans); + kfree(trans); } @@ -1698,10 +1777,15 @@ static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans, u32 len; int i, ptr; + len = sizeof(*data) + + cmdq->q.n_window * (sizeof(*txcmd) + TFD_MAX_PAYLOAD_SIZE); + + if (trans_pcie->fw_mon_page) + len += sizeof(*data) + sizeof(struct iwl_fw_error_fw_mon) + + trans_pcie->fw_mon_size; + if (!buf) - return sizeof(*data) + - cmdq->q.n_window * (sizeof(*txcmd) + - TFD_MAX_PAYLOAD_SIZE); + return len; len = 0; data = buf; @@ -1729,7 +1813,40 @@ static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans, spin_unlock_bh(&cmdq->lock); data->len = cpu_to_le32(len); - return sizeof(*data) + len; + len += sizeof(*data); + + if (trans_pcie->fw_mon_page) { + struct iwl_fw_error_fw_mon *fw_mon_data; + + data = iwl_fw_error_next_data(data); + data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FW_MONITOR); + data->len = cpu_to_le32(trans_pcie->fw_mon_size + + sizeof(*fw_mon_data)); + fw_mon_data = (void *)data->data; + fw_mon_data->fw_mon_wr_ptr = + cpu_to_le32(iwl_read_prph(trans, MON_BUFF_WRPTR)); + fw_mon_data->fw_mon_cycle_cnt = + cpu_to_le32(iwl_read_prph(trans, MON_BUFF_CYCLE_CNT)); + fw_mon_data->fw_mon_base_ptr = + cpu_to_le32(iwl_read_prph(trans, MON_BUFF_BASE_ADDR)); + + /* + * The firmware is now asserted, it won't write anything to + * the buffer. CPU can take ownership to fetch the data. + * The buffer will be handed back to the device before the + * firmware will be restarted. + */ + dma_sync_single_for_cpu(trans->dev, trans_pcie->fw_mon_phys, + trans_pcie->fw_mon_size, + DMA_FROM_DEVICE); + memcpy(fw_mon_data->data, page_address(trans_pcie->fw_mon_page), + trans_pcie->fw_mon_size); + + len += sizeof(*data) + sizeof(*fw_mon_data) + + trans_pcie->fw_mon_size; + } + + return len; } #else static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, From 52ab4c86a7ebbdce4e1f227acfc7b39352ad2967 Mon Sep 17 00:00:00 2001 From: Eran Harary Date: Sun, 1 Jun 2014 14:55:21 +0300 Subject: [PATCH 145/274] iwlwifi: mvm: fixes for 8000 NVM flow The nvm_file should be loaded by default for SDIO procucts only. Change the configuration accordingly. While at it, fix a typo in the device name. Signed-off-by: Eran Harary Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-8000.c | 3 +-- drivers/net/wireless/iwlwifi/iwl-config.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-8000.c b/drivers/net/wireless/iwlwifi/iwl-8000.c index 0a1bb5f564ba..51486cc9d943 100644 --- a/drivers/net/wireless/iwlwifi/iwl-8000.c +++ b/drivers/net/wireless/iwlwifi/iwl-8000.c @@ -119,10 +119,9 @@ const struct iwl_cfg iwl8260_2ac_cfg = { .ht_params = &iwl8000_ht_params, .nvm_ver = IWL8000_NVM_VERSION, .nvm_calib_ver = IWL8000_TX_POWER_VERSION, - .default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000, }; -const struct iwl_cfg iwl8260_n_cfg = { +const struct iwl_cfg iwl8260_2ac_sdio_cfg = { .name = "Intel(R) Dual Band Wireless-AC 8260", .fw_name_pre = IWL8000_FW_PRE, IWL_DEVICE_8000, diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index b7047905f41a..034c2fc4b69f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -337,7 +337,7 @@ extern const struct iwl_cfg iwl7265_2ac_cfg; extern const struct iwl_cfg iwl7265_2n_cfg; extern const struct iwl_cfg iwl7265_n_cfg; extern const struct iwl_cfg iwl8260_2ac_cfg; -extern const struct iwl_cfg iwl8260_n_cfg; +extern const struct iwl_cfg iwl8260_2ac_sdio_cfg; #endif /* CONFIG_IWLMVM */ #endif /* __IWL_CONFIG_H__ */ From 9ee6dace73029fc88767f33b13d52b60d5861f21 Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Wed, 29 May 2013 11:37:28 +0300 Subject: [PATCH 146/274] iwlwifi: fix NVM channel attribute map. Fix NVM channel attributes. Add indoor-only and GO Concurrent bits. Remove DFS channel bit which is overlapped with radar. Signed-off-by: David Spinadel Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-nvm-parse.c | 22 ++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index 85eee79c495c..0e3322a136c5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -174,7 +174,9 @@ static struct ieee80211_rate iwl_cfg80211_rates[] = { * @NVM_CHANNEL_IBSS: usable as an IBSS channel * @NVM_CHANNEL_ACTIVE: active scanning allowed * @NVM_CHANNEL_RADAR: radar detection required - * @NVM_CHANNEL_DFS: dynamic freq selection candidate + * @NVM_CHANNEL_INDOOR_ONLY: only indoor use is allowed + * @NVM_CHANNEL_GO_CONCURRENT: GO operation is allowed when connected to BSS + * on same channel on 2.4 or same UNII band on 5.2 * @NVM_CHANNEL_WIDE: 20 MHz channel okay (?) * @NVM_CHANNEL_40MHZ: 40 MHz channel okay (?) * @NVM_CHANNEL_80MHZ: 80 MHz channel okay (?) @@ -185,7 +187,8 @@ enum iwl_nvm_channel_flags { NVM_CHANNEL_IBSS = BIT(1), NVM_CHANNEL_ACTIVE = BIT(3), NVM_CHANNEL_RADAR = BIT(4), - NVM_CHANNEL_DFS = BIT(7), + NVM_CHANNEL_INDOOR_ONLY = BIT(5), + NVM_CHANNEL_GO_CONCURRENT = BIT(6), NVM_CHANNEL_WIDE = BIT(8), NVM_CHANNEL_40MHZ = BIT(9), NVM_CHANNEL_80MHZ = BIT(10), @@ -273,6 +276,16 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, if (ch_flags & NVM_CHANNEL_RADAR) channel->flags |= IEEE80211_CHAN_RADAR; + if (ch_flags & NVM_CHANNEL_INDOOR_ONLY) + channel->flags |= IEEE80211_CHAN_INDOOR_ONLY; + + /* Set the GO concurrent flag only in case that NO_IR is set. + * Otherwise it is meaningless + */ + if ((ch_flags & NVM_CHANNEL_GO_CONCURRENT) && + (channel->flags & IEEE80211_CHAN_NO_IR)) + channel->flags |= IEEE80211_CHAN_GO_CONCURRENT; + /* Initialize regulatory-based run-time data */ /* @@ -282,7 +295,7 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, channel->max_power = DEFAULT_MAX_TX_POWER; is_5ghz = channel->band == IEEE80211_BAND_5GHZ; IWL_DEBUG_EEPROM(dev, - "Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n", + "Ch. %d [%sGHz] %s%s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n", channel->hw_value, is_5ghz ? "5.2" : "2.4", CHECK_AND_PRINT_I(VALID), @@ -290,7 +303,8 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, CHECK_AND_PRINT_I(ACTIVE), CHECK_AND_PRINT_I(RADAR), CHECK_AND_PRINT_I(WIDE), - CHECK_AND_PRINT_I(DFS), + CHECK_AND_PRINT_I(INDOOR_ONLY), + CHECK_AND_PRINT_I(GO_CONCURRENT), ch_flags, channel->max_power, ((ch_flags & NVM_CHANNEL_IBSS) && From d536c32b45d2f86c9e9e7780bd94d28d1d7c4660 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Sat, 7 Jun 2014 09:00:11 -0700 Subject: [PATCH 147/274] iwlwifi: pcie: log when waking the NIC for hcmd submission fails I've never seen this happen, but it's useful to rule it out. Signed-off-by: Andy Lutomirski Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/pcie/tx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 038940afbdc5..6acccb19c4f3 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -1438,6 +1438,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); trans_pcie->cmd_in_flight = false; + IWL_ERR(trans, "Failed to wake NIC for hcmd\n"); idx = -EIO; goto out; } From f40faf623712153a64f39d2bddad2fc5a4f5e12d Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Sat, 7 Jun 2014 09:13:44 -0700 Subject: [PATCH 148/274] iwlwifi: pcie: improve debugfs queue info This adds need_update and write_actual to rx_queue and need_update and an HCMD indicator to tx_queue. On my card, rx_queue now looks like: read: 181 write: 180 write_actual: 176 need_update: 0 free_count: 40 closed_rb_num: 181 tx_queue now looks like: hwq 00: read=29 write=30 use=1 stop=0 need_update=0 hwq 01: read=0 write=0 use=1 stop=0 need_update=0 hwq 02: read=128 write=128 use=1 stop=0 need_update=0 hwq 03: read=0 write=0 use=1 stop=0 need_update=0 hwq 04: read=94 write=94 use=1 stop=0 need_update=0 HCMD hwq 05: read=0 write=0 use=0 stop=0 need_update=0 hwq 06: read=0 write=0 use=0 stop=0 need_update=0 hwq 07: read=0 write=0 use=0 stop=0 need_update=0 hwq 08: read=0 write=0 use=0 stop=0 need_update=0 hwq 09: read=0 write=0 use=0 stop=0 need_update=0 hwq 10: read=0 write=0 use=0 stop=0 need_update=0 hwq 11: read=0 write=0 use=0 stop=0 need_update=0 hwq 12: read=0 write=0 use=0 stop=0 need_update=0 hwq 13: read=0 write=0 use=0 stop=0 need_update=0 hwq 14: read=0 write=0 use=0 stop=0 need_update=0 hwq 15: read=0 write=0 use=0 stop=0 need_update=0 hwq 16: read=0 write=0 use=0 stop=0 need_update=0 hwq 17: read=0 write=0 use=0 stop=0 need_update=0 hwq 18: read=0 write=0 use=0 stop=0 need_update=0 hwq 19: read=0 write=0 use=0 stop=0 need_update=0 This may help with debugging queue stalls. Signed-off-by: Andy Lutomirski Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/pcie/trans.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 3ffac4888c12..0480857ee6e2 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -1573,10 +1573,12 @@ static ssize_t iwl_dbgfs_tx_queue_read(struct file *file, txq = &trans_pcie->txq[cnt]; q = &txq->q; pos += scnprintf(buf + pos, bufsz - pos, - "hwq %.2d: read=%u write=%u use=%d stop=%d\n", + "hwq %.2d: read=%u write=%u use=%d stop=%d need_update=%d%s\n", cnt, q->read_ptr, q->write_ptr, !!test_bit(cnt, trans_pcie->queue_used), - !!test_bit(cnt, trans_pcie->queue_stopped)); + !!test_bit(cnt, trans_pcie->queue_stopped), + txq->need_update, + (cnt == trans_pcie->cmd_queue ? " HCMD" : "")); } ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); @@ -1598,6 +1600,10 @@ static ssize_t iwl_dbgfs_rx_queue_read(struct file *file, rxq->read); pos += scnprintf(buf + pos, bufsz - pos, "write: %u\n", rxq->write); + pos += scnprintf(buf + pos, bufsz - pos, "write_actual: %u\n", + rxq->write_actual); + pos += scnprintf(buf + pos, bufsz - pos, "need_update: %d\n", + rxq->need_update); pos += scnprintf(buf + pos, bufsz - pos, "free_count: %u\n", rxq->free_count); if (rxq->rb_stts) { From c9e38e770981ff25d203f0f5c1423a863c484446 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Sat, 7 Jun 2014 09:21:15 -0700 Subject: [PATCH 149/274] iwlwifi: dvm: add a force_cam module parameter to fully disable power saving iwldvm stalls are often blamed on power management. Add an option to force it all the way off. Signed-off-by: Andy Lutomirski Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/dvm/power.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/dvm/power.c b/drivers/net/wireless/iwlwifi/dvm/power.c index f2c1439566b5..760c45c34ef3 100644 --- a/drivers/net/wireless/iwlwifi/dvm/power.c +++ b/drivers/net/wireless/iwlwifi/dvm/power.c @@ -40,6 +40,10 @@ #include "commands.h" #include "power.h" +static bool force_cam; +module_param(force_cam, bool, 0644); +MODULE_PARM_DESC(force_cam, "force continuously aware mode (no power saving at all)"); + /* * Setting power level allows the card to go to sleep when not busy. * @@ -288,6 +292,11 @@ static void iwl_power_build_cmd(struct iwl_priv *priv, bool enabled = priv->hw->conf.flags & IEEE80211_CONF_PS; int dtimper; + if (force_cam) { + iwl_power_sleep_cam_cmd(priv, cmd); + return; + } + dtimper = priv->hw->conf.ps_dtim_period ?: 1; if (priv->wowlan) From 6a68a39fcd3af3f8ae3d185a8a2c8a82cf0363ef Mon Sep 17 00:00:00 2001 From: Eran Harary Date: Wed, 7 May 2014 11:09:11 +0300 Subject: [PATCH 150/274] iwlwifi: mvm: fix bug in set_hw_address function Don't use nvm_hw pointer if it is NULL. Print an error message if the MAC address isn't valid. Signed-off-by: Eran Harary Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-nvm-parse.c | 34 +++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index 0e3322a136c5..f0ae038f3339 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -476,7 +476,8 @@ static void iwl_set_hw_address(const struct iwl_cfg *cfg, data->hw_addr[5] = hw_addr[4]; } -static void iwl_set_hw_address_family_8000(const struct iwl_cfg *cfg, +static void iwl_set_hw_address_family_8000(struct device *dev, + const struct iwl_cfg *cfg, struct iwl_nvm_data *data, const __le16 *mac_override, const __le16 *nvm_hw) @@ -495,20 +496,28 @@ static void iwl_set_hw_address_family_8000(const struct iwl_cfg *cfg, data->hw_addr[4] = hw_addr[5]; data->hw_addr[5] = hw_addr[4]; - if (is_valid_ether_addr(hw_addr)) + if (is_valid_ether_addr(data->hw_addr)) return; + + IWL_ERR_DEV(dev, + "mac address from nvm override section is not valid\n"); } - /* take the MAC address from the OTP */ - hw_addr = (const u8 *)(nvm_hw + HW_ADDR0_FAMILY_8000); - data->hw_addr[0] = hw_addr[3]; - data->hw_addr[1] = hw_addr[2]; - data->hw_addr[2] = hw_addr[1]; - data->hw_addr[3] = hw_addr[0]; + if (nvm_hw) { + /* take the MAC address from the OTP */ + hw_addr = (const u8 *)(nvm_hw + HW_ADDR0_FAMILY_8000); + data->hw_addr[0] = hw_addr[3]; + data->hw_addr[1] = hw_addr[2]; + data->hw_addr[2] = hw_addr[1]; + data->hw_addr[3] = hw_addr[0]; - hw_addr = (const u8 *)(nvm_hw + HW_ADDR1_FAMILY_8000); - data->hw_addr[4] = hw_addr[1]; - data->hw_addr[5] = hw_addr[0]; + hw_addr = (const u8 *)(nvm_hw + HW_ADDR1_FAMILY_8000); + data->hw_addr[4] = hw_addr[1]; + data->hw_addr[5] = hw_addr[0]; + return; + } + + IWL_ERR_DEV(dev, "mac address is not found\n"); } struct iwl_nvm_data * @@ -570,7 +579,8 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, rx_chains); } else { /* MAC address in family 8000 */ - iwl_set_hw_address_family_8000(cfg, data, mac_override, nvm_hw); + iwl_set_hw_address_family_8000(dev, cfg, data, mac_override, + nvm_hw); iwl_init_sbands(dev, cfg, data, regulatory, sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains, From b513ee7fd6dd5ca6498eee99fa46c0d6c0f21c9d Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Sun, 1 Jun 2014 17:21:33 +0300 Subject: [PATCH 151/274] iwlwifi: update trans->hw_rev 8000 hw family format The format of the CSR_HW_REV register has changed in 8000 HW family. To keep backwards compatibility, we store the value of this register as usual in trans->hw_rev, only we store it in the old format in this variable. Signed-off-by: Liad Kaufman Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/pcie/trans.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 0480857ee6e2..5703a3d7799b 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -1993,6 +1993,16 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, } trans->hw_rev = iwl_read32(trans, CSR_HW_REV); + /* + * In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have + * changed, and now the revision step also includes bit 0-1 (no more + * "dash" value). To keep hw_rev backwards compatible - we'll store it + * in the old format. + */ + if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + trans->hw_rev = (trans->hw_rev & 0xfff0) | + ((trans->hw_rev << 2) & 0xc); + trans->hw_id = (pdev->device << 16) + pdev->subsystem_device; snprintf(trans->hw_id_str, sizeof(trans->hw_id_str), "PCI ID: 0x%04X:0x%04X", pdev->device, pdev->subsystem_device); From f251c07c88513c02d9f815e16bcbace0e95a5fe0 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Mon, 28 Apr 2014 14:42:04 +0300 Subject: [PATCH 152/274] iwlwifi: nvm: update maximal parsed values in external nvm Some of the maximum values of the parsed external NVM file in the B-step of the 8000 HW family were updated, so this updates these values. Signed-off-by: Liad Kaufman Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/nvm.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index 808f78f6fbf9..26ba27e3992d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -69,7 +69,9 @@ /* Default NVM size to read */ #define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024) -#define IWL_MAX_NVM_SECTION_SIZE 7000 +#define IWL_MAX_NVM_SECTION_SIZE 0x1b58 +#define IWL_MAX_NVM_8000A_SECTION_SIZE 0xffc +#define IWL_MAX_NVM_8000B_SECTION_SIZE 0x1ffc #define NVM_WRITE_OPCODE 1 #define NVM_READ_OPCODE 0 @@ -326,6 +328,7 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm) u8 data[]; } *file_sec; const u8 *eof, *temp; + int max_section_size; #define NVM_WORD1_LEN(x) (8 * (x & 0x03FF)) #define NVM_WORD2_ID(x) (x >> 12) @@ -334,6 +337,14 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm) IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from external NVM\n"); + /* Maximal size depends on HW family and step */ + if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) + max_section_size = IWL_MAX_NVM_SECTION_SIZE; + else if ((mvm->trans->hw_rev & 0xc) == 0) /* Family 8000 A-step */ + max_section_size = IWL_MAX_NVM_8000A_SECTION_SIZE; + else /* Family 8000 B-step */ + max_section_size = IWL_MAX_NVM_8000B_SECTION_SIZE; + /* * Obtain NVM image via request_firmware. Since we already used * request_firmware_nowait() for the firmware binary load and only @@ -392,7 +403,7 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm) le16_to_cpu(file_sec->word1)); } - if (section_size > IWL_MAX_NVM_SECTION_SIZE) { + if (section_size > max_section_size) { IWL_ERR(mvm, "ERROR - section too large (%d)\n", section_size); ret = -EINVAL; From 5daddc99024b952bb4e66452febd2175fff8d26e Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Wed, 21 May 2014 14:37:00 +0300 Subject: [PATCH 153/274] iwlwifi: mvm: assure no overflows occur while reading otp Just in case sizes change in the OTP without proper SW updating, an additional check is inserted when reading OTP sections to assure no overflows occur. Signed-off-by: Liad Kaufman Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/nvm.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index 26ba27e3992d..1f1a550828fa 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -221,7 +221,7 @@ static int iwl_nvm_write_section(struct iwl_mvm *mvm, u16 section, * without overflowing, so no check is needed. */ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section, - u8 *data) + u8 *data, u32 size_read) { u16 length, offset = 0; int ret; @@ -233,6 +233,13 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section, /* Read the NVM until exhausted (reading less than requested) */ while (ret == length) { + /* Check no memory assumptions fail and cause an overflow */ + if ((size_read + offset + length) > + mvm->cfg->base_params->eeprom_size) { + IWL_ERR(mvm, "EEPROM size is too small for NVM\n"); + return -ENOBUFS; + } + ret = iwl_nvm_read_chunk(mvm, section, offset, length, data); if (ret < 0) { IWL_DEBUG_EEPROM(mvm->trans->dev, @@ -470,6 +477,7 @@ int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm) int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic) { int ret, section; + u32 size_read = 0; u8 *nvm_buffer, *temp; if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS)) @@ -486,9 +494,11 @@ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic) return -ENOMEM; for (section = 0; section < NVM_MAX_NUM_SECTIONS; section++) { /* we override the constness for initial read */ - ret = iwl_nvm_read_section(mvm, section, nvm_buffer); + ret = iwl_nvm_read_section(mvm, section, nvm_buffer, + size_read); if (ret < 0) continue; + size_read += ret; temp = kmemdup(nvm_buffer, ret, GFP_KERNEL); if (!temp) { ret = -ENOMEM; From a39979a8c3e71eac72f43568b2535da60099b8e4 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 28 May 2014 12:06:41 +0300 Subject: [PATCH 154/274] iwlwifi: mvm: BT Coex - allow to force the antenna allocation This can be used for testing. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/coex.c | 39 +++++++++++++++++++ drivers/net/wireless/iwlwifi/mvm/debugfs.c | 39 +++++++++++++++++++ .../net/wireless/iwlwifi/mvm/fw-api-coex.h | 6 +++ drivers/net/wireless/iwlwifi/mvm/mvm.h | 10 +++++ 4 files changed, 94 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c index c8c3b38228f0..b2003d82260a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex.c @@ -106,6 +106,9 @@ static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = { static int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm) { + if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) + return 0; + return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, 0, sizeof(struct iwl_bt_coex_prio_tbl_cmd), &iwl_bt_prio_tbl); @@ -578,6 +581,29 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm) return -ENOMEM; cmd.data[0] = bt_cmd; + lockdep_assert_held(&mvm->mutex); + + if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) { + switch (mvm->bt_force_ant_mode) { + case BT_FORCE_ANT_AUTO: + flags = BT_COEX_AUTO; + break; + case BT_FORCE_ANT_BT: + flags = BT_COEX_BT; + break; + case BT_FORCE_ANT_WIFI: + flags = BT_COEX_WIFI; + break; + default: + WARN_ON(1); + flags = 0; + } + + bt_cmd->flags = cpu_to_le32(flags); + bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE); + goto send_cmd; + } + bt_cmd->max_kill = 5; bt_cmd->bt4_antenna_isolation_thr = BT_ANTENNA_COUPLING_THRESHOLD; bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling; @@ -642,6 +668,7 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm) bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]); +send_cmd: memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif)); memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd)); @@ -955,6 +982,10 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) struct iwl_bt_coex_ci_cmd cmd = {}; u8 ci_bw_idx; + /* Ignore updates if we are in force mode */ + if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) + return; + rcu_read_lock(); ieee80211_iterate_active_interfaces_atomic( mvm->hw, IEEE80211_IFACE_ITER_NORMAL, @@ -1121,6 +1152,10 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, lockdep_assert_held(&mvm->mutex); + /* Ignore updates if we are in force mode */ + if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) + return; + /* * Rssi update while not associated - can happen since the statistics * are handled asynchronously @@ -1274,6 +1309,10 @@ int iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); + /* Ignore updates if we are in force mode */ + if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) + return 0; + if (ant_isolation == mvm->last_ant_isol) return 0; diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index 29ca72695eaa..602bbd29ec5a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -455,6 +455,43 @@ iwl_dbgfs_bt_tx_prio_write(struct iwl_mvm *mvm, char *buf, return count; } +static ssize_t +iwl_dbgfs_bt_force_ant_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + static const char * const modes_str[BT_FORCE_ANT_MAX] = { + [BT_FORCE_ANT_DIS] = "dis", + [BT_FORCE_ANT_AUTO] = "auto", + [BT_FORCE_ANT_BT] = "bt", + [BT_FORCE_ANT_WIFI] = "wifi", + }; + int ret, bt_force_ant_mode; + + for (bt_force_ant_mode = 0; + bt_force_ant_mode < ARRAY_SIZE(modes_str); + bt_force_ant_mode++) { + if (!strcmp(buf, modes_str[bt_force_ant_mode])) + break; + } + + if (bt_force_ant_mode >= ARRAY_SIZE(modes_str)) + return -EINVAL; + + ret = 0; + mutex_lock(&mvm->mutex); + if (mvm->bt_force_ant_mode == bt_force_ant_mode) + goto out; + + mvm->bt_force_ant_mode = bt_force_ant_mode; + IWL_DEBUG_COEX(mvm, "Force mode: %s\n", + modes_str[mvm->bt_force_ant_mode]); + ret = iwl_send_bt_init_conf(mvm); + +out: + mutex_unlock(&mvm->mutex); + return ret ?: count; +} + #define PRINT_STATS_LE32(_str, _val) \ pos += scnprintf(buf + pos, bufsz - pos, \ fmt_table, _str, \ @@ -1101,6 +1138,7 @@ MVM_DEBUGFS_READ_FILE_OPS(drv_rx_stats); MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart, 10); MVM_DEBUGFS_WRITE_FILE_OPS(fw_nmi, 10); MVM_DEBUGFS_WRITE_FILE_OPS(bt_tx_prio, 10); +MVM_DEBUGFS_WRITE_FILE_OPS(bt_force_ant, 10); MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8); MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8); @@ -1142,6 +1180,7 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(fw_nmi, mvm->debugfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(bt_tx_prio, mvm->debugfs_dir, S_IWUSR); + MVM_DEBUGFS_ADD_FILE(bt_force_ant, mvm->debugfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(scan_ant_rxchain, mvm->debugfs_dir, S_IWUSR | S_IRUSR); MVM_DEBUGFS_ADD_FILE(prph_reg, mvm->debugfs_dir, S_IWUSR | S_IRUSR); diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h index 5fe82c29c8ad..98175ca05e9d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h @@ -76,6 +76,9 @@ * @BT_COEX_2W: * @BT_COEX_3W: * @BT_COEX_NW: + * @BT_COEX_AUTO: + * @BT_COEX_BT: Antenna is for BT (manufacuring tests) + * @BT_COEX_WIFI: Antenna is for BT (manufacuring tests) * @BT_COEX_SYNC2SCO: * @BT_COEX_CORUNNING: * @BT_COEX_MPLUT: @@ -89,6 +92,9 @@ enum iwl_bt_coex_flags { BT_COEX_2W = 0x1 << BT_COEX_MODE_POS, BT_COEX_3W = 0x2 << BT_COEX_MODE_POS, BT_COEX_NW = 0x3 << BT_COEX_MODE_POS, + BT_COEX_AUTO = 0x5 << BT_COEX_MODE_POS, + BT_COEX_BT = 0x6 << BT_COEX_MODE_POS, + BT_COEX_WIFI = 0x7 << BT_COEX_MODE_POS, BT_COEX_SYNC2SCO = BIT(7), BT_COEX_CORUNNING = BIT(8), BT_COEX_MPLUT = BIT(9), diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index fcc6c29482d0..472ef09c1f5f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -235,6 +235,15 @@ enum iwl_mvm_ref_type { IWL_MVM_REF_COUNT, }; +enum iwl_bt_force_ant_mode { + BT_FORCE_ANT_DIS = 0, + BT_FORCE_ANT_AUTO, + BT_FORCE_ANT_BT, + BT_FORCE_ANT_WIFI, + + BT_FORCE_ANT_MAX, +}; + /** * struct iwl_mvm_vif_bf_data - beacon filtering related data * @bf_enabled: indicates if beacon filtering is enabled @@ -629,6 +638,7 @@ struct iwl_mvm { u32 last_ant_isol; u8 last_corun_lut; u8 bt_tx_prio; + enum iwl_bt_force_ant_mode bt_force_ant_mode; /* Thermal Throttling and CTkill */ struct iwl_mvm_tt_mgmt thermal_throttle; From 7fa4fa0c441835ebe78aa25496ae952177e8bb84 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 25 May 2014 13:58:31 +0300 Subject: [PATCH 155/274] iwlwifi: mvm: BT Coex - allow MIMO in more cases We can Tx in MIMO rates when we are in TxTx Disallow mode just like we can when we are in Tight mode. Same if we are in 5Ghz regardless of the mode we are. Change the code to allow MIMO in these cases. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/coex.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c index b2003d82260a..aab92a6b2f35 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex.c @@ -538,7 +538,7 @@ iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif) if (!chanctx_conf || chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) { rcu_read_unlock(); - return BT_COEX_LOOSE_LUT; + return BT_COEX_INVALID_LUT; } ret = BT_COEX_TX_DIS_LUT; @@ -1214,7 +1214,7 @@ u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, lut_type = iwl_get_coex_type(mvm, mvmsta->vif); - if (lut_type == BT_COEX_LOOSE_LUT) + if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT) return LINK_QUAL_AGG_TIME_LIMIT_DEF; /* tight coex, high bt traffic, reduce AGG time limit */ @@ -1225,18 +1225,21 @@ bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, struct ieee80211_sta *sta) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + enum iwl_bt_coex_lut_type lut_type; if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC) return true; /* - * In Tight, BT can't Rx while we Tx, so use both antennas since BT is - * already killed. - * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while we - * Tx. + * In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas + * since BT is already killed. + * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while + * we Tx. + * When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO. */ - return iwl_get_coex_type(mvm, mvmsta->vif) == BT_COEX_TIGHT_LUT; + lut_type = iwl_get_coex_type(mvm, mvmsta->vif); + return lut_type != BT_COEX_LOOSE_LUT; } bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, From 35fbf5d08e57cb4e7bd516781ba9499ff83688f6 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 2 Jun 2014 16:18:42 +0300 Subject: [PATCH 156/274] iwlwifi: mvm: BT Coex - don't limit rate control if TTC is on When the firmware enables TxTxCorunning, we can lift the constaints on the rate control. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/coex.c | 6 ++++++ drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c index aab92a6b2f35..8d7146e54269 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex.c @@ -1212,6 +1212,9 @@ u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, BT_HIGH_TRAFFIC) return LINK_QUAL_AGG_TIME_LIMIT_DEF; + if (mvm->last_bt_notif.ttc_enabled) + return LINK_QUAL_AGG_TIME_LIMIT_DEF; + lut_type = iwl_get_coex_type(mvm, mvmsta->vif); if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT) @@ -1227,6 +1230,9 @@ bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); enum iwl_bt_coex_lut_type lut_type; + if (mvm->last_bt_notif.ttc_enabled) + return true; + if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC) return true; diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h index 98175ca05e9d..39cb33a19862 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h @@ -305,6 +305,7 @@ enum iwl_bt_activity_grading { * @bt_traffic_load: load of BT traffic * @bt_agg_traffic_load: aggregated load of BT traffic * @bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant + * @ttc_enabled: true if ttc has been enabled by the firmware * @primary_ch_lut: LUT used for primary channel * @secondary_ch_lut: LUT used for secondary channel * @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading @@ -317,7 +318,8 @@ struct iwl_bt_coex_profile_notif { u8 bt_traffic_load; u8 bt_agg_traffic_load; u8 bt_ci_compliance; - u8 reserved[3]; + u8 ttc_enabled; + __le16 reserved; __le32 primary_ch_lut; __le32 secondary_ch_lut; From 8286d9f50af6f2c97946c6026482ee607a070c99 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 27 May 2014 22:54:18 +0300 Subject: [PATCH 157/274] iwlwifi: mvm: BT Coex - add reduced Tx power thresholds to constants This really belongs to the constants file. Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/coex.c | 10 ++++------ drivers/net/wireless/iwlwifi/mvm/constants.h | 2 ++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c index 8d7146e54269..cae44edbeef8 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex.c @@ -100,8 +100,6 @@ static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = { #undef EVENT_PRIO_ANT -#define BT_ENABLE_REDUCED_TXPOWER_THRESHOLD (-62) -#define BT_DISABLE_REDUCED_TXPOWER_THRESHOLD (-65) #define BT_ANTENNA_COUPLING_THRESHOLD (30) static int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm) @@ -807,9 +805,9 @@ void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm, mvmvif->bf_data.last_bt_coex_event = rssi; mvmvif->bf_data.bt_coex_max_thold = - enable ? BT_ENABLE_REDUCED_TXPOWER_THRESHOLD : 0; + enable ? -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH : 0; mvmvif->bf_data.bt_coex_min_thold = - enable ? BT_DISABLE_REDUCED_TXPOWER_THRESHOLD : 0; + enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0; } /* must be called under rcu_read_lock */ @@ -946,7 +944,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, /* if the RSSI isn't valid, fake it is very low */ if (!ave_rssi) ave_rssi = -100; - if (ave_rssi > BT_ENABLE_REDUCED_TXPOWER_THRESHOLD) { + if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) { if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true)) IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); @@ -957,7 +955,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, * the iteration, if one interface's rssi isn't good enough, * bt_kill_msk will be set to default values. */ - } else if (ave_rssi < BT_DISABLE_REDUCED_TXPOWER_THRESHOLD) { + } else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) { if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false)) IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); diff --git a/drivers/net/wireless/iwlwifi/mvm/constants.h b/drivers/net/wireless/iwlwifi/mvm/constants.h index 51685693af2e..ca79f7160573 100644 --- a/drivers/net/wireless/iwlwifi/mvm/constants.h +++ b/drivers/net/wireless/iwlwifi/mvm/constants.h @@ -79,6 +79,8 @@ #define IWL_MVM_PS_SNOOZE_WINDOW 50 #define IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW 25 #define IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT 64 +#define IWL_MVM_BT_COEX_EN_RED_TXP_THRESH 62 +#define IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH 65 #define IWL_MVM_BT_COEX_SYNC2SCO 1 #define IWL_MVM_BT_COEX_CORUNNING 1 #define IWL_MVM_BT_COEX_MPLUT 1 From 34c8b24ff284d78f7db17d34125e2395120c471a Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 28 May 2014 21:53:39 +0300 Subject: [PATCH 158/274] iwlwifi: mvm: BT Coex - avoid the shared antenna for management frames If BT is active, we need to refrain from using the shared antenna. This logic is done in the firmware when we use the link quality tables. But for management frames, the rate is written in the Tx command by the driver. Hence the driver needs to make sure not use the shared antenna when BT is active for any frames that don't use the rate scale table such as management frames or multicast. Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/coex.c | 5 +++++ drivers/net/wireless/iwlwifi/mvm/mvm.h | 1 + drivers/net/wireless/iwlwifi/mvm/tx.c | 8 +++++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c index cae44edbeef8..7e0388a32912 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex.c @@ -1246,6 +1246,11 @@ bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, return lut_type != BT_COEX_LOOSE_LUT; } +bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm) +{ + return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF; +} + bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, enum ieee80211_band band) { diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 472ef09c1f5f..8419840d4701 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -973,6 +973,7 @@ u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, struct ieee80211_sta *sta); bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, struct ieee80211_sta *sta); +bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm); bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, enum ieee80211_band band); u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index 3846a6c41eb1..4f7cff506ee7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -205,7 +205,13 @@ static void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, mvm->mgmt_last_antenna_idx = iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant, mvm->mgmt_last_antenna_idx); - rate_flags = BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS; + + if (info->band == IEEE80211_BAND_2GHZ && + !iwl_mvm_bt_coex_is_shared_ant_avail(mvm)) + rate_flags = BIT(ANT_A) << RATE_MCS_ANT_POS; + else + rate_flags = + BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS; /* Set CCK flag as needed */ if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE)) From 50675360a8b0190d23350d42cc12a4e31c056c4f Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 15 Jun 2014 11:12:38 +0300 Subject: [PATCH 159/274] iwlwifi: mvm: disable beacon filtering escape timer in d0i3 The beacon filtering configuration in d0i3 currently uses the max value defined as 1024. However, with beacon interval of 100ms we end up with too-frequent wakeups. Instead, configure the escape timer to 0, asking the fw to disable it altogether. Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/fw-api-power.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h index cbbcd8e284e4..c3a8c86b550d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h @@ -336,7 +336,7 @@ struct iwl_beacon_filter_cmd { #define IWL_BF_DEBUG_FLAG_D0I3 0 #define IWL_BF_ESCAPE_TIMER_DEFAULT 50 -#define IWL_BF_ESCAPE_TIMER_D0I3 1024 +#define IWL_BF_ESCAPE_TIMER_D0I3 0 #define IWL_BF_ESCAPE_TIMER_MAX 1024 #define IWL_BF_ESCAPE_TIMER_MIN 0 From 09b0ce1a874a644fb5799ebf5e54563632714115 Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Sun, 25 May 2014 17:07:38 +0300 Subject: [PATCH 160/274] iwlwifi: mvm: Introduce an API to set STA_FLG_DISABLE_TX flag Introduce new station flag STA_FLG_DISABLE_TX, which is modified with ADD_STA command. This flag, when set, disables tx to the STA. Provide an API (iwl_mvm_sta_modify_disable_tx) to modify this flag, which should be used in channel switch and immediate quiet flows. Signed-off-by: Andrei Otcheretianski Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-fw.h | 2 ++ drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h | 7 ++----- drivers/net/wireless/iwlwifi/mvm/sta.c | 20 +++++++++++++++++++ drivers/net/wireless/iwlwifi/mvm/sta.h | 2 ++ 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index 287b949c95f6..c71331034e9b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -120,10 +120,12 @@ enum iwl_ucode_tlv_flag { * enum iwl_ucode_tlv_api - ucode api * @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field. * @IWL_UCODE_TLV_API_CSA_FLOW: ucode can do unbind-bind flow for CSA. + * @IWL_UCODE_TLV_API_DISABLE_STA_TX: ucode supports tx_disable bit. */ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID = BIT(0), IWL_UCODE_TLV_API_CSA_FLOW = BIT(4), + IWL_UCODE_TLV_API_DISABLE_STA_TX = BIT(5), }; /** diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h index 39cebee8016f..47bd0406355d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h @@ -67,7 +67,7 @@ * enum iwl_sta_flags - flags for the ADD_STA host command * @STA_FLG_REDUCED_TX_PWR_CTRL: * @STA_FLG_REDUCED_TX_PWR_DATA: - * @STA_FLG_FLG_ANT_MSK: Antenna selection + * @STA_FLG_DISABLE_TX: set if TX should be disabled * @STA_FLG_PS: set if STA is in Power Save * @STA_FLG_INVALID: set if STA is invalid * @STA_FLG_DLP_EN: Direct Link Protocol is enabled @@ -91,10 +91,7 @@ enum iwl_sta_flags { STA_FLG_REDUCED_TX_PWR_CTRL = BIT(3), STA_FLG_REDUCED_TX_PWR_DATA = BIT(6), - STA_FLG_FLG_ANT_A = (1 << 4), - STA_FLG_FLG_ANT_B = (2 << 4), - STA_FLG_FLG_ANT_MSK = (STA_FLG_FLG_ANT_A | - STA_FLG_FLG_ANT_B), + STA_FLG_DISABLE_TX = BIT(4), STA_FLG_PS = BIT(8), STA_FLG_DRAIN_FLOW = BIT(12), diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 1fb01ea2e704..d3a6cf7558eb 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -1448,3 +1448,23 @@ int iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm, return 0; } + +void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm, + struct iwl_mvm_sta *mvmsta, bool disable) +{ + struct iwl_mvm_add_sta_cmd cmd = { + .add_modify = STA_MODE_MODIFY, + .sta_id = mvmsta->sta_id, + .station_flags = disable ? cpu_to_le32(STA_FLG_DISABLE_TX) : 0, + .station_flags_msk = cpu_to_le32(STA_FLG_DISABLE_TX), + .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color), + }; + int ret; + + if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_DISABLE_STA_TX)) + return; + + ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd); + if (ret) + IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); +} diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h index d98e8a2142b8..10c1a5352651 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/sta.h @@ -404,5 +404,7 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, bool agg); int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, bool drain); +void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm, + struct iwl_mvm_sta *mvmsta, bool disable); #endif /* __sta_h__ */ From 1c87bbad439d818f94f2e8bff98f73d6fb568dfc Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Thu, 27 Feb 2014 16:41:52 +0200 Subject: [PATCH 161/274] iwlwifi: mvm: support extended beacon notification Use extended beacon notification when supported by FW. Set last beacon system time to AP or GO interface. System time of last beacon can be used to avoid TBTT overlapping between two interfaces, CSA and other uses. Signed-off-by: David Spinadel Signed-off-by: Andrei Otcheretianski Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-fw.h | 2 ++ drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h | 14 +++++++++ drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c | 32 ++++++++++++++------ drivers/net/wireless/iwlwifi/mvm/mac80211.c | 1 + drivers/net/wireless/iwlwifi/mvm/mvm.h | 3 ++ 5 files changed, 42 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index c71331034e9b..13e5d69845e9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -119,11 +119,13 @@ enum iwl_ucode_tlv_flag { /** * enum iwl_ucode_tlv_api - ucode api * @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field. + * @IWL_UCODE_TLV_CAPA_EXTENDED_BEACON: Support Extended beacon notification * @IWL_UCODE_TLV_API_CSA_FLOW: ucode can do unbind-bind flow for CSA. * @IWL_UCODE_TLV_API_DISABLE_STA_TX: ucode supports tx_disable bit. */ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID = BIT(0), + IWL_UCODE_TLV_CAPA_EXTENDED_BEACON = BIT(1), IWL_UCODE_TLV_API_CSA_FLOW = BIT(4), IWL_UCODE_TLV_API_DISABLE_STA_TX = BIT(5), }; diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h index 6cc5f52b807f..bdd6ff648626 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h @@ -548,6 +548,20 @@ struct iwl_beacon_notif { __le32 ibss_mgr_status; } __packed; +/** + * struct iwl_extended_beacon_notif - notifies about beacon transmission + * @beacon_notify_hdr: tx response command associated with the beacon + * @tsf: last beacon tsf + * @ibss_mgr_status: whether IBSS is manager + * @gp2: last beacon time in gp2 + */ +struct iwl_extended_beacon_notif { + struct iwl_mvm_tx_resp beacon_notify_hdr; + __le64 tsf; + __le32 ibss_mgr_status; + __le32 gp2; +} __packed; /* BEACON_NTFY_API_S_VER_5 */ + /** * enum iwl_dump_control - dump (flush) control flags * @DUMP_TX_FIFO_FLUSH: Dump MSDUs until the the FIFO is empty diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index 8b5302777632..3b0390b8639c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -1205,19 +1205,31 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_beacon_notif *beacon = (void *)pkt->data; - u16 status __maybe_unused = - le16_to_cpu(beacon->beacon_notify_hdr.status.status); - u32 rate __maybe_unused = - le32_to_cpu(beacon->beacon_notify_hdr.initial_rate); + struct iwl_mvm_tx_resp *beacon_notify_hdr; + u64 tsf; lockdep_assert_held(&mvm->mutex); - IWL_DEBUG_RX(mvm, "beacon status %#x retries:%d tsf:0x%16llX rate:%d\n", - status & TX_STATUS_MSK, - beacon->beacon_notify_hdr.failure_frame, - le64_to_cpu(beacon->tsf), - rate); + if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_CAPA_EXTENDED_BEACON) { + struct iwl_extended_beacon_notif *beacon = (void *)pkt->data; + + beacon_notify_hdr = &beacon->beacon_notify_hdr; + tsf = le64_to_cpu(beacon->tsf); + mvm->ap_last_beacon_gp2 = le32_to_cpu(beacon->gp2); + } else { + struct iwl_beacon_notif *beacon = (void *)pkt->data; + + beacon_notify_hdr = &beacon->beacon_notify_hdr; + tsf = le64_to_cpu(beacon->tsf); + } + + IWL_DEBUG_RX(mvm, + "beacon status %#x retries:%d tsf:0x%16llX gp2:0x%X rate:%d\n", + le16_to_cpu(beacon_notify_hdr->status.status) & + TX_STATUS_MSK, + beacon_notify_hdr->failure_frame, tsf, + mvm->ap_last_beacon_gp2, + le32_to_cpu(beacon_notify_hdr->initial_rate)); if (unlikely(mvm->csa_vif && mvm->csa_vif->csa_active)) { if (!ieee80211_csa_is_complete(mvm->csa_vif)) { diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index f22be88c0387..f2fde3649139 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -1465,6 +1465,7 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); mvmvif->ap_ibss_active = false; + mvm->ap_last_beacon_gp2 = 0; iwl_mvm_bt_coex_vif_change(mvm); diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 8419840d4701..da692582d502 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -658,6 +658,9 @@ struct iwl_mvm { bool ps_disabled; struct ieee80211_vif *csa_vif; + + /* system time of last beacon (for AP/GO interface) */ + u32 ap_last_beacon_gp2; }; /* Extract MVM priv from op_mode and _hw */ From 75f6b9b64eeead2a7726f342b89a0c74c985e584 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 2 Jun 2014 10:03:33 +0300 Subject: [PATCH 162/274] iwlwivi: mvm: BT Coex - properly set the priority of beacons Since the new API allows multiple priorities, we need to properly set the beacon's prorities in the TX cmd associated to it. Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index 3b0390b8639c..aad36212c4d7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -904,7 +904,7 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, struct iwl_mac_beacon_cmd beacon_cmd = {}; struct ieee80211_tx_info *info; u32 beacon_skb_len; - u32 rate; + u32 rate, tx_flags; if (WARN_ON(!beacon)) return -EINVAL; @@ -914,14 +914,17 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, /* TODO: for now the beacon template id is set to be the mac context id. * Might be better to handle it as another resource ... */ beacon_cmd.template_id = cpu_to_le32((u32)mvmvif->id); + info = IEEE80211_SKB_CB(beacon); /* Set up TX command fields */ beacon_cmd.tx.len = cpu_to_le16((u16)beacon_skb_len); beacon_cmd.tx.sta_id = mvmvif->bcast_sta.sta_id; beacon_cmd.tx.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE); - beacon_cmd.tx.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL | - TX_CMD_FLG_BT_DIS | - TX_CMD_FLG_TSF); + tx_flags = TX_CMD_FLG_SEQ_CTL | TX_CMD_FLG_TSF; + tx_flags |= + iwl_mvm_bt_coex_tx_prio(mvm, (void *)beacon->data, info, 0) << + TX_CMD_FLG_BT_PRIO_POS; + beacon_cmd.tx.tx_flags = cpu_to_le32(tx_flags); mvm->mgmt_last_antenna_idx = iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant, @@ -931,8 +934,6 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS); - info = IEEE80211_SKB_CB(beacon); - if (info->band == IEEE80211_BAND_5GHZ || vif->p2p) { rate = IWL_FIRST_OFDM_RATE; } else { From c47af22ad630e03053f3eeefd19bd8274989ffbb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 30 Apr 2014 16:34:45 +0200 Subject: [PATCH 163/274] iwlwifi: mvm: handle device start failures during restart If the device fails during a restart, mac80211 now handles the situation better but we still have a little bit of cleanup to do in the driver - add the required code. Signed-off-by: Johannes Berg Reviewed-by: ArikX Nemtsov Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index f2fde3649139..3c2c9b99b59e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -689,6 +689,16 @@ static int iwl_mvm_mac_start(struct ieee80211_hw *hw) iwl_mvm_restart_cleanup(mvm); ret = iwl_mvm_up(mvm); + + if (ret && test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { + /* Something went wrong - we need to finish some cleanup + * that normally iwl_mvm_mac_restart_complete() below + * would do. + */ + clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); + iwl_mvm_d0i3_enable_tx(mvm, NULL); + } + mutex_unlock(&mvm->mutex); return ret; From a42c9fcc4a88cdd246fab3bcf06c4487afee3d88 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 8 May 2014 16:17:31 +0300 Subject: [PATCH 164/274] Revert "iwlwifi: remove IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT flag" This reverts commit dc9a19296a872644f19a06d8eeb5db222d327b41. 3610 cards don't support UAPSD. Signed-off-by: Arik Nemtsov Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-fw.h | 1 + drivers/net/wireless/iwlwifi/mvm/mac80211.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index 0aa7c0085c9f..b1a33322b9ba 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -88,6 +88,7 @@ * P2P client interfaces simultaneously if they are in different bindings. * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_SCM: support power save on BSS station and * P2P client interfaces simultaneously if they are in same bindings. + * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: General support for uAPSD * @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save * @IWL_UCODE_TLV_FLAGS_BCAST_FILTERING: uCode supports broadcast filtering. * @IWL_UCODE_TLV_FLAGS_GO_UAPSD: AP/GO interfaces support uAPSD clients diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 7215f5980186..1cef708cb74d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -303,6 +303,13 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; } + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT && + !iwlwifi_mod_params.uapsd_disable) { + hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD; + hw->uapsd_queues = IWL_UAPSD_AC_INFO; + hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; + } + hw->sta_data_size = sizeof(struct iwl_mvm_sta); hw->vif_data_size = sizeof(struct iwl_mvm_vif); hw->chanctx_data_size = sizeof(u16); From e48393e8cf99f2b070b0a1c3d79411ccddcba2df Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Thu, 22 May 2014 11:19:02 +0300 Subject: [PATCH 165/274] iwlwifi: mvm: Fix broadcast filtering Current code did not allow sending the broadcast filtering command for P2P Client interfaces. However, this was not enough, since once broadcast filtering command was issued over the station interface after the P2P Client connected, the command also attached the filters to the P2P Client MAC which is not allowed (FW ASSERT 1063). Fix this skipping P2P Client interfaces when constructing the broadcast filtering command Signed-off-by: Ilan Peer Reviewed-by: ArikX Nemtsov Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 1cef708cb74d..9bfb90680cdc 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -1166,8 +1166,12 @@ static void iwl_mvm_bcast_filter_iterator(void *_data, u8 *mac, bcast_mac = &cmd->macs[mvmvif->id]; - /* enable filtering only for associated stations */ - if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc) + /* + * enable filtering only for associated stations, but not for P2P + * Clients + */ + if (vif->type != NL80211_IFTYPE_STATION || vif->p2p || + !vif->bss_conf.assoc) return; bcast_mac->default_discard = 1; @@ -1244,10 +1248,6 @@ static int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm, if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING)) return 0; - /* bcast filtering isn't supported for P2P client */ - if (vif->p2p) - return 0; - if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) return 0; From c56ef6725068c0ce499e517409c0da226ef51b08 Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Wed, 5 Feb 2014 15:21:13 +0200 Subject: [PATCH 166/274] mac80211: support more than one band in scan request Some drivers (such as iwlmvm) can handle multiple bands in a single HW scan request. Add a HW flag to indicate that the driver support this. To hold the required data, create a separate structure for HW scan request that holds cfg scan request and data about different parts of the scan IEs. As this changes the mac80211 API, update all drivers using it to use the correct new function type/argument. Signed-off-by: David Spinadel Signed-off-by: Johannes Berg --- drivers/net/wireless/at76c50x-usb.c | 3 +- drivers/net/wireless/ath/ath10k/mac.c | 3 +- drivers/net/wireless/cw1200/scan.c | 3 +- drivers/net/wireless/cw1200/scan.h | 2 +- drivers/net/wireless/iwlegacy/common.c | 3 +- drivers/net/wireless/iwlegacy/common.h | 2 +- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 3 +- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 3 +- drivers/net/wireless/mac80211_hwsim.c | 3 +- drivers/net/wireless/ti/wl1251/main.c | 3 +- drivers/net/wireless/ti/wlcore/main.c | 3 +- include/net/mac80211.h | 39 +++++++- net/mac80211/driver-ops.h | 2 +- net/mac80211/ieee80211_i.h | 9 +- net/mac80211/scan.c | 89 ++++++++++++----- net/mac80211/util.c | 105 ++++++++++++++------ 16 files changed, 200 insertions(+), 75 deletions(-) diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index d48776e4f343..334c2ece855a 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -1955,8 +1955,9 @@ static void at76_dwork_hw_scan(struct work_struct *work) static int at76_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req) + struct ieee80211_scan_request *hw_req) { + struct cfg80211_scan_request *req = &hw_req->req; struct at76_priv *priv = hw->priv; struct at76_req_scan scan; u8 *ssid = NULL; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index a21080028c54..b8314a534972 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3137,10 +3137,11 @@ exit: static int ath10k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req) + struct ieee80211_scan_request *hw_req) { struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct cfg80211_scan_request *req = &hw_req->req; struct wmi_start_scan_arg arg; int ret = 0; int i; diff --git a/drivers/net/wireless/cw1200/scan.c b/drivers/net/wireless/cw1200/scan.c index 9afcd4ce3368..b2fb6c632092 100644 --- a/drivers/net/wireless/cw1200/scan.c +++ b/drivers/net/wireless/cw1200/scan.c @@ -53,9 +53,10 @@ static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan) int cw1200_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req) + struct ieee80211_scan_request *hw_req) { struct cw1200_common *priv = hw->priv; + struct cfg80211_scan_request *req = &hw_req->req; struct wsm_template_frame frame = { .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, }; diff --git a/drivers/net/wireless/cw1200/scan.h b/drivers/net/wireless/cw1200/scan.h index 5a8296ccfa82..cc75459e5784 100644 --- a/drivers/net/wireless/cw1200/scan.h +++ b/drivers/net/wireless/cw1200/scan.h @@ -41,7 +41,7 @@ struct cw1200_scan { int cw1200_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req); + struct ieee80211_scan_request *hw_req); void cw1200_scan_work(struct work_struct *work); void cw1200_scan_timeout(struct work_struct *work); void cw1200_clear_recent_scan_work(struct work_struct *work); diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c index ecc674627e6e..03de7467aecf 100644 --- a/drivers/net/wireless/iwlegacy/common.c +++ b/drivers/net/wireless/iwlegacy/common.c @@ -1572,8 +1572,9 @@ il_scan_initiate(struct il_priv *il, struct ieee80211_vif *vif) int il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req) + struct ieee80211_scan_request *hw_req) { + struct cfg80211_scan_request *req = &hw_req->req; struct il_priv *il = hw->priv; int ret; diff --git a/drivers/net/wireless/iwlegacy/common.h b/drivers/net/wireless/iwlegacy/common.h index ea5c0f863c4e..5b972798bdff 100644 --- a/drivers/net/wireless/iwlegacy/common.h +++ b/drivers/net/wireless/iwlegacy/common.h @@ -1787,7 +1787,7 @@ int il_scan_cancel(struct il_priv *il); int il_scan_cancel_timeout(struct il_priv *il, unsigned long ms); void il_force_scan_end(struct il_priv *il); int il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req); + struct ieee80211_scan_request *hw_req); void il_internal_short_hw_scan(struct il_priv *il); int il_force_reset(struct il_priv *il, bool external); u16 il_fill_probe_req(struct il_priv *il, struct ieee80211_mgmt *frame, diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 29af7b51e370..afb98f4fdaf3 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -1495,9 +1495,10 @@ static int iwlagn_mac_change_interface(struct ieee80211_hw *hw, static int iwlagn_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req) + struct ieee80211_scan_request *hw_req) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct cfg80211_scan_request *req = &hw_req->req; int ret; IWL_DEBUG_MAC80211(priv, "enter\n"); diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 7215f5980186..4dc2e05f49ce 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -1537,9 +1537,10 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw, static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req) + struct ieee80211_scan_request *hw_req) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct cfg80211_scan_request *req = &hw_req->req; int ret; if (req->n_channels == 0 || req->n_channels > MAX_NUM_SCAN_CHANNELS) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 06a0722164da..eba51460a5de 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1736,9 +1736,10 @@ static void hw_scan_work(struct work_struct *work) static int mac80211_hwsim_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req) + struct ieee80211_scan_request *hw_req) { struct mac80211_hwsim_data *hwsim = hw->priv; + struct cfg80211_scan_request *req = &hw_req->req; mutex_lock(&hwsim->mutex); if (WARN_ON(hwsim->tmp_chan || hwsim->hw_scan_request)) { diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c index 4e782f18ae34..38234851457e 100644 --- a/drivers/net/wireless/ti/wl1251/main.c +++ b/drivers/net/wireless/ti/wl1251/main.c @@ -991,8 +991,9 @@ out: static int wl1251_op_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req) + struct ieee80211_scan_request *hw_req) { + struct cfg80211_scan_request *req = &hw_req->req; struct wl1251 *wl = hw->priv; struct sk_buff *skb; size_t ssid_len = 0; diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 3d6028e62750..e5ffb8b91dd4 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -3540,8 +3540,9 @@ out: static int wl1271_op_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req) + struct ieee80211_scan_request *hw_req) { + struct cfg80211_scan_request *req = &hw_req->req; struct wl1271 *wl = hw->priv; int ret; u8 *ssid = NULL; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 18c2bdbaf988..9536ee3e22a9 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -768,6 +768,26 @@ struct ieee80211_sched_scan_ies { size_t len[IEEE80211_NUM_BANDS]; }; +/** + * struct ieee80211_scan_ies - descriptors for different blocks of IEs + * + * This structure is used to point to different blocks of IEs in HW scan. + * These blocks contain the IEs passed by userspace and the ones generated + * by mac80211. + * + * @ies: pointers to band specific IEs. + * @len: lengths of band_specific IEs. + * @common_ies: IEs for all bands (especially vendor specific ones) + * @common_ie_len: length of the common_ies + */ +struct ieee80211_scan_ies { + const u8 *ies[IEEE80211_NUM_BANDS]; + size_t len[IEEE80211_NUM_BANDS]; + const u8 *common_ies; + size_t common_ie_len; +}; + + static inline struct ieee80211_tx_info *IEEE80211_SKB_CB(struct sk_buff *skb) { return (struct ieee80211_tx_info *)skb->cb; @@ -1606,6 +1626,9 @@ struct ieee80211_tx_control { * on single-channel hardware. It can also be used as an * optimization in certain channel switch cases with * multi-channel. + * + * @IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS: The HW supports scanning on all bands + * in one command, mac80211 doesn't have to run separate scans per band. */ enum ieee80211_hw_flags { IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, @@ -1638,6 +1661,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_SUPPORTS_HT_CCK_RATES = 1<<27, IEEE80211_HW_CHANCTX_STA_CSA = 1<<28, IEEE80211_HW_CHANGE_RUNNING_CHANCTX = 1<<29, + IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS = 1<<30, }; /** @@ -1763,6 +1787,19 @@ struct ieee80211_hw { const struct ieee80211_cipher_scheme *cipher_schemes; }; +/** + * struct ieee80211_scan_request - hw scan request + * + * @ies: pointers different parts of IEs (in req.ie) + * @req: cfg80211 request. + */ +struct ieee80211_scan_request { + struct ieee80211_scan_ies ies; + + /* Keep last */ + struct cfg80211_scan_request req; +}; + /** * wiphy_to_ieee80211_hw - return a mac80211 driver hw struct from a wiphy * @@ -2874,7 +2911,7 @@ struct ieee80211_ops { void (*set_default_unicast_key)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int idx); int (*hw_scan)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req); + struct ieee80211_scan_request *req); void (*cancel_hw_scan)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); int (*sched_scan_start)(struct ieee80211_hw *hw, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 2265bd7a44ba..faa0d90f6e80 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -314,7 +314,7 @@ static inline void drv_update_tkip_key(struct ieee80211_local *local, static inline int drv_hw_scan(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, - struct cfg80211_scan_request *req) + struct ieee80211_scan_request *req) { int ret; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 6c8089429892..f88bd1659cde 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1152,7 +1152,8 @@ struct ieee80211_local { unsigned long scanning; struct cfg80211_ssid scan_ssid; struct cfg80211_scan_request *int_scan_req; - struct cfg80211_scan_request *scan_req, *hw_scan_req; + struct cfg80211_scan_request *scan_req; + struct ieee80211_scan_request *hw_scan_req; struct cfg80211_chan_def scan_chandef; enum ieee80211_band hw_scan_band; int scan_channel_idx; @@ -1756,8 +1757,10 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, const u8 *bssid, u16 stype, u16 reason, bool send_frame, u8 *frame_buf); int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, - size_t buffer_len, const u8 *ie, size_t ie_len, - enum ieee80211_band band, u32 rate_mask, + size_t buffer_len, + struct ieee80211_scan_ies *ie_desc, + const u8 *ie, size_t ie_len, + u8 bands_used, u32 *rate_masks, struct cfg80211_chan_def *chandef); struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, u32 ratemask, diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index f40661eb75b5..116959e070d0 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -235,38 +235,51 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) { struct cfg80211_scan_request *req = local->scan_req; struct cfg80211_chan_def chandef; - enum ieee80211_band band; + u8 bands_used = 0; int i, ielen, n_chans; if (test_bit(SCAN_HW_CANCELLED, &local->scanning)) return false; - do { - if (local->hw_scan_band == IEEE80211_NUM_BANDS) - return false; - - band = local->hw_scan_band; - n_chans = 0; + if (local->hw.flags & IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS) { for (i = 0; i < req->n_channels; i++) { - if (req->channels[i]->band == band) { - local->hw_scan_req->channels[n_chans] = - req->channels[i]; - n_chans++; - } + local->hw_scan_req->req.channels[i] = req->channels[i]; + bands_used |= BIT(req->channels[i]->band); } - local->hw_scan_band++; - } while (!n_chans); + n_chans = req->n_channels; + } else { + do { + if (local->hw_scan_band == IEEE80211_NUM_BANDS) + return false; - local->hw_scan_req->n_channels = n_chans; + n_chans = 0; + + for (i = 0; i < req->n_channels; i++) { + if (req->channels[i]->band != + local->hw_scan_band) + continue; + local->hw_scan_req->req.channels[n_chans] = + req->channels[i]; + n_chans++; + bands_used |= BIT(req->channels[i]->band); + } + + local->hw_scan_band++; + } while (!n_chans); + } + + local->hw_scan_req->req.n_channels = n_chans; ieee80211_prepare_scan_chandef(&chandef, req->scan_width); - ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie, + ielen = ieee80211_build_preq_ies(local, + (u8 *)local->hw_scan_req->req.ie, local->hw_scan_ies_bufsize, - req->ie, req->ie_len, band, - req->rates[band], &chandef); - local->hw_scan_req->ie_len = ielen; - local->hw_scan_req->no_cck = req->no_cck; + &local->hw_scan_req->ies, + req->ie, req->ie_len, + bands_used, req->rates, &chandef); + local->hw_scan_req->req.ie_len = ielen; + local->hw_scan_req->req.no_cck = req->no_cck; return true; } @@ -291,7 +304,9 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) if (WARN_ON(!local->scan_req)) return; - if (hw_scan && !aborted && ieee80211_prep_hw_scan(local)) { + if (hw_scan && !aborted && + !(local->hw.flags & IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS) && + ieee80211_prep_hw_scan(local)) { int rc; rc = drv_hw_scan(local, @@ -473,6 +488,21 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, u8 *ies; local->hw_scan_ies_bufsize = local->scan_ies_len + req->ie_len; + + if (local->hw.flags & IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS) { + int i, n_bands = 0; + u8 bands_counted = 0; + + for (i = 0; i < req->n_channels; i++) { + if (bands_counted & BIT(req->channels[i]->band)) + continue; + bands_counted |= BIT(req->channels[i]->band); + n_bands++; + } + + local->hw_scan_ies_bufsize *= n_bands; + } + local->hw_scan_req = kmalloc( sizeof(*local->hw_scan_req) + req->n_channels * sizeof(req->channels[0]) + @@ -480,13 +510,13 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, if (!local->hw_scan_req) return -ENOMEM; - local->hw_scan_req->ssids = req->ssids; - local->hw_scan_req->n_ssids = req->n_ssids; + local->hw_scan_req->req.ssids = req->ssids; + local->hw_scan_req->req.n_ssids = req->n_ssids; ies = (u8 *)local->hw_scan_req + sizeof(*local->hw_scan_req) + req->n_channels * sizeof(req->channels[0]); - local->hw_scan_req->ie = ies; - local->hw_scan_req->flags = req->flags; + local->hw_scan_req->req.ie = ies; + local->hw_scan_req->req.flags = req->flags; local->hw_scan_band = 0; @@ -976,6 +1006,7 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, struct ieee80211_sched_scan_ies sched_scan_ies = {}; struct cfg80211_chan_def chandef; int ret, i, iebufsz; + struct ieee80211_scan_ies dummy_ie_desc; iebufsz = local->scan_ies_len + req->ie_len; @@ -985,6 +1016,8 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, return -ENOTSUPP; for (i = 0; i < IEEE80211_NUM_BANDS; i++) { + u32 rate_masks[IEEE80211_NUM_BANDS] = {}; + if (!local->hw.wiphy->bands[i]) continue; @@ -995,11 +1028,13 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, } ieee80211_prepare_scan_chandef(&chandef, req->scan_width); + rate_masks[i] = (u32) -1; sched_scan_ies.len[i] = ieee80211_build_preq_ies(local, sched_scan_ies.ie[i], - iebufsz, req->ie, req->ie_len, - i, (u32) -1, &chandef); + iebufsz, &dummy_ie_desc, + req->ie, req->ie_len, BIT(i), + rate_masks, &chandef); } ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 42d448d765b4..e31458201278 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1219,14 +1219,17 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, } } -int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, - size_t buffer_len, const u8 *ie, size_t ie_len, - enum ieee80211_band band, u32 rate_mask, - struct cfg80211_chan_def *chandef) +static int ieee80211_build_preq_ies_band(struct ieee80211_local *local, + u8 *buffer, size_t buffer_len, + const u8 *ie, size_t ie_len, + enum ieee80211_band band, + u32 rate_mask, + struct cfg80211_chan_def *chandef, + size_t *offset) { struct ieee80211_supported_band *sband; u8 *pos = buffer, *end = buffer + buffer_len; - size_t offset = 0, noffset; + size_t noffset; int supp_rates_len, i; u8 rates[32]; int num_rates; @@ -1234,6 +1237,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, int shift; u32 rate_flags; + *offset = 0; + sband = local->hw.wiphy->bands[band]; if (WARN_ON_ONCE(!sband)) return 0; @@ -1272,12 +1277,12 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, noffset = ieee80211_ie_split(ie, ie_len, before_extrates, ARRAY_SIZE(before_extrates), - offset); - if (end - pos < noffset - offset) + *offset); + if (end - pos < noffset - *offset) goto out_err; - memcpy(pos, ie + offset, noffset - offset); - pos += noffset - offset; - offset = noffset; + memcpy(pos, ie + *offset, noffset - *offset); + pos += noffset - *offset; + *offset = noffset; } ext_rates_len = num_rates - supp_rates_len; @@ -1311,12 +1316,12 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, }; noffset = ieee80211_ie_split(ie, ie_len, before_ht, ARRAY_SIZE(before_ht), - offset); - if (end - pos < noffset - offset) + *offset); + if (end - pos < noffset - *offset) goto out_err; - memcpy(pos, ie + offset, noffset - offset); - pos += noffset - offset; - offset = noffset; + memcpy(pos, ie + *offset, noffset - *offset); + pos += noffset - *offset; + *offset = noffset; } if (sband->ht_cap.ht_supported) { @@ -1351,12 +1356,12 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, }; noffset = ieee80211_ie_split(ie, ie_len, before_vht, ARRAY_SIZE(before_vht), - offset); - if (end - pos < noffset - offset) + *offset); + if (end - pos < noffset - *offset) goto out_err; - memcpy(pos, ie + offset, noffset - offset); - pos += noffset - offset; - offset = noffset; + memcpy(pos, ie + *offset, noffset - *offset); + pos += noffset - *offset; + *offset = noffset; } if (sband->vht_cap.vht_supported) { @@ -1366,21 +1371,54 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, sband->vht_cap.cap); } - /* add any remaining custom IEs */ - if (ie && ie_len) { - noffset = ie_len; - if (end - pos < noffset - offset) - goto out_err; - memcpy(pos, ie + offset, noffset - offset); - pos += noffset - offset; - } - return pos - buffer; out_err: WARN_ONCE(1, "not enough space for preq IEs\n"); return pos - buffer; } +int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, + size_t buffer_len, + struct ieee80211_scan_ies *ie_desc, + const u8 *ie, size_t ie_len, + u8 bands_used, u32 *rate_masks, + struct cfg80211_chan_def *chandef) +{ + size_t pos = 0, old_pos = 0, custom_ie_offset = 0; + int i; + + memset(ie_desc, 0, sizeof(*ie_desc)); + + for (i = 0; i < IEEE80211_NUM_BANDS; i++) { + if (bands_used & BIT(i)) { + pos += ieee80211_build_preq_ies_band(local, + buffer + pos, + buffer_len - pos, + ie, ie_len, i, + rate_masks[i], + chandef, + &custom_ie_offset); + ie_desc->ies[i] = buffer + old_pos; + ie_desc->len[i] = pos - old_pos; + old_pos = pos; + } + } + + /* add any remaining custom IEs */ + if (ie && ie_len) { + if (WARN_ONCE(buffer_len - pos < ie_len - custom_ie_offset, + "not enough space for preq custom IEs\n")) + return pos; + memcpy(buffer + pos, ie + custom_ie_offset, + ie_len - custom_ie_offset); + ie_desc->common_ies = buffer + pos; + ie_desc->common_ie_len = ie_len - custom_ie_offset; + pos += ie_len - custom_ie_offset; + } + + return pos; +}; + struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, u32 ratemask, struct ieee80211_channel *chan, @@ -1393,6 +1431,8 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb; struct ieee80211_mgmt *mgmt; int ies_len; + u32 rate_masks[IEEE80211_NUM_BANDS] = {}; + struct ieee80211_scan_ies dummy_ie_desc; /* * Do not send DS Channel parameter for directed probe requests @@ -1410,10 +1450,11 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, if (!skb) return NULL; + rate_masks[chan->band] = ratemask; ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb), - skb_tailroom(skb), - ie, ie_len, chan->band, - ratemask, &chandef); + skb_tailroom(skb), &dummy_ie_desc, + ie, ie_len, BIT(chan->band), + rate_masks, &chandef); skb_put(skb, ies_len); if (dst) { From 633e27132625a0692440c4db58b901fb3cb67c55 Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Thu, 6 Feb 2014 16:15:23 +0200 Subject: [PATCH 167/274] mac80211: split sched scan IEs Split sched scan IEs to band specific and not band specific blocks. Common IEs blocks may be sent to the FW once per command, instead of per band. This allows optimization of size of the command, which may be required by some drivers (eg. iwlmvm with newer firmware version). As this changes the mac80211 API, update all drivers to use the new version correctly, even if they don't (yet) make use of the split data. Signed-off-by: David Spinadel Reviewed-by: Alexander Bondar Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 2 +- drivers/net/wireless/iwlwifi/mvm/mvm.h | 2 +- drivers/net/wireless/iwlwifi/mvm/scan.c | 27 ++++++++---- drivers/net/wireless/ti/wl12xx/scan.c | 20 ++++++--- drivers/net/wireless/ti/wl12xx/scan.h | 2 +- drivers/net/wireless/ti/wl18xx/scan.c | 16 +++++-- drivers/net/wireless/ti/wl18xx/scan.h | 2 +- drivers/net/wireless/ti/wlcore/cmd.c | 11 +++-- drivers/net/wireless/ti/wlcore/cmd.h | 3 +- drivers/net/wireless/ti/wlcore/main.c | 2 +- drivers/net/wireless/ti/wlcore/scan.h | 2 +- drivers/net/wireless/ti/wlcore/wlcore.h | 2 +- include/net/mac80211.h | 23 ++-------- net/mac80211/driver-ops.h | 2 +- net/mac80211/scan.c | 49 +++++++++++---------- 15 files changed, 89 insertions(+), 76 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 4dc2e05f49ce..7dd363dd3ad3 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -1828,7 +1828,7 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw, static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies) + struct ieee80211_scan_ies *ies) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); int ret; diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index fcc6c29482d0..97630f9a7cb9 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -854,7 +854,7 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm, int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies); + struct ieee80211_scan_ies *ies); int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm, struct cfg80211_sched_scan_request *req); int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 4b6c7d4bd199..3206fa097255 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -204,7 +204,8 @@ static void iwl_mvm_scan_fill_channels(struct iwl_scan_cmd *cmd, */ static u16 iwl_mvm_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta, int n_ssids, const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len, + const u8 *band_ie, int band_ie_len, + const u8 *common_ie, int common_ie_len, int left) { int len = 0; @@ -244,12 +245,19 @@ static u16 iwl_mvm_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta, len += ssid_len + 2; - if (WARN_ON(left < ie_len)) + if (WARN_ON(left < band_ie_len + common_ie_len)) return len; - if (ie && ie_len) { - memcpy(pos, ie, ie_len); - len += ie_len; + if (band_ie && band_ie_len) { + memcpy(pos, band_ie, band_ie_len); + pos += band_ie_len; + len += band_ie_len; + } + + if (common_ie && common_ie_len) { + memcpy(pos, common_ie, common_ie_len); + pos += common_ie_len; + len += common_ie_len; } return (u16)len; @@ -382,7 +390,7 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm, (struct ieee80211_mgmt *)cmd->data, vif->addr, req->n_ssids, ssid, ssid_len, - req->ie, req->ie_len, + req->ie, req->ie_len, NULL, 0, mvm->fw->ucode_capa.max_probe_length)); iwl_mvm_scan_fill_channels(cmd, req, basic_ssid, ¶ms); @@ -561,7 +569,7 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm, static void iwl_scan_offload_build_tx_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_sched_scan_ies *ies, + struct ieee80211_scan_ies *ies, enum ieee80211_band band, struct iwl_tx_cmd *cmd, u8 *data) @@ -577,7 +585,8 @@ static void iwl_scan_offload_build_tx_cmd(struct iwl_mvm *mvm, cmd_len = iwl_mvm_fill_probe_req((struct ieee80211_mgmt *)data, vif->addr, 1, NULL, 0, - ies->ie[band], ies->len[band], + ies->ies[band], ies->len[band], + ies->common_ies, ies->common_ie_len, SCAN_OFFLOAD_PROBE_REQ_SIZE); cmd->len = cpu_to_le16(cmd_len); } @@ -735,7 +744,7 @@ static void iwl_build_channel_cfg(struct iwl_mvm *mvm, int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies) + struct ieee80211_scan_ies *ies) { int band_2ghz = mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels; int band_5ghz = mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels; diff --git a/drivers/net/wireless/ti/wl12xx/scan.c b/drivers/net/wireless/ti/wl12xx/scan.c index 7541bd1a4a4b..0c0d5cd98514 100644 --- a/drivers/net/wireless/ti/wl12xx/scan.c +++ b/drivers/net/wireless/ti/wl12xx/scan.c @@ -156,7 +156,7 @@ static int wl1271_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif, cmd->params.role_id, band, wl->scan.ssid, wl->scan.ssid_len, wl->scan.req->ie, - wl->scan.req->ie_len, false); + wl->scan.req->ie_len, NULL, 0, false); if (ret < 0) { wl1271_error("PROBE request template failed"); goto out; @@ -317,7 +317,7 @@ static void wl12xx_adjust_channels(struct wl1271_cmd_sched_scan_config *cmd, int wl1271_scan_sched_scan_config(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies) + struct ieee80211_scan_ies *ies) { struct wl1271_cmd_sched_scan_config *cfg = NULL; struct wlcore_scan_channels *cfg_channels = NULL; @@ -378,8 +378,11 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl, wlvif->role_id, band, req->ssids[0].ssid, req->ssids[0].ssid_len, - ies->ie[band], - ies->len[band], true); + ies->ies[band], + ies->len[band], + ies->common_ies, + ies->common_ie_len, + true); if (ret < 0) { wl1271_error("2.4GHz PROBE request template failed"); goto out; @@ -392,8 +395,11 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl, wlvif->role_id, band, req->ssids[0].ssid, req->ssids[0].ssid_len, - ies->ie[band], - ies->len[band], true); + ies->ies[band], + ies->len[band], + ies->common_ies, + ies->common_ie_len, + true); if (ret < 0) { wl1271_error("5GHz PROBE request template failed"); goto out; @@ -449,7 +455,7 @@ out_free: int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies) + struct ieee80211_scan_ies *ies) { int ret; diff --git a/drivers/net/wireless/ti/wl12xx/scan.h b/drivers/net/wireless/ti/wl12xx/scan.h index 264af7ac2785..427f9af85a00 100644 --- a/drivers/net/wireless/ti/wl12xx/scan.h +++ b/drivers/net/wireless/ti/wl12xx/scan.h @@ -135,6 +135,6 @@ int wl12xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif); void wl12xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies); + struct ieee80211_scan_ies *ies); void wl12xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif); #endif diff --git a/drivers/net/wireless/ti/wl18xx/scan.c b/drivers/net/wireless/ti/wl18xx/scan.c index 2b642f8c9266..98666f235a12 100644 --- a/drivers/net/wireless/ti/wl18xx/scan.c +++ b/drivers/net/wireless/ti/wl18xx/scan.c @@ -113,6 +113,8 @@ static int wl18xx_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif, req->ssids ? req->ssids[0].ssid_len : 0, req->ie, req->ie_len, + NULL, + 0, false); if (ret < 0) { wl1271_error("2.4GHz PROBE request template failed"); @@ -128,6 +130,8 @@ static int wl18xx_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif, req->ssids ? req->ssids[0].ssid_len : 0, req->ie, req->ie_len, + NULL, + 0, false); if (ret < 0) { wl1271_error("5GHz PROBE request template failed"); @@ -161,7 +165,7 @@ static int wl18xx_scan_sched_scan_config(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies) + struct ieee80211_scan_ies *ies) { struct wl18xx_cmd_scan_params *cmd; struct wlcore_scan_channels *cmd_channels = NULL; @@ -237,8 +241,10 @@ int wl18xx_scan_sched_scan_config(struct wl1271 *wl, cmd->role_id, band, req->ssids ? req->ssids[0].ssid : NULL, req->ssids ? req->ssids[0].ssid_len : 0, - ies->ie[band], + ies->ies[band], ies->len[band], + ies->common_ies, + ies->common_ie_len, true); if (ret < 0) { wl1271_error("2.4GHz PROBE request template failed"); @@ -252,8 +258,10 @@ int wl18xx_scan_sched_scan_config(struct wl1271 *wl, cmd->role_id, band, req->ssids ? req->ssids[0].ssid : NULL, req->ssids ? req->ssids[0].ssid_len : 0, - ies->ie[band], + ies->ies[band], ies->len[band], + ies->common_ies, + ies->common_ie_len, true); if (ret < 0) { wl1271_error("5GHz PROBE request template failed"); @@ -277,7 +285,7 @@ out: int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies) + struct ieee80211_scan_ies *ies) { return wl18xx_scan_sched_scan_config(wl, wlvif, req, ies); } diff --git a/drivers/net/wireless/ti/wl18xx/scan.h b/drivers/net/wireless/ti/wl18xx/scan.h index eadee42689d1..2e636aa5dba9 100644 --- a/drivers/net/wireless/ti/wl18xx/scan.h +++ b/drivers/net/wireless/ti/wl18xx/scan.h @@ -122,6 +122,6 @@ int wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif); void wl18xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies); + struct ieee80211_scan_ies *ies); void wl18xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif); #endif diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 40dc30f4faaa..e269c0a57017 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -1124,7 +1124,8 @@ out: int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id, u8 band, const u8 *ssid, size_t ssid_len, - const u8 *ie, size_t ie_len, bool sched_scan) + const u8 *ie0, size_t ie0_len, const u8 *ie1, + size_t ie1_len, bool sched_scan) { struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); struct sk_buff *skb; @@ -1136,13 +1137,15 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, wl1271_debug(DEBUG_SCAN, "build probe request band %d", band); skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len, - ie_len); + ie0_len + ie1_len); if (!skb) { ret = -ENOMEM; goto out; } - if (ie_len) - memcpy(skb_put(skb, ie_len), ie, ie_len); + if (ie0_len) + memcpy(skb_put(skb, ie0_len), ie0, ie0_len); + if (ie1_len) + memcpy(skb_put(skb, ie1_len), ie1, ie1_len); if (sched_scan && (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL)) { diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h index b084830a61cf..6788d7356ca5 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.h +++ b/drivers/net/wireless/ti/wlcore/cmd.h @@ -64,7 +64,8 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, struct wl12xx_vif *wlvif, int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id, u8 band, const u8 *ssid, size_t ssid_len, - const u8 *ie, size_t ie_len, bool sched_scan); + const u8 *ie, size_t ie_len, const u8 *common_ie, + size_t common_ie_len, bool sched_scan); struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct sk_buff *skb); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index e5ffb8b91dd4..48f83868f9cb 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -3637,7 +3637,7 @@ out: static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies) + struct ieee80211_scan_ies *ies) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); diff --git a/drivers/net/wireless/ti/wlcore/scan.h b/drivers/net/wireless/ti/wlcore/scan.h index a6ab24b5c0f9..4dadd0c62cde 100644 --- a/drivers/net/wireless/ti/wlcore/scan.h +++ b/drivers/net/wireless/ti/wlcore/scan.h @@ -37,7 +37,7 @@ void wl1271_scan_complete_work(struct work_struct *work); int wl1271_scan_sched_scan_config(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies); + struct ieee80211_scan_ies *ies); int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif); void wlcore_scan_sched_scan_results(struct wl1271 *wl); diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 95a54504f0cc..71320509b56d 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -95,7 +95,7 @@ struct wlcore_ops { int (*scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif); int (*sched_scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies); + struct ieee80211_scan_ies *ies); void (*sched_scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif); int (*get_spare_blocks)(struct wl1271 *wl, bool is_gem); int (*set_key)(struct wl1271 *wl, enum set_key_cmd cmd, diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 9536ee3e22a9..545d2fa179c4 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -753,27 +753,12 @@ struct ieee80211_tx_info { }; }; -/** - * struct ieee80211_sched_scan_ies - scheduled scan IEs - * - * This structure is used to pass the appropriate IEs to be used in scheduled - * scans for all bands. It contains both the IEs passed from the userspace - * and the ones generated by mac80211. - * - * @ie: array with the IEs for each supported band - * @len: array with the total length of the IEs for each band - */ -struct ieee80211_sched_scan_ies { - u8 *ie[IEEE80211_NUM_BANDS]; - size_t len[IEEE80211_NUM_BANDS]; -}; - /** * struct ieee80211_scan_ies - descriptors for different blocks of IEs * - * This structure is used to point to different blocks of IEs in HW scan. - * These blocks contain the IEs passed by userspace and the ones generated - * by mac80211. + * This structure is used to point to different blocks of IEs in HW scan + * and scheduled scan. These blocks contain the IEs passed by userspace + * and the ones generated by mac80211. * * @ies: pointers to band specific IEs. * @len: lengths of band_specific IEs. @@ -2917,7 +2902,7 @@ struct ieee80211_ops { int (*sched_scan_start)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies); + struct ieee80211_scan_ies *ies); int (*sched_scan_stop)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); void (*sw_scan_start)(struct ieee80211_hw *hw); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index faa0d90f6e80..11423958116a 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -346,7 +346,7 @@ static inline int drv_sched_scan_start(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies) + struct ieee80211_scan_ies *ies) { int ret; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 116959e070d0..a0a938145dcc 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -1003,10 +1003,13 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, struct cfg80211_sched_scan_request *req) { struct ieee80211_local *local = sdata->local; - struct ieee80211_sched_scan_ies sched_scan_ies = {}; + struct ieee80211_scan_ies sched_scan_ies = {}; struct cfg80211_chan_def chandef; - int ret, i, iebufsz; - struct ieee80211_scan_ies dummy_ie_desc; + int ret, i, iebufsz, num_bands = 0; + u32 rate_masks[IEEE80211_NUM_BANDS] = {}; + u8 bands_used = 0; + u8 *ie; + size_t len; iebufsz = local->scan_ies_len + req->ie_len; @@ -1016,37 +1019,35 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, return -ENOTSUPP; for (i = 0; i < IEEE80211_NUM_BANDS; i++) { - u32 rate_masks[IEEE80211_NUM_BANDS] = {}; - - if (!local->hw.wiphy->bands[i]) - continue; - - sched_scan_ies.ie[i] = kzalloc(iebufsz, GFP_KERNEL); - if (!sched_scan_ies.ie[i]) { - ret = -ENOMEM; - goto out_free; + if (local->hw.wiphy->bands[i]) { + bands_used |= BIT(i); + rate_masks[i] = (u32) -1; + num_bands++; } - - ieee80211_prepare_scan_chandef(&chandef, req->scan_width); - rate_masks[i] = (u32) -1; - - sched_scan_ies.len[i] = - ieee80211_build_preq_ies(local, sched_scan_ies.ie[i], - iebufsz, &dummy_ie_desc, - req->ie, req->ie_len, BIT(i), - rate_masks, &chandef); } + ie = kzalloc(num_bands * iebufsz, GFP_KERNEL); + if (!ie) { + ret = -ENOMEM; + goto out; + } + + ieee80211_prepare_scan_chandef(&chandef, req->scan_width); + + len = ieee80211_build_preq_ies(local, ie, num_bands * iebufsz, + &sched_scan_ies, req->ie, + req->ie_len, bands_used, + rate_masks, &chandef); + ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies); if (ret == 0) { rcu_assign_pointer(local->sched_scan_sdata, sdata); local->sched_scan_req = req; } -out_free: - while (i > 0) - kfree(sched_scan_ies.ie[--i]); + kfree(ie); +out: if (ret) { /* Clean in case of failure after HW restart or upon resume. */ RCU_INIT_POINTER(local->sched_scan_sdata, NULL); From 5bcae31d9cb1ebfad3ad5a3eea04c8cdc329a04f Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 25 Jun 2014 12:35:06 +0200 Subject: [PATCH 168/274] mac80211: implement multi-vif in-place reservations Multi-vif in-place reservations happen when it is impossible to allocate more channel contexts as indicated by interface combinations. Such reservations are not finalized until all assigned interfaces are ready. This still doesn't handle all possible cases (i.e. degradation of number of channels) properly. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- include/net/mac80211.h | 8 +- net/mac80211/chan.c | 767 +++++++++++++++++++++++++++++++++---- net/mac80211/ieee80211_i.h | 26 +- net/mac80211/util.c | 9 +- 4 files changed, 717 insertions(+), 93 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 545d2fa179c4..9ce5cb17ed82 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1606,12 +1606,6 @@ struct ieee80211_tx_control { * is not enabled the default action is to disconnect when getting the * CSA frame. * - * @IEEE80211_HW_CHANGE_RUNNING_CHANCTX: The hardware can change a - * channel context on-the-fly. This is needed for channel switch - * on single-channel hardware. It can also be used as an - * optimization in certain channel switch cases with - * multi-channel. - * * @IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS: The HW supports scanning on all bands * in one command, mac80211 doesn't have to run separate scans per band. */ @@ -1645,7 +1639,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_TIMING_BEACON_ONLY = 1<<26, IEEE80211_HW_SUPPORTS_HT_CCK_RATES = 1<<27, IEEE80211_HW_CHANCTX_STA_CSA = 1<<28, - IEEE80211_HW_CHANGE_RUNNING_CHANCTX = 1<<29, + /* bit 29 unused */ IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS = 1<<30, }; diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index a310e33972de..0e4302bb5b34 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -63,6 +63,20 @@ static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local) return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local); } +static struct ieee80211_chanctx * +ieee80211_vif_get_chanctx(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx_conf *conf; + + conf = rcu_dereference_protected(sdata->vif.chanctx_conf, + lockdep_is_held(&local->chanctx_mtx)); + if (!conf) + return NULL; + + return container_of(conf, struct ieee80211_chanctx, conf); +} + static const struct cfg80211_chan_def * ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local, struct ieee80211_chanctx *ctx, @@ -160,6 +174,9 @@ ieee80211_find_reservation_chanctx(struct ieee80211_local *local, return NULL; list_for_each_entry(ctx, &local->chanctx_list, list) { + if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) + continue; + if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) continue; @@ -347,6 +364,9 @@ ieee80211_find_chanctx(struct ieee80211_local *local, list_for_each_entry(ctx, &local->chanctx_list, list) { const struct cfg80211_chan_def *compat; + if (ctx->replace_state != IEEE80211_CHANCTX_REPLACE_NONE) + continue; + if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) continue; @@ -622,6 +642,7 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) struct ieee80211_local *local = sdata->local; struct ieee80211_chanctx_conf *conf; struct ieee80211_chanctx *ctx; + bool use_reserved_switch = false; lockdep_assert_held(&local->chanctx_mtx); @@ -632,12 +653,23 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) ctx = container_of(conf, struct ieee80211_chanctx, conf); - if (sdata->reserved_chanctx) + if (sdata->reserved_chanctx) { + if (sdata->reserved_chanctx->replace_state == + IEEE80211_CHANCTX_REPLACES_OTHER && + ieee80211_chanctx_num_reserved(local, + sdata->reserved_chanctx) > 1) + use_reserved_switch = true; + ieee80211_vif_unreserve_chanctx(sdata); + } ieee80211_assign_vif_chanctx(sdata, NULL); if (ieee80211_chanctx_refcount(local, ctx) == 0) ieee80211_free_chanctx(local, ctx); + + /* Unreserving may ready an in-place reservation. */ + if (use_reserved_switch) + ieee80211_vif_use_reserved_switch(local); } void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, @@ -905,8 +937,25 @@ int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata) list_del(&sdata->reserved_chanctx_list); sdata->reserved_chanctx = NULL; - if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) - ieee80211_free_chanctx(sdata->local, ctx); + if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) { + if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) { + if (WARN_ON(!ctx->replace_ctx)) + return -EINVAL; + + WARN_ON(ctx->replace_ctx->replace_state != + IEEE80211_CHANCTX_WILL_BE_REPLACED); + WARN_ON(ctx->replace_ctx->replace_ctx != ctx); + + ctx->replace_ctx->replace_ctx = NULL; + ctx->replace_ctx->replace_state = + IEEE80211_CHANCTX_REPLACE_NONE; + + list_del_rcu(&ctx->list); + kfree_rcu(ctx, rcu_head); + } else { + ieee80211_free_chanctx(sdata->local, ctx); + } + } return 0; } @@ -917,40 +966,84 @@ int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata, bool radar_required) { struct ieee80211_local *local = sdata->local; - struct ieee80211_chanctx_conf *conf; - struct ieee80211_chanctx *new_ctx, *curr_ctx; - int ret = 0; + struct ieee80211_chanctx *new_ctx, *curr_ctx, *ctx; - mutex_lock(&local->chanctx_mtx); + lockdep_assert_held(&local->chanctx_mtx); - conf = rcu_dereference_protected(sdata->vif.chanctx_conf, - lockdep_is_held(&local->chanctx_mtx)); - if (!conf) { - ret = -EINVAL; - goto out; - } - - curr_ctx = container_of(conf, struct ieee80211_chanctx, conf); + curr_ctx = ieee80211_vif_get_chanctx(sdata); + if (curr_ctx && local->use_chanctx && !local->ops->switch_vif_chanctx) + return -ENOTSUPP; new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode); if (!new_ctx) { - if (ieee80211_chanctx_refcount(local, curr_ctx) == 1 && - (local->hw.flags & IEEE80211_HW_CHANGE_RUNNING_CHANCTX)) { - /* if we're the only users of the chanctx and - * the driver supports changing a running - * context, reserve our current context - */ - new_ctx = curr_ctx; - } else if (ieee80211_can_create_new_chanctx(local)) { - /* create a new context and reserve it */ + if (ieee80211_can_create_new_chanctx(local)) { new_ctx = ieee80211_new_chanctx(local, chandef, mode); - if (IS_ERR(new_ctx)) { - ret = PTR_ERR(new_ctx); - goto out; - } + if (IS_ERR(new_ctx)) + return PTR_ERR(new_ctx); } else { - ret = -EBUSY; - goto out; + if (!curr_ctx || + (curr_ctx->replace_state == + IEEE80211_CHANCTX_WILL_BE_REPLACED) || + !list_empty(&curr_ctx->reserved_vifs)) { + /* + * Another vif already requested this context + * for a reservation. Find another one hoping + * all vifs assigned to it will also switch + * soon enough. + * + * TODO: This needs a little more work as some + * cases (more than 2 chanctx capable devices) + * may fail which could otherwise succeed + * provided some channel context juggling was + * performed. + * + * Consider ctx1..3, vif1..6, each ctx has 2 + * vifs. vif1 and vif2 from ctx1 request new + * different chandefs starting 2 in-place + * reserations with ctx4 and ctx5 replacing + * ctx1 and ctx2 respectively. Next vif5 and + * vif6 from ctx3 reserve ctx4. If vif3 and + * vif4 remain on ctx2 as they are then this + * fails unless `replace_ctx` from ctx5 is + * replaced with ctx3. + */ + list_for_each_entry(ctx, &local->chanctx_list, + list) { + if (ctx->replace_state != + IEEE80211_CHANCTX_REPLACE_NONE) + continue; + + if (!list_empty(&ctx->reserved_vifs)) + continue; + + curr_ctx = ctx; + break; + } + } + + /* + * If that's true then all available contexts already + * have reservations and cannot be used. + */ + if (!curr_ctx || + (curr_ctx->replace_state == + IEEE80211_CHANCTX_WILL_BE_REPLACED) || + !list_empty(&curr_ctx->reserved_vifs)) + return -EBUSY; + + new_ctx = ieee80211_alloc_chanctx(local, chandef, mode); + if (!new_ctx) + return -ENOMEM; + + new_ctx->replace_ctx = curr_ctx; + new_ctx->replace_state = + IEEE80211_CHANCTX_REPLACES_OTHER; + + curr_ctx->replace_ctx = new_ctx; + curr_ctx->replace_state = + IEEE80211_CHANCTX_WILL_BE_REPLACED; + + list_add_rcu(&new_ctx->list, &local->chanctx_list); } } @@ -958,82 +1051,567 @@ int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata, sdata->reserved_chanctx = new_ctx; sdata->reserved_chandef = *chandef; sdata->reserved_radar_required = radar_required; -out: - mutex_unlock(&local->chanctx_mtx); - return ret; + sdata->reserved_ready = false; + + return 0; } -int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata, - u32 *changed) +static int +ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; - struct ieee80211_chanctx *ctx; - struct ieee80211_chanctx *old_ctx; - struct ieee80211_chanctx_conf *conf; - int ret; - u32 tmp_changed = *changed; - - /* TODO: need to recheck if the chandef is usable etc.? */ + struct ieee80211_vif_chanctx_switch vif_chsw[1] = {}; + struct ieee80211_chanctx *old_ctx, *new_ctx; + const struct cfg80211_chan_def *chandef; + u32 changed = 0; + int err; lockdep_assert_held(&local->mtx); + lockdep_assert_held(&local->chanctx_mtx); - mutex_lock(&local->chanctx_mtx); + new_ctx = sdata->reserved_chanctx; + old_ctx = ieee80211_vif_get_chanctx(sdata); - ctx = sdata->reserved_chanctx; - if (WARN_ON(!ctx)) { - ret = -EINVAL; - goto out; + if (WARN_ON(!sdata->reserved_ready)) + return -EBUSY; + + if (WARN_ON(!new_ctx)) + return -EINVAL; + + if (WARN_ON(!old_ctx)) + return -EINVAL; + + if (WARN_ON(new_ctx->replace_state == + IEEE80211_CHANCTX_REPLACES_OTHER)) + return -EINVAL; + + chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, + &sdata->reserved_chandef); + if (WARN_ON(!chandef)) + return -EINVAL; + + vif_chsw[0].vif = &sdata->vif; + vif_chsw[0].old_ctx = &old_ctx->conf; + vif_chsw[0].new_ctx = &new_ctx->conf; + + list_del(&sdata->reserved_chanctx_list); + sdata->reserved_chanctx = NULL; + + err = drv_switch_vif_chanctx(local, vif_chsw, 1, + CHANCTX_SWMODE_REASSIGN_VIF); + if (err) { + if (ieee80211_chanctx_refcount(local, new_ctx) == 0) + ieee80211_free_chanctx(local, new_ctx); + + return err; } - conf = rcu_dereference_protected(sdata->vif.chanctx_conf, - lockdep_is_held(&local->chanctx_mtx)); - if (!conf) { - ret = -EINVAL; - goto out; - } + list_move(&sdata->assigned_chanctx_list, &new_ctx->assigned_vifs); + rcu_assign_pointer(sdata->vif.chanctx_conf, &new_ctx->conf); - old_ctx = container_of(conf, struct ieee80211_chanctx, conf); + if (sdata->vif.type == NL80211_IFTYPE_AP) + __ieee80211_vif_copy_chanctx_to_vlans(sdata, false); + + if (ieee80211_chanctx_refcount(local, old_ctx) == 0) + ieee80211_free_chanctx(local, old_ctx); if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width) - tmp_changed |= BSS_CHANGED_BANDWIDTH; + changed = BSS_CHANGED_BANDWIDTH; sdata->vif.bss_conf.chandef = sdata->reserved_chandef; - /* unref our reservation */ - sdata->reserved_chanctx = NULL; - sdata->radar_required = sdata->reserved_radar_required; - list_del(&sdata->reserved_chanctx_list); + if (changed) + ieee80211_bss_info_change_notify(sdata, changed); - if (old_ctx == ctx) { - /* This is our own context, just change it */ - ret = __ieee80211_vif_change_channel(sdata, old_ctx, - &tmp_changed); - if (ret) - goto out; - } else { - ret = ieee80211_assign_vif_chanctx(sdata, ctx); - if (ieee80211_chanctx_refcount(local, old_ctx) == 0) - ieee80211_free_chanctx(local, old_ctx); - if (ret) { - /* if assign fails refcount stays the same */ - if (ieee80211_chanctx_refcount(local, ctx) == 0) - ieee80211_free_chanctx(local, ctx); + return err; +} + +static int +ieee80211_vif_use_reserved_assign(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx *old_ctx, *new_ctx; + const struct cfg80211_chan_def *chandef; + int err; + + old_ctx = ieee80211_vif_get_chanctx(sdata); + new_ctx = sdata->reserved_chanctx; + + if (WARN_ON(!sdata->reserved_ready)) + return -EINVAL; + + if (WARN_ON(old_ctx)) + return -EINVAL; + + if (WARN_ON(!new_ctx)) + return -EINVAL; + + if (WARN_ON(new_ctx->replace_state == + IEEE80211_CHANCTX_REPLACES_OTHER)) + return -EINVAL; + + chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, + &sdata->reserved_chandef); + if (WARN_ON(!chandef)) + return -EINVAL; + + list_del(&sdata->reserved_chanctx_list); + sdata->reserved_chanctx = NULL; + + err = ieee80211_assign_vif_chanctx(sdata, new_ctx); + if (err) { + if (ieee80211_chanctx_refcount(local, new_ctx) == 0) + ieee80211_free_chanctx(local, new_ctx); + + goto out; + } + +out: + return err; +} + +static bool +ieee80211_vif_has_in_place_reservation(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_chanctx *old_ctx, *new_ctx; + + lockdep_assert_held(&sdata->local->chanctx_mtx); + + new_ctx = sdata->reserved_chanctx; + old_ctx = ieee80211_vif_get_chanctx(sdata); + + if (!old_ctx) + return false; + + if (WARN_ON(!new_ctx)) + return false; + + if (old_ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED) + return false; + + if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) + return false; + + return true; +} + +static int ieee80211_chsw_switch_hwconf(struct ieee80211_local *local, + struct ieee80211_chanctx *new_ctx) +{ + const struct cfg80211_chan_def *chandef; + + lockdep_assert_held(&local->mtx); + lockdep_assert_held(&local->chanctx_mtx); + + chandef = ieee80211_chanctx_reserved_chandef(local, new_ctx, NULL); + if (WARN_ON(!chandef)) + return -EINVAL; + + local->hw.conf.radar_enabled = new_ctx->conf.radar_enabled; + local->_oper_chandef = *chandef; + ieee80211_hw_config(local, 0); + + return 0; +} + +static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local, + int n_vifs) +{ + struct ieee80211_vif_chanctx_switch *vif_chsw; + struct ieee80211_sub_if_data *sdata; + struct ieee80211_chanctx *ctx, *old_ctx; + int i, err; + + lockdep_assert_held(&local->mtx); + lockdep_assert_held(&local->chanctx_mtx); + + vif_chsw = kzalloc(sizeof(vif_chsw[0]) * n_vifs, GFP_KERNEL); + if (!vif_chsw) + return -ENOMEM; + + i = 0; + list_for_each_entry(ctx, &local->chanctx_list, list) { + if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) + continue; + + if (WARN_ON(!ctx->replace_ctx)) { + err = -EINVAL; goto out; } - if (sdata->vif.type == NL80211_IFTYPE_AP) - __ieee80211_vif_copy_chanctx_to_vlans(sdata, false); + list_for_each_entry(sdata, &ctx->reserved_vifs, + reserved_chanctx_list) { + if (!ieee80211_vif_has_in_place_reservation( + sdata)) + continue; + + old_ctx = ieee80211_vif_get_chanctx(sdata); + vif_chsw[i].vif = &sdata->vif; + vif_chsw[i].old_ctx = &old_ctx->conf; + vif_chsw[i].new_ctx = &ctx->conf; + + i++; + } } - *changed = tmp_changed; + err = drv_switch_vif_chanctx(local, vif_chsw, n_vifs, + CHANCTX_SWMODE_SWAP_CONTEXTS); - ieee80211_recalc_chanctx_chantype(local, ctx); - ieee80211_recalc_smps_chanctx(local, ctx); - ieee80211_recalc_radar_chanctx(local, ctx); - ieee80211_recalc_chanctx_min_def(local, ctx); out: - mutex_unlock(&local->chanctx_mtx); - return ret; + kfree(vif_chsw); + return err; +} + +static int ieee80211_chsw_switch_ctxs(struct ieee80211_local *local) +{ + struct ieee80211_chanctx *ctx; + int err; + + lockdep_assert_held(&local->mtx); + lockdep_assert_held(&local->chanctx_mtx); + + list_for_each_entry(ctx, &local->chanctx_list, list) { + if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) + continue; + + if (!list_empty(&ctx->replace_ctx->assigned_vifs)) + continue; + + ieee80211_del_chanctx(local, ctx->replace_ctx); + err = ieee80211_add_chanctx(local, ctx); + if (err) + goto err; + } + + return 0; + +err: + WARN_ON(ieee80211_add_chanctx(local, ctx)); + list_for_each_entry_continue_reverse(ctx, &local->chanctx_list, list) { + if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) + continue; + + if (!list_empty(&ctx->replace_ctx->assigned_vifs)) + continue; + + ieee80211_del_chanctx(local, ctx); + WARN_ON(ieee80211_add_chanctx(local, ctx->replace_ctx)); + } + + return err; +} + +int +ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) +{ + struct ieee80211_sub_if_data *sdata, *sdata_tmp; + struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx; + struct ieee80211_chanctx *new_ctx = NULL; + int i, err, n_assigned, n_reserved, n_ready; + int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0; + + lockdep_assert_held(&local->mtx); + lockdep_assert_held(&local->chanctx_mtx); + + /* + * If there are 2 independent pairs of channel contexts performing + * cross-switch of their vifs this code will still wait until both are + * ready even though it could be possible to switch one before the + * other is ready. + * + * For practical reasons and code simplicity just do a single huge + * switch. + */ + + /* + * Verify if the reservation is still feasible. + * - if it's not then disconnect + * - if it is but not all vifs necessary are ready then defer + */ + + list_for_each_entry(ctx, &local->chanctx_list, list) { + if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) + continue; + + if (WARN_ON(!ctx->replace_ctx)) { + err = -EINVAL; + goto err; + } + + if (!local->use_chanctx) + new_ctx = ctx; + + n_ctx++; + + n_assigned = 0; + n_reserved = 0; + n_ready = 0; + + list_for_each_entry(sdata, &ctx->replace_ctx->assigned_vifs, + assigned_chanctx_list) { + n_assigned++; + if (sdata->reserved_chanctx) { + n_reserved++; + if (sdata->reserved_ready) + n_ready++; + } + } + + if (n_assigned != n_reserved) { + if (n_ready == n_reserved) { + wiphy_info(local->hw.wiphy, + "channel context reservation cannot be finalized because some interfaces aren't switching\n"); + err = -EBUSY; + goto err; + } + + return -EAGAIN; + } + + ctx->conf.radar_enabled = false; + list_for_each_entry(sdata, &ctx->reserved_vifs, + reserved_chanctx_list) { + if (ieee80211_vif_has_in_place_reservation(sdata) && + !sdata->reserved_ready) + return -EAGAIN; + + old_ctx = ieee80211_vif_get_chanctx(sdata); + if (old_ctx) { + if (old_ctx->replace_state == + IEEE80211_CHANCTX_WILL_BE_REPLACED) + n_vifs_switch++; + else + n_vifs_assign++; + } else { + n_vifs_ctxless++; + } + + if (sdata->reserved_radar_required) + ctx->conf.radar_enabled = true; + } + } + + if (WARN_ON(n_ctx == 0) || + WARN_ON(n_vifs_switch == 0 && + n_vifs_assign == 0 && + n_vifs_ctxless == 0) || + WARN_ON(n_ctx > 1 && !local->use_chanctx) || + WARN_ON(!new_ctx && !local->use_chanctx)) { + err = -EINVAL; + goto err; + } + + /* + * All necessary vifs are ready. Perform the switch now depending on + * reservations and driver capabilities. + */ + + if (local->use_chanctx) { + if (n_vifs_switch > 0) { + err = ieee80211_chsw_switch_vifs(local, n_vifs_switch); + if (err) + goto err; + } + + if (n_vifs_assign > 0 || n_vifs_ctxless > 0) { + err = ieee80211_chsw_switch_ctxs(local); + if (err) + goto err; + } + } else { + err = ieee80211_chsw_switch_hwconf(local, new_ctx); + if (err) + goto err; + } + + /* + * Update all structures, values and pointers to point to new channel + * context(s). + */ + + i = 0; + list_for_each_entry(ctx, &local->chanctx_list, list) { + if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) + continue; + + if (WARN_ON(!ctx->replace_ctx)) { + err = -EINVAL; + goto err; + } + + list_for_each_entry(sdata, &ctx->reserved_vifs, + reserved_chanctx_list) { + u32 changed = 0; + + if (!ieee80211_vif_has_in_place_reservation(sdata)) + continue; + + rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf); + + if (sdata->vif.type == NL80211_IFTYPE_AP) + __ieee80211_vif_copy_chanctx_to_vlans(sdata, + false); + + sdata->radar_required = sdata->reserved_radar_required; + + if (sdata->vif.bss_conf.chandef.width != + sdata->reserved_chandef.width) + changed = BSS_CHANGED_BANDWIDTH; + + sdata->vif.bss_conf.chandef = sdata->reserved_chandef; + if (changed) + ieee80211_bss_info_change_notify(sdata, + changed); + + ieee80211_recalc_txpower(sdata); + } + + ieee80211_recalc_chanctx_chantype(local, ctx); + ieee80211_recalc_smps_chanctx(local, ctx); + ieee80211_recalc_radar_chanctx(local, ctx); + ieee80211_recalc_chanctx_min_def(local, ctx); + + list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, + reserved_chanctx_list) { + if (ieee80211_vif_get_chanctx(sdata) != ctx) + continue; + + list_del(&sdata->reserved_chanctx_list); + list_move(&sdata->assigned_chanctx_list, + &new_ctx->assigned_vifs); + sdata->reserved_chanctx = NULL; + } + + /* + * This context might have been a dependency for an already + * ready re-assign reservation interface that was deferred. Do + * not propagate error to the caller though. The in-place + * reservation for originally requested interface has already + * succeeded at this point. + */ + list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, + reserved_chanctx_list) { + if (WARN_ON(ieee80211_vif_has_in_place_reservation( + sdata))) + continue; + + if (WARN_ON(sdata->reserved_chanctx != ctx)) + continue; + + if (!sdata->reserved_ready) + continue; + + if (ieee80211_vif_get_chanctx(sdata)) + err = ieee80211_vif_use_reserved_reassign( + sdata); + else + err = ieee80211_vif_use_reserved_assign(sdata); + + if (err) { + sdata_info(sdata, + "failed to finalize (re-)assign reservation (err=%d)\n", + err); + ieee80211_vif_unreserve_chanctx(sdata); + cfg80211_stop_iface(local->hw.wiphy, + &sdata->wdev, + GFP_KERNEL); + } + } + } + + /* + * Finally free old contexts + */ + + list_for_each_entry_safe(ctx, ctx_tmp, &local->chanctx_list, list) { + if (ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED) + continue; + + ctx->replace_ctx->replace_ctx = NULL; + ctx->replace_ctx->replace_state = + IEEE80211_CHANCTX_REPLACE_NONE; + + list_del_rcu(&ctx->list); + kfree_rcu(ctx, rcu_head); + } + + return 0; + +err: + list_for_each_entry(ctx, &local->chanctx_list, list) { + if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) + continue; + + list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, + reserved_chanctx_list) + ieee80211_vif_unreserve_chanctx(sdata); + } + + return err; +} + +int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx *new_ctx; + struct ieee80211_chanctx *old_ctx; + int err; + + lockdep_assert_held(&local->mtx); + lockdep_assert_held(&local->chanctx_mtx); + + new_ctx = sdata->reserved_chanctx; + old_ctx = ieee80211_vif_get_chanctx(sdata); + + if (WARN_ON(!new_ctx)) + return -EINVAL; + + if (WARN_ON(new_ctx->replace_state == + IEEE80211_CHANCTX_WILL_BE_REPLACED)) + return -EINVAL; + + if (WARN_ON(sdata->reserved_ready)) + return -EINVAL; + + sdata->reserved_ready = true; + + if (new_ctx->replace_state == IEEE80211_CHANCTX_REPLACE_NONE) { + if (old_ctx) + err = ieee80211_vif_use_reserved_reassign(sdata); + else + err = ieee80211_vif_use_reserved_assign(sdata); + + if (err) + return err; + } + + /* + * In-place reservation may need to be finalized now either if: + * a) sdata is taking part in the swapping itself and is the last one + * b) sdata has switched with a re-assign reservation to an existing + * context readying in-place switching of old_ctx + * + * In case of (b) do not propagate the error up because the requested + * sdata already switched successfully. Just spill an extra warning. + * The ieee80211_vif_use_reserved_switch() already stops all necessary + * interfaces upon failure. + */ + if ((old_ctx && + old_ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) || + new_ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) { + err = ieee80211_vif_use_reserved_switch(local); + if (err && err != -EAGAIN) { + if (new_ctx->replace_state == + IEEE80211_CHANCTX_REPLACES_OTHER) + return err; + + wiphy_info(local->hw.wiphy, + "depending in-place reservation failed (err=%d)\n", + err); + } + } + + return 0; } int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, @@ -1043,6 +1621,7 @@ int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct ieee80211_chanctx_conf *conf; struct ieee80211_chanctx *ctx; + const struct cfg80211_chan_def *compat; int ret; if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, @@ -1069,11 +1648,33 @@ int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, } ctx = container_of(conf, struct ieee80211_chanctx, conf); - if (!cfg80211_chandef_compatible(&conf->def, chandef)) { + + compat = cfg80211_chandef_compatible(&conf->def, chandef); + if (!compat) { ret = -EINVAL; goto out; } + switch (ctx->replace_state) { + case IEEE80211_CHANCTX_REPLACE_NONE: + if (!ieee80211_chanctx_reserved_chandef(local, ctx, compat)) { + ret = -EBUSY; + goto out; + } + break; + case IEEE80211_CHANCTX_WILL_BE_REPLACED: + /* TODO: Perhaps the bandwith change could be treated as a + * reservation itself? */ + ret = -EBUSY; + goto out; + case IEEE80211_CHANCTX_REPLACES_OTHER: + /* channel context that is going to replace another channel + * context doesn't really exist and shouldn't be assigned + * anywhere yet */ + WARN_ON(1); + break; + } + sdata->vif.bss_conf.chandef = *chandef; ieee80211_recalc_chanctx_chantype(local, ctx); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index f88bd1659cde..c26955f6c14d 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -701,6 +701,24 @@ enum ieee80211_chanctx_mode { IEEE80211_CHANCTX_EXCLUSIVE }; +/** + * enum ieee80211_chanctx_replace_state - channel context replacement state + * + * This is used for channel context in-place reservations that require channel + * context switch/swap. + * + * @IEEE80211_CHANCTX_REPLACE_NONE: no replacement is taking place + * @IEEE80211_CHANCTX_WILL_BE_REPLACED: this channel context will be replaced + * by a (not yet registered) channel context pointed by %replace_ctx. + * @IEEE80211_CHANCTX_REPLACES_OTHER: this (not yet registered) channel context + * replaces an existing channel context pointed to by %replace_ctx. + */ +enum ieee80211_chanctx_replace_state { + IEEE80211_CHANCTX_REPLACE_NONE, + IEEE80211_CHANCTX_WILL_BE_REPLACED, + IEEE80211_CHANCTX_REPLACES_OTHER, +}; + struct ieee80211_chanctx { struct list_head list; struct rcu_head rcu_head; @@ -708,6 +726,9 @@ struct ieee80211_chanctx { struct list_head assigned_vifs; struct list_head reserved_vifs; + enum ieee80211_chanctx_replace_state replace_state; + struct ieee80211_chanctx *replace_ctx; + enum ieee80211_chanctx_mode mode; bool driver_present; @@ -778,6 +799,7 @@ struct ieee80211_sub_if_data { struct ieee80211_chanctx *reserved_chanctx; struct cfg80211_chan_def reserved_chandef; bool reserved_radar_required; + bool reserved_ready; /* used to reconfigure hardware SM PS */ struct work_struct recalc_smps; @@ -1820,9 +1842,9 @@ ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata, enum ieee80211_chanctx_mode mode, bool radar_required); int __must_check -ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata, - u32 *changed); +ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata); int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata); +int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local); int __must_check ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/util.c b/net/mac80211/util.c index e31458201278..1b42aa16ec03 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1698,7 +1698,9 @@ int ieee80211_reconfig(struct ieee80211_local *local) if (local->use_chanctx) { mutex_lock(&local->chanctx_mtx); list_for_each_entry(ctx, &local->chanctx_list, list) - WARN_ON(drv_add_chanctx(local, ctx)); + if (ctx->replace_state != + IEEE80211_CHANCTX_REPLACES_OTHER) + WARN_ON(drv_add_chanctx(local, ctx)); mutex_unlock(&local->chanctx_mtx); list_for_each_entry(sdata, &local->interfaces, list) { @@ -2972,6 +2974,8 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, num[iftype] = 1; list_for_each_entry(ctx, &local->chanctx_list, list) { + if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) + continue; if (ctx->conf.radar_enabled) radar_detect |= BIT(ctx->conf.def.width); if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) { @@ -3030,6 +3034,9 @@ int ieee80211_max_num_channels(struct ieee80211_local *local) lockdep_assert_held(&local->chanctx_mtx); list_for_each_entry(ctx, &local->chanctx_list, list) { + if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) + continue; + num_different_channels++; if (ctx->conf.radar_enabled) From 71e6195ed2541d764fb0d7ef39cdb2236817116b Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 25 Jun 2014 12:35:07 +0200 Subject: [PATCH 169/274] mac80211: make check_combinations() aware of chanctx reservation The ieee80211_check_combinations() computes radar_detect accordingly depending on chanctx reservation status. This makes it possible to use the function for channel_switch validation. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- net/mac80211/util.c | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 1b42aa16ec03..ea79668c2e5f 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2933,6 +2933,35 @@ void ieee80211_recalc_dtim(struct ieee80211_local *local, ps->dtim_count = dtim_count; } +static u8 ieee80211_chanctx_radar_detect(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx) +{ + struct ieee80211_sub_if_data *sdata; + u8 radar_detect = 0; + + lockdep_assert_held(&local->chanctx_mtx); + + if (WARN_ON(ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)) + return 0; + + list_for_each_entry(sdata, &ctx->reserved_vifs, reserved_chanctx_list) + if (sdata->reserved_radar_required) + radar_detect |= BIT(sdata->reserved_chandef.width); + + /* + * An in-place reservation context should not have any assigned vifs + * until it replaces the other context. + */ + WARN_ON(ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER && + !list_empty(&ctx->assigned_vifs)); + + list_for_each_entry(sdata, &ctx->assigned_vifs, assigned_chanctx_list) + if (sdata->radar_required) + radar_detect |= BIT(sdata->vif.bss_conf.chandef.width); + + return radar_detect; +} + int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, const struct cfg80211_chan_def *chandef, enum ieee80211_chanctx_mode chanmode, @@ -2976,8 +3005,7 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, list_for_each_entry(ctx, &local->chanctx_list, list) { if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) continue; - if (ctx->conf.radar_enabled) - radar_detect |= BIT(ctx->conf.def.width); + radar_detect |= ieee80211_chanctx_radar_detect(local, ctx); if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) { num_different_channels++; continue; @@ -3039,8 +3067,7 @@ int ieee80211_max_num_channels(struct ieee80211_local *local) num_different_channels++; - if (ctx->conf.radar_enabled) - radar_detect |= BIT(ctx->conf.def.width); + radar_detect |= ieee80211_chanctx_radar_detect(local, ctx); } list_for_each_entry_rcu(sdata, &local->interfaces, list) From 03078de4f928ffcbe629a914dea8bdf66a9d6a48 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 25 Jun 2014 12:35:08 +0200 Subject: [PATCH 170/274] mac80211: use chanctx reservation for AP CSA Channel switch finalization is now 2-step. First step is when driver calls csa_finish(), the other is when reservation is actually finalized (which can be deferred for in-place reservation). It is now safe to call ieee80211_csa_finish() more than once. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 98 +++++++++++++++++++++++++++++---------------- net/mac80211/chan.c | 35 +++++++++++++++- 2 files changed, 96 insertions(+), 37 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 73c9e1003e25..927b4ea0128b 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2792,17 +2792,35 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) sdata_assert_lock(sdata); lockdep_assert_held(&local->mtx); + lockdep_assert_held(&local->chanctx_mtx); - sdata->radar_required = sdata->csa_radar_required; - err = ieee80211_vif_change_channel(sdata, &changed); - if (err < 0) - return err; + /* + * using reservation isn't immediate as it may be deferred until later + * with multi-vif. once reservation is complete it will re-schedule the + * work with no reserved_chanctx so verify chandef to check if it + * completed successfully + */ - if (!local->use_chanctx) { - local->_oper_chandef = sdata->csa_chandef; - ieee80211_hw_config(local, 0); + if (sdata->reserved_chanctx) { + /* + * with multi-vif csa driver may call ieee80211_csa_finish() + * many times while waiting for other interfaces to use their + * reservations + */ + if (sdata->reserved_ready) + return 0; + + err = ieee80211_vif_use_reserved_context(sdata); + if (err) + return err; + + return 0; } + if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef, + &sdata->csa_chandef)) + return -EINVAL; + sdata->vif.csa_active = false; err = ieee80211_set_after_csa_beacon(sdata, &changed); @@ -2839,6 +2857,7 @@ void ieee80211_csa_finalize_work(struct work_struct *work) sdata_lock(sdata); mutex_lock(&local->mtx); + mutex_lock(&local->chanctx_mtx); /* AP might have been stopped while waiting for the lock. */ if (!sdata->vif.csa_active) @@ -2850,6 +2869,7 @@ void ieee80211_csa_finalize_work(struct work_struct *work) ieee80211_csa_finalize(sdata); unlock: + mutex_unlock(&local->chanctx_mtx); mutex_unlock(&local->mtx); sdata_unlock(sdata); } @@ -2995,7 +3015,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_local *local = sdata->local; struct ieee80211_chanctx_conf *conf; struct ieee80211_chanctx *chanctx; - int err, num_chanctx, changed = 0; + int err, changed = 0; sdata_assert_lock(sdata); lockdep_assert_held(&local->mtx); @@ -3010,37 +3030,43 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, &sdata->vif.bss_conf.chandef)) return -EINVAL; - mutex_lock(&local->chanctx_mtx); - conf = rcu_dereference_protected(sdata->vif.chanctx_conf, - lockdep_is_held(&local->chanctx_mtx)); - if (!conf) { - mutex_unlock(&local->chanctx_mtx); - return -EBUSY; - } - - /* don't handle for multi-VIF cases */ - chanctx = container_of(conf, struct ieee80211_chanctx, conf); - if (ieee80211_chanctx_refcount(local, chanctx) > 1) { - mutex_unlock(&local->chanctx_mtx); - return -EBUSY; - } - num_chanctx = 0; - list_for_each_entry_rcu(chanctx, &local->chanctx_list, list) - num_chanctx++; - mutex_unlock(&local->chanctx_mtx); - - if (num_chanctx > 1) - return -EBUSY; - /* don't allow another channel switch if one is already active. */ if (sdata->vif.csa_active) return -EBUSY; - err = ieee80211_set_csa_beacon(sdata, params, &changed); - if (err) - return err; + mutex_lock(&local->chanctx_mtx); + conf = rcu_dereference_protected(sdata->vif.chanctx_conf, + lockdep_is_held(&local->chanctx_mtx)); + if (!conf) { + err = -EBUSY; + goto out; + } + + chanctx = container_of(conf, struct ieee80211_chanctx, conf); + if (!chanctx) { + err = -EBUSY; + goto out; + } + + err = ieee80211_vif_reserve_chanctx(sdata, ¶ms->chandef, + chanctx->mode, + params->radar_required); + if (err) + goto out; + + /* if reservation is invalid then this will fail */ + err = ieee80211_check_combinations(sdata, NULL, chanctx->mode, 0); + if (err) { + ieee80211_vif_unreserve_chanctx(sdata); + goto out; + } + + err = ieee80211_set_csa_beacon(sdata, params, &changed); + if (err) { + ieee80211_vif_unreserve_chanctx(sdata); + goto out; + } - sdata->csa_radar_required = params->radar_required; sdata->csa_chandef = params->chandef; sdata->csa_block_tx = params->block_tx; sdata->vif.csa_active = true; @@ -3057,7 +3083,9 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, ieee80211_csa_finalize(sdata); } - return 0; +out: + mutex_unlock(&local->chanctx_mtx); + return err; } int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 0e4302bb5b34..ea4db2f0db87 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -1056,6 +1056,30 @@ int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata, return 0; } +static void +ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata) +{ + switch (sdata->vif.type) { + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MESH_POINT: + ieee80211_queue_work(&sdata->local->hw, + &sdata->csa_finalize_work); + break; + case NL80211_IFTYPE_UNSPECIFIED: + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_WDS: + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_P2P_DEVICE: + case NUM_NL80211_IFTYPES: + WARN_ON(1); + break; + } +} + static int ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata) { @@ -1103,7 +1127,7 @@ ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata) if (ieee80211_chanctx_refcount(local, new_ctx) == 0) ieee80211_free_chanctx(local, new_ctx); - return err; + goto out; } list_move(&sdata->assigned_chanctx_list, &new_ctx->assigned_vifs); @@ -1123,6 +1147,8 @@ ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata) if (changed) ieee80211_bss_info_change_notify(sdata, changed); +out: + ieee80211_vif_chanctx_reservation_complete(sdata); return err; } @@ -1167,6 +1193,7 @@ ieee80211_vif_use_reserved_assign(struct ieee80211_sub_if_data *sdata) } out: + ieee80211_vif_chanctx_reservation_complete(sdata); return err; } @@ -1480,6 +1507,8 @@ ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) list_move(&sdata->assigned_chanctx_list, &new_ctx->assigned_vifs); sdata->reserved_chanctx = NULL; + + ieee80211_vif_chanctx_reservation_complete(sdata); } /* @@ -1543,8 +1572,10 @@ err: continue; list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, - reserved_chanctx_list) + reserved_chanctx_list) { ieee80211_vif_unreserve_chanctx(sdata); + ieee80211_vif_chanctx_reservation_complete(sdata); + } } return err; From 4c3ebc56d7561526524ec62c61aa3e2040b71f6e Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 25 Jun 2014 12:35:09 +0200 Subject: [PATCH 171/274] mac80211: use chanctx reservation for STA CSA Channel switch finalization is now 2-step. First step is when driver calls chswitch_done(), the other is when reservation is actually finalized (which be defered for in-place reservation). It is now safe to call ieee80211_chswitch_done() more than once. Also remove the ieee80211_vif_change_channel() because it is no longer used. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- net/mac80211/chan.c | 69 ++------------------------ net/mac80211/ieee80211_i.h | 5 -- net/mac80211/mlme.c | 99 +++++++++++++++++++++++++------------- 3 files changed, 69 insertions(+), 104 deletions(-) diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index ea4db2f0db87..c3fd4d275bf4 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -819,70 +819,6 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, return ret; } -static int __ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata, - struct ieee80211_chanctx *ctx, - u32 *changed) -{ - struct ieee80211_local *local = sdata->local; - const struct cfg80211_chan_def *chandef = &sdata->csa_chandef; - u32 chanctx_changed = 0; - - if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, - IEEE80211_CHAN_DISABLED)) - return -EINVAL; - - if (ieee80211_chanctx_refcount(local, ctx) != 1) - return -EINVAL; - - if (sdata->vif.bss_conf.chandef.width != chandef->width) { - chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH; - *changed |= BSS_CHANGED_BANDWIDTH; - } - - sdata->vif.bss_conf.chandef = *chandef; - ctx->conf.def = *chandef; - - chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL; - drv_change_chanctx(local, ctx, chanctx_changed); - - ieee80211_recalc_chanctx_chantype(local, ctx); - ieee80211_recalc_smps_chanctx(local, ctx); - ieee80211_recalc_radar_chanctx(local, ctx); - ieee80211_recalc_chanctx_min_def(local, ctx); - - return 0; -} - -int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata, - u32 *changed) -{ - struct ieee80211_local *local = sdata->local; - struct ieee80211_chanctx_conf *conf; - struct ieee80211_chanctx *ctx; - int ret; - - lockdep_assert_held(&local->mtx); - - /* should never be called if not performing a channel switch. */ - if (WARN_ON(!sdata->vif.csa_active)) - return -EINVAL; - - mutex_lock(&local->chanctx_mtx); - conf = rcu_dereference_protected(sdata->vif.chanctx_conf, - lockdep_is_held(&local->chanctx_mtx)); - if (!conf) { - ret = -EINVAL; - goto out; - } - - ctx = container_of(conf, struct ieee80211_chanctx, conf); - - ret = __ieee80211_vif_change_channel(sdata, ctx, changed); - out: - mutex_unlock(&local->chanctx_mtx); - return ret; -} - static void __ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, bool clear) @@ -1066,8 +1002,11 @@ ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata) ieee80211_queue_work(&sdata->local->hw, &sdata->csa_finalize_work); break; - case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_STATION: + ieee80211_queue_work(&sdata->local->hw, + &sdata->u.mgd.chswitch_work); + break; + case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MONITOR: diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index c26955f6c14d..9e025e1184cc 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -788,7 +788,6 @@ struct ieee80211_sub_if_data { struct mac80211_qos_map __rcu *qos_map; struct work_struct csa_finalize_work; - bool csa_radar_required; bool csa_block_tx; /* write-protected by sdata_lock and local->mtx */ struct cfg80211_chan_def csa_chandef; @@ -1850,10 +1849,6 @@ int __must_check ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, const struct cfg80211_chan_def *chandef, u32 *changed); -/* NOTE: only use ieee80211_vif_change_channel() for channel switch */ -int __must_check -ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata, - u32 *changed); void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata); void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata); void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index eccc8492a59c..931330bbe00c 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -940,52 +940,70 @@ static void ieee80211_chswitch_work(struct work_struct *work) container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work); struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - u32 changed = 0; int ret; if (!ieee80211_sdata_running(sdata)) return; sdata_lock(sdata); + mutex_lock(&local->mtx); + mutex_lock(&local->chanctx_mtx); + if (!ifmgd->associated) goto out; - mutex_lock(&local->mtx); - ret = ieee80211_vif_change_channel(sdata, &changed); - mutex_unlock(&local->mtx); - if (ret) { + if (!sdata->vif.csa_active) + goto out; + + /* + * using reservation isn't immediate as it may be deferred until later + * with multi-vif. once reservation is complete it will re-schedule the + * work with no reserved_chanctx so verify chandef to check if it + * completed successfully + */ + + if (sdata->reserved_chanctx) { + /* + * with multi-vif csa driver may call ieee80211_csa_finish() + * many times while waiting for other interfaces to use their + * reservations + */ + if (sdata->reserved_ready) + goto out; + + ret = ieee80211_vif_use_reserved_context(sdata); + if (ret) { + sdata_info(sdata, + "failed to use reserved channel context, disconnecting (err=%d)\n", + ret); + ieee80211_queue_work(&sdata->local->hw, + &ifmgd->csa_connection_drop_work); + goto out; + } + + goto out; + } + + if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef, + &sdata->csa_chandef)) { sdata_info(sdata, - "vif channel switch failed, disconnecting\n"); + "failed to finalize channel switch, disconnecting\n"); ieee80211_queue_work(&sdata->local->hw, &ifmgd->csa_connection_drop_work); goto out; } - if (!local->use_chanctx) { - local->_oper_chandef = sdata->csa_chandef; - /* Call "hw_config" only if doing sw channel switch. - * Otherwise update the channel directly - */ - if (!local->ops->channel_switch) - ieee80211_hw_config(local, 0); - else - local->hw.conf.chandef = local->_oper_chandef; - } - /* XXX: shouldn't really modify cfg80211-owned data! */ ifmgd->associated->channel = sdata->csa_chandef.chan; - ieee80211_bss_info_change_notify(sdata, changed); - - mutex_lock(&local->mtx); sdata->vif.csa_active = false; + /* XXX: wait for a beacon first? */ if (sdata->csa_block_tx) { ieee80211_wake_vif_queues(local, sdata, IEEE80211_QUEUE_STOP_REASON_CSA); sdata->csa_block_tx = false; } - mutex_unlock(&local->mtx); ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; @@ -993,6 +1011,8 @@ static void ieee80211_chswitch_work(struct work_struct *work) ieee80211_sta_reset_conn_monitor(sdata); out: + mutex_unlock(&local->chanctx_mtx); + mutex_unlock(&local->mtx); sdata_unlock(sdata); } @@ -1029,6 +1049,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct cfg80211_bss *cbss = ifmgd->associated; + struct ieee80211_chanctx_conf *conf; struct ieee80211_chanctx *chanctx; enum ieee80211_band current_band; struct ieee80211_csa_ie csa_ie; @@ -1072,7 +1093,22 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; + mutex_lock(&local->mtx); mutex_lock(&local->chanctx_mtx); + conf = rcu_dereference_protected(sdata->vif.chanctx_conf, + lockdep_is_held(&local->chanctx_mtx)); + if (!conf) { + sdata_info(sdata, + "no channel context assigned to vif?, disconnecting\n"); + ieee80211_queue_work(&local->hw, + &ifmgd->csa_connection_drop_work); + mutex_unlock(&local->chanctx_mtx); + mutex_unlock(&local->mtx); + return; + } + + chanctx = container_of(conf, struct ieee80211_chanctx, conf); + if (local->use_chanctx) { u32 num_chanctx = 0; list_for_each_entry(chanctx, &local->chanctx_list, list) @@ -1085,32 +1121,27 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, ieee80211_queue_work(&local->hw, &ifmgd->csa_connection_drop_work); mutex_unlock(&local->chanctx_mtx); + mutex_unlock(&local->mtx); return; } } - if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) { - ieee80211_queue_work(&local->hw, - &ifmgd->csa_connection_drop_work); - mutex_unlock(&local->chanctx_mtx); - return; - } - chanctx = container_of(rcu_access_pointer(sdata->vif.chanctx_conf), - struct ieee80211_chanctx, conf); - if (ieee80211_chanctx_refcount(local, chanctx) > 1) { + res = ieee80211_vif_reserve_chanctx(sdata, &csa_ie.chandef, + chanctx->mode, false); + if (res) { sdata_info(sdata, - "channel switch with multiple interfaces on the same channel, disconnecting\n"); + "failed to reserve channel context for channel switch, disconnecting (err=%d)\n", + res); ieee80211_queue_work(&local->hw, &ifmgd->csa_connection_drop_work); mutex_unlock(&local->chanctx_mtx); + mutex_unlock(&local->mtx); return; } mutex_unlock(&local->chanctx_mtx); - sdata->csa_chandef = csa_ie.chandef; - - mutex_lock(&local->mtx); sdata->vif.csa_active = true; + sdata->csa_chandef = csa_ie.chandef; sdata->csa_block_tx = csa_ie.mode; if (sdata->csa_block_tx) From 97dc94f1d933c9df2c0b327066ea130c0e92083f Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 25 Jun 2014 12:35:10 +0200 Subject: [PATCH 172/274] cfg80211: remove channel_switch combination check Driver is now responsible for veryfing if the switch is possible. Since this is inherently tricky driver may decide to disconnect an interface later with cfg80211_stop_iface(). This doesn't mean driver can accept everything. It should do it's best to verify requests and reject them as soon as possible. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 7 ++++++- net/wireless/nl80211.c | 11 ----------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index b9eeae3990cf..0a080c4de275 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2301,7 +2301,12 @@ struct cfg80211_qos_map { * reliability. This operation can not fail. * @set_coalesce: Set coalesce parameters. * - * @channel_switch: initiate channel-switch procedure (with CSA) + * @channel_switch: initiate channel-switch procedure (with CSA). Driver is + * responsible for veryfing if the switch is possible. Since this is + * inherently tricky driver may decide to disconnect an interface later + * with cfg80211_stop_iface(). This doesn't mean driver can accept + * everything. It should do it's best to verify requests and reject them + * as soon as possible. * * @set_qos_map: Set QoS mapping information to the driver * diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 8f46b8ffbcf6..c10295138eb5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6013,17 +6013,6 @@ skip_beacons: params.radar_required = true; } - /* TODO: I left this here for now. With channel switch, the - * verification is a bit more complicated, because we only do - * it later when the channel switch really happens. - */ - err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, - params.chandef.chan, - CHAN_MODE_SHARED, - radar_detect_width); - if (err) - return err; - if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX]) params.block_tx = true; From 92c2538f55132d8e0e53945204cfb137e7d64b6b Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Thu, 19 Jun 2014 21:38:52 -0700 Subject: [PATCH 173/274] mwifiex: add firmware dump feature for PCIe Firmware dump feature is added for PCIe based chipsets which can be used with the help of ethtool commands. 1) Trigger firmware dump operation: ethtool --set-dump mlan0 0xff When the operation is completed, udev event will be sent to trigger external application. 2) Following udev rule can be used to get the data from ethtool: DRIVER=="mwifiex_pcie", ACTION=="change", RUN+="/sbin/mwifiex_pcie_fw_dump.sh" mwifiex_pcie_fw_dump.sh: #!/bin/bash ethtool --set-dump mlan0 0 ethtool --get-dump mlan0 ethtool --get-dump mlan0 data /tmp/ITCM.log ethtool --set-dump mlan0 1 ethtool --get-dump mlan0 ethtool --get-dump mlan0 data /tmp/DTCM.log ethtool --set-dump mlan0 2 ethtool --get-dump mlan0 ethtool --get-dump mlan0 data /tmp/SQRAM.log ethtool --set-dump mlan0 3 ethtool --get-dump mlan0 ethtool --get-dump mlan0 data /tmp/IRAM.log Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cmdevt.c | 3 + drivers/net/wireless/mwifiex/ethtool.c | 83 +++++++++++ drivers/net/wireless/mwifiex/init.c | 13 +- drivers/net/wireless/mwifiex/main.c | 2 + drivers/net/wireless/mwifiex/main.h | 29 ++++ drivers/net/wireless/mwifiex/pcie.c | 193 +++++++++++++++++++++++++ drivers/net/wireless/mwifiex/pcie.h | 10 ++ 7 files changed, 332 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index 8dee6c86f4f1..421322f5e5fb 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -960,6 +960,9 @@ mwifiex_cmd_timeout_func(unsigned long function_context) if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) mwifiex_init_fw_complete(adapter); + if (adapter->if_ops.fw_dump) + adapter->if_ops.fw_dump(adapter); + if (adapter->if_ops.card_reset) adapter->if_ops.card_reset(adapter); } diff --git a/drivers/net/wireless/mwifiex/ethtool.c b/drivers/net/wireless/mwifiex/ethtool.c index bfb39908b2c6..528bdfa6c60c 100644 --- a/drivers/net/wireless/mwifiex/ethtool.c +++ b/drivers/net/wireless/mwifiex/ethtool.c @@ -64,7 +64,90 @@ static int mwifiex_ethtool_set_wol(struct net_device *dev, return 0; } +static int +mwifiex_get_dump_flag(struct net_device *dev, struct ethtool_dump *dump) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct mwifiex_adapter *adapter = priv->adapter; + struct memory_type_mapping *entry; + + if (!adapter->if_ops.fw_dump) + return -ENOTSUPP; + + dump->flag = adapter->curr_mem_idx; + dump->version = 1; + if (adapter->curr_mem_idx != MWIFIEX_FW_DUMP_IDX) { + entry = &adapter->mem_type_mapping_tbl[adapter->curr_mem_idx]; + dump->len = entry->mem_size; + } else { + dump->len = 0; + } + + return 0; +} + +static int +mwifiex_get_dump_data(struct net_device *dev, struct ethtool_dump *dump, + void *buffer) +{ + u8 *p = buffer; + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct mwifiex_adapter *adapter = priv->adapter; + struct memory_type_mapping *entry; + + if (!adapter->if_ops.fw_dump) + return -ENOTSUPP; + + if (adapter->curr_mem_idx == MWIFIEX_FW_DUMP_IDX) { + dev_err(adapter->dev, "firmware dump in progress!!\n"); + return -EBUSY; + } + + entry = &adapter->mem_type_mapping_tbl[adapter->curr_mem_idx]; + + if (!entry->mem_ptr) + return -EFAULT; + + memcpy(p, entry->mem_ptr, entry->mem_size); + + entry->mem_size = 0; + vfree(entry->mem_ptr); + entry->mem_ptr = NULL; + + return 0; +} + +static int mwifiex_set_dump(struct net_device *dev, struct ethtool_dump *val) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct mwifiex_adapter *adapter = priv->adapter; + + if (!adapter->if_ops.fw_dump) + return -ENOTSUPP; + + if (adapter->curr_mem_idx == MWIFIEX_FW_DUMP_IDX) { + dev_err(adapter->dev, "firmware dump in progress!!\n"); + return -EBUSY; + } + + if (val->flag == MWIFIEX_FW_DUMP_IDX) { + adapter->curr_mem_idx = val->flag; + adapter->if_ops.fw_dump(adapter); + return 0; + } + + if (val->flag < 0 || val->flag >= adapter->num_mem_types) + return -EINVAL; + + adapter->curr_mem_idx = val->flag; + + return 0; +} + const struct ethtool_ops mwifiex_ethtool_ops = { .get_wol = mwifiex_ethtool_get_wol, .set_wol = mwifiex_ethtool_set_wol, + .get_dump_flag = mwifiex_get_dump_flag, + .get_dump_data = mwifiex_get_dump_data, + .set_dump = mwifiex_set_dump, }; diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 4ecd0b208ac6..2b68cf3aa336 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -382,6 +382,8 @@ static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter) static void mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter) { + int idx; + if (!adapter) { pr_err("%s: adapter is NULL\n", __func__); return; @@ -396,7 +398,16 @@ mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter) dev_dbg(adapter->dev, "info: free cmd buffer\n"); mwifiex_free_cmd_buffer(adapter); - dev_dbg(adapter->dev, "info: free scan table\n"); + for (idx = 0; idx < adapter->num_mem_types; idx++) { + struct memory_type_mapping *entry = + &adapter->mem_type_mapping_tbl[idx]; + + if (entry->mem_ptr) { + vfree(entry->mem_ptr); + entry->mem_ptr = NULL; + } + entry->mem_size = 0; + } if (adapter->sleep_cfm) dev_kfree_skb_any(adapter->sleep_cfm); diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 40e4dbd5b89d..31bd34d0f79b 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -879,6 +879,8 @@ mwifiex_add_card(void *card, struct semaphore *sem, goto err_kmalloc; INIT_WORK(&adapter->main_work, mwifiex_main_work_queue); + if (adapter->if_ops.iface_work) + INIT_WORK(&adapter->iface_work, adapter->if_ops.iface_work); /* Register the device. Fill up the private data structure with relevant information from the card. */ diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 1398afa84064..24791e2acf24 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -410,6 +411,28 @@ struct mwifiex_roc_cfg { struct ieee80211_channel chan; }; +#define MWIFIEX_FW_DUMP_IDX 0xff +#define FW_DUMP_MAX_NAME_LEN 8 +#define FW_DUMP_HOST_READY 0xEE +#define FW_DUMP_DONE 0xFF + +struct memory_type_mapping { + u8 mem_name[FW_DUMP_MAX_NAME_LEN]; + u8 *mem_ptr; + u32 mem_size; + u8 done_flag; +}; + +enum rdwr_status { + RDWR_STATUS_SUCCESS = 0, + RDWR_STATUS_FAILURE = 1, + RDWR_STATUS_DONE = 2 +}; + +enum mwifiex_iface_work_flags { + MWIFIEX_IFACE_WORK_FW_DUMP, +}; + struct mwifiex_adapter; struct mwifiex_private; @@ -674,6 +697,7 @@ struct mwifiex_if_ops { void (*card_reset) (struct mwifiex_adapter *); void (*fw_dump)(struct mwifiex_adapter *); int (*clean_pcie_ring) (struct mwifiex_adapter *adapter); + void (*iface_work)(struct work_struct *work); }; struct mwifiex_adapter { @@ -809,6 +833,11 @@ struct mwifiex_adapter { bool ext_scan; u8 fw_api_ver; u8 fw_key_api_major_ver, fw_key_api_minor_ver; + struct work_struct iface_work; + unsigned long iface_work_flags; + struct memory_type_mapping *mem_type_mapping_tbl; + u8 num_mem_types; + u8 curr_mem_idx; }; int mwifiex_init_lock_list(struct mwifiex_adapter *adapter); diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 574d4b597468..6ea8e9d314b7 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -37,6 +37,13 @@ static struct mwifiex_if_ops pcie_ops; static struct semaphore add_remove_card_sem; +static struct memory_type_mapping mem_type_mapping_tbl[] = { + {"ITCM", NULL, 0, 0xF0}, + {"DTCM", NULL, 0, 0xF1}, + {"SQRAM", NULL, 0, 0xF2}, + {"IRAM", NULL, 0, 0xF3}, +}; + static int mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb, size_t size, int flags) @@ -192,6 +199,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, card->pcie.reg = data->reg; card->pcie.blksz_fw_dl = data->blksz_fw_dl; card->pcie.tx_buf_size = data->tx_buf_size; + card->pcie.supports_fw_dump = data->supports_fw_dump; } if (mwifiex_add_card(card, &add_remove_card_sem, &pcie_ops, @@ -221,6 +229,8 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev) if (!adapter || !adapter->priv_num) return; + cancel_work_sync(&adapter->iface_work); + if (user_rmmod) { #ifdef CONFIG_PM_SLEEP if (adapter->is_suspended) @@ -307,6 +317,17 @@ static int mwifiex_read_reg(struct mwifiex_adapter *adapter, int reg, u32 *data) return 0; } +/* This function reads u8 data from PCIE card register. */ +static int mwifiex_read_reg_byte(struct mwifiex_adapter *adapter, + int reg, u8 *data) +{ + struct pcie_service_card *card = adapter->card; + + *data = ioread8(card->pci_mmap1 + reg); + + return 0; +} + /* * This function adds delay loop to ensure FW is awake before proceeding. */ @@ -2173,6 +2194,174 @@ static int mwifiex_pcie_host_to_card(struct mwifiex_adapter *adapter, u8 type, return 0; } +/* This function read/write firmware */ +static enum rdwr_status +mwifiex_pcie_rdwr_firmware(struct mwifiex_adapter *adapter, u8 doneflag) +{ + int ret, tries; + u8 ctrl_data; + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + ret = mwifiex_write_reg(adapter, reg->fw_dump_ctrl, FW_DUMP_HOST_READY); + if (ret) { + dev_err(adapter->dev, "PCIE write err\n"); + return RDWR_STATUS_FAILURE; + } + + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + mwifiex_read_reg_byte(adapter, reg->fw_dump_ctrl, &ctrl_data); + if (ctrl_data == FW_DUMP_DONE) + return RDWR_STATUS_SUCCESS; + if (doneflag && ctrl_data == doneflag) + return RDWR_STATUS_DONE; + if (ctrl_data != FW_DUMP_HOST_READY) { + dev_info(adapter->dev, + "The ctrl reg was changed, re-try again!\n"); + mwifiex_write_reg(adapter, reg->fw_dump_ctrl, + FW_DUMP_HOST_READY); + if (ret) { + dev_err(adapter->dev, "PCIE write err\n"); + return RDWR_STATUS_FAILURE; + } + } + usleep_range(100, 200); + } + + dev_err(adapter->dev, "Fail to pull ctrl_data\n"); + return RDWR_STATUS_FAILURE; +} + +/* This function dump firmware memory to file */ +static void mwifiex_pcie_fw_dump_work(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *creg = card->pcie.reg; + unsigned int reg, reg_start, reg_end; + struct timeval t; + u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0; + enum rdwr_status stat; + u32 memory_size; + static char *env[] = { "DRIVER=mwifiex_pcie", "EVENT=fw_dump", NULL }; + + if (!card->pcie.supports_fw_dump) + return; + + for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) { + struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; + + if (entry->mem_ptr) { + vfree(entry->mem_ptr); + entry->mem_ptr = NULL; + } + entry->mem_size = 0; + } + + do_gettimeofday(&t); + dev_info(adapter->dev, "== mwifiex firmware dump start: %u.%06u ==\n", + (u32)t.tv_sec, (u32)t.tv_usec); + + /* Read the number of the memories which will dump */ + stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); + if (stat == RDWR_STATUS_FAILURE) + goto done; + + reg = creg->fw_dump_start; + mwifiex_read_reg_byte(adapter, reg, &dump_num); + + /* Read the length of every memory which will dump */ + for (idx = 0; idx < dump_num; idx++) { + struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; + + stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); + if (stat == RDWR_STATUS_FAILURE) + goto done; + + memory_size = 0; + reg = creg->fw_dump_start; + for (i = 0; i < 4; i++) { + mwifiex_read_reg_byte(adapter, reg, &read_reg); + memory_size |= (read_reg << (i * 8)); + reg++; + } + + if (memory_size == 0) { + dev_info(adapter->dev, "Firmware dump Finished!\n"); + break; + } + + dev_info(adapter->dev, + "%s_SIZE=0x%x\n", entry->mem_name, memory_size); + entry->mem_ptr = vmalloc(memory_size + 1); + entry->mem_size = memory_size; + if (!entry->mem_ptr) { + dev_err(adapter->dev, + "Vmalloc %s failed\n", entry->mem_name); + goto done; + } + dbg_ptr = entry->mem_ptr; + end_ptr = dbg_ptr + memory_size; + + doneflag = entry->done_flag; + do_gettimeofday(&t); + dev_info(adapter->dev, "Start %s output %u.%06u, please wait...\n", + entry->mem_name, (u32)t.tv_sec, (u32)t.tv_usec); + + do { + stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); + if (RDWR_STATUS_FAILURE == stat) + goto done; + + reg_start = creg->fw_dump_start; + reg_end = creg->fw_dump_end; + for (reg = reg_start; reg <= reg_end; reg++) { + mwifiex_read_reg_byte(adapter, reg, dbg_ptr); + if (dbg_ptr < end_ptr) + dbg_ptr++; + else + dev_err(adapter->dev, + "Allocated buf not enough\n"); + } + + if (stat != RDWR_STATUS_DONE) + continue; + + dev_info(adapter->dev, "%s done: size=0x%tx\n", + entry->mem_name, dbg_ptr - entry->mem_ptr); + break; + } while (true); + } + do_gettimeofday(&t); + dev_info(adapter->dev, "== mwifiex firmware dump end: %u.%06u ==\n", + (u32)t.tv_sec, (u32)t.tv_usec); + + kobject_uevent_env(&adapter->wiphy->dev.kobj, KOBJ_CHANGE, env); + +done: + adapter->curr_mem_idx = 0; +} + +static void mwifiex_pcie_work(struct work_struct *work) +{ + struct mwifiex_adapter *adapter = + container_of(work, struct mwifiex_adapter, iface_work); + + if (test_and_clear_bit(MWIFIEX_IFACE_WORK_FW_DUMP, + &adapter->iface_work_flags)) + mwifiex_pcie_fw_dump_work(adapter); +} + +/* This function dumps FW information */ +static void mwifiex_pcie_fw_dump(struct mwifiex_adapter *adapter) +{ + if (test_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags)) + return; + + set_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags); + + schedule_work(&adapter->iface_work); +} + /* * This function initializes the PCI-E host memory space, WCB rings, etc. * @@ -2342,6 +2531,8 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) adapter->dev = &pdev->dev; adapter->tx_buf_size = card->pcie.tx_buf_size; + adapter->mem_type_mapping_tbl = mem_type_mapping_tbl; + adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl); strcpy(adapter->fw_name, card->pcie.firmware); return 0; @@ -2394,6 +2585,8 @@ static struct mwifiex_if_ops pcie_ops = { .cleanup_mpa_buf = NULL, .init_fw_port = mwifiex_pcie_init_fw_port, .clean_pcie_ring = mwifiex_clean_pcie_ring_buf, + .fw_dump = mwifiex_pcie_fw_dump, + .iface_work = mwifiex_pcie_work, }; /* diff --git a/drivers/net/wireless/mwifiex/pcie.h b/drivers/net/wireless/mwifiex/pcie.h index e8ec561f8a64..ee073f5c9f0d 100644 --- a/drivers/net/wireless/mwifiex/pcie.h +++ b/drivers/net/wireless/mwifiex/pcie.h @@ -129,6 +129,9 @@ struct mwifiex_pcie_card_reg { u32 ring_tx_start_ptr; u8 pfu_enabled; u8 sleep_cookie; + u16 fw_dump_ctrl; + u16 fw_dump_start; + u16 fw_dump_end; }; static const struct mwifiex_pcie_card_reg mwifiex_reg_8766 = { @@ -191,6 +194,9 @@ static const struct mwifiex_pcie_card_reg mwifiex_reg_8897 = { .ring_tx_start_ptr = MWIFIEX_BD_FLAG_TX_START_PTR, .pfu_enabled = 1, .sleep_cookie = 0, + .fw_dump_ctrl = 0xcf4, + .fw_dump_start = 0xcf8, + .fw_dump_end = 0xcff }; struct mwifiex_pcie_device { @@ -198,6 +204,7 @@ struct mwifiex_pcie_device { const struct mwifiex_pcie_card_reg *reg; u16 blksz_fw_dl; u16 tx_buf_size; + bool supports_fw_dump; }; static const struct mwifiex_pcie_device mwifiex_pcie8766 = { @@ -205,6 +212,7 @@ static const struct mwifiex_pcie_device mwifiex_pcie8766 = { .reg = &mwifiex_reg_8766, .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, + .supports_fw_dump = false, }; static const struct mwifiex_pcie_device mwifiex_pcie8897 = { @@ -212,6 +220,7 @@ static const struct mwifiex_pcie_device mwifiex_pcie8897 = { .reg = &mwifiex_reg_8897, .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, + .supports_fw_dump = true, }; struct mwifiex_evt_buf_desc { @@ -322,4 +331,5 @@ mwifiex_pcie_txbd_not_full(struct pcie_service_card *card) return 0; } + #endif /* _MWIFIEX_PCIE_H */ From 8915d73870a62c65fd7825f0ca2eb58c8bcdfb61 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Thu, 19 Jun 2014 21:38:53 -0700 Subject: [PATCH 174/274] mwifiex: use generic 'iface_work' workqueue for SDIO interface Existing dedicated card_reset work queue is replaced with the interface specific workqueue pointer provided by mwifiex module. Also new work flag is added for card reset task. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/main.h | 1 + drivers/net/wireless/mwifiex/sdio.c | 24 ++++++++++++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 24791e2acf24..582a9f9fee93 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -431,6 +431,7 @@ enum rdwr_status { enum mwifiex_iface_work_flags { MWIFIEX_IFACE_WORK_FW_DUMP, + MWIFIEX_IFACE_WORK_CARD_RESET, }; struct mwifiex_adapter; diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 4ce3d7b33991..2ae4dd754226 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -179,6 +179,8 @@ mwifiex_sdio_remove(struct sdio_func *func) if (!adapter || !adapter->priv_num) return; + cancel_work_sync(&adapter->iface_work); + if (user_rmmod) { if (adapter->is_suspended) mwifiex_sdio_resume(adapter->dev); @@ -1915,7 +1917,7 @@ mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port) } static struct mmc_host *reset_host; -static void sdio_card_reset_worker(struct work_struct *work) +static void mwifiex_sdio_card_reset_work(struct work_struct *work) { struct mmc_host *target = reset_host; @@ -1933,15 +1935,29 @@ static void sdio_card_reset_worker(struct work_struct *work) mdelay(20); mmc_add_host(target); } -static DECLARE_WORK(card_reset_work, sdio_card_reset_worker); + +static void mwifiex_sdio_work(struct work_struct *work) +{ + struct mwifiex_adapter *adapter = + container_of(work, struct mwifiex_adapter, iface_work); + + if (test_and_clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET, + &adapter->iface_work_flags)) + mwifiex_sdio_card_reset_work(work); +} /* This function resets the card */ static void mwifiex_sdio_card_reset(struct mwifiex_adapter *adapter) { struct sdio_mmc_card *card = adapter->card; + if (test_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &adapter->iface_work_flags)) + return; + + set_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &adapter->iface_work_flags); + reset_host = card->func->card->host; - schedule_work(&card_reset_work); + schedule_work(&adapter->iface_work); } static struct mwifiex_if_ops sdio_ops = { @@ -1964,6 +1980,7 @@ static struct mwifiex_if_ops sdio_ops = { .cmdrsp_complete = mwifiex_sdio_cmdrsp_complete, .event_complete = mwifiex_sdio_event_complete, .card_reset = mwifiex_sdio_card_reset, + .iface_work = mwifiex_sdio_work, }; /* @@ -2001,7 +2018,6 @@ mwifiex_sdio_cleanup_module(void) /* Set the flag as user is removing this module. */ user_rmmod = 1; - cancel_work_sync(&card_reset_work); sdio_unregister_driver(&mwifiex_sdio); } From 088df424be7fe5706ea7c385638d23f6a68c6dd1 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Thu, 19 Jun 2014 21:38:54 -0700 Subject: [PATCH 175/274] mwifiex: get rid of global pointer reset_host As we can derive host pointer from adapter, maintaining a global variable doesn't make sense. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/sdio.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 2ae4dd754226..b34270d425f4 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -1916,10 +1916,10 @@ mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port) port, card->mp_data_port_mask); } -static struct mmc_host *reset_host; -static void mwifiex_sdio_card_reset_work(struct work_struct *work) +static void mwifiex_sdio_card_reset_work(struct mwifiex_adapter *adapter) { - struct mmc_host *target = reset_host; + struct sdio_mmc_card *card = adapter->card; + struct mmc_host *target = card->func->card->host; /* The actual reset operation must be run outside of driver thread. * This is because mmc_remove_host() will cause the device to be @@ -1943,20 +1943,17 @@ static void mwifiex_sdio_work(struct work_struct *work) if (test_and_clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &adapter->iface_work_flags)) - mwifiex_sdio_card_reset_work(work); + mwifiex_sdio_card_reset_work(adapter); } /* This function resets the card */ static void mwifiex_sdio_card_reset(struct mwifiex_adapter *adapter) { - struct sdio_mmc_card *card = adapter->card; - if (test_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &adapter->iface_work_flags)) return; set_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &adapter->iface_work_flags); - reset_host = card->func->card->host; schedule_work(&adapter->iface_work); } From 54881c6b37c8d6127fa67c6baf0dc887f1920ae6 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Thu, 19 Jun 2014 21:38:55 -0700 Subject: [PATCH 176/274] mwifiex: add firmware dump feature for SDIO Firmware dump feature is added for SDIO based chipsets which can be used with the help of ethtool commands. 1) Trigger firmware dump operation: ethtool --set-dump mlan0 0xff When the operation is completed, udev event will be sent to trigger external application. 2) Following udev rule can be used to get the data from ethtool: DRIVER=="mwifiex_sdio", ACTION=="change", RUN+="/sbin/mwifiex_sdio_fw_dump.sh" mwifiex_sdio_fw_dump.sh: #!/bin/bash ethtool --set-dump mlan0 0 ethtool --get-dump mlan0 ethtool --get-dump mlan0 data /tmp/ITCM.log ethtool --set-dump mlan0 1 ethtool --get-dump mlan0 ethtool --get-dump mlan0 data /tmp/DTCM.log ethtool --set-dump mlan0 2 ethtool --get-dump mlan0 ethtool --get-dump mlan0 data /tmp/SQRAM.log ethtool --set-dump mlan0 3 ethtool --get-dump mlan0 ethtool --get-dump mlan0 data /tmp/APU.log ethtool --set-dump mlan0 4 ethtool --get-dump mlan0 ethtool --get-dump mlan0 data /tmp/CIU.log ethtool --set-dump mlan0 5 ethtool --get-dump mlan0 ethtool --get-dump mlan0 data /tmp/ICU.log ethtool --set-dump mlan0 6 ethtool --get-dump mlan0 ethtool --get-dump mlan0 data /tmp/MAC.log Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/sdio.c | 209 ++++++++++++++++++++++++++++ drivers/net/wireless/mwifiex/sdio.h | 12 ++ 2 files changed, 221 insertions(+) diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index b34270d425f4..95a1d1face95 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -50,6 +50,24 @@ static struct mwifiex_if_ops sdio_ops; static struct semaphore add_remove_card_sem; +static struct memory_type_mapping mem_type_mapping_tbl[] = { + {"ITCM", NULL, 0, 0xF0}, + {"DTCM", NULL, 0, 0xF1}, + {"SQRAM", NULL, 0, 0xF2}, + {"APU", NULL, 0, 0xF3}, + {"CIU", NULL, 0, 0xF4}, + {"ICU", NULL, 0, 0xF5}, + {"MAC", NULL, 0, 0xF6}, + {"EXT7", NULL, 0, 0xF7}, + {"EXT8", NULL, 0, 0xF8}, + {"EXT9", NULL, 0, 0xF9}, + {"EXT10", NULL, 0, 0xFA}, + {"EXT11", NULL, 0, 0xFB}, + {"EXT12", NULL, 0, 0xFC}, + {"EXT13", NULL, 0, 0xFD}, + {"EXTLAST", NULL, 0, 0xFE}, +}; + /* * SDIO probe. * @@ -87,6 +105,7 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) card->tx_buf_size = data->tx_buf_size; card->mp_tx_agg_buf_size = data->mp_tx_agg_buf_size; card->mp_rx_agg_buf_size = data->mp_rx_agg_buf_size; + card->supports_fw_dump = data->supports_fw_dump; } sdio_claim_host(func); @@ -1779,6 +1798,8 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) adapter->dev = &func->dev; strcpy(adapter->fw_name, card->firmware); + adapter->mem_type_mapping_tbl = mem_type_mapping_tbl; + adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl); return 0; } @@ -1936,6 +1957,180 @@ static void mwifiex_sdio_card_reset_work(struct mwifiex_adapter *adapter) mmc_add_host(target); } +/* This function read/write firmware */ +static enum +rdwr_status mwifiex_sdio_rdwr_firmware(struct mwifiex_adapter *adapter, + u8 doneflag) +{ + struct sdio_mmc_card *card = adapter->card; + int ret, tries; + u8 ctrl_data = 0; + + sdio_writeb(card->func, FW_DUMP_HOST_READY, card->reg->fw_dump_ctrl, + &ret); + if (ret) { + dev_err(adapter->dev, "SDIO Write ERR\n"); + return RDWR_STATUS_FAILURE; + } + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + ctrl_data = sdio_readb(card->func, card->reg->fw_dump_ctrl, + &ret); + if (ret) { + dev_err(adapter->dev, "SDIO read err\n"); + return RDWR_STATUS_FAILURE; + } + if (ctrl_data == FW_DUMP_DONE) + break; + if (doneflag && ctrl_data == doneflag) + return RDWR_STATUS_DONE; + if (ctrl_data != FW_DUMP_HOST_READY) { + dev_info(adapter->dev, + "The ctrl reg was changed, re-try again!\n"); + sdio_writeb(card->func, FW_DUMP_HOST_READY, + card->reg->fw_dump_ctrl, &ret); + if (ret) { + dev_err(adapter->dev, "SDIO write err\n"); + return RDWR_STATUS_FAILURE; + } + } + usleep_range(100, 200); + } + if (ctrl_data == FW_DUMP_HOST_READY) { + dev_err(adapter->dev, "Fail to pull ctrl_data\n"); + return RDWR_STATUS_FAILURE; + } + + return RDWR_STATUS_SUCCESS; +} + +/* This function dump firmware memory to file */ +static void mwifiex_sdio_fw_dump_work(struct work_struct *work) +{ + struct mwifiex_adapter *adapter = + container_of(work, struct mwifiex_adapter, iface_work); + struct sdio_mmc_card *card = adapter->card; + int ret = 0; + unsigned int reg, reg_start, reg_end; + u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0; + struct timeval t; + enum rdwr_status stat; + u32 memory_size; + static char *env[] = { "DRIVER=mwifiex_sdio", "EVENT=fw_dump", NULL }; + + if (!card->supports_fw_dump) + return; + + for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) { + struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; + + if (entry->mem_ptr) { + vfree(entry->mem_ptr); + entry->mem_ptr = NULL; + } + entry->mem_size = 0; + } + + mwifiex_pm_wakeup_card(adapter); + sdio_claim_host(card->func); + + do_gettimeofday(&t); + dev_info(adapter->dev, "== mwifiex firmware dump start: %u.%06u ==\n", + (u32)t.tv_sec, (u32)t.tv_usec); + + stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag); + if (stat == RDWR_STATUS_FAILURE) + goto done; + + reg = card->reg->fw_dump_start; + /* Read the number of the memories which will dump */ + dump_num = sdio_readb(card->func, reg, &ret); + if (ret) { + dev_err(adapter->dev, "SDIO read memory length err\n"); + goto done; + } + + /* Read the length of every memory which will dump */ + for (idx = 0; idx < dump_num; idx++) { + struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; + + stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag); + if (stat == RDWR_STATUS_FAILURE) + goto done; + + memory_size = 0; + reg = card->reg->fw_dump_start; + for (i = 0; i < 4; i++) { + read_reg = sdio_readb(card->func, reg, &ret); + if (ret) { + dev_err(adapter->dev, "SDIO read err\n"); + goto done; + } + memory_size |= (read_reg << i*8); + reg++; + } + + if (memory_size == 0) { + dev_info(adapter->dev, "Firmware dump Finished!\n"); + break; + } + + dev_info(adapter->dev, + "%s_SIZE=0x%x\n", entry->mem_name, memory_size); + entry->mem_ptr = vmalloc(memory_size + 1); + entry->mem_size = memory_size; + if (!entry->mem_ptr) { + dev_err(adapter->dev, "Vmalloc %s failed\n", + entry->mem_name); + goto done; + } + dbg_ptr = entry->mem_ptr; + end_ptr = dbg_ptr + memory_size; + + doneflag = entry->done_flag; + do_gettimeofday(&t); + dev_info(adapter->dev, "Start %s output %u.%06u, please wait...\n", + entry->mem_name, (u32)t.tv_sec, (u32)t.tv_usec); + + do { + stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag); + if (stat == RDWR_STATUS_FAILURE) + goto done; + + reg_start = card->reg->fw_dump_start; + reg_end = card->reg->fw_dump_end; + for (reg = reg_start; reg <= reg_end; reg++) { + *dbg_ptr = sdio_readb(card->func, reg, &ret); + if (ret) { + dev_err(adapter->dev, + "SDIO read err\n"); + goto done; + } + if (dbg_ptr < end_ptr) + dbg_ptr++; + else + dev_err(adapter->dev, + "Allocated buf not enough\n"); + } + + if (stat != RDWR_STATUS_DONE) + continue; + + dev_info(adapter->dev, "%s done: size=0x%tx\n", + entry->mem_name, dbg_ptr - entry->mem_ptr); + break; + } while (1); + } + do_gettimeofday(&t); + dev_info(adapter->dev, "== mwifiex firmware dump end: %u.%06u ==\n", + (u32)t.tv_sec, (u32)t.tv_usec); + + kobject_uevent_env(&adapter->wiphy->dev.kobj, KOBJ_CHANGE, env); + +done: + sdio_release_host(card->func); + adapter->curr_mem_idx = 0; +} + static void mwifiex_sdio_work(struct work_struct *work) { struct mwifiex_adapter *adapter = @@ -1944,6 +2139,9 @@ static void mwifiex_sdio_work(struct work_struct *work) if (test_and_clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &adapter->iface_work_flags)) mwifiex_sdio_card_reset_work(adapter); + if (test_and_clear_bit(MWIFIEX_IFACE_WORK_FW_DUMP, + &adapter->iface_work_flags)) + mwifiex_sdio_fw_dump_work(work); } /* This function resets the card */ @@ -1957,6 +2155,16 @@ static void mwifiex_sdio_card_reset(struct mwifiex_adapter *adapter) schedule_work(&adapter->iface_work); } +/* This function dumps FW information */ +static void mwifiex_sdio_fw_dump(struct mwifiex_adapter *adapter) +{ + if (test_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags)) + return; + + set_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags); + schedule_work(&adapter->iface_work); +} + static struct mwifiex_if_ops sdio_ops = { .init_if = mwifiex_init_sdio, .cleanup_if = mwifiex_cleanup_sdio, @@ -1978,6 +2186,7 @@ static struct mwifiex_if_ops sdio_ops = { .event_complete = mwifiex_sdio_event_complete, .card_reset = mwifiex_sdio_card_reset, .iface_work = mwifiex_sdio_work, + .fw_dump = mwifiex_sdio_fw_dump, }; /* diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h index 6eea30b43ed7..e5633095984e 100644 --- a/drivers/net/wireless/mwifiex/sdio.h +++ b/drivers/net/wireless/mwifiex/sdio.h @@ -219,6 +219,9 @@ struct mwifiex_sdio_card_reg { u8 rd_len_p0_l; u8 rd_len_p0_u; u8 card_misc_cfg_reg; + u8 fw_dump_ctrl; + u8 fw_dump_start; + u8 fw_dump_end; }; struct sdio_mmc_card { @@ -231,6 +234,7 @@ struct sdio_mmc_card { u8 mp_agg_pkt_limit; bool supports_sdio_new_mode; bool has_control_mask; + bool supports_fw_dump; u16 tx_buf_size; u32 mp_tx_agg_buf_size; u32 mp_rx_agg_buf_size; @@ -257,6 +261,7 @@ struct mwifiex_sdio_device { u8 mp_agg_pkt_limit; bool supports_sdio_new_mode; bool has_control_mask; + bool supports_fw_dump; u16 tx_buf_size; u32 mp_tx_agg_buf_size; u32 mp_rx_agg_buf_size; @@ -307,6 +312,9 @@ static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8897 = { .rd_len_p0_l = 0x0c, .rd_len_p0_u = 0x0d, .card_misc_cfg_reg = 0xcc, + .fw_dump_ctrl = 0xe2, + .fw_dump_start = 0xe3, + .fw_dump_end = 0xea, }; static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = { @@ -319,6 +327,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = { .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, + .supports_fw_dump = false, }; static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = { @@ -331,6 +340,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = { .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, + .supports_fw_dump = false, }; static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = { @@ -343,6 +353,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = { .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, + .supports_fw_dump = false, }; static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = { @@ -355,6 +366,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = { .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K, .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K, + .supports_fw_dump = true, }; /* From 937a50451b0d1d1834485b47f00a7c0295413d09 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Thu, 19 Jun 2014 21:38:56 -0700 Subject: [PATCH 177/274] mwifiex: add hscfg to debugfs Some SDIO controllers do not support MMC_PM_KEEP_POWER properly. To test host sleep feature without putting the system into sleep we need to simulate host sleep configuration & handshake between driver and firmware using customized parameters. This patch adds hscfg debugfs item, with which user could change host sleep parameters for debugging. Signed-off-by: Xinming Hu Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/README | 30 ++++++++ drivers/net/wireless/mwifiex/debugfs.c | 93 ++++++++++++++++++++++++ drivers/net/wireless/mwifiex/main.h | 2 + drivers/net/wireless/mwifiex/sta_ioctl.c | 4 +- 4 files changed, 127 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mwifiex/README b/drivers/net/wireless/mwifiex/README index 3b55ce5690a5..831a98fdeda8 100644 --- a/drivers/net/wireless/mwifiex/README +++ b/drivers/net/wireless/mwifiex/README @@ -194,6 +194,36 @@ rdeeprom Example: echo "0 20" > rdeeprom : Read 20 bytes of EEPROM data from offset 0 +hscfg + This command is used to debug/simulate host sleep feature using + different configuration parameters. + + Usage: + echo " [GPIO# [gap]]]" > hscfg + cat hscfg + + where the parameters are, + : bit 0 = 1 -- broadcast data + bit 1 = 1 -- unicast data + bit 2 = 1 -- mac event + bit 3 = 1 -- multicast data + [GPIO#]: pin number of GPIO used to wakeup the host. + GPIO pin# (e.g. 0-7) or 0xff (interface, e.g. SDIO + will be used instead). + [gap]: the gap in milliseconds between wakeup signal and + wakeup event or 0xff for special setting (host + acknowledge required) when GPIO is used to wakeup host. + + Examples: + echo "-1" > hscfg : Cancel host sleep mode + echo "3" > hscfg : Broadcast and unicast data; + Use GPIO and gap set previously + echo "2 3" > hscfg : Unicast data and GPIO 3; + Use gap set previously + echo "2 1 160" > hscfg : Unicast data, GPIO 1 and gap 160 ms + echo "2 1 0xff" > hscfg : Unicast data, GPIO 1; Wait for host + to ack before sending wakeup event + getlog This command is used to get the statistics available in the station. Usage: diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c index 7b419bbcd544..6a194a996836 100644 --- a/drivers/net/wireless/mwifiex/debugfs.c +++ b/drivers/net/wireless/mwifiex/debugfs.c @@ -692,6 +692,97 @@ done: return ret; } +/* Proc hscfg file write handler + * This function can be used to configure the host sleep parameters. + */ +static ssize_t +mwifiex_hscfg_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = (void *)file->private_data; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); + int ret, arg_num; + struct mwifiex_ds_hs_cfg hscfg; + int conditions = HS_CFG_COND_DEF; + u32 gpio = HS_CFG_GPIO_DEF, gap = HS_CFG_GAP_DEF; + + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, ubuf, buf_size)) { + ret = -EFAULT; + goto done; + } + + arg_num = sscanf(buf, "%d %x %x", &conditions, &gpio, &gap); + + memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg)); + + if (arg_num > 3) { + dev_err(priv->adapter->dev, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + if (arg_num >= 1 && arg_num < 3) + mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET, + MWIFIEX_SYNC_CMD, &hscfg); + + if (arg_num) { + if (conditions == HS_CFG_CANCEL) { + mwifiex_cancel_hs(priv, MWIFIEX_ASYNC_CMD); + ret = count; + goto done; + } + hscfg.conditions = conditions; + } + if (arg_num >= 2) + hscfg.gpio = gpio; + if (arg_num == 3) + hscfg.gap = gap; + + hscfg.is_invoke_hostcmd = false; + mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, + MWIFIEX_SYNC_CMD, &hscfg); + + mwifiex_enable_hs(priv->adapter); + priv->adapter->hs_enabling = false; + ret = count; +done: + free_page(addr); + return ret; +} + +/* Proc hscfg file read handler + * This function can be used to read host sleep configuration + * parameters from driver. + */ +static ssize_t +mwifiex_hscfg_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = (void *)file->private_data; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + int pos, ret; + struct mwifiex_ds_hs_cfg hscfg; + + if (!buf) + return -ENOMEM; + + mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET, + MWIFIEX_SYNC_CMD, &hscfg); + + pos = snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", hscfg.conditions, + hscfg.gpio, hscfg.gap); + + ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); + + free_page(addr); + return ret; +} #define MWIFIEX_DFS_ADD_FILE(name) do { \ if (!debugfs_create_file(#name, 0644, priv->dfs_dev_dir, \ @@ -725,6 +816,7 @@ MWIFIEX_DFS_FILE_READ_OPS(getlog); MWIFIEX_DFS_FILE_READ_OPS(fw_dump); MWIFIEX_DFS_FILE_OPS(regrdwr); MWIFIEX_DFS_FILE_OPS(rdeeprom); +MWIFIEX_DFS_FILE_OPS(hscfg); /* * This function creates the debug FS directory structure and the files. @@ -747,6 +839,7 @@ mwifiex_dev_debugfs_init(struct mwifiex_private *priv) MWIFIEX_DFS_ADD_FILE(regrdwr); MWIFIEX_DFS_ADD_FILE(rdeeprom); MWIFIEX_DFS_ADD_FILE(fw_dump); + MWIFIEX_DFS_ADD_FILE(hscfg); } /* diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 582a9f9fee93..5e734f948b07 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -920,6 +920,8 @@ int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv, void mwifiex_process_hs_config(struct mwifiex_adapter *adapter); void mwifiex_hs_activated_event(struct mwifiex_private *priv, u8 activated); +int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action, + int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg); int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv, struct host_cmd_ds_command *resp); int mwifiex_process_rx_packet(struct mwifiex_private *priv, diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index 229526356d9b..6b9395f37742 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -389,8 +389,8 @@ done: * This function prepares the correct firmware command and * issues it. */ -static int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action, - int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg) +int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action, + int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg) { struct mwifiex_adapter *adapter = priv->adapter; From 65da33f5557f0ed3f1227063bcf1f25248d456e5 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Thu, 19 Jun 2014 21:38:57 -0700 Subject: [PATCH 178/274] mwifiex: update Copyright to 2014 This patch updates mwifiex Copyright to 2014. Signed-off-by: Xinming Hu Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/11ac.c | 2 +- drivers/net/wireless/mwifiex/11ac.h | 2 +- drivers/net/wireless/mwifiex/11h.c | 2 +- drivers/net/wireless/mwifiex/11n.c | 2 +- drivers/net/wireless/mwifiex/11n.h | 2 +- drivers/net/wireless/mwifiex/11n_aggr.c | 2 +- drivers/net/wireless/mwifiex/11n_aggr.h | 2 +- drivers/net/wireless/mwifiex/11n_rxreorder.c | 2 +- drivers/net/wireless/mwifiex/11n_rxreorder.h | 2 +- drivers/net/wireless/mwifiex/Makefile | 2 +- drivers/net/wireless/mwifiex/README | 2 +- drivers/net/wireless/mwifiex/cfg80211.c | 2 +- drivers/net/wireless/mwifiex/cfg80211.h | 2 +- drivers/net/wireless/mwifiex/cfp.c | 2 +- drivers/net/wireless/mwifiex/cmdevt.c | 2 +- drivers/net/wireless/mwifiex/debugfs.c | 2 +- drivers/net/wireless/mwifiex/decl.h | 2 +- drivers/net/wireless/mwifiex/ethtool.c | 2 +- drivers/net/wireless/mwifiex/fw.h | 2 +- drivers/net/wireless/mwifiex/ie.c | 2 +- drivers/net/wireless/mwifiex/init.c | 2 +- drivers/net/wireless/mwifiex/ioctl.h | 2 +- drivers/net/wireless/mwifiex/join.c | 2 +- drivers/net/wireless/mwifiex/main.c | 2 +- drivers/net/wireless/mwifiex/main.h | 2 +- drivers/net/wireless/mwifiex/pcie.c | 2 +- drivers/net/wireless/mwifiex/pcie.h | 2 +- drivers/net/wireless/mwifiex/scan.c | 2 +- drivers/net/wireless/mwifiex/sdio.c | 2 +- drivers/net/wireless/mwifiex/sdio.h | 2 +- drivers/net/wireless/mwifiex/sta_cmd.c | 2 +- drivers/net/wireless/mwifiex/sta_cmdresp.c | 2 +- drivers/net/wireless/mwifiex/sta_event.c | 2 +- drivers/net/wireless/mwifiex/sta_ioctl.c | 2 +- drivers/net/wireless/mwifiex/sta_rx.c | 2 +- drivers/net/wireless/mwifiex/sta_tx.c | 2 +- drivers/net/wireless/mwifiex/txrx.c | 2 +- drivers/net/wireless/mwifiex/uap_cmd.c | 2 +- drivers/net/wireless/mwifiex/uap_event.c | 2 +- drivers/net/wireless/mwifiex/uap_txrx.c | 2 +- drivers/net/wireless/mwifiex/usb.c | 2 +- drivers/net/wireless/mwifiex/usb.h | 2 +- drivers/net/wireless/mwifiex/util.c | 2 +- drivers/net/wireless/mwifiex/util.h | 2 +- drivers/net/wireless/mwifiex/wmm.c | 2 +- drivers/net/wireless/mwifiex/wmm.h | 2 +- 46 files changed, 46 insertions(+), 46 deletions(-) diff --git a/drivers/net/wireless/mwifiex/11ac.c b/drivers/net/wireless/mwifiex/11ac.c index 706831df1fa2..59d23fb2365f 100644 --- a/drivers/net/wireless/mwifiex/11ac.c +++ b/drivers/net/wireless/mwifiex/11ac.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: 802.11ac * - * Copyright (C) 2013, Marvell International Ltd. + * Copyright (C) 2013-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/11ac.h b/drivers/net/wireless/mwifiex/11ac.h index 0b02cb6cfcb4..1ca92c7a8a4a 100644 --- a/drivers/net/wireless/mwifiex/11ac.h +++ b/drivers/net/wireless/mwifiex/11ac.h @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: 802.11ac * - * Copyright (C) 2013, Marvell International Ltd. + * Copyright (C) 2013-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/11h.c b/drivers/net/wireless/mwifiex/11h.c index e76b0db4e3e6..2668e83afbb6 100644 --- a/drivers/net/wireless/mwifiex/11h.c +++ b/drivers/net/wireless/mwifiex/11h.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: 802.11h * - * Copyright (C) 2013, Marvell International Ltd. + * Copyright (C) 2013-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c index e1c2f67ae85e..9d6d8d9f01e3 100644 --- a/drivers/net/wireless/mwifiex/11n.c +++ b/drivers/net/wireless/mwifiex/11n.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: 802.11n * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h index 0b73fa08f5d4..2ee268b632be 100644 --- a/drivers/net/wireless/mwifiex/11n.h +++ b/drivers/net/wireless/mwifiex/11n.h @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: 802.11n * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c index 5b32106182f8..b4c14b0fd3cb 100644 --- a/drivers/net/wireless/mwifiex/11n_aggr.c +++ b/drivers/net/wireless/mwifiex/11n_aggr.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: 802.11n Aggregation * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/11n_aggr.h b/drivers/net/wireless/mwifiex/11n_aggr.h index 892098d6a696..0cd2a3eb6c17 100644 --- a/drivers/net/wireless/mwifiex/11n_aggr.h +++ b/drivers/net/wireless/mwifiex/11n_aggr.h @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: 802.11n Aggregation * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c index 0c3571f830b0..b22bae3d1205 100644 --- a/drivers/net/wireless/mwifiex/11n_rxreorder.c +++ b/drivers/net/wireless/mwifiex/11n_rxreorder.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: 802.11n RX Re-ordering * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.h b/drivers/net/wireless/mwifiex/11n_rxreorder.h index 0fc76e4a60f8..3a87bb0e3a62 100644 --- a/drivers/net/wireless/mwifiex/11n_rxreorder.h +++ b/drivers/net/wireless/mwifiex/11n_rxreorder.h @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: 802.11n RX Re-ordering * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/Makefile b/drivers/net/wireless/mwifiex/Makefile index 2aa208ffbe23..9487d728ac20 100644 --- a/drivers/net/wireless/mwifiex/Makefile +++ b/drivers/net/wireless/mwifiex/Makefile @@ -1,5 +1,5 @@ # -# Copyright (C) 2011, Marvell International Ltd. +# Copyright (C) 2011-2014, Marvell International Ltd. # # This software file (the "File") is distributed by Marvell International # Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/README b/drivers/net/wireless/mwifiex/README index 831a98fdeda8..31928caeeed2 100644 --- a/drivers/net/wireless/mwifiex/README +++ b/drivers/net/wireless/mwifiex/README @@ -1,4 +1,4 @@ -# Copyright (C) 2011, Marvell International Ltd. +# Copyright (C) 2011-2014, Marvell International Ltd. # # This software file (the "File") is distributed by Marvell International # Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index e33a0347d3e2..15fa7b453372 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: CFG80211 * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/cfg80211.h b/drivers/net/wireless/mwifiex/cfg80211.h index c5848934f111..908367857d58 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.h +++ b/drivers/net/wireless/mwifiex/cfg80211.h @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: CFG80211 * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/cfp.c b/drivers/net/wireless/mwifiex/cfp.c index 0ddec3d4b059..b8242eb2be6f 100644 --- a/drivers/net/wireless/mwifiex/cfp.c +++ b/drivers/net/wireless/mwifiex/cfp.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: Channel, Frequence and Power * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index 421322f5e5fb..606877bc24d6 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: commands and events * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c index 6a194a996836..2713f7acd35e 100644 --- a/drivers/net/wireless/mwifiex/debugfs.c +++ b/drivers/net/wireless/mwifiex/debugfs.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: debugfs * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h index 38da6ff6f416..0e03fe39fc35 100644 --- a/drivers/net/wireless/mwifiex/decl.h +++ b/drivers/net/wireless/mwifiex/decl.h @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: generic data structures and APIs * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/ethtool.c b/drivers/net/wireless/mwifiex/ethtool.c index 528bdfa6c60c..04e56b5fc535 100644 --- a/drivers/net/wireless/mwifiex/ethtool.c +++ b/drivers/net/wireless/mwifiex/ethtool.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: ethtool * - * Copyright (C) 2013, Marvell International Ltd. + * Copyright (C) 2013-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 3175dd04834b..5561573452bb 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: Firmware specific macros & structures * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/ie.c b/drivers/net/wireless/mwifiex/ie.c index 3bf3d58bbc02..b933794758b7 100644 --- a/drivers/net/wireless/mwifiex/ie.c +++ b/drivers/net/wireless/mwifiex/ie.c @@ -2,7 +2,7 @@ * Marvell Wireless LAN device driver: management IE handling- setting and * deleting IE. * - * Copyright (C) 2012, Marvell International Ltd. + * Copyright (C) 2012-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 2b68cf3aa336..269a277d0a2e 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: HW/FW Initialization * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h index 1b576722671d..0847f3e07ab7 100644 --- a/drivers/net/wireless/mwifiex/ioctl.h +++ b/drivers/net/wireless/mwifiex/ioctl.h @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: ioctl data structures & APIs * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c index 89dc62a467f4..fc135649b85f 100644 --- a/drivers/net/wireless/mwifiex/join.c +++ b/drivers/net/wireless/mwifiex/join.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: association and ad-hoc start/join * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 31bd34d0f79b..657504c3c79d 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: major functions * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 5e734f948b07..a2733b1e63f9 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: major data structures and prototypes * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 6ea8e9d314b7..3c224a793b82 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: PCIE specific handling * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/pcie.h b/drivers/net/wireless/mwifiex/pcie.h index ee073f5c9f0d..a1a8fd3bc1be 100644 --- a/drivers/net/wireless/mwifiex/pcie.h +++ b/drivers/net/wireless/mwifiex/pcie.h @@ -3,7 +3,7 @@ * @brief This file contains definitions for PCI-E interface. * driver. * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index 45c5b3450cf5..dee717a19ddb 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: scan ioctl and command handling * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 95a1d1face95..1da04a086bd9 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: SDIO specific handling * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h index e5633095984e..6b8835ec88f1 100644 --- a/drivers/net/wireless/mwifiex/sdio.h +++ b/drivers/net/wireless/mwifiex/sdio.h @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: SDIO specific definitions * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index 88202ce0c139..0f077aaadab6 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: station command handling * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index 577f2979ed8f..822357b7b0bb 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: station command response handling * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c index f6395ef11a72..f1c240eca0cd 100644 --- a/drivers/net/wireless/mwifiex/sta_event.c +++ b/drivers/net/wireless/mwifiex/sta_event.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: station event handling * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index 6b9395f37742..1a03d4d8b418 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: functions for station ioctl * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/sta_rx.c b/drivers/net/wireless/mwifiex/sta_rx.c index 8b639d7fe6df..9ceb1dbe34c5 100644 --- a/drivers/net/wireless/mwifiex/sta_rx.c +++ b/drivers/net/wireless/mwifiex/sta_rx.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: station RX data handling * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/mwifiex/sta_tx.c index 5fce7e78a36e..cf330ba951cd 100644 --- a/drivers/net/wireless/mwifiex/sta_tx.c +++ b/drivers/net/wireless/mwifiex/sta_tx.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: station TX data handling * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c index 37f26afd4314..08205683f877 100644 --- a/drivers/net/wireless/mwifiex/txrx.c +++ b/drivers/net/wireless/mwifiex/txrx.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: generic TX/RX data handling * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c index 32643555dd2a..300bab438011 100644 --- a/drivers/net/wireless/mwifiex/uap_cmd.c +++ b/drivers/net/wireless/mwifiex/uap_cmd.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: AP specific command handling * - * Copyright (C) 2012, Marvell International Ltd. + * Copyright (C) 2012-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c index 92e77a398ecf..7c2b97660a03 100644 --- a/drivers/net/wireless/mwifiex/uap_event.c +++ b/drivers/net/wireless/mwifiex/uap_event.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: AP event handling * - * Copyright (C) 2012, Marvell International Ltd. + * Copyright (C) 2012-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/uap_txrx.c b/drivers/net/wireless/mwifiex/uap_txrx.c index 0c87f8f9113a..ddfc3c6c1e78 100644 --- a/drivers/net/wireless/mwifiex/uap_txrx.c +++ b/drivers/net/wireless/mwifiex/uap_txrx.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: AP TX and RX data handling * - * Copyright (C) 2012, Marvell International Ltd. + * Copyright (C) 2012-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c index a8ce8130cfae..7118a18b91ba 100644 --- a/drivers/net/wireless/mwifiex/usb.c +++ b/drivers/net/wireless/mwifiex/usb.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: USB specific handling * - * Copyright (C) 2012, Marvell International Ltd. + * Copyright (C) 2012-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/usb.h b/drivers/net/wireless/mwifiex/usb.h index 15b73d12e998..4c41c2a193c5 100644 --- a/drivers/net/wireless/mwifiex/usb.h +++ b/drivers/net/wireless/mwifiex/usb.h @@ -1,7 +1,7 @@ /* * This file contains definitions for mwifiex USB interface driver. * - * Copyright (C) 2012, Marvell International Ltd. + * Copyright (C) 2012-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c index 6da5abf52e61..cee028321a9a 100644 --- a/drivers/net/wireless/mwifiex/util.c +++ b/drivers/net/wireless/mwifiex/util.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: utility functions * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/util.h b/drivers/net/wireless/mwifiex/util.h index ddae57021397..9a31215487dd 100644 --- a/drivers/net/wireless/mwifiex/util.h +++ b/drivers/net/wireless/mwifiex/util.h @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: utility functions * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 6e86d1e17d3e..e8556b6e04ee 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: WMM * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 diff --git a/drivers/net/wireless/mwifiex/wmm.h b/drivers/net/wireless/mwifiex/wmm.h index eca56e371a57..569bd73f33c5 100644 --- a/drivers/net/wireless/mwifiex/wmm.h +++ b/drivers/net/wireless/mwifiex/wmm.h @@ -1,7 +1,7 @@ /* * Marvell Wireless LAN device driver: WMM * - * Copyright (C) 2011, Marvell International Ltd. + * Copyright (C) 2011-2014, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 From 363c1b4f75d7a8cec61b74ee163f1f315a56f639 Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Thu, 19 Jun 2014 21:38:58 -0700 Subject: [PATCH 179/274] mwifiex: print sleep_confirm cmd/response and power save events Sleep Confirm command is sent separately while other commands are handled through cmd_pending_q. Print sleep_confirm cmd and its response as well as power save events so that we have a clearer picture of power save handshake in driver log. Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cmdevt.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index 606877bc24d6..df42f066d70c 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -273,6 +273,7 @@ static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter) (struct mwifiex_opt_sleep_confirm *) adapter->sleep_cfm->data; struct sk_buff *sleep_cfm_tmp; + struct timeval ts; __le32 tmp; priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); @@ -283,6 +284,14 @@ static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter) (adapter->seq_num, priv->bss_num, priv->bss_type))); + do_gettimeofday(&ts); + dev_dbg(adapter->dev, + "cmd: DNLD_CMD: (%lu.%lu): %#x, act %#x, len %d, seqno %#x\n", + ts.tv_sec, ts.tv_usec, le16_to_cpu(sleep_cfm_buf->command), + le16_to_cpu(sleep_cfm_buf->action), + le16_to_cpu(sleep_cfm_buf->size), + le16_to_cpu(sleep_cfm_buf->seq_num)); + if (adapter->iface_type == MWIFIEX_USB) { sleep_cfm_tmp = dev_alloc_skb(sizeof(struct mwifiex_opt_sleep_confirm) @@ -457,11 +466,10 @@ int mwifiex_process_event(struct mwifiex_adapter *adapter) rx_info->bss_type = priv->bss_type; } - if (eventcause != EVENT_PS_SLEEP && eventcause != EVENT_PS_AWAKE) { - do_gettimeofday(&tstamp); - dev_dbg(adapter->dev, "event: %lu.%lu: cause: %#x\n", - tstamp.tv_sec, tstamp.tv_usec, eventcause); - } else { + do_gettimeofday(&tstamp); + dev_dbg(adapter->dev, "EVENT: %lu.%lu: cause: %#x\n", + tstamp.tv_sec, tstamp.tv_usec, eventcause); + if (eventcause == EVENT_PS_SLEEP || eventcause == EVENT_PS_AWAKE) { /* Handle PS_SLEEP/AWAKE events on STA */ priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); if (!priv) @@ -1228,12 +1236,19 @@ mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *adapter, uint16_t result = le16_to_cpu(cmd->result); uint16_t command = le16_to_cpu(cmd->command); uint16_t seq_num = le16_to_cpu(cmd->seq_num); + struct timeval ts; if (!upld_len) { dev_err(adapter->dev, "%s: cmd size is 0\n", __func__); return; } + do_gettimeofday(&ts); + dev_dbg(adapter->dev, + "cmd: CMD_RESP: (%lu.%lu): 0x%x, result %d, len %d, seqno 0x%x\n", + ts.tv_sec, ts.tv_usec, command, result, le16_to_cpu(cmd->size), + seq_num); + /* Get BSS number and corresponding priv */ priv = mwifiex_get_priv_by_id(adapter, HostCmd_GET_BSS_NO(seq_num), HostCmd_GET_BSS_TYPE(seq_num)); From e843bb199ba58ce5d1364d4c82fcf6975f08eec2 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Sun, 22 Jun 2014 20:50:40 +0200 Subject: [PATCH 180/274] net/wireless/brcm80211/brcmfmac: Make return type and name reflect actual semantics Applying ++ to a bool is equivalent to setting it true, regardless of its initial value (bools are not uint1_t). Hence the function wl_get_vif_state_all can only ever return true/false. The only in-tree caller uses its return value as a boolean. So update its return type, and since the list traversal and bit testing have no side effects, just return true immediately. Its return value tells if any vif is in the specified state, so also rename it to brcmf_get_vif_state_any. Reviewed-by: Arend van Spriel Signed-off-by: Rasmus Villemoes Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/p2p.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 7 +++---- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index f3445ac627e4..588fdbdb3255 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -708,7 +708,7 @@ static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans, active = P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS; else if (num_chans == AF_PEER_SEARCH_CNT) active = P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS; - else if (wl_get_vif_state_all(p2p->cfg, BRCMF_VIF_STATUS_CONNECTED)) + else if (brcmf_get_vif_state_any(p2p->cfg, BRCMF_VIF_STATUS_CONNECTED)) active = -1; else active = P2PAPI_SCAN_DWELL_TIME_MS; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index db3d8487dc42..cec55fc26f2e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -5619,16 +5619,15 @@ enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp) return wdev->iftype; } -u32 wl_get_vif_state_all(struct brcmf_cfg80211_info *cfg, unsigned long state) +bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg, unsigned long state) { struct brcmf_cfg80211_vif *vif; - bool result = 0; list_for_each_entry(vif, &cfg->vif_list, list) { if (test_bit(state, &vif->sme_state)) - result++; + return true; } - return result; + return false; } static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h index 283c525a44f7..f9fb10998e79 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h @@ -477,7 +477,7 @@ const struct brcmf_tlv * brcmf_parse_tlvs(const void *buf, int buflen, uint key); u16 channel_to_chanspec(struct brcmu_d11inf *d11inf, struct ieee80211_channel *ch); -u32 wl_get_vif_state_all(struct brcmf_cfg80211_info *cfg, unsigned long state); +bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg, unsigned long state); void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg, struct brcmf_cfg80211_vif *vif); bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg); From b6fd7fd23ef2fe32a6cf64c02211e48d79766b58 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sat, 21 Jun 2014 12:11:12 +0200 Subject: [PATCH 181/274] brcmfmac: Add 43569 USB support. Added usb device id for the new device 43569 to the list of supported devices. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 6db51a666f61..aa2355932a7e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -55,6 +55,7 @@ #define BRCMF_USB_43143_FW_NAME "brcm/brcmfmac43143.bin" #define BRCMF_USB_43236_FW_NAME "brcm/brcmfmac43236b.bin" #define BRCMF_USB_43242_FW_NAME "brcm/brcmfmac43242a.bin" +#define BRCMF_USB_43569_FW_NAME "brcm/brcmfmac43569.bin" struct brcmf_usb_image { struct list_head list; @@ -928,6 +929,8 @@ static bool brcmf_usb_chip_support(int chipid, int chiprev) return (chiprev == 3); case 43242: return true; + case 43569: + return true; default: break; } @@ -1028,6 +1031,8 @@ static const char *brcmf_usb_get_fwname(struct brcmf_usbdev_info *devinfo) return BRCMF_USB_43236_FW_NAME; case 43242: return BRCMF_USB_43242_FW_NAME; + case 43569: + return BRCMF_USB_43569_FW_NAME; default: return NULL; } @@ -1229,7 +1234,7 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) u8 endpoint_num; struct brcmf_usbdev_info *devinfo; - brcmf_dbg(USB, "Enter\n"); + brcmf_dbg(USB, "Enter 0x%04x:0x%04x\n", id->idVendor, id->idProduct); devinfo = kzalloc(sizeof(*devinfo), GFP_ATOMIC); if (devinfo == NULL) @@ -1392,12 +1397,14 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf) #define BRCMF_USB_DEVICE_ID_43143 0xbd1e #define BRCMF_USB_DEVICE_ID_43236 0xbd17 #define BRCMF_USB_DEVICE_ID_43242 0xbd1f +#define BRCMF_USB_DEVICE_ID_43569 0xbd27 #define BRCMF_USB_DEVICE_ID_BCMFW 0x0bdc static struct usb_device_id brcmf_usb_devid_table[] = { { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43143) }, { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43236) }, { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43242) }, + { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43569) }, /* special entry for device with firmware loaded and running */ { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_BCMFW) }, { } @@ -1407,6 +1414,7 @@ MODULE_DEVICE_TABLE(usb, brcmf_usb_devid_table); MODULE_FIRMWARE(BRCMF_USB_43143_FW_NAME); MODULE_FIRMWARE(BRCMF_USB_43236_FW_NAME); MODULE_FIRMWARE(BRCMF_USB_43242_FW_NAME); +MODULE_FIRMWARE(BRCMF_USB_43569_FW_NAME); static struct usb_driver brcmf_usbdrvr = { .name = KBUILD_MODNAME, From 457cfabb99461b9f8d6bf32396f9c14445b7155a Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sat, 21 Jun 2014 12:11:13 +0200 Subject: [PATCH 182/274] brcmfmac: Add USB device 43566 to supported devices. Add the USB 43566 device to the supported devices list. The 43566 is a WiFi-only variant of the 43569. It uses the same FW as 43569. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index aa2355932a7e..d2927acfbd79 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -929,6 +929,7 @@ static bool brcmf_usb_chip_support(int chipid, int chiprev) return (chiprev == 3); case 43242: return true; + case 43566: case 43569: return true; default: @@ -1031,6 +1032,7 @@ static const char *brcmf_usb_get_fwname(struct brcmf_usbdev_info *devinfo) return BRCMF_USB_43236_FW_NAME; case 43242: return BRCMF_USB_43242_FW_NAME; + case 43566: case 43569: return BRCMF_USB_43569_FW_NAME; default: From d83f8face594340551b2c57ae95acfe70ae444f8 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 21 Jun 2014 12:11:14 +0200 Subject: [PATCH 183/274] brcmfmac: clear ht info during attach phase After updating 2G bandwidth capability clear ht info. This will be properly set upon calling brcmf_update_wiphy_bands(). Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index cec55fc26f2e..2261918f734c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -5051,6 +5051,9 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, err = brcmf_fil_iovar_int_set(ifp, "obss_coex", BRCMF_OBSS_COEX_AUTO); } + /* clear for now and rely on update later */ + wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.ht_supported = false; + wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap = 0; err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1); if (err) { From 51c7f5eddd0f2e8eb6b49a3025b04148cf3b3912 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sat, 21 Jun 2014 12:11:15 +0200 Subject: [PATCH 184/274] brcmfmac: Change USB probe routine to support Composite USB Some of the USB devices also have Bluetooth inside. These devices can with specific firmware result in a composite USB device. This change will update the driver such that it will also accept the correct interface of composite devices. It is backward compatible with old non-composite USB fw. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 204 ++++++++---------- 1 file changed, 87 insertions(+), 117 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index d2927acfbd79..839bcda9465a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -29,33 +29,24 @@ #include "usb_rdl.h" #include "usb.h" -#define IOCTL_RESP_TIMEOUT 2000 +#define IOCTL_RESP_TIMEOUT 2000 #define BRCMF_USB_RESET_GETVER_SPINWAIT 100 /* in unit of ms */ #define BRCMF_USB_RESET_GETVER_LOOP_CNT 10 #define BRCMF_POSTBOOT_ID 0xA123 /* ID to detect if dongle has boot up */ -#define BRCMF_USB_NRXQ 50 -#define BRCMF_USB_NTXQ 50 +#define BRCMF_USB_NRXQ 50 +#define BRCMF_USB_NTXQ 50 -#define CONFIGDESC(usb) (&((usb)->actconfig)->desc) -#define IFPTR(usb, idx) ((usb)->actconfig->interface[(idx)]) -#define IFALTS(usb, idx) (IFPTR((usb), (idx))->altsetting[0]) -#define IFDESC(usb, idx) IFALTS((usb), (idx)).desc -#define IFEPDESC(usb, idx, ep) (IFALTS((usb), (idx)).endpoint[(ep)]).desc +#define BRCMF_USB_CBCTL_WRITE 0 +#define BRCMF_USB_CBCTL_READ 1 +#define BRCMF_USB_MAX_PKT_SIZE 1600 -#define CONTROL_IF 0 -#define BULK_IF 0 - -#define BRCMF_USB_CBCTL_WRITE 0 -#define BRCMF_USB_CBCTL_READ 1 -#define BRCMF_USB_MAX_PKT_SIZE 1600 - -#define BRCMF_USB_43143_FW_NAME "brcm/brcmfmac43143.bin" -#define BRCMF_USB_43236_FW_NAME "brcm/brcmfmac43236b.bin" -#define BRCMF_USB_43242_FW_NAME "brcm/brcmfmac43242a.bin" -#define BRCMF_USB_43569_FW_NAME "brcm/brcmfmac43569.bin" +#define BRCMF_USB_43143_FW_NAME "brcm/brcmfmac43143.bin" +#define BRCMF_USB_43236_FW_NAME "brcm/brcmfmac43236b.bin" +#define BRCMF_USB_43242_FW_NAME "brcm/brcmfmac43242a.bin" +#define BRCMF_USB_43569_FW_NAME "brcm/brcmfmac43569.bin" struct brcmf_usb_image { struct list_head list; @@ -71,7 +62,7 @@ struct brcmf_usbdev_info { struct list_head rx_postq; struct list_head tx_freeq; struct list_head tx_postq; - uint rx_pipe, tx_pipe, rx_pipe2; + uint rx_pipe, tx_pipe; int rx_low_watermark; int tx_low_watermark; @@ -98,6 +89,7 @@ struct brcmf_usbdev_info { int ctl_completed; wait_queue_head_t ioctl_resp_wait; ulong ctl_op; + u8 ifnum; struct urb *bulk_urb; /* used for FW download */ }; @@ -577,7 +569,6 @@ fail: static int brcmf_usb_up(struct device *dev) { struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); - u16 ifnum; brcmf_dbg(USB, "Enter\n"); if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) @@ -590,21 +581,19 @@ static int brcmf_usb_up(struct device *dev) devinfo->ctl_in_pipe = usb_rcvctrlpipe(devinfo->usbdev, 0); devinfo->ctl_out_pipe = usb_sndctrlpipe(devinfo->usbdev, 0); - ifnum = IFDESC(devinfo->usbdev, CONTROL_IF).bInterfaceNumber; - /* CTL Write */ devinfo->ctl_write.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; devinfo->ctl_write.bRequest = 0; devinfo->ctl_write.wValue = cpu_to_le16(0); - devinfo->ctl_write.wIndex = cpu_to_le16p(&ifnum); + devinfo->ctl_write.wIndex = cpu_to_le16(devinfo->ifnum); /* CTL Read */ devinfo->ctl_read.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; devinfo->ctl_read.bRequest = 1; devinfo->ctl_read.wValue = cpu_to_le16(0); - devinfo->ctl_read.wIndex = cpu_to_le16p(&ifnum); + devinfo->ctl_read.wIndex = cpu_to_le16(devinfo->ifnum); } brcmf_usb_rx_fill_all(devinfo); return 0; @@ -643,19 +632,19 @@ brcmf_usb_sync_complete(struct urb *urb) brcmf_usb_ioctl_resp_wake(devinfo); } -static bool brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd, - void *buffer, int buflen) +static int brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd, + void *buffer, int buflen) { - int ret = 0; + int ret; char *tmpbuf; u16 size; if ((!devinfo) || (devinfo->ctl_urb == NULL)) - return false; + return -EINVAL; tmpbuf = kmalloc(buflen, GFP_ATOMIC); if (!tmpbuf) - return false; + return -ENOMEM; size = buflen; devinfo->ctl_urb->transfer_buffer_length = size; @@ -676,14 +665,16 @@ static bool brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd, ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC); if (ret < 0) { brcmf_err("usb_submit_urb failed %d\n", ret); - kfree(tmpbuf); - return false; + goto finalize; } - ret = brcmf_usb_ioctl_resp_wait(devinfo); - memcpy(buffer, tmpbuf, buflen); - kfree(tmpbuf); + if (!brcmf_usb_ioctl_resp_wait(devinfo)) + ret = -ETIMEDOUT; + else + memcpy(buffer, tmpbuf, buflen); +finalize: + kfree(tmpbuf); return ret; } @@ -725,6 +716,7 @@ brcmf_usb_resetcfg(struct brcmf_usbdev_info *devinfo) { struct bootrom_id_le id; u32 loop_cnt; + int err; brcmf_dbg(USB, "Enter\n"); @@ -733,7 +725,9 @@ brcmf_usb_resetcfg(struct brcmf_usbdev_info *devinfo) mdelay(BRCMF_USB_RESET_GETVER_SPINWAIT); loop_cnt++; id.chip = cpu_to_le32(0xDEAD); /* Get the ID */ - brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(id)); + err = brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(id)); + if ((err) && (err != -ETIMEDOUT)) + return err; if (id.chip == cpu_to_le32(BRCMF_POSTBOOT_ID)) break; } while (loop_cnt < BRCMF_USB_RESET_GETVER_LOOP_CNT); @@ -795,8 +789,7 @@ brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen) } /* 1) Prepare USB boot loader for runtime image */ - brcmf_usb_dl_cmd(devinfo, DL_START, &state, - sizeof(struct rdl_state_le)); + brcmf_usb_dl_cmd(devinfo, DL_START, &state, sizeof(state)); rdlstate = le32_to_cpu(state.state); rdlbytes = le32_to_cpu(state.bytes); @@ -840,10 +833,10 @@ brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen) dlpos += sendlen; sent += sendlen; } - if (!brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state, - sizeof(struct rdl_state_le))) { - brcmf_err("DL_GETSTATE Failed xxxx\n"); - err = -EINVAL; + err = brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state, + sizeof(state)); + if (err) { + brcmf_err("DL_GETSTATE Failed\n"); goto fail; } @@ -899,13 +892,12 @@ static int brcmf_usb_dlrun(struct brcmf_usbdev_info *devinfo) return -EINVAL; /* Check we are runnable */ - brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state, - sizeof(struct rdl_state_le)); + state.state = 0; + brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state, sizeof(state)); /* Start the image */ if (state.state == cpu_to_le32(DL_RUNNABLE)) { - if (!brcmf_usb_dl_cmd(devinfo, DL_GO, &state, - sizeof(struct rdl_state_le))) + if (brcmf_usb_dl_cmd(devinfo, DL_GO, &state, sizeof(state))) return -ENODEV; if (brcmf_usb_resetcfg(devinfo)) return -ENODEV; @@ -1228,13 +1220,13 @@ brcmf_usb_disconnect_cb(struct brcmf_usbdev_info *devinfo) static int brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { - int ep; + struct usb_device *usb = interface_to_usbdev(intf); + struct brcmf_usbdev_info *devinfo; + struct usb_interface_descriptor *desc; struct usb_endpoint_descriptor *endpoint; int ret = 0; - struct usb_device *usb = interface_to_usbdev(intf); - int num_of_eps; - u8 endpoint_num; - struct brcmf_usbdev_info *devinfo; + u32 num_of_eps; + u8 endpoint_num, ep; brcmf_dbg(USB, "Enter 0x%04x:0x%04x\n", id->idVendor, id->idProduct); @@ -1244,92 +1236,71 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) devinfo->usbdev = usb; devinfo->dev = &usb->dev; - usb_set_intfdata(intf, devinfo); /* Check that the device supports only one configuration */ if (usb->descriptor.bNumConfigurations != 1) { - ret = -1; + brcmf_err("Number of configurations: %d not supported\n", + usb->descriptor.bNumConfigurations); + ret = -ENODEV; goto fail; } - if (usb->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) { - ret = -1; + if ((usb->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) && + (usb->descriptor.bDeviceClass != USB_CLASS_MISC) && + (usb->descriptor.bDeviceClass != USB_CLASS_WIRELESS_CONTROLLER)) { + brcmf_err("Device class: 0x%x not supported\n", + usb->descriptor.bDeviceClass); + ret = -ENODEV; goto fail; } - /* - * Only the BDC interface configuration is supported: - * Device class: USB_CLASS_VENDOR_SPEC - * if0 class: USB_CLASS_VENDOR_SPEC - * if0/ep0: control - * if0/ep1: bulk in - * if0/ep2: bulk out (ok if swapped with bulk in) - */ - if (CONFIGDESC(usb)->bNumInterfaces != 1) { - ret = -1; + desc = &intf->altsetting[0].desc; + if ((desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC) || + (desc->bInterfaceSubClass != 2) || + (desc->bInterfaceProtocol != 0xff)) { + brcmf_err("non WLAN interface %d: 0x%x:0x%x:0x%x\n", + desc->bInterfaceNumber, desc->bInterfaceClass, + desc->bInterfaceSubClass, desc->bInterfaceProtocol); + ret = -ENODEV; goto fail; } - /* Check interface */ - if (IFDESC(usb, CONTROL_IF).bInterfaceClass != USB_CLASS_VENDOR_SPEC || - IFDESC(usb, CONTROL_IF).bInterfaceSubClass != 2 || - IFDESC(usb, CONTROL_IF).bInterfaceProtocol != 0xff) { - brcmf_err("invalid control interface: class %d, subclass %d, proto %d\n", - IFDESC(usb, CONTROL_IF).bInterfaceClass, - IFDESC(usb, CONTROL_IF).bInterfaceSubClass, - IFDESC(usb, CONTROL_IF).bInterfaceProtocol); - ret = -1; - goto fail; - } - - /* Check control endpoint */ - endpoint = &IFEPDESC(usb, CONTROL_IF, 0); - if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - != USB_ENDPOINT_XFER_INT) { - brcmf_err("invalid control endpoint %d\n", - endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); - ret = -1; - goto fail; - } - - devinfo->rx_pipe = 0; - devinfo->rx_pipe2 = 0; - devinfo->tx_pipe = 0; - num_of_eps = IFDESC(usb, BULK_IF).bNumEndpoints - 1; - - /* Check data endpoints and get pipes */ - for (ep = 1; ep <= num_of_eps; ep++) { - endpoint = &IFEPDESC(usb, BULK_IF, ep); - if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != - USB_ENDPOINT_XFER_BULK) { - brcmf_err("invalid data endpoint %d\n", ep); - ret = -1; - goto fail; - } - - endpoint_num = endpoint->bEndpointAddress & - USB_ENDPOINT_NUMBER_MASK; - if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) - == USB_DIR_IN) { - if (!devinfo->rx_pipe) { + num_of_eps = desc->bNumEndpoints; + for (ep = 0; ep < num_of_eps; ep++) { + endpoint = &intf->altsetting[0].endpoint[ep].desc; + endpoint_num = usb_endpoint_num(endpoint); + if (!usb_endpoint_xfer_bulk(endpoint)) + continue; + if (usb_endpoint_dir_in(endpoint)) { + if (!devinfo->rx_pipe) devinfo->rx_pipe = usb_rcvbulkpipe(usb, endpoint_num); - } else { - devinfo->rx_pipe2 = - usb_rcvbulkpipe(usb, endpoint_num); - } } else { - devinfo->tx_pipe = usb_sndbulkpipe(usb, endpoint_num); + if (!devinfo->tx_pipe) + devinfo->tx_pipe = + usb_sndbulkpipe(usb, endpoint_num); } } + if (devinfo->rx_pipe == 0) { + brcmf_err("No RX (in) Bulk EP found\n"); + ret = -ENODEV; + goto fail; + } + if (devinfo->tx_pipe == 0) { + brcmf_err("No TX (out) Bulk EP found\n"); + ret = -ENODEV; + goto fail; + } + + devinfo->ifnum = desc->bInterfaceNumber; if (usb->speed == USB_SPEED_SUPER) - brcmf_dbg(USB, "Broadcom super speed USB wireless device detected\n"); + brcmf_dbg(USB, "Broadcom super speed USB WLAN interface detected\n"); else if (usb->speed == USB_SPEED_HIGH) - brcmf_dbg(USB, "Broadcom high speed USB wireless device detected\n"); + brcmf_dbg(USB, "Broadcom high speed USB WLAN interface detected\n"); else - brcmf_dbg(USB, "Broadcom full speed USB wireless device detected\n"); + brcmf_dbg(USB, "Broadcom full speed USB WLAN interface detected\n"); ret = brcmf_usb_probe_cb(devinfo); if (ret) @@ -1339,11 +1310,9 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) return 0; fail: - brcmf_err("failed with errno %d\n", ret); kfree(devinfo); usb_set_intfdata(intf, NULL); return ret; - } static void @@ -1388,6 +1357,7 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf) { struct usb_device *usb = interface_to_usbdev(intf); struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); + brcmf_dbg(USB, "Enter\n"); return brcmf_fw_get_firmwares(&usb->dev, 0, From 1bacb0487d0ed914a9fd08fa7c24fa6c4b27cacc Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Sat, 21 Jun 2014 12:11:16 +0200 Subject: [PATCH 185/274] brcmfmac: replace cfg80211 testmode with vendor command Passing a pointer from user space and using it directly in driver is not a preferable behavior. Switch to cfg80211 vendor mode for dongle command for better cross platform compatibility. Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Signed-off-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/Makefile | 3 +- drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 10 -- .../net/wireless/brcm80211/brcmfmac/vendor.c | 115 ++++++++++++++++++ .../net/wireless/brcm80211/brcmfmac/vendor.h | 64 ++++++++++ .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 37 ++---- 5 files changed, 188 insertions(+), 41 deletions(-) create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/vendor.c create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/vendor.h diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index 98e67c18f276..4cffb2ee3673 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -34,7 +34,8 @@ brcmfmac-objs += \ dhd_common.o \ dhd_linux.o \ firmware.o \ - btcoex.o + btcoex.o \ + vendor.o brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \ dhd_sdio.o \ bcmsdh.o diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index 16f9ab2568a8..a8998eb60d22 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h @@ -49,16 +49,6 @@ */ #define BRCMF_DRIVER_FIRMWARE_VERSION_LEN 32 -/* Bus independent dongle command */ -struct brcmf_dcmd { - uint cmd; /* common dongle cmd definition */ - void *buf; /* pointer to user buffer */ - uint len; /* length of user buffer */ - u8 set; /* get or set request (optional) */ - uint used; /* bytes read or written (optional) */ - uint needed; /* bytes needed (optional) */ -}; - /** * struct brcmf_ampdu_rx_reorder - AMPDU receive reorder info * diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c new file mode 100644 index 000000000000..5960d827508c --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2014 Broadcom Corporation + * + * 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 "fwil_types.h" +#include "dhd.h" +#include "p2p.h" +#include "dhd_dbg.h" +#include "wl_cfg80211.h" +#include "vendor.h" +#include "fwil.h" + +static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct net_device *ndev = cfg_to_ndev(cfg); + const struct brcmf_vndr_dcmd_hdr *cmdhdr = data; + struct sk_buff *reply; + int ret, payload, ret_len; + void *dcmd_buf = NULL, *wr_pointer; + u16 msglen, maxmsglen = PAGE_SIZE - 0x100; + + brcmf_dbg(TRACE, "cmd %x set %d len %d\n", cmdhdr->cmd, cmdhdr->set, + cmdhdr->len); + + len -= sizeof(struct brcmf_vndr_dcmd_hdr); + ret_len = cmdhdr->len; + if (ret_len > 0 || len > 0) { + if (len > BRCMF_DCMD_MAXLEN) { + brcmf_err("oversize input buffer %d\n", len); + len = BRCMF_DCMD_MAXLEN; + } + if (ret_len > BRCMF_DCMD_MAXLEN) { + brcmf_err("oversize return buffer %d\n", ret_len); + ret_len = BRCMF_DCMD_MAXLEN; + } + payload = max(ret_len, len) + 1; + dcmd_buf = vzalloc(payload); + if (NULL == dcmd_buf) + return -ENOMEM; + + memcpy(dcmd_buf, (void *)cmdhdr + cmdhdr->offset, len); + *(char *)(dcmd_buf + len) = '\0'; + } + + if (cmdhdr->set) + ret = brcmf_fil_cmd_data_set(netdev_priv(ndev), cmdhdr->cmd, + dcmd_buf, ret_len); + else + ret = brcmf_fil_cmd_data_get(netdev_priv(ndev), cmdhdr->cmd, + dcmd_buf, ret_len); + if (ret != 0) + goto exit; + + wr_pointer = dcmd_buf; + while (ret_len > 0) { + msglen = ret_len > maxmsglen ? maxmsglen : ret_len; + ret_len -= msglen; + payload = msglen + sizeof(msglen); + reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, payload); + if (NULL == reply) { + ret = -ENOMEM; + break; + } + + if (nla_put(reply, BRCMF_NLATTR_DATA, msglen, wr_pointer) || + nla_put_u16(reply, BRCMF_NLATTR_LEN, msglen)) { + kfree_skb(reply); + ret = -ENOBUFS; + break; + } + + ret = cfg80211_vendor_cmd_reply(reply); + if (ret) + break; + + wr_pointer += msglen; + } + +exit: + vfree(dcmd_buf); + + return ret; +} + +const struct wiphy_vendor_command brcmf_vendor_cmds[] = { + { + { + .vendor_id = BROADCOM_OUI, + .subcmd = BRCMF_VNDR_CMDS_DCMD + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = brcmf_cfg80211_vndr_cmds_dcmd_handler + }, +}; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.h b/drivers/net/wireless/brcm80211/brcmfmac/vendor.h new file mode 100644 index 000000000000..061b7bfa2e1c --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/vendor.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014 Broadcom Corporation + * + * 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 _vendor_h_ +#define _vendor_h_ + +#define BROADCOM_OUI 0x001018 + +enum brcmf_vndr_cmds { + BRCMF_VNDR_CMDS_UNSPEC, + BRCMF_VNDR_CMDS_DCMD, + BRCMF_VNDR_CMDS_LAST +}; + +/** + * enum brcmf_nlattrs - nl80211 message attributes + * + * @BRCMF_NLATTR_LEN: message body length + * @BRCMF_NLATTR_DATA: message body + */ +enum brcmf_nlattrs { + BRCMF_NLATTR_UNSPEC, + + BRCMF_NLATTR_LEN, + BRCMF_NLATTR_DATA, + + __BRCMF_NLATTR_AFTER_LAST, + BRCMF_NLATTR_MAX = __BRCMF_NLATTR_AFTER_LAST - 1 +}; + +/** + * struct brcmf_vndr_dcmd_hdr - message header for cfg80211 vendor command dcmd + * support + * + * @cmd: common dongle cmd definition + * @len: length of expecting return buffer + * @offset: offset of data buffer + * @set: get or set request(optional) + * @magic: magic number for verification + */ +struct brcmf_vndr_dcmd_hdr { + uint cmd; + int len; + uint offset; + uint set; + uint magic; +}; + +extern const struct wiphy_vendor_command brcmf_vendor_cmds[]; + +#endif /* _vendor_h_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 2261918f734c..46b57eafa130 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -33,6 +34,7 @@ #include "btcoex.h" #include "wl_cfg80211.h" #include "fwil.h" +#include "vendor.h" #define BRCMF_SCAN_IE_LEN_MAX 2048 #define BRCMF_PNO_VERSION 2 @@ -3257,35 +3259,6 @@ static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, return 0; } -#ifdef CONFIG_NL80211_TESTMODE -static int brcmf_cfg80211_testmode(struct wiphy *wiphy, - struct wireless_dev *wdev, - void *data, int len) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct net_device *ndev = cfg_to_ndev(cfg); - struct brcmf_dcmd *dcmd = data; - struct sk_buff *reply; - int ret; - - brcmf_dbg(TRACE, "cmd %x set %d buf %p len %d\n", dcmd->cmd, dcmd->set, - dcmd->buf, dcmd->len); - - if (dcmd->set) - ret = brcmf_fil_cmd_data_set(netdev_priv(ndev), dcmd->cmd, - dcmd->buf, dcmd->len); - else - ret = brcmf_fil_cmd_data_get(netdev_priv(ndev), dcmd->cmd, - dcmd->buf, dcmd->len); - if (ret == 0) { - reply = cfg80211_testmode_alloc_reply_skb(wiphy, sizeof(*dcmd)); - nla_put(reply, NL80211_ATTR_TESTDATA, sizeof(*dcmd), dcmd); - ret = cfg80211_testmode_reply(reply); - } - return ret; -} -#endif - static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp) { s32 err; @@ -4303,7 +4276,6 @@ static struct cfg80211_ops wl_cfg80211_ops = { .crit_proto_start = brcmf_cfg80211_crit_proto_start, .crit_proto_stop = brcmf_cfg80211_crit_proto_stop, .tdls_oper = brcmf_cfg80211_tdls_oper, - CFG80211_TESTMODE_CMD(brcmf_cfg80211_testmode) }; static void brcmf_wiphy_pno_params(struct wiphy *wiphy) @@ -4408,6 +4380,11 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev) brcmf_dbg(INFO, "Registering custom regulatory\n"); wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); + + /* vendor commands/events support */ + wiphy->vendor_commands = brcmf_vendor_cmds; + wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1; + err = wiphy_register(wiphy); if (err < 0) { brcmf_err("Could not register wiphy device (%d)\n", err); From 2bb443d9ade2d4557bffdb40447fdfbdadb6071c Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 21 Jun 2014 12:11:17 +0200 Subject: [PATCH 186/274] brcmfmac: correct logging levels in btcoex source All log messages were set to TRACE level, which is intended for function entry and exit. Using INFO instead in other places. Also reducing an error message that always popped up upon module unload. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/btcoex.c | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c index 0cb591b050b3..a29ac4977b3a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c @@ -157,7 +157,7 @@ static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci, */ /* save current */ - brcmf_dbg(TRACE, "new SCO/eSCO coex algo {save & override}\n"); + brcmf_dbg(INFO, "new SCO/eSCO coex algo {save & override}\n"); brcmf_btcoex_params_read(ifp, 50, &btci->reg50); brcmf_btcoex_params_read(ifp, 51, &btci->reg51); brcmf_btcoex_params_read(ifp, 64, &btci->reg64); @@ -165,7 +165,7 @@ static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci, brcmf_btcoex_params_read(ifp, 71, &btci->reg71); btci->saved_regs_part2 = true; - brcmf_dbg(TRACE, + brcmf_dbg(INFO, "saved bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n", btci->reg50, btci->reg51, btci->reg64, btci->reg65, btci->reg71); @@ -179,21 +179,21 @@ static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci, } else if (btci->saved_regs_part2) { /* restore previously saved bt params */ - brcmf_dbg(TRACE, "Do new SCO/eSCO coex algo {restore}\n"); + brcmf_dbg(INFO, "Do new SCO/eSCO coex algo {restore}\n"); brcmf_btcoex_params_write(ifp, 50, btci->reg50); brcmf_btcoex_params_write(ifp, 51, btci->reg51); brcmf_btcoex_params_write(ifp, 64, btci->reg64); brcmf_btcoex_params_write(ifp, 65, btci->reg65); brcmf_btcoex_params_write(ifp, 71, btci->reg71); - brcmf_dbg(TRACE, + brcmf_dbg(INFO, "restored bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n", btci->reg50, btci->reg51, btci->reg64, btci->reg65, btci->reg71); btci->saved_regs_part2 = false; } else { - brcmf_err("attempted to restore not saved BTCOEX params\n"); + brcmf_dbg(INFO, "attempted to restore not saved BTCOEX params\n"); } } @@ -219,14 +219,14 @@ static bool brcmf_btcoex_is_sco_active(struct brcmf_if *ifp) break; } - brcmf_dbg(TRACE, "sample[%d], btc_params 27:%x\n", i, param27); + brcmf_dbg(INFO, "sample[%d], btc_params 27:%x\n", i, param27); if ((param27 & 0x6) == 2) { /* count both sco & esco */ sco_id_cnt++; } if (sco_id_cnt > 2) { - brcmf_dbg(TRACE, + brcmf_dbg(INFO, "sco/esco detected, pkt id_cnt:%d samples:%d\n", sco_id_cnt, i); res = true; @@ -250,7 +250,7 @@ static void btcmf_btcoex_save_part1(struct brcmf_btcoex_info *btci) brcmf_btcoex_params_read(ifp, 41, &btci->reg41); brcmf_btcoex_params_read(ifp, 68, &btci->reg68); btci->saved_regs_part1 = true; - brcmf_dbg(TRACE, + brcmf_dbg(INFO, "saved btc_params regs (66,41,68) 0x%x 0x%x 0x%x\n", btci->reg66, btci->reg41, btci->reg68); @@ -270,7 +270,7 @@ static void brcmf_btcoex_restore_part1(struct brcmf_btcoex_info *btci) brcmf_btcoex_params_write(ifp, 66, btci->reg66); brcmf_btcoex_params_write(ifp, 41, btci->reg41); brcmf_btcoex_params_write(ifp, 68, btci->reg68); - brcmf_dbg(TRACE, + brcmf_dbg(INFO, "restored btc_params regs {66,41,68} 0x%x 0x%x 0x%x\n", btci->reg66, btci->reg41, btci->reg68); @@ -307,7 +307,7 @@ static void brcmf_btcoex_handler(struct work_struct *work) /* DHCP started provide OPPORTUNITY window to get DHCP address */ - brcmf_dbg(TRACE, "DHCP started\n"); + brcmf_dbg(INFO, "DHCP started\n"); btci->bt_state = BRCMF_BT_DHCP_OPPR_WIN; if (btci->timeout < BRCMF_BTCOEX_OPPR_WIN_TIME) { mod_timer(&btci->timer, btci->timer.expires); @@ -322,12 +322,12 @@ static void brcmf_btcoex_handler(struct work_struct *work) case BRCMF_BT_DHCP_OPPR_WIN: if (btci->dhcp_done) { - brcmf_dbg(TRACE, "DHCP done before T1 expiration\n"); + brcmf_dbg(INFO, "DHCP done before T1 expiration\n"); goto idle; } /* DHCP is not over yet, start lowering BT priority */ - brcmf_dbg(TRACE, "DHCP T1:%d expired\n", + brcmf_dbg(INFO, "DHCP T1:%d expired\n", BRCMF_BTCOEX_OPPR_WIN_TIME); brcmf_btcoex_boost_wifi(btci, true); @@ -339,9 +339,9 @@ static void brcmf_btcoex_handler(struct work_struct *work) case BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT: if (btci->dhcp_done) - brcmf_dbg(TRACE, "DHCP done before T2 expiration\n"); + brcmf_dbg(INFO, "DHCP done before T2 expiration\n"); else - brcmf_dbg(TRACE, "DHCP T2:%d expired\n", + brcmf_dbg(INFO, "DHCP T2:%d expired\n", BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT); goto idle; @@ -440,13 +440,13 @@ static void brcmf_btcoex_dhcp_end(struct brcmf_btcoex_info *btci) /* Stop any bt timer because DHCP session is done */ btci->dhcp_done = true; if (btci->timer_on) { - brcmf_dbg(TRACE, "disable BT DHCP Timer\n"); + brcmf_dbg(INFO, "disable BT DHCP Timer\n"); btci->timer_on = false; del_timer_sync(&btci->timer); /* schedule worker if transition to IDLE is needed */ if (btci->bt_state != BRCMF_BT_DHCP_IDLE) { - brcmf_dbg(TRACE, "bt_state:%d\n", + brcmf_dbg(INFO, "bt_state:%d\n", btci->bt_state); schedule_work(&btci->work); } @@ -472,7 +472,7 @@ int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif, switch (mode) { case BRCMF_BTCOEX_DISABLED: - brcmf_dbg(TRACE, "DHCP session starts\n"); + brcmf_dbg(INFO, "DHCP session starts\n"); if (btci->bt_state != BRCMF_BT_DHCP_IDLE) return -EBUSY; /* Start BT timer only for SCO connection */ @@ -484,14 +484,14 @@ int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif, break; case BRCMF_BTCOEX_ENABLED: - brcmf_dbg(TRACE, "DHCP session ends\n"); + brcmf_dbg(INFO, "DHCP session ends\n"); if (btci->bt_state != BRCMF_BT_DHCP_IDLE && vif == btci->vif) { brcmf_btcoex_dhcp_end(btci); } break; default: - brcmf_dbg(TRACE, "Unknown mode, ignored\n"); + brcmf_dbg(INFO, "Unknown mode, ignored\n"); } return 0; } From 5e787f7588ff69d3bcd546a64bf70e58a4e98fc5 Mon Sep 17 00:00:00 2001 From: Daniel Kim Date: Sat, 21 Jun 2014 12:11:18 +0200 Subject: [PATCH 187/274] brcmfmac: Don't control mpc setting during scan operation Instead of controlling mpc setting during scan operation, initialize mpc setting and then let firmware take care of it. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Signed-off-by: Daniel Kim [arend@broadcom.com: keep mpc setting for bcm4329] Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/dhd_common.c | 7 +++++++ .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 16 +++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c index ed3e32ce8c23..d991f8e3d9ec 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c @@ -282,6 +282,13 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) ptr = strrchr(buf, ' ') + 1; strlcpy(ifp->drvr->fwver, ptr, sizeof(ifp->drvr->fwver)); + /* set mpc */ + err = brcmf_fil_iovar_int_set(ifp, "mpc", 1); + if (err) { + brcmf_err("failed setting mpc\n"); + goto done; + } + /* * Setup timeout if Beacons are lost and roam is off to report * link down diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 46b57eafa130..9682cf213ec4 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -590,6 +590,12 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, } } +static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc) +{ + if ((brcmf_get_chip_info(ifp) >> 4) == 0x4329) + brcmf_set_mpc(ifp, mpc); +} + void brcmf_set_mpc(struct brcmf_if *ifp, int mpc) { s32 err = 0; @@ -643,7 +649,7 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, brcmf_err("Scan abort failed\n"); } - brcmf_set_mpc(ifp, 1); + brcmf_scan_config_mpc(ifp, 1); /* * e-scan can be initiated by scheduled scan @@ -922,7 +928,7 @@ brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy, brcmf_err("error (%d)\n", err); return err; } - brcmf_set_mpc(ifp, 0); + brcmf_scan_config_mpc(ifp, 0); results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf; results->version = 0; results->count = 0; @@ -930,7 +936,7 @@ brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy, err = escan->run(cfg, ifp, request, WL_ESCAN_ACTION_START); if (err) - brcmf_set_mpc(ifp, 1); + brcmf_scan_config_mpc(ifp, 1); return err; } @@ -1021,7 +1027,7 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif, brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err); goto scan_out; } - brcmf_set_mpc(ifp, 0); + brcmf_scan_config_mpc(ifp, 0); err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, &sr->ssid_le, sizeof(sr->ssid_le)); if (err) { @@ -1031,7 +1037,7 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif, else brcmf_err("WLC_SCAN error (%d)\n", err); - brcmf_set_mpc(ifp, 1); + brcmf_scan_config_mpc(ifp, 1); goto scan_out; } } From c94b7e6cb223986a1eaf7bd4c49f2ab758752649 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 21 Jun 2014 12:11:19 +0200 Subject: [PATCH 188/274] brcmfmac: reduce log level in fwil if firmware returns error The users of the fwil put an error message in the log so there is no need to do the same in the lower level functions in fwil when the firmware on the device returns an error. Some errors can be ignored for the driver to function and this will avoid driver users to point at the low-level error message as potential bug. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/fwil.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c index 59a5af5bf994..ded328f80cd1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c @@ -54,7 +54,7 @@ brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set) if (err >= 0) err = 0; else - brcmf_err("Failed err=%d\n", err); + brcmf_dbg(FIL, "Failed err=%d\n", err); return err; } From ed03033e3033c98e83d45bf4cdca1efdfd86e98e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sun, 22 Jun 2014 23:18:21 +0200 Subject: [PATCH 189/274] b43: N-PHY: update code for sending sample tone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/phy_n.c | 36 +++++++++++++++----------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index 6398c7e45ab7..d9409b446ac1 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -1248,7 +1248,8 @@ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max, /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RunSamples */ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops, - u16 wait, bool iqmode, bool dac_test) + u16 wait, bool iqmode, bool dac_test, + bool modify_bbmult) { struct b43_phy_n *nphy = dev->phy.n; int i; @@ -1262,12 +1263,10 @@ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops, nphy->bb_mult_save = (tmp & 0xFFFF) | 0x80000000; } - /* TODO: add modify_bbmult argument */ - if (!b43_is_40mhz(dev)) - tmp = 0x6464; - else - tmp = 0x4747; - b43_ntab_write(dev, B43_NTAB16(15, 87), tmp); + if (modify_bbmult) { + tmp = !b43_is_40mhz(dev) ? 0x6464 : 0x4747; + b43_ntab_write(dev, B43_NTAB16(15, 87), tmp); + } b43_phy_write(dev, B43_NPHY_SAMP_DEPCNT, (samps - 1)); @@ -1285,10 +1284,8 @@ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops, b43_phy_mask(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x7FFF); b43_phy_set(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8000); } else { - if (dac_test) - b43_phy_write(dev, B43_NPHY_SAMP_CMD, 5); - else - b43_phy_write(dev, B43_NPHY_SAMP_CMD, 1); + tmp = dac_test ? 5 : 1; + b43_phy_write(dev, B43_NPHY_SAMP_CMD, tmp); } for (i = 0; i < 100; i++) { if (!(b43_phy_read(dev, B43_NPHY_RFSEQST) & 1)) { @@ -2947,12 +2944,13 @@ static void b43_nphy_workarounds(struct b43_wldev *dev) * http://bcm-v4.sipsolutions.net/802.11/PHY/N/TXTone */ static int b43_nphy_tx_tone(struct b43_wldev *dev, u32 freq, u16 max_val, - bool iqmode, bool dac_test) + bool iqmode, bool dac_test, bool modify_bbmult) { u16 samp = b43_nphy_gen_load_samples(dev, freq, max_val, dac_test); if (samp == 0) return -1; - b43_nphy_run_samples(dev, samp, 0xFFFF, 0, iqmode, dac_test); + b43_nphy_run_samples(dev, samp, 0xFFFF, 0, iqmode, dac_test, + modify_bbmult); return 0; } @@ -3390,7 +3388,7 @@ static void b43_nphy_tx_power_ctl_idle_tssi(struct b43_wldev *dev) b43_nphy_rf_ctl_override(dev, 0x2000, 0, 3, false); b43_nphy_stop_playback(dev); - b43_nphy_tx_tone(dev, 0xFA0, 0, false, false); + b43_nphy_tx_tone(dev, 4000, 0, false, false, false); udelay(20); tmp = b43_nphy_poll_rssi(dev, N_RSSI_TSSI_2G, rssi, 1); b43_nphy_stop_playback(dev); @@ -4648,9 +4646,9 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev, if (nphy->mphase_cal_phase_id > 2) b43_nphy_run_samples(dev, (b43_is_40mhz(dev) ? 40 : 20) * 8, - 0xFFFF, 0, true, false); + 0xFFFF, 0, true, false, false); else - error = b43_nphy_tx_tone(dev, freq, 250, true, false); + error = b43_nphy_tx_tone(dev, freq, 250, true, false, false); if (error == 0) { if (nphy->mphase_cal_phase_id > 2) { @@ -4973,11 +4971,11 @@ static int b43_nphy_rev2_cal_rx_iq(struct b43_wldev *dev, if (playtone) { ret = b43_nphy_tx_tone(dev, 4000, (nphy->rxcalparams & 0xFFFF), - false, false); + false, false, true); playtone = false; } else { - b43_nphy_run_samples(dev, 160, 0xFFFF, 0, - false, false); + b43_nphy_run_samples(dev, 160, 0xFFFF, 0, false, + false, true); } if (ret == 0) { From 8ac3a2aa7224a7d05f1a71240663c80532969791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sun, 22 Jun 2014 23:18:22 +0200 Subject: [PATCH 190/274] b43: N-PHY: update low-pass filter setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for external PA and clean code a bit. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/phy_n.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index d9409b446ac1..0fb5e14a4554 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -3706,21 +3706,28 @@ static void b43_nphy_pa_override(struct b43_wldev *dev, bool enable) } } -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxLpFbw */ -static void b43_nphy_tx_lp_fbw(struct b43_wldev *dev) +/* + * TX low-pass filter bandwidth setup + * http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxLpFbw + */ +static void b43_nphy_tx_lpf_bw(struct b43_wldev *dev) { u16 tmp; - if (dev->phy.rev >= 3) { - if (b43_nphy_ipa(dev)) { - tmp = 4; - b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S2, - (((((tmp << 3) | tmp) << 3) | tmp) << 3) | tmp); - } + if (dev->phy.rev < 3 || dev->phy.rev >= 7) + return; - tmp = 1; + if (b43_nphy_ipa(dev)) + tmp = b43_is_40mhz(dev) ? 5 : 4; + else + tmp = b43_is_40mhz(dev) ? 3 : 1; + b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S2, + (tmp << 9) | (tmp << 6) | (tmp << 3) | tmp); + + if (b43_nphy_ipa(dev)) { + tmp = b43_is_40mhz(dev) ? 4 : 1; b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S2, - (((((tmp << 3) | tmp) << 3) | tmp) << 3) | tmp); + (tmp << 9) | (tmp << 6) | (tmp << 3) | tmp); } } @@ -5347,7 +5354,7 @@ static int b43_phy_initn(struct b43_wldev *dev) b43_phy_write(dev, B43_NPHY_TXMACDELAY, 0x0320); if (phy->rev >= 3 && phy->rev <= 6) b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x0032); - b43_nphy_tx_lp_fbw(dev); + b43_nphy_tx_lpf_bw(dev); if (phy->rev >= 3) b43_nphy_spur_workaround(dev); @@ -5433,7 +5440,7 @@ static void b43_nphy_channel_setup(struct b43_wldev *dev, if (dev->phy.rev < 3) b43_nphy_adjust_lna_gain_table(dev); - b43_nphy_tx_lp_fbw(dev); + b43_nphy_tx_lpf_bw(dev); if (dev->phy.rev >= 3 && dev->phy.n->spur_avoid != B43_SPUR_AVOID_DISABLE) { From 8a607208f522e6b37a93c9258d28d75c15263961 Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Mon, 23 Jun 2014 23:48:17 +0200 Subject: [PATCH 191/274] rtlwifi/rtl8192de: Fix media status register mask bt_msr & 0xfc will never match 0x3. Fix this by using a mask that actually matches the available types. Signed-off-by: Rickard Strandqvist Reviewed-by: Peter Wu Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/rtl8192de/hw.c | 2 +- drivers/net/wireless/rtlwifi/rtl8192de/reg.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/hw.c b/drivers/net/wireless/rtlwifi/rtl8192de/hw.c index 2b08671004a0..280c3da42993 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/hw.c @@ -1128,7 +1128,7 @@ static int _rtl92de_set_media_status(struct ieee80211_hw *hw, } rtl_write_byte(rtlpriv, REG_CR + 2, bt_msr); rtlpriv->cfg->ops->led_control(hw, ledaction); - if ((bt_msr & 0xfc) == MSR_AP) + if ((bt_msr & MSR_MASK) == MSR_AP) rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00); else rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66); diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/reg.h b/drivers/net/wireless/rtlwifi/rtl8192de/reg.h index 7f29b8d765b3..315a298bab06 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/reg.h +++ b/drivers/net/wireless/rtlwifi/rtl8192de/reg.h @@ -369,6 +369,7 @@ #define MSR_ADHOC 0x01 #define MSR_INFRA 0x02 #define MSR_AP 0x03 +#define MSR_MASK 0x03 /* 6. Adaptive Control Registers (Offset: 0x0160 - 0x01CF) */ /* ----------------------------------------------------- */ From 965ec74110011dbcb4e6a7059c7b4d31ee95c33a Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Mon, 23 Jun 2014 23:53:55 +0200 Subject: [PATCH 192/274] rtlwifi/rtl8192c[eu]: Fix media status register mask bt_msr & 0xfc will never match 0x3. Fix this by using a mask that actually matches the available types. Signed-off-by: Rickard Strandqvist Reviewed-by: Peter Wu Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/rtl8192ce/hw.c | 2 +- drivers/net/wireless/rtlwifi/rtl8192ce/reg.h | 1 + drivers/net/wireless/rtlwifi/rtl8192cu/hw.c | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c index cdecb0fd4d8e..e2736929b5d0 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c @@ -1206,7 +1206,7 @@ static int _rtl92ce_set_media_status(struct ieee80211_hw *hw, rtl_write_byte(rtlpriv, (MSR), bt_msr); rtlpriv->cfg->ops->led_control(hw, ledaction); - if ((bt_msr & 0xfc) == MSR_AP) + if ((bt_msr & MSR_MASK) == MSR_AP) rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00); else rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66); diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h b/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h index ed703a1b3b7c..dc8460c0b32f 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h @@ -375,6 +375,7 @@ #define MSR_ADHOC 0x01 #define MSR_INFRA 0x02 #define MSR_AP 0x03 +#define MSR_MASK 0x03 #define RRSR_RSC_OFFSET 21 #define RRSR_SHORT_OFFSET 23 diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c index a903c2671b4d..270cbffcac70 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c @@ -1360,7 +1360,7 @@ static int _rtl92cu_set_media_status(struct ieee80211_hw *hw, } rtl_write_byte(rtlpriv, (MSR), bt_msr); rtlpriv->cfg->ops->led_control(hw, ledaction); - if ((bt_msr & 0xfc) == MSR_AP) + if ((bt_msr & MSR_MASK) == MSR_AP) rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00); else rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66); From 1dabe76c348f9ced521e412b9bd63ee5ec4924fd Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Tue, 24 Jun 2014 00:00:22 +0200 Subject: [PATCH 193/274] rtlwifi/rtl8188ee: Fix media status register mask bt_msr & 0xfc will never match 0x3. Fix this by using a mask that actually matches the available types. Signed-off-by: Rickard Strandqvist Reviewed-by: Peter Wu Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/rtl8188ee/hw.c | 2 +- drivers/net/wireless/rtlwifi/rtl8188ee/reg.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c b/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c index b14cf5a10f44..d840ad7bdf65 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c @@ -1231,7 +1231,7 @@ static int _rtl88ee_set_media_status(struct ieee80211_hw *hw, rtl_write_byte(rtlpriv, (MSR), bt_msr); rtlpriv->cfg->ops->led_control(hw, ledaction); - if ((bt_msr & 0xfc) == MSR_AP) + if ((bt_msr & MSR_MASK) == MSR_AP) rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00); else rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66); diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/reg.h b/drivers/net/wireless/rtlwifi/rtl8188ee/reg.h index 7af85cfa8f87..cd7e7a527133 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/reg.h +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/reg.h @@ -411,6 +411,7 @@ #define MSR_ADHOC 0x01 #define MSR_INFRA 0x02 #define MSR_AP 0x03 +#define MSR_MASK 0x03 #define RRSR_RSC_OFFSET 21 #define RRSR_SHORT_OFFSET 23 From a3a228e4d6ceb51c54572e4faad7607f997fa342 Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Tue, 24 Jun 2014 00:05:24 +0200 Subject: [PATCH 194/274] rtlwifi/rtl8723ae: Replace magic number by macro For consistency with other drivers, replace a magic number by a macro. Signed-off-by: Rickard Strandqvist Reviewed-by: Peter Wu Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/rtl8723ae/hw.c | 2 +- drivers/net/wireless/rtlwifi/rtl8723ae/reg.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c index 87f69166a7ed..539e53987372 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c @@ -1109,7 +1109,7 @@ static int _rtl8723ae_set_media_status(struct ieee80211_hw *hw, rtl_write_byte(rtlpriv, (MSR), bt_msr); rtlpriv->cfg->ops->led_control(hw, ledaction); - if ((bt_msr & 0x03) == MSR_AP) + if ((bt_msr & MSR_MASK) == MSR_AP) rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00); else rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66); diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/reg.h b/drivers/net/wireless/rtlwifi/rtl8723ae/reg.h index 64376b38708b..ce2c66fd9eee 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/reg.h +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/reg.h @@ -361,6 +361,7 @@ #define MSR_ADHOC 0x01 #define MSR_INFRA 0x02 #define MSR_AP 0x03 +#define MSR_MASK 0x03 #define RRSR_RSC_OFFSET 21 #define RRSR_SHORT_OFFSET 23 From e5c3ef3652d8a05890cf49c1e2895f3d1fbd7f41 Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Tue, 24 Jun 2014 00:07:13 +0200 Subject: [PATCH 195/274] rtlwifi/rtl8723be: Replace magic number by macro For consistency with other drivers, replace a magic number by a macro. Signed-off-by: Rickard Strandqvist Reviewed-by: Peter Wu Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/rtl8723be/hw.c | 2 +- drivers/net/wireless/rtlwifi/rtl8723be/reg.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/hw.c b/drivers/net/wireless/rtlwifi/rtl8723be/hw.c index 3d555495b453..3cd286930fe0 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8723be/hw.c @@ -1197,7 +1197,7 @@ static int _rtl8723be_set_media_status(struct ieee80211_hw *hw, } rtl_write_byte(rtlpriv, (MSR), bt_msr); rtlpriv->cfg->ops->led_control(hw, ledaction); - if ((bt_msr & 0x03) == MSR_AP) + if ((bt_msr & MSR_MASK) == MSR_AP) rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00); else rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66); diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/reg.h b/drivers/net/wireless/rtlwifi/rtl8723be/reg.h index 4c653fab8795..3006849ed439 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/reg.h +++ b/drivers/net/wireless/rtlwifi/rtl8723be/reg.h @@ -412,6 +412,7 @@ #define MSR_ADHOC 0x01 #define MSR_INFRA 0x02 #define MSR_AP 0x03 +#define MSR_MASK 0x03 #define RRSR_RSC_OFFSET 21 #define RRSR_SHORT_OFFSET 23 From a60f99f75dd35179094a4e9ed4ae8ce0c7ece908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 24 Jun 2014 10:50:41 +0200 Subject: [PATCH 196/274] b43: update list and code making a selection of firmware files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clean ucode selection, fix choice of firmware for LCN, drop some goto-s, add new devices. Tested on 14e4:4312, 14e4:4315, 14e4:4328, 14e4:432b, 14e4:4353. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 224 ++++++++++++++++++-------------- 1 file changed, 125 insertions(+), 99 deletions(-) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 9cf07bb7adf8..aae3af2a7a9f 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -2201,52 +2201,78 @@ err_format: return -EPROTO; } +/* http://bcm-v4.sipsolutions.net/802.11/Init/Firmware */ static int b43_try_request_fw(struct b43_request_fw_context *ctx) { struct b43_wldev *dev = ctx->dev; struct b43_firmware *fw = &ctx->dev->fw; + struct b43_phy *phy = &dev->phy; const u8 rev = ctx->dev->dev->core_rev; const char *filename; - u32 tmshigh; int err; - /* Files for HT and LCN were found by trying one by one */ - /* Get microcode */ - if ((rev >= 5) && (rev <= 10)) { - filename = "ucode5"; - } else if ((rev >= 11) && (rev <= 12)) { - filename = "ucode11"; - } else if (rev == 13) { - filename = "ucode13"; - } else if (rev == 14) { - filename = "ucode14"; - } else if (rev == 15) { + filename = NULL; + switch (rev) { + case 42: + if (phy->type == B43_PHYTYPE_AC) + filename = "ucode42"; + break; + case 33: + if (phy->type == B43_PHYTYPE_LCN40) + filename = "ucode33_lcn40"; + break; + case 30: + if (phy->type == B43_PHYTYPE_N) + filename = "ucode30_mimo"; + break; + case 29: + if (phy->type == B43_PHYTYPE_HT) + filename = "ucode29_mimo"; + break; + case 26: + if (phy->type == B43_PHYTYPE_HT) + filename = "ucode26_mimo"; + break; + case 28: + case 25: + if (phy->type == B43_PHYTYPE_N) + filename = "ucode25_mimo"; + else if (phy->type == B43_PHYTYPE_LCN) + filename = "ucode25_lcn"; + break; + case 24: + if (phy->type == B43_PHYTYPE_LCN) + filename = "ucode24_lcn"; + break; + case 23: + if (phy->type == B43_PHYTYPE_N) + filename = "ucode16_mimo"; + break; + case 16 ... 19: + if (phy->type == B43_PHYTYPE_N) + filename = "ucode16_mimo"; + else if (phy->type == B43_PHYTYPE_LP) + filename = "ucode16_lp"; + break; + case 15: filename = "ucode15"; - } else { - switch (dev->phy.type) { - case B43_PHYTYPE_N: - if (rev >= 16) - filename = "ucode16_mimo"; - else - goto err_no_ucode; - break; - case B43_PHYTYPE_HT: - if (rev == 29) - filename = "ucode29_mimo"; - else - goto err_no_ucode; - break; - case B43_PHYTYPE_LCN: - if (rev == 24) - filename = "ucode24_mimo"; - else - goto err_no_ucode; - break; - default: - goto err_no_ucode; - } + break; + case 14: + filename = "ucode14"; + break; + case 13: + filename = "ucode13"; + break; + case 11 ... 12: + filename = "ucode11"; + break; + case 5 ... 10: + filename = "ucode5"; + break; } + if (!filename) + goto err_no_ucode; err = b43_do_request_fw(ctx, filename, &fw->ucode, true); if (err) goto err_load; @@ -2268,117 +2294,117 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) goto err_load; /* Get initvals */ + filename = NULL; switch (dev->phy.type) { - case B43_PHYTYPE_A: - if ((rev >= 5) && (rev <= 10)) { - tmshigh = ssb_read32(dev->dev->sdev, SSB_TMSHIGH); - if (tmshigh & B43_TMSHIGH_HAVE_2GHZ_PHY) - filename = "a0g1initvals5"; - else - filename = "a0g0initvals5"; - } else - goto err_no_initvals; - break; case B43_PHYTYPE_G: - if ((rev >= 5) && (rev <= 10)) - filename = "b0g0initvals5"; - else if (rev >= 13) + if (rev == 13) filename = "b0g0initvals13"; - else - goto err_no_initvals; + else if (rev >= 5 && rev <= 10) + filename = "b0g0initvals5"; break; case B43_PHYTYPE_N: - if (rev >= 16) + if (rev == 30) + filename = "n16initvals30"; + else if (rev == 28 || rev == 25) + filename = "n0initvals25"; + else if (rev == 24) + filename = "n0initvals24"; + else if (rev == 23) + filename = "n0initvals16"; /* What about n0initvals22? */ + else if (rev >= 16 && rev <= 18) filename = "n0initvals16"; - else if ((rev >= 11) && (rev <= 12)) + else if (rev >= 11 && rev <= 12) filename = "n0initvals11"; - else - goto err_no_initvals; break; case B43_PHYTYPE_LP: - if (rev == 13) - filename = "lp0initvals13"; + if (rev >= 16 && rev <= 18) + filename = "lp0initvals16"; + else if (rev == 15) + filename = "lp0initvals15"; else if (rev == 14) filename = "lp0initvals14"; - else if (rev >= 15) - filename = "lp0initvals15"; - else - goto err_no_initvals; + else if (rev == 13) + filename = "lp0initvals13"; break; case B43_PHYTYPE_HT: if (rev == 29) filename = "ht0initvals29"; - else - goto err_no_initvals; + else if (rev == 26) + filename = "ht0initvals26"; break; case B43_PHYTYPE_LCN: if (rev == 24) filename = "lcn0initvals24"; - else - goto err_no_initvals; break; - default: - goto err_no_initvals; + case B43_PHYTYPE_LCN40: + if (rev == 33) + filename = "lcn400initvals33"; + break; + case B43_PHYTYPE_AC: + if (rev == 42) + filename = "ac1initvals42"; + break; } + if (!filename) + goto err_no_initvals; err = b43_do_request_fw(ctx, filename, &fw->initvals, false); if (err) goto err_load; /* Get bandswitch initvals */ + filename = NULL; switch (dev->phy.type) { - case B43_PHYTYPE_A: - if ((rev >= 5) && (rev <= 10)) { - tmshigh = ssb_read32(dev->dev->sdev, SSB_TMSHIGH); - if (tmshigh & B43_TMSHIGH_HAVE_2GHZ_PHY) - filename = "a0g1bsinitvals5"; - else - filename = "a0g0bsinitvals5"; - } else if (rev >= 11) - filename = NULL; - else - goto err_no_initvals; - break; case B43_PHYTYPE_G: - if ((rev >= 5) && (rev <= 10)) + if (rev == 13) + filename = "b0g0bsinitvals13"; + else if (rev >= 5 && rev <= 10) filename = "b0g0bsinitvals5"; - else if (rev >= 11) - filename = NULL; - else - goto err_no_initvals; break; case B43_PHYTYPE_N: - if (rev >= 16) + if (rev == 30) + filename = "n16bsinitvals30"; + else if (rev == 28 || rev == 25) + filename = "n0bsinitvals25"; + else if (rev == 24) + filename = "n0bsinitvals24"; + else if (rev == 23) + filename = "n0bsinitvals16"; /* What about n0bsinitvals22? */ + else if (rev >= 16 && rev <= 18) filename = "n0bsinitvals16"; - else if ((rev >= 11) && (rev <= 12)) + else if (rev >= 11 && rev <= 12) filename = "n0bsinitvals11"; - else - goto err_no_initvals; break; case B43_PHYTYPE_LP: - if (rev == 13) - filename = "lp0bsinitvals13"; + if (rev >= 16 && rev <= 18) + filename = "lp0bsinitvals16"; + else if (rev == 15) + filename = "lp0bsinitvals15"; else if (rev == 14) filename = "lp0bsinitvals14"; - else if (rev >= 15) - filename = "lp0bsinitvals15"; - else - goto err_no_initvals; + else if (rev == 13) + filename = "lp0bsinitvals13"; break; case B43_PHYTYPE_HT: if (rev == 29) filename = "ht0bsinitvals29"; - else - goto err_no_initvals; + else if (rev == 26) + filename = "ht0bsinitvals26"; break; case B43_PHYTYPE_LCN: if (rev == 24) filename = "lcn0bsinitvals24"; - else - goto err_no_initvals; break; - default: - goto err_no_initvals; + case B43_PHYTYPE_LCN40: + if (rev == 33) + filename = "lcn400bsinitvals33"; + break; + case B43_PHYTYPE_AC: + if (rev == 42) + filename = "ac1bsinitvals42"; + break; } + if (!filename) + goto err_no_initvals; err = b43_do_request_fw(ctx, filename, &fw->initvals_band, false); if (err) goto err_load; From efeb1430160109d4f1e21d28e79cf6093359ea01 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 24 Jun 2014 22:27:36 +0530 Subject: [PATCH 197/274] ath9k_hw: update CCK loop coefficients for AR953x 1.0 Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar953x_initvals.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h index 8e5c3b9786e3..1907f863ab25 100644 --- a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h @@ -219,7 +219,7 @@ static const u32 qca953x_1p0_baseband_core[][2] = { {0x00009d04, 0x40206c10}, {0x00009d08, 0x009c4060}, {0x00009d0c, 0x9883800a}, - {0x00009d10, 0x01884061}, + {0x00009d10, 0x018848c6}, {0x00009d14, 0x00c0040b}, {0x00009d18, 0x00000000}, {0x00009e08, 0x0038230c}, From c01a72987162fb7b29769522c0a55aae7a203ccc Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 24 Jun 2014 22:27:37 +0530 Subject: [PATCH 198/274] ath9k_hw: Add QCA953x 2.0 initvals Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_hw.c | 15 +- .../net/wireless/ath/ath9k/ar953x_initvals.h | 199 ++++++++++++++++++ drivers/net/wireless/ath/ath9k/reg.h | 4 + 3 files changed, 214 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index ec1da0cc25f5..ddef9eedbac6 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -314,10 +314,17 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) qca953x_1p0_mac_core); INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST], qca953x_1p0_mac_postamble); - INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE], - qca953x_1p0_baseband_core); - INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST], - qca953x_1p0_baseband_postamble); + if (AR_SREV_9531_20(ah)) { + INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE], + qca953x_2p0_baseband_core); + INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST], + qca953x_2p0_baseband_postamble); + } else { + INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE], + qca953x_1p0_baseband_core); + INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST], + qca953x_1p0_baseband_postamble); + } INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE], qca953x_1p0_radio_core); INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST], diff --git a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h index 1907f863ab25..812a9d787bf3 100644 --- a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h @@ -715,4 +715,203 @@ static const u32 qca953x_1p1_modes_no_xpa_tx_gain_table[][2] = { {0x00016448, 0x6c927a70}, }; +static const u32 qca953x_2p0_baseband_core[][2] = { + /* Addr allmodes */ + {0x00009800, 0xafe68e30}, + {0x00009804, 0xfd14e000}, + {0x00009808, 0x9c0a9f6b}, + {0x0000980c, 0x04900000}, + {0x00009814, 0x0280c00a}, + {0x00009818, 0x00000000}, + {0x0000981c, 0x00020028}, + {0x00009834, 0x6400a190}, + {0x00009838, 0x0108ecff}, + {0x0000983c, 0x14000600}, + {0x00009880, 0x201fff00}, + {0x00009884, 0x00001042}, + {0x000098a4, 0x00200400}, + {0x000098b0, 0x32840bbe}, + {0x000098bc, 0x00000002}, + {0x000098d0, 0x004b6a8e}, + {0x000098d4, 0x00000820}, + {0x000098dc, 0x00000000}, + {0x000098f0, 0x00000000}, + {0x000098f4, 0x00000000}, + {0x00009c04, 0xff55ff55}, + {0x00009c08, 0x0320ff55}, + {0x00009c0c, 0x00000000}, + {0x00009c10, 0x00000000}, + {0x00009c14, 0x00046384}, + {0x00009c18, 0x05b6b440}, + {0x00009c1c, 0x00b6b440}, + {0x00009d00, 0xc080a333}, + {0x00009d04, 0x40206c10}, + {0x00009d08, 0x009c4060}, + {0x00009d0c, 0x9883800a}, + {0x00009d10, 0x018848c6}, + {0x00009d14, 0x00c0040b}, + {0x00009d18, 0x00000000}, + {0x00009e08, 0x0038230c}, + {0x00009e24, 0x990bb515}, + {0x00009e28, 0x0c6f0000}, + {0x00009e30, 0x06336f77}, + {0x00009e34, 0x6af6532f}, + {0x00009e38, 0x0cc80c00}, + {0x00009e40, 0x0d261820}, + {0x00009e4c, 0x00001004}, + {0x00009e50, 0x00ff03f1}, + {0x00009fc0, 0x813e4788}, + {0x00009fc4, 0x0001efb5}, + {0x00009fcc, 0x40000014}, + {0x00009fd0, 0x02993b93}, + {0x0000a20c, 0x00000000}, + {0x0000a220, 0x00000000}, + {0x0000a224, 0x00000000}, + {0x0000a228, 0x10002310}, + {0x0000a23c, 0x00000000}, + {0x0000a244, 0x0c000000}, + {0x0000a248, 0x00000140}, + {0x0000a2a0, 0x00000007}, + {0x0000a2c0, 0x00000007}, + {0x0000a2c8, 0x00000000}, + {0x0000a2d4, 0x00000000}, + {0x0000a2ec, 0x00000000}, + {0x0000a2f0, 0x00000000}, + {0x0000a2f4, 0x00000000}, + {0x0000a2f8, 0x00000000}, + {0x0000a344, 0x00000000}, + {0x0000a34c, 0x00000000}, + {0x0000a350, 0x0000a000}, + {0x0000a364, 0x00000000}, + {0x0000a370, 0x00000000}, + {0x0000a390, 0x00000001}, + {0x0000a394, 0x00000444}, + {0x0000a398, 0x001f0e0f}, + {0x0000a39c, 0x0075393f}, + {0x0000a3a0, 0xb79f6427}, + {0x0000a3a4, 0x000400ff}, + {0x0000a3a8, 0x6a6a6a6a}, + {0x0000a3ac, 0x6a6a6a6a}, + {0x0000a3b0, 0x00c8641a}, + {0x0000a3b4, 0x0000001a}, + {0x0000a3b8, 0x0088642a}, + {0x0000a3bc, 0x000001fa}, + {0x0000a3c0, 0x20202020}, + {0x0000a3c4, 0x22222220}, + {0x0000a3c8, 0x20200020}, + {0x0000a3cc, 0x20202020}, + {0x0000a3d0, 0x20202020}, + {0x0000a3d4, 0x20202020}, + {0x0000a3d8, 0x20202020}, + {0x0000a3dc, 0x20202020}, + {0x0000a3e0, 0x20202020}, + {0x0000a3e4, 0x20202020}, + {0x0000a3e8, 0x20202020}, + {0x0000a3ec, 0x20202020}, + {0x0000a3f0, 0x00000000}, + {0x0000a3f4, 0x00000000}, + {0x0000a3f8, 0x0c9bd380}, + {0x0000a3fc, 0x000f0f01}, + {0x0000a400, 0x8fa91f01}, + {0x0000a404, 0x00000000}, + {0x0000a408, 0x0e79e5c6}, + {0x0000a40c, 0x00820820}, + {0x0000a414, 0x1ce42108}, + {0x0000a418, 0x2d001dce}, + {0x0000a41c, 0x1ce73908}, + {0x0000a420, 0x000001ce}, + {0x0000a424, 0x1ce738e7}, + {0x0000a428, 0x000001ce}, + {0x0000a42c, 0x1ce739ce}, + {0x0000a430, 0x1ce739ce}, + {0x0000a434, 0x00000000}, + {0x0000a438, 0x00001801}, + {0x0000a43c, 0x00100000}, + {0x0000a444, 0x00000000}, + {0x0000a448, 0x05000080}, + {0x0000a44c, 0x00000001}, + {0x0000a450, 0x00010000}, + {0x0000a458, 0x00000000}, + {0x0000a644, 0xbfad9d74}, + {0x0000a648, 0x0048060a}, + {0x0000a64c, 0x00003c37}, + {0x0000a670, 0x03020100}, + {0x0000a674, 0x09080504}, + {0x0000a678, 0x0d0c0b0a}, + {0x0000a67c, 0x13121110}, + {0x0000a680, 0x31301514}, + {0x0000a684, 0x35343332}, + {0x0000a688, 0x00000036}, + {0x0000a690, 0x08000838}, + {0x0000a7cc, 0x00000000}, + {0x0000a7d0, 0x00000000}, + {0x0000a7d4, 0x00000004}, + {0x0000a7dc, 0x00000000}, + {0x0000a8d0, 0x004b6a8e}, + {0x0000a8d4, 0x00000820}, + {0x0000a8dc, 0x00000000}, + {0x0000a8f0, 0x00000000}, + {0x0000a8f4, 0x00000000}, + {0x0000b2d0, 0x00000080}, + {0x0000b2d4, 0x00000000}, + {0x0000b2ec, 0x00000000}, + {0x0000b2f0, 0x00000000}, + {0x0000b2f4, 0x00000000}, + {0x0000b2f8, 0x00000000}, + {0x0000b408, 0x0e79e5c0}, + {0x0000b40c, 0x00820820}, + {0x0000b420, 0x00000000}, +}; + +static const u32 qca953x_2p0_baseband_postamble[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011}, + {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e}, + {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, + {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881}, + {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, + {0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c}, + {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4}, + {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0}, + {0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020}, + {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2}, + {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec84d2e}, + {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3379605e, 0x33795d5e}, + {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, + {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, + {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, + {0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcf946222, 0xcf946222}, + {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27}, + {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012}, + {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, + {0x0000a204, 0x005c0ec0, 0x005c0ec4, 0x005c0ec4, 0x005c0ec0}, + {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004}, + {0x0000a22c, 0x07e26a2f, 0x07e26a2f, 0x01026a2f, 0x01026a2f}, + {0x0000a230, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b}, + {0x0000a234, 0x00000fff, 0x10000fff, 0x10000fff, 0x00000fff}, + {0x0000a238, 0xffb01018, 0xffb01018, 0xffb01018, 0xffb01018}, + {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, + {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, + {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002}, + {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01010e0e, 0x01010e0e}, + {0x0000a260, 0x0a021501, 0x0a021501, 0x3a021501, 0x3a021501}, + {0x0000a264, 0x00000e0e, 0x00000e0e, 0x01000e0e, 0x01000e0e}, + {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b}, + {0x0000a284, 0x00000000, 0x00000000, 0x00000010, 0x00000010}, + {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110}, + {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222}, + {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, + {0x0000a2cc, 0x18c50033, 0x18c43433, 0x18c41033, 0x18c44c33}, + {0x0000a2d0, 0x00041982, 0x00041982, 0x00041982, 0x00041982}, + {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b}, + {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, + {0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x001c0000}, + {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, + {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce}, + {0x0000b284, 0x00000000, 0x00000000, 0x00000010, 0x00000010}, +}; + #endif /* INITVALS_953X_H */ diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index f1bbce3f7774..a1499700bcf2 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -813,6 +813,7 @@ #define AR_SREV_VERSION_9531 0x500 #define AR_SREV_REVISION_9531_10 0 #define AR_SREV_REVISION_9531_11 1 +#define AR_SREV_REVISION_9531_20 2 #define AR_SREV_5416(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_5416_PCI) || \ @@ -958,6 +959,9 @@ #define AR_SREV_9531_11(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9531) && \ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9531_11)) +#define AR_SREV_9531_20(_ah) \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9531) && \ + ((_ah)->hw_version.macRev == AR_SREV_REVISION_9531_20)) /* NOTE: When adding chips newer than Peacock, add chip check here */ #define AR_SREV_9580_10_OR_LATER(_ah) \ From ddbbd9e854f00f4f78d5f3df4e374af3d9eec5b2 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 24 Jun 2014 22:27:38 +0530 Subject: [PATCH 199/274] ath9k_hw: fix XPABIASLEVEL settings for AR9531 Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_eeprom.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index 235053ba7737..80c6eacbda53 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -3535,7 +3535,8 @@ static void ar9003_hw_xpa_bias_level_apply(struct ath_hw *ah, bool is2ghz) { int bias = ar9003_modal_header(ah, is2ghz)->xpaBiasLvl; - if (AR_SREV_9485(ah) || AR_SREV_9330(ah) || AR_SREV_9340(ah)) + if (AR_SREV_9485(ah) || AR_SREV_9330(ah) || AR_SREV_9340(ah) || + AR_SREV_9531(ah)) REG_RMW_FIELD(ah, AR_CH0_TOP2, AR_CH0_TOP2_XPABIASLVL, bias); else if (AR_SREV_9462(ah) || AR_SREV_9550(ah) || AR_SREV_9565(ah)) REG_RMW_FIELD(ah, AR_CH0_TOP, AR_CH0_TOP_XPABIASLVL, bias); From af2db444855a574cb58db1f5f6b7a53f2c0fefda Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 24 Jun 2014 22:27:39 +0530 Subject: [PATCH 200/274] ath9k_hw: fix tx gain table index for AR953x Fix tx gain table index on fast channel change for AR953x. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_phy.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 8927fc34d84c..542a8d51d3b0 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -1552,13 +1552,15 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah, u8 *ini_reloaded) { unsigned int regWrites = 0; - u32 modesIndex; + u32 modesIndex, txgain_index; if (IS_CHAN_5GHZ(chan)) modesIndex = IS_CHAN_HT40(chan) ? 2 : 1; else modesIndex = IS_CHAN_HT40(chan) ? 3 : 4; + txgain_index = AR_SREV_9531(ah) ? 1 : modesIndex; + if (modesIndex == ah->modes_index) { *ini_reloaded = false; goto set_rfmode; @@ -1573,7 +1575,7 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah, ar9003_hw_prog_ini(ah, &ah->ini_radio_post_sys2ant, modesIndex); - REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites); + REG_WRITE_ARRAY(&ah->iniModesTxGain, txgain_index, regWrites); if (AR_SREV_9462_20_OR_LATER(ah)) { /* From 76ac9ed6ffb32eb38d2f208204159ae0cd3394b6 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 24 Jun 2014 22:27:40 +0530 Subject: [PATCH 201/274] ath9k_hw: Fix pll2_divfrac for AR953x Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index ace4fe2740d4..fd0158fdf144 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -791,7 +791,8 @@ static void ath9k_hw_init_pll(struct ath_hw *ah, refdiv = 5; } else { pll2_divint = 0x11; - pll2_divfrac = 0x26666; + pll2_divfrac = + AR_SREV_9531(ah) ? 0x26665 : 0x26666; refdiv = 1; } } From 80140b71e0bd2d9f6762791567cfd0aa04f20419 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 24 Jun 2014 15:39:43 -0700 Subject: [PATCH 202/274] p54: use request_firmware_direct() for optional EEPROM override The p54 driver uses request_firmware() twice, once for actual firmware and then another time for an optional user overide on EEPROM, 3826.eeprom. The custom EEPROM is optional but if not present we'll introduce an extra lag of 60 seconds with udev present. Annotate we don't want udev nonsense here to avoid the lag in case its not present. This was found with the following SmPL patch. @ firmware_not_critical @ expression cf; expression config_file; expression dev; int ret; identifier l; statement S; @@ - ret = request_firmware(&cf, config_file, dev); + ret = request_firmware_direct(&cf, config_file, dev); if (ret < 0) { ... when != goto l; when != return ret; when any } else { ... release_firmware(cf); ... } Cc: Takashi Iwai Cc: Christian Lamparter Cc: linux-wireless@vger.kernel.org Cc: cocci@systeme.lip6.fr Signed-off-by: Luis R. Rodriguez Acked-By: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c index de15171e2cd8..63de5eed25cf 100644 --- a/drivers/net/wireless/p54/p54spi.c +++ b/drivers/net/wireless/p54/p54spi.c @@ -193,7 +193,7 @@ static int p54spi_request_eeprom(struct ieee80211_hw *dev) /* allow users to customize their eeprom. */ - ret = request_firmware(&eeprom, "3826.eeprom", &priv->spi->dev); + ret = request_firmware_direct(&eeprom, "3826.eeprom", &priv->spi->dev); if (ret < 0) { #ifdef CONFIG_P54_SPI_DEFAULT_EEPROM dev_info(&priv->spi->dev, "loading default eeprom...\n"); From 688df7ec26ffde2c8d79b0ac1a3378155da59ff2 Mon Sep 17 00:00:00 2001 From: Jahnavi Meher Date: Wed, 25 Jun 2014 15:47:45 +0530 Subject: [PATCH 203/274] rsi: Fixed errors and warnings reported by static code analyzers. Fixed a potential buffer overflow in 'rsi_rates' and a sparse warning related to difference in endianness in rsi_91x_mgmt.c. Signed-off-by: Jahnavi Meher Signed-off-by: John W. Linville --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 83abe580cf5f..8d110fd9eba1 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -1055,7 +1055,8 @@ static int rsi_send_auto_rate_request(struct rsi_common *common) rate_table_offset = 4; } - for (ii = 0, jj = 0; ii < ARRAY_SIZE(rsi_rates); ii++) { + for (ii = 0, jj = 0; + ii < (ARRAY_SIZE(rsi_rates) - rate_table_offset); ii++) { if (rate_bitmap & BIT(ii)) { selected_rates[jj++] = (rsi_rates[ii + rate_table_offset].bitrate / 5); @@ -1103,7 +1104,7 @@ static int rsi_send_auto_rate_request(struct rsi_common *common) } for (; ii < RSI_TBL_SZ; ii++) - auto_rate->supported_rates[ii] = min_rate; + auto_rate->supported_rates[ii] = cpu_to_le16(min_rate); auto_rate->num_supported_rates = cpu_to_le16(num_supported_rates * 2); auto_rate->moderate_rate_inx = cpu_to_le16(num_supported_rates / 2); From d51193d4ae181df37e9196a54b1fe3360ca920f2 Mon Sep 17 00:00:00 2001 From: Jahnavi Meher Date: Wed, 25 Jun 2014 15:48:15 +0530 Subject: [PATCH 204/274] rsi: Fixed warnings reported by static code analyzers. Fixed a warning related to incorrect return type and removed an unnecessary semi colon. Signed-off-by: Jahnavi Meher Signed-off-by: John W. Linville --- drivers/net/wireless/rsi/rsi_91x_core.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_core.c b/drivers/net/wireless/rsi/rsi_91x_core.c index 1cd0914db270..f3d3995d8f6b 100644 --- a/drivers/net/wireless/rsi/rsi_91x_core.c +++ b/drivers/net/wireless/rsi/rsi_91x_core.c @@ -92,6 +92,7 @@ static u32 rsi_get_num_pkts_dequeue(struct rsi_common *common, u8 q_num) struct sk_buff *skb; u32 pkt_cnt = 0; s16 txop = common->tx_qinfo[q_num].txop * 32; + __le16 r_txop; struct ieee80211_rate rate; rate.bitrate = RSI_RATE_MCS0 * 5 * 10; /* Convert to Kbps */ @@ -104,10 +105,11 @@ static u32 rsi_get_num_pkts_dequeue(struct rsi_common *common, u8 q_num) return 0; do { - txop -= ieee80211_generic_frame_duration(adapter->hw, - adapter->vifs[0], - common->band, - skb->len, &rate); + r_txop = ieee80211_generic_frame_duration(adapter->hw, + adapter->vifs[0], + common->band, + skb->len, &rate); + txop -= le16_to_cpu(r_txop); pkt_cnt += 1; /*checking if pkts are still there*/ if (skb_queue_len(&common->tx_queue[q_num]) - pkt_cnt) @@ -191,7 +193,7 @@ get_queue_num: if (q_num == VO_Q || q_num == VI_Q) { common->pkt_cnt = rsi_get_num_pkts_dequeue(common, q_num); common->pkt_cnt -= 1; - }; + } return q_num; } From ad362984aad7127e46ff2403a85a3706530ac22b Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Wed, 25 Jun 2014 15:46:51 -0400 Subject: [PATCH 205/274] b43: b43_phyops_a can be static MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CC: "Rafał Miłecki" CC: "John W. Linville" Signed-off-by: Fengguang Wu Signed-off-by: John W. Linville --- drivers/net/wireless/b43/phy_a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/b43/phy_a.c b/drivers/net/wireless/b43/phy_a.c index a6c38104693d..25e40432d68b 100644 --- a/drivers/net/wireless/b43/phy_a.c +++ b/drivers/net/wireless/b43/phy_a.c @@ -573,7 +573,7 @@ static void b43_aphy_op_pwork_60sec(struct b43_wldev *dev) {//TODO } -const struct b43_phy_operations b43_phyops_a = { +static const struct b43_phy_operations b43_phyops_a = { .allocate = b43_aphy_op_allocate, .free = b43_aphy_op_free, .prepare_structs = b43_aphy_op_prepare_structs, From 057d32f03d0d5425b3e089d98712d6cc962b705f Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Fri, 20 Jun 2014 17:07:42 -0700 Subject: [PATCH 206/274] Revert "mwifiex: Use the proper interfaces" This reverts commit a82fc3b4a2bceb7c6587249cb690342eb5065979. Thomas corrected me on that I misunderstood Johannes' comment for net_timedelta() and the ktime_get_real() usage inside __net_timestamp(). Cc: Thomas Gleixner Cc: Johannes Berg Cc: John W. Linville Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 4 +++- drivers/net/wireless/mwifiex/main.c | 4 +++- drivers/net/wireless/mwifiex/tdls.c | 8 ++++++-- drivers/net/wireless/mwifiex/uap_txrx.c | 4 +++- drivers/net/wireless/mwifiex/wmm.c | 9 ++++++++- 5 files changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 15fa7b453372..44b8f4ab3b35 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -121,6 +121,7 @@ mwifiex_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len) u8 addr[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; u16 pkt_len; u32 tx_control = 0, pkt_type = PKT_TYPE_MGMT; + struct timeval tv; pkt_len = len + ETH_ALEN; @@ -142,7 +143,8 @@ mwifiex_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len) len - sizeof(struct ieee80211_hdr_3addr)); skb->priority = LOW_PRIO_TID; - __net_timestamp(skb); + do_gettimeofday(&tv); + skb->tstamp = timeval_to_ktime(tv); return 0; } diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 657504c3c79d..1261d9a41e11 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -609,6 +609,7 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); struct sk_buff *new_skb; struct mwifiex_txinfo *tx_info; + struct timeval tv; dev_dbg(priv->adapter->dev, "data: %lu BSS(%d-%d): Data <= kernel\n", jiffies, priv->bss_type, priv->bss_num); @@ -655,7 +656,8 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) * firmware for aggregate delay calculation for stats and * MSDU lifetime expiry. */ - __net_timestamp(skb); + do_gettimeofday(&tv); + skb->tstamp = timeval_to_ktime(tv); mwifiex_queue_tx_pkt(priv, skb); diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c index 3efbcbe7e891..e73034fbbde9 100644 --- a/drivers/net/wireless/mwifiex/tdls.c +++ b/drivers/net/wireless/mwifiex/tdls.c @@ -530,6 +530,7 @@ int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, { struct sk_buff *skb; struct mwifiex_txinfo *tx_info; + struct timeval tv; int ret; u16 skb_len; @@ -607,7 +608,8 @@ int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, tx_info->bss_num = priv->bss_num; tx_info->bss_type = priv->bss_type; - __net_timestamp(skb); + do_gettimeofday(&tv); + skb->tstamp = timeval_to_ktime(tv); mwifiex_queue_tx_pkt(priv, skb); return 0; @@ -700,6 +702,7 @@ int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer, { struct sk_buff *skb; struct mwifiex_txinfo *tx_info; + struct timeval tv; u8 *pos; u32 pkt_type, tx_control; u16 pkt_len, skb_len; @@ -764,7 +767,8 @@ int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer, pkt_len = skb->len - MWIFIEX_MGMT_FRAME_HEADER_SIZE - sizeof(pkt_len); memcpy(skb->data + MWIFIEX_MGMT_FRAME_HEADER_SIZE, &pkt_len, sizeof(pkt_len)); - __net_timestamp(skb); + do_gettimeofday(&tv); + skb->tstamp = timeval_to_ktime(tv); mwifiex_queue_tx_pkt(priv, skb); return 0; diff --git a/drivers/net/wireless/mwifiex/uap_txrx.c b/drivers/net/wireless/mwifiex/uap_txrx.c index ddfc3c6c1e78..57fa47d2c616 100644 --- a/drivers/net/wireless/mwifiex/uap_txrx.c +++ b/drivers/net/wireless/mwifiex/uap_txrx.c @@ -96,6 +96,7 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv, struct sk_buff *new_skb; struct mwifiex_txinfo *tx_info; int hdr_chop; + struct timeval tv; struct ethhdr *p_ethhdr; uap_rx_pd = (struct uap_rxpd *)(skb->data); @@ -191,7 +192,8 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv, tx_info->pkt_len = skb->len; } - __net_timestamp(skb); + do_gettimeofday(&tv); + skb->tstamp = timeval_to_ktime(tv); mwifiex_wmm_add_buf_txqueue(priv, skb); atomic_inc(&adapter->tx_pending); atomic_inc(&adapter->pending_bridged_pkts); diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index e8556b6e04ee..8cd123ecebbf 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -878,8 +878,15 @@ u8 mwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv, const struct sk_buff *skb) { - u32 queue_delay = ktime_to_ms(ktime_sub(ktime_get(), skb->tstamp)); u8 ret_val; + struct timeval out_tstamp, in_tstamp; + u32 queue_delay; + + do_gettimeofday(&out_tstamp); + in_tstamp = ktime_to_timeval(skb->tstamp); + + queue_delay = (out_tstamp.tv_sec - in_tstamp.tv_sec) * 1000; + queue_delay += (out_tstamp.tv_usec - in_tstamp.tv_usec) / 1000; /* * Queue delay is passed as a uint8 in units of 2ms (ms shifted From c64800e772a03a8162e107c2d03d06175fff443d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 12 Jun 2014 08:31:34 +0100 Subject: [PATCH 207/274] wireless: mwifiex: Use the proper interfaces Why is converting time formats so desired if there are proper interfaces for this? Signed-off-by: Thomas Gleixner Acked-by: Bing Zhao Cc: "John W. Linville" Cc: linux-wireless@vger.kernel.org Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 4 +--- drivers/net/wireless/mwifiex/main.c | 4 +--- drivers/net/wireless/mwifiex/tdls.c | 8 ++------ drivers/net/wireless/mwifiex/uap_txrx.c | 4 +--- drivers/net/wireless/mwifiex/wmm.c | 9 +-------- 5 files changed, 6 insertions(+), 23 deletions(-) diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 44b8f4ab3b35..15fa7b453372 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -121,7 +121,6 @@ mwifiex_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len) u8 addr[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; u16 pkt_len; u32 tx_control = 0, pkt_type = PKT_TYPE_MGMT; - struct timeval tv; pkt_len = len + ETH_ALEN; @@ -143,8 +142,7 @@ mwifiex_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len) len - sizeof(struct ieee80211_hdr_3addr)); skb->priority = LOW_PRIO_TID; - do_gettimeofday(&tv); - skb->tstamp = timeval_to_ktime(tv); + __net_timestamp(skb); return 0; } diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 1261d9a41e11..657504c3c79d 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -609,7 +609,6 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); struct sk_buff *new_skb; struct mwifiex_txinfo *tx_info; - struct timeval tv; dev_dbg(priv->adapter->dev, "data: %lu BSS(%d-%d): Data <= kernel\n", jiffies, priv->bss_type, priv->bss_num); @@ -656,8 +655,7 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) * firmware for aggregate delay calculation for stats and * MSDU lifetime expiry. */ - do_gettimeofday(&tv); - skb->tstamp = timeval_to_ktime(tv); + __net_timestamp(skb); mwifiex_queue_tx_pkt(priv, skb); diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c index e73034fbbde9..3efbcbe7e891 100644 --- a/drivers/net/wireless/mwifiex/tdls.c +++ b/drivers/net/wireless/mwifiex/tdls.c @@ -530,7 +530,6 @@ int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, { struct sk_buff *skb; struct mwifiex_txinfo *tx_info; - struct timeval tv; int ret; u16 skb_len; @@ -608,8 +607,7 @@ int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, tx_info->bss_num = priv->bss_num; tx_info->bss_type = priv->bss_type; - do_gettimeofday(&tv); - skb->tstamp = timeval_to_ktime(tv); + __net_timestamp(skb); mwifiex_queue_tx_pkt(priv, skb); return 0; @@ -702,7 +700,6 @@ int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer, { struct sk_buff *skb; struct mwifiex_txinfo *tx_info; - struct timeval tv; u8 *pos; u32 pkt_type, tx_control; u16 pkt_len, skb_len; @@ -767,8 +764,7 @@ int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer, pkt_len = skb->len - MWIFIEX_MGMT_FRAME_HEADER_SIZE - sizeof(pkt_len); memcpy(skb->data + MWIFIEX_MGMT_FRAME_HEADER_SIZE, &pkt_len, sizeof(pkt_len)); - do_gettimeofday(&tv); - skb->tstamp = timeval_to_ktime(tv); + __net_timestamp(skb); mwifiex_queue_tx_pkt(priv, skb); return 0; diff --git a/drivers/net/wireless/mwifiex/uap_txrx.c b/drivers/net/wireless/mwifiex/uap_txrx.c index 57fa47d2c616..ddfc3c6c1e78 100644 --- a/drivers/net/wireless/mwifiex/uap_txrx.c +++ b/drivers/net/wireless/mwifiex/uap_txrx.c @@ -96,7 +96,6 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv, struct sk_buff *new_skb; struct mwifiex_txinfo *tx_info; int hdr_chop; - struct timeval tv; struct ethhdr *p_ethhdr; uap_rx_pd = (struct uap_rxpd *)(skb->data); @@ -192,8 +191,7 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv, tx_info->pkt_len = skb->len; } - do_gettimeofday(&tv); - skb->tstamp = timeval_to_ktime(tv); + __net_timestamp(skb); mwifiex_wmm_add_buf_txqueue(priv, skb); atomic_inc(&adapter->tx_pending); atomic_inc(&adapter->pending_bridged_pkts); diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 8cd123ecebbf..94c98a86ebbe 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -878,15 +878,8 @@ u8 mwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv, const struct sk_buff *skb) { + u32 queue_delay = ktime_to_ms(net_timedelta(skb->tstamp)); u8 ret_val; - struct timeval out_tstamp, in_tstamp; - u32 queue_delay; - - do_gettimeofday(&out_tstamp); - in_tstamp = ktime_to_timeval(skb->tstamp); - - queue_delay = (out_tstamp.tv_sec - in_tstamp.tv_sec) * 1000; - queue_delay += (out_tstamp.tv_usec - in_tstamp.tv_usec) / 1000; /* * Queue delay is passed as a uint8 in units of 2ms (ms shifted From 41881354f93a5e82f16c811f95e0700bf99283ec Mon Sep 17 00:00:00 2001 From: Mathy Vanhoef Date: Fri, 13 Jun 2014 23:40:22 +0200 Subject: [PATCH 208/274] ath5k: support for FIF_FCSFAIL filter When the FIF_FCSFAIL filter flag is set, pass frames with CRC errors. Signed-off-by: Mathy Vanhoef Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/ath5k.h | 1 + drivers/net/wireless/ath/ath5k/base.c | 16 ++++++++++++++-- drivers/net/wireless/ath/ath5k/mac80211-ops.c | 2 ++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index 74bd54d6aceb..85316bb3f8c6 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -1285,6 +1285,7 @@ struct ath5k_hw { #define ATH_STAT_STARTED 3 /* opened & irqs enabled */ unsigned int filter_flags; /* HW flags, AR5K_RX_FILTER_* */ + unsigned int fif_filter_flags; /* Current FIF_* filter flags */ struct ieee80211_channel *curchan; /* current h/w channel */ u16 nvifs; diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 4b18434ba697..5839a3434119 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1382,6 +1382,9 @@ ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb, rxs->flag = 0; if (unlikely(rs->rs_status & AR5K_RXERR_MIC)) rxs->flag |= RX_FLAG_MMIC_ERROR; + if (unlikely(rs->rs_status & AR5K_RXERR_CRC)) + rxs->flag |= RX_FLAG_FAILED_FCS_CRC; + /* * always extend the mac timestamp, since this information is @@ -1449,6 +1452,8 @@ ath5k_receive_frame_ok(struct ath5k_hw *ah, struct ath5k_rx_status *rs) ah->stats.rx_bytes_count += rs->rs_datalen; if (unlikely(rs->rs_status)) { + unsigned int filters; + if (rs->rs_status & AR5K_RXERR_CRC) ah->stats.rxerr_crc++; if (rs->rs_status & AR5K_RXERR_FIFO) @@ -1480,8 +1485,15 @@ ath5k_receive_frame_ok(struct ath5k_hw *ah, struct ath5k_rx_status *rs) return true; } - /* reject any frames with non-crypto errors */ - if (rs->rs_status & ~(AR5K_RXERR_DECRYPT)) + /* + * Reject any frames with non-crypto errors, and take into account the + * current FIF_* filters. + */ + filters = AR5K_RXERR_DECRYPT; + if (ah->fif_filter_flags & FIF_FCSFAIL) + filters |= AR5K_RXERR_CRC; + + if (rs->rs_status & ~filters) return false; } diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index afb23b3cc7be..b65c38fdaa4b 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -473,6 +473,8 @@ ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, /* Set the cached hw filter flags, this will later actually * be set in HW */ ah->filter_flags = rfilt; + /* Store current FIF filter flags */ + ah->fif_filter_flags = *new_flags; mutex_unlock(&ah->lock); } From b76ff0d2e0aaf52adf0e7ec0625aa48a039cba23 Mon Sep 17 00:00:00 2001 From: Mathy Vanhoef Date: Sat, 14 Jun 2014 01:14:56 +0200 Subject: [PATCH 209/274] ath5k: capture CCK and OFDM restarts Treat frames that underwent a CCK or OFDM restart as frames with an invalid CRC. Signed-off-by: Mathy Vanhoef Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 5839a3434119..8ad2550bce7f 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1462,7 +1462,20 @@ ath5k_receive_frame_ok(struct ath5k_hw *ah, struct ath5k_rx_status *rs) ah->stats.rxerr_phy++; if (rs->rs_phyerr > 0 && rs->rs_phyerr < 32) ah->stats.rxerr_phy_code[rs->rs_phyerr]++; - return false; + + /* + * Treat packets that underwent a CCK or OFDM reset as having a bad CRC. + * These restarts happen when the radio resynchronizes to a stronger frame + * while receiving a weaker frame. Here we receive the prefix of the weak + * frame. Since these are incomplete packets, mark their CRC as invalid. + */ + if (rs->rs_phyerr == AR5K_RX_PHY_ERROR_OFDM_RESTART || + rs->rs_phyerr == AR5K_RX_PHY_ERROR_CCK_RESTART) { + rs->rs_status |= AR5K_RXERR_CRC; + rs->rs_status &= ~AR5K_RXERR_PHY; + } else { + return false; + } } if (rs->rs_status & AR5K_RXERR_DECRYPT) { /* From 9198cf4a84249f5eed8be1f02b17245b7d763477 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Thu, 26 Jun 2014 16:54:40 +0530 Subject: [PATCH 210/274] ath9k: Cache beacon config after association The beacon configurations are not cached properly after the station associates with AP. Not handling BEACON_INFO, is failing to update dtim period and also it is causing below warning message. WARNING: CPU: 1 PID: 0 at drivers/net/wireless/ath/ath9k/recv.c:548 ath_rx_tasklet+0xc89/0xca0 [ath9k]() Call Trace: [] dump_stack+0x48/0x69 [] warn_slowpath_common+0x82/0xa0 [] ? ath_rx_tasklet+0xc89/0xca0 [ath9k] [] ? ath_rx_tasklet+0xc89/0xca0 [ath9k] Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index f5727c7a53b8..e6ac8d2e610c 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1787,7 +1787,8 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, } if ((changed & BSS_CHANGED_BEACON_ENABLED) || - (changed & BSS_CHANGED_BEACON_INT)) { + (changed & BSS_CHANGED_BEACON_INT) || + (changed & BSS_CHANGED_BEACON_INFO)) { if (changed & BSS_CHANGED_BEACON_ENABLED) ath9k_calculate_summary_state(sc, avp->chanctx); ath9k_beacon_config(sc, vif, changed); From 5f2f9e44badc5e322523ef3b4583684255b80a07 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Thu, 26 Jun 2014 16:54:41 +0530 Subject: [PATCH 211/274] ath9k: Increase max listen interval Earlier the listen interval is used to decide switching between operating and off-channels during bgscan and to improve throughput, the listen interval is reduced to 1. After optimiztion in scan state machine, listen period is not used for decision making and hence reverting it back to original value. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 79fdab6a4003..39419ea845cc 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -797,7 +797,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) /* last queue for offchannel */ hw->offchannel_tx_hw_queue = hw->queues - 1; hw->max_rates = 4; - hw->max_listen_interval = 1; + hw->max_listen_interval = 10; hw->max_rate_tries = 10; hw->sta_data_size = sizeof(struct ath_node); hw->vif_data_size = sizeof(struct ath_vif); From 09ebb810927a110e4c354beb20308830d108a54b Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Thu, 26 Jun 2014 16:54:42 +0530 Subject: [PATCH 212/274] ath9k: Calculate sleep duration Right now sleep duration is configured as beacon interval. It should be the multiple of beacon interval by listen period which helps to reduce station power consumption. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/common-beacon.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/common-beacon.c b/drivers/net/wireless/ath/ath9k/common-beacon.c index 775d1d20ce0b..733be5178481 100644 --- a/drivers/net/wireless/ath/ath9k/common-beacon.c +++ b/drivers/net/wireless/ath/ath9k/common-beacon.c @@ -57,7 +57,7 @@ int ath9k_cmn_beacon_config_sta(struct ath_hw *ah, struct ath9k_beacon_state *bs) { struct ath_common *common = ath9k_hw_common(ah); - int dtim_intval; + int dtim_intval, sleepduration; u64 tsf; /* No need to configure beacon if we are not associated */ @@ -75,6 +75,7 @@ int ath9k_cmn_beacon_config_sta(struct ath_hw *ah, * last beacon we received (which may be none). */ dtim_intval = conf->intval * conf->dtim_period; + sleepduration = ah->hw->conf.listen_interval * conf->intval; /* * Pull nexttbtt forward to reflect the current @@ -112,7 +113,7 @@ int ath9k_cmn_beacon_config_sta(struct ath_hw *ah, */ bs->bs_sleepduration = TU_TO_USEC(roundup(IEEE80211_MS_TO_TU(100), - conf->intval)); + sleepduration)); if (bs->bs_sleepduration > bs->bs_dtimperiod) bs->bs_sleepduration = bs->bs_dtimperiod; From 5bc5ca85d54e0176cb967f550093f42bb0d65b67 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Fri, 27 Jun 2014 02:51:24 +0400 Subject: [PATCH 213/274] rsi: GFP_ATOMIC is not needed in rsi_init_usb_interface() Signed-off-by: Alexey Khoroshilov Signed-off-by: John W. Linville --- drivers/net/wireless/rsi/rsi_91x_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index 3226f19989e7..f89695abafcd 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -397,7 +397,7 @@ static int rsi_init_usb_interface(struct rsi_hw *adapter, return -ENOMEM; } - rsi_dev->tx_buffer = kmalloc(2048, GFP_ATOMIC); + rsi_dev->tx_buffer = kmalloc(2048, GFP_KERNEL); rsi_dev->rx_usb_urb[0] = usb_alloc_urb(0, GFP_KERNEL); rsi_dev->rx_usb_urb[0]->transfer_buffer = adapter->priv->rx_data_pkt; rsi_dev->tx_blk_size = 252; From 50591c60a93ad3a8d13833cb8048b02d3c2c4bd4 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Fri, 27 Jun 2014 02:51:25 +0400 Subject: [PATCH 214/274] rsi: fix memory leaks and error handling in rsi_91x_usb The patch fixes a couple of issues: - absence of deallocation of rsi_dev->rx_usb_urb[0] in the driver; - potential NULL pointer dereference because of lack of checks for memory allocation success in rsi_init_usb_interface(). By the way, it makes rsi_probe() returning error code instead of 1 and fixes comments regarding returning values. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: John W. Linville --- drivers/net/wireless/rsi/rsi_91x_usb.c | 58 +++++++++++++++++--------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index f89695abafcd..ef5d394f185b 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -25,7 +25,7 @@ * @len: Length to be written. * @endpoint: Type of endpoint. * - * Return: status: 0 on success, -1 on failure. + * Return: status: 0 on success, a negative error code on failure. */ static int rsi_usb_card_write(struct rsi_hw *adapter, void *buf, @@ -60,7 +60,7 @@ static int rsi_usb_card_write(struct rsi_hw *adapter, * @data: Pointer to the data that has to be written. * @count: Number of multiple bytes to be written. * - * Return: 0 on success, -1 on failure. + * Return: 0 on success, a negative error code on failure. */ static int rsi_write_multiple(struct rsi_hw *adapter, u8 endpoint, @@ -147,7 +147,7 @@ static int rsi_find_bulk_in_and_out_endpoints(struct usb_interface *interface, * @value: Value to be read. * @len: length of data to be read. * - * Return: status: 0 on success, -1 on failure. + * Return: status: 0 on success, a negative error code on failure. */ static int rsi_usb_reg_read(struct usb_device *usbdev, u32 reg, @@ -189,7 +189,7 @@ static int rsi_usb_reg_read(struct usb_device *usbdev, * @value: Value to write. * @len: Length of data to be written. * - * Return: status: 0 on success, -1 on failure. + * Return: status: 0 on success, a negative error code on failure. */ static int rsi_usb_reg_write(struct usb_device *usbdev, u32 reg, @@ -249,7 +249,7 @@ static void rsi_rx_done_handler(struct urb *urb) * rsi_rx_urb_submit() - This function submits the given URB to the USB stack. * @adapter: Pointer to the adapter structure. * - * Return: 0 on success, -1 on failure. + * Return: 0 on success, a negative error code on failure. */ static int rsi_rx_urb_submit(struct rsi_hw *adapter) { @@ -281,7 +281,7 @@ static int rsi_rx_urb_submit(struct rsi_hw *adapter) * @data: Pointer to the data that has to be written. * @count: Number of multiple bytes to be written on to the registers. * - * Return: status: 0 on success, -1 on failure. + * Return: status: 0 on success, a negative error code on failure. */ int rsi_usb_write_register_multiple(struct rsi_hw *adapter, u32 addr, @@ -331,7 +331,7 @@ int rsi_usb_write_register_multiple(struct rsi_hw *adapter, * @pkt: Pointer to the data to be written on to the card. * @len: Length of the data to be written on to the card. * - * Return: 0 on success, -1 on failure. + * Return: 0 on success, a negative error code on failure. */ static int rsi_usb_host_intf_write_pkt(struct rsi_hw *adapter, u8 *pkt, @@ -359,6 +359,7 @@ static void rsi_deinit_usb_interface(struct rsi_hw *adapter) struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; rsi_kill_thread(&dev->rx_thread); + usb_free_urb(dev->rx_usb_urb[0]); kfree(adapter->priv->rx_data_pkt); kfree(dev->tx_buffer); } @@ -368,7 +369,7 @@ static void rsi_deinit_usb_interface(struct rsi_hw *adapter) * @adapter: Pointer to the adapter structure. * @pfunction: Pointer to USB interface structure. * - * Return: 0 on success, -1 on failure. + * Return: 0 on success, a negative error code on failure. */ static int rsi_init_usb_interface(struct rsi_hw *adapter, struct usb_interface *pfunction) @@ -398,7 +399,15 @@ static int rsi_init_usb_interface(struct rsi_hw *adapter, } rsi_dev->tx_buffer = kmalloc(2048, GFP_KERNEL); + if (!rsi_dev->tx_buffer) { + status = -ENOMEM; + goto fail_tx; + } rsi_dev->rx_usb_urb[0] = usb_alloc_urb(0, GFP_KERNEL); + if (!rsi_dev->rx_usb_urb[0]) { + status = -ENOMEM; + goto fail_rx; + } rsi_dev->rx_usb_urb[0]->transfer_buffer = adapter->priv->rx_data_pkt; rsi_dev->tx_blk_size = 252; @@ -413,7 +422,7 @@ static int rsi_init_usb_interface(struct rsi_hw *adapter, rsi_usb_rx_thread, "RX-Thread"); if (status) { rsi_dbg(ERR_ZONE, "%s: Unable to init rx thrd\n", __func__); - goto fail; + goto fail_thread; } #ifdef CONFIG_RSI_DEBUGFS @@ -424,8 +433,11 @@ static int rsi_init_usb_interface(struct rsi_hw *adapter, rsi_dbg(INIT_ZONE, "%s: Enabled the interface\n", __func__); return 0; -fail: +fail_thread: + usb_free_urb(rsi_dev->rx_usb_urb[0]); +fail_rx: kfree(rsi_dev->tx_buffer); +fail_tx: kfree(common->rx_data_pkt); return status; } @@ -437,7 +449,7 @@ fail: * @pfunction: Pointer to the USB interface structure. * @id: Pointer to the usb_device_id structure. * - * Return: 0 on success, -1 on failure. + * Return: 0 on success, a negative error code on failure. */ static int rsi_probe(struct usb_interface *pfunction, const struct usb_device_id *id) @@ -445,6 +457,7 @@ static int rsi_probe(struct usb_interface *pfunction, struct rsi_hw *adapter; struct rsi_91x_usbdev *dev; u16 fw_status; + int status; rsi_dbg(INIT_ZONE, "%s: Init function called\n", __func__); @@ -452,10 +465,11 @@ static int rsi_probe(struct usb_interface *pfunction, if (!adapter) { rsi_dbg(ERR_ZONE, "%s: Failed to init os intf ops\n", __func__); - return 1; + return -ENOMEM; } - if (rsi_init_usb_interface(adapter, pfunction)) { + status = rsi_init_usb_interface(adapter, pfunction); + if (status) { rsi_dbg(ERR_ZONE, "%s: Failed to init usb interface\n", __func__); goto err; @@ -465,26 +479,30 @@ static int rsi_probe(struct usb_interface *pfunction, dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; - if (rsi_usb_reg_read(dev->usbdev, FW_STATUS_REG, &fw_status, 2) < 0) + status = rsi_usb_reg_read(dev->usbdev, FW_STATUS_REG, &fw_status, 2); + if (status) goto err1; else fw_status &= 1; if (!fw_status) { - if (rsi_usb_device_init(adapter->priv)) { + status = rsi_usb_device_init(adapter->priv); + if (status) { rsi_dbg(ERR_ZONE, "%s: Failed in device init\n", __func__); goto err1; } - if (rsi_usb_reg_write(dev->usbdev, - USB_INTERNAL_REG_1, - RSI_USB_READY_MAGIC_NUM, 1) < 0) + status = rsi_usb_reg_write(dev->usbdev, + USB_INTERNAL_REG_1, + RSI_USB_READY_MAGIC_NUM, 1); + if (status) goto err1; rsi_dbg(INIT_ZONE, "%s: Performed device init\n", __func__); } - if (rsi_rx_urb_submit(adapter)) + status = rsi_rx_urb_submit(adapter); + if (status) goto err1; return 0; @@ -493,7 +511,7 @@ err1: err: rsi_91x_deinit(adapter); rsi_dbg(ERR_ZONE, "%s: Failed in probe...Exiting\n", __func__); - return 1; + return status; } /** From f528f664d61cdb87fe12eb24ed9b05548a3e71b3 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sat, 28 Jun 2014 14:18:52 +0200 Subject: [PATCH 215/274] drivers/net/wireless/ipw2x00/libipw_module.c: remove unnecessary null test before kfree Fix checkpatch warning: WARNING: kfree(NULL) is safe this check is probably not required Cc: Stanislav Yakovlev Cc: "John W. Linville" Cc: linux-wireless@vger.kernel.org Signed-off-by: Fabian Frederick Signed-off-by: John W. Linville --- drivers/net/wireless/ipw2x00/libipw_module.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/ipw2x00/libipw_module.c b/drivers/net/wireless/ipw2x00/libipw_module.c index 3adb24021a28..5f31b72a4921 100644 --- a/drivers/net/wireless/ipw2x00/libipw_module.c +++ b/drivers/net/wireless/ipw2x00/libipw_module.c @@ -100,8 +100,7 @@ static inline void libipw_networks_free(struct libipw_device *ieee) int i; for (i = 0; i < MAX_NETWORK_COUNT; i++) { - if (ieee->networks[i]->ibss_dfs) - kfree(ieee->networks[i]->ibss_dfs); + kfree(ieee->networks[i]->ibss_dfs); kfree(ieee->networks[i]); } } From b49c3caf5ef1476895f92ea5fac0b6bd48854628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sun, 29 Jun 2014 21:46:45 +0200 Subject: [PATCH 216/274] b43: treat LCNXN-PHY as extra N-PHY devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LCNXN is simply a continuation of N, e.g. code handling LCNXN revs 0 and 1 is mostly the same as for N-PHY revs 7+. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index aae3af2a7a9f..3740b76162f9 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4331,6 +4331,13 @@ static int b43_phy_versioning(struct b43_wldev *dev) analog_type = (tmp & B43_PHYVER_ANALOG) >> B43_PHYVER_ANALOG_SHIFT; phy_type = (tmp & B43_PHYVER_TYPE) >> B43_PHYVER_TYPE_SHIFT; phy_rev = (tmp & B43_PHYVER_VERSION); + + /* LCNXN is continuation of N which run out of revisions */ + if (phy_type == B43_PHYTYPE_LCNXN) { + phy_type = B43_PHYTYPE_N; + phy_rev += 16; + } + switch (phy_type) { #ifdef CONFIG_B43_PHY_G case B43_PHYTYPE_G: From 1f622d76fa704bc67b4fbf0d72cc6c369112986c Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Mon, 30 Jun 2014 18:17:48 +0200 Subject: [PATCH 217/274] rtl818x_pci: Fix BSSID register written incorrectly BSSID register was written with six byte-writes. It seems that, similarly to what happens with MAC registers, they needs to be written with one 16-bit and one 32-bit writes, otherwise the write does not work. The byte write didn't work only on my rtl8185, while it worked on rtl8180 and rtl8187se, BTW since there are probably a number of different ASIC revisions out of there, I let the change to affect all cards. It shouldn't hurt anyway. Signed-off-by: Andrea Merello Signed-off-by: John W. Linville --- drivers/net/wireless/rtl818x/rtl8180/dev.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index 1e2592918fc6..e2dcedee5e41 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -1461,9 +1461,10 @@ static void rtl8180_bss_info_changed(struct ieee80211_hw *dev, vif_priv = (struct rtl8180_vif *)&vif->drv_priv; if (changed & BSS_CHANGED_BSSID) { - for (i = 0; i < ETH_ALEN; i++) - rtl818x_iowrite8(priv, &priv->map->BSSID[i], - info->bssid[i]); + rtl818x_iowrite16(priv, (__le16 __iomem *)&priv->map->BSSID[0], + le16_to_cpu(*(__le16 *)info->bssid)); + rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->BSSID[2], + le32_to_cpu(*(__le32 *)(info->bssid + 2))); if (is_valid_ether_addr(info->bssid)) { if (vif->type == NL80211_IFTYPE_ADHOC) From 7df007243be47808f647c23dd2727528135d1c7f Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Mon, 30 Jun 2014 18:18:25 +0200 Subject: [PATCH 218/274] rtl818x_pci: Fix rtl8185 excessive IFS after CTS-to-self Measuring time between _end_ of CTS-to-self and _end_ of datapacket (with a prism54 board and mac80211 hacked to let the MAC timestamp stay untouched in the radiotap header) resulted in about 300uS, while the datapacket itself should be by far shorter (less than 100uS) and IFS should be SIFS (10uS). This measure was confirmed whith a scope: about 250uS IFS has been seen between the two packets. This situation causes the CTS-to-self protection mechanism to work incorrectly due to the NAV expiring during, or even before beginning, the packet transmission, and it also causes the performances to be anyway reduced due to time waste. This problem has been seen at every packet TXed with CTS-to-self enabled on rtl8185 board. rtl8187se seems not affected (and rtl8180, being a 802.11b card, does not have CTS-to-self mechaninsm). This patch fixes this by adding a magic register write, making the board wait for correct SIFS after CTS-to-self packet. Signed-off-by: Andrea Merello Signed-off-by: John W. Linville --- drivers/net/wireless/rtl818x/rtl8180/dev.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index e2dcedee5e41..73baf268fe67 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -879,6 +879,8 @@ static int rtl8180_init_hw(struct ieee80211_hw *dev) reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | (1 << 2)); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + /* fix eccessive IFS after CTS-to-self */ + rtl818x_iowrite8(priv, REG_ADDR1(0x1ff), 0x35); } if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) { From f82be7c46a452fbd229cfbc4ddfb8fbcaf5b644f Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Mon, 30 Jun 2014 18:18:36 +0200 Subject: [PATCH 219/274] rtl818x_pci: add comment pointing to the rtl8187se reference code Rtl8187se support has been added to the rtl818x_pci driver by extracting a lot of information from a rtl8187se Linux staging driver included in the kernel at the time rtl8187se support was added. The rtl818x_pci main file has a comment that advertises this. Recently this staging driver has been removed from the kernel, but I still feel it can be useful as "reference" code (in case of bugs, or to implement improvements in rtl818x_pci driver). This one-line patch adds a comment in rtl818x_pci driver to point people searching for that "reference code" to the last kernel version still containing it (3.14). Signed-off-by: Andrea Merello Signed-off-by: John W. Linville --- drivers/net/wireless/rtl818x/rtl8180/dev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index 73baf268fe67..8a64bc116bfd 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -16,6 +16,7 @@ * * based also on: * - portions of rtl8187se Linux staging driver, Copyright Realtek corp. + * (available in drivers/staging/rtl8187se directory of Linux 3.14) * - other GPL, unpublished (until now), Linux driver code, * Copyright Larry Finger * From 81129fce7e4fc60fce616c53510699b093d87e26 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Mon, 30 Jun 2014 18:18:55 +0200 Subject: [PATCH 220/274] rtl8180: fix incorrect TX retry. HW is programmed with wrong retry count value for TX: Mac80211 passes to driver the number of times the TX should be attempted. The HW, instead, wants the number of time the TX should be retried if it fails the first time (assuming we have to TX it at least one time). This patch correct this. Signed-off-by: Andrea Merello Signed-off-by: John W. Linville --- drivers/net/wireless/rtl818x/rtl8180/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index 8a64bc116bfd..4f4fcf8b2013 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -542,7 +542,7 @@ static void rtl8180_tx(struct ieee80211_hw *dev, entry->flags2 = info->control.rates[1].idx >= 0 ? ieee80211_get_alt_retry_rate(dev, info, 0)->bitrate << 4 : 0; - entry->retry_limit = info->control.rates[0].count; + entry->retry_limit = info->control.rates[0].count - 1; /* We must be sure that tx_flags is written last because the HW * looks at it to check if the rest of data is valid or not From fe67bcd4c8407f12664cf45cc4bbd86d951515f2 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Mon, 30 Jun 2014 18:19:10 +0200 Subject: [PATCH 221/274] rtl8180: disable buggy rate fallback mechanism Currently the driver configures mac80211 to provide two rates for each TX frame: One initial rate and one alternate fallback rate, each one with its retry count. HW does not support fully this: rtl8180 doesn't have support for rate scaling at all, and rtl8185/rtl8187SE supports it in a way that does not fit with mac80211: The HW does automatically fall back to the next lower rate, and only a lower limit can be specified, so the HW may TX also on rates in between the two rates specified by mac80211. Furthermore only the total TX retry count can be specified for each packet, while the number of TX attempts before scaling rate can be configured only globally (not per each packet). Currently the driver sets the HW auto rate fallback mechanism to quickly scale rate after a couple of retries, and it uses the alternate rate requested by mac80211 as fallback limit rate (and it does this even wrongly). The HW indeed will behave differently than what mac80211 mandates, that is probably undesirable, and the reported TX retry count may not refer to what mac80211 thinks, and this could fool mac80211. This patch makes the driver to declare to mac80211 to support only one rate configuration for each packet, and it does disable the HW auto rate fallback mechanism, relying only on SW and letting mac80211 to do all by itself. This should ensure correct operation and fairness respect to mac80211. Indeed here tests with iperf do not show significant performance differences. Signed-off-by: Andrea Merello Signed-off-by: John W. Linville --- drivers/net/wireless/rtl818x/rtl8180/dev.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index 4f4fcf8b2013..5303b8f1d928 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -348,7 +348,6 @@ static void rtl8180_handle_tx(struct ieee80211_hw *dev, unsigned int prio) info->flags |= IEEE80211_TX_STAT_ACK; info->status.rates[0].count = (flags & 0xFF) + 1; - info->status.rates[1].idx = -1; ieee80211_tx_status_irqsafe(dev, skb); if (ring->entries - skb_queue_len(&ring->queue) == 2) @@ -540,8 +539,6 @@ static void rtl8180_tx(struct ieee80211_hw *dev, entry->plcp_len = cpu_to_le16(plcp_len); entry->tx_buf = cpu_to_le32(mapping); - entry->flags2 = info->control.rates[1].idx >= 0 ? - ieee80211_get_alt_retry_rate(dev, info, 0)->bitrate << 4 : 0; entry->retry_limit = info->control.rates[0].count - 1; /* We must be sure that tx_flags is written last because the HW @@ -864,7 +861,7 @@ static int rtl8180_init_hw(struct ieee80211_hw *dev) if (priv->chip_family != RTL818X_CHIP_FAMILY_RTL8180) { rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0); - rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0x81); + rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0); } else { rtl818x_iowrite8(priv, &priv->map->SECURITY, 0); @@ -1738,7 +1735,7 @@ static int rtl8180_probe(struct pci_dev *pdev, priv = dev->priv; priv->pdev = pdev; - dev->max_rates = 2; + dev->max_rates = 1; SET_IEEE80211_DEV(dev, &pdev->dev); pci_set_drvdata(pdev, dev); From f4cf628781f802ecf5efa0ceb5e81a2f80dea5b8 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Mon, 30 Jun 2014 18:19:27 +0200 Subject: [PATCH 222/274] rtl818x_pci: handle broken PIO mapping All boards supported by this driver could work using PIO or MMIO for accessing registers. This driver tries to access HW by using MMIO, and, if this fails for somewhat reason, the driver tries to fall back to PIO mode. MMIO-mode is straightforward on all boards. PIO-mode is straightforward on rtl8180 only. On rtl8185 and rtl8187se boards not all registers are directly available in PIO mode (they are paged). On rtl8185 there are two pages and it is known how to switch page. PIO mode works, except for only one access to a register out of default page, recently added by me in the initialization code with patch: rtl818x_pci: Fix rtl8185 excessive IFS after CTS-to-self This can be easily fixed to work in both cases (MMIO and PIO). On rtl8187se, for a number of reasons, there is much more work to do to fix PIO access. PIO access is currently broken on rtl8187se, and it never worked. This patch fixes the said register write for rtl8185 and makes the driver to fail cleanly if PIO mode is attempted with rtl8187se boards. While doing this, I converted also a couple of printk(KERN_ERR) to dev_err(), in order to make checkpatch happy. Signed-off-by: Andrea Merello Signed-off-by: John W. Linville --- drivers/net/wireless/rtl818x/rtl8180/dev.c | 24 +++++++++++++++---- .../net/wireless/rtl818x/rtl8180/rtl8180.h | 1 + 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index 5303b8f1d928..68304a9297fd 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -878,7 +878,15 @@ static int rtl8180_init_hw(struct ieee80211_hw *dev) rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | (1 << 2)); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); /* fix eccessive IFS after CTS-to-self */ - rtl818x_iowrite8(priv, REG_ADDR1(0x1ff), 0x35); + if (priv->map_pio) { + u8 reg; + + reg = rtl818x_ioread8(priv, &priv->map->PGSELECT); + rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg | 1); + rtl818x_iowrite8(priv, REG_ADDR1(0xff), 0x35); + rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); + } else + rtl818x_iowrite8(priv, REG_ADDR1(0x1ff), 0x35); } if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) { @@ -1739,13 +1747,15 @@ static int rtl8180_probe(struct pci_dev *pdev, SET_IEEE80211_DEV(dev, &pdev->dev); pci_set_drvdata(pdev, dev); + priv->map_pio = false; priv->map = pci_iomap(pdev, 1, mem_len); - if (!priv->map) + if (!priv->map) { priv->map = pci_iomap(pdev, 0, io_len); + priv->map_pio = true; + } if (!priv->map) { - printk(KERN_ERR "%s (rtl8180): Cannot map device memory\n", - pci_name(pdev)); + dev_err(&pdev->dev, "Cannot map device memory/PIO\n"); goto err_free_dev; } @@ -1794,6 +1804,12 @@ static int rtl8180_probe(struct pci_dev *pdev, case RTL818X_TX_CONF_RTL8187SE: chip_name = "RTL8187SE"; + if (priv->map_pio) { + dev_err(&pdev->dev, + "MMIO failed. PIO not supported on RTL8187SE\n"); + err = -ENOMEM; + goto err_iounmap; + } priv->chip_family = RTL818X_CHIP_FAMILY_RTL8187SE; break; diff --git a/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h b/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h index 291a55970d1a..e8243a44d6b6 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h +++ b/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h @@ -107,6 +107,7 @@ struct rtl8180_priv { struct ieee80211_vif *vif; /* rtl8180 driver specific */ + bool map_pio; spinlock_t lock; void *rx_ring; u8 rx_ring_sz; From c1084e026b4fe73fcc54f82d6a6bd26d9372d583 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Mon, 30 Jun 2014 18:19:40 +0200 Subject: [PATCH 223/274] rtl818x_pci: fix pci probe returns success when it fails There are several exit path from the PCI probe function. Some of them, that are taken in case of errors, forget to set the "err" variable, that is returned by the probe function. This can lead to the kernel thinking the probe function succeeds while it didn't, and this in turn causes extra calls to the "remove" function. This patch fix this problem by ensuring "err" variable is assigned to a proper non-zero value in each exit path. Signed-off-by: Andrea Merello Signed-off-by: John W. Linville --- drivers/net/wireless/rtl818x/rtl8180/dev.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index 68304a9297fd..4b904f708184 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -1756,6 +1756,7 @@ static int rtl8180_probe(struct pci_dev *pdev, if (!priv->map) { dev_err(&pdev->dev, "Cannot map device memory/PIO\n"); + err = -ENOMEM; goto err_free_dev; } @@ -1816,6 +1817,7 @@ static int rtl8180_probe(struct pci_dev *pdev, default: printk(KERN_ERR "%s (rtl8180): Unknown chip! (0x%x)\n", pci_name(pdev), reg >> 25); + err = -ENODEV; goto err_iounmap; } @@ -1866,12 +1868,14 @@ static int rtl8180_probe(struct pci_dev *pdev, default: printk(KERN_ERR "%s (rtl8180): Unknown RF! (0x%x)\n", pci_name(pdev), priv->rf_type); + err = -ENODEV; goto err_iounmap; } if (!priv->rf) { printk(KERN_ERR "%s (rtl8180): %s RF frontend not supported!\n", pci_name(pdev), rf_name); + err = -ENODEV; goto err_iounmap; } From e90cf1c7abf6b190f51f89e4f63f961defba38a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 1 Jul 2014 16:19:07 +0200 Subject: [PATCH 224/274] b43: N-PHY: fixes for radio 0x2057 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable initialization and update calibration code to fix: b43-phy0 ERROR: Radio 0x2057 rcal timeout b43-phy0 debug: Radio 0x2057 rccal timeout b43-phy0 debug: Radio 0x2057 rccal timeout b43-phy0 ERROR: Radio 0x2057 rcal timeout Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/phy_n.c | 63 ++++++++++++++++++++++----- drivers/net/wireless/b43/radio_2057.c | 20 ++++----- 2 files changed, 61 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index 0fb5e14a4554..8369a08f0327 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -590,7 +590,9 @@ static void b43_nphy_set_rf_sequence(struct b43_wldev *dev, u8 cmd, * Radio 0x2057 **************************************************/ -/* http://bcm-v4.sipsolutions.net/PHY/radio2057_rcal */ +/* Calibrate resistors in LPF of PLL? + * http://bcm-v4.sipsolutions.net/PHY/radio205x_rcal + */ static u8 b43_radio_2057_rcal(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; @@ -603,15 +605,25 @@ static u8 b43_radio_2057_rcal(struct b43_wldev *dev) b43_radio_maskset(dev, 0x1ca, ~0x2, 0x1); } + /* Enable */ b43_radio_set(dev, R2057_RCAL_CONFIG, 0x1); udelay(10); - b43_radio_set(dev, R2057_RCAL_CONFIG, 0x3); - if (!b43_radio_wait_value(dev, R2057_RCCAL_N1_1, 1, 1, 100, 1000000)) { + + /* Start */ + b43_radio_set(dev, R2057_RCAL_CONFIG, 0x2); + usleep_range(100, 200); + + /* Stop */ + b43_radio_mask(dev, R2057_RCAL_CONFIG, ~0x2); + + /* Wait and check for result */ + if (!b43_radio_wait_value(dev, R2057_RCAL_STATUS, 1, 1, 100, 1000000)) { b43err(dev->wl, "Radio 0x2057 rcal timeout\n"); return 0; } - b43_radio_mask(dev, R2057_RCAL_CONFIG, ~0x2); tmp = b43_radio_read(dev, R2057_RCAL_STATUS) & 0x3E; + + /* Disable */ b43_radio_mask(dev, R2057_RCAL_CONFIG, ~0x1); if (phy->radio_rev == 5) { @@ -627,7 +639,9 @@ static u8 b43_radio_2057_rcal(struct b43_wldev *dev) return tmp & 0x3e; } -/* http://bcm-v4.sipsolutions.net/PHY/radio2057_rccal */ +/* Calibrate the internal RC oscillator? + * http://bcm-v4.sipsolutions.net/PHY/radio2057_rccal + */ static u16 b43_radio_2057_rccal(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; @@ -635,49 +649,76 @@ static u16 b43_radio_2057_rccal(struct b43_wldev *dev) phy->radio_rev == 6); u16 tmp; + /* Setup cal */ if (special) { b43_radio_write(dev, R2057_RCCAL_MASTER, 0x61); b43_radio_write(dev, R2057_RCCAL_TRC0, 0xC0); } else { - b43_radio_write(dev, 0x1AE, 0x61); + b43_radio_write(dev, R2057v7_RCCAL_MASTER, 0x61); b43_radio_write(dev, R2057_RCCAL_TRC0, 0xE1); } b43_radio_write(dev, R2057_RCCAL_X1, 0x6E); + + /* Start, wait, stop */ b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x55); - if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 1, 1, 500, + if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 2, 2, 500, 5000000)) b43dbg(dev->wl, "Radio 0x2057 rccal timeout\n"); + usleep_range(35, 70); b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x15); + usleep_range(70, 140); + + /* Setup cal */ if (special) { b43_radio_write(dev, R2057_RCCAL_MASTER, 0x69); b43_radio_write(dev, R2057_RCCAL_TRC0, 0xB0); } else { - b43_radio_write(dev, 0x1AE, 0x69); + b43_radio_write(dev, R2057v7_RCCAL_MASTER, 0x69); b43_radio_write(dev, R2057_RCCAL_TRC0, 0xD5); } b43_radio_write(dev, R2057_RCCAL_X1, 0x6E); + + /* Start, wait, stop */ + usleep_range(35, 70); b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x55); - if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 1, 1, 500, + usleep_range(70, 140); + if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 2, 2, 500, 5000000)) b43dbg(dev->wl, "Radio 0x2057 rccal timeout\n"); + usleep_range(35, 70); b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x15); + usleep_range(70, 140); + + /* Setup cal */ if (special) { b43_radio_write(dev, R2057_RCCAL_MASTER, 0x73); b43_radio_write(dev, R2057_RCCAL_X1, 0x28); b43_radio_write(dev, R2057_RCCAL_TRC0, 0xB0); } else { - b43_radio_write(dev, 0x1AE, 0x73); + b43_radio_write(dev, R2057v7_RCCAL_MASTER, 0x73); b43_radio_write(dev, R2057_RCCAL_X1, 0x6E); b43_radio_write(dev, R2057_RCCAL_TRC0, 0x99); } + + /* Start, wait, stop */ + usleep_range(35, 70); b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x55); - if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 1, 1, 500, + usleep_range(70, 140); + if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 2, 2, 500, 5000000)) { b43err(dev->wl, "Radio 0x2057 rcal timeout\n"); return 0; } tmp = b43_radio_read(dev, R2057_RCCAL_DONE_OSCCAP); + usleep_range(35, 70); b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x15); + usleep_range(70, 140); + + if (special) + b43_radio_mask(dev, R2057_RCCAL_MASTER, ~0x1); + else + b43_radio_mask(dev, R2057v7_RCCAL_MASTER, ~0x1); + return tmp; } diff --git a/drivers/net/wireless/b43/radio_2057.c b/drivers/net/wireless/b43/radio_2057.c index d61d6830c5c7..5e49440d4125 100644 --- a/drivers/net/wireless/b43/radio_2057.c +++ b/drivers/net/wireless/b43/radio_2057.c @@ -26,7 +26,7 @@ #include "radio_2057.h" #include "phy_common.h" -static u16 r2057_rev4_init[42][2] = { +static u16 r2057_rev4_init[][2] = { { 0x0E, 0x20 }, { 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x35, 0x26 }, { 0x3C, 0xff }, { 0x3D, 0xff }, { 0x3E, 0xff }, { 0x3F, 0xff }, { 0x62, 0x33 }, { 0x8A, 0xf0 }, { 0x8B, 0x10 }, @@ -40,7 +40,7 @@ static u16 r2057_rev4_init[42][2] = { { 0x1AB, 0x00 }, { 0x1AC, 0x00 }, }; -static u16 r2057_rev5_init[44][2] = { +static u16 r2057_rev5_init[][2] = { { 0x00, 0x00 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x23, 0x6 }, { 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, { 0x59, 0x88 }, { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, @@ -54,7 +54,7 @@ static u16 r2057_rev5_init[44][2] = { { 0x1AC, 0x00 }, { 0x1B7, 0x0c }, { 0x1C1, 0x01 }, { 0x1C2, 0x80 }, }; -static u16 r2057_rev5a_init[45][2] = { +static u16 r2057_rev5a_init[][2] = { { 0x00, 0x15 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x23, 0x6 }, { 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, { 0x59, 0x88 }, { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, @@ -69,7 +69,7 @@ static u16 r2057_rev5a_init[45][2] = { { 0x1C2, 0x80 }, }; -static u16 r2057_rev7_init[54][2] = { +static u16 r2057_rev7_init[][2] = { { 0x00, 0x00 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, { 0x59, 0x88 }, { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, { 0x64, 0x13 }, @@ -86,7 +86,7 @@ static u16 r2057_rev7_init[54][2] = { { 0x1B7, 0x05 }, { 0x1C2, 0xa0 }, }; -static u16 r2057_rev8_init[54][2] = { +static u16 r2057_rev8_init[][2] = { { 0x00, 0x08 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, { 0x59, 0x88 }, { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, { 0x64, 0x0f }, @@ -130,12 +130,10 @@ void r2057_upload_inittabs(struct b43_wldev *dev) } } + B43_WARN_ON(!table); + if (table) { - for (i = 0; i < 10; i++) { - pr_info("radio_write 0x%X ", *table); - table++; - pr_info("0x%X\n", *table); - table++; - } + for (i = 0; i < size; i++, table += 2) + b43_radio_write(dev, table[0], table[1]); } } From fe255b40cbf0a760b4e62a5948d77aff12b6b0a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 1 Jul 2014 16:19:08 +0200 Subject: [PATCH 225/274] b43: N-PHY: complete generic support for 0x2057 radio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It doesn't include any device (radio revision) specific code yet, so it isn't really usable. As the commit says, it's just some generic code. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/phy_n.c | 132 ++++++++++++++++++++++++-- drivers/net/wireless/b43/radio_2057.c | 104 +++++++++++++++++--- drivers/net/wireless/b43/radio_2057.h | 66 +++++++++++++ 3 files changed, 282 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index 8369a08f0327..0a6f04be9073 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -590,6 +590,100 @@ static void b43_nphy_set_rf_sequence(struct b43_wldev *dev, u8 cmd, * Radio 0x2057 **************************************************/ +static void b43_radio_2057_chantab_upload(struct b43_wldev *dev, + const struct b43_nphy_chantabent_rev7 *e_r7, + const struct b43_nphy_chantabent_rev7_2g *e_r7_2g) +{ + if (e_r7_2g) { + b43_radio_write(dev, R2057_VCOCAL_COUNTVAL0, e_r7_2g->radio_vcocal_countval0); + b43_radio_write(dev, R2057_VCOCAL_COUNTVAL1, e_r7_2g->radio_vcocal_countval1); + b43_radio_write(dev, R2057_RFPLL_REFMASTER_SPAREXTALSIZE, e_r7_2g->radio_rfpll_refmaster_sparextalsize); + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, e_r7_2g->radio_rfpll_loopfilter_r1); + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, e_r7_2g->radio_rfpll_loopfilter_c2); + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, e_r7_2g->radio_rfpll_loopfilter_c1); + b43_radio_write(dev, R2057_CP_KPD_IDAC, e_r7_2g->radio_cp_kpd_idac); + b43_radio_write(dev, R2057_RFPLL_MMD0, e_r7_2g->radio_rfpll_mmd0); + b43_radio_write(dev, R2057_RFPLL_MMD1, e_r7_2g->radio_rfpll_mmd1); + b43_radio_write(dev, R2057_VCOBUF_TUNE, e_r7_2g->radio_vcobuf_tune); + b43_radio_write(dev, R2057_LOGEN_MX2G_TUNE, e_r7_2g->radio_logen_mx2g_tune); + b43_radio_write(dev, R2057_LOGEN_INDBUF2G_TUNE, e_r7_2g->radio_logen_indbuf2g_tune); + b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE0, e_r7_2g->radio_txmix2g_tune_boost_pu_core0); + b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE0, e_r7_2g->radio_pad2g_tune_pus_core0); + b43_radio_write(dev, R2057_LNA2G_TUNE_CORE0, e_r7_2g->radio_lna2g_tune_core0); + b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE1, e_r7_2g->radio_txmix2g_tune_boost_pu_core1); + b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE1, e_r7_2g->radio_pad2g_tune_pus_core1); + b43_radio_write(dev, R2057_LNA2G_TUNE_CORE1, e_r7_2g->radio_lna2g_tune_core1); + + } else { + b43_radio_write(dev, R2057_VCOCAL_COUNTVAL0, e_r7->radio_vcocal_countval0); + b43_radio_write(dev, R2057_VCOCAL_COUNTVAL1, e_r7->radio_vcocal_countval1); + b43_radio_write(dev, R2057_RFPLL_REFMASTER_SPAREXTALSIZE, e_r7->radio_rfpll_refmaster_sparextalsize); + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, e_r7->radio_rfpll_loopfilter_r1); + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, e_r7->radio_rfpll_loopfilter_c2); + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, e_r7->radio_rfpll_loopfilter_c1); + b43_radio_write(dev, R2057_CP_KPD_IDAC, e_r7->radio_cp_kpd_idac); + b43_radio_write(dev, R2057_RFPLL_MMD0, e_r7->radio_rfpll_mmd0); + b43_radio_write(dev, R2057_RFPLL_MMD1, e_r7->radio_rfpll_mmd1); + b43_radio_write(dev, R2057_VCOBUF_TUNE, e_r7->radio_vcobuf_tune); + b43_radio_write(dev, R2057_LOGEN_MX2G_TUNE, e_r7->radio_logen_mx2g_tune); + b43_radio_write(dev, R2057_LOGEN_MX5G_TUNE, e_r7->radio_logen_mx5g_tune); + b43_radio_write(dev, R2057_LOGEN_INDBUF2G_TUNE, e_r7->radio_logen_indbuf2g_tune); + b43_radio_write(dev, R2057_LOGEN_INDBUF5G_TUNE, e_r7->radio_logen_indbuf5g_tune); + b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE0, e_r7->radio_txmix2g_tune_boost_pu_core0); + b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE0, e_r7->radio_pad2g_tune_pus_core0); + b43_radio_write(dev, R2057_PGA_BOOST_TUNE_CORE0, e_r7->radio_pga_boost_tune_core0); + b43_radio_write(dev, R2057_TXMIX5G_BOOST_TUNE_CORE0, e_r7->radio_txmix5g_boost_tune_core0); + b43_radio_write(dev, R2057_PAD5G_TUNE_MISC_PUS_CORE0, e_r7->radio_pad5g_tune_misc_pus_core0); + b43_radio_write(dev, R2057_LNA2G_TUNE_CORE0, e_r7->radio_lna2g_tune_core0); + b43_radio_write(dev, R2057_LNA5G_TUNE_CORE0, e_r7->radio_lna5g_tune_core0); + b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE1, e_r7->radio_txmix2g_tune_boost_pu_core1); + b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE1, e_r7->radio_pad2g_tune_pus_core1); + b43_radio_write(dev, R2057_PGA_BOOST_TUNE_CORE1, e_r7->radio_pga_boost_tune_core1); + b43_radio_write(dev, R2057_TXMIX5G_BOOST_TUNE_CORE1, e_r7->radio_txmix5g_boost_tune_core1); + b43_radio_write(dev, R2057_PAD5G_TUNE_MISC_PUS_CORE1, e_r7->radio_pad5g_tune_misc_pus_core1); + b43_radio_write(dev, R2057_LNA2G_TUNE_CORE1, e_r7->radio_lna2g_tune_core1); + b43_radio_write(dev, R2057_LNA5G_TUNE_CORE1, e_r7->radio_lna5g_tune_core1); + } +} + +static void b43_radio_2057_setup(struct b43_wldev *dev, + const struct b43_nphy_chantabent_rev7 *tabent_r7, + const struct b43_nphy_chantabent_rev7_2g *tabent_r7_2g) +{ + struct b43_phy *phy = &dev->phy; + + b43_radio_2057_chantab_upload(dev, tabent_r7, tabent_r7_2g); + + switch (phy->radio_rev) { + case 0 ... 4: + case 6: + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, 0x3f); + b43_radio_write(dev, R2057_CP_KPD_IDAC, 0x3f); + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, 0x8); + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, 0x8); + } else { + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, 0x1f); + b43_radio_write(dev, R2057_CP_KPD_IDAC, 0x3f); + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, 0x8); + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, 0x8); + } + break; + /* TODO */ + } + + /* TODO */ + + usleep_range(50, 100); + + /* VCO calibration */ + b43_radio_mask(dev, R2057_RFPLL_MISC_EN, ~0x01); + b43_radio_mask(dev, R2057_RFPLL_MISC_CAL_RESETN, ~0x04); + b43_radio_set(dev, R2057_RFPLL_MISC_CAL_RESETN, 0x4); + b43_radio_set(dev, R2057_RFPLL_MISC_EN, 0x01); + usleep_range(300, 600); +} + /* Calibrate resistors in LPF of PLL? * http://bcm-v4.sipsolutions.net/PHY/radio205x_rcal */ @@ -5535,10 +5629,17 @@ static int b43_nphy_set_channel(struct b43_wldev *dev, const struct b43_nphy_channeltab_entry_rev2 *tabent_r2 = NULL; const struct b43_nphy_channeltab_entry_rev3 *tabent_r3 = NULL; + const struct b43_nphy_chantabent_rev7 *tabent_r7 = NULL; + const struct b43_nphy_chantabent_rev7_2g *tabent_r7_2g = NULL; u8 tmp; - if (dev->phy.rev >= 3) { + if (phy->rev >= 7) { + r2057_get_chantabent_rev7(dev, channel->center_freq, + &tabent_r7, &tabent_r7_2g); + if (!tabent_r7 && !tabent_r7_2g) + return -ESRCH; + } else if (phy->rev >= 3) { tabent_r3 = b43_nphy_get_chantabent_rev3(dev, channel->center_freq); if (!tabent_r3) @@ -5560,14 +5661,29 @@ static int b43_nphy_set_channel(struct b43_wldev *dev, ; /* TODO: BMAC BW Set (channel_type) */ #endif - if (channel_type == NL80211_CHAN_HT40PLUS) - b43_phy_set(dev, B43_NPHY_RXCTL, - B43_NPHY_RXCTL_BSELU20); - else if (channel_type == NL80211_CHAN_HT40MINUS) - b43_phy_mask(dev, B43_NPHY_RXCTL, - ~B43_NPHY_RXCTL_BSELU20); + if (channel_type == NL80211_CHAN_HT40PLUS) { + b43_phy_set(dev, B43_NPHY_RXCTL, B43_NPHY_RXCTL_BSELU20); + if (phy->rev >= 7) + b43_phy_set(dev, 0x310, 0x8000); + } else if (channel_type == NL80211_CHAN_HT40MINUS) { + b43_phy_mask(dev, B43_NPHY_RXCTL, ~B43_NPHY_RXCTL_BSELU20); + if (phy->rev >= 7) + b43_phy_mask(dev, 0x310, (u16)~0x8000); + } - if (dev->phy.rev >= 3) { + if (phy->rev >= 7) { + const struct b43_phy_n_sfo_cfg *phy_regs = tabent_r7 ? + &(tabent_r7->phy_regs) : &(tabent_r7_2g->phy_regs); + + if (phy->radio_rev <= 4 || phy->radio_rev == 6) { + tmp = (channel->band == IEEE80211_BAND_5GHZ) ? 2 : 0; + b43_radio_maskset(dev, R2057_TIA_CONFIG_CORE0, ~2, tmp); + b43_radio_maskset(dev, R2057_TIA_CONFIG_CORE1, ~2, tmp); + } + + b43_radio_2057_setup(dev, tabent_r7, tabent_r7_2g); + b43_nphy_channel_setup(dev, phy_regs, channel); + } else if (phy->rev >= 3) { tmp = (channel->band == IEEE80211_BAND_5GHZ) ? 4 : 0; b43_radio_maskset(dev, 0x08, 0xFFFB, tmp); b43_radio_2056_setup(dev, tabent_r3); diff --git a/drivers/net/wireless/b43/radio_2057.c b/drivers/net/wireless/b43/radio_2057.c index 5e49440d4125..df3574545819 100644 --- a/drivers/net/wireless/b43/radio_2057.c +++ b/drivers/net/wireless/b43/radio_2057.c @@ -86,6 +86,7 @@ static u16 r2057_rev7_init[][2] = { { 0x1B7, 0x05 }, { 0x1C2, 0xa0 }, }; +/* TODO: Which devices should use it? static u16 r2057_rev8_init[][2] = { { 0x00, 0x08 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, { 0x59, 0x88 }, @@ -102,6 +103,47 @@ static u16 r2057_rev8_init[][2] = { { 0x1A6, 0x00 }, { 0x1AA, 0x00 }, { 0x1AB, 0x00 }, { 0x1AC, 0x00 }, { 0x1B7, 0x05 }, { 0x1C2, 0xa0 }, }; +*/ + +#define RADIOREGS7(r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, \ + r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, \ + r20, r21, r22, r23, r24, r25, r26, r27) \ + .radio_vcocal_countval0 = r00, \ + .radio_vcocal_countval1 = r01, \ + .radio_rfpll_refmaster_sparextalsize = r02, \ + .radio_rfpll_loopfilter_r1 = r03, \ + .radio_rfpll_loopfilter_c2 = r04, \ + .radio_rfpll_loopfilter_c1 = r05, \ + .radio_cp_kpd_idac = r06, \ + .radio_rfpll_mmd0 = r07, \ + .radio_rfpll_mmd1 = r08, \ + .radio_vcobuf_tune = r09, \ + .radio_logen_mx2g_tune = r10, \ + .radio_logen_mx5g_tune = r11, \ + .radio_logen_indbuf2g_tune = r12, \ + .radio_logen_indbuf5g_tune = r13, \ + .radio_txmix2g_tune_boost_pu_core0 = r14, \ + .radio_pad2g_tune_pus_core0 = r15, \ + .radio_pga_boost_tune_core0 = r16, \ + .radio_txmix5g_boost_tune_core0 = r17, \ + .radio_pad5g_tune_misc_pus_core0 = r18, \ + .radio_lna2g_tune_core0 = r19, \ + .radio_lna5g_tune_core0 = r20, \ + .radio_txmix2g_tune_boost_pu_core1 = r21, \ + .radio_pad2g_tune_pus_core1 = r22, \ + .radio_pga_boost_tune_core1 = r23, \ + .radio_txmix5g_boost_tune_core1 = r24, \ + .radio_pad5g_tune_misc_pus_core1 = r25, \ + .radio_lna2g_tune_core1 = r26, \ + .radio_lna5g_tune_core1 = r27 + +#define PHYREGS(r0, r1, r2, r3, r4, r5) \ + .phy_regs.phy_bw1a = r0, \ + .phy_regs.phy_bw2 = r1, \ + .phy_regs.phy_bw3 = r2, \ + .phy_regs.phy_bw4 = r3, \ + .phy_regs.phy_bw5 = r4, \ + .phy_regs.phy_bw6 = r5 void r2057_upload_inittabs(struct b43_wldev *dev) { @@ -109,25 +151,26 @@ void r2057_upload_inittabs(struct b43_wldev *dev) u16 *table = NULL; u16 size, i; - if (phy->rev == 7) { + switch (phy->rev) { + case 7: table = r2057_rev4_init[0]; size = ARRAY_SIZE(r2057_rev4_init); - } else if (phy->rev == 8 || phy->rev == 9) { + break; + case 8: if (phy->radio_rev == 5) { - if (phy->radio_rev == 8) { - table = r2057_rev5_init[0]; - size = ARRAY_SIZE(r2057_rev5_init); - } else { - table = r2057_rev5a_init[0]; - size = ARRAY_SIZE(r2057_rev5a_init); - } + table = r2057_rev5_init[0]; + size = ARRAY_SIZE(r2057_rev5_init); } else if (phy->radio_rev == 7) { table = r2057_rev7_init[0]; size = ARRAY_SIZE(r2057_rev7_init); - } else if (phy->radio_rev == 9) { - table = r2057_rev8_init[0]; - size = ARRAY_SIZE(r2057_rev8_init); } + break; + case 9: + if (phy->radio_rev == 5) { + table = r2057_rev5a_init[0]; + size = ARRAY_SIZE(r2057_rev5a_init); + } + break; } B43_WARN_ON(!table); @@ -137,3 +180,40 @@ void r2057_upload_inittabs(struct b43_wldev *dev) b43_radio_write(dev, table[0], table[1]); } } + +void r2057_get_chantabent_rev7(struct b43_wldev *dev, u16 freq, + const struct b43_nphy_chantabent_rev7 **tabent_r7, + const struct b43_nphy_chantabent_rev7_2g **tabent_r7_2g) +{ + struct b43_phy *phy = &dev->phy; + const struct b43_nphy_chantabent_rev7 *e_r7 = NULL; + const struct b43_nphy_chantabent_rev7_2g *e_r7_2g = NULL; + unsigned int len, i; + + *tabent_r7 = NULL; + *tabent_r7_2g = NULL; + + /* TODO */ + switch (phy->rev) { + default: + break; + } + + if (e_r7) { + for (i = 0; i < len; i++, e_r7++) { + if (e_r7->freq == freq) { + *tabent_r7 = e_r7; + return; + } + } + } else if (e_r7_2g) { + for (i = 0; i < len; i++, e_r7_2g++) { + if (e_r7_2g->freq == freq) { + *tabent_r7_2g = e_r7_2g; + return; + } + } + } else { + B43_WARN_ON(1); + } +} diff --git a/drivers/net/wireless/b43/radio_2057.h b/drivers/net/wireless/b43/radio_2057.h index eeebd8fbeb0d..675d1bb64429 100644 --- a/drivers/net/wireless/b43/radio_2057.h +++ b/drivers/net/wireless/b43/radio_2057.h @@ -425,6 +425,72 @@ #define R2057_VCM_MASK 0x7 +struct b43_nphy_chantabent_rev7 { + /* The channel frequency in MHz */ + u16 freq; + /* Radio regs values on channelswitch */ + u8 radio_vcocal_countval0; + u8 radio_vcocal_countval1; + u8 radio_rfpll_refmaster_sparextalsize; + u8 radio_rfpll_loopfilter_r1; + u8 radio_rfpll_loopfilter_c2; + u8 radio_rfpll_loopfilter_c1; + u8 radio_cp_kpd_idac; + u8 radio_rfpll_mmd0; + u8 radio_rfpll_mmd1; + u8 radio_vcobuf_tune; + u8 radio_logen_mx2g_tune; + u8 radio_logen_mx5g_tune; + u8 radio_logen_indbuf2g_tune; + u8 radio_logen_indbuf5g_tune; + u8 radio_txmix2g_tune_boost_pu_core0; + u8 radio_pad2g_tune_pus_core0; + u8 radio_pga_boost_tune_core0; + u8 radio_txmix5g_boost_tune_core0; + u8 radio_pad5g_tune_misc_pus_core0; + u8 radio_lna2g_tune_core0; + u8 radio_lna5g_tune_core0; + u8 radio_txmix2g_tune_boost_pu_core1; + u8 radio_pad2g_tune_pus_core1; + u8 radio_pga_boost_tune_core1; + u8 radio_txmix5g_boost_tune_core1; + u8 radio_pad5g_tune_misc_pus_core1; + u8 radio_lna2g_tune_core1; + u8 radio_lna5g_tune_core1; + /* PHY res values on channelswitch */ + struct b43_phy_n_sfo_cfg phy_regs; +}; + +struct b43_nphy_chantabent_rev7_2g { + /* The channel frequency in MHz */ + u16 freq; + /* Radio regs values on channelswitch */ + u8 radio_vcocal_countval0; + u8 radio_vcocal_countval1; + u8 radio_rfpll_refmaster_sparextalsize; + u8 radio_rfpll_loopfilter_r1; + u8 radio_rfpll_loopfilter_c2; + u8 radio_rfpll_loopfilter_c1; + u8 radio_cp_kpd_idac; + u8 radio_rfpll_mmd0; + u8 radio_rfpll_mmd1; + u8 radio_vcobuf_tune; + u8 radio_logen_mx2g_tune; + u8 radio_logen_indbuf2g_tune; + u8 radio_txmix2g_tune_boost_pu_core0; + u8 radio_pad2g_tune_pus_core0; + u8 radio_lna2g_tune_core0; + u8 radio_txmix2g_tune_boost_pu_core1; + u8 radio_pad2g_tune_pus_core1; + u8 radio_lna2g_tune_core1; + /* PHY regs values on channelswitch */ + struct b43_phy_n_sfo_cfg phy_regs; +}; + void r2057_upload_inittabs(struct b43_wldev *dev); +void r2057_get_chantabent_rev7(struct b43_wldev *dev, u16 freq, + const struct b43_nphy_chantabent_rev7 **tabent_r7, + const struct b43_nphy_chantabent_rev7_2g **tabent_r7_2g); + #endif /* B43_RADIO_2057_H_ */ From 15be8e89cdd999124a2307ffd6dacb895c3b802e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 1 Jul 2014 16:33:57 +0200 Subject: [PATCH 226/274] b43: add more bcma cores MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds some cores with 0x2057 radio which will be supported soon as well as core 40 that I missed in the earlier firmware patch. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 3740b76162f9..ca4a19077d7e 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -122,7 +122,11 @@ static const struct bcma_device_id b43_bcma_tbl[] = { BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x11, BCMA_ANY_CLASS), BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x17, BCMA_ANY_CLASS), BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x18, BCMA_ANY_CLASS), + BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1C, BCMA_ANY_CLASS), BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1D, BCMA_ANY_CLASS), + BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1E, BCMA_ANY_CLASS), + BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x28, BCMA_ANY_CLASS), + BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x2A, BCMA_ANY_CLASS), BCMA_CORETABLE_END }; MODULE_DEVICE_TABLE(bcma, b43_bcma_tbl); @@ -2218,6 +2222,10 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) if (phy->type == B43_PHYTYPE_AC) filename = "ucode42"; break; + case 40: + if (phy->type == B43_PHYTYPE_AC) + filename = "ucode40"; + break; case 33: if (phy->type == B43_PHYTYPE_LCN40) filename = "ucode33_lcn40"; @@ -2343,6 +2351,8 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) case B43_PHYTYPE_AC: if (rev == 42) filename = "ac1initvals42"; + else if (rev == 40) + filename = "ac0initvals40"; break; } if (!filename) @@ -2401,6 +2411,8 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) case B43_PHYTYPE_AC: if (rev == 42) filename = "ac1bsinitvals42"; + else if (rev == 40) + filename = "ac0bsinitvals40"; break; } if (!filename) From 43d826ca5979927131685cc2092c7ce862cb91cd Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 25 Jun 2014 09:12:30 +0300 Subject: [PATCH 227/274] iwlwifi: dvm: don't enable CTS to self We should always prefer to use full RTS protection. Using CTS to self gives a meaningless improvement, but this flow is much harder for the firmware which is likely to have issues with it. CC: Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/dvm/rxon.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/dvm/rxon.c b/drivers/net/wireless/iwlwifi/dvm/rxon.c index ed50de6362ed..6dc5dd3ced44 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rxon.c +++ b/drivers/net/wireless/iwlwifi/dvm/rxon.c @@ -1068,13 +1068,6 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) /* recalculate basic rates */ iwl_calc_basic_rates(priv, ctx); - /* - * force CTS-to-self frames protection if RTS-CTS is not preferred - * one aggregation protection method - */ - if (!priv->hw_params.use_rts_for_aggregation) - ctx->staging.flags |= RXON_FLG_SELF_CTS_EN; - if ((ctx->vif && ctx->vif->bss_conf.use_short_slot) || !(ctx->staging.flags & RXON_FLG_BAND_24G_MSK)) ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; @@ -1480,11 +1473,6 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw, else ctx->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK; - if (bss_conf->use_cts_prot) - ctx->staging.flags |= RXON_FLG_SELF_CTS_EN; - else - ctx->staging.flags &= ~RXON_FLG_SELF_CTS_EN; - memcpy(ctx->staging.bssid_addr, bss_conf->bssid, ETH_ALEN); if (vif->type == NL80211_IFTYPE_AP || From dc271ee0d04d12d6bfabacbec803289a7072fbd9 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 3 Jul 2014 20:46:35 +0300 Subject: [PATCH 228/274] iwlwifi: mvm: disable CTS to Self Firmware folks seem say that this flag can make trouble. Drop it. The advantage of CTS to self is that it slightly reduces the cost of the protection, but make the protection less reliable. Cc: [3.13+] Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index 8b5302777632..725ba49576bf 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -667,10 +667,9 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, if (vif->bss_conf.qos) cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA); - if (vif->bss_conf.use_cts_prot) { + if (vif->bss_conf.use_cts_prot) cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT); - cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_SELF_CTS_EN); - } + IWL_DEBUG_RATE(mvm, "use_cts_prot %d, ht_operation_mode %d\n", vif->bss_conf.use_cts_prot, vif->bss_conf.ht_operation_mode); From 6d6a47516390d8baa634eb589010f9e9d76b84a1 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 26 Jun 2014 09:13:26 +0300 Subject: [PATCH 229/274] iwlwifi: fix naming mistake for the fw_monitor module parameter Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index bb842f4732dd..77e3178040b2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -1407,5 +1407,5 @@ MODULE_PARM_DESC(power_level, "default power save level (range from 1 - 5, default: 1)"); module_param_named(fw_monitor, iwlwifi_mod_params.fw_monitor, bool, S_IRUGO); -MODULE_PARM_DESC(dbgm, +MODULE_PARM_DESC(fw_monitor, "firmware monitor - to debug FW (default: false - needs lots of memory)"); From d4849277f92a0bfa08a988545ea527fc8e0c9571 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 24 Jun 2014 14:34:28 +0300 Subject: [PATCH 230/274] iwlwifi: remove wrong comment about alignment in iwl-fw-error-dump.h The chunks of data do not need to be multipliers of 4 nor 4-bytes aligned. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h index ced5ba95c23d..9fd860f82871 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h @@ -92,8 +92,8 @@ enum iwl_fw_error_dump_type { /** * struct iwl_fw_error_dump_data - data for one type * @type: %enum iwl_fw_error_dump_type - * @len: the length starting from %data - must be a multiplier of 4. - * @data: the data itself padded to be a multiplier of 4. + * @len: the length starting from %data + * @data: the data itself */ struct iwl_fw_error_dump_data { __le32 type; From 78dae98fab85f4cd2d38cfc3474dea6e87e7b53a Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 25 Jun 2014 13:46:10 +0300 Subject: [PATCH 231/274] iwlwifi: mvm: don't collect logs in the interrupt thread Instead of reading all the data in the context of the interrupt thread, collect the data in the restart flow before the actual restart takes place so that the device still has all the information. Remove iwl_mvm_fw_error_sram_dump and move its content to iwl_mvm_fw_error_dump. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mvm.h | 3 --- drivers/net/wireless/iwlwifi/mvm/ops.c | 31 +++++++++--------------- drivers/net/wireless/iwlwifi/mvm/utils.c | 22 ----------------- 3 files changed, 12 insertions(+), 44 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index f7e54a57f46d..4cc6788c68fb 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -595,8 +595,6 @@ struct iwl_mvm { /* -1 for always, 0 for never, >0 for that many times */ s8 restart_fw; void *fw_error_dump; - void *fw_error_sram; - u32 fw_error_sram_len; u32 *fw_error_rxf; u32 fw_error_rxf_len; @@ -734,7 +732,6 @@ u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx); void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm); #ifdef CONFIG_IWLWIFI_DEBUGFS void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm); -void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm); void iwl_mvm_fw_error_rxf_dump(struct iwl_mvm *mvm); #endif u8 first_antenna(u8 mask); diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 7bb763f3052b..889374d04fb1 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -550,7 +550,6 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) kfree(mvm->scan_cmd); vfree(mvm->fw_error_dump); - kfree(mvm->fw_error_sram); kfree(mvm->fw_error_rxf); kfree(mvm->mcast_filter_cmd); mvm->mcast_filter_cmd = NULL; @@ -828,6 +827,8 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) struct iwl_fw_error_dump_file *dump_file; struct iwl_fw_error_dump_data *dump_data; struct iwl_fw_error_dump_info *dump_info; + const struct fw_img *img; + u32 sram_len, sram_ofs; u32 file_len; u32 trans_len; @@ -836,9 +837,13 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) if (mvm->fw_error_dump) return; + img = &mvm->fw->img[mvm->cur_ucode]; + sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; + sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; + file_len = sizeof(*dump_file) + sizeof(*dump_data) * 3 + - mvm->fw_error_sram_len + + sram_len + mvm->fw_error_rxf_len + sizeof(*dump_info); @@ -870,6 +875,8 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) strncpy(dump_info->bus_human_readable, mvm->dev->bus->name, sizeof(dump_info->bus_human_readable)); + iwl_mvm_fw_error_rxf_dump(mvm); + dump_data = iwl_fw_error_next_data(dump_data); dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); dump_data->len = cpu_to_le32(mvm->fw_error_rxf_len); @@ -877,23 +884,14 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) dump_data = iwl_fw_error_next_data(dump_data); dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM); - dump_data->len = cpu_to_le32(mvm->fw_error_sram_len); - - /* - * No need for lock since at the stage the FW isn't loaded. So it - * can't assert - we are the only one who can possibly be accessing - * mvm->fw_error_sram right now. - */ - memcpy(dump_data->data, mvm->fw_error_sram, mvm->fw_error_sram_len); + dump_data->len = cpu_to_le32(sram_len); + iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_data->data, + sram_len); kfree(mvm->fw_error_rxf); mvm->fw_error_rxf = NULL; mvm->fw_error_rxf_len = 0; - kfree(mvm->fw_error_sram); - mvm->fw_error_sram = NULL; - mvm->fw_error_sram_len = 0; - if (trans_len) { void *buf = iwl_fw_error_next_data(dump_data); u32 real_trans_len = iwl_trans_dump_data(mvm->trans, buf, @@ -911,11 +909,6 @@ static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode) iwl_mvm_dump_nic_error_log(mvm); -#ifdef CONFIG_IWLWIFI_DEBUGFS - iwl_mvm_fw_error_sram_dump(mvm); - iwl_mvm_fw_error_rxf_dump(mvm); -#endif - iwl_mvm_nic_restart(mvm); } diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index aa9fc77e8413..15db97c7d822 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -520,28 +520,6 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) } #ifdef CONFIG_IWLWIFI_DEBUGFS -void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm) -{ - const struct fw_img *img; - u32 ofs, sram_len; - void *sram; - - if (!mvm->ucode_loaded || mvm->fw_error_sram || mvm->fw_error_dump) - return; - - img = &mvm->fw->img[mvm->cur_ucode]; - ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; - sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; - - sram = kzalloc(sram_len, GFP_ATOMIC); - if (!sram) - return; - - iwl_trans_read_mem_bytes(mvm->trans, ofs, sram, sram_len); - mvm->fw_error_sram = sram; - mvm->fw_error_sram_len = sram_len; -} - void iwl_mvm_fw_error_rxf_dump(struct iwl_mvm *mvm) { int i, reg_val; From 655e6d6db21b0c0d411aef9d816816fb68b0496c Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 25 Jun 2014 14:08:58 +0300 Subject: [PATCH 232/274] iwlwifi: mvm: kill iwl_mvm_fw_error_rxf_dump Its content can move to the caller. While at it, move iwl_mvm_fw_error_rxf_dump to caller. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 100 ++++++++++++++++++++ drivers/net/wireless/iwlwifi/mvm/mvm.h | 6 -- drivers/net/wireless/iwlwifi/mvm/ops.c | 83 ---------------- drivers/net/wireless/iwlwifi/mvm/utils.c | 43 --------- 4 files changed, 100 insertions(+), 132 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 9feca4a6bacf..9661a526ed51 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -80,6 +80,8 @@ #include "fw-api-scan.h" #include "iwl-phy-db.h" #include "testmode.h" +#include "iwl-fw-error-dump.h" +#include "iwl-prph.h" static const struct ieee80211_iface_limit iwl_mvm_limits[] = { { @@ -645,6 +647,104 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac, mvmvif->phy_ctxt = NULL; } +#ifdef CONFIG_IWLWIFI_DEBUGFS +static void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) +{ + struct iwl_fw_error_dump_file *dump_file; + struct iwl_fw_error_dump_data *dump_data; + struct iwl_fw_error_dump_info *dump_info; + const struct fw_img *img; + u32 sram_len, sram_ofs; + u32 file_len, rxf_len; + unsigned long flags; + u32 trans_len; + int reg_val; + + lockdep_assert_held(&mvm->mutex); + + if (mvm->fw_error_dump) + return; + + img = &mvm->fw->img[mvm->cur_ucode]; + sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; + sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; + + /* reading buffer size */ + reg_val = iwl_trans_read_prph(mvm->trans, RXF_SIZE_ADDR); + rxf_len = (reg_val & RXF_SIZE_BYTE_CNT_MSK) >> RXF_SIZE_BYTE_CND_POS; + + /* the register holds the value divided by 128 */ + rxf_len = rxf_len << 7; + + file_len = sizeof(*dump_file) + + sizeof(*dump_data) * 3 + + sram_len + + rxf_len + + sizeof(*dump_info); + + trans_len = iwl_trans_dump_data(mvm->trans, NULL, 0); + if (trans_len) + file_len += trans_len; + + dump_file = vmalloc(file_len); + if (!dump_file) + return; + + mvm->fw_error_dump = dump_file; + + dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER); + dump_file->file_len = cpu_to_le32(file_len); + dump_data = (void *)dump_file->data; + + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO); + dump_data->len = cpu_to_le32(sizeof(*dump_info)); + dump_info = (void *) dump_data->data; + dump_info->device_family = + mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ? + cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) : + cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8); + memcpy(dump_info->fw_human_readable, mvm->fw->human_readable, + sizeof(dump_info->fw_human_readable)); + strncpy(dump_info->dev_human_readable, mvm->cfg->name, + sizeof(dump_info->dev_human_readable)); + strncpy(dump_info->bus_human_readable, mvm->dev->bus->name, + sizeof(dump_info->bus_human_readable)); + + dump_data = iwl_fw_error_next_data(dump_data); + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); + dump_data->len = cpu_to_le32(rxf_len); + + if (iwl_trans_grab_nic_access(mvm->trans, false, &flags)) { + u32 *rxf = (void *)dump_data->data; + int i; + + for (i = 0; i < (rxf_len / sizeof(u32)); i++) { + iwl_trans_write_prph(mvm->trans, + RXF_LD_FENCE_OFFSET_ADDR, + i * sizeof(u32)); + rxf[i] = iwl_trans_read_prph(mvm->trans, + RXF_FIFO_RD_FENCE_ADDR); + } + iwl_trans_release_nic_access(mvm->trans, &flags); + } + + dump_data = iwl_fw_error_next_data(dump_data); + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM); + dump_data->len = cpu_to_le32(sram_len); + iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_data->data, + sram_len); + + if (trans_len) { + void *buf = iwl_fw_error_next_data(dump_data); + u32 real_trans_len = iwl_trans_dump_data(mvm->trans, buf, + trans_len); + dump_data = (void *)((u8 *)buf + real_trans_len); + dump_file->file_len = + cpu_to_le32(file_len - trans_len + real_trans_len); + } +} +#endif + static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) { #ifdef CONFIG_IWLWIFI_DEBUGFS diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 4cc6788c68fb..e067d9762603 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -595,8 +595,6 @@ struct iwl_mvm { /* -1 for always, 0 for never, >0 for that many times */ s8 restart_fw; void *fw_error_dump; - u32 *fw_error_rxf; - u32 fw_error_rxf_len; #ifdef CONFIG_IWLWIFI_LEDS struct led_classdev led; @@ -730,10 +728,6 @@ void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags, struct ieee80211_tx_rate *r); u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx); void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm); -#ifdef CONFIG_IWLWIFI_DEBUGFS -void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm); -void iwl_mvm_fw_error_rxf_dump(struct iwl_mvm *mvm); -#endif u8 first_antenna(u8 mask); u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx); diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 889374d04fb1..15c13a722a93 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -550,7 +550,6 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) kfree(mvm->scan_cmd); vfree(mvm->fw_error_dump); - kfree(mvm->fw_error_rxf); kfree(mvm->mcast_filter_cmd); mvm->mcast_filter_cmd = NULL; @@ -821,88 +820,6 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm) } } -#ifdef CONFIG_IWLWIFI_DEBUGFS -void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) -{ - struct iwl_fw_error_dump_file *dump_file; - struct iwl_fw_error_dump_data *dump_data; - struct iwl_fw_error_dump_info *dump_info; - const struct fw_img *img; - u32 sram_len, sram_ofs; - u32 file_len; - u32 trans_len; - - lockdep_assert_held(&mvm->mutex); - - if (mvm->fw_error_dump) - return; - - img = &mvm->fw->img[mvm->cur_ucode]; - sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; - sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; - - file_len = sizeof(*dump_file) + - sizeof(*dump_data) * 3 + - sram_len + - mvm->fw_error_rxf_len + - sizeof(*dump_info); - - trans_len = iwl_trans_dump_data(mvm->trans, NULL, 0); - if (trans_len) - file_len += trans_len; - - dump_file = vmalloc(file_len); - if (!dump_file) - return; - - mvm->fw_error_dump = dump_file; - - dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER); - dump_file->file_len = cpu_to_le32(file_len); - dump_data = (void *)dump_file->data; - - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO); - dump_data->len = cpu_to_le32(sizeof(*dump_info)); - dump_info = (void *) dump_data->data; - dump_info->device_family = - mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ? - cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) : - cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8); - memcpy(dump_info->fw_human_readable, mvm->fw->human_readable, - sizeof(dump_info->fw_human_readable)); - strncpy(dump_info->dev_human_readable, mvm->cfg->name, - sizeof(dump_info->dev_human_readable)); - strncpy(dump_info->bus_human_readable, mvm->dev->bus->name, - sizeof(dump_info->bus_human_readable)); - - iwl_mvm_fw_error_rxf_dump(mvm); - - dump_data = iwl_fw_error_next_data(dump_data); - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); - dump_data->len = cpu_to_le32(mvm->fw_error_rxf_len); - memcpy(dump_data->data, mvm->fw_error_rxf, mvm->fw_error_rxf_len); - - dump_data = iwl_fw_error_next_data(dump_data); - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM); - dump_data->len = cpu_to_le32(sram_len); - iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_data->data, - sram_len); - - kfree(mvm->fw_error_rxf); - mvm->fw_error_rxf = NULL; - mvm->fw_error_rxf_len = 0; - - if (trans_len) { - void *buf = iwl_fw_error_next_data(dump_data); - u32 real_trans_len = iwl_trans_dump_data(mvm->trans, buf, - trans_len); - dump_data = (void *)((u8 *)buf + real_trans_len); - dump_file->file_len = - cpu_to_le32(file_len - trans_len + real_trans_len); - } -} -#endif - static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index 15db97c7d822..ac249da8a22b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -519,49 +519,6 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) iwl_mvm_dump_umac_error_log(mvm); } -#ifdef CONFIG_IWLWIFI_DEBUGFS -void iwl_mvm_fw_error_rxf_dump(struct iwl_mvm *mvm) -{ - int i, reg_val; - unsigned long flags; - - if (!mvm->ucode_loaded || mvm->fw_error_rxf || mvm->fw_error_dump) - return; - - /* reading buffer size */ - reg_val = iwl_trans_read_prph(mvm->trans, RXF_SIZE_ADDR); - mvm->fw_error_rxf_len = - (reg_val & RXF_SIZE_BYTE_CNT_MSK) >> RXF_SIZE_BYTE_CND_POS; - - /* the register holds the value divided by 128 */ - mvm->fw_error_rxf_len = mvm->fw_error_rxf_len << 7; - - if (!mvm->fw_error_rxf_len) - return; - - mvm->fw_error_rxf = kzalloc(mvm->fw_error_rxf_len, GFP_ATOMIC); - if (!mvm->fw_error_rxf) { - mvm->fw_error_rxf_len = 0; - return; - } - - if (!iwl_trans_grab_nic_access(mvm->trans, false, &flags)) { - kfree(mvm->fw_error_rxf); - mvm->fw_error_rxf = NULL; - mvm->fw_error_rxf_len = 0; - return; - } - - for (i = 0; i < (mvm->fw_error_rxf_len / sizeof(u32)); i++) { - iwl_trans_write_prph(mvm->trans, RXF_LD_FENCE_OFFSET_ADDR, - i * sizeof(u32)); - mvm->fw_error_rxf[i] = - iwl_trans_read_prph(mvm->trans, RXF_FIFO_RD_FENCE_ADDR); - } - iwl_trans_release_nic_access(mvm->trans, &flags); -} -#endif - /** * iwl_mvm_send_lq_cmd() - Send link quality command * @init: This command is sent as part of station initialization right From 5bfe6f53283de44855ee45a102210abbfac995f9 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 25 Jun 2014 16:21:43 +0300 Subject: [PATCH 233/274] iwlwifi: mvm: update layout of firmware error dump The memory was not zeroed - fix that. Also update the iwl_fw_error_dump_info structure. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 9661a526ed51..5e425d3fddeb 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -686,7 +686,7 @@ static void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) if (trans_len) file_len += trans_len; - dump_file = vmalloc(file_len); + dump_file = vzalloc(file_len); if (!dump_file) return; From d40fc489f308951f6361742e8b9689326c831f41 Mon Sep 17 00:00:00 2001 From: Gregory Greenman Date: Wed, 25 Jun 2014 14:08:50 +0200 Subject: [PATCH 234/274] iwlwifi: mvm: wait for d0i3 exit in add interface flow This patch makes sure there're no target accesses in the add interface flow before d0i3 exit completes. Signed-off-by: Gregory Greenman Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 40 ++++++++++++++++----- drivers/net/wireless/iwlwifi/mvm/mvm.h | 1 + 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 5e425d3fddeb..7dde944a68fb 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -243,6 +243,21 @@ iwl_mvm_unref_all_except(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref) } } +static int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type) +{ + iwl_mvm_ref(mvm, ref_type); + + if (!wait_event_timeout(mvm->d0i3_exit_waitq, + !test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status), + HZ)) { + WARN_ON_ONCE(1); + iwl_mvm_unref(mvm, ref_type); + return -EIO; + } + + return 0; +} + static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm) { int i; @@ -559,9 +574,6 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: case IEEE80211_AMPDU_TX_OPERATIONAL: - iwl_mvm_ref(mvm, IWL_MVM_REF_TX_AGG); - tx_agg_ref = true; - /* * for tx start, wait synchronously until D0i3 exit to * get the correct sequence number for the tid. @@ -570,12 +582,11 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, * by the trans layer (unlike commands), so wait for * d0i3 exit in these cases as well. */ - if (!wait_event_timeout(mvm->d0i3_exit_waitq, - !test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status), HZ)) { - WARN_ON_ONCE(1); - iwl_mvm_unref(mvm, IWL_MVM_REF_TX_AGG); - return -EIO; - } + ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_TX_AGG); + if (ret) + return ret; + + tx_agg_ref = true; break; default: break; @@ -903,6 +914,15 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; + /* + * make sure D0i3 exit is completed, otherwise a target access + * during tx queue configuration could be done when still in + * D0i3 state. + */ + ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_ADD_IF); + if (ret) + return ret; + /* * Not much to do here. The stack will not allow interface * types or combinations that we didn't advertise, so we @@ -1017,6 +1037,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, out_unlock: mutex_unlock(&mvm->mutex); + iwl_mvm_unref(mvm, IWL_MVM_REF_ADD_IF); + return ret; } diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index e067d9762603..6fe93a7335c1 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -230,6 +230,7 @@ enum iwl_mvm_ref_type { IWL_MVM_REF_USER, IWL_MVM_REF_TX, IWL_MVM_REF_TX_AGG, + IWL_MVM_REF_ADD_IF, IWL_MVM_REF_EXIT_WORK, IWL_MVM_REF_COUNT, From 1e0b393a4478fdce0e39a5b7fe7d05ee4ab46105 Mon Sep 17 00:00:00 2001 From: Eran Harary Date: Wed, 11 Jun 2014 11:37:09 +0300 Subject: [PATCH 235/274] iwlwifi: mvm: read the mac address in family 8000 In family 8000 products the MAC address in the OTP could be in either: - WFPM address - PCIE address In sdio product we should read it from the WFPM, in pcie product we should read it from the PCIe location. This is relevant only from otp version 0xE08 and above. While at it, fix the bytes order in version 0xE08. Signed-off-by: Eran Harary Reviewed-by: Liad Kaufman Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-nvm-parse.c | 58 ++++++++++++++++---- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index f0ae038f3339..dd76ed28884d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -63,6 +63,7 @@ #include #include #include +#include #include "iwl-drv.h" #include "iwl-modparams.h" #include "iwl-nvm-parse.h" @@ -87,8 +88,10 @@ enum wkp_nvm_offsets { enum family_8000_nvm_offsets { /* NVM HW-Section offset (in words) definitions */ - HW_ADDR0_FAMILY_8000 = 0x12, - HW_ADDR1_FAMILY_8000 = 0x16, + HW_ADDR0_WFPM_FAMILY_8000 = 0x12, + HW_ADDR1_WFPM_FAMILY_8000 = 0x16, + HW_ADDR0_PCIE_FAMILY_8000 = 0x8A, + HW_ADDR1_PCIE_FAMILY_8000 = 0x8E, MAC_ADDRESS_OVERRIDE_FAMILY_8000 = 1, /* NVM SW-Section offset (in words) definitions */ @@ -504,16 +507,49 @@ static void iwl_set_hw_address_family_8000(struct device *dev, } if (nvm_hw) { - /* take the MAC address from the OTP */ - hw_addr = (const u8 *)(nvm_hw + HW_ADDR0_FAMILY_8000); - data->hw_addr[0] = hw_addr[3]; - data->hw_addr[1] = hw_addr[2]; - data->hw_addr[2] = hw_addr[1]; - data->hw_addr[3] = hw_addr[0]; + /* read the MAC address from OTP */ + if (!dev_is_pci(dev) || (data->nvm_version < 0xE08)) { + /* read the mac address from the WFPM location */ + hw_addr = (const u8 *)(nvm_hw + + HW_ADDR0_WFPM_FAMILY_8000); + data->hw_addr[0] = hw_addr[3]; + data->hw_addr[1] = hw_addr[2]; + data->hw_addr[2] = hw_addr[1]; + data->hw_addr[3] = hw_addr[0]; + + hw_addr = (const u8 *)(nvm_hw + + HW_ADDR1_WFPM_FAMILY_8000); + data->hw_addr[4] = hw_addr[1]; + data->hw_addr[5] = hw_addr[0]; + } else if ((data->nvm_version >= 0xE08) && + (data->nvm_version < 0xE0B)) { + /* read "reverse order" from the PCIe location */ + hw_addr = (const u8 *)(nvm_hw + + HW_ADDR0_PCIE_FAMILY_8000); + data->hw_addr[5] = hw_addr[2]; + data->hw_addr[4] = hw_addr[1]; + data->hw_addr[3] = hw_addr[0]; + + hw_addr = (const u8 *)(nvm_hw + + HW_ADDR1_PCIE_FAMILY_8000); + data->hw_addr[2] = hw_addr[3]; + data->hw_addr[1] = hw_addr[2]; + data->hw_addr[0] = hw_addr[1]; + } else { + /* read from the PCIe location */ + hw_addr = (const u8 *)(nvm_hw + + HW_ADDR0_PCIE_FAMILY_8000); + data->hw_addr[5] = hw_addr[0]; + data->hw_addr[4] = hw_addr[1]; + data->hw_addr[3] = hw_addr[2]; + + hw_addr = (const u8 *)(nvm_hw + + HW_ADDR1_PCIE_FAMILY_8000); + data->hw_addr[2] = hw_addr[1]; + data->hw_addr[1] = hw_addr[2]; + data->hw_addr[0] = hw_addr[3]; + } - hw_addr = (const u8 *)(nvm_hw + HW_ADDR1_FAMILY_8000); - data->hw_addr[4] = hw_addr[1]; - data->hw_addr[5] = hw_addr[0]; return; } From c544e9c4c32acb222782aeff2f24e55e7497e4bc Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 26 Jun 2014 09:54:23 +0300 Subject: [PATCH 236/274] iwlwifi: rename iwl_fw_error_fw_mon to iwl_fw_error_dump_fw_mon This is matches the convention of the other structures. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h | 4 ++-- drivers/net/wireless/iwlwifi/pcie/trans.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h index 9fd860f82871..c39a0b899e83 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h @@ -147,14 +147,14 @@ struct iwl_fw_error_dump_info { } __packed; /** - * struct iwl_fw_error_fw_mon - FW monitor data + * struct iwl_fw_error_dump_fw_mon - FW monitor data * @fw_mon_wr_ptr: the position of the write pointer in the cyclic buffer * @fw_mon_base_ptr: base pointer of the data * @fw_mon_cycle_cnt: number of wrap arounds * @reserved: for future use * @data: captured data */ -struct iwl_fw_error_fw_mon { +struct iwl_fw_error_dump_fw_mon { __le32 fw_mon_wr_ptr; __le32 fw_mon_base_ptr; __le32 fw_mon_cycle_cnt; diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 5703a3d7799b..5b5b0d8c6f60 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -1787,7 +1787,7 @@ static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans, cmdq->q.n_window * (sizeof(*txcmd) + TFD_MAX_PAYLOAD_SIZE); if (trans_pcie->fw_mon_page) - len += sizeof(*data) + sizeof(struct iwl_fw_error_fw_mon) + + len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) + trans_pcie->fw_mon_size; if (!buf) @@ -1822,7 +1822,7 @@ static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans, len += sizeof(*data); if (trans_pcie->fw_mon_page) { - struct iwl_fw_error_fw_mon *fw_mon_data; + struct iwl_fw_error_dump_fw_mon *fw_mon_data; data = iwl_fw_error_next_data(data); data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FW_MONITOR); From 7f514f5c8767b20dbd8706985cdc00a56a9fd280 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 2 Jul 2014 12:00:51 +0300 Subject: [PATCH 237/274] iwlwifi: mvm: remove unused flags from TX command These flags are not used by the firmware anyway. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h | 16 ++-------------- drivers/net/wireless/iwlwifi/mvm/tx.c | 1 - 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h index bdd6ff648626..d6073f67b212 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h @@ -69,10 +69,8 @@ * @TX_CMD_FLG_ACK: expect ACK from receiving station * @TX_CMD_FLG_STA_RATE: use RS table with initial index from the TX command. * Otherwise, use rate_n_flags from the TX command - * @TX_CMD_FLG_BA: this frame is a block ack * @TX_CMD_FLG_BAR: this frame is a BA request, immediate BAR is expected * Must set TX_CMD_FLG_ACK with this flag. - * @TX_CMD_FLG_TXOP_PROT: protect frame with full TXOP protection * @TX_CMD_FLG_VHT_NDPA: mark frame is NDPA for VHT beamformer sequence * @TX_CMD_FLG_HT_NDPA: mark frame is NDPA for HT beamformer sequence * @TX_CMD_FLG_CSI_FDBK2HOST: mark to send feedback to host (only if good CRC) @@ -82,12 +80,10 @@ * @TX_CMD_FLG_SEQ_CTL: set if FW should override the sequence control. * Should be set for mgmt, non-QOS data, mcast, bcast and in scan command * @TX_CMD_FLG_MORE_FRAG: this frame is non-last MPDU - * @TX_CMD_FLG_NEXT_FRAME: this frame includes information of the next frame * @TX_CMD_FLG_TSF: FW should calculate and insert TSF in the frame * Should be set for beacons and probe responses * @TX_CMD_FLG_CALIB: activate PA TX power calibrations * @TX_CMD_FLG_KEEP_SEQ_CTL: if seq_ctl is set, don't increase inner seq count - * @TX_CMD_FLG_AGG_START: allow this frame to start aggregation * @TX_CMD_FLG_MH_PAD: driver inserted 2 byte padding after MAC header. * Should be set for 26/30 length MAC headers * @TX_CMD_FLG_RESP_TO_DRV: zero this if the response should go only to FW @@ -103,7 +99,6 @@ enum iwl_tx_flags { TX_CMD_FLG_PROT_REQUIRE = BIT(0), TX_CMD_FLG_ACK = BIT(3), TX_CMD_FLG_STA_RATE = BIT(4), - TX_CMD_FLG_BA = BIT(5), TX_CMD_FLG_BAR = BIT(6), TX_CMD_FLG_TXOP_PROT = BIT(7), TX_CMD_FLG_VHT_NDPA = BIT(8), @@ -113,11 +108,9 @@ enum iwl_tx_flags { TX_CMD_FLG_BT_DIS = BIT(12), TX_CMD_FLG_SEQ_CTL = BIT(13), TX_CMD_FLG_MORE_FRAG = BIT(14), - TX_CMD_FLG_NEXT_FRAME = BIT(15), TX_CMD_FLG_TSF = BIT(16), TX_CMD_FLG_CALIB = BIT(17), TX_CMD_FLG_KEEP_SEQ_CTL = BIT(18), - TX_CMD_FLG_AGG_START = BIT(19), TX_CMD_FLG_MH_PAD = BIT(20), TX_CMD_FLG_RESP_TO_DRV = BIT(21), TX_CMD_FLG_CCMP_AGG = BIT(22), @@ -191,8 +184,6 @@ enum iwl_tx_flags { * struct iwl_tx_cmd - TX command struct to FW * ( TX_CMD = 0x1c ) * @len: in bytes of the payload, see below for details - * @next_frame_len: same as len, but for next frame (0 if not applicable) - * Used for fragmentation and bursting, but not in 11n aggregation. * @tx_flags: combination of TX_CMD_FLG_* * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is * cleared. Combination of RATE_MCS_* @@ -210,8 +201,6 @@ enum iwl_tx_flags { * @data_retry_limit: max attempts to send the data packet * @tid_spec: TID/tspec * @pm_frame_timeout: PM TX frame timeout - * @driver_txop: duration od EDCA TXOP, in 32-usec units. Set this if not - * specified by HCCA protocol * * The byte count (both len and next_frame_len) includes MAC header * (24/26/30/32 bytes) @@ -241,8 +230,7 @@ struct iwl_tx_cmd { u8 initial_rate_index; u8 reserved2; u8 key[16]; - __le16 next_frame_flags; - __le16 reserved3; + __le32 reserved3; __le32 life_time; __le32 dram_lsb_ptr; u8 dram_msb_ptr; @@ -250,7 +238,7 @@ struct iwl_tx_cmd { u8 data_retry_limit; u8 tid_tspec; __le16 pm_frame_timeout; - __le16 driver_txop; + __le16 reserved4; u8 payload[0]; struct ieee80211_hdr hdr[0]; } __packed; /* TX_CMD_API_S_VER_3 */ diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index 4f7cff506ee7..fa87a4ba25ec 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -131,7 +131,6 @@ static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, !is_multicast_ether_addr(ieee80211_get_DA(hdr))) tx_flags |= TX_CMD_FLG_PROT_REQUIRE; - tx_cmd->driver_txop = 0; tx_cmd->tx_flags = cpu_to_le32(tx_flags); /* Total # bytes to be transmitted */ tx_cmd->len = cpu_to_le16((u16)skb->len); From 0ea8d0432c09f240b8dfdec0dc0e4abaf422b838 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 2 Apr 2014 09:31:36 +0300 Subject: [PATCH 238/274] iwlwifi: mvm: BT Coex - prepare towards new API A new API is coming. This new API is not backward compatible. So we need to keep the old commands to be able to work with the former API. Move all the current code into a new file: coex_legacy. If a firmware with the new API is detected, we currently just bail out since the implementation of the new API will come in future patches. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-fw.h | 2 + drivers/net/wireless/iwlwifi/mvm/Makefile | 2 +- drivers/net/wireless/iwlwifi/mvm/coex.c | 84 +- .../net/wireless/iwlwifi/mvm/coex_legacy.c | 1332 +++++++++++++++++ drivers/net/wireless/iwlwifi/mvm/debugfs.c | 4 +- .../net/wireless/iwlwifi/mvm/fw-api-coex.h | 36 +- drivers/net/wireless/iwlwifi/mvm/mvm.h | 26 +- 7 files changed, 1448 insertions(+), 38 deletions(-) create mode 100644 drivers/net/wireless/iwlwifi/mvm/coex_legacy.c diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index 92d5e75a6b1f..79b0508fddef 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -121,12 +121,14 @@ enum iwl_ucode_tlv_flag { * enum iwl_ucode_tlv_api - ucode api * @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field. * @IWL_UCODE_TLV_CAPA_EXTENDED_BEACON: Support Extended beacon notification + * @IWL_UCODE_TLV_API_BT_COEX_SPLIT: new API for BT Coex * @IWL_UCODE_TLV_API_CSA_FLOW: ucode can do unbind-bind flow for CSA. * @IWL_UCODE_TLV_API_DISABLE_STA_TX: ucode supports tx_disable bit. */ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID = BIT(0), IWL_UCODE_TLV_CAPA_EXTENDED_BEACON = BIT(1), + IWL_UCODE_TLV_API_BT_COEX_SPLIT = BIT(3), IWL_UCODE_TLV_API_CSA_FLOW = BIT(4), IWL_UCODE_TLV_API_DISABLE_STA_TX = BIT(5), }; diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile index c30d7f64ec1e..a28235913c2c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/Makefile +++ b/drivers/net/wireless/iwlwifi/mvm/Makefile @@ -2,7 +2,7 @@ obj-$(CONFIG_IWLMVM) += iwlmvm.o iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o sf.o iwlmvm-y += scan.o time-event.o rs.o -iwlmvm-y += power.o coex.o +iwlmvm-y += power.o coex.o coex_legacy.o iwlmvm-y += tt.o offloading.o iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c index 7e0388a32912..f471de3373c9 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex.c @@ -561,7 +561,7 @@ iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif) int iwl_send_bt_init_conf(struct iwl_mvm *mvm) { - struct iwl_bt_coex_cmd *bt_cmd; + struct iwl_bt_coex_cmd_old *bt_cmd; struct iwl_host_cmd cmd = { .id = BT_CONFIG, .len = { sizeof(*bt_cmd), }, @@ -570,6 +570,12 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm) int ret; u32 flags; + if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) + return iwl_send_bt_init_conf_old(mvm); + + /* TODO */ + return 0; + ret = iwl_send_bt_prio_tbl(mvm); if (ret) return ret; @@ -584,13 +590,13 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm) if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) { switch (mvm->bt_force_ant_mode) { case BT_FORCE_ANT_AUTO: - flags = BT_COEX_AUTO; + flags = BT_COEX_AUTO_OLD; break; case BT_FORCE_ANT_BT: - flags = BT_COEX_BT; + flags = BT_COEX_BT_OLD; break; case BT_FORCE_ANT_WIFI: - flags = BT_COEX_WIFI; + flags = BT_COEX_WIFI_OLD; break; default: WARN_ON(1); @@ -611,7 +617,7 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm) bt_cmd->override_secondary_lut = BT_COEX_INVALID_LUT; flags = iwlwifi_mod_params.bt_coex_active ? - BT_COEX_NW : BT_COEX_DISABLE; + BT_COEX_NW_OLD : BT_COEX_DISABLE_OLD; bt_cmd->flags = cpu_to_le32(flags); bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE | @@ -680,8 +686,8 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm, bool reduced_tx_power) { enum iwl_bt_kill_msk bt_kill_msk; - struct iwl_bt_coex_cmd *bt_cmd; - struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif; + struct iwl_bt_coex_cmd_old *bt_cmd; + struct iwl_bt_coex_profile_notif_old *notif = &mvm->last_bt_notif; struct iwl_host_cmd cmd = { .id = BT_CONFIG, .data[0] = &bt_cmd, @@ -722,7 +728,7 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm, if (!bt_cmd) return -ENOMEM; cmd.data[0] = bt_cmd; - bt_cmd->flags = cpu_to_le32(BT_COEX_NW); + bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD); bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]); bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]); @@ -743,7 +749,7 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm, static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, bool enable) { - struct iwl_bt_coex_cmd *bt_cmd; + struct iwl_bt_coex_cmd_old *bt_cmd; /* Send ASYNC since this can be sent from an atomic context */ struct iwl_host_cmd cmd = { .id = BT_CONFIG, @@ -766,7 +772,7 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, if (!bt_cmd) return -ENOMEM; cmd.data[0] = bt_cmd; - bt_cmd->flags = cpu_to_le32(BT_COEX_NW); + bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD); bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE | BT_VALID_REDUCED_TX_POWER); @@ -787,7 +793,7 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, } struct iwl_bt_iterator_data { - struct iwl_bt_coex_profile_notif *notif; + struct iwl_bt_coex_profile_notif_old *notif; struct iwl_mvm *mvm; u32 num_bss_ifaces; bool reduced_tx_power; @@ -977,7 +983,7 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) .notif = &mvm->last_bt_notif, .reduced_tx_power = true, }; - struct iwl_bt_coex_ci_cmd cmd = {}; + struct iwl_bt_coex_ci_cmd_old cmd = {}; u8 ci_bw_idx; /* Ignore updates if we are in force mode */ @@ -1063,8 +1069,13 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, struct iwl_device_cmd *dev_cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data; + struct iwl_bt_coex_profile_notif_old *notif = (void *)pkt->data; + if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) + return iwl_mvm_rx_bt_coex_notif_old(mvm, rxb, dev_cmd); + + /* TODO */ + return 0; IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n"); IWL_DEBUG_COEX(mvm, "\tBT status: %s\n", @@ -1148,6 +1159,14 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, }; int ret; + if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) { + iwl_mvm_bt_rssi_event_old(mvm, vif, rssi_event); + return; + } + + /* TODO */ + return; + lockdep_assert_held(&mvm->mutex); /* Ignore updates if we are in force mode */ @@ -1206,6 +1225,12 @@ u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); enum iwl_bt_coex_lut_type lut_type; + if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) + return iwl_mvm_coex_agg_time_limit_old(mvm, sta); + + /* TODO */ + return LINK_QUAL_AGG_TIME_LIMIT_DEF; + if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC) return LINK_QUAL_AGG_TIME_LIMIT_DEF; @@ -1228,6 +1253,12 @@ bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); enum iwl_bt_coex_lut_type lut_type; + if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) + return iwl_mvm_coex_agg_time_limit_old(mvm, sta); + + return true; + + /* TODO */ if (mvm->last_bt_notif.ttc_enabled) return true; @@ -1248,6 +1279,9 @@ bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm) { + if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) + return iwl_mvm_bt_coex_is_shared_ant_avail_old(mvm); + return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF; } @@ -1256,6 +1290,12 @@ bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, { u32 bt_activity = le32_to_cpu(mvm->last_bt_notif.bt_activity_grading); + if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) + return iwl_mvm_bt_coex_is_tpc_allowed_old(mvm, band); + + /* TODO */ + return false; + if (band != IEEE80211_BAND_2GHZ) return false; @@ -1296,6 +1336,14 @@ u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm) { + if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) { + iwl_mvm_bt_coex_vif_change_old(mvm); + return; + } + + /* TODO */ + return; + iwl_mvm_bt_coex_notif_handle(mvm); } @@ -1309,13 +1357,19 @@ int iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm, int ret; u8 lut; - struct iwl_bt_coex_cmd *bt_cmd; + struct iwl_bt_coex_cmd_old *bt_cmd; struct iwl_host_cmd cmd = { .id = BT_CONFIG, .len = { sizeof(*bt_cmd), }, .dataflags = { IWL_HCMD_DFL_NOCOPY, }, }; + if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) + return iwl_mvm_rx_ant_coupling_notif_old(mvm, rxb, dev_cmd); + + /* TODO */ + return 0; + if (!IWL_MVM_BT_COEX_CORUNNING) return 0; @@ -1354,7 +1408,7 @@ int iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm, return 0; cmd.data[0] = bt_cmd; - bt_cmd->flags = cpu_to_le32(BT_COEX_NW); + bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD); bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE | BT_VALID_CORUN_LUT_20 | BT_VALID_CORUN_LUT_40); diff --git a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c new file mode 100644 index 000000000000..ce50363d314b --- /dev/null +++ b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c @@ -0,0 +1,1332 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include +#include +#include + +#include "fw-api-coex.h" +#include "iwl-modparams.h" +#include "mvm.h" +#include "iwl-debug.h" + +#define EVENT_PRIO_ANT(_evt, _prio, _shrd_ant) \ + [(_evt)] = (((_prio) << BT_COEX_PRIO_TBL_PRIO_POS) | \ + ((_shrd_ant) << BT_COEX_PRIO_TBL_SHRD_ANT_POS)) + +static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = { + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB1, + BT_COEX_PRIO_TBL_PRIO_BYPASS, 0), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB2, + BT_COEX_PRIO_TBL_PRIO_BYPASS, 1), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1, + BT_COEX_PRIO_TBL_PRIO_LOW, 0), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2, + BT_COEX_PRIO_TBL_PRIO_LOW, 1), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1, + BT_COEX_PRIO_TBL_PRIO_HIGH, 0), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2, + BT_COEX_PRIO_TBL_PRIO_HIGH, 1), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_DTIM, + BT_COEX_PRIO_TBL_DISABLED, 0), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN52, + BT_COEX_PRIO_TBL_PRIO_COEX_OFF, 0), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN24, + BT_COEX_PRIO_TBL_PRIO_COEX_ON, 0), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_IDLE, + BT_COEX_PRIO_TBL_PRIO_COEX_IDLE, 0), + 0, 0, 0, 0, 0, 0, +}; + +#undef EVENT_PRIO_ANT + +#define BT_ANTENNA_COUPLING_THRESHOLD (30) + +static int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm) +{ + if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) + return 0; + + return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, 0, + sizeof(struct iwl_bt_coex_prio_tbl_cmd), + &iwl_bt_prio_tbl); +} + +static const __le32 iwl_bt_prio_boost[BT_COEX_BOOST_SIZE] = { + cpu_to_le32(0xf0f0f0f0), /* 50% */ + cpu_to_le32(0xc0c0c0c0), /* 25% */ + cpu_to_le32(0xfcfcfcfc), /* 75% */ + cpu_to_le32(0xfefefefe), /* 87.5% */ +}; + +static const __le32 iwl_single_shared_ant[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = { + { + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + }, + { + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + }, + { + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + }, +}; + +static const __le32 iwl_combined_lookup[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = { + { + /* Tight */ + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaeaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xcc00ff28), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xcc00aaaa), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xc0004000), + cpu_to_le32(0x00004000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xf0005000), + }, + { + /* Loose */ + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xcc00ff28), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xcc00aaaa), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xf0005000), + }, + { + /* Tx Tx disabled */ + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xeeaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xcc00ff28), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xcc00aaaa), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xf0005000), + }, +}; + +/* 20MHz / 40MHz below / 40Mhz above*/ +static const __le64 iwl_ci_mask[][3] = { + /* dummy entry for channel 0 */ + {cpu_to_le64(0), cpu_to_le64(0), cpu_to_le64(0)}, + { + cpu_to_le64(0x0000001FFFULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x00007FFFFFULL), + }, + { + cpu_to_le64(0x000000FFFFULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x0003FFFFFFULL), + }, + { + cpu_to_le64(0x000003FFFCULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x000FFFFFFCULL), + }, + { + cpu_to_le64(0x00001FFFE0ULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x007FFFFFE0ULL), + }, + { + cpu_to_le64(0x00007FFF80ULL), + cpu_to_le64(0x00007FFFFFULL), + cpu_to_le64(0x01FFFFFF80ULL), + }, + { + cpu_to_le64(0x0003FFFC00ULL), + cpu_to_le64(0x0003FFFFFFULL), + cpu_to_le64(0x0FFFFFFC00ULL), + }, + { + cpu_to_le64(0x000FFFF000ULL), + cpu_to_le64(0x000FFFFFFCULL), + cpu_to_le64(0x3FFFFFF000ULL), + }, + { + cpu_to_le64(0x007FFF8000ULL), + cpu_to_le64(0x007FFFFFE0ULL), + cpu_to_le64(0xFFFFFF8000ULL), + }, + { + cpu_to_le64(0x01FFFE0000ULL), + cpu_to_le64(0x01FFFFFF80ULL), + cpu_to_le64(0xFFFFFE0000ULL), + }, + { + cpu_to_le64(0x0FFFF00000ULL), + cpu_to_le64(0x0FFFFFFC00ULL), + cpu_to_le64(0x0ULL), + }, + { + cpu_to_le64(0x3FFFC00000ULL), + cpu_to_le64(0x3FFFFFF000ULL), + cpu_to_le64(0x0) + }, + { + cpu_to_le64(0xFFFE000000ULL), + cpu_to_le64(0xFFFFFF8000ULL), + cpu_to_le64(0x0) + }, + { + cpu_to_le64(0xFFF8000000ULL), + cpu_to_le64(0xFFFFFE0000ULL), + cpu_to_le64(0x0) + }, + { + cpu_to_le64(0xFFC0000000ULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x0ULL) + }, +}; + +static const __le32 iwl_bt_mprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE] = { + cpu_to_le32(0x28412201), + cpu_to_le32(0x11118451), +}; + +struct corunning_block_luts { + u8 range; + __le32 lut20[BT_COEX_CORUN_LUT_SIZE]; +}; + +/* + * Ranges for the antenna coupling calibration / co-running block LUT: + * LUT0: [ 0, 12[ + * LUT1: [12, 20[ + * LUT2: [20, 21[ + * LUT3: [21, 23[ + * LUT4: [23, 27[ + * LUT5: [27, 30[ + * LUT6: [30, 32[ + * LUT7: [32, 33[ + * LUT8: [33, - [ + */ +static const struct corunning_block_luts antenna_coupling_ranges[] = { + { + .range = 0, + .lut20 = { + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 12, + .lut20 = { + cpu_to_le32(0x00000001), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 20, + .lut20 = { + cpu_to_le32(0x00000002), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 21, + .lut20 = { + cpu_to_le32(0x00000003), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 23, + .lut20 = { + cpu_to_le32(0x00000004), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 27, + .lut20 = { + cpu_to_le32(0x00000005), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 30, + .lut20 = { + cpu_to_le32(0x00000006), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 32, + .lut20 = { + cpu_to_le32(0x00000007), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 33, + .lut20 = { + cpu_to_le32(0x00000008), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, +}; + +static enum iwl_bt_coex_lut_type +iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif) +{ + struct ieee80211_chanctx_conf *chanctx_conf; + enum iwl_bt_coex_lut_type ret; + u16 phy_ctx_id; + + /* + * Checking that we hold mvm->mutex is a good idea, but the rate + * control can't acquire the mutex since it runs in Tx path. + * So this is racy in that case, but in the worst case, the AMPDU + * size limit will be wrong for a short time which is not a big + * issue. + */ + + rcu_read_lock(); + + chanctx_conf = rcu_dereference(vif->chanctx_conf); + + if (!chanctx_conf || + chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) { + rcu_read_unlock(); + return BT_COEX_INVALID_LUT; + } + + ret = BT_COEX_TX_DIS_LUT; + + if (mvm->cfg->bt_shared_single_ant) { + rcu_read_unlock(); + return ret; + } + + phy_ctx_id = *((u16 *)chanctx_conf->drv_priv); + + if (mvm->last_bt_ci_cmd_old.primary_ch_phy_id == phy_ctx_id) + ret = le32_to_cpu(mvm->last_bt_notif_old.primary_ch_lut); + else if (mvm->last_bt_ci_cmd_old.secondary_ch_phy_id == phy_ctx_id) + ret = le32_to_cpu(mvm->last_bt_notif_old.secondary_ch_lut); + /* else - default = TX TX disallowed */ + + rcu_read_unlock(); + + return ret; +} + +int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm) +{ + struct iwl_bt_coex_cmd_old *bt_cmd; + struct iwl_host_cmd cmd = { + .id = BT_CONFIG, + .len = { sizeof(*bt_cmd), }, + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, + }; + int ret; + u32 flags; + + ret = iwl_send_bt_prio_tbl(mvm); + if (ret) + return ret; + + bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL); + if (!bt_cmd) + return -ENOMEM; + cmd.data[0] = bt_cmd; + + lockdep_assert_held(&mvm->mutex); + + if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) { + switch (mvm->bt_force_ant_mode) { + case BT_FORCE_ANT_AUTO: + flags = BT_COEX_AUTO_OLD; + break; + case BT_FORCE_ANT_BT: + flags = BT_COEX_BT_OLD; + break; + case BT_FORCE_ANT_WIFI: + flags = BT_COEX_WIFI_OLD; + break; + default: + WARN_ON(1); + flags = 0; + } + + bt_cmd->flags = cpu_to_le32(flags); + bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE); + goto send_cmd; + } + + bt_cmd->max_kill = 5; + bt_cmd->bt4_antenna_isolation_thr = BT_ANTENNA_COUPLING_THRESHOLD; + bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling; + bt_cmd->bt4_tx_tx_delta_freq_thr = 15; + bt_cmd->bt4_tx_rx_max_freq0 = 15; + bt_cmd->override_primary_lut = BT_COEX_INVALID_LUT; + bt_cmd->override_secondary_lut = BT_COEX_INVALID_LUT; + + flags = iwlwifi_mod_params.bt_coex_active ? + BT_COEX_NW_OLD : BT_COEX_DISABLE_OLD; + bt_cmd->flags = cpu_to_le32(flags); + + bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE | + BT_VALID_BT_PRIO_BOOST | + BT_VALID_MAX_KILL | + BT_VALID_3W_TMRS | + BT_VALID_KILL_ACK | + BT_VALID_KILL_CTS | + BT_VALID_REDUCED_TX_POWER | + BT_VALID_LUT | + BT_VALID_WIFI_RX_SW_PRIO_BOOST | + BT_VALID_WIFI_TX_SW_PRIO_BOOST | + BT_VALID_ANT_ISOLATION | + BT_VALID_ANT_ISOLATION_THRS | + BT_VALID_TXTX_DELTA_FREQ_THRS | + BT_VALID_TXRX_MAX_FREQ_0 | + BT_VALID_SYNC_TO_SCO); + + if (IWL_MVM_BT_COEX_SYNC2SCO) + bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO); + + if (IWL_MVM_BT_COEX_CORUNNING) { + bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_CORUN_LUT_20 | + BT_VALID_CORUN_LUT_40); + bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING); + } + + if (IWL_MVM_BT_COEX_MPLUT) { + bt_cmd->flags |= cpu_to_le32(BT_COEX_MPLUT); + bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_MULTI_PRIO_LUT); + } + + if (mvm->cfg->bt_shared_single_ant) + memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant, + sizeof(iwl_single_shared_ant)); + else + memcpy(&bt_cmd->decision_lut, iwl_combined_lookup, + sizeof(iwl_combined_lookup)); + + /* Take first Co-running block LUT to get started */ + memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[0].lut20, + sizeof(bt_cmd->bt4_corun_lut20)); + memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[0].lut20, + sizeof(bt_cmd->bt4_corun_lut40)); + + memcpy(&bt_cmd->bt_prio_boost, iwl_bt_prio_boost, + sizeof(iwl_bt_prio_boost)); + memcpy(&bt_cmd->bt4_multiprio_lut, iwl_bt_mprio_lut, + sizeof(iwl_bt_mprio_lut)); + bt_cmd->kill_ack_msk = + cpu_to_le32(iwl_bt_ack_kill_msk[BT_KILL_MSK_DEFAULT]); + bt_cmd->kill_cts_msk = + cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]); + +send_cmd: + memset(&mvm->last_bt_notif_old, 0, sizeof(mvm->last_bt_notif_old)); + memset(&mvm->last_bt_ci_cmd_old, 0, sizeof(mvm->last_bt_ci_cmd_old)); + + ret = iwl_mvm_send_cmd(mvm, &cmd); + + kfree(bt_cmd); + return ret; +} + +static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm, + bool reduced_tx_power) +{ + enum iwl_bt_kill_msk bt_kill_msk; + struct iwl_bt_coex_cmd_old *bt_cmd; + struct iwl_bt_coex_profile_notif_old *notif = &mvm->last_bt_notif_old; + struct iwl_host_cmd cmd = { + .id = BT_CONFIG, + .data[0] = &bt_cmd, + .len = { sizeof(*bt_cmd), }, + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, + }; + int ret = 0; + + lockdep_assert_held(&mvm->mutex); + + if (reduced_tx_power) { + /* Reduced Tx power has precedence on the type of the profile */ + bt_kill_msk = BT_KILL_MSK_REDUCED_TXPOW; + } else { + /* Low latency BT profile is active: give higher prio to BT */ + if (BT_MBOX_MSG(notif, 3, SCO_STATE) || + BT_MBOX_MSG(notif, 3, A2DP_STATE) || + BT_MBOX_MSG(notif, 3, SNIFF_STATE)) + bt_kill_msk = BT_KILL_MSK_SCO_HID_A2DP; + else + bt_kill_msk = BT_KILL_MSK_DEFAULT; + } + + IWL_DEBUG_COEX(mvm, + "Update kill_msk: %d - SCO %sactive A2DP %sactive SNIFF %sactive\n", + bt_kill_msk, + BT_MBOX_MSG(notif, 3, SCO_STATE) ? "" : "in", + BT_MBOX_MSG(notif, 3, A2DP_STATE) ? "" : "in", + BT_MBOX_MSG(notif, 3, SNIFF_STATE) ? "" : "in"); + + /* Don't send HCMD if there is no update */ + if (bt_kill_msk == mvm->bt_kill_msk) + return 0; + + mvm->bt_kill_msk = bt_kill_msk; + + bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL); + if (!bt_cmd) + return -ENOMEM; + cmd.data[0] = bt_cmd; + bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD); + + bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]); + bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]); + bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE | + BT_VALID_KILL_ACK | + BT_VALID_KILL_CTS); + + IWL_DEBUG_COEX(mvm, "ACK Kill msk = 0x%08x, CTS Kill msk = 0x%08x\n", + iwl_bt_ack_kill_msk[bt_kill_msk], + iwl_bt_cts_kill_msk[bt_kill_msk]); + + ret = iwl_mvm_send_cmd(mvm, &cmd); + + kfree(bt_cmd); + return ret; +} + +static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, + bool enable) +{ + struct iwl_bt_coex_cmd_old *bt_cmd; + /* Send ASYNC since this can be sent from an atomic context */ + struct iwl_host_cmd cmd = { + .id = BT_CONFIG, + .len = { sizeof(*bt_cmd), }, + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, + .flags = CMD_ASYNC, + }; + struct iwl_mvm_sta *mvmsta; + int ret; + + mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id); + if (!mvmsta) + return 0; + + /* nothing to do */ + if (mvmsta->bt_reduced_txpower == enable) + return 0; + + bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_ATOMIC); + if (!bt_cmd) + return -ENOMEM; + cmd.data[0] = bt_cmd; + bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD); + + bt_cmd->valid_bit_msk = + cpu_to_le32(BT_VALID_ENABLE | BT_VALID_REDUCED_TX_POWER); + bt_cmd->bt_reduced_tx_power = sta_id; + + if (enable) + bt_cmd->bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT; + + IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n", + enable ? "en" : "dis", sta_id); + + mvmsta->bt_reduced_txpower = enable; + + ret = iwl_mvm_send_cmd(mvm, &cmd); + + kfree(bt_cmd); + return ret; +} + +struct iwl_bt_iterator_data { + struct iwl_bt_coex_profile_notif_old *notif; + struct iwl_mvm *mvm; + u32 num_bss_ifaces; + bool reduced_tx_power; + struct ieee80211_chanctx_conf *primary; + struct ieee80211_chanctx_conf *secondary; + bool primary_ll; +}; + +static inline +void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool enable, int rssi) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + mvmvif->bf_data.last_bt_coex_event = rssi; + mvmvif->bf_data.bt_coex_max_thold = + enable ? -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH : 0; + mvmvif->bf_data.bt_coex_min_thold = + enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0; +} + +/* must be called under rcu_read_lock */ +static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_bt_iterator_data *data = _data; + struct iwl_mvm *mvm = data->mvm; + struct ieee80211_chanctx_conf *chanctx_conf; + enum ieee80211_smps_mode smps_mode; + u32 bt_activity_grading; + int ave_rssi; + + lockdep_assert_held(&mvm->mutex); + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + /* Count BSSes vifs */ + data->num_bss_ifaces++; + /* default smps_mode for BSS / P2P client is AUTOMATIC */ + smps_mode = IEEE80211_SMPS_AUTOMATIC; + break; + case NL80211_IFTYPE_AP: + /* default smps_mode for AP / GO is OFF */ + smps_mode = IEEE80211_SMPS_OFF; + if (!mvmvif->ap_ibss_active) { + iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, + smps_mode); + return; + } + + /* the Ack / Cts kill mask must be default if AP / GO */ + data->reduced_tx_power = false; + break; + default: + return; + } + + chanctx_conf = rcu_dereference(vif->chanctx_conf); + + /* If channel context is invalid or not on 2.4GHz .. */ + if ((!chanctx_conf || + chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ)) { + /* ... relax constraints and disable rssi events */ + iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, + smps_mode); + data->reduced_tx_power = false; + if (vif->type == NL80211_IFTYPE_STATION) { + iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, + false); + iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); + } + return; + } + + bt_activity_grading = le32_to_cpu(data->notif->bt_activity_grading); + if (bt_activity_grading >= BT_HIGH_TRAFFIC) + smps_mode = IEEE80211_SMPS_STATIC; + else if (bt_activity_grading >= BT_LOW_TRAFFIC) + smps_mode = vif->type == NL80211_IFTYPE_AP ? + IEEE80211_SMPS_OFF : + IEEE80211_SMPS_DYNAMIC; + + /* relax SMPS contraints for next association */ + if (!vif->bss_conf.assoc) + smps_mode = IEEE80211_SMPS_AUTOMATIC; + + IWL_DEBUG_COEX(data->mvm, + "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n", + mvmvif->id, data->notif->bt_status, bt_activity_grading, + smps_mode); + + iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode); + + /* low latency is always primary */ + if (iwl_mvm_vif_low_latency(mvmvif)) { + data->primary_ll = true; + + data->secondary = data->primary; + data->primary = chanctx_conf; + } + + if (vif->type == NL80211_IFTYPE_AP) { + if (!mvmvif->ap_ibss_active) + return; + + if (chanctx_conf == data->primary) + return; + + if (!data->primary_ll) { + /* + * downgrade the current primary no matter what its + * type is. + */ + data->secondary = data->primary; + data->primary = chanctx_conf; + } else { + /* there is low latency vif - we will be secondary */ + data->secondary = chanctx_conf; + } + return; + } + + /* + * STA / P2P Client, try to be primary if first vif. If we are in low + * latency mode, we are already in primary and just don't do much + */ + if (!data->primary || data->primary == chanctx_conf) + data->primary = chanctx_conf; + else if (!data->secondary) + /* if secondary is not NULL, it might be a GO */ + data->secondary = chanctx_conf; + + /* + * don't reduce the Tx power if one of these is true: + * we are in LOOSE + * single share antenna product + * BT is active + * we are associated + */ + if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT || + mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc || + !data->notif->bt_status) { + data->reduced_tx_power = false; + iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false); + iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); + return; + } + + /* try to get the avg rssi from fw */ + ave_rssi = mvmvif->bf_data.ave_beacon_signal; + + /* if the RSSI isn't valid, fake it is very low */ + if (!ave_rssi) + ave_rssi = -100; + if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) { + if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true)) + IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); + + /* + * bt_kill_msk can be BT_KILL_MSK_REDUCED_TXPOW only if all the + * BSS / P2P clients have rssi above threshold. + * We set the bt_kill_msk to BT_KILL_MSK_REDUCED_TXPOW before + * the iteration, if one interface's rssi isn't good enough, + * bt_kill_msk will be set to default values. + */ + } else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) { + if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false)) + IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); + + /* + * One interface hasn't rssi above threshold, bt_kill_msk must + * be set to default values. + */ + data->reduced_tx_power = false; + } + + /* Begin to monitor the RSSI: it may influence the reduced Tx power */ + iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, true, ave_rssi); +} + +static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) +{ + struct iwl_bt_iterator_data data = { + .mvm = mvm, + .notif = &mvm->last_bt_notif_old, + .reduced_tx_power = true, + }; + struct iwl_bt_coex_ci_cmd_old cmd = {}; + u8 ci_bw_idx; + + /* Ignore updates if we are in force mode */ + if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) + return; + + rcu_read_lock(); + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_bt_notif_iterator, &data); + + if (data.primary) { + struct ieee80211_chanctx_conf *chan = data.primary; + + if (WARN_ON(!chan->def.chan)) { + rcu_read_unlock(); + return; + } + + if (chan->def.width < NL80211_CHAN_WIDTH_40) { + ci_bw_idx = 0; + cmd.co_run_bw_primary = 0; + } else { + cmd.co_run_bw_primary = 1; + if (chan->def.center_freq1 > + chan->def.chan->center_freq) + ci_bw_idx = 2; + else + ci_bw_idx = 1; + } + + cmd.bt_primary_ci = + iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; + cmd.primary_ch_phy_id = *((u16 *)data.primary->drv_priv); + } + + if (data.secondary) { + struct ieee80211_chanctx_conf *chan = data.secondary; + + if (WARN_ON(!data.secondary->def.chan)) { + rcu_read_unlock(); + return; + } + + if (chan->def.width < NL80211_CHAN_WIDTH_40) { + ci_bw_idx = 0; + cmd.co_run_bw_secondary = 0; + } else { + cmd.co_run_bw_secondary = 1; + if (chan->def.center_freq1 > + chan->def.chan->center_freq) + ci_bw_idx = 2; + else + ci_bw_idx = 1; + } + + cmd.bt_secondary_ci = + iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; + cmd.secondary_ch_phy_id = *((u16 *)data.secondary->drv_priv); + } + + rcu_read_unlock(); + + /* Don't spam the fw with the same command over and over */ + if (memcmp(&cmd, &mvm->last_bt_ci_cmd_old, sizeof(cmd))) { + if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, 0, + sizeof(cmd), &cmd)) + IWL_ERR(mvm, "Failed to send BT_CI cmd\n"); + memcpy(&mvm->last_bt_ci_cmd_old, &cmd, sizeof(cmd)); + } + + /* + * If there are no BSS / P2P client interfaces, reduced Tx Power is + * irrelevant since it is based on the RSSI coming from the beacon. + * Use BT_KILL_MSK_DEFAULT in that case. + */ + data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces; + + if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power)) + IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n"); +} + +int iwl_mvm_rx_bt_coex_notif_old(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *dev_cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_bt_coex_profile_notif_old *notif = (void *)pkt->data; + + IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n"); + IWL_DEBUG_COEX(mvm, "\tBT status: %s\n", + notif->bt_status ? "ON" : "OFF"); + IWL_DEBUG_COEX(mvm, "\tBT open conn %d\n", notif->bt_open_conn); + IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance); + IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n", + le32_to_cpu(notif->primary_ch_lut)); + IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n", + le32_to_cpu(notif->secondary_ch_lut)); + IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n", + le32_to_cpu(notif->bt_activity_grading)); + IWL_DEBUG_COEX(mvm, "\tBT agg traffic load %d\n", + notif->bt_agg_traffic_load); + + /* remember this notification for future use: rssi fluctuations */ + memcpy(&mvm->last_bt_notif_old, notif, sizeof(mvm->last_bt_notif_old)); + + iwl_mvm_bt_coex_notif_handle(mvm); + + /* + * This is an async handler for a notification, returning anything other + * than 0 doesn't make sense even if HCMD failed. + */ + return 0; +} + +static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv; + struct iwl_bt_iterator_data *data = _data; + struct iwl_mvm *mvm = data->mvm; + + struct ieee80211_sta *sta; + struct iwl_mvm_sta *mvmsta; + + struct ieee80211_chanctx_conf *chanctx_conf; + + rcu_read_lock(); + chanctx_conf = rcu_dereference(vif->chanctx_conf); + /* If channel context is invalid or not on 2.4GHz - don't count it */ + if (!chanctx_conf || + chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) { + rcu_read_unlock(); + return; + } + rcu_read_unlock(); + + if (vif->type != NL80211_IFTYPE_STATION || + mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) + return; + + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id], + lockdep_is_held(&mvm->mutex)); + + /* This can happen if the station has been removed right now */ + if (IS_ERR_OR_NULL(sta)) + return; + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + + data->num_bss_ifaces++; + + /* + * This interface doesn't support reduced Tx power (because of low + * RSSI probably), then set bt_kill_msk to default values. + */ + if (!mvmsta->bt_reduced_txpower) + data->reduced_tx_power = false; + /* else - possibly leave it to BT_KILL_MSK_REDUCED_TXPOW */ +} + +void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + enum ieee80211_rssi_event rssi_event) +{ + struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv; + struct iwl_bt_iterator_data data = { + .mvm = mvm, + .reduced_tx_power = true, + }; + int ret; + + lockdep_assert_held(&mvm->mutex); + + /* Ignore updates if we are in force mode */ + if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) + return; + + /* + * Rssi update while not associated - can happen since the statistics + * are handled asynchronously + */ + if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) + return; + + /* No BT - reports should be disabled */ + if (!mvm->last_bt_notif_old.bt_status) + return; + + IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid, + rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW"); + + /* + * Check if rssi is good enough for reduced Tx power, but not in loose + * scheme. + */ + if (rssi_event == RSSI_EVENT_LOW || mvm->cfg->bt_shared_single_ant || + iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT) + ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, + false); + else + ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true); + + if (ret) + IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n"); + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_bt_rssi_iterator, &data); + + /* + * If there are no BSS / P2P client interfaces, reduced Tx Power is + * irrelevant since it is based on the RSSI coming from the beacon. + * Use BT_KILL_MSK_DEFAULT in that case. + */ + data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces; + + if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power)) + IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n"); +} + +#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) +#define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT (1200) + +u16 iwl_mvm_coex_agg_time_limit_old(struct iwl_mvm *mvm, + struct ieee80211_sta *sta) +{ + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + enum iwl_bt_coex_lut_type lut_type; + + if (le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading) < + BT_HIGH_TRAFFIC) + return LINK_QUAL_AGG_TIME_LIMIT_DEF; + + if (mvm->last_bt_notif_old.ttc_enabled) + return LINK_QUAL_AGG_TIME_LIMIT_DEF; + + lut_type = iwl_get_coex_type(mvm, mvmsta->vif); + + if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT) + return LINK_QUAL_AGG_TIME_LIMIT_DEF; + + /* tight coex, high bt traffic, reduce AGG time limit */ + return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT; +} + +bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm, + struct ieee80211_sta *sta) +{ + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + enum iwl_bt_coex_lut_type lut_type; + + if (mvm->last_bt_notif_old.ttc_enabled) + return true; + + if (le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading) < + BT_HIGH_TRAFFIC) + return true; + + /* + * In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas + * since BT is already killed. + * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while + * we Tx. + * When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO. + */ + lut_type = iwl_get_coex_type(mvm, mvmsta->vif); + return lut_type != BT_COEX_LOOSE_LUT; +} + +bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm) +{ + u32 ag = le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading); + return ag == BT_OFF; +} + +bool iwl_mvm_bt_coex_is_tpc_allowed_old(struct iwl_mvm *mvm, + enum ieee80211_band band) +{ + u32 bt_activity = + le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading); + + if (band != IEEE80211_BAND_2GHZ) + return false; + + return bt_activity >= BT_LOW_TRAFFIC; +} + +void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm) +{ + iwl_mvm_bt_coex_notif_handle(mvm); +} + +int iwl_mvm_rx_ant_coupling_notif_old(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *dev_cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + u32 ant_isolation = le32_to_cpup((void *)pkt->data); + u8 __maybe_unused lower_bound, upper_bound; + int ret; + u8 lut; + + struct iwl_bt_coex_cmd_old *bt_cmd; + struct iwl_host_cmd cmd = { + .id = BT_CONFIG, + .len = { sizeof(*bt_cmd), }, + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, + }; + + if (!IWL_MVM_BT_COEX_CORUNNING) + return 0; + + lockdep_assert_held(&mvm->mutex); + + /* Ignore updates if we are in force mode */ + if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) + return 0; + + if (ant_isolation == mvm->last_ant_isol) + return 0; + + for (lut = 0; lut < ARRAY_SIZE(antenna_coupling_ranges) - 1; lut++) + if (ant_isolation < antenna_coupling_ranges[lut + 1].range) + break; + + lower_bound = antenna_coupling_ranges[lut].range; + + if (lut < ARRAY_SIZE(antenna_coupling_ranges) - 1) + upper_bound = antenna_coupling_ranges[lut + 1].range; + else + upper_bound = antenna_coupling_ranges[lut].range; + + IWL_DEBUG_COEX(mvm, "Antenna isolation=%d in range [%d,%d[, lut=%d\n", + ant_isolation, lower_bound, upper_bound, lut); + + mvm->last_ant_isol = ant_isolation; + + if (mvm->last_corun_lut == lut) + return 0; + + mvm->last_corun_lut = lut; + + bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL); + if (!bt_cmd) + return 0; + cmd.data[0] = bt_cmd; + + bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD); + bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE | + BT_VALID_CORUN_LUT_20 | + BT_VALID_CORUN_LUT_40); + + /* For the moment, use the same LUT for 20GHz and 40GHz */ + memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[lut].lut20, + sizeof(bt_cmd->bt4_corun_lut20)); + + memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[lut].lut20, + sizeof(bt_cmd->bt4_corun_lut40)); + + ret = iwl_mvm_send_cmd(mvm, &cmd); + + kfree(bt_cmd); + return ret; +} diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index 602bbd29ec5a..b2c751e71581 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -316,7 +316,7 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_mvm *mvm = file->private_data; - struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif; + struct iwl_bt_coex_profile_notif_old *notif = &mvm->last_bt_notif; char *buf; int ret, pos = 0, bufsz = sizeof(char) * 1024; @@ -411,7 +411,7 @@ static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_mvm *mvm = file->private_data; - struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd; + struct iwl_bt_coex_ci_cmd_old *cmd = &mvm->last_bt_ci_cmd; char buf[256]; int bufsz = sizeof(buf); int pos = 0; diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h index 39cb33a19862..b3626cc69052 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h @@ -72,13 +72,13 @@ * enum iwl_bt_coex_flags - flags for BT_COEX command * @BT_COEX_MODE_POS: * @BT_COEX_MODE_MSK: - * @BT_COEX_DISABLE: - * @BT_COEX_2W: - * @BT_COEX_3W: - * @BT_COEX_NW: - * @BT_COEX_AUTO: - * @BT_COEX_BT: Antenna is for BT (manufacuring tests) - * @BT_COEX_WIFI: Antenna is for BT (manufacuring tests) + * @BT_COEX_DISABLE_OLD: + * @BT_COEX_2W_OLD: + * @BT_COEX_3W_OLD: + * @BT_COEX_NW_OLD: + * @BT_COEX_AUTO_OLD: + * @BT_COEX_BT_OLD: Antenna is for BT (manufacuring tests) + * @BT_COEX_WIFI_OLD: Antenna is for BT (manufacuring tests) * @BT_COEX_SYNC2SCO: * @BT_COEX_CORUNNING: * @BT_COEX_MPLUT: @@ -88,13 +88,13 @@ enum iwl_bt_coex_flags { BT_COEX_MODE_POS = 3, BT_COEX_MODE_MSK = BITS(3) << BT_COEX_MODE_POS, - BT_COEX_DISABLE = 0x0 << BT_COEX_MODE_POS, - BT_COEX_2W = 0x1 << BT_COEX_MODE_POS, - BT_COEX_3W = 0x2 << BT_COEX_MODE_POS, - BT_COEX_NW = 0x3 << BT_COEX_MODE_POS, - BT_COEX_AUTO = 0x5 << BT_COEX_MODE_POS, - BT_COEX_BT = 0x6 << BT_COEX_MODE_POS, - BT_COEX_WIFI = 0x7 << BT_COEX_MODE_POS, + BT_COEX_DISABLE_OLD = 0x0 << BT_COEX_MODE_POS, + BT_COEX_2W_OLD = 0x1 << BT_COEX_MODE_POS, + BT_COEX_3W_OLD = 0x2 << BT_COEX_MODE_POS, + BT_COEX_NW_OLD = 0x3 << BT_COEX_MODE_POS, + BT_COEX_AUTO_OLD = 0x5 << BT_COEX_MODE_POS, + BT_COEX_BT_OLD = 0x6 << BT_COEX_MODE_POS, + BT_COEX_WIFI_OLD = 0x7 << BT_COEX_MODE_POS, BT_COEX_SYNC2SCO = BIT(7), BT_COEX_CORUNNING = BIT(8), BT_COEX_MPLUT = BIT(9), @@ -157,7 +157,7 @@ enum iwl_bt_coex_lut_type { #define BT_REDUCED_TX_POWER_BIT BIT(7) /** - * struct iwl_bt_coex_cmd - bt coex configuration command + * struct iwl_bt_coex_cmd_old - bt coex configuration command * @flags:&enum iwl_bt_coex_flags * @max_kill: * @bt_reduced_tx_power: enum %iwl_bt_reduced_tx_power @@ -182,7 +182,7 @@ enum iwl_bt_coex_lut_type { * * The structure is used for the BT_COEX command. */ -struct iwl_bt_coex_cmd { +struct iwl_bt_coex_cmd_old { __le32 flags; u8 max_kill; u8 bt_reduced_tx_power; @@ -219,7 +219,7 @@ struct iwl_bt_coex_cmd { * * Used for BT_COEX_CI command */ -struct iwl_bt_coex_ci_cmd { +struct iwl_bt_coex_ci_cmd_old { __le64 bt_primary_ci; __le64 bt_secondary_ci; @@ -310,7 +310,7 @@ enum iwl_bt_activity_grading { * @secondary_ch_lut: LUT used for secondary channel * @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading */ -struct iwl_bt_coex_profile_notif { +struct iwl_bt_coex_profile_notif_old { __le32 mbox_msg[4]; __le32 msg_idx; u8 bt_status; diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 6fe93a7335c1..0b52d0ae4a0d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -630,8 +630,12 @@ struct iwl_mvm { /* BT-Coex */ u8 bt_kill_msk; - struct iwl_bt_coex_profile_notif last_bt_notif; - struct iwl_bt_coex_ci_cmd last_bt_ci_cmd; + + struct iwl_bt_coex_profile_notif_old last_bt_notif_old; + struct iwl_bt_coex_ci_cmd_old last_bt_ci_cmd_old; + struct iwl_bt_coex_profile_notif_old last_bt_notif; + struct iwl_bt_coex_ci_cmd_old last_bt_ci_cmd; + u32 last_ant_isol; u8 last_corun_lut; u8 bt_tx_prio; @@ -974,6 +978,24 @@ bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, struct ieee80211_tx_info *info, u8 ac); +bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm); +void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm); +int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm); +int iwl_mvm_rx_bt_coex_notif_old(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); +void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + enum ieee80211_rssi_event rssi_event); +u16 iwl_mvm_coex_agg_time_limit_old(struct iwl_mvm *mvm, + struct ieee80211_sta *sta); +bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm, + struct ieee80211_sta *sta); +bool iwl_mvm_bt_coex_is_tpc_allowed_old(struct iwl_mvm *mvm, + enum ieee80211_band band); +int iwl_mvm_rx_ant_coupling_notif_old(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); + enum iwl_bt_kill_msk { BT_KILL_MSK_DEFAULT, BT_KILL_MSK_SCO_HID_A2DP, From 430a3bbafdc78e30307e6eacb90980f29719d91c Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 2 Apr 2014 09:55:16 +0300 Subject: [PATCH 239/274] iwlwifi: mvm: BT Coex - new API Start the new BT Coex implementation. Don't react to notifications for now - only the initial configuration is implemented. The rest will happen in next patches. Since coex.c now uses the new the new structures in all functions, we need to adapt the code to compile, even if it doesn't run yet. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/coex.c | 179 +++++----------- drivers/net/wireless/iwlwifi/mvm/debugfs.c | 22 +- .../net/wireless/iwlwifi/mvm/fw-api-coex.h | 193 +++++++++++++++--- drivers/net/wireless/iwlwifi/mvm/fw-api.h | 5 +- drivers/net/wireless/iwlwifi/mvm/mvm.h | 4 +- drivers/net/wireless/iwlwifi/mvm/ops.c | 3 + 6 files changed, 232 insertions(+), 174 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c index f471de3373c9..f8c293e54b5e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex.c @@ -70,48 +70,8 @@ #include "mvm.h" #include "iwl-debug.h" -#define EVENT_PRIO_ANT(_evt, _prio, _shrd_ant) \ - [(_evt)] = (((_prio) << BT_COEX_PRIO_TBL_PRIO_POS) | \ - ((_shrd_ant) << BT_COEX_PRIO_TBL_SHRD_ANT_POS)) - -static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = { - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB1, - BT_COEX_PRIO_TBL_PRIO_BYPASS, 0), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB2, - BT_COEX_PRIO_TBL_PRIO_BYPASS, 1), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1, - BT_COEX_PRIO_TBL_PRIO_LOW, 0), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2, - BT_COEX_PRIO_TBL_PRIO_LOW, 1), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1, - BT_COEX_PRIO_TBL_PRIO_HIGH, 0), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2, - BT_COEX_PRIO_TBL_PRIO_HIGH, 1), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_DTIM, - BT_COEX_PRIO_TBL_DISABLED, 0), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN52, - BT_COEX_PRIO_TBL_PRIO_COEX_OFF, 0), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN24, - BT_COEX_PRIO_TBL_PRIO_COEX_ON, 0), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_IDLE, - BT_COEX_PRIO_TBL_PRIO_COEX_IDLE, 0), - 0, 0, 0, 0, 0, 0, -}; - -#undef EVENT_PRIO_ANT - #define BT_ANTENNA_COUPLING_THRESHOLD (30) -static int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm) -{ - if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) - return 0; - - return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, 0, - sizeof(struct iwl_bt_coex_prio_tbl_cmd), - &iwl_bt_prio_tbl); -} - const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX] = { [BT_KILL_MSK_DEFAULT] = 0xffff0000, [BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff, @@ -520,6 +480,7 @@ iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif) struct ieee80211_chanctx_conf *chanctx_conf; enum iwl_bt_coex_lut_type ret; u16 phy_ctx_id; + u32 primary_ch_phy_id, secondary_ch_phy_id; /* * Checking that we hold mvm->mutex is a good idea, but the rate @@ -547,10 +508,13 @@ iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif) } phy_ctx_id = *((u16 *)chanctx_conf->drv_priv); + primary_ch_phy_id = le32_to_cpu(mvm->last_bt_ci_cmd.primary_ch_phy_id); + secondary_ch_phy_id = + le32_to_cpu(mvm->last_bt_ci_cmd.secondary_ch_phy_id); - if (mvm->last_bt_ci_cmd.primary_ch_phy_id == phy_ctx_id) + if (primary_ch_phy_id == phy_ctx_id) ret = le32_to_cpu(mvm->last_bt_notif.primary_ch_lut); - else if (mvm->last_bt_ci_cmd.secondary_ch_phy_id == phy_ctx_id) + else if (secondary_ch_phy_id == phy_ctx_id) ret = le32_to_cpu(mvm->last_bt_notif.secondary_ch_lut); /* else - default = TX TX disallowed */ @@ -561,25 +525,18 @@ iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif) int iwl_send_bt_init_conf(struct iwl_mvm *mvm) { - struct iwl_bt_coex_cmd_old *bt_cmd; + struct iwl_bt_coex_cmd *bt_cmd; struct iwl_host_cmd cmd = { .id = BT_CONFIG, .len = { sizeof(*bt_cmd), }, .dataflags = { IWL_HCMD_DFL_NOCOPY, }, }; int ret; - u32 flags; + u32 mode; if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) return iwl_send_bt_init_conf_old(mvm); - /* TODO */ - return 0; - - ret = iwl_send_bt_prio_tbl(mvm); - if (ret) - return ret; - bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL); if (!bt_cmd) return -ENOMEM; @@ -588,66 +545,46 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm) lockdep_assert_held(&mvm->mutex); if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) { + u32 mode; + switch (mvm->bt_force_ant_mode) { - case BT_FORCE_ANT_AUTO: - flags = BT_COEX_AUTO_OLD; - break; case BT_FORCE_ANT_BT: - flags = BT_COEX_BT_OLD; + mode = BT_COEX_BT; break; case BT_FORCE_ANT_WIFI: - flags = BT_COEX_WIFI_OLD; + mode = BT_COEX_WIFI; break; default: WARN_ON(1); - flags = 0; + mode = 0; } - bt_cmd->flags = cpu_to_le32(flags); - bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE); + bt_cmd->mode = cpu_to_le32(mode); goto send_cmd; } - bt_cmd->max_kill = 5; - bt_cmd->bt4_antenna_isolation_thr = BT_ANTENNA_COUPLING_THRESHOLD; - bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling; - bt_cmd->bt4_tx_tx_delta_freq_thr = 15; - bt_cmd->bt4_tx_rx_max_freq0 = 15; - bt_cmd->override_primary_lut = BT_COEX_INVALID_LUT; - bt_cmd->override_secondary_lut = BT_COEX_INVALID_LUT; + bt_cmd->max_kill = cpu_to_le32(5); + bt_cmd->bt4_antenna_isolation_thr = + cpu_to_le32(BT_ANTENNA_COUPLING_THRESHOLD); + bt_cmd->bt4_tx_tx_delta_freq_thr = cpu_to_le32(15); + bt_cmd->bt4_tx_rx_max_freq0 = cpu_to_le32(15); + bt_cmd->override_primary_lut = cpu_to_le32(BT_COEX_INVALID_LUT); + bt_cmd->override_secondary_lut = cpu_to_le32(BT_COEX_INVALID_LUT); - flags = iwlwifi_mod_params.bt_coex_active ? - BT_COEX_NW_OLD : BT_COEX_DISABLE_OLD; - bt_cmd->flags = cpu_to_le32(flags); - - bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE | - BT_VALID_BT_PRIO_BOOST | - BT_VALID_MAX_KILL | - BT_VALID_3W_TMRS | - BT_VALID_KILL_ACK | - BT_VALID_KILL_CTS | - BT_VALID_REDUCED_TX_POWER | - BT_VALID_LUT | - BT_VALID_WIFI_RX_SW_PRIO_BOOST | - BT_VALID_WIFI_TX_SW_PRIO_BOOST | - BT_VALID_ANT_ISOLATION | - BT_VALID_ANT_ISOLATION_THRS | - BT_VALID_TXTX_DELTA_FREQ_THRS | - BT_VALID_TXRX_MAX_FREQ_0 | - BT_VALID_SYNC_TO_SCO); + mode = iwlwifi_mod_params.bt_coex_active ? BT_COEX_NW : BT_COEX_DISABLE; + bt_cmd->mode = cpu_to_le32(mode); if (IWL_MVM_BT_COEX_SYNC2SCO) - bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO); + bt_cmd->enabled_modules |= + cpu_to_le32(BT_COEX_SYNC2SCO_ENABLED); - if (IWL_MVM_BT_COEX_CORUNNING) { - bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_CORUN_LUT_20 | - BT_VALID_CORUN_LUT_40); - bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING); - } + if (IWL_MVM_BT_COEX_CORUNNING) + bt_cmd->enabled_modules |= cpu_to_le32(BT_COEX_CORUN_ENABLED); if (IWL_MVM_BT_COEX_MPLUT) { - bt_cmd->flags |= cpu_to_le32(BT_COEX_MPLUT); - bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_MULTI_PRIO_LUT); + bt_cmd->enabled_modules |= cpu_to_le32(BT_COEX_MPLUT_ENABLED); + bt_cmd->enabled_modules |= + cpu_to_le32(BT_COEX_MPLUT_BOOST_ENABLED); } if (mvm->cfg->bt_shared_single_ant) @@ -657,20 +594,10 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm) memcpy(&bt_cmd->decision_lut, iwl_combined_lookup, sizeof(iwl_combined_lookup)); - /* Take first Co-running block LUT to get started */ - memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[0].lut20, - sizeof(bt_cmd->bt4_corun_lut20)); - memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[0].lut20, - sizeof(bt_cmd->bt4_corun_lut40)); - - memcpy(&bt_cmd->bt_prio_boost, iwl_bt_prio_boost, + memcpy(&bt_cmd->mplut_prio_boost, iwl_bt_prio_boost, sizeof(iwl_bt_prio_boost)); - memcpy(&bt_cmd->bt4_multiprio_lut, iwl_bt_mprio_lut, + memcpy(&bt_cmd->multiprio_lut, iwl_bt_mprio_lut, sizeof(iwl_bt_mprio_lut)); - bt_cmd->kill_ack_msk = - cpu_to_le32(iwl_bt_ack_kill_msk[BT_KILL_MSK_DEFAULT]); - bt_cmd->kill_cts_msk = - cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]); send_cmd: memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif)); @@ -687,7 +614,7 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm, { enum iwl_bt_kill_msk bt_kill_msk; struct iwl_bt_coex_cmd_old *bt_cmd; - struct iwl_bt_coex_profile_notif_old *notif = &mvm->last_bt_notif; + struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif; struct iwl_host_cmd cmd = { .id = BT_CONFIG, .data[0] = &bt_cmd, @@ -760,6 +687,8 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, struct iwl_mvm_sta *mvmsta; int ret; + return 0; + mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id); if (!mvmsta) return 0; @@ -793,7 +722,7 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, } struct iwl_bt_iterator_data { - struct iwl_bt_coex_profile_notif_old *notif; + struct iwl_bt_coex_profile_notif *notif; struct iwl_mvm *mvm; u32 num_bss_ifaces; bool reduced_tx_power; @@ -883,9 +812,8 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, smps_mode = IEEE80211_SMPS_AUTOMATIC; IWL_DEBUG_COEX(data->mvm, - "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n", - mvmvif->id, data->notif->bt_status, bt_activity_grading, - smps_mode); + "mac %d: bt_activity_grading %d smps_req %d\n", + mvmvif->id, bt_activity_grading, smps_mode); iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode); @@ -937,7 +865,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, */ if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT || mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc || - !data->notif->bt_status) { + le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF) { data->reduced_tx_power = false; iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false); iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); @@ -983,7 +911,7 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) .notif = &mvm->last_bt_notif, .reduced_tx_power = true, }; - struct iwl_bt_coex_ci_cmd_old cmd = {}; + struct iwl_bt_coex_ci_cmd cmd = {}; u8 ci_bw_idx; /* Ignore updates if we are in force mode */ @@ -1004,9 +932,7 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) if (chan->def.width < NL80211_CHAN_WIDTH_40) { ci_bw_idx = 0; - cmd.co_run_bw_primary = 0; } else { - cmd.co_run_bw_primary = 1; if (chan->def.center_freq1 > chan->def.chan->center_freq) ci_bw_idx = 2; @@ -1016,7 +942,8 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) cmd.bt_primary_ci = iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; - cmd.primary_ch_phy_id = *((u16 *)data.primary->drv_priv); + cmd.primary_ch_phy_id = + cpu_to_le32(*((u16 *)data.primary->drv_priv)); } if (data.secondary) { @@ -1028,9 +955,7 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) if (chan->def.width < NL80211_CHAN_WIDTH_40) { ci_bw_idx = 0; - cmd.co_run_bw_secondary = 0; } else { - cmd.co_run_bw_secondary = 1; if (chan->def.center_freq1 > chan->def.chan->center_freq) ci_bw_idx = 2; @@ -1040,7 +965,8 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) cmd.bt_secondary_ci = iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; - cmd.secondary_ch_phy_id = *((u16 *)data.secondary->drv_priv); + cmd.secondary_ch_phy_id = + cpu_to_le32(*((u16 *)data.secondary->drv_priv)); } rcu_read_unlock(); @@ -1078,9 +1004,6 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, return 0; IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n"); - IWL_DEBUG_COEX(mvm, "\tBT status: %s\n", - notif->bt_status ? "ON" : "OFF"); - IWL_DEBUG_COEX(mvm, "\tBT open conn %d\n", notif->bt_open_conn); IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance); IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n", le32_to_cpu(notif->primary_ch_lut)); @@ -1088,8 +1011,6 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, le32_to_cpu(notif->secondary_ch_lut)); IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n", le32_to_cpu(notif->bt_activity_grading)); - IWL_DEBUG_COEX(mvm, "\tBT agg traffic load %d\n", - notif->bt_agg_traffic_load); /* remember this notification for future use: rssi fluctuations */ memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif)); @@ -1181,7 +1102,7 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, return; /* No BT - reports should be disabled */ - if (!mvm->last_bt_notif.bt_status) + if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF) return; IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid, @@ -1234,9 +1155,11 @@ u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC) return LINK_QUAL_AGG_TIME_LIMIT_DEF; - +/* + TODO if (mvm->last_bt_notif.ttc_enabled) return LINK_QUAL_AGG_TIME_LIMIT_DEF; +*/ lut_type = iwl_get_coex_type(mvm, mvmsta->vif); @@ -1256,11 +1179,11 @@ bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) return iwl_mvm_coex_agg_time_limit_old(mvm, sta); - return true; - - /* TODO */ +/* + TODO if (mvm->last_bt_notif.ttc_enabled) return true; +*/ if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC) diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index b2c751e71581..e66c659b264a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -316,7 +316,7 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_mvm *mvm = file->private_data; - struct iwl_bt_coex_profile_notif_old *notif = &mvm->last_bt_notif; + struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif; char *buf; int ret, pos = 0, bufsz = sizeof(char) * 1024; @@ -378,14 +378,6 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf, BT_MBOX_PRINT(3, SSN_2, false); BT_MBOX_PRINT(3, UPDATE_REQUEST, true); - pos += scnprintf(buf+pos, bufsz-pos, "bt_status = %d\n", - notif->bt_status); - pos += scnprintf(buf+pos, bufsz-pos, "bt_open_conn = %d\n", - notif->bt_open_conn); - pos += scnprintf(buf+pos, bufsz-pos, "bt_traffic_load = %d\n", - notif->bt_traffic_load); - pos += scnprintf(buf+pos, bufsz-pos, "bt_agg_traffic_load = %d\n", - notif->bt_agg_traffic_load); pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n", notif->bt_ci_compliance); pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n", @@ -411,7 +403,7 @@ static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_mvm *mvm = file->private_data; - struct iwl_bt_coex_ci_cmd_old *cmd = &mvm->last_bt_ci_cmd; + struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd; char buf[256]; int bufsz = sizeof(buf); int pos = 0; @@ -420,13 +412,11 @@ static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf, pos += scnprintf(buf+pos, bufsz-pos, "Channel inhibition CMD\n"); pos += scnprintf(buf+pos, bufsz-pos, - "\tPrimary Channel Bitmap 0x%016llx Fat: %d\n", - le64_to_cpu(cmd->bt_primary_ci), - !!cmd->co_run_bw_primary); + "\tPrimary Channel Bitmap 0x%016llx\n", + le64_to_cpu(cmd->bt_primary_ci)); pos += scnprintf(buf+pos, bufsz-pos, - "\tSecondary Channel Bitmap 0x%016llx Fat: %d\n", - le64_to_cpu(cmd->bt_secondary_ci), - !!cmd->co_run_bw_secondary); + "\tSecondary Channel Bitmap 0x%016llx\n", + le64_to_cpu(cmd->bt_secondary_ci)); pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n"); pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill Mask 0x%08x\n", diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h index b3626cc69052..98becb9c90fa 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h @@ -208,26 +208,116 @@ struct iwl_bt_coex_cmd_old { __le32 valid_bit_msk; } __packed; /* BT_COEX_CMD_API_S_VER_5 */ +enum iwl_bt_coex_mode { + BT_COEX_DISABLE = 0x0, + BT_COEX_NW = 0x1, + BT_COEX_BT = 0x2, + BT_COEX_WIFI = 0x3, +}; /* BT_COEX_MODES_E */ + +enum iwl_bt_coex_enabled_modules { + BT_COEX_MPLUT_ENABLED = BIT(0), + BT_COEX_MPLUT_BOOST_ENABLED = BIT(1), + BT_COEX_SYNC2SCO_ENABLED = BIT(2), + BT_COEX_CORUN_ENABLED = BIT(3), +}; /* BT_COEX_MODULES_ENABLE_E_VER_1 */ + +/** + * struct iwl_bt_coex_cmd - bt coex configuration command + * @mode: enum %iwl_bt_coex_mode + * @enabled_modules: enum %iwl_bt_coex_enabled_modules + * @max_kill: max count of Tx retries due to kill from PTA + * @override_primary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT + * should be set by default + * @override_secondary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT + * should be set by default + * @bt4_antenna_isolation_thr: antenna threshold value + * @bt4_tx_tx_delta_freq_thr: TxTx delta frequency + * @bt4_tx_rx_max_freq0: TxRx max frequency + * @multiprio_lut: multi priority LUT configuration + * @mplut_prio_boost: BT priority boost registers + * @decision_lut: PTA decision LUT, per Prio-Ch + * + * The structure is used for the BT_COEX command. + */ +struct iwl_bt_coex_cmd { + __le32 mode; + __le32 enabled_modules; + + __le32 max_kill; + __le32 override_primary_lut; + __le32 override_secondary_lut; + __le32 bt4_antenna_isolation_thr; + + __le32 bt4_tx_tx_delta_freq_thr; + __le32 bt4_tx_rx_max_freq0; + + __le32 multiprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE]; + __le32 mplut_prio_boost[BT_COEX_BOOST_SIZE]; + + __le32 decision_lut[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE]; +} __packed; /* BT_COEX_CMD_API_S_VER_6 */ + +/** + * struct iwl_bt_coex_corun_lut_update - bt coex update the corun lut + * @corun_lut20: co-running 20 MHz LUT configuration + * @corun_lut40: co-running 40 MHz LUT configuration + * + * The structure is used for the BT_COEX_UPDATE_CORUN_LUT command. + */ +struct iwl_bt_coex_corun_lut_update_cmd { + __le32 corun_lut20[BT_COEX_CORUN_LUT_SIZE]; + __le32 corun_lut40[BT_COEX_CORUN_LUT_SIZE]; +} __packed; /* BT_COEX_UPDATE_CORUN_LUT_API_S_VER_1 */ + +/** + * struct iwl_bt_coex_sw_boost - SW boost values + * @wifi_tx_prio_boost: SW boost of wifi tx priority + * @wifi_rx_prio_boost: SW boost of wifi rx priority + * @kill_ack_msk: kill ACK mask. 1 - Tx ACK, 0 - kill Tx of ACK. + * @kill_cts_msk: kill CTS mask. 1 - Tx CTS, 0 - kill Tx of CTS. + */ +struct iwl_bt_coex_sw_boost { + __le32 wifi_tx_prio_boost; + __le32 wifi_rx_prio_boost; + __le32 kill_ack_msk; + __le32 kill_cts_msk; +}; + +/** + * struct iwl_bt_coex_sw_boost_update_cmd - command to update the SW boost + * @boost_values: check struct %iwl_bt_coex_sw_boost - one for each channel + * primary / secondary / low priority + */ +struct iwl_bt_coex_sw_boost_update_cmd { + struct iwl_bt_coex_sw_boost boost_values[3]; +} __packed; /* BT_COEX_UPDATE_SW_BOOST_S_VER_1 */ + +/** + * struct iwl_bt_coex_reduced_txp_update_cmd + * @reduced_txp: bit BT_REDUCED_TX_POWER_BIT to enable / disable, rest of the + * bits are the sta_id (value) + */ +struct iwl_bt_coex_reduced_txp_update_cmd { + __le32 reduced_txp; +} __packed; /* BT_COEX_UPDATE_REDUCED_TX_POWER_API_S_VER_1 */ + /** * struct iwl_bt_coex_ci_cmd - bt coex channel inhibition command * @bt_primary_ci: - * @bt_secondary_ci: - * @co_run_bw_primary: - * @co_run_bw_secondary: * @primary_ch_phy_id: + * @bt_secondary_ci: * @secondary_ch_phy_id: * * Used for BT_COEX_CI command */ -struct iwl_bt_coex_ci_cmd_old { +struct iwl_bt_coex_ci_cmd { __le64 bt_primary_ci; - __le64 bt_secondary_ci; + __le32 primary_ch_phy_id; - u8 co_run_bw_primary; - u8 co_run_bw_secondary; - u8 primary_ch_phy_id; - u8 secondary_ch_phy_id; -} __packed; /* BT_CI_MSG_API_S_VER_1 */ + __le64 bt_secondary_ci; + __le32 secondary_ch_phy_id; +} __packed; /* BT_CI_MSG_API_S_VER_2 */ #define BT_MBOX(n_dw, _msg, _pos, _nbits) \ BT_MBOX##n_dw##_##_msg##_POS = (_pos), \ @@ -296,35 +386,34 @@ enum iwl_bt_activity_grading { BT_HIGH_TRAFFIC = 3, }; /* BT_COEX_BT_ACTIVITY_GRADING_API_E_VER_1 */ +enum iwl_bt_ci_compliance { + BT_CI_COMPLIANCE_NONE = 0, + BT_CI_COMPLIANCE_PRIMARY = 1, + BT_CI_COMPLIANCE_SECONDARY = 2, + BT_CI_COMPLIANCE_BOTH = 3, +}; /* BT_COEX_CI_COMPLIENCE_E_VER_1 */ + /** * struct iwl_bt_coex_profile_notif - notification about BT coex * @mbox_msg: message from BT to WiFi * @msg_idx: the index of the message - * @bt_status: 0 - off, 1 - on - * @bt_open_conn: number of BT connections open - * @bt_traffic_load: load of BT traffic - * @bt_agg_traffic_load: aggregated load of BT traffic - * @bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant - * @ttc_enabled: true if ttc has been enabled by the firmware - * @primary_ch_lut: LUT used for primary channel - * @secondary_ch_lut: LUT used for secondary channel + * @bt_ci_compliance: enum %iwl_bt_ci_compliance + * @primary_ch_lut: LUT used for primary channel enum %iwl_bt_coex_lut_type + * @secondary_ch_lut: LUT used for secondary channel enume %iwl_bt_coex_lut_type * @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading + * @ttc_rrc_status: is TTC or RRC enabled - one bit per PHY */ -struct iwl_bt_coex_profile_notif_old { +struct iwl_bt_coex_profile_notif { __le32 mbox_msg[4]; __le32 msg_idx; - u8 bt_status; - u8 bt_open_conn; - u8 bt_traffic_load; - u8 bt_agg_traffic_load; - u8 bt_ci_compliance; - u8 ttc_enabled; - __le16 reserved; + __le32 bt_ci_compliance; __le32 primary_ch_lut; __le32 secondary_ch_lut; __le32 bt_activity_grading; -} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_3 */ + u8 ttc_rrc_status; + u8 reserved[3]; +} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_4 */ enum iwl_bt_coex_prio_table_event { BT_COEX_PRIO_TBL_EVT_INIT_CALIB1 = 0, @@ -363,4 +452,54 @@ struct iwl_bt_coex_prio_tbl_cmd { u8 prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX]; } __packed; +/** + * struct iwl_bt_coex_ci_cmd_old - bt coex channel inhibition command + * @bt_primary_ci: + * @bt_secondary_ci: + * @co_run_bw_primary: + * @co_run_bw_secondary: + * @primary_ch_phy_id: + * @secondary_ch_phy_id: + * + * Used for BT_COEX_CI command + */ +struct iwl_bt_coex_ci_cmd_old { + __le64 bt_primary_ci; + __le64 bt_secondary_ci; + + u8 co_run_bw_primary; + u8 co_run_bw_secondary; + u8 primary_ch_phy_id; + u8 secondary_ch_phy_id; +} __packed; /* BT_CI_MSG_API_S_VER_1 */ + +/** + * struct iwl_bt_coex_profile_notif_old - notification about BT coex + * @mbox_msg: message from BT to WiFi + * @msg_idx: the index of the message + * @bt_status: 0 - off, 1 - on + * @bt_open_conn: number of BT connections open + * @bt_traffic_load: load of BT traffic + * @bt_agg_traffic_load: aggregated load of BT traffic + * @bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant + * @primary_ch_lut: LUT used for primary channel + * @secondary_ch_lut: LUT used for secondary channel + * @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading + */ +struct iwl_bt_coex_profile_notif_old { + __le32 mbox_msg[4]; + __le32 msg_idx; + u8 bt_status; + u8 bt_open_conn; + u8 bt_traffic_load; + u8 bt_agg_traffic_load; + u8 bt_ci_compliance; + u8 ttc_enabled; + __le16 reserved; + + __le32 primary_ch_lut; + __le32 secondary_ch_lut; + __le32 bt_activity_grading; +} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_3 */ + #endif /* __fw_api_bt_coex_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index 309a9b9a94fe..3983a2beb246 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h @@ -163,7 +163,6 @@ enum { BEACON_NOTIFICATION = 0x90, BEACON_TEMPLATE_CMD = 0x91, TX_ANT_CONFIGURATION_CMD = 0x98, - BT_CONFIG = 0x9b, STATISTICS_NOTIFICATION = 0x9d, EOSP_NOTIFICATION = 0x9e, REDUCE_TX_POWER_CMD = 0x9f, @@ -185,6 +184,10 @@ enum { BT_COEX_PRIO_TABLE = 0xcc, BT_COEX_PROT_ENV = 0xcd, BT_PROFILE_NOTIFICATION = 0xce, + BT_CONFIG = 0x9b, + BT_COEX_UPDATE_SW_BOOST = 0x5a, + BT_COEX_UPDATE_CORUN_LUT = 0x5b, + BT_COEX_UPDATE_REDUCED_TXP = 0x5c, BT_COEX_CI = 0x5d, REPLY_SF_CFG_CMD = 0xd1, diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 0b52d0ae4a0d..fbe93a1df93a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -633,8 +633,8 @@ struct iwl_mvm { struct iwl_bt_coex_profile_notif_old last_bt_notif_old; struct iwl_bt_coex_ci_cmd_old last_bt_ci_cmd_old; - struct iwl_bt_coex_profile_notif_old last_bt_notif; - struct iwl_bt_coex_ci_cmd_old last_bt_ci_cmd; + struct iwl_bt_coex_profile_notif last_bt_notif; + struct iwl_bt_coex_ci_cmd last_bt_ci_cmd; u32 last_ant_isol; u8 last_corun_lut; diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 15c13a722a93..b843870be8b7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -324,6 +324,9 @@ static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = { CMD(REPLY_THERMAL_MNG_BACKOFF), CMD(MAC_PM_POWER_TABLE), CMD(BT_COEX_CI), + CMD(BT_COEX_UPDATE_SW_BOOST), + CMD(BT_COEX_UPDATE_CORUN_LUT), + CMD(BT_COEX_UPDATE_REDUCED_TXP), CMD(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION), CMD(ANTENNA_COUPLING_NOTIFICATION), }; From df878f38edc660e9626247724a8671c54fb6cbf7 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 2 Apr 2014 12:23:09 +0300 Subject: [PATCH 240/274] iwlwifi: mvm: BT Coex - convert the sw boost update to new API No need to send the big BT_COEX_CMD command, we have now a much thiner command that updates only what is needed. Adapt the code to that, and open the patch to the updates. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/coex.c | 58 +++++++------------------ 1 file changed, 16 insertions(+), 42 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c index f8c293e54b5e..aa3e83f27f0e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex.c @@ -609,19 +609,12 @@ send_cmd: return ret; } -static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm, - bool reduced_tx_power) +static int iwl_mvm_bt_udpate_sw_boost(struct iwl_mvm *mvm, + bool reduced_tx_power) { enum iwl_bt_kill_msk bt_kill_msk; - struct iwl_bt_coex_cmd_old *bt_cmd; + struct iwl_bt_coex_sw_boost_update_cmd cmd = {}; struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif; - struct iwl_host_cmd cmd = { - .id = BT_CONFIG, - .data[0] = &bt_cmd, - .len = { sizeof(*bt_cmd), }, - .dataflags = { IWL_HCMD_DFL_NOCOPY, }, - }; - int ret = 0; lockdep_assert_held(&mvm->mutex); @@ -651,26 +644,22 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm, mvm->bt_kill_msk = bt_kill_msk; - bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL); - if (!bt_cmd) - return -ENOMEM; - cmd.data[0] = bt_cmd; - bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD); + cmd.boost_values[0].kill_ack_msk = + cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]); + cmd.boost_values[0].kill_cts_msk = + cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]); - bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]); - bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]); - bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE | - BT_VALID_KILL_ACK | - BT_VALID_KILL_CTS); + cmd.boost_values[1].kill_ack_msk = cmd.boost_values[0].kill_ack_msk; + cmd.boost_values[2].kill_cts_msk = cmd.boost_values[0].kill_cts_msk; + cmd.boost_values[1].kill_ack_msk = cmd.boost_values[0].kill_ack_msk; + cmd.boost_values[2].kill_cts_msk = cmd.boost_values[0].kill_cts_msk; IWL_DEBUG_COEX(mvm, "ACK Kill msk = 0x%08x, CTS Kill msk = 0x%08x\n", iwl_bt_ack_kill_msk[bt_kill_msk], iwl_bt_cts_kill_msk[bt_kill_msk]); - ret = iwl_mvm_send_cmd(mvm, &cmd); - - kfree(bt_cmd); - return ret; + return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_SW_BOOST, 0, + sizeof(cmd), &cmd); } static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, @@ -986,7 +975,7 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) */ data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces; - if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power)) + if (iwl_mvm_bt_udpate_sw_boost(mvm, data.reduced_tx_power)) IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n"); } @@ -995,14 +984,11 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, struct iwl_device_cmd *dev_cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_bt_coex_profile_notif_old *notif = (void *)pkt->data; + struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data; if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) return iwl_mvm_rx_bt_coex_notif_old(mvm, rxb, dev_cmd); - /* TODO */ - return 0; - IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n"); IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance); IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n", @@ -1085,9 +1071,6 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, return; } - /* TODO */ - return; - lockdep_assert_held(&mvm->mutex); /* Ignore updates if we are in force mode */ @@ -1133,7 +1116,7 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, */ data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces; - if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power)) + if (iwl_mvm_bt_udpate_sw_boost(mvm, data.reduced_tx_power)) IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n"); } @@ -1149,9 +1132,6 @@ u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) return iwl_mvm_coex_agg_time_limit_old(mvm, sta); - /* TODO */ - return LINK_QUAL_AGG_TIME_LIMIT_DEF; - if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC) return LINK_QUAL_AGG_TIME_LIMIT_DEF; @@ -1216,9 +1196,6 @@ bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) return iwl_mvm_bt_coex_is_tpc_allowed_old(mvm, band); - /* TODO */ - return false; - if (band != IEEE80211_BAND_2GHZ) return false; @@ -1264,9 +1241,6 @@ void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm) return; } - /* TODO */ - return; - iwl_mvm_bt_coex_notif_handle(mvm); } From 704602a1537e3593bb46aa3a6d9b415c868f25e3 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 2 Apr 2014 12:23:09 +0300 Subject: [PATCH 241/274] iwlwifi: mvm: BT Coex - convert the co-running update to new API No need to send the big BT_COEX_CMD command, we have now a much thiner command that updates only what is needed. Adapt the code to that. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/coex.c | 36 +++++-------------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c index aa3e83f27f0e..c876c95f7b23 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex.c @@ -1250,23 +1250,13 @@ int iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm, { struct iwl_rx_packet *pkt = rxb_addr(rxb); u32 ant_isolation = le32_to_cpup((void *)pkt->data); + struct iwl_bt_coex_corun_lut_update_cmd cmd = {}; u8 __maybe_unused lower_bound, upper_bound; - int ret; u8 lut; - struct iwl_bt_coex_cmd_old *bt_cmd; - struct iwl_host_cmd cmd = { - .id = BT_CONFIG, - .len = { sizeof(*bt_cmd), }, - .dataflags = { IWL_HCMD_DFL_NOCOPY, }, - }; - if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) return iwl_mvm_rx_ant_coupling_notif_old(mvm, rxb, dev_cmd); - /* TODO */ - return 0; - if (!IWL_MVM_BT_COEX_CORUNNING) return 0; @@ -1300,25 +1290,13 @@ int iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm, mvm->last_corun_lut = lut; - bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL); - if (!bt_cmd) - return 0; - cmd.data[0] = bt_cmd; - - bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD); - bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE | - BT_VALID_CORUN_LUT_20 | - BT_VALID_CORUN_LUT_40); - /* For the moment, use the same LUT for 20GHz and 40GHz */ - memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[lut].lut20, - sizeof(bt_cmd->bt4_corun_lut20)); + memcpy(&cmd.corun_lut20, antenna_coupling_ranges[lut].lut20, + sizeof(cmd.corun_lut20)); - memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[lut].lut20, - sizeof(bt_cmd->bt4_corun_lut40)); + memcpy(&cmd.corun_lut40, antenna_coupling_ranges[lut].lut20, + sizeof(cmd.corun_lut40)); - ret = iwl_mvm_send_cmd(mvm, &cmd); - - kfree(bt_cmd); - return ret; + return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_CORUN_LUT, 0, + sizeof(cmd), &cmd); } From 455e7ac578a7bb325c7f09dd8386c3b347bb3e4b Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 22 May 2014 12:48:27 +0300 Subject: [PATCH 242/274] iwlwifi: mvm: BT Coex - convert reduced Tx power to new API No need to send the big BT_COEX_CMD command, we have now a much thiner command that updates only what is needed. Adapt the code to that. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/coex.c | 29 ++++++------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c index c876c95f7b23..3395df1c5b75 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex.c @@ -665,19 +665,11 @@ static int iwl_mvm_bt_udpate_sw_boost(struct iwl_mvm *mvm, static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, bool enable) { - struct iwl_bt_coex_cmd_old *bt_cmd; - /* Send ASYNC since this can be sent from an atomic context */ - struct iwl_host_cmd cmd = { - .id = BT_CONFIG, - .len = { sizeof(*bt_cmd), }, - .dataflags = { IWL_HCMD_DFL_NOCOPY, }, - .flags = CMD_ASYNC, - }; + struct iwl_bt_coex_reduced_txp_update_cmd cmd = {}; struct iwl_mvm_sta *mvmsta; + u32 value; int ret; - return 0; - mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id); if (!mvmsta) return 0; @@ -686,27 +678,20 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, if (mvmsta->bt_reduced_txpower == enable) return 0; - bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_ATOMIC); - if (!bt_cmd) - return -ENOMEM; - cmd.data[0] = bt_cmd; - bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD); - - bt_cmd->valid_bit_msk = - cpu_to_le32(BT_VALID_ENABLE | BT_VALID_REDUCED_TX_POWER); - bt_cmd->bt_reduced_tx_power = sta_id; + value = mvmsta->sta_id; if (enable) - bt_cmd->bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT; + value |= BT_REDUCED_TX_POWER_BIT; IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n", enable ? "en" : "dis", sta_id); + cmd.reduced_txp = cpu_to_le32(value); mvmsta->bt_reduced_txpower = enable; - ret = iwl_mvm_send_cmd(mvm, &cmd); + ret = iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_REDUCED_TXP, CMD_ASYNC, + sizeof(cmd), &cmd); - kfree(bt_cmd); return ret; } From 261c0ec07ecf1cc555a600315d687a94f2ee0660 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 11 Jun 2014 15:37:25 +0300 Subject: [PATCH 243/274] iwlwifi: mvm: BT Coex - add High Band retention Tell the firmware if TTC should be enabled when switching to High Band. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/coex.c | 2 ++ drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c index 3395df1c5b75..64325b60c0ff 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex.c @@ -587,6 +587,8 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm) cpu_to_le32(BT_COEX_MPLUT_BOOST_ENABLED); } + bt_cmd->enabled_modules |= cpu_to_le32(BT_COEX_HIGH_BAND_RET); + if (mvm->cfg->bt_shared_single_ant) memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant, sizeof(iwl_single_shared_ant)); diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h index 98becb9c90fa..fea817d6a9be 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h @@ -220,6 +220,7 @@ enum iwl_bt_coex_enabled_modules { BT_COEX_MPLUT_BOOST_ENABLED = BIT(1), BT_COEX_SYNC2SCO_ENABLED = BIT(2), BT_COEX_CORUN_ENABLED = BIT(3), + BT_COEX_HIGH_BAND_RET = BIT(4), }; /* BT_COEX_MODULES_ENABLE_E_VER_1 */ /** From 160be5719bbcb97b46f008a0f9f23e80139f1e2c Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 2 Jul 2014 17:33:52 +0300 Subject: [PATCH 244/274] iwlwifi: mvm: BT Coex - fix debugfs with old API Fix the debugfs hook to make it able to display the data with the old firmware API. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/debugfs.c | 187 +++++++++++++++++---- 1 file changed, 150 insertions(+), 37 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index e66c659b264a..f131ef0ec5b3 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -312,20 +312,10 @@ static ssize_t iwl_dbgfs_disable_power_off_write(struct iwl_mvm *mvm, char *buf, BT_MBOX_MSG(notif, _num, _field), \ true ? "\n" : ", "); -static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) +static +int iwl_mvm_coex_dump_mbox(struct iwl_bt_coex_profile_notif *notif, char *buf, + int pos, int bufsz) { - struct iwl_mvm *mvm = file->private_data; - struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif; - char *buf; - int ret, pos = 0, bufsz = sizeof(char) * 1024; - - buf = kmalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - mutex_lock(&mvm->mutex); - pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw0:\n"); BT_MBOX_PRINT(0, LE_SLAVE_LAT, false); @@ -378,17 +368,118 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf, BT_MBOX_PRINT(3, SSN_2, false); BT_MBOX_PRINT(3, UPDATE_REQUEST, true); - pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n", - notif->bt_ci_compliance); - pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n", - le32_to_cpu(notif->primary_ch_lut)); - pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n", - le32_to_cpu(notif->secondary_ch_lut)); - pos += scnprintf(buf+pos, bufsz-pos, "bt_activity_grading = %d\n", - le32_to_cpu(notif->bt_activity_grading)); - pos += scnprintf(buf+pos, bufsz-pos, - "antenna isolation = %d CORUN LUT index = %d\n", - mvm->last_ant_isol, mvm->last_corun_lut); + return pos; +} + +static +int iwl_mvm_coex_dump_mbox_old(struct iwl_bt_coex_profile_notif_old *notif, + char *buf, int pos, int bufsz) +{ + pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw0:\n"); + + BT_MBOX_PRINT(0, LE_SLAVE_LAT, false); + BT_MBOX_PRINT(0, LE_PROF1, false); + BT_MBOX_PRINT(0, LE_PROF2, false); + BT_MBOX_PRINT(0, LE_PROF_OTHER, false); + BT_MBOX_PRINT(0, CHL_SEQ_N, false); + BT_MBOX_PRINT(0, INBAND_S, false); + BT_MBOX_PRINT(0, LE_MIN_RSSI, false); + BT_MBOX_PRINT(0, LE_SCAN, false); + BT_MBOX_PRINT(0, LE_ADV, false); + BT_MBOX_PRINT(0, LE_MAX_TX_POWER, false); + BT_MBOX_PRINT(0, OPEN_CON_1, true); + + pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw1:\n"); + + BT_MBOX_PRINT(1, BR_MAX_TX_POWER, false); + BT_MBOX_PRINT(1, IP_SR, false); + BT_MBOX_PRINT(1, LE_MSTR, false); + BT_MBOX_PRINT(1, AGGR_TRFC_LD, false); + BT_MBOX_PRINT(1, MSG_TYPE, false); + BT_MBOX_PRINT(1, SSN, true); + + pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw2:\n"); + + BT_MBOX_PRINT(2, SNIFF_ACT, false); + BT_MBOX_PRINT(2, PAG, false); + BT_MBOX_PRINT(2, INQUIRY, false); + BT_MBOX_PRINT(2, CONN, false); + BT_MBOX_PRINT(2, SNIFF_INTERVAL, false); + BT_MBOX_PRINT(2, DISC, false); + BT_MBOX_PRINT(2, SCO_TX_ACT, false); + BT_MBOX_PRINT(2, SCO_RX_ACT, false); + BT_MBOX_PRINT(2, ESCO_RE_TX, false); + BT_MBOX_PRINT(2, SCO_DURATION, true); + + pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw3:\n"); + + BT_MBOX_PRINT(3, SCO_STATE, false); + BT_MBOX_PRINT(3, SNIFF_STATE, false); + BT_MBOX_PRINT(3, A2DP_STATE, false); + BT_MBOX_PRINT(3, ACL_STATE, false); + BT_MBOX_PRINT(3, MSTR_STATE, false); + BT_MBOX_PRINT(3, OBX_STATE, false); + BT_MBOX_PRINT(3, OPEN_CON_2, false); + BT_MBOX_PRINT(3, TRAFFIC_LOAD, false); + BT_MBOX_PRINT(3, CHL_SEQN_LSB, false); + BT_MBOX_PRINT(3, INBAND_P, false); + BT_MBOX_PRINT(3, MSG_TYPE_2, false); + BT_MBOX_PRINT(3, SSN_2, false); + BT_MBOX_PRINT(3, UPDATE_REQUEST, true); + + return pos; +} + +static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + char *buf; + int ret, pos = 0, bufsz = sizeof(char) * 1024; + + buf = kmalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&mvm->mutex); + + if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) { + struct iwl_bt_coex_profile_notif_old *notif = + &mvm->last_bt_notif_old; + + pos += iwl_mvm_coex_dump_mbox_old(notif, buf, pos, bufsz); + + pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n", + notif->bt_ci_compliance); + pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n", + le32_to_cpu(notif->primary_ch_lut)); + pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n", + le32_to_cpu(notif->secondary_ch_lut)); + pos += scnprintf(buf+pos, + bufsz-pos, "bt_activity_grading = %d\n", + le32_to_cpu(notif->bt_activity_grading)); + pos += scnprintf(buf+pos, bufsz-pos, + "antenna isolation = %d CORUN LUT index = %d\n", + mvm->last_ant_isol, mvm->last_corun_lut); + } else { + struct iwl_bt_coex_profile_notif *notif = + &mvm->last_bt_notif; + + pos += iwl_mvm_coex_dump_mbox(notif, buf, pos, bufsz); + + pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n", + notif->bt_ci_compliance); + pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n", + le32_to_cpu(notif->primary_ch_lut)); + pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n", + le32_to_cpu(notif->secondary_ch_lut)); + pos += scnprintf(buf+pos, + bufsz-pos, "bt_activity_grading = %d\n", + le32_to_cpu(notif->bt_activity_grading)); + pos += scnprintf(buf+pos, bufsz-pos, + "antenna isolation = %d CORUN LUT index = %d\n", + mvm->last_ant_isol, mvm->last_corun_lut); + } mutex_unlock(&mvm->mutex); @@ -403,26 +494,48 @@ static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_mvm *mvm = file->private_data; - struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd; char buf[256]; int bufsz = sizeof(buf); int pos = 0; mutex_lock(&mvm->mutex); - pos += scnprintf(buf+pos, bufsz-pos, "Channel inhibition CMD\n"); - pos += scnprintf(buf+pos, bufsz-pos, - "\tPrimary Channel Bitmap 0x%016llx\n", - le64_to_cpu(cmd->bt_primary_ci)); - pos += scnprintf(buf+pos, bufsz-pos, - "\tSecondary Channel Bitmap 0x%016llx\n", - le64_to_cpu(cmd->bt_secondary_ci)); + if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) { + struct iwl_bt_coex_ci_cmd_old *cmd = &mvm->last_bt_ci_cmd_old; - pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n"); - pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill Mask 0x%08x\n", - iwl_bt_ack_kill_msk[mvm->bt_kill_msk]); - pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill Mask 0x%08x\n", - iwl_bt_cts_kill_msk[mvm->bt_kill_msk]); + pos += scnprintf(buf+pos, bufsz-pos, + "Channel inhibition CMD\n"); + pos += scnprintf(buf+pos, bufsz-pos, + "\tPrimary Channel Bitmap 0x%016llx\n", + le64_to_cpu(cmd->bt_primary_ci)); + pos += scnprintf(buf+pos, bufsz-pos, + "\tSecondary Channel Bitmap 0x%016llx\n", + le64_to_cpu(cmd->bt_secondary_ci)); + + pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n"); + pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill Mask 0x%08x\n", + iwl_bt_ack_kill_msk[mvm->bt_kill_msk]); + pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill Mask 0x%08x\n", + iwl_bt_cts_kill_msk[mvm->bt_kill_msk]); + + } else { + struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd; + + pos += scnprintf(buf+pos, bufsz-pos, + "Channel inhibition CMD\n"); + pos += scnprintf(buf+pos, bufsz-pos, + "\tPrimary Channel Bitmap 0x%016llx\n", + le64_to_cpu(cmd->bt_primary_ci)); + pos += scnprintf(buf+pos, bufsz-pos, + "\tSecondary Channel Bitmap 0x%016llx\n", + le64_to_cpu(cmd->bt_secondary_ci)); + + pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n"); + pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill Mask 0x%08x\n", + iwl_bt_ack_kill_msk[mvm->bt_kill_msk]); + pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill Mask 0x%08x\n", + iwl_bt_cts_kill_msk[mvm->bt_kill_msk]); + } mutex_unlock(&mvm->mutex); From 4c86f938d359f30a52134d3fc9945f95a3e621e2 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 30 Jun 2014 10:26:02 +0300 Subject: [PATCH 245/274] iwlwifi: mvm: BT Coex - relax constraints when TTC / RRC is active When TxTxCo-Running is active, we can relax the constraints on the rate control. When RxRxCo-Running is active, we can relax the constrains on SMPS. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/coex.c | 21 +++++++++++-------- .../net/wireless/iwlwifi/mvm/fw-api-coex.h | 6 ++++++ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c index 64325b60c0ff..8110fe00bf55 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex.c @@ -787,6 +787,10 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, if (!vif->bss_conf.assoc) smps_mode = IEEE80211_SMPS_AUTOMATIC; + if (IWL_COEX_IS_RRC_ON(mvm->last_bt_notif.ttc_rrc_status, + mvmvif->phy_ctxt->id)) + smps_mode = IEEE80211_SMPS_AUTOMATIC; + IWL_DEBUG_COEX(data->mvm, "mac %d: bt_activity_grading %d smps_req %d\n", mvmvif->id, bt_activity_grading, smps_mode); @@ -1114,19 +1118,19 @@ u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, struct ieee80211_sta *sta) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); + struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt; enum iwl_bt_coex_lut_type lut_type; if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) return iwl_mvm_coex_agg_time_limit_old(mvm, sta); + if (IWL_COEX_IS_TTC_ON(mvm->last_bt_notif.ttc_rrc_status, phy_ctxt->id)) + return LINK_QUAL_AGG_TIME_LIMIT_DEF; + if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC) return LINK_QUAL_AGG_TIME_LIMIT_DEF; -/* - TODO - if (mvm->last_bt_notif.ttc_enabled) - return LINK_QUAL_AGG_TIME_LIMIT_DEF; -*/ lut_type = iwl_get_coex_type(mvm, mvmsta->vif); @@ -1141,16 +1145,15 @@ bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, struct ieee80211_sta *sta) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); + struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt; enum iwl_bt_coex_lut_type lut_type; if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) return iwl_mvm_coex_agg_time_limit_old(mvm, sta); -/* - TODO - if (mvm->last_bt_notif.ttc_enabled) + if (IWL_COEX_IS_TTC_ON(mvm->last_bt_notif.ttc_rrc_status, phy_ctxt->id)) return true; -*/ if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC) diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h index fea817d6a9be..ab12aaa43034 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h @@ -394,6 +394,12 @@ enum iwl_bt_ci_compliance { BT_CI_COMPLIANCE_BOTH = 3, }; /* BT_COEX_CI_COMPLIENCE_E_VER_1 */ +#define IWL_COEX_IS_TTC_ON(_ttc_rrc_status, _phy_id) \ + (_ttc_rrc_status & BIT(_phy_id)) + +#define IWL_COEX_IS_RRC_ON(_ttc_rrc_status, _phy_id) \ + ((_ttc_rrc_status >> 4) & BIT(_phy_id)) + /** * struct iwl_bt_coex_profile_notif - notification about BT coex * @mbox_msg: message from BT to WiFi From bdce40f006dc53bfdeeba4a4ff200d4c0cf5aac8 Mon Sep 17 00:00:00 2001 From: Eran Harary Date: Tue, 1 Jul 2014 17:31:38 +0300 Subject: [PATCH 246/274] iwlwifi: mvm: warn about empty OTP Signed-off-by: Eran Harary Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/nvm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index 1f1a550828fa..b04805ccb443 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -530,6 +530,8 @@ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic) } #endif } + if (!size_read) + IWL_ERR(mvm, "OTP is blank\n"); kfree(nvm_buffer); } From ae969afe43273c9bc8d22629efd9c51627837615 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 11 Jun 2014 15:51:33 +0300 Subject: [PATCH 247/274] iwlwifi: mvm: rs: don't clear persistent fields iwl_mvm_rs_rate_init() is called multiple times to re-init the rate scaling statistics (e.g. after some idle time). It clears all the lq_sta sta, including some fields that shouldn't be cleared (e.g. debugfs pointers). Fix it by adding a new 'persistent' sub-struct, and avoid clearing it on (re-)init. Move the initialization of the persistent fields to rs_alloc_sta instead. Signed-off-by: Eliad Peller Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/rs.c | 87 ++++++++++++++------------- drivers/net/wireless/iwlwifi/mvm/rs.h | 24 +++++--- 2 files changed, 59 insertions(+), 52 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 306a6caa4868..67bd8d79c9d3 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -927,7 +927,7 @@ static bool rs_get_lower_rate_in_column(struct iwl_lq_sta *lq_sta, u8 low; u16 high_low; u16 rate_mask; - struct iwl_mvm *mvm = lq_sta->drv; + struct iwl_mvm *mvm = lq_sta->pers.drv; rate_mask = rs_get_supported_rates(lq_sta, rate); high_low = rs_get_adjacent_rate(mvm, rate->index, rate_mask, @@ -946,7 +946,7 @@ static bool rs_get_lower_rate_in_column(struct iwl_lq_sta *lq_sta, static void rs_get_lower_rate_down_column(struct iwl_lq_sta *lq_sta, struct rs_rate *rate) { - struct iwl_mvm *mvm = lq_sta->drv; + struct iwl_mvm *mvm = lq_sta->pers.drv; if (is_legacy(rate)) { /* No column to downgrade from Legacy */ @@ -1026,14 +1026,14 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, if (!lq_sta) { IWL_DEBUG_RATE(mvm, "Station rate scaling not created yet.\n"); return; - } else if (!lq_sta->drv) { + } else if (!lq_sta->pers.drv) { IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n"); return; } #ifdef CONFIG_MAC80211_DEBUGFS /* Disable last tx check if we are debugging with fixed rate */ - if (lq_sta->dbg_fixed_rate) { + if (lq_sta->pers.dbg_fixed_rate) { IWL_DEBUG_RATE(mvm, "Fixed rate. avoid rate scaling\n"); return; } @@ -1405,7 +1405,7 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) int flush_interval_passed = 0; struct iwl_mvm *mvm; - mvm = lq_sta->drv; + mvm = lq_sta->pers.drv; active_tbl = lq_sta->active_tbl; tbl = &(lq_sta->lq_info[active_tbl]); @@ -1865,11 +1865,11 @@ static bool rs_tpc_perform(struct iwl_mvm *mvm, int weak_tpt = IWL_INVALID_VALUE, strong_tpt = IWL_INVALID_VALUE; #ifdef CONFIG_MAC80211_DEBUGFS - if (lq_sta->dbg_fixed_txp_reduction <= TPC_MAX_REDUCTION) { + if (lq_sta->pers.dbg_fixed_txp_reduction <= TPC_MAX_REDUCTION) { IWL_DEBUG_RATE(mvm, "fixed tpc: %d\n", - lq_sta->dbg_fixed_txp_reduction); - lq_sta->lq.reduced_tpc = lq_sta->dbg_fixed_txp_reduction; - return cur != lq_sta->dbg_fixed_txp_reduction; + lq_sta->pers.dbg_fixed_txp_reduction); + lq_sta->lq.reduced_tpc = lq_sta->pers.dbg_fixed_txp_reduction; + return cur != lq_sta->pers.dbg_fixed_txp_reduction; } #endif @@ -2382,7 +2382,7 @@ static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta, } /* Treat uninitialized rate scaling data same as non-existing. */ - if (lq_sta && !lq_sta->drv) { + if (lq_sta && !lq_sta->pers.drv) { IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n"); mvm_sta = NULL; } @@ -2401,12 +2401,18 @@ static void *rs_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta, gfp_t gfp) { struct iwl_mvm_sta *sta_priv = (struct iwl_mvm_sta *)sta->drv_priv; - struct iwl_op_mode *op_mode __maybe_unused = - (struct iwl_op_mode *)mvm_rate; - struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode); + struct iwl_op_mode *op_mode = (struct iwl_op_mode *)mvm_rate; + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + struct iwl_lq_sta *lq_sta = &sta_priv->lq_sta; IWL_DEBUG_RATE(mvm, "create station rate scale window\n"); + lq_sta->pers.drv = mvm; +#ifdef CONFIG_MAC80211_DEBUGFS + lq_sta->pers.dbg_fixed_rate = 0; + lq_sta->pers.dbg_fixed_txp_reduction = TPC_INVALID; +#endif + return &sta_priv->lq_sta; } @@ -2552,7 +2558,9 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, sta_priv = (struct iwl_mvm_sta *)sta->drv_priv; lq_sta = &sta_priv->lq_sta; - memset(lq_sta, 0, sizeof(*lq_sta)); + + /* clear all non-persistent lq data */ + memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers)); sband = hw->wiphy->bands[band]; @@ -2630,17 +2638,12 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, /* as default allow aggregation for all tids */ lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID; - lq_sta->drv = mvm; /* Set last_txrate_idx to lowest rate */ lq_sta->last_txrate_idx = rate_lowest_index(sband, sta); if (sband->band == IEEE80211_BAND_5GHZ) lq_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE; lq_sta->is_agg = 0; -#ifdef CONFIG_MAC80211_DEBUGFS - lq_sta->dbg_fixed_rate = 0; - lq_sta->dbg_fixed_txp_reduction = TPC_INVALID; -#endif #ifdef CONFIG_IWLWIFI_DEBUGFS iwl_mvm_reset_frame_stats(mvm, &mvm->drv_rx_stats); #endif @@ -2811,12 +2814,12 @@ static void rs_fill_lq_cmd(struct iwl_mvm *mvm, u8 ant = initial_rate->ant; #ifdef CONFIG_MAC80211_DEBUGFS - if (lq_sta->dbg_fixed_rate) { + if (lq_sta->pers.dbg_fixed_rate) { rs_build_rates_table_from_fixed(mvm, lq_cmd, lq_sta->band, - lq_sta->dbg_fixed_rate); + lq_sta->pers.dbg_fixed_rate); lq_cmd->reduced_tpc = 0; - ant = (lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) >> + ant = (lq_sta->pers.dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS; } else #endif @@ -2926,14 +2929,14 @@ static void rs_program_fix_rate(struct iwl_mvm *mvm, lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ IWL_DEBUG_RATE(mvm, "sta_id %d rate 0x%X\n", - lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate); + lq_sta->lq.sta_id, lq_sta->pers.dbg_fixed_rate); - if (lq_sta->dbg_fixed_rate) { + if (lq_sta->pers.dbg_fixed_rate) { struct rs_rate rate; - rs_rate_from_ucode_rate(lq_sta->dbg_fixed_rate, + rs_rate_from_ucode_rate(lq_sta->pers.dbg_fixed_rate, lq_sta->band, &rate); rs_fill_lq_cmd(mvm, NULL, lq_sta, &rate); - iwl_mvm_send_lq_cmd(lq_sta->drv, &lq_sta->lq, false); + iwl_mvm_send_lq_cmd(lq_sta->pers.drv, &lq_sta->lq, false); } } @@ -2946,16 +2949,16 @@ static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file, size_t buf_size; u32 parsed_rate; - mvm = lq_sta->drv; + mvm = lq_sta->pers.drv; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; if (sscanf(buf, "%x", &parsed_rate) == 1) - lq_sta->dbg_fixed_rate = parsed_rate; + lq_sta->pers.dbg_fixed_rate = parsed_rate; else - lq_sta->dbg_fixed_rate = 0; + lq_sta->pers.dbg_fixed_rate = 0; rs_program_fix_rate(mvm, lq_sta); @@ -2974,7 +2977,7 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, struct iwl_mvm *mvm; struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); struct rs_rate *rate = &tbl->rate; - mvm = lq_sta->drv; + mvm = lq_sta->pers.drv; buff = kmalloc(2048, GFP_KERNEL); if (!buff) return -ENOMEM; @@ -2984,7 +2987,7 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, lq_sta->total_failed, lq_sta->total_success, lq_sta->active_legacy_rate); desc += sprintf(buff+desc, "fixed rate 0x%X\n", - lq_sta->dbg_fixed_rate); + lq_sta->pers.dbg_fixed_rate); desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n", (mvm->fw->valid_tx_ant & ANT_A) ? "ANT_A," : "", (mvm->fw->valid_tx_ant & ANT_B) ? "ANT_B," : "", @@ -3182,31 +3185,31 @@ static const struct file_operations rs_sta_dbgfs_drv_tx_stats_ops = { static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir) { struct iwl_lq_sta *lq_sta = mvm_sta; - lq_sta->rs_sta_dbgfs_scale_table_file = + lq_sta->pers.rs_sta_dbgfs_scale_table_file = debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir, lq_sta, &rs_sta_dbgfs_scale_table_ops); - lq_sta->rs_sta_dbgfs_stats_table_file = + lq_sta->pers.rs_sta_dbgfs_stats_table_file = debugfs_create_file("rate_stats_table", S_IRUSR, dir, lq_sta, &rs_sta_dbgfs_stats_table_ops); - lq_sta->rs_sta_dbgfs_drv_tx_stats_file = + lq_sta->pers.rs_sta_dbgfs_drv_tx_stats_file = debugfs_create_file("drv_tx_stats", S_IRUSR | S_IWUSR, dir, lq_sta, &rs_sta_dbgfs_drv_tx_stats_ops); - lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file = + lq_sta->pers.rs_sta_dbgfs_tx_agg_tid_en_file = debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir, &lq_sta->tx_agg_tid_en); - lq_sta->rs_sta_dbgfs_reduced_txp_file = + lq_sta->pers.rs_sta_dbgfs_reduced_txp_file = debugfs_create_u8("reduced_tpc", S_IRUSR | S_IWUSR, dir, - &lq_sta->dbg_fixed_txp_reduction); + &lq_sta->pers.dbg_fixed_txp_reduction); } static void rs_remove_debugfs(void *mvm, void *mvm_sta) { struct iwl_lq_sta *lq_sta = mvm_sta; - debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file); - debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file); - debugfs_remove(lq_sta->rs_sta_dbgfs_drv_tx_stats_file); - debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file); - debugfs_remove(lq_sta->rs_sta_dbgfs_reduced_txp_file); + debugfs_remove(lq_sta->pers.rs_sta_dbgfs_scale_table_file); + debugfs_remove(lq_sta->pers.rs_sta_dbgfs_stats_table_file); + debugfs_remove(lq_sta->pers.rs_sta_dbgfs_drv_tx_stats_file); + debugfs_remove(lq_sta->pers.rs_sta_dbgfs_tx_agg_tid_en_file); + debugfs_remove(lq_sta->pers.rs_sta_dbgfs_reduced_txp_file); } #endif diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.h b/drivers/net/wireless/iwlwifi/mvm/rs.h index 374a83d7db25..2e1a683d0162 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.h +++ b/drivers/net/wireless/iwlwifi/mvm/rs.h @@ -349,16 +349,6 @@ struct iwl_lq_sta { struct iwl_lq_cmd lq; struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */ u8 tx_agg_tid_en; -#ifdef CONFIG_MAC80211_DEBUGFS - struct dentry *rs_sta_dbgfs_scale_table_file; - struct dentry *rs_sta_dbgfs_stats_table_file; - struct dentry *rs_sta_dbgfs_drv_tx_stats_file; - struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file; - struct dentry *rs_sta_dbgfs_reduced_txp_file; - u32 dbg_fixed_rate; - u8 dbg_fixed_txp_reduction; -#endif - struct iwl_mvm *drv; /* used to be in sta_info */ int last_txrate_idx; @@ -369,6 +359,20 @@ struct iwl_lq_sta { /* tx power reduce for this sta */ int tpc_reduce; + + /* persistent fields - initialized only once - keep last! */ + struct { +#ifdef CONFIG_MAC80211_DEBUGFS + struct dentry *rs_sta_dbgfs_scale_table_file; + struct dentry *rs_sta_dbgfs_stats_table_file; + struct dentry *rs_sta_dbgfs_drv_tx_stats_file; + struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file; + struct dentry *rs_sta_dbgfs_reduced_txp_file; + u32 dbg_fixed_rate; + u8 dbg_fixed_txp_reduction; +#endif + struct iwl_mvm *drv; + } pers; }; /* Initialize station's rate scaling information after adding station */ From c6e1faad7500c07b75d11d339414cead0d57534b Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 15 Jun 2014 12:02:20 +0300 Subject: [PATCH 248/274] iwlwifi: mvm: rs: don't save debugfs files These file are removed recursively anyway, so there's no point saving them just to redundantly remove them later. Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/rs.c | 31 +++++++++------------------ drivers/net/wireless/iwlwifi/mvm/rs.h | 5 ----- 2 files changed, 10 insertions(+), 26 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 67bd8d79c9d3..c70e959bf0e3 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -3185,31 +3185,20 @@ static const struct file_operations rs_sta_dbgfs_drv_tx_stats_ops = { static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir) { struct iwl_lq_sta *lq_sta = mvm_sta; - lq_sta->pers.rs_sta_dbgfs_scale_table_file = - debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir, - lq_sta, &rs_sta_dbgfs_scale_table_ops); - lq_sta->pers.rs_sta_dbgfs_stats_table_file = - debugfs_create_file("rate_stats_table", S_IRUSR, dir, - lq_sta, &rs_sta_dbgfs_stats_table_ops); - lq_sta->pers.rs_sta_dbgfs_drv_tx_stats_file = - debugfs_create_file("drv_tx_stats", S_IRUSR | S_IWUSR, dir, - lq_sta, &rs_sta_dbgfs_drv_tx_stats_ops); - lq_sta->pers.rs_sta_dbgfs_tx_agg_tid_en_file = - debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir, - &lq_sta->tx_agg_tid_en); - lq_sta->pers.rs_sta_dbgfs_reduced_txp_file = - debugfs_create_u8("reduced_tpc", S_IRUSR | S_IWUSR, dir, - &lq_sta->pers.dbg_fixed_txp_reduction); + debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir, + lq_sta, &rs_sta_dbgfs_scale_table_ops); + debugfs_create_file("rate_stats_table", S_IRUSR, dir, + lq_sta, &rs_sta_dbgfs_stats_table_ops); + debugfs_create_file("drv_tx_stats", S_IRUSR | S_IWUSR, dir, + lq_sta, &rs_sta_dbgfs_drv_tx_stats_ops); + debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir, + &lq_sta->tx_agg_tid_en); + debugfs_create_u8("reduced_tpc", S_IRUSR | S_IWUSR, dir, + &lq_sta->pers.dbg_fixed_txp_reduction); } static void rs_remove_debugfs(void *mvm, void *mvm_sta) { - struct iwl_lq_sta *lq_sta = mvm_sta; - debugfs_remove(lq_sta->pers.rs_sta_dbgfs_scale_table_file); - debugfs_remove(lq_sta->pers.rs_sta_dbgfs_stats_table_file); - debugfs_remove(lq_sta->pers.rs_sta_dbgfs_drv_tx_stats_file); - debugfs_remove(lq_sta->pers.rs_sta_dbgfs_tx_agg_tid_en_file); - debugfs_remove(lq_sta->pers.rs_sta_dbgfs_reduced_txp_file); } #endif diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.h b/drivers/net/wireless/iwlwifi/mvm/rs.h index 2e1a683d0162..f27b9d687a25 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.h +++ b/drivers/net/wireless/iwlwifi/mvm/rs.h @@ -363,11 +363,6 @@ struct iwl_lq_sta { /* persistent fields - initialized only once - keep last! */ struct { #ifdef CONFIG_MAC80211_DEBUGFS - struct dentry *rs_sta_dbgfs_scale_table_file; - struct dentry *rs_sta_dbgfs_stats_table_file; - struct dentry *rs_sta_dbgfs_drv_tx_stats_file; - struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file; - struct dentry *rs_sta_dbgfs_reduced_txp_file; u32 dbg_fixed_rate; u8 dbg_fixed_txp_reduction; #endif From fb98be5e94194ba76b507770dc96f744fed8e4c5 Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Sun, 4 May 2014 12:51:10 +0300 Subject: [PATCH 249/274] iwlwifi: mvm: add unified LMAC scan API Add new scan API that uses the same command 0x51 for both regular and sched scan. Signed-off-by: David Spinadel Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-fw.h | 2 + .../net/wireless/iwlwifi/mvm/fw-api-scan.h | 207 ++++++++ drivers/net/wireless/iwlwifi/mvm/fw-api.h | 1 + drivers/net/wireless/iwlwifi/mvm/mac80211.c | 29 +- drivers/net/wireless/iwlwifi/mvm/mvm.h | 19 +- drivers/net/wireless/iwlwifi/mvm/ops.c | 18 +- drivers/net/wireless/iwlwifi/mvm/scan.c | 447 ++++++++++++++++-- 7 files changed, 653 insertions(+), 70 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index 79b0508fddef..1bb5193c5b1b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -124,6 +124,7 @@ enum iwl_ucode_tlv_flag { * @IWL_UCODE_TLV_API_BT_COEX_SPLIT: new API for BT Coex * @IWL_UCODE_TLV_API_CSA_FLOW: ucode can do unbind-bind flow for CSA. * @IWL_UCODE_TLV_API_DISABLE_STA_TX: ucode supports tx_disable bit. + * @IWL_UCODE_TLV_API_LMAC_SCAN: This ucode uses LMAC unified scan API. */ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID = BIT(0), @@ -131,6 +132,7 @@ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_BT_COEX_SPLIT = BIT(3), IWL_UCODE_TLV_API_CSA_FLOW = BIT(4), IWL_UCODE_TLV_API_DISABLE_STA_TX = BIT(5), + IWL_UCODE_TLV_API_LMAC_SCAN = BIT(6), }; /** diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h index 1d586923d5b7..48a1d8f5675d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h @@ -582,4 +582,211 @@ struct iwl_sched_scan_results { u8 reserved; }; +/* Unified LMAC scan API */ + +#define IWL_MVM_BASIC_PASSIVE_DWELL 110 + +/** + * iwl_scan_req_tx_cmd - SCAN_REQ_TX_CMD_API_S + * @tx_flags: combination of TX_CMD_FLG_* + * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is + * cleared. Combination of RATE_MCS_* + * @sta_id: index of destination station in FW station table + * @reserved: for alignment and future use + */ +struct iwl_scan_req_tx_cmd { + __le32 tx_flags; + __le32 rate_n_flags; + u8 sta_id; + u8 reserved[3]; +} __packed; + +enum iwl_scan_channel_flags_lmac { + IWL_UNIFIED_SCAN_CHANNEL_FULL = BIT(27), + IWL_UNIFIED_SCAN_CHANNEL_PARTIAL = BIT(28), +}; + +/** + * iwl_scan_channel_cfg_lmac - SCAN_CHANNEL_CFG_S_VER2 + * @flags: bits 1-20: directed scan to i'th ssid + * other bits &enum iwl_scan_channel_flags_lmac + * @channel_number: channel number 1-13 etc + * @iter_count: scan iteration on this channel + * @iter_interval: interval in seconds between iterations on one channel + */ +struct iwl_scan_channel_cfg_lmac { + __le32 flags; + __le16 channel_num; + __le16 iter_count; + __le32 iter_interval; +} __packed; + +/* + * iwl_scan_probe_segment - PROBE_SEGMENT_API_S_VER_1 + * @offset: offset in the data block + * @len: length of the segment + */ +struct iwl_scan_probe_segment { + __le16 offset; + __le16 len; +} __packed; + +/* iwl_scan_probe_req - PROBE_REQUEST_FRAME_API_S_VER_2 + * @mac_header: first (and common) part of the probe + * @band_data: band specific data + * @common_data: last (and common) part of the probe + * @buf: raw data block + */ +struct iwl_scan_probe_req { + struct iwl_scan_probe_segment mac_header; + struct iwl_scan_probe_segment band_data[2]; + struct iwl_scan_probe_segment common_data; + u8 buf[SCAN_OFFLOAD_PROBE_REQ_SIZE]; +} __packed; + +enum iwl_scan_channel_flags { + IWL_SCAN_CHANNEL_FLAG_EBS = BIT(0), + IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE = BIT(1), + IWL_SCAN_CHANNEL_FLAG_CACHE_ADD = BIT(2), +}; + +/* iwl_scan_channel_opt - CHANNEL_OPTIMIZATION_API_S + * @flags: enum iwl_scan_channel_flgs + * @non_ebs_ratio: how many regular scan iteration before EBS + */ +struct iwl_scan_channel_opt { + __le16 flags; + __le16 non_ebs_ratio; +} __packed; + +/** + * iwl_mvm_lmac_scan_flags + * @IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL: pass all beacons and probe responses + * without filtering. + * @IWL_MVM_LMAC_SCAN_FLAG_PASSIVE: force passive scan on all channels + * @IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION: single channel scan + * @IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE: send iteration complete notification + * @IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS multiple SSID matching + * @IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED: all passive scans will be fragmented + */ +enum iwl_mvm_lmac_scan_flags { + IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL = BIT(0), + IWL_MVM_LMAC_SCAN_FLAG_PASSIVE = BIT(1), + IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION = BIT(2), + IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE = BIT(3), + IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS = BIT(4), + IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED = BIT(5), +}; + +enum iwl_scan_priority { + IWL_SCAN_PRIORITY_LOW, + IWL_SCAN_PRIORITY_MEDIUM, + IWL_SCAN_PRIORITY_HIGH, +}; + +/** + * iwl_scan_req_unified_lmac - SCAN_REQUEST_CMD_API_S_VER_1 + * @reserved1: for alignment and future use + * @channel_num: num of channels to scan + * @active-dwell: dwell time for active channels + * @passive-dwell: dwell time for passive channels + * @fragmented-dwell: dwell time for fragmented passive scan + * @reserved2: for alignment and future use + * @rx_chain_selct: PHY_RX_CHAIN_* flags + * @scan_flags: &enum iwl_mvm_lmac_scan_flags + * @max_out_time: max time (in TU) to be out of associated channel + * @suspend_time: pause scan this long (TUs) when returning to service channel + * @flags: RXON flags + * @filter_flags: RXON filter + * @tx_cmd: tx command for active scan; for 2GHz and for 5GHz + * @direct_scan: list of SSIDs for directed active scan + * @scan_prio: enum iwl_scan_priority + * @iter_num: number of scan iterations + * @delay: delay in seconds before first iteration + * @schedule: two scheduling plans. The first one is finite, the second one can + * be infinite. + * @channel_opt: channel optimization options, for full and partial scan + * @data: channel configuration and probe request packet. + */ +struct iwl_scan_req_unified_lmac { + /* SCAN_REQUEST_FIXED_PART_API_S_VER_7 */ + __le32 reserved1; + u8 n_channels; + u8 active_dwell; + u8 passive_dwell; + u8 fragmented_dwell; + __le16 reserved2; + __le16 rx_chain_select; + __le32 scan_flags; + __le32 max_out_time; + __le32 suspend_time; + /* RX_ON_FLAGS_API_S_VER_1 */ + __le32 flags; + __le32 filter_flags; + struct iwl_scan_req_tx_cmd tx_cmd[2]; + struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX]; + __le32 scan_prio; + /* SCAN_REQ_PERIODIC_PARAMS_API_S */ + __le32 iter_num; + __le32 delay; + struct iwl_scan_offload_schedule schedule[2]; + struct iwl_scan_channel_opt channel_opt[2]; + u8 data[]; +} __packed; + +/** + * struct iwl_lmac_scan_results_notif - scan results for one channel - + * SCAN_RESULT_NTF_API_S_VER_3 + * @channel: which channel the results are from + * @band: 0 for 5.2 GHz, 1 for 2.4 GHz + * @probe_status: SCAN_PROBE_STATUS_*, indicates success of probe request + * @num_probe_not_sent: # of request that weren't sent due to not enough time + * @duration: duration spent in channel, in usecs + */ +struct iwl_lmac_scan_results_notif { + u8 channel; + u8 band; + u8 probe_status; + u8 num_probe_not_sent; + __le32 duration; +} __packed; + +/** + * struct iwl_lmac_scan_complete_notif - notifies end of scanning (all channels) + * SCAN_COMPLETE_NTF_API_S_VER_3 + * @scanned_channels: number of channels scanned (and number of valid results) + * @status: one of SCAN_COMP_STATUS_* + * @bt_status: BT on/off status + * @last_channel: last channel that was scanned + * @tsf_low: TSF timer (lower half) in usecs + * @tsf_high: TSF timer (higher half) in usecs + * @results: an array of scan results, only "scanned_channels" of them are valid + */ +struct iwl_lmac_scan_complete_notif { + u8 scanned_channels; + u8 status; + u8 bt_status; + u8 last_channel; + __le32 tsf_low; + __le32 tsf_high; + struct iwl_scan_results_notif results[]; +} __packed; + +/** + * iwl_scan_offload_complete - PERIODIC_SCAN_COMPLETE_NTF_API_S_VER_2 + * @last_schedule_line: last schedule line executed (fast or regular) + * @last_schedule_iteration: last scan iteration executed before scan abort + * @status: enum iwl_scan_offload_complete_status + * @ebs_status: EBS success status &enum iwl_scan_ebs_status + * @time_after_last_iter; time in seconds elapsed after last iteration + */ +struct iwl_periodic_scan_complete { + u8 last_schedule_line; + u8 last_schedule_iteration; + u8 status; + u8 ebs_status; + __le32 time_after_last_iter; + __le32 reserved; +} __packed; + #endif diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index 3983a2beb246..6c479de3b8d4 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h @@ -135,6 +135,7 @@ enum { SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E, SCAN_OFFLOAD_CONFIG_CMD = 0x6f, MATCH_FOUND_NOTIFICATION = 0xd9, + SCAN_ITERATION_COMPLETE = 0xe7, /* Phy */ PHY_CONFIGURATION_CMD = 0x6a, diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 7dde944a68fb..522aa039a490 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -327,6 +327,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; } + if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) + hw->flags |= IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS; + hw->sta_data_size = sizeof(struct iwl_mvm_sta); hw->vif_data_size = sizeof(struct iwl_mvm_vif); hw->chanctx_data_size = sizeof(u16); @@ -1658,7 +1661,7 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); if (changes & BSS_CHANGED_IDLE && !bss_conf->idle) - iwl_mvm_sched_scan_stop(mvm, true); + iwl_mvm_scan_offload_stop(mvm, true); switch (vif->type) { case NL80211_IFTYPE_STATION: @@ -1692,7 +1695,7 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, switch (mvm->scan_status) { case IWL_MVM_SCAN_SCHED: - ret = iwl_mvm_sched_scan_stop(mvm, true); + ret = iwl_mvm_scan_offload_stop(mvm, true); if (ret) { ret = -EBUSY; goto out; @@ -1707,7 +1710,11 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN); - ret = iwl_mvm_scan_request(mvm, vif, req); + if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) + ret = iwl_mvm_unified_scan_lmac(mvm, vif, hw_req); + else + ret = iwl_mvm_scan_request(mvm, vif, req); + if (ret) iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); out: @@ -2008,15 +2015,21 @@ static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw, mvm->scan_status = IWL_MVM_SCAN_SCHED; - ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies); - if (ret) - goto err; + if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) { + ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies); + if (ret) + goto err; + } ret = iwl_mvm_config_sched_scan_profiles(mvm, req); if (ret) goto err; - ret = iwl_mvm_sched_scan_start(mvm, req); + if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) + ret = iwl_mvm_unified_sched_scan_lmac(mvm, vif, req, ies); + else + ret = iwl_mvm_sched_scan_start(mvm, req); + if (!ret) goto out; err: @@ -2035,7 +2048,7 @@ static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw, int ret; mutex_lock(&mvm->mutex); - ret = iwl_mvm_sched_scan_stop(mvm, false); + ret = iwl_mvm_scan_offload_stop(mvm, false); mutex_unlock(&mvm->mutex); iwl_mvm_wait_for_async_handlers(mvm); diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index fbe93a1df93a..927346e1a6e5 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -533,7 +533,7 @@ struct iwl_mvm { /* Scan status, cmd (pre-allocated) and auxiliary station */ enum iwl_scan_status scan_status; - struct iwl_scan_cmd *scan_cmd; + void *scan_cmd; struct iwl_mcast_filter_cmd *mcast_filter_cmd; /* rx chain antennas set through debugfs for the scan command */ @@ -868,10 +868,19 @@ int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm, struct cfg80211_sched_scan_request *req); int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, struct cfg80211_sched_scan_request *req); -int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify); -int iwl_mvm_rx_sched_scan_results(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd); +int iwl_mvm_scan_offload_stop(struct iwl_mvm *mvm, bool notify); +int iwl_mvm_rx_scan_offload_results(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); + +/* Unified scan */ +int iwl_mvm_unified_scan_lmac(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_scan_request *req); +int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_scan_ies *ies); /* MVM debugfs */ #ifdef CONFIG_IWLWIFI_DEBUGFS diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index b843870be8b7..4e2823faa5b9 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -233,7 +233,7 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, true), RX_HANDLER(SCAN_OFFLOAD_COMPLETE, iwl_mvm_rx_scan_offload_complete_notif, true), - RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_sched_scan_results, + RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_scan_offload_results, false), RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false), @@ -284,6 +284,7 @@ static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = { CMD(SCAN_OFFLOAD_ABORT_CMD), CMD(SCAN_OFFLOAD_COMPLETE), CMD(SCAN_OFFLOAD_UPDATE_PROFILES_CMD), + CMD(SCAN_ITERATION_COMPLETE), CMD(POWER_TABLE_CMD), CMD(WEP_KEY), CMD(REPLY_RX_PHY_CMD), @@ -505,10 +506,17 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, } } - scan_size = sizeof(struct iwl_scan_cmd) + - mvm->fw->ucode_capa.max_probe_length + - (mvm->fw->ucode_capa.n_scan_channels * - sizeof(struct iwl_scan_channel)); + if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) + scan_size = sizeof(struct iwl_scan_req_unified_lmac) + + sizeof(struct iwl_scan_channel_cfg_lmac) * + mvm->fw->ucode_capa.n_scan_channels + + sizeof(struct iwl_scan_probe_req); + else + scan_size = sizeof(struct iwl_scan_cmd) + + mvm->fw->ucode_capa.max_probe_length + + mvm->fw->ucode_capa.n_scan_channels * + sizeof(struct iwl_scan_channel); + mvm->scan_cmd = kmalloc(scan_size, GFP_KERNEL); if (!mvm->scan_cmd) goto out_free; diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index f2dde5604a80..919ed0e18bb0 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -97,10 +97,9 @@ static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm) return cpu_to_le16(rx_chain); } -static inline __le32 -iwl_mvm_scan_rxon_flags(struct cfg80211_scan_request *req) +static __le32 iwl_mvm_scan_rxon_flags(enum ieee80211_band band) { - if (req->channels[0]->band == IEEE80211_BAND_2GHZ) + if (band == IEEE80211_BAND_2GHZ) return cpu_to_le32(PHY_BAND_24); else return cpu_to_le32(PHY_BAND_5); @@ -130,19 +129,19 @@ iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum ieee80211_band band, * request list, is not copied here, but inserted directly to the probe * request. */ -static void iwl_mvm_scan_fill_ssids(struct iwl_scan_cmd *cmd, - struct cfg80211_scan_request *req, - int first) +static void iwl_mvm_scan_fill_ssids(struct iwl_ssid_ie *cmd_ssid, + struct cfg80211_ssid *ssids, + int n_ssids, int first) { int fw_idx, req_idx; - for (req_idx = req->n_ssids - 1, fw_idx = 0; req_idx >= first; + for (req_idx = n_ssids - 1, fw_idx = 0; req_idx >= first; req_idx--, fw_idx++) { - cmd->direct_scan[fw_idx].id = WLAN_EID_SSID; - cmd->direct_scan[fw_idx].len = req->ssids[req_idx].ssid_len; - memcpy(cmd->direct_scan[fw_idx].ssid, - req->ssids[req_idx].ssid, - req->ssids[req_idx].ssid_len); + cmd_ssid[fw_idx].id = WLAN_EID_SSID; + cmd_ssid[fw_idx].len = ssids[req_idx].ssid_len; + memcpy(cmd_ssid[fw_idx].ssid, + ssids[req_idx].ssid, + ssids[req_idx].ssid_len); } } @@ -349,7 +348,7 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm, if (params.passive_fragmented) cmd->scan_flags |= SCAN_FLAGS_FRAGMENTED_SCAN; - cmd->rxon_flags = iwl_mvm_scan_rxon_flags(req); + cmd->rxon_flags = iwl_mvm_scan_rxon_flags(req->channels[0]->band); cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP | MAC_FILTER_IN_BEACON); @@ -376,7 +375,8 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm, cmd->scan_flags &= ~SCAN_FLAGS_PASSIVE2ACTIVE; } - iwl_mvm_scan_fill_ssids(cmd, req, basic_ssid ? 1 : 0); + iwl_mvm_scan_fill_ssids(cmd->direct_scan, req->ssids, req->n_ssids, + basic_ssid ? 1 : 0); cmd->tx_cmd.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL | TX_CMD_FLG_BT_DIS); @@ -450,16 +450,27 @@ int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, return 0; } -int iwl_mvm_rx_sched_scan_results(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) +int iwl_mvm_rx_scan_offload_results(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_sched_scan_results *notif = (void *)pkt->data; + u8 client_bitmap = 0; - if (notif->client_bitmap & SCAN_CLIENT_SCHED_SCAN) { - IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n"); - ieee80211_sched_scan_results(mvm->hw); + if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) { + struct iwl_sched_scan_results *notif = (void *)pkt->data; + + client_bitmap = notif->client_bitmap; + } + + if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN || + client_bitmap & SCAN_CLIENT_SCHED_SCAN) { + if (mvm->scan_status == IWL_MVM_SCAN_SCHED) { + IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n"); + ieee80211_sched_scan_results(mvm->hw); + } else { + IWL_DEBUG_SCAN(mvm, "Scan results\n"); + } } return 0; @@ -503,7 +514,7 @@ static bool iwl_mvm_scan_abort_notif(struct iwl_notif_wait_data *notif_wait, }; } -int iwl_mvm_cancel_scan(struct iwl_mvm *mvm) +static int iwl_mvm_cancel_regular_scan(struct iwl_mvm *mvm) { struct iwl_notification_wait wait_scan_abort; static const u8 scan_abort_notif[] = { SCAN_ABORT_CMD, @@ -544,26 +555,45 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_scan_offload_complete *scan_notif = (void *)pkt->data; + u8 status, ebs_status; + if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) { + struct iwl_periodic_scan_complete *scan_notif; + + scan_notif = (void *)pkt->data; + status = scan_notif->status; + ebs_status = scan_notif->ebs_status; + } else { + struct iwl_scan_offload_complete *scan_notif; + + scan_notif = (void *)pkt->data; + status = scan_notif->status; + ebs_status = scan_notif->ebs_status; + } /* scan status must be locked for proper checking */ lockdep_assert_held(&mvm->mutex); IWL_DEBUG_SCAN(mvm, - "Scheduled scan completed, status %s EBS status %s:%d\n", - scan_notif->status == IWL_SCAN_OFFLOAD_COMPLETED ? - "completed" : "aborted", scan_notif->ebs_status == - IWL_SCAN_EBS_SUCCESS ? "success" : "failed", - scan_notif->ebs_status); + "%s completed, status %s, EBS status %s\n", + mvm->scan_status == IWL_MVM_SCAN_SCHED ? + "Scheduled scan" : "Scan", + status == IWL_SCAN_OFFLOAD_COMPLETED ? + "completed" : "aborted", + ebs_status == IWL_SCAN_EBS_SUCCESS ? + "success" : "failed"); /* only call mac80211 completion if the stop was initiated by FW */ if (mvm->scan_status == IWL_MVM_SCAN_SCHED) { mvm->scan_status = IWL_MVM_SCAN_NONE; ieee80211_sched_scan_stopped(mvm->hw); + } else if (mvm->scan_status == IWL_MVM_SCAN_OS) { + mvm->scan_status = IWL_MVM_SCAN_NONE; + ieee80211_scan_completed(mvm->hw, + status == IWL_SCAN_OFFLOAD_ABORTED); } - mvm->last_ebs_successful = !scan_notif->ebs_status; + mvm->last_ebs_successful = !ebs_status; return 0; } @@ -631,8 +661,8 @@ static int iwl_ssid_exist(u8 *ssid, u8 ssid_len, struct iwl_ssid_ie *ssid_list) } static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req, - struct iwl_scan_offload_cmd *scan, - u32 *ssid_bitmap) + struct iwl_ssid_ie *direct_scan, + u32 *ssid_bitmap, bool basic_ssid) { int i, j; int index; @@ -646,10 +676,10 @@ static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req, /* skip empty SSID matchsets */ if (!req->match_sets[i].ssid.ssid_len) continue; - scan->direct_scan[i].id = WLAN_EID_SSID; - scan->direct_scan[i].len = req->match_sets[i].ssid.ssid_len; - memcpy(scan->direct_scan[i].ssid, req->match_sets[i].ssid.ssid, - scan->direct_scan[i].len); + direct_scan[i].id = WLAN_EID_SSID; + direct_scan[i].len = req->match_sets[i].ssid.ssid_len; + memcpy(direct_scan[i].ssid, req->match_sets[i].ssid.ssid, + direct_scan[i].len); } /* add SSIDs from scan SSID list */ @@ -657,14 +687,14 @@ static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req, for (j = 0; j < req->n_ssids && i < PROBE_OPTION_MAX; j++) { index = iwl_ssid_exist(req->ssids[j].ssid, req->ssids[j].ssid_len, - scan->direct_scan); + direct_scan); if (index < 0) { - if (!req->ssids[j].ssid_len) + if (!req->ssids[j].ssid_len && basic_ssid) continue; - scan->direct_scan[i].id = WLAN_EID_SSID; - scan->direct_scan[i].len = req->ssids[j].ssid_len; - memcpy(scan->direct_scan[i].ssid, req->ssids[j].ssid, - scan->direct_scan[i].len); + direct_scan[i].id = WLAN_EID_SSID; + direct_scan[i].len = req->ssids[j].ssid_len; + memcpy(direct_scan[i].ssid, req->ssids[j].ssid, + direct_scan[i].len); *ssid_bitmap |= BIT(i + 1); i++; } else { @@ -734,6 +764,8 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, int cmd_len; int ret; u8 *probes; + bool basic_ssid = !(mvm->fw->ucode_capa.flags & + IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID); struct iwl_scan_offload_cfg *scan_cfg; struct iwl_host_cmd cmd = { @@ -758,7 +790,8 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, iwl_build_scan_cmd(mvm, vif, req, &scan_cfg->scan_cmd, ¶ms); scan_cfg->scan_cmd.len = cpu_to_le16(cmd_len); - iwl_scan_offload_build_ssid(req, &scan_cfg->scan_cmd, &ssid_bitmap); + iwl_scan_offload_build_ssid(req, scan_cfg->scan_cmd.direct_scan, + &ssid_bitmap, basic_ssid); /* build tx frames for supported bands */ if (band_2ghz) { iwl_scan_offload_build_tx_cmd(mvm, vif, ies, @@ -893,7 +926,7 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, sizeof(scan_req), &scan_req); } -static int iwl_mvm_send_sched_scan_abort(struct iwl_mvm *mvm) +static int iwl_mvm_send_scan_offload_abort(struct iwl_mvm *mvm) { int ret; struct iwl_host_cmd cmd = { @@ -904,7 +937,9 @@ static int iwl_mvm_send_sched_scan_abort(struct iwl_mvm *mvm) /* Exit instantly with error when device is not ready * to receive scan abort command or it does not perform * scheduled scan currently */ - if (mvm->scan_status != IWL_MVM_SCAN_SCHED) + if (mvm->scan_status != IWL_MVM_SCAN_SCHED && + (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) || + mvm->scan_status != IWL_MVM_SCAN_OS)) return -EIO; ret = iwl_mvm_send_cmd_status(mvm, &cmd, &status); @@ -926,16 +961,19 @@ static int iwl_mvm_send_sched_scan_abort(struct iwl_mvm *mvm) return ret; } -int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify) +int iwl_mvm_scan_offload_stop(struct iwl_mvm *mvm, bool notify) { int ret; struct iwl_notification_wait wait_scan_done; static const u8 scan_done_notif[] = { SCAN_OFFLOAD_COMPLETE, }; + bool sched = mvm->scan_status == IWL_MVM_SCAN_SCHED; lockdep_assert_held(&mvm->mutex); - if (mvm->scan_status != IWL_MVM_SCAN_SCHED) { - IWL_DEBUG_SCAN(mvm, "No offloaded scan to stop\n"); + if (mvm->scan_status != IWL_MVM_SCAN_SCHED && + (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) || + mvm->scan_status != IWL_MVM_SCAN_OS)) { + IWL_DEBUG_SCAN(mvm, "No scan to stop\n"); return 0; } @@ -944,14 +982,16 @@ int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify) ARRAY_SIZE(scan_done_notif), NULL, NULL); - ret = iwl_mvm_send_sched_scan_abort(mvm); + ret = iwl_mvm_send_scan_offload_abort(mvm); if (ret) { - IWL_DEBUG_SCAN(mvm, "Send stop offload scan failed %d\n", ret); + IWL_DEBUG_SCAN(mvm, "Send stop %sscan failed %d\n", + sched ? "offloaded " : "", ret); iwl_remove_notification(&mvm->notif_wait, &wait_scan_done); return ret; } - IWL_DEBUG_SCAN(mvm, "Successfully sent stop offload scan\n"); + IWL_DEBUG_SCAN(mvm, "Successfully sent stop %sscan\n", + sched ? "offloaded " : ""); ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_done, 1 * HZ); if (ret) @@ -964,8 +1004,311 @@ int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify) */ mvm->scan_status = IWL_MVM_SCAN_NONE; - if (notify) - ieee80211_sched_scan_stopped(mvm->hw); + if (notify) { + if (sched) + ieee80211_sched_scan_stopped(mvm->hw); + else + ieee80211_scan_completed(mvm->hw, true); + } return 0; } + +static void iwl_mvm_unified_scan_fill_tx_cmd(struct iwl_mvm *mvm, + struct iwl_scan_req_tx_cmd *tx_cmd, + bool no_cck) +{ + tx_cmd[0].tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL | + TX_CMD_FLG_BT_DIS); + tx_cmd[0].rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm, + IEEE80211_BAND_2GHZ, + no_cck); + tx_cmd[0].sta_id = mvm->aux_sta.sta_id; + + tx_cmd[1].tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL | + TX_CMD_FLG_BT_DIS); + tx_cmd[1].rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm, + IEEE80211_BAND_5GHZ, + no_cck); + tx_cmd[1].sta_id = mvm->aux_sta.sta_id; +} + +static void +iwl_mvm_lmac_scan_cfg_channels(struct iwl_mvm *mvm, + struct ieee80211_channel **channels, + int n_channels, u32 ssid_bitmap, + struct iwl_scan_req_unified_lmac *cmd) +{ + struct iwl_scan_channel_cfg_lmac *channel_cfg = (void *)&cmd->data; + int i; + + for (i = 0; i < n_channels; i++) { + channel_cfg[i].channel_num = + cpu_to_le16(channels[i]->hw_value); + channel_cfg[i].iter_count = cpu_to_le16(1); + channel_cfg[i].iter_interval = 0; + channel_cfg[i].flags = + cpu_to_le32(IWL_UNIFIED_SCAN_CHANNEL_PARTIAL | + ssid_bitmap); + } +} + +static void +iwl_mvm_build_unified_scan_probe(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct ieee80211_scan_ies *ies, + struct iwl_scan_req_unified_lmac *cmd) +{ + struct iwl_scan_probe_req *preq = (void *)(cmd->data + + sizeof(struct iwl_scan_channel_cfg_lmac) * + mvm->fw->ucode_capa.n_scan_channels); + struct ieee80211_mgmt *frame = (struct ieee80211_mgmt *)preq->buf; + u8 *pos; + + frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); + eth_broadcast_addr(frame->da); + memcpy(frame->sa, vif->addr, ETH_ALEN); + eth_broadcast_addr(frame->bssid); + frame->seq_ctrl = 0; + + pos = frame->u.probe_req.variable; + *pos++ = WLAN_EID_SSID; + *pos++ = 0; + + preq->mac_header.offset = 0; + preq->mac_header.len = cpu_to_le16(24 + 2); + + memcpy(pos, ies->ies[IEEE80211_BAND_2GHZ], + ies->len[IEEE80211_BAND_2GHZ]); + preq->band_data[0].offset = cpu_to_le16(pos - preq->buf); + preq->band_data[0].len = cpu_to_le16(ies->len[IEEE80211_BAND_2GHZ]); + pos += ies->len[IEEE80211_BAND_2GHZ]; + + memcpy(pos, ies->ies[IEEE80211_BAND_5GHZ], + ies->len[IEEE80211_BAND_5GHZ]); + preq->band_data[1].offset = cpu_to_le16(pos - preq->buf); + preq->band_data[1].len = cpu_to_le16(ies->len[IEEE80211_BAND_5GHZ]); + pos += ies->len[IEEE80211_BAND_5GHZ]; + + memcpy(pos, ies->common_ies, ies->common_ie_len); + preq->common_data.offset = cpu_to_le16(pos - preq->buf); + preq->common_data.len = cpu_to_le16(ies->common_ie_len); +} + +static void +iwl_mvm_build_generic_unified_scan_cmd(struct iwl_mvm *mvm, + struct iwl_scan_req_unified_lmac *cmd, + struct iwl_mvm_scan_params *params) +{ + cmd->active_dwell = (u8)params->dwell[IEEE80211_BAND_2GHZ].active; + cmd->passive_dwell = (u8)params->dwell[IEEE80211_BAND_2GHZ].passive; + /* TODO: Use params; now fragmented isn't used. */ + cmd->fragmented_dwell = 0; + cmd->rx_chain_select = iwl_mvm_scan_rx_chain(mvm); + cmd->max_out_time = cpu_to_le32(params->max_out_time); + cmd->suspend_time = cpu_to_le32(params->suspend_time); + cmd->scan_prio = cpu_to_le32(IWL_SCAN_PRIORITY_HIGH); + cmd->channel_opt[0].flags = mvm->last_ebs_successful ? + cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS & + IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE & + IWL_SCAN_CHANNEL_FLAG_CACHE_ADD) : + 0; + cmd->channel_opt[0].non_ebs_ratio = 0; + cmd->iter_num = cpu_to_le32(1); + cmd->delay = 0; +} + +int iwl_mvm_unified_scan_lmac(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_scan_request *req) +{ + struct iwl_host_cmd hcmd = { + .id = SCAN_OFFLOAD_REQUEST_CMD, + .len = { sizeof(struct iwl_scan_req_unified_lmac) + + sizeof(struct iwl_scan_channel_cfg_lmac) * + mvm->fw->ucode_capa.n_scan_channels + + sizeof(struct iwl_scan_probe_req), }, + .data = { mvm->scan_cmd, }, + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, + }; + struct iwl_scan_req_unified_lmac *cmd = mvm->scan_cmd; + struct iwl_mvm_scan_params params = {}; + u32 flags; + int ssid_bitmap = 0; + int ret, i; + + lockdep_assert_held(&mvm->mutex); + + /* we should have failed registration if scan_cmd was NULL */ + if (WARN_ON(mvm->scan_cmd == NULL)) + return -ENOMEM; + + if (WARN_ON_ONCE(req->req.n_ssids > PROBE_OPTION_MAX || + req->ies.common_ie_len + req->ies.len[0] + + req->ies.len[1] + 24 + 2 > + SCAN_OFFLOAD_PROBE_REQ_SIZE || + req->req.n_channels > + mvm->fw->ucode_capa.n_scan_channels)) + return -1; + + mvm->scan_status = IWL_MVM_SCAN_OS; + + iwl_mvm_scan_calc_params(mvm, vif, req->req.n_ssids, req->req.flags, + ¶ms); + + iwl_mvm_build_generic_unified_scan_cmd(mvm, cmd, ¶ms); + + cmd->n_channels = (u8)req->req.n_channels; + + flags = IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL; + + if (req->req.n_ssids == 1 && req->req.ssids[0].ssid_len != 0) + flags |= IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION; + + if (params.passive_fragmented) + flags |= IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED; + + if (req->req.n_ssids == 0) + flags |= IWL_MVM_LMAC_SCAN_FLAG_PASSIVE; + + cmd->scan_flags = cpu_to_le32(flags); + + cmd->flags = iwl_mvm_scan_rxon_flags(req->req.channels[0]->band); + cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP | + MAC_FILTER_IN_BEACON); + iwl_mvm_unified_scan_fill_tx_cmd(mvm, cmd->tx_cmd, req->req.no_cck); + iwl_mvm_scan_fill_ssids(cmd->direct_scan, req->req.ssids, + req->req.n_ssids, 0); + + cmd->schedule[0].delay = 0; + cmd->schedule[0].iterations = 1; + cmd->schedule[0].full_scan_mul = 0; + cmd->schedule[1].delay = 0; + cmd->schedule[1].iterations = 0; + cmd->schedule[1].full_scan_mul = 0; + + for (i = 1; i <= req->req.n_ssids; i++) + ssid_bitmap |= BIT(i); + + iwl_mvm_lmac_scan_cfg_channels(mvm, req->req.channels, + req->req.n_channels, ssid_bitmap, + cmd); + + iwl_mvm_build_unified_scan_probe(mvm, vif, &req->ies, cmd); + + ret = iwl_mvm_send_cmd(mvm, &hcmd); + if (!ret) { + IWL_DEBUG_SCAN(mvm, "Scan request was sent successfully\n"); + } else { + /* + * If the scan failed, it usually means that the FW was unable + * to allocate the time events. Warn on it, but maybe we + * should try to send the command again with different params. + */ + IWL_ERR(mvm, "Scan failed! ret %d\n", ret); + mvm->scan_status = IWL_MVM_SCAN_NONE; + ret = -EIO; + } + return ret; +} + +int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_scan_ies *ies) +{ + struct iwl_host_cmd hcmd = { + .id = SCAN_OFFLOAD_REQUEST_CMD, + .len = { sizeof(struct iwl_scan_req_unified_lmac) + + sizeof(struct iwl_scan_channel_cfg_lmac) * + mvm->fw->ucode_capa.n_scan_channels + + sizeof(struct iwl_scan_probe_req), }, + .data = { mvm->scan_cmd, }, + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, + }; + struct iwl_scan_req_unified_lmac *cmd = mvm->scan_cmd; + struct iwl_mvm_scan_params params = {}; + int ret; + u32 flags = 0, ssid_bitmap = 0; + + lockdep_assert_held(&mvm->mutex); + + /* we should have failed registration if scan_cmd was NULL */ + if (WARN_ON(mvm->scan_cmd == NULL)) + return -ENOMEM; + + if (WARN_ON_ONCE(req->n_ssids > PROBE_OPTION_MAX || + ies->common_ie_len + ies->len[0] + ies->len[1] + 24 + 2 + > SCAN_OFFLOAD_PROBE_REQ_SIZE || + req->n_channels > mvm->fw->ucode_capa.n_scan_channels)) + return -ENOBUFS; + + iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, 0, ¶ms); + + iwl_mvm_build_generic_unified_scan_cmd(mvm, cmd, ¶ms); + + cmd->n_channels = (u8)req->n_channels; + + if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) { + IWL_DEBUG_SCAN(mvm, + "Sending scheduled scan with filtering, n_match_sets %d\n", + req->n_match_sets); + } else { + IWL_DEBUG_SCAN(mvm, + "Sending Scheduled scan without filtering\n"); + flags |= IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL; + } + + if (req->n_ssids == 1 && req->ssids[0].ssid_len != 0) + flags |= IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION; + + if (params.passive_fragmented) + flags |= IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED; + + if (req->n_ssids == 0) + flags |= IWL_MVM_LMAC_SCAN_FLAG_PASSIVE; + + cmd->scan_flags = cpu_to_le32(flags); + + cmd->flags = iwl_mvm_scan_rxon_flags(req->channels[0]->band); + cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP | + MAC_FILTER_IN_BEACON); + iwl_mvm_unified_scan_fill_tx_cmd(mvm, cmd->tx_cmd, false); + iwl_scan_offload_build_ssid(req, cmd->direct_scan, &ssid_bitmap, false); + + cmd->schedule[0].delay = req->interval / MSEC_PER_SEC; + cmd->schedule[0].iterations = IWL_FAST_SCHED_SCAN_ITERATIONS; + cmd->schedule[0].full_scan_mul = 1; + + cmd->schedule[1].delay = req->interval / MSEC_PER_SEC; + cmd->schedule[1].iterations = 0xff; + cmd->schedule[1].full_scan_mul = IWL_FULL_SCAN_MULTIPLIER; + + iwl_mvm_lmac_scan_cfg_channels(mvm, req->channels, req->n_channels, + ssid_bitmap, cmd); + + iwl_mvm_build_unified_scan_probe(mvm, vif, ies, cmd); + + ret = iwl_mvm_send_cmd(mvm, &hcmd); + if (!ret) { + IWL_DEBUG_SCAN(mvm, + "Sched scan request was sent successfully\n"); + } else { + /* + * If the scan failed, it usually means that the FW was unable + * to allocate the time events. Warn on it, but maybe we + * should try to send the command again with different params. + */ + IWL_ERR(mvm, "Sched scan failed! ret %d\n", ret); + mvm->scan_status = IWL_MVM_SCAN_NONE; + ret = -EIO; + } + return ret; +} + + +int iwl_mvm_cancel_scan(struct iwl_mvm *mvm) +{ + if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) + return iwl_mvm_scan_offload_stop(mvm, true); + return iwl_mvm_cancel_regular_scan(mvm); +} From af91344c23fd23be44bc6e3dfb2c7b47bb9e6e11 Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Thu, 12 Jun 2014 19:29:40 +0300 Subject: [PATCH 250/274] iwlwifi: mvm: init lmac scan command Initialize LMAC scan command. Fix EBS flag to be dependant on TLV flg and fix other bugs. Signed-off-by: David Spinadel Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/scan.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 919ed0e18bb0..14fc6adc963a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -1099,6 +1099,7 @@ iwl_mvm_build_generic_unified_scan_cmd(struct iwl_mvm *mvm, struct iwl_scan_req_unified_lmac *cmd, struct iwl_mvm_scan_params *params) { + memset(cmd, 0, ksize(cmd)); cmd->active_dwell = (u8)params->dwell[IEEE80211_BAND_2GHZ].active; cmd->passive_dwell = (u8)params->dwell[IEEE80211_BAND_2GHZ].passive; /* TODO: Use params; now fragmented isn't used. */ @@ -1107,14 +1108,19 @@ iwl_mvm_build_generic_unified_scan_cmd(struct iwl_mvm *mvm, cmd->max_out_time = cpu_to_le32(params->max_out_time); cmd->suspend_time = cpu_to_le32(params->suspend_time); cmd->scan_prio = cpu_to_le32(IWL_SCAN_PRIORITY_HIGH); - cmd->channel_opt[0].flags = mvm->last_ebs_successful ? - cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS & - IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE & - IWL_SCAN_CHANNEL_FLAG_CACHE_ADD) : - 0; - cmd->channel_opt[0].non_ebs_ratio = 0; cmd->iter_num = cpu_to_le32(1); - cmd->delay = 0; + + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT && + mvm->last_ebs_successful) { + cmd->channel_opt[0].flags = + cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS | + IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | + IWL_SCAN_CHANNEL_FLAG_CACHE_ADD); + cmd->channel_opt[1].flags = + cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS | + IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | + IWL_SCAN_CHANNEL_FLAG_CACHE_ADD); + } } int iwl_mvm_unified_scan_lmac(struct iwl_mvm *mvm, From b14fc2befb3bdd01d05d2e399b05c9c8ead6a5ca Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Wed, 25 Jun 2014 13:17:53 +0300 Subject: [PATCH 251/274] iwlwifi: mvm: fix endianity in scan command Signed-off-by: David Spinadel Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h | 2 +- drivers/net/wireless/iwlwifi/mvm/scan.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h index 48a1d8f5675d..c02a9e45ec5e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h @@ -509,7 +509,7 @@ struct iwl_scan_offload_profile_cfg { * @full_scan_mul: number of partial scans before each full scan */ struct iwl_scan_offload_schedule { - u16 delay; + __le16 delay; u8 iterations; u8 full_scan_mul; } __packed; diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 14fc6adc963a..004b1f5d0314 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -899,11 +899,11 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, .watchdog = IWL_SCHED_SCAN_WATCHDOG, .schedule_line[0].iterations = IWL_FAST_SCHED_SCAN_ITERATIONS, - .schedule_line[0].delay = req->interval / 1000, + .schedule_line[0].delay = cpu_to_le16(req->interval / 1000), .schedule_line[0].full_scan_mul = 1, .schedule_line[1].iterations = 0xff, - .schedule_line[1].delay = req->interval / 1000, + .schedule_line[1].delay = cpu_to_le16(req->interval / 1000), .schedule_line[1].full_scan_mul = IWL_FULL_SCAN_MULTIPLIER, }; @@ -1281,11 +1281,11 @@ int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm, iwl_mvm_unified_scan_fill_tx_cmd(mvm, cmd->tx_cmd, false); iwl_scan_offload_build_ssid(req, cmd->direct_scan, &ssid_bitmap, false); - cmd->schedule[0].delay = req->interval / MSEC_PER_SEC; + cmd->schedule[0].delay = cpu_to_le16(req->interval / MSEC_PER_SEC); cmd->schedule[0].iterations = IWL_FAST_SCHED_SCAN_ITERATIONS; cmd->schedule[0].full_scan_mul = 1; - cmd->schedule[1].delay = req->interval / MSEC_PER_SEC; + cmd->schedule[1].delay = cpu_to_le16(req->interval / MSEC_PER_SEC); cmd->schedule[1].iterations = 0xff; cmd->schedule[1].full_scan_mul = IWL_FULL_SCAN_MULTIPLIER; From ca55eb47822560e56b126eeb05b3cd0b8415acfd Mon Sep 17 00:00:00 2001 From: Eran Harary Date: Sun, 29 Jun 2014 11:53:06 +0300 Subject: [PATCH 252/274] iwlwifi: 8000: drop a print when the address is invalid when driver takes the MAC address from the HW section and it isn't valid - print an error. Signed-off-by: Eran Harary Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-nvm-parse.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index dd76ed28884d..018af2957d3b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -549,6 +549,9 @@ static void iwl_set_hw_address_family_8000(struct device *dev, data->hw_addr[1] = hw_addr[2]; data->hw_addr[0] = hw_addr[3]; } + if (!is_valid_ether_addr(data->hw_addr)) + IWL_ERR_DEV(dev, + "mac address from hw section is not valid\n"); return; } From 63cbe180f8bfcf355516b09849c71fed19e035b3 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 13 May 2014 22:28:35 +0300 Subject: [PATCH 253/274] iwlwifi: mvm: let iwl_mvm_update_quotas disregard a disabled vif In some cases (e.g. when we're doing a channel switch), we may need to disable the quota of a vif temporarily. In order to do so, add an argument to the iwl_mvm_update_quotas() function to tell if the passed vif is a new one or if it should be disregarded. Signed-off-by: Luciano Coelho Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 28 +++++++++++++-------- drivers/net/wireless/iwlwifi/mvm/mvm.h | 24 +++++++++++++++++- drivers/net/wireless/iwlwifi/mvm/quota.c | 26 +++++++++++++------ drivers/net/wireless/iwlwifi/mvm/utils.c | 3 ++- 4 files changed, 61 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 522aa039a490..71353bd03a70 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -834,7 +834,8 @@ static void iwl_mvm_mac_restart_complete(struct ieee80211_hw *hw) clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); iwl_mvm_d0i3_enable_tx(mvm, NULL); - ret = iwl_mvm_update_quotas(mvm, NULL); + ret = iwl_mvm_update_quotas(mvm, NULL, + IWL_MVM_QUOTA_UPDATE_TYPE_REGULAR); if (ret) IWL_ERR(mvm, "Failed to update quotas after restart (%d)\n", ret); @@ -1421,7 +1422,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, if (changes & BSS_CHANGED_ASSOC) { if (bss_conf->assoc) { /* add quota for this interface */ - ret = iwl_mvm_update_quotas(mvm, vif); + ret = iwl_mvm_update_quotas(mvm, vif, + IWL_MVM_QUOTA_UPDATE_TYPE_NEW); if (ret) { IWL_ERR(mvm, "failed to update quotas\n"); return; @@ -1469,7 +1471,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; /* remove quota for this interface */ - ret = iwl_mvm_update_quotas(mvm, NULL); + ret = iwl_mvm_update_quotas(mvm, NULL, + IWL_MVM_QUOTA_UPDATE_TYPE_REGULAR); if (ret) IWL_ERR(mvm, "failed to update quotas\n"); @@ -1568,7 +1571,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, /* power updated needs to be done before quotas */ iwl_mvm_power_update_mac(mvm); - ret = iwl_mvm_update_quotas(mvm, vif); + ret = iwl_mvm_update_quotas(mvm, vif, IWL_MVM_QUOTA_UPDATE_TYPE_NEW); if (ret) goto out_quota_failed; @@ -1617,7 +1620,7 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, if (vif->p2p && mvm->p2p_device_vif) iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false); - iwl_mvm_update_quotas(mvm, NULL); + iwl_mvm_update_quotas(mvm, NULL, IWL_MVM_QUOTA_UPDATE_TYPE_REGULAR); iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta); iwl_mvm_binding_remove_vif(mvm, vif); @@ -2403,14 +2406,15 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, */ if (vif->type == NL80211_IFTYPE_MONITOR) { mvmvif->monitor_active = true; - ret = iwl_mvm_update_quotas(mvm, vif); + ret = iwl_mvm_update_quotas(mvm, vif, + IWL_MVM_QUOTA_UPDATE_TYPE_NEW); if (ret) goto out_remove_binding; } /* Handle binding during CSA */ if (vif->type == NL80211_IFTYPE_AP) { - iwl_mvm_update_quotas(mvm, vif); + iwl_mvm_update_quotas(mvm, vif, IWL_MVM_QUOTA_UPDATE_TYPE_NEW); iwl_mvm_mac_ctxt_changed(mvm, vif, false); } @@ -2442,7 +2446,8 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, goto out_unlock; case NL80211_IFTYPE_MONITOR: mvmvif->monitor_active = false; - iwl_mvm_update_quotas(mvm, NULL); + iwl_mvm_update_quotas(mvm, NULL, + IWL_MVM_QUOTA_UPDATE_TYPE_REGULAR); break; case NL80211_IFTYPE_AP: /* This part is triggered only during CSA */ @@ -2450,8 +2455,8 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, goto out_unlock; mvmvif->ap_ibss_active = false; - iwl_mvm_update_quotas(mvm, NULL); - /*TODO: bt_coex notification here? */ + iwl_mvm_update_quotas(mvm, NULL, + IWL_MVM_QUOTA_UPDATE_TYPE_REGULAR); default: break; } @@ -2515,7 +2520,8 @@ static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm, mvm->noa_duration = noa_duration; mvm->noa_vif = vif; - return iwl_mvm_update_quotas(mvm, NULL); + return iwl_mvm_update_quotas(mvm, NULL, + IWL_MVM_QUOTA_UPDATE_TYPE_REGULAR); case IWL_MVM_TM_CMD_SET_BEACON_FILTER: /* must be associated client vif - ignore authorized */ if (!vif || vif->type != NL80211_IFTYPE_STATION || diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 927346e1a6e5..55e42f4d6cce 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -844,7 +844,29 @@ int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); /* Quota management */ -int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif); + +/** + * enum iwl_mvm_quota_update_type - quota update type + + * @IWL_MVM_QUOTA_UPDATE_TYPE_REGULAR: regular quota update, use the + * existing vifs (ie. no new vif nor a disabled vif is passed). + * @IWL_MVM_QUOTA_UPDATE_TYPE_NEW: a new vif is being added to the + * quota calculation and needs to be treated differently. + * @IWL_MVM_QUOTA_UPDATE_TYPE_DISABLED: temporarily disable a vif from + * the quota calculation. The mvm mutex must remain held for the + * entire time during which the vif is to remain disabled, + * otherwise there is no guarantee that another code flow will + * not reenable it accidentally (by updating the quotas without + * marking the vif as disabled). + */ +enum iwl_mvm_quota_update_type { + IWL_MVM_QUOTA_UPDATE_TYPE_REGULAR, + IWL_MVM_QUOTA_UPDATE_TYPE_NEW, + IWL_MVM_QUOTA_UPDATE_TYPE_DISABLED, +}; + +int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + enum iwl_mvm_quota_update_type type); /* Scanning */ int iwl_mvm_scan_request(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c index ba68d7b84505..75d2c95b4076 100644 --- a/drivers/net/wireless/iwlwifi/mvm/quota.c +++ b/drivers/net/wireless/iwlwifi/mvm/quota.c @@ -73,7 +73,8 @@ struct iwl_mvm_quota_iterator_data { int colors[MAX_BINDINGS]; int low_latency[MAX_BINDINGS]; int n_low_latency_bindings; - struct ieee80211_vif *new_vif; + struct ieee80211_vif *vif; + enum iwl_mvm_quota_update_type type; }; static void iwl_mvm_quota_iterator(void *_data, u8 *mac, @@ -89,7 +90,7 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac, * the add_interface callback (otherwise it won't show * up in iteration) */ - if (vif == data->new_vif) + if (data->type == IWL_MVM_QUOTA_UPDATE_TYPE_NEW && vif == data->vif) return; if (!mvmvif->phy_ctxt) @@ -109,6 +110,10 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac, else WARN_ON_ONCE(data->colors[id] != mvmvif->phy_ctxt->color); + if (data->type == IWL_MVM_QUOTA_UPDATE_TYPE_DISABLED && + vif == data->vif) + return; + switch (vif->type) { case NL80211_IFTYPE_STATION: if (vif->bss_conf.assoc) @@ -171,14 +176,16 @@ static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm, #endif } -int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif) +int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + enum iwl_mvm_quota_update_type type) { struct iwl_time_quota_cmd cmd = {}; int i, idx, ret, num_active_macs, quota, quota_rem, n_non_lowlat; struct iwl_mvm_quota_iterator_data data = { .n_interfaces = {}, .colors = { -1, -1, -1, -1 }, - .new_vif = newvif, + .vif = vif, + .type = type, }; lockdep_assert_held(&mvm->mutex); @@ -190,12 +197,17 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif) /* iterator data above must match */ BUILD_BUG_ON(MAX_BINDINGS != 4); + if (WARN_ON_ONCE((type != IWL_MVM_QUOTA_UPDATE_TYPE_REGULAR && !vif) || + (type == IWL_MVM_QUOTA_UPDATE_TYPE_REGULAR && vif))) + return -EINVAL; + ieee80211_iterate_active_interfaces_atomic( mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_quota_iterator, &data); - if (newvif) { - data.new_vif = NULL; - iwl_mvm_quota_iterator(&data, newvif->addr, newvif); + if (type == IWL_MVM_QUOTA_UPDATE_TYPE_NEW) { + data.vif = NULL; + data.type = IWL_MVM_QUOTA_UPDATE_TYPE_REGULAR; + iwl_mvm_quota_iterator(&data, vif->addr, vif); } /* diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index ac249da8a22b..98ff64961896 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -631,7 +631,8 @@ int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif, mvmvif->low_latency = value; - res = iwl_mvm_update_quotas(mvm, NULL); + res = iwl_mvm_update_quotas(mvm, NULL, + IWL_MVM_QUOTA_UPDATE_TYPE_REGULAR); if (res) return res; From 63faceb60b8e026d62db228ff04038e8089fa74a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 23 May 2014 16:15:11 +0200 Subject: [PATCH 254/274] iwlwifi: mvm: don't send zero quota to the firmware There are some cases where we can currently send zero quota for a valid binding, e.g. if we update while an interface is bound to a channel context but not yet acting as an AP. Avoid this by reordering the checks. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/quota.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c index 75d2c95b4076..8bb2fced0811 100644 --- a/drivers/net/wireless/iwlwifi/mvm/quota.c +++ b/drivers/net/wireless/iwlwifi/mvm/quota.c @@ -105,11 +105,6 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac, if (WARN_ON_ONCE(id >= MAX_BINDINGS)) return; - if (data->colors[id] < 0) - data->colors[id] = mvmvif->phy_ctxt->color; - else - WARN_ON_ONCE(data->colors[id] != mvmvif->phy_ctxt->color); - if (data->type == IWL_MVM_QUOTA_UPDATE_TYPE_DISABLED && vif == data->vif) return; @@ -135,6 +130,11 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac, return; } + if (data->colors[id] < 0) + data->colors[id] = mvmvif->phy_ctxt->color; + else + WARN_ON_ONCE(data->colors[id] != mvmvif->phy_ctxt->color); + data->n_interfaces[id]++; if (iwl_mvm_vif_low_latency(mvmvif) && !data->low_latency[id]) { From 99a1230d9d1cb5d52bb37b8c03f3931502f0e44d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 23 May 2014 16:13:57 +0200 Subject: [PATCH 255/274] iwlwifi: mvm: validate that we don't send zero quota The firmware currently deals with zero quota for a given binding, but it seems odd to send that down. Make sure that we don't do that. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/quota.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c index 8bb2fced0811..3f18786311f4 100644 --- a/drivers/net/wireless/iwlwifi/mvm/quota.c +++ b/drivers/net/wireless/iwlwifi/mvm/quota.c @@ -297,6 +297,14 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *vif, iwl_mvm_adjust_quota_for_noa(mvm, &cmd); + /* check that we have non-zero quota for all valid bindings */ + for (i = 0; i < MAX_BINDINGS; i++) { + if (cmd.quotas[i].id_and_color == cpu_to_le32(FW_CTXT_INVALID)) + continue; + WARN_ONCE(cmd.quotas[i].quota == 0, + "zero quota on binding %d\n", i); + } + ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0, sizeof(cmd), &cmd); if (ret) From 494983e0e7ae24de7ad3eaa2edc1e31a6a0d7502 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 23 May 2014 16:17:13 +0200 Subject: [PATCH 256/274] iwlwifi: mvm: don't pass update type to quota iterator Simplify the quota iterator by not passing the update type, it only needs to know whether or not to skip an interface. Signed-off-by: Johannes Berg Reviewed-by: Luciano Coelho Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/quota.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c index 3f18786311f4..6de0c6352943 100644 --- a/drivers/net/wireless/iwlwifi/mvm/quota.c +++ b/drivers/net/wireless/iwlwifi/mvm/quota.c @@ -73,8 +73,7 @@ struct iwl_mvm_quota_iterator_data { int colors[MAX_BINDINGS]; int low_latency[MAX_BINDINGS]; int n_low_latency_bindings; - struct ieee80211_vif *vif; - enum iwl_mvm_quota_update_type type; + struct ieee80211_vif *skip_vif; }; static void iwl_mvm_quota_iterator(void *_data, u8 *mac, @@ -89,8 +88,9 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac, * skip it here in case we're not called from within * the add_interface callback (otherwise it won't show * up in iteration) + * Also skip disabled interfaces here immediately. */ - if (data->type == IWL_MVM_QUOTA_UPDATE_TYPE_NEW && vif == data->vif) + if (vif == data->skip_vif) return; if (!mvmvif->phy_ctxt) @@ -105,10 +105,6 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac, if (WARN_ON_ONCE(id >= MAX_BINDINGS)) return; - if (data->type == IWL_MVM_QUOTA_UPDATE_TYPE_DISABLED && - vif == data->vif) - return; - switch (vif->type) { case NL80211_IFTYPE_STATION: if (vif->bss_conf.assoc) @@ -184,8 +180,7 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mvm_quota_iterator_data data = { .n_interfaces = {}, .colors = { -1, -1, -1, -1 }, - .vif = vif, - .type = type, + .skip_vif = vif, }; lockdep_assert_held(&mvm->mutex); @@ -205,8 +200,7 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *vif, mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_quota_iterator, &data); if (type == IWL_MVM_QUOTA_UPDATE_TYPE_NEW) { - data.vif = NULL; - data.type = IWL_MVM_QUOTA_UPDATE_TYPE_REGULAR; + data.skip_vif = NULL; iwl_mvm_quota_iterator(&data, vif->addr, vif); } From 0166230c6c696d501a2d2616a30e295b71e39d43 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 6 Jun 2014 15:18:45 +0200 Subject: [PATCH 257/274] iwlwifi: mvm: remove update type argument from quota update It turns out that adding the update type argument was pointless as quota update is never called from the add_interface() callback. Therefore, IWL_MVM_QUOTA_UPDATE_TYPE_NEW isn't actually needed and then only a "disabled_vif" argument is needed for the upcoming CSA work. Remove the whole enum iwl_mvm_quota_update_type and pass the right arguments (always NULL for disabled vif right now) to the function in all current call sites. Signed-off-by: Johannes Berg Reviewed-by: Luciano Coelho Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 27 ++++++++------------- drivers/net/wireless/iwlwifi/mvm/mvm.h | 25 ++----------------- drivers/net/wireless/iwlwifi/mvm/quota.c | 26 +++++--------------- drivers/net/wireless/iwlwifi/mvm/utils.c | 3 +-- 4 files changed, 19 insertions(+), 62 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 71353bd03a70..72f82a33856c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -834,8 +834,7 @@ static void iwl_mvm_mac_restart_complete(struct ieee80211_hw *hw) clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); iwl_mvm_d0i3_enable_tx(mvm, NULL); - ret = iwl_mvm_update_quotas(mvm, NULL, - IWL_MVM_QUOTA_UPDATE_TYPE_REGULAR); + ret = iwl_mvm_update_quotas(mvm, NULL); if (ret) IWL_ERR(mvm, "Failed to update quotas after restart (%d)\n", ret); @@ -1422,8 +1421,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, if (changes & BSS_CHANGED_ASSOC) { if (bss_conf->assoc) { /* add quota for this interface */ - ret = iwl_mvm_update_quotas(mvm, vif, - IWL_MVM_QUOTA_UPDATE_TYPE_NEW); + ret = iwl_mvm_update_quotas(mvm, NULL); if (ret) { IWL_ERR(mvm, "failed to update quotas\n"); return; @@ -1471,8 +1469,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; /* remove quota for this interface */ - ret = iwl_mvm_update_quotas(mvm, NULL, - IWL_MVM_QUOTA_UPDATE_TYPE_REGULAR); + ret = iwl_mvm_update_quotas(mvm, NULL); if (ret) IWL_ERR(mvm, "failed to update quotas\n"); @@ -1571,7 +1568,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, /* power updated needs to be done before quotas */ iwl_mvm_power_update_mac(mvm); - ret = iwl_mvm_update_quotas(mvm, vif, IWL_MVM_QUOTA_UPDATE_TYPE_NEW); + ret = iwl_mvm_update_quotas(mvm, NULL); if (ret) goto out_quota_failed; @@ -1620,7 +1617,7 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, if (vif->p2p && mvm->p2p_device_vif) iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false); - iwl_mvm_update_quotas(mvm, NULL, IWL_MVM_QUOTA_UPDATE_TYPE_REGULAR); + iwl_mvm_update_quotas(mvm, NULL); iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta); iwl_mvm_binding_remove_vif(mvm, vif); @@ -2406,15 +2403,14 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, */ if (vif->type == NL80211_IFTYPE_MONITOR) { mvmvif->monitor_active = true; - ret = iwl_mvm_update_quotas(mvm, vif, - IWL_MVM_QUOTA_UPDATE_TYPE_NEW); + ret = iwl_mvm_update_quotas(mvm, NULL); if (ret) goto out_remove_binding; } /* Handle binding during CSA */ if (vif->type == NL80211_IFTYPE_AP) { - iwl_mvm_update_quotas(mvm, vif, IWL_MVM_QUOTA_UPDATE_TYPE_NEW); + iwl_mvm_update_quotas(mvm, NULL); iwl_mvm_mac_ctxt_changed(mvm, vif, false); } @@ -2446,8 +2442,7 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, goto out_unlock; case NL80211_IFTYPE_MONITOR: mvmvif->monitor_active = false; - iwl_mvm_update_quotas(mvm, NULL, - IWL_MVM_QUOTA_UPDATE_TYPE_REGULAR); + iwl_mvm_update_quotas(mvm, NULL); break; case NL80211_IFTYPE_AP: /* This part is triggered only during CSA */ @@ -2455,8 +2450,7 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, goto out_unlock; mvmvif->ap_ibss_active = false; - iwl_mvm_update_quotas(mvm, NULL, - IWL_MVM_QUOTA_UPDATE_TYPE_REGULAR); + iwl_mvm_update_quotas(mvm, NULL); default: break; } @@ -2520,8 +2514,7 @@ static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm, mvm->noa_duration = noa_duration; mvm->noa_vif = vif; - return iwl_mvm_update_quotas(mvm, NULL, - IWL_MVM_QUOTA_UPDATE_TYPE_REGULAR); + return iwl_mvm_update_quotas(mvm, NULL); case IWL_MVM_TM_CMD_SET_BEACON_FILTER: /* must be associated client vif - ignore authorized */ if (!vif || vif->type != NL80211_IFTYPE_STATION || diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 55e42f4d6cce..5fedd5521432 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -844,29 +844,8 @@ int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); /* Quota management */ - -/** - * enum iwl_mvm_quota_update_type - quota update type - - * @IWL_MVM_QUOTA_UPDATE_TYPE_REGULAR: regular quota update, use the - * existing vifs (ie. no new vif nor a disabled vif is passed). - * @IWL_MVM_QUOTA_UPDATE_TYPE_NEW: a new vif is being added to the - * quota calculation and needs to be treated differently. - * @IWL_MVM_QUOTA_UPDATE_TYPE_DISABLED: temporarily disable a vif from - * the quota calculation. The mvm mutex must remain held for the - * entire time during which the vif is to remain disabled, - * otherwise there is no guarantee that another code flow will - * not reenable it accidentally (by updating the quotas without - * marking the vif as disabled). - */ -enum iwl_mvm_quota_update_type { - IWL_MVM_QUOTA_UPDATE_TYPE_REGULAR, - IWL_MVM_QUOTA_UPDATE_TYPE_NEW, - IWL_MVM_QUOTA_UPDATE_TYPE_DISABLED, -}; - -int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - enum iwl_mvm_quota_update_type type); +int iwl_mvm_update_quotas(struct iwl_mvm *mvm, + struct ieee80211_vif *disabled_vif); /* Scanning */ int iwl_mvm_scan_request(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c index 6de0c6352943..4e20b3ce2b6a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/quota.c +++ b/drivers/net/wireless/iwlwifi/mvm/quota.c @@ -73,7 +73,7 @@ struct iwl_mvm_quota_iterator_data { int colors[MAX_BINDINGS]; int low_latency[MAX_BINDINGS]; int n_low_latency_bindings; - struct ieee80211_vif *skip_vif; + struct ieee80211_vif *disabled_vif; }; static void iwl_mvm_quota_iterator(void *_data, u8 *mac, @@ -83,14 +83,8 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac, struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); u16 id; - /* - * We'll account for the new interface (if any) below, - * skip it here in case we're not called from within - * the add_interface callback (otherwise it won't show - * up in iteration) - * Also skip disabled interfaces here immediately. - */ - if (vif == data->skip_vif) + /* skip disabled interfaces here immediately */ + if (vif == data->disabled_vif) return; if (!mvmvif->phy_ctxt) @@ -172,15 +166,15 @@ static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm, #endif } -int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - enum iwl_mvm_quota_update_type type) +int iwl_mvm_update_quotas(struct iwl_mvm *mvm, + struct ieee80211_vif *disabled_vif) { struct iwl_time_quota_cmd cmd = {}; int i, idx, ret, num_active_macs, quota, quota_rem, n_non_lowlat; struct iwl_mvm_quota_iterator_data data = { .n_interfaces = {}, .colors = { -1, -1, -1, -1 }, - .skip_vif = vif, + .disabled_vif = disabled_vif, }; lockdep_assert_held(&mvm->mutex); @@ -192,17 +186,9 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *vif, /* iterator data above must match */ BUILD_BUG_ON(MAX_BINDINGS != 4); - if (WARN_ON_ONCE((type != IWL_MVM_QUOTA_UPDATE_TYPE_REGULAR && !vif) || - (type == IWL_MVM_QUOTA_UPDATE_TYPE_REGULAR && vif))) - return -EINVAL; - ieee80211_iterate_active_interfaces_atomic( mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_quota_iterator, &data); - if (type == IWL_MVM_QUOTA_UPDATE_TYPE_NEW) { - data.skip_vif = NULL; - iwl_mvm_quota_iterator(&data, vif->addr, vif); - } /* * The FW's scheduling session consists of diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index 98ff64961896..ac249da8a22b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -631,8 +631,7 @@ int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif, mvmvif->low_latency = value; - res = iwl_mvm_update_quotas(mvm, NULL, - IWL_MVM_QUOTA_UPDATE_TYPE_REGULAR); + res = iwl_mvm_update_quotas(mvm, NULL); if (res) return res; From b08c1d972a3b9135a0ccc5432309ee635841f466 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 20 May 2014 23:31:05 +0300 Subject: [PATCH 258/274] iwlwifi: mvm: add switch_vif_chanctx operation Implement the switch_vif_chanctx operation with support for a single-vif and SWAP mode. The REASSIGN mode and multi-vifs are not supported yet. This operation needs to implement 4 steps, namely unassign, remove, add and assign the chanctx. In order to do this, split out these operations into locked and non-locked parts, thus allowing us to call them while locked. Additionally, in order to allow us to restart the hardware when something fails, add a boolean to the iwl_mvm_nic_restart() function that tells whether the restart was triggered by a FW error or something else. Signed-off-by: Luciano Coelho Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 156 ++++++++++++++++---- drivers/net/wireless/iwlwifi/mvm/mvm.h | 2 + drivers/net/wireless/iwlwifi/mvm/ops.c | 11 +- 3 files changed, 136 insertions(+), 33 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 72f82a33856c..24cc56957339 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -2282,17 +2282,17 @@ static int iwl_mvm_cancel_roc(struct ieee80211_hw *hw) return 0; } -static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw, - struct ieee80211_chanctx_conf *ctx) +static int __iwl_mvm_add_chanctx(struct iwl_mvm *mvm, + struct ieee80211_chanctx_conf *ctx) { - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; struct iwl_mvm_phy_ctxt *phy_ctxt; int ret; + lockdep_assert_held(&mvm->mutex); + IWL_DEBUG_MAC80211(mvm, "Add channel context\n"); - mutex_lock(&mvm->mutex); phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); if (!phy_ctxt) { ret = -ENOSPC; @@ -2310,19 +2310,40 @@ static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw, iwl_mvm_phy_ctxt_ref(mvm, phy_ctxt); *phy_ctxt_id = phy_ctxt->id; out: - mutex_unlock(&mvm->mutex); return ret; } +static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int ret; + + mutex_lock(&mvm->mutex); + ret = __iwl_mvm_add_chanctx(mvm, ctx); + mutex_unlock(&mvm->mutex); + + return ret; +} + +static void __iwl_mvm_remove_chanctx(struct iwl_mvm *mvm, + struct ieee80211_chanctx_conf *ctx) +{ + u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; + struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; + + lockdep_assert_held(&mvm->mutex); + + iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt); +} + static void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; - struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; mutex_lock(&mvm->mutex); - iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt); + __iwl_mvm_remove_chanctx(mvm, ctx); mutex_unlock(&mvm->mutex); } @@ -2351,17 +2372,16 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw, mutex_unlock(&mvm->mutex); } -static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_chanctx_conf *ctx) +static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) { - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; - mutex_lock(&mvm->mutex); + lockdep_assert_held(&mvm->mutex); mvmvif->phy_ctxt = phy_ctxt; @@ -2378,18 +2398,18 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, * (in bss_info_changed), similarly for IBSS. */ ret = 0; - goto out_unlock; + goto out; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_MONITOR: break; default: ret = -EINVAL; - goto out_unlock; + goto out; } ret = iwl_mvm_binding_add_vif(mvm, vif); if (ret) - goto out_unlock; + goto out; /* * Power state must be updated before quotas, @@ -2414,32 +2434,43 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, iwl_mvm_mac_ctxt_changed(mvm, vif, false); } - goto out_unlock; + goto out; - out_remove_binding: +out_remove_binding: iwl_mvm_binding_remove_vif(mvm, vif); iwl_mvm_power_update_mac(mvm); - out_unlock: - mutex_unlock(&mvm->mutex); +out: if (ret) mvmvif->phy_ctxt = NULL; return ret; } - -static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_chanctx_conf *ctx) +static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; mutex_lock(&mvm->mutex); + ret = __iwl_mvm_assign_vif_chanctx(mvm, vif, ctx); + mutex_unlock(&mvm->mutex); + + return ret; +} + +static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + lockdep_assert_held(&mvm->mutex); iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data); switch (vif->type) { case NL80211_IFTYPE_ADHOC: - goto out_unlock; + goto out; case NL80211_IFTYPE_MONITOR: mvmvif->monitor_active = false; iwl_mvm_update_quotas(mvm, NULL); @@ -2447,7 +2478,7 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, case NL80211_IFTYPE_AP: /* This part is triggered only during CSA */ if (!vif->csa_active || !mvmvif->ap_ibss_active) - goto out_unlock; + goto out; mvmvif->ap_ibss_active = false; iwl_mvm_update_quotas(mvm, NULL); @@ -2457,12 +2488,80 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, iwl_mvm_binding_remove_vif(mvm, vif); -out_unlock: +out: mvmvif->phy_ctxt = NULL; iwl_mvm_power_update_mac(mvm); +} + +static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + mutex_lock(&mvm->mutex); + __iwl_mvm_unassign_vif_chanctx(mvm, vif, ctx); mutex_unlock(&mvm->mutex); } +static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs, + enum ieee80211_chanctx_switch_mode mode) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int ret; + + /* we only support SWAP_CONTEXTS and with a single-vif right now */ + if (mode != CHANCTX_SWMODE_SWAP_CONTEXTS || n_vifs > 1) + return -EOPNOTSUPP; + + mutex_lock(&mvm->mutex); + __iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx); + __iwl_mvm_remove_chanctx(mvm, vifs[0].old_ctx); + + ret = __iwl_mvm_add_chanctx(mvm, vifs[0].new_ctx); + if (ret) { + IWL_ERR(mvm, "failed to add new_ctx during channel switch\n"); + goto out_reassign; + } + + ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx); + if (ret) { + IWL_ERR(mvm, + "failed to assign new_ctx during channel switch\n"); + goto out_remove; + } + + goto out; + +out_remove: + __iwl_mvm_remove_chanctx(mvm, vifs[0].new_ctx); + +out_reassign: + ret = __iwl_mvm_add_chanctx(mvm, vifs[0].old_ctx); + if (ret) { + IWL_ERR(mvm, "failed to add old_ctx back after failure.\n"); + goto out_restart; + } + + ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx); + if (ret) { + IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n"); + goto out_restart; + } + + goto out; + +out_restart: + /* things keep failing, better restart the hw */ + iwl_mvm_nic_restart(mvm, false); + +out: + mutex_unlock(&mvm->mutex); + return ret; +} + static int iwl_mvm_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set) @@ -2627,6 +2726,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = { .change_chanctx = iwl_mvm_change_chanctx, .assign_vif_chanctx = iwl_mvm_assign_vif_chanctx, .unassign_vif_chanctx = iwl_mvm_unassign_vif_chanctx, + .switch_vif_chanctx = iwl_mvm_switch_vif_chanctx, .start_ap = iwl_mvm_start_ap_ibss, .stop_ap = iwl_mvm_stop_ap_ibss, diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 5fedd5521432..fa8fcbcceb23 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -1077,4 +1077,6 @@ void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state); int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *vif, bool added_vif); +void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error); + #endif /* __IWL_MVM_H__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 4e2823faa5b9..89a095691507 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -764,7 +764,7 @@ static void iwl_mvm_reprobe_wk(struct work_struct *wk) module_put(THIS_MODULE); } -static void iwl_mvm_nic_restart(struct iwl_mvm *mvm) +void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error) { iwl_abort_notification_waits(&mvm->notif_wait); @@ -821,11 +821,12 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm) reprobe->dev = mvm->trans->dev; INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk); schedule_work(&reprobe->work); - } else if (mvm->cur_ucode == IWL_UCODE_REGULAR && mvm->restart_fw) { + } else if (mvm->cur_ucode == IWL_UCODE_REGULAR && + (!fw_error || mvm->restart_fw)) { /* don't let the transport/FW power down */ iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); - if (mvm->restart_fw > 0) + if (fw_error && mvm->restart_fw > 0) mvm->restart_fw--; ieee80211_restart_hw(mvm->hw); } @@ -837,7 +838,7 @@ static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode) iwl_mvm_dump_nic_error_log(mvm); - iwl_mvm_nic_restart(mvm); + iwl_mvm_nic_restart(mvm, true); } static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode) @@ -845,7 +846,7 @@ static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode) struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); WARN_ON(1); - iwl_mvm_nic_restart(mvm); + iwl_mvm_nic_restart(mvm, true); } struct iwl_d0i3_iter_data { From f0c977839611369958bb545c741f897f5141bb30 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Wed, 5 Mar 2014 15:41:48 +0200 Subject: [PATCH 259/274] iwlwifi: mvm: CSA unbind-bind flow support for client Implement support for unbind-bind flow for the client roles. This includes telling the firmware that we are not associated, removing time-events, removing quotas and updating power management during the actual switch, and redoing everything in the new channel. Signed-off-by: Luciano Coelho Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 35 +++++++++++++++------ 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 24cc56957339..78a78c02dda5 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -293,6 +293,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) IEEE80211_HW_AMPDU_AGGREGATION | IEEE80211_HW_TIMING_BEACON_ONLY | IEEE80211_HW_CONNECTION_MONITOR | + IEEE80211_HW_CHANCTX_STA_CSA | IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | IEEE80211_HW_SUPPORTS_STATIC_SMPS; @@ -2374,7 +2375,8 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw, static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_chanctx_conf *ctx) + struct ieee80211_chanctx_conf *ctx, + bool switching_chanctx) { u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; @@ -2429,7 +2431,8 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm, } /* Handle binding during CSA */ - if (vif->type == NL80211_IFTYPE_AP) { + if ((vif->type == NL80211_IFTYPE_AP) || + (switching_chanctx && (vif->type == NL80211_IFTYPE_STATION))) { iwl_mvm_update_quotas(mvm, NULL); iwl_mvm_mac_ctxt_changed(mvm, vif, false); } @@ -2452,7 +2455,7 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, int ret; mutex_lock(&mvm->mutex); - ret = __iwl_mvm_assign_vif_chanctx(mvm, vif, ctx); + ret = __iwl_mvm_assign_vif_chanctx(mvm, vif, ctx, false); mutex_unlock(&mvm->mutex); return ret; @@ -2460,9 +2463,11 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_chanctx_conf *ctx) + struct ieee80211_chanctx_conf *ctx, + bool switching_chanctx) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct ieee80211_vif *disabled_vif = NULL; lockdep_assert_held(&mvm->mutex); @@ -2473,7 +2478,6 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm, goto out; case NL80211_IFTYPE_MONITOR: mvmvif->monitor_active = false; - iwl_mvm_update_quotas(mvm, NULL); break; case NL80211_IFTYPE_AP: /* This part is triggered only during CSA */ @@ -2481,11 +2485,20 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm, goto out; mvmvif->ap_ibss_active = false; - iwl_mvm_update_quotas(mvm, NULL); + break; + case NL80211_IFTYPE_STATION: + if (!switching_chanctx) + break; + + disabled_vif = vif; + + iwl_mvm_mac_ctxt_changed(mvm, vif, true); + break; default: break; } + iwl_mvm_update_quotas(mvm, disabled_vif); iwl_mvm_binding_remove_vif(mvm, vif); out: @@ -2500,7 +2513,7 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); mutex_lock(&mvm->mutex); - __iwl_mvm_unassign_vif_chanctx(mvm, vif, ctx); + __iwl_mvm_unassign_vif_chanctx(mvm, vif, ctx, false); mutex_unlock(&mvm->mutex); } @@ -2517,7 +2530,7 @@ static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw, return -EOPNOTSUPP; mutex_lock(&mvm->mutex); - __iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx); + __iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true); __iwl_mvm_remove_chanctx(mvm, vifs[0].old_ctx); ret = __iwl_mvm_add_chanctx(mvm, vifs[0].new_ctx); @@ -2526,7 +2539,8 @@ static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw, goto out_reassign; } - ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx); + ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx, + true); if (ret) { IWL_ERR(mvm, "failed to assign new_ctx during channel switch\n"); @@ -2545,7 +2559,8 @@ out_reassign: goto out_restart; } - ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx); + ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, + true); if (ret) { IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n"); goto out_restart; From fe887665a87b4c45be21253c4a6904ad1334be85 Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Thu, 10 Apr 2014 21:46:16 +0300 Subject: [PATCH 260/274] iwlwifi: mvm: Use beacon_get_template instead of beacon_get Call ieee80211_beacon_get_template instead of ieee80211_beacon_get and sync the CSA counters with mac80211 after each beacon transmission. Signed-off-by: Andrei Otcheretianski Reviewed-by: Luciano Coelho Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index 549817d46bfc..6d094721a20e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -969,7 +969,7 @@ int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm, WARN_ON(vif->type != NL80211_IFTYPE_AP && vif->type != NL80211_IFTYPE_ADHOC); - beacon = ieee80211_beacon_get(mvm->hw, vif); + beacon = ieee80211_beacon_get_template(mvm->hw, vif, NULL); if (!beacon) return -ENOMEM; @@ -1233,6 +1233,7 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, if (unlikely(mvm->csa_vif && mvm->csa_vif->csa_active)) { if (!ieee80211_csa_is_complete(mvm->csa_vif)) { + ieee80211_csa_update_counter(mvm->csa_vif); iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm->csa_vif); } else { ieee80211_csa_finish(mvm->csa_vif); From 664322fa43b7a52a9e8be9a3874c024412c24a50 Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Thu, 5 Jun 2014 16:40:36 +0300 Subject: [PATCH 261/274] iwlwifi: mvm: Protect mvm->csa_vif with RCU Currently mvm->csa_vif is protected with mvm mutex. The RCU protection is required for "iwlwifi: mvm: Reflect GO channel switch in NoA" patch. Signed-off-by: Andrei Otcheretianski Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c | 15 +++++++++------ drivers/net/wireless/iwlwifi/mvm/mac80211.c | 12 ++++++++++-- drivers/net/wireless/iwlwifi/mvm/mvm.h | 2 +- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index 6d094721a20e..19bd696bd6f8 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -1206,6 +1206,7 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_mvm_tx_resp *beacon_notify_hdr; + struct ieee80211_vif *csa_vif; u64 tsf; lockdep_assert_held(&mvm->mutex); @@ -1231,13 +1232,15 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, mvm->ap_last_beacon_gp2, le32_to_cpu(beacon_notify_hdr->initial_rate)); - if (unlikely(mvm->csa_vif && mvm->csa_vif->csa_active)) { - if (!ieee80211_csa_is_complete(mvm->csa_vif)) { - ieee80211_csa_update_counter(mvm->csa_vif); - iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm->csa_vif); + csa_vif = rcu_dereference_protected(mvm->csa_vif, + lockdep_is_held(&mvm->mutex)); + if (unlikely(csa_vif && csa_vif->csa_active)) { + if (!ieee80211_csa_is_complete(csa_vif)) { + ieee80211_csa_update_counter(csa_vif); + iwl_mvm_mac_ctxt_beacon_changed(mvm, csa_vif); } else { - ieee80211_csa_finish(mvm->csa_vif); - mvm->csa_vif = NULL; + ieee80211_csa_finish(csa_vif); + RCU_INIT_POINTER(mvm->csa_vif, NULL); } } diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 78a78c02dda5..984a1e75aad0 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -1607,6 +1607,10 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); + /* Handle AP stop while in CSA */ + if (rcu_access_pointer(mvm->csa_vif) == vif) + RCU_INIT_POINTER(mvm->csa_vif, NULL); + mvmvif->ap_ibss_active = false; mvm->ap_last_beacon_gp2 = 0; @@ -2664,15 +2668,19 @@ static void iwl_mvm_channel_switch_beacon(struct ieee80211_hw *hw, struct cfg80211_chan_def *chandef) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct ieee80211_vif *csa_vif; mutex_lock(&mvm->mutex); - if (WARN(mvm->csa_vif && mvm->csa_vif->csa_active, + + csa_vif = rcu_dereference_protected(mvm->csa_vif, + lockdep_is_held(&mvm->mutex)); + if (WARN(csa_vif && csa_vif->csa_active, "Another CSA is already in progress")) goto out_unlock; IWL_DEBUG_MAC80211(mvm, "CSA started to freq %d\n", chandef->center_freq1); - mvm->csa_vif = vif; + rcu_assign_pointer(mvm->csa_vif, vif); out_unlock: mutex_unlock(&mvm->mutex); diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index fa8fcbcceb23..5bb42af8886b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -658,7 +658,7 @@ struct iwl_mvm { /* Indicate if device power save is allowed */ bool ps_disabled; - struct ieee80211_vif *csa_vif; + struct ieee80211_vif __rcu *csa_vif; /* system time of last beacon (for AP/GO interface) */ u32 ap_last_beacon_gp2; From 7f0a7c671cdc0396f99b60b77bd02e2dee7c4838 Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Sun, 4 May 2014 11:48:12 +0300 Subject: [PATCH 262/274] iwlwifi: mvm: Reflect GO channel switch in NoA According to the spec, GO/AP should perform the channel switch just before "beacon 0". However, since the exact timing isn't defined, it may result in a sudden GO disappearance from the channel. Prevent potential packet loss when performing the CS by scheduling NoA time event and executing the channel switch flow when a notification from fw is received. Signed-off-by: Andrei Otcheretianski Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/fw-api.h | 3 + drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c | 41 ++++++++--- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 5 +- drivers/net/wireless/iwlwifi/mvm/mvm.h | 12 +++ drivers/net/wireless/iwlwifi/mvm/time-event.c | 73 +++++++++++++++++++ drivers/net/wireless/iwlwifi/mvm/time-event.h | 29 ++++++++ 6 files changed, 153 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index 6c479de3b8d4..99329edcd99f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h @@ -538,6 +538,9 @@ enum iwl_time_event_type { /* WiDi Sync Events */ TE_WIDI_TX_SYNC, + /* Channel Switch NoA */ + TE_P2P_GO_CSA_NOA, + TE_MAX }; /* MAC_EVENT_TYPE_API_E_VER_1 */ diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index 19bd696bd6f8..a176d008dab1 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -67,6 +67,7 @@ #include "iwl-prph.h" #include "fw-api.h" #include "mvm.h" +#include "time-event.h" const u8 iwl_mvm_ac_to_tx_fifo[] = { IWL_MVM_TX_FIFO_VO, @@ -1200,6 +1201,35 @@ int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif) return 0; } +static void iwl_mvm_csa_count_down(struct iwl_mvm *mvm, + struct ieee80211_vif *csa_vif, u32 gp2) +{ + struct iwl_mvm_vif *mvmvif = + iwl_mvm_vif_from_mac80211(csa_vif); + + if (!ieee80211_csa_is_complete(csa_vif)) { + int c = ieee80211_csa_update_counter(csa_vif); + + iwl_mvm_mac_ctxt_beacon_changed(mvm, csa_vif); + if (csa_vif->p2p && + !iwl_mvm_te_scheduled(&mvmvif->time_event_data) && gp2) { + u32 rel_time = (c + 1) * + csa_vif->bss_conf.beacon_int - + IWL_MVM_CHANNEL_SWITCH_TIME; + u32 apply_time = gp2 + rel_time * 1024; + + iwl_mvm_schedule_csa_noa(mvm, csa_vif, + IWL_MVM_CHANNEL_SWITCH_TIME - + IWL_MVM_CHANNEL_SWITCH_MARGIN, + apply_time); + } + } else if (!iwl_mvm_te_scheduled(&mvmvif->time_event_data)) { + /* we don't have CSA NoA scheduled yet, switch now */ + ieee80211_csa_finish(csa_vif); + RCU_INIT_POINTER(mvm->csa_vif, NULL); + } +} + int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) @@ -1234,15 +1264,8 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, csa_vif = rcu_dereference_protected(mvm->csa_vif, lockdep_is_held(&mvm->mutex)); - if (unlikely(csa_vif && csa_vif->csa_active)) { - if (!ieee80211_csa_is_complete(csa_vif)) { - ieee80211_csa_update_counter(csa_vif); - iwl_mvm_mac_ctxt_beacon_changed(mvm, csa_vif); - } else { - ieee80211_csa_finish(csa_vif); - RCU_INIT_POINTER(mvm->csa_vif, NULL); - } - } + if (unlikely(csa_vif && csa_vif->csa_active)) + iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2); return 0; } diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 984a1e75aad0..4a0350fc4b4e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -1608,8 +1608,11 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); /* Handle AP stop while in CSA */ - if (rcu_access_pointer(mvm->csa_vif) == vif) + if (rcu_access_pointer(mvm->csa_vif) == vif) { + iwl_mvm_remove_time_event(mvm, mvmvif, + &mvmvif->time_event_data); RCU_INIT_POINTER(mvm->csa_vif, NULL); + } mvmvif->ap_ibss_active = false; mvm->ap_last_beacon_gp2 = 0; diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 5bb42af8886b..db496c5c73fc 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -83,6 +83,18 @@ #define IWL_RSSI_OFFSET 50 #define IWL_MVM_MISSED_BEACONS_THRESHOLD 8 +/* + * The CSA NoA is scheduled IWL_MVM_CHANNEL_SWITCH_TIME TUs before "beacon 0" + * TBTT. This value should be big enough to ensure that we switch in time. + */ +#define IWL_MVM_CHANNEL_SWITCH_TIME 40 + +/* + * This value (in TUs) is used to fine tune the CSA NoA end time which should + * be just before "beacon 0" TBTT. + */ +#define IWL_MVM_CHANNEL_SWITCH_MARGIN 4 + enum iwl_mvm_tx_fifo { IWL_MVM_TX_FIFO_BK = 0, IWL_MVM_TX_FIFO_BE, diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c index 80100f6cc12a..ae52613b97f2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c @@ -138,6 +138,41 @@ static void iwl_mvm_roc_finished(struct iwl_mvm *mvm) schedule_work(&mvm->roc_done_wk); } +static void iwl_mvm_csa_noa_start(struct iwl_mvm *mvm) +{ + struct ieee80211_vif *csa_vif; + + rcu_read_lock(); + + csa_vif = rcu_dereference(mvm->csa_vif); + if (!csa_vif || !csa_vif->csa_active) + goto out_unlock; + + IWL_DEBUG_TE(mvm, "CSA NOA started\n"); + + /* + * CSA NoA is started but we still have beacons to + * transmit on the current channel. + * So we just do nothing here and the switch + * will be performed on the last TBTT. + */ + if (!ieee80211_csa_is_complete(csa_vif)) { + IWL_WARN(mvm, "CSA NOA started too early\n"); + goto out_unlock; + } + + ieee80211_csa_finish(csa_vif); + + rcu_read_unlock(); + + RCU_INIT_POINTER(mvm->csa_vif, NULL); + + return; + +out_unlock: + rcu_read_unlock(); +} + static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm, struct ieee80211_vif *vif, const char *errmsg) @@ -213,6 +248,14 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm, set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status); iwl_mvm_ref(mvm, IWL_MVM_REF_ROC); ieee80211_ready_on_channel(mvm->hw); + } else if (te_data->vif->type == NL80211_IFTYPE_AP) { + if (le32_to_cpu(notif->status)) + iwl_mvm_csa_noa_start(mvm); + else + IWL_DEBUG_TE(mvm, "CSA NOA failed to start\n"); + + /* we don't need it anymore */ + iwl_mvm_te_clear_data(mvm, te_data); } } else { IWL_WARN(mvm, "Got TE with unknown action\n"); @@ -538,3 +581,33 @@ void iwl_mvm_stop_p2p_roc(struct iwl_mvm *mvm) iwl_mvm_roc_finished(mvm); } + +int iwl_mvm_schedule_csa_noa(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 duration, u32 apply_time) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; + struct iwl_time_event_cmd time_cmd = {}; + + lockdep_assert_held(&mvm->mutex); + + if (te_data->running) { + IWL_DEBUG_TE(mvm, "CS NOA is already scheduled\n"); + return -EBUSY; + } + + time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD); + time_cmd.id_and_color = + cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); + time_cmd.id = cpu_to_le32(TE_P2P_GO_CSA_NOA); + time_cmd.apply_time = cpu_to_le32(apply_time); + time_cmd.max_frags = TE_V2_FRAG_NONE; + time_cmd.duration = cpu_to_le32(duration); + time_cmd.repeat = 1; + time_cmd.interval = cpu_to_le32(1); + time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START | + TE_V2_ABSENCE); + + return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); +} diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.h b/drivers/net/wireless/iwlwifi/mvm/time-event.h index 4a61c8c02372..2f48a90d4ad3 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.h +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.h @@ -214,4 +214,33 @@ void iwl_mvm_te_clear_data(struct iwl_mvm *mvm, void iwl_mvm_roc_done_wk(struct work_struct *wk); +/** + * iwl_mvm_schedule_csa_noa - request NoA for channel switch + * @mvm: the mvm component + * @vif: the virtual interface for which the channel switch is issued + * @duration: the duration of the NoA in TU. + * @apply_time: NoA start time in GP2. + * + * This function is used to schedule NoA time event and is used to perform + * the channel switch flow. + */ +int iwl_mvm_schedule_csa_noa(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 duration, u32 apply_time); + +/** + * iwl_mvm_te_scheduled - check if the fw received the TE cmd + * @te_data: the time event data that corresponds to that time event + * + * This function returns true iff this TE is added to the fw. + */ +static inline bool +iwl_mvm_te_scheduled(struct iwl_mvm_time_event_data *te_data) +{ + if (!te_data) + return false; + + return !!te_data->uid; +} + #endif /* __time_event_h__ */ From 003e5236a1fcab3fc4576fe643e31a3d83027256 Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Sun, 25 May 2014 17:24:22 +0300 Subject: [PATCH 263/274] iwlwifi: mvm: Use CS tx block bit for AP/GO An AP/GO may perform the channel switch slightly before its stations. This scenario may result in packet loss, since the transmission may start before the client is actually on a new channel. In order to prevent potential packet loss disable tx to all the stations when the channel switch flow starts. Clear the disable_tx bit when a station is seen on a target channel, or after IWL_MVM_CS_UNBLOCK_TX_TIMEOUT beacons on a new channel. In addition call ieee80211_sta_block_awake in order to inform mac80211 that the frames for this station should be buffered. Signed-off-by: Andrei Otcheretianski Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c | 25 ++++++++++ drivers/net/wireless/iwlwifi/mvm/mac80211.c | 11 +++++ drivers/net/wireless/iwlwifi/mvm/mvm.h | 8 +++ drivers/net/wireless/iwlwifi/mvm/rx.c | 17 +++++++ drivers/net/wireless/iwlwifi/mvm/sta.c | 54 +++++++++++++++++++++ drivers/net/wireless/iwlwifi/mvm/sta.h | 10 ++++ drivers/net/wireless/iwlwifi/mvm/tx.c | 14 ++++-- 7 files changed, 136 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index a176d008dab1..96b9cf8137e7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -1237,6 +1237,7 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_mvm_tx_resp *beacon_notify_hdr; struct ieee80211_vif *csa_vif; + struct ieee80211_vif *tx_blocked_vif; u64 tsf; lockdep_assert_held(&mvm->mutex); @@ -1267,6 +1268,30 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, if (unlikely(csa_vif && csa_vif->csa_active)) iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2); + tx_blocked_vif = rcu_dereference_protected(mvm->csa_tx_blocked_vif, + lockdep_is_held(&mvm->mutex)); + if (unlikely(tx_blocked_vif)) { + struct iwl_mvm_vif *mvmvif = + iwl_mvm_vif_from_mac80211(tx_blocked_vif); + + /* + * The channel switch is started and we have blocked the + * stations. If this is the first beacon (the timeout wasn't + * set), set the unblock timeout, otherwise countdown + */ + if (!mvm->csa_tx_block_bcn_timeout) + mvm->csa_tx_block_bcn_timeout = + IWL_MVM_CS_UNBLOCK_TX_TIMEOUT; + else + mvm->csa_tx_block_bcn_timeout--; + + /* Check if the timeout is expired, and unblock tx */ + if (mvm->csa_tx_block_bcn_timeout == 0) { + iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, false); + RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL); + } + } + return 0; } diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 4a0350fc4b4e..5ed6fb32087c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -1614,6 +1614,11 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, RCU_INIT_POINTER(mvm->csa_vif, NULL); } + if (rcu_access_pointer(mvm->csa_tx_blocked_vif) == vif) { + RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL); + mvm->csa_tx_block_bcn_timeout = 0; + } + mvmvif->ap_ibss_active = false; mvm->ap_last_beacon_gp2 = 0; @@ -2491,6 +2496,12 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm, if (!vif->csa_active || !mvmvif->ap_ibss_active) goto out; + /* Set CS bit on all the stations */ + iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, true); + + /* Save blocked iface, the timeout is set on the next beacon */ + rcu_assign_pointer(mvm->csa_tx_blocked_vif, vif); + mvmvif->ap_ibss_active = false; break; case NL80211_IFTYPE_STATION: diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index db496c5c73fc..7b308c4834f6 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -95,6 +95,12 @@ */ #define IWL_MVM_CHANNEL_SWITCH_MARGIN 4 +/* + * Number of beacons to transmit on a new channel until we unblock tx to + * the stations, even if we didn't identify them on a new channel + */ +#define IWL_MVM_CS_UNBLOCK_TX_TIMEOUT 3 + enum iwl_mvm_tx_fifo { IWL_MVM_TX_FIFO_BK = 0, IWL_MVM_TX_FIFO_BE, @@ -671,6 +677,8 @@ struct iwl_mvm { bool ps_disabled; struct ieee80211_vif __rcu *csa_vif; + struct ieee80211_vif __rcu *csa_tx_blocked_vif; + u8 csa_tx_block_bcn_timeout; /* system time of last beacon (for AP/GO interface) */ u32 ap_last_beacon_gp2; diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c index cf7276967acd..4b98987fc413 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/iwlwifi/mvm/rx.c @@ -258,6 +258,23 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, memset(&rx_status, 0, sizeof(rx_status)); + /* + * We have tx blocked stations (with CS bit). If we heard frames from + * a blocked station on a new channel we can TX to it again. + */ + if (unlikely(mvm->csa_tx_block_bcn_timeout)) { + struct ieee80211_sta *sta; + + rcu_read_lock(); + + sta = ieee80211_find_sta( + rcu_dereference(mvm->csa_tx_blocked_vif), hdr->addr2); + if (sta) + iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false); + + rcu_read_unlock(); + } + /* * drop the packet if it has failed being decrypted by HW */ diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index d3a6cf7558eb..812813964847 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -1468,3 +1468,57 @@ void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm, if (ret) IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); } + +void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + bool disable) +{ + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); + + spin_lock_bh(&mvm_sta->lock); + + if (mvm_sta->disable_tx == disable) { + spin_unlock_bh(&mvm_sta->lock); + return; + } + + mvm_sta->disable_tx = disable; + + /* + * Tell mac80211 to start/stop queueing tx for this station, + * but don't stop queueing if there are still pending frames + * for this station. + */ + if (disable || !atomic_read(&mvm->pending_frames[mvm_sta->sta_id])) + ieee80211_sta_block_awake(mvm->hw, sta, disable); + + iwl_mvm_sta_modify_disable_tx(mvm, mvm_sta, disable); + + spin_unlock_bh(&mvm_sta->lock); +} + +void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm, + struct iwl_mvm_vif *mvmvif, + bool disable) +{ + struct ieee80211_sta *sta; + struct iwl_mvm_sta *mvm_sta; + int i; + + lockdep_assert_held(&mvm->mutex); + + /* Block/unblock all the stations of the given mvmvif */ + for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], + lockdep_is_held(&mvm->mutex)); + if (IS_ERR_OR_NULL(sta)) + continue; + + mvm_sta = iwl_mvm_sta_from_mac80211(sta); + if (mvm_sta->mac_id_n_color != + FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)) + continue; + + iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, disable); + } +} diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h index 10c1a5352651..3b1c8bd6cb54 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/sta.h @@ -73,6 +73,7 @@ #include "rs.h" struct iwl_mvm; +struct iwl_mvm_vif; /** * DOC: station table - introduction @@ -295,6 +296,7 @@ static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data) * @tid_data: per tid data. Look at %iwl_mvm_tid_data. * @tx_protection: reference counter for controlling the Tx protection. * @tt_tx_protection: is thermal throttling enable Tx protection? + * @disable_tx: is tx to this STA disabled? * * When mac80211 creates a station it reserves some space (hw->sta_data_size) * in the structure for use by driver. This structure is placed in that @@ -317,6 +319,8 @@ struct iwl_mvm_sta { /* Temporary, until the new TLC will control the Tx protection */ s8 tx_protection; bool tt_tx_protection; + + bool disable_tx; }; static inline struct iwl_mvm_sta * @@ -406,5 +410,11 @@ int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, bool drain); void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, bool disable); +void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + bool disable); +void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm, + struct iwl_mvm_vif *mvmvif, + bool disable); #endif /* __sta_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index fa87a4ba25ec..f5c0982d297c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -727,13 +727,21 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, goto out; if (mvmsta && mvmsta->vif->type == NL80211_IFTYPE_AP) { + /* - * If there are no pending frames for this STA, notify - * mac80211 that this station can go to sleep in its + * If there are no pending frames for this STA and + * the tx to this station is not disabled, notify + * mac80211 that this station can now wake up in its * STA table. * If mvmsta is not NULL, sta is valid. */ - ieee80211_sta_block_awake(mvm->hw, sta, false); + + spin_lock_bh(&mvmsta->lock); + + if (!mvmsta->disable_tx) + ieee80211_sta_block_awake(mvm->hw, sta, false); + + spin_unlock_bh(&mvmsta->lock); } if (PTR_ERR(sta) == -EBUSY || PTR_ERR(sta) == -ENOENT) { From cf7b491dbbac0e25f24265b005bc0ceff622d387 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 15 May 2014 11:44:40 +0300 Subject: [PATCH 264/274] iwlwifi: mvm: disallow new TDLS stations when appropriate HW/FW constraints dictate that TDLS should only be used when a single phy ctx is active. We also support at most 4 TDLS peers. We don't support TDLS on a P2P vif. Unify and move a phy-ctx counting implementation from the power-mgmt code in order to simplify implementation. Signed-off-by: Arik Nemtsov Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/fw-api.h | 2 ++ drivers/net/wireless/iwlwifi/mvm/mac80211.c | 30 +++++++++++++++++++++ drivers/net/wireless/iwlwifi/mvm/mvm.h | 1 + drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c | 26 ++++++++++++++++++ drivers/net/wireless/iwlwifi/mvm/power.c | 22 +-------------- 5 files changed, 60 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index 99329edcd99f..b8e4e78d601b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h @@ -86,6 +86,8 @@ enum { #define IWL_MVM_STATION_COUNT 16 +#define IWL_MVM_TDLS_STA_COUNT 4 + /* commands */ enum { MVM_ALIVE = 0x1, diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 5ed6fb32087c..57945fe06216 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -1843,6 +1843,26 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw, mutex_unlock(&mvm->mutex); } +static int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm) +{ + struct ieee80211_sta *sta; + int count = 0; + int i; + + lockdep_assert_held(&mvm->mutex); + + for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], + lockdep_is_held(&mvm->mutex)); + if (!sta || IS_ERR(sta) || !sta->tdls) + continue; + + count++; + } + + return count; +} + static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -1881,6 +1901,16 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, ret = -EINVAL; goto out_unlock; } + + if (sta->tdls && + (vif->p2p || + iwl_mvm_tdls_sta_count(mvm) == IWL_MVM_TDLS_STA_COUNT || + iwl_mvm_phy_ctx_count(mvm) > 1)) { + IWL_DEBUG_MAC80211(mvm, "refusing TDLS sta\n"); + ret = -EBUSY; + goto out_unlock; + } + ret = iwl_mvm_add_sta(mvm, vif, sta); } else if (old_state == IEEE80211_STA_NONE && new_state == IEEE80211_STA_AUTH) { diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 7b308c4834f6..c75b958736bc 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -838,6 +838,7 @@ void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt); void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt); +int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm); /* MAC (virtual interface) programming */ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif); diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c index 539f3a942d43..6cc243f7cf60 100644 --- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c @@ -261,3 +261,29 @@ void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) ctxt->ref--; } + +static void iwl_mvm_binding_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + unsigned long *data = _data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (!mvmvif->phy_ctxt) + return; + + if (vif->type == NL80211_IFTYPE_STATION || + vif->type == NL80211_IFTYPE_AP) + __set_bit(mvmvif->phy_ctxt->id, data); +} + +int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm) +{ + unsigned long phy_ctxt_counter = 0; + + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_binding_iterator, + &phy_ctxt_counter); + + return hweight8(phy_ctxt_counter); +} diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c index c182a8baf685..3b582dd6d77f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/iwlwifi/mvm/power.c @@ -246,30 +246,10 @@ static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm, IWL_MVM_PS_HEAVY_RX_THLD_PERCENT; } -static void iwl_mvm_binding_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - unsigned long *data = _data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (!mvmvif->phy_ctxt) - return; - - if (vif->type == NL80211_IFTYPE_STATION || - vif->type == NL80211_IFTYPE_AP) - __set_bit(mvmvif->phy_ctxt->id, data); -} - static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - unsigned long phy_ctxt_counter = 0; - - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_binding_iterator, - &phy_ctxt_counter); if (!memcmp(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid, ETH_ALEN)) @@ -291,7 +271,7 @@ static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm, * Avoid using uAPSD if client is in DCM - * low latency issue in Miracast */ - if (hweight8(phy_ctxt_counter) >= 2) + if (iwl_mvm_phy_ctx_count(mvm) >= 2) return false; return true; From 07ecd897b1634e0f34a9ba9d0d1b9263bc05d90b Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 20 May 2014 18:16:42 +0300 Subject: [PATCH 265/274] iwlwifi: mvm: protect TDLS discovery session Use the new mac80211 callback to protect a TDLS discovery session so we can hear the discovery-response packet. Signed-off-by: Arik Nemtsov Reviewed-by: Ilan Peer Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 57945fe06216..1acad838fc5c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -2017,6 +2017,18 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw, mutex_unlock(&mvm->mutex); } +static void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + u32 duration = 2 * vif->bss_conf.dtim_period * vif->bss_conf.beacon_int; + + mutex_lock(&mvm->mutex); + /* Protect the session to hear the TDLS setup response on the channel */ + iwl_mvm_protect_session(mvm, vif, duration, duration, 100); + mutex_unlock(&mvm->mutex); +} + static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_sched_scan_request *req, @@ -2781,6 +2793,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = { .sta_rc_update = iwl_mvm_sta_rc_update, .conf_tx = iwl_mvm_mac_conf_tx, .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx, + .mgd_protect_tdls_discover = iwl_mvm_mac_mgd_protect_tdls_discover, .flush = iwl_mvm_mac_flush, .sched_scan_start = iwl_mvm_mac_sched_scan_start, .sched_scan_stop = iwl_mvm_mac_sched_scan_stop, From fa3d07e47f2411aa9ccb54192c4f978c60a9e111 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 15 May 2014 18:59:32 +0300 Subject: [PATCH 266/274] iwlwifi: disable PSM on vifs with associated TDLS peers The FW does not support PSM on a vif with associated TDLS peers. Disable PSM when the first peer joins and re-enable it when the last leaves. Signed-off-by: Arik Nemtsov Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 31 +++++++++++++++++++-- drivers/net/wireless/iwlwifi/mvm/mvm.h | 3 ++ drivers/net/wireless/iwlwifi/mvm/power.c | 17 +++++++++-- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 1acad838fc5c..8aa6fa46c628 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -1843,9 +1843,10 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw, mutex_unlock(&mvm->mutex); } -static int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm) +int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct ieee80211_sta *sta; + struct iwl_mvm_sta *mvmsta; int count = 0; int i; @@ -1857,12 +1858,33 @@ static int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm) if (!sta || IS_ERR(sta) || !sta->tdls) continue; + if (vif) { + mvmsta = iwl_mvm_sta_from_mac80211(sta); + if (mvmsta->vif != vif) + continue; + } + count++; } return count; } +static void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool sta_added) +{ + int tdls_sta_cnt = iwl_mvm_tdls_sta_count(mvm, vif); + + /* + * Disable ps when the first TDLS sta is added and re-enable it + * when the last TDLS sta is removed + */ + if ((tdls_sta_cnt == 1 && sta_added) || + (tdls_sta_cnt == 0 && !sta_added)) + iwl_mvm_power_update_mac(mvm); +} + static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -1904,7 +1926,8 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, if (sta->tdls && (vif->p2p || - iwl_mvm_tdls_sta_count(mvm) == IWL_MVM_TDLS_STA_COUNT || + iwl_mvm_tdls_sta_count(mvm, NULL) == + IWL_MVM_TDLS_STA_COUNT || iwl_mvm_phy_ctx_count(mvm) > 1)) { IWL_DEBUG_MAC80211(mvm, "refusing TDLS sta\n"); ret = -EBUSY; @@ -1912,6 +1935,8 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, } ret = iwl_mvm_add_sta(mvm, vif, sta); + if (sta->tdls && ret == 0) + iwl_mvm_recalc_tdls_state(mvm, vif, true); } else if (old_state == IEEE80211_STA_NONE && new_state == IEEE80211_STA_AUTH) { /* @@ -1946,6 +1971,8 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, } else if (old_state == IEEE80211_STA_NONE && new_state == IEEE80211_STA_NOTEXIST) { ret = iwl_mvm_rm_sta(mvm, vif, sta); + if (sta->tdls) + iwl_mvm_recalc_tdls_state(mvm, vif, false); } else { ret = -EIO; } diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index c75b958736bc..785e5232c757 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -1098,6 +1098,9 @@ void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state); int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *vif, bool added_vif); +/* TDLS */ +int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif); + void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error); #endif /* __IWL_MVM_H__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c index 3b582dd6d77f..2b2d10800a55 100644 --- a/drivers/net/wireless/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/iwlwifi/mvm/power.c @@ -483,6 +483,7 @@ int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm, } struct iwl_power_vifs { + struct iwl_mvm *mvm; struct ieee80211_vif *bf_vif; struct ieee80211_vif *bss_vif; struct ieee80211_vif *p2p_vif; @@ -492,6 +493,8 @@ struct iwl_power_vifs { bool bss_active; bool ap_active; bool monitor_active; + bool bss_tdls; + bool p2p_tdls; }; static void iwl_mvm_power_iterator(void *_data, u8 *mac, @@ -528,6 +531,8 @@ static void iwl_mvm_power_iterator(void *_data, u8 *mac, /* only a single MAC of the same type */ WARN_ON(power_iterator->p2p_vif); power_iterator->p2p_vif = vif; + power_iterator->p2p_tdls = + !!iwl_mvm_tdls_sta_count(power_iterator->mvm, vif); if (mvmvif->phy_ctxt) if (mvmvif->phy_ctxt->id < MAX_PHYS) power_iterator->p2p_active = true; @@ -537,6 +542,8 @@ static void iwl_mvm_power_iterator(void *_data, u8 *mac, /* only a single MAC of the same type */ WARN_ON(power_iterator->bss_vif); power_iterator->bss_vif = vif; + power_iterator->bss_tdls = + !!iwl_mvm_tdls_sta_count(power_iterator->mvm, vif); if (mvmvif->phy_ctxt) if (mvmvif->phy_ctxt->id < MAX_PHYS) power_iterator->bss_active = true; @@ -579,13 +586,15 @@ iwl_mvm_power_set_pm(struct iwl_mvm *mvm, ap_mvmvif = iwl_mvm_vif_from_mac80211(vifs->ap_vif); /* enable PM on bss if bss stand alone */ - if (vifs->bss_active && !vifs->p2p_active && !vifs->ap_active) { + if (vifs->bss_active && !vifs->p2p_active && !vifs->ap_active && + !vifs->bss_tdls) { bss_mvmvif->pm_enabled = true; return; } /* enable PM on p2p if p2p stand alone */ - if (vifs->p2p_active && !vifs->bss_active && !vifs->ap_active) { + if (vifs->p2p_active && !vifs->bss_active && !vifs->ap_active && + !vifs->p2p_tdls) { if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM) p2p_mvmvif->pm_enabled = true; return; @@ -811,7 +820,9 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, int iwl_mvm_power_update_mac(struct iwl_mvm *mvm) { struct iwl_mvm_vif *mvmvif; - struct iwl_power_vifs vifs = {}; + struct iwl_power_vifs vifs = { + .mvm = mvm, + }; bool ba_enable; int ret; From f59e0e3cd8c1d57a1e5e94607b54221eccd3fdf3 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 10 Jun 2014 19:56:27 +0300 Subject: [PATCH 267/274] iwlwifi: mvm: teardown TDLS peers when initiating DCM The FW currently doesn't optimally support TDLS in DCM mode. Teardown all TDLS peers when we have more than a single phy context. Signed-off-by: Arik Nemtsov Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 27 +++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 8aa6fa46c628..2eb6ebee4467 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -1885,6 +1885,28 @@ static void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, iwl_mvm_power_update_mac(mvm); } +static void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm) +{ + struct ieee80211_sta *sta; + struct iwl_mvm_sta *mvmsta; + int i; + + lockdep_assert_held(&mvm->mutex); + + for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], + lockdep_is_held(&mvm->mutex)); + if (!sta || IS_ERR(sta) || !sta->tdls) + continue; + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + ieee80211_tdls_oper_request(mvmsta->vif, sta->addr, + NL80211_TDLS_TEARDOWN, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, + GFP_KERNEL); + } +} + static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -1954,6 +1976,11 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, true); } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTHORIZED) { + + /* we don't support TDLS during DCM */ + if (iwl_mvm_phy_ctx_count(mvm) > 1) + iwl_mvm_teardown_tdls_peers(mvm); + /* enable beacon filtering */ WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); ret = 0; From 9b1fcc11a83504a20de3aa4ef7d300f17e225245 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Thu, 8 May 2014 16:30:24 +0300 Subject: [PATCH 268/274] iwlwifi: mvm: remove 8000 HW family setting of adc sampling on nic config This patch removes the setting of the ADC sampling bits in the mvm nic configuration. This setting is not required by the firmware, and furthermore - it interferes with the DBGC when it is running in DRAM mode on PCIe. Signed-off-by: Liad Kaufman Reviewed-by: Dor Shaish Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/ops.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 89a095691507..7d7b2fbe7cd1 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -166,8 +166,15 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode) WARN_ON((radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE) & ~CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE); - /* silicon bits */ - reg_val |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI; + /* + * TODO: Bits 7-8 of CSR in 8000 HW family set the ADC sampling, and + * shouldn't be set to any non-zero value. The same is supposed to be + * true of the other HW, but unsetting them (such as the 7260) causes + * automatic tests to fail on seemingly unrelated errors. Need to + * further investigate this, but for now we'll separate cases. + */ + if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) + reg_val |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI; iwl_trans_set_bits_mask(mvm->trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH | From 589a6ba46bdf9a4a4817fef011ff61905a1aa244 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 5 Jun 2014 11:32:41 +0300 Subject: [PATCH 269/274] iwlwifi: mvm: minor fix in comment The comment was not accurate, we are talking about the frames *for* the station and not from the station. Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/tx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index f5c0982d297c..e9ff38635c21 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -722,7 +722,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, /* We can't free more than one frame at once on a shared queue */ WARN_ON(skb_freed > 1); - /* If we have still frames from this STA nothing to do here */ + /* If we have still frames for this STA nothing to do here */ if (!atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id])) goto out; From f5c35e15f940a1580c62715adc3e88710753de4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 2 Jul 2014 17:05:35 +0200 Subject: [PATCH 270/274] b43: N-PHY: initialize hardware tables on new devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/tables_nphy.c | 288 ++++++++++++++++++++++++- drivers/net/wireless/b43/tables_nphy.h | 4 + 2 files changed, 281 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/b43/tables_nphy.c b/drivers/net/wireless/b43/tables_nphy.c index b22171592926..dd876857969e 100644 --- a/drivers/net/wireless/b43/tables_nphy.c +++ b/drivers/net/wireless/b43/tables_nphy.c @@ -2146,6 +2146,192 @@ static const u16 b43_ntab_antswctl_r3[4][32] = { } }; +/* static tables, PHY revision >= 7 */ + +/* Copied from brcmsmac (5.75.11) */ +static const u32 b43_ntab_tmap_r7[] = { + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0xf1111110, 0x11111111, 0x11f11111, 0x00000111, + 0x11000000, 0x1111f111, 0x11111111, 0x111111f1, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x000aa888, + 0x88880000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, + 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, + 0xa2222220, 0x22222222, 0x22c22222, 0x00000222, + 0x22000000, 0x2222a222, 0x22222222, 0x222222a2, + 0xf1111110, 0x11111111, 0x11f11111, 0x00011111, + 0x11110000, 0x1111f111, 0x11111111, 0x111111f1, + 0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00088aaa, + 0xaaaa0000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, + 0xaaa8aaa0, 0x8aaa8aaa, 0xaa8a8a8a, 0x000aaa88, + 0x8aaa0000, 0xaaa8a888, 0x8aa88a8a, 0x8a88a888, + 0x08080a00, 0x0a08080a, 0x080a0a08, 0x00080808, + 0x080a0000, 0x080a0808, 0x080a0808, 0x0a0a0a08, + 0xa0a0a0a0, 0x80a0a080, 0x8080a0a0, 0x00008080, + 0x80a00000, 0x80a080a0, 0xa080a0a0, 0x8080a0a0, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x99999000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, + 0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888, + 0x22000000, 0x2222b222, 0x22222222, 0x222222b2, + 0xb2222220, 0x22222222, 0x22d22222, 0x00000222, + 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, + 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, + 0x33000000, 0x3333b333, 0x33333333, 0x333333b3, + 0xb3333330, 0x33333333, 0x33d33333, 0x00000333, + 0x22000000, 0x2222a222, 0x22222222, 0x222222a2, + 0xa2222220, 0x22222222, 0x22c22222, 0x00000222, + 0x99b99b00, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, + 0x9b99bb99, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888, + 0x22222200, 0x2222f222, 0x22222222, 0x222222f2, + 0x22222222, 0x22222222, 0x22f22222, 0x00000222, + 0x11000000, 0x1111f111, 0x11111111, 0x11111111, + 0xf1111111, 0x11111111, 0x11f11111, 0x01111111, + 0xbb9bb900, 0xb9b9bb99, 0xb99bbbbb, 0xbbbb9b9b, + 0xb9bb99bb, 0xb99999b9, 0xb9b9b99b, 0x00000bbb, + 0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, + 0xa8aa88aa, 0xa88888a8, 0xa8a8a88a, 0x0a888aaa, + 0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, + 0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00000aaa, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0xbbbbbb00, 0x999bbbbb, 0x9bb99b9b, 0xb9b9b9bb, + 0xb9b99bbb, 0xb9b9b9bb, 0xb9bb9b99, 0x00000999, + 0x8a000000, 0xaa88a888, 0xa88888aa, 0xa88a8a88, + 0xa88aa88a, 0x88a8aaaa, 0xa8aa8aaa, 0x0888a88a, + 0x0b0b0b00, 0x090b0b0b, 0x0b090b0b, 0x0909090b, + 0x09090b0b, 0x09090b0b, 0x09090b09, 0x00000909, + 0x0a000000, 0x0a080808, 0x080a080a, 0x080a0a08, + 0x080a080a, 0x0808080a, 0x0a0a0a08, 0x0808080a, + 0xb0b0b000, 0x9090b0b0, 0x90b09090, 0xb0b0b090, + 0xb0b090b0, 0x90b0b0b0, 0xb0b09090, 0x00000090, + 0x80000000, 0xa080a080, 0xa08080a0, 0xa0808080, + 0xa080a080, 0x80a0a0a0, 0xa0a080a0, 0x00a0a0a0, + 0x22000000, 0x2222f222, 0x22222222, 0x222222f2, + 0xf2222220, 0x22222222, 0x22f22222, 0x00000222, + 0x11000000, 0x1111f111, 0x11111111, 0x111111f1, + 0xf1111110, 0x11111111, 0x11f11111, 0x00000111, + 0x33000000, 0x3333f333, 0x33333333, 0x333333f3, + 0xf3333330, 0x33333333, 0x33f33333, 0x00000333, + 0x22000000, 0x2222f222, 0x22222222, 0x222222f2, + 0xf2222220, 0x22222222, 0x22f22222, 0x00000222, + 0x99000000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, + 0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x88888000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888, + 0x88a88a00, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x000aa888, + 0x88880000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888, + 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, + 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, + 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, + 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; + +/* Extracted from MMIO dump of 6.30.223.141 */ +static const u32 b43_ntab_noisevar_r7[] = { + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, +}; + /* TX gain tables */ static const u32 b43_ntab_tx_gain_rev0_1_2[] = { 0x03cc2b44, 0x03cc2b42, 0x03cc2a44, 0x03cc2a42, @@ -3031,6 +3217,91 @@ void b43_ntab_write_bulk(struct b43_wldev *dev, u32 offset, b43_ntab_write_bulk(dev, offset, ARRAY_SIZE(data), data); \ } while (0) +static void b43_nphy_tables_init_shared_lut(struct b43_wldev *dev) +{ + ntab_upload(dev, B43_NTAB_C0_ESTPLT_R3, b43_ntab_estimatepowerlt0_r3); + ntab_upload(dev, B43_NTAB_C1_ESTPLT_R3, b43_ntab_estimatepowerlt1_r3); + ntab_upload(dev, B43_NTAB_C0_ADJPLT_R3, b43_ntab_adjustpower0_r3); + ntab_upload(dev, B43_NTAB_C1_ADJPLT_R3, b43_ntab_adjustpower1_r3); + ntab_upload(dev, B43_NTAB_C0_GAINCTL_R3, b43_ntab_gainctl0_r3); + ntab_upload(dev, B43_NTAB_C1_GAINCTL_R3, b43_ntab_gainctl1_r3); + ntab_upload(dev, B43_NTAB_C0_IQLT_R3, b43_ntab_iqlt0_r3); + ntab_upload(dev, B43_NTAB_C1_IQLT_R3, b43_ntab_iqlt1_r3); + ntab_upload(dev, B43_NTAB_C0_LOFEEDTH_R3, b43_ntab_loftlt0_r3); + ntab_upload(dev, B43_NTAB_C1_LOFEEDTH_R3, b43_ntab_loftlt1_r3); +} + +static void b43_nphy_tables_init_rev7_volatile(struct b43_wldev *dev) +{ + struct ssb_sprom *sprom = dev->dev->bus_sprom; + u8 antswlut; + int core, offset, i; + + const int antswlut0_offsets[] = { 0, 4, 8, }; /* Offsets for values */ + const u8 antswlut0_values[][3] = { + { 0x2, 0x12, 0x8 }, /* Core 0 */ + { 0x2, 0x18, 0x2 }, /* Core 1 */ + }; + + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) + antswlut = sprom->fem.ghz5.antswlut; + else + antswlut = sprom->fem.ghz2.antswlut; + + switch (antswlut) { + case 0: + for (core = 0; core < 2; core++) { + for (i = 0; i < ARRAY_SIZE(antswlut0_values[0]); i++) { + offset = core ? 0x20 : 0x00; + offset += antswlut0_offsets[i]; + b43_ntab_write(dev, B43_NTAB8(9, offset), + antswlut0_values[core][i]); + } + } + break; + default: + b43err(dev->wl, "Unsupported antswlut: %d\n", antswlut); + break; + } +} + +static void b43_nphy_tables_init_rev16(struct b43_wldev *dev) +{ + /* Static tables */ + if (dev->phy.do_full_init) { + ntab_upload(dev, B43_NTAB_NOISEVAR_R7, b43_ntab_noisevar_r7); + b43_nphy_tables_init_shared_lut(dev); + } + + /* Volatile tables */ + b43_nphy_tables_init_rev7_volatile(dev); +} + +static void b43_nphy_tables_init_rev7(struct b43_wldev *dev) +{ + /* Static tables */ + if (dev->phy.do_full_init) { + ntab_upload(dev, B43_NTAB_FRAMESTRUCT_R3, b43_ntab_framestruct_r3); + ntab_upload(dev, B43_NTAB_PILOT_R3, b43_ntab_pilot_r3); + ntab_upload(dev, B43_NTAB_TMAP_R7, b43_ntab_tmap_r7); + ntab_upload(dev, B43_NTAB_INTLEVEL_R3, b43_ntab_intlevel_r3); + ntab_upload(dev, B43_NTAB_TDTRN_R3, b43_ntab_tdtrn_r3); + ntab_upload(dev, B43_NTAB_NOISEVAR_R7, b43_ntab_noisevar_r7); + ntab_upload(dev, B43_NTAB_MCS_R3, b43_ntab_mcs_r3); + ntab_upload(dev, B43_NTAB_TDI20A0_R3, b43_ntab_tdi20a0_r3); + ntab_upload(dev, B43_NTAB_TDI20A1_R3, b43_ntab_tdi20a1_r3); + ntab_upload(dev, B43_NTAB_TDI40A0_R3, b43_ntab_tdi40a0_r3); + ntab_upload(dev, B43_NTAB_TDI40A1_R3, b43_ntab_tdi40a1_r3); + ntab_upload(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3); + ntab_upload(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3); + ntab_upload(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3); + b43_nphy_tables_init_shared_lut(dev); + } + + /* Volatile tables */ + b43_nphy_tables_init_rev7_volatile(dev); +} + static void b43_nphy_tables_init_rev3(struct b43_wldev *dev) { struct ssb_sprom *sprom = dev->dev->bus_sprom; @@ -3057,16 +3328,7 @@ static void b43_nphy_tables_init_rev3(struct b43_wldev *dev) ntab_upload(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3); ntab_upload(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3); ntab_upload(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3); - ntab_upload(dev, B43_NTAB_C0_ESTPLT_R3, b43_ntab_estimatepowerlt0_r3); - ntab_upload(dev, B43_NTAB_C1_ESTPLT_R3, b43_ntab_estimatepowerlt1_r3); - ntab_upload(dev, B43_NTAB_C0_ADJPLT_R3, b43_ntab_adjustpower0_r3); - ntab_upload(dev, B43_NTAB_C1_ADJPLT_R3, b43_ntab_adjustpower1_r3); - ntab_upload(dev, B43_NTAB_C0_GAINCTL_R3, b43_ntab_gainctl0_r3); - ntab_upload(dev, B43_NTAB_C1_GAINCTL_R3, b43_ntab_gainctl1_r3); - ntab_upload(dev, B43_NTAB_C0_IQLT_R3, b43_ntab_iqlt0_r3); - ntab_upload(dev, B43_NTAB_C1_IQLT_R3, b43_ntab_iqlt1_r3); - ntab_upload(dev, B43_NTAB_C0_LOFEEDTH_R3, b43_ntab_loftlt0_r3); - ntab_upload(dev, B43_NTAB_C1_LOFEEDTH_R3, b43_ntab_loftlt1_r3); + b43_nphy_tables_init_shared_lut(dev); } /* Volatile tables */ @@ -3115,7 +3377,11 @@ static void b43_nphy_tables_init_rev0(struct b43_wldev *dev) /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/InitTables */ void b43_nphy_tables_init(struct b43_wldev *dev) { - if (dev->phy.rev >= 3) + if (dev->phy.rev >= 16) + b43_nphy_tables_init_rev16(dev); + else if (dev->phy.rev >= 7) + b43_nphy_tables_init_rev7(dev); + else if (dev->phy.rev >= 3) b43_nphy_tables_init_rev3(dev); else b43_nphy_tables_init_rev0(dev); diff --git a/drivers/net/wireless/b43/tables_nphy.h b/drivers/net/wireless/b43/tables_nphy.h index 3a58aee4c4cf..3ce2e6f3a278 100644 --- a/drivers/net/wireless/b43/tables_nphy.h +++ b/drivers/net/wireless/b43/tables_nphy.h @@ -165,6 +165,10 @@ struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent( #define B43_NTAB_C1_LOFEEDTH_R3 B43_NTAB16(27, 448) /* Local Oscillator Feed Through lookup 1 */ #define B43_NTAB_C1_PAPD_COMP_R3 B43_NTAB16(27, 576) +/* Static N-PHY tables, PHY revision >= 7 */ +#define B43_NTAB_TMAP_R7 B43_NTAB32(12, 0) /* TM AP */ +#define B43_NTAB_NOISEVAR_R7 B43_NTAB32(16, 0) /* noise variance */ + #define B43_NTAB_TX_IQLO_CAL_LOFT_LADDER_40_SIZE 18 #define B43_NTAB_TX_IQLO_CAL_LOFT_LADDER_20_SIZE 18 #define B43_NTAB_TX_IQLO_CAL_IQIMB_LADDER_40_SIZE 18 From 7ef5cd240a133efd506c2e80c060b89406700bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 2 Jul 2014 22:42:45 +0200 Subject: [PATCH 271/274] b43: N-PHY: rework names & picking of TX gain tables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows adding more revisions support, spotting lacking tables and unifies naming schema. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/phy_n.c | 12 ++- drivers/net/wireless/b43/tables_nphy.c | 102 ++++++++++++++++++------- 2 files changed, 84 insertions(+), 30 deletions(-) diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index 0a6f04be9073..50ca6f87d5e8 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -3369,7 +3369,11 @@ static void b43_nphy_tx_power_fix(struct b43_wldev *dev) */ for (i = 0; i < 2; i++) { - txgain = *(b43_nphy_get_tx_gain_table(dev) + txpi[i]); + const u32 *table = b43_nphy_get_tx_gain_table(dev); + + if (!table) + break; + txgain = *(table + txpi[i]); if (dev->phy.rev >= 3) radio_gain = (txgain >> 16) & 0x1FFFF; @@ -3783,6 +3787,9 @@ static void b43_nphy_tx_gain_table_upload(struct b43_wldev *dev) int i; table = b43_nphy_get_tx_gain_table(dev); + if (!table) + return; + b43_ntab_write_bulk(dev, B43_NTAB32(26, 192), 128, table); b43_ntab_write_bulk(dev, B43_NTAB32(27, 192), 128, table); @@ -4488,6 +4495,9 @@ static struct nphy_txgains b43_nphy_get_tx_gains(struct b43_wldev *dev) for (i = 0; i < 2; ++i) { table = b43_nphy_get_tx_gain_table(dev); + if (!table) + break; + if (dev->phy.rev >= 3) { target.ipa[i] = (table[index[i]] >> 16) & 0xF; target.pad[i] = (table[index[i]] >> 20) & 0xF; diff --git a/drivers/net/wireless/b43/tables_nphy.c b/drivers/net/wireless/b43/tables_nphy.c index dd876857969e..3d6dda7c4fe8 100644 --- a/drivers/net/wireless/b43/tables_nphy.c +++ b/drivers/net/wireless/b43/tables_nphy.c @@ -2332,7 +2332,10 @@ static const u32 b43_ntab_noisevar_r7[] = { 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, }; -/* TX gain tables */ +/************************************************** + * TX gain tables + **************************************************/ + static const u32 b43_ntab_tx_gain_rev0_1_2[] = { 0x03cc2b44, 0x03cc2b42, 0x03cc2a44, 0x03cc2a42, 0x03cc2944, 0x03c82b44, 0x03c82b42, 0x03c82a44, @@ -2368,7 +2371,9 @@ static const u32 b43_ntab_tx_gain_rev0_1_2[] = { 0x03801442, 0x03801344, 0x03801342, 0x00002b00, }; -static const u32 b43_ntab_tx_gain_rev3plus_2ghz[] = { +/* EPA 2 GHz */ + +static const u32 b43_ntab_tx_gain_epa_rev3_2g[] = { 0x1f410044, 0x1f410042, 0x1f410040, 0x1f41003e, 0x1f41003c, 0x1f41003b, 0x1f410039, 0x1f410037, 0x1e410044, 0x1e410042, 0x1e410040, 0x1e41003e, @@ -2403,7 +2408,9 @@ static const u32 b43_ntab_tx_gain_rev3plus_2ghz[] = { 0x1041003c, 0x1041003b, 0x10410039, 0x10410037, }; -static const u32 b43_ntab_tx_gain_rev3_5ghz[] = { +/* EPA 5 GHz */ + +static const u32 b43_ntab_tx_gain_epa_rev3_5g[] = { 0xcff70044, 0xcff70042, 0xcff70040, 0xcff7003e, 0xcff7003c, 0xcff7003b, 0xcff70039, 0xcff70037, 0xcef70044, 0xcef70042, 0xcef70040, 0xcef7003e, @@ -2438,7 +2445,7 @@ static const u32 b43_ntab_tx_gain_rev3_5ghz[] = { 0xc0f7003c, 0xc0f7003b, 0xc0f70039, 0xc0f70037, }; -static const u32 b43_ntab_tx_gain_rev4_5ghz[] = { +static const u32 b43_ntab_tx_gain_epa_rev4_5g[] = { 0x2ff20044, 0x2ff20042, 0x2ff20040, 0x2ff2003e, 0x2ff2003c, 0x2ff2003b, 0x2ff20039, 0x2ff20037, 0x2ef20044, 0x2ef20042, 0x2ef20040, 0x2ef2003e, @@ -2473,7 +2480,7 @@ static const u32 b43_ntab_tx_gain_rev4_5ghz[] = { 0x20d2003a, 0x20d20038, 0x20d20036, 0x20d20034, }; -static const u32 b43_ntab_tx_gain_rev5plus_5ghz[] = { +static const u32 b43_ntab_tx_gain_epa_rev5_5g[] = { 0x0f62004a, 0x0f620048, 0x0f620046, 0x0f620044, 0x0f620042, 0x0f620040, 0x0f62003e, 0x0f62003c, 0x0e620044, 0x0e620042, 0x0e620040, 0x0e62003e, @@ -2508,7 +2515,9 @@ static const u32 b43_ntab_tx_gain_rev5plus_5ghz[] = { 0x0062003b, 0x00620039, 0x00620037, 0x00620035, }; -static const u32 txpwrctrl_tx_gain_ipa[] = { +/* IPA 2 GHz */ + +static const u32 b43_ntab_tx_gain_ipa_rev3_2g[] = { 0x5ff7002d, 0x5ff7002b, 0x5ff7002a, 0x5ff70029, 0x5ff70028, 0x5ff70027, 0x5ff70026, 0x5ff70025, 0x5ef7002d, 0x5ef7002b, 0x5ef7002a, 0x5ef70029, @@ -2543,7 +2552,7 @@ static const u32 txpwrctrl_tx_gain_ipa[] = { 0x50f70028, 0x50f70027, 0x50f70026, 0x50f70025, }; -static const u32 txpwrctrl_tx_gain_ipa_rev5[] = { +static const u32 b43_ntab_tx_gain_ipa_rev5_2g[] = { 0x1ff7002d, 0x1ff7002b, 0x1ff7002a, 0x1ff70029, 0x1ff70028, 0x1ff70027, 0x1ff70026, 0x1ff70025, 0x1ef7002d, 0x1ef7002b, 0x1ef7002a, 0x1ef70029, @@ -2578,7 +2587,7 @@ static const u32 txpwrctrl_tx_gain_ipa_rev5[] = { 0x10f70028, 0x10f70027, 0x10f70026, 0x10f70025, }; -static const u32 txpwrctrl_tx_gain_ipa_rev6[] = { +static const u32 b43_ntab_tx_gain_ipa_rev6_2g[] = { 0x0ff7002d, 0x0ff7002b, 0x0ff7002a, 0x0ff70029, 0x0ff70028, 0x0ff70027, 0x0ff70026, 0x0ff70025, 0x0ef7002d, 0x0ef7002b, 0x0ef7002a, 0x0ef70029, @@ -2613,7 +2622,9 @@ static const u32 txpwrctrl_tx_gain_ipa_rev6[] = { 0x00f70028, 0x00f70027, 0x00f70026, 0x00f70025, }; -static const u32 txpwrctrl_tx_gain_ipa_5g[] = { +/* IPA 2 5Hz */ + +static const u32 b43_ntab_tx_gain_ipa_rev3_5g[] = { 0x7ff70035, 0x7ff70033, 0x7ff70032, 0x7ff70031, 0x7ff7002f, 0x7ff7002e, 0x7ff7002d, 0x7ff7002b, 0x7ff7002a, 0x7ff70029, 0x7ff70028, 0x7ff70027, @@ -3390,23 +3401,39 @@ void b43_nphy_tables_init(struct b43_wldev *dev) /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GetIpaGainTbl */ static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev) { + struct b43_phy *phy = &dev->phy; + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - if (dev->phy.rev >= 6) { - if (dev->dev->chip_id == 47162) - return txpwrctrl_tx_gain_ipa_rev5; - return txpwrctrl_tx_gain_ipa_rev6; - } else if (dev->phy.rev >= 5) { - return txpwrctrl_tx_gain_ipa_rev5; - } else { - return txpwrctrl_tx_gain_ipa; + switch (phy->rev) { + case 6: + if (dev->dev->chip_id == BCMA_CHIP_ID_BCM47162) + return b43_ntab_tx_gain_ipa_rev5_2g; + return b43_ntab_tx_gain_ipa_rev6_2g; + case 5: + return b43_ntab_tx_gain_ipa_rev5_2g; + case 4: + case 3: + return b43_ntab_tx_gain_ipa_rev3_2g; + default: + b43err(dev->wl, + "No 2GHz IPA gain table available for this device\n"); + return NULL; } } else { - return txpwrctrl_tx_gain_ipa_5g; + switch (phy->rev) { + case 3 ... 6: + return b43_ntab_tx_gain_ipa_rev3_5g; + default: + b43err(dev->wl, + "No 5GHz IPA gain table available for this device\n"); + return NULL; + } } } const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev) { + struct b43_phy *phy = &dev->phy; enum ieee80211_band band = b43_current_band(dev->wl); struct ssb_sprom *sprom = dev->dev->bus_sprom; @@ -3418,19 +3445,36 @@ const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev) (dev->phy.n->ipa5g_on && band == IEEE80211_BAND_5GHZ)) { return b43_nphy_get_ipa_gain_table(dev); } else if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { - if (dev->phy.rev == 3) - return b43_ntab_tx_gain_rev3_5ghz; - if (dev->phy.rev == 4) + switch (phy->rev) { + case 6: + case 5: + return b43_ntab_tx_gain_epa_rev5_5g; + case 4: return sprom->fem.ghz5.extpa_gain == 3 ? - b43_ntab_tx_gain_rev4_5ghz : - b43_ntab_tx_gain_rev4_5ghz; /* FIXME */ - else - return b43_ntab_tx_gain_rev5plus_5ghz; + b43_ntab_tx_gain_epa_rev4_5g : + b43_ntab_tx_gain_epa_rev4_5g; /* FIXME */ + case 3: + return b43_ntab_tx_gain_epa_rev3_5g; + default: + b43err(dev->wl, + "No 5GHz EPA gain table available for this device\n"); + return NULL; + } } else { - if (dev->phy.rev >= 5 && sprom->fem.ghz5.extpa_gain == 3) - return b43_ntab_tx_gain_rev3plus_2ghz; /* FIXME */ - else - return b43_ntab_tx_gain_rev3plus_2ghz; + switch (phy->rev) { + case 6: + case 5: + if (sprom->fem.ghz5.extpa_gain == 3) + return b43_ntab_tx_gain_epa_rev3_2g; /* FIXME */ + /* fall through */ + case 4: + case 3: + return b43_ntab_tx_gain_epa_rev3_2g; + default: + b43err(dev->wl, + "No 2GHz EPA gain table available for this device\n"); + return NULL; + } } } From c4e197195a0c38b2c0928a03ed1de1a4a32f52c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 2 Jul 2014 22:42:46 +0200 Subject: [PATCH 272/274] b43: N-PHY: add TX gains tables for radio 0x2057 rev 9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/tables_nphy.c | 78 ++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/drivers/net/wireless/b43/tables_nphy.c b/drivers/net/wireless/b43/tables_nphy.c index 3d6dda7c4fe8..b28dce950e1f 100644 --- a/drivers/net/wireless/b43/tables_nphy.c +++ b/drivers/net/wireless/b43/tables_nphy.c @@ -2622,6 +2622,42 @@ static const u32 b43_ntab_tx_gain_ipa_rev6_2g[] = { 0x00f70028, 0x00f70027, 0x00f70026, 0x00f70025, }; +/* Extracted from MMIO dump of 6.30.223.141 */ +static const u32 b43_ntab_tx_gain_ipa_2057_rev9_2g[] = { + 0x60ff0031, 0x60e7002c, 0x60cf002a, 0x60c70029, + 0x60b70029, 0x60a70029, 0x609f002a, 0x6097002b, + 0x6087002e, 0x60770031, 0x606f0032, 0x60670034, + 0x60670031, 0x605f0033, 0x605f0031, 0x60570033, + 0x60570030, 0x6057002d, 0x6057002b, 0x604f002d, + 0x604f002b, 0x604f0029, 0x604f0026, 0x60470029, + 0x60470027, 0x603f0029, 0x603f0027, 0x603f0025, + 0x60370029, 0x60370027, 0x60370024, 0x602f002a, + 0x602f0028, 0x602f0026, 0x602f0024, 0x6027002a, + 0x60270028, 0x60270026, 0x60270024, 0x60270022, + 0x601f002b, 0x601f0029, 0x601f0027, 0x601f0024, + 0x601f0022, 0x601f0020, 0x601f001f, 0x601f001d, + 0x60170029, 0x60170027, 0x60170025, 0x60170023, + 0x60170021, 0x6017001f, 0x6017001d, 0x6017001c, + 0x6017001a, 0x60170018, 0x60170018, 0x60170016, + 0x60170015, 0x600f0029, 0x600f0027, 0x600f0025, + 0x600f0023, 0x600f0021, 0x600f001f, 0x600f001d, + 0x600f001c, 0x600f001a, 0x600f0019, 0x600f0018, + 0x600f0016, 0x600f0015, 0x600f0115, 0x600f0215, + 0x600f0315, 0x600f0415, 0x600f0515, 0x600f0615, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, +}; + /* IPA 2 5Hz */ static const u32 b43_ntab_tx_gain_ipa_rev3_5g[] = { @@ -2659,6 +2695,42 @@ static const u32 b43_ntab_tx_gain_ipa_rev3_5g[] = { 0x70f70021, 0x70f70020, 0x70f70020, 0x70f7001f, }; +/* Extracted from MMIO dump of 6.30.223.141 */ +static const u32 b43_ntab_tx_gain_ipa_2057_rev9_5g[] = { + 0x7f7f0053, 0x7f7f004b, 0x7f7f0044, 0x7f7f003f, + 0x7f7f0039, 0x7f7f0035, 0x7f7f0032, 0x7f7f0030, + 0x7f7f002d, 0x7e7f0030, 0x7e7f002d, 0x7d7f0032, + 0x7d7f002f, 0x7d7f002c, 0x7c7f0032, 0x7c7f0030, + 0x7c7f002d, 0x7b7f0030, 0x7b7f002e, 0x7b7f002b, + 0x7a7f0032, 0x7a7f0030, 0x7a7f002d, 0x7a7f002b, + 0x797f0030, 0x797f002e, 0x797f002b, 0x797f0029, + 0x787f0030, 0x787f002d, 0x787f002b, 0x777f0032, + 0x777f0030, 0x777f002d, 0x777f002b, 0x767f0031, + 0x767f002f, 0x767f002c, 0x767f002a, 0x757f0031, + 0x757f002f, 0x757f002c, 0x757f002a, 0x747f0030, + 0x747f002d, 0x747f002b, 0x737f0032, 0x737f002f, + 0x737f002c, 0x737f002a, 0x727f0030, 0x727f002d, + 0x727f002b, 0x727f0029, 0x717f0030, 0x717f002d, + 0x717f002b, 0x707f0031, 0x707f002f, 0x707f002c, + 0x707f002a, 0x707f0027, 0x707f0025, 0x707f0023, + 0x707f0021, 0x707f001f, 0x707f001d, 0x707f001c, + 0x707f001a, 0x707f0019, 0x707f0017, 0x707f0016, + 0x707f0015, 0x707f0014, 0x707f0012, 0x707f0012, + 0x707f0011, 0x707f0010, 0x707f000f, 0x707f000e, + 0x707f000d, 0x707f000d, 0x707f000c, 0x707f000b, + 0x707f000a, 0x707f000a, 0x707f0009, 0x707f0008, + 0x707f0008, 0x707f0008, 0x707f0008, 0x707f0007, + 0x707f0007, 0x707f0006, 0x707f0006, 0x707f0006, + 0x707f0005, 0x707f0005, 0x707f0005, 0x707f0004, + 0x707f0004, 0x707f0004, 0x707f0003, 0x707f0003, + 0x707f0003, 0x707f0003, 0x707f0003, 0x707f0003, + 0x707f0003, 0x707f0003, 0x707f0003, 0x707f0003, + 0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002, + 0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002, + 0x707f0002, 0x707f0001, 0x707f0001, 0x707f0001, + 0x707f0001, 0x707f0001, 0x707f0001, 0x707f0001, +}; + const s8 b43_ntab_papd_pga_gain_delta_ipa_2g[] = { -114, -108, -98, -91, -84, -78, -70, -62, -54, -46, -39, -31, -23, -15, -8, 0 @@ -3405,6 +3477,9 @@ static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev) if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { switch (phy->rev) { + case 16: + if (phy->radio_rev == 9) + return b43_ntab_tx_gain_ipa_2057_rev9_2g; case 6: if (dev->dev->chip_id == BCMA_CHIP_ID_BCM47162) return b43_ntab_tx_gain_ipa_rev5_2g; @@ -3421,6 +3496,9 @@ static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev) } } else { switch (phy->rev) { + case 16: + if (phy->radio_rev == 9) + return b43_ntab_tx_gain_ipa_2057_rev9_5g; case 3 ... 6: return b43_ntab_tx_gain_ipa_rev3_5g; default: From fe5e499f427dadbeeb079e0a796702f4a3da78a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Fri, 4 Jul 2014 09:21:56 +0200 Subject: [PATCH 273/274] b43: fix reading info about radio for new devices (cores 40 & 42) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes b43-phy0: Found Radio: Manuf 0x17F, Version 0x7769, Revision 4 to the b43-phy0: Found Radio: Manuf 0x17F, Version 0x2069, Revision 4 which matches what closed source driver reports: $ wl revinfo radiorev 0x42069000 Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index ca4a19077d7e..b2bc593a6513 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4329,6 +4329,7 @@ static char *b43_phy_name(struct b43_wldev *dev, u8 phy_type) static int b43_phy_versioning(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; + const u8 core_rev = dev->dev->core_rev; u32 tmp; u8 analog_type; u8 phy_type; @@ -4394,7 +4395,15 @@ static int b43_phy_versioning(struct b43_wldev *dev) analog_type, phy_type, b43_phy_name(dev, phy_type), phy_rev); /* Get RADIO versioning */ - if (dev->dev->core_rev >= 24) { + if (core_rev == 40 || core_rev == 42) { + radio_manuf = 0x17F; + + b43_write16(dev, B43_MMIO_RADIO24_CONTROL, 0); + radio_rev = b43_read16(dev, B43_MMIO_RADIO24_DATA); + + b43_write16(dev, B43_MMIO_RADIO24_CONTROL, 1); + radio_ver = b43_read16(dev, B43_MMIO_RADIO24_DATA); + } else if (core_rev >= 24) { u16 radio24[3]; for (tmp = 0; tmp < 3; tmp++) { From f473832fece16611520bf54ad52b16c3f6db0a94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sat, 5 Jul 2014 01:10:41 +0200 Subject: [PATCH 274/274] bcma: add driver for PCIe Gen 2 core MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New Broadcom PCIe devices (802.11ac ones?) use Gen2 and have to be initialized differently. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/bcma/Makefile | 1 + drivers/bcma/driver_pcie2.c | 175 +++++++++++++++++++++++++ drivers/bcma/main.c | 8 ++ include/linux/bcma/bcma.h | 2 + include/linux/bcma/bcma_driver_pcie2.h | 158 ++++++++++++++++++++++ 5 files changed, 344 insertions(+) create mode 100644 drivers/bcma/driver_pcie2.c create mode 100644 include/linux/bcma/bcma_driver_pcie2.h diff --git a/drivers/bcma/Makefile b/drivers/bcma/Makefile index 734b32f09c0a..91290f7f61b8 100644 --- a/drivers/bcma/Makefile +++ b/drivers/bcma/Makefile @@ -3,6 +3,7 @@ bcma-y += driver_chipcommon.o driver_chipcommon_pmu.o bcma-$(CONFIG_BCMA_SFLASH) += driver_chipcommon_sflash.o bcma-$(CONFIG_BCMA_NFLASH) += driver_chipcommon_nflash.o bcma-y += driver_pci.o +bcma-y += driver_pcie2.o bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE) += driver_pci_host.o bcma-$(CONFIG_BCMA_DRIVER_MIPS) += driver_mips.o bcma-$(CONFIG_BCMA_DRIVER_GMAC_CMN) += driver_gmac_cmn.o diff --git a/drivers/bcma/driver_pcie2.c b/drivers/bcma/driver_pcie2.c new file mode 100644 index 000000000000..e4be537b0c66 --- /dev/null +++ b/drivers/bcma/driver_pcie2.c @@ -0,0 +1,175 @@ +/* + * Broadcom specific AMBA + * PCIe Gen 2 Core + * + * Copyright 2014, Broadcom Corporation + * Copyright 2014, Rafał Miłecki + * + * Licensed under the GNU/GPL. See COPYING for details. + */ + +#include "bcma_private.h" +#include + +/************************************************** + * R/W ops. + **************************************************/ + +#if 0 +static u32 bcma_core_pcie2_cfg_read(struct bcma_drv_pcie2 *pcie2, u32 addr) +{ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, addr); + pcie2_read32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR); + return pcie2_read32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA); +} +#endif + +static void bcma_core_pcie2_cfg_write(struct bcma_drv_pcie2 *pcie2, u32 addr, + u32 val) +{ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, addr); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, val); +} + +/************************************************** + * Init. + **************************************************/ + +static u32 bcma_core_pcie2_war_delay_perst_enab(struct bcma_drv_pcie2 *pcie2, + bool enable) +{ + u32 val; + + /* restore back to default */ + val = pcie2_read32(pcie2, BCMA_CORE_PCIE2_CLK_CONTROL); + val |= PCIE2_CLKC_DLYPERST; + val &= ~PCIE2_CLKC_DISSPROMLD; + if (enable) { + val &= ~PCIE2_CLKC_DLYPERST; + val |= PCIE2_CLKC_DISSPROMLD; + } + pcie2_write32(pcie2, (BCMA_CORE_PCIE2_CLK_CONTROL), val); + /* flush */ + return pcie2_read32(pcie2, BCMA_CORE_PCIE2_CLK_CONTROL); +} + +static void bcma_core_pcie2_set_ltr_vals(struct bcma_drv_pcie2 *pcie2) +{ + /* LTR0 */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, 0x844); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x883c883c); + /* LTR1 */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, 0x848); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x88648864); + /* LTR2 */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, 0x84C); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x90039003); +} + +static void bcma_core_pcie2_hw_ltr_war(struct bcma_drv_pcie2 *pcie2) +{ + u8 core_rev = pcie2->core->id.rev; + u32 devstsctr2; + + if (core_rev < 2 || core_rev == 10 || core_rev > 13) + return; + + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, + PCIE2_CAP_DEVSTSCTRL2_OFFSET); + devstsctr2 = pcie2_read32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA); + if (devstsctr2 & PCIE2_CAP_DEVSTSCTRL2_LTRENAB) { + /* force the right LTR values */ + bcma_core_pcie2_set_ltr_vals(pcie2); + + /* TODO: + si_core_wrapperreg(pcie2, 3, 0x60, 0x8080, 0); */ + + /* enable the LTR */ + devstsctr2 |= PCIE2_CAP_DEVSTSCTRL2_LTRENAB; + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, + PCIE2_CAP_DEVSTSCTRL2_OFFSET); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, devstsctr2); + + /* set the LTR state to be active */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_LTR_STATE, + PCIE2_LTR_ACTIVE); + usleep_range(1000, 2000); + + /* set the LTR state to be sleep */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_LTR_STATE, + PCIE2_LTR_SLEEP); + usleep_range(1000, 2000); + } +} + +static void pciedev_crwlpciegen2(struct bcma_drv_pcie2 *pcie2) +{ + u8 core_rev = pcie2->core->id.rev; + bool pciewar160, pciewar162; + + pciewar160 = core_rev == 7 || core_rev == 9 || core_rev == 11; + pciewar162 = core_rev == 5 || core_rev == 7 || core_rev == 8 || + core_rev == 9 || core_rev == 11; + + if (!pciewar160 && !pciewar162) + return; + +/* TODO */ +#if 0 + pcie2_set32(pcie2, BCMA_CORE_PCIE2_CLK_CONTROL, + PCIE_DISABLE_L1CLK_GATING); +#if 0 + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, + PCIEGEN2_COE_PVT_TL_CTRL_0); + pcie2_mask32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, + ~(1 << COE_PVT_TL_CTRL_0_PM_DIS_L1_REENTRY_BIT)); +#endif +#endif +} + +static void pciedev_crwlpciegen2_180(struct bcma_drv_pcie2 *pcie2) +{ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, PCIE2_PMCR_REFUP); + pcie2_set32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x1f); +} + +static void pciedev_crwlpciegen2_182(struct bcma_drv_pcie2 *pcie2) +{ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, PCIE2_SBMBX); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 1 << 0); +} + +static void pciedev_reg_pm_clk_period(struct bcma_drv_pcie2 *pcie2) +{ + struct bcma_drv_cc *drv_cc = &pcie2->core->bus->drv_cc; + u8 core_rev = pcie2->core->id.rev; + u32 alp_khz, pm_value; + + if (core_rev <= 13) { + alp_khz = bcma_pmu_get_alp_clock(drv_cc) / 1000; + pm_value = (1000000 * 2) / alp_khz; + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, + PCIE2_PVT_REG_PM_CLK_PERIOD); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, pm_value); + } +} + +void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2) +{ + struct bcma_chipinfo *ci = &pcie2->core->bus->chipinfo; + u32 tmp; + + tmp = pcie2_read32(pcie2, BCMA_CORE_PCIE2_SPROM(54)); + if ((tmp & 0xe) >> 1 == 2) + bcma_core_pcie2_cfg_write(pcie2, 0x4e0, 0x17); + + /* TODO: Do we need pcie_reqsize? */ + + if (ci->id == BCMA_CHIP_ID_BCM4360 && ci->rev > 3) + bcma_core_pcie2_war_delay_perst_enab(pcie2, true); + bcma_core_pcie2_hw_ltr_war(pcie2); + pciedev_crwlpciegen2(pcie2); + pciedev_reg_pm_clk_period(pcie2); + pciedev_crwlpciegen2_180(pcie2); + pciedev_crwlpciegen2_182(pcie2); +} diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index 34ea4c588d36..0ff8d58831ef 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -132,6 +132,7 @@ static int bcma_register_cores(struct bcma_bus *bus) case BCMA_CORE_CHIPCOMMON: case BCMA_CORE_PCI: case BCMA_CORE_PCIE: + case BCMA_CORE_PCIE2: case BCMA_CORE_MIPS_74K: case BCMA_CORE_4706_MAC_GBIT_COMMON: continue; @@ -281,6 +282,13 @@ int bcma_bus_register(struct bcma_bus *bus) bcma_core_pci_init(&bus->drv_pci[1]); } + /* Init PCIe Gen 2 core */ + core = bcma_find_core_unit(bus, BCMA_CORE_PCIE2, 0); + if (core) { + bus->drv_pcie2.core = core; + bcma_core_pcie2_init(&bus->drv_pcie2); + } + /* Init GBIT MAC COMMON core */ core = bcma_find_core(bus, BCMA_CORE_4706_MAC_GBIT_COMMON); if (core) { diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 0b3bb16c705a..452286a38b2b 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include /* SPROM sharing */ @@ -333,6 +334,7 @@ struct bcma_bus { struct bcma_drv_cc drv_cc; struct bcma_drv_pci drv_pci[2]; + struct bcma_drv_pcie2 drv_pcie2; struct bcma_drv_mips drv_mips; struct bcma_drv_gmac_cmn drv_gmac_cmn; diff --git a/include/linux/bcma/bcma_driver_pcie2.h b/include/linux/bcma/bcma_driver_pcie2.h new file mode 100644 index 000000000000..5988b05781c3 --- /dev/null +++ b/include/linux/bcma/bcma_driver_pcie2.h @@ -0,0 +1,158 @@ +#ifndef LINUX_BCMA_DRIVER_PCIE2_H_ +#define LINUX_BCMA_DRIVER_PCIE2_H_ + +#define BCMA_CORE_PCIE2_CLK_CONTROL 0x0000 +#define PCIE2_CLKC_RST_OE 0x0001 /* When set, drives PCI_RESET out to pin */ +#define PCIE2_CLKC_RST 0x0002 /* Value driven out to pin */ +#define PCIE2_CLKC_SPERST 0x0004 /* SurvivePeRst */ +#define PCIE2_CLKC_DISABLE_L1CLK_GATING 0x0010 +#define PCIE2_CLKC_DLYPERST 0x0100 /* Delay PeRst to CoE Core */ +#define PCIE2_CLKC_DISSPROMLD 0x0200 /* DisableSpromLoadOnPerst */ +#define PCIE2_CLKC_WAKE_MODE_L2 0x1000 /* Wake on L2 */ +#define BCMA_CORE_PCIE2_RC_PM_CONTROL 0x0004 +#define BCMA_CORE_PCIE2_RC_PM_STATUS 0x0008 +#define BCMA_CORE_PCIE2_EP_PM_CONTROL 0x000C +#define BCMA_CORE_PCIE2_EP_PM_STATUS 0x0010 +#define BCMA_CORE_PCIE2_EP_LTR_CONTROL 0x0014 +#define BCMA_CORE_PCIE2_EP_LTR_STATUS 0x0018 +#define BCMA_CORE_PCIE2_EP_OBFF_STATUS 0x001C +#define BCMA_CORE_PCIE2_PCIE_ERR_STATUS 0x0020 +#define BCMA_CORE_PCIE2_RC_AXI_CONFIG 0x0100 +#define BCMA_CORE_PCIE2_EP_AXI_CONFIG 0x0104 +#define BCMA_CORE_PCIE2_RXDEBUG_STATUS0 0x0108 +#define BCMA_CORE_PCIE2_RXDEBUG_CONTROL0 0x010C +#define BCMA_CORE_PCIE2_CONFIGINDADDR 0x0120 +#define BCMA_CORE_PCIE2_CONFIGINDDATA 0x0124 +#define BCMA_CORE_PCIE2_MDIOCONTROL 0x0128 +#define BCMA_CORE_PCIE2_MDIOWRDATA 0x012C +#define BCMA_CORE_PCIE2_MDIORDDATA 0x0130 +#define BCMA_CORE_PCIE2_DATAINTF 0x0180 +#define BCMA_CORE_PCIE2_D2H_INTRLAZY_0 0x0188 +#define BCMA_CORE_PCIE2_H2D_INTRLAZY_0 0x018c +#define BCMA_CORE_PCIE2_H2D_INTSTAT_0 0x0190 +#define BCMA_CORE_PCIE2_H2D_INTMASK_0 0x0194 +#define BCMA_CORE_PCIE2_D2H_INTSTAT_0 0x0198 +#define BCMA_CORE_PCIE2_D2H_INTMASK_0 0x019c +#define BCMA_CORE_PCIE2_LTR_STATE 0x01A0 /* Latency Tolerance Reporting */ +#define PCIE2_LTR_ACTIVE 2 +#define PCIE2_LTR_ACTIVE_IDLE 1 +#define PCIE2_LTR_SLEEP 0 +#define PCIE2_LTR_FINAL_MASK 0x300 +#define PCIE2_LTR_FINAL_SHIFT 8 +#define BCMA_CORE_PCIE2_PWR_INT_STATUS 0x01A4 +#define BCMA_CORE_PCIE2_PWR_INT_MASK 0x01A8 +#define BCMA_CORE_PCIE2_CFG_ADDR 0x01F8 +#define BCMA_CORE_PCIE2_CFG_DATA 0x01FC +#define BCMA_CORE_PCIE2_SYS_EQ_PAGE 0x0200 +#define BCMA_CORE_PCIE2_SYS_MSI_PAGE 0x0204 +#define BCMA_CORE_PCIE2_SYS_MSI_INTREN 0x0208 +#define BCMA_CORE_PCIE2_SYS_MSI_CTRL0 0x0210 +#define BCMA_CORE_PCIE2_SYS_MSI_CTRL1 0x0214 +#define BCMA_CORE_PCIE2_SYS_MSI_CTRL2 0x0218 +#define BCMA_CORE_PCIE2_SYS_MSI_CTRL3 0x021C +#define BCMA_CORE_PCIE2_SYS_MSI_CTRL4 0x0220 +#define BCMA_CORE_PCIE2_SYS_MSI_CTRL5 0x0224 +#define BCMA_CORE_PCIE2_SYS_EQ_HEAD0 0x0250 +#define BCMA_CORE_PCIE2_SYS_EQ_TAIL0 0x0254 +#define BCMA_CORE_PCIE2_SYS_EQ_HEAD1 0x0258 +#define BCMA_CORE_PCIE2_SYS_EQ_TAIL1 0x025C +#define BCMA_CORE_PCIE2_SYS_EQ_HEAD2 0x0260 +#define BCMA_CORE_PCIE2_SYS_EQ_TAIL2 0x0264 +#define BCMA_CORE_PCIE2_SYS_EQ_HEAD3 0x0268 +#define BCMA_CORE_PCIE2_SYS_EQ_TAIL3 0x026C +#define BCMA_CORE_PCIE2_SYS_EQ_HEAD4 0x0270 +#define BCMA_CORE_PCIE2_SYS_EQ_TAIL4 0x0274 +#define BCMA_CORE_PCIE2_SYS_EQ_HEAD5 0x0278 +#define BCMA_CORE_PCIE2_SYS_EQ_TAIL5 0x027C +#define BCMA_CORE_PCIE2_SYS_RC_INTX_EN 0x0330 +#define BCMA_CORE_PCIE2_SYS_RC_INTX_CSR 0x0334 +#define BCMA_CORE_PCIE2_SYS_MSI_REQ 0x0340 +#define BCMA_CORE_PCIE2_SYS_HOST_INTR_EN 0x0344 +#define BCMA_CORE_PCIE2_SYS_HOST_INTR_CSR 0x0348 +#define BCMA_CORE_PCIE2_SYS_HOST_INTR0 0x0350 +#define BCMA_CORE_PCIE2_SYS_HOST_INTR1 0x0354 +#define BCMA_CORE_PCIE2_SYS_HOST_INTR2 0x0358 +#define BCMA_CORE_PCIE2_SYS_HOST_INTR3 0x035C +#define BCMA_CORE_PCIE2_SYS_EP_INT_EN0 0x0360 +#define BCMA_CORE_PCIE2_SYS_EP_INT_EN1 0x0364 +#define BCMA_CORE_PCIE2_SYS_EP_INT_CSR0 0x0370 +#define BCMA_CORE_PCIE2_SYS_EP_INT_CSR1 0x0374 +#define BCMA_CORE_PCIE2_SPROM(wordoffset) (0x0800 + ((wordoffset) * 2)) +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_0 0x0C00 +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_1 0x0C04 +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_2 0x0C08 +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_3 0x0C0C +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_4 0x0C10 +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_5 0x0C14 +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_6 0x0C18 +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_7 0x0C1C +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_0 0x0C20 +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_1 0x0C24 +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_2 0x0C28 +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_3 0x0C2C +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_4 0x0C30 +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_5 0x0C34 +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_6 0x0C38 +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_7 0x0C3C +#define BCMA_CORE_PCIE2_FUNC0_IMAP1 0x0C80 +#define BCMA_CORE_PCIE2_FUNC1_IMAP1 0x0C88 +#define BCMA_CORE_PCIE2_FUNC0_IMAP2 0x0CC0 +#define BCMA_CORE_PCIE2_FUNC1_IMAP2 0x0CC8 +#define BCMA_CORE_PCIE2_IARR0_LOWER 0x0D00 +#define BCMA_CORE_PCIE2_IARR0_UPPER 0x0D04 +#define BCMA_CORE_PCIE2_IARR1_LOWER 0x0D08 +#define BCMA_CORE_PCIE2_IARR1_UPPER 0x0D0C +#define BCMA_CORE_PCIE2_IARR2_LOWER 0x0D10 +#define BCMA_CORE_PCIE2_IARR2_UPPER 0x0D14 +#define BCMA_CORE_PCIE2_OARR0 0x0D20 +#define BCMA_CORE_PCIE2_OARR1 0x0D28 +#define BCMA_CORE_PCIE2_OARR2 0x0D30 +#define BCMA_CORE_PCIE2_OMAP0_LOWER 0x0D40 +#define BCMA_CORE_PCIE2_OMAP0_UPPER 0x0D44 +#define BCMA_CORE_PCIE2_OMAP1_LOWER 0x0D48 +#define BCMA_CORE_PCIE2_OMAP1_UPPER 0x0D4C +#define BCMA_CORE_PCIE2_OMAP2_LOWER 0x0D50 +#define BCMA_CORE_PCIE2_OMAP2_UPPER 0x0D54 +#define BCMA_CORE_PCIE2_FUNC1_IARR1_SIZE 0x0D58 +#define BCMA_CORE_PCIE2_FUNC1_IARR2_SIZE 0x0D5C +#define BCMA_CORE_PCIE2_MEM_CONTROL 0x0F00 +#define BCMA_CORE_PCIE2_MEM_ECC_ERRLOG0 0x0F04 +#define BCMA_CORE_PCIE2_MEM_ECC_ERRLOG1 0x0F08 +#define BCMA_CORE_PCIE2_LINK_STATUS 0x0F0C +#define BCMA_CORE_PCIE2_STRAP_STATUS 0x0F10 +#define BCMA_CORE_PCIE2_RESET_STATUS 0x0F14 +#define BCMA_CORE_PCIE2_RESETEN_IN_LINKDOWN 0x0F18 +#define BCMA_CORE_PCIE2_MISC_INTR_EN 0x0F1C +#define BCMA_CORE_PCIE2_TX_DEBUG_CFG 0x0F20 +#define BCMA_CORE_PCIE2_MISC_CONFIG 0x0F24 +#define BCMA_CORE_PCIE2_MISC_STATUS 0x0F28 +#define BCMA_CORE_PCIE2_INTR_EN 0x0F30 +#define BCMA_CORE_PCIE2_INTR_CLEAR 0x0F34 +#define BCMA_CORE_PCIE2_INTR_STATUS 0x0F38 + +/* PCIE gen2 config regs */ +#define PCIE2_INTSTATUS 0x090 +#define PCIE2_INTMASK 0x094 +#define PCIE2_SBMBX 0x098 + +#define PCIE2_PMCR_REFUP 0x1814 /* Trefup time */ + +#define PCIE2_CAP_DEVSTSCTRL2_OFFSET 0xD4 +#define PCIE2_CAP_DEVSTSCTRL2_LTRENAB 0x400 +#define PCIE2_PVT_REG_PM_CLK_PERIOD 0x184c + +struct bcma_drv_pcie2 { + struct bcma_device *core; +}; + +#define pcie2_read16(pcie2, offset) bcma_read16((pcie2)->core, offset) +#define pcie2_read32(pcie2, offset) bcma_read32((pcie2)->core, offset) +#define pcie2_write16(pcie2, offset, val) bcma_write16((pcie2)->core, offset, val) +#define pcie2_write32(pcie2, offset, val) bcma_write32((pcie2)->core, offset, val) + +#define pcie2_set32(pcie2, offset, set) bcma_set32((pcie2)->core, offset, set) +#define pcie2_mask32(pcie2, offset, mask) bcma_mask32((pcie2)->core, offset, mask) + +void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2); + +#endif /* LINUX_BCMA_DRIVER_PCIE2_H_ */