2010-03-17 14:25:25 +05:30
/*
* Copyright ( c ) 2010 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 .
*/
# include "htc.h"
/******/
/* TX */
/******/
2010-11-07 14:59:39 +01:00
static const int subtype_txq_to_hwq [ ] = {
[ WME_AC_BE ] = ATH_TXQ_AC_BE ,
[ WME_AC_BK ] = ATH_TXQ_AC_BK ,
[ WME_AC_VI ] = ATH_TXQ_AC_VI ,
[ WME_AC_VO ] = ATH_TXQ_AC_VO ,
} ;
2010-05-14 11:18:56 +05:30
# define ATH9K_HTC_INIT_TXQ(subtype) do { \
2010-11-07 14:59:39 +01:00
qi . tqi_subtype = subtype_txq_to_hwq [ subtype ] ; \
2010-05-14 11:18:56 +05:30
qi . tqi_aifs = ATH9K_TXQ_USEDEFAULT ; \
qi . tqi_cwmin = ATH9K_TXQ_USEDEFAULT ; \
qi . tqi_cwmax = ATH9K_TXQ_USEDEFAULT ; \
qi . tqi_physCompBuf = 0 ; \
qi . tqi_qflags = TXQ_FLAG_TXEOLINT_ENABLE | \
TXQ_FLAG_TXDESCINT_ENABLE ; \
} while ( 0 )
2010-03-17 14:25:25 +05:30
int get_hw_qnum ( u16 queue , int * hwq_map )
{
switch ( queue ) {
case 0 :
2010-06-12 00:33:50 -04:00
return hwq_map [ WME_AC_VO ] ;
2010-03-17 14:25:25 +05:30
case 1 :
2010-06-12 00:33:50 -04:00
return hwq_map [ WME_AC_VI ] ;
2010-03-17 14:25:25 +05:30
case 2 :
2010-06-12 00:33:50 -04:00
return hwq_map [ WME_AC_BE ] ;
2010-03-17 14:25:25 +05:30
case 3 :
2010-06-12 00:33:50 -04:00
return hwq_map [ WME_AC_BK ] ;
2010-03-17 14:25:25 +05:30
default :
2010-06-12 00:33:50 -04:00
return hwq_map [ WME_AC_BE ] ;
2010-03-17 14:25:25 +05:30
}
}
2010-03-24 13:42:13 +05:30
int ath_htc_txq_update ( struct ath9k_htc_priv * priv , int qnum ,
struct ath9k_tx_queue_info * qinfo )
2010-03-17 14:25:25 +05:30
{
struct ath_hw * ah = priv - > ah ;
int error = 0 ;
struct ath9k_tx_queue_info qi ;
ath9k_hw_get_txq_props ( ah , qnum , & qi ) ;
qi . tqi_aifs = qinfo - > tqi_aifs ;
qi . tqi_cwmin = qinfo - > tqi_cwmin / 2 ; /* XXX */
qi . tqi_cwmax = qinfo - > tqi_cwmax ;
qi . tqi_burstTime = qinfo - > tqi_burstTime ;
qi . tqi_readyTime = qinfo - > tqi_readyTime ;
if ( ! ath9k_hw_set_txq_props ( ah , qnum , & qi ) ) {
2010-12-02 19:12:36 -08:00
ath_err ( ath9k_hw_common ( ah ) ,
" Unable to update hardware queue %u! \n " , qnum ) ;
2010-03-17 14:25:25 +05:30
error = - EIO ;
} else {
ath9k_hw_resettxqueue ( ah , qnum ) ;
}
return error ;
}
2011-04-13 11:23:34 +05:30
int ath9k_htc_tx_start ( struct ath9k_htc_priv * priv ,
struct sk_buff * skb , bool is_cab )
2010-03-17 14:25:25 +05:30
{
struct ieee80211_hdr * hdr ;
2011-04-13 11:23:17 +05:30
struct ieee80211_mgmt * mgmt ;
2010-03-17 14:25:25 +05:30
struct ieee80211_tx_info * tx_info = IEEE80211_SKB_CB ( skb ) ;
struct ieee80211_sta * sta = tx_info - > control . sta ;
2011-02-21 07:48:00 +05:30
struct ieee80211_vif * vif = tx_info - > control . vif ;
2010-03-17 14:25:25 +05:30
struct ath9k_htc_sta * ista ;
2011-04-13 11:23:17 +05:30
struct ath9k_htc_vif * avp = NULL ;
2010-03-17 14:25:25 +05:30
struct ath9k_htc_tx_ctl tx_ctl ;
enum htc_endpoint_id epid ;
2010-05-14 11:18:57 +05:30
u16 qnum ;
2010-03-17 14:25:25 +05:30
__le16 fc ;
u8 * tx_fhdr ;
2010-08-11 20:27:43 +05:30
u8 sta_idx , vif_idx ;
2010-03-17 14:25:25 +05:30
hdr = ( struct ieee80211_hdr * ) skb - > data ;
fc = hdr - > frame_control ;
2011-02-21 07:48:00 +05:30
/*
* Find out on which interface this packet has to be
* sent out .
*/
if ( vif ) {
avp = ( struct ath9k_htc_vif * ) vif - > drv_priv ;
vif_idx = avp - > index ;
} else {
if ( ! priv - > ah - > is_monitoring ) {
ath_dbg ( ath9k_hw_common ( priv - > ah ) , ATH_DBG_XMIT ,
" VIF is null, but no monitor interface ! \n " ) ;
return - EINVAL ;
}
vif_idx = priv - > mon_vif_idx ;
}
2010-08-11 20:27:43 +05:30
2011-02-21 07:48:00 +05:30
/*
* Find out which station this packet is destined for .
*/
2010-03-17 14:25:25 +05:30
if ( sta ) {
ista = ( struct ath9k_htc_sta * ) sta - > drv_priv ;
sta_idx = ista - > index ;
} else {
2011-02-21 07:48:00 +05:30
sta_idx = priv - > vif_sta_pos [ vif_idx ] ;
2010-03-17 14:25:25 +05:30
}
memset ( & tx_ctl , 0 , sizeof ( struct ath9k_htc_tx_ctl ) ) ;
if ( ieee80211_is_data ( fc ) ) {
struct tx_frame_hdr tx_hdr ;
2011-01-16 10:56:37 +05:30
u32 flags = 0 ;
2010-03-17 14:25:25 +05:30
u8 * qc ;
memset ( & tx_hdr , 0 , sizeof ( struct tx_frame_hdr ) ) ;
tx_hdr . node_idx = sta_idx ;
2010-08-11 20:27:43 +05:30
tx_hdr . vif_idx = vif_idx ;
2010-03-17 14:25:25 +05:30
if ( tx_info - > flags & IEEE80211_TX_CTL_AMPDU ) {
tx_ctl . type = ATH9K_HTC_AMPDU ;
tx_hdr . data_type = ATH9K_HTC_AMPDU ;
} else {
tx_ctl . type = ATH9K_HTC_NORMAL ;
tx_hdr . data_type = ATH9K_HTC_NORMAL ;
}
2010-11-18 12:19:52 +05:30
if ( ieee80211_is_data_qos ( fc ) ) {
2010-03-17 14:25:25 +05:30
qc = ieee80211_get_qos_ctl ( hdr ) ;
tx_hdr . tidno = qc [ 0 ] & IEEE80211_QOS_CTL_TID_MASK ;
}
/* Check for RTS protection */
if ( priv - > hw - > wiphy - > rts_threshold ! = ( u32 ) - 1 )
if ( skb - > len > priv - > hw - > wiphy - > rts_threshold )
2011-01-16 10:56:37 +05:30
flags | = ATH9K_HTC_TX_RTSCTS ;
2010-03-17 14:25:25 +05:30
/* CTS-to-self */
2011-01-16 10:56:37 +05:30
if ( ! ( flags & ATH9K_HTC_TX_RTSCTS ) & &
2011-02-21 07:48:31 +05:30
( vif & & vif - > bss_conf . use_cts_prot ) )
2011-01-16 10:56:37 +05:30
flags | = ATH9K_HTC_TX_CTSONLY ;
2010-03-17 14:25:25 +05:30
2011-01-16 10:56:37 +05:30
tx_hdr . flags = cpu_to_be32 ( flags ) ;
2010-03-17 14:25:25 +05:30
tx_hdr . key_type = ath9k_cmn_get_hw_crypto_keytype ( skb ) ;
if ( tx_hdr . key_type = = ATH9K_KEY_TYPE_CLEAR )
tx_hdr . keyix = ( u8 ) ATH9K_TXKEYIX_INVALID ;
else
tx_hdr . keyix = tx_info - > control . hw_key - > hw_key_idx ;
tx_fhdr = skb_push ( skb , sizeof ( tx_hdr ) ) ;
memcpy ( tx_fhdr , ( u8 * ) & tx_hdr , sizeof ( tx_hdr ) ) ;
2011-04-13 11:23:34 +05:30
if ( is_cab ) {
CAB_STAT_INC ;
epid = priv - > cab_ep ;
goto send ;
}
2010-03-17 14:25:25 +05:30
qnum = skb_get_queue_mapping ( skb ) ;
2010-05-14 11:18:57 +05:30
switch ( qnum ) {
2010-03-17 14:25:25 +05:30
case 0 :
2010-05-14 11:18:57 +05:30
TX_QSTAT_INC ( WME_AC_VO ) ;
epid = priv - > data_vo_ep ;
2010-03-17 14:25:25 +05:30
break ;
2010-05-14 11:18:57 +05:30
case 1 :
2010-05-14 11:18:54 +05:30
TX_QSTAT_INC ( WME_AC_VI ) ;
2010-03-17 14:25:25 +05:30
epid = priv - > data_vi_ep ;
break ;
2010-05-14 11:18:57 +05:30
case 2 :
TX_QSTAT_INC ( WME_AC_BE ) ;
epid = priv - > data_be_ep ;
2010-03-17 14:25:25 +05:30
break ;
2010-05-14 11:18:57 +05:30
case 3 :
2010-03-17 14:25:25 +05:30
default :
2010-05-14 11:18:54 +05:30
TX_QSTAT_INC ( WME_AC_BK ) ;
2010-03-17 14:25:25 +05:30
epid = priv - > data_bk_ep ;
break ;
}
} else {
struct tx_mgmt_hdr mgmt_hdr ;
memset ( & mgmt_hdr , 0 , sizeof ( struct tx_mgmt_hdr ) ) ;
2011-04-13 11:23:17 +05:30
/*
* Set the TSF adjust value for probe response
* frame also .
*/
if ( avp & & unlikely ( ieee80211_is_probe_resp ( fc ) ) ) {
mgmt = ( struct ieee80211_mgmt * ) skb - > data ;
mgmt - > u . probe_resp . timestamp = avp - > tsfadjust ;
}
2010-03-17 14:25:25 +05:30
tx_ctl . type = ATH9K_HTC_NORMAL ;
mgmt_hdr . node_idx = sta_idx ;
2010-08-11 20:27:43 +05:30
mgmt_hdr . vif_idx = vif_idx ;
2010-03-17 14:25:25 +05:30
mgmt_hdr . tidno = 0 ;
mgmt_hdr . flags = 0 ;
mgmt_hdr . key_type = ath9k_cmn_get_hw_crypto_keytype ( skb ) ;
if ( mgmt_hdr . key_type = = ATH9K_KEY_TYPE_CLEAR )
mgmt_hdr . keyix = ( u8 ) ATH9K_TXKEYIX_INVALID ;
else
mgmt_hdr . keyix = tx_info - > control . hw_key - > hw_key_idx ;
tx_fhdr = skb_push ( skb , sizeof ( mgmt_hdr ) ) ;
memcpy ( tx_fhdr , ( u8 * ) & mgmt_hdr , sizeof ( mgmt_hdr ) ) ;
epid = priv - > mgmt_ep ;
}
2011-04-13 11:23:34 +05:30
send :
2010-03-17 14:25:25 +05:30
return htc_send ( priv - > htc , skb , epid , & tx_ctl ) ;
}
2010-06-15 10:24:37 +05:30
static bool ath9k_htc_check_tx_aggr ( struct ath9k_htc_priv * priv ,
struct ath9k_htc_sta * ista , u8 tid )
{
bool ret = false ;
spin_lock_bh ( & priv - > tx_lock ) ;
if ( ( tid < ATH9K_HTC_MAX_TID ) & & ( ista - > tid_state [ tid ] = = AGGR_STOP ) )
ret = true ;
spin_unlock_bh ( & priv - > tx_lock ) ;
return ret ;
}
2010-03-17 14:25:25 +05:30
void ath9k_tx_tasklet ( unsigned long data )
{
struct ath9k_htc_priv * priv = ( struct ath9k_htc_priv * ) data ;
2011-02-21 07:49:00 +05:30
struct ieee80211_vif * vif ;
2010-03-17 14:25:25 +05:30
struct ieee80211_sta * sta ;
struct ieee80211_hdr * hdr ;
struct ieee80211_tx_info * tx_info ;
struct sk_buff * skb = NULL ;
__le16 fc ;
while ( ( skb = skb_dequeue ( & priv - > tx_queue ) ) ! = NULL ) {
hdr = ( struct ieee80211_hdr * ) skb - > data ;
fc = hdr - > frame_control ;
tx_info = IEEE80211_SKB_CB ( skb ) ;
2011-02-21 07:49:00 +05:30
vif = tx_info - > control . vif ;
2010-03-29 16:07:11 +05:30
memset ( & tx_info - > status , 0 , sizeof ( tx_info - > status ) ) ;
2010-03-17 14:25:25 +05:30
2011-02-21 07:49:00 +05:30
if ( ! vif )
goto send_mac80211 ;
2010-03-17 14:25:25 +05:30
rcu_read_lock ( ) ;
2011-02-21 07:49:00 +05:30
sta = ieee80211_find_sta ( vif , hdr - > addr1 ) ;
2010-03-29 16:07:11 +05:30
if ( ! sta ) {
rcu_read_unlock ( ) ;
ieee80211_tx_status ( priv - > hw , skb ) ;
continue ;
}
/* Check if we need to start aggregation */
2010-03-17 14:25:25 +05:30
if ( sta & & conf_is_ht ( & priv - > hw - > conf ) & &
2010-06-15 10:24:37 +05:30
! ( skb - > protocol = = cpu_to_be16 ( ETH_P_PAE ) ) ) {
2010-03-17 14:25:25 +05:30
if ( ieee80211_is_data_qos ( fc ) ) {
u8 * qc , tid ;
struct ath9k_htc_sta * ista ;
qc = ieee80211_get_qos_ctl ( hdr ) ;
tid = qc [ 0 ] & 0xf ;
ista = ( struct ath9k_htc_sta * ) sta - > drv_priv ;
2010-06-15 10:24:37 +05:30
if ( ath9k_htc_check_tx_aggr ( priv , ista , tid ) ) {
2010-12-15 07:47:10 +05:30
ieee80211_start_tx_ba_session ( sta , tid , 0 ) ;
2010-06-15 10:24:37 +05:30
spin_lock_bh ( & priv - > tx_lock ) ;
2010-03-17 14:25:25 +05:30
ista - > tid_state [ tid ] = AGGR_PROGRESS ;
2010-06-15 10:24:37 +05:30
spin_unlock_bh ( & priv - > tx_lock ) ;
2010-03-17 14:25:25 +05:30
}
}
}
rcu_read_unlock ( ) ;
2011-02-21 07:49:00 +05:30
send_mac80211 :
2010-03-29 16:07:11 +05:30
/* Send status to mac80211 */
2010-03-17 14:25:25 +05:30
ieee80211_tx_status ( priv - > hw , skb ) ;
}
2010-03-29 16:07:17 +05:30
/* Wake TX queues if needed */
spin_lock_bh ( & priv - > tx_lock ) ;
if ( priv - > tx_queues_stop ) {
priv - > tx_queues_stop = false ;
spin_unlock_bh ( & priv - > tx_lock ) ;
2010-12-02 19:12:37 -08:00
ath_dbg ( ath9k_hw_common ( priv - > ah ) , ATH_DBG_XMIT ,
" Waking up TX queues \n " ) ;
2010-03-29 16:07:17 +05:30
ieee80211_wake_queues ( priv - > hw ) ;
return ;
}
spin_unlock_bh ( & priv - > tx_lock ) ;
2010-03-17 14:25:25 +05:30
}
void ath9k_htc_txep ( void * drv_priv , struct sk_buff * skb ,
enum htc_endpoint_id ep_id , bool txok )
{
struct ath9k_htc_priv * priv = ( struct ath9k_htc_priv * ) drv_priv ;
2010-04-26 15:09:39 +05:30
struct ath_common * common = ath9k_hw_common ( priv - > ah ) ;
2010-03-17 14:25:25 +05:30
struct ieee80211_tx_info * tx_info ;
if ( ! skb )
return ;
2010-04-26 15:09:39 +05:30
if ( ep_id = = priv - > mgmt_ep ) {
2010-03-17 14:25:25 +05:30
skb_pull ( skb , sizeof ( struct tx_mgmt_hdr ) ) ;
2010-04-26 15:09:39 +05:30
} else if ( ( ep_id = = priv - > data_bk_ep ) | |
( ep_id = = priv - > data_be_ep ) | |
( ep_id = = priv - > data_vi_ep ) | |
2011-04-13 11:23:34 +05:30
( ep_id = = priv - > data_vo_ep ) | |
( ep_id = = priv - > cab_ep ) ) {
2010-03-17 14:25:25 +05:30
skb_pull ( skb , sizeof ( struct tx_frame_hdr ) ) ;
2010-04-26 15:09:39 +05:30
} else {
2010-12-02 19:12:36 -08:00
ath_err ( common , " Unsupported TX EPID: %d \n " , ep_id ) ;
2010-04-26 15:09:39 +05:30
dev_kfree_skb_any ( skb ) ;
return ;
}
2010-03-17 14:25:25 +05:30
tx_info = IEEE80211_SKB_CB ( skb ) ;
if ( txok )
tx_info - > flags | = IEEE80211_TX_STAT_ACK ;
skb_queue_tail ( & priv - > tx_queue , skb ) ;
tasklet_schedule ( & priv - > tx_tasklet ) ;
}
int ath9k_tx_init ( struct ath9k_htc_priv * priv )
{
skb_queue_head_init ( & priv - > tx_queue ) ;
return 0 ;
}
void ath9k_tx_cleanup ( struct ath9k_htc_priv * priv )
{
}
2010-06-12 00:33:50 -04:00
bool ath9k_htc_txq_setup ( struct ath9k_htc_priv * priv , int subtype )
2010-03-17 14:25:25 +05:30
{
struct ath_hw * ah = priv - > ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
struct ath9k_tx_queue_info qi ;
int qnum ;
memset ( & qi , 0 , sizeof ( qi ) ) ;
2010-05-14 11:18:56 +05:30
ATH9K_HTC_INIT_TXQ ( subtype ) ;
2010-03-17 14:25:25 +05:30
qnum = ath9k_hw_setuptxqueue ( priv - > ah , ATH9K_TX_QUEUE_DATA , & qi ) ;
if ( qnum = = - 1 )
return false ;
if ( qnum > = ARRAY_SIZE ( priv - > hwq_map ) ) {
2010-12-02 19:12:36 -08:00
ath_err ( common , " qnum %u out of range, max %zu! \n " ,
qnum , ARRAY_SIZE ( priv - > hwq_map ) ) ;
2010-03-17 14:25:25 +05:30
ath9k_hw_releasetxqueue ( ah , qnum ) ;
return false ;
}
priv - > hwq_map [ subtype ] = qnum ;
return true ;
}
2010-05-14 11:18:56 +05:30
int ath9k_htc_cabq_setup ( struct ath9k_htc_priv * priv )
{
struct ath9k_tx_queue_info qi ;
memset ( & qi , 0 , sizeof ( qi ) ) ;
ATH9K_HTC_INIT_TXQ ( 0 ) ;
return ath9k_hw_setuptxqueue ( priv - > ah , ATH9K_TX_QUEUE_CAB , & qi ) ;
}
2010-03-17 14:25:25 +05:30
/******/
/* RX */
/******/
2010-03-29 16:07:09 +05:30
/*
* Calculate the RX filter to be set in the HW .
*/
u32 ath9k_htc_calcrxfilter ( struct ath9k_htc_priv * priv )
{
# define RX_FILTER_PRESERVE (ATH9K_RX_FILTER_PHYERR | ATH9K_RX_FILTER_PHYRADAR)
struct ath_hw * ah = priv - > ah ;
u32 rfilt ;
rfilt = ( ath9k_hw_getrxfilter ( ah ) & RX_FILTER_PRESERVE )
| ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST
| ATH9K_RX_FILTER_MCAST ;
2010-10-14 10:50:26 +05:30
if ( priv - > rxfilter & FIF_PROBE_REQ )
2010-03-29 16:07:09 +05:30
rfilt | = ATH9K_RX_FILTER_PROBEREQ ;
/*
* Set promiscuous mode when FIF_PROMISC_IN_BSS is enabled for station
* mode interface or when in monitor mode . AP mode does not need this
* since it receives all in - BSS frames anyway .
*/
if ( ( ( ah - > opmode ! = NL80211_IFTYPE_AP ) & &
( priv - > rxfilter & FIF_PROMISC_IN_BSS ) ) | |
2011-02-21 07:50:23 +05:30
ah - > is_monitoring )
2010-03-29 16:07:09 +05:30
rfilt | = ATH9K_RX_FILTER_PROM ;
if ( priv - > rxfilter & FIF_CONTROL )
rfilt | = ATH9K_RX_FILTER_CONTROL ;
if ( ( ah - > opmode = = NL80211_IFTYPE_STATION ) & &
! ( priv - > rxfilter & FIF_BCN_PRBRESP_PROMISC ) )
rfilt | = ATH9K_RX_FILTER_MYBEACON ;
else
rfilt | = ATH9K_RX_FILTER_BEACON ;
2011-02-21 07:50:23 +05:30
if ( conf_is_ht ( & priv - > hw - > conf ) ) {
2010-03-29 16:07:09 +05:30
rfilt | = ATH9K_RX_FILTER_COMP_BAR ;
2011-02-21 07:50:23 +05:30
rfilt | = ATH9K_RX_FILTER_UNCOMP_BA_BAR ;
}
if ( priv - > rxfilter & FIF_PSPOLL )
rfilt | = ATH9K_RX_FILTER_PSPOLL ;
2010-03-29 16:07:09 +05:30
return rfilt ;
# undef RX_FILTER_PRESERVE
}
/*
* Recv initialization for opmode change .
*/
static void ath9k_htc_opmode_init ( struct ath9k_htc_priv * priv )
{
struct ath_hw * ah = priv - > ah ;
u32 rfilt , mfilt [ 2 ] ;
/* configure rx filter */
rfilt = ath9k_htc_calcrxfilter ( priv ) ;
ath9k_hw_setrxfilter ( ah , rfilt ) ;
/* calculate and install multicast filter */
mfilt [ 0 ] = mfilt [ 1 ] = ~ 0 ;
ath9k_hw_setmcastfilter ( ah , mfilt [ 0 ] , mfilt [ 1 ] ) ;
}
2010-03-17 14:25:25 +05:30
void ath9k_host_rx_init ( struct ath9k_htc_priv * priv )
{
ath9k_hw_rxena ( priv - > ah ) ;
2010-03-29 16:07:09 +05:30
ath9k_htc_opmode_init ( priv ) ;
2010-06-12 00:33:44 -04:00
ath9k_hw_startpcureceive ( priv - > ah , ( priv - > op_flags & OP_SCANNING ) ) ;
2010-03-17 14:25:25 +05:30
priv - > rx . last_rssi = ATH_RSSI_DUMMY_MARKER ;
}
static void ath9k_process_rate ( struct ieee80211_hw * hw ,
struct ieee80211_rx_status * rxs ,
u8 rx_rate , u8 rs_flags )
{
struct ieee80211_supported_band * sband ;
enum ieee80211_band band ;
unsigned int i = 0 ;
if ( rx_rate & 0x80 ) {
/* HT rate */
rxs - > flag | = RX_FLAG_HT ;
if ( rs_flags & ATH9K_RX_2040 )
rxs - > flag | = RX_FLAG_40MHZ ;
if ( rs_flags & ATH9K_RX_GI )
rxs - > flag | = RX_FLAG_SHORT_GI ;
rxs - > rate_idx = rx_rate & 0x7f ;
return ;
}
band = hw - > conf . channel - > band ;
sband = hw - > wiphy - > bands [ band ] ;
for ( i = 0 ; i < sband - > n_bitrates ; i + + ) {
if ( sband - > bitrates [ i ] . hw_value = = rx_rate ) {
rxs - > rate_idx = i ;
return ;
}
if ( sband - > bitrates [ i ] . hw_value_short = = rx_rate ) {
rxs - > rate_idx = i ;
rxs - > flag | = RX_FLAG_SHORTPRE ;
return ;
}
}
}
static bool ath9k_rx_prepare ( struct ath9k_htc_priv * priv ,
struct ath9k_htc_rxbuf * rxbuf ,
struct ieee80211_rx_status * rx_status )
{
struct ieee80211_hdr * hdr ;
struct ieee80211_hw * hw = priv - > hw ;
struct sk_buff * skb = rxbuf - > skb ;
struct ath_common * common = ath9k_hw_common ( priv - > ah ) ;
2010-04-26 15:09:42 +05:30
struct ath_htc_rx_status * rxstatus ;
2010-03-17 14:25:25 +05:30
int hdrlen , padpos , padsize ;
int last_rssi = ATH_RSSI_DUMMY_MARKER ;
__le16 fc ;
2010-04-26 15:09:42 +05:30
if ( skb - > len < = HTC_RX_FRAME_HEADER_SIZE ) {
2010-12-02 19:12:36 -08:00
ath_err ( common , " Corrupted RX frame, dropping \n " ) ;
2010-04-26 15:09:42 +05:30
goto rx_next ;
}
rxstatus = ( struct ath_htc_rx_status * ) skb - > data ;
if ( be16_to_cpu ( rxstatus - > rs_datalen ) -
( skb - > len - HTC_RX_FRAME_HEADER_SIZE ) ! = 0 ) {
2010-12-02 19:12:36 -08:00
ath_err ( common ,
" Corrupted RX data len, dropping (dlen: %d, skblen: %d) \n " ,
rxstatus - > rs_datalen , skb - > len ) ;
2010-04-26 15:09:42 +05:30
goto rx_next ;
}
2011-04-13 11:24:10 +05:30
ath9k_htc_err_stat_rx ( priv , rxstatus ) ;
2010-04-26 15:09:42 +05:30
/* Get the RX status information */
memcpy ( & rxbuf - > rxstatus , rxstatus , HTC_RX_FRAME_HEADER_SIZE ) ;
skb_pull ( skb , HTC_RX_FRAME_HEADER_SIZE ) ;
2010-03-17 14:25:25 +05:30
hdr = ( struct ieee80211_hdr * ) skb - > data ;
fc = hdr - > frame_control ;
hdrlen = ieee80211_get_hdrlen_from_skb ( skb ) ;
padpos = ath9k_cmn_padpos ( fc ) ;
padsize = padpos & 3 ;
2010-03-30 08:48:27 +05:30
if ( padsize & & skb - > len > = padpos + padsize + FCS_LEN ) {
2010-03-17 14:25:25 +05:30
memmove ( skb - > data + padsize , skb - > data , padpos ) ;
skb_pull ( skb , padsize ) ;
}
memset ( rx_status , 0 , sizeof ( struct ieee80211_rx_status ) ) ;
if ( rxbuf - > rxstatus . rs_status ! = 0 ) {
if ( rxbuf - > rxstatus . rs_status & ATH9K_RXERR_CRC )
rx_status - > flag | = RX_FLAG_FAILED_FCS_CRC ;
if ( rxbuf - > rxstatus . rs_status & ATH9K_RXERR_PHY )
goto rx_next ;
if ( rxbuf - > rxstatus . rs_status & ATH9K_RXERR_DECRYPT ) {
/* FIXME */
} else if ( rxbuf - > rxstatus . rs_status & ATH9K_RXERR_MIC ) {
if ( ieee80211_is_ctl ( fc ) )
/*
* Sometimes , we get invalid
* MIC failures on valid control frames .
* Remove these mic errors .
*/
rxbuf - > rxstatus . rs_status & = ~ ATH9K_RXERR_MIC ;
else
rx_status - > flag | = RX_FLAG_MMIC_ERROR ;
}
/*
* Reject error frames with the exception of
* decryption and MIC failures . For monitor mode ,
* we also ignore the CRC error .
*/
if ( priv - > ah - > opmode = = NL80211_IFTYPE_MONITOR ) {
if ( rxbuf - > rxstatus . rs_status &
~ ( ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC |
ATH9K_RXERR_CRC ) )
goto rx_next ;
} else {
if ( rxbuf - > rxstatus . rs_status &
~ ( ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC ) ) {
goto rx_next ;
}
}
}
if ( ! ( rxbuf - > rxstatus . rs_status & ATH9K_RXERR_DECRYPT ) ) {
u8 keyix ;
keyix = rxbuf - > rxstatus . rs_keyix ;
if ( keyix ! = ATH9K_RXKEYIX_INVALID ) {
rx_status - > flag | = RX_FLAG_DECRYPTED ;
} else if ( ieee80211_has_protected ( fc ) & &
skb - > len > = hdrlen + 4 ) {
keyix = skb - > data [ hdrlen + 3 ] > > 6 ;
if ( test_bit ( keyix , common - > keymap ) )
rx_status - > flag | = RX_FLAG_DECRYPTED ;
}
}
ath9k_process_rate ( hw , rx_status , rxbuf - > rxstatus . rs_rate ,
rxbuf - > rxstatus . rs_flags ) ;
2011-02-21 07:48:39 +05:30
if ( rxbuf - > rxstatus . rs_rssi ! = ATH9K_RSSI_BAD & &
! rxbuf - > rxstatus . rs_moreaggr )
ATH_RSSI_LPF ( priv - > rx . last_rssi ,
rxbuf - > rxstatus . rs_rssi ) ;
2010-03-17 14:25:25 +05:30
2011-02-21 07:48:39 +05:30
last_rssi = priv - > rx . last_rssi ;
2010-03-17 14:25:25 +05:30
2011-02-21 07:48:39 +05:30
if ( likely ( last_rssi ! = ATH_RSSI_DUMMY_MARKER ) )
rxbuf - > rxstatus . rs_rssi = ATH_EP_RND ( last_rssi ,
ATH_RSSI_EP_MULTIPLIER ) ;
2010-03-17 14:25:25 +05:30
2011-02-21 07:48:39 +05:30
if ( rxbuf - > rxstatus . rs_rssi < 0 )
rxbuf - > rxstatus . rs_rssi = 0 ;
2010-03-17 14:25:25 +05:30
2011-02-21 07:48:39 +05:30
if ( ieee80211_is_beacon ( fc ) )
priv - > ah - > stats . avgbrssi = rxbuf - > rxstatus . rs_rssi ;
2010-03-17 14:25:25 +05:30
2010-04-16 11:54:03 +05:30
rx_status - > mactime = be64_to_cpu ( rxbuf - > rxstatus . rs_tstamp ) ;
2010-03-17 14:25:25 +05:30
rx_status - > band = hw - > conf . channel - > band ;
rx_status - > freq = hw - > conf . channel - > center_freq ;
rx_status - > signal = rxbuf - > rxstatus . rs_rssi + ATH_DEFAULT_NOISE_FLOOR ;
rx_status - > antenna = rxbuf - > rxstatus . rs_antenna ;
2011-02-23 15:06:08 +01:00
rx_status - > flag | = RX_FLAG_MACTIME_MPDU ;
2010-03-17 14:25:25 +05:30
return true ;
rx_next :
return false ;
}
/*
* FIXME : Handle FLUSH later on .
*/
void ath9k_rx_tasklet ( unsigned long data )
{
struct ath9k_htc_priv * priv = ( struct ath9k_htc_priv * ) data ;
struct ath9k_htc_rxbuf * rxbuf = NULL , * tmp_buf = NULL ;
struct ieee80211_rx_status rx_status ;
struct sk_buff * skb ;
unsigned long flags ;
2010-04-05 14:48:05 +05:30
struct ieee80211_hdr * hdr ;
2010-03-17 14:25:25 +05:30
do {
spin_lock_irqsave ( & priv - > rx . rxbuflock , flags ) ;
list_for_each_entry ( tmp_buf , & priv - > rx . rxbuf , list ) {
if ( tmp_buf - > in_process ) {
rxbuf = tmp_buf ;
break ;
}
}
if ( rxbuf = = NULL ) {
spin_unlock_irqrestore ( & priv - > rx . rxbuflock , flags ) ;
break ;
}
if ( ! rxbuf - > skb )
goto requeue ;
if ( ! ath9k_rx_prepare ( priv , rxbuf , & rx_status ) ) {
dev_kfree_skb_any ( rxbuf - > skb ) ;
goto requeue ;
}
memcpy ( IEEE80211_SKB_RXCB ( rxbuf - > skb ) , & rx_status ,
sizeof ( struct ieee80211_rx_status ) ) ;
skb = rxbuf - > skb ;
2010-04-05 14:48:05 +05:30
hdr = ( struct ieee80211_hdr * ) skb - > data ;
if ( ieee80211_is_beacon ( hdr - > frame_control ) & & priv - > ps_enabled )
ieee80211_queue_work ( priv - > hw , & priv - > ps_work ) ;
2010-03-17 14:25:25 +05:30
spin_unlock_irqrestore ( & priv - > rx . rxbuflock , flags ) ;
ieee80211_rx ( priv - > hw , skb ) ;
spin_lock_irqsave ( & priv - > rx . rxbuflock , flags ) ;
requeue :
rxbuf - > in_process = false ;
rxbuf - > skb = NULL ;
list_move_tail ( & rxbuf - > list , & priv - > rx . rxbuf ) ;
rxbuf = NULL ;
spin_unlock_irqrestore ( & priv - > rx . rxbuflock , flags ) ;
} while ( 1 ) ;
}
void ath9k_htc_rxep ( void * drv_priv , struct sk_buff * skb ,
enum htc_endpoint_id ep_id )
{
struct ath9k_htc_priv * priv = ( struct ath9k_htc_priv * ) drv_priv ;
struct ath_hw * ah = priv - > ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
struct ath9k_htc_rxbuf * rxbuf = NULL , * tmp_buf = NULL ;
spin_lock ( & priv - > rx . rxbuflock ) ;
list_for_each_entry ( tmp_buf , & priv - > rx . rxbuf , list ) {
if ( ! tmp_buf - > in_process ) {
rxbuf = tmp_buf ;
break ;
}
}
spin_unlock ( & priv - > rx . rxbuflock ) ;
if ( rxbuf = = NULL ) {
2010-12-02 19:12:37 -08:00
ath_dbg ( common , ATH_DBG_ANY ,
" No free RX buffer \n " ) ;
2010-03-17 14:25:25 +05:30
goto err ;
}
spin_lock ( & priv - > rx . rxbuflock ) ;
rxbuf - > skb = skb ;
rxbuf - > in_process = true ;
spin_unlock ( & priv - > rx . rxbuflock ) ;
tasklet_schedule ( & priv - > rx_tasklet ) ;
return ;
err :
dev_kfree_skb_any ( skb ) ;
}
/* FIXME: Locking for cleanup/init */
void ath9k_rx_cleanup ( struct ath9k_htc_priv * priv )
{
struct ath9k_htc_rxbuf * rxbuf , * tbuf ;
list_for_each_entry_safe ( rxbuf , tbuf , & priv - > rx . rxbuf , list ) {
list_del ( & rxbuf - > list ) ;
if ( rxbuf - > skb )
dev_kfree_skb_any ( rxbuf - > skb ) ;
kfree ( rxbuf ) ;
}
}
int ath9k_rx_init ( struct ath9k_htc_priv * priv )
{
struct ath_hw * ah = priv - > ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
struct ath9k_htc_rxbuf * rxbuf ;
int i = 0 ;
INIT_LIST_HEAD ( & priv - > rx . rxbuf ) ;
spin_lock_init ( & priv - > rx . rxbuflock ) ;
for ( i = 0 ; i < ATH9K_HTC_RXBUF ; i + + ) {
rxbuf = kzalloc ( sizeof ( struct ath9k_htc_rxbuf ) , GFP_KERNEL ) ;
if ( rxbuf = = NULL ) {
2010-12-02 19:12:36 -08:00
ath_err ( common , " Unable to allocate RX buffers \n " ) ;
2010-03-17 14:25:25 +05:30
goto err ;
}
list_add_tail ( & rxbuf - > list , & priv - > rx . rxbuf ) ;
}
return 0 ;
err :
ath9k_rx_cleanup ( priv ) ;
return - ENOMEM ;
}