Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
This commit is contained in:
1
net/wireless/.gitignore
vendored
Normal file
1
net/wireless/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
regdb.c
|
@ -109,6 +109,22 @@ config WIRELESS_OLD_REGULATORY
|
||||
|
||||
Say N and if you say Y, please tell us why. The default is N.
|
||||
|
||||
config CFG80211_INTERNAL_REGDB
|
||||
bool "use statically compiled regulatory rules database" if EMBEDDED
|
||||
default n
|
||||
depends on CFG80211
|
||||
---help---
|
||||
This option generates an internal data structure representing
|
||||
the wireless regulatory rules described in net/wireless/db.txt
|
||||
and includes code to query that database. This is an alternative
|
||||
to using CRDA for defining regulatory rules for the kernel.
|
||||
|
||||
For details see:
|
||||
|
||||
http://wireless.kernel.org/en/developers/Regulatory
|
||||
|
||||
Most distributions have a CRDA package. So if unsure, say N.
|
||||
|
||||
config CFG80211_WEXT
|
||||
bool "cfg80211 wireless extensions compatibility"
|
||||
depends on CFG80211
|
||||
|
@ -13,5 +13,11 @@ cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o
|
||||
cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o
|
||||
cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o
|
||||
cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o
|
||||
cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o
|
||||
|
||||
ccflags-y += -D__CHECK_ENDIAN__
|
||||
|
||||
$(obj)/regdb.c: $(src)/db.txt $(src)/genregdb.awk
|
||||
@$(AWK) -f $(srctree)/$(src)/genregdb.awk < $< > $@
|
||||
|
||||
clean-files := regdb.c
|
||||
|
@ -378,6 +378,8 @@ int rdev_set_freq(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *for_wdev,
|
||||
int freq, enum nl80211_channel_type channel_type);
|
||||
|
||||
u16 cfg80211_calculate_bitrate(struct rate_info *rate);
|
||||
|
||||
#ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS
|
||||
#define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond)
|
||||
#else
|
||||
|
17
net/wireless/db.txt
Normal file
17
net/wireless/db.txt
Normal file
@ -0,0 +1,17 @@
|
||||
#
|
||||
# This file is a placeholder to prevent accidental build breakage if someone
|
||||
# enables CONFIG_CFG80211_INTERNAL_REGDB. Almost no one actually needs to
|
||||
# enable that build option.
|
||||
#
|
||||
# You should be using CRDA instead. It is even better if you use the CRDA
|
||||
# package provided by your distribution, since they will probably keep it
|
||||
# up-to-date on your behalf.
|
||||
#
|
||||
# If you _really_ intend to use CONFIG_CFG80211_INTERNAL_REGDB then you will
|
||||
# need to replace this file with one containing appropriately formatted
|
||||
# regulatory rules that cover the regulatory domains you will be using. Your
|
||||
# best option is to extract the db.txt file from the wireless-regdb git
|
||||
# repository:
|
||||
#
|
||||
# git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-regdb.git
|
||||
#
|
118
net/wireless/genregdb.awk
Normal file
118
net/wireless/genregdb.awk
Normal file
@ -0,0 +1,118 @@
|
||||
#!/usr/bin/awk -f
|
||||
#
|
||||
# genregdb.awk -- generate regdb.c from db.txt
|
||||
#
|
||||
# Actually, it reads from stdin (presumed to be db.txt) and writes
|
||||
# to stdout (presumed to be regdb.c), but close enough...
|
||||
#
|
||||
# Copyright 2009 John W. Linville <linville@tuxdriver.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
|
||||
BEGIN {
|
||||
active = 0
|
||||
rules = 0;
|
||||
print "/*"
|
||||
print " * DO NOT EDIT -- file generated from data in db.txt"
|
||||
print " */"
|
||||
print ""
|
||||
print "#include <linux/nl80211.h>"
|
||||
print "#include <net/cfg80211.h>"
|
||||
print ""
|
||||
regdb = "const struct ieee80211_regdomain *reg_regdb[] = {\n"
|
||||
}
|
||||
|
||||
/^[ \t]*#/ {
|
||||
/* Ignore */
|
||||
}
|
||||
|
||||
!active && /^[ \t]*$/ {
|
||||
/* Ignore */
|
||||
}
|
||||
|
||||
!active && /country/ {
|
||||
country=$2
|
||||
sub(/:/, "", country)
|
||||
printf "static const struct ieee80211_regdomain regdom_%s = {\n", country
|
||||
printf "\t.alpha2 = \"%s\",\n", country
|
||||
printf "\t.reg_rules = {\n"
|
||||
active = 1
|
||||
regdb = regdb "\t®dom_" country ",\n"
|
||||
}
|
||||
|
||||
active && /^[ \t]*\(/ {
|
||||
start = $1
|
||||
sub(/\(/, "", start)
|
||||
end = $3
|
||||
bw = $5
|
||||
sub(/\),/, "", bw)
|
||||
gain = $6
|
||||
sub(/\(/, "", gain)
|
||||
sub(/,/, "", gain)
|
||||
power = $7
|
||||
sub(/\)/, "", power)
|
||||
sub(/,/, "", power)
|
||||
# power might be in mW...
|
||||
units = $8
|
||||
sub(/\)/, "", units)
|
||||
sub(/,/, "", units)
|
||||
if (units == "mW") {
|
||||
if (power == 100) {
|
||||
power = 20
|
||||
} else if (power == 200) {
|
||||
power = 23
|
||||
} else if (power == 500) {
|
||||
power = 27
|
||||
} else if (power == 1000) {
|
||||
power = 30
|
||||
} else {
|
||||
print "Unknown power value in database!"
|
||||
}
|
||||
}
|
||||
flagstr = ""
|
||||
for (i=8; i<=NF; i++)
|
||||
flagstr = flagstr $i
|
||||
split(flagstr, flagarray, ",")
|
||||
flags = ""
|
||||
for (arg in flagarray) {
|
||||
if (flagarray[arg] == "NO-OFDM") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_NO_OFDM | "
|
||||
} else if (flagarray[arg] == "NO-CCK") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_NO_CCK | "
|
||||
} else if (flagarray[arg] == "NO-INDOOR") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_NO_INDOOR | "
|
||||
} else if (flagarray[arg] == "NO-OUTDOOR") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_NO_OUTDOOR | "
|
||||
} else if (flagarray[arg] == "DFS") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_DFS | "
|
||||
} else if (flagarray[arg] == "PTP-ONLY") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_PTP_ONLY | "
|
||||
} else if (flagarray[arg] == "PTMP-ONLY") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_PTMP_ONLY | "
|
||||
} else if (flagarray[arg] == "PASSIVE-SCAN") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_PASSIVE_SCAN | "
|
||||
} else if (flagarray[arg] == "NO-IBSS") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_NO_IBSS | "
|
||||
}
|
||||
}
|
||||
flags = flags "0"
|
||||
printf "\t\tREG_RULE(%d, %d, %d, %d, %d, %s),\n", start, end, bw, gain, power, flags
|
||||
rules++
|
||||
}
|
||||
|
||||
active && /^[ \t]*$/ {
|
||||
active = 0
|
||||
printf "\t},\n"
|
||||
printf "\t.n_reg_rules = %d\n", rules
|
||||
printf "};\n\n"
|
||||
rules = 0;
|
||||
}
|
||||
|
||||
END {
|
||||
print regdb "};"
|
||||
print ""
|
||||
print "int reg_regdb_size = ARRAY_SIZE(reg_regdb);"
|
||||
}
|
@ -137,22 +137,23 @@ void __cfg80211_send_deauth(struct net_device *dev,
|
||||
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
|
||||
const u8 *bssid = mgmt->bssid;
|
||||
int i;
|
||||
bool found = false;
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
|
||||
|
||||
if (wdev->current_bss &&
|
||||
memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
|
||||
cfg80211_unhold_bss(wdev->current_bss);
|
||||
cfg80211_put_bss(&wdev->current_bss->pub);
|
||||
wdev->current_bss = NULL;
|
||||
found = true;
|
||||
} else for (i = 0; i < MAX_AUTH_BSSES; i++) {
|
||||
if (wdev->auth_bsses[i] &&
|
||||
memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
|
||||
cfg80211_unhold_bss(wdev->auth_bsses[i]);
|
||||
cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
|
||||
wdev->auth_bsses[i] = NULL;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (wdev->authtry_bsses[i] &&
|
||||
@ -160,10 +161,16 @@ void __cfg80211_send_deauth(struct net_device *dev,
|
||||
cfg80211_unhold_bss(wdev->authtry_bsses[i]);
|
||||
cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
|
||||
wdev->authtry_bsses[i] = NULL;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return;
|
||||
|
||||
nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
|
||||
|
||||
if (wdev->sme_state == CFG80211_SME_CONNECTED) {
|
||||
u16 reason_code;
|
||||
bool from_ap;
|
||||
|
@ -1637,39 +1637,6 @@ static int parse_station_flags(struct genl_info *info,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u16 nl80211_calculate_bitrate(struct rate_info *rate)
|
||||
{
|
||||
int modulation, streams, bitrate;
|
||||
|
||||
if (!(rate->flags & RATE_INFO_FLAGS_MCS))
|
||||
return rate->legacy;
|
||||
|
||||
/* the formula below does only work for MCS values smaller than 32 */
|
||||
if (rate->mcs >= 32)
|
||||
return 0;
|
||||
|
||||
modulation = rate->mcs & 7;
|
||||
streams = (rate->mcs >> 3) + 1;
|
||||
|
||||
bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ?
|
||||
13500000 : 6500000;
|
||||
|
||||
if (modulation < 4)
|
||||
bitrate *= (modulation + 1);
|
||||
else if (modulation == 4)
|
||||
bitrate *= (modulation + 2);
|
||||
else
|
||||
bitrate *= (modulation + 3);
|
||||
|
||||
bitrate *= streams;
|
||||
|
||||
if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
|
||||
bitrate = (bitrate / 9) * 10;
|
||||
|
||||
/* do NOT round down here */
|
||||
return (bitrate + 50000) / 100000;
|
||||
}
|
||||
|
||||
static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
|
||||
int flags, struct net_device *dev,
|
||||
u8 *mac_addr, struct station_info *sinfo)
|
||||
@ -1716,8 +1683,8 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
|
||||
if (!txrate)
|
||||
goto nla_put_failure;
|
||||
|
||||
/* nl80211_calculate_bitrate will return 0 for mcs >= 32 */
|
||||
bitrate = nl80211_calculate_bitrate(&sinfo->txrate);
|
||||
/* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
|
||||
bitrate = cfg80211_calculate_bitrate(&sinfo->txrate);
|
||||
if (bitrate > 0)
|
||||
NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
|
||||
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include <net/cfg80211.h>
|
||||
#include "core.h"
|
||||
#include "reg.h"
|
||||
#include "regdb.h"
|
||||
#include "nl80211.h"
|
||||
|
||||
/* Receipt of information from last regulatory request */
|
||||
@ -335,6 +336,98 @@ static bool country_ie_integrity_changes(u32 checksum)
|
||||
return false;
|
||||
}
|
||||
|
||||
static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd,
|
||||
const struct ieee80211_regdomain *src_regd)
|
||||
{
|
||||
struct ieee80211_regdomain *regd;
|
||||
int size_of_regd = 0;
|
||||
unsigned int i;
|
||||
|
||||
size_of_regd = sizeof(struct ieee80211_regdomain) +
|
||||
((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule));
|
||||
|
||||
regd = kzalloc(size_of_regd, GFP_KERNEL);
|
||||
if (!regd)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain));
|
||||
|
||||
for (i = 0; i < src_regd->n_reg_rules; i++)
|
||||
memcpy(®d->reg_rules[i], &src_regd->reg_rules[i],
|
||||
sizeof(struct ieee80211_reg_rule));
|
||||
|
||||
*dst_regd = regd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CFG80211_INTERNAL_REGDB
|
||||
struct reg_regdb_search_request {
|
||||
char alpha2[2];
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static LIST_HEAD(reg_regdb_search_list);
|
||||
static DEFINE_SPINLOCK(reg_regdb_search_lock);
|
||||
|
||||
static void reg_regdb_search(struct work_struct *work)
|
||||
{
|
||||
struct reg_regdb_search_request *request;
|
||||
const struct ieee80211_regdomain *curdom, *regdom;
|
||||
int i, r;
|
||||
|
||||
spin_lock(®_regdb_search_lock);
|
||||
while (!list_empty(®_regdb_search_list)) {
|
||||
request = list_first_entry(®_regdb_search_list,
|
||||
struct reg_regdb_search_request,
|
||||
list);
|
||||
list_del(&request->list);
|
||||
|
||||
for (i=0; i<reg_regdb_size; i++) {
|
||||
curdom = reg_regdb[i];
|
||||
|
||||
if (!memcmp(request->alpha2, curdom->alpha2, 2)) {
|
||||
r = reg_copy_regd(®dom, curdom);
|
||||
if (r)
|
||||
break;
|
||||
spin_unlock(®_regdb_search_lock);
|
||||
mutex_lock(&cfg80211_mutex);
|
||||
set_regdom(regdom);
|
||||
mutex_unlock(&cfg80211_mutex);
|
||||
spin_lock(®_regdb_search_lock);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
kfree(request);
|
||||
}
|
||||
spin_unlock(®_regdb_search_lock);
|
||||
}
|
||||
|
||||
static DECLARE_WORK(reg_regdb_work, reg_regdb_search);
|
||||
|
||||
static void reg_regdb_query(const char *alpha2)
|
||||
{
|
||||
struct reg_regdb_search_request *request;
|
||||
|
||||
if (!alpha2)
|
||||
return;
|
||||
|
||||
request = kzalloc(sizeof(struct reg_regdb_search_request), GFP_KERNEL);
|
||||
if (!request)
|
||||
return;
|
||||
|
||||
memcpy(request->alpha2, alpha2, 2);
|
||||
|
||||
spin_lock(®_regdb_search_lock);
|
||||
list_add_tail(&request->list, ®_regdb_search_list);
|
||||
spin_unlock(®_regdb_search_lock);
|
||||
|
||||
schedule_work(®_regdb_work);
|
||||
}
|
||||
#else
|
||||
static inline void reg_regdb_query(const char *alpha2) {}
|
||||
#endif /* CONFIG_CFG80211_INTERNAL_REGDB */
|
||||
|
||||
/*
|
||||
* This lets us keep regulatory code which is updated on a regulatory
|
||||
* basis in userspace.
|
||||
@ -354,6 +447,9 @@ static int call_crda(const char *alpha2)
|
||||
printk(KERN_INFO "cfg80211: Calling CRDA to update world "
|
||||
"regulatory domain\n");
|
||||
|
||||
/* query internal regulatory database (if it exists) */
|
||||
reg_regdb_query(alpha2);
|
||||
|
||||
country_env[8] = alpha2[0];
|
||||
country_env[9] = alpha2[1];
|
||||
|
||||
@ -1342,30 +1438,6 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
|
||||
}
|
||||
EXPORT_SYMBOL(wiphy_apply_custom_regulatory);
|
||||
|
||||
static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd,
|
||||
const struct ieee80211_regdomain *src_regd)
|
||||
{
|
||||
struct ieee80211_regdomain *regd;
|
||||
int size_of_regd = 0;
|
||||
unsigned int i;
|
||||
|
||||
size_of_regd = sizeof(struct ieee80211_regdomain) +
|
||||
((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule));
|
||||
|
||||
regd = kzalloc(size_of_regd, GFP_KERNEL);
|
||||
if (!regd)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain));
|
||||
|
||||
for (i = 0; i < src_regd->n_reg_rules; i++)
|
||||
memcpy(®d->reg_rules[i], &src_regd->reg_rules[i],
|
||||
sizeof(struct ieee80211_reg_rule));
|
||||
|
||||
*dst_regd = regd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return value which can be used by ignore_request() to indicate
|
||||
* it has been determined we should intersect two regulatory domains
|
||||
|
7
net/wireless/regdb.h
Normal file
7
net/wireless/regdb.h
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef __REGDB_H__
|
||||
#define __REGDB_H__
|
||||
|
||||
extern const struct ieee80211_regdomain *reg_regdb[];
|
||||
extern int reg_regdb_size;
|
||||
|
||||
#endif /* __REGDB_H__ */
|
@ -285,7 +285,7 @@ static int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
|
||||
}
|
||||
}
|
||||
|
||||
int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr,
|
||||
int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
|
||||
enum nl80211_iftype iftype)
|
||||
{
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
||||
@ -383,7 +383,7 @@ int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr,
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_data_to_8023);
|
||||
|
||||
int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr,
|
||||
int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
|
||||
enum nl80211_iftype iftype, u8 *bssid, bool qos)
|
||||
{
|
||||
struct ieee80211_hdr hdr;
|
||||
@ -497,6 +497,101 @@ int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr,
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_data_from_8023);
|
||||
|
||||
|
||||
void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
|
||||
const u8 *addr, enum nl80211_iftype iftype,
|
||||
const unsigned int extra_headroom)
|
||||
{
|
||||
struct sk_buff *frame = NULL;
|
||||
u16 ethertype;
|
||||
u8 *payload;
|
||||
const struct ethhdr *eth;
|
||||
int remaining, err;
|
||||
u8 dst[ETH_ALEN], src[ETH_ALEN];
|
||||
|
||||
err = ieee80211_data_to_8023(skb, addr, iftype);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/* skip the wrapping header */
|
||||
eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr));
|
||||
if (!eth)
|
||||
goto out;
|
||||
|
||||
while (skb != frame) {
|
||||
u8 padding;
|
||||
__be16 len = eth->h_proto;
|
||||
unsigned int subframe_len = sizeof(struct ethhdr) + ntohs(len);
|
||||
|
||||
remaining = skb->len;
|
||||
memcpy(dst, eth->h_dest, ETH_ALEN);
|
||||
memcpy(src, eth->h_source, ETH_ALEN);
|
||||
|
||||
padding = (4 - subframe_len) & 0x3;
|
||||
/* the last MSDU has no padding */
|
||||
if (subframe_len > remaining)
|
||||
goto purge;
|
||||
|
||||
skb_pull(skb, sizeof(struct ethhdr));
|
||||
/* reuse skb for the last subframe */
|
||||
if (remaining <= subframe_len + padding)
|
||||
frame = skb;
|
||||
else {
|
||||
unsigned int hlen = ALIGN(extra_headroom, 4);
|
||||
/*
|
||||
* Allocate and reserve two bytes more for payload
|
||||
* alignment since sizeof(struct ethhdr) is 14.
|
||||
*/
|
||||
frame = dev_alloc_skb(hlen + subframe_len + 2);
|
||||
if (!frame)
|
||||
goto purge;
|
||||
|
||||
skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
|
||||
memcpy(skb_put(frame, ntohs(len)), skb->data,
|
||||
ntohs(len));
|
||||
|
||||
eth = (struct ethhdr *)skb_pull(skb, ntohs(len) +
|
||||
padding);
|
||||
if (!eth) {
|
||||
dev_kfree_skb(frame);
|
||||
goto purge;
|
||||
}
|
||||
}
|
||||
|
||||
skb_reset_network_header(frame);
|
||||
frame->dev = skb->dev;
|
||||
frame->priority = skb->priority;
|
||||
|
||||
payload = frame->data;
|
||||
ethertype = (payload[6] << 8) | payload[7];
|
||||
|
||||
if (likely((compare_ether_addr(payload, rfc1042_header) == 0 &&
|
||||
ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
|
||||
compare_ether_addr(payload,
|
||||
bridge_tunnel_header) == 0)) {
|
||||
/* remove RFC1042 or Bridge-Tunnel
|
||||
* encapsulation and replace EtherType */
|
||||
skb_pull(frame, 6);
|
||||
memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
|
||||
memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
|
||||
} else {
|
||||
memcpy(skb_push(frame, sizeof(__be16)), &len,
|
||||
sizeof(__be16));
|
||||
memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
|
||||
memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
|
||||
}
|
||||
__skb_queue_tail(list, frame);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
purge:
|
||||
__skb_queue_purge(list);
|
||||
out:
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_amsdu_to_8023s);
|
||||
|
||||
/* Given a data frame determine the 802.1p/1d tag to use. */
|
||||
unsigned int cfg80211_classify8021d(struct sk_buff *skb)
|
||||
{
|
||||
@ -720,3 +815,36 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
u16 cfg80211_calculate_bitrate(struct rate_info *rate)
|
||||
{
|
||||
int modulation, streams, bitrate;
|
||||
|
||||
if (!(rate->flags & RATE_INFO_FLAGS_MCS))
|
||||
return rate->legacy;
|
||||
|
||||
/* the formula below does only work for MCS values smaller than 32 */
|
||||
if (rate->mcs >= 32)
|
||||
return 0;
|
||||
|
||||
modulation = rate->mcs & 7;
|
||||
streams = (rate->mcs >> 3) + 1;
|
||||
|
||||
bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ?
|
||||
13500000 : 6500000;
|
||||
|
||||
if (modulation < 4)
|
||||
bitrate *= (modulation + 1);
|
||||
else if (modulation == 4)
|
||||
bitrate *= (modulation + 2);
|
||||
else
|
||||
bitrate *= (modulation + 3);
|
||||
|
||||
bitrate *= streams;
|
||||
|
||||
if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
|
||||
bitrate = (bitrate / 9) * 10;
|
||||
|
||||
/* do NOT round down here */
|
||||
return (bitrate + 50000) / 100000;
|
||||
}
|
||||
|
@ -1257,10 +1257,7 @@ int cfg80211_wext_giwrate(struct net_device *dev,
|
||||
if (!(sinfo.filled & STATION_INFO_TX_BITRATE))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
rate->value = 0;
|
||||
|
||||
if (!(sinfo.txrate.flags & RATE_INFO_FLAGS_MCS))
|
||||
rate->value = 100000 * sinfo.txrate.legacy;
|
||||
rate->value = 100000 * cfg80211_calculate_bitrate(&sinfo.txrate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Reference in New Issue
Block a user