@ -43,13 +43,17 @@ static int wl1271_tx_id(struct wl1271 *wl, struct sk_buff *skb)
return - EBUSY ;
}
static int wl1271_tx_allocate ( struct wl1271 * wl , struct sk_buff * skb , u32 extra )
static int wl1271_tx_allocate ( struct wl1271 * wl , struct sk_buff * skb , u32 extra ,
u32 buf_offset )
{
struct wl1271_tx_hw_descr * desc ;
u32 total_len = skb - > len + sizeof ( struct wl1271_tx_hw_descr ) + extra ;
u32 total_blocks ;
int id , ret = - EBUSY ;
if ( buf_offset + total_len > WL1271_AGGR_BUFFER_SIZE )
return - EBUSY ;
/* allocate free identifier for the packet */
id = wl1271_tx_id ( wl , skb ) ;
if ( id < 0 )
@ -82,7 +86,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra)
return ret ;
}
static int wl1271_tx_fill_hdr ( struct wl1271 * wl , struct sk_buff * skb ,
static void wl1271_tx_fill_hdr ( struct wl1271 * wl , struct sk_buff * skb ,
u32 extra , struct ieee80211_tx_info * control )
{
struct timespec ts ;
@ -133,59 +137,17 @@ static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
desc - > tx_attr = cpu_to_le16 ( tx_attr ) ;
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 */
wl1271_write ( wl , WL1271_SLV_MEM_DATA , skb - > data , len , true ) ;
/* 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 )
static int wl1271_prepare_tx_frame ( struct wl1271 * wl , struct sk_buff * skb ,
u32 buf_offset )
{
struct ieee80211_tx_info * info ;
u32 extra = 0 ;
int ret = 0 ;
u8 idx ;
u32 total_len ;
if ( ! skb )
return - EINVAL ;
@ -208,19 +170,22 @@ static int wl1271_tx_frame(struct wl1271 *wl, struct sk_buff *skb)
}
}
ret = wl1271_tx_allocate ( wl , skb , extra ) ;
ret = wl1271_tx_allocate ( wl , skb , extra , buf_offset ) ;
if ( ret < 0 )
return ret ;
ret = wl1271_tx_fill_hdr ( wl , skb , extra , info ) ;
if ( ret < 0 )
return ret ;
wl1271_tx_fill_hdr ( wl , skb , extra , info ) ;
ret = wl1271_tx_send_packet ( wl , skb , info ) ;
if ( ret < 0 )
return ret ;
/*
* The length of each packet is stored in terms of words . Thus , we must
* pad the skb data to make sure its length is aligned .
* The number of padding bytes is computed and set in wl1271_tx_fill_hdr
*/
total_len = WL1271_TX_ALIGN ( skb - > len ) ;
memcpy ( wl - > aggr_buf + buf_offset , skb - > data , skb - > len ) ;
memset ( wl - > aggr_buf + buf_offset + skb - > len , 0 , total_len - skb - > len ) ;
return ret ;
return total_len ;
}
u32 wl1271_tx_enabled_rates_get ( struct wl1271 * wl , u32 rate_set )
@ -245,7 +210,7 @@ void wl1271_tx_work(struct work_struct *work)
struct sk_buff * skb ;
bool woken_up = false ;
u32 sta_rates = 0 ;
u32 prev_tx_packets_coun t;
u32 buf_offse t;
int ret ;
/* check if the rates supported by the AP have changed */
@ -262,14 +227,15 @@ void wl1271_tx_work(struct work_struct *work)
if ( unlikely ( wl - > state = = WL1271_STATE_OFF ) )
goto out ;
prev_tx_packets_count = wl - > tx_packets_count ;
/* 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 ) ;
}
/* Prepare the transfer buffer, by aggregating all
* available packets */
buf_offset = 0 ;
while ( ( skb = skb_dequeue ( & wl - > tx_queue ) ) ) {
if ( ! woken_up ) {
ret = wl1271_ps_elp_wakeup ( wl , false ) ;
@ -278,21 +244,30 @@ void wl1271_tx_work(struct work_struct *work)
woken_up = true ;
}
ret = wl1271_tx_frame ( wl , skb ) ;
ret = wl1271_prepare_ tx_frame ( wl , skb , buf_offset ) ;
if ( ret = = - EBUSY ) {
/* firmware buffer is full, lets stop transmitting. */
/*
* Either the firmware buffer is full , or the
* aggregation buffer is .
* Queue back last skb , and stop aggregating .
*/
skb_queue_head ( & wl - > tx_queue , skb ) ;
goto out_ack ;
} else if ( ret < 0 ) {
dev_kfree_skb ( skb ) ;
goto out_ack ;
}
buf_offset + = ret ;
wl - > tx_packets_count + + ;
}
out_ack :
/* interrupt the firmware with the new packets */
if ( prev_tx_packets_count ! = wl - > tx_packets_count )
if ( buf_offset ) {
wl1271_write ( wl , WL1271_SLV_MEM_DATA , wl - > aggr_buf ,
buf_offset , true ) ;
/* interrupt the firmware with the new packets */
wl1271_write32 ( wl , WL1271_HOST_WR_ACCESS , wl - > tx_packets_count ) ;
}
out :
if ( woken_up )