aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/wireless/util.c')
-rw-r--r--net/wireless/util.c137
1 files changed, 134 insertions, 3 deletions
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 59361fdcb5d0..be2ab8c59e3a 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -227,8 +227,11 @@ unsigned int ieee80211_hdrlen(__le16 fc)
227 if (ieee80211_is_data(fc)) { 227 if (ieee80211_is_data(fc)) {
228 if (ieee80211_has_a4(fc)) 228 if (ieee80211_has_a4(fc))
229 hdrlen = 30; 229 hdrlen = 30;
230 if (ieee80211_is_data_qos(fc)) 230 if (ieee80211_is_data_qos(fc)) {
231 hdrlen += IEEE80211_QOS_CTL_LEN; 231 hdrlen += IEEE80211_QOS_CTL_LEN;
232 if (ieee80211_has_order(fc))
233 hdrlen += IEEE80211_HT_CTL_LEN;
234 }
232 goto out; 235 goto out;
233 } 236 }
234 237
@@ -285,7 +288,7 @@ static int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
285 } 288 }
286} 289}
287 290
288int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr, 291int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
289 enum nl80211_iftype iftype) 292 enum nl80211_iftype iftype)
290{ 293{
291 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; 294 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
@@ -383,7 +386,7 @@ int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr,
383} 386}
384EXPORT_SYMBOL(ieee80211_data_to_8023); 387EXPORT_SYMBOL(ieee80211_data_to_8023);
385 388
386int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr, 389int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
387 enum nl80211_iftype iftype, u8 *bssid, bool qos) 390 enum nl80211_iftype iftype, u8 *bssid, bool qos)
388{ 391{
389 struct ieee80211_hdr hdr; 392 struct ieee80211_hdr hdr;
@@ -497,6 +500,101 @@ int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr,
497} 500}
498EXPORT_SYMBOL(ieee80211_data_from_8023); 501EXPORT_SYMBOL(ieee80211_data_from_8023);
499 502
503
504void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
505 const u8 *addr, enum nl80211_iftype iftype,
506 const unsigned int extra_headroom)
507{
508 struct sk_buff *frame = NULL;
509 u16 ethertype;
510 u8 *payload;
511 const struct ethhdr *eth;
512 int remaining, err;
513 u8 dst[ETH_ALEN], src[ETH_ALEN];
514
515 err = ieee80211_data_to_8023(skb, addr, iftype);
516 if (err)
517 goto out;
518
519 /* skip the wrapping header */
520 eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr));
521 if (!eth)
522 goto out;
523
524 while (skb != frame) {
525 u8 padding;
526 __be16 len = eth->h_proto;
527 unsigned int subframe_len = sizeof(struct ethhdr) + ntohs(len);
528
529 remaining = skb->len;
530 memcpy(dst, eth->h_dest, ETH_ALEN);
531 memcpy(src, eth->h_source, ETH_ALEN);
532
533 padding = (4 - subframe_len) & 0x3;
534 /* the last MSDU has no padding */
535 if (subframe_len > remaining)
536 goto purge;
537
538 skb_pull(skb, sizeof(struct ethhdr));
539 /* reuse skb for the last subframe */
540 if (remaining <= subframe_len + padding)
541 frame = skb;
542 else {
543 unsigned int hlen = ALIGN(extra_headroom, 4);
544 /*
545 * Allocate and reserve two bytes more for payload
546 * alignment since sizeof(struct ethhdr) is 14.
547 */
548 frame = dev_alloc_skb(hlen + subframe_len + 2);
549 if (!frame)
550 goto purge;
551
552 skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
553 memcpy(skb_put(frame, ntohs(len)), skb->data,
554 ntohs(len));
555
556 eth = (struct ethhdr *)skb_pull(skb, ntohs(len) +
557 padding);
558 if (!eth) {
559 dev_kfree_skb(frame);
560 goto purge;
561 }
562 }
563
564 skb_reset_network_header(frame);
565 frame->dev = skb->dev;
566 frame->priority = skb->priority;
567
568 payload = frame->data;
569 ethertype = (payload[6] << 8) | payload[7];
570
571 if (likely((compare_ether_addr(payload, rfc1042_header) == 0 &&
572 ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
573 compare_ether_addr(payload,
574 bridge_tunnel_header) == 0)) {
575 /* remove RFC1042 or Bridge-Tunnel
576 * encapsulation and replace EtherType */
577 skb_pull(frame, 6);
578 memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
579 memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
580 } else {
581 memcpy(skb_push(frame, sizeof(__be16)), &len,
582 sizeof(__be16));
583 memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
584 memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
585 }
586 __skb_queue_tail(list, frame);
587 }
588
589 return;
590
591 purge:
592 __skb_queue_purge(list);
593 out:
594 dev_kfree_skb(skb);
595}
596EXPORT_SYMBOL(ieee80211_amsdu_to_8023s);
597
500/* Given a data frame determine the 802.1p/1d tag to use. */ 598/* Given a data frame determine the 802.1p/1d tag to use. */
501unsigned int cfg80211_classify8021d(struct sk_buff *skb) 599unsigned int cfg80211_classify8021d(struct sk_buff *skb)
502{ 600{
@@ -720,3 +818,36 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
720 818
721 return err; 819 return err;
722} 820}
821
822u16 cfg80211_calculate_bitrate(struct rate_info *rate)
823{
824 int modulation, streams, bitrate;
825
826 if (!(rate->flags & RATE_INFO_FLAGS_MCS))
827 return rate->legacy;
828
829 /* the formula below does only work for MCS values smaller than 32 */
830 if (rate->mcs >= 32)
831 return 0;
832
833 modulation = rate->mcs & 7;
834 streams = (rate->mcs >> 3) + 1;
835
836 bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ?
837 13500000 : 6500000;
838
839 if (modulation < 4)
840 bitrate *= (modulation + 1);
841 else if (modulation == 4)
842 bitrate *= (modulation + 2);
843 else
844 bitrate *= (modulation + 3);
845
846 bitrate *= streams;
847
848 if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
849 bitrate = (bitrate / 9) * 10;
850
851 /* do NOT round down here */
852 return (bitrate + 50000) / 100000;
853}