18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: ISC
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
48c2ecf20Sopenharmony_ci * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
88c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
98c2ecf20Sopenharmony_ci#include "wil6210.h"
108c2ecf20Sopenharmony_ci#include "txrx.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_cibool wil_has_other_active_ifaces(struct wil6210_priv *wil,
138c2ecf20Sopenharmony_ci				 struct net_device *ndev, bool up, bool ok)
148c2ecf20Sopenharmony_ci{
158c2ecf20Sopenharmony_ci	int i;
168c2ecf20Sopenharmony_ci	struct wil6210_vif *vif;
178c2ecf20Sopenharmony_ci	struct net_device *ndev_i;
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci	for (i = 0; i < GET_MAX_VIFS(wil); i++) {
208c2ecf20Sopenharmony_ci		vif = wil->vifs[i];
218c2ecf20Sopenharmony_ci		if (vif) {
228c2ecf20Sopenharmony_ci			ndev_i = vif_to_ndev(vif);
238c2ecf20Sopenharmony_ci			if (ndev_i != ndev)
248c2ecf20Sopenharmony_ci				if ((up && (ndev_i->flags & IFF_UP)) ||
258c2ecf20Sopenharmony_ci				    (ok && netif_carrier_ok(ndev_i)))
268c2ecf20Sopenharmony_ci					return true;
278c2ecf20Sopenharmony_ci		}
288c2ecf20Sopenharmony_ci	}
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	return false;
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cibool wil_has_active_ifaces(struct wil6210_priv *wil, bool up, bool ok)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	/* use NULL ndev argument to check all interfaces */
368c2ecf20Sopenharmony_ci	return wil_has_other_active_ifaces(wil, NULL, up, ok);
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic int wil_open(struct net_device *ndev)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	struct wil6210_priv *wil = ndev_to_wil(ndev);
428c2ecf20Sopenharmony_ci	int rc = 0;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	wil_dbg_misc(wil, "open\n");
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	if (debug_fw ||
478c2ecf20Sopenharmony_ci	    test_bit(WMI_FW_CAPABILITY_WMI_ONLY, wil->fw_capabilities)) {
488c2ecf20Sopenharmony_ci		wil_err(wil, "while in debug_fw or wmi_only mode\n");
498c2ecf20Sopenharmony_ci		return -EINVAL;
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	if (!wil_has_other_active_ifaces(wil, ndev, true, false)) {
538c2ecf20Sopenharmony_ci		wil_dbg_misc(wil, "open, first iface\n");
548c2ecf20Sopenharmony_ci		rc = wil_pm_runtime_get(wil);
558c2ecf20Sopenharmony_ci		if (rc < 0)
568c2ecf20Sopenharmony_ci			return rc;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci		rc = wil_up(wil);
598c2ecf20Sopenharmony_ci		if (rc)
608c2ecf20Sopenharmony_ci			wil_pm_runtime_put(wil);
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	return rc;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic int wil_stop(struct net_device *ndev)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct wil6210_priv *wil = ndev_to_wil(ndev);
698c2ecf20Sopenharmony_ci	int rc = 0;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	wil_dbg_misc(wil, "stop\n");
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	if (!wil_has_other_active_ifaces(wil, ndev, true, false)) {
748c2ecf20Sopenharmony_ci		wil_dbg_misc(wil, "stop, last iface\n");
758c2ecf20Sopenharmony_ci		rc = wil_down(wil);
768c2ecf20Sopenharmony_ci		if (!rc)
778c2ecf20Sopenharmony_ci			wil_pm_runtime_put(wil);
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	return rc;
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic const struct net_device_ops wil_netdev_ops = {
848c2ecf20Sopenharmony_ci	.ndo_open		= wil_open,
858c2ecf20Sopenharmony_ci	.ndo_stop		= wil_stop,
868c2ecf20Sopenharmony_ci	.ndo_start_xmit		= wil_start_xmit,
878c2ecf20Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
888c2ecf20Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
898c2ecf20Sopenharmony_ci};
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic int wil6210_netdev_poll_rx(struct napi_struct *napi, int budget)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	struct wil6210_priv *wil = container_of(napi, struct wil6210_priv,
948c2ecf20Sopenharmony_ci						napi_rx);
958c2ecf20Sopenharmony_ci	int quota = budget;
968c2ecf20Sopenharmony_ci	int done;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	wil_rx_handle(wil, &quota);
998c2ecf20Sopenharmony_ci	done = budget - quota;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (done < budget) {
1028c2ecf20Sopenharmony_ci		napi_complete_done(napi, done);
1038c2ecf20Sopenharmony_ci		wil6210_unmask_irq_rx(wil);
1048c2ecf20Sopenharmony_ci		wil_dbg_txrx(wil, "NAPI RX complete\n");
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	wil_dbg_txrx(wil, "NAPI RX poll(%d) done %d\n", budget, done);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return done;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic int wil6210_netdev_poll_rx_edma(struct napi_struct *napi, int budget)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct wil6210_priv *wil = container_of(napi, struct wil6210_priv,
1158c2ecf20Sopenharmony_ci						napi_rx);
1168c2ecf20Sopenharmony_ci	int quota = budget;
1178c2ecf20Sopenharmony_ci	int done;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	wil_rx_handle_edma(wil, &quota);
1208c2ecf20Sopenharmony_ci	done = budget - quota;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	if (done < budget) {
1238c2ecf20Sopenharmony_ci		napi_complete_done(napi, done);
1248c2ecf20Sopenharmony_ci		wil6210_unmask_irq_rx_edma(wil);
1258c2ecf20Sopenharmony_ci		wil_dbg_txrx(wil, "NAPI RX complete\n");
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	wil_dbg_txrx(wil, "NAPI RX poll(%d) done %d\n", budget, done);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	return done;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic int wil6210_netdev_poll_tx(struct napi_struct *napi, int budget)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	struct wil6210_priv *wil = container_of(napi, struct wil6210_priv,
1368c2ecf20Sopenharmony_ci						napi_tx);
1378c2ecf20Sopenharmony_ci	int tx_done = 0;
1388c2ecf20Sopenharmony_ci	uint i;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/* always process ALL Tx complete, regardless budget - it is fast */
1418c2ecf20Sopenharmony_ci	for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
1428c2ecf20Sopenharmony_ci		struct wil_ring *ring = &wil->ring_tx[i];
1438c2ecf20Sopenharmony_ci		struct wil_ring_tx_data *txdata = &wil->ring_tx_data[i];
1448c2ecf20Sopenharmony_ci		struct wil6210_vif *vif;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci		if (!ring->va || !txdata->enabled ||
1478c2ecf20Sopenharmony_ci		    txdata->mid >= GET_MAX_VIFS(wil))
1488c2ecf20Sopenharmony_ci			continue;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci		vif = wil->vifs[txdata->mid];
1518c2ecf20Sopenharmony_ci		if (unlikely(!vif)) {
1528c2ecf20Sopenharmony_ci			wil_dbg_txrx(wil, "Invalid MID %d\n", txdata->mid);
1538c2ecf20Sopenharmony_ci			continue;
1548c2ecf20Sopenharmony_ci		}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci		tx_done += wil_tx_complete(vif, i);
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (tx_done < budget) {
1608c2ecf20Sopenharmony_ci		napi_complete(napi);
1618c2ecf20Sopenharmony_ci		wil6210_unmask_irq_tx(wil);
1628c2ecf20Sopenharmony_ci		wil_dbg_txrx(wil, "NAPI TX complete\n");
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	wil_dbg_txrx(wil, "NAPI TX poll(%d) done %d\n", budget, tx_done);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	return min(tx_done, budget);
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic int wil6210_netdev_poll_tx_edma(struct napi_struct *napi, int budget)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	struct wil6210_priv *wil = container_of(napi, struct wil6210_priv,
1738c2ecf20Sopenharmony_ci						napi_tx);
1748c2ecf20Sopenharmony_ci	int tx_done;
1758c2ecf20Sopenharmony_ci	/* There is only one status TX ring */
1768c2ecf20Sopenharmony_ci	struct wil_status_ring *sring = &wil->srings[wil->tx_sring_idx];
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	if (!sring->va)
1798c2ecf20Sopenharmony_ci		return 0;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	tx_done = wil_tx_sring_handler(wil, sring);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (tx_done < budget) {
1848c2ecf20Sopenharmony_ci		napi_complete(napi);
1858c2ecf20Sopenharmony_ci		wil6210_unmask_irq_tx_edma(wil);
1868c2ecf20Sopenharmony_ci		wil_dbg_txrx(wil, "NAPI TX complete\n");
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	wil_dbg_txrx(wil, "NAPI TX poll(%d) done %d\n", budget, tx_done);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	return min(tx_done, budget);
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic void wil_dev_setup(struct net_device *dev)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	ether_setup(dev);
1978c2ecf20Sopenharmony_ci	dev->max_mtu = mtu_max;
1988c2ecf20Sopenharmony_ci	dev->tx_queue_len = WIL_TX_Q_LEN_DEFAULT;
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistatic void wil_vif_deinit(struct wil6210_vif *vif)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	del_timer_sync(&vif->scan_timer);
2048c2ecf20Sopenharmony_ci	del_timer_sync(&vif->p2p.discovery_timer);
2058c2ecf20Sopenharmony_ci	cancel_work_sync(&vif->disconnect_worker);
2068c2ecf20Sopenharmony_ci	cancel_work_sync(&vif->p2p.discovery_expired_work);
2078c2ecf20Sopenharmony_ci	cancel_work_sync(&vif->p2p.delayed_listen_work);
2088c2ecf20Sopenharmony_ci	wil_probe_client_flush(vif);
2098c2ecf20Sopenharmony_ci	cancel_work_sync(&vif->probe_client_worker);
2108c2ecf20Sopenharmony_ci	cancel_work_sync(&vif->enable_tx_key_worker);
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_civoid wil_vif_free(struct wil6210_vif *vif)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	struct net_device *ndev = vif_to_ndev(vif);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	wil_vif_deinit(vif);
2188c2ecf20Sopenharmony_ci	free_netdev(ndev);
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic void wil_ndev_destructor(struct net_device *ndev)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(ndev);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	wil_vif_deinit(vif);
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic void wil_connect_timer_fn(struct timer_list *t)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	struct wil6210_vif *vif = from_timer(vif, t, connect_timer);
2318c2ecf20Sopenharmony_ci	struct wil6210_priv *wil = vif_to_wil(vif);
2328c2ecf20Sopenharmony_ci	bool q;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	wil_err(wil, "Connect timeout detected, disconnect station\n");
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	/* reschedule to thread context - disconnect won't
2378c2ecf20Sopenharmony_ci	 * run from atomic context.
2388c2ecf20Sopenharmony_ci	 * queue on wmi_wq to prevent race with connect event.
2398c2ecf20Sopenharmony_ci	 */
2408c2ecf20Sopenharmony_ci	q = queue_work(wil->wmi_wq, &vif->disconnect_worker);
2418c2ecf20Sopenharmony_ci	wil_dbg_wmi(wil, "queue_work of disconnect_worker -> %d\n", q);
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic void wil_scan_timer_fn(struct timer_list *t)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	struct wil6210_vif *vif = from_timer(vif, t, scan_timer);
2478c2ecf20Sopenharmony_ci	struct wil6210_priv *wil = vif_to_wil(vif);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	clear_bit(wil_status_fwready, wil->status);
2508c2ecf20Sopenharmony_ci	wil_err(wil, "Scan timeout detected, start fw error recovery\n");
2518c2ecf20Sopenharmony_ci	wil_fw_error_recovery(wil);
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic void wil_p2p_discovery_timer_fn(struct timer_list *t)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	struct wil6210_vif *vif = from_timer(vif, t, p2p.discovery_timer);
2578c2ecf20Sopenharmony_ci	struct wil6210_priv *wil = vif_to_wil(vif);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	wil_dbg_misc(wil, "p2p_discovery_timer_fn\n");
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	schedule_work(&vif->p2p.discovery_expired_work);
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic void wil_vif_init(struct wil6210_vif *vif)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	vif->bcast_ring = -1;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	mutex_init(&vif->probe_client_mutex);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	timer_setup(&vif->connect_timer, wil_connect_timer_fn, 0);
2718c2ecf20Sopenharmony_ci	timer_setup(&vif->scan_timer, wil_scan_timer_fn, 0);
2728c2ecf20Sopenharmony_ci	timer_setup(&vif->p2p.discovery_timer, wil_p2p_discovery_timer_fn, 0);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	INIT_WORK(&vif->probe_client_worker, wil_probe_client_worker);
2758c2ecf20Sopenharmony_ci	INIT_WORK(&vif->disconnect_worker, wil_disconnect_worker);
2768c2ecf20Sopenharmony_ci	INIT_WORK(&vif->p2p.discovery_expired_work, wil_p2p_listen_expired);
2778c2ecf20Sopenharmony_ci	INIT_WORK(&vif->p2p.delayed_listen_work, wil_p2p_delayed_listen_work);
2788c2ecf20Sopenharmony_ci	INIT_WORK(&vif->enable_tx_key_worker, wil_enable_tx_key_worker);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&vif->probe_client_pending);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	vif->net_queue_stopped = 1;
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic u8 wil_vif_find_free_mid(struct wil6210_priv *wil)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	u8 i;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	for (i = 0; i < GET_MAX_VIFS(wil); i++) {
2908c2ecf20Sopenharmony_ci		if (!wil->vifs[i])
2918c2ecf20Sopenharmony_ci			return i;
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	return U8_MAX;
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistruct wil6210_vif *
2988c2ecf20Sopenharmony_ciwil_vif_alloc(struct wil6210_priv *wil, const char *name,
2998c2ecf20Sopenharmony_ci	      unsigned char name_assign_type, enum nl80211_iftype iftype)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	struct net_device *ndev;
3028c2ecf20Sopenharmony_ci	struct wireless_dev *wdev;
3038c2ecf20Sopenharmony_ci	struct wil6210_vif *vif;
3048c2ecf20Sopenharmony_ci	u8 mid;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	mid = wil_vif_find_free_mid(wil);
3078c2ecf20Sopenharmony_ci	if (mid == U8_MAX) {
3088c2ecf20Sopenharmony_ci		wil_err(wil, "no available virtual interface\n");
3098c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	ndev = alloc_netdev(sizeof(*vif), name, name_assign_type,
3138c2ecf20Sopenharmony_ci			    wil_dev_setup);
3148c2ecf20Sopenharmony_ci	if (!ndev) {
3158c2ecf20Sopenharmony_ci		dev_err(wil_to_dev(wil), "alloc_netdev failed\n");
3168c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
3178c2ecf20Sopenharmony_ci	}
3188c2ecf20Sopenharmony_ci	if (mid == 0) {
3198c2ecf20Sopenharmony_ci		wil->main_ndev = ndev;
3208c2ecf20Sopenharmony_ci	} else {
3218c2ecf20Sopenharmony_ci		ndev->priv_destructor = wil_ndev_destructor;
3228c2ecf20Sopenharmony_ci		ndev->needs_free_netdev = true;
3238c2ecf20Sopenharmony_ci	}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	vif = ndev_to_vif(ndev);
3268c2ecf20Sopenharmony_ci	vif->ndev = ndev;
3278c2ecf20Sopenharmony_ci	vif->wil = wil;
3288c2ecf20Sopenharmony_ci	vif->mid = mid;
3298c2ecf20Sopenharmony_ci	wil_vif_init(vif);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	wdev = &vif->wdev;
3328c2ecf20Sopenharmony_ci	wdev->wiphy = wil->wiphy;
3338c2ecf20Sopenharmony_ci	wdev->iftype = iftype;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	ndev->netdev_ops = &wil_netdev_ops;
3368c2ecf20Sopenharmony_ci	wil_set_ethtoolops(ndev);
3378c2ecf20Sopenharmony_ci	ndev->ieee80211_ptr = wdev;
3388c2ecf20Sopenharmony_ci	ndev->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM |
3398c2ecf20Sopenharmony_ci			    NETIF_F_SG | NETIF_F_GRO |
3408c2ecf20Sopenharmony_ci			    NETIF_F_TSO | NETIF_F_TSO6;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	ndev->features |= ndev->hw_features;
3438c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
3448c2ecf20Sopenharmony_ci	wdev->netdev = ndev;
3458c2ecf20Sopenharmony_ci	return vif;
3468c2ecf20Sopenharmony_ci}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_civoid *wil_if_alloc(struct device *dev)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	struct wil6210_priv *wil;
3518c2ecf20Sopenharmony_ci	struct wil6210_vif *vif;
3528c2ecf20Sopenharmony_ci	int rc = 0;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	wil = wil_cfg80211_init(dev);
3558c2ecf20Sopenharmony_ci	if (IS_ERR(wil)) {
3568c2ecf20Sopenharmony_ci		dev_err(dev, "wil_cfg80211_init failed\n");
3578c2ecf20Sopenharmony_ci		return wil;
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	rc = wil_priv_init(wil);
3618c2ecf20Sopenharmony_ci	if (rc) {
3628c2ecf20Sopenharmony_ci		dev_err(dev, "wil_priv_init failed\n");
3638c2ecf20Sopenharmony_ci		goto out_cfg;
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	wil_dbg_misc(wil, "if_alloc\n");
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	vif = wil_vif_alloc(wil, "wlan%d", NET_NAME_UNKNOWN,
3698c2ecf20Sopenharmony_ci			    NL80211_IFTYPE_STATION);
3708c2ecf20Sopenharmony_ci	if (IS_ERR(vif)) {
3718c2ecf20Sopenharmony_ci		dev_err(dev, "wil_vif_alloc failed\n");
3728c2ecf20Sopenharmony_ci		rc = -ENOMEM;
3738c2ecf20Sopenharmony_ci		goto out_priv;
3748c2ecf20Sopenharmony_ci	}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	wil->radio_wdev = vif_to_wdev(vif);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	return wil;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ciout_priv:
3818c2ecf20Sopenharmony_ci	wil_priv_deinit(wil);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ciout_cfg:
3848c2ecf20Sopenharmony_ci	wil_cfg80211_deinit(wil);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	return ERR_PTR(rc);
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_civoid wil_if_free(struct wil6210_priv *wil)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	struct net_device *ndev = wil->main_ndev;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	wil_dbg_misc(wil, "if_free\n");
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	if (!ndev)
3968c2ecf20Sopenharmony_ci		return;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	wil_priv_deinit(wil);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	wil->main_ndev = NULL;
4018c2ecf20Sopenharmony_ci	wil_ndev_destructor(ndev);
4028c2ecf20Sopenharmony_ci	free_netdev(ndev);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	wil_cfg80211_deinit(wil);
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ciint wil_vif_add(struct wil6210_priv *wil, struct wil6210_vif *vif)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	struct net_device *ndev = vif_to_ndev(vif);
4108c2ecf20Sopenharmony_ci	struct wireless_dev *wdev = vif_to_wdev(vif);
4118c2ecf20Sopenharmony_ci	bool any_active = wil_has_active_ifaces(wil, true, false);
4128c2ecf20Sopenharmony_ci	int rc;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	ASSERT_RTNL();
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	if (wil->vifs[vif->mid]) {
4178c2ecf20Sopenharmony_ci		dev_err(&ndev->dev, "VIF with mid %d already in use\n",
4188c2ecf20Sopenharmony_ci			vif->mid);
4198c2ecf20Sopenharmony_ci		return -EEXIST;
4208c2ecf20Sopenharmony_ci	}
4218c2ecf20Sopenharmony_ci	if (any_active && vif->mid != 0) {
4228c2ecf20Sopenharmony_ci		rc = wmi_port_allocate(wil, vif->mid, ndev->dev_addr,
4238c2ecf20Sopenharmony_ci				       wdev->iftype);
4248c2ecf20Sopenharmony_ci		if (rc)
4258c2ecf20Sopenharmony_ci			return rc;
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci	rc = register_netdevice(ndev);
4288c2ecf20Sopenharmony_ci	if (rc < 0) {
4298c2ecf20Sopenharmony_ci		dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc);
4308c2ecf20Sopenharmony_ci		if (any_active && vif->mid != 0)
4318c2ecf20Sopenharmony_ci			wmi_port_delete(wil, vif->mid);
4328c2ecf20Sopenharmony_ci		return rc;
4338c2ecf20Sopenharmony_ci	}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	wil->vifs[vif->mid] = vif;
4368c2ecf20Sopenharmony_ci	return 0;
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ciint wil_if_add(struct wil6210_priv *wil)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	struct wiphy *wiphy = wil->wiphy;
4428c2ecf20Sopenharmony_ci	struct net_device *ndev = wil->main_ndev;
4438c2ecf20Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(ndev);
4448c2ecf20Sopenharmony_ci	int rc;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	wil_dbg_misc(wil, "entered");
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	strlcpy(wiphy->fw_version, wil->fw_version, sizeof(wiphy->fw_version));
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	rc = wiphy_register(wiphy);
4518c2ecf20Sopenharmony_ci	if (rc < 0) {
4528c2ecf20Sopenharmony_ci		wil_err(wil, "failed to register wiphy, err %d\n", rc);
4538c2ecf20Sopenharmony_ci		return rc;
4548c2ecf20Sopenharmony_ci	}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	init_dummy_netdev(&wil->napi_ndev);
4578c2ecf20Sopenharmony_ci	if (wil->use_enhanced_dma_hw) {
4588c2ecf20Sopenharmony_ci		netif_napi_add(&wil->napi_ndev, &wil->napi_rx,
4598c2ecf20Sopenharmony_ci			       wil6210_netdev_poll_rx_edma,
4608c2ecf20Sopenharmony_ci			       WIL6210_NAPI_BUDGET);
4618c2ecf20Sopenharmony_ci		netif_tx_napi_add(&wil->napi_ndev,
4628c2ecf20Sopenharmony_ci				  &wil->napi_tx, wil6210_netdev_poll_tx_edma,
4638c2ecf20Sopenharmony_ci				  WIL6210_NAPI_BUDGET);
4648c2ecf20Sopenharmony_ci	} else {
4658c2ecf20Sopenharmony_ci		netif_napi_add(&wil->napi_ndev, &wil->napi_rx,
4668c2ecf20Sopenharmony_ci			       wil6210_netdev_poll_rx,
4678c2ecf20Sopenharmony_ci			       WIL6210_NAPI_BUDGET);
4688c2ecf20Sopenharmony_ci		netif_tx_napi_add(&wil->napi_ndev,
4698c2ecf20Sopenharmony_ci				  &wil->napi_tx, wil6210_netdev_poll_tx,
4708c2ecf20Sopenharmony_ci				  WIL6210_NAPI_BUDGET);
4718c2ecf20Sopenharmony_ci	}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	wil_update_net_queues_bh(wil, vif, NULL, true);
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	rtnl_lock();
4768c2ecf20Sopenharmony_ci	rc = wil_vif_add(wil, vif);
4778c2ecf20Sopenharmony_ci	rtnl_unlock();
4788c2ecf20Sopenharmony_ci	if (rc < 0)
4798c2ecf20Sopenharmony_ci		goto out_wiphy;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	return 0;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ciout_wiphy:
4848c2ecf20Sopenharmony_ci	wiphy_unregister(wiphy);
4858c2ecf20Sopenharmony_ci	return rc;
4868c2ecf20Sopenharmony_ci}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_civoid wil_vif_remove(struct wil6210_priv *wil, u8 mid)
4898c2ecf20Sopenharmony_ci{
4908c2ecf20Sopenharmony_ci	struct wil6210_vif *vif;
4918c2ecf20Sopenharmony_ci	struct net_device *ndev;
4928c2ecf20Sopenharmony_ci	bool any_active = wil_has_active_ifaces(wil, true, false);
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	ASSERT_RTNL();
4958c2ecf20Sopenharmony_ci	if (mid >= GET_MAX_VIFS(wil)) {
4968c2ecf20Sopenharmony_ci		wil_err(wil, "invalid MID: %d\n", mid);
4978c2ecf20Sopenharmony_ci		return;
4988c2ecf20Sopenharmony_ci	}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	vif = wil->vifs[mid];
5018c2ecf20Sopenharmony_ci	if (!vif) {
5028c2ecf20Sopenharmony_ci		wil_err(wil, "MID %d not registered\n", mid);
5038c2ecf20Sopenharmony_ci		return;
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	mutex_lock(&wil->mutex);
5078c2ecf20Sopenharmony_ci	wil6210_disconnect(vif, NULL, WLAN_REASON_DEAUTH_LEAVING);
5088c2ecf20Sopenharmony_ci	mutex_unlock(&wil->mutex);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	ndev = vif_to_ndev(vif);
5118c2ecf20Sopenharmony_ci	/* during unregister_netdevice cfg80211_leave may perform operations
5128c2ecf20Sopenharmony_ci	 * such as stop AP, disconnect, so we only clear the VIF afterwards
5138c2ecf20Sopenharmony_ci	 */
5148c2ecf20Sopenharmony_ci	unregister_netdevice(ndev);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	if (any_active && vif->mid != 0)
5178c2ecf20Sopenharmony_ci		wmi_port_delete(wil, vif->mid);
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	/* make sure no one is accessing the VIF before removing */
5208c2ecf20Sopenharmony_ci	mutex_lock(&wil->vif_mutex);
5218c2ecf20Sopenharmony_ci	wil->vifs[mid] = NULL;
5228c2ecf20Sopenharmony_ci	/* ensure NAPI code will see the NULL VIF */
5238c2ecf20Sopenharmony_ci	wmb();
5248c2ecf20Sopenharmony_ci	if (test_bit(wil_status_napi_en, wil->status)) {
5258c2ecf20Sopenharmony_ci		napi_synchronize(&wil->napi_rx);
5268c2ecf20Sopenharmony_ci		napi_synchronize(&wil->napi_tx);
5278c2ecf20Sopenharmony_ci	}
5288c2ecf20Sopenharmony_ci	mutex_unlock(&wil->vif_mutex);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	flush_work(&wil->wmi_event_worker);
5318c2ecf20Sopenharmony_ci	del_timer_sync(&vif->connect_timer);
5328c2ecf20Sopenharmony_ci	cancel_work_sync(&vif->disconnect_worker);
5338c2ecf20Sopenharmony_ci	wil_probe_client_flush(vif);
5348c2ecf20Sopenharmony_ci	cancel_work_sync(&vif->probe_client_worker);
5358c2ecf20Sopenharmony_ci	cancel_work_sync(&vif->enable_tx_key_worker);
5368c2ecf20Sopenharmony_ci	/* for VIFs, ndev will be freed by destructor after RTNL is unlocked.
5378c2ecf20Sopenharmony_ci	 * the main interface will be freed in wil_if_free, we need to keep it
5388c2ecf20Sopenharmony_ci	 * a bit longer so logging macros will work.
5398c2ecf20Sopenharmony_ci	 */
5408c2ecf20Sopenharmony_ci}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_civoid wil_if_remove(struct wil6210_priv *wil)
5438c2ecf20Sopenharmony_ci{
5448c2ecf20Sopenharmony_ci	struct net_device *ndev = wil->main_ndev;
5458c2ecf20Sopenharmony_ci	struct wireless_dev *wdev = ndev->ieee80211_ptr;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	wil_dbg_misc(wil, "if_remove\n");
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	rtnl_lock();
5508c2ecf20Sopenharmony_ci	wil_vif_remove(wil, 0);
5518c2ecf20Sopenharmony_ci	rtnl_unlock();
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	netif_napi_del(&wil->napi_tx);
5548c2ecf20Sopenharmony_ci	netif_napi_del(&wil->napi_rx);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	wiphy_unregister(wdev->wiphy);
5578c2ecf20Sopenharmony_ci}
558