diff -r -u orinoco-0.13a.orig/hermes.h orinoco-0.13a/hermes.h --- orinoco-0.13a.orig/hermes.h 2002-10-02 17:39:49.000000000 -0700 +++ orinoco-0.13a/hermes.h 2002-12-05 22:15:56.000000000 -0800 @@ -233,24 +233,56 @@ /* 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) + #define HERMES_LINKSTATUS_NOT_CONNECTED (0x0000) #define HERMES_LINKSTATUS_CONNECTED (0x0001) #define HERMES_LINKSTATUS_DISCONNECTED (0x0002) diff -r -u orinoco-0.13a.orig/orinoco.c orinoco-0.13a/orinoco.c --- orinoco-0.13a.orig/orinoco.c 2002-10-02 17:39:49.000000000 -0700 +++ orinoco-0.13a/orinoco.c 2002-12-13 21:39:10.000000000 -0800 @@ -378,14 +378,33 @@ #include #include #include +#if WIRELESS_EXT > 12 +#include +#endif /* WIRELESS_EXT > 12 */ #include "hermes.h" #include "hermes_rid.h" #include "orinoco.h" #include "ieee802_11.h" +#if WIRELESS_EXT <= 12 +/* Wireless extensions backward compatibility */ + +/* Part of iw_handler prototype we need */ +struct iw_request_info +{ + __u16 cmd; /* Wireless Extension command */ + __u16 flags; /* More to come ;-) */ +}; + +/* Private ioctl is migrating to a new range... */ +#ifndef SIOCIWFIRSTPRIV +#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE +#endif /* SIOCIWFIRSTPRIV */ +#endif /* WIRELESS_EXT <= 12 */ + /********************************************************************/ -/* Module information */ +/* Module information */ /********************************************************************/ MODULE_AUTHOR("David Gibson "); @@ -489,6 +508,43 @@ u16 ethertype; } __attribute__ ((packed)); +/* Used in Event handling + We avoid using nested strcutres 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; + 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}; @@ -515,8 +571,8 @@ static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, char buf[IW_ESSID_MAX_SIZE+1]); static long orinoco_hw_get_freq(struct orinoco_private *priv); -static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, int *numrates, - s32 *rates, int max); +static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, + int *numrates, s32 *rates, int max); static void __orinoco_set_multicast_list(struct net_device *dev); /* Interrupt handling routines */ @@ -529,7 +585,9 @@ static void __orinoco_ev_tx(struct orinoco_private *priv, hermes_t *hw); static void __orinoco_ev_alloc(struct orinoco_private *priv, hermes_t *hw); +#if 0 /* ioctl() routines */ +/* Either we fix those prototypes or we get rid of them - Jean II */ static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq); static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *erq); static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_point *erq); @@ -549,7 +607,7 @@ static int orinoco_ioctl_getpower(struct net_device *dev, struct iw_param *prq); static int orinoco_ioctl_setport3(struct net_device *dev, struct iwreq *wrq); static int orinoco_ioctl_getport3(struct net_device *dev, struct iwreq *wrq); - +#endif /* /proc debugging stuff */ static int orinoco_proc_init(void); static void orinoco_proc_cleanup(void); @@ -563,7 +621,7 @@ err = __orinoco_program_rids(priv); if (err) { printk(KERN_ERR "%s: Error %d configuring card\n", - dev->name, err); + dev->name, err); return err; } @@ -572,7 +630,7 @@ err = hermes_enable_port(hw, 0); if (err) { printk(KERN_ERR "%s: Error %d enabling MAC port\n", - dev->name, err); + dev->name, err); return err; } @@ -592,7 +650,7 @@ err = hermes_disable_port(hw, 0); if (err) { printk(KERN_ERR "%s: Error %d disabling MAC port\n", - dev->name, err); + dev->name, err); return err; } hermes_set_irqmask(hw, 0); @@ -615,8 +673,8 @@ if (err == -EIO) { /* Try workaround for old Symbol firmware bug */ printk(KERN_WARNING "%s: firmware ALLOC bug detected " - "(old Symbol firmware?). Trying to work around... ", - dev->name); + "(old Symbol firmware?). Trying to work around... ", + dev->name); priv->nicbuf_size = TX_NICBUF_SIZE_BUG; err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); @@ -639,7 +697,7 @@ if (err) return err; - priv->open = 1; + priv->open = 1; err = __orinoco_up(dev); @@ -676,7 +734,7 @@ /* Set the MAC address */ err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, - HERMES_BYTES_TO_RECLEN(ETH_ALEN), dev->dev_addr); + 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; @@ -723,15 +781,15 @@ 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); + 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); + 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; @@ -741,8 +799,8 @@ 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); + 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; @@ -754,7 +812,7 @@ priv->ap_density); if (err) { printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE. " - "Disabling sensitivity control\n", dev->name, err); + "Disabling sensitivity control\n", dev->name, err); priv->has_sensitivity = 0; } @@ -794,7 +852,7 @@ priv->pm_on); if (err) { printk(KERN_ERR "%s: Error %d setting up PM\n", - dev->name, err); + dev->name, err); return err; } @@ -803,7 +861,7 @@ priv->pm_mcast); if (err) { printk(KERN_ERR "%s: Error %d setting up PM\n", - dev->name, err); + dev->name, err); return err; } err = hermes_write_wordrec(hw, USER_BAP, @@ -811,7 +869,7 @@ priv->pm_period); if (err) { printk(KERN_ERR "%s: Error %d setting up PM\n", - dev->name, err); + dev->name, err); return err; } err = hermes_write_wordrec(hw, USER_BAP, @@ -819,7 +877,7 @@ priv->pm_timeout); if (err) { printk(KERN_ERR "%s: Error %d setting up PM\n", - dev->name, err); + dev->name, err); return err; } } @@ -831,7 +889,7 @@ priv->preamble); if (err) { printk(KERN_ERR "%s: Error %d setting preamble\n", - dev->name, err); + dev->name, err); return err; } } @@ -841,7 +899,7 @@ err = __orinoco_hw_setup_wep(priv); if (err) { printk(KERN_ERR "%s: Error %d activating WEP\n", - dev->name, err); + dev->name, err); return err; } } @@ -866,7 +924,7 @@ err = hermes_disable_port(hw, 0); if (err) { printk(KERN_ERR "%s: Unable to disable port in orinco_reconfigure()\n", - priv->ndev->name); + priv->ndev->name); goto out; } @@ -877,7 +935,7 @@ err = hermes_enable_port(hw, 0); if (err) { printk(KERN_ERR "%s: Unable to enable port in orinco_reconfigure()\n", - priv->ndev->name); + priv->ndev->name); goto out; } @@ -889,7 +947,7 @@ /* This must be called from user context, without locks held - use * schedule_task() */ -static void orinoco_reset(struct net_device *dev) +int orinoco_reset(struct net_device *dev) { struct orinoco_private *priv = dev->priv; int err; @@ -899,7 +957,7 @@ err = orinoco_lock(priv, &flags); if (err) - return; + return err; priv->hw_unavailable = 1; orinoco_unlock(priv, &flags); @@ -908,16 +966,16 @@ err = (*priv->hard_reset)(priv); if (err) { printk(KERN_ERR "%s: orinoco_reset: Error %d performing hard reset\n", - dev->name, err); + dev->name, err); /* FIXME: shutdown of some sort */ - return; + return err; } err = orinoco_reinit_firmware(dev); if (err) { printk(KERN_ERR "%s: orinoco_reset: Error %d re-initializing firmware\n", - dev->name, err); - return; + dev->name, err); + return err; } spin_lock_irqsave(&priv->lock, flags); @@ -927,13 +985,13 @@ err = __orinoco_up(dev); if (err) { printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n", - dev->name, err); + dev->name, err); } else dev->trans_start = jiffies; orinoco_unlock(priv, &flags); - return; + return err; } /********************************************************************/ @@ -959,7 +1017,7 @@ break; default: printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n", - priv->ndev->name); + priv->ndev->name); } } @@ -977,7 +1035,7 @@ if (orinoco_lock(priv, &flags) != 0) { printk(KERN_DEBUG "%s: orinoco_set_multicast_list() " - "called when hw_unavailable\n", dev->name); + "called when hw_unavailable\n", dev->name); return; } @@ -997,7 +1055,7 @@ if (priv->bitratemode >= BITRATE_TABLE_SIZE) { printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n", - priv->ndev->name, priv->bitratemode); + priv->ndev->name, priv->bitratemode); return -EINVAL; } @@ -1062,18 +1120,18 @@ /* Write all 4 keys */ for(i = 0; i < ORINOCO_MAX_KEYS; i++) { -/* int keylen = le16_to_cpu(priv->keys[i].len); */ + /* 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); + priv->ndev->name, i, keylen); return -E2BIG; } err = hermes_write_ltv(hw, USER_BAP, - HERMES_RID_CNFDEFAULTKEY0 + i, - HERMES_BYTES_TO_RECLEN(keylen), - priv->keys[i].data); + HERMES_RID_CNFDEFAULTKEY0 + i, + HERMES_BYTES_TO_RECLEN(keylen), + priv->keys[i].data); if (err) return err; } @@ -1116,7 +1174,7 @@ default: if (priv->wep_on) { printk(KERN_ERR "%s: WEP enabled, although not supported!\n", - priv->ndev->name); + priv->ndev->name); return -EINVAL; } } @@ -1124,7 +1182,9 @@ return 0; } -static int orinoco_hw_get_bssid(struct orinoco_private *priv, char buf[ETH_ALEN]) +/* This is called only once from orinoco_ioctl_getwap(). */ +static inline int orinoco_hw_get_bssid(struct orinoco_private *priv, + char buf[ETH_ALEN]) { hermes_t *hw = &priv->hw; int err = 0; @@ -1135,15 +1195,17 @@ return err; err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID, - ETH_ALEN, NULL, buf); + ETH_ALEN, NULL, buf); orinoco_unlock(priv, &flags); return err; } -static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, - char buf[IW_ESSID_MAX_SIZE+1]) +/* This is called only once from orinoco_ioctl_getessid(). */ +static inline int orinoco_hw_get_essid(struct orinoco_private *priv, + int *active, + char buf[IW_ESSID_MAX_SIZE+1]) { hermes_t *hw = &priv->hw; int err = 0; @@ -1172,14 +1234,14 @@ HERMES_RID_CNFDESIREDSSID; err = hermes_read_ltv(hw, USER_BAP, rid, sizeof(essidbuf), - NULL, &essidbuf); + NULL, &essidbuf); if (err) goto fail_unlock; } else { *active = 0; err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID, - sizeof(essidbuf), NULL, &essidbuf); + sizeof(essidbuf), NULL, &essidbuf); if (err) goto fail_unlock; } @@ -1193,10 +1255,11 @@ fail_unlock: orinoco_unlock(priv, &flags); - return err; + return err; } -static long orinoco_hw_get_freq(struct orinoco_private *priv) +/* This is called only once from orinoco_ioctl_getfreq(). */ +static inline long orinoco_hw_get_freq(struct orinoco_private *priv) { hermes_t *hw = &priv->hw; @@ -1237,8 +1300,10 @@ return err ? err : freq; } -static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, int *numrates, - s32 *rates, int max) +/* This is called only once from orinoco_ioctl_getiwrange(). */ +static inline int orinoco_hw_get_bitratelist(struct orinoco_private *priv, + int *numrates, + int32_t *rates, int max) { hermes_t *hw = &priv->hw; struct hermes_idstring list; @@ -1253,7 +1318,7 @@ return err; err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES, - sizeof(list), NULL, &list); + sizeof(list), NULL, &list); orinoco_unlock(priv, &flags); if (err) @@ -1287,39 +1352,39 @@ printk(KERN_DEBUG "IEEE 802.11 header:\n"); printk(KERN_DEBUG " frame_ctl = 0x%04x\n", - frame->p80211.frame_ctl); + frame->p80211.frame_ctl); printk(KERN_DEBUG " duration_id = 0x%04x\n", - frame->p80211.duration_id); + frame->p80211.duration_id); printk(KERN_DEBUG " addr1 = %02x:%02x:%02x:%02x:%02x:%02x\n", - frame->p80211.addr1[0], frame->p80211.addr1[1], - frame->p80211.addr1[2], frame->p80211.addr1[3], - frame->p80211.addr1[4], frame->p80211.addr1[5]); + frame->p80211.addr1[0], frame->p80211.addr1[1], + frame->p80211.addr1[2], frame->p80211.addr1[3], + frame->p80211.addr1[4], frame->p80211.addr1[5]); printk(KERN_DEBUG " addr2 = %02x:%02x:%02x:%02x:%02x:%02x\n", - frame->p80211.addr2[0], frame->p80211.addr2[1], - frame->p80211.addr2[2], frame->p80211.addr2[3], - frame->p80211.addr2[4], frame->p80211.addr2[5]); + frame->p80211.addr2[0], frame->p80211.addr2[1], + frame->p80211.addr2[2], frame->p80211.addr2[3], + frame->p80211.addr2[4], frame->p80211.addr2[5]); printk(KERN_DEBUG " addr3 = %02x:%02x:%02x:%02x:%02x:%02x\n", - frame->p80211.addr3[0], frame->p80211.addr3[1], - frame->p80211.addr3[2], frame->p80211.addr3[3], - frame->p80211.addr3[4], frame->p80211.addr3[5]); + frame->p80211.addr3[0], frame->p80211.addr3[1], + frame->p80211.addr3[2], frame->p80211.addr3[3], + frame->p80211.addr3[4], frame->p80211.addr3[5]); printk(KERN_DEBUG " seq_ctl = 0x%04x\n", - frame->p80211.seq_ctl); + frame->p80211.seq_ctl); printk(KERN_DEBUG " addr4 = %02x:%02x:%02x:%02x:%02x:%02x\n", - frame->p80211.addr4[0], frame->p80211.addr4[1], - frame->p80211.addr4[2], frame->p80211.addr4[3], - frame->p80211.addr4[4], frame->p80211.addr4[5]); + frame->p80211.addr4[0], frame->p80211.addr4[1], + frame->p80211.addr4[2], frame->p80211.addr4[3], + frame->p80211.addr4[4], frame->p80211.addr4[5]); printk(KERN_DEBUG " data_len = 0x%04x\n", - frame->p80211.data_len); + frame->p80211.data_len); printk(KERN_DEBUG "IEEE 802.3 header:\n"); printk(KERN_DEBUG " dest = %02x:%02x:%02x:%02x:%02x:%02x\n", - frame->p8023.h_dest[0], frame->p8023.h_dest[1], - frame->p8023.h_dest[2], frame->p8023.h_dest[3], - frame->p8023.h_dest[4], frame->p8023.h_dest[5]); + frame->p8023.h_dest[0], frame->p8023.h_dest[1], + frame->p8023.h_dest[2], frame->p8023.h_dest[3], + frame->p8023.h_dest[4], frame->p8023.h_dest[5]); printk(KERN_DEBUG " src = %02x:%02x:%02x:%02x:%02x:%02x\n", - frame->p8023.h_source[0], frame->p8023.h_source[1], - frame->p8023.h_source[2], frame->p8023.h_source[3], - frame->p8023.h_source[4], frame->p8023.h_source[5]); + frame->p8023.h_source[0], frame->p8023.h_source[1], + frame->p8023.h_source[2], frame->p8023.h_source[3], + frame->p8023.h_source[4], frame->p8023.h_source[5]); printk(KERN_DEBUG " len = 0x%04x\n", frame->p8023.h_proto); printk(KERN_DEBUG "IEEE 802.2 LLC/SNAP header:\n"); @@ -1327,7 +1392,7 @@ printk(KERN_DEBUG " SSAP = 0x%02x\n", frame->p8022.ssap); printk(KERN_DEBUG " ctrl = 0x%02x\n", frame->p8022.ctrl); printk(KERN_DEBUG " OUI = %02x:%02x:%02x\n", - frame->p8022.oui[0], frame->p8022.oui[1], frame->p8022.oui[2]); + frame->p8022.oui[0], frame->p8022.oui[1], frame->p8022.oui[2]); printk(KERN_DEBUG " ethertype = 0x%04x\n", frame->ethertype); } #endif @@ -1358,6 +1423,10 @@ evstat = hermes_read_regn(hw, EVSTAT); events = evstat & hw->inten; + /* if (! events) { */ + /* printk(KERN_WARNING "%s: Null event\n", dev->name); */ + /* } */ + if (jiffies != last_irq_jiffy) loops_this_jiffy = 0; last_irq_jiffy = jiffies; @@ -1366,7 +1435,7 @@ if (++loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY) { printk(KERN_CRIT "%s: IRQ handler is looping too \ much! Shutting down.\n", - dev->name); + dev->name); /* Perform an emergency shutdown */ hermes_set_irqmask(hw, 0); break; @@ -1414,7 +1483,7 @@ /* This seems to happen a fair bit under load, but ignoring it seems to work fine...*/ printk(KERN_DEBUG "%s: MAC controller error (WTERR). Ignoring.\n", - priv->ndev->name); + priv->ndev->name); } static void __orinoco_ev_infdrop(struct orinoco_private *priv, hermes_t *hw) @@ -1441,10 +1510,10 @@ /* Read the info frame header - don't try too hard */ err = hermes_bap_pread(hw, IRQ_BAP, &info, sizeof(info), - infofid, 0); + infofid, 0); if (err) { printk(KERN_ERR "%s: error %d reading info frame. " - "Frame dropped.\n", dev->name, err); + "Frame dropped.\n", dev->name, err); return; } @@ -1458,7 +1527,7 @@ if (len > sizeof(tallies)) { printk(KERN_WARNING "%s: Tallies frame too long (%d bytes)\n", - dev->name, len); + dev->name, len); len = sizeof(tallies); } @@ -1483,16 +1552,80 @@ le16_to_cpu(tallies.TxRetryLimitExceeded); /* wstats->miss.beacon - no match */ #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; } - 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 */ struct hermes_linkstatus linkstatus; u16 newstatus; const char *s; if (len != sizeof(linkstatus)) { printk(KERN_WARNING "%s: Unexpected size for linkstatus frame (%d bytes)\n", - dev->name, len); + dev->name, len); break; } @@ -1503,36 +1636,59 @@ switch (newstatus) { case HERMES_LINKSTATUS_NOT_CONNECTED: s = "Not Connected"; - break; - case HERMES_LINKSTATUS_CONNECTED: - s = "Connected"; - break; - case HERMES_LINKSTATUS_DISCONNECTED: - s = "Disconnected"; - break; - case HERMES_LINKSTATUS_AP_CHANGE: - s = "AP Changed"; - break; - case HERMES_LINKSTATUS_AP_OUT_OF_RANGE: - s = "AP Out of Range"; - break; - case HERMES_LINKSTATUS_AP_IN_RANGE: - s = "AP In Range"; - break; - case HERMES_LINKSTATUS_ASSOC_FAILED: - s = "Association Failed"; - break; + break; + case HERMES_LINKSTATUS_CONNECTED: + s = "Connected"; + break; + case HERMES_LINKSTATUS_DISCONNECTED: + s = "Disconnected"; + break; + case HERMES_LINKSTATUS_AP_CHANGE: + s = "AP Changed"; + break; + case HERMES_LINKSTATUS_AP_OUT_OF_RANGE: + s = "AP Out of Range"; + break; + case HERMES_LINKSTATUS_AP_IN_RANGE: + s = "AP In Range"; + break; + case HERMES_LINKSTATUS_ASSOC_FAILED: + s = "Association Failed"; + break; default: s = "UNKNOWN"; } + + printk(KERN_INFO "%s: New link status: %s (%04x)\n", + dev->name, s, newstatus); +#if WIRELESS_EXT > 13 + union iwreq_data wrqu; + + /* 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((newstatus == HERMES_LINKSTATUS_CONNECTED) || + (newstatus == HERMES_LINKSTATUS_AP_CHANGE) || + (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE)) + 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; - printk(KERN_INFO "%s: New link status: %s (%04x)\n", - dev->name, s, newstatus); + /* Send event to user space */ + wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); +#endif /* WIRELESS_EXT > 13 */ } break; default: printk(KERN_DEBUG "%s: Unknown information frame received (type %04x).\n", - dev->name, type); + dev->name, type); /* We don't actually do anything about it */ break; } @@ -1555,10 +1711,10 @@ rxfid = hermes_read_regn(hw, RXFID); err = hermes_bap_pread(hw, IRQ_BAP, &desc, sizeof(desc), - rxfid, 0); + rxfid, 0); if (err) { printk(KERN_ERR "%s: error %d reading Rx descriptor. " - "Frame dropped.\n", dev->name, err); + "Frame dropped.\n", dev->name, err); stats->rx_errors++; goto drop; } @@ -1569,7 +1725,7 @@ if (status & HERMES_RXSTAT_UNDECRYPTABLE) { wstats->discard.code++; DEBUG(1, "%s: Undecryptable frame on Rx. Frame dropped.\n", - dev->name); + dev->name); } else { stats->rx_crc_errors++; DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n", dev->name); @@ -1579,13 +1735,13 @@ } /* For now we ignore the 802.11 header completely, assuming - that the card's firmware has handled anything vital */ + that the card's firmware has handled anything vital */ err = hermes_bap_pread(hw, IRQ_BAP, &hdr, sizeof(hdr), - rxfid, HERMES_802_3_OFFSET); + rxfid, HERMES_802_3_OFFSET); if (err) { printk(KERN_ERR "%s: error %d reading frame header. " - "Frame dropped.\n", dev->name, err); + "Frame dropped.\n", dev->name, err); stats->rx_errors++; goto drop; } @@ -1602,7 +1758,7 @@ } if (length > IEEE802_11_DATA_LEN) { printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n", - dev->name, length); + dev->name, length); stats->rx_length_errors++; stats->rx_errors++; goto drop; @@ -1616,7 +1772,7 @@ skb = dev_alloc_skb(length+ETH_HLEN+2+1); if (!skb) { printk(KERN_WARNING "%s: Can't allocate skb for Rx\n", - dev->name); + dev->name); goto drop; } @@ -1660,10 +1816,10 @@ p = skb_put(skb, data_len); err = hermes_bap_pread(hw, IRQ_BAP, p, RUP_EVEN(data_len), - rxfid, data_off); + rxfid, data_off); if (err) { printk(KERN_ERR "%s: error %d reading frame. " - "Frame dropped.\n", dev->name, err); + "Frame dropped.\n", dev->name, err); stats->rx_errors++; goto drop; } @@ -1696,30 +1852,68 @@ 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; + /* Removed orinoco_txframe_hdr as it is no longer supported + (contains nested structures) -- Moustafa + */ + 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 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); + "(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)); + int status = le16_to_cpu(hdr.status); + + printk(KERN_INFO "%s: Tx error, status %d (FID=%04X)\n", + 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.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++; - + netif_wake_queue(dev); + hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); } static void __orinoco_ev_tx(struct orinoco_private *priv, hermes_t *hw) { + /* struct net_device *dev = priv->ndev; */ struct net_device_stats *stats = &priv->stats; + /* u16 fid = hermes_read_regn(hw, TXCOMPLFID); */ + + /* DEBUG(2, "%s: Transmit completed (FID=%04X)\n", priv->ndev->name, fid); */ stats->tx_packets++; @@ -1734,7 +1928,7 @@ if (fid != priv->txfid) { if (fid != DUMMY_FID) printk(KERN_WARNING "%s: Allocate event on unexpected fid (%04X)\n", - dev->name, fid); + dev->name, fid); return; } else { netif_wake_queue(dev); @@ -1755,7 +1949,7 @@ if (sta_id->variant == 1) return FIRMWARE_TYPE_AGERE; else if ((sta_id->variant == 2) && - ((firmver == 0x10001) || (firmver == 0x20001))) + ((firmver == 0x10001) || (firmver == 0x20001))) return FIRMWARE_TYPE_SYMBOL; else return FIRMWARE_TYPE_INTERSIL; @@ -1774,7 +1968,7 @@ 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", - dev->name, err); + dev->name, err); memset(&sta_id, 0, sizeof(sta_id)); } le16_to_cpus(&sta_id.id); @@ -1783,8 +1977,8 @@ 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); + 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); @@ -1805,8 +1999,8 @@ /* 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); + "version %d.%02d\n", dev->name, + sta_id.major, sta_id.minor); firmver = ((unsigned long)sta_id.major << 16) | sta_id.minor; @@ -1830,12 +2024,12 @@ 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); + 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); + "%s: Error %d reading Symbol firmware info. Wildly guessing capabilities...\n", + dev->name, err); firmver = 0; tmp[0] = '\0'; } else { @@ -1851,8 +2045,8 @@ } printk(KERN_DEBUG "%s: Looks like a Symbol firmware " - "version [%s] (parsing to %X)\n", dev->name, - tmp, firmver); + "version [%s] (parsing to %X)\n", dev->name, + tmp, firmver); priv->has_ibss = (firmver >= 0x20000); priv->has_wep = (firmver >= 0x15012); @@ -2142,7 +2336,7 @@ } static inline void orinoco_spy_gather(struct net_device *dev, u_char *mac, - int level, int noise) + int level, int noise) { struct orinoco_private *priv = (struct orinoco_private *)dev->priv; int i; @@ -2226,7 +2420,7 @@ eh = (struct ethhdr *)skb->data; memset(&desc, 0, sizeof(desc)); - desc.tx_control = cpu_to_le16(HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX); + 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", @@ -2408,152 +2602,259 @@ /* Wireless extensions support */ /********************************************************************/ -static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq) +/* Removed iwreq_data as they are not supported + before WIRELESS_EXT 13 -- Moustafa +*/ +static int orinoco_ioctl_getname(struct net_device *dev, + struct iw_request_info *info, + char *wrqu, + char *extra) +{ + DEBUG(1, "%s: SIOCGIWNAME\n", dev->name); + + strcpy(wrqu, "IEEE 802.11-DS"); + return 0; +} + +static int orinoco_ioctl_getwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, + char *extra) +{ + struct orinoco_private *priv = dev->priv; + + DEBUG(1, "%s: SIOCGIWAP\n", dev->name); + + ap_addr->sa_family = ARPHRD_ETHER; + return orinoco_hw_get_bssid(priv, ap_addr->sa_data); +} + +static int orinoco_ioctl_setmode(struct net_device *dev, + struct iw_request_info *info, + __u32 *mode, + char *extra) +{ + struct orinoco_private *priv = dev->priv; + int err = 0; + unsigned long flags; + + DEBUG(1, "%s: SIOCSIWMODE\n", dev->name); + + err = orinoco_lock(priv, &flags); + if (err) + return err; + err = -EINPROGRESS; /* Call commit handler */ + + switch (*mode) { + case IW_MODE_ADHOC: + if (! (priv->has_ibss || priv->has_port3) ) + err = -EINVAL; + else { + priv->iw_mode = IW_MODE_ADHOC; + } + break; + + case IW_MODE_INFRA: + priv->iw_mode = IW_MODE_INFRA; + break; + + default: + err = -EINVAL; + break; + } + set_port_type(priv); + orinoco_unlock(priv, &flags); + + return err; +} + +static int orinoco_ioctl_getmode(struct net_device *dev, + struct iw_request_info *info, + __u32 *mode, + char *extra) +{ + struct orinoco_private *priv = dev->priv; + unsigned long flags; + + DEBUG(1, "%s: SIOCGIWMODE\n", dev->name); + + /* No real need to lock here */ + orinoco_lock(priv, &flags); + *mode = priv->iw_mode; + orinoco_unlock(priv, &flags); + return 0; +} + +static int orinoco_ioctl_getiwrange(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *rrq, + char *extra) { struct orinoco_private *priv = dev->priv; int err = 0; int mode; - struct iw_range range; + struct iw_range *range = (struct iw_range *) extra; int numrates; int i, k; unsigned long flags; + DEBUG(1, "%s: SIOCGIWRANGE\n", dev->name); TRACE_ENTER(dev->name); - err = verify_area(VERIFY_WRITE, rrq->pointer, sizeof(range)); - if (err) - return err; - - rrq->length = sizeof(range); + rrq->length = sizeof(struct iw_range); err = orinoco_lock(priv, &flags); if (err) return err; mode = priv->iw_mode; - orinoco_unlock(priv, &flags); - memset(&range, 0, sizeof(range)); + memset(range, 0, sizeof(struct iw_range)); /* Much of this shamelessly taken from wvlan_cs.c. No idea * what it all means -dgibson */ #if WIRELESS_EXT > 10 - range.we_version_compiled = WIRELESS_EXT; - range.we_version_source = 11; + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 13; #endif /* WIRELESS_EXT > 10 */ - range.min_nwid = range.max_nwid = 0; /* We don't use nwids */ + // Already done in memset, don't redo it + range->min_nwid = range->max_nwid = 0; /* We don't use nwids */ /* Set available channels/frequencies */ - range.num_channels = NUM_CHANNELS; + range->num_channels = NUM_CHANNELS; k = 0; for (i = 0; i < NUM_CHANNELS; i++) { if (priv->channel_mask & (1 << i)) { - range.freq[k].i = i + 1; - range.freq[k].m = channel_frequency[i] * 100000; - range.freq[k].e = 1; + range->freq[k].i = i + 1; + range->freq[k].m = channel_frequency[i] * 100000; + range->freq[k].e = 1; k++; } if (k >= IW_MAX_FREQUENCIES) break; } - range.num_frequency = k; + range->num_frequency = k; - range.sensitivity = 3; + range->sensitivity = 3; + + /* Group all operation that need locking here - Jean II */ + /* Actually, as we just read a bunch of ints, we don't really + * need any locking (writing an int is atomic) - Jean II */ + mode = priv->iw_mode; + if (priv->has_wep) { + range->max_encoding_tokens = ORINOCO_MAX_KEYS; + + range->encoding_size[0] = SMALL_KEY_SIZE; + range->num_encoding_sizes = 1; + + if (priv->has_big_wep) { + range->encoding_size[1] = LARGE_KEY_SIZE; + range->num_encoding_sizes = 2; + } + } else { + // Already done in memset, don't redo it + range->num_encoding_sizes = 0; + range->max_encoding_tokens = 0; + } + orinoco_unlock(priv, &flags); if ((mode == IW_MODE_ADHOC) && (priv->spy_number == 0)){ /* Quality stats meaningless in ad-hoc mode */ - range.max_qual.qual = 0; - range.max_qual.level = 0; - range.max_qual.noise = 0; + range->max_qual.qual = 0; + range->max_qual.level = 0; + range->max_qual.noise = 0; + // Already done in memset, don't redo it #if WIRELESS_EXT > 11 - range.avg_qual.qual = 0; - range.avg_qual.level = 0; - range.avg_qual.noise = 0; + range->avg_qual.qual = 0; + range->avg_qual.level = 0; + range->avg_qual.noise = 0; #endif /* WIRELESS_EXT > 11 */ } else { - range.max_qual.qual = 0x8b - 0x2f; - range.max_qual.level = 0x2f - 0x95 - 1; - range.max_qual.noise = 0x2f - 0x95 - 1; + range->max_qual.qual = 0x8b - 0x2f; + range->max_qual.level = 0x2f - 0x95 - 1; + range->max_qual.noise = 0x2f - 0x95 - 1; #if WIRELESS_EXT > 11 /* Need to get better values */ - range.avg_qual.qual = 0x24; - range.avg_qual.level = 0xC2; - range.avg_qual.noise = 0x9E; + range->avg_qual.qual = 0x24; + range->avg_qual.level = 0xC2; + range->avg_qual.noise = 0x9E; #endif /* WIRELESS_EXT > 11 */ } err = orinoco_hw_get_bitratelist(priv, &numrates, - range.bitrate, IW_MAX_BITRATES); + range->bitrate, IW_MAX_BITRATES); if (err) return err; - range.num_bitrates = numrates; + range->num_bitrates = numrates; /* Set an indication of the max TCP throughput in bit/s that we can * expect using this interface. May be use for QoS stuff... * Jean II */ if(numrates > 2) - range.throughput = 5 * 1000 * 1000; /* ~5 Mb/s */ + range->throughput = 5 * 1000 * 1000; /* ~5 Mb/s */ else - range.throughput = 1.5 * 1000 * 1000; /* ~1.5 Mb/s */ - - range.min_rts = 0; - range.max_rts = 2347; - range.min_frag = 256; - range.max_frag = 2346; + range->throughput = 1.5 * 1000 * 1000; /* ~1.5 Mb/s */ err = orinoco_lock(priv, &flags); if (err) return err; if (priv->has_wep) { - range.max_encoding_tokens = ORINOCO_MAX_KEYS; + range->max_encoding_tokens = ORINOCO_MAX_KEYS; - range.encoding_size[0] = SMALL_KEY_SIZE; - range.num_encoding_sizes = 1; + range->encoding_size[0] = SMALL_KEY_SIZE; + range->num_encoding_sizes = 1; if (priv->has_big_wep) { - range.encoding_size[1] = LARGE_KEY_SIZE; - range.num_encoding_sizes = 2; + range->encoding_size[1] = LARGE_KEY_SIZE; + range->num_encoding_sizes = 2; } } else { - range.num_encoding_sizes = 0; - range.max_encoding_tokens = 0; + range->num_encoding_sizes = 0; + range->max_encoding_tokens = 0; } orinoco_unlock(priv, &flags); - - range.min_pmp = 0; - range.max_pmp = 65535000; - range.min_pmt = 0; - range.max_pmt = 65535 * 1000; /* ??? */ - range.pmp_flags = IW_POWER_PERIOD; - range.pmt_flags = IW_POWER_TIMEOUT; - range.pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_UNICAST_R; - - range.num_txpower = 1; - range.txpower[0] = 15; /* 15dBm */ - range.txpower_capa = IW_TXPOW_DBM; + + /* These 4 were in the patch, but not orig 11a or 13a. Maybe bogus. */ + range->min_rts = 0; + range->max_rts = 2347; + range->min_frag = 256; + range->max_frag = 2346; + + range->min_pmp = 0; + range->max_pmp = 65535000; + range->min_pmt = 0; + range->max_pmt = 65535 * 1000; /* ??? */ + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_TIMEOUT; + range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_UNICAST_R; + + range->num_txpower = 1; + range->txpower[0] = 15; /* 15dBm */ + range->txpower_capa = IW_TXPOW_DBM; #if WIRELESS_EXT > 10 - range.retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME; - range.retry_flags = IW_RETRY_LIMIT; - range.r_time_flags = IW_RETRY_LIFETIME; - range.min_retry = 0; - range.max_retry = 65535; /* ??? */ - range.min_r_time = 0; - range.max_r_time = 65535 * 1000; /* ??? */ + range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME; + range->retry_flags = IW_RETRY_LIMIT; + range->r_time_flags = IW_RETRY_LIFETIME; + range->min_retry = 0; + range->max_retry = 65535; /* ??? */ + range->min_r_time = 0; + range->max_r_time = 65535 * 1000; /* ??? */ #endif /* WIRELESS_EXT > 10 */ - if (copy_to_user(rrq->pointer, &range, sizeof(range))) - return -EFAULT; - TRACE_EXIT(dev->name); return 0; } -static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *erq) + +static int orinoco_ioctl_setiwencode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, + char *keybuf) { struct orinoco_private *priv = dev->priv; int index = (erq->flags & IW_ENCODE_INDEX) - 1; @@ -2562,30 +2863,25 @@ int restricted = priv->wep_restrict; u16 xlen = 0; int err = 0; - char keybuf[ORINOCO_MAX_KEY_SIZE]; unsigned long flags; - if (erq->pointer) { - /* We actually have a key to set */ - if ( (erq->length < SMALL_KEY_SIZE) || (erq->length > ORINOCO_MAX_KEY_SIZE) ) - return -EINVAL; - - if (copy_from_user(keybuf, erq->pointer, erq->length)) - return -EFAULT; + DEBUG(1, "%s: SIOCSIWENCODE\n", dev->name); + + if (! priv->has_wep) { + return -EOPNOTSUPP; } err = orinoco_lock(priv, &flags); if (err) return err; + err = -EINPROGRESS; /* Call commit handler */ - if (erq->pointer) { - if (erq->length > ORINOCO_MAX_KEY_SIZE) { - err = -E2BIG; - goto out; - } - + if (erq->length > 0) { + /* Check key size. Either it's small size, or it large (but + * only if the device support large keys) - Jean II */ if ( (erq->length > LARGE_KEY_SIZE) - || ( ! priv->has_big_wep && (erq->length > SMALL_KEY_SIZE)) ) { + || ( ! priv->has_big_wep && + (erq->length > SMALL_KEY_SIZE)) ) { err = -EINVAL; goto out; } @@ -2632,7 +2928,7 @@ if (erq->flags & IW_ENCODE_RESTRICTED) restricted = 1; - if (erq->pointer) { + if (erq->length > 0) { priv->keys[index].len = cpu_to_le16(xlen); memset(priv->keys[index].data, 0, sizeof(priv->keys[index].data)); memcpy(priv->keys[index].data, keybuf, erq->length); @@ -2648,15 +2944,21 @@ return err; } -static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_point *erq) +static int orinoco_ioctl_getiwencode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, + char *keybuf) { struct orinoco_private *priv = dev->priv; int index = (erq->flags & IW_ENCODE_INDEX) - 1; u16 xlen = 0; - char keybuf[ORINOCO_MAX_KEY_SIZE]; int err; unsigned long flags; + if (! priv->has_wep) { + return -EOPNOTSUPP; + } + err = orinoco_lock(priv, &flags); if (err) return err; @@ -2681,40 +2983,38 @@ 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) { - if (copy_to_user(erq->pointer, keybuf, xlen)) - return -EFAULT; - } - return 0; } -static int orinoco_ioctl_setessid(struct net_device *dev, struct iw_point *erq) +static int orinoco_ioctl_setessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, + char *essidbuf) { struct orinoco_private *priv = dev->priv; - char essidbuf[IW_ESSID_MAX_SIZE+1]; int err; unsigned long flags; /* Note : ESSID is ignored in Ad-Hoc demo mode, but we can set it * anyway... - Jean II */ - memset(&essidbuf, 0, sizeof(essidbuf)); - - if (erq->flags) { - if (erq->length > IW_ESSID_MAX_SIZE) - return -E2BIG; + /* Hum... Should not use Wireless Extension constant (may change), + * should use our own... - Jean II */ + if (erq->length > IW_ESSID_MAX_SIZE) + return -E2BIG; - if (copy_from_user(&essidbuf, erq->pointer, erq->length)) - return -EFAULT; + orinoco_lock(priv, &flags); + + /* NULL the string (for NULL termination & ESSID = ANY) - Jean II */ + memset(priv->desired_essid, 0, sizeof(priv->desired_essid)); - essidbuf[erq->length] = '\0'; + /* If not ANY, get the new ESSID */ + if (erq->flags) { + memcpy(priv->desired_essid, essidbuf, erq->length); } err = orinoco_lock(priv, &flags); @@ -2725,17 +3025,20 @@ orinoco_unlock(priv, &flags); - return 0; + return -EINPROGRESS; /* Call commit handler */ } -static int orinoco_ioctl_getessid(struct net_device *dev, struct iw_point *erq) +static int orinoco_ioctl_getessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, + char *essidbuf) { struct orinoco_private *priv = dev->priv; - char essidbuf[IW_ESSID_MAX_SIZE+1]; - int active; + int active; /* ??? */ int err = 0; unsigned long flags; + DEBUG(1, "%s: SIOCGIWESSID\n", dev->name); TRACE_ENTER(dev->name); if (netif_running(dev)) { @@ -2752,29 +3055,27 @@ erq->flags = 1; erq->length = strlen(essidbuf) + 1; - if (erq->pointer) - if ( copy_to_user(erq->pointer, essidbuf, erq->length) ) - return -EFAULT; TRACE_EXIT(dev->name); return 0; } -static int orinoco_ioctl_setnick(struct net_device *dev, struct iw_point *nrq) +static int orinoco_ioctl_setnick(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *nrq, + char *nickbuf) { struct orinoco_private *priv = dev->priv; - char nickbuf[IW_ESSID_MAX_SIZE+1]; int err; unsigned long flags; if (nrq->length > IW_ESSID_MAX_SIZE) return -E2BIG; - memset(nickbuf, 0, sizeof(nickbuf)); + orinoco_lock(priv, &flags); - if (copy_from_user(nickbuf, nrq->pointer, nrq->length)) - return -EFAULT; + memset(priv->nick, 0, sizeof(priv->nick)); nickbuf[nrq->length] = '\0'; @@ -2786,13 +3087,15 @@ orinoco_unlock(priv, &flags); - return 0; + return -EINPROGRESS; /* Call commit handler */ } -static int orinoco_ioctl_getnick(struct net_device *dev, struct iw_point *nrq) +static int orinoco_ioctl_getnick(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *nrq, + char *nickbuf) { struct orinoco_private *priv = dev->priv; - char nickbuf[IW_ESSID_MAX_SIZE+1]; int err; unsigned long flags; @@ -2803,21 +3106,23 @@ memcpy(nickbuf, priv->nick, IW_ESSID_MAX_SIZE+1); orinoco_unlock(priv, &flags); - nrq->length = strlen(nickbuf)+1; - - if (copy_to_user(nrq->pointer, nickbuf, sizeof(nickbuf))) - return -EFAULT; + nrq->length = strlen(nickbuf) + 1; return 0; } -static int orinoco_ioctl_setfreq(struct net_device *dev, struct iw_freq *frq) +static int orinoco_ioctl_setfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *frq, + char *extra) { struct orinoco_private *priv = dev->priv; int chan = -1; int err; unsigned long flags; + DEBUG(1, "%s: SIOCSIWFREQ\n", dev->name); + /* We can only use this in Ad-Hoc demo mode to set the operating * frequency, or in IBSS mode to set the frequency where the IBSS * will be created - Jean II */ @@ -2850,10 +3155,28 @@ priv->channel = chan; orinoco_unlock(priv, &flags); + return -EINPROGRESS; /* Call commit handler */ +} + +static int orinoco_ioctl_getfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *frq, + char *extra) +{ + struct orinoco_private *priv = dev->priv; + + DEBUG(1, "%s: SIOCGIWFREQ\n", dev->name); + + /* Locking done in there */ + frq->m = orinoco_hw_get_freq(priv); + frq->e = 1; return 0; } -static int orinoco_ioctl_getsens(struct net_device *dev, struct iw_param *srq) +static int orinoco_ioctl_getsens(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; @@ -2861,8 +3184,7 @@ int err; unsigned long flags; - if (!priv->has_sensitivity) - return -EOPNOTSUPP; + DEBUG(1, "%s: SIOCGIWSENS\n", dev->name); err = orinoco_lock(priv, &flags); if (err) @@ -2879,15 +3201,17 @@ return 0; } -static int orinoco_ioctl_setsens(struct net_device *dev, struct iw_param *srq) +static int orinoco_ioctl_setsens(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *srq, + char *extra) { struct orinoco_private *priv = dev->priv; int val = srq->value; int err; unsigned long flags; - if (!priv->has_sensitivity) - return -EOPNOTSUPP; + DEBUG(1, "%s: SIOCSIWSENS\n", dev->name); if ((val < 1) || (val > 3)) return -EINVAL; @@ -2898,16 +3222,21 @@ priv->ap_density = val; orinoco_unlock(priv, &flags); - return 0; + return -EINPROGRESS; /* Call commit handler */ } -static int orinoco_ioctl_setrts(struct net_device *dev, struct iw_param *rrq) +static int orinoco_ioctl_setrts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, + char *extra) { struct orinoco_private *priv = dev->priv; int val = rrq->value; int err; unsigned long flags; + DEBUG(1, "%s: SIOCSIWRTS\n", dev->name); + if (rrq->disabled) val = 2347; @@ -2921,10 +3250,28 @@ priv->rts_thresh = val; orinoco_unlock(priv, &flags); + return -EINPROGRESS; /* Call commit handler */ +} + +static int orinoco_ioctl_getrts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, + char *extra) +{ + struct orinoco_private *priv = dev->priv; + + DEBUG(1, "%s: SIOCGIWRTS\n", dev->name); + + rrq->value = priv->rts_thresh; + rrq->disabled = (rrq->value == 2347); + rrq->fixed = 1; return 0; } -static int orinoco_ioctl_setfrag(struct net_device *dev, struct iw_param *frq) +static int orinoco_ioctl_setfrag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *frq, + char *extra) { struct orinoco_private *priv = dev->priv; int err = 0; @@ -2933,6 +3280,7 @@ err = orinoco_lock(priv, &flags); if (err) return err; + err = -EINPROGRESS; /* Call commit handler */ if (priv->has_mwo) { if (frq->disabled) @@ -2947,10 +3295,12 @@ if (frq->disabled) priv->frag_thresh = 2346; else { - if ( (frq->value < 256) || (frq->value > 2346) ) + if ( (frq->value < 256) || + (frq->value > 2346) ) err = -EINVAL; else - priv->frag_thresh = frq->value & ~0x1; /* must be even */ + /* value must be even */ + priv->frag_thresh = frq->value & ~0x1; } } @@ -2959,7 +3309,10 @@ return err; } -static int orinoco_ioctl_getfrag(struct net_device *dev, struct iw_param *frq) +static int orinoco_ioctl_getfrag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *frq, + char *extra) { struct orinoco_private *priv = dev->priv; hermes_t *hw = &priv->hw; @@ -2997,7 +3350,10 @@ return err; } -static int orinoco_ioctl_setrate(struct net_device *dev, struct iw_param *rrq) +static int orinoco_ioctl_setrate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, + char *extra) { struct orinoco_private *priv = dev->priv; int err = 0; @@ -3006,6 +3362,8 @@ int i; unsigned long flags; + DEBUG(1, "%s: SIOCSIWRATE\n", dev->name); + /* As the user space doesn't know our highest rate, it uses -1 * to ask us to set the highest rate. Test it using "iwconfig * ethX rate auto" - Jean II */ @@ -3034,13 +3392,17 @@ err = orinoco_lock(priv, &flags); if (err) return err; + err = -EINPROGRESS; /* Call commit handler */ priv->bitratemode = ratemode; orinoco_unlock(priv, &flags); return err; } -static int orinoco_ioctl_getrate(struct net_device *dev, struct iw_param *rrq) +static int orinoco_ioctl_getrate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, + char *extra) { struct orinoco_private *priv = dev->priv; hermes_t *hw = &priv->hw; @@ -3082,7 +3444,7 @@ rrq->value = 5500000; else rrq->value = val * 1000000; - break; + break; case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */ case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */ for (i = 0; i < BITRATE_TABLE_SIZE; i++) @@ -3107,7 +3469,10 @@ return err; } -static int orinoco_ioctl_setpower(struct net_device *dev, struct iw_param *prq) +static int orinoco_ioctl_setpower(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *prq, + char *extra) { struct orinoco_private *priv = dev->priv; int err = 0; @@ -3116,6 +3481,7 @@ err = orinoco_lock(priv, &flags); if (err) return err; + err = -EINPROGRESS; /* Call commit handler */ if (prq->disabled) { priv->pm_on = 0; @@ -3160,7 +3526,10 @@ return err; } -static int orinoco_ioctl_getpower(struct net_device *dev, struct iw_param *prq) +static int orinoco_ioctl_getpower(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *prq, + char *extra) { struct orinoco_private *priv = dev->priv; hermes_t *hw = &priv->hw; @@ -3171,7 +3540,6 @@ err = orinoco_lock(priv, &flags); if (err) return err; - err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFPMENABLED, &enable); if (err) goto out; @@ -3209,8 +3577,26 @@ return err; } +static int orinoco_ioctl_gettxpower(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *trq, + char *extra) +{ + DEBUG(1, "%s: SIOCGIWTXPOW\n", dev->name); + + /* The card only supports one tx power, so this is easy */ + trq->value = 15; /* dBm */ + trq->fixed = 1; + trq->disabled = 0; + trq->flags = IW_TXPOW_DBM; + return 0; +} + #if WIRELESS_EXT > 10 -static int orinoco_ioctl_getretry(struct net_device *dev, struct iw_param *rrq) +static int orinoco_ioctl_getretry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, + char *extra) { struct orinoco_private *priv = dev->priv; hermes_t *hw = &priv->hw; @@ -3263,11 +3649,101 @@ } #endif /* WIRELESS_EXT > 10 */ -static int orinoco_ioctl_setibssport(struct net_device *dev, struct iwreq *wrq) +static int orinoco_ioctl_reset(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra) { struct orinoco_private *priv = dev->priv; - int val = *( (int *) wrq->u.name ); - int err; + + DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x%d (force_reset)\n", + dev->name, info->cmd - SIOCIWFIRSTPRIV); + + if (! capable(CAP_NET_ADMIN)) + return -EPERM; + + printk(KERN_DEBUG "%s: Forcing reset!\n", dev->name); + + /* COR reset as needed */ + /* card_reset_handler renamed to hard_reset -- Moustafa */ + if((info->cmd == (SIOCIWFIRSTPRIV + 0x1)) && + (priv->hard_reset != NULL)) + priv->hard_reset(priv); + + /* Firmware reset */ + orinoco_reset(dev); + + return 0; +} + +static int orinoco_ioctl_setpreamble(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra) +{ + struct orinoco_private *priv = dev->priv; + + DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x4 (set_preamble)\n", + dev->name); + + /* 802.11b has recently defined some short preamble. + * Basically, the Phy header has been reduced in size. + * This increase performance, especially at high rates + * (the preamble is transmitted at 1Mb/s), unfortunately + * this give compatibility troubles... - Jean II */ + if(priv->has_preamble) { + int err = 0; + unsigned long flags; + int val = *( (int *) extra ); + + err = orinoco_lock(priv, &flags); + if (err) + return err; + if(val) + priv->preamble = 1; + else + priv->preamble = 0; + orinoco_unlock(priv, &flags); + + return -EINPROGRESS; /* Call commit handler */ + } else + return -EOPNOTSUPP; +} + +static int orinoco_ioctl_getpreamble(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra) +{ + struct orinoco_private *priv = dev->priv; + + DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x5 (get_preamble)\n", + dev->name); + + if(priv->has_preamble) { + int err = 0; + unsigned long flags; + int *val = (int *) extra; + + err = orinoco_lock(priv, &flags); + if (err) + return err; + *val = priv->preamble; + orinoco_unlock(priv, &flags); + + return 0; + } else + return -EOPNOTSUPP; +} + +static int orinoco_ioctl_setibssport(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra) +{ + struct orinoco_private *priv = dev->priv; + int val = *( (int *) extra ); + int err = 0; unsigned long flags; err = orinoco_lock(priv, &flags); @@ -3280,13 +3756,16 @@ set_port_type(priv); orinoco_unlock(priv, &flags); - return 0; + return -EINPROGRESS; } -static int orinoco_ioctl_getibssport(struct net_device *dev, struct iwreq *wrq) +static int orinoco_ioctl_getibssport(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra) { struct orinoco_private *priv = dev->priv; - int *val = (int *)wrq->u.name; + int *val = (int *) extra; int err; unsigned long flags; @@ -3300,16 +3779,20 @@ return 0; } -static int orinoco_ioctl_setport3(struct net_device *dev, struct iwreq *wrq) +static int orinoco_ioctl_setport3(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra) { struct orinoco_private *priv = dev->priv; - int val = *( (int *) wrq->u.name ); + int val = *( (int *) extra ); int err = 0; unsigned long flags; err = orinoco_lock(priv, &flags); if (err) return err; + err = -EINPROGRESS; /* Call commit handler */ switch (val) { case 0: /* Try to do IEEE ad-hoc mode */ @@ -3342,10 +3825,13 @@ return err; } -static int orinoco_ioctl_getport3(struct net_device *dev, struct iwreq *wrq) +static int orinoco_ioctl_getport3(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra) { struct orinoco_private *priv = dev->priv; - int *val = (int *)wrq->u.name; + int *val = (int *) extra; int err; unsigned long flags; @@ -3361,25 +3847,19 @@ /* Spy is used for link quality/strength measurements in Ad-Hoc mode * Jean II */ -static int orinoco_ioctl_setspy(struct net_device *dev, struct iw_point *srq) +static int orinoco_ioctl_setspy(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *srq, + char *extra) { struct orinoco_private *priv = dev->priv; - struct sockaddr address[IW_MAX_SPY]; + struct sockaddr *address = (struct sockaddr *) extra; int number = srq->length; int i; - int err = 0; + int err = 0; /* Do NOT call commit handler */ unsigned long flags; - /* Check the number of addresses */ - if (number > IW_MAX_SPY) - return -E2BIG; - - /* Get the data in the driver */ - if (srq->pointer) { - if (copy_from_user(address, srq->pointer, - sizeof(struct sockaddr) * number)) - return -EFAULT; - } + DEBUG(1, "%s: SIOCSIWSPY\n", dev->name); /* Make sure nobody mess with the structure while we do */ err = orinoco_lock(priv, &flags); @@ -3408,11 +3888,13 @@ return err; } -static int orinoco_ioctl_getspy(struct net_device *dev, struct iw_point *srq) +static int orinoco_ioctl_getspy(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *srq, + char *extra) { struct orinoco_private *priv = dev->priv; - struct sockaddr address[IW_MAX_SPY]; - struct iw_quality spy_stat[IW_MAX_SPY]; + struct sockaddr *address = (struct sockaddr *) extra; int number; int i; int err; @@ -3423,41 +3905,410 @@ return err; number = priv->spy_number; - if ((number > 0) && (srq->pointer)) { - /* Create address struct */ - for (i = 0; i < number; i++) { - memcpy(address[i].sa_data, priv->spy_address[i], - ETH_ALEN); - address[i].sa_family = AF_UNIX; - } + /* Create address struct */ + for (i = 0; i < number; i++) { + memcpy(address[i].sa_data, priv->spy_address[i], + ETH_ALEN); + address[i].sa_family = AF_UNIX; + } + if (number > 0) { /* Copy stats */ /* In theory, we should disable irqs while copying the stats * because the rx path migh update it in the middle... * Bah, who care ? - Jean II */ - memcpy(&spy_stat, priv->spy_stat, - sizeof(struct iw_quality) * IW_MAX_SPY); - for (i=0; i < number; i++) - priv->spy_stat[i].updated = 0; + memcpy(extra + (sizeof(struct sockaddr) * number), + priv->spy_stat, sizeof(struct iw_quality) * number); } + /* Reset updated flags. */ + for (i=0; i < number; i++) + priv->spy_stat[i].updated = 0; orinoco_unlock(priv, &flags); - /* Push stuff to user space */ srq->length = number; - if(copy_to_user(srq->pointer, address, - sizeof(struct sockaddr) * number)) - return -EFAULT; - if(copy_to_user(srq->pointer + (sizeof(struct sockaddr)*number), - &spy_stat, sizeof(struct iw_quality) * number)) - return -EFAULT; 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; + 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 */ + + orinoco_lock(priv, &flags); + + /* 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, &flags); + 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; + unsigned long flags; + + err = orinoco_lock(priv, &flags); + if (err) + return err; + + /* 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, &flags); + 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 */ + void *wrqu, /* NULL */ + char *extra) /* NULL */ +{ + struct orinoco_private *priv = dev->priv; + int err = 0; + + DEBUG(1, "%s: SIOCSIWCOMMIT (set change commit)\n", dev->name); + + err = orinoco_reconfigure(priv); + if (err) { + /* Ouch ! What are we supposed to do ? */ + printk(KERN_ERR "orinoco_cs: Failed to set parameters on %s\n", + dev->name); + netif_device_detach(dev); + /* orinoco_shutdown(dev); seems to have vanished */ + } + + return err; +} + +static const struct iw_priv_args orinoco_privtab[] = { + { SIOCIWFIRSTPRIV + 0x0, 0, 0, "force_reset" }, + { SIOCIWFIRSTPRIV + 0x1, 0, 0, "card_reset" }, + { SIOCIWFIRSTPRIV + 0x2, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "set_port3" }, + { SIOCIWFIRSTPRIV + 0x3, 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_port3" }, + { SIOCIWFIRSTPRIV + 0x4, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "set_preamble" }, + { SIOCIWFIRSTPRIV + 0x5, 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_preamble" }, + { SIOCIWFIRSTPRIV + 0x6, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "set_ibssport" }, + { SIOCIWFIRSTPRIV + 0x7, 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_ibssport" } +}; + +#if WIRELESS_EXT > 12 + +/* + * Structures to export the Wireless Handlers + */ + +static const iw_handler orinoco_handler[] = +{ + (iw_handler) orinoco_ioctl_commit, /* SIOCSIWCOMMIT */ + (iw_handler) orinoco_ioctl_getname, /* SIOCGIWNAME */ + (iw_handler) NULL, /* SIOCSIWNWID */ + (iw_handler) NULL, /* SIOCGIWNWID */ + (iw_handler) orinoco_ioctl_setfreq, /* SIOCSIWFREQ */ + (iw_handler) orinoco_ioctl_getfreq, /* SIOCGIWFREQ */ + (iw_handler) orinoco_ioctl_setmode, /* SIOCSIWMODE */ + (iw_handler) orinoco_ioctl_getmode, /* SIOCGIWMODE */ + (iw_handler) orinoco_ioctl_setsens, /* SIOCSIWSENS */ + (iw_handler) orinoco_ioctl_getsens, /* SIOCGIWSENS */ + (iw_handler) NULL, /* SIOCSIWRANGE */ + (iw_handler) orinoco_ioctl_getiwrange, /* SIOCGIWRANGE */ + (iw_handler) NULL, /* SIOCSIWPRIV */ + (iw_handler) NULL, /* SIOCGIWPRIV */ + (iw_handler) NULL, /* SIOCSIWSTATS */ + (iw_handler) NULL, /* SIOCGIWSTATS */ + (iw_handler) orinoco_ioctl_setspy, /* SIOCSIWSPY */ + (iw_handler) orinoco_ioctl_getspy, /* SIOCGIWSPY */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* SIOCSIWAP */ + (iw_handler) orinoco_ioctl_getwap, /* SIOCGIWAP */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* SIOCGIWAPLIST */ +#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 */ + (iw_handler) orinoco_ioctl_getnick, /* SIOCGIWNICKN */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) orinoco_ioctl_setrate, /* SIOCSIWRATE */ + (iw_handler) orinoco_ioctl_getrate, /* SIOCGIWRATE */ + (iw_handler) orinoco_ioctl_setrts, /* SIOCSIWRTS */ + (iw_handler) orinoco_ioctl_getrts, /* SIOCGIWRTS */ + (iw_handler) orinoco_ioctl_setfrag, /* SIOCSIWFRAG */ + (iw_handler) orinoco_ioctl_getfrag, /* SIOCGIWFRAG */ + (iw_handler) NULL, /* SIOCSIWTXPOW */ + (iw_handler) orinoco_ioctl_gettxpower, /* SIOCGIWTXPOW */ + (iw_handler) NULL, /* SIOCSIWRETRY */ + (iw_handler) orinoco_ioctl_getretry, /* SIOCGIWRETRY */ + (iw_handler) orinoco_ioctl_setiwencode, /* SIOCSIWENCODE */ + (iw_handler) orinoco_ioctl_getiwencode, /* SIOCGIWENCODE */ + (iw_handler) orinoco_ioctl_setpower, /* SIOCSIWPOWER */ + (iw_handler) orinoco_ioctl_getpower, /* SIOCGIWPOWER */ +}; + +/* + Added typecasting since we no longer use iwreq_data -- Moustafa + */ +static const iw_handler orinoco_private_handler[] = +{ + (iw_handler) orinoco_ioctl_reset, /* SIOCIWFIRSTPRIV */ + (iw_handler) orinoco_ioctl_reset, /* SIOCIWFIRSTPRIV + 1 */ + (iw_handler) orinoco_ioctl_setport3, /* SIOCIWFIRSTPRIV + 2 */ + (iw_handler) orinoco_ioctl_getport3, /* SIOCIWFIRSTPRIV + 3 */ + (iw_handler) orinoco_ioctl_setpreamble, /* SIOCIWFIRSTPRIV + 4 */ + (iw_handler) orinoco_ioctl_getpreamble, /* SIOCIWFIRSTPRIV + 5 */ + (iw_handler) orinoco_ioctl_setibssport, /* SIOCIWFIRSTPRIV + 6 */ + (iw_handler) orinoco_ioctl_getibssport, /* SIOCIWFIRSTPRIV + 7 */ +}; + +static const struct iw_handler_def orinoco_handler_def = +{ + num_standard: sizeof(orinoco_handler)/sizeof(iw_handler), + num_private: sizeof(orinoco_private_handler)/sizeof(iw_handler), + num_private_args: sizeof(orinoco_privtab)/sizeof(struct iw_priv_args), + standard: (iw_handler *) orinoco_handler, + private: (iw_handler *) orinoco_private_handler, + private_args: (struct iw_priv_args *) orinoco_privtab, +}; + +#else /* WIRELESS_EXT > 12 */ static int orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { - struct orinoco_private *priv = dev->priv; struct iwreq *wrq = (struct iwreq *)rq; int err = 0; int tmp; @@ -3477,172 +4328,195 @@ switch (cmd) { case SIOCGIWNAME: - strcpy(wrq->u.name, "IEEE 802.11-DS"); + orinoco_ioctl_getname(dev, NULL, &(wrq->u), NULL); break; case SIOCGIWAP: - wrq->u.ap_addr.sa_family = ARPHRD_ETHER; - err = orinoco_hw_get_bssid(priv, wrq->u.ap_addr.sa_data); + err = orinoco_ioctl_getwap(dev, NULL, &(wrq->u.ap_addr), NULL); break; case SIOCGIWRANGE: - err = orinoco_ioctl_getiwrange(dev, &wrq->u.data); + { + struct iw_range range; + err = orinoco_ioctl_getiwrange(dev, NULL, + &(wrq->u.data), + (char *) &range); + if (copy_to_user(wrq->u.data.pointer, &range, + sizeof(struct iw_range))) + err = -EFAULT; + } break; case SIOCSIWMODE: - err = orinoco_lock(priv, &flags); - if (err) - return err; - switch (wrq->u.mode) { - case IW_MODE_ADHOC: - if (! (priv->has_ibss || priv->has_port3) ) - err = -EINVAL; - else { - priv->iw_mode = IW_MODE_ADHOC; - changed = 1; - } - break; - - case IW_MODE_INFRA: - priv->iw_mode = IW_MODE_INFRA; - changed = 1; - break; - - default: - err = -EINVAL; - break; - } - set_port_type(priv); - orinoco_unlock(priv, &flags); + err = orinoco_ioctl_setmode(dev, NULL, &(wrq->u.mode), NULL); break; case SIOCGIWMODE: - err = orinoco_lock(priv, &flags); - if (err) - return err; - wrq->u.mode = priv->iw_mode; - orinoco_unlock(priv, &flags); + orinoco_ioctl_getmode(dev, NULL, &(wrq->u.mode), NULL); break; case SIOCSIWENCODE: - if (! priv->has_wep) { - err = -EOPNOTSUPP; - break; + { + char keybuf[ORINOCO_MAX_KEY_SIZE]; + if (! priv->has_wep) { + err = -EOPNOTSUPP; + } else if (wrq->u.encoding.pointer) { + /* We actually have a key to set */ + if (wrq->u.encoding.length > ORINOCO_MAX_KEY_SIZE) { + err = -E2BIG; + break; + } + if (copy_from_user(keybuf, + wrq->u.encoding.pointer, + wrq->u.encoding.length)) { + err = -EFAULT; + break; + } + } else if (wrq->u.encoding.length != 0) { + err = -EINVAL; + break; + } + err = orinoco_ioctl_setiwencode(dev, NULL, &(wrq->u.encoding), keybuf); } - - err = orinoco_ioctl_setiwencode(dev, &wrq->u.encoding); - if (! err) - changed = 1; break; case SIOCGIWENCODE: + if (! capable(CAP_NET_ADMIN)) { + err = -EPERM; + break; + } if (! priv->has_wep) { err = -EOPNOTSUPP; break; } - - if (! capable(CAP_NET_ADMIN)) { - err = -EPERM; - break; + { + char keybuf[ORINOCO_MAX_KEY_SIZE]; + err = orinoco_ioctl_getiwencode(dev, NULL, + &(wrq->u.encoding), + keybuf); + if (wrq->u.encoding.pointer) { + if (copy_to_user(wrq->u.encoding.pointer, + keybuf, + wrq->u.encoding.length)) + err= -EFAULT; + } } - - err = orinoco_ioctl_getiwencode(dev, &wrq->u.encoding); break; case SIOCSIWESSID: - err = orinoco_ioctl_setessid(dev, &wrq->u.essid); - if (! err) - changed = 1; + { + char essidbuf[IW_ESSID_MAX_SIZE+1]; + if (wrq->u.essid.length > IW_ESSID_MAX_SIZE) { + err = -E2BIG; + break; + } + if (copy_from_user(essidbuf, wrq->u.essid.pointer, + wrq->u.essid.length)) { + err = -EFAULT; + break; + } + err = orinoco_ioctl_setessid(dev, NULL, + &(wrq->u.essid), + essidbuf); + } break; case SIOCGIWESSID: - err = orinoco_ioctl_getessid(dev, &wrq->u.essid); + { + char essidbuf[IW_ESSID_MAX_SIZE+1]; + err = orinoco_ioctl_getessid(dev, NULL, + &(wrq->u.essid), + essidbuf); + if (wrq->u.essid.pointer) + if ( copy_to_user(wrq->u.essid.pointer, + essidbuf, + wrq->u.essid.length) ) + err = -EFAULT; + } break; case SIOCSIWNICKN: - err = orinoco_ioctl_setnick(dev, &wrq->u.data); - if (! err) - changed = 1; + { + char nickbuf[IW_ESSID_MAX_SIZE+1]; + if (wrq->u.essid.length > IW_ESSID_MAX_SIZE) { + err = -E2BIG; + break; + } + if (copy_from_user(nickbuf, wrq->u.essid.pointer, + wrq->u.essid.length)) { + err = -EFAULT; + break; + } + err = orinoco_ioctl_setnick(dev, NULL, &(wrq->u.essid), + nickbuf); + } break; case SIOCGIWNICKN: - err = orinoco_ioctl_getnick(dev, &wrq->u.data); + { + char nickbuf[IW_ESSID_MAX_SIZE+1]; + err = orinoco_ioctl_getnick(dev, NULL, &(wrq->u.essid), + nickbuf); + if (wrq->u.essid.pointer) + if ( copy_to_user(wrq->u.essid.pointer, + nickbuf, + wrq->u.essid.length) ) + err = -EFAULT; + } break; case SIOCGIWFREQ: - tmp = orinoco_hw_get_freq(priv); - if (tmp < 0) { - err = tmp; - } else { - wrq->u.freq.m = tmp; - wrq->u.freq.e = 1; - } + orinoco_ioctl_getfreq(dev, NULL, &(wrq->u.freq), NULL); break; case SIOCSIWFREQ: - err = orinoco_ioctl_setfreq(dev, &wrq->u.freq); - if (! err) - changed = 1; + err = orinoco_ioctl_setfreq(dev, NULL, &(wrq->u.freq), NULL); break; case SIOCGIWSENS: - err = orinoco_ioctl_getsens(dev, &wrq->u.sens); + err = orinoco_ioctl_getsens(dev, NULL, &(wrq->u.sens), NULL); break; case SIOCSIWSENS: - err = orinoco_ioctl_setsens(dev, &wrq->u.sens); - if (! err) - changed = 1; + err = orinoco_ioctl_setsens(dev, NULL, &(wrq->u.sens), NULL); break; case SIOCGIWRTS: - wrq->u.rts.value = priv->rts_thresh; - wrq->u.rts.disabled = (wrq->u.rts.value == 2347); - wrq->u.rts.fixed = 1; + orinoco_ioctl_getrts(dev, NULL, &(wrq->u.rts), NULL); break; case SIOCSIWRTS: - err = orinoco_ioctl_setrts(dev, &wrq->u.rts); - if (! err) - changed = 1; + err = orinoco_ioctl_setrts(dev, NULL, &(wrq->u.rts), NULL); break; case SIOCSIWFRAG: - err = orinoco_ioctl_setfrag(dev, &wrq->u.frag); - if (! err) - changed = 1; + err = orinoco_ioctl_setfrag(dev, NULL, &(wrq->u.frag), NULL); break; case SIOCGIWFRAG: - err = orinoco_ioctl_getfrag(dev, &wrq->u.frag); + err = orinoco_ioctl_getfrag(dev, NULL, &(wrq->u.frag), NULL); break; case SIOCSIWRATE: - err = orinoco_ioctl_setrate(dev, &wrq->u.bitrate); - if (! err) - changed = 1; + err = orinoco_ioctl_setrate(dev, NULL, &(wrq->u.bitrate), + NULL); break; case SIOCGIWRATE: - err = orinoco_ioctl_getrate(dev, &wrq->u.bitrate); + err = orinoco_ioctl_getrate(dev, NULL, &(wrq->u.bitrate), + NULL); break; case SIOCSIWPOWER: - err = orinoco_ioctl_setpower(dev, &wrq->u.power); - if (! err) - changed = 1; + err = orinoco_ioctl_setpower(dev, NULL, &(wrq->u.power), NULL); break; case SIOCGIWPOWER: - err = orinoco_ioctl_getpower(dev, &wrq->u.power); + err = orinoco_ioctl_getpower(dev, NULL, &(wrq->u.power), NULL); break; case SIOCGIWTXPOW: - /* The card only supports one tx power, so this is easy */ - wrq->u.txpower.value = 15; /* dBm */ - wrq->u.txpower.fixed = 1; - wrq->u.txpower.disabled = 0; - wrq->u.txpower.flags = IW_TXPOW_DBM; + orinoco_ioctl_gettxpower(dev, NULL, &(wrq->u.txpower), NULL); break; #if WIRELESS_EXT > 10 @@ -3651,49 +4525,62 @@ break; case SIOCGIWRETRY: - err = orinoco_ioctl_getretry(dev, &wrq->u.retry); + err = orinoco_ioctl_getretry(dev, NULL, &(wrq->u.retry), NULL); break; #endif /* WIRELESS_EXT > 10 */ case SIOCSIWSPY: - err = orinoco_ioctl_setspy(dev, &wrq->u.data); + { + struct sockaddr address[IW_MAX_SPY]; + /* Check the number of addresses */ + if (wrq->u.data.length > IW_MAX_SPY) { + err = -E2BIG; + break; + } + /* Get the data in the driver */ + if (wrq->u.data.pointer) { + if (copy_from_user((char *) address, + wrq->u.data.pointer, + sizeof(struct sockaddr) * + wrq->u.data.length)) { + err = -EFAULT; + break; + } + } else if (wrq->u.data.length != 0) { + err = -EINVAL; + break; + } + err = orinoco_ioctl_setspy(dev, NULL, &(wrq->u.data), + (char *) address); + } break; case SIOCGIWSPY: - err = orinoco_ioctl_getspy(dev, &wrq->u.data); + { + char buffer[IW_MAX_SPY * (sizeof(struct sockaddr) + + sizeof(struct iw_quality))]; + err = orinoco_ioctl_getspy(dev, NULL, &(wrq->u.data), + buffer); + if (wrq->u.data.pointer) { + if (copy_to_user(wrq->u.data.pointer, + buffer, + (wrq->u.data.length * + (sizeof(struct sockaddr) + + sizeof(struct iw_quality))) + )) + err = -EFAULT; + } + } break; case SIOCGIWPRIV: if (wrq->u.data.pointer) { - struct iw_priv_args privtab[] = { - { SIOCIWFIRSTPRIV + 0x0, 0, 0, "force_reset" }, - { SIOCIWFIRSTPRIV + 0x1, 0, 0, "card_reset" }, - { SIOCIWFIRSTPRIV + 0x2, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - 0, "set_port3" }, - { SIOCIWFIRSTPRIV + 0x3, 0, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - "get_port3" }, - { SIOCIWFIRSTPRIV + 0x4, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - 0, "set_preamble" }, - { SIOCIWFIRSTPRIV + 0x5, 0, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - "get_preamble" }, - { SIOCIWFIRSTPRIV + 0x6, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - 0, "set_ibssport" }, - { SIOCIWFIRSTPRIV + 0x7, 0, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - "get_ibssport" } - }; - - err = verify_area(VERIFY_WRITE, wrq->u.data.pointer, sizeof(privtab)); + err = verify_area(VERIFY_WRITE, wrq->u.data.pointer, sizeof(orinoco_privtab)); if (err) break; - wrq->u.data.length = sizeof(privtab) / sizeof(privtab[0]); - if (copy_to_user(wrq->u.data.pointer, privtab, sizeof(privtab))) + wrq->u.data.length = sizeof(orinoco_privtab) / sizeof(orinoco_privtab[0]); + if (copy_to_user(wrq->u.data.pointer, orinoco_privtab, sizeof(orinoco_privtab))) err = -EFAULT; } break; @@ -3704,7 +4591,7 @@ err = -EPERM; break; } - + printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name); schedule_task(&priv->timeout_task); @@ -3716,13 +4603,13 @@ break; } - err = orinoco_ioctl_setport3(dev, wrq); - if (! err) - changed = 1; + err = orinoco_ioctl_setport3(dev, NULL, &(wrq->u), + (char *) &(wrq->u)); break; case SIOCIWFIRSTPRIV + 0x3: /* get_port3 */ - err = orinoco_ioctl_getport3(dev, wrq); + err = orinoco_ioctl_getport3(dev, NULL, &(wrq->u), + (char *) &(wrq->u)); break; case SIOCIWFIRSTPRIV + 0x4: /* set_preamble */ @@ -3731,38 +4618,13 @@ break; } - /* 802.11b has recently defined some short preamble. - * Basically, the Phy header has been reduced in size. - * This increase performance, especially at high rates - * (the preamble is transmitted at 1Mb/s), unfortunately - * this give compatibility troubles... - Jean II */ - if(priv->has_preamble) { - int val = *( (int *) wrq->u.name ); - - err = orinoco_lock(priv, &flags); - if (err) - return err; - if (val) - priv->preamble = 1; - else - priv->preamble = 0; - orinoco_unlock(priv, &flags); - changed = 1; - } else - err = -EOPNOTSUPP; + err = orinoco_ioctl_setpreamble(dev, NULL, &(wrq->u), + (char *) &(wrq->u)); break; case SIOCIWFIRSTPRIV + 0x5: /* get_preamble */ - if(priv->has_preamble) { - int *val = (int *)wrq->u.name; - - err = orinoco_lock(priv, &flags); - if (err) - return err; - *val = priv->preamble; - orinoco_unlock(priv, &flags); - } else - err = -EOPNOTSUPP; + err = orinoco_ioctl_getpreamble(dev, NULL, &(wrq->u), + (char *) &(wrq->u)); break; case SIOCIWFIRSTPRIV + 0x6: /* set_ibssport */ if (! capable(CAP_NET_ADMIN)) { @@ -3770,13 +4632,13 @@ break; } - err = orinoco_ioctl_setibssport(dev, wrq); - if (! err) - changed = 1; + err = orinoco_ioctl_setibssport(dev, NULL, &(wrq->u), + (char *) &(wrq->u)); break; case SIOCIWFIRSTPRIV + 0x7: /* get_ibssport */ - err = orinoco_ioctl_getibssport(dev, wrq); + err = orinoco_ioctl_getibssport(dev, NULL, &(wrq->u), + (char *) &(wrq->u)); break; @@ -3784,14 +4646,18 @@ err = -EOPNOTSUPP; } - if (! err && changed && netif_running(dev)) { - err = orinoco_reconfigure(priv); - } + if (err == -EINPROGRESS) { + if(netif_running(dev)) + err = orinoco_ioctl_commit(dev, rq, cmd, NULL); + else + err = 0; /* Success */ + } TRACE_EXIT(dev->name); return err; } +#endif /* WIRELESS_EXT > 12 */ /********************************************************************/ /* procfs stuff */ @@ -3952,7 +4818,7 @@ PROC_REC(CHANNELLIST,WORDS), PROC_REC(REGULATORYDOMAINS,WORDS), PROC_REC(TEMPTYPE,WORDS), -/* PROC_REC(CIS,BYTES), */ + /* PROC_REC(CIS,BYTES), */ PROC_REC(STAID,WORDS), PROC_REC(CURRENTSSID,STRING), PROC_REC(CURRENTBSSID,BYTES), @@ -3983,7 +4849,7 @@ static int orinoco_proc_get_hermes_recs(char *page, char **start, off_t requested_offset, - int requested_len, int *eof, void *data) + int requested_len, int *eof, void *data) { struct orinoco_private *priv = (struct orinoco_private *)data; struct net_device *dev = priv->ndev; @@ -4095,7 +4961,7 @@ static int orinoco_proc_get_hermes_prof(char *page, char **start, off_t requested_offset, - int requested_len, int *eof, void *data) + int requested_len, int *eof, void *data) { struct orinoco_private *priv = (struct orinoco_private *)data; hermes_t *hw = &priv->hw; @@ -4155,7 +5021,7 @@ } e = create_proc_read_entry("recs", S_IFREG | S_IRUGO, - priv->dir_dev, orinoco_proc_get_hermes_recs, priv); + priv->dir_dev, orinoco_proc_get_hermes_recs, priv); if (! e) { printk(KERN_ERR "Unable to initialize /proc/hermes/%s/recs\n", dev->name); goto fail; @@ -4163,14 +5029,14 @@ #ifdef HERMES_DEBUG_BUFFER e = create_proc_read_entry("buf", S_IFREG | S_IRUGO, - priv->dir_dev, orinoco_proc_get_hermes_buf, priv); + priv->dir_dev, orinoco_proc_get_hermes_buf, priv); if (! e) { printk(KERN_ERR "Unable to intialize /proc/hermes/%s/buf\n", dev->name); goto fail; } e = create_proc_read_entry("prof", S_IFREG | S_IRUGO, - priv->dir_dev, orinoco_proc_get_hermes_prof, priv); + priv->dir_dev, orinoco_proc_get_hermes_prof, priv); if (! e) { printk(KERN_ERR "Unable to intialize /proc/hermes/%s/prof\n", dev->name); goto fail; @@ -4187,7 +5053,22 @@ orinoco_proc_dev_cleanup(struct net_device *dev) { struct orinoco_private *priv = dev->priv; + int err = 0; + unsigned long flags; + /* This seems to be the only function called when the driver exits, + * so add my extra cleanup here - Jean II + * On lock error, if you clean up someone might dereference NULL, but + * if you don't, you have a memory leak. FIXME. */ + err = orinoco_lock(priv, &flags); + if (!err && 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, &flags); + if (priv->dir_dev) { remove_proc_entry("prof", priv->dir_dev); remove_proc_entry("buf", priv->dir_dev); @@ -4226,7 +5107,13 @@ dev->watchdog_timeo = HZ; /* 1 second timeout */ dev->get_stats = orinoco_get_stats; dev->get_wireless_stats = orinoco_get_wireless_stats; + +#if WIRELESS_EXT > 12 + dev->wireless_handlers = (struct iw_handler_def *)&orinoco_handler_def; +#else /* WIRELESS_EXT > 12 */ dev->do_ioctl = orinoco_ioctl; +#endif /* WIRELESS_EXT > 12 */ + dev->change_mtu = orinoco_change_mtu; dev->set_multicast_list = orinoco_set_multicast_list; @@ -4277,3 +5164,5 @@ module_init(init_orinoco); module_exit(exit_orinoco); + + diff -r -u orinoco-0.13a.orig/orinoco.h orinoco-0.13a/orinoco.h --- orinoco-0.13a.orig/orinoco.h 2002-10-02 17:39:49.000000000 -0700 +++ orinoco-0.13a/orinoco.h 2002-12-13 20:14:10.000000000 -0800 @@ -92,6 +92,13 @@ 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 */ + /* More to come related to monitor mode */ + /* /proc based debugging stuff */ struct proc_dir_entry *dir_dev; }; @@ -112,6 +119,13 @@ extern int __orinoco_down(struct net_device *dev); int orinoco_reinit_firmware(struct net_device *dev); +#if WIRELESS_EXT <= 12 +extern int orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +#endif /* WIRELESS_EXT <= 12 */ + +/* utility routines */ +/* extern void orinoco_shutdown(struct net_device *dev); seems to be gone */ +extern int orinoco_reset(struct net_device *dev); extern int orinoco_proc_dev_init(struct net_device *dev); extern void orinoco_proc_dev_cleanup(struct net_device *dev); extern void orinoco_interrupt(int irq, void * dev_id, struct pt_regs *regs);