diff options
Diffstat (limited to 'drivers/net/wireless/rndis_wlan.c')
-rw-r--r-- | drivers/net/wireless/rndis_wlan.c | 1108 |
1 files changed, 492 insertions, 616 deletions
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index bebf735cd4b..3bec3dbd345 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -2,7 +2,7 @@ * Driver for RNDIS based wireless USB devices. * * Copyright (C) 2007 by Bjorge Dijkstra <bjd@jooz.net> - * Copyright (C) 2008 by Jussi Kivilinna <jussi.kivilinna@mbnet.fi> + * Copyright (C) 2008-2009 by Jussi Kivilinna <jussi.kivilinna@mbnet.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -42,6 +42,7 @@ #include <linux/ctype.h> #include <linux/spinlock.h> #include <net/iw_handler.h> +#include <net/cfg80211.h> #include <linux/usb/usbnet.h> #include <linux/usb/rndis_host.h> @@ -156,43 +157,55 @@ MODULE_PARM_DESC(workaround_interval, #define NDIS_802_11_LENGTH_RATES_EX 16 enum ndis_80211_net_type { - ndis_80211_type_freq_hop, - ndis_80211_type_direct_seq, - ndis_80211_type_ofdm_a, - ndis_80211_type_ofdm_g + NDIS_80211_TYPE_FREQ_HOP, + NDIS_80211_TYPE_DIRECT_SEQ, + NDIS_80211_TYPE_OFDM_A, + NDIS_80211_TYPE_OFDM_G }; enum ndis_80211_net_infra { - ndis_80211_infra_adhoc, - ndis_80211_infra_infra, - ndis_80211_infra_auto_unknown + NDIS_80211_INFRA_ADHOC, + NDIS_80211_INFRA_INFRA, + NDIS_80211_INFRA_AUTO_UNKNOWN }; enum ndis_80211_auth_mode { - ndis_80211_auth_open, - ndis_80211_auth_shared, - ndis_80211_auth_auto_switch, - ndis_80211_auth_wpa, - ndis_80211_auth_wpa_psk, - ndis_80211_auth_wpa_none, - ndis_80211_auth_wpa2, - ndis_80211_auth_wpa2_psk + NDIS_80211_AUTH_OPEN, + NDIS_80211_AUTH_SHARED, + NDIS_80211_AUTH_AUTO_SWITCH, + NDIS_80211_AUTH_WPA, + NDIS_80211_AUTH_WPA_PSK, + NDIS_80211_AUTH_WPA_NONE, + NDIS_80211_AUTH_WPA2, + NDIS_80211_AUTH_WPA2_PSK }; enum ndis_80211_encr_status { - ndis_80211_encr_wep_enabled, - ndis_80211_encr_disabled, - ndis_80211_encr_wep_key_absent, - ndis_80211_encr_not_supported, - ndis_80211_encr_tkip_enabled, - ndis_80211_encr_tkip_key_absent, - ndis_80211_encr_ccmp_enabled, - ndis_80211_encr_ccmp_key_absent + NDIS_80211_ENCR_WEP_ENABLED, + NDIS_80211_ENCR_DISABLED, + NDIS_80211_ENCR_WEP_KEY_ABSENT, + NDIS_80211_ENCR_NOT_SUPPORTED, + NDIS_80211_ENCR_TKIP_ENABLED, + NDIS_80211_ENCR_TKIP_KEY_ABSENT, + NDIS_80211_ENCR_CCMP_ENABLED, + NDIS_80211_ENCR_CCMP_KEY_ABSENT }; enum ndis_80211_priv_filter { - ndis_80211_priv_accept_all, - ndis_80211_priv_8021x_wep + NDIS_80211_PRIV_ACCEPT_ALL, + NDIS_80211_PRIV_8021X_WEP +}; + +enum ndis_80211_addkey_bits { + NDIS_80211_ADDKEY_8021X_AUTH = cpu_to_le32(1 << 28), + NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ = cpu_to_le32(1 << 29), + NDIS_80211_ADDKEY_PAIRWISE_KEY = cpu_to_le32(1 << 30), + NDIS_80211_ADDKEY_TRANSMIT_KEY = cpu_to_le32(1 << 31) +}; + +enum ndis_80211_addwep_bits { + NDIS_80211_ADDWEP_PERCLIENT_KEY = cpu_to_le32(1 << 30), + NDIS_80211_ADDWEP_TRANSMIT_KEY = cpu_to_le32(1 << 31) }; struct ndis_80211_ssid { @@ -308,7 +321,6 @@ enum wpa_key_mgmt { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE, #define CAP_MODE_80211B 2 #define CAP_MODE_80211G 4 #define CAP_MODE_MASK 7 -#define CAP_SUPPORT_TXPOWER 8 #define WORK_LINK_UP (1<<0) #define WORK_LINK_DOWN (1<<1) @@ -316,25 +328,61 @@ enum wpa_key_mgmt { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE, #define COMMAND_BUFFER_SIZE (CONTROL_BUFFER_SIZE + sizeof(struct rndis_set)) -/* RNDIS device private data */ -struct rndis_wext_private { - char name[32]; +static const struct ieee80211_channel rndis_channels[] = { + { .center_freq = 2412 }, + { .center_freq = 2417 }, + { .center_freq = 2422 }, + { .center_freq = 2427 }, + { .center_freq = 2432 }, + { .center_freq = 2437 }, + { .center_freq = 2442 }, + { .center_freq = 2447 }, + { .center_freq = 2452 }, + { .center_freq = 2457 }, + { .center_freq = 2462 }, + { .center_freq = 2467 }, + { .center_freq = 2472 }, + { .center_freq = 2484 }, +}; +static const struct ieee80211_rate rndis_rates[] = { + { .bitrate = 10 }, + { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 60 }, + { .bitrate = 90 }, + { .bitrate = 120 }, + { .bitrate = 180 }, + { .bitrate = 240 }, + { .bitrate = 360 }, + { .bitrate = 480 }, + { .bitrate = 540 } +}; + +/* RNDIS device private data */ +struct rndis_wlan_private { struct usbnet *usbdev; + struct wireless_dev wdev; + + struct cfg80211_scan_request *scan_request; + struct workqueue_struct *workqueue; struct delayed_work stats_work; + struct delayed_work scan_work; struct work_struct work; struct mutex command_lock; spinlock_t stats_lock; unsigned long work_pending; + struct ieee80211_supported_band band; + struct ieee80211_channel channels[ARRAY_SIZE(rndis_channels)]; + struct ieee80211_rate rates[ARRAY_SIZE(rndis_rates)]; + struct iw_statistics iwstats; struct iw_statistics privstats; - int nick_len; - char nick[32]; - int caps; int multicast_size; @@ -357,6 +405,7 @@ struct rndis_wext_private { int encr_tx_key_index; char encr_keys[4][32]; int encr_key_len[4]; + char encr_key_wpa[4]; int wpa_version; int wpa_keymgmt; int wpa_authalg; @@ -368,8 +417,22 @@ struct rndis_wext_private { u8 command_buffer[COMMAND_BUFFER_SIZE]; }; +/* + * cfg80211 ops + */ +static int rndis_change_virtual_intf(struct wiphy *wiphy, int ifindex, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params); + +static int rndis_scan(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_scan_request *request); -static const int rates_80211g[8] = { 6, 9, 12, 18, 24, 36, 48, 54 }; +static struct cfg80211_ops rndis_config_ops = { + .change_virtual_intf = rndis_change_virtual_intf, + .scan = rndis_scan, +}; + +static void *rndis_wiphy_privid = &rndis_wiphy_privid; static const int bcm4320_power_output[4] = { 25, 50, 75, 100 }; @@ -378,13 +441,13 @@ static const unsigned char ffff_bssid[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; -static struct rndis_wext_private *get_rndis_wext_priv(struct usbnet *dev) +static struct rndis_wlan_private *get_rndis_wlan_priv(struct usbnet *dev) { - return (struct rndis_wext_private *)dev->driver_priv; + return (struct rndis_wlan_private *)dev->driver_priv; } -static u32 get_bcm4320_power(struct rndis_wext_private *priv) +static u32 get_bcm4320_power(struct rndis_wlan_private *priv) { return BCM4320_DEFAULT_TXPOWER * bcm4320_power_output[priv->param_power_output] / 100; @@ -417,7 +480,7 @@ static int rndis_error_status(__le32 rndis_status) static int rndis_query_oid(struct usbnet *dev, __le32 oid, void *data, int *len) { - struct rndis_wext_private *priv = get_rndis_wext_priv(dev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(dev); union { void *buf; struct rndis_msg_hdr *header; @@ -463,7 +526,7 @@ static int rndis_query_oid(struct usbnet *dev, __le32 oid, void *data, int *len) static int rndis_set_oid(struct usbnet *dev, __le32 oid, void *data, int len) { - struct rndis_wext_private *priv = get_rndis_wext_priv(dev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(dev); union { void *buf; struct rndis_msg_hdr *header; @@ -584,7 +647,7 @@ static int rndis_set_config_parameter(struct usbnet *dev, char *param, ret = rndis_set_oid(dev, OID_GEN_RNDIS_CONFIG_PARAMETER, infobuf, info_len); if (ret != 0) - devdbg(dev, "setting rndis config paramater failed, %d.", ret); + devdbg(dev, "setting rndis config parameter failed, %d.", ret); kfree(infobuf); return ret; @@ -684,7 +747,7 @@ static int get_essid(struct usbnet *usbdev, struct ndis_80211_ssid *ssid) static int set_essid(struct usbnet *usbdev, struct ndis_80211_ssid *ssid) { - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); int ret; ret = rndis_set_oid(usbdev, OID_802_11_SSID, ssid, sizeof(*ssid)); @@ -731,7 +794,7 @@ static int is_associated(struct usbnet *usbdev) static int disassociate(struct usbnet *usbdev, int reset_ssid) { - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); struct ndis_80211_ssid ssid; int i, ret = 0; @@ -763,7 +826,7 @@ static int disassociate(struct usbnet *usbdev, int reset_ssid) static int set_auth_mode(struct usbnet *usbdev, int wpa_version, int authalg) { - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); __le32 tmp; int auth_mode, ret; @@ -772,23 +835,23 @@ static int set_auth_mode(struct usbnet *usbdev, int wpa_version, int authalg) if (wpa_version & IW_AUTH_WPA_VERSION_WPA2) { if (priv->wpa_keymgmt & IW_AUTH_KEY_MGMT_802_1X) - auth_mode = ndis_80211_auth_wpa2; + auth_mode = NDIS_80211_AUTH_WPA2; else - auth_mode = ndis_80211_auth_wpa2_psk; + auth_mode = NDIS_80211_AUTH_WPA2_PSK; } else if (wpa_version & IW_AUTH_WPA_VERSION_WPA) { if (priv->wpa_keymgmt & IW_AUTH_KEY_MGMT_802_1X) - auth_mode = ndis_80211_auth_wpa; + auth_mode = NDIS_80211_AUTH_WPA; else if (priv->wpa_keymgmt & IW_AUTH_KEY_MGMT_PSK) - auth_mode = ndis_80211_auth_wpa_psk; + auth_mode = NDIS_80211_AUTH_WPA_PSK; else - auth_mode = ndis_80211_auth_wpa_none; + auth_mode = NDIS_80211_AUTH_WPA_NONE; } else if (authalg & IW_AUTH_ALG_SHARED_KEY) { if (authalg & IW_AUTH_ALG_OPEN_SYSTEM) - auth_mode = ndis_80211_auth_auto_switch; + auth_mode = NDIS_80211_AUTH_AUTO_SWITCH; else - auth_mode = ndis_80211_auth_shared; + auth_mode = NDIS_80211_AUTH_SHARED; } else - auth_mode = ndis_80211_auth_open; + auth_mode = NDIS_80211_AUTH_OPEN; tmp = cpu_to_le32(auth_mode); ret = rndis_set_oid(usbdev, OID_802_11_AUTHENTICATION_MODE, &tmp, @@ -806,16 +869,16 @@ static int set_auth_mode(struct usbnet *usbdev, int wpa_version, int authalg) static int set_priv_filter(struct usbnet *usbdev) { - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); __le32 tmp; devdbg(usbdev, "set_priv_filter: wpa_version=0x%x", priv->wpa_version); if (priv->wpa_version & IW_AUTH_WPA_VERSION_WPA2 || priv->wpa_version & IW_AUTH_WPA_VERSION_WPA) - tmp = cpu_to_le32(ndis_80211_priv_8021x_wep); + tmp = cpu_to_le32(NDIS_80211_PRIV_8021X_WEP); else - tmp = cpu_to_le32(ndis_80211_priv_accept_all); + tmp = cpu_to_le32(NDIS_80211_PRIV_ACCEPT_ALL); return rndis_set_oid(usbdev, OID_802_11_PRIVACY_FILTER, &tmp, sizeof(tmp)); @@ -824,7 +887,7 @@ static int set_priv_filter(struct usbnet *usbdev) static int set_encr_mode(struct usbnet *usbdev, int pairwise, int groupwise) { - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); __le32 tmp; int encr_mode, ret; @@ -833,18 +896,18 @@ static int set_encr_mode(struct usbnet *usbdev, int pairwise, int groupwise) groupwise); if (pairwise & IW_AUTH_CIPHER_CCMP) - encr_mode = ndis_80211_encr_ccmp_enabled; + encr_mode = NDIS_80211_ENCR_CCMP_ENABLED; else if (pairwise & IW_AUTH_CIPHER_TKIP) - encr_mode = ndis_80211_encr_tkip_enabled; + encr_mode = NDIS_80211_ENCR_TKIP_ENABLED; else if (pairwise & (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104)) - encr_mode = ndis_80211_encr_wep_enabled; + encr_mode = NDIS_80211_ENCR_WEP_ENABLED; else if (groupwise & IW_AUTH_CIPHER_CCMP) - encr_mode = ndis_80211_encr_ccmp_enabled; + encr_mode = NDIS_80211_ENCR_CCMP_ENABLED; else if (groupwise & IW_AUTH_CIPHER_TKIP) - encr_mode = ndis_80211_encr_tkip_enabled; + encr_mode = NDIS_80211_ENCR_TKIP_ENABLED; else - encr_mode = ndis_80211_encr_disabled; + encr_mode = NDIS_80211_ENCR_DISABLED; tmp = cpu_to_le32(encr_mode); ret = rndis_set_oid(usbdev, OID_802_11_ENCRYPTION_STATUS, &tmp, @@ -862,7 +925,7 @@ static int set_encr_mode(struct usbnet *usbdev, int pairwise, int groupwise) static int set_assoc_params(struct usbnet *usbdev) { - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); set_auth_mode(usbdev, priv->wpa_version, priv->wpa_authalg); set_priv_filter(usbdev); @@ -874,7 +937,7 @@ static int set_assoc_params(struct usbnet *usbdev) static int set_infra_mode(struct usbnet *usbdev, int mode) { - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); __le32 tmp; int ret, i; @@ -894,7 +957,7 @@ static int set_infra_mode(struct usbnet *usbdev, int mode) if (priv->wpa_keymgmt == 0 || priv->wpa_keymgmt == IW_AUTH_KEY_MGMT_802_1X) { for (i = 0; i < 4; i++) { - if (priv->encr_key_len[i] > 0) + if (priv->encr_key_len[i] > 0 && !priv->encr_key_wpa[i]) add_wep_key(usbdev, priv->encr_keys[i], priv->encr_key_len[i], i); } @@ -907,12 +970,12 @@ static int set_infra_mode(struct usbnet *usbdev, int mode) static void set_default_iw_params(struct usbnet *usbdev) { - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); priv->wpa_keymgmt = 0; priv->wpa_version = 0; - set_infra_mode(usbdev, ndis_80211_infra_infra); + set_infra_mode(usbdev, NDIS_80211_INFRA_INFRA); set_auth_mode(usbdev, IW_AUTH_WPA_VERSION_DISABLED, IW_AUTH_ALG_OPEN_SYSTEM); set_priv_filter(usbdev); @@ -933,7 +996,7 @@ static int deauthenticate(struct usbnet *usbdev) /* index must be 0 - N, as per NDIS */ static int add_wep_key(struct usbnet *usbdev, char *key, int key_len, int index) { - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); struct ndis_80211_wep_key ndis_key; int ret; @@ -948,7 +1011,7 @@ static int add_wep_key(struct usbnet *usbdev, char *key, int key_len, int index) memcpy(&ndis_key.material, key, key_len); if (index == priv->encr_tx_key_index) { - ndis_key.index |= cpu_to_le32(1 << 31); + ndis_key.index |= NDIS_80211_ADDWEP_TRANSMIT_KEY; ret = set_encr_mode(usbdev, IW_AUTH_CIPHER_WEP104, IW_AUTH_CIPHER_NONE); if (ret) @@ -965,16 +1028,85 @@ static int add_wep_key(struct usbnet *usbdev, char *key, int key_len, int index) } priv->encr_key_len[index] = key_len; + priv->encr_key_wpa[index] = 0; memcpy(&priv->encr_keys[index], key, key_len); return 0; } +static int add_wpa_key(struct usbnet *usbdev, const u8 *key, int key_len, + int index, const struct sockaddr *addr, + const u8 *rx_seq, int alg, int flags) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + struct ndis_80211_key ndis_key; + int ret; + + if (index < 0 || index >= 4) + return -EINVAL; + if (key_len > sizeof(ndis_key.material) || key_len < 0) + return -EINVAL; + if ((flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ) && !rx_seq) + return -EINVAL; + if ((flags & NDIS_80211_ADDKEY_PAIRWISE_KEY) && !addr) + return -EINVAL; + + devdbg(usbdev, "add_wpa_key(%i): flags:%i%i%i", index, + !!(flags & NDIS_80211_ADDKEY_TRANSMIT_KEY), + !!(flags & NDIS_80211_ADDKEY_PAIRWISE_KEY), + !!(flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ)); + + memset(&ndis_key, 0, sizeof(ndis_key)); + + ndis_key.size = cpu_to_le32(sizeof(ndis_key) - + sizeof(ndis_key.material) + key_len); + ndis_key.length = cpu_to_le32(key_len); + ndis_key.index = cpu_to_le32(index) | flags; + + if (alg == IW_ENCODE_ALG_TKIP && key_len == 32) { + /* wpa_supplicant gives us the Michael MIC RX/TX keys in + * different order than NDIS spec, so swap the order here. */ + memcpy(ndis_key.material, key, 16); + memcpy(ndis_key.material + 16, key + 24, 8); + memcpy(ndis_key.material + 24, key + 16, 8); + } else + memcpy(ndis_key.material, key, key_len); + + if (flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ) + memcpy(ndis_key.rsc, rx_seq, 6); + + if (flags & NDIS_80211_ADDKEY_PAIRWISE_KEY) { + /* pairwise key */ + memcpy(ndis_key.bssid, addr->sa_data, ETH_ALEN); + } else { + /* group key */ + if (priv->infra_mode == NDIS_80211_INFRA_ADHOC) + memset(ndis_key.bssid, 0xff, ETH_ALEN); + else + get_bssid(usbdev, ndis_key.bssid); + } + + ret = rndis_set_oid(usbdev, OID_802_11_ADD_KEY, &ndis_key, + le32_to_cpu(ndis_key.size)); + devdbg(usbdev, "add_wpa_key: OID_802_11_ADD_KEY -> %08X", ret); + if (ret != 0) + return ret; + + priv->encr_key_len[index] = key_len; + priv->encr_key_wpa[index] = 1; + + if (flags & NDIS_80211_ADDKEY_TRANSMIT_KEY) + priv->encr_tx_key_index = index; + + return 0; +} + + /* remove_key is for both wep and wpa */ static int remove_key(struct usbnet *usbdev, int index, u8 bssid[ETH_ALEN]) { - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); struct ndis_80211_remove_key remove_key; __le32 keyindex; int ret; @@ -983,6 +1115,7 @@ static int remove_key(struct usbnet *usbdev, int index, u8 bssid[ETH_ALEN]) return 0; priv->encr_key_len[index] = 0; + priv->encr_key_wpa[index] = 0; memset(&priv->encr_keys[index], 0, sizeof(priv->encr_keys[index])); if (priv->wpa_cipher_pair == IW_AUTH_CIPHER_TKIP || @@ -994,7 +1127,8 @@ static int remove_key(struct usbnet *usbdev, int index, u8 bssid[ETH_ALEN]) if (bssid) { /* pairwise key */ if (memcmp(bssid, ffff_bssid, ETH_ALEN) != 0) - remove_key.index |= cpu_to_le32(1 << 30); + remove_key.index |= + NDIS_80211_ADDKEY_PAIRWISE_KEY; memcpy(remove_key.bssid, bssid, sizeof(remove_key.bssid)); } else @@ -1027,7 +1161,7 @@ static int remove_key(struct usbnet *usbdev, int index, u8 bssid[ETH_ALEN]) static void set_multicast_list(struct usbnet *usbdev) { - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); struct dev_mc_list *mclist; __le32 filter; int ret, i, size; @@ -1086,131 +1220,180 @@ static void set_multicast_list(struct usbnet *usbdev) /* - * wireless extension handlers + * cfg80211 ops */ - -static int rndis_iw_commit(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) +static int rndis_change_virtual_intf(struct wiphy *wiphy, int ifindex, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) { - /* dummy op */ - return 0; + struct net_device *dev; + struct usbnet *usbdev; + int mode; + + /* we're under RTNL */ + dev = __dev_get_by_index(&init_net, ifindex); + if (!dev) + return -ENODEV; + usbdev = netdev_priv(dev); + + switch (type) { + case NL80211_IFTYPE_ADHOC: + mode = NDIS_80211_INFRA_ADHOC; + break; + case NL80211_IFTYPE_STATION: + mode = NDIS_80211_INFRA_INFRA; + break; + default: + return -EINVAL; + } + + return set_infra_mode(usbdev, mode); } -static int rndis_iw_get_range(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) +#define SCAN_DELAY_JIFFIES (HZ) +static int rndis_scan(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_scan_request *request) { - struct iw_range *range = (struct iw_range *)extra; struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); - int len, ret, i, j, num, has_80211g_rates; - u8 rates[8]; - __le32 tx_power; + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + int ret; + __le32 tmp; + + devdbg(usbdev, "cfg80211.scan"); - devdbg(usbdev, "SIOCGIWRANGE"); + if (!request) + return -EINVAL; - /* clear iw_range struct */ - memset(range, 0, sizeof(*range)); - wrqu->data.length = sizeof(*range); + if (priv->scan_request && priv->scan_request != request) + return -EBUSY; - range->txpower_capa = IW_TXPOW_MWATT; - range->num_txpower = 1; - if (priv->caps & CAP_SUPPORT_TXPOWER) { - len = sizeof(tx_power); - ret = rndis_query_oid(usbdev, OID_802_11_TX_POWER_LEVEL, - &tx_power, &len); - if (ret == 0 && le32_to_cpu(tx_power) != 0xFF) - range->txpower[0] = le32_to_cpu(tx_power); - else - range->txpower[0] = get_bcm4320_power(priv); - } else - range->txpower[0] = get_bcm4320_power(priv); + priv->scan_request = request; - len = sizeof(rates); - ret = rndis_query_oid(usbdev, OID_802_11_SUPPORTED_RATES, &rates, - &len); - has_80211g_rates = 0; + tmp = cpu_to_le32(1); + ret = rndis_set_oid(usbdev, OID_802_11_BSSID_LIST_SCAN, &tmp, + sizeof(tmp)); if (ret == 0) { - j = 0; - for (i = 0; i < len; i++) { - if (rates[i] == 0) - break; - range->bitrate[j] = (rates[i] & 0x7f) * 500000; - /* check for non 802.11b rates */ - if (range->bitrate[j] == 6000000 || - range->bitrate[j] == 9000000 || - (range->bitrate[j] >= 12000000 && - range->bitrate[j] != 22000000)) - has_80211g_rates = 1; - j++; - } - range->num_bitrates = j; - } else - range->num_bitrates = 0; - - /* fill in 802.11g rates */ - if (has_80211g_rates) { - num = range->num_bitrates; - for (i = 0; i < ARRAY_SIZE(rates_80211g); i++) { - for (j = 0; j < num; j++) { - if (range->bitrate[j] == - rates_80211g[i] * 1000000) - break; - } - if (j == num) - range->bitrate[range->num_bitrates++] = - rates_80211g[i] * 1000000; - if (range->num_bitrates == IW_MAX_BITRATES) - break; - } + /* Wait before retrieving scan results from device */ + queue_delayed_work(priv->workqueue, &priv->scan_work, + SCAN_DELAY_JIFFIES); + } - /* estimated max real througput in bps */ - range->throughput = 54 * 1000 * 1000 / 2; + return ret; +} - /* ~35% more with afterburner */ - if (priv->param_afterburner) - range->throughput = range->throughput / 100 * 135; - } else { - /* estimated max real througput in bps */ - range->throughput = 11 * 1000 * 1000 / 2; + +static struct cfg80211_bss *rndis_bss_info_update(struct usbnet *usbdev, + struct ndis_80211_bssid_ex *bssid) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + struct ieee80211_channel *channel; + s32 signal; + u64 timestamp; + u16 capability; + u16 beacon_interval; + struct ndis_80211_fixed_ies *fixed; + int ie_len, bssid_len; + u8 *ie; + + /* parse bssid structure */ + bssid_len = le32_to_cpu(bssid->length); + + if (bssid_len < sizeof(struct ndis_80211_bssid_ex) + + sizeof(struct ndis_80211_fixed_ies)) + return NULL; + + fixed = (struct ndis_80211_fixed_ies *)bssid->ies; + + ie = (void *)(bssid->ies + sizeof(struct ndis_80211_fixed_ies)); + ie_len = min(bssid_len - (int)sizeof(*bssid), + (int)le32_to_cpu(bssid->ie_length)); + ie_len -= sizeof(struct ndis_80211_fixed_ies); + if (ie_len < 0) + return NULL; + + /* extract data for cfg80211_inform_bss */ + channel = ieee80211_get_channel(priv->wdev.wiphy, + KHZ_TO_MHZ(le32_to_cpu(bssid->config.ds_config))); + if (!channel) + return NULL; + + signal = level_to_qual(le32_to_cpu(bssid->rssi)); + timestamp = le64_to_cpu(*(__le64 *)fixed->timestamp); + capability = le16_to_cpu(fixed->capabilities); + beacon_interval = le16_to_cpu(fixed->beacon_interval); + + return cfg80211_inform_bss(priv->wdev.wiphy, channel, bssid->mac, + timestamp, capability, beacon_interval, ie, ie_len, signal, + GFP_KERNEL); +} + + +static int rndis_check_bssid_list(struct usbnet *usbdev) +{ + void *buf = NULL; + struct ndis_80211_bssid_list_ex *bssid_list; + struct ndis_80211_bssid_ex *bssid; + int ret = -EINVAL, len, count, bssid_len; + + devdbg(usbdev, "check_bssid_list"); + + len = CONTROL_BUFFER_SIZE; + buf = kmalloc(len, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto out; } - range->num_channels = 14; + ret = rndis_query_oid(usbdev, OID_802_11_BSSID_LIST, buf, &len); + if (ret != 0) + goto out; - for (i = 0; (i < 14) && (i < IW_MAX_FREQUENCIES); i++) { - range->freq[i].i = i + 1; - range->freq[i].m = ieee80211_dsss_chan_to_freq(i + 1) * 100000; - range->freq[i].e = 1; + bssid_list = buf; + bssid = bssid_list->bssid; + bssid_len = le32_to_cpu(bssid->length); + count = le32_to_cpu(bssid_list->num_items); + devdbg(usbdev, "check_bssid_list: %d BSSIDs found", count); + + while (count && ((void *)bssid + bssid_len) <= (buf + len)) { + rndis_bss_info_update(usbdev, bssid); + + bssid = (void *)bssid + bssid_len; + bssid_len = le32_to_cpu(bssid->length); + count--; } - range->num_frequency = i; - range->min_rts = 0; - range->max_rts = 2347; - range->min_frag = 256; - range->max_frag = 2346; +out: + kfree(buf); + return ret; +} - range->max_qual.qual = 100; - range->max_qual.level = 154; - range->max_qual.updated = IW_QUAL_QUAL_UPDATED - | IW_QUAL_LEVEL_UPDATED - | IW_QUAL_NOISE_INVALID; - range->we_version_compiled = WIRELESS_EXT; - range->we_version_source = WIRELESS_EXT; +static void rndis_get_scan_results(struct work_struct *work) +{ + struct rndis_wlan_private *priv = + container_of(work, struct rndis_wlan_private, scan_work.work); + struct usbnet *usbdev = priv->usbdev; + int ret; + + devdbg(usbdev, "get_scan_results"); - range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | - IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; - return 0; + ret = rndis_check_bssid_list(usbdev); + + cfg80211_scan_done(priv->scan_request, ret < 0); + + priv->scan_request = NULL; } -static int rndis_iw_get_name(struct net_device *dev, +/* + * wireless extension handlers + */ + +static int rndis_iw_commit(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); - - strcpy(wrqu->name, priv->name); + /* dummy op */ return 0; } @@ -1314,7 +1497,7 @@ static int rndis_iw_set_auth(struct net_device *dev, { struct iw_param *p = &wrqu->param; struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); int ret = -ENOTSUPP; switch (p->flags & IW_AUTH_INDEX) { @@ -1395,7 +1578,7 @@ static int rndis_iw_get_auth(struct net_device *dev, { struct iw_param *p = &wrqu->param; struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); switch (p->flags & IW_AUTH_INDEX) { case IW_AUTH_WPA_VERSION: @@ -1422,60 +1605,11 @@ static int rndis_iw_get_auth(struct net_device *dev, } -static int rndis_iw_get_mode(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); - - switch (priv->infra_mode) { - case ndis_80211_infra_adhoc: - wrqu->mode = IW_MODE_ADHOC; - break; - case ndis_80211_infra_infra: - wrqu->mode = IW_MODE_INFRA; - break; - /*case ndis_80211_infra_auto_unknown:*/ - default: - wrqu->mode = IW_MODE_AUTO; - break; - } - devdbg(usbdev, "SIOCGIWMODE: %08x", wrqu->mode); - return 0; -} - - -static int rndis_iw_set_mode(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) -{ - struct usbnet *usbdev = netdev_priv(dev); - int mode; - - devdbg(usbdev, "SIOCSIWMODE: %08x", wrqu->mode); - - switch (wrqu->mode) { - case IW_MODE_ADHOC: - mode = ndis_80211_infra_adhoc; - break; - case IW_MODE_INFRA: - mode = ndis_80211_infra_infra; - break; - /*case IW_MODE_AUTO:*/ - default: - mode = ndis_80211_infra_auto_unknown; - break; - } - - return set_infra_mode(usbdev, mode); -} - - static int rndis_iw_set_encode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); int ret, index, key_len; u8 *key; @@ -1538,10 +1672,8 @@ static int rndis_iw_set_encode_ext(struct net_device *dev, { struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); - struct ndis_80211_key ndis_key; - int keyidx, ret; - u8 *addr; + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + int keyidx, flags; keyidx = wrqu->encoding.flags & IW_ENCODE_INDEX; @@ -1564,250 +1696,16 @@ static int rndis_iw_set_encode_ext(struct net_device *dev, ext->alg == IW_ENCODE_ALG_NONE || ext->key_len == 0) return remove_key(usbdev, keyidx, NULL); - if (ext->key_len > sizeof(ndis_key.material)) - return -1; - - memset(&ndis_key, 0, sizeof(ndis_key)); - - ndis_key.size = cpu_to_le32(sizeof(ndis_key) - - sizeof(ndis_key.material) + ext->key_len); - ndis_key.length = cpu_to_le32(ext->key_len); - ndis_key.index = cpu_to_le32(keyidx); - - if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) { - memcpy(ndis_key.rsc, ext->rx_seq, 6); - ndis_key.index |= cpu_to_le32(1 << 29); - } - - addr = ext->addr.sa_data; - if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { - /* group key */ - if (priv->infra_mode == ndis_80211_infra_adhoc) - memset(ndis_key.bssid, 0xff, ETH_ALEN); - else - get_bssid(usbdev, ndis_key.bssid); - } else { - /* pairwise key */ - ndis_key.index |= cpu_to_le32(1 << 30); - memcpy(ndis_key.bssid, addr, ETH_ALEN); - } - - if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) - ndis_key.index |= cpu_to_le32(1 << 31); - - if (ext->alg == IW_ENCODE_ALG_TKIP && ext->key_len == 32) { - /* wpa_supplicant gives us the Michael MIC RX/TX keys in - * different order than NDIS spec, so swap the order here. */ - memcpy(ndis_key.material, ext->key, 16); - memcpy(ndis_key.material + 16, ext->key + 24, 8); - memcpy(ndis_key.material + 24, ext->key + 16, 8); - } else - memcpy(ndis_key.material, ext->key, ext->key_len); - - ret = rndis_set_oid(usbdev, OID_802_11_ADD_KEY, &ndis_key, - le32_to_cpu(ndis_key.size)); - devdbg(usbdev, "SIOCSIWENCODEEXT: OID_802_11_ADD_KEY -> %08X", ret); - if (ret != 0) - return ret; - - priv->encr_key_len[keyidx] = ext->key_len; - memcpy(&priv->encr_keys[keyidx], ndis_key.material, ext->key_len); + flags = 0; + if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) + flags |= NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ; + if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY)) + flags |= NDIS_80211_ADDKEY_PAIRWISE_KEY; if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) - priv->encr_tx_key_index = keyidx; - - return 0; -} - - -static int rndis_iw_set_scan(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) -{ - struct usbnet *usbdev = netdev_priv(dev); - union iwreq_data evt; - int ret = -EINVAL; - __le32 tmp; - - devdbg(usbdev, "SIOCSIWSCAN"); - - if (wrqu->data.flags == 0) { - tmp = cpu_to_le32(1); - ret = rndis_set_oid(usbdev, OID_802_11_BSSID_LIST_SCAN, &tmp, - sizeof(tmp)); - evt.data.flags = 0; - evt.data.length = 0; - wireless_send_event(dev, SIOCGIWSCAN, &evt, NULL); - } - return ret; -} - - -static char *rndis_translate_scan(struct net_device *dev, - struct iw_request_info *info, char *cev, - char *end_buf, - struct ndis_80211_bssid_ex *bssid) -{ - struct usbnet *usbdev = netdev_priv(dev); - u8 *ie; - char *current_val; - int bssid_len, ie_len, i; - u32 beacon, atim; - struct iw_event iwe; - unsigned char sbuf[32]; - - bssid_len = le32_to_cpu(bssid->length); - - devdbg(usbdev, "BSSID %pM", bssid->mac); - iwe.cmd = SIOCGIWAP; - iwe.u.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(iwe.u.ap_addr.sa_data, bssid->mac, ETH_ALEN); - cev = iwe_stream_add_event(info, cev, end_buf, &iwe, IW_EV_ADDR_LEN); - - devdbg(usbdev, "SSID(%d) %s", le32_to_cpu(bssid->ssid.length), - bssid->ssid.essid); - iwe.cmd = SIOCGIWESSID; - iwe.u.essid.length = le32_to_cpu(bssid->ssid.length); - iwe.u.essid.flags = 1; - cev = iwe_stream_add_point(info, cev, end_buf, &iwe, bssid->ssid.essid); - - devdbg(usbdev, "MODE %d", le32_to_cpu(bssid->net_infra)); - iwe.cmd = SIOCGIWMODE; - switch (le32_to_cpu(bssid->net_infra)) { - case ndis_80211_infra_adhoc: - iwe.u.mode = IW_MODE_ADHOC; - break; - case ndis_80211_infra_infra: - iwe.u.mode = IW_MODE_INFRA; - break; - /*case ndis_80211_infra_auto_unknown:*/ - default: - iwe.u.mode = IW_MODE_AUTO; - break; - } - cev = iwe_stream_add_event(info, cev, end_buf, &iwe, IW_EV_UINT_LEN); - - devdbg(usbdev, "FREQ %d kHz", le32_to_cpu(bssid->config.ds_config)); - iwe.cmd = SIOCGIWFREQ; - dsconfig_to_freq(le32_to_cpu(bssid->config.ds_config), &iwe.u.freq); - cev = iwe_stream_add_event(info, cev, end_buf, &iwe, IW_EV_FREQ_LEN); - - devdbg(usbdev, "QUAL %d", le32_to_cpu(bssid->rssi)); - iwe.cmd = IWEVQUAL; - iwe.u.qual.qual = level_to_qual(le32_to_cpu(bssid->rssi)); - iwe.u.qual.level = le32_to_cpu(bssid->rssi); - iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED - | IW_QUAL_LEVEL_UPDATED - | IW_QUAL_NOISE_INVALID; - cev = iwe_stream_add_event(info, cev, end_buf, &iwe, IW_EV_QUAL_LEN); - - devdbg(usbdev, "ENCODE %d", le32_to_cpu(bssid->privacy)); - iwe.cmd = SIOCGIWENCODE; - iwe.u.data.length = 0; - if (le32_to_cpu(bssid->privacy) == ndis_80211_priv_accept_all) - iwe.u.data.flags = IW_ENCODE_DISABLED; - else - iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; - - cev = iwe_stream_add_point(info, cev, end_buf, &iwe, NULL); - - devdbg(usbdev, "RATES:"); - current_val = cev + iwe_stream_lcp_len(info); - iwe.cmd = SIOCGIWRATE; - for (i = 0; i < sizeof(bssid->rates); i++) { - if (bssid->rates[i] & 0x7f) { - iwe.u.bitrate.value = - ((bssid->rates[i] & 0x7f) * - 500000); - devdbg(usbdev, " %d", iwe.u.bitrate.value); - current_val = iwe_stream_add_value(info, cev, - current_val, end_buf, &iwe, - IW_EV_PARAM_LEN); - } - } - - if ((current_val - cev) > iwe_stream_lcp_len(info)) - cev = current_val; - - beacon = le32_to_cpu(bssid->config.beacon_period); - devdbg(usbdev, "BCN_INT %d", beacon); - iwe.cmd = IWEVCUSTOM; - snprintf(sbuf, sizeof(sbuf), "bcn_int=%d", beacon); - iwe.u.data.length = strlen(sbuf); - cev = iwe_stream_add_point(info, cev, end_buf, &iwe, sbuf); - - atim = le32_to_cpu(bssid->config.atim_window); - devdbg(usbdev, "ATIM %d", atim); - iwe.cmd = IWEVCUSTOM; - snprintf(sbuf, sizeof(sbuf), "atim=%u", atim); - iwe.u.data.length = strlen(sbuf); - cev = iwe_stream_add_point(info, cev, end_buf, &iwe, sbuf); - - ie = (void *)(bssid->ies + sizeof(struct ndis_80211_fixed_ies)); - ie_len = min(bssid_len - (int)sizeof(*bssid), - (int)le32_to_cpu(bssid->ie_length)); - ie_len -= sizeof(struct ndis_80211_fixed_ies); - while (ie_len >= 2 && 2 + ie[1] <= ie_len) { - if ((ie[0] == WLAN_EID_GENERIC && ie[1] >= 4 && - memcmp(ie + 2, "\x00\x50\xf2\x01", 4) == 0) || - ie[0] == WLAN_EID_RSN) { - devdbg(usbdev, "IE: WPA%d", - (ie[0] == WLAN_EID_RSN) ? 2 : 1); - iwe.cmd = IWEVGENIE; - /* arbitrary cut-off at 64 */ - iwe.u.data.length = min(ie[1] + 2, 64); - cev = iwe_stream_add_point(info, cev, end_buf, &iwe, ie); - } - - ie_len -= 2 + ie[1]; - ie += 2 + ie[1]; - } - - return cev; -} - - -static int rndis_iw_get_scan(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) -{ - struct usbnet *usbdev = netdev_priv(dev); - void *buf = NULL; - char *cev = extra; - struct ndis_80211_bssid_list_ex *bssid_list; - struct ndis_80211_bssid_ex *bssid; - int ret = -EINVAL, len, count, bssid_len; - - devdbg(usbdev, "SIOCGIWSCAN"); + flags |= NDIS_80211_ADDKEY_TRANSMIT_KEY; - len = CONTROL_BUFFER_SIZE; - buf = kmalloc(len, GFP_KERNEL); - if (!buf) { - ret = -ENOMEM; - goto out; - } - - ret = rndis_query_oid(usbdev, OID_802_11_BSSID_LIST, buf, &len); - - if (ret != 0) - goto out; - - bssid_list = buf; - bssid = bssid_list->bssid; - bssid_len = le32_to_cpu(bssid->length); - count = le32_to_cpu(bssid_list->num_items); - devdbg(usbdev, "SIOCGIWSCAN: %d BSSIDs found", count); - - while (count && ((void *)bssid + bssid_len) <= (buf + len)) { - cev = rndis_translate_scan(dev, info, cev, - extra + IW_SCAN_MAX_DATA, bssid); - bssid = (void *)bssid + bssid_len; - bssid_len = le32_to_cpu(bssid->length); - count--; - } - -out: - wrqu->data.length = cev - extra; - wrqu->data.flags = 0; - kfree(buf); - return ret; + return add_wpa_key(usbdev, ext->key, ext->key_len, keyidx, &ext->addr, + ext->rx_seq, ext->alg, flags); } @@ -1815,7 +1713,7 @@ static int rndis_iw_set_genie(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); int ret = 0; #ifdef DEBUG @@ -1849,7 +1747,7 @@ static int rndis_iw_get_genie(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); devdbg(usbdev, "SIOCGIWGENIE"); @@ -1936,39 +1834,6 @@ static int rndis_iw_get_frag(struct net_device *dev, } -static int rndis_iw_set_nick(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) -{ - struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); - - devdbg(usbdev, "SIOCSIWNICK"); - - priv->nick_len = wrqu->data.length; - if (priv->nick_len > 32) - priv->nick_len = 32; - - memcpy(priv->nick, extra, priv->nick_len); - return 0; -} - - -static int rndis_iw_get_nick(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) -{ - struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); - - wrqu->data.flags = 1; - wrqu->data.length = priv->nick_len; - memcpy(extra, priv->nick, priv->nick_len); - - devdbg(usbdev, "SIOCGIWNICK: '%s'", priv->nick); - - return 0; -} - - static int rndis_iw_set_freq(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { @@ -2021,20 +1886,12 @@ static int rndis_iw_get_txpower(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); __le32 tx_power; - int ret = 0, len; if (priv->radio_on) { - if (priv->caps & CAP_SUPPORT_TXPOWER) { - len = sizeof(tx_power); - ret = rndis_query_oid(usbdev, OID_802_11_TX_POWER_LEVEL, - &tx_power, &len); - if (ret != 0) - return ret; - } else - /* fake incase not supported */ - tx_power = cpu_to_le32(get_bcm4320_power(priv)); + /* fake since changing tx_power (by userlevel) not supported */ + tx_power = cpu_to_le32(get_bcm4320_power(priv)); wrqu->txpower.flags = IW_TXPOW_MWATT; wrqu->txpower.value = le32_to_cpu(tx_power); @@ -2047,7 +1904,7 @@ static int rndis_iw_get_txpower(struct net_device *dev, devdbg(usbdev, "SIOCGIWTXPOW: %d", wrqu->txpower.value); - return ret; + return 0; } @@ -2055,9 +1912,8 @@ static int rndis_iw_set_txpower(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); __le32 tx_power = 0; - int ret = 0; if (!wrqu->txpower.disabled) { if (wrqu->txpower.flags == IW_TXPOW_MWATT) @@ -2080,22 +1936,10 @@ static int rndis_iw_set_txpower(struct net_device *dev, devdbg(usbdev, "SIOCSIWTXPOW: %d", le32_to_cpu(tx_power)); if (le32_to_cpu(tx_power) != 0) { - if (priv->caps & CAP_SUPPORT_TXPOWER) { - /* turn radio on first */ - if (!priv->radio_on) - disassociate(usbdev, 1); - - ret = rndis_set_oid(usbdev, OID_802_11_TX_POWER_LEVEL, - &tx_power, sizeof(tx_power)); - if (ret != 0) - ret = -EOPNOTSUPP; - return ret; - } else { - /* txpower unsupported, just turn radio on */ - if (!priv->radio_on) - return disassociate(usbdev, 1); - return 0; /* all ready on */ - } + /* txpower unsupported, just turn radio on */ + if (!priv->radio_on) + return disassociate(usbdev, 1); + return 0; /* all ready on */ } /* tx_power == 0, turn off radio */ @@ -2125,7 +1969,7 @@ static int rndis_iw_set_mlme(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); struct iw_mlme *mlme = (struct iw_mlme *)extra; unsigned char bssid[ETH_ALEN]; @@ -2150,7 +1994,7 @@ static int rndis_iw_set_mlme(struct net_device *dev, static struct iw_statistics *rndis_get_wireless_stats(struct net_device *dev) { struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); unsigned long flags; spin_lock_irqsave(&priv->stats_lock, flags); @@ -2165,20 +2009,18 @@ static struct iw_statistics *rndis_get_wireless_stats(struct net_device *dev) static const iw_handler rndis_iw_handler[] = { IW_IOCTL(SIOCSIWCOMMIT) = rndis_iw_commit, - IW_IOCTL(SIOCGIWNAME) = rndis_iw_get_name, + IW_IOCTL(SIOCGIWNAME) = (iw_handler) cfg80211_wext_giwname, IW_IOCTL(SIOCSIWFREQ) = rndis_iw_set_freq, IW_IOCTL(SIOCGIWFREQ) = rndis_iw_get_freq, - IW_IOCTL(SIOCSIWMODE) = rndis_iw_set_mode, - IW_IOCTL(SIOCGIWMODE) = rndis_iw_get_mode, - IW_IOCTL(SIOCGIWRANGE) = rndis_iw_get_range, + IW_IOCTL(SIOCSIWMODE) = (iw_handler) cfg80211_wext_siwmode, + IW_IOCTL(SIOCGIWMODE) = (iw_handler) cfg80211_wext_giwmode, + IW_IOCTL(SIOCGIWRANGE) = (iw_handler) cfg80211_wext_giwrange, IW_IOCTL(SIOCSIWAP) = rndis_iw_set_bssid, IW_IOCTL(SIOCGIWAP) = rndis_iw_get_bssid, - IW_IOCTL(SIOCSIWSCAN) = rndis_iw_set_scan, - IW_IOCTL(SIOCGIWSCAN) = rndis_iw_get_scan, + IW_IOCTL(SIOCSIWSCAN) = (iw_handler) cfg80211_wext_siwscan, + IW_IOCTL(SIOCGIWSCAN) = (iw_handler) cfg80211_wext_giwscan, IW_IOCTL(SIOCSIWESSID) = rndis_iw_set_essid, IW_IOCTL(SIOCGIWESSID) = rndis_iw_get_essid, - IW_IOCTL(SIOCSIWNICKN) = rndis_iw_set_nick, - IW_IOCTL(SIOCGIWNICKN) = rndis_iw_get_nick, IW_IOCTL(SIOCGIWRATE) = rndis_iw_get_rate, IW_IOCTL(SIOCSIWRTS) = rndis_iw_set_rts, IW_IOCTL(SIOCGIWRTS) = rndis_iw_get_rts, @@ -2195,28 +2037,28 @@ static const iw_handler rndis_iw_handler[] = IW_IOCTL(SIOCSIWMLME) = rndis_iw_set_mlme, }; -static const iw_handler rndis_wext_private_handler[] = { +static const iw_handler rndis_wlan_private_handler[] = { }; -static const struct iw_priv_args rndis_wext_private_args[] = { +static const struct iw_priv_args rndis_wlan_private_args[] = { }; static const struct iw_handler_def rndis_iw_handlers = { .num_standard = ARRAY_SIZE(rndis_iw_handler), - .num_private = ARRAY_SIZE(rndis_wext_private_handler), - .num_private_args = ARRAY_SIZE(rndis_wext_private_args), + .num_private = ARRAY_SIZE(rndis_wlan_private_handler), + .num_private_args = ARRAY_SIZE(rndis_wlan_private_args), .standard = (iw_handler *)rndis_iw_handler, - .private = (iw_handler *)rndis_wext_private_handler, - .private_args = (struct iw_priv_args *)rndis_wext_private_args, + .private = (iw_handler *)rndis_wlan_private_handler, + .private_args = (struct iw_priv_args *)rndis_wlan_private_args, .get_wireless_stats = rndis_get_wireless_stats, }; -static void rndis_wext_worker(struct work_struct *work) +static void rndis_wlan_worker(struct work_struct *work) { - struct rndis_wext_private *priv = - container_of(work, struct rndis_wext_private, work); + struct rndis_wlan_private *priv = + container_of(work, struct rndis_wlan_private, work); struct usbnet *usbdev = priv->usbdev; union iwreq_data evt; unsigned char bssid[ETH_ALEN]; @@ -2277,10 +2119,10 @@ get_bssid: set_multicast_list(usbdev); } -static void rndis_wext_set_multicast_list(struct net_device *dev) +static void rndis_wlan_set_multicast_list(struct net_device *dev) { struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); if (test_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending)) return; @@ -2289,9 +2131,9 @@ static void rndis_wext_set_multicast_list(struct net_device *dev) queue_work(priv->workqueue, &priv->work); } -static void rndis_wext_link_change(struct usbnet *usbdev, int state) +static void rndis_wlan_link_change(struct usbnet *usbdev, int state) { - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); /* queue work to avoid recursive calls into rndis_command */ set_bit(state ? WORK_LINK_UP : WORK_LINK_DOWN, &priv->work_pending); @@ -2299,22 +2141,14 @@ static void rndis_wext_link_change(struct usbnet *usbdev, int state) } -static int rndis_wext_get_caps(struct usbnet *usbdev) +static int rndis_wlan_get_caps(struct usbnet *usbdev) { struct { __le32 num_items; __le32 items[8]; } networks_supported; int len, retval, i, n; - __le32 tx_power; - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); - - /* determine if supports setting txpower */ - len = sizeof(tx_power); - retval = rndis_query_oid(usbdev, OID_802_11_TX_POWER_LEVEL, &tx_power, - &len); - if (retval == 0 && le32_to_cpu(tx_power) != 0xFF) - priv->caps |= CAP_SUPPORT_TXPOWER; + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); /* determine supported modes */ len = sizeof(networks_supported); @@ -2326,24 +2160,18 @@ static int rndis_wext_get_caps(struct usbnet *usbdev) n = 8; for (i = 0; i < n; i++) { switch (le32_to_cpu(networks_supported.items[i])) { - case ndis_80211_type_freq_hop: - case ndis_80211_type_direct_seq: + case NDIS_80211_TYPE_FREQ_HOP: + case NDIS_80211_TYPE_DIRECT_SEQ: priv->caps |= CAP_MODE_80211B; break; - case ndis_80211_type_ofdm_a: + case NDIS_80211_TYPE_OFDM_A: priv->caps |= CAP_MODE_80211A; break; - case ndis_80211_type_ofdm_g: + case NDIS_80211_TYPE_OFDM_G: priv->caps |= CAP_MODE_80211G; break; } } - if (priv->caps & CAP_MODE_80211A) - strcat(priv->name, "a"); - if (priv->caps & CAP_MODE_80211B) - strcat(priv->name, "b"); - if (priv->caps & CAP_MODE_80211G) - strcat(priv->name, "g"); } return retval; @@ -2353,8 +2181,8 @@ static int rndis_wext_get_caps(struct usbnet *usbdev) #define STATS_UPDATE_JIFFIES (HZ) static void rndis_update_wireless_stats(struct work_struct *work) { - struct rndis_wext_private *priv = - container_of(work, struct rndis_wext_private, stats_work.work); + struct rndis_wlan_private *priv = + container_of(work, struct rndis_wlan_private, stats_work.work); struct usbnet *usbdev = priv->usbdev; struct iw_statistics iwstats; __le32 rssi, tmp; @@ -2387,7 +2215,7 @@ static void rndis_update_wireless_stats(struct work_struct *work) if (ret == 0) { memset(&iwstats.qual, 0, sizeof(iwstats.qual)); iwstats.qual.qual = level_to_qual(le32_to_cpu(rssi)); - iwstats.qual.level = le32_to_cpu(rssi); + iwstats.qual.level = level_to_qual(le32_to_cpu(rssi)); iwstats.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID; @@ -2457,9 +2285,19 @@ end: } -static int bcm4320_early_init(struct usbnet *usbdev) +static int bcm4320a_early_init(struct usbnet *usbdev) { - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); + /* bcm4320a doesn't handle configuration parameters well. Try + * set any and you get partially zeroed mac and broken device. + */ + + return 0; +} + + +static int bcm4320b_early_init(struct usbnet *usbdev) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); char buf[8]; /* Early initialization settings, setting these won't have effect @@ -2525,33 +2363,41 @@ static int bcm4320_early_init(struct usbnet *usbdev) } /* same as rndis_netdev_ops but with local multicast handler */ -static const struct net_device_ops rndis_wext_netdev_ops = { +static const struct net_device_ops rndis_wlan_netdev_ops = { .ndo_open = usbnet_open, .ndo_stop = usbnet_stop, .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, - .ndo_set_multicast_list = rndis_wext_set_multicast_list, + .ndo_set_multicast_list = rndis_wlan_set_multicast_list, }; -static int rndis_wext_bind(struct usbnet *usbdev, struct usb_interface *intf) +static int rndis_wlan_bind(struct usbnet *usbdev, struct usb_interface *intf) { - struct rndis_wext_private *priv; + struct wiphy *wiphy; + struct rndis_wlan_private *priv; int retval, len; __le32 tmp; - /* allocate rndis private data */ - priv = kzalloc(sizeof(struct rndis_wext_private), GFP_KERNEL); - if (!priv) + /* allocate wiphy and rndis private data + * NOTE: We only support a single virtual interface, so wiphy + * and wireless_dev are somewhat synonymous for this device. + */ + wiphy = wiphy_new(&rndis_config_ops, sizeof(struct rndis_wlan_private)); + if (!wiphy) return -ENOMEM; + priv = wiphy_priv(wiphy); + usbdev->net->ieee80211_ptr = &priv->wdev; + priv->wdev.wiphy = wiphy; + priv->wdev.iftype = NL80211_IFTYPE_STATION; + /* These have to be initialized before calling generic_rndis_bind(). - * Otherwise we'll be in big trouble in rndis_wext_early_init(). + * Otherwise we'll be in big trouble in rndis_wlan_early_init(). */ usbdev->driver_priv = priv; - strcpy(priv->name, "IEEE802.11"); usbdev->net->wireless_handlers = &rndis_iw_handlers; priv->usbdev = usbdev; @@ -2560,8 +2406,9 @@ static int rndis_wext_bind(struct usbnet *usbdev, struct usb_interface *intf) /* because rndis_command() sleeps we need to use workqueue */ priv->workqueue = create_singlethread_workqueue("rndis_wlan"); - INIT_WORK(&priv->work, rndis_wext_worker); + INIT_WORK(&priv->work, rndis_wlan_worker); INIT_DELAYED_WORK(&priv->stats_work, rndis_update_wireless_stats); + INIT_DELAYED_WORK(&priv->scan_work, rndis_get_scan_results); /* try bind rndis_host */ retval = generic_rndis_bind(usbdev, intf, FLAG_RNDIS_PHYM_WIRELESS); @@ -2573,9 +2420,9 @@ static int rndis_wext_bind(struct usbnet *usbdev, struct usb_interface *intf) * picks up rssi to closest station instead of to access point). * * rndis_host wants to avoid all OID as much as possible - * so do promisc/multicast handling in rndis_wext. + * so do promisc/multicast handling in rndis_wlan. */ - usbdev->net->netdev_ops = &rndis_wext_netdev_ops; + usbdev->net->netdev_ops = &rndis_wlan_netdev_ops; tmp = RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_BROADCAST; retval = rndis_set_oid(usbdev, OID_GEN_CURRENT_PACKET_FILTER, &tmp, @@ -2600,7 +2447,32 @@ static int rndis_wext_bind(struct usbnet *usbdev, struct usb_interface *intf) | IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID; - rndis_wext_get_caps(usbdev); + /* fill-out wiphy structure and register w/ cfg80211 */ + memcpy(wiphy->perm_addr, usbdev->net->dev_addr, ETH_ALEN); + wiphy->privid = rndis_wiphy_privid; + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) + | BIT(NL80211_IFTYPE_ADHOC); + wiphy->max_scan_ssids = 1; + + /* TODO: fill-out band information based on priv->caps */ + rndis_wlan_get_caps(usbdev); + + memcpy(priv->channels, rndis_channels, sizeof(rndis_channels)); + memcpy(priv->rates, rndis_rates, sizeof(rndis_rates)); + priv->band.channels = priv->channels; + priv->band.n_channels = ARRAY_SIZE(rndis_channels); + priv->band.bitrates = priv->rates; + priv->band.n_bitrates = ARRAY_SIZE(rndis_rates); + wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; + wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC; + + set_wiphy_dev(wiphy, &usbdev->udev->dev); + + if (wiphy_register(wiphy)) { + retval = -ENODEV; + goto fail; + } + set_default_iw_params(usbdev); /* turn radio on */ @@ -2615,36 +2487,40 @@ static int rndis_wext_bind(struct usbnet *usbdev, struct usb_interface *intf) fail: cancel_delayed_work_sync(&priv->stats_work); + cancel_delayed_work_sync(&priv->scan_work); cancel_work_sync(&priv->work); flush_workqueue(priv->workqueue); destroy_workqueue(priv->workqueue); - kfree(priv); + wiphy_free(wiphy); return retval; } -static void rndis_wext_unbind(struct usbnet *usbdev, struct usb_interface *intf) +static void rndis_wlan_unbind(struct usbnet *usbdev, struct usb_interface *intf) { - struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); /* turn radio off */ disassociate(usbdev, 0); cancel_delayed_work_sync(&priv->stats_work); + cancel_delayed_work_sync(&priv->scan_work); cancel_work_sync(&priv->work); flush_workqueue(priv->workqueue); destroy_workqueue(priv->workqueue); if (priv && priv->wpa_ie_len) kfree(priv->wpa_ie); - kfree(priv); rndis_unbind(usbdev, intf); + + wiphy_unregister(priv->wdev.wiphy); + wiphy_free(priv->wdev.wiphy); } -static int rndis_wext_reset(struct usbnet *usbdev) +static int rndis_wlan_reset(struct usbnet *usbdev) { return deauthenticate(usbdev); } @@ -2653,40 +2529,40 @@ static int rndis_wext_reset(struct usbnet *usbdev) static const struct driver_info bcm4320b_info = { .description = "Wireless RNDIS device, BCM4320b based", .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT, - .bind = rndis_wext_bind, - .unbind = rndis_wext_unbind, + .bind = rndis_wlan_bind, + .unbind = rndis_wlan_unbind, .status = rndis_status, .rx_fixup = rndis_rx_fixup, .tx_fixup = rndis_tx_fixup, - .reset = rndis_wext_reset, - .early_init = bcm4320_early_init, - .link_change = rndis_wext_link_change, + .reset = rndis_wlan_reset, + .early_init = bcm4320b_early_init, + .link_change = rndis_wlan_link_change, }; static const struct driver_info bcm4320a_info = { .description = "Wireless RNDIS device, BCM4320a based", .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT, - .bind = rndis_wext_bind, - .unbind = rndis_wext_unbind, + .bind = rndis_wlan_bind, + .unbind = rndis_wlan_unbind, .status = rndis_status, .rx_fixup = rndis_rx_fixup, .tx_fixup = rndis_tx_fixup, - .reset = rndis_wext_reset, - .early_init = bcm4320_early_init, - .link_change = rndis_wext_link_change, + .reset = rndis_wlan_reset, + .early_init = bcm4320a_early_init, + .link_change = rndis_wlan_link_change, }; -static const struct driver_info rndis_wext_info = { +static const struct driver_info rndis_wlan_info = { .description = "Wireless RNDIS device", .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT, - .bind = rndis_wext_bind, - .unbind = rndis_wext_unbind, + .bind = rndis_wlan_bind, + .unbind = rndis_wlan_unbind, .status = rndis_status, .rx_fixup = rndis_rx_fixup, .tx_fixup = rndis_tx_fixup, - .reset = rndis_wext_reset, - .early_init = bcm4320_early_init, - .link_change = rndis_wext_link_change, + .reset = rndis_wlan_reset, + .early_init = bcm4320a_early_init, + .link_change = rndis_wlan_link_change, }; /*-------------------------------------------------------------------------*/ @@ -2796,11 +2672,11 @@ static const struct usb_device_id products [] = { { /* RNDIS is MSFT's un-official variant of CDC ACM */ USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff), - .driver_info = (unsigned long) &rndis_wext_info, + .driver_info = (unsigned long) &rndis_wlan_info, }, { /* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */ USB_INTERFACE_INFO(USB_CLASS_MISC, 1, 1), - .driver_info = (unsigned long) &rndis_wext_info, + .driver_info = (unsigned long) &rndis_wlan_info, }, { }, // END }; |