985 lines
33 KiB
Diff
985 lines
33 KiB
Diff
commit 775f8efe32011d6efc55d383a1c88fb173c80246
|
|
Author: Patrick Walther <patrick.walther@netmodule.com>
|
|
Date: Fri Jul 24 18:56:44 2020 +0200
|
|
|
|
backport of netmodule patches from openwrt
|
|
|
|
%% original patch: 0009-backport-of-netmodule-patches-from-openwrt.patch
|
|
|
|
diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
|
|
index 2a18e2e..bb30fd7 100644
|
|
--- a/drivers/net/wireless/ath/ath.h
|
|
+++ b/drivers/net/wireless/ath/ath.h
|
|
@@ -198,12 +198,13 @@ struct sk_buff *ath_rxbuf_alloc(struct ath_common *common,
|
|
bool ath_is_mybeacon(struct ath_common *common, struct ieee80211_hdr *hdr);
|
|
|
|
void ath_hw_setbssidmask(struct ath_common *common);
|
|
-void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key);
|
|
+void ath_key_delete(struct ath_common *common, u8 hw_key_idx);
|
|
int ath_key_config(struct ath_common *common,
|
|
struct ieee80211_vif *vif,
|
|
struct ieee80211_sta *sta,
|
|
struct ieee80211_key_conf *key);
|
|
bool ath_hw_keyreset(struct ath_common *common, u16 entry);
|
|
+bool ath_hw_keysetmac(struct ath_common *common, u16 entry, const u8 *mac);
|
|
void ath_hw_cycle_counters_update(struct ath_common *common);
|
|
int32_t ath_hw_get_listen_time(struct ath_common *common);
|
|
|
|
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
|
|
index 7946d3b..ba289ad 100644
|
|
--- a/drivers/net/wireless/ath/ath10k/mac.c
|
|
+++ b/drivers/net/wireless/ath/ath10k/mac.c
|
|
@@ -8419,7 +8419,7 @@ static const struct ieee80211_iface_limit ath10k_10x_if_limits[] = {
|
|
#endif
|
|
},
|
|
{
|
|
- .max = 1,
|
|
+ .max = 2,
|
|
.types = BIT(NL80211_IFTYPE_STATION)
|
|
},
|
|
};
|
|
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
|
|
index d7435c9..57ec879 100644
|
|
--- a/drivers/net/wireless/ath/ath10k/pci.c
|
|
+++ b/drivers/net/wireless/ath/ath10k/pci.c
|
|
@@ -28,7 +28,7 @@ enum ath10k_pci_reset_mode {
|
|
ATH10K_PCI_RESET_WARM_ONLY = 1,
|
|
};
|
|
|
|
-static unsigned int ath10k_pci_irq_mode = ATH10K_PCI_IRQ_AUTO;
|
|
+static unsigned int ath10k_pci_irq_mode = ATH10K_PCI_IRQ_LEGACY;
|
|
static unsigned int ath10k_pci_reset_mode = ATH10K_PCI_RESET_AUTO;
|
|
|
|
module_param_named(irq_mode, ath10k_pci_irq_mode, uint, 0644);
|
|
diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
|
|
index 47dd580..0b890db 100644
|
|
--- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
|
|
+++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
|
|
@@ -516,7 +516,7 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
|
|
}
|
|
break;
|
|
case DISABLE_KEY:
|
|
- ath_key_delete(common, key);
|
|
+ ath_key_delete(common, key->->hw_key_idx);
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
|
|
index b647a32..c3892a9 100644
|
|
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
|
|
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
|
|
@@ -1460,7 +1460,7 @@ static int ath9k_htc_set_key(struct ieee80211_hw *hw,
|
|
}
|
|
break;
|
|
case DISABLE_KEY:
|
|
- ath_key_delete(common, key);
|
|
+ ath_key_delete(common, key->hw_key_idx);
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
|
|
index 015fe02..9b2e6d8 100644
|
|
--- a/drivers/net/wireless/ath/ath9k/hw.h
|
|
+++ b/drivers/net/wireless/ath/ath9k/hw.h
|
|
@@ -830,6 +830,7 @@ struct ath_hw {
|
|
struct ath9k_pacal_info pacal_info;
|
|
struct ar5416Stats stats;
|
|
struct ath9k_tx_queue_info txq[ATH9K_NUM_TX_QUEUES];
|
|
+ DECLARE_BITMAP(pending_del_keymap, ATH_KEYMAX);
|
|
|
|
enum ath9k_int imask;
|
|
u32 imrs2_reg;
|
|
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
|
|
index 17418f9..5efd45c 100644
|
|
--- a/drivers/net/wireless/ath/ath9k/main.c
|
|
+++ b/drivers/net/wireless/ath/ath9k/main.c
|
|
@@ -831,12 +831,80 @@ exit:
|
|
ieee80211_free_txskb(hw, skb);
|
|
}
|
|
|
|
+static bool ath9k_txq_list_has_key(struct list_head *txq_list, u32 keyix)
|
|
+{
|
|
+ struct ath_buf *bf;
|
|
+ struct ieee80211_tx_info *txinfo;
|
|
+ struct ath_frame_info *fi;
|
|
+
|
|
+ list_for_each_entry(bf, txq_list, list) {
|
|
+ if (bf->bf_state.stale || !bf->bf_mpdu)
|
|
+ continue;
|
|
+
|
|
+ txinfo = IEEE80211_SKB_CB(bf->bf_mpdu);
|
|
+ fi = (struct ath_frame_info *)&txinfo->rate_driver_data[0];
|
|
+ if (fi->keyix == keyix)
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static bool ath9k_txq_has_key(struct ath_softc *sc, u32 keyix)
|
|
+{
|
|
+ struct ath_hw *ah = sc->sc_ah;
|
|
+ int i;
|
|
+ struct ath_txq *txq;
|
|
+ bool key_in_use = false;
|
|
+
|
|
+ for (i = 0; !key_in_use && i < ATH9K_NUM_TX_QUEUES; i++) {
|
|
+ if (!ATH_TXQ_SETUP(sc, i))
|
|
+ continue;
|
|
+ txq = &sc->tx.txq[i];
|
|
+ if (!txq->axq_depth)
|
|
+ continue;
|
|
+ if (!ath9k_hw_numtxpending(ah, txq->axq_qnum))
|
|
+ continue;
|
|
+
|
|
+ ath_txq_lock(sc, txq);
|
|
+ key_in_use = ath9k_txq_list_has_key(&txq->axq_q, keyix);
|
|
+ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
|
|
+ int idx = txq->txq_tailidx;
|
|
+
|
|
+ while (!key_in_use &&
|
|
+ !list_empty(&txq->txq_fifo[idx])) {
|
|
+ key_in_use = ath9k_txq_list_has_key(
|
|
+ &txq->txq_fifo[idx], keyix);
|
|
+ INCR(idx, ATH_TXFIFO_DEPTH);
|
|
+ }
|
|
+ }
|
|
+ ath_txq_unlock(sc, txq);
|
|
+ }
|
|
+
|
|
+ return key_in_use;
|
|
+}
|
|
+
|
|
+static void ath9k_pending_key_del(struct ath_softc *sc, u8 keyix)
|
|
+{
|
|
+ struct ath_hw *ah = sc->sc_ah;
|
|
+ struct ath_common *common = ath9k_hw_common(ah);
|
|
+
|
|
+ if (!test_bit(keyix, ah->pending_del_keymap) ||
|
|
+ ath9k_txq_has_key(sc, keyix))
|
|
+ return;
|
|
+
|
|
+ /* No more TXQ frames point to this key cache entry, so delete it. */
|
|
+ clear_bit(keyix, ah->pending_del_keymap);
|
|
+ ath_key_delete(common, keyix);
|
|
+}
|
|
+
|
|
static void ath9k_stop(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);
|
|
bool prev_idle;
|
|
+ int i;
|
|
|
|
ath9k_deinit_channel_context(sc);
|
|
|
|
@@ -904,6 +972,14 @@ static void ath9k_stop(struct ieee80211_hw *hw)
|
|
|
|
spin_unlock_bh(&sc->sc_pcu_lock);
|
|
|
|
+ for (i = 0; i < ATH_KEYMAX; i++)
|
|
+ ath9k_pending_key_del(sc, i);
|
|
+
|
|
+ /* Clear key cache entries explicitly to get rid of any potentially
|
|
+ * remaining keys.
|
|
+ */
|
|
+ ath9k_cmn_init_crypto(sc->sc_ah);
|
|
+
|
|
ath9k_ps_restore(sc);
|
|
|
|
sc->ps_idle = prev_idle;
|
|
@@ -1546,12 +1622,11 @@ static void ath9k_del_ps_key(struct ath_softc *sc,
|
|
{
|
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
|
struct ath_node *an = (struct ath_node *) sta->drv_priv;
|
|
- struct ieee80211_key_conf ps_key = { .hw_key_idx = an->ps_key };
|
|
|
|
if (!an->ps_key)
|
|
return;
|
|
|
|
- ath_key_delete(common, &ps_key);
|
|
+ ath_key_delete(common, an->ps_key);
|
|
an->ps_key = 0;
|
|
an->key_idx[0] = 0;
|
|
}
|
|
@@ -1713,6 +1788,12 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
|
|
if (sta)
|
|
an = (struct ath_node *)sta->drv_priv;
|
|
|
|
+ /* Delete pending key cache entries if no more frames are pointing to
|
|
+ * them in TXQs.
|
|
+ */
|
|
+ for (i = 0; i < ATH_KEYMAX; i++)
|
|
+ ath9k_pending_key_del(sc, i);
|
|
+
|
|
switch (cmd) {
|
|
case SET_KEY:
|
|
if (sta)
|
|
@@ -1742,7 +1823,15 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
|
|
}
|
|
break;
|
|
case DISABLE_KEY:
|
|
- ath_key_delete(common, key);
|
|
+ if (ath9k_txq_has_key(sc, key->hw_key_idx)) {
|
|
+ /* Delay key cache entry deletion until there are no
|
|
+ * remaining TXQ frames pointing to this entry.
|
|
+ */
|
|
+ set_bit(key->hw_key_idx, sc->sc_ah->pending_del_keymap);
|
|
+ ath_hw_keysetmac(common, key->hw_key_idx, NULL);
|
|
+ } else {
|
|
+ ath_key_delete(common, key->hw_key_idx);
|
|
+ }
|
|
if (an) {
|
|
for (i = 0; i < ARRAY_SIZE(an->key_idx); i++) {
|
|
if (an->key_idx[i] != key->hw_key_idx)
|
|
diff --git a/drivers/net/wireless/ath/key.c b/drivers/net/wireless/ath/key.c
|
|
index 1816b4e..61b59a8 100644
|
|
--- a/drivers/net/wireless/ath/key.c
|
|
+++ b/drivers/net/wireless/ath/key.c
|
|
@@ -84,8 +84,7 @@ bool ath_hw_keyreset(struct ath_common *common, u16 entry)
|
|
}
|
|
EXPORT_SYMBOL(ath_hw_keyreset);
|
|
|
|
-static bool ath_hw_keysetmac(struct ath_common *common,
|
|
- u16 entry, const u8 *mac)
|
|
+bool ath_hw_keysetmac(struct ath_common *common, u16 entry, const u8 *mac)
|
|
{
|
|
u32 macHi, macLo;
|
|
u32 unicast_flag = AR_KEYTABLE_VALID;
|
|
@@ -125,6 +124,7 @@ static bool ath_hw_keysetmac(struct ath_common *common,
|
|
|
|
return true;
|
|
}
|
|
+EXPORT_SYMBOL(ath_hw_keysetmac);
|
|
|
|
static bool ath_hw_set_keycache_entry(struct ath_common *common, u16 entry,
|
|
const struct ath_keyval *k,
|
|
@@ -581,29 +581,38 @@ EXPORT_SYMBOL(ath_key_config);
|
|
/*
|
|
* Delete Key.
|
|
*/
|
|
-void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key)
|
|
+void ath_key_delete(struct ath_common *common, u8 hw_key_idx)
|
|
{
|
|
- ath_hw_keyreset(common, key->hw_key_idx);
|
|
- if (key->hw_key_idx < IEEE80211_WEP_NKID)
|
|
+ /* Leave CCMP and TKIP (main key) configured to avoid disabling
|
|
+ * encryption for potentially pending frames already in a TXQ with the
|
|
+ * keyix pointing to this key entry. Instead, only clear the MAC address
|
|
+ * to prevent RX processing from using this key cache entry.
|
|
+ */
|
|
+ if (test_bit(hw_key_idx, common->ccmp_keymap) ||
|
|
+ test_bit(hw_key_idx, common->tkip_keymap))
|
|
+ ath_hw_keysetmac(common, hw_key_idx, NULL);
|
|
+ else
|
|
+ ath_hw_keyreset(common, hw_key_idx);
|
|
+ if (hw_key_idx < IEEE80211_WEP_NKID)
|
|
return;
|
|
|
|
- clear_bit(key->hw_key_idx, common->keymap);
|
|
- clear_bit(key->hw_key_idx, common->ccmp_keymap);
|
|
- if (key->cipher != WLAN_CIPHER_SUITE_TKIP)
|
|
+ clear_bit(hw_key_idx, common->keymap);
|
|
+ clear_bit(hw_key_idx, common->ccmp_keymap);
|
|
+ if (!test_bit(hw_key_idx, common->tkip_keymap))
|
|
return;
|
|
|
|
- clear_bit(key->hw_key_idx + 64, common->keymap);
|
|
+ clear_bit(hw_key_idx + 64, common->keymap);
|
|
|
|
- clear_bit(key->hw_key_idx, common->tkip_keymap);
|
|
- clear_bit(key->hw_key_idx + 64, common->tkip_keymap);
|
|
+ clear_bit(hw_key_idx, common->tkip_keymap);
|
|
+ clear_bit(hw_key_idx + 64, common->tkip_keymap);
|
|
|
|
if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) {
|
|
- ath_hw_keyreset(common, key->hw_key_idx + 32);
|
|
- clear_bit(key->hw_key_idx + 32, common->keymap);
|
|
- clear_bit(key->hw_key_idx + 64 + 32, common->keymap);
|
|
+ ath_hw_keyreset(common, hw_key_idx + 32);
|
|
+ clear_bit(hw_key_idx + 32, common->keymap);
|
|
+ clear_bit(hw_key_idx + 64 + 32, common->keymap);
|
|
|
|
- clear_bit(key->hw_key_idx + 32, common->tkip_keymap);
|
|
- clear_bit(key->hw_key_idx + 64 + 32, common->tkip_keymap);
|
|
+ clear_bit(hw_key_idx + 32, common->tkip_keymap);
|
|
+ clear_bit(hw_key_idx + 64 + 32, common->tkip_keymap);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(ath_key_delete);
|
|
diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c
|
|
index e263400..c242f00 100644
|
|
--- a/drivers/net/wireless/ath/regd.c
|
|
+++ b/drivers/net/wireless/ath/regd.c
|
|
@@ -733,7 +733,7 @@ static int __ath_regd_init(struct ath_regulatory *reg)
|
|
regdmn == CTRY_DEFAULT) {
|
|
printk(KERN_DEBUG "ath: EEPROM indicates default "
|
|
"country code should be used\n");
|
|
- reg->country_code = CTRY_UNITED_STATES;
|
|
+ reg->country_code = CTRY_EU;
|
|
}
|
|
|
|
if (reg->country_code == CTRY_DEFAULT) {
|
|
diff --git a/drivers/net/wireless/ath/regd.h b/drivers/net/wireless/ath/regd.h
|
|
index 8d5a16b..bb59c9d 100644
|
|
--- a/drivers/net/wireless/ath/regd.h
|
|
+++ b/drivers/net/wireless/ath/regd.h
|
|
@@ -254,7 +254,8 @@ enum CountryCode {
|
|
CTRY_JAPAN59 = 4059,
|
|
CTRY_AUSTRALIA2 = 5000,
|
|
CTRY_CANADA2 = 5001,
|
|
- CTRY_BELGIUM2 = 5002
|
|
+ CTRY_BELGIUM2 = 5002,
|
|
+ CTRY_EU = 500,
|
|
};
|
|
|
|
bool ath_is_world_regd(struct ath_regulatory *reg);
|
|
diff --git a/drivers/net/wireless/ath/regd_common.h b/drivers/net/wireless/ath/regd_common.h
|
|
index 364011e..4db829c 100644
|
|
--- a/drivers/net/wireless/ath/regd_common.h
|
|
+++ b/drivers/net/wireless/ath/regd_common.h
|
|
@@ -498,6 +498,7 @@ static struct country_code_to_enum_rd allCountries[] = {
|
|
{CTRY_VIET_NAM, NULL1_WORLD, "VN"},
|
|
{CTRY_YEMEN, NULL1_WORLD, "YE"},
|
|
{CTRY_ZIMBABWE, ETSI1_WORLD, "ZW"},
|
|
+ {CTRY_EU, ETSI1_WORLD, "EU"},
|
|
};
|
|
|
|
#endif
|
|
diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c
|
|
index 2a48fc6..888cfd7 100644
|
|
--- a/drivers/net/wireless/ti/wlcore/cmd.c
|
|
+++ b/drivers/net/wireless/ti/wlcore/cmd.c
|
|
@@ -1429,7 +1429,7 @@ out:
|
|
int wl1271_cmd_set_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|
u16 action, u8 id, u8 key_type,
|
|
u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
|
|
- u16 tx_seq_16)
|
|
+ u16 tx_seq_16, bool is_pairwise)
|
|
{
|
|
struct wl1271_cmd_set_keys *cmd;
|
|
int ret = 0;
|
|
@@ -1444,8 +1444,10 @@ int wl1271_cmd_set_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|
lid_type = WEP_DEFAULT_LID_TYPE;
|
|
else
|
|
lid_type = BROADCAST_LID_TYPE;
|
|
- } else {
|
|
+ } else if (is_pairwise) {
|
|
lid_type = UNICAST_LID_TYPE;
|
|
+ } else {
|
|
+ lid_type = BROADCAST_LID_TYPE;
|
|
}
|
|
|
|
wl1271_debug(DEBUG_CRYPT, "ap key action: %d id: %d lid: %d type: %d"
|
|
@@ -1565,13 +1567,6 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|
cpu_to_le32(wl1271_tx_enabled_rates_get(wl, sta_rates,
|
|
wlvif->band));
|
|
|
|
- if (!cmd->supported_rates) {
|
|
- wl1271_debug(DEBUG_CMD,
|
|
- "peer has no supported rates yet, configuring basic rates: 0x%x",
|
|
- wlvif->basic_rate_set);
|
|
- cmd->supported_rates = cpu_to_le32(wlvif->basic_rate_set);
|
|
- }
|
|
-
|
|
wl1271_debug(DEBUG_CMD, "new peer rates=0x%x queues=0x%x",
|
|
cmd->supported_rates, sta->uapsd_queues);
|
|
|
|
diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h
|
|
index 084375b..24ee7ab 100644
|
|
--- a/drivers/net/wireless/ti/wlcore/cmd.h
|
|
+++ b/drivers/net/wireless/ti/wlcore/cmd.h
|
|
@@ -65,7 +65,7 @@ int wl1271_cmd_set_sta_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|
int wl1271_cmd_set_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|
u16 action, u8 id, u8 key_type,
|
|
u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
|
|
- u16 tx_seq_16);
|
|
+ u16 tx_seq_16, bool is_pairwise);
|
|
int wl12xx_cmd_set_peer_state(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|
u8 hlid);
|
|
int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id,
|
|
@@ -458,6 +458,7 @@ enum wl1271_cmd_key_type {
|
|
KEY_TKIP = 2,
|
|
KEY_AES = 3,
|
|
KEY_GEM = 4,
|
|
+ KEY_IGTK = 5,
|
|
};
|
|
|
|
struct wl1271_cmd_set_keys {
|
|
diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h
|
|
index 6116383..e33f577 100644
|
|
--- a/drivers/net/wireless/ti/wlcore/conf.h
|
|
+++ b/drivers/net/wireless/ti/wlcore/conf.h
|
|
@@ -215,6 +215,9 @@ struct conf_rx_settings {
|
|
CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | \
|
|
CONF_HW_BIT_RATE_11MBPS)
|
|
|
|
+#define CONF_TX_OFDM_BASIC_RATES (CONF_HW_BIT_RATE_6MBPS | \
|
|
+ CONF_HW_BIT_RATE_12MBPS | CONF_HW_BIT_RATE_24MBPS)
|
|
+
|
|
#define CONF_TX_OFDM_RATES (CONF_HW_BIT_RATE_6MBPS | \
|
|
CONF_HW_BIT_RATE_12MBPS | CONF_HW_BIT_RATE_24MBPS | \
|
|
CONF_HW_BIT_RATE_36MBPS | CONF_HW_BIT_RATE_48MBPS | \
|
|
diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c
|
|
index 03b49ba..6334351 100644
|
|
--- a/drivers/net/wireless/ti/wlcore/init.c
|
|
+++ b/drivers/net/wireless/ti/wlcore/init.c
|
|
@@ -425,6 +425,7 @@ int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
|
int i, ret;
|
|
struct conf_tx_rate_class rc;
|
|
u32 supported_rates;
|
|
+ struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
|
|
|
|
wl1271_debug(DEBUG_AP, "AP basic rate set: 0x%x",
|
|
wlvif->basic_rate_set);
|
|
@@ -432,16 +433,27 @@ int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
|
if (wlvif->basic_rate_set == 0)
|
|
return -EINVAL;
|
|
|
|
- rc.enabled_rates = wlvif->basic_rate_set;
|
|
- rc.long_retry_limit = 10;
|
|
- rc.short_retry_limit = 10;
|
|
- rc.aflags = 0;
|
|
+ /*In order to handle mesh PREQ/PREP loss, increase retry limit
|
|
+ and use OFDM basic rates (mgmt policy)*/
|
|
+ if (ieee80211_vif_is_mesh(vif)) {
|
|
+ rc.enabled_rates = wlvif->basic_rate_set;
|
|
+ rc.long_retry_limit = 30;
|
|
+ rc.short_retry_limit = 30;
|
|
+ rc.aflags = 0;
|
|
+ }
|
|
+ else {
|
|
+ rc.enabled_rates = wlvif->basic_rate_set;
|
|
+ rc.long_retry_limit = 10;
|
|
+ rc.short_retry_limit = 10;
|
|
+ rc.aflags = 0;
|
|
+ }
|
|
+
|
|
ret = wl1271_acx_ap_rate_policy(wl, &rc, wlvif->ap.mgmt_rate_idx);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* use the min basic rate for AP broadcast/multicast */
|
|
- rc.enabled_rates = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
|
|
+ rc.enabled_rates = wlvif->basic_rate;
|
|
rc.short_retry_limit = 10;
|
|
rc.long_retry_limit = 10;
|
|
rc.aflags = 0;
|
|
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
|
|
index 6d36cbf..aa6f22c 100644
|
|
--- a/drivers/net/wireless/ti/wlcore/main.c
|
|
+++ b/drivers/net/wireless/ti/wlcore/main.c
|
|
@@ -2213,12 +2213,13 @@ static u8 wl12xx_get_role_type(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
|
static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
|
|
{
|
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
|
+ enum nl80211_iftype iftype = ieee80211_vif_type_p2p(vif);
|
|
int i;
|
|
|
|
/* clear everything but the persistent data */
|
|
memset(wlvif, 0, offsetof(struct wl12xx_vif, persistent));
|
|
|
|
- switch (ieee80211_vif_type_p2p(vif)) {
|
|
+ switch (iftype) {
|
|
case NL80211_IFTYPE_P2P_CLIENT:
|
|
wlvif->p2p = 1;
|
|
/* fall-through */
|
|
@@ -2265,13 +2266,14 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
|
|
for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++)
|
|
wl12xx_allocate_rate_policy(wl,
|
|
&wlvif->ap.ucast_rate_idx[i]);
|
|
- wlvif->basic_rate_set = CONF_TX_ENABLED_RATES;
|
|
- /*
|
|
- * TODO: check if basic_rate shouldn't be
|
|
- * wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
|
|
- * instead (the same thing for STA above).
|
|
- */
|
|
- wlvif->basic_rate = CONF_TX_ENABLED_RATES;
|
|
+ /* For mesh set default basic rate set to OFDM basic rate set only*/
|
|
+ if (iftype == NL80211_IFTYPE_MESH_POINT)
|
|
+ wlvif->basic_rate_set = CONF_TX_OFDM_BASIC_RATES;
|
|
+ else
|
|
+ wlvif->basic_rate_set = CONF_TX_ENABLED_RATES;
|
|
+
|
|
+ wlvif->basic_rate = wl1271_tx_min_rate_get(wl, CONF_TX_ENABLED_RATES);
|
|
+
|
|
/* TODO: this seems to be used only for STA, check it */
|
|
wlvif->rate_set = CONF_TX_ENABLED_RATES;
|
|
}
|
|
@@ -3274,7 +3276,7 @@ out:
|
|
static int wl1271_record_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|
u8 id, u8 key_type, u8 key_size,
|
|
const u8 *key, u8 hlid, u32 tx_seq_32,
|
|
- u16 tx_seq_16)
|
|
+ u16 tx_seq_16, bool is_pairwise)
|
|
{
|
|
struct wl1271_ap_key *ap_key;
|
|
int i;
|
|
@@ -3312,6 +3314,7 @@ static int wl1271_record_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|
ap_key->hlid = hlid;
|
|
ap_key->tx_seq_32 = tx_seq_32;
|
|
ap_key->tx_seq_16 = tx_seq_16;
|
|
+ ap_key->is_pairwise = is_pairwise;
|
|
|
|
wlvif->ap.recorded_keys[i] = ap_key;
|
|
return 0;
|
|
@@ -3347,7 +3350,7 @@ static int wl1271_ap_init_hwenc(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
|
key->id, key->key_type,
|
|
key->key_size, key->key,
|
|
hlid, key->tx_seq_32,
|
|
- key->tx_seq_16);
|
|
+ key->tx_seq_16, key->is_pairwise);
|
|
if (ret < 0)
|
|
goto out;
|
|
|
|
@@ -3370,7 +3373,8 @@ out:
|
|
static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|
u16 action, u8 id, u8 key_type,
|
|
u8 key_size, const u8 *key, u32 tx_seq_32,
|
|
- u16 tx_seq_16, struct ieee80211_sta *sta)
|
|
+ u16 tx_seq_16, struct ieee80211_sta *sta,
|
|
+ bool is_pairwise)
|
|
{
|
|
int ret;
|
|
bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
|
|
@@ -3397,12 +3401,12 @@ static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|
ret = wl1271_record_ap_key(wl, wlvif, id,
|
|
key_type, key_size,
|
|
key, hlid, tx_seq_32,
|
|
- tx_seq_16);
|
|
+ tx_seq_16, is_pairwise);
|
|
} else {
|
|
ret = wl1271_cmd_set_ap_key(wl, wlvif, action,
|
|
id, key_type, key_size,
|
|
key, hlid, tx_seq_32,
|
|
- tx_seq_16);
|
|
+ tx_seq_16, is_pairwise);
|
|
}
|
|
|
|
if (ret < 0)
|
|
@@ -3502,6 +3506,7 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
|
|
u16 tx_seq_16 = 0;
|
|
u8 key_type;
|
|
u8 hlid;
|
|
+ bool is_pairwise;
|
|
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 set key");
|
|
|
|
@@ -3538,6 +3543,9 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
|
|
key_type = KEY_TKIP;
|
|
key_conf->hw_key_idx = key_conf->keyidx;
|
|
break;
|
|
+ case WLAN_CIPHER_SUITE_AES_CMAC:
|
|
+ key_type = KEY_IGTK;
|
|
+ break;
|
|
case WLAN_CIPHER_SUITE_CCMP:
|
|
key_type = KEY_AES;
|
|
key_conf->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
|
|
@@ -3551,12 +3559,14 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
+ is_pairwise = key_conf->flags & IEEE80211_KEY_FLAG_PAIRWISE;
|
|
+
|
|
switch (cmd) {
|
|
case SET_KEY:
|
|
ret = wl1271_set_key(wl, wlvif, KEY_ADD_OR_REPLACE,
|
|
key_conf->keyidx, key_type,
|
|
key_conf->keylen, key_conf->key,
|
|
- tx_seq_32, tx_seq_16, sta);
|
|
+ tx_seq_32, tx_seq_16, sta, is_pairwise);
|
|
if (ret < 0) {
|
|
wl1271_error("Could not add or replace key");
|
|
return ret;
|
|
@@ -3582,7 +3592,7 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
|
|
ret = wl1271_set_key(wl, wlvif, KEY_REMOVE,
|
|
key_conf->keyidx, key_type,
|
|
key_conf->keylen, key_conf->key,
|
|
- 0, 0, sta);
|
|
+ 0, 0, sta, is_pairwise);
|
|
if (ret < 0) {
|
|
wl1271_error("Could not remove key");
|
|
return ret;
|
|
@@ -5216,11 +5226,6 @@ static int wl12xx_update_sta_state(struct wl1271 *wl,
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
- /* reconfigure rates */
|
|
- ret = wl12xx_cmd_add_peer(wl, wlvif, sta, wl_sta->hlid);
|
|
- if (ret < 0)
|
|
- return ret;
|
|
-
|
|
ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true,
|
|
wl_sta->hlid);
|
|
if (ret)
|
|
@@ -5794,9 +5799,16 @@ static void wlcore_op_sta_statistics(struct ieee80211_hw *hw,
|
|
{
|
|
struct wl1271 *wl = hw->priv;
|
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
|
+ u8 role_type;
|
|
s8 rssi_dbm;
|
|
int ret;
|
|
|
|
+ role_type = wl12xx_get_role_type(wl, wlvif);
|
|
+ if (role_type == WL1271_ROLE_AP ||
|
|
+ role_type == WL1271_ROLE_MESH_POINT) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 get_rssi");
|
|
|
|
mutex_lock(&wl->mutex);
|
|
@@ -6210,6 +6222,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
|
|
WLAN_CIPHER_SUITE_TKIP,
|
|
WLAN_CIPHER_SUITE_CCMP,
|
|
WL1271_CIPHER_SUITE_GEM,
|
|
+ WLAN_CIPHER_SUITE_AES_CMAC,
|
|
};
|
|
|
|
/* The tx descriptor buffer */
|
|
@@ -6224,6 +6237,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
|
|
|
|
ieee80211_hw_set(wl->hw, SUPPORT_FAST_XMIT);
|
|
ieee80211_hw_set(wl->hw, CHANCTX_STA_CSA);
|
|
+ ieee80211_hw_set(wl->hw, SUPPORTS_PER_STA_GTK);
|
|
ieee80211_hw_set(wl->hw, QUEUE_CONTROL);
|
|
ieee80211_hw_set(wl->hw, TX_AMPDU_SETUP_IN_HW);
|
|
ieee80211_hw_set(wl->hw, AMPDU_AGGREGATION);
|
|
@@ -6268,9 +6282,11 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
|
|
|
|
wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD |
|
|
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
|
|
- WIPHY_FLAG_HAS_CHANNEL_SWITCH;
|
|
+ WIPHY_FLAG_HAS_CHANNEL_SWITCH |
|
|
+ WIPHY_FLAG_IBSS_RSN;
|
|
|
|
wl->hw->wiphy->features |= NL80211_FEATURE_AP_SCAN;
|
|
+ wl->hw->wiphy->features &= ~NL80211_FEATURE_FULL_AP_CLIENT_STATE;
|
|
|
|
/* make sure all our channels fit in the scanned_ch bitmask */
|
|
BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) +
|
|
diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h
|
|
index 6fab60b..eefae3f 100644
|
|
--- a/drivers/net/wireless/ti/wlcore/wlcore_i.h
|
|
+++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h
|
|
@@ -212,6 +212,7 @@ struct wl1271_ap_key {
|
|
u8 hlid;
|
|
u32 tx_seq_32;
|
|
u16 tx_seq_16;
|
|
+ bool is_pairwise;
|
|
};
|
|
|
|
enum wl12xx_flags {
|
|
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
|
|
index 75ecfeb..3b4b4e2 100644
|
|
--- a/include/net/cfg80211.h
|
|
+++ b/include/net/cfg80211.h
|
|
@@ -112,6 +112,7 @@ enum ieee80211_channel_flags {
|
|
IEEE80211_CHAN_IR_CONCURRENT = 1<<10,
|
|
IEEE80211_CHAN_NO_20MHZ = 1<<11,
|
|
IEEE80211_CHAN_NO_10MHZ = 1<<12,
|
|
+ IEEE80211_CHAN_SRD = 1<<13,
|
|
};
|
|
|
|
#define IEEE80211_CHAN_NO_HT40 \
|
|
@@ -3456,6 +3457,8 @@ struct cfg80211_update_owe_info {
|
|
* return 0 if successful
|
|
* @set_antenna_gain: set antenna gain to reduce maximum tx power if necessary
|
|
*
|
|
+ * @get_antenna_gain: get antenna gain
|
|
+ *
|
|
* @set_wds_peer: set the WDS peer for a WDS interface
|
|
*
|
|
* @rfkill_poll: polls the hw rfkill line, use cfg80211 reporting
|
|
@@ -3771,6 +3774,7 @@ struct cfg80211_ops {
|
|
int (*get_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev,
|
|
int *dbm);
|
|
int (*set_antenna_gain)(struct wiphy *wiphy, int dbi);
|
|
+ int (*get_antenna_gain)(struct wiphy *wiphy, int *dbi);
|
|
|
|
int (*set_wds_peer)(struct wiphy *wiphy, struct net_device *dev,
|
|
const u8 *addr);
|
|
@@ -4771,6 +4775,8 @@ static inline const char *wiphy_name(const struct wiphy *wiphy)
|
|
* @sizeof_priv: The size of the private area to allocate
|
|
* @requested_name: Request a particular name.
|
|
* NULL is valid value, and means use the default phy%d naming.
|
|
+ * @requested_index: Request a particular index.
|
|
+ * -1 is valid value, and means use the default phy%d naming.
|
|
*
|
|
* Create a new wiphy and associate the given operations with it.
|
|
* @sizeof_priv bytes are allocated for private use.
|
|
@@ -4779,7 +4785,7 @@ static inline const char *wiphy_name(const struct wiphy *wiphy)
|
|
* assigned to each netdev's ieee80211_ptr for proper operation.
|
|
*/
|
|
struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
|
|
- const char *requested_name);
|
|
+ const char *requested_name, const int requested_index);
|
|
|
|
/**
|
|
* wiphy_new - create a new wiphy for use with cfg80211
|
|
@@ -4796,7 +4802,7 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
|
|
static inline struct wiphy *wiphy_new(const struct cfg80211_ops *ops,
|
|
int sizeof_priv)
|
|
{
|
|
- return wiphy_new_nm(ops, sizeof_priv, NULL);
|
|
+ return wiphy_new_nm(ops, sizeof_priv, NULL, -1);
|
|
}
|
|
|
|
/**
|
|
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
|
|
index bca141e..ae3316f 100644
|
|
--- a/include/net/mac80211.h
|
|
+++ b/include/net/mac80211.h
|
|
@@ -4087,12 +4087,15 @@ struct ieee80211_ops {
|
|
* @ops: callbacks for this device
|
|
* @requested_name: Requested name for this device.
|
|
* NULL is valid value, and means use the default naming (phy%d)
|
|
+ * @requested_name: Requested index for this device.
|
|
+ * -1 is valid value, and means use the default naming (phy%d)
|
|
*
|
|
* Return: A pointer to the new hardware device, or %NULL on error.
|
|
*/
|
|
struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
|
|
const struct ieee80211_ops *ops,
|
|
- const char *requested_name);
|
|
+ const char *requested_name,
|
|
+ int requested_index);
|
|
|
|
/**
|
|
* ieee80211_alloc_hw - Allocate a new hardware device
|
|
@@ -4112,7 +4115,7 @@ static inline
|
|
struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
|
const struct ieee80211_ops *ops)
|
|
{
|
|
- return ieee80211_alloc_hw_nm(priv_data_len, ops, NULL);
|
|
+ return ieee80211_alloc_hw_nm(priv_data_len, ops, NULL, -1);
|
|
}
|
|
|
|
/**
|
|
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
|
|
index ef08258..2f41258 100644
|
|
--- a/include/uapi/linux/nl80211.h
|
|
+++ b/include/uapi/linux/nl80211.h
|
|
@@ -3559,6 +3559,8 @@ enum nl80211_wmm_rule {
|
|
* @NL80211_FREQUENCY_ATTR_WMM: this channel has wmm limitations.
|
|
* This is a nested attribute that contains the wmm limitation per AC.
|
|
* (see &enum nl80211_wmm_rule)
|
|
+ * @NL80211_FREQUENCY_ATTR_SRD_CHANNEL: short range devices mode
|
|
+ * on this channel in current regulatory domain.
|
|
* @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
|
|
* currently defined
|
|
* @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
|
|
@@ -3588,6 +3590,7 @@ enum nl80211_frequency_attr {
|
|
NL80211_FREQUENCY_ATTR_NO_20MHZ,
|
|
NL80211_FREQUENCY_ATTR_NO_10MHZ,
|
|
NL80211_FREQUENCY_ATTR_WMM,
|
|
+ NL80211_FREQUENCY_ATTR_SRD_CHANNEL,
|
|
|
|
/* keep last */
|
|
__NL80211_FREQUENCY_ATTR_AFTER_LAST,
|
|
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
|
|
index 993ee7b..13bd5b2 100644
|
|
--- a/net/mac80211/cfg.c
|
|
+++ b/net/mac80211/cfg.c
|
|
@@ -2595,6 +2595,18 @@ static int ieee80211_set_antenna_gain(struct wiphy *wiphy, int dbi)
|
|
return 0;
|
|
}
|
|
|
|
+static int ieee80211_get_antenna_gain(struct wiphy *wiphy,
|
|
+ int *dbi)
|
|
+{
|
|
+ struct ieee80211_local *local = wiphy_priv(wiphy);
|
|
+
|
|
+ if (local) {
|
|
+ *dbi = local->user_antenna_gain;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int ieee80211_set_wds_peer(struct wiphy *wiphy, struct net_device *dev,
|
|
const u8 *addr)
|
|
{
|
|
@@ -4031,6 +4043,7 @@ const struct cfg80211_ops mac80211_config_ops = {
|
|
.set_tx_power = ieee80211_set_tx_power,
|
|
.get_tx_power = ieee80211_get_tx_power,
|
|
.set_antenna_gain = ieee80211_set_antenna_gain,
|
|
+ .get_antenna_gain = ieee80211_get_antenna_gain,
|
|
.set_wds_peer = ieee80211_set_wds_peer,
|
|
.rfkill_poll = ieee80211_rfkill_poll,
|
|
CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
|
|
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
|
|
index 71dada6..7bfa7bb 100644
|
|
--- a/net/mac80211/main.c
|
|
+++ b/net/mac80211/main.c
|
|
@@ -512,7 +512,7 @@ static const struct ieee80211_vht_cap mac80211_vht_capa_mod_mask = {
|
|
|
|
struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
|
|
const struct ieee80211_ops *ops,
|
|
- const char *requested_name)
|
|
+ const char *requested_name, int requested_idx)
|
|
{
|
|
struct ieee80211_local *local;
|
|
int priv_size, i;
|
|
@@ -552,7 +552,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
|
|
*/
|
|
priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len;
|
|
|
|
- wiphy = wiphy_new_nm(&mac80211_config_ops, priv_size, requested_name);
|
|
+ wiphy = wiphy_new_nm(&mac80211_config_ops, priv_size, requested_name, requested_idx);
|
|
|
|
if (!wiphy)
|
|
return NULL;
|
|
diff --git a/net/wireless/core.c b/net/wireless/core.c
|
|
index c8a6c0e..847d4f6 100644
|
|
--- a/net/wireless/core.c
|
|
+++ b/net/wireless/core.c
|
|
@@ -123,6 +123,19 @@ static int cfg80211_dev_check_name(struct cfg80211_registered_device *rdev,
|
|
return 0;
|
|
}
|
|
|
|
+static int cfg80211_dev_check_index(const int index)
|
|
+{
|
|
+ struct cfg80211_registered_device *rdev;
|
|
+
|
|
+ ASSERT_RTNL();
|
|
+
|
|
+ /* Ensure another device does not already have this name. */
|
|
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list)
|
|
+ if (rdev->wiphy_idx == index)
|
|
+ return -EINVAL;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
|
|
char *newname)
|
|
{
|
|
@@ -396,7 +409,7 @@ static void cfg80211_propagate_cac_done_wk(struct work_struct *work)
|
|
/* exported functions */
|
|
|
|
struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
|
|
- const char *requested_name)
|
|
+ const char *requested_name, const int requested_index)
|
|
{
|
|
static atomic_t wiphy_counter = ATOMIC_INIT(0);
|
|
|
|
@@ -439,17 +452,31 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
|
|
|
|
rdev->ops = ops;
|
|
|
|
- rdev->wiphy_idx = atomic_inc_return(&wiphy_counter);
|
|
+ if (requested_index >= 0) {
|
|
+ int rv;
|
|
|
|
- if (unlikely(rdev->wiphy_idx < 0)) {
|
|
+ rtnl_lock();
|
|
+ rv = cfg80211_dev_check_index(requested_index);
|
|
+
|
|
+ if (rv < 0) {
|
|
+ rtnl_unlock();
|
|
+ goto use_default_index;
|
|
+ }
|
|
+ rdev->wiphy_idx = requested_index;
|
|
+ rtnl_unlock();
|
|
+ } else {
|
|
+use_default_index:
|
|
+ rdev->wiphy_idx = atomic_inc_return(&wiphy_counter);
|
|
+
|
|
+ if (unlikely(rdev->wiphy_idx < 0)) {
|
|
/* ugh, wrapped! */
|
|
atomic_dec(&wiphy_counter);
|
|
kfree(rdev);
|
|
return NULL;
|
|
- }
|
|
-
|
|
- /* atomic_inc_return makes it start at 1, make it start at 0 */
|
|
- rdev->wiphy_idx--;
|
|
+ }
|
|
+ /* atomic_inc_return makes it start at 1, make it start at 0 */
|
|
+ rdev->wiphy_idx--;
|
|
+ }
|
|
|
|
/* give it a proper name */
|
|
if (requested_name && requested_name[0]) {
|
|
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
|
|
index c93a4e0..1b3c0d9 100644
|
|
--- a/net/wireless/nl80211.c
|
|
+++ b/net/wireless/nl80211.c
|
|
@@ -970,6 +970,9 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy,
|
|
if ((chan->flags & IEEE80211_CHAN_NO_10MHZ) &&
|
|
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_10MHZ))
|
|
goto nla_put_failure;
|
|
+ if ((chan->flags & IEEE80211_CHAN_SRD) &&
|
|
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_SRD_CHANNEL))
|
|
+ goto nla_put_failure;
|
|
}
|
|
|
|
if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
|
|
@@ -3253,6 +3256,16 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag
|
|
goto nla_put_failure;
|
|
}
|
|
|
|
+ if (rdev->ops->get_antenna_gain) {
|
|
+ int dbi = 0, ret;
|
|
+
|
|
+ ret = rdev->ops->get_antenna_gain(&rdev->wiphy, &dbi);
|
|
+ if (ret == 0 &&
|
|
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_GAIN,
|
|
+ DBM_TO_MBM(dbi)))
|
|
+ goto nla_put_failure;
|
|
+ }
|
|
+
|
|
wdev_lock(wdev);
|
|
switch (wdev->iftype) {
|
|
case NL80211_IFTYPE_AP:
|
|
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
|
|
index a2908b8..ed16782 100644
|
|
--- a/net/wireless/reg.c
|
|
+++ b/net/wireless/reg.c
|
|
@@ -68,6 +68,7 @@
|
|
* channels allowed by the current regulatory domain.
|
|
*/
|
|
#define REG_ENFORCE_GRACE_MS 60000
|
|
+#define REG_CHANNEL_LOW_PWR_DBM 15
|
|
|
|
/**
|
|
* enum reg_request_treatment - regulatory request treatment
|
|
@@ -1778,11 +1779,21 @@ static void handle_channel(struct wiphy *wiphy,
|
|
MBI_TO_DBI(power_rule->max_antenna_gain));
|
|
chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp);
|
|
|
|
+ if (chan->max_reg_power <= REG_CHANNEL_LOW_PWR_DBM) {
|
|
+ chan->flags |= IEEE80211_CHAN_SRD;
|
|
+ }
|
|
+
|
|
if (chan->flags & IEEE80211_CHAN_RADAR) {
|
|
- if (reg_rule->dfs_cac_ms)
|
|
- chan->dfs_cac_ms = reg_rule->dfs_cac_ms;
|
|
- else
|
|
- chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
|
|
+ if (reg_rule->dfs_cac_ms)
|
|
+ chan->dfs_cac_ms = reg_rule->dfs_cac_ms;
|
|
+ else if (regd->dfs_region == NL80211_DFS_ETSI &&
|
|
+ chan->center_freq >= 5600 &&
|
|
+ chan->center_freq <= 5640) {
|
|
+ /* TDWR channels require 10 min */
|
|
+ chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS * 10;
|
|
+ } else {
|
|
+ chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
|
|
+ }
|
|
}
|
|
|
|
if (chan->orig_mpwr) {
|
|
@@ -2314,6 +2325,10 @@ static void handle_channel_custom(struct wiphy *wiphy,
|
|
}
|
|
|
|
chan->max_power = chan->max_reg_power;
|
|
+
|
|
+ if (chan->max_reg_power <= REG_CHANNEL_LOW_PWR_DBM) {
|
|
+ chan->flags |= IEEE80211_CHAN_SRD;
|
|
+ }
|
|
}
|
|
|
|
static void handle_band_custom(struct wiphy *wiphy,
|