diff options
author | Christoph Hellwig <hch@lst.de> | 2005-06-18 19:27:56 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@pobox.com> | 2005-06-27 00:23:55 -0400 |
commit | 95dd91fbd8d3c788ef93bc94b4b600889e04dba1 (patch) | |
tree | f9d1e9bb31856bc49d5853187b32c80f6ee79076 /drivers/net/wireless/orinoco.c | |
parent | 16739b065f4b0965d975f5c756204c7aa911cd61 (diff) |
[PATCH] orinoco: scanning support
Patch from Pavel Roskin
Diffstat (limited to 'drivers/net/wireless/orinoco.c')
-rw-r--r-- | drivers/net/wireless/orinoco.c | 543 |
1 files changed, 522 insertions, 21 deletions
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index c057b7f9a02a..38fd8623a444 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c | |||
@@ -514,6 +514,10 @@ MODULE_PARM_DESC(ignore_disconnect, "Don't report lost link to the network layer | |||
514 | /* Internal constants */ | 514 | /* Internal constants */ |
515 | /********************************************************************/ | 515 | /********************************************************************/ |
516 | 516 | ||
517 | /* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */ | ||
518 | static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; | ||
519 | #define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2) | ||
520 | |||
517 | #define ORINOCO_MIN_MTU 256 | 521 | #define ORINOCO_MIN_MTU 256 |
518 | #define ORINOCO_MAX_MTU (IEEE802_11_DATA_LEN - ENCAPS_OVERHEAD) | 522 | #define ORINOCO_MAX_MTU (IEEE802_11_DATA_LEN - ENCAPS_OVERHEAD) |
519 | 523 | ||
@@ -579,25 +583,42 @@ static struct { | |||
579 | /* Data types */ | 583 | /* Data types */ |
580 | /********************************************************************/ | 584 | /********************************************************************/ |
581 | 585 | ||
582 | struct header_struct { | 586 | /* Used in Event handling. |
583 | /* 802.3 */ | 587 | * We avoid nested structres as they break on ARM -- Moustafa */ |
584 | u8 dest[ETH_ALEN]; | 588 | struct hermes_tx_descriptor_802_11 { |
585 | u8 src[ETH_ALEN]; | 589 | /* hermes_tx_descriptor */ |
586 | u16 len; | 590 | u16 status; |
587 | /* 802.2 */ | 591 | u16 reserved1; |
592 | u16 reserved2; | ||
593 | u32 sw_support; | ||
594 | u8 retry_count; | ||
595 | u8 tx_rate; | ||
596 | u16 tx_control; | ||
597 | |||
598 | /* ieee802_11_hdr */ | ||
599 | u16 frame_ctl; | ||
600 | u16 duration_id; | ||
601 | u8 addr1[ETH_ALEN]; | ||
602 | u8 addr2[ETH_ALEN]; | ||
603 | u8 addr3[ETH_ALEN]; | ||
604 | u16 seq_ctl; | ||
605 | u8 addr4[ETH_ALEN]; | ||
606 | u16 data_len; | ||
607 | |||
608 | /* ethhdr */ | ||
609 | unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ | ||
610 | unsigned char h_source[ETH_ALEN]; /* source ether addr */ | ||
611 | unsigned short h_proto; /* packet type ID field */ | ||
612 | |||
613 | /* p8022_hdr */ | ||
588 | u8 dsap; | 614 | u8 dsap; |
589 | u8 ssap; | 615 | u8 ssap; |
590 | u8 ctrl; | 616 | u8 ctrl; |
591 | /* SNAP */ | ||
592 | u8 oui[3]; | 617 | u8 oui[3]; |
618 | |||
593 | u16 ethertype; | 619 | u16 ethertype; |
594 | } __attribute__ ((packed)); | 620 | } __attribute__ ((packed)); |
595 | 621 | ||
596 | /* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */ | ||
597 | u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; | ||
598 | |||
599 | #define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2) | ||
600 | |||
601 | struct hermes_rx_descriptor { | 622 | struct hermes_rx_descriptor { |
602 | u16 status; | 623 | u16 status; |
603 | u32 time; | 624 | u32 time; |
@@ -958,26 +979,55 @@ static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw) | |||
958 | struct orinoco_private *priv = netdev_priv(dev); | 979 | struct orinoco_private *priv = netdev_priv(dev); |
959 | struct net_device_stats *stats = &priv->stats; | 980 | struct net_device_stats *stats = &priv->stats; |
960 | u16 fid = hermes_read_regn(hw, TXCOMPLFID); | 981 | u16 fid = hermes_read_regn(hw, TXCOMPLFID); |
961 | struct hermes_tx_descriptor desc; | 982 | struct hermes_tx_descriptor_802_11 hdr; |
962 | int err = 0; | 983 | int err = 0; |
963 | 984 | ||
964 | if (fid == DUMMY_FID) | 985 | if (fid == DUMMY_FID) |
965 | return; /* Nothing's really happened */ | 986 | return; /* Nothing's really happened */ |
966 | 987 | ||
967 | err = hermes_bap_pread(hw, IRQ_BAP, &desc, sizeof(desc), fid, 0); | 988 | /* Read the frame header */ |
989 | err = hermes_bap_pread(hw, IRQ_BAP, &hdr, | ||
990 | sizeof(struct hermes_tx_descriptor) + | ||
991 | sizeof(struct ieee80211_hdr), | ||
992 | fid, 0); | ||
993 | |||
994 | hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); | ||
995 | stats->tx_errors++; | ||
996 | |||
968 | if (err) { | 997 | if (err) { |
969 | printk(KERN_WARNING "%s: Unable to read descriptor on Tx error " | 998 | printk(KERN_WARNING "%s: Unable to read descriptor on Tx error " |
970 | "(FID=%04X error %d)\n", | 999 | "(FID=%04X error %d)\n", |
971 | dev->name, fid, err); | 1000 | dev->name, fid, err); |
972 | } else { | 1001 | return; |
973 | DEBUG(1, "%s: Tx error, status %d\n", | ||
974 | dev->name, le16_to_cpu(desc.status)); | ||
975 | } | 1002 | } |
976 | 1003 | ||
977 | stats->tx_errors++; | 1004 | DEBUG(1, "%s: Tx error, err %d (FID=%04X)\n", dev->name, |
1005 | err, fid); | ||
1006 | |||
1007 | /* We produce a TXDROP event only for retry or lifetime | ||
1008 | * exceeded, because that's the only status that really mean | ||
1009 | * that this particular node went away. | ||
1010 | * Other errors means that *we* screwed up. - Jean II */ | ||
1011 | hdr.status = le16_to_cpu(hdr.status); | ||
1012 | if (hdr.status & (HERMES_TXSTAT_RETRYERR | HERMES_TXSTAT_AGEDERR)) { | ||
1013 | union iwreq_data wrqu; | ||
1014 | |||
1015 | /* Copy 802.11 dest address. | ||
1016 | * We use the 802.11 header because the frame may | ||
1017 | * not be 802.3 or may be mangled... | ||
1018 | * In Ad-Hoc mode, it will be the node address. | ||
1019 | * In managed mode, it will be most likely the AP addr | ||
1020 | * User space will figure out how to convert it to | ||
1021 | * whatever it needs (IP address or else). | ||
1022 | * - Jean II */ | ||
1023 | memcpy(wrqu.addr.sa_data, hdr.addr1, ETH_ALEN); | ||
1024 | wrqu.addr.sa_family = ARPHRD_ETHER; | ||
1025 | |||
1026 | /* Send event to user space */ | ||
1027 | wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL); | ||
1028 | } | ||
978 | 1029 | ||
979 | netif_wake_queue(dev); | 1030 | netif_wake_queue(dev); |
980 | hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); | ||
981 | } | 1031 | } |
982 | 1032 | ||
983 | static void orinoco_tx_timeout(struct net_device *dev) | 1033 | static void orinoco_tx_timeout(struct net_device *dev) |
@@ -1316,6 +1366,30 @@ static void orinoco_join_ap(struct net_device *dev) | |||
1316 | orinoco_unlock(priv, &flags); | 1366 | orinoco_unlock(priv, &flags); |
1317 | } | 1367 | } |
1318 | 1368 | ||
1369 | /* Send new BSSID to userspace */ | ||
1370 | static void orinoco_send_wevents(struct net_device *dev) | ||
1371 | { | ||
1372 | struct orinoco_private *priv = netdev_priv(dev); | ||
1373 | struct hermes *hw = &priv->hw; | ||
1374 | union iwreq_data wrqu; | ||
1375 | int err; | ||
1376 | unsigned long flags; | ||
1377 | |||
1378 | if (orinoco_lock(priv, &flags) != 0) | ||
1379 | return; | ||
1380 | |||
1381 | err = hermes_read_ltv(hw, IRQ_BAP, HERMES_RID_CURRENTBSSID, | ||
1382 | ETH_ALEN, NULL, wrqu.ap_addr.sa_data); | ||
1383 | if (err != 0) | ||
1384 | return; | ||
1385 | |||
1386 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | ||
1387 | |||
1388 | /* Send event to user space */ | ||
1389 | wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); | ||
1390 | orinoco_unlock(priv, &flags); | ||
1391 | } | ||
1392 | |||
1319 | static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) | 1393 | static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) |
1320 | { | 1394 | { |
1321 | struct orinoco_private *priv = netdev_priv(dev); | 1395 | struct orinoco_private *priv = netdev_priv(dev); |
@@ -1395,6 +1469,15 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) | |||
1395 | break; | 1469 | break; |
1396 | newstatus = le16_to_cpu(linkstatus.linkstatus); | 1470 | newstatus = le16_to_cpu(linkstatus.linkstatus); |
1397 | 1471 | ||
1472 | /* Symbol firmware uses "out of range" to signal that | ||
1473 | * the hostscan frame can be requested. */ | ||
1474 | if (newstatus == HERMES_LINKSTATUS_AP_OUT_OF_RANGE && | ||
1475 | priv->firmware_type == FIRMWARE_TYPE_SYMBOL && | ||
1476 | priv->has_hostscan && priv->scan_inprogress) { | ||
1477 | hermes_inquire(hw, HERMES_INQ_HOSTSCAN_SYMBOL); | ||
1478 | break; | ||
1479 | } | ||
1480 | |||
1398 | connected = (newstatus == HERMES_LINKSTATUS_CONNECTED) | 1481 | connected = (newstatus == HERMES_LINKSTATUS_CONNECTED) |
1399 | || (newstatus == HERMES_LINKSTATUS_AP_CHANGE) | 1482 | || (newstatus == HERMES_LINKSTATUS_AP_CHANGE) |
1400 | || (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE); | 1483 | || (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE); |
@@ -1404,12 +1487,89 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) | |||
1404 | else if (!ignore_disconnect) | 1487 | else if (!ignore_disconnect) |
1405 | netif_carrier_off(dev); | 1488 | netif_carrier_off(dev); |
1406 | 1489 | ||
1407 | if (newstatus != priv->last_linkstatus) | 1490 | if (newstatus != priv->last_linkstatus) { |
1491 | priv->last_linkstatus = newstatus; | ||
1408 | print_linkstatus(dev, newstatus); | 1492 | print_linkstatus(dev, newstatus); |
1493 | /* The info frame contains only one word which is the | ||
1494 | * status (see hermes.h). The status is pretty boring | ||
1495 | * in itself, that's why we export the new BSSID... | ||
1496 | * Jean II */ | ||
1497 | schedule_work(&priv->wevent_work); | ||
1498 | } | ||
1499 | } | ||
1500 | break; | ||
1501 | case HERMES_INQ_SCAN: | ||
1502 | if (!priv->scan_inprogress && priv->bssid_fixed && | ||
1503 | priv->firmware_type == FIRMWARE_TYPE_INTERSIL) { | ||
1504 | schedule_work(&priv->join_work); | ||
1505 | break; | ||
1506 | } | ||
1507 | /* fall through */ | ||
1508 | case HERMES_INQ_HOSTSCAN: | ||
1509 | case HERMES_INQ_HOSTSCAN_SYMBOL: { | ||
1510 | /* Result of a scanning. Contains information about | ||
1511 | * cells in the vicinity - Jean II */ | ||
1512 | union iwreq_data wrqu; | ||
1513 | unsigned char *buf; | ||
1514 | |||
1515 | /* Sanity check */ | ||
1516 | if (len > 4096) { | ||
1517 | printk(KERN_WARNING "%s: Scan results too large (%d bytes)\n", | ||
1518 | dev->name, len); | ||
1519 | break; | ||
1520 | } | ||
1521 | |||
1522 | /* We are a strict producer. If the previous scan results | ||
1523 | * have not been consumed, we just have to drop this | ||
1524 | * frame. We can't remove the previous results ourselves, | ||
1525 | * that would be *very* racy... Jean II */ | ||
1526 | if (priv->scan_result != NULL) { | ||
1527 | printk(KERN_WARNING "%s: Previous scan results not consumed, dropping info frame.\n", dev->name); | ||
1528 | break; | ||
1529 | } | ||
1409 | 1530 | ||
1410 | priv->last_linkstatus = newstatus; | 1531 | /* Allocate buffer for results */ |
1532 | buf = kmalloc(len, GFP_ATOMIC); | ||
1533 | if (buf == NULL) | ||
1534 | /* No memory, so can't printk()... */ | ||
1535 | break; | ||
1536 | |||
1537 | /* Read scan data */ | ||
1538 | err = hermes_bap_pread(hw, IRQ_BAP, (void *) buf, len, | ||
1539 | infofid, sizeof(info)); | ||
1540 | if (err) | ||
1541 | break; | ||
1542 | |||
1543 | #ifdef ORINOCO_DEBUG | ||
1544 | { | ||
1545 | int i; | ||
1546 | printk(KERN_DEBUG "Scan result [%02X", buf[0]); | ||
1547 | for(i = 1; i < (len * 2); i++) | ||
1548 | printk(":%02X", buf[i]); | ||
1549 | printk("]\n"); | ||
1550 | } | ||
1551 | #endif /* ORINOCO_DEBUG */ | ||
1552 | |||
1553 | /* Allow the clients to access the results */ | ||
1554 | priv->scan_len = len; | ||
1555 | priv->scan_result = buf; | ||
1556 | |||
1557 | /* Send an empty event to user space. | ||
1558 | * We don't send the received data on the event because | ||
1559 | * it would require us to do complex transcoding, and | ||
1560 | * we want to minimise the work done in the irq handler | ||
1561 | * Use a request to extract the data - Jean II */ | ||
1562 | wrqu.data.length = 0; | ||
1563 | wrqu.data.flags = 0; | ||
1564 | wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); | ||
1411 | } | 1565 | } |
1412 | break; | 1566 | break; |
1567 | case HERMES_INQ_SEC_STAT_AGERE: | ||
1568 | /* Security status (Agere specific) */ | ||
1569 | /* Ignore this frame for now */ | ||
1570 | if (priv->firmware_type == FIRMWARE_TYPE_AGERE) | ||
1571 | break; | ||
1572 | /* fall through */ | ||
1413 | default: | 1573 | default: |
1414 | printk(KERN_DEBUG "%s: Unknown information frame received: " | 1574 | printk(KERN_DEBUG "%s: Unknown information frame received: " |
1415 | "type 0x%04x, length %d\n", dev->name, type, len); | 1575 | "type 0x%04x, length %d\n", dev->name, type, len); |
@@ -2010,6 +2170,11 @@ static void orinoco_reset(struct net_device *dev) | |||
2010 | 2170 | ||
2011 | orinoco_unlock(priv, &flags); | 2171 | orinoco_unlock(priv, &flags); |
2012 | 2172 | ||
2173 | /* Scanning support: Cleanup of driver struct */ | ||
2174 | kfree(priv->scan_result); | ||
2175 | priv->scan_result = NULL; | ||
2176 | priv->scan_inprogress = 0; | ||
2177 | |||
2013 | if (priv->hard_reset) { | 2178 | if (priv->hard_reset) { |
2014 | err = (*priv->hard_reset)(priv); | 2179 | err = (*priv->hard_reset)(priv); |
2015 | if (err) { | 2180 | if (err) { |
@@ -2248,6 +2413,7 @@ static int determine_firmware(struct net_device *dev) | |||
2248 | priv->has_mwo = (firmver >= 0x60000); | 2413 | priv->has_mwo = (firmver >= 0x60000); |
2249 | priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */ | 2414 | priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */ |
2250 | priv->ibss_port = 1; | 2415 | priv->ibss_port = 1; |
2416 | priv->has_hostscan = (firmver >= 0x8000a); | ||
2251 | 2417 | ||
2252 | /* Tested with Agere firmware : | 2418 | /* Tested with Agere firmware : |
2253 | * 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II | 2419 | * 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) | |||
2293 | priv->ibss_port = 4; | 2459 | priv->ibss_port = 4; |
2294 | priv->broken_disableport = (firmver == 0x25013) || | 2460 | priv->broken_disableport = (firmver == 0x25013) || |
2295 | (firmver >= 0x30000 && firmver <= 0x31000); | 2461 | (firmver >= 0x30000 && firmver <= 0x31000); |
2462 | priv->has_hostscan = (firmver >= 0x31001) || | ||
2463 | (firmver >= 0x29057 && firmver < 0x30000); | ||
2296 | /* Tested with Intel firmware : 0x20015 => Jean II */ | 2464 | /* Tested with Intel firmware : 0x20015 => Jean II */ |
2297 | /* Tested with 3Com firmware : 0x15012 & 0x22001 => Jean II */ | 2465 | /* Tested with 3Com firmware : 0x15012 & 0x22001 => Jean II */ |
2298 | break; | 2466 | break; |
@@ -2312,6 +2480,7 @@ static int determine_firmware(struct net_device *dev) | |||
2312 | priv->has_ibss = (firmver >= 0x000700); /* FIXME */ | 2480 | priv->has_ibss = (firmver >= 0x000700); /* FIXME */ |
2313 | priv->has_big_wep = priv->has_wep = (firmver >= 0x000800); | 2481 | priv->has_big_wep = priv->has_wep = (firmver >= 0x000800); |
2314 | priv->has_pm = (firmver >= 0x000700); | 2482 | priv->has_pm = (firmver >= 0x000700); |
2483 | priv->has_hostscan = (firmver >= 0x010301); | ||
2315 | 2484 | ||
2316 | if (firmver >= 0x000800) | 2485 | if (firmver >= 0x000800) |
2317 | priv->ibss_port = 0; | 2486 | priv->ibss_port = 0; |
@@ -2539,6 +2708,7 @@ struct net_device *alloc_orinocodev(int sizeof_card, | |||
2539 | * hardware */ | 2708 | * hardware */ |
2540 | INIT_WORK(&priv->reset_work, (void (*)(void *))orinoco_reset, dev); | 2709 | INIT_WORK(&priv->reset_work, (void (*)(void *))orinoco_reset, dev); |
2541 | INIT_WORK(&priv->join_work, (void (*)(void *))orinoco_join_ap, dev); | 2710 | INIT_WORK(&priv->join_work, (void (*)(void *))orinoco_join_ap, dev); |
2711 | INIT_WORK(&priv->wevent_work, (void (*)(void *))orinoco_send_wevents, dev); | ||
2542 | 2712 | ||
2543 | netif_carrier_off(dev); | 2713 | netif_carrier_off(dev); |
2544 | priv->last_linkstatus = 0xffff; | 2714 | priv->last_linkstatus = 0xffff; |
@@ -2549,6 +2719,9 @@ struct net_device *alloc_orinocodev(int sizeof_card, | |||
2549 | 2719 | ||
2550 | void free_orinocodev(struct net_device *dev) | 2720 | void free_orinocodev(struct net_device *dev) |
2551 | { | 2721 | { |
2722 | struct orinoco_private *priv = netdev_priv(dev); | ||
2723 | |||
2724 | kfree(priv->scan_result); | ||
2552 | free_netdev(dev); | 2725 | free_netdev(dev); |
2553 | } | 2726 | } |
2554 | 2727 | ||
@@ -3967,6 +4140,332 @@ static int orinoco_ioctl_getspy(struct net_device *dev, | |||
3967 | return 0; | 4140 | return 0; |
3968 | } | 4141 | } |
3969 | 4142 | ||
4143 | /* Trigger a scan (look for other cells in the vicinity */ | ||
4144 | static int orinoco_ioctl_setscan(struct net_device *dev, | ||
4145 | struct iw_request_info *info, | ||
4146 | struct iw_param *srq, | ||
4147 | char *extra) | ||
4148 | { | ||
4149 | struct orinoco_private *priv = netdev_priv(dev); | ||
4150 | hermes_t *hw = &priv->hw; | ||
4151 | int err = 0; | ||
4152 | unsigned long flags; | ||
4153 | |||
4154 | /* Note : you may have realised that, as this is a SET operation, | ||
4155 | * this is priviledged and therefore a normal user can't | ||
4156 | * perform scanning. | ||
4157 | * This is not an error, while the device perform scanning, | ||
4158 | * traffic doesn't flow, so it's a perfect DoS... | ||
4159 | * Jean II */ | ||
4160 | |||
4161 | if (orinoco_lock(priv, &flags) != 0) | ||
4162 | return -EBUSY; | ||
4163 | |||
4164 | /* Scanning with port 0 disabled would fail */ | ||
4165 | if (!netif_running(dev)) { | ||
4166 | err = -ENETDOWN; | ||
4167 | goto out; | ||
4168 | } | ||
4169 | |||
4170 | /* In monitor mode, the scan results are always empty. | ||
4171 | * Probe responses are passed to the driver as received | ||
4172 | * frames and could be processed in software. */ | ||
4173 | if (priv->iw_mode == IW_MODE_MONITOR) { | ||
4174 | err = -EOPNOTSUPP; | ||
4175 | goto out; | ||
4176 | } | ||
4177 | |||
4178 | /* Note : because we don't lock out the irq handler, the way | ||
4179 | * we access scan variables in priv is critical. | ||
4180 | * o scan_inprogress : not touched by irq handler | ||
4181 | * o scan_mode : not touched by irq handler | ||
4182 | * o scan_result : irq is strict producer, non-irq is strict | ||
4183 | * consumer. | ||
4184 | * o scan_len : synchronised with scan_result | ||
4185 | * Before modifying anything on those variables, please think hard ! | ||
4186 | * Jean II */ | ||
4187 | |||
4188 | /* If there is still some left-over scan results, get rid of it */ | ||
4189 | if (priv->scan_result != NULL) { | ||
4190 | /* What's likely is that a client did crash or was killed | ||
4191 | * between triggering the scan request and reading the | ||
4192 | * results, so we need to reset everything. | ||
4193 | * Some clients that are too slow may suffer from that... | ||
4194 | * Jean II */ | ||
4195 | kfree(priv->scan_result); | ||
4196 | priv->scan_result = NULL; | ||
4197 | } | ||
4198 | |||
4199 | /* Save flags */ | ||
4200 | priv->scan_mode = srq->flags; | ||
4201 | |||
4202 | /* Always trigger scanning, even if it's in progress. | ||
4203 | * This way, if the info frame get lost, we will recover somewhat | ||
4204 | * gracefully - Jean II */ | ||
4205 | |||
4206 | if (priv->has_hostscan) { | ||
4207 | switch (priv->firmware_type) { | ||
4208 | case FIRMWARE_TYPE_SYMBOL: | ||
4209 | err = hermes_write_wordrec(hw, USER_BAP, | ||
4210 | HERMES_RID_CNFHOSTSCAN_SYMBOL, | ||
4211 | HERMES_HOSTSCAN_SYMBOL_ONCE | | ||
4212 | HERMES_HOSTSCAN_SYMBOL_BCAST); | ||
4213 | break; | ||
4214 | case FIRMWARE_TYPE_INTERSIL: { | ||
4215 | u16 req[3]; | ||
4216 | |||
4217 | req[0] = cpu_to_le16(0x3fff); /* All channels */ | ||
4218 | req[1] = cpu_to_le16(0x0001); /* rate 1 Mbps */ | ||
4219 | req[2] = 0; /* Any ESSID */ | ||
4220 | err = HERMES_WRITE_RECORD(hw, USER_BAP, | ||
4221 | HERMES_RID_CNFHOSTSCAN, &req); | ||
4222 | } | ||
4223 | break; | ||
4224 | case FIRMWARE_TYPE_AGERE: | ||
4225 | err = hermes_write_wordrec(hw, USER_BAP, | ||
4226 | HERMES_RID_CNFSCANSSID_AGERE, | ||
4227 | 0); /* Any ESSID */ | ||
4228 | if (err) | ||
4229 | break; | ||
4230 | |||
4231 | err = hermes_inquire(hw, HERMES_INQ_SCAN); | ||
4232 | break; | ||
4233 | } | ||
4234 | } else | ||
4235 | err = hermes_inquire(hw, HERMES_INQ_SCAN); | ||
4236 | |||
4237 | /* One more client */ | ||
4238 | if (! err) | ||
4239 | priv->scan_inprogress = 1; | ||
4240 | |||
4241 | out: | ||
4242 | orinoco_unlock(priv, &flags); | ||
4243 | return err; | ||
4244 | } | ||
4245 | |||
4246 | /* Translate scan data returned from the card to a card independant | ||
4247 | * format that the Wireless Tools will understand - Jean II */ | ||
4248 | static inline int orinoco_translate_scan(struct net_device *dev, | ||
4249 | char *buffer, | ||
4250 | char *scan, | ||
4251 | int scan_len) | ||
4252 | { | ||
4253 | struct orinoco_private *priv = netdev_priv(dev); | ||
4254 | int offset; /* In the scan data */ | ||
4255 | union hermes_scan_info *atom; | ||
4256 | int atom_len; | ||
4257 | u16 capabilities; | ||
4258 | u16 channel; | ||
4259 | struct iw_event iwe; /* Temporary buffer */ | ||
4260 | char * current_ev = buffer; | ||
4261 | char * end_buf = buffer + IW_SCAN_MAX_DATA; | ||
4262 | |||
4263 | switch (priv->firmware_type) { | ||
4264 | case FIRMWARE_TYPE_AGERE: | ||
4265 | atom_len = sizeof(struct agere_scan_apinfo); | ||
4266 | offset = 0; | ||
4267 | break; | ||
4268 | case FIRMWARE_TYPE_SYMBOL: | ||
4269 | /* Lack of documentation necessitates this hack. | ||
4270 | * Different firmwares have 68 or 76 byte long atoms. | ||
4271 | * We try modulo first. If the length divides by both, | ||
4272 | * we check what would be the channel in the second | ||
4273 | * frame for a 68-byte atom. 76-byte atoms have 0 there. | ||
4274 | * Valid channel cannot be 0. */ | ||
4275 | if (scan_len % 76) | ||
4276 | atom_len = 68; | ||
4277 | else if (scan_len % 68) | ||
4278 | atom_len = 76; | ||
4279 | else if (scan_len >= 1292 && scan[68] == 0) | ||
4280 | atom_len = 76; | ||
4281 | else | ||
4282 | atom_len = 68; | ||
4283 | offset = 0; | ||
4284 | break; | ||
4285 | case FIRMWARE_TYPE_INTERSIL: | ||
4286 | offset = 4; | ||
4287 | if (priv->has_hostscan) | ||
4288 | atom_len = scan[0] + (scan[1] << 8); | ||
4289 | else | ||
4290 | atom_len = offsetof(struct prism2_scan_apinfo, atim); | ||
4291 | break; | ||
4292 | default: | ||
4293 | return 0; | ||
4294 | } | ||
4295 | |||
4296 | /* Check that we got an whole number of atoms */ | ||
4297 | if ((scan_len - offset) % atom_len) { | ||
4298 | printk(KERN_ERR "%s: Unexpected scan data length %d, " | ||
4299 | "atom_len %d, offset %d\n", dev->name, scan_len, | ||
4300 | atom_len, offset); | ||
4301 | return 0; | ||
4302 | } | ||
4303 | |||
4304 | /* Read the entries one by one */ | ||
4305 | for (; offset + atom_len <= scan_len; offset += atom_len) { | ||
4306 | /* Get next atom */ | ||
4307 | atom = (union hermes_scan_info *) (scan + offset); | ||
4308 | |||
4309 | /* First entry *MUST* be the AP MAC address */ | ||
4310 | iwe.cmd = SIOCGIWAP; | ||
4311 | iwe.u.ap_addr.sa_family = ARPHRD_ETHER; | ||
4312 | memcpy(iwe.u.ap_addr.sa_data, atom->a.bssid, ETH_ALEN); | ||
4313 | current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN); | ||
4314 | |||
4315 | /* Other entries will be displayed in the order we give them */ | ||
4316 | |||
4317 | /* Add the ESSID */ | ||
4318 | iwe.u.data.length = le16_to_cpu(atom->a.essid_len); | ||
4319 | if (iwe.u.data.length > 32) | ||
4320 | iwe.u.data.length = 32; | ||
4321 | iwe.cmd = SIOCGIWESSID; | ||
4322 | iwe.u.data.flags = 1; | ||
4323 | current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, atom->a.essid); | ||
4324 | |||
4325 | /* Add mode */ | ||
4326 | iwe.cmd = SIOCGIWMODE; | ||
4327 | capabilities = le16_to_cpu(atom->a.capabilities); | ||
4328 | if (capabilities & 0x3) { | ||
4329 | if (capabilities & 0x1) | ||
4330 | iwe.u.mode = IW_MODE_MASTER; | ||
4331 | else | ||
4332 | iwe.u.mode = IW_MODE_ADHOC; | ||
4333 | current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_UINT_LEN); | ||
4334 | } | ||
4335 | |||
4336 | channel = atom->s.channel; | ||
4337 | if ( (channel >= 1) && (channel <= NUM_CHANNELS) ) { | ||
4338 | /* Add frequency */ | ||
4339 | iwe.cmd = SIOCGIWFREQ; | ||
4340 | iwe.u.freq.m = channel_frequency[channel-1] * 100000; | ||
4341 | iwe.u.freq.e = 1; | ||
4342 | current_ev = iwe_stream_add_event(current_ev, end_buf, | ||
4343 | &iwe, IW_EV_FREQ_LEN); | ||
4344 | } | ||
4345 | |||
4346 | /* Add quality statistics */ | ||
4347 | iwe.cmd = IWEVQUAL; | ||
4348 | iwe.u.qual.updated = 0x10; /* no link quality */ | ||
4349 | iwe.u.qual.level = (__u8) le16_to_cpu(atom->a.level) - 0x95; | ||
4350 | iwe.u.qual.noise = (__u8) le16_to_cpu(atom->a.noise) - 0x95; | ||
4351 | /* Wireless tools prior to 27.pre22 will show link quality | ||
4352 | * anyway, so we provide a reasonable value. */ | ||
4353 | if (iwe.u.qual.level > iwe.u.qual.noise) | ||
4354 | iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise; | ||
4355 | else | ||
4356 | iwe.u.qual.qual = 0; | ||
4357 | current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN); | ||
4358 | |||
4359 | /* Add encryption capability */ | ||
4360 | iwe.cmd = SIOCGIWENCODE; | ||
4361 | if (capabilities & 0x10) | ||
4362 | iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; | ||
4363 | else | ||
4364 | iwe.u.data.flags = IW_ENCODE_DISABLED; | ||
4365 | iwe.u.data.length = 0; | ||
4366 | current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, atom->a.essid); | ||
4367 | |||
4368 | /* Bit rate is not available in Lucent/Agere firmwares */ | ||
4369 | if (priv->firmware_type != FIRMWARE_TYPE_AGERE) { | ||
4370 | char * current_val = current_ev + IW_EV_LCP_LEN; | ||
4371 | int i; | ||
4372 | int step; | ||
4373 | |||
4374 | if (priv->firmware_type == FIRMWARE_TYPE_SYMBOL) | ||
4375 | step = 2; | ||
4376 | else | ||
4377 | step = 1; | ||
4378 | |||
4379 | iwe.cmd = SIOCGIWRATE; | ||
4380 | /* Those two flags are ignored... */ | ||
4381 | iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; | ||
4382 | /* Max 10 values */ | ||
4383 | for (i = 0; i < 10; i += step) { | ||
4384 | /* NULL terminated */ | ||
4385 | if (atom->p.rates[i] == 0x0) | ||
4386 | break; | ||
4387 | /* Bit rate given in 500 kb/s units (+ 0x80) */ | ||
4388 | iwe.u.bitrate.value = ((atom->p.rates[i] & 0x7f) * 500000); | ||
4389 | current_val = iwe_stream_add_value(current_ev, current_val, | ||
4390 | end_buf, &iwe, | ||
4391 | IW_EV_PARAM_LEN); | ||
4392 | } | ||
4393 | /* Check if we added any event */ | ||
4394 | if ((current_val - current_ev) > IW_EV_LCP_LEN) | ||
4395 | current_ev = current_val; | ||
4396 | } | ||
4397 | |||
4398 | /* The other data in the scan result are not really | ||
4399 | * interesting, so for now drop it - Jean II */ | ||
4400 | } | ||
4401 | return current_ev - buffer; | ||
4402 | } | ||
4403 | |||
4404 | /* Return results of a scan */ | ||
4405 | static int orinoco_ioctl_getscan(struct net_device *dev, | ||
4406 | struct iw_request_info *info, | ||
4407 | struct iw_point *srq, | ||
4408 | char *extra) | ||
4409 | { | ||
4410 | struct orinoco_private *priv = netdev_priv(dev); | ||
4411 | int err = 0; | ||
4412 | unsigned long flags; | ||
4413 | |||
4414 | if (orinoco_lock(priv, &flags) != 0) | ||
4415 | return -EBUSY; | ||
4416 | |||
4417 | /* If no results yet, ask to try again later */ | ||
4418 | if (priv->scan_result == NULL) { | ||
4419 | if (priv->scan_inprogress) | ||
4420 | /* Important note : we don't want to block the caller | ||
4421 | * until results are ready for various reasons. | ||
4422 | * First, managing wait queues is complex and racy. | ||
4423 | * Second, we grab some rtnetlink lock before comming | ||
4424 | * here (in dev_ioctl()). | ||
4425 | * Third, we generate an Wireless Event, so the | ||
4426 | * caller can wait itself on that - Jean II */ | ||
4427 | err = -EAGAIN; | ||
4428 | else | ||
4429 | /* Client error, no scan results... | ||
4430 | * The caller need to restart the scan. */ | ||
4431 | err = -ENODATA; | ||
4432 | } else { | ||
4433 | /* We have some results to push back to user space */ | ||
4434 | |||
4435 | /* Translate to WE format */ | ||
4436 | srq->length = orinoco_translate_scan(dev, extra, | ||
4437 | priv->scan_result, | ||
4438 | priv->scan_len); | ||
4439 | |||
4440 | /* Return flags */ | ||
4441 | srq->flags = (__u16) priv->scan_mode; | ||
4442 | |||
4443 | /* Results are here, so scan no longer in progress */ | ||
4444 | priv->scan_inprogress = 0; | ||
4445 | |||
4446 | /* In any case, Scan results will be cleaned up in the | ||
4447 | * reset function and when exiting the driver. | ||
4448 | * The person triggering the scanning may never come to | ||
4449 | * pick the results, so we need to do it in those places. | ||
4450 | * Jean II */ | ||
4451 | |||
4452 | #ifdef SCAN_SINGLE_READ | ||
4453 | /* If you enable this option, only one client (the first | ||
4454 | * one) will be able to read the result (and only one | ||
4455 | * time). If there is multiple concurent clients that | ||
4456 | * want to read scan results, this behavior is not | ||
4457 | * advisable - Jean II */ | ||
4458 | kfree(priv->scan_result); | ||
4459 | priv->scan_result = NULL; | ||
4460 | #endif /* SCAN_SINGLE_READ */ | ||
4461 | /* Here, if too much time has elapsed since last scan, | ||
4462 | * we may want to clean up scan results... - Jean II */ | ||
4463 | } | ||
4464 | |||
4465 | orinoco_unlock(priv, &flags); | ||
4466 | return err; | ||
4467 | } | ||
4468 | |||
3970 | /* Commit handler, called after set operations */ | 4469 | /* Commit handler, called after set operations */ |
3971 | static int orinoco_ioctl_commit(struct net_device *dev, | 4470 | static int orinoco_ioctl_commit(struct net_device *dev, |
3972 | struct iw_request_info *info, | 4471 | struct iw_request_info *info, |
@@ -4060,6 +4559,8 @@ static const iw_handler orinoco_handler[] = { | |||
4060 | [SIOCGIWSPY -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getspy, | 4559 | [SIOCGIWSPY -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getspy, |
4061 | [SIOCSIWAP -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setwap, | 4560 | [SIOCSIWAP -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setwap, |
4062 | [SIOCGIWAP -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getwap, | 4561 | [SIOCGIWAP -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getwap, |
4562 | [SIOCSIWSCAN -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setscan, | ||
4563 | [SIOCGIWSCAN -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getscan, | ||
4063 | [SIOCSIWESSID -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setessid, | 4564 | [SIOCSIWESSID -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setessid, |
4064 | [SIOCGIWESSID -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getessid, | 4565 | [SIOCGIWESSID -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getessid, |
4065 | [SIOCSIWNICKN -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setnick, | 4566 | [SIOCSIWNICKN -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setnick, |