"README.md" did not exist on "68730b451b7d44a9e85b76aac2d8c1aa751d38a4"
Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/*
* Driver for RNDIS based wireless USB devices.
*
* Copyright (C) 2007 by Bjorge Dijkstra <bjd@jooz.net>
* Copyright (C) 2008 by Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Portions of this file are based on NDISwrapper project,
* Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
* http://ndiswrapper.sourceforge.net/
*/
// #define DEBUG // error path messages, extra info
// #define VERBOSE // more; success messages
#include <linux/module.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/workqueue.h>
#include <linux/mutex.h>
#include <linux/mii.h>
#include <linux/usb.h>
#include <linux/usb/cdc.h>
#include <linux/wireless.h>
#include <linux/if_arp.h>
#include <linux/ctype.h>
#include <linux/spinlock.h>
#include <net/iw_handler.h>
#include <net/ieee80211.h>
#include <linux/usb/usbnet.h>
#include <linux/usb/rndis_host.h>
/* NOTE: All these are settings for Broadcom chipset */
static char modparam_country[4] = "EU";
module_param_string(country, modparam_country, 4, 0444);
MODULE_PARM_DESC(country, "Country code (ISO 3166-1 alpha-2), default: EU");
static int modparam_frameburst = 1;
module_param_named(frameburst, modparam_frameburst, int, 0444);
MODULE_PARM_DESC(frameburst, "enable frame bursting (default: on)");
static int modparam_afterburner = 0;
module_param_named(afterburner, modparam_afterburner, int, 0444);
MODULE_PARM_DESC(afterburner,
"enable afterburner aka '125 High Speed Mode' (default: off)");
static int modparam_power_save = 0;
module_param_named(power_save, modparam_power_save, int, 0444);
MODULE_PARM_DESC(power_save,
"set power save mode: 0=off, 1=on, 2=fast (default: off)");
static int modparam_power_output = 3;
module_param_named(power_output, modparam_power_output, int, 0444);
MODULE_PARM_DESC(power_output,
"set power output: 0=25%, 1=50%, 2=75%, 3=100% (default: 100%)");
static int modparam_roamtrigger = -70;
module_param_named(roamtrigger, modparam_roamtrigger, int, 0444);
MODULE_PARM_DESC(roamtrigger,
"set roaming dBm trigger: -80=optimize for distance, "
"-60=bandwidth (default: -70)");
static int modparam_roamdelta = 1;
module_param_named(roamdelta, modparam_roamdelta, int, 0444);
MODULE_PARM_DESC(roamdelta,
"set roaming tendency: 0=aggressive, 1=moderate, "
"2=conservative (default: moderate)");
static int modparam_workaround_interval = 500;
module_param_named(workaround_interval, modparam_workaround_interval,
int, 0444);
MODULE_PARM_DESC(workaround_interval,
"set stall workaround interval in msecs (default: 500)");
/* various RNDIS OID defs */
#define OID_GEN_LINK_SPEED ccpu2(0x00010107)
#define OID_GEN_RNDIS_CONFIG_PARAMETER ccpu2(0x0001021b)
#define OID_GEN_XMIT_OK ccpu2(0x00020101)
#define OID_GEN_RCV_OK ccpu2(0x00020102)
#define OID_GEN_XMIT_ERROR ccpu2(0x00020103)
#define OID_GEN_RCV_ERROR ccpu2(0x00020104)
#define OID_GEN_RCV_NO_BUFFER ccpu2(0x00020105)
#define OID_802_3_PERMANENT_ADDRESS ccpu2(0x01010101)
#define OID_802_3_CURRENT_ADDRESS ccpu2(0x01010102)
#define OID_802_3_MULTICAST_LIST ccpu2(0x01010103)
#define OID_802_3_MAXIMUM_LIST_SIZE ccpu2(0x01010104)
#define OID_802_11_BSSID ccpu2(0x0d010101)
#define OID_802_11_SSID ccpu2(0x0d010102)
#define OID_802_11_INFRASTRUCTURE_MODE ccpu2(0x0d010108)
#define OID_802_11_ADD_WEP ccpu2(0x0d010113)
#define OID_802_11_REMOVE_WEP ccpu2(0x0d010114)
#define OID_802_11_DISASSOCIATE ccpu2(0x0d010115)
#define OID_802_11_AUTHENTICATION_MODE ccpu2(0x0d010118)
#define OID_802_11_PRIVACY_FILTER ccpu2(0x0d010119)
#define OID_802_11_BSSID_LIST_SCAN ccpu2(0x0d01011a)
#define OID_802_11_ENCRYPTION_STATUS ccpu2(0x0d01011b)
#define OID_802_11_ADD_KEY ccpu2(0x0d01011d)
#define OID_802_11_REMOVE_KEY ccpu2(0x0d01011e)
#define OID_802_11_ASSOCIATION_INFORMATION ccpu2(0x0d01011f)
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#define OID_802_11_PMKID ccpu2(0x0d010123)
#define OID_802_11_NETWORK_TYPES_SUPPORTED ccpu2(0x0d010203)
#define OID_802_11_NETWORK_TYPE_IN_USE ccpu2(0x0d010204)
#define OID_802_11_TX_POWER_LEVEL ccpu2(0x0d010205)
#define OID_802_11_RSSI ccpu2(0x0d010206)
#define OID_802_11_RSSI_TRIGGER ccpu2(0x0d010207)
#define OID_802_11_FRAGMENTATION_THRESHOLD ccpu2(0x0d010209)
#define OID_802_11_RTS_THRESHOLD ccpu2(0x0d01020a)
#define OID_802_11_SUPPORTED_RATES ccpu2(0x0d01020e)
#define OID_802_11_CONFIGURATION ccpu2(0x0d010211)
#define OID_802_11_BSSID_LIST ccpu2(0x0d010217)
/* Typical noise/maximum signal level values taken from ndiswrapper iw_ndis.h */
#define WL_NOISE -96 /* typical noise level in dBm */
#define WL_SIGMAX -32 /* typical maximum signal level in dBm */
/* Assume that Broadcom 4320 (only chipset at time of writing known to be
* based on wireless rndis) has default txpower of 13dBm.
* This value is from Linksys WUSB54GSC User Guide, Appendix F: Specifications.
* 13dBm == 19.9mW
*/
#define BCM4320_DEFAULT_TXPOWER 20
/* codes for "status" field of completion messages */
#define RNDIS_STATUS_ADAPTER_NOT_READY ccpu2(0xc0010011)
#define RNDIS_STATUS_ADAPTER_NOT_OPEN ccpu2(0xc0010012)
/* NDIS data structures. Taken from wpa_supplicant driver_ndis.c
* slightly modified for datatype endianess, etc
*/
#define NDIS_802_11_LENGTH_SSID 32
#define NDIS_802_11_LENGTH_RATES 8
#define NDIS_802_11_LENGTH_RATES_EX 16
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
enum ndis_80211_net_type {
ndis_80211_type_freq_hop,
ndis_80211_type_direct_seq,
ndis_80211_type_ofdm_a,
ndis_80211_type_ofdm_g
};
enum ndis_80211_net_infra {
ndis_80211_infra_adhoc,
ndis_80211_infra_infra,
ndis_80211_infra_auto_unknown
};
enum ndis_80211_auth_mode {
ndis_80211_auth_open,
ndis_80211_auth_shared,
ndis_80211_auth_auto_switch,
ndis_80211_auth_wpa,
ndis_80211_auth_wpa_psk,
ndis_80211_auth_wpa_none,
ndis_80211_auth_wpa2,
ndis_80211_auth_wpa2_psk
};
enum ndis_80211_encr_status {
ndis_80211_encr_wep_enabled,
ndis_80211_encr_disabled,
ndis_80211_encr_wep_key_absent,
ndis_80211_encr_not_supported,
ndis_80211_encr_tkip_enabled,
ndis_80211_encr_tkip_key_absent,
ndis_80211_encr_ccmp_enabled,
ndis_80211_encr_ccmp_key_absent
};
enum ndis_80211_priv_filter {
ndis_80211_priv_accept_all,
ndis_80211_priv_8021x_wep
};
struct ndis_80211_ssid {
__le32 length;
u8 essid[NDIS_802_11_LENGTH_SSID];
} __attribute__((packed));
struct ndis_80211_conf_freq_hop {
__le32 length;
__le32 hop_pattern;
__le32 hop_set;
__le32 dwell_time;
} __attribute__((packed));
struct ndis_80211_conf {
__le32 length;
__le32 beacon_period;
__le32 atim_window;
__le32 ds_config;
struct ndis_80211_conf_freq_hop fh_config;
} __attribute__((packed));
struct ndis_80211_bssid_ex {
__le32 length;
u8 mac[6];
u8 padding[2];
struct ndis_80211_ssid ssid;
__le32 privacy;
__le32 rssi;
__le32 net_type;
struct ndis_80211_conf config;
__le32 net_infra;
u8 rates[NDIS_802_11_LENGTH_RATES_EX];
__le32 ie_length;
u8 ies[0];
} __attribute__((packed));
struct ndis_80211_bssid_list_ex {
__le32 num_items;
struct ndis_80211_bssid_ex bssid[0];
} __attribute__((packed));
struct ndis_80211_fixed_ies {
u8 timestamp[8];
__le16 beacon_interval;
__le16 capabilities;
} __attribute__((packed));
struct ndis_80211_wep_key {
__le32 size;
__le32 index;
__le32 length;
u8 material[32];
} __attribute__((packed));
struct ndis_80211_key {
__le32 size;
__le32 index;
__le32 length;
u8 bssid[6];
u8 padding[6];
u8 rsc[8];
u8 material[32];
} __attribute__((packed));
struct ndis_80211_remove_key {
__le32 size;
__le32 index;
u8 bssid[6];
} __attribute__((packed));
struct ndis_config_param {
__le32 name_offs;
__le32 name_length;
__le32 type;
__le32 value_offs;
__le32 value_length;
} __attribute__((packed));
struct ndis_80211_assoc_info {
__le32 length;
__le16 req_ies;
struct req_ie {
__le16 capa;
__le16 listen_interval;
u8 cur_ap_address[6];
} req_ie;
__le32 req_ie_length;
__le32 offset_req_ies;
__le16 resp_ies;
struct resp_ie {
__le16 capa;
__le16 status_code;
__le16 assoc_id;
} resp_ie;
__le32 resp_ie_length;
__le32 offset_resp_ies;
} __attribute__((packed));
/* these have to match what is in wpa_supplicant */
enum wpa_alg { WPA_ALG_NONE, WPA_ALG_WEP, WPA_ALG_TKIP, WPA_ALG_CCMP };
enum wpa_cipher { CIPHER_NONE, CIPHER_WEP40, CIPHER_TKIP, CIPHER_CCMP,
CIPHER_WEP104 };
enum wpa_key_mgmt { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE,
KEY_MGMT_802_1X_NO_WPA, KEY_MGMT_WPA_NONE };
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
/*
* private data
*/
#define NET_TYPE_11FB 0
#define CAP_MODE_80211A 1
#define CAP_MODE_80211B 2
#define CAP_MODE_80211G 4
#define CAP_MODE_MASK 7
#define CAP_SUPPORT_TXPOWER 8
#define WORK_CONNECTION_EVENT (1<<0)
#define WORK_SET_MULTICAST_LIST (1<<1)
/* RNDIS device private data */
struct rndis_wext_private {
char name[32];
struct usbnet *usbdev;
struct workqueue_struct *workqueue;
struct delayed_work stats_work;
struct work_struct work;
struct mutex command_lock;
spinlock_t stats_lock;
unsigned long work_pending;
struct iw_statistics iwstats;
struct iw_statistics privstats;
int nick_len;
char nick[32];
int caps;
int multicast_size;
/* module parameters */
char param_country[4];
int param_frameburst;
int param_afterburner;
int param_power_save;
int param_power_output;
int param_roamtrigger;
int param_roamdelta;
u32 param_workaround_interval;
/* hardware state */
int radio_on;
int infra_mode;
struct ndis_80211_ssid essid;
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
/* encryption stuff */
int encr_tx_key_index;
char encr_keys[4][32];
int encr_key_len[4];
int wpa_version;
int wpa_keymgmt;
int wpa_authalg;
int wpa_ie_len;
u8 *wpa_ie;
int wpa_cipher_pair;
int wpa_cipher_group;
};
static const int freq_chan[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442,
2447, 2452, 2457, 2462, 2467, 2472, 2484 };
static const int rates_80211g[8] = { 6, 9, 12, 18, 24, 36, 48, 54 };
static const int bcm4320_power_output[4] = { 25, 50, 75, 100 };
static const unsigned char zero_bssid[ETH_ALEN] = {0,};
static const unsigned char ffff_bssid[ETH_ALEN] = { 0xff, 0xff, 0xff,
0xff, 0xff, 0xff };
static struct rndis_wext_private *get_rndis_wext_priv(struct usbnet *dev)
{
return (struct rndis_wext_private *)dev->driver_priv;
}
static u32 get_bcm4320_power(struct rndis_wext_private *priv)
{
return BCM4320_DEFAULT_TXPOWER *
bcm4320_power_output[priv->param_power_output] / 100;
}
/* translate error code */
static int rndis_error_status(__le32 rndis_status)
{
int ret = -EINVAL;
switch (rndis_status) {
case RNDIS_STATUS_SUCCESS:
ret = 0;
break;
case RNDIS_STATUS_FAILURE:
case RNDIS_STATUS_INVALID_DATA:
ret = -EINVAL;
break;
case RNDIS_STATUS_NOT_SUPPORTED:
ret = -EOPNOTSUPP;
break;
case RNDIS_STATUS_ADAPTER_NOT_READY:
case RNDIS_STATUS_ADAPTER_NOT_OPEN:
ret = -EBUSY;
break;
}
return ret;
}
static int rndis_query_oid(struct usbnet *dev, __le32 oid, void *data, int *len)
{
struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
union {
void *buf;
struct rndis_msg_hdr *header;
struct rndis_query *get;
struct rndis_query_c *get_c;
} u;
int ret, buflen;
buflen = *len + sizeof(*u.get);
if (buflen < CONTROL_BUFFER_SIZE)
buflen = CONTROL_BUFFER_SIZE;
u.buf = kmalloc(buflen, GFP_KERNEL);
if (!u.buf)
return -ENOMEM;
memset(u.get, 0, sizeof *u.get);
u.get->msg_type = RNDIS_MSG_QUERY;
u.get->msg_len = ccpu2(sizeof *u.get);
u.get->oid = oid;
mutex_lock(&priv->command_lock);
ret = rndis_command(dev, u.header);
mutex_unlock(&priv->command_lock);
if (ret == 0) {
ret = le32_to_cpu(u.get_c->len);
*len = (*len > ret) ? ret : *len;
memcpy(data, u.buf + le32_to_cpu(u.get_c->offset) + 8, *len);
ret = rndis_error_status(u.get_c->status);
}
kfree(u.buf);
return ret;
}
static int rndis_set_oid(struct usbnet *dev, __le32 oid, void *data, int len)
{
struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
union {
void *buf;
struct rndis_msg_hdr *header;
struct rndis_set *set;
struct rndis_set_c *set_c;
} u;
int ret, buflen;
buflen = len + sizeof(*u.set);
if (buflen < CONTROL_BUFFER_SIZE)
buflen = CONTROL_BUFFER_SIZE;
u.buf = kmalloc(buflen, GFP_KERNEL);
if (!u.buf)
return -ENOMEM;
memset(u.set, 0, sizeof *u.set);
u.set->msg_type = RNDIS_MSG_SET;
u.set->msg_len = cpu_to_le32(sizeof(*u.set) + len);
u.set->oid = oid;
u.set->len = cpu_to_le32(len);
u.set->offset = ccpu2(sizeof(*u.set) - 8);
u.set->handle = ccpu2(0);
memcpy(u.buf + sizeof(*u.set), data, len);
mutex_lock(&priv->command_lock);
ret = rndis_command(dev, u.header);
mutex_unlock(&priv->command_lock);
if (ret == 0)
ret = rndis_error_status(u.set_c->status);
kfree(u.buf);
return ret;
}
/*
* Specs say that we can only set config parameters only soon after device
* initialization.
* value_type: 0 = u32, 2 = unicode string
*/
static int rndis_set_config_parameter(struct usbnet *dev, char *param,
int value_type, void *value)
{
struct ndis_config_param *infobuf;
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
int value_len, info_len, param_len, ret, i;
__le16 *unibuf;
__le32 *dst_value;
if (value_type == 0)
value_len = sizeof(__le32);
else if (value_type == 2)
value_len = strlen(value) * sizeof(__le16);
else
return -EINVAL;
param_len = strlen(param) * sizeof(__le16);
info_len = sizeof(*infobuf) + param_len + value_len;
#ifdef DEBUG
info_len += 12;
#endif
infobuf = kmalloc(info_len, GFP_KERNEL);
if (!infobuf)
return -ENOMEM;
#ifdef DEBUG
info_len -= 12;
/* extra 12 bytes are for padding (debug output) */
memset(infobuf, 0xCC, info_len + 12);
#endif
if (value_type == 2)
devdbg(dev, "setting config parameter: %s, value: %s",
param, (u8 *)value);
else
devdbg(dev, "setting config parameter: %s, value: %d",
param, *(u32 *)value);
infobuf->name_offs = cpu_to_le32(sizeof(*infobuf));
infobuf->name_length = cpu_to_le32(param_len);
infobuf->type = cpu_to_le32(value_type);
infobuf->value_offs = cpu_to_le32(sizeof(*infobuf) + param_len);
infobuf->value_length = cpu_to_le32(value_len);
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
/* simple string to unicode string conversion */
unibuf = (void *)infobuf + sizeof(*infobuf);
for (i = 0; i < param_len / sizeof(__le16); i++)
unibuf[i] = cpu_to_le16(param[i]);
if (value_type == 2) {
unibuf = (void *)infobuf + sizeof(*infobuf) + param_len;
for (i = 0; i < value_len / sizeof(__le16); i++)
unibuf[i] = cpu_to_le16(((u8 *)value)[i]);
} else {
dst_value = (void *)infobuf + sizeof(*infobuf) + param_len;
*dst_value = cpu_to_le32(*(u32 *)value);
}
#ifdef DEBUG
devdbg(dev, "info buffer (len: %d):", info_len);
for (i = 0; i < info_len; i += 12) {
u32 *tmp = (u32 *)((u8 *)infobuf + i);
devdbg(dev, "%08X:%08X:%08X",
cpu_to_be32(tmp[0]),
cpu_to_be32(tmp[1]),
cpu_to_be32(tmp[2]));
}
#endif
ret = rndis_set_oid(dev, OID_GEN_RNDIS_CONFIG_PARAMETER,
infobuf, info_len);
if (ret != 0)
devdbg(dev, "setting rndis config paramater failed, %d.", ret);
kfree(infobuf);
return ret;
}
static int rndis_set_config_parameter_str(struct usbnet *dev,
char *param, char *value)
{
return(rndis_set_config_parameter(dev, param, 2, value));
}
/*static int rndis_set_config_parameter_u32(struct usbnet *dev,
char *param, u32 value)
{
return(rndis_set_config_parameter(dev, param, 0, &value));
}*/
/*
* data conversion functions
*/
static int level_to_qual(int level)
{
int qual = 100 * (level - WL_NOISE) / (WL_SIGMAX - WL_NOISE);
return qual >= 0 ? (qual <= 100 ? qual : 100) : 0;
}
static void dsconfig_to_freq(unsigned int dsconfig, struct iw_freq *freq)
{
freq->e = 0;
freq->i = 0;
freq->flags = 0;
/* see comment in wireless.h above the "struct iw_freq"
* definition for an explanation of this if
* NOTE: 1000000 is due to the kHz
*/
if (dsconfig > 1000000) {
freq->m = dsconfig / 10;
freq->e = 1;
} else
freq->m = dsconfig;
/* convert from kHz to Hz */
freq->e += 3;
}
static int freq_to_dsconfig(struct iw_freq *freq, unsigned int *dsconfig)
{
if (freq->m < 1000 && freq->e == 0) {
if (freq->m >= 1 &&
freq->m <= (sizeof(freq_chan) / sizeof(freq_chan[0])))
*dsconfig = freq_chan[freq->m - 1] * 1000;
else
return -1;
} else {
int i;
*dsconfig = freq->m;
for (i = freq->e; i > 0; i--)
*dsconfig *= 10;
*dsconfig /= 1000;
}
return 0;
}
/*
* common functions
*/
static int
add_wep_key(struct usbnet *usbdev, char *key, int key_len, int index);
static int get_essid(struct usbnet *usbdev, struct ndis_80211_ssid *ssid)
{
int ret, len;
len = sizeof(*ssid);
ret = rndis_query_oid(usbdev, OID_802_11_SSID, ssid, &len);
if (ret != 0)
#ifdef DEBUG
{
unsigned char tmp[NDIS_802_11_LENGTH_SSID + 1];
memcpy(tmp, ssid->essid, le32_to_cpu(ssid->length));
tmp[le32_to_cpu(ssid->length)] = 0;
devdbg(usbdev, "get_essid: '%s', ret: %d", tmp, ret);
}
#endif
return ret;
}
static int set_essid(struct usbnet *usbdev, struct ndis_80211_ssid *ssid)
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
{
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
int ret;
ret = rndis_set_oid(usbdev, OID_802_11_SSID, ssid, sizeof(*ssid));
if (ret == 0) {
memcpy(&priv->essid, ssid, sizeof(priv->essid));
priv->radio_on = 1;
devdbg(usbdev, "set_essid: radio_on = 1");
}
return ret;
}
static int get_bssid(struct usbnet *usbdev, u8 bssid[ETH_ALEN])
{
int ret, len;
len = ETH_ALEN;
ret = rndis_query_oid(usbdev, OID_802_11_BSSID, bssid, &len);
if (ret != 0)
memset(bssid, 0, ETH_ALEN);
return ret;
}
static int get_association_info(struct usbnet *usbdev,
struct ndis_80211_assoc_info *info, int len)
{
return rndis_query_oid(usbdev, OID_802_11_ASSOCIATION_INFORMATION,
info, &len);
}
static int is_associated(struct usbnet *usbdev)
{
u8 bssid[ETH_ALEN];
int ret;
ret = get_bssid(usbdev, bssid);
return(ret == 0 && memcmp(bssid, zero_bssid, ETH_ALEN) != 0);
}
static int disassociate(struct usbnet *usbdev, int reset_ssid)
{
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
struct ndis_80211_ssid ssid;
int i, ret = 0;
if (priv->radio_on) {
ret = rndis_set_oid(usbdev, OID_802_11_DISASSOCIATE, NULL, 0);
if (ret == 0) {
priv->radio_on = 0;
devdbg(usbdev, "disassociate: radio_on = 0");
if (reset_ssid)
msleep(100);
}
}
/* disassociate causes radio to be turned off; if reset_ssid
* is given, set random ssid to enable radio */
if (reset_ssid) {
ssid.length = cpu_to_le32(sizeof(ssid.essid));
get_random_bytes(&ssid.essid[2], sizeof(ssid.essid)-2);
ssid.essid[0] = 0x1;
ssid.essid[1] = 0xff;
for (i = 2; i < sizeof(ssid.essid); i++)
ssid.essid[i] = 0x1 + (ssid.essid[i] * 0xfe / 0xff);
ret = set_essid(usbdev, &ssid);
}
return ret;
}
static int set_auth_mode(struct usbnet *usbdev, int wpa_version, int authalg)
{
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
__le32 tmp;
int auth_mode, ret;
devdbg(usbdev, "set_auth_mode: wpa_version=0x%x authalg=0x%x "
"keymgmt=0x%x", wpa_version, authalg, priv->wpa_keymgmt);
if (wpa_version & IW_AUTH_WPA_VERSION_WPA2) {
if (priv->wpa_keymgmt & IW_AUTH_KEY_MGMT_802_1X)
auth_mode = ndis_80211_auth_wpa2;
auth_mode = ndis_80211_auth_wpa2_psk;
} else if (wpa_version & IW_AUTH_WPA_VERSION_WPA) {
if (priv->wpa_keymgmt & IW_AUTH_KEY_MGMT_802_1X)
auth_mode = ndis_80211_auth_wpa;
else if (priv->wpa_keymgmt & IW_AUTH_KEY_MGMT_PSK)
auth_mode = ndis_80211_auth_wpa_psk;
auth_mode = ndis_80211_auth_wpa_none;
} else if (authalg & IW_AUTH_ALG_SHARED_KEY) {
if (authalg & IW_AUTH_ALG_OPEN_SYSTEM)
auth_mode = ndis_80211_auth_auto_switch;
auth_mode = ndis_80211_auth_shared;
auth_mode = ndis_80211_auth_open;
tmp = cpu_to_le32(auth_mode);
ret = rndis_set_oid(usbdev, OID_802_11_AUTHENTICATION_MODE, &tmp,
sizeof(tmp));
if (ret != 0) {
devwarn(usbdev, "setting auth mode failed (%08X)", ret);
return ret;
}
priv->wpa_version = wpa_version;
priv->wpa_authalg = authalg;
return 0;
}
static int set_priv_filter(struct usbnet *usbdev)
{
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
__le32 tmp;
devdbg(usbdev, "set_priv_filter: wpa_version=0x%x", priv->wpa_version);
if (priv->wpa_version & IW_AUTH_WPA_VERSION_WPA2 ||
priv->wpa_version & IW_AUTH_WPA_VERSION_WPA)
tmp = cpu_to_le32(ndis_80211_priv_8021x_wep);
tmp = cpu_to_le32(ndis_80211_priv_accept_all);
return rndis_set_oid(usbdev, OID_802_11_PRIVACY_FILTER, &tmp,
sizeof(tmp));
}
static int set_encr_mode(struct usbnet *usbdev, int pairwise, int groupwise)
{
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
__le32 tmp;
int encr_mode, ret;
devdbg(usbdev, "set_encr_mode: cipher_pair=0x%x cipher_group=0x%x",
pairwise,
groupwise);
if (pairwise & IW_AUTH_CIPHER_CCMP)
encr_mode = ndis_80211_encr_ccmp_enabled;
else if (pairwise & IW_AUTH_CIPHER_TKIP)
encr_mode = ndis_80211_encr_tkip_enabled;
else if (pairwise &
(IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104))
encr_mode = ndis_80211_encr_wep_enabled;
else if (groupwise & IW_AUTH_CIPHER_CCMP)
encr_mode = ndis_80211_encr_ccmp_enabled;
else if (groupwise & IW_AUTH_CIPHER_TKIP)
encr_mode = ndis_80211_encr_tkip_enabled;
encr_mode = ndis_80211_encr_disabled;
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
tmp = cpu_to_le32(encr_mode);
ret = rndis_set_oid(usbdev, OID_802_11_ENCRYPTION_STATUS, &tmp,
sizeof(tmp));
if (ret != 0) {
devwarn(usbdev, "setting encr mode failed (%08X)", ret);
return ret;
}
priv->wpa_cipher_pair = pairwise;
priv->wpa_cipher_group = groupwise;
return 0;
}
static int set_assoc_params(struct usbnet *usbdev)
{
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
set_auth_mode(usbdev, priv->wpa_version, priv->wpa_authalg);
set_priv_filter(usbdev);
set_encr_mode(usbdev, priv->wpa_cipher_pair, priv->wpa_cipher_group);
return 0;
}
static int set_infra_mode(struct usbnet *usbdev, int mode)
{
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
__le32 tmp;
int ret, i;
devdbg(usbdev, "set_infra_mode: infra_mode=0x%x", priv->infra_mode);
tmp = cpu_to_le32(mode);
ret = rndis_set_oid(usbdev, OID_802_11_INFRASTRUCTURE_MODE, &tmp,
sizeof(tmp));
if (ret != 0) {
devwarn(usbdev, "setting infra mode failed (%08X)", ret);
return ret;
}
/* NDIS drivers clear keys when infrastructure mode is
* changed. But Linux tools assume otherwise. So set the
* keys */
if (priv->wpa_keymgmt == 0 ||
priv->wpa_keymgmt == IW_AUTH_KEY_MGMT_802_1X) {
for (i = 0; i < 4; i++) {
if (priv->encr_key_len[i] > 0)
add_wep_key(usbdev, priv->encr_keys[i],
priv->encr_key_len[i], i);
}
}
priv->infra_mode = mode;
return 0;
}
static void set_default_iw_params(struct usbnet *usbdev)
{
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
priv->wpa_keymgmt = 0;
priv->wpa_version = 0;
set_infra_mode(usbdev, ndis_80211_infra_infra);
set_auth_mode(usbdev, IW_AUTH_WPA_VERSION_DISABLED,
IW_AUTH_ALG_OPEN_SYSTEM);
set_priv_filter(usbdev);
set_encr_mode(usbdev, IW_AUTH_CIPHER_NONE, IW_AUTH_CIPHER_NONE);
}
static int deauthenticate(struct usbnet *usbdev)
{
int ret;
ret = disassociate(usbdev, 1);
set_default_iw_params(usbdev);
return ret;
}
/* index must be 0 - N, as per NDIS */
static int add_wep_key(struct usbnet *usbdev, char *key, int key_len, int index)
{
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
struct ndis_80211_wep_key ndis_key;
int ret;
if (key_len <= 0 || key_len > 32 || index < 0 || index >= 4)
return -EINVAL;
memset(&ndis_key, 0, sizeof(ndis_key));
ndis_key.size = cpu_to_le32(sizeof(ndis_key));
ndis_key.length = cpu_to_le32(key_len);
ndis_key.index = cpu_to_le32(index);
memcpy(&ndis_key.material, key, key_len);
if (index == priv->encr_tx_key_index) {
ndis_key.index |= cpu_to_le32(1 << 31);
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
ret = set_encr_mode(usbdev, IW_AUTH_CIPHER_WEP104,
IW_AUTH_CIPHER_NONE);
if (ret)
devwarn(usbdev, "encryption couldn't be enabled (%08X)",
ret);
}
ret = rndis_set_oid(usbdev, OID_802_11_ADD_WEP, &ndis_key,
sizeof(ndis_key));
if (ret != 0) {
devwarn(usbdev, "adding encryption key %d failed (%08X)",
index+1, ret);
return ret;
}
priv->encr_key_len[index] = key_len;
memcpy(&priv->encr_keys[index], key, key_len);
return 0;
}
/* remove_key is for both wep and wpa */
static int remove_key(struct usbnet *usbdev, int index, u8 bssid[ETH_ALEN])
{
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
struct ndis_80211_remove_key remove_key;
__le32 keyindex;
int ret;
if (priv->encr_key_len[index] == 0)
return 0;
priv->encr_key_len[index] = 0;
memset(&priv->encr_keys[index], 0, sizeof(priv->encr_keys[index]));
if (priv->wpa_cipher_pair == IW_AUTH_CIPHER_TKIP ||
priv->wpa_cipher_pair == IW_AUTH_CIPHER_CCMP ||
priv->wpa_cipher_group == IW_AUTH_CIPHER_TKIP ||
priv->wpa_cipher_group == IW_AUTH_CIPHER_CCMP) {
remove_key.size = cpu_to_le32(sizeof(remove_key));
remove_key.index = cpu_to_le32(index);
if (bssid) {
/* pairwise key */
if (memcmp(bssid, ffff_bssid, ETH_ALEN) != 0)
remove_key.index |= cpu_to_le32(1 << 30);
memcpy(remove_key.bssid, bssid,
sizeof(remove_key.bssid));
memset(remove_key.bssid, 0xff,
sizeof(remove_key.bssid));
ret = rndis_set_oid(usbdev, OID_802_11_REMOVE_KEY, &remove_key,
sizeof(remove_key));
if (ret != 0)
return ret;
} else {
keyindex = cpu_to_le32(index);
ret = rndis_set_oid(usbdev, OID_802_11_REMOVE_WEP, &keyindex,
sizeof(keyindex));
if (ret != 0) {
devwarn(usbdev,
"removing encryption key %d failed (%08X)",
index, ret);
return ret;