diff -u -p -r linux/drivers/net/wireless-w13/hermes.h linux/drivers/net/wireless/hermes.h --- linux/drivers/net/wireless-w13/hermes.h Wed Jan 23 17:10:54 2002 +++ linux/drivers/net/wireless/hermes.h Thu Jan 24 17:37:03 2002 @@ -233,23 +233,55 @@ struct hermes_tallies_frame { /* Grabbed from wlan-ng - Thanks Mark... - Jean II * This is the result of a scan inquiry command */ /* Structure describing info about an Access Point */ -struct hermes_scan_apinfo { +struct prism2_scan_apinfo { u16 channel; /* Channel where the AP sits */ u16 noise; /* Noise level */ u16 level; /* Signal level */ u8 bssid[ETH_ALEN]; /* MAC address of the Access Point */ u16 beacon_interv; /* Beacon interval ? */ u16 capabilities; /* Capabilities ? */ + u16 essid_len; /* ESSID length */ u8 essid[32]; /* ESSID of the network */ u8 rates[10]; /* Bit rate supported */ u16 proberesp_rate; /* ???? */ } __attribute__ ((packed)); /* Container */ -struct hermes_scan_frame { +struct prism2_scan_frame { u16 rsvd; /* ??? */ u16 scanreason; /* ??? */ - struct hermes_scan_apinfo aps[35]; /* Scan result */ + struct prism2_scan_apinfo aps[35]; /* Scan result */ } __attribute__ ((packed)); + +/* Same stuff for the Lucent/Agere card. + * Thanks to h1kari - Jean II */ +struct agere_scan_apinfo { + u16 channel; /* Channel where the AP sits */ + u16 noise; /* Noise level */ + u16 level; /* Signal level */ + u8 bssid[ETH_ALEN]; /* MAC address of the Access Point */ + u16 beacon_interv; /* Beacon interval ? */ + u16 capabilities; /* Capabilities ? */ + /* bits: 0-ess, 1-ibss, 4-privacy [wep] */ + u16 essid_len; /* ESSID length */ + u8 essid[32]; /* ESSID of the network */ +} __attribute__ ((packed)); +/* No container */ + +/* Hack to be able to read both type */ +union hermes_scan_info { + struct agere_scan_apinfo a; + struct prism2_scan_apinfo p; +}; + +/* Link status. Once again, grabbed from wlan-ng - Thanks Mark... - Jean II */ +#define HERMES_LINK_NOTCONNECTED (0) +#define HERMES_LINK_CONNECTED (1) +#define HERMES_LINK_DISCONNECTED (2) +#define HERMES_LINK_AP_CHANGE (3) +#define HERMES_LINK_AP_OUTOFRANGE (4) +#define HERMES_LINK_AP_INRANGE (5) +#define HERMES_LINK_ASSOCFAIL (6) + #ifdef __KERNEL__ diff -u -p -r linux/drivers/net/wireless-w13/orinoco.c linux/drivers/net/wireless/orinoco.c --- linux/drivers/net/wireless-w13/orinoco.c Wed Jan 23 17:55:17 2002 +++ linux/drivers/net/wireless/orinoco.c Fri Jan 25 15:00:00 2002 @@ -622,6 +622,14 @@ orinoco_reset(struct orinoco_private *pr orinoco_lock(priv); __orinoco_stop_irqs(priv); + /* Cleanup of driver struct */ + if(priv->scan_result != NULL) { + DEBUG(1, "%s: scan KFREE %p\n", dev->name, priv->scan_result); + kfree(priv->scan_result); + priv->scan_result = NULL; + } + priv->scan_inprogress = 0; + /* Check if we need a card reset */ if((priv->need_card_reset) && (priv->card_reset_handler != NULL)) priv->card_reset_handler(priv); @@ -1297,8 +1305,105 @@ static void __orinoco_ev_info(struct ori wstats->miss.beacon += le16_to_cpu(tallies.RxUnicastFrames); #endif #endif /* WIRELESS_EXT > 11 */ + break; + } + case HERMES_INQ_SCAN: { + /* Result of a scanning. Contains information about + * cells in the vicinity - Jean II */ +#if WIRELESS_EXT > 13 + int len = le16_to_cpu(info.len) - 1; + union iwreq_data wrqu; + unsigned char *buf; + + DEBUG(1, "%s: scan frame is %d words.\n", dev->name, len); + + /* Keep sanity (2048 bytes) */ + if(len > 1024) { + printk(KERN_WARNING "%s: Scan results too large (%d words).\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; + } + + /* Allocate buffer for results */ + buf = kmalloc(len * 2, GFP_ATOMIC); + if(buf == NULL) + /* No memory, so can't printk()... */ + break; + DEBUG(1, "%s: scan KMALLOC %p\n", dev->name, buf); + + /* Read directly the data (no seek) */ + hermes_read_words(hw, HERMES_DATA1, (void *) buf, len); + +#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 * 2; + 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); +#endif /* WIRELESS_EXT > 13 */ + break; + } + case HERMES_INQ_LINKSTATUS: { + /* Link status information frame. + * The link status has changed, and the card tell us about + * it. Most likely, the card has just connected to the cell + * or created an Ad-Hoc cell, or roamed... - Jean II */ +#if WIRELESS_EXT > 13 + uint16_t status; + union iwreq_data wrqu; + + /* Read directly the data (no seek) */ + hermes_read_words(hw, HERMES_DATA1, (void *) &status, 1); + printk(KERN_DEBUG "%s: Link status = %d.\n", + priv->ndev.name, status); + + /* 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 */ + + /* Do we have a valid AP address ? */ + if((status == HERMES_LINK_CONNECTED) || + (status == HERMES_LINK_AP_CHANGE) || + (status == HERMES_LINK_AP_INRANGE)) + err = hermes_read_ltv(hw, IRQ_BAP, + HERMES_RID_CURRENTBSSID, + ETH_ALEN, NULL, + wrqu.ap_addr.sa_data); + else + memset(wrqu.ap_addr.sa_data, '\0', ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + + /* Send event to user space */ + wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); +#endif /* WIRELESS_EXT > 13 */ + + break; } - break; default: DEBUG(1, "%s: Unknown information frame received (type %04x).\n", priv->ndev.name, le16_to_cpu(info.type)); @@ -1458,20 +1563,50 @@ static void __orinoco_ev_txexc(struct or struct net_device *dev = &priv->ndev; struct net_device_stats *stats = &priv->stats; u16 fid = hermes_read_regn(hw, TXCOMPLFID); - struct hermes_tx_descriptor desc; + struct orinoco_txframe_hdr hdr; int err = 0; if (fid == DUMMY_FID) return; /* Nothing's really happened */ - err = hermes_bap_pread(hw, USER_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 ieee802_11_hdr), + fid, 0); if (err) { printk(KERN_WARNING "%s: Unable to read descriptor on Tx error " "(FID=%04X error %d)\n", dev->name, fid, err); } else { + int status = le16_to_cpu(hdr.desc.status); + printk(KERN_INFO "%s: Tx error, status %d (FID=%04X)\n", - dev->name, le16_to_cpu(desc.status), fid); + dev->name, status, fid); + +#if WIRELESS_EXT > 13 + /* 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 */ + if(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.p80211.addr1, ETH_ALEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + + /* Send event to user space */ + wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL); + } +#endif /* WIRELESS_EXT > 13 */ } stats->tx_errors++; @@ -3281,6 +3416,245 @@ static int orinoco_ioctl_getspy(struct n return 0; } +#if WIRELESS_EXT > 13 +/* 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 = dev->priv; + hermes_t *hw = &priv->hw; + int err = 0; + + /* 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 */ + + orinoco_lock(priv); + + /* 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 */ + DEBUG(1, "%s: scan KFREE %p\n", dev->name, priv->scan_result); + 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 */ + + /* Simple scanning for now... + * We will do better later - Jean II */ + err = hermes_inquire(hw, HERMES_INQ_SCAN); + + /* One more client */ + priv->scan_inprogress = 1; + + orinoco_unlock(priv); + return 0; +} + +/* 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 = dev->priv; + int offset; /* In the scan data */ + union hermes_scan_info *atom; + int atom_len; + u16 capabilities; + struct iw_event iwe; /* Temporary buffer */ + char * current_ev = buffer; + char * end_buf = buffer + IW_SCAN_MAX_DATA; + + if(priv->firmware_type == FIRMWARE_TYPE_AGERE) { + atom_len = sizeof(struct agere_scan_apinfo); + offset = 0; + } else { + atom_len = sizeof(struct prism2_scan_apinfo); + //offset = sizeof(struct prism2_scan_frame); + offset = 4; + } + + DEBUG(4, "%s: scan_len = %d, atom_len = %d\n", dev->name, + scan_len, atom_len); + + /* 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); + + DEBUG(4, "%s: offset = %d, current_ev = %p, end_buf = %p, ap_addr = %02x:%02x:%02x:%02x:%02x:%02x\n", dev->name, offset, current_ev, end_buf, atom->a.bssid[0], atom->a.bssid[1], atom->a.bssid[2], atom->a.bssid[3], atom->a.bssid[4], atom->a.bssid[5]); + + /* 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_INFRA; + else + iwe.u.mode = IW_MODE_ADHOC; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_UINT_LEN); + } + + /* Add frequency */ + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = channel_frequency[le16_to_cpu(atom->a.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.level = (__u8) le16_to_cpu(atom->a.level) - 0x95; + iwe.u.qual.noise = (__u8) le16_to_cpu(atom->a.noise) - 0x95; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN); + + /* Add encryption capability */ + /* Note : it works on Lucent/Agere cards, need to check + * on PrismII cards - Jean */ + 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 only available with PrismII firmwares */ + if(priv->firmware_type != FIRMWARE_TYPE_AGERE) { + char * current_val = current_ev + IW_EV_LCP_LEN; + int i; + + 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++) { + /* 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 = dev->priv; + int err = 0; + + orinoco_lock(priv); + + /* 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 */ + DEBUG(1, "%s: scan KFREE %p\n", + dev->name, priv->scan_result); + 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); + return err; +} +#endif /* WIRELESS_EXT > 13 */ + /* Commit handler, called after a bunch of SET operation */ static int orinoco_ioctl_commit(struct net_device *dev, struct iw_request_info *info, /* NULL */ @@ -3359,8 +3733,13 @@ static const iw_handler orinoco_handler (iw_handler) orinoco_ioctl_getwap, /* SIOCGIWAP */ (iw_handler) NULL, /* -- hole -- */ (iw_handler) NULL, /* SIOCGIWAPLIST */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ +#if WIRELESS_EXT > 13 + (iw_handler) orinoco_ioctl_setscan, /* SIOCSIWSCAN */ + (iw_handler) orinoco_ioctl_getscan, /* SIOCGIWSCAN */ +#else /* WIRELESS_EXT > 13 */ + (iw_handler) NULL, /* SIOCSIWSCAN */ + (iw_handler) NULL, /* SIOCGIWSCAN */ +#endif /* WIRELESS_EXT > 13 */ (iw_handler) orinoco_ioctl_setessid, /* SIOCSIWESSID */ (iw_handler) orinoco_ioctl_getessid, /* SIOCGIWESSID */ (iw_handler) orinoco_ioctl_setnick, /* SIOCSIWNICKN */ @@ -4249,6 +4628,17 @@ orinoco_proc_dev_cleanup(struct orinoco_ struct net_device *dev = &priv->ndev; TRACE_ENTER(priv->ndev.name); + + /* This seems to be the only function called when the driver exits, + * so add my extra cleanup here - Jean II */ + orinoco_lock(priv); + if(priv->scan_result != NULL) { + DEBUG(1, "%s: scan KFREE %p\n", dev->name, priv->scan_result); + priv->scan_inprogress = 0; + kfree(priv->scan_result); + priv->scan_result = NULL; + } + orinoco_unlock(priv); if (priv->dir_regs) { remove_proc_entry("regs", priv->dir_dev); diff -u -p -r linux/drivers/net/wireless-w13/orinoco.h linux/drivers/net/wireless/orinoco.h --- linux/drivers/net/wireless-w13/orinoco.h Wed Jan 23 19:14:45 2002 +++ linux/drivers/net/wireless/orinoco.h Fri Jan 25 16:32:11 2002 @@ -85,6 +85,13 @@ struct orinoco_private { struct iw_quality spy_stat[IW_MAX_SPY]; #endif + /* 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 */ + /* More to come related to monitor mode */ + /* /proc based debugging stuff */ struct proc_dir_entry *dir_dev; struct proc_dir_entry *dir_regs;