2020-08-19 08:23:36 +01:00
/*
2014-03-16 03:47:02 +05:30
* Copyright ( c ) 2014 Redpine Signals Inc .
*
* Permission to use , copy , modify , and / or distribute this software for any
* purpose with or without fee is hereby granted , provided that the above
* copyright notice and this permission notice appear in all copies .
*
* THE SOFTWARE IS PROVIDED " AS IS " AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS . IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL , DIRECT , INDIRECT , OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN
* ACTION OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
*/
2014-03-18 17:59:47 -07:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2014-03-16 03:47:02 +05:30
# include <linux/module.h>
# include <linux/firmware.h>
2018-02-27 19:56:16 +05:30
# include <net/rsi_91x.h>
2014-03-16 03:47:02 +05:30
# include "rsi_mgmt.h"
# include "rsi_common.h"
2018-02-27 19:56:14 +05:30
# include "rsi_coex.h"
2017-09-21 18:20:34 +05:30
# include "rsi_hal.h"
2021-10-29 16:19:23 -04:00
# include "rsi_usb.h"
2014-03-16 03:47:02 +05:30
u32 rsi_zone_enabled = /* INFO_ZONE |
INIT_ZONE |
MGMT_TX_ZONE |
MGMT_RX_ZONE |
DATA_TX_ZONE |
DATA_RX_ZONE |
FSM_ZONE |
ISR_ZONE | */
ERR_ZONE |
0 ;
EXPORT_SYMBOL_GPL ( rsi_zone_enabled ) ;
2018-02-27 19:56:16 +05:30
# ifdef CONFIG_RSI_COEX
static struct rsi_proto_ops g_proto_ops = {
. coex_send_pkt = rsi_coex_send_pkt ,
. get_host_intf = rsi_get_host_intf ,
. set_bt_context = rsi_set_bt_context ,
} ;
# endif
2014-03-18 16:35:45 -04:00
/**
* rsi_dbg ( ) - This function outputs informational messages .
* @ zone : Zone of interest for output message .
* @ fmt : printf - style format for output message .
*
* Return : none
*/
void rsi_dbg ( u32 zone , const char * fmt , . . . )
{
struct va_format vaf ;
va_list args ;
va_start ( args , fmt ) ;
vaf . fmt = fmt ;
vaf . va = & args ;
if ( zone & rsi_zone_enabled )
pr_info ( " %pV " , & vaf ) ;
va_end ( args ) ;
}
EXPORT_SYMBOL_GPL ( rsi_dbg ) ;
2017-09-21 18:20:34 +05:30
static char * opmode_str ( int oper_mode )
{
switch ( oper_mode ) {
2018-02-27 19:56:17 +05:30
case DEV_OPMODE_WIFI_ALONE :
2017-09-21 18:20:34 +05:30
return " Wi-Fi alone " ;
2018-02-27 19:56:17 +05:30
case DEV_OPMODE_BT_ALONE :
return " BT EDR alone " ;
case DEV_OPMODE_BT_LE_ALONE :
return " BT LE alone " ;
case DEV_OPMODE_BT_DUAL :
return " BT Dual " ;
case DEV_OPMODE_STA_BT :
return " Wi-Fi STA + BT EDR " ;
case DEV_OPMODE_STA_BT_LE :
return " Wi-Fi STA + BT LE " ;
case DEV_OPMODE_STA_BT_DUAL :
return " Wi-Fi STA + BT DUAL " ;
case DEV_OPMODE_AP_BT :
return " Wi-Fi AP + BT EDR " ;
case DEV_OPMODE_AP_BT_DUAL :
return " Wi-Fi AP + BT DUAL " ;
2017-09-21 18:20:34 +05:30
}
return " Unknown " ;
}
void rsi_print_version ( struct rsi_common * common )
{
rsi_dbg ( ERR_ZONE , " ================================================ \n " ) ;
rsi_dbg ( ERR_ZONE , " ================ RSI Version Info ============== \n " ) ;
rsi_dbg ( ERR_ZONE , " ================================================ \n " ) ;
rsi_dbg ( ERR_ZONE , " FW Version \t : %d.%d.%d \n " ,
common - > lmac_ver . major , common - > lmac_ver . minor ,
common - > lmac_ver . release_num ) ;
rsi_dbg ( ERR_ZONE , " Operating mode \t : %d [%s] " ,
common - > oper_mode , opmode_str ( common - > oper_mode ) ) ;
rsi_dbg ( ERR_ZONE , " Firmware file \t : %s " , common - > priv - > fw_file_name ) ;
rsi_dbg ( ERR_ZONE , " ================================================ \n " ) ;
}
2014-03-16 03:47:02 +05:30
/**
* rsi_prepare_skb ( ) - This function prepares the skb .
* @ common : Pointer to the driver private structure .
* @ buffer : Pointer to the packet data .
* @ pkt_len : Length of the packet .
* @ extended_desc : Extended descriptor .
*
* Return : Successfully skb .
*/
static struct sk_buff * rsi_prepare_skb ( struct rsi_common * common ,
u8 * buffer ,
u32 pkt_len ,
u8 extended_desc )
{
struct sk_buff * skb = NULL ;
u8 payload_offset ;
if ( WARN ( ! pkt_len , " %s: Dummy pkt received " , __func__ ) )
return NULL ;
if ( pkt_len > ( RSI_RCV_BUFFER_LEN * 4 ) ) {
rsi_dbg ( ERR_ZONE , " %s: Pkt size > max rx buf size %d \n " ,
__func__ , pkt_len ) ;
pkt_len = RSI_RCV_BUFFER_LEN * 4 ;
}
pkt_len - = extended_desc ;
skb = dev_alloc_skb ( pkt_len + FRAME_DESC_SZ ) ;
if ( skb = = NULL )
return NULL ;
payload_offset = ( extended_desc + FRAME_DESC_SZ ) ;
skb_put ( skb , pkt_len ) ;
memcpy ( ( skb - > data ) , ( buffer + payload_offset ) , skb - > len ) ;
return skb ;
}
/**
* rsi_read_pkt ( ) - This function reads frames from the card .
* @ common : Pointer to the driver private structure .
2020-08-19 08:23:51 +01:00
* @ rx_pkt : Received pkt .
2014-03-16 03:47:02 +05:30
* @ rcv_pkt_len : Received pkt length . In case of USB it is 0.
*
* Return : 0 on success , - 1 on failure .
*/
2018-02-27 19:56:11 +05:30
int rsi_read_pkt ( struct rsi_common * common , u8 * rx_pkt , s32 rcv_pkt_len )
2014-03-16 03:47:02 +05:30
{
u8 * frame_desc = NULL , extended_desc = 0 ;
u32 index , length = 0 , queueno = 0 ;
u16 actual_length = 0 , offset ;
struct sk_buff * skb = NULL ;
2018-02-27 19:56:16 +05:30
# ifdef CONFIG_RSI_COEX
u8 bt_pkt_type ;
# endif
2014-03-16 03:47:02 +05:30
index = 0 ;
do {
2018-02-27 19:56:11 +05:30
frame_desc = & rx_pkt [ index ] ;
2014-03-16 03:47:02 +05:30
actual_length = * ( u16 * ) & frame_desc [ 0 ] ;
offset = * ( u16 * ) & frame_desc [ 2 ] ;
2021-10-29 16:19:23 -04:00
if ( ! rcv_pkt_len & & offset >
RSI_MAX_RX_USB_PKT_SIZE - FRAME_DESC_SZ )
goto fail ;
2014-03-16 03:47:02 +05:30
queueno = rsi_get_queueno ( frame_desc , offset ) ;
length = rsi_get_length ( frame_desc , offset ) ;
2017-06-16 20:05:39 +05:30
/* Extended descriptor is valid for WLAN queues only */
if ( queueno = = RSI_WIFI_DATA_Q | | queueno = = RSI_WIFI_MGMT_Q )
extended_desc = rsi_get_extended_desc ( frame_desc ,
offset ) ;
2014-03-16 03:47:02 +05:30
switch ( queueno ) {
2017-06-16 20:05:39 +05:30
case RSI_COEX_Q :
2018-02-27 19:56:14 +05:30
# ifdef CONFIG_RSI_COEX
if ( common - > coex_mode > 1 )
rsi_coex_recv_pkt ( common , frame_desc + offset ) ;
else
# endif
rsi_mgmt_pkt_recv ( common ,
( frame_desc + offset ) ) ;
2017-06-16 20:05:39 +05:30
break ;
2018-02-27 19:56:14 +05:30
2014-03-16 03:47:02 +05:30
case RSI_WIFI_DATA_Q :
skb = rsi_prepare_skb ( common ,
( frame_desc + offset ) ,
length ,
extended_desc ) ;
if ( skb = = NULL )
goto fail ;
rsi_indicate_pkt_to_os ( common , skb ) ;
break ;
case RSI_WIFI_MGMT_Q :
rsi_mgmt_pkt_recv ( common , ( frame_desc + offset ) ) ;
break ;
2018-02-27 19:56:16 +05:30
# ifdef CONFIG_RSI_COEX
case RSI_BT_MGMT_Q :
case RSI_BT_DATA_Q :
# define BT_RX_PKT_TYPE_OFST 14
# define BT_CARD_READY_IND 0x89
bt_pkt_type = frame_desc [ offset + BT_RX_PKT_TYPE_OFST ] ;
if ( bt_pkt_type = = BT_CARD_READY_IND ) {
rsi_dbg ( INFO_ZONE , " BT Card ready recvd \n " ) ;
2021-08-30 17:26:44 +02:00
if ( common - > fsm_state = = FSM_MAC_INIT_DONE )
rsi_attach_bt ( common ) ;
else
common - > bt_defer_attach = true ;
2018-02-27 19:56:16 +05:30
} else {
if ( common - > bt_adapter )
rsi_bt_ops . recv_pkt ( common - > bt_adapter ,
frame_desc + offset ) ;
}
break ;
# endif
2014-03-16 03:47:02 +05:30
default :
rsi_dbg ( ERR_ZONE , " %s: pkt from invalid queue: %d \n " ,
__func__ , queueno ) ;
goto fail ;
}
index + = actual_length ;
rcv_pkt_len - = actual_length ;
} while ( rcv_pkt_len > 0 ) ;
return 0 ;
fail :
return - EINVAL ;
}
EXPORT_SYMBOL_GPL ( rsi_read_pkt ) ;
/**
* rsi_tx_scheduler_thread ( ) - This function is a kernel thread to send the
* packets to the device .
* @ common : Pointer to the driver private structure .
*
* Return : None .
*/
static void rsi_tx_scheduler_thread ( struct rsi_common * common )
{
struct rsi_hw * adapter = common - > priv ;
u32 timeout = EVENT_WAIT_FOREVER ;
do {
if ( adapter - > determine_event_timeout )
timeout = adapter - > determine_event_timeout ( adapter ) ;
rsi_wait_event ( & common - > tx_thread . event , timeout ) ;
rsi_reset_event ( & common - > tx_thread . event ) ;
if ( common - > init_done )
rsi_core_qos_processor ( common ) ;
} while ( atomic_read ( & common - > tx_thread . thread_done ) = = 0 ) ;
2021-11-22 11:15:19 -06:00
kthread_complete_and_exit ( & common - > tx_thread . completion , 0 ) ;
2014-03-16 03:47:02 +05:30
}
2018-02-27 19:56:14 +05:30
# ifdef CONFIG_RSI_COEX
enum rsi_host_intf rsi_get_host_intf ( void * priv )
{
struct rsi_common * common = ( struct rsi_common * ) priv ;
return common - > priv - > rsi_host_intf ;
}
2018-02-27 19:56:16 +05:30
void rsi_set_bt_context ( void * priv , void * bt_context )
{
struct rsi_common * common = ( struct rsi_common * ) priv ;
common - > bt_adapter = bt_context ;
}
2018-02-27 19:56:14 +05:30
# endif
2021-08-30 17:26:44 +02:00
void rsi_attach_bt ( struct rsi_common * common )
{
# ifdef CONFIG_RSI_COEX
if ( rsi_bt_ops . attach ( common , & g_proto_ops ) )
rsi_dbg ( ERR_ZONE ,
" Failed to attach BT module \n " ) ;
# endif
}
2014-03-16 03:47:02 +05:30
/**
* rsi_91x_init ( ) - This function initializes os interface operations .
2020-08-19 08:23:36 +01:00
* @ oper_mode : One of DEV_OPMODE_ * .
2014-03-16 03:47:02 +05:30
*
* Return : Pointer to the adapter structure on success , NULL on failure .
*/
2018-02-27 19:56:17 +05:30
struct rsi_hw * rsi_91x_init ( u16 oper_mode )
2014-03-16 03:47:02 +05:30
{
struct rsi_hw * adapter = NULL ;
struct rsi_common * common = NULL ;
u8 ii = 0 ;
adapter = kzalloc ( sizeof ( * adapter ) , GFP_KERNEL ) ;
if ( ! adapter )
return NULL ;
adapter - > priv = kzalloc ( sizeof ( * common ) , GFP_KERNEL ) ;
if ( adapter - > priv = = NULL ) {
rsi_dbg ( ERR_ZONE , " %s: Failed in allocation of memory \n " ,
__func__ ) ;
kfree ( adapter ) ;
return NULL ;
} else {
common = adapter - > priv ;
common - > priv = adapter ;
}
for ( ii = 0 ; ii < NUM_SOFT_QUEUES ; ii + + )
skb_queue_head_init ( & common - > tx_queue [ ii ] ) ;
rsi_init_event ( & common - > tx_thread . event ) ;
mutex_init ( & common - > mutex ) ;
2017-07-06 20:07:24 +05:30
mutex_init ( & common - > tx_lock ) ;
2017-07-06 20:07:23 +05:30
mutex_init ( & common - > rx_lock ) ;
2018-02-27 19:56:14 +05:30
mutex_init ( & common - > tx_bus_mutex ) ;
2014-03-16 03:47:02 +05:30
if ( rsi_create_kthread ( common ,
& common - > tx_thread ,
rsi_tx_scheduler_thread ,
" Tx-Thread " ) ) {
rsi_dbg ( ERR_ZONE , " %s: Unable to init tx thrd \n " , __func__ ) ;
goto err ;
}
2017-08-03 19:58:59 +05:30
rsi_default_ps_params ( adapter ) ;
2018-12-27 14:56:18 +05:30
init_bgscan_params ( common ) ;
2017-08-03 19:58:59 +05:30
spin_lock_init ( & adapter - > ps_lock ) ;
2017-10-24 02:29:09 -07:00
timer_setup ( & common - > roc_timer , rsi_roc_timeout , 0 ) ;
2017-10-27 16:55:56 +05:30
init_completion ( & common - > wlan_init_completion ) ;
2018-02-27 19:56:17 +05:30
adapter - > device_model = RSI_DEV_9113 ;
common - > oper_mode = oper_mode ;
/* Determine coex mode */
switch ( common - > oper_mode ) {
case DEV_OPMODE_STA_BT_DUAL :
case DEV_OPMODE_STA_BT :
case DEV_OPMODE_STA_BT_LE :
case DEV_OPMODE_BT_ALONE :
case DEV_OPMODE_BT_LE_ALONE :
case DEV_OPMODE_BT_DUAL :
common - > coex_mode = 2 ;
break ;
case DEV_OPMODE_AP_BT_DUAL :
case DEV_OPMODE_AP_BT :
common - > coex_mode = 4 ;
break ;
case DEV_OPMODE_WIFI_ALONE :
common - > coex_mode = 1 ;
break ;
default :
common - > oper_mode = 1 ;
common - > coex_mode = 1 ;
}
rsi_dbg ( INFO_ZONE , " %s: oper_mode = %d, coex_mode = %d \n " ,
__func__ , common - > oper_mode , common - > coex_mode ) ;
2018-02-27 19:56:14 +05:30
adapter - > device_model = RSI_DEV_9113 ;
# ifdef CONFIG_RSI_COEX
if ( common - > coex_mode > 1 ) {
if ( rsi_coex_attach ( common ) ) {
rsi_dbg ( ERR_ZONE , " Failed to init coex module \n " ) ;
2021-10-15 12:03:35 +08:00
rsi_kill_thread ( & common - > tx_thread ) ;
2018-02-27 19:56:14 +05:30
goto err ;
}
}
# endif
2018-07-16 19:09:37 +05:30
common - > init_done = true ;
2014-03-16 03:47:02 +05:30
return adapter ;
err :
kfree ( common ) ;
kfree ( adapter ) ;
return NULL ;
}
EXPORT_SYMBOL_GPL ( rsi_91x_init ) ;
/**
* rsi_91x_deinit ( ) - This function de - intializes os intf operations .
* @ adapter : Pointer to the adapter structure .
*
* Return : None .
*/
void rsi_91x_deinit ( struct rsi_hw * adapter )
{
struct rsi_common * common = adapter - > priv ;
u8 ii ;
rsi_dbg ( INFO_ZONE , " %s: Performing deinit os ops \n " , __func__ ) ;
rsi_kill_thread ( & common - > tx_thread ) ;
for ( ii = 0 ; ii < NUM_SOFT_QUEUES ; ii + + )
skb_queue_purge ( & common - > tx_queue [ ii ] ) ;
2018-02-27 19:56:14 +05:30
# ifdef CONFIG_RSI_COEX
2018-02-27 19:56:16 +05:30
if ( common - > coex_mode > 1 ) {
if ( common - > bt_adapter ) {
rsi_bt_ops . detach ( common - > bt_adapter ) ;
common - > bt_adapter = NULL ;
}
2018-02-27 19:56:14 +05:30
rsi_coex_detach ( common ) ;
2018-02-27 19:56:16 +05:30
}
2018-02-27 19:56:14 +05:30
# endif
2018-02-27 19:56:16 +05:30
common - > init_done = false ;
2014-03-16 03:47:02 +05:30
kfree ( common ) ;
kfree ( adapter - > rsi_dev ) ;
kfree ( adapter ) ;
}
EXPORT_SYMBOL_GPL ( rsi_91x_deinit ) ;
/**
* rsi_91x_hal_module_init ( ) - This function is invoked when the module is
* loaded into the kernel .
* It registers the client driver .
* @ void : Void .
*
* Return : 0 on success , - 1 on failure .
*/
static int rsi_91x_hal_module_init ( void )
{
rsi_dbg ( INIT_ZONE , " %s: Module init called \n " , __func__ ) ;
return 0 ;
}
/**
* rsi_91x_hal_module_exit ( ) - This function is called at the time of
* removing / unloading the module .
* It unregisters the client driver .
* @ void : Void .
*
* Return : None .
*/
static void rsi_91x_hal_module_exit ( void )
{
rsi_dbg ( INIT_ZONE , " %s: Module exit called \n " , __func__ ) ;
}
module_init ( rsi_91x_hal_module_init ) ;
module_exit ( rsi_91x_hal_module_exit ) ;
MODULE_AUTHOR ( " Redpine Signals Inc " ) ;
MODULE_DESCRIPTION ( " Station driver for RSI 91x devices " ) ;
MODULE_VERSION ( " 0.1 " ) ;
MODULE_LICENSE ( " Dual BSD/GPL " ) ;