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) " ) ;
static int modparam_workaround_interval = 500 ;
module_param_named ( workaround_interval , modparam_workaround_interval ,
int , 0444 ) ;
MODULE_PARM_DESC ( workaround_interval ,
" set stall workaround interval in msecs (default: 500) " ) ;
/* various RNDIS OID defs */
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-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
} ;
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 ] ;
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
/* these have to match what is in wpa_supplicant */
2008-02-20 13:47:45 +03:00
enum wpa_alg { WPA_ALG_NONE , WPA_ALG_WEP , WPA_ALG_TKIP , WPA_ALG_CCMP } ;
enum wpa_cipher { CIPHER_NONE , CIPHER_WEP40 , CIPHER_TKIP , CIPHER_CCMP ,
CIPHER_WEP104 } ;
enum wpa_key_mgmt { KEY_MGMT_802_1X , KEY_MGMT_PSK , KEY_MGMT_NONE ,
KEY_MGMT_802_1X_NO_WPA , KEY_MGMT_WPA_NONE } ;
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
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 }
} ;
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 ;
struct delayed_work stats_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 ;
spinlock_t stats_lock ;
unsigned long work_pending ;
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 ) ] ;
2008-01-26 01:51:51 +03:00
struct iw_statistics iwstats ;
struct iw_statistics privstats ;
int caps ;
int multicast_size ;
/* module parameters */
char param_country [ 4 ] ;
int param_frameburst ;
int param_afterburner ;
int param_power_save ;
int param_power_output ;
int param_roamtrigger ;
int param_roamdelta ;
u32 param_workaround_interval ;
/* hardware state */
int radio_on ;
int infra_mode ;
2008-03-08 02:23:17 +03:00
struct ndis_80211_ssid essid ;
2008-01-26 01:51:51 +03:00
/* encryption stuff */
int encr_tx_key_index ;
char encr_keys [ 4 ] [ 32 ] ;
int encr_key_len [ 4 ] ;
2009-05-22 18:40:34 +04:00
char encr_key_wpa [ 4 ] ;
2008-01-26 01:51:51 +03:00
int wpa_version ;
int wpa_keymgmt ;
int wpa_authalg ;
int wpa_ie_len ;
u8 * wpa_ie ;
int wpa_cipher_pair ;
int wpa_cipher_group ;
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-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-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
static const unsigned char zero_bssid [ ETH_ALEN ] = { 0 , } ;
static const unsigned char ffff_bssid [ ETH_ALEN ] = { 0xff , 0xff , 0xff ,
0xff , 0xff , 0xff } ;
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: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 ;
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 ;
2008-06-18 16:40:12 +04:00
ret = rndis_command ( dev , u . header , buflen ) ;
2009-07-30 20:41:47 +04:00
if ( ret < 0 )
devdbg ( dev , " rndis_query_oid(%s): rndis_command() failed, %d "
" (%08x) " , oid_to_string ( oid ) , ret ,
le32_to_cpu ( u . get_c - > status ) ) ;
2008-01-26 01:51:51 +03:00
if ( ret = = 0 ) {
ret = le32_to_cpu ( u . get_c - > len ) ;
* len = ( * len > ret ) ? ret : * len ;
memcpy ( data , u . buf + le32_to_cpu ( u . get_c - > offset ) + 8 , * len ) ;
ret = rndis_error_status ( u . get_c - > status ) ;
2009-07-30 20:41:47 +04:00
if ( ret < 0 )
devdbg ( dev , " rndis_query_oid(%s): device returned "
" error, 0x%08x (%d) " , oid_to_string ( oid ) ,
le32_to_cpu ( u . get_c - > status ) , ret ) ;
2008-01-26 01:51:51 +03: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 ;
}
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 ) ;
2008-06-18 16:40:12 +04:00
ret = rndis_command ( dev , u . header , buflen ) ;
2009-07-30 20:41:47 +04:00
if ( ret < 0 )
devdbg ( dev , " rndis_set_oid(%s): rndis_command() failed, %d "
" (%08x) " , oid_to_string ( oid ) , ret ,
le32_to_cpu ( u . set_c - > status ) ) ;
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 )
devdbg ( dev , " rndis_set_oid(%s): device returned error, "
" 0x%08x (%d) " , oid_to_string ( oid ) ,
le32_to_cpu ( u . set_c - > status ) , ret ) ;
}
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 ) ) ;
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 )
devdbg ( dev , " setting config parameter: %s, value: %s " ,
param , ( u8 * ) value ) ;
else
devdbg ( dev , " setting config parameter: %s, value: %d " ,
param , * ( u32 * ) value ) ;
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
devdbg ( dev , " info buffer (len: %d): " , info_len ) ;
for ( i = 0 ; i < info_len ; i + = 12 ) {
u32 * tmp = ( u32 * ) ( ( u8 * ) infobuf + i ) ;
devdbg ( dev , " %08X:%08X:%08X " ,
cpu_to_be32 ( tmp [ 0 ] ) ,
cpu_to_be32 ( tmp [ 1 ] ) ,
cpu_to_be32 ( tmp [ 2 ] ) ) ;
}
# endif
ret = rndis_set_oid ( dev , OID_GEN_RNDIS_CONFIG_PARAMETER ,
infobuf , info_len ) ;
if ( ret ! = 0 )
2009-04-07 12:30:24 +04:00
devdbg ( dev , " setting rndis config parameter failed, %d. " , 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 )
{
return ( rndis_set_config_parameter ( dev , param , 2 , value ) ) ;
}
/*static int rndis_set_config_parameter_u32(struct usbnet *dev,
char * param , u32 value )
{
return ( rndis_set_config_parameter ( dev , param , 0 , & value ) ) ;
} */
/*
* data conversion functions
*/
static int level_to_qual ( int level )
{
int qual = 100 * ( level - WL_NOISE ) / ( WL_SIGMAX - WL_NOISE ) ;
return qual > = 0 ? ( qual < = 100 ? qual : 100 ) : 0 ;
}
static void dsconfig_to_freq ( unsigned int dsconfig , struct iw_freq * freq )
{
freq - > e = 0 ;
freq - > i = 0 ;
freq - > flags = 0 ;
/* see comment in wireless.h above the "struct iw_freq"
* definition for an explanation of this if
* NOTE : 1000000 is due to the kHz
*/
if ( dsconfig > 1000000 ) {
freq - > m = dsconfig / 10 ;
freq - > e = 1 ;
} else
freq - > m = dsconfig ;
/* convert from kHz to Hz */
freq - > e + = 3 ;
}
static int freq_to_dsconfig ( struct iw_freq * freq , unsigned int * dsconfig )
{
if ( freq - > m < 1000 & & freq - > e = = 0 ) {
2008-12-23 17:03:38 +03:00
if ( freq - > m > = 1 & & freq - > m < = 14 )
* dsconfig = ieee80211_dsss_chan_to_freq ( freq - > m ) * 1000 ;
2008-01-26 01:51:51 +03:00
else
return - 1 ;
} else {
int i ;
* dsconfig = freq - > m ;
for ( i = freq - > e ; i > 0 ; i - - )
* dsconfig * = 10 ;
* dsconfig / = 1000 ;
}
return 0 ;
}
/*
* common functions
*/
static int
add_wep_key ( struct usbnet * usbdev , char * key , int key_len , int index ) ;
2008-03-08 02:23:17 +03:00
static int get_essid ( struct usbnet * usbdev , struct ndis_80211_ssid * ssid )
2008-01-26 01:51:51 +03:00
{
int ret , len ;
len = sizeof ( * ssid ) ;
ret = rndis_query_oid ( usbdev , OID_802_11_SSID , ssid , & len ) ;
if ( ret ! = 0 )
2008-03-08 02:23:25 +03:00
ssid - > length = 0 ;
2008-01-26 01:51:51 +03:00
# ifdef DEBUG
{
unsigned char tmp [ NDIS_802_11_LENGTH_SSID + 1 ] ;
2008-03-08 02:23:25 +03:00
memcpy ( tmp , ssid - > essid , le32_to_cpu ( ssid - > length ) ) ;
tmp [ le32_to_cpu ( ssid - > length ) ] = 0 ;
2008-01-26 01:51:51 +03:00
devdbg ( usbdev , " get_essid: '%s', ret: %d " , tmp , ret ) ;
}
# endif
return ret ;
}
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 ) ) ;
if ( ret = = 0 ) {
memcpy ( & priv - > essid , ssid , sizeof ( priv - > essid ) ) ;
priv - > radio_on = 1 ;
devdbg ( usbdev , " set_essid: radio_on = 1 " ) ;
}
return ret ;
}
static int get_bssid ( struct usbnet * usbdev , u8 bssid [ ETH_ALEN ] )
{
int ret , len ;
len = ETH_ALEN ;
ret = rndis_query_oid ( usbdev , OID_802_11_BSSID , bssid , & len ) ;
if ( ret ! = 0 )
memset ( bssid , 0 , ETH_ALEN ) ;
return ret ;
}
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
static int is_associated ( struct usbnet * usbdev )
{
u8 bssid [ ETH_ALEN ] ;
int ret ;
ret = get_bssid ( usbdev , bssid ) ;
return ( ret = = 0 & & memcmp ( bssid , zero_bssid , ETH_ALEN ) ! = 0 ) ;
}
static int disassociate ( struct usbnet * usbdev , int reset_ssid )
{
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 ) {
priv - > radio_on = 0 ;
devdbg ( usbdev , " disassociate: radio_on = 0 " ) ;
if ( reset_ssid )
msleep ( 100 ) ;
}
}
/* disassociate causes radio to be turned off; if reset_ssid
* is given , set random ssid to enable radio */
if ( reset_ssid ) {
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 ;
}
static int set_auth_mode ( struct usbnet * usbdev , int wpa_version , int authalg )
{
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 ;
devdbg ( usbdev , " set_auth_mode: wpa_version=0x%x authalg=0x%x "
" keymgmt=0x%x " , wpa_version , authalg , priv - > wpa_keymgmt ) ;
if ( wpa_version & IW_AUTH_WPA_VERSION_WPA2 ) {
if ( priv - > wpa_keymgmt & IW_AUTH_KEY_MGMT_802_1X )
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 ;
2008-01-26 01:51:51 +03:00
} else if ( wpa_version & IW_AUTH_WPA_VERSION_WPA ) {
if ( priv - > wpa_keymgmt & IW_AUTH_KEY_MGMT_802_1X )
2009-06-04 21:13:19 +04:00
auth_mode = NDIS_80211_AUTH_WPA ;
2008-01-26 01:51:51 +03:00
else if ( priv - > wpa_keymgmt & IW_AUTH_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 ;
2008-01-26 01:51:51 +03:00
} else if ( authalg & IW_AUTH_ALG_SHARED_KEY ) {
if ( authalg & IW_AUTH_ALG_OPEN_SYSTEM )
2009-06-04 21:13:19 +04:00
auth_mode = NDIS_80211_AUTH_AUTO_SWITCH ;
2008-01-26 01:51:51 +03:00
else
2009-06-04 21:13:19 +04:00
auth_mode = NDIS_80211_AUTH_SHARED ;
2008-01-26 01:51:51 +03:00
} else
2009-06-04 21:13:19 +04:00
auth_mode = NDIS_80211_AUTH_OPEN ;
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 ) {
devwarn ( usbdev , " setting auth mode failed (%08X) " , ret ) ;
return ret ;
}
priv - > wpa_version = wpa_version ;
priv - > wpa_authalg = authalg ;
return 0 ;
}
static int set_priv_filter ( struct usbnet * usbdev )
{
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 ;
devdbg ( usbdev , " set_priv_filter: wpa_version=0x%x " , priv - > wpa_version ) ;
if ( priv - > wpa_version & IW_AUTH_WPA_VERSION_WPA2 | |
priv - > wpa_version & IW_AUTH_WPA_VERSION_WPA )
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 )
{
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 encr_mode , ret ;
devdbg ( usbdev , " set_encr_mode: cipher_pair=0x%x cipher_group=0x%x " ,
pairwise ,
groupwise ) ;
if ( pairwise & IW_AUTH_CIPHER_CCMP )
2009-06-04 21:13:19 +04:00
encr_mode = NDIS_80211_ENCR_CCMP_ENABLED ;
2008-01-26 01:51:51 +03:00
else if ( pairwise & IW_AUTH_CIPHER_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 if ( pairwise &
( IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104 ) )
2009-06-04 21:13:19 +04:00
encr_mode = NDIS_80211_ENCR_WEP_ENABLED ;
2008-01-26 01:51:51 +03:00
else if ( groupwise & IW_AUTH_CIPHER_CCMP )
2009-06-04 21:13:19 +04:00
encr_mode = NDIS_80211_ENCR_CCMP_ENABLED ;
2008-01-26 01:51:51 +03:00
else if ( groupwise & IW_AUTH_CIPHER_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 ) {
devwarn ( usbdev , " setting encr mode failed (%08X) " , ret ) ;
return ret ;
}
priv - > wpa_cipher_pair = pairwise ;
priv - > wpa_cipher_group = groupwise ;
return 0 ;
}
static int set_assoc_params ( struct usbnet * usbdev )
{
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
set_auth_mode ( usbdev , priv - > wpa_version , priv - > wpa_authalg ) ;
set_priv_filter ( usbdev ) ;
set_encr_mode ( usbdev , priv - > wpa_cipher_pair , priv - > wpa_cipher_group ) ;
return 0 ;
}
static int set_infra_mode ( struct usbnet * usbdev , int mode )
{
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 ret , i ;
devdbg ( usbdev , " set_infra_mode: infra_mode=0x%x " , priv - > infra_mode ) ;
tmp = cpu_to_le32 ( mode ) ;
ret = rndis_set_oid ( usbdev , OID_802_11_INFRASTRUCTURE_MODE , & tmp ,
sizeof ( tmp ) ) ;
if ( ret ! = 0 ) {
devwarn ( usbdev , " setting infra mode failed (%08X) " , ret ) ;
return ret ;
}
/* NDIS drivers clear keys when infrastructure mode is
* changed . But Linux tools assume otherwise . So set the
* keys */
if ( priv - > wpa_keymgmt = = 0 | |
priv - > wpa_keymgmt = = IW_AUTH_KEY_MGMT_802_1X ) {
for ( i = 0 ; i < 4 ; i + + ) {
2009-05-22 18:40:34 +04:00
if ( priv - > encr_key_len [ i ] > 0 & & ! priv - > encr_key_wpa [ i ] )
2008-01-26 01:51:51 +03:00
add_wep_key ( usbdev , priv - > encr_keys [ i ] ,
priv - > encr_key_len [ i ] , i ) ;
}
}
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 ;
devdbg ( usbdev , " set_rts_threshold %i " , rts_threshold ) ;
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 ;
devdbg ( usbdev , " set_frag_threshold %i " , frag_threshold ) ;
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:25 +04:00
struct rndis_wlan_private * priv = get_rndis_wlan_priv ( usbdev ) ;
2008-01-26 01:51:51 +03:00
priv - > wpa_keymgmt = 0 ;
priv - > wpa_version = 0 ;
2009-06-04 21:13:19 +04:00
set_infra_mode ( usbdev , NDIS_80211_INFRA_INFRA ) ;
2008-01-26 01:51:51 +03:00
set_auth_mode ( usbdev , IW_AUTH_WPA_VERSION_DISABLED ,
IW_AUTH_ALG_OPEN_SYSTEM ) ;
set_priv_filter ( usbdev ) ;
set_encr_mode ( usbdev , IW_AUTH_CIPHER_NONE , IW_AUTH_CIPHER_NONE ) ;
}
static int deauthenticate ( struct usbnet * usbdev )
{
int ret ;
ret = disassociate ( usbdev , 1 ) ;
set_default_iw_params ( usbdev ) ;
return ret ;
}
/* index must be 0 - N, as per NDIS */
static int add_wep_key ( struct usbnet * usbdev , char * key , int key_len , int index )
{
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 ;
2008-01-26 01:51:51 +03:00
int ret ;
if ( key_len < = 0 | | key_len > 32 | | index < 0 | | index > = 4 )
return - EINVAL ;
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 ;
2008-01-26 01:51:51 +03:00
ret = set_encr_mode ( usbdev , IW_AUTH_CIPHER_WEP104 ,
IW_AUTH_CIPHER_NONE ) ;
if ( ret )
devwarn ( usbdev , " encryption couldn't be enabled (%08X) " ,
ret ) ;
}
ret = rndis_set_oid ( usbdev , OID_802_11_ADD_WEP , & ndis_key ,
sizeof ( ndis_key ) ) ;
if ( ret ! = 0 ) {
devwarn ( usbdev , " adding encryption key %d failed (%08X) " ,
index + 1 , ret ) ;
return ret ;
}
priv - > encr_key_len [ index ] = key_len ;
2009-05-22 18:40:34 +04:00
priv - > encr_key_wpa [ index ] = 0 ;
2008-01-26 01:51:51 +03:00
memcpy ( & priv - > encr_keys [ index ] , key , key_len ) ;
return 0 ;
}
2009-05-22 18:40:27 +04:00
static int add_wpa_key ( struct usbnet * usbdev , const u8 * key , int key_len ,
int index , const struct sockaddr * addr ,
const u8 * rx_seq , int alg , int flags )
{
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 ;
int ret ;
if ( index < 0 | | index > = 4 )
return - EINVAL ;
if ( key_len > sizeof ( ndis_key . material ) | | key_len < 0 )
return - EINVAL ;
2009-06-04 21:13:19 +04:00
if ( ( flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ ) & & ! rx_seq )
2009-05-22 18:40:27 +04:00
return - EINVAL ;
2009-06-04 21:13:19 +04:00
if ( ( flags & NDIS_80211_ADDKEY_PAIRWISE_KEY ) & & ! addr )
2009-05-22 18:40:27 +04:00
return - EINVAL ;
devdbg ( usbdev , " add_wpa_key(%i): flags:%i%i%i " , index ,
2009-06-04 21:13:19 +04:00
! ! ( 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 ;
if ( alg = = IW_ENCODE_ALG_TKIP & & key_len = = 32 ) {
/* 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-05-22 18:40:27 +04:00
memcpy ( ndis_key . rsc , rx_seq , 6 ) ;
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 */
memcpy ( ndis_key . bssid , addr - > sa_data , ETH_ALEN ) ;
} 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 ) ) ;
devdbg ( usbdev , " add_wpa_key: OID_802_11_ADD_KEY -> %08X " , ret ) ;
if ( ret ! = 0 )
return ret ;
priv - > encr_key_len [ index ] = key_len ;
2009-05-22 18:40:34 +04:00
priv - > encr_key_wpa [ index ] = 1 ;
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 ;
}
2008-01-26 01:51:51 +03:00
/* remove_key is for both wep and wpa */
static int remove_key ( struct usbnet * usbdev , int index , u8 bssid [ ETH_ALEN ] )
{
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 ;
int ret ;
if ( priv - > encr_key_len [ index ] = = 0 )
return 0 ;
priv - > encr_key_len [ index ] = 0 ;
2009-05-22 18:40:34 +04:00
priv - > encr_key_wpa [ index ] = 0 ;
2008-01-26 01:51:51 +03:00
memset ( & priv - > encr_keys [ index ] , 0 , sizeof ( priv - > encr_keys [ index ] ) ) ;
if ( priv - > wpa_cipher_pair = = IW_AUTH_CIPHER_TKIP | |
priv - > wpa_cipher_pair = = IW_AUTH_CIPHER_CCMP | |
priv - > wpa_cipher_group = = IW_AUTH_CIPHER_TKIP | |
priv - > wpa_cipher_group = = IW_AUTH_CIPHER_CCMP ) {
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 */
if ( memcmp ( bssid , ffff_bssid , ETH_ALEN ) ! = 0 )
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 ) {
devwarn ( usbdev ,
" removing encryption key %d failed (%08X) " ,
index , ret ) ;
return ret ;
}
}
/* if it is transmit key, disable encryption */
if ( index = = priv - > encr_tx_key_index )
set_encr_mode ( usbdev , IW_AUTH_CIPHER_NONE , IW_AUTH_CIPHER_NONE ) ;
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 ;
if ( usbdev - > net - > flags & IFF_PROMISC ) {
filter | = RNDIS_PACKET_TYPE_PROMISCUOUS |
RNDIS_PACKET_TYPE_ALL_LOCAL ;
} else if ( usbdev - > net - > flags & IFF_ALLMULTI | |
usbdev - > net - > mc_count > priv - > multicast_size ) {
filter | = RNDIS_PACKET_TYPE_ALL_MULTICAST ;
} else if ( usbdev - > net - > mc_count > 0 ) {
size = min ( priv - > multicast_size , usbdev - > net - > mc_count ) ;
buf = kmalloc ( size * ETH_ALEN , GFP_KERNEL ) ;
if ( ! buf ) {
devwarn ( usbdev ,
" couldn't alloc %d bytes of memory " ,
size * ETH_ALEN ) ;
return ;
}
mclist = usbdev - > net - > mc_list ;
for ( i = 0 ; i < size & & mclist ; mclist = mclist - > next ) {
if ( mclist - > dmi_addrlen ! = ETH_ALEN )
continue ;
memcpy ( buf + i * ETH_ALEN , mclist - > dmi_addr , ETH_ALEN ) ;
i + + ;
}
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 ;
devdbg ( usbdev , " OID_802_3_MULTICAST_LIST(%d, max: %d) -> %d " ,
i , priv - > multicast_size , ret ) ;
kfree ( buf ) ;
}
ret = rndis_set_oid ( usbdev , OID_GEN_CURRENT_PACKET_FILTER , & filter ,
sizeof ( filter ) ) ;
if ( ret < 0 ) {
devwarn ( usbdev , " couldn't set packet filter: %08x " ,
le32_to_cpu ( filter ) ) ;
}
devdbg ( usbdev , " OID_GEN_CURRENT_PACKET_FILTER(%08x) -> %d " ,
le32_to_cpu ( filter ) , ret ) ;
}
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-06-09 23:04:43 +04:00
struct usbnet * usbdev = netdev_priv ( dev ) ;
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 ;
}
return set_infra_mode ( usbdev , mode ) ;
}
2009-03-27 00:40:31 +03:00
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 ;
devdbg ( usbdev , " rndis_set_tx_power type:0x%x dbm:%i " , type , dbm ) ;
/* 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 )
disassociate ( usbdev , 1 ) ; /* turn on radio */
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 ) ;
devdbg ( usbdev , " rndis_get_tx_power dbm:%i " , * dbm ) ;
return 0 ;
}
2009-03-27 00:40:31 +03:00
# define SCAN_DELAY_JIFFIES (HZ)
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 ;
devdbg ( usbdev , " cfg80211.scan " ) ;
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 ;
/* 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 ;
devdbg ( usbdev , " check_bssid_list " ) ;
len = CONTROL_BUFFER_SIZE ;
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 ;
bssid_list = buf ;
bssid = bssid_list - > bssid ;
bssid_len = le32_to_cpu ( bssid - > length ) ;
count = le32_to_cpu ( bssid_list - > num_items ) ;
devdbg ( usbdev , " check_bssid_list: %d BSSIDs found " , count ) ;
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 ;
devdbg ( usbdev , " get_scan_results " ) ;
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 ;
}
2008-01-26 01:51:51 +03:00
/*
* wireless extension handlers
*/
static int rndis_iw_commit ( struct net_device * dev ,
struct iw_request_info * info , union iwreq_data * wrqu , char * extra )
{
/* dummy op */
return 0 ;
}
static int rndis_iw_set_essid ( struct net_device * dev ,
struct iw_request_info * info , union iwreq_data * wrqu , char * essid )
{
2008-03-08 02:23:17 +03:00
struct ndis_80211_ssid ssid ;
2008-01-26 01:51:51 +03:00
int length = wrqu - > essid . length ;
2008-11-13 10:39:10 +03:00
struct usbnet * usbdev = netdev_priv ( dev ) ;
2008-01-26 01:51:51 +03:00
devdbg ( usbdev , " SIOCSIWESSID: [flags:%d,len:%d] '%.32s' " ,
wrqu - > essid . flags , wrqu - > essid . length , essid ) ;
if ( length > NDIS_802_11_LENGTH_SSID )
length = NDIS_802_11_LENGTH_SSID ;
2008-03-08 02:23:25 +03:00
ssid . length = cpu_to_le32 ( length ) ;
2008-01-26 01:51:51 +03:00
if ( length > 0 )
2008-03-08 02:23:25 +03:00
memcpy ( ssid . essid , essid , length ) ;
2008-01-26 01:51:51 +03:00
else
2008-03-08 02:23:25 +03:00
memset ( ssid . essid , 0 , NDIS_802_11_LENGTH_SSID ) ;
2008-01-26 01:51:51 +03:00
set_assoc_params ( usbdev ) ;
if ( ! wrqu - > essid . flags | | length = = 0 )
return disassociate ( usbdev , 1 ) ;
else
return set_essid ( usbdev , & ssid ) ;
}
static int rndis_iw_get_essid ( struct net_device * dev ,
struct iw_request_info * info , union iwreq_data * wrqu , char * essid )
{
2008-03-08 02:23:17 +03:00
struct ndis_80211_ssid ssid ;
2008-11-13 10:39:10 +03:00
struct usbnet * usbdev = netdev_priv ( dev ) ;
2008-01-26 01:51:51 +03:00
int ret ;
ret = get_essid ( usbdev , & ssid ) ;
2008-03-08 02:23:25 +03:00
if ( ret = = 0 & & le32_to_cpu ( ssid . length ) > 0 ) {
2008-01-26 01:51:51 +03:00
wrqu - > essid . flags = 1 ;
2008-03-08 02:23:25 +03:00
wrqu - > essid . length = le32_to_cpu ( ssid . length ) ;
memcpy ( essid , ssid . essid , wrqu - > essid . length ) ;
2008-01-26 01:51:51 +03:00
essid [ wrqu - > essid . length ] = 0 ;
} else {
memset ( essid , 0 , sizeof ( NDIS_802_11_LENGTH_SSID ) ) ;
wrqu - > essid . flags = 0 ;
wrqu - > essid . length = 0 ;
}
devdbg ( usbdev , " SIOCGIWESSID: %s " , essid ) ;
return ret ;
}
static int rndis_iw_get_bssid ( struct net_device * dev ,
struct iw_request_info * info , union iwreq_data * wrqu , char * extra )
{
2008-11-13 10:39:10 +03:00
struct usbnet * usbdev = netdev_priv ( dev ) ;
2008-01-26 01:51:51 +03:00
unsigned char bssid [ ETH_ALEN ] ;
int ret ;
ret = get_bssid ( usbdev , bssid ) ;
if ( ret = = 0 )
2008-10-28 01:59:26 +03:00
devdbg ( usbdev , " SIOCGIWAP: %pM " , bssid ) ;
2008-01-26 01:51:51 +03:00
else
devdbg ( usbdev , " SIOCGIWAP: <not associated> " ) ;
wrqu - > ap_addr . sa_family = ARPHRD_ETHER ;
memcpy ( wrqu - > ap_addr . sa_data , bssid , ETH_ALEN ) ;
return ret ;
}
static int rndis_iw_set_bssid ( struct net_device * dev ,
struct iw_request_info * info , union iwreq_data * wrqu , char * extra )
{
2008-11-13 10:39:10 +03:00
struct usbnet * usbdev = netdev_priv ( dev ) ;
2008-01-26 01:51:51 +03:00
u8 * bssid = ( u8 * ) wrqu - > ap_addr . sa_data ;
int ret ;
2008-10-28 01:59:26 +03:00
devdbg ( usbdev , " SIOCSIWAP: %pM " , bssid ) ;
2008-01-26 01:51:51 +03:00
ret = rndis_set_oid ( usbdev , OID_802_11_BSSID , bssid , ETH_ALEN ) ;
/* user apps may set ap's mac address, which is not required;
* they may fail to work if this function fails , so return
* success */
if ( ret )
devwarn ( usbdev , " setting AP mac address failed (%08X) " , ret ) ;
return 0 ;
}
static int rndis_iw_set_auth ( struct net_device * dev ,
struct iw_request_info * info , union iwreq_data * wrqu , char * extra )
{
struct iw_param * p = & wrqu - > param ;
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
int ret = - ENOTSUPP ;
switch ( p - > flags & IW_AUTH_INDEX ) {
case IW_AUTH_WPA_VERSION :
devdbg ( usbdev , " SIOCSIWAUTH: WPA_VERSION, %08x " , p - > value ) ;
priv - > wpa_version = p - > value ;
ret = 0 ;
break ;
case IW_AUTH_CIPHER_PAIRWISE :
devdbg ( usbdev , " SIOCSIWAUTH: CIPHER_PAIRWISE, %08x " , p - > value ) ;
priv - > wpa_cipher_pair = p - > value ;
ret = 0 ;
break ;
case IW_AUTH_CIPHER_GROUP :
devdbg ( usbdev , " SIOCSIWAUTH: CIPHER_GROUP, %08x " , p - > value ) ;
priv - > wpa_cipher_group = p - > value ;
ret = 0 ;
break ;
case IW_AUTH_KEY_MGMT :
devdbg ( usbdev , " SIOCSIWAUTH: KEY_MGMT, %08x " , p - > value ) ;
priv - > wpa_keymgmt = p - > value ;
ret = 0 ;
break ;
case IW_AUTH_TKIP_COUNTERMEASURES :
devdbg ( usbdev , " SIOCSIWAUTH: TKIP_COUNTERMEASURES, %08x " ,
p - > value ) ;
ret = 0 ;
break ;
case IW_AUTH_DROP_UNENCRYPTED :
devdbg ( usbdev , " SIOCSIWAUTH: DROP_UNENCRYPTED, %08x " , p - > value ) ;
ret = 0 ;
break ;
case IW_AUTH_80211_AUTH_ALG :
devdbg ( usbdev , " SIOCSIWAUTH: 80211_AUTH_ALG, %08x " , p - > value ) ;
priv - > wpa_authalg = p - > value ;
ret = 0 ;
break ;
case IW_AUTH_WPA_ENABLED :
devdbg ( usbdev , " SIOCSIWAUTH: WPA_ENABLED, %08x " , p - > value ) ;
if ( wrqu - > param . value )
deauthenticate ( usbdev ) ;
ret = 0 ;
break ;
case IW_AUTH_RX_UNENCRYPTED_EAPOL :
devdbg ( usbdev , " SIOCSIWAUTH: RX_UNENCRYPTED_EAPOL, %08x " ,
p - > value ) ;
ret = 0 ;
break ;
case IW_AUTH_ROAMING_CONTROL :
devdbg ( usbdev , " SIOCSIWAUTH: ROAMING_CONTROL, %08x " , p - > value ) ;
ret = 0 ;
break ;
case IW_AUTH_PRIVACY_INVOKED :
devdbg ( usbdev , " SIOCSIWAUTH: invalid cmd %d " ,
wrqu - > param . flags & IW_AUTH_INDEX ) ;
return - EOPNOTSUPP ;
default :
devdbg ( usbdev , " SIOCSIWAUTH: UNKNOWN %08x, %08x " ,
p - > flags & IW_AUTH_INDEX , p - > value ) ;
}
return ret ;
}
static int rndis_iw_get_auth ( struct net_device * dev ,
struct iw_request_info * info , union iwreq_data * wrqu , char * extra )
{
struct iw_param * p = & wrqu - > param ;
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
switch ( p - > flags & IW_AUTH_INDEX ) {
case IW_AUTH_WPA_VERSION :
p - > value = priv - > wpa_version ;
break ;
case IW_AUTH_CIPHER_PAIRWISE :
p - > value = priv - > wpa_cipher_pair ;
break ;
case IW_AUTH_CIPHER_GROUP :
p - > value = priv - > wpa_cipher_group ;
break ;
case IW_AUTH_KEY_MGMT :
p - > value = priv - > wpa_keymgmt ;
break ;
case IW_AUTH_80211_AUTH_ALG :
p - > value = priv - > wpa_authalg ;
break ;
default :
devdbg ( usbdev , " SIOCGIWAUTH: invalid cmd %d " ,
wrqu - > param . flags & IW_AUTH_INDEX ) ;
return - EOPNOTSUPP ;
}
return 0 ;
}
static int rndis_iw_set_encode ( struct net_device * dev ,
struct iw_request_info * info , union iwreq_data * wrqu , char * extra )
{
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
int ret , index , key_len ;
u8 * key ;
index = ( wrqu - > encoding . flags & IW_ENCODE_INDEX ) ;
/* iwconfig gives index as 1 - N */
if ( index > 0 )
index - - ;
else
index = priv - > encr_tx_key_index ;
if ( index < 0 | | index > = 4 ) {
devwarn ( usbdev , " encryption index out of range (%u) " , index ) ;
return - EINVAL ;
}
/* remove key if disabled */
if ( wrqu - > data . flags & IW_ENCODE_DISABLED ) {
if ( remove_key ( usbdev , index , NULL ) )
return - EINVAL ;
else
return 0 ;
}
/* global encryption state (for all keys) */
if ( wrqu - > data . flags & IW_ENCODE_OPEN )
ret = set_auth_mode ( usbdev , IW_AUTH_WPA_VERSION_DISABLED ,
IW_AUTH_ALG_OPEN_SYSTEM ) ;
else /*if (wrqu->data.flags & IW_ENCODE_RESTRICTED)*/
ret = set_auth_mode ( usbdev , IW_AUTH_WPA_VERSION_DISABLED ,
IW_AUTH_ALG_SHARED_KEY ) ;
if ( ret ! = 0 )
return ret ;
if ( wrqu - > data . length > 0 ) {
key_len = wrqu - > data . length ;
key = extra ;
} else {
/* must be set as tx key */
if ( priv - > encr_key_len [ index ] = = 0 )
return - EINVAL ;
key_len = priv - > encr_key_len [ index ] ;
key = priv - > encr_keys [ index ] ;
priv - > encr_tx_key_index = index ;
}
if ( add_wep_key ( usbdev , key , key_len , index ) ! = 0 )
return - EINVAL ;
if ( index = = priv - > encr_tx_key_index )
/* ndis drivers want essid to be set after setting encr */
set_essid ( usbdev , & priv - > essid ) ;
return 0 ;
}
static int rndis_iw_set_encode_ext ( struct net_device * dev ,
struct iw_request_info * info , union iwreq_data * wrqu , char * extra )
{
struct iw_encode_ext * ext = ( struct iw_encode_ext * ) extra ;
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 ) ;
2009-05-22 18:40:27 +04:00
int keyidx , flags ;
2008-01-26 01:51:51 +03:00
keyidx = wrqu - > encoding . flags & IW_ENCODE_INDEX ;
/* iwconfig gives index as 1 - N */
if ( keyidx )
keyidx - - ;
else
keyidx = priv - > encr_tx_key_index ;
if ( keyidx < 0 | | keyidx > = 4 )
return - EINVAL ;
if ( ext - > alg = = WPA_ALG_WEP ) {
if ( ext - > ext_flags & IW_ENCODE_EXT_SET_TX_KEY )
priv - > encr_tx_key_index = keyidx ;
return add_wep_key ( usbdev , ext - > key , ext - > key_len , keyidx ) ;
}
if ( ( wrqu - > encoding . flags & IW_ENCODE_DISABLED ) | |
ext - > alg = = IW_ENCODE_ALG_NONE | | ext - > key_len = = 0 )
return remove_key ( usbdev , keyidx , NULL ) ;
2009-05-22 18:40:27 +04:00
flags = 0 ;
if ( ext - > ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID )
2009-06-04 21:13:19 +04:00
flags | = NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ ;
2009-05-22 18:40:27 +04:00
if ( ! ( ext - > ext_flags & IW_ENCODE_EXT_GROUP_KEY ) )
2009-06-04 21:13:19 +04:00
flags | = NDIS_80211_ADDKEY_PAIRWISE_KEY ;
2008-01-26 01:51:51 +03:00
if ( ext - > ext_flags & IW_ENCODE_EXT_SET_TX_KEY )
2009-06-04 21:13:19 +04:00
flags | = NDIS_80211_ADDKEY_TRANSMIT_KEY ;
2008-01-26 01:51:51 +03:00
2009-05-22 18:40:27 +04:00
return add_wpa_key ( usbdev , ext - > key , ext - > key_len , keyidx , & ext - > addr ,
ext - > rx_seq , ext - > alg , flags ) ;
2008-01-26 01:51:51 +03:00
}
static int rndis_iw_set_genie ( struct net_device * dev ,
struct iw_request_info * info , union iwreq_data * wrqu , char * extra )
{
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
int ret = 0 ;
# ifdef DEBUG
int j ;
u8 * gie = extra ;
for ( j = 0 ; j < wrqu - > data . length ; j + = 8 )
devdbg ( usbdev ,
" SIOCSIWGENIE %04x - "
" %02x %02x %02x %02x %02x %02x %02x %02x " , j ,
gie [ j + 0 ] , gie [ j + 1 ] , gie [ j + 2 ] , gie [ j + 3 ] ,
gie [ j + 4 ] , gie [ j + 5 ] , gie [ j + 6 ] , gie [ j + 7 ] ) ;
# endif
/* clear existing IEs */
if ( priv - > wpa_ie_len ) {
kfree ( priv - > wpa_ie ) ;
priv - > wpa_ie_len = 0 ;
}
/* set new IEs */
priv - > wpa_ie = kmalloc ( wrqu - > data . length , GFP_KERNEL ) ;
if ( priv - > wpa_ie ) {
priv - > wpa_ie_len = wrqu - > data . length ;
memcpy ( priv - > wpa_ie , extra , priv - > wpa_ie_len ) ;
} else
ret = - ENOMEM ;
return ret ;
}
static int rndis_iw_get_genie ( struct net_device * dev ,
struct iw_request_info * info , union iwreq_data * wrqu , char * extra )
{
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
devdbg ( usbdev , " SIOCGIWGENIE " ) ;
if ( priv - > wpa_ie_len = = 0 | | priv - > wpa_ie = = NULL ) {
wrqu - > data . length = 0 ;
return 0 ;
}
if ( wrqu - > data . length < priv - > wpa_ie_len )
return - E2BIG ;
wrqu - > data . length = priv - > wpa_ie_len ;
memcpy ( extra , priv - > wpa_ie , priv - > wpa_ie_len ) ;
return 0 ;
}
static int rndis_iw_set_freq ( struct net_device * dev ,
struct iw_request_info * info , union iwreq_data * wrqu , char * extra )
{
2008-11-13 10:39:10 +03:00
struct usbnet * usbdev = netdev_priv ( dev ) ;
2008-03-08 02:23:17 +03:00
struct ndis_80211_conf config ;
2008-01-26 01:51:51 +03:00
unsigned int dsconfig ;
int len , ret ;
/* this OID is valid only when not associated */
if ( is_associated ( usbdev ) )
return 0 ;
dsconfig = 0 ;
if ( freq_to_dsconfig ( & wrqu - > freq , & dsconfig ) )
return - EINVAL ;
len = sizeof ( config ) ;
ret = rndis_query_oid ( usbdev , OID_802_11_CONFIGURATION , & config , & len ) ;
if ( ret ! = 0 ) {
devdbg ( usbdev , " SIOCSIWFREQ: querying configuration failed " ) ;
return 0 ;
}
2008-03-08 02:23:25 +03:00
config . ds_config = cpu_to_le32 ( dsconfig ) ;
2008-01-26 01:51:51 +03:00
devdbg ( usbdev , " SIOCSIWFREQ: %d * 10^%d " , wrqu - > freq . m , wrqu - > freq . e ) ;
return rndis_set_oid ( usbdev , OID_802_11_CONFIGURATION , & config ,
sizeof ( config ) ) ;
}
static int rndis_iw_get_freq ( struct net_device * dev ,
struct iw_request_info * info , union iwreq_data * wrqu , char * extra )
{
2008-11-13 10:39:10 +03:00
struct usbnet * usbdev = netdev_priv ( dev ) ;
2008-03-08 02:23:17 +03:00
struct ndis_80211_conf config ;
2008-01-26 01:51:51 +03:00
int len , ret ;
len = sizeof ( config ) ;
ret = rndis_query_oid ( usbdev , OID_802_11_CONFIGURATION , & config , & len ) ;
if ( ret = = 0 )
2008-03-08 02:23:25 +03:00
dsconfig_to_freq ( le32_to_cpu ( config . ds_config ) , & wrqu - > freq ) ;
2008-01-26 01:51:51 +03:00
devdbg ( usbdev , " SIOCGIWFREQ: %d " , wrqu - > freq . m ) ;
return ret ;
}
static int rndis_iw_get_rate ( struct net_device * dev ,
struct iw_request_info * info , union iwreq_data * wrqu , char * extra )
{
2008-11-13 10:39:10 +03:00
struct usbnet * usbdev = netdev_priv ( dev ) ;
2008-01-26 01:51:51 +03:00
__le32 tmp ;
int ret , len ;
len = sizeof ( tmp ) ;
ret = rndis_query_oid ( usbdev , OID_GEN_LINK_SPEED , & tmp , & len ) ;
if ( ret = = 0 ) {
wrqu - > bitrate . value = le32_to_cpu ( tmp ) * 100 ;
wrqu - > bitrate . disabled = 0 ;
wrqu - > bitrate . flags = 1 ;
}
return ret ;
}
static int rndis_iw_set_mlme ( struct net_device * dev ,
struct iw_request_info * info , union iwreq_data * wrqu , char * extra )
{
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
struct iw_mlme * mlme = ( struct iw_mlme * ) extra ;
unsigned char bssid [ ETH_ALEN ] ;
get_bssid ( usbdev , bssid ) ;
if ( memcmp ( bssid , mlme - > addr . sa_data , ETH_ALEN ) )
return - EINVAL ;
switch ( mlme - > cmd ) {
case IW_MLME_DEAUTH :
return deauthenticate ( usbdev ) ;
case IW_MLME_DISASSOC :
return disassociate ( usbdev , priv - > radio_on ) ;
default :
return - EOPNOTSUPP ;
}
return 0 ;
}
static struct iw_statistics * rndis_get_wireless_stats ( struct net_device * dev )
{
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
unsigned long flags ;
spin_lock_irqsave ( & priv - > stats_lock , flags ) ;
memcpy ( & priv - > iwstats , & priv - > privstats , sizeof ( priv - > iwstats ) ) ;
spin_unlock_irqrestore ( & priv - > stats_lock , flags ) ;
return & priv - > iwstats ;
}
# define IW_IOCTL(x) [(x) - SIOCSIWCOMMIT]
static const iw_handler rndis_iw_handler [ ] =
{
IW_IOCTL ( SIOCSIWCOMMIT ) = rndis_iw_commit ,
2009-03-27 00:39:53 +03:00
IW_IOCTL ( SIOCGIWNAME ) = ( iw_handler ) cfg80211_wext_giwname ,
2008-01-26 01:51:51 +03:00
IW_IOCTL ( SIOCSIWFREQ ) = rndis_iw_set_freq ,
IW_IOCTL ( SIOCGIWFREQ ) = rndis_iw_get_freq ,
2009-03-27 00:40:01 +03:00
IW_IOCTL ( SIOCSIWMODE ) = ( iw_handler ) cfg80211_wext_siwmode ,
IW_IOCTL ( SIOCGIWMODE ) = ( iw_handler ) cfg80211_wext_giwmode ,
2009-03-27 00:40:23 +03:00
IW_IOCTL ( SIOCGIWRANGE ) = ( iw_handler ) cfg80211_wext_giwrange ,
2008-01-26 01:51:51 +03:00
IW_IOCTL ( SIOCSIWAP ) = rndis_iw_set_bssid ,
IW_IOCTL ( SIOCGIWAP ) = rndis_iw_get_bssid ,
2009-03-27 00:40:31 +03:00
IW_IOCTL ( SIOCSIWSCAN ) = ( iw_handler ) cfg80211_wext_siwscan ,
IW_IOCTL ( SIOCGIWSCAN ) = ( iw_handler ) cfg80211_wext_giwscan ,
2008-01-26 01:51:51 +03:00
IW_IOCTL ( SIOCSIWESSID ) = rndis_iw_set_essid ,
IW_IOCTL ( SIOCGIWESSID ) = rndis_iw_get_essid ,
IW_IOCTL ( SIOCGIWRATE ) = rndis_iw_get_rate ,
2009-06-16 18:17:21 +04:00
IW_IOCTL ( SIOCSIWRTS ) = ( iw_handler ) cfg80211_wext_siwrts ,
IW_IOCTL ( SIOCGIWRTS ) = ( iw_handler ) cfg80211_wext_giwrts ,
IW_IOCTL ( SIOCSIWFRAG ) = ( iw_handler ) cfg80211_wext_siwfrag ,
IW_IOCTL ( SIOCGIWFRAG ) = ( iw_handler ) cfg80211_wext_giwfrag ,
2009-06-16 18:17:32 +04:00
IW_IOCTL ( SIOCSIWTXPOW ) = ( iw_handler ) cfg80211_wext_siwtxpower ,
IW_IOCTL ( SIOCGIWTXPOW ) = ( iw_handler ) cfg80211_wext_giwtxpower ,
2008-01-26 01:51:51 +03:00
IW_IOCTL ( SIOCSIWENCODE ) = rndis_iw_set_encode ,
IW_IOCTL ( SIOCSIWENCODEEXT ) = rndis_iw_set_encode_ext ,
IW_IOCTL ( SIOCSIWAUTH ) = rndis_iw_set_auth ,
IW_IOCTL ( SIOCGIWAUTH ) = rndis_iw_get_auth ,
IW_IOCTL ( SIOCSIWGENIE ) = rndis_iw_set_genie ,
IW_IOCTL ( SIOCGIWGENIE ) = rndis_iw_get_genie ,
IW_IOCTL ( SIOCSIWMLME ) = rndis_iw_set_mlme ,
} ;
2009-06-04 21:13:25 +04:00
static const iw_handler rndis_wlan_private_handler [ ] = {
2008-01-26 01:51:51 +03:00
} ;
2009-06-04 21:13:25 +04:00
static const struct iw_priv_args rndis_wlan_private_args [ ] = {
2008-01-26 01:51:51 +03:00
} ;
static const struct iw_handler_def rndis_iw_handlers = {
. num_standard = ARRAY_SIZE ( rndis_iw_handler ) ,
2009-06-04 21:13:25 +04:00
. num_private = ARRAY_SIZE ( rndis_wlan_private_handler ) ,
. num_private_args = ARRAY_SIZE ( rndis_wlan_private_args ) ,
2008-01-26 01:51:51 +03:00
. standard = ( iw_handler * ) rndis_iw_handler ,
2009-06-04 21:13:25 +04:00
. private = ( iw_handler * ) rndis_wlan_private_handler ,
. private_args = ( struct iw_priv_args * ) rndis_wlan_private_args ,
2008-01-26 01:51:51 +03:00
. get_wireless_stats = rndis_get_wireless_stats ,
} ;
2009-06-04 21:13:25 +04:00
static void rndis_wlan_worker ( 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 =
container_of ( work , struct rndis_wlan_private , work ) ;
2008-01-26 01:51:51 +03:00
struct usbnet * usbdev = priv - > usbdev ;
union iwreq_data evt ;
unsigned char bssid [ ETH_ALEN ] ;
2008-05-27 01:06:15 +04:00
struct ndis_80211_assoc_info * info ;
int assoc_size = sizeof ( * info ) + IW_CUSTOM_MAX + 32 ;
int ret , offset ;
2008-01-26 01:51:51 +03:00
2008-06-02 19:35:21 +04:00
if ( test_and_clear_bit ( WORK_LINK_UP , & priv - > work_pending ) ) {
2008-06-02 19:35:29 +04:00
netif_carrier_on ( usbdev - > net ) ;
2008-05-27 01:06:15 +04:00
info = kzalloc ( assoc_size , GFP_KERNEL ) ;
if ( ! info )
goto get_bssid ;
2008-01-26 01:51:51 +03:00
2008-05-27 01:06:15 +04:00
/* Get association info IEs from device and send them back to
* userspace . */
ret = get_association_info ( usbdev , info , assoc_size ) ;
if ( ! ret ) {
evt . data . length = le32_to_cpu ( info - > req_ie_length ) ;
if ( evt . data . length > 0 ) {
offset = le32_to_cpu ( info - > offset_req_ies ) ;
wireless_send_event ( usbdev - > net ,
IWEVASSOCREQIE , & evt ,
( char * ) info + offset ) ;
}
evt . data . length = le32_to_cpu ( info - > resp_ie_length ) ;
if ( evt . data . length > 0 ) {
offset = le32_to_cpu ( info - > offset_resp_ies ) ;
wireless_send_event ( usbdev - > net ,
IWEVASSOCRESPIE , & evt ,
( char * ) info + offset ) ;
}
}
kfree ( info ) ;
2008-01-26 01:51:51 +03:00
2008-05-27 01:06:15 +04:00
get_bssid :
ret = get_bssid ( usbdev , bssid ) ;
2008-01-26 01:51:51 +03:00
if ( ! ret ) {
evt . data . flags = 0 ;
evt . data . length = 0 ;
memcpy ( evt . ap_addr . sa_data , bssid , ETH_ALEN ) ;
wireless_send_event ( usbdev - > net , SIOCGIWAP , & evt , NULL ) ;
}
}
2008-06-02 19:35:21 +04:00
if ( test_and_clear_bit ( WORK_LINK_DOWN , & priv - > work_pending ) ) {
2008-06-02 19:35:29 +04:00
netif_carrier_off ( usbdev - > net ) ;
2008-06-02 19:35:21 +04:00
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 ) ;
}
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: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
2008-06-02 19:35:21 +04:00
/* queue work to avoid recursive calls into rndis_command */
2009-07-30 20:41:52 +04:00
switch ( msg - > status ) {
case RNDIS_STATUS_MEDIA_CONNECT :
devinfo ( usbdev , " media connect " ) ;
set_bit ( WORK_LINK_UP , & priv - > work_pending ) ;
queue_work ( priv - > workqueue , & priv - > work ) ;
break ;
case RNDIS_STATUS_MEDIA_DISCONNECT :
devinfo ( usbdev , " media disconnect " ) ;
set_bit ( WORK_LINK_DOWN , & priv - > work_pending ) ;
queue_work ( priv - > workqueue , & priv - > work ) ;
break ;
default :
devinfo ( usbdev , " indication: 0x%08x " ,
le32_to_cpu ( msg - > status ) ) ;
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 ;
}
# define STATS_UPDATE_JIFFIES (HZ)
static void rndis_update_wireless_stats ( struct work_struct * work )
{
2009-06-04 21:13:25 +04:00
struct rndis_wlan_private * priv =
container_of ( work , struct rndis_wlan_private , stats_work . work ) ;
2008-01-26 01:51:51 +03:00
struct usbnet * usbdev = priv - > usbdev ;
struct iw_statistics iwstats ;
__le32 rssi , tmp ;
2008-02-06 16:36:10 +03:00
int len , ret , j ;
2008-01-26 01:51:51 +03:00
unsigned long flags ;
int update_jiffies = STATS_UPDATE_JIFFIES ;
void * buf ;
spin_lock_irqsave ( & priv - > stats_lock , flags ) ;
memcpy ( & iwstats , & priv - > privstats , sizeof ( iwstats ) ) ;
spin_unlock_irqrestore ( & priv - > stats_lock , flags ) ;
/* only update stats when connected */
if ( ! is_associated ( usbdev ) ) {
iwstats . qual . qual = 0 ;
iwstats . qual . level = 0 ;
iwstats . qual . updated = IW_QUAL_QUAL_UPDATED
| IW_QUAL_LEVEL_UPDATED
| IW_QUAL_NOISE_INVALID
| IW_QUAL_QUAL_INVALID
| IW_QUAL_LEVEL_INVALID ;
goto end ;
}
len = sizeof ( rssi ) ;
ret = rndis_query_oid ( usbdev , OID_802_11_RSSI , & rssi , & len ) ;
devdbg ( usbdev , " stats: OID_802_11_RSSI -> %d, rssi:%d " , ret ,
le32_to_cpu ( rssi ) ) ;
if ( ret = = 0 ) {
memset ( & iwstats . qual , 0 , sizeof ( iwstats . qual ) ) ;
iwstats . qual . qual = level_to_qual ( le32_to_cpu ( rssi ) ) ;
2009-03-27 00:40:16 +03:00
iwstats . qual . level = level_to_qual ( le32_to_cpu ( rssi ) ) ;
2008-01-26 01:51:51 +03:00
iwstats . qual . updated = IW_QUAL_QUAL_UPDATED
| IW_QUAL_LEVEL_UPDATED
| IW_QUAL_NOISE_INVALID ;
}
memset ( & iwstats . discard , 0 , sizeof ( iwstats . discard ) ) ;
len = sizeof ( tmp ) ;
ret = rndis_query_oid ( usbdev , OID_GEN_XMIT_ERROR , & tmp , & len ) ;
if ( ret = = 0 )
iwstats . discard . misc + = le32_to_cpu ( tmp ) ;
len = sizeof ( tmp ) ;
ret = rndis_query_oid ( usbdev , OID_GEN_RCV_ERROR , & tmp , & len ) ;
if ( ret = = 0 )
iwstats . discard . misc + = le32_to_cpu ( tmp ) ;
len = sizeof ( tmp ) ;
ret = rndis_query_oid ( usbdev , OID_GEN_RCV_NO_BUFFER , & tmp , & len ) ;
if ( ret = = 0 )
iwstats . discard . misc + = le32_to_cpu ( tmp ) ;
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 ) . */
if ( iwstats . qual . 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 ) ;
if ( j > STATS_UPDATE_JIFFIES )
j = STATS_UPDATE_JIFFIES ;
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 ) ;
}
end :
spin_lock_irqsave ( & priv - > stats_lock , flags ) ;
memcpy ( & priv - > privstats , & iwstats , sizeof ( iwstats ) ) ;
spin_unlock_irqrestore ( & priv - > stats_lock , flags ) ;
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 ;
}
queue_delayed_work ( priv - > workqueue , & priv - > stats_work , update_jiffies ) ;
}
2009-05-22 12:58:36 +04:00
static int bcm4320a_early_init ( struct usbnet * usbdev )
{
/* 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 )
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
char buf [ 8 ] ;
/* Early initialization settings, setting these won't have effect
* if called after generic_rndis_bind ( ) .
*/
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 ;
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
} ;
2008-01-26 01:51:51 +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 ;
usbdev - > net - > wireless_handlers = & rndis_iw_handlers ;
priv - > usbdev = usbdev ;
2008-01-26 01:51:51 +03:00
mutex_init ( & priv - > command_lock ) ;
spin_lock_init ( & priv - > stats_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-04-21 20:48:07 +04:00
INIT_DELAYED_WORK ( & priv - > stats_work , rndis_update_wireless_stats ) ;
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
priv - > iwstats . qual . qual = 0 ;
priv - > iwstats . qual . level = 0 ;
priv - > iwstats . qual . updated = IW_QUAL_QUAL_UPDATED
| IW_QUAL_LEVEL_UPDATED
| IW_QUAL_NOISE_INVALID
| IW_QUAL_QUAL_INVALID
| IW_QUAL_LEVEL_INVALID ;
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 ;
/* TODO: fill-out band 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
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 */
priv - > radio_on = 1 ;
2008-06-02 19:35:44 +04:00
disassociate ( usbdev , 1 ) ;
netif_carrier_off ( usbdev - > net ) ;
2008-01-26 01:51:51 +03:00
return 0 ;
fail :
2009-04-21 20:48:07 +04:00
cancel_delayed_work_sync ( & priv - > stats_work ) ;
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 */
2008-06-02 19:35:44 +04:00
disassociate ( usbdev , 0 ) ;
2008-01-26 01:51:51 +03:00
cancel_delayed_work_sync ( & priv - > stats_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 ) ;
if ( priv & & priv - > wpa_ie_len )
kfree ( priv - > wpa_ie ) ;
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
2009-06-16 18:17:32 +04:00
devdbg ( usbdev , " rndis_wlan_reset " ) ;
2009-07-30 20:41:26 +04:00
2009-07-30 20:41:37 +04:00
retval = rndis_reset ( usbdev ) ;
if ( retval )
devwarn ( usbdev , " rndis_reset() failed: %d " , retval ) ;
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-07-30 20:41:26 +04:00
queue_delayed_work ( priv - > workqueue , & priv - > stats_work ,
round_jiffies_relative ( STATS_UPDATE_JIFFIES ) ) ;
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
2009-06-16 18:17:32 +04:00
devdbg ( usbdev , " rndis_wlan_stop " ) ;
2009-07-30 20:41:26 +04:00
retval = disassociate ( usbdev , 0 ) ;
priv - > work_pending = 0 ;
cancel_delayed_work_sync ( & priv - > stats_work ) ;
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 " ) ;