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

Commit d0ff7383 authored by Naveen Gangadharan's avatar Naveen Gangadharan Committed by Kalle Valo
Browse files

ath6kl: Add unicast mgmt frame buffering



PS buffering of unicast Action frames that are sent in a context
of a BSS. In AP mode when the recepient station goes to powersave
and PS_POLL flag is not set, we would buffer the frames. Send out
unicast mgmt bufferred frame when PS_POLL is received.

This fixes a bug in P2P GO behavior when sending a GO Discoverability
Request to a client that is in sleep mode.

kvalo: indentation fixes

Signed-off-by: default avatarThirumalai Pachamuthu <tpachamu@qca.qualcomm.com>
Signed-off-by: default avatarNaveen Gangadharan <ngangadh@qca.qualcomm.com>
Signed-off-by: default avatarAarthi Thiruvengadam <athiruve@qca.qualcomm.com>
Signed-off-by: default avatarKalle Valo <kvalo@qca.qualcomm.com>
parent 3b96d49a
Loading
Loading
Loading
Loading
+83 −15
Original line number Diff line number Diff line
@@ -2573,6 +2573,76 @@ static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif,
	return ret;
}

static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif,
				     u32 id,
				     u32 freq,
				     u32 wait,
				     const u8 *buf,
				     size_t len,
				     bool *more_data,
				     bool no_cck)
{
	struct ieee80211_mgmt *mgmt;
	struct ath6kl_sta *conn;
	bool is_psq_empty = false;
	struct ath6kl_mgmt_buff *mgmt_buf;
	size_t mgmt_buf_size;
	struct ath6kl *ar = vif->ar;

	mgmt = (struct ieee80211_mgmt *) buf;
	if (is_multicast_ether_addr(mgmt->da))
		return false;

	conn = ath6kl_find_sta(vif, mgmt->da);
	if (!conn)
		return false;

	if (conn->sta_flags & STA_PS_SLEEP) {
		if (!(conn->sta_flags & STA_PS_POLLED)) {
			/* Queue the frames if the STA is sleeping */
			mgmt_buf_size = len + sizeof(struct ath6kl_mgmt_buff);
			mgmt_buf = kmalloc(mgmt_buf_size, GFP_KERNEL);
			if (!mgmt_buf)
				return false;

			INIT_LIST_HEAD(&mgmt_buf->list);
			mgmt_buf->id = id;
			mgmt_buf->freq = freq;
			mgmt_buf->wait = wait;
			mgmt_buf->len = len;
			mgmt_buf->no_cck = no_cck;
			memcpy(mgmt_buf->buf, buf, len);
			spin_lock_bh(&conn->psq_lock);
			is_psq_empty = skb_queue_empty(&conn->psq) &&
					(conn->mgmt_psq_len == 0);
			list_add_tail(&mgmt_buf->list, &conn->mgmt_psq);
			conn->mgmt_psq_len++;
			spin_unlock_bh(&conn->psq_lock);

			/*
			 * If this is the first pkt getting queued
			 * for this STA, update the PVB for this
			 * STA.
			 */
			if (is_psq_empty)
				ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx,
						       conn->aid, 1);
			return true;
		}

		/*
		 * This tx is because of a PsPoll.
		 * Determine if MoreData bit has to be set.
		 */
		spin_lock_bh(&conn->psq_lock);
		if (!skb_queue_empty(&conn->psq) || (conn->mgmt_psq_len != 0))
			*more_data = true;
		spin_unlock_bh(&conn->psq_lock);
	}

	return false;
}

static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
			  struct ieee80211_channel *chan, bool offchan,
			  enum nl80211_channel_type channel_type,
@@ -2584,6 +2654,7 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
	struct ath6kl_vif *vif = netdev_priv(dev);
	u32 id;
	const struct ieee80211_mgmt *mgmt;
	bool more_data, queued;

	mgmt = (const struct ieee80211_mgmt *) buf;
	if (buf + len >= mgmt->u.probe_resp.variable &&
@@ -2609,22 +2680,19 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,

	*cookie = id;

	if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
		    ar->fw_capabilities)) {
		/*
		 * If capable of doing P2P mgmt operations using
		 * station interface, send additional information like
		 * supported rates to advertise and xmit rates for
		 * probe requests
		 */
	/* AP mode Power saving processing */
	if (vif->nw_type == AP_NETWORK) {
		queued = ath6kl_mgmt_powersave_ap(vif,
					id, chan->center_freq,
					wait, buf,
					len, &more_data, no_cck);
		if (queued)
			return 0;
	}

	return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
					chan->center_freq, wait,
					buf, len, no_cck);
	} else {
		return ath6kl_wmi_send_action_cmd(ar->wmi, vif->fw_vif_idx, id,
						  chan->center_freq, wait,
						  buf, len);
	}
}

static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
+2 −0
Original line number Diff line number Diff line
@@ -262,6 +262,8 @@ struct ath6kl *ath6kl_core_create(struct device *dev)
		spin_lock_init(&ar->sta_list[ctr].psq_lock);
		skb_queue_head_init(&ar->sta_list[ctr].psq);
		skb_queue_head_init(&ar->sta_list[ctr].apsdq);
		ar->sta_list[ctr].mgmt_psq_len = 0;
		INIT_LIST_HEAD(&ar->sta_list[ctr].mgmt_psq);
		ar->sta_list[ctr].aggr_conn =
			kzalloc(sizeof(struct aggr_info_conn), GFP_KERNEL);
		if (!ar->sta_list[ctr].aggr_conn) {
+12 −0
Original line number Diff line number Diff line
@@ -286,6 +286,16 @@ struct ath6kl_cookie {
	struct ath6kl_cookie *arc_list_next;
};

struct ath6kl_mgmt_buff {
	struct list_head list;
	u32 freq;
	u32 wait;
	u32 id;
	bool no_cck;
	size_t len;
	u8 buf[0];
};

struct ath6kl_sta {
	u16 sta_flags;
	u8 mac[ETH_ALEN];
@@ -296,6 +306,8 @@ struct ath6kl_sta {
	u8 wpa_ie[ATH6KL_MAX_IE];
	struct sk_buff_head psq;
	spinlock_t psq_lock;
	struct list_head mgmt_psq;
	size_t mgmt_psq_len;
	u8 apsd_info;
	struct sk_buff_head apsdq;
	struct aggr_info_conn *aggr_conn;
+34 −7
Original line number Diff line number Diff line
@@ -81,11 +81,21 @@ static void ath6kl_add_new_sta(struct ath6kl_vif *vif, u8 *mac, u16 aid,
static void ath6kl_sta_cleanup(struct ath6kl *ar, u8 i)
{
	struct ath6kl_sta *sta = &ar->sta_list[i];
	struct ath6kl_mgmt_buff *entry, *tmp;

	/* empty the queued pkts in the PS queue if any */
	spin_lock_bh(&sta->psq_lock);
	skb_queue_purge(&sta->psq);
	skb_queue_purge(&sta->apsdq);

	if (sta->mgmt_psq_len != 0) {
		list_for_each_entry_safe(entry, tmp, &sta->mgmt_psq, list) {
			kfree(entry);
		}
		INIT_LIST_HEAD(&sta->mgmt_psq);
		sta->mgmt_psq_len = 0;
	}

	spin_unlock_bh(&sta->psq_lock);

	memset(&ar->ap_stats.sta[sta->aid - 1], 0,
@@ -811,6 +821,7 @@ void ath6kl_pspoll_event(struct ath6kl_vif *vif, u8 aid)
	struct sk_buff *skb;
	bool psq_empty = false;
	struct ath6kl *ar = vif->ar;
	struct ath6kl_mgmt_buff *mgmt_buf;

	conn = ath6kl_find_sta_by_aid(ar, aid);

@@ -821,7 +832,7 @@ void ath6kl_pspoll_event(struct ath6kl_vif *vif, u8 aid)
	 * becomes empty update the PVB for this station.
	 */
	spin_lock_bh(&conn->psq_lock);
	psq_empty  = skb_queue_empty(&conn->psq);
	psq_empty  = skb_queue_empty(&conn->psq) && (conn->mgmt_psq_len == 0);
	spin_unlock_bh(&conn->psq_lock);

	if (psq_empty)
@@ -829,15 +840,31 @@ void ath6kl_pspoll_event(struct ath6kl_vif *vif, u8 aid)
		return;

	spin_lock_bh(&conn->psq_lock);
	if (conn->mgmt_psq_len > 0) {
		mgmt_buf = list_first_entry(&conn->mgmt_psq,
					struct ath6kl_mgmt_buff, list);
		list_del(&mgmt_buf->list);
		conn->mgmt_psq_len--;
		spin_unlock_bh(&conn->psq_lock);

		conn->sta_flags |= STA_PS_POLLED;
		ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx,
					 mgmt_buf->id, mgmt_buf->freq,
					 mgmt_buf->wait, mgmt_buf->buf,
					 mgmt_buf->len, mgmt_buf->no_cck);
		conn->sta_flags &= ~STA_PS_POLLED;
		kfree(mgmt_buf);
	} else {
		skb = skb_dequeue(&conn->psq);
		spin_unlock_bh(&conn->psq_lock);

		conn->sta_flags |= STA_PS_POLLED;
		ath6kl_data_tx(skb, vif->ndev);
		conn->sta_flags &= ~STA_PS_POLLED;
	}

	spin_lock_bh(&conn->psq_lock);
	psq_empty  = skb_queue_empty(&conn->psq);
	psq_empty  = skb_queue_empty(&conn->psq) && (conn->mgmt_psq_len == 0);
	spin_unlock_bh(&conn->psq_lock);

	if (psq_empty)
+25 −0
Original line number Diff line number Diff line
@@ -1417,8 +1417,33 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
			if (!(conn->sta_flags & STA_PS_SLEEP)) {
				struct sk_buff *skbuff = NULL;
				bool is_apsdq_empty;
				struct ath6kl_mgmt_buff *mgmt;
				u8 idx;

				spin_lock_bh(&conn->psq_lock);
				while (conn->mgmt_psq_len > 0) {
					mgmt = list_first_entry(
							&conn->mgmt_psq,
							struct ath6kl_mgmt_buff,
							list);
					list_del(&mgmt->list);
					conn->mgmt_psq_len--;
					spin_unlock_bh(&conn->psq_lock);
					idx = vif->fw_vif_idx;

					ath6kl_wmi_send_mgmt_cmd(ar->wmi,
								 idx,
								 mgmt->id,
								 mgmt->freq,
								 mgmt->wait,
								 mgmt->buf,
								 mgmt->len,
								 mgmt->no_cck);

					kfree(mgmt);
					spin_lock_bh(&conn->psq_lock);
				}
				conn->mgmt_psq_len = 0;
				while ((skbuff = skb_dequeue(&conn->psq))) {
					spin_unlock_bh(&conn->psq_lock);
					ath6kl_data_tx(skbuff, vif->ndev);
Loading