Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 16739b06 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Jeff Garzik
Browse files

[PATCH] orinoco: manual roaming for Symbol and Intersilfirmware

Patch from Pavel Roskin
parent 1fab2e8b
Loading
Loading
Loading
Loading
+169 −0
Original line number Original line Diff line number Diff line
@@ -1247,6 +1247,75 @@ static void print_linkstatus(struct net_device *dev, u16 status)
	       dev->name, s, status);
	       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)
static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
{
{
	struct orinoco_private *priv = netdev_priv(dev);
	struct orinoco_private *priv = netdev_priv(dev);
@@ -1477,6 +1546,36 @@ static int __orinoco_hw_set_bitrate(struct orinoco_private *priv)
	return err;
	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
/* Change the WEP keys and/or the current keys.  Can be called
 * either from __orinoco_hw_setup_wep() or directly from
 * either from __orinoco_hw_setup_wep() or directly from
 * orinoco_ioctl_setiwencode().  In the later case the association
 * orinoco_ioctl_setiwencode().  In the later case the association
@@ -1662,6 +1761,13 @@ static int __orinoco_program_rids(struct net_device *dev)
		}
		}
	}
	}


	/* 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 */
	/* Set the desired ESSID */
	idbuf.len = cpu_to_le16(strlen(priv->desired_essid));
	idbuf.len = cpu_to_le16(strlen(priv->desired_essid));
	memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val));
	memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val));
@@ -2432,6 +2538,7 @@ struct net_device *alloc_orinocodev(int sizeof_card,
				   * before anything else touches the
				   * before anything else touches the
				   * hardware */
				   * hardware */
	INIT_WORK(&priv->reset_work, (void (*)(void *))orinoco_reset, dev);
	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);
	netif_carrier_off(dev);
	priv->last_linkstatus = 0xffff;
	priv->last_linkstatus = 0xffff;
@@ -2593,6 +2700,67 @@ static int orinoco_ioctl_getname(struct net_device *dev,
	return 0;
	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,
static int orinoco_ioctl_getwap(struct net_device *dev,
				struct iw_request_info *info,
				struct iw_request_info *info,
				struct sockaddr *ap_addr,
				struct sockaddr *ap_addr,
@@ -3890,6 +4058,7 @@ static const iw_handler orinoco_handler[] = {
	[SIOCGIWRANGE -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getiwrange,
	[SIOCGIWRANGE -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getiwrange,
	[SIOCSIWSPY   -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setspy,
	[SIOCSIWSPY   -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setspy,
	[SIOCGIWSPY   -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getspy,
	[SIOCGIWSPY   -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getspy,
	[SIOCSIWAP    -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setwap,
	[SIOCGIWAP    -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getwap,
	[SIOCGIWAP    -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getwap,
	[SIOCSIWESSID -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setessid,
	[SIOCSIWESSID -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setessid,
	[SIOCGIWESSID -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getessid,
	[SIOCGIWESSID -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getessid,
+5 −0
Original line number Original line Diff line number Diff line
@@ -22,6 +22,8 @@


#define WIRELESS_SPY		// enable iwspy support
#define WIRELESS_SPY		// enable iwspy support


#define MAX_SCAN_LEN		4096

#define ORINOCO_MAX_KEY_SIZE	14
#define ORINOCO_MAX_KEY_SIZE	14
#define ORINOCO_MAX_KEYS	4
#define ORINOCO_MAX_KEYS	4


@@ -48,6 +50,7 @@ struct orinoco_private {
	/* driver state */
	/* driver state */
	int open;
	int open;
	u16 last_linkstatus;
	u16 last_linkstatus;
	struct work_struct join_work;


	/* Net device stuff */
	/* Net device stuff */
	struct net_device *ndev;
	struct net_device *ndev;
@@ -84,6 +87,8 @@ struct orinoco_private {
	int bitratemode;
	int bitratemode;
 	char nick[IW_ESSID_MAX_SIZE+1];
 	char nick[IW_ESSID_MAX_SIZE+1];
	char desired_essid[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 frag_thresh, mwo_robust;
	u16 channel;
	u16 channel;
	u16 ap_density, rts_thresh;
	u16 ap_density, rts_thresh;