Commit 9f985b0e authored by Ron Rindjunsky's avatar Ron Rindjunsky Committed by David S. Miller

mac80211: adding 802.11n essential A-MPDU addBA capability

This patch adds the capability to identify and answer an add block ACK
request.
As this series of patches only adds HT handling with no aggregations,
(A-MPDU aggregations acceptance is not obligatory according to 802.11n
draft) we are currently sending back a refusal upon this request.
Signed-off-by: default avatarRon Rindjunsky <ron.rindjunsky@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c7153508
...@@ -57,6 +57,13 @@ ...@@ -57,6 +57,13 @@
#define ERP_INFO_USE_PROTECTION BIT(1) #define ERP_INFO_USE_PROTECTION BIT(1)
/* mgmt header + 1 byte action code */
#define IEEE80211_MIN_ACTION_SIZE (24 + 1)
#define IEEE80211_ADDBA_PARAM_POLICY_MASK 0x0002
#define IEEE80211_ADDBA_PARAM_TID_MASK 0x003C
#define IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK 0xFFA0
static void ieee80211_send_probe_req(struct net_device *dev, u8 *dst, static void ieee80211_send_probe_req(struct net_device *dev, u8 *dst,
u8 *ssid, size_t ssid_len); u8 *ssid, size_t ssid_len);
static struct ieee80211_sta_bss * static struct ieee80211_sta_bss *
...@@ -987,6 +994,91 @@ static void ieee80211_auth_challenge(struct net_device *dev, ...@@ -987,6 +994,91 @@ static void ieee80211_auth_challenge(struct net_device *dev,
elems.challenge_len + 2, 1); elems.challenge_len + 2, 1);
} }
static void ieee80211_send_addba_resp(struct net_device *dev, u8 *da, u16 tid,
u8 dialog_token, u16 status, u16 policy,
u16 buf_size, u16 timeout)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_if_sta *ifsta = &sdata->u.sta;
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
u16 capab;
skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer "
"for addba resp frame\n", dev->name);
return;
}
skb_reserve(skb, local->hw.extra_tx_headroom);
mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
memset(mgmt, 0, 24);
memcpy(mgmt->da, da, ETH_ALEN);
memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
if (sdata->type == IEEE80211_IF_TYPE_AP)
memcpy(mgmt->bssid, dev->dev_addr, ETH_ALEN);
else
memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
IEEE80211_STYPE_ACTION);
skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_resp));
mgmt->u.action.category = WLAN_CATEGORY_BACK;
mgmt->u.action.u.addba_resp.action_code = WLAN_ACTION_ADDBA_RESP;
mgmt->u.action.u.addba_resp.dialog_token = dialog_token;
capab = (u16)(policy << 1); /* bit 1 aggregation policy */
capab |= (u16)(tid << 2); /* bit 5:2 TID number */
capab |= (u16)(buf_size << 6); /* bit 15:6 max size of aggregation */
mgmt->u.action.u.addba_resp.capab = cpu_to_le16(capab);
mgmt->u.action.u.addba_resp.timeout = cpu_to_le16(timeout);
mgmt->u.action.u.addba_resp.status = cpu_to_le16(status);
ieee80211_sta_tx(dev, skb, 0);
return;
}
static void ieee80211_sta_process_addba_request(struct net_device *dev,
struct ieee80211_mgmt *mgmt,
size_t len)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct sta_info *sta;
u16 capab, tid, timeout, ba_policy, buf_size, status;
u8 dialog_token;
sta = sta_info_get(local, mgmt->sa);
if (!sta)
return;
/* 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);
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;
/* TODO - currently aggregation is declined (A-MPDU add BA request
* acceptance is not obligatory by 802.11n draft), but here is
* the entry point for dealing with it */
#ifdef MAC80211_HT_DEBUG
if (net_ratelimit())
printk(KERN_DEBUG "Add Block Ack request arrived,"
" currently denying it\n");
#endif /* MAC80211_HT_DEBUG */
status = WLAN_STATUS_REQUEST_DECLINED;
ieee80211_send_addba_resp(sta->dev, sta->addr, tid, dialog_token,
status, 1, buf_size, timeout);
sta_info_put(sta);
}
static void ieee80211_rx_mgmt_auth(struct net_device *dev, static void ieee80211_rx_mgmt_auth(struct net_device *dev,
struct ieee80211_if_sta *ifsta, struct ieee80211_if_sta *ifsta,
...@@ -1870,6 +1962,34 @@ static void ieee80211_rx_mgmt_probe_req(struct net_device *dev, ...@@ -1870,6 +1962,34 @@ static void ieee80211_rx_mgmt_probe_req(struct net_device *dev,
ieee80211_sta_tx(dev, skb, 0); ieee80211_sta_tx(dev, skb, 0);
} }
void ieee80211_rx_mgmt_action(struct net_device *dev,
struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
size_t len)
{
if (len < IEEE80211_MIN_ACTION_SIZE)
return;
switch (mgmt->u.action.category) {
case WLAN_CATEGORY_BACK:
switch (mgmt->u.action.u.addba_req.action_code) {
case WLAN_ACTION_ADDBA_REQ:
if (len < (IEEE80211_MIN_ACTION_SIZE +
sizeof(mgmt->u.action.u.addba_req)))
break;
ieee80211_sta_process_addba_request(dev, mgmt, len);
break;
default:
if (net_ratelimit())
printk(KERN_DEBUG "%s: received unsupported BACK\n",
dev->name);
break;
}
break;
default:
break;
}
}
void ieee80211_sta_rx_mgmt(struct net_device *dev, struct sk_buff *skb, void ieee80211_sta_rx_mgmt(struct net_device *dev, struct sk_buff *skb,
struct ieee80211_rx_status *rx_status) struct ieee80211_rx_status *rx_status)
...@@ -1899,6 +2019,7 @@ void ieee80211_sta_rx_mgmt(struct net_device *dev, struct sk_buff *skb, ...@@ -1899,6 +2019,7 @@ void ieee80211_sta_rx_mgmt(struct net_device *dev, struct sk_buff *skb,
case IEEE80211_STYPE_REASSOC_RESP: case IEEE80211_STYPE_REASSOC_RESP:
case IEEE80211_STYPE_DEAUTH: case IEEE80211_STYPE_DEAUTH:
case IEEE80211_STYPE_DISASSOC: case IEEE80211_STYPE_DISASSOC:
case IEEE80211_STYPE_ACTION:
skb_queue_tail(&ifsta->skb_queue, skb); skb_queue_tail(&ifsta->skb_queue, skb);
queue_work(local->hw.workqueue, &ifsta->work); queue_work(local->hw.workqueue, &ifsta->work);
return; return;
...@@ -1956,6 +2077,9 @@ static void ieee80211_sta_rx_queued_mgmt(struct net_device *dev, ...@@ -1956,6 +2077,9 @@ static void ieee80211_sta_rx_queued_mgmt(struct net_device *dev,
case IEEE80211_STYPE_DISASSOC: case IEEE80211_STYPE_DISASSOC:
ieee80211_rx_mgmt_disassoc(dev, ifsta, mgmt, skb->len); ieee80211_rx_mgmt_disassoc(dev, ifsta, mgmt, skb->len);
break; break;
case IEEE80211_STYPE_ACTION:
ieee80211_rx_mgmt_action(dev, ifsta, mgmt, skb->len);
break;
} }
kfree_skb(skb); kfree_skb(skb);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment