Commit e26dc173 authored by Bob Copeland's avatar Bob Copeland Committed by Kalle Valo

wcn36xx: initiate TX BA sessions

Currently, wcn36xx only asks for a TX BA session if it has
already established one for RX.  Thus, two wcn36xx devices cannot
do a-mpdu between themselves since they both wait for the other
to go first.  Fix this by starting a BA session after a few QoS
data frames have been sent to a STA.
Signed-off-by: default avatarBob Copeland <me@bobcopeland.com>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent 5f3f9285
...@@ -797,6 +797,7 @@ static int wcn36xx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, ...@@ -797,6 +797,7 @@ static int wcn36xx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta add vif %p sta %pM\n", wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta add vif %p sta %pM\n",
vif, sta->addr); vif, sta->addr);
spin_lock_init(&sta_priv->ampdu_lock);
vif_priv->sta = sta_priv; vif_priv->sta = sta_priv;
sta_priv->vif = vif_priv; sta_priv->vif = vif_priv;
/* /*
...@@ -875,21 +876,32 @@ static int wcn36xx_ampdu_action(struct ieee80211_hw *hw, ...@@ -875,21 +876,32 @@ static int wcn36xx_ampdu_action(struct ieee80211_hw *hw,
get_sta_index(vif, sta_priv)); get_sta_index(vif, sta_priv));
wcn36xx_smd_add_ba(wcn); wcn36xx_smd_add_ba(wcn);
wcn36xx_smd_trigger_ba(wcn, get_sta_index(vif, sta_priv)); wcn36xx_smd_trigger_ba(wcn, get_sta_index(vif, sta_priv));
ieee80211_start_tx_ba_session(sta, tid, 0);
break; break;
case IEEE80211_AMPDU_RX_STOP: case IEEE80211_AMPDU_RX_STOP:
wcn36xx_smd_del_ba(wcn, tid, get_sta_index(vif, sta_priv)); wcn36xx_smd_del_ba(wcn, tid, get_sta_index(vif, sta_priv));
break; break;
case IEEE80211_AMPDU_TX_START: case IEEE80211_AMPDU_TX_START:
spin_lock_bh(&sta_priv->ampdu_lock);
sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_START;
spin_unlock_bh(&sta_priv->ampdu_lock);
ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break; break;
case IEEE80211_AMPDU_TX_OPERATIONAL: case IEEE80211_AMPDU_TX_OPERATIONAL:
spin_lock_bh(&sta_priv->ampdu_lock);
sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_OPERATIONAL;
spin_unlock_bh(&sta_priv->ampdu_lock);
wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 1, wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 1,
get_sta_index(vif, sta_priv)); get_sta_index(vif, sta_priv));
break; break;
case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH:
case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
case IEEE80211_AMPDU_TX_STOP_CONT: case IEEE80211_AMPDU_TX_STOP_CONT:
spin_lock_bh(&sta_priv->ampdu_lock);
sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_NONE;
spin_unlock_bh(&sta_priv->ampdu_lock);
ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break; break;
default: default:
......
...@@ -111,6 +111,42 @@ static inline struct wcn36xx_vif *get_vif_by_addr(struct wcn36xx *wcn, ...@@ -111,6 +111,42 @@ static inline struct wcn36xx_vif *get_vif_by_addr(struct wcn36xx *wcn,
wcn36xx_warn("vif %pM not found\n", addr); wcn36xx_warn("vif %pM not found\n", addr);
return NULL; return NULL;
} }
static void wcn36xx_tx_start_ampdu(struct wcn36xx *wcn,
struct wcn36xx_sta *sta_priv,
struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_sta *sta;
u8 *qc, tid;
if (!conf_is_ht(&wcn->hw->conf))
return;
sta = wcn36xx_priv_to_sta(sta_priv);
if (WARN_ON(!ieee80211_is_data_qos(hdr->frame_control)))
return;
if (skb_get_queue_mapping(skb) == IEEE80211_AC_VO)
return;
qc = ieee80211_get_qos_ctl(hdr);
tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
spin_lock(&sta_priv->ampdu_lock);
if (sta_priv->ampdu_state[tid] != WCN36XX_AMPDU_NONE)
goto out_unlock;
if (sta_priv->non_agg_frame_ct++ >= WCN36XX_AMPDU_START_THRESH) {
sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_START;
sta_priv->non_agg_frame_ct = 0;
ieee80211_start_tx_ba_session(sta, tid, 0);
}
out_unlock:
spin_unlock(&sta_priv->ampdu_lock);
}
static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd, static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd,
struct wcn36xx *wcn, struct wcn36xx *wcn,
struct wcn36xx_vif **vif_priv, struct wcn36xx_vif **vif_priv,
...@@ -121,6 +157,8 @@ static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd, ...@@ -121,6 +157,8 @@ static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd,
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_vif *vif = NULL; struct ieee80211_vif *vif = NULL;
struct wcn36xx_vif *__vif_priv = NULL; struct wcn36xx_vif *__vif_priv = NULL;
bool is_data_qos;
bd->bd_rate = WCN36XX_BD_RATE_DATA; bd->bd_rate = WCN36XX_BD_RATE_DATA;
/* /*
...@@ -160,11 +198,16 @@ static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd, ...@@ -160,11 +198,16 @@ static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd,
} }
*vif_priv = __vif_priv; *vif_priv = __vif_priv;
is_data_qos = ieee80211_is_data_qos(hdr->frame_control);
wcn36xx_set_tx_pdu(bd, wcn36xx_set_tx_pdu(bd,
ieee80211_is_data_qos(hdr->frame_control) ? is_data_qos ?
sizeof(struct ieee80211_qos_hdr) : sizeof(struct ieee80211_qos_hdr) :
sizeof(struct ieee80211_hdr_3addr), sizeof(struct ieee80211_hdr_3addr),
skb->len, sta_priv ? sta_priv->tid : 0); skb->len, sta_priv ? sta_priv->tid : 0);
if (sta_priv && is_data_qos)
wcn36xx_tx_start_ampdu(wcn, sta_priv, skb);
} }
static void wcn36xx_set_tx_mgmt(struct wcn36xx_tx_bd *bd, static void wcn36xx_set_tx_mgmt(struct wcn36xx_tx_bd *bd,
......
...@@ -32,6 +32,9 @@ ...@@ -32,6 +32,9 @@
#define WLAN_NV_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin" #define WLAN_NV_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin"
#define WCN36XX_AGGR_BUFFER_SIZE 64 #define WCN36XX_AGGR_BUFFER_SIZE 64
/* How many frames until we start a-mpdu TX session */
#define WCN36XX_AMPDU_START_THRESH 20
extern unsigned int wcn36xx_dbg_mask; extern unsigned int wcn36xx_dbg_mask;
enum wcn36xx_debug_mask { enum wcn36xx_debug_mask {
...@@ -74,6 +77,13 @@ enum wcn36xx_debug_mask { ...@@ -74,6 +77,13 @@ enum wcn36xx_debug_mask {
buf, len, false); \ buf, len, false); \
} while (0) } while (0)
enum wcn36xx_ampdu_state {
WCN36XX_AMPDU_NONE,
WCN36XX_AMPDU_INIT,
WCN36XX_AMPDU_START,
WCN36XX_AMPDU_OPERATIONAL,
};
#define WCN36XX_HW_CHANNEL(__wcn) (__wcn->hw->conf.chandef.chan->hw_value) #define WCN36XX_HW_CHANNEL(__wcn) (__wcn->hw->conf.chandef.chan->hw_value)
#define WCN36XX_BAND(__wcn) (__wcn->hw->conf.chandef.chan->band) #define WCN36XX_BAND(__wcn) (__wcn->hw->conf.chandef.chan->band)
#define WCN36XX_CENTER_FREQ(__wcn) (__wcn->hw->conf.chandef.chan->center_freq) #define WCN36XX_CENTER_FREQ(__wcn) (__wcn->hw->conf.chandef.chan->center_freq)
...@@ -165,6 +175,10 @@ struct wcn36xx_sta { ...@@ -165,6 +175,10 @@ struct wcn36xx_sta {
bool is_data_encrypted; bool is_data_encrypted;
/* Rates */ /* Rates */
struct wcn36xx_hal_supported_rates supported_rates; struct wcn36xx_hal_supported_rates supported_rates;
spinlock_t ampdu_lock; /* protects next two fields */
enum wcn36xx_ampdu_state ampdu_state[16];
int non_agg_frame_ct;
}; };
struct wcn36xx_dxe_ch; struct wcn36xx_dxe_ch;
struct wcn36xx { struct wcn36xx {
...@@ -243,4 +257,10 @@ static inline bool wcn36xx_is_fw_version(struct wcn36xx *wcn, ...@@ -243,4 +257,10 @@ static inline bool wcn36xx_is_fw_version(struct wcn36xx *wcn,
} }
void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates); void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates);
static inline
struct ieee80211_sta *wcn36xx_priv_to_sta(struct wcn36xx_sta *sta_priv)
{
return container_of((void *)sta_priv, struct ieee80211_sta, drv_priv);
}
#endif /* _WCN36XX_H_ */ #endif /* _WCN36XX_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment