summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/rndis_wlan.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/rndis_wlan.c')
-rw-r--r--drivers/net/wireless/rndis_wlan.c205
1 files changed, 115 insertions, 90 deletions
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 18c9931e3267..00e965b9da75 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -310,8 +310,11 @@ enum wpa_key_mgmt { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE,
#define CAP_MODE_MASK 7
#define CAP_SUPPORT_TXPOWER 8
-#define WORK_CONNECTION_EVENT (1<<0)
-#define WORK_SET_MULTICAST_LIST (1<<1)
+#define WORK_LINK_UP (1<<0)
+#define WORK_LINK_DOWN (1<<1)
+#define WORK_SET_MULTICAST_LIST (1<<2)
+
+#define COMMAND_BUFFER_SIZE (CONTROL_BUFFER_SIZE + sizeof(struct rndis_set))
/* RNDIS device private data */
struct rndis_wext_private {
@@ -361,6 +364,8 @@ struct rndis_wext_private {
u8 *wpa_ie;
int wpa_cipher_pair;
int wpa_cipher_group;
+
+ u8 command_buffer[COMMAND_BUFFER_SIZE];
};
@@ -427,18 +432,23 @@ static int rndis_query_oid(struct usbnet *dev, __le32 oid, void *data, int *len)
buflen = *len + sizeof(*u.get);
if (buflen < CONTROL_BUFFER_SIZE)
buflen = CONTROL_BUFFER_SIZE;
- u.buf = kmalloc(buflen, GFP_KERNEL);
- if (!u.buf)
- return -ENOMEM;
+
+ if (buflen > COMMAND_BUFFER_SIZE) {
+ u.buf = kmalloc(buflen, GFP_KERNEL);
+ if (!u.buf)
+ return -ENOMEM;
+ } else {
+ u.buf = priv->command_buffer;
+ }
+
+ mutex_lock(&priv->command_lock);
+
memset(u.get, 0, sizeof *u.get);
u.get->msg_type = RNDIS_MSG_QUERY;
u.get->msg_len = ccpu2(sizeof *u.get);
u.get->oid = oid;
- mutex_lock(&priv->command_lock);
- ret = rndis_command(dev, u.header);
- mutex_unlock(&priv->command_lock);
-
+ ret = rndis_command(dev, u.header, buflen);
if (ret == 0) {
ret = le32_to_cpu(u.get_c->len);
*len = (*len > ret) ? ret : *len;
@@ -446,7 +456,10 @@ static int rndis_query_oid(struct usbnet *dev, __le32 oid, void *data, int *len)
ret = rndis_error_status(u.get_c->status);
}
- kfree(u.buf);
+ mutex_unlock(&priv->command_lock);
+
+ if (u.buf != priv->command_buffer)
+ kfree(u.buf);
return ret;
}
@@ -465,9 +478,16 @@ static int rndis_set_oid(struct usbnet *dev, __le32 oid, void *data, int len)
buflen = len + sizeof(*u.set);
if (buflen < CONTROL_BUFFER_SIZE)
buflen = CONTROL_BUFFER_SIZE;
- u.buf = kmalloc(buflen, GFP_KERNEL);
- if (!u.buf)
- return -ENOMEM;
+
+ if (buflen > COMMAND_BUFFER_SIZE) {
+ u.buf = kmalloc(buflen, GFP_KERNEL);
+ if (!u.buf)
+ return -ENOMEM;
+ } else {
+ u.buf = priv->command_buffer;
+ }
+
+ mutex_lock(&priv->command_lock);
memset(u.set, 0, sizeof *u.set);
u.set->msg_type = RNDIS_MSG_SET;
@@ -478,14 +498,14 @@ static int rndis_set_oid(struct usbnet *dev, __le32 oid, void *data, int len)
u.set->handle = ccpu2(0);
memcpy(u.buf + sizeof(*u.set), data, len);
- mutex_lock(&priv->command_lock);
- ret = rndis_command(dev, u.header);
- mutex_unlock(&priv->command_lock);
-
+ ret = rndis_command(dev, u.header, buflen);
if (ret == 0)
ret = rndis_error_status(u.set_c->status);
- kfree(u.buf);
+ mutex_unlock(&priv->command_lock);
+
+ if (u.buf != priv->command_buffer)
+ kfree(u.buf);
return ret;
}
@@ -620,8 +640,7 @@ static void dsconfig_to_freq(unsigned int dsconfig, struct iw_freq *freq)
static int freq_to_dsconfig(struct iw_freq *freq, unsigned int *dsconfig)
{
if (freq->m < 1000 && freq->e == 0) {
- if (freq->m >= 1 &&
- freq->m <= (sizeof(freq_chan) / sizeof(freq_chan[0])))
+ if (freq->m >= 1 && freq->m <= ARRAY_SIZE(freq_chan))
*dsconfig = freq_chan[freq->m - 1] * 1000;
else
return -1;
@@ -1135,7 +1154,7 @@ static int rndis_iw_get_range(struct net_device *dev,
/* fill in 802.11g rates */
if (has_80211g_rates) {
num = range->num_bitrates;
- for (i = 0; i < sizeof(rates_80211g); i++) {
+ for (i = 0; i < ARRAY_SIZE(rates_80211g); i++) {
for (j = 0; j < num; j++) {
if (range->bitrate[j] ==
rates_80211g[i] * 1000000)
@@ -1159,10 +1178,9 @@ static int rndis_iw_get_range(struct net_device *dev,
range->throughput = 11 * 1000 * 1000 / 2;
}
- range->num_channels = (sizeof(freq_chan)/sizeof(freq_chan[0]));
+ range->num_channels = ARRAY_SIZE(freq_chan);
- for (i = 0; i < (sizeof(freq_chan)/sizeof(freq_chan[0])) &&
- i < IW_MAX_FREQUENCIES; i++) {
+ for (i = 0; i < ARRAY_SIZE(freq_chan) && i < IW_MAX_FREQUENCIES; i++) {
range->freq[i].i = i + 1;
range->freq[i].m = freq_chan[i] * 100000;
range->freq[i].e = 1;
@@ -1630,7 +1648,9 @@ static int rndis_iw_set_scan(struct net_device *dev,
static char *rndis_translate_scan(struct net_device *dev,
- char *cev, char *end_buf, struct ndis_80211_bssid_ex *bssid)
+ struct iw_request_info *info, char *cev,
+ char *end_buf,
+ struct ndis_80211_bssid_ex *bssid)
{
#ifdef DEBUG
struct usbnet *usbdev = dev->priv;
@@ -1649,14 +1669,14 @@ static char *rndis_translate_scan(struct net_device *dev,
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(cev, end_buf, &iwe, IW_EV_ADDR_LEN);
+ 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(cev, end_buf, &iwe, bssid->ssid.essid);
+ 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;
@@ -1672,12 +1692,12 @@ static char *rndis_translate_scan(struct net_device *dev,
iwe.u.mode = IW_MODE_AUTO;
break;
}
- cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_UINT_LEN);
+ 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(cev, end_buf, &iwe, IW_EV_FREQ_LEN);
+ 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;
@@ -1686,7 +1706,7 @@ static char *rndis_translate_scan(struct net_device *dev,
iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED
| IW_QUAL_LEVEL_UPDATED
| IW_QUAL_NOISE_INVALID;
- cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_QUAL_LEN);
+ 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;
@@ -1696,10 +1716,10 @@ static char *rndis_translate_scan(struct net_device *dev,
else
iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
- cev = iwe_stream_add_point(cev, end_buf, &iwe, NULL);
+ cev = iwe_stream_add_point(info, cev, end_buf, &iwe, NULL);
devdbg(usbdev, "RATES:");
- current_val = cev + IW_EV_LCP_LEN;
+ current_val = cev + iwe_stream_lcp_len(info);
iwe.cmd = SIOCGIWRATE;
for (i = 0; i < sizeof(bssid->rates); i++) {
if (bssid->rates[i] & 0x7f) {
@@ -1707,13 +1727,13 @@ static char *rndis_translate_scan(struct net_device *dev,
((bssid->rates[i] & 0x7f) *
500000);
devdbg(usbdev, " %d", iwe.u.bitrate.value);
- current_val = iwe_stream_add_value(cev,
+ current_val = iwe_stream_add_value(info, cev,
current_val, end_buf, &iwe,
IW_EV_PARAM_LEN);
}
}
- if ((current_val - cev) > IW_EV_LCP_LEN)
+ if ((current_val - cev) > iwe_stream_lcp_len(info))
cev = current_val;
beacon = le32_to_cpu(bssid->config.beacon_period);
@@ -1721,14 +1741,14 @@ static char *rndis_translate_scan(struct net_device *dev,
iwe.cmd = IWEVCUSTOM;
snprintf(sbuf, sizeof(sbuf), "bcn_int=%d", beacon);
iwe.u.data.length = strlen(sbuf);
- cev = iwe_stream_add_point(cev, end_buf, &iwe, 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(cev, end_buf, &iwe, 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),
@@ -1742,7 +1762,7 @@ static char *rndis_translate_scan(struct net_device *dev,
(ie->id == MFIE_TYPE_RSN) ? 2 : 1);
iwe.cmd = IWEVGENIE;
iwe.u.data.length = min(ie->len + 2, MAX_WPA_IE_LEN);
- cev = iwe_stream_add_point(cev, end_buf, &iwe,
+ cev = iwe_stream_add_point(info, cev, end_buf, &iwe,
(u8 *)ie);
}
@@ -1785,8 +1805,8 @@ static int rndis_iw_get_scan(struct net_device *dev,
devdbg(usbdev, "SIOCGIWSCAN: %d BSSIDs found", count);
while (count && ((void *)bssid + bssid_len) <= (buf + len)) {
- cev = rndis_translate_scan(dev, cev, extra + IW_SCAN_MAX_DATA,
- bssid);
+ 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--;
@@ -2213,7 +2233,9 @@ static void rndis_wext_worker(struct work_struct *work)
int assoc_size = sizeof(*info) + IW_CUSTOM_MAX + 32;
int ret, offset;
- if (test_and_clear_bit(WORK_CONNECTION_EVENT, &priv->work_pending)) {
+ if (test_and_clear_bit(WORK_LINK_UP, &priv->work_pending)) {
+ netif_carrier_on(usbdev->net);
+
info = kzalloc(assoc_size, GFP_KERNEL);
if (!info)
goto get_bssid;
@@ -2251,6 +2273,15 @@ get_bssid:
}
}
+ if (test_and_clear_bit(WORK_LINK_DOWN, &priv->work_pending)) {
+ netif_carrier_off(usbdev->net);
+
+ evt.data.flags = 0;
+ evt.data.length = 0;
+ memset(evt.ap_addr.sa_data, 0, ETH_ALEN);
+ wireless_send_event(usbdev->net, SIOCGIWAP, &evt, NULL);
+ }
+
if (test_and_clear_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending))
set_multicast_list(usbdev);
}
@@ -2260,29 +2291,24 @@ static void rndis_wext_set_multicast_list(struct net_device *dev)
struct usbnet *usbdev = dev->priv;
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+ if (test_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending))
+ return;
+
set_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending);
queue_work(priv->workqueue, &priv->work);
}
-static void rndis_wext_link_change(struct usbnet *dev, int state)
+static void rndis_wext_link_change(struct usbnet *usbdev, int state)
{
- struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
- union iwreq_data evt;
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
- if (state) {
- /* queue work to avoid recursive calls into rndis_command */
- set_bit(WORK_CONNECTION_EVENT, &priv->work_pending);
- queue_work(priv->workqueue, &priv->work);
- } else {
- evt.data.flags = 0;
- evt.data.length = 0;
- memset(evt.ap_addr.sa_data, 0, ETH_ALEN);
- wireless_send_event(dev->net, SIOCGIWAP, &evt, NULL);
- }
+ /* queue work to avoid recursive calls into rndis_command */
+ set_bit(state ? WORK_LINK_UP : WORK_LINK_DOWN, &priv->work_pending);
+ queue_work(priv->workqueue, &priv->work);
}
-static int rndis_wext_get_caps(struct usbnet *dev)
+static int rndis_wext_get_caps(struct usbnet *usbdev)
{
struct {
__le32 num_items;
@@ -2290,18 +2316,18 @@ static int rndis_wext_get_caps(struct usbnet *dev)
} networks_supported;
int len, retval, i, n;
__le32 tx_power;
- struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
/* determine if supports setting txpower */
len = sizeof(tx_power);
- retval = rndis_query_oid(dev, OID_802_11_TX_POWER_LEVEL, &tx_power,
- &len);
+ 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;
/* determine supported modes */
len = sizeof(networks_supported);
- retval = rndis_query_oid(dev, OID_802_11_NETWORK_TYPES_SUPPORTED,
+ retval = rndis_query_oid(usbdev, OID_802_11_NETWORK_TYPES_SUPPORTED,
&networks_supported, &len);
if (retval >= 0) {
n = le32_to_cpu(networks_supported.num_items);
@@ -2440,9 +2466,9 @@ end:
}
-static int bcm4320_early_init(struct usbnet *dev)
+static int bcm4320_early_init(struct usbnet *usbdev)
{
- struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
char buf[8];
/* Early initialization settings, setting these won't have effect
@@ -2490,51 +2516,48 @@ static int bcm4320_early_init(struct usbnet *dev)
else
priv->param_workaround_interval = modparam_workaround_interval;
- rndis_set_config_parameter_str(dev, "Country", priv->param_country);
- rndis_set_config_parameter_str(dev, "FrameBursting",
+ rndis_set_config_parameter_str(usbdev, "Country", priv->param_country);
+ rndis_set_config_parameter_str(usbdev, "FrameBursting",
priv->param_frameburst ? "1" : "0");
- rndis_set_config_parameter_str(dev, "Afterburner",
+ rndis_set_config_parameter_str(usbdev, "Afterburner",
priv->param_afterburner ? "1" : "0");
sprintf(buf, "%d", priv->param_power_save);
- rndis_set_config_parameter_str(dev, "PowerSaveMode", buf);
+ rndis_set_config_parameter_str(usbdev, "PowerSaveMode", buf);
sprintf(buf, "%d", priv->param_power_output);
- rndis_set_config_parameter_str(dev, "PwrOut", buf);
+ rndis_set_config_parameter_str(usbdev, "PwrOut", buf);
sprintf(buf, "%d", priv->param_roamtrigger);
- rndis_set_config_parameter_str(dev, "RoamTrigger", buf);
+ rndis_set_config_parameter_str(usbdev, "RoamTrigger", buf);
sprintf(buf, "%d", priv->param_roamdelta);
- rndis_set_config_parameter_str(dev, "RoamDelta", buf);
+ rndis_set_config_parameter_str(usbdev, "RoamDelta", buf);
return 0;
}
-static int rndis_wext_bind(struct usbnet *dev, struct usb_interface *intf)
+static int rndis_wext_bind(struct usbnet *usbdev, struct usb_interface *intf)
{
- struct net_device *net = dev->net;
struct rndis_wext_private *priv;
int retval, len;
__le32 tmp;
/* allocate rndis private data */
- priv = kmalloc(sizeof(struct rndis_wext_private), GFP_KERNEL);
+ priv = kzalloc(sizeof(struct rndis_wext_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
/* These have to be initialized before calling generic_rndis_bind().
* Otherwise we'll be in big trouble in rndis_wext_early_init().
*/
- dev->driver_priv = priv;
- memset(priv, 0, sizeof(*priv));
- memset(priv->name, 0, sizeof(priv->name));
+ usbdev->driver_priv = priv;
strcpy(priv->name, "IEEE802.11");
- net->wireless_handlers = &rndis_iw_handlers;
- priv->usbdev = dev;
+ usbdev->net->wireless_handlers = &rndis_iw_handlers;
+ priv->usbdev = usbdev;
mutex_init(&priv->command_lock);
spin_lock_init(&priv->stats_lock);
/* try bind rndis_host */
- retval = generic_rndis_bind(dev, intf, FLAG_RNDIS_PHYM_WIRELESS);
+ retval = generic_rndis_bind(usbdev, intf, FLAG_RNDIS_PHYM_WIRELESS);
if (retval < 0)
goto fail;
@@ -2545,20 +2568,21 @@ static int rndis_wext_bind(struct usbnet *dev, struct usb_interface *intf)
* rndis_host wants to avoid all OID as much as possible
* so do promisc/multicast handling in rndis_wext.
*/
- dev->net->set_multicast_list = rndis_wext_set_multicast_list;
+ usbdev->net->set_multicast_list = rndis_wext_set_multicast_list;
tmp = RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_BROADCAST;
- retval = rndis_set_oid(dev, OID_GEN_CURRENT_PACKET_FILTER, &tmp,
+ retval = rndis_set_oid(usbdev, OID_GEN_CURRENT_PACKET_FILTER, &tmp,
sizeof(tmp));
len = sizeof(tmp);
- retval = rndis_query_oid(dev, OID_802_3_MAXIMUM_LIST_SIZE, &tmp, &len);
+ retval = rndis_query_oid(usbdev, OID_802_3_MAXIMUM_LIST_SIZE, &tmp,
+ &len);
priv->multicast_size = le32_to_cpu(tmp);
if (retval < 0 || priv->multicast_size < 0)
priv->multicast_size = 0;
if (priv->multicast_size > 0)
- dev->net->flags |= IFF_MULTICAST;
+ usbdev->net->flags |= IFF_MULTICAST;
else
- dev->net->flags &= ~IFF_MULTICAST;
+ usbdev->net->flags &= ~IFF_MULTICAST;
priv->iwstats.qual.qual = 0;
priv->iwstats.qual.level = 0;
@@ -2568,12 +2592,13 @@ static int rndis_wext_bind(struct usbnet *dev, struct usb_interface *intf)
| IW_QUAL_QUAL_INVALID
| IW_QUAL_LEVEL_INVALID;
- rndis_wext_get_caps(dev);
- set_default_iw_params(dev);
+ rndis_wext_get_caps(usbdev);
+ set_default_iw_params(usbdev);
/* turn radio on */
priv->radio_on = 1;
- disassociate(dev, 1);
+ disassociate(usbdev, 1);
+ netif_carrier_off(usbdev->net);
/* because rndis_command() sleeps we need to use workqueue */
priv->workqueue = create_singlethread_workqueue("rndis_wlan");
@@ -2590,12 +2615,12 @@ fail:
}
-static void rndis_wext_unbind(struct usbnet *dev, struct usb_interface *intf)
+static void rndis_wext_unbind(struct usbnet *usbdev, struct usb_interface *intf)
{
- struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
/* turn radio off */
- disassociate(dev, 0);
+ disassociate(usbdev, 0);
cancel_delayed_work_sync(&priv->stats_work);
cancel_work_sync(&priv->work);
@@ -2606,13 +2631,13 @@ static void rndis_wext_unbind(struct usbnet *dev, struct usb_interface *intf)
kfree(priv->wpa_ie);
kfree(priv);
- rndis_unbind(dev, intf);
+ rndis_unbind(usbdev, intf);
}
-static int rndis_wext_reset(struct usbnet *dev)
+static int rndis_wext_reset(struct usbnet *usbdev)
{
- return deauthenticate(dev);
+ return deauthenticate(usbdev);
}
OpenPOWER on IntegriCloud