2011-03-21 18:00:50 -07:00
/*
* Marvell Wireless LAN device driver : 802.11 n Aggregation
*
2014-06-19 21:38:57 -07:00
* Copyright ( C ) 2011 - 2014 , Marvell International Ltd .
2011-03-21 18:00:50 -07:00
*
* This software file ( the " File " ) is distributed by Marvell International
* Ltd . under the terms of the GNU General Public License Version 2 , June 1991
* ( the " License " ) . You may use , redistribute and / or modify this File in
* accordance with the terms and conditions of the License , a copy of which
* is available by writing to the Free Software Foundation , Inc . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA or on the
* worldwide web at http : //www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* THE FILE IS DISTRIBUTED AS - IS , WITHOUT WARRANTY OF ANY KIND , AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED . The License provides additional details about
* this warranty disclaimer .
*/
# include "decl.h"
# include "ioctl.h"
# include "util.h"
# include "fw.h"
# include "main.h"
# include "wmm.h"
# include "11n.h"
# include "11n_aggr.h"
/*
* Creates an AMSDU subframe for aggregation into one AMSDU packet .
*
* The resultant AMSDU subframe format is -
*
* + - - - - ~ - - - - - + - - - - ~ - - - - - - + - - - - ~ - - - - - + - - - - - ~ - - - - - + - - - - ~ - - - - - +
* | DA | SA | Length | SNAP header | MSDU |
* | data [ 0. .5 ] | data [ 6. .11 ] | | | data [ 14. . ] |
* + - - - - ~ - - - - - + - - - - ~ - - - - - - + - - - - ~ - - - - - + - - - - - ~ - - - - - + - - - - ~ - - - - - +
* < - - 6 - bytes - - > < - - 6 - bytes - - > < - - 2 - bytes - - > < - - 8 - bytes - - > < - - n - bytes - - >
*
* This function also computes the amount of padding required to make the
* buffer length multiple of 4 bytes .
*
* Data = > | DA | SA | SNAP - TYPE | . . . . . . . . . |
* MSDU = > | DA | SA | Length | SNAP | . . . . . . . . |
*/
static int
2011-04-13 17:27:08 -07:00
mwifiex_11n_form_amsdu_pkt ( struct sk_buff * skb_aggr ,
2011-03-21 18:00:50 -07:00
struct sk_buff * skb_src , int * pad )
{
int dt_offset ;
struct rfc_1042_hdr snap = {
0xaa , /* LLC DSAP */
0xaa , /* LLC SSAP */
0x03 , /* LLC CTRL */
{ 0x00 , 0x00 , 0x00 } , /* SNAP OUI */
0x0000 /* SNAP type */
/*
* This field will be overwritten
* later with ethertype
*/
} ;
2011-05-03 20:11:46 -07:00
struct tx_packet_hdr * tx_header ;
2011-03-21 18:00:50 -07:00
2012-08-03 18:05:59 -07:00
tx_header = ( void * ) skb_put ( skb_aggr , sizeof ( * tx_header ) ) ;
2011-03-21 18:00:50 -07:00
/* Copy DA and SA */
dt_offset = 2 * ETH_ALEN ;
memcpy ( & tx_header - > eth803_hdr , skb_src - > data , dt_offset ) ;
/* Copy SNAP header */
2013-12-02 23:17:49 -08:00
snap . snap_type = ( ( struct ethhdr * ) skb_src - > data ) - > h_proto ;
dt_offset + = sizeof ( __be16 ) ;
2011-03-21 18:00:50 -07:00
memcpy ( & tx_header - > rfc1042_hdr , & snap , sizeof ( struct rfc_1042_hdr ) ) ;
skb_pull ( skb_src , dt_offset ) ;
/* Update Length field */
tx_header - > eth803_hdr . h_proto = htons ( skb_src - > len + LLC_SNAP_LEN ) ;
/* Add payload */
2012-08-03 18:05:59 -07:00
memcpy ( skb_put ( skb_aggr , skb_src - > len ) , skb_src - > data , skb_src - > len ) ;
2012-08-03 18:06:01 -07:00
/* Add padding for new MSDU to start from 4 byte boundary */
* pad = ( 4 - ( ( unsigned long ) skb_aggr - > tail & 0x3 ) ) % 4 ;
2011-03-21 18:00:50 -07:00
return skb_aggr - > len + * pad ;
}
/*
* Adds TxPD to AMSDU header .
*
* Each AMSDU packet will contain one TxPD at the beginning ,
* followed by multiple AMSDU subframes .
*/
static void
mwifiex_11n_form_amsdu_txpd ( struct mwifiex_private * priv ,
struct sk_buff * skb )
{
struct txpd * local_tx_pd ;
2014-05-21 22:02:30 -07:00
struct mwifiex_txinfo * tx_info = MWIFIEX_SKB_TXCB ( skb ) ;
2011-03-21 18:00:50 -07:00
skb_push ( skb , sizeof ( * local_tx_pd ) ) ;
local_tx_pd = ( struct txpd * ) skb - > data ;
memset ( local_tx_pd , 0 , sizeof ( struct txpd ) ) ;
/* Original priority has been overwritten */
local_tx_pd - > priority = ( u8 ) skb - > priority ;
local_tx_pd - > pkt_delay_2ms =
mwifiex_wmm_compute_drv_pkt_delay ( priv , skb ) ;
local_tx_pd - > bss_num = priv - > bss_num ;
local_tx_pd - > bss_type = priv - > bss_type ;
/* Always zero as the data is followed by struct txpd */
local_tx_pd - > tx_pkt_offset = cpu_to_le16 ( sizeof ( struct txpd ) ) ;
local_tx_pd - > tx_pkt_type = cpu_to_le16 ( PKT_TYPE_AMSDU ) ;
local_tx_pd - > tx_pkt_length = cpu_to_le16 ( skb - > len -
2012-03-13 19:22:34 -07:00
sizeof ( * local_tx_pd ) ) ;
2011-03-21 18:00:50 -07:00
2014-05-21 22:02:30 -07:00
if ( tx_info - > flags & MWIFIEX_BUF_FLAG_TDLS_PKT )
local_tx_pd - > flags | = MWIFIEX_TXPD_FLAGS_TDLS_PACKET ;
2011-03-21 18:00:50 -07:00
if ( local_tx_pd - > tx_control = = 0 )
/* TxCtrl set by user or default */
local_tx_pd - > tx_control = cpu_to_le32 ( priv - > pkt_tx_ctrl ) ;
2012-03-13 19:22:34 -07:00
if ( GET_BSS_ROLE ( priv ) = = MWIFIEX_BSS_ROLE_STA & &
priv - > adapter - > pps_uapsd_mode ) {
2011-03-21 18:00:50 -07:00
if ( true = = mwifiex_check_last_packet_indication ( priv ) ) {
priv - > adapter - > tx_lock_flag = true ;
local_tx_pd - > flags =
MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET ;
}
}
}
/*
* Create aggregated packet .
*
* This function creates an aggregated MSDU packet , by combining buffers
* from the RA list . Each individual buffer is encapsulated as an AMSDU
* subframe and all such subframes are concatenated together to form the
* AMSDU packet .
*
* A TxPD is also added to the front of the resultant AMSDU packets for
* transmission . The resultant packets format is -
*
* + - - - - ~ - - - - + - - - - - - ~ - - - - - - + - - - - - - ~ - - - - - - + - . . - + - - - - - - ~ - - - - - - +
* | TxPD | AMSDU sub - frame | AMSDU sub - frame | . . | AMSDU sub - frame |
* | | 1 | 2 | . . | n |
* + - - - - ~ - - - - + - - - - - - ~ - - - - - - + - - - - - - ~ - - - - - - + . . + - - - - - - ~ - - - - - - +
*/
int
mwifiex_11n_aggregate_pkt ( struct mwifiex_private * priv ,
2013-09-24 19:31:24 -07:00
struct mwifiex_ra_list_tbl * pra_list ,
2011-03-21 18:00:50 -07:00
int ptrindex , unsigned long ra_list_flags )
__releases ( & priv - > wmm . ra_list_spinlock )
{
struct mwifiex_adapter * adapter = priv - > adapter ;
struct sk_buff * skb_aggr , * skb_src ;
struct mwifiex_txinfo * tx_info_aggr , * tx_info_src ;
2011-05-03 20:11:46 -07:00
int pad = 0 , ret ;
2011-03-21 18:00:50 -07:00
struct mwifiex_tx_param tx_param ;
struct txpd * ptx_pd = NULL ;
2014-04-14 15:32:53 -07:00
struct timeval tv ;
2013-09-24 19:31:24 -07:00
int headroom = adapter - > iface_type = = MWIFIEX_USB ? 0 : INTF_HEADER_LEN ;
2011-03-21 18:00:50 -07:00
2011-06-06 14:50:58 +05:30
skb_src = skb_peek ( & pra_list - > skb_head ) ;
if ( ! skb_src ) {
2011-03-21 18:00:50 -07:00
spin_unlock_irqrestore ( & priv - > wmm . ra_list_spinlock ,
ra_list_flags ) ;
return 0 ;
}
2011-06-06 14:50:58 +05:30
2011-03-21 18:00:50 -07:00
tx_info_src = MWIFIEX_SKB_TXCB ( skb_src ) ;
skb_aggr = dev_alloc_skb ( adapter - > tx_buf_size ) ;
if ( ! skb_aggr ) {
dev_err ( adapter - > dev , " %s: alloc skb_aggr \n " , __func__ ) ;
spin_unlock_irqrestore ( & priv - > wmm . ra_list_spinlock ,
ra_list_flags ) ;
return - 1 ;
}
skb_reserve ( skb_aggr , headroom + sizeof ( struct txpd ) ) ;
tx_info_aggr = MWIFIEX_SKB_TXCB ( skb_aggr ) ;
2014-07-01 14:39:04 -07:00
memset ( tx_info_aggr , 0 , sizeof ( * tx_info_aggr ) ) ;
2012-01-11 20:06:11 -08:00
tx_info_aggr - > bss_type = tx_info_src - > bss_type ;
tx_info_aggr - > bss_num = tx_info_src - > bss_num ;
2014-05-21 22:02:30 -07:00
if ( tx_info_src - > flags & MWIFIEX_BUF_FLAG_TDLS_PKT )
tx_info_aggr - > flags | = MWIFIEX_BUF_FLAG_TDLS_PKT ;
2011-03-21 18:00:50 -07:00
skb_aggr - > priority = skb_src - > priority ;
2014-04-14 15:32:53 -07:00
do_gettimeofday ( & tv ) ;
skb_aggr - > tstamp = timeval_to_ktime ( tv ) ;
2011-06-06 14:53:02 +05:30
do {
/* Check if AMSDU can accommodate this MSDU */
if ( skb_tailroom ( skb_aggr ) < ( skb_src - > len + LLC_SNAP_LEN ) )
break ;
2011-03-21 18:00:50 -07:00
2011-06-06 14:50:58 +05:30
skb_src = skb_dequeue ( & pra_list - > skb_head ) ;
2011-03-21 18:00:50 -07:00
2013-07-22 19:17:40 -07:00
pra_list - > total_pkt_count - - ;
2011-03-21 18:00:50 -07:00
2011-05-16 19:17:49 -07:00
atomic_dec ( & priv - > wmm . tx_pkts_queued ) ;
2011-03-21 18:00:50 -07:00
spin_unlock_irqrestore ( & priv - > wmm . ra_list_spinlock ,
ra_list_flags ) ;
2011-04-13 17:27:08 -07:00
mwifiex_11n_form_amsdu_pkt ( skb_aggr , skb_src , & pad ) ;
2011-03-21 18:00:50 -07:00
2012-11-01 18:44:16 -07:00
mwifiex_write_data_complete ( adapter , skb_src , 0 , 0 ) ;
2011-03-21 18:00:50 -07:00
spin_lock_irqsave ( & priv - > wmm . ra_list_spinlock , ra_list_flags ) ;
if ( ! mwifiex_is_ralist_valid ( priv , pra_list , ptrindex ) ) {
spin_unlock_irqrestore ( & priv - > wmm . ra_list_spinlock ,
ra_list_flags ) ;
return - 1 ;
}
2011-06-06 14:53:02 +05:30
if ( skb_tailroom ( skb_aggr ) < pad ) {
pad = 0 ;
break ;
}
skb_put ( skb_aggr , pad ) ;
2011-06-06 14:50:58 +05:30
skb_src = skb_peek ( & pra_list - > skb_head ) ;
2011-06-06 14:53:02 +05:30
} while ( skb_src ) ;
2011-03-21 18:00:50 -07:00
spin_unlock_irqrestore ( & priv - > wmm . ra_list_spinlock , ra_list_flags ) ;
/* Last AMSDU packet does not need padding */
skb_trim ( skb_aggr , skb_aggr - > len - pad ) ;
/* Form AMSDU */
mwifiex_11n_form_amsdu_txpd ( priv , skb_aggr ) ;
if ( GET_BSS_ROLE ( priv ) = = MWIFIEX_BSS_ROLE_STA )
ptx_pd = ( struct txpd * ) skb_aggr - > data ;
skb_push ( skb_aggr , headroom ) ;
2012-04-18 20:08:28 -07:00
if ( adapter - > iface_type = = MWIFIEX_USB ) {
adapter - > data_sent = true ;
ret = adapter - > if_ops . host_to_card ( adapter , MWIFIEX_USB_EP_DATA ,
skb_aggr , NULL ) ;
} else {
2014-04-14 15:32:51 -07:00
if ( skb_src )
tx_param . next_pkt_len =
skb_src - > len + sizeof ( struct txpd ) ;
else
tx_param . next_pkt_len = 0 ;
2012-04-18 20:08:28 -07:00
ret = adapter - > if_ops . host_to_card ( adapter , MWIFIEX_TYPE_DATA ,
skb_aggr , & tx_param ) ;
}
2011-03-21 18:00:50 -07:00
switch ( ret ) {
case - EBUSY :
spin_lock_irqsave ( & priv - > wmm . ra_list_spinlock , ra_list_flags ) ;
if ( ! mwifiex_is_ralist_valid ( priv , pra_list , ptrindex ) ) {
spin_unlock_irqrestore ( & priv - > wmm . ra_list_spinlock ,
ra_list_flags ) ;
2012-11-01 18:44:16 -07:00
mwifiex_write_data_complete ( adapter , skb_aggr , 1 , - 1 ) ;
2011-03-21 18:00:50 -07:00
return - 1 ;
}
2012-03-13 19:22:34 -07:00
if ( GET_BSS_ROLE ( priv ) = = MWIFIEX_BSS_ROLE_STA & &
adapter - > pps_uapsd_mode & & adapter - > tx_lock_flag ) {
2011-03-21 18:00:50 -07:00
priv - > adapter - > tx_lock_flag = false ;
2011-05-08 22:50:09 +02:00
if ( ptx_pd )
ptx_pd - > flags = 0 ;
2011-03-21 18:00:50 -07:00
}
skb_queue_tail ( & pra_list - > skb_head , skb_aggr ) ;
2013-07-22 19:17:40 -07:00
pra_list - > total_pkt_count + + ;
2011-03-21 18:00:50 -07:00
2011-05-16 19:17:49 -07:00
atomic_inc ( & priv - > wmm . tx_pkts_queued ) ;
2011-03-21 18:00:50 -07:00
tx_info_aggr - > flags | = MWIFIEX_BUF_FLAG_REQUEUED_PKT ;
spin_unlock_irqrestore ( & priv - > wmm . ra_list_spinlock ,
ra_list_flags ) ;
dev_dbg ( adapter - > dev , " data: -EBUSY is returned \n " ) ;
break ;
case - 1 :
2013-01-03 21:21:32 -08:00
if ( adapter - > iface_type ! = MWIFIEX_PCIE )
adapter - > data_sent = false ;
2011-03-21 18:00:50 -07:00
dev_err ( adapter - > dev , " %s: host_to_card failed: %#x \n " ,
2012-03-13 19:22:34 -07:00
__func__ , ret ) ;
2011-03-21 18:00:50 -07:00
adapter - > dbg . num_tx_host_to_card_failure + + ;
2012-11-01 18:44:16 -07:00
mwifiex_write_data_complete ( adapter , skb_aggr , 1 , ret ) ;
2011-03-21 18:00:50 -07:00
return 0 ;
case - EINPROGRESS :
2013-01-03 21:21:32 -08:00
if ( adapter - > iface_type ! = MWIFIEX_PCIE )
adapter - > data_sent = false ;
2011-03-21 18:00:50 -07:00
break ;
case 0 :
2012-11-01 18:44:16 -07:00
mwifiex_write_data_complete ( adapter , skb_aggr , 1 , ret ) ;
2011-03-21 18:00:50 -07:00
break ;
default :
break ;
}
if ( ret ! = - EBUSY ) {
2013-04-18 16:33:45 -07:00
mwifiex_rotate_priolists ( priv , pra_list , ptrindex ) ;
2011-03-21 18:00:50 -07:00
}
return 0 ;
}