2007-09-18 15:39:42 -04:00
/*
Broadcom B43 wireless driver
Transmission ( TX / RX ) related functions .
Copyright ( C ) 2005 Martin Langer < martin - langer @ gmx . de >
2007-11-06 22:49:20 +01:00
Copyright ( C ) 2005 Stefano Brivio < stefano . brivio @ polimi . it >
2007-09-18 15:39:42 -04:00
Copyright ( C ) 2005 , 2006 Michael Buesch < mb @ bu3sch . de >
Copyright ( C ) 2005 Danny van Dyk < kugelfang @ gentoo . org >
Copyright ( C ) 2005 Andreas Jaggi < andreas . jaggi @ waterwave . ch >
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
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 ; see the file COPYING . If not , write to
the Free Software Foundation , Inc . , 51 Franklin Steet , Fifth Floor ,
Boston , MA 02110 - 1301 , USA .
*/
2009-10-09 20:33:32 +02:00
# include "xmit.h"
2008-08-27 18:53:02 +02:00
# include "phy_common.h"
2007-09-18 15:39:42 -04:00
# include "dma.h"
2008-03-29 21:01:16 +01:00
# include "pio.h"
2007-12-26 14:41:30 +01:00
2007-09-18 15:39:42 -04:00
2008-01-24 19:38:38 +01:00
/* Extract the bitrate index out of a CCK PLCP header. */
static int b43_plcp_get_bitrate_idx_cck ( struct b43_plcp_hdr6 * plcp )
2007-09-18 15:39:42 -04:00
{
switch ( plcp - > raw [ 0 ] ) {
case 0x0A :
2008-01-24 19:38:38 +01:00
return 0 ;
2007-09-18 15:39:42 -04:00
case 0x14 :
2008-01-24 19:38:38 +01:00
return 1 ;
2007-09-18 15:39:42 -04:00
case 0x37 :
2008-01-24 19:38:38 +01:00
return 2 ;
2007-09-18 15:39:42 -04:00
case 0x6E :
2008-01-24 19:38:38 +01:00
return 3 ;
2007-09-18 15:39:42 -04:00
}
2008-01-24 19:38:38 +01:00
return - 1 ;
2007-09-18 15:39:42 -04:00
}
2008-01-24 19:38:38 +01:00
/* Extract the bitrate index out of an OFDM PLCP header. */
2009-03-22 19:15:41 +01:00
static int b43_plcp_get_bitrate_idx_ofdm ( struct b43_plcp_hdr6 * plcp , bool aphy )
2007-09-18 15:39:42 -04:00
{
2008-01-24 19:38:38 +01:00
int base = aphy ? 0 : 4 ;
2007-09-18 15:39:42 -04:00
switch ( plcp - > raw [ 0 ] & 0xF ) {
case 0xB :
2008-01-24 19:38:38 +01:00
return base + 0 ;
2007-09-18 15:39:42 -04:00
case 0xF :
2008-01-24 19:38:38 +01:00
return base + 1 ;
2007-09-18 15:39:42 -04:00
case 0xA :
2008-01-24 19:38:38 +01:00
return base + 2 ;
2007-09-18 15:39:42 -04:00
case 0xE :
2008-01-24 19:38:38 +01:00
return base + 3 ;
2007-09-18 15:39:42 -04:00
case 0x9 :
2008-01-24 19:38:38 +01:00
return base + 4 ;
2007-09-18 15:39:42 -04:00
case 0xD :
2008-01-24 19:38:38 +01:00
return base + 5 ;
2007-09-18 15:39:42 -04:00
case 0x8 :
2008-01-24 19:38:38 +01:00
return base + 6 ;
2007-09-18 15:39:42 -04:00
case 0xC :
2008-01-24 19:38:38 +01:00
return base + 7 ;
2007-09-18 15:39:42 -04:00
}
2008-01-24 19:38:38 +01:00
return - 1 ;
2007-09-18 15:39:42 -04:00
}
u8 b43_plcp_get_ratecode_cck ( const u8 bitrate )
{
switch ( bitrate ) {
case B43_CCK_RATE_1MB :
return 0x0A ;
case B43_CCK_RATE_2MB :
return 0x14 ;
case B43_CCK_RATE_5MB :
return 0x37 ;
case B43_CCK_RATE_11MB :
return 0x6E ;
}
B43_WARN_ON ( 1 ) ;
return 0 ;
}
u8 b43_plcp_get_ratecode_ofdm ( const u8 bitrate )
{
switch ( bitrate ) {
case B43_OFDM_RATE_6MB :
return 0xB ;
case B43_OFDM_RATE_9MB :
return 0xF ;
case B43_OFDM_RATE_12MB :
return 0xA ;
case B43_OFDM_RATE_18MB :
return 0xE ;
case B43_OFDM_RATE_24MB :
return 0x9 ;
case B43_OFDM_RATE_36MB :
return 0xD ;
case B43_OFDM_RATE_48MB :
return 0x8 ;
case B43_OFDM_RATE_54MB :
return 0xC ;
}
B43_WARN_ON ( 1 ) ;
return 0 ;
}
void b43_generate_plcp_hdr ( struct b43_plcp_hdr4 * plcp ,
const u16 octets , const u8 bitrate )
{
__u8 * raw = plcp - > raw ;
if ( b43_is_ofdm_rate ( bitrate ) ) {
2007-09-20 11:13:40 -07:00
u32 d ;
d = b43_plcp_get_ratecode_ofdm ( bitrate ) ;
2007-09-18 15:39:42 -04:00
B43_WARN_ON ( octets & 0xF000 ) ;
2007-09-20 11:13:40 -07:00
d | = ( octets < < 5 ) ;
2009-06-04 23:18:33 +02:00
plcp - > data = cpu_to_le32 ( d ) ;
2007-09-18 15:39:42 -04:00
} else {
u32 plen ;
plen = octets * 16 / bitrate ;
if ( ( octets * 16 % bitrate ) > 0 ) {
plen + + ;
if ( ( bitrate = = B43_CCK_RATE_11MB )
& & ( ( octets * 8 % 11 ) < 4 ) ) {
raw [ 1 ] = 0x84 ;
} else
raw [ 1 ] = 0x04 ;
} else
raw [ 1 ] = 0x04 ;
2009-06-04 23:18:33 +02:00
plcp - > data | = cpu_to_le32 ( plen < < 16 ) ;
2007-09-18 15:39:42 -04:00
raw [ 0 ] = b43_plcp_get_ratecode_cck ( bitrate ) ;
}
}
static u8 b43_calc_fallback_rate ( u8 bitrate )
{
switch ( bitrate ) {
case B43_CCK_RATE_1MB :
return B43_CCK_RATE_1MB ;
case B43_CCK_RATE_2MB :
return B43_CCK_RATE_1MB ;
case B43_CCK_RATE_5MB :
return B43_CCK_RATE_2MB ;
case B43_CCK_RATE_11MB :
return B43_CCK_RATE_5MB ;
case B43_OFDM_RATE_6MB :
return B43_CCK_RATE_5MB ;
case B43_OFDM_RATE_9MB :
return B43_OFDM_RATE_6MB ;
case B43_OFDM_RATE_12MB :
return B43_OFDM_RATE_9MB ;
case B43_OFDM_RATE_18MB :
return B43_OFDM_RATE_12MB ;
case B43_OFDM_RATE_24MB :
return B43_OFDM_RATE_18MB ;
case B43_OFDM_RATE_36MB :
return B43_OFDM_RATE_24MB ;
case B43_OFDM_RATE_48MB :
return B43_OFDM_RATE_36MB ;
case B43_OFDM_RATE_54MB :
return B43_OFDM_RATE_48MB ;
}
B43_WARN_ON ( 1 ) ;
return 0 ;
}
2008-01-28 14:47:41 -08:00
/* Generate a TX data header. */
2008-01-23 21:44:15 +01:00
int b43_generate_txhdr ( struct b43_wldev * dev ,
u8 * _txhdr ,
2009-08-19 22:35:45 +02:00
struct sk_buff * skb_frag ,
2008-10-21 12:40:02 +02:00
struct ieee80211_tx_info * info ,
2008-01-23 21:44:15 +01:00
u16 cookie )
2007-09-18 15:39:42 -04:00
{
2009-08-19 22:35:45 +02:00
const unsigned char * fragment_data = skb_frag - > data ;
unsigned int fragment_len = skb_frag - > len ;
2008-01-28 14:47:41 -08:00
struct b43_txhdr * txhdr = ( struct b43_txhdr * ) _txhdr ;
2007-09-18 15:39:42 -04:00
const struct b43_phy * phy = & dev - > phy ;
const struct ieee80211_hdr * wlhdr =
( const struct ieee80211_hdr * ) fragment_data ;
2008-07-29 11:32:07 +02:00
int use_encryption = ! ! info - > control . hw_key ;
2008-06-14 23:33:39 -07:00
__le16 fctl = wlhdr - > frame_control ;
2008-01-24 19:38:38 +01:00
struct ieee80211_rate * fbrate ;
2007-09-18 15:39:42 -04:00
u8 rate , rate_fb ;
int rate_ofdm , rate_fb_ofdm ;
unsigned int plcp_fragment_len ;
u32 mac_ctl = 0 ;
u16 phy_ctl = 0 ;
u8 extra_ft = 0 ;
2008-05-15 12:55:27 +02:00
struct ieee80211_rate * txrate ;
2008-10-21 12:40:02 +02:00
struct ieee80211_tx_rate * rates ;
2007-09-18 15:39:42 -04:00
memset ( txhdr , 0 , sizeof ( * txhdr ) ) ;
2008-05-15 12:55:29 +02:00
txrate = ieee80211_get_tx_rate ( dev - > wl - > hw , info ) ;
2008-05-15 12:55:27 +02:00
rate = txrate ? txrate - > hw_value : B43_CCK_RATE_1MB ;
2007-09-18 15:39:42 -04:00
rate_ofdm = b43_is_ofdm_rate ( rate ) ;
2008-10-05 18:04:24 +02:00
fbrate = ieee80211_get_alt_retry_rate ( dev - > wl - > hw , info , 0 ) ? : txrate ;
2008-01-24 19:38:38 +01:00
rate_fb = fbrate - > hw_value ;
2007-09-18 15:39:42 -04:00
rate_fb_ofdm = b43_is_ofdm_rate ( rate_fb ) ;
if ( rate_ofdm )
txhdr - > phy_rate = b43_plcp_get_ratecode_ofdm ( rate ) ;
else
txhdr - > phy_rate = b43_plcp_get_ratecode_cck ( rate ) ;
txhdr - > mac_frame_ctl = wlhdr - > frame_control ;
memcpy ( txhdr - > tx_receiver , wlhdr - > addr1 , 6 ) ;
/* Calculate duration for fallback rate */
if ( ( rate_fb = = rate ) | |
( wlhdr - > duration_id & cpu_to_le16 ( 0x8000 ) ) | |
( wlhdr - > duration_id = = cpu_to_le16 ( 0 ) ) ) {
/* If the fallback rate equals the normal rate or the
* dur_id field contains an AID , CFP magic or 0 ,
* use the original dur_id field . */
txhdr - > dur_fb = wlhdr - > duration_id ;
} else {
2008-05-15 12:55:29 +02:00
txhdr - > dur_fb = ieee80211_generic_frame_duration (
dev - > wl - > hw , info - > control . vif , fragment_len , fbrate ) ;
2007-09-18 15:39:42 -04:00
}
plcp_fragment_len = fragment_len + FCS_LEN ;
if ( use_encryption ) {
2008-05-15 12:55:29 +02:00
u8 key_idx = info - > control . hw_key - > hw_key_idx ;
2007-09-18 15:39:42 -04:00
struct b43_key * key ;
int wlhdr_len ;
size_t iv_len ;
2009-08-06 10:36:50 +02:00
B43_WARN_ON ( key_idx > = ARRAY_SIZE ( dev - > key ) ) ;
2007-09-18 15:39:42 -04:00
key = & ( dev - > key [ key_idx ] ) ;
2008-01-23 21:10:56 +01:00
2008-01-23 21:44:15 +01:00
if ( unlikely ( ! key - > keyconf ) ) {
/* This key is invalid. This might only happen
* in a short timeframe after machine resume before
* we were able to reconfigure keys .
* Drop this packet completely . Do not transmit it
* unencrypted to avoid leaking information . */
return - ENOKEY ;
2008-01-23 21:10:56 +01:00
}
2008-01-23 21:44:15 +01:00
/* Hardware appends ICV. */
2008-10-05 18:02:48 +02:00
plcp_fragment_len + = info - > control . hw_key - > icv_len ;
2008-01-23 21:44:15 +01:00
key_idx = b43_kidx_to_fw ( dev , key_idx ) ;
mac_ctl | = ( key_idx < < B43_TXH_MAC_KEYIDX_SHIFT ) &
B43_TXH_MAC_KEYIDX ;
mac_ctl | = ( key - > algorithm < < B43_TXH_MAC_KEYALG_SHIFT ) &
B43_TXH_MAC_KEYALG ;
2008-06-14 23:33:39 -07:00
wlhdr_len = ieee80211_hdrlen ( fctl ) ;
2009-08-19 22:35:45 +02:00
if ( key - > algorithm = = B43_SEC_ALGO_TKIP ) {
u16 phase1key [ 5 ] ;
int i ;
/* we give the phase1key and iv16 here, the key is stored in
* shm . With that the hardware can do phase 2 and encryption .
*/
ieee80211_get_tkip_key ( info - > control . hw_key , skb_frag ,
IEEE80211_TKIP_P1_KEY , ( u8 * ) phase1key ) ;
2009-09-06 16:18:58 +02:00
/* phase1key is in host endian. Copy to little-endian txhdr->iv. */
for ( i = 0 ; i < 5 ; i + + ) {
txhdr - > iv [ i * 2 + 0 ] = phase1key [ i ] ;
txhdr - > iv [ i * 2 + 1 ] = phase1key [ i ] > > 8 ;
}
2009-08-19 22:35:45 +02:00
/* iv16 */
memcpy ( txhdr - > iv + 10 , ( ( u8 * ) wlhdr ) + wlhdr_len , 3 ) ;
} else {
iv_len = min ( ( size_t ) info - > control . hw_key - > iv_len ,
ARRAY_SIZE ( txhdr - > iv ) ) ;
memcpy ( txhdr - > iv , ( ( u8 * ) wlhdr ) + wlhdr_len , iv_len ) ;
}
2007-09-18 15:39:42 -04:00
}
2008-01-28 14:47:41 -08:00
if ( b43_is_old_txhdr_format ( dev ) ) {
b43_generate_plcp_hdr ( ( struct b43_plcp_hdr4 * ) ( & txhdr - > old_format . plcp ) ,
plcp_fragment_len , rate ) ;
} else {
b43_generate_plcp_hdr ( ( struct b43_plcp_hdr4 * ) ( & txhdr - > new_format . plcp ) ,
plcp_fragment_len , rate ) ;
}
2007-09-18 15:39:42 -04:00
b43_generate_plcp_hdr ( ( struct b43_plcp_hdr4 * ) ( & txhdr - > plcp_fb ) ,
plcp_fragment_len , rate_fb ) ;
/* Extra Frame Types */
if ( rate_fb_ofdm )
2008-01-28 14:47:41 -08:00
extra_ft | = B43_TXH_EFT_FB_OFDM ;
else
extra_ft | = B43_TXH_EFT_FB_CCK ;
2007-09-18 15:39:42 -04:00
/* Set channel radio code. Note that the micrcode ORs 0x100 to
* this value before comparing it to the value in SHM , if this
* is a 5 Ghz packet .
*/
txhdr - > chan_radio_code = phy - > channel ;
/* PHY TX Control word */
if ( rate_ofdm )
2008-01-28 14:47:41 -08:00
phy_ctl | = B43_TXH_PHY_ENC_OFDM ;
else
phy_ctl | = B43_TXH_PHY_ENC_CCK ;
2008-10-21 12:40:02 +02:00
if ( info - > control . rates [ 0 ] . flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE )
2008-01-28 14:47:41 -08:00
phy_ctl | = B43_TXH_PHY_SHORTPRMBL ;
2007-12-22 21:54:20 +01:00
2008-05-15 12:55:29 +02:00
switch ( b43_ieee80211_antenna_sanitize ( dev , info - > antenna_sel_tx ) ) {
2007-12-22 21:54:20 +01:00
case 0 : /* Default */
2008-01-28 14:47:41 -08:00
phy_ctl | = B43_TXH_PHY_ANT01AUTO ;
2007-09-18 15:39:42 -04:00
break ;
2007-12-22 21:54:20 +01:00
case 1 : /* Antenna 0 */
2008-01-28 14:47:41 -08:00
phy_ctl | = B43_TXH_PHY_ANT0 ;
2007-09-18 15:39:42 -04:00
break ;
2007-12-22 21:54:20 +01:00
case 2 : /* Antenna 1 */
2008-01-28 14:47:41 -08:00
phy_ctl | = B43_TXH_PHY_ANT1 ;
break ;
case 3 : /* Antenna 2 */
phy_ctl | = B43_TXH_PHY_ANT2 ;
break ;
case 4 : /* Antenna 3 */
phy_ctl | = B43_TXH_PHY_ANT3 ;
2007-09-18 15:39:42 -04:00
break ;
default :
B43_WARN_ON ( 1 ) ;
}
2008-10-21 12:40:02 +02:00
rates = info - > control . rates ;
2007-09-18 15:39:42 -04:00
/* MAC control */
2008-05-15 12:55:29 +02:00
if ( ! ( info - > flags & IEEE80211_TX_CTL_NO_ACK ) )
2008-01-28 14:47:41 -08:00
mac_ctl | = B43_TXH_MAC_ACK ;
2008-07-10 11:21:26 +02:00
/* use hardware sequence counter as the non-TID counter */
if ( info - > flags & IEEE80211_TX_CTL_ASSIGN_SEQ )
2008-01-28 14:47:41 -08:00
mac_ctl | = B43_TXH_MAC_HWSEQ ;
2008-05-15 12:55:29 +02:00
if ( info - > flags & IEEE80211_TX_CTL_FIRST_FRAGMENT )
2008-01-28 14:47:41 -08:00
mac_ctl | = B43_TXH_MAC_STMSDU ;
2007-09-18 15:39:42 -04:00
if ( phy - > type = = B43_PHYTYPE_A )
2008-01-28 14:47:41 -08:00
mac_ctl | = B43_TXH_MAC_5GHZ ;
2008-10-21 12:40:02 +02:00
/* Overwrite rates[0].count to make the retry calculation
* in the tx status easier . need the actual retry limit to
* detect whether the fallback rate was used .
*/
if ( ( rates [ 0 ] . flags & IEEE80211_TX_RC_USE_RTS_CTS ) | |
( rates [ 0 ] . count < = dev - > wl - > hw - > conf . long_frame_max_tx_count ) ) {
rates [ 0 ] . count = dev - > wl - > hw - > conf . long_frame_max_tx_count ;
2008-01-28 14:47:41 -08:00
mac_ctl | = B43_TXH_MAC_LONGFRAME ;
2008-10-21 12:40:02 +02:00
} else {
rates [ 0 ] . count = dev - > wl - > hw - > conf . short_frame_max_tx_count ;
}
2007-09-18 15:39:42 -04:00
/* Generate the RTS or CTS-to-self frame */
2008-10-21 12:40:02 +02:00
if ( ( rates [ 0 ] . flags & IEEE80211_TX_RC_USE_RTS_CTS ) | |
( rates [ 0 ] . flags & IEEE80211_TX_RC_USE_CTS_PROTECT ) ) {
2007-09-18 15:39:42 -04:00
unsigned int len ;
struct ieee80211_hdr * hdr ;
int rts_rate , rts_rate_fb ;
int rts_rate_ofdm , rts_rate_fb_ofdm ;
2008-01-28 14:47:41 -08:00
struct b43_plcp_hdr6 * plcp ;
2008-05-15 12:55:27 +02:00
struct ieee80211_rate * rts_cts_rate ;
2007-09-18 15:39:42 -04:00
2008-05-15 12:55:29 +02:00
rts_cts_rate = ieee80211_get_rts_cts_rate ( dev - > wl - > hw , info ) ;
2008-05-15 12:55:27 +02:00
rts_rate = rts_cts_rate ? rts_cts_rate - > hw_value : B43_CCK_RATE_1MB ;
2007-09-18 15:39:42 -04:00
rts_rate_ofdm = b43_is_ofdm_rate ( rts_rate ) ;
rts_rate_fb = b43_calc_fallback_rate ( rts_rate ) ;
rts_rate_fb_ofdm = b43_is_ofdm_rate ( rts_rate_fb ) ;
2008-10-21 12:40:02 +02:00
if ( rates [ 0 ] . flags & IEEE80211_TX_RC_USE_CTS_PROTECT ) {
2008-01-28 14:47:41 -08:00
struct ieee80211_cts * cts ;
if ( b43_is_old_txhdr_format ( dev ) ) {
cts = ( struct ieee80211_cts * )
( txhdr - > old_format . rts_frame ) ;
} else {
cts = ( struct ieee80211_cts * )
( txhdr - > new_format . rts_frame ) ;
}
2008-05-15 12:55:29 +02:00
ieee80211_ctstoself_get ( dev - > wl - > hw , info - > control . vif ,
2007-09-18 15:39:42 -04:00
fragment_data , fragment_len ,
2008-05-15 12:55:29 +02:00
info , cts ) ;
2008-01-28 14:47:41 -08:00
mac_ctl | = B43_TXH_MAC_SENDCTS ;
2007-09-18 15:39:42 -04:00
len = sizeof ( struct ieee80211_cts ) ;
} else {
2008-01-28 14:47:41 -08:00
struct ieee80211_rts * rts ;
if ( b43_is_old_txhdr_format ( dev ) ) {
rts = ( struct ieee80211_rts * )
( txhdr - > old_format . rts_frame ) ;
} else {
rts = ( struct ieee80211_rts * )
( txhdr - > new_format . rts_frame ) ;
}
2008-05-15 12:55:29 +02:00
ieee80211_rts_get ( dev - > wl - > hw , info - > control . vif ,
2008-01-28 14:47:41 -08:00
fragment_data , fragment_len ,
2008-05-15 12:55:29 +02:00
info , rts ) ;
2008-01-28 14:47:41 -08:00
mac_ctl | = B43_TXH_MAC_SENDRTS ;
2007-09-18 15:39:42 -04:00
len = sizeof ( struct ieee80211_rts ) ;
}
len + = FCS_LEN ;
2008-01-28 14:47:41 -08:00
/* Generate the PLCP headers for the RTS/CTS frame */
if ( b43_is_old_txhdr_format ( dev ) )
plcp = & txhdr - > old_format . rts_plcp ;
else
plcp = & txhdr - > new_format . rts_plcp ;
b43_generate_plcp_hdr ( ( struct b43_plcp_hdr4 * ) plcp ,
len , rts_rate ) ;
plcp = & txhdr - > rts_plcp_fb ;
b43_generate_plcp_hdr ( ( struct b43_plcp_hdr4 * ) plcp ,
2007-09-18 15:39:42 -04:00
len , rts_rate_fb ) ;
2008-01-28 14:47:41 -08:00
if ( b43_is_old_txhdr_format ( dev ) ) {
hdr = ( struct ieee80211_hdr * )
( & txhdr - > old_format . rts_frame ) ;
} else {
hdr = ( struct ieee80211_hdr * )
( & txhdr - > new_format . rts_frame ) ;
}
2007-09-18 15:39:42 -04:00
txhdr - > rts_dur_fb = hdr - > duration_id ;
2008-01-28 14:47:41 -08:00
2007-09-18 15:39:42 -04:00
if ( rts_rate_ofdm ) {
2008-01-28 14:47:41 -08:00
extra_ft | = B43_TXH_EFT_RTS_OFDM ;
2007-09-18 15:39:42 -04:00
txhdr - > phy_rate_rts =
b43_plcp_get_ratecode_ofdm ( rts_rate ) ;
2008-01-28 14:47:41 -08:00
} else {
extra_ft | = B43_TXH_EFT_RTS_CCK ;
2007-09-18 15:39:42 -04:00
txhdr - > phy_rate_rts =
b43_plcp_get_ratecode_cck ( rts_rate ) ;
2008-01-28 14:47:41 -08:00
}
2007-09-18 15:39:42 -04:00
if ( rts_rate_fb_ofdm )
2008-01-28 14:47:41 -08:00
extra_ft | = B43_TXH_EFT_RTSFB_OFDM ;
else
extra_ft | = B43_TXH_EFT_RTSFB_CCK ;
2007-09-18 15:39:42 -04:00
}
/* Magic cookie */
2008-01-28 14:47:41 -08:00
if ( b43_is_old_txhdr_format ( dev ) )
txhdr - > old_format . cookie = cpu_to_le16 ( cookie ) ;
else
txhdr - > new_format . cookie = cpu_to_le16 ( cookie ) ;
2007-09-18 15:39:42 -04:00
/* Apply the bitfields */
txhdr - > mac_ctl = cpu_to_le32 ( mac_ctl ) ;
txhdr - > phy_ctl = cpu_to_le16 ( phy_ctl ) ;
txhdr - > extra_ft = extra_ft ;
2008-01-23 21:44:15 +01:00
return 0 ;
2007-09-18 15:39:42 -04:00
}
static s8 b43_rssi_postprocess ( struct b43_wldev * dev ,
u8 in_rssi , int ofdm ,
int adjust_2053 , int adjust_2050 )
{
struct b43_phy * phy = & dev - > phy ;
2008-08-27 18:53:02 +02:00
struct b43_phy_g * gphy = phy - > g ;
2007-09-18 15:39:42 -04:00
s32 tmp ;
switch ( phy - > radio_ver ) {
case 0x2050 :
if ( ofdm ) {
tmp = in_rssi ;
if ( tmp > 127 )
tmp - = 256 ;
tmp * = 73 ;
tmp / = 64 ;
if ( adjust_2050 )
tmp + = 25 ;
else
tmp - = 3 ;
} else {
2007-11-09 16:57:18 -06:00
if ( dev - > dev - > bus - > sprom .
2007-09-18 15:39:42 -04:00
boardflags_lo & B43_BFL_RSSI ) {
if ( in_rssi > 63 )
in_rssi = 63 ;
2008-08-27 18:53:02 +02:00
B43_WARN_ON ( phy - > type ! = B43_PHYTYPE_G ) ;
tmp = gphy - > nrssi_lt [ in_rssi ] ;
2007-09-18 15:39:42 -04:00
tmp = 31 - tmp ;
tmp * = - 131 ;
tmp / = 128 ;
tmp - = 57 ;
} else {
tmp = in_rssi ;
tmp = 31 - tmp ;
tmp * = - 149 ;
tmp / = 128 ;
tmp - = 68 ;
}
if ( phy - > type = = B43_PHYTYPE_G & & adjust_2050 )
tmp + = 25 ;
}
break ;
case 0x2060 :
if ( in_rssi > 127 )
tmp = in_rssi - 256 ;
else
tmp = in_rssi ;
break ;
default :
tmp = in_rssi ;
tmp - = 11 ;
tmp * = 103 ;
tmp / = 64 ;
if ( adjust_2053 )
tmp - = 109 ;
else
tmp - = 83 ;
}
return ( s8 ) tmp ;
}
//TODO
#if 0
static s8 b43_rssinoise_postprocess ( struct b43_wldev * dev , u8 in_rssi )
{
struct b43_phy * phy = & dev - > phy ;
s8 ret ;
if ( phy - > type = = B43_PHYTYPE_A ) {
//TODO: Incomplete specs.
ret = 0 ;
} else
ret = b43_rssi_postprocess ( dev , in_rssi , 0 , 1 , 1 ) ;
return ret ;
}
# endif
void b43_rx ( struct b43_wldev * dev , struct sk_buff * skb , const void * _rxhdr )
{
struct ieee80211_rx_status status ;
struct b43_plcp_hdr6 * plcp ;
struct ieee80211_hdr * wlhdr ;
const struct b43_rxhdr_fw4 * rxhdr = _rxhdr ;
2008-06-14 23:33:39 -07:00
__le16 fctl ;
2007-09-18 15:39:42 -04:00
u16 phystat0 , phystat3 , chanstat , mactime ;
u32 macstat ;
u16 chanid ;
2008-01-24 19:38:38 +01:00
u16 phytype ;
2007-09-18 15:39:42 -04:00
int padding ;
memset ( & status , 0 , sizeof ( status ) ) ;
/* Get metadata about the frame from the header. */
phystat0 = le16_to_cpu ( rxhdr - > phy_status0 ) ;
phystat3 = le16_to_cpu ( rxhdr - > phy_status3 ) ;
macstat = le32_to_cpu ( rxhdr - > mac_status ) ;
mactime = le16_to_cpu ( rxhdr - > mac_time ) ;
chanstat = le16_to_cpu ( rxhdr - > channel ) ;
2008-01-24 19:38:38 +01:00
phytype = chanstat & B43_RX_CHAN_PHYTYPE ;
2007-09-18 15:39:42 -04:00
2009-03-02 23:18:37 +01:00
if ( unlikely ( macstat & B43_RX_MAC_FCSERR ) ) {
2007-09-18 15:39:42 -04:00
dev - > wl - > ieee_stats . dot11FCSErrorCount + + ;
2009-03-02 23:18:37 +01:00
status . flag | = RX_FLAG_FAILED_FCS_CRC ;
}
if ( unlikely ( phystat0 & ( B43_RX_PHYST0_PLCPHCF | B43_RX_PHYST0_PLCPFV ) ) )
status . flag | = RX_FLAG_FAILED_PLCP_CRC ;
if ( phystat0 & B43_RX_PHYST0_SHORTPRMBL )
status . flag | = RX_FLAG_SHORTPRE ;
2007-09-18 15:39:42 -04:00
if ( macstat & B43_RX_MAC_DECERR ) {
/* Decryption with the given key failed.
* Drop the packet . We also won ' t be able to decrypt it with
* the key in software . */
goto drop ;
}
/* Skip PLCP and padding */
padding = ( macstat & B43_RX_MAC_PADDING ) ? 2 : 0 ;
if ( unlikely ( skb - > len < ( sizeof ( struct b43_plcp_hdr6 ) + padding ) ) ) {
b43dbg ( dev - > wl , " RX: Packet size underrun (1) \n " ) ;
goto drop ;
}
plcp = ( struct b43_plcp_hdr6 * ) ( skb - > data + padding ) ;
skb_pull ( skb , sizeof ( struct b43_plcp_hdr6 ) + padding ) ;
/* The skb contains the Wireless Header + payload data now */
if ( unlikely ( skb - > len < ( 2 + 2 + 6 /*minimum hdr */ + FCS_LEN ) ) ) {
b43dbg ( dev - > wl , " RX: Packet size underrun (2) \n " ) ;
goto drop ;
}
wlhdr = ( struct ieee80211_hdr * ) ( skb - > data ) ;
2008-06-14 23:33:39 -07:00
fctl = wlhdr - > frame_control ;
2007-09-18 15:39:42 -04:00
if ( macstat & B43_RX_MAC_DEC ) {
unsigned int keyidx ;
int wlhdr_len ;
keyidx = ( ( macstat & B43_RX_MAC_KEYIDX )
> > B43_RX_MAC_KEYIDX_SHIFT ) ;
/* We must adjust the key index here. We want the "physical"
* key index , but the ucode passed it slightly different .
*/
keyidx = b43_kidx_to_raw ( dev , keyidx ) ;
2009-08-06 10:36:50 +02:00
B43_WARN_ON ( keyidx > = ARRAY_SIZE ( dev - > key ) ) ;
2007-09-18 15:39:42 -04:00
if ( dev - > key [ keyidx ] . algorithm ! = B43_SEC_ALGO_NONE ) {
2008-06-14 23:33:39 -07:00
wlhdr_len = ieee80211_hdrlen ( fctl ) ;
2007-09-18 15:39:42 -04:00
if ( unlikely ( skb - > len < ( wlhdr_len + 3 ) ) ) {
b43dbg ( dev - > wl ,
" RX: Packet size underrun (3) \n " ) ;
goto drop ;
}
status . flag | = RX_FLAG_DECRYPTED ;
}
}
2008-04-03 18:01:12 +02:00
/* Link quality statistics */
2007-09-18 15:39:42 -04:00
status . noise = dev - > stats . link_noise ;
2008-04-03 18:01:12 +02:00
if ( ( chanstat & B43_RX_CHAN_PHYTYPE ) = = B43_PHYTYPE_N ) {
// s8 rssi = max(rxhdr->power0, rxhdr->power1);
//TODO: Find out what the rssi value is (dBm or percentage?)
// and also find out what the maximum possible value is.
// Fill status.ssi and status.signal fields.
} else {
2008-05-08 19:15:40 +02:00
status . signal = b43_rssi_postprocess ( dev , rxhdr - > jssi ,
2008-04-03 18:01:12 +02:00
( phystat0 & B43_RX_PHYST0_OFDM ) ,
( phystat0 & B43_RX_PHYST0_GAINCTL ) ,
( phystat3 & B43_RX_PHYST3_TRSTATE ) ) ;
2008-05-08 19:15:40 +02:00
status . qual = ( rxhdr - > jssi * 100 ) / B43_RX_MAX_SSI ;
2008-04-03 18:01:12 +02:00
}
2007-09-18 15:39:42 -04:00
if ( phystat0 & B43_RX_PHYST0_OFDM )
2008-01-24 19:38:38 +01:00
status . rate_idx = b43_plcp_get_bitrate_idx_ofdm ( plcp ,
phytype = = B43_PHYTYPE_A ) ;
2007-09-18 15:39:42 -04:00
else
2008-01-24 19:38:38 +01:00
status . rate_idx = b43_plcp_get_bitrate_idx_cck ( plcp ) ;
2009-03-02 23:18:37 +01:00
if ( unlikely ( status . rate_idx = = - 1 ) ) {
/* PLCP seems to be corrupted.
* Drop the frame , if we are not interested in corrupted frames . */
if ( ! ( dev - > wl - > filter_flags & FIF_PLCPFAIL ) )
goto drop ;
}
2007-09-18 15:39:42 -04:00
status . antenna = ! ! ( phystat0 & B43_RX_PHYST0_ANT ) ;
2008-01-21 13:41:18 -05:00
/*
2008-02-18 18:53:55 +01:00
* All frames on monitor interfaces and beacons always need a full
* 64 - bit timestamp . Monitor interfaces need it for diagnostic
* purposes and beacons for IBSS merging .
* This code assumes we get to process the packet within 16 bits
* of timestamp , i . e . about 65 milliseconds after the PHY received
* the first symbol .
2008-01-21 13:41:18 -05:00
*/
2008-06-14 23:33:39 -07:00
if ( ieee80211_is_beacon ( fctl ) | | dev - > wl - > radiotap_enabled ) {
2008-01-21 13:41:18 -05:00
u16 low_mactime_now ;
b43_tsf_read ( dev , & status . mactime ) ;
low_mactime_now = status . mactime ;
status . mactime = status . mactime & ~ 0xFFFFULL ;
status . mactime + = mactime ;
if ( low_mactime_now < = mactime )
status . mactime - = 0x10000 ;
status . flag | = RX_FLAG_TSFT ;
}
2007-09-18 15:39:42 -04:00
chanid = ( chanstat & B43_RX_CHAN_ID ) > > B43_RX_CHAN_ID_SHIFT ;
switch ( chanstat & B43_RX_CHAN_PHYTYPE ) {
case B43_PHYTYPE_A :
2008-01-24 19:38:38 +01:00
status . band = IEEE80211_BAND_5GHZ ;
2008-01-02 18:55:53 +01:00
B43_WARN_ON ( 1 ) ;
/* FIXME: We don't really know which value the "chanid" contains.
* So the following assignment might be wrong . */
2008-01-24 19:38:38 +01:00
status . freq = b43_channel_to_freq_5ghz ( chanid ) ;
2007-09-18 15:39:42 -04:00
break ;
case B43_PHYTYPE_G :
2008-01-24 19:38:38 +01:00
status . band = IEEE80211_BAND_2GHZ ;
2008-01-02 18:55:53 +01:00
/* chanid is the radio channel cookie value as used
* to tune the radio . */
2007-09-18 15:39:42 -04:00
status . freq = chanid + 2400 ;
2008-01-02 18:55:53 +01:00
break ;
case B43_PHYTYPE_N :
2009-08-15 00:52:02 +02:00
case B43_PHYTYPE_LP :
2008-01-02 18:55:53 +01:00
/* chanid is the SHM channel cookie. Which is the plain
* channel number in b43 . */
2008-01-24 19:38:38 +01:00
if ( chanstat & B43_RX_CHAN_5GHZ ) {
status . band = IEEE80211_BAND_5GHZ ;
status . freq = b43_freq_to_channel_5ghz ( chanid ) ;
} else {
status . band = IEEE80211_BAND_2GHZ ;
status . freq = b43_freq_to_channel_2ghz ( chanid ) ;
}
2007-09-18 15:39:42 -04:00
break ;
default :
B43_WARN_ON ( 1 ) ;
2008-01-02 18:55:53 +01:00
goto drop ;
2007-09-18 15:39:42 -04:00
}
2009-06-17 13:13:00 +02:00
memcpy ( IEEE80211_SKB_RXCB ( skb ) , & status , sizeof ( status ) ) ;
2009-10-11 12:19:21 +02:00
local_bh_disable ( ) ;
2009-09-10 20:22:02 +02:00
ieee80211_rx ( dev - > wl - > hw , skb ) ;
2009-10-11 12:19:21 +02:00
local_bh_enable ( ) ;
2007-09-18 15:39:42 -04:00
2009-09-12 00:48:03 +02:00
# if B43_DEBUG
dev - > rx_count + + ;
# endif
2007-09-18 15:39:42 -04:00
return ;
drop :
b43dbg ( dev - > wl , " RX: Packet dropped \n " ) ;
dev_kfree_skb_any ( skb ) ;
}
void b43_handle_txstatus ( struct b43_wldev * dev ,
const struct b43_txstatus * status )
{
b43_debugfs_log_txstat ( dev , status ) ;
if ( status - > intermediate )
return ;
if ( status - > for_ampdu )
return ;
if ( ! status - > acked )
dev - > wl - > ieee_stats . dot11ACKFailureCount + + ;
if ( status - > rts_count ) {
if ( status - > rts_count = = 0xF ) //FIXME
dev - > wl - > ieee_stats . dot11RTSFailureCount + + ;
else
dev - > wl - > ieee_stats . dot11RTSSuccessCount + + ;
}
2008-03-29 21:01:16 +01:00
if ( b43_using_pio_transfers ( dev ) )
b43_pio_handle_txstatus ( dev , status ) ;
else
b43_dma_handle_txstatus ( dev , status ) ;
2008-08-28 19:33:40 +02:00
b43_phy_txpower_check ( dev , 0 ) ;
2007-09-18 15:39:42 -04:00
}
2008-03-29 21:01:16 +01:00
/* Fill out the mac80211 TXstatus report based on the b43-specific
* txstatus report data . This returns a boolean whether the frame was
* successfully transmitted . */
2008-10-21 12:40:02 +02:00
bool b43_fill_txstatus_report ( struct b43_wldev * dev ,
struct ieee80211_tx_info * report ,
2008-03-29 21:01:16 +01:00
const struct b43_txstatus * status )
2007-09-18 15:39:42 -04:00
{
2008-03-29 21:01:16 +01:00
bool frame_success = 1 ;
2008-10-21 12:40:02 +02:00
int retry_limit ;
/* preserve the confiured retry limit before clearing the status
* The xmit function has overwritten the rc ' s value with the actual
* retry limit done by the hardware */
retry_limit = report - > status . rates [ 0 ] . count ;
ieee80211_tx_info_clear_status ( report ) ;
2008-03-29 21:01:16 +01:00
if ( status - > acked ) {
/* The frame was ACKed. */
2008-05-15 12:55:29 +02:00
report - > flags | = IEEE80211_TX_STAT_ACK ;
2008-03-29 21:01:16 +01:00
} else {
/* The frame was not ACKed... */
2008-05-15 12:55:29 +02:00
if ( ! ( report - > flags & IEEE80211_TX_CTL_NO_ACK ) ) {
2008-03-29 21:01:16 +01:00
/* ...but we expected an ACK. */
frame_success = 0 ;
}
}
if ( status - > frame_count = = 0 ) {
/* The frame was not transmitted at all. */
2008-10-21 12:40:02 +02:00
report - > status . rates [ 0 ] . count = 0 ;
} else if ( status - > rts_count > dev - > wl - > hw - > conf . short_frame_max_tx_count ) {
/*
* If the short retries ( RTS , not data frame ) have exceeded
* the limit , the hw will not have tried the selected rate ,
* but will have used the fallback rate instead .
* Don ' t let the rate control count attempts for the selected
* rate in this case , otherwise the statistics will be off .
*/
report - > status . rates [ 0 ] . count = 0 ;
report - > status . rates [ 1 ] . count = status - > frame_count ;
} else {
if ( status - > frame_count > retry_limit ) {
report - > status . rates [ 0 ] . count = retry_limit ;
report - > status . rates [ 1 ] . count = status - > frame_count -
retry_limit ;
} else {
report - > status . rates [ 0 ] . count = status - > frame_count ;
report - > status . rates [ 1 ] . idx = - 1 ;
}
}
2008-03-29 21:01:16 +01:00
return frame_success ;
2007-09-18 15:39:42 -04:00
}
/* Stop any TX operation on the device (suspend the hardware queues) */
void b43_tx_suspend ( struct b43_wldev * dev )
{
2008-03-29 21:01:16 +01:00
if ( b43_using_pio_transfers ( dev ) )
b43_pio_tx_suspend ( dev ) ;
else
b43_dma_tx_suspend ( dev ) ;
2007-09-18 15:39:42 -04:00
}
/* Resume any TX operation on the device (resume the hardware queues) */
void b43_tx_resume ( struct b43_wldev * dev )
{
2008-03-29 21:01:16 +01:00
if ( b43_using_pio_transfers ( dev ) )
b43_pio_tx_resume ( dev ) ;
else
b43_dma_tx_resume ( dev ) ;
2007-09-18 15:39:42 -04:00
}