diff options
author | Ron Rindjunsky <ron.rindjunsky@intel.com> | 2007-11-26 09:14:33 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-01-28 17:55:32 -0500 |
commit | fd4c7f2fce1737105208c564e1458c885918982d (patch) | |
tree | 3cb7781bb09cc3fc2619fe2bc535bb221e1d4863 /net/mac80211/rx.c | |
parent | 9f985b0eee4070e494b9d62313da982cfef9b5e3 (diff) |
mac80211: adding 802.11n essential A-MSDU Rx capability
This patch adds the ability to receive and handle A-MSDU frames.
Signed-off-by: Ron Rindjunsky <ron.rindjunsky@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/mac80211/rx.c')
-rw-r--r-- | net/mac80211/rx.c | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 478d85563bdb..4525d7332c88 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c | |||
@@ -243,6 +243,10 @@ ieee80211_rx_h_parse_qos(struct ieee80211_txrx_data *rx) | |||
243 | u8 *qc = data + ieee80211_get_hdrlen(rx->fc) - QOS_CONTROL_LEN; | 243 | u8 *qc = data + ieee80211_get_hdrlen(rx->fc) - QOS_CONTROL_LEN; |
244 | /* frame has qos control */ | 244 | /* frame has qos control */ |
245 | tid = qc[0] & QOS_CONTROL_TID_MASK; | 245 | tid = qc[0] & QOS_CONTROL_TID_MASK; |
246 | if (qc[0] & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT) | ||
247 | rx->u.rx.amsdu_frame = 1; | ||
248 | else | ||
249 | rx->u.rx.amsdu_frame = 0; | ||
246 | } else { | 250 | } else { |
247 | if (unlikely((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)) { | 251 | if (unlikely((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)) { |
248 | /* Separate TID for management frames */ | 252 | /* Separate TID for management frames */ |
@@ -1197,6 +1201,125 @@ ieee80211_deliver_skb(struct ieee80211_txrx_data *rx) | |||
1197 | } | 1201 | } |
1198 | 1202 | ||
1199 | static ieee80211_txrx_result | 1203 | static ieee80211_txrx_result |
1204 | ieee80211_rx_h_amsdu(struct ieee80211_txrx_data *rx) | ||
1205 | { | ||
1206 | struct net_device *dev = rx->dev; | ||
1207 | struct ieee80211_local *local = rx->local; | ||
1208 | u16 fc, ethertype; | ||
1209 | u8 *payload; | ||
1210 | struct sk_buff *skb = rx->skb, *frame = NULL; | ||
1211 | const struct ethhdr *eth; | ||
1212 | int remaining, err; | ||
1213 | u8 dst[ETH_ALEN]; | ||
1214 | u8 src[ETH_ALEN]; | ||
1215 | DECLARE_MAC_BUF(mac); | ||
1216 | |||
1217 | fc = rx->fc; | ||
1218 | if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)) | ||
1219 | return TXRX_CONTINUE; | ||
1220 | |||
1221 | if (unlikely(!WLAN_FC_DATA_PRESENT(fc))) | ||
1222 | return TXRX_DROP; | ||
1223 | |||
1224 | if (!rx->u.rx.amsdu_frame) | ||
1225 | return TXRX_CONTINUE; | ||
1226 | |||
1227 | err = ieee80211_data_to_8023(rx); | ||
1228 | if (unlikely(err)) | ||
1229 | return TXRX_DROP; | ||
1230 | |||
1231 | skb->dev = dev; | ||
1232 | |||
1233 | dev->stats.rx_packets++; | ||
1234 | dev->stats.rx_bytes += skb->len; | ||
1235 | |||
1236 | /* skip the wrapping header */ | ||
1237 | eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr)); | ||
1238 | if (!eth) | ||
1239 | return TXRX_DROP; | ||
1240 | |||
1241 | while (skb != frame) { | ||
1242 | u8 padding; | ||
1243 | __be16 len = eth->h_proto; | ||
1244 | unsigned int subframe_len = sizeof(struct ethhdr) + ntohs(len); | ||
1245 | |||
1246 | remaining = skb->len; | ||
1247 | memcpy(dst, eth->h_dest, ETH_ALEN); | ||
1248 | memcpy(src, eth->h_source, ETH_ALEN); | ||
1249 | |||
1250 | padding = ((4 - subframe_len) & 0x3); | ||
1251 | /* the last MSDU has no padding */ | ||
1252 | if (subframe_len > remaining) { | ||
1253 | printk(KERN_DEBUG "%s: wrong buffer size", dev->name); | ||
1254 | return TXRX_DROP; | ||
1255 | } | ||
1256 | |||
1257 | skb_pull(skb, sizeof(struct ethhdr)); | ||
1258 | /* if last subframe reuse skb */ | ||
1259 | if (remaining <= subframe_len + padding) | ||
1260 | frame = skb; | ||
1261 | else { | ||
1262 | frame = dev_alloc_skb(local->hw.extra_tx_headroom + | ||
1263 | subframe_len); | ||
1264 | |||
1265 | if (frame == NULL) | ||
1266 | return TXRX_DROP; | ||
1267 | |||
1268 | skb_reserve(frame, local->hw.extra_tx_headroom + | ||
1269 | sizeof(struct ethhdr)); | ||
1270 | memcpy(skb_put(frame, ntohs(len)), skb->data, | ||
1271 | ntohs(len)); | ||
1272 | |||
1273 | eth = (struct ethhdr *) skb_pull(skb, ntohs(len) + | ||
1274 | padding); | ||
1275 | if (!eth) { | ||
1276 | printk(KERN_DEBUG "%s: wrong buffer size ", | ||
1277 | dev->name); | ||
1278 | dev_kfree_skb(frame); | ||
1279 | return TXRX_DROP; | ||
1280 | } | ||
1281 | } | ||
1282 | |||
1283 | skb_set_network_header(frame, 0); | ||
1284 | frame->dev = dev; | ||
1285 | frame->priority = skb->priority; | ||
1286 | rx->skb = frame; | ||
1287 | |||
1288 | if ((ieee80211_drop_802_1x_pae(rx, 0)) || | ||
1289 | (ieee80211_drop_unencrypted(rx, 0))) { | ||
1290 | if (skb == frame) /* last frame */ | ||
1291 | return TXRX_DROP; | ||
1292 | dev_kfree_skb(frame); | ||
1293 | continue; | ||
1294 | } | ||
1295 | |||
1296 | payload = frame->data; | ||
1297 | ethertype = (payload[6] << 8) | payload[7]; | ||
1298 | |||
1299 | if (likely((compare_ether_addr(payload, rfc1042_header) == 0 && | ||
1300 | ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || | ||
1301 | compare_ether_addr(payload, | ||
1302 | bridge_tunnel_header) == 0)) { | ||
1303 | /* remove RFC1042 or Bridge-Tunnel | ||
1304 | * encapsulation and replace EtherType */ | ||
1305 | skb_pull(frame, 6); | ||
1306 | memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN); | ||
1307 | memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN); | ||
1308 | } else { | ||
1309 | memcpy(skb_push(frame, sizeof(__be16)), &len, | ||
1310 | sizeof(__be16)); | ||
1311 | memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN); | ||
1312 | memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN); | ||
1313 | } | ||
1314 | |||
1315 | |||
1316 | ieee80211_deliver_skb(rx); | ||
1317 | } | ||
1318 | |||
1319 | return TXRX_QUEUED; | ||
1320 | } | ||
1321 | |||
1322 | static ieee80211_txrx_result | ||
1200 | ieee80211_rx_h_data(struct ieee80211_txrx_data *rx) | 1323 | ieee80211_rx_h_data(struct ieee80211_txrx_data *rx) |
1201 | { | 1324 | { |
1202 | struct net_device *dev = rx->dev; | 1325 | struct net_device *dev = rx->dev; |
@@ -1379,6 +1502,7 @@ ieee80211_rx_handler ieee80211_rx_handlers[] = | |||
1379 | * are not passed to user space by these functions | 1502 | * are not passed to user space by these functions |
1380 | */ | 1503 | */ |
1381 | ieee80211_rx_h_remove_qos_control, | 1504 | ieee80211_rx_h_remove_qos_control, |
1505 | ieee80211_rx_h_amsdu, | ||
1382 | ieee80211_rx_h_data, | 1506 | ieee80211_rx_h_data, |
1383 | ieee80211_rx_h_mgmt, | 1507 | ieee80211_rx_h_mgmt, |
1384 | NULL | 1508 | NULL |