2008-02-07 13:58:57 +03:00
/*
* PS3 gelic network driver .
*
* Copyright ( C ) 2007 Sony Computer Entertainment Inc .
* Copyright 2007 Sony Corporation
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation .
*
* 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 . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# undef DEBUG
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/etherdevice.h>
# include <linux/ethtool.h>
# include <linux/if_vlan.h>
# include <linux/in.h>
# include <linux/ip.h>
# include <linux/tcp.h>
# include <linux/wireless.h>
2008-10-31 00:09:54 +03:00
# include <linux/ieee80211.h>
# include <linux/if_arp.h>
2008-02-07 13:58:57 +03:00
# include <linux/ctype.h>
# include <linux/string.h>
# include <net/iw_handler.h>
# include <linux/dma-mapping.h>
# include <net/checksum.h>
# include <asm/firmware.h>
# include <asm/ps3.h>
# include <asm/lv1call.h>
# include "ps3_gelic_net.h"
# include "ps3_gelic_wireless.h"
2008-06-02 07:49:16 +04:00
static int gelic_wl_start_scan ( struct gelic_wl_info * wl , int always_scan ,
u8 * essid , size_t essid_len ) ;
2008-02-07 13:58:57 +03:00
static int gelic_wl_try_associate ( struct net_device * netdev ) ;
/*
* tables
*/
/* 802.11b/g channel to freq in MHz */
static const int channel_freq [ ] = {
2412 , 2417 , 2422 , 2427 , 2432 ,
2437 , 2442 , 2447 , 2452 , 2457 ,
2462 , 2467 , 2472 , 2484
} ;
# define NUM_CHANNELS ARRAY_SIZE(channel_freq)
/* in bps */
static const int bitrate_list [ ] = {
1000000 ,
2000000 ,
5500000 ,
11000000 ,
6000000 ,
9000000 ,
12000000 ,
18000000 ,
24000000 ,
36000000 ,
48000000 ,
54000000
} ;
# define NUM_BITRATES ARRAY_SIZE(bitrate_list)
/*
* wpa2 support requires the hypervisor version 2.0 or later
*/
static inline int wpa2_capable ( void )
{
return ( 0 < = ps3_compare_firmware_version ( 2 , 0 , 0 ) ) ;
}
static inline int precise_ie ( void )
{
2008-03-25 10:21:08 +03:00
return ( 0 < = ps3_compare_firmware_version ( 2 , 2 , 0 ) ) ;
2008-02-07 13:58:57 +03:00
}
/*
* post_eurus_cmd helpers
*/
struct eurus_cmd_arg_info {
int pre_arg ; /* command requres arg1, arg2 at POST COMMAND */
int post_arg ; /* command requires arg1, arg2 at GET_RESULT */
} ;
static const struct eurus_cmd_arg_info cmd_info [ GELIC_EURUS_CMD_MAX_INDEX ] = {
[ GELIC_EURUS_CMD_SET_COMMON_CFG ] = { . pre_arg = 1 } ,
[ GELIC_EURUS_CMD_SET_WEP_CFG ] = { . pre_arg = 1 } ,
[ GELIC_EURUS_CMD_SET_WPA_CFG ] = { . pre_arg = 1 } ,
[ GELIC_EURUS_CMD_GET_COMMON_CFG ] = { . post_arg = 1 } ,
[ GELIC_EURUS_CMD_GET_WEP_CFG ] = { . post_arg = 1 } ,
[ GELIC_EURUS_CMD_GET_WPA_CFG ] = { . post_arg = 1 } ,
[ GELIC_EURUS_CMD_GET_RSSI_CFG ] = { . post_arg = 1 } ,
2008-06-02 07:49:16 +04:00
[ GELIC_EURUS_CMD_START_SCAN ] = { . pre_arg = 1 } ,
2008-02-07 13:58:57 +03:00
[ GELIC_EURUS_CMD_GET_SCAN ] = { . post_arg = 1 } ,
} ;
# ifdef DEBUG
static const char * cmdstr ( enum gelic_eurus_command ix )
{
switch ( ix ) {
case GELIC_EURUS_CMD_ASSOC :
return " ASSOC " ;
case GELIC_EURUS_CMD_DISASSOC :
return " DISASSOC " ;
case GELIC_EURUS_CMD_START_SCAN :
return " SCAN " ;
case GELIC_EURUS_CMD_GET_SCAN :
return " GET SCAN " ;
case GELIC_EURUS_CMD_SET_COMMON_CFG :
return " SET_COMMON_CFG " ;
case GELIC_EURUS_CMD_GET_COMMON_CFG :
return " GET_COMMON_CFG " ;
case GELIC_EURUS_CMD_SET_WEP_CFG :
return " SET_WEP_CFG " ;
case GELIC_EURUS_CMD_GET_WEP_CFG :
return " GET_WEP_CFG " ;
case GELIC_EURUS_CMD_SET_WPA_CFG :
return " SET_WPA_CFG " ;
case GELIC_EURUS_CMD_GET_WPA_CFG :
return " GET_WPA_CFG " ;
case GELIC_EURUS_CMD_GET_RSSI_CFG :
return " GET_RSSI " ;
default :
break ;
}
return " " ;
} ;
# else
static inline const char * cmdstr ( enum gelic_eurus_command ix )
{
return " " ;
}
# endif
/* synchronously do eurus commands */
static void gelic_eurus_sync_cmd_worker ( struct work_struct * work )
{
struct gelic_eurus_cmd * cmd ;
struct gelic_card * card ;
struct gelic_wl_info * wl ;
u64 arg1 , arg2 ;
pr_debug ( " %s: <- \n " , __func__ ) ;
cmd = container_of ( work , struct gelic_eurus_cmd , work ) ;
BUG_ON ( cmd_info [ cmd - > cmd ] . pre_arg & &
cmd_info [ cmd - > cmd ] . post_arg ) ;
wl = cmd - > wl ;
card = port_to_card ( wl_port ( wl ) ) ;
if ( cmd_info [ cmd - > cmd ] . pre_arg ) {
2008-06-02 07:49:16 +04:00
arg1 = ( cmd - > buffer ) ?
ps3_mm_phys_to_lpar ( __pa ( cmd - > buffer ) ) :
0 ;
2008-02-07 13:58:57 +03:00
arg2 = cmd - > buf_size ;
} else {
arg1 = 0 ;
arg2 = 0 ;
}
init_completion ( & wl - > cmd_done_intr ) ;
pr_debug ( " %s: cmd='%s' start \n " , __func__ , cmdstr ( cmd - > cmd ) ) ;
cmd - > status = lv1_net_control ( bus_id ( card ) , dev_id ( card ) ,
GELIC_LV1_POST_WLAN_CMD ,
cmd - > cmd , arg1 , arg2 ,
& cmd - > tag , & cmd - > size ) ;
if ( cmd - > status ) {
complete ( & cmd - > done ) ;
pr_info ( " %s: cmd issue failed \n " , __func__ ) ;
return ;
}
wait_for_completion ( & wl - > cmd_done_intr ) ;
if ( cmd_info [ cmd - > cmd ] . post_arg ) {
arg1 = ps3_mm_phys_to_lpar ( __pa ( cmd - > buffer ) ) ;
arg2 = cmd - > buf_size ;
} else {
arg1 = 0 ;
arg2 = 0 ;
}
cmd - > status = lv1_net_control ( bus_id ( card ) , dev_id ( card ) ,
GELIC_LV1_GET_WLAN_CMD_RESULT ,
cmd - > tag , arg1 , arg2 ,
& cmd - > cmd_status , & cmd - > size ) ;
# ifdef DEBUG
if ( cmd - > status | | cmd - > cmd_status ) {
pr_debug ( " %s: cmd done tag=%#lx arg1=%#lx, arg2=%#lx \n " , __func__ ,
cmd - > tag , arg1 , arg2 ) ;
pr_debug ( " %s: cmd done status=%#x cmd_status=%#lx size=%#lx \n " ,
__func__ , cmd - > status , cmd - > cmd_status , cmd - > size ) ;
}
# endif
complete ( & cmd - > done ) ;
pr_debug ( " %s: cmd='%s' done \n " , __func__ , cmdstr ( cmd - > cmd ) ) ;
}
static struct gelic_eurus_cmd * gelic_eurus_sync_cmd ( struct gelic_wl_info * wl ,
unsigned int eurus_cmd ,
void * buffer ,
unsigned int buf_size )
{
struct gelic_eurus_cmd * cmd ;
/* allocate cmd */
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( ! cmd )
return NULL ;
/* initialize members */
cmd - > cmd = eurus_cmd ;
cmd - > buffer = buffer ;
cmd - > buf_size = buf_size ;
cmd - > wl = wl ;
INIT_WORK ( & cmd - > work , gelic_eurus_sync_cmd_worker ) ;
init_completion ( & cmd - > done ) ;
queue_work ( wl - > eurus_cmd_queue , & cmd - > work ) ;
/* wait for command completion */
wait_for_completion ( & cmd - > done ) ;
return cmd ;
}
static u32 gelic_wl_get_link ( struct net_device * netdev )
{
struct gelic_wl_info * wl = port_wl ( netdev_port ( netdev ) ) ;
u32 ret ;
pr_debug ( " %s: <- \n " , __func__ ) ;
2008-05-22 11:00:02 +04:00
mutex_lock ( & wl - > assoc_stat_lock ) ;
2008-02-07 13:58:57 +03:00
if ( wl - > assoc_stat = = GELIC_WL_ASSOC_STAT_ASSOCIATED )
ret = 1 ;
else
ret = 0 ;
2008-05-22 11:00:02 +04:00
mutex_unlock ( & wl - > assoc_stat_lock ) ;
2008-02-07 13:58:57 +03:00
pr_debug ( " %s: -> \n " , __func__ ) ;
return ret ;
}
static void gelic_wl_send_iwap_event ( struct gelic_wl_info * wl , u8 * bssid )
{
union iwreq_data data ;
memset ( & data , 0 , sizeof ( data ) ) ;
if ( bssid )
memcpy ( data . ap_addr . sa_data , bssid , ETH_ALEN ) ;
data . ap_addr . sa_family = ARPHRD_ETHER ;
wireless_send_event ( port_to_netdev ( wl_port ( wl ) ) , SIOCGIWAP ,
& data , NULL ) ;
}
/*
* wireless extension handlers and helpers
*/
/* SIOGIWNAME */
static int gelic_wl_get_name ( struct net_device * dev ,
struct iw_request_info * info ,
union iwreq_data * iwreq , char * extra )
{
strcpy ( iwreq - > name , " IEEE 802.11bg " ) ;
return 0 ;
}
static void gelic_wl_get_ch_info ( struct gelic_wl_info * wl )
{
struct gelic_card * card = port_to_card ( wl_port ( wl ) ) ;
u64 ch_info_raw , tmp ;
int status ;
if ( ! test_and_set_bit ( GELIC_WL_STAT_CH_INFO , & wl - > stat ) ) {
status = lv1_net_control ( bus_id ( card ) , dev_id ( card ) ,
GELIC_LV1_GET_CHANNEL , 0 , 0 , 0 ,
& ch_info_raw ,
& tmp ) ;
/* some fw versions may return error */
if ( status ) {
if ( status ! = LV1_NO_ENTRY )
pr_info ( " %s: available ch unknown \n " , __func__ ) ;
wl - > ch_info = 0x07ff ; /* 11 ch */
} else
/* 16 bits of MSB has available channels */
wl - > ch_info = ch_info_raw > > 48 ;
}
return ;
}
/* SIOGIWRANGE */
static int gelic_wl_get_range ( struct net_device * netdev ,
struct iw_request_info * info ,
union iwreq_data * iwreq , char * extra )
{
struct iw_point * point = & iwreq - > data ;
struct iw_range * range = ( struct iw_range * ) extra ;
struct gelic_wl_info * wl = port_wl ( netdev_port ( netdev ) ) ;
unsigned int i , chs ;
pr_debug ( " %s: <- \n " , __func__ ) ;
point - > length = sizeof ( struct iw_range ) ;
memset ( range , 0 , sizeof ( struct iw_range ) ) ;
range - > we_version_compiled = WIRELESS_EXT ;
range - > we_version_source = 22 ;
/* available channels and frequencies */
gelic_wl_get_ch_info ( wl ) ;
for ( i = 0 , chs = 0 ;
i < NUM_CHANNELS & & chs < IW_MAX_FREQUENCIES ; i + + )
if ( wl - > ch_info & ( 1 < < i ) ) {
range - > freq [ chs ] . i = i + 1 ;
range - > freq [ chs ] . m = channel_freq [ i ] ;
range - > freq [ chs ] . e = 6 ;
chs + + ;
}
range - > num_frequency = chs ;
range - > old_num_frequency = chs ;
range - > num_channels = chs ;
range - > old_num_channels = chs ;
/* bitrates */
for ( i = 0 ; i < NUM_BITRATES ; i + + )
range - > bitrate [ i ] = bitrate_list [ i ] ;
range - > num_bitrates = i ;
/* signal levels */
range - > max_qual . qual = 100 ; /* relative value */
range - > max_qual . level = 100 ;
range - > avg_qual . qual = 50 ;
range - > avg_qual . level = 50 ;
range - > sensitivity = 0 ;
/* Event capability */
IW_EVENT_CAPA_SET_KERNEL ( range - > event_capa ) ;
IW_EVENT_CAPA_SET ( range - > event_capa , SIOCGIWAP ) ;
IW_EVENT_CAPA_SET ( range - > event_capa , SIOCGIWSCAN ) ;
/* encryption capability */
range - > enc_capa = IW_ENC_CAPA_WPA |
2008-05-30 11:52:44 +04:00
IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP |
IW_ENC_CAPA_4WAY_HANDSHAKE ;
2008-02-07 13:58:57 +03:00
if ( wpa2_capable ( ) )
range - > enc_capa | = IW_ENC_CAPA_WPA2 ;
range - > encoding_size [ 0 ] = 5 ; /* 40bit WEP */
range - > encoding_size [ 1 ] = 13 ; /* 104bit WEP */
range - > encoding_size [ 2 ] = 32 ; /* WPA-PSK */
range - > num_encoding_sizes = 3 ;
range - > max_encoding_tokens = GELIC_WEP_KEYS ;
2008-06-02 07:49:16 +04:00
/* scan capability */
range - > scan_capa = IW_SCAN_CAPA_ESSID ;
2008-02-07 13:58:57 +03:00
pr_debug ( " %s: -> \n " , __func__ ) ;
return 0 ;
}
/* SIOC{G,S}IWSCAN */
static int gelic_wl_set_scan ( struct net_device * netdev ,
struct iw_request_info * info ,
union iwreq_data * wrqu , char * extra )
{
struct gelic_wl_info * wl = port_wl ( netdev_priv ( netdev ) ) ;
2008-06-02 07:49:16 +04:00
struct iw_scan_req * req ;
u8 * essid = NULL ;
size_t essid_len = 0 ;
if ( wrqu - > data . length = = sizeof ( struct iw_scan_req ) & &
wrqu - > data . flags & IW_SCAN_THIS_ESSID ) {
req = ( struct iw_scan_req * ) extra ;
essid = req - > essid ;
essid_len = req - > essid_len ;
pr_debug ( " %s: ESSID scan =%s \n " , __func__ , essid ) ;
}
return gelic_wl_start_scan ( wl , 1 , essid , essid_len ) ;
2008-02-07 13:58:57 +03:00
}
# define OUI_LEN 3
static const u8 rsn_oui [ OUI_LEN ] = { 0x00 , 0x0f , 0xac } ;
static const u8 wpa_oui [ OUI_LEN ] = { 0x00 , 0x50 , 0xf2 } ;
/*
* synthesize WPA / RSN IE data
* See WiFi WPA specification and IEEE 802.11 - 2007 7.3 .2 .25
* for the format
*/
static size_t gelic_wl_synthesize_ie ( u8 * buf ,
struct gelic_eurus_scan_info * scan )
{
const u8 * oui_header ;
u8 * start = buf ;
int rsn ;
int ccmp ;
pr_debug ( " %s: <- sec=%16x \n " , __func__ , scan - > security ) ;
switch ( be16_to_cpu ( scan - > security ) & GELIC_EURUS_SCAN_SEC_MASK ) {
case GELIC_EURUS_SCAN_SEC_WPA :
rsn = 0 ;
break ;
case GELIC_EURUS_SCAN_SEC_WPA2 :
rsn = 1 ;
break ;
default :
/* WEP or none. No IE returned */
return 0 ;
}
switch ( be16_to_cpu ( scan - > security ) & GELIC_EURUS_SCAN_SEC_WPA_MASK ) {
case GELIC_EURUS_SCAN_SEC_WPA_TKIP :
ccmp = 0 ;
break ;
case GELIC_EURUS_SCAN_SEC_WPA_AES :
ccmp = 1 ;
break ;
default :
if ( rsn ) {
ccmp = 1 ;
pr_info ( " %s: no cipher info. defaulted to CCMP \n " ,
__func__ ) ;
} else {
ccmp = 0 ;
pr_info ( " %s: no cipher info. defaulted to TKIP \n " ,
__func__ ) ;
}
}
if ( rsn )
oui_header = rsn_oui ;
else
oui_header = wpa_oui ;
/* element id */
if ( rsn )
2008-10-31 00:09:54 +03:00
* buf + + = WLAN_EID_RSN ;
2008-02-07 13:58:57 +03:00
else
2008-10-31 00:09:54 +03:00
* buf + + = WLAN_EID_GENERIC ;
2008-02-07 13:58:57 +03:00
/* length filed; set later */
buf + + ;
/* wpa special header */
if ( ! rsn ) {
memcpy ( buf , wpa_oui , OUI_LEN ) ;
buf + = OUI_LEN ;
* buf + + = 0x01 ;
}
/* version */
* buf + + = 0x01 ; /* version 1.0 */
* buf + + = 0x00 ;
/* group cipher */
memcpy ( buf , oui_header , OUI_LEN ) ;
buf + = OUI_LEN ;
if ( ccmp )
* buf + + = 0x04 ; /* CCMP */
else
* buf + + = 0x02 ; /* TKIP */
/* pairwise key count always 1 */
* buf + + = 0x01 ;
* buf + + = 0x00 ;
/* pairwise key suit */
memcpy ( buf , oui_header , OUI_LEN ) ;
buf + = OUI_LEN ;
if ( ccmp )
* buf + + = 0x04 ; /* CCMP */
else
* buf + + = 0x02 ; /* TKIP */
/* AKM count is 1 */
* buf + + = 0x01 ;
* buf + + = 0x00 ;
/* AKM suite is assumed as PSK*/
memcpy ( buf , oui_header , OUI_LEN ) ;
buf + = OUI_LEN ;
* buf + + = 0x02 ; /* PSK */
/* RSN capabilities is 0 */
* buf + + = 0x00 ;
* buf + + = 0x00 ;
/* set length field */
start [ 1 ] = ( buf - start - 2 ) ;
pr_debug ( " %s: -> \n " , __func__ ) ;
return ( buf - start ) ;
}
struct ie_item {
u8 * data ;
u8 len ;
} ;
struct ie_info {
struct ie_item wpa ;
struct ie_item rsn ;
} ;
static void gelic_wl_parse_ie ( u8 * data , size_t len ,
struct ie_info * ie_info )
{
size_t data_left = len ;
u8 * pos = data ;
u8 item_len ;
u8 item_id ;
pr_debug ( " %s: data=%p len=%ld \n " , __func__ ,
data , len ) ;
memset ( ie_info , 0 , sizeof ( struct ie_info ) ) ;
2008-04-14 13:07:21 +04:00
while ( 2 < = data_left ) {
2008-02-07 13:58:57 +03:00
item_id = * pos + + ;
item_len = * pos + + ;
2008-04-14 13:07:21 +04:00
data_left - = 2 ;
if ( data_left < item_len )
break ;
2008-02-07 13:58:57 +03:00
switch ( item_id ) {
2008-10-31 00:09:54 +03:00
case WLAN_EID_GENERIC :
2008-04-14 13:07:21 +04:00
if ( ( OUI_LEN + 1 < = item_len ) & &
! memcmp ( pos , wpa_oui , OUI_LEN ) & &
2008-02-07 13:58:57 +03:00
pos [ OUI_LEN ] = = 0x01 ) {
ie_info - > wpa . data = pos - 2 ;
ie_info - > wpa . len = item_len + 2 ;
}
break ;
2008-10-31 00:09:54 +03:00
case WLAN_EID_RSN :
2008-02-07 13:58:57 +03:00
ie_info - > rsn . data = pos - 2 ;
/* length includes the header */
ie_info - > rsn . len = item_len + 2 ;
break ;
default :
pr_debug ( " %s: ignore %#x,%d \n " , __func__ ,
item_id , item_len ) ;
break ;
}
pos + = item_len ;
2008-04-14 13:07:21 +04:00
data_left - = item_len ;
2008-02-07 13:58:57 +03:00
}
pr_debug ( " %s: wpa=%p,%d wpa2=%p,%d \n " , __func__ ,
ie_info - > wpa . data , ie_info - > wpa . len ,
ie_info - > rsn . data , ie_info - > rsn . len ) ;
}
/*
* translate the scan informations from hypervisor to a
* independent format
*/
static char * gelic_wl_translate_scan ( struct net_device * netdev ,
2008-06-17 05:50:49 +04:00
struct iw_request_info * info ,
2008-02-07 13:58:57 +03:00
char * ev ,
char * stop ,
struct gelic_wl_scan_info * network )
{
struct iw_event iwe ;
struct gelic_eurus_scan_info * scan = network - > hwinfo ;
char * tmp ;
u8 rate ;
unsigned int i , j , len ;
2008-10-31 00:09:54 +03:00
u8 buf [ 64 ] ; /* arbitrary size large enough */
2008-02-07 13:58:57 +03:00
pr_debug ( " %s: <- \n " , __func__ ) ;
/* first entry should be AP's mac address */
iwe . cmd = SIOCGIWAP ;
iwe . u . ap_addr . sa_family = ARPHRD_ETHER ;
memcpy ( iwe . u . ap_addr . sa_data , & scan - > bssid [ 2 ] , ETH_ALEN ) ;
2008-06-17 05:50:49 +04:00
ev = iwe_stream_add_event ( info , ev , stop , & iwe , IW_EV_ADDR_LEN ) ;
2008-02-07 13:58:57 +03:00
/* ESSID */
iwe . cmd = SIOCGIWESSID ;
iwe . u . data . flags = 1 ;
iwe . u . data . length = strnlen ( scan - > essid , 32 ) ;
2008-06-17 05:50:49 +04:00
ev = iwe_stream_add_point ( info , ev , stop , & iwe , scan - > essid ) ;
2008-02-07 13:58:57 +03:00
/* FREQUENCY */
iwe . cmd = SIOCGIWFREQ ;
iwe . u . freq . m = be16_to_cpu ( scan - > channel ) ;
iwe . u . freq . e = 0 ; /* table value in MHz */
iwe . u . freq . i = 0 ;
2008-06-17 05:50:49 +04:00
ev = iwe_stream_add_event ( info , ev , stop , & iwe , IW_EV_FREQ_LEN ) ;
2008-02-07 13:58:57 +03:00
/* RATES */
iwe . cmd = SIOCGIWRATE ;
iwe . u . bitrate . fixed = iwe . u . bitrate . disabled = 0 ;
/* to stuff multiple values in one event */
2008-06-17 05:50:49 +04:00
tmp = ev + iwe_stream_lcp_len ( info ) ;
2008-02-07 13:58:57 +03:00
/* put them in ascendant order (older is first) */
i = 0 ;
j = 0 ;
pr_debug ( " %s: rates=%d rate=%d \n " , __func__ ,
network - > rate_len , network - > rate_ext_len ) ;
while ( i < network - > rate_len ) {
if ( j < network - > rate_ext_len & &
( ( scan - > ext_rate [ j ] & 0x7f ) < ( scan - > rate [ i ] & 0x7f ) ) )
rate = scan - > ext_rate [ j + + ] & 0x7f ;
else
rate = scan - > rate [ i + + ] & 0x7f ;
iwe . u . bitrate . value = rate * 500000 ; /* 500kbps unit */
2008-06-17 05:50:49 +04:00
tmp = iwe_stream_add_value ( info , ev , tmp , stop , & iwe ,
2008-02-07 13:58:57 +03:00
IW_EV_PARAM_LEN ) ;
}
while ( j < network - > rate_ext_len ) {
iwe . u . bitrate . value = ( scan - > ext_rate [ j + + ] & 0x7f ) * 500000 ;
2008-06-17 05:50:49 +04:00
tmp = iwe_stream_add_value ( info , ev , tmp , stop , & iwe ,
2008-02-07 13:58:57 +03:00
IW_EV_PARAM_LEN ) ;
}
/* Check if we added any rate */
2008-06-17 05:50:49 +04:00
if ( iwe_stream_lcp_len ( info ) < ( tmp - ev ) )
2008-02-07 13:58:57 +03:00
ev = tmp ;
/* ENCODE */
iwe . cmd = SIOCGIWENCODE ;
if ( be16_to_cpu ( scan - > capability ) & WLAN_CAPABILITY_PRIVACY )
iwe . u . data . flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY ;
else
iwe . u . data . flags = IW_ENCODE_DISABLED ;
iwe . u . data . length = 0 ;
2008-06-17 05:50:49 +04:00
ev = iwe_stream_add_point ( info , ev , stop , & iwe , scan - > essid ) ;
2008-02-07 13:58:57 +03:00
/* MODE */
iwe . cmd = SIOCGIWMODE ;
if ( be16_to_cpu ( scan - > capability ) &
( WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS ) ) {
if ( be16_to_cpu ( scan - > capability ) & WLAN_CAPABILITY_ESS )
iwe . u . mode = IW_MODE_MASTER ;
else
iwe . u . mode = IW_MODE_ADHOC ;
2008-06-17 05:50:49 +04:00
ev = iwe_stream_add_event ( info , ev , stop , & iwe , IW_EV_UINT_LEN ) ;
2008-02-07 13:58:57 +03:00
}
/* QUAL */
iwe . cmd = IWEVQUAL ;
iwe . u . qual . updated = IW_QUAL_ALL_UPDATED |
IW_QUAL_QUAL_INVALID | IW_QUAL_NOISE_INVALID ;
iwe . u . qual . level = be16_to_cpu ( scan - > rssi ) ;
iwe . u . qual . qual = be16_to_cpu ( scan - > rssi ) ;
iwe . u . qual . noise = 0 ;
2008-06-17 05:50:49 +04:00
ev = iwe_stream_add_event ( info , ev , stop , & iwe , IW_EV_QUAL_LEN ) ;
2008-02-07 13:58:57 +03:00
/* RSN */
memset ( & iwe , 0 , sizeof ( iwe ) ) ;
if ( be16_to_cpu ( scan - > size ) < = sizeof ( * scan ) ) {
/* If wpa[2] capable station, synthesize IE and put it */
len = gelic_wl_synthesize_ie ( buf , scan ) ;
if ( len ) {
iwe . cmd = IWEVGENIE ;
iwe . u . data . length = len ;
2008-06-17 05:50:49 +04:00
ev = iwe_stream_add_point ( info , ev , stop , & iwe , buf ) ;
2008-02-07 13:58:57 +03:00
}
} else {
/* this scan info has IE data */
struct ie_info ie_info ;
size_t data_len ;
data_len = be16_to_cpu ( scan - > size ) - sizeof ( * scan ) ;
gelic_wl_parse_ie ( scan - > elements , data_len , & ie_info ) ;
if ( ie_info . wpa . len & & ( ie_info . wpa . len < = sizeof ( buf ) ) ) {
memcpy ( buf , ie_info . wpa . data , ie_info . wpa . len ) ;
iwe . cmd = IWEVGENIE ;
iwe . u . data . length = ie_info . wpa . len ;
2008-06-17 05:50:49 +04:00
ev = iwe_stream_add_point ( info , ev , stop , & iwe , buf ) ;
2008-02-07 13:58:57 +03:00
}
if ( ie_info . rsn . len & & ( ie_info . rsn . len < = sizeof ( buf ) ) ) {
memset ( & iwe , 0 , sizeof ( iwe ) ) ;
memcpy ( buf , ie_info . rsn . data , ie_info . rsn . len ) ;
iwe . cmd = IWEVGENIE ;
iwe . u . data . length = ie_info . rsn . len ;
2008-06-17 05:50:49 +04:00
ev = iwe_stream_add_point ( info , ev , stop , & iwe , buf ) ;
2008-02-07 13:58:57 +03:00
}
}
pr_debug ( " %s: -> \n " , __func__ ) ;
return ev ;
}
static int gelic_wl_get_scan ( struct net_device * netdev ,
struct iw_request_info * info ,
union iwreq_data * wrqu , char * extra )
{
struct gelic_wl_info * wl = port_wl ( netdev_priv ( netdev ) ) ;
struct gelic_wl_scan_info * scan_info ;
char * ev = extra ;
char * stop = ev + wrqu - > data . length ;
int ret = 0 ;
unsigned long this_time = jiffies ;
pr_debug ( " %s: <- \n " , __func__ ) ;
2008-05-22 11:00:01 +04:00
if ( mutex_lock_interruptible ( & wl - > scan_lock ) )
2008-02-07 13:58:57 +03:00
return - EAGAIN ;
switch ( wl - > scan_stat ) {
case GELIC_WL_SCAN_STAT_SCANNING :
/* If a scan in progress, caller should call me again */
ret = - EAGAIN ;
goto out ;
break ;
case GELIC_WL_SCAN_STAT_INIT :
/* last scan request failed or never issued */
ret = - ENODEV ;
goto out ;
break ;
case GELIC_WL_SCAN_STAT_GOT_LIST :
/* ok, use current list */
break ;
}
list_for_each_entry ( scan_info , & wl - > network_list , list ) {
if ( wl - > scan_age = = 0 | |
time_after ( scan_info - > last_scanned + wl - > scan_age ,
this_time ) )
2008-06-17 05:50:49 +04:00
ev = gelic_wl_translate_scan ( netdev , info ,
ev , stop ,
2008-02-07 13:58:57 +03:00
scan_info ) ;
else
pr_debug ( " %s:entry too old \n " , __func__ ) ;
if ( stop - ev < = IW_EV_ADDR_LEN ) {
ret = - E2BIG ;
goto out ;
}
}
wrqu - > data . length = ev - extra ;
wrqu - > data . flags = 0 ;
out :
2008-05-22 11:00:01 +04:00
mutex_unlock ( & wl - > scan_lock ) ;
2008-02-07 13:58:57 +03:00
pr_debug ( " %s: -> %d %d \n " , __func__ , ret , wrqu - > data . length ) ;
return ret ;
}
# ifdef DEBUG
static void scan_list_dump ( struct gelic_wl_info * wl )
{
struct gelic_wl_scan_info * scan_info ;
int i ;
i = 0 ;
list_for_each_entry ( scan_info , & wl - > network_list , list ) {
pr_debug ( " %s: item %d \n " , __func__ , i + + ) ;
pr_debug ( " valid=%d eurusindex=%d last=%lx \n " ,
scan_info - > valid , scan_info - > eurus_index ,
scan_info - > last_scanned ) ;
pr_debug ( " r_len=%d r_ext_len=%d essid_len=%d \n " ,
scan_info - > rate_len , scan_info - > rate_ext_len ,
scan_info - > essid_len ) ;
/* -- */
2008-10-28 01:59:26 +03:00
pr_debug ( " bssid=%pM \n " , & scan_info - > hwinfo - > bssid [ 2 ] ) ;
2008-02-07 13:58:57 +03:00
pr_debug ( " essid=%s \n " , scan_info - > hwinfo - > essid ) ;
}
}
# endif
static int gelic_wl_set_auth ( struct net_device * netdev ,
struct iw_request_info * info ,
union iwreq_data * data , char * extra )
{
struct iw_param * param = & data - > param ;
struct gelic_wl_info * wl = port_wl ( netdev_port ( netdev ) ) ;
unsigned long irqflag ;
int ret = 0 ;
pr_debug ( " %s: <- %d \n " , __func__ , param - > flags & IW_AUTH_INDEX ) ;
spin_lock_irqsave ( & wl - > lock , irqflag ) ;
switch ( param - > flags & IW_AUTH_INDEX ) {
case IW_AUTH_WPA_VERSION :
if ( param - > value & IW_AUTH_WPA_VERSION_DISABLED ) {
pr_debug ( " %s: NO WPA selected \n " , __func__ ) ;
wl - > wpa_level = GELIC_WL_WPA_LEVEL_NONE ;
wl - > group_cipher_method = GELIC_WL_CIPHER_WEP ;
wl - > pairwise_cipher_method = GELIC_WL_CIPHER_WEP ;
}
if ( param - > value & IW_AUTH_WPA_VERSION_WPA ) {
pr_debug ( " %s: WPA version 1 selected \n " , __func__ ) ;
wl - > wpa_level = GELIC_WL_WPA_LEVEL_WPA ;
wl - > group_cipher_method = GELIC_WL_CIPHER_TKIP ;
wl - > pairwise_cipher_method = GELIC_WL_CIPHER_TKIP ;
wl - > auth_method = GELIC_EURUS_AUTH_OPEN ;
}
if ( param - > value & IW_AUTH_WPA_VERSION_WPA2 ) {
/*
* As the hypervisor may not tell the cipher
* information of the AP if it is WPA2 ,
* you will not decide suitable cipher from
* its beacon .
* You should have knowledge about the AP ' s
* cipher infomation in other method prior to
* the association .
*/
if ( ! precise_ie ( ) )
pr_info ( " %s: WPA2 may not work \n " , __func__ ) ;
if ( wpa2_capable ( ) ) {
wl - > wpa_level = GELIC_WL_WPA_LEVEL_WPA2 ;
wl - > group_cipher_method = GELIC_WL_CIPHER_AES ;
wl - > pairwise_cipher_method =
GELIC_WL_CIPHER_AES ;
wl - > auth_method = GELIC_EURUS_AUTH_OPEN ;
} else
ret = - EINVAL ;
}
break ;
case IW_AUTH_CIPHER_PAIRWISE :
if ( param - > value &
( IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_WEP40 ) ) {
pr_debug ( " %s: WEP selected \n " , __func__ ) ;
wl - > pairwise_cipher_method = GELIC_WL_CIPHER_WEP ;
}
if ( param - > value & IW_AUTH_CIPHER_TKIP ) {
pr_debug ( " %s: TKIP selected \n " , __func__ ) ;
wl - > pairwise_cipher_method = GELIC_WL_CIPHER_TKIP ;
}
if ( param - > value & IW_AUTH_CIPHER_CCMP ) {
pr_debug ( " %s: CCMP selected \n " , __func__ ) ;
wl - > pairwise_cipher_method = GELIC_WL_CIPHER_AES ;
}
if ( param - > value & IW_AUTH_CIPHER_NONE ) {
pr_debug ( " %s: no auth selected \n " , __func__ ) ;
wl - > pairwise_cipher_method = GELIC_WL_CIPHER_NONE ;
}
break ;
case IW_AUTH_CIPHER_GROUP :
if ( param - > value &
( IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_WEP40 ) ) {
pr_debug ( " %s: WEP selected \n " , __func__ ) ;
wl - > group_cipher_method = GELIC_WL_CIPHER_WEP ;
}
if ( param - > value & IW_AUTH_CIPHER_TKIP ) {
pr_debug ( " %s: TKIP selected \n " , __func__ ) ;
wl - > group_cipher_method = GELIC_WL_CIPHER_TKIP ;
}
if ( param - > value & IW_AUTH_CIPHER_CCMP ) {
pr_debug ( " %s: CCMP selected \n " , __func__ ) ;
wl - > group_cipher_method = GELIC_WL_CIPHER_AES ;
}
if ( param - > value & IW_AUTH_CIPHER_NONE ) {
pr_debug ( " %s: no auth selected \n " , __func__ ) ;
wl - > group_cipher_method = GELIC_WL_CIPHER_NONE ;
}
break ;
case IW_AUTH_80211_AUTH_ALG :
if ( param - > value & IW_AUTH_ALG_SHARED_KEY ) {
pr_debug ( " %s: shared key specified \n " , __func__ ) ;
wl - > auth_method = GELIC_EURUS_AUTH_SHARED ;
} else if ( param - > value & IW_AUTH_ALG_OPEN_SYSTEM ) {
pr_debug ( " %s: open system specified \n " , __func__ ) ;
wl - > auth_method = GELIC_EURUS_AUTH_OPEN ;
} else
ret = - EINVAL ;
break ;
case IW_AUTH_WPA_ENABLED :
if ( param - > value ) {
pr_debug ( " %s: WPA enabled \n " , __func__ ) ;
wl - > wpa_level = GELIC_WL_WPA_LEVEL_WPA ;
} else {
pr_debug ( " %s: WPA disabled \n " , __func__ ) ;
wl - > wpa_level = GELIC_WL_WPA_LEVEL_NONE ;
}
break ;
case IW_AUTH_KEY_MGMT :
if ( param - > value & IW_AUTH_KEY_MGMT_PSK )
break ;
/* intentionally fall through */
default :
ret = - EOPNOTSUPP ;
break ;
} ;
if ( ! ret )
set_bit ( GELIC_WL_STAT_CONFIGURED , & wl - > stat ) ;
spin_unlock_irqrestore ( & wl - > lock , irqflag ) ;
pr_debug ( " %s: -> %d \n " , __func__ , ret ) ;
return ret ;
}
static int gelic_wl_get_auth ( struct net_device * netdev ,
struct iw_request_info * info ,
union iwreq_data * iwreq , char * extra )
{
struct iw_param * param = & iwreq - > param ;
struct gelic_wl_info * wl = port_wl ( netdev_port ( netdev ) ) ;
unsigned long irqflag ;
int ret = 0 ;
pr_debug ( " %s: <- %d \n " , __func__ , param - > flags & IW_AUTH_INDEX ) ;
spin_lock_irqsave ( & wl - > lock , irqflag ) ;
switch ( param - > flags & IW_AUTH_INDEX ) {
case IW_AUTH_WPA_VERSION :
switch ( wl - > wpa_level ) {
case GELIC_WL_WPA_LEVEL_WPA :
param - > value | = IW_AUTH_WPA_VERSION_WPA ;
break ;
case GELIC_WL_WPA_LEVEL_WPA2 :
param - > value | = IW_AUTH_WPA_VERSION_WPA2 ;
break ;
default :
param - > value | = IW_AUTH_WPA_VERSION_DISABLED ;
}
break ;
case IW_AUTH_80211_AUTH_ALG :
if ( wl - > auth_method = = GELIC_EURUS_AUTH_SHARED )
param - > value = IW_AUTH_ALG_SHARED_KEY ;
else if ( wl - > auth_method = = GELIC_EURUS_AUTH_OPEN )
param - > value = IW_AUTH_ALG_OPEN_SYSTEM ;
break ;
case IW_AUTH_WPA_ENABLED :
switch ( wl - > wpa_level ) {
case GELIC_WL_WPA_LEVEL_WPA :
case GELIC_WL_WPA_LEVEL_WPA2 :
param - > value = 1 ;
break ;
default :
param - > value = 0 ;
break ;
}
break ;
default :
ret = - EOPNOTSUPP ;
}
spin_unlock_irqrestore ( & wl - > lock , irqflag ) ;
pr_debug ( " %s: -> %d \n " , __func__ , ret ) ;
return ret ;
}
/* SIOC{S,G}IWESSID */
static int gelic_wl_set_essid ( struct net_device * netdev ,
struct iw_request_info * info ,
union iwreq_data * data , char * extra )
{
struct gelic_wl_info * wl = port_wl ( netdev_priv ( netdev ) ) ;
unsigned long irqflag ;
pr_debug ( " %s: <- l=%d f=%d \n " , __func__ ,
data - > essid . length , data - > essid . flags ) ;
if ( IW_ESSID_MAX_SIZE < data - > essid . length )
return - EINVAL ;
spin_lock_irqsave ( & wl - > lock , irqflag ) ;
if ( data - > essid . flags ) {
wl - > essid_len = data - > essid . length ;
memcpy ( wl - > essid , extra , wl - > essid_len ) ;
pr_debug ( " %s: essid = '%s' \n " , __func__ , extra ) ;
set_bit ( GELIC_WL_STAT_ESSID_SET , & wl - > stat ) ;
} else {
pr_debug ( " %s: ESSID any \n " , __func__ ) ;
clear_bit ( GELIC_WL_STAT_ESSID_SET , & wl - > stat ) ;
}
set_bit ( GELIC_WL_STAT_CONFIGURED , & wl - > stat ) ;
spin_unlock_irqrestore ( & wl - > lock , irqflag ) ;
gelic_wl_try_associate ( netdev ) ; /* FIXME */
pr_debug ( " %s: -> \n " , __func__ ) ;
return 0 ;
}
static int gelic_wl_get_essid ( struct net_device * netdev ,
struct iw_request_info * info ,
union iwreq_data * data , char * extra )
{
struct gelic_wl_info * wl = port_wl ( netdev_priv ( netdev ) ) ;
unsigned long irqflag ;
pr_debug ( " %s: <- \n " , __func__ ) ;
2008-05-22 11:00:02 +04:00
mutex_lock ( & wl - > assoc_stat_lock ) ;
2008-02-07 13:58:57 +03:00
spin_lock_irqsave ( & wl - > lock , irqflag ) ;
if ( test_bit ( GELIC_WL_STAT_ESSID_SET , & wl - > stat ) | |
wl - > assoc_stat = = GELIC_WL_ASSOC_STAT_ASSOCIATED ) {
memcpy ( extra , wl - > essid , wl - > essid_len ) ;
data - > essid . length = wl - > essid_len ;
data - > essid . flags = 1 ;
} else
data - > essid . flags = 0 ;
2008-05-22 11:00:02 +04:00
mutex_unlock ( & wl - > assoc_stat_lock ) ;
2008-02-07 13:58:57 +03:00
spin_unlock_irqrestore ( & wl - > lock , irqflag ) ;
pr_debug ( " %s: -> len=%d \n " , __func__ , data - > essid . length ) ;
return 0 ;
}
/* SIO{S,G}IWENCODE */
static int gelic_wl_set_encode ( struct net_device * netdev ,
struct iw_request_info * info ,
union iwreq_data * data , char * extra )
{
struct gelic_wl_info * wl = port_wl ( netdev_priv ( netdev ) ) ;
struct iw_point * enc = & data - > encoding ;
__u16 flags ;
PS3: gelic: use unsigned long for irqflags
The semantic patch I used was this:
@@
expression lock;
identifier flags;
expression subclass;
@@
- unsigned int flags;
+ unsigned long flags;
...
<+...
(
spin_lock_irqsave(lock, flags)
|
_spin_lock_irqsave(lock)
|
spin_unlock_irqrestore(lock, flags)
|
_spin_unlock_irqrestore(lock, flags)
|
read_lock_irqsave(lock, flags)
|
_read_lock_irqsave(lock)
|
read_unlock_irqrestore(lock, flags)
|
_read_unlock_irqrestore(lock, flags)
|
write_lock_irqsave(lock, flags)
|
_write_lock_irqsave(lock)
|
write_unlock_irqrestore(lock, flags)
|
_write_unlock_irqrestore(lock, flags)
|
spin_lock_irqsave_nested(lock, flags, subclass)
|
_spin_lock_irqsave_nested(lock, subclass)
|
spin_unlock_irqrestore(lock, flags)
|
_spin_unlock_irqrestore(lock, flags)
|
_raw_spin_lock_flags(lock, flags)
|
__raw_spin_lock_flags(lock, flags)
)
...+>
This patch was generated using the Coccinelle framework.
Cc: Masakazu Mokuno <mokuno@sm.sony.co.jp>
Cc: Julia Lawall <julia@diku.dk>
Cc: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Vegard Nossum <vegard.nossum@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2008-07-29 15:31:47 +04:00
unsigned long irqflag ;
2008-02-07 13:58:57 +03:00
int key_index , index_specified ;
int ret = 0 ;
pr_debug ( " %s: <- \n " , __func__ ) ;
flags = enc - > flags & IW_ENCODE_FLAGS ;
key_index = enc - > flags & IW_ENCODE_INDEX ;
pr_debug ( " %s: key_index = %d \n " , __func__ , key_index ) ;
pr_debug ( " %s: key_len = %d \n " , __func__ , enc - > length ) ;
pr_debug ( " %s: flag=%x \n " , __func__ , enc - > flags & IW_ENCODE_FLAGS ) ;
if ( GELIC_WEP_KEYS < key_index )
return - EINVAL ;
spin_lock_irqsave ( & wl - > lock , irqflag ) ;
if ( key_index ) {
index_specified = 1 ;
key_index - - ;
} else {
index_specified = 0 ;
key_index = wl - > current_key ;
}
if ( flags & IW_ENCODE_NOKEY ) {
/* if just IW_ENCODE_NOKEY, change current key index */
if ( ! flags & & index_specified ) {
wl - > current_key = key_index ;
goto done ;
}
if ( flags & IW_ENCODE_DISABLED ) {
if ( ! index_specified ) {
/* disable encryption */
wl - > group_cipher_method = GELIC_WL_CIPHER_NONE ;
wl - > pairwise_cipher_method =
GELIC_WL_CIPHER_NONE ;
/* invalidate all key */
wl - > key_enabled = 0 ;
} else
clear_bit ( key_index , & wl - > key_enabled ) ;
}
if ( flags & IW_ENCODE_OPEN )
wl - > auth_method = GELIC_EURUS_AUTH_OPEN ;
if ( flags & IW_ENCODE_RESTRICTED ) {
pr_info ( " %s: shared key mode enabled \n " , __func__ ) ;
wl - > auth_method = GELIC_EURUS_AUTH_SHARED ;
}
} else {
if ( IW_ENCODING_TOKEN_MAX < enc - > length ) {
ret = - EINVAL ;
goto done ;
}
wl - > key_len [ key_index ] = enc - > length ;
memcpy ( wl - > key [ key_index ] , extra , enc - > length ) ;
set_bit ( key_index , & wl - > key_enabled ) ;
wl - > pairwise_cipher_method = GELIC_WL_CIPHER_WEP ;
wl - > group_cipher_method = GELIC_WL_CIPHER_WEP ;
}
set_bit ( GELIC_WL_STAT_CONFIGURED , & wl - > stat ) ;
done :
spin_unlock_irqrestore ( & wl - > lock , irqflag ) ;
pr_debug ( " %s: -> \n " , __func__ ) ;
return ret ;
}
static int gelic_wl_get_encode ( struct net_device * netdev ,
struct iw_request_info * info ,
union iwreq_data * data , char * extra )
{
struct gelic_wl_info * wl = port_wl ( netdev_priv ( netdev ) ) ;
struct iw_point * enc = & data - > encoding ;
PS3: gelic: use unsigned long for irqflags
The semantic patch I used was this:
@@
expression lock;
identifier flags;
expression subclass;
@@
- unsigned int flags;
+ unsigned long flags;
...
<+...
(
spin_lock_irqsave(lock, flags)
|
_spin_lock_irqsave(lock)
|
spin_unlock_irqrestore(lock, flags)
|
_spin_unlock_irqrestore(lock, flags)
|
read_lock_irqsave(lock, flags)
|
_read_lock_irqsave(lock)
|
read_unlock_irqrestore(lock, flags)
|
_read_unlock_irqrestore(lock, flags)
|
write_lock_irqsave(lock, flags)
|
_write_lock_irqsave(lock)
|
write_unlock_irqrestore(lock, flags)
|
_write_unlock_irqrestore(lock, flags)
|
spin_lock_irqsave_nested(lock, flags, subclass)
|
_spin_lock_irqsave_nested(lock, subclass)
|
spin_unlock_irqrestore(lock, flags)
|
_spin_unlock_irqrestore(lock, flags)
|
_raw_spin_lock_flags(lock, flags)
|
__raw_spin_lock_flags(lock, flags)
)
...+>
This patch was generated using the Coccinelle framework.
Cc: Masakazu Mokuno <mokuno@sm.sony.co.jp>
Cc: Julia Lawall <julia@diku.dk>
Cc: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Vegard Nossum <vegard.nossum@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2008-07-29 15:31:47 +04:00
unsigned long irqflag ;
2008-02-07 13:58:57 +03:00
unsigned int key_index , index_specified ;
int ret = 0 ;
pr_debug ( " %s: <- \n " , __func__ ) ;
key_index = enc - > flags & IW_ENCODE_INDEX ;
pr_debug ( " %s: flag=%#x point=%p len=%d extra=%p \n " , __func__ ,
enc - > flags , enc - > pointer , enc - > length , extra ) ;
if ( GELIC_WEP_KEYS < key_index )
return - EINVAL ;
spin_lock_irqsave ( & wl - > lock , irqflag ) ;
if ( key_index ) {
index_specified = 1 ;
key_index - - ;
} else {
index_specified = 0 ;
key_index = wl - > current_key ;
}
if ( wl - > group_cipher_method = = GELIC_WL_CIPHER_WEP ) {
switch ( wl - > auth_method ) {
case GELIC_EURUS_AUTH_OPEN :
enc - > flags = IW_ENCODE_OPEN ;
break ;
case GELIC_EURUS_AUTH_SHARED :
enc - > flags = IW_ENCODE_RESTRICTED ;
break ;
}
} else
enc - > flags = IW_ENCODE_DISABLED ;
if ( test_bit ( key_index , & wl - > key_enabled ) ) {
if ( enc - > length < wl - > key_len [ key_index ] ) {
ret = - EINVAL ;
goto done ;
}
enc - > length = wl - > key_len [ key_index ] ;
memcpy ( extra , wl - > key [ key_index ] , wl - > key_len [ key_index ] ) ;
} else {
enc - > length = 0 ;
enc - > flags | = IW_ENCODE_NOKEY ;
}
enc - > flags | = key_index + 1 ;
pr_debug ( " %s: -> flag=%x len=%d \n " , __func__ ,
enc - > flags , enc - > length ) ;
done :
spin_unlock_irqrestore ( & wl - > lock , irqflag ) ;
return ret ;
}
/* SIOC{S,G}IWAP */
static int gelic_wl_set_ap ( struct net_device * netdev ,
struct iw_request_info * info ,
union iwreq_data * data , char * extra )
{
struct gelic_wl_info * wl = port_wl ( netdev_priv ( netdev ) ) ;
unsigned long irqflag ;
pr_debug ( " %s: <- \n " , __func__ ) ;
if ( data - > ap_addr . sa_family ! = ARPHRD_ETHER )
return - EINVAL ;
spin_lock_irqsave ( & wl - > lock , irqflag ) ;
if ( is_valid_ether_addr ( data - > ap_addr . sa_data ) ) {
memcpy ( wl - > bssid , data - > ap_addr . sa_data ,
ETH_ALEN ) ;
set_bit ( GELIC_WL_STAT_BSSID_SET , & wl - > stat ) ;
set_bit ( GELIC_WL_STAT_CONFIGURED , & wl - > stat ) ;
2008-10-28 03:47:26 +03:00
pr_debug ( " %s: bss=%pM \n " , __func__ , wl - > bssid ) ;
2008-02-07 13:58:57 +03:00
} else {
pr_debug ( " %s: clear bssid \n " , __func__ ) ;
clear_bit ( GELIC_WL_STAT_BSSID_SET , & wl - > stat ) ;
memset ( wl - > bssid , 0 , ETH_ALEN ) ;
}
spin_unlock_irqrestore ( & wl - > lock , irqflag ) ;
pr_debug ( " %s: -> \n " , __func__ ) ;
return 0 ;
}
static int gelic_wl_get_ap ( struct net_device * netdev ,
struct iw_request_info * info ,
union iwreq_data * data , char * extra )
{
struct gelic_wl_info * wl = port_wl ( netdev_priv ( netdev ) ) ;
unsigned long irqflag ;
pr_debug ( " %s: <- \n " , __func__ ) ;
2008-05-22 11:00:02 +04:00
mutex_lock ( & wl - > assoc_stat_lock ) ;
2008-02-07 13:58:57 +03:00
spin_lock_irqsave ( & wl - > lock , irqflag ) ;
if ( wl - > assoc_stat = = GELIC_WL_ASSOC_STAT_ASSOCIATED ) {
data - > ap_addr . sa_family = ARPHRD_ETHER ;
memcpy ( data - > ap_addr . sa_data , wl - > active_bssid ,
ETH_ALEN ) ;
} else
memset ( data - > ap_addr . sa_data , 0 , ETH_ALEN ) ;
spin_unlock_irqrestore ( & wl - > lock , irqflag ) ;
2008-05-22 11:00:02 +04:00
mutex_unlock ( & wl - > assoc_stat_lock ) ;
2008-02-07 13:58:57 +03:00
pr_debug ( " %s: -> \n " , __func__ ) ;
return 0 ;
}
/* SIOC{S,G}IWENCODEEXT */
static int gelic_wl_set_encodeext ( struct net_device * netdev ,
struct iw_request_info * info ,
union iwreq_data * data , char * extra )
{
struct gelic_wl_info * wl = port_wl ( netdev_priv ( netdev ) ) ;
struct iw_point * enc = & data - > encoding ;
struct iw_encode_ext * ext = ( struct iw_encode_ext * ) extra ;
__u16 alg ;
__u16 flags ;
PS3: gelic: use unsigned long for irqflags
The semantic patch I used was this:
@@
expression lock;
identifier flags;
expression subclass;
@@
- unsigned int flags;
+ unsigned long flags;
...
<+...
(
spin_lock_irqsave(lock, flags)
|
_spin_lock_irqsave(lock)
|
spin_unlock_irqrestore(lock, flags)
|
_spin_unlock_irqrestore(lock, flags)
|
read_lock_irqsave(lock, flags)
|
_read_lock_irqsave(lock)
|
read_unlock_irqrestore(lock, flags)
|
_read_unlock_irqrestore(lock, flags)
|
write_lock_irqsave(lock, flags)
|
_write_lock_irqsave(lock)
|
write_unlock_irqrestore(lock, flags)
|
_write_unlock_irqrestore(lock, flags)
|
spin_lock_irqsave_nested(lock, flags, subclass)
|
_spin_lock_irqsave_nested(lock, subclass)
|
spin_unlock_irqrestore(lock, flags)
|
_spin_unlock_irqrestore(lock, flags)
|
_raw_spin_lock_flags(lock, flags)
|
__raw_spin_lock_flags(lock, flags)
)
...+>
This patch was generated using the Coccinelle framework.
Cc: Masakazu Mokuno <mokuno@sm.sony.co.jp>
Cc: Julia Lawall <julia@diku.dk>
Cc: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Vegard Nossum <vegard.nossum@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2008-07-29 15:31:47 +04:00
unsigned long irqflag ;
2008-02-07 13:58:57 +03:00
int key_index ;
int ret = 0 ;
pr_debug ( " %s: <- \n " , __func__ ) ;
flags = enc - > flags & IW_ENCODE_FLAGS ;
alg = ext - > alg ;
key_index = enc - > flags & IW_ENCODE_INDEX ;
pr_debug ( " %s: key_index = %d \n " , __func__ , key_index ) ;
pr_debug ( " %s: key_len = %d \n " , __func__ , enc - > length ) ;
pr_debug ( " %s: flag=%x \n " , __func__ , enc - > flags & IW_ENCODE_FLAGS ) ;
pr_debug ( " %s: ext_flag=%x \n " , __func__ , ext - > ext_flags ) ;
pr_debug ( " %s: ext_key_len=%x \n " , __func__ , ext - > key_len ) ;
if ( GELIC_WEP_KEYS < key_index )
return - EINVAL ;
spin_lock_irqsave ( & wl - > lock , irqflag ) ;
if ( key_index )
key_index - - ;
else
key_index = wl - > current_key ;
if ( ! enc - > length & & ( ext - > ext_flags & IW_ENCODE_EXT_SET_TX_KEY ) ) {
/* reques to change default key index */
pr_debug ( " %s: request to change default key to %d \n " ,
__func__ , key_index ) ;
wl - > current_key = key_index ;
goto done ;
}
if ( alg = = IW_ENCODE_ALG_NONE | | ( flags & IW_ENCODE_DISABLED ) ) {
pr_debug ( " %s: alg disabled \n " , __func__ ) ;
wl - > wpa_level = GELIC_WL_WPA_LEVEL_NONE ;
wl - > group_cipher_method = GELIC_WL_CIPHER_NONE ;
wl - > pairwise_cipher_method = GELIC_WL_CIPHER_NONE ;
wl - > auth_method = GELIC_EURUS_AUTH_OPEN ; /* should be open */
} else if ( alg = = IW_ENCODE_ALG_WEP ) {
pr_debug ( " %s: WEP requested \n " , __func__ ) ;
if ( flags & IW_ENCODE_OPEN ) {
pr_debug ( " %s: open key mode \n " , __func__ ) ;
wl - > auth_method = GELIC_EURUS_AUTH_OPEN ;
}
if ( flags & IW_ENCODE_RESTRICTED ) {
pr_debug ( " %s: shared key mode \n " , __func__ ) ;
wl - > auth_method = GELIC_EURUS_AUTH_SHARED ;
}
if ( IW_ENCODING_TOKEN_MAX < ext - > key_len ) {
pr_info ( " %s: key is too long %d \n " , __func__ ,
ext - > key_len ) ;
ret = - EINVAL ;
goto done ;
}
/* OK, update the key */
wl - > key_len [ key_index ] = ext - > key_len ;
memset ( wl - > key [ key_index ] , 0 , IW_ENCODING_TOKEN_MAX ) ;
memcpy ( wl - > key [ key_index ] , ext - > key , ext - > key_len ) ;
set_bit ( key_index , & wl - > key_enabled ) ;
/* remember wep info changed */
set_bit ( GELIC_WL_STAT_CONFIGURED , & wl - > stat ) ;
2008-05-30 11:52:44 +04:00
} else if ( alg = = IW_ENCODE_ALG_PMK ) {
if ( ext - > key_len ! = WPA_PSK_LEN ) {
pr_err ( " %s: PSK length wrong %d \n " , __func__ ,
ext - > key_len ) ;
2008-02-07 13:58:57 +03:00
ret = - EINVAL ;
goto done ;
}
2008-05-30 11:52:44 +04:00
memset ( wl - > psk , 0 , sizeof ( wl - > psk ) ) ;
memcpy ( wl - > psk , ext - > key , ext - > key_len ) ;
wl - > psk_len = ext - > key_len ;
wl - > psk_type = GELIC_EURUS_WPA_PSK_BIN ;
/* remember PSK configured */
set_bit ( GELIC_WL_STAT_WPA_PSK_SET , & wl - > stat ) ;
2008-02-07 13:58:57 +03:00
}
done :
spin_unlock_irqrestore ( & wl - > lock , irqflag ) ;
pr_debug ( " %s: -> \n " , __func__ ) ;
return ret ;
}
static int gelic_wl_get_encodeext ( struct net_device * netdev ,
struct iw_request_info * info ,
union iwreq_data * data , char * extra )
{
struct gelic_wl_info * wl = port_wl ( netdev_priv ( netdev ) ) ;
struct iw_point * enc = & data - > encoding ;
struct iw_encode_ext * ext = ( struct iw_encode_ext * ) extra ;
PS3: gelic: use unsigned long for irqflags
The semantic patch I used was this:
@@
expression lock;
identifier flags;
expression subclass;
@@
- unsigned int flags;
+ unsigned long flags;
...
<+...
(
spin_lock_irqsave(lock, flags)
|
_spin_lock_irqsave(lock)
|
spin_unlock_irqrestore(lock, flags)
|
_spin_unlock_irqrestore(lock, flags)
|
read_lock_irqsave(lock, flags)
|
_read_lock_irqsave(lock)
|
read_unlock_irqrestore(lock, flags)
|
_read_unlock_irqrestore(lock, flags)
|
write_lock_irqsave(lock, flags)
|
_write_lock_irqsave(lock)
|
write_unlock_irqrestore(lock, flags)
|
_write_unlock_irqrestore(lock, flags)
|
spin_lock_irqsave_nested(lock, flags, subclass)
|
_spin_lock_irqsave_nested(lock, subclass)
|
spin_unlock_irqrestore(lock, flags)
|
_spin_unlock_irqrestore(lock, flags)
|
_raw_spin_lock_flags(lock, flags)
|
__raw_spin_lock_flags(lock, flags)
)
...+>
This patch was generated using the Coccinelle framework.
Cc: Masakazu Mokuno <mokuno@sm.sony.co.jp>
Cc: Julia Lawall <julia@diku.dk>
Cc: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Vegard Nossum <vegard.nossum@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2008-07-29 15:31:47 +04:00
unsigned long irqflag ;
2008-02-07 13:58:57 +03:00
int key_index ;
int ret = 0 ;
int max_key_len ;
pr_debug ( " %s: <- \n " , __func__ ) ;
max_key_len = enc - > length - sizeof ( struct iw_encode_ext ) ;
if ( max_key_len < 0 )
return - EINVAL ;
key_index = enc - > flags & IW_ENCODE_INDEX ;
pr_debug ( " %s: key_index = %d \n " , __func__ , key_index ) ;
pr_debug ( " %s: key_len = %d \n " , __func__ , enc - > length ) ;
pr_debug ( " %s: flag=%x \n " , __func__ , enc - > flags & IW_ENCODE_FLAGS ) ;
if ( GELIC_WEP_KEYS < key_index )
return - EINVAL ;
spin_lock_irqsave ( & wl - > lock , irqflag ) ;
if ( key_index )
key_index - - ;
else
key_index = wl - > current_key ;
memset ( ext , 0 , sizeof ( struct iw_encode_ext ) ) ;
switch ( wl - > group_cipher_method ) {
case GELIC_WL_CIPHER_WEP :
ext - > alg = IW_ENCODE_ALG_WEP ;
enc - > flags | = IW_ENCODE_ENABLED ;
break ;
case GELIC_WL_CIPHER_TKIP :
ext - > alg = IW_ENCODE_ALG_TKIP ;
enc - > flags | = IW_ENCODE_ENABLED ;
break ;
case GELIC_WL_CIPHER_AES :
ext - > alg = IW_ENCODE_ALG_CCMP ;
enc - > flags | = IW_ENCODE_ENABLED ;
break ;
case GELIC_WL_CIPHER_NONE :
default :
ext - > alg = IW_ENCODE_ALG_NONE ;
enc - > flags | = IW_ENCODE_NOKEY ;
break ;
}
if ( ! ( enc - > flags & IW_ENCODE_NOKEY ) ) {
if ( max_key_len < wl - > key_len [ key_index ] ) {
ret = - E2BIG ;
goto out ;
}
if ( test_bit ( key_index , & wl - > key_enabled ) )
memcpy ( ext - > key , wl - > key [ key_index ] ,
wl - > key_len [ key_index ] ) ;
else
pr_debug ( " %s: disabled key requested ix=%d \n " ,
__func__ , key_index ) ;
}
out :
spin_unlock_irqrestore ( & wl - > lock , irqflag ) ;
pr_debug ( " %s: -> \n " , __func__ ) ;
return ret ;
}
/* SIOC{S,G}IWMODE */
static int gelic_wl_set_mode ( struct net_device * netdev ,
struct iw_request_info * info ,
union iwreq_data * data , char * extra )
{
__u32 mode = data - > mode ;
int ret ;
pr_debug ( " %s: <- \n " , __func__ ) ;
if ( mode = = IW_MODE_INFRA )
ret = 0 ;
else
ret = - EOPNOTSUPP ;
pr_debug ( " %s: -> %d \n " , __func__ , ret ) ;
return ret ;
}
static int gelic_wl_get_mode ( struct net_device * netdev ,
struct iw_request_info * info ,
union iwreq_data * data , char * extra )
{
__u32 * mode = & data - > mode ;
pr_debug ( " %s: <- \n " , __func__ ) ;
* mode = IW_MODE_INFRA ;
pr_debug ( " %s: -> \n " , __func__ ) ;
return 0 ;
}
2008-05-30 11:52:55 +04:00
# ifdef CONFIG_GELIC_WIRELESS_OLD_PSK_INTERFACE
2008-02-07 13:58:57 +03:00
/* SIOCIWFIRSTPRIV */
static int hex2bin ( u8 * str , u8 * bin , unsigned int len )
{
unsigned int i ;
static unsigned char * hex = " 0123456789ABCDEF " ;
unsigned char * p , * q ;
u8 tmp ;
if ( len ! = WPA_PSK_LEN * 2 )
return - EINVAL ;
for ( i = 0 ; i < WPA_PSK_LEN * 2 ; i + = 2 ) {
p = strchr ( hex , toupper ( str [ i ] ) ) ;
q = strchr ( hex , toupper ( str [ i + 1 ] ) ) ;
if ( ! p | | ! q ) {
pr_info ( " %s: unconvertible PSK digit=%d \n " ,
__func__ , i ) ;
return - EINVAL ;
}
tmp = ( ( p - hex ) < < 4 ) + ( q - hex ) ;
* bin + + = tmp ;
}
return 0 ;
} ;
static int gelic_wl_priv_set_psk ( struct net_device * net_dev ,
struct iw_request_info * info ,
union iwreq_data * data , char * extra )
{
struct gelic_wl_info * wl = port_wl ( netdev_priv ( net_dev ) ) ;
unsigned int len ;
PS3: gelic: use unsigned long for irqflags
The semantic patch I used was this:
@@
expression lock;
identifier flags;
expression subclass;
@@
- unsigned int flags;
+ unsigned long flags;
...
<+...
(
spin_lock_irqsave(lock, flags)
|
_spin_lock_irqsave(lock)
|
spin_unlock_irqrestore(lock, flags)
|
_spin_unlock_irqrestore(lock, flags)
|
read_lock_irqsave(lock, flags)
|
_read_lock_irqsave(lock)
|
read_unlock_irqrestore(lock, flags)
|
_read_unlock_irqrestore(lock, flags)
|
write_lock_irqsave(lock, flags)
|
_write_lock_irqsave(lock)
|
write_unlock_irqrestore(lock, flags)
|
_write_unlock_irqrestore(lock, flags)
|
spin_lock_irqsave_nested(lock, flags, subclass)
|
_spin_lock_irqsave_nested(lock, subclass)
|
spin_unlock_irqrestore(lock, flags)
|
_spin_unlock_irqrestore(lock, flags)
|
_raw_spin_lock_flags(lock, flags)
|
__raw_spin_lock_flags(lock, flags)
)
...+>
This patch was generated using the Coccinelle framework.
Cc: Masakazu Mokuno <mokuno@sm.sony.co.jp>
Cc: Julia Lawall <julia@diku.dk>
Cc: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Vegard Nossum <vegard.nossum@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2008-07-29 15:31:47 +04:00
unsigned long irqflag ;
2008-02-07 13:58:57 +03:00
int ret = 0 ;
pr_debug ( " %s:<- len=%d \n " , __func__ , data - > data . length ) ;
len = data - > data . length - 1 ;
if ( len < = 2 )
return - EINVAL ;
spin_lock_irqsave ( & wl - > lock , irqflag ) ;
if ( extra [ 0 ] = = ' " ' & & extra [ len - 1 ] = = ' " ' ) {
pr_debug ( " %s: passphrase mode \n " , __func__ ) ;
/* pass phrase */
if ( GELIC_WL_EURUS_PSK_MAX_LEN < ( len - 2 ) ) {
pr_info ( " %s: passphrase too long \n " , __func__ ) ;
ret = - E2BIG ;
goto out ;
}
memset ( wl - > psk , 0 , sizeof ( wl - > psk ) ) ;
wl - > psk_len = len - 2 ;
memcpy ( wl - > psk , & ( extra [ 1 ] ) , wl - > psk_len ) ;
wl - > psk_type = GELIC_EURUS_WPA_PSK_PASSPHRASE ;
} else {
ret = hex2bin ( extra , wl - > psk , len ) ;
if ( ret )
goto out ;
wl - > psk_len = WPA_PSK_LEN ;
wl - > psk_type = GELIC_EURUS_WPA_PSK_BIN ;
}
set_bit ( GELIC_WL_STAT_WPA_PSK_SET , & wl - > stat ) ;
out :
spin_unlock_irqrestore ( & wl - > lock , irqflag ) ;
pr_debug ( " %s:-> \n " , __func__ ) ;
return ret ;
}
static int gelic_wl_priv_get_psk ( struct net_device * net_dev ,
struct iw_request_info * info ,
union iwreq_data * data , char * extra )
{
struct gelic_wl_info * wl = port_wl ( netdev_priv ( net_dev ) ) ;
char * p ;
PS3: gelic: use unsigned long for irqflags
The semantic patch I used was this:
@@
expression lock;
identifier flags;
expression subclass;
@@
- unsigned int flags;
+ unsigned long flags;
...
<+...
(
spin_lock_irqsave(lock, flags)
|
_spin_lock_irqsave(lock)
|
spin_unlock_irqrestore(lock, flags)
|
_spin_unlock_irqrestore(lock, flags)
|
read_lock_irqsave(lock, flags)
|
_read_lock_irqsave(lock)
|
read_unlock_irqrestore(lock, flags)
|
_read_unlock_irqrestore(lock, flags)
|
write_lock_irqsave(lock, flags)
|
_write_lock_irqsave(lock)
|
write_unlock_irqrestore(lock, flags)
|
_write_unlock_irqrestore(lock, flags)
|
spin_lock_irqsave_nested(lock, flags, subclass)
|
_spin_lock_irqsave_nested(lock, subclass)
|
spin_unlock_irqrestore(lock, flags)
|
_spin_unlock_irqrestore(lock, flags)
|
_raw_spin_lock_flags(lock, flags)
|
__raw_spin_lock_flags(lock, flags)
)
...+>
This patch was generated using the Coccinelle framework.
Cc: Masakazu Mokuno <mokuno@sm.sony.co.jp>
Cc: Julia Lawall <julia@diku.dk>
Cc: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Vegard Nossum <vegard.nossum@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2008-07-29 15:31:47 +04:00
unsigned long irqflag ;
2008-02-07 13:58:57 +03:00
unsigned int i ;
pr_debug ( " %s:<- \n " , __func__ ) ;
if ( ! capable ( CAP_NET_ADMIN ) )
return - EPERM ;
spin_lock_irqsave ( & wl - > lock , irqflag ) ;
p = extra ;
if ( test_bit ( GELIC_WL_STAT_WPA_PSK_SET , & wl - > stat ) ) {
if ( wl - > psk_type = = GELIC_EURUS_WPA_PSK_BIN ) {
for ( i = 0 ; i < wl - > psk_len ; i + + ) {
sprintf ( p , " %02xu " , wl - > psk [ i ] ) ;
p + = 2 ;
}
* p = ' \0 ' ;
data - > data . length = wl - > psk_len * 2 ;
} else {
* p + + = ' " ' ;
memcpy ( p , wl - > psk , wl - > psk_len ) ;
p + = wl - > psk_len ;
* p + + = ' " ' ;
* p = ' \0 ' ;
data - > data . length = wl - > psk_len + 2 ;
}
} else
/* no psk set */
data - > data . length = 0 ;
spin_unlock_irqrestore ( & wl - > lock , irqflag ) ;
pr_debug ( " %s:-> %d \n " , __func__ , data - > data . length ) ;
return 0 ;
}
2008-05-30 11:52:55 +04:00
# endif
2008-02-07 13:58:57 +03:00
/* SIOCGIWNICKN */
static int gelic_wl_get_nick ( struct net_device * net_dev ,
struct iw_request_info * info ,
union iwreq_data * data , char * extra )
{
strcpy ( extra , " gelic_wl " ) ;
data - > data . length = strlen ( extra ) ;
data - > data . flags = 1 ;
return 0 ;
}
/* --- */
static struct iw_statistics * gelic_wl_get_wireless_stats (
struct net_device * netdev )
{
struct gelic_wl_info * wl = port_wl ( netdev_priv ( netdev ) ) ;
struct gelic_eurus_cmd * cmd ;
struct iw_statistics * is ;
struct gelic_eurus_rssi_info * rssi ;
2008-05-30 11:27:24 +04:00
void * buf ;
2008-02-07 13:58:57 +03:00
pr_debug ( " %s: <- \n " , __func__ ) ;
2008-05-30 11:27:24 +04:00
buf = ( void * ) __get_free_page ( GFP_KERNEL ) ;
if ( ! buf )
return NULL ;
2008-02-07 13:58:57 +03:00
is = & wl - > iwstat ;
memset ( is , 0 , sizeof ( * is ) ) ;
cmd = gelic_eurus_sync_cmd ( wl , GELIC_EURUS_CMD_GET_RSSI_CFG ,
2008-05-30 11:27:24 +04:00
buf , sizeof ( * rssi ) ) ;
2008-02-07 13:58:57 +03:00
if ( cmd & & ! cmd - > status & & ! cmd - > cmd_status ) {
2008-05-30 11:27:24 +04:00
rssi = buf ;
2008-02-07 13:58:57 +03:00
is - > qual . level = be16_to_cpu ( rssi - > rssi ) ;
is - > qual . updated = IW_QUAL_LEVEL_UPDATED |
IW_QUAL_QUAL_INVALID | IW_QUAL_NOISE_INVALID ;
} else
/* not associated */
is - > qual . updated = IW_QUAL_ALL_INVALID ;
kfree ( cmd ) ;
2008-05-30 11:27:24 +04:00
free_page ( ( unsigned long ) buf ) ;
2008-02-07 13:58:57 +03:00
pr_debug ( " %s: -> \n " , __func__ ) ;
return is ;
}
/*
* scanning helpers
*/
2008-06-02 07:49:16 +04:00
static int gelic_wl_start_scan ( struct gelic_wl_info * wl , int always_scan ,
u8 * essid , size_t essid_len )
2008-02-07 13:58:57 +03:00
{
struct gelic_eurus_cmd * cmd ;
int ret = 0 ;
2008-06-02 07:49:16 +04:00
void * buf = NULL ;
size_t len ;
2008-02-07 13:58:57 +03:00
pr_debug ( " %s: <- always=%d \n " , __func__ , always_scan ) ;
2008-05-22 11:00:01 +04:00
if ( mutex_lock_interruptible ( & wl - > scan_lock ) )
2008-02-07 13:58:57 +03:00
return - ERESTARTSYS ;
/*
* If already a scan in progress , do not trigger more
*/
if ( wl - > scan_stat = = GELIC_WL_SCAN_STAT_SCANNING ) {
pr_debug ( " %s: scanning now \n " , __func__ ) ;
goto out ;
}
init_completion ( & wl - > scan_done ) ;
/*
* If we have already a bss list , don ' t try to get new
*/
if ( ! always_scan & & wl - > scan_stat = = GELIC_WL_SCAN_STAT_GOT_LIST ) {
pr_debug ( " %s: already has the list \n " , __func__ ) ;
complete ( & wl - > scan_done ) ;
goto out ;
}
2008-06-02 07:49:16 +04:00
/* ESSID scan ? */
if ( essid_len & & essid ) {
buf = ( void * ) __get_free_page ( GFP_KERNEL ) ;
if ( ! buf ) {
ret = - ENOMEM ;
goto out ;
}
len = IW_ESSID_MAX_SIZE ; /* hypervisor always requires 32 */
memset ( buf , 0 , len ) ;
memcpy ( buf , essid , essid_len ) ;
pr_debug ( " %s: essid scan='%s' \n " , __func__ , ( char * ) buf ) ;
} else
len = 0 ;
2008-02-07 13:58:57 +03:00
/*
* issue start scan request
*/
wl - > scan_stat = GELIC_WL_SCAN_STAT_SCANNING ;
cmd = gelic_eurus_sync_cmd ( wl , GELIC_EURUS_CMD_START_SCAN ,
2008-06-02 07:49:16 +04:00
buf , len ) ;
2008-02-07 13:58:57 +03:00
if ( ! cmd | | cmd - > status | | cmd - > cmd_status ) {
wl - > scan_stat = GELIC_WL_SCAN_STAT_INIT ;
complete ( & wl - > scan_done ) ;
ret = - ENOMEM ;
goto out ;
}
kfree ( cmd ) ;
out :
2008-06-02 07:49:16 +04:00
free_page ( ( unsigned long ) buf ) ;
2008-05-22 11:00:01 +04:00
mutex_unlock ( & wl - > scan_lock ) ;
2008-02-07 13:58:57 +03:00
pr_debug ( " %s: -> \n " , __func__ ) ;
return ret ;
}
/*
* retrieve scan result from the chip ( hypervisor )
* this function is invoked by schedule work .
*/
static void gelic_wl_scan_complete_event ( struct gelic_wl_info * wl )
{
struct gelic_eurus_cmd * cmd = NULL ;
struct gelic_wl_scan_info * target , * tmp ;
struct gelic_wl_scan_info * oldest = NULL ;
struct gelic_eurus_scan_info * scan_info ;
unsigned int scan_info_size ;
union iwreq_data data ;
unsigned long this_time = jiffies ;
unsigned int data_len , i , found , r ;
2008-05-30 11:27:24 +04:00
void * buf ;
2008-02-07 13:58:57 +03:00
pr_debug ( " %s:start \n " , __func__ ) ;
2008-05-22 11:00:01 +04:00
mutex_lock ( & wl - > scan_lock ) ;
2008-02-07 13:58:57 +03:00
2008-05-30 11:27:24 +04:00
buf = ( void * ) __get_free_page ( GFP_KERNEL ) ;
if ( ! buf ) {
pr_info ( " %s: scan buffer alloc failed \n " , __func__ ) ;
goto out ;
}
2008-02-07 13:58:57 +03:00
if ( wl - > scan_stat ! = GELIC_WL_SCAN_STAT_SCANNING ) {
/*
* stop ( ) may be called while scanning , ignore result
*/
pr_debug ( " %s: scan complete when stat != scanning(%d) \n " ,
__func__ , wl - > scan_stat ) ;
goto out ;
}
cmd = gelic_eurus_sync_cmd ( wl , GELIC_EURUS_CMD_GET_SCAN ,
2008-05-30 11:27:24 +04:00
buf , PAGE_SIZE ) ;
2008-02-07 13:58:57 +03:00
if ( ! cmd | | cmd - > status | | cmd - > cmd_status ) {
wl - > scan_stat = GELIC_WL_SCAN_STAT_INIT ;
pr_info ( " %s:cmd failed \n " , __func__ ) ;
kfree ( cmd ) ;
goto out ;
}
data_len = cmd - > size ;
pr_debug ( " %s: data_len = %d \n " , __func__ , data_len ) ;
kfree ( cmd ) ;
/* OK, bss list retrieved */
wl - > scan_stat = GELIC_WL_SCAN_STAT_GOT_LIST ;
/* mark all entries are old */
list_for_each_entry_safe ( target , tmp , & wl - > network_list , list ) {
target - > valid = 0 ;
/* expire too old entries */
if ( time_before ( target - > last_scanned + wl - > scan_age ,
this_time ) ) {
kfree ( target - > hwinfo ) ;
target - > hwinfo = NULL ;
list_move_tail ( & target - > list , & wl - > network_free_list ) ;
}
}
/* put them in the newtork_list */
2008-05-30 11:27:24 +04:00
for ( i = 0 , scan_info_size = 0 , scan_info = buf ;
2008-03-11 07:15:44 +03:00
scan_info_size < data_len ;
i + + , scan_info_size + = be16_to_cpu ( scan_info - > size ) ,
scan_info = ( void * ) scan_info + be16_to_cpu ( scan_info - > size ) ) {
2008-10-28 01:59:26 +03:00
pr_debug ( " %s:size=%d bssid=%pM scan_info=%p \n " , __func__ ,
2008-02-07 13:58:57 +03:00
be16_to_cpu ( scan_info - > size ) ,
2008-10-28 01:59:26 +03:00
& scan_info - > bssid [ 2 ] , scan_info ) ;
2008-03-11 07:15:44 +03:00
/*
* The wireless firmware may return invalid channel 0 and / or
* invalid rate if the AP emits zero length SSID ie . As this
* scan information is useless , ignore it
*/
if ( ! be16_to_cpu ( scan_info - > channel ) | | ! scan_info - > rate [ 0 ] ) {
pr_debug ( " %s: invalid scan info \n " , __func__ ) ;
continue ;
}
2008-02-07 13:58:57 +03:00
found = 0 ;
oldest = NULL ;
list_for_each_entry ( target , & wl - > network_list , list ) {
if ( ! compare_ether_addr ( & target - > hwinfo - > bssid [ 2 ] ,
& scan_info - > bssid [ 2 ] ) ) {
found = 1 ;
pr_debug ( " %s: same BBS found scanned list \n " ,
__func__ ) ;
break ;
}
if ( ! oldest | |
( target - > last_scanned < oldest - > last_scanned ) )
oldest = target ;
}
if ( ! found ) {
/* not found in the list */
if ( list_empty ( & wl - > network_free_list ) ) {
/* expire oldest */
target = oldest ;
} else {
target = list_entry ( wl - > network_free_list . next ,
struct gelic_wl_scan_info ,
list ) ;
}
}
/* update the item */
target - > last_scanned = this_time ;
target - > valid = 1 ;
target - > eurus_index = i ;
kfree ( target - > hwinfo ) ;
target - > hwinfo = kzalloc ( be16_to_cpu ( scan_info - > size ) ,
GFP_KERNEL ) ;
if ( ! target - > hwinfo ) {
pr_info ( " %s: kzalloc failed \n " , __func__ ) ;
continue ;
}
/* copy hw scan info */
memcpy ( target - > hwinfo , scan_info , scan_info - > size ) ;
target - > essid_len = strnlen ( scan_info - > essid ,
sizeof ( scan_info - > essid ) ) ;
target - > rate_len = 0 ;
2008-10-31 00:09:54 +03:00
for ( r = 0 ; r < 12 ; r + + )
2008-02-07 13:58:57 +03:00
if ( scan_info - > rate [ r ] )
target - > rate_len + + ;
if ( 8 < target - > rate_len )
pr_info ( " %s: AP returns %d rates \n " , __func__ ,
target - > rate_len ) ;
target - > rate_ext_len = 0 ;
2008-10-31 00:09:54 +03:00
for ( r = 0 ; r < 16 ; r + + )
2008-02-07 13:58:57 +03:00
if ( scan_info - > ext_rate [ r ] )
target - > rate_ext_len + + ;
list_move_tail ( & target - > list , & wl - > network_list ) ;
}
memset ( & data , 0 , sizeof ( data ) ) ;
wireless_send_event ( port_to_netdev ( wl_port ( wl ) ) , SIOCGIWSCAN , & data ,
NULL ) ;
out :
2008-05-30 11:27:24 +04:00
free_page ( ( unsigned long ) buf ) ;
2008-02-07 13:58:57 +03:00
complete ( & wl - > scan_done ) ;
2008-05-22 11:00:01 +04:00
mutex_unlock ( & wl - > scan_lock ) ;
2008-02-07 13:58:57 +03:00
pr_debug ( " %s:end \n " , __func__ ) ;
}
/*
* Select an appropriate bss from current scan list regarding
* current settings from userspace .
* The caller must hold wl - > scan_lock ,
* and on the state of wl - > scan_state = = GELIC_WL_SCAN_GOT_LIST
*/
static void update_best ( struct gelic_wl_scan_info * * best ,
struct gelic_wl_scan_info * candid ,
int * best_weight ,
int * weight )
{
if ( * best_weight < + + ( * weight ) ) {
* best_weight = * weight ;
* best = candid ;
}
}
static
struct gelic_wl_scan_info * gelic_wl_find_best_bss ( struct gelic_wl_info * wl )
{
struct gelic_wl_scan_info * scan_info ;
struct gelic_wl_scan_info * best_bss ;
int weight , best_weight ;
u16 security ;
pr_debug ( " %s: <- \n " , __func__ ) ;
best_bss = NULL ;
best_weight = 0 ;
list_for_each_entry ( scan_info , & wl - > network_list , list ) {
pr_debug ( " %s: station %p \n " , __func__ , scan_info ) ;
if ( ! scan_info - > valid ) {
pr_debug ( " %s: station invalid \n " , __func__ ) ;
continue ;
}
/* If bss specified, check it only */
if ( test_bit ( GELIC_WL_STAT_BSSID_SET , & wl - > stat ) ) {
if ( ! compare_ether_addr ( & scan_info - > hwinfo - > bssid [ 2 ] ,
wl - > bssid ) ) {
best_bss = scan_info ;
pr_debug ( " %s: bssid matched \n " , __func__ ) ;
break ;
} else {
pr_debug ( " %s: bssid unmached \n " , __func__ ) ;
continue ;
}
}
weight = 0 ;
/* security */
security = be16_to_cpu ( scan_info - > hwinfo - > security ) &
GELIC_EURUS_SCAN_SEC_MASK ;
if ( wl - > wpa_level = = GELIC_WL_WPA_LEVEL_WPA2 ) {
if ( security = = GELIC_EURUS_SCAN_SEC_WPA2 )
update_best ( & best_bss , scan_info ,
& best_weight , & weight ) ;
else
continue ;
} else if ( wl - > wpa_level = = GELIC_WL_WPA_LEVEL_WPA ) {
if ( security = = GELIC_EURUS_SCAN_SEC_WPA )
update_best ( & best_bss , scan_info ,
& best_weight , & weight ) ;
else
continue ;
} else if ( wl - > wpa_level = = GELIC_WL_WPA_LEVEL_NONE & &
wl - > group_cipher_method = = GELIC_WL_CIPHER_WEP ) {
if ( security = = GELIC_EURUS_SCAN_SEC_WEP )
update_best ( & best_bss , scan_info ,
& best_weight , & weight ) ;
else
continue ;
}
/* If ESSID is set, check it */
if ( test_bit ( GELIC_WL_STAT_ESSID_SET , & wl - > stat ) ) {
if ( ( scan_info - > essid_len = = wl - > essid_len ) & &
! strncmp ( wl - > essid ,
scan_info - > hwinfo - > essid ,
scan_info - > essid_len ) )
update_best ( & best_bss , scan_info ,
& best_weight , & weight ) ;
else
continue ;
}
}
# ifdef DEBUG
pr_debug ( " %s: -> bss=%p \n " , __func__ , best_bss ) ;
if ( best_bss ) {
2008-10-28 01:59:26 +03:00
pr_debug ( " %s:addr=%pM \n " , __func__ ,
& best_bss - > hwinfo - > bssid [ 2 ] ) ;
2008-02-07 13:58:57 +03:00
}
# endif
return best_bss ;
}
/*
* Setup WEP configuration to the chip
* The caller must hold wl - > scan_lock ,
* and on the state of wl - > scan_state = = GELIC_WL_SCAN_GOT_LIST
*/
static int gelic_wl_do_wep_setup ( struct gelic_wl_info * wl )
{
unsigned int i ;
struct gelic_eurus_wep_cfg * wep ;
struct gelic_eurus_cmd * cmd ;
int wep104 = 0 ;
int have_key = 0 ;
int ret = 0 ;
pr_debug ( " %s: <- \n " , __func__ ) ;
/* we can assume no one should uses the buffer */
2008-05-30 11:27:24 +04:00
wep = ( struct gelic_eurus_wep_cfg * ) __get_free_page ( GFP_KERNEL ) ;
if ( ! wep )
return - ENOMEM ;
2008-02-07 13:58:57 +03:00
memset ( wep , 0 , sizeof ( * wep ) ) ;
if ( wl - > group_cipher_method = = GELIC_WL_CIPHER_WEP ) {
pr_debug ( " %s: WEP mode \n " , __func__ ) ;
for ( i = 0 ; i < GELIC_WEP_KEYS ; i + + ) {
if ( ! test_bit ( i , & wl - > key_enabled ) )
continue ;
pr_debug ( " %s: key#%d enabled \n " , __func__ , i ) ;
have_key = 1 ;
if ( wl - > key_len [ i ] = = 13 )
wep104 = 1 ;
else if ( wl - > key_len [ i ] ! = 5 ) {
pr_info ( " %s: wrong wep key[%d]=%d \n " ,
__func__ , i , wl - > key_len [ i ] ) ;
ret = - EINVAL ;
goto out ;
}
memcpy ( wep - > key [ i ] , wl - > key [ i ] , wl - > key_len [ i ] ) ;
}
if ( ! have_key ) {
pr_info ( " %s: all wep key disabled \n " , __func__ ) ;
ret = - EINVAL ;
goto out ;
}
if ( wep104 ) {
pr_debug ( " %s: 104bit key \n " , __func__ ) ;
wep - > security = cpu_to_be16 ( GELIC_EURUS_WEP_SEC_104BIT ) ;
} else {
pr_debug ( " %s: 40bit key \n " , __func__ ) ;
wep - > security = cpu_to_be16 ( GELIC_EURUS_WEP_SEC_40BIT ) ;
}
} else {
pr_debug ( " %s: NO encryption \n " , __func__ ) ;
wep - > security = cpu_to_be16 ( GELIC_EURUS_WEP_SEC_NONE ) ;
}
/* issue wep setup */
cmd = gelic_eurus_sync_cmd ( wl , GELIC_EURUS_CMD_SET_WEP_CFG ,
wep , sizeof ( * wep ) ) ;
if ( ! cmd )
ret = - ENOMEM ;
else if ( cmd - > status | | cmd - > cmd_status )
ret = - ENXIO ;
kfree ( cmd ) ;
out :
2008-05-30 11:27:24 +04:00
free_page ( ( unsigned long ) wep ) ;
2008-02-07 13:58:57 +03:00
pr_debug ( " %s: -> \n " , __func__ ) ;
return ret ;
}
# ifdef DEBUG
static const char * wpasecstr ( enum gelic_eurus_wpa_security sec )
{
switch ( sec ) {
case GELIC_EURUS_WPA_SEC_NONE :
return " NONE " ;
break ;
case GELIC_EURUS_WPA_SEC_WPA_TKIP_TKIP :
return " WPA_TKIP_TKIP " ;
break ;
case GELIC_EURUS_WPA_SEC_WPA_TKIP_AES :
return " WPA_TKIP_AES " ;
break ;
case GELIC_EURUS_WPA_SEC_WPA_AES_AES :
return " WPA_AES_AES " ;
break ;
case GELIC_EURUS_WPA_SEC_WPA2_TKIP_TKIP :
return " WPA2_TKIP_TKIP " ;
break ;
case GELIC_EURUS_WPA_SEC_WPA2_TKIP_AES :
return " WPA2_TKIP_AES " ;
break ;
case GELIC_EURUS_WPA_SEC_WPA2_AES_AES :
return " WPA2_AES_AES " ;
break ;
}
return " " ;
} ;
# endif
static int gelic_wl_do_wpa_setup ( struct gelic_wl_info * wl )
{
struct gelic_eurus_wpa_cfg * wpa ;
struct gelic_eurus_cmd * cmd ;
u16 security ;
int ret = 0 ;
pr_debug ( " %s: <- \n " , __func__ ) ;
/* we can assume no one should uses the buffer */
2008-05-30 11:27:24 +04:00
wpa = ( struct gelic_eurus_wpa_cfg * ) __get_free_page ( GFP_KERNEL ) ;
if ( ! wpa )
return - ENOMEM ;
2008-02-07 13:58:57 +03:00
memset ( wpa , 0 , sizeof ( * wpa ) ) ;
if ( ! test_bit ( GELIC_WL_STAT_WPA_PSK_SET , & wl - > stat ) )
pr_info ( " %s: PSK not configured yet \n " , __func__ ) ;
/* copy key */
memcpy ( wpa - > psk , wl - > psk , wl - > psk_len ) ;
/* set security level */
if ( wl - > wpa_level = = GELIC_WL_WPA_LEVEL_WPA2 ) {
if ( wl - > group_cipher_method = = GELIC_WL_CIPHER_AES ) {
security = GELIC_EURUS_WPA_SEC_WPA2_AES_AES ;
} else {
if ( wl - > pairwise_cipher_method = = GELIC_WL_CIPHER_AES & &
precise_ie ( ) )
security = GELIC_EURUS_WPA_SEC_WPA2_TKIP_AES ;
else
security = GELIC_EURUS_WPA_SEC_WPA2_TKIP_TKIP ;
}
} else {
if ( wl - > group_cipher_method = = GELIC_WL_CIPHER_AES ) {
security = GELIC_EURUS_WPA_SEC_WPA_AES_AES ;
} else {
if ( wl - > pairwise_cipher_method = = GELIC_WL_CIPHER_AES & &
precise_ie ( ) )
security = GELIC_EURUS_WPA_SEC_WPA_TKIP_AES ;
else
security = GELIC_EURUS_WPA_SEC_WPA_TKIP_TKIP ;
}
}
wpa - > security = cpu_to_be16 ( security ) ;
/* PSK type */
wpa - > psk_type = cpu_to_be16 ( wl - > psk_type ) ;
# ifdef DEBUG
pr_debug ( " %s: sec=%s psktype=%s \n n " , __func__ ,
wpasecstr ( wpa - > security ) ,
( wpa - > psk_type = = GELIC_EURUS_WPA_PSK_BIN ) ?
" BIN " : " passphrase " ) ;
#if 0
/*
* don ' t enable here if you plan to submit
* the debug log because this dumps your precious
* passphrase / key .
*/
pr_debug ( " %s: psk=%s \n " ,
( wpa - > psk_type = = GELIC_EURUS_WPA_PSK_BIN ) ?
( char * ) " N/A " : ( char * ) wpa - > psk ) ;
# endif
# endif
/* issue wpa setup */
cmd = gelic_eurus_sync_cmd ( wl , GELIC_EURUS_CMD_SET_WPA_CFG ,
wpa , sizeof ( * wpa ) ) ;
if ( ! cmd )
ret = - ENOMEM ;
else if ( cmd - > status | | cmd - > cmd_status )
ret = - ENXIO ;
kfree ( cmd ) ;
2008-05-30 11:27:24 +04:00
free_page ( ( unsigned long ) wpa ) ;
2008-02-07 13:58:57 +03:00
pr_debug ( " %s: --> %d \n " , __func__ , ret ) ;
return ret ;
}
/*
* Start association . caller must hold assoc_stat_lock
*/
static int gelic_wl_associate_bss ( struct gelic_wl_info * wl ,
struct gelic_wl_scan_info * bss )
{
struct gelic_eurus_cmd * cmd ;
struct gelic_eurus_common_cfg * common ;
int ret = 0 ;
unsigned long rc ;
pr_debug ( " %s: <- \n " , __func__ ) ;
/* do common config */
2008-05-30 11:27:24 +04:00
common = ( struct gelic_eurus_common_cfg * ) __get_free_page ( GFP_KERNEL ) ;
if ( ! common )
return - ENOMEM ;
2008-02-07 13:58:57 +03:00
memset ( common , 0 , sizeof ( * common ) ) ;
common - > bss_type = cpu_to_be16 ( GELIC_EURUS_BSS_INFRA ) ;
common - > op_mode = cpu_to_be16 ( GELIC_EURUS_OPMODE_11BG ) ;
common - > scan_index = cpu_to_be16 ( bss - > eurus_index ) ;
switch ( wl - > auth_method ) {
case GELIC_EURUS_AUTH_OPEN :
common - > auth_method = cpu_to_be16 ( GELIC_EURUS_AUTH_OPEN ) ;
break ;
case GELIC_EURUS_AUTH_SHARED :
common - > auth_method = cpu_to_be16 ( GELIC_EURUS_AUTH_SHARED ) ;
break ;
}
# ifdef DEBUG
scan_list_dump ( wl ) ;
# endif
pr_debug ( " %s: common cfg index=%d bsstype=%d auth=%d \n " , __func__ ,
be16_to_cpu ( common - > scan_index ) ,
be16_to_cpu ( common - > bss_type ) ,
be16_to_cpu ( common - > auth_method ) ) ;
cmd = gelic_eurus_sync_cmd ( wl , GELIC_EURUS_CMD_SET_COMMON_CFG ,
common , sizeof ( * common ) ) ;
if ( ! cmd | | cmd - > status | | cmd - > cmd_status ) {
ret = - ENOMEM ;
kfree ( cmd ) ;
goto out ;
}
kfree ( cmd ) ;
/* WEP/WPA */
switch ( wl - > wpa_level ) {
case GELIC_WL_WPA_LEVEL_NONE :
/* If WEP or no security, setup WEP config */
ret = gelic_wl_do_wep_setup ( wl ) ;
break ;
case GELIC_WL_WPA_LEVEL_WPA :
case GELIC_WL_WPA_LEVEL_WPA2 :
ret = gelic_wl_do_wpa_setup ( wl ) ;
break ;
} ;
if ( ret ) {
pr_debug ( " %s: WEP/WPA setup failed %d \n " , __func__ ,
ret ) ;
}
/* start association */
init_completion ( & wl - > assoc_done ) ;
wl - > assoc_stat = GELIC_WL_ASSOC_STAT_ASSOCIATING ;
cmd = gelic_eurus_sync_cmd ( wl , GELIC_EURUS_CMD_ASSOC ,
NULL , 0 ) ;
if ( ! cmd | | cmd - > status | | cmd - > cmd_status ) {
pr_debug ( " %s: assoc request failed \n " , __func__ ) ;
wl - > assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN ;
kfree ( cmd ) ;
ret = - ENOMEM ;
gelic_wl_send_iwap_event ( wl , NULL ) ;
goto out ;
}
kfree ( cmd ) ;
/* wait for connected event */
rc = wait_for_completion_timeout ( & wl - > assoc_done , HZ * 4 ) ; /*FIXME*/
if ( ! rc ) {
/* timeouted. Maybe key or cyrpt mode is wrong */
pr_info ( " %s: connect timeout \n " , __func__ ) ;
cmd = gelic_eurus_sync_cmd ( wl , GELIC_EURUS_CMD_DISASSOC ,
NULL , 0 ) ;
kfree ( cmd ) ;
wl - > assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN ;
gelic_wl_send_iwap_event ( wl , NULL ) ;
ret = - ENXIO ;
} else {
wl - > assoc_stat = GELIC_WL_ASSOC_STAT_ASSOCIATED ;
/* copy bssid */
memcpy ( wl - > active_bssid , & bss - > hwinfo - > bssid [ 2 ] , ETH_ALEN ) ;
/* send connect event */
gelic_wl_send_iwap_event ( wl , wl - > active_bssid ) ;
pr_info ( " %s: connected \n " , __func__ ) ;
}
out :
2008-05-30 11:27:24 +04:00
free_page ( ( unsigned long ) common ) ;
2008-02-07 13:58:57 +03:00
pr_debug ( " %s: -> \n " , __func__ ) ;
return ret ;
}
/*
* connected event
*/
static void gelic_wl_connected_event ( struct gelic_wl_info * wl ,
u64 event )
{
u64 desired_event = 0 ;
switch ( wl - > wpa_level ) {
case GELIC_WL_WPA_LEVEL_NONE :
desired_event = GELIC_LV1_WL_EVENT_CONNECTED ;
break ;
case GELIC_WL_WPA_LEVEL_WPA :
case GELIC_WL_WPA_LEVEL_WPA2 :
desired_event = GELIC_LV1_WL_EVENT_WPA_CONNECTED ;
break ;
}
if ( desired_event = = event ) {
pr_debug ( " %s: completed \n " , __func__ ) ;
complete ( & wl - > assoc_done ) ;
netif_carrier_on ( port_to_netdev ( wl_port ( wl ) ) ) ;
} else
pr_debug ( " %s: event %#lx under wpa \n " ,
__func__ , event ) ;
}
/*
* disconnect event
*/
static void gelic_wl_disconnect_event ( struct gelic_wl_info * wl ,
u64 event )
{
struct gelic_eurus_cmd * cmd ;
int lock ;
/*
* If we fall here in the middle of association ,
* associate_bss ( ) should be waiting for complation of
* wl - > assoc_done .
* As it waits with timeout , just leave assoc_done
* uncompleted , then it terminates with timeout
*/
2008-05-22 11:00:02 +04:00
if ( ! mutex_trylock ( & wl - > assoc_stat_lock ) ) {
2008-02-07 13:58:57 +03:00
pr_debug ( " %s: already locked \n " , __func__ ) ;
lock = 0 ;
} else {
pr_debug ( " %s: obtain lock \n " , __func__ ) ;
lock = 1 ;
}
cmd = gelic_eurus_sync_cmd ( wl , GELIC_EURUS_CMD_DISASSOC , NULL , 0 ) ;
kfree ( cmd ) ;
/* send disconnected event to the supplicant */
if ( wl - > assoc_stat = = GELIC_WL_ASSOC_STAT_ASSOCIATED )
gelic_wl_send_iwap_event ( wl , NULL ) ;
wl - > assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN ;
netif_carrier_off ( port_to_netdev ( wl_port ( wl ) ) ) ;
if ( lock )
2008-05-22 11:00:02 +04:00
mutex_unlock ( & wl - > assoc_stat_lock ) ;
2008-02-07 13:58:57 +03:00
}
/*
* event worker
*/
# ifdef DEBUG
static const char * eventstr ( enum gelic_lv1_wl_event event )
{
static char buf [ 32 ] ;
char * ret ;
if ( event & GELIC_LV1_WL_EVENT_DEVICE_READY )
ret = " EURUS_READY " ;
else if ( event & GELIC_LV1_WL_EVENT_SCAN_COMPLETED )
ret = " SCAN_COMPLETED " ;
else if ( event & GELIC_LV1_WL_EVENT_DEAUTH )
ret = " DEAUTH " ;
else if ( event & GELIC_LV1_WL_EVENT_BEACON_LOST )
ret = " BEACON_LOST " ;
else if ( event & GELIC_LV1_WL_EVENT_CONNECTED )
ret = " CONNECTED " ;
else if ( event & GELIC_LV1_WL_EVENT_WPA_CONNECTED )
ret = " WPA_CONNECTED " ;
else if ( event & GELIC_LV1_WL_EVENT_WPA_ERROR )
ret = " WPA_ERROR " ;
else {
sprintf ( buf , " Unknown(%#x) " , event ) ;
ret = buf ;
}
return ret ;
}
# else
static const char * eventstr ( enum gelic_lv1_wl_event event )
{
return NULL ;
}
# endif
static void gelic_wl_event_worker ( struct work_struct * work )
{
struct gelic_wl_info * wl ;
struct gelic_port * port ;
u64 event , tmp ;
int status ;
pr_debug ( " %s:start \n " , __func__ ) ;
wl = container_of ( work , struct gelic_wl_info , event_work . work ) ;
port = wl_port ( wl ) ;
while ( 1 ) {
status = lv1_net_control ( bus_id ( port - > card ) , dev_id ( port - > card ) ,
GELIC_LV1_GET_WLAN_EVENT , 0 , 0 , 0 ,
& event , & tmp ) ;
if ( status ) {
if ( status ! = LV1_NO_ENTRY )
pr_debug ( " %s:wlan event failed %d \n " ,
__func__ , status ) ;
/* got all events */
pr_debug ( " %s:end \n " , __func__ ) ;
return ;
}
pr_debug ( " %s: event=%s \n " , __func__ , eventstr ( event ) ) ;
switch ( event ) {
case GELIC_LV1_WL_EVENT_SCAN_COMPLETED :
gelic_wl_scan_complete_event ( wl ) ;
break ;
case GELIC_LV1_WL_EVENT_BEACON_LOST :
case GELIC_LV1_WL_EVENT_DEAUTH :
gelic_wl_disconnect_event ( wl , event ) ;
break ;
case GELIC_LV1_WL_EVENT_CONNECTED :
case GELIC_LV1_WL_EVENT_WPA_CONNECTED :
gelic_wl_connected_event ( wl , event ) ;
break ;
default :
break ;
}
} /* while */
}
/*
* association worker
*/
static void gelic_wl_assoc_worker ( struct work_struct * work )
{
struct gelic_wl_info * wl ;
struct gelic_wl_scan_info * best_bss ;
int ret ;
2008-06-02 07:49:16 +04:00
unsigned long irqflag ;
u8 * essid ;
size_t essid_len ;
2008-02-07 13:58:57 +03:00
wl = container_of ( work , struct gelic_wl_info , assoc_work . work ) ;
2008-05-22 11:00:02 +04:00
mutex_lock ( & wl - > assoc_stat_lock ) ;
2008-02-07 13:58:57 +03:00
if ( wl - > assoc_stat ! = GELIC_WL_ASSOC_STAT_DISCONN )
goto out ;
2008-06-02 07:49:16 +04:00
spin_lock_irqsave ( & wl - > lock , irqflag ) ;
if ( test_bit ( GELIC_WL_STAT_ESSID_SET , & wl - > stat ) ) {
pr_debug ( " %s: assoc ESSID configured %s \n " , __func__ ,
wl - > essid ) ;
essid = wl - > essid ;
essid_len = wl - > essid_len ;
} else {
essid = NULL ;
essid_len = 0 ;
}
spin_unlock_irqrestore ( & wl - > lock , irqflag ) ;
ret = gelic_wl_start_scan ( wl , 0 , essid , essid_len ) ;
2008-02-07 13:58:57 +03:00
if ( ret = = - ERESTARTSYS ) {
pr_debug ( " %s: scan start failed association \n " , __func__ ) ;
schedule_delayed_work ( & wl - > assoc_work , HZ / 10 ) ; /*FIXME*/
goto out ;
} else if ( ret ) {
pr_info ( " %s: scan prerequisite failed \n " , __func__ ) ;
goto out ;
}
/*
* Wait for bss scan completion
* If we have scan list already , gelic_wl_start_scan ( )
* returns OK and raises the complete . Thus ,
* it ' s ok to wait unconditionally here
*/
wait_for_completion ( & wl - > scan_done ) ;
pr_debug ( " %s: scan done \n " , __func__ ) ;
2008-05-22 11:00:01 +04:00
mutex_lock ( & wl - > scan_lock ) ;
2008-02-07 13:58:57 +03:00
if ( wl - > scan_stat ! = GELIC_WL_SCAN_STAT_GOT_LIST ) {
gelic_wl_send_iwap_event ( wl , NULL ) ;
pr_info ( " %s: no scan list. association failed \n " , __func__ ) ;
goto scan_lock_out ;
}
/* find best matching bss */
best_bss = gelic_wl_find_best_bss ( wl ) ;
if ( ! best_bss ) {
gelic_wl_send_iwap_event ( wl , NULL ) ;
pr_info ( " %s: no bss matched. association failed \n " , __func__ ) ;
goto scan_lock_out ;
}
/* ok, do association */
ret = gelic_wl_associate_bss ( wl , best_bss ) ;
if ( ret )
pr_info ( " %s: association failed %d \n " , __func__ , ret ) ;
scan_lock_out :
2008-05-22 11:00:01 +04:00
mutex_unlock ( & wl - > scan_lock ) ;
2008-02-07 13:58:57 +03:00
out :
2008-05-22 11:00:02 +04:00
mutex_unlock ( & wl - > assoc_stat_lock ) ;
2008-02-07 13:58:57 +03:00
}
/*
* Interrupt handler
* Called from the ethernet interrupt handler
* Processes wireless specific virtual interrupts only
*/
void gelic_wl_interrupt ( struct net_device * netdev , u64 status )
{
struct gelic_wl_info * wl = port_wl ( netdev_priv ( netdev ) ) ;
if ( status & GELIC_CARD_WLAN_COMMAND_COMPLETED ) {
pr_debug ( " %s:cmd complete \n " , __func__ ) ;
complete ( & wl - > cmd_done_intr ) ;
}
if ( status & GELIC_CARD_WLAN_EVENT_RECEIVED ) {
pr_debug ( " %s:event received \n " , __func__ ) ;
queue_delayed_work ( wl - > event_queue , & wl - > event_work , 0 ) ;
}
}
/*
* driver helpers
*/
# define IW_IOCTL(n) [(n) - SIOCSIWCOMMIT]
static const iw_handler gelic_wl_wext_handler [ ] =
{
IW_IOCTL ( SIOCGIWNAME ) = gelic_wl_get_name ,
IW_IOCTL ( SIOCGIWRANGE ) = gelic_wl_get_range ,
IW_IOCTL ( SIOCSIWSCAN ) = gelic_wl_set_scan ,
IW_IOCTL ( SIOCGIWSCAN ) = gelic_wl_get_scan ,
IW_IOCTL ( SIOCSIWAUTH ) = gelic_wl_set_auth ,
IW_IOCTL ( SIOCGIWAUTH ) = gelic_wl_get_auth ,
IW_IOCTL ( SIOCSIWESSID ) = gelic_wl_set_essid ,
IW_IOCTL ( SIOCGIWESSID ) = gelic_wl_get_essid ,
IW_IOCTL ( SIOCSIWENCODE ) = gelic_wl_set_encode ,
IW_IOCTL ( SIOCGIWENCODE ) = gelic_wl_get_encode ,
IW_IOCTL ( SIOCSIWAP ) = gelic_wl_set_ap ,
IW_IOCTL ( SIOCGIWAP ) = gelic_wl_get_ap ,
IW_IOCTL ( SIOCSIWENCODEEXT ) = gelic_wl_set_encodeext ,
IW_IOCTL ( SIOCGIWENCODEEXT ) = gelic_wl_get_encodeext ,
IW_IOCTL ( SIOCSIWMODE ) = gelic_wl_set_mode ,
IW_IOCTL ( SIOCGIWMODE ) = gelic_wl_get_mode ,
IW_IOCTL ( SIOCGIWNICKN ) = gelic_wl_get_nick ,
} ;
2008-05-30 11:52:55 +04:00
# ifdef CONFIG_GELIC_WIRELESS_OLD_PSK_INTERFACE
2008-02-07 13:58:57 +03:00
static struct iw_priv_args gelic_wl_private_args [ ] =
{
{
. cmd = GELIC_WL_PRIV_SET_PSK ,
. set_args = IW_PRIV_TYPE_CHAR |
( GELIC_WL_EURUS_PSK_MAX_LEN + 2 ) ,
. name = " set_psk "
} ,
{
. cmd = GELIC_WL_PRIV_GET_PSK ,
. get_args = IW_PRIV_TYPE_CHAR |
( GELIC_WL_EURUS_PSK_MAX_LEN + 2 ) ,
. name = " get_psk "
}
} ;
static const iw_handler gelic_wl_private_handler [ ] =
{
gelic_wl_priv_set_psk ,
gelic_wl_priv_get_psk ,
} ;
2008-05-30 11:52:55 +04:00
# endif
2008-02-07 13:58:57 +03:00
static const struct iw_handler_def gelic_wl_wext_handler_def = {
. num_standard = ARRAY_SIZE ( gelic_wl_wext_handler ) ,
. standard = gelic_wl_wext_handler ,
. get_wireless_stats = gelic_wl_get_wireless_stats ,
2008-05-30 11:52:55 +04:00
# ifdef CONFIG_GELIC_WIRELESS_OLD_PSK_INTERFACE
2008-02-07 13:58:57 +03:00
. num_private = ARRAY_SIZE ( gelic_wl_private_handler ) ,
. num_private_args = ARRAY_SIZE ( gelic_wl_private_args ) ,
. private = gelic_wl_private_handler ,
. private_args = gelic_wl_private_args ,
2008-05-30 11:52:55 +04:00
# endif
2008-02-07 13:58:57 +03:00
} ;
static struct net_device * gelic_wl_alloc ( struct gelic_card * card )
{
struct net_device * netdev ;
struct gelic_port * port ;
struct gelic_wl_info * wl ;
unsigned int i ;
pr_debug ( " %s:start \n " , __func__ ) ;
netdev = alloc_etherdev ( sizeof ( struct gelic_port ) +
sizeof ( struct gelic_wl_info ) ) ;
pr_debug ( " %s: netdev =%p card=%p \n p " , __func__ , netdev , card ) ;
if ( ! netdev )
return NULL ;
2008-03-12 10:41:11 +03:00
strcpy ( netdev - > name , " wlan%d " ) ;
2008-02-07 13:58:57 +03:00
port = netdev_priv ( netdev ) ;
port - > netdev = netdev ;
port - > card = card ;
port - > type = GELIC_PORT_WIRELESS ;
wl = port_wl ( port ) ;
pr_debug ( " %s: wl=%p port=%p \n " , __func__ , wl , port ) ;
/* allocate scan list */
wl - > networks = kzalloc ( sizeof ( struct gelic_wl_scan_info ) *
GELIC_WL_BSS_MAX_ENT , GFP_KERNEL ) ;
if ( ! wl - > networks )
goto fail_bss ;
wl - > eurus_cmd_queue = create_singlethread_workqueue ( " gelic_cmd " ) ;
if ( ! wl - > eurus_cmd_queue )
goto fail_cmd_workqueue ;
wl - > event_queue = create_singlethread_workqueue ( " gelic_event " ) ;
if ( ! wl - > event_queue )
goto fail_event_workqueue ;
INIT_LIST_HEAD ( & wl - > network_free_list ) ;
INIT_LIST_HEAD ( & wl - > network_list ) ;
for ( i = 0 ; i < GELIC_WL_BSS_MAX_ENT ; i + + )
list_add_tail ( & wl - > networks [ i ] . list ,
& wl - > network_free_list ) ;
init_completion ( & wl - > cmd_done_intr ) ;
INIT_DELAYED_WORK ( & wl - > event_work , gelic_wl_event_worker ) ;
INIT_DELAYED_WORK ( & wl - > assoc_work , gelic_wl_assoc_worker ) ;
2008-05-22 11:00:01 +04:00
mutex_init ( & wl - > scan_lock ) ;
2008-05-22 11:00:02 +04:00
mutex_init ( & wl - > assoc_stat_lock ) ;
2008-02-07 13:58:57 +03:00
init_completion ( & wl - > scan_done ) ;
/* for the case that no scan request is issued and stop() is called */
complete ( & wl - > scan_done ) ;
spin_lock_init ( & wl - > lock ) ;
wl - > scan_age = 5 * HZ ; /* FIXME */
/* buffer for receiving scanned list etc */
BUILD_BUG_ON ( PAGE_SIZE <
sizeof ( struct gelic_eurus_scan_info ) *
GELIC_EURUS_MAX_SCAN ) ;
pr_debug ( " %s:end \n " , __func__ ) ;
return netdev ;
fail_event_workqueue :
destroy_workqueue ( wl - > eurus_cmd_queue ) ;
fail_cmd_workqueue :
kfree ( wl - > networks ) ;
fail_bss :
free_netdev ( netdev ) ;
pr_debug ( " %s:end error \n " , __func__ ) ;
return NULL ;
}
static void gelic_wl_free ( struct gelic_wl_info * wl )
{
struct gelic_wl_scan_info * scan_info ;
unsigned int i ;
pr_debug ( " %s: <- \n " , __func__ ) ;
pr_debug ( " %s: destroy queues \n " , __func__ ) ;
destroy_workqueue ( wl - > eurus_cmd_queue ) ;
destroy_workqueue ( wl - > event_queue ) ;
scan_info = wl - > networks ;
for ( i = 0 ; i < GELIC_WL_BSS_MAX_ENT ; i + + , scan_info + + )
kfree ( scan_info - > hwinfo ) ;
kfree ( wl - > networks ) ;
free_netdev ( port_to_netdev ( wl_port ( wl ) ) ) ;
pr_debug ( " %s: -> \n " , __func__ ) ;
}
static int gelic_wl_try_associate ( struct net_device * netdev )
{
struct gelic_wl_info * wl = port_wl ( netdev_priv ( netdev ) ) ;
int ret = - 1 ;
unsigned int i ;
pr_debug ( " %s: <- \n " , __func__ ) ;
/* check constraits for start association */
/* for no access restriction AP */
if ( wl - > group_cipher_method = = GELIC_WL_CIPHER_NONE ) {
if ( test_bit ( GELIC_WL_STAT_CONFIGURED ,
& wl - > stat ) )
goto do_associate ;
else {
pr_debug ( " %s: no wep, not configured \n " , __func__ ) ;
return ret ;
}
}
/* for WEP, one of four keys should be set */
if ( wl - > group_cipher_method = = GELIC_WL_CIPHER_WEP ) {
/* one of keys set */
for ( i = 0 ; i < GELIC_WEP_KEYS ; i + + ) {
if ( test_bit ( i , & wl - > key_enabled ) )
goto do_associate ;
}
pr_debug ( " %s: WEP, but no key specified \n " , __func__ ) ;
return ret ;
}
/* for WPA[2], psk should be set */
if ( ( wl - > group_cipher_method = = GELIC_WL_CIPHER_TKIP ) | |
( wl - > group_cipher_method = = GELIC_WL_CIPHER_AES ) ) {
if ( test_bit ( GELIC_WL_STAT_WPA_PSK_SET ,
& wl - > stat ) )
goto do_associate ;
else {
pr_debug ( " %s: AES/TKIP, but PSK not configured \n " ,
__func__ ) ;
return ret ;
}
}
do_associate :
ret = schedule_delayed_work ( & wl - > assoc_work , 0 ) ;
pr_debug ( " %s: start association work %d \n " , __func__ , ret ) ;
return ret ;
}
/*
* netdev handlers
*/
static int gelic_wl_open ( struct net_device * netdev )
{
struct gelic_card * card = netdev_card ( netdev ) ;
pr_debug ( " %s:->%p \n " , __func__ , netdev ) ;
gelic_card_up ( card ) ;
/* try to associate */
gelic_wl_try_associate ( netdev ) ;
netif_start_queue ( netdev ) ;
pr_debug ( " %s:<- \n " , __func__ ) ;
return 0 ;
}
/*
* reset state machine
*/
static int gelic_wl_reset_state ( struct gelic_wl_info * wl )
{
struct gelic_wl_scan_info * target ;
struct gelic_wl_scan_info * tmp ;
/* empty scan list */
list_for_each_entry_safe ( target , tmp , & wl - > network_list , list ) {
list_move_tail ( & target - > list , & wl - > network_free_list ) ;
}
wl - > scan_stat = GELIC_WL_SCAN_STAT_INIT ;
/* clear configuration */
wl - > auth_method = GELIC_EURUS_AUTH_OPEN ;
wl - > group_cipher_method = GELIC_WL_CIPHER_NONE ;
wl - > pairwise_cipher_method = GELIC_WL_CIPHER_NONE ;
wl - > wpa_level = GELIC_WL_WPA_LEVEL_NONE ;
wl - > key_enabled = 0 ;
wl - > current_key = 0 ;
wl - > psk_type = GELIC_EURUS_WPA_PSK_PASSPHRASE ;
wl - > psk_len = 0 ;
wl - > essid_len = 0 ;
memset ( wl - > essid , 0 , sizeof ( wl - > essid ) ) ;
memset ( wl - > bssid , 0 , sizeof ( wl - > bssid ) ) ;
memset ( wl - > active_bssid , 0 , sizeof ( wl - > active_bssid ) ) ;
wl - > assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN ;
memset ( & wl - > iwstat , 0 , sizeof ( wl - > iwstat ) ) ;
/* all status bit clear */
wl - > stat = 0 ;
return 0 ;
}
/*
* Tell eurus to terminate association
*/
static void gelic_wl_disconnect ( struct net_device * netdev )
{
struct gelic_port * port = netdev_priv ( netdev ) ;
struct gelic_wl_info * wl = port_wl ( port ) ;
struct gelic_eurus_cmd * cmd ;
/*
* If scann process is running on chip ,
* further requests will be rejected
*/
if ( wl - > scan_stat = = GELIC_WL_SCAN_STAT_SCANNING )
wait_for_completion_timeout ( & wl - > scan_done , HZ ) ;
cmd = gelic_eurus_sync_cmd ( wl , GELIC_EURUS_CMD_DISASSOC , NULL , 0 ) ;
kfree ( cmd ) ;
gelic_wl_send_iwap_event ( wl , NULL ) ;
} ;
static int gelic_wl_stop ( struct net_device * netdev )
{
struct gelic_port * port = netdev_priv ( netdev ) ;
struct gelic_wl_info * wl = port_wl ( port ) ;
struct gelic_card * card = netdev_card ( netdev ) ;
pr_debug ( " %s:<- \n " , __func__ ) ;
/*
* Cancel pending association work .
* event work can run after netdev down
*/
cancel_delayed_work ( & wl - > assoc_work ) ;
if ( wl - > assoc_stat = = GELIC_WL_ASSOC_STAT_ASSOCIATED )
gelic_wl_disconnect ( netdev ) ;
/* reset our state machine */
gelic_wl_reset_state ( wl ) ;
netif_stop_queue ( netdev ) ;
gelic_card_down ( card ) ;
pr_debug ( " %s:-> \n " , __func__ ) ;
return 0 ;
}
/* -- */
static struct ethtool_ops gelic_wl_ethtool_ops = {
. get_drvinfo = gelic_net_get_drvinfo ,
. get_link = gelic_wl_get_link ,
. get_tx_csum = ethtool_op_get_tx_csum ,
. set_tx_csum = ethtool_op_set_tx_csum ,
. get_rx_csum = gelic_net_get_rx_csum ,
. set_rx_csum = gelic_net_set_rx_csum ,
} ;
static void gelic_wl_setup_netdev_ops ( struct net_device * netdev )
{
struct gelic_wl_info * wl ;
wl = port_wl ( netdev_priv ( netdev ) ) ;
BUG_ON ( ! wl ) ;
netdev - > open = & gelic_wl_open ;
netdev - > stop = & gelic_wl_stop ;
netdev - > hard_start_xmit = & gelic_net_xmit ;
netdev - > set_multicast_list = & gelic_net_set_multi ;
netdev - > change_mtu = & gelic_net_change_mtu ;
netdev - > wireless_data = & wl - > wireless_data ;
netdev - > wireless_handlers = & gelic_wl_wext_handler_def ;
/* tx watchdog */
netdev - > tx_timeout = & gelic_net_tx_timeout ;
netdev - > watchdog_timeo = GELIC_NET_WATCHDOG_TIMEOUT ;
netdev - > ethtool_ops = & gelic_wl_ethtool_ops ;
# ifdef CONFIG_NET_POLL_CONTROLLER
netdev - > poll_controller = gelic_net_poll_controller ;
# endif
}
/*
* driver probe / remove
*/
int gelic_wl_driver_probe ( struct gelic_card * card )
{
int ret ;
struct net_device * netdev ;
pr_debug ( " %s:start \n " , __func__ ) ;
if ( ps3_compare_firmware_version ( 1 , 6 , 0 ) < 0 )
return 0 ;
if ( ! card - > vlan [ GELIC_PORT_WIRELESS ] . tx )
return 0 ;
/* alloc netdevice for wireless */
netdev = gelic_wl_alloc ( card ) ;
if ( ! netdev )
return - ENOMEM ;
/* setup net_device structure */
2008-02-22 10:45:26 +03:00
SET_NETDEV_DEV ( netdev , & card - > dev - > core ) ;
2008-02-07 13:58:57 +03:00
gelic_wl_setup_netdev_ops ( netdev ) ;
/* setup some of net_device and register it */
ret = gelic_net_setup_netdev ( netdev , card ) ;
if ( ret )
goto fail_setup ;
card - > netdev [ GELIC_PORT_WIRELESS ] = netdev ;
/* add enable wireless interrupt */
card - > irq_mask | = GELIC_CARD_WLAN_EVENT_RECEIVED |
GELIC_CARD_WLAN_COMMAND_COMPLETED ;
/* to allow wireless commands while both interfaces are down */
gelic_card_set_irq_mask ( card , GELIC_CARD_WLAN_EVENT_RECEIVED |
GELIC_CARD_WLAN_COMMAND_COMPLETED ) ;
pr_debug ( " %s:end \n " , __func__ ) ;
return 0 ;
fail_setup :
gelic_wl_free ( port_wl ( netdev_port ( netdev ) ) ) ;
return ret ;
}
int gelic_wl_driver_remove ( struct gelic_card * card )
{
struct gelic_wl_info * wl ;
struct net_device * netdev ;
pr_debug ( " %s:start \n " , __func__ ) ;
if ( ps3_compare_firmware_version ( 1 , 6 , 0 ) < 0 )
return 0 ;
if ( ! card - > vlan [ GELIC_PORT_WIRELESS ] . tx )
return 0 ;
netdev = card - > netdev [ GELIC_PORT_WIRELESS ] ;
wl = port_wl ( netdev_priv ( netdev ) ) ;
/* if the interface was not up, but associated */
if ( wl - > assoc_stat = = GELIC_WL_ASSOC_STAT_ASSOCIATED )
gelic_wl_disconnect ( netdev ) ;
complete ( & wl - > cmd_done_intr ) ;
/* cancel all work queue */
cancel_delayed_work ( & wl - > assoc_work ) ;
cancel_delayed_work ( & wl - > event_work ) ;
flush_workqueue ( wl - > eurus_cmd_queue ) ;
flush_workqueue ( wl - > event_queue ) ;
unregister_netdev ( netdev ) ;
/* disable wireless interrupt */
pr_debug ( " %s: disable intr \n " , __func__ ) ;
card - > irq_mask & = ~ ( GELIC_CARD_WLAN_EVENT_RECEIVED |
GELIC_CARD_WLAN_COMMAND_COMPLETED ) ;
/* free bss list, netdev*/
gelic_wl_free ( wl ) ;
pr_debug ( " %s:end \n " , __func__ ) ;
return 0 ;
}