aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRon Rindjunsky <ron.rindjunsky@intel.com>2007-11-26 09:14:33 -0500
committerDavid S. Miller <davem@davemloft.net>2008-01-28 17:55:32 -0500
commitfd4c7f2fce1737105208c564e1458c885918982d (patch)
tree3cb7781bb09cc3fc2619fe2bc535bb221e1d4863
parent9f985b0eee4070e494b9d62313da982cfef9b5e3 (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>
-rw-r--r--net/mac80211/ieee80211_i.h1
-rw-r--r--net/mac80211/rx.c124
2 files changed, 125 insertions, 0 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 982f99672994..66b6cf3a62ac 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -155,6 +155,7 @@ struct ieee80211_txrx_data {
155 int load; 155 int load;
156 u32 tkip_iv32; 156 u32 tkip_iv32;
157 u16 tkip_iv16; 157 u16 tkip_iv16;
158 u8 amsdu_frame;
158 } rx; 159 } rx;
159 } u; 160 } u;
160}; 161};
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
1199static ieee80211_txrx_result 1203static ieee80211_txrx_result
1204ieee80211_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
1322static ieee80211_txrx_result
1200ieee80211_rx_h_data(struct ieee80211_txrx_data *rx) 1323ieee80211_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