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, "a); 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, "a); 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