2011-04-26 15:25:29 -07:00
/*
* This file contains the handling of TX in wlan driver .
*/
2011-06-06 10:43:46 +00:00
# include <linux/hardirq.h>
2007-02-10 12:25:27 -02:00
# include <linux/netdevice.h>
2007-12-10 00:49:26 -05:00
# include <linux/etherdevice.h>
2009-10-07 17:09:06 +04:00
# include <linux/sched.h>
2011-05-27 16:14:23 -04:00
# include <linux/export.h>
2010-06-14 22:01:26 +05:30
# include <net/cfg80211.h>
2007-02-10 12:25:27 -02:00
2009-10-16 17:32:16 +02:00
# include "host.h"
2007-02-10 12:25:27 -02:00
# include "radiotap.h"
# include "decl.h"
# include "defs.h"
# include "dev.h"
2011-07-21 20:43:17 +01:00
# include "mesh.h"
2007-02-10 12:25:27 -02:00
/**
2011-04-26 15:25:29 -07:00
* convert_radiotap_rate_to_mv - converts Tx / Rx rates from IEEE80211_RADIOTAP_RATE
* units ( 500 Kb / s ) into Marvell WLAN format ( see Table 8 in Section 3.2 .1 )
2007-02-10 12:25:27 -02:00
*
2011-04-26 15:25:29 -07:00
* @ rate : Input rate
* returns : Output Rate ( 0 if invalid )
2007-02-10 12:25:27 -02:00
*/
static u32 convert_radiotap_rate_to_mv ( u8 rate )
{
switch ( rate ) {
case 2 : /* 1 Mbps */
return 0 | ( 1 < < 4 ) ;
case 4 : /* 2 Mbps */
return 1 | ( 1 < < 4 ) ;
case 11 : /* 5.5 Mbps */
return 2 | ( 1 < < 4 ) ;
case 22 : /* 11 Mbps */
return 3 | ( 1 < < 4 ) ;
case 12 : /* 6 Mbps */
return 4 | ( 1 < < 4 ) ;
case 18 : /* 9 Mbps */
return 5 | ( 1 < < 4 ) ;
case 24 : /* 12 Mbps */
return 6 | ( 1 < < 4 ) ;
case 36 : /* 18 Mbps */
return 7 | ( 1 < < 4 ) ;
case 48 : /* 24 Mbps */
return 8 | ( 1 < < 4 ) ;
case 72 : /* 36 Mbps */
return 9 | ( 1 < < 4 ) ;
case 96 : /* 48 Mbps */
return 10 | ( 1 < < 4 ) ;
case 108 : /* 54 Mbps */
return 11 | ( 1 < < 4 ) ;
}
return 0 ;
}
/**
2011-04-26 15:25:29 -07:00
* lbs_hard_start_xmit - checks the conditions and sends packet to IF
* layer if everything is ok
2007-02-10 12:25:27 -02:00
*
2011-04-26 15:25:29 -07:00
* @ skb : A pointer to skb which includes TX packet
* @ dev : A pointer to the & struct net_device
* returns : 0 or - 1
2007-02-10 12:25:27 -02:00
*/
2009-08-31 19:50:57 +00:00
netdev_tx_t lbs_hard_start_xmit ( struct sk_buff * skb , struct net_device * dev )
2007-02-10 12:25:27 -02:00
{
2007-12-09 14:37:59 -05:00
unsigned long flags ;
2009-02-19 19:32:39 -05:00
struct lbs_private * priv = dev - > ml_priv ;
2007-12-09 14:37:59 -05:00
struct txpd * txpd ;
char * p802x_hdr ;
uint16_t pkt_len ;
2009-08-31 19:50:57 +00:00
netdev_tx_t ret = NETDEV_TX_OK ;
2007-02-10 12:25:27 -02:00
2007-05-25 11:27:16 -04:00
lbs_deb_enter ( LBS_DEB_TX ) ;
2007-02-10 12:25:27 -02:00
2007-12-09 23:54:27 -05:00
/* We need to protect against the queues being restarted before
we get round to stopping them */
spin_lock_irqsave ( & priv - > driver_lock , flags ) ;
2007-12-09 12:48:10 -05:00
2007-12-08 20:04:36 +00:00
if ( priv - > surpriseremoved )
2007-12-09 23:54:27 -05:00
goto free ;
2007-02-10 12:25:27 -02:00
if ( ! skb - > len | | ( skb - > len > MRVDRV_ETH_TX_PACKET_BUFFER_SIZE ) ) {
2007-05-25 11:27:16 -04:00
lbs_deb_tx ( " tx err: skb length %d 0 or > %zd \n " ,
2007-02-10 12:25:27 -02:00
skb - > len , MRVDRV_ETH_TX_PACKET_BUFFER_SIZE ) ;
2007-12-09 14:37:59 -05:00
/* We'll never manage to send this one; drop it and return 'OK' */
2007-12-09 23:54:27 -05:00
2009-03-20 19:36:36 +00:00
dev - > stats . tx_dropped + + ;
dev - > stats . tx_errors + + ;
2007-12-09 23:54:27 -05:00
goto free ;
}
netif_stop_queue ( priv - > dev ) ;
if ( priv - > mesh_dev )
netif_stop_queue ( priv - > mesh_dev ) ;
if ( priv - > tx_pending_len ) {
2007-12-14 22:53:41 -05:00
/* This can happen if packets come in on the mesh and eth
device simultaneously - - there ' s no mutual exclusion on
2007-12-09 23:54:27 -05:00
hard_start_xmit ( ) calls between devices . */
lbs_deb_tx ( " Packet on %s while busy \n " , dev - > name ) ;
ret = NETDEV_TX_BUSY ;
goto unlock ;
2007-02-10 12:25:27 -02:00
}
2007-12-09 23:54:27 -05:00
priv - > tx_pending_len = - 1 ;
spin_unlock_irqrestore ( & priv - > driver_lock , flags ) ;
2007-12-09 14:37:59 -05:00
lbs_deb_hex ( LBS_DEB_TX , " TX Data " , skb - > data , min_t ( unsigned int , skb - > len , 100 ) ) ;
2007-02-10 12:25:27 -02:00
2007-12-09 23:54:27 -05:00
txpd = ( void * ) priv - > tx_pending_buf ;
2007-12-09 14:37:59 -05:00
memset ( txpd , 0 , sizeof ( struct txpd ) ) ;
2007-02-10 12:25:27 -02:00
p802x_hdr = skb - > data ;
2007-12-09 14:37:59 -05:00
pkt_len = skb - > len ;
2007-02-10 12:25:27 -02:00
2010-06-14 22:01:26 +05:30
if ( priv - > wdev - > iftype = = NL80211_IFTYPE_MONITOR ) {
2007-12-09 14:37:59 -05:00
struct tx_radiotap_hdr * rtap_hdr = ( void * ) skb - > data ;
2007-02-10 12:25:27 -02:00
/* set txpd fields from the radiotap header */
2007-12-09 14:37:59 -05:00
txpd - > tx_control = cpu_to_le32 ( convert_radiotap_rate_to_mv ( rtap_hdr - > rate ) ) ;
2007-02-10 12:25:27 -02:00
/* skip the radiotap header */
2007-12-09 14:37:59 -05:00
p802x_hdr + = sizeof ( * rtap_hdr ) ;
pkt_len - = sizeof ( * rtap_hdr ) ;
2007-02-10 12:25:27 -02:00
2007-12-09 14:37:59 -05:00
/* copy destination address from 802.11 header */
memcpy ( txpd - > tx_dest_addr_high , p802x_hdr + 4 , ETH_ALEN ) ;
} else {
/* copy destination address from 802.3 header */
memcpy ( txpd - > tx_dest_addr_high , p802x_hdr , ETH_ALEN ) ;
2007-02-10 12:25:27 -02:00
}
2007-12-09 14:37:59 -05:00
txpd - > tx_packet_length = cpu_to_le16 ( pkt_len ) ;
txpd - > tx_packet_location = cpu_to_le32 ( sizeof ( struct txpd ) ) ;
2007-02-10 12:25:27 -02:00
2009-11-25 13:10:15 +01:00
lbs_mesh_set_txpd ( priv , dev , txpd ) ;
2007-02-10 12:25:27 -02:00
2007-12-09 14:37:59 -05:00
lbs_deb_hex ( LBS_DEB_TX , " txpd " , ( u8 * ) & txpd , sizeof ( struct txpd ) ) ;
2007-02-10 12:25:27 -02:00
2007-12-09 14:37:59 -05:00
lbs_deb_hex ( LBS_DEB_TX , " Tx Data " , ( u8 * ) p802x_hdr , le16_to_cpu ( txpd - > tx_packet_length ) ) ;
2007-02-10 12:25:27 -02:00
2007-12-09 14:37:59 -05:00
memcpy ( & txpd [ 1 ] , p802x_hdr , le16_to_cpu ( txpd - > tx_packet_length ) ) ;
2007-02-10 12:25:27 -02:00
2007-12-09 14:37:59 -05:00
spin_lock_irqsave ( & priv - > driver_lock , flags ) ;
2007-12-09 23:54:27 -05:00
priv - > tx_pending_len = pkt_len + sizeof ( struct txpd ) ;
2007-02-10 12:25:27 -02:00
2007-12-09 23:54:27 -05:00
lbs_deb_tx ( " %s lined up packet \n " , __func__ ) ;
2007-12-09 14:37:59 -05:00
2009-03-20 19:36:36 +00:00
dev - > stats . tx_packets + + ;
dev - > stats . tx_bytes + = skb - > len ;
2007-12-09 14:37:59 -05:00
2010-06-14 22:01:26 +05:30
if ( priv - > wdev - > iftype = = NL80211_IFTYPE_MONITOR ) {
2007-12-09 23:54:27 -05:00
/* Keep the skb to echo it back once Tx feedback is
received from FW */
skb_orphan ( skb ) ;
2007-12-08 19:46:19 +00:00
2007-12-09 23:54:27 -05:00
/* Keep the skb around for when we get feedback */
priv - > currenttxskb = skb ;
} else {
free :
2007-02-10 12:25:27 -02:00
dev_kfree_skb_any ( skb ) ;
}
2010-06-14 22:01:26 +05:30
2007-12-09 23:54:27 -05:00
unlock :
spin_unlock_irqrestore ( & priv - > driver_lock , flags ) ;
wake_up ( & priv - > waitq ) ;
2007-02-10 12:25:27 -02:00
2007-05-25 11:27:16 -04:00
lbs_deb_leave_args ( LBS_DEB_TX , " ret %d " , ret ) ;
2007-02-10 12:25:27 -02:00
return ret ;
}
/**
2011-04-26 15:25:29 -07:00
* lbs_send_tx_feedback - sends to the host the last transmitted packet ,
* filling the radiotap headers with transmission information .
2007-02-10 12:25:27 -02:00
*
2011-04-26 15:25:29 -07:00
* @ priv : A pointer to & struct lbs_private structure
* @ try_count : A 32 - bit value containing transmission retry status .
2007-02-10 12:25:27 -02:00
*
2011-04-26 15:25:29 -07:00
* returns : void
2007-02-10 12:25:27 -02:00
*/
2008-04-01 14:50:43 +02:00
void lbs_send_tx_feedback ( struct lbs_private * priv , u32 try_count )
2007-02-10 12:25:27 -02:00
{
struct tx_radiotap_hdr * radiotap_hdr ;
2010-07-22 14:21:02 +02:00
if ( priv - > wdev - > iftype ! = NL80211_IFTYPE_MONITOR | |
2010-06-14 22:01:26 +05:30
priv - > currenttxskb = = NULL )
2007-02-10 12:25:27 -02:00
return ;
2007-12-08 20:04:36 +00:00
radiotap_hdr = ( struct tx_radiotap_hdr * ) priv - > currenttxskb - > data ;
2007-02-10 12:25:27 -02:00
2008-04-01 14:50:43 +02:00
radiotap_hdr - > data_retries = try_count ?
( 1 + priv - > txretrycount - try_count ) : 0 ;
2007-12-10 00:49:26 -05:00
priv - > currenttxskb - > protocol = eth_type_trans ( priv - > currenttxskb ,
2010-06-14 22:01:26 +05:30
priv - > dev ) ;
2007-12-10 00:49:26 -05:00
netif_rx ( priv - > currenttxskb ) ;
2007-12-08 20:04:36 +00:00
priv - > currenttxskb = NULL ;
2007-11-20 17:44:14 -05:00
2007-12-08 20:04:36 +00:00
if ( priv - > connect_status = = LBS_CONNECTED )
2007-05-25 13:05:16 -04:00
netif_wake_queue ( priv - > dev ) ;
2007-11-20 17:44:14 -05:00
2011-07-20 17:53:56 +01:00
if ( priv - > mesh_dev & & netif_running ( priv - > mesh_dev ) )
2007-11-20 17:44:14 -05:00
netif_wake_queue ( priv - > mesh_dev ) ;
2007-02-10 12:25:27 -02:00
}
2007-11-15 18:05:47 -05:00
EXPORT_SYMBOL_GPL ( lbs_send_tx_feedback ) ;