* Re: [PATCH 8/12] orinoco: manual roaming for Symbol and Intersilfirmware
[not found] <20050514153041.GI3643@lst.de>
@ 2005-05-16 1:06 ` David Gibson
0 siblings, 0 replies; only message in thread
From: David Gibson @ 2005-05-16 1:06 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: jgarzik, proski, netdev
On Sat, May 14, 2005 at 05:30:41PM +0200, Christoph Hellwig wrote:
> Patch from Pavel Roskin
Acked-by: David Gibson <hermes@gibson.dropbear.id.au>
> Index: linux-2.6/drivers/net/wireless/orinoco.h
> ===================================================================
> +++ linux-2.6/drivers/net/wireless/orinoco.h 2005-05-14 17:03:00.000000000 +0200
> @@ -22,6 +22,8 @@
>
> #define WIRELESS_SPY // enable iwspy support
>
> +#define MAX_SCAN_LEN 4096
> +
> #define ORINOCO_MAX_KEY_SIZE 14
> #define ORINOCO_MAX_KEYS 4
>
> @@ -48,6 +50,7 @@
> /* driver state */
> int open;
> u16 last_linkstatus;
> + struct work_struct join_work;
>
> /* Net device stuff */
> struct net_device *ndev;
> @@ -84,6 +87,8 @@
> int bitratemode;
> char nick[IW_ESSID_MAX_SIZE+1];
> char desired_essid[IW_ESSID_MAX_SIZE+1];
> + char desired_bssid[ETH_ALEN];
> + int bssid_fixed;
> u16 frag_thresh, mwo_robust;
> u16 channel;
> u16 ap_density, rts_thresh;
> Index: linux-2.6/drivers/net/wireless/orinoco.c
> ===================================================================
> +++ linux-2.6/drivers/net/wireless/orinoco.c 2005-05-14 17:03:00.000000000 +0200
> @@ -1247,6 +1247,75 @@
> dev->name, s, status);
> }
>
> +/* Search scan results for requested BSSID, join it if found */
> +static void orinoco_join_ap(struct net_device *dev)
> +{
> + struct orinoco_private *priv = netdev_priv(dev);
> + struct hermes *hw = &priv->hw;
> + int err;
> + unsigned long flags;
> + struct join_req {
> + u8 bssid[ETH_ALEN];
> + u16 channel;
> + } __attribute__ ((packed)) req;
> + const int atom_len = offsetof(struct prism2_scan_apinfo, atim);
> + struct prism2_scan_apinfo *atom;
> + int offset = 4;
> + u8 *buf;
> + u16 len;
> +
> + /* Allocate buffer for scan results */
> + buf = kmalloc(MAX_SCAN_LEN, GFP_KERNEL);
> + if (! buf)
> + return;
> +
> + if (orinoco_lock(priv, &flags) != 0)
> + goto out;
> +
> + /* Sanity checks in case user changed something in the meantime */
> + if (! priv->bssid_fixed)
> + goto out;
> +
> + if (strlen(priv->desired_essid) == 0)
> + goto out;
> +
> + /* Read scan results from the firmware */
> + err = hermes_read_ltv(hw, USER_BAP,
> + HERMES_RID_SCANRESULTSTABLE,
> + MAX_SCAN_LEN, &len, buf);
> + if (err) {
> + printk(KERN_ERR "%s: Cannot read scan results\n",
> + dev->name);
> + goto out;
> + }
> +
> + len = HERMES_RECLEN_TO_BYTES(len);
> +
> + /* Go through the scan results looking for the channel of the AP
> + * we were requested to join */
> + for (; offset + atom_len <= len; offset += atom_len) {
> + atom = (struct prism2_scan_apinfo *) (buf + offset);
> + if (memcmp(&atom->bssid, priv->desired_bssid, ETH_ALEN) == 0)
> + goto found;
> + }
> +
> + DEBUG(1, "%s: Requested AP not found in scan results\n",
> + dev->name);
> + goto out;
> +
> + found:
> + memcpy(req.bssid, priv->desired_bssid, ETH_ALEN);
> + req.channel = atom->channel; /* both are little-endian */
> + err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFJOINREQUEST,
> + &req);
> + if (err)
> + printk(KERN_ERR "%s: Error issuing join request\n", dev->name);
> +
> + out:
> + kfree(buf);
> + orinoco_unlock(priv, &flags);
> +}
> +
> static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
> {
> struct orinoco_private *priv = netdev_priv(dev);
> @@ -1477,6 +1546,36 @@
> return err;
> }
>
> +/* Set fixed AP address */
> +static int __orinoco_hw_set_wap(struct orinoco_private *priv)
> +{
> + int roaming_flag;
> + int err = 0;
> + hermes_t *hw = &priv->hw;
> +
> + switch (priv->firmware_type) {
> + case FIRMWARE_TYPE_AGERE:
> + /* not supported */
> + break;
> + case FIRMWARE_TYPE_INTERSIL:
> + if (priv->bssid_fixed)
> + roaming_flag = 2;
> + else
> + roaming_flag = 1;
> +
> + err = hermes_write_wordrec(hw, USER_BAP,
> + HERMES_RID_CNFROAMINGMODE,
> + roaming_flag);
> + break;
> + case FIRMWARE_TYPE_SYMBOL:
> + err = HERMES_WRITE_RECORD(hw, USER_BAP,
> + HERMES_RID_CNFMANDATORYBSSID_SYMBOL,
> + &priv->desired_bssid);
> + break;
> + }
> + return err;
> +}
> +
> /* Change the WEP keys and/or the current keys. Can be called
> * either from __orinoco_hw_setup_wep() or directly from
> * orinoco_ioctl_setiwencode(). In the later case the association
> @@ -1662,6 +1761,13 @@
> }
> }
>
> + /* Set the desired BSSID */
> + err = __orinoco_hw_set_wap(priv);
> + if (err) {
> + printk(KERN_ERR "%s: Error %d setting AP address\n",
> + dev->name, err);
> + return err;
> + }
> /* Set the desired ESSID */
> idbuf.len = cpu_to_le16(strlen(priv->desired_essid));
> memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val));
> @@ -2432,6 +2538,7 @@
> * before anything else touches the
> * hardware */
> INIT_WORK(&priv->reset_work, (void (*)(void *))orinoco_reset, dev);
> + INIT_WORK(&priv->join_work, (void (*)(void *))orinoco_join_ap, dev);
>
> netif_carrier_off(dev);
> priv->last_linkstatus = 0xffff;
> @@ -2593,6 +2700,67 @@
> return 0;
> }
>
> +static int orinoco_ioctl_setwap(struct net_device *dev,
> + struct iw_request_info *info,
> + struct sockaddr *ap_addr,
> + char *extra)
> +{
> + struct orinoco_private *priv = netdev_priv(dev);
> + int err = -EINPROGRESS; /* Call commit handler */
> + unsigned long flags;
> + static const u8 off_addr[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
> + static const u8 any_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
> +
> + if (orinoco_lock(priv, &flags) != 0)
> + return -EBUSY;
> +
> + /* Enable automatic roaming - no sanity checks are needed */
> + if (memcmp(&ap_addr->sa_data, off_addr, ETH_ALEN) == 0 ||
> + memcmp(&ap_addr->sa_data, any_addr, ETH_ALEN) == 0) {
> + priv->bssid_fixed = 0;
> + memset(priv->desired_bssid, 0, ETH_ALEN);
> +
> + /* "off" means keep existing connection */
> + if (ap_addr->sa_data[0] == 0) {
> + __orinoco_hw_set_wap(priv);
> + err = 0;
> + }
> + goto out;
> + }
> +
> + if (priv->firmware_type == FIRMWARE_TYPE_AGERE) {
> + printk(KERN_WARNING "%s: Lucent/Agere firmware doesn't "
> + "support manual roaming\n",
> + dev->name);
> + err = -EOPNOTSUPP;
> + goto out;
> + }
> +
> + if (priv->iw_mode != IW_MODE_INFRA) {
> + printk(KERN_WARNING "%s: Manual roaming supported only in "
> + "managed mode\n", dev->name);
> + err = -EOPNOTSUPP;
> + goto out;
> + }
> +
> + /* Intersil firmware hangs without Desired ESSID */
> + if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL &&
> + strlen(priv->desired_essid) == 0) {
> + printk(KERN_WARNING "%s: Desired ESSID must be set for "
> + "manual roaming\n", dev->name);
> + err = -EOPNOTSUPP;
> + goto out;
> + }
> +
> + /* Finally, enable manual roaming */
> + priv->bssid_fixed = 1;
> + memcpy(priv->desired_bssid, &ap_addr->sa_data, ETH_ALEN);
> +
> + out:
> + orinoco_unlock(priv, &flags);
> + return err;
> +}
> +
> static int orinoco_ioctl_getwap(struct net_device *dev,
> struct iw_request_info *info,
> struct sockaddr *ap_addr,
> @@ -3890,6 +4058,7 @@
> [SIOCGIWRANGE -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getiwrange,
> [SIOCSIWSPY -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setspy,
> [SIOCGIWSPY -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getspy,
> + [SIOCSIWAP -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setwap,
> [SIOCGIWAP -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getwap,
> [SIOCSIWESSID -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setessid,
> [SIOCGIWESSID -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getessid,
>
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/people/dgibson
^ permalink raw reply [flat|nested] only message in thread