162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
462306a36Sopenharmony_ci * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/etherdevice.h>
862306a36Sopenharmony_ci#include <linux/rtnetlink.h>
962306a36Sopenharmony_ci#include "wil6210.h"
1062306a36Sopenharmony_ci#include "txrx.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cibool wil_has_other_active_ifaces(struct wil6210_priv *wil,
1362306a36Sopenharmony_ci				 struct net_device *ndev, bool up, bool ok)
1462306a36Sopenharmony_ci{
1562306a36Sopenharmony_ci	int i;
1662306a36Sopenharmony_ci	struct wil6210_vif *vif;
1762306a36Sopenharmony_ci	struct net_device *ndev_i;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci	for (i = 0; i < GET_MAX_VIFS(wil); i++) {
2062306a36Sopenharmony_ci		vif = wil->vifs[i];
2162306a36Sopenharmony_ci		if (vif) {
2262306a36Sopenharmony_ci			ndev_i = vif_to_ndev(vif);
2362306a36Sopenharmony_ci			if (ndev_i != ndev)
2462306a36Sopenharmony_ci				if ((up && (ndev_i->flags & IFF_UP)) ||
2562306a36Sopenharmony_ci				    (ok && netif_carrier_ok(ndev_i)))
2662306a36Sopenharmony_ci					return true;
2762306a36Sopenharmony_ci		}
2862306a36Sopenharmony_ci	}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	return false;
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cibool wil_has_active_ifaces(struct wil6210_priv *wil, bool up, bool ok)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	/* use NULL ndev argument to check all interfaces */
3662306a36Sopenharmony_ci	return wil_has_other_active_ifaces(wil, NULL, up, ok);
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic int wil_open(struct net_device *ndev)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct wil6210_priv *wil = ndev_to_wil(ndev);
4262306a36Sopenharmony_ci	int rc = 0;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	wil_dbg_misc(wil, "open\n");
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	if (debug_fw ||
4762306a36Sopenharmony_ci	    test_bit(WMI_FW_CAPABILITY_WMI_ONLY, wil->fw_capabilities)) {
4862306a36Sopenharmony_ci		wil_err(wil, "while in debug_fw or wmi_only mode\n");
4962306a36Sopenharmony_ci		return -EINVAL;
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (!wil_has_other_active_ifaces(wil, ndev, true, false)) {
5362306a36Sopenharmony_ci		wil_dbg_misc(wil, "open, first iface\n");
5462306a36Sopenharmony_ci		rc = wil_pm_runtime_get(wil);
5562306a36Sopenharmony_ci		if (rc < 0)
5662306a36Sopenharmony_ci			return rc;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci		rc = wil_up(wil);
5962306a36Sopenharmony_ci		if (rc)
6062306a36Sopenharmony_ci			wil_pm_runtime_put(wil);
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	return rc;
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic int wil_stop(struct net_device *ndev)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	struct wil6210_priv *wil = ndev_to_wil(ndev);
6962306a36Sopenharmony_ci	int rc = 0;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	wil_dbg_misc(wil, "stop\n");
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (!wil_has_other_active_ifaces(wil, ndev, true, false)) {
7462306a36Sopenharmony_ci		wil_dbg_misc(wil, "stop, last iface\n");
7562306a36Sopenharmony_ci		rc = wil_down(wil);
7662306a36Sopenharmony_ci		if (!rc)
7762306a36Sopenharmony_ci			wil_pm_runtime_put(wil);
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return rc;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic const struct net_device_ops wil_netdev_ops = {
8462306a36Sopenharmony_ci	.ndo_open		= wil_open,
8562306a36Sopenharmony_ci	.ndo_stop		= wil_stop,
8662306a36Sopenharmony_ci	.ndo_start_xmit		= wil_start_xmit,
8762306a36Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
8862306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
8962306a36Sopenharmony_ci};
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic int wil6210_netdev_poll_rx(struct napi_struct *napi, int budget)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	struct wil6210_priv *wil = container_of(napi, struct wil6210_priv,
9462306a36Sopenharmony_ci						napi_rx);
9562306a36Sopenharmony_ci	int quota = budget;
9662306a36Sopenharmony_ci	int done;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	wil_rx_handle(wil, &quota);
9962306a36Sopenharmony_ci	done = budget - quota;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (done < budget) {
10262306a36Sopenharmony_ci		napi_complete_done(napi, done);
10362306a36Sopenharmony_ci		wil6210_unmask_irq_rx(wil);
10462306a36Sopenharmony_ci		wil_dbg_txrx(wil, "NAPI RX complete\n");
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	wil_dbg_txrx(wil, "NAPI RX poll(%d) done %d\n", budget, done);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	return done;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic int wil6210_netdev_poll_rx_edma(struct napi_struct *napi, int budget)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	struct wil6210_priv *wil = container_of(napi, struct wil6210_priv,
11562306a36Sopenharmony_ci						napi_rx);
11662306a36Sopenharmony_ci	int quota = budget;
11762306a36Sopenharmony_ci	int done;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	wil_rx_handle_edma(wil, &quota);
12062306a36Sopenharmony_ci	done = budget - quota;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	if (done < budget) {
12362306a36Sopenharmony_ci		napi_complete_done(napi, done);
12462306a36Sopenharmony_ci		wil6210_unmask_irq_rx_edma(wil);
12562306a36Sopenharmony_ci		wil_dbg_txrx(wil, "NAPI RX complete\n");
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	wil_dbg_txrx(wil, "NAPI RX poll(%d) done %d\n", budget, done);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	return done;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic int wil6210_netdev_poll_tx(struct napi_struct *napi, int budget)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	struct wil6210_priv *wil = container_of(napi, struct wil6210_priv,
13662306a36Sopenharmony_ci						napi_tx);
13762306a36Sopenharmony_ci	int tx_done = 0;
13862306a36Sopenharmony_ci	uint i;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/* always process ALL Tx complete, regardless budget - it is fast */
14162306a36Sopenharmony_ci	for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
14262306a36Sopenharmony_ci		struct wil_ring *ring = &wil->ring_tx[i];
14362306a36Sopenharmony_ci		struct wil_ring_tx_data *txdata = &wil->ring_tx_data[i];
14462306a36Sopenharmony_ci		struct wil6210_vif *vif;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci		if (!ring->va || !txdata->enabled ||
14762306a36Sopenharmony_ci		    txdata->mid >= GET_MAX_VIFS(wil))
14862306a36Sopenharmony_ci			continue;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci		vif = wil->vifs[txdata->mid];
15162306a36Sopenharmony_ci		if (unlikely(!vif)) {
15262306a36Sopenharmony_ci			wil_dbg_txrx(wil, "Invalid MID %d\n", txdata->mid);
15362306a36Sopenharmony_ci			continue;
15462306a36Sopenharmony_ci		}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci		tx_done += wil_tx_complete(vif, i);
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (tx_done < budget) {
16062306a36Sopenharmony_ci		napi_complete(napi);
16162306a36Sopenharmony_ci		wil6210_unmask_irq_tx(wil);
16262306a36Sopenharmony_ci		wil_dbg_txrx(wil, "NAPI TX complete\n");
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	wil_dbg_txrx(wil, "NAPI TX poll(%d) done %d\n", budget, tx_done);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	return min(tx_done, budget);
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic int wil6210_netdev_poll_tx_edma(struct napi_struct *napi, int budget)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	struct wil6210_priv *wil = container_of(napi, struct wil6210_priv,
17362306a36Sopenharmony_ci						napi_tx);
17462306a36Sopenharmony_ci	int tx_done;
17562306a36Sopenharmony_ci	/* There is only one status TX ring */
17662306a36Sopenharmony_ci	struct wil_status_ring *sring = &wil->srings[wil->tx_sring_idx];
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (!sring->va)
17962306a36Sopenharmony_ci		return 0;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	tx_done = wil_tx_sring_handler(wil, sring);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	if (tx_done < budget) {
18462306a36Sopenharmony_ci		napi_complete(napi);
18562306a36Sopenharmony_ci		wil6210_unmask_irq_tx_edma(wil);
18662306a36Sopenharmony_ci		wil_dbg_txrx(wil, "NAPI TX complete\n");
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	wil_dbg_txrx(wil, "NAPI TX poll(%d) done %d\n", budget, tx_done);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	return min(tx_done, budget);
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic void wil_dev_setup(struct net_device *dev)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	ether_setup(dev);
19762306a36Sopenharmony_ci	dev->max_mtu = mtu_max;
19862306a36Sopenharmony_ci	dev->tx_queue_len = WIL_TX_Q_LEN_DEFAULT;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic void wil_vif_deinit(struct wil6210_vif *vif)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	del_timer_sync(&vif->scan_timer);
20462306a36Sopenharmony_ci	del_timer_sync(&vif->p2p.discovery_timer);
20562306a36Sopenharmony_ci	cancel_work_sync(&vif->disconnect_worker);
20662306a36Sopenharmony_ci	cancel_work_sync(&vif->p2p.discovery_expired_work);
20762306a36Sopenharmony_ci	cancel_work_sync(&vif->p2p.delayed_listen_work);
20862306a36Sopenharmony_ci	wil_probe_client_flush(vif);
20962306a36Sopenharmony_ci	cancel_work_sync(&vif->probe_client_worker);
21062306a36Sopenharmony_ci	cancel_work_sync(&vif->enable_tx_key_worker);
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_civoid wil_vif_free(struct wil6210_vif *vif)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	struct net_device *ndev = vif_to_ndev(vif);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	wil_vif_deinit(vif);
21862306a36Sopenharmony_ci	free_netdev(ndev);
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic void wil_ndev_destructor(struct net_device *ndev)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(ndev);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	wil_vif_deinit(vif);
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic void wil_connect_timer_fn(struct timer_list *t)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct wil6210_vif *vif = from_timer(vif, t, connect_timer);
23162306a36Sopenharmony_ci	struct wil6210_priv *wil = vif_to_wil(vif);
23262306a36Sopenharmony_ci	bool q;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	wil_err(wil, "Connect timeout detected, disconnect station\n");
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	/* reschedule to thread context - disconnect won't
23762306a36Sopenharmony_ci	 * run from atomic context.
23862306a36Sopenharmony_ci	 * queue on wmi_wq to prevent race with connect event.
23962306a36Sopenharmony_ci	 */
24062306a36Sopenharmony_ci	q = queue_work(wil->wmi_wq, &vif->disconnect_worker);
24162306a36Sopenharmony_ci	wil_dbg_wmi(wil, "queue_work of disconnect_worker -> %d\n", q);
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic void wil_scan_timer_fn(struct timer_list *t)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	struct wil6210_vif *vif = from_timer(vif, t, scan_timer);
24762306a36Sopenharmony_ci	struct wil6210_priv *wil = vif_to_wil(vif);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	clear_bit(wil_status_fwready, wil->status);
25062306a36Sopenharmony_ci	wil_err(wil, "Scan timeout detected, start fw error recovery\n");
25162306a36Sopenharmony_ci	wil_fw_error_recovery(wil);
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic void wil_p2p_discovery_timer_fn(struct timer_list *t)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	struct wil6210_vif *vif = from_timer(vif, t, p2p.discovery_timer);
25762306a36Sopenharmony_ci	struct wil6210_priv *wil = vif_to_wil(vif);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	wil_dbg_misc(wil, "p2p_discovery_timer_fn\n");
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	schedule_work(&vif->p2p.discovery_expired_work);
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic void wil_vif_init(struct wil6210_vif *vif)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	vif->bcast_ring = -1;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	mutex_init(&vif->probe_client_mutex);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	timer_setup(&vif->connect_timer, wil_connect_timer_fn, 0);
27162306a36Sopenharmony_ci	timer_setup(&vif->scan_timer, wil_scan_timer_fn, 0);
27262306a36Sopenharmony_ci	timer_setup(&vif->p2p.discovery_timer, wil_p2p_discovery_timer_fn, 0);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	INIT_WORK(&vif->probe_client_worker, wil_probe_client_worker);
27562306a36Sopenharmony_ci	INIT_WORK(&vif->disconnect_worker, wil_disconnect_worker);
27662306a36Sopenharmony_ci	INIT_WORK(&vif->p2p.discovery_expired_work, wil_p2p_listen_expired);
27762306a36Sopenharmony_ci	INIT_WORK(&vif->p2p.delayed_listen_work, wil_p2p_delayed_listen_work);
27862306a36Sopenharmony_ci	INIT_WORK(&vif->enable_tx_key_worker, wil_enable_tx_key_worker);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	INIT_LIST_HEAD(&vif->probe_client_pending);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	vif->net_queue_stopped = 1;
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic u8 wil_vif_find_free_mid(struct wil6210_priv *wil)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	u8 i;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	for (i = 0; i < GET_MAX_VIFS(wil); i++) {
29062306a36Sopenharmony_ci		if (!wil->vifs[i])
29162306a36Sopenharmony_ci			return i;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	return U8_MAX;
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistruct wil6210_vif *
29862306a36Sopenharmony_ciwil_vif_alloc(struct wil6210_priv *wil, const char *name,
29962306a36Sopenharmony_ci	      unsigned char name_assign_type, enum nl80211_iftype iftype)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct net_device *ndev;
30262306a36Sopenharmony_ci	struct wireless_dev *wdev;
30362306a36Sopenharmony_ci	struct wil6210_vif *vif;
30462306a36Sopenharmony_ci	u8 mid;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	mid = wil_vif_find_free_mid(wil);
30762306a36Sopenharmony_ci	if (mid == U8_MAX) {
30862306a36Sopenharmony_ci		wil_err(wil, "no available virtual interface\n");
30962306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	ndev = alloc_netdev(sizeof(*vif), name, name_assign_type,
31362306a36Sopenharmony_ci			    wil_dev_setup);
31462306a36Sopenharmony_ci	if (!ndev) {
31562306a36Sopenharmony_ci		dev_err(wil_to_dev(wil), "alloc_netdev failed\n");
31662306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
31762306a36Sopenharmony_ci	}
31862306a36Sopenharmony_ci	if (mid == 0) {
31962306a36Sopenharmony_ci		wil->main_ndev = ndev;
32062306a36Sopenharmony_ci	} else {
32162306a36Sopenharmony_ci		ndev->priv_destructor = wil_ndev_destructor;
32262306a36Sopenharmony_ci		ndev->needs_free_netdev = true;
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	vif = ndev_to_vif(ndev);
32662306a36Sopenharmony_ci	vif->ndev = ndev;
32762306a36Sopenharmony_ci	vif->wil = wil;
32862306a36Sopenharmony_ci	vif->mid = mid;
32962306a36Sopenharmony_ci	wil_vif_init(vif);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	wdev = &vif->wdev;
33262306a36Sopenharmony_ci	wdev->wiphy = wil->wiphy;
33362306a36Sopenharmony_ci	wdev->iftype = iftype;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	ndev->netdev_ops = &wil_netdev_ops;
33662306a36Sopenharmony_ci	wil_set_ethtoolops(ndev);
33762306a36Sopenharmony_ci	ndev->ieee80211_ptr = wdev;
33862306a36Sopenharmony_ci	ndev->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM |
33962306a36Sopenharmony_ci			    NETIF_F_SG | NETIF_F_GRO |
34062306a36Sopenharmony_ci			    NETIF_F_TSO | NETIF_F_TSO6;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	ndev->features |= ndev->hw_features;
34362306a36Sopenharmony_ci	SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
34462306a36Sopenharmony_ci	wdev->netdev = ndev;
34562306a36Sopenharmony_ci	return vif;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_civoid *wil_if_alloc(struct device *dev)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	struct wil6210_priv *wil;
35162306a36Sopenharmony_ci	struct wil6210_vif *vif;
35262306a36Sopenharmony_ci	int rc = 0;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	wil = wil_cfg80211_init(dev);
35562306a36Sopenharmony_ci	if (IS_ERR(wil)) {
35662306a36Sopenharmony_ci		dev_err(dev, "wil_cfg80211_init failed\n");
35762306a36Sopenharmony_ci		return wil;
35862306a36Sopenharmony_ci	}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	rc = wil_priv_init(wil);
36162306a36Sopenharmony_ci	if (rc) {
36262306a36Sopenharmony_ci		dev_err(dev, "wil_priv_init failed\n");
36362306a36Sopenharmony_ci		goto out_cfg;
36462306a36Sopenharmony_ci	}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	wil_dbg_misc(wil, "if_alloc\n");
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	vif = wil_vif_alloc(wil, "wlan%d", NET_NAME_UNKNOWN,
36962306a36Sopenharmony_ci			    NL80211_IFTYPE_STATION);
37062306a36Sopenharmony_ci	if (IS_ERR(vif)) {
37162306a36Sopenharmony_ci		dev_err(dev, "wil_vif_alloc failed\n");
37262306a36Sopenharmony_ci		rc = -ENOMEM;
37362306a36Sopenharmony_ci		goto out_priv;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	wil->radio_wdev = vif_to_wdev(vif);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	return wil;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ciout_priv:
38162306a36Sopenharmony_ci	wil_priv_deinit(wil);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ciout_cfg:
38462306a36Sopenharmony_ci	wil_cfg80211_deinit(wil);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	return ERR_PTR(rc);
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_civoid wil_if_free(struct wil6210_priv *wil)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	struct net_device *ndev = wil->main_ndev;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	wil_dbg_misc(wil, "if_free\n");
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	if (!ndev)
39662306a36Sopenharmony_ci		return;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	wil_priv_deinit(wil);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	wil->main_ndev = NULL;
40162306a36Sopenharmony_ci	wil_ndev_destructor(ndev);
40262306a36Sopenharmony_ci	free_netdev(ndev);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	wil_cfg80211_deinit(wil);
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ciint wil_vif_add(struct wil6210_priv *wil, struct wil6210_vif *vif)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	struct net_device *ndev = vif_to_ndev(vif);
41062306a36Sopenharmony_ci	struct wireless_dev *wdev = vif_to_wdev(vif);
41162306a36Sopenharmony_ci	bool any_active = wil_has_active_ifaces(wil, true, false);
41262306a36Sopenharmony_ci	int rc;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	ASSERT_RTNL();
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	if (wil->vifs[vif->mid]) {
41762306a36Sopenharmony_ci		dev_err(&ndev->dev, "VIF with mid %d already in use\n",
41862306a36Sopenharmony_ci			vif->mid);
41962306a36Sopenharmony_ci		return -EEXIST;
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci	if (any_active && vif->mid != 0) {
42262306a36Sopenharmony_ci		rc = wmi_port_allocate(wil, vif->mid, ndev->dev_addr,
42362306a36Sopenharmony_ci				       wdev->iftype);
42462306a36Sopenharmony_ci		if (rc)
42562306a36Sopenharmony_ci			return rc;
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci	rc = cfg80211_register_netdevice(ndev);
42862306a36Sopenharmony_ci	if (rc < 0) {
42962306a36Sopenharmony_ci		dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc);
43062306a36Sopenharmony_ci		if (any_active && vif->mid != 0)
43162306a36Sopenharmony_ci			wmi_port_delete(wil, vif->mid);
43262306a36Sopenharmony_ci		return rc;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	wil->vifs[vif->mid] = vif;
43662306a36Sopenharmony_ci	return 0;
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ciint wil_if_add(struct wil6210_priv *wil)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	struct wiphy *wiphy = wil->wiphy;
44262306a36Sopenharmony_ci	struct net_device *ndev = wil->main_ndev;
44362306a36Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(ndev);
44462306a36Sopenharmony_ci	int rc;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	wil_dbg_misc(wil, "entered");
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	strscpy(wiphy->fw_version, wil->fw_version, sizeof(wiphy->fw_version));
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	rc = wiphy_register(wiphy);
45162306a36Sopenharmony_ci	if (rc < 0) {
45262306a36Sopenharmony_ci		wil_err(wil, "failed to register wiphy, err %d\n", rc);
45362306a36Sopenharmony_ci		return rc;
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	init_dummy_netdev(&wil->napi_ndev);
45762306a36Sopenharmony_ci	if (wil->use_enhanced_dma_hw) {
45862306a36Sopenharmony_ci		netif_napi_add(&wil->napi_ndev, &wil->napi_rx,
45962306a36Sopenharmony_ci			       wil6210_netdev_poll_rx_edma);
46062306a36Sopenharmony_ci		netif_napi_add_tx(&wil->napi_ndev,
46162306a36Sopenharmony_ci				  &wil->napi_tx, wil6210_netdev_poll_tx_edma);
46262306a36Sopenharmony_ci	} else {
46362306a36Sopenharmony_ci		netif_napi_add(&wil->napi_ndev, &wil->napi_rx,
46462306a36Sopenharmony_ci			       wil6210_netdev_poll_rx);
46562306a36Sopenharmony_ci		netif_napi_add_tx(&wil->napi_ndev,
46662306a36Sopenharmony_ci				  &wil->napi_tx, wil6210_netdev_poll_tx);
46762306a36Sopenharmony_ci	}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	wil_update_net_queues_bh(wil, vif, NULL, true);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	rtnl_lock();
47262306a36Sopenharmony_ci	wiphy_lock(wiphy);
47362306a36Sopenharmony_ci	rc = wil_vif_add(wil, vif);
47462306a36Sopenharmony_ci	wiphy_unlock(wiphy);
47562306a36Sopenharmony_ci	rtnl_unlock();
47662306a36Sopenharmony_ci	if (rc < 0)
47762306a36Sopenharmony_ci		goto out_wiphy;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	return 0;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ciout_wiphy:
48262306a36Sopenharmony_ci	wiphy_unregister(wiphy);
48362306a36Sopenharmony_ci	return rc;
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_civoid wil_vif_remove(struct wil6210_priv *wil, u8 mid)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	struct wil6210_vif *vif;
48962306a36Sopenharmony_ci	struct net_device *ndev;
49062306a36Sopenharmony_ci	bool any_active = wil_has_active_ifaces(wil, true, false);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	ASSERT_RTNL();
49362306a36Sopenharmony_ci	if (mid >= GET_MAX_VIFS(wil)) {
49462306a36Sopenharmony_ci		wil_err(wil, "invalid MID: %d\n", mid);
49562306a36Sopenharmony_ci		return;
49662306a36Sopenharmony_ci	}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	vif = wil->vifs[mid];
49962306a36Sopenharmony_ci	if (!vif) {
50062306a36Sopenharmony_ci		wil_err(wil, "MID %d not registered\n", mid);
50162306a36Sopenharmony_ci		return;
50262306a36Sopenharmony_ci	}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	mutex_lock(&wil->mutex);
50562306a36Sopenharmony_ci	wil6210_disconnect(vif, NULL, WLAN_REASON_DEAUTH_LEAVING);
50662306a36Sopenharmony_ci	mutex_unlock(&wil->mutex);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	ndev = vif_to_ndev(vif);
50962306a36Sopenharmony_ci	/* during unregister_netdevice cfg80211_leave may perform operations
51062306a36Sopenharmony_ci	 * such as stop AP, disconnect, so we only clear the VIF afterwards
51162306a36Sopenharmony_ci	 */
51262306a36Sopenharmony_ci	cfg80211_unregister_netdevice(ndev);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	if (any_active && vif->mid != 0)
51562306a36Sopenharmony_ci		wmi_port_delete(wil, vif->mid);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	/* make sure no one is accessing the VIF before removing */
51862306a36Sopenharmony_ci	mutex_lock(&wil->vif_mutex);
51962306a36Sopenharmony_ci	wil->vifs[mid] = NULL;
52062306a36Sopenharmony_ci	/* ensure NAPI code will see the NULL VIF */
52162306a36Sopenharmony_ci	wmb();
52262306a36Sopenharmony_ci	if (test_bit(wil_status_napi_en, wil->status)) {
52362306a36Sopenharmony_ci		napi_synchronize(&wil->napi_rx);
52462306a36Sopenharmony_ci		napi_synchronize(&wil->napi_tx);
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci	mutex_unlock(&wil->vif_mutex);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	flush_work(&wil->wmi_event_worker);
52962306a36Sopenharmony_ci	del_timer_sync(&vif->connect_timer);
53062306a36Sopenharmony_ci	cancel_work_sync(&vif->disconnect_worker);
53162306a36Sopenharmony_ci	wil_probe_client_flush(vif);
53262306a36Sopenharmony_ci	cancel_work_sync(&vif->probe_client_worker);
53362306a36Sopenharmony_ci	cancel_work_sync(&vif->enable_tx_key_worker);
53462306a36Sopenharmony_ci	/* for VIFs, ndev will be freed by destructor after RTNL is unlocked.
53562306a36Sopenharmony_ci	 * the main interface will be freed in wil_if_free, we need to keep it
53662306a36Sopenharmony_ci	 * a bit longer so logging macros will work.
53762306a36Sopenharmony_ci	 */
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_civoid wil_if_remove(struct wil6210_priv *wil)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	struct net_device *ndev = wil->main_ndev;
54362306a36Sopenharmony_ci	struct wireless_dev *wdev = ndev->ieee80211_ptr;
54462306a36Sopenharmony_ci	struct wiphy *wiphy = wdev->wiphy;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	wil_dbg_misc(wil, "if_remove\n");
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	rtnl_lock();
54962306a36Sopenharmony_ci	wiphy_lock(wiphy);
55062306a36Sopenharmony_ci	wil_vif_remove(wil, 0);
55162306a36Sopenharmony_ci	wiphy_unlock(wiphy);
55262306a36Sopenharmony_ci	rtnl_unlock();
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	netif_napi_del(&wil->napi_tx);
55562306a36Sopenharmony_ci	netif_napi_del(&wil->napi_rx);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	wiphy_unregister(wiphy);
55862306a36Sopenharmony_ci}
559