Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[linux-block.git] / drivers / net / wireless / orinoco / main.c
index a370e510f19f8e3800f8f30d55eda1587032bbf6..e8c550a61f331caa8035e82aa890ed6bd58326c0 100644 (file)
@@ -80,6 +80,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/delay.h>
+#include <linux/device.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/ethtool.h>
@@ -88,6 +89,7 @@
 #include <linux/wireless.h>
 #include <linux/ieee80211.h>
 #include <net/iw_handler.h>
+#include <net/cfg80211.h>
 
 #include "hermes_rid.h"
 #include "hermes_dld.h"
@@ -96,6 +98,7 @@
 #include "mic.h"
 #include "fw.h"
 #include "wext.h"
+#include "cfg.h"
 #include "main.h"
 
 #include "orinoco.h"
@@ -142,13 +145,11 @@ static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
 #define ORINOCO_MIN_MTU                256
 #define ORINOCO_MAX_MTU                (IEEE80211_MAX_DATA_LEN - ENCAPS_OVERHEAD)
 
-#define SYMBOL_MAX_VER_LEN     (14)
 #define MAX_IRQLOOPS_PER_IRQ   10
 #define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* Based on a guestimate of
                                            * how many events the
                                            * device could
                                            * legitimately generate */
-#define TX_NICBUF_SIZE_BUG     1585            /* Bug in Symbol firmware */
 
 #define DUMMY_FID              0xFFFF
 
@@ -205,11 +206,21 @@ struct orinoco_rx_data {
        struct list_head list;
 };
 
+struct orinoco_scan_data {
+       void *buf;
+       size_t len;
+       int type;
+       struct list_head list;
+};
+
 /********************************************************************/
 /* Function prototypes                                              */
 /********************************************************************/
 
-static void __orinoco_set_multicast_list(struct net_device *dev);
+static int __orinoco_set_multicast_list(struct net_device *dev);
+static int __orinoco_up(struct orinoco_private *priv);
+static int __orinoco_down(struct orinoco_private *priv);
+static int __orinoco_commit(struct orinoco_private *priv);
 
 /********************************************************************/
 /* Internal helper functions                                        */
@@ -218,11 +229,11 @@ static void __orinoco_set_multicast_list(struct net_device *dev);
 void set_port_type(struct orinoco_private *priv)
 {
        switch (priv->iw_mode) {
-       case IW_MODE_INFRA:
+       case NL80211_IFTYPE_STATION:
                priv->port_type = 1;
                priv->createibss = 0;
                break;
-       case IW_MODE_ADHOC:
+       case NL80211_IFTYPE_ADHOC:
                if (priv->prefer_port3) {
                        priv->port_type = 3;
                        priv->createibss = 0;
@@ -231,7 +242,7 @@ void set_port_type(struct orinoco_private *priv)
                        priv->createibss = 1;
                }
                break;
-       case IW_MODE_MONITOR:
+       case NL80211_IFTYPE_MONITOR:
                priv->port_type = 3;
                priv->createibss = 0;
                break;
@@ -247,14 +258,14 @@ void set_port_type(struct orinoco_private *priv)
 
 static int orinoco_open(struct net_device *dev)
 {
-       struct orinoco_private *priv = netdev_priv(dev);
+       struct orinoco_private *priv = ndev_priv(dev);
        unsigned long flags;
        int err;
 
        if (orinoco_lock(priv, &flags) != 0)
                return -EBUSY;
 
-       err = __orinoco_up(dev);
+       err = __orinoco_up(priv);
 
        if (!err)
                priv->open = 1;
@@ -266,7 +277,7 @@ static int orinoco_open(struct net_device *dev)
 
 static int orinoco_stop(struct net_device *dev)
 {
-       struct orinoco_private *priv = netdev_priv(dev);
+       struct orinoco_private *priv = ndev_priv(dev);
        int err = 0;
 
        /* We mustn't use orinoco_lock() here, because we need to be
@@ -276,7 +287,7 @@ static int orinoco_stop(struct net_device *dev)
 
        priv->open = 0;
 
-       err = __orinoco_down(dev);
+       err = __orinoco_down(priv);
 
        spin_unlock_irq(&priv->lock);
 
@@ -285,14 +296,14 @@ static int orinoco_stop(struct net_device *dev)
 
 static struct net_device_stats *orinoco_get_stats(struct net_device *dev)
 {
-       struct orinoco_private *priv = netdev_priv(dev);
+       struct orinoco_private *priv = ndev_priv(dev);
 
        return &priv->stats;
 }
 
 static void orinoco_set_multicast_list(struct net_device *dev)
 {
-       struct orinoco_private *priv = netdev_priv(dev);
+       struct orinoco_private *priv = ndev_priv(dev);
        unsigned long flags;
 
        if (orinoco_lock(priv, &flags) != 0) {
@@ -307,7 +318,7 @@ static void orinoco_set_multicast_list(struct net_device *dev)
 
 static int orinoco_change_mtu(struct net_device *dev, int new_mtu)
 {
-       struct orinoco_private *priv = netdev_priv(dev);
+       struct orinoco_private *priv = ndev_priv(dev);
 
        if ((new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU))
                return -EINVAL;
@@ -328,7 +339,7 @@ static int orinoco_change_mtu(struct net_device *dev, int new_mtu)
 
 static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
 {
-       struct orinoco_private *priv = netdev_priv(dev);
+       struct orinoco_private *priv = ndev_priv(dev);
        struct net_device_stats *stats = &priv->stats;
        hermes_t *hw = &priv->hw;
        int err = 0;
@@ -355,7 +366,8 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
                return NETDEV_TX_BUSY;
        }
 
-       if (!netif_carrier_ok(dev) || (priv->iw_mode == IW_MODE_MONITOR)) {
+       if (!netif_carrier_ok(dev) ||
+           (priv->iw_mode == NL80211_IFTYPE_MONITOR)) {
                /* Oops, the firmware hasn't established a connection,
                   silently drop the packet (this seems to be the
                   safest approach). */
@@ -518,7 +530,7 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
 
 static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw)
 {
-       struct orinoco_private *priv = netdev_priv(dev);
+       struct orinoco_private *priv = ndev_priv(dev);
        u16 fid = hermes_read_regn(hw, ALLOCFID);
 
        if (fid != priv->txfid) {
@@ -533,7 +545,7 @@ static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw)
 
 static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw)
 {
-       struct orinoco_private *priv = netdev_priv(dev);
+       struct orinoco_private *priv = ndev_priv(dev);
        struct net_device_stats *stats = &priv->stats;
 
        stats->tx_packets++;
@@ -545,7 +557,7 @@ static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw)
 
 static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw)
 {
-       struct orinoco_private *priv = netdev_priv(dev);
+       struct orinoco_private *priv = ndev_priv(dev);
        struct net_device_stats *stats = &priv->stats;
        u16 fid = hermes_read_regn(hw, TXCOMPLFID);
        u16 status;
@@ -601,7 +613,7 @@ static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw)
 
 static void orinoco_tx_timeout(struct net_device *dev)
 {
-       struct orinoco_private *priv = netdev_priv(dev);
+       struct orinoco_private *priv = ndev_priv(dev);
        struct net_device_stats *stats = &priv->stats;
        struct hermes *hw = &priv->hw;
 
@@ -650,7 +662,7 @@ static void orinoco_stat_gather(struct net_device *dev,
                                struct sk_buff *skb,
                                struct hermes_rx_descriptor *desc)
 {
-       struct orinoco_private *priv = netdev_priv(dev);
+       struct orinoco_private *priv = ndev_priv(dev);
 
        /* Using spy support with lots of Rx packets, like in an
         * infrastructure (AP), will really slow down everything, because
@@ -687,7 +699,7 @@ static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid,
        int err;
        int len;
        struct sk_buff *skb;
-       struct orinoco_private *priv = netdev_priv(dev);
+       struct orinoco_private *priv = ndev_priv(dev);
        struct net_device_stats *stats = &priv->stats;
        hermes_t *hw = &priv->hw;
 
@@ -778,7 +790,7 @@ static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid,
 
 static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
 {
-       struct orinoco_private *priv = netdev_priv(dev);
+       struct orinoco_private *priv = ndev_priv(dev);
        struct net_device_stats *stats = &priv->stats;
        struct iw_statistics *wstats = &priv->wstats;
        struct sk_buff *skb = NULL;
@@ -816,7 +828,7 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
        }
 
        /* Handle frames in monitor mode */
-       if (priv->iw_mode == IW_MODE_MONITOR) {
+       if (priv->iw_mode == NL80211_IFTYPE_MONITOR) {
                orinoco_rx_monitor(dev, rxfid, desc);
                goto out;
        }
@@ -902,7 +914,7 @@ static void orinoco_rx(struct net_device *dev,
                       struct hermes_rx_descriptor *desc,
                       struct sk_buff *skb)
 {
-       struct orinoco_private *priv = netdev_priv(dev);
+       struct orinoco_private *priv = ndev_priv(dev);
        struct net_device_stats *stats = &priv->stats;
        u16 status, fc;
        int length;
@@ -1016,8 +1028,8 @@ static void orinoco_rx(struct net_device *dev,
 
 static void orinoco_rx_isr_tasklet(unsigned long data)
 {
-       struct net_device *dev = (struct net_device *) data;
-       struct orinoco_private *priv = netdev_priv(dev);
+       struct orinoco_private *priv = (struct orinoco_private *) data;
+       struct net_device *dev = priv->ndev;
        struct orinoco_rx_data *rx_data, *temp;
        struct hermes_rx_descriptor *desc;
        struct sk_buff *skb;
@@ -1260,9 +1272,81 @@ static void orinoco_send_wevents(struct work_struct *work)
        orinoco_unlock(priv, &flags);
 }
 
+static void qbuf_scan(struct orinoco_private *priv, void *buf,
+                     int len, int type)
+{
+       struct orinoco_scan_data *sd;
+       unsigned long flags;
+
+       sd = kmalloc(sizeof(*sd), GFP_ATOMIC);
+       sd->buf = buf;
+       sd->len = len;
+       sd->type = type;
+
+       spin_lock_irqsave(&priv->scan_lock, flags);
+       list_add_tail(&sd->list, &priv->scan_list);
+       spin_unlock_irqrestore(&priv->scan_lock, flags);
+
+       schedule_work(&priv->process_scan);
+}
+
+static void qabort_scan(struct orinoco_private *priv)
+{
+       struct orinoco_scan_data *sd;
+       unsigned long flags;
+
+       sd = kmalloc(sizeof(*sd), GFP_ATOMIC);
+       sd->len = -1; /* Abort */
+
+       spin_lock_irqsave(&priv->scan_lock, flags);
+       list_add_tail(&sd->list, &priv->scan_list);
+       spin_unlock_irqrestore(&priv->scan_lock, flags);
+
+       schedule_work(&priv->process_scan);
+}
+
+static void orinoco_process_scan_results(struct work_struct *work)
+{
+       struct orinoco_private *priv =
+               container_of(work, struct orinoco_private, process_scan);
+       struct orinoco_scan_data *sd, *temp;
+       unsigned long flags;
+       void *buf;
+       int len;
+       int type;
+
+       spin_lock_irqsave(&priv->scan_lock, flags);
+       list_for_each_entry_safe(sd, temp, &priv->scan_list, list) {
+               spin_unlock_irqrestore(&priv->scan_lock, flags);
+
+               buf = sd->buf;
+               len = sd->len;
+               type = sd->type;
+
+               list_del(&sd->list);
+               kfree(sd);
+
+               if (len > 0) {
+                       if (type == HERMES_INQ_CHANNELINFO)
+                               orinoco_add_extscan_result(priv, buf, len);
+                       else
+                               orinoco_add_hostscan_results(priv, buf, len);
+
+                       kfree(buf);
+               } else if (priv->scan_request) {
+                       /* Either abort or complete the scan */
+                       cfg80211_scan_done(priv->scan_request, (len < 0));
+                       priv->scan_request = NULL;
+               }
+
+               spin_lock_irqsave(&priv->scan_lock, flags);
+       }
+       spin_unlock_irqrestore(&priv->scan_lock, flags);
+}
+
 static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
 {
-       struct orinoco_private *priv = netdev_priv(dev);
+       struct orinoco_private *priv = ndev_priv(dev);
        u16 infofid;
        struct {
                __le16 len;
@@ -1327,7 +1411,7 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
                u16 newstatus;
                int connected;
 
-               if (priv->iw_mode == IW_MODE_MONITOR)
+               if (priv->iw_mode == NL80211_IFTYPE_MONITOR)
                        break;
 
                if (len != sizeof(linkstatus)) {
@@ -1346,7 +1430,7 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
                 * the hostscan frame can be requested.  */
                if (newstatus == HERMES_LINKSTATUS_AP_OUT_OF_RANGE &&
                    priv->firmware_type == FIRMWARE_TYPE_SYMBOL &&
-                   priv->has_hostscan && priv->scan_inprogress) {
+                   priv->has_hostscan && priv->scan_request) {
                        hermes_inquire(hw, HERMES_INQ_HOSTSCAN_SYMBOL);
                        break;
                }
@@ -1372,7 +1456,7 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
        }
        break;
        case HERMES_INQ_SCAN:
-               if (!priv->scan_inprogress && priv->bssid_fixed &&
+               if (!priv->scan_request && priv->bssid_fixed &&
                    priv->firmware_type == FIRMWARE_TYPE_INTERSIL) {
                        schedule_work(&priv->join_work);
                        break;
@@ -1382,30 +1466,30 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
        case HERMES_INQ_HOSTSCAN_SYMBOL: {
                /* Result of a scanning. Contains information about
                 * cells in the vicinity - Jean II */
-               union iwreq_data        wrqu;
                unsigned char *buf;
 
-               /* Scan is no longer in progress */
-               priv->scan_inprogress = 0;
-
                /* Sanity check */
                if (len > 4096) {
                        printk(KERN_WARNING "%s: Scan results too large (%d bytes)\n",
                               dev->name, len);
+                       qabort_scan(priv);
                        break;
                }
 
                /* Allocate buffer for results */
                buf = kmalloc(len, GFP_ATOMIC);
-               if (buf == NULL)
+               if (buf == NULL) {
                        /* No memory, so can't printk()... */
+                       qabort_scan(priv);
                        break;
+               }
 
                /* Read scan data */
                err = hermes_bap_pread(hw, IRQ_BAP, (void *) buf, len,
                                       infofid, sizeof(info));
                if (err) {
                        kfree(buf);
+                       qabort_scan(priv);
                        break;
                }
 
@@ -1419,24 +1503,14 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
                }
 #endif /* ORINOCO_DEBUG */
 
-               if (orinoco_process_scan_results(priv, buf, len) == 0) {
-                       /* Send an empty event to user space.
-                        * We don't send the received data on the event because
-                        * it would require us to do complex transcoding, and
-                        * we want to minimise the work done in the irq handler
-                        * Use a request to extract the data - Jean II */
-                       wrqu.data.length = 0;
-                       wrqu.data.flags = 0;
-                       wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
-               }
-               kfree(buf);
+               qbuf_scan(priv, buf, len, type);
        }
        break;
        case HERMES_INQ_CHANNELINFO:
        {
                struct agere_ext_scan_info *bss;
 
-               if (!priv->scan_inprogress) {
+               if (!priv->scan_request) {
                        printk(KERN_DEBUG "%s: Got chaninfo without scan, "
                               "len=%d\n", dev->name, len);
                        break;
@@ -1444,25 +1518,12 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
 
                /* An empty result indicates that the scan is complete */
                if (len == 0) {
-                       union iwreq_data        wrqu;
-
-                       /* Scan is no longer in progress */
-                       priv->scan_inprogress = 0;
-
-                       wrqu.data.length = 0;
-                       wrqu.data.flags = 0;
-                       wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
+                       qbuf_scan(priv, NULL, len, type);
                        break;
                }
 
                /* Sanity check */
-               else if (len > sizeof(*bss)) {
-                       printk(KERN_WARNING
-                              "%s: Ext scan results too large (%d bytes). "
-                              "Truncating results to %zd bytes.\n",
-                              dev->name, len, sizeof(*bss));
-                       len = sizeof(*bss);
-               } else if (len < (offsetof(struct agere_ext_scan_info,
+               else if (len < (offsetof(struct agere_ext_scan_info,
                                           data) + 2)) {
                        /* Drop this result now so we don't have to
                         * keep checking later */
@@ -1472,21 +1533,18 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
                        break;
                }
 
-               bss = kmalloc(sizeof(*bss), GFP_ATOMIC);
+               bss = kmalloc(len, GFP_ATOMIC);
                if (bss == NULL)
                        break;
 
                /* Read scan data */
                err = hermes_bap_pread(hw, IRQ_BAP, (void *) bss, len,
                                       infofid, sizeof(info));
-               if (err) {
+               if (err)
                        kfree(bss);
-                       break;
-               }
-
-               orinoco_add_ext_scan_result(priv, bss);
+               else
+                       qbuf_scan(priv, bss, len, type);
 
-               kfree(bss);
                break;
        }
        case HERMES_INQ_SEC_STAT_AGERE:
@@ -1501,6 +1559,8 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
                /* We don't actually do anything about it */
                break;
        }
+
+       return;
 }
 
 static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw)
@@ -1513,15 +1573,15 @@ static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw)
 /* Internal hardware control routines                               */
 /********************************************************************/
 
-int __orinoco_up(struct net_device *dev)
+static int __orinoco_up(struct orinoco_private *priv)
 {
-       struct orinoco_private *priv = netdev_priv(dev);
+       struct net_device *dev = priv->ndev;
        struct hermes *hw = &priv->hw;
        int err;
 
        netif_carrier_off(dev); /* just to make sure */
 
-       err = __orinoco_program_rids(dev);
+       err = __orinoco_commit(priv);
        if (err) {
                printk(KERN_ERR "%s: Error %d configuring card\n",
                       dev->name, err);
@@ -1541,11 +1601,10 @@ int __orinoco_up(struct net_device *dev)
 
        return 0;
 }
-EXPORT_SYMBOL(__orinoco_up);
 
-int __orinoco_down(struct net_device *dev)
+static int __orinoco_down(struct orinoco_private *priv)
 {
-       struct orinoco_private *priv = netdev_priv(dev);
+       struct net_device *dev = priv->ndev;
        struct hermes *hw = &priv->hw;
        int err;
 
@@ -1573,31 +1632,9 @@ int __orinoco_down(struct net_device *dev)
 
        return 0;
 }
-EXPORT_SYMBOL(__orinoco_down);
 
-static int orinoco_allocate_fid(struct net_device *dev)
+static int orinoco_reinit_firmware(struct orinoco_private *priv)
 {
-       struct orinoco_private *priv = netdev_priv(dev);
-       struct hermes *hw = &priv->hw;
-       int err;
-
-       err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
-       if (err == -EIO && priv->nicbuf_size > TX_NICBUF_SIZE_BUG) {
-               /* Try workaround for old Symbol firmware bug */
-               priv->nicbuf_size = TX_NICBUF_SIZE_BUG;
-               err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
-
-               printk(KERN_WARNING "%s: firmware ALLOC bug detected "
-                      "(old Symbol firmware?). Work around %s\n",
-                      dev->name, err ? "failed!" : "ok.");
-       }
-
-       return err;
-}
-
-int orinoco_reinit_firmware(struct net_device *dev)
-{
-       struct orinoco_private *priv = netdev_priv(dev);
        struct hermes *hw = &priv->hw;
        int err;
 
@@ -1608,246 +1645,15 @@ int orinoco_reinit_firmware(struct net_device *dev)
                        priv->do_fw_download = 0;
        }
        if (!err)
-               err = orinoco_allocate_fid(dev);
+               err = orinoco_hw_allocate_fid(priv);
 
        return err;
 }
-EXPORT_SYMBOL(orinoco_reinit_firmware);
-
-int __orinoco_program_rids(struct net_device *dev)
-{
-       struct orinoco_private *priv = netdev_priv(dev);
-       hermes_t *hw = &priv->hw;
-       int err;
-       struct hermes_idstring idbuf;
-
-       /* Set the MAC address */
-       err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
-                              HERMES_BYTES_TO_RECLEN(ETH_ALEN), dev->dev_addr);
-       if (err) {
-               printk(KERN_ERR "%s: Error %d setting MAC address\n",
-                      dev->name, err);
-               return err;
-       }
-
-       /* Set up the link mode */
-       err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE,
-                                  priv->port_type);
-       if (err) {
-               printk(KERN_ERR "%s: Error %d setting port type\n",
-                      dev->name, err);
-               return err;
-       }
-       /* Set the channel/frequency */
-       if (priv->channel != 0 && priv->iw_mode != IW_MODE_INFRA) {
-               err = hermes_write_wordrec(hw, USER_BAP,
-                                          HERMES_RID_CNFOWNCHANNEL,
-                                          priv->channel);
-               if (err) {
-                       printk(KERN_ERR "%s: Error %d setting channel %d\n",
-                              dev->name, err, priv->channel);
-                       return err;
-               }
-       }
-
-       if (priv->has_ibss) {
-               u16 createibss;
-
-               if ((strlen(priv->desired_essid) == 0) && (priv->createibss)) {
-                       printk(KERN_WARNING "%s: This firmware requires an "
-                              "ESSID in IBSS-Ad-Hoc mode.\n", dev->name);
-                       /* With wvlan_cs, in this case, we would crash.
-                        * hopefully, this driver will behave better...
-                        * Jean II */
-                       createibss = 0;
-               } else {
-                       createibss = priv->createibss;
-               }
-
-               err = hermes_write_wordrec(hw, USER_BAP,
-                                          HERMES_RID_CNFCREATEIBSS,
-                                          createibss);
-               if (err) {
-                       printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n",
-                              dev->name, err);
-                       return err;
-               }
-       }
-
-       /* Set the desired BSSID */
-       err = __orinoco_hw_set_wap(priv);
-       if (err) {
-               printk(KERN_ERR "%s: Error %d setting AP address\n",
-                      dev->name, err);
-               return err;
-       }
-       /* Set the desired ESSID */
-       idbuf.len = cpu_to_le16(strlen(priv->desired_essid));
-       memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val));
-       /* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */
-       err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID,
-                       HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
-                       &idbuf);
-       if (err) {
-               printk(KERN_ERR "%s: Error %d setting OWNSSID\n",
-                      dev->name, err);
-               return err;
-       }
-       err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID,
-                       HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
-                       &idbuf);
-       if (err) {
-               printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n",
-                      dev->name, err);
-               return err;
-       }
-
-       /* Set the station name */
-       idbuf.len = cpu_to_le16(strlen(priv->nick));
-       memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val));
-       err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
-                              HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2),
-                              &idbuf);
-       if (err) {
-               printk(KERN_ERR "%s: Error %d setting nickname\n",
-                      dev->name, err);
-               return err;
-       }
-
-       /* Set AP density */
-       if (priv->has_sensitivity) {
-               err = hermes_write_wordrec(hw, USER_BAP,
-                                          HERMES_RID_CNFSYSTEMSCALE,
-                                          priv->ap_density);
-               if (err) {
-                       printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE. "
-                              "Disabling sensitivity control\n",
-                              dev->name, err);
-
-                       priv->has_sensitivity = 0;
-               }
-       }
-
-       /* Set RTS threshold */
-       err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD,
-                                  priv->rts_thresh);
-       if (err) {
-               printk(KERN_ERR "%s: Error %d setting RTS threshold\n",
-                      dev->name, err);
-               return err;
-       }
-
-       /* Set fragmentation threshold or MWO robustness */
-       if (priv->has_mwo)
-               err = hermes_write_wordrec(hw, USER_BAP,
-                                          HERMES_RID_CNFMWOROBUST_AGERE,
-                                          priv->mwo_robust);
-       else
-               err = hermes_write_wordrec(hw, USER_BAP,
-                                          HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
-                                          priv->frag_thresh);
-       if (err) {
-               printk(KERN_ERR "%s: Error %d setting fragmentation\n",
-                      dev->name, err);
-               return err;
-       }
-
-       /* Set bitrate */
-       err = __orinoco_hw_set_bitrate(priv);
-       if (err) {
-               printk(KERN_ERR "%s: Error %d setting bitrate\n",
-                      dev->name, err);
-               return err;
-       }
-
-       /* Set power management */
-       if (priv->has_pm) {
-               err = hermes_write_wordrec(hw, USER_BAP,
-                                          HERMES_RID_CNFPMENABLED,
-                                          priv->pm_on);
-               if (err) {
-                       printk(KERN_ERR "%s: Error %d setting up PM\n",
-                              dev->name, err);
-                       return err;
-               }
-
-               err = hermes_write_wordrec(hw, USER_BAP,
-                                          HERMES_RID_CNFMULTICASTRECEIVE,
-                                          priv->pm_mcast);
-               if (err) {
-                       printk(KERN_ERR "%s: Error %d setting up PM\n",
-                              dev->name, err);
-                       return err;
-               }
-               err = hermes_write_wordrec(hw, USER_BAP,
-                                          HERMES_RID_CNFMAXSLEEPDURATION,
-                                          priv->pm_period);
-               if (err) {
-                       printk(KERN_ERR "%s: Error %d setting up PM\n",
-                              dev->name, err);
-                       return err;
-               }
-               err = hermes_write_wordrec(hw, USER_BAP,
-                                          HERMES_RID_CNFPMHOLDOVERDURATION,
-                                          priv->pm_timeout);
-               if (err) {
-                       printk(KERN_ERR "%s: Error %d setting up PM\n",
-                              dev->name, err);
-                       return err;
-               }
-       }
-
-       /* Set preamble - only for Symbol so far... */
-       if (priv->has_preamble) {
-               err = hermes_write_wordrec(hw, USER_BAP,
-                                          HERMES_RID_CNFPREAMBLE_SYMBOL,
-                                          priv->preamble);
-               if (err) {
-                       printk(KERN_ERR "%s: Error %d setting preamble\n",
-                              dev->name, err);
-                       return err;
-               }
-       }
-
-       /* Set up encryption */
-       if (priv->has_wep || priv->has_wpa) {
-               err = __orinoco_hw_setup_enc(priv);
-               if (err) {
-                       printk(KERN_ERR "%s: Error %d activating encryption\n",
-                              dev->name, err);
-                       return err;
-               }
-       }
-
-       if (priv->iw_mode == IW_MODE_MONITOR) {
-               /* Enable monitor mode */
-               dev->type = ARPHRD_IEEE80211;
-               err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
-                                           HERMES_TEST_MONITOR, 0, NULL);
-       } else {
-               /* Disable monitor mode */
-               dev->type = ARPHRD_ETHER;
-               err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
-                                           HERMES_TEST_STOP, 0, NULL);
-       }
-       if (err)
-               return err;
-
-       /* Set promiscuity / multicast*/
-       priv->promiscuous = 0;
-       priv->mc_count = 0;
-
-       /* FIXME: what about netif_tx_lock */
-       __orinoco_set_multicast_list(dev);
-
-       return 0;
-}
 
-/* FIXME: return int? */
-static void
+static int
 __orinoco_set_multicast_list(struct net_device *dev)
 {
-       struct orinoco_private *priv = netdev_priv(dev);
+       struct orinoco_private *priv = ndev_priv(dev);
        int err = 0;
        int promisc, mc_count;
 
@@ -1864,6 +1670,8 @@ __orinoco_set_multicast_list(struct net_device *dev)
 
        err = __orinoco_hw_set_multicast_list(priv, dev->mc_list, mc_count,
                                              promisc);
+
+       return err;
 }
 
 /* This must be called from user context, without locks held - use
@@ -1896,9 +1704,11 @@ void orinoco_reset(struct work_struct *work)
 
        orinoco_unlock(priv, &flags);
 
-       /* Scanning support: Cleanup of driver struct */
-       orinoco_clear_scan_results(priv, 0);
-       priv->scan_inprogress = 0;
+       /* Scanning support: Notify scan cancellation */
+       if (priv->scan_request) {
+               cfg80211_scan_done(priv->scan_request, 1);
+               priv->scan_request = NULL;
+       }
 
        if (priv->hard_reset) {
                err = (*priv->hard_reset)(priv);
@@ -1909,7 +1719,7 @@ void orinoco_reset(struct work_struct *work)
                }
        }
 
-       err = orinoco_reinit_firmware(dev);
+       err = orinoco_reinit_firmware(priv);
        if (err) {
                printk(KERN_ERR "%s: orinoco_reset: Error %d re-initializing firmware\n",
                       dev->name, err);
@@ -1924,7 +1734,7 @@ void orinoco_reset(struct work_struct *work)
        /* priv->open or priv->hw_unavailable might have changed while
         * we dropped the lock */
        if (priv->open && (!priv->hw_unavailable)) {
-               err = __orinoco_up(dev);
+               err = __orinoco_up(priv);
                if (err) {
                        printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n",
                               dev->name, err);
@@ -1941,6 +1751,64 @@ void orinoco_reset(struct work_struct *work)
        printk(KERN_ERR "%s: Device has been disabled!\n", dev->name);
 }
 
+static int __orinoco_commit(struct orinoco_private *priv)
+{
+       struct net_device *dev = priv->ndev;
+       int err = 0;
+
+       err = orinoco_hw_program_rids(priv);
+
+       /* FIXME: what about netif_tx_lock */
+       (void) __orinoco_set_multicast_list(dev);
+
+       return err;
+}
+
+/* Ensures configuration changes are applied. May result in a reset.
+ * The caller should hold priv->lock
+ */
+int orinoco_commit(struct orinoco_private *priv)
+{
+       struct net_device *dev = priv->ndev;
+       hermes_t *hw = &priv->hw;
+       int err;
+
+       if (priv->broken_disableport) {
+               schedule_work(&priv->reset_work);
+               return 0;
+       }
+
+       err = hermes_disable_port(hw, 0);
+       if (err) {
+               printk(KERN_WARNING "%s: Unable to disable port "
+                      "while reconfiguring card\n", dev->name);
+               priv->broken_disableport = 1;
+               goto out;
+       }
+
+       err = __orinoco_commit(priv);
+       if (err) {
+               printk(KERN_WARNING "%s: Unable to reconfigure card\n",
+                      dev->name);
+               goto out;
+       }
+
+       err = hermes_enable_port(hw, 0);
+       if (err) {
+               printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n",
+                      dev->name);
+               goto out;
+       }
+
+ out:
+       if (err) {
+               printk(KERN_WARNING "%s: Resetting instead...\n", dev->name);
+               schedule_work(&priv->reset_work);
+               err = 0;
+       }
+       return err;
+}
+
 /********************************************************************/
 /* Interrupt handler                                                */
 /********************************************************************/
@@ -1960,8 +1828,8 @@ static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw)
 
 irqreturn_t orinoco_interrupt(int irq, void *dev_id)
 {
-       struct net_device *dev = dev_id;
-       struct orinoco_private *priv = netdev_priv(dev);
+       struct orinoco_private *priv = dev_id;
+       struct net_device *dev = priv->ndev;
        hermes_t *hw = &priv->hw;
        int count = MAX_IRQLOOPS_PER_IRQ;
        u16 evstat, events;
@@ -2096,227 +1964,12 @@ static void orinoco_unregister_pm_notifier(struct orinoco_private *priv)
 /* Initialization                                                   */
 /********************************************************************/
 
-struct comp_id {
-       u16 id, variant, major, minor;
-} __attribute__ ((packed));
-
-static inline fwtype_t determine_firmware_type(struct comp_id *nic_id)
-{
-       if (nic_id->id < 0x8000)
-               return FIRMWARE_TYPE_AGERE;
-       else if (nic_id->id == 0x8000 && nic_id->major == 0)
-               return FIRMWARE_TYPE_SYMBOL;
-       else
-               return FIRMWARE_TYPE_INTERSIL;
-}
-
-/* Set priv->firmware type, determine firmware properties */
-static int determine_firmware(struct net_device *dev)
-{
-       struct orinoco_private *priv = netdev_priv(dev);
-       hermes_t *hw = &priv->hw;
-       int err;
-       struct comp_id nic_id, sta_id;
-       unsigned int firmver;
-       char tmp[SYMBOL_MAX_VER_LEN+1] __attribute__((aligned(2)));
-
-       /* Get the hardware version */
-       err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_NICID, &nic_id);
-       if (err) {
-               printk(KERN_ERR "%s: Cannot read hardware identity: error %d\n",
-                      dev->name, err);
-               return err;
-       }
-
-       le16_to_cpus(&nic_id.id);
-       le16_to_cpus(&nic_id.variant);
-       le16_to_cpus(&nic_id.major);
-       le16_to_cpus(&nic_id.minor);
-       printk(KERN_DEBUG "%s: Hardware identity %04x:%04x:%04x:%04x\n",
-              dev->name, nic_id.id, nic_id.variant,
-              nic_id.major, nic_id.minor);
-
-       priv->firmware_type = determine_firmware_type(&nic_id);
-
-       /* Get the firmware version */
-       err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_STAID, &sta_id);
-       if (err) {
-               printk(KERN_ERR "%s: Cannot read station identity: error %d\n",
-                      dev->name, err);
-               return err;
-       }
-
-       le16_to_cpus(&sta_id.id);
-       le16_to_cpus(&sta_id.variant);
-       le16_to_cpus(&sta_id.major);
-       le16_to_cpus(&sta_id.minor);
-       printk(KERN_DEBUG "%s: Station identity  %04x:%04x:%04x:%04x\n",
-              dev->name, sta_id.id, sta_id.variant,
-              sta_id.major, sta_id.minor);
-
-       switch (sta_id.id) {
-       case 0x15:
-               printk(KERN_ERR "%s: Primary firmware is active\n",
-                      dev->name);
-               return -ENODEV;
-       case 0x14b:
-               printk(KERN_ERR "%s: Tertiary firmware is active\n",
-                      dev->name);
-               return -ENODEV;
-       case 0x1f:      /* Intersil, Agere, Symbol Spectrum24 */
-       case 0x21:      /* Symbol Spectrum24 Trilogy */
-               break;
-       default:
-               printk(KERN_NOTICE "%s: Unknown station ID, please report\n",
-                      dev->name);
-               break;
-       }
-
-       /* Default capabilities */
-       priv->has_sensitivity = 1;
-       priv->has_mwo = 0;
-       priv->has_preamble = 0;
-       priv->has_port3 = 1;
-       priv->has_ibss = 1;
-       priv->has_wep = 0;
-       priv->has_big_wep = 0;
-       priv->has_alt_txcntl = 0;
-       priv->has_ext_scan = 0;
-       priv->has_wpa = 0;
-       priv->do_fw_download = 0;
-
-       /* Determine capabilities from the firmware version */
-       switch (priv->firmware_type) {
-       case FIRMWARE_TYPE_AGERE:
-               /* Lucent Wavelan IEEE, Lucent Orinoco, Cabletron RoamAbout,
-                  ELSA, Melco, HP, IBM, Dell 1150, Compaq 110/210 */
-               snprintf(priv->fw_name, sizeof(priv->fw_name) - 1,
-                        "Lucent/Agere %d.%02d", sta_id.major, sta_id.minor);
-
-               firmver = ((unsigned long)sta_id.major << 16) | sta_id.minor;
-
-               priv->has_ibss = (firmver >= 0x60006);
-               priv->has_wep = (firmver >= 0x40020);
-               priv->has_big_wep = 1; /* FIXME: this is wrong - how do we tell
-                                         Gold cards from the others? */
-               priv->has_mwo = (firmver >= 0x60000);
-               priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */
-               priv->ibss_port = 1;
-               priv->has_hostscan = (firmver >= 0x8000a);
-               priv->do_fw_download = 1;
-               priv->broken_monitor = (firmver >= 0x80000);
-               priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */
-               priv->has_ext_scan = (firmver >= 0x90000); /* All 9.x ? */
-               priv->has_wpa = (firmver >= 0x9002a);
-               /* Tested with Agere firmware :
-                *      1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II
-                * Tested CableTron firmware : 4.32 => Anton */
-               break;
-       case FIRMWARE_TYPE_SYMBOL:
-               /* Symbol , 3Com AirConnect, Intel, Ericsson WLAN */
-               /* Intel MAC : 00:02:B3:* */
-               /* 3Com MAC : 00:50:DA:* */
-               memset(tmp, 0, sizeof(tmp));
-               /* Get the Symbol firmware version */
-               err = hermes_read_ltv(hw, USER_BAP,
-                                     HERMES_RID_SECONDARYVERSION_SYMBOL,
-                                     SYMBOL_MAX_VER_LEN, NULL, &tmp);
-               if (err) {
-                       printk(KERN_WARNING
-                              "%s: Error %d reading Symbol firmware info. "
-                              "Wildly guessing capabilities...\n",
-                              dev->name, err);
-                       firmver = 0;
-                       tmp[0] = '\0';
-               } else {
-                       /* The firmware revision is a string, the format is
-                        * something like : "V2.20-01".
-                        * Quick and dirty parsing... - Jean II
-                        */
-                       firmver = ((tmp[1] - '0') << 16)
-                               | ((tmp[3] - '0') << 12)
-                               | ((tmp[4] - '0') << 8)
-                               | ((tmp[6] - '0') << 4)
-                               | (tmp[7] - '0');
-
-                       tmp[SYMBOL_MAX_VER_LEN] = '\0';
-               }
-
-               snprintf(priv->fw_name, sizeof(priv->fw_name) - 1,
-                        "Symbol %s", tmp);
-
-               priv->has_ibss = (firmver >= 0x20000);
-               priv->has_wep = (firmver >= 0x15012);
-               priv->has_big_wep = (firmver >= 0x20000);
-               priv->has_pm = (firmver >= 0x20000 && firmver < 0x22000) ||
-                              (firmver >= 0x29000 && firmver < 0x30000) ||
-                              firmver >= 0x31000;
-               priv->has_preamble = (firmver >= 0x20000);
-               priv->ibss_port = 4;
-
-               /* Symbol firmware is found on various cards, but
-                * there has been no attempt to check firmware
-                * download on non-spectrum_cs based cards.
-                *
-                * Given that the Agere firmware download works
-                * differently, we should avoid doing a firmware
-                * download with the Symbol algorithm on non-spectrum
-                * cards.
-                *
-                * For now we can identify a spectrum_cs based card
-                * because it has a firmware reset function.
-                */
-               priv->do_fw_download = (priv->stop_fw != NULL);
-
-               priv->broken_disableport = (firmver == 0x25013) ||
-                               (firmver >= 0x30000 && firmver <= 0x31000);
-               priv->has_hostscan = (firmver >= 0x31001) ||
-                                    (firmver >= 0x29057 && firmver < 0x30000);
-               /* Tested with Intel firmware : 0x20015 => Jean II */
-               /* Tested with 3Com firmware : 0x15012 & 0x22001 => Jean II */
-               break;
-       case FIRMWARE_TYPE_INTERSIL:
-               /* D-Link, Linksys, Adtron, ZoomAir, and many others...
-                * Samsung, Compaq 100/200 and Proxim are slightly
-                * different and less well tested */
-               /* D-Link MAC : 00:40:05:* */
-               /* Addtron MAC : 00:90:D1:* */
-               snprintf(priv->fw_name, sizeof(priv->fw_name) - 1,
-                        "Intersil %d.%d.%d", sta_id.major, sta_id.minor,
-                        sta_id.variant);
-
-               firmver = ((unsigned long)sta_id.major << 16) |
-                       ((unsigned long)sta_id.minor << 8) | sta_id.variant;
-
-               priv->has_ibss = (firmver >= 0x000700); /* FIXME */
-               priv->has_big_wep = priv->has_wep = (firmver >= 0x000800);
-               priv->has_pm = (firmver >= 0x000700);
-               priv->has_hostscan = (firmver >= 0x010301);
-
-               if (firmver >= 0x000800)
-                       priv->ibss_port = 0;
-               else {
-                       printk(KERN_NOTICE "%s: Intersil firmware earlier "
-                              "than v0.8.x - several features not supported\n",
-                              dev->name);
-                       priv->ibss_port = 1;
-               }
-               break;
-       }
-       printk(KERN_DEBUG "%s: Firmware determined as %s\n", dev->name,
-              priv->fw_name);
-
-       return 0;
-}
-
-static int orinoco_init(struct net_device *dev)
+int orinoco_init(struct orinoco_private *priv)
 {
-       struct orinoco_private *priv = netdev_priv(dev);
+       struct device *dev = priv->dev;
+       struct wiphy *wiphy = priv_to_wiphy(priv);
        hermes_t *hw = &priv->hw;
        int err = 0;
-       struct hermes_idstring nickbuf;
-       u16 reclen;
-       int len;
 
        /* No need to lock, the hw_unavailable flag is already set in
         * alloc_orinocodev() */
@@ -2325,15 +1978,14 @@ static int orinoco_init(struct net_device *dev)
        /* Initialize the firmware */
        err = hermes_init(hw);
        if (err != 0) {
-               printk(KERN_ERR "%s: failed to initialize firmware (err = %d)\n",
-                      dev->name, err);
+               dev_err(dev, "Failed to initialize firmware (err = %d)\n",
+                       err);
                goto out;
        }
 
-       err = determine_firmware(dev);
+       err = determine_fw_capabilities(priv);
        if (err != 0) {
-               printk(KERN_ERR "%s: Incompatible firmware, aborting\n",
-                      dev->name);
+               dev_err(dev, "Incompatible firmware, aborting\n");
                goto out;
        }
 
@@ -2347,147 +1999,41 @@ static int orinoco_init(struct net_device *dev)
                        priv->do_fw_download = 0;
 
                /* Check firmware version again */
-               err = determine_firmware(dev);
+               err = determine_fw_capabilities(priv);
                if (err != 0) {
-                       printk(KERN_ERR "%s: Incompatible firmware, aborting\n",
-                              dev->name);
+                       dev_err(dev, "Incompatible firmware, aborting\n");
                        goto out;
                }
        }
 
        if (priv->has_port3)
-               printk(KERN_DEBUG "%s: Ad-hoc demo mode supported\n",
-                      dev->name);
+               dev_info(dev, "Ad-hoc demo mode supported\n");
        if (priv->has_ibss)
-               printk(KERN_DEBUG "%s: IEEE standard IBSS ad-hoc mode supported\n",
-                      dev->name);
-       if (priv->has_wep) {
-               printk(KERN_DEBUG "%s: WEP supported, %s-bit key\n", dev->name,
-                      priv->has_big_wep ? "104" : "40");
-       }
+               dev_info(dev, "IEEE standard IBSS ad-hoc mode supported\n");
+       if (priv->has_wep)
+               dev_info(dev, "WEP supported, %s-bit key\n",
+                        priv->has_big_wep ? "104" : "40");
        if (priv->has_wpa) {
-               printk(KERN_DEBUG "%s: WPA-PSK supported\n", dev->name);
+               dev_info(dev, "WPA-PSK supported\n");
                if (orinoco_mic_init(priv)) {
-                       printk(KERN_ERR "%s: Failed to setup MIC crypto "
-                              "algorithm. Disabling WPA support\n", dev->name);
+                       dev_err(dev, "Failed to setup MIC crypto algorithm. "
+                               "Disabling WPA support\n");
                        priv->has_wpa = 0;
                }
        }
 
-       /* Now we have the firmware capabilities, allocate appropiate
-        * sized scan buffers */
-       if (orinoco_bss_data_allocate(priv))
-               goto out;
-       orinoco_bss_data_init(priv);
-
-       /* Get the MAC address */
-       err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
-                             ETH_ALEN, NULL, dev->dev_addr);
-       if (err) {
-               printk(KERN_WARNING "%s: failed to read MAC address!\n",
-                      dev->name);
-               goto out;
-       }
-
-       printk(KERN_DEBUG "%s: MAC address %pM\n",
-              dev->name, dev->dev_addr);
-
-       /* Get the station name */
-       err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
-                             sizeof(nickbuf), &reclen, &nickbuf);
-       if (err) {
-               printk(KERN_ERR "%s: failed to read station name\n",
-                      dev->name);
-               goto out;
-       }
-       if (nickbuf.len)
-               len = min(IW_ESSID_MAX_SIZE, (int)le16_to_cpu(nickbuf.len));
-       else
-               len = min(IW_ESSID_MAX_SIZE, 2 * reclen);
-       memcpy(priv->nick, &nickbuf.val, len);
-       priv->nick[len] = '\0';
-
-       printk(KERN_DEBUG "%s: Station name \"%s\"\n", dev->name, priv->nick);
-
-       err = orinoco_allocate_fid(dev);
-       if (err) {
-               printk(KERN_ERR "%s: failed to allocate NIC buffer!\n",
-                      dev->name);
-               goto out;
-       }
-
-       /* Get allowed channels */
-       err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CHANNELLIST,
-                                 &priv->channel_mask);
-       if (err) {
-               printk(KERN_ERR "%s: failed to read channel list!\n",
-                      dev->name);
-               goto out;
-       }
-
-       /* Get initial AP density */
-       err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE,
-                                 &priv->ap_density);
-       if (err || priv->ap_density < 1 || priv->ap_density > 3)
-               priv->has_sensitivity = 0;
-
-       /* Get initial RTS threshold */
-       err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD,
-                                 &priv->rts_thresh);
-       if (err) {
-               printk(KERN_ERR "%s: failed to read RTS threshold!\n",
-                      dev->name);
+       err = orinoco_hw_read_card_settings(priv, wiphy->perm_addr);
+       if (err)
                goto out;
-       }
 
-       /* Get initial fragmentation settings */
-       if (priv->has_mwo)
-               err = hermes_read_wordrec(hw, USER_BAP,
-                                         HERMES_RID_CNFMWOROBUST_AGERE,
-                                         &priv->mwo_robust);
-       else
-               err = hermes_read_wordrec(hw, USER_BAP,
-                                         HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
-                                         &priv->frag_thresh);
+       err = orinoco_hw_allocate_fid(priv);
        if (err) {
-               printk(KERN_ERR "%s: failed to read fragmentation settings!\n",
-                      dev->name);
+               dev_err(dev, "Failed to allocate NIC buffer!\n");
                goto out;
        }
 
-       /* Power management setup */
-       if (priv->has_pm) {
-               priv->pm_on = 0;
-               priv->pm_mcast = 1;
-               err = hermes_read_wordrec(hw, USER_BAP,
-                                         HERMES_RID_CNFMAXSLEEPDURATION,
-                                         &priv->pm_period);
-               if (err) {
-                       printk(KERN_ERR "%s: failed to read power management period!\n",
-                              dev->name);
-                       goto out;
-               }
-               err = hermes_read_wordrec(hw, USER_BAP,
-                                         HERMES_RID_CNFPMHOLDOVERDURATION,
-                                         &priv->pm_timeout);
-               if (err) {
-                       printk(KERN_ERR "%s: failed to read power management timeout!\n",
-                              dev->name);
-                       goto out;
-               }
-       }
-
-       /* Preamble setup */
-       if (priv->has_preamble) {
-               err = hermes_read_wordrec(hw, USER_BAP,
-                                         HERMES_RID_CNFPREAMBLE_SYMBOL,
-                                         &priv->preamble);
-               if (err)
-                       goto out;
-       }
-
        /* Set up the default configuration */
-       priv->iw_mode = IW_MODE_INFRA;
+       priv->iw_mode = NL80211_IFTYPE_STATION;
        /* By default use IEEE/IBSS ad-hoc mode if we have it */
        priv->prefer_port3 = priv->has_port3 && (!priv->has_ibss);
        set_port_type(priv);
@@ -2502,20 +2048,25 @@ static int orinoco_init(struct net_device *dev)
        priv->wpa_ie_len = 0;
        priv->wpa_ie = NULL;
 
+       if (orinoco_wiphy_register(wiphy)) {
+               err = -ENODEV;
+               goto out;
+       }
+
        /* Make the hardware available, as long as it hasn't been
         * removed elsewhere (e.g. by PCMCIA hot unplug) */
        spin_lock_irq(&priv->lock);
        priv->hw_unavailable--;
        spin_unlock_irq(&priv->lock);
 
-       printk(KERN_DEBUG "%s: ready\n", dev->name);
+       dev_dbg(dev, "Ready\n");
 
  out:
        return err;
 }
+EXPORT_SYMBOL(orinoco_init);
 
 static const struct net_device_ops orinoco_netdev_ops = {
-       .ndo_init               = orinoco_init,
        .ndo_open               = orinoco_open,
        .ndo_stop               = orinoco_stop,
        .ndo_start_xmit         = orinoco_xmit,
@@ -2527,40 +2078,64 @@ static const struct net_device_ops orinoco_netdev_ops = {
        .ndo_get_stats          = orinoco_get_stats,
 };
 
-struct net_device
+/* Allocate private data.
+ *
+ * This driver has a number of structures associated with it
+ *  netdev - Net device structure for each network interface
+ *  wiphy - structure associated with wireless phy
+ *  wireless_dev (wdev) - structure for each wireless interface
+ *  hw - structure for hermes chip info
+ *  card - card specific structure for use by the card driver
+ *         (airport, orinoco_cs)
+ *  priv - orinoco private data
+ *  device - generic linux device structure
+ *
+ *  +---------+    +---------+
+ *  |  wiphy  |    | netdev  |
+ *  | +-------+    | +-------+
+ *  | | priv  |    | | wdev  |
+ *  | | +-----+    +-+-------+
+ *  | | | hw  |
+ *  | +-+-----+
+ *  | | card  |
+ *  +-+-------+
+ *
+ * priv has a link to netdev and device
+ * wdev has a link to wiphy
+ */
+struct orinoco_private
 *alloc_orinocodev(int sizeof_card,
                  struct device *device,
                  int (*hard_reset)(struct orinoco_private *),
                  int (*stop_fw)(struct orinoco_private *, int))
 {
-       struct net_device *dev;
        struct orinoco_private *priv;
+       struct wiphy *wiphy;
 
-       dev = alloc_etherdev(sizeof(struct orinoco_private) + sizeof_card);
-       if (!dev)
+       /* allocate wiphy
+        * NOTE: We only support a single virtual interface
+        *       but this may change when monitor mode is added
+        */
+       wiphy = wiphy_new(&orinoco_cfg_ops,
+                         sizeof(struct orinoco_private) + sizeof_card);
+       if (!wiphy)
                return NULL;
-       priv = netdev_priv(dev);
-       priv->ndev = dev;
+
+       priv = wiphy_priv(wiphy);
+       priv->dev = device;
+
        if (sizeof_card)
                priv->card = (void *)((unsigned long)priv
                                      + sizeof(struct orinoco_private));
        else
                priv->card = NULL;
-       priv->dev = device;
 
-       /* Setup / override net_device fields */
-       dev->netdev_ops = &orinoco_netdev_ops;
-       dev->watchdog_timeo = HZ; /* 1 second timeout */
-       dev->ethtool_ops = &orinoco_ethtool_ops;
-       dev->wireless_handlers = &orinoco_handler_def;
+       orinoco_wiphy_init(wiphy);
+
 #ifdef WIRELESS_SPY
        priv->wireless_data.spy_data = &priv->spy_data;
-       dev->wireless_data = &priv->wireless_data;
 #endif
 
-       /* Reserve space in skb for the SNAP header */
-       dev->hard_header_len += ENCAPS_OVERHEAD;
-
        /* Set up default callbacks */
        priv->hard_reset = hard_reset;
        priv->stop_fw = stop_fw;
@@ -2576,9 +2151,12 @@ struct net_device
 
        INIT_LIST_HEAD(&priv->rx_list);
        tasklet_init(&priv->rx_tasklet, orinoco_rx_isr_tasklet,
-                    (unsigned long) dev);
+                    (unsigned long) priv);
+
+       spin_lock_init(&priv->scan_lock);
+       INIT_LIST_HEAD(&priv->scan_list);
+       INIT_WORK(&priv->process_scan, orinoco_process_scan_results);
 
-       netif_carrier_off(dev);
        priv->last_linkstatus = 0xffff;
 
 #if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP)
@@ -2589,14 +2167,91 @@ struct net_device
        /* Register PM notifiers */
        orinoco_register_pm_notifier(priv);
 
-       return dev;
+       return priv;
 }
 EXPORT_SYMBOL(alloc_orinocodev);
 
-void free_orinocodev(struct net_device *dev)
+/* We can only support a single interface. We provide a separate
+ * function to set it up to distinguish between hardware
+ * initialisation and interface setup.
+ *
+ * The base_addr and irq parameters are passed on to netdev for use
+ * with SIOCGIFMAP.
+ */
+int orinoco_if_add(struct orinoco_private *priv,
+                  unsigned long base_addr,
+                  unsigned int irq)
+{
+       struct wiphy *wiphy = priv_to_wiphy(priv);
+       struct wireless_dev *wdev;
+       struct net_device *dev;
+       int ret;
+
+       dev = alloc_etherdev(sizeof(struct wireless_dev));
+
+       if (!dev)
+               return -ENOMEM;
+
+       /* Initialise wireless_dev */
+       wdev = netdev_priv(dev);
+       wdev->wiphy = wiphy;
+       wdev->iftype = NL80211_IFTYPE_STATION;
+
+       /* Setup / override net_device fields */
+       dev->ieee80211_ptr = wdev;
+       dev->netdev_ops = &orinoco_netdev_ops;
+       dev->watchdog_timeo = HZ; /* 1 second timeout */
+       dev->ethtool_ops = &orinoco_ethtool_ops;
+       dev->wireless_handlers = &orinoco_handler_def;
+#ifdef WIRELESS_SPY
+       dev->wireless_data = &priv->wireless_data;
+#endif
+       /* we use the default eth_mac_addr for setting the MAC addr */
+
+       /* Reserve space in skb for the SNAP header */
+       dev->hard_header_len += ENCAPS_OVERHEAD;
+
+       netif_carrier_off(dev);
+
+       memcpy(dev->dev_addr, wiphy->perm_addr, ETH_ALEN);
+
+       dev->base_addr = base_addr;
+       dev->irq = irq;
+
+       SET_NETDEV_DEV(dev, priv->dev);
+       ret = register_netdev(dev);
+       if (ret)
+               goto fail;
+
+       priv->ndev = dev;
+
+       /* Report what we've done */
+       dev_dbg(priv->dev, "Registerred interface %s.\n", dev->name);
+
+       return 0;
+
+ fail:
+       free_netdev(dev);
+       return ret;
+}
+EXPORT_SYMBOL(orinoco_if_add);
+
+void orinoco_if_del(struct orinoco_private *priv)
+{
+       struct net_device *dev = priv->ndev;
+
+       unregister_netdev(dev);
+       free_netdev(dev);
+}
+EXPORT_SYMBOL(orinoco_if_del);
+
+void free_orinocodev(struct orinoco_private *priv)
 {
-       struct orinoco_private *priv = netdev_priv(dev);
+       struct wiphy *wiphy = priv_to_wiphy(priv);
        struct orinoco_rx_data *rx_data, *temp;
+       struct orinoco_scan_data *sd, *sdtemp;
+
+       wiphy_unregister(wiphy);
 
        /* If the tasklet is scheduled when we call tasklet_kill it
         * will run one final time. However the tasklet will only
@@ -2612,21 +2267,80 @@ void free_orinocodev(struct net_device *dev)
                kfree(rx_data);
        }
 
+       cancel_work_sync(&priv->process_scan);
+       /* Explicitly drain priv->scan_list */
+       list_for_each_entry_safe(sd, sdtemp, &priv->scan_list, list) {
+               list_del(&sd->list);
+
+               if ((sd->len > 0) && sd->buf)
+                       kfree(sd->buf);
+               kfree(sd);
+       }
+
        orinoco_unregister_pm_notifier(priv);
        orinoco_uncache_fw(priv);
 
        priv->wpa_ie_len = 0;
        kfree(priv->wpa_ie);
        orinoco_mic_free(priv);
-       orinoco_bss_data_free(priv);
-       free_netdev(dev);
+       wiphy_free(wiphy);
 }
 EXPORT_SYMBOL(free_orinocodev);
 
+int orinoco_up(struct orinoco_private *priv)
+{
+       struct net_device *dev = priv->ndev;
+       unsigned long flags;
+       int err;
+
+       spin_lock_irqsave(&priv->lock, flags);
+
+       err = orinoco_reinit_firmware(priv);
+       if (err) {
+               printk(KERN_ERR "%s: Error %d re-initializing firmware\n",
+                      dev->name, err);
+               goto exit;
+       }
+
+       netif_device_attach(dev);
+       priv->hw_unavailable--;
+
+       if (priv->open && !priv->hw_unavailable) {
+               err = __orinoco_up(priv);
+               if (err)
+                       printk(KERN_ERR "%s: Error %d restarting card\n",
+                              dev->name, err);
+       }
+
+exit:
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       return 0;
+}
+EXPORT_SYMBOL(orinoco_up);
+
+void orinoco_down(struct orinoco_private *priv)
+{
+       struct net_device *dev = priv->ndev;
+       unsigned long flags;
+       int err;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       err = __orinoco_down(priv);
+       if (err)
+               printk(KERN_WARNING "%s: Error %d downing interface\n",
+                      dev->name, err);
+
+       netif_device_detach(dev);
+       priv->hw_unavailable++;
+       spin_unlock_irqrestore(&priv->lock, flags);
+}
+EXPORT_SYMBOL(orinoco_down);
+
 static void orinoco_get_drvinfo(struct net_device *dev,
                                struct ethtool_drvinfo *info)
 {
-       struct orinoco_private *priv = netdev_priv(dev);
+       struct orinoco_private *priv = ndev_priv(dev);
 
        strncpy(info->driver, DRIVER_NAME, sizeof(info->driver) - 1);
        strncpy(info->version, DRIVER_VERSION, sizeof(info->version) - 1);