2008-01-26 01:51:51 +03:00
/*
* Driver for RNDIS based wireless USB devices .
*
* Copyright ( C ) 2007 by Bjorge Dijkstra < bjd @ jooz . net >
2009-05-22 18:40:12 +04:00
* Copyright ( C ) 2008 - 2009 by Jussi Kivilinna < jussi . kivilinna @ mbnet . fi >
2008-01-26 01:51:51 +03:00
*
* 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>
2008-10-31 00:09:54 +03:00
# include <linux/ieee80211.h>
2008-01-26 01:51:51 +03:00
# include <linux/if_arp.h>
# include <linux/ctype.h>
# include <linux/spinlock.h>
# include <net/iw_handler.h>
2009-03-27 00:39:53 +03:00
# include <net/cfg80211.h>
2008-01-26 01:51:51 +03:00
# 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) " ) ;
2009-11-22 21:16:47 +03:00
static int modparam_workaround_interval ;
2008-01-26 01:51:51 +03:00
module_param_named ( workaround_interval , modparam_workaround_interval ,
int , 0444 ) ;
MODULE_PARM_DESC ( workaround_interval ,
2009-11-22 21:16:47 +03:00
" set stall workaround interval in msecs (0=disabled) (default: 0) " ) ;
2008-01-26 01:51:51 +03:00
/* various RNDIS OID defs */
2009-02-15 09:56:56 +03:00
# 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_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)
2008-01-26 01:51:51 +03:00
/* 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 13 dBm .
* This value is from Linksys WUSB54GSC User Guide , Appendix F : Specifications .
2009-06-16 18:17:32 +04:00
* 100 % : 20 mW ~ 13 dBm
* 75 % : 15 mW ~ 12 dBm
* 50 % : 10 mW ~ 10 dBm
* 25 % : 5 mW ~ 7 dBm
2008-01-26 01:51:51 +03:00
*/
2009-06-16 18:17:32 +04:00
# 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
2008-01-26 01:51:51 +03:00
/* codes for "status" field of completion messages */
2009-02-15 09:56:56 +03:00
# define RNDIS_STATUS_ADAPTER_NOT_READY cpu_to_le32(0xc0010011)
# define RNDIS_STATUS_ADAPTER_NOT_OPEN cpu_to_le32(0xc0010012)
2008-01-26 01:51:51 +03:00
/* 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
2008-03-08 02:23:17 +03:00
enum ndis_80211_net_type {
2009-06-04 21:13:19 +04:00
NDIS_80211_TYPE_FREQ_HOP ,
NDIS_80211_TYPE_DIRECT_SEQ ,
NDIS_80211_TYPE_OFDM_A ,
NDIS_80211_TYPE_OFDM_G
2008-03-08 02:23:17 +03:00
} ;
enum ndis_80211_net_infra {
2009-06-04 21:13:19 +04:00
NDIS_80211_INFRA_ADHOC ,
NDIS_80211_INFRA_INFRA ,
NDIS_80211_INFRA_AUTO_UNKNOWN
2008-03-08 02:23:17 +03:00
} ;
enum ndis_80211_auth_mode {
2009-06-04 21:13:19 +04:00
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
2008-03-08 02:23:17 +03:00
} ;
enum ndis_80211_encr_status {
2009-06-04 21:13:19 +04:00
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
2008-03-08 02:23:17 +03:00
} ;
enum ndis_80211_priv_filter {
2009-06-04 21:13:19 +04:00
NDIS_80211_PRIV_ACCEPT_ALL ,
NDIS_80211_PRIV_8021X_WEP
2008-03-08 02:23:17 +03:00
} ;
2009-07-30 20:41:58 +04:00
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 ,
} ;
2009-05-22 18:40:20 +04:00
enum ndis_80211_addkey_bits {
2009-06-04 21:13:19 +04:00
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 )
2009-05-22 18:40:20 +04:00
} ;
enum ndis_80211_addwep_bits {
2009-06-04 21:13:19 +04:00
NDIS_80211_ADDWEP_PERCLIENT_KEY = cpu_to_le32 ( 1 < < 30 ) ,
NDIS_80211_ADDWEP_TRANSMIT_KEY = cpu_to_le32 ( 1 < < 31 )
2009-05-22 18:40:20 +04:00
} ;
2009-07-30 20:41:58 +04:00
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 {
2009-09-01 16:33:06 +04:00
__le32 media_stream_mode ;
__le32 radio_status ;
2009-07-30 20:41:58 +04:00
struct ndis_80211_auth_request auth_request [ 0 ] ;
struct ndis_80211_pmkid_cand_list cand_list ;
} u ;
} __attribute__ ( ( packed ) ) ;
2008-03-08 02:23:17 +03:00
struct ndis_80211_ssid {
2008-03-08 02:23:25 +03:00
__le32 length ;
u8 essid [ NDIS_802_11_LENGTH_SSID ] ;
2008-01-26 01:51:51 +03:00
} __attribute__ ( ( packed ) ) ;
2008-03-08 02:23:17 +03:00
struct ndis_80211_conf_freq_hop {
2008-03-08 02:23:25 +03:00
__le32 length ;
__le32 hop_pattern ;
__le32 hop_set ;
__le32 dwell_time ;
2008-01-26 01:51:51 +03:00
} __attribute__ ( ( packed ) ) ;
2008-03-08 02:23:17 +03:00
struct ndis_80211_conf {
2008-03-08 02:23:25 +03:00
__le32 length ;
__le32 beacon_period ;
__le32 atim_window ;
__le32 ds_config ;
struct ndis_80211_conf_freq_hop fh_config ;
2008-01-26 01:51:51 +03:00
} __attribute__ ( ( packed ) ) ;
2008-03-08 02:23:17 +03:00
struct ndis_80211_bssid_ex {
2008-03-08 02:23:25 +03:00
__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 ] ;
2008-01-26 01:51:51 +03:00
} __attribute__ ( ( packed ) ) ;
2008-03-08 02:23:17 +03:00
struct ndis_80211_bssid_list_ex {
2008-03-08 02:23:25 +03:00
__le32 num_items ;
struct ndis_80211_bssid_ex bssid [ 0 ] ;
2008-01-26 01:51:51 +03:00
} __attribute__ ( ( packed ) ) ;
2008-03-08 02:23:17 +03:00
struct ndis_80211_fixed_ies {
2008-03-08 02:23:25 +03:00
u8 timestamp [ 8 ] ;
__le16 beacon_interval ;
__le16 capabilities ;
2008-01-26 01:51:51 +03:00
} __attribute__ ( ( packed ) ) ;
2008-03-08 02:23:17 +03:00
struct ndis_80211_wep_key {
2008-03-08 02:23:25 +03:00
__le32 size ;
__le32 index ;
__le32 length ;
u8 material [ 32 ] ;
2008-01-26 01:51:51 +03:00
} __attribute__ ( ( packed ) ) ;
2008-03-08 02:23:17 +03:00
struct ndis_80211_key {
2008-03-08 02:23:25 +03:00
__le32 size ;
__le32 index ;
__le32 length ;
u8 bssid [ 6 ] ;
u8 padding [ 6 ] ;
u8 rsc [ 8 ] ;
u8 material [ 32 ] ;
2008-01-26 01:51:51 +03:00
} __attribute__ ( ( packed ) ) ;
2008-03-08 02:23:17 +03:00
struct ndis_80211_remove_key {
2008-03-08 02:23:25 +03:00
__le32 size ;
__le32 index ;
u8 bssid [ 6 ] ;
2009-07-30 20:42:03 +04:00
u8 padding [ 2 ] ;
2008-01-26 01:51:51 +03:00
} __attribute__ ( ( packed ) ) ;
2008-03-08 02:23:17 +03:00
struct ndis_config_param {
2008-03-08 02:23:25 +03:00
__le32 name_offs ;
__le32 name_length ;
__le32 type ;
__le32 value_offs ;
__le32 value_length ;
2008-01-26 01:51:51 +03:00
} __attribute__ ( ( packed ) ) ;
2008-05-27 01:06:15 +04:00
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 ) ) ;
2008-01-26 01:51:51 +03:00
/*
* 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
2008-06-02 19:35:21 +04:00
# define WORK_LINK_UP (1<<0)
# define WORK_LINK_DOWN (1<<1)
# define WORK_SET_MULTICAST_LIST (1<<2)
2008-01-26 01:51:51 +03:00
2009-08-28 14:27:47 +04:00
# 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)
2008-06-12 21:19:19 +04:00
# define COMMAND_BUFFER_SIZE (CONTROL_BUFFER_SIZE + sizeof(struct rndis_set))
2009-03-27 00:39:53 +03:00
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 }
} ;
2009-08-26 16:53:02 +04:00
static const u32 rndis_cipher_suites [ ] = {
WLAN_CIPHER_SUITE_WEP40 ,
WLAN_CIPHER_SUITE_WEP104 ,
WLAN_CIPHER_SUITE_TKIP ,
WLAN_CIPHER_SUITE_CCMP ,
} ;
2009-07-30 20:42:08 +04:00
struct rndis_wlan_encr_key {
int len ;
2009-08-28 14:27:58 +04:00
u32 cipher ;
2009-07-30 20:42:08 +04:00
u8 material [ 32 ] ;
u8 bssid [ ETH_ALEN ] ;
bool pairwise ;
bool tx_key ;
} ;
2008-01-26 01:51:51 +03:00
/* RNDIS device private data */
2009-06-04 21:13:25 +04:00
struct rndis_wlan_private {
2008-01-26 01:51:51 +03:00
struct usbnet * usbdev ;
2009-03-27 00:39:53 +03:00
struct wireless_dev wdev ;
2009-03-27 00:40:31 +03:00
struct cfg80211_scan_request * scan_request ;
2008-01-26 01:51:51 +03:00
struct workqueue_struct * workqueue ;
2009-08-28 14:28:14 +04:00
struct delayed_work dev_poller_work ;
2009-03-27 00:40:31 +03:00
struct delayed_work scan_work ;
2008-01-26 01:51:51 +03:00
struct work_struct work ;
struct mutex command_lock ;
unsigned long work_pending ;
2009-08-28 14:28:03 +04:00
int last_qual ;
2008-01-26 01:51:51 +03:00
2009-03-27 00:39:53 +03:00
struct ieee80211_supported_band band ;
struct ieee80211_channel channels [ ARRAY_SIZE ( rndis_channels ) ] ;
struct ieee80211_rate rates [ ARRAY_SIZE ( rndis_rates ) ] ;
2009-08-26 16:53:02 +04:00
u32 cipher_suites [ ARRAY_SIZE ( rndis_cipher_suites ) ] ;
2009-03-27 00:39:53 +03:00
2008-01-26 01:51:51 +03:00
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 */
2009-09-01 16:32:55 +04:00
bool radio_on ;
2008-01-26 01:51:51 +03:00
int infra_mode ;
2009-08-28 14:27:47 +04:00
bool connected ;
2009-08-28 14:28:03 +04:00
u8 bssid [ ETH_ALEN ] ;
2008-03-08 02:23:17 +03:00
struct ndis_80211_ssid essid ;
2009-08-28 13:58:49 +04:00
__le32 current_command_oid ;
2008-01-26 01:51:51 +03:00
/* encryption stuff */
int encr_tx_key_index ;
2009-07-30 20:42:08 +04:00
struct rndis_wlan_encr_key encr_keys [ 4 ] ;
2008-01-26 01:51:51 +03:00
int wpa_version ;
2008-06-12 21:19:19 +04:00
u8 command_buffer [ COMMAND_BUFFER_SIZE ] ;
2008-01-26 01:51:51 +03:00
} ;
2009-03-27 00:40:01 +03:00
/*
* cfg80211 ops
*/
2009-06-09 23:04:43 +04:00
static int rndis_change_virtual_intf ( struct wiphy * wiphy ,
struct net_device * dev ,
2009-03-27 00:40:01 +03:00
enum nl80211_iftype type , u32 * flags ,
struct vif_params * params ) ;
2009-03-27 00:40:31 +03:00
static int rndis_scan ( struct wiphy * wiphy , struct net_device * dev ,
struct cfg80211_scan_request * request ) ;
2009-06-16 18:17:21 +04:00
static int rndis_set_wiphy_params ( struct wiphy * wiphy , u32 changed ) ;
2009-06-16 18:17:32 +04:00
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 ) ;
2009-08-28 14:27:47 +04:00
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 ) ;
2009-08-28 14:27:53 +04:00
static int rndis_set_channel ( struct wiphy * wiphy ,
struct ieee80211_channel * chan , enum nl80211_channel_type channel_type ) ;
2009-08-28 14:27:58 +04:00
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 ) ;
2009-08-28 14:28:03 +04:00
static int rndis_get_station ( struct wiphy * wiphy , struct net_device * dev ,
u8 * mac , struct station_info * sinfo ) ;
2009-08-28 14:28:09 +04:00
static int rndis_dump_station ( struct wiphy * wiphy , struct net_device * dev ,
int idx , u8 * mac , struct station_info * sinfo ) ;
2009-04-21 12:55:10 +04:00
static struct cfg80211_ops rndis_config_ops = {
2009-03-27 00:40:01 +03:00
. change_virtual_intf = rndis_change_virtual_intf ,
2009-03-27 00:40:31 +03:00
. scan = rndis_scan ,
2009-06-16 18:17:21 +04:00
. set_wiphy_params = rndis_set_wiphy_params ,
2009-06-16 18:17:32 +04:00
. set_tx_power = rndis_set_tx_power ,
. get_tx_power = rndis_get_tx_power ,
2009-08-28 14:27:47 +04:00
. connect = rndis_connect ,
. disconnect = rndis_disconnect ,
. join_ibss = rndis_join_ibss ,
. leave_ibss = rndis_leave_ibss ,
2009-08-28 14:27:53 +04:00
. set_channel = rndis_set_channel ,
2009-08-28 14:27:58 +04:00
. add_key = rndis_add_key ,
. del_key = rndis_del_key ,
. set_default_key = rndis_set_default_key ,
2009-08-28 14:28:03 +04:00
. get_station = rndis_get_station ,
2009-08-28 14:28:09 +04:00
. dump_station = rndis_dump_station ,
2009-03-27 00:40:01 +03:00
} ;
2008-01-26 01:51:51 +03:00
2009-04-21 12:55:10 +04:00
static void * rndis_wiphy_privid = & rndis_wiphy_privid ;
2008-01-26 01:51:51 +03:00
2009-06-04 21:13:25 +04:00
static struct rndis_wlan_private * get_rndis_wlan_priv ( struct usbnet * dev )
2008-01-26 01:51:51 +03:00
{
2009-06-04 21:13:25 +04:00
return ( struct rndis_wlan_private * ) dev - > driver_priv ;
2008-01-26 01:51:51 +03:00
}
2009-06-16 18:17:32 +04:00
static u32 get_bcm4320_power_dbm ( struct rndis_wlan_private * priv )
2008-01-26 01:51:51 +03:00
{
2009-06-16 18:17:32 +04:00
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 ;
}
2008-01-26 01:51:51 +03:00
}
2009-07-30 20:42:08 +04:00
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 ) ;
}
2009-08-28 14:27:47 +04:00
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 ;
}
}
2009-07-30 20:41:47 +04:00
# 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
2008-01-26 01:51:51 +03:00
/* 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 )
{
2009-06-04 21:13:25 +04:00
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( dev ) ;
2008-01-26 01:51:51 +03:00
union {
void * buf ;
struct rndis_msg_hdr * header ;
struct rndis_query * get ;
struct rndis_query_c * get_c ;
} u ;
int ret , buflen ;
2010-03-04 19:27:02 +03:00
int resplen , respoffs , copylen ;
2008-01-26 01:51:51 +03:00
buflen = * len + sizeof ( * u . get ) ;
if ( buflen < CONTROL_BUFFER_SIZE )
buflen = CONTROL_BUFFER_SIZE ;
2008-06-12 21:19:19 +04:00
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 ) ;
2008-01-26 01:51:51 +03:00
memset ( u . get , 0 , sizeof * u . get ) ;
u . get - > msg_type = RNDIS_MSG_QUERY ;
2009-02-15 09:56:56 +03:00
u . get - > msg_len = cpu_to_le32 ( sizeof * u . get ) ;
2008-01-26 01:51:51 +03:00
u . get - > oid = oid ;
2009-08-28 13:58:49 +04:00
priv - > current_command_oid = oid ;
2008-06-18 16:40:12 +04:00
ret = rndis_command ( dev , u . header , buflen ) ;
2009-08-28 13:58:49 +04:00
priv - > current_command_oid = 0 ;
2009-07-30 20:41:47 +04:00
if ( ret < 0 )
2010-02-17 13:30:23 +03:00
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 ) ) ;
2009-07-30 20:41:47 +04:00
2008-01-26 01:51:51 +03:00
if ( ret = = 0 ) {
2010-03-04 19:27:02 +03:00
resplen = le32_to_cpu ( u . get_c - > len ) ;
respoffs = le32_to_cpu ( u . get_c - > offset ) + 8 ;
2009-11-22 21:16:42 +03:00
2010-03-04 19:27:02 +03:00
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 ;
2009-07-30 20:41:47 +04:00
2009-11-22 21:16:42 +03:00
ret = rndis_error_status ( u . get_c - > status ) ;
2009-07-30 20:41:47 +04:00
if ( ret < 0 )
2010-02-17 13:30:23 +03:00
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 ) ;
2008-01-26 01:51:51 +03:00
}
2010-03-04 19:27:02 +03:00
exit_unlock :
2008-06-12 21:19:19 +04:00
mutex_unlock ( & priv - > command_lock ) ;
if ( u . buf ! = priv - > command_buffer )
kfree ( u . buf ) ;
2008-01-26 01:51:51 +03:00
return ret ;
}
static int rndis_set_oid ( struct usbnet * dev , __le32 oid , void * data , int len )
{
2009-06-04 21:13:25 +04:00
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( dev ) ;
2008-01-26 01:51:51 +03:00
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 ;
2008-06-12 21:19:19 +04:00
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 ) ;
2008-01-26 01:51:51 +03:00
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 ) ;
2009-02-15 09:56:56 +03:00
u . set - > offset = cpu_to_le32 ( sizeof ( * u . set ) - 8 ) ;
u . set - > handle = cpu_to_le32 ( 0 ) ;
2008-01-26 01:51:51 +03:00
memcpy ( u . buf + sizeof ( * u . set ) , data , len ) ;
2009-08-28 13:58:49 +04:00
priv - > current_command_oid = oid ;
2008-06-18 16:40:12 +04:00
ret = rndis_command ( dev , u . header , buflen ) ;
2009-08-28 13:58:49 +04:00
priv - > current_command_oid = 0 ;
2009-07-30 20:41:47 +04:00
if ( ret < 0 )
2010-02-17 13:30:23 +03:00
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 ) ) ;
2009-07-30 20:41:47 +04:00
if ( ret = = 0 ) {
2008-01-26 01:51:51 +03:00
ret = rndis_error_status ( u . set_c - > status ) ;
2009-07-30 20:41:47 +04:00
if ( ret < 0 )
2010-02-17 13:30:23 +03:00
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 ) ;
2009-07-30 20:41:47 +04:00
}
2008-06-12 21:19:19 +04:00
mutex_unlock ( & priv - > command_lock ) ;
if ( u . buf ! = priv - > command_buffer )
kfree ( u . buf ) ;
2008-01-26 01:51:51 +03:00
return ret ;
}
2009-07-30 20:41:37 +04:00
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 ) ) ;
2009-08-28 13:58:49 +04:00
priv - > current_command_oid = 0 ;
2009-07-30 20:41:37 +04:00
ret = rndis_command ( usbdev , ( void * ) reset , CONTROL_BUFFER_SIZE ) ;
mutex_unlock ( & priv - > command_lock ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
2008-01-26 01:51:51 +03:00
/*
* 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 )
{
2008-03-08 02:23:17 +03:00
struct ndis_config_param * infobuf ;
2008-01-26 01:51:51 +03:00
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 )
2010-02-17 13:30:23 +03:00
netdev_dbg ( dev - > net , " setting config parameter: %s, value: %s \n " ,
param , ( u8 * ) value ) ;
2008-01-26 01:51:51 +03:00
else
2010-02-17 13:30:23 +03:00
netdev_dbg ( dev - > net , " setting config parameter: %s, value: %d \n " ,
param , * ( u32 * ) value ) ;
2008-01-26 01:51:51 +03:00
2008-03-08 02:23:25 +03:00
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 ) ;
2008-01-26 01:51:51 +03:00
/* 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
2010-02-17 13:30:23 +03:00
netdev_dbg ( dev - > net , " info buffer (len: %d) \n " , info_len ) ;
2008-01-26 01:51:51 +03:00
for ( i = 0 ; i < info_len ; i + = 12 ) {
u32 * tmp = ( u32 * ) ( ( u8 * ) infobuf + i ) ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( dev - > net , " %08X:%08X:%08X \n " ,
cpu_to_be32 ( tmp [ 0 ] ) ,
cpu_to_be32 ( tmp [ 1 ] ) ,
cpu_to_be32 ( tmp [ 2 ] ) ) ;
2008-01-26 01:51:51 +03:00
}
# endif
ret = rndis_set_oid ( dev , OID_GEN_RNDIS_CONFIG_PARAMETER ,
infobuf , info_len ) ;
if ( ret ! = 0 )
2010-02-17 13:30:23 +03:00
netdev_dbg ( dev - > net , " setting rndis config parameter failed, %d \n " ,
ret ) ;
2008-01-26 01:51:51 +03:00
kfree ( infobuf ) ;
return ret ;
}
static int rndis_set_config_parameter_str ( struct usbnet * dev ,
char * param , char * value )
{
2009-09-01 16:33:00 +04:00
return rndis_set_config_parameter ( dev , param , 2 , value ) ;
2008-01-26 01:51:51 +03:00
}
/*
* 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
*/
2009-08-28 13:59:26 +04:00
static int set_infra_mode ( struct usbnet * usbdev , int mode ) ;
2009-07-30 20:42:08 +04:00
static void restore_keys ( struct usbnet * usbdev ) ;
2009-08-28 13:58:55 +04:00
static int rndis_check_bssid_list ( struct usbnet * usbdev ) ;
2008-01-26 01:51:51 +03:00
2008-03-08 02:23:17 +03:00
static int set_essid ( struct usbnet * usbdev , struct ndis_80211_ssid * ssid )
2008-01-26 01:51:51 +03:00
{
2009-06-04 21:13:25 +04:00
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( usbdev ) ;
2008-01-26 01:51:51 +03:00
int ret ;
ret = rndis_set_oid ( usbdev , OID_802_11_SSID , ssid , sizeof ( * ssid ) ) ;
2009-08-28 14:27:47 +04:00
if ( ret < 0 ) {
2010-02-17 13:30:23 +03:00
netdev_warn ( usbdev - > net , " setting SSID failed (%08X) \n " , ret ) ;
2009-08-28 14:27:47 +04:00
return ret ;
}
2008-01-26 01:51:51 +03:00
if ( ret = = 0 ) {
memcpy ( & priv - > essid , ssid , sizeof ( priv - > essid ) ) ;
2009-09-01 16:32:55 +04:00
priv - > radio_on = true ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(): radio_on = true \n " , __func__ ) ;
2008-01-26 01:51:51 +03:00
}
return ret ;
}
2009-08-28 14:27:47 +04:00
static int set_bssid ( struct usbnet * usbdev , u8 bssid [ ETH_ALEN ] )
{
int ret ;
ret = rndis_set_oid ( usbdev , OID_802_11_BSSID , bssid , ETH_ALEN ) ;
if ( ret < 0 ) {
2010-02-17 13:30:23 +03:00
netdev_warn ( usbdev - > net , " setting BSSID[%pM] failed (%08X) \n " ,
bssid , ret ) ;
2009-08-28 14:27:47 +04:00
return ret ;
}
return ret ;
}
static int clear_bssid ( struct usbnet * usbdev )
{
u8 broadcast_mac [ ETH_ALEN ] = { 0xff , 0xff , 0xff , 0xff , 0xff , 0xff } ;
return set_bssid ( usbdev , broadcast_mac ) ;
}
2008-01-26 01:51:51 +03:00
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 ;
}
2008-05-27 01:06:15 +04:00
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 ) ;
}
2008-01-26 01:51:51 +03:00
2009-08-28 14:27:47 +04:00
static bool is_associated ( struct usbnet * usbdev )
2008-01-26 01:51:51 +03:00
{
2009-08-28 14:27:47 +04:00
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( usbdev ) ;
2008-01-26 01:51:51 +03:00
u8 bssid [ ETH_ALEN ] ;
int ret ;
2009-08-28 14:27:47 +04:00
if ( ! priv - > radio_on )
return false ;
2008-01-26 01:51:51 +03:00
ret = get_bssid ( usbdev , bssid ) ;
2009-08-28 13:59:15 +04:00
return ( ret = = 0 & & ! is_zero_ether_addr ( bssid ) ) ;
2008-01-26 01:51:51 +03:00
}
2009-09-01 16:32:55 +04:00
static int disassociate ( struct usbnet * usbdev , bool reset_ssid )
2008-01-26 01:51:51 +03:00
{
2009-06-04 21:13:25 +04:00
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( usbdev ) ;
2008-03-08 02:23:17 +03:00
struct ndis_80211_ssid ssid ;
2008-01-26 01:51:51 +03:00
int i , ret = 0 ;
if ( priv - > radio_on ) {
ret = rndis_set_oid ( usbdev , OID_802_11_DISASSOCIATE , NULL , 0 ) ;
if ( ret = = 0 ) {
2009-09-01 16:32:55 +04:00
priv - > radio_on = false ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(): radio_on = false \n " ,
__func__ ) ;
2008-01-26 01:51:51 +03:00
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 ) {
2009-08-28 13:59:26 +04:00
/* Set device to infrastructure mode so we don't get ad-hoc
* ' media connect ' indications with the random ssid .
*/
set_infra_mode ( usbdev , NDIS_80211_INFRA_INFRA ) ;
2008-03-08 02:23:25 +03:00
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 ) ;
2008-01-26 01:51:51 +03:00
ret = set_essid ( usbdev , & ssid ) ;
}
return ret ;
}
2009-08-28 14:27:47 +04:00
static int set_auth_mode ( struct usbnet * usbdev , u32 wpa_version ,
enum nl80211_auth_type auth_type , int keymgmt )
2008-01-26 01:51:51 +03:00
{
2009-06-04 21:13:25 +04:00
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( usbdev ) ;
2008-01-26 01:51:51 +03:00
__le32 tmp ;
int auth_mode , ret ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(): wpa_version=0x%x authalg=0x%x keymgmt=0x%x \n " ,
__func__ , wpa_version , auth_type , keymgmt ) ;
2008-01-26 01:51:51 +03:00
2009-08-28 14:27:47 +04:00
if ( wpa_version & NL80211_WPA_VERSION_2 ) {
if ( keymgmt & RNDIS_WLAN_KEY_MGMT_802_1X )
2009-06-04 21:13:19 +04:00
auth_mode = NDIS_80211_AUTH_WPA2 ;
2008-01-26 01:51:51 +03:00
else
2009-06-04 21:13:19 +04:00
auth_mode = NDIS_80211_AUTH_WPA2_PSK ;
2009-08-28 14:27:47 +04:00
} else if ( wpa_version & NL80211_WPA_VERSION_1 ) {
if ( keymgmt & RNDIS_WLAN_KEY_MGMT_802_1X )
2009-06-04 21:13:19 +04:00
auth_mode = NDIS_80211_AUTH_WPA ;
2009-08-28 14:27:47 +04:00
else if ( keymgmt & RNDIS_WLAN_KEY_MGMT_PSK )
2009-06-04 21:13:19 +04:00
auth_mode = NDIS_80211_AUTH_WPA_PSK ;
2008-01-26 01:51:51 +03:00
else
2009-06-04 21:13:19 +04:00
auth_mode = NDIS_80211_AUTH_WPA_NONE ;
2009-08-28 14:27:47 +04:00
} else if ( auth_type = = NL80211_AUTHTYPE_SHARED_KEY )
auth_mode = NDIS_80211_AUTH_SHARED ;
else if ( auth_type = = NL80211_AUTHTYPE_OPEN_SYSTEM )
2009-06-04 21:13:19 +04:00
auth_mode = NDIS_80211_AUTH_OPEN ;
2009-11-16 13:49:43 +03:00
else if ( auth_type = = NL80211_AUTHTYPE_AUTOMATIC )
auth_mode = NDIS_80211_AUTH_AUTO_SWITCH ;
2009-08-28 14:27:47 +04:00
else
return - ENOTSUPP ;
2008-01-26 01:51:51 +03:00
tmp = cpu_to_le32 ( auth_mode ) ;
ret = rndis_set_oid ( usbdev , OID_802_11_AUTHENTICATION_MODE , & tmp ,
sizeof ( tmp ) ) ;
if ( ret ! = 0 ) {
2010-02-17 13:30:23 +03:00
netdev_warn ( usbdev - > net , " setting auth mode failed (%08X) \n " ,
ret ) ;
2008-01-26 01:51:51 +03:00
return ret ;
}
priv - > wpa_version = wpa_version ;
2009-08-28 14:27:47 +04:00
2008-01-26 01:51:51 +03:00
return 0 ;
}
static int set_priv_filter ( struct usbnet * usbdev )
{
2009-06-04 21:13:25 +04:00
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( usbdev ) ;
2008-01-26 01:51:51 +03:00
__le32 tmp ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(): wpa_version=0x%x \n " ,
__func__ , priv - > wpa_version ) ;
2008-01-26 01:51:51 +03:00
2009-08-28 14:27:47 +04:00
if ( priv - > wpa_version & NL80211_WPA_VERSION_2 | |
priv - > wpa_version & NL80211_WPA_VERSION_1 )
2009-06-04 21:13:19 +04:00
tmp = cpu_to_le32 ( NDIS_80211_PRIV_8021X_WEP ) ;
2008-01-26 01:51:51 +03:00
else
2009-06-04 21:13:19 +04:00
tmp = cpu_to_le32 ( NDIS_80211_PRIV_ACCEPT_ALL ) ;
2008-01-26 01:51:51 +03:00
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 )
{
__le32 tmp ;
int encr_mode , ret ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(): cipher_pair=0x%x cipher_group=0x%x \n " ,
__func__ , pairwise , groupwise ) ;
2008-01-26 01:51:51 +03:00
2009-08-28 14:27:47 +04:00
if ( pairwise & RNDIS_WLAN_ALG_CCMP )
2009-06-04 21:13:19 +04:00
encr_mode = NDIS_80211_ENCR_CCMP_ENABLED ;
2009-08-28 14:27:47 +04:00
else if ( pairwise & RNDIS_WLAN_ALG_TKIP )
2009-06-04 21:13:19 +04:00
encr_mode = NDIS_80211_ENCR_TKIP_ENABLED ;
2009-08-28 14:27:47 +04:00
else if ( pairwise & RNDIS_WLAN_ALG_WEP )
2009-06-04 21:13:19 +04:00
encr_mode = NDIS_80211_ENCR_WEP_ENABLED ;
2009-08-28 14:27:47 +04:00
else if ( groupwise & RNDIS_WLAN_ALG_CCMP )
2009-06-04 21:13:19 +04:00
encr_mode = NDIS_80211_ENCR_CCMP_ENABLED ;
2009-08-28 14:27:47 +04:00
else if ( groupwise & RNDIS_WLAN_ALG_TKIP )
2009-06-04 21:13:19 +04:00
encr_mode = NDIS_80211_ENCR_TKIP_ENABLED ;
2008-01-26 01:51:51 +03:00
else
2009-06-04 21:13:19 +04:00
encr_mode = NDIS_80211_ENCR_DISABLED ;
2008-01-26 01:51:51 +03:00
tmp = cpu_to_le32 ( encr_mode ) ;
ret = rndis_set_oid ( usbdev , OID_802_11_ENCRYPTION_STATUS , & tmp ,
sizeof ( tmp ) ) ;
if ( ret ! = 0 ) {
2010-02-17 13:30:23 +03:00
netdev_warn ( usbdev - > net , " setting encr mode failed (%08X) \n " ,
ret ) ;
2008-01-26 01:51:51 +03:00
return ret ;
}
return 0 ;
}
static int set_infra_mode ( struct usbnet * usbdev , int mode )
{
2009-06-04 21:13:25 +04:00
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( usbdev ) ;
2008-01-26 01:51:51 +03:00
__le32 tmp ;
2009-07-30 20:42:08 +04:00
int ret ;
2008-01-26 01:51:51 +03:00
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(): infra_mode=0x%x \n " ,
__func__ , priv - > infra_mode ) ;
2008-01-26 01:51:51 +03:00
tmp = cpu_to_le32 ( mode ) ;
ret = rndis_set_oid ( usbdev , OID_802_11_INFRASTRUCTURE_MODE , & tmp ,
sizeof ( tmp ) ) ;
if ( ret ! = 0 ) {
2010-02-17 13:30:23 +03:00
netdev_warn ( usbdev - > net , " setting infra mode failed (%08X) \n " ,
ret ) ;
2008-01-26 01:51:51 +03:00
return ret ;
}
/* NDIS drivers clear keys when infrastructure mode is
* changed . But Linux tools assume otherwise . So set the
* keys */
2009-07-30 20:42:08 +04:00
restore_keys ( usbdev ) ;
2008-01-26 01:51:51 +03:00
priv - > infra_mode = mode ;
return 0 ;
}
2009-06-16 18:17:21 +04:00
static int set_rts_threshold ( struct usbnet * usbdev , u32 rts_threshold )
{
__le32 tmp ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(): %i \n " , __func__ , rts_threshold ) ;
2009-06-16 18:17:21 +04:00
if ( rts_threshold < 0 | | rts_threshold > 2347 )
rts_threshold = 2347 ;
tmp = cpu_to_le32 ( rts_threshold ) ;
return rndis_set_oid ( usbdev , OID_802_11_RTS_THRESHOLD , & tmp ,
sizeof ( tmp ) ) ;
}
static int set_frag_threshold ( struct usbnet * usbdev , u32 frag_threshold )
{
__le32 tmp ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(): %i \n " , __func__ , frag_threshold ) ;
2009-06-16 18:17:21 +04:00
if ( frag_threshold < 256 | | frag_threshold > 2346 )
frag_threshold = 2346 ;
tmp = cpu_to_le32 ( frag_threshold ) ;
return rndis_set_oid ( usbdev , OID_802_11_FRAGMENTATION_THRESHOLD , & tmp ,
sizeof ( tmp ) ) ;
}
2008-01-26 01:51:51 +03:00
static void set_default_iw_params ( struct usbnet * usbdev )
{
2009-06-04 21:13:19 +04:00
set_infra_mode ( usbdev , NDIS_80211_INFRA_INFRA ) ;
2009-08-28 14:27:47 +04:00
set_auth_mode ( usbdev , 0 , NL80211_AUTHTYPE_OPEN_SYSTEM ,
RNDIS_WLAN_KEY_MGMT_NONE ) ;
2008-01-26 01:51:51 +03:00
set_priv_filter ( usbdev ) ;
2009-08-28 14:27:47 +04:00
set_encr_mode ( usbdev , RNDIS_WLAN_ALG_NONE , RNDIS_WLAN_ALG_NONE ) ;
2008-01-26 01:51:51 +03:00
}
static int deauthenticate ( struct usbnet * usbdev )
{
int ret ;
2009-09-01 16:32:55 +04:00
ret = disassociate ( usbdev , true ) ;
2008-01-26 01:51:51 +03:00
set_default_iw_params ( usbdev ) ;
return ret ;
}
2009-08-28 14:27:47 +04:00
static int set_channel ( struct usbnet * usbdev , int channel )
{
struct ndis_80211_conf config ;
unsigned int dsconfig ;
int len , ret ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(%d) \n " , __func__ , channel ) ;
2009-08-28 14:27:47 +04:00
/* this OID is valid only when not associated */
if ( is_associated ( usbdev ) )
return 0 ;
dsconfig = ieee80211_dsss_chan_to_freq ( channel ) * 1000 ;
len = sizeof ( config ) ;
ret = rndis_query_oid ( usbdev , OID_802_11_CONFIGURATION , & config , & len ) ;
if ( ret < 0 ) {
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(): querying configuration failed \n " ,
__func__ ) ;
2009-08-28 14:27:47 +04:00
return ret ;
}
config . ds_config = cpu_to_le32 ( dsconfig ) ;
ret = rndis_set_oid ( usbdev , OID_802_11_CONFIGURATION , & config ,
sizeof ( config ) ) ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(): %d -> %d \n " , __func__ , channel , ret ) ;
2009-08-28 14:27:47 +04:00
return ret ;
}
2008-01-26 01:51:51 +03:00
/* index must be 0 - N, as per NDIS */
2009-08-28 14:27:47 +04:00
static int add_wep_key ( struct usbnet * usbdev , const u8 * key , int key_len ,
int index )
2008-01-26 01:51:51 +03:00
{
2009-06-04 21:13:25 +04:00
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( usbdev ) ;
2008-03-08 02:23:17 +03:00
struct ndis_80211_wep_key ndis_key ;
2009-08-28 14:27:58 +04:00
u32 cipher ;
int ret ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(idx: %d, len: %d) \n " ,
__func__ , index , key_len ) ;
2008-01-26 01:51:51 +03:00
2009-08-20 22:00:34 +04:00
if ( ( key_len ! = 5 & & key_len ! = 13 ) | | index < 0 | | index > 3 )
2008-01-26 01:51:51 +03:00
return - EINVAL ;
2009-07-30 20:42:08 +04:00
if ( key_len = = 5 )
cipher = WLAN_CIPHER_SUITE_WEP40 ;
else
cipher = WLAN_CIPHER_SUITE_WEP104 ;
2008-01-26 01:51:51 +03:00
memset ( & ndis_key , 0 , sizeof ( ndis_key ) ) ;
2008-03-08 02:23:25 +03:00
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 ) ;
2008-01-26 01:51:51 +03:00
if ( index = = priv - > encr_tx_key_index ) {
2009-06-04 21:13:19 +04:00
ndis_key . index | = NDIS_80211_ADDWEP_TRANSMIT_KEY ;
2009-08-28 14:27:47 +04:00
ret = set_encr_mode ( usbdev , RNDIS_WLAN_ALG_WEP ,
RNDIS_WLAN_ALG_NONE ) ;
2008-01-26 01:51:51 +03:00
if ( ret )
2010-02-17 13:30:23 +03:00
netdev_warn ( usbdev - > net , " encryption couldn't be enabled (%08X) \n " ,
ret ) ;
2008-01-26 01:51:51 +03:00
}
ret = rndis_set_oid ( usbdev , OID_802_11_ADD_WEP , & ndis_key ,
sizeof ( ndis_key ) ) ;
if ( ret ! = 0 ) {
2010-02-17 13:30:23 +03:00
netdev_warn ( usbdev - > net , " adding encryption key %d failed (%08X) \n " ,
index + 1 , ret ) ;
2008-01-26 01:51:51 +03:00
return ret ;
}
2009-07-30 20:42:08 +04:00
priv - > encr_keys [ index ] . len = key_len ;
priv - > encr_keys [ index ] . cipher = cipher ;
memcpy ( & priv - > encr_keys [ index ] . material , key , key_len ) ;
memset ( & priv - > encr_keys [ index ] . bssid , 0xff , ETH_ALEN ) ;
2008-01-26 01:51:51 +03:00
return 0 ;
}
2009-05-22 18:40:27 +04:00
static int add_wpa_key ( struct usbnet * usbdev , const u8 * key , int key_len ,
2009-08-28 14:27:58 +04:00
int index , const u8 * addr , const u8 * rx_seq ,
2009-09-01 16:33:06 +04:00
int seq_len , u32 cipher , __le32 flags )
2009-05-22 18:40:27 +04:00
{
2009-06-04 21:13:25 +04:00
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( usbdev ) ;
2009-05-22 18:40:27 +04:00
struct ndis_80211_key ndis_key ;
2009-07-30 20:42:08 +04:00
bool is_addr_ok ;
2009-05-22 18:40:27 +04:00
int ret ;
2009-07-30 20:42:08 +04:00
if ( index < 0 | | index > = 4 ) {
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(): index out of range (%i) \n " ,
__func__ , index ) ;
2009-05-22 18:40:27 +04:00
return - EINVAL ;
2009-07-30 20:42:08 +04:00
}
if ( key_len > sizeof ( ndis_key . material ) | | key_len < 0 ) {
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(): key length out of range (%i) \n " ,
__func__ , key_len ) ;
2009-05-22 18:40:27 +04:00
return - EINVAL ;
2009-07-30 20:42:08 +04:00
}
2009-08-28 14:27:58 +04:00
if ( flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ ) {
if ( ! rx_seq | | seq_len < = 0 ) {
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(): recv seq flag without buffer \n " ,
__func__ ) ;
2009-08-28 14:27:58 +04:00
return - EINVAL ;
}
if ( rx_seq & & seq_len > sizeof ( ndis_key . rsc ) ) {
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(): too big recv seq buffer \n " , __func__ ) ;
2009-08-28 14:27:58 +04:00
return - EINVAL ;
}
2009-07-30 20:42:08 +04:00
}
2009-08-28 14:27:58 +04:00
2009-08-28 13:59:15 +04:00
is_addr_ok = addr & & ! is_zero_ether_addr ( addr ) & &
! is_broadcast_ether_addr ( addr ) ;
2009-07-30 20:42:08 +04:00
if ( ( flags & NDIS_80211_ADDKEY_PAIRWISE_KEY ) & & ! is_addr_ok ) {
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(): pairwise but bssid invalid (%pM) \n " ,
__func__ , addr ) ;
2009-05-22 18:40:27 +04:00
return - EINVAL ;
2009-07-30 20:42:08 +04:00
}
2009-05-22 18:40:27 +04:00
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(%i): flags:%i%i%i \n " ,
__func__ , index ,
! ! ( flags & NDIS_80211_ADDKEY_TRANSMIT_KEY ) ,
! ! ( flags & NDIS_80211_ADDKEY_PAIRWISE_KEY ) ,
! ! ( flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ ) ) ;
2009-05-22 18:40:27 +04:00
memset ( & ndis_key , 0 , sizeof ( ndis_key ) ) ;
ndis_key . size = cpu_to_le32 ( sizeof ( ndis_key ) -
sizeof ( ndis_key . material ) + key_len ) ;
ndis_key . length = cpu_to_le32 ( key_len ) ;
ndis_key . index = cpu_to_le32 ( index ) | flags ;
2009-07-30 20:42:08 +04:00
if ( cipher = = WLAN_CIPHER_SUITE_TKIP & & key_len = = 32 ) {
2009-05-22 18:40:27 +04:00
/* wpa_supplicant gives us the Michael MIC RX/TX keys in
* different order than NDIS spec , so swap the order here . */
memcpy ( ndis_key . material , key , 16 ) ;
memcpy ( ndis_key . material + 16 , key + 24 , 8 ) ;
memcpy ( ndis_key . material + 24 , key + 16 , 8 ) ;
} else
memcpy ( ndis_key . material , key , key_len ) ;
2009-06-04 21:13:19 +04:00
if ( flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ )
2009-08-28 14:27:58 +04:00
memcpy ( ndis_key . rsc , rx_seq , seq_len ) ;
2009-05-22 18:40:27 +04:00
2009-06-04 21:13:19 +04:00
if ( flags & NDIS_80211_ADDKEY_PAIRWISE_KEY ) {
2009-05-22 18:40:27 +04:00
/* pairwise key */
2009-07-30 20:42:08 +04:00
memcpy ( ndis_key . bssid , addr , ETH_ALEN ) ;
2009-05-22 18:40:27 +04:00
} else {
/* group key */
2009-06-04 21:13:19 +04:00
if ( priv - > infra_mode = = NDIS_80211_INFRA_ADHOC )
2009-05-22 18:40:27 +04:00
memset ( ndis_key . bssid , 0xff , ETH_ALEN ) ;
else
get_bssid ( usbdev , ndis_key . bssid ) ;
}
ret = rndis_set_oid ( usbdev , OID_802_11_ADD_KEY , & ndis_key ,
le32_to_cpu ( ndis_key . size ) ) ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(): OID_802_11_ADD_KEY -> %08X \n " ,
__func__ , ret ) ;
2009-05-22 18:40:27 +04:00
if ( ret ! = 0 )
return ret ;
2009-07-30 20:42:08 +04:00
memset ( & priv - > encr_keys [ index ] , 0 , sizeof ( priv - > encr_keys [ index ] ) ) ;
priv - > encr_keys [ index ] . len = key_len ;
priv - > encr_keys [ index ] . cipher = cipher ;
memcpy ( & priv - > encr_keys [ index ] . material , key , key_len ) ;
if ( flags & NDIS_80211_ADDKEY_PAIRWISE_KEY )
memcpy ( & priv - > encr_keys [ index ] . bssid , ndis_key . bssid , ETH_ALEN ) ;
else
memset ( & priv - > encr_keys [ index ] . bssid , 0xff , ETH_ALEN ) ;
2009-05-22 18:40:34 +04:00
2009-06-04 21:13:19 +04:00
if ( flags & NDIS_80211_ADDKEY_TRANSMIT_KEY )
2009-05-22 18:40:27 +04:00
priv - > encr_tx_key_index = index ;
return 0 ;
}
2009-07-30 20:42:08 +04:00
static int restore_key ( struct usbnet * usbdev , int key_idx )
{
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( usbdev ) ;
struct rndis_wlan_encr_key key ;
2009-08-28 14:27:58 +04:00
if ( is_wpa_key ( priv , key_idx ) )
return 0 ;
2009-07-30 20:42:08 +04:00
key = priv - > encr_keys [ key_idx ] ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(): %i:%i \n " , __func__ , key_idx , key . len ) ;
2009-07-30 20:42:08 +04:00
if ( key . len = = 0 )
return 0 ;
return add_wep_key ( usbdev , key . material , key . len , key_idx ) ;
}
static void restore_keys ( struct usbnet * usbdev )
{
int i ;
for ( i = 0 ; i < 4 ; i + + )
restore_key ( usbdev , i ) ;
}
static void clear_key ( struct rndis_wlan_private * priv , int idx )
{
memset ( & priv - > encr_keys [ idx ] , 0 , sizeof ( priv - > encr_keys [ idx ] ) ) ;
}
2008-01-26 01:51:51 +03:00
/* remove_key is for both wep and wpa */
2009-08-28 14:27:58 +04:00
static int remove_key ( struct usbnet * usbdev , int index , const u8 * bssid )
2008-01-26 01:51:51 +03:00
{
2009-06-04 21:13:25 +04:00
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( usbdev ) ;
2008-03-08 02:23:17 +03:00
struct ndis_80211_remove_key remove_key ;
2008-01-26 01:51:51 +03:00
__le32 keyindex ;
2009-07-30 20:42:08 +04:00
bool is_wpa ;
2008-01-26 01:51:51 +03:00
int ret ;
2009-07-30 20:42:08 +04:00
if ( priv - > encr_keys [ index ] . len = = 0 )
2008-01-26 01:51:51 +03:00
return 0 ;
2009-07-30 20:42:08 +04:00
is_wpa = is_wpa_key ( priv , index ) ;
2008-01-26 01:51:51 +03:00
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(): %i:%s:%i \n " ,
__func__ , index , is_wpa ? " wpa " : " wep " ,
priv - > encr_keys [ index ] . len ) ;
2009-07-30 20:42:08 +04:00
clear_key ( priv , index ) ;
if ( is_wpa ) {
2008-03-08 02:23:25 +03:00
remove_key . size = cpu_to_le32 ( sizeof ( remove_key ) ) ;
remove_key . index = cpu_to_le32 ( index ) ;
2008-01-26 01:51:51 +03:00
if ( bssid ) {
/* pairwise key */
2009-08-28 13:59:15 +04:00
if ( ! is_broadcast_ether_addr ( bssid ) )
2009-05-22 18:40:20 +04:00
remove_key . index | =
2009-06-04 21:13:19 +04:00
NDIS_80211_ADDKEY_PAIRWISE_KEY ;
2008-03-08 02:23:25 +03:00
memcpy ( remove_key . bssid , bssid ,
sizeof ( remove_key . bssid ) ) ;
2008-01-26 01:51:51 +03:00
} else
2008-03-08 02:23:25 +03:00
memset ( remove_key . bssid , 0xff ,
sizeof ( remove_key . bssid ) ) ;
2008-01-26 01:51:51 +03:00
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 ) {
2010-02-17 13:30:23 +03:00
netdev_warn ( usbdev - > net ,
" removing encryption key %d failed (%08X) \n " ,
index , ret ) ;
2008-01-26 01:51:51 +03:00
return ret ;
}
}
/* if it is transmit key, disable encryption */
if ( index = = priv - > encr_tx_key_index )
2009-08-28 14:27:47 +04:00
set_encr_mode ( usbdev , RNDIS_WLAN_ALG_NONE , RNDIS_WLAN_ALG_NONE ) ;
2008-01-26 01:51:51 +03:00
return 0 ;
}
static void set_multicast_list ( struct usbnet * usbdev )
{
2009-06-04 21:13:25 +04:00
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( usbdev ) ;
2008-01-26 01:51:51 +03:00
struct dev_mc_list * mclist ;
__le32 filter ;
int ret , i , size ;
char * buf ;
filter = RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_BROADCAST ;
2010-02-27 10:35:45 +03:00
netif_addr_lock_bh ( usbdev - > net ) ;
2008-01-26 01:51:51 +03:00
if ( usbdev - > net - > flags & IFF_PROMISC ) {
filter | = RNDIS_PACKET_TYPE_PROMISCUOUS |
RNDIS_PACKET_TYPE_ALL_LOCAL ;
} else if ( usbdev - > net - > flags & IFF_ALLMULTI | |
2010-02-08 07:30:35 +03:00
netdev_mc_count ( usbdev - > net ) > priv - > multicast_size ) {
2008-01-26 01:51:51 +03:00
filter | = RNDIS_PACKET_TYPE_ALL_MULTICAST ;
2010-02-08 07:30:35 +03:00
} else if ( ! netdev_mc_empty ( usbdev - > net ) ) {
size = min ( priv - > multicast_size , netdev_mc_count ( usbdev - > net ) ) ;
2008-01-26 01:51:51 +03:00
buf = kmalloc ( size * ETH_ALEN , GFP_KERNEL ) ;
if ( ! buf ) {
2010-02-17 13:30:23 +03:00
netdev_warn ( usbdev - > net ,
" couldn't alloc %d bytes of memory \n " ,
size * ETH_ALEN ) ;
2010-02-27 10:35:45 +03:00
netif_addr_unlock_bh ( usbdev - > net ) ;
2008-01-26 01:51:51 +03:00
return ;
}
2010-02-27 10:35:45 +03:00
i = 0 ;
netdev_for_each_mc_addr ( mclist , usbdev - > net ) {
if ( i = = size )
break ;
memcpy ( buf + i + + * ETH_ALEN , mclist - > dmi_addr , ETH_ALEN ) ;
2008-01-26 01:51:51 +03:00
}
ret = rndis_set_oid ( usbdev , OID_802_3_MULTICAST_LIST , buf ,
i * ETH_ALEN ) ;
if ( ret = = 0 & & i > 0 )
filter | = RNDIS_PACKET_TYPE_MULTICAST ;
else
filter | = RNDIS_PACKET_TYPE_ALL_MULTICAST ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " OID_802_3_MULTICAST_LIST(%d, max: %d) -> %d \n " ,
i , priv - > multicast_size , ret ) ;
2008-01-26 01:51:51 +03:00
kfree ( buf ) ;
}
2010-02-27 10:35:45 +03:00
netif_addr_unlock_bh ( usbdev - > net ) ;
2008-01-26 01:51:51 +03:00
ret = rndis_set_oid ( usbdev , OID_GEN_CURRENT_PACKET_FILTER , & filter ,
sizeof ( filter ) ) ;
if ( ret < 0 ) {
2010-02-17 13:30:23 +03:00
netdev_warn ( usbdev - > net , " couldn't set packet filter: %08x \n " ,
le32_to_cpu ( filter ) ) ;
2008-01-26 01:51:51 +03:00
}
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " OID_GEN_CURRENT_PACKET_FILTER(%08x) -> %d \n " ,
le32_to_cpu ( filter ) , ret ) ;
2008-01-26 01:51:51 +03:00
}
2009-03-27 00:40:01 +03:00
/*
* cfg80211 ops
*/
2009-06-09 23:04:43 +04:00
static int rndis_change_virtual_intf ( struct wiphy * wiphy ,
struct net_device * dev ,
2009-03-27 00:40:01 +03:00
enum nl80211_iftype type , u32 * flags ,
struct vif_params * params )
{
2009-08-28 13:59:21 +04:00
struct rndis_wlan_private * priv = wiphy_priv ( wiphy ) ;
struct usbnet * usbdev = priv - > usbdev ;
2009-03-27 00:40:01 +03:00
int mode ;
switch ( type ) {
case NL80211_IFTYPE_ADHOC :
2009-06-04 21:13:19 +04:00
mode = NDIS_80211_INFRA_ADHOC ;
2009-03-27 00:40:01 +03:00
break ;
case NL80211_IFTYPE_STATION :
2009-06-04 21:13:19 +04:00
mode = NDIS_80211_INFRA_INFRA ;
2009-03-27 00:40:01 +03:00
break ;
default :
return - EINVAL ;
}
2009-08-28 13:59:21 +04:00
priv - > wdev . iftype = type ;
2009-03-27 00:40:01 +03:00
return set_infra_mode ( usbdev , mode ) ;
}
2009-06-16 18:17:21 +04:00
static int rndis_set_wiphy_params ( struct wiphy * wiphy , u32 changed )
{
struct rndis_wlan_private * priv = wiphy_priv ( wiphy ) ;
struct usbnet * usbdev = priv - > usbdev ;
int err ;
if ( changed & WIPHY_PARAM_FRAG_THRESHOLD ) {
err = set_frag_threshold ( usbdev , wiphy - > frag_threshold ) ;
if ( err < 0 )
return err ;
}
if ( changed & WIPHY_PARAM_RTS_THRESHOLD ) {
err = set_rts_threshold ( usbdev , wiphy - > rts_threshold ) ;
if ( err < 0 )
return err ;
}
return 0 ;
}
2009-06-16 18:17:32 +04:00
static int rndis_set_tx_power ( struct wiphy * wiphy , enum tx_power_setting type ,
int dbm )
{
struct rndis_wlan_private * priv = wiphy_priv ( wiphy ) ;
struct usbnet * usbdev = priv - > usbdev ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(): type:0x%x dbm:%i \n " ,
__func__ , type , dbm ) ;
2009-06-16 18:17:32 +04:00
/* Device doesn't support changing txpower after initialization, only
* turn off / on radio . Support ' auto ' mode and setting same dBm that is
* currently used .
*/
if ( type = = TX_POWER_AUTOMATIC | | dbm = = get_bcm4320_power_dbm ( priv ) ) {
if ( ! priv - > radio_on )
2009-09-01 16:32:55 +04:00
disassociate ( usbdev , true ) ; /* turn on radio */
2009-06-16 18:17:32 +04:00
return 0 ;
}
return - ENOTSUPP ;
}
static int rndis_get_tx_power ( struct wiphy * wiphy , int * dbm )
{
struct rndis_wlan_private * priv = wiphy_priv ( wiphy ) ;
struct usbnet * usbdev = priv - > usbdev ;
* dbm = get_bcm4320_power_dbm ( priv ) ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(): dbm:%i \n " , __func__ , * dbm ) ;
2009-06-16 18:17:32 +04:00
return 0 ;
}
2009-08-28 13:59:05 +04:00
# define SCAN_DELAY_JIFFIES (6 * HZ)
2009-03-27 00:40:31 +03:00
static int rndis_scan ( struct wiphy * wiphy , struct net_device * dev ,
struct cfg80211_scan_request * request )
{
struct usbnet * usbdev = netdev_priv ( dev ) ;
2009-06-04 21:13:25 +04:00
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( usbdev ) ;
2009-03-27 00:40:31 +03:00
int ret ;
__le32 tmp ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " cfg80211.scan \n " ) ;
2009-03-27 00:40:31 +03:00
2009-08-28 13:58:55 +04:00
/* Get current bssid list from device before new scan, as new scan
* clears internal bssid list .
*/
rndis_check_bssid_list ( usbdev ) ;
2009-03-27 00:40:31 +03:00
if ( ! request )
return - EINVAL ;
if ( priv - > scan_request & & priv - > scan_request ! = request )
return - EBUSY ;
priv - > scan_request = request ;
tmp = cpu_to_le32 ( 1 ) ;
ret = rndis_set_oid ( usbdev , OID_802_11_BSSID_LIST_SCAN , & tmp ,
sizeof ( tmp ) ) ;
if ( ret = = 0 ) {
/* Wait before retrieving scan results from device */
queue_delayed_work ( priv - > workqueue , & priv - > scan_work ,
SCAN_DELAY_JIFFIES ) ;
}
return ret ;
}
static struct cfg80211_bss * rndis_bss_info_update ( struct usbnet * usbdev ,
struct ndis_80211_bssid_ex * bssid )
{
2009-06-04 21:13:25 +04:00
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( usbdev ) ;
2009-03-27 00:40:31 +03:00
struct ieee80211_channel * channel ;
s32 signal ;
u64 timestamp ;
u16 capability ;
u16 beacon_interval ;
struct ndis_80211_fixed_ies * fixed ;
int ie_len , bssid_len ;
u8 * ie ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " found bssid: '%.32s' [%pM] \n " ,
bssid - > ssid . essid , bssid - > mac ) ;
2009-08-28 13:59:00 +04:00
2009-03-27 00:40:31 +03:00
/* parse bssid structure */
bssid_len = le32_to_cpu ( bssid - > length ) ;
if ( bssid_len < sizeof ( struct ndis_80211_bssid_ex ) +
sizeof ( struct ndis_80211_fixed_ies ) )
return NULL ;
fixed = ( struct ndis_80211_fixed_ies * ) bssid - > ies ;
ie = ( void * ) ( bssid - > ies + sizeof ( struct ndis_80211_fixed_ies ) ) ;
ie_len = min ( bssid_len - ( int ) sizeof ( * bssid ) ,
( int ) le32_to_cpu ( bssid - > ie_length ) ) ;
ie_len - = sizeof ( struct ndis_80211_fixed_ies ) ;
if ( ie_len < 0 )
return NULL ;
/* extract data for cfg80211_inform_bss */
channel = ieee80211_get_channel ( priv - > wdev . wiphy ,
KHZ_TO_MHZ ( le32_to_cpu ( bssid - > config . ds_config ) ) ) ;
if ( ! channel )
return NULL ;
signal = level_to_qual ( le32_to_cpu ( bssid - > rssi ) ) ;
timestamp = le64_to_cpu ( * ( __le64 * ) fixed - > timestamp ) ;
capability = le16_to_cpu ( fixed - > capabilities ) ;
beacon_interval = le16_to_cpu ( fixed - > beacon_interval ) ;
return cfg80211_inform_bss ( priv - > wdev . wiphy , channel , bssid - > mac ,
timestamp , capability , beacon_interval , ie , ie_len , signal ,
GFP_KERNEL ) ;
}
static int rndis_check_bssid_list ( struct usbnet * usbdev )
{
void * buf = NULL ;
struct ndis_80211_bssid_list_ex * bssid_list ;
struct ndis_80211_bssid_ex * bssid ;
int ret = - EINVAL , len , count , bssid_len ;
2009-08-28 13:59:00 +04:00
bool resized = false ;
2009-03-27 00:40:31 +03:00
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " check_bssid_list \n " ) ;
2009-03-27 00:40:31 +03:00
len = CONTROL_BUFFER_SIZE ;
2009-08-28 13:59:00 +04:00
resize_buf :
2009-03-27 00:40:31 +03:00
buf = kmalloc ( len , GFP_KERNEL ) ;
if ( ! buf ) {
ret = - ENOMEM ;
goto out ;
}
ret = rndis_query_oid ( usbdev , OID_802_11_BSSID_LIST , buf , & len ) ;
if ( ret ! = 0 )
goto out ;
2009-08-28 13:59:00 +04:00
if ( ! resized & & len > CONTROL_BUFFER_SIZE ) {
resized = true ;
kfree ( buf ) ;
goto resize_buf ;
}
2009-03-27 00:40:31 +03:00
bssid_list = buf ;
bssid = bssid_list - > bssid ;
bssid_len = le32_to_cpu ( bssid - > length ) ;
count = le32_to_cpu ( bssid_list - > num_items ) ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " check_bssid_list: %d BSSIDs found (buflen: %d) \n " ,
count , len ) ;
2009-03-27 00:40:31 +03:00
while ( count & & ( ( void * ) bssid + bssid_len ) < = ( buf + len ) ) {
rndis_bss_info_update ( usbdev , bssid ) ;
bssid = ( void * ) bssid + bssid_len ;
bssid_len = le32_to_cpu ( bssid - > length ) ;
count - - ;
}
out :
kfree ( buf ) ;
return ret ;
}
static void rndis_get_scan_results ( struct work_struct * work )
{
2009-06-04 21:13:25 +04:00
struct rndis_wlan_private * priv =
container_of ( work , struct rndis_wlan_private , scan_work . work ) ;
2009-03-27 00:40:31 +03:00
struct usbnet * usbdev = priv - > usbdev ;
int ret ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " get_scan_results \n " ) ;
2009-03-27 00:40:31 +03:00
2009-07-30 20:41:31 +04:00
if ( ! priv - > scan_request )
return ;
2009-03-27 00:40:31 +03:00
ret = rndis_check_bssid_list ( usbdev ) ;
cfg80211_scan_done ( priv - > scan_request , ret < 0 ) ;
priv - > scan_request = NULL ;
}
2009-08-28 14:27:47 +04:00
static int rndis_connect ( struct wiphy * wiphy , struct net_device * dev ,
struct cfg80211_connect_params * sme )
{
struct rndis_wlan_private * priv = wiphy_priv ( wiphy ) ;
struct usbnet * usbdev = priv - > usbdev ;
struct ieee80211_channel * channel = sme - > channel ;
struct ndis_80211_ssid ssid ;
int pairwise = RNDIS_WLAN_ALG_NONE ;
int groupwise = RNDIS_WLAN_ALG_NONE ;
int keymgmt = RNDIS_WLAN_KEY_MGMT_NONE ;
int length , i , ret , chan = - 1 ;
if ( channel )
chan = ieee80211_frequency_to_channel ( channel - > center_freq ) ;
groupwise = rndis_cipher_to_alg ( sme - > crypto . cipher_group ) ;
for ( i = 0 ; i < sme - > crypto . n_ciphers_pairwise ; i + + )
pairwise | =
rndis_cipher_to_alg ( sme - > crypto . ciphers_pairwise [ i ] ) ;
if ( sme - > crypto . n_ciphers_pairwise > 0 & &
pairwise = = RNDIS_WLAN_ALG_NONE ) {
2010-02-17 13:30:23 +03:00
netdev_err ( usbdev - > net , " Unsupported pairwise cipher \n " ) ;
2009-08-28 14:27:47 +04:00
return - ENOTSUPP ;
}
for ( i = 0 ; i < sme - > crypto . n_akm_suites ; i + + )
keymgmt | =
rndis_akm_suite_to_key_mgmt ( sme - > crypto . akm_suites [ i ] ) ;
if ( sme - > crypto . n_akm_suites > 0 & &
keymgmt = = RNDIS_WLAN_KEY_MGMT_NONE ) {
2010-02-17 13:30:23 +03:00
netdev_err ( usbdev - > net , " Invalid keymgmt \n " ) ;
2009-08-28 14:27:47 +04:00
return - ENOTSUPP ;
}
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " cfg80211.connect('%.32s':[%pM]:%d:[%d,0x%x:0x%x]:[0x%x:0x%x]:0x%x) \n " ,
sme - > ssid , sme - > bssid , chan ,
sme - > privacy , sme - > crypto . wpa_versions , sme - > auth_type ,
groupwise , pairwise , keymgmt ) ;
2009-08-28 14:27:47 +04:00
if ( is_associated ( usbdev ) )
disassociate ( usbdev , false ) ;
ret = set_infra_mode ( usbdev , NDIS_80211_INFRA_INFRA ) ;
if ( ret < 0 ) {
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " connect: set_infra_mode failed, %d \n " ,
ret ) ;
2009-08-28 14:27:47 +04:00
goto err_turn_radio_on ;
}
ret = set_auth_mode ( usbdev , sme - > crypto . wpa_versions , sme - > auth_type ,
keymgmt ) ;
if ( ret < 0 ) {
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " connect: set_auth_mode failed, %d \n " ,
ret ) ;
2009-08-28 14:27:47 +04:00
goto err_turn_radio_on ;
}
set_priv_filter ( usbdev ) ;
ret = set_encr_mode ( usbdev , pairwise , groupwise ) ;
if ( ret < 0 ) {
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " connect: set_encr_mode failed, %d \n " ,
ret ) ;
2009-08-28 14:27:47 +04:00
goto err_turn_radio_on ;
}
if ( channel ) {
ret = set_channel ( usbdev , chan ) ;
if ( ret < 0 ) {
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " connect: set_channel failed, %d \n " ,
ret ) ;
2009-08-28 14:27:47 +04:00
goto err_turn_radio_on ;
}
}
if ( sme - > key & & ( ( groupwise | pairwise ) & RNDIS_WLAN_ALG_WEP ) ) {
priv - > encr_tx_key_index = sme - > key_idx ;
ret = add_wep_key ( usbdev , sme - > key , sme - > key_len , sme - > key_idx ) ;
if ( ret < 0 ) {
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " connect: add_wep_key failed, %d (%d, %d) \n " ,
ret , sme - > key_len , sme - > key_idx ) ;
2009-08-28 14:27:47 +04:00
goto err_turn_radio_on ;
}
}
if ( sme - > bssid & & ! is_zero_ether_addr ( sme - > bssid ) & &
! is_broadcast_ether_addr ( sme - > bssid ) ) {
ret = set_bssid ( usbdev , sme - > bssid ) ;
if ( ret < 0 ) {
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " connect: set_bssid failed, %d \n " ,
ret ) ;
2009-08-28 14:27:47 +04:00
goto err_turn_radio_on ;
}
} else
clear_bssid ( usbdev ) ;
length = sme - > ssid_len ;
if ( length > NDIS_802_11_LENGTH_SSID )
length = NDIS_802_11_LENGTH_SSID ;
memset ( & ssid , 0 , sizeof ( ssid ) ) ;
ssid . length = cpu_to_le32 ( length ) ;
memcpy ( ssid . essid , sme - > ssid , length ) ;
/* Pause and purge rx queue, so we don't pass packets before
* ' media connect ' - indication .
*/
usbnet_pause_rx ( usbdev ) ;
usbnet_purge_paused_rxq ( usbdev ) ;
ret = set_essid ( usbdev , & ssid ) ;
if ( ret < 0 )
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " connect: set_essid failed, %d \n " , ret ) ;
2009-08-28 14:27:47 +04:00
return ret ;
err_turn_radio_on :
2009-09-01 16:32:55 +04:00
disassociate ( usbdev , true ) ;
2009-08-28 14:27:47 +04:00
return ret ;
}
static int rndis_disconnect ( struct wiphy * wiphy , struct net_device * dev ,
u16 reason_code )
{
struct rndis_wlan_private * priv = wiphy_priv ( wiphy ) ;
struct usbnet * usbdev = priv - > usbdev ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " cfg80211.disconnect(%d) \n " , reason_code ) ;
2009-08-28 14:27:47 +04:00
priv - > connected = false ;
2009-08-28 14:28:03 +04:00
memset ( priv - > bssid , 0 , ETH_ALEN ) ;
2009-08-28 14:27:47 +04:00
return deauthenticate ( usbdev ) ;
}
static int rndis_join_ibss ( struct wiphy * wiphy , struct net_device * dev ,
struct cfg80211_ibss_params * params )
{
struct rndis_wlan_private * priv = wiphy_priv ( wiphy ) ;
struct usbnet * usbdev = priv - > usbdev ;
struct ieee80211_channel * channel = params - > channel ;
struct ndis_80211_ssid ssid ;
enum nl80211_auth_type auth_type ;
int ret , alg , length , chan = - 1 ;
if ( channel )
chan = ieee80211_frequency_to_channel ( channel - > center_freq ) ;
/* TODO: How to handle ad-hoc encryption?
* connect ( ) has * key , join_ibss ( ) doesn ' t . RNDIS requires key to be
* pre - shared for encryption ( open / shared / wpa ) , is key set before
* join_ibss ? Which auth_type to use ( not in params ) ? What about WPA ?
*/
if ( params - > privacy ) {
auth_type = NL80211_AUTHTYPE_SHARED_KEY ;
alg = RNDIS_WLAN_ALG_WEP ;
} else {
auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM ;
alg = RNDIS_WLAN_ALG_NONE ;
}
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " cfg80211.join_ibss('%.32s':[%pM]:%d:%d) \n " ,
params - > ssid , params - > bssid , chan , params - > privacy ) ;
2009-08-28 14:27:47 +04:00
if ( is_associated ( usbdev ) )
disassociate ( usbdev , false ) ;
ret = set_infra_mode ( usbdev , NDIS_80211_INFRA_ADHOC ) ;
if ( ret < 0 ) {
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " join_ibss: set_infra_mode failed, %d \n " ,
ret ) ;
2009-08-28 14:27:47 +04:00
goto err_turn_radio_on ;
}
ret = set_auth_mode ( usbdev , 0 , auth_type , RNDIS_WLAN_KEY_MGMT_NONE ) ;
if ( ret < 0 ) {
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " join_ibss: set_auth_mode failed, %d \n " ,
ret ) ;
2009-08-28 14:27:47 +04:00
goto err_turn_radio_on ;
}
set_priv_filter ( usbdev ) ;
ret = set_encr_mode ( usbdev , alg , RNDIS_WLAN_ALG_NONE ) ;
if ( ret < 0 ) {
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " join_ibss: set_encr_mode failed, %d \n " ,
ret ) ;
2009-08-28 14:27:47 +04:00
goto err_turn_radio_on ;
}
if ( channel ) {
ret = set_channel ( usbdev , chan ) ;
if ( ret < 0 ) {
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " join_ibss: set_channel failed, %d \n " ,
ret ) ;
2009-08-28 14:27:47 +04:00
goto err_turn_radio_on ;
}
}
if ( params - > bssid & & ! is_zero_ether_addr ( params - > bssid ) & &
! is_broadcast_ether_addr ( params - > bssid ) ) {
ret = set_bssid ( usbdev , params - > bssid ) ;
if ( ret < 0 ) {
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " join_ibss: set_bssid failed, %d \n " ,
ret ) ;
2009-08-28 14:27:47 +04:00
goto err_turn_radio_on ;
}
} else
clear_bssid ( usbdev ) ;
length = params - > ssid_len ;
if ( length > NDIS_802_11_LENGTH_SSID )
length = NDIS_802_11_LENGTH_SSID ;
memset ( & ssid , 0 , sizeof ( ssid ) ) ;
ssid . length = cpu_to_le32 ( length ) ;
memcpy ( ssid . essid , params - > ssid , length ) ;
/* Don't need to pause rx queue for ad-hoc. */
usbnet_purge_paused_rxq ( usbdev ) ;
usbnet_resume_rx ( usbdev ) ;
ret = set_essid ( usbdev , & ssid ) ;
if ( ret < 0 )
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " join_ibss: set_essid failed, %d \n " ,
ret ) ;
2009-08-28 14:27:47 +04:00
return ret ;
err_turn_radio_on :
2009-09-01 16:32:55 +04:00
disassociate ( usbdev , true ) ;
2009-08-28 14:27:47 +04:00
return ret ;
}
static int rndis_leave_ibss ( struct wiphy * wiphy , struct net_device * dev )
{
struct rndis_wlan_private * priv = wiphy_priv ( wiphy ) ;
struct usbnet * usbdev = priv - > usbdev ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " cfg80211.leave_ibss() \n " ) ;
2009-08-28 14:27:47 +04:00
priv - > connected = false ;
2009-08-28 14:28:03 +04:00
memset ( priv - > bssid , 0 , ETH_ALEN ) ;
2009-08-28 14:27:47 +04:00
return deauthenticate ( usbdev ) ;
}
2009-08-28 14:27:53 +04:00
static int rndis_set_channel ( struct wiphy * wiphy ,
struct ieee80211_channel * chan , enum nl80211_channel_type channel_type )
{
struct rndis_wlan_private * priv = wiphy_priv ( wiphy ) ;
struct usbnet * usbdev = priv - > usbdev ;
return set_channel ( usbdev ,
ieee80211_frequency_to_channel ( chan - > center_freq ) ) ;
}
2009-03-27 00:40:31 +03:00
2009-08-28 14:27:58 +04:00
static int rndis_add_key ( struct wiphy * wiphy , struct net_device * netdev ,
u8 key_index , const u8 * mac_addr ,
struct key_params * params )
{
struct rndis_wlan_private * priv = wiphy_priv ( wiphy ) ;
struct usbnet * usbdev = priv - > usbdev ;
2009-09-01 16:33:06 +04:00
__le32 flags ;
2009-08-28 14:27:58 +04:00
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(%i, %pM, %08x) \n " ,
__func__ , key_index , mac_addr , params - > cipher ) ;
2009-08-28 14:27:58 +04:00
switch ( params - > cipher ) {
case WLAN_CIPHER_SUITE_WEP40 :
case WLAN_CIPHER_SUITE_WEP104 :
return add_wep_key ( usbdev , params - > key , params - > key_len ,
key_index ) ;
case WLAN_CIPHER_SUITE_TKIP :
case WLAN_CIPHER_SUITE_CCMP :
flags = 0 ;
if ( params - > seq & & params - > seq_len > 0 )
flags | = NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ ;
if ( mac_addr )
flags | = NDIS_80211_ADDKEY_PAIRWISE_KEY |
NDIS_80211_ADDKEY_TRANSMIT_KEY ;
return add_wpa_key ( usbdev , params - > key , params - > key_len ,
key_index , mac_addr , params - > seq ,
params - > seq_len , params - > cipher , flags ) ;
default :
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(): unsupported cipher %08x \n " ,
__func__ , params - > cipher ) ;
2009-08-28 14:27:58 +04:00
return - ENOTSUPP ;
}
}
static int rndis_del_key ( struct wiphy * wiphy , struct net_device * netdev ,
u8 key_index , const u8 * mac_addr )
{
struct rndis_wlan_private * priv = wiphy_priv ( wiphy ) ;
struct usbnet * usbdev = priv - > usbdev ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(%i, %pM) \n " , __func__ , key_index , mac_addr ) ;
2009-08-28 14:27:58 +04:00
return remove_key ( usbdev , key_index , mac_addr ) ;
}
static int rndis_set_default_key ( struct wiphy * wiphy , struct net_device * netdev ,
u8 key_index )
{
struct rndis_wlan_private * priv = wiphy_priv ( wiphy ) ;
struct usbnet * usbdev = priv - > usbdev ;
struct rndis_wlan_encr_key key ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s(%i) \n " , __func__ , key_index ) ;
2009-08-28 14:27:58 +04:00
priv - > encr_tx_key_index = key_index ;
key = priv - > encr_keys [ key_index ] ;
return add_wep_key ( usbdev , key . material , key . len , key_index ) ;
}
2009-08-28 14:28:03 +04:00
static void rndis_fill_station_info ( struct usbnet * usbdev ,
struct station_info * sinfo )
{
__le32 linkspeed , rssi ;
int ret , len ;
memset ( sinfo , 0 , sizeof ( * sinfo ) ) ;
len = sizeof ( linkspeed ) ;
ret = rndis_query_oid ( usbdev , OID_GEN_LINK_SPEED , & linkspeed , & len ) ;
if ( ret = = 0 ) {
sinfo - > txrate . legacy = le32_to_cpu ( linkspeed ) / 1000 ;
sinfo - > filled | = STATION_INFO_TX_BITRATE ;
}
len = sizeof ( rssi ) ;
ret = rndis_query_oid ( usbdev , OID_802_11_RSSI , & rssi , & len ) ;
if ( ret = = 0 ) {
sinfo - > signal = level_to_qual ( le32_to_cpu ( rssi ) ) ;
sinfo - > filled | = STATION_INFO_SIGNAL ;
}
}
static int rndis_get_station ( struct wiphy * wiphy , struct net_device * dev ,
u8 * mac , struct station_info * sinfo )
{
struct rndis_wlan_private * priv = wiphy_priv ( wiphy ) ;
struct usbnet * usbdev = priv - > usbdev ;
if ( compare_ether_addr ( priv - > bssid , mac ) )
return - ENOENT ;
rndis_fill_station_info ( usbdev , sinfo ) ;
return 0 ;
}
2009-08-28 14:28:09 +04:00
static int rndis_dump_station ( struct wiphy * wiphy , struct net_device * dev ,
int idx , u8 * mac , struct station_info * sinfo )
{
struct rndis_wlan_private * priv = wiphy_priv ( wiphy ) ;
struct usbnet * usbdev = priv - > usbdev ;
if ( idx ! = 0 )
return - ENOENT ;
memcpy ( mac , priv - > bssid , ETH_ALEN ) ;
rndis_fill_station_info ( usbdev , sinfo ) ;
return 0 ;
}
2009-09-01 16:33:00 +04:00
/*
* workers , indication handlers , device poller
*/
2009-08-28 13:59:10 +04:00
static void rndis_wlan_do_link_up_work ( struct usbnet * usbdev )
2008-01-26 01:51:51 +03:00
{
2009-08-28 14:27:47 +04:00
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( usbdev ) ;
2008-05-27 01:06:15 +04:00
struct ndis_80211_assoc_info * info ;
2009-08-28 13:59:10 +04:00
u8 assoc_buf [ sizeof ( * info ) + IW_CUSTOM_MAX + 32 ] ;
u8 bssid [ ETH_ALEN ] ;
2009-08-28 14:27:47 +04:00
int resp_ie_len , req_ie_len ;
u8 * req_ie , * resp_ie ;
2008-05-27 01:06:15 +04:00
int ret , offset ;
2009-08-28 14:27:47 +04:00
bool roamed = false ;
2008-01-26 01:51:51 +03:00
2009-08-28 14:27:47 +04:00
if ( priv - > infra_mode = = NDIS_80211_INFRA_INFRA & & priv - > connected ) {
/* received media connect indication while connected, either
* device reassociated with same AP or roamed to new . */
roamed = true ;
}
2009-08-28 13:59:10 +04:00
2009-08-28 14:27:47 +04:00
req_ie_len = 0 ;
resp_ie_len = 0 ;
req_ie = NULL ;
resp_ie = NULL ;
if ( priv - > infra_mode = = NDIS_80211_INFRA_INFRA ) {
memset ( assoc_buf , 0 , sizeof ( assoc_buf ) ) ;
info = ( void * ) assoc_buf ;
/* Get association info IEs from device and send them back to
* userspace . */
ret = get_association_info ( usbdev , info , sizeof ( assoc_buf ) ) ;
if ( ! ret ) {
req_ie_len = le32_to_cpu ( info - > req_ie_length ) ;
if ( req_ie_len > 0 ) {
offset = le32_to_cpu ( info - > offset_req_ies ) ;
req_ie = ( u8 * ) info + offset ;
}
resp_ie_len = le32_to_cpu ( info - > resp_ie_length ) ;
if ( resp_ie_len > 0 ) {
offset = le32_to_cpu ( info - > offset_resp_ies ) ;
resp_ie = ( u8 * ) info + offset ;
}
2008-05-27 01:06:15 +04:00
}
2009-08-28 14:27:47 +04:00
} else if ( WARN_ON ( priv - > infra_mode ! = NDIS_80211_INFRA_ADHOC ) )
return ;
2008-05-27 01:06:15 +04:00
2009-08-28 14:27:47 +04:00
ret = get_bssid ( usbdev , bssid ) ;
if ( ret < 0 )
memset ( bssid , 0 , sizeof ( bssid ) ) ;
2009-08-11 23:57:16 +04:00
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " link up work: [%pM]%s \n " ,
bssid , roamed ? " roamed " : " " ) ;
2008-01-26 01:51:51 +03:00
2009-08-28 14:27:47 +04:00
/* Internal bss list in device always contains at least the currently
* connected bss and we can get it to cfg80211 with
* rndis_check_bssid_list ( ) .
* NOTE : This is true for Broadcom chip , but not mentioned in RNDIS
* spec .
*/
rndis_check_bssid_list ( usbdev ) ;
if ( priv - > infra_mode = = NDIS_80211_INFRA_INFRA ) {
if ( ! roamed )
cfg80211_connect_result ( usbdev - > net , bssid , req_ie ,
req_ie_len , resp_ie ,
resp_ie_len , 0 , GFP_KERNEL ) ;
else
cfg80211_roamed ( usbdev - > net , bssid , req_ie , req_ie_len ,
resp_ie , resp_ie_len , GFP_KERNEL ) ;
} else if ( priv - > infra_mode = = NDIS_80211_INFRA_ADHOC )
cfg80211_ibss_joined ( usbdev - > net , bssid , GFP_KERNEL ) ;
priv - > connected = true ;
2009-08-28 14:28:03 +04:00
memcpy ( priv - > bssid , bssid , ETH_ALEN ) ;
2008-06-02 19:35:21 +04:00
2009-08-28 13:59:10 +04:00
usbnet_resume_rx ( usbdev ) ;
2009-08-28 14:27:47 +04:00
netif_carrier_on ( usbdev - > net ) ;
2009-08-28 13:59:10 +04:00
}
static void rndis_wlan_do_link_down_work ( struct usbnet * usbdev )
{
union iwreq_data evt ;
netif_carrier_off ( usbdev - > net ) ;
evt . data . flags = 0 ;
evt . data . length = 0 ;
memset ( evt . ap_addr . sa_data , 0 , ETH_ALEN ) ;
wireless_send_event ( usbdev - > net , SIOCGIWAP , & evt , NULL ) ;
}
static void rndis_wlan_worker ( struct work_struct * work )
{
struct rndis_wlan_private * priv =
container_of ( work , struct rndis_wlan_private , work ) ;
struct usbnet * usbdev = priv - > usbdev ;
if ( test_and_clear_bit ( WORK_LINK_UP , & priv - > work_pending ) )
rndis_wlan_do_link_up_work ( usbdev ) ;
if ( test_and_clear_bit ( WORK_LINK_DOWN , & priv - > work_pending ) )
rndis_wlan_do_link_down_work ( usbdev ) ;
2008-01-26 01:51:51 +03:00
if ( test_and_clear_bit ( WORK_SET_MULTICAST_LIST , & priv - > work_pending ) )
set_multicast_list ( usbdev ) ;
}
2009-06-04 21:13:25 +04:00
static void rndis_wlan_set_multicast_list ( struct net_device * dev )
2008-01-26 01:51:51 +03:00
{
2008-11-13 10:39:10 +03:00
struct usbnet * usbdev = netdev_priv ( dev ) ;
2009-06-04 21:13:25 +04:00
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( usbdev ) ;
2008-01-26 01:51:51 +03:00
2008-06-02 19:35:36 +04:00
if ( test_bit ( WORK_SET_MULTICAST_LIST , & priv - > work_pending ) )
return ;
2008-01-26 01:51:51 +03:00
set_bit ( WORK_SET_MULTICAST_LIST , & priv - > work_pending ) ;
queue_work ( priv - > workqueue , & priv - > work ) ;
}
2009-07-30 20:41:58 +04:00
static void rndis_wlan_auth_indication ( struct usbnet * usbdev ,
struct ndis_80211_status_indication * indication ,
int len )
{
u8 * buf ;
const char * type ;
2009-08-28 14:28:24 +04:00
int flags , buflen , key_id ;
2009-07-30 20:41:58 +04:00
bool pairwise_error , group_error ;
struct ndis_80211_auth_request * auth_req ;
2009-08-28 14:28:24 +04:00
enum nl80211_key_type key_type ;
2009-07-30 20:41:58 +04:00
/* must have at least one array entry */
if ( len < offsetof ( struct ndis_80211_status_indication , u ) +
sizeof ( struct ndis_80211_auth_request ) ) {
2010-02-17 13:30:23 +03:00
netdev_info ( usbdev - > net , " authentication indication: too short message (%i) \n " ,
len ) ;
2009-07-30 20:41:58 +04:00
return ;
}
buf = ( void * ) & indication - > u . auth_request [ 0 ] ;
buflen = len - offsetof ( struct ndis_80211_status_indication , u ) ;
while ( buflen > = sizeof ( * auth_req ) ) {
auth_req = ( void * ) buf ;
type = " unknown " ;
flags = le32_to_cpu ( auth_req - > flags ) ;
pairwise_error = false ;
group_error = false ;
if ( flags & 0x1 )
type = " reauth request " ;
if ( flags & 0x2 )
type = " key update request " ;
if ( flags & 0x6 ) {
pairwise_error = true ;
type = " pairwise_error " ;
}
if ( flags & 0xe ) {
group_error = true ;
type = " group_error " ;
}
2010-02-17 13:30:23 +03:00
netdev_info ( usbdev - > net , " authentication indication: %s (0x%08x) \n " ,
type , le32_to_cpu ( auth_req - > flags ) ) ;
2009-07-30 20:41:58 +04:00
2009-08-28 14:28:24 +04:00
if ( pairwise_error ) {
key_type = NL80211_KEYTYPE_PAIRWISE ;
key_id = - 1 ;
2009-07-30 20:41:58 +04:00
2009-08-28 14:28:24 +04:00
cfg80211_michael_mic_failure ( usbdev - > net ,
auth_req - > bssid ,
key_type , key_id , NULL ,
GFP_KERNEL ) ;
}
2009-07-30 20:41:58 +04:00
2009-08-28 14:28:24 +04:00
if ( group_error ) {
key_type = NL80211_KEYTYPE_GROUP ;
key_id = - 1 ;
2009-07-30 20:41:58 +04:00
2009-08-28 14:28:24 +04:00
cfg80211_michael_mic_failure ( usbdev - > net ,
auth_req - > bssid ,
key_type , key_id , NULL ,
GFP_KERNEL ) ;
2009-07-30 20:41:58 +04:00
}
buflen - = le32_to_cpu ( auth_req - > length ) ;
buf + = le32_to_cpu ( auth_req - > length ) ;
}
}
static void rndis_wlan_pmkid_cand_list_indication ( struct usbnet * usbdev ,
struct ndis_80211_status_indication * indication ,
int len )
{
struct ndis_80211_pmkid_cand_list * cand_list ;
int list_len , expected_len , i ;
if ( len < offsetof ( struct ndis_80211_status_indication , u ) +
sizeof ( struct ndis_80211_pmkid_cand_list ) ) {
2010-02-17 13:30:23 +03:00
netdev_info ( usbdev - > net , " pmkid candidate list indication: too short message (%i) \n " ,
len ) ;
2009-07-30 20:41:58 +04:00
return ;
}
list_len = le32_to_cpu ( indication - > u . cand_list . num_candidates ) *
sizeof ( struct ndis_80211_pmkid_candidate ) ;
expected_len = sizeof ( struct ndis_80211_pmkid_cand_list ) + list_len +
offsetof ( struct ndis_80211_status_indication , u ) ;
if ( len < expected_len ) {
2010-02-17 13:30:23 +03:00
netdev_info ( usbdev - > net , " pmkid candidate list indication: list larger than buffer (%i < %i) \n " ,
len , expected_len ) ;
2009-07-30 20:41:58 +04:00
return ;
}
cand_list = & indication - > u . cand_list ;
2010-02-17 13:30:23 +03:00
netdev_info ( usbdev - > net , " pmkid candidate list indication: version %i, candidates %i \n " ,
le32_to_cpu ( cand_list - > version ) ,
le32_to_cpu ( cand_list - > num_candidates ) ) ;
2009-07-30 20:41:58 +04:00
if ( le32_to_cpu ( cand_list - > version ) ! = 1 )
return ;
for ( i = 0 ; i < le32_to_cpu ( cand_list - > num_candidates ) ; i + + ) {
struct ndis_80211_pmkid_candidate * cand =
& cand_list - > candidate_list [ i ] ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " cand[%i]: flags: 0x%08x, bssid: %pM \n " ,
i , le32_to_cpu ( cand - > flags ) , cand - > bssid ) ;
2009-07-30 20:41:58 +04:00
2009-08-28 14:28:30 +04:00
#if 0
struct iw_pmkid_cand pcand ;
union iwreq_data wrqu ;
2009-07-30 20:41:58 +04:00
memset ( & pcand , 0 , sizeof ( pcand ) ) ;
if ( le32_to_cpu ( cand - > flags ) & 0x01 )
pcand . flags | = IW_PMKID_CAND_PREAUTH ;
pcand . index = i ;
memcpy ( pcand . bssid . sa_data , cand - > bssid , ETH_ALEN ) ;
memset ( & wrqu , 0 , sizeof ( wrqu ) ) ;
wrqu . data . length = sizeof ( pcand ) ;
wireless_send_event ( usbdev - > net , IWEVPMKIDCAND , & wrqu ,
( u8 * ) & pcand ) ;
2009-08-28 14:28:30 +04:00
# endif
2009-07-30 20:41:58 +04:00
}
}
static void rndis_wlan_media_specific_indication ( struct usbnet * usbdev ,
struct rndis_indicate * msg , int buflen )
{
struct ndis_80211_status_indication * indication ;
int len , offset ;
offset = offsetof ( struct rndis_indicate , status ) +
le32_to_cpu ( msg - > offset ) ;
len = le32_to_cpu ( msg - > length ) ;
if ( len < 8 ) {
2010-02-17 13:30:23 +03:00
netdev_info ( usbdev - > net , " media specific indication, ignore too short message (%i < 8) \n " ,
len ) ;
2009-07-30 20:41:58 +04:00
return ;
}
if ( offset + len > buflen ) {
2010-02-17 13:30:23 +03:00
netdev_info ( usbdev - > net , " media specific indication, too large to fit to buffer (%i > %i) \n " ,
offset + len , buflen ) ;
2009-07-30 20:41:58 +04:00
return ;
}
indication = ( void * ) ( ( u8 * ) msg + offset ) ;
switch ( le32_to_cpu ( indication - > status_type ) ) {
case NDIS_80211_STATUSTYPE_RADIOSTATE :
2010-02-17 13:30:23 +03:00
netdev_info ( usbdev - > net , " radio state indication: %i \n " ,
le32_to_cpu ( indication - > u . radio_status ) ) ;
2009-07-30 20:41:58 +04:00
return ;
case NDIS_80211_STATUSTYPE_MEDIASTREAMMODE :
2010-02-17 13:30:23 +03:00
netdev_info ( usbdev - > net , " media stream mode indication: %i \n " ,
le32_to_cpu ( indication - > u . media_stream_mode ) ) ;
2009-07-30 20:41:58 +04:00
return ;
case NDIS_80211_STATUSTYPE_AUTHENTICATION :
rndis_wlan_auth_indication ( usbdev , indication , len ) ;
return ;
case NDIS_80211_STATUSTYPE_PMKID_CANDIDATELIST :
rndis_wlan_pmkid_cand_list_indication ( usbdev , indication , len ) ;
return ;
default :
2010-02-17 13:30:23 +03:00
netdev_info ( usbdev - > net , " media specific indication: unknown status type 0x%08x \n " ,
le32_to_cpu ( indication - > status_type ) ) ;
2009-07-30 20:41:58 +04:00
}
}
2009-07-30 20:41:52 +04:00
static void rndis_wlan_indication ( struct usbnet * usbdev , void * ind , int buflen )
2008-01-26 01:51:51 +03:00
{
2009-06-04 21:13:25 +04:00
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( usbdev ) ;
2009-07-30 20:41:52 +04:00
struct rndis_indicate * msg = ind ;
2008-01-26 01:51:51 +03:00
2009-07-30 20:41:52 +04:00
switch ( msg - > status ) {
case RNDIS_STATUS_MEDIA_CONNECT :
2009-08-28 13:58:49 +04:00
if ( priv - > current_command_oid = = OID_802_11_ADD_KEY ) {
/* OID_802_11_ADD_KEY causes sometimes extra
* " media connect " indications which confuses driver
* and userspace to think that device is
* roaming / reassociating when it isn ' t .
*/
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " ignored OID_802_11_ADD_KEY triggered 'media connect' \n " ) ;
2009-08-28 13:58:49 +04:00
return ;
}
2009-08-11 23:57:16 +04:00
usbnet_pause_rx ( usbdev ) ;
2010-02-17 13:30:23 +03:00
netdev_info ( usbdev - > net , " media connect \n " ) ;
2009-07-30 20:41:52 +04:00
2009-07-30 20:41:58 +04:00
/* queue work to avoid recursive calls into rndis_command */
2009-07-30 20:41:52 +04:00
set_bit ( WORK_LINK_UP , & priv - > work_pending ) ;
queue_work ( priv - > workqueue , & priv - > work ) ;
break ;
case RNDIS_STATUS_MEDIA_DISCONNECT :
2010-02-17 13:30:23 +03:00
netdev_info ( usbdev - > net , " media disconnect \n " ) ;
2009-07-30 20:41:52 +04:00
2009-07-30 20:41:58 +04:00
/* queue work to avoid recursive calls into rndis_command */
2009-07-30 20:41:52 +04:00
set_bit ( WORK_LINK_DOWN , & priv - > work_pending ) ;
queue_work ( priv - > workqueue , & priv - > work ) ;
break ;
2009-07-30 20:41:58 +04:00
case RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION :
rndis_wlan_media_specific_indication ( usbdev , msg , buflen ) ;
break ;
2009-07-30 20:41:52 +04:00
default :
2010-02-17 13:30:23 +03:00
netdev_info ( usbdev - > net , " indication: 0x%08x \n " ,
le32_to_cpu ( msg - > status ) ) ;
2009-07-30 20:41:52 +04:00
break ;
}
2008-01-26 01:51:51 +03:00
}
2009-06-04 21:13:25 +04:00
static int rndis_wlan_get_caps ( struct usbnet * usbdev )
2008-01-26 01:51:51 +03:00
{
struct {
__le32 num_items ;
__le32 items [ 8 ] ;
} networks_supported ;
int len , retval , i , n ;
2009-06-04 21:13:25 +04:00
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( usbdev ) ;
2008-01-26 01:51:51 +03:00
/* determine supported modes */
len = sizeof ( networks_supported ) ;
2008-06-02 19:35:44 +04:00
retval = rndis_query_oid ( usbdev , OID_802_11_NETWORK_TYPES_SUPPORTED ,
2008-01-26 01:51:51 +03:00
& networks_supported , & len ) ;
if ( retval > = 0 ) {
n = le32_to_cpu ( networks_supported . num_items ) ;
if ( n > 8 )
n = 8 ;
for ( i = 0 ; i < n ; i + + ) {
switch ( le32_to_cpu ( networks_supported . items [ i ] ) ) {
2009-06-04 21:13:19 +04:00
case NDIS_80211_TYPE_FREQ_HOP :
case NDIS_80211_TYPE_DIRECT_SEQ :
2008-01-26 01:51:51 +03:00
priv - > caps | = CAP_MODE_80211B ;
break ;
2009-06-04 21:13:19 +04:00
case NDIS_80211_TYPE_OFDM_A :
2008-01-26 01:51:51 +03:00
priv - > caps | = CAP_MODE_80211A ;
break ;
2009-06-04 21:13:19 +04:00
case NDIS_80211_TYPE_OFDM_G :
2008-01-26 01:51:51 +03:00
priv - > caps | = CAP_MODE_80211G ;
break ;
}
}
}
return retval ;
}
2009-08-28 14:28:14 +04:00
# define DEVICE_POLLER_JIFFIES (HZ)
static void rndis_device_poller ( struct work_struct * work )
2008-01-26 01:51:51 +03:00
{
2009-06-04 21:13:25 +04:00
struct rndis_wlan_private * priv =
2009-08-28 14:28:14 +04:00
container_of ( work , struct rndis_wlan_private ,
dev_poller_work . work ) ;
2008-01-26 01:51:51 +03:00
struct usbnet * usbdev = priv - > usbdev ;
__le32 rssi , tmp ;
2008-02-06 16:36:10 +03:00
int len , ret , j ;
2009-08-28 14:28:14 +04:00
int update_jiffies = DEVICE_POLLER_JIFFIES ;
2008-01-26 01:51:51 +03:00
void * buf ;
2009-08-28 14:28:03 +04:00
/* Only check/do workaround when connected. Calling is_associated()
* also polls device with rndis_command ( ) and catches for media link
* indications .
*/
if ( ! is_associated ( usbdev ) )
2008-01-26 01:51:51 +03:00
goto end ;
len = sizeof ( rssi ) ;
ret = rndis_query_oid ( usbdev , OID_802_11_RSSI , & rssi , & len ) ;
2009-08-28 14:28:03 +04:00
if ( ret = = 0 )
priv - > last_qual = level_to_qual ( le32_to_cpu ( rssi ) ) ;
2008-01-26 01:51:51 +03:00
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " dev-poller: OID_802_11_RSSI -> %d, rssi:%d, qual: %d \n " ,
ret , le32_to_cpu ( rssi ) , level_to_qual ( le32_to_cpu ( rssi ) ) ) ;
2008-01-26 01:51:51 +03:00
2008-02-06 16:36:10 +03:00
/* Workaround transfer stalls on poor quality links.
* TODO : find right way to fix these stalls ( as stalls do not happen
* with ndiswrapper / windows driver ) . */
2009-11-22 21:16:47 +03:00
if ( priv - > param_workaround_interval > 0 & & priv - > last_qual < = 25 ) {
2008-01-26 01:51:51 +03:00
/* Decrease stats worker interval to catch stalls.
* faster . Faster than 400 - 500 ms causes packet loss ,
* Slower doesn ' t catch stalls fast enough .
*/
j = msecs_to_jiffies ( priv - > param_workaround_interval ) ;
2009-08-28 14:28:14 +04:00
if ( j > DEVICE_POLLER_JIFFIES )
j = DEVICE_POLLER_JIFFIES ;
2008-01-26 01:51:51 +03:00
else if ( j < = 0 )
j = 1 ;
update_jiffies = j ;
/* Send scan OID. Use of both OIDs is required to get device
* working .
*/
2009-02-15 09:56:56 +03:00
tmp = cpu_to_le32 ( 1 ) ;
2008-01-26 01:51:51 +03:00
rndis_set_oid ( usbdev , OID_802_11_BSSID_LIST_SCAN , & tmp ,
sizeof ( tmp ) ) ;
len = CONTROL_BUFFER_SIZE ;
buf = kmalloc ( len , GFP_KERNEL ) ;
if ( ! buf )
goto end ;
rndis_query_oid ( usbdev , OID_802_11_BSSID_LIST , buf , & len ) ;
kfree ( buf ) ;
}
2009-08-28 14:28:14 +04:00
end :
2008-01-26 01:51:51 +03:00
if ( update_jiffies > = HZ )
update_jiffies = round_jiffies_relative ( update_jiffies ) ;
else {
j = round_jiffies_relative ( update_jiffies ) ;
if ( abs ( j - update_jiffies ) < = 10 )
update_jiffies = j ;
}
2009-08-28 14:28:14 +04:00
queue_delayed_work ( priv - > workqueue , & priv - > dev_poller_work ,
update_jiffies ) ;
2008-01-26 01:51:51 +03:00
}
2009-09-01 16:33:00 +04:00
/*
* driver / device initialization
*/
2009-11-30 22:50:07 +03:00
static void rndis_copy_module_params ( struct usbnet * usbdev )
2008-01-26 01:51:51 +03:00
{
2009-06-04 21:13:25 +04:00
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( usbdev ) ;
2008-01-26 01:51:51 +03:00
priv - > param_country [ 0 ] = modparam_country [ 0 ] ;
priv - > param_country [ 1 ] = modparam_country [ 1 ] ;
priv - > param_country [ 2 ] = 0 ;
priv - > param_frameburst = modparam_frameburst ;
priv - > param_afterburner = modparam_afterburner ;
priv - > param_power_save = modparam_power_save ;
priv - > param_power_output = modparam_power_output ;
priv - > param_roamtrigger = modparam_roamtrigger ;
priv - > param_roamdelta = modparam_roamdelta ;
priv - > param_country [ 0 ] = toupper ( priv - > param_country [ 0 ] ) ;
priv - > param_country [ 1 ] = toupper ( priv - > param_country [ 1 ] ) ;
/* doesn't support EU as country code, use FI instead */
if ( ! strcmp ( priv - > param_country , " EU " ) )
strcpy ( priv - > param_country , " FI " ) ;
if ( priv - > param_power_save < 0 )
priv - > param_power_save = 0 ;
else if ( priv - > param_power_save > 2 )
priv - > param_power_save = 2 ;
2008-05-27 12:15:08 +04:00
if ( priv - > param_power_output < 0 )
priv - > param_power_output = 0 ;
else if ( priv - > param_power_output > 3 )
priv - > param_power_output = 3 ;
2008-01-26 01:51:51 +03:00
if ( priv - > param_roamtrigger < - 80 )
priv - > param_roamtrigger = - 80 ;
else if ( priv - > param_roamtrigger > - 60 )
priv - > param_roamtrigger = - 60 ;
if ( priv - > param_roamdelta < 0 )
priv - > param_roamdelta = 0 ;
else if ( priv - > param_roamdelta > 2 )
priv - > param_roamdelta = 2 ;
2008-04-24 00:10:29 +04:00
if ( modparam_workaround_interval < 0 )
2008-01-26 01:51:51 +03:00
priv - > param_workaround_interval = 500 ;
2008-04-24 00:10:29 +04:00
else
priv - > param_workaround_interval = modparam_workaround_interval ;
2009-11-30 22:50:07 +03:00
}
static int bcm4320a_early_init ( struct usbnet * usbdev )
{
2009-11-30 22:50:13 +03:00
/* copy module parameters for bcm4320a so that iwconfig reports txpower
* and workaround parameter is copied to private structure correctly .
*/
rndis_copy_module_params ( usbdev ) ;
2009-11-30 22:50:07 +03:00
/* bcm4320a doesn't handle configuration parameters well. Try
* set any and you get partially zeroed mac and broken device .
*/
return 0 ;
}
static int bcm4320b_early_init ( struct usbnet * usbdev )
{
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( usbdev ) ;
char buf [ 8 ] ;
rndis_copy_module_params ( usbdev ) ;
/* Early initialization settings, setting these won't have effect
* if called after generic_rndis_bind ( ) .
*/
2008-01-26 01:51:51 +03:00
2008-06-02 19:35:44 +04:00
rndis_set_config_parameter_str ( usbdev , " Country " , priv - > param_country ) ;
rndis_set_config_parameter_str ( usbdev , " FrameBursting " ,
2008-01-26 01:51:51 +03:00
priv - > param_frameburst ? " 1 " : " 0 " ) ;
2008-06-02 19:35:44 +04:00
rndis_set_config_parameter_str ( usbdev , " Afterburner " ,
2008-01-26 01:51:51 +03:00
priv - > param_afterburner ? " 1 " : " 0 " ) ;
sprintf ( buf , " %d " , priv - > param_power_save ) ;
2008-06-02 19:35:44 +04:00
rndis_set_config_parameter_str ( usbdev , " PowerSaveMode " , buf ) ;
2008-01-26 01:51:51 +03:00
sprintf ( buf , " %d " , priv - > param_power_output ) ;
2008-06-02 19:35:44 +04:00
rndis_set_config_parameter_str ( usbdev , " PwrOut " , buf ) ;
2008-01-26 01:51:51 +03:00
sprintf ( buf , " %d " , priv - > param_roamtrigger ) ;
2008-06-02 19:35:44 +04:00
rndis_set_config_parameter_str ( usbdev , " RoamTrigger " , buf ) ;
2008-01-26 01:51:51 +03:00
sprintf ( buf , " %d " , priv - > param_roamdelta ) ;
2008-06-02 19:35:44 +04:00
rndis_set_config_parameter_str ( usbdev , " RoamDelta " , buf ) ;
2008-01-26 01:51:51 +03:00
return 0 ;
}
2009-03-25 10:03:16 +03:00
/* same as rndis_netdev_ops but with local multicast handler */
2009-06-04 21:13:25 +04:00
static const struct net_device_ops rndis_wlan_netdev_ops = {
2009-03-25 10:03:16 +03:00
. ndo_open = usbnet_open ,
. ndo_stop = usbnet_stop ,
. ndo_start_xmit = usbnet_start_xmit ,
. ndo_tx_timeout = usbnet_tx_timeout ,
. ndo_set_mac_address = eth_mac_addr ,
. ndo_validate_addr = eth_validate_addr ,
2009-06-04 21:13:25 +04:00
. ndo_set_multicast_list = rndis_wlan_set_multicast_list ,
2009-03-25 10:03:16 +03:00
} ;
2009-06-04 21:13:25 +04:00
static int rndis_wlan_bind ( struct usbnet * usbdev , struct usb_interface * intf )
2008-01-26 01:51:51 +03:00
{
2009-03-27 00:39:53 +03:00
struct wiphy * wiphy ;
2009-06-04 21:13:25 +04:00
struct rndis_wlan_private * priv ;
2008-01-26 01:51:51 +03:00
int retval , len ;
__le32 tmp ;
2009-03-27 00:39:53 +03:00
/* allocate wiphy and rndis private data
* NOTE : We only support a single virtual interface , so wiphy
* and wireless_dev are somewhat synonymous for this device .
*/
2009-06-04 21:13:25 +04:00
wiphy = wiphy_new ( & rndis_config_ops , sizeof ( struct rndis_wlan_private ) ) ;
2009-03-27 00:39:53 +03:00
if ( ! wiphy )
2008-01-26 01:51:51 +03:00
return - ENOMEM ;
2009-03-27 00:39:53 +03:00
priv = wiphy_priv ( wiphy ) ;
usbdev - > net - > ieee80211_ptr = & priv - > wdev ;
priv - > wdev . wiphy = wiphy ;
priv - > wdev . iftype = NL80211_IFTYPE_STATION ;
2008-01-26 01:51:51 +03:00
/* These have to be initialized before calling generic_rndis_bind().
2009-06-04 21:13:25 +04:00
* Otherwise we ' ll be in big trouble in rndis_wlan_early_init ( ) .
2008-01-26 01:51:51 +03:00
*/
2008-06-02 19:35:44 +04:00
usbdev - > driver_priv = priv ;
priv - > usbdev = usbdev ;
2008-01-26 01:51:51 +03:00
mutex_init ( & priv - > command_lock ) ;
2009-04-21 20:48:07 +04:00
/* because rndis_command() sleeps we need to use workqueue */
priv - > workqueue = create_singlethread_workqueue ( " rndis_wlan " ) ;
2009-06-04 21:13:25 +04:00
INIT_WORK ( & priv - > work , rndis_wlan_worker ) ;
2009-08-28 14:28:14 +04:00
INIT_DELAYED_WORK ( & priv - > dev_poller_work , rndis_device_poller ) ;
2009-04-21 20:48:07 +04:00
INIT_DELAYED_WORK ( & priv - > scan_work , rndis_get_scan_results ) ;
2008-01-26 01:51:51 +03:00
/* try bind rndis_host */
2008-06-02 19:35:44 +04:00
retval = generic_rndis_bind ( usbdev , intf , FLAG_RNDIS_PHYM_WIRELESS ) ;
2008-01-26 01:51:51 +03:00
if ( retval < 0 )
goto fail ;
/* generic_rndis_bind set packet filter to multicast_all+
* promisc mode which doesn ' t work well for our devices ( device
* picks up rssi to closest station instead of to access point ) .
*
* rndis_host wants to avoid all OID as much as possible
2009-06-04 21:13:25 +04:00
* so do promisc / multicast handling in rndis_wlan .
2008-01-26 01:51:51 +03:00
*/
2009-06-04 21:13:25 +04:00
usbdev - > net - > netdev_ops = & rndis_wlan_netdev_ops ;
2009-03-25 10:03:16 +03:00
2008-01-26 01:51:51 +03:00
tmp = RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_BROADCAST ;
2008-06-02 19:35:44 +04:00
retval = rndis_set_oid ( usbdev , OID_GEN_CURRENT_PACKET_FILTER , & tmp ,
2008-01-26 01:51:51 +03:00
sizeof ( tmp ) ) ;
len = sizeof ( tmp ) ;
2008-06-02 19:35:44 +04:00
retval = rndis_query_oid ( usbdev , OID_802_3_MAXIMUM_LIST_SIZE , & tmp ,
& len ) ;
2008-01-26 01:51:51 +03:00
priv - > multicast_size = le32_to_cpu ( tmp ) ;
if ( retval < 0 | | priv - > multicast_size < 0 )
priv - > multicast_size = 0 ;
if ( priv - > multicast_size > 0 )
2008-06-02 19:35:44 +04:00
usbdev - > net - > flags | = IFF_MULTICAST ;
2008-01-26 01:51:51 +03:00
else
2008-06-02 19:35:44 +04:00
usbdev - > net - > flags & = ~ IFF_MULTICAST ;
2008-01-26 01:51:51 +03:00
2009-03-27 00:39:53 +03:00
/* fill-out wiphy structure and register w/ cfg80211 */
memcpy ( wiphy - > perm_addr , usbdev - > net - > dev_addr , ETH_ALEN ) ;
wiphy - > privid = rndis_wiphy_privid ;
wiphy - > interface_modes = BIT ( NL80211_IFTYPE_STATION )
| BIT ( NL80211_IFTYPE_ADHOC ) ;
wiphy - > max_scan_ssids = 1 ;
2009-08-26 16:53:02 +04:00
/* TODO: fill-out band/encr information based on priv->caps */
2009-06-04 21:13:25 +04:00
rndis_wlan_get_caps ( usbdev ) ;
2009-03-27 00:39:53 +03:00
memcpy ( priv - > channels , rndis_channels , sizeof ( rndis_channels ) ) ;
memcpy ( priv - > rates , rndis_rates , sizeof ( rndis_rates ) ) ;
priv - > band . channels = priv - > channels ;
priv - > band . n_channels = ARRAY_SIZE ( rndis_channels ) ;
priv - > band . bitrates = priv - > rates ;
priv - > band . n_bitrates = ARRAY_SIZE ( rndis_rates ) ;
wiphy - > bands [ IEEE80211_BAND_2GHZ ] = & priv - > band ;
2009-03-27 00:40:23 +03:00
wiphy - > signal_type = CFG80211_SIGNAL_TYPE_UNSPEC ;
2009-03-27 00:39:53 +03:00
2009-08-26 16:53:02 +04:00
memcpy ( priv - > cipher_suites , rndis_cipher_suites ,
sizeof ( rndis_cipher_suites ) ) ;
wiphy - > cipher_suites = priv - > cipher_suites ;
wiphy - > n_cipher_suites = ARRAY_SIZE ( rndis_cipher_suites ) ;
2009-03-27 00:39:53 +03:00
set_wiphy_dev ( wiphy , & usbdev - > udev - > dev ) ;
if ( wiphy_register ( wiphy ) ) {
2009-04-21 20:48:15 +04:00
retval = - ENODEV ;
goto fail ;
2009-03-27 00:39:53 +03:00
}
2008-06-02 19:35:44 +04:00
set_default_iw_params ( usbdev ) ;
2008-01-26 01:51:51 +03:00
2009-06-16 18:17:21 +04:00
/* set default rts/frag */
rndis_set_wiphy_params ( wiphy ,
WIPHY_PARAM_FRAG_THRESHOLD | WIPHY_PARAM_RTS_THRESHOLD ) ;
2008-01-26 01:51:51 +03:00
/* turn radio on */
2009-09-01 16:32:55 +04:00
priv - > radio_on = true ;
disassociate ( usbdev , true ) ;
2008-06-02 19:35:44 +04:00
netif_carrier_off ( usbdev - > net ) ;
2008-01-26 01:51:51 +03:00
return 0 ;
fail :
2009-08-28 14:28:14 +04:00
cancel_delayed_work_sync ( & priv - > dev_poller_work ) ;
2009-04-21 20:48:07 +04:00
cancel_delayed_work_sync ( & priv - > scan_work ) ;
cancel_work_sync ( & priv - > work ) ;
flush_workqueue ( priv - > workqueue ) ;
destroy_workqueue ( priv - > workqueue ) ;
2009-04-21 20:48:15 +04:00
wiphy_free ( wiphy ) ;
2008-01-26 01:51:51 +03:00
return retval ;
}
2009-06-04 21:13:25 +04:00
static void rndis_wlan_unbind ( struct usbnet * usbdev , struct usb_interface * intf )
2008-01-26 01:51:51 +03:00
{
2009-06-04 21:13:25 +04:00
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( usbdev ) ;
2008-01-26 01:51:51 +03:00
/* turn radio off */
2009-09-01 16:32:55 +04:00
disassociate ( usbdev , false ) ;
2008-01-26 01:51:51 +03:00
2009-08-28 14:28:14 +04:00
cancel_delayed_work_sync ( & priv - > dev_poller_work ) ;
2009-03-27 00:40:31 +03:00
cancel_delayed_work_sync ( & priv - > scan_work ) ;
2008-01-26 01:51:51 +03:00
cancel_work_sync ( & priv - > work ) ;
flush_workqueue ( priv - > workqueue ) ;
destroy_workqueue ( priv - > workqueue ) ;
2008-06-02 19:35:44 +04:00
rndis_unbind ( usbdev , intf ) ;
2009-03-27 00:39:53 +03:00
wiphy_unregister ( priv - > wdev . wiphy ) ;
wiphy_free ( priv - > wdev . wiphy ) ;
2008-01-26 01:51:51 +03:00
}
2009-06-04 21:13:25 +04:00
static int rndis_wlan_reset ( struct usbnet * usbdev )
2008-01-26 01:51:51 +03:00
{
2009-07-30 20:41:26 +04:00
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( usbdev ) ;
2009-07-30 20:41:37 +04:00
int retval ;
2009-07-30 20:41:26 +04:00
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s() \n " , __func__ ) ;
2009-07-30 20:41:26 +04:00
2009-07-30 20:41:37 +04:00
retval = rndis_reset ( usbdev ) ;
if ( retval )
2010-02-17 13:30:23 +03:00
netdev_warn ( usbdev - > net , " rndis_reset failed: %d \n " , retval ) ;
2009-07-30 20:41:37 +04:00
2009-07-30 20:41:42 +04:00
/* rndis_reset cleared multicast list, so restore here.
( set_multicast_list ( ) also turns on current packet filter ) */
2009-07-30 20:41:37 +04:00
set_multicast_list ( usbdev ) ;
2009-08-28 14:28:14 +04:00
queue_delayed_work ( priv - > workqueue , & priv - > dev_poller_work ,
round_jiffies_relative ( DEVICE_POLLER_JIFFIES ) ) ;
2009-07-30 20:41:26 +04:00
2008-06-02 19:35:44 +04:00
return deauthenticate ( usbdev ) ;
2008-01-26 01:51:51 +03:00
}
2009-06-16 18:17:32 +04:00
static int rndis_wlan_stop ( struct usbnet * usbdev )
{
2009-07-30 20:41:26 +04:00
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( usbdev ) ;
int retval ;
2009-07-30 20:41:42 +04:00
__le32 filter ;
2009-07-30 20:41:26 +04:00
2010-02-17 13:30:23 +03:00
netdev_dbg ( usbdev - > net , " %s() \n " , __func__ ) ;
2009-07-30 20:41:26 +04:00
2009-09-01 16:32:55 +04:00
retval = disassociate ( usbdev , false ) ;
2009-07-30 20:41:26 +04:00
priv - > work_pending = 0 ;
2009-08-28 14:28:14 +04:00
cancel_delayed_work_sync ( & priv - > dev_poller_work ) ;
2009-07-30 20:41:26 +04:00
cancel_delayed_work_sync ( & priv - > scan_work ) ;
cancel_work_sync ( & priv - > work ) ;
flush_workqueue ( priv - > workqueue ) ;
2009-07-30 20:41:31 +04:00
if ( priv - > scan_request ) {
cfg80211_scan_done ( priv - > scan_request , true ) ;
priv - > scan_request = NULL ;
}
2009-07-30 20:41:42 +04:00
/* Set current packet filter zero to block receiving data packets from
device . */
filter = 0 ;
rndis_set_oid ( usbdev , OID_GEN_CURRENT_PACKET_FILTER , & filter ,
sizeof ( filter ) ) ;
2009-07-30 20:41:26 +04:00
return retval ;
2009-06-16 18:17:32 +04:00
}
2008-01-26 01:51:51 +03:00
static const struct driver_info bcm4320b_info = {
. description = " Wireless RNDIS device, BCM4320b based " ,
2009-07-30 20:41:20 +04:00
. flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT |
FLAG_AVOID_UNLINK_URBS ,
2009-06-04 21:13:25 +04:00
. bind = rndis_wlan_bind ,
. unbind = rndis_wlan_unbind ,
2008-01-26 01:51:51 +03:00
. status = rndis_status ,
. rx_fixup = rndis_rx_fixup ,
. tx_fixup = rndis_tx_fixup ,
2009-06-04 21:13:25 +04:00
. reset = rndis_wlan_reset ,
2009-06-16 18:17:32 +04:00
. stop = rndis_wlan_stop ,
2009-05-22 12:58:36 +04:00
. early_init = bcm4320b_early_init ,
2009-07-30 20:41:52 +04:00
. indication = rndis_wlan_indication ,
2008-01-26 01:51:51 +03:00
} ;
static const struct driver_info bcm4320a_info = {
. description = " Wireless RNDIS device, BCM4320a based " ,
2009-07-30 20:41:20 +04:00
. flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT |
FLAG_AVOID_UNLINK_URBS ,
2009-06-04 21:13:25 +04:00
. bind = rndis_wlan_bind ,
. unbind = rndis_wlan_unbind ,
2008-01-26 01:51:51 +03:00
. status = rndis_status ,
. rx_fixup = rndis_rx_fixup ,
. tx_fixup = rndis_tx_fixup ,
2009-06-04 21:13:25 +04:00
. reset = rndis_wlan_reset ,
2009-06-16 18:17:32 +04:00
. stop = rndis_wlan_stop ,
2009-05-22 12:58:36 +04:00
. early_init = bcm4320a_early_init ,
2009-07-30 20:41:52 +04:00
. indication = rndis_wlan_indication ,
2008-01-26 01:51:51 +03:00
} ;
2009-06-04 21:13:25 +04:00
static const struct driver_info rndis_wlan_info = {
2008-01-26 01:51:51 +03:00
. description = " Wireless RNDIS device " ,
2009-07-30 20:41:20 +04:00
. flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT |
FLAG_AVOID_UNLINK_URBS ,
2009-06-04 21:13:25 +04:00
. bind = rndis_wlan_bind ,
. unbind = rndis_wlan_unbind ,
2008-01-26 01:51:51 +03:00
. status = rndis_status ,
. rx_fixup = rndis_rx_fixup ,
. tx_fixup = rndis_tx_fixup ,
2009-06-04 21:13:25 +04:00
. reset = rndis_wlan_reset ,
2009-06-16 18:17:32 +04:00
. stop = rndis_wlan_stop ,
2009-05-22 12:58:36 +04:00
. early_init = bcm4320a_early_init ,
2009-07-30 20:41:52 +04:00
. indication = rndis_wlan_indication ,
2008-01-26 01:51:51 +03:00
} ;
/*-------------------------------------------------------------------------*/
static const struct usb_device_id products [ ] = {
# define RNDIS_MASTER_INTERFACE \
. bInterfaceClass = USB_CLASS_COMM , \
. bInterfaceSubClass = 2 /* ACM */ , \
. bInterfaceProtocol = 0x0ff
/* INF driver for these devices have DriverVer >= 4.xx.xx.xx and many custom
* parameters available . Chipset marked as ' BCM4320SKFBG ' in NDISwrapper - wiki .
*/
{
. match_flags = USB_DEVICE_ID_MATCH_INT_INFO
| USB_DEVICE_ID_MATCH_DEVICE ,
. idVendor = 0x0411 ,
. idProduct = 0x00bc , /* Buffalo WLI-U2-KG125S */
RNDIS_MASTER_INTERFACE ,
. driver_info = ( unsigned long ) & bcm4320b_info ,
} , {
. match_flags = USB_DEVICE_ID_MATCH_INT_INFO
| USB_DEVICE_ID_MATCH_DEVICE ,
. idVendor = 0x0baf ,
. idProduct = 0x011b , /* U.S. Robotics USR5421 */
RNDIS_MASTER_INTERFACE ,
. driver_info = ( unsigned long ) & bcm4320b_info ,
} , {
. match_flags = USB_DEVICE_ID_MATCH_INT_INFO
| USB_DEVICE_ID_MATCH_DEVICE ,
. idVendor = 0x050d ,
. idProduct = 0x011b , /* Belkin F5D7051 */
RNDIS_MASTER_INTERFACE ,
. driver_info = ( unsigned long ) & bcm4320b_info ,
} , {
. match_flags = USB_DEVICE_ID_MATCH_INT_INFO
| USB_DEVICE_ID_MATCH_DEVICE ,
. idVendor = 0x1799 , /* Belkin has two vendor ids */
. idProduct = 0x011b , /* Belkin F5D7051 */
RNDIS_MASTER_INTERFACE ,
. driver_info = ( unsigned long ) & bcm4320b_info ,
} , {
. match_flags = USB_DEVICE_ID_MATCH_INT_INFO
| USB_DEVICE_ID_MATCH_DEVICE ,
. idVendor = 0x13b1 ,
. idProduct = 0x0014 , /* Linksys WUSB54GSv2 */
RNDIS_MASTER_INTERFACE ,
. driver_info = ( unsigned long ) & bcm4320b_info ,
} , {
. match_flags = USB_DEVICE_ID_MATCH_INT_INFO
| USB_DEVICE_ID_MATCH_DEVICE ,
. idVendor = 0x13b1 ,
. idProduct = 0x0026 , /* Linksys WUSB54GSC */
RNDIS_MASTER_INTERFACE ,
. driver_info = ( unsigned long ) & bcm4320b_info ,
} , {
. match_flags = USB_DEVICE_ID_MATCH_INT_INFO
| USB_DEVICE_ID_MATCH_DEVICE ,
. idVendor = 0x0b05 ,
. idProduct = 0x1717 , /* Asus WL169gE */
RNDIS_MASTER_INTERFACE ,
. driver_info = ( unsigned long ) & bcm4320b_info ,
} , {
. match_flags = USB_DEVICE_ID_MATCH_INT_INFO
| USB_DEVICE_ID_MATCH_DEVICE ,
. idVendor = 0x0a5c ,
. idProduct = 0xd11b , /* Eminent EM4045 */
RNDIS_MASTER_INTERFACE ,
. driver_info = ( unsigned long ) & bcm4320b_info ,
} , {
. match_flags = USB_DEVICE_ID_MATCH_INT_INFO
| USB_DEVICE_ID_MATCH_DEVICE ,
. idVendor = 0x1690 ,
. idProduct = 0x0715 , /* BT Voyager 1055 */
RNDIS_MASTER_INTERFACE ,
. driver_info = ( unsigned long ) & bcm4320b_info ,
} ,
/* These devices have DriverVer < 4.xx.xx.xx and do not have any custom
* parameters available , hardware probably contain older firmware version with
* no way of updating . Chipset marked as ' BCM4320 ? ? ? ? ' in NDISwrapper - wiki .
*/
{
. match_flags = USB_DEVICE_ID_MATCH_INT_INFO
| USB_DEVICE_ID_MATCH_DEVICE ,
. idVendor = 0x13b1 ,
. idProduct = 0x000e , /* Linksys WUSB54GSv1 */
RNDIS_MASTER_INTERFACE ,
. driver_info = ( unsigned long ) & bcm4320a_info ,
} , {
. match_flags = USB_DEVICE_ID_MATCH_INT_INFO
| USB_DEVICE_ID_MATCH_DEVICE ,
. idVendor = 0x0baf ,
. idProduct = 0x0111 , /* U.S. Robotics USR5420 */
RNDIS_MASTER_INTERFACE ,
. driver_info = ( unsigned long ) & bcm4320a_info ,
} , {
. match_flags = USB_DEVICE_ID_MATCH_INT_INFO
| USB_DEVICE_ID_MATCH_DEVICE ,
. idVendor = 0x0411 ,
. idProduct = 0x004b , /* BUFFALO WLI-USB-G54 */
RNDIS_MASTER_INTERFACE ,
. driver_info = ( unsigned long ) & bcm4320a_info ,
} ,
/* Generic Wireless RNDIS devices that we don't have exact
* idVendor / idProduct / chip yet .
*/
{
/* RNDIS is MSFT's un-official variant of CDC ACM */
USB_INTERFACE_INFO ( USB_CLASS_COMM , 2 /* ACM */ , 0x0ff ) ,
2009-06-04 21:13:25 +04:00
. driver_info = ( unsigned long ) & rndis_wlan_info ,
2008-01-26 01:51:51 +03:00
} , {
/* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */
USB_INTERFACE_INFO ( USB_CLASS_MISC , 1 , 1 ) ,
2009-06-04 21:13:25 +04:00
. driver_info = ( unsigned long ) & rndis_wlan_info ,
2008-01-26 01:51:51 +03:00
} ,
{ } , // END
} ;
MODULE_DEVICE_TABLE ( usb , products ) ;
static struct usb_driver rndis_wlan_driver = {
. name = " rndis_wlan " ,
. id_table = products ,
. probe = usbnet_probe ,
. disconnect = usbnet_disconnect ,
. suspend = usbnet_suspend ,
. resume = usbnet_resume ,
} ;
static int __init rndis_wlan_init ( void )
{
return usb_register ( & rndis_wlan_driver ) ;
}
module_init ( rndis_wlan_init ) ;
static void __exit rndis_wlan_exit ( void )
{
usb_deregister ( & rndis_wlan_driver ) ;
}
module_exit ( rndis_wlan_exit ) ;
MODULE_AUTHOR ( " Bjorge Dijkstra " ) ;
MODULE_AUTHOR ( " Jussi Kivilinna " ) ;
MODULE_DESCRIPTION ( " Driver for RNDIS based USB Wireless adapters " ) ;
MODULE_LICENSE ( " GPL " ) ;