diff mbox

[RFC,1/4] orinoco: add cfg80211 connect and disconnect

Message ID 1249504372-17063-2-git-send-email-kilroyd@googlemail.com (mailing list archive)
State Not Applicable, archived
Headers show

Commit Message

Dave Aug. 5, 2009, 8:32 p.m. UTC
Basic station mode support.

Signed-off-by: David Kilroy <kilroyd@googlemail.com>
---
 drivers/net/wireless/orinoco/cfg.c |  236 ++++++++++++++++++++++++++++++++++++
 1 files changed, 236 insertions(+), 0 deletions(-)

Comments

Johannes Berg Aug. 6, 2009, 7:47 a.m. UTC | #1
On Wed, 2009-08-05 at 21:32 +0100, David Kilroy wrote:

> +static int __orinoco_connect(struct wiphy *wiphy,
> +			     struct ieee80211_channel *channel,
> +			     const u8 *bssid, const u8 *ssid,
> +			     u8 ssid_len)
> +{
> +	struct orinoco_private *priv = wiphy_priv(wiphy);
> +

> +		/* Intersil firmware hangs if you try to roam manually
> +		 * without an ESSID set. */
> +		if ((priv->firmware_type == FIRMWARE_TYPE_INTERSIL) &&
> +		    (priv->desired_essid[0] == 0)) {

This is a wrong way to check for an empty SSID, since \0 is a valid byte
in the SSID. You should check for ssid_len == 0 instead.

However, it's also not necessary, cfg80211 will never call this function
without an SSID. So you can even just BUG_ON(!ssid_len); or something :)

> +	switch (sme->auth_type) {
> +	case NL80211_AUTHTYPE_OPEN_SYSTEM:
> +		priv->wep_restrict = 0;
> +		break;
> +	case NL80211_AUTHTYPE_SHARED_KEY:
> +		priv->wep_restrict = 1;
> +		break;
> +	/* Ignore all other settings */
> +	default:
> +		break;

I think you should return -EINVAL for other values.

> +	}
> +
> +	switch (sme->crypto.cipher_group) {
> +	case WLAN_CIPHER_SUITE_TKIP:
> +		encode_alg = ORINOCO_ALG_TKIP;
> +		break;
> +
> +	case WLAN_CIPHER_SUITE_WEP40:
> +	case WLAN_CIPHER_SUITE_WEP104:
> +		encode_alg = ORINOCO_ALG_WEP;
> +		break;
> +
> +	default:
> +		encode_alg = ORINOCO_ALG_NONE;
> +	}

Same here, I guess, although that can't really happen since you only
advertised these ones.

Other than that, as noted in my reply to 0/5, this won't work properly
since you don't ever call cfg80211_connect_result().

johannes
diff mbox

Patch

diff --git a/drivers/net/wireless/orinoco/cfg.c b/drivers/net/wireless/orinoco/cfg.c
index 1a87d3a..56bd4f1 100644
--- a/drivers/net/wireless/orinoco/cfg.c
+++ b/drivers/net/wireless/orinoco/cfg.c
@@ -156,7 +156,243 @@  static int orinoco_scan(struct wiphy *wiphy, struct net_device *dev,
 	return err;
 }
 
+/* Helper to ensure all keys are valid for the current encoding
+   algorithm */
+static void orinoco_set_encoding(struct orinoco_private *priv,
+				 enum orinoco_alg encoding)
+{
+	if (priv->encode_alg &&
+	    priv->encode_alg != encoding) {
+		int i;
+
+		for (i = 0; i < ORINOCO_MAX_KEYS; i++) {
+			kfree(priv->keys[i].key);
+			kfree(priv->keys[i].seq);
+			priv->keys[i].key = NULL;
+			priv->keys[i].seq = NULL;
+			priv->keys[i].key_len = 0;
+			priv->keys[i].seq_len = 0;
+			priv->keys[i].cipher = 0;
+		}
+
+		if (priv->encode_alg == ORINOCO_ALG_TKIP &&
+		    priv->has_wpa) {
+			(void) orinoco_clear_tkip_key(priv, i);
+			(void) orinoco_clear_tkip_key(priv, i);
+			(void) orinoco_clear_tkip_key(priv, i);
+			(void) orinoco_clear_tkip_key(priv, i);
+		} else if (priv->encode_alg == ORINOCO_ALG_WEP)
+			__orinoco_hw_setup_wepkeys(priv);
+	}
+	priv->encode_alg = encoding;
+}
+
+/* Helper routine to record keys
+ * Do not call from interrupt context */
+static int orinoco_set_key(struct orinoco_private *priv, int index,
+			   enum orinoco_alg alg, const u8 *key, int key_len,
+			   const u8 *seq, int seq_len)
+{
+	kzfree(priv->keys[index].key);
+	kzfree(priv->keys[index].seq);
+
+	if (key_len) {
+		priv->keys[index].key = kzalloc(key_len, GFP_KERNEL);
+		if (!priv->keys[index].key)
+			goto nomem;
+	} else
+		priv->keys[index].key = NULL;
+
+	if (seq_len) {
+		priv->keys[index].seq = kzalloc(seq_len, GFP_KERNEL);
+		if (!priv->keys[index].seq)
+			goto free_key;
+	} else
+		priv->keys[index].seq = NULL;
+
+	priv->keys[index].key_len = key_len;
+	priv->keys[index].seq_len = seq_len;
+
+	if (key_len)
+		memcpy(priv->keys[index].key, key, key_len);
+	if (seq_len)
+		memcpy(priv->keys[index].seq, seq, seq_len);
+
+
+	switch (alg) {
+	case ORINOCO_ALG_TKIP:
+		priv->keys[index].cipher = WLAN_CIPHER_SUITE_TKIP;
+		break;
+
+	case ORINOCO_ALG_WEP:
+		priv->keys[index].cipher = (key_len > SMALL_KEY_SIZE) ?
+			WLAN_CIPHER_SUITE_WEP104 : WLAN_CIPHER_SUITE_WEP40;
+		break;
+
+	case ORINOCO_ALG_NONE:
+	default:
+		priv->keys[index].cipher = 0;
+		break;
+	}
+
+	return 0;
+
+free_key:
+	kfree(priv->keys[index].key);
+	priv->keys[index].key = NULL;
+
+nomem:
+	priv->keys[index].key_len = 0;
+	priv->keys[index].seq_len = 0;
+	priv->keys[index].cipher = 0;
+
+	return -ENOMEM;
+}
+
+/* Setup channel, SSID and BSSID */
+static int __orinoco_connect(struct wiphy *wiphy,
+			     struct ieee80211_channel *channel,
+			     const u8 *bssid, const u8 *ssid,
+			     u8 ssid_len)
+{
+	struct orinoco_private *priv = wiphy_priv(wiphy);
+
+	if (channel) {
+		int chan;
+
+		chan = ieee80211_freq_to_dsss_chan(channel->center_freq);
+
+		if ((chan > 0) && (chan < NUM_CHANNELS) &&
+		    (priv->channel_mask & (1 << chan)))
+			priv->channel = chan;
+	}
+
+	memset(priv->desired_essid, 0, sizeof(priv->desired_essid));
+	if (ssid)
+		memcpy(priv->desired_essid, ssid, ssid_len);
+
+	if (bssid) {
+		memcpy(priv->desired_bssid, bssid, ETH_ALEN);
+
+		/* Intersil firmware hangs if you try to roam manually
+		 * without an ESSID set. */
+		if ((priv->firmware_type == FIRMWARE_TYPE_INTERSIL) &&
+		    (priv->desired_essid[0] == 0)) {
+			printk(KERN_WARNING "%s: Desired SSID must be set for "
+			       "manual roaming\n", wiphy_name(wiphy));
+			return -EOPNOTSUPP;
+		}
+
+		priv->bssid_fixed = 1;
+	} else {
+		memset(priv->desired_bssid, 0, ETH_ALEN);
+		priv->bssid_fixed = 0;
+	}
+
+	return 0;
+}
+
+static int orinoco_connect(struct wiphy *wiphy, struct net_device *dev,
+			   struct cfg80211_connect_params *sme)
+{
+	struct orinoco_private *priv = wiphy_priv(wiphy);
+	enum orinoco_alg encode_alg;
+	unsigned long lock;
+	int err;
+
+	if (orinoco_lock(priv, &lock) != 0)
+		return -EBUSY;
+
+	/* Setup the requested parameters in priv. If the card is not
+	 * capable, then the driver will just ignore the settings that
+	 * it can't do. */
+	err = __orinoco_connect(wiphy, sme->channel, sme->bssid,
+				sme->ssid, sme->ssid_len);
+	if (err)
+		goto out;
+
+	switch (sme->auth_type) {
+	case NL80211_AUTHTYPE_OPEN_SYSTEM:
+		priv->wep_restrict = 0;
+		break;
+	case NL80211_AUTHTYPE_SHARED_KEY:
+		priv->wep_restrict = 1;
+		break;
+	/* Ignore all other settings */
+	default:
+		break;
+	}
+
+	switch (sme->crypto.cipher_group) {
+	case WLAN_CIPHER_SUITE_TKIP:
+		encode_alg = ORINOCO_ALG_TKIP;
+		break;
+
+	case WLAN_CIPHER_SUITE_WEP40:
+	case WLAN_CIPHER_SUITE_WEP104:
+		encode_alg = ORINOCO_ALG_WEP;
+		break;
+
+	default:
+		encode_alg = ORINOCO_ALG_NONE;
+	}
+
+	orinoco_set_encoding(priv, encode_alg);
+
+	/* What are we supposed to do with multiple AKM suites? */
+	if (sme->crypto.n_akm_suites > 0)
+		priv->key_mgmt = sme->crypto.akm_suites[0];
+
+	/* Finally, set WEP key */
+	if (encode_alg == ORINOCO_ALG_WEP) {
+		err = orinoco_set_key(priv, sme->key_idx, encode_alg,
+				      &sme->key[0], sme->key_len, NULL, 0);
+		if (err)
+			goto out;
+
+		priv->tx_key = sme->key_idx;
+	}
+
+	err = orinoco_commit(priv);
+
+out:
+	orinoco_unlock(priv, &lock);
+
+	return err;
+}
+
+static int orinoco_disconnect(struct wiphy *wiphy, struct net_device *dev,
+			      u16 reason_code)
+{
+	struct orinoco_private *priv = wiphy_priv(wiphy);
+	unsigned long lock;
+	u8 addr[ETH_ALEN];
+	int err;
+
+	if (orinoco_lock(priv, &lock) != 0)
+		return -EBUSY;
+
+	memset(priv->desired_bssid, 0, ETH_ALEN);
+	memset(priv->desired_essid, 0, sizeof(priv->desired_essid));
+
+	err = orinoco_hw_get_current_bssid(priv, &addr[0]);
+
+	if (!err) {
+		err = orinoco_hw_disassociate(priv, &addr[0],
+					      reason_code);
+	}
+
+	if (err)
+		err = orinoco_commit(priv);
+
+	orinoco_unlock(priv, &lock);
+
+	return err;
+}
+
 const struct cfg80211_ops orinoco_cfg_ops = {
 	.change_virtual_intf = orinoco_change_vif,
 	.scan = orinoco_scan,
+	.connect = orinoco_connect,
+	.disconnect = orinoco_disconnect,
 };