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"
# include "wl1271_spi.h"
# 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 ;
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 ;
u32 total_blocks , excluded ;
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 */
/* FIXME: try to figure out what is done here and make it cleaner */
2009-10-12 15:08:53 +03:00
total_blocks = ( total_len + 20 ) > > TX_HW_BLOCK_SHIFT_DIV ;
excluded = ( total_blocks < < 2 ) + ( ( total_len + 20 ) & 0xff ) + 34 ;
2009-08-06 16:25:28 +03:00
total_blocks + = ( excluded > 252 ) ? 2 : 1 ;
total_blocks + = TX_HW_BLOCK_SPARE ;
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 ) ;
} else
wl - > tx_frames [ id ] = NULL ;
return ret ;
}
static int wl1271_tx_fill_hdr ( struct wl1271 * wl , struct sk_buff * skb ,
u32 extra , struct ieee80211_tx_info * control )
{
struct wl1271_tx_hw_descr * desc ;
int pad ;
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 */
2009-10-15 10:33:29 +03:00
desc - > start_time = cpu_to_le32 ( jiffies_to_usecs ( jiffies ) -
wl - > time_offset ) ;
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 ;
2009-08-06 16:25:28 +03:00
/* FIXME: do we know the packet priority? can we identify mgmt
packets , and use max prio for them at least ? */
desc - > tid = 0 ;
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 ;
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 */
2009-10-12 15:08:54 +03:00
wl1271_spi_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 + + ;
2009-10-12 15:08:54 +03:00
wl1271_spi_write32 ( wl , WL1271_HOST_WR_ACCESS , wl - > tx_packets_count ) ;
2009-08-06 16:25:28 +03:00
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 & &
info - > control . hw_key - > alg = = ALG_TKIP )
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 ;
}
}
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 ;
}
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 ;
int ret ;
mutex_lock ( & wl - > mutex ) ;
if ( unlikely ( wl - > state = = WL1271_STATE_OFF ) )
goto out ;
while ( ( skb = skb_dequeue ( & wl - > tx_queue ) ) ) {
if ( ! woken_up ) {
ret = wl1271_ps_elp_wakeup ( wl , false ) ;
if ( ret < 0 )
goto out ;
woken_up = true ;
}
ret = wl1271_tx_frame ( wl , skb ) ;
if ( ret = = - EBUSY ) {
/* firmware buffer is full, stop queues */
wl1271_debug ( DEBUG_TX , " tx_work: fw buffer full, "
" stop queues " ) ;
ieee80211_stop_queues ( wl - > hw ) ;
wl - > tx_queue_stopped = true ;
skb_queue_head ( & wl - > tx_queue , skb ) ;
goto out ;
} else if ( ret < 0 ) {
dev_kfree_skb ( skb ) ;
goto out ;
} else if ( wl - > tx_queue_stopped ) {
/* firmware buffer has space, restart queues */
wl1271_debug ( DEBUG_TX ,
" complete_packet: waking queues " ) ;
ieee80211_wake_queues ( wl - > hw ) ;
wl - > tx_queue_stopped = false ;
}
}
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 ;
2009-10-08 21:56:19 +03:00
u16 seq ;
2009-08-06 16:25:28 +03:00
int id = result - > id ;
/* check for id legality */
2009-10-08 21:56:26 +03:00
if ( 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 ) ;
/* update packet status */
if ( ! ( info - > flags & IEEE80211_TX_CTL_NO_ACK ) ) {
if ( result - > status = = TX_SUCCESS )
info - > flags | = IEEE80211_TX_STAT_ACK ;
if ( result - > status & TX_RETRY_EXCEEDED ) {
/* FIXME */
/* info->status.excessive_retries = 1; */
wl - > stats . excessive_retries + + ;
}
}
/* FIXME */
/* info->status.retry_count = result->ack_failures; */
wl - > stats . retry_count + = result - > ack_failures ;
2009-10-08 21:56:19 +03:00
/* update security sequence number */
seq = wl - > tx_security_seq_16 +
( result - > lsb_security_sequence_number -
wl - > tx_security_last_seq ) ;
wl - > tx_security_last_seq = result - > lsb_security_sequence_number ;
if ( seq < wl - > tx_security_seq_16 )
wl - > tx_security_seq_32 + + ;
wl - > tx_security_seq_16 = seq ;
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 & &
2009-10-08 21:56:20 +03:00
info - > control . hw_key - > alg = = ALG_TKIP ) {
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 ;
}
/* Called upon reception of a TX complete interrupt */
void wl1271_tx_complete ( struct wl1271 * wl , u32 count )
{
struct wl1271_acx_mem_map * memmap =
( struct wl1271_acx_mem_map * ) wl - > target_mem_map ;
u32 i ;
wl1271_debug ( DEBUG_TX , " tx_complete received, packets: %d " , count ) ;
/* read the tx results from the chipset */
2009-10-15 10:33:29 +03:00
wl1271_spi_read ( wl , le32_to_cpu ( memmap - > tx_result ) ,
2009-10-12 15:08:54 +03:00
wl - > tx_res_if , sizeof ( * wl - > tx_res_if ) , false ) ;
2009-08-06 16:25:28 +03:00
/* verify that the result buffer is not getting overrun */
if ( count > TX_HW_RESULT_QUEUE_LEN ) {
wl1271_warning ( " TX result overflow from chipset: %d " , count ) ;
count = TX_HW_RESULT_QUEUE_LEN ;
}
/* 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 + + ;
}
/* write host counter to chipset (to ack) */
2009-10-15 10:33:29 +03:00
wl1271_spi_write32 ( wl , le32_to_cpu ( memmap - > tx_result ) +
2009-08-06 16:25:28 +03:00
offsetof ( struct wl1271_tx_hw_res_if ,
tx_result_host_counter ) ,
2009-10-15 10:33:29 +03:00
le32_to_cpu ( wl - > tx_res_if - > tx_result_fw_counter ) ) ;
2009-08-06 16:25:28 +03:00
}
/* caller must hold wl->mutex */
void wl1271_tx_flush ( struct wl1271 * wl )
{
int i ;
struct sk_buff * skb ;
struct ieee80211_tx_info * info ;
/* TX failure */
/* control->flags = 0; FIXME */
while ( ( skb = skb_dequeue ( & wl - > tx_queue ) ) ) {
info = IEEE80211_SKB_CB ( skb ) ;
wl1271_debug ( DEBUG_TX , " flushing skb 0x%p " , skb ) ;
if ( ! ( info - > flags & IEEE80211_TX_CTL_REQ_TX_STATUS ) )
continue ;
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 ] ;
info = IEEE80211_SKB_CB ( skb ) ;
if ( ! ( info - > flags & IEEE80211_TX_CTL_REQ_TX_STATUS ) )
continue ;
ieee80211_tx_status ( wl - > hw , skb ) ;
wl - > tx_frames [ i ] = NULL ;
}
}