2008-03-21 13:53:44 -07:00
/******************************************************************************
*
* GPL LICENSE SUMMARY
*
2009-01-08 10:20:02 -08:00
* Copyright ( c ) 2008 - 2009 Intel Corporation . All rights reserved .
2008-03-21 13:53:44 -07:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of version 2 of the GNU General Public License 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 Street , Fifth Floor , Boston , MA 02110 ,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called LICENSE . GPL .
*
* Contact Information :
2008-12-09 11:28:58 -08:00
* Intel Linux Wireless < ilw @ linux . intel . com >
2008-03-21 13:53:44 -07:00
* Intel Corporation , 5200 N . E . Elam Young Parkway , Hillsboro , OR 97124 - 6497
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <linux/kernel.h>
# include <linux/module.h>
# include <net/mac80211.h>
2008-04-24 11:55:38 -07:00
# include "iwl-dev.h" /* FIXME: remove */
2008-03-21 13:53:44 -07:00
# include "iwl-debug.h"
# include "iwl-eeprom.h"
# include "iwl-core.h"
2008-11-12 13:14:08 -08:00
# define IWL_CMD(x) case x: return #x
2008-03-21 13:53:44 -07:00
const char * get_cmd_string ( u8 cmd )
{
switch ( cmd ) {
IWL_CMD ( REPLY_ALIVE ) ;
IWL_CMD ( REPLY_ERROR ) ;
IWL_CMD ( REPLY_RXON ) ;
IWL_CMD ( REPLY_RXON_ASSOC ) ;
IWL_CMD ( REPLY_QOS_PARAM ) ;
IWL_CMD ( REPLY_RXON_TIMING ) ;
IWL_CMD ( REPLY_ADD_STA ) ;
IWL_CMD ( REPLY_REMOVE_STA ) ;
IWL_CMD ( REPLY_REMOVE_ALL_STA ) ;
2008-04-14 21:16:05 -07:00
IWL_CMD ( REPLY_WEPKEY ) ;
2009-01-08 10:20:00 -08:00
IWL_CMD ( REPLY_3945_RX ) ;
2008-03-21 13:53:44 -07:00
IWL_CMD ( REPLY_TX ) ;
IWL_CMD ( REPLY_RATE_SCALE ) ;
IWL_CMD ( REPLY_LEDS_CMD ) ;
IWL_CMD ( REPLY_TX_LINK_QUALITY_CMD ) ;
2008-05-29 16:35:05 +08:00
IWL_CMD ( COEX_PRIORITY_TABLE_CMD ) ;
2008-03-21 13:53:44 -07:00
IWL_CMD ( RADAR_NOTIFICATION ) ;
IWL_CMD ( REPLY_QUIET_CMD ) ;
IWL_CMD ( REPLY_CHANNEL_SWITCH ) ;
IWL_CMD ( CHANNEL_SWITCH_NOTIFICATION ) ;
IWL_CMD ( REPLY_SPECTRUM_MEASUREMENT_CMD ) ;
IWL_CMD ( SPECTRUM_MEASURE_NOTIFICATION ) ;
IWL_CMD ( POWER_TABLE_CMD ) ;
IWL_CMD ( PM_SLEEP_NOTIFICATION ) ;
IWL_CMD ( PM_DEBUG_STATISTIC_NOTIFIC ) ;
IWL_CMD ( REPLY_SCAN_CMD ) ;
IWL_CMD ( REPLY_SCAN_ABORT_CMD ) ;
IWL_CMD ( SCAN_START_NOTIFICATION ) ;
IWL_CMD ( SCAN_RESULTS_NOTIFICATION ) ;
IWL_CMD ( SCAN_COMPLETE_NOTIFICATION ) ;
IWL_CMD ( BEACON_NOTIFICATION ) ;
IWL_CMD ( REPLY_TX_BEACON ) ;
IWL_CMD ( WHO_IS_AWAKE_NOTIFICATION ) ;
IWL_CMD ( QUIET_NOTIFICATION ) ;
IWL_CMD ( REPLY_TX_PWR_TABLE_CMD ) ;
IWL_CMD ( MEASURE_ABORT_NOTIFICATION ) ;
IWL_CMD ( REPLY_BT_CONFIG ) ;
IWL_CMD ( REPLY_STATISTICS_CMD ) ;
IWL_CMD ( STATISTICS_NOTIFICATION ) ;
IWL_CMD ( REPLY_CARD_STATE_CMD ) ;
IWL_CMD ( CARD_STATE_NOTIFICATION ) ;
IWL_CMD ( MISSED_BEACONS_NOTIFICATION ) ;
IWL_CMD ( REPLY_CT_KILL_CONFIG_CMD ) ;
IWL_CMD ( SENSITIVITY_CMD ) ;
IWL_CMD ( REPLY_PHY_CALIBRATION_CMD ) ;
IWL_CMD ( REPLY_RX_PHY_CMD ) ;
IWL_CMD ( REPLY_RX_MPDU_CMD ) ;
IWL_CMD ( REPLY_RX ) ;
IWL_CMD ( REPLY_COMPRESSED_BA ) ;
2008-05-29 16:35:05 +08:00
IWL_CMD ( CALIBRATION_CFG_CMD ) ;
IWL_CMD ( CALIBRATION_RES_NOTIFICATION ) ;
IWL_CMD ( CALIBRATION_COMPLETE_NOTIFICATION ) ;
2008-06-30 17:23:05 +08:00
IWL_CMD ( REPLY_TX_POWER_DBM_CMD ) ;
2008-03-21 13:53:44 -07:00
default :
return " UNKNOWN " ;
}
}
EXPORT_SYMBOL ( get_cmd_string ) ;
# define HOST_COMPLETE_TIMEOUT (HZ / 2)
2008-03-25 16:33:40 -07:00
static int iwl_generic_cmd_callback ( struct iwl_priv * priv ,
struct iwl_cmd * cmd , struct sk_buff * skb )
{
2008-05-05 10:22:33 +08:00
struct iwl_rx_packet * pkt = NULL ;
2008-03-25 16:33:40 -07:00
if ( ! skb ) {
2008-12-19 10:37:33 +08:00
IWL_ERR ( priv , " Error: Response NULL in %s. \n " ,
2008-03-25 16:33:40 -07:00
get_cmd_string ( cmd - > hdr . cmd ) ) ;
return 1 ;
}
2008-05-05 10:22:33 +08:00
pkt = ( struct iwl_rx_packet * ) skb - > data ;
2008-03-25 16:33:40 -07:00
if ( pkt - > hdr . flags & IWL_CMD_FAILED_MSK ) {
2008-12-19 10:37:33 +08:00
IWL_ERR ( priv , " Bad return from %s (0x%08X) \n " ,
2008-03-25 16:33:40 -07:00
get_cmd_string ( cmd - > hdr . cmd ) , pkt - > hdr . flags ) ;
return 1 ;
}
2008-08-04 16:00:45 +08:00
# ifdef CONFIG_IWLWIFI_DEBUG
switch ( cmd - > hdr . cmd ) {
case REPLY_TX_LINK_QUALITY_CMD :
case SENSITIVITY_CMD :
2009-01-27 14:27:56 -08:00
IWL_DEBUG_HC_DUMP ( priv , " back from %s (0x%08X) \n " ,
2008-08-04 16:00:45 +08:00
get_cmd_string ( cmd - > hdr . cmd ) , pkt - > hdr . flags ) ;
break ;
default :
2009-01-27 14:27:56 -08:00
IWL_DEBUG_HC ( priv , " back from %s (0x%08X) \n " ,
2008-08-04 16:00:45 +08:00
get_cmd_string ( cmd - > hdr . cmd ) , pkt - > hdr . flags ) ;
}
# endif
2008-03-25 16:33:40 -07:00
/* Let iwl_tx_complete free the response skb */
return 1 ;
}
2008-03-21 13:53:44 -07:00
static int iwl_send_cmd_async ( struct iwl_priv * priv , struct iwl_host_cmd * cmd )
{
int ret ;
BUG_ON ( ! ( cmd - > meta . flags & CMD_ASYNC ) ) ;
/* An asynchronous command can not expect an SKB to be set. */
BUG_ON ( cmd - > meta . flags & CMD_WANT_SKB ) ;
2008-03-25 16:33:40 -07:00
/* Assign a generic callback if one is not provided */
if ( ! cmd - > meta . u . callback )
cmd - > meta . u . callback = iwl_generic_cmd_callback ;
2008-03-21 13:53:44 -07:00
if ( test_bit ( STATUS_EXIT_PENDING , & priv - > status ) )
return - EBUSY ;
2008-05-15 13:54:07 +08:00
ret = iwl_enqueue_hcmd ( priv , cmd ) ;
2008-03-21 13:53:44 -07:00
if ( ret < 0 ) {
2008-12-19 10:37:33 +08:00
IWL_ERR ( priv , " Error sending %s: enqueue_hcmd failed: %d \n " ,
2008-03-21 13:53:44 -07:00
get_cmd_string ( cmd - > id ) , ret ) ;
return ret ;
}
return 0 ;
}
int iwl_send_cmd_sync ( struct iwl_priv * priv , struct iwl_host_cmd * cmd )
{
int cmd_idx ;
int ret ;
BUG_ON ( cmd - > meta . flags & CMD_ASYNC ) ;
/* A synchronous command can not have a callback set. */
BUG_ON ( cmd - > meta . u . callback ! = NULL ) ;
2008-03-28 16:21:12 -07:00
if ( test_and_set_bit ( STATUS_HCMD_SYNC_ACTIVE , & priv - > status ) ) {
2008-12-19 10:37:33 +08:00
IWL_ERR ( priv ,
" Error sending %s: Already sending a host command \n " ,
get_cmd_string ( cmd - > id ) ) ;
2008-03-28 16:21:12 -07:00
ret = - EBUSY ;
goto out ;
2008-03-21 13:53:44 -07:00
}
set_bit ( STATUS_HCMD_ACTIVE , & priv - > status ) ;
if ( cmd - > meta . flags & CMD_WANT_SKB )
cmd - > meta . source = & cmd - > meta ;
2008-05-15 13:54:07 +08:00
cmd_idx = iwl_enqueue_hcmd ( priv , cmd ) ;
2008-03-21 13:53:44 -07:00
if ( cmd_idx < 0 ) {
ret = cmd_idx ;
2008-12-19 10:37:33 +08:00
IWL_ERR ( priv , " Error sending %s: enqueue_hcmd failed: %d \n " ,
2008-03-21 13:53:44 -07:00
get_cmd_string ( cmd - > id ) , ret ) ;
goto out ;
}
ret = wait_event_interruptible_timeout ( priv - > wait_command_queue ,
! test_bit ( STATUS_HCMD_ACTIVE , & priv - > status ) ,
HOST_COMPLETE_TIMEOUT ) ;
if ( ! ret ) {
if ( test_bit ( STATUS_HCMD_ACTIVE , & priv - > status ) ) {
2008-12-19 10:37:33 +08:00
IWL_ERR ( priv ,
" Error sending %s: time out after %dms. \n " ,
get_cmd_string ( cmd - > id ) ,
jiffies_to_msecs ( HOST_COMPLETE_TIMEOUT ) ) ;
2008-03-21 13:53:44 -07:00
clear_bit ( STATUS_HCMD_ACTIVE , & priv - > status ) ;
ret = - ETIMEDOUT ;
goto cancel ;
}
}
if ( test_bit ( STATUS_RF_KILL_HW , & priv - > status ) ) {
2009-01-27 14:27:56 -08:00
IWL_DEBUG_INFO ( priv , " Command %s aborted: RF KILL Switch \n " ,
2008-03-21 13:53:44 -07:00
get_cmd_string ( cmd - > id ) ) ;
ret = - ECANCELED ;
goto fail ;
}
if ( test_bit ( STATUS_FW_ERROR , & priv - > status ) ) {
2009-01-27 14:27:56 -08:00
IWL_DEBUG_INFO ( priv , " Command %s failed: FW Error \n " ,
2008-03-21 13:53:44 -07:00
get_cmd_string ( cmd - > id ) ) ;
ret = - EIO ;
goto fail ;
}
if ( ( cmd - > meta . flags & CMD_WANT_SKB ) & & ! cmd - > meta . u . skb ) {
2008-12-19 10:37:33 +08:00
IWL_ERR ( priv , " Error: Response NULL in '%s' \n " ,
2008-03-21 13:53:44 -07:00
get_cmd_string ( cmd - > id ) ) ;
ret = - EIO ;
2009-01-08 10:19:58 -08:00
goto cancel ;
2008-03-21 13:53:44 -07:00
}
ret = 0 ;
goto out ;
cancel :
if ( cmd - > meta . flags & CMD_WANT_SKB ) {
struct iwl_cmd * qcmd ;
/* Cancel the CMD_WANT_SKB flag for the cmd in the
* TX cmd queue . Otherwise in case the cmd comes
* in later , it will possibly set an invalid
* address ( cmd - > meta . source ) . */
2008-08-04 16:00:40 +08:00
qcmd = priv - > txq [ IWL_CMD_QUEUE_NUM ] . cmd [ cmd_idx ] ;
2008-03-21 13:53:44 -07:00
qcmd - > meta . flags & = ~ CMD_WANT_SKB ;
}
fail :
if ( cmd - > meta . u . skb ) {
dev_kfree_skb_any ( cmd - > meta . u . skb ) ;
cmd - > meta . u . skb = NULL ;
}
out :
2008-03-28 16:21:12 -07:00
clear_bit ( STATUS_HCMD_SYNC_ACTIVE , & priv - > status ) ;
2008-03-21 13:53:44 -07:00
return ret ;
}
EXPORT_SYMBOL ( iwl_send_cmd_sync ) ;
int iwl_send_cmd ( struct iwl_priv * priv , struct iwl_host_cmd * cmd )
{
if ( cmd - > meta . flags & CMD_ASYNC )
return iwl_send_cmd_async ( priv , cmd ) ;
return iwl_send_cmd_sync ( priv , cmd ) ;
}
EXPORT_SYMBOL ( iwl_send_cmd ) ;
int iwl_send_cmd_pdu ( struct iwl_priv * priv , u8 id , u16 len , const void * data )
{
struct iwl_host_cmd cmd = {
. id = id ,
. len = len ,
. data = data ,
} ;
return iwl_send_cmd_sync ( priv , & cmd ) ;
}
EXPORT_SYMBOL ( iwl_send_cmd_pdu ) ;
int iwl_send_cmd_pdu_async ( struct iwl_priv * priv ,
u8 id , u16 len , const void * data ,
int ( * callback ) ( struct iwl_priv * priv ,
struct iwl_cmd * cmd ,
struct sk_buff * skb ) )
{
struct iwl_host_cmd cmd = {
. id = id ,
. len = len ,
. data = data ,
} ;
cmd . meta . flags | = CMD_ASYNC ;
cmd . meta . u . callback = callback ;
return iwl_send_cmd_async ( priv , & cmd ) ;
}
EXPORT_SYMBOL ( iwl_send_cmd_pdu_async ) ;