WorkStruct: make allyesconfig
[safe/jmp/linux-2.6] / net / ieee80211 / softmac / ieee80211softmac_wx.c
index bae5fcc..2ffaebd 100644 (file)
@@ -1,11 +1,34 @@
 /*
  * This file contains our _wx handlers. Make sure you EXPORT_SYMBOL_GPL them
+ *
+ * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
+ *                          Joseph Jezak <josejx@gentoo.org>
+ *                          Larry Finger <Larry.Finger@lwfinger.net>
+ *                          Danny van Dyk <kugelfang@gentoo.org>
+ *                          Michael Buesch <mbuesch@freenet.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
  */
 
 #include "ieee80211softmac_priv.h"
 
 #include <net/iw_handler.h>
-
+/* for is_broadcast_ether_addr and is_zero_ether_addr */
+#include <linux/etherdevice.h>
 
 int
 ieee80211softmac_wx_trigger_scan(struct net_device *net_dev,
@@ -19,13 +42,23 @@ ieee80211softmac_wx_trigger_scan(struct net_device *net_dev,
 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_trigger_scan);
 
 
+/* if we're still scanning, return -EAGAIN so that userspace tools
+ * can get the complete scan results, otherwise return 0. */
 int
 ieee80211softmac_wx_get_scan_results(struct net_device *net_dev,
                                     struct iw_request_info *info,
                                     union iwreq_data *data,
                                     char *extra)
 {
+       unsigned long flags;
        struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
+
+       spin_lock_irqsave(&sm->lock, flags);
+       if (sm->scanning) {
+               spin_unlock_irqrestore(&sm->lock, flags);
+               return -EAGAIN;
+       }
+       spin_unlock_irqrestore(&sm->lock, flags);
        return ieee80211_wx_get_scan(sm->ieee, info, data, extra);
 }
 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_scan_results);
@@ -37,30 +70,62 @@ ieee80211softmac_wx_set_essid(struct net_device *net_dev,
                              char *extra)
 {
        struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
+       struct ieee80211softmac_network *n;
+       struct ieee80211softmac_auth_queue_item *authptr;
        int length = 0;
-       unsigned long flags;
-       
-       spin_lock_irqsave(&sm->lock, flags);
-       
+
+       mutex_lock(&sm->associnfo.mutex);
+
+       /* Check if we're already associating to this or another network
+        * If it's another network, cancel and start over with our new network
+        * If it's our network, ignore the change, we're already doing it!
+        */
+       if((sm->associnfo.associating || sm->associnfo.associated) &&
+          (data->essid.flags && data->essid.length)) {
+               /* Get the associating network */
+               n = ieee80211softmac_get_network_by_bssid(sm, sm->associnfo.bssid);
+               if(n && n->essid.len == data->essid.length &&
+                  !memcmp(n->essid.data, extra, n->essid.len)) {
+                       dprintk(KERN_INFO PFX "Already associating or associated to "MAC_FMT"\n",
+                               MAC_ARG(sm->associnfo.bssid));
+                       goto out;
+               } else {
+                       dprintk(KERN_INFO PFX "Canceling existing associate request!\n");
+                       /* Cancel assoc work */
+                       cancel_delayed_work(&sm->associnfo.work);
+                       /* We don't have to do this, but it's a little cleaner */
+                       list_for_each_entry(authptr, &sm->auth_queue, list)
+                               cancel_delayed_work(&authptr->work);
+                       sm->associnfo.bssvalid = 0;
+                       sm->associnfo.bssfixed = 0;
+                       flush_scheduled_work();
+                       sm->associnfo.associating = 0;
+                       sm->associnfo.associated = 0;
+               }
+       }
+
+
        sm->associnfo.static_essid = 0;
+       sm->associnfo.assoc_wait = 0;
 
-       if (data->essid.flags && data->essid.length && extra /*required?*/) {
-               length = min(data->essid.length - 1, IW_ESSID_MAX_SIZE);
+       if (data->essid.flags && data->essid.length) {
+               length = min((int)data->essid.length, IW_ESSID_MAX_SIZE);
                if (length) {
                        memcpy(sm->associnfo.req_essid.data, extra, length);
                        sm->associnfo.static_essid = 1;
                }
        }
-       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;
 
+       sm->associnfo.associating = 1;
        /* queue lower level code to do work (if necessary) */
-       queue_work(sm->workqueue, &sm->associnfo.work);
+       schedule_delayed_work(&sm->associnfo.work, 0);
+out:
+       mutex_unlock(&sm->associnfo.mutex);
 
-       spin_unlock_irqrestore(&sm->lock, flags);
        return 0;
 }
 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_essid);
@@ -72,10 +137,8 @@ ieee80211softmac_wx_get_essid(struct net_device *net_dev,
                              char *extra)
 {
        struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
-       unsigned long flags;
 
-       /* avoid getting inconsistent information */
-       spin_lock_irqsave(&sm->lock, flags);
+       mutex_lock(&sm->associnfo.mutex);
        /* If all fails, return ANY (empty) */
        data->essid.length = 0;
        data->essid.flags = 0;  /* active */
@@ -88,12 +151,13 @@ ieee80211softmac_wx_get_essid(struct net_device *net_dev,
        }
        
        /* If we're associating/associated, return that */
-       if (sm->associated || sm->associnfo.associating) {
+       if (sm->associnfo.associated || sm->associnfo.associating) {
                data->essid.length = sm->associnfo.associate_essid.len;
                data->essid.flags = 1;  /* active */
                memcpy(extra, sm->associnfo.associate_essid.data, sm->associnfo.associate_essid.len);
        }
-       spin_unlock_irqrestore(&sm->lock, flags);
+       mutex_unlock(&sm->associnfo.mutex);
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_essid);
@@ -113,11 +177,15 @@ ieee80211softmac_wx_set_rate(struct net_device *net_dev,
        int err = -EINVAL;
 
        if (in_rate == -1) {
-               /* automatic detect */
-               if (ieee->modulation & IEEE80211_OFDM_MODULATION)
-                       in_rate = 54000000;
-               else
+               /* FIXME: We don't correctly handle backing down to lower
+                  rates, so 801.11g devices start off at 11M for now. People
+                  can manually change it if they really need to, but 11M is
+                  more reliable. Note similar logic in
+                  ieee80211softmac_wx_set_rate() */     
+               if (ieee->modulation & IEEE80211_CCK_MODULATION)
                        in_rate = 11000000;
+               else
+                       in_rate = 54000000;
        }
 
        switch (in_rate) {
@@ -175,8 +243,8 @@ ieee80211softmac_wx_set_rate(struct net_device *net_dev,
        if (is_ofdm && !(ieee->modulation & IEEE80211_OFDM_MODULATION))
                goto out_unlock;
 
-       mac->txrates.default_rate = rate;
-       mac->txrates.default_fallback = lower_rate(mac, rate);
+       mac->txrates.user_rate = rate;
+       ieee80211softmac_recalc_txrates(mac);
        err = 0;
 
 out_unlock:    
@@ -254,15 +322,15 @@ ieee80211softmac_wx_get_wap(struct net_device *net_dev,
 {
        struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
        int err = 0;
-       unsigned long flags;
 
-       spin_lock_irqsave(&mac->lock, flags);
+       mutex_lock(&mac->associnfo.mutex);
        if (mac->associnfo.bssvalid)
                memcpy(data->ap_addr.sa_data, mac->associnfo.bssid, ETH_ALEN);
        else
                memset(data->ap_addr.sa_data, 0xff, ETH_ALEN);
        data->ap_addr.sa_family = ARPHRD_ETHER;
-       spin_unlock_irqrestore(&mac->lock, flags);
+       mutex_unlock(&mac->associnfo.mutex);
+
        return err;
 }
 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_wap);
@@ -274,36 +342,43 @@ ieee80211softmac_wx_set_wap(struct net_device *net_dev,
                            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)) {
-               queue_work(mac->workqueue, &mac->associnfo.work);
-               goto out;
+       mutex_lock(&mac->associnfo.mutex);
+       if (is_broadcast_ether_addr(data->ap_addr.sa_data)) {
+               /* the bssid we have is not to be fixed any longer,
+                * and we should reassociate to the best AP. */
+               mac->associnfo.bssfixed = 0;
+               /* force reassociation */
+               mac->associnfo.bssvalid = 0;
+               if (mac->associnfo.associated)
+                       schedule_delayed_work(&mac->associnfo.work, 0);
+       } else if (is_zero_ether_addr(data->ap_addr.sa_data)) {
+               /* the bssid we have is no longer fixed */
+               mac->associnfo.bssfixed = 0;
         } else {
                if (!memcmp(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN)) {
-                       if (mac->associnfo.associating || mac->associated) {
+                       if (mac->associnfo.associating || mac->associnfo.associated) {
                        /* bssid unchanged and associated or associating - just return */
                                goto out;
                        }
                } else {
                        /* copy new value in data->ap_addr.sa_data to bssid */
                        memcpy(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN);
-               }       
+               }
+               /* tell the other code that this bssid should be used no matter what */
+               mac->associnfo.bssfixed = 1;
                /* queue associate if new bssid or (old one again and not associated) */
-               queue_work(mac->workqueue,&mac->associnfo.work);
+               schedule_delayed_work(&mac->associnfo.work, 0);
         }
 
-out:
-       spin_unlock_irqrestore(&mac->lock, flags);
+ out:
+       mutex_unlock(&mac->associnfo.mutex);
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_wap);
@@ -319,7 +394,8 @@ ieee80211softmac_wx_set_genie(struct net_device *dev,
        int err = 0;
        char *buf;
        int i;
-       
+
+       mutex_lock(&mac->associnfo.mutex);
        spin_lock_irqsave(&mac->lock, flags);
        /* bleh. shouldn't be locked for that kmalloc... */
 
@@ -345,7 +421,7 @@ ieee80211softmac_wx_set_genie(struct net_device *dev,
                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("%.2x", (u8)mac->wpa.IE[i]);
                dprintk("\n");
                mac->wpa.IElen = wrqu->data.length;
        } else {
@@ -357,6 +433,8 @@ ieee80211softmac_wx_set_genie(struct net_device *dev,
 
  out:  
        spin_unlock_irqrestore(&mac->lock, flags);
+       mutex_unlock(&mac->associnfo.mutex);
+
        return err;
 }
 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_genie);
@@ -371,7 +449,8 @@ ieee80211softmac_wx_get_genie(struct net_device *dev,
        unsigned long flags;
        int err = 0;
        int space = wrqu->data.length;
-       
+
+       mutex_lock(&mac->associnfo.mutex);
        spin_lock_irqsave(&mac->lock, flags);
        
        wrqu->data.length = 0;
@@ -384,7 +463,52 @@ ieee80211softmac_wx_get_genie(struct net_device *dev,
                        err = -E2BIG;
        }
        spin_unlock_irqrestore(&mac->lock, flags);
+       mutex_lock(&mac->associnfo.mutex);
+
        return err;
 }
 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_genie);
 
+int
+ieee80211softmac_wx_set_mlme(struct net_device *dev,
+                            struct iw_request_info *info,
+                            union iwreq_data *wrqu,
+                            char *extra)
+{
+       struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+       struct iw_mlme *mlme = (struct iw_mlme *)extra;
+       u16 reason = cpu_to_le16(mlme->reason_code);
+       struct ieee80211softmac_network *net;
+       int err = -EINVAL;
+
+       mutex_lock(&mac->associnfo.mutex);
+
+       if (memcmp(mac->associnfo.bssid, mlme->addr.sa_data, ETH_ALEN)) {
+               printk(KERN_DEBUG PFX "wx_set_mlme: requested operation on net we don't use\n");
+               goto out;
+       }
+
+       switch (mlme->cmd) {
+       case IW_MLME_DEAUTH:
+               net = ieee80211softmac_get_network_by_bssid_locked(mac, mlme->addr.sa_data);
+               if (!net) {
+                       printk(KERN_DEBUG PFX "wx_set_mlme: we should know the net here...\n");
+                       goto out;
+               }
+               return ieee80211softmac_deauth_req(mac, net, reason);
+       case IW_MLME_DISASSOC:
+               ieee80211softmac_send_disassoc_req(mac, reason);
+               mac->associnfo.associated = 0;
+               mac->associnfo.associating = 0;
+               err = 0;
+               goto out;
+       default:
+               err = -EOPNOTSUPP;
+       }
+
+out:
+       mutex_unlock(&mac->associnfo.mutex);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_mlme);