aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@lst.de>2005-06-18 19:27:51 -0400
committerJeff Garzik <jgarzik@pobox.com>2005-06-27 00:23:55 -0400
commit16739b065f4b0965d975f5c756204c7aa911cd61 (patch)
treeb52614d4654009bcddf61ad44bb7b8cbbfaedc8e /drivers
parent1fab2e8b7a9dd0226e42ad5d3688edd5065bd231 (diff)
[PATCH] orinoco: manual roaming for Symbol and Intersilfirmware
Patch from Pavel Roskin
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/orinoco.c169
-rw-r--r--drivers/net/wireless/orinoco.h5
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 */
1251static 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
1250static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) 1319static 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 */
1550static 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
2703static 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
2596static int orinoco_ioctl_getwap(struct net_device *dev, 2764static 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;