Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6

This commit is contained in:
David S. Miller
2009-12-25 16:34:56 -08:00
117 changed files with 2964 additions and 1121 deletions

1
net/wireless/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
regdb.c

View File

@ -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

View File

@ -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

View File

@ -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
View 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
View 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&regdom_" 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);"
}

View File

@ -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;

View File

@ -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);

View File

@ -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(&regd->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(&reg_regdb_search_lock);
while (!list_empty(&reg_regdb_search_list)) {
request = list_first_entry(&reg_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(&regdom, curdom);
if (r)
break;
spin_unlock(&reg_regdb_search_lock);
mutex_lock(&cfg80211_mutex);
set_regdom(regdom);
mutex_unlock(&cfg80211_mutex);
spin_lock(&reg_regdb_search_lock);
break;
}
}
kfree(request);
}
spin_unlock(&reg_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(&reg_regdb_search_lock);
list_add_tail(&request->list, &reg_regdb_search_list);
spin_unlock(&reg_regdb_search_lock);
schedule_work(&reg_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(&regd->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
View 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__ */

View File

@ -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;
}

View File

@ -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;
}