2009-03-10 10:13:33 +01:00
/*
2009-07-16 14:00:45 +02:00
* drivers / net / wireless / mwl8k . c
* Driver for Marvell TOPDOG 802.11 Wireless cards
2009-03-10 10:13:33 +01:00
*
2009-08-18 04:34:26 +02:00
* Copyright ( C ) 2008 - 2009 Marvell Semiconductor Inc .
2009-03-10 10:13:33 +01:00
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/spinlock.h>
# include <linux/list.h>
# include <linux/pci.h>
# include <linux/delay.h>
# include <linux/completion.h>
# include <linux/etherdevice.h>
# include <net/mac80211.h>
# include <linux/moduleparam.h>
# include <linux/firmware.h>
# include <linux/workqueue.h>
# define MWL8K_DESC "Marvell TOPDOG(R) 802.11 Wireless Network Driver"
# define MWL8K_NAME KBUILD_MODNAME
2009-08-18 04:34:26 +02:00
# define MWL8K_VERSION "0.10"
2009-03-10 10:13:33 +01:00
MODULE_DESCRIPTION ( MWL8K_DESC ) ;
MODULE_VERSION ( MWL8K_VERSION ) ;
MODULE_AUTHOR ( " Lennert Buytenhek <buytenh@marvell.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;
static DEFINE_PCI_DEVICE_TABLE ( mwl8k_table ) = {
{ PCI_VDEVICE ( MARVELL , 0x2a2b ) , . driver_data = 8687 , } ,
{ PCI_VDEVICE ( MARVELL , 0x2a30 ) , . driver_data = 8687 , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( pci , mwl8k_table ) ;
/* Register definitions */
# define MWL8K_HIU_GEN_PTR 0x00000c10
2009-07-16 14:00:45 +02:00
# define MWL8K_MODE_STA 0x0000005a
# define MWL8K_MODE_AP 0x000000a5
2009-03-10 10:13:33 +01:00
# define MWL8K_HIU_INT_CODE 0x00000c14
2009-07-16 14:00:45 +02:00
# define MWL8K_FWSTA_READY 0xf0f1f2f4
# define MWL8K_FWAP_READY 0xf1f2f4a5
# define MWL8K_INT_CODE_CMD_FINISHED 0x00000005
2009-03-10 10:13:33 +01:00
# define MWL8K_HIU_SCRATCH 0x00000c40
/* Host->device communications */
# define MWL8K_HIU_H2A_INTERRUPT_EVENTS 0x00000c18
# define MWL8K_HIU_H2A_INTERRUPT_STATUS 0x00000c1c
# define MWL8K_HIU_H2A_INTERRUPT_MASK 0x00000c20
# define MWL8K_HIU_H2A_INTERRUPT_CLEAR_SEL 0x00000c24
# define MWL8K_HIU_H2A_INTERRUPT_STATUS_MASK 0x00000c28
2009-07-16 14:00:45 +02:00
# define MWL8K_H2A_INT_DUMMY (1 << 20)
# define MWL8K_H2A_INT_RESET (1 << 15)
# define MWL8K_H2A_INT_DOORBELL (1 << 1)
# define MWL8K_H2A_INT_PPA_READY (1 << 0)
2009-03-10 10:13:33 +01:00
/* Device->host communications */
# define MWL8K_HIU_A2H_INTERRUPT_EVENTS 0x00000c2c
# define MWL8K_HIU_A2H_INTERRUPT_STATUS 0x00000c30
# define MWL8K_HIU_A2H_INTERRUPT_MASK 0x00000c34
# define MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL 0x00000c38
# define MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK 0x00000c3c
2009-07-16 14:00:45 +02:00
# define MWL8K_A2H_INT_DUMMY (1 << 20)
# define MWL8K_A2H_INT_CHNL_SWITCHED (1 << 11)
# define MWL8K_A2H_INT_QUEUE_EMPTY (1 << 10)
# define MWL8K_A2H_INT_RADAR_DETECT (1 << 7)
# define MWL8K_A2H_INT_RADIO_ON (1 << 6)
# define MWL8K_A2H_INT_RADIO_OFF (1 << 5)
# define MWL8K_A2H_INT_MAC_EVENT (1 << 3)
# define MWL8K_A2H_INT_OPC_DONE (1 << 2)
# define MWL8K_A2H_INT_RX_READY (1 << 1)
# define MWL8K_A2H_INT_TX_DONE (1 << 0)
2009-03-10 10:13:33 +01:00
# define MWL8K_A2H_EVENTS (MWL8K_A2H_INT_DUMMY | \
MWL8K_A2H_INT_CHNL_SWITCHED | \
MWL8K_A2H_INT_QUEUE_EMPTY | \
MWL8K_A2H_INT_RADAR_DETECT | \
MWL8K_A2H_INT_RADIO_ON | \
MWL8K_A2H_INT_RADIO_OFF | \
MWL8K_A2H_INT_MAC_EVENT | \
MWL8K_A2H_INT_OPC_DONE | \
MWL8K_A2H_INT_RX_READY | \
MWL8K_A2H_INT_TX_DONE )
/* WME stream classes */
# define WME_AC_BE 0 /* best effort */
# define WME_AC_BK 1 /* background */
# define WME_AC_VI 2 /* video */
# define WME_AC_VO 3 /* voice */
# define MWL8K_RX_QUEUES 1
# define MWL8K_TX_QUEUES 4
struct mwl8k_rx_queue {
int rx_desc_count ;
/* hw receives here */
int rx_head ;
/* refill descs here */
int rx_tail ;
struct mwl8k_rx_desc * rx_desc_area ;
dma_addr_t rx_desc_dma ;
struct sk_buff * * rx_skb ;
} ;
struct mwl8k_tx_queue {
/* hw transmits here */
int tx_head ;
/* sw appends here */
int tx_tail ;
struct ieee80211_tx_queue_stats tx_stats ;
struct mwl8k_tx_desc * tx_desc_area ;
dma_addr_t tx_desc_dma ;
2009-07-16 11:07:09 +02:00
struct sk_buff * * tx_skb ;
2009-03-10 10:13:33 +01:00
} ;
/* Pointers to the firmware data and meta information about it. */
struct mwl8k_firmware {
/* Microcode */
struct firmware * ucode ;
/* Boot helper code */
struct firmware * helper ;
} ;
struct mwl8k_priv {
void __iomem * regs ;
struct ieee80211_hw * hw ;
struct pci_dev * pdev ;
u8 name [ 16 ] ;
/* firmware files and meta data */
struct mwl8k_firmware fw ;
u32 part_num ;
2009-08-18 03:18:01 +02:00
/* firmware access */
struct mutex fw_mutex ;
struct task_struct * fw_mutex_owner ;
int fw_mutex_depth ;
struct completion * tx_wait ;
struct completion * hostcmd_wait ;
2009-03-10 10:13:33 +01:00
/* lock held over TX and TX reap */
spinlock_t tx_lock ;
struct ieee80211_vif * vif ;
struct ieee80211_channel * current_channel ;
/* power management status cookie from firmware */
u32 * cookie ;
dma_addr_t cookie_dma ;
u16 num_mcaddrs ;
u8 hw_rev ;
__le32 fw_rev ;
/*
* Running count of TX packets in flight , to avoid
* iterating over the transmit rings each time .
*/
int pending_tx_pkts ;
struct mwl8k_rx_queue rxq [ MWL8K_RX_QUEUES ] ;
struct mwl8k_tx_queue txq [ MWL8K_TX_QUEUES ] ;
/* PHY parameters */
struct ieee80211_supported_band band ;
struct ieee80211_channel channels [ 14 ] ;
struct ieee80211_rate rates [ 12 ] ;
2009-07-16 12:14:58 +02:00
bool radio_on ;
2009-07-16 12:26:57 +02:00
bool radio_short_preamble ;
2009-07-16 12:34:02 +02:00
bool wmm_enabled ;
2009-03-10 10:13:33 +01:00
/* XXX need to convert this to handle multiple interfaces */
bool capture_beacon ;
2009-07-16 09:54:27 +02:00
u8 capture_bssid [ ETH_ALEN ] ;
2009-03-10 10:13:33 +01:00
struct sk_buff * beacon_skb ;
/*
* This FJ worker has to be global as it is scheduled from the
* RX handler . At this point we don ' t know which interface it
* belongs to until the list of bssids waiting to complete join
* is checked .
*/
struct work_struct finalize_join_worker ;
/* Tasklet to reclaim TX descriptors and buffers after tx */
struct tasklet_struct tx_reclaim_task ;
/* Work thread to serialize configuration requests */
struct workqueue_struct * config_wq ;
} ;
/* Per interface specific private data */
struct mwl8k_vif {
/* backpointer to parent config block */
struct mwl8k_priv * priv ;
/* BSS config of AP or IBSS from mac80211*/
struct ieee80211_bss_conf bss_info ;
/* BSSID of AP or IBSS */
2009-07-16 09:54:27 +02:00
u8 bssid [ ETH_ALEN ] ;
u8 mac_addr [ ETH_ALEN ] ;
2009-03-10 10:13:33 +01:00
/*
* Subset of supported legacy rates .
* Intersection of AP and STA supported rates .
*/
struct ieee80211_rate legacy_rates [ 12 ] ;
/* number of supported legacy rates */
u8 legacy_nrates ;
/* Index into station database.Returned by update_sta_db call */
u8 peer_id ;
/* Non AMPDU sequence number assigned by driver */
u16 seqno ;
} ;
2009-08-03 21:58:57 +02:00
# define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv))
2009-03-10 10:13:33 +01:00
static const struct ieee80211_channel mwl8k_channels [ ] = {
{ . center_freq = 2412 , . hw_value = 1 , } ,
{ . center_freq = 2417 , . hw_value = 2 , } ,
{ . center_freq = 2422 , . hw_value = 3 , } ,
{ . center_freq = 2427 , . hw_value = 4 , } ,
{ . center_freq = 2432 , . hw_value = 5 , } ,
{ . center_freq = 2437 , . hw_value = 6 , } ,
{ . center_freq = 2442 , . hw_value = 7 , } ,
{ . center_freq = 2447 , . hw_value = 8 , } ,
{ . center_freq = 2452 , . hw_value = 9 , } ,
{ . center_freq = 2457 , . hw_value = 10 , } ,
{ . center_freq = 2462 , . hw_value = 11 , } ,
} ;
static const struct ieee80211_rate mwl8k_rates [ ] = {
{ . bitrate = 10 , . hw_value = 2 , } ,
{ . bitrate = 20 , . hw_value = 4 , } ,
{ . bitrate = 55 , . hw_value = 11 , } ,
{ . bitrate = 60 , . hw_value = 12 , } ,
{ . bitrate = 90 , . hw_value = 18 , } ,
{ . bitrate = 110 , . hw_value = 22 , } ,
{ . bitrate = 120 , . hw_value = 24 , } ,
{ . bitrate = 180 , . hw_value = 36 , } ,
{ . bitrate = 240 , . hw_value = 48 , } ,
{ . bitrate = 360 , . hw_value = 72 , } ,
{ . bitrate = 480 , . hw_value = 96 , } ,
{ . bitrate = 540 , . hw_value = 108 , } ,
} ;
/* Set or get info from Firmware */
# define MWL8K_CMD_SET 0x0001
# define MWL8K_CMD_GET 0x0000
/* Firmware command codes */
# define MWL8K_CMD_CODE_DNLD 0x0001
# define MWL8K_CMD_GET_HW_SPEC 0x0003
# define MWL8K_CMD_MAC_MULTICAST_ADR 0x0010
# define MWL8K_CMD_GET_STAT 0x0014
2009-07-16 11:50:36 +02:00
# define MWL8K_CMD_RADIO_CONTROL 0x001c
# define MWL8K_CMD_RF_TX_POWER 0x001e
2009-03-10 10:13:33 +01:00
# define MWL8K_CMD_SET_PRE_SCAN 0x0107
# define MWL8K_CMD_SET_POST_SCAN 0x0108
2009-07-16 11:50:36 +02:00
# define MWL8K_CMD_SET_RF_CHANNEL 0x010a
# define MWL8K_CMD_SET_AID 0x010d
# define MWL8K_CMD_SET_RATE 0x0110
# define MWL8K_CMD_SET_FINALIZE_JOIN 0x0111
# define MWL8K_CMD_RTS_THRESHOLD 0x0113
2009-03-10 10:13:33 +01:00
# define MWL8K_CMD_SET_SLOT 0x0114
2009-07-16 11:50:36 +02:00
# define MWL8K_CMD_SET_EDCA_PARAMS 0x0115
# define MWL8K_CMD_SET_WMM_MODE 0x0123
2009-03-10 10:13:33 +01:00
# define MWL8K_CMD_MIMO_CONFIG 0x0125
2009-07-16 11:50:36 +02:00
# define MWL8K_CMD_USE_FIXED_RATE 0x0126
2009-03-10 10:13:33 +01:00
# define MWL8K_CMD_ENABLE_SNIFFER 0x0150
# define MWL8K_CMD_SET_RATEADAPT_MODE 0x0203
2009-07-16 11:50:36 +02:00
# define MWL8K_CMD_UPDATE_STADB 0x1123
2009-03-10 10:13:33 +01:00
static const char * mwl8k_cmd_name ( u16 cmd , char * buf , int bufsize )
{
# define MWL8K_CMDNAME(x) case MWL8K_CMD_##x: do {\
snprintf ( buf , bufsize , " %s " , # x ) ; \
return buf ; \
} while ( 0 )
2009-07-16 14:00:45 +02:00
switch ( cmd & ~ 0x8000 ) {
2009-03-10 10:13:33 +01:00
MWL8K_CMDNAME ( CODE_DNLD ) ;
MWL8K_CMDNAME ( GET_HW_SPEC ) ;
MWL8K_CMDNAME ( MAC_MULTICAST_ADR ) ;
MWL8K_CMDNAME ( GET_STAT ) ;
MWL8K_CMDNAME ( RADIO_CONTROL ) ;
MWL8K_CMDNAME ( RF_TX_POWER ) ;
MWL8K_CMDNAME ( SET_PRE_SCAN ) ;
MWL8K_CMDNAME ( SET_POST_SCAN ) ;
MWL8K_CMDNAME ( SET_RF_CHANNEL ) ;
2009-07-16 11:50:36 +02:00
MWL8K_CMDNAME ( SET_AID ) ;
MWL8K_CMDNAME ( SET_RATE ) ;
MWL8K_CMDNAME ( SET_FINALIZE_JOIN ) ;
MWL8K_CMDNAME ( RTS_THRESHOLD ) ;
2009-03-10 10:13:33 +01:00
MWL8K_CMDNAME ( SET_SLOT ) ;
2009-07-16 11:50:36 +02:00
MWL8K_CMDNAME ( SET_EDCA_PARAMS ) ;
MWL8K_CMDNAME ( SET_WMM_MODE ) ;
2009-03-10 10:13:33 +01:00
MWL8K_CMDNAME ( MIMO_CONFIG ) ;
2009-07-16 11:50:36 +02:00
MWL8K_CMDNAME ( USE_FIXED_RATE ) ;
2009-03-10 10:13:33 +01:00
MWL8K_CMDNAME ( ENABLE_SNIFFER ) ;
MWL8K_CMDNAME ( SET_RATEADAPT_MODE ) ;
2009-07-16 11:50:36 +02:00
MWL8K_CMDNAME ( UPDATE_STADB ) ;
2009-03-10 10:13:33 +01:00
default :
snprintf ( buf , bufsize , " 0x%x " , cmd ) ;
}
# undef MWL8K_CMDNAME
return buf ;
}
/* Hardware and firmware reset */
static void mwl8k_hw_reset ( struct mwl8k_priv * priv )
{
iowrite32 ( MWL8K_H2A_INT_RESET ,
priv - > regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS ) ;
iowrite32 ( MWL8K_H2A_INT_RESET ,
priv - > regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS ) ;
msleep ( 20 ) ;
}
/* Release fw image */
static void mwl8k_release_fw ( struct firmware * * fw )
{
if ( * fw = = NULL )
return ;
release_firmware ( * fw ) ;
* fw = NULL ;
}
static void mwl8k_release_firmware ( struct mwl8k_priv * priv )
{
mwl8k_release_fw ( & priv - > fw . ucode ) ;
mwl8k_release_fw ( & priv - > fw . helper ) ;
}
/* Request fw image */
static int mwl8k_request_fw ( struct mwl8k_priv * priv ,
const char * fname , struct firmware * * fw )
{
/* release current image */
if ( * fw ! = NULL )
mwl8k_release_fw ( fw ) ;
return request_firmware ( ( const struct firmware * * ) fw ,
fname , & priv - > pdev - > dev ) ;
}
static int mwl8k_request_firmware ( struct mwl8k_priv * priv , u32 part_num )
{
u8 filename [ 64 ] ;
int rc ;
priv - > part_num = part_num ;
snprintf ( filename , sizeof ( filename ) ,
" mwl8k/helper_%u.fw " , priv - > part_num ) ;
rc = mwl8k_request_fw ( priv , filename , & priv - > fw . helper ) ;
if ( rc ) {
printk ( KERN_ERR
" %s Error requesting helper firmware file %s \n " ,
pci_name ( priv - > pdev ) , filename ) ;
return rc ;
}
snprintf ( filename , sizeof ( filename ) ,
" mwl8k/fmimage_%u.fw " , priv - > part_num ) ;
rc = mwl8k_request_fw ( priv , filename , & priv - > fw . ucode ) ;
if ( rc ) {
printk ( KERN_ERR " %s Error requesting firmware file %s \n " ,
pci_name ( priv - > pdev ) , filename ) ;
mwl8k_release_fw ( & priv - > fw . helper ) ;
return rc ;
}
return 0 ;
}
struct mwl8k_cmd_pkt {
__le16 code ;
__le16 length ;
__le16 seq_num ;
__le16 result ;
char payload [ 0 ] ;
} __attribute__ ( ( packed ) ) ;
/*
* Firmware loading .
*/
static int
mwl8k_send_fw_load_cmd ( struct mwl8k_priv * priv , void * data , int length )
{
void __iomem * regs = priv - > regs ;
dma_addr_t dma_addr ;
int loops ;
dma_addr = pci_map_single ( priv - > pdev , data , length , PCI_DMA_TODEVICE ) ;
if ( pci_dma_mapping_error ( priv - > pdev , dma_addr ) )
return - ENOMEM ;
iowrite32 ( dma_addr , regs + MWL8K_HIU_GEN_PTR ) ;
iowrite32 ( 0 , regs + MWL8K_HIU_INT_CODE ) ;
iowrite32 ( MWL8K_H2A_INT_DOORBELL ,
regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS ) ;
iowrite32 ( MWL8K_H2A_INT_DUMMY ,
regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS ) ;
loops = 1000 ;
do {
u32 int_code ;
int_code = ioread32 ( regs + MWL8K_HIU_INT_CODE ) ;
if ( int_code = = MWL8K_INT_CODE_CMD_FINISHED ) {
iowrite32 ( 0 , regs + MWL8K_HIU_INT_CODE ) ;
break ;
}
udelay ( 1 ) ;
} while ( - - loops ) ;
pci_unmap_single ( priv - > pdev , dma_addr , length , PCI_DMA_TODEVICE ) ;
2009-07-16 12:44:45 +02:00
return loops ? 0 : - ETIMEDOUT ;
2009-03-10 10:13:33 +01:00
}
static int mwl8k_load_fw_image ( struct mwl8k_priv * priv ,
const u8 * data , size_t length )
{
struct mwl8k_cmd_pkt * cmd ;
int done ;
int rc = 0 ;
cmd = kmalloc ( sizeof ( * cmd ) + 256 , GFP_KERNEL ) ;
if ( cmd = = NULL )
return - ENOMEM ;
cmd - > code = cpu_to_le16 ( MWL8K_CMD_CODE_DNLD ) ;
cmd - > seq_num = 0 ;
cmd - > result = 0 ;
done = 0 ;
while ( length ) {
int block_size = length > 256 ? 256 : length ;
memcpy ( cmd - > payload , data + done , block_size ) ;
cmd - > length = cpu_to_le16 ( block_size ) ;
rc = mwl8k_send_fw_load_cmd ( priv , cmd ,
sizeof ( * cmd ) + block_size ) ;
if ( rc )
break ;
done + = block_size ;
length - = block_size ;
}
if ( ! rc ) {
cmd - > length = 0 ;
rc = mwl8k_send_fw_load_cmd ( priv , cmd , sizeof ( * cmd ) ) ;
}
kfree ( cmd ) ;
return rc ;
}
static int mwl8k_feed_fw_image ( struct mwl8k_priv * priv ,
const u8 * data , size_t length )
{
unsigned char * buffer ;
int may_continue , rc = 0 ;
u32 done , prev_block_size ;
buffer = kmalloc ( 1024 , GFP_KERNEL ) ;
if ( buffer = = NULL )
return - ENOMEM ;
done = 0 ;
prev_block_size = 0 ;
may_continue = 1000 ;
while ( may_continue > 0 ) {
u32 block_size ;
block_size = ioread32 ( priv - > regs + MWL8K_HIU_SCRATCH ) ;
if ( block_size & 1 ) {
block_size & = ~ 1 ;
may_continue - - ;
} else {
done + = prev_block_size ;
length - = prev_block_size ;
}
if ( block_size > 1024 | | block_size > length ) {
rc = - EOVERFLOW ;
break ;
}
if ( length = = 0 ) {
rc = 0 ;
break ;
}
if ( block_size = = 0 ) {
rc = - EPROTO ;
may_continue - - ;
udelay ( 1 ) ;
continue ;
}
prev_block_size = block_size ;
memcpy ( buffer , data + done , block_size ) ;
rc = mwl8k_send_fw_load_cmd ( priv , buffer , block_size ) ;
if ( rc )
break ;
}
if ( ! rc & & length ! = 0 )
rc = - EREMOTEIO ;
kfree ( buffer ) ;
return rc ;
}
static int mwl8k_load_firmware ( struct mwl8k_priv * priv )
{
int loops , rc ;
const u8 * ucode = priv - > fw . ucode - > data ;
size_t ucode_len = priv - > fw . ucode - > size ;
const u8 * helper = priv - > fw . helper - > data ;
size_t helper_len = priv - > fw . helper - > size ;
if ( ! memcmp ( ucode , " \x01 \x00 \x00 \x00 " , 4 ) ) {
rc = mwl8k_load_fw_image ( priv , helper , helper_len ) ;
if ( rc ) {
printk ( KERN_ERR " %s: unable to load firmware "
" helper image \n " , pci_name ( priv - > pdev ) ) ;
return rc ;
}
msleep ( 1 ) ;
rc = mwl8k_feed_fw_image ( priv , ucode , ucode_len ) ;
} else {
rc = mwl8k_load_fw_image ( priv , ucode , ucode_len ) ;
}
if ( rc ) {
printk ( KERN_ERR " %s: unable to load firmware data \n " ,
pci_name ( priv - > pdev ) ) ;
return rc ;
}
iowrite32 ( MWL8K_MODE_STA , priv - > regs + MWL8K_HIU_GEN_PTR ) ;
msleep ( 1 ) ;
loops = 200000 ;
do {
if ( ioread32 ( priv - > regs + MWL8K_HIU_INT_CODE )
= = MWL8K_FWSTA_READY )
break ;
udelay ( 1 ) ;
} while ( - - loops ) ;
return loops ? 0 : - ETIMEDOUT ;
}
/*
* Defines shared between transmission and reception .
*/
/* HT control fields for firmware */
struct ewc_ht_info {
__le16 control1 ;
__le16 control2 ;
__le16 control3 ;
} __attribute__ ( ( packed ) ) ;
/* Firmware Station database operations */
# define MWL8K_STA_DB_ADD_ENTRY 0
# define MWL8K_STA_DB_MODIFY_ENTRY 1
# define MWL8K_STA_DB_DEL_ENTRY 2
# define MWL8K_STA_DB_FLUSH 3
/* Peer Entry flags - used to define the type of the peer node */
# define MWL8K_PEER_TYPE_ACCESSPOINT 2
# define MWL8K_IEEE_LEGACY_DATA_RATES 12
# define MWL8K_MCS_BITMAP_SIZE 16
struct peer_capability_info {
/* Peer type - AP vs. STA. */
__u8 peer_type ;
/* Basic 802.11 capabilities from assoc resp. */
__le16 basic_caps ;
/* Set if peer supports 802.11n high throughput (HT). */
__u8 ht_support ;
/* Valid if HT is supported. */
__le16 ht_caps ;
__u8 extended_ht_caps ;
struct ewc_ht_info ewc_info ;
/* Legacy rate table. Intersection of our rates and peer rates. */
__u8 legacy_rates [ MWL8K_IEEE_LEGACY_DATA_RATES ] ;
/* HT rate table. Intersection of our rates and peer rates. */
__u8 ht_rates [ MWL8K_MCS_BITMAP_SIZE ] ;
2009-07-16 13:49:55 +02:00
__u8 pad [ 16 ] ;
2009-03-10 10:13:33 +01:00
/* If set, interoperability mode, no proprietary extensions. */
__u8 interop ;
__u8 pad2 ;
__u8 station_id ;
__le16 amsdu_enabled ;
} __attribute__ ( ( packed ) ) ;
/* Inline functions to manipulate QoS field in data descriptor. */
static inline u16 mwl8k_qos_setbit_eosp ( u16 qos )
{
u16 val_mask = 1 < < 4 ;
/* End of Service Period Bit 4 */
return qos | val_mask ;
}
static inline u16 mwl8k_qos_setbit_ack ( u16 qos , u8 ack_policy )
{
u16 val_mask = 0x3 ;
u8 shift = 5 ;
u16 qos_mask = ~ ( val_mask < < shift ) ;
/* Ack Policy Bit 5-6 */
return ( qos & qos_mask ) | ( ( ack_policy & val_mask ) < < shift ) ;
}
static inline u16 mwl8k_qos_setbit_amsdu ( u16 qos )
{
u16 val_mask = 1 < < 7 ;
/* AMSDU present Bit 7 */
return qos | val_mask ;
}
static inline u16 mwl8k_qos_setbit_qlen ( u16 qos , u8 len )
{
u16 val_mask = 0xff ;
u8 shift = 8 ;
u16 qos_mask = ~ ( val_mask < < shift ) ;
/* Queue Length Bits 8-15 */
return ( qos & qos_mask ) | ( ( len & val_mask ) < < shift ) ;
}
/* DMA header used by firmware and hardware. */
struct mwl8k_dma_data {
__le16 fwlen ;
struct ieee80211_hdr wh ;
} __attribute__ ( ( packed ) ) ;
/* Routines to add/remove DMA header from skb. */
2009-07-16 11:07:09 +02:00
static inline void mwl8k_remove_dma_header ( struct sk_buff * skb )
2009-03-10 10:13:33 +01:00
{
2009-07-16 11:07:09 +02:00
struct mwl8k_dma_data * tr = ( struct mwl8k_dma_data * ) skb - > data ;
2009-03-10 10:13:33 +01:00
void * dst , * src = & tr - > wh ;
2009-07-16 11:07:09 +02:00
int hdrlen = ieee80211_hdrlen ( tr - > wh . frame_control ) ;
2009-03-10 10:13:33 +01:00
u16 space = sizeof ( struct mwl8k_dma_data ) - hdrlen ;
dst = ( void * ) tr + space ;
if ( dst ! = src ) {
memmove ( dst , src , hdrlen ) ;
skb_pull ( skb , space ) ;
}
}
2009-07-16 11:07:09 +02:00
static inline void mwl8k_add_dma_header ( struct sk_buff * skb )
2009-03-10 10:13:33 +01:00
{
struct ieee80211_hdr * wh ;
u32 hdrlen , pktlen ;
struct mwl8k_dma_data * tr ;
wh = ( struct ieee80211_hdr * ) skb - > data ;
hdrlen = ieee80211_hdrlen ( wh - > frame_control ) ;
pktlen = skb - > len ;
/*
* Copy up / down the 802.11 header ; the firmware requires
* we present a 2 - byte payload length followed by a
* 4 - address header ( w / o QoS ) , followed ( optionally ) by
* any WEP / ExtIV header ( but only filled in for CCMP ) .
*/
if ( hdrlen ! = sizeof ( struct mwl8k_dma_data ) )
skb_push ( skb , sizeof ( struct mwl8k_dma_data ) - hdrlen ) ;
tr = ( struct mwl8k_dma_data * ) skb - > data ;
if ( wh ! = & tr - > wh )
memmove ( & tr - > wh , wh , hdrlen ) ;
/* Clear addr4 */
2009-07-16 09:54:27 +02:00
memset ( tr - > wh . addr4 , 0 , ETH_ALEN ) ;
2009-03-10 10:13:33 +01:00
/*
* Firmware length is the length of the fully formed " 802.11
* payload " . That is, everything except for the 802.11 header.
* This includes all crypto material including the MIC .
*/
tr - > fwlen = cpu_to_le16 ( pktlen - hdrlen ) ;
}
/*
* Packet reception .
*/
# define MWL8K_RX_CTRL_OWNED_BY_HOST 0x02
struct mwl8k_rx_desc {
__le16 pkt_len ;
__u8 link_quality ;
__u8 noise_level ;
__le32 pkt_phys_addr ;
__le32 next_rx_desc_phys_addr ;
__le16 qos_control ;
__le16 rate_info ;
__le32 pad0 [ 4 ] ;
__u8 rssi ;
__u8 channel ;
__le16 pad1 ;
__u8 rx_ctrl ;
__u8 rx_status ;
__u8 pad2 [ 2 ] ;
} __attribute__ ( ( packed ) ) ;
# define MWL8K_RX_DESCS 256
# define MWL8K_RX_MAXSZ 3800
static int mwl8k_rxq_init ( struct ieee80211_hw * hw , int index )
{
struct mwl8k_priv * priv = hw - > priv ;
struct mwl8k_rx_queue * rxq = priv - > rxq + index ;
int size ;
int i ;
rxq - > rx_desc_count = 0 ;
rxq - > rx_head = 0 ;
rxq - > rx_tail = 0 ;
size = MWL8K_RX_DESCS * sizeof ( struct mwl8k_rx_desc ) ;
rxq - > rx_desc_area =
pci_alloc_consistent ( priv - > pdev , size , & rxq - > rx_desc_dma ) ;
if ( rxq - > rx_desc_area = = NULL ) {
printk ( KERN_ERR " %s: failed to alloc RX descriptors \n " ,
priv - > name ) ;
return - ENOMEM ;
}
memset ( rxq - > rx_desc_area , 0 , size ) ;
rxq - > rx_skb = kmalloc ( MWL8K_RX_DESCS *
sizeof ( * rxq - > rx_skb ) , GFP_KERNEL ) ;
if ( rxq - > rx_skb = = NULL ) {
printk ( KERN_ERR " %s: failed to alloc RX skbuff list \n " ,
priv - > name ) ;
pci_free_consistent ( priv - > pdev , size ,
rxq - > rx_desc_area , rxq - > rx_desc_dma ) ;
return - ENOMEM ;
}
memset ( rxq - > rx_skb , 0 , MWL8K_RX_DESCS * sizeof ( * rxq - > rx_skb ) ) ;
for ( i = 0 ; i < MWL8K_RX_DESCS ; i + + ) {
struct mwl8k_rx_desc * rx_desc ;
int nexti ;
rx_desc = rxq - > rx_desc_area + i ;
nexti = ( i + 1 ) % MWL8K_RX_DESCS ;
rx_desc - > next_rx_desc_phys_addr =
cpu_to_le32 ( rxq - > rx_desc_dma
+ nexti * sizeof ( * rx_desc ) ) ;
2009-04-21 16:22:01 +03:00
rx_desc - > rx_ctrl = MWL8K_RX_CTRL_OWNED_BY_HOST ;
2009-03-10 10:13:33 +01:00
}
return 0 ;
}
static int rxq_refill ( struct ieee80211_hw * hw , int index , int limit )
{
struct mwl8k_priv * priv = hw - > priv ;
struct mwl8k_rx_queue * rxq = priv - > rxq + index ;
int refilled ;
refilled = 0 ;
while ( rxq - > rx_desc_count < MWL8K_RX_DESCS & & limit - - ) {
struct sk_buff * skb ;
int rx ;
skb = dev_alloc_skb ( MWL8K_RX_MAXSZ ) ;
if ( skb = = NULL )
break ;
rxq - > rx_desc_count + + ;
rx = rxq - > rx_tail ;
rxq - > rx_tail = ( rx + 1 ) % MWL8K_RX_DESCS ;
rxq - > rx_desc_area [ rx ] . pkt_phys_addr =
cpu_to_le32 ( pci_map_single ( priv - > pdev , skb - > data ,
MWL8K_RX_MAXSZ , DMA_FROM_DEVICE ) ) ;
rxq - > rx_desc_area [ rx ] . pkt_len = cpu_to_le16 ( MWL8K_RX_MAXSZ ) ;
rxq - > rx_skb [ rx ] = skb ;
wmb ( ) ;
rxq - > rx_desc_area [ rx ] . rx_ctrl = 0 ;
refilled + + ;
}
return refilled ;
}
/* Must be called only when the card's reception is completely halted */
static void mwl8k_rxq_deinit ( struct ieee80211_hw * hw , int index )
{
struct mwl8k_priv * priv = hw - > priv ;
struct mwl8k_rx_queue * rxq = priv - > rxq + index ;
int i ;
for ( i = 0 ; i < MWL8K_RX_DESCS ; i + + ) {
if ( rxq - > rx_skb [ i ] ! = NULL ) {
unsigned long addr ;
addr = le32_to_cpu ( rxq - > rx_desc_area [ i ] . pkt_phys_addr ) ;
pci_unmap_single ( priv - > pdev , addr , MWL8K_RX_MAXSZ ,
PCI_DMA_FROMDEVICE ) ;
kfree_skb ( rxq - > rx_skb [ i ] ) ;
rxq - > rx_skb [ i ] = NULL ;
}
}
kfree ( rxq - > rx_skb ) ;
rxq - > rx_skb = NULL ;
pci_free_consistent ( priv - > pdev ,
MWL8K_RX_DESCS * sizeof ( struct mwl8k_rx_desc ) ,
rxq - > rx_desc_area , rxq - > rx_desc_dma ) ;
rxq - > rx_desc_area = NULL ;
}
/*
* Scan a list of BSSIDs to process for finalize join .
* Allows for extension to process multiple BSSIDs .
*/
static inline int
mwl8k_capture_bssid ( struct mwl8k_priv * priv , struct ieee80211_hdr * wh )
{
return priv - > capture_beacon & &
ieee80211_is_beacon ( wh - > frame_control ) & &
! compare_ether_addr ( wh - > addr3 , priv - > capture_bssid ) ;
}
static inline void mwl8k_save_beacon ( struct mwl8k_priv * priv ,
struct sk_buff * skb )
{
priv - > capture_beacon = false ;
2009-07-16 09:54:27 +02:00
memset ( priv - > capture_bssid , 0 , ETH_ALEN ) ;
2009-03-10 10:13:33 +01:00
/*
* Use GFP_ATOMIC as rxq_process is called from
* the primary interrupt handler , memory allocation call
* must not sleep .
*/
priv - > beacon_skb = skb_copy ( skb , GFP_ATOMIC ) ;
if ( priv - > beacon_skb ! = NULL )
queue_work ( priv - > config_wq ,
& priv - > finalize_join_worker ) ;
}
static int rxq_process ( struct ieee80211_hw * hw , int index , int limit )
{
struct mwl8k_priv * priv = hw - > priv ;
struct mwl8k_rx_queue * rxq = priv - > rxq + index ;
int processed ;
processed = 0 ;
while ( rxq - > rx_desc_count & & limit - - ) {
struct mwl8k_rx_desc * rx_desc ;
struct sk_buff * skb ;
struct ieee80211_rx_status status ;
unsigned long addr ;
struct ieee80211_hdr * wh ;
rx_desc = rxq - > rx_desc_area + rxq - > rx_head ;
if ( ! ( rx_desc - > rx_ctrl & MWL8K_RX_CTRL_OWNED_BY_HOST ) )
break ;
rmb ( ) ;
skb = rxq - > rx_skb [ rxq - > rx_head ] ;
2009-08-03 21:58:26 +02:00
if ( skb = = NULL )
break ;
2009-03-10 10:13:33 +01:00
rxq - > rx_skb [ rxq - > rx_head ] = NULL ;
rxq - > rx_head = ( rxq - > rx_head + 1 ) % MWL8K_RX_DESCS ;
rxq - > rx_desc_count - - ;
addr = le32_to_cpu ( rx_desc - > pkt_phys_addr ) ;
pci_unmap_single ( priv - > pdev , addr ,
MWL8K_RX_MAXSZ , PCI_DMA_FROMDEVICE ) ;
skb_put ( skb , le16_to_cpu ( rx_desc - > pkt_len ) ) ;
2009-07-16 11:07:09 +02:00
mwl8k_remove_dma_header ( skb ) ;
2009-03-10 10:13:33 +01:00
wh = ( struct ieee80211_hdr * ) skb - > data ;
/*
* Check for pending join operation . save a copy of
* the beacon and schedule a tasklet to send finalize
* join command to the firmware .
*/
if ( mwl8k_capture_bssid ( priv , wh ) )
mwl8k_save_beacon ( priv , skb ) ;
memset ( & status , 0 , sizeof ( status ) ) ;
status . mactime = 0 ;
status . signal = - rx_desc - > rssi ;
status . noise = - rx_desc - > noise_level ;
status . qual = rx_desc - > link_quality ;
status . antenna = 1 ;
status . rate_idx = 1 ;
status . flag = 0 ;
status . band = IEEE80211_BAND_2GHZ ;
status . freq = ieee80211_channel_to_frequency ( rx_desc - > channel ) ;
2009-06-17 13:13:00 +02:00
memcpy ( IEEE80211_SKB_RXCB ( skb ) , & status , sizeof ( status ) ) ;
ieee80211_rx_irqsafe ( hw , skb ) ;
2009-03-10 10:13:33 +01:00
processed + + ;
}
return processed ;
}
/*
* Packet transmission .
*/
/* Transmit queue assignment. */
enum {
MWL8K_WME_AC_BK = 0 , /* background access */
MWL8K_WME_AC_BE = 1 , /* best effort access */
MWL8K_WME_AC_VI = 2 , /* video access */
MWL8K_WME_AC_VO = 3 , /* voice access */
} ;
/* Transmit packet ACK policy */
# define MWL8K_TXD_ACK_POLICY_NORMAL 0
# define MWL8K_TXD_ACK_POLICY_BLOCKACK 3
# define GET_TXQ(_ac) (\
( ( _ac ) = = WME_AC_VO ) ? MWL8K_WME_AC_VO : \
( ( _ac ) = = WME_AC_VI ) ? MWL8K_WME_AC_VI : \
( ( _ac ) = = WME_AC_BK ) ? MWL8K_WME_AC_BK : \
MWL8K_WME_AC_BE )
# define MWL8K_TXD_STATUS_OK 0x00000001
# define MWL8K_TXD_STATUS_OK_RETRY 0x00000002
# define MWL8K_TXD_STATUS_OK_MORE_RETRY 0x00000004
# define MWL8K_TXD_STATUS_MULTICAST_TX 0x00000008
# define MWL8K_TXD_STATUS_FW_OWNED 0x80000000
struct mwl8k_tx_desc {
__le32 status ;
__u8 data_rate ;
__u8 tx_priority ;
__le16 qos_control ;
__le32 pkt_phys_addr ;
__le16 pkt_len ;
2009-07-16 09:54:27 +02:00
__u8 dest_MAC_addr [ ETH_ALEN ] ;
2009-03-10 10:13:33 +01:00
__le32 next_tx_desc_phys_addr ;
__le32 reserved ;
__le16 rate_info ;
__u8 peer_id ;
__u8 tx_frag_cnt ;
} __attribute__ ( ( packed ) ) ;
# define MWL8K_TX_DESCS 128
static int mwl8k_txq_init ( struct ieee80211_hw * hw , int index )
{
struct mwl8k_priv * priv = hw - > priv ;
struct mwl8k_tx_queue * txq = priv - > txq + index ;
int size ;
int i ;
2009-07-16 14:00:45 +02:00
memset ( & txq - > tx_stats , 0 , sizeof ( struct ieee80211_tx_queue_stats ) ) ;
2009-03-10 10:13:33 +01:00
txq - > tx_stats . limit = MWL8K_TX_DESCS ;
txq - > tx_head = 0 ;
txq - > tx_tail = 0 ;
size = MWL8K_TX_DESCS * sizeof ( struct mwl8k_tx_desc ) ;
txq - > tx_desc_area =
pci_alloc_consistent ( priv - > pdev , size , & txq - > tx_desc_dma ) ;
if ( txq - > tx_desc_area = = NULL ) {
printk ( KERN_ERR " %s: failed to alloc TX descriptors \n " ,
priv - > name ) ;
return - ENOMEM ;
}
memset ( txq - > tx_desc_area , 0 , size ) ;
txq - > tx_skb = kmalloc ( MWL8K_TX_DESCS * sizeof ( * txq - > tx_skb ) ,
GFP_KERNEL ) ;
if ( txq - > tx_skb = = NULL ) {
printk ( KERN_ERR " %s: failed to alloc TX skbuff list \n " ,
priv - > name ) ;
pci_free_consistent ( priv - > pdev , size ,
txq - > tx_desc_area , txq - > tx_desc_dma ) ;
return - ENOMEM ;
}
memset ( txq - > tx_skb , 0 , MWL8K_TX_DESCS * sizeof ( * txq - > tx_skb ) ) ;
for ( i = 0 ; i < MWL8K_TX_DESCS ; i + + ) {
struct mwl8k_tx_desc * tx_desc ;
int nexti ;
tx_desc = txq - > tx_desc_area + i ;
nexti = ( i + 1 ) % MWL8K_TX_DESCS ;
tx_desc - > status = 0 ;
tx_desc - > next_tx_desc_phys_addr =
cpu_to_le32 ( txq - > tx_desc_dma +
nexti * sizeof ( * tx_desc ) ) ;
}
return 0 ;
}
static inline void mwl8k_tx_start ( struct mwl8k_priv * priv )
{
iowrite32 ( MWL8K_H2A_INT_PPA_READY ,
priv - > regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS ) ;
iowrite32 ( MWL8K_H2A_INT_DUMMY ,
priv - > regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS ) ;
ioread32 ( priv - > regs + MWL8K_HIU_INT_CODE ) ;
}
static inline int mwl8k_txq_busy ( struct mwl8k_priv * priv )
{
return priv - > pending_tx_pkts ;
}
struct mwl8k_txq_info {
u32 fw_owned ;
u32 drv_owned ;
u32 unused ;
u32 len ;
u32 head ;
u32 tail ;
} ;
static int mwl8k_scan_tx_ring ( struct mwl8k_priv * priv ,
2009-08-18 04:15:22 +02:00
struct mwl8k_txq_info * txinfo )
2009-03-10 10:13:33 +01:00
{
int count , desc , status ;
struct mwl8k_tx_queue * txq ;
struct mwl8k_tx_desc * tx_desc ;
int ndescs = 0 ;
2009-08-18 04:15:22 +02:00
memset ( txinfo , 0 , MWL8K_TX_QUEUES * sizeof ( struct mwl8k_txq_info ) ) ;
2009-03-10 10:13:33 +01:00
spin_lock_bh ( & priv - > tx_lock ) ;
2009-08-18 04:15:22 +02:00
for ( count = 0 ; count < MWL8K_TX_QUEUES ; count + + ) {
2009-03-10 10:13:33 +01:00
txq = priv - > txq + count ;
txinfo [ count ] . len = txq - > tx_stats . len ;
txinfo [ count ] . head = txq - > tx_head ;
txinfo [ count ] . tail = txq - > tx_tail ;
for ( desc = 0 ; desc < MWL8K_TX_DESCS ; desc + + ) {
tx_desc = txq - > tx_desc_area + desc ;
status = le32_to_cpu ( tx_desc - > status ) ;
if ( status & MWL8K_TXD_STATUS_FW_OWNED )
txinfo [ count ] . fw_owned + + ;
else
txinfo [ count ] . drv_owned + + ;
if ( tx_desc - > pkt_len = = 0 )
txinfo [ count ] . unused + + ;
}
}
spin_unlock_bh ( & priv - > tx_lock ) ;
return ndescs ;
}
2009-08-18 03:18:01 +02:00
/*
* Must be called with hw - > fw_mutex held and tx queues stopped .
*/
2009-07-17 01:48:41 +02:00
static int mwl8k_tx_wait_empty ( struct ieee80211_hw * hw )
2009-03-10 10:13:33 +01:00
{
struct mwl8k_priv * priv = hw - > priv ;
DECLARE_COMPLETION_ONSTACK ( cmd_wait ) ;
2009-07-16 14:00:45 +02:00
u32 count ;
unsigned long timeout ;
2009-03-10 10:13:33 +01:00
might_sleep ( ) ;
spin_lock_bh ( & priv - > tx_lock ) ;
count = mwl8k_txq_busy ( priv ) ;
if ( count ) {
priv - > tx_wait = & cmd_wait ;
2009-07-16 12:14:58 +02:00
if ( priv - > radio_on )
2009-03-10 10:13:33 +01:00
mwl8k_tx_start ( priv ) ;
}
spin_unlock_bh ( & priv - > tx_lock ) ;
if ( count ) {
2009-08-18 04:15:22 +02:00
struct mwl8k_txq_info txinfo [ MWL8K_TX_QUEUES ] ;
2009-03-10 10:13:33 +01:00
int index ;
int newcount ;
timeout = wait_for_completion_timeout ( & cmd_wait ,
2009-08-18 03:18:01 +02:00
msecs_to_jiffies ( 5000 ) ) ;
2009-03-10 10:13:33 +01:00
if ( timeout )
return 0 ;
spin_lock_bh ( & priv - > tx_lock ) ;
priv - > tx_wait = NULL ;
newcount = mwl8k_txq_busy ( priv ) ;
spin_unlock_bh ( & priv - > tx_lock ) ;
2009-08-18 03:18:01 +02:00
printk ( KERN_ERR " %s(%u) TIMEDOUT:5000ms Pend:%u-->%u \n " ,
2009-07-17 01:48:41 +02:00
__func__ , __LINE__ , count , newcount ) ;
2009-03-10 10:13:33 +01:00
2009-08-18 04:15:22 +02:00
mwl8k_scan_tx_ring ( priv , txinfo ) ;
for ( index = 0 ; index < MWL8K_TX_QUEUES ; index + + )
2009-03-10 10:13:33 +01:00
printk ( KERN_ERR
" TXQ:%u L:%u H:%u T:%u FW:%u DRV:%u U:%u \n " ,
index ,
txinfo [ index ] . len ,
txinfo [ index ] . head ,
txinfo [ index ] . tail ,
txinfo [ index ] . fw_owned ,
txinfo [ index ] . drv_owned ,
txinfo [ index ] . unused ) ;
2009-07-16 14:00:45 +02:00
2009-03-10 10:13:33 +01:00
return - ETIMEDOUT ;
}
return 0 ;
}
2009-07-16 13:49:55 +02:00
# define MWL8K_TXD_SUCCESS(status) \
( ( status ) & ( MWL8K_TXD_STATUS_OK | \
MWL8K_TXD_STATUS_OK_RETRY | \
MWL8K_TXD_STATUS_OK_MORE_RETRY ) )
2009-03-10 10:13:33 +01:00
static void mwl8k_txq_reclaim ( struct ieee80211_hw * hw , int index , int force )
{
struct mwl8k_priv * priv = hw - > priv ;
struct mwl8k_tx_queue * txq = priv - > txq + index ;
int wake = 0 ;
while ( txq - > tx_stats . len > 0 ) {
int tx ;
struct mwl8k_tx_desc * tx_desc ;
unsigned long addr ;
2009-07-16 14:00:45 +02:00
int size ;
2009-03-10 10:13:33 +01:00
struct sk_buff * skb ;
struct ieee80211_tx_info * info ;
u32 status ;
tx = txq - > tx_head ;
tx_desc = txq - > tx_desc_area + tx ;
status = le32_to_cpu ( tx_desc - > status ) ;
if ( status & MWL8K_TXD_STATUS_FW_OWNED ) {
if ( ! force )
break ;
tx_desc - > status & =
~ cpu_to_le32 ( MWL8K_TXD_STATUS_FW_OWNED ) ;
}
txq - > tx_head = ( tx + 1 ) % MWL8K_TX_DESCS ;
BUG_ON ( txq - > tx_stats . len = = 0 ) ;
txq - > tx_stats . len - - ;
priv - > pending_tx_pkts - - ;
addr = le32_to_cpu ( tx_desc - > pkt_phys_addr ) ;
2009-07-16 14:00:45 +02:00
size = le16_to_cpu ( tx_desc - > pkt_len ) ;
2009-07-16 11:07:09 +02:00
skb = txq - > tx_skb [ tx ] ;
txq - > tx_skb [ tx ] = NULL ;
2009-03-10 10:13:33 +01:00
BUG_ON ( skb = = NULL ) ;
pci_unmap_single ( priv - > pdev , addr , size , PCI_DMA_TODEVICE ) ;
2009-07-16 11:07:09 +02:00
mwl8k_remove_dma_header ( skb ) ;
2009-03-10 10:13:33 +01:00
/* Mark descriptor as unused */
tx_desc - > pkt_phys_addr = 0 ;
tx_desc - > pkt_len = 0 ;
info = IEEE80211_SKB_CB ( skb ) ;
ieee80211_tx_info_clear_status ( info ) ;
2009-07-16 14:00:45 +02:00
if ( MWL8K_TXD_SUCCESS ( status ) )
2009-03-10 10:13:33 +01:00
info - > flags | = IEEE80211_TX_STAT_ACK ;
ieee80211_tx_status_irqsafe ( hw , skb ) ;
2009-08-18 03:18:01 +02:00
wake = 1 ;
2009-03-10 10:13:33 +01:00
}
2009-08-18 03:18:01 +02:00
if ( wake & & priv - > radio_on & & ! mutex_is_locked ( & priv - > fw_mutex ) )
2009-03-10 10:13:33 +01:00
ieee80211_wake_queue ( hw , index ) ;
}
/* must be called only when the card's transmit is completely halted */
static void mwl8k_txq_deinit ( struct ieee80211_hw * hw , int index )
{
struct mwl8k_priv * priv = hw - > priv ;
struct mwl8k_tx_queue * txq = priv - > txq + index ;
mwl8k_txq_reclaim ( hw , index , 1 ) ;
kfree ( txq - > tx_skb ) ;
txq - > tx_skb = NULL ;
pci_free_consistent ( priv - > pdev ,
MWL8K_TX_DESCS * sizeof ( struct mwl8k_tx_desc ) ,
txq - > tx_desc_area , txq - > tx_desc_dma ) ;
txq - > tx_desc_area = NULL ;
}
static int
mwl8k_txq_xmit ( struct ieee80211_hw * hw , int index , struct sk_buff * skb )
{
struct mwl8k_priv * priv = hw - > priv ;
struct ieee80211_tx_info * tx_info ;
2009-07-17 05:21:04 +02:00
struct mwl8k_vif * mwl8k_vif ;
2009-03-10 10:13:33 +01:00
struct ieee80211_hdr * wh ;
struct mwl8k_tx_queue * txq ;
struct mwl8k_tx_desc * tx ;
dma_addr_t dma ;
2009-07-17 05:21:04 +02:00
u32 txstatus ;
u8 txdatarate ;
u16 qos ;
2009-03-10 10:13:33 +01:00
2009-07-17 05:21:04 +02:00
wh = ( struct ieee80211_hdr * ) skb - > data ;
if ( ieee80211_is_data_qos ( wh - > frame_control ) )
qos = le16_to_cpu ( * ( ( __le16 * ) ieee80211_get_qos_ctl ( wh ) ) ) ;
else
qos = 0 ;
2009-03-10 10:13:33 +01:00
2009-07-16 11:07:09 +02:00
mwl8k_add_dma_header ( skb ) ;
2009-07-17 05:21:04 +02:00
wh = & ( ( struct mwl8k_dma_data * ) skb - > data ) - > wh ;
2009-03-10 10:13:33 +01:00
tx_info = IEEE80211_SKB_CB ( skb ) ;
mwl8k_vif = MWL8K_VIF ( tx_info - > control . vif ) ;
if ( tx_info - > flags & IEEE80211_TX_CTL_ASSIGN_SEQ ) {
u16 seqno = mwl8k_vif - > seqno ;
2009-07-17 05:21:04 +02:00
2009-03-10 10:13:33 +01:00
wh - > seq_ctrl & = cpu_to_le16 ( IEEE80211_SCTL_FRAG ) ;
wh - > seq_ctrl | = cpu_to_le16 ( seqno < < 4 ) ;
mwl8k_vif - > seqno = seqno + + % 4096 ;
}
2009-07-17 05:21:04 +02:00
/* Setup firmware control bit fields for each frame type. */
txstatus = 0 ;
txdatarate = 0 ;
if ( ieee80211_is_mgmt ( wh - > frame_control ) | |
ieee80211_is_ctl ( wh - > frame_control ) ) {
txdatarate = 0 ;
qos = mwl8k_qos_setbit_eosp ( qos ) ;
/* Set Queue size to unspecified */
qos = mwl8k_qos_setbit_qlen ( qos , 0xff ) ;
} else if ( ieee80211_is_data ( wh - > frame_control ) ) {
txdatarate = 1 ;
if ( is_multicast_ether_addr ( wh - > addr1 ) )
txstatus | = MWL8K_TXD_STATUS_MULTICAST_TX ;
/* Send pkt in an aggregate if AMPDU frame. */
if ( tx_info - > flags & IEEE80211_TX_CTL_AMPDU )
qos = mwl8k_qos_setbit_ack ( qos ,
MWL8K_TXD_ACK_POLICY_BLOCKACK ) ;
else
qos = mwl8k_qos_setbit_ack ( qos ,
MWL8K_TXD_ACK_POLICY_NORMAL ) ;
if ( qos & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT )
qos = mwl8k_qos_setbit_amsdu ( qos ) ;
}
2009-03-10 10:13:33 +01:00
dma = pci_map_single ( priv - > pdev , skb - > data ,
skb - > len , PCI_DMA_TODEVICE ) ;
if ( pci_dma_mapping_error ( priv - > pdev , dma ) ) {
printk ( KERN_DEBUG " %s: failed to dma map skb, "
" dropping TX frame. \n " , priv - > name ) ;
2009-07-17 05:21:04 +02:00
dev_kfree_skb ( skb ) ;
2009-03-10 10:13:33 +01:00
return NETDEV_TX_OK ;
}
2009-07-17 05:21:04 +02:00
spin_lock_bh ( & priv - > tx_lock ) ;
2009-03-10 10:13:33 +01:00
2009-07-17 05:21:04 +02:00
txq = priv - > txq + index ;
2009-03-10 10:13:33 +01:00
2009-07-17 05:21:04 +02:00
BUG_ON ( txq - > tx_skb [ txq - > tx_tail ] ! = NULL ) ;
txq - > tx_skb [ txq - > tx_tail ] = skb ;
2009-03-10 10:13:33 +01:00
2009-07-17 05:21:04 +02:00
tx = txq - > tx_desc_area + txq - > tx_tail ;
tx - > data_rate = txdatarate ;
tx - > tx_priority = index ;
2009-03-10 10:13:33 +01:00
tx - > qos_control = cpu_to_le16 ( qos ) ;
tx - > pkt_phys_addr = cpu_to_le32 ( dma ) ;
tx - > pkt_len = cpu_to_le16 ( skb - > len ) ;
2009-07-17 05:21:04 +02:00
tx - > rate_info = 0 ;
tx - > peer_id = mwl8k_vif - > peer_id ;
2009-03-10 10:13:33 +01:00
wmb ( ) ;
2009-07-17 05:21:04 +02:00
tx - > status = cpu_to_le32 ( MWL8K_TXD_STATUS_FW_OWNED | txstatus ) ;
txq - > tx_stats . count + + ;
2009-03-10 10:13:33 +01:00
txq - > tx_stats . len + + ;
priv - > pending_tx_pkts + + ;
2009-07-17 05:21:04 +02:00
txq - > tx_tail + + ;
2009-03-10 10:13:33 +01:00
if ( txq - > tx_tail = = MWL8K_TX_DESCS )
txq - > tx_tail = 0 ;
2009-07-17 05:21:04 +02:00
2009-03-10 10:13:33 +01:00
if ( txq - > tx_head = = txq - > tx_tail )
ieee80211_stop_queue ( hw , index ) ;
2009-07-17 05:21:04 +02:00
mwl8k_tx_start ( priv ) ;
2009-03-10 10:13:33 +01:00
spin_unlock_bh ( & priv - > tx_lock ) ;
return NETDEV_TX_OK ;
}
2009-08-18 03:18:01 +02:00
/*
* Firmware access .
*
* We have the following requirements for issuing firmware commands :
* - Some commands require that the packet transmit path is idle when
* the command is issued . ( For simplicity , we ' ll just quiesce the
* transmit path for every command . )
* - There are certain sequences of commands that need to be issued to
* the hardware sequentially , with no other intervening commands .
*
* This leads to an implementation of a " firmware lock " as a mutex that
* can be taken recursively , and which is taken by both the low - level
* command submission function ( mwl8k_post_cmd ) as well as any users of
* that function that require issuing of an atomic sequence of commands ,
* and quiesces the transmit path whenever it ' s taken .
*/
static int mwl8k_fw_lock ( struct ieee80211_hw * hw )
{
struct mwl8k_priv * priv = hw - > priv ;
if ( priv - > fw_mutex_owner ! = current ) {
int rc ;
mutex_lock ( & priv - > fw_mutex ) ;
ieee80211_stop_queues ( hw ) ;
rc = mwl8k_tx_wait_empty ( hw ) ;
if ( rc ) {
ieee80211_wake_queues ( hw ) ;
mutex_unlock ( & priv - > fw_mutex ) ;
return rc ;
}
priv - > fw_mutex_owner = current ;
}
priv - > fw_mutex_depth + + ;
return 0 ;
}
static void mwl8k_fw_unlock ( struct ieee80211_hw * hw )
{
struct mwl8k_priv * priv = hw - > priv ;
if ( ! - - priv - > fw_mutex_depth ) {
ieee80211_wake_queues ( hw ) ;
priv - > fw_mutex_owner = NULL ;
mutex_unlock ( & priv - > fw_mutex ) ;
}
}
2009-03-10 10:13:33 +01:00
/*
* Command processing .
*/
/* Timeout firmware commands after 2000ms */
# define MWL8K_CMD_TIMEOUT_MS 2000
static int mwl8k_post_cmd ( struct ieee80211_hw * hw , struct mwl8k_cmd_pkt * cmd )
{
DECLARE_COMPLETION_ONSTACK ( cmd_wait ) ;
struct mwl8k_priv * priv = hw - > priv ;
void __iomem * regs = priv - > regs ;
dma_addr_t dma_addr ;
unsigned int dma_size ;
int rc ;
unsigned long timeout = 0 ;
u8 buf [ 32 ] ;
cmd - > result = 0xFFFF ;
dma_size = le16_to_cpu ( cmd - > length ) ;
dma_addr = pci_map_single ( priv - > pdev , cmd , dma_size ,
PCI_DMA_BIDIRECTIONAL ) ;
if ( pci_dma_mapping_error ( priv - > pdev , dma_addr ) )
return - ENOMEM ;
2009-08-18 03:18:01 +02:00
rc = mwl8k_fw_lock ( hw ) ;
2009-08-24 15:42:46 +02:00
if ( rc ) {
pci_unmap_single ( priv - > pdev , dma_addr , dma_size ,
PCI_DMA_BIDIRECTIONAL ) ;
2009-08-18 03:18:01 +02:00
return rc ;
2009-08-24 15:42:46 +02:00
}
2009-03-10 10:13:33 +01:00
priv - > hostcmd_wait = & cmd_wait ;
iowrite32 ( dma_addr , regs + MWL8K_HIU_GEN_PTR ) ;
iowrite32 ( MWL8K_H2A_INT_DOORBELL ,
regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS ) ;
iowrite32 ( MWL8K_H2A_INT_DUMMY ,
regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS ) ;
timeout = wait_for_completion_timeout ( & cmd_wait ,
msecs_to_jiffies ( MWL8K_CMD_TIMEOUT_MS ) ) ;
2009-08-18 03:18:01 +02:00
priv - > hostcmd_wait = NULL ;
mwl8k_fw_unlock ( hw ) ;
2009-08-03 21:58:47 +02:00
pci_unmap_single ( priv - > pdev , dma_addr , dma_size ,
PCI_DMA_BIDIRECTIONAL ) ;
2009-03-10 10:13:33 +01:00
if ( ! timeout ) {
printk ( KERN_ERR " %s: Command %s timeout after %u ms \n " ,
priv - > name ,
mwl8k_cmd_name ( cmd - > code , buf , sizeof ( buf ) ) ,
MWL8K_CMD_TIMEOUT_MS ) ;
rc = - ETIMEDOUT ;
} else {
2009-07-16 14:00:45 +02:00
rc = cmd - > result ? - EINVAL : 0 ;
2009-03-10 10:13:33 +01:00
if ( rc )
printk ( KERN_ERR " %s: Command %s error 0x%x \n " ,
priv - > name ,
mwl8k_cmd_name ( cmd - > code , buf , sizeof ( buf ) ) ,
2009-08-24 15:42:56 +02:00
le16_to_cpu ( cmd - > result ) ) ;
2009-03-10 10:13:33 +01:00
}
return rc ;
}
/*
* GET_HW_SPEC .
*/
struct mwl8k_cmd_get_hw_spec {
struct mwl8k_cmd_pkt header ;
__u8 hw_rev ;
__u8 host_interface ;
__le16 num_mcaddrs ;
2009-07-16 09:54:27 +02:00
__u8 perm_addr [ ETH_ALEN ] ;
2009-03-10 10:13:33 +01:00
__le16 region_code ;
__le32 fw_rev ;
__le32 ps_cookie ;
__le32 caps ;
__u8 mcs_bitmap [ 16 ] ;
__le32 rx_queue_ptr ;
__le32 num_tx_queues ;
__le32 tx_queue_ptrs [ MWL8K_TX_QUEUES ] ;
__le32 caps2 ;
__le32 num_tx_desc_per_queue ;
__le32 total_rx_desc ;
} __attribute__ ( ( packed ) ) ;
static int mwl8k_cmd_get_hw_spec ( struct ieee80211_hw * hw )
{
struct mwl8k_priv * priv = hw - > priv ;
struct mwl8k_cmd_get_hw_spec * cmd ;
int rc ;
int i ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( cmd = = NULL )
return - ENOMEM ;
cmd - > header . code = cpu_to_le16 ( MWL8K_CMD_GET_HW_SPEC ) ;
cmd - > header . length = cpu_to_le16 ( sizeof ( * cmd ) ) ;
memset ( cmd - > perm_addr , 0xff , sizeof ( cmd - > perm_addr ) ) ;
cmd - > ps_cookie = cpu_to_le32 ( priv - > cookie_dma ) ;
cmd - > rx_queue_ptr = cpu_to_le32 ( priv - > rxq [ 0 ] . rx_desc_dma ) ;
2009-08-03 21:58:39 +02:00
cmd - > num_tx_queues = cpu_to_le32 ( MWL8K_TX_QUEUES ) ;
2009-03-10 10:13:33 +01:00
for ( i = 0 ; i < MWL8K_TX_QUEUES ; i + + )
cmd - > tx_queue_ptrs [ i ] = cpu_to_le32 ( priv - > txq [ i ] . tx_desc_dma ) ;
2009-08-03 21:58:39 +02:00
cmd - > num_tx_desc_per_queue = cpu_to_le32 ( MWL8K_TX_DESCS ) ;
cmd - > total_rx_desc = cpu_to_le32 ( MWL8K_RX_DESCS ) ;
2009-03-10 10:13:33 +01:00
rc = mwl8k_post_cmd ( hw , & cmd - > header ) ;
if ( ! rc ) {
SET_IEEE80211_PERM_ADDR ( hw , cmd - > perm_addr ) ;
priv - > num_mcaddrs = le16_to_cpu ( cmd - > num_mcaddrs ) ;
2009-08-03 21:58:39 +02:00
priv - > fw_rev = le32_to_cpu ( cmd - > fw_rev ) ;
2009-03-10 10:13:33 +01:00
priv - > hw_rev = cmd - > hw_rev ;
}
kfree ( cmd ) ;
return rc ;
}
/*
* CMD_MAC_MULTICAST_ADR .
*/
struct mwl8k_cmd_mac_multicast_adr {
struct mwl8k_cmd_pkt header ;
__le16 action ;
__le16 numaddr ;
2009-07-16 14:00:45 +02:00
__u8 addr [ 0 ] [ ETH_ALEN ] ;
2009-03-10 10:13:33 +01:00
} ;
# define MWL8K_ENABLE_RX_MULTICAST 0x000F
2009-07-16 14:00:45 +02:00
2009-08-18 03:55:42 +02:00
static struct mwl8k_cmd_pkt *
__mwl8k_cmd_mac_multicast_adr ( struct ieee80211_hw * hw ,
int mc_count , struct dev_addr_list * mclist )
2009-03-10 10:13:33 +01:00
{
2009-08-18 03:55:42 +02:00
struct mwl8k_priv * priv = hw - > priv ;
2009-03-10 10:13:33 +01:00
struct mwl8k_cmd_mac_multicast_adr * cmd ;
2009-08-18 03:55:42 +02:00
int size ;
int i ;
if ( mc_count > priv - > num_mcaddrs )
mc_count = priv - > num_mcaddrs ;
size = sizeof ( * cmd ) + mc_count * ETH_ALEN ;
2009-07-16 14:00:45 +02:00
2009-08-18 03:55:42 +02:00
cmd = kzalloc ( size , GFP_ATOMIC ) ;
2009-03-10 10:13:33 +01:00
if ( cmd = = NULL )
2009-08-18 03:55:42 +02:00
return NULL ;
2009-03-10 10:13:33 +01:00
cmd - > header . code = cpu_to_le16 ( MWL8K_CMD_MAC_MULTICAST_ADR ) ;
cmd - > header . length = cpu_to_le16 ( size ) ;
cmd - > action = cpu_to_le16 ( MWL8K_ENABLE_RX_MULTICAST ) ;
cmd - > numaddr = cpu_to_le16 ( mc_count ) ;
2009-07-16 14:00:45 +02:00
2009-08-18 03:55:42 +02:00
for ( i = 0 ; i < mc_count & & mclist ; i + + ) {
2009-07-16 09:54:27 +02:00
if ( mclist - > da_addrlen ! = ETH_ALEN ) {
2009-08-18 03:55:42 +02:00
kfree ( cmd ) ;
return NULL ;
2009-03-10 10:13:33 +01:00
}
2009-08-18 03:55:42 +02:00
memcpy ( cmd - > addr [ i ] , mclist - > da_addr , ETH_ALEN ) ;
2009-03-10 10:13:33 +01:00
mclist = mclist - > next ;
}
2009-08-18 03:55:42 +02:00
return & cmd - > header ;
2009-03-10 10:13:33 +01:00
}
/*
* CMD_802_11_GET_STAT .
*/
struct mwl8k_cmd_802_11_get_stat {
struct mwl8k_cmd_pkt header ;
__le16 action ;
__le32 stats [ 64 ] ;
} __attribute__ ( ( packed ) ) ;
# define MWL8K_STAT_ACK_FAILURE 9
# define MWL8K_STAT_RTS_FAILURE 12
# define MWL8K_STAT_FCS_ERROR 24
# define MWL8K_STAT_RTS_SUCCESS 11
static int mwl8k_cmd_802_11_get_stat ( struct ieee80211_hw * hw ,
struct ieee80211_low_level_stats * stats )
{
struct mwl8k_cmd_802_11_get_stat * cmd ;
int rc ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( cmd = = NULL )
return - ENOMEM ;
cmd - > header . code = cpu_to_le16 ( MWL8K_CMD_GET_STAT ) ;
cmd - > header . length = cpu_to_le16 ( sizeof ( * cmd ) ) ;
cmd - > action = cpu_to_le16 ( MWL8K_CMD_GET ) ;
rc = mwl8k_post_cmd ( hw , & cmd - > header ) ;
if ( ! rc ) {
stats - > dot11ACKFailureCount =
le32_to_cpu ( cmd - > stats [ MWL8K_STAT_ACK_FAILURE ] ) ;
stats - > dot11RTSFailureCount =
le32_to_cpu ( cmd - > stats [ MWL8K_STAT_RTS_FAILURE ] ) ;
stats - > dot11FCSErrorCount =
le32_to_cpu ( cmd - > stats [ MWL8K_STAT_FCS_ERROR ] ) ;
stats - > dot11RTSSuccessCount =
le32_to_cpu ( cmd - > stats [ MWL8K_STAT_RTS_SUCCESS ] ) ;
}
kfree ( cmd ) ;
return rc ;
}
/*
* CMD_802_11_RADIO_CONTROL .
*/
struct mwl8k_cmd_802_11_radio_control {
struct mwl8k_cmd_pkt header ;
__le16 action ;
__le16 control ;
__le16 radio_on ;
} __attribute__ ( ( packed ) ) ;
2009-07-16 12:14:58 +02:00
static int
mwl8k_cmd_802_11_radio_control ( struct ieee80211_hw * hw , bool enable , bool force )
2009-03-10 10:13:33 +01:00
{
struct mwl8k_priv * priv = hw - > priv ;
struct mwl8k_cmd_802_11_radio_control * cmd ;
int rc ;
2009-07-16 12:14:58 +02:00
if ( enable = = priv - > radio_on & & ! force )
2009-03-10 10:13:33 +01:00
return 0 ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( cmd = = NULL )
return - ENOMEM ;
cmd - > header . code = cpu_to_le16 ( MWL8K_CMD_RADIO_CONTROL ) ;
cmd - > header . length = cpu_to_le16 ( sizeof ( * cmd ) ) ;
cmd - > action = cpu_to_le16 ( MWL8K_CMD_SET ) ;
2009-07-16 12:26:57 +02:00
cmd - > control = cpu_to_le16 ( priv - > radio_short_preamble ? 3 : 1 ) ;
2009-03-10 10:13:33 +01:00
cmd - > radio_on = cpu_to_le16 ( enable ? 0x0001 : 0x0000 ) ;
rc = mwl8k_post_cmd ( hw , & cmd - > header ) ;
kfree ( cmd ) ;
if ( ! rc )
2009-07-16 12:14:58 +02:00
priv - > radio_on = enable ;
2009-03-10 10:13:33 +01:00
return rc ;
}
2009-07-16 12:14:58 +02:00
static int mwl8k_cmd_802_11_radio_disable ( struct ieee80211_hw * hw )
{
return mwl8k_cmd_802_11_radio_control ( hw , 0 , 0 ) ;
}
static int mwl8k_cmd_802_11_radio_enable ( struct ieee80211_hw * hw )
{
return mwl8k_cmd_802_11_radio_control ( hw , 1 , 0 ) ;
}
2009-03-10 10:13:33 +01:00
static int
mwl8k_set_radio_preamble ( struct ieee80211_hw * hw , bool short_preamble )
{
struct mwl8k_priv * priv ;
if ( hw = = NULL | | hw - > priv = = NULL )
return - EINVAL ;
priv = hw - > priv ;
2009-07-16 12:26:57 +02:00
priv - > radio_short_preamble = short_preamble ;
2009-03-10 10:13:33 +01:00
2009-07-16 12:14:58 +02:00
return mwl8k_cmd_802_11_radio_control ( hw , 1 , 1 ) ;
2009-03-10 10:13:33 +01:00
}
/*
* CMD_802_11_RF_TX_POWER .
*/
# define MWL8K_TX_POWER_LEVEL_TOTAL 8
struct mwl8k_cmd_802_11_rf_tx_power {
struct mwl8k_cmd_pkt header ;
__le16 action ;
__le16 support_level ;
__le16 current_level ;
__le16 reserved ;
__le16 power_level_list [ MWL8K_TX_POWER_LEVEL_TOTAL ] ;
} __attribute__ ( ( packed ) ) ;
static int mwl8k_cmd_802_11_rf_tx_power ( struct ieee80211_hw * hw , int dBm )
{
struct mwl8k_cmd_802_11_rf_tx_power * cmd ;
int rc ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( cmd = = NULL )
return - ENOMEM ;
cmd - > header . code = cpu_to_le16 ( MWL8K_CMD_RF_TX_POWER ) ;
cmd - > header . length = cpu_to_le16 ( sizeof ( * cmd ) ) ;
cmd - > action = cpu_to_le16 ( MWL8K_CMD_SET ) ;
cmd - > support_level = cpu_to_le16 ( dBm ) ;
rc = mwl8k_post_cmd ( hw , & cmd - > header ) ;
kfree ( cmd ) ;
return rc ;
}
/*
* CMD_SET_PRE_SCAN .
*/
struct mwl8k_cmd_set_pre_scan {
struct mwl8k_cmd_pkt header ;
} __attribute__ ( ( packed ) ) ;
static int mwl8k_cmd_set_pre_scan ( struct ieee80211_hw * hw )
{
struct mwl8k_cmd_set_pre_scan * cmd ;
int rc ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( cmd = = NULL )
return - ENOMEM ;
cmd - > header . code = cpu_to_le16 ( MWL8K_CMD_SET_PRE_SCAN ) ;
cmd - > header . length = cpu_to_le16 ( sizeof ( * cmd ) ) ;
rc = mwl8k_post_cmd ( hw , & cmd - > header ) ;
kfree ( cmd ) ;
return rc ;
}
/*
* CMD_SET_POST_SCAN .
*/
struct mwl8k_cmd_set_post_scan {
struct mwl8k_cmd_pkt header ;
__le32 isibss ;
2009-07-16 09:54:27 +02:00
__u8 bssid [ ETH_ALEN ] ;
2009-03-10 10:13:33 +01:00
} __attribute__ ( ( packed ) ) ;
static int
2009-07-16 14:00:45 +02:00
mwl8k_cmd_set_post_scan ( struct ieee80211_hw * hw , __u8 * mac )
2009-03-10 10:13:33 +01:00
{
struct mwl8k_cmd_set_post_scan * cmd ;
int rc ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( cmd = = NULL )
return - ENOMEM ;
cmd - > header . code = cpu_to_le16 ( MWL8K_CMD_SET_POST_SCAN ) ;
cmd - > header . length = cpu_to_le16 ( sizeof ( * cmd ) ) ;
cmd - > isibss = 0 ;
2009-07-16 09:54:27 +02:00
memcpy ( cmd - > bssid , mac , ETH_ALEN ) ;
2009-03-10 10:13:33 +01:00
rc = mwl8k_post_cmd ( hw , & cmd - > header ) ;
kfree ( cmd ) ;
return rc ;
}
/*
* CMD_SET_RF_CHANNEL .
*/
struct mwl8k_cmd_set_rf_channel {
struct mwl8k_cmd_pkt header ;
__le16 action ;
__u8 current_channel ;
__le32 channel_flags ;
} __attribute__ ( ( packed ) ) ;
static int mwl8k_cmd_set_rf_channel ( struct ieee80211_hw * hw ,
struct ieee80211_channel * channel )
{
struct mwl8k_cmd_set_rf_channel * cmd ;
int rc ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( cmd = = NULL )
return - ENOMEM ;
cmd - > header . code = cpu_to_le16 ( MWL8K_CMD_SET_RF_CHANNEL ) ;
cmd - > header . length = cpu_to_le16 ( sizeof ( * cmd ) ) ;
cmd - > action = cpu_to_le16 ( MWL8K_CMD_SET ) ;
cmd - > current_channel = channel - > hw_value ;
if ( channel - > band = = IEEE80211_BAND_2GHZ )
cmd - > channel_flags = cpu_to_le32 ( 0x00000081 ) ;
else
cmd - > channel_flags = cpu_to_le32 ( 0x00000000 ) ;
rc = mwl8k_post_cmd ( hw , & cmd - > header ) ;
kfree ( cmd ) ;
return rc ;
}
/*
* CMD_SET_SLOT .
*/
struct mwl8k_cmd_set_slot {
struct mwl8k_cmd_pkt header ;
__le16 action ;
__u8 short_slot ;
} __attribute__ ( ( packed ) ) ;
2009-07-16 16:06:53 +02:00
static int mwl8k_cmd_set_slot ( struct ieee80211_hw * hw , bool short_slot_time )
2009-03-10 10:13:33 +01:00
{
struct mwl8k_cmd_set_slot * cmd ;
int rc ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( cmd = = NULL )
return - ENOMEM ;
cmd - > header . code = cpu_to_le16 ( MWL8K_CMD_SET_SLOT ) ;
cmd - > header . length = cpu_to_le16 ( sizeof ( * cmd ) ) ;
cmd - > action = cpu_to_le16 ( MWL8K_CMD_SET ) ;
2009-07-16 16:06:53 +02:00
cmd - > short_slot = short_slot_time ;
2009-03-10 10:13:33 +01:00
rc = mwl8k_post_cmd ( hw , & cmd - > header ) ;
kfree ( cmd ) ;
return rc ;
}
/*
* CMD_MIMO_CONFIG .
*/
struct mwl8k_cmd_mimo_config {
struct mwl8k_cmd_pkt header ;
__le32 action ;
__u8 rx_antenna_map ;
__u8 tx_antenna_map ;
} __attribute__ ( ( packed ) ) ;
static int mwl8k_cmd_mimo_config ( struct ieee80211_hw * hw , __u8 rx , __u8 tx )
{
struct mwl8k_cmd_mimo_config * cmd ;
int rc ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( cmd = = NULL )
return - ENOMEM ;
cmd - > header . code = cpu_to_le16 ( MWL8K_CMD_MIMO_CONFIG ) ;
cmd - > header . length = cpu_to_le16 ( sizeof ( * cmd ) ) ;
cmd - > action = cpu_to_le32 ( ( u32 ) MWL8K_CMD_SET ) ;
cmd - > rx_antenna_map = rx ;
cmd - > tx_antenna_map = tx ;
rc = mwl8k_post_cmd ( hw , & cmd - > header ) ;
kfree ( cmd ) ;
return rc ;
}
/*
* CMD_ENABLE_SNIFFER .
*/
struct mwl8k_cmd_enable_sniffer {
struct mwl8k_cmd_pkt header ;
__le32 action ;
} __attribute__ ( ( packed ) ) ;
static int mwl8k_enable_sniffer ( struct ieee80211_hw * hw , bool enable )
{
struct mwl8k_cmd_enable_sniffer * cmd ;
int rc ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( cmd = = NULL )
return - ENOMEM ;
cmd - > header . code = cpu_to_le16 ( MWL8K_CMD_ENABLE_SNIFFER ) ;
cmd - > header . length = cpu_to_le16 ( sizeof ( * cmd ) ) ;
2009-07-16 14:00:45 +02:00
cmd - > action = cpu_to_le32 ( ! ! enable ) ;
2009-03-10 10:13:33 +01:00
rc = mwl8k_post_cmd ( hw , & cmd - > header ) ;
kfree ( cmd ) ;
return rc ;
}
/*
2009-07-16 14:00:45 +02:00
* CMD_SET_RATEADAPT_MODE .
2009-03-10 10:13:33 +01:00
*/
struct mwl8k_cmd_set_rate_adapt_mode {
struct mwl8k_cmd_pkt header ;
__le16 action ;
__le16 mode ;
} __attribute__ ( ( packed ) ) ;
static int mwl8k_cmd_setrateadaptmode ( struct ieee80211_hw * hw , __u16 mode )
{
struct mwl8k_cmd_set_rate_adapt_mode * cmd ;
int rc ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( cmd = = NULL )
return - ENOMEM ;
cmd - > header . code = cpu_to_le16 ( MWL8K_CMD_SET_RATEADAPT_MODE ) ;
cmd - > header . length = cpu_to_le16 ( sizeof ( * cmd ) ) ;
cmd - > action = cpu_to_le16 ( MWL8K_CMD_SET ) ;
cmd - > mode = cpu_to_le16 ( mode ) ;
rc = mwl8k_post_cmd ( hw , & cmd - > header ) ;
kfree ( cmd ) ;
return rc ;
}
/*
* CMD_SET_WMM_MODE .
*/
struct mwl8k_cmd_set_wmm {
struct mwl8k_cmd_pkt header ;
__le16 action ;
} __attribute__ ( ( packed ) ) ;
static int mwl8k_set_wmm ( struct ieee80211_hw * hw , bool enable )
{
struct mwl8k_priv * priv = hw - > priv ;
struct mwl8k_cmd_set_wmm * cmd ;
int rc ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( cmd = = NULL )
return - ENOMEM ;
cmd - > header . code = cpu_to_le16 ( MWL8K_CMD_SET_WMM_MODE ) ;
cmd - > header . length = cpu_to_le16 ( sizeof ( * cmd ) ) ;
2009-07-16 12:34:02 +02:00
cmd - > action = cpu_to_le16 ( ! ! enable ) ;
2009-03-10 10:13:33 +01:00
rc = mwl8k_post_cmd ( hw , & cmd - > header ) ;
kfree ( cmd ) ;
if ( ! rc )
2009-07-16 12:34:02 +02:00
priv - > wmm_enabled = enable ;
2009-03-10 10:13:33 +01:00
return rc ;
}
/*
* CMD_SET_RTS_THRESHOLD .
*/
struct mwl8k_cmd_rts_threshold {
struct mwl8k_cmd_pkt header ;
__le16 action ;
__le16 threshold ;
} __attribute__ ( ( packed ) ) ;
static int mwl8k_rts_threshold ( struct ieee80211_hw * hw ,
2009-07-17 07:24:15 +02:00
u16 action , u16 threshold )
2009-03-10 10:13:33 +01:00
{
struct mwl8k_cmd_rts_threshold * cmd ;
int rc ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( cmd = = NULL )
return - ENOMEM ;
cmd - > header . code = cpu_to_le16 ( MWL8K_CMD_RTS_THRESHOLD ) ;
cmd - > header . length = cpu_to_le16 ( sizeof ( * cmd ) ) ;
cmd - > action = cpu_to_le16 ( action ) ;
2009-07-17 07:24:15 +02:00
cmd - > threshold = cpu_to_le16 ( threshold ) ;
2009-03-10 10:13:33 +01:00
rc = mwl8k_post_cmd ( hw , & cmd - > header ) ;
kfree ( cmd ) ;
return rc ;
}
/*
* CMD_SET_EDCA_PARAMS .
*/
struct mwl8k_cmd_set_edca_params {
struct mwl8k_cmd_pkt header ;
/* See MWL8K_SET_EDCA_XXX below */
__le16 action ;
/* TX opportunity in units of 32 us */
__le16 txop ;
/* Log exponent of max contention period: 0...15*/
__u8 log_cw_max ;
/* Log exponent of min contention period: 0...15 */
__u8 log_cw_min ;
/* Adaptive interframe spacing in units of 32us */
__u8 aifs ;
/* TX queue to configure */
__u8 txq ;
} __attribute__ ( ( packed ) ) ;
# define MWL8K_SET_EDCA_CW 0x01
# define MWL8K_SET_EDCA_TXOP 0x02
# define MWL8K_SET_EDCA_AIFS 0x04
# define MWL8K_SET_EDCA_ALL (MWL8K_SET_EDCA_CW | \
MWL8K_SET_EDCA_TXOP | \
MWL8K_SET_EDCA_AIFS )
static int
mwl8k_set_edca_params ( struct ieee80211_hw * hw , __u8 qnum ,
__u16 cw_min , __u16 cw_max ,
__u8 aifs , __u16 txop )
{
struct mwl8k_cmd_set_edca_params * cmd ;
int rc ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( cmd = = NULL )
return - ENOMEM ;
cmd - > header . code = cpu_to_le16 ( MWL8K_CMD_SET_EDCA_PARAMS ) ;
cmd - > header . length = cpu_to_le16 ( sizeof ( * cmd ) ) ;
cmd - > action = cpu_to_le16 ( MWL8K_SET_EDCA_ALL ) ;
cmd - > txop = cpu_to_le16 ( txop ) ;
2009-07-16 14:00:45 +02:00
cmd - > log_cw_max = ( u8 ) ilog2 ( cw_max + 1 ) ;
cmd - > log_cw_min = ( u8 ) ilog2 ( cw_min + 1 ) ;
2009-03-10 10:13:33 +01:00
cmd - > aifs = aifs ;
cmd - > txq = qnum ;
rc = mwl8k_post_cmd ( hw , & cmd - > header ) ;
kfree ( cmd ) ;
return rc ;
}
/*
* CMD_FINALIZE_JOIN .
*/
/* FJ beacon buffer size is compiled into the firmware. */
# define MWL8K_FJ_BEACON_MAXLEN 128
struct mwl8k_cmd_finalize_join {
struct mwl8k_cmd_pkt header ;
__le32 sleep_interval ; /* Number of beacon periods to sleep */
__u8 beacon_data [ MWL8K_FJ_BEACON_MAXLEN ] ;
} __attribute__ ( ( packed ) ) ;
static int mwl8k_finalize_join ( struct ieee80211_hw * hw , void * frame ,
__u16 framelen , __u16 dtim )
{
struct mwl8k_cmd_finalize_join * cmd ;
struct ieee80211_mgmt * payload = frame ;
u16 hdrlen ;
u32 payload_len ;
int rc ;
if ( frame = = NULL )
return - EINVAL ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( cmd = = NULL )
return - ENOMEM ;
cmd - > header . code = cpu_to_le16 ( MWL8K_CMD_SET_FINALIZE_JOIN ) ;
cmd - > header . length = cpu_to_le16 ( sizeof ( * cmd ) ) ;
2009-07-16 14:00:45 +02:00
cmd - > sleep_interval = cpu_to_le32 ( dtim ? dtim : 1 ) ;
2009-03-10 10:13:33 +01:00
hdrlen = ieee80211_hdrlen ( payload - > frame_control ) ;
payload_len = framelen > hdrlen ? framelen - hdrlen : 0 ;
/* XXX TBD Might just have to abort and return an error */
if ( payload_len > MWL8K_FJ_BEACON_MAXLEN )
printk ( KERN_ERR " %s(): WARNING: Incomplete beacon "
" sent to firmware. Sz=%u MAX=%u \n " , __func__ ,
payload_len , MWL8K_FJ_BEACON_MAXLEN ) ;
2009-07-16 14:00:45 +02:00
if ( payload_len > MWL8K_FJ_BEACON_MAXLEN )
payload_len = MWL8K_FJ_BEACON_MAXLEN ;
2009-03-10 10:13:33 +01:00
if ( payload & & payload_len )
memcpy ( cmd - > beacon_data , & payload - > u . beacon , payload_len ) ;
rc = mwl8k_post_cmd ( hw , & cmd - > header ) ;
kfree ( cmd ) ;
return rc ;
}
/*
* CMD_UPDATE_STADB .
*/
struct mwl8k_cmd_update_sta_db {
struct mwl8k_cmd_pkt header ;
/* See STADB_ACTION_TYPE */
__le32 action ;
/* Peer MAC address */
2009-07-16 09:54:27 +02:00
__u8 peer_addr [ ETH_ALEN ] ;
2009-03-10 10:13:33 +01:00
__le32 reserved ;
/* Peer info - valid during add/update. */
struct peer_capability_info peer_info ;
} __attribute__ ( ( packed ) ) ;
static int mwl8k_cmd_update_sta_db ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif , __u32 action )
{
struct mwl8k_vif * mv_vif = MWL8K_VIF ( vif ) ;
struct ieee80211_bss_conf * info = & mv_vif - > bss_info ;
struct mwl8k_cmd_update_sta_db * cmd ;
struct peer_capability_info * peer_info ;
struct ieee80211_rate * bitrates = mv_vif - > legacy_rates ;
int rc ;
__u8 count , * rates ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( cmd = = NULL )
return - ENOMEM ;
cmd - > header . code = cpu_to_le16 ( MWL8K_CMD_UPDATE_STADB ) ;
cmd - > header . length = cpu_to_le16 ( sizeof ( * cmd ) ) ;
cmd - > action = cpu_to_le32 ( action ) ;
peer_info = & cmd - > peer_info ;
2009-07-16 09:54:27 +02:00
memcpy ( cmd - > peer_addr , mv_vif - > bssid , ETH_ALEN ) ;
2009-03-10 10:13:33 +01:00
switch ( action ) {
case MWL8K_STA_DB_ADD_ENTRY :
case MWL8K_STA_DB_MODIFY_ENTRY :
/* Build peer_info block */
peer_info - > peer_type = MWL8K_PEER_TYPE_ACCESSPOINT ;
peer_info - > basic_caps = cpu_to_le16 ( info - > assoc_capability ) ;
peer_info - > interop = 1 ;
peer_info - > amsdu_enabled = 0 ;
rates = peer_info - > legacy_rates ;
2009-07-16 14:00:45 +02:00
for ( count = 0 ; count < mv_vif - > legacy_nrates ; count + + )
2009-03-10 10:13:33 +01:00
rates [ count ] = bitrates [ count ] . hw_value ;
rc = mwl8k_post_cmd ( hw , & cmd - > header ) ;
if ( rc = = 0 )
mv_vif - > peer_id = peer_info - > station_id ;
break ;
case MWL8K_STA_DB_DEL_ENTRY :
case MWL8K_STA_DB_FLUSH :
default :
rc = mwl8k_post_cmd ( hw , & cmd - > header ) ;
if ( rc = = 0 )
mv_vif - > peer_id = 0 ;
break ;
}
kfree ( cmd ) ;
return rc ;
}
/*
* CMD_SET_AID .
*/
# define MWL8K_RATE_INDEX_MAX_ARRAY 14
# define MWL8K_FRAME_PROT_DISABLED 0x00
# define MWL8K_FRAME_PROT_11G 0x07
# define MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY 0x02
# define MWL8K_FRAME_PROT_11N_HT_ALL 0x06
struct mwl8k_cmd_update_set_aid {
struct mwl8k_cmd_pkt header ;
__le16 aid ;
/* AP's MAC address (BSSID) */
2009-07-16 09:54:27 +02:00
__u8 bssid [ ETH_ALEN ] ;
2009-03-10 10:13:33 +01:00
__le16 protection_mode ;
__u8 supp_rates [ MWL8K_RATE_INDEX_MAX_ARRAY ] ;
} __attribute__ ( ( packed ) ) ;
static int mwl8k_cmd_set_aid ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif )
{
struct mwl8k_vif * mv_vif = MWL8K_VIF ( vif ) ;
struct ieee80211_bss_conf * info = & mv_vif - > bss_info ;
struct mwl8k_cmd_update_set_aid * cmd ;
struct ieee80211_rate * bitrates = mv_vif - > legacy_rates ;
int count ;
u16 prot_mode ;
int rc ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( cmd = = NULL )
return - ENOMEM ;
cmd - > header . code = cpu_to_le16 ( MWL8K_CMD_SET_AID ) ;
cmd - > header . length = cpu_to_le16 ( sizeof ( * cmd ) ) ;
cmd - > aid = cpu_to_le16 ( info - > aid ) ;
2009-07-16 09:54:27 +02:00
memcpy ( cmd - > bssid , mv_vif - > bssid , ETH_ALEN ) ;
2009-03-10 10:13:33 +01:00
if ( info - > use_cts_prot ) {
prot_mode = MWL8K_FRAME_PROT_11G ;
} else {
2009-05-08 20:47:39 +02:00
switch ( info - > ht_operation_mode &
2009-03-10 10:13:33 +01:00
IEEE80211_HT_OP_MODE_PROTECTION ) {
case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ :
prot_mode = MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY ;
break ;
case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED :
prot_mode = MWL8K_FRAME_PROT_11N_HT_ALL ;
break ;
default :
prot_mode = MWL8K_FRAME_PROT_DISABLED ;
break ;
}
}
cmd - > protection_mode = cpu_to_le16 ( prot_mode ) ;
for ( count = 0 ; count < mv_vif - > legacy_nrates ; count + + )
cmd - > supp_rates [ count ] = bitrates [ count ] . hw_value ;
rc = mwl8k_post_cmd ( hw , & cmd - > header ) ;
kfree ( cmd ) ;
return rc ;
}
/*
* CMD_SET_RATE .
*/
struct mwl8k_cmd_update_rateset {
struct mwl8k_cmd_pkt header ;
__u8 legacy_rates [ MWL8K_RATE_INDEX_MAX_ARRAY ] ;
/* Bitmap for supported MCS codes. */
__u8 mcs_set [ MWL8K_IEEE_LEGACY_DATA_RATES ] ;
__u8 reserved [ MWL8K_IEEE_LEGACY_DATA_RATES ] ;
} __attribute__ ( ( packed ) ) ;
static int mwl8k_update_rateset ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif )
{
struct mwl8k_vif * mv_vif = MWL8K_VIF ( vif ) ;
struct mwl8k_cmd_update_rateset * cmd ;
struct ieee80211_rate * bitrates = mv_vif - > legacy_rates ;
int count ;
int rc ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( cmd = = NULL )
return - ENOMEM ;
cmd - > header . code = cpu_to_le16 ( MWL8K_CMD_SET_RATE ) ;
cmd - > header . length = cpu_to_le16 ( sizeof ( * cmd ) ) ;
for ( count = 0 ; count < mv_vif - > legacy_nrates ; count + + )
cmd - > legacy_rates [ count ] = bitrates [ count ] . hw_value ;
rc = mwl8k_post_cmd ( hw , & cmd - > header ) ;
kfree ( cmd ) ;
return rc ;
}
/*
* CMD_USE_FIXED_RATE .
*/
# define MWL8K_RATE_TABLE_SIZE 8
# define MWL8K_UCAST_RATE 0
# define MWL8K_USE_AUTO_RATE 0x0002
struct mwl8k_rate_entry {
/* Set to 1 if HT rate, 0 if legacy. */
__le32 is_ht_rate ;
/* Set to 1 to use retry_count field. */
__le32 enable_retry ;
/* Specified legacy rate or MCS. */
__le32 rate ;
/* Number of allowed retries. */
__le32 retry_count ;
} __attribute__ ( ( packed ) ) ;
struct mwl8k_rate_table {
/* 1 to allow specified rate and below */
__le32 allow_rate_drop ;
__le32 num_rates ;
struct mwl8k_rate_entry rate_entry [ MWL8K_RATE_TABLE_SIZE ] ;
} __attribute__ ( ( packed ) ) ;
struct mwl8k_cmd_use_fixed_rate {
struct mwl8k_cmd_pkt header ;
__le32 action ;
struct mwl8k_rate_table rate_table ;
/* Unicast, Broadcast or Multicast */
__le32 rate_type ;
__le32 reserved1 ;
__le32 reserved2 ;
} __attribute__ ( ( packed ) ) ;
static int mwl8k_cmd_use_fixed_rate ( struct ieee80211_hw * hw ,
u32 action , u32 rate_type , struct mwl8k_rate_table * rate_table )
{
struct mwl8k_cmd_use_fixed_rate * cmd ;
int count ;
int rc ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( cmd = = NULL )
return - ENOMEM ;
cmd - > header . code = cpu_to_le16 ( MWL8K_CMD_USE_FIXED_RATE ) ;
cmd - > header . length = cpu_to_le16 ( sizeof ( * cmd ) ) ;
cmd - > action = cpu_to_le32 ( action ) ;
cmd - > rate_type = cpu_to_le32 ( rate_type ) ;
if ( rate_table ! = NULL ) {
/* Copy over each field manually so
* that bitflipping can be done
*/
cmd - > rate_table . allow_rate_drop =
cpu_to_le32 ( rate_table - > allow_rate_drop ) ;
cmd - > rate_table . num_rates =
cpu_to_le32 ( rate_table - > num_rates ) ;
for ( count = 0 ; count < rate_table - > num_rates ; count + + ) {
struct mwl8k_rate_entry * dst =
& cmd - > rate_table . rate_entry [ count ] ;
struct mwl8k_rate_entry * src =
& rate_table - > rate_entry [ count ] ;
dst - > is_ht_rate = cpu_to_le32 ( src - > is_ht_rate ) ;
dst - > enable_retry = cpu_to_le32 ( src - > enable_retry ) ;
dst - > rate = cpu_to_le32 ( src - > rate ) ;
dst - > retry_count = cpu_to_le32 ( src - > retry_count ) ;
}
}
rc = mwl8k_post_cmd ( hw , & cmd - > header ) ;
kfree ( cmd ) ;
return rc ;
}
/*
* Interrupt handling .
*/
static irqreturn_t mwl8k_interrupt ( int irq , void * dev_id )
{
struct ieee80211_hw * hw = dev_id ;
struct mwl8k_priv * priv = hw - > priv ;
u32 status ;
status = ioread32 ( priv - > regs + MWL8K_HIU_A2H_INTERRUPT_STATUS ) ;
iowrite32 ( ~ status , priv - > regs + MWL8K_HIU_A2H_INTERRUPT_STATUS ) ;
if ( ! status )
return IRQ_NONE ;
if ( status & MWL8K_A2H_INT_TX_DONE )
tasklet_schedule ( & priv - > tx_reclaim_task ) ;
if ( status & MWL8K_A2H_INT_RX_READY ) {
while ( rxq_process ( hw , 0 , 1 ) )
rxq_refill ( hw , 0 , 1 ) ;
}
if ( status & MWL8K_A2H_INT_OPC_DONE ) {
2009-08-18 03:18:01 +02:00
if ( priv - > hostcmd_wait ! = NULL )
2009-03-10 10:13:33 +01:00
complete ( priv - > hostcmd_wait ) ;
}
if ( status & MWL8K_A2H_INT_QUEUE_EMPTY ) {
2009-08-18 03:18:01 +02:00
if ( ! mutex_is_locked ( & priv - > fw_mutex ) & &
priv - > radio_on & & mwl8k_txq_busy ( priv ) )
mwl8k_tx_start ( priv ) ;
2009-03-10 10:13:33 +01:00
}
return IRQ_HANDLED ;
}
/*
* Core driver operations .
*/
static int mwl8k_tx ( struct ieee80211_hw * hw , struct sk_buff * skb )
{
struct mwl8k_priv * priv = hw - > priv ;
int index = skb_get_queue_mapping ( skb ) ;
int rc ;
if ( priv - > current_channel = = NULL ) {
printk ( KERN_DEBUG " %s: dropped TX frame since radio "
" disabled \n " , priv - > name ) ;
dev_kfree_skb ( skb ) ;
return NETDEV_TX_OK ;
}
rc = mwl8k_txq_xmit ( hw , index , skb ) ;
return rc ;
}
static int mwl8k_start ( struct ieee80211_hw * hw )
{
struct mwl8k_priv * priv = hw - > priv ;
int rc ;
rc = request_irq ( priv - > pdev - > irq , & mwl8k_interrupt ,
IRQF_SHARED , MWL8K_NAME , hw ) ;
if ( rc ) {
printk ( KERN_ERR " %s: failed to register IRQ handler \n " ,
priv - > name ) ;
2009-07-17 07:11:37 +02:00
return - EIO ;
2009-03-10 10:13:33 +01:00
}
2009-07-17 07:11:37 +02:00
/* Enable tx reclaim tasklet */
tasklet_enable ( & priv - > tx_reclaim_task ) ;
2009-03-10 10:13:33 +01:00
/* Enable interrupts */
2009-07-16 13:49:55 +02:00
iowrite32 ( MWL8K_A2H_EVENTS , priv - > regs + MWL8K_HIU_A2H_INTERRUPT_MASK ) ;
2009-03-10 10:13:33 +01:00
2009-07-17 07:11:37 +02:00
rc = mwl8k_fw_lock ( hw ) ;
if ( ! rc ) {
rc = mwl8k_cmd_802_11_radio_enable ( hw ) ;
2009-03-10 10:13:33 +01:00
2009-07-17 07:11:37 +02:00
if ( ! rc )
rc = mwl8k_cmd_set_pre_scan ( hw ) ;
2009-03-10 10:13:33 +01:00
2009-07-17 07:11:37 +02:00
if ( ! rc )
rc = mwl8k_cmd_set_post_scan ( hw ,
" \x00 \x00 \x00 \x00 \x00 \x00 " ) ;
if ( ! rc )
rc = mwl8k_cmd_setrateadaptmode ( hw , 0 ) ;
2009-03-10 10:13:33 +01:00
2009-07-17 07:11:37 +02:00
if ( ! rc )
rc = mwl8k_set_wmm ( hw , 0 ) ;
2009-03-10 10:13:33 +01:00
2009-07-17 07:11:37 +02:00
if ( ! rc )
rc = mwl8k_enable_sniffer ( hw , 0 ) ;
2009-03-10 10:13:33 +01:00
2009-07-17 07:11:37 +02:00
mwl8k_fw_unlock ( hw ) ;
}
if ( rc ) {
iowrite32 ( 0 , priv - > regs + MWL8K_HIU_A2H_INTERRUPT_MASK ) ;
free_irq ( priv - > pdev - > irq , hw ) ;
tasklet_disable ( & priv - > tx_reclaim_task ) ;
}
2009-03-10 10:13:33 +01:00
return rc ;
}
static void mwl8k_stop ( struct ieee80211_hw * hw )
{
struct mwl8k_priv * priv = hw - > priv ;
int i ;
2009-07-17 07:15:49 +02:00
mwl8k_cmd_802_11_radio_disable ( hw ) ;
2009-03-10 10:13:33 +01:00
ieee80211_stop_queues ( hw ) ;
/* Disable interrupts */
iowrite32 ( 0 , priv - > regs + MWL8K_HIU_A2H_INTERRUPT_MASK ) ;
free_irq ( priv - > pdev - > irq , hw ) ;
/* Stop finalize join worker */
cancel_work_sync ( & priv - > finalize_join_worker ) ;
if ( priv - > beacon_skb ! = NULL )
dev_kfree_skb ( priv - > beacon_skb ) ;
/* Stop tx reclaim tasklet */
tasklet_disable ( & priv - > tx_reclaim_task ) ;
/* Stop config thread */
flush_workqueue ( priv - > config_wq ) ;
/* Return all skbs to mac80211 */
for ( i = 0 ; i < MWL8K_TX_QUEUES ; i + + )
mwl8k_txq_reclaim ( hw , i , 1 ) ;
}
static int mwl8k_add_interface ( struct ieee80211_hw * hw ,
struct ieee80211_if_init_conf * conf )
{
struct mwl8k_priv * priv = hw - > priv ;
struct mwl8k_vif * mwl8k_vif ;
/*
* We only support one active interface at a time .
*/
if ( priv - > vif ! = NULL )
return - EBUSY ;
/*
* We only support managed interfaces for now .
*/
2009-07-16 14:15:44 +02:00
if ( conf - > type ! = NL80211_IFTYPE_STATION )
2009-03-10 10:13:33 +01:00
return - EINVAL ;
/* Clean out driver private area */
mwl8k_vif = MWL8K_VIF ( conf - > vif ) ;
memset ( mwl8k_vif , 0 , sizeof ( * mwl8k_vif ) ) ;
/* Save the mac address */
2009-07-16 09:54:27 +02:00
memcpy ( mwl8k_vif - > mac_addr , conf - > mac_addr , ETH_ALEN ) ;
2009-03-10 10:13:33 +01:00
/* Back pointer to parent config block */
mwl8k_vif - > priv = priv ;
/* Setup initial PHY parameters */
2009-07-16 14:00:45 +02:00
memcpy ( mwl8k_vif - > legacy_rates ,
2009-03-10 10:13:33 +01:00
priv - > rates , sizeof ( mwl8k_vif - > legacy_rates ) ) ;
mwl8k_vif - > legacy_nrates = ARRAY_SIZE ( priv - > rates ) ;
/* Set Initial sequence number to zero */
mwl8k_vif - > seqno = 0 ;
priv - > vif = conf - > vif ;
priv - > current_channel = NULL ;
return 0 ;
}
static void mwl8k_remove_interface ( struct ieee80211_hw * hw ,
struct ieee80211_if_init_conf * conf )
{
struct mwl8k_priv * priv = hw - > priv ;
if ( priv - > vif = = NULL )
return ;
priv - > vif = NULL ;
}
2009-07-17 07:19:37 +02:00
static int mwl8k_config ( struct ieee80211_hw * hw , u32 changed )
2009-03-10 10:13:33 +01:00
{
struct ieee80211_conf * conf = & hw - > conf ;
struct mwl8k_priv * priv = hw - > priv ;
2009-07-17 07:19:37 +02:00
int rc ;
2009-03-10 10:13:33 +01:00
2009-08-17 23:59:40 +02:00
if ( conf - > flags & IEEE80211_CONF_IDLE ) {
mwl8k_cmd_802_11_radio_disable ( hw ) ;
priv - > current_channel = NULL ;
2009-07-17 07:19:37 +02:00
return 0 ;
2009-08-17 23:59:40 +02:00
}
2009-07-17 07:19:37 +02:00
rc = mwl8k_fw_lock ( hw ) ;
if ( rc )
return rc ;
2009-03-10 10:13:33 +01:00
2009-07-17 07:19:37 +02:00
rc = mwl8k_cmd_802_11_radio_enable ( hw ) ;
if ( rc )
goto out ;
2009-03-10 10:13:33 +01:00
2009-07-17 07:19:37 +02:00
rc = mwl8k_cmd_set_rf_channel ( hw , conf - > channel ) ;
if ( rc )
goto out ;
priv - > current_channel = conf - > channel ;
2009-03-10 10:13:33 +01:00
if ( conf - > power_level > 18 )
conf - > power_level = 18 ;
2009-07-17 07:19:37 +02:00
rc = mwl8k_cmd_802_11_rf_tx_power ( hw , conf - > power_level ) ;
if ( rc )
goto out ;
2009-03-10 10:13:33 +01:00
if ( mwl8k_cmd_mimo_config ( hw , 0x7 , 0x7 ) )
rc = - EINVAL ;
2009-07-17 07:19:37 +02:00
out :
mwl8k_fw_unlock ( hw ) ;
2009-03-10 10:13:33 +01:00
2009-07-17 07:19:37 +02:00
return rc ;
2009-03-10 10:13:33 +01:00
}
2009-07-17 07:21:46 +02:00
static void mwl8k_bss_info_changed ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif ,
struct ieee80211_bss_conf * info ,
u32 changed )
2009-03-10 10:13:33 +01:00
{
struct mwl8k_priv * priv = hw - > priv ;
struct mwl8k_vif * mwl8k_vif = MWL8K_VIF ( vif ) ;
2009-07-17 07:21:46 +02:00
int rc ;
if ( changed & BSS_CHANGED_BSSID )
memcpy ( mwl8k_vif - > bssid , info - > bssid , ETH_ALEN ) ;
if ( ( changed & BSS_CHANGED_ASSOC ) = = 0 )
return ;
2009-03-10 10:13:33 +01:00
priv - > capture_beacon = false ;
2009-07-17 07:21:46 +02:00
rc = mwl8k_fw_lock ( hw ) ;
2009-08-24 15:42:36 +02:00
if ( rc )
2009-07-17 07:21:46 +02:00
return ;
2009-03-10 10:13:33 +01:00
if ( info - > assoc ) {
memcpy ( & mwl8k_vif - > bss_info , info ,
sizeof ( struct ieee80211_bss_conf ) ) ;
/* Install rates */
2009-07-17 07:21:46 +02:00
rc = mwl8k_update_rateset ( hw , vif ) ;
if ( rc )
goto out ;
2009-03-10 10:13:33 +01:00
/* Turn on rate adaptation */
2009-07-17 07:21:46 +02:00
rc = mwl8k_cmd_use_fixed_rate ( hw , MWL8K_USE_AUTO_RATE ,
MWL8K_UCAST_RATE , NULL ) ;
if ( rc )
goto out ;
2009-03-10 10:13:33 +01:00
/* Set radio preamble */
2009-07-17 07:21:46 +02:00
rc = mwl8k_set_radio_preamble ( hw , info - > use_short_preamble ) ;
if ( rc )
goto out ;
2009-03-10 10:13:33 +01:00
/* Set slot time */
2009-07-17 07:21:46 +02:00
rc = mwl8k_cmd_set_slot ( hw , info - > use_short_slot ) ;
if ( rc )
goto out ;
2009-03-10 10:13:33 +01:00
/* Update peer rate info */
2009-07-17 07:21:46 +02:00
rc = mwl8k_cmd_update_sta_db ( hw , vif ,
MWL8K_STA_DB_MODIFY_ENTRY ) ;
if ( rc )
goto out ;
2009-03-10 10:13:33 +01:00
/* Set AID */
2009-07-17 07:21:46 +02:00
rc = mwl8k_cmd_set_aid ( hw , vif ) ;
if ( rc )
goto out ;
2009-03-10 10:13:33 +01:00
/*
* Finalize the join . Tell rx handler to process
* next beacon from our BSSID .
*/
2009-07-16 09:54:27 +02:00
memcpy ( priv - > capture_bssid , mwl8k_vif - > bssid , ETH_ALEN ) ;
2009-03-10 10:13:33 +01:00
priv - > capture_beacon = true ;
} else {
2009-07-17 07:21:46 +02:00
rc = mwl8k_cmd_update_sta_db ( hw , vif , MWL8K_STA_DB_DEL_ENTRY ) ;
2009-03-10 10:13:33 +01:00
memset ( & mwl8k_vif - > bss_info , 0 ,
sizeof ( struct ieee80211_bss_conf ) ) ;
2009-07-16 09:54:27 +02:00
memset ( mwl8k_vif - > bssid , 0 , ETH_ALEN ) ;
2009-03-10 10:13:33 +01:00
}
2009-07-17 07:21:46 +02:00
out :
mwl8k_fw_unlock ( hw ) ;
2009-03-10 10:13:33 +01:00
}
2009-08-18 03:55:42 +02:00
static u64 mwl8k_prepare_multicast ( struct ieee80211_hw * hw ,
int mc_count , struct dev_addr_list * mclist )
{
struct mwl8k_cmd_pkt * cmd ;
cmd = __mwl8k_cmd_mac_multicast_adr ( hw , mc_count , mclist ) ;
return ( unsigned long ) cmd ;
}
2009-08-18 04:06:20 +02:00
static void mwl8k_configure_filter ( struct ieee80211_hw * hw ,
unsigned int changed_flags ,
unsigned int * total_flags ,
u64 multicast )
{
struct mwl8k_priv * priv = hw - > priv ;
2009-08-18 03:55:42 +02:00
struct mwl8k_cmd_pkt * multicast_adr_cmd ;
2009-03-10 10:13:33 +01:00
2009-08-18 04:06:20 +02:00
/* Clear unsupported feature flags */
* total_flags & = FIF_BCN_PRBRESP_PROMISC ;
2009-03-10 10:13:33 +01:00
2009-08-18 04:06:20 +02:00
if ( mwl8k_fw_lock ( hw ) )
return ;
2009-03-10 10:13:33 +01:00
2009-08-18 04:06:20 +02:00
if ( changed_flags & FIF_BCN_PRBRESP_PROMISC ) {
if ( * total_flags & FIF_BCN_PRBRESP_PROMISC )
mwl8k_cmd_set_pre_scan ( hw ) ;
2009-03-10 10:13:33 +01:00
else {
2009-08-03 21:58:57 +02:00
u8 * bssid ;
bssid = " \x00 \x00 \x00 \x00 \x00 \x00 " ;
if ( priv - > vif ! = NULL )
bssid = MWL8K_VIF ( priv - > vif ) - > bssid ;
2009-08-18 04:06:20 +02:00
mwl8k_cmd_set_post_scan ( hw , bssid ) ;
2009-03-10 10:13:33 +01:00
}
}
2009-08-18 04:06:20 +02:00
multicast_adr_cmd = ( void * ) ( unsigned long ) multicast ;
if ( multicast_adr_cmd ! = NULL ) {
mwl8k_post_cmd ( hw , multicast_adr_cmd ) ;
kfree ( multicast_adr_cmd ) ;
}
2009-03-10 10:13:33 +01:00
2009-08-18 04:06:20 +02:00
mwl8k_fw_unlock ( hw ) ;
2009-03-10 10:13:33 +01:00
}
static int mwl8k_set_rts_threshold ( struct ieee80211_hw * hw , u32 value )
{
2009-07-17 07:24:15 +02:00
return mwl8k_rts_threshold ( hw , MWL8K_CMD_SET , value ) ;
2009-03-10 10:13:33 +01:00
}
static int mwl8k_conf_tx ( struct ieee80211_hw * hw , u16 queue ,
const struct ieee80211_tx_queue_params * params )
{
2009-07-17 07:25:59 +02:00
struct mwl8k_priv * priv = hw - > priv ;
2009-03-10 10:13:33 +01:00
int rc ;
2009-07-17 07:25:59 +02:00
rc = mwl8k_fw_lock ( hw ) ;
if ( ! rc ) {
if ( ! priv - > wmm_enabled )
rc = mwl8k_set_wmm ( hw , 1 ) ;
2009-03-10 10:13:33 +01:00
2009-07-17 07:25:59 +02:00
if ( ! rc )
rc = mwl8k_set_edca_params ( hw , queue ,
params - > cw_min ,
params - > cw_max ,
params - > aifs ,
params - > txop ) ;
mwl8k_fw_unlock ( hw ) ;
2009-03-10 10:13:33 +01:00
}
2009-07-17 07:25:59 +02:00
2009-03-10 10:13:33 +01:00
return rc ;
}
static int mwl8k_get_tx_stats ( struct ieee80211_hw * hw ,
struct ieee80211_tx_queue_stats * stats )
{
struct mwl8k_priv * priv = hw - > priv ;
struct mwl8k_tx_queue * txq ;
int index ;
spin_lock_bh ( & priv - > tx_lock ) ;
for ( index = 0 ; index < MWL8K_TX_QUEUES ; index + + ) {
txq = priv - > txq + index ;
memcpy ( & stats [ index ] , & txq - > tx_stats ,
sizeof ( struct ieee80211_tx_queue_stats ) ) ;
}
spin_unlock_bh ( & priv - > tx_lock ) ;
2009-07-17 07:26:27 +02:00
return 0 ;
2009-03-10 10:13:33 +01:00
}
static int mwl8k_get_stats ( struct ieee80211_hw * hw ,
struct ieee80211_low_level_stats * stats )
{
2009-07-17 07:26:27 +02:00
return mwl8k_cmd_802_11_get_stat ( hw , stats ) ;
2009-03-10 10:13:33 +01:00
}
static const struct ieee80211_ops mwl8k_ops = {
. tx = mwl8k_tx ,
. start = mwl8k_start ,
. stop = mwl8k_stop ,
. add_interface = mwl8k_add_interface ,
. remove_interface = mwl8k_remove_interface ,
. config = mwl8k_config ,
. bss_info_changed = mwl8k_bss_info_changed ,
2009-08-17 16:16:53 +02:00
. prepare_multicast = mwl8k_prepare_multicast ,
2009-03-10 10:13:33 +01:00
. configure_filter = mwl8k_configure_filter ,
. set_rts_threshold = mwl8k_set_rts_threshold ,
. conf_tx = mwl8k_conf_tx ,
. get_tx_stats = mwl8k_get_tx_stats ,
. get_stats = mwl8k_get_stats ,
} ;
static void mwl8k_tx_reclaim_handler ( unsigned long data )
{
int i ;
struct ieee80211_hw * hw = ( struct ieee80211_hw * ) data ;
struct mwl8k_priv * priv = hw - > priv ;
spin_lock_bh ( & priv - > tx_lock ) ;
for ( i = 0 ; i < MWL8K_TX_QUEUES ; i + + )
mwl8k_txq_reclaim ( hw , i , 0 ) ;
2009-07-16 14:00:45 +02:00
if ( priv - > tx_wait ! = NULL & & mwl8k_txq_busy ( priv ) = = 0 ) {
complete ( priv - > tx_wait ) ;
priv - > tx_wait = NULL ;
2009-03-10 10:13:33 +01:00
}
spin_unlock_bh ( & priv - > tx_lock ) ;
}
static void mwl8k_finalize_join_worker ( struct work_struct * work )
{
struct mwl8k_priv * priv =
container_of ( work , struct mwl8k_priv , finalize_join_worker ) ;
struct sk_buff * skb = priv - > beacon_skb ;
2009-07-16 14:00:45 +02:00
u8 dtim = MWL8K_VIF ( priv - > vif ) - > bss_info . dtim_period ;
2009-03-10 10:13:33 +01:00
mwl8k_finalize_join ( priv - > hw , skb - > data , skb - > len , dtim ) ;
dev_kfree_skb ( skb ) ;
priv - > beacon_skb = NULL ;
}
static int __devinit mwl8k_probe ( struct pci_dev * pdev ,
const struct pci_device_id * id )
{
struct ieee80211_hw * hw ;
struct mwl8k_priv * priv ;
int rc ;
int i ;
u8 * fw ;
rc = pci_enable_device ( pdev ) ;
if ( rc ) {
printk ( KERN_ERR " %s: Cannot enable new PCI device \n " ,
MWL8K_NAME ) ;
return rc ;
}
rc = pci_request_regions ( pdev , MWL8K_NAME ) ;
if ( rc ) {
printk ( KERN_ERR " %s: Cannot obtain PCI resources \n " ,
MWL8K_NAME ) ;
return rc ;
}
pci_set_master ( pdev ) ;
hw = ieee80211_alloc_hw ( sizeof ( * priv ) , & mwl8k_ops ) ;
if ( hw = = NULL ) {
printk ( KERN_ERR " %s: ieee80211 alloc failed \n " , MWL8K_NAME ) ;
rc = - ENOMEM ;
goto err_free_reg ;
}
priv = hw - > priv ;
priv - > hw = hw ;
priv - > pdev = pdev ;
2009-07-16 12:34:02 +02:00
priv - > wmm_enabled = false ;
2009-03-10 10:13:33 +01:00
priv - > pending_tx_pkts = 0 ;
strncpy ( priv - > name , MWL8K_NAME , sizeof ( priv - > name ) ) ;
SET_IEEE80211_DEV ( hw , & pdev - > dev ) ;
pci_set_drvdata ( pdev , hw ) ;
priv - > regs = pci_iomap ( pdev , 1 , 0x10000 ) ;
if ( priv - > regs = = NULL ) {
printk ( KERN_ERR " %s: Cannot map device memory \n " , priv - > name ) ;
goto err_iounmap ;
}
memcpy ( priv - > channels , mwl8k_channels , sizeof ( mwl8k_channels ) ) ;
priv - > band . band = IEEE80211_BAND_2GHZ ;
priv - > band . channels = priv - > channels ;
priv - > band . n_channels = ARRAY_SIZE ( mwl8k_channels ) ;
priv - > band . bitrates = priv - > rates ;
priv - > band . n_bitrates = ARRAY_SIZE ( mwl8k_rates ) ;
hw - > wiphy - > bands [ IEEE80211_BAND_2GHZ ] = & priv - > band ;
BUILD_BUG_ON ( sizeof ( priv - > rates ) ! = sizeof ( mwl8k_rates ) ) ;
memcpy ( priv - > rates , mwl8k_rates , sizeof ( mwl8k_rates ) ) ;
/*
* Extra headroom is the size of the required DMA header
* minus the size of the smallest 802.11 frame ( CTS frame ) .
*/
hw - > extra_tx_headroom =
sizeof ( struct mwl8k_dma_data ) - sizeof ( struct ieee80211_cts ) ;
hw - > channel_change_time = 10 ;
hw - > queues = MWL8K_TX_QUEUES ;
2009-07-16 14:15:44 +02:00
hw - > wiphy - > interface_modes = BIT ( NL80211_IFTYPE_STATION ) ;
2009-03-10 10:13:33 +01:00
/* Set rssi and noise values to dBm */
2009-07-16 14:00:45 +02:00
hw - > flags | = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM ;
2009-03-10 10:13:33 +01:00
hw - > vif_data_size = sizeof ( struct mwl8k_vif ) ;
priv - > vif = NULL ;
/* Set default radio state and preamble */
2009-07-16 12:14:58 +02:00
priv - > radio_on = 0 ;
2009-07-16 12:26:57 +02:00
priv - > radio_short_preamble = 0 ;
2009-03-10 10:13:33 +01:00
/* Finalize join worker */
INIT_WORK ( & priv - > finalize_join_worker , mwl8k_finalize_join_worker ) ;
/* TX reclaim tasklet */
tasklet_init ( & priv - > tx_reclaim_task ,
mwl8k_tx_reclaim_handler , ( unsigned long ) hw ) ;
tasklet_disable ( & priv - > tx_reclaim_task ) ;
/* Config workthread */
priv - > config_wq = create_singlethread_workqueue ( " mwl8k_config " ) ;
if ( priv - > config_wq = = NULL )
goto err_iounmap ;
/* Power management cookie */
priv - > cookie = pci_alloc_consistent ( priv - > pdev , 4 , & priv - > cookie_dma ) ;
if ( priv - > cookie = = NULL )
goto err_iounmap ;
rc = mwl8k_rxq_init ( hw , 0 ) ;
if ( rc )
goto err_iounmap ;
rxq_refill ( hw , 0 , INT_MAX ) ;
2009-08-18 03:18:01 +02:00
mutex_init ( & priv - > fw_mutex ) ;
priv - > fw_mutex_owner = NULL ;
priv - > fw_mutex_depth = 0 ;
priv - > tx_wait = NULL ;
priv - > hostcmd_wait = NULL ;
2009-03-10 10:13:33 +01:00
spin_lock_init ( & priv - > tx_lock ) ;
for ( i = 0 ; i < MWL8K_TX_QUEUES ; i + + ) {
rc = mwl8k_txq_init ( hw , i ) ;
if ( rc )
goto err_free_queues ;
}
iowrite32 ( 0 , priv - > regs + MWL8K_HIU_A2H_INTERRUPT_STATUS ) ;
2009-07-16 13:49:55 +02:00
iowrite32 ( 0 , priv - > regs + MWL8K_HIU_A2H_INTERRUPT_MASK ) ;
2009-03-10 10:13:33 +01:00
iowrite32 ( 0 , priv - > regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL ) ;
iowrite32 ( 0xffffffff , priv - > regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK ) ;
rc = request_irq ( priv - > pdev - > irq , & mwl8k_interrupt ,
IRQF_SHARED , MWL8K_NAME , hw ) ;
if ( rc ) {
printk ( KERN_ERR " %s: failed to register IRQ handler \n " ,
priv - > name ) ;
goto err_free_queues ;
}
/* Reset firmware and hardware */
mwl8k_hw_reset ( priv ) ;
/* Ask userland hotplug daemon for the device firmware */
rc = mwl8k_request_firmware ( priv , ( u32 ) id - > driver_data ) ;
if ( rc ) {
printk ( KERN_ERR " %s: Firmware files not found \n " , priv - > name ) ;
goto err_free_irq ;
}
/* Load firmware into hardware */
rc = mwl8k_load_firmware ( priv ) ;
if ( rc ) {
printk ( KERN_ERR " %s: Cannot start firmware \n " , priv - > name ) ;
goto err_stop_firmware ;
}
/* Reclaim memory once firmware is successfully loaded */
mwl8k_release_firmware ( priv ) ;
/*
* Temporarily enable interrupts . Initial firmware host
* commands use interrupts and avoids polling . Disable
* interrupts when done .
*/
2009-07-16 13:49:55 +02:00
iowrite32 ( MWL8K_A2H_EVENTS , priv - > regs + MWL8K_HIU_A2H_INTERRUPT_MASK ) ;
2009-03-10 10:13:33 +01:00
/* Get config data, mac addrs etc */
rc = mwl8k_cmd_get_hw_spec ( hw ) ;
if ( rc ) {
printk ( KERN_ERR " %s: Cannot initialise firmware \n " , priv - > name ) ;
goto err_stop_firmware ;
}
/* Turn radio off */
2009-07-16 12:14:58 +02:00
rc = mwl8k_cmd_802_11_radio_disable ( hw ) ;
2009-03-10 10:13:33 +01:00
if ( rc ) {
printk ( KERN_ERR " %s: Cannot disable \n " , priv - > name ) ;
goto err_stop_firmware ;
}
/* Disable interrupts */
iowrite32 ( 0 , priv - > regs + MWL8K_HIU_A2H_INTERRUPT_MASK ) ;
free_irq ( priv - > pdev - > irq , hw ) ;
rc = ieee80211_register_hw ( hw ) ;
if ( rc ) {
printk ( KERN_ERR " %s: Cannot register device \n " , priv - > name ) ;
goto err_stop_firmware ;
}
fw = ( u8 * ) & priv - > fw_rev ;
printk ( KERN_INFO " %s: 88W%u %s \n " , priv - > name , priv - > part_num ,
MWL8K_DESC ) ;
printk ( KERN_INFO " %s: Driver Ver:%s Firmware Ver:%u.%u.%u.%u \n " ,
priv - > name , MWL8K_VERSION , fw [ 3 ] , fw [ 2 ] , fw [ 1 ] , fw [ 0 ] ) ;
2009-07-15 17:21:41 +02:00
printk ( KERN_INFO " %s: MAC Address: %pM \n " , priv - > name ,
hw - > wiphy - > perm_addr ) ;
2009-03-10 10:13:33 +01:00
return 0 ;
err_stop_firmware :
mwl8k_hw_reset ( priv ) ;
mwl8k_release_firmware ( priv ) ;
err_free_irq :
iowrite32 ( 0 , priv - > regs + MWL8K_HIU_A2H_INTERRUPT_MASK ) ;
free_irq ( priv - > pdev - > irq , hw ) ;
err_free_queues :
for ( i = 0 ; i < MWL8K_TX_QUEUES ; i + + )
mwl8k_txq_deinit ( hw , i ) ;
mwl8k_rxq_deinit ( hw , 0 ) ;
err_iounmap :
if ( priv - > cookie ! = NULL )
pci_free_consistent ( priv - > pdev , 4 ,
priv - > cookie , priv - > cookie_dma ) ;
if ( priv - > regs ! = NULL )
pci_iounmap ( pdev , priv - > regs ) ;
if ( priv - > config_wq ! = NULL )
destroy_workqueue ( priv - > config_wq ) ;
pci_set_drvdata ( pdev , NULL ) ;
ieee80211_free_hw ( hw ) ;
err_free_reg :
pci_release_regions ( pdev ) ;
pci_disable_device ( pdev ) ;
return rc ;
}
2009-04-18 02:10:45 +02:00
static void __devexit mwl8k_shutdown ( struct pci_dev * pdev )
2009-03-10 10:13:33 +01:00
{
printk ( KERN_ERR " ===>%s(%u) \n " , __func__ , __LINE__ ) ;
}
2009-04-18 02:10:45 +02:00
static void __devexit mwl8k_remove ( struct pci_dev * pdev )
2009-03-10 10:13:33 +01:00
{
struct ieee80211_hw * hw = pci_get_drvdata ( pdev ) ;
struct mwl8k_priv * priv ;
int i ;
if ( hw = = NULL )
return ;
priv = hw - > priv ;
ieee80211_stop_queues ( hw ) ;
2009-08-03 21:59:09 +02:00
ieee80211_unregister_hw ( hw ) ;
2009-03-10 10:13:33 +01:00
/* Remove tx reclaim tasklet */
tasklet_kill ( & priv - > tx_reclaim_task ) ;
/* Stop config thread */
destroy_workqueue ( priv - > config_wq ) ;
/* Stop hardware */
mwl8k_hw_reset ( priv ) ;
/* Return all skbs to mac80211 */
for ( i = 0 ; i < MWL8K_TX_QUEUES ; i + + )
mwl8k_txq_reclaim ( hw , i , 1 ) ;
for ( i = 0 ; i < MWL8K_TX_QUEUES ; i + + )
mwl8k_txq_deinit ( hw , i ) ;
mwl8k_rxq_deinit ( hw , 0 ) ;
pci_free_consistent ( priv - > pdev , 4 ,
priv - > cookie , priv - > cookie_dma ) ;
pci_iounmap ( pdev , priv - > regs ) ;
pci_set_drvdata ( pdev , NULL ) ;
ieee80211_free_hw ( hw ) ;
pci_release_regions ( pdev ) ;
pci_disable_device ( pdev ) ;
}
static struct pci_driver mwl8k_driver = {
. name = MWL8K_NAME ,
. id_table = mwl8k_table ,
. probe = mwl8k_probe ,
. remove = __devexit_p ( mwl8k_remove ) ,
. shutdown = __devexit_p ( mwl8k_shutdown ) ,
} ;
static int __init mwl8k_init ( void )
{
return pci_register_driver ( & mwl8k_driver ) ;
}
static void __exit mwl8k_exit ( void )
{
pci_unregister_driver ( & mwl8k_driver ) ;
}
module_init ( mwl8k_init ) ;
module_exit ( mwl8k_exit ) ;