blob: c995d7c3139c422c3d0c0ad471c0ace4f6676a4d [file] [log] [blame]
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001/*
2 * Driver for RNDIS based wireless USB devices.
3 *
4 * Copyright (C) 2007 by Bjorge Dijkstra <bjd@jooz.net>
5 * Copyright (C) 2008 by Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 * Portions of this file are based on NDISwrapper project,
22 * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
23 * http://ndiswrapper.sourceforge.net/
24 */
25
26// #define DEBUG // error path messages, extra info
27// #define VERBOSE // more; success messages
28
29#include <linux/module.h>
30#include <linux/init.h>
31#include <linux/netdevice.h>
32#include <linux/etherdevice.h>
33#include <linux/ethtool.h>
34#include <linux/workqueue.h>
35#include <linux/mutex.h>
36#include <linux/mii.h>
37#include <linux/usb.h>
38#include <linux/usb/cdc.h>
39#include <linux/wireless.h>
Johannes Berg2c7060022008-10-30 22:09:54 +010040#include <linux/ieee80211.h>
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +020041#include <linux/if_arp.h>
42#include <linux/ctype.h>
43#include <linux/spinlock.h>
44#include <net/iw_handler.h>
John W. Linville5c8fa4f2009-03-26 23:39:53 +020045#include <net/wireless.h>
46#include <net/cfg80211.h>
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +020047#include <linux/usb/usbnet.h>
48#include <linux/usb/rndis_host.h>
49
50
51/* NOTE: All these are settings for Broadcom chipset */
52static char modparam_country[4] = "EU";
53module_param_string(country, modparam_country, 4, 0444);
54MODULE_PARM_DESC(country, "Country code (ISO 3166-1 alpha-2), default: EU");
55
56static int modparam_frameburst = 1;
57module_param_named(frameburst, modparam_frameburst, int, 0444);
58MODULE_PARM_DESC(frameburst, "enable frame bursting (default: on)");
59
60static int modparam_afterburner = 0;
61module_param_named(afterburner, modparam_afterburner, int, 0444);
62MODULE_PARM_DESC(afterburner,
63 "enable afterburner aka '125 High Speed Mode' (default: off)");
64
65static int modparam_power_save = 0;
66module_param_named(power_save, modparam_power_save, int, 0444);
67MODULE_PARM_DESC(power_save,
68 "set power save mode: 0=off, 1=on, 2=fast (default: off)");
69
70static int modparam_power_output = 3;
71module_param_named(power_output, modparam_power_output, int, 0444);
72MODULE_PARM_DESC(power_output,
73 "set power output: 0=25%, 1=50%, 2=75%, 3=100% (default: 100%)");
74
75static int modparam_roamtrigger = -70;
76module_param_named(roamtrigger, modparam_roamtrigger, int, 0444);
77MODULE_PARM_DESC(roamtrigger,
78 "set roaming dBm trigger: -80=optimize for distance, "
79 "-60=bandwidth (default: -70)");
80
81static int modparam_roamdelta = 1;
82module_param_named(roamdelta, modparam_roamdelta, int, 0444);
83MODULE_PARM_DESC(roamdelta,
84 "set roaming tendency: 0=aggressive, 1=moderate, "
85 "2=conservative (default: moderate)");
86
87static int modparam_workaround_interval = 500;
88module_param_named(workaround_interval, modparam_workaround_interval,
89 int, 0444);
90MODULE_PARM_DESC(workaround_interval,
91 "set stall workaround interval in msecs (default: 500)");
92
93
94/* various RNDIS OID defs */
Harvey Harrison35c26c22009-02-14 22:56:56 -080095#define OID_GEN_LINK_SPEED cpu_to_le32(0x00010107)
96#define OID_GEN_RNDIS_CONFIG_PARAMETER cpu_to_le32(0x0001021b)
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +020097
Harvey Harrison35c26c22009-02-14 22:56:56 -080098#define OID_GEN_XMIT_OK cpu_to_le32(0x00020101)
99#define OID_GEN_RCV_OK cpu_to_le32(0x00020102)
100#define OID_GEN_XMIT_ERROR cpu_to_le32(0x00020103)
101#define OID_GEN_RCV_ERROR cpu_to_le32(0x00020104)
102#define OID_GEN_RCV_NO_BUFFER cpu_to_le32(0x00020105)
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200103
Harvey Harrison35c26c22009-02-14 22:56:56 -0800104#define OID_802_3_PERMANENT_ADDRESS cpu_to_le32(0x01010101)
105#define OID_802_3_CURRENT_ADDRESS cpu_to_le32(0x01010102)
106#define OID_802_3_MULTICAST_LIST cpu_to_le32(0x01010103)
107#define OID_802_3_MAXIMUM_LIST_SIZE cpu_to_le32(0x01010104)
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200108
Harvey Harrison35c26c22009-02-14 22:56:56 -0800109#define OID_802_11_BSSID cpu_to_le32(0x0d010101)
110#define OID_802_11_SSID cpu_to_le32(0x0d010102)
111#define OID_802_11_INFRASTRUCTURE_MODE cpu_to_le32(0x0d010108)
112#define OID_802_11_ADD_WEP cpu_to_le32(0x0d010113)
113#define OID_802_11_REMOVE_WEP cpu_to_le32(0x0d010114)
114#define OID_802_11_DISASSOCIATE cpu_to_le32(0x0d010115)
115#define OID_802_11_AUTHENTICATION_MODE cpu_to_le32(0x0d010118)
116#define OID_802_11_PRIVACY_FILTER cpu_to_le32(0x0d010119)
117#define OID_802_11_BSSID_LIST_SCAN cpu_to_le32(0x0d01011a)
118#define OID_802_11_ENCRYPTION_STATUS cpu_to_le32(0x0d01011b)
119#define OID_802_11_ADD_KEY cpu_to_le32(0x0d01011d)
120#define OID_802_11_REMOVE_KEY cpu_to_le32(0x0d01011e)
121#define OID_802_11_ASSOCIATION_INFORMATION cpu_to_le32(0x0d01011f)
122#define OID_802_11_PMKID cpu_to_le32(0x0d010123)
123#define OID_802_11_NETWORK_TYPES_SUPPORTED cpu_to_le32(0x0d010203)
124#define OID_802_11_NETWORK_TYPE_IN_USE cpu_to_le32(0x0d010204)
125#define OID_802_11_TX_POWER_LEVEL cpu_to_le32(0x0d010205)
126#define OID_802_11_RSSI cpu_to_le32(0x0d010206)
127#define OID_802_11_RSSI_TRIGGER cpu_to_le32(0x0d010207)
128#define OID_802_11_FRAGMENTATION_THRESHOLD cpu_to_le32(0x0d010209)
129#define OID_802_11_RTS_THRESHOLD cpu_to_le32(0x0d01020a)
130#define OID_802_11_SUPPORTED_RATES cpu_to_le32(0x0d01020e)
131#define OID_802_11_CONFIGURATION cpu_to_le32(0x0d010211)
132#define OID_802_11_BSSID_LIST cpu_to_le32(0x0d010217)
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200133
134
135/* Typical noise/maximum signal level values taken from ndiswrapper iw_ndis.h */
136#define WL_NOISE -96 /* typical noise level in dBm */
137#define WL_SIGMAX -32 /* typical maximum signal level in dBm */
138
139
140/* Assume that Broadcom 4320 (only chipset at time of writing known to be
141 * based on wireless rndis) has default txpower of 13dBm.
142 * This value is from Linksys WUSB54GSC User Guide, Appendix F: Specifications.
143 * 13dBm == 19.9mW
144 */
145#define BCM4320_DEFAULT_TXPOWER 20
146
147
148/* codes for "status" field of completion messages */
Harvey Harrison35c26c22009-02-14 22:56:56 -0800149#define RNDIS_STATUS_ADAPTER_NOT_READY cpu_to_le32(0xc0010011)
150#define RNDIS_STATUS_ADAPTER_NOT_OPEN cpu_to_le32(0xc0010012)
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200151
152
153/* NDIS data structures. Taken from wpa_supplicant driver_ndis.c
154 * slightly modified for datatype endianess, etc
155 */
156#define NDIS_802_11_LENGTH_SSID 32
157#define NDIS_802_11_LENGTH_RATES 8
158#define NDIS_802_11_LENGTH_RATES_EX 16
159
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200160enum ndis_80211_net_type {
161 ndis_80211_type_freq_hop,
162 ndis_80211_type_direct_seq,
163 ndis_80211_type_ofdm_a,
164 ndis_80211_type_ofdm_g
165};
166
167enum ndis_80211_net_infra {
168 ndis_80211_infra_adhoc,
169 ndis_80211_infra_infra,
170 ndis_80211_infra_auto_unknown
171};
172
173enum ndis_80211_auth_mode {
174 ndis_80211_auth_open,
175 ndis_80211_auth_shared,
176 ndis_80211_auth_auto_switch,
177 ndis_80211_auth_wpa,
178 ndis_80211_auth_wpa_psk,
179 ndis_80211_auth_wpa_none,
180 ndis_80211_auth_wpa2,
181 ndis_80211_auth_wpa2_psk
182};
183
184enum ndis_80211_encr_status {
185 ndis_80211_encr_wep_enabled,
186 ndis_80211_encr_disabled,
187 ndis_80211_encr_wep_key_absent,
188 ndis_80211_encr_not_supported,
189 ndis_80211_encr_tkip_enabled,
190 ndis_80211_encr_tkip_key_absent,
191 ndis_80211_encr_ccmp_enabled,
192 ndis_80211_encr_ccmp_key_absent
193};
194
195enum ndis_80211_priv_filter {
196 ndis_80211_priv_accept_all,
197 ndis_80211_priv_8021x_wep
198};
199
200struct ndis_80211_ssid {
Jussi Kivilinna461b3f22008-03-08 01:23:25 +0200201 __le32 length;
202 u8 essid[NDIS_802_11_LENGTH_SSID];
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200203} __attribute__((packed));
204
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200205struct ndis_80211_conf_freq_hop {
Jussi Kivilinna461b3f22008-03-08 01:23:25 +0200206 __le32 length;
207 __le32 hop_pattern;
208 __le32 hop_set;
209 __le32 dwell_time;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200210} __attribute__((packed));
211
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200212struct ndis_80211_conf {
Jussi Kivilinna461b3f22008-03-08 01:23:25 +0200213 __le32 length;
214 __le32 beacon_period;
215 __le32 atim_window;
216 __le32 ds_config;
217 struct ndis_80211_conf_freq_hop fh_config;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200218} __attribute__((packed));
219
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200220struct ndis_80211_bssid_ex {
Jussi Kivilinna461b3f22008-03-08 01:23:25 +0200221 __le32 length;
222 u8 mac[6];
223 u8 padding[2];
224 struct ndis_80211_ssid ssid;
225 __le32 privacy;
226 __le32 rssi;
227 __le32 net_type;
228 struct ndis_80211_conf config;
229 __le32 net_infra;
230 u8 rates[NDIS_802_11_LENGTH_RATES_EX];
231 __le32 ie_length;
232 u8 ies[0];
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200233} __attribute__((packed));
234
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200235struct ndis_80211_bssid_list_ex {
Jussi Kivilinna461b3f22008-03-08 01:23:25 +0200236 __le32 num_items;
237 struct ndis_80211_bssid_ex bssid[0];
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200238} __attribute__((packed));
239
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200240struct ndis_80211_fixed_ies {
Jussi Kivilinna461b3f22008-03-08 01:23:25 +0200241 u8 timestamp[8];
242 __le16 beacon_interval;
243 __le16 capabilities;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200244} __attribute__((packed));
245
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200246struct ndis_80211_wep_key {
Jussi Kivilinna461b3f22008-03-08 01:23:25 +0200247 __le32 size;
248 __le32 index;
249 __le32 length;
250 u8 material[32];
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200251} __attribute__((packed));
252
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200253struct ndis_80211_key {
Jussi Kivilinna461b3f22008-03-08 01:23:25 +0200254 __le32 size;
255 __le32 index;
256 __le32 length;
257 u8 bssid[6];
258 u8 padding[6];
259 u8 rsc[8];
260 u8 material[32];
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200261} __attribute__((packed));
262
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200263struct ndis_80211_remove_key {
Jussi Kivilinna461b3f22008-03-08 01:23:25 +0200264 __le32 size;
265 __le32 index;
266 u8 bssid[6];
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200267} __attribute__((packed));
268
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200269struct ndis_config_param {
Jussi Kivilinna461b3f22008-03-08 01:23:25 +0200270 __le32 name_offs;
271 __le32 name_length;
272 __le32 type;
273 __le32 value_offs;
274 __le32 value_length;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200275} __attribute__((packed));
276
Scott Ashcroft43646232008-05-27 00:06:15 +0300277struct ndis_80211_assoc_info {
278 __le32 length;
279 __le16 req_ies;
280 struct req_ie {
281 __le16 capa;
282 __le16 listen_interval;
283 u8 cur_ap_address[6];
284 } req_ie;
285 __le32 req_ie_length;
286 __le32 offset_req_ies;
287 __le16 resp_ies;
288 struct resp_ie {
289 __le16 capa;
290 __le16 status_code;
291 __le16 assoc_id;
292 } resp_ie;
293 __le32 resp_ie_length;
294 __le32 offset_resp_ies;
295} __attribute__((packed));
296
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200297/* these have to match what is in wpa_supplicant */
Johannes Berg5b0acc62008-02-20 11:47:45 +0100298enum wpa_alg { WPA_ALG_NONE, WPA_ALG_WEP, WPA_ALG_TKIP, WPA_ALG_CCMP };
299enum wpa_cipher { CIPHER_NONE, CIPHER_WEP40, CIPHER_TKIP, CIPHER_CCMP,
300 CIPHER_WEP104 };
301enum wpa_key_mgmt { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE,
302 KEY_MGMT_802_1X_NO_WPA, KEY_MGMT_WPA_NONE };
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200303
304/*
305 * private data
306 */
307#define NET_TYPE_11FB 0
308
309#define CAP_MODE_80211A 1
310#define CAP_MODE_80211B 2
311#define CAP_MODE_80211G 4
312#define CAP_MODE_MASK 7
313#define CAP_SUPPORT_TXPOWER 8
314
Jussi Kivilinna6010ce02008-06-02 18:35:21 +0300315#define WORK_LINK_UP (1<<0)
316#define WORK_LINK_DOWN (1<<1)
317#define WORK_SET_MULTICAST_LIST (1<<2)
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200318
Jussi Kivilinna90d07342008-06-12 20:19:19 +0300319#define COMMAND_BUFFER_SIZE (CONTROL_BUFFER_SIZE + sizeof(struct rndis_set))
320
John W. Linville5c8fa4f2009-03-26 23:39:53 +0200321static const struct ieee80211_channel rndis_channels[] = {
322 { .center_freq = 2412 },
323 { .center_freq = 2417 },
324 { .center_freq = 2422 },
325 { .center_freq = 2427 },
326 { .center_freq = 2432 },
327 { .center_freq = 2437 },
328 { .center_freq = 2442 },
329 { .center_freq = 2447 },
330 { .center_freq = 2452 },
331 { .center_freq = 2457 },
332 { .center_freq = 2462 },
333 { .center_freq = 2467 },
334 { .center_freq = 2472 },
335 { .center_freq = 2484 },
336};
337
338static const struct ieee80211_rate rndis_rates[] = {
339 { .bitrate = 10 },
340 { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
341 { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
342 { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
343 { .bitrate = 60 },
344 { .bitrate = 90 },
345 { .bitrate = 120 },
346 { .bitrate = 180 },
347 { .bitrate = 240 },
348 { .bitrate = 360 },
349 { .bitrate = 480 },
350 { .bitrate = 540 }
351};
352
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200353/* RNDIS device private data */
354struct rndis_wext_private {
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200355 struct usbnet *usbdev;
356
John W. Linville5c8fa4f2009-03-26 23:39:53 +0200357 struct wireless_dev wdev;
358
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200359 struct workqueue_struct *workqueue;
360 struct delayed_work stats_work;
361 struct work_struct work;
362 struct mutex command_lock;
363 spinlock_t stats_lock;
364 unsigned long work_pending;
365
John W. Linville5c8fa4f2009-03-26 23:39:53 +0200366 struct ieee80211_supported_band band;
367 struct ieee80211_channel channels[ARRAY_SIZE(rndis_channels)];
368 struct ieee80211_rate rates[ARRAY_SIZE(rndis_rates)];
369
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200370 struct iw_statistics iwstats;
371 struct iw_statistics privstats;
372
373 int nick_len;
374 char nick[32];
375
376 int caps;
377 int multicast_size;
378
379 /* module parameters */
380 char param_country[4];
381 int param_frameburst;
382 int param_afterburner;
383 int param_power_save;
384 int param_power_output;
385 int param_roamtrigger;
386 int param_roamdelta;
387 u32 param_workaround_interval;
388
389 /* hardware state */
390 int radio_on;
391 int infra_mode;
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200392 struct ndis_80211_ssid essid;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200393
394 /* encryption stuff */
395 int encr_tx_key_index;
396 char encr_keys[4][32];
397 int encr_key_len[4];
398 int wpa_version;
399 int wpa_keymgmt;
400 int wpa_authalg;
401 int wpa_ie_len;
402 u8 *wpa_ie;
403 int wpa_cipher_pair;
404 int wpa_cipher_group;
Jussi Kivilinna90d07342008-06-12 20:19:19 +0300405
406 u8 command_buffer[COMMAND_BUFFER_SIZE];
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200407};
408
409
John W. Linville5c8fa4f2009-03-26 23:39:53 +0200410struct cfg80211_ops rndis_config_ops = { };
411void *rndis_wiphy_privid = &rndis_wiphy_privid;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200412
413static const int bcm4320_power_output[4] = { 25, 50, 75, 100 };
414
415static const unsigned char zero_bssid[ETH_ALEN] = {0,};
416static const unsigned char ffff_bssid[ETH_ALEN] = { 0xff, 0xff, 0xff,
417 0xff, 0xff, 0xff };
418
419
420static struct rndis_wext_private *get_rndis_wext_priv(struct usbnet *dev)
421{
422 return (struct rndis_wext_private *)dev->driver_priv;
423}
424
425
426static u32 get_bcm4320_power(struct rndis_wext_private *priv)
427{
428 return BCM4320_DEFAULT_TXPOWER *
429 bcm4320_power_output[priv->param_power_output] / 100;
430}
431
432
433/* translate error code */
434static int rndis_error_status(__le32 rndis_status)
435{
436 int ret = -EINVAL;
437 switch (rndis_status) {
438 case RNDIS_STATUS_SUCCESS:
439 ret = 0;
440 break;
441 case RNDIS_STATUS_FAILURE:
442 case RNDIS_STATUS_INVALID_DATA:
443 ret = -EINVAL;
444 break;
445 case RNDIS_STATUS_NOT_SUPPORTED:
446 ret = -EOPNOTSUPP;
447 break;
448 case RNDIS_STATUS_ADAPTER_NOT_READY:
449 case RNDIS_STATUS_ADAPTER_NOT_OPEN:
450 ret = -EBUSY;
451 break;
452 }
453 return ret;
454}
455
456
457static int rndis_query_oid(struct usbnet *dev, __le32 oid, void *data, int *len)
458{
459 struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
460 union {
461 void *buf;
462 struct rndis_msg_hdr *header;
463 struct rndis_query *get;
464 struct rndis_query_c *get_c;
465 } u;
466 int ret, buflen;
467
468 buflen = *len + sizeof(*u.get);
469 if (buflen < CONTROL_BUFFER_SIZE)
470 buflen = CONTROL_BUFFER_SIZE;
Jussi Kivilinna90d07342008-06-12 20:19:19 +0300471
472 if (buflen > COMMAND_BUFFER_SIZE) {
473 u.buf = kmalloc(buflen, GFP_KERNEL);
474 if (!u.buf)
475 return -ENOMEM;
476 } else {
477 u.buf = priv->command_buffer;
478 }
479
480 mutex_lock(&priv->command_lock);
481
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200482 memset(u.get, 0, sizeof *u.get);
483 u.get->msg_type = RNDIS_MSG_QUERY;
Harvey Harrison35c26c22009-02-14 22:56:56 -0800484 u.get->msg_len = cpu_to_le32(sizeof *u.get);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200485 u.get->oid = oid;
486
Jussi Kivilinna818727b2008-06-18 15:40:12 +0300487 ret = rndis_command(dev, u.header, buflen);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200488 if (ret == 0) {
489 ret = le32_to_cpu(u.get_c->len);
490 *len = (*len > ret) ? ret : *len;
491 memcpy(data, u.buf + le32_to_cpu(u.get_c->offset) + 8, *len);
492 ret = rndis_error_status(u.get_c->status);
493 }
494
Jussi Kivilinna90d07342008-06-12 20:19:19 +0300495 mutex_unlock(&priv->command_lock);
496
497 if (u.buf != priv->command_buffer)
498 kfree(u.buf);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200499 return ret;
500}
501
502
503static int rndis_set_oid(struct usbnet *dev, __le32 oid, void *data, int len)
504{
505 struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
506 union {
507 void *buf;
508 struct rndis_msg_hdr *header;
509 struct rndis_set *set;
510 struct rndis_set_c *set_c;
511 } u;
512 int ret, buflen;
513
514 buflen = len + sizeof(*u.set);
515 if (buflen < CONTROL_BUFFER_SIZE)
516 buflen = CONTROL_BUFFER_SIZE;
Jussi Kivilinna90d07342008-06-12 20:19:19 +0300517
518 if (buflen > COMMAND_BUFFER_SIZE) {
519 u.buf = kmalloc(buflen, GFP_KERNEL);
520 if (!u.buf)
521 return -ENOMEM;
522 } else {
523 u.buf = priv->command_buffer;
524 }
525
526 mutex_lock(&priv->command_lock);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200527
528 memset(u.set, 0, sizeof *u.set);
529 u.set->msg_type = RNDIS_MSG_SET;
530 u.set->msg_len = cpu_to_le32(sizeof(*u.set) + len);
531 u.set->oid = oid;
532 u.set->len = cpu_to_le32(len);
Harvey Harrison35c26c22009-02-14 22:56:56 -0800533 u.set->offset = cpu_to_le32(sizeof(*u.set) - 8);
534 u.set->handle = cpu_to_le32(0);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200535 memcpy(u.buf + sizeof(*u.set), data, len);
536
Jussi Kivilinna818727b2008-06-18 15:40:12 +0300537 ret = rndis_command(dev, u.header, buflen);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200538 if (ret == 0)
539 ret = rndis_error_status(u.set_c->status);
540
Jussi Kivilinna90d07342008-06-12 20:19:19 +0300541 mutex_unlock(&priv->command_lock);
542
543 if (u.buf != priv->command_buffer)
544 kfree(u.buf);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200545 return ret;
546}
547
548
549/*
550 * Specs say that we can only set config parameters only soon after device
551 * initialization.
552 * value_type: 0 = u32, 2 = unicode string
553 */
554static int rndis_set_config_parameter(struct usbnet *dev, char *param,
555 int value_type, void *value)
556{
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200557 struct ndis_config_param *infobuf;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200558 int value_len, info_len, param_len, ret, i;
559 __le16 *unibuf;
560 __le32 *dst_value;
561
562 if (value_type == 0)
563 value_len = sizeof(__le32);
564 else if (value_type == 2)
565 value_len = strlen(value) * sizeof(__le16);
566 else
567 return -EINVAL;
568
569 param_len = strlen(param) * sizeof(__le16);
570 info_len = sizeof(*infobuf) + param_len + value_len;
571
572#ifdef DEBUG
573 info_len += 12;
574#endif
575 infobuf = kmalloc(info_len, GFP_KERNEL);
576 if (!infobuf)
577 return -ENOMEM;
578
579#ifdef DEBUG
580 info_len -= 12;
581 /* extra 12 bytes are for padding (debug output) */
582 memset(infobuf, 0xCC, info_len + 12);
583#endif
584
585 if (value_type == 2)
586 devdbg(dev, "setting config parameter: %s, value: %s",
587 param, (u8 *)value);
588 else
589 devdbg(dev, "setting config parameter: %s, value: %d",
590 param, *(u32 *)value);
591
Jussi Kivilinna461b3f22008-03-08 01:23:25 +0200592 infobuf->name_offs = cpu_to_le32(sizeof(*infobuf));
593 infobuf->name_length = cpu_to_le32(param_len);
594 infobuf->type = cpu_to_le32(value_type);
595 infobuf->value_offs = cpu_to_le32(sizeof(*infobuf) + param_len);
596 infobuf->value_length = cpu_to_le32(value_len);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200597
598 /* simple string to unicode string conversion */
599 unibuf = (void *)infobuf + sizeof(*infobuf);
600 for (i = 0; i < param_len / sizeof(__le16); i++)
601 unibuf[i] = cpu_to_le16(param[i]);
602
603 if (value_type == 2) {
604 unibuf = (void *)infobuf + sizeof(*infobuf) + param_len;
605 for (i = 0; i < value_len / sizeof(__le16); i++)
606 unibuf[i] = cpu_to_le16(((u8 *)value)[i]);
607 } else {
608 dst_value = (void *)infobuf + sizeof(*infobuf) + param_len;
609 *dst_value = cpu_to_le32(*(u32 *)value);
610 }
611
612#ifdef DEBUG
613 devdbg(dev, "info buffer (len: %d):", info_len);
614 for (i = 0; i < info_len; i += 12) {
615 u32 *tmp = (u32 *)((u8 *)infobuf + i);
616 devdbg(dev, "%08X:%08X:%08X",
617 cpu_to_be32(tmp[0]),
618 cpu_to_be32(tmp[1]),
619 cpu_to_be32(tmp[2]));
620 }
621#endif
622
623 ret = rndis_set_oid(dev, OID_GEN_RNDIS_CONFIG_PARAMETER,
624 infobuf, info_len);
625 if (ret != 0)
626 devdbg(dev, "setting rndis config paramater failed, %d.", ret);
627
628 kfree(infobuf);
629 return ret;
630}
631
632static int rndis_set_config_parameter_str(struct usbnet *dev,
633 char *param, char *value)
634{
635 return(rndis_set_config_parameter(dev, param, 2, value));
636}
637
638/*static int rndis_set_config_parameter_u32(struct usbnet *dev,
639 char *param, u32 value)
640{
641 return(rndis_set_config_parameter(dev, param, 0, &value));
642}*/
643
644
645/*
646 * data conversion functions
647 */
648static int level_to_qual(int level)
649{
650 int qual = 100 * (level - WL_NOISE) / (WL_SIGMAX - WL_NOISE);
651 return qual >= 0 ? (qual <= 100 ? qual : 100) : 0;
652}
653
654
655static void dsconfig_to_freq(unsigned int dsconfig, struct iw_freq *freq)
656{
657 freq->e = 0;
658 freq->i = 0;
659 freq->flags = 0;
660
661 /* see comment in wireless.h above the "struct iw_freq"
662 * definition for an explanation of this if
663 * NOTE: 1000000 is due to the kHz
664 */
665 if (dsconfig > 1000000) {
666 freq->m = dsconfig / 10;
667 freq->e = 1;
668 } else
669 freq->m = dsconfig;
670
671 /* convert from kHz to Hz */
672 freq->e += 3;
673}
674
675
676static int freq_to_dsconfig(struct iw_freq *freq, unsigned int *dsconfig)
677{
678 if (freq->m < 1000 && freq->e == 0) {
David Kilroy9ee677c2008-12-23 14:03:38 +0000679 if (freq->m >= 1 && freq->m <= 14)
680 *dsconfig = ieee80211_dsss_chan_to_freq(freq->m) * 1000;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200681 else
682 return -1;
683 } else {
684 int i;
685 *dsconfig = freq->m;
686 for (i = freq->e; i > 0; i--)
687 *dsconfig *= 10;
688 *dsconfig /= 1000;
689 }
690
691 return 0;
692}
693
694
695/*
696 * common functions
697 */
698static int
699add_wep_key(struct usbnet *usbdev, char *key, int key_len, int index);
700
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200701static int get_essid(struct usbnet *usbdev, struct ndis_80211_ssid *ssid)
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200702{
703 int ret, len;
704
705 len = sizeof(*ssid);
706 ret = rndis_query_oid(usbdev, OID_802_11_SSID, ssid, &len);
707
708 if (ret != 0)
Jussi Kivilinna461b3f22008-03-08 01:23:25 +0200709 ssid->length = 0;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200710
711#ifdef DEBUG
712 {
713 unsigned char tmp[NDIS_802_11_LENGTH_SSID + 1];
714
Jussi Kivilinna461b3f22008-03-08 01:23:25 +0200715 memcpy(tmp, ssid->essid, le32_to_cpu(ssid->length));
716 tmp[le32_to_cpu(ssid->length)] = 0;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200717 devdbg(usbdev, "get_essid: '%s', ret: %d", tmp, ret);
718 }
719#endif
720 return ret;
721}
722
723
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200724static int set_essid(struct usbnet *usbdev, struct ndis_80211_ssid *ssid)
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200725{
726 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
727 int ret;
728
729 ret = rndis_set_oid(usbdev, OID_802_11_SSID, ssid, sizeof(*ssid));
730 if (ret == 0) {
731 memcpy(&priv->essid, ssid, sizeof(priv->essid));
732 priv->radio_on = 1;
733 devdbg(usbdev, "set_essid: radio_on = 1");
734 }
735
736 return ret;
737}
738
739
740static int get_bssid(struct usbnet *usbdev, u8 bssid[ETH_ALEN])
741{
742 int ret, len;
743
744 len = ETH_ALEN;
745 ret = rndis_query_oid(usbdev, OID_802_11_BSSID, bssid, &len);
746
747 if (ret != 0)
748 memset(bssid, 0, ETH_ALEN);
749
750 return ret;
751}
752
Scott Ashcroft43646232008-05-27 00:06:15 +0300753static int get_association_info(struct usbnet *usbdev,
754 struct ndis_80211_assoc_info *info, int len)
755{
756 return rndis_query_oid(usbdev, OID_802_11_ASSOCIATION_INFORMATION,
757 info, &len);
758}
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200759
760static int is_associated(struct usbnet *usbdev)
761{
762 u8 bssid[ETH_ALEN];
763 int ret;
764
765 ret = get_bssid(usbdev, bssid);
766
767 return(ret == 0 && memcmp(bssid, zero_bssid, ETH_ALEN) != 0);
768}
769
770
771static int disassociate(struct usbnet *usbdev, int reset_ssid)
772{
773 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200774 struct ndis_80211_ssid ssid;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200775 int i, ret = 0;
776
777 if (priv->radio_on) {
778 ret = rndis_set_oid(usbdev, OID_802_11_DISASSOCIATE, NULL, 0);
779 if (ret == 0) {
780 priv->radio_on = 0;
781 devdbg(usbdev, "disassociate: radio_on = 0");
782
783 if (reset_ssid)
784 msleep(100);
785 }
786 }
787
788 /* disassociate causes radio to be turned off; if reset_ssid
789 * is given, set random ssid to enable radio */
790 if (reset_ssid) {
Jussi Kivilinna461b3f22008-03-08 01:23:25 +0200791 ssid.length = cpu_to_le32(sizeof(ssid.essid));
792 get_random_bytes(&ssid.essid[2], sizeof(ssid.essid)-2);
793 ssid.essid[0] = 0x1;
794 ssid.essid[1] = 0xff;
795 for (i = 2; i < sizeof(ssid.essid); i++)
796 ssid.essid[i] = 0x1 + (ssid.essid[i] * 0xfe / 0xff);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200797 ret = set_essid(usbdev, &ssid);
798 }
799 return ret;
800}
801
802
803static int set_auth_mode(struct usbnet *usbdev, int wpa_version, int authalg)
804{
805 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
806 __le32 tmp;
807 int auth_mode, ret;
808
809 devdbg(usbdev, "set_auth_mode: wpa_version=0x%x authalg=0x%x "
810 "keymgmt=0x%x", wpa_version, authalg, priv->wpa_keymgmt);
811
812 if (wpa_version & IW_AUTH_WPA_VERSION_WPA2) {
813 if (priv->wpa_keymgmt & IW_AUTH_KEY_MGMT_802_1X)
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200814 auth_mode = ndis_80211_auth_wpa2;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200815 else
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200816 auth_mode = ndis_80211_auth_wpa2_psk;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200817 } else if (wpa_version & IW_AUTH_WPA_VERSION_WPA) {
818 if (priv->wpa_keymgmt & IW_AUTH_KEY_MGMT_802_1X)
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200819 auth_mode = ndis_80211_auth_wpa;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200820 else if (priv->wpa_keymgmt & IW_AUTH_KEY_MGMT_PSK)
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200821 auth_mode = ndis_80211_auth_wpa_psk;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200822 else
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200823 auth_mode = ndis_80211_auth_wpa_none;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200824 } else if (authalg & IW_AUTH_ALG_SHARED_KEY) {
825 if (authalg & IW_AUTH_ALG_OPEN_SYSTEM)
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200826 auth_mode = ndis_80211_auth_auto_switch;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200827 else
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200828 auth_mode = ndis_80211_auth_shared;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200829 } else
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200830 auth_mode = ndis_80211_auth_open;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200831
832 tmp = cpu_to_le32(auth_mode);
833 ret = rndis_set_oid(usbdev, OID_802_11_AUTHENTICATION_MODE, &tmp,
834 sizeof(tmp));
835 if (ret != 0) {
836 devwarn(usbdev, "setting auth mode failed (%08X)", ret);
837 return ret;
838 }
839
840 priv->wpa_version = wpa_version;
841 priv->wpa_authalg = authalg;
842 return 0;
843}
844
845
846static int set_priv_filter(struct usbnet *usbdev)
847{
848 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
849 __le32 tmp;
850
851 devdbg(usbdev, "set_priv_filter: wpa_version=0x%x", priv->wpa_version);
852
853 if (priv->wpa_version & IW_AUTH_WPA_VERSION_WPA2 ||
854 priv->wpa_version & IW_AUTH_WPA_VERSION_WPA)
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200855 tmp = cpu_to_le32(ndis_80211_priv_8021x_wep);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200856 else
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200857 tmp = cpu_to_le32(ndis_80211_priv_accept_all);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200858
859 return rndis_set_oid(usbdev, OID_802_11_PRIVACY_FILTER, &tmp,
860 sizeof(tmp));
861}
862
863
864static int set_encr_mode(struct usbnet *usbdev, int pairwise, int groupwise)
865{
866 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
867 __le32 tmp;
868 int encr_mode, ret;
869
870 devdbg(usbdev, "set_encr_mode: cipher_pair=0x%x cipher_group=0x%x",
871 pairwise,
872 groupwise);
873
874 if (pairwise & IW_AUTH_CIPHER_CCMP)
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200875 encr_mode = ndis_80211_encr_ccmp_enabled;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200876 else if (pairwise & IW_AUTH_CIPHER_TKIP)
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200877 encr_mode = ndis_80211_encr_tkip_enabled;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200878 else if (pairwise &
879 (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104))
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200880 encr_mode = ndis_80211_encr_wep_enabled;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200881 else if (groupwise & IW_AUTH_CIPHER_CCMP)
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200882 encr_mode = ndis_80211_encr_ccmp_enabled;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200883 else if (groupwise & IW_AUTH_CIPHER_TKIP)
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200884 encr_mode = ndis_80211_encr_tkip_enabled;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200885 else
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200886 encr_mode = ndis_80211_encr_disabled;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200887
888 tmp = cpu_to_le32(encr_mode);
889 ret = rndis_set_oid(usbdev, OID_802_11_ENCRYPTION_STATUS, &tmp,
890 sizeof(tmp));
891 if (ret != 0) {
892 devwarn(usbdev, "setting encr mode failed (%08X)", ret);
893 return ret;
894 }
895
896 priv->wpa_cipher_pair = pairwise;
897 priv->wpa_cipher_group = groupwise;
898 return 0;
899}
900
901
902static int set_assoc_params(struct usbnet *usbdev)
903{
904 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
905
906 set_auth_mode(usbdev, priv->wpa_version, priv->wpa_authalg);
907 set_priv_filter(usbdev);
908 set_encr_mode(usbdev, priv->wpa_cipher_pair, priv->wpa_cipher_group);
909
910 return 0;
911}
912
913
914static int set_infra_mode(struct usbnet *usbdev, int mode)
915{
916 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
917 __le32 tmp;
918 int ret, i;
919
920 devdbg(usbdev, "set_infra_mode: infra_mode=0x%x", priv->infra_mode);
921
922 tmp = cpu_to_le32(mode);
923 ret = rndis_set_oid(usbdev, OID_802_11_INFRASTRUCTURE_MODE, &tmp,
924 sizeof(tmp));
925 if (ret != 0) {
926 devwarn(usbdev, "setting infra mode failed (%08X)", ret);
927 return ret;
928 }
929
930 /* NDIS drivers clear keys when infrastructure mode is
931 * changed. But Linux tools assume otherwise. So set the
932 * keys */
933 if (priv->wpa_keymgmt == 0 ||
934 priv->wpa_keymgmt == IW_AUTH_KEY_MGMT_802_1X) {
935 for (i = 0; i < 4; i++) {
936 if (priv->encr_key_len[i] > 0)
937 add_wep_key(usbdev, priv->encr_keys[i],
938 priv->encr_key_len[i], i);
939 }
940 }
941
942 priv->infra_mode = mode;
943 return 0;
944}
945
946
947static void set_default_iw_params(struct usbnet *usbdev)
948{
949 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
950
951 priv->wpa_keymgmt = 0;
952 priv->wpa_version = 0;
953
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200954 set_infra_mode(usbdev, ndis_80211_infra_infra);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200955 set_auth_mode(usbdev, IW_AUTH_WPA_VERSION_DISABLED,
956 IW_AUTH_ALG_OPEN_SYSTEM);
957 set_priv_filter(usbdev);
958 set_encr_mode(usbdev, IW_AUTH_CIPHER_NONE, IW_AUTH_CIPHER_NONE);
959}
960
961
962static int deauthenticate(struct usbnet *usbdev)
963{
964 int ret;
965
966 ret = disassociate(usbdev, 1);
967 set_default_iw_params(usbdev);
968 return ret;
969}
970
971
972/* index must be 0 - N, as per NDIS */
973static int add_wep_key(struct usbnet *usbdev, char *key, int key_len, int index)
974{
975 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +0200976 struct ndis_80211_wep_key ndis_key;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200977 int ret;
978
979 if (key_len <= 0 || key_len > 32 || index < 0 || index >= 4)
980 return -EINVAL;
981
982 memset(&ndis_key, 0, sizeof(ndis_key));
983
Jussi Kivilinna461b3f22008-03-08 01:23:25 +0200984 ndis_key.size = cpu_to_le32(sizeof(ndis_key));
985 ndis_key.length = cpu_to_le32(key_len);
986 ndis_key.index = cpu_to_le32(index);
987 memcpy(&ndis_key.material, key, key_len);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200988
989 if (index == priv->encr_tx_key_index) {
Jussi Kivilinna461b3f22008-03-08 01:23:25 +0200990 ndis_key.index |= cpu_to_le32(1 << 31);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +0200991 ret = set_encr_mode(usbdev, IW_AUTH_CIPHER_WEP104,
992 IW_AUTH_CIPHER_NONE);
993 if (ret)
994 devwarn(usbdev, "encryption couldn't be enabled (%08X)",
995 ret);
996 }
997
998 ret = rndis_set_oid(usbdev, OID_802_11_ADD_WEP, &ndis_key,
999 sizeof(ndis_key));
1000 if (ret != 0) {
1001 devwarn(usbdev, "adding encryption key %d failed (%08X)",
1002 index+1, ret);
1003 return ret;
1004 }
1005
1006 priv->encr_key_len[index] = key_len;
1007 memcpy(&priv->encr_keys[index], key, key_len);
1008
1009 return 0;
1010}
1011
1012
1013/* remove_key is for both wep and wpa */
1014static int remove_key(struct usbnet *usbdev, int index, u8 bssid[ETH_ALEN])
1015{
1016 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +02001017 struct ndis_80211_remove_key remove_key;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001018 __le32 keyindex;
1019 int ret;
1020
1021 if (priv->encr_key_len[index] == 0)
1022 return 0;
1023
1024 priv->encr_key_len[index] = 0;
1025 memset(&priv->encr_keys[index], 0, sizeof(priv->encr_keys[index]));
1026
1027 if (priv->wpa_cipher_pair == IW_AUTH_CIPHER_TKIP ||
1028 priv->wpa_cipher_pair == IW_AUTH_CIPHER_CCMP ||
1029 priv->wpa_cipher_group == IW_AUTH_CIPHER_TKIP ||
1030 priv->wpa_cipher_group == IW_AUTH_CIPHER_CCMP) {
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001031 remove_key.size = cpu_to_le32(sizeof(remove_key));
1032 remove_key.index = cpu_to_le32(index);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001033 if (bssid) {
1034 /* pairwise key */
1035 if (memcmp(bssid, ffff_bssid, ETH_ALEN) != 0)
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001036 remove_key.index |= cpu_to_le32(1 << 30);
1037 memcpy(remove_key.bssid, bssid,
1038 sizeof(remove_key.bssid));
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001039 } else
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001040 memset(remove_key.bssid, 0xff,
1041 sizeof(remove_key.bssid));
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001042
1043 ret = rndis_set_oid(usbdev, OID_802_11_REMOVE_KEY, &remove_key,
1044 sizeof(remove_key));
1045 if (ret != 0)
1046 return ret;
1047 } else {
1048 keyindex = cpu_to_le32(index);
1049 ret = rndis_set_oid(usbdev, OID_802_11_REMOVE_WEP, &keyindex,
1050 sizeof(keyindex));
1051 if (ret != 0) {
1052 devwarn(usbdev,
1053 "removing encryption key %d failed (%08X)",
1054 index, ret);
1055 return ret;
1056 }
1057 }
1058
1059 /* if it is transmit key, disable encryption */
1060 if (index == priv->encr_tx_key_index)
1061 set_encr_mode(usbdev, IW_AUTH_CIPHER_NONE, IW_AUTH_CIPHER_NONE);
1062
1063 return 0;
1064}
1065
1066
1067static void set_multicast_list(struct usbnet *usbdev)
1068{
1069 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
1070 struct dev_mc_list *mclist;
1071 __le32 filter;
1072 int ret, i, size;
1073 char *buf;
1074
1075 filter = RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_BROADCAST;
1076
1077 if (usbdev->net->flags & IFF_PROMISC) {
1078 filter |= RNDIS_PACKET_TYPE_PROMISCUOUS |
1079 RNDIS_PACKET_TYPE_ALL_LOCAL;
1080 } else if (usbdev->net->flags & IFF_ALLMULTI ||
1081 usbdev->net->mc_count > priv->multicast_size) {
1082 filter |= RNDIS_PACKET_TYPE_ALL_MULTICAST;
1083 } else if (usbdev->net->mc_count > 0) {
1084 size = min(priv->multicast_size, usbdev->net->mc_count);
1085 buf = kmalloc(size * ETH_ALEN, GFP_KERNEL);
1086 if (!buf) {
1087 devwarn(usbdev,
1088 "couldn't alloc %d bytes of memory",
1089 size * ETH_ALEN);
1090 return;
1091 }
1092
1093 mclist = usbdev->net->mc_list;
1094 for (i = 0; i < size && mclist; mclist = mclist->next) {
1095 if (mclist->dmi_addrlen != ETH_ALEN)
1096 continue;
1097
1098 memcpy(buf + i * ETH_ALEN, mclist->dmi_addr, ETH_ALEN);
1099 i++;
1100 }
1101
1102 ret = rndis_set_oid(usbdev, OID_802_3_MULTICAST_LIST, buf,
1103 i * ETH_ALEN);
1104 if (ret == 0 && i > 0)
1105 filter |= RNDIS_PACKET_TYPE_MULTICAST;
1106 else
1107 filter |= RNDIS_PACKET_TYPE_ALL_MULTICAST;
1108
1109 devdbg(usbdev, "OID_802_3_MULTICAST_LIST(%d, max: %d) -> %d",
1110 i, priv->multicast_size, ret);
1111
1112 kfree(buf);
1113 }
1114
1115 ret = rndis_set_oid(usbdev, OID_GEN_CURRENT_PACKET_FILTER, &filter,
1116 sizeof(filter));
1117 if (ret < 0) {
1118 devwarn(usbdev, "couldn't set packet filter: %08x",
1119 le32_to_cpu(filter));
1120 }
1121
1122 devdbg(usbdev, "OID_GEN_CURRENT_PACKET_FILTER(%08x) -> %d",
1123 le32_to_cpu(filter), ret);
1124}
1125
1126
1127/*
1128 * wireless extension handlers
1129 */
1130
1131static int rndis_iw_commit(struct net_device *dev,
1132 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
1133{
1134 /* dummy op */
1135 return 0;
1136}
1137
1138
1139static int rndis_iw_get_range(struct net_device *dev,
1140 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
1141{
1142 struct iw_range *range = (struct iw_range *)extra;
Wang Chen524ad0a2008-11-12 23:39:10 -08001143 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001144 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
1145 int len, ret, i, j, num, has_80211g_rates;
1146 u8 rates[8];
1147 __le32 tx_power;
1148
1149 devdbg(usbdev, "SIOCGIWRANGE");
1150
1151 /* clear iw_range struct */
1152 memset(range, 0, sizeof(*range));
1153 wrqu->data.length = sizeof(*range);
1154
1155 range->txpower_capa = IW_TXPOW_MWATT;
1156 range->num_txpower = 1;
1157 if (priv->caps & CAP_SUPPORT_TXPOWER) {
1158 len = sizeof(tx_power);
1159 ret = rndis_query_oid(usbdev, OID_802_11_TX_POWER_LEVEL,
1160 &tx_power, &len);
1161 if (ret == 0 && le32_to_cpu(tx_power) != 0xFF)
1162 range->txpower[0] = le32_to_cpu(tx_power);
1163 else
1164 range->txpower[0] = get_bcm4320_power(priv);
1165 } else
1166 range->txpower[0] = get_bcm4320_power(priv);
1167
1168 len = sizeof(rates);
1169 ret = rndis_query_oid(usbdev, OID_802_11_SUPPORTED_RATES, &rates,
1170 &len);
1171 has_80211g_rates = 0;
1172 if (ret == 0) {
1173 j = 0;
1174 for (i = 0; i < len; i++) {
1175 if (rates[i] == 0)
1176 break;
1177 range->bitrate[j] = (rates[i] & 0x7f) * 500000;
1178 /* check for non 802.11b rates */
1179 if (range->bitrate[j] == 6000000 ||
1180 range->bitrate[j] == 9000000 ||
1181 (range->bitrate[j] >= 12000000 &&
1182 range->bitrate[j] != 22000000))
1183 has_80211g_rates = 1;
1184 j++;
1185 }
1186 range->num_bitrates = j;
1187 } else
1188 range->num_bitrates = 0;
1189
1190 /* fill in 802.11g rates */
1191 if (has_80211g_rates) {
1192 num = range->num_bitrates;
John W. Linville5c8fa4f2009-03-26 23:39:53 +02001193 for (i = 4; i < ARRAY_SIZE(rndis_rates); i++) {
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001194 for (j = 0; j < num; j++) {
1195 if (range->bitrate[j] ==
John W. Linville5c8fa4f2009-03-26 23:39:53 +02001196 rndis_rates[i].bitrate * 100000)
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001197 break;
1198 }
1199 if (j == num)
1200 range->bitrate[range->num_bitrates++] =
John W. Linville5c8fa4f2009-03-26 23:39:53 +02001201 rndis_rates[i].bitrate * 100000;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001202 if (range->num_bitrates == IW_MAX_BITRATES)
1203 break;
1204 }
1205
1206 /* estimated max real througput in bps */
1207 range->throughput = 54 * 1000 * 1000 / 2;
1208
1209 /* ~35% more with afterburner */
1210 if (priv->param_afterburner)
1211 range->throughput = range->throughput / 100 * 135;
1212 } else {
1213 /* estimated max real througput in bps */
1214 range->throughput = 11 * 1000 * 1000 / 2;
1215 }
1216
David Kilroy9ee677c2008-12-23 14:03:38 +00001217 range->num_channels = 14;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001218
David Kilroy9ee677c2008-12-23 14:03:38 +00001219 for (i = 0; (i < 14) && (i < IW_MAX_FREQUENCIES); i++) {
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001220 range->freq[i].i = i + 1;
David Kilroy9ee677c2008-12-23 14:03:38 +00001221 range->freq[i].m = ieee80211_dsss_chan_to_freq(i + 1) * 100000;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001222 range->freq[i].e = 1;
1223 }
1224 range->num_frequency = i;
1225
1226 range->min_rts = 0;
1227 range->max_rts = 2347;
1228 range->min_frag = 256;
1229 range->max_frag = 2346;
1230
1231 range->max_qual.qual = 100;
1232 range->max_qual.level = 154;
1233 range->max_qual.updated = IW_QUAL_QUAL_UPDATED
1234 | IW_QUAL_LEVEL_UPDATED
1235 | IW_QUAL_NOISE_INVALID;
1236
1237 range->we_version_compiled = WIRELESS_EXT;
1238 range->we_version_source = WIRELESS_EXT;
1239
1240 range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
1241 IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
1242 return 0;
1243}
1244
1245
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001246static int rndis_iw_set_essid(struct net_device *dev,
1247 struct iw_request_info *info, union iwreq_data *wrqu, char *essid)
1248{
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +02001249 struct ndis_80211_ssid ssid;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001250 int length = wrqu->essid.length;
Wang Chen524ad0a2008-11-12 23:39:10 -08001251 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001252
1253 devdbg(usbdev, "SIOCSIWESSID: [flags:%d,len:%d] '%.32s'",
1254 wrqu->essid.flags, wrqu->essid.length, essid);
1255
1256 if (length > NDIS_802_11_LENGTH_SSID)
1257 length = NDIS_802_11_LENGTH_SSID;
1258
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001259 ssid.length = cpu_to_le32(length);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001260 if (length > 0)
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001261 memcpy(ssid.essid, essid, length);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001262 else
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001263 memset(ssid.essid, 0, NDIS_802_11_LENGTH_SSID);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001264
1265 set_assoc_params(usbdev);
1266
1267 if (!wrqu->essid.flags || length == 0)
1268 return disassociate(usbdev, 1);
1269 else
1270 return set_essid(usbdev, &ssid);
1271}
1272
1273
1274static int rndis_iw_get_essid(struct net_device *dev,
1275 struct iw_request_info *info, union iwreq_data *wrqu, char *essid)
1276{
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +02001277 struct ndis_80211_ssid ssid;
Wang Chen524ad0a2008-11-12 23:39:10 -08001278 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001279 int ret;
1280
1281 ret = get_essid(usbdev, &ssid);
1282
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001283 if (ret == 0 && le32_to_cpu(ssid.length) > 0) {
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001284 wrqu->essid.flags = 1;
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001285 wrqu->essid.length = le32_to_cpu(ssid.length);
1286 memcpy(essid, ssid.essid, wrqu->essid.length);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001287 essid[wrqu->essid.length] = 0;
1288 } else {
1289 memset(essid, 0, sizeof(NDIS_802_11_LENGTH_SSID));
1290 wrqu->essid.flags = 0;
1291 wrqu->essid.length = 0;
1292 }
1293 devdbg(usbdev, "SIOCGIWESSID: %s", essid);
1294 return ret;
1295}
1296
1297
1298static int rndis_iw_get_bssid(struct net_device *dev,
1299 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
1300{
Wang Chen524ad0a2008-11-12 23:39:10 -08001301 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001302 unsigned char bssid[ETH_ALEN];
1303 int ret;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001304
1305 ret = get_bssid(usbdev, bssid);
1306
1307 if (ret == 0)
Johannes Berge1749612008-10-27 15:59:26 -07001308 devdbg(usbdev, "SIOCGIWAP: %pM", bssid);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001309 else
1310 devdbg(usbdev, "SIOCGIWAP: <not associated>");
1311
1312 wrqu->ap_addr.sa_family = ARPHRD_ETHER;
1313 memcpy(wrqu->ap_addr.sa_data, bssid, ETH_ALEN);
1314
1315 return ret;
1316}
1317
1318
1319static int rndis_iw_set_bssid(struct net_device *dev,
1320 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
1321{
Wang Chen524ad0a2008-11-12 23:39:10 -08001322 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001323 u8 *bssid = (u8 *)wrqu->ap_addr.sa_data;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001324 int ret;
1325
Johannes Berge1749612008-10-27 15:59:26 -07001326 devdbg(usbdev, "SIOCSIWAP: %pM", bssid);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001327
1328 ret = rndis_set_oid(usbdev, OID_802_11_BSSID, bssid, ETH_ALEN);
1329
1330 /* user apps may set ap's mac address, which is not required;
1331 * they may fail to work if this function fails, so return
1332 * success */
1333 if (ret)
1334 devwarn(usbdev, "setting AP mac address failed (%08X)", ret);
1335
1336 return 0;
1337}
1338
1339
1340static int rndis_iw_set_auth(struct net_device *dev,
1341 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
1342{
1343 struct iw_param *p = &wrqu->param;
Wang Chen524ad0a2008-11-12 23:39:10 -08001344 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001345 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
1346 int ret = -ENOTSUPP;
1347
1348 switch (p->flags & IW_AUTH_INDEX) {
1349 case IW_AUTH_WPA_VERSION:
1350 devdbg(usbdev, "SIOCSIWAUTH: WPA_VERSION, %08x", p->value);
1351 priv->wpa_version = p->value;
1352 ret = 0;
1353 break;
1354
1355 case IW_AUTH_CIPHER_PAIRWISE:
1356 devdbg(usbdev, "SIOCSIWAUTH: CIPHER_PAIRWISE, %08x", p->value);
1357 priv->wpa_cipher_pair = p->value;
1358 ret = 0;
1359 break;
1360
1361 case IW_AUTH_CIPHER_GROUP:
1362 devdbg(usbdev, "SIOCSIWAUTH: CIPHER_GROUP, %08x", p->value);
1363 priv->wpa_cipher_group = p->value;
1364 ret = 0;
1365 break;
1366
1367 case IW_AUTH_KEY_MGMT:
1368 devdbg(usbdev, "SIOCSIWAUTH: KEY_MGMT, %08x", p->value);
1369 priv->wpa_keymgmt = p->value;
1370 ret = 0;
1371 break;
1372
1373 case IW_AUTH_TKIP_COUNTERMEASURES:
1374 devdbg(usbdev, "SIOCSIWAUTH: TKIP_COUNTERMEASURES, %08x",
1375 p->value);
1376 ret = 0;
1377 break;
1378
1379 case IW_AUTH_DROP_UNENCRYPTED:
1380 devdbg(usbdev, "SIOCSIWAUTH: DROP_UNENCRYPTED, %08x", p->value);
1381 ret = 0;
1382 break;
1383
1384 case IW_AUTH_80211_AUTH_ALG:
1385 devdbg(usbdev, "SIOCSIWAUTH: 80211_AUTH_ALG, %08x", p->value);
1386 priv->wpa_authalg = p->value;
1387 ret = 0;
1388 break;
1389
1390 case IW_AUTH_WPA_ENABLED:
1391 devdbg(usbdev, "SIOCSIWAUTH: WPA_ENABLED, %08x", p->value);
1392 if (wrqu->param.value)
1393 deauthenticate(usbdev);
1394 ret = 0;
1395 break;
1396
1397 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
1398 devdbg(usbdev, "SIOCSIWAUTH: RX_UNENCRYPTED_EAPOL, %08x",
1399 p->value);
1400 ret = 0;
1401 break;
1402
1403 case IW_AUTH_ROAMING_CONTROL:
1404 devdbg(usbdev, "SIOCSIWAUTH: ROAMING_CONTROL, %08x", p->value);
1405 ret = 0;
1406 break;
1407
1408 case IW_AUTH_PRIVACY_INVOKED:
1409 devdbg(usbdev, "SIOCSIWAUTH: invalid cmd %d",
1410 wrqu->param.flags & IW_AUTH_INDEX);
1411 return -EOPNOTSUPP;
1412
1413 default:
1414 devdbg(usbdev, "SIOCSIWAUTH: UNKNOWN %08x, %08x",
1415 p->flags & IW_AUTH_INDEX, p->value);
1416 }
1417 return ret;
1418}
1419
1420
1421static int rndis_iw_get_auth(struct net_device *dev,
1422 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
1423{
1424 struct iw_param *p = &wrqu->param;
Wang Chen524ad0a2008-11-12 23:39:10 -08001425 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001426 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
1427
1428 switch (p->flags & IW_AUTH_INDEX) {
1429 case IW_AUTH_WPA_VERSION:
1430 p->value = priv->wpa_version;
1431 break;
1432 case IW_AUTH_CIPHER_PAIRWISE:
1433 p->value = priv->wpa_cipher_pair;
1434 break;
1435 case IW_AUTH_CIPHER_GROUP:
1436 p->value = priv->wpa_cipher_group;
1437 break;
1438 case IW_AUTH_KEY_MGMT:
1439 p->value = priv->wpa_keymgmt;
1440 break;
1441 case IW_AUTH_80211_AUTH_ALG:
1442 p->value = priv->wpa_authalg;
1443 break;
1444 default:
1445 devdbg(usbdev, "SIOCGIWAUTH: invalid cmd %d",
1446 wrqu->param.flags & IW_AUTH_INDEX);
1447 return -EOPNOTSUPP;
1448 }
1449 return 0;
1450}
1451
1452
1453static int rndis_iw_get_mode(struct net_device *dev,
1454 struct iw_request_info *info,
1455 union iwreq_data *wrqu, char *extra)
1456{
Wang Chen524ad0a2008-11-12 23:39:10 -08001457 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001458 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
1459
1460 switch (priv->infra_mode) {
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +02001461 case ndis_80211_infra_adhoc:
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001462 wrqu->mode = IW_MODE_ADHOC;
1463 break;
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +02001464 case ndis_80211_infra_infra:
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001465 wrqu->mode = IW_MODE_INFRA;
1466 break;
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +02001467 /*case ndis_80211_infra_auto_unknown:*/
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001468 default:
1469 wrqu->mode = IW_MODE_AUTO;
1470 break;
1471 }
1472 devdbg(usbdev, "SIOCGIWMODE: %08x", wrqu->mode);
1473 return 0;
1474}
1475
1476
1477static int rndis_iw_set_mode(struct net_device *dev,
1478 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
1479{
Wang Chen524ad0a2008-11-12 23:39:10 -08001480 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001481 int mode;
1482
1483 devdbg(usbdev, "SIOCSIWMODE: %08x", wrqu->mode);
1484
1485 switch (wrqu->mode) {
1486 case IW_MODE_ADHOC:
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +02001487 mode = ndis_80211_infra_adhoc;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001488 break;
1489 case IW_MODE_INFRA:
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +02001490 mode = ndis_80211_infra_infra;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001491 break;
1492 /*case IW_MODE_AUTO:*/
1493 default:
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +02001494 mode = ndis_80211_infra_auto_unknown;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001495 break;
1496 }
1497
1498 return set_infra_mode(usbdev, mode);
1499}
1500
1501
1502static int rndis_iw_set_encode(struct net_device *dev,
1503 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
1504{
Wang Chen524ad0a2008-11-12 23:39:10 -08001505 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001506 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
1507 int ret, index, key_len;
1508 u8 *key;
1509
1510 index = (wrqu->encoding.flags & IW_ENCODE_INDEX);
1511
1512 /* iwconfig gives index as 1 - N */
1513 if (index > 0)
1514 index--;
1515 else
1516 index = priv->encr_tx_key_index;
1517
1518 if (index < 0 || index >= 4) {
1519 devwarn(usbdev, "encryption index out of range (%u)", index);
1520 return -EINVAL;
1521 }
1522
1523 /* remove key if disabled */
1524 if (wrqu->data.flags & IW_ENCODE_DISABLED) {
1525 if (remove_key(usbdev, index, NULL))
1526 return -EINVAL;
1527 else
1528 return 0;
1529 }
1530
1531 /* global encryption state (for all keys) */
1532 if (wrqu->data.flags & IW_ENCODE_OPEN)
1533 ret = set_auth_mode(usbdev, IW_AUTH_WPA_VERSION_DISABLED,
1534 IW_AUTH_ALG_OPEN_SYSTEM);
1535 else /*if (wrqu->data.flags & IW_ENCODE_RESTRICTED)*/
1536 ret = set_auth_mode(usbdev, IW_AUTH_WPA_VERSION_DISABLED,
1537 IW_AUTH_ALG_SHARED_KEY);
1538 if (ret != 0)
1539 return ret;
1540
1541 if (wrqu->data.length > 0) {
1542 key_len = wrqu->data.length;
1543 key = extra;
1544 } else {
1545 /* must be set as tx key */
1546 if (priv->encr_key_len[index] == 0)
1547 return -EINVAL;
1548 key_len = priv->encr_key_len[index];
1549 key = priv->encr_keys[index];
1550 priv->encr_tx_key_index = index;
1551 }
1552
1553 if (add_wep_key(usbdev, key, key_len, index) != 0)
1554 return -EINVAL;
1555
1556 if (index == priv->encr_tx_key_index)
1557 /* ndis drivers want essid to be set after setting encr */
1558 set_essid(usbdev, &priv->essid);
1559
1560 return 0;
1561}
1562
1563
1564static int rndis_iw_set_encode_ext(struct net_device *dev,
1565 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
1566{
1567 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
Wang Chen524ad0a2008-11-12 23:39:10 -08001568 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001569 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +02001570 struct ndis_80211_key ndis_key;
Jussi Kivilinnacdb2a9f2008-03-04 20:05:27 +02001571 int keyidx, ret;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001572 u8 *addr;
1573
1574 keyidx = wrqu->encoding.flags & IW_ENCODE_INDEX;
1575
1576 /* iwconfig gives index as 1 - N */
1577 if (keyidx)
1578 keyidx--;
1579 else
1580 keyidx = priv->encr_tx_key_index;
1581
1582 if (keyidx < 0 || keyidx >= 4)
1583 return -EINVAL;
1584
1585 if (ext->alg == WPA_ALG_WEP) {
1586 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)
1587 priv->encr_tx_key_index = keyidx;
1588 return add_wep_key(usbdev, ext->key, ext->key_len, keyidx);
1589 }
1590
1591 if ((wrqu->encoding.flags & IW_ENCODE_DISABLED) ||
1592 ext->alg == IW_ENCODE_ALG_NONE || ext->key_len == 0)
1593 return remove_key(usbdev, keyidx, NULL);
1594
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001595 if (ext->key_len > sizeof(ndis_key.material))
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001596 return -1;
1597
1598 memset(&ndis_key, 0, sizeof(ndis_key));
1599
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001600 ndis_key.size = cpu_to_le32(sizeof(ndis_key) -
1601 sizeof(ndis_key.material) + ext->key_len);
1602 ndis_key.length = cpu_to_le32(ext->key_len);
1603 ndis_key.index = cpu_to_le32(keyidx);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001604
1605 if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) {
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001606 memcpy(ndis_key.rsc, ext->rx_seq, 6);
1607 ndis_key.index |= cpu_to_le32(1 << 29);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001608 }
1609
1610 addr = ext->addr.sa_data;
1611 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
1612 /* group key */
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +02001613 if (priv->infra_mode == ndis_80211_infra_adhoc)
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001614 memset(ndis_key.bssid, 0xff, ETH_ALEN);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001615 else
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001616 get_bssid(usbdev, ndis_key.bssid);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001617 } else {
1618 /* pairwise key */
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001619 ndis_key.index |= cpu_to_le32(1 << 30);
1620 memcpy(ndis_key.bssid, addr, ETH_ALEN);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001621 }
1622
1623 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001624 ndis_key.index |= cpu_to_le32(1 << 31);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001625
1626 if (ext->alg == IW_ENCODE_ALG_TKIP && ext->key_len == 32) {
1627 /* wpa_supplicant gives us the Michael MIC RX/TX keys in
1628 * different order than NDIS spec, so swap the order here. */
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001629 memcpy(ndis_key.material, ext->key, 16);
1630 memcpy(ndis_key.material + 16, ext->key + 24, 8);
1631 memcpy(ndis_key.material + 24, ext->key + 16, 8);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001632 } else
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001633 memcpy(ndis_key.material, ext->key, ext->key_len);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001634
1635 ret = rndis_set_oid(usbdev, OID_802_11_ADD_KEY, &ndis_key,
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001636 le32_to_cpu(ndis_key.size));
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001637 devdbg(usbdev, "SIOCSIWENCODEEXT: OID_802_11_ADD_KEY -> %08X", ret);
1638 if (ret != 0)
1639 return ret;
1640
1641 priv->encr_key_len[keyidx] = ext->key_len;
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001642 memcpy(&priv->encr_keys[keyidx], ndis_key.material, ext->key_len);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001643 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)
1644 priv->encr_tx_key_index = keyidx;
1645
1646 return 0;
1647}
1648
1649
1650static int rndis_iw_set_scan(struct net_device *dev,
1651 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
1652{
Wang Chen524ad0a2008-11-12 23:39:10 -08001653 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001654 union iwreq_data evt;
1655 int ret = -EINVAL;
1656 __le32 tmp;
1657
1658 devdbg(usbdev, "SIOCSIWSCAN");
1659
David Kilroy9930cce2008-09-13 12:22:05 +01001660 if (wrqu->data.flags == 0) {
Harvey Harrison35c26c22009-02-14 22:56:56 -08001661 tmp = cpu_to_le32(1);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001662 ret = rndis_set_oid(usbdev, OID_802_11_BSSID_LIST_SCAN, &tmp,
1663 sizeof(tmp));
1664 evt.data.flags = 0;
1665 evt.data.length = 0;
1666 wireless_send_event(dev, SIOCGIWSCAN, &evt, NULL);
1667 }
1668 return ret;
1669}
1670
1671
1672static char *rndis_translate_scan(struct net_device *dev,
David S. Millerccc58052008-06-16 18:50:49 -07001673 struct iw_request_info *info, char *cev,
1674 char *end_buf,
1675 struct ndis_80211_bssid_ex *bssid)
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001676{
Wang Chen524ad0a2008-11-12 23:39:10 -08001677 struct usbnet *usbdev = netdev_priv(dev);
Johannes Berg2c7060022008-10-30 22:09:54 +01001678 u8 *ie;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001679 char *current_val;
1680 int bssid_len, ie_len, i;
1681 u32 beacon, atim;
1682 struct iw_event iwe;
1683 unsigned char sbuf[32];
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001684
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001685 bssid_len = le32_to_cpu(bssid->length);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001686
Johannes Berge1749612008-10-27 15:59:26 -07001687 devdbg(usbdev, "BSSID %pM", bssid->mac);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001688 iwe.cmd = SIOCGIWAP;
1689 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001690 memcpy(iwe.u.ap_addr.sa_data, bssid->mac, ETH_ALEN);
David S. Millerccc58052008-06-16 18:50:49 -07001691 cev = iwe_stream_add_event(info, cev, end_buf, &iwe, IW_EV_ADDR_LEN);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001692
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001693 devdbg(usbdev, "SSID(%d) %s", le32_to_cpu(bssid->ssid.length),
1694 bssid->ssid.essid);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001695 iwe.cmd = SIOCGIWESSID;
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001696 iwe.u.essid.length = le32_to_cpu(bssid->ssid.length);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001697 iwe.u.essid.flags = 1;
David S. Millerccc58052008-06-16 18:50:49 -07001698 cev = iwe_stream_add_point(info, cev, end_buf, &iwe, bssid->ssid.essid);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001699
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001700 devdbg(usbdev, "MODE %d", le32_to_cpu(bssid->net_infra));
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001701 iwe.cmd = SIOCGIWMODE;
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001702 switch (le32_to_cpu(bssid->net_infra)) {
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +02001703 case ndis_80211_infra_adhoc:
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001704 iwe.u.mode = IW_MODE_ADHOC;
1705 break;
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +02001706 case ndis_80211_infra_infra:
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001707 iwe.u.mode = IW_MODE_INFRA;
1708 break;
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +02001709 /*case ndis_80211_infra_auto_unknown:*/
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001710 default:
1711 iwe.u.mode = IW_MODE_AUTO;
1712 break;
1713 }
David S. Millerccc58052008-06-16 18:50:49 -07001714 cev = iwe_stream_add_event(info, cev, end_buf, &iwe, IW_EV_UINT_LEN);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001715
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001716 devdbg(usbdev, "FREQ %d kHz", le32_to_cpu(bssid->config.ds_config));
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001717 iwe.cmd = SIOCGIWFREQ;
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001718 dsconfig_to_freq(le32_to_cpu(bssid->config.ds_config), &iwe.u.freq);
David S. Millerccc58052008-06-16 18:50:49 -07001719 cev = iwe_stream_add_event(info, cev, end_buf, &iwe, IW_EV_FREQ_LEN);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001720
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001721 devdbg(usbdev, "QUAL %d", le32_to_cpu(bssid->rssi));
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001722 iwe.cmd = IWEVQUAL;
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001723 iwe.u.qual.qual = level_to_qual(le32_to_cpu(bssid->rssi));
1724 iwe.u.qual.level = le32_to_cpu(bssid->rssi);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001725 iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED
1726 | IW_QUAL_LEVEL_UPDATED
1727 | IW_QUAL_NOISE_INVALID;
David S. Millerccc58052008-06-16 18:50:49 -07001728 cev = iwe_stream_add_event(info, cev, end_buf, &iwe, IW_EV_QUAL_LEN);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001729
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001730 devdbg(usbdev, "ENCODE %d", le32_to_cpu(bssid->privacy));
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001731 iwe.cmd = SIOCGIWENCODE;
1732 iwe.u.data.length = 0;
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001733 if (le32_to_cpu(bssid->privacy) == ndis_80211_priv_accept_all)
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001734 iwe.u.data.flags = IW_ENCODE_DISABLED;
1735 else
1736 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
1737
David S. Millerccc58052008-06-16 18:50:49 -07001738 cev = iwe_stream_add_point(info, cev, end_buf, &iwe, NULL);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001739
1740 devdbg(usbdev, "RATES:");
David S. Millerccc58052008-06-16 18:50:49 -07001741 current_val = cev + iwe_stream_lcp_len(info);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001742 iwe.cmd = SIOCGIWRATE;
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001743 for (i = 0; i < sizeof(bssid->rates); i++) {
1744 if (bssid->rates[i] & 0x7f) {
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001745 iwe.u.bitrate.value =
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001746 ((bssid->rates[i] & 0x7f) *
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001747 500000);
1748 devdbg(usbdev, " %d", iwe.u.bitrate.value);
David S. Millerccc58052008-06-16 18:50:49 -07001749 current_val = iwe_stream_add_value(info, cev,
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001750 current_val, end_buf, &iwe,
1751 IW_EV_PARAM_LEN);
1752 }
1753 }
1754
David S. Millerccc58052008-06-16 18:50:49 -07001755 if ((current_val - cev) > iwe_stream_lcp_len(info))
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001756 cev = current_val;
1757
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001758 beacon = le32_to_cpu(bssid->config.beacon_period);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001759 devdbg(usbdev, "BCN_INT %d", beacon);
1760 iwe.cmd = IWEVCUSTOM;
1761 snprintf(sbuf, sizeof(sbuf), "bcn_int=%d", beacon);
1762 iwe.u.data.length = strlen(sbuf);
David S. Millerccc58052008-06-16 18:50:49 -07001763 cev = iwe_stream_add_point(info, cev, end_buf, &iwe, sbuf);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001764
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001765 atim = le32_to_cpu(bssid->config.atim_window);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001766 devdbg(usbdev, "ATIM %d", atim);
1767 iwe.cmd = IWEVCUSTOM;
1768 snprintf(sbuf, sizeof(sbuf), "atim=%u", atim);
1769 iwe.u.data.length = strlen(sbuf);
David S. Millerccc58052008-06-16 18:50:49 -07001770 cev = iwe_stream_add_point(info, cev, end_buf, &iwe, sbuf);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001771
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001772 ie = (void *)(bssid->ies + sizeof(struct ndis_80211_fixed_ies));
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001773 ie_len = min(bssid_len - (int)sizeof(*bssid),
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001774 (int)le32_to_cpu(bssid->ie_length));
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +02001775 ie_len -= sizeof(struct ndis_80211_fixed_ies);
Johannes Berg2c7060022008-10-30 22:09:54 +01001776 while (ie_len >= 2 && 2 + ie[1] <= ie_len) {
1777 if ((ie[0] == WLAN_EID_GENERIC && ie[1] >= 4 &&
1778 memcmp(ie + 2, "\x00\x50\xf2\x01", 4) == 0) ||
1779 ie[0] == WLAN_EID_RSN) {
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001780 devdbg(usbdev, "IE: WPA%d",
Johannes Berg2c7060022008-10-30 22:09:54 +01001781 (ie[0] == WLAN_EID_RSN) ? 2 : 1);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001782 iwe.cmd = IWEVGENIE;
Johannes Berg2c7060022008-10-30 22:09:54 +01001783 /* arbitrary cut-off at 64 */
1784 iwe.u.data.length = min(ie[1] + 2, 64);
1785 cev = iwe_stream_add_point(info, cev, end_buf, &iwe, ie);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001786 }
1787
Johannes Berg2c7060022008-10-30 22:09:54 +01001788 ie_len -= 2 + ie[1];
1789 ie += 2 + ie[1];
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001790 }
1791
1792 return cev;
1793}
1794
1795
1796static int rndis_iw_get_scan(struct net_device *dev,
1797 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
1798{
Wang Chen524ad0a2008-11-12 23:39:10 -08001799 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001800 void *buf = NULL;
1801 char *cev = extra;
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +02001802 struct ndis_80211_bssid_list_ex *bssid_list;
1803 struct ndis_80211_bssid_ex *bssid;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001804 int ret = -EINVAL, len, count, bssid_len;
1805
1806 devdbg(usbdev, "SIOCGIWSCAN");
1807
1808 len = CONTROL_BUFFER_SIZE;
1809 buf = kmalloc(len, GFP_KERNEL);
1810 if (!buf) {
1811 ret = -ENOMEM;
1812 goto out;
1813 }
1814
1815 ret = rndis_query_oid(usbdev, OID_802_11_BSSID_LIST, buf, &len);
1816
1817 if (ret != 0)
1818 goto out;
1819
1820 bssid_list = buf;
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001821 bssid = bssid_list->bssid;
1822 bssid_len = le32_to_cpu(bssid->length);
1823 count = le32_to_cpu(bssid_list->num_items);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001824 devdbg(usbdev, "SIOCGIWSCAN: %d BSSIDs found", count);
1825
1826 while (count && ((void *)bssid + bssid_len) <= (buf + len)) {
David S. Millerccc58052008-06-16 18:50:49 -07001827 cev = rndis_translate_scan(dev, info, cev,
1828 extra + IW_SCAN_MAX_DATA, bssid);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001829 bssid = (void *)bssid + bssid_len;
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02001830 bssid_len = le32_to_cpu(bssid->length);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001831 count--;
1832 }
1833
1834out:
1835 wrqu->data.length = cev - extra;
1836 wrqu->data.flags = 0;
1837 kfree(buf);
1838 return ret;
1839}
1840
1841
1842static int rndis_iw_set_genie(struct net_device *dev,
1843 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
1844{
Wang Chen524ad0a2008-11-12 23:39:10 -08001845 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001846 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
1847 int ret = 0;
1848
1849#ifdef DEBUG
1850 int j;
1851 u8 *gie = extra;
1852 for (j = 0; j < wrqu->data.length; j += 8)
1853 devdbg(usbdev,
1854 "SIOCSIWGENIE %04x - "
1855 "%02x %02x %02x %02x %02x %02x %02x %02x", j,
1856 gie[j + 0], gie[j + 1], gie[j + 2], gie[j + 3],
1857 gie[j + 4], gie[j + 5], gie[j + 6], gie[j + 7]);
1858#endif
1859 /* clear existing IEs */
1860 if (priv->wpa_ie_len) {
1861 kfree(priv->wpa_ie);
1862 priv->wpa_ie_len = 0;
1863 }
1864
1865 /* set new IEs */
1866 priv->wpa_ie = kmalloc(wrqu->data.length, GFP_KERNEL);
1867 if (priv->wpa_ie) {
1868 priv->wpa_ie_len = wrqu->data.length;
1869 memcpy(priv->wpa_ie, extra, priv->wpa_ie_len);
1870 } else
1871 ret = -ENOMEM;
1872 return ret;
1873}
1874
1875
1876static int rndis_iw_get_genie(struct net_device *dev,
1877 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
1878{
Wang Chen524ad0a2008-11-12 23:39:10 -08001879 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001880 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
1881
1882 devdbg(usbdev, "SIOCGIWGENIE");
1883
1884 if (priv->wpa_ie_len == 0 || priv->wpa_ie == NULL) {
1885 wrqu->data.length = 0;
1886 return 0;
1887 }
1888
1889 if (wrqu->data.length < priv->wpa_ie_len)
1890 return -E2BIG;
1891
1892 wrqu->data.length = priv->wpa_ie_len;
1893 memcpy(extra, priv->wpa_ie, priv->wpa_ie_len);
1894
1895 return 0;
1896}
1897
1898
1899static int rndis_iw_set_rts(struct net_device *dev,
1900 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
1901{
Wang Chen524ad0a2008-11-12 23:39:10 -08001902 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001903 __le32 tmp;
1904 devdbg(usbdev, "SIOCSIWRTS");
1905
1906 tmp = cpu_to_le32(wrqu->rts.value);
1907 return rndis_set_oid(usbdev, OID_802_11_RTS_THRESHOLD, &tmp,
1908 sizeof(tmp));
1909}
1910
1911
1912static int rndis_iw_get_rts(struct net_device *dev,
1913 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
1914{
Wang Chen524ad0a2008-11-12 23:39:10 -08001915 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001916 __le32 tmp;
1917 int len, ret;
1918
1919 len = sizeof(tmp);
1920 ret = rndis_query_oid(usbdev, OID_802_11_RTS_THRESHOLD, &tmp, &len);
1921 if (ret == 0) {
1922 wrqu->rts.value = le32_to_cpu(tmp);
1923 wrqu->rts.flags = 1;
1924 wrqu->rts.disabled = 0;
1925 }
1926
1927 devdbg(usbdev, "SIOCGIWRTS: %d", wrqu->rts.value);
1928
1929 return ret;
1930}
1931
1932
1933static int rndis_iw_set_frag(struct net_device *dev,
1934 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
1935{
Wang Chen524ad0a2008-11-12 23:39:10 -08001936 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001937 __le32 tmp;
1938
1939 devdbg(usbdev, "SIOCSIWFRAG");
1940
1941 tmp = cpu_to_le32(wrqu->frag.value);
1942 return rndis_set_oid(usbdev, OID_802_11_FRAGMENTATION_THRESHOLD, &tmp,
1943 sizeof(tmp));
1944}
1945
1946
1947static int rndis_iw_get_frag(struct net_device *dev,
1948 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
1949{
Wang Chen524ad0a2008-11-12 23:39:10 -08001950 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001951 __le32 tmp;
1952 int len, ret;
1953
1954 len = sizeof(tmp);
1955 ret = rndis_query_oid(usbdev, OID_802_11_FRAGMENTATION_THRESHOLD, &tmp,
1956 &len);
1957 if (ret == 0) {
1958 wrqu->frag.value = le32_to_cpu(tmp);
1959 wrqu->frag.flags = 1;
1960 wrqu->frag.disabled = 0;
1961 }
1962 devdbg(usbdev, "SIOCGIWFRAG: %d", wrqu->frag.value);
1963 return ret;
1964}
1965
1966
1967static int rndis_iw_set_nick(struct net_device *dev,
1968 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
1969{
Wang Chen524ad0a2008-11-12 23:39:10 -08001970 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001971 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
1972
1973 devdbg(usbdev, "SIOCSIWNICK");
1974
1975 priv->nick_len = wrqu->data.length;
1976 if (priv->nick_len > 32)
1977 priv->nick_len = 32;
1978
1979 memcpy(priv->nick, extra, priv->nick_len);
1980 return 0;
1981}
1982
1983
1984static int rndis_iw_get_nick(struct net_device *dev,
1985 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
1986{
Wang Chen524ad0a2008-11-12 23:39:10 -08001987 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02001988 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
1989
1990 wrqu->data.flags = 1;
1991 wrqu->data.length = priv->nick_len;
1992 memcpy(extra, priv->nick, priv->nick_len);
1993
1994 devdbg(usbdev, "SIOCGIWNICK: '%s'", priv->nick);
1995
1996 return 0;
1997}
1998
1999
2000static int rndis_iw_set_freq(struct net_device *dev,
2001 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
2002{
Wang Chen524ad0a2008-11-12 23:39:10 -08002003 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +02002004 struct ndis_80211_conf config;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002005 unsigned int dsconfig;
2006 int len, ret;
2007
2008 /* this OID is valid only when not associated */
2009 if (is_associated(usbdev))
2010 return 0;
2011
2012 dsconfig = 0;
2013 if (freq_to_dsconfig(&wrqu->freq, &dsconfig))
2014 return -EINVAL;
2015
2016 len = sizeof(config);
2017 ret = rndis_query_oid(usbdev, OID_802_11_CONFIGURATION, &config, &len);
2018 if (ret != 0) {
2019 devdbg(usbdev, "SIOCSIWFREQ: querying configuration failed");
2020 return 0;
2021 }
2022
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02002023 config.ds_config = cpu_to_le32(dsconfig);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002024
2025 devdbg(usbdev, "SIOCSIWFREQ: %d * 10^%d", wrqu->freq.m, wrqu->freq.e);
2026 return rndis_set_oid(usbdev, OID_802_11_CONFIGURATION, &config,
2027 sizeof(config));
2028}
2029
2030
2031static int rndis_iw_get_freq(struct net_device *dev,
2032 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
2033{
Wang Chen524ad0a2008-11-12 23:39:10 -08002034 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +02002035 struct ndis_80211_conf config;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002036 int len, ret;
2037
2038 len = sizeof(config);
2039 ret = rndis_query_oid(usbdev, OID_802_11_CONFIGURATION, &config, &len);
2040 if (ret == 0)
Jussi Kivilinna461b3f22008-03-08 01:23:25 +02002041 dsconfig_to_freq(le32_to_cpu(config.ds_config), &wrqu->freq);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002042
2043 devdbg(usbdev, "SIOCGIWFREQ: %d", wrqu->freq.m);
2044 return ret;
2045}
2046
2047
2048static int rndis_iw_get_txpower(struct net_device *dev,
2049 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
2050{
Wang Chen524ad0a2008-11-12 23:39:10 -08002051 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002052 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
2053 __le32 tx_power;
2054 int ret = 0, len;
2055
2056 if (priv->radio_on) {
2057 if (priv->caps & CAP_SUPPORT_TXPOWER) {
2058 len = sizeof(tx_power);
2059 ret = rndis_query_oid(usbdev, OID_802_11_TX_POWER_LEVEL,
2060 &tx_power, &len);
2061 if (ret != 0)
2062 return ret;
2063 } else
2064 /* fake incase not supported */
2065 tx_power = cpu_to_le32(get_bcm4320_power(priv));
2066
2067 wrqu->txpower.flags = IW_TXPOW_MWATT;
2068 wrqu->txpower.value = le32_to_cpu(tx_power);
2069 wrqu->txpower.disabled = 0;
2070 } else {
2071 wrqu->txpower.flags = IW_TXPOW_MWATT;
2072 wrqu->txpower.value = 0;
2073 wrqu->txpower.disabled = 1;
2074 }
2075
2076 devdbg(usbdev, "SIOCGIWTXPOW: %d", wrqu->txpower.value);
2077
2078 return ret;
2079}
2080
2081
2082static int rndis_iw_set_txpower(struct net_device *dev,
2083 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
2084{
Wang Chen524ad0a2008-11-12 23:39:10 -08002085 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002086 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
2087 __le32 tx_power = 0;
2088 int ret = 0;
2089
2090 if (!wrqu->txpower.disabled) {
2091 if (wrqu->txpower.flags == IW_TXPOW_MWATT)
2092 tx_power = cpu_to_le32(wrqu->txpower.value);
2093 else { /* wrqu->txpower.flags == IW_TXPOW_DBM */
2094 if (wrqu->txpower.value > 20)
2095 tx_power = cpu_to_le32(128);
2096 else if (wrqu->txpower.value < -43)
2097 tx_power = cpu_to_le32(127);
2098 else {
2099 signed char tmp;
2100 tmp = wrqu->txpower.value;
2101 tmp = -12 - tmp;
2102 tmp <<= 2;
2103 tx_power = cpu_to_le32((unsigned char)tmp);
2104 }
2105 }
2106 }
2107
2108 devdbg(usbdev, "SIOCSIWTXPOW: %d", le32_to_cpu(tx_power));
2109
2110 if (le32_to_cpu(tx_power) != 0) {
2111 if (priv->caps & CAP_SUPPORT_TXPOWER) {
2112 /* turn radio on first */
2113 if (!priv->radio_on)
2114 disassociate(usbdev, 1);
2115
2116 ret = rndis_set_oid(usbdev, OID_802_11_TX_POWER_LEVEL,
2117 &tx_power, sizeof(tx_power));
2118 if (ret != 0)
2119 ret = -EOPNOTSUPP;
2120 return ret;
2121 } else {
2122 /* txpower unsupported, just turn radio on */
2123 if (!priv->radio_on)
2124 return disassociate(usbdev, 1);
2125 return 0; /* all ready on */
2126 }
2127 }
2128
2129 /* tx_power == 0, turn off radio */
2130 return disassociate(usbdev, 0);
2131}
2132
2133
2134static int rndis_iw_get_rate(struct net_device *dev,
2135 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
2136{
Wang Chen524ad0a2008-11-12 23:39:10 -08002137 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002138 __le32 tmp;
2139 int ret, len;
2140
2141 len = sizeof(tmp);
2142 ret = rndis_query_oid(usbdev, OID_GEN_LINK_SPEED, &tmp, &len);
2143 if (ret == 0) {
2144 wrqu->bitrate.value = le32_to_cpu(tmp) * 100;
2145 wrqu->bitrate.disabled = 0;
2146 wrqu->bitrate.flags = 1;
2147 }
2148 return ret;
2149}
2150
2151
2152static int rndis_iw_set_mlme(struct net_device *dev,
2153 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
2154{
Wang Chen524ad0a2008-11-12 23:39:10 -08002155 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002156 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
2157 struct iw_mlme *mlme = (struct iw_mlme *)extra;
2158 unsigned char bssid[ETH_ALEN];
2159
2160 get_bssid(usbdev, bssid);
2161
2162 if (memcmp(bssid, mlme->addr.sa_data, ETH_ALEN))
2163 return -EINVAL;
2164
2165 switch (mlme->cmd) {
2166 case IW_MLME_DEAUTH:
2167 return deauthenticate(usbdev);
2168 case IW_MLME_DISASSOC:
2169 return disassociate(usbdev, priv->radio_on);
2170 default:
2171 return -EOPNOTSUPP;
2172 }
2173
2174 return 0;
2175}
2176
2177
2178static struct iw_statistics *rndis_get_wireless_stats(struct net_device *dev)
2179{
Wang Chen524ad0a2008-11-12 23:39:10 -08002180 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002181 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
2182 unsigned long flags;
2183
2184 spin_lock_irqsave(&priv->stats_lock, flags);
2185 memcpy(&priv->iwstats, &priv->privstats, sizeof(priv->iwstats));
2186 spin_unlock_irqrestore(&priv->stats_lock, flags);
2187
2188 return &priv->iwstats;
2189}
2190
2191
2192#define IW_IOCTL(x) [(x) - SIOCSIWCOMMIT]
2193static const iw_handler rndis_iw_handler[] =
2194{
2195 IW_IOCTL(SIOCSIWCOMMIT) = rndis_iw_commit,
John W. Linville5c8fa4f2009-03-26 23:39:53 +02002196 IW_IOCTL(SIOCGIWNAME) = (iw_handler) cfg80211_wext_giwname,
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002197 IW_IOCTL(SIOCSIWFREQ) = rndis_iw_set_freq,
2198 IW_IOCTL(SIOCGIWFREQ) = rndis_iw_get_freq,
2199 IW_IOCTL(SIOCSIWMODE) = rndis_iw_set_mode,
2200 IW_IOCTL(SIOCGIWMODE) = rndis_iw_get_mode,
2201 IW_IOCTL(SIOCGIWRANGE) = rndis_iw_get_range,
2202 IW_IOCTL(SIOCSIWAP) = rndis_iw_set_bssid,
2203 IW_IOCTL(SIOCGIWAP) = rndis_iw_get_bssid,
2204 IW_IOCTL(SIOCSIWSCAN) = rndis_iw_set_scan,
2205 IW_IOCTL(SIOCGIWSCAN) = rndis_iw_get_scan,
2206 IW_IOCTL(SIOCSIWESSID) = rndis_iw_set_essid,
2207 IW_IOCTL(SIOCGIWESSID) = rndis_iw_get_essid,
2208 IW_IOCTL(SIOCSIWNICKN) = rndis_iw_set_nick,
2209 IW_IOCTL(SIOCGIWNICKN) = rndis_iw_get_nick,
2210 IW_IOCTL(SIOCGIWRATE) = rndis_iw_get_rate,
2211 IW_IOCTL(SIOCSIWRTS) = rndis_iw_set_rts,
2212 IW_IOCTL(SIOCGIWRTS) = rndis_iw_get_rts,
2213 IW_IOCTL(SIOCSIWFRAG) = rndis_iw_set_frag,
2214 IW_IOCTL(SIOCGIWFRAG) = rndis_iw_get_frag,
2215 IW_IOCTL(SIOCSIWTXPOW) = rndis_iw_set_txpower,
2216 IW_IOCTL(SIOCGIWTXPOW) = rndis_iw_get_txpower,
2217 IW_IOCTL(SIOCSIWENCODE) = rndis_iw_set_encode,
2218 IW_IOCTL(SIOCSIWENCODEEXT) = rndis_iw_set_encode_ext,
2219 IW_IOCTL(SIOCSIWAUTH) = rndis_iw_set_auth,
2220 IW_IOCTL(SIOCGIWAUTH) = rndis_iw_get_auth,
2221 IW_IOCTL(SIOCSIWGENIE) = rndis_iw_set_genie,
2222 IW_IOCTL(SIOCGIWGENIE) = rndis_iw_get_genie,
2223 IW_IOCTL(SIOCSIWMLME) = rndis_iw_set_mlme,
2224};
2225
2226static const iw_handler rndis_wext_private_handler[] = {
2227};
2228
2229static const struct iw_priv_args rndis_wext_private_args[] = {
2230};
2231
2232
2233static const struct iw_handler_def rndis_iw_handlers = {
2234 .num_standard = ARRAY_SIZE(rndis_iw_handler),
2235 .num_private = ARRAY_SIZE(rndis_wext_private_handler),
2236 .num_private_args = ARRAY_SIZE(rndis_wext_private_args),
2237 .standard = (iw_handler *)rndis_iw_handler,
2238 .private = (iw_handler *)rndis_wext_private_handler,
2239 .private_args = (struct iw_priv_args *)rndis_wext_private_args,
2240 .get_wireless_stats = rndis_get_wireless_stats,
2241};
2242
2243
2244static void rndis_wext_worker(struct work_struct *work)
2245{
2246 struct rndis_wext_private *priv =
2247 container_of(work, struct rndis_wext_private, work);
2248 struct usbnet *usbdev = priv->usbdev;
2249 union iwreq_data evt;
2250 unsigned char bssid[ETH_ALEN];
Scott Ashcroft43646232008-05-27 00:06:15 +03002251 struct ndis_80211_assoc_info *info;
2252 int assoc_size = sizeof(*info) + IW_CUSTOM_MAX + 32;
2253 int ret, offset;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002254
Jussi Kivilinna6010ce02008-06-02 18:35:21 +03002255 if (test_and_clear_bit(WORK_LINK_UP, &priv->work_pending)) {
Jussi Kivilinna5331b962008-06-02 18:35:29 +03002256 netif_carrier_on(usbdev->net);
2257
Scott Ashcroft43646232008-05-27 00:06:15 +03002258 info = kzalloc(assoc_size, GFP_KERNEL);
2259 if (!info)
2260 goto get_bssid;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002261
Scott Ashcroft43646232008-05-27 00:06:15 +03002262 /* Get association info IEs from device and send them back to
2263 * userspace. */
2264 ret = get_association_info(usbdev, info, assoc_size);
2265 if (!ret) {
2266 evt.data.length = le32_to_cpu(info->req_ie_length);
2267 if (evt.data.length > 0) {
2268 offset = le32_to_cpu(info->offset_req_ies);
2269 wireless_send_event(usbdev->net,
2270 IWEVASSOCREQIE, &evt,
2271 (char *)info + offset);
2272 }
2273
2274 evt.data.length = le32_to_cpu(info->resp_ie_length);
2275 if (evt.data.length > 0) {
2276 offset = le32_to_cpu(info->offset_resp_ies);
2277 wireless_send_event(usbdev->net,
2278 IWEVASSOCRESPIE, &evt,
2279 (char *)info + offset);
2280 }
2281 }
2282
2283 kfree(info);
2284
2285get_bssid:
2286 ret = get_bssid(usbdev, bssid);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002287 if (!ret) {
2288 evt.data.flags = 0;
2289 evt.data.length = 0;
2290 memcpy(evt.ap_addr.sa_data, bssid, ETH_ALEN);
2291 wireless_send_event(usbdev->net, SIOCGIWAP, &evt, NULL);
2292 }
2293 }
2294
Jussi Kivilinna6010ce02008-06-02 18:35:21 +03002295 if (test_and_clear_bit(WORK_LINK_DOWN, &priv->work_pending)) {
Jussi Kivilinna5331b962008-06-02 18:35:29 +03002296 netif_carrier_off(usbdev->net);
2297
Jussi Kivilinna6010ce02008-06-02 18:35:21 +03002298 evt.data.flags = 0;
2299 evt.data.length = 0;
2300 memset(evt.ap_addr.sa_data, 0, ETH_ALEN);
2301 wireless_send_event(usbdev->net, SIOCGIWAP, &evt, NULL);
2302 }
2303
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002304 if (test_and_clear_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending))
2305 set_multicast_list(usbdev);
2306}
2307
2308static void rndis_wext_set_multicast_list(struct net_device *dev)
2309{
Wang Chen524ad0a2008-11-12 23:39:10 -08002310 struct usbnet *usbdev = netdev_priv(dev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002311 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
2312
Jussi Kivilinnaa67edb92008-06-02 18:35:36 +03002313 if (test_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending))
2314 return;
2315
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002316 set_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending);
2317 queue_work(priv->workqueue, &priv->work);
2318}
2319
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002320static void rndis_wext_link_change(struct usbnet *usbdev, int state)
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002321{
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002322 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002323
Jussi Kivilinna6010ce02008-06-02 18:35:21 +03002324 /* queue work to avoid recursive calls into rndis_command */
2325 set_bit(state ? WORK_LINK_UP : WORK_LINK_DOWN, &priv->work_pending);
2326 queue_work(priv->workqueue, &priv->work);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002327}
2328
2329
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002330static int rndis_wext_get_caps(struct usbnet *usbdev)
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002331{
2332 struct {
2333 __le32 num_items;
2334 __le32 items[8];
2335 } networks_supported;
2336 int len, retval, i, n;
2337 __le32 tx_power;
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002338 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002339
2340 /* determine if supports setting txpower */
2341 len = sizeof(tx_power);
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002342 retval = rndis_query_oid(usbdev, OID_802_11_TX_POWER_LEVEL, &tx_power,
2343 &len);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002344 if (retval == 0 && le32_to_cpu(tx_power) != 0xFF)
2345 priv->caps |= CAP_SUPPORT_TXPOWER;
2346
2347 /* determine supported modes */
2348 len = sizeof(networks_supported);
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002349 retval = rndis_query_oid(usbdev, OID_802_11_NETWORK_TYPES_SUPPORTED,
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002350 &networks_supported, &len);
2351 if (retval >= 0) {
2352 n = le32_to_cpu(networks_supported.num_items);
2353 if (n > 8)
2354 n = 8;
2355 for (i = 0; i < n; i++) {
2356 switch (le32_to_cpu(networks_supported.items[i])) {
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +02002357 case ndis_80211_type_freq_hop:
2358 case ndis_80211_type_direct_seq:
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002359 priv->caps |= CAP_MODE_80211B;
2360 break;
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +02002361 case ndis_80211_type_ofdm_a:
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002362 priv->caps |= CAP_MODE_80211A;
2363 break;
Jussi Kivilinna1e41e1d2008-03-08 01:23:17 +02002364 case ndis_80211_type_ofdm_g:
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002365 priv->caps |= CAP_MODE_80211G;
2366 break;
2367 }
2368 }
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002369 }
2370
2371 return retval;
2372}
2373
2374
2375#define STATS_UPDATE_JIFFIES (HZ)
2376static void rndis_update_wireless_stats(struct work_struct *work)
2377{
2378 struct rndis_wext_private *priv =
2379 container_of(work, struct rndis_wext_private, stats_work.work);
2380 struct usbnet *usbdev = priv->usbdev;
2381 struct iw_statistics iwstats;
2382 __le32 rssi, tmp;
Jussi Kivilinnaa97b1f32008-02-06 15:36:10 +02002383 int len, ret, j;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002384 unsigned long flags;
2385 int update_jiffies = STATS_UPDATE_JIFFIES;
2386 void *buf;
2387
2388 spin_lock_irqsave(&priv->stats_lock, flags);
2389 memcpy(&iwstats, &priv->privstats, sizeof(iwstats));
2390 spin_unlock_irqrestore(&priv->stats_lock, flags);
2391
2392 /* only update stats when connected */
2393 if (!is_associated(usbdev)) {
2394 iwstats.qual.qual = 0;
2395 iwstats.qual.level = 0;
2396 iwstats.qual.updated = IW_QUAL_QUAL_UPDATED
2397 | IW_QUAL_LEVEL_UPDATED
2398 | IW_QUAL_NOISE_INVALID
2399 | IW_QUAL_QUAL_INVALID
2400 | IW_QUAL_LEVEL_INVALID;
2401 goto end;
2402 }
2403
2404 len = sizeof(rssi);
2405 ret = rndis_query_oid(usbdev, OID_802_11_RSSI, &rssi, &len);
2406
2407 devdbg(usbdev, "stats: OID_802_11_RSSI -> %d, rssi:%d", ret,
2408 le32_to_cpu(rssi));
2409 if (ret == 0) {
2410 memset(&iwstats.qual, 0, sizeof(iwstats.qual));
2411 iwstats.qual.qual = level_to_qual(le32_to_cpu(rssi));
2412 iwstats.qual.level = le32_to_cpu(rssi);
2413 iwstats.qual.updated = IW_QUAL_QUAL_UPDATED
2414 | IW_QUAL_LEVEL_UPDATED
2415 | IW_QUAL_NOISE_INVALID;
2416 }
2417
2418 memset(&iwstats.discard, 0, sizeof(iwstats.discard));
2419
2420 len = sizeof(tmp);
2421 ret = rndis_query_oid(usbdev, OID_GEN_XMIT_ERROR, &tmp, &len);
2422 if (ret == 0)
2423 iwstats.discard.misc += le32_to_cpu(tmp);
2424
2425 len = sizeof(tmp);
2426 ret = rndis_query_oid(usbdev, OID_GEN_RCV_ERROR, &tmp, &len);
2427 if (ret == 0)
2428 iwstats.discard.misc += le32_to_cpu(tmp);
2429
2430 len = sizeof(tmp);
2431 ret = rndis_query_oid(usbdev, OID_GEN_RCV_NO_BUFFER, &tmp, &len);
2432 if (ret == 0)
2433 iwstats.discard.misc += le32_to_cpu(tmp);
2434
Jussi Kivilinnaa97b1f32008-02-06 15:36:10 +02002435 /* Workaround transfer stalls on poor quality links.
2436 * TODO: find right way to fix these stalls (as stalls do not happen
2437 * with ndiswrapper/windows driver). */
2438 if (iwstats.qual.qual <= 25) {
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002439 /* Decrease stats worker interval to catch stalls.
2440 * faster. Faster than 400-500ms causes packet loss,
2441 * Slower doesn't catch stalls fast enough.
2442 */
2443 j = msecs_to_jiffies(priv->param_workaround_interval);
2444 if (j > STATS_UPDATE_JIFFIES)
2445 j = STATS_UPDATE_JIFFIES;
2446 else if (j <= 0)
2447 j = 1;
2448 update_jiffies = j;
2449
2450 /* Send scan OID. Use of both OIDs is required to get device
2451 * working.
2452 */
Harvey Harrison35c26c22009-02-14 22:56:56 -08002453 tmp = cpu_to_le32(1);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002454 rndis_set_oid(usbdev, OID_802_11_BSSID_LIST_SCAN, &tmp,
2455 sizeof(tmp));
2456
2457 len = CONTROL_BUFFER_SIZE;
2458 buf = kmalloc(len, GFP_KERNEL);
2459 if (!buf)
2460 goto end;
2461
2462 rndis_query_oid(usbdev, OID_802_11_BSSID_LIST, buf, &len);
2463 kfree(buf);
2464 }
2465end:
2466 spin_lock_irqsave(&priv->stats_lock, flags);
2467 memcpy(&priv->privstats, &iwstats, sizeof(iwstats));
2468 spin_unlock_irqrestore(&priv->stats_lock, flags);
2469
2470 if (update_jiffies >= HZ)
2471 update_jiffies = round_jiffies_relative(update_jiffies);
2472 else {
2473 j = round_jiffies_relative(update_jiffies);
2474 if (abs(j - update_jiffies) <= 10)
2475 update_jiffies = j;
2476 }
2477
2478 queue_delayed_work(priv->workqueue, &priv->stats_work, update_jiffies);
2479}
2480
2481
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002482static int bcm4320_early_init(struct usbnet *usbdev)
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002483{
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002484 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002485 char buf[8];
2486
2487 /* Early initialization settings, setting these won't have effect
2488 * if called after generic_rndis_bind().
2489 */
2490
2491 priv->param_country[0] = modparam_country[0];
2492 priv->param_country[1] = modparam_country[1];
2493 priv->param_country[2] = 0;
2494 priv->param_frameburst = modparam_frameburst;
2495 priv->param_afterburner = modparam_afterburner;
2496 priv->param_power_save = modparam_power_save;
2497 priv->param_power_output = modparam_power_output;
2498 priv->param_roamtrigger = modparam_roamtrigger;
2499 priv->param_roamdelta = modparam_roamdelta;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002500
2501 priv->param_country[0] = toupper(priv->param_country[0]);
2502 priv->param_country[1] = toupper(priv->param_country[1]);
2503 /* doesn't support EU as country code, use FI instead */
2504 if (!strcmp(priv->param_country, "EU"))
2505 strcpy(priv->param_country, "FI");
2506
2507 if (priv->param_power_save < 0)
2508 priv->param_power_save = 0;
2509 else if (priv->param_power_save > 2)
2510 priv->param_power_save = 2;
2511
Jussi Kivilinnaa7624832008-05-27 11:15:08 +03002512 if (priv->param_power_output < 0)
2513 priv->param_power_output = 0;
2514 else if (priv->param_power_output > 3)
2515 priv->param_power_output = 3;
2516
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002517 if (priv->param_roamtrigger < -80)
2518 priv->param_roamtrigger = -80;
2519 else if (priv->param_roamtrigger > -60)
2520 priv->param_roamtrigger = -60;
2521
2522 if (priv->param_roamdelta < 0)
2523 priv->param_roamdelta = 0;
2524 else if (priv->param_roamdelta > 2)
2525 priv->param_roamdelta = 2;
2526
Roel Kluin4d381ff2008-04-23 22:10:29 +02002527 if (modparam_workaround_interval < 0)
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002528 priv->param_workaround_interval = 500;
Roel Kluin4d381ff2008-04-23 22:10:29 +02002529 else
2530 priv->param_workaround_interval = modparam_workaround_interval;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002531
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002532 rndis_set_config_parameter_str(usbdev, "Country", priv->param_country);
2533 rndis_set_config_parameter_str(usbdev, "FrameBursting",
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002534 priv->param_frameburst ? "1" : "0");
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002535 rndis_set_config_parameter_str(usbdev, "Afterburner",
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002536 priv->param_afterburner ? "1" : "0");
2537 sprintf(buf, "%d", priv->param_power_save);
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002538 rndis_set_config_parameter_str(usbdev, "PowerSaveMode", buf);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002539 sprintf(buf, "%d", priv->param_power_output);
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002540 rndis_set_config_parameter_str(usbdev, "PwrOut", buf);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002541 sprintf(buf, "%d", priv->param_roamtrigger);
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002542 rndis_set_config_parameter_str(usbdev, "RoamTrigger", buf);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002543 sprintf(buf, "%d", priv->param_roamdelta);
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002544 rndis_set_config_parameter_str(usbdev, "RoamDelta", buf);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002545
2546 return 0;
2547}
2548
David S. Miller23d12e22009-03-25 00:03:16 -07002549/* same as rndis_netdev_ops but with local multicast handler */
2550static const struct net_device_ops rndis_wext_netdev_ops = {
2551 .ndo_open = usbnet_open,
2552 .ndo_stop = usbnet_stop,
2553 .ndo_start_xmit = usbnet_start_xmit,
2554 .ndo_tx_timeout = usbnet_tx_timeout,
2555 .ndo_set_mac_address = eth_mac_addr,
2556 .ndo_validate_addr = eth_validate_addr,
2557 .ndo_set_multicast_list = rndis_wext_set_multicast_list,
2558};
2559
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002560
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002561static int rndis_wext_bind(struct usbnet *usbdev, struct usb_interface *intf)
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002562{
John W. Linville5c8fa4f2009-03-26 23:39:53 +02002563 struct wiphy *wiphy;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002564 struct rndis_wext_private *priv;
2565 int retval, len;
2566 __le32 tmp;
2567
John W. Linville5c8fa4f2009-03-26 23:39:53 +02002568 /* allocate wiphy and rndis private data
2569 * NOTE: We only support a single virtual interface, so wiphy
2570 * and wireless_dev are somewhat synonymous for this device.
2571 */
2572 wiphy = wiphy_new(&rndis_config_ops, sizeof(struct rndis_wext_private));
2573 if (!wiphy)
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002574 return -ENOMEM;
2575
John W. Linville5c8fa4f2009-03-26 23:39:53 +02002576 priv = wiphy_priv(wiphy);
2577 usbdev->net->ieee80211_ptr = &priv->wdev;
2578 priv->wdev.wiphy = wiphy;
2579 priv->wdev.iftype = NL80211_IFTYPE_STATION;
2580
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002581 /* These have to be initialized before calling generic_rndis_bind().
2582 * Otherwise we'll be in big trouble in rndis_wext_early_init().
2583 */
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002584 usbdev->driver_priv = priv;
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002585 usbdev->net->wireless_handlers = &rndis_iw_handlers;
2586 priv->usbdev = usbdev;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002587
2588 mutex_init(&priv->command_lock);
2589 spin_lock_init(&priv->stats_lock);
2590
2591 /* try bind rndis_host */
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002592 retval = generic_rndis_bind(usbdev, intf, FLAG_RNDIS_PHYM_WIRELESS);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002593 if (retval < 0)
2594 goto fail;
2595
2596 /* generic_rndis_bind set packet filter to multicast_all+
2597 * promisc mode which doesn't work well for our devices (device
2598 * picks up rssi to closest station instead of to access point).
2599 *
2600 * rndis_host wants to avoid all OID as much as possible
2601 * so do promisc/multicast handling in rndis_wext.
2602 */
David S. Miller23d12e22009-03-25 00:03:16 -07002603 usbdev->net->netdev_ops = &rndis_wext_netdev_ops;
2604
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002605 tmp = RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_BROADCAST;
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002606 retval = rndis_set_oid(usbdev, OID_GEN_CURRENT_PACKET_FILTER, &tmp,
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002607 sizeof(tmp));
2608
2609 len = sizeof(tmp);
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002610 retval = rndis_query_oid(usbdev, OID_802_3_MAXIMUM_LIST_SIZE, &tmp,
2611 &len);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002612 priv->multicast_size = le32_to_cpu(tmp);
2613 if (retval < 0 || priv->multicast_size < 0)
2614 priv->multicast_size = 0;
2615 if (priv->multicast_size > 0)
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002616 usbdev->net->flags |= IFF_MULTICAST;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002617 else
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002618 usbdev->net->flags &= ~IFF_MULTICAST;
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002619
2620 priv->iwstats.qual.qual = 0;
2621 priv->iwstats.qual.level = 0;
2622 priv->iwstats.qual.updated = IW_QUAL_QUAL_UPDATED
2623 | IW_QUAL_LEVEL_UPDATED
2624 | IW_QUAL_NOISE_INVALID
2625 | IW_QUAL_QUAL_INVALID
2626 | IW_QUAL_LEVEL_INVALID;
2627
John W. Linville5c8fa4f2009-03-26 23:39:53 +02002628 /* fill-out wiphy structure and register w/ cfg80211 */
2629 memcpy(wiphy->perm_addr, usbdev->net->dev_addr, ETH_ALEN);
2630 wiphy->privid = rndis_wiphy_privid;
2631 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION)
2632 | BIT(NL80211_IFTYPE_ADHOC);
2633 wiphy->max_scan_ssids = 1;
2634
2635 /* TODO: fill-out band information based on priv->caps */
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002636 rndis_wext_get_caps(usbdev);
John W. Linville5c8fa4f2009-03-26 23:39:53 +02002637
2638 memcpy(priv->channels, rndis_channels, sizeof(rndis_channels));
2639 memcpy(priv->rates, rndis_rates, sizeof(rndis_rates));
2640 priv->band.channels = priv->channels;
2641 priv->band.n_channels = ARRAY_SIZE(rndis_channels);
2642 priv->band.bitrates = priv->rates;
2643 priv->band.n_bitrates = ARRAY_SIZE(rndis_rates);
2644 wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band;
2645
2646 set_wiphy_dev(wiphy, &usbdev->udev->dev);
2647
2648 if (wiphy_register(wiphy)) {
2649 wiphy_free(wiphy);
2650 return -ENODEV;
2651 }
2652
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002653 set_default_iw_params(usbdev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002654
2655 /* turn radio on */
2656 priv->radio_on = 1;
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002657 disassociate(usbdev, 1);
2658 netif_carrier_off(usbdev->net);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002659
2660 /* because rndis_command() sleeps we need to use workqueue */
2661 priv->workqueue = create_singlethread_workqueue("rndis_wlan");
2662 INIT_DELAYED_WORK(&priv->stats_work, rndis_update_wireless_stats);
2663 queue_delayed_work(priv->workqueue, &priv->stats_work,
2664 round_jiffies_relative(STATS_UPDATE_JIFFIES));
2665 INIT_WORK(&priv->work, rndis_wext_worker);
2666
2667 return 0;
2668
2669fail:
2670 kfree(priv);
2671 return retval;
2672}
2673
2674
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002675static void rndis_wext_unbind(struct usbnet *usbdev, struct usb_interface *intf)
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002676{
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002677 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002678
2679 /* turn radio off */
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002680 disassociate(usbdev, 0);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002681
2682 cancel_delayed_work_sync(&priv->stats_work);
2683 cancel_work_sync(&priv->work);
2684 flush_workqueue(priv->workqueue);
2685 destroy_workqueue(priv->workqueue);
2686
2687 if (priv && priv->wpa_ie_len)
2688 kfree(priv->wpa_ie);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002689
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002690 rndis_unbind(usbdev, intf);
John W. Linville5c8fa4f2009-03-26 23:39:53 +02002691
2692 wiphy_unregister(priv->wdev.wiphy);
2693 wiphy_free(priv->wdev.wiphy);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002694}
2695
2696
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002697static int rndis_wext_reset(struct usbnet *usbdev)
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002698{
Jussi Kivilinnaa19d7292008-06-02 18:35:44 +03002699 return deauthenticate(usbdev);
Jussi Kivilinnabf164cc2008-01-26 00:51:51 +02002700}
2701
2702
2703static const struct driver_info bcm4320b_info = {
2704 .description = "Wireless RNDIS device, BCM4320b based",
2705 .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT,
2706 .bind = rndis_wext_bind,
2707 .unbind = rndis_wext_unbind,
2708 .status = rndis_status,
2709 .rx_fixup = rndis_rx_fixup,
2710 .tx_fixup = rndis_tx_fixup,
2711 .reset = rndis_wext_reset,
2712 .early_init = bcm4320_early_init,
2713 .link_change = rndis_wext_link_change,
2714};
2715
2716static const struct driver_info bcm4320a_info = {
2717 .description = "Wireless RNDIS device, BCM4320a based",
2718 .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT,
2719 .bind = rndis_wext_bind,
2720 .unbind = rndis_wext_unbind,
2721 .status = rndis_status,
2722 .rx_fixup = rndis_rx_fixup,
2723 .tx_fixup = rndis_tx_fixup,
2724 .reset = rndis_wext_reset,
2725 .early_init = bcm4320_early_init,
2726 .link_change = rndis_wext_link_change,
2727};
2728
2729static const struct driver_info rndis_wext_info = {
2730 .description = "Wireless RNDIS device",
2731 .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT,
2732 .bind = rndis_wext_bind,
2733 .unbind = rndis_wext_unbind,
2734 .status = rndis_status,
2735 .rx_fixup = rndis_rx_fixup,
2736 .tx_fixup = rndis_tx_fixup,
2737 .reset = rndis_wext_reset,
2738 .early_init = bcm4320_early_init,
2739 .link_change = rndis_wext_link_change,
2740};
2741
2742/*-------------------------------------------------------------------------*/
2743
2744static const struct usb_device_id products [] = {
2745#define RNDIS_MASTER_INTERFACE \
2746 .bInterfaceClass = USB_CLASS_COMM, \
2747 .bInterfaceSubClass = 2 /* ACM */, \
2748 .bInterfaceProtocol = 0x0ff
2749
2750/* INF driver for these devices have DriverVer >= 4.xx.xx.xx and many custom
2751 * parameters available. Chipset marked as 'BCM4320SKFBG' in NDISwrapper-wiki.
2752 */
2753{
2754 .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
2755 | USB_DEVICE_ID_MATCH_DEVICE,
2756 .idVendor = 0x0411,
2757 .idProduct = 0x00bc, /* Buffalo WLI-U2-KG125S */
2758 RNDIS_MASTER_INTERFACE,
2759 .driver_info = (unsigned long) &bcm4320b_info,
2760}, {
2761 .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
2762 | USB_DEVICE_ID_MATCH_DEVICE,
2763 .idVendor = 0x0baf,
2764 .idProduct = 0x011b, /* U.S. Robotics USR5421 */
2765 RNDIS_MASTER_INTERFACE,
2766 .driver_info = (unsigned long) &bcm4320b_info,
2767}, {
2768 .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
2769 | USB_DEVICE_ID_MATCH_DEVICE,
2770 .idVendor = 0x050d,
2771 .idProduct = 0x011b, /* Belkin F5D7051 */
2772 RNDIS_MASTER_INTERFACE,
2773 .driver_info = (unsigned long) &bcm4320b_info,
2774}, {
2775 .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
2776 | USB_DEVICE_ID_MATCH_DEVICE,
2777 .idVendor = 0x1799, /* Belkin has two vendor ids */
2778 .idProduct = 0x011b, /* Belkin F5D7051 */
2779 RNDIS_MASTER_INTERFACE,
2780 .driver_info = (unsigned long) &bcm4320b_info,
2781}, {
2782 .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
2783 | USB_DEVICE_ID_MATCH_DEVICE,
2784 .idVendor = 0x13b1,
2785 .idProduct = 0x0014, /* Linksys WUSB54GSv2 */
2786 RNDIS_MASTER_INTERFACE,
2787 .driver_info = (unsigned long) &bcm4320b_info,
2788}, {
2789 .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
2790 | USB_DEVICE_ID_MATCH_DEVICE,
2791 .idVendor = 0x13b1,
2792 .idProduct = 0x0026, /* Linksys WUSB54GSC */
2793 RNDIS_MASTER_INTERFACE,
2794 .driver_info = (unsigned long) &bcm4320b_info,
2795}, {
2796 .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
2797 | USB_DEVICE_ID_MATCH_DEVICE,
2798 .idVendor = 0x0b05,
2799 .idProduct = 0x1717, /* Asus WL169gE */
2800 RNDIS_MASTER_INTERFACE,
2801 .driver_info = (unsigned long) &bcm4320b_info,
2802}, {
2803 .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
2804 | USB_DEVICE_ID_MATCH_DEVICE,
2805 .idVendor = 0x0a5c,
2806 .idProduct = 0xd11b, /* Eminent EM4045 */
2807 RNDIS_MASTER_INTERFACE,
2808 .driver_info = (unsigned long) &bcm4320b_info,
2809}, {
2810 .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
2811 | USB_DEVICE_ID_MATCH_DEVICE,
2812 .idVendor = 0x1690,
2813 .idProduct = 0x0715, /* BT Voyager 1055 */
2814 RNDIS_MASTER_INTERFACE,
2815 .driver_info = (unsigned long) &bcm4320b_info,
2816},
2817/* These devices have DriverVer < 4.xx.xx.xx and do not have any custom
2818 * parameters available, hardware probably contain older firmware version with
2819 * no way of updating. Chipset marked as 'BCM4320????' in NDISwrapper-wiki.
2820 */
2821{
2822 .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
2823 | USB_DEVICE_ID_MATCH_DEVICE,
2824 .idVendor = 0x13b1,
2825 .idProduct = 0x000e, /* Linksys WUSB54GSv1 */
2826 RNDIS_MASTER_INTERFACE,
2827 .driver_info = (unsigned long) &bcm4320a_info,
2828}, {
2829 .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
2830 | USB_DEVICE_ID_MATCH_DEVICE,
2831 .idVendor = 0x0baf,
2832 .idProduct = 0x0111, /* U.S. Robotics USR5420 */
2833 RNDIS_MASTER_INTERFACE,
2834 .driver_info = (unsigned long) &bcm4320a_info,
2835}, {
2836 .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
2837 | USB_DEVICE_ID_MATCH_DEVICE,
2838 .idVendor = 0x0411,
2839 .idProduct = 0x004b, /* BUFFALO WLI-USB-G54 */
2840 RNDIS_MASTER_INTERFACE,
2841 .driver_info = (unsigned long) &bcm4320a_info,
2842},
2843/* Generic Wireless RNDIS devices that we don't have exact
2844 * idVendor/idProduct/chip yet.
2845 */
2846{
2847 /* RNDIS is MSFT's un-official variant of CDC ACM */
2848 USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff),
2849 .driver_info = (unsigned long) &rndis_wext_info,
2850}, {
2851 /* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */
2852 USB_INTERFACE_INFO(USB_CLASS_MISC, 1, 1),
2853 .driver_info = (unsigned long) &rndis_wext_info,
2854},
2855 { }, // END
2856};
2857MODULE_DEVICE_TABLE(usb, products);
2858
2859static struct usb_driver rndis_wlan_driver = {
2860 .name = "rndis_wlan",
2861 .id_table = products,
2862 .probe = usbnet_probe,
2863 .disconnect = usbnet_disconnect,
2864 .suspend = usbnet_suspend,
2865 .resume = usbnet_resume,
2866};
2867
2868static int __init rndis_wlan_init(void)
2869{
2870 return usb_register(&rndis_wlan_driver);
2871}
2872module_init(rndis_wlan_init);
2873
2874static void __exit rndis_wlan_exit(void)
2875{
2876 usb_deregister(&rndis_wlan_driver);
2877}
2878module_exit(rndis_wlan_exit);
2879
2880MODULE_AUTHOR("Bjorge Dijkstra");
2881MODULE_AUTHOR("Jussi Kivilinna");
2882MODULE_DESCRIPTION("Driver for RNDIS based USB Wireless adapters");
2883MODULE_LICENSE("GPL");
2884