2009-08-06 16:25:28 +03:00
/*
* This file is part of wl1271
*
* Copyright ( C ) 2009 Nokia Corporation
*
* Contact : Luciano Coelho < luciano . coelho @ nokia . com >
*
* 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 . , 51 Franklin St , Fifth Floor , Boston , MA
* 02110 - 1301 USA
*
*/
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/crc7.h>
# include <linux/spi/spi.h>
# include <linux/etherdevice.h>
# include "wl1271.h"
# include "wl1271_reg.h"
# include "wl1271_spi.h"
2010-02-18 13:25:55 +02:00
# include "wl1271_io.h"
2009-08-06 16:25:28 +03:00
# include "wl1271_acx.h"
# include "wl12xx_80211.h"
# include "wl1271_cmd.h"
/*
* send command to firmware
*
* @ wl : wl struct
* @ id : command id
* @ buf : buffer containing the command , must work with dma
* @ len : length of the buffer
*/
2009-11-02 20:22:13 +02:00
int wl1271_cmd_send ( struct wl1271 * wl , u16 id , void * buf , size_t len ,
size_t res_len )
2009-08-06 16:25:28 +03:00
{
struct wl1271_cmd_header * cmd ;
unsigned long timeout ;
u32 intr ;
int ret = 0 ;
2009-11-02 20:22:12 +02:00
u16 status ;
2009-08-06 16:25:28 +03:00
cmd = buf ;
2009-10-15 10:33:29 +03:00
cmd - > id = cpu_to_le16 ( id ) ;
2009-08-06 16:25:28 +03:00
cmd - > status = 0 ;
WARN_ON ( len % 4 ! = 0 ) ;
2010-02-18 13:25:55 +02:00
wl1271_write ( wl , wl - > cmd_box_addr , buf , len , false ) ;
2009-08-06 16:25:28 +03:00
2010-02-18 13:25:55 +02:00
wl1271_write32 ( wl , ACX_REG_INTERRUPT_TRIG , INTR_TRIG_CMD ) ;
2009-08-06 16:25:28 +03:00
timeout = jiffies + msecs_to_jiffies ( WL1271_COMMAND_TIMEOUT ) ;
2010-02-18 13:25:55 +02:00
intr = wl1271_read32 ( wl , ACX_REG_INTERRUPT_NO_CLEAR ) ;
2009-08-06 16:25:28 +03:00
while ( ! ( intr & WL1271_ACX_INTR_CMD_COMPLETE ) ) {
if ( time_after ( jiffies , timeout ) ) {
wl1271_error ( " command complete timeout " ) ;
ret = - ETIMEDOUT ;
goto out ;
}
msleep ( 1 ) ;
2010-02-18 13:25:55 +02:00
intr = wl1271_read32 ( wl , ACX_REG_INTERRUPT_NO_CLEAR ) ;
2009-08-06 16:25:28 +03:00
}
2009-11-02 20:22:10 +02:00
/* read back the status code of the command */
2009-11-02 20:22:13 +02:00
if ( res_len = = 0 )
res_len = sizeof ( struct wl1271_cmd_header ) ;
2010-02-18 13:25:55 +02:00
wl1271_read ( wl , wl - > cmd_box_addr , cmd , res_len , false ) ;
2009-11-02 20:22:10 +02:00
2009-11-02 20:22:12 +02:00
status = le16_to_cpu ( cmd - > status ) ;
if ( status ! = CMD_STATUS_SUCCESS ) {
wl1271_error ( " command execute failure %d " , status ) ;
2009-11-02 20:22:10 +02:00
ret = - EIO ;
}
2010-02-18 13:25:55 +02:00
wl1271_write32 ( wl , ACX_REG_INTERRUPT_ACK ,
WL1271_ACX_INTR_CMD_COMPLETE ) ;
2009-08-06 16:25:28 +03:00
out :
return ret ;
}
2009-10-15 10:33:27 +03:00
static int wl1271_cmd_cal_channel_tune ( struct wl1271 * wl )
2009-08-06 16:25:28 +03:00
{
struct wl1271_cmd_cal_channel_tune * cmd ;
int ret = 0 ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( ! cmd )
return - ENOMEM ;
cmd - > test . id = TEST_CMD_CHANNEL_TUNE ;
cmd - > band = WL1271_CHANNEL_TUNE_BAND_2_4 ;
/* set up any channel, 7 is in the middle of the range */
cmd - > channel = 7 ;
ret = wl1271_cmd_test ( wl , cmd , sizeof ( * cmd ) , 0 ) ;
if ( ret < 0 )
wl1271_warning ( " TEST_CMD_CHANNEL_TUNE failed " ) ;
kfree ( cmd ) ;
return ret ;
}
2009-10-15 10:33:27 +03:00
static int wl1271_cmd_cal_update_ref_point ( struct wl1271 * wl )
2009-08-06 16:25:28 +03:00
{
struct wl1271_cmd_cal_update_ref_point * cmd ;
int ret = 0 ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( ! cmd )
return - ENOMEM ;
cmd - > test . id = TEST_CMD_UPDATE_PD_REFERENCE_POINT ;
/* FIXME: still waiting for the correct values */
cmd - > ref_power = 0 ;
cmd - > ref_detector = 0 ;
cmd - > sub_band = WL1271_PD_REFERENCE_POINT_BAND_B_G ;
ret = wl1271_cmd_test ( wl , cmd , sizeof ( * cmd ) , 0 ) ;
if ( ret < 0 )
wl1271_warning ( " TEST_CMD_UPDATE_PD_REFERENCE_POINT failed " ) ;
kfree ( cmd ) ;
return ret ;
}
2009-10-15 10:33:27 +03:00
static int wl1271_cmd_cal_p2g ( struct wl1271 * wl )
2009-08-06 16:25:28 +03:00
{
struct wl1271_cmd_cal_p2g * cmd ;
int ret = 0 ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( ! cmd )
return - ENOMEM ;
cmd - > test . id = TEST_CMD_P2G_CAL ;
cmd - > sub_band_mask = WL1271_CAL_P2G_BAND_B_G ;
ret = wl1271_cmd_test ( wl , cmd , sizeof ( * cmd ) , 0 ) ;
if ( ret < 0 )
wl1271_warning ( " TEST_CMD_P2G_CAL failed " ) ;
kfree ( cmd ) ;
return ret ;
}
2009-10-15 10:33:27 +03:00
static int wl1271_cmd_cal ( struct wl1271 * wl )
2009-08-06 16:25:28 +03:00
{
/*
* FIXME : we must make sure that we ' re not sleeping when calibration
* is done
*/
int ret ;
wl1271_notice ( " performing tx calibration " ) ;
ret = wl1271_cmd_cal_channel_tune ( wl ) ;
if ( ret < 0 )
return ret ;
ret = wl1271_cmd_cal_update_ref_point ( wl ) ;
if ( ret < 0 )
return ret ;
ret = wl1271_cmd_cal_p2g ( wl ) ;
if ( ret < 0 )
return ret ;
return ret ;
}
2009-11-23 23:22:17 +02:00
int wl1271_cmd_general_parms ( struct wl1271 * wl )
{
struct wl1271_general_parms_cmd * gen_parms ;
int ret ;
2010-02-18 13:25:42 +02:00
if ( ! wl - > nvs )
return - ENODEV ;
2009-11-23 23:22:17 +02:00
gen_parms = kzalloc ( sizeof ( * gen_parms ) , GFP_KERNEL ) ;
if ( ! gen_parms )
return - ENOMEM ;
gen_parms - > test . id = TEST_CMD_INI_FILE_GENERAL_PARAM ;
2010-02-18 13:25:42 +02:00
memcpy ( gen_parms - > params , wl - > nvs - > general_params ,
WL1271_NVS_GENERAL_PARAMS_SIZE ) ;
2009-12-11 15:40:41 +02:00
2009-11-23 23:22:17 +02:00
ret = wl1271_cmd_test ( wl , gen_parms , sizeof ( * gen_parms ) , 0 ) ;
if ( ret < 0 )
wl1271_warning ( " CMD_INI_FILE_GENERAL_PARAM failed " ) ;
kfree ( gen_parms ) ;
return ret ;
}
int wl1271_cmd_radio_parms ( struct wl1271 * wl )
{
struct wl1271_radio_parms_cmd * radio_parms ;
2010-02-18 13:25:42 +02:00
struct conf_radio_parms * rparam = & wl - > conf . init . radioparam ;
int ret ;
if ( ! wl - > nvs )
return - ENODEV ;
2009-11-23 23:22:17 +02:00
radio_parms = kzalloc ( sizeof ( * radio_parms ) , GFP_KERNEL ) ;
if ( ! radio_parms )
return - ENOMEM ;
radio_parms - > test . id = TEST_CMD_INI_FILE_RADIO_PARAM ;
2010-02-18 13:25:42 +02:00
memcpy ( radio_parms - > stat_radio_params , wl - > nvs - > stat_radio_params ,
WL1271_NVS_STAT_RADIO_PARAMS_SIZE ) ;
memcpy ( radio_parms - > dyn_radio_params ,
wl - > nvs - > dyn_radio_params [ rparam - > fem ] ,
WL1271_NVS_DYN_RADIO_PARAMS_SIZE ) ;
/* FIXME: current NVS is missing 5GHz parameters */
2009-11-23 23:22:17 +02:00
wl1271_dump ( DEBUG_CMD , " TEST_CMD_INI_FILE_RADIO_PARAM: " ,
radio_parms , sizeof ( * radio_parms ) ) ;
ret = wl1271_cmd_test ( wl , radio_parms , sizeof ( * radio_parms ) , 0 ) ;
if ( ret < 0 )
wl1271_warning ( " CMD_INI_FILE_RADIO_PARAM failed " ) ;
kfree ( radio_parms ) ;
return ret ;
}
2009-10-08 21:56:25 +03:00
int wl1271_cmd_join ( struct wl1271 * wl )
2009-08-06 16:25:28 +03:00
{
static bool do_cal = true ;
struct wl1271_cmd_join * join ;
int ret , i ;
u8 * bssid ;
/* FIXME: remove when we get calibration from the factory */
if ( do_cal ) {
ret = wl1271_cmd_cal ( wl ) ;
if ( ret < 0 )
wl1271_warning ( " couldn't calibrate " ) ;
else
do_cal = false ;
}
join = kzalloc ( sizeof ( * join ) , GFP_KERNEL ) ;
if ( ! join ) {
ret = - ENOMEM ;
goto out ;
}
wl1271_debug ( DEBUG_CMD , " cmd join " ) ;
/* Reverse order BSSID */
bssid = ( u8 * ) & join - > bssid_lsb ;
for ( i = 0 ; i < ETH_ALEN ; i + + )
bssid [ i ] = wl - > bssid [ ETH_ALEN - i - 1 ] ;
2009-10-15 10:33:29 +03:00
join - > rx_config_options = cpu_to_le32 ( wl - > rx_config ) ;
join - > rx_filter_options = cpu_to_le32 ( wl - > rx_filter ) ;
2009-10-13 12:47:51 +03:00
join - > bss_type = wl - > bss_type ;
2009-08-06 16:25:28 +03:00
2009-10-08 21:56:28 +03:00
/*
* FIXME : disable temporarily all filters because after commit
* 9 cef8737 " mac80211: fix managed mode BSSID handling " broke
* association . The filter logic needs to be implemented properly
* and once that is done , this hack can be removed .
*/
2009-10-15 10:33:29 +03:00
join - > rx_config_options = cpu_to_le32 ( 0 ) ;
join - > rx_filter_options = cpu_to_le32 ( WL1271_DEFAULT_RX_FILTER ) ;
2009-10-08 21:56:28 +03:00
2009-10-13 12:47:51 +03:00
if ( wl - > band = = IEEE80211_BAND_2GHZ )
2009-10-15 10:33:29 +03:00
join - > basic_rate_set = cpu_to_le32 ( CONF_HW_BIT_RATE_1MBPS |
CONF_HW_BIT_RATE_2MBPS |
CONF_HW_BIT_RATE_5_5MBPS |
CONF_HW_BIT_RATE_11MBPS ) ;
2009-10-13 12:47:51 +03:00
else {
join - > bss_type | = WL1271_JOIN_CMD_BSS_TYPE_5GHZ ;
2009-10-15 10:33:29 +03:00
join - > basic_rate_set = cpu_to_le32 ( CONF_HW_BIT_RATE_6MBPS |
CONF_HW_BIT_RATE_12MBPS |
CONF_HW_BIT_RATE_24MBPS ) ;
2009-10-13 12:47:51 +03:00
}
2009-08-06 16:25:28 +03:00
2009-10-15 10:33:29 +03:00
join - > beacon_interval = cpu_to_le16 ( WL1271_DEFAULT_BEACON_INT ) ;
2009-10-12 15:08:57 +03:00
join - > dtim_interval = WL1271_DEFAULT_DTIM_PERIOD ;
2009-10-13 12:47:51 +03:00
2009-08-06 16:25:28 +03:00
join - > channel = wl - > channel ;
join - > ssid_len = wl - > ssid_len ;
memcpy ( join - > ssid , wl - > ssid , wl - > ssid_len ) ;
join - > ctrl = WL1271_JOIN_CMD_CTRL_TX_FLUSH ;
/* increment the session counter */
wl - > session_counter + + ;
if ( wl - > session_counter > = SESSION_COUNTER_MAX )
wl - > session_counter = 0 ;
join - > ctrl | = wl - > session_counter < < WL1271_JOIN_CMD_TX_SESSION_OFFSET ;
2009-10-08 21:56:19 +03:00
/* reset TX security counters */
wl - > tx_security_last_seq = 0 ;
wl - > tx_security_seq_16 = 0 ;
wl - > tx_security_seq_32 = 0 ;
2009-08-06 16:25:28 +03:00
2009-11-02 20:22:13 +02:00
ret = wl1271_cmd_send ( wl , CMD_START_JOIN , join , sizeof ( * join ) , 0 ) ;
2009-08-06 16:25:28 +03:00
if ( ret < 0 ) {
wl1271_error ( " failed to initiate cmd join " ) ;
goto out_free ;
}
/*
* ugly hack : we should wait for JOIN_EVENT_COMPLETE_ID but to
* simplify locking we just sleep instead , for now
*/
2009-10-08 21:56:25 +03:00
msleep ( 10 ) ;
2009-08-06 16:25:28 +03:00
out_free :
kfree ( join ) ;
out :
return ret ;
}
/**
* send test command to firmware
*
* @ wl : wl struct
* @ buf : buffer containing the command , with all headers , must work with dma
* @ len : length of the buffer
* @ answer : is answer needed
*/
int wl1271_cmd_test ( struct wl1271 * wl , void * buf , size_t buf_len , u8 answer )
{
int ret ;
2009-11-02 20:22:13 +02:00
size_t res_len = 0 ;
2009-08-06 16:25:28 +03:00
wl1271_debug ( DEBUG_CMD , " cmd test " ) ;
2009-11-02 20:22:13 +02:00
if ( answer )
res_len = buf_len ;
ret = wl1271_cmd_send ( wl , CMD_TEST , buf , buf_len , res_len ) ;
2009-08-06 16:25:28 +03:00
if ( ret < 0 ) {
wl1271_warning ( " TEST command failed " ) ;
return ret ;
}
2009-11-02 20:22:13 +02:00
return ret ;
2009-08-06 16:25:28 +03:00
}
/**
* read acx from firmware
*
* @ wl : wl struct
* @ id : acx id
* @ buf : buffer for the response , including all headers , must work with dma
* @ len : lenght of buf
*/
int wl1271_cmd_interrogate ( struct wl1271 * wl , u16 id , void * buf , size_t len )
{
struct acx_header * acx = buf ;
int ret ;
wl1271_debug ( DEBUG_CMD , " cmd interrogate " ) ;
2009-10-15 10:33:29 +03:00
acx - > id = cpu_to_le16 ( id ) ;
2009-08-06 16:25:28 +03:00
/* payload length, does not include any headers */
2009-10-15 10:33:29 +03:00
acx - > len = cpu_to_le16 ( len - sizeof ( * acx ) ) ;
2009-08-06 16:25:28 +03:00
2009-11-02 20:22:13 +02:00
ret = wl1271_cmd_send ( wl , CMD_INTERROGATE , acx , sizeof ( * acx ) , len ) ;
if ( ret < 0 )
2009-08-06 16:25:28 +03:00
wl1271_error ( " INTERROGATE command failed " ) ;
return ret ;
}
/**
* write acx value to firmware
*
* @ wl : wl struct
* @ id : acx id
* @ buf : buffer containing acx , including all headers , must work with dma
* @ len : length of buf
*/
int wl1271_cmd_configure ( struct wl1271 * wl , u16 id , void * buf , size_t len )
{
struct acx_header * acx = buf ;
int ret ;
wl1271_debug ( DEBUG_CMD , " cmd configure " ) ;
2009-10-15 10:33:29 +03:00
acx - > id = cpu_to_le16 ( id ) ;
2009-08-06 16:25:28 +03:00
/* payload length, does not include any headers */
2009-10-15 10:33:29 +03:00
acx - > len = cpu_to_le16 ( len - sizeof ( * acx ) ) ;
2009-08-06 16:25:28 +03:00
2009-11-02 20:22:13 +02:00
ret = wl1271_cmd_send ( wl , CMD_CONFIGURE , acx , len , 0 ) ;
2009-08-06 16:25:28 +03:00
if ( ret < 0 ) {
wl1271_warning ( " CONFIGURE command NOK " ) ;
return ret ;
}
return 0 ;
}
2009-12-11 15:40:55 +02:00
int wl1271_cmd_data_path ( struct wl1271 * wl , bool enable )
2009-08-06 16:25:28 +03:00
{
struct cmd_enabledisable_path * cmd ;
int ret ;
u16 cmd_rx , cmd_tx ;
wl1271_debug ( DEBUG_CMD , " cmd data path " ) ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( ! cmd ) {
ret = - ENOMEM ;
goto out ;
}
2009-12-11 15:40:55 +02:00
/* the channel here is only used for calibration, so hardcoded to 1 */
cmd - > channel = 1 ;
2009-08-06 16:25:28 +03:00
if ( enable ) {
cmd_rx = CMD_ENABLE_RX ;
cmd_tx = CMD_ENABLE_TX ;
} else {
cmd_rx = CMD_DISABLE_RX ;
cmd_tx = CMD_DISABLE_TX ;
}
2009-11-02 20:22:13 +02:00
ret = wl1271_cmd_send ( wl , cmd_rx , cmd , sizeof ( * cmd ) , 0 ) ;
2009-08-06 16:25:28 +03:00
if ( ret < 0 ) {
wl1271_error ( " rx %s cmd for channel %d failed " ,
2009-12-11 15:40:55 +02:00
enable ? " start " : " stop " , cmd - > channel ) ;
2009-08-06 16:25:28 +03:00
goto out ;
}
wl1271_debug ( DEBUG_BOOT , " rx %s cmd channel %d " ,
2009-12-11 15:40:55 +02:00
enable ? " start " : " stop " , cmd - > channel ) ;
2009-08-06 16:25:28 +03:00
2009-11-02 20:22:13 +02:00
ret = wl1271_cmd_send ( wl , cmd_tx , cmd , sizeof ( * cmd ) , 0 ) ;
2009-08-06 16:25:28 +03:00
if ( ret < 0 ) {
wl1271_error ( " tx %s cmd for channel %d failed " ,
2009-12-11 15:40:55 +02:00
enable ? " start " : " stop " , cmd - > channel ) ;
2009-08-06 16:25:28 +03:00
return ret ;
}
wl1271_debug ( DEBUG_BOOT , " tx %s cmd channel %d " ,
2009-12-11 15:40:55 +02:00
enable ? " start " : " stop " , cmd - > channel ) ;
2009-08-06 16:25:28 +03:00
out :
kfree ( cmd ) ;
return ret ;
}
2010-02-18 13:25:36 +02:00
int wl1271_cmd_ps_mode ( struct wl1271 * wl , u8 ps_mode , bool send )
2009-08-06 16:25:28 +03:00
{
struct wl1271_cmd_ps_params * ps_params = NULL ;
int ret = 0 ;
/* FIXME: this should be in ps.c */
2009-10-13 12:47:42 +03:00
ret = wl1271_acx_wake_up_conditions ( wl ) ;
2009-08-06 16:25:28 +03:00
if ( ret < 0 ) {
wl1271_error ( " couldn't set wake up conditions " ) ;
goto out ;
}
wl1271_debug ( DEBUG_CMD , " cmd set ps mode " ) ;
ps_params = kzalloc ( sizeof ( * ps_params ) , GFP_KERNEL ) ;
if ( ! ps_params ) {
ret = - ENOMEM ;
goto out ;
}
ps_params - > ps_mode = ps_mode ;
2010-02-18 13:25:36 +02:00
ps_params - > send_null_data = send ;
2009-08-06 16:25:28 +03:00
ps_params - > retries = 5 ;
ps_params - > hang_over_period = 128 ;
2009-10-15 10:33:29 +03:00
ps_params - > null_data_rate = cpu_to_le32 ( 1 ) ; /* 1 Mbps */
2009-08-06 16:25:28 +03:00
ret = wl1271_cmd_send ( wl , CMD_SET_PS_MODE , ps_params ,
2009-11-02 20:22:13 +02:00
sizeof ( * ps_params ) , 0 ) ;
2009-08-06 16:25:28 +03:00
if ( ret < 0 ) {
wl1271_error ( " cmd set_ps_mode failed " ) ;
goto out ;
}
out :
kfree ( ps_params ) ;
return ret ;
}
int wl1271_cmd_read_memory ( struct wl1271 * wl , u32 addr , void * answer ,
size_t len )
{
struct cmd_read_write_memory * cmd ;
int ret = 0 ;
wl1271_debug ( DEBUG_CMD , " cmd read memory " ) ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( ! cmd ) {
ret = - ENOMEM ;
goto out ;
}
WARN_ON ( len > MAX_READ_SIZE ) ;
len = min_t ( size_t , len , MAX_READ_SIZE ) ;
2009-10-15 10:33:29 +03:00
cmd - > addr = cpu_to_le32 ( addr ) ;
cmd - > size = cpu_to_le32 ( len ) ;
2009-08-06 16:25:28 +03:00
2009-11-02 20:22:13 +02:00
ret = wl1271_cmd_send ( wl , CMD_READ_MEMORY , cmd , sizeof ( * cmd ) ,
sizeof ( * cmd ) ) ;
2009-08-06 16:25:28 +03:00
if ( ret < 0 ) {
wl1271_error ( " read memory command failed: %d " , ret ) ;
goto out ;
}
2009-11-02 20:22:13 +02:00
/* the read command got in */
2009-08-06 16:25:28 +03:00
memcpy ( answer , cmd - > value , len ) ;
out :
kfree ( cmd ) ;
return ret ;
}
int wl1271_cmd_scan ( struct wl1271 * wl , u8 * ssid , size_t len ,
2009-10-13 12:47:50 +03:00
u8 active_scan , u8 high_prio , u8 band ,
2009-08-06 16:25:28 +03:00
u8 probe_requests )
{
struct wl1271_cmd_trigger_scan_to * trigger = NULL ;
struct wl1271_cmd_scan * params = NULL ;
2009-10-13 12:47:49 +03:00
struct ieee80211_channel * channels ;
int i , j , n_ch , ret ;
2009-08-06 16:25:28 +03:00
u16 scan_options = 0 ;
2009-10-13 12:47:50 +03:00
u8 ieee_band ;
if ( band = = WL1271_SCAN_BAND_2_4_GHZ )
ieee_band = IEEE80211_BAND_2GHZ ;
else if ( band = = WL1271_SCAN_BAND_DUAL & & wl1271_11a_enabled ( ) )
ieee_band = IEEE80211_BAND_2GHZ ;
else if ( band = = WL1271_SCAN_BAND_5_GHZ & & wl1271_11a_enabled ( ) )
ieee_band = IEEE80211_BAND_5GHZ ;
else
return - EINVAL ;
2009-08-06 16:25:28 +03:00
2009-10-13 12:47:50 +03:00
if ( wl - > hw - > wiphy - > bands [ ieee_band ] - > channels = = NULL )
2009-08-06 16:25:28 +03:00
return - EINVAL ;
2009-10-13 12:47:50 +03:00
channels = wl - > hw - > wiphy - > bands [ ieee_band ] - > channels ;
n_ch = wl - > hw - > wiphy - > bands [ ieee_band ] - > n_channels ;
2009-12-11 15:41:07 +02:00
if ( test_bit ( WL1271_FLAG_SCANNING , & wl - > flags ) )
2009-10-13 12:47:50 +03:00
return - EINVAL ;
2009-10-13 12:47:49 +03:00
2009-08-06 16:25:28 +03:00
params = kzalloc ( sizeof ( * params ) , GFP_KERNEL ) ;
if ( ! params )
return - ENOMEM ;
params - > params . rx_config_options = cpu_to_le32 ( CFG_RX_ALL_GOOD ) ;
params - > params . rx_filter_options =
cpu_to_le32 ( CFG_RX_PRSP_EN | CFG_RX_MGMT_EN | CFG_RX_BCN_EN ) ;
if ( ! active_scan )
scan_options | = WL1271_SCAN_OPT_PASSIVE ;
if ( high_prio )
scan_options | = WL1271_SCAN_OPT_PRIORITY_HIGH ;
2009-10-15 10:33:29 +03:00
params - > params . scan_options = cpu_to_le16 ( scan_options ) ;
2009-08-06 16:25:28 +03:00
params - > params . num_probe_requests = probe_requests ;
2009-10-13 12:47:50 +03:00
/* Let the fw autodetect suitable tx_rate for probes */
params - > params . tx_rate = 0 ;
2009-08-06 16:25:28 +03:00
params - > params . tid_trigger = 0 ;
params - > params . scan_tag = WL1271_SCAN_DEFAULT_TAG ;
2009-10-13 12:47:50 +03:00
if ( band = = WL1271_SCAN_BAND_DUAL )
params - > params . band = WL1271_SCAN_BAND_2_4_GHZ ;
else
params - > params . band = band ;
2009-10-13 12:47:49 +03:00
for ( i = 0 , j = 0 ; i < n_ch & & i < WL1271_SCAN_MAX_CHANNELS ; i + + ) {
if ( ! ( channels [ i ] . flags & IEEE80211_CHAN_DISABLED ) ) {
params - > channels [ j ] . min_duration =
cpu_to_le32 ( WL1271_SCAN_CHAN_MIN_DURATION ) ;
params - > channels [ j ] . max_duration =
cpu_to_le32 ( WL1271_SCAN_CHAN_MAX_DURATION ) ;
memset ( & params - > channels [ j ] . bssid_lsb , 0xff , 4 ) ;
memset ( & params - > channels [ j ] . bssid_msb , 0xff , 2 ) ;
params - > channels [ j ] . early_termination = 0 ;
params - > channels [ j ] . tx_power_att =
WL1271_SCAN_CURRENT_TX_PWR ;
params - > channels [ j ] . channel = channels [ i ] . hw_value ;
j + + ;
}
2009-08-06 16:25:28 +03:00
}
2009-10-13 12:47:49 +03:00
params - > params . num_channels = j ;
2009-08-06 16:25:28 +03:00
if ( len & & ssid ) {
params - > params . ssid_len = len ;
memcpy ( params - > params . ssid , ssid , len ) ;
}
2009-10-13 12:47:50 +03:00
ret = wl1271_cmd_build_probe_req ( wl , ssid , len , ieee_band ) ;
2009-08-06 16:25:28 +03:00
if ( ret < 0 ) {
wl1271_error ( " PROBE request template failed " ) ;
goto out ;
}
trigger = kzalloc ( sizeof ( * trigger ) , GFP_KERNEL ) ;
if ( ! trigger ) {
ret = - ENOMEM ;
goto out ;
}
/* disable the timeout */
trigger - > timeout = 0 ;
ret = wl1271_cmd_send ( wl , CMD_TRIGGER_SCAN_TO , trigger ,
2009-11-02 20:22:13 +02:00
sizeof ( * trigger ) , 0 ) ;
2009-08-06 16:25:28 +03:00
if ( ret < 0 ) {
wl1271_error ( " trigger scan to failed for hw scan " ) ;
goto out ;
}
wl1271_dump ( DEBUG_SCAN , " SCAN: " , params , sizeof ( * params ) ) ;
2009-12-11 15:41:07 +02:00
set_bit ( WL1271_FLAG_SCANNING , & wl - > flags ) ;
2009-10-13 12:47:50 +03:00
if ( wl1271_11a_enabled ( ) ) {
wl - > scan . state = band ;
if ( band = = WL1271_SCAN_BAND_DUAL ) {
wl - > scan . active = active_scan ;
wl - > scan . high_prio = high_prio ;
wl - > scan . probe_requests = probe_requests ;
if ( len & & ssid ) {
wl - > scan . ssid_len = len ;
memcpy ( wl - > scan . ssid , ssid , len ) ;
} else
wl - > scan . ssid_len = 0 ;
}
}
2009-08-06 16:25:28 +03:00
2009-11-02 20:22:13 +02:00
ret = wl1271_cmd_send ( wl , CMD_SCAN , params , sizeof ( * params ) , 0 ) ;
2009-08-06 16:25:28 +03:00
if ( ret < 0 ) {
wl1271_error ( " SCAN failed " ) ;
2009-12-11 15:41:07 +02:00
clear_bit ( WL1271_FLAG_SCANNING , & wl - > flags ) ;
2009-08-06 16:25:28 +03:00
goto out ;
}
out :
kfree ( params ) ;
return ret ;
}
int wl1271_cmd_template_set ( struct wl1271 * wl , u16 template_id ,
void * buf , size_t buf_len )
{
struct wl1271_cmd_template_set * cmd ;
int ret = 0 ;
wl1271_debug ( DEBUG_CMD , " cmd template_set %d " , template_id ) ;
WARN_ON ( buf_len > WL1271_CMD_TEMPL_MAX_SIZE ) ;
buf_len = min_t ( size_t , buf_len , WL1271_CMD_TEMPL_MAX_SIZE ) ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( ! cmd ) {
ret = - ENOMEM ;
goto out ;
}
cmd - > len = cpu_to_le16 ( buf_len ) ;
cmd - > template_type = template_id ;
2009-10-15 10:33:29 +03:00
cmd - > enabled_rates = cpu_to_le32 ( wl - > conf . tx . rc_conf . enabled_rates ) ;
2009-10-13 12:47:41 +03:00
cmd - > short_retry_limit = wl - > conf . tx . rc_conf . short_retry_limit ;
cmd - > long_retry_limit = wl - > conf . tx . rc_conf . long_retry_limit ;
2009-08-06 16:25:28 +03:00
if ( buf )
memcpy ( cmd - > template_data , buf , buf_len ) ;
2009-11-02 20:22:13 +02:00
ret = wl1271_cmd_send ( wl , CMD_SET_TEMPLATE , cmd , sizeof ( * cmd ) , 0 ) ;
2009-08-06 16:25:28 +03:00
if ( ret < 0 ) {
wl1271_warning ( " cmd set_template failed: %d " , ret ) ;
goto out_free ;
}
out_free :
kfree ( cmd ) ;
out :
return ret ;
}
2009-12-23 15:23:19 +02:00
static int wl1271_build_basic_rates ( u8 * rates , u8 band )
2009-08-06 16:25:28 +03:00
{
u8 index = 0 ;
2009-10-13 12:47:50 +03:00
if ( band = = IEEE80211_BAND_2GHZ ) {
rates [ index + + ] =
IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB ;
rates [ index + + ] =
IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB ;
rates [ index + + ] =
IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_5MB ;
rates [ index + + ] =
IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_11MB ;
} else if ( band = = IEEE80211_BAND_5GHZ ) {
rates [ index + + ] =
IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_6MB ;
rates [ index + + ] =
IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_12MB ;
rates [ index + + ] =
IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_24MB ;
} else {
wl1271_error ( " build_basic_rates invalid band: %d " , band ) ;
}
2009-08-06 16:25:28 +03:00
return index ;
}
2009-12-23 15:23:19 +02:00
static int wl1271_build_extended_rates ( u8 * rates , u8 band )
2009-08-06 16:25:28 +03:00
{
u8 index = 0 ;
2009-10-13 12:47:50 +03:00
if ( band = = IEEE80211_BAND_2GHZ ) {
rates [ index + + ] = IEEE80211_OFDM_RATE_6MB ;
rates [ index + + ] = IEEE80211_OFDM_RATE_9MB ;
rates [ index + + ] = IEEE80211_OFDM_RATE_12MB ;
rates [ index + + ] = IEEE80211_OFDM_RATE_18MB ;
rates [ index + + ] = IEEE80211_OFDM_RATE_24MB ;
rates [ index + + ] = IEEE80211_OFDM_RATE_36MB ;
rates [ index + + ] = IEEE80211_OFDM_RATE_48MB ;
rates [ index + + ] = IEEE80211_OFDM_RATE_54MB ;
} else if ( band = = IEEE80211_BAND_5GHZ ) {
rates [ index + + ] =
IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_9MB ;
rates [ index + + ] =
IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_18MB ;
rates [ index + + ] =
IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_24MB ;
rates [ index + + ] =
IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_36MB ;
rates [ index + + ] =
IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_48MB ;
rates [ index + + ] =
IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_54MB ;
} else {
wl1271_error ( " build_basic_rates invalid band: %d " , band ) ;
}
2009-08-06 16:25:28 +03:00
return index ;
}
int wl1271_cmd_build_null_data ( struct wl1271 * wl )
{
struct wl12xx_null_data_template template ;
if ( ! is_zero_ether_addr ( wl - > bssid ) ) {
memcpy ( template . header . da , wl - > bssid , ETH_ALEN ) ;
memcpy ( template . header . bssid , wl - > bssid , ETH_ALEN ) ;
} else {
memset ( template . header . da , 0xff , ETH_ALEN ) ;
memset ( template . header . bssid , 0xff , ETH_ALEN ) ;
}
memcpy ( template . header . sa , wl - > mac_addr , ETH_ALEN ) ;
template . header . frame_ctl = cpu_to_le16 ( IEEE80211_FTYPE_DATA |
2009-10-15 10:33:26 +03:00
IEEE80211_STYPE_NULLFUNC |
IEEE80211_FCTL_TODS ) ;
2009-08-06 16:25:28 +03:00
return wl1271_cmd_template_set ( wl , CMD_TEMPL_NULL_DATA , & template ,
sizeof ( template ) ) ;
}
int wl1271_cmd_build_ps_poll ( struct wl1271 * wl , u16 aid )
{
struct wl12xx_ps_poll_template template ;
memcpy ( template . bssid , wl - > bssid , ETH_ALEN ) ;
memcpy ( template . ta , wl - > mac_addr , ETH_ALEN ) ;
2009-10-08 21:56:22 +03:00
/* aid in PS-Poll has its two MSBs each set to 1 */
template . aid = cpu_to_le16 ( 1 < < 15 | 1 < < 14 | aid ) ;
2009-08-06 16:25:28 +03:00
template . fc = cpu_to_le16 ( IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL ) ;
return wl1271_cmd_template_set ( wl , CMD_TEMPL_PS_POLL , & template ,
sizeof ( template ) ) ;
}
2009-10-13 12:47:50 +03:00
int wl1271_cmd_build_probe_req ( struct wl1271 * wl , u8 * ssid , size_t ssid_len ,
u8 band )
2009-08-06 16:25:28 +03:00
{
struct wl12xx_probe_req_template template ;
struct wl12xx_ie_rates * rates ;
char * ptr ;
u16 size ;
2009-10-13 12:47:50 +03:00
int ret ;
2009-08-06 16:25:28 +03:00
ptr = ( char * ) & template ;
size = sizeof ( struct ieee80211_header ) ;
memset ( template . header . da , 0xff , ETH_ALEN ) ;
memset ( template . header . bssid , 0xff , ETH_ALEN ) ;
memcpy ( template . header . sa , wl - > mac_addr , ETH_ALEN ) ;
template . header . frame_ctl = cpu_to_le16 ( IEEE80211_STYPE_PROBE_REQ ) ;
/* IEs */
/* SSID */
template . ssid . header . id = WLAN_EID_SSID ;
template . ssid . header . len = ssid_len ;
if ( ssid_len & & ssid )
memcpy ( template . ssid . ssid , ssid , ssid_len ) ;
size + = sizeof ( struct wl12xx_ie_header ) + ssid_len ;
ptr + = size ;
/* Basic Rates */
rates = ( struct wl12xx_ie_rates * ) ptr ;
rates - > header . id = WLAN_EID_SUPP_RATES ;
2009-10-13 12:47:50 +03:00
rates - > header . len = wl1271_build_basic_rates ( rates - > rates , band ) ;
2009-08-06 16:25:28 +03:00
size + = sizeof ( struct wl12xx_ie_header ) + rates - > header . len ;
ptr + = sizeof ( struct wl12xx_ie_header ) + rates - > header . len ;
/* Extended rates */
rates = ( struct wl12xx_ie_rates * ) ptr ;
rates - > header . id = WLAN_EID_EXT_SUPP_RATES ;
2009-10-13 12:47:50 +03:00
rates - > header . len = wl1271_build_extended_rates ( rates - > rates , band ) ;
2009-08-06 16:25:28 +03:00
size + = sizeof ( struct wl12xx_ie_header ) + rates - > header . len ;
wl1271_dump ( DEBUG_SCAN , " PROBE REQ: " , & template , size ) ;
2009-10-13 12:47:50 +03:00
if ( band = = IEEE80211_BAND_2GHZ )
ret = wl1271_cmd_template_set ( wl , CMD_TEMPL_CFG_PROBE_REQ_2_4 ,
& template , size ) ;
else
ret = wl1271_cmd_template_set ( wl , CMD_TEMPL_CFG_PROBE_REQ_5 ,
& template , size ) ;
return ret ;
2009-08-06 16:25:28 +03:00
}
int wl1271_cmd_set_default_wep_key ( struct wl1271 * wl , u8 id )
{
struct wl1271_cmd_set_keys * cmd ;
int ret = 0 ;
wl1271_debug ( DEBUG_CMD , " cmd set_default_wep_key %d " , id ) ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( ! cmd ) {
ret = - ENOMEM ;
goto out ;
}
cmd - > id = id ;
2009-10-15 10:33:29 +03:00
cmd - > key_action = cpu_to_le16 ( KEY_SET_ID ) ;
2009-08-06 16:25:28 +03:00
cmd - > key_type = KEY_WEP ;
2009-11-02 20:22:13 +02:00
ret = wl1271_cmd_send ( wl , CMD_SET_KEYS , cmd , sizeof ( * cmd ) , 0 ) ;
2009-08-06 16:25:28 +03:00
if ( ret < 0 ) {
wl1271_warning ( " cmd set_default_wep_key failed: %d " , ret ) ;
goto out ;
}
out :
kfree ( cmd ) ;
return ret ;
}
int wl1271_cmd_set_key ( struct wl1271 * wl , u16 action , u8 id , u8 key_type ,
2009-10-08 21:56:19 +03:00
u8 key_size , const u8 * key , const u8 * addr ,
u32 tx_seq_32 , u16 tx_seq_16 )
2009-08-06 16:25:28 +03:00
{
struct wl1271_cmd_set_keys * cmd ;
int ret = 0 ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( ! cmd ) {
ret = - ENOMEM ;
goto out ;
}
if ( key_type ! = KEY_WEP )
memcpy ( cmd - > addr , addr , ETH_ALEN ) ;
2009-10-15 10:33:29 +03:00
cmd - > key_action = cpu_to_le16 ( action ) ;
2009-08-06 16:25:28 +03:00
cmd - > key_size = key_size ;
cmd - > key_type = key_type ;
2009-10-15 10:33:29 +03:00
cmd - > ac_seq_num16 [ 0 ] = cpu_to_le16 ( tx_seq_16 ) ;
cmd - > ac_seq_num32 [ 0 ] = cpu_to_le32 ( tx_seq_32 ) ;
2009-10-08 21:56:19 +03:00
2009-08-06 16:25:28 +03:00
/* we have only one SSID profile */
cmd - > ssid_profile = 0 ;
cmd - > id = id ;
if ( key_type = = KEY_TKIP ) {
/*
* We get the key in the following form :
* TKIP ( 16 bytes ) - TX MIC ( 8 bytes ) - RX MIC ( 8 bytes )
* but the target is expecting :
* TKIP - RX MIC - TX MIC
*/
memcpy ( cmd - > key , key , 16 ) ;
memcpy ( cmd - > key + 16 , key + 24 , 8 ) ;
memcpy ( cmd - > key + 24 , key + 16 , 8 ) ;
} else {
memcpy ( cmd - > key , key , key_size ) ;
}
wl1271_dump ( DEBUG_CRYPT , " TARGET KEY: " , cmd , sizeof ( * cmd ) ) ;
2009-11-02 20:22:13 +02:00
ret = wl1271_cmd_send ( wl , CMD_SET_KEYS , cmd , sizeof ( * cmd ) , 0 ) ;
2009-08-06 16:25:28 +03:00
if ( ret < 0 ) {
wl1271_warning ( " could not set keys " ) ;
2010-02-18 13:25:42 +02:00
goto out ;
2009-08-06 16:25:28 +03:00
}
out :
kfree ( cmd ) ;
return ret ;
}
2009-10-12 15:08:42 +03:00
int wl1271_cmd_disconnect ( struct wl1271 * wl )
{
struct wl1271_cmd_disconnect * cmd ;
int ret = 0 ;
wl1271_debug ( DEBUG_CMD , " cmd disconnect " ) ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( ! cmd ) {
ret = - ENOMEM ;
goto out ;
}
2009-10-15 10:33:29 +03:00
cmd - > rx_config_options = cpu_to_le32 ( wl - > rx_config ) ;
cmd - > rx_filter_options = cpu_to_le32 ( wl - > rx_filter ) ;
2009-10-12 15:08:42 +03:00
/* disconnect reason is not used in immediate disconnections */
cmd - > type = DISCONNECT_IMMEDIATE ;
2009-11-02 20:22:13 +02:00
ret = wl1271_cmd_send ( wl , CMD_DISCONNECT , cmd , sizeof ( * cmd ) , 0 ) ;
2009-10-12 15:08:42 +03:00
if ( ret < 0 ) {
wl1271_error ( " failed to send disconnect command " ) ;
goto out_free ;
}
out_free :
kfree ( cmd ) ;
out :
return ret ;
}