Newer
Older
/*
* Driver for RNDIS based wireless USB devices.
*
* Copyright (C) 2007 by Bjorge Dijkstra <bjd@jooz.net>
* Copyright (C) 2008-2009 by Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* 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>
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
#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;
module_param_named(workaround_interval, modparam_workaround_interval,
int, 0444);
MODULE_PARM_DESC(workaround_interval,
"set stall workaround interval in msecs (0=disabled) (default: 0)");
/* various RNDIS OID defs */
#define OID_GEN_LINK_SPEED cpu_to_le32(0x00010107)
#define OID_GEN_RNDIS_CONFIG_PARAMETER cpu_to_le32(0x0001021b)
#define OID_GEN_XMIT_OK cpu_to_le32(0x00020101)
#define OID_GEN_RCV_OK cpu_to_le32(0x00020102)
#define OID_GEN_XMIT_ERROR cpu_to_le32(0x00020103)
#define OID_GEN_RCV_ERROR cpu_to_le32(0x00020104)
#define OID_GEN_RCV_NO_BUFFER cpu_to_le32(0x00020105)
#define OID_802_3_CURRENT_ADDRESS cpu_to_le32(0x01010102)
#define OID_802_3_MULTICAST_LIST cpu_to_le32(0x01010103)
#define OID_802_3_MAXIMUM_LIST_SIZE cpu_to_le32(0x01010104)
#define OID_802_11_BSSID cpu_to_le32(0x0d010101)
#define OID_802_11_SSID cpu_to_le32(0x0d010102)
#define OID_802_11_INFRASTRUCTURE_MODE cpu_to_le32(0x0d010108)
#define OID_802_11_ADD_WEP cpu_to_le32(0x0d010113)
#define OID_802_11_REMOVE_WEP cpu_to_le32(0x0d010114)
#define OID_802_11_DISASSOCIATE cpu_to_le32(0x0d010115)
#define OID_802_11_AUTHENTICATION_MODE cpu_to_le32(0x0d010118)
#define OID_802_11_PRIVACY_FILTER cpu_to_le32(0x0d010119)
#define OID_802_11_BSSID_LIST_SCAN cpu_to_le32(0x0d01011a)
#define OID_802_11_ENCRYPTION_STATUS cpu_to_le32(0x0d01011b)
#define OID_802_11_ADD_KEY cpu_to_le32(0x0d01011d)
#define OID_802_11_REMOVE_KEY cpu_to_le32(0x0d01011e)
#define OID_802_11_ASSOCIATION_INFORMATION cpu_to_le32(0x0d01011f)
#define OID_802_11_CAPABILITY cpu_to_le32(0x0d010122)
#define OID_802_11_PMKID cpu_to_le32(0x0d010123)
#define OID_802_11_NETWORK_TYPES_SUPPORTED cpu_to_le32(0x0d010203)
#define OID_802_11_NETWORK_TYPE_IN_USE cpu_to_le32(0x0d010204)
#define OID_802_11_TX_POWER_LEVEL cpu_to_le32(0x0d010205)
#define OID_802_11_RSSI cpu_to_le32(0x0d010206)
#define OID_802_11_RSSI_TRIGGER cpu_to_le32(0x0d010207)
#define OID_802_11_FRAGMENTATION_THRESHOLD cpu_to_le32(0x0d010209)
#define OID_802_11_RTS_THRESHOLD cpu_to_le32(0x0d01020a)
#define OID_802_11_SUPPORTED_RATES cpu_to_le32(0x0d01020e)
#define OID_802_11_CONFIGURATION cpu_to_le32(0x0d010211)
#define OID_802_11_BSSID_LIST cpu_to_le32(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.
* 100% : 20 mW ~ 13dBm
* 75% : 15 mW ~ 12dBm
* 50% : 10 mW ~ 10dBm
* 25% : 5 mW ~ 7dBm
#define BCM4320_DEFAULT_TXPOWER_DBM_100 13
#define BCM4320_DEFAULT_TXPOWER_DBM_75 12
#define BCM4320_DEFAULT_TXPOWER_DBM_50 10
#define BCM4320_DEFAULT_TXPOWER_DBM_25 7
/* codes for "status" field of completion messages */
#define RNDIS_STATUS_ADAPTER_NOT_READY cpu_to_le32(0xc0010011)
#define RNDIS_STATUS_ADAPTER_NOT_OPEN cpu_to_le32(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
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
enum ndis_80211_status_type {
NDIS_80211_STATUSTYPE_AUTHENTICATION,
NDIS_80211_STATUSTYPE_MEDIASTREAMMODE,
NDIS_80211_STATUSTYPE_PMKID_CANDIDATELIST,
NDIS_80211_STATUSTYPE_RADIOSTATE,
};
enum ndis_80211_media_stream_mode {
NDIS_80211_MEDIA_STREAM_OFF,
NDIS_80211_MEDIA_STREAM_ON
};
enum ndis_80211_radio_status {
NDIS_80211_RADIO_STATUS_ON,
NDIS_80211_RADIO_STATUS_HARDWARE_OFF,
NDIS_80211_RADIO_STATUS_SOFTWARE_OFF,
};
enum ndis_80211_addkey_bits {
NDIS_80211_ADDKEY_8021X_AUTH = cpu_to_le32(1 << 28),
NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ = cpu_to_le32(1 << 29),
NDIS_80211_ADDKEY_PAIRWISE_KEY = cpu_to_le32(1 << 30),
NDIS_80211_ADDKEY_TRANSMIT_KEY = cpu_to_le32(1 << 31)
};
enum ndis_80211_addwep_bits {
NDIS_80211_ADDWEP_PERCLIENT_KEY = cpu_to_le32(1 << 30),
NDIS_80211_ADDWEP_TRANSMIT_KEY = cpu_to_le32(1 << 31)
struct ndis_80211_auth_request {
__le32 length;
u8 bssid[6];
u8 padding[2];
__le32 flags;
} __attribute__((packed));
struct ndis_80211_pmkid_candidate {
u8 bssid[6];
u8 padding[2];
__le32 flags;
} __attribute__((packed));
struct ndis_80211_pmkid_cand_list {
__le32 version;
__le32 num_candidates;
struct ndis_80211_pmkid_candidate candidate_list[0];
} __attribute__((packed));
struct ndis_80211_status_indication {
__le32 status_type;
union {
__le32 media_stream_mode;
__le32 radio_status;
struct ndis_80211_auth_request auth_request[0];
struct ndis_80211_pmkid_cand_list cand_list;
} u;
} __attribute__((packed));
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];
u8 padding[2];
} __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));
struct ndis_80211_auth_encr_pair {
__le32 auth_mode;
__le32 encr_mode;
} __attribute__((packed));
struct ndis_80211_capability {
__le32 length;
__le32 version;
__le32 num_pmkids;
__le32 num_auth_encr_pair;
struct ndis_80211_auth_encr_pair auth_encr_pair[0];
} __attribute__((packed));
struct ndis_80211_bssid_info {
u8 bssid[6];
u8 pmkid[16];
};
struct ndis_80211_pmkid {
__le32 length;
__le32 bssid_info_count;
struct ndis_80211_bssid_info bssid_info[0];
};
/*
* 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 WORK_LINK_UP (1<<0)
#define WORK_LINK_DOWN (1<<1)
#define WORK_SET_MULTICAST_LIST (1<<2)
#define RNDIS_WLAN_ALG_NONE 0
#define RNDIS_WLAN_ALG_WEP (1<<0)
#define RNDIS_WLAN_ALG_TKIP (1<<1)
#define RNDIS_WLAN_ALG_CCMP (1<<2)
#define RNDIS_WLAN_KEY_MGMT_NONE 0
#define RNDIS_WLAN_KEY_MGMT_802_1X (1<<0)
#define RNDIS_WLAN_KEY_MGMT_PSK (1<<1)
#define COMMAND_BUFFER_SIZE (CONTROL_BUFFER_SIZE + sizeof(struct rndis_set))
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
static const struct ieee80211_channel rndis_channels[] = {
{ .center_freq = 2412 },
{ .center_freq = 2417 },
{ .center_freq = 2422 },
{ .center_freq = 2427 },
{ .center_freq = 2432 },
{ .center_freq = 2437 },
{ .center_freq = 2442 },
{ .center_freq = 2447 },
{ .center_freq = 2452 },
{ .center_freq = 2457 },
{ .center_freq = 2462 },
{ .center_freq = 2467 },
{ .center_freq = 2472 },
{ .center_freq = 2484 },
};
static const struct ieee80211_rate rndis_rates[] = {
{ .bitrate = 10 },
{ .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
{ .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
{ .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
{ .bitrate = 60 },
{ .bitrate = 90 },
{ .bitrate = 120 },
{ .bitrate = 180 },
{ .bitrate = 240 },
{ .bitrate = 360 },
{ .bitrate = 480 },
{ .bitrate = 540 }
};
static const u32 rndis_cipher_suites[] = {
WLAN_CIPHER_SUITE_WEP40,
WLAN_CIPHER_SUITE_WEP104,
WLAN_CIPHER_SUITE_TKIP,
WLAN_CIPHER_SUITE_CCMP,
};
struct rndis_wlan_encr_key {
int len;
u8 material[32];
u8 bssid[ETH_ALEN];
bool pairwise;
bool tx_key;
};
/* RNDIS device private data */
struct rndis_wlan_private {
struct usbnet *usbdev;
struct wireless_dev wdev;
struct cfg80211_scan_request *scan_request;
struct workqueue_struct *workqueue;
struct delayed_work dev_poller_work;
struct work_struct work;
struct mutex command_lock;
unsigned long work_pending;
struct ieee80211_supported_band band;
struct ieee80211_channel channels[ARRAY_SIZE(rndis_channels)];
struct ieee80211_rate rates[ARRAY_SIZE(rndis_rates)];
u32 cipher_suites[ARRAY_SIZE(rndis_cipher_suites)];
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 infra_mode;
bool connected;
struct ndis_80211_ssid essid;
__le32 current_command_oid;
/* encryption stuff */
int encr_tx_key_index;
struct rndis_wlan_encr_key encr_keys[4];
int wpa_version;
u8 command_buffer[COMMAND_BUFFER_SIZE];
/*
* cfg80211 ops
*/
static int rndis_change_virtual_intf(struct wiphy *wiphy,
struct net_device *dev,
enum nl80211_iftype type, u32 *flags,
struct vif_params *params);
static int rndis_scan(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_scan_request *request);
static int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed);
static int rndis_set_tx_power(struct wiphy *wiphy, enum tx_power_setting type,
int dbm);
static int rndis_get_tx_power(struct wiphy *wiphy, int *dbm);
static int rndis_connect(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_connect_params *sme);
static int rndis_disconnect(struct wiphy *wiphy, struct net_device *dev,
u16 reason_code);
static int rndis_join_ibss(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_ibss_params *params);
static int rndis_leave_ibss(struct wiphy *wiphy, struct net_device *dev);
static int rndis_set_channel(struct wiphy *wiphy,
struct ieee80211_channel *chan, enum nl80211_channel_type channel_type);
static int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev,
u8 key_index, const u8 *mac_addr,
struct key_params *params);
static int rndis_del_key(struct wiphy *wiphy, struct net_device *netdev,
u8 key_index, const u8 *mac_addr);
static int rndis_set_default_key(struct wiphy *wiphy, struct net_device *netdev,
u8 key_index);
static int rndis_get_station(struct wiphy *wiphy, struct net_device *dev,
u8 *mac, struct station_info *sinfo);
static int rndis_dump_station(struct wiphy *wiphy, struct net_device *dev,
int idx, u8 *mac, struct station_info *sinfo);
static int rndis_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
struct cfg80211_pmksa *pmksa);
static int rndis_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
struct cfg80211_pmksa *pmksa);
static int rndis_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev);
static struct cfg80211_ops rndis_config_ops = {
.change_virtual_intf = rndis_change_virtual_intf,
.set_wiphy_params = rndis_set_wiphy_params,
.set_tx_power = rndis_set_tx_power,
.get_tx_power = rndis_get_tx_power,
.connect = rndis_connect,
.disconnect = rndis_disconnect,
.join_ibss = rndis_join_ibss,
.leave_ibss = rndis_leave_ibss,
.set_channel = rndis_set_channel,
.add_key = rndis_add_key,
.del_key = rndis_del_key,
.set_default_key = rndis_set_default_key,
.get_station = rndis_get_station,
.dump_station = rndis_dump_station,
.set_pmksa = rndis_set_pmksa,
.del_pmksa = rndis_del_pmksa,
.flush_pmksa = rndis_flush_pmksa,
static void *rndis_wiphy_privid = &rndis_wiphy_privid;
static struct rndis_wlan_private *get_rndis_wlan_priv(struct usbnet *dev)
return (struct rndis_wlan_private *)dev->driver_priv;
static u32 get_bcm4320_power_dbm(struct rndis_wlan_private *priv)
switch (priv->param_power_output) {
default:
case 3:
return BCM4320_DEFAULT_TXPOWER_DBM_100;
case 2:
return BCM4320_DEFAULT_TXPOWER_DBM_75;
case 1:
return BCM4320_DEFAULT_TXPOWER_DBM_50;
case 0:
return BCM4320_DEFAULT_TXPOWER_DBM_25;
}
static bool is_wpa_key(struct rndis_wlan_private *priv, int idx)
{
int cipher = priv->encr_keys[idx].cipher;
return (cipher == WLAN_CIPHER_SUITE_CCMP ||
cipher == WLAN_CIPHER_SUITE_TKIP);
}
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
static int rndis_cipher_to_alg(u32 cipher)
{
switch (cipher) {
default:
return RNDIS_WLAN_ALG_NONE;
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
return RNDIS_WLAN_ALG_WEP;
case WLAN_CIPHER_SUITE_TKIP:
return RNDIS_WLAN_ALG_TKIP;
case WLAN_CIPHER_SUITE_CCMP:
return RNDIS_WLAN_ALG_CCMP;
}
}
static int rndis_akm_suite_to_key_mgmt(u32 akm_suite)
{
switch (akm_suite) {
default:
return RNDIS_WLAN_KEY_MGMT_NONE;
case WLAN_AKM_SUITE_8021X:
return RNDIS_WLAN_KEY_MGMT_802_1X;
case WLAN_AKM_SUITE_PSK:
return RNDIS_WLAN_KEY_MGMT_PSK;
}
}
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
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
698
699
700
701
702
703
#ifdef DEBUG
static const char *oid_to_string(__le32 oid)
{
switch (oid) {
#define OID_STR(oid) case oid: return(#oid)
/* from rndis_host.h */
OID_STR(OID_802_3_PERMANENT_ADDRESS);
OID_STR(OID_GEN_MAXIMUM_FRAME_SIZE);
OID_STR(OID_GEN_CURRENT_PACKET_FILTER);
OID_STR(OID_GEN_PHYSICAL_MEDIUM);
/* from rndis_wlan.c */
OID_STR(OID_GEN_LINK_SPEED);
OID_STR(OID_GEN_RNDIS_CONFIG_PARAMETER);
OID_STR(OID_GEN_XMIT_OK);
OID_STR(OID_GEN_RCV_OK);
OID_STR(OID_GEN_XMIT_ERROR);
OID_STR(OID_GEN_RCV_ERROR);
OID_STR(OID_GEN_RCV_NO_BUFFER);
OID_STR(OID_802_3_CURRENT_ADDRESS);
OID_STR(OID_802_3_MULTICAST_LIST);
OID_STR(OID_802_3_MAXIMUM_LIST_SIZE);
OID_STR(OID_802_11_BSSID);
OID_STR(OID_802_11_SSID);
OID_STR(OID_802_11_INFRASTRUCTURE_MODE);
OID_STR(OID_802_11_ADD_WEP);
OID_STR(OID_802_11_REMOVE_WEP);
OID_STR(OID_802_11_DISASSOCIATE);
OID_STR(OID_802_11_AUTHENTICATION_MODE);
OID_STR(OID_802_11_PRIVACY_FILTER);
OID_STR(OID_802_11_BSSID_LIST_SCAN);
OID_STR(OID_802_11_ENCRYPTION_STATUS);
OID_STR(OID_802_11_ADD_KEY);
OID_STR(OID_802_11_REMOVE_KEY);
OID_STR(OID_802_11_ASSOCIATION_INFORMATION);
OID_STR(OID_802_11_PMKID);
OID_STR(OID_802_11_NETWORK_TYPES_SUPPORTED);
OID_STR(OID_802_11_NETWORK_TYPE_IN_USE);
OID_STR(OID_802_11_TX_POWER_LEVEL);
OID_STR(OID_802_11_RSSI);
OID_STR(OID_802_11_RSSI_TRIGGER);
OID_STR(OID_802_11_FRAGMENTATION_THRESHOLD);
OID_STR(OID_802_11_RTS_THRESHOLD);
OID_STR(OID_802_11_SUPPORTED_RATES);
OID_STR(OID_802_11_CONFIGURATION);
OID_STR(OID_802_11_BSSID_LIST);
#undef OID_STR
}
return "?";
}
#else
static const char *oid_to_string(__le32 oid)
{
return "?";
}
#endif
/* 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_wlan_private *priv = get_rndis_wlan_priv(dev);
union {
void *buf;
struct rndis_msg_hdr *header;
struct rndis_query *get;
struct rndis_query_c *get_c;
} u;
int ret, buflen;
int resplen, respoffs, copylen;
buflen = *len + sizeof(*u.get);
if (buflen < CONTROL_BUFFER_SIZE)
buflen = CONTROL_BUFFER_SIZE;
if (buflen > COMMAND_BUFFER_SIZE) {
u.buf = kmalloc(buflen, GFP_KERNEL);
if (!u.buf)
return -ENOMEM;
} else {
u.buf = priv->command_buffer;
}
mutex_lock(&priv->command_lock);
memset(u.get, 0, sizeof *u.get);
u.get->msg_type = RNDIS_MSG_QUERY;
u.get->msg_len = cpu_to_le32(sizeof *u.get);
u.get->oid = oid;
priv->current_command_oid = oid;
ret = rndis_command(dev, u.header, buflen);
priv->current_command_oid = 0;
netdev_dbg(dev->net, "%s(%s): rndis_command() failed, %d (%08x)\n",
__func__, oid_to_string(oid), ret,
le32_to_cpu(u.get_c->status));
if (ret == 0) {
resplen = le32_to_cpu(u.get_c->len);
respoffs = le32_to_cpu(u.get_c->offset) + 8;
if (respoffs > buflen) {
/* Device returned data offset outside buffer, error. */
netdev_dbg(dev->net, "%s(%s): received invalid "
"data offset: %d > %d\n", __func__,
oid_to_string(oid), respoffs, buflen);
ret = -EINVAL;
goto exit_unlock;
}
if ((resplen + respoffs) > buflen) {
/* Device would have returned more data if buffer would
* have been big enough. Copy just the bits that we got.
*/
copylen = buflen - respoffs;
} else {
copylen = resplen;
}
if (copylen > *len)
copylen = *len;
memcpy(data, u.buf + respoffs, copylen);
*len = resplen;
ret = rndis_error_status(u.get_c->status);
netdev_dbg(dev->net, "%s(%s): device returned error, 0x%08x (%d)\n",
__func__, oid_to_string(oid),
le32_to_cpu(u.get_c->status), ret);
exit_unlock:
mutex_unlock(&priv->command_lock);
if (u.buf != priv->command_buffer)
kfree(u.buf);
return ret;
}
static int rndis_set_oid(struct usbnet *dev, __le32 oid, void *data, int len)
{
struct rndis_wlan_private *priv = get_rndis_wlan_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;
if (buflen > COMMAND_BUFFER_SIZE) {
u.buf = kmalloc(buflen, GFP_KERNEL);
if (!u.buf)
return -ENOMEM;
} else {
u.buf = priv->command_buffer;
}
mutex_lock(&priv->command_lock);
memset(u.set, 0, sizeof *u.set);
u.set->msg_type = RNDIS_MSG_SET;
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 = cpu_to_le32(sizeof(*u.set) - 8);
u.set->handle = cpu_to_le32(0);
memcpy(u.buf + sizeof(*u.set), data, len);
priv->current_command_oid = oid;
ret = rndis_command(dev, u.header, buflen);
priv->current_command_oid = 0;
netdev_dbg(dev->net, "%s(%s): rndis_command() failed, %d (%08x)\n",
__func__, oid_to_string(oid), ret,
le32_to_cpu(u.set_c->status));
ret = rndis_error_status(u.set_c->status);
netdev_dbg(dev->net, "%s(%s): device returned error, 0x%08x (%d)\n",
__func__, oid_to_string(oid),
le32_to_cpu(u.set_c->status), ret);
mutex_unlock(&priv->command_lock);
if (u.buf != priv->command_buffer)
kfree(u.buf);
return ret;
}
static int rndis_reset(struct usbnet *usbdev)
{
struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
struct rndis_reset *reset;
int ret;
mutex_lock(&priv->command_lock);
reset = (void *)priv->command_buffer;
memset(reset, 0, sizeof(*reset));
reset->msg_type = RNDIS_MSG_RESET;
reset->msg_len = cpu_to_le32(sizeof(*reset));
priv->current_command_oid = 0;
ret = rndis_command(usbdev, (void *)reset, CONTROL_BUFFER_SIZE);
mutex_unlock(&priv->command_lock);
if (ret < 0)
return ret;
return 0;
}
/*
* 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;
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
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)
netdev_dbg(dev->net, "setting config parameter: %s, value: %s\n",
param, (u8 *)value);
netdev_dbg(dev->net, "setting config parameter: %s, value: %d\n",
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);
/* 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
netdev_dbg(dev->net, "info buffer (len: %d)\n", info_len);
for (i = 0; i < info_len; i += 12) {
u32 *tmp = (u32 *)((u8 *)infobuf + i);
netdev_dbg(dev->net, "%08X:%08X:%08X\n",
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)
netdev_dbg(dev->net, "setting rndis config parameter failed, %d\n",
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);
}
/*
* 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;
}
/*
* common functions
*/
static int set_infra_mode(struct usbnet *usbdev, int mode);
static void restore_keys(struct usbnet *usbdev);
static int rndis_check_bssid_list(struct usbnet *usbdev);
static int set_essid(struct usbnet *usbdev, struct ndis_80211_ssid *ssid)
struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);