2008-08-04 00:16:41 -07:00
/*
* Copyright ( c ) 2008 Atheros Communications 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 .
*/
/* mac80211 and PCI callbacks */
# include <linux/nl80211.h>
# include "core.h"
# define ATH_PCI_VERSION "0.1"
# define IEEE80211_HTCAP_MAXRXAMPDU_FACTOR 13
static char * dev_info = " ath9k " ;
MODULE_AUTHOR ( " Atheros Communications " ) ;
MODULE_DESCRIPTION ( " Support for Atheros 802.11n wireless LAN cards. " ) ;
MODULE_SUPPORTED_DEVICE ( " Atheros 802.11n WLAN cards " ) ;
MODULE_LICENSE ( " Dual BSD/GPL " ) ;
static struct pci_device_id ath_pci_id_table [ ] __devinitdata = {
{ PCI_VDEVICE ( ATHEROS , 0x0023 ) } , /* PCI */
{ PCI_VDEVICE ( ATHEROS , 0x0024 ) } , /* PCI-E */
{ PCI_VDEVICE ( ATHEROS , 0x0027 ) } , /* PCI */
{ PCI_VDEVICE ( ATHEROS , 0x0029 ) } , /* PCI */
{ PCI_VDEVICE ( ATHEROS , 0x002A ) } , /* PCI-E */
{ 0 }
} ;
static int ath_get_channel ( struct ath_softc * sc ,
struct ieee80211_channel * chan )
{
int i ;
for ( i = 0 ; i < sc - > sc_ah - > ah_nchan ; i + + ) {
if ( sc - > sc_ah - > ah_channels [ i ] . channel = = chan - > center_freq )
return i ;
}
return - 1 ;
}
static u32 ath_get_extchanmode ( struct ath_softc * sc ,
struct ieee80211_channel * chan )
{
u32 chanmode = 0 ;
u8 ext_chan_offset = sc - > sc_ht_info . ext_chan_offset ;
enum ath9k_ht_macmode tx_chan_width = sc - > sc_ht_info . tx_chan_width ;
switch ( chan - > band ) {
case IEEE80211_BAND_2GHZ :
2008-10-09 12:13:49 +02:00
if ( ( ext_chan_offset = = IEEE80211_HT_PARAM_CHA_SEC_NONE ) & &
2008-08-04 00:16:41 -07:00
( tx_chan_width = = ATH9K_HT_MACMODE_20 ) )
chanmode = CHANNEL_G_HT20 ;
2008-10-09 12:13:49 +02:00
if ( ( ext_chan_offset = = IEEE80211_HT_PARAM_CHA_SEC_ABOVE ) & &
2008-08-04 00:16:41 -07:00
( tx_chan_width = = ATH9K_HT_MACMODE_2040 ) )
chanmode = CHANNEL_G_HT40PLUS ;
2008-10-09 12:13:49 +02:00
if ( ( ext_chan_offset = = IEEE80211_HT_PARAM_CHA_SEC_BELOW ) & &
2008-08-04 00:16:41 -07:00
( tx_chan_width = = ATH9K_HT_MACMODE_2040 ) )
chanmode = CHANNEL_G_HT40MINUS ;
break ;
case IEEE80211_BAND_5GHZ :
2008-10-09 12:13:49 +02:00
if ( ( ext_chan_offset = = IEEE80211_HT_PARAM_CHA_SEC_NONE ) & &
2008-08-04 00:16:41 -07:00
( tx_chan_width = = ATH9K_HT_MACMODE_20 ) )
chanmode = CHANNEL_A_HT20 ;
2008-10-09 12:13:49 +02:00
if ( ( ext_chan_offset = = IEEE80211_HT_PARAM_CHA_SEC_ABOVE ) & &
2008-08-04 00:16:41 -07:00
( tx_chan_width = = ATH9K_HT_MACMODE_2040 ) )
chanmode = CHANNEL_A_HT40PLUS ;
2008-10-09 12:13:49 +02:00
if ( ( ext_chan_offset = = IEEE80211_HT_PARAM_CHA_SEC_BELOW ) & &
2008-08-04 00:16:41 -07:00
( tx_chan_width = = ATH9K_HT_MACMODE_2040 ) )
chanmode = CHANNEL_A_HT40MINUS ;
break ;
default :
break ;
}
return chanmode ;
}
static int ath_setkey_tkip ( struct ath_softc * sc ,
struct ieee80211_key_conf * key ,
struct ath9k_keyval * hk ,
const u8 * addr )
{
u8 * key_rxmic = NULL ;
u8 * key_txmic = NULL ;
key_txmic = key - > key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY ;
key_rxmic = key - > key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY ;
if ( addr = = NULL ) {
/* Group key installation */
memcpy ( hk - > kv_mic , key_rxmic , sizeof ( hk - > kv_mic ) ) ;
return ath_keyset ( sc , key - > keyidx , hk , addr ) ;
}
if ( ! sc - > sc_splitmic ) {
/*
* data key goes at first index ,
* the hal handles the MIC keys at index + 64.
*/
memcpy ( hk - > kv_mic , key_rxmic , sizeof ( hk - > kv_mic ) ) ;
memcpy ( hk - > kv_txmic , key_txmic , sizeof ( hk - > kv_txmic ) ) ;
return ath_keyset ( sc , key - > keyidx , hk , addr ) ;
}
/*
* TX key goes at first index , RX key at + 32.
* The hal handles the MIC keys at index + 64.
*/
memcpy ( hk - > kv_mic , key_txmic , sizeof ( hk - > kv_mic ) ) ;
if ( ! ath_keyset ( sc , key - > keyidx , hk , NULL ) ) {
/* Txmic entry failed. No need to proceed further */
DPRINTF ( sc , ATH_DBG_KEYCACHE ,
" %s Setting TX MIC Key Failed \n " , __func__ ) ;
return 0 ;
}
memcpy ( hk - > kv_mic , key_rxmic , sizeof ( hk - > kv_mic ) ) ;
/* XXX delete tx key on failure? */
return ath_keyset ( sc , key - > keyidx + 32 , hk , addr ) ;
}
static int ath_key_config ( struct ath_softc * sc ,
const u8 * addr ,
struct ieee80211_key_conf * key )
{
struct ieee80211_vif * vif ;
struct ath9k_keyval hk ;
const u8 * mac = NULL ;
int ret = 0 ;
2008-09-11 00:01:58 +02:00
enum nl80211_iftype opmode ;
2008-08-04 00:16:41 -07:00
memset ( & hk , 0 , sizeof ( hk ) ) ;
switch ( key - > alg ) {
case ALG_WEP :
hk . kv_type = ATH9K_CIPHER_WEP ;
break ;
case ALG_TKIP :
hk . kv_type = ATH9K_CIPHER_TKIP ;
break ;
case ALG_CCMP :
hk . kv_type = ATH9K_CIPHER_AES_CCM ;
break ;
default :
return - EINVAL ;
}
hk . kv_len = key - > keylen ;
memcpy ( hk . kv_val , key - > key , key - > keylen ) ;
if ( ! sc - > sc_vaps [ 0 ] )
return - EIO ;
vif = sc - > sc_vaps [ 0 ] - > av_if_data ;
opmode = vif - > type ;
/*
* Strategy :
* For _M_STA mc tx , we will not setup a key at all since we never
* tx mc .
* _M_STA mc rx , we will use the keyID .
* for _M_IBSS mc tx , we will use the keyID , and no macaddr .
* for _M_IBSS mc rx , we will alloc a slot and plumb the mac of the
* peer node . BUT we will plumb a cleartext key so that we can do
* perSta default key table lookup in software .
*/
if ( is_broadcast_ether_addr ( addr ) ) {
switch ( opmode ) {
2008-09-11 00:01:58 +02:00
case NL80211_IFTYPE_STATION :
2008-08-04 00:16:41 -07:00
/* default key: could be group WPA key
* or could be static WEP key */
mac = NULL ;
break ;
2008-09-11 00:01:58 +02:00
case NL80211_IFTYPE_ADHOC :
2008-08-04 00:16:41 -07:00
break ;
2008-09-11 00:01:58 +02:00
case NL80211_IFTYPE_AP :
2008-08-04 00:16:41 -07:00
break ;
default :
ASSERT ( 0 ) ;
break ;
}
} else {
mac = addr ;
}
if ( key - > alg = = ALG_TKIP )
ret = ath_setkey_tkip ( sc , key , & hk , mac ) ;
else
ret = ath_keyset ( sc , key - > keyidx , & hk , mac ) ;
if ( ! ret )
return - EIO ;
return 0 ;
}
static void ath_key_delete ( struct ath_softc * sc , struct ieee80211_key_conf * key )
{
int freeslot ;
2008-08-14 13:27:16 +05:30
freeslot = ( key - > keyidx > = 4 ) ? 1 : 0 ;
2008-08-04 00:16:41 -07:00
ath_key_reset ( sc , key - > keyidx , freeslot ) ;
}
2008-10-09 12:13:49 +02:00
static void setup_ht_cap ( struct ieee80211_sta_ht_cap * ht_info )
2008-08-04 00:16:41 -07:00
{
2008-08-14 13:28:02 +05:30
# define ATH9K_HT_CAP_MAXRXAMPDU_65536 0x3 /* 2 ^ 16 */
# define ATH9K_HT_CAP_MPDUDENSITY_8 0x6 /* 8 usec */
2008-08-04 00:16:41 -07:00
2008-10-09 12:13:49 +02:00
ht_info - > ht_supported = true ;
ht_info - > cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
IEEE80211_HT_CAP_SM_PS |
IEEE80211_HT_CAP_SGI_40 |
IEEE80211_HT_CAP_DSSSCCK40 ;
2008-08-04 00:16:41 -07:00
2008-08-14 13:28:02 +05:30
ht_info - > ampdu_factor = ATH9K_HT_CAP_MAXRXAMPDU_65536 ;
ht_info - > ampdu_density = ATH9K_HT_CAP_MPDUDENSITY_8 ;
2008-10-09 12:13:49 +02:00
/* set up supported mcs set */
memset ( & ht_info - > mcs , 0 , sizeof ( ht_info - > mcs ) ) ;
ht_info - > mcs . rx_mask [ 0 ] = 0xff ;
ht_info - > mcs . rx_mask [ 1 ] = 0xff ;
ht_info - > mcs . tx_params = IEEE80211_HT_MCS_TX_DEFINED ;
2008-08-04 00:16:41 -07:00
}
static int ath_rate2idx ( struct ath_softc * sc , int rate )
{
int i = 0 , cur_band , n_rates ;
struct ieee80211_hw * hw = sc - > hw ;
cur_band = hw - > conf . channel - > band ;
n_rates = sc - > sbands [ cur_band ] . n_bitrates ;
for ( i = 0 ; i < n_rates ; i + + ) {
if ( sc - > sbands [ cur_band ] . bitrates [ i ] . bitrate = = rate )
break ;
}
/*
* NB : mac80211 validates rx rate index against the supported legacy rate
* index only ( should be done against ht rates also ) , return the highest
* legacy rate index for rx rate which does not match any one of the
* supported basic and extended rates to make mac80211 happy .
* The following hack will be cleaned up once the issue with
* the rx rate index validation in mac80211 is fixed .
*/
if ( i = = n_rates )
return n_rates - 1 ;
return i ;
}
static void ath9k_rx_prepare ( struct ath_softc * sc ,
struct sk_buff * skb ,
struct ath_recv_status * status ,
struct ieee80211_rx_status * rx_status )
{
struct ieee80211_hw * hw = sc - > hw ;
struct ieee80211_channel * curchan = hw - > conf . channel ;
memset ( rx_status , 0 , sizeof ( struct ieee80211_rx_status ) ) ;
rx_status - > mactime = status - > tsf ;
rx_status - > band = curchan - > band ;
rx_status - > freq = curchan - > center_freq ;
2008-10-03 15:45:27 -07:00
rx_status - > noise = sc - > sc_ani . sc_noise_floor ;
2008-08-04 00:16:41 -07:00
rx_status - > signal = rx_status - > noise + status - > rssi ;
rx_status - > rate_idx = ath_rate2idx ( sc , ( status - > rateKbps / 100 ) ) ;
rx_status - > antenna = status - > antenna ;
2008-10-03 15:45:27 -07:00
2008-10-13 14:08:09 -07:00
/* at 45 you will be able to use MCS 15 reliably. A more elaborate
* scheme can be used here but it requires tables of SNR / throughput for
* each possible mode used . */
rx_status - > qual = status - > rssi * 100 / 45 ;
/* rssi can be more than 45 though, anything above that
* should be considered at 100 % */
if ( rx_status - > qual > 100 )
rx_status - > qual = 100 ;
2008-08-04 00:16:41 -07:00
if ( status - > flags & ATH_RX_MIC_ERROR )
rx_status - > flag | = RX_FLAG_MMIC_ERROR ;
if ( status - > flags & ATH_RX_FCS_ERROR )
rx_status - > flag | = RX_FLAG_FAILED_FCS_CRC ;
rx_status - > flag | = RX_FLAG_TSFT ;
}
static u8 parse_mpdudensity ( u8 mpdudensity )
{
/*
* 802.11 n D2 .0 defined values for " Minimum MPDU Start Spacing " :
* 0 for no restriction
* 1 for 1 / 4 us
* 2 for 1 / 2 us
* 3 for 1 us
* 4 for 2 us
* 5 for 4 us
* 6 for 8 us
* 7 for 16 us
*/
switch ( mpdudensity ) {
case 0 :
return 0 ;
case 1 :
case 2 :
case 3 :
/* Our lower layer calculations limit our precision to
1 microsecond */
return 1 ;
case 4 :
return 2 ;
case 5 :
return 4 ;
case 6 :
return 8 ;
case 7 :
return 16 ;
default :
return 0 ;
}
}
2008-09-10 18:49:27 +05:30
static void ath9k_ht_conf ( struct ath_softc * sc ,
struct ieee80211_bss_conf * bss_conf )
2008-08-04 00:16:41 -07:00
{
2008-09-10 18:49:27 +05:30
struct ath_ht_info * ht_info = & sc - > sc_ht_info ;
2008-08-04 00:16:41 -07:00
2008-10-14 16:58:37 +02:00
if ( sc - > hw - > conf . ht . enabled ) {
ht_info - > ext_chan_offset = bss_conf - > ht . secondary_channel_offset ;
if ( bss_conf - > ht . width_40_ok )
2008-09-10 18:49:27 +05:30
ht_info - > tx_chan_width = ATH9K_HT_MACMODE_2040 ;
else
ht_info - > tx_chan_width = ATH9K_HT_MACMODE_20 ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
ath9k_hw_set11nmac2040 ( sc - > sc_ah , ht_info - > tx_chan_width ) ;
2008-08-04 00:16:41 -07:00
}
}
2008-09-10 18:49:27 +05:30
static void ath9k_bss_assoc_info ( struct ath_softc * sc ,
struct ieee80211_bss_conf * bss_conf )
2008-08-04 00:16:41 -07:00
{
2008-09-10 18:49:27 +05:30
struct ieee80211_hw * hw = sc - > hw ;
struct ieee80211_channel * curchan = hw - > conf . channel ;
struct ath_vap * avp ;
int pos ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
if ( bss_conf - > assoc ) {
DPRINTF ( sc , ATH_DBG_CONFIG , " %s: Bss Info ASSOC %d \n " ,
__func__ ,
bss_conf - > aid ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
avp = sc - > sc_vaps [ 0 ] ;
if ( avp = = NULL ) {
DPRINTF ( sc , ATH_DBG_FATAL , " %s: Invalid interface \n " ,
__func__ ) ;
return ;
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/* New association, store aid */
if ( avp - > av_opmode = = ATH9K_M_STA ) {
sc - > sc_curaid = bss_conf - > aid ;
ath9k_hw_write_associd ( sc - > sc_ah , sc - > sc_curbssid ,
sc - > sc_curaid ) ;
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/* Configure the beacon */
ath_beacon_config ( sc , 0 ) ;
sc - > sc_flags | = SC_OP_BEACONS ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/* Reset rssi stats */
sc - > sc_halstats . ns_avgbrssi = ATH_RSSI_DUMMY_MARKER ;
sc - > sc_halstats . ns_avgrssi = ATH_RSSI_DUMMY_MARKER ;
sc - > sc_halstats . ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER ;
sc - > sc_halstats . ns_avgtxrate = ATH_RATE_DUMMY_MARKER ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/* Update chainmask */
2008-10-14 16:58:37 +02:00
ath_update_chainmask ( sc , hw - > conf . ht . enabled ) ;
2008-08-04 00:16:41 -07:00
DPRINTF ( sc , ATH_DBG_CONFIG ,
2008-10-27 15:59:26 -07:00
" %s: bssid %pM aid 0x%x \n " ,
2008-09-10 18:49:27 +05:30
__func__ ,
2008-10-27 15:59:26 -07:00
sc - > sc_curbssid , sc - > sc_curaid ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
DPRINTF ( sc , ATH_DBG_CONFIG , " %s: Set channel: %d MHz \n " ,
__func__ ,
curchan - > center_freq ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
pos = ath_get_channel ( sc , curchan ) ;
if ( pos = = - 1 ) {
DPRINTF ( sc , ATH_DBG_FATAL ,
" %s: Invalid channel \n " , __func__ ) ;
return ;
}
2008-08-04 00:16:41 -07:00
2008-10-14 16:58:37 +02:00
if ( hw - > conf . ht . enabled )
2008-09-10 18:49:27 +05:30
sc - > sc_ah - > ah_channels [ pos ] . chanmode =
ath_get_extchanmode ( sc , curchan ) ;
else
sc - > sc_ah - > ah_channels [ pos ] . chanmode =
( curchan - > band = = IEEE80211_BAND_2GHZ ) ?
CHANNEL_G : CHANNEL_A ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/* set h/w channel */
if ( ath_set_channel ( sc , & sc - > sc_ah - > ah_channels [ pos ] ) < 0 )
DPRINTF ( sc , ATH_DBG_FATAL ,
" %s: Unable to set channel \n " ,
__func__ ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
ath_rate_newstate ( sc , avp ) ;
/* Update ratectrl about the new state */
ath_rc_node_update ( hw , avp - > rc_node ) ;
2008-10-03 15:45:27 -07:00
/* Start ANI */
mod_timer ( & sc - > sc_ani . timer ,
jiffies + msecs_to_jiffies ( ATH_ANI_POLLINTERVAL ) ) ;
2008-09-10 18:49:27 +05:30
} else {
DPRINTF ( sc , ATH_DBG_CONFIG ,
" %s: Bss Info DISSOC \n " , __func__ ) ;
sc - > sc_curaid = 0 ;
2008-08-04 00:16:41 -07:00
}
2008-09-10 18:49:27 +05:30
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
void ath_get_beaconconfig ( struct ath_softc * sc ,
int if_id ,
struct ath_beacon_config * conf )
{
struct ieee80211_hw * hw = sc - > hw ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/* fill in beacon config data */
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
conf - > beacon_interval = hw - > conf . beacon_int ;
conf - > listen_interval = 100 ;
conf - > dtim_count = 1 ;
conf - > bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf - > listen_interval ;
2008-08-04 00:16:41 -07:00
}
2008-09-10 18:49:27 +05:30
void ath_tx_complete ( struct ath_softc * sc , struct sk_buff * skb ,
struct ath_xmit_status * tx_status , struct ath_node * an )
2008-08-04 00:16:41 -07:00
{
2008-09-10 18:49:27 +05:30
struct ieee80211_hw * hw = sc - > hw ;
struct ieee80211_tx_info * tx_info = IEEE80211_SKB_CB ( skb ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
DPRINTF ( sc , ATH_DBG_XMIT ,
" %s: TX complete: skb: %p \n " , __func__ , skb ) ;
2008-08-04 00:16:41 -07:00
2008-10-21 12:40:02 +02:00
ieee80211_tx_info_clear_status ( tx_info ) ;
2008-09-10 18:49:27 +05:30
if ( tx_info - > flags & IEEE80211_TX_CTL_NO_ACK | |
tx_info - > flags & IEEE80211_TX_STAT_TX_FILTERED ) {
2008-10-21 12:40:02 +02:00
/* free driver's private data area of tx_info, XXX: HACK! */
if ( tx_info - > control . vif ! = NULL )
kfree ( tx_info - > control . vif ) ;
tx_info - > control . vif = NULL ;
2008-08-04 00:16:41 -07:00
}
2008-09-10 18:49:27 +05:30
if ( tx_status - > flags & ATH_TX_BAR ) {
tx_info - > flags | = IEEE80211_TX_STAT_AMPDU_NO_BACK ;
tx_status - > flags & = ~ ATH_TX_BAR ;
}
2008-08-04 00:16:41 -07:00
2008-10-21 12:40:02 +02:00
if ( ! ( tx_status - > flags & ( ATH_TX_ERROR | ATH_TX_XRETRY ) ) ) {
2008-09-10 18:49:27 +05:30
/* Frame was ACKed */
tx_info - > flags | = IEEE80211_TX_STAT_ACK ;
2008-08-04 00:16:41 -07:00
}
2008-10-21 12:40:02 +02:00
tx_info - > status . rates [ 0 ] . count = tx_status - > retries + 1 ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
ieee80211_tx_status ( hw , skb ) ;
if ( an )
ath_node_put ( sc , an , ATH9K_BH_STATUS_CHANGE ) ;
2008-08-04 00:16:41 -07:00
}
2008-09-10 18:49:27 +05:30
int _ath_rx_indicate ( struct ath_softc * sc ,
struct sk_buff * skb ,
struct ath_recv_status * status ,
u16 keyix )
2008-08-04 00:16:41 -07:00
{
2008-09-10 18:49:27 +05:30
struct ieee80211_hw * hw = sc - > hw ;
struct ath_node * an = NULL ;
struct ieee80211_rx_status rx_status ;
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
int hdrlen = ieee80211_get_hdrlen_from_skb ( skb ) ;
int padsize ;
enum ATH_RX_TYPE st ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/* see if any padding is done by the hw and remove it */
if ( hdrlen & 3 ) {
padsize = hdrlen % 4 ;
memmove ( skb - > data + padsize , skb - > data , hdrlen ) ;
skb_pull ( skb , padsize ) ;
2008-08-04 00:16:41 -07:00
}
2008-09-10 18:49:27 +05:30
/* Prepare rx status */
ath9k_rx_prepare ( sc , skb , status , & rx_status ) ;
2008-08-07 10:54:57 +05:30
2008-09-10 18:49:27 +05:30
if ( ! ( keyix = = ATH9K_RXKEYIX_INVALID ) & &
! ( status - > flags & ATH_RX_DECRYPT_ERROR ) ) {
rx_status . flag | = RX_FLAG_DECRYPTED ;
} else if ( ( le16_to_cpu ( hdr - > frame_control ) & IEEE80211_FCTL_PROTECTED )
& & ! ( status - > flags & ATH_RX_DECRYPT_ERROR )
& & skb - > len > = hdrlen + 4 ) {
keyix = skb - > data [ hdrlen + 3 ] > > 6 ;
2008-08-07 10:54:57 +05:30
2008-09-10 18:49:27 +05:30
if ( test_bit ( keyix , sc - > sc_keymap ) )
rx_status . flag | = RX_FLAG_DECRYPTED ;
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
spin_lock_bh ( & sc - > node_lock ) ;
an = ath_node_find ( sc , hdr - > addr2 ) ;
spin_unlock_bh ( & sc - > node_lock ) ;
if ( an ) {
ath_rx_input ( sc , an ,
skb , status , & st ) ;
}
if ( ! an | | ( st ! = ATH_RX_CONSUMED ) )
__ieee80211_rx ( hw , skb , & rx_status ) ;
2008-08-04 00:16:41 -07:00
return 0 ;
}
2008-09-10 18:49:27 +05:30
int ath_rx_subframe ( struct ath_node * an ,
struct sk_buff * skb ,
struct ath_recv_status * status )
2008-08-04 00:16:41 -07:00
{
2008-09-10 18:49:27 +05:30
struct ath_softc * sc = an - > an_sc ;
struct ieee80211_hw * hw = sc - > hw ;
struct ieee80211_rx_status rx_status ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/* Prepare rx status */
ath9k_rx_prepare ( sc , skb , status , & rx_status ) ;
if ( ! ( status - > flags & ATH_RX_DECRYPT_ERROR ) )
rx_status . flag | = RX_FLAG_DECRYPTED ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
__ieee80211_rx ( hw , skb , & rx_status ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
return 0 ;
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/********************************/
/* LED functions */
/********************************/
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
static void ath_led_brightness ( struct led_classdev * led_cdev ,
enum led_brightness brightness )
{
struct ath_led * led = container_of ( led_cdev , struct ath_led , led_cdev ) ;
struct ath_softc * sc = led - > sc ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
switch ( brightness ) {
case LED_OFF :
if ( led - > led_type = = ATH_LED_ASSOC | |
led - > led_type = = ATH_LED_RADIO )
sc - > sc_flags & = ~ SC_OP_LED_ASSOCIATED ;
ath9k_hw_set_gpio ( sc - > sc_ah , ATH_LED_PIN ,
( led - > led_type = = ATH_LED_RADIO ) ? 1 :
! ! ( sc - > sc_flags & SC_OP_LED_ASSOCIATED ) ) ;
break ;
case LED_FULL :
if ( led - > led_type = = ATH_LED_ASSOC )
sc - > sc_flags | = SC_OP_LED_ASSOCIATED ;
ath9k_hw_set_gpio ( sc - > sc_ah , ATH_LED_PIN , 0 ) ;
break ;
default :
break ;
2008-08-04 00:16:41 -07:00
}
2008-09-10 18:49:27 +05:30
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
static int ath_register_led ( struct ath_softc * sc , struct ath_led * led ,
char * trigger )
{
int ret ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
led - > sc = sc ;
led - > led_cdev . name = led - > name ;
led - > led_cdev . default_trigger = trigger ;
led - > led_cdev . brightness_set = ath_led_brightness ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
ret = led_classdev_register ( wiphy_dev ( sc - > hw - > wiphy ) , & led - > led_cdev ) ;
if ( ret )
DPRINTF ( sc , ATH_DBG_FATAL ,
" Failed to register led:%s " , led - > name ) ;
else
led - > registered = 1 ;
return ret ;
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
static void ath_unregister_led ( struct ath_led * led )
{
if ( led - > registered ) {
led_classdev_unregister ( & led - > led_cdev ) ;
led - > registered = 0 ;
2008-08-04 00:16:41 -07:00
}
}
2008-09-10 18:49:27 +05:30
static void ath_deinit_leds ( struct ath_softc * sc )
2008-08-04 00:16:41 -07:00
{
2008-09-10 18:49:27 +05:30
ath_unregister_led ( & sc - > assoc_led ) ;
sc - > sc_flags & = ~ SC_OP_LED_ASSOCIATED ;
ath_unregister_led ( & sc - > tx_led ) ;
ath_unregister_led ( & sc - > rx_led ) ;
ath_unregister_led ( & sc - > radio_led ) ;
ath9k_hw_set_gpio ( sc - > sc_ah , ATH_LED_PIN , 1 ) ;
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
static void ath_init_leds ( struct ath_softc * sc )
{
char * trigger ;
int ret ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/* Configure gpio 1 for output */
ath9k_hw_cfg_output ( sc - > sc_ah , ATH_LED_PIN ,
AR_GPIO_OUTPUT_MUX_AS_OUTPUT ) ;
/* LED off, active low */
ath9k_hw_set_gpio ( sc - > sc_ah , ATH_LED_PIN , 1 ) ;
2008-08-11 14:03:13 +05:30
2008-09-10 18:49:27 +05:30
trigger = ieee80211_get_radio_led_name ( sc - > hw ) ;
snprintf ( sc - > radio_led . name , sizeof ( sc - > radio_led . name ) ,
" ath9k-%s:radio " , wiphy_name ( sc - > hw - > wiphy ) ) ;
ret = ath_register_led ( sc , & sc - > radio_led , trigger ) ;
sc - > radio_led . led_type = ATH_LED_RADIO ;
if ( ret )
goto fail ;
2008-08-11 14:03:13 +05:30
2008-09-10 18:49:27 +05:30
trigger = ieee80211_get_assoc_led_name ( sc - > hw ) ;
snprintf ( sc - > assoc_led . name , sizeof ( sc - > assoc_led . name ) ,
" ath9k-%s:assoc " , wiphy_name ( sc - > hw - > wiphy ) ) ;
ret = ath_register_led ( sc , & sc - > assoc_led , trigger ) ;
sc - > assoc_led . led_type = ATH_LED_ASSOC ;
if ( ret )
goto fail ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
trigger = ieee80211_get_tx_led_name ( sc - > hw ) ;
snprintf ( sc - > tx_led . name , sizeof ( sc - > tx_led . name ) ,
" ath9k-%s:tx " , wiphy_name ( sc - > hw - > wiphy ) ) ;
ret = ath_register_led ( sc , & sc - > tx_led , trigger ) ;
sc - > tx_led . led_type = ATH_LED_TX ;
if ( ret )
goto fail ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
trigger = ieee80211_get_rx_led_name ( sc - > hw ) ;
snprintf ( sc - > rx_led . name , sizeof ( sc - > rx_led . name ) ,
" ath9k-%s:rx " , wiphy_name ( sc - > hw - > wiphy ) ) ;
ret = ath_register_led ( sc , & sc - > rx_led , trigger ) ;
sc - > rx_led . led_type = ATH_LED_RX ;
if ( ret )
goto fail ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
return ;
fail :
ath_deinit_leds ( sc ) ;
2008-08-04 00:16:41 -07:00
}
2008-09-10 18:50:17 +05:30
# ifdef CONFIG_RFKILL
/*******************/
/* Rfkill */
/*******************/
static void ath_radio_enable ( struct ath_softc * sc )
{
struct ath_hal * ah = sc - > sc_ah ;
int status ;
spin_lock_bh ( & sc - > sc_resetlock ) ;
if ( ! ath9k_hw_reset ( ah , ah - > ah_curchan ,
sc - > sc_ht_info . tx_chan_width ,
sc - > sc_tx_chainmask ,
sc - > sc_rx_chainmask ,
sc - > sc_ht_extprotspacing ,
false , & status ) ) {
DPRINTF ( sc , ATH_DBG_FATAL ,
" %s: unable to reset channel %u (%uMhz) "
" flags 0x%x hal status %u \n " , __func__ ,
ath9k_hw_mhz2ieee ( ah ,
ah - > ah_curchan - > channel ,
ah - > ah_curchan - > channelFlags ) ,
ah - > ah_curchan - > channel ,
ah - > ah_curchan - > channelFlags , status ) ;
}
spin_unlock_bh ( & sc - > sc_resetlock ) ;
ath_update_txpow ( sc ) ;
if ( ath_startrecv ( sc ) ! = 0 ) {
DPRINTF ( sc , ATH_DBG_FATAL ,
" %s: unable to restart recv logic \n " , __func__ ) ;
return ;
}
if ( sc - > sc_flags & SC_OP_BEACONS )
ath_beacon_config ( sc , ATH_IF_ID_ANY ) ; /* restart beacons */
/* Re-Enable interrupts */
ath9k_hw_set_interrupts ( ah , sc - > sc_imask ) ;
/* Enable LED */
ath9k_hw_cfg_output ( ah , ATH_LED_PIN ,
AR_GPIO_OUTPUT_MUX_AS_OUTPUT ) ;
ath9k_hw_set_gpio ( ah , ATH_LED_PIN , 0 ) ;
ieee80211_wake_queues ( sc - > hw ) ;
}
static void ath_radio_disable ( struct ath_softc * sc )
{
struct ath_hal * ah = sc - > sc_ah ;
int status ;
ieee80211_stop_queues ( sc - > hw ) ;
/* Disable LED */
ath9k_hw_set_gpio ( ah , ATH_LED_PIN , 1 ) ;
ath9k_hw_cfg_gpio_input ( ah , ATH_LED_PIN ) ;
/* Disable interrupts */
ath9k_hw_set_interrupts ( ah , 0 ) ;
ath_draintxq ( sc , false ) ; /* clear pending tx frames */
ath_stoprecv ( sc ) ; /* turn off frame recv */
ath_flushrecv ( sc ) ; /* flush recv queue */
spin_lock_bh ( & sc - > sc_resetlock ) ;
if ( ! ath9k_hw_reset ( ah , ah - > ah_curchan ,
sc - > sc_ht_info . tx_chan_width ,
sc - > sc_tx_chainmask ,
sc - > sc_rx_chainmask ,
sc - > sc_ht_extprotspacing ,
false , & status ) ) {
DPRINTF ( sc , ATH_DBG_FATAL ,
" %s: unable to reset channel %u (%uMhz) "
" flags 0x%x hal status %u \n " , __func__ ,
ath9k_hw_mhz2ieee ( ah ,
ah - > ah_curchan - > channel ,
ah - > ah_curchan - > channelFlags ) ,
ah - > ah_curchan - > channel ,
ah - > ah_curchan - > channelFlags , status ) ;
}
spin_unlock_bh ( & sc - > sc_resetlock ) ;
ath9k_hw_phy_disable ( ah ) ;
ath9k_hw_setpower ( ah , ATH9K_PM_FULL_SLEEP ) ;
}
static bool ath_is_rfkill_set ( struct ath_softc * sc )
{
struct ath_hal * ah = sc - > sc_ah ;
return ath9k_hw_gpio_get ( ah , ah - > ah_rfkill_gpio ) = =
ah - > ah_rfkill_polarity ;
}
/* h/w rfkill poll function */
static void ath_rfkill_poll ( struct work_struct * work )
{
struct ath_softc * sc = container_of ( work , struct ath_softc ,
rf_kill . rfkill_poll . work ) ;
bool radio_on ;
if ( sc - > sc_flags & SC_OP_INVALID )
return ;
radio_on = ! ath_is_rfkill_set ( sc ) ;
/*
* enable / disable radio only when there is a
* state change in RF switch
*/
if ( radio_on = = ! ! ( sc - > sc_flags & SC_OP_RFKILL_HW_BLOCKED ) ) {
enum rfkill_state state ;
if ( sc - > sc_flags & SC_OP_RFKILL_SW_BLOCKED ) {
state = radio_on ? RFKILL_STATE_SOFT_BLOCKED
: RFKILL_STATE_HARD_BLOCKED ;
} else if ( radio_on ) {
ath_radio_enable ( sc ) ;
state = RFKILL_STATE_UNBLOCKED ;
} else {
ath_radio_disable ( sc ) ;
state = RFKILL_STATE_HARD_BLOCKED ;
}
if ( state = = RFKILL_STATE_HARD_BLOCKED )
sc - > sc_flags | = SC_OP_RFKILL_HW_BLOCKED ;
else
sc - > sc_flags & = ~ SC_OP_RFKILL_HW_BLOCKED ;
rfkill_force_state ( sc - > rf_kill . rfkill , state ) ;
}
queue_delayed_work ( sc - > hw - > workqueue , & sc - > rf_kill . rfkill_poll ,
msecs_to_jiffies ( ATH_RFKILL_POLL_INTERVAL ) ) ;
}
/* s/w rfkill handler */
static int ath_sw_toggle_radio ( void * data , enum rfkill_state state )
{
struct ath_softc * sc = data ;
switch ( state ) {
case RFKILL_STATE_SOFT_BLOCKED :
if ( ! ( sc - > sc_flags & ( SC_OP_RFKILL_HW_BLOCKED |
SC_OP_RFKILL_SW_BLOCKED ) ) )
ath_radio_disable ( sc ) ;
sc - > sc_flags | = SC_OP_RFKILL_SW_BLOCKED ;
return 0 ;
case RFKILL_STATE_UNBLOCKED :
if ( ( sc - > sc_flags & SC_OP_RFKILL_SW_BLOCKED ) ) {
sc - > sc_flags & = ~ SC_OP_RFKILL_SW_BLOCKED ;
if ( sc - > sc_flags & SC_OP_RFKILL_HW_BLOCKED ) {
DPRINTF ( sc , ATH_DBG_FATAL , " Can't turn on the "
" radio as it is disabled by h/w \n " ) ;
return - EPERM ;
}
ath_radio_enable ( sc ) ;
}
return 0 ;
default :
return - EINVAL ;
}
}
/* Init s/w rfkill */
static int ath_init_sw_rfkill ( struct ath_softc * sc )
{
sc - > rf_kill . rfkill = rfkill_allocate ( wiphy_dev ( sc - > hw - > wiphy ) ,
RFKILL_TYPE_WLAN ) ;
if ( ! sc - > rf_kill . rfkill ) {
DPRINTF ( sc , ATH_DBG_FATAL , " Failed to allocate rfkill \n " ) ;
return - ENOMEM ;
}
snprintf ( sc - > rf_kill . rfkill_name , sizeof ( sc - > rf_kill . rfkill_name ) ,
" ath9k-%s:rfkill " , wiphy_name ( sc - > hw - > wiphy ) ) ;
sc - > rf_kill . rfkill - > name = sc - > rf_kill . rfkill_name ;
sc - > rf_kill . rfkill - > data = sc ;
sc - > rf_kill . rfkill - > toggle_radio = ath_sw_toggle_radio ;
sc - > rf_kill . rfkill - > state = RFKILL_STATE_UNBLOCKED ;
sc - > rf_kill . rfkill - > user_claim_unsupported = 1 ;
return 0 ;
}
/* Deinitialize rfkill */
static void ath_deinit_rfkill ( struct ath_softc * sc )
{
if ( sc - > sc_ah - > ah_caps . hw_caps & ATH9K_HW_CAP_RFSILENT )
cancel_delayed_work_sync ( & sc - > rf_kill . rfkill_poll ) ;
if ( sc - > sc_flags & SC_OP_RFKILL_REGISTERED ) {
rfkill_unregister ( sc - > rf_kill . rfkill ) ;
sc - > sc_flags & = ~ SC_OP_RFKILL_REGISTERED ;
sc - > rf_kill . rfkill = NULL ;
}
}
# endif /* CONFIG_RFKILL */
2008-09-10 18:49:27 +05:30
static int ath_detach ( struct ath_softc * sc )
2008-08-04 00:16:41 -07:00
{
2008-09-10 18:49:27 +05:30
struct ieee80211_hw * hw = sc - > hw ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
DPRINTF ( sc , ATH_DBG_CONFIG , " %s: Detach ATH hw \n " , __func__ ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/* Deinit LED control */
ath_deinit_leds ( sc ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:50:17 +05:30
# ifdef CONFIG_RFKILL
/* deinit rfkill */
ath_deinit_rfkill ( sc ) ;
# endif
2008-09-10 18:49:27 +05:30
/* Unregister hw */
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
ieee80211_unregister_hw ( hw ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/* unregister Rate control */
ath_rate_control_unregister ( ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/* tx/rx cleanup */
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
ath_rx_cleanup ( sc ) ;
ath_tx_cleanup ( sc ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/* Deinit */
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
ath_deinit ( sc ) ;
return 0 ;
2008-08-04 00:16:41 -07:00
}
2008-09-10 18:49:27 +05:30
static int ath_attach ( u16 devid ,
struct ath_softc * sc )
2008-08-04 00:16:41 -07:00
{
2008-09-10 18:49:27 +05:30
struct ieee80211_hw * hw = sc - > hw ;
int error = 0 ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
DPRINTF ( sc , ATH_DBG_CONFIG , " %s: Attach ATH hw \n " , __func__ ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
error = ath_init ( devid , sc ) ;
if ( error ! = 0 )
return error ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/* Init nodes */
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
INIT_LIST_HEAD ( & sc - > node_list ) ;
spin_lock_init ( & sc - > node_lock ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/* get mac address from hardware and set in mac80211 */
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
SET_IEEE80211_PERM_ADDR ( hw , sc - > sc_myaddr ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/* setup channels and rates */
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
sc - > sbands [ IEEE80211_BAND_2GHZ ] . channels =
sc - > channels [ IEEE80211_BAND_2GHZ ] ;
sc - > sbands [ IEEE80211_BAND_2GHZ ] . bitrates =
sc - > rates [ IEEE80211_BAND_2GHZ ] ;
sc - > sbands [ IEEE80211_BAND_2GHZ ] . band = IEEE80211_BAND_2GHZ ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
if ( sc - > sc_ah - > ah_caps . hw_caps & ATH9K_HW_CAP_HT )
/* Setup HT capabilities for 2.4Ghz*/
2008-10-09 12:13:49 +02:00
setup_ht_cap ( & sc - > sbands [ IEEE80211_BAND_2GHZ ] . ht_cap ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
hw - > wiphy - > bands [ IEEE80211_BAND_2GHZ ] =
& sc - > sbands [ IEEE80211_BAND_2GHZ ] ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
if ( test_bit ( ATH9K_MODE_11A , sc - > sc_ah - > ah_caps . wireless_modes ) ) {
sc - > sbands [ IEEE80211_BAND_5GHZ ] . channels =
sc - > channels [ IEEE80211_BAND_5GHZ ] ;
sc - > sbands [ IEEE80211_BAND_5GHZ ] . bitrates =
sc - > rates [ IEEE80211_BAND_5GHZ ] ;
sc - > sbands [ IEEE80211_BAND_5GHZ ] . band =
IEEE80211_BAND_5GHZ ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
if ( sc - > sc_ah - > ah_caps . hw_caps & ATH9K_HW_CAP_HT )
/* Setup HT capabilities for 5Ghz*/
2008-10-09 12:13:49 +02:00
setup_ht_cap ( & sc - > sbands [ IEEE80211_BAND_5GHZ ] . ht_cap ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
hw - > wiphy - > bands [ IEEE80211_BAND_5GHZ ] =
& sc - > sbands [ IEEE80211_BAND_5GHZ ] ;
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/* FIXME: Have to figure out proper hw init values later */
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
hw - > queues = 4 ;
hw - > ampdu_queues = 1 ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/* Register rate control */
hw - > rate_control_algorithm = " ath9k_rate_control " ;
error = ath_rate_control_register ( ) ;
if ( error ! = 0 ) {
DPRINTF ( sc , ATH_DBG_FATAL ,
" %s: Unable to register rate control "
" algorithm:%d \n " , __func__ , error ) ;
ath_rate_control_unregister ( ) ;
goto bad ;
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
error = ieee80211_register_hw ( hw ) ;
if ( error ! = 0 ) {
ath_rate_control_unregister ( ) ;
goto bad ;
2008-08-04 00:16:41 -07:00
}
2008-09-10 18:49:27 +05:30
/* Initialize LED control */
ath_init_leds ( sc ) ;
2008-09-10 18:50:17 +05:30
# ifdef CONFIG_RFKILL
/* Initialze h/w Rfkill */
if ( sc - > sc_ah - > ah_caps . hw_caps & ATH9K_HW_CAP_RFSILENT )
INIT_DELAYED_WORK ( & sc - > rf_kill . rfkill_poll , ath_rfkill_poll ) ;
/* Initialize s/w rfkill */
if ( ath_init_sw_rfkill ( sc ) )
goto detach ;
# endif
2008-09-10 18:49:27 +05:30
/* initialize tx/rx engine */
error = ath_tx_init ( sc , ATH_TXBUF ) ;
if ( error ! = 0 )
goto detach ;
error = ath_rx_init ( sc , ATH_RXBUF ) ;
if ( error ! = 0 )
goto detach ;
return 0 ;
detach :
ath_detach ( sc ) ;
bad :
return error ;
2008-08-04 00:16:41 -07:00
}
2008-09-10 18:49:27 +05:30
static int ath9k_start ( struct ieee80211_hw * hw )
2008-08-04 00:16:41 -07:00
{
struct ath_softc * sc = hw - > priv ;
2008-09-10 18:49:27 +05:30
struct ieee80211_channel * curchan = hw - > conf . channel ;
int error = 0 , pos ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
DPRINTF ( sc , ATH_DBG_CONFIG , " %s: Starting driver with "
" initial channel: %d MHz \n " , __func__ , curchan - > center_freq ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/* setup initial channel */
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
pos = ath_get_channel ( sc , curchan ) ;
if ( pos = = - 1 ) {
DPRINTF ( sc , ATH_DBG_FATAL , " %s: Invalid channel \n " , __func__ ) ;
return - EINVAL ;
2008-08-04 00:16:41 -07:00
}
2008-09-10 18:49:27 +05:30
sc - > sc_ah - > ah_channels [ pos ] . chanmode =
( curchan - > band = = IEEE80211_BAND_2GHZ ) ? CHANNEL_G : CHANNEL_A ;
/* open ath_dev */
error = ath_open ( sc , & sc - > sc_ah - > ah_channels [ pos ] ) ;
if ( error ) {
DPRINTF ( sc , ATH_DBG_FATAL ,
" %s: Unable to complete ath_open \n " , __func__ ) ;
return error ;
2008-08-04 00:16:41 -07:00
}
2008-09-10 18:49:27 +05:30
2008-09-10 18:50:17 +05:30
# ifdef CONFIG_RFKILL
/* Start rfkill polling */
if ( sc - > sc_ah - > ah_caps . hw_caps & ATH9K_HW_CAP_RFSILENT )
queue_delayed_work ( sc - > hw - > workqueue ,
& sc - > rf_kill . rfkill_poll , 0 ) ;
if ( ! ( sc - > sc_flags & SC_OP_RFKILL_REGISTERED ) ) {
if ( rfkill_register ( sc - > rf_kill . rfkill ) ) {
DPRINTF ( sc , ATH_DBG_FATAL ,
" Unable to register rfkill \n " ) ;
rfkill_free ( sc - > rf_kill . rfkill ) ;
/* Deinitialize the device */
if ( sc - > pdev - > irq )
free_irq ( sc - > pdev - > irq , sc ) ;
ath_detach ( sc ) ;
pci_iounmap ( sc - > pdev , sc - > mem ) ;
pci_release_region ( sc - > pdev , 0 ) ;
pci_disable_device ( sc - > pdev ) ;
ieee80211_free_hw ( hw ) ;
return - EIO ;
} else {
sc - > sc_flags | = SC_OP_RFKILL_REGISTERED ;
}
}
# endif
2008-09-10 18:49:27 +05:30
ieee80211_wake_queues ( hw ) ;
return 0 ;
2008-08-04 00:16:41 -07:00
}
2008-09-10 18:49:27 +05:30
static int ath9k_tx ( struct ieee80211_hw * hw ,
struct sk_buff * skb )
2008-08-04 00:16:41 -07:00
{
struct ath_softc * sc = hw - > priv ;
2008-09-10 18:49:27 +05:30
int hdrlen , padsize ;
struct ieee80211_tx_info * info = IEEE80211_SKB_CB ( skb ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/*
* As a temporary workaround , assign seq # here ; this will likely need
* to be cleaned up to work better with Beacon transmission and virtual
* BSSes .
*/
if ( info - > flags & IEEE80211_TX_CTL_ASSIGN_SEQ ) {
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
if ( info - > flags & IEEE80211_TX_CTL_FIRST_FRAGMENT )
sc - > seq_no + = 0x10 ;
hdr - > seq_ctrl & = cpu_to_le16 ( IEEE80211_SCTL_FRAG ) ;
hdr - > seq_ctrl | = cpu_to_le16 ( sc - > seq_no ) ;
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/* Add the padding after the header if this is not already done */
hdrlen = ieee80211_get_hdrlen_from_skb ( skb ) ;
if ( hdrlen & 3 ) {
padsize = hdrlen % 4 ;
if ( skb_headroom ( skb ) < padsize )
return - 1 ;
skb_push ( skb , padsize ) ;
memmove ( skb - > data , skb - > data + padsize , hdrlen ) ;
}
DPRINTF ( sc , ATH_DBG_XMIT , " %s: transmitting packet, skb: %p \n " ,
__func__ ,
skb ) ;
if ( ath_tx_start ( sc , skb ) ! = 0 ) {
DPRINTF ( sc , ATH_DBG_XMIT , " %s: TX failed \n " , __func__ ) ;
dev_kfree_skb_any ( skb ) ;
/* FIXME: Check for proper return value from ATH_DEV */
return 0 ;
}
return 0 ;
2008-08-04 00:16:41 -07:00
}
2008-09-10 18:49:27 +05:30
static void ath9k_stop ( struct ieee80211_hw * hw )
2008-08-04 00:16:41 -07:00
{
struct ath_softc * sc = hw - > priv ;
2008-09-10 18:49:27 +05:30
int error ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
DPRINTF ( sc , ATH_DBG_CONFIG , " %s: Driver halt \n " , __func__ ) ;
error = ath_suspend ( sc ) ;
if ( error )
DPRINTF ( sc , ATH_DBG_CONFIG ,
" %s: Device is no longer present \n " , __func__ ) ;
ieee80211_stop_queues ( hw ) ;
2008-09-10 18:50:17 +05:30
# ifdef CONFIG_RFKILL
if ( sc - > sc_ah - > ah_caps . hw_caps & ATH9K_HW_CAP_RFSILENT )
cancel_delayed_work_sync ( & sc - > rf_kill . rfkill_poll ) ;
# endif
2008-08-04 00:16:41 -07:00
}
2008-09-10 18:49:27 +05:30
static int ath9k_add_interface ( struct ieee80211_hw * hw ,
struct ieee80211_if_init_conf * conf )
2008-08-04 00:16:41 -07:00
{
struct ath_softc * sc = hw - > priv ;
2008-09-10 18:49:27 +05:30
int error , ic_opmode = 0 ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/* Support only vap for now */
if ( sc - > sc_nvaps )
return - ENOBUFS ;
switch ( conf - > type ) {
2008-09-11 00:01:58 +02:00
case NL80211_IFTYPE_STATION :
2008-09-10 18:49:27 +05:30
ic_opmode = ATH9K_M_STA ;
2008-08-04 00:16:41 -07:00
break ;
2008-09-11 00:01:58 +02:00
case NL80211_IFTYPE_ADHOC :
2008-09-10 18:49:27 +05:30
ic_opmode = ATH9K_M_IBSS ;
2008-08-04 00:16:41 -07:00
break ;
2008-09-11 00:01:58 +02:00
case NL80211_IFTYPE_AP :
2008-09-10 18:49:27 +05:30
ic_opmode = ATH9K_M_HOSTAP ;
2008-08-04 00:16:41 -07:00
break ;
default :
DPRINTF ( sc , ATH_DBG_FATAL ,
2008-09-10 18:49:27 +05:30
" %s: Interface type %d not yet supported \n " ,
__func__ , conf - > type ) ;
return - EOPNOTSUPP ;
2008-08-04 00:16:41 -07:00
}
2008-09-10 18:49:27 +05:30
DPRINTF ( sc , ATH_DBG_CONFIG , " %s: Attach a VAP of type: %d \n " ,
__func__ ,
ic_opmode ) ;
error = ath_vap_attach ( sc , 0 , conf - > vif , ic_opmode ) ;
if ( error ) {
DPRINTF ( sc , ATH_DBG_FATAL ,
" %s: Unable to attach vap, error: %d \n " ,
__func__ , error ) ;
return error ;
}
2008-10-03 15:45:27 -07:00
if ( conf - > type = = NL80211_IFTYPE_AP ) {
/* TODO: is this a suitable place to start ANI for AP mode? */
/* Start ANI */
mod_timer ( & sc - > sc_ani . timer ,
jiffies + msecs_to_jiffies ( ATH_ANI_POLLINTERVAL ) ) ;
}
2008-09-10 18:49:27 +05:30
return 0 ;
2008-08-04 00:16:41 -07:00
}
2008-09-10 18:49:27 +05:30
static void ath9k_remove_interface ( struct ieee80211_hw * hw ,
struct ieee80211_if_init_conf * conf )
2008-08-04 00:16:41 -07:00
{
2008-09-10 18:49:27 +05:30
struct ath_softc * sc = hw - > priv ;
struct ath_vap * avp ;
int error ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
DPRINTF ( sc , ATH_DBG_CONFIG , " %s: Detach VAP \n " , __func__ ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
avp = sc - > sc_vaps [ 0 ] ;
if ( avp = = NULL ) {
DPRINTF ( sc , ATH_DBG_FATAL , " %s: Invalid interface \n " ,
__func__ ) ;
return ;
2008-08-04 00:16:41 -07:00
}
2008-09-10 18:49:27 +05:30
# ifdef CONFIG_SLOW_ANT_DIV
ath_slow_ant_div_stop ( & sc - > sc_antdiv ) ;
# endif
2008-10-03 15:45:27 -07:00
/* Stop ANI */
del_timer_sync ( & sc - > sc_ani . timer ) ;
2008-08-11 14:01:49 +03:00
2008-09-10 18:49:27 +05:30
/* Update ratectrl */
ath_rate_newstate ( sc , avp ) ;
/* Reclaim beacon resources */
if ( sc - > sc_ah - > ah_opmode = = ATH9K_M_HOSTAP | |
sc - > sc_ah - > ah_opmode = = ATH9K_M_IBSS ) {
ath9k_hw_stoptxdma ( sc - > sc_ah , sc - > sc_bhalq ) ;
ath_beacon_return ( sc , avp ) ;
2008-08-11 14:01:49 +03:00
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/* Set interrupt mask */
sc - > sc_imask & = ~ ( ATH9K_INT_SWBA | ATH9K_INT_BMISS ) ;
ath9k_hw_set_interrupts ( sc - > sc_ah , sc - > sc_imask & ~ ATH9K_INT_GLOBAL ) ;
sc - > sc_flags & = ~ SC_OP_BEACONS ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
error = ath_vap_detach ( sc , 0 ) ;
if ( error )
DPRINTF ( sc , ATH_DBG_FATAL ,
" %s: Unable to detach vap, error: %d \n " ,
__func__ , error ) ;
2008-08-04 00:16:41 -07:00
}
2008-10-09 12:18:51 +02:00
static int ath9k_config ( struct ieee80211_hw * hw , u32 changed )
2008-08-04 00:16:41 -07:00
{
2008-09-10 18:49:27 +05:30
struct ath_softc * sc = hw - > priv ;
struct ieee80211_channel * curchan = hw - > conf . channel ;
2008-10-09 12:18:51 +02:00
struct ieee80211_conf * conf = & hw - > conf ;
2008-09-10 18:49:27 +05:30
int pos ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
DPRINTF ( sc , ATH_DBG_CONFIG , " %s: Set channel: %d MHz \n " ,
__func__ ,
curchan - > center_freq ) ;
2008-08-04 00:16:41 -07:00
2008-10-14 16:58:37 +02:00
/* Update chainmask */
ath_update_chainmask ( sc , conf - > ht . enabled ) ;
2008-09-10 18:49:27 +05:30
pos = ath_get_channel ( sc , curchan ) ;
if ( pos = = - 1 ) {
DPRINTF ( sc , ATH_DBG_FATAL , " %s: Invalid channel \n " , __func__ ) ;
return - EINVAL ;
2008-08-04 00:16:41 -07:00
}
2008-09-10 18:49:27 +05:30
sc - > sc_ah - > ah_channels [ pos ] . chanmode =
( curchan - > band = = IEEE80211_BAND_2GHZ ) ?
CHANNEL_G : CHANNEL_A ;
2008-08-04 00:16:41 -07:00
2008-10-14 16:58:37 +02:00
if ( sc - > sc_curaid & & hw - > conf . ht . enabled )
2008-09-10 18:49:27 +05:30
sc - > sc_ah - > ah_channels [ pos ] . chanmode =
ath_get_extchanmode ( sc , curchan ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
sc - > sc_config . txpowlimit = 2 * conf - > power_level ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
/* set h/w channel */
if ( ath_set_channel ( sc , & sc - > sc_ah - > ah_channels [ pos ] ) < 0 )
DPRINTF ( sc , ATH_DBG_FATAL , " %s: Unable to set channel \n " ,
__func__ ) ;
2008-08-04 00:16:41 -07:00
return 0 ;
}
2008-09-10 18:49:27 +05:30
static int ath9k_config_interface ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif ,
struct ieee80211_if_conf * conf )
2008-08-25 20:47:29 +05:30
{
2008-09-10 18:49:27 +05:30
struct ath_softc * sc = hw - > priv ;
struct ath_hal * ah = sc - > sc_ah ;
struct ath_vap * avp ;
u32 rfilt = 0 ;
int error , i ;
2008-08-25 20:47:29 +05:30
2008-09-10 18:49:27 +05:30
avp = sc - > sc_vaps [ 0 ] ;
if ( avp = = NULL ) {
DPRINTF ( sc , ATH_DBG_FATAL , " %s: Invalid interface \n " ,
__func__ ) ;
return - EINVAL ;
2008-08-25 20:47:29 +05:30
}
2008-09-10 18:49:27 +05:30
/* TODO: Need to decide which hw opmode to use for multi-interface
* cases */
2008-09-11 00:01:58 +02:00
if ( vif - > type = = NL80211_IFTYPE_AP & &
2008-09-10 18:49:27 +05:30
ah - > ah_opmode ! = ATH9K_M_HOSTAP ) {
ah - > ah_opmode = ATH9K_M_HOSTAP ;
ath9k_hw_setopmode ( ah ) ;
ath9k_hw_write_associd ( ah , sc - > sc_myaddr , 0 ) ;
/* Request full reset to get hw opmode changed properly */
sc - > sc_flags | = SC_OP_FULL_RESET ;
}
2008-08-25 20:47:29 +05:30
2008-09-10 18:49:27 +05:30
if ( ( conf - > changed & IEEE80211_IFCC_BSSID ) & &
! is_zero_ether_addr ( conf - > bssid ) ) {
switch ( vif - > type ) {
2008-09-11 00:01:58 +02:00
case NL80211_IFTYPE_STATION :
case NL80211_IFTYPE_ADHOC :
2008-09-10 18:49:27 +05:30
/* Update ratectrl about the new state */
ath_rate_newstate ( sc , avp ) ;
2008-08-25 20:47:29 +05:30
2008-09-10 18:49:27 +05:30
/* Set BSSID */
memcpy ( sc - > sc_curbssid , conf - > bssid , ETH_ALEN ) ;
sc - > sc_curaid = 0 ;
ath9k_hw_write_associd ( sc - > sc_ah , sc - > sc_curbssid ,
sc - > sc_curaid ) ;
2008-08-25 20:47:29 +05:30
2008-09-10 18:49:27 +05:30
/* Set aggregation protection mode parameters */
sc - > sc_config . ath_aggr_prot = 0 ;
2008-08-25 20:47:29 +05:30
2008-09-10 18:49:27 +05:30
/*
* Reset our TSF so that its value is lower than the
* beacon that we are trying to catch .
* Only then hw will update its TSF register with the
* new beacon . Reset the TSF before setting the BSSID
* to avoid allowing in any frames that would update
* our TSF only to have us clear it
* immediately thereafter .
*/
ath9k_hw_reset_tsf ( sc - > sc_ah ) ;
2008-08-25 20:47:29 +05:30
2008-09-10 18:49:27 +05:30
/* Disable BMISS interrupt when we're not associated */
ath9k_hw_set_interrupts ( sc - > sc_ah ,
sc - > sc_imask &
~ ( ATH9K_INT_SWBA | ATH9K_INT_BMISS ) ) ;
sc - > sc_imask & = ~ ( ATH9K_INT_SWBA | ATH9K_INT_BMISS ) ;
2008-08-25 20:47:29 +05:30
2008-09-10 18:49:27 +05:30
DPRINTF ( sc , ATH_DBG_CONFIG ,
2008-10-27 15:59:26 -07:00
" %s: RX filter 0x%x bssid %pM aid 0x%x \n " ,
2008-09-10 18:49:27 +05:30
__func__ , rfilt ,
2008-10-27 15:59:26 -07:00
sc - > sc_curbssid , sc - > sc_curaid ) ;
2008-08-25 20:47:29 +05:30
2008-09-10 18:49:27 +05:30
/* need to reconfigure the beacon */
sc - > sc_flags & = ~ SC_OP_BEACONS ;
2008-08-25 20:47:29 +05:30
2008-09-10 18:49:27 +05:30
break ;
default :
break ;
}
}
2008-08-25 20:47:29 +05:30
2008-09-10 18:49:27 +05:30
if ( ( conf - > changed & IEEE80211_IFCC_BEACON ) & &
2008-09-11 00:01:58 +02:00
( ( vif - > type = = NL80211_IFTYPE_ADHOC ) | |
( vif - > type = = NL80211_IFTYPE_AP ) ) ) {
2008-09-10 18:49:27 +05:30
/*
* Allocate and setup the beacon frame .
*
* Stop any previous beacon DMA . This may be
* necessary , for example , when an ibss merge
* causes reconfiguration ; we may be called
* with beacon transmission active .
*/
ath9k_hw_stoptxdma ( sc - > sc_ah , sc - > sc_bhalq ) ;
2008-08-25 20:47:29 +05:30
2008-09-10 18:49:27 +05:30
error = ath_beacon_alloc ( sc , 0 ) ;
if ( error ! = 0 )
return error ;
2008-08-25 20:47:29 +05:30
2008-09-10 18:49:27 +05:30
ath_beacon_sync ( sc , 0 ) ;
}
2008-08-25 20:47:29 +05:30
2008-09-10 18:49:27 +05:30
/* Check for WLAN_CAPABILITY_PRIVACY ? */
2008-09-11 00:01:58 +02:00
if ( ( avp - > av_opmode ! = NL80211_IFTYPE_STATION ) ) {
2008-09-10 18:49:27 +05:30
for ( i = 0 ; i < IEEE80211_WEP_NKID ; i + + )
if ( ath9k_hw_keyisvalid ( sc - > sc_ah , ( u16 ) i ) )
ath9k_hw_keysetmac ( sc - > sc_ah ,
( u16 ) i ,
sc - > sc_curbssid ) ;
}
2008-08-25 20:47:29 +05:30
2008-09-10 18:49:27 +05:30
/* Only legacy IBSS for now */
2008-09-11 00:01:58 +02:00
if ( vif - > type = = NL80211_IFTYPE_ADHOC )
2008-09-10 18:49:27 +05:30
ath_update_chainmask ( sc , 0 ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
return 0 ;
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
# define SUPPORTED_FILTERS \
( FIF_PROMISC_IN_BSS | \
FIF_ALLMULTI | \
FIF_CONTROL | \
FIF_OTHER_BSS | \
FIF_BCN_PRBRESP_PROMISC | \
FIF_FCSFAIL )
2008-08-25 20:47:29 +05:30
2008-09-10 18:49:27 +05:30
/* FIXME: sc->sc_full_reset ? */
static void ath9k_configure_filter ( struct ieee80211_hw * hw ,
unsigned int changed_flags ,
unsigned int * total_flags ,
int mc_count ,
struct dev_mc_list * mclist )
{
struct ath_softc * sc = hw - > priv ;
u32 rfilt ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
changed_flags & = SUPPORTED_FILTERS ;
* total_flags & = SUPPORTED_FILTERS ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
sc - > rx_filter = * total_flags ;
rfilt = ath_calcrxfilter ( sc ) ;
ath9k_hw_setrxfilter ( sc - > sc_ah , rfilt ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
if ( changed_flags & FIF_BCN_PRBRESP_PROMISC ) {
if ( * total_flags & FIF_BCN_PRBRESP_PROMISC )
ath9k_hw_write_associd ( sc - > sc_ah , ath_bcast_mac , 0 ) ;
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
DPRINTF ( sc , ATH_DBG_CONFIG , " %s: Set HW RX filter: 0x%x \n " ,
__func__ , sc - > rx_filter ) ;
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
static void ath9k_sta_notify ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif ,
enum sta_notify_cmd cmd ,
2008-09-11 00:02:02 +02:00
struct ieee80211_sta * sta )
2008-09-10 18:49:27 +05:30
{
struct ath_softc * sc = hw - > priv ;
struct ath_node * an ;
unsigned long flags ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
spin_lock_irqsave ( & sc - > node_lock , flags ) ;
2008-09-11 00:02:02 +02:00
an = ath_node_find ( sc , sta - > addr ) ;
2008-09-10 18:49:27 +05:30
spin_unlock_irqrestore ( & sc - > node_lock , flags ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
switch ( cmd ) {
case STA_NOTIFY_ADD :
spin_lock_irqsave ( & sc - > node_lock , flags ) ;
if ( ! an ) {
2008-09-11 00:02:02 +02:00
ath_node_attach ( sc , sta - > addr , 0 ) ;
2008-10-27 15:59:26 -07:00
DPRINTF ( sc , ATH_DBG_CONFIG , " %s: Attach a node: %pM \n " ,
__func__ , sta - > addr ) ;
2008-09-10 18:49:27 +05:30
} else {
2008-09-11 00:02:02 +02:00
ath_node_get ( sc , sta - > addr ) ;
2008-09-10 18:49:27 +05:30
}
2008-10-14 16:58:37 +02:00
/* XXX: Is this right? Can the capabilities change? */
an = ath_node_find ( sc , sta - > addr ) ;
an - > maxampdu = 1 < < ( IEEE80211_HTCAP_MAXRXAMPDU_FACTOR +
sta - > ht_cap . ampdu_factor ) ;
an - > mpdudensity =
parse_mpdudensity ( sta - > ht_cap . ampdu_density ) ;
2008-09-10 18:49:27 +05:30
spin_unlock_irqrestore ( & sc - > node_lock , flags ) ;
break ;
case STA_NOTIFY_REMOVE :
if ( ! an )
DPRINTF ( sc , ATH_DBG_FATAL ,
" %s: Removal of a non-existent node \n " ,
__func__ ) ;
else {
ath_node_put ( sc , an , ATH9K_BH_STATUS_INTACT ) ;
2008-10-27 15:59:26 -07:00
DPRINTF ( sc , ATH_DBG_CONFIG , " %s: Put a node: %pM \n " ,
2008-09-10 18:49:27 +05:30
__func__ ,
2008-10-27 15:59:26 -07:00
sta - > addr ) ;
2008-09-10 18:49:27 +05:30
}
break ;
default :
break ;
}
2008-08-04 00:16:41 -07:00
}
2008-09-10 18:49:27 +05:30
static int ath9k_conf_tx ( struct ieee80211_hw * hw ,
u16 queue ,
const struct ieee80211_tx_queue_params * params )
2008-08-04 00:16:41 -07:00
{
2008-09-10 18:49:27 +05:30
struct ath_softc * sc = hw - > priv ;
struct ath9k_tx_queue_info qi ;
int ret = 0 , qnum ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
if ( queue > = WME_NUM_AC )
return 0 ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
qi . tqi_aifs = params - > aifs ;
qi . tqi_cwmin = params - > cw_min ;
qi . tqi_cwmax = params - > cw_max ;
qi . tqi_burstTime = params - > txop ;
qnum = ath_get_hal_qnum ( queue , sc ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
DPRINTF ( sc , ATH_DBG_CONFIG ,
" %s: Configure tx [queue/halq] [%d/%d], "
" aifs: %d, cw_min: %d, cw_max: %d, txop: %d \n " ,
__func__ ,
queue ,
qnum ,
params - > aifs ,
params - > cw_min ,
params - > cw_max ,
params - > txop ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
ret = ath_txq_update ( sc , qnum , & qi ) ;
if ( ret )
DPRINTF ( sc , ATH_DBG_FATAL ,
" %s: TXQ Update failed \n " , __func__ ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
return ret ;
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
static int ath9k_set_key ( struct ieee80211_hw * hw ,
enum set_key_cmd cmd ,
const u8 * local_addr ,
const u8 * addr ,
struct ieee80211_key_conf * key )
{
struct ath_softc * sc = hw - > priv ;
int ret = 0 ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
DPRINTF ( sc , ATH_DBG_KEYCACHE , " %s: Set HW Key \n " , __func__ ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
switch ( cmd ) {
case SET_KEY :
ret = ath_key_config ( sc , addr , key ) ;
if ( ! ret ) {
set_bit ( key - > keyidx , sc - > sc_keymap ) ;
key - > hw_key_idx = key - > keyidx ;
/* push IV and Michael MIC generation to stack */
key - > flags | = IEEE80211_KEY_FLAG_GENERATE_IV ;
if ( key - > alg = = ALG_TKIP )
key - > flags | = IEEE80211_KEY_FLAG_GENERATE_MMIC ;
}
break ;
case DISABLE_KEY :
ath_key_delete ( sc , key ) ;
clear_bit ( key - > keyidx , sc - > sc_keymap ) ;
break ;
default :
ret = - EINVAL ;
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
return ret ;
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
static void ath9k_bss_info_changed ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif ,
struct ieee80211_bss_conf * bss_conf ,
u32 changed )
{
struct ath_softc * sc = hw - > priv ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
if ( changed & BSS_CHANGED_ERP_PREAMBLE ) {
DPRINTF ( sc , ATH_DBG_CONFIG , " %s: BSS Changed PREAMBLE %d \n " ,
__func__ ,
bss_conf - > use_short_preamble ) ;
if ( bss_conf - > use_short_preamble )
sc - > sc_flags | = SC_OP_PREAMBLE_SHORT ;
else
sc - > sc_flags & = ~ SC_OP_PREAMBLE_SHORT ;
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
if ( changed & BSS_CHANGED_ERP_CTS_PROT ) {
DPRINTF ( sc , ATH_DBG_CONFIG , " %s: BSS Changed CTS PROT %d \n " ,
__func__ ,
bss_conf - > use_cts_prot ) ;
if ( bss_conf - > use_cts_prot & &
hw - > conf . channel - > band ! = IEEE80211_BAND_5GHZ )
sc - > sc_flags | = SC_OP_PROTECT_ENABLE ;
else
sc - > sc_flags & = ~ SC_OP_PROTECT_ENABLE ;
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
if ( changed & BSS_CHANGED_HT ) {
2008-10-14 16:58:37 +02:00
DPRINTF ( sc , ATH_DBG_CONFIG , " %s: BSS Changed HT \n " ,
__func__ ) ;
2008-09-10 18:49:27 +05:30
ath9k_ht_conf ( sc , bss_conf ) ;
2008-08-04 00:16:41 -07:00
}
2008-09-10 18:49:27 +05:30
if ( changed & BSS_CHANGED_ASSOC ) {
DPRINTF ( sc , ATH_DBG_CONFIG , " %s: BSS Changed ASSOC %d \n " ,
__func__ ,
bss_conf - > assoc ) ;
ath9k_bss_assoc_info ( sc , bss_conf ) ;
}
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
static u64 ath9k_get_tsf ( struct ieee80211_hw * hw )
{
u64 tsf ;
struct ath_softc * sc = hw - > priv ;
struct ath_hal * ah = sc - > sc_ah ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
tsf = ath9k_hw_gettsf64 ( ah ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
return tsf ;
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
static void ath9k_reset_tsf ( struct ieee80211_hw * hw )
{
struct ath_softc * sc = hw - > priv ;
struct ath_hal * ah = sc - > sc_ah ;
2008-08-25 20:47:29 +05:30
2008-09-10 18:49:27 +05:30
ath9k_hw_reset_tsf ( ah ) ;
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
static int ath9k_ampdu_action ( struct ieee80211_hw * hw ,
enum ieee80211_ampdu_mlme_action action ,
2008-09-11 00:02:02 +02:00
struct ieee80211_sta * sta ,
u16 tid , u16 * ssn )
2008-09-10 18:49:27 +05:30
{
struct ath_softc * sc = hw - > priv ;
int ret = 0 ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
switch ( action ) {
case IEEE80211_AMPDU_RX_START :
2008-09-11 00:02:02 +02:00
ret = ath_rx_aggr_start ( sc , sta - > addr , tid , ssn ) ;
2008-09-10 18:49:27 +05:30
if ( ret < 0 )
DPRINTF ( sc , ATH_DBG_FATAL ,
" %s: Unable to start RX aggregation \n " ,
__func__ ) ;
break ;
case IEEE80211_AMPDU_RX_STOP :
2008-09-11 00:02:02 +02:00
ret = ath_rx_aggr_stop ( sc , sta - > addr , tid ) ;
2008-09-10 18:49:27 +05:30
if ( ret < 0 )
DPRINTF ( sc , ATH_DBG_FATAL ,
" %s: Unable to stop RX aggregation \n " ,
__func__ ) ;
break ;
case IEEE80211_AMPDU_TX_START :
2008-09-11 00:02:02 +02:00
ret = ath_tx_aggr_start ( sc , sta - > addr , tid , ssn ) ;
2008-09-10 18:49:27 +05:30
if ( ret < 0 )
DPRINTF ( sc , ATH_DBG_FATAL ,
" %s: Unable to start TX aggregation \n " ,
__func__ ) ;
else
2008-09-11 00:02:02 +02:00
ieee80211_start_tx_ba_cb_irqsafe ( hw , sta - > addr , tid ) ;
2008-09-10 18:49:27 +05:30
break ;
case IEEE80211_AMPDU_TX_STOP :
2008-09-11 00:02:02 +02:00
ret = ath_tx_aggr_stop ( sc , sta - > addr , tid ) ;
2008-09-10 18:49:27 +05:30
if ( ret < 0 )
DPRINTF ( sc , ATH_DBG_FATAL ,
" %s: Unable to stop TX aggregation \n " ,
__func__ ) ;
2008-08-04 00:16:41 -07:00
2008-09-11 00:02:02 +02:00
ieee80211_stop_tx_ba_cb_irqsafe ( hw , sta - > addr , tid ) ;
2008-09-10 18:49:27 +05:30
break ;
default :
DPRINTF ( sc , ATH_DBG_FATAL ,
" %s: Unknown AMPDU action \n " , __func__ ) ;
}
return ret ;
2008-08-04 00:16:41 -07:00
}
2008-10-13 13:35:05 +02:00
static int ath9k_no_fragmentation ( struct ieee80211_hw * hw , u32 value )
{
return - EOPNOTSUPP ;
}
2008-09-10 18:49:27 +05:30
static struct ieee80211_ops ath9k_ops = {
. tx = ath9k_tx ,
. start = ath9k_start ,
. stop = ath9k_stop ,
. add_interface = ath9k_add_interface ,
. remove_interface = ath9k_remove_interface ,
. config = ath9k_config ,
. config_interface = ath9k_config_interface ,
. configure_filter = ath9k_configure_filter ,
. get_stats = NULL ,
. sta_notify = ath9k_sta_notify ,
. conf_tx = ath9k_conf_tx ,
. get_tx_stats = NULL ,
. bss_info_changed = ath9k_bss_info_changed ,
. set_tim = NULL ,
. set_key = ath9k_set_key ,
. hw_scan = NULL ,
. get_tkip_seq = NULL ,
. set_rts_threshold = NULL ,
. set_frag_threshold = NULL ,
. get_tsf = ath9k_get_tsf ,
. reset_tsf = ath9k_reset_tsf ,
. tx_last_beacon = NULL ,
2008-10-13 13:35:05 +02:00
. ampdu_action = ath9k_ampdu_action ,
. set_frag_threshold = ath9k_no_fragmentation ,
2008-09-10 18:49:27 +05:30
} ;
2008-08-04 00:16:41 -07:00
static int ath_pci_probe ( struct pci_dev * pdev , const struct pci_device_id * id )
{
void __iomem * mem ;
struct ath_softc * sc ;
struct ieee80211_hw * hw ;
const char * athname ;
u8 csz ;
u32 val ;
int ret = 0 ;
if ( pci_enable_device ( pdev ) )
return - EIO ;
/* XXX 32-bit addressing only */
if ( pci_set_dma_mask ( pdev , 0xffffffff ) ) {
printk ( KERN_ERR " ath_pci: 32-bit DMA not available \n " ) ;
ret = - ENODEV ;
goto bad ;
}
/*
* Cache line size is used to size and align various
* structures used to communicate with the hardware .
*/
pci_read_config_byte ( pdev , PCI_CACHE_LINE_SIZE , & csz ) ;
if ( csz = = 0 ) {
/*
* Linux 2.4 .18 ( at least ) writes the cache line size
* register as a 16 - bit wide register which is wrong .
* We must have this setup properly for rx buffer
* DMA to work so force a reasonable value here if it
* comes up zero .
*/
csz = L1_CACHE_BYTES / sizeof ( u32 ) ;
pci_write_config_byte ( pdev , PCI_CACHE_LINE_SIZE , csz ) ;
}
/*
* The default setting of latency timer yields poor results ,
* set it to the value used by other systems . It may be worth
* tweaking this setting more .
*/
pci_write_config_byte ( pdev , PCI_LATENCY_TIMER , 0xa8 ) ;
pci_set_master ( pdev ) ;
/*
* Disable the RETRY_TIMEOUT register ( 0x41 ) to keep
* PCI Tx retries from interfering with C3 CPU state .
*/
pci_read_config_dword ( pdev , 0x40 , & val ) ;
if ( ( val & 0x0000ff00 ) ! = 0 )
pci_write_config_dword ( pdev , 0x40 , val & 0xffff00ff ) ;
ret = pci_request_region ( pdev , 0 , " ath9k " ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " PCI memory region reserve error \n " ) ;
ret = - ENODEV ;
goto bad ;
}
mem = pci_iomap ( pdev , 0 , 0 ) ;
if ( ! mem ) {
printk ( KERN_ERR " PCI memory map error \n " ) ;
ret = - EIO ;
goto bad1 ;
}
hw = ieee80211_alloc_hw ( sizeof ( struct ath_softc ) , & ath9k_ops ) ;
if ( hw = = NULL ) {
printk ( KERN_ERR " ath_pci: no memory for ieee80211_hw \n " ) ;
goto bad2 ;
}
2008-08-14 13:28:20 +05:30
hw - > flags = IEEE80211_HW_RX_INCLUDES_FCS |
2008-08-22 17:31:33 +03:00
IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
2008-08-14 13:28:20 +05:30
IEEE80211_HW_SIGNAL_DBM |
2008-08-04 00:16:41 -07:00
IEEE80211_HW_NOISE_DBM ;
2008-08-29 16:26:43 -07:00
hw - > wiphy - > interface_modes =
BIT ( NL80211_IFTYPE_AP ) |
BIT ( NL80211_IFTYPE_STATION ) |
BIT ( NL80211_IFTYPE_ADHOC ) ;
2008-08-04 00:16:41 -07:00
SET_IEEE80211_DEV ( hw , & pdev - > dev ) ;
pci_set_drvdata ( pdev , hw ) ;
sc = hw - > priv ;
sc - > hw = hw ;
sc - > pdev = pdev ;
sc - > mem = mem ;
if ( ath_attach ( id - > device , sc ) ! = 0 ) {
ret = - ENODEV ;
goto bad3 ;
}
/* setup interrupt service routine */
if ( request_irq ( pdev - > irq , ath_isr , IRQF_SHARED , " ath " , sc ) ) {
printk ( KERN_ERR " %s: request_irq failed \n " ,
wiphy_name ( hw - > wiphy ) ) ;
ret = - EIO ;
goto bad4 ;
}
athname = ath9k_hw_probe ( id - > vendor , id - > device ) ;
printk ( KERN_INFO " %s: %s: mem=0x%lx, irq=%d \n " ,
wiphy_name ( hw - > wiphy ) ,
athname ? athname : " Atheros ??? " ,
( unsigned long ) mem , pdev - > irq ) ;
return 0 ;
bad4 :
ath_detach ( sc ) ;
bad3 :
ieee80211_free_hw ( hw ) ;
bad2 :
pci_iounmap ( pdev , mem ) ;
bad1 :
pci_release_region ( pdev , 0 ) ;
bad :
pci_disable_device ( pdev ) ;
return ret ;
}
static void ath_pci_remove ( struct pci_dev * pdev )
{
struct ieee80211_hw * hw = pci_get_drvdata ( pdev ) ;
struct ath_softc * sc = hw - > priv ;
2008-09-22 14:22:39 +05:30
enum ath9k_int status ;
2008-08-04 00:16:41 -07:00
2008-09-22 14:22:39 +05:30
if ( pdev - > irq ) {
ath9k_hw_set_interrupts ( sc - > sc_ah , 0 ) ;
/* clear the ISR */
ath9k_hw_getisr ( sc - > sc_ah , & status ) ;
2008-10-01 06:12:56 -07:00
sc - > sc_flags | = SC_OP_INVALID ;
2008-08-04 00:16:41 -07:00
free_irq ( pdev - > irq , sc ) ;
2008-09-22 14:22:39 +05:30
}
2008-08-04 00:16:41 -07:00
ath_detach ( sc ) ;
2008-09-22 14:22:39 +05:30
2008-08-04 00:16:41 -07:00
pci_iounmap ( pdev , sc - > mem ) ;
pci_release_region ( pdev , 0 ) ;
pci_disable_device ( pdev ) ;
ieee80211_free_hw ( hw ) ;
}
# ifdef CONFIG_PM
static int ath_pci_suspend ( struct pci_dev * pdev , pm_message_t state )
{
2008-08-25 20:47:29 +05:30
struct ieee80211_hw * hw = pci_get_drvdata ( pdev ) ;
struct ath_softc * sc = hw - > priv ;
ath9k_hw_set_gpio ( sc - > sc_ah , ATH_LED_PIN , 1 ) ;
2008-09-10 18:50:17 +05:30
# ifdef CONFIG_RFKILL
if ( sc - > sc_ah - > ah_caps . hw_caps & ATH9K_HW_CAP_RFSILENT )
cancel_delayed_work_sync ( & sc - > rf_kill . rfkill_poll ) ;
# endif
2008-08-04 00:16:41 -07:00
pci_save_state ( pdev ) ;
pci_disable_device ( pdev ) ;
pci_set_power_state ( pdev , 3 ) ;
return 0 ;
}
static int ath_pci_resume ( struct pci_dev * pdev )
{
2008-08-25 20:47:29 +05:30
struct ieee80211_hw * hw = pci_get_drvdata ( pdev ) ;
struct ath_softc * sc = hw - > priv ;
2008-08-04 00:16:41 -07:00
u32 val ;
int err ;
err = pci_enable_device ( pdev ) ;
if ( err )
return err ;
pci_restore_state ( pdev ) ;
/*
* Suspend / Resume resets the PCI configuration space , so we have to
* re - disable the RETRY_TIMEOUT register ( 0x41 ) to keep
* PCI Tx retries from interfering with C3 CPU state
*/
pci_read_config_dword ( pdev , 0x40 , & val ) ;
if ( ( val & 0x0000ff00 ) ! = 0 )
pci_write_config_dword ( pdev , 0x40 , val & 0xffff00ff ) ;
2008-08-25 20:47:29 +05:30
/* Enable LED */
ath9k_hw_cfg_output ( sc - > sc_ah , ATH_LED_PIN ,
AR_GPIO_OUTPUT_MUX_AS_OUTPUT ) ;
ath9k_hw_set_gpio ( sc - > sc_ah , ATH_LED_PIN , 1 ) ;
2008-09-10 18:50:17 +05:30
# ifdef CONFIG_RFKILL
/*
* check the h / w rfkill state on resume
* and start the rfkill poll timer
*/
if ( sc - > sc_ah - > ah_caps . hw_caps & ATH9K_HW_CAP_RFSILENT )
queue_delayed_work ( sc - > hw - > workqueue ,
& sc - > rf_kill . rfkill_poll , 0 ) ;
# endif
2008-08-04 00:16:41 -07:00
return 0 ;
}
# endif /* CONFIG_PM */
MODULE_DEVICE_TABLE ( pci , ath_pci_id_table ) ;
static struct pci_driver ath_pci_driver = {
. name = " ath9k " ,
. id_table = ath_pci_id_table ,
. probe = ath_pci_probe ,
. remove = ath_pci_remove ,
# ifdef CONFIG_PM
. suspend = ath_pci_suspend ,
. resume = ath_pci_resume ,
# endif /* CONFIG_PM */
} ;
static int __init init_ath_pci ( void )
{
printk ( KERN_INFO " %s: %s \n " , dev_info , ATH_PCI_VERSION ) ;
if ( pci_register_driver ( & ath_pci_driver ) < 0 ) {
printk ( KERN_ERR
" ath_pci: No devices found, driver not installed. \n " ) ;
pci_unregister_driver ( & ath_pci_driver ) ;
return - ENODEV ;
}
return 0 ;
}
module_init ( init_ath_pci ) ;
static void __exit exit_ath_pci ( void )
{
pci_unregister_driver ( & ath_pci_driver ) ;
printk ( KERN_INFO " %s: driver unloaded \n " , dev_info ) ;
}
module_exit ( exit_ath_pci ) ;