mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-10 07:10:27 +00:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
This commit is contained in:
commit
a006827a15
@ -1001,6 +1001,26 @@ struct ieee80211_vendor_ie {
|
||||
u8 oui_type;
|
||||
} __packed;
|
||||
|
||||
struct ieee80211_wmm_ac_param {
|
||||
u8 aci_aifsn; /* AIFSN, ACM, ACI */
|
||||
u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */
|
||||
__le16 txop_limit;
|
||||
} __packed;
|
||||
|
||||
struct ieee80211_wmm_param_ie {
|
||||
u8 element_id; /* Element ID: 221 (0xdd); */
|
||||
u8 len; /* Length: 24 */
|
||||
/* required fields for WMM version 1 */
|
||||
u8 oui[3]; /* 00:50:f2 */
|
||||
u8 oui_type; /* 2 */
|
||||
u8 oui_subtype; /* 1 */
|
||||
u8 version; /* 1 for WMM version 1.0 */
|
||||
u8 qos_info; /* AP/STA specific QoS info */
|
||||
u8 reserved; /* 0 */
|
||||
/* AC_BE, AC_BK, AC_VI, AC_VO */
|
||||
struct ieee80211_wmm_ac_param ac[4];
|
||||
} __packed;
|
||||
|
||||
/* Control frames */
|
||||
struct ieee80211_rts {
|
||||
__le16 frame_control;
|
||||
|
@ -4552,6 +4552,40 @@ void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap,
|
||||
*/
|
||||
void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn);
|
||||
|
||||
/**
|
||||
* ieee80211_start_rx_ba_session_offl - start a Rx BA session
|
||||
*
|
||||
* Some device drivers may offload part of the Rx aggregation flow including
|
||||
* AddBa/DelBa negotiation but may otherwise be incapable of full Rx
|
||||
* reordering.
|
||||
*
|
||||
* Create structures responsible for reordering so device drivers may call here
|
||||
* when they complete AddBa negotiation.
|
||||
*
|
||||
* @vif: &struct ieee80211_vif pointer from the add_interface callback
|
||||
* @addr: station mac address
|
||||
* @tid: the rx tid
|
||||
*/
|
||||
void ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif,
|
||||
const u8 *addr, u16 tid);
|
||||
|
||||
/**
|
||||
* ieee80211_stop_rx_ba_session_offl - stop a Rx BA session
|
||||
*
|
||||
* Some device drivers may offload part of the Rx aggregation flow including
|
||||
* AddBa/DelBa negotiation but may otherwise be incapable of full Rx
|
||||
* reordering.
|
||||
*
|
||||
* Destroy structures responsible for reordering so device drivers may call here
|
||||
* when they complete DelBa negotiation.
|
||||
*
|
||||
* @vif: &struct ieee80211_vif pointer from the add_interface callback
|
||||
* @addr: station mac address
|
||||
* @tid: the rx tid
|
||||
*/
|
||||
void ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif,
|
||||
const u8 *addr, u16 tid);
|
||||
|
||||
/* Rate control API */
|
||||
|
||||
/**
|
||||
|
@ -52,7 +52,7 @@ static void ieee80211_free_tid_rx(struct rcu_head *h)
|
||||
del_timer_sync(&tid_rx->reorder_timer);
|
||||
|
||||
for (i = 0; i < tid_rx->buf_size; i++)
|
||||
dev_kfree_skb(tid_rx->reorder_buf[i]);
|
||||
__skb_queue_purge(&tid_rx->reorder_buf[i]);
|
||||
kfree(tid_rx->reorder_buf);
|
||||
kfree(tid_rx->reorder_time);
|
||||
kfree(tid_rx);
|
||||
@ -224,28 +224,15 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d
|
||||
ieee80211_tx_skb(sdata, skb);
|
||||
}
|
||||
|
||||
void ieee80211_process_addba_request(struct ieee80211_local *local,
|
||||
struct sta_info *sta,
|
||||
struct ieee80211_mgmt *mgmt,
|
||||
size_t len)
|
||||
void __ieee80211_start_rx_ba_session(struct sta_info *sta,
|
||||
u8 dialog_token, u16 timeout,
|
||||
u16 start_seq_num, u16 ba_policy, u16 tid,
|
||||
u16 buf_size, bool tx)
|
||||
{
|
||||
struct ieee80211_local *local = sta->sdata->local;
|
||||
struct tid_ampdu_rx *tid_agg_rx;
|
||||
u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num, status;
|
||||
u8 dialog_token;
|
||||
int ret = -EOPNOTSUPP;
|
||||
|
||||
/* extract session parameters from addba request frame */
|
||||
dialog_token = mgmt->u.action.u.addba_req.dialog_token;
|
||||
timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout);
|
||||
start_seq_num =
|
||||
le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4;
|
||||
|
||||
capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
|
||||
ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1;
|
||||
tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
|
||||
buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
|
||||
|
||||
status = WLAN_STATUS_REQUEST_DECLINED;
|
||||
int i, ret = -EOPNOTSUPP;
|
||||
u16 status = WLAN_STATUS_REQUEST_DECLINED;
|
||||
|
||||
if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
|
||||
ht_dbg(sta->sdata,
|
||||
@ -264,7 +251,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
|
||||
status = WLAN_STATUS_INVALID_QOS_PARAM;
|
||||
ht_dbg_ratelimited(sta->sdata,
|
||||
"AddBA Req with bad params from %pM on tid %u. policy %d, buffer size %d\n",
|
||||
mgmt->sa, tid, ba_policy, buf_size);
|
||||
sta->sta.addr, tid, ba_policy, buf_size);
|
||||
goto end_no_lock;
|
||||
}
|
||||
/* determine default buffer size */
|
||||
@ -281,7 +268,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
|
||||
if (sta->ampdu_mlme.tid_rx[tid]) {
|
||||
ht_dbg_ratelimited(sta->sdata,
|
||||
"unexpected AddBA Req from %pM on tid %u\n",
|
||||
mgmt->sa, tid);
|
||||
sta->sta.addr, tid);
|
||||
|
||||
/* delete existing Rx BA session on the same tid */
|
||||
___ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT,
|
||||
@ -308,7 +295,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
|
||||
|
||||
/* prepare reordering buffer */
|
||||
tid_agg_rx->reorder_buf =
|
||||
kcalloc(buf_size, sizeof(struct sk_buff *), GFP_KERNEL);
|
||||
kcalloc(buf_size, sizeof(struct sk_buff_head), GFP_KERNEL);
|
||||
tid_agg_rx->reorder_time =
|
||||
kcalloc(buf_size, sizeof(unsigned long), GFP_KERNEL);
|
||||
if (!tid_agg_rx->reorder_buf || !tid_agg_rx->reorder_time) {
|
||||
@ -318,6 +305,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
|
||||
goto end;
|
||||
}
|
||||
|
||||
for (i = 0; i < buf_size; i++)
|
||||
__skb_queue_head_init(&tid_agg_rx->reorder_buf[i]);
|
||||
|
||||
ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START,
|
||||
&sta->sta, tid, &start_seq_num, 0);
|
||||
ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n",
|
||||
@ -350,6 +340,74 @@ end:
|
||||
mutex_unlock(&sta->ampdu_mlme.mtx);
|
||||
|
||||
end_no_lock:
|
||||
ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid,
|
||||
dialog_token, status, 1, buf_size, timeout);
|
||||
if (tx)
|
||||
ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid,
|
||||
dialog_token, status, 1, buf_size,
|
||||
timeout);
|
||||
}
|
||||
|
||||
void ieee80211_process_addba_request(struct ieee80211_local *local,
|
||||
struct sta_info *sta,
|
||||
struct ieee80211_mgmt *mgmt,
|
||||
size_t len)
|
||||
{
|
||||
u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num;
|
||||
u8 dialog_token;
|
||||
|
||||
/* extract session parameters from addba request frame */
|
||||
dialog_token = mgmt->u.action.u.addba_req.dialog_token;
|
||||
timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout);
|
||||
start_seq_num =
|
||||
le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4;
|
||||
|
||||
capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
|
||||
ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1;
|
||||
tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
|
||||
buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
|
||||
|
||||
__ieee80211_start_rx_ba_session(sta, dialog_token, timeout,
|
||||
start_seq_num, ba_policy, tid,
|
||||
buf_size, true);
|
||||
}
|
||||
|
||||
void ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif,
|
||||
const u8 *addr, u16 tid)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_rx_agg *rx_agg;
|
||||
struct sk_buff *skb = dev_alloc_skb(0);
|
||||
|
||||
if (unlikely(!skb))
|
||||
return;
|
||||
|
||||
rx_agg = (struct ieee80211_rx_agg *) &skb->cb;
|
||||
memcpy(&rx_agg->addr, addr, ETH_ALEN);
|
||||
rx_agg->tid = tid;
|
||||
|
||||
skb->pkt_type = IEEE80211_SDATA_QUEUE_RX_AGG_START;
|
||||
skb_queue_tail(&sdata->skb_queue, skb);
|
||||
ieee80211_queue_work(&local->hw, &sdata->work);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_start_rx_ba_session_offl);
|
||||
|
||||
void ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif,
|
||||
const u8 *addr, u16 tid)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_rx_agg *rx_agg;
|
||||
struct sk_buff *skb = dev_alloc_skb(0);
|
||||
|
||||
if (unlikely(!skb))
|
||||
return;
|
||||
|
||||
rx_agg = (struct ieee80211_rx_agg *) &skb->cb;
|
||||
memcpy(&rx_agg->addr, addr, ETH_ALEN);
|
||||
rx_agg->tid = tid;
|
||||
|
||||
skb->pkt_type = IEEE80211_SDATA_QUEUE_RX_AGG_STOP;
|
||||
skb_queue_tail(&sdata->skb_queue, skb);
|
||||
ieee80211_queue_work(&local->hw, &sdata->work);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_stop_rx_ba_session_offl);
|
||||
|
@ -66,7 +66,7 @@ static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local)
|
||||
static struct ieee80211_chanctx *
|
||||
ieee80211_vif_get_chanctx(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_local *local __maybe_unused = sdata->local;
|
||||
struct ieee80211_chanctx_conf *conf;
|
||||
|
||||
conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
|
||||
|
@ -150,13 +150,12 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
/*
|
||||
* If user has specified capability over-rides, take care
|
||||
* of that if the station we're setting up is the AP that
|
||||
* of that if the station we're setting up is the AP or TDLS peer that
|
||||
* we advertised a restricted capability set to. Override
|
||||
* our own capabilities and then use those below.
|
||||
*/
|
||||
if ((sdata->vif.type == NL80211_IFTYPE_STATION ||
|
||||
sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
|
||||
!test_sta_flag(sta, WLAN_STA_TDLS_PEER))
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION ||
|
||||
sdata->vif.type == NL80211_IFTYPE_ADHOC)
|
||||
ieee80211_apply_htcap_overrides(sdata, &own_cap);
|
||||
|
||||
/*
|
||||
@ -228,6 +227,9 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
|
||||
if (own_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
|
||||
ht_cap.mcs.rx_mask[32/8] |= 1;
|
||||
|
||||
/* set Rx highest rate */
|
||||
ht_cap.mcs.rx_highest = ht_cap_ie->mcs.rx_highest;
|
||||
|
||||
apply:
|
||||
changed = memcmp(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));
|
||||
|
||||
|
@ -189,17 +189,8 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
|
||||
chandef, 0);
|
||||
}
|
||||
|
||||
if (local->hw.queues >= IEEE80211_NUM_ACS) {
|
||||
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
|
||||
*pos++ = 7; /* len */
|
||||
*pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
|
||||
*pos++ = 0x50;
|
||||
*pos++ = 0xf2;
|
||||
*pos++ = 2; /* WME */
|
||||
*pos++ = 0; /* WME info */
|
||||
*pos++ = 1; /* WME ver */
|
||||
*pos++ = 0; /* U-APSD no in use */
|
||||
}
|
||||
if (local->hw.queues >= IEEE80211_NUM_ACS)
|
||||
pos = ieee80211_add_wmm_info_ie(pos, 0); /* U-APSD not in use */
|
||||
|
||||
presp->head_len = pos - presp->head;
|
||||
if (WARN_ON(presp->head_len > frame_len))
|
||||
|
@ -345,7 +345,6 @@ enum ieee80211_sta_flags {
|
||||
IEEE80211_STA_CONNECTION_POLL = BIT(1),
|
||||
IEEE80211_STA_CONTROL_PORT = BIT(2),
|
||||
IEEE80211_STA_DISABLE_HT = BIT(4),
|
||||
IEEE80211_STA_CSA_RECEIVED = BIT(5),
|
||||
IEEE80211_STA_MFP_ENABLED = BIT(6),
|
||||
IEEE80211_STA_UAPSD_ENABLED = BIT(7),
|
||||
IEEE80211_STA_NULLFUNC_ACKED = BIT(8),
|
||||
@ -503,6 +502,9 @@ struct ieee80211_if_managed {
|
||||
struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */
|
||||
struct ieee80211_vht_cap vht_capa; /* configured VHT overrides */
|
||||
struct ieee80211_vht_cap vht_capa_mask; /* Valid parts of vht_capa */
|
||||
|
||||
u8 tdls_peer[ETH_ALEN] __aligned(2);
|
||||
struct delayed_work tdls_peer_del_work;
|
||||
};
|
||||
|
||||
struct ieee80211_if_ibss {
|
||||
@ -815,9 +817,6 @@ struct ieee80211_sub_if_data {
|
||||
bool radar_required;
|
||||
struct delayed_work dfs_cac_timer_work;
|
||||
|
||||
u8 tdls_peer[ETH_ALEN] __aligned(2);
|
||||
struct delayed_work tdls_peer_del_work;
|
||||
|
||||
/*
|
||||
* AP this belongs to: self in AP mode and
|
||||
* corresponding AP in VLAN mode, NULL for
|
||||
@ -926,10 +925,17 @@ ieee80211_vif_get_shift(struct ieee80211_vif *vif)
|
||||
return shift;
|
||||
}
|
||||
|
||||
struct ieee80211_rx_agg {
|
||||
u8 addr[ETH_ALEN];
|
||||
u16 tid;
|
||||
};
|
||||
|
||||
enum sdata_queue_type {
|
||||
IEEE80211_SDATA_QUEUE_TYPE_FRAME = 0,
|
||||
IEEE80211_SDATA_QUEUE_AGG_START = 1,
|
||||
IEEE80211_SDATA_QUEUE_AGG_STOP = 2,
|
||||
IEEE80211_SDATA_QUEUE_RX_AGG_START = 3,
|
||||
IEEE80211_SDATA_QUEUE_RX_AGG_STOP = 4,
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -1578,6 +1584,10 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
|
||||
u16 initiator, u16 reason, bool stop);
|
||||
void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
|
||||
u16 initiator, u16 reason, bool stop);
|
||||
void __ieee80211_start_rx_ba_session(struct sta_info *sta,
|
||||
u8 dialog_token, u16 timeout,
|
||||
u16 start_seq_num, u16 ba_policy, u16 tid,
|
||||
u16 buf_size, bool tx);
|
||||
void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
|
||||
enum ieee80211_agg_stop_reason reason);
|
||||
void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
|
||||
@ -1730,6 +1740,21 @@ static inline void ieee802_11_parse_elems(const u8 *start, size_t len,
|
||||
ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0);
|
||||
}
|
||||
|
||||
static inline bool ieee80211_rx_reorder_ready(struct sk_buff_head *frames)
|
||||
{
|
||||
struct sk_buff *tail = skb_peek_tail(frames);
|
||||
struct ieee80211_rx_status *status;
|
||||
|
||||
if (!tail)
|
||||
return false;
|
||||
|
||||
status = IEEE80211_SKB_RXCB(tail);
|
||||
if (status->flag & RX_FLAG_AMSDU_MORE)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ieee80211_dynamic_ps_enable_work(struct work_struct *work);
|
||||
void ieee80211_dynamic_ps_disable_work(struct work_struct *work);
|
||||
void ieee80211_dynamic_ps_timer(unsigned long data);
|
||||
@ -1824,6 +1849,7 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
|
||||
int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, bool need_basic,
|
||||
enum ieee80211_band band);
|
||||
u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo);
|
||||
|
||||
/* channel management */
|
||||
void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
|
||||
|
@ -1140,6 +1140,7 @@ static void ieee80211_iface_work(struct work_struct *work)
|
||||
struct sk_buff *skb;
|
||||
struct sta_info *sta;
|
||||
struct ieee80211_ra_tid *ra_tid;
|
||||
struct ieee80211_rx_agg *rx_agg;
|
||||
|
||||
if (!ieee80211_sdata_running(sdata))
|
||||
return;
|
||||
@ -1167,6 +1168,34 @@ static void ieee80211_iface_work(struct work_struct *work)
|
||||
ra_tid = (void *)&skb->cb;
|
||||
ieee80211_stop_tx_ba_cb(&sdata->vif, ra_tid->ra,
|
||||
ra_tid->tid);
|
||||
} else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_RX_AGG_START) {
|
||||
rx_agg = (void *)&skb->cb;
|
||||
mutex_lock(&local->sta_mtx);
|
||||
sta = sta_info_get_bss(sdata, rx_agg->addr);
|
||||
if (sta) {
|
||||
u16 last_seq;
|
||||
|
||||
last_seq = le16_to_cpu(
|
||||
sta->last_seq_ctrl[rx_agg->tid]);
|
||||
|
||||
__ieee80211_start_rx_ba_session(sta,
|
||||
0, 0,
|
||||
ieee80211_sn_inc(last_seq),
|
||||
1, rx_agg->tid,
|
||||
IEEE80211_MAX_AMPDU_BUF,
|
||||
false);
|
||||
}
|
||||
mutex_unlock(&local->sta_mtx);
|
||||
} else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_RX_AGG_STOP) {
|
||||
rx_agg = (void *)&skb->cb;
|
||||
mutex_lock(&local->sta_mtx);
|
||||
sta = sta_info_get_bss(sdata, rx_agg->addr);
|
||||
if (sta)
|
||||
__ieee80211_stop_rx_ba_session(sta,
|
||||
rx_agg->tid,
|
||||
WLAN_BACK_RECIPIENT, 0,
|
||||
false);
|
||||
mutex_unlock(&local->sta_mtx);
|
||||
} else if (ieee80211_is_action(mgmt->frame_control) &&
|
||||
mgmt->u.action.category == WLAN_CATEGORY_BACK) {
|
||||
int len = skb->len;
|
||||
@ -1672,8 +1701,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
|
||||
ieee80211_dfs_cac_timer_work);
|
||||
INIT_DELAYED_WORK(&sdata->dec_tailroom_needed_wk,
|
||||
ieee80211_delayed_tailroom_dec);
|
||||
INIT_DELAYED_WORK(&sdata->tdls_peer_del_work,
|
||||
ieee80211_tdls_peer_del_work);
|
||||
|
||||
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
|
||||
struct ieee80211_supported_band *sband;
|
||||
|
@ -482,9 +482,6 @@ int ieee80211_key_link(struct ieee80211_key *key,
|
||||
int idx, ret;
|
||||
bool pairwise;
|
||||
|
||||
if (WARN_ON(!sdata || !key))
|
||||
return -EINVAL;
|
||||
|
||||
pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE;
|
||||
idx = key->conf.keyidx;
|
||||
key->local = sdata->local;
|
||||
|
@ -830,16 +830,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
|
||||
qos_info = 0;
|
||||
}
|
||||
|
||||
pos = skb_put(skb, 9);
|
||||
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
|
||||
*pos++ = 7; /* len */
|
||||
*pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
|
||||
*pos++ = 0x50;
|
||||
*pos++ = 0xf2;
|
||||
*pos++ = 2; /* WME */
|
||||
*pos++ = 0; /* WME info */
|
||||
*pos++ = 1; /* WME ver */
|
||||
*pos++ = qos_info;
|
||||
pos = ieee80211_add_wmm_info_ie(skb_put(skb, 9), qos_info);
|
||||
}
|
||||
|
||||
/* add any remaining custom (i.e. vendor specific here) IEs */
|
||||
@ -1005,8 +996,6 @@ static void ieee80211_chswitch_work(struct work_struct *work)
|
||||
sdata->csa_block_tx = false;
|
||||
}
|
||||
|
||||
ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
|
||||
|
||||
ieee80211_sta_reset_beacon_monitor(sdata);
|
||||
ieee80211_sta_reset_conn_monitor(sdata);
|
||||
|
||||
@ -1064,7 +1053,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
return;
|
||||
|
||||
/* disregard subsequent announcements if we are already processing */
|
||||
if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)
|
||||
if (sdata->vif.csa_active)
|
||||
return;
|
||||
|
||||
current_band = cbss->channel->band;
|
||||
@ -1091,8 +1080,6 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
return;
|
||||
}
|
||||
|
||||
ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
|
||||
|
||||
mutex_lock(&local->mtx);
|
||||
mutex_lock(&local->chanctx_mtx);
|
||||
conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
|
||||
@ -2108,8 +2095,6 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
|
||||
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
|
||||
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
|
||||
true, frame_buf);
|
||||
ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
|
||||
|
||||
mutex_lock(&local->mtx);
|
||||
sdata->vif.csa_active = false;
|
||||
if (sdata->csa_block_tx) {
|
||||
@ -3722,6 +3707,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
|
||||
INIT_WORK(&ifmgd->csa_connection_drop_work,
|
||||
ieee80211_csa_connection_drop_work);
|
||||
INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_mgd_work);
|
||||
INIT_DELAYED_WORK(&ifmgd->tdls_peer_del_work,
|
||||
ieee80211_tdls_peer_del_work);
|
||||
setup_timer(&ifmgd->timer, ieee80211_sta_timer,
|
||||
(unsigned long) sdata);
|
||||
setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
|
||||
@ -4585,6 +4572,7 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata)
|
||||
cancel_work_sync(&ifmgd->request_smps_work);
|
||||
cancel_work_sync(&ifmgd->csa_connection_drop_work);
|
||||
cancel_work_sync(&ifmgd->chswitch_work);
|
||||
cancel_delayed_work_sync(&ifmgd->tdls_peer_del_work);
|
||||
|
||||
sdata_lock(sdata);
|
||||
if (ifmgd->assoc_data) {
|
||||
|
@ -688,20 +688,27 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
|
||||
int index,
|
||||
struct sk_buff_head *frames)
|
||||
{
|
||||
struct sk_buff *skb = tid_agg_rx->reorder_buf[index];
|
||||
struct sk_buff_head *skb_list = &tid_agg_rx->reorder_buf[index];
|
||||
struct sk_buff *skb;
|
||||
struct ieee80211_rx_status *status;
|
||||
|
||||
lockdep_assert_held(&tid_agg_rx->reorder_lock);
|
||||
|
||||
if (!skb)
|
||||
if (skb_queue_empty(skb_list))
|
||||
goto no_frame;
|
||||
|
||||
/* release the frame from the reorder ring buffer */
|
||||
if (!ieee80211_rx_reorder_ready(skb_list)) {
|
||||
__skb_queue_purge(skb_list);
|
||||
goto no_frame;
|
||||
}
|
||||
|
||||
/* release frames from the reorder ring buffer */
|
||||
tid_agg_rx->stored_mpdu_num--;
|
||||
tid_agg_rx->reorder_buf[index] = NULL;
|
||||
status = IEEE80211_SKB_RXCB(skb);
|
||||
status->rx_flags |= IEEE80211_RX_DEFERRED_RELEASE;
|
||||
__skb_queue_tail(frames, skb);
|
||||
while ((skb = __skb_dequeue(skb_list))) {
|
||||
status = IEEE80211_SKB_RXCB(skb);
|
||||
status->rx_flags |= IEEE80211_RX_DEFERRED_RELEASE;
|
||||
__skb_queue_tail(frames, skb);
|
||||
}
|
||||
|
||||
no_frame:
|
||||
tid_agg_rx->head_seq_num = ieee80211_sn_inc(tid_agg_rx->head_seq_num);
|
||||
@ -738,13 +745,13 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
|
||||
struct tid_ampdu_rx *tid_agg_rx,
|
||||
struct sk_buff_head *frames)
|
||||
{
|
||||
int index, j;
|
||||
int index, i, j;
|
||||
|
||||
lockdep_assert_held(&tid_agg_rx->reorder_lock);
|
||||
|
||||
/* release the buffer until next missing frame */
|
||||
index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;
|
||||
if (!tid_agg_rx->reorder_buf[index] &&
|
||||
if (!ieee80211_rx_reorder_ready(&tid_agg_rx->reorder_buf[index]) &&
|
||||
tid_agg_rx->stored_mpdu_num) {
|
||||
/*
|
||||
* No buffers ready to be released, but check whether any
|
||||
@ -753,7 +760,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
|
||||
int skipped = 1;
|
||||
for (j = (index + 1) % tid_agg_rx->buf_size; j != index;
|
||||
j = (j + 1) % tid_agg_rx->buf_size) {
|
||||
if (!tid_agg_rx->reorder_buf[j]) {
|
||||
if (!ieee80211_rx_reorder_ready(
|
||||
&tid_agg_rx->reorder_buf[j])) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
@ -762,6 +770,11 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
|
||||
HT_RX_REORDER_BUF_TIMEOUT))
|
||||
goto set_release_timer;
|
||||
|
||||
/* don't leave incomplete A-MSDUs around */
|
||||
for (i = (index + 1) % tid_agg_rx->buf_size; i != j;
|
||||
i = (i + 1) % tid_agg_rx->buf_size)
|
||||
__skb_queue_purge(&tid_agg_rx->reorder_buf[i]);
|
||||
|
||||
ht_dbg_ratelimited(sdata,
|
||||
"release an RX reorder frame due to timeout on earlier frames\n");
|
||||
ieee80211_release_reorder_frame(sdata, tid_agg_rx, j,
|
||||
@ -775,7 +788,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
|
||||
skipped) & IEEE80211_SN_MASK;
|
||||
skipped = 0;
|
||||
}
|
||||
} else while (tid_agg_rx->reorder_buf[index]) {
|
||||
} else while (ieee80211_rx_reorder_ready(
|
||||
&tid_agg_rx->reorder_buf[index])) {
|
||||
ieee80211_release_reorder_frame(sdata, tid_agg_rx, index,
|
||||
frames);
|
||||
index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;
|
||||
@ -786,7 +800,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
for (; j != (index - 1) % tid_agg_rx->buf_size;
|
||||
j = (j + 1) % tid_agg_rx->buf_size) {
|
||||
if (tid_agg_rx->reorder_buf[j])
|
||||
if (ieee80211_rx_reorder_ready(
|
||||
&tid_agg_rx->reorder_buf[j]))
|
||||
break;
|
||||
}
|
||||
|
||||
@ -811,6 +826,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
|
||||
struct sk_buff_head *frames)
|
||||
{
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
||||
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
|
||||
u16 sc = le16_to_cpu(hdr->seq_ctrl);
|
||||
u16 mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4;
|
||||
u16 head_seq_num, buf_size;
|
||||
@ -845,7 +861,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
|
||||
index = mpdu_seq_num % tid_agg_rx->buf_size;
|
||||
|
||||
/* check if we already stored this frame */
|
||||
if (tid_agg_rx->reorder_buf[index]) {
|
||||
if (ieee80211_rx_reorder_ready(&tid_agg_rx->reorder_buf[index])) {
|
||||
dev_kfree_skb(skb);
|
||||
goto out;
|
||||
}
|
||||
@ -858,17 +874,20 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
|
||||
*/
|
||||
if (mpdu_seq_num == tid_agg_rx->head_seq_num &&
|
||||
tid_agg_rx->stored_mpdu_num == 0) {
|
||||
tid_agg_rx->head_seq_num =
|
||||
ieee80211_sn_inc(tid_agg_rx->head_seq_num);
|
||||
if (!(status->flag & RX_FLAG_AMSDU_MORE))
|
||||
tid_agg_rx->head_seq_num =
|
||||
ieee80211_sn_inc(tid_agg_rx->head_seq_num);
|
||||
ret = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* put the frame in the reordering buffer */
|
||||
tid_agg_rx->reorder_buf[index] = skb;
|
||||
tid_agg_rx->reorder_time[index] = jiffies;
|
||||
tid_agg_rx->stored_mpdu_num++;
|
||||
ieee80211_sta_reorder_release(sdata, tid_agg_rx, frames);
|
||||
__skb_queue_tail(&tid_agg_rx->reorder_buf[index], skb);
|
||||
if (!(status->flag & RX_FLAG_AMSDU_MORE)) {
|
||||
tid_agg_rx->reorder_time[index] = jiffies;
|
||||
tid_agg_rx->stored_mpdu_num++;
|
||||
ieee80211_sta_reorder_release(sdata, tid_agg_rx, frames);
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock(&tid_agg_rx->reorder_lock);
|
||||
@ -3129,6 +3148,14 @@ static bool prepare_for_handlers(struct ieee80211_rx_data *rx,
|
||||
if (!ieee80211_is_beacon(hdr->frame_control))
|
||||
return false;
|
||||
status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
|
||||
} else if (!ieee80211_has_tods(hdr->frame_control)) {
|
||||
/* ignore data frames to TDLS-peers */
|
||||
if (ieee80211_is_data(hdr->frame_control))
|
||||
return false;
|
||||
/* ignore action frames to TDLS-peers */
|
||||
if (ieee80211_is_action(hdr->frame_control) &&
|
||||
!ether_addr_equal(bssid, hdr->addr1))
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case NL80211_IFTYPE_WDS:
|
||||
|
@ -47,6 +47,8 @@
|
||||
* @WLAN_STA_TDLS_PEER: Station is a TDLS peer.
|
||||
* @WLAN_STA_TDLS_PEER_AUTH: This TDLS peer is authorized to send direct
|
||||
* packets. This means the link is enabled.
|
||||
* @WLAN_STA_TDLS_INITIATOR: We are the initiator of the TDLS link with this
|
||||
* station.
|
||||
* @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was
|
||||
* keeping station in power-save mode, reply when the driver
|
||||
* unblocks the station.
|
||||
@ -76,6 +78,7 @@ enum ieee80211_sta_info_flags {
|
||||
WLAN_STA_PSPOLL,
|
||||
WLAN_STA_TDLS_PEER,
|
||||
WLAN_STA_TDLS_PEER_AUTH,
|
||||
WLAN_STA_TDLS_INITIATOR,
|
||||
WLAN_STA_UAPSD,
|
||||
WLAN_STA_SP,
|
||||
WLAN_STA_4ADDR_EVENT,
|
||||
@ -152,7 +155,8 @@ struct tid_ampdu_tx {
|
||||
/**
|
||||
* struct tid_ampdu_rx - TID aggregation information (Rx).
|
||||
*
|
||||
* @reorder_buf: buffer to reorder incoming aggregated MPDUs
|
||||
* @reorder_buf: buffer to reorder incoming aggregated MPDUs. An MPDU may be an
|
||||
* A-MSDU with individually reported subframes.
|
||||
* @reorder_time: jiffies when skb was added
|
||||
* @session_timer: check if peer keeps Tx-ing on the TID (by timeout value)
|
||||
* @reorder_timer: releases expired frames from the reorder buffer.
|
||||
@ -177,7 +181,7 @@ struct tid_ampdu_tx {
|
||||
struct tid_ampdu_rx {
|
||||
struct rcu_head rcu_head;
|
||||
spinlock_t reorder_lock;
|
||||
struct sk_buff **reorder_buf;
|
||||
struct sk_buff_head *reorder_buf;
|
||||
unsigned long *reorder_time;
|
||||
struct timer_list session_timer;
|
||||
struct timer_list reorder_timer;
|
||||
|
@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/ieee80211.h>
|
||||
#include <linux/log2.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include "ieee80211_i.h"
|
||||
#include "driver-ops.h"
|
||||
@ -21,14 +22,14 @@ void ieee80211_tdls_peer_del_work(struct work_struct *wk)
|
||||
struct ieee80211_local *local;
|
||||
|
||||
sdata = container_of(wk, struct ieee80211_sub_if_data,
|
||||
tdls_peer_del_work.work);
|
||||
u.mgd.tdls_peer_del_work.work);
|
||||
local = sdata->local;
|
||||
|
||||
mutex_lock(&local->mtx);
|
||||
if (!is_zero_ether_addr(sdata->tdls_peer)) {
|
||||
tdls_dbg(sdata, "TDLS del peer %pM\n", sdata->tdls_peer);
|
||||
sta_info_destroy_addr(sdata, sdata->tdls_peer);
|
||||
eth_zero_addr(sdata->tdls_peer);
|
||||
if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer)) {
|
||||
tdls_dbg(sdata, "TDLS del peer %pM\n", sdata->u.mgd.tdls_peer);
|
||||
sta_info_destroy_addr(sdata, sdata->u.mgd.tdls_peer);
|
||||
eth_zero_addr(sdata->u.mgd.tdls_peer);
|
||||
}
|
||||
mutex_unlock(&local->mtx);
|
||||
}
|
||||
@ -46,11 +47,16 @@ static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb)
|
||||
*pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
|
||||
}
|
||||
|
||||
static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
|
||||
static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata,
|
||||
u16 status_code)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
u16 capab;
|
||||
|
||||
/* The capability will be 0 when sending a failure code */
|
||||
if (status_code != 0)
|
||||
return 0;
|
||||
|
||||
capab = 0;
|
||||
if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ)
|
||||
return capab;
|
||||
@ -63,19 +69,332 @@ static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
|
||||
return capab;
|
||||
}
|
||||
|
||||
static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr,
|
||||
const u8 *peer, const u8 *bssid)
|
||||
static void ieee80211_tdls_add_link_ie(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, const u8 *peer,
|
||||
bool initiator)
|
||||
{
|
||||
struct ieee80211_tdls_lnkie *lnkid;
|
||||
const u8 *init_addr, *rsp_addr;
|
||||
|
||||
if (initiator) {
|
||||
init_addr = sdata->vif.addr;
|
||||
rsp_addr = peer;
|
||||
} else {
|
||||
init_addr = peer;
|
||||
rsp_addr = sdata->vif.addr;
|
||||
}
|
||||
|
||||
lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
|
||||
|
||||
lnkid->ie_type = WLAN_EID_LINK_ID;
|
||||
lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;
|
||||
|
||||
memcpy(lnkid->bssid, bssid, ETH_ALEN);
|
||||
memcpy(lnkid->init_sta, src_addr, ETH_ALEN);
|
||||
memcpy(lnkid->resp_sta, peer, ETH_ALEN);
|
||||
memcpy(lnkid->bssid, sdata->u.mgd.bssid, ETH_ALEN);
|
||||
memcpy(lnkid->init_sta, init_addr, ETH_ALEN);
|
||||
memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN);
|
||||
}
|
||||
|
||||
/* translate numbering in the WMM parameter IE to the mac80211 notation */
|
||||
static enum ieee80211_ac_numbers ieee80211_ac_from_wmm(int ac)
|
||||
{
|
||||
switch (ac) {
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
case 0:
|
||||
return IEEE80211_AC_BE;
|
||||
case 1:
|
||||
return IEEE80211_AC_BK;
|
||||
case 2:
|
||||
return IEEE80211_AC_VI;
|
||||
case 3:
|
||||
return IEEE80211_AC_VO;
|
||||
}
|
||||
}
|
||||
|
||||
static u8 ieee80211_wmm_aci_aifsn(int aifsn, bool acm, int aci)
|
||||
{
|
||||
u8 ret;
|
||||
|
||||
ret = aifsn & 0x0f;
|
||||
if (acm)
|
||||
ret |= 0x10;
|
||||
ret |= (aci << 5) & 0x60;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u8 ieee80211_wmm_ecw(u16 cw_min, u16 cw_max)
|
||||
{
|
||||
return ((ilog2(cw_min + 1) << 0x0) & 0x0f) |
|
||||
((ilog2(cw_max + 1) << 0x4) & 0xf0);
|
||||
}
|
||||
|
||||
static void ieee80211_tdls_add_wmm_param_ie(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_wmm_param_ie *wmm;
|
||||
struct ieee80211_tx_queue_params *txq;
|
||||
int i;
|
||||
|
||||
wmm = (void *)skb_put(skb, sizeof(*wmm));
|
||||
memset(wmm, 0, sizeof(*wmm));
|
||||
|
||||
wmm->element_id = WLAN_EID_VENDOR_SPECIFIC;
|
||||
wmm->len = sizeof(*wmm) - 2;
|
||||
|
||||
wmm->oui[0] = 0x00; /* Microsoft OUI 00:50:F2 */
|
||||
wmm->oui[1] = 0x50;
|
||||
wmm->oui[2] = 0xf2;
|
||||
wmm->oui_type = 2; /* WME */
|
||||
wmm->oui_subtype = 1; /* WME param */
|
||||
wmm->version = 1; /* WME ver */
|
||||
wmm->qos_info = 0; /* U-APSD not in use */
|
||||
|
||||
/*
|
||||
* Use the EDCA parameters defined for the BSS, or default if the AP
|
||||
* doesn't support it, as mandated by 802.11-2012 section 10.22.4
|
||||
*/
|
||||
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
|
||||
txq = &sdata->tx_conf[ieee80211_ac_from_wmm(i)];
|
||||
wmm->ac[i].aci_aifsn = ieee80211_wmm_aci_aifsn(txq->aifs,
|
||||
txq->acm, i);
|
||||
wmm->ac[i].cw = ieee80211_wmm_ecw(txq->cw_min, txq->cw_max);
|
||||
wmm->ac[i].txop_limit = cpu_to_le16(txq->txop);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, const u8 *peer,
|
||||
u8 action_code, bool initiator,
|
||||
const u8 *extra_ies, size_t extra_ies_len)
|
||||
{
|
||||
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_supported_band *sband;
|
||||
struct ieee80211_sta_ht_cap ht_cap;
|
||||
struct sta_info *sta = NULL;
|
||||
size_t offset = 0, noffset;
|
||||
u8 *pos;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
/* we should have the peer STA if we're already responding */
|
||||
if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
|
||||
sta = sta_info_get(sdata, peer);
|
||||
if (WARN_ON_ONCE(!sta)) {
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ieee80211_add_srates_ie(sdata, skb, false, band);
|
||||
ieee80211_add_ext_srates_ie(sdata, skb, false, band);
|
||||
|
||||
/* add any custom IEs that go before Extended Capabilities */
|
||||
if (extra_ies_len) {
|
||||
static const u8 before_ext_cap[] = {
|
||||
WLAN_EID_SUPP_RATES,
|
||||
WLAN_EID_COUNTRY,
|
||||
WLAN_EID_EXT_SUPP_RATES,
|
||||
WLAN_EID_SUPPORTED_CHANNELS,
|
||||
WLAN_EID_RSN,
|
||||
};
|
||||
noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
|
||||
before_ext_cap,
|
||||
ARRAY_SIZE(before_ext_cap),
|
||||
offset);
|
||||
pos = skb_put(skb, noffset - offset);
|
||||
memcpy(pos, extra_ies + offset, noffset - offset);
|
||||
offset = noffset;
|
||||
}
|
||||
|
||||
ieee80211_tdls_add_ext_capab(skb);
|
||||
|
||||
/* add the QoS element if we support it */
|
||||
if (local->hw.queues >= IEEE80211_NUM_ACS &&
|
||||
action_code != WLAN_PUB_ACTION_TDLS_DISCOVER_RES)
|
||||
ieee80211_add_wmm_info_ie(skb_put(skb, 9), 0); /* no U-APSD */
|
||||
|
||||
/* add any custom IEs that go before HT capabilities */
|
||||
if (extra_ies_len) {
|
||||
static const u8 before_ht_cap[] = {
|
||||
WLAN_EID_SUPP_RATES,
|
||||
WLAN_EID_COUNTRY,
|
||||
WLAN_EID_EXT_SUPP_RATES,
|
||||
WLAN_EID_SUPPORTED_CHANNELS,
|
||||
WLAN_EID_RSN,
|
||||
WLAN_EID_EXT_CAPABILITY,
|
||||
WLAN_EID_QOS_CAPA,
|
||||
WLAN_EID_FAST_BSS_TRANSITION,
|
||||
WLAN_EID_TIMEOUT_INTERVAL,
|
||||
WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
|
||||
};
|
||||
noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
|
||||
before_ht_cap,
|
||||
ARRAY_SIZE(before_ht_cap),
|
||||
offset);
|
||||
pos = skb_put(skb, noffset - offset);
|
||||
memcpy(pos, extra_ies + offset, noffset - offset);
|
||||
offset = noffset;
|
||||
}
|
||||
|
||||
/*
|
||||
* with TDLS we can switch channels, and HT-caps are not necessarily
|
||||
* the same on all bands. The specification limits the setup to a
|
||||
* single HT-cap, so use the current band for now.
|
||||
*/
|
||||
sband = local->hw.wiphy->bands[band];
|
||||
memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
|
||||
if ((action_code == WLAN_TDLS_SETUP_REQUEST ||
|
||||
action_code == WLAN_TDLS_SETUP_RESPONSE) &&
|
||||
ht_cap.ht_supported && (!sta || sta->sta.ht_cap.ht_supported)) {
|
||||
if (action_code == WLAN_TDLS_SETUP_REQUEST) {
|
||||
ieee80211_apply_htcap_overrides(sdata, &ht_cap);
|
||||
|
||||
/* disable SMPS in TDLS initiator */
|
||||
ht_cap.cap |= (WLAN_HT_CAP_SM_PS_DISABLED
|
||||
<< IEEE80211_HT_CAP_SM_PS_SHIFT);
|
||||
} else {
|
||||
/* disable SMPS in TDLS responder */
|
||||
sta->sta.ht_cap.cap |=
|
||||
(WLAN_HT_CAP_SM_PS_DISABLED
|
||||
<< IEEE80211_HT_CAP_SM_PS_SHIFT);
|
||||
|
||||
/* the peer caps are already intersected with our own */
|
||||
memcpy(&ht_cap, &sta->sta.ht_cap, sizeof(ht_cap));
|
||||
}
|
||||
|
||||
pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
|
||||
ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
/* add any remaining IEs */
|
||||
if (extra_ies_len) {
|
||||
noffset = extra_ies_len;
|
||||
pos = skb_put(skb, noffset - offset);
|
||||
memcpy(pos, extra_ies + offset, noffset - offset);
|
||||
}
|
||||
|
||||
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
|
||||
}
|
||||
|
||||
static void
|
||||
ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, const u8 *peer,
|
||||
bool initiator, const u8 *extra_ies,
|
||||
size_t extra_ies_len)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
size_t offset = 0, noffset;
|
||||
struct sta_info *sta, *ap_sta;
|
||||
u8 *pos;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
sta = sta_info_get(sdata, peer);
|
||||
ap_sta = sta_info_get(sdata, ifmgd->bssid);
|
||||
if (WARN_ON_ONCE(!sta || !ap_sta)) {
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
/* add any custom IEs that go before the QoS IE */
|
||||
if (extra_ies_len) {
|
||||
static const u8 before_qos[] = {
|
||||
WLAN_EID_RSN,
|
||||
};
|
||||
noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
|
||||
before_qos,
|
||||
ARRAY_SIZE(before_qos),
|
||||
offset);
|
||||
pos = skb_put(skb, noffset - offset);
|
||||
memcpy(pos, extra_ies + offset, noffset - offset);
|
||||
offset = noffset;
|
||||
}
|
||||
|
||||
/* add the QoS param IE if both the peer and we support it */
|
||||
if (local->hw.queues >= IEEE80211_NUM_ACS &&
|
||||
test_sta_flag(sta, WLAN_STA_WME))
|
||||
ieee80211_tdls_add_wmm_param_ie(sdata, skb);
|
||||
|
||||
/* add any custom IEs that go before HT operation */
|
||||
if (extra_ies_len) {
|
||||
static const u8 before_ht_op[] = {
|
||||
WLAN_EID_RSN,
|
||||
WLAN_EID_QOS_CAPA,
|
||||
WLAN_EID_FAST_BSS_TRANSITION,
|
||||
WLAN_EID_TIMEOUT_INTERVAL,
|
||||
};
|
||||
noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
|
||||
before_ht_op,
|
||||
ARRAY_SIZE(before_ht_op),
|
||||
offset);
|
||||
pos = skb_put(skb, noffset - offset);
|
||||
memcpy(pos, extra_ies + offset, noffset - offset);
|
||||
offset = noffset;
|
||||
}
|
||||
|
||||
/* if HT support is only added in TDLS, we need an HT-operation IE */
|
||||
if (!ap_sta->sta.ht_cap.ht_supported && sta->sta.ht_cap.ht_supported) {
|
||||
struct ieee80211_chanctx_conf *chanctx_conf =
|
||||
rcu_dereference(sdata->vif.chanctx_conf);
|
||||
if (!WARN_ON(!chanctx_conf)) {
|
||||
pos = skb_put(skb, 2 +
|
||||
sizeof(struct ieee80211_ht_operation));
|
||||
/* send an empty HT operation IE */
|
||||
ieee80211_ie_build_ht_oper(pos, &sta->sta.ht_cap,
|
||||
&chanctx_conf->def, 0);
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
/* add any remaining IEs */
|
||||
if (extra_ies_len) {
|
||||
noffset = extra_ies_len;
|
||||
pos = skb_put(skb, noffset - offset);
|
||||
memcpy(pos, extra_ies + offset, noffset - offset);
|
||||
}
|
||||
|
||||
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
|
||||
}
|
||||
|
||||
static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, const u8 *peer,
|
||||
u8 action_code, u16 status_code,
|
||||
bool initiator, const u8 *extra_ies,
|
||||
size_t extra_ies_len)
|
||||
{
|
||||
switch (action_code) {
|
||||
case WLAN_TDLS_SETUP_REQUEST:
|
||||
case WLAN_TDLS_SETUP_RESPONSE:
|
||||
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
|
||||
if (status_code == 0)
|
||||
ieee80211_tdls_add_setup_start_ies(sdata, skb, peer,
|
||||
action_code,
|
||||
initiator,
|
||||
extra_ies,
|
||||
extra_ies_len);
|
||||
break;
|
||||
case WLAN_TDLS_SETUP_CONFIRM:
|
||||
if (status_code == 0)
|
||||
ieee80211_tdls_add_setup_cfm_ies(sdata, skb, peer,
|
||||
initiator, extra_ies,
|
||||
extra_ies_len);
|
||||
break;
|
||||
case WLAN_TDLS_TEARDOWN:
|
||||
case WLAN_TDLS_DISCOVERY_REQUEST:
|
||||
if (extra_ies_len)
|
||||
memcpy(skb_put(skb, extra_ies_len), extra_ies,
|
||||
extra_ies_len);
|
||||
if (status_code == 0 || action_code == WLAN_TDLS_TEARDOWN)
|
||||
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int
|
||||
@ -84,7 +403,6 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
|
||||
u16 status_code, struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
|
||||
struct ieee80211_tdls_data *tf;
|
||||
|
||||
tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u));
|
||||
@ -102,11 +420,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
|
||||
skb_put(skb, sizeof(tf->u.setup_req));
|
||||
tf->u.setup_req.dialog_token = dialog_token;
|
||||
tf->u.setup_req.capability =
|
||||
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
|
||||
|
||||
ieee80211_add_srates_ie(sdata, skb, false, band);
|
||||
ieee80211_add_ext_srates_ie(sdata, skb, false, band);
|
||||
ieee80211_tdls_add_ext_capab(skb);
|
||||
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata,
|
||||
status_code));
|
||||
break;
|
||||
case WLAN_TDLS_SETUP_RESPONSE:
|
||||
tf->category = WLAN_CATEGORY_TDLS;
|
||||
@ -116,11 +431,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
|
||||
tf->u.setup_resp.status_code = cpu_to_le16(status_code);
|
||||
tf->u.setup_resp.dialog_token = dialog_token;
|
||||
tf->u.setup_resp.capability =
|
||||
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
|
||||
|
||||
ieee80211_add_srates_ie(sdata, skb, false, band);
|
||||
ieee80211_add_ext_srates_ie(sdata, skb, false, band);
|
||||
ieee80211_tdls_add_ext_capab(skb);
|
||||
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata,
|
||||
status_code));
|
||||
break;
|
||||
case WLAN_TDLS_SETUP_CONFIRM:
|
||||
tf->category = WLAN_CATEGORY_TDLS;
|
||||
@ -157,7 +469,6 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
|
||||
u16 status_code, struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
|
||||
mgmt = (void *)skb_put(skb, 24);
|
||||
@ -178,11 +489,8 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
|
||||
mgmt->u.action.u.tdls_discover_resp.dialog_token =
|
||||
dialog_token;
|
||||
mgmt->u.action.u.tdls_discover_resp.capability =
|
||||
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
|
||||
|
||||
ieee80211_add_srates_ie(sdata, skb, false, band);
|
||||
ieee80211_add_ext_srates_ie(sdata, skb, false, band);
|
||||
ieee80211_tdls_add_ext_capab(skb);
|
||||
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata,
|
||||
status_code));
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -202,7 +510,7 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct sk_buff *skb = NULL;
|
||||
bool send_direct;
|
||||
const u8 *init_addr, *rsp_addr;
|
||||
struct sta_info *sta;
|
||||
int ret;
|
||||
|
||||
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
|
||||
@ -210,6 +518,9 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
|
||||
sizeof(struct ieee80211_tdls_data)) +
|
||||
50 + /* supported rates */
|
||||
7 + /* ext capab */
|
||||
26 + /* max(WMM-info, WMM-param) */
|
||||
2 + max(sizeof(struct ieee80211_ht_cap),
|
||||
sizeof(struct ieee80211_ht_operation)) +
|
||||
extra_ies_len +
|
||||
sizeof(struct ieee80211_tdls_lnkie));
|
||||
if (!skb)
|
||||
@ -242,45 +553,48 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
if (extra_ies_len)
|
||||
memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
|
||||
rcu_read_lock();
|
||||
sta = sta_info_get(sdata, peer);
|
||||
|
||||
/* sanity check for initiator */
|
||||
/* infer the initiator if we can, to support old userspace */
|
||||
switch (action_code) {
|
||||
case WLAN_TDLS_SETUP_REQUEST:
|
||||
if (sta)
|
||||
set_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
|
||||
/* fall-through */
|
||||
case WLAN_TDLS_SETUP_CONFIRM:
|
||||
case WLAN_TDLS_DISCOVERY_REQUEST:
|
||||
if (!initiator) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
initiator = true;
|
||||
break;
|
||||
case WLAN_TDLS_SETUP_RESPONSE:
|
||||
/*
|
||||
* In some testing scenarios, we send a request and response.
|
||||
* Make the last packet sent take effect for the initiator
|
||||
* value.
|
||||
*/
|
||||
if (sta)
|
||||
clear_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
|
||||
/* fall-through */
|
||||
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
|
||||
if (initiator) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
initiator = false;
|
||||
break;
|
||||
case WLAN_TDLS_TEARDOWN:
|
||||
/* any value is ok */
|
||||
break;
|
||||
default:
|
||||
ret = -ENOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sta && test_sta_flag(sta, WLAN_STA_TDLS_INITIATOR))
|
||||
initiator = true;
|
||||
|
||||
rcu_read_unlock();
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (initiator) {
|
||||
init_addr = sdata->vif.addr;
|
||||
rsp_addr = peer;
|
||||
} else {
|
||||
init_addr = peer;
|
||||
rsp_addr = sdata->vif.addr;
|
||||
}
|
||||
|
||||
ieee80211_tdls_add_link_ie(skb, init_addr, rsp_addr,
|
||||
sdata->u.mgd.bssid);
|
||||
|
||||
ieee80211_tdls_add_ies(sdata, skb, peer, action_code, status_code,
|
||||
initiator, extra_ies, extra_ies_len);
|
||||
if (send_direct) {
|
||||
ieee80211_tx_skb(sdata, skb);
|
||||
return 0;
|
||||
@ -327,8 +641,8 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
|
||||
mutex_lock(&local->mtx);
|
||||
|
||||
/* we don't support concurrent TDLS peer setups */
|
||||
if (!is_zero_ether_addr(sdata->tdls_peer) &&
|
||||
!ether_addr_equal(sdata->tdls_peer, peer)) {
|
||||
if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer) &&
|
||||
!ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) {
|
||||
ret = -EBUSY;
|
||||
goto exit;
|
||||
}
|
||||
@ -336,15 +650,19 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
|
||||
/*
|
||||
* make sure we have a STA representing the peer so we drop or buffer
|
||||
* non-TDLS-setup frames to the peer. We can't send other packets
|
||||
* during setup through the AP path
|
||||
* during setup through the AP path.
|
||||
* Allow error packets to be sent - sometimes we don't even add a STA
|
||||
* before failing the setup.
|
||||
*/
|
||||
rcu_read_lock();
|
||||
if (!sta_info_get(sdata, peer)) {
|
||||
if (status_code == 0) {
|
||||
rcu_read_lock();
|
||||
if (!sta_info_get(sdata, peer)) {
|
||||
rcu_read_unlock();
|
||||
ret = -ENOLINK;
|
||||
goto exit;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
ret = -ENOLINK;
|
||||
goto exit;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
ieee80211_flush_queues(local, sdata);
|
||||
|
||||
@ -355,9 +673,9 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
memcpy(sdata->tdls_peer, peer, ETH_ALEN);
|
||||
memcpy(sdata->u.mgd.tdls_peer, peer, ETH_ALEN);
|
||||
ieee80211_queue_delayed_work(&sdata->local->hw,
|
||||
&sdata->tdls_peer_del_work,
|
||||
&sdata->u.mgd.tdls_peer_del_work,
|
||||
TDLS_PEER_SETUP_TIMEOUT);
|
||||
|
||||
exit:
|
||||
@ -513,11 +831,22 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
|
||||
set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
|
||||
rcu_read_unlock();
|
||||
|
||||
WARN_ON_ONCE(is_zero_ether_addr(sdata->tdls_peer) ||
|
||||
!ether_addr_equal(sdata->tdls_peer, peer));
|
||||
WARN_ON_ONCE(is_zero_ether_addr(sdata->u.mgd.tdls_peer) ||
|
||||
!ether_addr_equal(sdata->u.mgd.tdls_peer, peer));
|
||||
ret = 0;
|
||||
break;
|
||||
case NL80211_TDLS_DISABLE_LINK:
|
||||
/*
|
||||
* The teardown message in ieee80211_tdls_mgmt_teardown() was
|
||||
* created while the queues were stopped, so it might still be
|
||||
* pending. Before flushing the queues we need to be sure the
|
||||
* message is handled by the tasklet handling pending messages,
|
||||
* otherwise we might start destroying the station before
|
||||
* sending the teardown packet.
|
||||
* Note that this only forces the tasklet to flush pendings -
|
||||
* not to stop the tasklet from rescheduling itself.
|
||||
*/
|
||||
tasklet_kill(&local->tx_pending_tasklet);
|
||||
/* flush a potentially queued teardown packet */
|
||||
ieee80211_flush_queues(local, sdata);
|
||||
|
||||
@ -528,9 +857,9 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret == 0 && ether_addr_equal(sdata->tdls_peer, peer)) {
|
||||
cancel_delayed_work(&sdata->tdls_peer_del_work);
|
||||
eth_zero_addr(sdata->tdls_peer);
|
||||
if (ret == 0 && ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) {
|
||||
cancel_delayed_work(&sdata->u.mgd.tdls_peer_del_work);
|
||||
eth_zero_addr(sdata->u.mgd.tdls_peer);
|
||||
}
|
||||
|
||||
mutex_unlock(&local->mtx);
|
||||
|
@ -3083,3 +3083,18 @@ int ieee80211_max_num_channels(struct ieee80211_local *local)
|
||||
|
||||
return max_num_different_channels;
|
||||
}
|
||||
|
||||
u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo)
|
||||
{
|
||||
*buf++ = WLAN_EID_VENDOR_SPECIFIC;
|
||||
*buf++ = 7; /* len */
|
||||
*buf++ = 0x00; /* Microsoft OUI 00:50:F2 */
|
||||
*buf++ = 0x50;
|
||||
*buf++ = 0xf2;
|
||||
*buf++ = 2; /* WME */
|
||||
*buf++ = 0; /* WME info */
|
||||
*buf++ = 1; /* WME ver */
|
||||
*buf++ = qosinfo; /* U-APSD no in use */
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
@ -129,6 +129,10 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
|
||||
if (!vht_cap_ie || !sband->vht_cap.vht_supported)
|
||||
return;
|
||||
|
||||
/* don't support VHT for TDLS peers for now */
|
||||
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
|
||||
return;
|
||||
|
||||
/*
|
||||
* A VHT STA must support 40 MHz, but if we verify that here
|
||||
* then we break a few things - some APs (e.g. Netgear R6300v2
|
||||
|
@ -811,7 +811,7 @@ ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx)
|
||||
ieee80211_rx_result
|
||||
ieee80211_crypto_hw_decrypt(struct ieee80211_rx_data *rx)
|
||||
{
|
||||
if (rx->sta->cipher_scheme)
|
||||
if (rx->sta && rx->sta->cipher_scheme)
|
||||
return ieee80211_crypto_cs_decrypt(rx);
|
||||
|
||||
return RX_DROP_UNUSABLE;
|
||||
|
@ -162,6 +162,12 @@ config CFG80211_INTERNAL_REGDB
|
||||
and includes code to query that database. This is an alternative
|
||||
to using CRDA for defining regulatory rules for the kernel.
|
||||
|
||||
Using this option requires some parsing of the db.txt at build time,
|
||||
the parser will be upkept with the latest wireless-regdb updates but
|
||||
older wireless-regdb formats will be ignored. The parser may later
|
||||
be replaced to avoid issues with conflicts on versions of
|
||||
wireless-regdb.
|
||||
|
||||
For details see:
|
||||
|
||||
http://wireless.kernel.org/en/developers/Regulatory
|
||||
|
@ -51,32 +51,41 @@ function parse_country_head() {
|
||||
|
||||
function parse_reg_rule()
|
||||
{
|
||||
flag_starts_at = 7
|
||||
|
||||
start = $1
|
||||
sub(/\(/, "", start)
|
||||
end = $3
|
||||
bw = $5
|
||||
sub(/\),/, "", bw)
|
||||
gain = $6
|
||||
sub(/\(/, "", gain)
|
||||
sub(/,/, "", gain)
|
||||
power = $7
|
||||
sub(/\)/, "", power)
|
||||
sub(/,/, "", power)
|
||||
gain = 0
|
||||
power = $6
|
||||
# power might be in mW...
|
||||
units = $8
|
||||
units = $7
|
||||
dfs_cac = 0
|
||||
|
||||
sub(/\(/, "", power)
|
||||
sub(/\),/, "", power)
|
||||
sub(/\),/, "", units)
|
||||
sub(/\)/, "", units)
|
||||
sub(/,/, "", units)
|
||||
dfs_cac = $9
|
||||
|
||||
if (units == "mW") {
|
||||
flag_starts_at = 8
|
||||
power = 10 * log(power)/log(10)
|
||||
if ($8 ~ /[[:digit:]]/) {
|
||||
flag_starts_at = 9
|
||||
dfs_cac = $8
|
||||
}
|
||||
} else {
|
||||
dfs_cac = $8
|
||||
if ($7 ~ /[[:digit:]]/) {
|
||||
flag_starts_at = 8
|
||||
dfs_cac = $7
|
||||
}
|
||||
}
|
||||
sub(/,/, "", dfs_cac)
|
||||
sub(/\(/, "", dfs_cac)
|
||||
sub(/\)/, "", dfs_cac)
|
||||
sub(/\),/, "", dfs_cac)
|
||||
flagstr = ""
|
||||
for (i=8; i<=NF; i++)
|
||||
for (i=flag_starts_at; i<=NF; i++)
|
||||
flagstr = flagstr $i
|
||||
split(flagstr, flagarray, ",")
|
||||
flags = ""
|
||||
|
@ -3814,7 +3814,8 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
|
||||
{
|
||||
if (params->listen_interval != -1)
|
||||
return -EINVAL;
|
||||
if (params->aid)
|
||||
if (params->aid &&
|
||||
!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
|
||||
return -EINVAL;
|
||||
|
||||
/* When you run into this, adjust the code below for the new flag */
|
||||
|
Loading…
x
Reference in New Issue
Block a user