diff options
Diffstat (limited to 'net/wireless/util.c')
| -rw-r--r-- | net/wireless/util.c | 137 |
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 | ||
| 288 | int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr, | 291 | int 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 | } |
| 384 | EXPORT_SYMBOL(ieee80211_data_to_8023); | 387 | EXPORT_SYMBOL(ieee80211_data_to_8023); |
| 385 | 388 | ||
| 386 | int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr, | 389 | int 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 | } |
| 498 | EXPORT_SYMBOL(ieee80211_data_from_8023); | 501 | EXPORT_SYMBOL(ieee80211_data_from_8023); |
| 499 | 502 | ||
| 503 | |||
| 504 | void 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 | } | ||
| 596 | EXPORT_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. */ |
| 501 | unsigned int cfg80211_classify8021d(struct sk_buff *skb) | 599 | unsigned 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 | |||
| 822 | u16 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 | } | ||
