diff options
Diffstat (limited to 'drivers')
194 files changed, 5533 insertions, 1555 deletions
diff --git a/drivers/bcma/driver_gpio.c b/drivers/bcma/driver_gpio.c index d7f81ad56b8..aec9f850b4a 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: diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index d48776e4f34..334c2ece855 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/ath.h b/drivers/net/wireless/ath/ath.h index a889fd66fc6..fd9e5305e77 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/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index a21080028c5..b8314a53497 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/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index 74bd54d6ace..85316bb3f8c 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 4b18434ba69..8ad2550bce7 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) @@ -1457,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) { /* @@ -1480,8 +1498,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 afb23b3cc7b..b65c38fdaa4 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); } diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 8fcd586d1c3..6b4020a5798 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/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index 235053ba773..80c6eacbda5 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); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index ec1da0cc25f..ddef9eedbac 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/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 8927fc34d84..542a8d51d3b 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)) { /* diff --git a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h index 8e5c3b9786e..812a9d787bf 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}, @@ -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/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 2ca8f7e0617..11b5e4dd629 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -22,6 +22,7 @@ #include <linux/interrupt.h> #include <linux/leds.h> #include <linux/completion.h> +#include <linux/time.h> #include "common.h" #include "debug.h" @@ -35,10 +36,7 @@ extern struct ieee80211_ops ath9k_ops; extern int ath9k_modparam_nohwcrypt; extern int led_blink; extern bool is_ath9k_unloaded; - -struct ath_config { - u16 txpowlimit; -}; +extern int ath9k_use_chanctx; /*************************/ /* Descriptor Management */ @@ -167,7 +165,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; @@ -280,8 +277,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; }; @@ -325,6 +323,116 @@ struct ath_rx { u32 ampdu_ref; }; +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; + + struct ath_beacon_config beacon; + struct ath9k_hw_cal_data caldata; + struct timespec tsf_ts; + u64 tsf_val; + u32 last_beacon; + + u16 txpower; + bool offchannel; + 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, + 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 { + ATH_CHANCTX_STATE_IDLE, + 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 { + bool beacon_pending; + bool offchannel_pending; + enum ath_chanctx_state state; + u8 beacon_miss; + + u32 next_tbtt; + 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 { + ATH_OFFCHANNEL_IDLE, + 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 { + 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; + 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]; \ + ctx <= &sc->chanctx[ARRAY_SIZE(sc->chanctx) - 1]; \ + 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) +{ + 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); +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, + 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); bool ath_stoprecv(struct ath_softc *sc); u32 ath_calcrxfilter(struct ath_softc *sc); @@ -341,6 +449,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); @@ -370,32 +479,47 @@ 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; + + /* P2P GO */ + u8 noa_index; + u32 offchannel_start; + u32 offchannel_duration; + + u32 periodic_noa_start; + u32 periodic_noa_duration; }; 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 */ @@ -458,6 +582,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); @@ -473,6 +598,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 */ @@ -702,6 +828,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; @@ -720,6 +848,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; @@ -738,23 +867,27 @@ 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; + 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; + struct ath_offchannel offchannel; + struct ath_chanctx_sched sched; + #ifdef CONFIG_MAC80211_LEDS bool led_registered; char led_name[32]; struct led_classdev led_cdev; #endif - struct ath9k_hw_cal_data caldata; - #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 e387f0b2954..eaf8f058c15 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; @@ -108,6 +108,55 @@ 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, noa_desc, i = 0; + u8 *hdr; + + 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; + hdr[7] = noa_len; + + noa = (void *) skb_put(skb, noa_len); + memset(noa, 0, noa_len); + + noa->index = avp->noa_index; + 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, struct ieee80211_vif *vif) { @@ -155,6 +204,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))) { @@ -249,7 +301,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,8 +329,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_beacon_conf; struct ath_vif *avp = (void *)vif->drv_priv; + struct ath_beacon_config *cur_conf = &avp->chanctx->beacon; u32 tsfadjust; if (avp->av_bslot == 0) @@ -374,12 +426,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) { @@ -500,7 +559,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 +572,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 +583,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_beacon_conf; + struct ath_beacon_config *cur_conf = &ctx->beacon; ath_dbg(common, BEACON, "Caching beacon data for BSS: %pM\n", bss_conf->bssid); @@ -564,20 +623,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_beacon_conf; 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 +661,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. @@ -631,7 +702,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 new file mode 100644 index 00000000000..ba214ebdcd1 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -0,0 +1,685 @@ +/* + * 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; +} + +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_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_vif *avp; + bool active = false; + u8 n_active = 0; + + 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; + + ath_for_each_chanctx(sc, ctx) { + if (!ctx->assigned || list_empty(&ctx->vifs)) + continue; + n_active++; + } + + 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 +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; +} + +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; +} + +static void ath_chanctx_set_next(struct ath_softc *sc, bool force) +{ + struct timespec ts; + bool measure_time = false; + bool send_ps = false; + + spin_lock_bh(&sc->chan_lock); + if (!sc->next_chan) { + spin_unlock_bh(&sc->chan_lock); + return; + } + + if (!force && ath_chanctx_defer_switch(sc)) { + spin_unlock_bh(&sc->chan_lock); + 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)) + __ath9k_flush(sc->hw, BIT(IEEE80211_AC_VO), false); + + 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; + sc->next_chan = NULL; + 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); + + if (sc->sc_ah->chip_fullsleep || + memcmp(&sc->cur_chandef, &sc->cur_chan->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); + + ath_offchannel_channel_change(sc); + ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_SWITCH); +} + +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); +} + +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, j; + + 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); + ctx->txpower = ATH_TXPOWER_MAX; + for (j = 0; j < ARRAY_SIZE(ctx->acq); j++) + INIT_LIST_HEAD(&ctx->acq[j]); + } + 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 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) +{ + 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; + + 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); +} + +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); +} + +struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc, bool active) +{ + struct ath_chanctx *ctx; + + ath_for_each_chanctx(sc, ctx) { + if (!ctx->assigned || list_empty(&ctx->vifs)) + continue; + if (active && !ctx->active) + continue; + + if (ctx->switch_after_beacon) + return ctx; + } + + 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); +} + +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_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) +{ + 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; + u32 beacon_int; + 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 (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; + + sc->sched.beacon_pending = true; + 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 = sc->sched.next_tbtt + beacon_int / 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 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; + 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) + break; + + sc->sched.beacon_pending = false; + if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON) + break; + + sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER; + 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) + 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; + case ATH_CHANCTX_EVENT_BEACON_RECEIVED: + if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) || + sc->cur_chan == &sc->offchannel.chan) + break; + + 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); + + + ath_chanctx_setup_timer(sc, tsf_time); + 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); + cur_conf = &sc->cur_chan->beacon; + + sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER; + + 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; + + ath_chanctx_setup_timer(sc, tsf_time); + sc->sched.beacon_pending = true; + 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/common-beacon.c b/drivers/net/wireless/ath/ath9k/common-beacon.c index 775d1d20ce0..733be517848 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; diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 6cc42be48d4..ce073e995df 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); - - 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); + 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: 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); @@ -1080,7 +1086,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/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 2a8ed8375ec..fd0158fdf14 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; } } @@ -1730,6 +1731,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 +1757,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 +1802,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 +1834,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 0acd4b5a489..51b4ebe04c0 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/init.c b/drivers/net/wireless/ath/ath9k/init.c index 0246b990fe8..39419ea845c 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"); @@ -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; @@ -511,6 +510,9 @@ 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 (!ath9k_use_chanctx) + sc->cur_chan->hw_queue_base = 0; if (!pdata || pdata->use_eeprom) { ah->ah_flags |= AH_USE_EEPROM; @@ -556,6 +558,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, @@ -564,7 +567,11 @@ 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); + 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 @@ -599,6 +606,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); @@ -664,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 @@ -672,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, @@ -712,6 +736,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) @@ -739,12 +764,21 @@ 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; + } else { + 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; + hw->chanctx_data_size = sizeof(void *); + hw->extra_beacon_tailroom = + sizeof(struct ieee80211_p2p_noa_attr) + 9; + } } hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; @@ -756,9 +790,14 @@ 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_listen_interval = 10; hw->max_rate_tries = 10; hw->sta_data_size = sizeof(struct ath_node); hw->vif_data_size = sizeof(struct ath_vif); diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index 72a715fe8f2..2343f56e649 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; @@ -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; @@ -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/main.c b/drivers/net/wireless/ath/ath9k/main.c index 62ac95d6bb9..e6ac8d2e610 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) { /* @@ -63,9 +60,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; } @@ -227,13 +231,22 @@ 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); - ath9k_hw_enable_interrupts(ah); + ath9k_calculate_summary_state(sc, sc->cur_chan); + + 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 (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) && start) { if (!test_bit(ATH_OP_BEACONS, &common->op_flags)) goto work; @@ -247,26 +260,35 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) } work: ath_restart_work(sc); + ath_txq_schedule_all(sc); + } - for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { - if (!ATH_TXQ_SETUP(sc, i)) - continue; + sc->gtt_cnt = 0; - 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); + ath9k_hw_set_interrupts(ah); + ath9k_hw_enable_interrupts(ah); + + 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); } - sc->gtt_cnt = 0; - ieee80211_wake_queues(sc->hw); - ath9k_p2p_ps_timer(sc); 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,9 +301,9 @@ 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; + caldata = &sc->cur_chan->caldata; } if (!hchan) { @@ -292,6 +314,10 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan) if (!ath_prepare_reset(sc)) fastcc = false; + 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); @@ -307,7 +333,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 +346,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) { @@ -712,7 +646,8 @@ 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; @@ -723,7 +658,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); + init_channel = ath9k_cmn_get_channel(hw, ah, &ctx->chandef); + sc->cur_chandef = hw->conf.chandef; /* Reset SERDES registers */ ath9k_hw_configpcipowersave(ah, false); @@ -886,6 +822,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); @@ -934,7 +871,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); @@ -979,18 +917,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++; @@ -1000,26 +949,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 @@ -1028,29 +963,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; + + list_for_each_entry(avp, &ctx->vifs, list) + ath9k_vif_iter(iter_data, avp->vif->addr, avp->vif); + + if (ctx == &sc->offchannel.chan) { + struct ieee80211_vif *vif; + + 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; - if (vif) - ath9k_vif_iter(iter_data, vif->addr, vif); + 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); - /* 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 (changed) { + common->last_rssi = ATH_RSSI_DUMMY_MARKER; + sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER; - memcpy(common->macaddr, iter_data->hw_macaddr, ETH_ALEN); + 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); @@ -1073,24 +1059,57 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw, 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) { + 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, @@ -1101,6 +1120,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); @@ -1115,14 +1135,20 @@ 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) { + 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; @@ -1141,6 +1167,8 @@ 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); @@ -1157,13 +1185,19 @@ 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); + 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); return 0; } @@ -1211,14 +1245,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); @@ -1345,7 +1377,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; - bool reset_channel = false; + struct ath_chanctx *ctx = sc->cur_chan; ath9k_ps_wakeup(sc); mutex_lock(&sc->mutex); @@ -1361,7 +1393,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); } } @@ -1391,20 +1423,16 @@ 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) { - ath_err(common, "Unable to set channel\n"); - mutex_unlock(&sc->mutex); - ath9k_ps_restore(sc); - return -EINVAL; - } + 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); } 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); @@ -1659,58 +1687,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; @@ -1720,7 +1696,11 @@ void ath9k_p2p_ps_timer(void *priv) struct ath_node *an; u32 tsf; - if (!avp) + 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); + + if (!avp || avp->chanctx != sc->cur_chan) return; tsf = ath9k_hw_gettsf32(sc->sc_ah); @@ -1795,26 +1775,9 @@ 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); - } + 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) { @@ -1824,10 +1787,15 @@ 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); + } - 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 @@ -2032,23 +2000,30 @@ 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; struct ath_common *common = ath9k_hw_common(ah); int timeout = HZ / 5; /* 200 ms */ bool drain_txq; + int i; - 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; } @@ -2066,11 +2041,13 @@ static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 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); - mutex_unlock(&sc->mutex); } static bool ath9k_tx_frames_pending(struct ieee80211_hw *hw) @@ -2230,6 +2207,403 @@ 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) +{ + 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, false), + NULL); + return; + } + + 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); +} + +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.duration = sc->offchannel.roc_duration; + 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, false), + 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); + + 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); +} + +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; + + 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 + sc->offchannel.duration); +} + +void ath_offchannel_channel_change(struct ath_softc *sc) +{ + 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; + + 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.duration)); + ieee80211_ready_on_channel(sc->hw); + break; + case ATH_OFFCHANNEL_ROC_DONE: + ath_roc_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; + + 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); + mod_timer(&sc->offchannel.timer, jiffies + HZ / 10); + break; + } + /* 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: + ctx = ath_chanctx_get_oper_chan(sc, false); + sc->offchannel.state = ATH_OFFCHANNEL_ROC_DONE; + ath_chanctx_switch(sc, ctx, NULL); + break; + default: + break; + } +} + +static int ath9k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_scan_request *hw_req) +{ + struct cfg80211_scan_request *req = &hw_req->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; + + if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE) + ath_offchannel_next(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); +} + +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; +} + +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 pos; + + mutex_lock(&sc->mutex); + + ath_for_each_chanctx(sc, ctx) { + if (ctx->assigned) + continue; + + 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; + } + mutex_unlock(&sc->mutex); + return -ENOSPC; +} + + +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; + ctx->hw_queue_base = -1; + ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_UNASSIGN); + 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); + 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; +} + +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); + 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); +} + +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; + 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; + ath9k_ops.mgd_prepare_tx = ath9k_chanctx_force_active; +} + struct ieee80211_ops ath9k_ops = { .tx = ath9k_tx, .start = ath9k_start, diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index a0dbcc41238..3f7a11edb82 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)) @@ -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) diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index 4dec09e565e..7a2b2c5cace 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 9105a92364f..74ab1d02013 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) @@ -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; } @@ -457,7 +462,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; } @@ -540,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); @@ -887,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; diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index f1bbce3f777..a1499700bcf 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) \ diff --git a/drivers/net/wireless/ath/ath9k/tx99.c b/drivers/net/wireless/ath/ath9k/tx99.c index a65cfb91adc..23972924c77 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; diff --git a/drivers/net/wireless/ath/ath9k/wow.c b/drivers/net/wireless/ath/ath9k/wow.c index 2879887f569..a4f4f0da81f 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 66acb2cbd9d..d4927c9a6ba 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) @@ -147,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) @@ -159,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; } } @@ -626,7 +637,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 +1494,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 +1518,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 +1653,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; @@ -1686,7 +1696,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); @@ -1804,7 +1814,7 @@ void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq) sc->tx.txqsetup &= ~(1<<txq->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 +1822,31 @@ 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; + + 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(&txq->axq_acq)) + list_empty(ac_list)) return; + spin_lock_bh(&sc->chan_lock); 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); + 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); ac->sched = false; @@ -1844,7 +1866,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 +1874,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,12 +1885,27 @@ 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); } } rcu_read_unlock(); + spin_unlock_bh(&sc->chan_lock); +} + +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); + } } /***********/ @@ -2150,13 +2187,21 @@ 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; - int q; + bool queue; + int q, hw_queue; int ret; + 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; @@ -2168,24 +2213,39 @@ 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; } - 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) { + 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); - } 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) @@ -2198,7 +2258,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; @@ -2244,8 +2304,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); @@ -2560,6 +2620,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; } diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 820d4ebd932..4ac2c208c9b 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, @@ -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); @@ -443,15 +444,15 @@ 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; 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 { @@ -460,8 +461,10 @@ static int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, } __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); @@ -470,10 +473,12 @@ static int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, 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; } @@ -562,6 +567,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) { @@ -595,8 +628,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; @@ -604,11 +640,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); @@ -663,6 +707,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); diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 8d4bc4bfb66..a868c5eebe3 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -19,6 +19,7 @@ #include <linux/seq_file.h> #include <linux/pci.h> #include <linux/rtnetlink.h> +#include <linux/power_supply.h> #include "wil6210.h" #include "txrx.h" @@ -69,14 +70,32 @@ 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]; + 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]; + /* performance monitoring */ + cycles_t now = get_cycles(); + cycles_t idle = txdata->idle * 100; + cycles_t total = now - txdata->begin; + + do_div(idle, total); + txdata->begin = now; + txdata->idle = 0ULL; + 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] idle %3d%%\n", + wil->sta[cid].addr, cid, tid, used, avail, + (int)idle); + wil_print_vring(s, wil, name, vring, '_', 'H'); } } @@ -231,6 +250,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) @@ -284,11 +323,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; @@ -397,6 +436,124 @@ 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, +}; + +/* 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) @@ -600,8 +757,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; } @@ -618,6 +775,130 @@ 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, +}; + +/*---------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; + unsigned int i; + + /* >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; + + +#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; +} + +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) { @@ -630,7 +911,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) @@ -703,6 +984,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)); @@ -719,7 +1002,13 @@ 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("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); + debugfs_create_file("info", S_IRUGO, dbg, wil, &fops_info); wil->rgf_blob.data = (void * __force)wil->csr + 0; wil->rgf_blob.size = 0xa000; diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index 73593aa3cd9..67f1002a03a 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; } @@ -257,6 +259,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); } diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 11e6d9d22ea..53a689ed7c7 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; } } @@ -465,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); } @@ -475,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); } @@ -552,6 +562,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/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 1e2e07b9d13..77b6272d93f 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -15,7 +15,6 @@ */ #include <linux/module.h> -#include <linux/debugfs.h> #include <linux/pci.h> #include <linux/moduleparam.h> @@ -27,11 +26,22 @@ 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) { 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); @@ -63,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; @@ -71,6 +87,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; @@ -119,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); diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c index 747ae127587..180ca479390 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/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 0784ef3d4ce..af4b93e4beb 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]); + } } /** @@ -760,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; @@ -881,6 +892,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 +965,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); @@ -1016,15 +1031,17 @@ 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 */ 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: @@ -1132,8 +1149,16 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid) done++; } } - if (wil_vring_avail_tx(vring) > wil_vring_wmark_high(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)) { + wil_dbg_txrx(wil, "netif_tx_wake : ring not full\n"); netif_tx_wake_all_queues(wil_to_ndev(wil)); + } return done; } diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index e25edc52398..424906635f0 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -20,6 +20,7 @@ #include <linux/netdevice.h> #include <linux/wireless.h> #include <net/cfg80211.h> +#include <linux/timex.h> #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 */ @@ -303,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? */ }; @@ -410,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; @@ -504,9 +507,14 @@ 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); +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); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 6cc0e182cc7..a136dab560e 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 @@ -327,6 +328,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); @@ -351,6 +363,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; @@ -668,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); @@ -684,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", @@ -705,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; @@ -737,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, diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig index 40fd9b7b142..d4c6ae3a921 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 098fe9ee709..6e00b8804ad 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 0d6a0bb1f87..4d7b38c07a7 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); @@ -2201,52 +2205,82 @@ 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 40: + if (phy->type == B43_PHYTYPE_AC) + filename = "ucode40"; + 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 +2302,121 @@ 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"; + else if (rev == 40) + filename = "ac0initvals40"; + 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"; + else if (rev == 40) + filename = "ac0bsinitvals40"; + break; } + if (!filename) + goto err_no_initvals; err = b43_do_request_fw(ctx, filename, &fw->initvals_band, false); if (err) goto err_load; @@ -3798,38 +3836,29 @@ 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. */ - err = b43_switch_band(dev, conf->chandef.chan); - if (err) - goto out_unlock_mutex; + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + phy->chandef = &conf->chandef; + phy->channel = conf->chandef.chan->hw_value; - /* 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; + /* Switch the band (if necessary). */ + err = b43_switch_band(dev, conf->chandef.chan); + if (err) + goto out_mac_enable; - if (conf_is_ht(conf)) - phy->is_40mhz = - (conf_is_ht40_minus(conf) || conf_is_ht40_plus(conf)); - else - phy->is_40mhz = false; + /* Switch to the requested channel. + * The firmware takes care of races with the TX handler. + */ + b43_switch_channel(dev, phy->channel); + } if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) b43_set_retry_limits(dev, conf->short_frame_max_tx_count, @@ -3838,11 +3867,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. */ @@ -3878,12 +3902,8 @@ 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); - if (wl->vif && reload_bss) - b43_op_bss_info_changed(hw, wl->vif, &wl->vif->bss_conf, ~0); - return err; } @@ -4323,20 +4343,20 @@ 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) { - 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; +#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_a.c b/drivers/net/wireless/b43/phy_a.c index a6c38104693..25e40432d68 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, diff --git a/drivers/net/wireless/b43/phy_a.h b/drivers/net/wireless/b43/phy_a.h index 5cfaab7b16e..f7d0d929a37 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 08244b3b327..3cbef21b472 100644 --- a/drivers/net/wireless/b43/phy_common.c +++ b/drivers/net/wireless/b43/phy_common.c @@ -45,11 +45,10 @@ 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: +#ifdef CONFIG_B43_PHY_G phy->ops = &b43_phyops_g; +#endif break; case B43_PHYTYPE_N: #ifdef CONFIG_B43_PHY_N @@ -94,7 +93,13 @@ 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); + /* 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); @@ -106,9 +111,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; @@ -408,9 +411,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. */ @@ -428,7 +428,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); @@ -547,10 +546,9 @@ 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) { - return (channel_type == NL80211_CHAN_HT40MINUS || - channel_type == NL80211_CHAN_HT40PLUS); + return dev->phy.chandef->width == NL80211_CHAN_WIDTH_40; } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/BmacPhyClkFgc */ diff --git a/drivers/net/wireless/b43/phy_common.h b/drivers/net/wireless/b43/phy_common.h index 4ad6240d9ff..3912274f71e 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; @@ -267,9 +264,8 @@ 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; /* PHY TX errors counter. */ atomic_t txerr_cnt; @@ -400,10 +396,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. @@ -454,7 +446,7 @@ 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_ht.c b/drivers/net/wireless/b43/phy_ht.c index 5d6833f1849..f2974c6b1c0 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 86569f6a870..0a6f04be907 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -590,7 +590,103 @@ static void b43_nphy_set_rf_sequence(struct b43_wldev *dev, u8 cmd, * Radio 0x2057 **************************************************/ -/* http://bcm-v4.sipsolutions.net/PHY/radio2057_rcal */ +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 + */ static u8 b43_radio_2057_rcal(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; @@ -603,15 +699,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 +733,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 +743,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; } @@ -798,6 +933,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; @@ -895,7 +1031,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); @@ -909,7 +1045,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; @@ -1210,8 +1346,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) { @@ -1220,7 +1355,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; @@ -1248,7 +1383,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 +1398,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 (!dev->phy.is_40mhz) - 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 +1419,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)) { @@ -1675,6 +1807,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 +2030,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); @@ -2192,7 +2325,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); @@ -2206,7 +2339,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, @@ -2221,12 +2354,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 */ @@ -2298,7 +2431,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; } @@ -2371,13 +2504,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; @@ -2519,7 +2652,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 { @@ -2528,7 +2661,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); @@ -2592,7 +2725,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 { @@ -2691,7 +2824,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 { @@ -2946,12 +3079,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; } @@ -3114,7 +3248,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, @@ -3168,7 +3302,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)) { @@ -3184,12 +3318,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+ */ @@ -3388,7 +3523,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); @@ -3439,21 +3574,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; } @@ -3474,6 +3609,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 +3619,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; @@ -3705,21 +3841,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); } } @@ -3992,7 +4135,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)*/ @@ -4286,7 +4429,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]); @@ -4500,8 +4643,9 @@ 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->channel_type = dev->phy.channel_type; + iqcal_chanspec->center_freq = dev->phy.chandef->chan->center_freq; + 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) @@ -4581,6 +4725,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; @@ -4621,7 +4766,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, @@ -4636,16 +4781,16 @@ 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, - 0xFFFF, 0, true, false); + b43_nphy_run_samples(dev, (b43_is_40mhz(dev) ? 40 : 20) * 8, + 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) { @@ -4773,9 +4918,9 @@ 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; + cfg80211_get_chandef_type(phy->chandef); } else { length = 11; if (dev->phy.rev < 3) @@ -4811,8 +4956,8 @@ 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.channel_type != dev->phy.channel_type) + nphy->txiqlocal_chanspec.center_freq != dev->phy.chandef->chan->center_freq || + nphy->txiqlocal_chanspec.channel_type != cfg80211_get_chandef_type(dev->phy.chandef)) return; b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 7, buffer); @@ -4968,11 +5113,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) { @@ -5344,7 +5489,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); @@ -5430,14 +5575,14 @@ 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) { 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 */ @@ -5484,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) @@ -5502,20 +5654,36 @@ 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 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, - 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 d61d6830c5c..df357454581 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,8 @@ static u16 r2057_rev7_init[54][2] = { { 0x1B7, 0x05 }, { 0x1C2, 0xa0 }, }; -static u16 r2057_rev8_init[54][2] = { +/* 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 }, { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, { 0x64, 0x0f }, @@ -102,6 +103,47 @@ static u16 r2057_rev8_init[54][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,33 +151,69 @@ 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); + 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]); + } +} + +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 eeebd8fbeb0..675d1bb6442 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_ */ diff --git a/drivers/net/wireless/b43/tables_nphy.c b/drivers/net/wireless/b43/tables_nphy.c index 4047c05e380..b2217159292 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, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index 98e67c18f27..4cffb2ee367 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/btcoex.c b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c index 0cb591b050b..a29ac4977b3 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; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index 16f9ab2568a..a8998eb60d2 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/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c index ed3e32ce8c2..d991f8e3d9e 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/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c index 59a5af5bf99..ded328f80cd 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; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index f3445ac627e..588fdbdb325 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/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 6db51a666f6..839bcda9465 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -29,32 +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_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; @@ -70,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; @@ -97,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 */ }; @@ -576,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) @@ -589,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; @@ -642,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; @@ -675,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; } @@ -724,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"); @@ -732,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); @@ -794,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); @@ -839,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; } @@ -898,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; @@ -928,6 +921,9 @@ static bool brcmf_usb_chip_support(int chipid, int chiprev) return (chiprev == 3); case 43242: return true; + case 43566: + case 43569: + return true; default: break; } @@ -1028,6 +1024,9 @@ 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: return NULL; } @@ -1221,15 +1220,15 @@ 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_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; + struct usb_interface_descriptor *desc; + struct usb_endpoint_descriptor *endpoint; + int ret = 0; + u32 num_of_eps; + u8 endpoint_num, ep; - 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) @@ -1237,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; - goto fail; - } - - if (usb->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) { - ret = -1; + brcmf_err("Number of configurations: %d not supported\n", + usb->descriptor.bNumConfigurations); + 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; + 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; } - /* 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; + 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 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) @@ -1332,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 @@ -1381,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, @@ -1392,12 +1369,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 +1386,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, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c new file mode 100644 index 00000000000..5960d827508 --- /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 <linux/vmalloc.h> +#include <net/cfg80211.h> +#include <net/netlink.h> + +#include <brcmu_wifi.h> +#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 00000000000..061b7bfa2e1 --- /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 d8fa276e368..9682cf213ec 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -19,6 +19,7 @@ #include <linux/kernel.h> #include <linux/etherdevice.h> #include <linux/module.h> +#include <linux/vmalloc.h> #include <net/cfg80211.h> #include <net/netlink.h> @@ -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 @@ -588,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; @@ -641,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 @@ -920,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; @@ -928,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; } @@ -1019,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) { @@ -1029,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; } } @@ -1331,7 +1339,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 +1348,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 +2395,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 +2444,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 +2696,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 +2786,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) @@ -3260,35 +3265,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; @@ -3507,7 +3483,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 +3535,7 @@ next: ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len + TLV_HDR_LEN); } - return err; + return 0; } static u32 @@ -4307,7 +4282,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) @@ -4412,6 +4386,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); @@ -4650,7 +4629,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 +4654,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 +4746,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 +4756,7 @@ brcmf_notify_roaming_status(struct brcmf_if *ifp, brcmf_bss_connect_done(cfg, ifp->ndev, e, true); } - return err; + return 0; } static s32 @@ -5057,6 +5034,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) { @@ -5625,16 +5605,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 283c525a44f..f9fb10998e7 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); diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c index b0fd807f2b2..57ecc05802e 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) diff --git a/drivers/net/wireless/cw1200/scan.c b/drivers/net/wireless/cw1200/scan.c index 9afcd4ce336..b2fb6c63209 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 5a8296ccfa8..cc75459e578 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/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c index cd0cad7f775..5b84664db13 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, diff --git a/drivers/net/wireless/ipw2x00/libipw_module.c b/drivers/net/wireless/ipw2x00/libipw_module.c index 3adb24021a2..5f31b72a492 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]); } } diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c index ecc674627e6..03de7467aec 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 ea5c0f863c4..5b972798bdf 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 29af7b51e37..afb98f4fdaf 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/dvm/power.c b/drivers/net/wireless/iwlwifi/dvm/power.c index f2c1439566b..760c45c34ef 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) diff --git a/drivers/net/wireless/iwlwifi/iwl-8000.c b/drivers/net/wireless/iwlwifi/iwl-8000.c index 51c41531d81..51486cc9d94 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 @@ -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 b7047905f41..034c2fc4b69 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__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index f2a5c12269a..bb842f4732d 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. @@ -565,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) @@ -819,6 +823,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 +983,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; @@ -1394,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-eeprom-parse.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c index c44cf114964..07ff7e0028e 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; } diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h index 2953ffceda3..ced5ba95c23 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h @@ -74,12 +74,17 @@ * @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. + * @IWL_FW_ERROR_DUMP_FW_MONITOR: firmware monitor */ 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_FW_MONITOR = 5, IWL_FW_ERROR_DUMP_MAX, }; @@ -120,13 +125,50 @@ 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; + +/** + * 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_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/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h index b45e576a4b5..929a8063354 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 { @@ -136,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 { /* @@ -147,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 0aa7c0085c9..13e5d69845e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -65,6 +65,8 @@ #include <linux/types.h> #include <net/mac80211.h> +#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 @@ -117,11 +119,15 @@ 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), }; /** @@ -178,6 +184,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]; @@ -311,6 +318,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/iwl-modparams.h b/drivers/net/wireless/iwlwifi/iwl-modparams.h index d051857729a..f2d39cb011f 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-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index 85eee79c495..f0ae038f333 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) && @@ -462,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) @@ -481,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]; + return; + } - hw_addr = (const u8 *)(nvm_hw + HW_ADDR1_FAMILY_8000); - data->hw_addr[4] = hw_addr[1]; - data->hw_addr[5] = hw_addr[0]; + IWL_ERR_DEV(dev, "mac address is not found\n"); } struct iwl_nvm_data * @@ -556,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, diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index 4997e27672b..47033a35a40 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/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c index c8c3b38228f..7e0388a3291 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex.c @@ -100,12 +100,13 @@ 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) { + 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); @@ -535,7 +536,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; @@ -578,6 +579,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 +666,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)); @@ -780,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 */ @@ -919,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"); @@ -930,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"); @@ -955,6 +980,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 +1150,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 @@ -1177,9 +1210,12 @@ 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) + 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 */ @@ -1190,18 +1226,29 @@ 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 (mvm->last_bt_notif.ttc_enabled) + return true; 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_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, @@ -1274,6 +1321,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/constants.h b/drivers/net/wireless/iwlwifi/mvm/constants.h index 51685693af2..ca79f716057 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 diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index 29ca72695ea..602bbd29ec5 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 5fe82c29c8a..39cb33a1986 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), @@ -299,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 @@ -311,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; diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h index cbbcd8e284e..c3a8c86b550 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 diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h index 6959fda3fe0..1d586923d5b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h @@ -170,18 +170,12 @@ enum iwl_scan_type { }; /* 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/fw-api-sta.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h index 39cebee8016..47bd0406355 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/fw-api-tx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h index 6cc5f52b807..bdd6ff64862 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h @@ -549,6 +549,20 @@ struct iwl_beacon_notif { } __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 * and the TFD queues are empty. diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index 8b530277763..aad36212c4d 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 { @@ -1205,19 +1206,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 7215f598018..72e3146625f 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; @@ -688,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; @@ -1464,6 +1475,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); @@ -1537,12 +1549,14 @@ 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) + if (req->n_channels == 0 || + req->n_channels > mvm->fw->ucode_capa.n_scan_channels) return -EINVAL; mutex_lock(&mvm->mutex); @@ -1827,7 +1841,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 fcc6c29482d..f7e54a57f46 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; @@ -648,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 */ @@ -854,7 +867,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, @@ -963,6 +976,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/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index 808f78f6fbf..1f1a550828f 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 @@ -219,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; @@ -231,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, @@ -326,6 +335,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 +344,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 +410,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; @@ -459,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)) @@ -475,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; diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index cc2f7de396d..7bb763f3052 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; @@ -826,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; @@ -834,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) @@ -852,11 +855,27 @@ 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_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); @@ -876,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); diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 4b6c7d4bd19..f2dde5604a8 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; @@ -267,7 +275,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 +297,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++) { @@ -325,16 +336,14 @@ 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); 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) @@ -382,7 +391,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 +570,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 +586,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); } @@ -588,9 +598,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; @@ -667,83 +675,65 @@ 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, 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; + 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++) { + struct ieee80211_channel *chan = req->channels[i]; + + if (chan->band != band) + continue; - /* - * 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 < 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)); - channels->dwell_time[index][0] = params->dwell[band].active; - channels->dwell_time[index][1] = params->dwell[band].passive; + index = *head; + (*head)++; - channels->iter_count[index] = cpu_to_le16(1); - channels->iter_interval[index] = 0; + channel_number[index] = cpu_to_le16(chan->hw_value); + active_dwell[index] = params->dwell[band].active; + passive_dwell[index] = params->dwell[band].passive; - if (!(s_band->channels[i].flags & IEEE80211_CHAN_NO_IR)) - channels->type[index] |= + iter_count[index] = cpu_to_le16(1); + iter_interval[index] = 0; + + if (!(chan->flags & IEEE80211_CHAN_NO_IR)) + 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); + type[index] |= cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_FULL | + IWL_SCAN_OFFLOAD_CHANNEL_PARTIAL); - if (s_band->channels[i].flags & IEEE80211_CHAN_NO_HT40) - channels->type[index] |= + if (chan->flags & IEEE80211_CHAN_NO_HT40) + 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); } } 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; int head = 0; - int tail = band_2ghz + band_5ghz - 1; u32 ssid_bitmap; int cmd_len; int ret; + u8 *probes; struct iwl_scan_offload_cfg *scan_cfg; struct iwl_host_cmd cmd = { @@ -754,13 +744,17 @@ 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; - iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, ¶ms); + 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); @@ -770,19 +764,19 @@ 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, - IEEE80211_BAND_2GHZ, &head, &tail, + probes); + iwl_build_channel_cfg(mvm, req, scan_cfg->data, + IEEE80211_BAND_2GHZ, &head, ssid_bitmap, ¶ms); } if (band_5ghz) { 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, - IEEE80211_BAND_5GHZ, &head, &tail, + iwl_build_channel_cfg(mvm, req, scan_cfg->data, + IEEE80211_BAND_5GHZ, &head, ssid_bitmap, ¶ms); } diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 1fb01ea2e70..d3a6cf7558e 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 d98e8a2142b..10c1a535265 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__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index 3846a6c41eb..4f7cff506ee 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)) diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index 6c22b23a284..78f72c34438 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 788085bc65d..5703a3d7799 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); } @@ -1494,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); @@ -1519,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) { @@ -1698,10 +1783,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 +1819,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, @@ -1870,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); diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 038940afbdc..6acccb19c4f 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; } diff --git a/drivers/net/wireless/libertas/Kconfig b/drivers/net/wireless/libertas/Kconfig index 0485c995757..e6268ceacbf 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. diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c index aaa297315c4..0387a5b380c 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) { diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index a312c653d11..eba51460a5d 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) @@ -1740,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/mwifiex/11ac.c b/drivers/net/wireless/mwifiex/11ac.c index 706831df1fa..59d23fb2365 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 0b02cb6cfcb..1ca92c7a8a4 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 e76b0db4e3e..2668e83afbb 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 e1c2f67ae85..9d6d8d9f01e 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 0b73fa08f5d..2ee268b632b 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 5b32106182f..b4c14b0fd3c 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 892098d6a69..0cd2a3eb6c1 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 0c3571f830b..b22bae3d120 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 0fc76e4a60f..3a87bb0e3a6 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 2aa208ffbe2..9487d728ac2 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 3b55ce5690a..31928caeeed 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 @@ -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 "<condition> [GPIO# [gap]]]" > hscfg + cat hscfg + + where the parameters are, + <condition>: 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/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index e95dec91a56..15fa7b45337 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 @@ -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. * @@ -151,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; @@ -173,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; } @@ -2483,6 +2451,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) @@ -2631,7 +2609,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; @@ -2916,12 +2895,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 diff --git a/drivers/net/wireless/mwifiex/cfg80211.h b/drivers/net/wireless/mwifiex/cfg80211.h index c5848934f11..908367857d5 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 0ddec3d4b05..b8242eb2be6 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 8dee6c86f4f..df42f066d70 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 @@ -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) @@ -960,6 +968,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); } @@ -1225,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)); diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c index 7b419bbcd54..2713f7acd35 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 @@ -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/decl.h b/drivers/net/wireless/mwifiex/decl.h index 38da6ff6f41..0e03fe39fc3 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 bfb39908b2c..04e56b5fc53 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 @@ -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/fw.h b/drivers/net/wireless/mwifiex/fw.h index 3175dd04834..5561573452b 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 3bf3d58bbc0..b933794758b 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 4ecd0b208ac..269a277d0a2 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 @@ -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/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h index 1b576722671..0847f3e07ab 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 89dc62a467f..fc135649b85 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 cbabc12fbda..657504c3c79 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 @@ -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); @@ -881,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 1398afa8406..a2733b1e63f 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 @@ -30,6 +30,7 @@ #include <linux/etherdevice.h> #include <net/sock.h> #include <net/lib80211.h> +#include <linux/vmalloc.h> #include <linux/firmware.h> #include <linux/ctype.h> #include <linux/of.h> @@ -410,6 +411,29 @@ 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, + MWIFIEX_IFACE_WORK_CARD_RESET, +}; + struct mwifiex_adapter; struct mwifiex_private; @@ -674,6 +698,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 +834,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); @@ -890,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/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 2cc9b6fca49..5f7afffdd34 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 @@ -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 e8ec561f8a6..a1a8fd3bc1b 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 @@ -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 */ diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index 45c5b3450cf..dee717a19dd 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 4ce3d7b3399..1da04a086bd 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 @@ -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); @@ -179,6 +198,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); @@ -1777,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; } @@ -1914,10 +1937,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 sdio_card_reset_worker(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 @@ -1933,15 +1956,213 @@ 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); + +/* 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 = + 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(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 */ 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); + + 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; - reset_host = card->func->card->host; - schedule_work(&card_reset_work); + set_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags); + schedule_work(&adapter->iface_work); } static struct mwifiex_if_ops sdio_ops = { @@ -1964,6 +2185,8 @@ 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, + .fw_dump = mwifiex_sdio_fw_dump, }; /* @@ -2001,7 +2224,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); } diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h index 6eea30b43ed..6b8835ec88f 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 @@ -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, }; /* diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index 88202ce0c13..0f077aaadab 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 577f2979ed8..822357b7b0b 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 f6395ef11a7..f1c240eca0c 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 536c14aa71f..1a03d4d8b41 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 @@ -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); /* @@ -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; diff --git a/drivers/net/wireless/mwifiex/sta_rx.c b/drivers/net/wireless/mwifiex/sta_rx.c index 8b639d7fe6d..9ceb1dbe34c 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 5fce7e78a36..cf330ba951c 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/tdls.c b/drivers/net/wireless/mwifiex/tdls.c index e73034fbbde..3efbcbe7e89 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/txrx.c b/drivers/net/wireless/mwifiex/txrx.c index 37f26afd431..08205683f87 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 32643555dd2..300bab43801 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 92e77a398ec..7c2b97660a0 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 9a56bc61cb1..ddfc3c6c1e7 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 @@ -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/usb.c b/drivers/net/wireless/mwifiex/usb.c index a8ce8130cfa..7118a18b91b 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 15b73d12e99..4c41c2a193c 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 6da5abf52e6..cee028321a9 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 caadb3737b9..40296cb4a3f 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 d3671d009f6..94c98a86ebb 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 @@ -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 diff --git a/drivers/net/wireless/mwifiex/wmm.h b/drivers/net/wireless/mwifiex/wmm.h index eca56e371a5..569bd73f33c 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 diff --git a/drivers/net/wireless/orinoco/Kconfig b/drivers/net/wireless/orinoco/Kconfig index 60819bcf437..60698b02085 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/ @@ -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 diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c index de15171e2cd..63de5eed25c 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"); diff --git a/drivers/net/wireless/rsi/rsi_91x_core.c b/drivers/net/wireless/rsi/rsi_91x_core.c index cf61d6e3eaa..f3d3995d8f6 100644 --- a/drivers/net/wireless/rsi/rsi_91x_core.c +++ b/drivers/net/wireless/rsi/rsi_91x_core.c @@ -77,6 +77,52 @@ static bool rsi_recalculate_weights(struct rsi_common *common) } /** + * 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; + __le16 r_txop; + 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 { + 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) + 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. * @common: Pointer to the driver private structure. @@ -88,7 +134,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 +142,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 +155,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,25 +190,9 @@ 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; @@ -252,6 +286,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; } @@ -306,7 +341,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 { @@ -325,6 +361,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); diff --git a/drivers/net/wireless/rsi/rsi_91x_debugfs.c b/drivers/net/wireless/rsi/rsi_91x_debugfs.c index c466246a323..828a042f903 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]); diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 54aaeb09deb..aeaf87bb551 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; @@ -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. * @@ -341,6 +341,59 @@ static void rsi_mac80211_remove_interface(struct ieee80211_hw *hw, } /** + * 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 * change hardware configuration, e.g., channel. @@ -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); } @@ -723,17 +778,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); @@ -741,6 +796,37 @@ static int rsi_mac80211_set_rate_mask(struct ieee80211_hw *hw, } /** + * 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. * @hw: Pointer to the ieee80211_hw 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; @@ -770,10 +857,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); @@ -792,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; } /** @@ -983,6 +1075,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); @@ -1000,6 +1093,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 2eefbf159bc..8d110fd9eba 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; } /** @@ -276,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; @@ -285,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__); @@ -307,29 +306,36 @@ 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); + } } } + 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++) { @@ -588,7 +594,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); @@ -615,6 +621,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__); @@ -644,13 +653,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); @@ -827,6 +847,63 @@ static int rsi_send_reset_mac(struct rsi_common *common) } /** + * 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. * @channel: Channel value to be set. @@ -841,23 +918,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", @@ -877,6 +937,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); @@ -950,7 +1011,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; @@ -986,14 +1047,19 @@ 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++) { + 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].bitrate / 5); + selected_rates[jj++] = + (rsi_rates[ii + rate_table_offset].bitrate / 5); rate_offset++; } } @@ -1006,13 +1072,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 */ @@ -1028,25 +1087,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] = 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); auto_rate->desc_word[7] |= cpu_to_le16(0 << 8); @@ -1141,6 +1200,49 @@ static int rsi_eeprom_read(struct rsi_common *common) } /** + * 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. * @msg: Pointer to received packet. @@ -1164,7 +1266,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; @@ -1227,7 +1329,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; @@ -1245,7 +1347,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; diff --git a/drivers/net/wireless/rsi/rsi_91x_pkt.c b/drivers/net/wireless/rsi/rsi_91x_pkt.c index 8e48e72bae2..702593f1999 100644 --- a/drivers/net/wireless/rsi/rsi_91x_pkt.c +++ b/drivers/net/wireless/rsi/rsi_91x_pkt.c @@ -81,6 +81,16 @@ 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] |= + cpu_to_le16(ENABLE_SHORTGI_RATE); + } + } frame_desc[6] |= cpu_to_le16(seq_num & 0xfff); @@ -116,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; @@ -175,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)); diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio.c b/drivers/net/wireless/rsi/rsi_91x_sdio.c index 46e7af446f0..8428858204a 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; } /** diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c index 20d11ccfffe..4834a9abc17 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_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index 4c46e5631e2..ef5d394f185 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) @@ -397,8 +398,16 @@ 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); + 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; } /** @@ -550,33 +568,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"); diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h index 2cb73e7edb9..5baed945f60 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 { @@ -141,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 { @@ -192,6 +199,11 @@ struct rsi_common { u8 selected_qnum; u32 pkt_cnt; u8 min_weight; + + /* bgscan related */ + struct cqm_info cqm_info; + + bool hw_data_qs_blocked; }; struct rsi_hw { diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index 225215a3b8b..3741173fd3a 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 @@ -123,6 +124,20 @@ #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 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 { @@ -153,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, @@ -238,6 +253,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) @@ -272,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); @@ -283,4 +305,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 diff --git a/drivers/net/wireless/rsi/rsi_sdio.h b/drivers/net/wireless/rsi/rsi_sdio.h index df4b5e20e05..c7e8f2be790 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 { diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index c17fcf27272..893c9d5f3d6 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,8 +1093,13 @@ 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. */ rt2800_register_write(rt2x00dev, BCN_TIME_CFG, orig_reg); @@ -1556,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); } @@ -4517,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); diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 4fa43a2eeb7..9967a1d9f0e 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) @@ -1470,8 +1473,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); diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index 004dff9b962..ad6e5a8d1e1 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -626,25 +626,24 @@ 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; - /* - * 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) { /* * Last beaconing interface disabled * -> stop beacon queue. */ - mutex_lock(&intf->beacon_skb_mutex); rt2x00queue_stop_queue(rt2x00dev->bcn); - mutex_unlock(&intf->beacon_skb_mutex); } + /* + * 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; @@ -660,11 +659,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); } /* @@ -801,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); diff --git a/drivers/net/wireless/rt2x00/rt2x00mmio.c b/drivers/net/wireless/rt2x00/rt2x00mmio.c index 6f236ea180a..f0178fd4fe5 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. */ diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 5642ccceca7..8e68f87ab13 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, diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h index c48125be0e3..2233b911a1d 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, diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index 2c1c02bafa1..4b904f70818 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 <Larry.Finger@lwfinger.net> * @@ -209,7 +210,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--) { @@ -222,12 +223,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); } @@ -266,18 +275,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); + signal = -clamp_t(u8, agc, 25, 90) - 9; else - signal = 95 - clamp_t(u8, agc, 30, 95); - } else if (priv->chip_family == - RTL818X_CHIP_FAMILY_RTL8180) { + signal = -clamp_t(u8, agc, 30, 95); + 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; @@ -336,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) @@ -528,9 +539,7 @@ 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; + 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 @@ -852,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); @@ -868,6 +877,16 @@ 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 */ + 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) { @@ -1450,9 +1469,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) @@ -1723,17 +1743,20 @@ 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); + 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"); + err = -ENOMEM; goto err_free_dev; } @@ -1751,8 +1774,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); @@ -1783,12 +1805,19 @@ 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; default: printk(KERN_ERR "%s (rtl8180): Unknown chip! (0x%x)\n", pci_name(pdev), reg >> 25); + err = -ENODEV; goto err_iounmap; } @@ -1809,6 +1838,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) { @@ -1834,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; } diff --git a/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h b/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h index 291a55970d1..e8243a44d6b 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; diff --git a/drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.h b/drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.h index 871fc3c6d55..049f4c8d98a 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 { \ diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c b/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c index b14cf5a10f4..d840ad7bdf6 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 7af85cfa8f8..cd7e7a52713 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 diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c index cdecb0fd4d8..e2736929b5d 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 ed703a1b3b7..dc8460c0b32 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 a903c2671b4..270cbffcac7 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); diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/hw.c b/drivers/net/wireless/rtlwifi/rtl8192de/hw.c index 2b08671004a..280c3da4299 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/phy.c b/drivers/net/wireless/rtlwifi/rtl8192de/phy.c index 3d1f0dd4e52..592125a5f19 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) diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/reg.h b/drivers/net/wireless/rtlwifi/rtl8192de/reg.h index 7f29b8d765b..315a298bab0 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) */ /* ----------------------------------------------------- */ diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c index 87f69166a7e..539e5398737 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 64376b38708..ce2c66fd9ee 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 diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/hw.c b/drivers/net/wireless/rtlwifi/rtl8723be/hw.c index 3d555495b45..3cd286930fe 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 4c653fab879..3006849ed43 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 diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c index 4e782f18ae3..38234851457 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/wl12xx/scan.c b/drivers/net/wireless/ti/wl12xx/scan.c index 7541bd1a4a4..0c0d5cd9851 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 264af7ac278..427f9af85a0 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 2b642f8c926..98666f235a1 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 eadee42689d..2e636aa5dba 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 40dc30f4faa..e269c0a5701 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 b084830a61c..6788d7356ca 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 3d6028e6275..48f83868f9c 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; @@ -3636,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 a6ab24b5c0f..4dadd0c62cd 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 95a54504f0c..71320509b56 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, |