diff options
author | Christoph Hellwig <hch@lst.de> | 2005-06-18 19:27:51 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@pobox.com> | 2005-06-27 00:23:55 -0400 |
commit | 16739b065f4b0965d975f5c756204c7aa911cd61 (patch) | |
tree | b52614d4654009bcddf61ad44bb7b8cbbfaedc8e /drivers/net | |
parent | 1fab2e8b7a9dd0226e42ad5d3688edd5065bd231 (diff) |
[PATCH] orinoco: manual roaming for Symbol and Intersilfirmware
Patch from Pavel Roskin
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/wireless/orinoco.c | 169 | ||||
-rw-r--r-- | drivers/net/wireless/orinoco.h | 5 |
2 files changed, 174 insertions, 0 deletions
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index b5f626cae98c..c057b7f9a02a 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c | |||
@@ -1247,6 +1247,75 @@ static void print_linkstatus(struct net_device *dev, u16 status) | |||
1247 | dev->name, s, status); | 1247 | dev->name, s, status); |
1248 | } | 1248 | } |
1249 | 1249 | ||
1250 | /* Search scan results for requested BSSID, join it if found */ | ||
1251 | static void orinoco_join_ap(struct net_device *dev) | ||
1252 | { | ||
1253 | struct orinoco_private *priv = netdev_priv(dev); | ||
1254 | struct hermes *hw = &priv->hw; | ||
1255 | int err; | ||
1256 | unsigned long flags; | ||
1257 | struct join_req { | ||
1258 | u8 bssid[ETH_ALEN]; | ||
1259 | u16 channel; | ||
1260 | } __attribute__ ((packed)) req; | ||
1261 | const int atom_len = offsetof(struct prism2_scan_apinfo, atim); | ||
1262 | struct prism2_scan_apinfo *atom; | ||
1263 | int offset = 4; | ||
1264 | u8 *buf; | ||
1265 | u16 len; | ||
1266 | |||
1267 | /* Allocate buffer for scan results */ | ||
1268 | buf = kmalloc(MAX_SCAN_LEN, GFP_KERNEL); | ||
1269 | if (! buf) | ||
1270 | return; | ||
1271 | |||
1272 | if (orinoco_lock(priv, &flags) != 0) | ||
1273 | goto out; | ||
1274 | |||
1275 | /* Sanity checks in case user changed something in the meantime */ | ||
1276 | if (! priv->bssid_fixed) | ||
1277 | goto out; | ||
1278 | |||
1279 | if (strlen(priv->desired_essid) == 0) | ||
1280 | goto out; | ||
1281 | |||
1282 | /* Read scan results from the firmware */ | ||
1283 | err = hermes_read_ltv(hw, USER_BAP, | ||
1284 | HERMES_RID_SCANRESULTSTABLE, | ||
1285 | MAX_SCAN_LEN, &len, buf); | ||
1286 | if (err) { | ||
1287 | printk(KERN_ERR "%s: Cannot read scan results\n", | ||
1288 | dev->name); | ||
1289 | goto out; | ||
1290 | } | ||
1291 | |||
1292 | len = HERMES_RECLEN_TO_BYTES(len); | ||
1293 | |||
1294 | /* Go through the scan results looking for the channel of the AP | ||
1295 | * we were requested to join */ | ||
1296 | for (; offset + atom_len <= len; offset += atom_len) { | ||
1297 | atom = (struct prism2_scan_apinfo *) (buf + offset); | ||
1298 | if (memcmp(&atom->bssid, priv->desired_bssid, ETH_ALEN) == 0) | ||
1299 | goto found; | ||
1300 | } | ||
1301 | |||
1302 | DEBUG(1, "%s: Requested AP not found in scan results\n", | ||
1303 | dev->name); | ||
1304 | goto out; | ||
1305 | |||
1306 | found: | ||
1307 | memcpy(req.bssid, priv->desired_bssid, ETH_ALEN); | ||
1308 | req.channel = atom->channel; /* both are little-endian */ | ||
1309 | err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFJOINREQUEST, | ||
1310 | &req); | ||
1311 | if (err) | ||
1312 | printk(KERN_ERR "%s: Error issuing join request\n", dev->name); | ||
1313 | |||
1314 | out: | ||
1315 | kfree(buf); | ||
1316 | orinoco_unlock(priv, &flags); | ||
1317 | } | ||
1318 | |||
1250 | static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) | 1319 | static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) |
1251 | { | 1320 | { |
1252 | struct orinoco_private *priv = netdev_priv(dev); | 1321 | struct orinoco_private *priv = netdev_priv(dev); |
@@ -1477,6 +1546,36 @@ static int __orinoco_hw_set_bitrate(struct orinoco_private *priv) | |||
1477 | return err; | 1546 | return err; |
1478 | } | 1547 | } |
1479 | 1548 | ||
1549 | /* Set fixed AP address */ | ||
1550 | static int __orinoco_hw_set_wap(struct orinoco_private *priv) | ||
1551 | { | ||
1552 | int roaming_flag; | ||
1553 | int err = 0; | ||
1554 | hermes_t *hw = &priv->hw; | ||
1555 | |||
1556 | switch (priv->firmware_type) { | ||
1557 | case FIRMWARE_TYPE_AGERE: | ||
1558 | /* not supported */ | ||
1559 | break; | ||
1560 | case FIRMWARE_TYPE_INTERSIL: | ||
1561 | if (priv->bssid_fixed) | ||
1562 | roaming_flag = 2; | ||
1563 | else | ||
1564 | roaming_flag = 1; | ||
1565 | |||
1566 | err = hermes_write_wordrec(hw, USER_BAP, | ||
1567 | HERMES_RID_CNFROAMINGMODE, | ||
1568 | roaming_flag); | ||
1569 | break; | ||
1570 | case FIRMWARE_TYPE_SYMBOL: | ||
1571 | err = HERMES_WRITE_RECORD(hw, USER_BAP, | ||
1572 | HERMES_RID_CNFMANDATORYBSSID_SYMBOL, | ||
1573 | &priv->desired_bssid); | ||
1574 | break; | ||
1575 | } | ||
1576 | return err; | ||
1577 | } | ||
1578 | |||
1480 | /* Change the WEP keys and/or the current keys. Can be called | 1579 | /* Change the WEP keys and/or the current keys. Can be called |
1481 | * either from __orinoco_hw_setup_wep() or directly from | 1580 | * either from __orinoco_hw_setup_wep() or directly from |
1482 | * orinoco_ioctl_setiwencode(). In the later case the association | 1581 | * orinoco_ioctl_setiwencode(). In the later case the association |
@@ -1662,6 +1761,13 @@ static int __orinoco_program_rids(struct net_device *dev) | |||
1662 | } | 1761 | } |
1663 | } | 1762 | } |
1664 | 1763 | ||
1764 | /* Set the desired BSSID */ | ||
1765 | err = __orinoco_hw_set_wap(priv); | ||
1766 | if (err) { | ||
1767 | printk(KERN_ERR "%s: Error %d setting AP address\n", | ||
1768 | dev->name, err); | ||
1769 | return err; | ||
1770 | } | ||
1665 | /* Set the desired ESSID */ | 1771 | /* Set the desired ESSID */ |
1666 | idbuf.len = cpu_to_le16(strlen(priv->desired_essid)); | 1772 | idbuf.len = cpu_to_le16(strlen(priv->desired_essid)); |
1667 | memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val)); | 1773 | memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val)); |
@@ -2432,6 +2538,7 @@ struct net_device *alloc_orinocodev(int sizeof_card, | |||
2432 | * before anything else touches the | 2538 | * before anything else touches the |
2433 | * hardware */ | 2539 | * hardware */ |
2434 | INIT_WORK(&priv->reset_work, (void (*)(void *))orinoco_reset, dev); | 2540 | INIT_WORK(&priv->reset_work, (void (*)(void *))orinoco_reset, dev); |
2541 | INIT_WORK(&priv->join_work, (void (*)(void *))orinoco_join_ap, dev); | ||
2435 | 2542 | ||
2436 | netif_carrier_off(dev); | 2543 | netif_carrier_off(dev); |
2437 | priv->last_linkstatus = 0xffff; | 2544 | priv->last_linkstatus = 0xffff; |
@@ -2593,6 +2700,67 @@ static int orinoco_ioctl_getname(struct net_device *dev, | |||
2593 | return 0; | 2700 | return 0; |
2594 | } | 2701 | } |
2595 | 2702 | ||
2703 | static int orinoco_ioctl_setwap(struct net_device *dev, | ||
2704 | struct iw_request_info *info, | ||
2705 | struct sockaddr *ap_addr, | ||
2706 | char *extra) | ||
2707 | { | ||
2708 | struct orinoco_private *priv = netdev_priv(dev); | ||
2709 | int err = -EINPROGRESS; /* Call commit handler */ | ||
2710 | unsigned long flags; | ||
2711 | static const u8 off_addr[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | ||
2712 | static const u8 any_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | ||
2713 | |||
2714 | if (orinoco_lock(priv, &flags) != 0) | ||
2715 | return -EBUSY; | ||
2716 | |||
2717 | /* Enable automatic roaming - no sanity checks are needed */ | ||
2718 | if (memcmp(&ap_addr->sa_data, off_addr, ETH_ALEN) == 0 || | ||
2719 | memcmp(&ap_addr->sa_data, any_addr, ETH_ALEN) == 0) { | ||
2720 | priv->bssid_fixed = 0; | ||
2721 | memset(priv->desired_bssid, 0, ETH_ALEN); | ||
2722 | |||
2723 | /* "off" means keep existing connection */ | ||
2724 | if (ap_addr->sa_data[0] == 0) { | ||
2725 | __orinoco_hw_set_wap(priv); | ||
2726 | err = 0; | ||
2727 | } | ||
2728 | goto out; | ||
2729 | } | ||
2730 | |||
2731 | if (priv->firmware_type == FIRMWARE_TYPE_AGERE) { | ||
2732 | printk(KERN_WARNING "%s: Lucent/Agere firmware doesn't " | ||
2733 | "support manual roaming\n", | ||
2734 | dev->name); | ||
2735 | err = -EOPNOTSUPP; | ||
2736 | goto out; | ||
2737 | } | ||
2738 | |||
2739 | if (priv->iw_mode != IW_MODE_INFRA) { | ||
2740 | printk(KERN_WARNING "%s: Manual roaming supported only in " | ||
2741 | "managed mode\n", dev->name); | ||
2742 | err = -EOPNOTSUPP; | ||
2743 | goto out; | ||
2744 | } | ||
2745 | |||
2746 | /* Intersil firmware hangs without Desired ESSID */ | ||
2747 | if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL && | ||
2748 | strlen(priv->desired_essid) == 0) { | ||
2749 | printk(KERN_WARNING "%s: Desired ESSID must be set for " | ||
2750 | "manual roaming\n", dev->name); | ||
2751 | err = -EOPNOTSUPP; | ||
2752 | goto out; | ||
2753 | } | ||
2754 | |||
2755 | /* Finally, enable manual roaming */ | ||
2756 | priv->bssid_fixed = 1; | ||
2757 | memcpy(priv->desired_bssid, &ap_addr->sa_data, ETH_ALEN); | ||
2758 | |||
2759 | out: | ||
2760 | orinoco_unlock(priv, &flags); | ||
2761 | return err; | ||
2762 | } | ||
2763 | |||
2596 | static int orinoco_ioctl_getwap(struct net_device *dev, | 2764 | static int orinoco_ioctl_getwap(struct net_device *dev, |
2597 | struct iw_request_info *info, | 2765 | struct iw_request_info *info, |
2598 | struct sockaddr *ap_addr, | 2766 | struct sockaddr *ap_addr, |
@@ -3890,6 +4058,7 @@ static const iw_handler orinoco_handler[] = { | |||
3890 | [SIOCGIWRANGE -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getiwrange, | 4058 | [SIOCGIWRANGE -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getiwrange, |
3891 | [SIOCSIWSPY -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setspy, | 4059 | [SIOCSIWSPY -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setspy, |
3892 | [SIOCGIWSPY -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getspy, | 4060 | [SIOCGIWSPY -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getspy, |
4061 | [SIOCSIWAP -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setwap, | ||
3893 | [SIOCGIWAP -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getwap, | 4062 | [SIOCGIWAP -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getwap, |
3894 | [SIOCSIWESSID -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setessid, | 4063 | [SIOCSIWESSID -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setessid, |
3895 | [SIOCGIWESSID -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getessid, | 4064 | [SIOCGIWESSID -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getessid, |
diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h index f749b50d1088..329e79f6d7b1 100644 --- a/drivers/net/wireless/orinoco.h +++ b/drivers/net/wireless/orinoco.h | |||
@@ -22,6 +22,8 @@ | |||
22 | 22 | ||
23 | #define WIRELESS_SPY // enable iwspy support | 23 | #define WIRELESS_SPY // enable iwspy support |
24 | 24 | ||
25 | #define MAX_SCAN_LEN 4096 | ||
26 | |||
25 | #define ORINOCO_MAX_KEY_SIZE 14 | 27 | #define ORINOCO_MAX_KEY_SIZE 14 |
26 | #define ORINOCO_MAX_KEYS 4 | 28 | #define ORINOCO_MAX_KEYS 4 |
27 | 29 | ||
@@ -48,6 +50,7 @@ struct orinoco_private { | |||
48 | /* driver state */ | 50 | /* driver state */ |
49 | int open; | 51 | int open; |
50 | u16 last_linkstatus; | 52 | u16 last_linkstatus; |
53 | struct work_struct join_work; | ||
51 | 54 | ||
52 | /* Net device stuff */ | 55 | /* Net device stuff */ |
53 | struct net_device *ndev; | 56 | struct net_device *ndev; |
@@ -84,6 +87,8 @@ struct orinoco_private { | |||
84 | int bitratemode; | 87 | int bitratemode; |
85 | char nick[IW_ESSID_MAX_SIZE+1]; | 88 | char nick[IW_ESSID_MAX_SIZE+1]; |
86 | char desired_essid[IW_ESSID_MAX_SIZE+1]; | 89 | char desired_essid[IW_ESSID_MAX_SIZE+1]; |
90 | char desired_bssid[ETH_ALEN]; | ||
91 | int bssid_fixed; | ||
87 | u16 frag_thresh, mwo_robust; | 92 | u16 frag_thresh, mwo_robust; |
88 | u16 channel; | 93 | u16 channel; |
89 | u16 ap_density, rts_thresh; | 94 | u16 ap_density, rts_thresh; |