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