Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 08cf42e8 authored by Michal Kazior's avatar Michal Kazior Committed by Johannes Berg
Browse files

mac80211: add support for Rx reordering offloading



Some drivers may be performing most of Tx/Rx
aggregation on their own (e.g. in firmware)
including AddBa/DelBa negotiations but may
otherwise require Rx reordering assistance.

The patch exports 2 new functions for establishing
Rx aggregation sessions in assumption device
driver has taken care of the necessary
negotiations.

Signed-off-by: default avatarMichal Kazior <michal.kazior@tieto.com>
[fix endian bug]
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 83eb935e
Loading
Loading
Loading
Loading
+34 −0
Original line number Diff line number Diff line
@@ -4552,6 +4552,40 @@ void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap,
 */
void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn);

/**
 * ieee80211_start_rx_ba_session_offl - start a Rx BA session
 *
 * Some device drivers may offload part of the Rx aggregation flow including
 * AddBa/DelBa negotiation but may otherwise be incapable of full Rx
 * reordering.
 *
 * Create structures responsible for reordering so device drivers may call here
 * when they complete AddBa negotiation.
 *
 * @vif: &struct ieee80211_vif pointer from the add_interface callback
 * @addr: station mac address
 * @tid: the rx tid
 */
void ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif,
					const u8 *addr, u16 tid);

/**
 * ieee80211_stop_rx_ba_session_offl - stop a Rx BA session
 *
 * Some device drivers may offload part of the Rx aggregation flow including
 * AddBa/DelBa negotiation but may otherwise be incapable of full Rx
 * reordering.
 *
 * Destroy structures responsible for reordering so device drivers may call here
 * when they complete DelBa negotiation.
 *
 * @vif: &struct ieee80211_vif pointer from the add_interface callback
 * @addr: station mac address
 * @tid: the rx tid
 */
void ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif,
				       const u8 *addr, u16 tid);

/* Rate control API */

/**
+78 −23
Original line number Diff line number Diff line
@@ -224,28 +224,15 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d
	ieee80211_tx_skb(sdata, skb);
}

void ieee80211_process_addba_request(struct ieee80211_local *local,
				     struct sta_info *sta,
				     struct ieee80211_mgmt *mgmt,
				     size_t len)
void __ieee80211_start_rx_ba_session(struct sta_info *sta,
				     u8 dialog_token, u16 timeout,
				     u16 start_seq_num, u16 ba_policy, u16 tid,
				     u16 buf_size, bool tx)
{
	struct ieee80211_local *local = sta->sdata->local;
	struct tid_ampdu_rx *tid_agg_rx;
	u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num, status;
	u8 dialog_token;
	int i, ret = -EOPNOTSUPP;

	/* extract session parameters from addba request frame */
	dialog_token = mgmt->u.action.u.addba_req.dialog_token;
	timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout);
	start_seq_num =
		le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4;

	capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
	ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1;
	tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
	buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;

	status = WLAN_STATUS_REQUEST_DECLINED;
	u16 status = WLAN_STATUS_REQUEST_DECLINED;

	if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
		ht_dbg(sta->sdata,
@@ -264,7 +251,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
		status = WLAN_STATUS_INVALID_QOS_PARAM;
		ht_dbg_ratelimited(sta->sdata,
				   "AddBA Req with bad params from %pM on tid %u. policy %d, buffer size %d\n",
				   mgmt->sa, tid, ba_policy, buf_size);
				   sta->sta.addr, tid, ba_policy, buf_size);
		goto end_no_lock;
	}
	/* determine default buffer size */
@@ -281,7 +268,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
	if (sta->ampdu_mlme.tid_rx[tid]) {
		ht_dbg_ratelimited(sta->sdata,
				   "unexpected AddBA Req from %pM on tid %u\n",
				   mgmt->sa, tid);
				   sta->sta.addr, tid);

		/* delete existing Rx BA session on the same tid */
		___ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT,
@@ -353,6 +340,74 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
	mutex_unlock(&sta->ampdu_mlme.mtx);

end_no_lock:
	if (tx)
		ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid,
				  dialog_token, status, 1, buf_size, timeout);
					  dialog_token, status, 1, buf_size,
					  timeout);
}

void ieee80211_process_addba_request(struct ieee80211_local *local,
				     struct sta_info *sta,
				     struct ieee80211_mgmt *mgmt,
				     size_t len)
{
	u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num;
	u8 dialog_token;

	/* extract session parameters from addba request frame */
	dialog_token = mgmt->u.action.u.addba_req.dialog_token;
	timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout);
	start_seq_num =
		le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4;

	capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
	ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1;
	tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
	buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;

	__ieee80211_start_rx_ba_session(sta, dialog_token, timeout,
					start_seq_num, ba_policy, tid,
					buf_size, true);
}

void ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif,
					const u8 *addr, u16 tid)
{
	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
	struct ieee80211_local *local = sdata->local;
	struct ieee80211_rx_agg *rx_agg;
	struct sk_buff *skb = dev_alloc_skb(0);

	if (unlikely(!skb))
		return;

	rx_agg = (struct ieee80211_rx_agg *) &skb->cb;
	memcpy(&rx_agg->addr, addr, ETH_ALEN);
	rx_agg->tid = tid;

	skb->pkt_type = IEEE80211_SDATA_QUEUE_RX_AGG_START;
	skb_queue_tail(&sdata->skb_queue, skb);
	ieee80211_queue_work(&local->hw, &sdata->work);
}
EXPORT_SYMBOL(ieee80211_start_rx_ba_session_offl);

void ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif,
				       const u8 *addr, u16 tid)
{
	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
	struct ieee80211_local *local = sdata->local;
	struct ieee80211_rx_agg *rx_agg;
	struct sk_buff *skb = dev_alloc_skb(0);

	if (unlikely(!skb))
		return;

	rx_agg = (struct ieee80211_rx_agg *) &skb->cb;
	memcpy(&rx_agg->addr, addr, ETH_ALEN);
	rx_agg->tid = tid;

	skb->pkt_type = IEEE80211_SDATA_QUEUE_RX_AGG_STOP;
	skb_queue_tail(&sdata->skb_queue, skb);
	ieee80211_queue_work(&local->hw, &sdata->work);
}
EXPORT_SYMBOL(ieee80211_stop_rx_ba_session_offl);
+11 −0
Original line number Diff line number Diff line
@@ -925,10 +925,17 @@ ieee80211_vif_get_shift(struct ieee80211_vif *vif)
	return shift;
}

struct ieee80211_rx_agg {
	u8 addr[ETH_ALEN];
	u16 tid;
};

enum sdata_queue_type {
	IEEE80211_SDATA_QUEUE_TYPE_FRAME	= 0,
	IEEE80211_SDATA_QUEUE_AGG_START		= 1,
	IEEE80211_SDATA_QUEUE_AGG_STOP		= 2,
	IEEE80211_SDATA_QUEUE_RX_AGG_START	= 3,
	IEEE80211_SDATA_QUEUE_RX_AGG_STOP	= 4,
};

enum {
@@ -1577,6 +1584,10 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
				     u16 initiator, u16 reason, bool stop);
void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
				    u16 initiator, u16 reason, bool stop);
void __ieee80211_start_rx_ba_session(struct sta_info *sta,
				     u8 dialog_token, u16 timeout,
				     u16 start_seq_num, u16 ba_policy, u16 tid,
				     u16 buf_size, bool tx);
void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
					 enum ieee80211_agg_stop_reason reason);
void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
+29 −0
Original line number Diff line number Diff line
@@ -1140,6 +1140,7 @@ static void ieee80211_iface_work(struct work_struct *work)
	struct sk_buff *skb;
	struct sta_info *sta;
	struct ieee80211_ra_tid *ra_tid;
	struct ieee80211_rx_agg *rx_agg;

	if (!ieee80211_sdata_running(sdata))
		return;
@@ -1167,6 +1168,34 @@ static void ieee80211_iface_work(struct work_struct *work)
			ra_tid = (void *)&skb->cb;
			ieee80211_stop_tx_ba_cb(&sdata->vif, ra_tid->ra,
						ra_tid->tid);
		} else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_RX_AGG_START) {
			rx_agg = (void *)&skb->cb;
			mutex_lock(&local->sta_mtx);
			sta = sta_info_get_bss(sdata, rx_agg->addr);
			if (sta) {
				u16 last_seq;

				last_seq = le16_to_cpu(
					sta->last_seq_ctrl[rx_agg->tid]);

				__ieee80211_start_rx_ba_session(sta,
						0, 0,
						ieee80211_sn_inc(last_seq),
						1, rx_agg->tid,
						IEEE80211_MAX_AMPDU_BUF,
						false);
			}
			mutex_unlock(&local->sta_mtx);
		} else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_RX_AGG_STOP) {
			rx_agg = (void *)&skb->cb;
			mutex_lock(&local->sta_mtx);
			sta = sta_info_get_bss(sdata, rx_agg->addr);
			if (sta)
				__ieee80211_stop_rx_ba_session(sta,
							rx_agg->tid,
							WLAN_BACK_RECIPIENT, 0,
							false);
			mutex_unlock(&local->sta_mtx);
		} else if (ieee80211_is_action(mgmt->frame_control) &&
			   mgmt->u.action.category == WLAN_CATEGORY_BACK) {
			int len = skb->len;