[PATCH] orinoco: scanning support
authorChristoph Hellwig <hch@lst.de>
Sat, 18 Jun 2005 23:27:56 +0000 (01:27 +0200)
committerJeff Garzik <jgarzik@pobox.com>
Mon, 27 Jun 2005 04:23:55 +0000 (00:23 -0400)
Patch from Pavel Roskin

drivers/net/wireless/orinoco.c
drivers/net/wireless/orinoco.h

index c057b7f..38fd862 100644 (file)
@@ -514,6 +514,10 @@ MODULE_PARM_DESC(ignore_disconnect, "Don't report lost link to the network layer
 /* Internal constants                                               */
 /********************************************************************/
 
+/* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */
+static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
+#define ENCAPS_OVERHEAD                (sizeof(encaps_hdr) + 2)
+
 #define ORINOCO_MIN_MTU                256
 #define ORINOCO_MAX_MTU                (IEEE802_11_DATA_LEN - ENCAPS_OVERHEAD)
 
@@ -579,25 +583,42 @@ static struct {
 /* Data types                                                       */
 /********************************************************************/
 
-struct header_struct {
-       /* 802.3 */
-       u8 dest[ETH_ALEN];
-       u8 src[ETH_ALEN];
-       u16 len;
-       /* 802.2 */
+/* Used in Event handling.
+ * We avoid nested structres as they break on ARM -- Moustafa */
+struct hermes_tx_descriptor_802_11 {
+       /* hermes_tx_descriptor */
+       u16 status;
+       u16 reserved1;
+       u16 reserved2;
+       u32 sw_support;
+       u8 retry_count;
+       u8 tx_rate;
+       u16 tx_control;
+
+       /* ieee802_11_hdr */
+       u16 frame_ctl;
+       u16 duration_id;
+       u8 addr1[ETH_ALEN];
+       u8 addr2[ETH_ALEN];
+       u8 addr3[ETH_ALEN];
+       u16 seq_ctl;
+       u8 addr4[ETH_ALEN];
+       u16 data_len;
+
+       /* ethhdr */
+       unsigned char   h_dest[ETH_ALEN];       /* destination eth addr */
+       unsigned char   h_source[ETH_ALEN];     /* source ether addr    */
+       unsigned short  h_proto;                /* packet type ID field */
+
+       /* p8022_hdr */
        u8 dsap;
        u8 ssap;
        u8 ctrl;
-       /* SNAP */
        u8 oui[3];
+
        u16 ethertype;
 } __attribute__ ((packed));
 
-/* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */
-u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
-
-#define ENCAPS_OVERHEAD                (sizeof(encaps_hdr) + 2)
-
 struct hermes_rx_descriptor {
        u16 status;
        u32 time;
@@ -958,26 +979,55 @@ static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw)
        struct orinoco_private *priv = netdev_priv(dev);
        struct net_device_stats *stats = &priv->stats;
        u16 fid = hermes_read_regn(hw, TXCOMPLFID);
-       struct hermes_tx_descriptor desc;
+       struct hermes_tx_descriptor_802_11 hdr;
        int err = 0;
 
        if (fid == DUMMY_FID)
                return; /* Nothing's really happened */
 
-       err = hermes_bap_pread(hw, IRQ_BAP, &desc, sizeof(desc), fid, 0);
+       /* Read the frame header */
+       err = hermes_bap_pread(hw, IRQ_BAP, &hdr,
+                              sizeof(struct hermes_tx_descriptor) +
+                              sizeof(struct ieee80211_hdr),
+                              fid, 0);
+
+       hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
+       stats->tx_errors++;
+
        if (err) {
                printk(KERN_WARNING "%s: Unable to read descriptor on Tx error "
                       "(FID=%04X error %d)\n",
                       dev->name, fid, err);
-       } else {
-               DEBUG(1, "%s: Tx error, status %d\n",
-                     dev->name, le16_to_cpu(desc.status));
+               return;
        }
        
-       stats->tx_errors++;
+       DEBUG(1, "%s: Tx error, err %d (FID=%04X)\n", dev->name,
+             err, fid);
+    
+       /* We produce a TXDROP event only for retry or lifetime
+        * exceeded, because that's the only status that really mean
+        * that this particular node went away.
+        * Other errors means that *we* screwed up. - Jean II */
+       hdr.status = le16_to_cpu(hdr.status);
+       if (hdr.status & (HERMES_TXSTAT_RETRYERR | HERMES_TXSTAT_AGEDERR)) {
+               union iwreq_data        wrqu;
+
+               /* Copy 802.11 dest address.
+                * We use the 802.11 header because the frame may
+                * not be 802.3 or may be mangled...
+                * In Ad-Hoc mode, it will be the node address.
+                * In managed mode, it will be most likely the AP addr
+                * User space will figure out how to convert it to
+                * whatever it needs (IP address or else).
+                * - Jean II */
+               memcpy(wrqu.addr.sa_data, hdr.addr1, ETH_ALEN);
+               wrqu.addr.sa_family = ARPHRD_ETHER;
+
+               /* Send event to user space */
+               wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL);
+       }
 
        netif_wake_queue(dev);
-       hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
 }
 
 static void orinoco_tx_timeout(struct net_device *dev)
@@ -1316,6 +1366,30 @@ static void orinoco_join_ap(struct net_device *dev)
        orinoco_unlock(priv, &flags);
 }
 
+/* Send new BSSID to userspace */
+static void orinoco_send_wevents(struct net_device *dev)
+{
+       struct orinoco_private *priv = netdev_priv(dev);
+       struct hermes *hw = &priv->hw;
+       union iwreq_data wrqu;
+       int err;
+       unsigned long flags;
+
+       if (orinoco_lock(priv, &flags) != 0)
+               return;
+
+       err = hermes_read_ltv(hw, IRQ_BAP, HERMES_RID_CURRENTBSSID,
+                             ETH_ALEN, NULL, wrqu.ap_addr.sa_data);
+       if (err != 0)
+               return;
+
+       wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+
+       /* Send event to user space */
+       wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
+       orinoco_unlock(priv, &flags);
+}
+
 static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
 {
        struct orinoco_private *priv = netdev_priv(dev);
@@ -1395,6 +1469,15 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
                        break;
                newstatus = le16_to_cpu(linkstatus.linkstatus);
 
+               /* Symbol firmware uses "out of range" to signal that
+                * 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) {
+                       hermes_inquire(hw, HERMES_INQ_HOSTSCAN_SYMBOL);
+                       break;
+               }
+
                connected = (newstatus == HERMES_LINKSTATUS_CONNECTED)
                        || (newstatus == HERMES_LINKSTATUS_AP_CHANGE)
                        || (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE);
@@ -1404,12 +1487,89 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
                else if (!ignore_disconnect)
                        netif_carrier_off(dev);
 
-               if (newstatus != priv->last_linkstatus)
+               if (newstatus != priv->last_linkstatus) {
+                       priv->last_linkstatus = newstatus;
                        print_linkstatus(dev, newstatus);
+                       /* The info frame contains only one word which is the
+                        * status (see hermes.h). The status is pretty boring
+                        * in itself, that's why we export the new BSSID...
+                        * Jean II */
+                       schedule_work(&priv->wevent_work);
+               }
+       }
+       break;
+       case HERMES_INQ_SCAN:
+               if (!priv->scan_inprogress && priv->bssid_fixed &&
+                   priv->firmware_type == FIRMWARE_TYPE_INTERSIL) {
+                       schedule_work(&priv->join_work);
+                       break;
+               }
+               /* fall through */
+       case HERMES_INQ_HOSTSCAN:
+       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;
+
+               /* Sanity check */
+               if (len > 4096) {
+                       printk(KERN_WARNING "%s: Scan results too large (%d bytes)\n",
+                              dev->name, len);
+                       break;
+               }
+
+               /* We are a strict producer. If the previous scan results
+                * have not been consumed, we just have to drop this
+                * frame. We can't remove the previous results ourselves,
+                * that would be *very* racy... Jean II */
+               if (priv->scan_result != NULL) {
+                       printk(KERN_WARNING "%s: Previous scan results not consumed, dropping info frame.\n", dev->name);
+                       break;
+               }
 
-               priv->last_linkstatus = newstatus;
+               /* Allocate buffer for results */
+               buf = kmalloc(len, GFP_ATOMIC);
+               if (buf == NULL)
+                       /* No memory, so can't printk()... */
+                       break;
+
+               /* Read scan data */
+               err = hermes_bap_pread(hw, IRQ_BAP, (void *) buf, len,
+                                      infofid, sizeof(info));
+               if (err)
+                       break;
+
+#ifdef ORINOCO_DEBUG
+               {
+                       int     i;
+                       printk(KERN_DEBUG "Scan result [%02X", buf[0]);
+                       for(i = 1; i < (len * 2); i++)
+                               printk(":%02X", buf[i]);
+                       printk("]\n");
+               }
+#endif /* ORINOCO_DEBUG */
+
+               /* Allow the clients to access the results */
+               priv->scan_len = len;
+               priv->scan_result = buf;
+
+               /* 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);
        }
        break;
+       case HERMES_INQ_SEC_STAT_AGERE:
+               /* Security status (Agere specific) */
+               /* Ignore this frame for now */
+               if (priv->firmware_type == FIRMWARE_TYPE_AGERE)
+                       break;
+               /* fall through */
        default:
                printk(KERN_DEBUG "%s: Unknown information frame received: "
                       "type 0x%04x, length %d\n", dev->name, type, len);
@@ -2010,6 +2170,11 @@ static void orinoco_reset(struct net_device *dev)
 
        orinoco_unlock(priv, &flags);
 
+       /* Scanning support: Cleanup of driver struct */
+       kfree(priv->scan_result);
+       priv->scan_result = NULL;
+       priv->scan_inprogress = 0;
+
        if (priv->hard_reset) {
                err = (*priv->hard_reset)(priv);
                if (err) {
@@ -2248,6 +2413,7 @@ static int determine_firmware(struct net_device *dev)
                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);
 
                /* Tested with Agere firmware :
                 *      1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II
@@ -2293,6 +2459,8 @@ static int determine_firmware(struct net_device *dev)
                priv->ibss_port = 4;
                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;
@@ -2312,6 +2480,7 @@ static int determine_firmware(struct net_device *dev)
                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;
@@ -2539,6 +2708,7 @@ struct net_device *alloc_orinocodev(int sizeof_card,
                                   * hardware */
        INIT_WORK(&priv->reset_work, (void (*)(void *))orinoco_reset, dev);
        INIT_WORK(&priv->join_work, (void (*)(void *))orinoco_join_ap, dev);
+       INIT_WORK(&priv->wevent_work, (void (*)(void *))orinoco_send_wevents, dev);
 
        netif_carrier_off(dev);
        priv->last_linkstatus = 0xffff;
@@ -2549,6 +2719,9 @@ struct net_device *alloc_orinocodev(int sizeof_card,
 
 void free_orinocodev(struct net_device *dev)
 {
+       struct orinoco_private *priv = netdev_priv(dev);
+
+       kfree(priv->scan_result);
        free_netdev(dev);
 }
 
@@ -3967,6 +4140,332 @@ static int orinoco_ioctl_getspy(struct net_device *dev,
        return 0;
 }
 
+/* Trigger a scan (look for other cells in the vicinity */
+static int orinoco_ioctl_setscan(struct net_device *dev,
+                                struct iw_request_info *info,
+                                struct iw_param *srq,
+                                char *extra)
+{
+       struct orinoco_private *priv = netdev_priv(dev);
+       hermes_t *hw = &priv->hw;
+       int err = 0;
+       unsigned long flags;
+
+       /* Note : you may have realised that, as this is a SET operation,
+        * this is priviledged and therefore a normal user can't
+        * perform scanning.
+        * This is not an error, while the device perform scanning,
+        * traffic doesn't flow, so it's a perfect DoS...
+        * Jean II */
+
+       if (orinoco_lock(priv, &flags) != 0)
+               return -EBUSY;
+
+       /* Scanning with port 0 disabled would fail */
+       if (!netif_running(dev)) {
+               err = -ENETDOWN;
+               goto out;
+       }
+
+       /* In monitor mode, the scan results are always empty.
+        * Probe responses are passed to the driver as received
+        * frames and could be processed in software. */
+       if (priv->iw_mode == IW_MODE_MONITOR) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       /* Note : because we don't lock out the irq handler, the way
+        * we access scan variables in priv is critical.
+        *      o scan_inprogress : not touched by irq handler
+        *      o scan_mode : not touched by irq handler
+        *      o scan_result : irq is strict producer, non-irq is strict
+        *              consumer.
+        *      o scan_len : synchronised with scan_result
+        * Before modifying anything on those variables, please think hard !
+        * Jean II */
+
+       /* If there is still some left-over scan results, get rid of it */
+       if (priv->scan_result != NULL) {
+               /* What's likely is that a client did crash or was killed
+                * between triggering the scan request and reading the
+                * results, so we need to reset everything.
+                * Some clients that are too slow may suffer from that...
+                * Jean II */
+               kfree(priv->scan_result);
+               priv->scan_result = NULL;
+       }
+
+       /* Save flags */
+       priv->scan_mode = srq->flags;
+
+       /* Always trigger scanning, even if it's in progress.
+        * This way, if the info frame get lost, we will recover somewhat
+        * gracefully  - Jean II */
+
+       if (priv->has_hostscan) {
+               switch (priv->firmware_type) {
+               case FIRMWARE_TYPE_SYMBOL:
+                       err = hermes_write_wordrec(hw, USER_BAP,
+                                                  HERMES_RID_CNFHOSTSCAN_SYMBOL,
+                                                  HERMES_HOSTSCAN_SYMBOL_ONCE |
+                                                  HERMES_HOSTSCAN_SYMBOL_BCAST);
+                       break;
+               case FIRMWARE_TYPE_INTERSIL: {
+                       u16 req[3];
+
+                       req[0] = cpu_to_le16(0x3fff);   /* All channels */
+                       req[1] = cpu_to_le16(0x0001);   /* rate 1 Mbps */
+                       req[2] = 0;                     /* Any ESSID */
+                       err = HERMES_WRITE_RECORD(hw, USER_BAP,
+                                                 HERMES_RID_CNFHOSTSCAN, &req);
+               }
+               break;
+               case FIRMWARE_TYPE_AGERE:
+                       err = hermes_write_wordrec(hw, USER_BAP,
+                                                  HERMES_RID_CNFSCANSSID_AGERE,
+                                                  0);  /* Any ESSID */
+                       if (err)
+                               break;
+
+                       err = hermes_inquire(hw, HERMES_INQ_SCAN);
+                       break;
+               }
+       } else
+               err = hermes_inquire(hw, HERMES_INQ_SCAN);
+
+       /* One more client */
+       if (! err)
+               priv->scan_inprogress = 1;
+
+ out:
+       orinoco_unlock(priv, &flags);
+       return err;
+}
+
+/* Translate scan data returned from the card to a card independant
+ * format that the Wireless Tools will understand - Jean II */
+static inline int orinoco_translate_scan(struct net_device *dev,
+                                        char *buffer,
+                                        char *scan,
+                                        int scan_len)
+{
+       struct orinoco_private *priv = netdev_priv(dev);
+       int                     offset;         /* In the scan data */
+       union hermes_scan_info *atom;
+       int                     atom_len;
+       u16                     capabilities;
+       u16                     channel;
+       struct iw_event         iwe;            /* Temporary buffer */
+       char *                  current_ev = buffer;
+       char *                  end_buf = buffer + IW_SCAN_MAX_DATA;
+
+       switch (priv->firmware_type) {
+       case FIRMWARE_TYPE_AGERE:
+               atom_len = sizeof(struct agere_scan_apinfo);
+               offset = 0;
+               break;
+       case FIRMWARE_TYPE_SYMBOL:
+               /* Lack of documentation necessitates this hack.
+                * Different firmwares have 68 or 76 byte long atoms.
+                * We try modulo first.  If the length divides by both,
+                * we check what would be the channel in the second
+                * frame for a 68-byte atom.  76-byte atoms have 0 there.
+                * Valid channel cannot be 0.  */
+               if (scan_len % 76)
+                       atom_len = 68;
+               else if (scan_len % 68)
+                       atom_len = 76;
+               else if (scan_len >= 1292 && scan[68] == 0)
+                       atom_len = 76;
+               else
+                       atom_len = 68;
+               offset = 0;
+               break;
+       case FIRMWARE_TYPE_INTERSIL:
+               offset = 4;
+               if (priv->has_hostscan)
+                       atom_len = scan[0] + (scan[1] << 8);
+               else
+                       atom_len = offsetof(struct prism2_scan_apinfo, atim);
+               break;
+       default:
+               return 0;
+       }
+
+       /* Check that we got an whole number of atoms */
+       if ((scan_len - offset) % atom_len) {
+               printk(KERN_ERR "%s: Unexpected scan data length %d, "
+                      "atom_len %d, offset %d\n", dev->name, scan_len,
+                      atom_len, offset);
+               return 0;
+       }
+
+       /* Read the entries one by one */
+       for (; offset + atom_len <= scan_len; offset += atom_len) {
+               /* Get next atom */
+               atom = (union hermes_scan_info *) (scan + offset);
+
+               /* First entry *MUST* be the AP MAC address */
+               iwe.cmd = SIOCGIWAP;
+               iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+               memcpy(iwe.u.ap_addr.sa_data, atom->a.bssid, ETH_ALEN);
+               current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN);
+
+               /* Other entries will be displayed in the order we give them */
+
+               /* Add the ESSID */
+               iwe.u.data.length = le16_to_cpu(atom->a.essid_len);
+               if (iwe.u.data.length > 32)
+                       iwe.u.data.length = 32;
+               iwe.cmd = SIOCGIWESSID;
+               iwe.u.data.flags = 1;
+               current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, atom->a.essid);
+
+               /* Add mode */
+               iwe.cmd = SIOCGIWMODE;
+               capabilities = le16_to_cpu(atom->a.capabilities);
+               if (capabilities & 0x3) {
+                       if (capabilities & 0x1)
+                               iwe.u.mode = IW_MODE_MASTER;
+                       else
+                               iwe.u.mode = IW_MODE_ADHOC;
+                       current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_UINT_LEN);
+               }
+
+               channel = atom->s.channel;
+               if ( (channel >= 1) && (channel <= NUM_CHANNELS) ) {
+                       /* Add frequency */
+                       iwe.cmd = SIOCGIWFREQ;
+                       iwe.u.freq.m = channel_frequency[channel-1] * 100000;
+                       iwe.u.freq.e = 1;
+                       current_ev = iwe_stream_add_event(current_ev, end_buf,
+                                                         &iwe, IW_EV_FREQ_LEN);
+               }
+
+               /* Add quality statistics */
+               iwe.cmd = IWEVQUAL;
+               iwe.u.qual.updated = 0x10;      /* no link quality */
+               iwe.u.qual.level = (__u8) le16_to_cpu(atom->a.level) - 0x95;
+               iwe.u.qual.noise = (__u8) le16_to_cpu(atom->a.noise) - 0x95;
+               /* Wireless tools prior to 27.pre22 will show link quality
+                * anyway, so we provide a reasonable value. */
+               if (iwe.u.qual.level > iwe.u.qual.noise)
+                       iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise;
+               else
+                       iwe.u.qual.qual = 0;
+               current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN);
+
+               /* Add encryption capability */
+               iwe.cmd = SIOCGIWENCODE;
+               if (capabilities & 0x10)
+                       iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+               else
+                       iwe.u.data.flags = IW_ENCODE_DISABLED;
+               iwe.u.data.length = 0;
+               current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, atom->a.essid);
+
+               /* Bit rate is not available in Lucent/Agere firmwares */
+               if (priv->firmware_type != FIRMWARE_TYPE_AGERE) {
+                       char *  current_val = current_ev + IW_EV_LCP_LEN;
+                       int     i;
+                       int     step;
+
+                       if (priv->firmware_type == FIRMWARE_TYPE_SYMBOL)
+                               step = 2;
+                       else
+                               step = 1;
+
+                       iwe.cmd = SIOCGIWRATE;
+                       /* Those two flags are ignored... */
+                       iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+                       /* Max 10 values */
+                       for (i = 0; i < 10; i += step) {
+                               /* NULL terminated */
+                               if (atom->p.rates[i] == 0x0)
+                                       break;
+                               /* Bit rate given in 500 kb/s units (+ 0x80) */
+                               iwe.u.bitrate.value = ((atom->p.rates[i] & 0x7f) * 500000);
+                               current_val = iwe_stream_add_value(current_ev, current_val,
+                                                                  end_buf, &iwe,
+                                                                  IW_EV_PARAM_LEN);
+                       }
+                       /* Check if we added any event */
+                       if ((current_val - current_ev) > IW_EV_LCP_LEN)
+                               current_ev = current_val;
+               }
+
+               /* The other data in the scan result are not really
+                * interesting, so for now drop it - Jean II */
+       }
+       return current_ev - buffer;
+}
+
+/* Return results of a scan */
+static int orinoco_ioctl_getscan(struct net_device *dev,
+                                struct iw_request_info *info,
+                                struct iw_point *srq,
+                                char *extra)
+{
+       struct orinoco_private *priv = netdev_priv(dev);
+       int err = 0;
+       unsigned long flags;
+
+       if (orinoco_lock(priv, &flags) != 0)
+               return -EBUSY;
+
+       /* If no results yet, ask to try again later */
+       if (priv->scan_result == NULL) {
+               if (priv->scan_inprogress)
+                       /* Important note : we don't want to block the caller
+                        * until results are ready for various reasons.
+                        * First, managing wait queues is complex and racy.
+                        * Second, we grab some rtnetlink lock before comming
+                        * here (in dev_ioctl()).
+                        * Third, we generate an Wireless Event, so the
+                        * caller can wait itself on that - Jean II */
+                       err = -EAGAIN;
+               else
+                       /* Client error, no scan results...
+                        * The caller need to restart the scan. */
+                       err = -ENODATA;
+       } else {
+               /* We have some results to push back to user space */
+
+               /* Translate to WE format */
+               srq->length = orinoco_translate_scan(dev, extra,
+                                                    priv->scan_result,
+                                                    priv->scan_len);
+
+               /* Return flags */
+               srq->flags = (__u16) priv->scan_mode;
+
+               /* Results are here, so scan no longer in progress */
+               priv->scan_inprogress = 0;
+
+               /* In any case, Scan results will be cleaned up in the
+                * reset function and when exiting the driver.
+                * The person triggering the scanning may never come to
+                * pick the results, so we need to do it in those places.
+                * Jean II */
+
+#ifdef SCAN_SINGLE_READ
+               /* If you enable this option, only one client (the first
+                * one) will be able to read the result (and only one
+                * time). If there is multiple concurent clients that
+                * want to read scan results, this behavior is not
+                * advisable - Jean II */
+               kfree(priv->scan_result);
+               priv->scan_result = NULL;
+#endif /* SCAN_SINGLE_READ */
+               /* Here, if too much time has elapsed since last scan,
+                * we may want to clean up scan results... - Jean II */
+       }
+         
+       orinoco_unlock(priv, &flags);
+       return err;
+}
+
 /* Commit handler, called after set operations */
 static int orinoco_ioctl_commit(struct net_device *dev,
                                struct iw_request_info *info,
@@ -4060,6 +4559,8 @@ static const iw_handler   orinoco_handler[] = {
        [SIOCGIWSPY   -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getspy,
        [SIOCSIWAP    -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setwap,
        [SIOCGIWAP    -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getwap,
+       [SIOCSIWSCAN  -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setscan,
+       [SIOCGIWSCAN  -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getscan,
        [SIOCSIWESSID -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setessid,
        [SIOCGIWESSID -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getessid,
        [SIOCSIWNICKN -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setnick,
index 329e79f..2a6b1c0 100644 (file)
@@ -32,6 +32,20 @@ struct orinoco_key {
        char data[ORINOCO_MAX_KEY_SIZE];
 } __attribute__ ((packed));
 
+struct header_struct {
+       /* 802.3 */
+       u8 dest[ETH_ALEN];
+       u8 src[ETH_ALEN];
+       u16 len;
+       /* 802.2 */
+       u8 dsap;
+       u8 ssap;
+       u8 ctrl;
+       /* SNAP */
+       u8 oui[3];
+       u16 ethertype;
+} __attribute__ ((packed));
+
 typedef enum {
        FIRMWARE_TYPE_AGERE,
        FIRMWARE_TYPE_INTERSIL,
@@ -51,6 +65,7 @@ struct orinoco_private {
        int open;
        u16 last_linkstatus;
        struct work_struct join_work;
+       struct work_struct wevent_work;
 
        /* Net device stuff */
        struct net_device *ndev;
@@ -77,6 +92,7 @@ struct orinoco_private {
        unsigned int has_pm:1;
        unsigned int has_preamble:1;
        unsigned int has_sensitivity:1;
+       unsigned int has_hostscan:1;
        unsigned int broken_disableport:1;
 
        /* Configuration paramaters */
@@ -103,6 +119,12 @@ struct orinoco_private {
        /* Configuration dependent variables */
        int port_type, createibss;
        int promiscuous, mc_count;
+
+       /* Scanning support */
+       int     scan_inprogress;        /* Scan pending... */
+       u32     scan_mode;              /* Type of scan done */
+       char *  scan_result;            /* Result of previous scan */
+       int     scan_len;               /* Lenght of result */
 };
 
 #ifdef ORINOCO_DEBUG