diff options
author | David Kilroy <kilroyd@gmail.com> | 2008-08-21 18:27:58 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-08-22 19:28:05 -0400 |
commit | 01632fa4af34addf78ce999eabb4430f33942ee2 (patch) | |
tree | c2127215d99e2d67dd402e07286b4c66df64efb3 /drivers/net/wireless/orinoco.c | |
parent | 3056c40426b9cd4f7c334d773d69db64e195431d (diff) |
orinoco: Use extended Agere scans available on 9.x series firmwares
This provides more information than the standard Agere scan, including
the WPA IE.
Signed-off-by: David Kilroy <kilroyd@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/orinoco.c')
-rw-r--r-- | drivers/net/wireless/orinoco.c | 448 |
1 files changed, 405 insertions, 43 deletions
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index 3d5570d0a262..22718e8176ff 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c | |||
@@ -275,13 +275,19 @@ static inline void set_port_type(struct orinoco_private *priv) | |||
275 | #define ORINOCO_MAX_BSS_COUNT 64 | 275 | #define ORINOCO_MAX_BSS_COUNT 64 |
276 | static int orinoco_bss_data_allocate(struct orinoco_private *priv) | 276 | static int orinoco_bss_data_allocate(struct orinoco_private *priv) |
277 | { | 277 | { |
278 | if (priv->bss_data) | 278 | if (priv->bss_xbss_data) |
279 | return 0; | 279 | return 0; |
280 | 280 | ||
281 | priv->bss_data = | 281 | if (priv->has_ext_scan) |
282 | kzalloc(ORINOCO_MAX_BSS_COUNT * sizeof(struct bss_element), | 282 | priv->bss_xbss_data = kzalloc(ORINOCO_MAX_BSS_COUNT * |
283 | GFP_KERNEL); | 283 | sizeof(struct xbss_element), |
284 | if (!priv->bss_data) { | 284 | GFP_KERNEL); |
285 | else | ||
286 | priv->bss_xbss_data = kzalloc(ORINOCO_MAX_BSS_COUNT * | ||
287 | sizeof(struct bss_element), | ||
288 | GFP_KERNEL); | ||
289 | |||
290 | if (!priv->bss_xbss_data) { | ||
285 | printk(KERN_WARNING "Out of memory allocating beacons"); | 291 | printk(KERN_WARNING "Out of memory allocating beacons"); |
286 | return -ENOMEM; | 292 | return -ENOMEM; |
287 | } | 293 | } |
@@ -290,18 +296,53 @@ static int orinoco_bss_data_allocate(struct orinoco_private *priv) | |||
290 | 296 | ||
291 | static void orinoco_bss_data_free(struct orinoco_private *priv) | 297 | static void orinoco_bss_data_free(struct orinoco_private *priv) |
292 | { | 298 | { |
293 | kfree(priv->bss_data); | 299 | kfree(priv->bss_xbss_data); |
294 | priv->bss_data = NULL; | 300 | priv->bss_xbss_data = NULL; |
295 | } | 301 | } |
296 | 302 | ||
303 | #define PRIV_BSS ((struct bss_element *)priv->bss_xbss_data) | ||
304 | #define PRIV_XBSS ((struct xbss_element *)priv->bss_xbss_data) | ||
297 | static void orinoco_bss_data_init(struct orinoco_private *priv) | 305 | static void orinoco_bss_data_init(struct orinoco_private *priv) |
298 | { | 306 | { |
299 | int i; | 307 | int i; |
300 | 308 | ||
301 | INIT_LIST_HEAD(&priv->bss_free_list); | 309 | INIT_LIST_HEAD(&priv->bss_free_list); |
302 | INIT_LIST_HEAD(&priv->bss_list); | 310 | INIT_LIST_HEAD(&priv->bss_list); |
303 | for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++) | 311 | if (priv->has_ext_scan) |
304 | list_add_tail(&priv->bss_data[i].list, &priv->bss_free_list); | 312 | for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++) |
313 | list_add_tail(&(PRIV_XBSS[i].list), | ||
314 | &priv->bss_free_list); | ||
315 | else | ||
316 | for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++) | ||
317 | list_add_tail(&(PRIV_BSS[i].list), | ||
318 | &priv->bss_free_list); | ||
319 | |||
320 | } | ||
321 | |||
322 | static inline u8 *orinoco_get_ie(u8 *data, size_t len, | ||
323 | enum ieee80211_mfie eid) | ||
324 | { | ||
325 | u8 *p = data; | ||
326 | while ((p + 2) < (data + len)) { | ||
327 | if (p[0] == eid) | ||
328 | return p; | ||
329 | p += p[1] + 2; | ||
330 | } | ||
331 | return NULL; | ||
332 | } | ||
333 | |||
334 | #define WPA_OUI_TYPE "\x00\x50\xF2\x01" | ||
335 | #define WPA_SELECTOR_LEN 4 | ||
336 | static inline u8 *orinoco_get_wpa_ie(u8 *data, size_t len) | ||
337 | { | ||
338 | u8 *p = data; | ||
339 | while ((p + 2 + WPA_SELECTOR_LEN) < (data + len)) { | ||
340 | if ((p[0] == MFIE_TYPE_GENERIC) && | ||
341 | (memcmp(&p[2], WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0)) | ||
342 | return p; | ||
343 | p += p[1] + 2; | ||
344 | } | ||
345 | return NULL; | ||
305 | } | 346 | } |
306 | 347 | ||
307 | 348 | ||
@@ -1414,18 +1455,72 @@ static void orinoco_send_wevents(struct work_struct *work) | |||
1414 | static inline void orinoco_clear_scan_results(struct orinoco_private *priv, | 1455 | static inline void orinoco_clear_scan_results(struct orinoco_private *priv, |
1415 | unsigned long scan_age) | 1456 | unsigned long scan_age) |
1416 | { | 1457 | { |
1417 | struct bss_element *bss; | 1458 | if (priv->has_ext_scan) { |
1418 | struct bss_element *tmp_bss; | 1459 | struct xbss_element *bss; |
1419 | 1460 | struct xbss_element *tmp_bss; | |
1420 | /* Blow away current list of scan results */ | 1461 | |
1421 | list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) { | 1462 | /* Blow away current list of scan results */ |
1422 | if (!scan_age || | 1463 | list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) { |
1423 | time_after(jiffies, bss->last_scanned + scan_age)) { | 1464 | if (!scan_age || |
1424 | list_move_tail(&bss->list, &priv->bss_free_list); | 1465 | time_after(jiffies, bss->last_scanned + scan_age)) { |
1425 | /* Don't blow away ->list, just BSS data */ | 1466 | list_move_tail(&bss->list, |
1426 | memset(bss, 0, sizeof(bss->bss)); | 1467 | &priv->bss_free_list); |
1427 | bss->last_scanned = 0; | 1468 | /* Don't blow away ->list, just BSS data */ |
1469 | memset(&bss->bss, 0, sizeof(bss->bss)); | ||
1470 | bss->last_scanned = 0; | ||
1471 | } | ||
1428 | } | 1472 | } |
1473 | } else { | ||
1474 | struct bss_element *bss; | ||
1475 | struct bss_element *tmp_bss; | ||
1476 | |||
1477 | /* Blow away current list of scan results */ | ||
1478 | list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) { | ||
1479 | if (!scan_age || | ||
1480 | time_after(jiffies, bss->last_scanned + scan_age)) { | ||
1481 | list_move_tail(&bss->list, | ||
1482 | &priv->bss_free_list); | ||
1483 | /* Don't blow away ->list, just BSS data */ | ||
1484 | memset(&bss->bss, 0, sizeof(bss->bss)); | ||
1485 | bss->last_scanned = 0; | ||
1486 | } | ||
1487 | } | ||
1488 | } | ||
1489 | } | ||
1490 | |||
1491 | static void orinoco_add_ext_scan_result(struct orinoco_private *priv, | ||
1492 | struct agere_ext_scan_info *atom) | ||
1493 | { | ||
1494 | struct xbss_element *bss = NULL; | ||
1495 | int found = 0; | ||
1496 | |||
1497 | /* Try to update an existing bss first */ | ||
1498 | list_for_each_entry(bss, &priv->bss_list, list) { | ||
1499 | if (compare_ether_addr(bss->bss.bssid, atom->bssid)) | ||
1500 | continue; | ||
1501 | /* ESSID lengths */ | ||
1502 | if (bss->bss.data[1] != atom->data[1]) | ||
1503 | continue; | ||
1504 | if (memcmp(&bss->bss.data[2], &atom->data[2], | ||
1505 | atom->data[1])) | ||
1506 | continue; | ||
1507 | found = 1; | ||
1508 | break; | ||
1509 | } | ||
1510 | |||
1511 | /* Grab a bss off the free list */ | ||
1512 | if (!found && !list_empty(&priv->bss_free_list)) { | ||
1513 | bss = list_entry(priv->bss_free_list.next, | ||
1514 | struct xbss_element, list); | ||
1515 | list_del(priv->bss_free_list.next); | ||
1516 | |||
1517 | list_add_tail(&bss->list, &priv->bss_list); | ||
1518 | } | ||
1519 | |||
1520 | if (bss) { | ||
1521 | /* Always update the BSS to get latest beacon info */ | ||
1522 | memcpy(&bss->bss, atom, sizeof(bss->bss)); | ||
1523 | bss->last_scanned = jiffies; | ||
1429 | } | 1524 | } |
1430 | } | 1525 | } |
1431 | 1526 | ||
@@ -1700,6 +1795,63 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) | |||
1700 | kfree(buf); | 1795 | kfree(buf); |
1701 | } | 1796 | } |
1702 | break; | 1797 | break; |
1798 | case HERMES_INQ_CHANNELINFO: | ||
1799 | { | ||
1800 | struct agere_ext_scan_info *bss; | ||
1801 | |||
1802 | if (!priv->scan_inprogress) { | ||
1803 | printk(KERN_DEBUG "%s: Got chaninfo without scan, " | ||
1804 | "len=%d\n", dev->name, len); | ||
1805 | break; | ||
1806 | } | ||
1807 | |||
1808 | /* An empty result indicates that the scan is complete */ | ||
1809 | if (len == 0) { | ||
1810 | union iwreq_data wrqu; | ||
1811 | |||
1812 | /* Scan is no longer in progress */ | ||
1813 | priv->scan_inprogress = 0; | ||
1814 | |||
1815 | wrqu.data.length = 0; | ||
1816 | wrqu.data.flags = 0; | ||
1817 | wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); | ||
1818 | break; | ||
1819 | } | ||
1820 | |||
1821 | /* Sanity check */ | ||
1822 | else if (len > sizeof(*bss)) { | ||
1823 | printk(KERN_WARNING | ||
1824 | "%s: Ext scan results too large (%d bytes). " | ||
1825 | "Truncating results to %zd bytes.\n", | ||
1826 | dev->name, len, sizeof(*bss)); | ||
1827 | len = sizeof(*bss); | ||
1828 | } else if (len < (offsetof(struct agere_ext_scan_info, | ||
1829 | data) + 2)) { | ||
1830 | /* Drop this result now so we don't have to | ||
1831 | * keep checking later */ | ||
1832 | printk(KERN_WARNING | ||
1833 | "%s: Ext scan results too short (%d bytes)\n", | ||
1834 | dev->name, len); | ||
1835 | break; | ||
1836 | } | ||
1837 | |||
1838 | bss = kmalloc(sizeof(*bss), GFP_ATOMIC); | ||
1839 | if (bss == NULL) | ||
1840 | break; | ||
1841 | |||
1842 | /* Read scan data */ | ||
1843 | err = hermes_bap_pread(hw, IRQ_BAP, (void *) bss, len, | ||
1844 | infofid, sizeof(info)); | ||
1845 | if (err) { | ||
1846 | kfree(bss); | ||
1847 | break; | ||
1848 | } | ||
1849 | |||
1850 | orinoco_add_ext_scan_result(priv, bss); | ||
1851 | |||
1852 | kfree(bss); | ||
1853 | break; | ||
1854 | } | ||
1703 | case HERMES_INQ_SEC_STAT_AGERE: | 1855 | case HERMES_INQ_SEC_STAT_AGERE: |
1704 | /* Security status (Agere specific) */ | 1856 | /* Security status (Agere specific) */ |
1705 | /* Ignore this frame for now */ | 1857 | /* Ignore this frame for now */ |
@@ -2557,6 +2709,7 @@ static int determine_firmware(struct net_device *dev) | |||
2557 | priv->has_wep = 0; | 2709 | priv->has_wep = 0; |
2558 | priv->has_big_wep = 0; | 2710 | priv->has_big_wep = 0; |
2559 | priv->has_alt_txcntl = 0; | 2711 | priv->has_alt_txcntl = 0; |
2712 | priv->has_ext_scan = 0; | ||
2560 | priv->do_fw_download = 0; | 2713 | priv->do_fw_download = 0; |
2561 | 2714 | ||
2562 | /* Determine capabilities from the firmware version */ | 2715 | /* Determine capabilities from the firmware version */ |
@@ -2580,7 +2733,7 @@ static int determine_firmware(struct net_device *dev) | |||
2580 | priv->do_fw_download = 1; | 2733 | priv->do_fw_download = 1; |
2581 | priv->broken_monitor = (firmver >= 0x80000); | 2734 | priv->broken_monitor = (firmver >= 0x80000); |
2582 | priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */ | 2735 | priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */ |
2583 | 2736 | priv->has_ext_scan = (firmver >= 0x90000); /* All 9.x ? */ | |
2584 | /* Tested with Agere firmware : | 2737 | /* Tested with Agere firmware : |
2585 | * 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II | 2738 | * 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II |
2586 | * Tested CableTron firmware : 4.32 => Anton */ | 2739 | * Tested CableTron firmware : 4.32 => Anton */ |
@@ -2735,6 +2888,12 @@ static int orinoco_init(struct net_device *dev) | |||
2735 | printk("40-bit key\n"); | 2888 | printk("40-bit key\n"); |
2736 | } | 2889 | } |
2737 | 2890 | ||
2891 | /* Now we have the firmware capabilities, allocate appropiate | ||
2892 | * sized scan buffers */ | ||
2893 | if (orinoco_bss_data_allocate(priv)) | ||
2894 | goto out; | ||
2895 | orinoco_bss_data_init(priv); | ||
2896 | |||
2738 | /* Get the MAC address */ | 2897 | /* Get the MAC address */ |
2739 | err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, | 2898 | err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, |
2740 | ETH_ALEN, NULL, dev->dev_addr); | 2899 | ETH_ALEN, NULL, dev->dev_addr); |
@@ -2885,10 +3044,6 @@ struct net_device | |||
2885 | priv->card = NULL; | 3044 | priv->card = NULL; |
2886 | priv->dev = device; | 3045 | priv->dev = device; |
2887 | 3046 | ||
2888 | if (orinoco_bss_data_allocate(priv)) | ||
2889 | goto err_out_free; | ||
2890 | orinoco_bss_data_init(priv); | ||
2891 | |||
2892 | /* Setup / override net_device fields */ | 3047 | /* Setup / override net_device fields */ |
2893 | dev->init = orinoco_init; | 3048 | dev->init = orinoco_init; |
2894 | dev->hard_start_xmit = orinoco_xmit; | 3049 | dev->hard_start_xmit = orinoco_xmit; |
@@ -2924,10 +3079,6 @@ struct net_device | |||
2924 | priv->last_linkstatus = 0xffff; | 3079 | priv->last_linkstatus = 0xffff; |
2925 | 3080 | ||
2926 | return dev; | 3081 | return dev; |
2927 | |||
2928 | err_out_free: | ||
2929 | free_netdev(dev); | ||
2930 | return NULL; | ||
2931 | } | 3082 | } |
2932 | 3083 | ||
2933 | void free_orinocodev(struct net_device *dev) | 3084 | void free_orinocodev(struct net_device *dev) |
@@ -4375,7 +4526,25 @@ static int orinoco_ioctl_setscan(struct net_device *dev, | |||
4375 | if (err) | 4526 | if (err) |
4376 | break; | 4527 | break; |
4377 | 4528 | ||
4378 | err = hermes_inquire(hw, HERMES_INQ_SCAN); | 4529 | if (priv->has_ext_scan) { |
4530 | /* Clear scan results at the start of | ||
4531 | * an extended scan */ | ||
4532 | orinoco_clear_scan_results(priv, | ||
4533 | msecs_to_jiffies(15000)); | ||
4534 | |||
4535 | /* TODO: Is this available on older firmware? | ||
4536 | * Can we use it to scan specific channels | ||
4537 | * for IW_SCAN_THIS_FREQ? */ | ||
4538 | err = hermes_write_wordrec(hw, USER_BAP, | ||
4539 | HERMES_RID_CNFSCANCHANNELS2GHZ, | ||
4540 | 0x7FFF); | ||
4541 | if (err) | ||
4542 | goto out; | ||
4543 | |||
4544 | err = hermes_inquire(hw, | ||
4545 | HERMES_INQ_CHANNELINFO); | ||
4546 | } else | ||
4547 | err = hermes_inquire(hw, HERMES_INQ_SCAN); | ||
4379 | break; | 4548 | break; |
4380 | } | 4549 | } |
4381 | } else | 4550 | } else |
@@ -4541,6 +4710,171 @@ static inline char *orinoco_translate_scan(struct net_device *dev, | |||
4541 | return current_ev; | 4710 | return current_ev; |
4542 | } | 4711 | } |
4543 | 4712 | ||
4713 | static inline char *orinoco_translate_ext_scan(struct net_device *dev, | ||
4714 | struct iw_request_info *info, | ||
4715 | char *current_ev, | ||
4716 | char *end_buf, | ||
4717 | struct agere_ext_scan_info *bss, | ||
4718 | unsigned int last_scanned) | ||
4719 | { | ||
4720 | u16 capabilities; | ||
4721 | u16 channel; | ||
4722 | struct iw_event iwe; /* Temporary buffer */ | ||
4723 | char custom[MAX_CUSTOM_LEN]; | ||
4724 | u8 *ie; | ||
4725 | |||
4726 | memset(&iwe, 0, sizeof(iwe)); | ||
4727 | |||
4728 | /* First entry *MUST* be the AP MAC address */ | ||
4729 | iwe.cmd = SIOCGIWAP; | ||
4730 | iwe.u.ap_addr.sa_family = ARPHRD_ETHER; | ||
4731 | memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN); | ||
4732 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
4733 | &iwe, IW_EV_ADDR_LEN); | ||
4734 | |||
4735 | /* Other entries will be displayed in the order we give them */ | ||
4736 | |||
4737 | /* Add the ESSID */ | ||
4738 | ie = bss->data; | ||
4739 | iwe.u.data.length = ie[1]; | ||
4740 | if (iwe.u.data.length) { | ||
4741 | if (iwe.u.data.length > 32) | ||
4742 | iwe.u.data.length = 32; | ||
4743 | iwe.cmd = SIOCGIWESSID; | ||
4744 | iwe.u.data.flags = 1; | ||
4745 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
4746 | &iwe, &ie[2]); | ||
4747 | } | ||
4748 | |||
4749 | /* Add mode */ | ||
4750 | capabilities = le16_to_cpu(bss->capabilities); | ||
4751 | if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) { | ||
4752 | iwe.cmd = SIOCGIWMODE; | ||
4753 | if (capabilities & WLAN_CAPABILITY_ESS) | ||
4754 | iwe.u.mode = IW_MODE_MASTER; | ||
4755 | else | ||
4756 | iwe.u.mode = IW_MODE_ADHOC; | ||
4757 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
4758 | &iwe, IW_EV_UINT_LEN); | ||
4759 | } | ||
4760 | |||
4761 | ie = orinoco_get_ie(bss->data, sizeof(bss->data), MFIE_TYPE_DS_SET); | ||
4762 | channel = ie ? ie[2] : 0; | ||
4763 | if ((channel >= 1) && (channel <= NUM_CHANNELS)) { | ||
4764 | /* Add channel and frequency */ | ||
4765 | iwe.cmd = SIOCGIWFREQ; | ||
4766 | iwe.u.freq.m = channel; | ||
4767 | iwe.u.freq.e = 0; | ||
4768 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
4769 | &iwe, IW_EV_FREQ_LEN); | ||
4770 | |||
4771 | iwe.u.freq.m = channel_frequency[channel-1] * 100000; | ||
4772 | iwe.u.freq.e = 1; | ||
4773 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
4774 | &iwe, IW_EV_FREQ_LEN); | ||
4775 | } | ||
4776 | |||
4777 | /* Add quality statistics. level and noise in dB. No link quality */ | ||
4778 | iwe.cmd = IWEVQUAL; | ||
4779 | iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID; | ||
4780 | iwe.u.qual.level = bss->level - 0x95; | ||
4781 | iwe.u.qual.noise = bss->noise - 0x95; | ||
4782 | /* Wireless tools prior to 27.pre22 will show link quality | ||
4783 | * anyway, so we provide a reasonable value. */ | ||
4784 | if (iwe.u.qual.level > iwe.u.qual.noise) | ||
4785 | iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise; | ||
4786 | else | ||
4787 | iwe.u.qual.qual = 0; | ||
4788 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
4789 | &iwe, IW_EV_QUAL_LEN); | ||
4790 | |||
4791 | /* Add encryption capability */ | ||
4792 | iwe.cmd = SIOCGIWENCODE; | ||
4793 | if (capabilities & WLAN_CAPABILITY_PRIVACY) | ||
4794 | iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; | ||
4795 | else | ||
4796 | iwe.u.data.flags = IW_ENCODE_DISABLED; | ||
4797 | iwe.u.data.length = 0; | ||
4798 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
4799 | &iwe, NULL); | ||
4800 | |||
4801 | /* WPA IE */ | ||
4802 | ie = orinoco_get_wpa_ie(bss->data, sizeof(bss->data)); | ||
4803 | if (ie) { | ||
4804 | iwe.cmd = IWEVGENIE; | ||
4805 | iwe.u.data.length = ie[1] + 2; | ||
4806 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
4807 | &iwe, ie); | ||
4808 | } | ||
4809 | |||
4810 | /* RSN IE */ | ||
4811 | ie = orinoco_get_ie(bss->data, sizeof(bss->data), MFIE_TYPE_RSN); | ||
4812 | if (ie) { | ||
4813 | iwe.cmd = IWEVGENIE; | ||
4814 | iwe.u.data.length = ie[1] + 2; | ||
4815 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
4816 | &iwe, ie); | ||
4817 | } | ||
4818 | |||
4819 | ie = orinoco_get_ie(bss->data, sizeof(bss->data), MFIE_TYPE_RATES); | ||
4820 | if (ie) { | ||
4821 | char *p = current_ev + iwe_stream_lcp_len(info); | ||
4822 | int i; | ||
4823 | |||
4824 | iwe.cmd = SIOCGIWRATE; | ||
4825 | /* Those two flags are ignored... */ | ||
4826 | iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; | ||
4827 | |||
4828 | for (i = 2; i < (ie[1] + 2); i++) { | ||
4829 | iwe.u.bitrate.value = ((ie[i] & 0x7F) * 500000); | ||
4830 | p = iwe_stream_add_value(info, current_ev, p, end_buf, | ||
4831 | &iwe, IW_EV_PARAM_LEN); | ||
4832 | } | ||
4833 | /* Check if we added any event */ | ||
4834 | if (p > (current_ev + iwe_stream_lcp_len(info))) | ||
4835 | current_ev = p; | ||
4836 | } | ||
4837 | |||
4838 | /* Timestamp */ | ||
4839 | iwe.cmd = IWEVCUSTOM; | ||
4840 | iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, | ||
4841 | "tsf=%016llx", | ||
4842 | le64_to_cpu(bss->timestamp)); | ||
4843 | if (iwe.u.data.length) | ||
4844 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
4845 | &iwe, custom); | ||
4846 | |||
4847 | /* Beacon interval */ | ||
4848 | iwe.cmd = IWEVCUSTOM; | ||
4849 | iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, | ||
4850 | "bcn_int=%d", | ||
4851 | le16_to_cpu(bss->beacon_interval)); | ||
4852 | if (iwe.u.data.length) | ||
4853 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
4854 | &iwe, custom); | ||
4855 | |||
4856 | /* Capabilites */ | ||
4857 | iwe.cmd = IWEVCUSTOM; | ||
4858 | iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, | ||
4859 | "capab=0x%04x", | ||
4860 | capabilities); | ||
4861 | if (iwe.u.data.length) | ||
4862 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
4863 | &iwe, custom); | ||
4864 | |||
4865 | /* Add EXTRA: Age to display seconds since last beacon/probe response | ||
4866 | * for given network. */ | ||
4867 | iwe.cmd = IWEVCUSTOM; | ||
4868 | iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, | ||
4869 | " Last beacon: %dms ago", | ||
4870 | jiffies_to_msecs(jiffies - last_scanned)); | ||
4871 | if (iwe.u.data.length) | ||
4872 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
4873 | &iwe, custom); | ||
4874 | |||
4875 | return current_ev; | ||
4876 | } | ||
4877 | |||
4544 | /* Return results of a scan */ | 4878 | /* Return results of a scan */ |
4545 | static int orinoco_ioctl_getscan(struct net_device *dev, | 4879 | static int orinoco_ioctl_getscan(struct net_device *dev, |
4546 | struct iw_request_info *info, | 4880 | struct iw_request_info *info, |
@@ -4548,7 +4882,6 @@ static int orinoco_ioctl_getscan(struct net_device *dev, | |||
4548 | char *extra) | 4882 | char *extra) |
4549 | { | 4883 | { |
4550 | struct orinoco_private *priv = netdev_priv(dev); | 4884 | struct orinoco_private *priv = netdev_priv(dev); |
4551 | struct bss_element *bss; | ||
4552 | int err = 0; | 4885 | int err = 0; |
4553 | unsigned long flags; | 4886 | unsigned long flags; |
4554 | char *current_ev = extra; | 4887 | char *current_ev = extra; |
@@ -4568,18 +4901,47 @@ static int orinoco_ioctl_getscan(struct net_device *dev, | |||
4568 | goto out; | 4901 | goto out; |
4569 | } | 4902 | } |
4570 | 4903 | ||
4571 | list_for_each_entry(bss, &priv->bss_list, list) { | 4904 | if (priv->has_ext_scan) { |
4572 | /* Translate to WE format this entry */ | 4905 | struct xbss_element *bss; |
4573 | current_ev = orinoco_translate_scan(dev, info, current_ev, | 4906 | |
4574 | extra + srq->length, | 4907 | list_for_each_entry(bss, &priv->bss_list, list) { |
4575 | &bss->bss, | 4908 | /* Translate this entry to WE format */ |
4576 | bss->last_scanned); | 4909 | current_ev = |
4577 | 4910 | orinoco_translate_ext_scan(dev, info, | |
4578 | /* Check if there is space for one more entry */ | 4911 | current_ev, |
4579 | if ((extra + srq->length - current_ev) <= IW_EV_ADDR_LEN) { | 4912 | extra + srq->length, |
4580 | /* Ask user space to try again with a bigger buffer */ | 4913 | &bss->bss, |
4581 | err = -E2BIG; | 4914 | bss->last_scanned); |
4582 | goto out; | 4915 | |
4916 | /* Check if there is space for one more entry */ | ||
4917 | if ((extra + srq->length - current_ev) | ||
4918 | <= IW_EV_ADDR_LEN) { | ||
4919 | /* Ask user space to try again with a | ||
4920 | * bigger buffer */ | ||
4921 | err = -E2BIG; | ||
4922 | goto out; | ||
4923 | } | ||
4924 | } | ||
4925 | |||
4926 | } else { | ||
4927 | struct bss_element *bss; | ||
4928 | |||
4929 | list_for_each_entry(bss, &priv->bss_list, list) { | ||
4930 | /* Translate this entry to WE format */ | ||
4931 | current_ev = orinoco_translate_scan(dev, info, | ||
4932 | current_ev, | ||
4933 | extra + srq->length, | ||
4934 | &bss->bss, | ||
4935 | bss->last_scanned); | ||
4936 | |||
4937 | /* Check if there is space for one more entry */ | ||
4938 | if ((extra + srq->length - current_ev) | ||
4939 | <= IW_EV_ADDR_LEN) { | ||
4940 | /* Ask user space to try again with a | ||
4941 | * bigger buffer */ | ||
4942 | err = -E2BIG; | ||
4943 | goto out; | ||
4944 | } | ||
4583 | } | 4945 | } |
4584 | } | 4946 | } |
4585 | 4947 | ||