Search Linux Wireless

Re: [PATCH] mac80211: Add transmit power control support (TPC)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



--- On Sun, 8/7/12, Thomas Huehn <thomas@xxxxxxxxxxxxxxxxxxxxxxx> wrote:

> From: Thomas Huehn <thomas@xxxxxxxxxxxxxxxxxxxxxxx>
> Subject: [PATCH] mac80211: Add transmit power control support (TPC)
> To: linville@xxxxxxxxxxxx
> Cc: linux-wireless@xxxxxxxxxxxxxxx, mcgrof@xxxxxxxxxxxxxxxx, jouni@xxxxxxxxxxxxxxxx, vthiagar@xxxxxxxxxxxxxxxx, senthilb@xxxxxxxxxxxxxxxx, ath9k-devel@xxxxxxxxxxxxxxx, nbd@xxxxxxxxxxx, jirislaby@xxxxxxxxx, mickflemm@xxxxxxxxx, ath5k-devel@xxxxxxxxxxxxxxx, sgruszka@xxxxxxxxxx, johannes.berg@xxxxxxxxx, wey-yi.w.guy@xxxxxxxxx, ilw@xxxxxxxxxxxxxxx, buytenh@xxxxxxxxxxxxxx, chunkeey@xxxxxxxxxxxxxx, IvDoorn@xxxxxxxxx, gwingerde@xxxxxxxxx, helmut.schaa@xxxxxxxxxxxxxx, users@xxxxxxxxxxxxxxxxxxxxxxx, herton@xxxxxxxxxxxxx, htl10@xxxxxxxxxxxxxxxxxxxxx, stefano.brivio@xxxxxxxxx, b43-dev@xxxxxxxxxxxxxxxxxxx, brudley@xxxxxxxxxxxx, rvossen@xxxxxxxxxxxx, arend@xxxxxxxxxxxx, frankyl@xxxxxxxxxxxx, kanyan@xxxxxxxxxxxx, pieterpg@xxxxxxxxxxxx, alwin@xxxxxxxxxxxx, brcm80211-dev-list@xxxxxxxxxxxx, dsd@xxxxxxxxxx, kune@xxxxxxxxxxxxxx, thomas@xxxxxxxxxxxxxxxxxxxxxxx, x-alina@xxxxxxx
> Date: Sunday, 8 July, 2012, 18:02
> This patch creates an transmit power
> control (TPC) API within the mac80211
> subsystem. It enables a per multi-rate-retry stage annotaion
> of a power-level
> value in dBm for each data packet and a global power-level
> for acknowledgement
> packets. Furthermore, necessary flags are defined to specify
> and map TPC
> hardware capabilities of individual wireless cards. This TPC
> API is a
> pre-requisite to implement any power control algorithm at
> mac80211. A new joint
> rate and power control algorithm "Minstrel-Blues" is
> released soon.
> This patch consists of the following 6 logical sections:
> 
> (1) structure ieee80211_tx_control is added to mac80211
> It holds the STA structure to be able to remove
> info->control.sta from
> struct ieee80211_tx_info (therefor out of the tx-path) and
> put it on the stack.
> 
> (2) restructuring of ieee80211_tx_info to add TPC
> annotation
> Remove info->control.sta from struct ieee80211_tx_info to
> free up suitable
> memory in SKB_CB. Make use of the freed space to extend the
> struct
> ieee80211_tx_rate by u8 tpc[4]. This enables a per packet
> annotation of one
> powerlevel in dBm per multi-rate-retry stage.
> 
> (3) add tpc hardware capability flags
> To map different tpc hardware capabilities to mac80211, a
> new enum
> ieee80211_tpc_support (type of transmit power control)
> support is added. Based
> on these flags someone can specify transmit power control
> capabilities
> to the stack.
> 
> @IEEE80211_TPC_NONE: No tpc beside a fixed global setting is
> available.
>     This setting is used as the default case.
> Extended tpc capabilities
>     need to be announced via flags within the
> individual hardware driver.
> @IEEE80211_TPC_PER_DATA_PACKET: One power level per data
> packet can
>     be set. Each data packet is send out with
> its individual power level.
> @IEEE80211_TPC_PER_DATA_MRR: Multiple individual power
> levels per
>     multi-rate-retry stage within a data
> packet are supported.
> @IEEE80211_TPC_ACK_POWER_GLOBAL: One power level of ack
> packets is
>     globaly adjustable.
> 
> (4) add support to change power-level of acknowledgement
> packets.
> Add flag IEEE80211_CONF_CHANGE_ACK_POWER to
> ieee80211_conf_flags. This enables
> to specify ack_power as global power level in dBm to use for
> all mac80211
> acknowledgement packets.
> 
> (5) brcmsmac: restructure info->control.sta handling as
> it is goning to be removed.
> brcmsmac uses info->control.sta while doing ampdu
> aggregation. The usage of the
> structure info->control.sta is changed, as it is going to
> be removed from
> struct ieee80211_tx_info.
> 
> (6) restructure tx-path of all effected drivers
> Restructure tx-path of all effected drivers to respect new
> TPC support in
> mac80211. TPC support is added to mac80211 by restructuring
> of struct
> ieee80211_tx_info. Therfore the tx-path of all effected
> drivers is modified
> to receive struct sta from the stack and respect the new
> ieee80211_tx_info
> struct. List of modified driver:
> ath9k
> ath5k
> iwl3954
> iwl4965
> iwl-agn
> mwl8k
> carl9170
> ath9k-htc
> p54
> rt2x00
> rtl8180
> rtl8087
> hwsim
> b43
> b43legacy
> brcmsmac
> zd1211rw
> 
> Signed-off-by: Thomas Huehn <thomas@xxxxxxxxxxxxxxxxxxxxxxx>
> Signed-off-by: Alina Friedrichsen <x-alina@xxxxxxx>
> Signed-off-by: Felix Fietkau <nbd@xxxxxxxxxxx>

Acked-by: Hin-Tak Leung <htl10@xxxxxxxxxxxxxxxxxxxxx>

> ---
>  drivers/net/wireless/ath/ath5k/mac80211-ops.c   
>   |    3 +-
>  drivers/net/wireless/ath/ath9k/ath9k.h     
>        |    1 +
>  drivers/net/wireless/ath/ath9k/htc.h     
>          |    1 +
>  drivers/net/wireless/ath/ath9k/htc_drv_beacon.c 
>   |    3 +-
>  drivers/net/wireless/ath/ath9k/htc_drv_main.c   
>   |    6 +-
>  drivers/net/wireless/ath/ath9k/htc_drv_txrx.c   
>   |    3 +-
>  drivers/net/wireless/ath/ath9k/main.c     
>         |    5 +-
>  drivers/net/wireless/ath/ath9k/xmit.c     
>         |   10 ++-
>  drivers/net/wireless/ath/carl9170/carl9170.h   
>    |    4 +-
>  drivers/net/wireless/ath/carl9170/tx.c     
>        |   16 ++--
>  drivers/net/wireless/b43/main.c       
>             |    3
> +-
>  drivers/net/wireless/b43legacy/main.c     
>         |    1 +
>  drivers/net/wireless/brcm80211/brcmsmac/ampdu.c 
>   |   10 +--
>  .../net/wireless/brcm80211/brcmsmac/mac80211_if.c 
> |    6 +-
>  drivers/net/wireless/brcm80211/brcmsmac/main.c 
>    |    2 +-
>  drivers/net/wireless/iwlegacy/3945-mac.c   
>        |   12 ++-
>  drivers/net/wireless/iwlegacy/4965-mac.c   
>        |   25
> ++++--
>  drivers/net/wireless/iwlegacy/4965.h     
>          |    8 +-
>  drivers/net/wireless/iwlwifi/dvm/agn.h     
>        |    4 +-
>  drivers/net/wireless/iwlwifi/dvm/mac80211.c   
>     |    6 +-
>  drivers/net/wireless/iwlwifi/dvm/tx.c     
>         |   15 ++--
>  drivers/net/wireless/mac80211_hwsim.c     
>         |   14 ++--
>  drivers/net/wireless/mwl8k.c       
>            
>    |   33 +++++---
>  drivers/net/wireless/p54/lmac.h       
>             |    4
> +-
>  drivers/net/wireless/p54/main.c       
>             |    3
> +-
>  drivers/net/wireless/p54/txrx.c       
>            
> |   17 ++--
>  drivers/net/wireless/rt2x00/rt2x00.h     
>          |    4 +-
>  drivers/net/wireless/rt2x00/rt2x00dev.c     
>       |    3 +-
>  drivers/net/wireless/rt2x00/rt2x00mac.c     
>       |    4 +-
>  drivers/net/wireless/rt2x00/rt2x00queue.c   
>       |   22 +++--
>  drivers/net/wireless/rtl818x/rtl8180/dev.c   
>      |    7 +-
>  drivers/net/wireless/rtl818x/rtl8187/dev.c   
>      |    7 +-
>  drivers/net/wireless/zd1211rw/zd_mac.c     
>        |    7 +-
>  include/net/mac80211.h         
>                
>    |   87
> +++++++++++++++-----
>  net/mac80211/driver-ops.h         
>                
> |    6 +-
>  net/mac80211/ieee80211_i.h       
>              
>    |    2 +
>  net/mac80211/tx.c           
>                
>       |   13 +--
>  37 files changed, 258 insertions(+), 119 deletions(-)
> 
> diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
> b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
> index 22b80af..93a800f 100644
> --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
> +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
> @@ -55,7 +55,8 @@
>  \********************/
>  
>  static void
> -ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
> +ath5k_tx(struct ieee80211_hw *hw, struct
> ieee80211_tx_control *control,
> +     struct sk_buff *skb)
>  {
>      struct ath5k_hw *ah = hw->priv;
>      u16 qnum = skb_get_queue_mapping(skb);
> diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h
> b/drivers/net/wireless/ath/ath9k/ath9k.h
> index 79840d6..1de556c 100644
> --- a/drivers/net/wireless/ath/ath9k/ath9k.h
> +++ b/drivers/net/wireless/ath/ath9k/ath9k.h
> @@ -280,6 +280,7 @@ struct ath_tx_control {
>      struct ath_txq *txq;
>      struct ath_node *an;
>      u8 paprd;
> +    struct ieee80211_tx_control control;
>  };
>  
>  #define ATH_TX_ERROR        0x01
> diff --git a/drivers/net/wireless/ath/ath9k/htc.h
> b/drivers/net/wireless/ath/ath9k/htc.h
> index 936e920..b0a6ad5 100644
> --- a/drivers/net/wireless/ath/ath9k/htc.h
> +++ b/drivers/net/wireless/ath/ath9k/htc.h
> @@ -542,6 +542,7 @@ void ath9k_htc_stop_ani(struct
> ath9k_htc_priv *priv);
>  
>  int ath9k_tx_init(struct ath9k_htc_priv *priv);
>  int ath9k_htc_tx_start(struct ath9k_htc_priv *priv,
> +           
>    struct ieee80211_tx_control *control,
>             
>    struct sk_buff *skb, u8 slot, bool
> is_cab);
>  void ath9k_tx_cleanup(struct ath9k_htc_priv *priv);
>  bool ath9k_htc_txq_setup(struct ath9k_htc_priv *priv, int
> subtype);
> diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
> b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
> index 77d541f..5f07359 100644
> --- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
> +++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
> @@ -296,6 +296,7 @@ static void
> ath9k_htc_send_buffered(struct ath9k_htc_priv *priv,
>      struct ath_common *common =
> ath9k_hw_common(priv->ah);
>      struct ieee80211_vif *vif;
>      struct sk_buff *skb;
> +    struct ieee80211_tx_control control;
>      struct ieee80211_hdr *hdr;
>      int padpos, padsize, ret, tx_slot;
>  
> @@ -326,7 +327,7 @@ static void
> ath9k_htc_send_buffered(struct ath9k_htc_priv *priv,
>             
> goto next;
>          }
>  
> -        ret =
> ath9k_htc_tx_start(priv, skb, tx_slot, true);
> +        ret =
> ath9k_htc_tx_start(priv, &control, skb, tx_slot, true);
>          if (ret != 0) {
>             
> ath9k_htc_tx_clear_slot(priv, tx_slot);
>             
> dev_kfree_skb_any(skb);
> diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
> b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
> index 374c32e..295f89a 100644
> --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
> +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
> @@ -856,7 +856,9 @@ set_timer:
>  /* mac80211 Callbacks */
>  /**********************/
>  
> -static void ath9k_htc_tx(struct ieee80211_hw *hw, struct
> sk_buff *skb)
> +static void ath9k_htc_tx(struct ieee80211_hw *hw,
> +       
>      struct ieee80211_tx_control
> *control,
> +       
>      struct sk_buff *skb)
>  {
>      struct ieee80211_hdr *hdr;
>      struct ath9k_htc_priv *priv =
> hw->priv;
> @@ -883,7 +885,7 @@ static void ath9k_htc_tx(struct
> ieee80211_hw *hw, struct sk_buff *skb)
>          goto fail_tx;
>      }
>  
> -    ret = ath9k_htc_tx_start(priv, skb,
> slot, false);
> +    ret = ath9k_htc_tx_start(priv, control,
> skb, slot, false);
>      if (ret != 0) {
>          ath_dbg(common, XMIT,
> "Tx failed\n");
>          goto clear_slot;
> diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
> b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
> index 47e61d0..948a060 100644
> --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
> +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
> @@ -333,12 +333,13 @@ static void ath9k_htc_tx_data(struct
> ath9k_htc_priv *priv,
>  }
>  
>  int ath9k_htc_tx_start(struct ath9k_htc_priv *priv,
> +           
>    struct ieee80211_tx_control *control,
>             
>    struct sk_buff *skb,
>             
>    u8 slot, bool is_cab)
>  {
>      struct ieee80211_hdr *hdr;
>      struct ieee80211_tx_info *tx_info =
> IEEE80211_SKB_CB(skb);
> -    struct ieee80211_sta *sta =
> tx_info->control.sta;
> +    struct ieee80211_sta *sta =
> control->sta;
>      struct ieee80211_vif *vif =
> tx_info->control.vif;
>      struct ath9k_htc_sta *ista;
>      struct ath9k_htc_vif *avp = NULL;
> diff --git a/drivers/net/wireless/ath/ath9k/main.c
> b/drivers/net/wireless/ath/ath9k/main.c
> index 248e5b2..9673693c 100644
> --- a/drivers/net/wireless/ath/ath9k/main.c
> +++ b/drivers/net/wireless/ath/ath9k/main.c
> @@ -680,7 +680,9 @@ mutex_unlock:
>      return r;
>  }
>  
> -static void ath9k_tx(struct ieee80211_hw *hw, struct
> sk_buff *skb)
> +static void ath9k_tx(struct ieee80211_hw *hw,
> +         
>    struct ieee80211_tx_control *control,
> +         
>    struct sk_buff *skb)
>  {
>      struct ath_softc *sc = hw->priv;
>      struct ath_common *common =
> ath9k_hw_common(sc->sc_ah);
> @@ -740,6 +742,7 @@ static void ath9k_tx(struct ieee80211_hw
> *hw, struct sk_buff *skb)
>  
>      memset(&txctl, 0, sizeof(struct
> ath_tx_control));
>      txctl.txq =
> sc->tx.txq_map[skb_get_queue_mapping(skb)];
> +    memcpy(&txctl.control, control,
> sizeof(struct ieee80211_tx_control));
>  
>      ath_dbg(common, XMIT, "transmitting
> packet, skb: %p\n", skb);
>  
> diff --git a/drivers/net/wireless/ath/ath9k/xmit.c
> b/drivers/net/wireless/ath/ath9k/xmit.c
> index cafb4a0..bf5d390 100644
> --- a/drivers/net/wireless/ath/ath9k/xmit.c
> +++ b/drivers/net/wireless/ath/ath9k/xmit.c
> @@ -1771,11 +1771,13 @@ static void
> ath_tx_send_normal(struct ath_softc *sc, struct ath_txq
> *txq,
>      TX_STAT_INC(txq->axq_qnum, queued);
>  }
>  
> -static void setup_frame_info(struct ieee80211_hw *hw,
> struct sk_buff *skb,
> +static void setup_frame_info(struct ieee80211_hw *hw,
> +           
>      struct ieee80211_tx_control
> *control,
> +           
>      struct sk_buff *skb,
>             
>      int framelen)
>  {
>      struct ieee80211_tx_info *tx_info =
> IEEE80211_SKB_CB(skb);
> -    struct ieee80211_sta *sta =
> tx_info->control.sta;
> +    struct ieee80211_sta *sta =
> control->sta;
>      struct ieee80211_key_conf *hw_key =
> tx_info->control.hw_key;
>      struct ieee80211_hdr *hdr = (struct
> ieee80211_hdr *)skb->data;
>      const struct ieee80211_rate *rate;
> @@ -1933,7 +1935,7 @@ int ath_tx_start(struct ieee80211_hw
> *hw, struct sk_buff *skb,
>  {
>      struct ieee80211_hdr *hdr = (struct
> ieee80211_hdr *) skb->data;
>      struct ieee80211_tx_info *info =
> IEEE80211_SKB_CB(skb);
> -    struct ieee80211_sta *sta =
> info->control.sta;
> +    struct ieee80211_sta *sta =
> txctl->control.sta;
>      struct ieee80211_vif *vif =
> info->control.vif;
>      struct ath_softc *sc = hw->priv;
>      struct ath_txq *txq = txctl->txq;
> @@ -1977,7 +1979,7 @@ int ath_tx_start(struct ieee80211_hw
> *hw, struct sk_buff *skb,
>         
> !ieee80211_is_data(hdr->frame_control))
>          info->flags |=
> IEEE80211_TX_CTL_CLEAR_PS_FILT;
>  
> -    setup_frame_info(hw, skb, frmlen);
> +    setup_frame_info(hw,
> &txctl->control, skb, frmlen);
>  
>      /*
>       * At this point, the vif,
> hw_key and sta pointers in the tx control
> diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h
> b/drivers/net/wireless/ath/carl9170/carl9170.h
> index 0cea20e..5b48b43 100644
> --- a/drivers/net/wireless/ath/carl9170/carl9170.h
> +++ b/drivers/net/wireless/ath/carl9170/carl9170.h
> @@ -566,7 +566,9 @@ void carl9170_rx(struct ar9170 *ar, void
> *buf, unsigned int len);
>  void carl9170_handle_command_response(struct ar9170 *ar,
> void *buf, u32 len);
>  
>  /* TX */
> -void carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff
> *skb);
> +void carl9170_op_tx(struct ieee80211_hw *hw,
> +            struct
> ieee80211_tx_control *control,
> +            struct
> sk_buff *skb);
>  void carl9170_tx_janitor(struct work_struct *work);
>  void carl9170_tx_process_status(struct ar9170 *ar,
>             
>     const struct carl9170_rsp *cmd);
> diff --git a/drivers/net/wireless/ath/carl9170/tx.c
> b/drivers/net/wireless/ath/carl9170/tx.c
> index ede0b57..1aa9564 100644
> --- a/drivers/net/wireless/ath/carl9170/tx.c
> +++ b/drivers/net/wireless/ath/carl9170/tx.c
> @@ -277,7 +277,7 @@ static void carl9170_tx_release(struct
> kref *ref)
>          return;
>  
>      BUILD_BUG_ON(
> -        offsetof(struct
> ieee80211_tx_info, status.ack_signal) != 20);
> +        offsetof(struct
> ieee80211_tx_info, status.ack_signal) != 24);
>  
>     
> memset(&txinfo->status.ack_signal, 0,
>         
>    sizeof(struct ieee80211_tx_info) -
> @@ -826,7 +826,9 @@ static bool carl9170_tx_cts_check(struct
> ar9170 *ar,
>      return false;
>  }
>  
> -static int carl9170_tx_prepare(struct ar9170 *ar, struct
> sk_buff *skb)
> +static int carl9170_tx_prepare(struct ar9170 *ar,
> +           
>        struct ieee80211_tx_control
> *control,
> +           
>        struct sk_buff *skb)
>  {
>      struct ieee80211_hdr *hdr;
>      struct _carl9170_tx_superframe *txc;
> @@ -869,7 +871,7 @@ static int carl9170_tx_prepare(struct
> ar9170 *ar, struct sk_buff *skb)
>      else
>          cvif = NULL;
>  
> -    sta = info->control.sta;
> +    sta = control->sta;
>  
>      txc = (void *)skb_push(skb,
> sizeof(*txc));
>      memset(txc, 0, sizeof(*txc));
> @@ -1394,7 +1396,9 @@ err_unlock_rcu:
>      return false;
>  }
>  
> -void carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff
> *skb)
> +void carl9170_op_tx(struct ieee80211_hw *hw,
> +            struct
> ieee80211_tx_control *control,
> +            struct
> sk_buff *skb)
>  {
>      struct ar9170 *ar = hw->priv;
>      struct ieee80211_tx_info *info;
> @@ -1405,9 +1409,9 @@ void carl9170_op_tx(struct
> ieee80211_hw *hw, struct sk_buff *skb)
>          goto err_free;
>  
>      info = IEEE80211_SKB_CB(skb);
> -    sta = info->control.sta;
> +    sta = control->sta;
>  
> -    if (unlikely(carl9170_tx_prepare(ar,
> skb)))
> +    if (unlikely(carl9170_tx_prepare(ar,
> control, skb)))
>          goto err_free;
>  
>      carl9170_tx_accounting(ar, skb);
> diff --git a/drivers/net/wireless/b43/main.c
> b/drivers/net/wireless/b43/main.c
> index 1b988f2..b6fc409 100644
> --- a/drivers/net/wireless/b43/main.c
> +++ b/drivers/net/wireless/b43/main.c
> @@ -3409,7 +3409,8 @@ static void b43_tx_work(struct
> work_struct *work)
>  }
>  
>  static void b43_op_tx(struct ieee80211_hw *hw,
> -         
>    struct sk_buff *skb)
> +             
> struct ieee80211_tx_control *control,
> +             
> struct sk_buff *skb)
>  {
>      struct b43_wl *wl = hw_to_b43_wl(hw);
>  
> diff --git a/drivers/net/wireless/b43legacy/main.c
> b/drivers/net/wireless/b43legacy/main.c
> index 8156135..74d4c20 100644
> --- a/drivers/net/wireless/b43legacy/main.c
> +++ b/drivers/net/wireless/b43legacy/main.c
> @@ -2492,6 +2492,7 @@ static void b43legacy_tx_work(struct
> work_struct *work)
>  }
>  
>  static void b43legacy_op_tx(struct ieee80211_hw *hw,
> +           
>     struct ieee80211_tx_control *control,
>             
>     struct sk_buff *skb)
>  {
>      struct b43legacy_wl *wl =
> hw_to_b43legacy_wl(hw);
> diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c
> b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c
> index 01b190a..d341c91 100644
> --- a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c
> +++ b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c
> @@ -665,7 +665,7 @@ brcms_c_sendampdu(struct ampdu_info
> *ampdu, struct brcms_txq_info *qi,
>              u8
> plcp0, plcp3, is40, sgi;
>             
> struct ieee80211_sta *sta;
>  
> -           
> sta = tx_info->control.sta;
> +           
> sta = tx_info->rate_driver_data[0];
>  
>              if
> (rr) {
>             
>     plcp0 = plcp[0];
> @@ -1195,8 +1195,8 @@ static bool cb_del_ampdu_pkt(struct
> sk_buff *mpdu, void *arg_a)
>      bool rc;
>  
>      rc = tx_info->flags &
> IEEE80211_TX_CTL_AMPDU ? true : false;
> -    rc = rc &&
> (tx_info->control.sta == NULL || ampdu_pars->sta ==
> NULL ||
> -           
> tx_info->control.sta == ampdu_pars->sta);
> +    rc = rc &&
> (tx_info->rate_driver_data[0] == NULL ||
> ampdu_pars->sta == NULL ||
> +           
> tx_info->rate_driver_data[0] == ampdu_pars->sta);
>      rc = rc &&
> ((u8)(mpdu->priority) == ampdu_pars->tid);
>      return rc;
>  }
> @@ -1210,8 +1210,8 @@ static void dma_cb_fn_ampdu(void *txi,
> void *arg_a)
>      struct ieee80211_tx_info *tx_info =
> (struct ieee80211_tx_info *)txi;
>  
>      if ((tx_info->flags &
> IEEE80211_TX_CTL_AMPDU) &&
> -        (tx_info->control.sta
> == sta || sta == NULL))
> -       
> tx_info->control.sta = NULL;
> +       
> (tx_info->rate_driver_data[0] == sta || sta == NULL))
> +       
> tx_info->rate_driver_data[0] = NULL;
>  }
>  
>  /*
> diff --git
> a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
> b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
> index 2d5a404..d270c4f 100644
> --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
> +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
> @@ -264,9 +264,12 @@ static void brcms_set_basic_rate(struct
> brcm_rateset *rs, u16 rate, bool is_br)
>      }
>  }
>  
> -static void brcms_ops_tx(struct ieee80211_hw *hw, struct
> sk_buff *skb)
> +static void brcms_ops_tx(struct ieee80211_hw *hw,
> +       
>      struct ieee80211_tx_control
> *control,
> +       
>      struct sk_buff *skb)
>  {
>      struct brcms_info *wl = hw->priv;
> +    struct ieee80211_tx_info *tx_info =
> IEEE80211_SKB_CB(skb);
>  
>      spin_lock_bh(&wl->lock);
>      if (!wl->pub->up) {
> @@ -275,6 +278,7 @@ static void brcms_ops_tx(struct
> ieee80211_hw *hw, struct sk_buff *skb)
>          goto done;
>      }
>      brcms_c_sendpkt_mac80211(wl->wlc,
> skb, hw);
> +    tx_info->rate_driver_data[0] =
> control->sta;
>   done:
>      spin_unlock_bh(&wl->lock);
>  }
> diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c
> b/drivers/net/wireless/brcm80211/brcmsmac/main.c
> index 8776fbc..28dd37e3 100644
> --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c
> +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c
> @@ -879,7 +879,7 @@ brcms_c_dotxstatus(struct brcms_c_info
> *wlc, struct tx_status *txs)
>      tx_info = IEEE80211_SKB_CB(p);
>      h = (struct ieee80211_hdr *)((u8 *) (txh
> + 1) + D11_PHY_HDR_LEN);
>  
> -    if (tx_info->control.sta)
> +    if (tx_info->rate_driver_data[0])
>          scb =
> &wlc->pri_scb;
>  
>      if (tx_info->flags &
> IEEE80211_TX_CTL_AMPDU) {
> diff --git a/drivers/net/wireless/iwlegacy/3945-mac.c
> b/drivers/net/wireless/iwlegacy/3945-mac.c
> index faec404..7efec7c 100644
> --- a/drivers/net/wireless/iwlegacy/3945-mac.c
> +++ b/drivers/net/wireless/iwlegacy/3945-mac.c
> @@ -460,7 +460,9 @@ il3945_build_tx_cmd_basic(struct il_priv
> *il, struct il_device_cmd *cmd,
>   * start C_TX command process
>   */
>  static int
> -il3945_tx_skb(struct il_priv *il, struct sk_buff *skb)
> +il3945_tx_skb(struct il_priv *il,
> +           struct
> ieee80211_tx_control *control,
> +           struct
> sk_buff *skb)
>  {
>      struct ieee80211_hdr *hdr = (struct
> ieee80211_hdr *)skb->data;
>      struct ieee80211_tx_info *info =
> IEEE80211_SKB_CB(skb);
> @@ -512,7 +514,7 @@ il3945_tx_skb(struct il_priv *il, struct
> sk_buff *skb)
>      hdr_len = ieee80211_hdrlen(fc);
>  
>      /* Find idx into station table for
> destination station */
> -    sta_id = il_sta_id_or_broadcast(il,
> info->control.sta);
> +    sta_id = il_sta_id_or_broadcast(il,
> control->sta);
>      if (sta_id == IL_INVALID_STATION) {
>          D_DROP("Dropping -
> INVALID STATION: %pM\n", hdr->addr1);
>          goto drop;
> @@ -2859,7 +2861,9 @@ il3945_mac_stop(struct ieee80211_hw
> *hw)
>  }
>  
>  static void
> -il3945_mac_tx(struct ieee80211_hw *hw, struct sk_buff
> *skb)
> +il3945_mac_tx(struct ieee80211_hw *hw,
> +           struct
> ieee80211_tx_control *control,
> +           struct
> sk_buff *skb)
>  {
>      struct il_priv *il = hw->priv;
>  
> @@ -2868,7 +2872,7 @@ il3945_mac_tx(struct ieee80211_hw *hw,
> struct sk_buff *skb)
>      D_TX("dev->xmit(%d bytes) at rate
> 0x%02x\n", skb->len,
>       
>    ieee80211_get_tx_rate(hw,
> IEEE80211_SKB_CB(skb))->bitrate);
>  
> -    if (il3945_tx_skb(il, skb))
> +    if (il3945_tx_skb(il, control, skb))
>         
> dev_kfree_skb_any(skb);
>  
>      D_MAC80211("leave\n");
> diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c
> b/drivers/net/wireless/iwlegacy/4965-mac.c
> index 34f61a0..95e3b34 100644
> --- a/drivers/net/wireless/iwlegacy/4965-mac.c
> +++ b/drivers/net/wireless/iwlegacy/4965-mac.c
> @@ -1526,8 +1526,11 @@ il4965_tx_cmd_build_basic(struct
> il_priv *il, struct sk_buff *skb,
>  }
>  
>  static void
> -il4965_tx_cmd_build_rate(struct il_priv *il, struct
> il_tx_cmd *tx_cmd,
> -       
>      struct ieee80211_tx_info
> *info, __le16 fc)
> +il4965_tx_cmd_build_rate(struct il_priv *il,
> +       
>      struct il_tx_cmd *tx_cmd,
> +       
>      struct ieee80211_tx_info
> *info,
> +       
>      struct ieee80211_tx_control
> *control,
> +       
>      __le16 fc)
>  {
>      const u8 rts_retry_limit = 60;
>      u32 rate_flags;
> @@ -1563,7 +1566,7 @@ il4965_tx_cmd_build_rate(struct
> il_priv *il, struct il_tx_cmd *tx_cmd,
>          || rate_idx >
> RATE_COUNT_LEGACY)
>          rate_idx =
>             
> rate_lowest_index(&il->bands[info->band],
> -           
>          
> info->control.sta);
> +           
>           control->sta);
>      /* For 5 GHZ band, remap mac80211 rate
> indices into driver indices */
>      if (info->band ==
> IEEE80211_BAND_5GHZ)
>          rate_idx +=
> IL_FIRST_OFDM_RATE;
> @@ -1630,11 +1633,13 @@ il4965_tx_cmd_build_hwcrypto(struct
> il_priv *il, struct ieee80211_tx_info *info,
>   * start C_TX command process
>   */
>  int
> -il4965_tx_skb(struct il_priv *il, struct sk_buff *skb)
> +il4965_tx_skb(struct il_priv *il,
> +          struct
> ieee80211_tx_control *control,
> +          struct sk_buff
> *skb)
>  {
>      struct ieee80211_hdr *hdr = (struct
> ieee80211_hdr *)skb->data;
>      struct ieee80211_tx_info *info =
> IEEE80211_SKB_CB(skb);
> -    struct ieee80211_sta *sta =
> info->control.sta;
> +    struct ieee80211_sta *sta =
> control->sta;
>      struct il_station_priv *sta_priv =
> NULL;
>      struct il_tx_queue *txq;
>      struct il_queue *q;
> @@ -1680,7 +1685,7 @@ il4965_tx_skb(struct il_priv *il,
> struct sk_buff *skb)
>          sta_id =
> il->hw_params.bcast_id;
>      else {
>          /* Find idx into
> station table for destination station */
> -        sta_id =
> il_sta_id_or_broadcast(il, info->control.sta);
> +        sta_id =
> il_sta_id_or_broadcast(il, control->sta);
>  
>          if (sta_id ==
> IL_INVALID_STATION) {
>             
> D_DROP("Dropping - INVALID STATION: %pM\n", hdr->addr1);
> @@ -1786,7 +1791,7 @@ il4965_tx_skb(struct il_priv *il,
> struct sk_buff *skb)
>      /* TODO need this for burst mode later
> on */
>      il4965_tx_cmd_build_basic(il, skb,
> tx_cmd, info, hdr, sta_id);
>  
> -    il4965_tx_cmd_build_rate(il, tx_cmd,
> info, fc);
> +    il4965_tx_cmd_build_rate(il, tx_cmd,
> info, control, fc);
>  
>      il_update_stats(il, true, fc, len);
>      /*
> @@ -5828,7 +5833,9 @@ il4965_mac_stop(struct ieee80211_hw
> *hw)
>  }
>  
>  void
> -il4965_mac_tx(struct ieee80211_hw *hw, struct sk_buff
> *skb)
> +il4965_mac_tx(struct ieee80211_hw *hw,
> +          struct
> ieee80211_tx_control *control,
> +          struct sk_buff
> *skb)
>  {
>      struct il_priv *il = hw->priv;
>  
> @@ -5837,7 +5844,7 @@ il4965_mac_tx(struct ieee80211_hw *hw,
> struct sk_buff *skb)
>      D_TX("dev->xmit(%d bytes) at rate
> 0x%02x\n", skb->len,
>       
>    ieee80211_get_tx_rate(hw,
> IEEE80211_SKB_CB(skb))->bitrate);
>  
> -    if (il4965_tx_skb(il, skb))
> +    if (il4965_tx_skb(il, control, skb))
>         
> dev_kfree_skb_any(skb);
>  
>      D_MACDUMP("leave\n");
> diff --git a/drivers/net/wireless/iwlegacy/4965.h
> b/drivers/net/wireless/iwlegacy/4965.h
> index 1db6776..29e77bf 100644
> --- a/drivers/net/wireless/iwlegacy/4965.h
> +++ b/drivers/net/wireless/iwlegacy/4965.h
> @@ -78,7 +78,9 @@ int il4965_hw_txq_attach_buf_to_tfd(struct
> il_priv *il, struct il_tx_queue *txq,
>  int il4965_hw_tx_queue_init(struct il_priv *il, struct
> il_tx_queue *txq);
>  void il4965_hwrate_to_tx_control(struct il_priv *il, u32
> rate_n_flags,
>             
>      struct ieee80211_tx_info
> *info);
> -int il4965_tx_skb(struct il_priv *il, struct sk_buff
> *skb);
> +int il4965_tx_skb(struct il_priv *il,
> +          struct
> ieee80211_tx_control *control,
> +          struct sk_buff
> *skb);
>  int il4965_tx_agg_start(struct il_priv *il, struct
> ieee80211_vif *vif,
>             
> struct ieee80211_sta *sta, u16 tid, u16 * ssn);
>  int il4965_tx_agg_stop(struct il_priv *il, struct
> ieee80211_vif *vif,
> @@ -163,7 +165,9 @@ void
> il4965_eeprom_release_semaphore(struct il_priv *il);
>  int il4965_eeprom_check_version(struct il_priv *il);
>  
>  /* mac80211 handlers (for 4965) */
> -void il4965_mac_tx(struct ieee80211_hw *hw, struct sk_buff
> *skb);
> +void il4965_mac_tx(struct ieee80211_hw *hw,
> +       
>    struct ieee80211_tx_control *control,
> +       
>    struct sk_buff *skb);
>  int il4965_mac_start(struct ieee80211_hw *hw);
>  void il4965_mac_stop(struct ieee80211_hw *hw);
>  void il4965_configure_filter(struct ieee80211_hw *hw,
> diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h
> b/drivers/net/wireless/iwlwifi/dvm/agn.h
> index 9bb16bd..a02d843 100644
> --- a/drivers/net/wireless/iwlwifi/dvm/agn.h
> +++ b/drivers/net/wireless/iwlwifi/dvm/agn.h
> @@ -201,7 +201,9 @@ void iwl_chswitch_done(struct iwl_priv
> *priv, bool is_success);
>  
>  
>  /* tx */
> -int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff
> *skb);
> +int iwlagn_tx_skb(struct iwl_priv *priv,
> +          struct
> ieee80211_tx_control *control,
> +          struct sk_buff
> *skb);
>  int iwlagn_tx_agg_start(struct iwl_priv *priv, struct
> ieee80211_vif *vif,
>             
> struct ieee80211_sta *sta, u16 tid, u16 *ssn);
>  int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct
> ieee80211_vif *vif,
> diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c
> b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
> index a5f7bce..6bc03a3 100644
> --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c
> +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
> @@ -511,14 +511,16 @@ static void
> iwlagn_mac_set_wakeup(struct ieee80211_hw *hw, bool
> enabled)
>  }
>  #endif
>  
> -static void iwlagn_mac_tx(struct ieee80211_hw *hw, struct
> sk_buff *skb)
> +static void iwlagn_mac_tx(struct ieee80211_hw *hw,
> +           
>   struct ieee80211_tx_control *control,
> +           
>   struct sk_buff *skb)
>  {
>      struct iwl_priv *priv =
> IWL_MAC80211_GET_DVM(hw);
>  
>      IWL_DEBUG_TX(priv, "dev->xmit(%d
> bytes) at rate 0x%02x\n", skb->len,
>           
>    ieee80211_get_tx_rate(hw,
> IEEE80211_SKB_CB(skb))->bitrate);
>  
> -    if (iwlagn_tx_skb(priv, skb))
> +    if (iwlagn_tx_skb(priv, control, skb))
>         
> dev_kfree_skb_any(skb);
>  }
>  
> diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c
> b/drivers/net/wireless/iwlwifi/dvm/tx.c
> index 5971a23..fc386dc 100644
> --- a/drivers/net/wireless/iwlwifi/dvm/tx.c
> +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c
> @@ -127,6 +127,7 @@ static void
> iwlagn_tx_cmd_build_basic(struct iwl_priv *priv,
>  static void iwlagn_tx_cmd_build_rate(struct iwl_priv
> *priv,
>             
>          struct
> iwl_tx_cmd *tx_cmd,
>             
>          struct
> ieee80211_tx_info *info,
> +           
>          struct
> ieee80211_tx_control *control,
>             
>          __le16 fc)
>  {
>      u32 rate_flags;
> @@ -188,7 +189,7 @@ static void
> iwlagn_tx_cmd_build_rate(struct iwl_priv *priv,
>             
> (rate_idx < 0) || (rate_idx > IWL_RATE_COUNT_LEGACY))
>          rate_idx =
> rate_lowest_index(
>             
>    
> &priv->eeprom_data->bands[info->band],
> -           
>     info->control.sta);
> +           
>     control->sta);
>      /* For 5 GHZ band, remap mac80211 rate
> indices into driver indices */
>      if (info->band ==
> IEEE80211_BAND_5GHZ)
>          rate_idx +=
> IWL_FIRST_OFDM_RATE;
> @@ -291,7 +292,9 @@ static int
> iwl_sta_id_or_broadcast(struct iwl_rxon_context *context,
>  /*
>   * start REPLY_TX command process
>   */
> -int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff
> *skb)
> +int iwlagn_tx_skb(struct iwl_priv *priv,
> +          struct
> ieee80211_tx_control *control,
> +          struct sk_buff
> *skb)
>  {
>      struct ieee80211_hdr *hdr = (struct
> ieee80211_hdr *)skb->data;
>      struct ieee80211_tx_info *info =
> IEEE80211_SKB_CB(skb);
> @@ -345,7 +348,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv,
> struct sk_buff *skb)
>          sta_id =
> ctx->bcast_sta_id;
>      else {
>          /* Find index into
> station table for destination station */
> -        sta_id =
> iwl_sta_id_or_broadcast(ctx, info->control.sta);
> +        sta_id =
> iwl_sta_id_or_broadcast(ctx, control->sta);
>          if (sta_id ==
> IWL_INVALID_STATION) {
>             
> IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n",
>             
>        
>    hdr->addr1);
> @@ -355,8 +358,8 @@ int iwlagn_tx_skb(struct iwl_priv *priv,
> struct sk_buff *skb)
>  
>      IWL_DEBUG_TX(priv, "station Id %d\n",
> sta_id);
>  
> -    if (info->control.sta)
> -        sta_priv = (void
> *)info->control.sta->drv_priv;
> +    if (control->sta)
> +        sta_priv = (void
> *)control->sta->drv_priv;
>  
>      if (sta_priv &&
> sta_priv->asleep &&
>          (info->flags &
> IEEE80211_TX_CTL_NO_PS_BUFFER)) {
> @@ -397,7 +400,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv,
> struct sk_buff *skb)
>      /* TODO need this for burst mode later
> on */
>      iwlagn_tx_cmd_build_basic(priv, skb,
> tx_cmd, info, hdr, sta_id);
>  
> -    iwlagn_tx_cmd_build_rate(priv, tx_cmd,
> info, fc);
> +    iwlagn_tx_cmd_build_rate(priv, tx_cmd,
> info, control, fc);
>  
>      memset(&info->status, 0,
> sizeof(info->status));
>  
> diff --git a/drivers/net/wireless/mac80211_hwsim.c
> b/drivers/net/wireless/mac80211_hwsim.c
> index 3f38d84..a55c70b 100644
> --- a/drivers/net/wireless/mac80211_hwsim.c
> +++ b/drivers/net/wireless/mac80211_hwsim.c
> @@ -709,7 +709,9 @@ static bool
> mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
>      return ack;
>  }
>  
> -static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
> struct sk_buff *skb)
> +static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
> +           
>       struct ieee80211_tx_control *control,
> +           
>       struct sk_buff *skb)
>  {
>      bool ack;
>      struct ieee80211_tx_info *txi;
> @@ -741,8 +743,8 @@ static void mac80211_hwsim_tx(struct
> ieee80211_hw *hw, struct sk_buff *skb)
>  
>      if (txi->control.vif)
>         
> hwsim_check_magic(txi->control.vif);
> -    if (txi->control.sta)
> -       
> hwsim_check_sta_magic(txi->control.sta);
> +    if (control->sta)
> +       
> hwsim_check_sta_magic(control->sta);
>  
>      ieee80211_tx_info_clear_status(txi);
>  
> @@ -1495,6 +1497,8 @@ static int
> hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
>      struct hwsim_tx_rate *tx_attempts;
>      unsigned long ret_skb_ptr;
>      struct sk_buff *skb, *tmp;
> +    struct ieee80211_tx_control control;
> +    struct ieee80211_sta *sta =
> control.sta;
>      struct mac_address *src;
>      unsigned int hwsim_flags;
>  
> @@ -1542,8 +1546,8 @@ static int
> hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
>  
>      if (txi->control.vif)
>         
> hwsim_check_magic(txi->control.vif);
> -    if (txi->control.sta)
> -       
> hwsim_check_sta_magic(txi->control.sta);
> +    if (sta)
> +       
> hwsim_check_sta_magic(sta);
>  
>      ieee80211_tx_info_clear_status(txi);
>  
> diff --git a/drivers/net/wireless/mwl8k.c
> b/drivers/net/wireless/mwl8k.c
> index 1404373..af669a2 100644
> --- a/drivers/net/wireless/mwl8k.c
> +++ b/drivers/net/wireless/mwl8k.c
> @@ -1610,7 +1610,9 @@ static int mwl8k_tid_queue_mapping(u8
> tid)
>  #define RI_RATE_ID_MCS(a)     ((a
> & 0x01f8) >> 3)
>  
>  static int
> -mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int
> limit, int force)
> +mwl8k_txq_reclaim(struct ieee80211_hw *hw,
> +          struct
> ieee80211_tx_control *control,
> +          int index, int
> limit, int force)
>  {
>      struct mwl8k_priv *priv = hw->priv;
>      struct mwl8k_tx_queue *txq =
> priv->txq + index;
> @@ -1708,12 +1710,13 @@ mwl8k_txq_reclaim(struct
> ieee80211_hw *hw, int index, int limit, int force)
>  static void mwl8k_txq_deinit(struct ieee80211_hw *hw, int
> index)
>  {
>      struct mwl8k_priv *priv = hw->priv;
> +    struct ieee80211_tx_control control;
>      struct mwl8k_tx_queue *txq =
> priv->txq + index;
>  
>      if (txq->txd == NULL)
>          return;
>  
> -    mwl8k_txq_reclaim(hw, index, INT_MAX,
> 1);
> +    mwl8k_txq_reclaim(hw, &control,
> index, INT_MAX, 1);
>  
>      kfree(txq->skb);
>      txq->skb = NULL;
> @@ -1828,7 +1831,10 @@ static inline void
> mwl8k_tx_count_packet(struct ieee80211_sta *sta, u8 tid)
>  }
>  
>  static void
> -mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct
> sk_buff *skb)
> +mwl8k_txq_xmit(struct ieee80211_hw *hw,
> +           int
> index,
> +           struct
> ieee80211_tx_control *control,
> +           struct
> sk_buff *skb)
>  {
>      struct mwl8k_priv *priv = hw->priv;
>      struct ieee80211_tx_info *tx_info;
> @@ -1865,7 +1871,7 @@ mwl8k_txq_xmit(struct ieee80211_hw
> *hw, int index, struct sk_buff *skb)
>      wh = &((struct mwl8k_dma_data
> *)skb->data)->wh;
>  
>      tx_info = IEEE80211_SKB_CB(skb);
> -    sta = tx_info->control.sta;
> +    sta = control->sta;
>      mwl8k_vif =
> MWL8K_VIF(tx_info->control.vif);
>  
>      if (tx_info->flags &
> IEEE80211_TX_CTL_ASSIGN_SEQ) {
> @@ -2017,8 +2023,8 @@ mwl8k_txq_xmit(struct ieee80211_hw
> *hw, int index, struct sk_buff *skb)
>      tx->pkt_phys_addr =
> cpu_to_le32(dma);
>      tx->pkt_len =
> cpu_to_le16(skb->len);
>      tx->rate_info = 0;
> -    if (!priv->ap_fw &&
> tx_info->control.sta != NULL)
> -        tx->peer_id =
> MWL8K_STA(tx_info->control.sta)->peer_id;
> +    if (!priv->ap_fw &&
> control->sta != NULL)
> +        tx->peer_id =
> MWL8K_STA(control->sta)->peer_id;
>      else
>          tx->peer_id = 0;
>  
> @@ -4315,6 +4321,7 @@ static void mwl8k_tx_poll(unsigned
> long data)
>  {
>      struct ieee80211_hw *hw = (struct
> ieee80211_hw *)data;
>      struct mwl8k_priv *priv = hw->priv;
> +    struct ieee80211_tx_control control;
>      int limit;
>      int i;
>  
> @@ -4323,7 +4330,7 @@ static void mwl8k_tx_poll(unsigned
> long data)
>      spin_lock_bh(&priv->tx_lock);
>  
>      for (i = 0; i <
> mwl8k_tx_queues(priv); i++)
> -        limit -=
> mwl8k_txq_reclaim(hw, i, limit, 0);
> +        limit -=
> mwl8k_txq_reclaim(hw, &control, i, limit, 0);
>  
>      if (!priv->pending_tx_pkts &&
> priv->tx_wait != NULL) {
>         
> complete(priv->tx_wait);
> @@ -4362,7 +4369,9 @@ static void mwl8k_rx_poll(unsigned
> long data)
>  /*
>   * Core driver operations.
>   */
> -static void mwl8k_tx(struct ieee80211_hw *hw, struct
> sk_buff *skb)
> +static void mwl8k_tx(struct ieee80211_hw *hw,
> +         
>    struct ieee80211_tx_control *control,
> +         
>    struct sk_buff *skb)
>  {
>      struct mwl8k_priv *priv = hw->priv;
>      int index = skb_get_queue_mapping(skb);
> @@ -4374,7 +4383,7 @@ static void mwl8k_tx(struct
> ieee80211_hw *hw, struct sk_buff *skb)
>          return;
>      }
>  
> -    mwl8k_txq_xmit(hw, index, skb);
> +    mwl8k_txq_xmit(hw, index, control,
> skb);
>  }
>  
>  static int mwl8k_start(struct ieee80211_hw *hw)
> @@ -4439,6 +4448,7 @@ static int mwl8k_start(struct
> ieee80211_hw *hw)
>  static void mwl8k_stop(struct ieee80211_hw *hw)
>  {
>      struct mwl8k_priv *priv = hw->priv;
> +    struct ieee80211_tx_control control;
>      int i;
>  
>      if (!priv->hw_restart_in_progress)
> @@ -4465,7 +4475,7 @@ static void mwl8k_stop(struct
> ieee80211_hw *hw)
>  
>      /* Return all skbs to mac80211 */
>      for (i = 0; i <
> mwl8k_tx_queues(priv); i++)
> -        mwl8k_txq_reclaim(hw,
> i, INT_MAX, 1);
> +        mwl8k_txq_reclaim(hw,
> &control, i, INT_MAX, 1);
>  }
>  
>  static int mwl8k_reload_firmware(struct ieee80211_hw *hw,
> char *fw_image);
> @@ -5841,6 +5851,7 @@ static void __devexit
> mwl8k_shutdown(struct pci_dev *pdev)
>  static void __devexit mwl8k_remove(struct pci_dev *pdev)
>  {
>      struct ieee80211_hw *hw =
> pci_get_drvdata(pdev);
> +    struct ieee80211_tx_control control;
>      struct mwl8k_priv *priv;
>      int i;
>  
> @@ -5868,7 +5879,7 @@ static void __devexit
> mwl8k_remove(struct pci_dev *pdev)
>  
>      /* Return all skbs to mac80211 */
>      for (i = 0; i <
> mwl8k_tx_queues(priv); i++)
> -        mwl8k_txq_reclaim(hw,
> i, INT_MAX, 1);
> +        mwl8k_txq_reclaim(hw,
> &control, i, INT_MAX, 1);
>  
>      for (i = 0; i <
> mwl8k_tx_queues(priv); i++)
>          mwl8k_txq_deinit(hw,
> i);
> diff --git a/drivers/net/wireless/p54/lmac.h
> b/drivers/net/wireless/p54/lmac.h
> index 3d8d622..de1d46b 100644
> --- a/drivers/net/wireless/p54/lmac.h
> +++ b/drivers/net/wireless/p54/lmac.h
> @@ -526,7 +526,9 @@ int p54_init_leds(struct p54_common
> *priv);
>  void p54_unregister_leds(struct p54_common *priv);
>  
>  /* xmit functions */
> -void p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff
> *skb);
> +void p54_tx_80211(struct ieee80211_hw *dev,
> +          struct
> ieee80211_tx_control *control,
> +          struct sk_buff
> *skb);
>  int p54_tx_cancel(struct p54_common *priv, __le32 req_id);
>  void p54_tx(struct p54_common *priv, struct sk_buff *skb);
>  
> diff --git a/drivers/net/wireless/p54/main.c
> b/drivers/net/wireless/p54/main.c
> index 7cffea7..9c8ce8e 100644
> --- a/drivers/net/wireless/p54/main.c
> +++ b/drivers/net/wireless/p54/main.c
> @@ -140,6 +140,7 @@ static int p54_beacon_update(struct
> p54_common *priv,
>             
> struct ieee80211_vif *vif)
>  {
>      struct sk_buff *beacon;
> +    struct ieee80211_tx_control control;
>      int ret;
>  
>      beacon =
> ieee80211_beacon_get(priv->hw, vif);
> @@ -158,7 +159,7 @@ static int p54_beacon_update(struct
> p54_common *priv,
>       * to cancel the old beacon
> template by hand, instead the firmware
>       * will release the previous
> one through the feedback mechanism.
>       */
> -    p54_tx_80211(priv->hw, beacon);
> +    p54_tx_80211(priv->hw, &control,
> beacon);
>      priv->tsf_high32 = 0;
>      priv->tsf_low32 = 0;
>  
> diff --git a/drivers/net/wireless/p54/txrx.c
> b/drivers/net/wireless/p54/txrx.c
> index f38786e..edec732 100644
> --- a/drivers/net/wireless/p54/txrx.c
> +++ b/drivers/net/wireless/p54/txrx.c
> @@ -426,7 +426,7 @@ static void p54_rx_frame_sent(struct
> p54_common *priv, struct sk_buff *skb)
>         
>    sizeof(struct ieee80211_tx_info) -
>         
>    offsetof(struct ieee80211_tx_info,
> status.ack_signal));
>      BUILD_BUG_ON(offsetof(struct
> ieee80211_tx_info,
> -           
>       status.ack_signal) != 20);
> +           
>       status.ack_signal) != 24);
>  
>      if (entry_hdr->flags &
> cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN))
>          pad =
> entry_data->align[0];
> @@ -676,8 +676,9 @@ int p54_rx(struct ieee80211_hw *dev,
> struct sk_buff *skb)
>  EXPORT_SYMBOL_GPL(p54_rx);
>  
>  static void p54_tx_80211_header(struct p54_common *priv,
> struct sk_buff *skb,
> -           
>     struct ieee80211_tx_info *info, u8
> *queue,
> -           
>     u32 *extra_len, u16 *flags, u16 *aid,
> +           
>     struct ieee80211_tx_info *info,
> +           
>     struct ieee80211_tx_control *control,
> +           
>     u8 *queue, u32 *extra_len, u16 *flags,
> u16 *aid,
>             
>     bool *burst_possible)
>  {
>      struct ieee80211_hdr *hdr = (struct
> ieee80211_hdr *)skb->data;
> @@ -746,8 +747,8 @@ static void p54_tx_80211_header(struct
> p54_common *priv, struct sk_buff *skb,
>              }
>          }
>  
> -        if
> (info->control.sta)
> -           
> *aid = info->control.sta->aid;
> +        if (control->sta)
> +           
> *aid = control->sta->aid;
>          break;
>      }
>  }
> @@ -767,7 +768,9 @@ static u8 p54_convert_algo(u32 cipher)
>      }
>  }
>  
> -void p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff
> *skb)
> +void p54_tx_80211(struct ieee80211_hw *dev,
> +          struct
> ieee80211_tx_control *control,
> +          struct sk_buff
> *skb)
>  {
>      struct p54_common *priv = dev->priv;
>      struct ieee80211_tx_info *info =
> IEEE80211_SKB_CB(skb);
> @@ -784,7 +787,7 @@ void p54_tx_80211(struct ieee80211_hw
> *dev, struct sk_buff *skb)
>      u8 nrates = 0, nremaining = 8;
>      bool burst_allowed = false;
>  
> -    p54_tx_80211_header(priv, skb, info,
> &queue, &extra_len,
> +    p54_tx_80211_header(priv, skb, info,
> control, &queue, &extra_len,
>             
>     &hdr_flags, &aid,
> &burst_allowed);
>  
>      if (p54_tx_qos_accounting_alloc(priv,
> skb, queue)) {
> diff --git a/drivers/net/wireless/rt2x00/rt2x00.h
> b/drivers/net/wireless/rt2x00/rt2x00.h
> index 8afb546..f991e8b 100644
> --- a/drivers/net/wireless/rt2x00/rt2x00.h
> +++ b/drivers/net/wireless/rt2x00/rt2x00.h
> @@ -1287,7 +1287,9 @@ void rt2x00lib_rxdone(struct
> queue_entry *entry, gfp_t gfp);
>  /*
>   * mac80211 handlers.
>   */
> -void rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff
> *skb);
> +void rt2x00mac_tx(struct ieee80211_hw *hw,
> +          struct
> ieee80211_tx_control *control,
> +          struct sk_buff
> *skb);
>  int rt2x00mac_start(struct ieee80211_hw *hw);
>  void rt2x00mac_stop(struct ieee80211_hw *hw);
>  int rt2x00mac_add_interface(struct ieee80211_hw *hw,
> diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c
> b/drivers/net/wireless/rt2x00/rt2x00dev.c
> index a6b88bd..87e335a 100644
> --- a/drivers/net/wireless/rt2x00/rt2x00dev.c
> +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
> @@ -181,6 +181,7 @@ static void
> rt2x00lib_bc_buffer_iter(void *data, u8 *mac,
>             
>          struct
> ieee80211_vif *vif)
>  {
>      struct rt2x00_dev *rt2x00dev = data;
> +    struct ieee80211_tx_control control;
>      struct sk_buff *skb;
>  
>      /*
> @@ -194,7 +195,7 @@ static void
> rt2x00lib_bc_buffer_iter(void *data, u8 *mac,
>       */
>      skb =
> ieee80211_get_buffered_bc(rt2x00dev->hw, vif);
>      while (skb) {
> -       
> rt2x00mac_tx(rt2x00dev->hw, skb);
> +       
> rt2x00mac_tx(rt2x00dev->hw, &control, skb);
>          skb =
> ieee80211_get_buffered_bc(rt2x00dev->hw, vif);
>      }
>  }
> diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c
> b/drivers/net/wireless/rt2x00/rt2x00mac.c
> index 4ff26c2..c3d0f2f 100644
> --- a/drivers/net/wireless/rt2x00/rt2x00mac.c
> +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
> @@ -99,7 +99,9 @@ static int rt2x00mac_tx_rts_cts(struct
> rt2x00_dev *rt2x00dev,
>      return retval;
>  }
>  
> -void rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff
> *skb)
> +void rt2x00mac_tx(struct ieee80211_hw *hw,
> +          struct
> ieee80211_tx_control *control,
> +          struct sk_buff
> *skb)
>  {
>      struct rt2x00_dev *rt2x00dev =
> hw->priv;
>      struct ieee80211_tx_info *tx_info =
> IEEE80211_SKB_CB(skb);
> diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c
> b/drivers/net/wireless/rt2x00/rt2x00queue.c
> index 2fd8301..d80118d 100644
> --- a/drivers/net/wireless/rt2x00/rt2x00queue.c
> +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
> @@ -315,6 +315,7 @@ static void
> rt2x00queue_create_tx_descriptor_plcp(struct rt2x00_dev
> *rt2x00dev,
>  static void rt2x00queue_create_tx_descriptor_ht(struct
> rt2x00_dev *rt2x00dev,
>             
>            
> struct sk_buff *skb,
>             
>            
> struct txentry_desc *txdesc,
> +           
>            
> struct ieee80211_tx_control *control,
>             
>            
> const struct rt2x00_rate *hwrate)
>  {
>      struct ieee80211_tx_info *tx_info =
> IEEE80211_SKB_CB(skb);
> @@ -322,11 +323,11 @@ static void
> rt2x00queue_create_tx_descriptor_ht(struct rt2x00_dev
> *rt2x00dev,
>      struct ieee80211_hdr *hdr = (struct
> ieee80211_hdr *)skb->data;
>      struct rt2x00_sta *sta_priv = NULL;
>  
> -    if (tx_info->control.sta) {
> +    if (control->sta) {
>         
> txdesc->u.ht.mpdu_density =
> -           
> tx_info->control.sta->ht_cap.ampdu_density;
> +           
> control->sta->ht_cap.ampdu_density;
>  
> -        sta_priv =
> sta_to_rt2x00_sta(tx_info->control.sta);
> +        sta_priv =
> sta_to_rt2x00_sta(control->sta);
>          txdesc->u.ht.wcid
> = sta_priv->wcid;
>      }
>  
> @@ -341,8 +342,8 @@ static void
> rt2x00queue_create_tx_descriptor_ht(struct rt2x00_dev
> *rt2x00dev,
>           * MIMO PS
> should be set to 1 for STA's using dynamic SM PS
>           * when
> using more then one tx stream (>MCS7).
>           */
> -        if
> (tx_info->control.sta && txdesc->u.ht.mcs >
> 7 &&
> -           
> ((tx_info->control.sta->ht_cap.cap &
> +        if (control->sta
> && txdesc->u.ht.mcs > 7 &&
> +           
> ((control->sta->ht_cap.cap &
>               
> IEEE80211_HT_CAP_SM_PS) >>
>           
>    IEEE80211_HT_CAP_SM_PS_SHIFT) ==
>             
> WLAN_HT_CAP_SM_PS_DYNAMIC)
> @@ -409,7 +410,8 @@ static void
> rt2x00queue_create_tx_descriptor_ht(struct rt2x00_dev
> *rt2x00dev,
>  
>  static void rt2x00queue_create_tx_descriptor(struct
> rt2x00_dev *rt2x00dev,
>             
>          
>    struct sk_buff *skb,
> -           
>          
>    struct txentry_desc *txdesc)
> +           
>          
>    struct txentry_desc *txdesc,
> +           
>          
>    struct ieee80211_tx_control *control)
>  {
>      struct ieee80211_tx_info *tx_info =
> IEEE80211_SKB_CB(skb);
>      struct ieee80211_hdr *hdr = (struct
> ieee80211_hdr *)skb->data;
> @@ -503,7 +505,7 @@ static void
> rt2x00queue_create_tx_descriptor(struct rt2x00_dev
> *rt2x00dev,
>  
>      if (test_bit(REQUIRE_HT_TX_DESC,
> &rt2x00dev->cap_flags))
>         
> rt2x00queue_create_tx_descriptor_ht(rt2x00dev, skb, txdesc,
> -           
>            
>     hwrate);
> +           
>            
>    control, hwrate);
>      else
>         
> rt2x00queue_create_tx_descriptor_plcp(rt2x00dev, skb,
> txdesc,
>             
>            
>       hwrate);
> @@ -587,6 +589,7 @@ int rt2x00queue_write_tx_frame(struct
> data_queue *queue, struct sk_buff *skb,
>      struct queue_entry *entry;
>      struct txentry_desc txdesc;
>      struct skb_frame_desc *skbdesc;
> +    struct ieee80211_tx_control control;
>      u8 rate_idx, rate_flags;
>      int ret = 0;
>  
> @@ -595,7 +598,7 @@ int rt2x00queue_write_tx_frame(struct
> data_queue *queue, struct sk_buff *skb,
>       * after that we are free to
> use the skb->cb array
>       * for our information.
>       */
> -   
> rt2x00queue_create_tx_descriptor(queue->rt2x00dev, skb,
> &txdesc);
> +   
> rt2x00queue_create_tx_descriptor(queue->rt2x00dev, skb,
> &txdesc, &control);
>  
>      /*
>       * All information is
> retrieved from the skb->cb array,
> @@ -722,6 +725,7 @@ int
> rt2x00queue_update_beacon_locked(struct rt2x00_dev
> *rt2x00dev,
>      struct rt2x00_intf *intf =
> vif_to_intf(vif);
>      struct skb_frame_desc *skbdesc;
>      struct txentry_desc txdesc;
> +    struct ieee80211_tx_control control;
>  
>      if (unlikely(!intf->beacon))
>          return -ENOBUFS;
> @@ -740,7 +744,7 @@ int
> rt2x00queue_update_beacon_locked(struct rt2x00_dev
> *rt2x00dev,
>       * after that we are free to
> use the skb->cb array
>       * for our information.
>       */
> -   
> rt2x00queue_create_tx_descriptor(rt2x00dev,
> intf->beacon->skb, &txdesc);
> +   
> rt2x00queue_create_tx_descriptor(rt2x00dev,
> intf->beacon->skb, &txdesc, &control);
>  
>      /*
>       * Fill in skb descriptor
> diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c
> b/drivers/net/wireless/rtl818x/rtl8180/dev.c
> index 3b50539..69963f3 100644
> --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c
> +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c
> @@ -244,7 +244,9 @@ static irqreturn_t rtl8180_interrupt(int
> irq, void *dev_id)
>      return IRQ_HANDLED;
>  }
>  
> -static void rtl8180_tx(struct ieee80211_hw *dev, struct
> sk_buff *skb)
> +static void rtl8180_tx(struct ieee80211_hw *dev,
> +           
>    struct ieee80211_tx_control *control,
> +           
>    struct sk_buff *skb)
>  {
>      struct ieee80211_tx_info *info =
> IEEE80211_SKB_CB(skb);
>      struct ieee80211_hdr *hdr = (struct
> ieee80211_hdr *)skb->data;
> @@ -689,6 +691,7 @@ static void rtl8180_beacon_work(struct
> work_struct *work)
>          container_of((void
> *)vif_priv, struct ieee80211_vif, drv_priv);
>      struct ieee80211_hw *dev =
> vif_priv->dev;
>      struct ieee80211_mgmt *mgmt;
> +    struct ieee80211_tx_control control;
>      struct sk_buff *skb;
>  
>      /* don't overflow the tx ring */
> @@ -710,7 +713,7 @@ static void rtl8180_beacon_work(struct
> work_struct *work)
>      /* TODO: use actual beacon queue */
>      skb_set_queue_mapping(skb, 0);
>  
> -    rtl8180_tx(dev, skb);
> +    rtl8180_tx(dev, &control, skb);
>  
>  resched:
>      /*
> diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c
> b/drivers/net/wireless/rtl818x/rtl8187/dev.c
> index 4fb1ca1..5c4bd11 100644
> --- a/drivers/net/wireless/rtl818x/rtl8187/dev.c
> +++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c
> @@ -228,7 +228,9 @@ static void rtl8187_tx_cb(struct urb
> *urb)
>      }
>  }
>  
> -static void rtl8187_tx(struct ieee80211_hw *dev, struct
> sk_buff *skb)
> +static void rtl8187_tx(struct ieee80211_hw *dev,
> +           
>    struct ieee80211_tx_control *control,
> +           
>    struct sk_buff *skb)
>  {
>      struct rtl8187_priv *priv =
> dev->priv;
>      struct ieee80211_tx_info *info =
> IEEE80211_SKB_CB(skb);
> @@ -1055,6 +1057,7 @@ static void rtl8187_beacon_work(struct
> work_struct *work)
>          container_of((void
> *)vif_priv, struct ieee80211_vif, drv_priv);
>      struct ieee80211_hw *dev =
> vif_priv->dev;
>      struct ieee80211_mgmt *mgmt;
> +    struct ieee80211_tx_control control;
>      struct sk_buff *skb;
>  
>      /* don't overflow the tx ring */
> @@ -1076,7 +1079,7 @@ static void rtl8187_beacon_work(struct
> work_struct *work)
>      /* TODO: use actual beacon queue */
>      skb_set_queue_mapping(skb, 0);
>  
> -    rtl8187_tx(dev, skb);
> +    rtl8187_tx(dev, &control, skb);
>  
>  resched:
>      /*
> diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c
> b/drivers/net/wireless/zd1211rw/zd_mac.c
> index c9e2660..4b05427 100644
> --- a/drivers/net/wireless/zd1211rw/zd_mac.c
> +++ b/drivers/net/wireless/zd1211rw/zd_mac.c
> @@ -937,7 +937,9 @@ static int fill_ctrlset(struct zd_mac
> *mac,
>   * control block of the skbuff will be initialized. If
> necessary the incoming
>   * mac80211 queues will be stopped.
>   */
> -static void zd_op_tx(struct ieee80211_hw *hw, struct
> sk_buff *skb)
> +static void zd_op_tx(struct ieee80211_hw *hw,
> +         
>    struct ieee80211_tx_control *control,
> +         
>    struct sk_buff *skb)
>  {
>      struct zd_mac *mac = zd_hw_mac(hw);
>      struct ieee80211_tx_info *info =
> IEEE80211_SKB_CB(skb);
> @@ -1162,6 +1164,7 @@ static int zd_op_config(struct
> ieee80211_hw *hw, u32 changed)
>  
>  static void zd_beacon_done(struct zd_mac *mac)
>  {
> +    struct ieee80211_tx_control control;
>      struct sk_buff *skb, *beacon;
>  
>      if (!test_bit(ZD_DEVICE_RUNNING,
> &mac->flags))
> @@ -1176,7 +1179,7 @@ static void zd_beacon_done(struct
> zd_mac *mac)
>          skb =
> ieee80211_get_buffered_bc(mac->hw, mac->vif);
>          if (!skb)
>             
> break;
> -        zd_op_tx(mac->hw,
> skb);
> +        zd_op_tx(mac->hw,
> &control, skb);
>      }
>  
>      /*
> diff --git a/include/net/mac80211.h
> b/include/net/mac80211.h
> index e3fa90c..a5b8ce5 100644
> --- a/include/net/mac80211.h
> +++ b/include/net/mac80211.h
> @@ -483,32 +483,35 @@ enum mac80211_rate_control_flags {
>   * @idx: rate index to attempt to send with
>   * @flags: rate control flags (&enum
> mac80211_rate_control_flags)
>   * @count: number of tries in this rate before going
> to the next rate
> + * @tpc: transmit power level in dBm per packet
> multi-rate-retry (mrr) stage
>   *
>   * A value of -1 for @idx indicates an invalid rate
> and, if used
>   * in an array of retry rates, that no more rates
> should be tried.
>   *
>   * When used for transmit status reporting, the driver
> should
> - * always report the rate along with the flags it used.
> + * always report the rate  and power along with the
> flags it used.
>   *
>   * &struct ieee80211_tx_info contains an array of
> these structs
> - * in the control information, and it will be filled by the
> rate
> - * control algorithm according to what should be sent. For
> example,
> - * if this array contains, in the format { <idx>,
> <count> } the
> + * in the control information, and it will be filled by the
> joint rate-
> + * power control algorithm according to what should be
> sent. For example,
> + * if this array contains, in the format { <idx>,
> <count>, <tpc> } the
>   * information
> - *    { 3, 2 }, { 2, 2 }, { 1, 4 }, { -1, 0 }, {
> -1, 0 }
> + *    { 3, 2, 16 }, { 2, 2, 10 }, { 1, 4, 5 }, {
> -1, 0, 0 }
>   * then this means that the frame should be
> transmitted
> - * up to twice at rate 3, up to twice at rate 2, and up to
> four
> - * times at rate 1 if it doesn't get acknowledged. Say it
> gets
> - * acknowledged by the peer after the fifth attempt, the
> status
> + * up to twice at rate 3 with 16 dBm, up to twice at rate 2
> with 10 dBm,
> + * and up to four times at rate 1 with 5 dBm if it doesn't
> get acknowledged.
> + * Say it gets acknowledged by the peer after the fifth
> attempt, the status
>   * information should then contain
> - *   { 3, 2 }, { 2, 2 }, { 1, 1 }, { -1, 0 }
> ...
> - * since it was transmitted twice at rate 3, twice at rate
> 2
> - * and once at rate 1 after which we received an
> acknowledgement.
> + *   { 3, 2, 16 }, { 2, 2, 10 }, { 1, 1, 5
> }, { -1, 0, 0 }
> + * since it was transmitted twice at rate 3 with 16 dBm,
> twice at rate 2 with
> + * 10 dBm and once at rate 1 with 5 dBm after which we
> received an
> + * acknowledgement.
>   */
>  struct ieee80211_tx_rate {
>      s8 idx;
>      u8 count;
>      u8 flags;
> +    u8 tpc;
>  } __packed;
>  
>  /**
> @@ -519,9 +522,6 @@ struct ieee80211_tx_rate {
>   *  (2) driver internal use (if applicable)
>   *  (3) TX status information - driver tells
> mac80211 what happened
>   *
> - * The TX control's sta pointer is only valid during the
> ->tx call,
> - * it may be NULL.
> - *
>   * @flags: transmit info flags, defined above
>   * @band: the band to transmit on (use for checking
> for races)
>   * @hw_queue: HW queue to put the frame on,
> skb_get_queue_mapping() gives the AC
> @@ -529,11 +529,11 @@ struct ieee80211_tx_rate {
>   * @control: union for control data
>   * @status: union for status data
>   * @driver_data: array of driver_data pointers
> + * @ack_signal: signal strength of the ACK frame
>   * @ampdu_ack_len: number of acked aggregated frames.
>   *     relevant only if
> IEEE80211_TX_STAT_AMPDU was set.
>   * @ampdu_len: number of aggregated frames.
>   *     relevant only if
> IEEE80211_TX_STAT_AMPDU was set.
> - * @ack_signal: signal strength of the ACK frame
>   */
>  struct ieee80211_tx_info {
>      /* common information */
> @@ -547,7 +547,7 @@ struct ieee80211_tx_info {
>      union {
>          struct {
>             
> union {
> -           
>     /* rate control */
> +           
>     /* rate control and transmit power
> control */
>             
>     struct {
>             
>         struct
> ieee80211_tx_rate rates[
>             
>            
> IEEE80211_TX_MAX_RATES];
> @@ -559,7 +559,6 @@ struct ieee80211_tx_info {
>              /*
> NB: vif can be NULL for injected frames */
>             
> struct ieee80211_vif *vif;
>             
> struct ieee80211_key_conf *hw_key;
> -           
> struct ieee80211_sta *sta;
>          } control;
>          struct {
>             
> struct ieee80211_tx_rate rates[IEEE80211_TX_MAX_RATES];
> @@ -567,7 +566,7 @@ struct ieee80211_tx_info {
>              u8
> ampdu_ack_len;
>              u8
> ampdu_len;
>              u8
> antenna;
> -            /*
> 21 bytes free */
> +            /*
> 17 bytes free */
>          } status;
>          struct {
>             
> struct ieee80211_tx_rate driver_rates[
> @@ -634,7 +633,7 @@ ieee80211_tx_info_clear_status(struct
> ieee80211_tx_info *info)
>         
> info->status.rates[i].count = 0;
>  
>      BUILD_BUG_ON(
> -        offsetof(struct
> ieee80211_tx_info, status.ack_signal) != 20);
> +        offsetof(struct
> ieee80211_tx_info, status.ack_signal) != 24);
>     
> memset(&info->status.ampdu_ack_len, 0,
>         
>    sizeof(struct ieee80211_tx_info) -
>         
>    offsetof(struct ieee80211_tx_info,
> status.ampdu_ack_len));
> @@ -760,6 +759,7 @@ enum ieee80211_conf_flags {
>   * @IEEE80211_CONF_CHANGE_RETRY_LIMITS: retry limits
> changed
>   * @IEEE80211_CONF_CHANGE_IDLE: Idle flag changed
>   * @IEEE80211_CONF_CHANGE_SMPS: Spatial multiplexing
> powersave mode changed
> + * @IEEE80211_CONF_CHANGE_ACK_POWER: Global ACK power level
> (in dBm) changed
>   */
>  enum ieee80211_conf_changed {
>     
> IEEE80211_CONF_CHANGE_SMPS   
>     = BIT(1),
> @@ -770,6 +770,7 @@ enum ieee80211_conf_changed {
>     
> IEEE80211_CONF_CHANGE_CHANNEL   
>     = BIT(6),
>     
> IEEE80211_CONF_CHANGE_RETRY_LIMITS    =
> BIT(7),
>     
> IEEE80211_CONF_CHANGE_IDLE   
>     = BIT(8),
> +   
> IEEE80211_CONF_CHANGE_ACK_POWER   
>     = BIT(9),
>  };
>  
>  /**
> @@ -826,6 +827,8 @@ enum ieee80211_smps_mode {
>   * @smps_mode: spatial multiplexing powersave mode;
> note that
>   *    %IEEE80211_SMPS_STATIC is used
> when the device is not
>   *    configured for an HT channel
> + * @ack_power: global power level in dBm to use for all
> + *     mac80211 acknowledgement_packets 
>   */
>  struct ieee80211_conf {
>      u32 flags;
> @@ -840,6 +843,8 @@ struct ieee80211_conf {
>      struct ieee80211_channel *channel;
>      enum nl80211_channel_type channel_type;
>      enum ieee80211_smps_mode smps_mode;
> +
> +    u8 ack_power;
>  };
>  
>  /**
> @@ -1068,6 +1073,15 @@ enum sta_notify_cmd {
>  };
>  
>  /**
> + * struct ieee80211_tx_control - TX control data
> + *
> + * @sta: station table entry
> +  */
> +struct ieee80211_tx_control {
> +    struct ieee80211_sta *sta;
> +};
> +
> +/**
>   * enum ieee80211_hw_flags - hardware flags
>   *
>   * These flags are used to indicate hardware
> capabilities to
> @@ -1227,6 +1241,35 @@ enum ieee80211_hw_flags {
>  };
>  
>  /**
> + * enum ieee80211_tpc_support - type of transmit power
> control support
> + *
> + * These flags are used to indicate transmit power control
> capabilities
> + * to the stack. Generally, flags here should have their
> meaning
> + * done in a way that the simplest hardware doesn't need
> setting
> + * any particular flags. There are some exceptions to this
> rule,
> + * however, so you are advised to review these flags
> carefully.
> + *
> + * @IEEE80211_TPC_NONE: No tpc beside a fixed global
> setting is available.
> + * This setting is used as the default case. Extended tpc
> capabilities
> + * need to be announced via flags within the individual
> hardware driver.
> + *
> + * @IEEE80211_TPC_PER_DATA_PACKET: One power level per data
> packet can
> + * be set. Each data packet is send out with its individual
> power level.
> + *
> + * @IEEE80211_TPC_PER_DATA_MRR: Multiple individual power
> levels per
> + * multi-rate-retry stage within a data packet are
> supported.
> + *
> + * @IEEE80211_TPC_ACK_POWER_GLOBAL: One power level of ack
> packets is
> + * globaly adjustable.
> + */
> +enum ieee80211_tpc_support {
> +    IEEE80211_TPC_NONE   
>            
> =1<<0,
> +   
> IEEE80211_TPC_PER_DATA_PACKET   
>         =1<<1,
> +   
> IEEE80211_TPC_PER_DATA_MRR   
>         =1<<2,
> +   
> IEEE80211_TPC_ACK_POWER_GLOBAL   
>         =1<<3,
> +};
> +
> +/**
>   * struct ieee80211_hw - hardware information and
> state
>   *
>   * This structure contains the configuration and
> hardware
> @@ -1274,6 +1317,7 @@ enum ieee80211_hw_flags {
>   * @max_report_rates: maximum number of alternate rate
> retry stages
>   *    the hw can report back.
>   * @max_rate_tries: maximum number of tries for each
> stage
> + * @tpc_support: indicates the type of transmit power
> control support
>   *
>   * @napi_weight: weight used for NAPI polling. 
> You must specify an
>   *    appropriate value here if a
> napi_poll operation is provided
> @@ -1319,6 +1363,7 @@ struct ieee80211_hw {
>      u8 max_rates;
>      u8 max_report_rates;
>      u8 max_rate_tries;
> +    u8 tpc_support;
>      u8 max_rx_aggregation_subframes;
>      u8 max_tx_aggregation_subframes;
>      u8 offchannel_tx_hw_queue;
> @@ -2258,7 +2303,9 @@ enum ieee80211_rate_control_changed {
>   *    The callback is optional and can
> (should!) sleep.
>   */
>  struct ieee80211_ops {
> -    void (*tx)(struct ieee80211_hw *hw,
> struct sk_buff *skb);
> +    void (*tx)(struct ieee80211_hw *hw,
> +       
>    struct ieee80211_tx_control *control,
> +       
>    struct sk_buff *skb);
>      int (*start)(struct ieee80211_hw *hw);
>      void (*stop)(struct ieee80211_hw *hw);
>  #ifdef CONFIG_PM
> diff --git a/net/mac80211/driver-ops.h
> b/net/mac80211/driver-ops.h
> index df92031..a81117a 100644
> --- a/net/mac80211/driver-ops.h
> +++ b/net/mac80211/driver-ops.h
> @@ -22,9 +22,11 @@ get_bss_sdata(struct
> ieee80211_sub_if_data *sdata)
>      return sdata;
>  }
>  
> -static inline void drv_tx(struct ieee80211_local *local,
> struct sk_buff *skb)
> +static inline void drv_tx(struct ieee80211_local *local,
> +           
>   struct ieee80211_tx_control *control,
> +           
>   struct sk_buff *skb)
>  {
> -    local->ops->tx(&local->hw,
> skb);
> +    local->ops->tx(&local->hw,
> control, skb);
>  }
>  
>  static inline void drv_get_et_strings(struct
> ieee80211_sub_if_data *sdata,
> diff --git a/net/mac80211/ieee80211_i.h
> b/net/mac80211/ieee80211_i.h
> index e0423f8..d3c69db 100644
> --- a/net/mac80211/ieee80211_i.h
> +++ b/net/mac80211/ieee80211_i.h
> @@ -194,6 +194,8 @@ struct ieee80211_tx_data {
>      struct ieee80211_channel *channel;
>  
>      unsigned int flags;
> +
> +    struct ieee80211_tx_control control;
>  };
>  
>  
> diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
> index c9d2175..94c362d 100644
> --- a/net/mac80211/tx.c
> +++ b/net/mac80211/tx.c
> @@ -1194,6 +1194,7 @@ ieee80211_tx_prepare(struct
> ieee80211_sub_if_data *sdata,
>  static bool ieee80211_tx_frags(struct ieee80211_local
> *local,
>             
>        struct ieee80211_vif *vif,
>             
>        struct ieee80211_sta *sta,
> +           
>        struct ieee80211_tx_control
> *control,
>             
>        struct sk_buff_head *skbs,
>             
>        bool txpending)
>  {
> @@ -1233,10 +1234,10 @@ static bool
> ieee80211_tx_frags(struct ieee80211_local *local,
>         
> spin_unlock_irqrestore(&local->queue_stop_reason_lock,
> flags);
>  
>          info->control.vif
> = vif;
> -        info->control.sta
> = sta;
> +        control->sta =
> sta;
>  
>          __skb_unlink(skb,
> skbs);
> -        drv_tx(local, skb);
> +        drv_tx(local,
> control, skb);
>      }
>  
>      return true;
> @@ -1246,6 +1247,7 @@ static bool ieee80211_tx_frags(struct
> ieee80211_local *local,
>   * Returns false if the frame couldn't be transmitted
> but was queued instead.
>   */
>  static bool __ieee80211_tx(struct ieee80211_local *local,
> +           
>    struct ieee80211_tx_control *control,
>             
>    struct sk_buff_head *skbs, int led_len,
>             
>    struct sta_info *sta, bool txpending)
>  {
> @@ -1294,7 +1296,7 @@ static bool __ieee80211_tx(struct
> ieee80211_local *local,
>          break;
>      }
>  
> -    result = ieee80211_tx_frags(local, vif,
> pubsta, skbs,
> +    result = ieee80211_tx_frags(local, vif,
> pubsta, control, skbs,
>             
>         txpending);
>  
>      ieee80211_tpt_led_trig_tx(local, fc,
> led_len);
> @@ -1402,7 +1404,7 @@ static bool ieee80211_tx(struct
> ieee80211_sub_if_data *sdata,
>             
> sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
>  
>      if (!invoke_tx_handlers(&tx))
> -        result =
> __ieee80211_tx(local, &tx.skbs, led_len,
> +        result =
> __ieee80211_tx(local, &tx.control, &tx.skbs,
> led_len,
>             
>         tx.sta, txpending);
>   out:
>      rcu_read_unlock();
> @@ -2144,6 +2146,7 @@ static bool
> ieee80211_tx_pending_skb(struct ieee80211_local *local,
>      struct ieee80211_sub_if_data *sdata;
>      struct sta_info *sta;
>      struct ieee80211_hdr *hdr;
> +    struct ieee80211_tx_control control;
>      bool result;
>  
>      sdata =
> vif_to_sdata(info->control.vif);
> @@ -2159,7 +2162,7 @@ static bool
> ieee80211_tx_pending_skb(struct ieee80211_local *local,
>          hdr = (struct
> ieee80211_hdr *)skb->data;
>          sta =
> sta_info_get(sdata, hdr->addr1);
>  
> -        result =
> __ieee80211_tx(local, &skbs, skb->len, sta, true);
> +        result =
> __ieee80211_tx(local, &control, &skbs, skb->len,
> sta, true);
>      }
>  
>      return result;
> -- 
> 1.7.10.4
> 
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux