Merge git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next

This commit is contained in:
John W. Linville 2014-07-22 13:49:34 -04:00
commit a006827a15
19 changed files with 709 additions and 171 deletions

View File

@ -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;

View File

@ -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 */
/**

View File

@ -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);

View File

@ -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,

View File

@ -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));

View File

@ -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))

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -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:

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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 = ""

View File

@ -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 */