2009-08-06 16:25:28 +03:00
/*
* This file is part of wl1271
*
* Copyright ( C ) 2009 Nokia Corporation
*
* Contact : Luciano Coelho < luciano . coelho @ nokia . com >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA
* 02110 - 1301 USA
*
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include "wl1271.h"
2010-02-18 13:25:55 +02:00
# include "wl1271_io.h"
2009-08-06 16:25:28 +03:00
# include "wl1271_reg.h"
# include "wl1271_ps.h"
# include "wl1271_tx.h"
static int wl1271_tx_id ( struct wl1271 * wl , struct sk_buff * skb )
{
int i ;
2009-10-08 21:56:26 +03:00
for ( i = 0 ; i < ACX_TX_DESCRIPTORS ; i + + )
2009-08-06 16:25:28 +03:00
if ( wl - > tx_frames [ i ] = = NULL ) {
wl - > tx_frames [ i ] = skb ;
2010-05-24 11:18:17 +03:00
wl - > tx_frames_cnt + + ;
2009-08-06 16:25:28 +03:00
return i ;
}
return - EBUSY ;
}
static int wl1271_tx_allocate ( struct wl1271 * wl , struct sk_buff * skb , u32 extra )
{
struct wl1271_tx_hw_descr * desc ;
u32 total_len = skb - > len + sizeof ( struct wl1271_tx_hw_descr ) + extra ;
2010-02-22 08:38:39 +02:00
u32 total_blocks ;
2009-08-06 16:25:28 +03:00
int id , ret = - EBUSY ;
/* allocate free identifier for the packet */
id = wl1271_tx_id ( wl , skb ) ;
if ( id < 0 )
return id ;
/* approximate the number of blocks required for this packet
in the firmware */
2010-02-22 08:38:39 +02:00
total_blocks = total_len + TX_HW_BLOCK_SIZE - 1 ;
total_blocks = total_blocks / TX_HW_BLOCK_SIZE + TX_HW_BLOCK_SPARE ;
2009-08-06 16:25:28 +03:00
if ( total_blocks < = wl - > tx_blocks_available ) {
desc = ( struct wl1271_tx_hw_descr * ) skb_push (
skb , total_len - skb - > len ) ;
desc - > extra_mem_blocks = TX_HW_BLOCK_SPARE ;
desc - > total_mem_blocks = total_blocks ;
desc - > id = id ;
wl - > tx_blocks_available - = total_blocks ;
ret = 0 ;
wl1271_debug ( DEBUG_TX ,
" tx_allocate: size: %d, blocks: %d, id: %d " ,
total_len , total_blocks , id ) ;
2010-05-24 11:18:17 +03:00
} else {
2009-08-06 16:25:28 +03:00
wl - > tx_frames [ id ] = NULL ;
2010-05-24 11:18:17 +03:00
wl - > tx_frames_cnt - - ;
}
2009-08-06 16:25:28 +03:00
return ret ;
}
static int wl1271_tx_fill_hdr ( struct wl1271 * wl , struct sk_buff * skb ,
u32 extra , struct ieee80211_tx_info * control )
{
2010-02-22 08:38:38 +02:00
struct timespec ts ;
2009-08-06 16:25:28 +03:00
struct wl1271_tx_hw_descr * desc ;
2010-02-18 13:25:41 +02:00
int pad , ac ;
2010-02-22 08:38:38 +02:00
s64 hosttime ;
2009-10-15 10:33:29 +03:00
u16 tx_attr ;
2009-08-06 16:25:28 +03:00
desc = ( struct wl1271_tx_hw_descr * ) skb - > data ;
2009-10-08 21:56:20 +03:00
/* relocate space for security header */
if ( extra ) {
void * framestart = skb - > data + sizeof ( * desc ) ;
u16 fc = * ( u16 * ) ( framestart + extra ) ;
2009-10-15 10:33:29 +03:00
int hdrlen = ieee80211_hdrlen ( cpu_to_le16 ( fc ) ) ;
2009-10-08 21:56:20 +03:00
memmove ( framestart , framestart + extra , hdrlen ) ;
}
2009-08-06 16:25:28 +03:00
/* configure packet life time */
2010-02-22 08:38:38 +02:00
getnstimeofday ( & ts ) ;
hosttime = ( timespec_to_ns ( & ts ) > > 10 ) ;
desc - > start_time = cpu_to_le32 ( hosttime - wl - > time_offset ) ;
2009-10-15 10:33:29 +03:00
desc - > life_time = cpu_to_le16 ( TX_HW_MGMT_PKT_LIFETIME_TU ) ;
2009-08-06 16:25:28 +03:00
/* configure the tx attributes */
2009-10-15 10:33:29 +03:00
tx_attr = wl - > session_counter < < TX_HW_ATTR_OFST_SESSION_COUNTER ;
2010-02-18 13:25:41 +02:00
/* queue */
ac = wl1271_tx_get_queue ( skb_get_queue_mapping ( skb ) ) ;
desc - > tid = wl1271_tx_ac_to_tid ( ac ) ;
2009-08-06 16:25:28 +03:00
desc - > aid = TX_HW_DEFAULT_AID ;
desc - > reserved = 0 ;
/* align the length (and store in terms of words) */
pad = WL1271_TX_ALIGN ( skb - > len ) ;
2009-10-15 10:33:29 +03:00
desc - > length = cpu_to_le16 ( pad > > 2 ) ;
2009-08-06 16:25:28 +03:00
/* calculate number of padding bytes */
pad = pad - skb - > len ;
2009-10-15 10:33:29 +03:00
tx_attr | = pad < < TX_HW_ATTR_OFST_LAST_WORD_PAD ;
2009-12-11 15:41:06 +02:00
/* if the packets are destined for AP (have a STA entry) send them
with AP rate policies , otherwise use default basic rates */
if ( control - > control . sta )
tx_attr | = ACX_TX_AP_FULL_RATE < < TX_HW_ATTR_OFST_RATE_POLICY ;
2009-10-15 10:33:29 +03:00
desc - > tx_attr = cpu_to_le16 ( tx_attr ) ;
2009-08-06 16:25:28 +03:00
wl1271_debug ( DEBUG_TX , " tx_fill_hdr: pad: %d " , pad ) ;
return 0 ;
}
static int wl1271_tx_send_packet ( struct wl1271 * wl , struct sk_buff * skb ,
struct ieee80211_tx_info * control )
{
struct wl1271_tx_hw_descr * desc ;
int len ;
/* FIXME: This is a workaround for getting non-aligned packets.
This happens at least with EAPOL packets from the user space .
Our DMA requires packets to be aligned on a 4 - byte boundary .
*/
if ( unlikely ( ( long ) skb - > data & 0x03 ) ) {
int offset = ( 4 - ( long ) skb - > data ) & 0x03 ;
wl1271_debug ( DEBUG_TX , " skb offset %d " , offset ) ;
/* check whether the current skb can be used */
if ( ! skb_cloned ( skb ) & & ( skb_tailroom ( skb ) > = offset ) ) {
unsigned char * src = skb - > data ;
/* align the buffer on a 4-byte boundary */
skb_reserve ( skb , offset ) ;
memmove ( skb - > data , src , skb - > len ) ;
} else {
wl1271_info ( " No handler, fixme! " ) ;
return - EINVAL ;
}
}
len = WL1271_TX_ALIGN ( skb - > len ) ;
/* perform a fixed address block write with the packet */
2010-02-18 13:25:55 +02:00
wl1271_write ( wl , WL1271_SLV_MEM_DATA , skb - > data , len , true ) ;
2009-08-06 16:25:28 +03:00
/* write packet new counter into the write access register */
wl - > tx_packets_count + + ;
desc = ( struct wl1271_tx_hw_descr * ) skb - > data ;
wl1271_debug ( DEBUG_TX , " tx id %u skb 0x%p payload %u (%u words) " ,
desc - > id , skb , len , desc - > length ) ;
return 0 ;
}
/* caller must hold wl->mutex */
static int wl1271_tx_frame ( struct wl1271 * wl , struct sk_buff * skb )
{
struct ieee80211_tx_info * info ;
u32 extra = 0 ;
int ret = 0 ;
u8 idx ;
if ( ! skb )
return - EINVAL ;
info = IEEE80211_SKB_CB ( skb ) ;
if ( info - > control . hw_key & &
2010-08-10 09:46:38 +02:00
info - > control . hw_key - > cipher = = WLAN_CIPHER_SUITE_TKIP )
2009-08-06 16:25:28 +03:00
extra = WL1271_TKIP_IV_SPACE ;
if ( info - > control . hw_key ) {
idx = info - > control . hw_key - > hw_key_idx ;
/* FIXME: do we have to do this if we're not using WEP? */
if ( unlikely ( wl - > default_key ! = idx ) ) {
ret = wl1271_cmd_set_default_wep_key ( wl , idx ) ;
if ( ret < 0 )
return ret ;
2010-02-18 13:25:50 +02:00
wl - > default_key = idx ;
2009-08-06 16:25:28 +03:00
}
}
ret = wl1271_tx_allocate ( wl , skb , extra ) ;
if ( ret < 0 )
return ret ;
ret = wl1271_tx_fill_hdr ( wl , skb , extra , info ) ;
if ( ret < 0 )
return ret ;
ret = wl1271_tx_send_packet ( wl , skb , info ) ;
if ( ret < 0 )
return ret ;
return ret ;
}
2010-04-01 11:38:20 +03:00
u32 wl1271_tx_enabled_rates_get ( struct wl1271 * wl , u32 rate_set )
2009-12-11 15:41:06 +02:00
{
struct ieee80211_supported_band * band ;
u32 enabled_rates = 0 ;
int bit ;
band = wl - > hw - > wiphy - > bands [ wl - > band ] ;
for ( bit = 0 ; bit < band - > n_bitrates ; bit + + ) {
if ( rate_set & 0x1 )
enabled_rates | = band - > bitrates [ bit ] . hw_value ;
rate_set > > = 1 ;
}
return enabled_rates ;
}
2009-08-06 16:25:28 +03:00
void wl1271_tx_work ( struct work_struct * work )
{
struct wl1271 * wl = container_of ( work , struct wl1271 , tx_work ) ;
struct sk_buff * skb ;
bool woken_up = false ;
2009-12-11 15:41:06 +02:00
u32 sta_rates = 0 ;
2010-02-22 08:38:31 +02:00
u32 prev_tx_packets_count ;
2009-08-06 16:25:28 +03:00
int ret ;
2009-12-11 15:41:06 +02:00
/* check if the rates supported by the AP have changed */
if ( unlikely ( test_and_clear_bit ( WL1271_FLAG_STA_RATES_CHANGED ,
& wl - > flags ) ) ) {
unsigned long flags ;
spin_lock_irqsave ( & wl - > wl_lock , flags ) ;
sta_rates = wl - > sta_rate_set ;
spin_unlock_irqrestore ( & wl - > wl_lock , flags ) ;
}
2009-08-06 16:25:28 +03:00
mutex_lock ( & wl - > mutex ) ;
if ( unlikely ( wl - > state = = WL1271_STATE_OFF ) )
goto out ;
2010-02-22 08:38:31 +02:00
prev_tx_packets_count = wl - > tx_packets_count ;
2009-12-11 15:41:06 +02:00
/* if rates have changed, re-configure the rate policy */
if ( unlikely ( sta_rates ) ) {
wl - > rate_set = wl1271_tx_enabled_rates_get ( wl , sta_rates ) ;
wl1271_acx_rate_policies ( wl ) ;
}
2009-08-06 16:25:28 +03:00
while ( ( skb = skb_dequeue ( & wl - > tx_queue ) ) ) {
if ( ! woken_up ) {
ret = wl1271_ps_elp_wakeup ( wl , false ) ;
if ( ret < 0 )
2010-02-22 08:38:31 +02:00
goto out_ack ;
2009-08-06 16:25:28 +03:00
woken_up = true ;
}
ret = wl1271_tx_frame ( wl , skb ) ;
if ( ret = = - EBUSY ) {
2010-02-22 08:38:33 +02:00
/* firmware buffer is full, lets stop transmitting. */
2009-08-06 16:25:28 +03:00
skb_queue_head ( & wl - > tx_queue , skb ) ;
2010-02-22 08:38:31 +02:00
goto out_ack ;
2009-08-06 16:25:28 +03:00
} else if ( ret < 0 ) {
dev_kfree_skb ( skb ) ;
2010-02-22 08:38:31 +02:00
goto out_ack ;
2009-08-06 16:25:28 +03:00
}
}
2010-02-22 08:38:31 +02:00
out_ack :
/* interrupt the firmware with the new packets */
if ( prev_tx_packets_count ! = wl - > tx_packets_count )
wl1271_write32 ( wl , WL1271_HOST_WR_ACCESS , wl - > tx_packets_count ) ;
2009-08-06 16:25:28 +03:00
out :
if ( woken_up )
wl1271_ps_elp_sleep ( wl ) ;
mutex_unlock ( & wl - > mutex ) ;
}
static void wl1271_tx_complete_packet ( struct wl1271 * wl ,
struct wl1271_tx_hw_res_descr * result )
{
struct ieee80211_tx_info * info ;
struct sk_buff * skb ;
int id = result - > id ;
2010-03-26 12:53:12 +02:00
int rate = - 1 ;
u8 retries = 0 ;
2009-08-06 16:25:28 +03:00
/* check for id legality */
2010-02-22 08:38:31 +02:00
if ( unlikely ( id > = ACX_TX_DESCRIPTORS | | wl - > tx_frames [ id ] = = NULL ) ) {
2009-08-06 16:25:28 +03:00
wl1271_warning ( " TX result illegal id: %d " , id ) ;
return ;
}
skb = wl - > tx_frames [ id ] ;
info = IEEE80211_SKB_CB ( skb ) ;
2010-03-26 12:53:12 +02:00
/* update the TX status info */
if ( result - > status = = TX_SUCCESS ) {
if ( ! ( info - > flags & IEEE80211_TX_CTL_NO_ACK ) )
2009-08-06 16:25:28 +03:00
info - > flags | = IEEE80211_TX_STAT_ACK ;
2010-03-26 12:53:12 +02:00
rate = wl1271_rate_to_idx ( wl , result - > rate_class_index ) ;
retries = result - > ack_failures ;
} else if ( result - > status = = TX_RETRY_EXCEEDED ) {
wl - > stats . excessive_retries + + ;
retries = result - > ack_failures ;
2009-08-06 16:25:28 +03:00
}
2010-03-26 12:53:12 +02:00
info - > status . rates [ 0 ] . idx = rate ;
info - > status . rates [ 0 ] . count = retries ;
info - > status . rates [ 0 ] . flags = 0 ;
info - > status . ack_signal = - 1 ;
2009-08-06 16:25:28 +03:00
wl - > stats . retry_count + = result - > ack_failures ;
2009-10-08 21:56:19 +03:00
/* update security sequence number */
2010-02-22 08:38:40 +02:00
wl - > tx_security_seq + = ( result - > lsb_security_sequence_number -
wl - > tx_security_last_seq ) ;
2009-10-08 21:56:19 +03:00
wl - > tx_security_last_seq = result - > lsb_security_sequence_number ;
2009-10-08 21:56:20 +03:00
/* remove private header from packet */
skb_pull ( skb , sizeof ( struct wl1271_tx_hw_descr ) ) ;
/* remove TKIP header space if present */
2009-08-06 16:25:28 +03:00
if ( info - > control . hw_key & &
2010-08-10 09:46:38 +02:00
info - > control . hw_key - > cipher = = WLAN_CIPHER_SUITE_TKIP ) {
2009-10-08 21:56:20 +03:00
int hdrlen = ieee80211_get_hdrlen_from_skb ( skb ) ;
memmove ( skb - > data + WL1271_TKIP_IV_SPACE , skb - > data , hdrlen ) ;
skb_pull ( skb , WL1271_TKIP_IV_SPACE ) ;
}
2009-08-06 16:25:28 +03:00
wl1271_debug ( DEBUG_TX , " tx status id %u skb 0x%p failures %u rate 0x%x "
" status 0x%x " ,
result - > id , skb , result - > ack_failures ,
result - > rate_class_index , result - > status ) ;
/* return the packet to the stack */
ieee80211_tx_status ( wl - > hw , skb ) ;
wl - > tx_frames [ result - > id ] = NULL ;
2010-05-24 11:18:17 +03:00
wl - > tx_frames_cnt - - ;
2009-08-06 16:25:28 +03:00
}
/* Called upon reception of a TX complete interrupt */
2010-02-22 08:38:31 +02:00
void wl1271_tx_complete ( struct wl1271 * wl )
2009-08-06 16:25:28 +03:00
{
struct wl1271_acx_mem_map * memmap =
( struct wl1271_acx_mem_map * ) wl - > target_mem_map ;
2010-02-22 08:38:31 +02:00
u32 count , fw_counter ;
2009-08-06 16:25:28 +03:00
u32 i ;
/* read the tx results from the chipset */
2010-02-18 13:25:55 +02:00
wl1271_read ( wl , le32_to_cpu ( memmap - > tx_result ) ,
wl - > tx_res_if , sizeof ( * wl - > tx_res_if ) , false ) ;
2010-02-22 08:38:31 +02:00
fw_counter = le32_to_cpu ( wl - > tx_res_if - > tx_result_fw_counter ) ;
/* write host counter to chipset (to ack) */
wl1271_write32 ( wl , le32_to_cpu ( memmap - > tx_result ) +
offsetof ( struct wl1271_tx_hw_res_if ,
tx_result_host_counter ) , fw_counter ) ;
count = fw_counter - wl - > tx_results_count ;
2010-02-22 08:38:33 +02:00
wl1271_debug ( DEBUG_TX , " tx_complete received, packets: %d " , count ) ;
2009-08-06 16:25:28 +03:00
/* verify that the result buffer is not getting overrun */
2010-02-22 08:38:31 +02:00
if ( unlikely ( count > TX_HW_RESULT_QUEUE_LEN ) )
2009-08-06 16:25:28 +03:00
wl1271_warning ( " TX result overflow from chipset: %d " , count ) ;
/* process the results */
for ( i = 0 ; i < count ; i + + ) {
struct wl1271_tx_hw_res_descr * result ;
u8 offset = wl - > tx_results_count & TX_HW_RESULT_QUEUE_LEN_MASK ;
/* process the packet */
result = & ( wl - > tx_res_if - > tx_results_queue [ offset ] ) ;
wl1271_tx_complete_packet ( wl , result ) ;
wl - > tx_results_count + + ;
}
2010-02-22 08:38:33 +02:00
if ( test_bit ( WL1271_FLAG_TX_QUEUE_STOPPED , & wl - > flags ) & &
skb_queue_len ( & wl - > tx_queue ) < = WL1271_TX_QUEUE_LOW_WATERMARK ) {
unsigned long flags ;
/* firmware buffer has space, restart queues */
wl1271_debug ( DEBUG_TX , " tx_complete: waking queues " ) ;
spin_lock_irqsave ( & wl - > wl_lock , flags ) ;
ieee80211_wake_queues ( wl - > hw ) ;
clear_bit ( WL1271_FLAG_TX_QUEUE_STOPPED , & wl - > flags ) ;
spin_unlock_irqrestore ( & wl - > wl_lock , flags ) ;
ieee80211_queue_work ( wl - > hw , & wl - > tx_work ) ;
}
2009-08-06 16:25:28 +03:00
}
/* caller must hold wl->mutex */
2010-05-24 11:18:17 +03:00
void wl1271_tx_reset ( struct wl1271 * wl )
2009-08-06 16:25:28 +03:00
{
int i ;
struct sk_buff * skb ;
/* TX failure */
/* control->flags = 0; FIXME */
while ( ( skb = skb_dequeue ( & wl - > tx_queue ) ) ) {
2010-05-24 11:18:17 +03:00
wl1271_debug ( DEBUG_TX , " freeing skb 0x%p " , skb ) ;
2009-08-06 16:25:28 +03:00
ieee80211_tx_status ( wl - > hw , skb ) ;
}
2009-10-08 21:56:26 +03:00
for ( i = 0 ; i < ACX_TX_DESCRIPTORS ; i + + )
2009-08-06 16:25:28 +03:00
if ( wl - > tx_frames [ i ] ! = NULL ) {
skb = wl - > tx_frames [ i ] ;
wl - > tx_frames [ i ] = NULL ;
2010-05-24 11:18:17 +03:00
wl1271_debug ( DEBUG_TX , " freeing skb 0x%p " , skb ) ;
2010-04-01 11:38:24 +03:00
ieee80211_tx_status ( wl - > hw , skb ) ;
2009-08-06 16:25:28 +03:00
}
2010-05-24 11:18:17 +03:00
wl - > tx_frames_cnt = 0 ;
}
# define WL1271_TX_FLUSH_TIMEOUT 500000
/* caller must *NOT* hold wl->mutex */
void wl1271_tx_flush ( struct wl1271 * wl )
{
unsigned long timeout ;
timeout = jiffies + usecs_to_jiffies ( WL1271_TX_FLUSH_TIMEOUT ) ;
while ( ! time_after ( jiffies , timeout ) ) {
mutex_lock ( & wl - > mutex ) ;
wl1271_debug ( DEBUG_TX , " flushing tx buffer: %d " ,
wl - > tx_frames_cnt ) ;
if ( ( wl - > tx_frames_cnt = = 0 ) & &
skb_queue_empty ( & wl - > tx_queue ) ) {
mutex_unlock ( & wl - > mutex ) ;
return ;
}
mutex_unlock ( & wl - > mutex ) ;
msleep ( 1 ) ;
}
wl1271_warning ( " Unable to flush all TX buffers, timed out. " ) ;
2009-08-06 16:25:28 +03:00
}