summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorTodd Doucet <todd.doucet@timesys.com>2010-02-03 17:06:33 -0500
committerTodd Doucet <todd.doucet@timesys.com>2010-02-03 17:06:33 -0500
commitff238a4df84428befc55d49f58864dfc47ff853d (patch)
tree8796b828b396f5b7ed017904ff77b614dbf38677 /net
parent4a6908a3a050aacc9c3a2f36b276b46c0629ad91 (diff)
Kernel as received from Digi for their Wi-Mx51 SoC running on their
CCWMX51JS carrier board.
Diffstat (limited to 'net')
-rw-r--r--net/core/skbuff.c1
-rw-r--r--net/ieee80211/Kconfig1
-rw-r--r--net/ieee80211/Makefile1
-rw-r--r--net/ieee80211/compat.h104
-rw-r--r--net/ieee80211/ieee80211_tx.c176
-rw-r--r--net/ieee80211/ieee80211_wx.c89
-rw-r--r--net/ieee80211/softmac/Kconfig12
-rw-r--r--net/ieee80211/softmac/Makefile9
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_assoc.c542
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_auth.c413
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_event.c190
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_io.c488
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_module.c621
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_priv.h256
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_scan.c331
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_wx.c514
-rw-r--r--net/ipv4/Kconfig8
-rw-r--r--net/ipv4/ipconfig.c9
-rw-r--r--net/mac80211/Kconfig30
-rw-r--r--net/mac80211/Makefile6
-rw-r--r--net/mac80211/cfg.c194
-rw-r--r--net/mac80211/debugfs.c12
-rw-r--r--net/mac80211/debugfs_key.c4
-rw-r--r--net/mac80211/debugfs_netdev.c117
-rw-r--r--net/mac80211/debugfs_sta.c84
-rw-r--r--net/mac80211/event.c5
-rw-r--r--net/mac80211/ht.c267
-rw-r--r--net/mac80211/ieee80211_i.h92
-rw-r--r--net/mac80211/iface.c55
-rw-r--r--net/mac80211/key.c10
-rw-r--r--net/mac80211/main.c288
-rw-r--r--net/mac80211/mesh.c4
-rw-r--r--net/mac80211/mesh.h5
-rw-r--r--net/mac80211/mesh_hwmp.c7
-rw-r--r--net/mac80211/mesh_plink.c50
-rw-r--r--net/mac80211/mlme.c609
-rw-r--r--net/mac80211/rate.c52
-rw-r--r--net/mac80211/rate.h24
-rw-r--r--net/mac80211/rc80211_minstrel.c119
-rw-r--r--net/mac80211/rc80211_minstrel.h2
-rw-r--r--net/mac80211/rc80211_multirate_pid.h301
-rw-r--r--net/mac80211/rc80211_multirate_pid_algo.c569
-rw-r--r--net/mac80211/rc80211_multirate_pid_debugfs.c257
-rw-r--r--net/mac80211/rc80211_pid.h3
-rw-r--r--net/mac80211/rc80211_pid_algo.c37
-rw-r--r--net/mac80211/rc80211_pid_debugfs.c5
-rw-r--r--net/mac80211/rx.c278
-rw-r--r--net/mac80211/scan.c24
-rw-r--r--net/mac80211/sta_info.c37
-rw-r--r--net/mac80211/sta_info.h44
-rw-r--r--net/mac80211/tkip.c10
-rw-r--r--net/mac80211/tx.c472
-rw-r--r--net/mac80211/util.c107
-rw-r--r--net/mac80211/wep.c33
-rw-r--r--net/mac80211/wep.h2
-rw-r--r--net/mac80211/wext.c293
-rw-r--r--net/mac80211/wme.c32
-rw-r--r--net/mac80211/wpa.c32
-rw-r--r--net/wireless/Kconfig40
-rw-r--r--net/wireless/Makefile7
-rw-r--r--net/wireless/core.c14
-rw-r--r--net/wireless/core.h13
-rw-r--r--net/wireless/lib80211.c284
-rw-r--r--net/wireless/lib80211_crypt_ccmp.c494
-rw-r--r--net/wireless/lib80211_crypt_tkip.c788
-rw-r--r--net/wireless/lib80211_crypt_wep.c296
-rw-r--r--net/wireless/nl80211.c428
-rw-r--r--net/wireless/reg.c1146
-rw-r--r--net/wireless/reg.h33
-rw-r--r--net/wireless/sysfs.c25
-rw-r--r--net/wireless/util.c19
-rw-r--r--net/wireless/wext-compat.c139
-rw-r--r--net/wireless/wext.c2
73 files changed, 10162 insertions, 1903 deletions
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 65f7757465bd..08c4f10ade69 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -556,6 +556,7 @@ static struct sk_buff *__skb_clone(struct sk_buff *n, struct sk_buff *skb)
C(truesize);
#if defined(CONFIG_MAC80211) || defined(CONFIG_MAC80211_MODULE)
C(do_not_encrypt);
+ C(requeue);
#endif
atomic_set(&n->users, 1);
diff --git a/net/ieee80211/Kconfig b/net/ieee80211/Kconfig
index 94ed7d3cd9da..bd501046c9c0 100644
--- a/net/ieee80211/Kconfig
+++ b/net/ieee80211/Kconfig
@@ -71,3 +71,4 @@ config IEEE80211_CRYPT_TKIP
This can be compiled as a module and it will be called
"ieee80211_crypt_tkip".
+source "net/ieee80211/softmac/Kconfig"
diff --git a/net/ieee80211/Makefile b/net/ieee80211/Makefile
index f988417121da..796a7c76ee48 100644
--- a/net/ieee80211/Makefile
+++ b/net/ieee80211/Makefile
@@ -10,3 +10,4 @@ ieee80211-objs := \
ieee80211_wx.o \
ieee80211_geo.o
+obj-$(CONFIG_IEEE80211_SOFTMAC) += softmac/
diff --git a/net/ieee80211/compat.h b/net/ieee80211/compat.h
new file mode 100644
index 000000000000..eedd84a98c0f
--- /dev/null
+++ b/net/ieee80211/compat.h
@@ -0,0 +1,104 @@
+/*
+ * Header file to maintain compatibility among different kernel versions.
+ *
+ * Copyright (c) 2004-2006 Zhu Yi <yi.zhu@intel.com>, Intel Corporation
+ *
+ * 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. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/version.h>
+#include <linux/if_ether.h> /* ETH_ALEN */
+#include <linux/wireless.h>
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,8)
+#define __iomem
+#define __le32 u32
+#endif
+
+#ifndef NETDEV_TX_OK
+#define NETDEV_TX_OK 0
+#endif
+
+#ifndef ARPHRD_IEEE80211_RADIOTAP
+#define ARPHRD_IEEE80211_RADIOTAP 803 /* IEEE 802.11 + radiotap header */
+#endif
+
+#ifndef __bitwise /* if __leXX is not defined */
+typedef __u16 __le16;
+typedef __u64 __le64;
+#endif
+
+#ifndef DEFINE_SPINLOCK
+#define DEFINE_SPINLOCK(s) spinlock_t s = SPIN_LOCK_UNLOCKED
+#endif
+
+#ifndef WIRELESS_SPY
+#define WIRELESS_SPY /* enable iwspy support */
+#endif
+
+#ifndef __nocast
+#define __nocast
+#endif
+
+#ifndef NETDEV_TX_BUSY
+#define NETDEV_TX_BUSY 1
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+typedef unsigned gfp_t;
+#endif
+
+/* WE compatibility macros */
+#if WIRELESS_EXT < 17
+#define IW_QUAL_QUAL_UPDATED 0x01 /* Value was updated since last read */
+#define IW_QUAL_LEVEL_UPDATED 0x02
+#define IW_QUAL_NOISE_UPDATED 0x04
+#define IW_QUAL_ALL_UPDATED 0x07
+#define IW_QUAL_QUAL_INVALID 0x10 /* Driver doesn't provide value */
+#define IW_QUAL_LEVEL_INVALID 0x20
+#define IW_QUAL_NOISE_INVALID 0x40
+#define IW_QUAL_ALL_INVALID 0x70
+#endif
+
+#if WIRELESS_EXT < 19
+#define IW_QUAL_DBM 0x08 /* Level + Noise are dBm */
+#endif
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)
+static inline int is_multicast_ether_addr(const u8 *addr)
+{
+ return addr[0] & 0x01;
+}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+static inline int is_broadcast_ether_addr(const u8 *addr)
+{
+ return (addr[0] & addr[1] & addr[2] & addr[3] & addr[4] & addr[5]) == 0xff;
+}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+static inline void *kzalloc(size_t size, unsigned __nocast flags)
+{
+ void *ret = kmalloc(size, flags);
+ if (ret)
+ memset(ret, 0, size);
+ return ret;
+}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
+static inline unsigned compare_ether_addr(const u8 *_a, const u8 *_b)
+{
+ const u16 *a = (const u16 *) _a;
+ const u16 *b = (const u16 *) _b;
+
+ BUILD_BUG_ON(ETH_ALEN != 6);
+ return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2])) != 0;
+}
+#endif
diff --git a/net/ieee80211/ieee80211_tx.c b/net/ieee80211/ieee80211_tx.c
index d996547f7a62..d44c96b73d51 100644
--- a/net/ieee80211/ieee80211_tx.c
+++ b/net/ieee80211/ieee80211_tx.c
@@ -41,6 +41,7 @@
#include <linux/etherdevice.h>
#include <asm/uaccess.h>
+#include "compat.h"
#include <net/ieee80211.h>
/*
@@ -54,7 +55,7 @@ Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | Frame | fcs |
| | tion | (BSSID) | | | ence | data | |
`--------------------------------------------------| |------'
Total: 28 non-data bytes `----.----'
- |
+ |
.- 'Frame data' expands, if WEP enabled, to <----------'
|
V
@@ -64,8 +65,8 @@ Bytes | 4 | 0-2296 | 4 |
Desc. | IV | Encrypted | ICV |
| | Packet | |
`-----| |-----'
- `-----.-----'
- |
+ `-----.-----'
+ |
.- 'Encrypted Packet' expands to
|
V
@@ -126,7 +127,7 @@ payload of each frame is reduced to 492 bytes.
static u8 P802_1H_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0xf8 };
static u8 RFC1042_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 };
-static int ieee80211_copy_snap(u8 * data, __be16 h_proto)
+static int ieee80211_copy_snap(u8 * data, u16 h_proto)
{
struct ieee80211_snap_hdr *snap;
u8 *oui;
@@ -136,7 +137,7 @@ static int ieee80211_copy_snap(u8 * data, __be16 h_proto)
snap->ssap = 0xaa;
snap->ctrl = 0x03;
- if (h_proto == htons(ETH_P_AARP) || h_proto == htons(ETH_P_IPX))
+ if (h_proto == 0x8137 || h_proto == 0x80f3)
oui = P802_1H_OUI;
else
oui = RFC1042_OUI;
@@ -144,7 +145,7 @@ static int ieee80211_copy_snap(u8 * data, __be16 h_proto)
snap->oui[1] = oui[1];
snap->oui[2] = oui[2];
- memcpy(data + SNAP_SIZE, &h_proto, sizeof(u16));
+ *(u16 *) (data + SNAP_SIZE) = htons(h_proto);
return SNAP_SIZE + sizeof(u16);
}
@@ -167,9 +168,13 @@ static int ieee80211_encrypt_fragment(struct ieee80211_device *ieee,
atomic_dec(&crypt->refcnt);
if (res < 0) {
+ unsigned long flags;
+
+ spin_lock_irqsave( &ieee->lock, flags );
printk(KERN_INFO "%s: Encryption failed: len=%d.\n",
ieee->dev->name, frag->len);
ieee->ieee_stats.tx_discards++;
+ spin_unlock_irqrestore( &ieee->lock, flags );
return -1;
}
@@ -225,10 +230,10 @@ static int ieee80211_classify(struct sk_buff *skb)
struct iphdr *ip;
eth = (struct ethhdr *)skb->data;
- if (eth->h_proto != htons(ETH_P_IP))
+ if (eth->h_proto != __constant_htons(ETH_P_IP))
return 0;
- ip = ip_hdr(skb);
+ ip = ip_hdr(skb);
switch (ip->tos & 0xfc) {
case 0x20:
return 2;
@@ -256,12 +261,11 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
struct ieee80211_device *ieee = netdev_priv(dev);
struct ieee80211_txb *txb = NULL;
struct ieee80211_hdr_3addrqos *frag_hdr;
- int i, bytes_per_frag, nr_frags, bytes_last_frag, frag_size,
+ int i, i_unlocked, bytes_per_frag, nr_frags, bytes_last_frag, frag_size,
rts_required;
unsigned long flags;
struct net_device_stats *stats = &ieee->stats;
- int encrypt, host_encrypt, host_encrypt_msdu, host_build_iv;
- __be16 ether_type;
+ int ether_type, encrypt, host_encrypt, host_encrypt_msdu, host_build_iv;
int bytes, fc, hdr_len;
struct sk_buff *skb_frag;
struct ieee80211_hdr_3addrqos header = {/* Ensure zero initialized */
@@ -273,6 +277,7 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
struct ieee80211_crypt_data *crypt;
int priority = skb->priority;
int snapped = 0;
+ u16 frag = 0;
if (ieee->is_queue_full && (*ieee->is_queue_full) (dev, priority))
return NETDEV_TX_BUSY;
@@ -283,20 +288,22 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
* creating it... */
if (!ieee->hard_start_xmit) {
printk(KERN_WARNING "%s: No xmit handler.\n", ieee->dev->name);
+ spin_unlock_irqrestore(&ieee->lock, flags);
goto success;
}
if (unlikely(skb->len < SNAP_SIZE + sizeof(u16))) {
printk(KERN_WARNING "%s: skb too small (%d).\n",
ieee->dev->name, skb->len);
+ spin_unlock_irqrestore(&ieee->lock, flags);
goto success;
}
- ether_type = ((struct ethhdr *)skb->data)->h_proto;
+ ether_type = ntohs(((struct ethhdr *)skb->data)->h_proto);
crypt = ieee->crypt[ieee->tx_keyidx];
- encrypt = !(ether_type == htons(ETH_P_PAE) && ieee->ieee802_1x) &&
+ encrypt = !(ether_type == ETH_P_PAE && ieee->ieee802_1x) &&
ieee->sec.encrypt;
host_encrypt = ieee->host_encrypt && encrypt && crypt;
@@ -304,14 +311,15 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
host_build_iv = ieee->host_build_iv && encrypt && crypt;
if (!encrypt && ieee->ieee802_1x &&
- ieee->drop_unencrypted && ether_type != htons(ETH_P_PAE)) {
+ ieee->drop_unencrypted && ether_type != ETH_P_PAE) {
stats->tx_dropped++;
+ spin_unlock_irqrestore(&ieee->lock, flags);
goto success;
}
/* Save source and destination addresses */
- skb_copy_from_linear_data(skb, dest, ETH_ALEN);
- skb_copy_from_linear_data_offset(skb, ETH_ALEN, src, ETH_ALEN);
+ memcpy(dest, skb->data, ETH_ALEN);
+ memcpy(src, skb->data + ETH_ALEN, ETH_ALEN);
if (host_encrypt || host_build_iv)
fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA |
@@ -364,7 +372,7 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
snapped = 1;
ieee80211_copy_snap(skb_put(skb_new, SNAP_SIZE + sizeof(u16)),
ether_type);
- skb_copy_from_linear_data(skb, skb_put(skb_new, skb->len), skb->len);
+ memcpy(skb_put(skb_new, skb->len), skb->data, skb->len);
res = crypt->ops->encrypt_msdu(skb_new, hdr_len, crypt->priv);
if (res < 0) {
IEEE80211_ERROR("msdu encryption failed\n");
@@ -387,14 +395,14 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
else
frag_size = ieee->fts;
+ /* the FCS must be included always in fragmentation calculation */
+ frag_size -= IEEE80211_FCS_LEN;
+
/* Determine amount of payload per fragment. Regardless of if
* this stack is providing the full 802.11 header, one will
* eventually be affixed to this fragment -- so we must account
* for it when determining the amount of payload space. */
- bytes_per_frag = frag_size - hdr_len;
- if (ieee->config &
- (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS))
- bytes_per_frag -= IEEE80211_FCS_LEN;
+ bytes_per_frag = frag_size - IEEE80211_3ADDR_LEN;
/* Each fragment may need to have room for encryptiong
* pre/postfix */
@@ -413,11 +421,15 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
} else {
nr_frags = 1;
bytes_per_frag = bytes_last_frag = bytes;
- frag_size = bytes + hdr_len;
+ frag_size = bytes + IEEE80211_3ADDR_LEN - IEEE80211_FCS_LEN;
}
- rts_required = (frag_size > ieee->rts
- && ieee->config & CFG_IEEE80211_RTS);
+ /* threshold is on transmitted data */
+ rts_required = ( ieee->config & CFG_IEEE80211_RTS ) &&
+ ( ieee->rts < ( ( ( nr_frags > 1 ) ?
+ bytes_per_frag :
+ bytes_last_frag ) +
+ IEEE80211_FCS_LEN + IEEE80211_3ADDR_LEN));
if (rts_required)
nr_frags++;
@@ -433,7 +445,7 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
}
txb->encrypted = encrypt;
if (host_encrypt)
- txb->payload_size = frag_size * (nr_frags - 1) +
+ txb->payload_size = ( frag_size + IEEE80211_FCS_LEN ) * (nr_frags - 1) +
bytes_last_frag;
else
txb->payload_size = bytes;
@@ -464,7 +476,9 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
} else
i = 0;
+ i_unlocked = i;
for (; i < nr_frags; i++) {
+ u16 sc;
skb_frag = txb->fragments[i];
if (host_encrypt || host_build_iv)
@@ -475,6 +489,10 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
(struct ieee80211_hdr_3addrqos *)skb_put(skb_frag, hdr_len);
memcpy(frag_hdr, &header, hdr_len);
+ /* add frag number to frag_hdr */
+ sc = le16_to_cpu(frag_hdr->seq_ctl);
+ sc |= (frag++ & 0x0F);
+ frag_hdr->seq_ctl = cpu_to_le16(sc);
/* If this is not the last fragment, then add the MOREFRAGS
* bit to the frame control */
if (i != nr_frags - 1) {
@@ -486,23 +504,37 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
bytes = bytes_last_frag;
}
- if (i == 0 && !snapped) {
+ if ((((i == 0) && !txb->rts_included) ||
+ ((i == 1) && txb->rts_included)) && !snapped) {
ieee80211_copy_snap(skb_put
(skb_frag, SNAP_SIZE + sizeof(u16)),
ether_type);
bytes -= SNAP_SIZE + sizeof(u16);
}
- skb_copy_from_linear_data(skb, skb_put(skb_frag, bytes), bytes);
+ memcpy(skb_put(skb_frag, bytes), skb->data, bytes);
/* Advance the SKB... */
skb_pull(skb, bytes);
+ }
+
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+ /* this may take some time. We really don't wonna do this with irqs
+ * disabled. */
+ i = i_unlocked;
+ for (; i < nr_frags; i++) {
+ skb_frag = txb->fragments[i];
/* Encryption routine will move the header forward in order
* to insert the IV between the header and the payload */
if (host_encrypt)
ieee80211_encrypt_fragment(ieee, skb_frag, hdr_len);
else if (host_build_iv) {
+ struct ieee80211_crypt_data *crypt;
+
+ spin_lock_irqsave( &ieee->lock, flags );
+ crypt = ieee->crypt[ieee->tx_keyidx];
atomic_inc(&crypt->refcnt);
if (crypt->ops->build_iv)
crypt->ops->build_iv(skb_frag, hdr_len,
@@ -510,6 +542,7 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
ieee->sec.key_sizes[ieee->sec.active_key],
crypt->priv);
atomic_dec(&crypt->refcnt);
+ spin_unlock_irqrestore( &ieee->lock, flags );
}
if (ieee->config &
@@ -518,7 +551,6 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
}
success:
- spin_unlock_irqrestore(&ieee->lock, flags);
dev_kfree_skb_any(skb);
@@ -542,4 +574,92 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
return 1;
}
+/* Incoming 802.11 strucure is converted to a TXB
+ * a block of 802.11 fragment packets (stored as skbs) */
+int ieee80211_tx_frame(struct ieee80211_device *ieee,
+ struct ieee80211_hdr *frame, int hdr_len, int total_len,
+ int encrypt_mpdu)
+{
+ struct ieee80211_txb *txb = NULL;
+ unsigned long flags;
+ struct net_device_stats *stats = &ieee->stats;
+ struct sk_buff *skb_frag;
+ int priority = -1;
+ int fraglen = total_len;
+ int headroom = ieee->tx_headroom;
+ struct ieee80211_crypt_data *crypt = ieee->crypt[ieee->tx_keyidx];
+
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ if (encrypt_mpdu && (!ieee->sec.encrypt || !crypt))
+ encrypt_mpdu = 0;
+
+ /* If there is no driver handler to take the TXB, dont' bother
+ * creating it... */
+ if (!ieee->hard_start_xmit) {
+ printk(KERN_WARNING "%s: No xmit handler.\n", ieee->dev->name);
+ spin_unlock_irqrestore(&ieee->lock, flags);
+ goto success;
+ }
+
+ if (unlikely(total_len < 24)) {
+ printk(KERN_WARNING "%s: skb too small (%d).\n",
+ ieee->dev->name, total_len);
+ spin_unlock_irqrestore(&ieee->lock, flags);
+ goto success;
+ }
+
+ if (encrypt_mpdu) {
+ frame->frame_ctl |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+ fraglen += crypt->ops->extra_mpdu_prefix_len +
+ crypt->ops->extra_mpdu_postfix_len;
+ headroom += crypt->ops->extra_mpdu_prefix_len;
+ }
+
+ /* When we allocate the TXB we allocate enough space for the reserve
+ * and full fragment bytes (bytes_per_frag doesn't include prefix,
+ * postfix, header, FCS, etc.) */
+ txb = ieee80211_alloc_txb(1, fraglen, headroom, GFP_ATOMIC);
+ if (unlikely(!txb)) {
+ printk(KERN_WARNING "%s: Could not allocate TXB\n",
+ ieee->dev->name);
+ goto failed;
+ }
+ txb->encrypted = 0;
+ txb->payload_size = fraglen;
+
+ skb_frag = txb->fragments[0];
+
+ memcpy(skb_put(skb_frag, total_len), frame, total_len);
+
+ if (ieee->config &
+ (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS))
+ skb_put(skb_frag, 4);
+
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+ /* To avoid overcomplicating things, we do the corner-case frame
+ * encryption in software. The only real situation where encryption is
+ * needed here is during software-based shared key authentication. */
+ if (encrypt_mpdu)
+ ieee80211_encrypt_fragment(ieee, skb_frag, hdr_len);
+
+ success:
+ if (txb) {
+ if ((*ieee->hard_start_xmit) (txb, ieee->dev, priority) == 0) {
+ stats->tx_packets++;
+ stats->tx_bytes += txb->payload_size;
+ return 0;
+ }
+ ieee80211_txb_free(txb);
+ }
+ return 0;
+
+ failed:
+ spin_unlock_irqrestore(&ieee->lock, flags);
+ stats->tx_errors++;
+ return 1;
+}
+
+EXPORT_SYMBOL(ieee80211_tx_frame);
EXPORT_SYMBOL(ieee80211_txb_free);
diff --git a/net/ieee80211/ieee80211_wx.c b/net/ieee80211/ieee80211_wx.c
index 973832dd7faf..689e11b602d3 100644
--- a/net/ieee80211/ieee80211_wx.c
+++ b/net/ieee80211/ieee80211_wx.c
@@ -752,9 +752,98 @@ int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
return 0;
}
+int ieee80211_wx_set_auth(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra)
+{
+ struct ieee80211_device *ieee = netdev_priv(dev);
+ unsigned long flags;
+ int err = 0;
+
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ switch (wrqu->param.flags & IW_AUTH_INDEX) {
+ case IW_AUTH_WPA_VERSION:
+ case IW_AUTH_CIPHER_PAIRWISE:
+ case IW_AUTH_CIPHER_GROUP:
+ case IW_AUTH_KEY_MGMT:
+ /*
+ * Host AP driver does not use these parameters and allows
+ * wpa_supplicant to control them internally.
+ */
+ break;
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ break; /* FIXME */
+ case IW_AUTH_DROP_UNENCRYPTED:
+ ieee->drop_unencrypted = !!wrqu->param.value;
+ break;
+ case IW_AUTH_80211_AUTH_ALG:
+ break; /* FIXME */
+ case IW_AUTH_WPA_ENABLED:
+ ieee->privacy_invoked = ieee->wpa_enabled = !!wrqu->param.value;
+ break;
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ ieee->ieee802_1x = !!wrqu->param.value;
+ break;
+ case IW_AUTH_PRIVACY_INVOKED:
+ ieee->privacy_invoked = !!wrqu->param.value;
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+ spin_unlock_irqrestore(&ieee->lock, flags);
+ return err;
+}
+
+int ieee80211_wx_get_auth(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra)
+{
+ struct ieee80211_device *ieee = netdev_priv(dev);
+ unsigned long flags;
+ int err = 0;
+
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ switch (wrqu->param.flags & IW_AUTH_INDEX) {
+ case IW_AUTH_WPA_VERSION:
+ case IW_AUTH_CIPHER_PAIRWISE:
+ case IW_AUTH_CIPHER_GROUP:
+ case IW_AUTH_KEY_MGMT:
+ case IW_AUTH_TKIP_COUNTERMEASURES: /* FIXME */
+ case IW_AUTH_80211_AUTH_ALG: /* FIXME */
+ /*
+ * Host AP driver does not use these parameters and allows
+ * wpa_supplicant to control them internally.
+ */
+ err = -EOPNOTSUPP;
+ break;
+ case IW_AUTH_DROP_UNENCRYPTED:
+ wrqu->param.value = ieee->drop_unencrypted;
+ break;
+ case IW_AUTH_WPA_ENABLED:
+ wrqu->param.value = ieee->wpa_enabled;
+ break;
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ wrqu->param.value = ieee->ieee802_1x;
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+ spin_unlock_irqrestore(&ieee->lock, flags);
+ return err;
+}
+
EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
EXPORT_SYMBOL(ieee80211_wx_get_scan);
EXPORT_SYMBOL(ieee80211_wx_set_encode);
EXPORT_SYMBOL(ieee80211_wx_get_encode);
+
+EXPORT_SYMBOL_GPL(ieee80211_wx_set_auth);
+EXPORT_SYMBOL_GPL(ieee80211_wx_get_auth);
diff --git a/net/ieee80211/softmac/Kconfig b/net/ieee80211/softmac/Kconfig
new file mode 100644
index 000000000000..2811651cb134
--- /dev/null
+++ b/net/ieee80211/softmac/Kconfig
@@ -0,0 +1,12 @@
+config IEEE80211_SOFTMAC
+ tristate "Software MAC add-on to the IEEE 802.11 networking stack"
+ depends on IEEE80211 && EXPERIMENTAL
+ select WIRELESS_EXT
+ select IEEE80211_CRYPT_WEP
+ ---help---
+ This option enables the hardware independent software MAC addon
+ for the IEEE 802.11 networking stack.
+
+config IEEE80211_SOFTMAC_DEBUG
+ bool "Enable full debugging output"
+ depends on IEEE80211_SOFTMAC
diff --git a/net/ieee80211/softmac/Makefile b/net/ieee80211/softmac/Makefile
new file mode 100644
index 000000000000..bfcb391bb2c7
--- /dev/null
+++ b/net/ieee80211/softmac/Makefile
@@ -0,0 +1,9 @@
+obj-$(CONFIG_IEEE80211_SOFTMAC) += ieee80211softmac.o
+ieee80211softmac-objs := \
+ ieee80211softmac_io.o \
+ ieee80211softmac_auth.o \
+ ieee80211softmac_module.o \
+ ieee80211softmac_scan.o \
+ ieee80211softmac_wx.o \
+ ieee80211softmac_assoc.o \
+ ieee80211softmac_event.o
diff --git a/net/ieee80211/softmac/ieee80211softmac_assoc.c b/net/ieee80211/softmac/ieee80211softmac_assoc.c
new file mode 100644
index 000000000000..47b35c160b41
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_assoc.c
@@ -0,0 +1,542 @@
+/*
+ * This file contains the softmac's association logic.
+ *
+ * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
+ * Joseph Jezak <josejx@gentoo.org>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ * Danny van Dyk <kugelfang@gentoo.org>
+ * Michael Buesch <mbuesch@freenet.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+
+#include "ieee80211softmac_priv.h"
+
+/*
+ * Overview
+ *
+ * Before you can associate, you have to authenticate.
+ *
+ */
+
+static void ieee80211softmac_associated(struct ieee80211softmac_device *mac,
+ struct ieee80211_assoc_response *resp, struct ieee80211softmac_network *net);
+
+/* Sends out an association request to the desired AP */
+static void
+ieee80211softmac_assoc(struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net)
+{
+ unsigned long flags;
+
+ /* Switch to correct channel for this network */
+ mac->set_channel(mac->dev, net->channel);
+
+ /* Send association request */
+ ieee80211softmac_send_mgt_frame(mac, net, IEEE80211_STYPE_ASSOC_REQ, 0);
+
+ spin_lock_irqsave(&mac->lock, flags);
+ dprintk(KERN_INFO PFX "sent association request!\n");
+
+ mac->associnfo.associated = 0; /* just to make sure */
+
+ /* Set a timer for timeout */
+ /* FIXME: make timeout configurable */
+ if (likely(mac->running))
+ queue_delayed_work(mac->wq, &mac->associnfo.timeout, 5 * HZ);
+ spin_unlock_irqrestore(&mac->lock, flags);
+}
+
+static void
+ieee80211softmac_assoc_adhoc(struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net)
+{
+ unsigned long flags;
+
+ dprintk(KERN_INFO PFX "Associating to ad-hoc bssid " MAC_FMT "\n", MAC_ARG(net->bssid)); /* P3 */
+
+ mac->set_channel(mac->dev, net->channel);
+
+ spin_lock_irqsave(&mac->lock, flags);
+ ieee80211softmac_associated(mac, NULL, net);
+ ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATED, net);
+ spin_unlock_irqrestore(&mac->lock, flags);
+}
+
+void
+ieee80211softmac_assoc_timeout(struct work_struct *work)
+{
+ struct ieee80211softmac_device *mac =
+ container_of(work, struct ieee80211softmac_device,
+ associnfo.timeout.work);
+
+ struct ieee80211softmac_network *n = NULL;
+
+ mutex_lock(&mac->associnfo.mutex);
+ /* we might race against ieee80211softmac_handle_assoc_response,
+ * so make sure only one of us does something */
+ if (!mac->associnfo.associating)
+ goto out;
+ mac->associnfo.associating = 0;
+ mac->associnfo.bssvalid = 0;
+ mac->associnfo.associated = 0;
+
+ n = ieee80211softmac_get_network_by_bssid(mac, mac->associnfo.bssid);
+
+ dprintk(KERN_INFO PFX "assoc request timed out!\n");
+ ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_TIMEOUT, n);
+out:
+ mutex_unlock(&mac->associnfo.mutex);
+}
+
+void
+ieee80211softmac_disassoc(struct ieee80211softmac_device *mac)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mac->lock, flags);
+ if (mac->associnfo.associating)
+ cancel_delayed_work(&mac->associnfo.timeout);
+
+ netif_carrier_off(mac->dev);
+
+ mac->associnfo.associated = 0;
+ mac->associnfo.bssvalid = 0;
+ mac->associnfo.associating = 0;
+ mac->associnfo.channel = -1;
+ ieee80211softmac_init_bss(mac);
+ ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_DISASSOCIATED, NULL);
+ spin_unlock_irqrestore(&mac->lock, flags);
+}
+
+/* Sends out a disassociation request to the desired AP */
+void
+ieee80211softmac_send_disassoc_req(struct ieee80211softmac_device *mac, u16 reason)
+{
+ struct ieee80211softmac_network *found;
+
+ if (mac->associnfo.bssvalid && mac->associnfo.associated) {
+ found = ieee80211softmac_get_network_by_bssid(mac, mac->associnfo.bssid);
+ if (found)
+ ieee80211softmac_send_mgt_frame(mac, found, IEEE80211_STYPE_DISASSOC, reason);
+ }
+
+ ieee80211softmac_disassoc(mac);
+}
+
+static inline int
+we_support_all_basic_rates(struct ieee80211softmac_device *mac, u8 *from, u8 from_len)
+{
+ int idx;
+ u8 rate;
+
+ for (idx = 0; idx < (from_len); idx++) {
+ rate = (from)[idx];
+ if (!(rate & IEEE80211_BASIC_RATE_MASK))
+ continue;
+ rate &= ~IEEE80211_BASIC_RATE_MASK;
+ if (!ieee80211softmac_ratesinfo_rate_supported(&mac->ratesinfo, rate))
+ return 0;
+ }
+ return 1;
+}
+
+static int
+network_matches_request(struct ieee80211softmac_device *mac, struct ieee80211_network *net)
+{
+ /* we cannot associate to networks whose name we don't know */
+ if (ieee80211_is_empty_essid(net->ssid, net->ssid_len))
+ return 0;
+ /* do not associate to a network whose BSSBasicRateSet we cannot support */
+ if (!we_support_all_basic_rates(mac, net->rates, net->rates_len))
+ return 0;
+ /* do we really need to check the ex rates? */
+ if (!we_support_all_basic_rates(mac, net->rates_ex, net->rates_ex_len))
+ return 0;
+ /* assume that users know what they're doing ...
+ * (note we don't let them select a net we're incompatible with) */
+ if (mac->associnfo.bssfixed) {
+ return !memcmp(mac->associnfo.bssid, net->bssid, ETH_ALEN);
+ }
+ /* if 'ANY' network requested, take any that doesn't have privacy enabled */
+ if (mac->associnfo.req_essid.len == 0
+ && !(net->capability & WLAN_CAPABILITY_PRIVACY))
+ return 1;
+ if (net->ssid_len != mac->associnfo.req_essid.len)
+ return 0;
+ if (!memcmp(net->ssid, mac->associnfo.req_essid.data, mac->associnfo.req_essid.len))
+ return 1;
+
+ return 0;
+}
+
+static void
+ieee80211softmac_assoc_notify_scan(struct net_device *dev, int event_type, void *context)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ ieee80211softmac_assoc_work(&mac->associnfo.work.work);
+}
+
+static void
+ieee80211softmac_assoc_notify_auth(struct net_device *dev, int event_type, void *context)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+
+ switch (event_type) {
+ case IEEE80211SOFTMAC_EVENT_AUTHENTICATED:
+ ieee80211softmac_assoc_work(&mac->associnfo.work.work);
+ break;
+ case IEEE80211SOFTMAC_EVENT_AUTH_FAILED:
+ case IEEE80211SOFTMAC_EVENT_AUTH_TIMEOUT:
+ ieee80211softmac_disassoc(mac);
+ break;
+ }
+}
+
+/* This function is called to handle userspace requests (asynchronously) */
+void
+ieee80211softmac_assoc_work(struct work_struct *work)
+{
+ struct ieee80211softmac_device *mac =
+ container_of(work, struct ieee80211softmac_device,
+ associnfo.work.work);
+
+ struct ieee80211softmac_network *found = NULL;
+ struct ieee80211_network *net = NULL, *best = NULL;
+ int bssvalid;
+ unsigned long flags;
+
+ mutex_lock(&mac->associnfo.mutex);
+
+ if (!mac->associnfo.associating)
+ goto out;
+
+ /* ieee80211_disassoc might clear this */
+ bssvalid = mac->associnfo.bssvalid;
+
+ /* meh */
+ if (mac->associnfo.associated)
+ ieee80211softmac_send_disassoc_req(mac, WLAN_REASON_DISASSOC_STA_HAS_LEFT);
+
+ /* try to find the requested network in our list, if we found one already */
+ if (bssvalid || mac->associnfo.bssfixed) {
+ found = ieee80211softmac_get_network_by_bssid_with_channel(mac, mac->associnfo.bssid, mac->associnfo.channel);
+ }
+
+ /* Search the ieee80211 networks for this network if we didn't find it by bssid,
+ * but only if we've scanned at least once (to get a better list of networks to
+ * select from). If we have not scanned before, the !found logic below will be
+ * invoked and will scan. */
+ if (!found && (mac->associnfo.scan_retry < IEEE80211SOFTMAC_ASSOC_SCAN_RETRY_LIMIT))
+ {
+ s8 rssi = -128; /* if I don't initialise, gcc emits an invalid warning
+ because it cannot follow the best pointer logic. */
+ spin_lock_irqsave(&mac->ieee->lock, flags);
+ list_for_each_entry(net, &mac->ieee->network_list, list) {
+ /* we're supposed to find the network with
+ * the best signal here, as we're asked to join
+ * any network with a specific ESSID, and many
+ * different ones could have that.
+ *
+ * I'll for now just go with the reported rssi.
+ *
+ * We also should take into account the rateset
+ * here to find the best BSSID to try.
+ */
+ if (network_matches_request(mac, net)) {
+ if (!best) {
+ best = net;
+ rssi = best->stats.rssi;
+ continue;
+ }
+ /* we already had a matching network, so
+ * compare their properties to get the
+ * better of the two ... (see above)
+ */
+ if (rssi < net->stats.rssi) {
+ best = net;
+ rssi = best->stats.rssi;
+ }
+ }
+ }
+ /* if we unlock here, we might get interrupted and the `best'
+ * pointer could go stale */
+ if (best) {
+ found = ieee80211softmac_create_network(mac, best);
+ /* if found is still NULL, then we got -ENOMEM somewhere */
+ if (found)
+ ieee80211softmac_add_network(mac, found);
+ }
+ spin_unlock_irqrestore(&mac->ieee->lock, flags);
+ }
+
+ if (!found) {
+ if (mac->associnfo.scan_retry > 0) {
+ mac->associnfo.scan_retry--;
+
+ /* We know of no such network. Let's scan.
+ * NB: this also happens if we had no memory to copy the network info...
+ * Maybe we can hope to have more memory after scanning finishes ;)
+ */
+ dprintk(KERN_INFO PFX "Associate: Scanning for networks first.\n");
+ ieee80211softmac_notify(mac->dev, IEEE80211SOFTMAC_EVENT_SCAN_FINISHED, ieee80211softmac_assoc_notify_scan, NULL);
+ if (ieee80211softmac_start_scan(mac))
+ dprintk(KERN_INFO PFX "Associate: failed to initiate scan. Is device up?\n");
+ goto out;
+ } else {
+ mac->associnfo.associating = 0;
+ mac->associnfo.associated = 0;
+
+ dprintk(KERN_INFO PFX "Unable to find matching network after scan!\n");
+ /* reset the retry counter for the next user request since we
+ * break out and don't reschedule ourselves after this point. */
+ mac->associnfo.scan_retry = IEEE80211SOFTMAC_ASSOC_SCAN_RETRY_LIMIT;
+ ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_NET_NOT_FOUND, NULL);
+ goto out;
+ }
+ }
+
+ /* reset the retry counter for the next user request since we
+ * now found a net and will try to associate to it, but not
+ * schedule this function again. */
+ mac->associnfo.scan_retry = IEEE80211SOFTMAC_ASSOC_SCAN_RETRY_LIMIT;
+ mac->associnfo.bssvalid = 1;
+ mac->associnfo.channel = found->channel;
+ memcpy(mac->associnfo.bssid, found->bssid, ETH_ALEN);
+ /* copy the ESSID for displaying it */
+ mac->associnfo.associate_essid.len = found->essid.len;
+ memcpy(mac->associnfo.associate_essid.data, found->essid.data, IW_ESSID_MAX_SIZE + 1);
+
+ /* we found a network! authenticate (if necessary) and associate to it. */
+ if (found->authenticating) {
+ dprintk(KERN_INFO PFX "Already requested authentication, waiting...\n");
+ if(!mac->associnfo.assoc_wait) {
+ mac->associnfo.assoc_wait = 1;
+ ieee80211softmac_notify_internal(mac, IEEE80211SOFTMAC_EVENT_ANY, found, ieee80211softmac_assoc_notify_auth, NULL, GFP_KERNEL);
+ }
+ goto out;
+ }
+ if (found->mode == IW_MODE_ADHOC) {
+ ieee80211softmac_assoc_adhoc(mac, found);
+ mac->associnfo.assoc_wait = 0;
+ goto out;
+ }
+ if (!found->authenticated && !found->authenticating) {
+ /* This relies on the fact that _auth_req only queues the work,
+ * otherwise adding the notification would be racy. */
+ if (!ieee80211softmac_auth_req(mac, found)) {
+ if(!mac->associnfo.assoc_wait) {
+ dprintk(KERN_INFO PFX "Cannot associate without being authenticated, requested authentication\n");
+ mac->associnfo.assoc_wait = 1;
+ ieee80211softmac_notify_internal(mac, IEEE80211SOFTMAC_EVENT_ANY, found, ieee80211softmac_assoc_notify_auth, NULL, GFP_KERNEL);
+ }
+ } else {
+ printkl(KERN_WARNING PFX "Not authenticated, but requesting authentication failed. Giving up to associate\n");
+ mac->associnfo.assoc_wait = 0;
+ ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_FAILED, found);
+ }
+ goto out;
+ }
+ /* finally! now we can start associating */
+ mac->associnfo.assoc_wait = 0;
+ ieee80211softmac_assoc(mac, found);
+
+out:
+ mutex_unlock(&mac->associnfo.mutex);
+}
+
+/* call this to do whatever is necessary when we're associated */
+static void
+ieee80211softmac_associated(struct ieee80211softmac_device *mac,
+ struct ieee80211_assoc_response * resp,
+ struct ieee80211softmac_network *net)
+{
+ u16 cap = 0;
+ u8 erp_value = net->erp_value;
+
+ if (resp != NULL)
+ cap = le16_to_cpu(resp->capability);
+ mac->associnfo.associating = 0;
+ mac->bssinfo.supported_rates = net->supported_rates;
+ ieee80211softmac_recalc_txrates(mac);
+
+ mac->associnfo.associated = 1;
+
+ if (resp != NULL)
+ mac->associnfo.short_preamble_available =
+ (cap & WLAN_CAPABILITY_SHORT_PREAMBLE) != 0;
+ ieee80211softmac_process_erp(mac, erp_value);
+
+ if (mac->set_bssid_filter)
+ mac->set_bssid_filter(mac->dev, net->bssid);
+ memcpy(mac->ieee->bssid, net->bssid, ETH_ALEN);
+ netif_carrier_on(mac->dev);
+
+ if (resp != NULL)
+ mac->association_id = le16_to_cpup(&resp->aid);
+}
+
+/* received frame handling functions */
+int
+ieee80211softmac_handle_assoc_response(struct net_device * dev,
+ struct ieee80211_assoc_response * resp,
+ struct ieee80211_network * _ieee80211_network)
+{
+ /* NOTE: the network parameter has to be mostly ignored by
+ * this code because it is the ieee80211's pointer
+ * to the struct, not ours (we made a copy)
+ */
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ u16 status = le16_to_cpup(&resp->status);
+ struct ieee80211softmac_network *network = NULL;
+ unsigned long flags;
+
+ if (unlikely(!mac->running))
+ return -ENODEV;
+
+ spin_lock_irqsave(&mac->lock, flags);
+
+ if (!mac->associnfo.associating) {
+ /* we race against the timeout function, so make sure
+ * only one of us can do work */
+ spin_unlock_irqrestore(&mac->lock, flags);
+ return 0;
+ }
+ network = ieee80211softmac_get_network_by_bssid_locked(mac, resp->header.addr3);
+
+ /* someone sending us things without us knowing him? Ignore. */
+ if (!network) {
+ dprintk(KERN_INFO PFX "Received unrequested assocation response from " MAC_FMT "\n", MAC_ARG(resp->header.addr3));
+ spin_unlock_irqrestore(&mac->lock, flags);
+ return 0;
+ }
+
+ /* now that we know it was for us, we can cancel the timeout */
+ cancel_delayed_work(&mac->associnfo.timeout);
+
+ /* if the association response included an ERP IE, update our saved
+ * copy */
+ if (_ieee80211_network->flags & NETWORK_HAS_ERP_VALUE)
+ network->erp_value = _ieee80211_network->erp_value;
+
+ switch (status) {
+ case 0:
+ dprintk(KERN_INFO PFX "associated!\n");
+ ieee80211softmac_associated(mac, resp, network);
+ ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATED, network);
+ break;
+ case WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH:
+ if (!network->auth_desynced_once) {
+ /* there seem to be a few rare cases where our view of
+ * the world is obscured, or buggy APs that don't DEAUTH
+ * us properly. So we handle that, but allow it only once.
+ */
+ printkl(KERN_INFO PFX "We were not authenticated during association, retrying...\n");
+ network->authenticated = 0;
+ /* we don't want to do this more than once ... */
+ network->auth_desynced_once = 1;
+ queue_delayed_work(mac->wq, &mac->associnfo.work, 0);
+ break;
+ }
+ default:
+ dprintk(KERN_INFO PFX "associating failed (reason: 0x%x)!\n", status);
+ mac->associnfo.associating = 0;
+ mac->associnfo.bssvalid = 0;
+ mac->associnfo.associated = 0;
+ ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_FAILED, network);
+ }
+
+ spin_unlock_irqrestore(&mac->lock, flags);
+ return 0;
+}
+
+void
+ieee80211softmac_try_reassoc(struct ieee80211softmac_device *mac)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mac->lock, flags);
+ mac->associnfo.associating = 1;
+ queue_delayed_work(mac->wq, &mac->associnfo.work, 0);
+ spin_unlock_irqrestore(&mac->lock, flags);
+}
+
+int
+ieee80211softmac_handle_disassoc(struct net_device * dev,
+ struct ieee80211_disassoc *disassoc)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ struct ieee80211softmac_network *network = NULL;
+ struct ieee80211_network *net = NULL;
+ struct ieee80211_network *nettmp = NULL;
+ unsigned long flags;
+
+ if (unlikely(!mac->running))
+ return -ENODEV;
+
+ if (memcmp(disassoc->header.addr2, mac->associnfo.bssid, ETH_ALEN))
+ return 0;
+
+ if (memcmp(disassoc->header.addr1, mac->dev->dev_addr, ETH_ALEN))
+ return 0;
+
+ dprintk(KERN_INFO PFX "got disassoc frame\n");
+
+ /* Delete this network, so a rescan is triggered when again trying to
+ * reassociate. This is necessary because of channel switches and we
+ * don't want logic to find this channel again. */
+ network = ieee80211softmac_get_network_by_bssid_with_channel( mac, mac->associnfo.bssid, mac->associnfo.channel );
+ ieee80211softmac_disassoc( mac ); /* associnfo no longer valid */
+
+ spin_lock_irqsave(&mac->lock, flags);
+ ieee80211softmac_del_network_locked(mac, network);
+ if(network->challenge != NULL)
+ kfree(network->challenge);
+ kfree(network);
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ /* delete all ieee802.11 networks */
+ spin_lock_irqsave(&mac->ieee->lock, flags);
+ list_for_each_entry_safe(net, nettmp, &mac->ieee->network_list, list) {
+ struct ieee80211_network* net80211 = list_entry( &net->list, struct ieee80211_network, list);
+ if( !memcmp(net80211->bssid, mac->associnfo.bssid, ETH_ALEN))
+ list_del(&net80211->list);
+ }
+ spin_unlock_irqrestore(&mac->ieee->lock, flags);
+
+ ieee80211softmac_try_reassoc(mac);
+
+ return 0;
+}
+
+int
+ieee80211softmac_handle_reassoc_req(struct net_device * dev,
+ struct ieee80211_reassoc_request * resp)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ struct ieee80211softmac_network *network;
+
+ if (unlikely(!mac->running))
+ return -ENODEV;
+
+ network = ieee80211softmac_get_network_by_bssid(mac, resp->header.addr3);
+ if (!network) {
+ dprintkl(KERN_INFO PFX "reassoc request from unknown network\n");
+ return 0;
+ }
+ queue_delayed_work(mac->wq, &mac->associnfo.work, 0);
+
+ return 0;
+}
diff --git a/net/ieee80211/softmac/ieee80211softmac_auth.c b/net/ieee80211/softmac/ieee80211softmac_auth.c
new file mode 100644
index 000000000000..3441ca53f533
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_auth.c
@@ -0,0 +1,413 @@
+/*
+ * This file contains the softmac's authentication logic.
+ *
+ * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
+ * Joseph Jezak <josejx@gentoo.org>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ * Danny van Dyk <kugelfang@gentoo.org>
+ * Michael Buesch <mbuesch@freenet.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+
+#include "ieee80211softmac_priv.h"
+
+
+static void ieee80211softmac_auth_queue(struct work_struct *work);
+/* Queues an auth request to the desired AP */
+int
+ieee80211softmac_auth_req(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *net)
+{
+ struct ieee80211softmac_auth_queue_item *auth;
+ unsigned long flags;
+
+ if (net->authenticating || net->authenticated)
+ return 0;
+ net->authenticating = 1;
+
+ /* Add the network if it's not already added */
+ ieee80211softmac_add_network(mac, net);
+
+ dprintk(KERN_NOTICE PFX "Queueing Authentication Request to "MAC_FMT"\n", MAC_ARG(net->bssid));
+ /* Queue the auth request */
+ auth = (struct ieee80211softmac_auth_queue_item *)
+ kmalloc(sizeof(struct ieee80211softmac_auth_queue_item), GFP_KERNEL);
+ if(auth == NULL)
+ return -ENOMEM;
+
+ auth->net = net;
+ auth->mac = mac;
+ auth->retry = IEEE80211SOFTMAC_AUTH_RETRY_LIMIT;
+ auth->state = IEEE80211SOFTMAC_AUTH_OPEN_REQUEST;
+ INIT_DELAYED_WORK(&auth->work, ieee80211softmac_auth_queue);
+
+ /* Lock (for list) */
+ spin_lock_irqsave(&mac->lock, flags);
+
+ /* add to list */
+ list_add_tail(&auth->list, &mac->auth_queue);
+ queue_delayed_work(mac->wq, &auth->work, 0);
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ return 0;
+}
+
+
+/* Sends an auth request to the desired AP and handles timeouts */
+static void
+ieee80211softmac_auth_queue(struct work_struct *work)
+{
+ struct ieee80211softmac_device *mac;
+ struct ieee80211softmac_auth_queue_item *auth;
+ struct ieee80211softmac_network *net;
+ unsigned long flags;
+
+ auth = container_of(work, struct ieee80211softmac_auth_queue_item,
+ work.work);
+
+ net = auth->net;
+ mac = auth->mac;
+
+ if(auth->retry > 0) {
+ /* Switch to correct channel for this network */
+ mac->set_channel(mac->dev, net->channel);
+
+ /* Lock and set flags */
+ spin_lock_irqsave(&mac->lock, flags);
+ if (unlikely(!mac->running)) {
+ /* Prevent reschedule on workqueue flush */
+ spin_unlock_irqrestore(&mac->lock, flags);
+ return;
+ }
+ net->authenticated = 0;
+ /* add a timeout call so we eventually give up waiting for an auth reply */
+ queue_delayed_work(mac->wq, &auth->work, IEEE80211SOFTMAC_AUTH_TIMEOUT);
+ auth->retry--;
+ spin_unlock_irqrestore(&mac->lock, flags);
+ if (ieee80211softmac_send_mgt_frame(mac, auth->net, IEEE80211_STYPE_AUTH, auth->state))
+ dprintk(KERN_NOTICE PFX "Sending Authentication Request to "MAC_FMT" failed (this shouldn't happen, wait for the timeout).\n", MAC_ARG(net->bssid));
+ else
+ dprintk(KERN_NOTICE PFX "Sent Authentication Request to "MAC_FMT".\n", MAC_ARG(net->bssid));
+ return;
+ }
+
+ printkl(KERN_WARNING PFX "Authentication timed out with "MAC_FMT"\n", MAC_ARG(net->bssid));
+ /* Remove this item from the queue */
+ spin_lock_irqsave(&mac->lock, flags);
+ net->authenticating = 0;
+ ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_AUTH_TIMEOUT, net);
+ cancel_delayed_work(&auth->work); /* just to make sure... */
+ list_del(&auth->list);
+ spin_unlock_irqrestore(&mac->lock, flags);
+ /* Free it */
+ kfree(auth);
+}
+
+/* Sends a response to an auth challenge (for shared key auth). */
+static void
+ieee80211softmac_auth_challenge_response(struct work_struct *work)
+{
+ struct ieee80211softmac_auth_queue_item *aq =
+ container_of(work, struct ieee80211softmac_auth_queue_item,
+ work.work);
+
+
+ /* Send our response */
+ ieee80211softmac_send_mgt_frame(aq->mac, aq->net, IEEE80211_STYPE_AUTH, aq->state);
+}
+
+/* Handle the auth response from the AP
+ * This should be registered with ieee80211 as handle_auth
+ */
+int
+ieee80211softmac_auth_resp(struct net_device *dev, struct ieee80211_auth *auth)
+{
+
+ struct list_head *list_ptr;
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ struct ieee80211softmac_auth_queue_item *aq = NULL;
+ struct ieee80211softmac_network *net = NULL;
+ unsigned long flags;
+ u8 * data;
+
+ if (unlikely(!mac->running))
+ return -ENODEV;
+
+ /* Find correct auth queue item */
+ spin_lock_irqsave(&mac->lock, flags);
+ list_for_each(list_ptr, &mac->auth_queue) {
+ aq = list_entry(list_ptr, struct ieee80211softmac_auth_queue_item, list);
+ net = aq->net;
+ if (!memcmp(net->bssid, auth->header.addr2, ETH_ALEN))
+ break;
+ else
+ aq = NULL;
+ }
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ /* Make sure that we've got an auth queue item for this request */
+ if(aq == NULL)
+ {
+ dprintkl(KERN_DEBUG PFX "Authentication response received from "MAC_FMT" but no queue item exists.\n", MAC_ARG(auth->header.addr2));
+ /* Error #? */
+ return -1;
+ }
+
+ /* Check for out of order authentication */
+ if(!net->authenticating)
+ {
+ dprintkl(KERN_DEBUG PFX "Authentication response received from "MAC_FMT" but did not request authentication.\n",MAC_ARG(auth->header.addr2));
+ return -1;
+ }
+
+ /* Parse the auth packet */
+ switch(auth->algorithm) {
+ case WLAN_AUTH_OPEN:
+ /* Check the status code of the response */
+
+ switch(auth->status) {
+ case WLAN_STATUS_SUCCESS:
+ /* Update the status to Authenticated */
+ spin_lock_irqsave(&mac->lock, flags);
+ net->authenticating = 0;
+ net->authenticated = 1;
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ /* Send event */
+ dprintkl(KERN_NOTICE PFX "Open Authentication completed with "MAC_FMT"\n", MAC_ARG(net->bssid));
+ ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_AUTHENTICATED, net);
+ break;
+ default:
+ /* Lock and reset flags */
+ spin_lock_irqsave(&mac->lock, flags);
+ net->authenticated = 0;
+ net->authenticating = 0;
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ dprintkl(KERN_NOTICE PFX "Open Authentication with "MAC_FMT" failed, error code: %i\n",
+ MAC_ARG(net->bssid), le16_to_cpup(&auth->status));
+ /* Count the error? */
+ break;
+ }
+ goto free_aq;
+ break;
+ case WLAN_AUTH_SHARED_KEY:
+ /* Figure out where we are in the process */
+ switch(auth->transaction) {
+ case IEEE80211SOFTMAC_AUTH_SHARED_CHALLENGE:
+ /* Check to make sure we have a challenge IE */
+ data = (u8 *)auth->info_element;
+ if (*data++ != MFIE_TYPE_CHALLENGE) {
+ dprintkl(KERN_NOTICE PFX "Shared Key Authentication failed due to a missing challenge.\n");
+ break;
+ }
+ /* Save the challenge */
+ spin_lock_irqsave(&mac->lock, flags);
+ net->challenge_len = *data++;
+ if (net->challenge_len > WLAN_AUTH_CHALLENGE_LEN)
+ net->challenge_len = WLAN_AUTH_CHALLENGE_LEN;
+ kfree(net->challenge);
+
+
+ net->challenge = kmalloc(net->challenge_len, GFP_ATOMIC);
+ if (net->challenge == NULL) {
+ printkl(KERN_NOTICE PFX "Shared Key "
+ "Authentication failed due to "
+ "memory shortage.\n");
+ spin_unlock_irqrestore(&mac->lock, flags);
+ break;
+ }
+ memcpy(net->challenge, data, net->challenge_len);
+ aq->state = IEEE80211SOFTMAC_AUTH_SHARED_RESPONSE;
+
+ /* We reuse the work struct from the auth request here.
+ * It is safe to do so as each one is per-request, and
+ * at this point (dealing with authentication response)
+ * we have obviously already sent the initial auth
+ * request. */
+ cancel_delayed_work(&aq->work);
+ INIT_DELAYED_WORK(&aq->work, &ieee80211softmac_auth_challenge_response);
+ queue_delayed_work(mac->wq, &aq->work, 0);
+
+ spin_unlock_irqrestore(&mac->lock, flags);
+ return 0;
+ case IEEE80211SOFTMAC_AUTH_SHARED_PASS:
+ kfree(net->challenge);
+ net->challenge = NULL;
+ net->challenge_len = 0;
+ /* Check the status code of the response */
+ switch(auth->status) {
+ case WLAN_STATUS_SUCCESS:
+ /* Update the status to Authenticated */
+ spin_lock_irqsave(&mac->lock, flags);
+ net->authenticating = 0;
+ net->authenticated = 1;
+ spin_unlock_irqrestore(&mac->lock, flags);
+ dprintkl(KERN_NOTICE PFX "Shared Key Authentication completed with "MAC_FMT"\n",
+ MAC_ARG(net->bssid));
+ ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_AUTHENTICATED, net);
+ break;
+ default:
+ dprintkl(KERN_NOTICE PFX "Shared Key Authentication with "MAC_FMT" failed, error code: %i\n",
+ MAC_ARG(net->bssid), le16_to_cpup(&auth->status));
+ /* Lock and reset flags */
+ spin_lock_irqsave(&mac->lock, flags);
+ net->authenticating = 0;
+ net->authenticated = 0;
+ spin_unlock_irqrestore(&mac->lock, flags);
+ /* Count the error? */
+ break;
+ }
+ goto free_aq;
+ break;
+ default:
+ printkl(KERN_WARNING PFX "Unhandled Authentication Step: %i\n", auth->transaction);
+ break;
+ }
+ goto free_aq;
+ break;
+ default:
+ /* ERROR */
+ goto free_aq;
+ break;
+ }
+ return 0;
+free_aq:
+ /* Cancel the timeout */
+ spin_lock_irqsave(&mac->lock, flags);
+ cancel_delayed_work(&aq->work);
+ /* Remove this item from the queue */
+ list_del(&aq->list);
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ /* Free it */
+ kfree(aq);
+ return 0;
+}
+
+/*
+ * Handle deauthorization
+ */
+static void
+ieee80211softmac_deauth_from_net(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *net)
+{
+ struct ieee80211softmac_auth_queue_item *aq = NULL;
+ struct list_head *list_ptr;
+ unsigned long flags;
+
+ /* deauthentication implies disassociation */
+ ieee80211softmac_disassoc(mac);
+
+ /* Lock and reset status flags */
+ spin_lock_irqsave(&mac->lock, flags);
+ net->authenticating = 0;
+ net->authenticated = 0;
+
+ /* Find correct auth queue item, if it exists */
+ list_for_each(list_ptr, &mac->auth_queue) {
+ aq = list_entry(list_ptr, struct ieee80211softmac_auth_queue_item, list);
+ if (!memcmp(net->bssid, aq->net->bssid, ETH_ALEN))
+ break;
+ else
+ aq = NULL;
+ }
+
+ /* Cancel pending work */
+ if(aq != NULL)
+ /* Not entirely safe? What about running work? */
+ cancel_delayed_work(&aq->work);
+
+ /* Free our network ref */
+ ieee80211softmac_del_network_locked(mac, net);
+ if(net->challenge != NULL)
+ kfree(net->challenge);
+ kfree(net);
+
+ /* can't transmit data right now... */
+ netif_carrier_off(mac->dev);
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ ieee80211softmac_try_reassoc(mac);
+}
+
+/*
+ * Sends a deauth request to the desired AP
+ */
+int
+ieee80211softmac_deauth_req(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *net, int reason)
+{
+ int ret;
+
+ /* Make sure the network is authenticated */
+ if (!net->authenticated)
+ {
+ dprintkl(KERN_DEBUG PFX "Can't send deauthentication packet, network is not authenticated.\n");
+ /* Error okay? */
+ return -EPERM;
+ }
+
+ /* Send the de-auth packet */
+ if((ret = ieee80211softmac_send_mgt_frame(mac, net, IEEE80211_STYPE_DEAUTH, reason)))
+ return ret;
+
+ ieee80211softmac_deauth_from_net(mac, net);
+ return 0;
+}
+
+/*
+ * This should be registered with ieee80211 as handle_deauth
+ */
+int
+ieee80211softmac_deauth_resp(struct net_device *dev, struct ieee80211_deauth *deauth)
+{
+
+ struct ieee80211softmac_network *net = NULL;
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+
+ if (unlikely(!mac->running))
+ return -ENODEV;
+
+ if (!deauth) {
+ dprintk("deauth without deauth packet. eek!\n");
+ return 0;
+ }
+
+ net = ieee80211softmac_get_network_by_bssid(mac, deauth->header.addr2);
+
+ if (net == NULL) {
+ dprintkl(KERN_DEBUG PFX "Received deauthentication packet from "MAC_FMT", but that network is unknown.\n",
+ MAC_ARG(deauth->header.addr2));
+ return 0;
+ }
+
+ /* Make sure the network is authenticated */
+ if(!net->authenticated)
+ {
+ dprintkl(KERN_DEBUG PFX "Can't perform deauthentication, network is not authenticated.\n");
+ /* Error okay? */
+ return -EPERM;
+ }
+
+ ieee80211softmac_deauth_from_net(mac, net);
+
+ /* let's try to re-associate */
+ queue_delayed_work(mac->wq, &mac->associnfo.work, 0);
+ return 0;
+}
diff --git a/net/ieee80211/softmac/ieee80211softmac_event.c b/net/ieee80211/softmac/ieee80211softmac_event.c
new file mode 100644
index 000000000000..f1d1790e179c
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_event.c
@@ -0,0 +1,190 @@
+/*
+ * Event system
+ * Also see comments in public header file and longer explanation below.
+ *
+ * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
+ * Joseph Jezak <josejx@gentoo.org>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ * Danny van Dyk <kugelfang@gentoo.org>
+ * Michael Buesch <mbuesch@freenet.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+
+#include "ieee80211softmac_priv.h"
+
+/*
+ * Each event has associated to it
+ * - an event type (see constants in public header)
+ * - an event context (see below)
+ * - the function to be called
+ * - a context (extra parameter to call the function with)
+ * - and the softmac struct
+ *
+ * The event context is private and can only be used from
+ * within this module. Its meaning varies with the event
+ * type:
+ * SCAN_FINISHED,
+ * DISASSOCIATED: NULL
+ * ASSOCIATED,
+ * ASSOCIATE_FAILED,
+ * ASSOCIATE_TIMEOUT,
+ * AUTHENTICATED,
+ * AUTH_FAILED,
+ * AUTH_TIMEOUT: a pointer to the network struct
+ * ...
+ * Code within this module can use the event context to be only
+ * called when the event is true for that specific context
+ * as per above table.
+ * If the event context is NULL, then the notification is always called,
+ * regardless of the event context. The event context is not passed to
+ * the callback, it is assumed that the context suffices.
+ *
+ * You can also use the event context only by setting the event type
+ * to -1 (private use only), in which case you'll be notified
+ * whenever the event context matches.
+ */
+
+static char *event_descriptions[IEEE80211SOFTMAC_EVENT_LAST+1] = {
+ NULL, /* scan finished */
+ NULL, /* associated */
+ "associating failed",
+ "associating timed out",
+ "authenticated",
+ "authenticating failed",
+ "authenticating timed out",
+ "associating failed because no suitable network was found",
+ NULL, /* disassociated */
+};
+
+
+static void
+ieee80211softmac_notify_callback(struct work_struct *work)
+{
+ struct ieee80211softmac_event *pevent =
+ container_of(work, struct ieee80211softmac_event, work.work);
+ struct ieee80211softmac_event event = *pevent;
+ kfree(pevent);
+
+ event.fun(event.mac->dev, event.event_type, event.context);
+}
+
+
+int
+ieee80211softmac_notify_internal(struct ieee80211softmac_device *mac,
+ int event, void *event_context, notify_function_ptr fun, void *context, gfp_t gfp_mask)
+{
+ struct ieee80211softmac_event *eventptr;
+ unsigned long flags;
+
+ if (event < -1 || event > IEEE80211SOFTMAC_EVENT_LAST)
+ return -ENOSYS;
+
+ if (!fun)
+ return -EINVAL;
+
+ eventptr = kmalloc(sizeof(struct ieee80211softmac_event), gfp_mask);
+ if (!eventptr)
+ return -ENOMEM;
+
+ eventptr->event_type = event;
+ INIT_DELAYED_WORK(&eventptr->work, ieee80211softmac_notify_callback);
+ eventptr->fun = fun;
+ eventptr->context = context;
+ eventptr->mac = mac;
+ eventptr->event_context = event_context;
+
+ spin_lock_irqsave(&mac->lock, flags);
+ list_add(&eventptr->list, &mac->events);
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ return 0;
+}
+
+int
+ieee80211softmac_notify_gfp(struct net_device *dev,
+ int event, notify_function_ptr fun, void *context, gfp_t gfp_mask)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+
+ if (event < 0 || event > IEEE80211SOFTMAC_EVENT_LAST)
+ return -ENOSYS;
+
+ return ieee80211softmac_notify_internal(mac, event, NULL, fun, context, gfp_mask);
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_notify_gfp);
+
+/* private -- calling all callbacks that were specified */
+void
+ieee80211softmac_call_events_locked(struct ieee80211softmac_device *mac, int event, void *event_ctx)
+{
+ struct ieee80211softmac_event *eventptr, *tmp;
+ struct ieee80211softmac_network *network;
+
+ if (event >= 0) {
+ union iwreq_data wrqu;
+ int we_event;
+ char *msg = NULL;
+
+ memset(&wrqu, '\0', sizeof (union iwreq_data));
+
+ switch(event) {
+ case IEEE80211SOFTMAC_EVENT_ASSOCIATED:
+ network = (struct ieee80211softmac_network *)event_ctx;
+ memcpy(wrqu.ap_addr.sa_data, &network->bssid[0], ETH_ALEN);
+ /* fall through */
+ case IEEE80211SOFTMAC_EVENT_DISASSOCIATED:
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+ we_event = SIOCGIWAP;
+ break;
+ case IEEE80211SOFTMAC_EVENT_SCAN_FINISHED:
+ we_event = SIOCGIWSCAN;
+ break;
+ default:
+ msg = event_descriptions[event];
+ if (!msg)
+ msg = "SOFTMAC EVENT BUG";
+ wrqu.data.length = strlen(msg);
+ we_event = IWEVCUSTOM;
+ break;
+ }
+ wireless_send_event(mac->dev, we_event, &wrqu, msg);
+ }
+
+ if (!list_empty(&mac->events))
+ list_for_each_entry_safe(eventptr, tmp, &mac->events, list) {
+ if ((eventptr->event_type == event || eventptr->event_type == -1)
+ && (eventptr->event_context == NULL || eventptr->event_context == event_ctx)) {
+ list_del(&eventptr->list);
+ /* User may have subscribed to ANY event, so
+ * we tell them which event triggered it. */
+ eventptr->event_type = event;
+ queue_delayed_work(mac->wq, &eventptr->work, 0);
+ }
+ }
+}
+
+void
+ieee80211softmac_call_events(struct ieee80211softmac_device *mac, int event, void *event_ctx)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mac->lock, flags);
+ ieee80211softmac_call_events_locked(mac, event, event_ctx);
+
+ spin_unlock_irqrestore(&mac->lock, flags);
+}
diff --git a/net/ieee80211/softmac/ieee80211softmac_io.c b/net/ieee80211/softmac/ieee80211softmac_io.c
new file mode 100644
index 000000000000..19a690046c72
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_io.c
@@ -0,0 +1,488 @@
+/*
+ * Some parts based on code from net80211
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "ieee80211softmac_priv.h"
+
+/* Helper functions for inserting data into the frames */
+
+/*
+ * Adds an ESSID element to the frame
+ *
+ */
+static u8 *
+ieee80211softmac_add_essid(u8 *dst, struct ieee80211softmac_essid *essid)
+{
+ if (essid) {
+ *dst++ = MFIE_TYPE_SSID;
+ *dst++ = essid->len;
+ memcpy(dst, essid->data, essid->len);
+ return dst+essid->len;
+ } else {
+ *dst++ = MFIE_TYPE_SSID;
+ *dst++ = 0;
+ return dst;
+ }
+}
+
+/* Adds Supported Rates and if required Extended Rates Information Element
+ * to the frame, ASSUMES WE HAVE A SORTED LIST OF RATES */
+static u8 *
+ieee80211softmac_frame_add_rates(u8 *dst, const struct ieee80211softmac_ratesinfo *r)
+{
+ int cck_len, ofdm_len;
+ *dst++ = MFIE_TYPE_RATES;
+
+ for(cck_len=0; ieee80211_is_cck_rate(r->rates[cck_len]) && (cck_len < r->count);cck_len++);
+
+ if(cck_len > IEEE80211SOFTMAC_MAX_RATES_LEN)
+ cck_len = IEEE80211SOFTMAC_MAX_RATES_LEN;
+ *dst++ = cck_len;
+ memcpy(dst, r->rates, cck_len);
+ dst += cck_len;
+
+ if(cck_len < r->count){
+ for (ofdm_len=0; ieee80211_is_ofdm_rate(r->rates[ofdm_len + cck_len]) && (ofdm_len + cck_len < r->count); ofdm_len++);
+ if (ofdm_len > 0) {
+ if (ofdm_len > IEEE80211SOFTMAC_MAX_EX_RATES_LEN)
+ ofdm_len = IEEE80211SOFTMAC_MAX_EX_RATES_LEN;
+ *dst++ = MFIE_TYPE_RATES_EX;
+ *dst++ = ofdm_len;
+ memcpy(dst, r->rates + cck_len, ofdm_len);
+ dst += ofdm_len;
+ }
+ }
+ return dst;
+}
+
+/* Allocate a management frame */
+static u8 *
+ieee80211softmac_alloc_mgt(u32 size)
+{
+ u8 * data;
+
+ /* Add the header and FCS to the size */
+ size = size + IEEE80211_3ADDR_LEN;
+ if(size > IEEE80211_DATA_LEN)
+ return NULL;
+ /* Allocate the frame */
+ data = kzalloc(size, GFP_ATOMIC);
+ return data;
+}
+
+/*
+ * Add a 2 Address Header
+ */
+static void
+ieee80211softmac_hdr_2addr(struct ieee80211softmac_device *mac,
+ struct ieee80211_hdr_2addr *header, u32 type, u8 *dest)
+{
+ /* Fill in the frame control flags */
+ header->frame_ctl = cpu_to_le16(type);
+ /* Control packets always have WEP turned off */
+ if(type > IEEE80211_STYPE_CFENDACK && type < IEEE80211_STYPE_PSPOLL)
+ header->frame_ctl |= mac->ieee->sec.level ? cpu_to_le16(IEEE80211_FCTL_PROTECTED) : 0;
+
+ /* Fill in the duration */
+ header->duration_id = 0;
+ /* FIXME: How do I find this?
+ * calculate. But most drivers just fill in 0 (except if it's a station id of course) */
+
+ /* Fill in the Destination Address */
+ if(dest == NULL)
+ memset(header->addr1, 0xFF, ETH_ALEN);
+ else
+ memcpy(header->addr1, dest, ETH_ALEN);
+ /* Fill in the Source Address */
+ memcpy(header->addr2, mac->ieee->dev->dev_addr, ETH_ALEN);
+
+}
+
+
+/* Add a 3 Address Header */
+static void
+ieee80211softmac_hdr_3addr(struct ieee80211softmac_device *mac,
+ struct ieee80211_hdr_3addr *header, u32 type, u8 *dest, u8 *bssid)
+{
+ /* This is common with 2addr, so use that instead */
+ ieee80211softmac_hdr_2addr(mac, (struct ieee80211_hdr_2addr *)header, type, dest);
+
+ /* Fill in the BSS ID */
+ if(bssid == NULL)
+ memset(header->addr3, 0xFF, ETH_ALEN);
+ else
+ memcpy(header->addr3, bssid, ETH_ALEN);
+
+ /* Fill in the sequence # */
+ /* FIXME: I need to add this to the softmac struct
+ * shouldn't the sequence number be in ieee80211? */
+}
+
+static u16
+ieee80211softmac_capabilities(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *net)
+{
+ u16 capability = 0;
+
+ /* ESS and IBSS bits are set according to the current mode */
+ switch (mac->ieee->iw_mode) {
+ case IW_MODE_INFRA:
+ capability = cpu_to_le16(WLAN_CAPABILITY_ESS);
+ break;
+ case IW_MODE_ADHOC:
+ capability = cpu_to_le16(WLAN_CAPABILITY_IBSS);
+ break;
+ case IW_MODE_AUTO:
+ capability = net->capabilities &
+ (WLAN_CAPABILITY_ESS|WLAN_CAPABILITY_IBSS);
+ break;
+ default:
+ /* bleh. we don't ever go to these modes */
+ printk(KERN_ERR PFX "invalid iw_mode!\n");
+ break;
+ }
+
+ /* CF Pollable / CF Poll Request */
+ /* Needs to be implemented, for now, the 0's == not supported */
+
+ /* Privacy Bit */
+ capability |= mac->ieee->sec.level ?
+ cpu_to_le16(WLAN_CAPABILITY_PRIVACY) : 0;
+
+ /* Short Preamble */
+ /* Always supported: we probably won't ever be powering devices which
+ * dont support this... */
+ capability |= WLAN_CAPABILITY_SHORT_PREAMBLE;
+
+ /* PBCC */
+ /* Not widely used */
+
+ /* Channel Agility */
+ /* Not widely used */
+
+ /* Short Slot */
+ /* Will be implemented later */
+
+ /* DSSS-OFDM */
+ /* Not widely used */
+
+ return capability;
+}
+
+/*****************************************************************************
+ * Create Management packets
+ *****************************************************************************/
+
+/* Creates an association request packet */
+static u32
+ieee80211softmac_assoc_req(struct ieee80211_assoc_request **pkt,
+ struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net)
+{
+ u8 *data;
+ (*pkt) = (struct ieee80211_assoc_request *)ieee80211softmac_alloc_mgt(
+ 2 + /* Capability Info */
+ 2 + /* Listen Interval */
+ /* SSID IE */
+ 1 + 1 + IW_ESSID_MAX_SIZE +
+ /* Rates IE */
+ 1 + 1 + IEEE80211SOFTMAC_MAX_RATES_LEN +
+ /* Extended Rates IE */
+ 1 + 1 + IEEE80211SOFTMAC_MAX_EX_RATES_LEN +
+ /* WPA IE if present */
+ mac->wpa.IElen
+ /* Other IE's? Optional?
+ * Yeah, probably need an extra IE parameter -- lots of vendors like to
+ * fill in their own IEs */
+ );
+ if (unlikely((*pkt) == NULL))
+ return 0;
+ ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_ASSOC_REQ, net->bssid, net->bssid);
+
+ /* Fill in the capabilities */
+ (*pkt)->capability = ieee80211softmac_capabilities(mac, net);
+
+ /* Fill in Listen Interval (?) */
+ (*pkt)->listen_interval = cpu_to_le16(10);
+
+ data = (u8 *)(*pkt)->info_element;
+ /* Add SSID */
+ data = ieee80211softmac_add_essid(data, &net->essid);
+ /* Add Rates */
+ data = ieee80211softmac_frame_add_rates(data, &mac->ratesinfo);
+ /* Add WPA IE */
+ if (mac->wpa.IElen && mac->wpa.IE) {
+ memcpy(data, mac->wpa.IE, mac->wpa.IElen);
+ data += mac->wpa.IElen;
+ }
+ /* Return the number of used bytes */
+ return (data - (u8*)(*pkt));
+}
+
+/* Create a reassociation request packet */
+static u32
+ieee80211softmac_reassoc_req(struct ieee80211_reassoc_request **pkt,
+ struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net)
+{
+ u8 *data;
+ (*pkt) = (struct ieee80211_reassoc_request *)ieee80211softmac_alloc_mgt(
+ 2 + /* Capability Info */
+ 2 + /* Listen Interval */
+ ETH_ALEN + /* AP MAC */
+ /* SSID IE */
+ 1 + 1 + IW_ESSID_MAX_SIZE +
+ /* Rates IE */
+ 1 + 1 + IEEE80211SOFTMAC_MAX_RATES_LEN +
+ /* Extended Rates IE */
+ 1 + 1 + IEEE80211SOFTMAC_MAX_EX_RATES_LEN
+ /* Other IE's? */
+ );
+ if (unlikely((*pkt) == NULL))
+ return 0;
+ ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_REASSOC_REQ, net->bssid, net->bssid);
+
+ /* Fill in the capabilities */
+ (*pkt)->capability = ieee80211softmac_capabilities(mac, net);
+
+ /* Fill in Listen Interval (?) */
+ (*pkt)->listen_interval = cpu_to_le16(10);
+ /* Fill in the current AP MAC */
+ memcpy((*pkt)->current_ap, mac->ieee->bssid, ETH_ALEN);
+
+ data = (u8 *)(*pkt)->info_element;
+ /* Add SSID */
+ data = ieee80211softmac_add_essid(data, &net->essid);
+ /* Add Rates */
+ data = ieee80211softmac_frame_add_rates(data, &mac->ratesinfo);
+ /* Return packet size */
+ return (data - (u8 *)(*pkt));
+}
+
+/* Create an authentication packet */
+static u32
+ieee80211softmac_auth(struct ieee80211_auth **pkt,
+ struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net,
+ u16 transaction, u16 status, int *encrypt_mpdu)
+{
+ u8 *data;
+ int auth_mode = mac->ieee->sec.auth_mode;
+ int is_shared_response = (auth_mode == WLAN_AUTH_SHARED_KEY
+ && transaction == IEEE80211SOFTMAC_AUTH_SHARED_RESPONSE);
+
+ /* Allocate Packet */
+ (*pkt) = (struct ieee80211_auth *)ieee80211softmac_alloc_mgt(
+ 2 + /* Auth Algorithm */
+ 2 + /* Auth Transaction Seq */
+ 2 + /* Status Code */
+ /* Challenge Text IE */
+ (is_shared_response ? 1 + 1 + net->challenge_len : 0)
+ );
+ if (unlikely((*pkt) == NULL))
+ return 0;
+ ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_AUTH, net->bssid, net->bssid);
+
+ /* Algorithm */
+ (*pkt)->algorithm = cpu_to_le16(auth_mode);
+ /* Transaction */
+ (*pkt)->transaction = cpu_to_le16(transaction);
+ /* Status */
+ (*pkt)->status = cpu_to_le16(status);
+
+ data = (u8 *)(*pkt)->info_element;
+ /* Challenge Text */
+ if (is_shared_response) {
+ *data = MFIE_TYPE_CHALLENGE;
+ data++;
+
+ /* Copy the challenge in */
+ *data = net->challenge_len;
+ data++;
+ memcpy(data, net->challenge, net->challenge_len);
+ data += net->challenge_len;
+
+ /* Make sure this frame gets encrypted with the shared key */
+ *encrypt_mpdu = 1;
+ } else
+ *encrypt_mpdu = 0;
+
+ /* Return the packet size */
+ return (data - (u8 *)(*pkt));
+}
+
+/* Create a disassocation or deauthentication packet */
+static u32
+ieee80211softmac_disassoc_deauth(struct ieee80211_disassoc **pkt,
+ struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net,
+ u16 type, u16 reason)
+{
+ /* Allocate Packet */
+ (*pkt) = (struct ieee80211_disassoc *)ieee80211softmac_alloc_mgt(2);
+ if (unlikely((*pkt) == NULL))
+ return 0;
+ ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), type, net->bssid, net->bssid);
+ /* Reason */
+ (*pkt)->reason = cpu_to_le16(reason);
+ /* Return the packet size */
+ return (2 + IEEE80211_3ADDR_LEN);
+}
+
+/* Create a probe request packet */
+static u32
+ieee80211softmac_probe_req(struct ieee80211_probe_request **pkt,
+ struct ieee80211softmac_device *mac, struct ieee80211softmac_essid *essid)
+{
+ u8 *data;
+ /* Allocate Packet */
+ (*pkt) = (struct ieee80211_probe_request *)ieee80211softmac_alloc_mgt(
+ /* SSID of requested network */
+ 1 + 1 + IW_ESSID_MAX_SIZE +
+ /* Rates IE */
+ 1 + 1 + IEEE80211SOFTMAC_MAX_RATES_LEN +
+ /* Extended Rates IE */
+ 1 + 1 + IEEE80211SOFTMAC_MAX_EX_RATES_LEN
+ );
+ if (unlikely((*pkt) == NULL))
+ return 0;
+ ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_PROBE_REQ, NULL, NULL);
+
+ data = (u8 *)(*pkt)->info_element;
+ /* Add ESSID (can be NULL) */
+ data = ieee80211softmac_add_essid(data, essid);
+ /* Add Rates */
+ data = ieee80211softmac_frame_add_rates(data, &mac->ratesinfo);
+ /* Return packet size */
+ return (data - (u8 *)(*pkt));
+}
+
+/* Create a probe response packet */
+/* FIXME: Not complete */
+static u32
+ieee80211softmac_probe_resp(struct ieee80211_probe_response **pkt,
+ struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net)
+{
+ u8 *data;
+ /* Allocate Packet */
+ (*pkt) = (struct ieee80211_probe_response *)ieee80211softmac_alloc_mgt(
+ 8 + /* Timestamp */
+ 2 + /* Beacon Interval */
+ 2 + /* Capability Info */
+ /* SSID IE */
+ 1 + 1 + IW_ESSID_MAX_SIZE +
+ 7 + /* FH Parameter Set */
+ 2 + /* DS Parameter Set */
+ 8 + /* CF Parameter Set */
+ 4 /* IBSS Parameter Set */
+ );
+ if (unlikely((*pkt) == NULL))
+ return 0;
+ ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_PROBE_RESP, net->bssid, net->bssid);
+ data = (u8 *)(*pkt)->info_element;
+
+ /* Return the packet size */
+ return (data - (u8 *)(*pkt));
+}
+
+
+/* Sends a manangement packet
+ * FIXME: document the use of the arg parameter
+ * for _AUTH: (transaction #) | (status << 16)
+ */
+int
+ieee80211softmac_send_mgt_frame(struct ieee80211softmac_device *mac,
+ void *ptrarg, u32 type, u32 arg)
+{
+ void *pkt = NULL;
+ u32 pkt_size = 0;
+ int encrypt_mpdu = 0;
+
+ switch(type) {
+ case IEEE80211_STYPE_ASSOC_REQ:
+ pkt_size = ieee80211softmac_assoc_req((struct ieee80211_assoc_request **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg);
+ break;
+ case IEEE80211_STYPE_REASSOC_REQ:
+ pkt_size = ieee80211softmac_reassoc_req((struct ieee80211_reassoc_request **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg);
+ break;
+ case IEEE80211_STYPE_AUTH:
+ pkt_size = ieee80211softmac_auth((struct ieee80211_auth **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg, (u16)(arg & 0xFFFF), (u16) (arg >> 16), &encrypt_mpdu);
+ break;
+ case IEEE80211_STYPE_DISASSOC:
+ case IEEE80211_STYPE_DEAUTH:
+ pkt_size = ieee80211softmac_disassoc_deauth((struct ieee80211_disassoc **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg, type, (u16)(arg & 0xFFFF));
+ break;
+ case IEEE80211_STYPE_PROBE_REQ:
+ pkt_size = ieee80211softmac_probe_req((struct ieee80211_probe_request **)(&pkt), mac, (struct ieee80211softmac_essid *)ptrarg);
+ break;
+ case IEEE80211_STYPE_PROBE_RESP:
+ pkt_size = ieee80211softmac_probe_resp((struct ieee80211_probe_response **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg);
+ break;
+ default:
+ printkl(KERN_DEBUG PFX "Unsupported Management Frame type: %i\n", type);
+ return -EINVAL;
+ };
+
+ if(pkt_size == 0 || pkt == NULL) {
+ printkl(KERN_DEBUG PFX "Error, packet is nonexistant or 0 length\n");
+ return -ENOMEM;
+ }
+
+ /* Send the packet to the ieee80211 layer for tx */
+ /* we defined softmac->mgmt_xmit for this. Should we keep it
+ * as it is (that means we'd need to wrap this into a txb),
+ * modify the prototype (so it matches this function),
+ * or get rid of it alltogether?
+ * Does this work for you now?
+ */
+ ieee80211_tx_frame(mac->ieee, (struct ieee80211_hdr *)pkt,
+ IEEE80211_3ADDR_LEN, pkt_size, encrypt_mpdu);
+
+ kfree(pkt);
+ return 0;
+}
+
+/* Beacon handling */
+int ieee80211softmac_handle_beacon(struct net_device *dev,
+ struct ieee80211_beacon *beacon,
+ struct ieee80211_network *network)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+
+ /* This might race, but we don't really care and it's not worth
+ * adding heavyweight locking in this fastpath.
+ */
+ if (mac->associnfo.associated) {
+ if (memcmp(network->bssid, mac->associnfo.bssid, ETH_ALEN) == 0)
+ ieee80211softmac_process_erp(mac, network->erp_value);
+ }
+
+ return 0;
+}
+
diff --git a/net/ieee80211/softmac/ieee80211softmac_module.c b/net/ieee80211/softmac/ieee80211softmac_module.c
new file mode 100644
index 000000000000..fcf6a28295e8
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_module.c
@@ -0,0 +1,621 @@
+/*
+ * Contains some basic softmac functions along with module registration code etc.
+ *
+ * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
+ * Joseph Jezak <josejx@gentoo.org>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ * Danny van Dyk <kugelfang@gentoo.org>
+ * Michael Buesch <mbuesch@freenet.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+
+#include "ieee80211softmac_priv.h"
+#include <linux/sort.h>
+#include <linux/etherdevice.h>
+
+struct net_device *alloc_ieee80211softmac(int sizeof_priv)
+{
+ struct ieee80211softmac_device *softmac;
+ struct net_device *dev;
+
+ dev = alloc_ieee80211(sizeof(struct ieee80211softmac_device) + sizeof_priv);
+ if (!dev)
+ return NULL;
+
+ softmac = ieee80211_priv(dev);
+// softmac->wq = create_freezeable_workqueue("softmac");
+ softmac->wq = create_workqueue("softmac");
+ if (!softmac->wq) {
+ free_ieee80211(dev);
+ return NULL;
+ }
+
+ softmac->dev = dev;
+ softmac->ieee = netdev_priv(dev);
+ spin_lock_init(&softmac->lock);
+
+ softmac->ieee->handle_auth = ieee80211softmac_auth_resp;
+ softmac->ieee->handle_deauth = ieee80211softmac_deauth_resp;
+ softmac->ieee->handle_assoc_response = ieee80211softmac_handle_assoc_response;
+ softmac->ieee->handle_reassoc_request = ieee80211softmac_handle_reassoc_req;
+ softmac->ieee->handle_disassoc = ieee80211softmac_handle_disassoc;
+ softmac->ieee->handle_beacon = ieee80211softmac_handle_beacon;
+ softmac->ieee->handle_probe_response = ieee80211softmac_handle_probe_resp;
+ softmac->scaninfo = NULL;
+
+ softmac->associnfo.scan_retry = IEEE80211SOFTMAC_ASSOC_SCAN_RETRY_LIMIT;
+ softmac->associnfo.channel = -1;
+ /* TODO: initialise all the other callbacks in the ieee struct
+ * (once they're written)
+ */
+
+ INIT_LIST_HEAD(&softmac->auth_queue);
+ INIT_LIST_HEAD(&softmac->network_list);
+ INIT_LIST_HEAD(&softmac->events);
+
+ mutex_init(&softmac->associnfo.mutex);
+
+ INIT_DELAYED_WORK(&softmac->associnfo.work, ieee80211softmac_assoc_work);
+ INIT_DELAYED_WORK(&softmac->associnfo.timeout, ieee80211softmac_assoc_timeout);
+
+ softmac->start_scan = ieee80211softmac_start_scan_implementation;
+ softmac->wait_for_scan = ieee80211softmac_wait_for_scan_implementation;
+ softmac->stop_scan = ieee80211softmac_stop_scan_implementation;
+
+ /* to start with, we can't send anything ... */
+ netif_carrier_off(dev);
+
+ return dev;
+}
+EXPORT_SYMBOL_GPL(alloc_ieee80211softmac);
+
+/* Clears the pending work queue items, stops all scans, etc. */
+void
+ieee80211softmac_clear_pending_work(struct ieee80211softmac_device *sm)
+{
+ unsigned long flags;
+ struct ieee80211softmac_event *eventptr, *eventtmp;
+ struct ieee80211softmac_auth_queue_item *authptr, *authtmp;
+ struct ieee80211softmac_network *netptr, *nettmp;
+
+ ieee80211softmac_stop_scan(sm);
+ ieee80211softmac_wait_for_scan(sm);
+
+ spin_lock_irqsave(&sm->lock, flags);
+ sm->running = 0;
+
+ /* Free all pending assoc work items */
+ cancel_delayed_work(&sm->associnfo.work);
+
+ /* Free all pending scan work items */
+ if(sm->scaninfo != NULL)
+ cancel_delayed_work(&sm->scaninfo->softmac_scan);
+
+ /* Free all pending auth work items */
+ list_for_each_entry(authptr, &sm->auth_queue, list)
+ cancel_delayed_work(&authptr->work);
+
+ /* delete all pending event calls and work items */
+ list_for_each_entry_safe(eventptr, eventtmp, &sm->events, list)
+ cancel_delayed_work(&eventptr->work);
+
+ spin_unlock_irqrestore(&sm->lock, flags);
+ flush_scheduled_work();
+
+ /* now we should be save and no longer need locking... */
+ spin_lock_irqsave(&sm->lock, flags);
+ /* Free all pending auth work items */
+ list_for_each_entry_safe(authptr, authtmp, &sm->auth_queue, list) {
+ list_del(&authptr->list);
+ kfree(authptr);
+ }
+
+ /* delete all pending event calls and work items */
+ list_for_each_entry_safe(eventptr, eventtmp, &sm->events, list) {
+ list_del(&eventptr->list);
+ kfree(eventptr);
+ }
+
+ /* Free all networks */
+ list_for_each_entry_safe(netptr, nettmp, &sm->network_list, list) {
+ ieee80211softmac_del_network_locked(sm, netptr);
+ if(netptr->challenge != NULL)
+ kfree(netptr->challenge);
+ kfree(netptr);
+ }
+
+ spin_unlock_irqrestore(&sm->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_clear_pending_work);
+
+void free_ieee80211softmac(struct net_device *dev)
+{
+ struct ieee80211softmac_device *sm = ieee80211_priv(dev);
+ ieee80211softmac_clear_pending_work(sm);
+ kfree(sm->scaninfo);
+ kfree(sm->wpa.IE);
+ free_ieee80211(dev);
+}
+EXPORT_SYMBOL_GPL(free_ieee80211softmac);
+
+static void ieee80211softmac_start_check_rates(struct ieee80211softmac_device *mac)
+{
+ struct ieee80211softmac_ratesinfo *ri = &mac->ratesinfo;
+ /* I took out the sorting check, we're seperating by modulation now. */
+ if (ri->count)
+ return;
+ /* otherwise assume we hav'em all! */
+ if (mac->ieee->modulation & IEEE80211_CCK_MODULATION) {
+ ri->rates[ri->count++] = IEEE80211_CCK_RATE_1MB | IEEE80211_BASIC_RATE_MASK;
+ ri->rates[ri->count++] = IEEE80211_CCK_RATE_2MB | IEEE80211_BASIC_RATE_MASK;
+ ri->rates[ri->count++] = IEEE80211_CCK_RATE_5MB | IEEE80211_BASIC_RATE_MASK;
+ ri->rates[ri->count++] = IEEE80211_CCK_RATE_11MB | IEEE80211_BASIC_RATE_MASK;
+ }
+ if (mac->ieee->modulation & IEEE80211_OFDM_MODULATION) {
+ ri->rates[ri->count++] = IEEE80211_OFDM_RATE_6MB;
+ ri->rates[ri->count++] = IEEE80211_OFDM_RATE_9MB;
+ ri->rates[ri->count++] = IEEE80211_OFDM_RATE_12MB;
+ ri->rates[ri->count++] = IEEE80211_OFDM_RATE_18MB;
+ ri->rates[ri->count++] = IEEE80211_OFDM_RATE_24MB;
+ ri->rates[ri->count++] = IEEE80211_OFDM_RATE_36MB;
+ ri->rates[ri->count++] = IEEE80211_OFDM_RATE_48MB;
+ ri->rates[ri->count++] = IEEE80211_OFDM_RATE_54MB;
+ }
+}
+
+int ieee80211softmac_ratesinfo_rate_supported(struct ieee80211softmac_ratesinfo *ri, u8 rate)
+{
+ int search;
+ u8 search_rate;
+
+ for (search = 0; search < ri->count; search++) {
+ search_rate = ri->rates[search];
+ search_rate &= ~IEEE80211_BASIC_RATE_MASK;
+ if (rate == search_rate)
+ return 1;
+ }
+
+ return 0;
+}
+
+u8 ieee80211softmac_highest_supported_rate(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_ratesinfo *ri, int basic_only)
+{
+ u8 user_rate = mac->txrates.user_rate;
+ int i;
+
+ if (ri->count == 0)
+ return IEEE80211_CCK_RATE_1MB;
+
+ for (i = ri->count - 1; i >= 0; i--) {
+ u8 rate = ri->rates[i];
+ if (basic_only && !(rate & IEEE80211_BASIC_RATE_MASK))
+ continue;
+ rate &= ~IEEE80211_BASIC_RATE_MASK;
+ if (rate > user_rate)
+ continue;
+ if (ieee80211softmac_ratesinfo_rate_supported(&mac->ratesinfo, rate))
+ return rate;
+ }
+
+ /* If we haven't found a suitable rate by now, just trust the user */
+ return user_rate;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_highest_supported_rate);
+
+void ieee80211softmac_process_erp(struct ieee80211softmac_device *mac,
+ u8 erp_value)
+{
+ int use_protection;
+ int short_preamble;
+ u32 changes = 0;
+
+ /* Barker preamble mode */
+ short_preamble = ((erp_value & WLAN_ERP_BARKER_PREAMBLE) == 0
+ && mac->associnfo.short_preamble_available) ? 1 : 0;
+
+ /* Protection needed? */
+ use_protection = (erp_value & WLAN_ERP_USE_PROTECTION) != 0;
+
+ if (mac->bssinfo.short_preamble != short_preamble) {
+ changes |= IEEE80211SOFTMAC_BSSINFOCHG_SHORT_PREAMBLE;
+ mac->bssinfo.short_preamble = short_preamble;
+ }
+
+ if (mac->bssinfo.use_protection != use_protection) {
+ changes |= IEEE80211SOFTMAC_BSSINFOCHG_PROTECTION;
+ mac->bssinfo.use_protection = use_protection;
+ }
+
+ if (mac->bssinfo_change && changes)
+ mac->bssinfo_change(mac->dev, changes);
+}
+
+void ieee80211softmac_recalc_txrates(struct ieee80211softmac_device *mac)
+{
+ struct ieee80211softmac_txrates *txrates = &mac->txrates;
+ u32 change = 0;
+
+ change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
+ txrates->default_rate = ieee80211softmac_highest_supported_rate(mac, &mac->bssinfo.supported_rates, 0);
+
+ change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
+ txrates->default_fallback = lower_rate(mac, txrates->default_rate);
+
+ change |= IEEE80211SOFTMAC_TXRATECHG_MCAST;
+ txrates->mcast_rate = ieee80211softmac_highest_supported_rate(mac, &mac->bssinfo.supported_rates, 1);
+
+ if (mac->txrates_change)
+ mac->txrates_change(mac->dev, change);
+
+}
+
+void ieee80211softmac_init_bss(struct ieee80211softmac_device *mac)
+{
+ struct ieee80211_device *ieee = mac->ieee;
+ u32 change = 0;
+ struct ieee80211softmac_txrates *txrates = &mac->txrates;
+ struct ieee80211softmac_bss_info *bssinfo = &mac->bssinfo;
+
+ /* TODO: We need some kind of state machine to lower the default rates
+ * if we loose too many packets.
+ */
+ /* Change the default txrate to the highest possible value.
+ * The txrate machine will lower it, if it is too high.
+ */
+ if (ieee->modulation & IEEE80211_OFDM_MODULATION)
+ txrates->user_rate = IEEE80211_OFDM_RATE_24MB;
+ else
+ txrates->user_rate = IEEE80211_CCK_RATE_11MB;
+
+ txrates->default_rate = IEEE80211_CCK_RATE_1MB;
+ change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
+
+ txrates->default_fallback = IEEE80211_CCK_RATE_1MB;
+ change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
+
+ txrates->mcast_rate = IEEE80211_CCK_RATE_1MB;
+ change |= IEEE80211SOFTMAC_TXRATECHG_MCAST;
+
+ txrates->mgt_mcast_rate = IEEE80211_CCK_RATE_1MB;
+ change |= IEEE80211SOFTMAC_TXRATECHG_MGT_MCAST;
+
+ if (mac->txrates_change)
+ mac->txrates_change(mac->dev, change);
+
+ change = 0;
+
+ bssinfo->supported_rates.count = 0;
+ memset(bssinfo->supported_rates.rates, 0,
+ sizeof(bssinfo->supported_rates.rates));
+ change |= IEEE80211SOFTMAC_BSSINFOCHG_RATES;
+
+ bssinfo->short_preamble = 0;
+ change |= IEEE80211SOFTMAC_BSSINFOCHG_SHORT_PREAMBLE;
+
+ bssinfo->use_protection = 0;
+ change |= IEEE80211SOFTMAC_BSSINFOCHG_PROTECTION;
+
+ if (mac->bssinfo_change)
+ mac->bssinfo_change(mac->dev, change);
+
+ mac->running = 1;
+}
+
+void ieee80211softmac_start(struct net_device *dev)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+
+ ieee80211softmac_start_check_rates(mac);
+ ieee80211softmac_init_bss(mac);
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_start);
+
+void ieee80211softmac_stop(struct net_device *dev)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+
+ ieee80211softmac_clear_pending_work(mac);
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_stop);
+
+void ieee80211softmac_set_rates(struct net_device *dev, u8 count, u8 *rates)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&mac->lock, flags);
+ memcpy(mac->ratesinfo.rates, rates, count);
+ mac->ratesinfo.count = count;
+ spin_unlock_irqrestore(&mac->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_set_rates);
+
+static u8 raise_rate(struct ieee80211softmac_device *mac, u8 rate)
+{
+ int i;
+ struct ieee80211softmac_ratesinfo *ri = &mac->ratesinfo;
+
+ for (i=0; i<ri->count-1; i++) {
+ if (ri->rates[i] == rate)
+ return ri->rates[i+1];
+ }
+ /* I guess we can't go any higher... */
+ return ri->rates[ri->count];
+}
+
+u8 ieee80211softmac_lower_rate_delta(struct ieee80211softmac_device *mac, u8 rate, int delta)
+{
+ int i;
+ struct ieee80211softmac_ratesinfo *ri = &mac->ratesinfo;
+
+ for (i=delta; i<ri->count; i++) {
+ if (ri->rates[i] == rate)
+ return ri->rates[i-delta];
+ }
+ /* I guess we can't go any lower... */
+ return ri->rates[0];
+}
+
+static void ieee80211softmac_add_txrates_badness(struct ieee80211softmac_device *mac,
+ int amount)
+{
+ u8 default_rate = mac->txrates.default_rate;
+ u8 default_fallback = mac->txrates.default_fallback;
+ u32 changes = 0;
+
+ //TODO: This is highly experimental code.
+ // Maybe the dynamic rate selection does not work
+ // and it has to be removed again.
+
+printk("badness %d\n", mac->txrate_badness);
+ mac->txrate_badness += amount;
+ if (mac->txrate_badness <= -1000) {
+ /* Very small badness. Try a faster bitrate. */
+ default_rate = raise_rate(mac, default_rate);
+ changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
+ default_fallback = get_fallback_rate(mac, default_rate);
+ changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
+ mac->txrate_badness = 0;
+printk("Bitrate raised to %u\n", default_rate);
+ } else if (mac->txrate_badness >= 10000) {
+ /* Very high badness. Try a slower bitrate. */
+ default_rate = lower_rate(mac, default_rate);
+ changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
+ default_fallback = get_fallback_rate(mac, default_rate);
+ changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
+ mac->txrate_badness = 0;
+printk("Bitrate lowered to %u\n", default_rate);
+ }
+
+ mac->txrates.default_rate = default_rate;
+ mac->txrates.default_fallback = default_fallback;
+
+ if (changes && mac->txrates_change)
+ mac->txrates_change(mac->dev, changes);
+}
+
+void ieee80211softmac_fragment_lost(struct net_device *dev,
+ u16 wl_seq)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&mac->lock, flags);
+ ieee80211softmac_add_txrates_badness(mac, 1000);
+ //TODO
+
+ spin_unlock_irqrestore(&mac->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_fragment_lost);
+
+static int rate_cmp(const void *a_, const void *b_) {
+ u8 *a, *b;
+ a = (u8*)a_;
+ b = (u8*)b_;
+ return ((*a & ~IEEE80211_BASIC_RATE_MASK) - (*b & ~IEEE80211_BASIC_RATE_MASK));
+}
+
+/* Allocate a softmac network struct and fill it from a network */
+struct ieee80211softmac_network *
+ieee80211softmac_create_network(struct ieee80211softmac_device *mac,
+ struct ieee80211_network *net)
+{
+ struct ieee80211softmac_network *softnet;
+ softnet = kzalloc(sizeof(struct ieee80211softmac_network), GFP_ATOMIC);
+ if(softnet == NULL)
+ return NULL;
+ memcpy(softnet->bssid, net->bssid, ETH_ALEN);
+ softnet->mode = (net->capability & WLAN_CAPABILITY_ESS)? IW_MODE_INFRA: IW_MODE_ADHOC;
+ softnet->channel = net->channel;
+ softnet->essid.len = net->ssid_len;
+ memcpy(softnet->essid.data, net->ssid, softnet->essid.len);
+
+ /* copy rates over */
+ softnet->supported_rates.count = net->rates_len;
+ memcpy(&softnet->supported_rates.rates[0], net->rates, net->rates_len);
+ memcpy(&softnet->supported_rates.rates[softnet->supported_rates.count], net->rates_ex, net->rates_ex_len);
+ softnet->supported_rates.count += net->rates_ex_len;
+ sort(softnet->supported_rates.rates, softnet->supported_rates.count, sizeof(softnet->supported_rates.rates[0]), rate_cmp, NULL);
+
+ /* we save the ERP value because it is needed at association time, and
+ * many AP's do not include an ERP IE in the association response. */
+ softnet->erp_value = net->erp_value;
+
+ softnet->capabilities = net->capability;
+ return softnet;
+}
+
+
+/* Add a network to the list, while locked */
+void
+ieee80211softmac_add_network_locked(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *add_net)
+{
+ struct list_head *list_ptr;
+ struct ieee80211softmac_network *softmac_net = NULL;
+
+ list_for_each(list_ptr, &mac->network_list) {
+ softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list);
+ if(!memcmp(softmac_net->bssid, add_net->bssid, ETH_ALEN))
+ break;
+ else
+ softmac_net = NULL;
+ }
+ if(softmac_net == NULL)
+ list_add(&(add_net->list), &mac->network_list);
+}
+
+/* Add a network to the list, with locking */
+void
+ieee80211softmac_add_network(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *add_net)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&mac->lock, flags);
+ ieee80211softmac_add_network_locked(mac, add_net);
+ spin_unlock_irqrestore(&mac->lock, flags);
+}
+
+
+/* Delete a network from the list, while locked*/
+void
+ieee80211softmac_del_network_locked(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *del_net)
+{
+ list_del(&(del_net->list));
+}
+
+/* Delete a network from the list with locking */
+void
+ieee80211softmac_del_network(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *del_net)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&mac->lock, flags);
+ ieee80211softmac_del_network_locked(mac, del_net);
+ spin_unlock_irqrestore(&mac->lock, flags);
+}
+
+/* Get a network from the list by MAC while locked */
+struct ieee80211softmac_network *
+ieee80211softmac_get_network_by_bssid_locked(struct ieee80211softmac_device *mac,
+ u8 *bssid)
+{
+ struct list_head *list_ptr;
+ struct ieee80211softmac_network *softmac_net = NULL;
+ list_for_each(list_ptr, &mac->network_list) {
+ softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list);
+ if(!memcmp(softmac_net->bssid, bssid, ETH_ALEN))
+ break;
+ else
+ softmac_net = NULL;
+ }
+ return softmac_net;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_get_network_by_bssid_locked);
+
+/* Get a network from the list by BSSID with locking */
+struct ieee80211softmac_network *
+ieee80211softmac_get_network_by_bssid(struct ieee80211softmac_device *mac,
+ u8 *bssid)
+{
+ unsigned long flags;
+ struct ieee80211softmac_network *softmac_net;
+
+ spin_lock_irqsave(&mac->lock, flags);
+ softmac_net = ieee80211softmac_get_network_by_bssid_locked(mac, bssid);
+ spin_unlock_irqrestore(&mac->lock, flags);
+ return softmac_net;
+}
+
+/* Get a network from the list by MAC and channel while locked */
+struct ieee80211softmac_network *
+ieee80211softmac_get_network_by_bssid_with_channel_locked(
+ struct ieee80211softmac_device *mac,
+ u8 *bssid,
+ u8 channel )
+{
+ struct list_head *list_ptr;
+ struct ieee80211softmac_network *softmac_net = NULL;
+ list_for_each(list_ptr, &mac->network_list) {
+ softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list);
+ if(!memcmp(softmac_net->bssid, bssid, ETH_ALEN) &&
+ (channel == softmac_net->channel))
+ break;
+ else
+ softmac_net = NULL;
+ }
+ return softmac_net;
+}
+
+/* Get a network from the list by BSSID and channel with locking */
+struct ieee80211softmac_network *
+ieee80211softmac_get_network_by_bssid_with_channel(
+ struct ieee80211softmac_device *mac,
+ u8 *bssid,
+ u8 channel )
+{
+ unsigned long flags;
+ struct ieee80211softmac_network *softmac_net;
+
+ spin_lock_irqsave(&mac->lock, flags);
+ softmac_net = ieee80211softmac_get_network_by_bssid_with_channel_locked(mac, bssid,channel);
+ spin_unlock_irqrestore(&mac->lock, flags);
+ return softmac_net;
+}
+
+/* Get a network from the list by ESSID while locked */
+struct ieee80211softmac_network *
+ieee80211softmac_get_network_by_essid_locked(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_essid *essid)
+{
+ struct list_head *list_ptr;
+ struct ieee80211softmac_network *softmac_net = NULL;
+
+ list_for_each(list_ptr, &mac->network_list) {
+ softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list);
+ if (softmac_net->essid.len == essid->len &&
+ !memcmp(softmac_net->essid.data, essid->data, essid->len))
+ return softmac_net;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_get_network_by_essid_locked);
+
+/* Get a network from the list by ESSID with locking */
+struct ieee80211softmac_network *
+ieee80211softmac_get_network_by_essid(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_essid *essid)
+{
+ unsigned long flags;
+ struct ieee80211softmac_network *softmac_net = NULL;
+
+ spin_lock_irqsave(&mac->lock, flags);
+ softmac_net = ieee80211softmac_get_network_by_essid_locked(mac, essid);
+ spin_unlock_irqrestore(&mac->lock, flags);
+ return softmac_net;
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Johannes Berg");
+MODULE_AUTHOR("Joseph Jezak");
+MODULE_AUTHOR("Larry Finger");
+MODULE_AUTHOR("Danny van Dyk");
+MODULE_AUTHOR("Michael Buesch");
+MODULE_DESCRIPTION("802.11 software MAC");
diff --git a/net/ieee80211/softmac/ieee80211softmac_priv.h b/net/ieee80211/softmac/ieee80211softmac_priv.h
new file mode 100644
index 000000000000..c94d19929a8a
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_priv.h
@@ -0,0 +1,256 @@
+/*
+ * Internal softmac API definitions.
+ *
+ * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
+ * Joseph Jezak <josejx@gentoo.org>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ * Danny van Dyk <kugelfang@gentoo.org>
+ * Michael Buesch <mbuesch@freenet.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+
+#ifndef IEEE80211SOFTMAC_PRIV_H_
+#define IEEE80211SOFTMAC_PRIV_H_
+
+#include <net/ieee80211softmac.h>
+#include <net/ieee80211softmac_wx.h>
+#include <linux/kernel.h>
+#include <linux/stringify.h>
+
+
+#define PFX "SoftMAC: "
+//#define CONFIG_IEEE80211_SOFTMAC_DEBUG 1
+
+#ifdef assert
+# undef assert
+#endif
+#ifdef CONFIG_IEEE80211_SOFTMAC_DEBUG
+#define assert(expr) \
+ do { \
+ if (unlikely(!(expr))) { \
+ printkl(KERN_ERR PFX "ASSERTION FAILED (%s) at: %s:%d:%s()\n", #expr, \
+ __FILE__, __LINE__, __FUNCTION__); \
+ } \
+ } while (0)
+#else
+#define assert(expr) do {} while (0)
+#endif
+
+/* rate limited printk(). */
+#ifdef printkl
+# undef printkl
+#endif
+#define printkl(f, x...) do { if (printk_ratelimit()) printk(f ,##x); } while (0)
+/* rate limited printk() for debugging */
+#ifdef dprintkl
+# undef dprintkl
+#endif
+#ifdef CONFIG_IEEE80211_SOFTMAC_DEBUG
+# define dprintkl printkl
+#else
+# define dprintkl(f, x...) do { /* nothing */ } while (0)
+#endif
+
+/* debugging printk() */
+#ifdef dprintk
+# undef dprintk
+#endif
+#ifdef CONFIG_IEEE80211_SOFTMAC_DEBUG
+# define dprintk(f, x...) do { printk(f ,##x); } while (0)
+#else
+# define dprintk(f, x...) do { /* nothing */ } while (0)
+#endif
+
+#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
+#define MAC_ARG(x) ((u8*)(x))[0],((u8*)(x))[1],((u8*)(x))[2],((u8*)(x))[3],((u8*)(x))[4],((u8*)(x))[5]
+
+
+/* private definitions and prototypes */
+
+/*** prototypes from _scan.c */
+void ieee80211softmac_scan(struct work_struct *work);
+/* for internal use if scanning is needed */
+int ieee80211softmac_start_scan(struct ieee80211softmac_device *mac);
+void ieee80211softmac_stop_scan(struct ieee80211softmac_device *mac);
+void ieee80211softmac_wait_for_scan(struct ieee80211softmac_device *mac);
+
+/* for use by _module.c to assign to the callbacks */
+int ieee80211softmac_start_scan_implementation(struct net_device *dev);
+void ieee80211softmac_stop_scan_implementation(struct net_device *dev);
+void ieee80211softmac_wait_for_scan_implementation(struct net_device *dev);
+int ieee80211softmac_handle_probe_resp(struct net_device *dev,
+ struct ieee80211_beacon *beacon,
+ struct ieee80211_network *network);
+
+/*** Network prototypes from _module.c */
+struct ieee80211softmac_network * ieee80211softmac_create_network(
+ struct ieee80211softmac_device *mac, struct ieee80211_network *net);
+void ieee80211softmac_add_network_locked(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *net);
+void ieee80211softmac_add_network(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *net);
+void ieee80211softmac_del_network_locked(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *net);
+void ieee80211softmac_del_network(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *net);
+struct ieee80211softmac_network * ieee80211softmac_get_network_by_bssid_locked(
+ struct ieee80211softmac_device *mac, u8 *ea);
+struct ieee80211softmac_network * ieee80211softmac_get_network_by_bssid(
+ struct ieee80211softmac_device *mac, u8 *ea);
+struct ieee80211softmac_network * ieee80211softmac_get_network_by_bssid_with_channel(
+ struct ieee80211softmac_device *mac, u8 *ea, u8 channel );
+struct ieee80211softmac_network * ieee80211softmac_get_network_by_ssid_locked(
+ struct ieee80211softmac_device *mac, u8 *ssid, u8 ssid_len);
+struct ieee80211softmac_network * ieee80211softmac_get_network_by_ssid(
+ struct ieee80211softmac_device *mac, u8 *ssid, u8 ssid_len);
+struct ieee80211softmac_network *
+ieee80211softmac_get_network_by_essid_locked(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_essid *essid);
+struct ieee80211softmac_network *
+ieee80211softmac_get_network_by_essid(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_essid *essid);
+
+/* Rates related */
+void ieee80211softmac_process_erp(struct ieee80211softmac_device *mac,
+ u8 erp_value);
+int ieee80211softmac_ratesinfo_rate_supported(struct ieee80211softmac_ratesinfo *ri, u8 rate);
+u8 ieee80211softmac_lower_rate_delta(struct ieee80211softmac_device *mac, u8 rate, int delta);
+void ieee80211softmac_init_bss(struct ieee80211softmac_device *mac);
+void ieee80211softmac_recalc_txrates(struct ieee80211softmac_device *mac);
+static inline u8 lower_rate(struct ieee80211softmac_device *mac, u8 rate) {
+ return ieee80211softmac_lower_rate_delta(mac, rate, 1);
+}
+
+static inline u8 get_fallback_rate(struct ieee80211softmac_device *mac, u8 rate)
+{
+ return ieee80211softmac_lower_rate_delta(mac, rate, 2);
+}
+
+
+/*** prototypes from _io.c */
+int ieee80211softmac_send_mgt_frame(struct ieee80211softmac_device *mac,
+ void* ptrarg, u32 type, u32 arg);
+int ieee80211softmac_handle_beacon(struct net_device *dev,
+ struct ieee80211_beacon *beacon,
+ struct ieee80211_network *network);
+
+/*** prototypes from _auth.c */
+/* do these have to go into the public header? */
+int ieee80211softmac_auth_req(struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net);
+int ieee80211softmac_deauth_req(struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net, int reason);
+
+/* for use by _module.c to assign to the callbacks */
+int ieee80211softmac_auth_resp(struct net_device *dev, struct ieee80211_auth *auth);
+int ieee80211softmac_deauth_resp(struct net_device *dev, struct ieee80211_deauth *deauth);
+
+/*** prototypes from _assoc.c */
+void ieee80211softmac_assoc_work(struct work_struct *work);
+int ieee80211softmac_handle_assoc_response(struct net_device * dev,
+ struct ieee80211_assoc_response * resp,
+ struct ieee80211_network * network);
+int ieee80211softmac_handle_disassoc(struct net_device * dev,
+ struct ieee80211_disassoc * disassoc);
+int ieee80211softmac_handle_reassoc_req(struct net_device * dev,
+ struct ieee80211_reassoc_request * reassoc);
+void ieee80211softmac_assoc_timeout(struct work_struct *work);
+void ieee80211softmac_send_disassoc_req(struct ieee80211softmac_device *mac, u16 reason);
+void ieee80211softmac_disassoc(struct ieee80211softmac_device *mac);
+
+/* some helper functions */
+static inline int ieee80211softmac_scan_handlers_check_self(struct ieee80211softmac_device *sm)
+{
+ return (sm->start_scan == ieee80211softmac_start_scan_implementation) &&
+ (sm->stop_scan == ieee80211softmac_stop_scan_implementation) &&
+ (sm->wait_for_scan == ieee80211softmac_wait_for_scan_implementation);
+}
+
+static inline int ieee80211softmac_scan_sanity_check(struct ieee80211softmac_device *sm)
+{
+ return ((sm->start_scan != ieee80211softmac_start_scan_implementation) &&
+ (sm->stop_scan != ieee80211softmac_stop_scan_implementation) &&
+ (sm->wait_for_scan != ieee80211softmac_wait_for_scan_implementation)
+ ) || ieee80211softmac_scan_handlers_check_self(sm);
+}
+
+#define IEEE80211SOFTMAC_PROBE_DELAY HZ/50
+#define IEEE80211SOFTMAC_WORKQUEUE_NAME_LEN (17 + IFNAMSIZ)
+
+struct ieee80211softmac_network {
+ struct list_head list; /* List */
+ /* Network information copied from ieee80211_network */
+ u8 bssid[ETH_ALEN];
+ u8 mode;
+ u8 channel;
+ struct ieee80211softmac_essid essid;
+
+ struct ieee80211softmac_ratesinfo supported_rates;
+
+ /* SoftMAC specific */
+ u16 authenticating:1, /* Status Flags */
+ authenticated:1,
+ auth_desynced_once:1;
+
+ u8 erp_value; /* Saved ERP value */
+ u16 capabilities; /* Capabilities bitfield */
+ u8 challenge_len; /* Auth Challenge length */
+ char *challenge; /* Challenge Text */
+};
+
+/* structure used to keep track of networks we're auth'ing to */
+struct ieee80211softmac_auth_queue_item {
+ struct list_head list; /* List head */
+ struct ieee80211softmac_network *net; /* Network to auth */
+ struct ieee80211softmac_device *mac; /* SoftMAC device */
+ u8 retry; /* Retry limit */
+ u8 state; /* Auth State */
+ struct delayed_work work; /* Work queue */
+
+};
+
+/* scanning information */
+struct ieee80211softmac_scaninfo {
+ u8 current_channel_idx,
+ number_channels;
+ struct ieee80211_channel *channels;
+ u8 started:1,
+ stop:1;
+ u8 skip_flags;
+ struct completion finished;
+ struct delayed_work softmac_scan;
+ struct ieee80211softmac_device *mac;
+};
+
+/* private event struct */
+struct ieee80211softmac_event {
+ struct list_head list;
+ int event_type;
+ void *event_context;
+ struct delayed_work work;
+ notify_function_ptr fun;
+ void *context;
+ struct ieee80211softmac_device *mac;
+};
+
+void ieee80211softmac_call_events(struct ieee80211softmac_device *mac, int event, void *event_context);
+void ieee80211softmac_call_events_locked(struct ieee80211softmac_device *mac, int event, void *event_context);
+int ieee80211softmac_notify_internal(struct ieee80211softmac_device *mac,
+ int event, void *event_context, notify_function_ptr fun, void *context, gfp_t gfp_mask);
+
+void ieee80211softmac_try_reassoc(struct ieee80211softmac_device *mac);
+
+#endif /* IEEE80211SOFTMAC_PRIV_H_ */
diff --git a/net/ieee80211/softmac/ieee80211softmac_scan.c b/net/ieee80211/softmac/ieee80211softmac_scan.c
new file mode 100644
index 000000000000..dd43abee552d
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_scan.c
@@ -0,0 +1,331 @@
+/*
+ * Scanning routines.
+ *
+ * These are not exported because they're assigned to the function pointers.
+ *
+ * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
+ * Joseph Jezak <josejx@gentoo.org>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ * Danny van Dyk <kugelfang@gentoo.org>
+ * Michael Buesch <mbuesch@freenet.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+
+#include <linux/completion.h>
+#include "ieee80211softmac_priv.h"
+
+/* internal, use to trigger scanning if needed.
+ * Returns -EBUSY if already scanning,
+ * result of start_scan otherwise */
+int
+ieee80211softmac_start_scan(struct ieee80211softmac_device *sm)
+{
+ struct ieee80211_network *net = NULL;
+ struct ieee80211_network *nettmp = NULL;
+ struct ieee80211softmac_network *netptr, *nettmp_sm;
+ unsigned long flags;
+ unsigned long flags2;
+ int ret;
+
+ spin_lock_irqsave(&sm->lock, flags);
+ if (sm->scanning)
+ {
+ spin_unlock_irqrestore(&sm->lock, flags);
+ return -EINPROGRESS;
+ }
+ sm->scanning = 1;
+ sm->scan.had_probe_resp = 0;
+ sm->scan.wait_cycles_left = 0;
+
+ if( !sm->associnfo.associated ) {
+ /* Delete all existing networks. This is necessary to overcome
+ * stale entries that are not active any longer but are still in
+ * list with good values. The association logic based on RSSI
+ * might decide to switch to that AP when a new but worse
+ * active entry is present. all ieee802.11 networks */
+ spin_lock_irqsave(&sm->ieee->lock, flags2);
+ list_for_each_entry_safe(net, nettmp, &sm->ieee->network_list, list) {
+ list_move(&net->list, &sm->ieee->network_free_list);
+ }
+ spin_unlock_irqrestore(&sm->ieee->lock, flags2);
+
+ /* all softmac networks */
+ list_for_each_entry_safe(netptr, nettmp_sm, &sm->network_list, list) {
+ ieee80211softmac_del_network_locked(sm, netptr);
+ if(netptr->challenge != NULL)
+ kfree(netptr->challenge);
+ kfree(netptr);
+ }
+ }
+ spin_unlock_irqrestore(&sm->lock, flags);
+
+ ret = sm->start_scan(sm->dev);
+ if (ret) {
+ spin_lock_irqsave(&sm->lock, flags);
+ sm->scanning = 0;
+ spin_unlock_irqrestore(&sm->lock, flags);
+ }
+ return ret;
+}
+
+void
+ieee80211softmac_stop_scan(struct ieee80211softmac_device *sm)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sm->lock, flags);
+
+ if (!sm->scanning) {
+ spin_unlock_irqrestore(&sm->lock, flags);
+ return;
+ }
+
+ spin_unlock_irqrestore(&sm->lock, flags);
+ sm->stop_scan(sm->dev);
+}
+
+void
+ieee80211softmac_wait_for_scan(struct ieee80211softmac_device *sm)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sm->lock, flags);
+
+ if (!sm->scanning) {
+ spin_unlock_irqrestore(&sm->lock, flags);
+ return;
+ }
+
+ spin_unlock_irqrestore(&sm->lock, flags);
+ sm->wait_for_scan(sm->dev);
+}
+
+
+/* internal scanning implementation follows */
+void ieee80211softmac_scan(struct work_struct *work)
+{
+ int invalid_channel;
+ u8 current_channel_idx;
+ struct ieee80211softmac_scaninfo *si =
+ container_of(work, struct ieee80211softmac_scaninfo,
+ softmac_scan.work);
+
+ struct ieee80211softmac_device *sm = si->mac;
+ unsigned long flags;
+
+ /* waiting a fixed time for a probe response is maybe not the best
+ * solution. We probably won't catch all if there are many. Therefore,
+ * if we had a probe response, we wait a tick longer. Maybe another AP
+ * responds as well, but was too slow for that time slice. */
+ spin_lock_irqsave(&sm->lock, flags);
+ if( sm->scan.had_probe_resp && sm->scan.wait_cycles_left ) {
+ sm->scan.had_probe_resp = 0;
+ sm->scan.wait_cycles_left--;
+ queue_delayed_work(si->mac->wq, &si->softmac_scan, IEEE80211SOFTMAC_PROBE_DELAY);
+ spin_unlock_irqrestore(&sm->lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&sm->lock, flags);
+
+ while (!(si->stop) && (si->current_channel_idx < si->number_channels)) {
+ current_channel_idx = si->current_channel_idx;
+ si->current_channel_idx++; /* go to the next channel */
+
+ invalid_channel = (si->skip_flags & si->channels[current_channel_idx].flags);
+
+ if (!invalid_channel) {
+ spin_lock_irqsave(&sm->lock, flags);
+ /* limit cycles we wait for probe responses so that
+ we never hang. */
+ sm->scan.wait_cycles_left = 3;
+ sm->scan.had_probe_resp = 0;
+ spin_unlock_irqrestore(&sm->lock, flags);
+
+ sm->set_channel(sm->dev, si->channels[current_channel_idx].channel);
+ // FIXME make this user configurable (active/passive)
+ if(ieee80211softmac_send_mgt_frame(sm, NULL, IEEE80211_STYPE_PROBE_REQ, 0))
+ printkl(KERN_DEBUG PFX "Sending Probe Request Failed\n");
+
+ /* also send directed management frame for the network we're looking for */
+ // TODO: is this if correct, or should we do this only if scanning from assoc request?
+ if (sm->associnfo.req_essid.len)
+ ieee80211softmac_send_mgt_frame(sm, &sm->associnfo.req_essid, IEEE80211_STYPE_PROBE_REQ, 0);
+
+ spin_lock_irqsave(&sm->lock, flags);
+ if (unlikely(!sm->running)) {
+ /* Prevent reschedule on workqueue flush */
+ spin_unlock_irqrestore(&sm->lock, flags);
+ break;
+ }
+ queue_delayed_work(si->mac->wq, &si->softmac_scan, IEEE80211SOFTMAC_PROBE_DELAY);
+ spin_unlock_irqrestore(&sm->lock, flags);
+ return;
+ } else {
+ dprintk(PFX "Not probing Channel %d (not allowed here)\n", si->channels[current_channel_idx].channel);
+ }
+ }
+
+ spin_lock_irqsave(&sm->lock, flags);
+ cancel_delayed_work(&si->softmac_scan);
+ si->started = 0;
+ spin_unlock_irqrestore(&sm->lock, flags);
+
+ dprintk(PFX "Scanning finished: scanned %d channels starting with channel %d\n",
+ sm->scaninfo->number_channels, sm->scaninfo->channels[0].channel);
+ ieee80211softmac_scan_finished(sm);
+ complete_all(&sm->scaninfo->finished);
+}
+
+static inline struct ieee80211softmac_scaninfo *allocate_scaninfo(struct ieee80211softmac_device *mac)
+{
+ /* ugh. can we call this without having the spinlock held? */
+ struct ieee80211softmac_scaninfo *info = kmalloc(sizeof(struct ieee80211softmac_scaninfo), GFP_ATOMIC);
+ if (unlikely(!info))
+ return NULL;
+
+ INIT_DELAYED_WORK(&info->softmac_scan, ieee80211softmac_scan);
+
+ info->mac = mac;
+ init_completion(&info->finished);
+ return info;
+}
+
+int ieee80211softmac_start_scan_implementation(struct net_device *dev)
+{
+ struct ieee80211softmac_device *sm = ieee80211_priv(dev);
+ unsigned long flags;
+
+ if (!(dev->flags & IFF_UP))
+ return -ENODEV;
+
+ assert(ieee80211softmac_scan_handlers_check_self(sm));
+ if (!ieee80211softmac_scan_handlers_check_self(sm))
+ return -EINVAL;
+
+ spin_lock_irqsave(&sm->lock, flags);
+ /* it looks like we need to hold the lock here
+ * to make sure we don't allocate two of these... */
+ if (unlikely(!sm->scaninfo))
+ sm->scaninfo = allocate_scaninfo(sm);
+ if (unlikely(!sm->scaninfo)) {
+ spin_unlock_irqrestore(&sm->lock, flags);
+ return -ENOMEM;
+ }
+
+ sm->scaninfo->skip_flags = IEEE80211_CH_INVALID;
+ if (0 /* not scanning in IEEE802.11b */)//TODO
+ sm->scaninfo->skip_flags |= IEEE80211_CH_B_ONLY;
+ if (0 /* IEEE802.11a */) {//TODO
+ sm->scaninfo->channels = sm->ieee->geo.a;
+ sm->scaninfo->number_channels = sm->ieee->geo.a_channels;
+ } else {
+ sm->scaninfo->channels = sm->ieee->geo.bg;
+ sm->scaninfo->number_channels = sm->ieee->geo.bg_channels;
+ }
+ sm->scaninfo->current_channel_idx = 0;
+ sm->scaninfo->started = 1;
+ sm->scaninfo->stop = 0;
+ INIT_COMPLETION(sm->scaninfo->finished);
+ if (sm->set_bssid_filter) {
+ u8 bc_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+ sm->set_bssid_filter(sm->dev, bc_addr);
+ }
+ queue_delayed_work(sm->wq, &sm->scaninfo->softmac_scan, 0);
+ spin_unlock_irqrestore(&sm->lock, flags);
+ return 0;
+}
+
+void ieee80211softmac_stop_scan_implementation(struct net_device *dev)
+{
+ struct ieee80211softmac_device *sm = ieee80211_priv(dev);
+ unsigned long flags;
+
+ assert(ieee80211softmac_scan_handlers_check_self(sm));
+ if (!ieee80211softmac_scan_handlers_check_self(sm))
+ return;
+
+ spin_lock_irqsave(&sm->lock, flags);
+ assert(sm->scaninfo != NULL);
+ if (sm->scaninfo) {
+ if (sm->scaninfo->started)
+ sm->scaninfo->stop = 1;
+ else
+ complete_all(&sm->scaninfo->finished);
+ }
+ spin_unlock_irqrestore(&sm->lock, flags);
+}
+
+void ieee80211softmac_wait_for_scan_implementation(struct net_device *dev)
+{
+ struct ieee80211softmac_device *sm = ieee80211_priv(dev);
+ unsigned long flags;
+
+ assert(ieee80211softmac_scan_handlers_check_self(sm));
+ if (!ieee80211softmac_scan_handlers_check_self(sm))
+ return;
+
+ spin_lock_irqsave(&sm->lock, flags);
+ if (!sm->scaninfo->started) {
+ spin_unlock_irqrestore(&sm->lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&sm->lock, flags);
+ wait_for_completion(&sm->scaninfo->finished);
+}
+
+/* this is what drivers (that do scanning) call when they're done */
+void ieee80211softmac_scan_finished(struct ieee80211softmac_device *sm)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sm->lock, flags);
+ sm->scanning = 0;
+ spin_unlock_irqrestore(&sm->lock, flags);
+
+ if (sm->associnfo.bssvalid) {
+ struct ieee80211softmac_network *net;
+
+ net = ieee80211softmac_get_network_by_bssid_with_channel(sm, sm->associnfo.bssid, sm->associnfo.channel);
+ if (net) {
+ sm->set_channel(sm->dev, net->channel);
+ if (sm->set_bssid_filter)
+ sm->set_bssid_filter(sm->dev, net->bssid);
+ }
+ }
+ ieee80211softmac_call_events(sm, IEEE80211SOFTMAC_EVENT_SCAN_FINISHED, NULL);
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_scan_finished);
+
+/* Probe Response handling */
+int ieee80211softmac_handle_probe_resp(
+ struct net_device *dev,
+ struct ieee80211_beacon *beacon,
+ struct ieee80211_network *network )
+{
+ struct ieee80211softmac_device *sm = ieee80211_priv(dev);
+
+ unsigned long flags;
+
+ spin_lock_irqsave(&sm->lock, flags);
+ /* it's unlikely that we overrun it while scanning */
+ sm->scan.had_probe_resp++;
+ spin_unlock_irqrestore(&sm->lock, flags);
+
+ return 0;
+}
+
diff --git a/net/ieee80211/softmac/ieee80211softmac_wx.c b/net/ieee80211/softmac/ieee80211softmac_wx.c
new file mode 100644
index 000000000000..b8e0f60541db
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_wx.c
@@ -0,0 +1,514 @@
+/*
+ * This file contains our _wx handlers. Make sure you EXPORT_SYMBOL_GPL them
+ *
+ * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
+ * Joseph Jezak <josejx@gentoo.org>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ * Danny van Dyk <kugelfang@gentoo.org>
+ * Michael Buesch <mbuesch@freenet.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+
+#include "ieee80211softmac_priv.h"
+
+#include <net/iw_handler.h>
+/* for is_broadcast_ether_addr and is_zero_ether_addr */
+#include <linux/etherdevice.h>
+
+int
+ieee80211softmac_wx_trigger_scan(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
+ return ieee80211softmac_start_scan(sm);
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_trigger_scan);
+
+
+/* if we're still scanning, return -EAGAIN so that userspace tools
+ * can get the complete scan results, otherwise return 0. */
+int
+ieee80211softmac_wx_get_scan_results(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ unsigned long flags;
+ struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
+
+ spin_lock_irqsave(&sm->lock, flags);
+ if (sm->scanning) {
+ spin_unlock_irqrestore(&sm->lock, flags);
+ return -EAGAIN;
+ }
+ spin_unlock_irqrestore(&sm->lock, flags);
+ return ieee80211_wx_get_scan(sm->ieee, info, data, extra);
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_scan_results);
+
+int
+ieee80211softmac_wx_set_essid(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
+ struct ieee80211softmac_auth_queue_item *authptr;
+ int length = 0;
+
+ mutex_lock(&sm->associnfo.mutex);
+
+ /* Doe not cancel resetting of essid if new essid is equal to the old
+ essid.
+ Because if using wpa_supplicant and setting the same network with
+ "select x" or reassociating with "reassociate", wpa_supplicant removes
+ key information and tries to reassociate. But we did not do a rescan and
+ assoc_work. Now wireless system is stalled until the AP is sending a
+ disconnect_event. */
+ if((sm->associnfo.associating || sm->associnfo.associated) &&
+ (data->essid.flags && data->essid.length)) {
+ /* Get the associating network */
+ dprintk(KERN_INFO PFX "Canceling existing associate request!\n");
+ /* Cancel assoc work */
+ cancel_delayed_work(&sm->associnfo.work);
+ /* We don't have to do this, but it's a little cleaner */
+ list_for_each_entry(authptr, &sm->auth_queue, list)
+ cancel_delayed_work(&authptr->work);
+ sm->associnfo.bssvalid = 0;
+ sm->associnfo.bssfixed = 0;
+ flush_scheduled_work();
+ sm->associnfo.associating = 0;
+ sm->associnfo.associated = 0;
+ }
+
+
+ sm->associnfo.static_essid = 0;
+ sm->associnfo.assoc_wait = 0;
+
+ if (data->essid.flags && data->essid.length) {
+ length = min((int)data->essid.length, IW_ESSID_MAX_SIZE);
+ if (length) {
+ memcpy(sm->associnfo.req_essid.data, extra, length);
+ sm->associnfo.static_essid = 1;
+ }
+ }
+
+ /* set our requested ESSID length.
+ * If applicable, we have already copied the data in */
+ sm->associnfo.req_essid.len = length;
+
+ sm->associnfo.associating = 1;
+ /* queue lower level code to do work (if necessary) */
+ queue_delayed_work(sm->wq, &sm->associnfo.work, 0);
+ mutex_unlock(&sm->associnfo.mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_essid);
+
+int
+ieee80211softmac_wx_get_essid(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
+
+ mutex_lock(&sm->associnfo.mutex);
+ /* If all fails, return ANY (empty) */
+ data->essid.length = 0;
+ data->essid.flags = 0; /* active */
+
+ /* If we have a statically configured ESSID then return it */
+ if (sm->associnfo.static_essid) {
+ data->essid.length = sm->associnfo.req_essid.len;
+ data->essid.flags = 1; /* active */
+ memcpy(extra, sm->associnfo.req_essid.data, sm->associnfo.req_essid.len);
+ }
+
+ /* If we're associating/associated, return that */
+ if (sm->associnfo.associated || sm->associnfo.associating) {
+ data->essid.length = sm->associnfo.associate_essid.len;
+ data->essid.flags = 1; /* active */
+ memcpy(extra, sm->associnfo.associate_essid.data, sm->associnfo.associate_essid.len);
+ }
+ mutex_unlock(&sm->associnfo.mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_essid);
+
+int
+ieee80211softmac_wx_set_rate(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
+ struct ieee80211_device *ieee = mac->ieee;
+ unsigned long flags;
+ s32 in_rate = data->bitrate.value;
+ u8 rate;
+ int is_ofdm = 0;
+ int err = -EINVAL;
+
+ if (in_rate == -1) {
+ /* FIXME: We don't correctly handle backing down to lower
+ rates, so 801.11g devices start off at 11M for now. People
+ can manually change it if they really need to, but 11M is
+ more reliable. Note similar logic in
+ ieee80211softmac_wx_set_rate() */
+ if (ieee->modulation & IEEE80211_CCK_MODULATION)
+ in_rate = 11000000;
+ else
+ in_rate = 54000000;
+ }
+
+ switch (in_rate) {
+ case 1000000:
+ rate = IEEE80211_CCK_RATE_1MB;
+ break;
+ case 2000000:
+ rate = IEEE80211_CCK_RATE_2MB;
+ break;
+ case 5500000:
+ rate = IEEE80211_CCK_RATE_5MB;
+ break;
+ case 11000000:
+ rate = IEEE80211_CCK_RATE_11MB;
+ break;
+ case 6000000:
+ rate = IEEE80211_OFDM_RATE_6MB;
+ is_ofdm = 1;
+ break;
+ case 9000000:
+ rate = IEEE80211_OFDM_RATE_9MB;
+ is_ofdm = 1;
+ break;
+ case 12000000:
+ rate = IEEE80211_OFDM_RATE_12MB;
+ is_ofdm = 1;
+ break;
+ case 18000000:
+ rate = IEEE80211_OFDM_RATE_18MB;
+ is_ofdm = 1;
+ break;
+ case 24000000:
+ rate = IEEE80211_OFDM_RATE_24MB;
+ is_ofdm = 1;
+ break;
+ case 36000000:
+ rate = IEEE80211_OFDM_RATE_36MB;
+ is_ofdm = 1;
+ break;
+ case 48000000:
+ rate = IEEE80211_OFDM_RATE_48MB;
+ is_ofdm = 1;
+ break;
+ case 54000000:
+ rate = IEEE80211_OFDM_RATE_54MB;
+ is_ofdm = 1;
+ break;
+ default:
+ goto out;
+ }
+
+ spin_lock_irqsave(&mac->lock, flags);
+
+ /* Check if correct modulation for this PHY. */
+ if (is_ofdm && !(ieee->modulation & IEEE80211_OFDM_MODULATION))
+ goto out_unlock;
+
+ mac->txrates.user_rate = rate;
+ ieee80211softmac_recalc_txrates(mac);
+ err = 0;
+
+out_unlock:
+ spin_unlock_irqrestore(&mac->lock, flags);
+out:
+ return err;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_rate);
+
+int
+ieee80211softmac_wx_get_rate(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
+ unsigned long flags;
+ int err = -EINVAL;
+
+ spin_lock_irqsave(&mac->lock, flags);
+
+ if (unlikely(!mac->running)) {
+ err = -ENODEV;
+ goto out_unlock;
+ }
+
+ switch (mac->txrates.default_rate) {
+ case IEEE80211_CCK_RATE_1MB:
+ data->bitrate.value = 1000000;
+ break;
+ case IEEE80211_CCK_RATE_2MB:
+ data->bitrate.value = 2000000;
+ break;
+ case IEEE80211_CCK_RATE_5MB:
+ data->bitrate.value = 5500000;
+ break;
+ case IEEE80211_CCK_RATE_11MB:
+ data->bitrate.value = 11000000;
+ break;
+ case IEEE80211_OFDM_RATE_6MB:
+ data->bitrate.value = 6000000;
+ break;
+ case IEEE80211_OFDM_RATE_9MB:
+ data->bitrate.value = 9000000;
+ break;
+ case IEEE80211_OFDM_RATE_12MB:
+ data->bitrate.value = 12000000;
+ break;
+ case IEEE80211_OFDM_RATE_18MB:
+ data->bitrate.value = 18000000;
+ break;
+ case IEEE80211_OFDM_RATE_24MB:
+ data->bitrate.value = 24000000;
+ break;
+ case IEEE80211_OFDM_RATE_36MB:
+ data->bitrate.value = 36000000;
+ break;
+ case IEEE80211_OFDM_RATE_48MB:
+ data->bitrate.value = 48000000;
+ break;
+ case IEEE80211_OFDM_RATE_54MB:
+ data->bitrate.value = 54000000;
+ break;
+ default:
+ assert(0);
+ goto out_unlock;
+ }
+ err = 0;
+out_unlock:
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_rate);
+
+int
+ieee80211softmac_wx_get_wap(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
+ int err = 0;
+
+ mutex_lock(&mac->associnfo.mutex);
+ if (mac->associnfo.bssvalid)
+ memcpy(data->ap_addr.sa_data, mac->associnfo.bssid, ETH_ALEN);
+ else
+ memset(data->ap_addr.sa_data, 0xff, ETH_ALEN);
+ data->ap_addr.sa_family = ARPHRD_ETHER;
+ mutex_unlock(&mac->associnfo.mutex);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_wap);
+
+int
+ieee80211softmac_wx_set_wap(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
+
+ /* sanity check */
+ if (data->ap_addr.sa_family != ARPHRD_ETHER) {
+ return -EINVAL;
+ }
+
+ mutex_lock(&mac->associnfo.mutex);
+ if (is_broadcast_ether_addr(data->ap_addr.sa_data)) {
+ /* the bssid we have is not to be fixed any longer,
+ * and we should reassociate to the best AP. */
+ mac->associnfo.bssfixed = 0;
+ /* force reassociation */
+ mac->associnfo.bssvalid = 0;
+ if (mac->associnfo.associated)
+ queue_delayed_work(mac->wq, &mac->associnfo.work, 0);
+ } else if (is_zero_ether_addr(data->ap_addr.sa_data)) {
+ /* the bssid we have is no longer fixed */
+ mac->associnfo.bssfixed = 0;
+ } else {
+ if (!memcmp(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN)) {
+ if (mac->associnfo.associating || mac->associnfo.associated) {
+ /* bssid unchanged and associated or associating - just return */
+ goto out;
+ }
+ } else {
+ /* copy new value in data->ap_addr.sa_data to bssid */
+ memcpy(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN);
+ }
+ /* tell the other code that this bssid should be used no matter what */
+ mac->associnfo.bssfixed = 1;
+ /* queue associate if new bssid or (old one again and not associated) */
+ queue_delayed_work(mac->wq, &mac->associnfo.work, 0);
+ }
+
+ out:
+ mutex_unlock(&mac->associnfo.mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_wap);
+
+int
+ieee80211softmac_wx_set_genie(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ unsigned long flags;
+ int err = 0;
+ char *buf;
+ int i;
+
+ mutex_lock(&mac->associnfo.mutex);
+ spin_lock_irqsave(&mac->lock, flags);
+ /* bleh. shouldn't be locked for that kmalloc... */
+
+ if (wrqu->data.length) {
+ if ((wrqu->data.length < 2) || (extra[1]+2 != wrqu->data.length)) {
+ /* this is an IE, so the length must be
+ * correct. Is it possible though that
+ * more than one IE is passed in?
+ */
+ err = -EINVAL;
+ goto out;
+ }
+ if (mac->wpa.IEbuflen <= wrqu->data.length) {
+ buf = kmalloc(wrqu->data.length, GFP_ATOMIC);
+ if (!buf) {
+ err = -ENOMEM;
+ goto out;
+ }
+ kfree(mac->wpa.IE);
+ mac->wpa.IE = buf;
+ mac->wpa.IEbuflen = wrqu->data.length;
+ }
+ memcpy(mac->wpa.IE, extra, wrqu->data.length);
+ dprintk(KERN_INFO PFX "generic IE set to ");
+ for (i=0;i<wrqu->data.length;i++)
+ dprintk("%.2x", (u8)mac->wpa.IE[i]);
+ dprintk("\n");
+ mac->wpa.IElen = wrqu->data.length;
+ } else {
+ kfree(mac->wpa.IE);
+ mac->wpa.IE = NULL;
+ mac->wpa.IElen = 0;
+ mac->wpa.IEbuflen = 0;
+ }
+
+ out:
+ spin_unlock_irqrestore(&mac->lock, flags);
+ mutex_unlock(&mac->associnfo.mutex);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_genie);
+
+int
+ieee80211softmac_wx_get_genie(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ unsigned long flags;
+ int err = 0;
+ int space = wrqu->data.length;
+
+ mutex_lock(&mac->associnfo.mutex);
+ spin_lock_irqsave(&mac->lock, flags);
+
+ wrqu->data.length = 0;
+
+ if (mac->wpa.IE && mac->wpa.IElen) {
+ wrqu->data.length = mac->wpa.IElen;
+ if (mac->wpa.IElen <= space)
+ memcpy(extra, mac->wpa.IE, mac->wpa.IElen);
+ else
+ err = -E2BIG;
+ }
+ spin_unlock_irqrestore(&mac->lock, flags);
+ mutex_unlock(&mac->associnfo.mutex);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_genie);
+
+int
+ieee80211softmac_wx_set_mlme(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ struct iw_mlme *mlme = (struct iw_mlme *)extra;
+ u16 reason = cpu_to_le16(mlme->reason_code);
+ struct ieee80211softmac_network *net;
+ int err = -EINVAL;
+
+ mutex_lock(&mac->associnfo.mutex);
+
+ if (memcmp(mac->associnfo.bssid, mlme->addr.sa_data, ETH_ALEN)) {
+ printk(KERN_DEBUG PFX "wx_set_mlme: requested operation on net we don't use\n");
+ goto out;
+ }
+
+ switch (mlme->cmd) {
+ case IW_MLME_DEAUTH:
+ net = ieee80211softmac_get_network_by_bssid_locked(mac, mlme->addr.sa_data);
+ if (!net) {
+ printk(KERN_DEBUG PFX "wx_set_mlme: we should know the net here...\n");
+ goto out;
+ }
+ err = ieee80211softmac_deauth_req(mac, net, reason);
+ goto out;
+ case IW_MLME_DISASSOC:
+ ieee80211softmac_send_disassoc_req(mac, reason);
+ mac->associnfo.associated = 0;
+ mac->associnfo.associating = 0;
+ err = 0;
+ goto out;
+ default:
+ err = -EOPNOTSUPP;
+ }
+
+out:
+ mutex_unlock(&mac->associnfo.mutex);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_mlme);
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index 691268f3a359..dbdf6d198524 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -145,6 +145,14 @@ config IP_PNP
on NFS" as well), because all other machines configure the network
in their startup scripts.
+config IP_PNP_DELAY
+ bool "Insert delay for initialization of network"
+ depends on IP_PNP
+ default y
+ help
+ Some network devices need a delay before setting the IP address for
+ them. On embedded systems that might not be necessary.
+
config IP_PNP_DHCP
bool "IP: DHCP support"
depends on IP_PNP
diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c
index 42065fff46c4..695be5573424 100644
--- a/net/ipv4/ipconfig.c
+++ b/net/ipv4/ipconfig.c
@@ -86,8 +86,13 @@
#endif
/* Define the friendly delay before and after opening net devices */
-#define CONF_PRE_OPEN 500 /* Before opening: 1/2 second */
-#define CONF_POST_OPEN 1 /* After opening: 1 second */
+#ifdef CONFIG_IP_PNP_DELAY
+# define CONF_PRE_OPEN 500 /* Before opening: 1/2 second */
+# define CONF_POST_OPEN 1 /* After opening: 1 second */
+#else
+# define CONF_PRE_OPEN 0
+# define CONF_POST_OPEN 0
+#endif /* CONFIG_IP_PNP_DELAY */
/* Define the timeout for waiting for a DHCP/BOOTP/RARP reply */
#define CONF_OPEN_RETRIES 2 /* (Re)open devices twice */
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index 7f710a27e91c..78d59a2e94e8 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -16,20 +16,30 @@ menu "Rate control algorithm selection"
config MAC80211_RC_PID
bool "PID controller based rate control algorithm" if EMBEDDED
- default y
---help---
This option enables a TX rate control algorithm for
mac80211 that uses a PID controller to select the TX
rate.
+config MAC80211_RC_MULTIRATE_PID
+ bool "Multirate PID controller based rate control algorithm" if EMBEDDED
+ ---help---
+ This option enables a TX rate control algorithm for
+ mac80211 that uses a PID controller to select the TX
+ rate. This version of the PID algorithm has been enhanced
+ to perform retries of packets at different rates.
+ This algorithm is recommended for the
+ ConnectCore Wi-9P 9215.
+
config MAC80211_RC_MINSTREL
- bool "Minstrel"
+ bool "Minstrel" if EMBEDDED
+ default y
---help---
This option enables the 'minstrel' TX rate control algorithm
choice
prompt "Default rate control algorithm"
- default MAC80211_RC_DEFAULT_PID
+ default MAC80211_RC_MULTIRATE_DEFAULT_PID
---help---
This option selects the default rate control algorithm
mac80211 will use. Note that this default can still be
@@ -41,8 +51,15 @@ config MAC80211_RC_DEFAULT_PID
depends on MAC80211_RC_PID
---help---
Select the PID controller based rate control as the
- default rate control algorithm. You should choose
- this unless you know what you are doing.
+ default rate control algorithm.
+
+config MAC80211_RC_MULTIRATE_DEFAULT_PID
+ bool "Multirate PID controller based rate control algorithm"
+ depends on MAC80211_RC_MULTIRATE_PID
+ ---help---
+ Select the multirate PID controller based rate control as the
+ default rate control algorithm. You should choose this
+ unless you know what you are doing.
config MAC80211_RC_DEFAULT_MINSTREL
bool "Minstrel"
@@ -55,8 +72,9 @@ endchoice
config MAC80211_RC_DEFAULT
string
- default "pid" if MAC80211_RC_DEFAULT_PID
default "minstrel" if MAC80211_RC_DEFAULT_MINSTREL
+ default "pid" if MAC80211_RC_DEFAULT_PID
+ default "multirate-pid" if MAC80211_RC_MULTIRATE_DEFAULT_PID
default ""
endmenu
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index 31cfd1f89a72..87e6dcb2d096 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -41,8 +41,14 @@ mac80211-$(CONFIG_MAC80211_MESH) += \
rc80211_pid-y := rc80211_pid_algo.o
rc80211_pid-$(CONFIG_MAC80211_DEBUGFS) += rc80211_pid_debugfs.o
+rc80211_multirate_pid-y := rc80211_multirate_pid_algo.o
+rc80211_multirate_pid-$(CONFIG_MAC80211_DEBUGFS) += rc80211_multirate_pid_debugfs.o
+
rc80211_minstrel-y := rc80211_minstrel.o
rc80211_minstrel-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_debugfs.o
mac80211-$(CONFIG_MAC80211_RC_PID) += $(rc80211_pid-y)
+mac80211-$(CONFIG_MAC80211_RC_MULTIRATE_PID) += $(rc80211_multirate_pid-y)
mac80211-$(CONFIG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y)
+
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 855126a3039d..9d4e4d846ec1 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -17,13 +17,6 @@
#include "rate.h"
#include "mesh.h"
-struct ieee80211_hw *wiphy_to_hw(struct wiphy *wiphy)
-{
- struct ieee80211_local *local = wiphy_priv(wiphy);
- return &local->hw;
-}
-EXPORT_SYMBOL(wiphy_to_hw);
-
static bool nl80211_type_check(enum nl80211_iftype type)
{
switch (type) {
@@ -33,6 +26,8 @@ static bool nl80211_type_check(enum nl80211_iftype type)
#ifdef CONFIG_MAC80211_MESH
case NL80211_IFTYPE_MESH_POINT:
#endif
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_WDS:
return true;
default:
@@ -315,12 +310,35 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
sinfo->filled = STATION_INFO_INACTIVE_TIME |
STATION_INFO_RX_BYTES |
- STATION_INFO_TX_BYTES;
+ STATION_INFO_TX_BYTES |
+ STATION_INFO_TX_BITRATE;
sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
sinfo->rx_bytes = sta->rx_bytes;
sinfo->tx_bytes = sta->tx_bytes;
+ if (sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) {
+ sinfo->filled |= STATION_INFO_SIGNAL;
+ sinfo->signal = (s8)sta->last_signal;
+ }
+
+ sinfo->txrate.flags = 0;
+ if (sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)
+ sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
+ if (sta->last_tx_rate.flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+ sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+ if (sta->last_tx_rate.flags & IEEE80211_TX_RC_SHORT_GI)
+ sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+
+ if (!(sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)) {
+ struct ieee80211_supported_band *sband;
+ sband = sta->local->hw.wiphy->bands[
+ sta->local->hw.conf.channel->band];
+ sinfo->txrate.legacy =
+ sband->bitrates[sta->last_tx_rate.idx].bitrate;
+ } else
+ sinfo->txrate.mcs = sta->last_tx_rate.idx;
+
if (ieee80211_vif_is_mesh(&sdata->vif)) {
#ifdef CONFIG_MAC80211_MESH
sinfo->filled |= STATION_INFO_LLID |
@@ -401,8 +419,10 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,
*/
if (params->interval) {
sdata->local->hw.conf.beacon_int = params->interval;
- if (ieee80211_hw_config(sdata->local))
- return -EINVAL;
+ err = ieee80211_hw_config(sdata->local,
+ IEEE80211_CONF_CHANGE_BEACON_INTERVAL);
+ if (err < 0)
+ return err;
/*
* We updated some parameter so if below bails out
* it's not an error.
@@ -589,6 +609,8 @@ static void sta_apply_parameters(struct ieee80211_local *local,
struct ieee80211_supported_band *sband;
struct ieee80211_sub_if_data *sdata = sta->sdata;
+ sband = local->hw.wiphy->bands[local->oper_channel->band];
+
/*
* FIXME: updating the flags is racy when this function is
* called from ieee80211_change_station(), this will
@@ -629,7 +651,6 @@ static void sta_apply_parameters(struct ieee80211_local *local,
if (params->supported_rates) {
rates = 0;
- sband = local->hw.wiphy->bands[local->oper_channel->band];
for (i = 0; i < params->supported_rates_len; i++) {
int rate = (params->supported_rates[i] & 0x7f) * 5;
@@ -641,10 +662,10 @@ static void sta_apply_parameters(struct ieee80211_local *local,
sta->sta.supp_rates[local->oper_channel->band] = rates;
}
- if (params->ht_capa) {
- ieee80211_ht_cap_ie_to_ht_info(params->ht_capa,
- &sta->sta.ht_info);
- }
+ if (params->ht_capa)
+ ieee80211_ht_cap_ie_to_sta_ht_cap(sband,
+ params->ht_capa,
+ &sta->sta.ht_cap);
if (ieee80211_vif_is_mesh(&sdata->vif) && params->plink_action) {
switch (params->plink_action) {
@@ -665,6 +686,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
struct sta_info *sta;
struct ieee80211_sub_if_data *sdata;
int err;
+ int layer2_update;
/* Prevent a race with changing the rate control algorithm */
if (!netif_running(dev))
@@ -695,17 +717,25 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
rate_control_rate_init(sta);
+ layer2_update = sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
+ sdata->vif.type == NL80211_IFTYPE_AP;
+
rcu_read_lock();
err = sta_info_insert(sta);
if (err) {
/* STA has been freed */
+ if (err == -EEXIST && layer2_update) {
+ /* Need to update layer 2 devices on reassociation */
+ sta = sta_info_get(local, mac);
+ if (sta)
+ ieee80211_send_layer2_update(sta);
+ }
rcu_read_unlock();
return err;
}
- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
- sdata->vif.type == NL80211_IFTYPE_AP)
+ if (layer2_update)
ieee80211_send_layer2_update(sta);
rcu_read_unlock();
@@ -957,6 +987,72 @@ static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
rcu_read_unlock();
return 0;
}
+
+static int ieee80211_get_mesh_params(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct mesh_config *conf)
+{
+ struct ieee80211_sub_if_data *sdata;
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+ memcpy(conf, &(sdata->u.mesh.mshcfg), sizeof(struct mesh_config));
+ return 0;
+}
+
+static inline bool _chg_mesh_attr(enum nl80211_meshconf_params parm, u32 mask)
+{
+ return (mask >> (parm-1)) & 0x1;
+}
+
+static int ieee80211_set_mesh_params(struct wiphy *wiphy,
+ struct net_device *dev,
+ const struct mesh_config *nconf, u32 mask)
+{
+ struct mesh_config *conf;
+ struct ieee80211_sub_if_data *sdata;
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ /* Set the config options which we are interested in setting */
+ conf = &(sdata->u.mesh.mshcfg);
+ if (_chg_mesh_attr(NL80211_MESHCONF_RETRY_TIMEOUT, mask))
+ conf->dot11MeshRetryTimeout = nconf->dot11MeshRetryTimeout;
+ if (_chg_mesh_attr(NL80211_MESHCONF_CONFIRM_TIMEOUT, mask))
+ conf->dot11MeshConfirmTimeout = nconf->dot11MeshConfirmTimeout;
+ if (_chg_mesh_attr(NL80211_MESHCONF_HOLDING_TIMEOUT, mask))
+ conf->dot11MeshHoldingTimeout = nconf->dot11MeshHoldingTimeout;
+ if (_chg_mesh_attr(NL80211_MESHCONF_MAX_PEER_LINKS, mask))
+ conf->dot11MeshMaxPeerLinks = nconf->dot11MeshMaxPeerLinks;
+ if (_chg_mesh_attr(NL80211_MESHCONF_MAX_RETRIES, mask))
+ conf->dot11MeshMaxRetries = nconf->dot11MeshMaxRetries;
+ if (_chg_mesh_attr(NL80211_MESHCONF_TTL, mask))
+ conf->dot11MeshTTL = nconf->dot11MeshTTL;
+ if (_chg_mesh_attr(NL80211_MESHCONF_AUTO_OPEN_PLINKS, mask))
+ conf->auto_open_plinks = nconf->auto_open_plinks;
+ if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, mask))
+ conf->dot11MeshHWMPmaxPREQretries =
+ nconf->dot11MeshHWMPmaxPREQretries;
+ if (_chg_mesh_attr(NL80211_MESHCONF_PATH_REFRESH_TIME, mask))
+ conf->path_refresh_time = nconf->path_refresh_time;
+ if (_chg_mesh_attr(NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, mask))
+ conf->min_discovery_timeout = nconf->min_discovery_timeout;
+ if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, mask))
+ conf->dot11MeshHWMPactivePathTimeout =
+ nconf->dot11MeshHWMPactivePathTimeout;
+ if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, mask))
+ conf->dot11MeshHWMPpreqMinInterval =
+ nconf->dot11MeshHWMPpreqMinInterval;
+ if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
+ mask))
+ conf->dot11MeshHWMPnetDiameterTraversalTime =
+ nconf->dot11MeshHWMPnetDiameterTraversalTime;
+ return 0;
+}
+
#endif
static int ieee80211_change_bss(struct wiphy *wiphy,
@@ -972,25 +1068,79 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
return -EINVAL;
if (params->use_cts_prot >= 0) {
- sdata->bss_conf.use_cts_prot = params->use_cts_prot;
+ sdata->vif.bss_conf.use_cts_prot = params->use_cts_prot;
changed |= BSS_CHANGED_ERP_CTS_PROT;
}
if (params->use_short_preamble >= 0) {
- sdata->bss_conf.use_short_preamble =
+ sdata->vif.bss_conf.use_short_preamble =
params->use_short_preamble;
changed |= BSS_CHANGED_ERP_PREAMBLE;
}
if (params->use_short_slot_time >= 0) {
- sdata->bss_conf.use_short_slot =
+ sdata->vif.bss_conf.use_short_slot =
params->use_short_slot_time;
changed |= BSS_CHANGED_ERP_SLOT;
}
+ if (params->basic_rates) {
+ int i, j;
+ u32 rates = 0;
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct ieee80211_supported_band *sband =
+ wiphy->bands[local->oper_channel->band];
+
+ for (i = 0; i < params->basic_rates_len; i++) {
+ int rate = (params->basic_rates[i] & 0x7f) * 5;
+ for (j = 0; j < sband->n_bitrates; j++) {
+ if (sband->bitrates[j].bitrate == rate)
+ rates |= BIT(j);
+ }
+ }
+ sdata->vif.bss_conf.basic_rates = rates;
+ changed |= BSS_CHANGED_BASIC_RATES;
+ }
+
ieee80211_bss_info_change_notify(sdata, changed);
return 0;
}
+static int ieee80211_set_txq_params(struct wiphy *wiphy,
+ struct ieee80211_txq_params *params)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct ieee80211_tx_queue_params p;
+
+ if (!local->ops->conf_tx)
+ return -EOPNOTSUPP;
+
+ memset(&p, 0, sizeof(p));
+ p.aifs = params->aifs;
+ p.cw_max = params->cwmax;
+ p.cw_min = params->cwmin;
+ p.txop = params->txop;
+ if (local->ops->conf_tx(local_to_hw(local), params->queue, &p)) {
+ printk(KERN_DEBUG "%s: failed to set TX queue "
+ "parameters for queue %d\n", local->mdev->name,
+ params->queue);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ieee80211_set_channel(struct wiphy *wiphy,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type channel_type)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+
+ local->oper_channel = chan;
+ local->oper_channel_type = channel_type;
+
+ return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+}
+
struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
@@ -1013,6 +1163,10 @@ struct cfg80211_ops mac80211_config_ops = {
.change_mpath = ieee80211_change_mpath,
.get_mpath = ieee80211_get_mpath,
.dump_mpath = ieee80211_dump_mpath,
+ .set_mesh_params = ieee80211_set_mesh_params,
+ .get_mesh_params = ieee80211_get_mesh_params,
#endif
.change_bss = ieee80211_change_bss,
+ .set_txq_params = ieee80211_set_txq_params,
+ .set_channel = ieee80211_set_channel,
};
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 24ce54463310..2697a2fe608f 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -47,18 +47,14 @@ static const struct file_operations name## _ops = { \
DEBUGFS_READONLY_FILE(frequency, 20, "%d",
local->hw.conf.channel->center_freq);
-DEBUGFS_READONLY_FILE(antenna_sel_tx, 20, "%d",
- local->hw.conf.antenna_sel_tx);
-DEBUGFS_READONLY_FILE(antenna_sel_rx, 20, "%d",
- local->hw.conf.antenna_sel_rx);
DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d",
local->rts_threshold);
DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d",
local->fragmentation_threshold);
DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d",
- local->short_retry_limit);
+ local->hw.conf.short_frame_max_tx_count);
DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d",
- local->long_retry_limit);
+ local->hw.conf.long_frame_max_tx_count);
DEBUGFS_READONLY_FILE(total_ps_buffered, 20, "%d",
local->total_ps_buffered);
DEBUGFS_READONLY_FILE(wep_iv, 20, "%#06x",
@@ -202,8 +198,6 @@ void debugfs_hw_add(struct ieee80211_local *local)
local->debugfs.keys = debugfs_create_dir("keys", phyd);
DEBUGFS_ADD(frequency);
- DEBUGFS_ADD(antenna_sel_tx);
- DEBUGFS_ADD(antenna_sel_rx);
DEBUGFS_ADD(rts_threshold);
DEBUGFS_ADD(fragmentation_threshold);
DEBUGFS_ADD(short_retry_limit);
@@ -258,8 +252,6 @@ void debugfs_hw_add(struct ieee80211_local *local)
void debugfs_hw_del(struct ieee80211_local *local)
{
DEBUGFS_DEL(frequency);
- DEBUGFS_DEL(antenna_sel_tx);
- DEBUGFS_DEL(antenna_sel_rx);
DEBUGFS_DEL(rts_threshold);
DEBUGFS_DEL(fragmentation_threshold);
DEBUGFS_DEL(short_retry_limit);
diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c
index a3294d109322..6424ac565ae0 100644
--- a/net/mac80211/debugfs_key.c
+++ b/net/mac80211/debugfs_key.c
@@ -188,7 +188,6 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key)
{
static int keycount;
char buf[50];
- DECLARE_MAC_BUF(mac);
struct sta_info *sta;
if (!key->local->debugfs.keys)
@@ -206,8 +205,7 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key)
rcu_read_lock();
sta = rcu_dereference(key->sta);
if (sta)
- sprintf(buf, "../../stations/%s",
- print_mac(mac, sta->sta.addr));
+ sprintf(buf, "../../stations/%pM", sta->sta.addr);
rcu_read_unlock();
/* using sta as a boolean is fine outside RCU lock */
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index 2ad504fc3414..c54219301724 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -41,29 +41,6 @@ static ssize_t ieee80211_if_read(
return ret;
}
-#ifdef CONFIG_MAC80211_MESH
-static ssize_t ieee80211_if_write(
- struct ieee80211_sub_if_data *sdata,
- char const __user *userbuf,
- size_t count, loff_t *ppos,
- int (*format)(struct ieee80211_sub_if_data *, char *))
-{
- char buf[10];
- int buf_size;
-
- memset(buf, 0x00, sizeof(buf));
- buf_size = min(count, (sizeof(buf)-1));
- if (copy_from_user(buf, userbuf, buf_size))
- return count;
- read_lock(&dev_base_lock);
- if (sdata->dev->reg_state == NETREG_REGISTERED)
- (*format)(sdata, buf);
- read_unlock(&dev_base_lock);
-
- return count;
-}
-#endif
-
#define IEEE80211_IF_FMT(name, field, format_string) \
static ssize_t ieee80211_if_fmt_##name( \
const struct ieee80211_sub_if_data *sdata, char *buf, \
@@ -71,19 +48,6 @@ static ssize_t ieee80211_if_fmt_##name( \
{ \
return scnprintf(buf, buflen, format_string, sdata->field); \
}
-#define IEEE80211_IF_WFMT(name, field, type) \
-static int ieee80211_if_wfmt_##name( \
- struct ieee80211_sub_if_data *sdata, char *buf) \
-{ \
- unsigned long tmp; \
- char *endp; \
- \
- tmp = simple_strtoul(buf, &endp, 0); \
- if ((endp == buf) || ((type)tmp != tmp)) \
- return -EINVAL; \
- sdata->field = tmp; \
- return 0; \
-}
#define IEEE80211_IF_FMT_DEC(name, field) \
IEEE80211_IF_FMT(name, field, "%d\n")
#define IEEE80211_IF_FMT_HEX(name, field) \
@@ -104,8 +68,7 @@ static ssize_t ieee80211_if_fmt_##name( \
const struct ieee80211_sub_if_data *sdata, char *buf, \
int buflen) \
{ \
- DECLARE_MAC_BUF(mac); \
- return scnprintf(buf, buflen, "%s\n", print_mac(mac, sdata->field));\
+ return scnprintf(buf, buflen, "%pM\n", sdata->field); \
}
#define __IEEE80211_IF_FILE(name) \
@@ -126,34 +89,6 @@ static const struct file_operations name##_ops = { \
IEEE80211_IF_FMT_##format(name, field) \
__IEEE80211_IF_FILE(name)
-#define __IEEE80211_IF_WFILE(name) \
-static ssize_t ieee80211_if_read_##name(struct file *file, \
- char __user *userbuf, \
- size_t count, loff_t *ppos) \
-{ \
- return ieee80211_if_read(file->private_data, \
- userbuf, count, ppos, \
- ieee80211_if_fmt_##name); \
-} \
-static ssize_t ieee80211_if_write_##name(struct file *file, \
- const char __user *userbuf, \
- size_t count, loff_t *ppos) \
-{ \
- return ieee80211_if_write(file->private_data, \
- userbuf, count, ppos, \
- ieee80211_if_wfmt_##name); \
-} \
-static const struct file_operations name##_ops = { \
- .read = ieee80211_if_read_##name, \
- .write = ieee80211_if_write_##name, \
- .open = mac80211_open_file_generic, \
-}
-
-#define IEEE80211_IF_WFILE(name, field, format, type) \
- IEEE80211_IF_FMT_##format(name, field) \
- IEEE80211_IF_WFMT(name, field, type) \
- __IEEE80211_IF_WFILE(name)
-
/* common attributes */
IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC);
IEEE80211_IF_FILE(force_unicast_rateidx, force_unicast_rateidx, DEC);
@@ -184,7 +119,7 @@ static ssize_t ieee80211_if_fmt_flags(
sdata->u.sta.flags & IEEE80211_STA_AUTHENTICATED ? "AUTH\n" : "",
sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED ? "ASSOC\n" : "",
sdata->u.sta.flags & IEEE80211_STA_PROBEREQ_POLL ? "PROBEREQ POLL\n" : "",
- sdata->bss_conf.use_cts_prot ? "CTS prot\n" : "");
+ sdata->vif.bss_conf.use_cts_prot ? "CTS prot\n" : "");
}
__IEEE80211_IF_FILE(flags);
@@ -212,30 +147,30 @@ IEEE80211_IF_FILE(dropped_frames_no_route,
IEEE80211_IF_FILE(estab_plinks, u.mesh.mshstats.estab_plinks, ATOMIC);
/* Mesh parameters */
-IEEE80211_IF_WFILE(dot11MeshMaxRetries,
- u.mesh.mshcfg.dot11MeshMaxRetries, DEC, u8);
-IEEE80211_IF_WFILE(dot11MeshRetryTimeout,
- u.mesh.mshcfg.dot11MeshRetryTimeout, DEC, u16);
-IEEE80211_IF_WFILE(dot11MeshConfirmTimeout,
- u.mesh.mshcfg.dot11MeshConfirmTimeout, DEC, u16);
-IEEE80211_IF_WFILE(dot11MeshHoldingTimeout,
- u.mesh.mshcfg.dot11MeshHoldingTimeout, DEC, u16);
-IEEE80211_IF_WFILE(dot11MeshTTL, u.mesh.mshcfg.dot11MeshTTL, DEC, u8);
-IEEE80211_IF_WFILE(auto_open_plinks, u.mesh.mshcfg.auto_open_plinks, DEC, u8);
-IEEE80211_IF_WFILE(dot11MeshMaxPeerLinks,
- u.mesh.mshcfg.dot11MeshMaxPeerLinks, DEC, u16);
-IEEE80211_IF_WFILE(dot11MeshHWMPactivePathTimeout,
- u.mesh.mshcfg.dot11MeshHWMPactivePathTimeout, DEC, u32);
-IEEE80211_IF_WFILE(dot11MeshHWMPpreqMinInterval,
- u.mesh.mshcfg.dot11MeshHWMPpreqMinInterval, DEC, u16);
-IEEE80211_IF_WFILE(dot11MeshHWMPnetDiameterTraversalTime,
- u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime, DEC, u16);
-IEEE80211_IF_WFILE(dot11MeshHWMPmaxPREQretries,
- u.mesh.mshcfg.dot11MeshHWMPmaxPREQretries, DEC, u8);
-IEEE80211_IF_WFILE(path_refresh_time,
- u.mesh.mshcfg.path_refresh_time, DEC, u32);
-IEEE80211_IF_WFILE(min_discovery_timeout,
- u.mesh.mshcfg.min_discovery_timeout, DEC, u16);
+IEEE80211_IF_FILE(dot11MeshMaxRetries,
+ u.mesh.mshcfg.dot11MeshMaxRetries, DEC);
+IEEE80211_IF_FILE(dot11MeshRetryTimeout,
+ u.mesh.mshcfg.dot11MeshRetryTimeout, DEC);
+IEEE80211_IF_FILE(dot11MeshConfirmTimeout,
+ u.mesh.mshcfg.dot11MeshConfirmTimeout, DEC);
+IEEE80211_IF_FILE(dot11MeshHoldingTimeout,
+ u.mesh.mshcfg.dot11MeshHoldingTimeout, DEC);
+IEEE80211_IF_FILE(dot11MeshTTL, u.mesh.mshcfg.dot11MeshTTL, DEC);
+IEEE80211_IF_FILE(auto_open_plinks, u.mesh.mshcfg.auto_open_plinks, DEC);
+IEEE80211_IF_FILE(dot11MeshMaxPeerLinks,
+ u.mesh.mshcfg.dot11MeshMaxPeerLinks, DEC);
+IEEE80211_IF_FILE(dot11MeshHWMPactivePathTimeout,
+ u.mesh.mshcfg.dot11MeshHWMPactivePathTimeout, DEC);
+IEEE80211_IF_FILE(dot11MeshHWMPpreqMinInterval,
+ u.mesh.mshcfg.dot11MeshHWMPpreqMinInterval, DEC);
+IEEE80211_IF_FILE(dot11MeshHWMPnetDiameterTraversalTime,
+ u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime, DEC);
+IEEE80211_IF_FILE(dot11MeshHWMPmaxPREQretries,
+ u.mesh.mshcfg.dot11MeshHWMPmaxPREQretries, DEC);
+IEEE80211_IF_FILE(path_refresh_time,
+ u.mesh.mshcfg.path_refresh_time, DEC);
+IEEE80211_IF_FILE(min_discovery_timeout,
+ u.mesh.mshcfg.min_discovery_timeout, DEC);
#endif
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index b85c4f27b361..a2fbe0131312 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -39,13 +39,6 @@ static const struct file_operations sta_ ##name## _ops = { \
.open = mac80211_open_file_generic, \
}
-#define STA_OPS_WR(name) \
-static const struct file_operations sta_ ##name## _ops = { \
- .read = sta_##name##_read, \
- .write = sta_##name##_write, \
- .open = mac80211_open_file_generic, \
-}
-
#define STA_FILE(name, field, format) \
STA_READ_##format(name, field) \
STA_OPS(name)
@@ -144,7 +137,7 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
p += scnprintf(p, sizeof(buf)+buf-p, "\n DTKN:");
for (i = 0; i < STA_TID_NUM; i++)
p += scnprintf(p, sizeof(buf)+buf-p, "%5d",
- sta->ampdu_mlme.tid_state_rx[i]?
+ sta->ampdu_mlme.tid_state_rx[i] ?
sta->ampdu_mlme.tid_rx[i]->dialog_token : 0);
p += scnprintf(p, sizeof(buf)+buf-p, "\n TX :");
@@ -155,84 +148,20 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
p += scnprintf(p, sizeof(buf)+buf-p, "\n DTKN:");
for (i = 0; i < STA_TID_NUM; i++)
p += scnprintf(p, sizeof(buf)+buf-p, "%5d",
- sta->ampdu_mlme.tid_state_tx[i]?
+ sta->ampdu_mlme.tid_state_tx[i] ?
sta->ampdu_mlme.tid_tx[i]->dialog_token : 0);
p += scnprintf(p, sizeof(buf)+buf-p, "\n SSN :");
for (i = 0; i < STA_TID_NUM; i++)
p += scnprintf(p, sizeof(buf)+buf-p, "%5d",
- sta->ampdu_mlme.tid_state_tx[i]?
+ sta->ampdu_mlme.tid_state_tx[i] ?
sta->ampdu_mlme.tid_tx[i]->ssn : 0);
p += scnprintf(p, sizeof(buf)+buf-p, "\n");
return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
}
-
-static ssize_t sta_agg_status_write(struct file *file,
- const char __user *user_buf, size_t count, loff_t *ppos)
-{
- struct sta_info *sta = file->private_data;
- struct ieee80211_local *local = sta->sdata->local;
- struct ieee80211_hw *hw = &local->hw;
- u8 *da = sta->sta.addr;
- static int tid_static_tx[16] = {0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0};
- static int tid_static_rx[16] = {1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1};
- char *endp;
- char buf[32];
- int buf_size, rs;
- unsigned int tid_num;
- char state[4];
-
- memset(buf, 0x00, sizeof(buf));
- buf_size = min(count, (sizeof(buf)-1));
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
-
- tid_num = simple_strtoul(buf, &endp, 0);
- if (endp == buf)
- return -EINVAL;
-
- if ((tid_num >= 100) && (tid_num <= 115)) {
- /* toggle Rx aggregation command */
- tid_num = tid_num - 100;
- if (tid_static_rx[tid_num] == 1) {
- strcpy(state, "off");
- ieee80211_sta_stop_rx_ba_session(sta->sdata, da, tid_num, 0,
- WLAN_REASON_QSTA_REQUIRE_SETUP);
- sta->ampdu_mlme.tid_state_rx[tid_num] |=
- HT_AGG_STATE_DEBUGFS_CTL;
- tid_static_rx[tid_num] = 0;
- } else {
- strcpy(state, "on ");
- sta->ampdu_mlme.tid_state_rx[tid_num] &=
- ~HT_AGG_STATE_DEBUGFS_CTL;
- tid_static_rx[tid_num] = 1;
- }
- printk(KERN_DEBUG "debugfs - try switching tid %u %s\n",
- tid_num, state);
- } else if ((tid_num >= 0) && (tid_num <= 15)) {
- /* toggle Tx aggregation command */
- if (tid_static_tx[tid_num] == 0) {
- strcpy(state, "on ");
- rs = ieee80211_start_tx_ba_session(hw, da, tid_num);
- if (rs == 0)
- tid_static_tx[tid_num] = 1;
- } else {
- strcpy(state, "off");
- rs = ieee80211_stop_tx_ba_session(hw, da, tid_num, 1);
- if (rs == 0)
- tid_static_tx[tid_num] = 0;
- }
- printk(KERN_DEBUG "debugfs - switching tid %u %s, return=%d\n",
- tid_num, state, rs);
- }
-
- return count;
-}
-STA_OPS_WR(agg_status);
+STA_OPS(agg_status);
#define DEBUGFS_ADD(name) \
sta->debugfs.name = debugfs_create_file(#name, 0400, \
@@ -246,15 +175,14 @@ STA_OPS_WR(agg_status);
void ieee80211_sta_debugfs_add(struct sta_info *sta)
{
struct dentry *stations_dir = sta->local->debugfs.stations;
- DECLARE_MAC_BUF(mbuf);
- u8 *mac;
+ u8 mac[3*ETH_ALEN];
sta->debugfs.add_has_run = true;
if (!stations_dir)
return;
- mac = print_mac(mbuf, sta->sta.addr);
+ snprintf(mac, sizeof(mac), "%pM", sta->sta.addr);
/*
* This might fail due to a race condition:
diff --git a/net/mac80211/event.c b/net/mac80211/event.c
index 8de60de70bc9..0d95561c0ee0 100644
--- a/net/mac80211/event.c
+++ b/net/mac80211/event.c
@@ -21,14 +21,13 @@ void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int ke
{
union iwreq_data wrqu;
char *buf = kmalloc(128, GFP_ATOMIC);
- DECLARE_MAC_BUF(mac);
if (buf) {
/* TODO: needed parameters: count, key type, TSC */
sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
- "keyid=%d %scast addr=%s)",
+ "keyid=%d %scast addr=%pM)",
keyidx, hdr->addr1[0] & 0x01 ? "broad" : "uni",
- print_mac(mac, hdr->addr2));
+ hdr->addr2);
memset(&wrqu, 0, sizeof(wrqu));
wrqu.data.length = strlen(buf);
wireless_send_event(sdata->dev, IWEVCUSTOM, &wrqu, buf);
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index dc7d9a3d70d5..c5c0c5271096 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -20,50 +20,138 @@
#include "sta_info.h"
#include "wme.h"
-int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie,
- struct ieee80211_ht_info *ht_info)
+void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband,
+ struct ieee80211_ht_cap *ht_cap_ie,
+ struct ieee80211_sta_ht_cap *ht_cap)
{
+ u8 ampdu_info, tx_mcs_set_cap;
+ int i, max_tx_streams;
- if (ht_info == NULL)
- return -EINVAL;
+ BUG_ON(!ht_cap);
+
+ memset(ht_cap, 0, sizeof(*ht_cap));
+
+ if (!ht_cap_ie)
+ return;
+
+ ht_cap->ht_supported = true;
- memset(ht_info, 0, sizeof(*ht_info));
+ ht_cap->cap = le16_to_cpu(ht_cap_ie->cap_info) & sband->ht_cap.cap;
+ ht_cap->cap &= ~IEEE80211_HT_CAP_SM_PS;
+ ht_cap->cap |= sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS;
- if (ht_cap_ie) {
- u8 ampdu_info = ht_cap_ie->ampdu_params_info;
+ ampdu_info = ht_cap_ie->ampdu_params_info;
+ ht_cap->ampdu_factor =
+ ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR;
+ ht_cap->ampdu_density =
+ (ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2;
- ht_info->ht_supported = 1;
- ht_info->cap = le16_to_cpu(ht_cap_ie->cap_info);
- ht_info->ampdu_factor =
- ampdu_info & IEEE80211_HT_CAP_AMPDU_FACTOR;
- ht_info->ampdu_density =
- (ampdu_info & IEEE80211_HT_CAP_AMPDU_DENSITY) >> 2;
- memcpy(ht_info->supp_mcs_set, ht_cap_ie->supp_mcs_set, 16);
- } else
- ht_info->ht_supported = 0;
+ /* own MCS TX capabilities */
+ tx_mcs_set_cap = sband->ht_cap.mcs.tx_params;
- return 0;
+ /* can we TX with MCS rates? */
+ if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED))
+ return;
+
+ /* Counting from 0, therefore +1 */
+ if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF)
+ max_tx_streams =
+ ((tx_mcs_set_cap & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
+ >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1;
+ else
+ max_tx_streams = IEEE80211_HT_MCS_TX_MAX_STREAMS;
+
+ /*
+ * 802.11n D5.0 20.3.5 / 20.6 says:
+ * - indices 0 to 7 and 32 are single spatial stream
+ * - 8 to 31 are multiple spatial streams using equal modulation
+ * [8..15 for two streams, 16..23 for three and 24..31 for four]
+ * - remainder are multiple spatial streams using unequal modulation
+ */
+ for (i = 0; i < max_tx_streams; i++)
+ ht_cap->mcs.rx_mask[i] =
+ sband->ht_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i];
+
+ if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION)
+ for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE;
+ i < IEEE80211_HT_MCS_MASK_LEN; i++)
+ ht_cap->mcs.rx_mask[i] =
+ sband->ht_cap.mcs.rx_mask[i] &
+ ht_cap_ie->mcs.rx_mask[i];
+
+ /* handle MCS rate 32 too */
+ if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
+ ht_cap->mcs.rx_mask[32/8] |= 1;
}
-int ieee80211_ht_addt_info_ie_to_ht_bss_info(
- struct ieee80211_ht_addt_info *ht_add_info_ie,
- struct ieee80211_ht_bss_info *bss_info)
+/*
+ * ieee80211_enable_ht should be called only after the operating band
+ * has been determined as ht configuration depends on the hw's
+ * HT abilities for a specific band.
+ */
+u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_ht_info *hti,
+ u16 ap_ht_cap_flags)
{
- if (bss_info == NULL)
- return -EINVAL;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_bss_ht_conf ht;
+ u32 changed = 0;
+ bool enable_ht = true, ht_changed;
+ enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
+
+ sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+
+ memset(&ht, 0, sizeof(ht));
+
+ /* HT is not supported */
+ if (!sband->ht_cap.ht_supported)
+ enable_ht = false;
+
+ /* check that channel matches the right operating channel */
+ if (local->hw.conf.channel->center_freq !=
+ ieee80211_channel_to_frequency(hti->control_chan))
+ enable_ht = false;
+
+ if (enable_ht) {
+ channel_type = NL80211_CHAN_HT20;
+
+ if (!(ap_ht_cap_flags & IEEE80211_HT_CAP_40MHZ_INTOLERANT) &&
+ (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
+ (hti->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) {
+ switch(hti->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ channel_type = NL80211_CHAN_HT40PLUS;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ channel_type = NL80211_CHAN_HT40MINUS;
+ break;
+ }
+ }
+ }
+
+ ht_changed = local->hw.conf.ht.enabled != enable_ht ||
+ channel_type != local->hw.conf.ht.channel_type;
+
+ local->oper_channel_type = channel_type;
+ local->hw.conf.ht.enabled = enable_ht;
- memset(bss_info, 0, sizeof(*bss_info));
+ if (ht_changed)
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_HT);
- if (ht_add_info_ie) {
- u16 op_mode;
- op_mode = le16_to_cpu(ht_add_info_ie->operation_mode);
+ /* disable HT */
+ if (!enable_ht)
+ return 0;
- bss_info->primary_channel = ht_add_info_ie->control_chan;
- bss_info->bss_cap = ht_add_info_ie->ht_param;
- bss_info->bss_op_mode = (u8)(op_mode & 0xff);
+ ht.operation_mode = le16_to_cpu(hti->operation_mode);
+
+ /* if bss configuration changed store the new one */
+ if (memcmp(&sdata->vif.bss_conf.ht, &ht, sizeof(ht))) {
+ changed |= BSS_CHANGED_HT;
+ sdata->vif.bss_conf.ht = ht;
}
- return 0;
+ return changed;
}
static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
@@ -241,7 +329,6 @@ void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *r
struct ieee80211_hw *hw = &local->hw;
struct sta_info *sta;
int ret, i;
- DECLARE_MAC_BUF(mac);
rcu_read_lock();
@@ -269,8 +356,8 @@ void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *r
BUG_ON(!local->ops->ampdu_action);
#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Rx BA session stop requested for %s tid %u\n",
- print_mac(mac, ra), tid);
+ printk(KERN_DEBUG "Rx BA session stop requested for %pM tid %u\n",
+ ra, tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_RX_STOP,
@@ -382,15 +469,14 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
struct ieee80211_sub_if_data *sdata;
u16 start_seq_num;
u8 *state;
- int ret;
- DECLARE_MAC_BUF(mac);
+ int ret = 0;
- if (tid >= STA_TID_NUM)
+ if ((tid >= STA_TID_NUM) || !(hw->flags & IEEE80211_HW_AMPDU_AGGREGATION))
return -EINVAL;
#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Open BA session requested for %s tid %u\n",
- print_mac(mac, ra), tid);
+ printk(KERN_DEBUG "Open BA session requested for %pM tid %u\n",
+ ra, tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
rcu_read_lock();
@@ -442,17 +528,19 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
(unsigned long)&sta->timer_to_tid[tid];
init_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
- /* create a new queue for this aggregation */
- ret = ieee80211_ht_agg_queue_add(local, sta, tid);
+ if (hw->ampdu_queues) {
+ /* create a new queue for this aggregation */
+ ret = ieee80211_ht_agg_queue_add(local, sta, tid);
- /* case no queue is available to aggregation
- * don't switch to aggregation */
- if (ret) {
+ /* case no queue is available to aggregation
+ * don't switch to aggregation */
+ if (ret) {
#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "BA request denied - queue unavailable for"
- " tid %d\n", tid);
+ printk(KERN_DEBUG "BA request denied - "
+ "queue unavailable for tid %d\n", tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
- goto err_unlock_queue;
+ goto err_unlock_queue;
+ }
}
sdata = sta->sdata;
@@ -471,7 +559,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
/* No need to requeue the packets in the agg queue, since we
* held the tx lock: no packet could be enqueued to the newly
* allocated queue */
- ieee80211_ht_agg_queue_remove(local, sta, tid, 0);
+ if (hw->ampdu_queues)
+ ieee80211_ht_agg_queue_remove(local, sta, tid, 0);
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "BA request denied - HW unavailable for"
" tid %d\n", tid);
@@ -481,7 +570,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
}
/* Will put all the packets in the new SW queue */
- ieee80211_requeue(local, ieee802_1d_to_ac[tid]);
+ if (hw->ampdu_queues)
+ ieee80211_requeue(local, ieee802_1d_to_ac[tid]);
spin_unlock_bh(&sta->lock);
/* send an addBA request */
@@ -524,7 +614,6 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
struct sta_info *sta;
u8 *state;
int ret = 0;
- DECLARE_MAC_BUF(mac);
if (tid >= STA_TID_NUM)
return -EINVAL;
@@ -546,11 +635,12 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
}
#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Tx BA session stop requested for %s tid %u\n",
- print_mac(mac, ra), tid);
+ printk(KERN_DEBUG "Tx BA session stop requested for %pM tid %u\n",
+ ra, tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
- ieee80211_stop_queue(hw, sta->tid_to_tx_q[tid]);
+ if (hw->ampdu_queues)
+ ieee80211_stop_queue(hw, sta->tid_to_tx_q[tid]);
*state = HT_AGG_STATE_REQ_STOP_BA_MSK |
(initiator << HT_AGG_STATE_INITIATOR_SHIFT);
@@ -563,7 +653,8 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
if (ret) {
WARN_ON(ret != -EBUSY);
*state = HT_AGG_STATE_OPERATIONAL;
- ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
+ if (hw->ampdu_queues)
+ ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
goto stop_BA_exit;
}
@@ -579,7 +670,6 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
struct ieee80211_local *local = hw_to_local(hw);
struct sta_info *sta;
u8 *state;
- DECLARE_MAC_BUF(mac);
if (tid >= STA_TID_NUM) {
#ifdef CONFIG_MAC80211_HT_DEBUG
@@ -594,8 +684,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
if (!sta) {
rcu_read_unlock();
#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Could not find station: %s\n",
- print_mac(mac, ra));
+ printk(KERN_DEBUG "Could not find station: %pM\n", ra);
#endif
return;
}
@@ -621,7 +710,8 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid);
#endif
- ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
+ if (hw->ampdu_queues)
+ ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
}
spin_unlock_bh(&sta->lock);
rcu_read_unlock();
@@ -634,7 +724,6 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
struct sta_info *sta;
u8 *state;
int agg_queue;
- DECLARE_MAC_BUF(mac);
if (tid >= STA_TID_NUM) {
#ifdef CONFIG_MAC80211_HT_DEBUG
@@ -645,16 +734,15 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
}
#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Stopping Tx BA session for %s tid %d\n",
- print_mac(mac, ra), tid);
+ printk(KERN_DEBUG "Stopping Tx BA session for %pM tid %d\n",
+ ra, tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
rcu_read_lock();
sta = sta_info_get(local, ra);
if (!sta) {
#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Could not find station: %s\n",
- print_mac(mac, ra));
+ printk(KERN_DEBUG "Could not find station: %pM\n", ra);
#endif
rcu_read_unlock();
return;
@@ -677,16 +765,18 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
ieee80211_send_delba(sta->sdata, ra, tid,
WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
- agg_queue = sta->tid_to_tx_q[tid];
-
- ieee80211_ht_agg_queue_remove(local, sta, tid, 1);
-
- /* We just requeued the all the frames that were in the
- * removed queue, and since we might miss a softirq we do
- * netif_schedule_queue. ieee80211_wake_queue is not used
- * here as this queue is not necessarily stopped
- */
- netif_schedule_queue(netdev_get_tx_queue(local->mdev, agg_queue));
+ if (hw->ampdu_queues) {
+ agg_queue = sta->tid_to_tx_q[tid];
+ ieee80211_ht_agg_queue_remove(local, sta, tid, 1);
+
+ /* We just requeued the all the frames that were in the
+ * removed queue, and since we might miss a softirq we do
+ * netif_schedule_queue. ieee80211_wake_queue is not used
+ * here as this queue is not necessarily stopped
+ */
+ netif_schedule_queue(netdev_get_tx_queue(local->mdev,
+ agg_queue));
+ }
spin_lock_bh(&sta->lock);
*state = HT_AGG_STATE_IDLE;
sta->ampdu_mlme.addba_req_num[tid] = 0;
@@ -783,7 +873,6 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num, status;
u8 dialog_token;
int ret = -EOPNOTSUPP;
- DECLARE_MAC_BUF(mac);
/* extract session parameters from addba request frame */
dialog_token = mgmt->u.action.u.addba_req.dialog_token;
@@ -801,15 +890,16 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
/* sanity check for incoming parameters:
* check if configuration can support the BA policy
* and if buffer size does not exceeds max value */
+ /* XXX: check own ht delayed BA capability?? */
if (((ba_policy != 1)
- && (!(conf->ht_conf.cap & IEEE80211_HT_CAP_DELAY_BA)))
+ && (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_DELAY_BA)))
|| (buf_size > IEEE80211_MAX_AMPDU_BUF)) {
status = WLAN_STATUS_INVALID_QOS_PARAM;
#ifdef CONFIG_MAC80211_HT_DEBUG
if (net_ratelimit())
printk(KERN_DEBUG "AddBA Req with bad params from "
- "%s on tid %u. policy %d, buffer size %d\n",
- print_mac(mac, mgmt->sa), tid, ba_policy,
+ "%pM on tid %u. policy %d, buffer size %d\n",
+ mgmt->sa, tid, ba_policy,
buf_size);
#endif /* CONFIG_MAC80211_HT_DEBUG */
goto end_no_lock;
@@ -820,7 +910,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
sband = local->hw.wiphy->bands[conf->channel->band];
buf_size = IEEE80211_MIN_AMPDU_BUF;
- buf_size = buf_size << sband->ht_info.ampdu_factor;
+ buf_size = buf_size << sband->ht_cap.ampdu_factor;
}
@@ -831,8 +921,8 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
#ifdef CONFIG_MAC80211_HT_DEBUG
if (net_ratelimit())
printk(KERN_DEBUG "unexpected AddBA Req from "
- "%s on tid %u\n",
- print_mac(mac, mgmt->sa), tid);
+ "%pM on tid %u\n",
+ mgmt->sa, tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
goto end;
}
@@ -910,7 +1000,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
{
struct ieee80211_hw *hw = &local->hw;
u16 capab;
- u16 tid;
+ u16 tid, start_seq_num;
u8 *state;
capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab);
@@ -943,9 +1033,18 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
*state |= HT_ADDBA_RECEIVED_MSK;
sta->ampdu_mlme.addba_req_num[tid] = 0;
- if (*state == HT_AGG_STATE_OPERATIONAL)
+ if (*state == HT_AGG_STATE_OPERATIONAL &&
+ local->hw.ampdu_queues)
ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
+ if (local->ops->ampdu_action) {
+ (void)local->ops->ampdu_action(hw,
+ IEEE80211_AMPDU_TX_RESUME,
+ &sta->sta, tid, &start_seq_num);
+ }
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "Resuming TX aggregation for tid %d\n", tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
spin_unlock_bh(&sta->lock);
} else {
sta->ampdu_mlme.addba_req_num[tid]++;
@@ -964,7 +1063,6 @@ void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
u16 tid, params;
u16 initiator;
- DECLARE_MAC_BUF(mac);
params = le16_to_cpu(mgmt->u.action.u.delba.params);
tid = (params & IEEE80211_DELBA_PARAM_TID_MASK) >> 12;
@@ -972,9 +1070,8 @@ void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
#ifdef CONFIG_MAC80211_HT_DEBUG
if (net_ratelimit())
- printk(KERN_DEBUG "delba from %s (%s) tid %d reason code %d\n",
- print_mac(mac, mgmt->sa),
- initiator ? "initiator" : "recipient", tid,
+ printk(KERN_DEBUG "delba from %pM (%s) tid %d reason code %d\n",
+ mgmt->sa, initiator ? "initiator" : "recipient", tid,
mgmt->u.action.u.delba.reason_code);
#endif /* CONFIG_MAC80211_HT_DEBUG */
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 156e42a003ae..e036c7e748d8 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -23,6 +23,7 @@
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/etherdevice.h>
+#include <net/cfg80211.h>
#include <net/wireless.h>
#include <net/iw_handler.h>
#include <net/mac80211.h>
@@ -142,7 +143,6 @@ typedef unsigned __bitwise__ ieee80211_tx_result;
#define IEEE80211_TX_FRAGMENTED BIT(0)
#define IEEE80211_TX_UNICAST BIT(1)
#define IEEE80211_TX_PS_BUFFERED BIT(2)
-#define IEEE80211_TX_PROBE_LAST_FRAG BIT(3)
struct ieee80211_tx_data {
struct sk_buff *skb;
@@ -153,11 +153,6 @@ struct ieee80211_tx_data {
struct ieee80211_key *key;
struct ieee80211_channel *channel;
- s8 rate_idx;
- /* use this rate (if set) for last fragment; rate can
- * be set to lower rate for the first fragments, e.g.,
- * when using CTS protection with IEEE 802.11g. */
- s8 last_frag_rate_idx;
/* Extra fragments (in addition to the first fragment
* in skb) */
@@ -192,7 +187,6 @@ struct ieee80211_rx_data {
struct ieee80211_rx_status *status;
struct ieee80211_rate *rate;
- u16 ethertype;
unsigned int flags;
int sent_ps_buffered;
int queue;
@@ -203,9 +197,7 @@ struct ieee80211_rx_data {
struct ieee80211_tx_stored_packet {
struct sk_buff *skb;
struct sk_buff **extra_frag;
- s8 last_frag_rate_idx;
int num_extra_frag;
- bool last_frag_rate_ctrl_probe;
};
struct beacon_data {
@@ -219,9 +211,6 @@ struct ieee80211_if_ap {
struct list_head vlans;
- u8 ssid[IEEE80211_MAX_SSID_LEN];
- size_t ssid_len;
-
/* yes, this looks ugly, but guarantees that we can later use
* bitmap_empty :)
* NB: don't touch this bitmap, use sta_info_{set,clear}_tim_bit */
@@ -255,26 +244,6 @@ struct mesh_preq_queue {
u8 flags;
};
-struct mesh_config {
- /* Timeouts in ms */
- /* Mesh plink management parameters */
- u16 dot11MeshRetryTimeout;
- u16 dot11MeshConfirmTimeout;
- u16 dot11MeshHoldingTimeout;
- u16 dot11MeshMaxPeerLinks;
- u8 dot11MeshMaxRetries;
- u8 dot11MeshTTL;
- bool auto_open_plinks;
- /* HWMP parameters */
- u8 dot11MeshHWMPmaxPREQretries;
- u32 path_refresh_time;
- u16 min_discovery_timeout;
- u32 dot11MeshHWMPactivePathTimeout;
- u16 dot11MeshHWMPpreqMinInterval;
- u16 dot11MeshHWMPnetDiameterTraversalTime;
-};
-
-
/* flags used in struct ieee80211_if_sta.flags */
#define IEEE80211_STA_SSID_SET BIT(0)
#define IEEE80211_STA_BSSID_SET BIT(1)
@@ -438,8 +407,7 @@ struct ieee80211_sub_if_data {
struct ieee80211_key *keys[NUM_DEFAULT_KEYS];
struct ieee80211_key *default_key;
- /* BSS configuration for this interface. */
- struct ieee80211_bss_conf bss_conf;
+ u16 sequence_number;
/*
* AP this belongs to: self in AP mode and
@@ -570,6 +538,11 @@ enum {
IEEE80211_ADDBA_MSG = 4,
};
+enum queue_stop_reason {
+ IEEE80211_QUEUE_STOP_REASON_DRIVER,
+ IEEE80211_QUEUE_STOP_REASON_PS,
+};
+
/* maximum number of hardware queues we support. */
#define QD_MAX_QUEUES (IEEE80211_MAX_AMPDU_QUEUES + IEEE80211_MAX_QUEUES)
@@ -586,7 +559,8 @@ struct ieee80211_local {
const struct ieee80211_ops *ops;
unsigned long queue_pool[BITS_TO_LONGS(QD_MAX_QUEUES)];
-
+ unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES];
+ spinlock_t queue_stop_reason_lock;
struct net_device *mdev; /* wmaster# - "master" 802.11 device */
int open_count;
int monitors, cooked_mntrs;
@@ -633,8 +607,6 @@ struct ieee80211_local {
int rts_threshold;
int fragmentation_threshold;
- int short_retry_limit; /* dot11ShortRetryLimit */
- int long_retry_limit; /* dot11LongRetryLimit */
struct crypto_blkcipher *wep_tx_tfm;
struct crypto_blkcipher *wep_rx_tfm;
@@ -659,6 +631,7 @@ struct ieee80211_local {
struct delayed_work scan_work;
struct ieee80211_sub_if_data *scan_sdata;
struct ieee80211_channel *oper_channel, *scan_channel;
+ enum nl80211_channel_type oper_channel_type;
u8 scan_ssid[IEEE80211_MAX_SSID_LEN];
size_t scan_ssid_len;
struct list_head bss_list;
@@ -722,13 +695,17 @@ struct ieee80211_local {
int wifi_wme_noack_test;
unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */
+ bool powersave;
+ int dynamic_ps_timeout;
+ struct work_struct dynamic_ps_enable_work;
+ struct work_struct dynamic_ps_disable_work;
+ struct timer_list dynamic_ps_timer;
+
#ifdef CONFIG_MAC80211_DEBUGFS
struct local_debugfsdentries {
struct dentry *rcdir;
struct dentry *rcname;
struct dentry *frequency;
- struct dentry *antenna_sel_tx;
- struct dentry *antenna_sel_rx;
struct dentry *rts_threshold;
struct dentry *fragmentation_threshold;
struct dentry *short_retry_limit;
@@ -817,7 +794,7 @@ struct ieee802_11_elems {
u8 *wmm_info;
u8 *wmm_param;
struct ieee80211_ht_cap *ht_cap_elem;
- struct ieee80211_ht_addt_info *ht_info_elem;
+ struct ieee80211_ht_info *ht_info_elem;
u8 *mesh_config;
u8 *mesh_id;
u8 *peer_link;
@@ -869,11 +846,6 @@ static inline struct ieee80211_hw *local_to_hw(
return &local->hw;
}
-struct sta_attribute {
- struct attribute attr;
- ssize_t (*show)(const struct sta_info *, char *buf);
- ssize_t (*store)(struct sta_info *, const char *buf, size_t count);
-};
static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
{
@@ -882,12 +854,9 @@ static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
}
-int ieee80211_hw_config(struct ieee80211_local *local);
+int ieee80211_hw_config(struct ieee80211_local *local, u32 changed);
int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed);
void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx);
-u32 ieee80211_handle_ht(struct ieee80211_local *local, int enable_ht,
- struct ieee80211_ht_info *req_ht_cap,
- struct ieee80211_ht_bss_info *req_bss_cap);
void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
u32 changed);
void ieee80211_configure_filter(struct ieee80211_local *local);
@@ -906,8 +875,7 @@ int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid);
void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta);
struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, u8 *bssid,
- u8 *addr, u64 supp_rates);
+ u8 *bssid, u8 *addr, u64 supp_rates);
int ieee80211_sta_deauthenticate(struct ieee80211_sub_if_data *sdata, u16 reason);
int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason);
u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata);
@@ -968,11 +936,12 @@ int ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev);
int ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev);
/* HT */
-int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie,
- struct ieee80211_ht_info *ht_info);
-int ieee80211_ht_addt_info_ie_to_ht_bss_info(
- struct ieee80211_ht_addt_info *ht_add_info_ie,
- struct ieee80211_ht_bss_info *bss_info);
+void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband,
+ struct ieee80211_ht_cap *ht_cap_ie,
+ struct ieee80211_sta_ht_cap *ht_cap);
+u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_ht_info *hti,
+ u16 ap_ht_cap_flags);
void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u16 ssn);
void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *da,
@@ -1014,6 +983,17 @@ int ieee80211_set_freq(struct ieee80211_sub_if_data *sdata, int freq);
u64 ieee80211_mandatory_rates(struct ieee80211_local *local,
enum ieee80211_band band);
+void ieee80211_dynamic_ps_enable_work(struct work_struct *work);
+void ieee80211_dynamic_ps_disable_work(struct work_struct *work);
+void ieee80211_dynamic_ps_timer(unsigned long data);
+
+void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
+ enum queue_stop_reason reason);
+void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
+ enum queue_stop_reason reason);
+void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
+ u16 stype, u16 reason);
+
#ifdef CONFIG_MAC80211_NOINLINE
#define debug_noinline noinline
#else
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 8336fee68d3e..b9074824862a 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -65,7 +65,7 @@ static int ieee80211_open(struct net_device *dev)
struct ieee80211_if_init_conf conf;
u32 changed = 0;
int res;
- bool need_hw_reconfig = 0;
+ u32 hw_reconf_flags = 0;
u8 null_addr[ETH_ALEN] = {0};
/* fail early if user set an invalid address */
@@ -152,7 +152,8 @@ static int ieee80211_open(struct net_device *dev)
res = local->ops->start(local_to_hw(local));
if (res)
goto err_del_bss;
- need_hw_reconfig = 1;
+ /* we're brought up, everything changes */
+ hw_reconf_flags = ~0;
ieee80211_led_radio(local, local->hw.conf.radio_enabled);
}
@@ -198,8 +199,10 @@ static int ieee80211_open(struct net_device *dev)
/* must be before the call to ieee80211_configure_filter */
local->monitors++;
- if (local->monitors == 1)
+ if (local->monitors == 1) {
local->hw.conf.flags |= IEEE80211_CONF_RADIOTAP;
+ hw_reconf_flags |= IEEE80211_CONF_CHANGE_RADIOTAP;
+ }
if (sdata->u.mntr_flags & MONITOR_FLAG_FCSFAIL)
local->fif_fcsfail++;
@@ -226,8 +229,14 @@ static int ieee80211_open(struct net_device *dev)
if (res)
goto err_stop;
- if (ieee80211_vif_is_mesh(&sdata->vif))
+ if (ieee80211_vif_is_mesh(&sdata->vif)) {
+ local->fif_other_bss++;
+ netif_addr_lock_bh(local->mdev);
+ ieee80211_configure_filter(local);
+ netif_addr_unlock_bh(local->mdev);
+
ieee80211_start_mesh(sdata);
+ }
changed |= ieee80211_reset_erp_info(sdata);
ieee80211_bss_info_change_notify(sdata, changed);
ieee80211_enable_keys(sdata);
@@ -279,8 +288,8 @@ static int ieee80211_open(struct net_device *dev)
atomic_inc(&local->iff_promiscs);
local->open_count++;
- if (need_hw_reconfig) {
- ieee80211_hw_config(local);
+ if (hw_reconf_flags) {
+ ieee80211_hw_config(local, hw_reconf_flags);
/*
* set default queue parameters so drivers don't
* need to initialise the hardware if the hardware
@@ -322,6 +331,7 @@ static int ieee80211_stop(struct net_device *dev)
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_init_conf conf;
struct sta_info *sta;
+ u32 hw_reconf_flags = 0;
/*
* Stop TX on this interface first.
@@ -405,8 +415,10 @@ static int ieee80211_stop(struct net_device *dev)
}
local->monitors--;
- if (local->monitors == 0)
+ if (local->monitors == 0) {
local->hw.conf.flags &= ~IEEE80211_CONF_RADIOTAP;
+ hw_reconf_flags |= IEEE80211_CONF_CHANGE_RADIOTAP;
+ }
if (sdata->u.mntr_flags & MONITOR_FLAG_FCSFAIL)
local->fif_fcsfail--;
@@ -423,7 +435,11 @@ static int ieee80211_stop(struct net_device *dev)
break;
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_ADHOC:
- sdata->u.sta.state = IEEE80211_STA_MLME_DISABLED;
+ /* Announce that we are leaving the network. */
+ if (sdata->u.sta.state != IEEE80211_STA_MLME_DISABLED)
+ ieee80211_sta_deauthenticate(sdata,
+ WLAN_REASON_DEAUTH_LEAVING);
+
memset(sdata->u.sta.bssid, 0, ETH_ALEN);
del_timer_sync(&sdata->u.sta.timer);
/*
@@ -450,8 +466,15 @@ static int ieee80211_stop(struct net_device *dev)
/* fall through */
case NL80211_IFTYPE_MESH_POINT:
if (ieee80211_vif_is_mesh(&sdata->vif)) {
- /* allmulti is always set on mesh ifaces */
+ /* other_bss and allmulti are always set on mesh
+ * ifaces */
+ local->fif_other_bss--;
atomic_dec(&local->iff_allmultis);
+
+ netif_addr_lock_bh(local->mdev);
+ ieee80211_configure_filter(local);
+ netif_addr_unlock_bh(local->mdev);
+
ieee80211_stop_mesh(sdata);
}
/* fall through */
@@ -504,8 +527,15 @@ static int ieee80211_stop(struct net_device *dev)
tasklet_disable(&local->tx_pending_tasklet);
tasklet_disable(&local->tasklet);
+
+ /* no reconfiguring after stop! */
+ hw_reconf_flags = 0;
}
+ /* do after stop to avoid reconfiguring when we stop anyway */
+ if (hw_reconf_flags)
+ ieee80211_hw_config(local, hw_reconf_flags);
+
return 0;
}
@@ -668,6 +698,11 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
if (type == sdata->vif.type)
return 0;
+ /* Setting ad-hoc mode on non-IBSS channel is not supported. */
+ if (sdata->local->oper_channel->flags & IEEE80211_CHAN_NO_IBSS &&
+ type == NL80211_IFTYPE_ADHOC)
+ return -EOPNOTSUPP;
+
/*
* We could, here, on changes between IBSS/STA/MESH modes,
* invoke an MLME function instead that disassociates etc.
@@ -682,7 +717,7 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
ieee80211_setup_sdata(sdata, type);
/* reset some values that shouldn't be kept across type changes */
- sdata->bss_conf.basic_rates =
+ sdata->vif.bss_conf.basic_rates =
ieee80211_mandatory_rates(sdata->local,
sdata->local->hw.conf.channel->band);
sdata->drop_unencrypted = 0;
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index a5b06fe71980..999f7aa42326 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -132,7 +132,6 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
{
const u8 *addr;
int ret;
- DECLARE_MAC_BUF(mac);
assert_key_lock();
might_sleep();
@@ -154,16 +153,15 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
if (ret && ret != -ENOSPC && ret != -EOPNOTSUPP)
printk(KERN_ERR "mac80211-%s: failed to set key "
- "(%d, %s) to hardware (%d)\n",
+ "(%d, %pM) to hardware (%d)\n",
wiphy_name(key->local->hw.wiphy),
- key->conf.keyidx, print_mac(mac, addr), ret);
+ key->conf.keyidx, addr, ret);
}
static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
{
const u8 *addr;
int ret;
- DECLARE_MAC_BUF(mac);
assert_key_lock();
might_sleep();
@@ -186,9 +184,9 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
if (ret)
printk(KERN_ERR "mac80211-%s: failed to remove key "
- "(%d, %s) from hardware (%d)\n",
+ "(%d, %pM) from hardware (%d)\n",
wiphy_name(key->local->hw.wiphy),
- key->conf.keyidx, print_mac(mac, addr), ret);
+ key->conf.keyidx, addr, ret);
spin_lock(&todo_lock);
key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index ae62ad40ad63..4a61fd0b8d79 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -41,6 +41,8 @@
*/
struct ieee80211_tx_status_rtap_hdr {
struct ieee80211_radiotap_header hdr;
+ u8 rate;
+ u8 padding_for_rate;
__le16 tx_flags;
u8 data_retries;
} __attribute__ ((packed));
@@ -169,19 +171,13 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed)
conf.changed = changed;
if (sdata->vif.type == NL80211_IFTYPE_STATION ||
- sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+ sdata->vif.type == NL80211_IFTYPE_ADHOC)
conf.bssid = sdata->u.sta.bssid;
- conf.ssid = sdata->u.sta.ssid;
- conf.ssid_len = sdata->u.sta.ssid_len;
- } else if (sdata->vif.type == NL80211_IFTYPE_AP) {
+ else if (sdata->vif.type == NL80211_IFTYPE_AP)
conf.bssid = sdata->dev->dev_addr;
- conf.ssid = sdata->u.ap.ssid;
- conf.ssid_len = sdata->u.ap.ssid_len;
- } else if (ieee80211_vif_is_mesh(&sdata->vif)) {
+ else if (ieee80211_vif_is_mesh(&sdata->vif)) {
u8 zero[ETH_ALEN] = { 0 };
conf.bssid = zero;
- conf.ssid = zero;
- conf.ssid_len = 0;
} else {
WARN_ON(1);
return -EINVAL;
@@ -190,136 +186,73 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed)
if (WARN_ON(!conf.bssid && (changed & IEEE80211_IFCC_BSSID)))
return -EINVAL;
- if (WARN_ON(!conf.ssid && (changed & IEEE80211_IFCC_SSID)))
- return -EINVAL;
-
return local->ops->config_interface(local_to_hw(local),
&sdata->vif, &conf);
}
-int ieee80211_hw_config(struct ieee80211_local *local)
+int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
{
struct ieee80211_channel *chan;
int ret = 0;
+ int power;
+ enum nl80211_channel_type channel_type;
+
+ might_sleep();
- if (local->sw_scanning)
+ if (local->sw_scanning) {
chan = local->scan_channel;
- else
+ channel_type = NL80211_CHAN_NO_HT;
+ } else {
chan = local->oper_channel;
+ channel_type = local->oper_channel_type;
+ }
- local->hw.conf.channel = chan;
+ if (chan != local->hw.conf.channel ||
+ channel_type != local->hw.conf.ht.channel_type) {
+ local->hw.conf.channel = chan;
+ local->hw.conf.ht.channel_type = channel_type;
+ switch (channel_type) {
+ case NL80211_CHAN_NO_HT:
+ local->hw.conf.ht.enabled = false;
+ break;
+ case NL80211_CHAN_HT20:
+ case NL80211_CHAN_HT40MINUS:
+ case NL80211_CHAN_HT40PLUS:
+ local->hw.conf.ht.enabled = true;
+ break;
+ }
+ changed |= IEEE80211_CONF_CHANGE_CHANNEL;
+ }
if (!local->hw.conf.power_level)
- local->hw.conf.power_level = chan->max_power;
+ power = chan->max_power;
else
- local->hw.conf.power_level = min(chan->max_power,
- local->hw.conf.power_level);
-
- local->hw.conf.max_antenna_gain = chan->max_antenna_gain;
-
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: HW CONFIG: freq=%d\n",
- wiphy_name(local->hw.wiphy), chan->center_freq);
-#endif
-
- if (local->open_count)
- ret = local->ops->config(local_to_hw(local), &local->hw.conf);
-
- return ret;
-}
-
-/**
- * ieee80211_handle_ht should be used only after legacy configuration
- * has been determined namely band, as ht configuration depends upon
- * the hardware's HT abilities for a _specific_ band.
- */
-u32 ieee80211_handle_ht(struct ieee80211_local *local, int enable_ht,
- struct ieee80211_ht_info *req_ht_cap,
- struct ieee80211_ht_bss_info *req_bss_cap)
-{
- struct ieee80211_conf *conf = &local->hw.conf;
- struct ieee80211_supported_band *sband;
- struct ieee80211_ht_info ht_conf;
- struct ieee80211_ht_bss_info ht_bss_conf;
- u32 changed = 0;
- int i;
- u8 max_tx_streams = IEEE80211_HT_CAP_MAX_STREAMS;
- u8 tx_mcs_set_cap;
-
- sband = local->hw.wiphy->bands[conf->channel->band];
-
- memset(&ht_conf, 0, sizeof(struct ieee80211_ht_info));
- memset(&ht_bss_conf, 0, sizeof(struct ieee80211_ht_bss_info));
-
- /* HT is not supported */
- if (!sband->ht_info.ht_supported) {
- conf->flags &= ~IEEE80211_CONF_SUPPORT_HT_MODE;
- goto out;
+ power = min(chan->max_power, local->hw.conf.power_level);
+ if (local->hw.conf.power_level != power) {
+ changed |= IEEE80211_CONF_CHANGE_POWER;
+ local->hw.conf.power_level = power;
}
- /* disable HT */
- if (!enable_ht) {
- if (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE)
- changed |= BSS_CHANGED_HT;
- conf->flags &= ~IEEE80211_CONF_SUPPORT_HT_MODE;
- conf->ht_conf.ht_supported = 0;
- goto out;
+ if (changed && local->open_count) {
+ ret = local->ops->config(local_to_hw(local), changed);
+ /*
+ * Goal:
+ * HW reconfiguration should never fail, the driver has told
+ * us what it can support so it should live up to that promise.
+ *
+ * Current status:
+ * rfkill is not integrated with mac80211 and a
+ * configuration command can thus fail if hardware rfkill
+ * is enabled
+ *
+ * FIXME: integrate rfkill with mac80211 and then add this
+ * WARN_ON() back
+ *
+ */
+ /* WARN_ON(ret); */
}
-
- if (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE))
- changed |= BSS_CHANGED_HT;
-
- conf->flags |= IEEE80211_CONF_SUPPORT_HT_MODE;
- ht_conf.ht_supported = 1;
-
- ht_conf.cap = req_ht_cap->cap & sband->ht_info.cap;
- ht_conf.cap &= ~(IEEE80211_HT_CAP_SM_PS);
- ht_conf.cap |= sband->ht_info.cap & IEEE80211_HT_CAP_SM_PS;
- ht_bss_conf.primary_channel = req_bss_cap->primary_channel;
- ht_bss_conf.bss_cap = req_bss_cap->bss_cap;
- ht_bss_conf.bss_op_mode = req_bss_cap->bss_op_mode;
-
- ht_conf.ampdu_factor = req_ht_cap->ampdu_factor;
- ht_conf.ampdu_density = req_ht_cap->ampdu_density;
-
- /* Bits 96-100 */
- tx_mcs_set_cap = sband->ht_info.supp_mcs_set[12];
-
- /* configure suppoerted Tx MCS according to requested MCS
- * (based in most cases on Rx capabilities of peer) and self
- * Tx MCS capabilities (as defined by low level driver HW
- * Tx capabilities) */
- if (!(tx_mcs_set_cap & IEEE80211_HT_CAP_MCS_TX_DEFINED))
- goto check_changed;
-
- /* Counting from 0 therfore + 1 */
- if (tx_mcs_set_cap & IEEE80211_HT_CAP_MCS_TX_RX_DIFF)
- max_tx_streams = ((tx_mcs_set_cap &
- IEEE80211_HT_CAP_MCS_TX_STREAMS) >> 2) + 1;
-
- for (i = 0; i < max_tx_streams; i++)
- ht_conf.supp_mcs_set[i] =
- sband->ht_info.supp_mcs_set[i] &
- req_ht_cap->supp_mcs_set[i];
-
- if (tx_mcs_set_cap & IEEE80211_HT_CAP_MCS_TX_UEQM)
- for (i = IEEE80211_SUPP_MCS_SET_UEQM;
- i < IEEE80211_SUPP_MCS_SET_LEN; i++)
- ht_conf.supp_mcs_set[i] =
- sband->ht_info.supp_mcs_set[i] &
- req_ht_cap->supp_mcs_set[i];
-
-check_changed:
- /* if bss configuration changed store the new one */
- if (memcmp(&conf->ht_conf, &ht_conf, sizeof(ht_conf)) ||
- memcmp(&conf->ht_bss_conf, &ht_bss_conf, sizeof(ht_bss_conf))) {
- changed |= BSS_CHANGED_HT;
- memcpy(&conf->ht_conf, &ht_conf, sizeof(ht_conf));
- memcpy(&conf->ht_bss_conf, &ht_bss_conf, sizeof(ht_bss_conf));
- }
-out:
- return changed;
+ return ret;
}
void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
@@ -336,15 +269,18 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
if (local->ops->bss_info_changed)
local->ops->bss_info_changed(local_to_hw(local),
&sdata->vif,
- &sdata->bss_conf,
+ &sdata->vif.bss_conf,
changed);
}
u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata)
{
- sdata->bss_conf.use_cts_prot = 0;
- sdata->bss_conf.use_short_preamble = 0;
- return BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_ERP_PREAMBLE;
+ sdata->vif.bss_conf.use_cts_prot = false;
+ sdata->vif.bss_conf.use_short_preamble = false;
+ sdata->vif.bss_conf.use_short_slot = false;
+ return BSS_CHANGED_ERP_CTS_PROT |
+ BSS_CHANGED_ERP_PREAMBLE |
+ BSS_CHANGED_ERP_SLOT;
}
void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw,
@@ -405,7 +341,8 @@ static void ieee80211_tasklet_handler(unsigned long data)
dev_kfree_skb(skb);
break ;
default:
- WARN_ON(1);
+ WARN(1, "mac80211: Packet is of unknown type %d\n",
+ skb->pkt_type);
dev_kfree_skb(skb);
break;
}
@@ -466,8 +403,6 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
struct sta_info *sta,
struct sk_buff *skb)
{
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
sta->tx_filtered_count++;
/*
@@ -514,10 +449,9 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
return;
}
- if (!test_sta_flags(sta, WLAN_STA_PS) &&
- !(info->flags & IEEE80211_TX_CTL_REQUEUE)) {
+ if (!test_sta_flags(sta, WLAN_STA_PS) && !skb->requeue) {
/* Software retry the packet once */
- info->flags |= IEEE80211_TX_CTL_REQUEUE;
+ skb->requeue = 1;
ieee80211_remove_tx_extra(local, sta->key, skb);
dev_queue_xmit(skb);
return;
@@ -547,13 +481,28 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
struct ieee80211_sub_if_data *sdata;
struct net_device *prev_dev = NULL;
struct sta_info *sta;
+ int retry_count = -1, i;
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ /* the HW cannot have attempted that rate */
+ if (i >= hw->max_rates) {
+ info->status.rates[i].idx = -1;
+ info->status.rates[i].count = 0;
+ }
+
+ retry_count += info->status.rates[i].count;
+ }
+ if (retry_count < 0)
+ retry_count = 0;
rcu_read_lock();
+ sband = local->hw.wiphy->bands[info->band];
+
sta = sta_info_get(local, hdr->addr1);
if (sta) {
- if (info->status.excessive_retries &&
+ if (!(info->flags & IEEE80211_TX_STAT_ACK) &&
test_sta_flags(sta, WLAN_STA_PS)) {
/*
* The STA is in power save mode, so assume
@@ -584,12 +533,11 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
rcu_read_unlock();
return;
} else {
- if (info->status.excessive_retries)
+ if (!(info->flags & IEEE80211_TX_STAT_ACK))
sta->tx_retry_failed++;
- sta->tx_retry_count += info->status.retry_count;
+ sta->tx_retry_count += retry_count;
}
- sband = local->hw.wiphy->bands[info->band];
rate_control_tx_status(local, sband, sta, skb);
}
@@ -610,9 +558,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
local->dot11TransmittedFrameCount++;
if (is_multicast_ether_addr(hdr->addr1))
local->dot11MulticastTransmittedFrameCount++;
- if (info->status.retry_count > 0)
+ if (retry_count > 0)
local->dot11RetryCount++;
- if (info->status.retry_count > 1)
+ if (retry_count > 1)
local->dot11MultipleRetryCount++;
}
@@ -656,19 +604,30 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr));
rthdr->hdr.it_present =
cpu_to_le32((1 << IEEE80211_RADIOTAP_TX_FLAGS) |
- (1 << IEEE80211_RADIOTAP_DATA_RETRIES));
+ (1 << IEEE80211_RADIOTAP_DATA_RETRIES) |
+ (1 << IEEE80211_RADIOTAP_RATE));
if (!(info->flags & IEEE80211_TX_STAT_ACK) &&
!is_multicast_ether_addr(hdr->addr1))
rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_FAIL);
- if ((info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) &&
- (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT))
+ /*
+ * XXX: Once radiotap gets the bitmap reset thing the vendor
+ * extensions proposal contains, we can actually report
+ * the whole set of tries we did.
+ */
+ if ((info->status.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) ||
+ (info->status.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT))
rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_CTS);
- else if (info->flags & IEEE80211_TX_CTL_USE_RTS_CTS)
+ else if (info->status.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS)
rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_RTS);
+ if (info->status.rates[0].idx >= 0 &&
+ !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS))
+ rthdr->rate = sband->bitrates[
+ info->status.rates[0].idx].bitrate / 5;
- rthdr->data_retries = info->status.retry_count;
+ /* for now report the total retry_count */
+ rthdr->data_retries = retry_count;
/* XXX: is this sufficient for BPF? */
skb_set_mac_header(skb, 0);
@@ -753,20 +712,30 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
BUG_ON(!ops->configure_filter);
local->ops = ops;
- local->hw.queues = 1; /* default */
-
+ /* set up some defaults */
+ local->hw.queues = 1;
+ local->hw.max_rates = 1;
local->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
local->fragmentation_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
- local->short_retry_limit = 7;
- local->long_retry_limit = 4;
- local->hw.conf.radio_enabled = 1;
+ local->hw.conf.long_frame_max_tx_count = 4;
+ local->hw.conf.short_frame_max_tx_count = 7;
+ local->hw.conf.radio_enabled = true;
INIT_LIST_HEAD(&local->interfaces);
spin_lock_init(&local->key_lock);
+ spin_lock_init(&local->queue_stop_reason_lock);
+
INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work);
+ INIT_WORK(&local->dynamic_ps_enable_work,
+ ieee80211_dynamic_ps_enable_work);
+ INIT_WORK(&local->dynamic_ps_disable_work,
+ ieee80211_dynamic_ps_disable_work);
+ setup_timer(&local->dynamic_ps_timer,
+ ieee80211_dynamic_ps_timer, (unsigned long) local);
+
sta_info_init(local);
tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending,
@@ -788,7 +757,6 @@ EXPORT_SYMBOL(ieee80211_alloc_hw);
int ieee80211_register_hw(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
- const char *name;
int result;
enum ieee80211_band band;
struct net_device *mdev;
@@ -853,8 +821,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
mdev->header_ops = &ieee80211_header_ops;
mdev->set_multicast_list = ieee80211_master_set_multicast_list;
- name = wiphy_dev(local->hw.wiphy)->driver->name;
- local->hw.workqueue = create_freezeable_workqueue(name);
+ local->hw.workqueue =
+ create_freezeable_workqueue(wiphy_name(local->hw.wiphy));
if (!local->hw.workqueue) {
result = -ENOMEM;
goto fail_workqueue;
@@ -921,12 +889,14 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
local->mdev->select_queue = ieee80211_select_queue;
- /* add one default STA interface */
- result = ieee80211_if_add(local, "wlan%d", NULL,
- NL80211_IFTYPE_STATION, NULL);
- if (result)
- printk(KERN_WARNING "%s: Failed to add default virtual iface\n",
- wiphy_name(local->hw.wiphy));
+ /* add one default STA interface if supported */
+ if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION)) {
+ result = ieee80211_if_add(local, "wlan%d", NULL,
+ NL80211_IFTYPE_STATION, NULL);
+ if (result)
+ printk(KERN_WARNING "%s: Failed to add default virtual iface\n",
+ wiphy_name(local->hw.wiphy));
+ }
rtnl_unlock();
@@ -1013,7 +983,7 @@ static int __init ieee80211_init(void)
BUILD_BUG_ON(sizeof(struct ieee80211_tx_info) > sizeof(skb->cb));
BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, driver_data) +
- IEEE80211_TX_INFO_DRIVER_DATA_SIZE > sizeof(skb->cb));
+ IEEE80211_TX_INFO_DRIVER_DATA_SIZE > sizeof(skb->cb));
ret = rc80211_minstrel_init();
if (ret)
@@ -1023,6 +993,10 @@ static int __init ieee80211_init(void)
if (ret)
return ret;
+ ret = rc80211_multirate_pid_init();
+ if (ret)
+ return ret;
+
ieee80211_debugfs_netdev_init();
return 0;
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 8013277924f2..82f568e94365 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -238,7 +238,7 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
pos = skb_put(skb, 21);
*pos++ = WLAN_EID_MESH_CONFIG;
- *pos++ = MESH_CFG_LEN;
+ *pos++ = IEEE80211_MESH_CONFIG_LEN;
/* Version */
*pos++ = 1;
@@ -473,7 +473,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
size_t len,
struct ieee80211_rx_status *rx_status)
{
- struct ieee80211_local *local= sdata->local;
+ struct ieee80211_local *local = sdata->local;
struct ieee802_11_elems elems;
struct ieee80211_channel *channel;
u64 supp_rates = 0;
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index e10471c6ba42..c197ab545e54 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -145,9 +145,6 @@ struct mesh_rmc {
};
-/* Mesh IEs constants */
-#define MESH_CFG_LEN 19
-
/*
* MESH_CFG_COMP_LEN Includes:
* - Active path selection protocol ID.
@@ -157,7 +154,7 @@ struct mesh_rmc {
* Does not include mesh capabilities, which may vary across nodes in the same
* mesh
*/
-#define MESH_CFG_CMP_LEN 17
+#define MESH_CFG_CMP_LEN (IEEE80211_MESH_CONFIG_LEN - 2)
/* Default values, timeouts in ms */
#define MESH_TTL 5
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 501c7831adb4..71fe60961230 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -218,12 +218,16 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,
if (sta->fail_avg >= 100)
return MAX_METRIC;
+
+ if (sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)
+ return MAX_METRIC;
+
err = (sta->fail_avg << ARITH_SHIFT) / 100;
/* bitrate is in units of 100 Kbps, while we need rate in units of
* 1Mbps. This will be corrected on tx_time computation.
*/
- rate = sband->bitrates[sta->last_txrate_idx].bitrate;
+ rate = sband->bitrates[sta->last_tx_rate.idx].bitrate;
tx_time = (device_constant + 10 * test_frame_len / rate);
estimated_retx = ((1 << (2 * ARITH_SHIFT)) / (s_unit - err));
result = (tx_time * estimated_retx) >> (2 * ARITH_SHIFT) ;
@@ -759,7 +763,6 @@ enddiscovery:
*
* @skb: 802.11 frame to be sent
* @sdata: network subif the frame will be sent through
- * @fwd_frame: true if this frame was originally from a different host
*
* Returns: 0 if the next hop was found. Nonzero otherwise. If no next hop is
* found, the function will start a path discovery and queue the frame so it is
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index faac101c0f85..1159bdb4119c 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -107,6 +107,7 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
sta->flags = WLAN_STA_AUTHORIZED;
sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
+ rate_control_rate_init(sta);
return sta;
}
@@ -257,9 +258,6 @@ static void mesh_plink_timer(unsigned long data)
struct sta_info *sta;
__le16 llid, plid, reason;
struct ieee80211_sub_if_data *sdata;
-#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
- DECLARE_MAC_BUF(mac);
-#endif
/*
* This STA is valid because sta_info_destroy() will
@@ -274,8 +272,8 @@ static void mesh_plink_timer(unsigned long data)
spin_unlock_bh(&sta->lock);
return;
}
- mpl_dbg("Mesh plink timer for %s fired on state %d\n",
- print_mac(mac, sta->sta.addr), sta->plink_state);
+ mpl_dbg("Mesh plink timer for %pM fired on state %d\n",
+ sta->sta.addr, sta->plink_state);
reason = 0;
llid = sta->llid;
plid = sta->plid;
@@ -287,9 +285,9 @@ static void mesh_plink_timer(unsigned long data)
/* retry timer */
if (sta->plink_retries < dot11MeshMaxRetries(sdata)) {
u32 rand;
- mpl_dbg("Mesh plink for %s (retry, timeout): %d %d\n",
- print_mac(mac, sta->sta.addr),
- sta->plink_retries, sta->plink_timeout);
+ mpl_dbg("Mesh plink for %pM (retry, timeout): %d %d\n",
+ sta->sta.addr, sta->plink_retries,
+ sta->plink_timeout);
get_random_bytes(&rand, sizeof(u32));
sta->plink_timeout = sta->plink_timeout +
rand % sta->plink_timeout;
@@ -337,9 +335,6 @@ int mesh_plink_open(struct sta_info *sta)
{
__le16 llid;
struct ieee80211_sub_if_data *sdata = sta->sdata;
-#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
- DECLARE_MAC_BUF(mac);
-#endif
spin_lock_bh(&sta->lock);
get_random_bytes(&llid, 2);
@@ -351,8 +346,8 @@ int mesh_plink_open(struct sta_info *sta)
sta->plink_state = PLINK_OPN_SNT;
mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata));
spin_unlock_bh(&sta->lock);
- mpl_dbg("Mesh plink: starting establishment with %s\n",
- print_mac(mac, sta->sta.addr));
+ mpl_dbg("Mesh plink: starting establishment with %pM\n",
+ sta->sta.addr);
return mesh_plink_frame_tx(sdata, PLINK_OPEN,
sta->sta.addr, llid, 0, 0);
@@ -360,10 +355,6 @@ int mesh_plink_open(struct sta_info *sta)
void mesh_plink_block(struct sta_info *sta)
{
-#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
- DECLARE_MAC_BUF(mac);
-#endif
-
spin_lock_bh(&sta->lock);
__mesh_plink_deactivate(sta);
sta->plink_state = PLINK_BLOCKED;
@@ -374,12 +365,8 @@ int mesh_plink_close(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
__le16 llid, plid, reason;
-#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
- DECLARE_MAC_BUF(mac);
-#endif
- mpl_dbg("Mesh plink: closing link with %s\n",
- print_mac(mac, sta->sta.addr));
+ mpl_dbg("Mesh plink: closing link with %pM\n", sta->sta.addr);
spin_lock_bh(&sta->lock);
sta->reason = cpu_to_le16(MESH_LINK_CANCELLED);
reason = sta->reason;
@@ -417,9 +404,6 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
u8 ie_len;
u8 *baseaddr;
__le16 plid, llid, reason;
-#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
- DECLARE_MAC_BUF(mac);
-#endif
/* need action_code, aux */
if (len < IEEE80211_MIN_ACTION_SIZE + 3)
@@ -557,10 +541,10 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
}
}
- mpl_dbg("Mesh plink (peer, state, llid, plid, event): %s %d %d %d %d\n",
- print_mac(mac, mgmt->sa), sta->plink_state,
- le16_to_cpu(sta->llid), le16_to_cpu(sta->plid),
- event);
+ mpl_dbg("Mesh plink (peer, state, llid, plid, event): %pM %d %d %d %d\n",
+ mgmt->sa, sta->plink_state,
+ le16_to_cpu(sta->llid), le16_to_cpu(sta->plid),
+ event);
reason = 0;
switch (sta->plink_state) {
/* spin_unlock as soon as state is updated at each case */
@@ -660,8 +644,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
sta->plink_state = PLINK_ESTAB;
mesh_plink_inc_estab_count(sdata);
spin_unlock_bh(&sta->lock);
- mpl_dbg("Mesh plink with %s ESTABLISHED\n",
- print_mac(mac, sta->sta.addr));
+ mpl_dbg("Mesh plink with %pM ESTABLISHED\n",
+ sta->sta.addr);
break;
default:
spin_unlock_bh(&sta->lock);
@@ -693,8 +677,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
sta->plink_state = PLINK_ESTAB;
mesh_plink_inc_estab_count(sdata);
spin_unlock_bh(&sta->lock);
- mpl_dbg("Mesh plink with %s ESTABLISHED\n",
- print_mac(mac, sta->sta.addr));
+ mpl_dbg("Mesh plink with %pM ESTABLISHED\n",
+ sta->sta.addr);
mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid,
plid, 0);
break;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 409bb7716236..88a26198747f 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -14,7 +14,6 @@
#include <linux/delay.h>
#include <linux/if_ether.h>
#include <linux/skbuff.h>
-#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/wireless.h>
#include <linux/random.h>
@@ -236,7 +235,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
- u8 *pos, *ies, *ht_add_ie;
+ u8 *pos, *ies, *ht_ie;
int i, len, count, rates_len, supp_rates_len;
u16 capab;
struct ieee80211_bss *bss;
@@ -310,7 +309,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ASSOC_REQ);
mgmt->u.assoc_req.capab_info = cpu_to_le16(capab);
- mgmt->u.reassoc_req.listen_interval =
+ mgmt->u.assoc_req.listen_interval =
cpu_to_le16(local->hw.conf.listen_interval);
}
@@ -393,24 +392,25 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
/* wmm support is a must to HT */
if (wmm && (ifsta->flags & IEEE80211_STA_WMM_ENABLED) &&
- sband->ht_info.ht_supported &&
- (ht_add_ie = ieee80211_bss_get_ie(bss, WLAN_EID_HT_EXTRA_INFO))) {
- struct ieee80211_ht_addt_info *ht_add_info =
- (struct ieee80211_ht_addt_info *)ht_add_ie;
- u16 cap = sband->ht_info.cap;
+ sband->ht_cap.ht_supported &&
+ (ht_ie = ieee80211_bss_get_ie(bss, WLAN_EID_HT_INFORMATION)) &&
+ ht_ie[1] >= sizeof(struct ieee80211_ht_info)) {
+ struct ieee80211_ht_info *ht_info =
+ (struct ieee80211_ht_info *)(ht_ie + 2);
+ u16 cap = sband->ht_cap.cap;
__le16 tmp;
u32 flags = local->hw.conf.channel->flags;
- switch (ht_add_info->ht_param & IEEE80211_HT_IE_CHA_SEC_OFFSET) {
- case IEEE80211_HT_IE_CHA_SEC_ABOVE:
+ switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
if (flags & IEEE80211_CHAN_NO_FAT_ABOVE) {
- cap &= ~IEEE80211_HT_CAP_SUP_WIDTH;
+ cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
cap &= ~IEEE80211_HT_CAP_SGI_40;
}
break;
- case IEEE80211_HT_IE_CHA_SEC_BELOW:
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
if (flags & IEEE80211_CHAN_NO_FAT_BELOW) {
- cap &= ~IEEE80211_HT_CAP_SUP_WIDTH;
+ cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
cap &= ~IEEE80211_HT_CAP_SGI_40;
}
break;
@@ -424,9 +424,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
memcpy(pos, &tmp, sizeof(u16));
pos += sizeof(u16);
/* TODO: needs a define here for << 2 */
- *pos++ = sband->ht_info.ampdu_factor |
- (sband->ht_info.ampdu_density << 2);
- memcpy(pos, sband->ht_info.supp_mcs_set, 16);
+ *pos++ = sband->ht_cap.ampdu_factor |
+ (sband->ht_cap.ampdu_density << 2);
+ memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
}
kfree(ifsta->assocreq_ies);
@@ -439,7 +439,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
}
-static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
+void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
u16 stype, u16 reason)
{
struct ieee80211_local *local = sdata->local;
@@ -467,6 +467,7 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
ieee80211_tx_skb(sdata, skb, 0);
}
+EXPORT_SYMBOL(ieee80211_send_deauth_disassoc);
/* MLME */
static void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
@@ -568,25 +569,35 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
}
}
-static u32 ieee80211_handle_protect_preamb(struct ieee80211_sub_if_data *sdata,
- bool use_protection,
- bool use_short_preamble)
+static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
+ u16 capab, bool erp_valid, u8 erp)
{
- struct ieee80211_bss_conf *bss_conf = &sdata->bss_conf;
+ struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
struct ieee80211_if_sta *ifsta = &sdata->u.sta;
- DECLARE_MAC_BUF(mac);
#endif
u32 changed = 0;
+ bool use_protection;
+ bool use_short_preamble;
+ bool use_short_slot;
+
+ if (erp_valid) {
+ use_protection = (erp & WLAN_ERP_USE_PROTECTION) != 0;
+ use_short_preamble = (erp & WLAN_ERP_BARKER_PREAMBLE) == 0;
+ } else {
+ use_protection = false;
+ use_short_preamble = !!(capab & WLAN_CAPABILITY_SHORT_PREAMBLE);
+ }
+
+ use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME);
if (use_protection != bss_conf->use_cts_prot) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: CTS protection %s (BSSID="
- "%s)\n",
+ printk(KERN_DEBUG "%s: CTS protection %s (BSSID=%pM)\n",
sdata->dev->name,
use_protection ? "enabled" : "disabled",
- print_mac(mac, ifsta->bssid));
+ ifsta->bssid);
}
#endif
bss_conf->use_cts_prot = use_protection;
@@ -597,40 +608,28 @@ static u32 ieee80211_handle_protect_preamb(struct ieee80211_sub_if_data *sdata,
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
if (net_ratelimit()) {
printk(KERN_DEBUG "%s: switched to %s barker preamble"
- " (BSSID=%s)\n",
+ " (BSSID=%pM)\n",
sdata->dev->name,
use_short_preamble ? "short" : "long",
- print_mac(mac, ifsta->bssid));
+ ifsta->bssid);
}
#endif
bss_conf->use_short_preamble = use_short_preamble;
changed |= BSS_CHANGED_ERP_PREAMBLE;
}
- return changed;
-}
-
-static u32 ieee80211_handle_erp_ie(struct ieee80211_sub_if_data *sdata,
- u8 erp_value)
-{
- bool use_protection = (erp_value & WLAN_ERP_USE_PROTECTION) != 0;
- bool use_short_preamble = (erp_value & WLAN_ERP_BARKER_PREAMBLE) == 0;
-
- return ieee80211_handle_protect_preamb(sdata,
- use_protection, use_short_preamble);
-}
-
-static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_bss *bss)
-{
- u32 changed = 0;
-
- if (bss->has_erp_value)
- changed |= ieee80211_handle_erp_ie(sdata, bss->erp_value);
- else {
- u16 capab = bss->capability;
- changed |= ieee80211_handle_protect_preamb(sdata, false,
- (capab & WLAN_CAPABILITY_SHORT_PREAMBLE) != 0);
+ if (use_short_slot != bss_conf->use_short_slot) {
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "%s: switched to %s slot time"
+ " (BSSID=%pM)\n",
+ sdata->dev->name,
+ use_short_slot ? "short" : "long",
+ ifsta->bssid);
+ }
+#endif
+ bss_conf->use_short_slot = use_short_slot;
+ changed |= BSS_CHANGED_ERP_SLOT;
}
return changed;
@@ -701,14 +700,15 @@ static void ieee80211_sta_send_associnfo(struct ieee80211_sub_if_data *sdata,
static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta)
+ struct ieee80211_if_sta *ifsta,
+ u32 bss_info_changed)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_conf *conf = &local_to_hw(local)->conf;
- u32 changed = BSS_CHANGED_ASSOC;
struct ieee80211_bss *bss;
+ bss_info_changed |= BSS_CHANGED_ASSOC;
ifsta->flags |= IEEE80211_STA_ASSOCIATED;
if (sdata->vif.type != NL80211_IFTYPE_STATION)
@@ -719,22 +719,16 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
ifsta->ssid, ifsta->ssid_len);
if (bss) {
/* set timing information */
- sdata->bss_conf.beacon_int = bss->beacon_int;
- sdata->bss_conf.timestamp = bss->timestamp;
- sdata->bss_conf.dtim_period = bss->dtim_period;
+ sdata->vif.bss_conf.beacon_int = bss->beacon_int;
+ sdata->vif.bss_conf.timestamp = bss->timestamp;
+ sdata->vif.bss_conf.dtim_period = bss->dtim_period;
- changed |= ieee80211_handle_bss_capability(sdata, bss);
+ bss_info_changed |= ieee80211_handle_bss_capability(sdata,
+ bss->capability, bss->has_erp_value, bss->erp_value);
ieee80211_rx_bss_put(local, bss);
}
- if (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) {
- changed |= BSS_CHANGED_HT;
- sdata->bss_conf.assoc_ht = 1;
- sdata->bss_conf.ht_conf = &conf->ht_conf;
- sdata->bss_conf.ht_bss_conf = &conf->ht_bss_conf;
- }
-
ifsta->flags |= IEEE80211_STA_PREV_BSSID_SET;
memcpy(ifsta->prev_bssid, sdata->u.sta.bssid, ETH_ALEN);
ieee80211_sta_send_associnfo(sdata, ifsta);
@@ -742,14 +736,25 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
ifsta->last_probe = jiffies;
ieee80211_led_assoc(local, 1);
- sdata->bss_conf.assoc = 1;
+ sdata->vif.bss_conf.assoc = 1;
/*
* For now just always ask the driver to update the basic rateset
* when we have associated, we aren't checking whether it actually
* changed or not.
*/
- changed |= BSS_CHANGED_BASIC_RATES;
- ieee80211_bss_info_change_notify(sdata, changed);
+ bss_info_changed |= BSS_CHANGED_BASIC_RATES;
+ ieee80211_bss_info_change_notify(sdata, bss_info_changed);
+
+ if (local->powersave) {
+ if (local->dynamic_ps_timeout > 0)
+ mod_timer(&local->dynamic_ps_timer, jiffies +
+ msecs_to_jiffies(local->dynamic_ps_timeout));
+ else {
+ conf->flags |= IEEE80211_CONF_PS;
+ ieee80211_hw_config(local,
+ IEEE80211_CONF_CHANGE_PS);
+ }
+ }
netif_tx_start_all_queues(sdata->dev);
netif_carrier_on(sdata->dev);
@@ -760,18 +765,17 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta)
{
- DECLARE_MAC_BUF(mac);
-
ifsta->direct_probe_tries++;
if (ifsta->direct_probe_tries > IEEE80211_AUTH_MAX_TRIES) {
- printk(KERN_DEBUG "%s: direct probe to AP %s timed out\n",
- sdata->dev->name, print_mac(mac, ifsta->bssid));
+ printk(KERN_DEBUG "%s: direct probe to AP %pM timed out\n",
+ sdata->dev->name, ifsta->bssid);
ifsta->state = IEEE80211_STA_MLME_DISABLED;
+ ieee80211_sta_send_apinfo(sdata, ifsta);
return;
}
- printk(KERN_DEBUG "%s: direct probe to AP %s try %d\n",
- sdata->dev->name, print_mac(mac, ifsta->bssid),
+ printk(KERN_DEBUG "%s: direct probe to AP %pM try %d\n",
+ sdata->dev->name, ifsta->bssid,
ifsta->direct_probe_tries);
ifsta->state = IEEE80211_STA_MLME_DIRECT_PROBE;
@@ -791,33 +795,36 @@ static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata,
static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta)
{
- DECLARE_MAC_BUF(mac);
-
ifsta->auth_tries++;
if (ifsta->auth_tries > IEEE80211_AUTH_MAX_TRIES) {
- printk(KERN_DEBUG "%s: authentication with AP %s"
+ printk(KERN_DEBUG "%s: authentication with AP %pM"
" timed out\n",
- sdata->dev->name, print_mac(mac, ifsta->bssid));
+ sdata->dev->name, ifsta->bssid);
ifsta->state = IEEE80211_STA_MLME_DISABLED;
+ ieee80211_sta_send_apinfo(sdata, ifsta);
return;
}
ifsta->state = IEEE80211_STA_MLME_AUTHENTICATE;
- printk(KERN_DEBUG "%s: authenticate with AP %s\n",
- sdata->dev->name, print_mac(mac, ifsta->bssid));
+ printk(KERN_DEBUG "%s: authenticate with AP %pM\n",
+ sdata->dev->name, ifsta->bssid);
ieee80211_send_auth(sdata, ifsta, 1, NULL, 0, 0);
mod_timer(&ifsta->timer, jiffies + IEEE80211_AUTH_TIMEOUT);
}
+/*
+ * The disassoc 'reason' argument can be either our own reason
+ * if self disconnected or a reason code from the AP.
+ */
static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta, bool deauth,
bool self_disconnected, u16 reason)
{
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
- u32 changed = BSS_CHANGED_ASSOC;
+ u32 changed = 0, config_changed = 0;
rcu_read_lock();
@@ -851,21 +858,40 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
ifsta->flags &= ~IEEE80211_STA_ASSOCIATED;
changed |= ieee80211_reset_erp_info(sdata);
- if (sdata->bss_conf.assoc_ht)
- changed |= BSS_CHANGED_HT;
-
- sdata->bss_conf.assoc_ht = 0;
- sdata->bss_conf.ht_conf = NULL;
- sdata->bss_conf.ht_bss_conf = NULL;
-
ieee80211_led_assoc(local, 0);
- sdata->bss_conf.assoc = 0;
+ changed |= BSS_CHANGED_ASSOC;
+ sdata->vif.bss_conf.assoc = false;
ieee80211_sta_send_apinfo(sdata, ifsta);
- if (self_disconnected)
+ if (self_disconnected || reason == WLAN_REASON_DISASSOC_STA_HAS_LEFT)
ifsta->state = IEEE80211_STA_MLME_DISABLED;
+ rcu_read_unlock();
+
+ local->hw.conf.ht.enabled = false;
+ local->oper_channel_type = NL80211_CHAN_NO_HT;
+ config_changed |= IEEE80211_CONF_CHANGE_HT;
+
+ del_timer_sync(&local->dynamic_ps_timer);
+ cancel_work_sync(&local->dynamic_ps_enable_work);
+
+ if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+ local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+ config_changed |= IEEE80211_CONF_CHANGE_PS;
+ }
+
+ ieee80211_hw_config(local, config_changed);
+ ieee80211_bss_info_change_notify(sdata, changed);
+
+ rcu_read_lock();
+
+ sta = sta_info_get(local, ifsta->bssid);
+ if (!sta) {
+ rcu_read_unlock();
+ return;
+ }
+
sta_info_unlink(&sta);
rcu_read_unlock();
@@ -914,20 +940,19 @@ static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata,
static void ieee80211_associate(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta)
{
- DECLARE_MAC_BUF(mac);
-
ifsta->assoc_tries++;
if (ifsta->assoc_tries > IEEE80211_ASSOC_MAX_TRIES) {
- printk(KERN_DEBUG "%s: association with AP %s"
+ printk(KERN_DEBUG "%s: association with AP %pM"
" timed out\n",
- sdata->dev->name, print_mac(mac, ifsta->bssid));
+ sdata->dev->name, ifsta->bssid);
ifsta->state = IEEE80211_STA_MLME_DISABLED;
+ ieee80211_sta_send_apinfo(sdata, ifsta);
return;
}
ifsta->state = IEEE80211_STA_MLME_ASSOCIATE;
- printk(KERN_DEBUG "%s: associate with AP %s\n",
- sdata->dev->name, print_mac(mac, ifsta->bssid));
+ printk(KERN_DEBUG "%s: associate with AP %pM\n",
+ sdata->dev->name, ifsta->bssid);
if (ieee80211_privacy_mismatch(sdata, ifsta)) {
printk(KERN_DEBUG "%s: mismatch in privacy configuration and "
"mixed-cell disabled - abort association\n", sdata->dev->name);
@@ -947,7 +972,6 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
int disassoc;
- DECLARE_MAC_BUF(mac);
/* TODO: start monitoring current AP signal quality and number of
* missed beacons. Scan other channels every now and then and search
@@ -960,8 +984,8 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata,
sta = sta_info_get(local, ifsta->bssid);
if (!sta) {
- printk(KERN_DEBUG "%s: No STA entry for own AP %s\n",
- sdata->dev->name, print_mac(mac, ifsta->bssid));
+ printk(KERN_DEBUG "%s: No STA entry for own AP %pM\n",
+ sdata->dev->name, ifsta->bssid);
disassoc = 1;
} else {
disassoc = 0;
@@ -969,9 +993,9 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata,
sta->last_rx + IEEE80211_MONITORING_INTERVAL)) {
if (ifsta->flags & IEEE80211_STA_PROBEREQ_POLL) {
printk(KERN_DEBUG "%s: No ProbeResp from "
- "current AP %s - assume out of "
+ "current AP %pM - assume out of "
"range\n",
- sdata->dev->name, print_mac(mac, ifsta->bssid));
+ sdata->dev->name, ifsta->bssid);
disassoc = 1;
} else
ieee80211_send_probe_req(sdata, ifsta->bssid,
@@ -1032,7 +1056,6 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
size_t len)
{
u16 auth_alg, auth_transaction, status_code;
- DECLARE_MAC_BUF(mac);
if (ifsta->state != IEEE80211_STA_MLME_AUTHENTICATE &&
sdata->vif.type != NL80211_IFTYPE_ADHOC)
@@ -1125,7 +1148,6 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
size_t len)
{
u16 reason_code;
- DECLARE_MAC_BUF(mac);
if (len < 24 + 2)
return;
@@ -1136,7 +1158,8 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
if (ifsta->flags & IEEE80211_STA_AUTHENTICATED)
- printk(KERN_DEBUG "%s: deauthenticated\n", sdata->dev->name);
+ printk(KERN_DEBUG "%s: deauthenticated (Reason: %u)\n",
+ sdata->dev->name, reason_code);
if (ifsta->state == IEEE80211_STA_MLME_AUTHENTICATE ||
ifsta->state == IEEE80211_STA_MLME_ASSOCIATE ||
@@ -1157,7 +1180,6 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
size_t len)
{
u16 reason_code;
- DECLARE_MAC_BUF(mac);
if (len < 24 + 2)
return;
@@ -1168,7 +1190,8 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
if (ifsta->flags & IEEE80211_STA_ASSOCIATED)
- printk(KERN_DEBUG "%s: disassociated\n", sdata->dev->name);
+ printk(KERN_DEBUG "%s: disassociated (Reason: %u)\n",
+ sdata->dev->name, reason_code);
if (ifsta->state == IEEE80211_STA_MLME_ASSOCIATED) {
ifsta->state = IEEE80211_STA_MLME_ASSOCIATE;
@@ -1176,7 +1199,7 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
IEEE80211_RETRY_AUTH_INTERVAL);
}
- ieee80211_set_disassoc(sdata, ifsta, false, false, 0);
+ ieee80211_set_disassoc(sdata, ifsta, false, false, reason_code);
}
@@ -1192,11 +1215,12 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
u64 rates, basic_rates;
u16 capab_info, status_code, aid;
struct ieee802_11_elems elems;
- struct ieee80211_bss_conf *bss_conf = &sdata->bss_conf;
+ struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
u8 *pos;
+ u32 changed = 0;
int i, j;
- DECLARE_MAC_BUF(mac);
- bool have_higher_than_11mbit = false;
+ bool have_higher_than_11mbit = false, newsta = false;
+ u16 ap_ht_cap_flags;
/* AssocResp and ReassocResp have identical structure, so process both
* of them in this function. */
@@ -1214,9 +1238,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
- printk(KERN_DEBUG "%s: RX %sssocResp from %s (capab=0x%x "
+ printk(KERN_DEBUG "%s: RX %sssocResp from %pM (capab=0x%x "
"status=%d aid=%d)\n",
- sdata->dev->name, reassoc ? "Rea" : "A", print_mac(mac, mgmt->sa),
+ sdata->dev->name, reassoc ? "Rea" : "A", mgmt->sa,
capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));
if (status_code != WLAN_STATUS_SUCCESS) {
@@ -1259,7 +1283,8 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
sta = sta_info_get(local, ifsta->bssid);
if (!sta) {
struct ieee80211_bss *bss;
- int err;
+
+ newsta = true;
sta = sta_info_alloc(sdata, ifsta->bssid, GFP_ATOMIC);
if (!sta) {
@@ -1278,13 +1303,6 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
ieee80211_rx_bss_put(local, bss);
}
- err = sta_info_insert(sta);
- if (err) {
- printk(KERN_DEBUG "%s: failed to insert STA entry for"
- " the AP (error %d)\n", sdata->dev->name, err);
- rcu_read_unlock();
- return;
- }
/* update new sta with its last rx activity */
sta->last_rx = jiffies;
}
@@ -1308,34 +1326,40 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
for (i = 0; i < elems.supp_rates_len; i++) {
int rate = (elems.supp_rates[i] & 0x7f) * 5;
+ bool is_basic = !!(elems.supp_rates[i] & 0x80);
if (rate > 110)
have_higher_than_11mbit = true;
for (j = 0; j < sband->n_bitrates; j++) {
- if (sband->bitrates[j].bitrate == rate)
+ if (sband->bitrates[j].bitrate == rate) {
rates |= BIT(j);
- if (elems.supp_rates[i] & 0x80)
- basic_rates |= BIT(j);
+ if (is_basic)
+ basic_rates |= BIT(j);
+ break;
+ }
}
}
for (i = 0; i < elems.ext_supp_rates_len; i++) {
int rate = (elems.ext_supp_rates[i] & 0x7f) * 5;
+ bool is_basic = !!(elems.supp_rates[i] & 0x80);
if (rate > 110)
have_higher_than_11mbit = true;
for (j = 0; j < sband->n_bitrates; j++) {
- if (sband->bitrates[j].bitrate == rate)
+ if (sband->bitrates[j].bitrate == rate) {
rates |= BIT(j);
- if (elems.ext_supp_rates[i] & 0x80)
- basic_rates |= BIT(j);
+ if (is_basic)
+ basic_rates |= BIT(j);
+ break;
+ }
}
}
sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
- sdata->bss_conf.basic_rates = basic_rates;
+ sdata->vif.bss_conf.basic_rates = basic_rates;
/* cf. IEEE 802.11 9.2.12 */
if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ &&
@@ -1344,31 +1368,43 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
else
sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
- if (elems.ht_cap_elem && elems.ht_info_elem && elems.wmm_param &&
- (ifsta->flags & IEEE80211_STA_WMM_ENABLED)) {
- struct ieee80211_ht_bss_info bss_info;
- ieee80211_ht_cap_ie_to_ht_info(
- elems.ht_cap_elem, &sta->sta.ht_info);
- ieee80211_ht_addt_info_ie_to_ht_bss_info(
- elems.ht_info_elem, &bss_info);
- ieee80211_handle_ht(local, 1, &sta->sta.ht_info, &bss_info);
- }
+ if (elems.ht_cap_elem)
+ ieee80211_ht_cap_ie_to_sta_ht_cap(sband,
+ elems.ht_cap_elem, &sta->sta.ht_cap);
+
+ ap_ht_cap_flags = sta->sta.ht_cap.cap;
rate_control_rate_init(sta);
- if (elems.wmm_param) {
+ if (elems.wmm_param)
set_sta_flags(sta, WLAN_STA_WME);
- rcu_read_unlock();
+
+ if (newsta) {
+ int err = sta_info_insert(sta);
+ if (err) {
+ printk(KERN_DEBUG "%s: failed to insert STA entry for"
+ " the AP (error %d)\n", sdata->dev->name, err);
+ rcu_read_unlock();
+ return;
+ }
+ }
+
+ rcu_read_unlock();
+
+ if (elems.wmm_param)
ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param,
elems.wmm_param_len);
- } else
- rcu_read_unlock();
+
+ if (elems.ht_info_elem && elems.wmm_param &&
+ (ifsta->flags & IEEE80211_STA_WMM_ENABLED))
+ changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
+ ap_ht_cap_flags);
/* set AID and assoc capability,
* ieee80211_set_associated() will tell the driver */
bss_conf->aid = aid;
bss_conf->assoc_capability = capab_info;
- ieee80211_set_associated(sdata, ifsta);
+ ieee80211_set_associated(sdata, ifsta, changed);
ieee80211_associated(sdata, ifsta);
}
@@ -1386,6 +1422,13 @@ static int ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband;
union iwreq_data wrqu;
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
+ if (!skb) {
+ printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
+ "response\n", sdata->dev->name);
+ return -ENOMEM;
+ }
+
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
/* Remove possible STA entries from other IBSS networks. */
@@ -1411,63 +1454,62 @@ static int ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
return res;
/* Build IBSS probe response */
- skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
- if (skb) {
- skb_reserve(skb, local->hw.extra_tx_headroom);
- mgmt = (struct ieee80211_mgmt *)
- skb_put(skb, 24 + sizeof(mgmt->u.beacon));
- memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon));
- mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
- IEEE80211_STYPE_PROBE_RESP);
- memset(mgmt->da, 0xff, ETH_ALEN);
- memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
- memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
- mgmt->u.beacon.beacon_int =
- cpu_to_le16(local->hw.conf.beacon_int);
- mgmt->u.beacon.timestamp = cpu_to_le64(bss->timestamp);
- mgmt->u.beacon.capab_info = cpu_to_le16(bss->capability);
-
- pos = skb_put(skb, 2 + ifsta->ssid_len);
- *pos++ = WLAN_EID_SSID;
- *pos++ = ifsta->ssid_len;
- memcpy(pos, ifsta->ssid, ifsta->ssid_len);
-
- rates = bss->supp_rates_len;
- if (rates > 8)
- rates = 8;
- pos = skb_put(skb, 2 + rates);
- *pos++ = WLAN_EID_SUPP_RATES;
- *pos++ = rates;
- memcpy(pos, bss->supp_rates, rates);
+ skb_reserve(skb, local->hw.extra_tx_headroom);
- if (bss->band == IEEE80211_BAND_2GHZ) {
- pos = skb_put(skb, 2 + 1);
- *pos++ = WLAN_EID_DS_PARAMS;
- *pos++ = 1;
- *pos++ = ieee80211_frequency_to_channel(bss->freq);
- }
+ mgmt = (struct ieee80211_mgmt *)
+ skb_put(skb, 24 + sizeof(mgmt->u.beacon));
+ memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon));
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_PROBE_RESP);
+ memset(mgmt->da, 0xff, ETH_ALEN);
+ memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+ memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+ mgmt->u.beacon.beacon_int =
+ cpu_to_le16(local->hw.conf.beacon_int);
+ mgmt->u.beacon.timestamp = cpu_to_le64(bss->timestamp);
+ mgmt->u.beacon.capab_info = cpu_to_le16(bss->capability);
- pos = skb_put(skb, 2 + 2);
- *pos++ = WLAN_EID_IBSS_PARAMS;
- *pos++ = 2;
- /* FIX: set ATIM window based on scan results */
- *pos++ = 0;
- *pos++ = 0;
+ pos = skb_put(skb, 2 + ifsta->ssid_len);
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = ifsta->ssid_len;
+ memcpy(pos, ifsta->ssid, ifsta->ssid_len);
- if (bss->supp_rates_len > 8) {
- rates = bss->supp_rates_len - 8;
- pos = skb_put(skb, 2 + rates);
- *pos++ = WLAN_EID_EXT_SUPP_RATES;
- *pos++ = rates;
- memcpy(pos, &bss->supp_rates[8], rates);
- }
+ rates = bss->supp_rates_len;
+ if (rates > 8)
+ rates = 8;
+ pos = skb_put(skb, 2 + rates);
+ *pos++ = WLAN_EID_SUPP_RATES;
+ *pos++ = rates;
+ memcpy(pos, bss->supp_rates, rates);
+
+ if (bss->band == IEEE80211_BAND_2GHZ) {
+ pos = skb_put(skb, 2 + 1);
+ *pos++ = WLAN_EID_DS_PARAMS;
+ *pos++ = 1;
+ *pos++ = ieee80211_frequency_to_channel(bss->freq);
+ }
- ifsta->probe_resp = skb;
+ pos = skb_put(skb, 2 + 2);
+ *pos++ = WLAN_EID_IBSS_PARAMS;
+ *pos++ = 2;
+ /* FIX: set ATIM window based on scan results */
+ *pos++ = 0;
+ *pos++ = 0;
- ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
+ if (bss->supp_rates_len > 8) {
+ rates = bss->supp_rates_len - 8;
+ pos = skb_put(skb, 2 + rates);
+ *pos++ = WLAN_EID_EXT_SUPP_RATES;
+ *pos++ = rates;
+ memcpy(pos, &bss->supp_rates[8], rates);
}
+ ifsta->probe_resp = skb;
+
+ ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
+
+
rates = 0;
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
for (i = 0; i < bss->supp_rates_len; i++) {
@@ -1507,8 +1549,6 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
u64 beacon_timestamp, rx_timestamp;
u64 supp_rates = 0;
enum ieee80211_band band = rx_status->band;
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
if (elems->ds_params && elems->ds_params_len == 1)
freq = ieee80211_channel_to_frequency(elems->ds_params[0]);
@@ -1538,17 +1578,16 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
#ifdef CONFIG_MAC80211_IBSS_DEBUG
if (sta->sta.supp_rates[band] != prev_rates)
printk(KERN_DEBUG "%s: updated supp_rates set "
- "for %s based on beacon info (0x%llx | "
+ "for %pM based on beacon info (0x%llx | "
"0x%llx -> 0x%llx)\n",
sdata->dev->name,
- print_mac(mac, sta->sta.addr),
+ sta->sta.addr,
(unsigned long long) prev_rates,
(unsigned long long) supp_rates,
(unsigned long long) sta->sta.supp_rates[band]);
#endif
} else {
- ieee80211_ibss_add_sta(sdata, NULL, mgmt->bssid,
- mgmt->sa, supp_rates);
+ ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, supp_rates);
}
rcu_read_unlock();
@@ -1595,8 +1634,13 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
* e.g: at 1 MBit that means mactime is 192 usec earlier
* (=24 bytes * 8 usecs/byte) than the beacon timestamp.
*/
- int rate = local->hw.wiphy->bands[band]->
+ int rate;
+ if (rx_status->flag & RX_FLAG_HT) {
+ rate = 65; /* TODO: HT rates */
+ } else {
+ rate = local->hw.wiphy->bands[band]->
bitrates[rx_status->rate_idx].bitrate;
+ }
rx_timestamp = rx_status->mactime + (24 * 8 * 10 / rate);
} else if (local && local->ops && local->ops->get_tsf)
/* second best option: get current TSF */
@@ -1605,10 +1649,9 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
/* can't merge without knowing the TSF */
rx_timestamp = -1LLU;
#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG "RX beacon SA=%s BSSID="
- "%s TSF=0x%llx BCN=0x%llx diff=%lld @%lu\n",
- print_mac(mac, mgmt->sa),
- print_mac(mac2, mgmt->bssid),
+ printk(KERN_DEBUG "RX beacon SA=%pM BSSID="
+ "%pM TSF=0x%llx BCN=0x%llx diff=%lld @%lu\n",
+ mgmt->sa, mgmt->bssid,
(unsigned long long)rx_timestamp,
(unsigned long long)beacon_timestamp,
(unsigned long long)(rx_timestamp - beacon_timestamp),
@@ -1617,13 +1660,11 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
if (beacon_timestamp > rx_timestamp) {
#ifdef CONFIG_MAC80211_IBSS_DEBUG
printk(KERN_DEBUG "%s: beacon TSF higher than "
- "local TSF - IBSS merge with BSSID %s\n",
- sdata->dev->name, print_mac(mac, mgmt->bssid));
+ "local TSF - IBSS merge with BSSID %pM\n",
+ sdata->dev->name, mgmt->bssid);
#endif
ieee80211_sta_join_ibss(sdata, &sdata->u.sta, bss);
- ieee80211_ibss_add_sta(sdata, NULL,
- mgmt->bssid, mgmt->sa,
- supp_rates);
+ ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, supp_rates);
}
}
@@ -1671,8 +1712,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
size_t baselen;
struct ieee802_11_elems elems;
struct ieee80211_local *local = sdata->local;
- struct ieee80211_conf *conf = &local->hw.conf;
u32 changed = 0;
+ bool erp_valid;
+ u8 erp_value = 0;
/* Process beacon from the current BSS */
baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
@@ -1694,22 +1736,49 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param,
elems.wmm_param_len);
- if (elems.erp_info && elems.erp_info_len >= 1)
- changed |= ieee80211_handle_erp_ie(sdata, elems.erp_info[0]);
- else {
- u16 capab = le16_to_cpu(mgmt->u.beacon.capab_info);
- changed |= ieee80211_handle_protect_preamb(sdata, false,
- (capab & WLAN_CAPABILITY_SHORT_PREAMBLE) != 0);
+
+ if (elems.erp_info && elems.erp_info_len >= 1) {
+ erp_valid = true;
+ erp_value = elems.erp_info[0];
+ } else {
+ erp_valid = false;
}
+ changed |= ieee80211_handle_bss_capability(sdata,
+ le16_to_cpu(mgmt->u.beacon.capab_info),
+ erp_valid, erp_value);
- if (elems.ht_cap_elem && elems.ht_info_elem &&
- elems.wmm_param && conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) {
- struct ieee80211_ht_bss_info bss_info;
- ieee80211_ht_addt_info_ie_to_ht_bss_info(
- elems.ht_info_elem, &bss_info);
- changed |= ieee80211_handle_ht(local, 1, &conf->ht_conf,
- &bss_info);
+ if (elems.ht_cap_elem && elems.ht_info_elem && elems.wmm_param) {
+ struct sta_info *sta;
+ struct ieee80211_supported_band *sband;
+ u16 ap_ht_cap_flags;
+
+ rcu_read_lock();
+
+ sta = sta_info_get(local, ifsta->bssid);
+ if (!sta) {
+ rcu_read_unlock();
+ return;
+ }
+
+ sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+
+ ieee80211_ht_cap_ie_to_sta_ht_cap(sband,
+ elems.ht_cap_elem, &sta->sta.ht_cap);
+
+ ap_ht_cap_flags = sta->sta.ht_cap.cap;
+
+ rcu_read_unlock();
+
+ changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
+ ap_ht_cap_flags);
+ }
+
+ if (elems.country_elem) {
+ /* Note we are only reviewing this on beacons
+ * for the BSSID we are associated to */
+ regulatory_hint_11d(local->hw.wiphy,
+ elems.country_elem, elems.country_elem_len);
}
ieee80211_bss_info_change_notify(sdata, changed);
@@ -1727,11 +1796,6 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb;
struct ieee80211_mgmt *resp;
u8 *pos, *end;
- DECLARE_MAC_BUF(mac);
-#ifdef CONFIG_MAC80211_IBSS_DEBUG
- DECLARE_MAC_BUF(mac2);
- DECLARE_MAC_BUF(mac3);
-#endif
if (sdata->vif.type != NL80211_IFTYPE_ADHOC ||
ifsta->state != IEEE80211_STA_MLME_IBSS_JOINED ||
@@ -1744,10 +1808,10 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
tx_last_beacon = 1;
#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG "%s: RX ProbeReq SA=%s DA=%s BSSID="
- "%s (tx_last_beacon=%d)\n",
- sdata->dev->name, print_mac(mac, mgmt->sa), print_mac(mac2, mgmt->da),
- print_mac(mac3, mgmt->bssid), tx_last_beacon);
+ printk(KERN_DEBUG "%s: RX ProbeReq SA=%pM DA=%pM BSSID=%pM"
+ " (tx_last_beacon=%d)\n",
+ sdata->dev->name, mgmt->sa, mgmt->da,
+ mgmt->bssid, tx_last_beacon);
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
if (!tx_last_beacon)
@@ -1763,8 +1827,8 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
pos + 2 + pos[1] > end) {
#ifdef CONFIG_MAC80211_IBSS_DEBUG
printk(KERN_DEBUG "%s: Invalid SSID IE in ProbeReq "
- "from %s\n",
- sdata->dev->name, print_mac(mac, mgmt->sa));
+ "from %pM\n",
+ sdata->dev->name, mgmt->sa);
#endif
return;
}
@@ -1783,8 +1847,8 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
resp = (struct ieee80211_mgmt *) skb->data;
memcpy(resp->da, mgmt->sa, ETH_ALEN);
#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG "%s: Sending ProbeResp to %s\n",
- sdata->dev->name, print_mac(mac, resp->da));
+ printk(KERN_DEBUG "%s: Sending ProbeResp to %pM\n",
+ sdata->dev->name, resp->da);
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
ieee80211_tx_skb(sdata, skb, 0);
}
@@ -1972,7 +2036,7 @@ static int ieee80211_sta_match_ssid(struct ieee80211_if_sta *ifsta,
}
}
- if (hidden_ssid && ifsta->ssid_len == ssid_len)
+ if (hidden_ssid && (ifsta->ssid_len == ssid_len || ssid_len == 0))
return 1;
if (ssid_len == 1 && ssid[0] == ' ')
@@ -1990,7 +2054,6 @@ static int ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata,
u8 bssid[ETH_ALEN], *pos;
int i;
int ret;
- DECLARE_MAC_BUF(mac);
#if 0
/* Easier testing, use fixed BSSID. */
@@ -2006,8 +2069,8 @@ static int ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata,
bssid[0] |= 0x02;
#endif
- printk(KERN_DEBUG "%s: Creating new IBSS network, BSSID %s\n",
- sdata->dev->name, print_mac(mac, bssid));
+ printk(KERN_DEBUG "%s: Creating new IBSS network, BSSID %pM\n",
+ sdata->dev->name, bssid);
bss = ieee80211_rx_bss_add(local, bssid,
local->hw.conf.channel->center_freq,
@@ -2050,8 +2113,6 @@ static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata,
int found = 0;
u8 bssid[ETH_ALEN];
int active_ibss;
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
if (ifsta->ssid_len == 0)
return -EINVAL;
@@ -2068,8 +2129,7 @@ static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata,
|| !(bss->capability & WLAN_CAPABILITY_IBSS))
continue;
#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG " bssid=%s found\n",
- print_mac(mac, bss->bssid));
+ printk(KERN_DEBUG " bssid=%pM found\n", bss->bssid);
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
memcpy(bssid, bss->bssid, ETH_ALEN);
found = 1;
@@ -2080,9 +2140,8 @@ static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata,
#ifdef CONFIG_MAC80211_IBSS_DEBUG
if (found)
- printk(KERN_DEBUG " sta_find_ibss: selected %s current "
- "%s\n", print_mac(mac, bssid),
- print_mac(mac2, ifsta->bssid));
+ printk(KERN_DEBUG " sta_find_ibss: selected %pM current "
+ "%pM\n", bssid, ifsta->bssid);
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
if (found && memcmp(ifsta->bssid, bssid, ETH_ALEN) != 0) {
@@ -2099,9 +2158,9 @@ static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata,
if (!bss)
goto dont_join;
- printk(KERN_DEBUG "%s: Selected IBSS BSSID %s"
+ printk(KERN_DEBUG "%s: Selected IBSS BSSID %pM"
" based on configured SSID\n",
- sdata->dev->name, print_mac(mac, bssid));
+ sdata->dev->name, bssid);
ret = ieee80211_sta_join_ibss(sdata, ifsta, bss);
ieee80211_rx_bss_put(local, bss);
return ret;
@@ -2338,12 +2397,10 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
* must be callable in atomic context.
*/
struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, u8 *bssid,
- u8 *addr, u64 supp_rates)
+ u8 *bssid,u8 *addr, u64 supp_rates)
{
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
- DECLARE_MAC_BUF(mac);
int band = local->hw.conf.channel->band;
/* TODO: Could consider removing the least recently used entry and
@@ -2351,7 +2408,7 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) {
if (net_ratelimit()) {
printk(KERN_DEBUG "%s: No room for a new IBSS STA "
- "entry %s\n", sdata->dev->name, print_mac(mac, addr));
+ "entry %pM\n", sdata->dev->name, addr);
}
return NULL;
}
@@ -2360,8 +2417,8 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
return NULL;
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: Adding new IBSS station %s (dev=%s)\n",
- wiphy_name(local->hw.wiphy), print_mac(mac, addr), sdata->dev->name);
+ printk(KERN_DEBUG "%s: Adding new IBSS station %pM (dev=%s)\n",
+ wiphy_name(local->hw.wiphy), addr, sdata->dev->name);
#endif
sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
@@ -2408,7 +2465,6 @@ void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata,
int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t len)
{
struct ieee80211_if_sta *ifsta;
- int res;
if (len > IEEE80211_MAX_SSID_LEN)
return -EINVAL;
@@ -2420,19 +2476,6 @@ int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size
memcpy(ifsta->ssid, ssid, len);
ifsta->ssid_len = len;
ifsta->flags &= ~IEEE80211_STA_PREV_BSSID_SET;
-
- res = 0;
- /*
- * Hack! MLME code needs to be cleaned up to have different
- * entry points for configuration and internal selection change
- */
- if (netif_running(sdata->dev))
- res = ieee80211_if_config(sdata, IEEE80211_IFCC_SSID);
- if (res) {
- printk(KERN_DEBUG "%s: Failed to config new SSID to "
- "the low-level driver\n", sdata->dev->name);
- return res;
- }
}
if (len)
@@ -2560,3 +2603,39 @@ void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
ieee80211_restart_sta_timer(sdata);
rcu_read_unlock();
}
+
+void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
+{
+ struct ieee80211_local *local =
+ container_of(work, struct ieee80211_local,
+ dynamic_ps_disable_work);
+
+ if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+ local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+ }
+
+ ieee80211_wake_queues_by_reason(&local->hw,
+ IEEE80211_QUEUE_STOP_REASON_PS);
+}
+
+void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
+{
+ struct ieee80211_local *local =
+ container_of(work, struct ieee80211_local,
+ dynamic_ps_enable_work);
+
+ if (local->hw.conf.flags & IEEE80211_CONF_PS)
+ return;
+
+ local->hw.conf.flags |= IEEE80211_CONF_PS;
+
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+}
+
+void ieee80211_dynamic_ps_timer(unsigned long data)
+{
+ struct ieee80211_local *local = (void *) data;
+
+ queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work);
+}
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index 5d786720d935..3fa7ab285066 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -199,48 +199,44 @@ static void rate_control_release(struct kref *kref)
}
void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_supported_band *sband,
- struct sta_info *sta, struct sk_buff *skb,
- struct rate_selection *sel)
+ struct sta_info *sta,
+ struct ieee80211_tx_rate_control *txrc)
{
struct rate_control_ref *ref = sdata->local->rate_ctrl;
void *priv_sta = NULL;
struct ieee80211_sta *ista = NULL;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb);
int i;
- sel->rate_idx = -1;
- sel->nonerp_idx = -1;
- sel->probe_idx = -1;
- sel->max_rate_idx = sdata->max_ratectrl_rateidx;
-
if (sta) {
ista = &sta->sta;
priv_sta = sta->rate_ctrl_priv;
}
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ info->control.rates[i].idx = -1;
+ info->control.rates[i].flags = 0;
+ info->control.rates[i].count = 1;
+ }
+
if (sta && sdata->force_unicast_rateidx > -1)
- sel->rate_idx = sdata->force_unicast_rateidx;
+ info->control.rates[0].idx = sdata->force_unicast_rateidx;
else
- ref->ops->get_rate(ref->priv, sband, ista, priv_sta, skb, sel);
-
- if (sdata->max_ratectrl_rateidx > -1 &&
- sel->rate_idx > sdata->max_ratectrl_rateidx)
- sel->rate_idx = sdata->max_ratectrl_rateidx;
-
- BUG_ON(sel->rate_idx < 0);
-
- /* Select a non-ERP backup rate. */
- if (sel->nonerp_idx < 0) {
- for (i = 0; i < sband->n_bitrates; i++) {
- struct ieee80211_rate *rate = &sband->bitrates[i];
- if (sband->bitrates[sel->rate_idx].bitrate < rate->bitrate)
- break;
-
- if (rate_supported(ista, sband->band, i) &&
- !(rate->flags & IEEE80211_RATE_ERP_G))
- sel->nonerp_idx = i;
- }
+ ref->ops->get_rate(ref->priv, ista, priv_sta, txrc);
+
+ /*
+ * try to enforce the maximum rate the user wanted
+ */
+ if (sdata->max_ratectrl_rateidx > -1)
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ if (info->control.rates[i].flags & IEEE80211_TX_RC_MCS)
+ continue;
+ info->control.rates[i].idx =
+ min_t(s8, info->control.rates[i].idx,
+ sdata->max_ratectrl_rateidx);
}
+
+ BUG_ON(info->control.rates[0].idx < 0);
}
struct rate_control_ref *rate_control_get(struct rate_control_ref *ref)
diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h
index d0092f847f82..9b632372e688 100644
--- a/net/mac80211/rate.h
+++ b/net/mac80211/rate.h
@@ -31,9 +31,8 @@ struct rate_control_ref {
struct rate_control_ref *rate_control_alloc(const char *name,
struct ieee80211_local *local);
void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_supported_band *sband,
- struct sta_info *sta, struct sk_buff *skb,
- struct rate_selection *sel);
+ struct sta_info *sta,
+ struct ieee80211_tx_rate_control *txrc);
struct rate_control_ref *rate_control_get(struct rate_control_ref *ref);
void rate_control_put(struct rate_control_ref *ref);
@@ -64,12 +63,6 @@ static inline void rate_control_rate_init(struct sta_info *sta)
}
-static inline void rate_control_clear(struct ieee80211_local *local)
-{
- struct rate_control_ref *ref = local->rate_ctrl;
- ref->ops->clear(ref->priv);
-}
-
static inline void *rate_control_alloc_sta(struct rate_control_ref *ref,
struct ieee80211_sta *sta,
gfp_t gfp)
@@ -125,6 +118,19 @@ static inline void rc80211_pid_exit(void)
}
#endif
+#ifdef CONFIG_MAC80211_RC_MULTIRATE_PID
+extern int rc80211_multirate_pid_init(void);
+extern void rc80211_multirate_pid_exit(void);
+#else
+static inline int rc80211_multirate_pid_init(void)
+{
+ return 0;
+}
+static inline void rc80211_multirate_pid_exit(void)
+{
+}
+#endif
+
#ifdef CONFIG_MAC80211_RC_MINSTREL
extern int rc80211_minstrel_init(void);
extern void rc80211_minstrel_exit(void);
diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c
index f6d69dab07a3..3824990d340b 100644
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -126,7 +126,9 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
mr->adjusted_retry_count = mr->retry_count >> 1;
if (mr->adjusted_retry_count > 2)
mr->adjusted_retry_count = 2;
+ mr->sample_limit = 4;
} else {
+ mr->sample_limit = -1;
mr->adjusted_retry_count = mr->retry_count;
}
if (!mr->adjusted_retry_count)
@@ -169,30 +171,20 @@ minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband,
{
struct minstrel_sta_info *mi = priv_sta;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct ieee80211_tx_altrate *ar = info->status.retries;
- struct minstrel_priv *mp = priv;
- int i, ndx, tries;
- int success = 0;
-
- if (!info->status.excessive_retries)
- success = 1;
+ struct ieee80211_tx_rate *ar = info->status.rates;
+ int i, ndx;
+ int success;
- if (!mp->has_mrr || (ar[0].rate_idx < 0)) {
- ndx = rix_to_ndx(mi, info->tx_rate_idx);
- tries = info->status.retry_count + 1;
- mi->r[ndx].success += success;
- mi->r[ndx].attempts += tries;
- return;
- }
+ success = !!(info->flags & IEEE80211_TX_STAT_ACK);
- for (i = 0; i < 4; i++) {
- if (ar[i].rate_idx < 0)
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ if (ar[i].idx < 0)
break;
- ndx = rix_to_ndx(mi, ar[i].rate_idx);
- mi->r[ndx].attempts += ar[i].limit + 1;
+ ndx = rix_to_ndx(mi, ar[i].idx);
+ mi->r[ndx].attempts += ar[i].count;
- if ((i != 3) && (ar[i + 1].rate_idx < 0))
+ if ((i != IEEE80211_TX_MAX_RATES - 1) && (ar[i + 1].idx < 0))
mi->r[ndx].success += success;
}
@@ -210,9 +202,9 @@ minstrel_get_retry_count(struct minstrel_rate *mr,
{
unsigned int retry = mr->adjusted_retry_count;
- if (info->flags & IEEE80211_TX_CTL_USE_RTS_CTS)
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS)
retry = max(2U, min(mr->retry_count_rtscts, retry));
- else if (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT)
+ else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)
retry = max(2U, min(mr->retry_count_cts, retry));
return retry;
}
@@ -233,15 +225,16 @@ minstrel_get_next_sample(struct minstrel_sta_info *mi)
return sample_ndx;
}
-void
-minstrel_get_rate(void *priv, struct ieee80211_supported_band *sband,
- struct ieee80211_sta *sta, void *priv_sta,
- struct sk_buff *skb, struct rate_selection *sel)
+static void
+minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
+ void *priv_sta, struct ieee80211_tx_rate_control *txrc)
{
+ struct sk_buff *skb = txrc->skb;
+ struct ieee80211_supported_band *sband = txrc->sband;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct minstrel_sta_info *mi = priv_sta;
struct minstrel_priv *mp = priv;
- struct ieee80211_tx_altrate *ar = info->control.retries;
+ struct ieee80211_tx_rate *ar = info->control.rates;
unsigned int ndx, sample_ndx = 0;
bool mrr;
bool sample_slower = false;
@@ -251,16 +244,12 @@ minstrel_get_rate(void *priv, struct ieee80211_supported_band *sband,
int sample_rate;
if (!sta || !mi || use_low_rate(skb)) {
- sel->rate_idx = rate_lowest_index(sband, sta);
+ ar[0].idx = rate_lowest_index(sband, sta);
+ ar[0].count = mp->max_retry;
return;
}
- mrr = mp->has_mrr;
-
- /* mac80211 does not allow mrr for RTS/CTS */
- if ((info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) ||
- (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT))
- mrr = false;
+ mrr = mp->has_mrr && !txrc->rts && !txrc->bss_conf->use_cts_prot;
if (time_after(jiffies, mi->stats_update + (mp->update_interval *
HZ) / 1000))
@@ -278,7 +267,8 @@ minstrel_get_rate(void *priv, struct ieee80211_supported_band *sband,
(mi->sample_count + mi->sample_deferred / 2);
/* delta > 0: sampling required */
- if (delta > 0) {
+ if ((delta > 0) && (mrr || !mi->prev_sample)) {
+ struct minstrel_rate *msr;
if (mi->packet_count >= 10000) {
mi->sample_deferred = 0;
mi->sample_count = 0;
@@ -297,13 +287,20 @@ minstrel_get_rate(void *priv, struct ieee80211_supported_band *sband,
}
sample_ndx = minstrel_get_next_sample(mi);
+ msr = &mi->r[sample_ndx];
sample = true;
- sample_slower = mrr && (mi->r[sample_ndx].perfect_tx_time >
+ sample_slower = mrr && (msr->perfect_tx_time >
mi->r[ndx].perfect_tx_time);
if (!sample_slower) {
- ndx = sample_ndx;
- mi->sample_count++;
+ if (msr->sample_limit != 0) {
+ ndx = sample_ndx;
+ mi->sample_count++;
+ if (msr->sample_limit > 0)
+ msr->sample_limit--;
+ } else {
+ sample = false;
+ }
} else {
/* Only use IEEE80211_TX_CTL_RATE_CTRL_PROBE to mark
* packets that have the sampling rate deferred to the
@@ -315,13 +312,22 @@ minstrel_get_rate(void *priv, struct ieee80211_supported_band *sband,
mi->sample_deferred++;
}
}
- sel->rate_idx = mi->r[ndx].rix;
- info->control.retry_limit = minstrel_get_retry_count(&mi->r[ndx], info);
+ mi->prev_sample = sample;
+
+ /* If we're not using MRR and the sampling rate already
+ * has a probability of >95%, we shouldn't be attempting
+ * to use it, as this only wastes precious airtime */
+ if (!mrr && sample && (mi->r[ndx].probability > 17100))
+ ndx = mi->max_tp_rate;
+
+ ar[0].idx = mi->r[ndx].rix;
+ ar[0].count = minstrel_get_retry_count(&mi->r[ndx], info);
if (!mrr) {
- ar[0].rate_idx = mi->lowest_rix;
- ar[0].limit = mp->max_retry;
- ar[1].rate_idx = -1;
+ if (!sample)
+ ar[0].count = mp->max_retry;
+ ar[1].idx = mi->lowest_rix;
+ ar[1].count = mp->max_retry;
return;
}
@@ -336,9 +342,9 @@ minstrel_get_rate(void *priv, struct ieee80211_supported_band *sband,
}
mrr_ndx[1] = mi->max_prob_rate;
mrr_ndx[2] = 0;
- for (i = 0; i < 3; i++) {
- ar[i].rate_idx = mi->r[mrr_ndx[i]].rix;
- ar[i].limit = mi->r[mrr_ndx[i]].adjusted_retry_count;
+ for (i = 1; i < 4; i++) {
+ ar[i].idx = mi->r[mrr_ndx[i - 1]].rix;
+ ar[i].count = mi->r[mrr_ndx[i - 1]].adjusted_retry_count;
}
}
@@ -389,13 +395,15 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
{
struct minstrel_sta_info *mi = priv_sta;
struct minstrel_priv *mp = priv;
- struct minstrel_rate *mr_ctl;
+ struct ieee80211_local *local = hw_to_local(mp->hw);
+ struct ieee80211_rate *ctl_rate;
unsigned int i, n = 0;
unsigned int t_slot = 9; /* FIXME: get real slot time */
mi->lowest_rix = rate_lowest_index(sband, sta);
- mr_ctl = &mi->r[rix_to_ndx(mi, mi->lowest_rix)];
- mi->sp_ack_dur = mr_ctl->ack_time;
+ ctl_rate = &sband->bitrates[mi->lowest_rix];
+ mi->sp_ack_dur = ieee80211_frame_duration(local, 10, ctl_rate->bitrate,
+ !!(ctl_rate->flags & IEEE80211_RATE_ERP_G), 1);
for (i = 0; i < sband->n_bitrates; i++) {
struct minstrel_rate *mr = &mi->r[n];
@@ -410,11 +418,12 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
mr->rix = i;
mr->bitrate = sband->bitrates[i].bitrate / 5;
- calc_rate_durations(mi, hw_to_local(mp->hw), mr,
+ calc_rate_durations(mi, local, mr,
&sband->bitrates[i]);
/* calculate maximum number of retransmissions before
* fallback (based on maximum segment size) */
+ mr->sample_limit = -1;
mr->retry_count = 1;
mr->retry_count_cts = 1;
mr->retry_count_rtscts = 1;
@@ -500,11 +509,6 @@ minstrel_free_sta(void *priv, struct ieee80211_sta *sta, void *priv_sta)
kfree(mi);
}
-static void
-minstrel_clear(void *priv)
-{
-}
-
static void *
minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
{
@@ -532,13 +536,13 @@ minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
/* maximum time that the hw is allowed to stay in one MRR segment */
mp->segment_size = 6000;
- if (hw->max_altrate_tries > 0)
- mp->max_retry = hw->max_altrate_tries;
+ if (hw->max_rate_tries > 0)
+ mp->max_retry = hw->max_rate_tries;
else
/* safe default, does not necessarily have to match hw properties */
mp->max_retry = 7;
- if (hw->max_altrates >= 3)
+ if (hw->max_rates >= 4)
mp->has_mrr = true;
mp->hw = hw;
@@ -558,7 +562,6 @@ static struct rate_control_ops mac80211_minstrel = {
.tx_status = minstrel_tx_status,
.get_rate = minstrel_get_rate,
.rate_init = minstrel_rate_init,
- .clear = minstrel_clear,
.alloc = minstrel_alloc,
.free = minstrel_free,
.alloc_sta = minstrel_alloc_sta,
diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h
index 9a90a6aee043..869fe0ef951d 100644
--- a/net/mac80211/rc80211_minstrel.h
+++ b/net/mac80211/rc80211_minstrel.h
@@ -16,6 +16,7 @@ struct minstrel_rate {
unsigned int perfect_tx_time;
unsigned int ack_time;
+ int sample_limit;
unsigned int retry_count;
unsigned int retry_count_cts;
unsigned int retry_count_rtscts;
@@ -57,6 +58,7 @@ struct minstrel_sta_info {
int n_rates;
struct minstrel_rate *r;
+ bool prev_sample;
/* sampling table */
u8 *sample_table;
diff --git a/net/mac80211/rc80211_multirate_pid.h b/net/mac80211/rc80211_multirate_pid.h
new file mode 100644
index 000000000000..9f6b473f8821
--- /dev/null
+++ b/net/mac80211/rc80211_multirate_pid.h
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de>
+ * Copyright 2007, Stefano Brivio <stefano.brivio@polimi.it>
+ * Copyright 2009, Digi International <support@digi.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.
+ */
+
+/*
+ * This is a modified version of the 802.11 PID algorithm. The standard
+ * version uses the same rate for all the retries of the same frame. The code
+ * was modified to perform the first few retries at the same rate and then
+ * reduce the data rate for each successive retry until we either run out of retries
+ * or successfully transmit the frame. This seems to provide better performance
+ * on error prone networks.
+ */
+
+#ifndef RC80211_MULTIRATE_PID_H
+#define RC80211_MULTIRATE_PID_H
+
+/* Sampling period for measuring percentage of failed frames in ms. */
+#define RC80211_MULTIRATE_PID_INTERVAL 125
+
+/* Exponential averaging smoothness (used for I part of PID controller) */
+#define RC80211_MULTIRATE_PID_SMOOTHING_SHIFT 3
+#define RC80211_MULTIRATE_PID_SMOOTHING (1 << RC80211_MULTIRATE_PID_SMOOTHING_SHIFT)
+
+/* Sharpening factor (used for D part of PID controller) */
+#define RC80211_MULTIRATE_PID_SHARPENING_FACTOR 0
+#define RC80211_MULTIRATE_PID_SHARPENING_DURATION 0
+
+/* Fixed point arithmetic shifting amount. */
+#define RC80211_MULTIRATE_PID_ARITH_SHIFT 8
+
+/* Fixed point arithmetic factor. */
+#define RC80211_MULTIRATE_PID_ARITH_FACTOR (1 << RC80211_MULTIRATE_PID_ARITH_SHIFT)
+
+/* Proportional PID component coefficient. */
+#define RC80211_MULTIRATE_PID_COEFF_P 15
+/* Integral PID component coefficient. */
+#define RC80211_MULTIRATE_PID_COEFF_I 9
+/* Derivative PID component coefficient. */
+#define RC80211_MULTIRATE_PID_COEFF_D 15
+
+/* Target failed frames rate for the PID controller. NB: This effectively gives
+ * maximum failed frames percentage we're willing to accept. If the wireless
+ * link quality is good, the controller will fail to adjust failed frames
+ * percentage to the target. This is intentional.
+ */
+#define RC80211_MULTIRATE_PID_TARGET_PF 14
+
+/* Rate behaviour normalization quantity over time. */
+#define RC80211_MULTIRATE_PID_NORM_OFFSET 3
+
+/* Push high rates right after loading. */
+#define RC80211_MULTIRATE_PID_FAST_START 0
+
+/* Arithmetic right shift for positive and negative values for ISO C. */
+#define RC80211_MULTIRATE_PID_DO_ARITH_RIGHT_SHIFT(x, y) \
+ ((x) < 0 ? -((-(x)) >> (y)) : (x) >> (y))
+
+enum rc_multirate_pid_event_type {
+ RC80211_MULTIRATE_PID_EVENT_TYPE_TX_STATUS,
+ RC80211_MULTIRATE_PID_EVENT_TYPE_RATE_CHANGE,
+ RC80211_MULTIRATE_PID_EVENT_TYPE_TX_RATE,
+ RC80211_MULTIRATE_PID_EVENT_TYPE_PF_SAMPLE,
+};
+
+union rc_multirate_pid_event_data {
+ /* RC80211_MULTIRATE_PID_EVENT_TX_STATUS */
+ struct {
+ u32 flags;
+ struct ieee80211_tx_info tx_status;
+ };
+ /* RC80211_MULTIRATE_PID_EVENT_TYPE_RATE_CHANGE */
+ /* RC80211_MULTIRATE_PID_EVENT_TYPE_TX_RATE */
+ struct {
+ int index;
+ int rate;
+ };
+ /* RC80211_MULTIRATE_PID_EVENT_TYPE_PF_SAMPLE */
+ struct {
+ s32 pf_sample;
+ s32 prop_err;
+ s32 int_err;
+ s32 der_err;
+ };
+};
+
+struct rc_multirate_pid_event {
+ /* The time when the event occured */
+ unsigned long timestamp;
+
+ /* Event ID number */
+ unsigned int id;
+
+ /* Type of event */
+ enum rc_multirate_pid_event_type type;
+
+ /* type specific data */
+ union rc_multirate_pid_event_data data;
+};
+
+/* Size of the event ring buffer. */
+#define RC80211_MULTIRATE_PID_EVENT_RING_SIZE 32
+
+struct rc_multirate_pid_event_buffer {
+ /* Counter that generates event IDs */
+ unsigned int ev_count;
+
+ /* Ring buffer of events */
+ struct rc_multirate_pid_event
+ ring[RC80211_MULTIRATE_PID_EVENT_RING_SIZE];
+
+ /* Index to the entry in events_buf to be reused */
+ unsigned int next_entry;
+
+ /* Lock that guards against concurrent access to this buffer struct */
+ spinlock_t lock;
+
+ /* Wait queue for poll/select and blocking I/O */
+ wait_queue_head_t waitqueue;
+};
+
+struct rc_multirate_pid_events_file_info {
+ /* The event buffer we read */
+ struct rc_multirate_pid_event_buffer *events;
+
+ /* The entry we have should read next */
+ unsigned int next_entry;
+};
+
+/**
+ * struct rc_multirate_pid_debugfs_entries - tunable parameters
+ *
+ * Algorithm parameters, tunable via debugfs.
+ * @target: target percentage for failed frames
+ * @sampling_period: error sampling interval in milliseconds
+ * @coeff_p: absolute value of the proportional coefficient
+ * @coeff_i: absolute value of the integral coefficient
+ * @coeff_d: absolute value of the derivative coefficient
+ * @smoothing_shift: absolute value of the integral smoothing factor (i.e.
+ * amount of smoothing introduced by the exponential moving average)
+ * @sharpen_factor: absolute value of the derivative sharpening factor (i.e.
+ * amount of emphasis given to the derivative term after low activity
+ * events)
+ * @sharpen_duration: duration of the sharpening effect after the detected low
+ * activity event, relative to sampling_period
+ * @norm_offset: amount of normalization periodically performed on the learnt
+ * rate behaviour values (lower means we should trust more what we learnt
+ * about behaviour of rates, higher means we should trust more the natural
+ * ordering of rates)
+ */
+struct rc_multirate_pid_debugfs_entries {
+ struct dentry *target;
+ struct dentry *sampling_period;
+ struct dentry *coeff_p;
+ struct dentry *coeff_i;
+ struct dentry *coeff_d;
+ struct dentry *smoothing_shift;
+ struct dentry *sharpen_factor;
+ struct dentry *sharpen_duration;
+ struct dentry *norm_offset;
+};
+
+void rate_control_multirate_pid_event_tx_status(struct
+ rc_multirate_pid_event_buffer
+ *buf,
+ struct ieee80211_tx_info
+ *stat);
+
+void rate_control_multirate_pid_event_rate_change(struct
+ rc_multirate_pid_event_buffer
+ *buf, int index,
+ int rate);
+
+void rate_control_multirate_pid_event_tx_rate(struct
+ rc_multirate_pid_event_buffer
+ *buf, int index, int rate);
+
+void rate_control_multirate_pid_event_pf_sample(struct
+ rc_multirate_pid_event_buffer
+ *buf, s32 pf_sample,
+ s32 prop_err, s32 int_err,
+ s32 der_err);
+
+void rate_control_multirate_pid_add_sta_debugfs(void *priv, void *priv_sta,
+ struct dentry *dir);
+
+void rate_control_multirate_pid_remove_sta_debugfs(void *priv,
+ void *priv_sta);
+
+struct rc_multirate_pid_sta_info {
+ unsigned long last_change;
+ unsigned long last_sample;
+
+ u32 tx_num_failed;
+ u32 tx_num_xmit;
+
+ int txrate_idx;
+
+ /* Average failed frames percentage error (i.e. actual vs. target
+ * percentage), scaled by RC80211_MULTIRATE_PID_SMOOTHING. This value is computed
+ * using using an exponential weighted average technique:
+ *
+ * (RC80211_MULTIRATE_PID_SMOOTHING - 1) * err_avg_old + err
+ * err_avg = ------------------------------------------
+ * RC80211_MULTIRATE_PID_SMOOTHING
+ *
+ * where err_avg is the new approximation, err_avg_old the previous one
+ * and err is the error w.r.t. to the current failed frames percentage
+ * sample. Note that the bigger RC80211_MULTIRATE_PID_SMOOTHING the more weight is
+ * given to the previous estimate, resulting in smoother behavior (i.e.
+ * corresponding to a longer integration window).
+ *
+ * For computation, we actually don't use the above formula, but this
+ * one:
+ *
+ * err_avg_scaled = err_avg_old_scaled - err_avg_old + err
+ *
+ * where:
+ * err_avg_scaled = err * RC80211_MULTIRATE_PID_SMOOTHING
+ * err_avg_old_scaled = err_avg_old * RC80211_MULTIRATE_PID_SMOOTHING
+ *
+ * This avoids floating point numbers and the per_failed_old value can
+ * easily be obtained by shifting per_failed_old_scaled right by
+ * RC80211_MULTIRATE_PID_SMOOTHING_SHIFT.
+ */
+ s32 err_avg_sc;
+
+ /* Last framed failes percentage sample. */
+ u32 last_pf;
+
+ /* Sharpening needed. */
+ u8 sharp_cnt;
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ /* Event buffer */
+ struct rc_multirate_pid_event_buffer events;
+
+ /* Events debugfs file entry */
+ struct dentry *events_entry;
+#endif
+};
+
+/* Algorithm parameters. We keep them on a per-algorithm approach, so they can
+ * be tuned individually for each interface.
+ */
+struct rc_multirate_pid_rateinfo {
+
+ /* Map sorted rates to rates in ieee80211_hw_mode. */
+ int index;
+
+ /* Map rates in ieee80211_hw_mode to sorted rates. */
+ int rev_index;
+
+ /* Did we do any measurement on this rate? */
+ bool valid;
+
+ /* Comparison with the lowest rate. */
+ int diff;
+};
+
+struct rc_multirate_pid_info {
+
+ /* The failed frames percentage target. */
+ unsigned int target;
+
+ /* Rate at which failed frames percentage is sampled in 0.001s. */
+ unsigned int sampling_period;
+
+ /* P, I and D coefficients. */
+ int coeff_p;
+ int coeff_i;
+ int coeff_d;
+
+ /* Exponential averaging shift. */
+ unsigned int smoothing_shift;
+
+ /* Sharpening factor and duration. */
+ unsigned int sharpen_factor;
+ unsigned int sharpen_duration;
+
+ /* Normalization offset. */
+ unsigned int norm_offset;
+
+ /* Rates information. */
+ struct rc_multirate_pid_rateinfo *rinfo;
+
+ /* Index of the last used rate. */
+ int oldrate;
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ /* Debugfs entries created for the parameters above. */
+ struct rc_multirate_pid_debugfs_entries dentries;
+#endif
+};
+
+#endif /* RC80211_MULTIRATE_PID_H */
diff --git a/net/mac80211/rc80211_multirate_pid_algo.c b/net/mac80211/rc80211_multirate_pid_algo.c
new file mode 100644
index 000000000000..aae2c44e572a
--- /dev/null
+++ b/net/mac80211/rc80211_multirate_pid_algo.c
@@ -0,0 +1,569 @@
+/*
+ * Copyright 2002-2005, Instant802 Networks, Inc.
+ * Copyright 2005, Devicescape Software, Inc.
+ * Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de>
+ * Copyright 2007-2008, Stefano Brivio <stefano.brivio@polimi.it>
+ * Copyright 2009, Digi International <support@multirate.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.
+ */
+
+#include <linux/netdevice.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/debugfs.h>
+#include <net/mac80211.h>
+#include "rate.h"
+#include "mesh.h"
+#include "rc80211_multirate_pid.h"
+
+/*
+ * This is a modified version of the 802.11 PID algorithm. The standard
+ * version uses the same rate for all the retries of the same frame. The code
+ * was modified to perform the first few retries at the same rate and then
+ * reduce the data rate for each successive retry until we either run out of retries
+ * or successfully transmit the frame. This seems to provide better performance
+ * on error prone networks.
+ */
+
+/* This is an implementation of a TX rate control algorithm that uses a PID
+ * controller. Given a target failed frames rate, the controller decides about
+ * TX rate changes to meet the target failed frames rate.
+ *
+ * The controller basically computes the following:
+ *
+ * adj = CP * err + CI * err_avg + CD * (err - last_err) * (1 + sharpening)
+ *
+ * where
+ * adj adjustment value that is used to switch TX rate (see below)
+ * err current error: target vs. current failed frames percentage
+ * last_err last error
+ * err_avg average (i.e. poor man's integral) of recent errors
+ * sharpening non-zero when fast response is needed (i.e. right after
+ * association or no frames sent for a long time), heading
+ * to zero over time
+ * CP Proportional coefficient
+ * CI Integral coefficient
+ * CD Derivative coefficient
+ *
+ * CP, CI, CD are subject to careful tuning.
+ *
+ * The integral component uses a exponential moving average approach instead of
+ * an actual sliding window. The advantage is that we don't need to keep an
+ * array of the last N error values and computation is easier.
+ *
+ * Once we have the adj value, we map it to a rate by means of a learning
+ * algorithm. This algorithm keeps the state of the percentual failed frames
+ * difference between rates. The behaviour of the lowest available rate is kept
+ * as a reference value, and every time we switch between two rates, we compute
+ * the difference between the failed frames each rate exhibited. By doing so,
+ * we compare behaviours which different rates exhibited in adjacent timeslices,
+ * thus the comparison is minimally affected by external conditions. This
+ * difference gets propagated to the whole set of measurements, so that the
+ * reference is always the same. Periodically, we normalize this set so that
+ * recent events weigh the most. By comparing the adj value with this set, we
+ * avoid pejorative switches to lower rates and allow for switches to higher
+ * rates if they behaved well.
+ *
+ * Note that for the computations we use a fixed-point representation to avoid
+ * floating point arithmetic. Hence, all values are shifted left by
+ * RC80211_MULTIRATE_PID_ARITH_SHIFT.
+ */
+
+
+/* Adjust the rate while ensuring that we won't switch to a lower rate if it
+ * exhibited a worse failed frames behaviour and we'll choose the highest rate
+ * whose failed frames behaviour is not worse than the one of the original rate
+ * target. While at it, check that the new rate is valid. */
+static void rate_control_multirate_pid_adjust_rate(struct
+ ieee80211_supported_band
+ *sband,
+ struct ieee80211_sta
+ *sta,
+ struct
+ rc_multirate_pid_sta_info
+ *spinfo, int adj,
+ struct
+ rc_multirate_pid_rateinfo
+ *rinfo)
+{
+ int cur_sorted, new_sorted, probe, tmp, n_bitrates, band;
+ int cur = spinfo->txrate_idx;
+
+ band = sband->band;
+ n_bitrates = sband->n_bitrates;
+
+ /* Map passed arguments to sorted values. */
+ cur_sorted = rinfo[cur].rev_index;
+ new_sorted = cur_sorted + adj;
+
+ /* Check limits. */
+ if (new_sorted < 0)
+ new_sorted = rinfo[0].rev_index;
+ else if (new_sorted >= n_bitrates)
+ new_sorted = rinfo[n_bitrates - 1].rev_index;
+
+ tmp = new_sorted;
+
+ if (adj < 0) {
+ /* Ensure that the rate decrease isn't disadvantageous. */
+ for (probe = cur_sorted; probe >= new_sorted; probe--)
+ if (rinfo[probe].diff <= rinfo[cur_sorted].diff &&
+ rate_supported(sta, band, rinfo[probe].index))
+ tmp = probe;
+ } else {
+ /* Look for rate increase with zero (or below) cost. */
+ for (probe = new_sorted + 1; probe < n_bitrates; probe++)
+ if (rinfo[probe].diff <= rinfo[new_sorted].diff &&
+ rate_supported(sta, band, rinfo[probe].index))
+ tmp = probe;
+ }
+
+ /* Fit the rate found to the nearest supported rate. */
+ do {
+ if (rate_supported(sta, band, rinfo[tmp].index)) {
+ spinfo->txrate_idx = rinfo[tmp].index;
+ break;
+ }
+ if (adj < 0)
+ tmp--;
+ else
+ tmp++;
+ } while (tmp < n_bitrates && tmp >= 0);
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ rate_control_multirate_pid_event_rate_change(&spinfo->events,
+ spinfo->txrate_idx,
+ sband->
+ bitrates[spinfo->
+ txrate_idx].
+ bitrate);
+#endif
+}
+
+/* Normalize the failed frames per-rate differences. */
+static void rate_control_multirate_pid_normalize(struct
+ rc_multirate_pid_info
+ *pinfo, int l)
+{
+ int i, norm_offset = pinfo->norm_offset;
+ struct rc_multirate_pid_rateinfo *r = pinfo->rinfo;
+
+ if (r[0].diff > norm_offset)
+ r[0].diff -= norm_offset;
+ else if (r[0].diff < -norm_offset)
+ r[0].diff += norm_offset;
+ for (i = 0; i < l - 1; i++)
+ if (r[i + 1].diff > r[i].diff + norm_offset)
+ r[i + 1].diff -= norm_offset;
+ else if (r[i + 1].diff <= r[i].diff)
+ r[i + 1].diff += norm_offset;
+}
+
+static void rate_control_multirate_pid_sample(struct rc_multirate_pid_info
+ *pinfo,
+ struct
+ ieee80211_supported_band
+ *sband,
+ struct ieee80211_sta *sta,
+ struct
+ rc_multirate_pid_sta_info
+ *spinfo)
+{
+ struct rc_multirate_pid_rateinfo *rinfo = pinfo->rinfo;
+ u32 pf;
+ s32 err_avg;
+ u32 err_prop;
+ u32 err_int;
+ u32 err_der;
+ int adj, i, j, tmp;
+ unsigned long period;
+
+ /* In case nothing happened during the previous control interval, turn
+ * the sharpening factor on. */
+ period = (HZ * pinfo->sampling_period + 500) / 1000;
+ if (!period)
+ period = 1;
+ if (jiffies - spinfo->last_sample > 2 * period)
+ spinfo->sharp_cnt = pinfo->sharpen_duration;
+
+ spinfo->last_sample = jiffies;
+
+ /* This should never happen, but in case, we assume the old sample is
+ * still a good measurement and copy it. */
+ if (unlikely(spinfo->tx_num_xmit == 0))
+ pf = spinfo->last_pf;
+ else {
+ /* XXX: BAD HACK!!! */
+ struct sta_info *si =
+ container_of(sta, struct sta_info, sta);
+
+ pf = spinfo->tx_num_failed * 100 / spinfo->tx_num_xmit;
+
+ if (ieee80211_vif_is_mesh(&si->sdata->vif) && pf == 100)
+ mesh_plink_broken(si);
+ pf <<= RC80211_MULTIRATE_PID_ARITH_SHIFT;
+ si->fail_avg = ((pf + (spinfo->last_pf << 3)) / 9)
+ >> RC80211_MULTIRATE_PID_ARITH_SHIFT;
+ }
+
+ spinfo->tx_num_xmit = 0;
+ spinfo->tx_num_failed = 0;
+
+ /* If we just switched rate, update the rate behaviour info. */
+ if (pinfo->oldrate != spinfo->txrate_idx) {
+
+ i = rinfo[pinfo->oldrate].rev_index;
+ j = rinfo[spinfo->txrate_idx].rev_index;
+
+ tmp = (pf - spinfo->last_pf);
+ tmp =
+ RC80211_MULTIRATE_PID_DO_ARITH_RIGHT_SHIFT(tmp,
+ RC80211_MULTIRATE_PID_ARITH_SHIFT);
+
+ rinfo[j].diff = rinfo[i].diff + tmp;
+ pinfo->oldrate = spinfo->txrate_idx;
+ }
+ rate_control_multirate_pid_normalize(pinfo, sband->n_bitrates);
+
+ /* Compute the proportional, integral and derivative errors. */
+ err_prop =
+ (pinfo->target << RC80211_MULTIRATE_PID_ARITH_SHIFT) - pf;
+
+ err_avg = spinfo->err_avg_sc >> pinfo->smoothing_shift;
+ spinfo->err_avg_sc = spinfo->err_avg_sc - err_avg + err_prop;
+ err_int = spinfo->err_avg_sc >> pinfo->smoothing_shift;
+
+ err_der = (pf - spinfo->last_pf) *
+ (1 + pinfo->sharpen_factor * spinfo->sharp_cnt);
+ spinfo->last_pf = pf;
+ if (spinfo->sharp_cnt)
+ spinfo->sharp_cnt--;
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ rate_control_multirate_pid_event_pf_sample(&spinfo->events, pf,
+ err_prop, err_int,
+ err_der);
+#endif
+
+ /* Compute the controller output. */
+ adj = (err_prop * pinfo->coeff_p + err_int * pinfo->coeff_i
+ + err_der * pinfo->coeff_d);
+ adj =
+ RC80211_MULTIRATE_PID_DO_ARITH_RIGHT_SHIFT(adj,
+ 2 *
+ RC80211_MULTIRATE_PID_ARITH_SHIFT);
+
+ /* Change rate. */
+ if (adj)
+ rate_control_multirate_pid_adjust_rate(sband, sta, spinfo,
+ adj, rinfo);
+}
+
+static void rate_control_multirate_pid_tx_status(void *priv,
+ struct
+ ieee80211_supported_band
+ *sband,
+ struct ieee80211_sta *sta,
+ void *priv_sta,
+ struct sk_buff *skb)
+{
+ struct rc_multirate_pid_info *pinfo = priv;
+ struct rc_multirate_pid_sta_info *spinfo = priv_sta;
+ unsigned long period;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ if (!spinfo)
+ return;
+
+ /* Ignore all frames that were sent with a different rate than the rate
+ * we currently advise mac80211 to use. */
+ if (info->status.rates[0].idx != spinfo->txrate_idx)
+ return;
+
+ spinfo->tx_num_xmit++;
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ rate_control_multirate_pid_event_tx_status(&spinfo->events, info);
+#endif
+
+ /* We count frames that totally failed to be transmitted as two bad
+ * frames, those that made it out but had some retries as one good and
+ * one bad frame. */
+ if (!(info->flags & IEEE80211_TX_STAT_ACK)) {
+ spinfo->tx_num_failed += 2;
+ spinfo->tx_num_xmit++;
+ } else if (info->status.rates[0].count > 1) {
+ spinfo->tx_num_failed++;
+ spinfo->tx_num_xmit++;
+ }
+
+ /* Update PID controller state. */
+ period = (HZ * pinfo->sampling_period + 500) / 1000;
+ if (!period)
+ period = 1;
+ if (time_after(jiffies, spinfo->last_sample + period))
+ rate_control_multirate_pid_sample(pinfo, sband, sta,
+ spinfo);
+}
+
+static void
+rate_control_multirate_pid_get_rate(void *priv, struct ieee80211_sta *sta,
+ void *priv_sta,
+ struct ieee80211_tx_rate_control *txrc)
+{
+ struct sk_buff *skb = txrc->skb;
+ struct ieee80211_supported_band *sband = txrc->sband;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct rc_multirate_pid_sta_info *spinfo = priv_sta;
+ int rateidx, savedidx;
+ u16 fc;
+ int max_tries, i;
+
+ if (txrc->rts)
+ max_tries = txrc->hw->conf.long_frame_max_tx_count;
+ else
+ max_tries = txrc->hw->conf.short_frame_max_tx_count;
+
+ /* Send management frames and broadcast/multicast data using lowest
+ * rate. */
+ fc = le16_to_cpu(hdr->frame_control);
+ if (!sta || !spinfo ||
+ (fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
+ is_multicast_ether_addr(hdr->addr1)) {
+ info->control.rates[0].idx = rate_lowest_index(sband, sta);
+ return;
+ }
+
+ rateidx = spinfo->txrate_idx;
+
+ if (rateidx >= sband->n_bitrates)
+ rateidx = sband->n_bitrates - 1;
+
+ savedidx = rateidx;
+
+ if (max_tries >= 2) {
+ info->control.rates[0].count = 2;
+ max_tries -= 2;
+ } else {
+ info->control.rates[0].count = max_tries;
+ max_tries = 0;
+ }
+ info->control.rates[0].idx = rateidx;
+ rateidx--;
+ for (i = 1; (i < IEEE80211_TX_MAX_RATES)
+ && (max_tries > 0) && (rateidx >= 0); i++) {
+ info->control.rates[i].idx = rateidx;
+ info->control.rates[i].count = 1;
+ rateidx--;
+ max_tries--;
+ }
+ info->control.rates[0].count += max_tries;
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ rate_control_multirate_pid_event_tx_rate(&spinfo->events,
+ savedidx,
+ sband->bitrates[savedidx].
+ bitrate);
+#endif
+}
+
+static void
+rate_control_multirate_pid_rate_init(void *priv,
+ struct ieee80211_supported_band
+ *sband, struct ieee80211_sta *sta,
+ void *priv_sta)
+{
+ struct rc_multirate_pid_sta_info *spinfo = priv_sta;
+ struct sta_info *si;
+
+ /* TODO: This routine should consider using RSSI from previous packets
+ * as we need to have IEEE 802.1X auth succeed immediately after assoc..
+ * Until that method is implemented, we will use the lowest supported
+ * rate as a workaround. */
+
+ spinfo->txrate_idx = rate_lowest_index(sband, sta);
+ /* HACK */
+ si = container_of(sta, struct sta_info, sta);
+ si->fail_avg = 0;
+}
+
+static void *rate_control_multirate_pid_alloc(struct ieee80211_hw *hw,
+ struct dentry *debugfsdir)
+{
+ struct rc_multirate_pid_info *pinfo;
+ struct rc_multirate_pid_rateinfo *rinfo;
+ struct ieee80211_supported_band *sband;
+ int i, j, tmp;
+ bool s;
+#ifdef CONFIG_MAC80211_DEBUGFS
+ struct rc_multirate_pid_debugfs_entries *de;
+#endif
+
+ sband = hw->wiphy->bands[hw->conf.channel->band];
+
+ pinfo = kmalloc(sizeof(*pinfo), GFP_ATOMIC);
+ if (!pinfo)
+ return NULL;
+
+ /* We can safely assume that sband won't change unless we get
+ * reinitialized. */
+ rinfo = kmalloc(sizeof(*rinfo) * sband->n_bitrates, GFP_ATOMIC);
+ if (!rinfo) {
+ kfree(pinfo);
+ return NULL;
+ }
+
+ pinfo->target = RC80211_MULTIRATE_PID_TARGET_PF;
+ pinfo->sampling_period = RC80211_MULTIRATE_PID_INTERVAL;
+ pinfo->coeff_p = RC80211_MULTIRATE_PID_COEFF_P;
+ pinfo->coeff_i = RC80211_MULTIRATE_PID_COEFF_I;
+ pinfo->coeff_d = RC80211_MULTIRATE_PID_COEFF_D;
+ pinfo->smoothing_shift = RC80211_MULTIRATE_PID_SMOOTHING_SHIFT;
+ pinfo->sharpen_factor = RC80211_MULTIRATE_PID_SHARPENING_FACTOR;
+ pinfo->sharpen_duration =
+ RC80211_MULTIRATE_PID_SHARPENING_DURATION;
+ pinfo->norm_offset = RC80211_MULTIRATE_PID_NORM_OFFSET;
+ pinfo->rinfo = rinfo;
+ pinfo->oldrate = 0;
+
+ /* Sort the rates. This is optimized for the most common case (i.e.
+ * almost-sorted CCK+OFDM rates). Kind of bubble-sort with reversed
+ * mapping too. */
+ for (i = 0; i < sband->n_bitrates; i++) {
+ rinfo[i].index = i;
+ rinfo[i].rev_index = i;
+ if (RC80211_MULTIRATE_PID_FAST_START)
+ rinfo[i].diff = 0;
+ else
+ rinfo[i].diff = i * pinfo->norm_offset;
+ }
+ for (i = 1; i < sband->n_bitrates; i++) {
+ s = 0;
+ for (j = 0; j < sband->n_bitrates - i; j++)
+ if (unlikely
+ (sband->bitrates[rinfo[j].index].bitrate >
+ sband->bitrates[rinfo[j + 1].index].
+ bitrate)) {
+ tmp = rinfo[j].index;
+ rinfo[j].index = rinfo[j + 1].index;
+ rinfo[j + 1].index = tmp;
+ rinfo[rinfo[j].index].rev_index = j;
+ rinfo[rinfo[j + 1].index].rev_index =
+ j + 1;
+ s = 1;
+ }
+ if (!s)
+ break;
+ }
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ de = &pinfo->dentries;
+ de->target = debugfs_create_u32("target_pf", S_IRUSR | S_IWUSR,
+ debugfsdir, &pinfo->target);
+ de->sampling_period = debugfs_create_u32("sampling_period",
+ S_IRUSR | S_IWUSR,
+ debugfsdir,
+ &pinfo->sampling_period);
+ de->coeff_p =
+ debugfs_create_u32("coeff_p", S_IRUSR | S_IWUSR, debugfsdir,
+ (u32 *) & pinfo->coeff_p);
+ de->coeff_i =
+ debugfs_create_u32("coeff_i", S_IRUSR | S_IWUSR, debugfsdir,
+ (u32 *) & pinfo->coeff_i);
+ de->coeff_d =
+ debugfs_create_u32("coeff_d", S_IRUSR | S_IWUSR, debugfsdir,
+ (u32 *) & pinfo->coeff_d);
+ de->smoothing_shift =
+ debugfs_create_u32("smoothing_shift", S_IRUSR | S_IWUSR,
+ debugfsdir, &pinfo->smoothing_shift);
+ de->sharpen_factor =
+ debugfs_create_u32("sharpen_factor", S_IRUSR | S_IWUSR,
+ debugfsdir, &pinfo->sharpen_factor);
+ de->sharpen_duration =
+ debugfs_create_u32("sharpen_duration", S_IRUSR | S_IWUSR,
+ debugfsdir, &pinfo->sharpen_duration);
+ de->norm_offset =
+ debugfs_create_u32("norm_offset", S_IRUSR | S_IWUSR,
+ debugfsdir, &pinfo->norm_offset);
+#endif
+
+ return pinfo;
+}
+
+static void rate_control_multirate_pid_free(void *priv)
+{
+ struct rc_multirate_pid_info *pinfo = priv;
+#ifdef CONFIG_MAC80211_DEBUGFS
+ struct rc_multirate_pid_debugfs_entries *de = &pinfo->dentries;
+
+ debugfs_remove(de->norm_offset);
+ debugfs_remove(de->sharpen_duration);
+ debugfs_remove(de->sharpen_factor);
+ debugfs_remove(de->smoothing_shift);
+ debugfs_remove(de->coeff_d);
+ debugfs_remove(de->coeff_i);
+ debugfs_remove(de->coeff_p);
+ debugfs_remove(de->sampling_period);
+ debugfs_remove(de->target);
+#endif
+
+ kfree(pinfo->rinfo);
+ kfree(pinfo);
+}
+
+static void *rate_control_multirate_pid_alloc_sta(void *priv,
+ struct ieee80211_sta
+ *sta, gfp_t gfp)
+{
+ struct rc_multirate_pid_sta_info *spinfo;
+
+ spinfo = kzalloc(sizeof(*spinfo), gfp);
+ if (spinfo == NULL)
+ return NULL;
+
+ spinfo->last_sample = jiffies;
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ spin_lock_init(&spinfo->events.lock);
+ init_waitqueue_head(&spinfo->events.waitqueue);
+#endif
+
+ return spinfo;
+}
+
+static void rate_control_multirate_pid_free_sta(void *priv,
+ struct ieee80211_sta *sta,
+ void *priv_sta)
+{
+ kfree(priv_sta);
+}
+
+static struct rate_control_ops mac80211_rcpid = {
+ .name = "multirate-pid",
+ .tx_status = rate_control_multirate_pid_tx_status,
+ .get_rate = rate_control_multirate_pid_get_rate,
+ .rate_init = rate_control_multirate_pid_rate_init,
+ .alloc = rate_control_multirate_pid_alloc,
+ .free = rate_control_multirate_pid_free,
+ .alloc_sta = rate_control_multirate_pid_alloc_sta,
+ .free_sta = rate_control_multirate_pid_free_sta,
+#ifdef CONFIG_MAC80211_DEBUGFS
+ .add_sta_debugfs = rate_control_multirate_pid_add_sta_debugfs,
+ .remove_sta_debugfs =
+ rate_control_multirate_pid_remove_sta_debugfs,
+#endif
+};
+
+int __init rc80211_multirate_pid_init(void)
+{
+ return ieee80211_rate_control_register(&mac80211_rcpid);
+}
+
+void rc80211_multirate_pid_exit(void)
+{
+ ieee80211_rate_control_unregister(&mac80211_rcpid);
+}
diff --git a/net/mac80211/rc80211_multirate_pid_debugfs.c b/net/mac80211/rc80211_multirate_pid_debugfs.c
new file mode 100644
index 000000000000..1d98a3062793
--- /dev/null
+++ b/net/mac80211/rc80211_multirate_pid_debugfs.c
@@ -0,0 +1,257 @@
+/*
+ * Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de>
+ *
+ * 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.
+ */
+
+#include <linux/spinlock.h>
+#include <linux/poll.h>
+#include <linux/netdevice.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+
+#include <net/mac80211.h>
+#include "rate.h"
+
+#include "rc80211_multirate_pid.h"
+
+static void rate_control_multirate_pid_event(struct
+ rc_multirate_pid_event_buffer
+ *buf,
+ enum
+ rc_multirate_pid_event_type
+ type,
+ union
+ rc_multirate_pid_event_data
+ *data)
+{
+ struct rc_multirate_pid_event *ev;
+ unsigned long status;
+
+ spin_lock_irqsave(&buf->lock, status);
+ ev = &(buf->ring[buf->next_entry]);
+ buf->next_entry =
+ (buf->next_entry + 1) % RC80211_MULTIRATE_PID_EVENT_RING_SIZE;
+
+ ev->timestamp = jiffies;
+ ev->id = buf->ev_count++;
+ ev->type = type;
+ ev->data = *data;
+
+ spin_unlock_irqrestore(&buf->lock, status);
+
+ wake_up_all(&buf->waitqueue);
+}
+
+void rate_control_multirate_pid_event_tx_status(struct
+ rc_multirate_pid_event_buffer
+ *buf,
+ struct ieee80211_tx_info
+ *stat)
+{
+ union rc_multirate_pid_event_data evd;
+
+ evd.flags = stat->flags;
+ memcpy(&evd.tx_status, stat, sizeof(struct ieee80211_tx_info));
+ rate_control_multirate_pid_event(buf,
+ RC80211_MULTIRATE_PID_EVENT_TYPE_TX_STATUS,
+ &evd);
+}
+
+void rate_control_multirate_pid_event_rate_change(struct
+ rc_multirate_pid_event_buffer
+ *buf, int index,
+ int rate)
+{
+ union rc_multirate_pid_event_data evd;
+
+ evd.index = index;
+ evd.rate = rate;
+ rate_control_multirate_pid_event(buf,
+ RC80211_MULTIRATE_PID_EVENT_TYPE_RATE_CHANGE,
+ &evd);
+}
+
+void rate_control_multirate_pid_event_tx_rate(struct
+ rc_multirate_pid_event_buffer
+ *buf, int index, int rate)
+{
+ union rc_multirate_pid_event_data evd;
+
+ evd.index = index;
+ evd.rate = rate;
+ rate_control_multirate_pid_event(buf,
+ RC80211_MULTIRATE_PID_EVENT_TYPE_TX_RATE,
+ &evd);
+}
+
+void rate_control_multirate_pid_event_pf_sample(struct
+ rc_multirate_pid_event_buffer
+ *buf, s32 pf_sample,
+ s32 prop_err, s32 int_err,
+ s32 der_err)
+{
+ union rc_multirate_pid_event_data evd;
+
+ evd.pf_sample = pf_sample;
+ evd.prop_err = prop_err;
+ evd.int_err = int_err;
+ evd.der_err = der_err;
+ rate_control_multirate_pid_event(buf,
+ RC80211_MULTIRATE_PID_EVENT_TYPE_PF_SAMPLE,
+ &evd);
+}
+
+static int rate_control_multirate_pid_events_open(struct inode *inode,
+ struct file *file)
+{
+ struct rc_multirate_pid_sta_info *sinfo = inode->i_private;
+ struct rc_multirate_pid_event_buffer *events = &sinfo->events;
+ struct rc_multirate_pid_events_file_info *file_info;
+ unsigned long status;
+
+ /* Allocate a state struct */
+ file_info = kmalloc(sizeof(*file_info), GFP_KERNEL);
+ if (file_info == NULL)
+ return -ENOMEM;
+
+ spin_lock_irqsave(&events->lock, status);
+
+ file_info->next_entry = events->next_entry;
+ file_info->events = events;
+
+ spin_unlock_irqrestore(&events->lock, status);
+
+ file->private_data = file_info;
+
+ return 0;
+}
+
+static int rate_control_multirate_pid_events_release(struct inode *inode,
+ struct file *file)
+{
+ struct rc_multirate_pid_events_file_info *file_info =
+ file->private_data;
+
+ kfree(file_info);
+
+ return 0;
+}
+
+static unsigned int rate_control_multirate_pid_events_poll(struct file
+ *file,
+ poll_table *
+ wait)
+{
+ struct rc_multirate_pid_events_file_info *file_info =
+ file->private_data;
+
+ poll_wait(file, &file_info->events->waitqueue, wait);
+
+ return POLLIN | POLLRDNORM;
+}
+
+#define RC80211_MULTIRATE_PID_PRINT_BUF_SIZE 64
+
+static ssize_t rate_control_multirate_pid_events_read(struct file *file,
+ char __user * buf,
+ size_t length,
+ loff_t * offset)
+{
+ struct rc_multirate_pid_events_file_info *file_info =
+ file->private_data;
+ struct rc_multirate_pid_event_buffer *events = file_info->events;
+ struct rc_multirate_pid_event *ev;
+ char pb[RC80211_MULTIRATE_PID_PRINT_BUF_SIZE];
+ int ret;
+ int p;
+ unsigned long status;
+
+ /* Check if there is something to read. */
+ if (events->next_entry == file_info->next_entry) {
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ /* Wait */
+ ret = wait_event_interruptible(events->waitqueue,
+ events->next_entry !=
+ file_info->next_entry);
+
+ if (ret)
+ return ret;
+ }
+
+ /* Write out one event per call. I don't care whether it's a little
+ * inefficient, this is debugging code anyway. */
+ spin_lock_irqsave(&events->lock, status);
+
+ /* Get an event */
+ ev = &(events->ring[file_info->next_entry]);
+ file_info->next_entry = (file_info->next_entry + 1) %
+ RC80211_MULTIRATE_PID_EVENT_RING_SIZE;
+
+ /* Print information about the event. Note that userpace needs to
+ * provide large enough buffers. */
+ length = length < RC80211_MULTIRATE_PID_PRINT_BUF_SIZE ?
+ length : RC80211_MULTIRATE_PID_PRINT_BUF_SIZE;
+ p = snprintf(pb, length, "%u %lu ", ev->id, ev->timestamp);
+ switch (ev->type) {
+ case RC80211_MULTIRATE_PID_EVENT_TYPE_TX_STATUS:
+ p += snprintf(pb + p, length - p, "tx_status %u %u",
+ !(ev->data.flags & IEEE80211_TX_STAT_ACK),
+ ev->data.tx_status.status.rates[0].idx);
+ break;
+ case RC80211_MULTIRATE_PID_EVENT_TYPE_RATE_CHANGE:
+ p += snprintf(pb + p, length - p, "rate_change %d %d",
+ ev->data.index, ev->data.rate);
+ break;
+ case RC80211_MULTIRATE_PID_EVENT_TYPE_TX_RATE:
+ p += snprintf(pb + p, length - p, "tx_rate %d %d",
+ ev->data.index, ev->data.rate);
+ break;
+ case RC80211_MULTIRATE_PID_EVENT_TYPE_PF_SAMPLE:
+ p += snprintf(pb + p, length - p,
+ "pf_sample %d %d %d %d",
+ ev->data.pf_sample, ev->data.prop_err,
+ ev->data.int_err, ev->data.der_err);
+ break;
+ }
+ p += snprintf(pb + p, length - p, "\n");
+
+ spin_unlock_irqrestore(&events->lock, status);
+
+ if (copy_to_user(buf, pb, p))
+ return -EFAULT;
+
+ return p;
+}
+
+#undef RC80211_MULTIRATE_PID_PRINT_BUF_SIZE
+
+static struct file_operations rc_multirate_pid_fop_events = {
+ .owner = THIS_MODULE,
+ .read = rate_control_multirate_pid_events_read,
+ .poll = rate_control_multirate_pid_events_poll,
+ .open = rate_control_multirate_pid_events_open,
+ .release = rate_control_multirate_pid_events_release,
+};
+
+void rate_control_multirate_pid_add_sta_debugfs(void *priv, void *priv_sta,
+ struct dentry *dir)
+{
+ struct rc_multirate_pid_sta_info *spinfo = priv_sta;
+
+ spinfo->events_entry =
+ debugfs_create_file("rc_multirate_pid_events", S_IRUGO, dir,
+ spinfo, &rc_multirate_pid_fop_events);
+}
+
+void rate_control_multirate_pid_remove_sta_debugfs(void *priv,
+ void *priv_sta)
+{
+ struct rc_multirate_pid_sta_info *spinfo = priv_sta;
+
+ debugfs_remove(spinfo->events_entry);
+}
diff --git a/net/mac80211/rc80211_pid.h b/net/mac80211/rc80211_pid.h
index 01d64d53f3b9..1a873f00691a 100644
--- a/net/mac80211/rc80211_pid.h
+++ b/net/mac80211/rc80211_pid.h
@@ -49,7 +49,7 @@
/* Arithmetic right shift for positive and negative values for ISO C. */
#define RC_PID_DO_ARITH_RIGHT_SHIFT(x, y) \
- (x) < 0 ? -((-(x)) >> (y)) : (x) >> (y)
+ ((x) < 0 ? -((-(x)) >> (y)) : (x) >> (y))
enum rc_pid_event_type {
RC_PID_EVENT_TYPE_TX_STATUS,
@@ -61,6 +61,7 @@ enum rc_pid_event_type {
union rc_pid_event_data {
/* RC_PID_EVENT_TX_STATUS */
struct {
+ u32 flags;
struct ieee80211_tx_info tx_status;
};
/* RC_PID_EVENT_TYPE_RATE_CHANGE */
diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c
index 86eb374e3b87..5b1c9f1abf10 100644
--- a/net/mac80211/rc80211_pid_algo.c
+++ b/net/mac80211/rc80211_pid_algo.c
@@ -241,7 +241,7 @@ static void rate_control_pid_tx_status(void *priv, struct ieee80211_supported_ba
/* Ignore all frames that were sent with a different rate than the rate
* we currently advise mac80211 to use. */
- if (info->tx_rate_idx != spinfo->txrate_idx)
+ if (info->status.rates[0].idx != spinfo->txrate_idx)
return;
spinfo->tx_num_xmit++;
@@ -253,10 +253,10 @@ static void rate_control_pid_tx_status(void *priv, struct ieee80211_supported_ba
/* We count frames that totally failed to be transmitted as two bad
* frames, those that made it out but had some retries as one good and
* one bad frame. */
- if (info->status.excessive_retries) {
+ if (!(info->flags & IEEE80211_TX_STAT_ACK)) {
spinfo->tx_num_failed += 2;
spinfo->tx_num_xmit++;
- } else if (info->status.retry_count) {
+ } else if (info->status.rates[0].count > 1) {
spinfo->tx_num_failed++;
spinfo->tx_num_xmit++;
}
@@ -270,23 +270,31 @@ static void rate_control_pid_tx_status(void *priv, struct ieee80211_supported_ba
}
static void
-rate_control_pid_get_rate(void *priv, struct ieee80211_supported_band *sband,
- struct ieee80211_sta *sta, void *priv_sta,
- struct sk_buff *skb,
- struct rate_selection *sel)
+rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta,
+ void *priv_sta,
+ struct ieee80211_tx_rate_control *txrc)
{
+ struct sk_buff *skb = txrc->skb;
+ struct ieee80211_supported_band *sband = txrc->sband;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct rc_pid_sta_info *spinfo = priv_sta;
int rateidx;
u16 fc;
+ if (txrc->rts)
+ info->control.rates[0].count =
+ txrc->hw->conf.long_frame_max_tx_count;
+ else
+ info->control.rates[0].count =
+ txrc->hw->conf.short_frame_max_tx_count;
/* Send management frames and broadcast/multicast data using lowest
* rate. */
fc = le16_to_cpu(hdr->frame_control);
if (!sta || !spinfo ||
(fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
is_multicast_ether_addr(hdr->addr1)) {
- sel->rate_idx = rate_lowest_index(sband, sta);
+ info->control.rates[0].idx = rate_lowest_index(sband, sta);
return;
}
@@ -295,7 +303,7 @@ rate_control_pid_get_rate(void *priv, struct ieee80211_supported_band *sband,
if (rateidx >= sband->n_bitrates)
rateidx = sband->n_bitrates - 1;
- sel->rate_idx = rateidx;
+ info->control.rates[0].idx = rateidx;
#ifdef CONFIG_MAC80211_DEBUGFS
rate_control_pid_event_tx_rate(&spinfo->events,
@@ -394,11 +402,11 @@ static void *rate_control_pid_alloc(struct ieee80211_hw *hw,
S_IRUSR | S_IWUSR, debugfsdir,
&pinfo->sampling_period);
de->coeff_p = debugfs_create_u32("coeff_p", S_IRUSR | S_IWUSR,
- debugfsdir, &pinfo->coeff_p);
+ debugfsdir, (u32 *)&pinfo->coeff_p);
de->coeff_i = debugfs_create_u32("coeff_i", S_IRUSR | S_IWUSR,
- debugfsdir, &pinfo->coeff_i);
+ debugfsdir, (u32 *)&pinfo->coeff_i);
de->coeff_d = debugfs_create_u32("coeff_d", S_IRUSR | S_IWUSR,
- debugfsdir, &pinfo->coeff_d);
+ debugfsdir, (u32 *)&pinfo->coeff_d);
de->smoothing_shift = debugfs_create_u32("smoothing_shift",
S_IRUSR | S_IWUSR, debugfsdir,
&pinfo->smoothing_shift);
@@ -437,10 +445,6 @@ static void rate_control_pid_free(void *priv)
kfree(pinfo);
}
-static void rate_control_pid_clear(void *priv)
-{
-}
-
static void *rate_control_pid_alloc_sta(void *priv, struct ieee80211_sta *sta,
gfp_t gfp)
{
@@ -471,7 +475,6 @@ static struct rate_control_ops mac80211_rcpid = {
.tx_status = rate_control_pid_tx_status,
.get_rate = rate_control_pid_get_rate,
.rate_init = rate_control_pid_rate_init,
- .clear = rate_control_pid_clear,
.alloc = rate_control_pid_alloc,
.free = rate_control_pid_free,
.alloc_sta = rate_control_pid_alloc_sta,
diff --git a/net/mac80211/rc80211_pid_debugfs.c b/net/mac80211/rc80211_pid_debugfs.c
index 8121d3bc6835..a08a9b530347 100644
--- a/net/mac80211/rc80211_pid_debugfs.c
+++ b/net/mac80211/rc80211_pid_debugfs.c
@@ -43,6 +43,7 @@ void rate_control_pid_event_tx_status(struct rc_pid_event_buffer *buf,
{
union rc_pid_event_data evd;
+ evd.flags = stat->flags;
memcpy(&evd.tx_status, stat, sizeof(struct ieee80211_tx_info));
rate_control_pid_event(buf, RC_PID_EVENT_TYPE_TX_STATUS, &evd);
}
@@ -167,8 +168,8 @@ static ssize_t rate_control_pid_events_read(struct file *file, char __user *buf,
switch (ev->type) {
case RC_PID_EVENT_TYPE_TX_STATUS:
p += snprintf(pb + p, length - p, "tx_status %u %u",
- ev->data.tx_status.status.excessive_retries,
- ev->data.tx_status.status.retry_count);
+ !(ev->data.flags & IEEE80211_TX_STAT_ACK),
+ ev->data.tx_status.status.rates[0].idx);
break;
case RC_PID_EVENT_TYPE_RATE_CHANGE:
p += snprintf(pb + p, length - p, "rate_change %d %d",
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index cf6b121e1bbf..6cc9c174d071 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -26,10 +26,11 @@
#include "tkip.h"
#include "wme.h"
-u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
- struct tid_ampdu_rx *tid_agg_rx,
- struct sk_buff *skb, u16 mpdu_seq_num,
- int bar_req);
+static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
+ struct tid_ampdu_rx *tid_agg_rx,
+ struct sk_buff *skb,
+ u16 mpdu_seq_num,
+ int bar_req);
/*
* monitor mode reception
*
@@ -122,7 +123,6 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
/* radiotap header, set always present flags */
rthdr->it_present =
cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
- (1 << IEEE80211_RADIOTAP_RATE) |
(1 << IEEE80211_RADIOTAP_CHANNEL) |
(1 << IEEE80211_RADIOTAP_ANTENNA) |
(1 << IEEE80211_RADIOTAP_RX_FLAGS));
@@ -148,7 +148,19 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
pos++;
/* IEEE80211_RADIOTAP_RATE */
- *pos = rate->bitrate / 5;
+ if (status->flag & RX_FLAG_HT) {
+ /*
+ * TODO: add following information into radiotap header once
+ * suitable fields are defined for it:
+ * - MCS index (status->rate_idx)
+ * - HT40 (status->flag & RX_FLAG_40MHZ)
+ * - short-GI (status->flag & RX_FLAG_SHORT_GI)
+ */
+ *pos = 0;
+ } else {
+ rthdr->it_present |= (1 << IEEE80211_RADIOTAP_RATE);
+ *pos = rate->bitrate / 5;
+ }
pos++;
/* IEEE80211_RADIOTAP_CHANNEL */
@@ -474,6 +486,79 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
}
+#define FRAME_CLASS_1 (1)
+#define FRAME_CLASS_2 (2)
+#define FRAME_CLASS_3 (3)
+
+static unsigned int debug_noinline frame_class_type(struct ieee80211_hdr *hdr, bool is_ibss)
+{
+ unsigned result = FRAME_CLASS_3;
+
+ if (ieee80211_is_mgmt(hdr->frame_control)) {
+ if ((ieee80211_is_assoc_req(hdr->frame_control))
+ || (ieee80211_is_assoc_resp(hdr->frame_control))
+ || (ieee80211_is_reassoc_req(hdr->frame_control))
+ || (ieee80211_is_reassoc_resp(hdr->frame_control))
+ || (ieee80211_is_disassoc(hdr->frame_control))) {
+ result = FRAME_CLASS_2;
+ } else if ((ieee80211_is_probe_req(hdr->frame_control))
+ || (ieee80211_is_probe_resp(hdr->frame_control))
+ || (ieee80211_is_beacon(hdr->frame_control))
+ || (ieee80211_is_auth(hdr->frame_control))
+ || (ieee80211_is_deauth(hdr->frame_control))
+ || (ieee80211_is_atim(hdr->frame_control))
+ || (ieee80211_is_cfend(hdr->frame_control))
+ || ((ieee80211_is_action(hdr->frame_control)) && (is_ibss))) {
+ result = FRAME_CLASS_1;
+ }
+ } else if (ieee80211_is_ctl(hdr->frame_control)) {
+ if ((ieee80211_is_rts(hdr->frame_control))
+ || (ieee80211_is_cts(hdr->frame_control))
+ || (ieee80211_is_ack(hdr->frame_control))
+ || (ieee80211_is_cfend(hdr->frame_control))
+ || (ieee80211_is_cfendack(hdr->frame_control))) {
+ result = FRAME_CLASS_1;
+ }
+ } else if (ieee80211_is_data(hdr->frame_control)) {
+ if ((!ieee80211_has_fromds(hdr->frame_control))
+ && (!ieee80211_has_tods(hdr->frame_control))) {
+ result = FRAME_CLASS_1;
+ }
+ }
+
+ return result;
+}
+
+static void private_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
+ u16 stype, u16 reason, u8 *destination)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *mgmt;
+
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt));
+ if (!skb) {
+ printk(KERN_DEBUG "%s: failed to allocate buffer for "
+ "deauth/disassoc frame\n", sdata->dev->name);
+ return;
+ }
+ skb_reserve(skb, local->hw.extra_tx_headroom);
+
+ mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
+ memset(mgmt, 0, 24);
+ memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
+ memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+ memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
+ skb_put(skb, 2);
+ /* u.deauth.reason_code == u.disassoc.reason_code */
+ mgmt->u.deauth.reason_code = cpu_to_le16(reason);
+
+ ieee80211_tx_skb(sdata, skb, 0);
+}
+
+
static ieee80211_rx_result debug_noinline
ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
{
@@ -510,6 +595,37 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
if (ieee80211_vif_is_mesh(&rx->sdata->vif))
return ieee80211_rx_mesh_check(rx);
+ if ((rx->sdata)
+ && !is_multicast_ether_addr(hdr->addr1)
+ && (rx->sdata->vif.type != NL80211_IFTYPE_ADHOC)
+ && (rx->flags & IEEE80211_RX_RA_MATCH)) {
+ unsigned class_type = frame_class_type(hdr, false);
+
+ switch(class_type) {
+ case FRAME_CLASS_1:
+ default:
+ break;
+ case FRAME_CLASS_2:
+ if ((rx->sta != NULL) && (!test_sta_flags(rx->sta, WLAN_STA_AUTH))) {
+ private_send_deauth_disassoc(rx->sdata,
+ IEEE80211_STYPE_DEAUTH,
+ WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA,
+ hdr->addr1);
+ }
+ break;
+ case FRAME_CLASS_3:
+ if ((rx->sta == NULL) || (!test_sta_flags(rx->sta, WLAN_STA_ASSOC))) {
+ if ((rx->sta != NULL) && (test_sta_flags(rx->sta, WLAN_STA_AUTH))) {
+ private_send_deauth_disassoc(rx->sdata,
+ IEEE80211_STYPE_DISASSOC,
+ WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA,
+ hdr->addr1);
+ }
+ }
+ break;
+ }
+ }
+
if (unlikely((ieee80211_is_data(hdr->frame_control) ||
ieee80211_is_pspoll(hdr->frame_control)) &&
rx->sdata->vif.type != NL80211_IFTYPE_ADHOC &&
@@ -653,13 +769,16 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
static void ap_sta_ps_start(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
- DECLARE_MAC_BUF(mac);
+ struct ieee80211_local *local = sdata->local;
atomic_inc(&sdata->bss->num_sta_ps);
set_and_clear_sta_flags(sta, WLAN_STA_PS, WLAN_STA_PSPOLL);
+ if (local->ops->sta_notify)
+ local->ops->sta_notify(local_to_hw(local), &sdata->vif,
+ STA_NOTIFY_SLEEP, &sta->sta);
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "%s: STA %s aid %d enters power save mode\n",
- sdata->dev->name, print_mac(mac, sta->sta.addr), sta->sta.aid);
+ printk(KERN_DEBUG "%s: STA %pM aid %d enters power save mode\n",
+ sdata->dev->name, sta->sta.addr, sta->sta.aid);
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
}
@@ -669,38 +788,37 @@ static int ap_sta_ps_end(struct sta_info *sta)
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
int sent = 0;
- struct ieee80211_tx_info *info;
- DECLARE_MAC_BUF(mac);
atomic_dec(&sdata->bss->num_sta_ps);
clear_sta_flags(sta, WLAN_STA_PS | WLAN_STA_PSPOLL);
+ if (local->ops->sta_notify)
+ local->ops->sta_notify(local_to_hw(local), &sdata->vif,
+ STA_NOTIFY_AWAKE, &sta->sta);
if (!skb_queue_empty(&sta->ps_tx_buf))
sta_info_clear_tim_bit(sta);
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "%s: STA %s aid %d exits power save mode\n",
- sdata->dev->name, print_mac(mac, sta->sta.addr), sta->sta.aid);
+ printk(KERN_DEBUG "%s: STA %pM aid %d exits power save mode\n",
+ sdata->dev->name, sta->sta.addr, sta->sta.aid);
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
/* Send all buffered frames to the station */
while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) {
- info = IEEE80211_SKB_CB(skb);
sent++;
- info->flags |= IEEE80211_TX_CTL_REQUEUE;
+ skb->requeue = 1;
dev_queue_xmit(skb);
}
while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
- info = IEEE80211_SKB_CB(skb);
local->total_ps_buffered--;
sent++;
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "%s: STA %s aid %d send PS frame "
+ printk(KERN_DEBUG "%s: STA %pM aid %d send PS frame "
"since STA not sleeping anymore\n", sdata->dev->name,
- print_mac(mac, sta->sta.addr), sta->sta.aid);
+ sta->sta.addr, sta->sta.aid);
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
- info->flags |= IEEE80211_TX_CTL_REQUEUE;
+ skb->requeue = 1;
dev_queue_xmit(skb);
}
@@ -745,17 +863,29 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
sta->last_qual = rx->status->qual;
sta->last_noise = rx->status->noise;
+ /*
+ * Change STA power saving mode only at the end of a frame
+ * exchange sequence.
+ */
if (!ieee80211_has_morefrags(hdr->frame_control) &&
(rx->sdata->vif.type == NL80211_IFTYPE_AP ||
rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) {
- /* Change STA power saving mode only in the end of a frame
- * exchange sequence */
- if (test_sta_flags(sta, WLAN_STA_PS) &&
- !ieee80211_has_pm(hdr->frame_control))
- rx->sent_ps_buffered += ap_sta_ps_end(sta);
- else if (!test_sta_flags(sta, WLAN_STA_PS) &&
- ieee80211_has_pm(hdr->frame_control))
- ap_sta_ps_start(sta);
+ if (test_sta_flags(sta, WLAN_STA_PS)) {
+ /*
+ * Ignore doze->wake transitions that are
+ * indicated by non-data frames, the standard
+ * is unclear here, but for example going to
+ * PS mode and then scanning would cause a
+ * doze->wake transition for the probe request,
+ * and that is clearly undesirable.
+ */
+ if (ieee80211_is_data(hdr->frame_control) &&
+ !ieee80211_has_pm(hdr->frame_control))
+ rx->sent_ps_buffered += ap_sta_ps_end(sta);
+ } else {
+ if (ieee80211_has_pm(hdr->frame_control))
+ ap_sta_ps_start(sta);
+ }
}
/* Drop data::nullfunc frames silently, since they are used only to
@@ -789,15 +919,12 @@ ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata,
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
struct ieee80211_hdr *hdr =
(struct ieee80211_hdr *) entry->skb_list.next->data;
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
printk(KERN_DEBUG "%s: RX reassembly removed oldest "
"fragment entry (idx=%d age=%lu seq=%d last_frag=%d "
- "addr1=%s addr2=%s\n",
+ "addr1=%pM addr2=%pM\n",
sdata->dev->name, idx,
jiffies - entry->first_frag_time, entry->seq,
- entry->last_frag, print_mac(mac, hdr->addr1),
- print_mac(mac2, hdr->addr2));
+ entry->last_frag, hdr->addr1, hdr->addr2);
#endif
__skb_queue_purge(&entry->skb_list);
}
@@ -866,7 +993,6 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
unsigned int frag, seq;
struct ieee80211_fragment_entry *entry;
struct sk_buff *skb;
- DECLARE_MAC_BUF(mac);
hdr = (struct ieee80211_hdr *)rx->skb->data;
fc = hdr->frame_control;
@@ -970,7 +1096,6 @@ ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
struct sk_buff *skb;
int no_pending_pkts;
- DECLARE_MAC_BUF(mac);
__le16 fc = ((struct ieee80211_hdr *)rx->skb->data)->frame_control;
if (likely(!rx->sta || !ieee80211_is_pspoll(fc) ||
@@ -1001,8 +1126,8 @@ ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
set_sta_flags(rx->sta, WLAN_STA_PSPOLL);
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "STA %s aid %d: PS Poll (entries after %d)\n",
- print_mac(mac, rx->sta->sta.addr), rx->sta->sta.aid,
+ printk(KERN_DEBUG "STA %pM aid %d: PS Poll (entries after %d)\n",
+ rx->sta->sta.addr, rx->sta->sta.aid,
skb_queue_len(&rx->sta->ps_tx_buf));
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
@@ -1025,9 +1150,9 @@ ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
* Should we send it a null-func frame indicating we
* have nothing buffered for it?
*/
- printk(KERN_DEBUG "%s: STA %s sent PS Poll even "
+ printk(KERN_DEBUG "%s: STA %pM sent PS Poll even "
"though there are no buffered frames for it\n",
- rx->dev->name, print_mac(mac, rx->sta->sta.addr));
+ rx->dev->name, rx->sta->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
}
@@ -1097,10 +1222,6 @@ ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
u8 src[ETH_ALEN] __aligned(2);
struct sk_buff *skb = rx->skb;
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
- DECLARE_MAC_BUF(mac3);
- DECLARE_MAC_BUF(mac4);
if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
return -1;
@@ -1279,7 +1400,6 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
int remaining, err;
u8 dst[ETH_ALEN];
u8 src[ETH_ALEN];
- DECLARE_MAC_BUF(mac);
if (unlikely(!ieee80211_is_data(fc)))
return RX_CONTINUE;
@@ -1552,14 +1672,6 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
if (len < IEEE80211_MIN_ACTION_SIZE + 1)
return RX_DROP_MONITOR;
- /*
- * FIXME: revisit this, I'm sure we should handle most
- * of these frames in other modes as well!
- */
- if (sdata->vif.type != NL80211_IFTYPE_STATION &&
- sdata->vif.type != NL80211_IFTYPE_ADHOC)
- return RX_CONTINUE;
-
switch (mgmt->u.action.category) {
case WLAN_CATEGORY_BACK:
switch (mgmt->u.action.u.addba_req.action_code) {
@@ -1632,8 +1744,6 @@ static void ieee80211_rx_michael_mic_report(struct net_device *dev,
{
int keyidx;
unsigned int hdrlen;
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
hdrlen = ieee80211_hdrlen(hdr->frame_control);
if (rx->skb->len >= hdrlen + 4)
@@ -1854,10 +1964,15 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
if (!(sdata->dev->flags & IFF_PROMISC))
return 0;
rx->flags &= ~IEEE80211_RX_RA_MATCH;
- } else if (!rx->sta)
- rx->sta = ieee80211_ibss_add_sta(sdata, rx->skb,
- bssid, hdr->addr2,
- BIT(rx->status->rate_idx));
+ } else if (!rx->sta) {
+ int rate_idx;
+ if (rx->status->flag & RX_FLAG_HT)
+ rate_idx = 0; /* TODO: HT rates */
+ else
+ rate_idx = rx->status->rate_idx;
+ rx->sta = ieee80211_ibss_add_sta(sdata, bssid, hdr->addr2,
+ BIT(rate_idx));
+ }
break;
case NL80211_IFTYPE_MESH_POINT:
if (!multicast &&
@@ -2002,17 +2117,17 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
static inline int seq_less(u16 sq1, u16 sq2)
{
- return (((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1));
+ return ((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1);
}
static inline u16 seq_inc(u16 sq)
{
- return ((sq + 1) & SEQ_MASK);
+ return (sq + 1) & SEQ_MASK;
}
static inline u16 seq_sub(u16 sq1, u16 sq2)
{
- return ((sq1 - sq2) & SEQ_MASK);
+ return (sq1 - sq2) & SEQ_MASK;
}
@@ -2020,10 +2135,11 @@ static inline u16 seq_sub(u16 sq1, u16 sq2)
* As it function blongs to Rx path it must be called with
* the proper rcu_read_lock protection for its flow.
*/
-u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
- struct tid_ampdu_rx *tid_agg_rx,
- struct sk_buff *skb, u16 mpdu_seq_num,
- int bar_req)
+static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
+ struct tid_ampdu_rx *tid_agg_rx,
+ struct sk_buff *skb,
+ u16 mpdu_seq_num,
+ int bar_req)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_rx_status status;
@@ -2062,7 +2178,13 @@ u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
tid_agg_rx->reorder_buf[index]->cb,
sizeof(status));
sband = local->hw.wiphy->bands[status.band];
- rate = &sband->bitrates[status.rate_idx];
+ if (status.flag & RX_FLAG_HT) {
+ /* TODO: HT rates */
+ rate = sband->bitrates;
+ } else {
+ rate = &sband->bitrates
+ [status.rate_idx];
+ }
__ieee80211_rx_handle_packet(hw,
tid_agg_rx->reorder_buf[index],
&status, rate);
@@ -2106,7 +2228,10 @@ u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
memcpy(&status, tid_agg_rx->reorder_buf[index]->cb,
sizeof(status));
sband = local->hw.wiphy->bands[status.band];
- rate = &sband->bitrates[status.rate_idx];
+ if (status.flag & RX_FLAG_HT)
+ rate = sband->bitrates; /* TODO: HT rates */
+ else
+ rate = &sband->bitrates[status.rate_idx];
__ieee80211_rx_handle_packet(hw, tid_agg_rx->reorder_buf[index],
&status, rate);
tid_agg_rx->stored_mpdu_num--;
@@ -2194,15 +2319,26 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
}
sband = local->hw.wiphy->bands[status->band];
-
- if (!sband ||
- status->rate_idx < 0 ||
- status->rate_idx >= sband->n_bitrates) {
+ if (!sband) {
WARN_ON(1);
return;
}
- rate = &sband->bitrates[status->rate_idx];
+ if (status->flag & RX_FLAG_HT) {
+ /* rate_idx is MCS index */
+ if (WARN_ON(status->rate_idx < 0 ||
+ status->rate_idx >= 76))
+ return;
+ /* HT rates are not in the table - use the highest legacy rate
+ * for now since other parts of mac80211 may not yet be fully
+ * MCS aware. */
+ rate = &sband->bitrates[sband->n_bitrates - 1];
+ } else {
+ if (WARN_ON(status->rate_idx < 0 ||
+ status->rate_idx >= sband->n_bitrates))
+ return;
+ rate = &sband->bitrates[status->rate_idx];
+ }
/*
* key references and virtual interfaces are protected using RCU
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 416bb41099f3..f5c7c3371929 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -159,7 +159,7 @@ ieee80211_rx_mesh_bss_add(struct ieee80211_local *local, u8 *mesh_id, int mesh_i
{
struct ieee80211_bss *bss;
- if (mesh_config_len != MESH_CFG_LEN)
+ if (mesh_config_len != IEEE80211_MESH_CONFIG_LEN)
return NULL;
bss = kzalloc(sizeof(*bss), GFP_ATOMIC);
@@ -448,18 +448,17 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
if (local->hw_scanning) {
local->hw_scanning = false;
- if (ieee80211_hw_config(local))
- printk(KERN_DEBUG "%s: failed to restore operational "
- "channel after scan\n", wiphy_name(local->hw.wiphy));
-
+ /*
+ * Somebody might have requested channel change during scan
+ * that we won't have acted upon, try now. ieee80211_hw_config
+ * will set the flag based on actual changes.
+ */
+ ieee80211_hw_config(local, 0);
goto done;
}
local->sw_scanning = false;
- if (ieee80211_hw_config(local))
- printk(KERN_DEBUG "%s: failed to restore operational "
- "channel after scan\n", wiphy_name(local->hw.wiphy));
-
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
netif_tx_lock_bh(local->mdev);
netif_addr_lock(local->mdev);
@@ -546,12 +545,9 @@ void ieee80211_scan_work(struct work_struct *work)
if (!skip) {
local->scan_channel = chan;
- if (ieee80211_hw_config(local)) {
- printk(KERN_DEBUG "%s: failed to set freq to "
- "%d MHz for scan\n", wiphy_name(local->hw.wiphy),
- chan->center_freq);
+ if (ieee80211_hw_config(local,
+ IEEE80211_CONF_CHANGE_CHANNEL))
skip = 1;
- }
}
/* advance state machine to next channel/band */
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index d254446b85b5..10c5539c20ab 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -137,14 +137,12 @@ struct sta_info *sta_info_get_by_idx(struct ieee80211_local *local, int idx,
static void __sta_info_free(struct ieee80211_local *local,
struct sta_info *sta)
{
- DECLARE_MAC_BUF(mbuf);
-
rate_control_free_sta(sta);
rate_control_put(sta->rate_ctrl);
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: Destroyed STA %s\n",
- wiphy_name(local->hw.wiphy), print_mac(mbuf, sta->sta.addr));
+ printk(KERN_DEBUG "%s: Destroyed STA %pM\n",
+ wiphy_name(local->hw.wiphy), sta->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
kfree(sta);
@@ -222,7 +220,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
int i;
- DECLARE_MAC_BUF(mbuf);
sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp);
if (!sta)
@@ -263,8 +260,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
skb_queue_head_init(&sta->tx_filtered);
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: Allocated STA %s\n",
- wiphy_name(local->hw.wiphy), print_mac(mbuf, sta->sta.addr));
+ printk(KERN_DEBUG "%s: Allocated STA %pM\n",
+ wiphy_name(local->hw.wiphy), sta->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
#ifdef CONFIG_MAC80211_MESH
@@ -281,7 +278,6 @@ int sta_info_insert(struct sta_info *sta)
struct ieee80211_sub_if_data *sdata = sta->sdata;
unsigned long flags;
int err = 0;
- DECLARE_MAC_BUF(mac);
/*
* Can't be a WARN_ON because it can be triggered through a race:
@@ -294,7 +290,7 @@ int sta_info_insert(struct sta_info *sta)
}
if (WARN_ON(compare_ether_addr(sta->sta.addr, sdata->dev->dev_addr) == 0 ||
- is_multicast_ether_addr(sta->sta.addr))) {
+ is_multicast_ether_addr(sta->sta.addr))) {
err = -EINVAL;
goto out_free;
}
@@ -322,8 +318,8 @@ int sta_info_insert(struct sta_info *sta)
}
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: Inserted STA %s\n",
- wiphy_name(local->hw.wiphy), print_mac(mac, sta->sta.addr));
+ printk(KERN_DEBUG "%s: Inserted STA %pM\n",
+ wiphy_name(local->hw.wiphy), sta->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
spin_unlock_irqrestore(&local->sta_lock, flags);
@@ -423,9 +419,6 @@ static void __sta_info_unlink(struct sta_info **sta)
{
struct ieee80211_local *local = (*sta)->local;
struct ieee80211_sub_if_data *sdata = (*sta)->sdata;
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- DECLARE_MAC_BUF(mbuf);
-#endif
/*
* pull caller's reference if we're already gone.
*/
@@ -468,8 +461,8 @@ static void __sta_info_unlink(struct sta_info **sta)
}
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: Removed STA %s\n",
- wiphy_name(local->hw.wiphy), print_mac(mbuf, (*sta)->sta.addr));
+ printk(KERN_DEBUG "%s: Removed STA %pM\n",
+ wiphy_name(local->hw.wiphy), (*sta)->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
/*
@@ -544,7 +537,6 @@ static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
unsigned long flags;
struct sk_buff *skb;
struct ieee80211_sub_if_data *sdata;
- DECLARE_MAC_BUF(mac);
if (skb_queue_empty(&sta->ps_tx_buf))
return;
@@ -564,8 +556,8 @@ static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
sdata = sta->sdata;
local->total_ps_buffered--;
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "Buffered frame expired (STA "
- "%s)\n", print_mac(mac, sta->sta.addr));
+ printk(KERN_DEBUG "Buffered frame expired (STA %pM)\n",
+ sta->sta.addr);
#endif
dev_kfree_skb(skb);
@@ -809,15 +801,14 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
struct sta_info *sta, *tmp;
LIST_HEAD(tmp_list);
- DECLARE_MAC_BUF(mac);
unsigned long flags;
spin_lock_irqsave(&local->sta_lock, flags);
list_for_each_entry_safe(sta, tmp, &local->sta_list, list)
if (time_after(jiffies, sta->last_rx + exp_time)) {
#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG "%s: expiring inactive STA %s\n",
- sdata->dev->name, print_mac(mac, sta->sta.addr));
+ printk(KERN_DEBUG "%s: expiring inactive STA %pM\n",
+ sdata->dev->name, sta->sta.addr);
#endif
__sta_info_unlink(&sta);
if (sta)
@@ -830,7 +821,7 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
}
struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_hw *hw,
- const u8 *addr)
+ const u8 *addr)
{
struct sta_info *sta = sta_info_get(hw_to_local(hw), addr);
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 168a39a298bd..e49a5b99cf10 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -160,18 +160,17 @@ struct sta_ampdu_mlme {
* @list: global linked list entry
* @hnext: hash table linked list pointer
* @local: pointer to the global information
- * @sdata: TBD
- * @key: TBD
- * @rate_ctrl: TBD
- * @rate_ctrl_priv: TBD
+ * @sdata: virtual interface this station belongs to
+ * @key: peer key negotiated with this station, if any
+ * @rate_ctrl: rate control algorithm reference
+ * @rate_ctrl_priv: rate control private per-STA pointer
+ * @last_tx_rate: rate used for last transmit, to report to userspace as
+ * "the" transmit rate
* @lock: used for locking all fields that require locking, see comments
* in the header file.
* @flaglock: spinlock for flags accesses
- * @addr: MAC address of this STA
- * @aid: STA's unique AID (1..2007, 0 = not assigned yet),
- * only used in AP (and IBSS?) mode
- * @listen_interval: TBD
- * @pin_status: TBD
+ * @listen_interval: listen interval of this station, when we're acting as AP
+ * @pin_status: used internally for pinning a STA struct into memory
* @flags: STA flags, see &enum ieee80211_sta_info_flags
* @ps_tx_buf: buffer of frames to transmit to this station
* when it leaves power saving state
@@ -180,8 +179,8 @@ struct sta_ampdu_mlme {
* power saving state
* @rx_packets: Number of MSDUs received from this STA
* @rx_bytes: Number of bytes received from this STA
- * @wep_weak_iv_count: TBD
- * @last_rx: TBD
+ * @wep_weak_iv_count: number of weak WEP IVs received from this station
+ * @last_rx: time (in jiffies) when last frame was received from this STA
* @num_duplicates: number of duplicate frames received from this STA
* @rx_fragments: number of received MPDUs
* @rx_dropped: number of dropped MPDUs from this STA
@@ -189,26 +188,25 @@ struct sta_ampdu_mlme {
* @last_qual: qual of last received frame from this STA
* @last_noise: noise of last received frame from this STA
* @last_seq_ctrl: last received seq/frag number from this STA (per RX queue)
- * @tx_filtered_count: TBD
- * @tx_retry_failed: TBD
- * @tx_retry_count: TBD
+ * @tx_filtered_count: number of frames the hardware filtered for this STA
+ * @tx_retry_failed: number of frames that failed retry
+ * @tx_retry_count: total number of retries for frames to this STA
* @fail_avg: moving percentage of failed MSDUs
* @tx_packets: number of RX/TX MSDUs
- * @tx_bytes: TBD
+ * @tx_bytes: number of bytes transmitted to this STA
* @tx_fragments: number of transmitted MPDUs
- * @last_txrate_idx: Index of the last used transmit rate
- * @tid_seq: TBD
- * @ampdu_mlme: TBD
+ * @tid_seq: per-TID sequence numbers for sending to this STA
+ * @ampdu_mlme: A-MPDU state machine state
* @timer_to_tid: identity mapping to ID timers
* @tid_to_tx_q: map tid to tx queue
* @llid: Local link ID
* @plid: Peer link ID
* @reason: Cancel reason on PLINK_HOLDING state
* @plink_retries: Retries in establishment
- * @ignore_plink_timer: TBD
- * @plink_state plink_state: TBD
- * @plink_timeout: TBD
- * @plink_timer: TBD
+ * @ignore_plink_timer: ignore the peer-link timer (used internally)
+ * @plink_state: peer link state
+ * @plink_timeout: timeout of peer link
+ * @plink_timer: peer link watch timer
* @debugfs: debug filesystem info
* @sta: station information we share with the driver
*/
@@ -267,7 +265,7 @@ struct sta_info {
unsigned long tx_packets;
unsigned long tx_bytes;
unsigned long tx_fragments;
- unsigned int last_txrate_idx;
+ struct ieee80211_tx_rate last_tx_rate;
u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1];
/*
diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c
index 34b32bc8f609..38fa111d2dc6 100644
--- a/net/mac80211/tkip.c
+++ b/net/mac80211/tkip.c
@@ -263,10 +263,9 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,
(iv32 == key->u.tkip.rx[queue].iv32 &&
iv16 <= key->u.tkip.rx[queue].iv16))) {
#ifdef CONFIG_MAC80211_TKIP_DEBUG
- DECLARE_MAC_BUF(mac);
printk(KERN_DEBUG "TKIP replay detected for RX frame from "
- "%s (RX IV (%04x,%02x) <= prev. IV (%04x,%02x)\n",
- print_mac(mac, ta),
+ "%pM (RX IV (%04x,%02x) <= prev. IV (%04x,%02x)\n",
+ ta,
iv32, iv16, key->u.tkip.rx[queue].iv32,
key->u.tkip.rx[queue].iv16);
#endif
@@ -287,9 +286,8 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,
{
int i;
u8 key_offset = NL80211_TKIP_DATA_OFFSET_ENCR_KEY;
- DECLARE_MAC_BUF(mac);
- printk(KERN_DEBUG "TKIP decrypt: Phase1 TA=%s"
- " TK=", print_mac(mac, ta));
+ printk(KERN_DEBUG "TKIP decrypt: Phase1 TA=%pM"
+ " TK=", ta);
for (i = 0; i < 16; i++)
printk("%02x ",
key->conf.key[key_offset + i]);
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 1460537faf33..37e3d5ef7e3f 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -46,13 +46,20 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
struct ieee80211_local *local = tx->local;
struct ieee80211_supported_band *sband;
struct ieee80211_hdr *hdr;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
+
+ /* assume HW handles this */
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS)
+ return 0;
+
+ /* uh huh? */
+ if (WARN_ON_ONCE(info->control.rates[0].idx < 0))
+ return 0;
sband = local->hw.wiphy->bands[tx->channel->band];
- txrate = &sband->bitrates[tx->rate_idx];
+ txrate = &sband->bitrates[info->control.rates[0].idx];
- erp = 0;
- if (tx->sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
- erp = txrate->flags & IEEE80211_RATE_ERP_G;
+ erp = txrate->flags & IEEE80211_RATE_ERP_G;
/*
* data and mgmt (except PS Poll):
@@ -116,7 +123,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
if (r->bitrate > txrate->bitrate)
break;
- if (tx->sdata->bss_conf.basic_rates & BIT(i))
+ if (tx->sdata->vif.bss_conf.basic_rates & BIT(i))
rate = r->bitrate;
switch (sband->band) {
@@ -150,7 +157,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
* to closest integer */
dur = ieee80211_frame_duration(local, 10, rate, erp,
- tx->sdata->bss_conf.use_short_preamble);
+ tx->sdata->vif.bss_conf.use_short_preamble);
if (next_frag_len) {
/* Frame is fragmented: duration increases with time needed to
@@ -159,7 +166,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
/* next fragment */
dur += ieee80211_frame_duration(local, next_frag_len,
txrate->bitrate, erp,
- tx->sdata->bss_conf.use_short_preamble);
+ tx->sdata->vif.bss_conf.use_short_preamble);
}
return cpu_to_le16(dur);
@@ -201,10 +208,9 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
tx->sdata->vif.type != NL80211_IFTYPE_ADHOC &&
ieee80211_is_data(hdr->frame_control))) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- DECLARE_MAC_BUF(mac);
printk(KERN_DEBUG "%s: dropped data frame to not "
- "associated station %s\n",
- tx->dev->name, print_mac(mac, hdr->addr1));
+ "associated station %pM\n",
+ tx->dev->name, hdr->addr1);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
I802_DEBUG_INC(tx->local->tx_handlers_drop_not_assoc);
return TX_DROP;
@@ -331,7 +337,6 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
u32 staflags;
- DECLARE_MAC_BUF(mac);
if (unlikely(!sta || ieee80211_is_probe_resp(hdr->frame_control)))
return TX_CONTINUE;
@@ -341,9 +346,9 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
if (unlikely((staflags & WLAN_STA_PS) &&
!(staflags & WLAN_STA_PSPOLL))) {
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "STA %s aid %d: PS buffer (entries "
+ printk(KERN_DEBUG "STA %pM aid %d: PS buffer (entries "
"before %d)\n",
- print_mac(mac, sta->sta.addr), sta->sta.aid,
+ sta->sta.addr, sta->sta.aid,
skb_queue_len(&sta->ps_tx_buf));
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
@@ -352,9 +357,9 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf);
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: STA %s TX "
+ printk(KERN_DEBUG "%s: STA %pM TX "
"buffer full - dropping oldest frame\n",
- tx->dev->name, print_mac(mac, sta->sta.addr));
+ tx->dev->name, sta->sta.addr);
}
#endif
dev_kfree_skb(old);
@@ -371,9 +376,9 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
}
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
else if (unlikely(test_sta_flags(sta, WLAN_STA_PS))) {
- printk(KERN_DEBUG "%s: STA %s in PS mode, but pspoll "
+ printk(KERN_DEBUG "%s: STA %pM in PS mode, but pspoll "
"set -> send frame\n", tx->dev->name,
- print_mac(mac, sta->sta.addr));
+ sta->sta.addr);
}
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
clear_sta_flags(sta, WLAN_STA_PSPOLL);
@@ -439,140 +444,154 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
static ieee80211_tx_result debug_noinline
ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
{
- struct rate_selection rsel;
- struct ieee80211_supported_band *sband;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
+ struct ieee80211_hdr *hdr = (void *)tx->skb->data;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_rate *rate;
+ int i, len;
+ bool inval = false, rts = false, short_preamble = false;
+ struct ieee80211_tx_rate_control txrc;
- sband = tx->local->hw.wiphy->bands[tx->channel->band];
+ memset(&txrc, 0, sizeof(txrc));
- if (likely(tx->rate_idx < 0)) {
- rate_control_get_rate(tx->sdata, sband, tx->sta,
- tx->skb, &rsel);
- if (tx->sta)
- tx->sta->last_txrate_idx = rsel.rate_idx;
- tx->rate_idx = rsel.rate_idx;
- if (unlikely(rsel.probe_idx >= 0)) {
- info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE;
- tx->flags |= IEEE80211_TX_PROBE_LAST_FRAG;
- info->control.retries[0].rate_idx = tx->rate_idx;
- info->control.retries[0].limit = tx->local->hw.max_altrate_tries;
- tx->rate_idx = rsel.probe_idx;
- } else if (info->control.retries[0].limit == 0)
- info->control.retries[0].rate_idx = -1;
-
- if (unlikely(tx->rate_idx < 0))
- return TX_DROP;
- } else
- info->control.retries[0].rate_idx = -1;
+ sband = tx->local->hw.wiphy->bands[tx->channel->band];
- if (tx->sdata->bss_conf.use_cts_prot &&
- (tx->flags & IEEE80211_TX_FRAGMENTED) && (rsel.nonerp_idx >= 0)) {
- tx->last_frag_rate_idx = tx->rate_idx;
- if (rsel.probe_idx >= 0)
- tx->flags &= ~IEEE80211_TX_PROBE_LAST_FRAG;
- else
- tx->flags |= IEEE80211_TX_PROBE_LAST_FRAG;
- tx->rate_idx = rsel.nonerp_idx;
- info->tx_rate_idx = rsel.nonerp_idx;
- info->flags &= ~IEEE80211_TX_CTL_RATE_CTRL_PROBE;
- } else {
- tx->last_frag_rate_idx = tx->rate_idx;
- info->tx_rate_idx = tx->rate_idx;
+ len = min_t(int, tx->skb->len + FCS_LEN,
+ tx->local->fragmentation_threshold);
+
+ /* set up the tx rate control struct we give the RC algo */
+ txrc.hw = local_to_hw(tx->local);
+ txrc.sband = sband;
+ txrc.bss_conf = &tx->sdata->vif.bss_conf;
+ txrc.skb = tx->skb;
+ txrc.reported_rate.idx = -1;
+ txrc.max_rate_idx = tx->sdata->max_ratectrl_rateidx;
+
+ /* set up RTS protection if desired */
+ if (tx->local->rts_threshold < IEEE80211_MAX_RTS_THRESHOLD &&
+ len > tx->local->rts_threshold) {
+ txrc.rts = rts = true;
}
- info->tx_rate_idx = tx->rate_idx;
- return TX_CONTINUE;
-}
+ /*
+ * Use short preamble if the BSS can handle it, but not for
+ * management frames unless we know the receiver can handle
+ * that -- the management frame might be to a station that
+ * just wants a probe response.
+ */
+ if (tx->sdata->vif.bss_conf.use_short_preamble &&
+ (ieee80211_is_data(hdr->frame_control) ||
+ (tx->sta && test_sta_flags(tx->sta, WLAN_STA_SHORT_PREAMBLE))))
+ txrc.short_preamble = short_preamble = true;
-static ieee80211_tx_result debug_noinline
-ieee80211_tx_h_misc(struct ieee80211_tx_data *tx)
-{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
- struct ieee80211_supported_band *sband;
- sband = tx->local->hw.wiphy->bands[tx->channel->band];
+ rate_control_get_rate(tx->sdata, tx->sta, &txrc);
+
+ if (unlikely(info->control.rates[0].idx < 0))
+ return TX_DROP;
+
+ if (txrc.reported_rate.idx < 0)
+ txrc.reported_rate = info->control.rates[0];
if (tx->sta)
- info->control.sta = &tx->sta->sta;
+ tx->sta->last_tx_rate = txrc.reported_rate;
- if (!info->control.retry_limit) {
- if (!is_multicast_ether_addr(hdr->addr1)) {
- int len = min_t(int, tx->skb->len + FCS_LEN,
- tx->local->fragmentation_threshold);
- if (len > tx->local->rts_threshold
- && tx->local->rts_threshold <
- IEEE80211_MAX_RTS_THRESHOLD) {
- info->flags |= IEEE80211_TX_CTL_USE_RTS_CTS;
- info->flags |=
- IEEE80211_TX_CTL_LONG_RETRY_LIMIT;
- info->control.retry_limit =
- tx->local->long_retry_limit;
- } else {
- info->control.retry_limit =
- tx->local->short_retry_limit;
- }
- } else {
- info->control.retry_limit = 1;
- }
- }
+ if (unlikely(!info->control.rates[0].count))
+ info->control.rates[0].count = 1;
- if (tx->flags & IEEE80211_TX_FRAGMENTED) {
- /* Do not use multiple retry rates when sending fragmented
- * frames.
- * TODO: The last fragment could still use multiple retry
- * rates. */
- info->control.retries[0].rate_idx = -1;
+ if (is_multicast_ether_addr(hdr->addr1)) {
+ /*
+ * XXX: verify the rate is in the basic rateset
+ */
+ return TX_CONTINUE;
}
- /* Use CTS protection for unicast frames sent using extended rates if
- * there are associated non-ERP stations and RTS/CTS is not configured
- * for the frame. */
- if ((tx->sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) &&
- (sband->bitrates[tx->rate_idx].flags & IEEE80211_RATE_ERP_G) &&
- (tx->flags & IEEE80211_TX_UNICAST) &&
- tx->sdata->bss_conf.use_cts_prot &&
- !(info->flags & IEEE80211_TX_CTL_USE_RTS_CTS))
- info->flags |= IEEE80211_TX_CTL_USE_CTS_PROTECT;
-
- /* Transmit data frames using short preambles if the driver supports
- * short preambles at the selected rate and short preambles are
- * available on the network at the current point in time. */
- if (ieee80211_is_data(hdr->frame_control) &&
- (sband->bitrates[tx->rate_idx].flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
- tx->sdata->bss_conf.use_short_preamble &&
- (!tx->sta || test_sta_flags(tx->sta, WLAN_STA_SHORT_PREAMBLE))) {
- info->flags |= IEEE80211_TX_CTL_SHORT_PREAMBLE;
+ /*
+ * set up the RTS/CTS rate as the fastest basic rate
+ * that is not faster than the data rate
+ *
+ * XXX: Should this check all retry rates?
+ */
+ if (!(info->control.rates[0].flags & IEEE80211_TX_RC_MCS)) {
+ s8 baserate = 0;
+
+ rate = &sband->bitrates[info->control.rates[0].idx];
+
+ for (i = 0; i < sband->n_bitrates; i++) {
+ /* must be a basic rate */
+ if (!(tx->sdata->vif.bss_conf.basic_rates & BIT(i)))
+ continue;
+ /* must not be faster than the data rate */
+ if (sband->bitrates[i].bitrate > rate->bitrate)
+ continue;
+ /* maximum */
+ if (sband->bitrates[baserate].bitrate <
+ sband->bitrates[i].bitrate)
+ baserate = i;
+ }
+
+ info->control.rts_cts_rate_idx = baserate;
}
- if ((info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) ||
- (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT)) {
- struct ieee80211_rate *rate;
- s8 baserate = -1;
- int idx;
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ /*
+ * make sure there's no valid rate following
+ * an invalid one, just in case drivers don't
+ * take the API seriously to stop at -1.
+ */
+ if (inval) {
+ info->control.rates[i].idx = -1;
+ continue;
+ }
+ if (info->control.rates[i].idx < 0) {
+ inval = true;
+ continue;
+ }
- /* Do not use multiple retry rates when using RTS/CTS */
- info->control.retries[0].rate_idx = -1;
+ /*
+ * For now assume MCS is already set up correctly, this
+ * needs to be fixed.
+ */
+ if (info->control.rates[i].flags & IEEE80211_TX_RC_MCS) {
+ WARN_ON(info->control.rates[i].idx > 76);
+ continue;
+ }
- /* Use min(data rate, max base rate) as CTS/RTS rate */
- rate = &sband->bitrates[tx->rate_idx];
+ /* set up RTS protection if desired */
+ if (rts)
+ info->control.rates[i].flags |=
+ IEEE80211_TX_RC_USE_RTS_CTS;
- for (idx = 0; idx < sband->n_bitrates; idx++) {
- if (sband->bitrates[idx].bitrate > rate->bitrate)
- continue;
- if (tx->sdata->bss_conf.basic_rates & BIT(idx) &&
- (baserate < 0 ||
- (sband->bitrates[baserate].bitrate
- < sband->bitrates[idx].bitrate)))
- baserate = idx;
+ /* RC is busted */
+ if (WARN_ON_ONCE(info->control.rates[i].idx >=
+ sband->n_bitrates)) {
+ info->control.rates[i].idx = -1;
+ continue;
}
- if (baserate >= 0)
- info->control.rts_cts_rate_idx = baserate;
- else
- info->control.rts_cts_rate_idx = 0;
+ rate = &sband->bitrates[info->control.rates[i].idx];
+
+ /* set up short preamble */
+ if (short_preamble &&
+ rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
+ info->control.rates[i].flags |=
+ IEEE80211_TX_RC_USE_SHORT_PREAMBLE;
+
+ /* set up G protection */
+ if (!rts && tx->sdata->vif.bss_conf.use_cts_prot &&
+ rate->flags & IEEE80211_RATE_ERP_G)
+ info->control.rates[i].flags |=
+ IEEE80211_TX_RC_USE_CTS_PROTECT;
}
+ return TX_CONTINUE;
+}
+
+static ieee80211_tx_result debug_noinline
+ieee80211_tx_h_misc(struct ieee80211_tx_data *tx)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
+
if (tx->sta)
info->control.sta = &tx->sta->sta;
@@ -602,8 +621,18 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
if (ieee80211_hdrlen(hdr->frame_control) < 24)
return TX_CONTINUE;
+ /*
+ * Anything but QoS data that has a sequence number field
+ * (is long enough) gets a sequence number from the global
+ * counter.
+ */
if (!ieee80211_is_data_qos(hdr->frame_control)) {
+ /* driver should assign sequence number */
info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
+ /* for pure STA mode without beacons, we can do it */
+ hdr->seq_ctrl = cpu_to_le16(tx->sdata->sequence_number);
+ tx->sdata->sequence_number += 0x10;
+ tx->sdata->sequence_number &= IEEE80211_SCTL_SEQ;
return TX_CONTINUE;
}
@@ -632,6 +661,7 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
static ieee80211_tx_result debug_noinline
ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
size_t hdrlen, per_fragm, num_fragm, payload_len, left;
struct sk_buff **frags, *first, *frag;
@@ -648,9 +678,7 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
* This scenario is handled in __ieee80211_tx_prepare but extra
* caution taken here as fragmented ampdu may cause Tx stop.
*/
- if (WARN_ON(tx->flags & IEEE80211_TX_CTL_AMPDU ||
- skb_get_queue_mapping(tx->skb) >=
- ieee80211_num_regular_queues(&tx->local->hw)))
+ if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU))
return TX_DROP;
first = tx->skb;
@@ -684,21 +712,48 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
IEEE80211_ENCRYPT_TAILROOM);
if (!frag)
goto fail;
+
/* Make sure that all fragments use the same priority so
* that they end up using the same TX queue */
frag->priority = first->priority;
+
skb_reserve(frag, tx->local->tx_headroom +
IEEE80211_ENCRYPT_HEADROOM);
+
+ /* copy TX information */
+ info = IEEE80211_SKB_CB(frag);
+ memcpy(info, first->cb, sizeof(frag->cb));
+
+ /* copy/fill in 802.11 header */
fhdr = (struct ieee80211_hdr *) skb_put(frag, hdrlen);
memcpy(fhdr, first->data, hdrlen);
- if (i == num_fragm - 2)
- fhdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREFRAGS);
fhdr->seq_ctrl = cpu_to_le16(seq | ((i + 1) & IEEE80211_SCTL_FRAG));
+
+ if (i == num_fragm - 2) {
+ /* clear MOREFRAGS bit for the last fragment */
+ fhdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREFRAGS);
+ } else {
+ /*
+ * No multi-rate retries for fragmented frames, that
+ * would completely throw off the NAV at other STAs.
+ */
+ info->control.rates[1].idx = -1;
+ info->control.rates[2].idx = -1;
+ info->control.rates[3].idx = -1;
+ info->control.rates[4].idx = -1;
+ BUILD_BUG_ON(IEEE80211_TX_MAX_RATES != 5);
+ info->flags &= ~IEEE80211_TX_CTL_RATE_CTRL_PROBE;
+ }
+
+ /* copy data */
copylen = left > per_fragm ? per_fragm : left;
memcpy(skb_put(frag, copylen), pos, copylen);
- memcpy(frag->cb, first->cb, sizeof(frag->cb));
+
skb_copy_queue_mapping(frag, first);
+
frag->do_not_encrypt = first->do_not_encrypt;
+ frag->dev = first->dev;
+ frag->iif = first->iif;
pos += copylen;
left -= copylen;
@@ -757,12 +812,10 @@ ieee80211_tx_h_calculate_duration(struct ieee80211_tx_data *tx)
tx->extra_frag[0]->len);
for (i = 0; i < tx->num_extra_frag; i++) {
- if (i + 1 < tx->num_extra_frag) {
+ if (i + 1 < tx->num_extra_frag)
next_len = tx->extra_frag[i + 1]->len;
- } else {
+ else
next_len = 0;
- tx->rate_idx = tx->last_frag_rate_idx;
- }
hdr = (struct ieee80211_hdr *)tx->extra_frag[i]->data;
hdr->duration_id = ieee80211_duration(tx, 0, next_len);
@@ -815,7 +868,6 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
(struct ieee80211_radiotap_header *) skb->data;
struct ieee80211_supported_band *sband;
int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len);
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
sband = tx->local->hw.wiphy->bands[tx->channel->band];
@@ -829,8 +881,6 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
*/
while (!ret) {
- int i, target_rate;
-
ret = ieee80211_radiotap_iterator_next(&iterator);
if (ret)
@@ -844,38 +894,6 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
* get_unaligned((type *)iterator.this_arg) to dereference
* iterator.this_arg for type "type" safely on all arches.
*/
- case IEEE80211_RADIOTAP_RATE:
- /*
- * radiotap rate u8 is in 500kbps units eg, 0x02=1Mbps
- * ieee80211 rate int is in 100kbps units eg, 0x0a=1Mbps
- */
- target_rate = (*iterator.this_arg) * 5;
- for (i = 0; i < sband->n_bitrates; i++) {
- struct ieee80211_rate *r;
-
- r = &sband->bitrates[i];
-
- if (r->bitrate == target_rate) {
- tx->rate_idx = i;
- break;
- }
- }
- break;
-
- case IEEE80211_RADIOTAP_ANTENNA:
- /*
- * radiotap uses 0 for 1st ant, mac80211 is 1 for
- * 1st ant
- */
- info->antenna_sel_tx = (*iterator.this_arg) + 1;
- break;
-
-#if 0
- case IEEE80211_RADIOTAP_DBM_TX_POWER:
- control->power_level = *iterator.this_arg;
- break;
-#endif
-
case IEEE80211_RADIOTAP_FLAGS:
if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FCS) {
/*
@@ -933,7 +951,8 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
struct ieee80211_sub_if_data *sdata;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- int hdrlen;
+ int hdrlen, tid;
+ u8 *qc, *state;
memset(tx, 0, sizeof(*tx));
tx->skb = skb;
@@ -941,8 +960,6 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
tx->local = local;
tx->sdata = IEEE80211_DEV_TO_SUB_IF(dev);
tx->channel = local->hw.conf.channel;
- tx->rate_idx = -1;
- tx->last_frag_rate_idx = -1;
/*
* Set this flag (used below to indicate "automatic fragmentation"),
* it will be cleared/left by radiotap as desired.
@@ -966,6 +983,15 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
tx->sta = sta_info_get(local, hdr->addr1);
+ if (tx->sta && ieee80211_is_data_qos(hdr->frame_control)) {
+ qc = ieee80211_get_qos_ctl(hdr);
+ tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
+
+ state = &tx->sta->ampdu_mlme.tid_state_tx[tid];
+ if (*state == HT_AGG_STATE_OPERATIONAL)
+ info->flags |= IEEE80211_TX_CTL_AMPDU;
+ }
+
if (is_multicast_ether_addr(hdr->addr1)) {
tx->flags &= ~IEEE80211_TX_UNICAST;
info->flags |= IEEE80211_TX_CTL_NO_ACK;
@@ -977,7 +1003,6 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
if (tx->flags & IEEE80211_TX_FRAGMENTED) {
if ((tx->flags & IEEE80211_TX_UNICAST) &&
skb->len + FCS_LEN > local->fragmentation_threshold &&
- !local->ops->set_frag_threshold &&
!(info->flags & IEEE80211_TX_CTL_AMPDU))
tx->flags |= IEEE80211_TX_FRAGMENTED;
else
@@ -1043,23 +1068,11 @@ static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
if (!tx->extra_frag[i])
continue;
info = IEEE80211_SKB_CB(tx->extra_frag[i]);
- info->flags &= ~(IEEE80211_TX_CTL_USE_RTS_CTS |
- IEEE80211_TX_CTL_USE_CTS_PROTECT |
- IEEE80211_TX_CTL_CLEAR_PS_FILT |
+ info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT |
IEEE80211_TX_CTL_FIRST_FRAGMENT);
if (netif_subqueue_stopped(local->mdev,
tx->extra_frag[i]))
return IEEE80211_TX_FRAG_AGAIN;
- if (i == tx->num_extra_frag) {
- info->tx_rate_idx = tx->last_frag_rate_idx;
-
- if (tx->flags & IEEE80211_TX_PROBE_LAST_FRAG)
- info->flags |=
- IEEE80211_TX_CTL_RATE_CTRL_PROBE;
- else
- info->flags &=
- ~IEEE80211_TX_CTL_RATE_CTRL_PROBE;
- }
ret = local->ops->tx(local_to_hw(local),
tx->extra_frag[i]);
@@ -1168,7 +1181,7 @@ retry:
* queues, there's no reason for a driver to reject
* a frame there, warn and drop it.
*/
- if (WARN_ON(queue >= ieee80211_num_regular_queues(&local->hw)))
+ if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU))
goto drop;
store = &local->pending_packet[queue];
@@ -1196,9 +1209,6 @@ retry:
store->skb = skb;
store->extra_frag = tx.extra_frag;
store->num_extra_frag = tx.num_extra_frag;
- store->last_frag_rate_idx = tx.last_frag_rate_idx;
- store->last_frag_rate_ctrl_probe =
- !!(tx.flags & IEEE80211_TX_PROBE_LAST_FRAG);
}
out:
rcu_read_unlock();
@@ -1299,8 +1309,10 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (is_multicast_ether_addr(hdr->addr3))
memcpy(hdr->addr1, hdr->addr3, ETH_ALEN);
else
- if (mesh_nexthop_lookup(skb, osdata))
- return 0;
+ if (mesh_nexthop_lookup(skb, osdata)) {
+ dev_put(odev);
+ return 0;
+ }
if (memcmp(odev->dev_addr, hdr->addr4, ETH_ALEN) != 0)
IEEE80211_IFSTA_MESH_CTR_INC(&osdata->u.mesh,
fwded_frames);
@@ -1333,6 +1345,8 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
list) {
if (!netif_running(sdata->dev))
continue;
+ if (sdata->vif.type != NL80211_IFTYPE_AP)
+ continue;
if (compare_ether_addr(sdata->dev->dev_addr,
hdr->addr2)) {
dev_hold(sdata->dev);
@@ -1465,6 +1479,19 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
goto fail;
}
+ if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) &&
+ local->dynamic_ps_timeout > 0) {
+ if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+ ieee80211_stop_queues_by_reason(&local->hw,
+ IEEE80211_QUEUE_STOP_REASON_PS);
+ queue_work(local->hw.workqueue,
+ &local->dynamic_ps_disable_work);
+ }
+
+ mod_timer(&local->dynamic_ps_timer, jiffies +
+ msecs_to_jiffies(local->dynamic_ps_timeout));
+ }
+
nh_pos = skb_network_header(skb) - skb->data;
h_pos = skb_transport_header(skb) - skb->data;
@@ -1593,12 +1620,10 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
compare_ether_addr(dev->dev_addr,
skb->data + ETH_ALEN) == 0))) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- DECLARE_MAC_BUF(mac);
-
if (net_ratelimit())
- printk(KERN_DEBUG "%s: dropped frame to %s"
+ printk(KERN_DEBUG "%s: dropped frame to %pM"
" (unauthorized port)\n", dev->name,
- print_mac(mac, hdr.addr1));
+ hdr.addr1);
#endif
I802_DEBUG_INC(local->tx_handlers_drop_unauth_port);
@@ -1757,10 +1782,7 @@ void ieee80211_tx_pending(unsigned long data)
store = &local->pending_packet[i];
tx.extra_frag = store->extra_frag;
tx.num_extra_frag = store->num_extra_frag;
- tx.last_frag_rate_idx = store->last_frag_rate_idx;
tx.flags = 0;
- if (store->last_frag_rate_ctrl_probe)
- tx.flags |= IEEE80211_TX_PROBE_LAST_FRAG;
ret = __ieee80211_tx(local, store->skb, &tx);
if (ret) {
if (ret == IEEE80211_TX_FRAG_AGAIN)
@@ -1775,8 +1797,7 @@ void ieee80211_tx_pending(unsigned long data)
/* functions for drivers to get certain frames */
-static void ieee80211_beacon_add_tim(struct ieee80211_local *local,
- struct ieee80211_if_ap *bss,
+static void ieee80211_beacon_add_tim(struct ieee80211_if_ap *bss,
struct sk_buff *skb,
struct beacon_data *beacon)
{
@@ -1844,11 +1865,9 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
struct ieee80211_local *local = hw_to_local(hw);
struct sk_buff *skb = NULL;
struct ieee80211_tx_info *info;
- struct net_device *bdev;
struct ieee80211_sub_if_data *sdata = NULL;
struct ieee80211_if_ap *ap = NULL;
struct ieee80211_if_sta *ifsta = NULL;
- struct rate_selection rsel;
struct beacon_data *beacon;
struct ieee80211_supported_band *sband;
enum ieee80211_band band = local->hw.conf.channel->band;
@@ -1858,7 +1877,6 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
rcu_read_lock();
sdata = vif_to_sdata(vif);
- bdev = sdata->dev;
if (sdata->vif.type == NL80211_IFTYPE_AP) {
ap = &sdata->u.ap;
@@ -1886,12 +1904,12 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
* of the tim bitmap in mac80211 and the driver.
*/
if (local->tim_in_locked_section) {
- ieee80211_beacon_add_tim(local, ap, skb, beacon);
+ ieee80211_beacon_add_tim(ap, skb, beacon);
} else {
unsigned long flags;
spin_lock_irqsave(&local->sta_lock, flags);
- ieee80211_beacon_add_tim(local, ap, skb, beacon);
+ ieee80211_beacon_add_tim(ap, skb, beacon);
spin_unlock_irqrestore(&local->sta_lock, flags);
}
@@ -1952,33 +1970,23 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
skb->do_not_encrypt = 1;
info->band = band;
- rate_control_get_rate(sdata, sband, NULL, skb, &rsel);
-
- if (unlikely(rsel.rate_idx < 0)) {
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: ieee80211_beacon_get: "
- "no rate found\n",
- wiphy_name(local->hw.wiphy));
- }
- dev_kfree_skb_any(skb);
- skb = NULL;
- goto out;
- }
+ /*
+ * XXX: For now, always use the lowest rate
+ */
+ info->control.rates[0].idx = 0;
+ info->control.rates[0].count = 1;
+ info->control.rates[1].idx = -1;
+ info->control.rates[2].idx = -1;
+ info->control.rates[3].idx = -1;
+ info->control.rates[4].idx = -1;
+ BUILD_BUG_ON(IEEE80211_TX_MAX_RATES != 5);
info->control.vif = vif;
- info->tx_rate_idx = rsel.rate_idx;
info->flags |= IEEE80211_TX_CTL_NO_ACK;
info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
- if (sdata->bss_conf.use_short_preamble &&
- sband->bitrates[rsel.rate_idx].flags & IEEE80211_RATE_SHORT_PREAMBLE)
- info->flags |= IEEE80211_TX_CTL_SHORT_PREAMBLE;
-
- info->antenna_sel_tx = local->hw.conf.antenna_sel_tx;
- info->control.retry_limit = 1;
-
-out:
+ out:
rcu_read_unlock();
return skb;
}
@@ -2023,14 +2031,12 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
struct sk_buff *skb = NULL;
struct sta_info *sta;
struct ieee80211_tx_data tx;
- struct net_device *bdev;
struct ieee80211_sub_if_data *sdata;
struct ieee80211_if_ap *bss = NULL;
struct beacon_data *beacon;
struct ieee80211_tx_info *info;
sdata = vif_to_sdata(vif);
- bdev = sdata->dev;
bss = &sdata->u.ap;
if (!bss)
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index cee4884b9d06..fb89e1d0aa03 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -239,7 +239,7 @@ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
erp = 0;
if (vif) {
sdata = vif_to_sdata(vif);
- short_preamble = sdata->bss_conf.use_short_preamble;
+ short_preamble = sdata->vif.bss_conf.use_short_preamble;
if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
erp = rate->flags & IEEE80211_RATE_ERP_G;
}
@@ -272,7 +272,7 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
erp = 0;
if (vif) {
sdata = vif_to_sdata(vif);
- short_preamble = sdata->bss_conf.use_short_preamble;
+ short_preamble = sdata->vif.bss_conf.use_short_preamble;
if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
erp = rate->flags & IEEE80211_RATE_ERP_G;
}
@@ -312,7 +312,7 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
erp = 0;
if (vif) {
sdata = vif_to_sdata(vif);
- short_preamble = sdata->bss_conf.use_short_preamble;
+ short_preamble = sdata->vif.bss_conf.use_short_preamble;
if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
erp = rate->flags & IEEE80211_RATE_ERP_G;
}
@@ -330,10 +330,20 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_ctstoself_duration);
-void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
+static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
+ enum queue_stop_reason reason)
{
struct ieee80211_local *local = hw_to_local(hw);
+ /* we don't need to track ampdu queues */
+ if (queue < ieee80211_num_regular_queues(hw)) {
+ __clear_bit(reason, &local->queue_stop_reasons[queue]);
+
+ if (local->queue_stop_reasons[queue] != 0)
+ /* someone still has this queue stopped */
+ return;
+ }
+
if (test_bit(queue, local->queues_pending)) {
set_bit(queue, local->queues_pending_run);
tasklet_schedule(&local->tx_pending_tasklet);
@@ -341,22 +351,74 @@ void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
netif_wake_subqueue(local->mdev, queue);
}
}
+
+void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
+ enum queue_stop_reason reason)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+ __ieee80211_wake_queue(hw, queue, reason);
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
+{
+ ieee80211_wake_queue_by_reason(hw, queue,
+ IEEE80211_QUEUE_STOP_REASON_DRIVER);
+}
EXPORT_SYMBOL(ieee80211_wake_queue);
-void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
+static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
+ enum queue_stop_reason reason)
{
struct ieee80211_local *local = hw_to_local(hw);
+ /* we don't need to track ampdu queues */
+ if (queue < ieee80211_num_regular_queues(hw))
+ __set_bit(reason, &local->queue_stop_reasons[queue]);
+
netif_stop_subqueue(local->mdev, queue);
}
+
+void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
+ enum queue_stop_reason reason)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+ __ieee80211_stop_queue(hw, queue, reason);
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
+{
+ ieee80211_stop_queue_by_reason(hw, queue,
+ IEEE80211_QUEUE_STOP_REASON_DRIVER);
+}
EXPORT_SYMBOL(ieee80211_stop_queue);
-void ieee80211_stop_queues(struct ieee80211_hw *hw)
+void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
+ enum queue_stop_reason reason)
{
+ struct ieee80211_local *local = hw_to_local(hw);
+ unsigned long flags;
int i;
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+
for (i = 0; i < ieee80211_num_queues(hw); i++)
- ieee80211_stop_queue(hw, i);
+ __ieee80211_stop_queue(hw, i, reason);
+
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+void ieee80211_stop_queues(struct ieee80211_hw *hw)
+{
+ ieee80211_stop_queues_by_reason(hw,
+ IEEE80211_QUEUE_STOP_REASON_DRIVER);
}
EXPORT_SYMBOL(ieee80211_stop_queues);
@@ -367,12 +429,24 @@ int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue)
}
EXPORT_SYMBOL(ieee80211_queue_stopped);
-void ieee80211_wake_queues(struct ieee80211_hw *hw)
+void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
+ enum queue_stop_reason reason)
{
+ struct ieee80211_local *local = hw_to_local(hw);
+ unsigned long flags;
int i;
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+
for (i = 0; i < hw->queues + hw->ampdu_queues; i++)
- ieee80211_wake_queue(hw, i);
+ __ieee80211_wake_queue(hw, i, reason);
+
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+void ieee80211_wake_queues(struct ieee80211_hw *hw)
+{
+ ieee80211_wake_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_DRIVER);
}
EXPORT_SYMBOL(ieee80211_wake_queues);
@@ -532,8 +606,8 @@ void ieee802_11_parse_elems(u8 *start, size_t len,
if (elen >= sizeof(struct ieee80211_ht_cap))
elems->ht_cap_elem = (void *)pos;
break;
- case WLAN_EID_HT_EXTRA_INFO:
- if (elen >= sizeof(struct ieee80211_ht_addt_info))
+ case WLAN_EID_HT_INFORMATION:
+ if (elen >= sizeof(struct ieee80211_ht_info))
elems->ht_info_elem = (void *)pos;
break;
case WLAN_EID_MESH_ID:
@@ -638,19 +712,16 @@ int ieee80211_set_freq(struct ieee80211_sub_if_data *sdata, int freqMHz)
if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) {
if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
- chan->flags & IEEE80211_CHAN_NO_IBSS) {
- printk(KERN_DEBUG "%s: IBSS not allowed on frequency "
- "%d MHz\n", sdata->dev->name, chan->center_freq);
+ chan->flags & IEEE80211_CHAN_NO_IBSS)
return ret;
- }
local->oper_channel = chan;
+ local->oper_channel_type = NL80211_CHAN_NO_HT;
if (local->sw_scanning || local->hw_scanning)
ret = 0;
else
- ret = ieee80211_hw_config(local);
-
- rate_control_clear(local);
+ ret = ieee80211_hw_config(
+ local, IEEE80211_CONF_CHANGE_CHANNEL);
}
return ret;
diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c
index f0e2d3ecb5c4..7043ddc75498 100644
--- a/net/mac80211/wep.c
+++ b/net/mac80211/wep.c
@@ -17,6 +17,7 @@
#include <linux/err.h>
#include <linux/mm.h>
#include <linux/scatterlist.h>
+#include <asm/unaligned.h>
#include <net/mac80211.h>
#include "ieee80211_i.h"
@@ -49,17 +50,19 @@ void ieee80211_wep_free(struct ieee80211_local *local)
crypto_free_blkcipher(local->wep_rx_tfm);
}
-static inline int ieee80211_wep_weak_iv(u32 iv, int keylen)
+static inline bool ieee80211_wep_weak_iv(u32 iv, int keylen)
{
- /* Fluhrer, Mantin, and Shamir have reported weaknesses in the
+ /*
+ * Fluhrer, Mantin, and Shamir have reported weaknesses in the
* key scheduling algorithm of RC4. At least IVs (KeyByte + 3,
- * 0xff, N) can be used to speedup attacks, so avoid using them. */
+ * 0xff, N) can be used to speedup attacks, so avoid using them.
+ */
if ((iv & 0xff00) == 0xff00) {
u8 B = (iv >> 16) & 0xff;
if (B >= 3 && B < 3 + keylen)
- return 1;
+ return true;
}
- return 0;
+ return false;
}
@@ -123,10 +126,10 @@ void ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key,
{
struct blkcipher_desc desc = { .tfm = tfm };
struct scatterlist sg;
- __le32 *icv;
+ __le32 icv;
- icv = (__le32 *)(data + data_len);
- *icv = cpu_to_le32(~crc32_le(~0, data, data_len));
+ icv = cpu_to_le32(~crc32_le(~0, data, data_len));
+ put_unaligned(icv, (__le32 *)(data + data_len));
crypto_blkcipher_setkey(tfm, rc4key, klen);
sg_init_one(&sg, data, data_len + WEP_ICV_LEN);
@@ -268,7 +271,7 @@ int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb,
}
-u8 * ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key)
+bool ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
unsigned int hdrlen;
@@ -276,16 +279,13 @@ u8 * ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key)
u32 iv;
if (!ieee80211_has_protected(hdr->frame_control))
- return NULL;
+ return false;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
ivpos = skb->data + hdrlen;
iv = (ivpos[0] << 16) | (ivpos[1] << 8) | ivpos[2];
- if (ieee80211_wep_weak_iv(iv, key->conf.keylen))
- return ivpos;
-
- return NULL;
+ return ieee80211_wep_weak_iv(iv, key->conf.keylen);
}
ieee80211_rx_result
@@ -329,6 +329,8 @@ static int wep_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
ieee80211_tx_result
ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx)
{
+ int i;
+
ieee80211_tx_set_protected(tx);
if (wep_encrypt_skb(tx, tx->skb) < 0) {
@@ -337,9 +339,8 @@ ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx)
}
if (tx->extra_frag) {
- int i;
for (i = 0; i < tx->num_extra_frag; i++) {
- if (wep_encrypt_skb(tx, tx->extra_frag[i]) < 0) {
+ if (wep_encrypt_skb(tx, tx->extra_frag[i])) {
I802_DEBUG_INC(tx->local->
tx_handlers_drop_wep);
return TX_DROP;
diff --git a/net/mac80211/wep.h b/net/mac80211/wep.h
index e587172115b8..d3f0db48314e 100644
--- a/net/mac80211/wep.h
+++ b/net/mac80211/wep.h
@@ -26,7 +26,7 @@ int ieee80211_wep_encrypt(struct ieee80211_local *local, struct sk_buff *skb,
struct ieee80211_key *key);
int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb,
struct ieee80211_key *key);
-u8 *ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key);
+bool ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key);
ieee80211_rx_result
ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx);
diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c
index ab4ddba874be..bfe4a950225a 100644
--- a/net/mac80211/wext.c
+++ b/net/mac80211/wext.c
@@ -135,48 +135,6 @@ static int ieee80211_ioctl_siwgenie(struct net_device *dev,
return -EOPNOTSUPP;
}
-static int ieee80211_ioctl_giwname(struct net_device *dev,
- struct iw_request_info *info,
- char *name, char *extra)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_supported_band *sband;
- u8 is_ht = 0, is_a = 0, is_b = 0, is_g = 0;
-
-
- sband = local->hw.wiphy->bands[IEEE80211_BAND_5GHZ];
- if (sband) {
- is_a = 1;
- is_ht |= sband->ht_info.ht_supported;
- }
-
- sband = local->hw.wiphy->bands[IEEE80211_BAND_2GHZ];
- if (sband) {
- int i;
- /* Check for mandatory rates */
- for (i = 0; i < sband->n_bitrates; i++) {
- if (sband->bitrates[i].bitrate == 10)
- is_b = 1;
- if (sband->bitrates[i].bitrate == 60)
- is_g = 1;
- }
- is_ht |= sband->ht_info.ht_supported;
- }
-
- strcpy(name, "IEEE 802.11");
- if (is_a)
- strcat(name, "a");
- if (is_b)
- strcat(name, "b");
- if (is_g)
- strcat(name, "g");
- if (is_ht)
- strcat(name, "n");
-
- return 0;
-}
-
-
static int ieee80211_ioctl_giwrange(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *data, char *extra)
@@ -185,6 +143,8 @@ static int ieee80211_ioctl_giwrange(struct net_device *dev,
struct iw_range *range = (struct iw_range *) extra;
enum ieee80211_band band;
int c = 0;
+ int max_power = 0;
+ int i;
data->length = sizeof(struct iw_range);
memset(range, 0, sizeof(struct iw_range));
@@ -230,9 +190,7 @@ static int ieee80211_ioctl_giwrange(struct net_device *dev,
range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
-
for (band = 0; band < IEEE80211_NUM_BANDS; band ++) {
- int i;
struct ieee80211_supported_band *sband;
sband = local->hw.wiphy->bands[band];
@@ -250,12 +208,44 @@ static int ieee80211_ioctl_giwrange(struct net_device *dev,
range->freq[c].m = chan->center_freq;
range->freq[c].e = 6;
c++;
+ if (chan->max_power > max_power)
+ max_power = chan->max_power;
+ }
+ }
+
+ for (i=0; i< sband->n_bitrates; i++) {
+ struct ieee80211_rate *brate = &sband->bitrates[i];
+ int j;
+
+ /* avoid duplicating rates from previous bands */
+ for (j = 0; j < range->num_bitrates; j++)
+ if (range->bitrate[j] == brate->bitrate * 100000)
+ break;
+
+ if (j == range->num_bitrates) {
+ range->bitrate[range->num_bitrates] =
+ brate->bitrate * 100000;
+ range->num_bitrates++;
}
}
}
+
range->num_channels = c;
range->num_frequency = c;
+ if (max_power >= IW_MAX_TXPOWER) {
+ range->num_txpower = IW_MAX_TXPOWER;
+ range->txpower[0] = 0;
+ for (i = 1; i < IW_MAX_TXPOWER; i++) {
+ range->txpower[i] = max_power - (IW_MAX_TXPOWER - (i + 1));
+ }
+ } else {
+ range->num_txpower = max_power + 1;
+ for (i = 0; i < max_power; i++) {
+ range->txpower[i] = i;
+ }
+ }
+
IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
@@ -266,78 +256,6 @@ static int ieee80211_ioctl_giwrange(struct net_device *dev,
}
-static int ieee80211_ioctl_siwmode(struct net_device *dev,
- struct iw_request_info *info,
- __u32 *mode, char *extra)
-{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_local *local = sdata->local;
- int type;
-
- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
- return -EOPNOTSUPP;
-
- switch (*mode) {
- case IW_MODE_INFRA:
- type = NL80211_IFTYPE_STATION;
- break;
- case IW_MODE_ADHOC:
- /* Setting ad-hoc mode on non ibss channel is not
- * supported.
- */
- if (local->oper_channel &&
- (local->oper_channel->flags & IEEE80211_CHAN_NO_IBSS))
- return -EOPNOTSUPP;
-
- type = NL80211_IFTYPE_ADHOC;
- break;
- case IW_MODE_REPEAT:
- type = NL80211_IFTYPE_WDS;
- break;
- case IW_MODE_MONITOR:
- type = NL80211_IFTYPE_MONITOR;
- break;
- default:
- return -EINVAL;
- }
-
- return ieee80211_if_change_type(sdata, type);
-}
-
-
-static int ieee80211_ioctl_giwmode(struct net_device *dev,
- struct iw_request_info *info,
- __u32 *mode, char *extra)
-{
- struct ieee80211_sub_if_data *sdata;
-
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- switch (sdata->vif.type) {
- case NL80211_IFTYPE_AP:
- *mode = IW_MODE_MASTER;
- break;
- case NL80211_IFTYPE_STATION:
- *mode = IW_MODE_INFRA;
- break;
- case NL80211_IFTYPE_ADHOC:
- *mode = IW_MODE_ADHOC;
- break;
- case NL80211_IFTYPE_MONITOR:
- *mode = IW_MODE_MONITOR;
- break;
- case NL80211_IFTYPE_WDS:
- *mode = IW_MODE_REPEAT;
- break;
- case NL80211_IFTYPE_AP_VLAN:
- *mode = IW_MODE_SECOND; /* FIXME */
- break;
- default:
- *mode = IW_MODE_AUTO;
- break;
- }
- return 0;
-}
-
static int ieee80211_ioctl_siwfreq(struct net_device *dev,
struct iw_request_info *info,
struct iw_freq *freq, char *extra)
@@ -415,13 +333,6 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev,
return 0;
}
- if (sdata->vif.type == NL80211_IFTYPE_AP) {
- memcpy(sdata->u.ap.ssid, ssid, len);
- memset(sdata->u.ap.ssid + len, 0,
- IEEE80211_MAX_SSID_LEN - len);
- sdata->u.ap.ssid_len = len;
- return ieee80211_if_config(sdata, IEEE80211_IFCC_SSID);
- }
return -EOPNOTSUPP;
}
@@ -445,15 +356,6 @@ static int ieee80211_ioctl_giwessid(struct net_device *dev,
return res;
}
- if (sdata->vif.type == NL80211_IFTYPE_AP) {
- len = sdata->u.ap.ssid_len;
- if (len > IW_ESSID_MAX_SIZE)
- len = IW_ESSID_MAX_SIZE;
- memcpy(ssid, sdata->u.ap.ssid, len);
- data->length = len;
- data->flags = 1;
- return 0;
- }
return -EOPNOTSUPP;
}
@@ -548,8 +450,7 @@ static int ieee80211_ioctl_siwscan(struct net_device *dev,
if (sdata->vif.type != NL80211_IFTYPE_STATION &&
sdata->vif.type != NL80211_IFTYPE_ADHOC &&
- sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
- sdata->vif.type != NL80211_IFTYPE_AP)
+ sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
return -EOPNOTSUPP;
/* if SSID was specified explicitly then use that */
@@ -644,8 +545,8 @@ static int ieee80211_ioctl_giwrate(struct net_device *dev,
sta = sta_info_get(local, sdata->u.sta.bssid);
- if (sta && sta->last_txrate_idx < sband->n_bitrates)
- rate->value = sband->bitrates[sta->last_txrate_idx].bitrate;
+ if (sta && !(sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS))
+ rate->value = sband->bitrates[sta->last_tx_rate.idx].bitrate;
else
rate->value = 0;
@@ -664,45 +565,35 @@ static int ieee80211_ioctl_siwtxpower(struct net_device *dev,
union iwreq_data *data, char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- bool need_reconfig = 0;
+ struct ieee80211_channel* chan = local->hw.conf.channel;
+ u32 reconf_flags = 0;
int new_power_level;
if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM)
return -EINVAL;
if (data->txpower.flags & IW_TXPOW_RANGE)
return -EINVAL;
+ if (!chan)
+ return -EINVAL;
- if (data->txpower.fixed) {
- new_power_level = data->txpower.value;
- } else {
- /*
- * Automatic power level. Use maximum power for the current
- * channel. Should be part of rate control.
- */
- struct ieee80211_channel* chan = local->hw.conf.channel;
- if (!chan)
- return -EINVAL;
-
+ if (data->txpower.fixed)
+ new_power_level = min(data->txpower.value, chan->max_power);
+ else /* Automatic power level setting */
new_power_level = chan->max_power;
- }
if (local->hw.conf.power_level != new_power_level) {
local->hw.conf.power_level = new_power_level;
- need_reconfig = 1;
+ reconf_flags |= IEEE80211_CONF_CHANGE_POWER;
}
if (local->hw.conf.radio_enabled != !(data->txpower.disabled)) {
local->hw.conf.radio_enabled = !(data->txpower.disabled);
- need_reconfig = 1;
+ reconf_flags |= IEEE80211_CONF_CHANGE_RADIO_ENABLED;
ieee80211_led_radio(local, local->hw.conf.radio_enabled);
}
- if (need_reconfig) {
- ieee80211_hw_config(local);
- /* The return value of hw_config is not of big interest here,
- * as it doesn't say that it failed because of _this_ config
- * change or something else. Ignore it. */
- }
+ if (reconf_flags)
+ ieee80211_hw_config(local, reconf_flags);
return 0;
}
@@ -779,14 +670,6 @@ static int ieee80211_ioctl_siwfrag(struct net_device *dev,
local->fragmentation_threshold = frag->value & ~0x1;
}
- /* If the wlan card performs fragmentation in hardware/firmware,
- * configure it here */
-
- if (local->ops->set_frag_threshold)
- return local->ops->set_frag_threshold(
- local_to_hw(local),
- local->fragmentation_threshold);
-
return 0;
}
@@ -814,21 +697,16 @@ static int ieee80211_ioctl_siwretry(struct net_device *dev,
(retry->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT)
return -EINVAL;
- if (retry->flags & IW_RETRY_MAX)
- local->long_retry_limit = retry->value;
- else if (retry->flags & IW_RETRY_MIN)
- local->short_retry_limit = retry->value;
- else {
- local->long_retry_limit = retry->value;
- local->short_retry_limit = retry->value;
+ if (retry->flags & IW_RETRY_MAX) {
+ local->hw.conf.long_frame_max_tx_count = retry->value;
+ } else if (retry->flags & IW_RETRY_MIN) {
+ local->hw.conf.short_frame_max_tx_count = retry->value;
+ } else {
+ local->hw.conf.long_frame_max_tx_count = retry->value;
+ local->hw.conf.short_frame_max_tx_count = retry->value;
}
- if (local->ops->set_retry_limit) {
- return local->ops->set_retry_limit(
- local_to_hw(local),
- local->short_retry_limit,
- local->long_retry_limit);
- }
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_RETRY_LIMITS);
return 0;
}
@@ -845,14 +723,15 @@ static int ieee80211_ioctl_giwretry(struct net_device *dev,
/* first return min value, iwconfig will ask max value
* later if needed */
retry->flags |= IW_RETRY_LIMIT;
- retry->value = local->short_retry_limit;
- if (local->long_retry_limit != local->short_retry_limit)
+ retry->value = local->hw.conf.short_frame_max_tx_count;
+ if (local->hw.conf.long_frame_max_tx_count !=
+ local->hw.conf.short_frame_max_tx_count)
retry->flags |= IW_RETRY_MIN;
return 0;
}
if (retry->flags & IW_RETRY_MAX) {
retry->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
- retry->value = local->long_retry_limit;
+ retry->value = local->hw.conf.long_frame_max_tx_count;
}
return 0;
@@ -983,25 +862,56 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev,
struct iw_param *wrq,
char *extra)
{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_conf *conf = &local->hw.conf;
+ int ret = 0, timeout = 0;
+ bool ps;
+
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return -EINVAL;
if (wrq->disabled) {
- conf->flags &= ~IEEE80211_CONF_PS;
- return ieee80211_hw_config(local);
+ ps = false;
+ timeout = 0;
+ goto set;
}
switch (wrq->flags & IW_POWER_MODE) {
case IW_POWER_ON: /* If not specified */
case IW_POWER_MODE: /* If set all mask */
case IW_POWER_ALL_R: /* If explicitely state all */
- conf->flags |= IEEE80211_CONF_PS;
+ ps = true;
break;
- default: /* Otherwise we don't support it */
- return -EINVAL;
+ default: /* Otherwise we ignore */
+ break;
+ }
+
+ if (wrq->flags & IW_POWER_TIMEOUT)
+ timeout = wrq->value / 1000;
+
+set:
+ if (ps == local->powersave && timeout == local->dynamic_ps_timeout)
+ return ret;
+
+ local->powersave = ps;
+ local->dynamic_ps_timeout = timeout;
+
+ if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) {
+ if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) &&
+ local->dynamic_ps_timeout > 0)
+ mod_timer(&local->dynamic_ps_timer, jiffies +
+ msecs_to_jiffies(local->dynamic_ps_timeout));
+ else {
+ if (local->powersave)
+ conf->flags |= IEEE80211_CONF_PS;
+ else
+ conf->flags &= ~IEEE80211_CONF_PS;
+ }
+ ret = ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
}
- return ieee80211_hw_config(local);
+ return ret;
}
static int ieee80211_ioctl_giwpower(struct net_device *dev,
@@ -1010,9 +920,8 @@ static int ieee80211_ioctl_giwpower(struct net_device *dev,
char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_conf *conf = &local->hw.conf;
- wrqu->power.disabled = !(conf->flags & IEEE80211_CONF_PS);
+ wrqu->power.disabled = !local->powersave;
return 0;
}
@@ -1176,13 +1085,13 @@ static int ieee80211_ioctl_siwencodeext(struct net_device *dev,
static const iw_handler ieee80211_handler[] =
{
(iw_handler) NULL, /* SIOCSIWCOMMIT */
- (iw_handler) ieee80211_ioctl_giwname, /* SIOCGIWNAME */
+ (iw_handler) cfg80211_wext_giwname, /* SIOCGIWNAME */
(iw_handler) NULL, /* SIOCSIWNWID */
(iw_handler) NULL, /* SIOCGIWNWID */
(iw_handler) ieee80211_ioctl_siwfreq, /* SIOCSIWFREQ */
(iw_handler) ieee80211_ioctl_giwfreq, /* SIOCGIWFREQ */
- (iw_handler) ieee80211_ioctl_siwmode, /* SIOCSIWMODE */
- (iw_handler) ieee80211_ioctl_giwmode, /* SIOCGIWMODE */
+ (iw_handler) cfg80211_wext_siwmode, /* SIOCSIWMODE */
+ (iw_handler) cfg80211_wext_giwmode, /* SIOCGIWMODE */
(iw_handler) NULL, /* SIOCSIWSENS */
(iw_handler) NULL, /* SIOCGIWSENS */
(iw_handler) NULL /* not used */, /* SIOCSIWRANGE */
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index 139b5f267b34..ac71b38f7cb5 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -114,8 +114,8 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb)
{
struct ieee80211_master_priv *mpriv = netdev_priv(dev);
struct ieee80211_local *local = mpriv->local;
+ struct ieee80211_hw *hw = &local->hw;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct sta_info *sta;
u16 queue;
u8 tid;
@@ -124,21 +124,19 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb)
if (unlikely(queue >= local->hw.queues))
queue = local->hw.queues - 1;
- if (info->flags & IEEE80211_TX_CTL_REQUEUE) {
+ if (skb->requeue) {
+ if (!hw->ampdu_queues)
+ return queue;
+
rcu_read_lock();
sta = sta_info_get(local, hdr->addr1);
tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
if (sta) {
- struct ieee80211_hw *hw = &local->hw;
int ampdu_queue = sta->tid_to_tx_q[tid];
if ((ampdu_queue < ieee80211_num_queues(hw)) &&
- test_bit(ampdu_queue, local->queue_pool)) {
+ test_bit(ampdu_queue, local->queue_pool))
queue = ampdu_queue;
- info->flags |= IEEE80211_TX_CTL_AMPDU;
- } else {
- info->flags &= ~IEEE80211_TX_CTL_AMPDU;
- }
}
rcu_read_unlock();
@@ -159,20 +157,18 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb)
*p++ = ack_policy | tid;
*p = 0;
+ if (!hw->ampdu_queues)
+ return queue;
+
rcu_read_lock();
sta = sta_info_get(local, hdr->addr1);
if (sta) {
int ampdu_queue = sta->tid_to_tx_q[tid];
- struct ieee80211_hw *hw = &local->hw;
if ((ampdu_queue < ieee80211_num_queues(hw)) &&
- test_bit(ampdu_queue, local->queue_pool)) {
+ test_bit(ampdu_queue, local->queue_pool))
queue = ampdu_queue;
- info->flags |= IEEE80211_TX_CTL_AMPDU;
- } else {
- info->flags &= ~IEEE80211_TX_CTL_AMPDU;
- }
}
rcu_read_unlock();
@@ -206,13 +202,11 @@ int ieee80211_ht_agg_queue_add(struct ieee80211_local *local,
* on the previous queue
* since HT is strict in order */
#ifdef CONFIG_MAC80211_HT_DEBUG
- if (net_ratelimit()) {
- DECLARE_MAC_BUF(mac);
+ if (net_ratelimit())
printk(KERN_DEBUG "allocated aggregation queue"
- " %d tid %d addr %s pool=0x%lX\n",
- i, tid, print_mac(mac, sta->sta.addr),
+ " %d tid %d addr %pM pool=0x%lX\n",
+ i, tid, sta->sta.addr,
local->queue_pool[0]);
- }
#endif /* CONFIG_MAC80211_HT_DEBUG */
return 0;
}
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index 6db649480e8f..7aa63caf8d50 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -49,8 +49,7 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)
!(tx->flags & IEEE80211_TX_FRAGMENTED) &&
!(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) &&
!wpa_test) {
- /* hwaccel - with no need for preallocated room for Michael MIC
- */
+ /* hwaccel - with no need for preallocated room for MMIC */
return TX_CONTINUE;
}
@@ -67,8 +66,6 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)
#else
authenticator = 1;
#endif
- /* At this point we know we're using ALG_TKIP. To get the MIC key
- * we now will rely on the offset from the ieee80211_key_conf::key */
key_offset = authenticator ?
NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY :
NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY;
@@ -90,11 +87,8 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)
u8 mic[MICHAEL_MIC_LEN];
struct sk_buff *skb = rx->skb;
int authenticator = 1, wpa_test = 0;
- DECLARE_MAC_BUF(mac);
- /*
- * No way to verify the MIC if the hardware stripped it
- */
+ /* No way to verify the MIC if the hardware stripped it */
if (rx->status->flag & RX_FLAG_MMIC_STRIPPED)
return RX_CONTINUE;
@@ -116,8 +110,6 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)
#else
authenticator = 1;
#endif
- /* At this point we know we're using ALG_TKIP. To get the MIC key
- * we now will rely on the offset from the ieee80211_key_conf::key */
key_offset = authenticator ?
NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY :
NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY;
@@ -202,6 +194,7 @@ ieee80211_tx_result
ieee80211_crypto_tkip_encrypt(struct ieee80211_tx_data *tx)
{
struct sk_buff *skb = tx->skb;
+ int i;
ieee80211_tx_set_protected(tx);
@@ -209,9 +202,8 @@ ieee80211_crypto_tkip_encrypt(struct ieee80211_tx_data *tx)
return TX_DROP;
if (tx->extra_frag) {
- int i;
for (i = 0; i < tx->num_extra_frag; i++) {
- if (tkip_encrypt_skb(tx, tx->extra_frag[i]) < 0)
+ if (tkip_encrypt_skb(tx, tx->extra_frag[i]))
return TX_DROP;
}
}
@@ -227,7 +219,6 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
int hdrlen, res, hwaccel = 0, wpa_test = 0;
struct ieee80211_key *key = rx->key;
struct sk_buff *skb = rx->skb;
- DECLARE_MAC_BUF(mac);
hdrlen = ieee80211_hdrlen(hdr->frame_control);
@@ -350,7 +341,7 @@ static inline void ccmp_pn2hdr(u8 *hdr, u8 *pn, int key_id)
}
-static inline int ccmp_hdr2pn(u8 *pn, u8 *hdr)
+static inline void ccmp_hdr2pn(u8 *pn, u8 *hdr)
{
pn[0] = hdr[7];
pn[1] = hdr[6];
@@ -358,7 +349,6 @@ static inline int ccmp_hdr2pn(u8 *pn, u8 *hdr)
pn[3] = hdr[4];
pn[4] = hdr[1];
pn[5] = hdr[0];
- return (hdr[3] >> 6) & 0x03;
}
@@ -373,7 +363,7 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
if ((tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) &&
!(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
- /* hwaccel - with no need for preallocated room for CCMP "
+ /* hwaccel - with no need for preallocated room for CCMP
* header or MIC fields */
info->control.hw_key = &tx->key->conf;
return 0;
@@ -426,6 +416,7 @@ ieee80211_tx_result
ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx)
{
struct sk_buff *skb = tx->skb;
+ int i;
ieee80211_tx_set_protected(tx);
@@ -433,9 +424,8 @@ ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx)
return TX_DROP;
if (tx->extra_frag) {
- int i;
for (i = 0; i < tx->num_extra_frag; i++) {
- if (ccmp_encrypt_skb(tx, tx->extra_frag[i]) < 0)
+ if (ccmp_encrypt_skb(tx, tx->extra_frag[i]))
return TX_DROP;
}
}
@@ -453,7 +443,6 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
struct sk_buff *skb = rx->skb;
u8 pn[CCMP_PN_LEN];
int data_len;
- DECLARE_MAC_BUF(mac);
hdrlen = ieee80211_hdrlen(hdr->frame_control);
@@ -468,7 +457,7 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
(rx->status->flag & RX_FLAG_IV_STRIPPED))
return RX_CONTINUE;
- (void) ccmp_hdr2pn(pn, skb->data + hdrlen);
+ ccmp_hdr2pn(pn, skb->data + hdrlen);
if (memcmp(pn, key->u.ccmp.rx_pn[rx->queue], CCMP_PN_LEN) <= 0) {
key->u.ccmp.replays++;
@@ -483,9 +472,8 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
key->u.ccmp.tfm, key->u.ccmp.rx_crypto_buf,
skb->data + hdrlen + CCMP_HDR_LEN, data_len,
skb->data + skb->len - CCMP_MIC_LEN,
- skb->data + hdrlen + CCMP_HDR_LEN)) {
+ skb->data + hdrlen + CCMP_HDR_LEN))
return RX_DROP_UNUSABLE;
- }
}
memcpy(key->u.ccmp.rx_pn[rx->queue], pn, CCMP_PN_LEN);
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig
index 646c7121dbc0..092ae6faccca 100644
--- a/net/wireless/Kconfig
+++ b/net/wireless/Kconfig
@@ -1,6 +1,15 @@
config CFG80211
tristate "Improved wireless configuration API"
+config CFG80211_REG_DEBUG
+ bool "cfg80211 regulatory debugging"
+ depends on CFG80211
+ default n
+ ---help---
+ You can enable this if you want to debug regulatory changes.
+
+ If unsure, say N.
+
config NL80211
bool "nl80211 new netlink interface support"
depends on CFG80211
@@ -40,6 +49,8 @@ config WIRELESS_OLD_REGULATORY
ieee80211_regdom module parameter. This is being phased out and you
should stop using them ASAP.
+ Note: You will need CRDA if you want 802.11d support
+
Say Y unless you have installed a new userspace application.
Also say Y if have one currently depending on the ieee80211_regdom
module parameter and cannot port it to use the new userspace
@@ -72,3 +83,32 @@ config WIRELESS_EXT_SYSFS
Say Y if you have programs using it, like old versions of
hal.
+
+config LIB80211
+ tristate "Common routines for IEEE802.11 drivers"
+ default n
+ help
+ This options enables a library of common routines used
+ by IEEE802.11 wireless LAN drivers.
+
+ Drivers should select this themselves if needed. Say Y if
+ you want this built into your kernel.
+
+config LIB80211_CRYPT_WEP
+ tristate
+
+config LIB80211_CRYPT_CCMP
+ tristate
+
+config LIB80211_CRYPT_TKIP
+ tristate
+
+config LIB80211_DEBUG
+ bool "lib80211 debugging messages"
+ depends on LIB80211
+ default n
+ ---help---
+ You can enable this if you want verbose debugging messages
+ from lib80211.
+
+ If unsure, say N.
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index b9f943c45f3b..938a334c8dbc 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -1,5 +1,12 @@
obj-$(CONFIG_WIRELESS_EXT) += wext.o
obj-$(CONFIG_CFG80211) += cfg80211.o
+obj-$(CONFIG_LIB80211) += lib80211.o
+obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o
+obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o
+obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o
cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o
+cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o
cfg80211-$(CONFIG_NL80211) += nl80211.o
+
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 5031db7b275b..b96fc0c3f1c4 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -19,7 +19,6 @@
#include "nl80211.h"
#include "core.h"
#include "sysfs.h"
-#include "reg.h"
/* name for sysfs, %d is appended */
#define PHY_NAME "phy"
@@ -236,8 +235,7 @@ struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv)
mutex_unlock(&cfg80211_drv_mutex);
/* give it a proper name */
- snprintf(drv->wiphy.dev.bus_id, BUS_ID_SIZE,
- PHY_NAME "%d", drv->idx);
+ dev_set_name(&drv->wiphy.dev, PHY_NAME "%d", drv->idx);
mutex_init(&drv->mtx);
mutex_init(&drv->devlist_mtx);
@@ -301,12 +299,10 @@ int wiphy_register(struct wiphy *wiphy)
/* check and set up bitrates */
ieee80211_set_bitrate_flags(wiphy);
+ mutex_lock(&cfg80211_drv_mutex);
+
/* set up regulatory info */
- mutex_lock(&cfg80211_reg_mutex);
wiphy_update_regulatory(wiphy, REGDOM_SET_BY_CORE);
- mutex_unlock(&cfg80211_reg_mutex);
-
- mutex_lock(&cfg80211_drv_mutex);
res = device_add(&drv->wiphy.dev);
if (res)
@@ -351,6 +347,10 @@ void wiphy_unregister(struct wiphy *wiphy)
/* unlock again before freeing */
mutex_unlock(&drv->mtx);
+ /* If this device got a regulatory hint tell core its
+ * free to listen now to a new shiny device regulatory hint */
+ reg_device_remove(wiphy);
+
list_del(&drv->list);
device_del(&drv->wiphy.dev);
debugfs_remove(drv->wiphy.debugfsdir);
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 771cc5cc7658..f7fb9f413028 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -11,6 +11,7 @@
#include <net/genetlink.h>
#include <net/wireless.h>
#include <net/cfg80211.h>
+#include "reg.h"
struct cfg80211_registered_device {
struct cfg80211_ops *ops;
@@ -21,6 +22,18 @@ struct cfg80211_registered_device {
* any call is in progress */
struct mutex mtx;
+ /* ISO / IEC 3166 alpha2 for which this device is receiving
+ * country IEs on, this can help disregard country IEs from APs
+ * on the same alpha2 quickly. The alpha2 may differ from
+ * cfg80211_regdomain's alpha2 when an intersection has occurred.
+ * If the AP is reconfigured this can also be used to tell us if
+ * the country on the country IE changed. */
+ char country_ie_alpha2[2];
+
+ /* If a Country IE has been received this tells us the environment
+ * which its telling us its in. This defaults to ENVIRON_ANY */
+ enum environment_cap env;
+
/* wiphy index, internal only */
int idx;
diff --git a/net/wireless/lib80211.c b/net/wireless/lib80211.c
new file mode 100644
index 000000000000..97d411f74507
--- /dev/null
+++ b/net/wireless/lib80211.c
@@ -0,0 +1,284 @@
+/*
+ * lib80211 -- common bits for IEEE802.11 drivers
+ *
+ * Copyright(c) 2008 John W. Linville <linville@tuxdriver.com>
+ *
+ * Portions copied from old ieee80211 component, w/ original copyright
+ * notices below:
+ *
+ * Host AP crypto routines
+ *
+ * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
+ * Portions Copyright (C) 2004, Intel Corporation <jketreno@linux.intel.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/ctype.h>
+#include <linux/ieee80211.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <net/lib80211.h>
+
+#define DRV_NAME "lib80211"
+
+#define DRV_DESCRIPTION "common routines for IEEE802.11 drivers"
+
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_AUTHOR("John W. Linville <linville@tuxdriver.com>");
+MODULE_LICENSE("GPL");
+
+struct lib80211_crypto_alg {
+ struct list_head list;
+ struct lib80211_crypto_ops *ops;
+};
+
+static LIST_HEAD(lib80211_crypto_algs);
+static DEFINE_SPINLOCK(lib80211_crypto_lock);
+
+const char *print_ssid(char *buf, const char *ssid, u8 ssid_len)
+{
+ const char *s = ssid;
+ char *d = buf;
+
+ ssid_len = min_t(u8, ssid_len, IEEE80211_MAX_SSID_LEN);
+ while (ssid_len--) {
+ if (isprint(*s)) {
+ *d++ = *s++;
+ continue;
+ }
+
+ *d++ = '\\';
+ if (*s == '\0')
+ *d++ = '0';
+ else if (*s == '\n')
+ *d++ = 'n';
+ else if (*s == '\r')
+ *d++ = 'r';
+ else if (*s == '\t')
+ *d++ = 't';
+ else if (*s == '\\')
+ *d++ = '\\';
+ else
+ d += snprintf(d, 3, "%03o", *s);
+ s++;
+ }
+ *d = '\0';
+ return buf;
+}
+EXPORT_SYMBOL(print_ssid);
+
+int lib80211_crypt_info_init(struct lib80211_crypt_info *info, char *name,
+ spinlock_t *lock)
+{
+ memset(info, 0, sizeof(*info));
+
+ info->name = name;
+ info->lock = lock;
+
+ INIT_LIST_HEAD(&info->crypt_deinit_list);
+ setup_timer(&info->crypt_deinit_timer, lib80211_crypt_deinit_handler,
+ (unsigned long)info);
+
+ return 0;
+}
+EXPORT_SYMBOL(lib80211_crypt_info_init);
+
+void lib80211_crypt_info_free(struct lib80211_crypt_info *info)
+{
+ int i;
+
+ lib80211_crypt_quiescing(info);
+ del_timer_sync(&info->crypt_deinit_timer);
+ lib80211_crypt_deinit_entries(info, 1);
+
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ struct lib80211_crypt_data *crypt = info->crypt[i];
+ if (crypt) {
+ if (crypt->ops) {
+ crypt->ops->deinit(crypt->priv);
+ module_put(crypt->ops->owner);
+ }
+ kfree(crypt);
+ info->crypt[i] = NULL;
+ }
+ }
+}
+EXPORT_SYMBOL(lib80211_crypt_info_free);
+
+void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info, int force)
+{
+ struct lib80211_crypt_data *entry, *next;
+ unsigned long flags;
+
+ spin_lock_irqsave(info->lock, flags);
+ list_for_each_entry_safe(entry, next, &info->crypt_deinit_list, list) {
+ if (atomic_read(&entry->refcnt) != 0 && !force)
+ continue;
+
+ list_del(&entry->list);
+
+ if (entry->ops) {
+ entry->ops->deinit(entry->priv);
+ module_put(entry->ops->owner);
+ }
+ kfree(entry);
+ }
+ spin_unlock_irqrestore(info->lock, flags);
+}
+EXPORT_SYMBOL(lib80211_crypt_deinit_entries);
+
+/* After this, crypt_deinit_list won't accept new members */
+void lib80211_crypt_quiescing(struct lib80211_crypt_info *info)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(info->lock, flags);
+ info->crypt_quiesced = 1;
+ spin_unlock_irqrestore(info->lock, flags);
+}
+EXPORT_SYMBOL(lib80211_crypt_quiescing);
+
+void lib80211_crypt_deinit_handler(unsigned long data)
+{
+ struct lib80211_crypt_info *info = (struct lib80211_crypt_info *)data;
+ unsigned long flags;
+
+ lib80211_crypt_deinit_entries(info, 0);
+
+ spin_lock_irqsave(info->lock, flags);
+ if (!list_empty(&info->crypt_deinit_list) && !info->crypt_quiesced) {
+ printk(KERN_DEBUG "%s: entries remaining in delayed crypt "
+ "deletion list\n", info->name);
+ info->crypt_deinit_timer.expires = jiffies + HZ;
+ add_timer(&info->crypt_deinit_timer);
+ }
+ spin_unlock_irqrestore(info->lock, flags);
+}
+EXPORT_SYMBOL(lib80211_crypt_deinit_handler);
+
+void lib80211_crypt_delayed_deinit(struct lib80211_crypt_info *info,
+ struct lib80211_crypt_data **crypt)
+{
+ struct lib80211_crypt_data *tmp;
+ unsigned long flags;
+
+ if (*crypt == NULL)
+ return;
+
+ tmp = *crypt;
+ *crypt = NULL;
+
+ /* must not run ops->deinit() while there may be pending encrypt or
+ * decrypt operations. Use a list of delayed deinits to avoid needing
+ * locking. */
+
+ spin_lock_irqsave(info->lock, flags);
+ if (!info->crypt_quiesced) {
+ list_add(&tmp->list, &info->crypt_deinit_list);
+ if (!timer_pending(&info->crypt_deinit_timer)) {
+ info->crypt_deinit_timer.expires = jiffies + HZ;
+ add_timer(&info->crypt_deinit_timer);
+ }
+ }
+ spin_unlock_irqrestore(info->lock, flags);
+}
+EXPORT_SYMBOL(lib80211_crypt_delayed_deinit);
+
+int lib80211_register_crypto_ops(struct lib80211_crypto_ops *ops)
+{
+ unsigned long flags;
+ struct lib80211_crypto_alg *alg;
+
+ alg = kzalloc(sizeof(*alg), GFP_KERNEL);
+ if (alg == NULL)
+ return -ENOMEM;
+
+ alg->ops = ops;
+
+ spin_lock_irqsave(&lib80211_crypto_lock, flags);
+ list_add(&alg->list, &lib80211_crypto_algs);
+ spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
+
+ printk(KERN_DEBUG "lib80211_crypt: registered algorithm '%s'\n",
+ ops->name);
+
+ return 0;
+}
+EXPORT_SYMBOL(lib80211_register_crypto_ops);
+
+int lib80211_unregister_crypto_ops(struct lib80211_crypto_ops *ops)
+{
+ struct lib80211_crypto_alg *alg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lib80211_crypto_lock, flags);
+ list_for_each_entry(alg, &lib80211_crypto_algs, list) {
+ if (alg->ops == ops)
+ goto found;
+ }
+ spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
+ return -EINVAL;
+
+ found:
+ printk(KERN_DEBUG "lib80211_crypt: unregistered algorithm "
+ "'%s'\n", ops->name);
+ list_del(&alg->list);
+ spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
+ kfree(alg);
+ return 0;
+}
+EXPORT_SYMBOL(lib80211_unregister_crypto_ops);
+
+struct lib80211_crypto_ops *lib80211_get_crypto_ops(const char *name)
+{
+ struct lib80211_crypto_alg *alg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lib80211_crypto_lock, flags);
+ list_for_each_entry(alg, &lib80211_crypto_algs, list) {
+ if (strcmp(alg->ops->name, name) == 0)
+ goto found;
+ }
+ spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
+ return NULL;
+
+ found:
+ spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
+ return alg->ops;
+}
+EXPORT_SYMBOL(lib80211_get_crypto_ops);
+
+static void *lib80211_crypt_null_init(int keyidx)
+{
+ return (void *)1;
+}
+
+static void lib80211_crypt_null_deinit(void *priv)
+{
+}
+
+static struct lib80211_crypto_ops lib80211_crypt_null = {
+ .name = "NULL",
+ .init = lib80211_crypt_null_init,
+ .deinit = lib80211_crypt_null_deinit,
+ .owner = THIS_MODULE,
+};
+
+static int __init lib80211_init(void)
+{
+ printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION "\n");
+ return lib80211_register_crypto_ops(&lib80211_crypt_null);
+}
+
+static void __exit lib80211_exit(void)
+{
+ lib80211_unregister_crypto_ops(&lib80211_crypt_null);
+ BUG_ON(!list_empty(&lib80211_crypto_algs));
+}
+
+module_init(lib80211_init);
+module_exit(lib80211_exit);
diff --git a/net/wireless/lib80211_crypt_ccmp.c b/net/wireless/lib80211_crypt_ccmp.c
new file mode 100644
index 000000000000..2301dc1edc4c
--- /dev/null
+++ b/net/wireless/lib80211_crypt_ccmp.c
@@ -0,0 +1,494 @@
+/*
+ * lib80211 crypt: host-based CCMP encryption implementation for lib80211
+ *
+ * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008, 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. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <asm/string.h>
+#include <linux/wireless.h>
+
+#include <linux/ieee80211.h>
+
+#include <linux/crypto.h>
+
+#include <net/lib80211.h>
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("Host AP crypt: CCMP");
+MODULE_LICENSE("GPL");
+
+#define AES_BLOCK_LEN 16
+#define CCMP_HDR_LEN 8
+#define CCMP_MIC_LEN 8
+#define CCMP_TK_LEN 16
+#define CCMP_PN_LEN 6
+
+struct lib80211_ccmp_data {
+ u8 key[CCMP_TK_LEN];
+ int key_set;
+
+ u8 tx_pn[CCMP_PN_LEN];
+ u8 rx_pn[CCMP_PN_LEN];
+
+ u32 dot11RSNAStatsCCMPFormatErrors;
+ u32 dot11RSNAStatsCCMPReplays;
+ u32 dot11RSNAStatsCCMPDecryptErrors;
+
+ int key_idx;
+
+ struct crypto_cipher *tfm;
+
+ /* scratch buffers for virt_to_page() (crypto API) */
+ u8 tx_b0[AES_BLOCK_LEN], tx_b[AES_BLOCK_LEN],
+ tx_e[AES_BLOCK_LEN], tx_s0[AES_BLOCK_LEN];
+ u8 rx_b0[AES_BLOCK_LEN], rx_b[AES_BLOCK_LEN], rx_a[AES_BLOCK_LEN];
+};
+
+static inline void lib80211_ccmp_aes_encrypt(struct crypto_cipher *tfm,
+ const u8 pt[16], u8 ct[16])
+{
+ crypto_cipher_encrypt_one(tfm, ct, pt);
+}
+
+static void *lib80211_ccmp_init(int key_idx)
+{
+ struct lib80211_ccmp_data *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
+ if (priv == NULL)
+ goto fail;
+ priv->key_idx = key_idx;
+
+ priv->tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->tfm)) {
+ printk(KERN_DEBUG "lib80211_crypt_ccmp: could not allocate "
+ "crypto API aes\n");
+ priv->tfm = NULL;
+ goto fail;
+ }
+
+ return priv;
+
+ fail:
+ if (priv) {
+ if (priv->tfm)
+ crypto_free_cipher(priv->tfm);
+ kfree(priv);
+ }
+
+ return NULL;
+}
+
+static void lib80211_ccmp_deinit(void *priv)
+{
+ struct lib80211_ccmp_data *_priv = priv;
+ if (_priv && _priv->tfm)
+ crypto_free_cipher(_priv->tfm);
+ kfree(priv);
+}
+
+static inline void xor_block(u8 * b, u8 * a, size_t len)
+{
+ int i;
+ for (i = 0; i < len; i++)
+ b[i] ^= a[i];
+}
+
+static void ccmp_init_blocks(struct crypto_cipher *tfm,
+ struct ieee80211_hdr *hdr,
+ u8 * pn, size_t dlen, u8 * b0, u8 * auth, u8 * s0)
+{
+ u8 *pos, qc = 0;
+ size_t aad_len;
+ int a4_included, qc_included;
+ u8 aad[2 * AES_BLOCK_LEN];
+
+ a4_included = ieee80211_has_a4(hdr->frame_control);
+ qc_included = ieee80211_is_data_qos(hdr->frame_control);
+
+ aad_len = 22;
+ if (a4_included)
+ aad_len += 6;
+ if (qc_included) {
+ pos = (u8 *) & hdr->addr4;
+ if (a4_included)
+ pos += 6;
+ qc = *pos & 0x0f;
+ aad_len += 2;
+ }
+
+ /* CCM Initial Block:
+ * Flag (Include authentication header, M=3 (8-octet MIC),
+ * L=1 (2-octet Dlen))
+ * Nonce: 0x00 | A2 | PN
+ * Dlen */
+ b0[0] = 0x59;
+ b0[1] = qc;
+ memcpy(b0 + 2, hdr->addr2, ETH_ALEN);
+ memcpy(b0 + 8, pn, CCMP_PN_LEN);
+ b0[14] = (dlen >> 8) & 0xff;
+ b0[15] = dlen & 0xff;
+
+ /* AAD:
+ * FC with bits 4..6 and 11..13 masked to zero; 14 is always one
+ * A1 | A2 | A3
+ * SC with bits 4..15 (seq#) masked to zero
+ * A4 (if present)
+ * QC (if present)
+ */
+ pos = (u8 *) hdr;
+ aad[0] = 0; /* aad_len >> 8 */
+ aad[1] = aad_len & 0xff;
+ aad[2] = pos[0] & 0x8f;
+ aad[3] = pos[1] & 0xc7;
+ memcpy(aad + 4, hdr->addr1, 3 * ETH_ALEN);
+ pos = (u8 *) & hdr->seq_ctrl;
+ aad[22] = pos[0] & 0x0f;
+ aad[23] = 0; /* all bits masked */
+ memset(aad + 24, 0, 8);
+ if (a4_included)
+ memcpy(aad + 24, hdr->addr4, ETH_ALEN);
+ if (qc_included) {
+ aad[a4_included ? 30 : 24] = qc;
+ /* rest of QC masked */
+ }
+
+ /* Start with the first block and AAD */
+ lib80211_ccmp_aes_encrypt(tfm, b0, auth);
+ xor_block(auth, aad, AES_BLOCK_LEN);
+ lib80211_ccmp_aes_encrypt(tfm, auth, auth);
+ xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN);
+ lib80211_ccmp_aes_encrypt(tfm, auth, auth);
+ b0[0] &= 0x07;
+ b0[14] = b0[15] = 0;
+ lib80211_ccmp_aes_encrypt(tfm, b0, s0);
+}
+
+static int lib80211_ccmp_hdr(struct sk_buff *skb, int hdr_len,
+ u8 *aeskey, int keylen, void *priv)
+{
+ struct lib80211_ccmp_data *key = priv;
+ int i;
+ u8 *pos;
+
+ if (skb_headroom(skb) < CCMP_HDR_LEN || skb->len < hdr_len)
+ return -1;
+
+ if (aeskey != NULL && keylen >= CCMP_TK_LEN)
+ memcpy(aeskey, key->key, CCMP_TK_LEN);
+
+ pos = skb_push(skb, CCMP_HDR_LEN);
+ memmove(pos, pos + CCMP_HDR_LEN, hdr_len);
+ pos += hdr_len;
+
+ i = CCMP_PN_LEN - 1;
+ while (i >= 0) {
+ key->tx_pn[i]++;
+ if (key->tx_pn[i] != 0)
+ break;
+ i--;
+ }
+
+ *pos++ = key->tx_pn[5];
+ *pos++ = key->tx_pn[4];
+ *pos++ = 0;
+ *pos++ = (key->key_idx << 6) | (1 << 5) /* Ext IV included */ ;
+ *pos++ = key->tx_pn[3];
+ *pos++ = key->tx_pn[2];
+ *pos++ = key->tx_pn[1];
+ *pos++ = key->tx_pn[0];
+
+ return CCMP_HDR_LEN;
+}
+
+static int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct lib80211_ccmp_data *key = priv;
+ int data_len, i, blocks, last, len;
+ u8 *pos, *mic;
+ struct ieee80211_hdr *hdr;
+ u8 *b0 = key->tx_b0;
+ u8 *b = key->tx_b;
+ u8 *e = key->tx_e;
+ u8 *s0 = key->tx_s0;
+
+ if (skb_tailroom(skb) < CCMP_MIC_LEN || skb->len < hdr_len)
+ return -1;
+
+ data_len = skb->len - hdr_len;
+ len = lib80211_ccmp_hdr(skb, hdr_len, NULL, 0, priv);
+ if (len < 0)
+ return -1;
+
+ pos = skb->data + hdr_len + CCMP_HDR_LEN;
+ mic = skb_put(skb, CCMP_MIC_LEN);
+ hdr = (struct ieee80211_hdr *)skb->data;
+ ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0);
+
+ blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
+ last = data_len % AES_BLOCK_LEN;
+
+ for (i = 1; i <= blocks; i++) {
+ len = (i == blocks && last) ? last : AES_BLOCK_LEN;
+ /* Authentication */
+ xor_block(b, pos, len);
+ lib80211_ccmp_aes_encrypt(key->tfm, b, b);
+ /* Encryption, with counter */
+ b0[14] = (i >> 8) & 0xff;
+ b0[15] = i & 0xff;
+ lib80211_ccmp_aes_encrypt(key->tfm, b0, e);
+ xor_block(pos, e, len);
+ pos += len;
+ }
+
+ for (i = 0; i < CCMP_MIC_LEN; i++)
+ mic[i] = b[i] ^ s0[i];
+
+ return 0;
+}
+
+/*
+ * deal with seq counter wrapping correctly.
+ * refer to timer_after() for jiffies wrapping handling
+ */
+static inline int ccmp_replay_check(u8 *pn_n, u8 *pn_o)
+{
+ u32 iv32_n, iv16_n;
+ u32 iv32_o, iv16_o;
+
+ iv32_n = (pn_n[0] << 24) | (pn_n[1] << 16) | (pn_n[2] << 8) | pn_n[3];
+ iv16_n = (pn_n[4] << 8) | pn_n[5];
+
+ iv32_o = (pn_o[0] << 24) | (pn_o[1] << 16) | (pn_o[2] << 8) | pn_o[3];
+ iv16_o = (pn_o[4] << 8) | pn_o[5];
+
+ if ((s32)iv32_n - (s32)iv32_o < 0 ||
+ (iv32_n == iv32_o && iv16_n <= iv16_o))
+ return 1;
+ return 0;
+}
+
+static int lib80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct lib80211_ccmp_data *key = priv;
+ u8 keyidx, *pos;
+ struct ieee80211_hdr *hdr;
+ u8 *b0 = key->rx_b0;
+ u8 *b = key->rx_b;
+ u8 *a = key->rx_a;
+ u8 pn[6];
+ int i, blocks, last, len;
+ size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN - CCMP_MIC_LEN;
+ u8 *mic = skb->data + skb->len - CCMP_MIC_LEN;
+
+ if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) {
+ key->dot11RSNAStatsCCMPFormatErrors++;
+ return -1;
+ }
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+ pos = skb->data + hdr_len;
+ keyidx = pos[3];
+ if (!(keyidx & (1 << 5))) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "CCMP: received packet without ExtIV"
+ " flag from %pM\n", hdr->addr2);
+ }
+ key->dot11RSNAStatsCCMPFormatErrors++;
+ return -2;
+ }
+ keyidx >>= 6;
+ if (key->key_idx != keyidx) {
+ printk(KERN_DEBUG "CCMP: RX tkey->key_idx=%d frame "
+ "keyidx=%d priv=%p\n", key->key_idx, keyidx, priv);
+ return -6;
+ }
+ if (!key->key_set) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "CCMP: received packet from %pM"
+ " with keyid=%d that does not have a configured"
+ " key\n", hdr->addr2, keyidx);
+ }
+ return -3;
+ }
+
+ pn[0] = pos[7];
+ pn[1] = pos[6];
+ pn[2] = pos[5];
+ pn[3] = pos[4];
+ pn[4] = pos[1];
+ pn[5] = pos[0];
+ pos += 8;
+
+ if (ccmp_replay_check(pn, key->rx_pn)) {
+#ifdef CONFIG_LIB80211_DEBUG
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "CCMP: replay detected: STA=%pM "
+ "previous PN %02x%02x%02x%02x%02x%02x "
+ "received PN %02x%02x%02x%02x%02x%02x\n",
+ hdr->addr2,
+ key->rx_pn[0], key->rx_pn[1], key->rx_pn[2],
+ key->rx_pn[3], key->rx_pn[4], key->rx_pn[5],
+ pn[0], pn[1], pn[2], pn[3], pn[4], pn[5]);
+ }
+#endif
+ key->dot11RSNAStatsCCMPReplays++;
+ return -4;
+ }
+
+ ccmp_init_blocks(key->tfm, hdr, pn, data_len, b0, a, b);
+ xor_block(mic, b, CCMP_MIC_LEN);
+
+ blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
+ last = data_len % AES_BLOCK_LEN;
+
+ for (i = 1; i <= blocks; i++) {
+ len = (i == blocks && last) ? last : AES_BLOCK_LEN;
+ /* Decrypt, with counter */
+ b0[14] = (i >> 8) & 0xff;
+ b0[15] = i & 0xff;
+ lib80211_ccmp_aes_encrypt(key->tfm, b0, b);
+ xor_block(pos, b, len);
+ /* Authentication */
+ xor_block(a, pos, len);
+ lib80211_ccmp_aes_encrypt(key->tfm, a, a);
+ pos += len;
+ }
+
+ if (memcmp(mic, a, CCMP_MIC_LEN) != 0) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "CCMP: decrypt failed: STA="
+ "%pM\n", hdr->addr2);
+ }
+ key->dot11RSNAStatsCCMPDecryptErrors++;
+ return -5;
+ }
+
+ memcpy(key->rx_pn, pn, CCMP_PN_LEN);
+
+ /* Remove hdr and MIC */
+ memmove(skb->data + CCMP_HDR_LEN, skb->data, hdr_len);
+ skb_pull(skb, CCMP_HDR_LEN);
+ skb_trim(skb, skb->len - CCMP_MIC_LEN);
+
+ return keyidx;
+}
+
+static int lib80211_ccmp_set_key(void *key, int len, u8 * seq, void *priv)
+{
+ struct lib80211_ccmp_data *data = priv;
+ int keyidx;
+ struct crypto_cipher *tfm = data->tfm;
+
+ keyidx = data->key_idx;
+ memset(data, 0, sizeof(*data));
+ data->key_idx = keyidx;
+ data->tfm = tfm;
+ if (len == CCMP_TK_LEN) {
+ memcpy(data->key, key, CCMP_TK_LEN);
+ data->key_set = 1;
+ if (seq) {
+ data->rx_pn[0] = seq[5];
+ data->rx_pn[1] = seq[4];
+ data->rx_pn[2] = seq[3];
+ data->rx_pn[3] = seq[2];
+ data->rx_pn[4] = seq[1];
+ data->rx_pn[5] = seq[0];
+ }
+ crypto_cipher_setkey(data->tfm, data->key, CCMP_TK_LEN);
+ } else if (len == 0)
+ data->key_set = 0;
+ else
+ return -1;
+
+ return 0;
+}
+
+static int lib80211_ccmp_get_key(void *key, int len, u8 * seq, void *priv)
+{
+ struct lib80211_ccmp_data *data = priv;
+
+ if (len < CCMP_TK_LEN)
+ return -1;
+
+ if (!data->key_set)
+ return 0;
+ memcpy(key, data->key, CCMP_TK_LEN);
+
+ if (seq) {
+ seq[0] = data->tx_pn[5];
+ seq[1] = data->tx_pn[4];
+ seq[2] = data->tx_pn[3];
+ seq[3] = data->tx_pn[2];
+ seq[4] = data->tx_pn[1];
+ seq[5] = data->tx_pn[0];
+ }
+
+ return CCMP_TK_LEN;
+}
+
+static char *lib80211_ccmp_print_stats(char *p, void *priv)
+{
+ struct lib80211_ccmp_data *ccmp = priv;
+
+ p += sprintf(p, "key[%d] alg=CCMP key_set=%d "
+ "tx_pn=%02x%02x%02x%02x%02x%02x "
+ "rx_pn=%02x%02x%02x%02x%02x%02x "
+ "format_errors=%d replays=%d decrypt_errors=%d\n",
+ ccmp->key_idx, ccmp->key_set,
+ ccmp->tx_pn[0], ccmp->tx_pn[1], ccmp->tx_pn[2],
+ ccmp->tx_pn[3], ccmp->tx_pn[4], ccmp->tx_pn[5],
+ ccmp->rx_pn[0], ccmp->rx_pn[1], ccmp->rx_pn[2],
+ ccmp->rx_pn[3], ccmp->rx_pn[4], ccmp->rx_pn[5],
+ ccmp->dot11RSNAStatsCCMPFormatErrors,
+ ccmp->dot11RSNAStatsCCMPReplays,
+ ccmp->dot11RSNAStatsCCMPDecryptErrors);
+
+ return p;
+}
+
+static struct lib80211_crypto_ops lib80211_crypt_ccmp = {
+ .name = "CCMP",
+ .init = lib80211_ccmp_init,
+ .deinit = lib80211_ccmp_deinit,
+ .build_iv = lib80211_ccmp_hdr,
+ .encrypt_mpdu = lib80211_ccmp_encrypt,
+ .decrypt_mpdu = lib80211_ccmp_decrypt,
+ .encrypt_msdu = NULL,
+ .decrypt_msdu = NULL,
+ .set_key = lib80211_ccmp_set_key,
+ .get_key = lib80211_ccmp_get_key,
+ .print_stats = lib80211_ccmp_print_stats,
+ .extra_mpdu_prefix_len = CCMP_HDR_LEN,
+ .extra_mpdu_postfix_len = CCMP_MIC_LEN,
+ .owner = THIS_MODULE,
+};
+
+static int __init lib80211_crypto_ccmp_init(void)
+{
+ return lib80211_register_crypto_ops(&lib80211_crypt_ccmp);
+}
+
+static void __exit lib80211_crypto_ccmp_exit(void)
+{
+ lib80211_unregister_crypto_ops(&lib80211_crypt_ccmp);
+}
+
+module_init(lib80211_crypto_ccmp_init);
+module_exit(lib80211_crypto_ccmp_exit);
diff --git a/net/wireless/lib80211_crypt_tkip.c b/net/wireless/lib80211_crypt_tkip.c
new file mode 100644
index 000000000000..c36287399d7e
--- /dev/null
+++ b/net/wireless/lib80211_crypt_tkip.c
@@ -0,0 +1,788 @@
+/*
+ * lib80211 crypt: host-based TKIP encryption implementation for lib80211
+ *
+ * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008, 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. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/mm.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <asm/string.h>
+
+#include <linux/wireless.h>
+#include <linux/ieee80211.h>
+#include <net/iw_handler.h>
+
+#include <linux/crypto.h>
+#include <linux/crc32.h>
+
+#include <net/lib80211.h>
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("lib80211 crypt: TKIP");
+MODULE_LICENSE("GPL");
+
+struct lib80211_tkip_data {
+#define TKIP_KEY_LEN 32
+ u8 key[TKIP_KEY_LEN];
+ int key_set;
+
+ u32 tx_iv32;
+ u16 tx_iv16;
+ u16 tx_ttak[5];
+ int tx_phase1_done;
+
+ u32 rx_iv32;
+ u16 rx_iv16;
+ u16 rx_ttak[5];
+ int rx_phase1_done;
+ u32 rx_iv32_new;
+ u16 rx_iv16_new;
+
+ u32 dot11RSNAStatsTKIPReplays;
+ u32 dot11RSNAStatsTKIPICVErrors;
+ u32 dot11RSNAStatsTKIPLocalMICFailures;
+
+ int key_idx;
+
+ struct crypto_blkcipher *rx_tfm_arc4;
+ struct crypto_hash *rx_tfm_michael;
+ struct crypto_blkcipher *tx_tfm_arc4;
+ struct crypto_hash *tx_tfm_michael;
+
+ /* scratch buffers for virt_to_page() (crypto API) */
+ u8 rx_hdr[16], tx_hdr[16];
+
+ unsigned long flags;
+};
+
+static unsigned long lib80211_tkip_set_flags(unsigned long flags, void *priv)
+{
+ struct lib80211_tkip_data *_priv = priv;
+ unsigned long old_flags = _priv->flags;
+ _priv->flags = flags;
+ return old_flags;
+}
+
+static unsigned long lib80211_tkip_get_flags(void *priv)
+{
+ struct lib80211_tkip_data *_priv = priv;
+ return _priv->flags;
+}
+
+static void *lib80211_tkip_init(int key_idx)
+{
+ struct lib80211_tkip_data *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
+ if (priv == NULL)
+ goto fail;
+
+ priv->key_idx = key_idx;
+
+ priv->tx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->tx_tfm_arc4)) {
+ printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate "
+ "crypto API arc4\n");
+ priv->tx_tfm_arc4 = NULL;
+ goto fail;
+ }
+
+ priv->tx_tfm_michael = crypto_alloc_hash("michael_mic", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->tx_tfm_michael)) {
+ printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate "
+ "crypto API michael_mic\n");
+ priv->tx_tfm_michael = NULL;
+ goto fail;
+ }
+
+ priv->rx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->rx_tfm_arc4)) {
+ printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate "
+ "crypto API arc4\n");
+ priv->rx_tfm_arc4 = NULL;
+ goto fail;
+ }
+
+ priv->rx_tfm_michael = crypto_alloc_hash("michael_mic", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->rx_tfm_michael)) {
+ printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate "
+ "crypto API michael_mic\n");
+ priv->rx_tfm_michael = NULL;
+ goto fail;
+ }
+
+ return priv;
+
+ fail:
+ if (priv) {
+ if (priv->tx_tfm_michael)
+ crypto_free_hash(priv->tx_tfm_michael);
+ if (priv->tx_tfm_arc4)
+ crypto_free_blkcipher(priv->tx_tfm_arc4);
+ if (priv->rx_tfm_michael)
+ crypto_free_hash(priv->rx_tfm_michael);
+ if (priv->rx_tfm_arc4)
+ crypto_free_blkcipher(priv->rx_tfm_arc4);
+ kfree(priv);
+ }
+
+ return NULL;
+}
+
+static void lib80211_tkip_deinit(void *priv)
+{
+ struct lib80211_tkip_data *_priv = priv;
+ if (_priv) {
+ if (_priv->tx_tfm_michael)
+ crypto_free_hash(_priv->tx_tfm_michael);
+ if (_priv->tx_tfm_arc4)
+ crypto_free_blkcipher(_priv->tx_tfm_arc4);
+ if (_priv->rx_tfm_michael)
+ crypto_free_hash(_priv->rx_tfm_michael);
+ if (_priv->rx_tfm_arc4)
+ crypto_free_blkcipher(_priv->rx_tfm_arc4);
+ }
+ kfree(priv);
+}
+
+static inline u16 RotR1(u16 val)
+{
+ return (val >> 1) | (val << 15);
+}
+
+static inline u8 Lo8(u16 val)
+{
+ return val & 0xff;
+}
+
+static inline u8 Hi8(u16 val)
+{
+ return val >> 8;
+}
+
+static inline u16 Lo16(u32 val)
+{
+ return val & 0xffff;
+}
+
+static inline u16 Hi16(u32 val)
+{
+ return val >> 16;
+}
+
+static inline u16 Mk16(u8 hi, u8 lo)
+{
+ return lo | (((u16) hi) << 8);
+}
+
+static inline u16 Mk16_le(__le16 * v)
+{
+ return le16_to_cpu(*v);
+}
+
+static const u16 Sbox[256] = {
+ 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
+ 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
+ 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
+ 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
+ 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
+ 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
+ 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
+ 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
+ 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
+ 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
+ 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
+ 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
+ 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
+ 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
+ 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
+ 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
+ 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
+ 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
+ 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
+ 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
+ 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
+ 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
+ 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
+ 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
+ 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
+ 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
+ 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
+ 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
+ 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
+ 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
+ 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
+ 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
+};
+
+static inline u16 _S_(u16 v)
+{
+ u16 t = Sbox[Hi8(v)];
+ return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8));
+}
+
+#define PHASE1_LOOP_COUNT 8
+
+static void tkip_mixing_phase1(u16 * TTAK, const u8 * TK, const u8 * TA,
+ u32 IV32)
+{
+ int i, j;
+
+ /* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */
+ TTAK[0] = Lo16(IV32);
+ TTAK[1] = Hi16(IV32);
+ TTAK[2] = Mk16(TA[1], TA[0]);
+ TTAK[3] = Mk16(TA[3], TA[2]);
+ TTAK[4] = Mk16(TA[5], TA[4]);
+
+ for (i = 0; i < PHASE1_LOOP_COUNT; i++) {
+ j = 2 * (i & 1);
+ TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j]));
+ TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j]));
+ TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j]));
+ TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j]));
+ TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i;
+ }
+}
+
+static void tkip_mixing_phase2(u8 * WEPSeed, const u8 * TK, const u16 * TTAK,
+ u16 IV16)
+{
+ /* Make temporary area overlap WEP seed so that the final copy can be
+ * avoided on little endian hosts. */
+ u16 *PPK = (u16 *) & WEPSeed[4];
+
+ /* Step 1 - make copy of TTAK and bring in TSC */
+ PPK[0] = TTAK[0];
+ PPK[1] = TTAK[1];
+ PPK[2] = TTAK[2];
+ PPK[3] = TTAK[3];
+ PPK[4] = TTAK[4];
+ PPK[5] = TTAK[4] + IV16;
+
+ /* Step 2 - 96-bit bijective mixing using S-box */
+ PPK[0] += _S_(PPK[5] ^ Mk16_le((__le16 *) & TK[0]));
+ PPK[1] += _S_(PPK[0] ^ Mk16_le((__le16 *) & TK[2]));
+ PPK[2] += _S_(PPK[1] ^ Mk16_le((__le16 *) & TK[4]));
+ PPK[3] += _S_(PPK[2] ^ Mk16_le((__le16 *) & TK[6]));
+ PPK[4] += _S_(PPK[3] ^ Mk16_le((__le16 *) & TK[8]));
+ PPK[5] += _S_(PPK[4] ^ Mk16_le((__le16 *) & TK[10]));
+
+ PPK[0] += RotR1(PPK[5] ^ Mk16_le((__le16 *) & TK[12]));
+ PPK[1] += RotR1(PPK[0] ^ Mk16_le((__le16 *) & TK[14]));
+ PPK[2] += RotR1(PPK[1]);
+ PPK[3] += RotR1(PPK[2]);
+ PPK[4] += RotR1(PPK[3]);
+ PPK[5] += RotR1(PPK[4]);
+
+ /* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value
+ * WEPSeed[0..2] is transmitted as WEP IV */
+ WEPSeed[0] = Hi8(IV16);
+ WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F;
+ WEPSeed[2] = Lo8(IV16);
+ WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((__le16 *) & TK[0])) >> 1);
+
+#ifdef __BIG_ENDIAN
+ {
+ int i;
+ for (i = 0; i < 6; i++)
+ PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8);
+ }
+#endif
+}
+
+static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len,
+ u8 * rc4key, int keylen, void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+ int len;
+ u8 *pos;
+ struct ieee80211_hdr *hdr;
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+
+ if (skb_headroom(skb) < 8 || skb->len < hdr_len)
+ return -1;
+
+ if (rc4key == NULL || keylen < 16)
+ return -1;
+
+ if (!tkey->tx_phase1_done) {
+ tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2,
+ tkey->tx_iv32);
+ tkey->tx_phase1_done = 1;
+ }
+ tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16);
+
+ len = skb->len - hdr_len;
+ pos = skb_push(skb, 8);
+ memmove(pos, pos + 8, hdr_len);
+ pos += hdr_len;
+
+ *pos++ = *rc4key;
+ *pos++ = *(rc4key + 1);
+ *pos++ = *(rc4key + 2);
+ *pos++ = (tkey->key_idx << 6) | (1 << 5) /* Ext IV included */ ;
+ *pos++ = tkey->tx_iv32 & 0xff;
+ *pos++ = (tkey->tx_iv32 >> 8) & 0xff;
+ *pos++ = (tkey->tx_iv32 >> 16) & 0xff;
+ *pos++ = (tkey->tx_iv32 >> 24) & 0xff;
+
+ tkey->tx_iv16++;
+ if (tkey->tx_iv16 == 0) {
+ tkey->tx_phase1_done = 0;
+ tkey->tx_iv32++;
+ }
+
+ return 8;
+}
+
+static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+ struct blkcipher_desc desc = { .tfm = tkey->tx_tfm_arc4 };
+ int len;
+ u8 rc4key[16], *pos, *icv;
+ u32 crc;
+ struct scatterlist sg;
+
+ if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) {
+ if (net_ratelimit()) {
+ struct ieee80211_hdr *hdr =
+ (struct ieee80211_hdr *)skb->data;
+ printk(KERN_DEBUG ": TKIP countermeasures: dropped "
+ "TX packet to %pM\n", hdr->addr1);
+ }
+ return -1;
+ }
+
+ if (skb_tailroom(skb) < 4 || skb->len < hdr_len)
+ return -1;
+
+ len = skb->len - hdr_len;
+ pos = skb->data + hdr_len;
+
+ if ((lib80211_tkip_hdr(skb, hdr_len, rc4key, 16, priv)) < 0)
+ return -1;
+
+ icv = skb_put(skb, 4);
+
+ crc = ~crc32_le(~0, pos, len);
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+
+ crypto_blkcipher_setkey(tkey->tx_tfm_arc4, rc4key, 16);
+ sg_init_one(&sg, pos, len + 4);
+ return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4);
+}
+
+/*
+ * deal with seq counter wrapping correctly.
+ * refer to timer_after() for jiffies wrapping handling
+ */
+static inline int tkip_replay_check(u32 iv32_n, u16 iv16_n,
+ u32 iv32_o, u16 iv16_o)
+{
+ if ((s32)iv32_n - (s32)iv32_o < 0 ||
+ (iv32_n == iv32_o && iv16_n <= iv16_o))
+ return 1;
+ return 0;
+}
+
+static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+ struct blkcipher_desc desc = { .tfm = tkey->rx_tfm_arc4 };
+ u8 rc4key[16];
+ u8 keyidx, *pos;
+ u32 iv32;
+ u16 iv16;
+ struct ieee80211_hdr *hdr;
+ u8 icv[4];
+ u32 crc;
+ struct scatterlist sg;
+ int plen;
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+
+ if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG ": TKIP countermeasures: dropped "
+ "received packet from %pM\n", hdr->addr2);
+ }
+ return -1;
+ }
+
+ if (skb->len < hdr_len + 8 + 4)
+ return -1;
+
+ pos = skb->data + hdr_len;
+ keyidx = pos[3];
+ if (!(keyidx & (1 << 5))) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "TKIP: received packet without ExtIV"
+ " flag from %pM\n", hdr->addr2);
+ }
+ return -2;
+ }
+ keyidx >>= 6;
+ if (tkey->key_idx != keyidx) {
+ printk(KERN_DEBUG "TKIP: RX tkey->key_idx=%d frame "
+ "keyidx=%d priv=%p\n", tkey->key_idx, keyidx, priv);
+ return -6;
+ }
+ if (!tkey->key_set) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "TKIP: received packet from %pM"
+ " with keyid=%d that does not have a configured"
+ " key\n", hdr->addr2, keyidx);
+ }
+ return -3;
+ }
+ iv16 = (pos[0] << 8) | pos[2];
+ iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24);
+ pos += 8;
+
+ if (tkip_replay_check(iv32, iv16, tkey->rx_iv32, tkey->rx_iv16)) {
+#ifdef CONFIG_LIB80211_DEBUG
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "TKIP: replay detected: STA=%pM"
+ " previous TSC %08x%04x received TSC "
+ "%08x%04x\n", hdr->addr2,
+ tkey->rx_iv32, tkey->rx_iv16, iv32, iv16);
+ }
+#endif
+ tkey->dot11RSNAStatsTKIPReplays++;
+ return -4;
+ }
+
+ if (iv32 != tkey->rx_iv32 || !tkey->rx_phase1_done) {
+ tkip_mixing_phase1(tkey->rx_ttak, tkey->key, hdr->addr2, iv32);
+ tkey->rx_phase1_done = 1;
+ }
+ tkip_mixing_phase2(rc4key, tkey->key, tkey->rx_ttak, iv16);
+
+ plen = skb->len - hdr_len - 12;
+
+ crypto_blkcipher_setkey(tkey->rx_tfm_arc4, rc4key, 16);
+ sg_init_one(&sg, pos, plen + 4);
+ if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4)) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG ": TKIP: failed to decrypt "
+ "received packet from %pM\n",
+ hdr->addr2);
+ }
+ return -7;
+ }
+
+ crc = ~crc32_le(~0, pos, plen);
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+ if (memcmp(icv, pos + plen, 4) != 0) {
+ if (iv32 != tkey->rx_iv32) {
+ /* Previously cached Phase1 result was already lost, so
+ * it needs to be recalculated for the next packet. */
+ tkey->rx_phase1_done = 0;
+ }
+#ifdef CONFIG_LIB80211_DEBUG
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "TKIP: ICV error detected: STA="
+ "%pM\n", hdr->addr2);
+ }
+#endif
+ tkey->dot11RSNAStatsTKIPICVErrors++;
+ return -5;
+ }
+
+ /* Update real counters only after Michael MIC verification has
+ * completed */
+ tkey->rx_iv32_new = iv32;
+ tkey->rx_iv16_new = iv16;
+
+ /* Remove IV and ICV */
+ memmove(skb->data + 8, skb->data, hdr_len);
+ skb_pull(skb, 8);
+ skb_trim(skb, skb->len - 4);
+
+ return keyidx;
+}
+
+static int michael_mic(struct crypto_hash *tfm_michael, u8 * key, u8 * hdr,
+ u8 * data, size_t data_len, u8 * mic)
+{
+ struct hash_desc desc;
+ struct scatterlist sg[2];
+
+ if (tfm_michael == NULL) {
+ printk(KERN_WARNING "michael_mic: tfm_michael == NULL\n");
+ return -1;
+ }
+ sg_init_table(sg, 2);
+ sg_set_buf(&sg[0], hdr, 16);
+ sg_set_buf(&sg[1], data, data_len);
+
+ if (crypto_hash_setkey(tfm_michael, key, 8))
+ return -1;
+
+ desc.tfm = tfm_michael;
+ desc.flags = 0;
+ return crypto_hash_digest(&desc, sg, data_len + 16, mic);
+}
+
+static void michael_mic_hdr(struct sk_buff *skb, u8 * hdr)
+{
+ struct ieee80211_hdr *hdr11;
+
+ hdr11 = (struct ieee80211_hdr *)skb->data;
+
+ switch (le16_to_cpu(hdr11->frame_control) &
+ (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
+ case IEEE80211_FCTL_TODS:
+ memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
+ memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
+ break;
+ case IEEE80211_FCTL_FROMDS:
+ memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
+ memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */
+ break;
+ case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS:
+ memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
+ memcpy(hdr + ETH_ALEN, hdr11->addr4, ETH_ALEN); /* SA */
+ break;
+ case 0:
+ memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
+ memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
+ break;
+ }
+
+ if (ieee80211_is_data_qos(hdr11->frame_control)) {
+ hdr[12] = le16_to_cpu(*ieee80211_get_qos_ctl(hdr11))
+ & IEEE80211_QOS_CTL_TID_MASK;
+ } else
+ hdr[12] = 0; /* priority */
+
+ hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */
+}
+
+static int lib80211_michael_mic_add(struct sk_buff *skb, int hdr_len,
+ void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+ u8 *pos;
+
+ if (skb_tailroom(skb) < 8 || skb->len < hdr_len) {
+ printk(KERN_DEBUG "Invalid packet for Michael MIC add "
+ "(tailroom=%d hdr_len=%d skb->len=%d)\n",
+ skb_tailroom(skb), hdr_len, skb->len);
+ return -1;
+ }
+
+ michael_mic_hdr(skb, tkey->tx_hdr);
+ pos = skb_put(skb, 8);
+ if (michael_mic(tkey->tx_tfm_michael, &tkey->key[16], tkey->tx_hdr,
+ skb->data + hdr_len, skb->len - 8 - hdr_len, pos))
+ return -1;
+
+ return 0;
+}
+
+static void lib80211_michael_mic_failure(struct net_device *dev,
+ struct ieee80211_hdr *hdr,
+ int keyidx)
+{
+ union iwreq_data wrqu;
+ struct iw_michaelmicfailure ev;
+
+ /* TODO: needed parameters: count, keyid, key type, TSC */
+ memset(&ev, 0, sizeof(ev));
+ ev.flags = keyidx & IW_MICFAILURE_KEY_ID;
+ if (hdr->addr1[0] & 0x01)
+ ev.flags |= IW_MICFAILURE_GROUP;
+ else
+ ev.flags |= IW_MICFAILURE_PAIRWISE;
+ ev.src_addr.sa_family = ARPHRD_ETHER;
+ memcpy(ev.src_addr.sa_data, hdr->addr2, ETH_ALEN);
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.data.length = sizeof(ev);
+ wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, (char *)&ev);
+}
+
+static int lib80211_michael_mic_verify(struct sk_buff *skb, int keyidx,
+ int hdr_len, void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+ u8 mic[8];
+
+ if (!tkey->key_set)
+ return -1;
+
+ michael_mic_hdr(skb, tkey->rx_hdr);
+ if (michael_mic(tkey->rx_tfm_michael, &tkey->key[24], tkey->rx_hdr,
+ skb->data + hdr_len, skb->len - 8 - hdr_len, mic))
+ return -1;
+ if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) {
+ struct ieee80211_hdr *hdr;
+ hdr = (struct ieee80211_hdr *)skb->data;
+ printk(KERN_DEBUG "%s: Michael MIC verification failed for "
+ "MSDU from %pM keyidx=%d\n",
+ skb->dev ? skb->dev->name : "N/A", hdr->addr2,
+ keyidx);
+ if (skb->dev)
+ lib80211_michael_mic_failure(skb->dev, hdr, keyidx);
+ tkey->dot11RSNAStatsTKIPLocalMICFailures++;
+ return -1;
+ }
+
+ /* Update TSC counters for RX now that the packet verification has
+ * completed. */
+ tkey->rx_iv32 = tkey->rx_iv32_new;
+ tkey->rx_iv16 = tkey->rx_iv16_new;
+
+ skb_trim(skb, skb->len - 8);
+
+ return 0;
+}
+
+static int lib80211_tkip_set_key(void *key, int len, u8 * seq, void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+ int keyidx;
+ struct crypto_hash *tfm = tkey->tx_tfm_michael;
+ struct crypto_blkcipher *tfm2 = tkey->tx_tfm_arc4;
+ struct crypto_hash *tfm3 = tkey->rx_tfm_michael;
+ struct crypto_blkcipher *tfm4 = tkey->rx_tfm_arc4;
+
+ keyidx = tkey->key_idx;
+ memset(tkey, 0, sizeof(*tkey));
+ tkey->key_idx = keyidx;
+ tkey->tx_tfm_michael = tfm;
+ tkey->tx_tfm_arc4 = tfm2;
+ tkey->rx_tfm_michael = tfm3;
+ tkey->rx_tfm_arc4 = tfm4;
+ if (len == TKIP_KEY_LEN) {
+ memcpy(tkey->key, key, TKIP_KEY_LEN);
+ tkey->key_set = 1;
+ tkey->tx_iv16 = 1; /* TSC is initialized to 1 */
+ if (seq) {
+ tkey->rx_iv32 = (seq[5] << 24) | (seq[4] << 16) |
+ (seq[3] << 8) | seq[2];
+ tkey->rx_iv16 = (seq[1] << 8) | seq[0];
+ }
+ } else if (len == 0)
+ tkey->key_set = 0;
+ else
+ return -1;
+
+ return 0;
+}
+
+static int lib80211_tkip_get_key(void *key, int len, u8 * seq, void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+
+ if (len < TKIP_KEY_LEN)
+ return -1;
+
+ if (!tkey->key_set)
+ return 0;
+ memcpy(key, tkey->key, TKIP_KEY_LEN);
+
+ if (seq) {
+ /* Return the sequence number of the last transmitted frame. */
+ u16 iv16 = tkey->tx_iv16;
+ u32 iv32 = tkey->tx_iv32;
+ if (iv16 == 0)
+ iv32--;
+ iv16--;
+ seq[0] = tkey->tx_iv16;
+ seq[1] = tkey->tx_iv16 >> 8;
+ seq[2] = tkey->tx_iv32;
+ seq[3] = tkey->tx_iv32 >> 8;
+ seq[4] = tkey->tx_iv32 >> 16;
+ seq[5] = tkey->tx_iv32 >> 24;
+ }
+
+ return TKIP_KEY_LEN;
+}
+
+static char *lib80211_tkip_print_stats(char *p, void *priv)
+{
+ struct lib80211_tkip_data *tkip = priv;
+ p += sprintf(p, "key[%d] alg=TKIP key_set=%d "
+ "tx_pn=%02x%02x%02x%02x%02x%02x "
+ "rx_pn=%02x%02x%02x%02x%02x%02x "
+ "replays=%d icv_errors=%d local_mic_failures=%d\n",
+ tkip->key_idx, tkip->key_set,
+ (tkip->tx_iv32 >> 24) & 0xff,
+ (tkip->tx_iv32 >> 16) & 0xff,
+ (tkip->tx_iv32 >> 8) & 0xff,
+ tkip->tx_iv32 & 0xff,
+ (tkip->tx_iv16 >> 8) & 0xff,
+ tkip->tx_iv16 & 0xff,
+ (tkip->rx_iv32 >> 24) & 0xff,
+ (tkip->rx_iv32 >> 16) & 0xff,
+ (tkip->rx_iv32 >> 8) & 0xff,
+ tkip->rx_iv32 & 0xff,
+ (tkip->rx_iv16 >> 8) & 0xff,
+ tkip->rx_iv16 & 0xff,
+ tkip->dot11RSNAStatsTKIPReplays,
+ tkip->dot11RSNAStatsTKIPICVErrors,
+ tkip->dot11RSNAStatsTKIPLocalMICFailures);
+ return p;
+}
+
+static struct lib80211_crypto_ops lib80211_crypt_tkip = {
+ .name = "TKIP",
+ .init = lib80211_tkip_init,
+ .deinit = lib80211_tkip_deinit,
+ .build_iv = lib80211_tkip_hdr,
+ .encrypt_mpdu = lib80211_tkip_encrypt,
+ .decrypt_mpdu = lib80211_tkip_decrypt,
+ .encrypt_msdu = lib80211_michael_mic_add,
+ .decrypt_msdu = lib80211_michael_mic_verify,
+ .set_key = lib80211_tkip_set_key,
+ .get_key = lib80211_tkip_get_key,
+ .print_stats = lib80211_tkip_print_stats,
+ .extra_mpdu_prefix_len = 4 + 4, /* IV + ExtIV */
+ .extra_mpdu_postfix_len = 4, /* ICV */
+ .extra_msdu_postfix_len = 8, /* MIC */
+ .get_flags = lib80211_tkip_get_flags,
+ .set_flags = lib80211_tkip_set_flags,
+ .owner = THIS_MODULE,
+};
+
+static int __init lib80211_crypto_tkip_init(void)
+{
+ return lib80211_register_crypto_ops(&lib80211_crypt_tkip);
+}
+
+static void __exit lib80211_crypto_tkip_exit(void)
+{
+ lib80211_unregister_crypto_ops(&lib80211_crypt_tkip);
+}
+
+module_init(lib80211_crypto_tkip_init);
+module_exit(lib80211_crypto_tkip_exit);
diff --git a/net/wireless/lib80211_crypt_wep.c b/net/wireless/lib80211_crypt_wep.c
new file mode 100644
index 000000000000..6d41e05ca33b
--- /dev/null
+++ b/net/wireless/lib80211_crypt_wep.c
@@ -0,0 +1,296 @@
+/*
+ * lib80211 crypt: host-based WEP encryption implementation for lib80211
+ *
+ * Copyright (c) 2002-2004, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008, 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. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+#include <linux/skbuff.h>
+#include <linux/mm.h>
+#include <asm/string.h>
+
+#include <net/lib80211.h>
+
+#include <linux/crypto.h>
+#include <linux/crc32.h>
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("lib80211 crypt: WEP");
+MODULE_LICENSE("GPL");
+
+struct lib80211_wep_data {
+ u32 iv;
+#define WEP_KEY_LEN 13
+ u8 key[WEP_KEY_LEN + 1];
+ u8 key_len;
+ u8 key_idx;
+ struct crypto_blkcipher *tx_tfm;
+ struct crypto_blkcipher *rx_tfm;
+};
+
+static void *lib80211_wep_init(int keyidx)
+{
+ struct lib80211_wep_data *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
+ if (priv == NULL)
+ goto fail;
+ priv->key_idx = keyidx;
+
+ priv->tx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->tx_tfm)) {
+ printk(KERN_DEBUG "lib80211_crypt_wep: could not allocate "
+ "crypto API arc4\n");
+ priv->tx_tfm = NULL;
+ goto fail;
+ }
+
+ priv->rx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->rx_tfm)) {
+ printk(KERN_DEBUG "lib80211_crypt_wep: could not allocate "
+ "crypto API arc4\n");
+ priv->rx_tfm = NULL;
+ goto fail;
+ }
+ /* start WEP IV from a random value */
+ get_random_bytes(&priv->iv, 4);
+
+ return priv;
+
+ fail:
+ if (priv) {
+ if (priv->tx_tfm)
+ crypto_free_blkcipher(priv->tx_tfm);
+ if (priv->rx_tfm)
+ crypto_free_blkcipher(priv->rx_tfm);
+ kfree(priv);
+ }
+ return NULL;
+}
+
+static void lib80211_wep_deinit(void *priv)
+{
+ struct lib80211_wep_data *_priv = priv;
+ if (_priv) {
+ if (_priv->tx_tfm)
+ crypto_free_blkcipher(_priv->tx_tfm);
+ if (_priv->rx_tfm)
+ crypto_free_blkcipher(_priv->rx_tfm);
+ }
+ kfree(priv);
+}
+
+/* Add WEP IV/key info to a frame that has at least 4 bytes of headroom */
+static int lib80211_wep_build_iv(struct sk_buff *skb, int hdr_len,
+ u8 *key, int keylen, void *priv)
+{
+ struct lib80211_wep_data *wep = priv;
+ u32 klen, len;
+ u8 *pos;
+
+ if (skb_headroom(skb) < 4 || skb->len < hdr_len)
+ return -1;
+
+ len = skb->len - hdr_len;
+ pos = skb_push(skb, 4);
+ memmove(pos, pos + 4, hdr_len);
+ pos += hdr_len;
+
+ klen = 3 + wep->key_len;
+
+ wep->iv++;
+
+ /* Fluhrer, Mantin, and Shamir have reported weaknesses in the key
+ * scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N)
+ * can be used to speedup attacks, so avoid using them. */
+ if ((wep->iv & 0xff00) == 0xff00) {
+ u8 B = (wep->iv >> 16) & 0xff;
+ if (B >= 3 && B < klen)
+ wep->iv += 0x0100;
+ }
+
+ /* Prepend 24-bit IV to RC4 key and TX frame */
+ *pos++ = (wep->iv >> 16) & 0xff;
+ *pos++ = (wep->iv >> 8) & 0xff;
+ *pos++ = wep->iv & 0xff;
+ *pos++ = wep->key_idx << 6;
+
+ return 0;
+}
+
+/* Perform WEP encryption on given skb that has at least 4 bytes of headroom
+ * for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted,
+ * so the payload length increases with 8 bytes.
+ *
+ * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data))
+ */
+static int lib80211_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct lib80211_wep_data *wep = priv;
+ struct blkcipher_desc desc = { .tfm = wep->tx_tfm };
+ u32 crc, klen, len;
+ u8 *pos, *icv;
+ struct scatterlist sg;
+ u8 key[WEP_KEY_LEN + 3];
+
+ /* other checks are in lib80211_wep_build_iv */
+ if (skb_tailroom(skb) < 4)
+ return -1;
+
+ /* add the IV to the frame */
+ if (lib80211_wep_build_iv(skb, hdr_len, NULL, 0, priv))
+ return -1;
+
+ /* Copy the IV into the first 3 bytes of the key */
+ skb_copy_from_linear_data_offset(skb, hdr_len, key, 3);
+
+ /* Copy rest of the WEP key (the secret part) */
+ memcpy(key + 3, wep->key, wep->key_len);
+
+ len = skb->len - hdr_len - 4;
+ pos = skb->data + hdr_len + 4;
+ klen = 3 + wep->key_len;
+
+ /* Append little-endian CRC32 over only the data and encrypt it to produce ICV */
+ crc = ~crc32_le(~0, pos, len);
+ icv = skb_put(skb, 4);
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+
+ crypto_blkcipher_setkey(wep->tx_tfm, key, klen);
+ sg_init_one(&sg, pos, len + 4);
+ return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4);
+}
+
+/* Perform WEP decryption on given buffer. Buffer includes whole WEP part of
+ * the frame: IV (4 bytes), encrypted payload (including SNAP header),
+ * ICV (4 bytes). len includes both IV and ICV.
+ *
+ * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on
+ * failure. If frame is OK, IV and ICV will be removed.
+ */
+static int lib80211_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct lib80211_wep_data *wep = priv;
+ struct blkcipher_desc desc = { .tfm = wep->rx_tfm };
+ u32 crc, klen, plen;
+ u8 key[WEP_KEY_LEN + 3];
+ u8 keyidx, *pos, icv[4];
+ struct scatterlist sg;
+
+ if (skb->len < hdr_len + 8)
+ return -1;
+
+ pos = skb->data + hdr_len;
+ key[0] = *pos++;
+ key[1] = *pos++;
+ key[2] = *pos++;
+ keyidx = *pos++ >> 6;
+ if (keyidx != wep->key_idx)
+ return -1;
+
+ klen = 3 + wep->key_len;
+
+ /* Copy rest of the WEP key (the secret part) */
+ memcpy(key + 3, wep->key, wep->key_len);
+
+ /* Apply RC4 to data and compute CRC32 over decrypted data */
+ plen = skb->len - hdr_len - 8;
+
+ crypto_blkcipher_setkey(wep->rx_tfm, key, klen);
+ sg_init_one(&sg, pos, plen + 4);
+ if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4))
+ return -7;
+
+ crc = ~crc32_le(~0, pos, plen);
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+ if (memcmp(icv, pos + plen, 4) != 0) {
+ /* ICV mismatch - drop frame */
+ return -2;
+ }
+
+ /* Remove IV and ICV */
+ memmove(skb->data + 4, skb->data, hdr_len);
+ skb_pull(skb, 4);
+ skb_trim(skb, skb->len - 4);
+
+ return 0;
+}
+
+static int lib80211_wep_set_key(void *key, int len, u8 * seq, void *priv)
+{
+ struct lib80211_wep_data *wep = priv;
+
+ if (len < 0 || len > WEP_KEY_LEN)
+ return -1;
+
+ memcpy(wep->key, key, len);
+ wep->key_len = len;
+
+ return 0;
+}
+
+static int lib80211_wep_get_key(void *key, int len, u8 * seq, void *priv)
+{
+ struct lib80211_wep_data *wep = priv;
+
+ if (len < wep->key_len)
+ return -1;
+
+ memcpy(key, wep->key, wep->key_len);
+
+ return wep->key_len;
+}
+
+static char *lib80211_wep_print_stats(char *p, void *priv)
+{
+ struct lib80211_wep_data *wep = priv;
+ p += sprintf(p, "key[%d] alg=WEP len=%d\n", wep->key_idx, wep->key_len);
+ return p;
+}
+
+static struct lib80211_crypto_ops lib80211_crypt_wep = {
+ .name = "WEP",
+ .init = lib80211_wep_init,
+ .deinit = lib80211_wep_deinit,
+ .build_iv = lib80211_wep_build_iv,
+ .encrypt_mpdu = lib80211_wep_encrypt,
+ .decrypt_mpdu = lib80211_wep_decrypt,
+ .encrypt_msdu = NULL,
+ .decrypt_msdu = NULL,
+ .set_key = lib80211_wep_set_key,
+ .get_key = lib80211_wep_get_key,
+ .print_stats = lib80211_wep_print_stats,
+ .extra_mpdu_prefix_len = 4, /* IV */
+ .extra_mpdu_postfix_len = 4, /* ICV */
+ .owner = THIS_MODULE,
+};
+
+static int __init lib80211_crypto_wep_init(void)
+{
+ return lib80211_register_crypto_ops(&lib80211_crypt_wep);
+}
+
+static void __exit lib80211_crypto_wep_exit(void)
+{
+ lib80211_unregister_crypto_ops(&lib80211_crypt_wep);
+}
+
+module_init(lib80211_crypto_wep_init);
+module_exit(lib80211_crypto_wep_exit);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 572793c8c7ab..31b807af3235 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -58,6 +58,9 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
.len = BUS_ID_SIZE-1 },
+ [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
+ [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
+ [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
[NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
[NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
@@ -84,7 +87,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
.len = NL80211_MAX_SUPP_RATES },
[NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
[NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
- [NL80211_ATTR_MNTR_FLAGS] = { .type = NLA_NESTED },
+ [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
[NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_MESH_ID_LEN },
[NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
@@ -95,6 +98,10 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
[NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
[NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
[NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
+ [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
+ .len = NL80211_MAX_SUPP_RATES },
+
+ [NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED },
[NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY,
.len = NL80211_HT_CAPABILITY_LEN },
@@ -157,6 +164,19 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
if (!nl_band)
goto nla_put_failure;
+ /* add HT info */
+ if (dev->wiphy.bands[band]->ht_cap.ht_supported) {
+ NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET,
+ sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
+ &dev->wiphy.bands[band]->ht_cap.mcs);
+ NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA,
+ dev->wiphy.bands[band]->ht_cap.cap);
+ NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
+ dev->wiphy.bands[band]->ht_cap.ampdu_factor);
+ NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
+ dev->wiphy.bands[band]->ht_cap.ampdu_density);
+ }
+
/* add frequencies */
nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
if (!nl_freqs)
@@ -180,6 +200,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
if (chan->flags & IEEE80211_CHAN_RADAR)
NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
+ NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
+ DBM_TO_MBM(chan->max_power));
+
nla_nest_end(msg, nl_freq);
}
@@ -269,20 +292,142 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
return -ENOBUFS;
}
+static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
+ [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
+ [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
+ [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
+ [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
+ [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
+};
+
+static int parse_txq_params(struct nlattr *tb[],
+ struct ieee80211_txq_params *txq_params)
+{
+ if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
+ !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
+ !tb[NL80211_TXQ_ATTR_AIFS])
+ return -EINVAL;
+
+ txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
+ txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
+ txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
+ txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
+ txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
+
+ return 0;
+}
+
static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev;
- int result;
-
- if (!info->attrs[NL80211_ATTR_WIPHY_NAME])
- return -EINVAL;
+ int result = 0, rem_txq_params = 0;
+ struct nlattr *nl_txq_params;
rdev = cfg80211_get_dev_from_info(info);
if (IS_ERR(rdev))
return PTR_ERR(rdev);
- result = cfg80211_dev_rename(rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
+ if (info->attrs[NL80211_ATTR_WIPHY_NAME]) {
+ result = cfg80211_dev_rename(
+ rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
+ if (result)
+ goto bad_res;
+ }
+
+ if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
+ struct ieee80211_txq_params txq_params;
+ struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
+
+ if (!rdev->ops->set_txq_params) {
+ result = -EOPNOTSUPP;
+ goto bad_res;
+ }
+
+ nla_for_each_nested(nl_txq_params,
+ info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
+ rem_txq_params) {
+ nla_parse(tb, NL80211_TXQ_ATTR_MAX,
+ nla_data(nl_txq_params),
+ nla_len(nl_txq_params),
+ txq_params_policy);
+ result = parse_txq_params(tb, &txq_params);
+ if (result)
+ goto bad_res;
+
+ result = rdev->ops->set_txq_params(&rdev->wiphy,
+ &txq_params);
+ if (result)
+ goto bad_res;
+ }
+ }
+
+ if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+ enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
+ struct ieee80211_channel *chan;
+ struct ieee80211_sta_ht_cap *ht_cap;
+ u32 freq, sec_freq;
+
+ if (!rdev->ops->set_channel) {
+ result = -EOPNOTSUPP;
+ goto bad_res;
+ }
+
+ result = -EINVAL;
+
+ if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+ channel_type = nla_get_u32(info->attrs[
+ NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+ if (channel_type != NL80211_CHAN_NO_HT &&
+ channel_type != NL80211_CHAN_HT20 &&
+ channel_type != NL80211_CHAN_HT40PLUS &&
+ channel_type != NL80211_CHAN_HT40MINUS)
+ goto bad_res;
+ }
+
+ freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
+ chan = ieee80211_get_channel(&rdev->wiphy, freq);
+
+ /* Primary channel not allowed */
+ if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
+ goto bad_res;
+
+ if (channel_type == NL80211_CHAN_HT40MINUS)
+ sec_freq = freq - 20;
+ else if (channel_type == NL80211_CHAN_HT40PLUS)
+ sec_freq = freq + 20;
+ else
+ sec_freq = 0;
+
+ ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
+
+ /* no HT capabilities */
+ if (channel_type != NL80211_CHAN_NO_HT &&
+ !ht_cap->ht_supported)
+ goto bad_res;
+
+ if (sec_freq) {
+ struct ieee80211_channel *schan;
+
+ /* no 40 MHz capabilities */
+ if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
+ (ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
+ goto bad_res;
+ schan = ieee80211_get_channel(&rdev->wiphy, sec_freq);
+
+ /* Secondary channel not allowed */
+ if (!schan || schan->flags & IEEE80211_CHAN_DISABLED)
+ goto bad_res;
+ }
+
+ result = rdev->ops->set_channel(&rdev->wiphy, chan,
+ channel_type);
+ if (result)
+ goto bad_res;
+ }
+
+
+ bad_res:
cfg80211_put_dev(rdev);
return result;
}
@@ -945,12 +1090,46 @@ static int parse_station_flags(struct nlattr *nla, u32 *staflags)
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)
{
void *hdr;
- struct nlattr *sinfoattr;
+ struct nlattr *sinfoattr, *txrate;
+ u16 bitrate;
hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
if (!hdr)
@@ -980,7 +1159,29 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
if (sinfo->filled & STATION_INFO_PLINK_STATE)
NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
sinfo->plink_state);
+ if (sinfo->filled & STATION_INFO_SIGNAL)
+ NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
+ sinfo->signal);
+ if (sinfo->filled & STATION_INFO_TX_BITRATE) {
+ txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE);
+ if (!txrate)
+ goto nla_put_failure;
+
+ /* nl80211_calculate_bitrate will return 0 for mcs >= 32 */
+ bitrate = nl80211_calculate_bitrate(&sinfo->txrate);
+ if (bitrate > 0)
+ NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
+ if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS)
+ NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS,
+ sinfo->txrate.mcs);
+ if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH)
+ NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH);
+ if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI)
+ NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI);
+
+ nla_nest_end(msg, txrate);
+ }
nla_nest_end(msg, sinfoattr);
return genlmsg_end(msg, hdr);
@@ -1598,6 +1799,12 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
params.use_short_slot_time =
nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
+ if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
+ params.basic_rates =
+ nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
+ params.basic_rates_len =
+ nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
+ }
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
@@ -1680,11 +1887,199 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
#endif
mutex_lock(&cfg80211_drv_mutex);
- r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, NULL);
+ r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, 0, ENVIRON_ANY);
mutex_unlock(&cfg80211_drv_mutex);
return r;
}
+static int nl80211_get_mesh_params(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *drv;
+ struct mesh_config cur_params;
+ int err;
+ struct net_device *dev;
+ void *hdr;
+ struct nlattr *pinfoattr;
+ struct sk_buff *msg;
+
+ /* Look up our device */
+ err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+ if (err)
+ return err;
+
+ if (!drv->ops->get_mesh_params) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ /* Get the mesh params */
+ rtnl_lock();
+ err = drv->ops->get_mesh_params(&drv->wiphy, dev, &cur_params);
+ rtnl_unlock();
+ if (err)
+ goto out;
+
+ /* Draw up a netlink message to send back */
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg) {
+ err = -ENOBUFS;
+ goto out;
+ }
+ hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
+ NL80211_CMD_GET_MESH_PARAMS);
+ if (!hdr)
+ goto nla_put_failure;
+ pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS);
+ if (!pinfoattr)
+ goto nla_put_failure;
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+ NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
+ cur_params.dot11MeshRetryTimeout);
+ NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
+ cur_params.dot11MeshConfirmTimeout);
+ NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
+ cur_params.dot11MeshHoldingTimeout);
+ NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
+ cur_params.dot11MeshMaxPeerLinks);
+ NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES,
+ cur_params.dot11MeshMaxRetries);
+ NLA_PUT_U8(msg, NL80211_MESHCONF_TTL,
+ cur_params.dot11MeshTTL);
+ NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
+ cur_params.auto_open_plinks);
+ NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
+ cur_params.dot11MeshHWMPmaxPREQretries);
+ NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
+ cur_params.path_refresh_time);
+ NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
+ cur_params.min_discovery_timeout);
+ NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
+ cur_params.dot11MeshHWMPactivePathTimeout);
+ NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
+ cur_params.dot11MeshHWMPpreqMinInterval);
+ NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
+ cur_params.dot11MeshHWMPnetDiameterTraversalTime);
+ nla_nest_end(msg, pinfoattr);
+ genlmsg_end(msg, hdr);
+ err = genlmsg_unicast(msg, info->snd_pid);
+ goto out;
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ err = -EMSGSIZE;
+out:
+ /* Cleanup */
+ cfg80211_put_dev(drv);
+ dev_put(dev);
+ return err;
+}
+
+#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
+do {\
+ if (table[attr_num]) {\
+ cfg.param = nla_fn(table[attr_num]); \
+ mask |= (1 << (attr_num - 1)); \
+ } \
+} while (0);\
+
+static struct nla_policy
+nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] __read_mostly = {
+ [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
+ [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
+ [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
+
+ [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
+ [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
+ [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
+ [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
+};
+
+static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
+{
+ int err;
+ u32 mask;
+ struct cfg80211_registered_device *drv;
+ struct net_device *dev;
+ struct mesh_config cfg;
+ struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
+ struct nlattr *parent_attr;
+
+ parent_attr = info->attrs[NL80211_ATTR_MESH_PARAMS];
+ if (!parent_attr)
+ return -EINVAL;
+ if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
+ parent_attr, nl80211_meshconf_params_policy))
+ return -EINVAL;
+
+ err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+ if (err)
+ return err;
+
+ if (!drv->ops->set_mesh_params) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ /* This makes sure that there aren't more than 32 mesh config
+ * parameters (otherwise our bitfield scheme would not work.) */
+ BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
+
+ /* Fill in the params struct */
+ mask = 0;
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
+ mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
+ mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
+ mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
+ mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
+ mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
+ mask, NL80211_MESHCONF_TTL, nla_get_u8);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
+ mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
+ mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
+ nla_get_u8);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
+ mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
+ mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
+ nla_get_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
+ mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
+ nla_get_u32);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
+ mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
+ nla_get_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
+ dot11MeshHWMPnetDiameterTraversalTime,
+ mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
+ nla_get_u16);
+
+ /* Apply changes */
+ rtnl_lock();
+ err = drv->ops->set_mesh_params(&drv->wiphy, dev, &cfg, mask);
+ rtnl_unlock();
+
+ out:
+ /* cleanup */
+ cfg80211_put_dev(drv);
+ dev_put(dev);
+ return err;
+}
+
+#undef FILL_IN_MESH_PARAM_IF_SET
+
static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
@@ -1743,12 +2138,9 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
mutex_lock(&cfg80211_drv_mutex);
r = set_regdom(rd);
mutex_unlock(&cfg80211_drv_mutex);
- if (r)
- goto bad_reg;
-
return r;
-bad_reg:
+ bad_reg:
kfree(rd);
return -EINVAL;
}
@@ -1902,6 +2294,18 @@ static struct genl_ops nl80211_ops[] = {
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
},
+ {
+ .cmd = NL80211_CMD_GET_MESH_PARAMS,
+ .doit = nl80211_get_mesh_params,
+ .policy = nl80211_policy,
+ /* can be retrieved by unprivileged users */
+ },
+ {
+ .cmd = NL80211_CMD_SET_MESH_PARAMS,
+ .doit = nl80211_set_mesh_params,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
};
/* multicast groups */
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index eb3b1a9f9b12..22713aa4ea40 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -42,17 +42,40 @@
#include "core.h"
#include "reg.h"
-/* wiphy is set if this request's initiator is REGDOM_SET_BY_DRIVER */
+/**
+ * struct regulatory_request - receipt of last regulatory request
+ *
+ * @wiphy: this is set if this request's initiator is
+ * %REGDOM_SET_BY_COUNTRY_IE or %REGDOM_SET_BY_DRIVER. This
+ * can be used by the wireless core to deal with conflicts
+ * and potentially inform users of which devices specifically
+ * cased the conflicts.
+ * @initiator: indicates who sent this request, could be any of
+ * of those set in reg_set_by, %REGDOM_SET_BY_*
+ * @alpha2: the ISO / IEC 3166 alpha2 country code of the requested
+ * regulatory domain. We have a few special codes:
+ * 00 - World regulatory domain
+ * 99 - built by driver but a specific alpha2 cannot be determined
+ * 98 - result of an intersection between two regulatory domains
+ * @intersect: indicates whether the wireless core should intersect
+ * the requested regulatory domain with the presently set regulatory
+ * domain.
+ * @country_ie_checksum: checksum of the last processed and accepted
+ * country IE
+ * @country_ie_env: lets us know if the AP is telling us we are outdoor,
+ * indoor, or if it doesn't matter
+ */
struct regulatory_request {
- struct list_head list;
struct wiphy *wiphy;
- int granted;
enum reg_set_by initiator;
char alpha2[2];
+ bool intersect;
+ u32 country_ie_checksum;
+ enum environment_cap country_ie_env;
};
-static LIST_HEAD(regulatory_requests);
-DEFINE_MUTEX(cfg80211_reg_mutex);
+/* Receipt of information from last regulatory request */
+static struct regulatory_request *last_request;
/* To trigger userspace events */
static struct platform_device *reg_pdev;
@@ -63,13 +86,16 @@ static u32 supported_bandwidths[] = {
MHZ_TO_KHZ(20),
};
-static struct list_head regulatory_requests;
-
/* Central wireless core regulatory domains, we only need two,
* the current one and a world regulatory domain in case we have no
* information to give us an alpha2 */
static const struct ieee80211_regdomain *cfg80211_regdomain;
+/* We use this as a place for the rd structure built from the
+ * last parsed country IE to rest until CRDA gets back to us with
+ * what it thinks should apply for the same country */
+static const struct ieee80211_regdomain *country_ie_regdomain;
+
/* We keep a static world regulatory domain in case of the absence of CRDA */
static const struct ieee80211_regdomain world_regdom = {
.n_reg_rules = 1,
@@ -85,7 +111,7 @@ static const struct ieee80211_regdomain *cfg80211_world_regdom =
&world_regdom;
#ifdef CONFIG_WIRELESS_OLD_REGULATORY
-static char *ieee80211_regdom = "US";
+static char *ieee80211_regdom = "";
module_param(ieee80211_regdom, charp, 0444);
MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
@@ -164,8 +190,14 @@ static const struct ieee80211_regdomain *static_regdom(char *alpha2)
return &jp_regdom;
if (alpha2[0] == 'E' && alpha2[1] == 'U')
return &eu_regdom;
+
+#if 1
+ /* PPH Default to europe now... */
+ return &eu_regdom;
+#else
/* Default, as per the old rules */
return &us_regdom;
+#endif
}
static bool is_old_static_regdom(const struct ieee80211_regdomain *rd)
@@ -204,7 +236,7 @@ static void reset_regdomains(void)
* core upon initialization */
static void update_world_regdomain(const struct ieee80211_regdomain *rd)
{
- BUG_ON(list_empty(&regulatory_requests));
+ BUG_ON(!last_request);
reset_regdomains();
@@ -249,6 +281,18 @@ static bool is_unknown_alpha2(const char *alpha2)
return false;
}
+static bool is_intersected_alpha2(const char *alpha2)
+{
+ if (!alpha2)
+ return false;
+ /* Special case where regulatory domain is the
+ * result of an intersection between two regulatory domain
+ * structures */
+ if (alpha2[0] == '9' && alpha2[1] == '8')
+ return true;
+ return false;
+}
+
static bool is_an_alpha2(const char *alpha2)
{
if (!alpha2)
@@ -277,6 +321,25 @@ static bool regdom_changed(const char *alpha2)
return true;
}
+/**
+ * country_ie_integrity_changes - tells us if the country IE has changed
+ * @checksum: checksum of country IE of fields we are interested in
+ *
+ * If the country IE has not changed you can ignore it safely. This is
+ * useful to determine if two devices are seeing two different country IEs
+ * even on the same alpha2. Note that this will return false if no IE has
+ * been set on the wireless core yet.
+ */
+static bool country_ie_integrity_changes(u32 checksum)
+{
+ /* If no IE has been set then the checksum doesn't change */
+ if (unlikely(!last_request->country_ie_checksum))
+ return false;
+ if (unlikely(last_request->country_ie_checksum != checksum))
+ return true;
+ return false;
+}
+
/* This lets us keep regulatory code which is updated on a regulatory
* basis in userspace. */
static int call_crda(const char *alpha2)
@@ -300,121 +363,13 @@ static int call_crda(const char *alpha2)
return kobject_uevent_env(&reg_pdev->dev.kobj, KOBJ_CHANGE, envp);
}
-/* This has the logic which determines when a new request
- * should be ignored. */
-static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by,
- char *alpha2, struct ieee80211_regdomain *rd)
-{
- struct regulatory_request *last_request = NULL;
-
- /* All initial requests are respected */
- if (list_empty(&regulatory_requests))
- return 0;
-
- last_request = list_first_entry(&regulatory_requests,
- struct regulatory_request, list);
-
- switch (set_by) {
- case REGDOM_SET_BY_INIT:
- return -EINVAL;
- case REGDOM_SET_BY_CORE:
- /* Always respect new wireless core hints, should only
- * come in for updating the world regulatory domain at init
- * anyway */
- return 0;
- case REGDOM_SET_BY_COUNTRY_IE:
- if (last_request->initiator == set_by) {
- if (last_request->wiphy != wiphy) {
- /* Two cards with two APs claiming different
- * different Country IE alpha2s!
- * You're special!! */
- if (!alpha2_equal(last_request->alpha2,
- cfg80211_regdomain->alpha2)) {
- /* XXX: Deal with conflict, consider
- * building a new one out of the
- * intersection */
- WARN_ON(1);
- return -EOPNOTSUPP;
- }
- return -EALREADY;
- }
- /* Two consecutive Country IE hints on the same wiphy */
- if (!alpha2_equal(cfg80211_regdomain->alpha2, alpha2))
- return 0;
- return -EALREADY;
- }
- if (WARN(!is_alpha2_set(alpha2) || !is_an_alpha2(alpha2),
- "Invalid Country IE regulatory hint passed "
- "to the wireless core\n"))
- return -EINVAL;
- /* We ignore Country IE hints for now, as we haven't yet
- * added the dot11MultiDomainCapabilityEnabled flag
- * for wiphys */
- return 1;
- case REGDOM_SET_BY_DRIVER:
- BUG_ON(!wiphy);
- if (last_request->initiator == set_by) {
- /* Two separate drivers hinting different things,
- * this is possible if you have two devices present
- * on a system with different EEPROM regulatory
- * readings. XXX: Do intersection, we support only
- * the first regulatory hint for now */
- if (last_request->wiphy != wiphy)
- return -EALREADY;
- if (rd)
- return -EALREADY;
- /* Driver should not be trying to hint different
- * regulatory domains! */
- BUG_ON(!alpha2_equal(alpha2,
- cfg80211_regdomain->alpha2));
- return -EALREADY;
- }
- if (last_request->initiator == REGDOM_SET_BY_CORE)
- return 0;
- /* XXX: Handle intersection, and add the
- * dot11MultiDomainCapabilityEnabled flag to wiphy. For now
- * we assume the driver has this set to false, following the
- * 802.11d dot11MultiDomainCapabilityEnabled documentation */
- if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE)
- return 0;
- return 0;
- case REGDOM_SET_BY_USER:
- if (last_request->initiator == set_by ||
- last_request->initiator == REGDOM_SET_BY_CORE)
- return 0;
- /* Drivers can use their wiphy's reg_notifier()
- * to override any information */
- if (last_request->initiator == REGDOM_SET_BY_DRIVER)
- return 0;
- /* XXX: Handle intersection */
- if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE)
- return -EOPNOTSUPP;
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-static bool __reg_is_valid_request(const char *alpha2,
- struct regulatory_request **request)
-{
- struct regulatory_request *req;
- if (list_empty(&regulatory_requests))
- return false;
- list_for_each_entry(req, &regulatory_requests, list) {
- if (alpha2_equal(req->alpha2, alpha2)) {
- *request = req;
- return true;
- }
- }
- return false;
-}
-
/* Used by nl80211 before kmalloc'ing our regulatory domain */
bool reg_is_valid_request(const char *alpha2)
{
- struct regulatory_request *request = NULL;
- return __reg_is_valid_request(alpha2, &request);
+ if (!last_request)
+ return false;
+
+ return alpha2_equal(last_request->alpha2, alpha2);
}
/* Sanity check on a regulatory rule */
@@ -423,7 +378,7 @@ static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule)
const struct ieee80211_freq_range *freq_range = &rule->freq_range;
u32 freq_diff;
- if (freq_range->start_freq_khz == 0 || freq_range->end_freq_khz == 0)
+ if (freq_range->start_freq_khz <= 0 || freq_range->end_freq_khz <= 0)
return false;
if (freq_range->start_freq_khz > freq_range->end_freq_khz)
@@ -431,7 +386,8 @@ static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule)
freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;
- if (freq_range->max_bandwidth_khz > freq_diff)
+ if (freq_range->end_freq_khz <= freq_range->start_freq_khz ||
+ freq_range->max_bandwidth_khz > freq_diff)
return false;
return true;
@@ -445,6 +401,9 @@ static bool is_valid_rd(const struct ieee80211_regdomain *rd)
if (!rd->n_reg_rules)
return false;
+ if (WARN_ON(rd->n_reg_rules > NL80211_MAX_SUPP_REG_RULES))
+ return false;
+
for (i = 0; i < rd->n_reg_rules; i++) {
reg_rule = &rd->reg_rules[i];
if (!is_valid_reg_rule(reg_rule))
@@ -469,6 +428,361 @@ static u32 freq_max_bandwidth(const struct ieee80211_freq_range *freq_range,
return 0;
}
+/**
+ * freq_in_rule_band - tells us if a frequency is in a frequency band
+ * @freq_range: frequency rule we want to query
+ * @freq_khz: frequency we are inquiring about
+ *
+ * This lets us know if a specific frequency rule is or is not relevant to
+ * a specific frequency's band. Bands are device specific and artificial
+ * definitions (the "2.4 GHz band" and the "5 GHz band"), however it is
+ * safe for now to assume that a frequency rule should not be part of a
+ * frequency's band if the start freq or end freq are off by more than 2 GHz.
+ * This resolution can be lowered and should be considered as we add
+ * regulatory rule support for other "bands".
+ **/
+static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
+ u32 freq_khz)
+{
+#define ONE_GHZ_IN_KHZ 1000000
+ if (abs(freq_khz - freq_range->start_freq_khz) <= (2 * ONE_GHZ_IN_KHZ))
+ return true;
+ if (abs(freq_khz - freq_range->end_freq_khz) <= (2 * ONE_GHZ_IN_KHZ))
+ return true;
+ return false;
+#undef ONE_GHZ_IN_KHZ
+}
+
+/* Converts a country IE to a regulatory domain. A regulatory domain
+ * structure has a lot of information which the IE doesn't yet have,
+ * so for the other values we use upper max values as we will intersect
+ * with our userspace regulatory agent to get lower bounds. */
+static struct ieee80211_regdomain *country_ie_2_rd(
+ u8 *country_ie,
+ u8 country_ie_len,
+ u32 *checksum)
+{
+ struct ieee80211_regdomain *rd = NULL;
+ unsigned int i = 0;
+ char alpha2[2];
+ u32 flags = 0;
+ u32 num_rules = 0, size_of_regd = 0;
+ u8 *triplets_start = NULL;
+ u8 len_at_triplet = 0;
+ /* the last channel we have registered in a subband (triplet) */
+ int last_sub_max_channel = 0;
+
+ *checksum = 0xDEADBEEF;
+
+ /* Country IE requirements */
+ BUG_ON(country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN ||
+ country_ie_len & 0x01);
+
+ alpha2[0] = country_ie[0];
+ alpha2[1] = country_ie[1];
+
+ /*
+ * Third octet can be:
+ * 'I' - Indoor
+ * 'O' - Outdoor
+ *
+ * anything else we assume is no restrictions
+ */
+ if (country_ie[2] == 'I')
+ flags = NL80211_RRF_NO_OUTDOOR;
+ else if (country_ie[2] == 'O')
+ flags = NL80211_RRF_NO_INDOOR;
+
+ country_ie += 3;
+ country_ie_len -= 3;
+
+ triplets_start = country_ie;
+ len_at_triplet = country_ie_len;
+
+ *checksum ^= ((flags ^ alpha2[0] ^ alpha2[1]) << 8);
+
+ /* We need to build a reg rule for each triplet, but first we must
+ * calculate the number of reg rules we will need. We will need one
+ * for each channel subband */
+ while (country_ie_len >= 3) {
+ int end_channel = 0;
+ struct ieee80211_country_ie_triplet *triplet =
+ (struct ieee80211_country_ie_triplet *) country_ie;
+ int cur_sub_max_channel = 0, cur_channel = 0;
+
+ if (triplet->ext.reg_extension_id >=
+ IEEE80211_COUNTRY_EXTENSION_ID) {
+ country_ie += 3;
+ country_ie_len -= 3;
+ continue;
+ }
+
+ /* 2 GHz */
+ if (triplet->chans.first_channel <= 14)
+ end_channel = triplet->chans.first_channel +
+ triplet->chans.num_channels;
+ else
+ /*
+ * 5 GHz -- For example in country IEs if the first
+ * channel given is 36 and the number of channels is 4
+ * then the individual channel numbers defined for the
+ * 5 GHz PHY by these parameters are: 36, 40, 44, and 48
+ * and not 36, 37, 38, 39.
+ *
+ * See: http://tinyurl.com/11d-clarification
+ */
+ end_channel = triplet->chans.first_channel +
+ (4 * (triplet->chans.num_channels - 1));
+
+ cur_channel = triplet->chans.first_channel;
+ cur_sub_max_channel = end_channel;
+
+ /* Basic sanity check */
+ if (cur_sub_max_channel < cur_channel)
+ return NULL;
+
+ /* Do not allow overlapping channels. Also channels
+ * passed in each subband must be monotonically
+ * increasing */
+ if (last_sub_max_channel) {
+ if (cur_channel <= last_sub_max_channel)
+ return NULL;
+ if (cur_sub_max_channel <= last_sub_max_channel)
+ return NULL;
+ }
+
+ /* When dot11RegulatoryClassesRequired is supported
+ * we can throw ext triplets as part of this soup,
+ * for now we don't care when those change as we
+ * don't support them */
+ *checksum ^= ((cur_channel ^ cur_sub_max_channel) << 8) |
+ ((cur_sub_max_channel ^ cur_sub_max_channel) << 16) |
+ ((triplet->chans.max_power ^ cur_sub_max_channel) << 24);
+
+ last_sub_max_channel = cur_sub_max_channel;
+
+ country_ie += 3;
+ country_ie_len -= 3;
+ num_rules++;
+
+ /* Note: this is not a IEEE requirement but
+ * simply a memory requirement */
+ if (num_rules > NL80211_MAX_SUPP_REG_RULES)
+ return NULL;
+ }
+
+ country_ie = triplets_start;
+ country_ie_len = len_at_triplet;
+
+ size_of_regd = sizeof(struct ieee80211_regdomain) +
+ (num_rules * sizeof(struct ieee80211_reg_rule));
+
+ rd = kzalloc(size_of_regd, GFP_KERNEL);
+ if (!rd)
+ return NULL;
+
+ rd->n_reg_rules = num_rules;
+ rd->alpha2[0] = alpha2[0];
+ rd->alpha2[1] = alpha2[1];
+
+ /* This time around we fill in the rd */
+ while (country_ie_len >= 3) {
+ int end_channel = 0;
+ struct ieee80211_country_ie_triplet *triplet =
+ (struct ieee80211_country_ie_triplet *) country_ie;
+ struct ieee80211_reg_rule *reg_rule = NULL;
+ struct ieee80211_freq_range *freq_range = NULL;
+ struct ieee80211_power_rule *power_rule = NULL;
+
+ /* Must parse if dot11RegulatoryClassesRequired is true,
+ * we don't support this yet */
+ if (triplet->ext.reg_extension_id >=
+ IEEE80211_COUNTRY_EXTENSION_ID) {
+ country_ie += 3;
+ country_ie_len -= 3;
+ continue;
+ }
+
+ reg_rule = &rd->reg_rules[i];
+ freq_range = &reg_rule->freq_range;
+ power_rule = &reg_rule->power_rule;
+
+ reg_rule->flags = flags;
+
+ /* 2 GHz */
+ if (triplet->chans.first_channel <= 14)
+ end_channel = triplet->chans.first_channel +
+ triplet->chans.num_channels;
+ else
+ end_channel = triplet->chans.first_channel +
+ (4 * (triplet->chans.num_channels - 1));
+
+ /* The +10 is since the regulatory domain expects
+ * the actual band edge, not the center of freq for
+ * its start and end freqs, assuming 20 MHz bandwidth on
+ * the channels passed */
+ freq_range->start_freq_khz =
+ MHZ_TO_KHZ(ieee80211_channel_to_frequency(
+ triplet->chans.first_channel) - 10);
+ freq_range->end_freq_khz =
+ MHZ_TO_KHZ(ieee80211_channel_to_frequency(
+ end_channel) + 10);
+
+ /* Large arbitrary values, we intersect later */
+ /* Increment this if we ever support >= 40 MHz channels
+ * in IEEE 802.11 */
+ freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40);
+ power_rule->max_antenna_gain = DBI_TO_MBI(100);
+ power_rule->max_eirp = DBM_TO_MBM(100);
+
+ country_ie += 3;
+ country_ie_len -= 3;
+ i++;
+
+ BUG_ON(i > NL80211_MAX_SUPP_REG_RULES);
+ }
+
+ return rd;
+}
+
+
+/* Helper for regdom_intersect(), this does the real
+ * mathematical intersection fun */
+static int reg_rules_intersect(
+ const struct ieee80211_reg_rule *rule1,
+ const struct ieee80211_reg_rule *rule2,
+ struct ieee80211_reg_rule *intersected_rule)
+{
+ const struct ieee80211_freq_range *freq_range1, *freq_range2;
+ struct ieee80211_freq_range *freq_range;
+ const struct ieee80211_power_rule *power_rule1, *power_rule2;
+ struct ieee80211_power_rule *power_rule;
+ u32 freq_diff;
+
+ freq_range1 = &rule1->freq_range;
+ freq_range2 = &rule2->freq_range;
+ freq_range = &intersected_rule->freq_range;
+
+ power_rule1 = &rule1->power_rule;
+ power_rule2 = &rule2->power_rule;
+ power_rule = &intersected_rule->power_rule;
+
+ freq_range->start_freq_khz = max(freq_range1->start_freq_khz,
+ freq_range2->start_freq_khz);
+ freq_range->end_freq_khz = min(freq_range1->end_freq_khz,
+ freq_range2->end_freq_khz);
+ freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz,
+ freq_range2->max_bandwidth_khz);
+
+ freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;
+ if (freq_range->max_bandwidth_khz > freq_diff)
+ freq_range->max_bandwidth_khz = freq_diff;
+
+ power_rule->max_eirp = min(power_rule1->max_eirp,
+ power_rule2->max_eirp);
+ power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain,
+ power_rule2->max_antenna_gain);
+
+ intersected_rule->flags = (rule1->flags | rule2->flags);
+
+ if (!is_valid_reg_rule(intersected_rule))
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * regdom_intersect - do the intersection between two regulatory domains
+ * @rd1: first regulatory domain
+ * @rd2: second regulatory domain
+ *
+ * Use this function to get the intersection between two regulatory domains.
+ * Once completed we will mark the alpha2 for the rd as intersected, "98",
+ * as no one single alpha2 can represent this regulatory domain.
+ *
+ * Returns a pointer to the regulatory domain structure which will hold the
+ * resulting intersection of rules between rd1 and rd2. We will
+ * kzalloc() this structure for you.
+ */
+static struct ieee80211_regdomain *regdom_intersect(
+ const struct ieee80211_regdomain *rd1,
+ const struct ieee80211_regdomain *rd2)
+{
+ int r, size_of_regd;
+ unsigned int x, y;
+ unsigned int num_rules = 0, rule_idx = 0;
+ const struct ieee80211_reg_rule *rule1, *rule2;
+ struct ieee80211_reg_rule *intersected_rule;
+ struct ieee80211_regdomain *rd;
+ /* This is just a dummy holder to help us count */
+ struct ieee80211_reg_rule irule;
+
+ /* Uses the stack temporarily for counter arithmetic */
+ intersected_rule = &irule;
+
+ memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule));
+
+ if (!rd1 || !rd2)
+ return NULL;
+
+ /* First we get a count of the rules we'll need, then we actually
+ * build them. This is to so we can malloc() and free() a
+ * regdomain once. The reason we use reg_rules_intersect() here
+ * is it will return -EINVAL if the rule computed makes no sense.
+ * All rules that do check out OK are valid. */
+
+ for (x = 0; x < rd1->n_reg_rules; x++) {
+ rule1 = &rd1->reg_rules[x];
+ for (y = 0; y < rd2->n_reg_rules; y++) {
+ rule2 = &rd2->reg_rules[y];
+ if (!reg_rules_intersect(rule1, rule2,
+ intersected_rule))
+ num_rules++;
+ memset(intersected_rule, 0,
+ sizeof(struct ieee80211_reg_rule));
+ }
+ }
+
+ if (!num_rules)
+ return NULL;
+
+ size_of_regd = sizeof(struct ieee80211_regdomain) +
+ ((num_rules + 1) * sizeof(struct ieee80211_reg_rule));
+
+ rd = kzalloc(size_of_regd, GFP_KERNEL);
+ if (!rd)
+ return NULL;
+
+ for (x = 0; x < rd1->n_reg_rules; x++) {
+ rule1 = &rd1->reg_rules[x];
+ for (y = 0; y < rd2->n_reg_rules; y++) {
+ rule2 = &rd2->reg_rules[y];
+ /* This time around instead of using the stack lets
+ * write to the target rule directly saving ourselves
+ * a memcpy() */
+ intersected_rule = &rd->reg_rules[rule_idx];
+ r = reg_rules_intersect(rule1, rule2,
+ intersected_rule);
+ /* No need to memset here the intersected rule here as
+ * we're not using the stack anymore */
+ if (r)
+ continue;
+ rule_idx++;
+ }
+ }
+
+ if (rule_idx != num_rules) {
+ kfree(rd);
+ return NULL;
+ }
+
+ rd->n_reg_rules = num_rules;
+ rd->alpha2[0] = '9';
+ rd->alpha2[1] = '8';
+
+ return rd;
+}
+
/* XXX: add support for the rest of enum nl80211_reg_rule_flags, we may
* want to just have the channel structure use these */
static u32 map_regdom_flags(u32 rd_flags)
@@ -491,12 +805,23 @@ static u32 map_regdom_flags(u32 rd_flags)
* this value to the maximum allowed bandwidth.
* @reg_rule: the regulatory rule which we have for this frequency
*
- * Use this function to get the regulatory rule for a specific frequency.
+ * Use this function to get the regulatory rule for a specific frequency on
+ * a given wireless device. If the device has a specific regulatory domain
+ * it wants to follow we respect that unless a country IE has been received
+ * and processed already.
+ *
+ * Returns 0 if it was able to find a valid regulatory rule which does
+ * apply to the given center_freq otherwise it returns non-zero. It will
+ * also return -ERANGE if we determine the given center_freq does not even have
+ * a regulatory rule for a frequency range in the center_freq's band. See
+ * freq_in_rule_band() for our current definition of a band -- this is purely
+ * subjective and right now its 802.11 specific.
*/
static int freq_reg_info(u32 center_freq, u32 *bandwidth,
const struct ieee80211_reg_rule **reg_rule)
{
int i;
+ bool band_rule_found = false;
u32 max_bandwidth = 0;
if (!cfg80211_regdomain)
@@ -510,7 +835,15 @@ static int freq_reg_info(u32 center_freq, u32 *bandwidth,
rr = &cfg80211_regdomain->reg_rules[i];
fr = &rr->freq_range;
pr = &rr->power_rule;
+
+ /* We only need to know if one frequency rule was
+ * was in center_freq's band, that's enough, so lets
+ * not overwrite it once found */
+ if (!band_rule_found)
+ band_rule_found = freq_in_rule_band(fr, center_freq);
+
max_bandwidth = freq_max_bandwidth(fr, center_freq);
+
if (max_bandwidth && *bandwidth <= max_bandwidth) {
*reg_rule = rr;
*bandwidth = max_bandwidth;
@@ -518,23 +851,64 @@ static int freq_reg_info(u32 center_freq, u32 *bandwidth,
}
}
+ if (!band_rule_found)
+ return -ERANGE;
+
return !max_bandwidth;
}
-static void handle_channel(struct ieee80211_channel *chan)
+static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band,
+ unsigned int chan_idx)
{
int r;
- u32 flags = chan->orig_flags;
+ u32 flags;
u32 max_bandwidth = 0;
const struct ieee80211_reg_rule *reg_rule = NULL;
const struct ieee80211_power_rule *power_rule = NULL;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *chan;
+
+ sband = wiphy->bands[band];
+ BUG_ON(chan_idx >= sband->n_channels);
+ chan = &sband->channels[chan_idx];
+
+ flags = chan->orig_flags;
r = freq_reg_info(MHZ_TO_KHZ(chan->center_freq),
&max_bandwidth, &reg_rule);
if (r) {
- flags |= IEEE80211_CHAN_DISABLED;
- chan->flags = flags;
+ /* This means no regulatory rule was found in the country IE
+ * with a frequency range on the center_freq's band, since
+ * IEEE-802.11 allows for a country IE to have a subset of the
+ * regulatory information provided in a country we ignore
+ * disabling the channel unless at least one reg rule was
+ * found on the center_freq's band. For details see this
+ * clarification:
+ *
+ * http://tinyurl.com/11d-clarification
+ */
+ if (r == -ERANGE &&
+ last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) {
+#ifdef CONFIG_CFG80211_REG_DEBUG
+ printk(KERN_DEBUG "cfg80211: Leaving channel %d MHz "
+ "intact on %s - no rule found in band on "
+ "Country IE\n",
+ chan->center_freq, wiphy_name(wiphy));
+#endif
+ } else {
+ /* In this case we know the country IE has at least one reg rule
+ * for the band so we respect its band definitions */
+#ifdef CONFIG_CFG80211_REG_DEBUG
+ if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE)
+ printk(KERN_DEBUG "cfg80211: Disabling "
+ "channel %d MHz on %s due to "
+ "Country IE\n",
+ chan->center_freq, wiphy_name(wiphy));
+#endif
+ flags |= IEEE80211_CHAN_DISABLED;
+ chan->flags = flags;
+ }
return;
}
@@ -551,12 +925,26 @@ static void handle_channel(struct ieee80211_channel *chan)
chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp);
}
-static void handle_band(struct ieee80211_supported_band *sband)
+static void handle_band(struct wiphy *wiphy, enum ieee80211_band band)
{
- int i;
+ unsigned int i;
+ struct ieee80211_supported_band *sband;
+
+ BUG_ON(!wiphy->bands[band]);
+ sband = wiphy->bands[band];
for (i = 0; i < sband->n_channels; i++)
- handle_channel(&sband->channels[i]);
+ handle_channel(wiphy, band, i);
+}
+
+static bool ignore_reg_update(struct wiphy *wiphy, enum reg_set_by setby)
+{
+ if (!last_request)
+ return true;
+ if (setby == REGDOM_SET_BY_CORE &&
+ wiphy->fw_handles_regulatory)
+ return true;
+ return false;
}
static void update_all_wiphy_regulatory(enum reg_set_by setby)
@@ -564,7 +952,8 @@ static void update_all_wiphy_regulatory(enum reg_set_by setby)
struct cfg80211_registered_device *drv;
list_for_each_entry(drv, &cfg80211_drv_list, list)
- wiphy_update_regulatory(&drv->wiphy, setby);
+ if (!ignore_reg_update(&drv->wiphy, setby))
+ wiphy_update_regulatory(&drv->wiphy, setby);
}
void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby)
@@ -572,84 +961,243 @@ void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby)
enum ieee80211_band band;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
if (wiphy->bands[band])
- handle_band(wiphy->bands[band]);
+ handle_band(wiphy, band);
if (wiphy->reg_notifier)
wiphy->reg_notifier(wiphy, setby);
}
}
-/* Caller must hold &cfg80211_drv_mutex */
-int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by,
- const char *alpha2, struct ieee80211_regdomain *rd)
-{
- struct regulatory_request *request;
- char *rd_alpha2;
- int r = 0;
-
- r = ignore_request(wiphy, set_by, (char *) alpha2, rd);
- if (r)
- return r;
+/* Return value which can be used by ignore_request() to indicate
+ * it has been determined we should intersect two regulatory domains */
+#define REG_INTERSECT 1
- if (rd)
- rd_alpha2 = rd->alpha2;
- else
- rd_alpha2 = (char *) alpha2;
+/* This has the logic which determines when a new request
+ * should be ignored. */
+static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by,
+ const char *alpha2)
+{
+ /* All initial requests are respected */
+ if (!last_request)
+ return 0;
switch (set_by) {
+ case REGDOM_SET_BY_INIT:
+ return -EINVAL;
case REGDOM_SET_BY_CORE:
+ /*
+ * Always respect new wireless core hints, should only happen
+ * when updating the world regulatory domain at init.
+ */
+ return 0;
case REGDOM_SET_BY_COUNTRY_IE:
+ if (unlikely(!is_an_alpha2(alpha2)))
+ return -EINVAL;
+ if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) {
+ if (last_request->wiphy != wiphy) {
+ /*
+ * Two cards with two APs claiming different
+ * different Country IE alpha2s. We could
+ * intersect them, but that seems unlikely
+ * to be correct. Reject second one for now.
+ */
+ if (!alpha2_equal(alpha2,
+ cfg80211_regdomain->alpha2))
+ return -EOPNOTSUPP;
+ return -EALREADY;
+ }
+ /* Two consecutive Country IE hints on the same wiphy.
+ * This should be picked up early by the driver/stack */
+ if (WARN_ON(!alpha2_equal(cfg80211_regdomain->alpha2,
+ alpha2)))
+ return 0;
+ return -EALREADY;
+ }
+ return REG_INTERSECT;
case REGDOM_SET_BY_DRIVER:
+ if (last_request->initiator == REGDOM_SET_BY_DRIVER)
+ return -EALREADY;
+ return 0;
case REGDOM_SET_BY_USER:
- request = kzalloc(sizeof(struct regulatory_request),
- GFP_KERNEL);
- if (!request)
- return -ENOMEM;
-
- request->alpha2[0] = rd_alpha2[0];
- request->alpha2[1] = rd_alpha2[1];
- request->initiator = set_by;
- request->wiphy = wiphy;
-
- list_add_tail(&request->list, &regulatory_requests);
- if (rd)
- break;
- r = call_crda(alpha2);
-#ifndef CONFIG_WIRELESS_OLD_REGULATORY
- if (r)
- printk(KERN_ERR "cfg80211: Failed calling CRDA\n");
-#endif
- break;
- default:
- r = -ENOTSUPP;
- break;
+ if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE)
+ return REG_INTERSECT;
+ /* If the user knows better the user should set the regdom
+ * to their country before the IE is picked up */
+ if (last_request->initiator == REGDOM_SET_BY_USER &&
+ last_request->intersect)
+ return -EOPNOTSUPP;
+ return 0;
}
- return r;
+ return -EINVAL;
}
-/* If rd is not NULL and if this call fails the caller must free it */
-int regulatory_hint(struct wiphy *wiphy, const char *alpha2,
- struct ieee80211_regdomain *rd)
+/* Caller must hold &cfg80211_drv_mutex */
+int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by,
+ const char *alpha2,
+ u32 country_ie_checksum,
+ enum environment_cap env)
{
- int r;
- BUG_ON(!rd && !alpha2);
+ struct regulatory_request *request;
+ bool intersect = false;
+ int r = 0;
- mutex_lock(&cfg80211_drv_mutex);
+ r = ignore_request(wiphy, set_by, alpha2);
- r = __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2, rd);
- if (r || !rd)
- goto unlock_and_exit;
+ if (r == REG_INTERSECT)
+ intersect = true;
+ else if (r)
+ return r;
- /* If the driver passed a regulatory domain we skipped asking
- * userspace for one so we can now go ahead and set it */
- r = set_regdom(rd);
+ request = kzalloc(sizeof(struct regulatory_request),
+ GFP_KERNEL);
+ if (!request)
+ return -ENOMEM;
+
+ request->alpha2[0] = alpha2[0];
+ request->alpha2[1] = alpha2[1];
+ request->initiator = set_by;
+ request->wiphy = wiphy;
+ request->intersect = intersect;
+ request->country_ie_checksum = country_ie_checksum;
+ request->country_ie_env = env;
+
+ kfree(last_request);
+ last_request = request;
+ /*
+ * Note: When CONFIG_WIRELESS_OLD_REGULATORY is enabled
+ * AND if CRDA is NOT present nothing will happen, if someone
+ * wants to bother with 11d with OLD_REG you can add a timer.
+ * If after x amount of time nothing happens you can call:
+ *
+ * return set_regdom(country_ie_regdomain);
+ *
+ * to intersect with the static rd
+ */
+ return call_crda(alpha2);
+}
-unlock_and_exit:
+void regulatory_hint(struct wiphy *wiphy, const char *alpha2)
+{
+ BUG_ON(!alpha2);
+
+ mutex_lock(&cfg80211_drv_mutex);
+ __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2, 0, ENVIRON_ANY);
mutex_unlock(&cfg80211_drv_mutex);
- return r;
}
EXPORT_SYMBOL(regulatory_hint);
+static bool reg_same_country_ie_hint(struct wiphy *wiphy,
+ u32 country_ie_checksum)
+{
+ if (!last_request->wiphy)
+ return false;
+ if (likely(last_request->wiphy != wiphy))
+ return !country_ie_integrity_changes(country_ie_checksum);
+ /* We should not have let these through at this point, they
+ * should have been picked up earlier by the first alpha2 check
+ * on the device */
+ if (WARN_ON(!country_ie_integrity_changes(country_ie_checksum)))
+ return true;
+ return false;
+}
+
+void regulatory_hint_11d(struct wiphy *wiphy,
+ u8 *country_ie,
+ u8 country_ie_len)
+{
+ struct ieee80211_regdomain *rd = NULL;
+ char alpha2[2];
+ u32 checksum = 0;
+ enum environment_cap env = ENVIRON_ANY;
+
+ if (!last_request)
+ return;
+
+ mutex_lock(&cfg80211_drv_mutex);
+
+ /* IE len must be evenly divisible by 2 */
+ if (country_ie_len & 0x01)
+ goto out;
+
+ if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
+ goto out;
+
+ /* Pending country IE processing, this can happen after we
+ * call CRDA and wait for a response if a beacon was received before
+ * we were able to process the last regulatory_hint_11d() call */
+ if (country_ie_regdomain)
+ goto out;
+
+ alpha2[0] = country_ie[0];
+ alpha2[1] = country_ie[1];
+
+ if (country_ie[2] == 'I')
+ env = ENVIRON_INDOOR;
+ else if (country_ie[2] == 'O')
+ env = ENVIRON_OUTDOOR;
+
+ /* We will run this for *every* beacon processed for the BSSID, so
+ * we optimize an early check to exit out early if we don't have to
+ * do anything */
+ if (likely(last_request->wiphy)) {
+ struct cfg80211_registered_device *drv_last_ie;
+
+ drv_last_ie = wiphy_to_dev(last_request->wiphy);
+
+ /* Lets keep this simple -- we trust the first AP
+ * after we intersect with CRDA */
+ if (likely(last_request->wiphy == wiphy)) {
+ /* Ignore IEs coming in on this wiphy with
+ * the same alpha2 and environment cap */
+ if (likely(alpha2_equal(drv_last_ie->country_ie_alpha2,
+ alpha2) &&
+ env == drv_last_ie->env)) {
+ goto out;
+ }
+ /* the wiphy moved on to another BSSID or the AP
+ * was reconfigured. XXX: We need to deal with the
+ * case where the user suspends and goes to goes
+ * to another country, and then gets IEs from an
+ * AP with different settings */
+ goto out;
+ } else {
+ /* Ignore IEs coming in on two separate wiphys with
+ * the same alpha2 and environment cap */
+ if (likely(alpha2_equal(drv_last_ie->country_ie_alpha2,
+ alpha2) &&
+ env == drv_last_ie->env)) {
+ goto out;
+ }
+ /* We could potentially intersect though */
+ goto out;
+ }
+ }
+
+ rd = country_ie_2_rd(country_ie, country_ie_len, &checksum);
+ if (!rd)
+ goto out;
+
+ /* This will not happen right now but we leave it here for the
+ * the future when we want to add suspend/resume support and having
+ * the user move to another country after doing so, or having the user
+ * move to another AP. Right now we just trust the first AP. This is why
+ * this is marked as likley(). If we hit this before we add this support
+ * we want to be informed of it as it would indicate a mistake in the
+ * current design */
+ if (likely(WARN_ON(reg_same_country_ie_hint(wiphy, checksum))))
+ goto out;
+
+ /* We keep this around for when CRDA comes back with a response so
+ * we can intersect with that */
+ country_ie_regdomain = rd;
+
+ __regulatory_hint(wiphy, REGDOM_SET_BY_COUNTRY_IE,
+ country_ie_regdomain->alpha2, checksum, env);
+
+out:
+ mutex_unlock(&cfg80211_drv_mutex);
+}
+EXPORT_SYMBOL(regulatory_hint_11d);
static void print_rd_rules(const struct ieee80211_regdomain *rd)
{
@@ -689,7 +1237,25 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd)
static void print_regdomain(const struct ieee80211_regdomain *rd)
{
- if (is_world_regdom(rd->alpha2))
+ if (is_intersected_alpha2(rd->alpha2)) {
+ struct wiphy *wiphy = NULL;
+ struct cfg80211_registered_device *drv;
+
+ if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) {
+ if (last_request->wiphy) {
+ wiphy = last_request->wiphy;
+ drv = wiphy_to_dev(wiphy);
+ printk(KERN_INFO "cfg80211: Current regulatory "
+ "domain updated by AP to: %c%c\n",
+ drv->country_ie_alpha2[0],
+ drv->country_ie_alpha2[1]);
+ } else
+ printk(KERN_INFO "cfg80211: Current regulatory "
+ "domain intersected: \n");
+ } else
+ printk(KERN_INFO "cfg80211: Current regulatory "
+ "intersected: \n");
+ } else if (is_world_regdom(rd->alpha2))
printk(KERN_INFO "cfg80211: World regulatory "
"domain updated:\n");
else {
@@ -705,21 +1271,50 @@ static void print_regdomain(const struct ieee80211_regdomain *rd)
print_rd_rules(rd);
}
-void print_regdomain_info(const struct ieee80211_regdomain *rd)
+static void print_regdomain_info(const struct ieee80211_regdomain *rd)
{
printk(KERN_INFO "cfg80211: Regulatory domain: %c%c\n",
rd->alpha2[0], rd->alpha2[1]);
print_rd_rules(rd);
}
-static int __set_regdom(const struct ieee80211_regdomain *rd)
+#ifdef CONFIG_CFG80211_REG_DEBUG
+static void reg_country_ie_process_debug(
+ const struct ieee80211_regdomain *rd,
+ const struct ieee80211_regdomain *country_ie_regdomain,
+ const struct ieee80211_regdomain *intersected_rd)
+{
+ printk(KERN_DEBUG "cfg80211: Received country IE:\n");
+ print_regdomain_info(country_ie_regdomain);
+ printk(KERN_DEBUG "cfg80211: CRDA thinks this should applied:\n");
+ print_regdomain_info(rd);
+ if (intersected_rd) {
+ printk(KERN_DEBUG "cfg80211: We intersect both of these "
+ "and get:\n");
+ print_regdomain_info(intersected_rd);
+ return;
+ }
+ printk(KERN_DEBUG "cfg80211: Intersection between both failed\n");
+}
+#else
+static inline void reg_country_ie_process_debug(
+ const struct ieee80211_regdomain *rd,
+ const struct ieee80211_regdomain *country_ie_regdomain,
+ const struct ieee80211_regdomain *intersected_rd)
{
- struct regulatory_request *request = NULL;
+}
+#endif
+/* Takes ownership of rd only if it doesn't fail */
+static int __set_regdom(const struct ieee80211_regdomain *rd)
+{
+ const struct ieee80211_regdomain *intersected_rd = NULL;
+ struct cfg80211_registered_device *drv = NULL;
+ struct wiphy *wiphy = NULL;
/* Some basic sanity checks first */
if (is_world_regdom(rd->alpha2)) {
- if (WARN_ON(!__reg_is_valid_request(rd->alpha2, &request)))
+ if (WARN_ON(!reg_is_valid_request(rd->alpha2)))
return -EINVAL;
update_world_regdomain(rd);
return 0;
@@ -729,45 +1324,102 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
!is_unknown_alpha2(rd->alpha2))
return -EINVAL;
- if (list_empty(&regulatory_requests))
+ if (!last_request)
return -EINVAL;
- /* allow overriding the static definitions if CRDA is present */
- if (!is_old_static_regdom(cfg80211_regdomain) &&
- !regdom_changed(rd->alpha2))
- return -EINVAL;
+ /* Lets only bother proceeding on the same alpha2 if the current
+ * rd is non static (it means CRDA was present and was used last)
+ * and the pending request came in from a country IE */
+ if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE) {
+ /* If someone else asked us to change the rd lets only bother
+ * checking if the alpha2 changes if CRDA was already called */
+ if (!is_old_static_regdom(cfg80211_regdomain) &&
+ !regdom_changed(rd->alpha2))
+ return -EINVAL;
+ }
+
+ wiphy = last_request->wiphy;
/* Now lets set the regulatory domain, update all driver channels
* and finally inform them of what we have done, in case they want
* to review or adjust their own settings based on their own
* internal EEPROM data */
- if (WARN_ON(!__reg_is_valid_request(rd->alpha2, &request)))
+ if (WARN_ON(!reg_is_valid_request(rd->alpha2)))
return -EINVAL;
- reset_regdomains();
+ if (!is_valid_rd(rd)) {
+ printk(KERN_ERR "cfg80211: Invalid "
+ "regulatory domain detected:\n");
+ print_regdomain_info(rd);
+ return -EINVAL;
+ }
- /* Country IE parsing coming soon */
- switch (request->initiator) {
- case REGDOM_SET_BY_CORE:
- case REGDOM_SET_BY_DRIVER:
- case REGDOM_SET_BY_USER:
- if (!is_valid_rd(rd)) {
- printk(KERN_ERR "cfg80211: Invalid "
- "regulatory domain detected:\n");
- print_regdomain_info(rd);
+ if (!last_request->intersect) {
+ reset_regdomains();
+ cfg80211_regdomain = rd;
+ return 0;
+ }
+
+ /* Intersection requires a bit more work */
+
+ if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE) {
+
+ intersected_rd = regdom_intersect(rd, cfg80211_regdomain);
+ if (!intersected_rd)
return -EINVAL;
- }
- break;
- case REGDOM_SET_BY_COUNTRY_IE: /* Not yet */
- WARN_ON(1);
- default:
- return -EOPNOTSUPP;
+
+ /* We can trash what CRDA provided now */
+ kfree(rd);
+ rd = NULL;
+
+ reset_regdomains();
+ cfg80211_regdomain = intersected_rd;
+
+ return 0;
}
- /* Tada! */
- cfg80211_regdomain = rd;
- request->granted = 1;
+ /*
+ * Country IE requests are handled a bit differently, we intersect
+ * the country IE rd with what CRDA believes that country should have
+ */
+
+ BUG_ON(!country_ie_regdomain);
+
+ if (rd != country_ie_regdomain) {
+ /* Intersect what CRDA returned and our what we
+ * had built from the Country IE received */
+
+ intersected_rd = regdom_intersect(rd, country_ie_regdomain);
+
+ reg_country_ie_process_debug(rd, country_ie_regdomain,
+ intersected_rd);
+
+ kfree(country_ie_regdomain);
+ country_ie_regdomain = NULL;
+ } else {
+ /* This would happen when CRDA was not present and
+ * OLD_REGULATORY was enabled. We intersect our Country
+ * IE rd and what was set on cfg80211 originally */
+ intersected_rd = regdom_intersect(rd, cfg80211_regdomain);
+ }
+
+ if (!intersected_rd)
+ return -EINVAL;
+
+ drv = wiphy_to_dev(wiphy);
+
+ drv->country_ie_alpha2[0] = rd->alpha2[0];
+ drv->country_ie_alpha2[1] = rd->alpha2[1];
+ drv->env = last_request->country_ie_env;
+
+ BUG_ON(intersected_rd == rd);
+
+ kfree(rd);
+ rd = NULL;
+
+ reset_regdomains();
+ cfg80211_regdomain = intersected_rd;
return 0;
}
@@ -775,52 +1427,41 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
/* Use this call to set the current regulatory domain. Conflicts with
* multiple drivers can be ironed out later. Caller must've already
- * kmalloc'd the rd structure. If this calls fails you should kfree()
- * the passed rd. Caller must hold cfg80211_drv_mutex */
+ * kmalloc'd the rd structure. Caller must hold cfg80211_drv_mutex */
int set_regdom(const struct ieee80211_regdomain *rd)
{
- struct regulatory_request *this_request = NULL, *prev_request = NULL;
int r;
- if (!list_empty(&regulatory_requests))
- prev_request = list_first_entry(&regulatory_requests,
- struct regulatory_request, list);
-
/* Note that this doesn't update the wiphys, this is done below */
r = __set_regdom(rd);
- if (r)
+ if (r) {
+ kfree(rd);
return r;
-
- BUG_ON((!__reg_is_valid_request(rd->alpha2, &this_request)));
-
- /* The initial standard core update of the world regulatory domain, no
- * need to keep that request info around if it didn't fail. */
- if (is_world_regdom(rd->alpha2) &&
- this_request->initiator == REGDOM_SET_BY_CORE &&
- this_request->granted) {
- list_del(&this_request->list);
- kfree(this_request);
- this_request = NULL;
- }
-
- /* Remove old requests, we only leave behind the last one */
- if (prev_request) {
- list_del(&prev_request->list);
- kfree(prev_request);
- prev_request = NULL;
}
/* This would make this whole thing pointless */
- BUG_ON(rd != cfg80211_regdomain);
+ if (!last_request->intersect)
+ BUG_ON(rd != cfg80211_regdomain);
/* update all wiphys now with the new established regulatory domain */
- update_all_wiphy_regulatory(this_request->initiator);
+ update_all_wiphy_regulatory(last_request->initiator);
- print_regdomain(rd);
+ print_regdomain(cfg80211_regdomain);
return r;
}
+/* Caller must hold cfg80211_drv_mutex */
+void reg_device_remove(struct wiphy *wiphy)
+{
+ if (!last_request || !last_request->wiphy)
+ return;
+ if (last_request->wiphy != wiphy)
+ return;
+ last_request->wiphy = NULL;
+ last_request->country_ie_env = ENVIRON_ANY;
+}
+
int regulatory_init(void)
{
int err;
@@ -838,13 +1479,13 @@ int regulatory_init(void)
* you have CRDA you get it updated, otherwise you get
* stuck with the static values. We ignore "EU" code as
* that is not a valid ISO / IEC 3166 alpha2 */
- if (ieee80211_regdom[0] != 'E' && ieee80211_regdom[1] != 'U')
+ if (ieee80211_regdom[0] != 'E' || ieee80211_regdom[1] != 'U')
err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE,
- ieee80211_regdom, NULL);
+ ieee80211_regdom, 0, ENVIRON_ANY);
#else
cfg80211_regdomain = cfg80211_world_regdom;
- err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, "00", NULL);
+ err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, "00", 0, ENVIRON_ANY);
if (err)
printk(KERN_ERR "cfg80211: calling CRDA failed - "
"unable to update world regulatory domain, "
@@ -856,16 +1497,15 @@ int regulatory_init(void)
void regulatory_exit(void)
{
- struct regulatory_request *req, *req_tmp;
-
mutex_lock(&cfg80211_drv_mutex);
reset_regdomains();
- list_for_each_entry_safe(req, req_tmp, &regulatory_requests, list) {
- list_del(&req->list);
- kfree(req);
- }
+ kfree(country_ie_regdomain);
+ country_ie_regdomain = NULL;
+
+ kfree(last_request);
+
platform_device_unregister(reg_pdev);
mutex_unlock(&cfg80211_drv_mutex);
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index a33362872f3c..a76ea3ff7cd6 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -1,13 +1,44 @@
#ifndef __NET_WIRELESS_REG_H
#define __NET_WIRELESS_REG_H
-extern struct mutex cfg80211_reg_mutex;
bool is_world_regdom(const char *alpha2);
bool reg_is_valid_request(const char *alpha2);
+void reg_device_remove(struct wiphy *wiphy);
+
int regulatory_init(void);
void regulatory_exit(void);
int set_regdom(const struct ieee80211_regdomain *rd);
+enum environment_cap {
+ ENVIRON_ANY,
+ ENVIRON_INDOOR,
+ ENVIRON_OUTDOOR,
+};
+
+
+/**
+ * __regulatory_hint - hint to the wireless core a regulatory domain
+ * @wiphy: if the hint comes from country information from an AP, this
+ * is required to be set to the wiphy that received the information
+ * @alpha2: the ISO/IEC 3166 alpha2 being claimed the regulatory domain
+ * should be in.
+ * @country_ie_checksum: checksum of processed country IE, set this to 0
+ * if the hint did not come from a country IE
+ * @country_ie_env: the environment the IE told us we are in, %ENVIRON_*
+ *
+ * The Wireless subsystem can use this function to hint to the wireless core
+ * what it believes should be the current regulatory domain by giving it an
+ * ISO/IEC 3166 alpha2 country code it knows its regulatory domain should be
+ * in.
+ *
+ * Returns zero if all went fine, %-EALREADY if a regulatory domain had
+ * already been set or other standard error codes.
+ *
+ */
+extern int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by,
+ const char *alpha2, u32 country_ie_checksum,
+ enum environment_cap country_ie_env);
+
#endif /* __NET_WIRELESS_REG_H */
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
index 29f820e18251..79a382877641 100644
--- a/net/wireless/sysfs.c
+++ b/net/wireless/sysfs.c
@@ -23,25 +23,20 @@ static inline struct cfg80211_registered_device *dev_to_rdev(
return container_of(dev, struct cfg80211_registered_device, wiphy.dev);
}
-static ssize_t _show_index(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- return sprintf(buf, "%d\n", dev_to_rdev(dev)->idx);
+#define SHOW_FMT(name, fmt, member) \
+static ssize_t name ## _show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member); \
}
-static ssize_t _show_permaddr(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- unsigned char *addr = dev_to_rdev(dev)->wiphy.perm_addr;
-
- return sprintf(buf, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
- addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
-}
+SHOW_FMT(index, "%d", idx);
+SHOW_FMT(macaddress, "%pM", wiphy.perm_addr);
static struct device_attribute ieee80211_dev_attrs[] = {
- __ATTR(index, S_IRUGO, _show_index, NULL),
- __ATTR(macaddress, S_IRUGO, _show_permaddr, NULL),
+ __ATTR_RO(index),
+ __ATTR_RO(macaddress),
{}
};
diff --git a/net/wireless/util.c b/net/wireless/util.c
index f54424693a38..e76cc28b0345 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -7,6 +7,25 @@
#include <asm/bitops.h>
#include "core.h"
+struct ieee80211_rate *
+ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
+ u64 basic_rates, int bitrate)
+{
+ struct ieee80211_rate *result = &sband->bitrates[0];
+ int i;
+
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if (!(basic_rates & BIT(i)))
+ continue;
+ if (sband->bitrates[i].bitrate > bitrate)
+ continue;
+ result = &sband->bitrates[i];
+ }
+
+ return result;
+}
+EXPORT_SYMBOL(ieee80211_get_response_rate);
+
int ieee80211_channel_to_frequency(int chan)
{
if (chan < 14)
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
new file mode 100644
index 000000000000..58e489fd4aed
--- /dev/null
+++ b/net/wireless/wext-compat.c
@@ -0,0 +1,139 @@
+/*
+ * cfg80211 - wext compat code
+ *
+ * This is temporary code until all wireless functionality is migrated
+ * into cfg80211, when that happens all the exports here go away and
+ * we directly assign the wireless handlers of wireless interfaces.
+ *
+ * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
+ */
+
+#include <linux/wireless.h>
+#include <linux/nl80211.h>
+#include <net/iw_handler.h>
+#include <net/wireless.h>
+#include <net/cfg80211.h>
+#include "core.h"
+
+int cfg80211_wext_giwname(struct net_device *dev,
+ struct iw_request_info *info,
+ char *name, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct ieee80211_supported_band *sband;
+ bool is_ht = false, is_a = false, is_b = false, is_g = false;
+
+ if (!wdev)
+ return -EOPNOTSUPP;
+
+ sband = wdev->wiphy->bands[IEEE80211_BAND_5GHZ];
+ if (sband) {
+ is_a = true;
+ is_ht |= sband->ht_cap.ht_supported;
+ }
+
+ sband = wdev->wiphy->bands[IEEE80211_BAND_2GHZ];
+ if (sband) {
+ int i;
+ /* Check for mandatory rates */
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if (sband->bitrates[i].bitrate == 10)
+ is_b = true;
+ if (sband->bitrates[i].bitrate == 60)
+ is_g = true;
+ }
+ is_ht |= sband->ht_cap.ht_supported;
+ }
+
+ strcpy(name, "IEEE 802.11");
+ if (is_a)
+ strcat(name, "a");
+ if (is_b)
+ strcat(name, "b");
+ if (is_g)
+ strcat(name, "g");
+ if (is_ht)
+ strcat(name, "n");
+
+ return 0;
+}
+EXPORT_SYMBOL(cfg80211_wext_giwname);
+
+int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,
+ u32 *mode, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev;
+ struct vif_params vifparams;
+ enum nl80211_iftype type;
+
+ if (!wdev)
+ return -EOPNOTSUPP;
+
+ rdev = wiphy_to_dev(wdev->wiphy);
+
+ if (!rdev->ops->change_virtual_intf)
+ return -EOPNOTSUPP;
+
+ /* don't support changing VLANs, you just re-create them */
+ if (wdev->iftype == NL80211_IFTYPE_AP_VLAN)
+ return -EOPNOTSUPP;
+
+ switch (*mode) {
+ case IW_MODE_INFRA:
+ type = NL80211_IFTYPE_STATION;
+ break;
+ case IW_MODE_ADHOC:
+ type = NL80211_IFTYPE_ADHOC;
+ break;
+ case IW_MODE_REPEAT:
+ type = NL80211_IFTYPE_WDS;
+ break;
+ case IW_MODE_MONITOR:
+ type = NL80211_IFTYPE_MONITOR;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ memset(&vifparams, 0, sizeof(vifparams));
+
+ return rdev->ops->change_virtual_intf(wdev->wiphy, dev->ifindex, type,
+ NULL, &vifparams);
+}
+EXPORT_SYMBOL(cfg80211_wext_siwmode);
+
+int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info,
+ u32 *mode, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ if (!wdev)
+ return -EOPNOTSUPP;
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_AP:
+ *mode = IW_MODE_MASTER;
+ break;
+ case NL80211_IFTYPE_STATION:
+ *mode = IW_MODE_INFRA;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ *mode = IW_MODE_ADHOC;
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ *mode = IW_MODE_MONITOR;
+ break;
+ case NL80211_IFTYPE_WDS:
+ *mode = IW_MODE_REPEAT;
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+ *mode = IW_MODE_SECOND; /* FIXME */
+ break;
+ default:
+ *mode = IW_MODE_AUTO;
+ break;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(cfg80211_wext_giwmode);
diff --git a/net/wireless/wext.c b/net/wireless/wext.c
index d98ffb75119a..e49a2d1ef1e4 100644
--- a/net/wireless/wext.c
+++ b/net/wireless/wext.c
@@ -64,7 +64,7 @@
* o Remove spy_offset from struct iw_handler_def
* o Start deprecating dev->get_wireless_stats, output a warning
* o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless
- * o Don't loose INVALID/DBM flags when clearing UPDATED flags (iwstats)
+ * o Don't lose INVALID/DBM flags when clearing UPDATED flags (iwstats)
*
* v8 - 17.02.06 - Jean II
* o RtNetlink requests support (SET/GET)