summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@g5.osdl.org>2006-03-23 16:25:49 -0800
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-23 16:25:49 -0800
commitaca361c1a0dc0165ac3148137983cb4b1458b5c1 (patch)
tree3527ff422fe90e97657d5996499fa4a9d30e2d5a /net
parentcec6062037783a762aa5606b06b8bc5c14d9657f (diff)
parent9b7c84899ea6bfc4f8932a83b28db313e7397bd1 (diff)
downloadlinux-aca361c1a0dc0165ac3148137983cb4b1458b5c1.tar.gz
linux-aca361c1a0dc0165ac3148137983cb4b1458b5c1.tar.bz2
linux-aca361c1a0dc0165ac3148137983cb4b1458b5c1.zip
Merge branch 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik/netdev-2.6
* 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik/netdev-2.6: (45 commits) [PATCH] Restore channel setting after scan. [PATCH] hostap: Fix memory leak on PCI probe error path [PATCH] hostap: Remove dead code (duplicated idx != 0) [PATCH] hostap: Fix unlikely read overrun in CIS parsing [PATCH] hostap: Fix double free in prism2_config() error path [PATCH] hostap: Fix ap_add_sta() return value verification [PATCH] hostap: Fix hw reset after CMDCODE_ACCESS_WRITE timeout [PATCH] wireless/airo: cache wireless scans [PATCH] wireless/airo: define default MTU [PATCH] wireless/airo: clean up printk usage to print device name [PATCH] WE-20 for kernel 2.6.16 [PATCH] softmac: remove function_enter() [PATCH] skge: version 1.5 [PATCH] skge: compute available ring buffers [PATCH] skge: dont free skb until multi-part transmit complete [PATCH] skge: multicast statistics fix [PATCH] skge: rx_reuse called twice [PATCH] skge: dont use dev_alloc_skb for rx buffs [PATCH] skge: align receive buffers [PATCH] sky2: dont need to use dev_kfree_skb_any ...
Diffstat (limited to 'net')
-rw-r--r--net/core/rtnetlink.c98
-rw-r--r--net/core/wireless.c911
-rw-r--r--net/ieee80211/Kconfig1
-rw-r--r--net/ieee80211/Makefile1
-rw-r--r--net/ieee80211/ieee80211_rx.c74
-rw-r--r--net/ieee80211/softmac/Kconfig10
-rw-r--r--net/ieee80211/softmac/Makefile9
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_assoc.c396
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_auth.c364
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_event.c159
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_io.c474
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_module.c457
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_priv.h230
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_scan.c244
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_wx.c412
15 files changed, 3751 insertions, 89 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index ae10d3740faa..3fcfa9c59e1f 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -51,6 +51,10 @@
#include <net/sock.h>
#include <net/pkt_sched.h>
#include <net/netlink.h>
+#ifdef CONFIG_NET_WIRELESS_RTNETLINK
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+#endif /* CONFIG_NET_WIRELESS_RTNETLINK */
static DEFINE_MUTEX(rtnl_mutex);
@@ -467,6 +471,17 @@ static int do_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
goto out;
}
+#ifdef CONFIG_NET_WIRELESS_RTNETLINK
+ if (ida[IFLA_WIRELESS - 1]) {
+
+ /* Call Wireless Extensions.
+ * Various stuff checked in there... */
+ err = wireless_rtnetlink_set(dev, RTA_DATA(ida[IFLA_WIRELESS - 1]), ida[IFLA_WIRELESS - 1]->rta_len);
+ if (err)
+ goto out;
+ }
+#endif /* CONFIG_NET_WIRELESS_RTNETLINK */
+
err = 0;
out:
@@ -477,6 +492,83 @@ out:
return err;
}
+#ifdef CONFIG_NET_WIRELESS_RTNETLINK
+static int do_getlink(struct sk_buff *in_skb, struct nlmsghdr* in_nlh, void *arg)
+{
+ struct ifinfomsg *ifm = NLMSG_DATA(in_nlh);
+ struct rtattr **ida = arg;
+ struct net_device *dev;
+ struct ifinfomsg *r;
+ struct nlmsghdr *nlh;
+ int err = -ENOBUFS;
+ struct sk_buff *skb;
+ unsigned char *b;
+ char *iw_buf = NULL;
+ int iw_buf_len = 0;
+
+ if (ifm->ifi_index >= 0)
+ dev = dev_get_by_index(ifm->ifi_index);
+ else
+ return -EINVAL;
+ if (!dev)
+ return -ENODEV;
+
+#ifdef CONFIG_NET_WIRELESS_RTNETLINK
+ if (ida[IFLA_WIRELESS - 1]) {
+
+ /* Call Wireless Extensions. We need to know the size before
+ * we can alloc. Various stuff checked in there... */
+ err = wireless_rtnetlink_get(dev, RTA_DATA(ida[IFLA_WIRELESS - 1]), ida[IFLA_WIRELESS - 1]->rta_len, &iw_buf, &iw_buf_len);
+ if (err)
+ goto out;
+ }
+#endif /* CONFIG_NET_WIRELESS_RTNETLINK */
+
+ /* Create a skb big enough to include all the data.
+ * Some requests are way bigger than 4k... Jean II */
+ skb = alloc_skb((NLMSG_LENGTH(sizeof(*r))) + (RTA_SPACE(iw_buf_len)),
+ GFP_KERNEL);
+ if (!skb)
+ goto out;
+ b = skb->tail;
+
+ /* Put in the message the usual good stuff */
+ nlh = NLMSG_PUT(skb, NETLINK_CB(in_skb).pid, in_nlh->nlmsg_seq,
+ RTM_NEWLINK, sizeof(*r));
+ r = NLMSG_DATA(nlh);
+ r->ifi_family = AF_UNSPEC;
+ r->__ifi_pad = 0;
+ r->ifi_type = dev->type;
+ r->ifi_index = dev->ifindex;
+ r->ifi_flags = dev->flags;
+ r->ifi_change = 0;
+
+ /* Put the wireless payload if it exist */
+ if(iw_buf != NULL)
+ RTA_PUT(skb, IFLA_WIRELESS, iw_buf_len,
+ iw_buf + IW_EV_POINT_OFF);
+
+ nlh->nlmsg_len = skb->tail - b;
+
+ /* Needed ? */
+ NETLINK_CB(skb).dst_pid = NETLINK_CB(in_skb).pid;
+
+ err = netlink_unicast(rtnl, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT);
+ if (err > 0)
+ err = 0;
+out:
+ if(iw_buf != NULL)
+ kfree(iw_buf);
+ dev_put(dev);
+ return err;
+
+rtattr_failure:
+nlmsg_failure:
+ kfree_skb(skb);
+ goto out;
+}
+#endif /* CONFIG_NET_WIRELESS_RTNETLINK */
+
static int rtnetlink_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
{
int idx;
@@ -642,7 +734,11 @@ static void rtnetlink_rcv(struct sock *sk, int len)
static struct rtnetlink_link link_rtnetlink_table[RTM_NR_MSGTYPES] =
{
- [RTM_GETLINK - RTM_BASE] = { .dumpit = rtnetlink_dump_ifinfo },
+ [RTM_GETLINK - RTM_BASE] = {
+#ifdef CONFIG_NET_WIRELESS_RTNETLINK
+ .doit = do_getlink,
+#endif /* CONFIG_NET_WIRELESS_RTNETLINK */
+ .dumpit = rtnetlink_dump_ifinfo },
[RTM_SETLINK - RTM_BASE] = { .doit = do_setlink },
[RTM_GETADDR - RTM_BASE] = { .dumpit = rtnetlink_dump_all },
[RTM_GETROUTE - RTM_BASE] = { .dumpit = rtnetlink_dump_all },
diff --git a/net/core/wireless.c b/net/core/wireless.c
index 2add7ed609e9..81d6995fcfdb 100644
--- a/net/core/wireless.c
+++ b/net/core/wireless.c
@@ -2,7 +2,7 @@
* This file implement the Wireless Extensions APIs.
*
* Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
- * Copyright (c) 1997-2005 Jean Tourrilhes, All Rights Reserved.
+ * Copyright (c) 1997-2006 Jean Tourrilhes, All Rights Reserved.
*
* (As all part of the Linux kernel, this file is GPL)
*/
@@ -65,6 +65,9 @@
* 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)
+ *
+ * v8 - 17.02.06 - Jean II
+ * o RtNetlink requests support (SET/GET)
*/
/***************************** INCLUDES *****************************/
@@ -89,11 +92,13 @@
/* Debugging stuff */
#undef WE_IOCTL_DEBUG /* Debug IOCTL API */
+#undef WE_RTNETLINK_DEBUG /* Debug RtNetlink API */
#undef WE_EVENT_DEBUG /* Debug Event dispatcher */
#undef WE_SPY_DEBUG /* Debug enhanced spy support */
/* Options */
-#define WE_EVENT_NETLINK /* Propagate events using rtnetlink */
+//CONFIG_NET_WIRELESS_RTNETLINK /* Wireless requests over RtNetlink */
+#define WE_EVENT_RTNETLINK /* Propagate events using RtNetlink */
#define WE_SET_EVENT /* Generate an event on some set commands */
/************************* GLOBAL VARIABLES *************************/
@@ -156,13 +161,18 @@ static const struct iw_ioctl_description standard_ioctl[] = {
.header_type = IW_HEADER_TYPE_NULL,
},
[SIOCGIWPRIV - SIOCIWFIRST] = { /* (handled directly by us) */
- .header_type = IW_HEADER_TYPE_NULL,
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = sizeof(struct iw_priv_args),
+ .max_tokens = 16,
+ .flags = IW_DESCR_FLAG_NOMAX,
},
[SIOCSIWSTATS - SIOCIWFIRST] = {
.header_type = IW_HEADER_TYPE_NULL,
},
[SIOCGIWSTATS - SIOCIWFIRST] = { /* (handled directly by us) */
- .header_type = IW_HEADER_TYPE_NULL,
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = sizeof(struct iw_statistics),
.flags = IW_DESCR_FLAG_DUMP,
},
[SIOCSIWSPY - SIOCIWFIRST] = {
@@ -529,6 +539,70 @@ static inline int adjust_priv_size(__u16 args,
return num * iw_priv_type_size[type];
}
+/* ---------------------------------------------------------------- */
+/*
+ * Standard Wireless Handler : get wireless stats
+ * Allow programatic access to /proc/net/wireless even if /proc
+ * doesn't exist... Also more efficient...
+ */
+static int iw_handler_get_iwstats(struct net_device * dev,
+ struct iw_request_info * info,
+ union iwreq_data * wrqu,
+ char * extra)
+{
+ /* Get stats from the driver */
+ struct iw_statistics *stats;
+
+ stats = get_wireless_stats(dev);
+ if (stats != (struct iw_statistics *) NULL) {
+
+ /* Copy statistics to extra */
+ memcpy(extra, stats, sizeof(struct iw_statistics));
+ wrqu->data.length = sizeof(struct iw_statistics);
+
+ /* Check if we need to clear the updated flag */
+ if(wrqu->data.flags != 0)
+ stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
+ return 0;
+ } else
+ return -EOPNOTSUPP;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Standard Wireless Handler : get iwpriv definitions
+ * Export the driver private handler definition
+ * They will be picked up by tools like iwpriv...
+ */
+static int iw_handler_get_private(struct net_device * dev,
+ struct iw_request_info * info,
+ union iwreq_data * wrqu,
+ char * extra)
+{
+ /* Check if the driver has something to export */
+ if((dev->wireless_handlers->num_private_args == 0) ||
+ (dev->wireless_handlers->private_args == NULL))
+ return -EOPNOTSUPP;
+
+ /* Check if there is enough buffer up there */
+ if(wrqu->data.length < dev->wireless_handlers->num_private_args) {
+ /* User space can't know in advance how large the buffer
+ * needs to be. Give it a hint, so that we can support
+ * any size buffer we want somewhat efficiently... */
+ wrqu->data.length = dev->wireless_handlers->num_private_args;
+ return -E2BIG;
+ }
+
+ /* Set the number of available ioctls. */
+ wrqu->data.length = dev->wireless_handlers->num_private_args;
+
+ /* Copy structure to the user buffer. */
+ memcpy(extra, dev->wireless_handlers->private_args,
+ sizeof(struct iw_priv_args) * wrqu->data.length);
+
+ return 0;
+}
+
/******************** /proc/net/wireless SUPPORT ********************/
/*
@@ -630,81 +704,14 @@ int __init wireless_proc_init(void)
/* ---------------------------------------------------------------- */
/*
- * Allow programatic access to /proc/net/wireless even if /proc
- * doesn't exist... Also more efficient...
- */
-static inline int dev_iwstats(struct net_device *dev, struct ifreq *ifr)
-{
- /* Get stats from the driver */
- struct iw_statistics *stats;
-
- stats = get_wireless_stats(dev);
- if (stats != (struct iw_statistics *) NULL) {
- struct iwreq * wrq = (struct iwreq *)ifr;
-
- /* Copy statistics to the user buffer */
- if(copy_to_user(wrq->u.data.pointer, stats,
- sizeof(struct iw_statistics)))
- return -EFAULT;
-
- /* Check if we need to clear the updated flag */
- if(wrq->u.data.flags != 0)
- stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
- return 0;
- } else
- return -EOPNOTSUPP;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Export the driver private handler definition
- * They will be picked up by tools like iwpriv...
- */
-static inline int ioctl_export_private(struct net_device * dev,
- struct ifreq * ifr)
-{
- struct iwreq * iwr = (struct iwreq *) ifr;
-
- /* Check if the driver has something to export */
- if((dev->wireless_handlers->num_private_args == 0) ||
- (dev->wireless_handlers->private_args == NULL))
- return -EOPNOTSUPP;
-
- /* Check NULL pointer */
- if(iwr->u.data.pointer == NULL)
- return -EFAULT;
-
- /* Check if there is enough buffer up there */
- if(iwr->u.data.length < dev->wireless_handlers->num_private_args) {
- /* User space can't know in advance how large the buffer
- * needs to be. Give it a hint, so that we can support
- * any size buffer we want somewhat efficiently... */
- iwr->u.data.length = dev->wireless_handlers->num_private_args;
- return -E2BIG;
- }
-
- /* Set the number of available ioctls. */
- iwr->u.data.length = dev->wireless_handlers->num_private_args;
-
- /* Copy structure to the user buffer. */
- if (copy_to_user(iwr->u.data.pointer,
- dev->wireless_handlers->private_args,
- sizeof(struct iw_priv_args) * iwr->u.data.length))
- return -EFAULT;
-
- return 0;
-}
-
-/* ---------------------------------------------------------------- */
-/*
* Wrapper to call a standard Wireless Extension handler.
* We do various checks and also take care of moving data between
* user space and kernel space.
*/
-static inline int ioctl_standard_call(struct net_device * dev,
- struct ifreq * ifr,
- unsigned int cmd,
- iw_handler handler)
+static int ioctl_standard_call(struct net_device * dev,
+ struct ifreq * ifr,
+ unsigned int cmd,
+ iw_handler handler)
{
struct iwreq * iwr = (struct iwreq *) ifr;
const struct iw_ioctl_description * descr;
@@ -1048,14 +1055,20 @@ int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd)
{
case SIOCGIWSTATS:
/* Get Wireless Stats */
- return dev_iwstats(dev, ifr);
+ return ioctl_standard_call(dev,
+ ifr,
+ cmd,
+ &iw_handler_get_iwstats);
case SIOCGIWPRIV:
/* Check if we have some wireless handlers defined */
if(dev->wireless_handlers != NULL) {
/* We export to user space the definition of
* the private handler ourselves */
- return ioctl_export_private(dev, ifr);
+ return ioctl_standard_call(dev,
+ ifr,
+ cmd,
+ &iw_handler_get_private);
}
// ## Fall-through for old API ##
default:
@@ -1088,16 +1101,739 @@ int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd)
return -EINVAL;
}
+/********************** RTNETLINK REQUEST API **********************/
+/*
+ * The alternate user space API to configure all those Wireless Extensions
+ * is through RtNetlink.
+ * This API support only the new driver API (iw_handler).
+ *
+ * This RtNetlink API use the same query/reply model as the ioctl API.
+ * Maximum effort has been done to fit in the RtNetlink model, and
+ * we support both RtNetlink Set and RtNelink Get operations.
+ * On the other hand, we don't offer Dump operations because of the
+ * following reasons :
+ * o Large number of parameters, most optional
+ * o Large size of some parameters (> 100 bytes)
+ * o Each parameters need to be extracted from hardware
+ * o Scan requests can take seconds and disable network activity.
+ * Because of this high cost/overhead, we want to return only the
+ * parameters the user application is really interested in.
+ * We could offer partial Dump using the IW_DESCR_FLAG_DUMP flag.
+ *
+ * The API uses the standard RtNetlink socket. When the RtNetlink code
+ * find a IFLA_WIRELESS field in a RtNetlink SET_LINK request,
+ * it calls here.
+ */
+
+#ifdef CONFIG_NET_WIRELESS_RTNETLINK
+/* ---------------------------------------------------------------- */
+/*
+ * Wrapper to call a standard Wireless Extension GET handler.
+ * We do various checks and call the handler with the proper args.
+ */
+static int rtnetlink_standard_get(struct net_device * dev,
+ struct iw_event * request,
+ int request_len,
+ iw_handler handler,
+ char ** p_buf,
+ int * p_len)
+{
+ const struct iw_ioctl_description * descr = NULL;
+ unsigned int cmd;
+ union iwreq_data * wrqu;
+ int hdr_len;
+ struct iw_request_info info;
+ char * buffer = NULL;
+ int buffer_size = 0;
+ int ret = -EINVAL;
+
+ /* Get the description of the Request */
+ cmd = request->cmd;
+ if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
+ return -EOPNOTSUPP;
+ descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
+
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Found standard handler for 0x%04X\n",
+ dev->name, cmd);
+ printk(KERN_DEBUG "%s (WE.r) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
+#endif /* WE_RTNETLINK_DEBUG */
+
+ /* Check if wrqu is complete */
+ hdr_len = event_type_size[descr->header_type];
+ if(request_len < hdr_len) {
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG
+ "%s (WE.r) : Wireless request too short (%d)\n",
+ dev->name, request_len);
+#endif /* WE_RTNETLINK_DEBUG */
+ return -EINVAL;
+ }
+
+ /* Prepare the call */
+ info.cmd = cmd;
+ info.flags = 0;
+
+ /* Check if we have extra data in the reply or not */
+ if(descr->header_type != IW_HEADER_TYPE_POINT) {
+
+ /* Create the kernel buffer that we will return.
+ * It's at an offset to match the TYPE_POINT case... */
+ buffer_size = request_len + IW_EV_POINT_OFF;
+ buffer = kmalloc(buffer_size, GFP_KERNEL);
+ if (buffer == NULL) {
+ return -ENOMEM;
+ }
+ /* Copy event data */
+ memcpy(buffer + IW_EV_POINT_OFF, request, request_len);
+ /* Use our own copy of wrqu */
+ wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF
+ + IW_EV_LCP_LEN);
+
+ /* No extra arguments. Trivial to handle */
+ ret = handler(dev, &info, wrqu, NULL);
+
+ } else {
+ union iwreq_data wrqu_point;
+ char * extra = NULL;
+ int extra_size = 0;
+
+ /* Get a temp copy of wrqu (skip pointer) */
+ memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
+ ((char *) request) + IW_EV_LCP_LEN,
+ IW_EV_POINT_LEN - IW_EV_LCP_LEN);
+
+ /* Calculate space needed by arguments. Always allocate
+ * for max space. Easier, and won't last long... */
+ extra_size = descr->max_tokens * descr->token_size;
+ /* Support for very large requests */
+ if((descr->flags & IW_DESCR_FLAG_NOMAX) &&
+ (wrqu_point.data.length > descr->max_tokens))
+ extra_size = (wrqu_point.data.length
+ * descr->token_size);
+ buffer_size = extra_size + IW_EV_POINT_LEN + IW_EV_POINT_OFF;
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n",
+ dev->name, extra_size, buffer_size);
+#endif /* WE_RTNETLINK_DEBUG */
+
+ /* Create the kernel buffer that we will return */
+ buffer = kmalloc(buffer_size, GFP_KERNEL);
+ if (buffer == NULL) {
+ return -ENOMEM;
+ }
+
+ /* Put wrqu in the right place (just before extra).
+ * Leave space for IWE header and dummy pointer...
+ * Note that IW_EV_LCP_LEN==4 bytes, so it's still aligned...
+ */
+ memcpy(buffer + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
+ ((char *) &wrqu_point) + IW_EV_POINT_OFF,
+ IW_EV_POINT_LEN - IW_EV_LCP_LEN);
+ wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_LEN);
+
+ /* Extra comes logically after that. Offset +12 bytes. */
+ extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_LEN;
+
+ /* Call the handler */
+ ret = handler(dev, &info, wrqu, extra);
+
+ /* Calculate real returned length */
+ extra_size = (wrqu->data.length * descr->token_size);
+ /* Re-adjust reply size */
+ request->len = extra_size + IW_EV_POINT_LEN;
+
+ /* Put the iwe header where it should, i.e. scrap the
+ * dummy pointer. */
+ memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_LEN);
+
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Reply 0x%04X, hdr_len %d, tokens %d, extra_size %d, buffer_size %d\n", dev->name, cmd, hdr_len, wrqu->data.length, extra_size, buffer_size);
+#endif /* WE_RTNETLINK_DEBUG */
+
+ /* Check if there is enough buffer up there */
+ if(wrqu_point.data.length < wrqu->data.length)
+ ret = -E2BIG;
+ }
+
+ /* Return the buffer to the caller */
+ if (!ret) {
+ *p_buf = buffer;
+ *p_len = request->len;
+ } else {
+ /* Cleanup */
+ if(buffer)
+ kfree(buffer);
+ }
+
+ return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Wrapper to call a standard Wireless Extension SET handler.
+ * We do various checks and call the handler with the proper args.
+ */
+static inline int rtnetlink_standard_set(struct net_device * dev,
+ struct iw_event * request,
+ int request_len,
+ iw_handler handler)
+{
+ const struct iw_ioctl_description * descr = NULL;
+ unsigned int cmd;
+ union iwreq_data * wrqu;
+ union iwreq_data wrqu_point;
+ int hdr_len;
+ char * extra = NULL;
+ int extra_size = 0;
+ struct iw_request_info info;
+ int ret = -EINVAL;
+
+ /* Get the description of the Request */
+ cmd = request->cmd;
+ if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
+ return -EOPNOTSUPP;
+ descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
+
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Found standard SET handler for 0x%04X\n",
+ dev->name, cmd);
+ printk(KERN_DEBUG "%s (WE.r) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
+#endif /* WE_RTNETLINK_DEBUG */
+
+ /* Extract fixed header from request. This is properly aligned. */
+ wrqu = &request->u;
+
+ /* Check if wrqu is complete */
+ hdr_len = event_type_size[descr->header_type];
+ if(request_len < hdr_len) {
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG
+ "%s (WE.r) : Wireless request too short (%d)\n",
+ dev->name, request_len);
+#endif /* WE_RTNETLINK_DEBUG */
+ return -EINVAL;
+ }
+
+ /* Prepare the call */
+ info.cmd = cmd;
+ info.flags = 0;
+
+ /* Check if we have extra data in the request or not */
+ if(descr->header_type != IW_HEADER_TYPE_POINT) {
+
+ /* No extra arguments. Trivial to handle */
+ ret = handler(dev, &info, wrqu, NULL);
+
+ } else {
+ int extra_len;
+
+ /* Put wrqu in the right place (skip pointer) */
+ memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
+ wrqu, IW_EV_POINT_LEN - IW_EV_LCP_LEN);
+ /* Don't forget about the event code... */
+ wrqu = &wrqu_point;
+
+ /* Check if number of token fits within bounds */
+ if(wrqu_point.data.length > descr->max_tokens)
+ return -E2BIG;
+ if(wrqu_point.data.length < descr->min_tokens)
+ return -EINVAL;
+
+ /* Real length of payload */
+ extra_len = wrqu_point.data.length * descr->token_size;
+
+ /* Check if request is self consistent */
+ if((request_len - hdr_len) < extra_len) {
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n",
+ dev->name, extra_size);
+#endif /* WE_RTNETLINK_DEBUG */
+ return -EINVAL;
+ }
+
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n",
+ dev->name, extra_size);
+#endif /* WE_RTNETLINK_DEBUG */
+
+ /* Always allocate for max space. Easier, and won't last
+ * long... */
+ extra_size = descr->max_tokens * descr->token_size;
+ extra = kmalloc(extra_size, GFP_KERNEL);
+ if (extra == NULL)
+ return -ENOMEM;
+
+ /* Copy extra in aligned buffer */
+ memcpy(extra, ((char *) request) + hdr_len, extra_len);
+
+ /* Call the handler */
+ ret = handler(dev, &info, &wrqu_point, extra);
+ }
+
+#ifdef WE_SET_EVENT
+ /* Generate an event to notify listeners of the change */
+ if((descr->flags & IW_DESCR_FLAG_EVENT) &&
+ ((ret == 0) || (ret == -EIWCOMMIT))) {
+ if(descr->flags & IW_DESCR_FLAG_RESTRICT)
+ /* If the event is restricted, don't
+ * export the payload */
+ wireless_send_event(dev, cmd, wrqu, NULL);
+ else
+ wireless_send_event(dev, cmd, wrqu, extra);
+ }
+#endif /* WE_SET_EVENT */
+
+ /* Cleanup - I told you it wasn't that long ;-) */
+ if(extra)
+ kfree(extra);
+
+ /* Call commit handler if needed and defined */
+ if(ret == -EIWCOMMIT)
+ ret = call_commit_handler(dev);
+
+ return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Wrapper to call a private Wireless Extension GET handler.
+ * Same as above...
+ * It's not as nice and slimline as the standard wrapper. The cause
+ * is struct iw_priv_args, which was not really designed for the
+ * job we are going here.
+ *
+ * IMPORTANT : This function prevent to set and get data on the same
+ * IOCTL and enforce the SET/GET convention. Not doing it would be
+ * far too hairy...
+ * If you need to set and get data at the same time, please don't use
+ * a iw_handler but process it in your ioctl handler (i.e. use the
+ * old driver API).
+ */
+static inline int rtnetlink_private_get(struct net_device * dev,
+ struct iw_event * request,
+ int request_len,
+ iw_handler handler,
+ char ** p_buf,
+ int * p_len)
+{
+ const struct iw_priv_args * descr = NULL;
+ unsigned int cmd;
+ union iwreq_data * wrqu;
+ int hdr_len;
+ struct iw_request_info info;
+ int extra_size = 0;
+ int i;
+ char * buffer = NULL;
+ int buffer_size = 0;
+ int ret = -EINVAL;
+
+ /* Get the description of the Request */
+ cmd = request->cmd;
+ for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
+ if(cmd == dev->wireless_handlers->private_args[i].cmd) {
+ descr = &(dev->wireless_handlers->private_args[i]);
+ break;
+ }
+ if(descr == NULL)
+ return -EOPNOTSUPP;
+
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n",
+ dev->name, cmd);
+ printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n",
+ dev->name, descr->name, descr->set_args, descr->get_args);
+#endif /* WE_RTNETLINK_DEBUG */
+
+ /* Compute the max size of the get arguments */
+ extra_size = get_priv_size(descr->get_args);
+
+ /* Does it fits in wrqu ? */
+ if((descr->get_args & IW_PRIV_SIZE_FIXED) &&
+ (extra_size <= IFNAMSIZ)) {
+ hdr_len = extra_size;
+ extra_size = 0;
+ } else {
+ hdr_len = IW_EV_POINT_LEN;
+ }
+
+ /* Check if wrqu is complete */
+ if(request_len < hdr_len) {
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG
+ "%s (WE.r) : Wireless request too short (%d)\n",
+ dev->name, request_len);
+#endif /* WE_RTNETLINK_DEBUG */
+ return -EINVAL;
+ }
+
+ /* Prepare the call */
+ info.cmd = cmd;
+ info.flags = 0;
+
+ /* Check if we have a pointer to user space data or not. */
+ if(extra_size == 0) {
+
+ /* Create the kernel buffer that we will return.
+ * It's at an offset to match the TYPE_POINT case... */
+ buffer_size = request_len + IW_EV_POINT_OFF;
+ buffer = kmalloc(buffer_size, GFP_KERNEL);
+ if (buffer == NULL) {
+ return -ENOMEM;
+ }
+ /* Copy event data */
+ memcpy(buffer + IW_EV_POINT_OFF, request, request_len);
+ /* Use our own copy of wrqu */
+ wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF
+ + IW_EV_LCP_LEN);
+
+ /* No extra arguments. Trivial to handle */
+ ret = handler(dev, &info, wrqu, (char *) wrqu);
+
+ } else {
+ char * extra;
+
+ /* Buffer for full reply */
+ buffer_size = extra_size + IW_EV_POINT_LEN + IW_EV_POINT_OFF;
+
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n",
+ dev->name, extra_size, buffer_size);
+#endif /* WE_RTNETLINK_DEBUG */
+
+ /* Create the kernel buffer that we will return */
+ buffer = kmalloc(buffer_size, GFP_KERNEL);
+ if (buffer == NULL) {
+ return -ENOMEM;
+ }
+
+ /* Put wrqu in the right place (just before extra).
+ * Leave space for IWE header and dummy pointer...
+ * Note that IW_EV_LCP_LEN==4 bytes, so it's still aligned...
+ */
+ memcpy(buffer + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
+ ((char *) request) + IW_EV_LCP_LEN,
+ IW_EV_POINT_LEN - IW_EV_LCP_LEN);
+ wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_LEN);
+
+ /* Extra comes logically after that. Offset +12 bytes. */
+ extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_LEN;
+
+ /* Call the handler */
+ ret = handler(dev, &info, wrqu, extra);
+
+ /* Adjust for the actual length if it's variable,
+ * avoid leaking kernel bits outside. */
+ if (!(descr->get_args & IW_PRIV_SIZE_FIXED))
+ extra_size = adjust_priv_size(descr->get_args, wrqu);
+ /* Re-adjust reply size */
+ request->len = extra_size + IW_EV_POINT_LEN;
+
+ /* Put the iwe header where it should, i.e. scrap the
+ * dummy pointer. */
+ memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_LEN);
+
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Reply 0x%04X, hdr_len %d, tokens %d, extra_size %d, buffer_size %d\n", dev->name, cmd, hdr_len, wrqu->data.length, extra_size, buffer_size);
+#endif /* WE_RTNETLINK_DEBUG */
+ }
+
+ /* Return the buffer to the caller */
+ if (!ret) {
+ *p_buf = buffer;
+ *p_len = request->len;
+ } else {
+ /* Cleanup */
+ if(buffer)
+ kfree(buffer);
+ }
+
+ return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Wrapper to call a private Wireless Extension SET handler.
+ * Same as above...
+ * It's not as nice and slimline as the standard wrapper. The cause
+ * is struct iw_priv_args, which was not really designed for the
+ * job we are going here.
+ *
+ * IMPORTANT : This function prevent to set and get data on the same
+ * IOCTL and enforce the SET/GET convention. Not doing it would be
+ * far too hairy...
+ * If you need to set and get data at the same time, please don't use
+ * a iw_handler but process it in your ioctl handler (i.e. use the
+ * old driver API).
+ */
+static inline int rtnetlink_private_set(struct net_device * dev,
+ struct iw_event * request,
+ int request_len,
+ iw_handler handler)
+{
+ const struct iw_priv_args * descr = NULL;
+ unsigned int cmd;
+ union iwreq_data * wrqu;
+ union iwreq_data wrqu_point;
+ int hdr_len;
+ char * extra = NULL;
+ int extra_size = 0;
+ int offset = 0; /* For sub-ioctls */
+ struct iw_request_info info;
+ int i;
+ int ret = -EINVAL;
+
+ /* Get the description of the Request */
+ cmd = request->cmd;
+ for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
+ if(cmd == dev->wireless_handlers->private_args[i].cmd) {
+ descr = &(dev->wireless_handlers->private_args[i]);
+ break;
+ }
+ if(descr == NULL)
+ return -EOPNOTSUPP;
+
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n",
+ ifr->ifr_name, cmd);
+ printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n",
+ dev->name, descr->name, descr->set_args, descr->get_args);
+#endif /* WE_RTNETLINK_DEBUG */
+
+ /* Compute the size of the set arguments */
+ /* Check for sub-ioctl handler */
+ if(descr->name[0] == '\0')
+ /* Reserve one int for sub-ioctl index */
+ offset = sizeof(__u32);
+
+ /* Size of set arguments */
+ extra_size = get_priv_size(descr->set_args);
+
+ /* Does it fits in wrqu ? */
+ if((descr->set_args & IW_PRIV_SIZE_FIXED) &&
+ (extra_size <= IFNAMSIZ)) {
+ hdr_len = IW_EV_LCP_LEN + extra_size;
+ extra_size = 0;
+ } else {
+ hdr_len = IW_EV_POINT_LEN;
+ }
+
+ /* Extract fixed header from request. This is properly aligned. */
+ wrqu = &request->u;
+
+ /* Check if wrqu is complete */
+ if(request_len < hdr_len) {
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG
+ "%s (WE.r) : Wireless request too short (%d)\n",
+ dev->name, request_len);
+#endif /* WE_RTNETLINK_DEBUG */
+ return -EINVAL;
+ }
+
+ /* Prepare the call */
+ info.cmd = cmd;
+ info.flags = 0;
+
+ /* Check if we have a pointer to user space data or not. */
+ if(extra_size == 0) {
+
+ /* No extra arguments. Trivial to handle */
+ ret = handler(dev, &info, wrqu, (char *) wrqu);
+
+ } else {
+ int extra_len;
+
+ /* Put wrqu in the right place (skip pointer) */
+ memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
+ wrqu, IW_EV_POINT_LEN - IW_EV_LCP_LEN);
+
+ /* Does it fits within bounds ? */
+ if(wrqu_point.data.length > (descr->set_args &
+ IW_PRIV_SIZE_MASK))
+ return -E2BIG;
+
+ /* Real length of payload */
+ extra_len = adjust_priv_size(descr->set_args, &wrqu_point);
+
+ /* Check if request is self consistent */
+ if((request_len - hdr_len) < extra_len) {
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n",
+ dev->name, extra_size);
+#endif /* WE_RTNETLINK_DEBUG */
+ return -EINVAL;
+ }
+
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n",
+ dev->name, extra_size);
+#endif /* WE_RTNETLINK_DEBUG */
+
+ /* Always allocate for max space. Easier, and won't last
+ * long... */
+ extra = kmalloc(extra_size, GFP_KERNEL);
+ if (extra == NULL)
+ return -ENOMEM;
+
+ /* Copy extra in aligned buffer */
+ memcpy(extra, ((char *) request) + hdr_len, extra_len);
+
+ /* Call the handler */
+ ret = handler(dev, &info, &wrqu_point, extra);
+
+ /* Cleanup - I told you it wasn't that long ;-) */
+ kfree(extra);
+ }
+
+ /* Call commit handler if needed and defined */
+ if(ret == -EIWCOMMIT)
+ ret = call_commit_handler(dev);
+
+ return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Main RtNetlink dispatcher. Called from the main networking code
+ * (do_getlink() in net/core/rtnetlink.c).
+ * Check the type of Request and call the appropriate wrapper...
+ */
+int wireless_rtnetlink_get(struct net_device * dev,
+ char * data,
+ int len,
+ char ** p_buf,
+ int * p_len)
+{
+ struct iw_event * request = (struct iw_event *) data;
+ iw_handler handler;
+
+ /* Check length */
+ if(len < IW_EV_LCP_LEN) {
+ printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n",
+ dev->name, len);
+ return -EINVAL;
+ }
+
+ /* ReCheck length (len may have padding) */
+ if(request->len > len) {
+ printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n",
+ dev->name, request->len, len);
+ return -EINVAL;
+ }
+
+ /* Only accept GET requests in here */
+ if(!IW_IS_GET(request->cmd))
+ return -EOPNOTSUPP;
+
+ /* Special cases */
+ if(request->cmd == SIOCGIWSTATS)
+ /* Get Wireless Stats */
+ return rtnetlink_standard_get(dev,
+ request,
+ request->len,
+ &iw_handler_get_iwstats,
+ p_buf, p_len);
+ if(request->cmd == SIOCGIWPRIV) {
+ /* Check if we have some wireless handlers defined */
+ if(dev->wireless_handlers == NULL)
+ return -EOPNOTSUPP;
+ /* Get Wireless Stats */
+ return rtnetlink_standard_get(dev,
+ request,
+ request->len,
+ &iw_handler_get_private,
+ p_buf, p_len);
+ }
+
+ /* Basic check */
+ if (!netif_device_present(dev))
+ return -ENODEV;
+
+ /* Try to find the handler */
+ handler = get_handler(dev, request->cmd);
+ if(handler != NULL) {
+ /* Standard and private are not the same */
+ if(request->cmd < SIOCIWFIRSTPRIV)
+ return rtnetlink_standard_get(dev,
+ request,
+ request->len,
+ handler,
+ p_buf, p_len);
+ else
+ return rtnetlink_private_get(dev,
+ request,
+ request->len,
+ handler,
+ p_buf, p_len);
+ }
+
+ return -EOPNOTSUPP;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Main RtNetlink dispatcher. Called from the main networking code
+ * (do_setlink() in net/core/rtnetlink.c).
+ * Check the type of Request and call the appropriate wrapper...
+ */
+int wireless_rtnetlink_set(struct net_device * dev,
+ char * data,
+ int len)
+{
+ struct iw_event * request = (struct iw_event *) data;
+ iw_handler handler;
+
+ /* Check length */
+ if(len < IW_EV_LCP_LEN) {
+ printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n",
+ dev->name, len);
+ return -EINVAL;
+ }
+
+ /* ReCheck length (len may have padding) */
+ if(request->len > len) {
+ printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n",
+ dev->name, request->len, len);
+ return -EINVAL;
+ }
+
+ /* Only accept SET requests in here */
+ if(!IW_IS_SET(request->cmd))
+ return -EOPNOTSUPP;
+
+ /* Basic check */
+ if (!netif_device_present(dev))
+ return -ENODEV;
+
+ /* New driver API : try to find the handler */
+ handler = get_handler(dev, request->cmd);
+ if(handler != NULL) {
+ /* Standard and private are not the same */
+ if(request->cmd < SIOCIWFIRSTPRIV)
+ return rtnetlink_standard_set(dev,
+ request,
+ request->len,
+ handler);
+ else
+ return rtnetlink_private_set(dev,
+ request,
+ request->len,
+ handler);
+ }
+
+ return -EOPNOTSUPP;
+}
+#endif /* CONFIG_NET_WIRELESS_RTNETLINK */
+
+
/************************* EVENT PROCESSING *************************/
/*
* Process events generated by the wireless layer or the driver.
* Most often, the event will be propagated through rtnetlink
*/
-#ifdef WE_EVENT_NETLINK
-/* "rtnl" is defined in net/core/rtnetlink.c, but we need it here.
- * It is declared in <linux/rtnetlink.h> */
-
+#ifdef WE_EVENT_RTNETLINK
/* ---------------------------------------------------------------- */
/*
* Fill a rtnetlink message with our event data.
@@ -1121,12 +1857,11 @@ static inline int rtnetlink_fill_iwinfo(struct sk_buff * skb,
r->__ifi_pad = 0;
r->ifi_type = dev->type;
r->ifi_index = dev->ifindex;
- r->ifi_flags = dev->flags;
+ r->ifi_flags = dev_get_flags(dev);
r->ifi_change = 0; /* Wireless changes don't affect those flags */
/* Add the wireless events in the netlink packet */
- RTA_PUT(skb, IFLA_WIRELESS,
- event_len, event);
+ RTA_PUT(skb, IFLA_WIRELESS, event_len, event);
nlh->nlmsg_len = skb->tail - b;
return skb->len;
@@ -1163,7 +1898,7 @@ static inline void rtmsg_iwinfo(struct net_device * dev,
NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
netlink_broadcast(rtnl, skb, 0, RTNLGRP_LINK, GFP_ATOMIC);
}
-#endif /* WE_EVENT_NETLINK */
+#endif /* WE_EVENT_RTNETLINK */
/* ---------------------------------------------------------------- */
/*
@@ -1255,10 +1990,10 @@ void wireless_send_event(struct net_device * dev,
if(extra != NULL)
memcpy(((char *) event) + hdr_len, extra, extra_len);
-#ifdef WE_EVENT_NETLINK
- /* rtnetlink event channel */
+#ifdef WE_EVENT_RTNETLINK
+ /* Send via the RtNetlink event channel */
rtmsg_iwinfo(dev, (char *) event, event_len);
-#endif /* WE_EVENT_NETLINK */
+#endif /* WE_EVENT_RTNETLINK */
/* Cleanup */
kfree(event);
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..be61de78dfa4
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_assoc.c
@@ -0,0 +1,396 @@
+/*
+ * 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;
+
+ /* 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;
+
+ 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;
+
+ 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;
+
+ /* 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 ... */
+ network->auth_desynced_once = 1;
+ schedule_work(&mac->associnfo.work);
+ break;
+ }
+ default:
+ dprintk(KERN_INFO PFX "associating failed (reason: 0x%x)!\n", status);
+ mac->associnfo.associating = 0;
+ mac->associnfo.bssvalid = 0;
+ mac->associated = 0;
+ ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_FAILED, network);
+ }
+
+ spin_unlock_irqrestore(&mac->lock, flags);
+ return 0;
+}
+
+int
+ieee80211softmac_handle_disassoc(struct net_device * dev,
+ struct ieee80211_disassoc *disassoc)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ unsigned long flags;
+ 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");
+ netif_carrier_off(dev);
+ spin_lock_irqsave(&mac->lock, flags);
+ mac->associnfo.bssvalid = 0;
+ mac->associated = 0;
+ schedule_work(&mac->associnfo.work);
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ 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;
+
+ network = ieee80211softmac_get_network_by_bssid(mac, resp->header.addr3);
+ if (!network) {
+ dprintkl(KERN_INFO PFX "reassoc request from unknown network\n");
+ return 0;
+ }
+ ieee80211softmac_assoc(mac, network);
+ return 0;
+}
diff --git a/net/ieee80211/softmac/ieee80211softmac_auth.c b/net/ieee80211/softmac/ieee80211softmac_auth.c
new file mode 100644
index 000000000000..9a0eac6c61eb
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_auth.c
@@ -0,0 +1,364 @@
+/*
+ * 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(void *data);
+
+/* 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)
+ return 0;
+
+ /* 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_WORK(&auth->work, &ieee80211softmac_auth_queue, (void *)auth);
+
+ /* Lock (for list) */
+ spin_lock_irqsave(&mac->lock, flags);
+
+ /* add to list */
+ list_add_tail(&auth->list, &mac->auth_queue);
+ schedule_work(&auth->work);
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ return 0;
+}
+
+
+/* Sends an auth request to the desired AP and handles timeouts */
+static void
+ieee80211softmac_auth_queue(void *data)
+{
+ struct ieee80211softmac_device *mac;
+ struct ieee80211softmac_auth_queue_item *auth;
+ struct ieee80211softmac_network *net;
+ unsigned long flags;
+
+ auth = (struct ieee80211softmac_auth_queue_item *)data;
+ 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);
+ net->authenticated = 0;
+ net->authenticating = 1;
+ /* add a timeout call so we eventually give up waiting for an auth reply */
+ schedule_delayed_work(&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);
+ 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);
+}
+
+/* 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;
+
+ /* 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)
+ {
+ printkl(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)
+ {
+ printkl(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 */
+ printkl(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);
+
+ printkl(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){
+ printkl(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;
+ if(net->challenge != NULL)
+ kfree(net->challenge);
+ net->challenge = kmalloc(net->challenge_len, GFP_ATOMIC);
+ memcpy(net->challenge, data, net->challenge_len);
+ aq->state = IEEE80211SOFTMAC_AUTH_SHARED_RESPONSE;
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ /* Switch to correct channel for this network */
+ mac->set_channel(mac->dev, net->channel);
+
+ /* Send our response (How to encrypt?) */
+ ieee80211softmac_send_mgt_frame(mac, aq->net, IEEE80211_STYPE_AUTH, aq->state);
+ break;
+ case IEEE80211SOFTMAC_AUTH_SHARED_PASS:
+ /* 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);
+ printkl(KERN_NOTICE PFX "Shared Key Authentication completed with "MAC_FMT"\n",
+ MAC_ARG(net->bssid));
+ break;
+ default:
+ printkl(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;
+
+ /* 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);
+ /* let's try to re-associate */
+ schedule_work(&mac->associnfo.work);
+ spin_unlock_irqrestore(&mac->lock, flags);
+}
+
+/*
+ * 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)
+ {
+ printkl(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 (!deauth) {
+ dprintk("deauth without deauth packet. eek!\n");
+ return 0;
+ }
+
+ net = ieee80211softmac_get_network_by_bssid(mac, deauth->header.addr2);
+
+ if (net == NULL) {
+ printkl(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)
+ {
+ printkl(KERN_DEBUG PFX "Can't perform deauthentication, network is not authenticated.\n");
+ /* Error okay? */
+ return -EPERM;
+ }
+
+ ieee80211softmac_deauth_from_net(mac, net);
+ return 0;
+}
diff --git a/net/ieee80211/softmac/ieee80211softmac_event.c b/net/ieee80211/softmac/ieee80211softmac_event.c
new file mode 100644
index 000000000000..0a52bbda1e4c
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_event.c
@@ -0,0 +1,159 @@
+/*
+ * 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: no special meaning
+ * 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] = {
+ "scan finished",
+ "associated",
+ "associating failed",
+ "associating timed out",
+ "authenticated",
+ "authenticating failed",
+ "authenticating timed out",
+ "associating failed because no suitable network was found",
+};
+
+
+static void
+ieee80211softmac_notify_callback(void *d)
+{
+ struct ieee80211softmac_event event = *(struct ieee80211softmac_event*) d;
+ kfree(d);
+
+ event.fun(event.mac->dev, 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_WORK(&eventptr->work, ieee80211softmac_notify_callback, eventptr);
+ 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;
+ union iwreq_data wrqu;
+ char *msg;
+
+ if (event >= 0) {
+ msg = event_descriptions[event];
+ wrqu.data.length = strlen(msg);
+ wireless_send_event(mac->dev, IWEVCUSTOM, &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);
+ schedule_work(&eventptr->work);
+ }
+ }
+}
+
+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..febc51dbb412
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_io.c
@@ -0,0 +1,474 @@
+/*
+ * 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 = kmalloc(size, GFP_ATOMIC);
+ memset(data, 0, size);
+ 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? */
+}
+
+
+/*****************************************************************************
+ * 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 capability Info */
+ (*pkt)->capability = (mac->ieee->iw_mode == IW_MODE_MASTER) || (mac->ieee->iw_mode == IW_MODE_INFRA) ?
+ cpu_to_le16(WLAN_CAPABILITY_ESS) :
+ cpu_to_le16(WLAN_CAPABILITY_IBSS);
+ /* Need to add this
+ (*pkt)->capability |= mac->ieee->short_slot ?
+ cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME) : 0;
+ */
+ (*pkt)->capability |= mac->ieee->sec.level ? cpu_to_le16(WLAN_CAPABILITY_PRIVACY) : 0;
+ /* 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 capability Info */
+ (*pkt)->capability = mac->ieee->iw_mode == IW_MODE_MASTER ?
+ cpu_to_le16(WLAN_CAPABILITY_ESS) :
+ cpu_to_le16(WLAN_CAPABILITY_IBSS);
+ /*
+ (*pkt)->capability |= mac->ieee->short_slot ?
+ cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME) : 0;
+ */
+ (*pkt)->capability |= mac->ieee->sec.level ?
+ cpu_to_le16(WLAN_CAPABILITY_PRIVACY) : 0;
+
+ /* 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)
+{
+ u8 *data;
+ /* Allocate Packet */
+ (*pkt) = (struct ieee80211_auth *)ieee80211softmac_alloc_mgt(
+ 2 + /* Auth Algorithm */
+ 2 + /* Auth Transaction Seq */
+ 2 + /* Status Code */
+ /* Challenge Text IE */
+ mac->ieee->open_wep ? 0 :
+ 1 + 1 + WLAN_AUTH_CHALLENGE_LEN
+ );
+ if (unlikely((*pkt) == NULL))
+ return 0;
+ ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_AUTH, net->bssid, net->bssid);
+
+ /* Algorithm */
+ (*pkt)->algorithm = mac->ieee->open_wep ?
+ cpu_to_le16(WLAN_AUTH_OPEN) :
+ cpu_to_le16(WLAN_AUTH_SHARED_KEY);
+ /* Transaction */
+ (*pkt)->transaction = cpu_to_le16(transaction);
+ /* Status */
+ (*pkt)->status = cpu_to_le16(status);
+
+ data = (u8 *)(*pkt)->info_element;
+ /* Challenge Text */
+ if(!mac->ieee->open_wep){
+ *data = MFIE_TYPE_CHALLENGE;
+ data++;
+
+ /* Copy the challenge in */
+ // *data = challenge length
+ // data += sizeof(u16);
+ // memcpy(data, challenge, challenge length);
+ // data += challenge length;
+
+ /* Add the full size to the packet length */
+ }
+
+ /* 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;
+
+ 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));
+ 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, pkt_size);
+
+ kfree(pkt);
+ return 0;
+}
+
+
+/* Create an rts/cts frame */
+static u32
+ieee80211softmac_rts_cts(struct ieee80211_hdr_2addr **pkt,
+ struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net,
+ u32 type)
+{
+ /* Allocate Packet */
+ (*pkt) = kmalloc(IEEE80211_2ADDR_LEN, GFP_ATOMIC);
+ memset(*pkt, 0, IEEE80211_2ADDR_LEN);
+ if((*pkt) == NULL)
+ return 0;
+ ieee80211softmac_hdr_2addr(mac, (*pkt), type, net->bssid);
+ return IEEE80211_2ADDR_LEN;
+}
+
+
+/* Sends a control packet */
+static int
+ieee80211softmac_send_ctl_frame(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *net, u32 type, u32 arg)
+{
+ void *pkt = NULL;
+ u32 pkt_size = 0;
+
+ switch(type) {
+ case IEEE80211_STYPE_RTS:
+ case IEEE80211_STYPE_CTS:
+ pkt_size = ieee80211softmac_rts_cts((struct ieee80211_hdr_2addr **)(&pkt), mac, net, type);
+ break;
+ default:
+ printkl(KERN_DEBUG PFX "Unsupported Control Frame type: %i\n", type);
+ return -EINVAL;
+ }
+
+ if(pkt_size == 0)
+ return -ENOMEM;
+
+ /* Send the packet to the ieee80211 layer for tx */
+ ieee80211_tx_frame(mac->ieee, (struct ieee80211_hdr *) pkt, pkt_size);
+
+ kfree(pkt);
+ return 0;
+}
diff --git a/net/ieee80211/softmac/ieee80211softmac_module.c b/net/ieee80211/softmac/ieee80211softmac_module.c
new file mode 100644
index 000000000000..6f99f781bff8
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_module.c
@@ -0,0 +1,457 @@
+/*
+ * 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>
+
+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);
+ softmac = ieee80211_priv(dev);
+ 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->scaninfo = NULL;
+
+ /* 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);
+
+ INIT_WORK(&softmac->associnfo.work, ieee80211softmac_assoc_work, softmac);
+ INIT_WORK(&softmac->associnfo.timeout, ieee80211softmac_assoc_timeout, softmac);
+ softmac->start_scan = ieee80211softmac_start_scan_implementation;
+ softmac->wait_for_scan = ieee80211softmac_wait_for_scan_implementation;
+ softmac->stop_scan = ieee80211softmac_stop_scan_implementation;
+
+ //TODO: The mcast rate has to be assigned dynamically somewhere (in scanning, association. Not sure...)
+ // It has to be set to the highest rate all stations in the current network can handle.
+ softmac->txrates.mcast_rate = IEEE80211_CCK_RATE_1MB;
+ softmac->txrates.mcast_fallback = IEEE80211_CCK_RATE_1MB;
+ /* This is reassigned in ieee80211softmac_start to sane values. */
+ softmac->txrates.default_rate = IEEE80211_CCK_RATE_1MB;
+ softmac->txrates.default_fallback = IEEE80211_CCK_RATE_1MB;
+
+ /* 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);
+ /* 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;
+ ri->rates[ri->count++] = IEEE80211_CCK_RATE_2MB;
+ ri->rates[ri->count++] = IEEE80211_CCK_RATE_5MB;
+ ri->rates[ri->count++] = IEEE80211_CCK_RATE_11MB;
+ }
+ 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;
+ }
+}
+
+void ieee80211softmac_start(struct net_device *dev)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ struct ieee80211_device *ieee = mac->ieee;
+ u32 change = 0;
+ struct ieee80211softmac_txrates oldrates;
+
+ ieee80211softmac_start_check_rates(mac);
+
+ /* 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 (mac->txrates_change)
+ oldrates = mac->txrates;
+ if (ieee->modulation & IEEE80211_OFDM_MODULATION) {
+ mac->txrates.default_rate = IEEE80211_OFDM_RATE_54MB;
+ change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
+ mac->txrates.default_fallback = IEEE80211_OFDM_RATE_24MB;
+ change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
+ } else if (ieee->modulation & IEEE80211_CCK_MODULATION) {
+ mac->txrates.default_rate = IEEE80211_CCK_RATE_11MB;
+ change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
+ mac->txrates.default_fallback = IEEE80211_CCK_RATE_5MB;
+ change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
+ } else
+ assert(0);
+ if (mac->txrates_change)
+ mac->txrates_change(dev, change, &oldrates);
+}
+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)
+{
+ struct ieee80211softmac_txrates oldrates;
+ 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. */
+ if (mac->txrates_change)
+ memcpy(&oldrates, &mac->txrates, sizeof(oldrates));
+ 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. */
+ if (mac->txrates_change)
+ memcpy(&oldrates, &mac->txrates, sizeof(oldrates));
+ 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, &oldrates);
+}
+
+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->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);
+
+ 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;
+}
+
+/* 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 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;
+}
+
+/* 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..9ba7dbd161eb
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_priv.h
@@ -0,0 +1,230 @@
+/*
+ * 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: "
+
+#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
+
+/* private definitions and prototypes */
+
+/*** prototypes from _scan.c */
+void ieee80211softmac_scan(void *sm);
+/* 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);
+
+/*** 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_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 */
+u8 ieee80211softmac_lower_rate_delta(struct ieee80211softmac_device *mac, u8 rate, int delta);
+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);
+
+/*** 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(void *d);
+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(void *d);
+
+/* 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/2
+#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 channel;
+ struct ieee80211softmac_essid essid;
+
+ struct ieee80211softmac_ratesinfo supported_rates;
+
+ /* SoftMAC specific */
+ u16 authenticating:1, /* Status Flags */
+ authenticated:1,
+ auth_desynced_once:1;
+
+ 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 work_struct 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 work_struct softmac_scan;
+};
+
+/* private event struct */
+struct ieee80211softmac_event {
+ struct list_head list;
+ int event_type;
+ void *event_context;
+ struct work_struct 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);
+
+#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..bb9ab8b45d09
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_scan.c
@@ -0,0 +1,244 @@
+/*
+ * 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)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&sm->lock, flags);
+ if (sm->scanning)
+ {
+ spin_unlock_irqrestore(&sm->lock, flags);
+ return -EINPROGRESS;
+ }
+ sm->scanning = 1;
+ 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(void *d)
+{
+ int invalid_channel;
+ u8 current_channel_idx;
+ struct ieee80211softmac_device *sm = (struct ieee80211softmac_device *)d;
+ struct ieee80211softmac_scaninfo *si = sm->scaninfo;
+ unsigned long 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) {
+ 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);
+ schedule_delayed_work(&si->softmac_scan, IEEE80211SOFTMAC_PROBE_DELAY);
+ 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\n");
+ 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_WORK(&info->softmac_scan, ieee80211softmac_scan, 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;
+ }
+ dprintk(PFX "Start scanning with channel: %d\n", sm->scaninfo->channels[0].channel);
+ dprintk(PFX "Scanning %d channels\n", sm->scaninfo->number_channels);
+ sm->scaninfo->current_channel_idx = 0;
+ sm->scaninfo->started = 1;
+ sm->scaninfo->stop = 0;
+ INIT_COMPLETION(sm->scaninfo->finished);
+ schedule_work(&sm->scaninfo->softmac_scan);
+ 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(sm, sm->associnfo.bssid);
+ if (net)
+ sm->set_channel(sm->dev, net->channel);
+ }
+ ieee80211softmac_call_events(sm, IEEE80211SOFTMAC_EVENT_SCAN_FINISHED, NULL);
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_scan_finished);
diff --git a/net/ieee80211/softmac/ieee80211softmac_wx.c b/net/ieee80211/softmac/ieee80211softmac_wx.c
new file mode 100644
index 000000000000..e1a9bc6d36ff
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_wx.c
@@ -0,0 +1,412 @@
+/*
+ * 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>
+
+
+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);
+
+
+int
+ieee80211softmac_wx_get_scan_results(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 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);
+ int length = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sm->lock, flags);
+
+ sm->associnfo.static_essid = 0;
+
+ if (data->essid.flags && data->essid.length && extra /*required?*/) {
+ length = min(data->essid.length - 1, IW_ESSID_MAX_SIZE);
+ if (length) {
+ memcpy(sm->associnfo.req_essid.data, extra, length);
+ sm->associnfo.static_essid = 1;
+ }
+ }
+ sm->associnfo.scan_retry = IEEE80211SOFTMAC_ASSOC_SCAN_RETRY_LIMIT;
+
+ /* set our requested ESSID length.
+ * If applicable, we have already copied the data in */
+ sm->associnfo.req_essid.len = length;
+
+ /* queue lower level code to do work (if necessary) */
+ schedule_work(&sm->associnfo.work);
+
+ spin_unlock_irqrestore(&sm->lock, flags);
+ 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);
+ unsigned long flags;
+
+ /* avoid getting inconsistent information */
+ spin_lock_irqsave(&sm->lock, flags);
+ /* 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->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);
+ }
+ spin_unlock_irqrestore(&sm->lock, flags);
+ 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) {
+ /* automatic detect */
+ if (ieee->modulation & IEEE80211_OFDM_MODULATION)
+ in_rate = 54000000;
+ else
+ in_rate = 11000000;
+ }
+
+ 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.default_rate = rate;
+ mac->txrates.default_fallback = lower_rate(mac, rate);
+ 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);
+ 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;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mac->lock, flags);
+ 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;
+ spin_unlock_irqrestore(&mac->lock, flags);
+ 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);
+ static const unsigned char any[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ static const unsigned char off[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ unsigned long flags;
+
+ /* sanity check */
+ if (data->ap_addr.sa_family != ARPHRD_ETHER) {
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&mac->lock, flags);
+ if (!memcmp(any, data->ap_addr.sa_data, ETH_ALEN) ||
+ !memcmp(off, data->ap_addr.sa_data, ETH_ALEN)) {
+ schedule_work(&mac->associnfo.work);
+ goto out;
+ } else {
+ if (!memcmp(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN)) {
+ if (mac->associnfo.associating || mac->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);
+ }
+ /* queue associate if new bssid or (old one again and not associated) */
+ schedule_work(&mac->associnfo.work);
+ }
+
+out:
+ spin_unlock_irqrestore(&mac->lock, flags);
+ 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;
+
+ 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", 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);
+ 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;
+
+ 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);
+ return err;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_genie);
+