diff options
author | Zhu Yi <yi.zhu@intel.com> | 2009-11-30 21:18:37 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-12-22 13:31:15 -0500 |
commit | eaf85ca7fecb218fc41ff57c1642ca73b097aabb (patch) | |
tree | ad7965715f0cd67fe8946980ae2d1e9f3ea231a1 /net/wireless/util.c | |
parent | ca99861d5421c91f5a8fd3a77acb4b7be14f119d (diff) |
wireless: add ieee80211_amsdu_to_8023s
Move the A-MSDU handling code from mac80211 to cfg80211 so that more
drivers can use it. The new created function ieee80211_amsdu_to_8023s
converts an A-MSDU frame to a list of 802.3 frames.
Cc: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Zhu Yi <yi.zhu@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/wireless/util.c')
-rw-r--r-- | net/wireless/util.c | 99 |
1 files changed, 97 insertions, 2 deletions
diff --git a/net/wireless/util.c b/net/wireless/util.c index a3c841a255db..23557c1d0a9c 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c | |||
@@ -285,7 +285,7 @@ static int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) | |||
285 | } | 285 | } |
286 | } | 286 | } |
287 | 287 | ||
288 | int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr, | 288 | int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, |
289 | enum nl80211_iftype iftype) | 289 | enum nl80211_iftype iftype) |
290 | { | 290 | { |
291 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | 291 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; |
@@ -383,7 +383,7 @@ int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr, | |||
383 | } | 383 | } |
384 | EXPORT_SYMBOL(ieee80211_data_to_8023); | 384 | EXPORT_SYMBOL(ieee80211_data_to_8023); |
385 | 385 | ||
386 | int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr, | 386 | int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, |
387 | enum nl80211_iftype iftype, u8 *bssid, bool qos) | 387 | enum nl80211_iftype iftype, u8 *bssid, bool qos) |
388 | { | 388 | { |
389 | struct ieee80211_hdr hdr; | 389 | struct ieee80211_hdr hdr; |
@@ -497,6 +497,101 @@ int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr, | |||
497 | } | 497 | } |
498 | EXPORT_SYMBOL(ieee80211_data_from_8023); | 498 | EXPORT_SYMBOL(ieee80211_data_from_8023); |
499 | 499 | ||
500 | |||
501 | void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, | ||
502 | const u8 *addr, enum nl80211_iftype iftype, | ||
503 | const unsigned int extra_headroom) | ||
504 | { | ||
505 | struct sk_buff *frame = NULL; | ||
506 | u16 ethertype; | ||
507 | u8 *payload; | ||
508 | const struct ethhdr *eth; | ||
509 | int remaining, err; | ||
510 | u8 dst[ETH_ALEN], src[ETH_ALEN]; | ||
511 | |||
512 | err = ieee80211_data_to_8023(skb, addr, iftype); | ||
513 | if (err) | ||
514 | goto out; | ||
515 | |||
516 | /* skip the wrapping header */ | ||
517 | eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr)); | ||
518 | if (!eth) | ||
519 | goto out; | ||
520 | |||
521 | while (skb != frame) { | ||
522 | u8 padding; | ||
523 | __be16 len = eth->h_proto; | ||
524 | unsigned int subframe_len = sizeof(struct ethhdr) + ntohs(len); | ||
525 | |||
526 | remaining = skb->len; | ||
527 | memcpy(dst, eth->h_dest, ETH_ALEN); | ||
528 | memcpy(src, eth->h_source, ETH_ALEN); | ||
529 | |||
530 | padding = (4 - subframe_len) & 0x3; | ||
531 | /* the last MSDU has no padding */ | ||
532 | if (subframe_len > remaining) | ||
533 | goto purge; | ||
534 | |||
535 | skb_pull(skb, sizeof(struct ethhdr)); | ||
536 | /* reuse skb for the last subframe */ | ||
537 | if (remaining <= subframe_len + padding) | ||
538 | frame = skb; | ||
539 | else { | ||
540 | unsigned int hlen = ALIGN(extra_headroom, 4); | ||
541 | /* | ||
542 | * Allocate and reserve two bytes more for payload | ||
543 | * alignment since sizeof(struct ethhdr) is 14. | ||
544 | */ | ||
545 | frame = dev_alloc_skb(hlen + subframe_len + 2); | ||
546 | if (!frame) | ||
547 | goto purge; | ||
548 | |||
549 | skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2); | ||
550 | memcpy(skb_put(frame, ntohs(len)), skb->data, | ||
551 | ntohs(len)); | ||
552 | |||
553 | eth = (struct ethhdr *)skb_pull(skb, ntohs(len) + | ||
554 | padding); | ||
555 | if (!eth) { | ||
556 | dev_kfree_skb(frame); | ||
557 | goto purge; | ||
558 | } | ||
559 | } | ||
560 | |||
561 | skb_reset_network_header(frame); | ||
562 | frame->dev = skb->dev; | ||
563 | frame->priority = skb->priority; | ||
564 | |||
565 | payload = frame->data; | ||
566 | ethertype = (payload[6] << 8) | payload[7]; | ||
567 | |||
568 | if (likely((compare_ether_addr(payload, rfc1042_header) == 0 && | ||
569 | ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || | ||
570 | compare_ether_addr(payload, | ||
571 | bridge_tunnel_header) == 0)) { | ||
572 | /* remove RFC1042 or Bridge-Tunnel | ||
573 | * encapsulation and replace EtherType */ | ||
574 | skb_pull(frame, 6); | ||
575 | memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN); | ||
576 | memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN); | ||
577 | } else { | ||
578 | memcpy(skb_push(frame, sizeof(__be16)), &len, | ||
579 | sizeof(__be16)); | ||
580 | memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN); | ||
581 | memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN); | ||
582 | } | ||
583 | __skb_queue_tail(list, frame); | ||
584 | } | ||
585 | |||
586 | return; | ||
587 | |||
588 | purge: | ||
589 | __skb_queue_purge(list); | ||
590 | out: | ||
591 | dev_kfree_skb(skb); | ||
592 | } | ||
593 | EXPORT_SYMBOL(ieee80211_amsdu_to_8023s); | ||
594 | |||
500 | /* Given a data frame determine the 802.1p/1d tag to use. */ | 595 | /* Given a data frame determine the 802.1p/1d tag to use. */ |
501 | unsigned int cfg80211_classify8021d(struct sk_buff *skb) | 596 | unsigned int cfg80211_classify8021d(struct sk_buff *skb) |
502 | { | 597 | { |