diff options
| author | Jeff Garzik <jeff@garzik.org> | 2006-03-22 22:55:57 -0500 |
|---|---|---|
| committer | Jeff Garzik <jeff@garzik.org> | 2006-03-22 22:55:57 -0500 |
| commit | fa4fa40a990f8f4eff65476bef32007c154bbac0 (patch) | |
| tree | c6e15822d1da2811ed004d3f8a4c72864adb5250 | |
| parent | b7ad6d75028d021362221d9b2db19fcff995c3f8 (diff) | |
| parent | fe0b06b123762ab620b5bee3dab1576ddddd0a7f (diff) | |
| download | linux-fa4fa40a990f8f4eff65476bef32007c154bbac0.tar.gz linux-fa4fa40a990f8f4eff65476bef32007c154bbac0.tar.bz2 linux-fa4fa40a990f8f4eff65476bef32007c154bbac0.zip | |
Merge branch 'upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-2.6
| -rw-r--r-- | include/net/ieee80211softmac.h | 292 | ||||
| -rw-r--r-- | include/net/ieee80211softmac_wx.h | 94 | ||||
| -rw-r--r-- | net/ieee80211/Kconfig | 1 | ||||
| -rw-r--r-- | net/ieee80211/Makefile | 1 | ||||
| -rw-r--r-- | net/ieee80211/ieee80211_rx.c | 74 | ||||
| -rw-r--r-- | net/ieee80211/softmac/Kconfig | 10 | ||||
| -rw-r--r-- | net/ieee80211/softmac/Makefile | 9 | ||||
| -rw-r--r-- | net/ieee80211/softmac/ieee80211softmac_assoc.c | 403 | ||||
| -rw-r--r-- | net/ieee80211/softmac/ieee80211softmac_auth.c | 376 | ||||
| -rw-r--r-- | net/ieee80211/softmac/ieee80211softmac_event.c | 159 | ||||
| -rw-r--r-- | net/ieee80211/softmac/ieee80211softmac_io.c | 474 | ||||
| -rw-r--r-- | net/ieee80211/softmac/ieee80211softmac_module.c | 457 | ||||
| -rw-r--r-- | net/ieee80211/softmac/ieee80211softmac_priv.h | 239 | ||||
| -rw-r--r-- | net/ieee80211/softmac/ieee80211softmac_scan.c | 237 | ||||
| -rw-r--r-- | net/ieee80211/softmac/ieee80211softmac_wx.c | 412 |
15 files changed, 3238 insertions, 0 deletions
diff --git a/include/net/ieee80211softmac.h b/include/net/ieee80211softmac.h new file mode 100644 index 000000000000..b971d8c82bdd --- /dev/null +++ b/include/net/ieee80211softmac.h @@ -0,0 +1,292 @@ +/* + * ieee80211softmac.h - public interface to the softmac + * + * Copyright (c) 2005 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_H_ +#define IEEE80211SOFTMAC_H_ + +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/workqueue.h> +#include <linux/list.h> +#include <net/ieee80211.h> + +/* Once the API is considered more or less stable, + * this should be incremented on API incompatible changes. + */ +#define IEEE80211SOFTMAC_API 0 + +#define IEEE80211SOFTMAC_MAX_RATES_LEN 8 +#define IEEE80211SOFTMAC_MAX_EX_RATES_LEN 255 + +struct ieee80211softmac_ratesinfo { + u8 count; + u8 rates[IEEE80211SOFTMAC_MAX_RATES_LEN + IEEE80211SOFTMAC_MAX_EX_RATES_LEN]; +}; + +/* internal structures */ +struct ieee80211softmac_network; +struct ieee80211softmac_scaninfo; + +struct ieee80211softmac_essid { + u8 len; + char data[IW_ESSID_MAX_SIZE+1]; +}; + +struct ieee80211softmac_wpa { + char *IE; + int IElen; + int IEbuflen; +}; + +/* + * Information about association + * + * Do we need a lock for this? + * We only ever use this structure inlined + * into our global struct. I've used its lock, + * but maybe we need a local one here? + */ +struct ieee80211softmac_assoc_info { + /* + * This is the requested ESSID. It is written + * only by the WX handlers. + * + */ + struct ieee80211softmac_essid req_essid; + /* + * the ESSID of the network we're currently + * associated (or trying) to. This is + * updated to the network's actual ESSID + * even if the requested ESSID was 'ANY' + */ + struct ieee80211softmac_essid associate_essid; + + /* BSSID we're trying to associate to */ + char bssid[ETH_ALEN]; + + /* some flags. + * static_essid is valid if the essid is constant, + * this is for use by the wx handlers only. + * + * associating is true, if the network has been + * auth'ed on and we are in the process of associating. + * + * bssvalid is true if we found a matching network + * and saved it's BSSID into the bssid above. + */ + u8 static_essid:1, + associating:1, + bssvalid:1; + + /* Scan retries remaining */ + int scan_retry; + + struct work_struct work; + struct work_struct timeout; +}; + +enum { + IEEE80211SOFTMAC_AUTH_OPEN_REQUEST = 1, + IEEE80211SOFTMAC_AUTH_OPEN_RESPONSE = 2, +}; + +enum { + IEEE80211SOFTMAC_AUTH_SHARED_REQUEST = 1, + IEEE80211SOFTMAC_AUTH_SHARED_CHALLENGE = 2, + IEEE80211SOFTMAC_AUTH_SHARED_RESPONSE = 3, + IEEE80211SOFTMAC_AUTH_SHARED_PASS = 4, +}; + +/* We should make these tunable + * AUTH_TIMEOUT seems really long, but that's what it is in BSD */ +#define IEEE80211SOFTMAC_AUTH_TIMEOUT (12 * HZ) +#define IEEE80211SOFTMAC_AUTH_RETRY_LIMIT 5 +#define IEEE80211SOFTMAC_ASSOC_SCAN_RETRY_LIMIT 3 + +struct ieee80211softmac_txrates { + /* The Bit-Rate to be used for multicast frames. */ + u8 mcast_rate; + /* The Bit-Rate to be used for multicast fallback + * (If the device supports fallback and hardware-retry) + */ + u8 mcast_fallback; + /* The Bit-Rate to be used for any other (normal) data packet. */ + u8 default_rate; + /* The Bit-Rate to be used for default fallback + * (If the device supports fallback and hardware-retry) + */ + u8 default_fallback; +}; + +/* Bits for txrates_change callback. */ +#define IEEE80211SOFTMAC_TXRATECHG_DEFAULT (1 << 0) /* default_rate */ +#define IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK (1 << 1) /* default_fallback */ +#define IEEE80211SOFTMAC_TXRATECHG_MCAST (1 << 2) /* mcast_rate */ +#define IEEE80211SOFTMAC_TXRATECHG_MCAST_FBACK (1 << 3) /* mcast_fallback */ + +struct ieee80211softmac_device { + /* 802.11 structure for data stuff */ + struct ieee80211_device *ieee; + struct net_device *dev; + + /* only valid if associated, then holds the Association ID */ + u16 association_id; + + /* the following methods are callbacks that the driver + * using this framework has to assign + */ + + /* always assign these */ + void (*set_bssid_filter)(struct net_device *dev, const u8 *bssid); + void (*set_channel)(struct net_device *dev, u8 channel); + + /* assign if you need it, informational only */ + void (*link_change)(struct net_device *dev); + + /* If the hardware can do scanning, assign _all_ three of these callbacks. + * When the scan finishes, call ieee80211softmac_scan_finished(). + */ + + /* when called, start_scan is guaranteed to not be called again + * until you call ieee80211softmac_scan_finished. + * Return 0 if scanning could start, error otherwise. + * SOFTMAC AUTHORS: don't call this, use ieee80211softmac_start_scan */ + int (*start_scan)(struct net_device *dev); + /* this should block until after ieee80211softmac_scan_finished was called + * SOFTMAC AUTHORS: don't call this, use ieee80211softmac_wait_for_scan */ + void (*wait_for_scan)(struct net_device *dev); + /* stop_scan aborts a scan, but is asynchronous. + * if you want to wait for it too, use wait_for_scan + * SOFTMAC AUTHORS: don't call this, use ieee80211softmac_stop_scan */ + void (*stop_scan)(struct net_device *dev); + + /* we'll need something about beacons here too, for AP or ad-hoc modes */ + + /* Transmission rates to be used by the driver. + * The SoftMAC figures out the best possible rates. + * The driver just needs to read them. + */ + struct ieee80211softmac_txrates txrates; + /* If the driver needs to do stuff on TX rate changes, assign this callback. */ + void (*txrates_change)(struct net_device *dev, + u32 changes, /* see IEEE80211SOFTMAC_TXRATECHG flags */ + const struct ieee80211softmac_txrates *rates_before_change); + + /* private stuff follows */ + /* this lock protects this structure */ + spinlock_t lock; + + /* couple of flags */ + u8 scanning:1, /* protects scanning from being done multiple times at once */ + associated:1; + + struct ieee80211softmac_scaninfo *scaninfo; + struct ieee80211softmac_assoc_info associnfo; + + struct list_head auth_queue; + struct list_head events; + + struct ieee80211softmac_ratesinfo ratesinfo; + int txrate_badness; + + /* WPA stuff */ + struct ieee80211softmac_wpa wpa; + + /* we need to keep a list of network structs we copied */ + struct list_head network_list; + + /* This must be the last item so that it points to the data + * allocated beyond this structure by alloc_ieee80211 */ + u8 priv[0]; +}; + +extern void ieee80211softmac_scan_finished(struct ieee80211softmac_device *sm); + +static inline void * ieee80211softmac_priv(struct net_device *dev) +{ + return ((struct ieee80211softmac_device *)ieee80211_priv(dev))->priv; +} + +extern struct net_device * alloc_ieee80211softmac(int sizeof_priv); +extern void free_ieee80211softmac(struct net_device *dev); + +/* Call this function if you detect a lost TX fragment. + * (If the device indicates failure of ACK RX, for example.) + * It is wise to call this function if you are able to detect lost packets, + * because it contributes to the TX Rates auto adjustment. + */ +extern void ieee80211softmac_fragment_lost(struct net_device *dev, + u16 wireless_sequence_number); +/* Call this function before _start to tell the softmac what rates + * the hw supports. The rates parameter is copied, so you can + * free it right after calling this function. + * Note that the rates need to be sorted. */ +extern void ieee80211softmac_set_rates(struct net_device *dev, u8 count, u8 *rates); + +/* Start the SoftMAC. Call this after you initialized the device + * and it is ready to run. + */ +extern void ieee80211softmac_start(struct net_device *dev); +/* Stop the SoftMAC. Call this before you shutdown the device. */ +extern void ieee80211softmac_stop(struct net_device *dev); + +/* + * Event system + */ + +/* valid event types */ +#define IEEE80211SOFTMAC_EVENT_ANY -1 /*private use only*/ +#define IEEE80211SOFTMAC_EVENT_SCAN_FINISHED 0 +#define IEEE80211SOFTMAC_EVENT_ASSOCIATED 1 +#define IEEE80211SOFTMAC_EVENT_ASSOCIATE_FAILED 2 +#define IEEE80211SOFTMAC_EVENT_ASSOCIATE_TIMEOUT 3 +#define IEEE80211SOFTMAC_EVENT_AUTHENTICATED 4 +#define IEEE80211SOFTMAC_EVENT_AUTH_FAILED 5 +#define IEEE80211SOFTMAC_EVENT_AUTH_TIMEOUT 6 +#define IEEE80211SOFTMAC_EVENT_ASSOCIATE_NET_NOT_FOUND 7 +/* keep this updated! */ +#define IEEE80211SOFTMAC_EVENT_LAST 7 +/* + * If you want to be notified of certain events, you can call + * ieee80211softmac_notify[_atomic] with + * - event set to one of the constants below + * - fun set to a function pointer of the appropriate type + * - context set to the context data you want passed + * The return value is 0, or an error. + */ +typedef void (*notify_function_ptr)(struct net_device *dev, void *context); + +#define ieee80211softmac_notify(dev, event, fun, context) ieee80211softmac_notify_gfp(dev, event, fun, context, GFP_KERNEL); +#define ieee80211softmac_notify_atomic(dev, event, fun, context) ieee80211softmac_notify_gfp(dev, event, fun, context, GFP_ATOMIC); + +extern int ieee80211softmac_notify_gfp(struct net_device *dev, + int event, notify_function_ptr fun, void *context, gfp_t gfp_mask); + +/* To clear pending work (for ifconfig down, etc.) */ +extern void +ieee80211softmac_clear_pending_work(struct ieee80211softmac_device *sm); + +#endif /* IEEE80211SOFTMAC_H_ */ diff --git a/include/net/ieee80211softmac_wx.h b/include/net/ieee80211softmac_wx.h new file mode 100644 index 000000000000..3e0be453ecea --- /dev/null +++ b/include/net/ieee80211softmac_wx.h @@ -0,0 +1,94 @@ +/* + * This file contains the prototypes for the wireless extension + * handlers that the softmac API provides. Include this file to + * use the wx handlers, you can assign these directly. + * + * Copyright (c) 2005 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_WX_H +#define _IEEE80211SOFTMAC_WX_H + +#include <net/ieee80211softmac.h> +#include <net/iw_handler.h> + +extern int +ieee80211softmac_wx_trigger_scan(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra); + +extern int +ieee80211softmac_wx_get_scan_results(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra); + +extern int +ieee80211softmac_wx_set_essid(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra); + +extern int +ieee80211softmac_wx_get_essid(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra); + +extern int +ieee80211softmac_wx_set_rate(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra); + +extern int +ieee80211softmac_wx_get_rate(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra); + +extern int +ieee80211softmac_wx_get_wap(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra); + +extern int +ieee80211softmac_wx_set_wap(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra); + +extern int +ieee80211softmac_wx_set_genie(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra); + +extern int +ieee80211softmac_wx_get_genie(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra); +#endif /* _IEEE80211SOFTMAC_WX */ diff --git a/net/ieee80211/Kconfig b/net/ieee80211/Kconfig index d18ccba3ea9e..dbb08528ddf5 100644 --- a/net/ieee80211/Kconfig +++ b/net/ieee80211/Kconfig @@ -66,3 +66,4 @@ config IEEE80211_CRYPT_TKIP This can be compiled as a modules 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/ieee80211_rx.c b/net/ieee80211/ieee80211_rx.c index a7f2a642a512..604b7b0097bc 100644 --- a/net/ieee80211/ieee80211_rx.c +++ b/net/ieee80211/ieee80211_rx.c @@ -780,6 +780,80 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, return 0; } +/* Filter out unrelated packets, call ieee80211_rx[_mgt] */ +int ieee80211_rx_any(struct ieee80211_device *ieee, + struct sk_buff *skb, struct ieee80211_rx_stats *stats) +{ + struct ieee80211_hdr_4addr *hdr; + int is_packet_for_us; + u16 fc; + + if (ieee->iw_mode == IW_MODE_MONITOR) + return ieee80211_rx(ieee, skb, stats) ? 0 : -EINVAL; + + hdr = (struct ieee80211_hdr_4addr *)skb->data; + fc = le16_to_cpu(hdr->frame_ctl); + + if ((fc & IEEE80211_FCTL_VERS) != 0) + return -EINVAL; + + switch (fc & IEEE80211_FCTL_FTYPE) { + case IEEE80211_FTYPE_MGMT: + ieee80211_rx_mgt(ieee, hdr, stats); + return 0; + case IEEE80211_FTYPE_DATA: + break; + case IEEE80211_FTYPE_CTL: + return 0; + default: + return -EINVAL; + } + + is_packet_for_us = 0; + switch (ieee->iw_mode) { + case IW_MODE_ADHOC: + /* our BSS and not from/to DS */ + if (memcmp(hdr->addr3, ieee->bssid, ETH_ALEN) == 0) + if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == 0) { + /* promisc: get all */ + if (ieee->dev->flags & IFF_PROMISC) + is_packet_for_us = 1; + /* to us */ + else if (memcmp(hdr->addr1, ieee->dev->dev_addr, ETH_ALEN) == 0) + is_packet_for_us = 1; + /* mcast */ + else if (is_multicast_ether_addr(hdr->addr1)) + is_packet_for_us = 1; + } + break; + case IW_MODE_INFRA: + /* our BSS (== from our AP) and from DS */ + if (memcmp(hdr->addr2, ieee->bssid, ETH_ALEN) == 0) + if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_FROMDS) { + /* promisc: get all */ + if (ieee->dev->flags & IFF_PROMISC) + is_packet_for_us = 1; + /* to us */ + else if (memcmp(hdr->addr1, ieee->dev->dev_addr, ETH_ALEN) == 0) + is_packet_for_us = 1; + /* mcast */ + else if (is_multicast_ether_addr(hdr->addr1)) { + /* not our own packet bcasted from AP */ + if (memcmp(hdr->addr3, ieee->dev->dev_addr, ETH_ALEN)) + is_packet_for_us = 1; + } + } + break; + default: + /* ? */ + break; + } + + if (is_packet_for_us) + return (ieee80211_rx(ieee, skb, stats) ? 0 : -EINVAL); + return 0; +} + #define MGMT_FRAME_FIXED_PART_LENGTH 0x24 static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 }; diff --git a/net/ieee80211/softmac/Kconfig b/net/ieee80211/softmac/Kconfig new file mode 100644 index 000000000000..6cd9f3427be6 --- /dev/null +++ b/net/ieee80211/softmac/Kconfig @@ -0,0 +1,10 @@ +config IEEE80211_SOFTMAC + tristate "Software MAC add-on to the IEEE 802.11 networking stack" + depends on IEEE80211 && EXPERIMENTAL + ---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..c788377f5648 --- /dev/null +++ b/net/ieee80211/softmac/ieee80211softmac_assoc.c @@ -0,0 +1,403 @@ +/* + * 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. + * + */ + +/* Sends out an association request to the desired AP */ +static void +ieee80211softmac_assoc(struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net) +{ + unsigned long flags; + function_enter(); + /* 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); + + dprintk(KERN_INFO PFX "sent association request!\n"); + + /* Change the state to associating */ + spin_lock_irqsave(&mac->lock, flags); + mac->associnfo.associating = 1; + mac->associated = 0; /* just to make sure */ + spin_unlock_irqrestore(&mac->lock, flags); + + /* Set a timer for timeout */ + /* FIXME: make timeout configurable */ + schedule_delayed_work(&mac->associnfo.timeout, 5 * HZ); +} + +void +ieee80211softmac_assoc_timeout(void *d) +{ + struct ieee80211softmac_device *mac = (struct ieee80211softmac_device *)d; + unsigned long flags; + + function_enter(); + + spin_lock_irqsave(&mac->lock, flags); + /* we might race against ieee80211softmac_handle_assoc_response, + * so make sure only one of us does something */ + if (!mac->associnfo.associating) { + spin_unlock_irqrestore(&mac->lock, flags); + return; + } + mac->associnfo.associating = 0; + mac->associnfo.bssvalid = 0; + mac->associated = 0; + spin_unlock_irqrestore(&mac->lock, flags); + + dprintk(KERN_INFO PFX "assoc request timed out!\n"); + /* FIXME: we need to know the network here. that requires a bit of restructuring */ + ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_TIMEOUT, NULL); +} + +/* Sends out a disassociation request to the desired AP */ +static void +ieee80211softmac_disassoc(struct ieee80211softmac_device *mac, u16 reason) +{ + unsigned long flags; + struct ieee80211softmac_network *found; + function_enter(); + + if (mac->associnfo.bssvalid && mac->associated) { + found = ieee80211softmac_get_network_by_bssid(mac, mac->associnfo.bssid); + if (found) + ieee80211softmac_send_mgt_frame(mac, found, IEEE80211_STYPE_DISASSOC, reason); + } else if (mac->associnfo.associating) { + cancel_delayed_work(&mac->associnfo.timeout); + } + + /* Change our state */ + spin_lock_irqsave(&mac->lock, flags); + /* Do NOT clear bssvalid as that will break ieee80211softmac_assoc_work! */ + mac->associated = 0; + mac->associnfo.associating = 0; + spin_unlock_irqrestore(&mac->lock, flags); +} + +static inline int +we_support_all_basic_rates(struct ieee80211softmac_device *mac, u8 *from, u8 from_len) +{ + int idx, search, found; + u8 rate, search_rate; + + for (idx = 0; idx < (from_len); idx++) { + rate = (from)[idx]; + if (!(rate & IEEE80211_BASIC_RATE_MASK)) + continue; + found = 0; + rate &= ~IEEE80211_BASIC_RATE_MASK; + for (search = 0; search < mac->ratesinfo.count; search++) { + search_rate = mac->ratesinfo.rates[search]; + search_rate &= ~IEEE80211_BASIC_RATE_MASK; + if (rate == search_rate) { + found = 1; + break; + } + } + if (!found) + 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; + + /* 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(struct net_device *dev, void *context) +{ + struct ieee80211softmac_device *mac = ieee80211_priv(dev); + ieee80211softmac_assoc_work((void*)mac); +} + +/* This function is called to handle userspace requests (asynchronously) */ +void +ieee80211softmac_assoc_work(void *d) +{ + struct ieee80211softmac_device *mac = (struct ieee80211softmac_device *)d; + struct ieee80211softmac_network *found = NULL; + struct ieee80211_network *net = NULL, *best = NULL; + unsigned long flags; + + function_enter(); + + /* meh */ + if (mac->associated) + ieee80211softmac_disassoc(mac, WLAN_REASON_DISASSOC_STA_HAS_LEFT); + + /* try to find the requested network in our list, if we found one already */ + if (mac->associnfo.bssvalid) + found = ieee80211softmac_get_network_by_bssid(mac, mac->associnfo.bssid); + + /* 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) { + spin_lock_irqsave(&mac->lock, flags); + mac->associnfo.scan_retry--; + spin_unlock_irqrestore(&mac->lock, flags); + + /* 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, NULL); + if (ieee80211softmac_start_scan(mac)) + dprintk(KERN_INFO PFX "Associate: failed to initiate scan. Is device up?\n"); + return; + } + else { + spin_lock_irqsave(&mac->lock, flags); + mac->associnfo.associating = 0; + mac->associated = 0; + spin_unlock_irqrestore(&mac->lock, flags); + + dprintk(KERN_INFO PFX "Unable to find matching network after scan!\n"); + ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_NET_NOT_FOUND, NULL); + return; + } + } + + mac->associnfo.bssvalid = 1; + 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->authenticated) { + /* 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)) { + dprintk(KERN_INFO PFX "cannot associate without being authenticated, requested authentication\n"); + ieee80211softmac_notify_internal(mac, IEEE80211SOFTMAC_EVENT_ANY, found, ieee80211softmac_assoc_notify, NULL, GFP_KERNEL); + } else { + printkl(KERN_WARNING PFX "Not authenticated, but requesting authentication failed. Giving up to associate\n"); + ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_FAILED, found); + } + return; + } + /* finally! now we can start associating */ + ieee80211softmac_assoc(mac, found); +} + +/* 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) +{ + mac->associnfo.associating = 0; + mac->associated = 1; + 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); + + 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_do_not_use) +{ + /* NOTE: the network parameter has to be 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; + + 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); + + 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 ... */ |
