vserver 2.0 rc7
[linux-2.6.git] / drivers / net / wireless / orinoco.c
index 8f74605..a3a3243 100644 (file)
  *       in the rx_dropped statistics.
  *     o Provided a module parameter to suppress linkstatus messages.
  *
+ * v0.13e -> v0.14alpha1 - 30 Sep 2003 - David Gibson
+ *     o Replaced priv->connected logic with netif_carrier_on/off()
+ *       calls.
+ *     o Remove has_ibss_any and never set the CREATEIBSS RID when
+ *       the ESSID is empty.  Too many firmwares break if we do.
+ *     o 2.6 merges: Replace pdev->slot_name with pci_name(), remove
+ *       __devinitdata from PCI ID tables, use free_netdev().
+ *     o Enabled shared-key authentication for Agere firmware (from
+ *       Robert J. Moore <Robert.J.Moore AT allanbank.com>
+ *     o Move netif_wake_queue() (back) to the Tx completion from the
+ *       ALLOC event.  This seems to prevent/mitigate the rolling
+ *       error -110 problems at least on some Intersil firmwares.
+ *       Theoretically reduces performance, but I can't measure it.
+ *       Patch from Andrew Tridgell <tridge AT samba.org>
+ *
+ * v0.14alpha1 -> v0.14alpha2 - 20 Oct 2003 - David Gibson
+ *     o Correctly turn off shared-key authentication when requested
+ *       (bugfix from Robert J. Moore).
+ *     o Correct airport sleep interfaces for current 2.6 kernels.
+ *     o Add code for key change without disabling/enabling the MAC
+ *       port.  This is supposed to allow 802.1x to work sanely, but
+ *       doesn't seem to yet.
+ *
  * TODO
  *     o New wireless extensions API (patch from Moustafa
  *       Youssef, updated by Jim Carter and Pavel Roskin).
@@ -461,12 +484,14 @@ MODULE_LICENSE("Dual MPL/GPL");
 /* Level of debugging. Used in the macros in orinoco.h */
 #ifdef ORINOCO_DEBUG
 int orinoco_debug = ORINOCO_DEBUG;
-module_param(orinoco_debug, int, 0);
+module_param(orinoco_debug, int, 0644);
+MODULE_PARM_DESC(orinoco_debug, "Debug level");
 EXPORT_SYMBOL(orinoco_debug);
 #endif
 
 static int suppress_linkstatus; /* = 0 */
-module_param(suppress_linkstatus, bool, 0);
+module_param(suppress_linkstatus, bool, 0644);
+MODULE_PARM_DESC(suppress_linkstatus, "Don't log link status changes");
 
 /********************************************************************/
 /* Compile time configuration and compatibility stuff               */
@@ -784,7 +809,7 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
                return 1;
        }
 
-       if (! priv->connected) {
+       if (! netif_carrier_ok(dev)) {
                /* Oops, the firmware hasn't established a connection,
                    silently drop the packet (this seems to be the
                    safest approach). */
@@ -805,8 +830,9 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
        desc.tx_control = cpu_to_le16(HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX);
        err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc), txfid, 0);
        if (err) {
-               printk(KERN_ERR "%s: Error %d writing Tx descriptor to BAP\n",
-                      dev->name, err);
+               if (net_ratelimit())
+                       printk(KERN_ERR "%s: Error %d writing Tx descriptor "
+                              "to BAP\n", dev->name, err);
                stats->tx_errors++;
                goto fail;
        }
@@ -836,8 +862,9 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
                err  = hermes_bap_pwrite(hw, USER_BAP, &hdr, sizeof(hdr),
                                         txfid, HERMES_802_3_OFFSET);
                if (err) {
-                       printk(KERN_ERR "%s: Error %d writing packet header to BAP\n",
-                              dev->name, err);
+                       if (net_ratelimit())
+                               printk(KERN_ERR "%s: Error %d writing packet "
+                                      "header to BAP\n", dev->name, err);
                        stats->tx_errors++;
                        goto fail;
                }
@@ -897,8 +924,6 @@ static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw)
                        printk(KERN_WARNING "%s: Allocate event on unexpected fid (%04X)\n",
                               dev->name, fid);
                return;
-       } else {
-               netif_wake_queue(dev);
        }
 
        hermes_write_regn(hw, ALLOCFID, DUMMY_FID);
@@ -911,6 +936,8 @@ static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw)
 
        stats->tx_packets++;
 
+       netif_wake_queue(dev);
+
        hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
 }
 
@@ -937,6 +964,7 @@ static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw)
        
        stats->tx_errors++;
 
+       netif_wake_queue(dev);
        hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
 }
 
@@ -962,15 +990,17 @@ static void orinoco_tx_timeout(struct net_device *dev)
 
 /* Does the frame have a SNAP header indicating it should be
  * de-encapsulated to Ethernet-II? */
-static inline int is_ethersnap(struct header_struct *hdr)
+static inline int is_ethersnap(void *_hdr)
 {
+       u8 *hdr = _hdr;
+
        /* We de-encapsulate all packets which, a) have SNAP headers
         * (i.e. SSAP=DSAP=0xaa and CTRL=0x3 in the 802.2 LLC header
         * and where b) the OUI of the SNAP header is 00:00:00 or
         * 00:00:f8 - we need both because different APs appear to use
         * different OUIs for some reason */
-       return (memcmp(&hdr->dsap, &encaps_hdr, 5) == 0)
-               && ( (hdr->oui[2] == 0x00) || (hdr->oui[2] == 0xf8) );
+       return (memcmp(hdr, &encaps_hdr, 5) == 0)
+               && ( (hdr[5] == 0x00) || (hdr[5] == 0xf8) );
 }
 
 static inline void orinoco_spy_gather(struct net_device *dev, u_char *mac,
@@ -1269,6 +1299,7 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
        case HERMES_INQ_LINKSTATUS: {
                struct hermes_linkstatus linkstatus;
                u16 newstatus;
+               int connected;
 
                if (len != sizeof(linkstatus)) {
                        printk(KERN_WARNING "%s: Unexpected size for linkstatus frame (%d bytes)\n",
@@ -1280,15 +1311,14 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
                                  len / 2);
                newstatus = le16_to_cpu(linkstatus.linkstatus);
 
-               if ( (newstatus == HERMES_LINKSTATUS_CONNECTED)
-                    || (newstatus == HERMES_LINKSTATUS_AP_CHANGE)
-                    || (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE) )
-                       priv->connected = 1;
-               else if ( (newstatus == HERMES_LINKSTATUS_NOT_CONNECTED)
-                         || (newstatus == HERMES_LINKSTATUS_DISCONNECTED)
-                         || (newstatus == HERMES_LINKSTATUS_AP_OUT_OF_RANGE)
-                         || (newstatus == HERMES_LINKSTATUS_ASSOC_FAILED) )
-                       priv->connected = 0;
+               connected = (newstatus == HERMES_LINKSTATUS_CONNECTED)
+                       || (newstatus == HERMES_LINKSTATUS_AP_CHANGE)
+                       || (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE);
+
+               if (connected)
+                       netif_carrier_on(dev);
+               else
+                       netif_carrier_off(dev);
 
                if (newstatus != priv->last_linkstatus)
                        print_linkstatus(dev, newstatus);
@@ -1297,8 +1327,8 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
        }
        break;
        default:
-               printk(KERN_DEBUG "%s: Unknown information frame received "
-                      "(type %04x).\n", dev->name, type);
+               printk(KERN_DEBUG "%s: Unknown information frame received: "
+                      "type 0x%04x, length %d\n", dev->name, type, len);
                /* We don't actually do anything about it */
                break;
        }
@@ -1307,7 +1337,7 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
 static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw)
 {
        if (net_ratelimit())
-               printk(KERN_WARNING "%s: Information frame lost.\n", dev->name);
+               printk(KERN_DEBUG "%s: Information frame lost.\n", dev->name);
 }
 
 /********************************************************************/
@@ -1366,8 +1396,8 @@ int __orinoco_down(struct net_device *dev)
        }
        
        /* firmware will have to reassociate */
+       netif_carrier_off(dev);
        priv->last_linkstatus = 0xffff;
-       priv->connected = 0;
 
        return 0;
 }
@@ -1430,55 +1460,46 @@ static int __orinoco_hw_set_bitrate(struct orinoco_private *priv)
        return err;
 }
 
-static int __orinoco_hw_setup_wep(struct orinoco_private *priv)
+/* Change the WEP keys and/or the current keys.  Can be called
+ * either from __orinoco_hw_setup_wep() or directly from
+ * orinoco_ioctl_setiwencode().  In the later case the association
+ * with the AP is not broken (if the firmware can handle it),
+ * which is needed for 802.1x implementations. */
+static int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv)
 {
        hermes_t *hw = &priv->hw;
        int err = 0;
-       int     master_wep_flag;
-       int     auth_flag;
 
        switch (priv->firmware_type) {
-       case FIRMWARE_TYPE_AGERE: /* Agere style WEP */
-               if (priv->wep_on) {
-                       err = hermes_write_wordrec(hw, USER_BAP,
-                                                  HERMES_RID_CNFTXKEY_AGERE,
-                                                  priv->tx_key);
-                       if (err)
-                               return err;
-                       
-                       err = HERMES_WRITE_RECORD(hw, USER_BAP,
-                                                 HERMES_RID_CNFWEPKEYS_AGERE,
-                                                 &priv->keys);
-                       if (err)
-                               return err;
-               }
+       case FIRMWARE_TYPE_AGERE:
+               err = HERMES_WRITE_RECORD(hw, USER_BAP,
+                                         HERMES_RID_CNFWEPKEYS_AGERE,
+                                         &priv->keys);
+               if (err)
+                       return err;
                err = hermes_write_wordrec(hw, USER_BAP,
-                                          HERMES_RID_CNFWEPENABLED_AGERE,
-                                          priv->wep_on);
+                                          HERMES_RID_CNFTXKEY_AGERE,
+                                          priv->tx_key);
                if (err)
                        return err;
                break;
-
-       case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */
-       case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */
-               master_wep_flag = 0;            /* Off */
-               if (priv->wep_on) {
+       case FIRMWARE_TYPE_INTERSIL:
+       case FIRMWARE_TYPE_SYMBOL:
+               {
                        int keylen;
                        int i;
 
-                       /* Fudge around firmware weirdness */
+                       /* Force uniform key length to work around firmware bugs */
                        keylen = le16_to_cpu(priv->keys[priv->tx_key].len);
                        
+                       if (keylen > LARGE_KEY_SIZE) {
+                               printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n",
+                                      priv->ndev->name, priv->tx_key, keylen);
+                               return -E2BIG;
+                       }
+
                        /* Write all 4 keys */
                        for(i = 0; i < ORINOCO_MAX_KEYS; i++) {
-/*                             int keylen = le16_to_cpu(priv->keys[i].len); */
-                               
-                               if (keylen > LARGE_KEY_SIZE) {
-                                       printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n",
-                                              priv->ndev->name, i, keylen);
-                                       return -E2BIG;
-                               }
-
                                err = hermes_write_ltv(hw, USER_BAP,
                                                       HERMES_RID_CNFDEFAULTKEY0 + i,
                                                       HERMES_BYTES_TO_RECLEN(keylen),
@@ -1493,27 +1514,63 @@ static int __orinoco_hw_setup_wep(struct orinoco_private *priv)
                                                   priv->tx_key);
                        if (err)
                                return err;
-                       
-                       if (priv->wep_restrict) {
-                               auth_flag = 2;
-                               master_wep_flag = 3;
-                       } else {
-                               /* Authentication is where Intersil and Symbol
-                                * firmware differ... */
-                               auth_flag = 1;
-                               if (priv->firmware_type == FIRMWARE_TYPE_SYMBOL)
-                                       master_wep_flag = 3; /* Symbol */ 
-                               else 
-                                       master_wep_flag = 1; /* Intersil */
-                       }
+               }
+               break;
+       }
 
+       return 0;
+}
+
+static int __orinoco_hw_setup_wep(struct orinoco_private *priv)
+{
+       hermes_t *hw = &priv->hw;
+       int err = 0;
+       int master_wep_flag;
+       int auth_flag;
+
+       if (priv->wep_on)
+               __orinoco_hw_setup_wepkeys(priv);
+
+       if (priv->wep_restrict)
+               auth_flag = HERMES_AUTH_SHARED_KEY;
+       else
+               auth_flag = HERMES_AUTH_OPEN;
+
+       switch (priv->firmware_type) {
+       case FIRMWARE_TYPE_AGERE: /* Agere style WEP */
+               if (priv->wep_on) {
+                       /* Enable the shared-key authentication. */
+                       err = hermes_write_wordrec(hw, USER_BAP,
+                                                  HERMES_RID_CNFAUTHENTICATION_AGERE,
+                                                  auth_flag);
+               }
+               err = hermes_write_wordrec(hw, USER_BAP,
+                                          HERMES_RID_CNFWEPENABLED_AGERE,
+                                          priv->wep_on);
+               if (err)
+                       return err;
+               break;
+
+       case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */
+       case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */
+               if (priv->wep_on) {
+                       if (priv->wep_restrict ||
+                           (priv->firmware_type == FIRMWARE_TYPE_SYMBOL))
+                               master_wep_flag = HERMES_WEP_PRIVACY_INVOKED |
+                                                 HERMES_WEP_EXCL_UNENCRYPTED;
+                       else
+                               master_wep_flag = HERMES_WEP_PRIVACY_INVOKED;
 
                        err = hermes_write_wordrec(hw, USER_BAP,
                                                   HERMES_RID_CNFAUTHENTICATION,
                                                   auth_flag);
                        if (err)
                                return err;
-               }
+               } else
+                       master_wep_flag = 0;
+
+               if (priv->iw_mode == IW_MODE_MONITOR)
+                       master_wep_flag |= HERMES_WEP_HOST_DECRYPT;
 
                /* Master WEP setting : on/off */
                err = hermes_write_wordrec(hw, USER_BAP,
@@ -1523,13 +1580,6 @@ static int __orinoco_hw_setup_wep(struct orinoco_private *priv)
                        return err;     
 
                break;
-
-       default:
-               if (priv->wep_on) {
-                       printk(KERN_ERR "%s: WEP enabled, although not supported!\n",
-                              priv->ndev->name);
-                       return -EINVAL;
-               }
        }
 
        return 0;
@@ -1574,21 +1624,26 @@ static int __orinoco_program_rids(struct net_device *dev)
        }
 
        if (priv->has_ibss) {
-               err = hermes_write_wordrec(hw, USER_BAP,
-                                          HERMES_RID_CNFCREATEIBSS,
-                                          priv->createibss);
-               if (err) {
-                       printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n", dev->name, err);
-                       return err;
-               }
+               u16 createibss;
 
-               if ((strlen(priv->desired_essid) == 0) && (priv->createibss)
-                  && (!priv->has_ibss_any)) {
+               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;
                }
        }
 
@@ -1785,7 +1840,8 @@ __orinoco_set_multicast_list(struct net_device *dev)
                }
                
                if (p)
-                       printk(KERN_WARNING "Multicast list is longer than mc_count\n");
+                       printk(KERN_WARNING "%s: Multicast list is "
+                              "longer than mc_count\n", dev->name);
 
                err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFGROUPADDRESSES,
                                       HERMES_BYTES_TO_RECLEN(priv->mc_count * ETH_ALEN),
@@ -1878,7 +1934,7 @@ static void orinoco_reset(struct net_device *dev)
 
        priv->hw_unavailable++;
        priv->last_linkstatus = 0xffff; /* firmware will have to reassociate */
-       priv->connected = 0;
+       netif_carrier_off(dev);
 
        orinoco_unlock(priv, &flags);
 
@@ -2014,51 +2070,81 @@ irqreturn_t orinoco_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 /* Initialization                                                   */
 /********************************************************************/
 
-struct sta_id {
+struct comp_id {
        u16 id, variant, major, minor;
 } __attribute__ ((packed));
 
-static int determine_firmware_type(struct net_device *dev, struct sta_id *sta_id)
+static inline fwtype_t determine_firmware_type(struct comp_id *nic_id)
 {
-       /* FIXME: this is fundamentally broken */
-       unsigned int firmver = ((u32)sta_id->major << 16) | sta_id->minor;
-       
-       if (sta_id->variant == 1)
+       if (nic_id->id < 0x8000)
                return FIRMWARE_TYPE_AGERE;
-       else if ((sta_id->variant == 2) &&
-                  ((firmver == 0x10001) || (firmver == 0x20001)))
+       else if (nic_id->id == 0x8000 && nic_id->major == 0)
                return FIRMWARE_TYPE_SYMBOL;
        else
                return FIRMWARE_TYPE_INTERSIL;
 }
 
-static void determine_firmware(struct net_device *dev)
+/* 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 sta_id sta_id;
+       struct comp_id nic_id, sta_id;
        unsigned int firmver;
        char tmp[SYMBOL_MAX_VER_LEN+1];
 
+       /* 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_WARNING "%s: Error %d reading firmware info. Wildly guessing capabilities...\n",
+               printk(KERN_ERR "%s: Cannot read station identity: error %d\n",
                       dev->name, err);
-               memset(&sta_id, 0, sizeof(sta_id));
+               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",
+       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);
 
-       if (! priv->firmware_type)
-               priv->firmware_type = determine_firmware_type(dev, &sta_id);
+       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;
@@ -2066,7 +2152,6 @@ static void determine_firmware(struct net_device *dev)
        priv->has_preamble = 0;
        priv->has_port3 = 1;
        priv->has_ibss = 1;
-       priv->has_ibss_any = 0;
        priv->has_wep = 0;
        priv->has_big_wep = 0;
 
@@ -2075,14 +2160,12 @@ static void determine_firmware(struct net_device *dev)
        case FIRMWARE_TYPE_AGERE:
                /* Lucent Wavelan IEEE, Lucent Orinoco, Cabletron RoamAbout,
                   ELSA, Melco, HP, IBM, Dell 1150, Compaq 110/210 */
-               printk(KERN_DEBUG "%s: Looks like a Lucent/Agere firmware "
-                      "version %d.%02d\n", dev->name,
-                      sta_id.major, sta_id.minor);
+               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_ibss_any = (firmver >= 0x60010);
                priv->has_wep = (firmver >= 0x40020);
                priv->has_big_wep = 1; /* FIXME: this is wrong - how do we tell
                                          Gold cards from the others? */
@@ -2121,14 +2204,15 @@ static void determine_firmware(struct net_device *dev)
                        tmp[SYMBOL_MAX_VER_LEN] = '\0';
                }
 
-               printk(KERN_DEBUG "%s: Looks like a Symbol firmware "
-                      "version [%s] (parsing to %X)\n", dev->name,
-                      tmp, firmver);
+               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);
+               priv->has_pm = (firmver >= 0x20000 && firmver < 0x22000) || 
+                              (firmver >= 0x29000 && firmver < 0x30000) ||
+                              firmver >= 0x31000;
                priv->has_preamble = (firmver >= 0x20000);
                priv->ibss_port = 4;
                /* Tested with Intel firmware : 0x20015 => Jean II */
@@ -2140,9 +2224,9 @@ static void determine_firmware(struct net_device *dev)
                 * different and less well tested */
                /* D-Link MAC : 00:40:05:* */
                /* Addtron MAC : 00:90:D1:* */
-               printk(KERN_DEBUG "%s: Looks like an Intersil firmware "
-                      "version %d.%d.%d\n", dev->name,
-                      sta_id.major, sta_id.minor, sta_id.variant);
+               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;
@@ -2160,9 +2244,11 @@ static void determine_firmware(struct net_device *dev)
                        priv->ibss_port = 1;
                }
                break;
-       default:
-               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)
@@ -2188,7 +2274,12 @@ static int orinoco_init(struct net_device *dev)
                goto out;
        }
 
-       determine_firmware(dev);
+       err = determine_firmware(dev);
+       if (err != 0) {
+               printk(KERN_ERR "%s: Incompatible firmware, aborting\n",
+                      dev->name);
+               goto out;
+       }
 
        if (priv->has_port3)
                printk(KERN_DEBUG "%s: Ad-hoc demo mode supported\n", dev->name);
@@ -2388,13 +2479,18 @@ struct net_device *alloc_orinocodev(int sizeof_card,
                                   * hardware */
        INIT_WORK(&priv->reset_work, (void (*)(void *))orinoco_reset, dev);
 
+       netif_carrier_off(dev);
        priv->last_linkstatus = 0xffff;
-       priv->connected = 0;
 
        return dev;
 
 }
 
+void free_orinocodev(struct net_device *dev)
+{
+       free_netdev(dev);
+}
+
 /********************************************************************/
 /* Wireless extensions                                              */
 /********************************************************************/
@@ -2553,9 +2649,8 @@ static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq
 
        TRACE_ENTER(dev->name);
 
-       err = verify_area(VERIFY_WRITE, rrq->pointer, sizeof(range));
-       if (err)
-               return err;
+       if (!access_ok(VERIFY_WRITE, rrq->pointer, sizeof(range)))
+               return -EFAULT;
 
        rrq->length = sizeof(range);
 
@@ -2686,11 +2781,17 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *er
        int err = 0;
        char keybuf[ORINOCO_MAX_KEY_SIZE];
        unsigned long flags;
-       
+
+       if (! priv->has_wep)
+               return -EOPNOTSUPP;
+
        if (erq->pointer) {
-               /* We actually have a key to set */
-               if ( (erq->length < SMALL_KEY_SIZE) || (erq->length > ORINOCO_MAX_KEY_SIZE) )
-                       return -EINVAL;
+               /* We actually have a key to set - check its length */
+               if (erq->length > LARGE_KEY_SIZE)
+                       return -E2BIG;
+
+               if ( (erq->length > SMALL_KEY_SIZE) && !priv->has_big_wep )
+                       return -E2BIG;
                
                if (copy_from_user(keybuf, erq->pointer, erq->length))
                        return -EFAULT;
@@ -2698,19 +2799,8 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *er
 
        if (orinoco_lock(priv, &flags) != 0)
                return -EBUSY;
-       
+
        if (erq->pointer) {
-               if (erq->length > ORINOCO_MAX_KEY_SIZE) {
-                       err = -E2BIG;
-                       goto out;
-               }
-               
-               if ( (erq->length > LARGE_KEY_SIZE)
-                    || ( ! priv->has_big_wep && (erq->length > SMALL_KEY_SIZE))  ) {
-                       err = -EINVAL;
-                       goto out;
-               }
-               
                if ((index < 0) || (index >= ORINOCO_MAX_KEYS))
                        index = priv->tx_key;
 
@@ -2721,7 +2811,7 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *er
                        xlen = SMALL_KEY_SIZE;
                } else
                        xlen = 0;
-               
+
                /* Switch on WEP if off */
                if ((!enable) && (xlen > 0)) {
                        setindex = index;
@@ -2745,10 +2835,9 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *er
                        setindex = index;
                }
        }
-       
+
        if (erq->flags & IW_ENCODE_DISABLED)
                enable = 0;
-       /* Only for Prism2 & Symbol cards (so far) - Jean II */
        if (erq->flags & IW_ENCODE_OPEN)
                restricted = 0;
        if (erq->flags & IW_ENCODE_RESTRICTED)
@@ -2761,6 +2850,15 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *er
                memcpy(priv->keys[index].data, keybuf, erq->length);
        }
        priv->tx_key = setindex;
+
+       /* Try fast key change if connected and only keys are changed */
+       if (priv->wep_on && enable && (priv->wep_restrict == restricted) &&
+           netif_carrier_ok(dev)) {
+               err = __orinoco_hw_setup_wepkeys(priv);
+               /* No need to commit if successful */
+               goto out;
+       }
+
        priv->wep_on = enable;
        priv->wep_restrict = restricted;
 
@@ -2778,6 +2876,9 @@ static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_point *er
        char keybuf[ORINOCO_MAX_KEY_SIZE];
        unsigned long flags;
 
+       if (! priv->has_wep)
+               return -EOPNOTSUPP;
+
        if (orinoco_lock(priv, &flags) != 0)
                return -EBUSY;
 
@@ -2788,23 +2889,18 @@ static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_point *er
        if (! priv->wep_on)
                erq->flags |= IW_ENCODE_DISABLED;
        erq->flags |= index + 1;
-       
-       /* Only for symbol cards - Jean II */
-       if (priv->firmware_type != FIRMWARE_TYPE_AGERE) {
-               if(priv->wep_restrict)
-                       erq->flags |= IW_ENCODE_RESTRICTED;
-               else
-                       erq->flags |= IW_ENCODE_OPEN;
-       }
+
+       if (priv->wep_restrict)
+               erq->flags |= IW_ENCODE_RESTRICTED;
+       else
+               erq->flags |= IW_ENCODE_OPEN;
 
        xlen = le16_to_cpu(priv->keys[index].len);
 
        erq->length = xlen;
 
-       if (erq->pointer) {
-               memcpy(keybuf, priv->keys[index].data, ORINOCO_MAX_KEY_SIZE);
-       }
-       
+       memcpy(keybuf, priv->keys[index].data, ORINOCO_MAX_KEY_SIZE);
+
        orinoco_unlock(priv, &flags);
 
        if (erq->pointer) {
@@ -3045,8 +3141,9 @@ static int orinoco_ioctl_setfrag(struct net_device *dev, struct iw_param *frq)
                        priv->mwo_robust = 0;
                else {
                        if (frq->fixed)
-                               printk(KERN_WARNING "%s: Fixed fragmentation not \
-supported on this firmware. Using MWO robust instead.\n", dev->name);
+                               printk(KERN_WARNING "%s: Fixed fragmentation is "
+                                      "not supported on this firmware. "
+                                      "Using MWO robust instead.\n", dev->name);
                        priv->mwo_robust = 1;
                }
        } else {
@@ -3518,7 +3615,7 @@ static int orinoco_ioctl_getspy(struct net_device *dev, struct iw_point *srq)
                }
                /* Copy stats */
                /* In theory, we should disable irqs while copying the stats
-                * because the rx path migh update it in the middle...
+                * because the rx path might update it in the middle...
                 * Bah, who care ? - Jean II */
                memcpy(&spy_stat, priv->spy_stat,
                       sizeof(struct iw_quality) * IW_MAX_SPY);
@@ -3609,22 +3706,12 @@ orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
                break;
 
        case SIOCSIWENCODE:
-               if (! priv->has_wep) {
-                       err = -EOPNOTSUPP;
-                       break;
-               }
-
                err = orinoco_ioctl_setiwencode(dev, &wrq->u.encoding);
                if (! err)
                        changed = 1;
                break;
 
        case SIOCGIWENCODE:
-               if (! priv->has_wep) {
-                       err = -EOPNOTSUPP;
-                       break;
-               }
-
                if (! capable(CAP_NET_ADMIN)) {
                        err = -EPERM;
                        break;
@@ -4127,6 +4214,7 @@ static void show_rx_frame(struct orinoco_rxframe_hdr *frame)
 /********************************************************************/
 
 EXPORT_SYMBOL(alloc_orinocodev);
+EXPORT_SYMBOL(free_orinocodev);
 
 EXPORT_SYMBOL(__orinoco_up);
 EXPORT_SYMBOL(__orinoco_down);