diff options
author | Todd Doucet <todd.doucet@timesys.com> | 2010-02-03 17:06:33 -0500 |
---|---|---|
committer | Todd Doucet <todd.doucet@timesys.com> | 2010-02-03 17:06:33 -0500 |
commit | ff238a4df84428befc55d49f58864dfc47ff853d (patch) | |
tree | 8796b828b396f5b7ed017904ff77b614dbf38677 /net | |
parent | 4a6908a3a050aacc9c3a2f36b276b46c0629ad91 (diff) |
Kernel as received from Digi for their Wi-Mx51 SoC running on their
CCWMX51JS carrier board.
Diffstat (limited to 'net')
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(®ulatory_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(®_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(®ulatory_requests)) - return 0; - - last_request = list_first_entry(®ulatory_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(®ulatory_requests)) - return false; - list_for_each_entry(req, ®ulatory_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 = ®_rule->freq_range; + power_rule = ®_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, ®_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, ®ulatory_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(®ulatory_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(®ulatory_requests)) - prev_request = list_first_entry(®ulatory_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, ®ulatory_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) |