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

Commit 49644832 authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "wil6210: notify cqm packet loss on disable_ap_sme"

parents 2b40bce1 efd252e4
Loading
Loading
Loading
Loading
+98 −6
Original line number Diff line number Diff line
@@ -65,6 +65,19 @@ static struct ieee80211_channel wil_60ghz_channels[] = {
	CHAN60G(4, 0),
};

static void
wil_memdup_ie(u8 **pdst, size_t *pdst_len, const u8 *src, size_t src_len)
{
	kfree(*pdst);
	*pdst = NULL;
	*pdst_len = 0;
	if (src_len > 0) {
		*pdst = kmemdup(src, src_len, GFP_KERNEL);
		if (*pdst)
			*pdst_len = src_len;
	}
}

struct wil_regd_2_brd_suffix {
	const char regdomain[3]; /* alpha2 */
	const char *brd_suffix;
@@ -1734,11 +1747,19 @@ static int wil_cfg80211_add_key(struct wiphy *wiphy,

	rc = wmi_add_cipher_key(vif, key_index, mac_addr, params->key_len,
				params->key, key_usage);
	if (!rc && !IS_ERR(cs))
	if (!rc && !IS_ERR(cs)) {
		/* update local storage used for AP recovery */
		if (key_usage == WMI_KEY_USE_TX_GROUP && params->key &&
		    params->key_len <= WMI_MAX_KEY_LEN) {
			vif->gtk_index = key_index;
			memcpy(vif->gtk, params->key, params->key_len);
			vif->gtk_len = params->key_len;
		}
		/* in FT set crypto will take place upon receiving
		 * WMI_RING_EN_EVENTID event
		 */
		wil_set_crypto_rx(key_index, key_usage, cs, params);
	}

	return rc;
}
@@ -1935,6 +1956,14 @@ static int _wil_cfg80211_set_ies(struct wil6210_vif *vif,
	u16 len = 0, proberesp_len = 0;
	u8 *ies = NULL, *proberesp;

	/* update local storage used for AP recovery */
	wil_memdup_ie(&vif->proberesp, &vif->proberesp_len, bcon->probe_resp,
		      bcon->probe_resp_len);
	wil_memdup_ie(&vif->proberesp_ies, &vif->proberesp_ies_len,
		      bcon->proberesp_ies, bcon->proberesp_ies_len);
	wil_memdup_ie(&vif->assocresp_ies, &vif->assocresp_ies_len,
		      bcon->assocresp_ies, bcon->assocresp_ies_len);

	proberesp = _wil_cfg80211_get_proberesp_ies(bcon->probe_resp,
						    bcon->probe_resp_len,
						    &proberesp_len);
@@ -2036,6 +2065,9 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy,
	vif->channel = chan;
	vif->hidden_ssid = hidden_ssid;
	vif->pbss = pbss;
	vif->bi = bi;
	memcpy(vif->ssid, ssid, ssid_len);
	vif->ssid_len = ssid_len;

	netif_carrier_on(ndev);
	if (!wil_has_other_active_ifaces(wil, ndev, false, true))
@@ -2062,11 +2094,64 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy,
	return rc;
}

void wil_cfg80211_ap_recovery(struct wil6210_priv *wil)
{
	int rc, i;
	struct wiphy *wiphy = wil_to_wiphy(wil);

	for (i = 0; i < wil->max_vifs; i++) {
		struct wil6210_vif *vif = wil->vifs[i];
		struct net_device *ndev;
		struct cfg80211_beacon_data bcon = {};
		struct key_params key_params = {};

		if (!vif || vif->ssid_len == 0)
			continue;

		ndev = vif_to_ndev(vif);
		bcon.proberesp_ies = vif->proberesp_ies;
		bcon.assocresp_ies = vif->assocresp_ies;
		bcon.probe_resp = vif->proberesp;
		bcon.proberesp_ies_len = vif->proberesp_ies_len;
		bcon.assocresp_ies_len = vif->assocresp_ies_len;
		bcon.probe_resp_len = vif->proberesp_len;

		wil_info(wil,
			 "AP (vif %d) recovery: privacy %d, bi %d, channel %d, hidden %d, pbss %d\n",
			 i, vif->privacy, vif->bi, vif->channel,
			 vif->hidden_ssid, vif->pbss);
		wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
				  vif->ssid, vif->ssid_len, true);
		rc = _wil_cfg80211_start_ap(wiphy, ndev,
					    vif->ssid, vif->ssid_len,
					    vif->privacy, vif->bi,
					    vif->channel, &bcon,
					    vif->hidden_ssid, vif->pbss);
		if (rc) {
			wil_err(wil, "vif %d recovery failed (%d)\n", i, rc);
			continue;
		}

		if (!vif->privacy || vif->gtk_len == 0)
			continue;

		key_params.key = vif->gtk;
		key_params.key_len = vif->gtk_len;
		key_params.seq_len = IEEE80211_GCMP_PN_LEN;
		rc = wil_cfg80211_add_key(wiphy, ndev, vif->gtk_index, false,
					  NULL, &key_params);
		if (rc)
			wil_err(wil, "vif %d recovery add key failed (%d)\n",
				i, rc);
	}
}

static int wil_cfg80211_change_beacon(struct wiphy *wiphy,
				      struct net_device *ndev,
				      struct cfg80211_beacon_data *bcon)
{
	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
	struct wireless_dev *wdev = ndev->ieee80211_ptr;
	struct wil6210_vif *vif = ndev_to_vif(ndev);
	int rc;
	u32 privacy = 0;
@@ -2079,15 +2164,16 @@ static int wil_cfg80211_change_beacon(struct wiphy *wiphy,
			     bcon->tail_len))
		privacy = 1;

	memcpy(vif->ssid, wdev->ssid, wdev->ssid_len);
	vif->ssid_len = wdev->ssid_len;

	/* in case privacy has changed, need to restart the AP */
	if (vif->privacy != privacy) {
		struct wireless_dev *wdev = ndev->ieee80211_ptr;

		wil_dbg_misc(wil, "privacy changed %d=>%d. Restarting AP\n",
			     vif->privacy, privacy);

		rc = _wil_cfg80211_start_ap(wiphy, ndev, wdev->ssid,
					    wdev->ssid_len, privacy,
		rc = _wil_cfg80211_start_ap(wiphy, ndev, vif->ssid,
					    vif->ssid_len, privacy,
					    wdev->beacon_interval,
					    vif->channel, bcon,
					    vif->hidden_ssid,
@@ -2177,6 +2263,12 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,

	wmi_pcp_stop(vif);
	clear_bit(wil_vif_ft_roam, vif->status);
	vif->ssid_len = 0;
	wil_memdup_ie(&vif->proberesp, &vif->proberesp_len, NULL, 0);
	wil_memdup_ie(&vif->proberesp_ies, &vif->proberesp_ies_len, NULL, 0);
	wil_memdup_ie(&vif->assocresp_ies, &vif->assocresp_ies_len, NULL, 0);
	memset(vif->gtk, 0, WMI_MAX_KEY_LEN);
	vif->gtk_len = 0;

	if (last)
		__wil_down(wil);
@@ -2224,7 +2316,7 @@ static int wil_cfg80211_del_station(struct wiphy *wiphy,
		     params->mac, params->reason_code, vif->mid);

	mutex_lock(&wil->mutex);
	wil6210_disconnect(vif, params->mac, params->reason_code, false);
	wil6210_disconnect(vif, params->mac, params->reason_code);
	mutex_unlock(&wil->mutex);

	return 0;
+148 −50
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#include <linux/moduleparam.h>
#include <linux/if_arp.h>
#include <linux/etherdevice.h>
#include <linux/rtnetlink.h>

#include "wil6210.h"
#include "txrx.h"
@@ -214,8 +215,21 @@ static void wil_ring_fini_tx(struct wil6210_priv *wil, int id)
	wil->txrx_ops.ring_fini_tx(wil, ring);
}

static void wil_disconnect_cid(struct wil6210_vif *vif, int cid,
			       u16 reason_code, bool from_event)
static bool wil_vif_is_connected(struct wil6210_priv *wil, u8 mid)
{
	int i;

	for (i = 0; i < WIL6210_MAX_CID; i++) {
		if (wil->sta[i].mid == mid &&
		    wil->sta[i].status == wil_sta_connected)
			return true;
	}

	return false;
}

static void wil_disconnect_cid_complete(struct wil6210_vif *vif, int cid,
					u16 reason_code)
__acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
{
	uint i;
@@ -226,24 +240,14 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
	int min_ring_id = wil_get_min_tx_ring_id(wil);

	might_sleep();
	wil_dbg_misc(wil, "disconnect_cid: CID %d, MID %d, status %d\n",
	wil_dbg_misc(wil,
		     "disconnect_cid_complete: CID %d, MID %d, status %d\n",
		     cid, sta->mid, sta->status);
	/* inform upper/lower layers */
	/* inform upper layers */
	if (sta->status != wil_sta_unused) {
		if (vif->mid != sta->mid) {
			wil_err(wil, "STA MID mismatch with VIF MID(%d)\n",
				vif->mid);
			/* let FW override sta->mid but be more strict with
			 * user space requests
			 */
			if (!from_event)
				return;
		}
		if (!from_event) {
			bool del_sta = (wdev->iftype == NL80211_IFTYPE_AP) ?
						disable_ap_sme : false;
			wmi_disconnect_sta(vif, sta->addr, reason_code,
					   true, del_sta);
		}

		switch (wdev->iftype) {
@@ -284,36 +288,20 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
	sta->stats.tx_latency_min_us = U32_MAX;
}

static bool wil_vif_is_connected(struct wil6210_priv *wil, u8 mid)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
		if (wil->sta[i].mid == mid &&
		    wil->sta[i].status == wil_sta_connected)
			return true;
	}

	return false;
}

static void _wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
				u16 reason_code, bool from_event)
static void _wil6210_disconnect_complete(struct wil6210_vif *vif,
					 const u8 *bssid, u16 reason_code)
{
	struct wil6210_priv *wil = vif_to_wil(vif);
	int cid = -ENOENT;
	struct net_device *ndev;
	struct wireless_dev *wdev;

	if (unlikely(!vif))
		return;

	ndev = vif_to_ndev(vif);
	wdev = vif_to_wdev(vif);

	might_sleep();
	wil_info(wil, "bssid=%pM, reason=%d, ev%s\n", bssid,
		 reason_code, from_event ? "+" : "-");
	wil_info(wil, "disconnect_complete: bssid=%pM, reason=%d\n",
		 bssid, reason_code);

	/* Cases are:
	 * - disconnect single STA, still connected
@@ -328,14 +316,15 @@ static void _wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
	if (bssid && !is_broadcast_ether_addr(bssid) &&
	    !ether_addr_equal_unaligned(ndev->dev_addr, bssid)) {
		cid = wil_find_cid(wil, vif->mid, bssid);
		wil_dbg_misc(wil, "Disconnect %pM, CID=%d, reason=%d\n",
		wil_dbg_misc(wil,
			     "Disconnect complete %pM, CID=%d, reason=%d\n",
			     bssid, cid, reason_code);
		if (cid >= 0) /* disconnect 1 peer */
			wil_disconnect_cid(vif, cid, reason_code, from_event);
			wil_disconnect_cid_complete(vif, cid, reason_code);
	} else { /* all */
		wil_dbg_misc(wil, "Disconnect all\n");
		wil_dbg_misc(wil, "Disconnect complete all\n");
		for (cid = 0; cid < WIL6210_MAX_CID; cid++)
			wil_disconnect_cid(vif, cid, reason_code, from_event);
			wil_disconnect_cid_complete(vif, cid, reason_code);
	}

	/* link state */
@@ -381,6 +370,84 @@ static void _wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
	}
}

static int wil_disconnect_cid(struct wil6210_vif *vif, int cid,
			      u16 reason_code)
{
	struct wil6210_priv *wil = vif_to_wil(vif);
	struct wireless_dev *wdev = vif_to_wdev(vif);
	struct wil_sta_info *sta = &wil->sta[cid];
	bool del_sta = false;

	might_sleep();
	wil_dbg_misc(wil, "disconnect_cid: CID %d, MID %d, status %d\n",
		     cid, sta->mid, sta->status);

	if (sta->status == wil_sta_unused)
		return 0;

	if (vif->mid != sta->mid) {
		wil_err(wil, "STA MID mismatch with VIF MID(%d)\n", vif->mid);
		return -EINVAL;
	}

	/* inform lower layers */
	if (wdev->iftype == NL80211_IFTYPE_AP && disable_ap_sme)
		del_sta = true;

	/* disconnect by sending command disconnect/del_sta and wait
	 * synchronously for WMI_DISCONNECT_EVENTID event.
	 */
	return wmi_disconnect_sta(vif, sta->addr, reason_code, del_sta);
}

static void _wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
				u16 reason_code)
{
	struct wil6210_priv *wil;
	struct net_device *ndev;
	struct wireless_dev *wdev;
	int cid = -ENOENT;

	if (unlikely(!vif))
		return;

	wil = vif_to_wil(vif);
	ndev = vif_to_ndev(vif);
	wdev = vif_to_wdev(vif);

	might_sleep();
	wil_info(wil, "disconnect bssid=%pM, reason=%d\n", bssid, reason_code);

	/* Cases are:
	 * - disconnect single STA, still connected
	 * - disconnect single STA, already disconnected
	 * - disconnect all
	 *
	 * For "disconnect all", there are 3 options:
	 * - bssid == NULL
	 * - bssid is broadcast address (ff:ff:ff:ff:ff:ff)
	 * - bssid is our MAC address
	 */
	if (bssid && !is_broadcast_ether_addr(bssid) &&
	    !ether_addr_equal_unaligned(ndev->dev_addr, bssid)) {
		cid = wil_find_cid(wil, vif->mid, bssid);
		wil_dbg_misc(wil, "Disconnect %pM, CID=%d, reason=%d\n",
			     bssid, cid, reason_code);
		if (cid >= 0) /* disconnect 1 peer */
			wil_disconnect_cid(vif, cid, reason_code);
	} else { /* all */
		wil_dbg_misc(wil, "Disconnect all\n");
		for (cid = 0; cid < WIL6210_MAX_CID; cid++)
			wil_disconnect_cid(vif, cid, reason_code);
	}

	/* call event handler manually after processing wmi_call,
	 * to avoid deadlock - disconnect event handler acquires
	 * wil->mutex while it is already held here
	 */
	_wil6210_disconnect_complete(vif, bssid, reason_code);
}

void wil_disconnect_worker(struct work_struct *work)
{
	struct wil6210_vif *vif = container_of(work,
@@ -487,10 +554,11 @@ static void wil_fw_error_worker(struct work_struct *work)
	if (wil_wait_for_recovery(wil) != 0)
		return;

	rtnl_lock();
	mutex_lock(&wil->mutex);
	/* Needs adaptation for multiple VIFs
	 * need to go over all VIFs and consider the appropriate
	 * recovery.
	 * recovery because each one can have different iftype.
	 */
	switch (wdev->iftype) {
	case NL80211_IFTYPE_STATION:
@@ -502,15 +570,24 @@ static void wil_fw_error_worker(struct work_struct *work)
		break;
	case NL80211_IFTYPE_AP:
	case NL80211_IFTYPE_P2P_GO:
		wil_info(wil, "No recovery for AP-like interface\n");
		/* recovery in these modes is done by upper layers */
		if (no_fw_recovery) /* upper layers do recovery */
			break;
		/* silent recovery, upper layers will see disconnect */
		__wil_down(wil);
		__wil_up(wil);
		mutex_unlock(&wil->mutex);
		wil_cfg80211_ap_recovery(wil);
		mutex_lock(&wil->mutex);
		wil_info(wil, "... completed\n");
		break;
	default:
		wil_err(wil, "No recovery - unknown interface type %d\n",
			wdev->iftype);
		break;
	}

	mutex_unlock(&wil->mutex);
	rtnl_unlock();
}

static int wil_find_free_ring(struct wil6210_priv *wil)
@@ -696,20 +773,41 @@ void wil6210_bus_request(struct wil6210_priv *wil, u32 kbps)
 * @vif: virtual interface context
 * @bssid: peer to disconnect, NULL to disconnect all
 * @reason_code: Reason code for the Disassociation frame
 * @from_event: whether is invoked from FW event handler
 *
 * Disconnect and release associated resources. If invoked not from the
 * FW event handler, issue WMI command(s) to trigger MAC disconnect.
 * Disconnect and release associated resources. Issue WMI
 * command(s) to trigger MAC disconnect. When command was issued
 * successfully, call the wil6210_disconnect_complete function
 * to handle the event synchronously
 */
void wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
			u16 reason_code, bool from_event)
			u16 reason_code)
{
	struct wil6210_priv *wil = vif_to_wil(vif);

	wil_dbg_misc(wil, "disconnecting\n");

	del_timer_sync(&vif->connect_timer);
	_wil6210_disconnect(vif, bssid, reason_code);
}

/**
 * wil6210_disconnect_complete - handle disconnect event
 * @vif: virtual interface context
 * @bssid: peer to disconnect, NULL to disconnect all
 * @reason_code: Reason code for the Disassociation frame
 *
 * Release associated resources and indicate upper layers the
 * connection is terminated.
 */
void wil6210_disconnect_complete(struct wil6210_vif *vif, const u8 *bssid,
				 u16 reason_code)
{
	struct wil6210_priv *wil = vif_to_wil(vif);

	wil_dbg_misc(wil, "disconnect\n");
	wil_dbg_misc(wil, "got disconnect\n");

	del_timer_sync(&vif->connect_timer);
	_wil6210_disconnect(vif, bssid, reason_code, from_event);
	_wil6210_disconnect_complete(vif, bssid, reason_code);
}

void wil_priv_deinit(struct wil6210_priv *wil)
@@ -1549,7 +1647,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
		if (vif) {
			cancel_work_sync(&vif->disconnect_worker);
			wil6210_disconnect(vif, NULL,
					   WLAN_REASON_DEAUTH_LEAVING, false);
					   WLAN_REASON_DEAUTH_LEAVING);
		}
	}
	wil_bcast_fini_all(wil);
@@ -1733,7 +1831,7 @@ int __wil_up(struct wil6210_priv *wil)
			WIL_RX_RING_SIZE_ORDER_DEFAULT :
			WIL_RX_RING_SIZE_ORDER_TALYN_DEFAULT;

	rc = wil->txrx_ops.rx_init(wil, 1 << rx_ring_order);
	rc = wil->txrx_ops.rx_init(wil, rx_ring_order);
	if (rc)
		return rc;

+2 −3
Original line number Diff line number Diff line
@@ -361,8 +361,7 @@ wil_vif_alloc(struct wil6210_priv *wil, const char *name,
	ndev->ieee80211_ptr = wdev;
	ndev->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM |
			    NETIF_F_SG | NETIF_F_GRO |
			    NETIF_F_TSO | NETIF_F_TSO6 |
			    NETIF_F_RXHASH;
			    NETIF_F_TSO | NETIF_F_TSO6;

	ndev->features |= ndev->hw_features;
	SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
@@ -530,7 +529,7 @@ void wil_vif_remove(struct wil6210_priv *wil, u8 mid)
	}

	mutex_lock(&wil->mutex);
	wil6210_disconnect(vif, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
	wil6210_disconnect(vif, NULL, WLAN_REASON_DEAUTH_LEAVING);
	mutex_unlock(&wil->mutex);

	ndev = vif_to_ndev(vif);
+4 −10
Original line number Diff line number Diff line
@@ -769,14 +769,6 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)

	stats = &wil->sta[cid].stats;

	if (ndev->features & NETIF_F_RXHASH)
		/* fake L4 to ensure it won't be re-calculated later
		 * set hash to any non-zero value to activate rps
		 * mechanism, core will be chosen according
		 * to user-level rps configuration.
		 */
		skb_set_hash(skb, 1, PKT_HASH_TYPE_L4);

	skb_orphan(skb);

	if (security && (wil->txrx_ops.rx_crypto_check(wil, skb) != 0)) {
@@ -906,7 +898,7 @@ static void wil_rx_buf_len_init(struct wil6210_priv *wil)
	}
}

static int wil_rx_init(struct wil6210_priv *wil, u16 size)
static int wil_rx_init(struct wil6210_priv *wil, uint order)
{
	struct wil_ring *vring = &wil->ring_rx;
	int rc;
@@ -920,7 +912,7 @@ static int wil_rx_init(struct wil6210_priv *wil, u16 size)

	wil_rx_buf_len_init(wil);

	vring->size = size;
	vring->size = 1 << order;
	vring->is_rx = true;
	rc = wil_vring_alloc(wil, vring);
	if (rc)
@@ -1429,6 +1421,8 @@ static struct wil_ring *wil_find_tx_bcast_2(struct wil6210_priv *wil,
			wil_dbg_txrx(wil, "BCAST DUP -> ring %d\n", i);
			wil_set_da_for_vring(wil, skb2, i);
			wil_tx_ring(wil, vif, v2, skb2);
			/* successful call to wil_tx_ring takes skb2 ref */
			dev_kfree_skb_any(skb2);
		} else {
			wil_err(wil, "skb_copy failed\n");
		}
+8 −3
Original line number Diff line number Diff line
@@ -607,9 +607,9 @@ static void wil_rx_buf_len_init_edma(struct wil6210_priv *wil)
		WIL_MAX_ETH_MTU : WIL_EDMA_RX_BUF_LEN_DEFAULT;
}

static int wil_rx_init_edma(struct wil6210_priv *wil, u16 desc_ring_size)
static int wil_rx_init_edma(struct wil6210_priv *wil, uint desc_ring_order)
{
	u16 status_ring_size;
	u16 status_ring_size, desc_ring_size = 1 << desc_ring_order;
	struct wil_ring *ring = &wil->ring_rx;
	int rc;
	size_t elem_size = wil->use_compressed_rx_status ?
@@ -624,7 +624,12 @@ static int wil_rx_init_edma(struct wil6210_priv *wil, u16 desc_ring_size)
			"compressed RX status cannot be used with SW reorder\n");
		return -EINVAL;
	}

	if (wil->rx_status_ring_order <= desc_ring_order)
		/* make sure sring is larger than desc ring */
		wil->rx_status_ring_order = desc_ring_order + 1;
	if (wil->rx_buff_id_count <= desc_ring_size)
		/* make sure we will not run out of buff_ids */
		wil->rx_buff_id_count = desc_ring_size + 512;
	if (wil->rx_status_ring_order < WIL_SRING_SIZE_ORDER_MIN ||
	    wil->rx_status_ring_order > WIL_SRING_SIZE_ORDER_MAX)
		wil->rx_status_ring_order = WIL_RX_SRING_SIZE_ORDER_DEFAULT;
Loading