162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries. 462306a36Sopenharmony_ci * All rights reserved. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/irq.h> 862306a36Sopenharmony_ci#include <linux/kthread.h> 962306a36Sopenharmony_ci#include <linux/firmware.h> 1062306a36Sopenharmony_ci#include <linux/netdevice.h> 1162306a36Sopenharmony_ci#include <linux/inetdevice.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "cfg80211.h" 1462306a36Sopenharmony_ci#include "wlan_cfg.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define WILC_MULTICAST_TABLE_SIZE 8 1762306a36Sopenharmony_ci#define WILC_MAX_FW_VERSION_STR_SIZE 50 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* latest API version supported */ 2062306a36Sopenharmony_ci#define WILC1000_API_VER 1 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define WILC1000_FW_PREFIX "atmel/wilc1000_wifi_firmware-" 2362306a36Sopenharmony_ci#define __WILC1000_FW(api) WILC1000_FW_PREFIX #api ".bin" 2462306a36Sopenharmony_ci#define WILC1000_FW(api) __WILC1000_FW(api) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic irqreturn_t isr_uh_routine(int irq, void *user_data) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci struct wilc *wilc = user_data; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci if (wilc->close) { 3162306a36Sopenharmony_ci pr_err("Can't handle UH interrupt\n"); 3262306a36Sopenharmony_ci return IRQ_HANDLED; 3362306a36Sopenharmony_ci } 3462306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic irqreturn_t isr_bh_routine(int irq, void *userdata) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci struct wilc *wilc = userdata; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci if (wilc->close) { 4262306a36Sopenharmony_ci pr_err("Can't handle BH interrupt\n"); 4362306a36Sopenharmony_ci return IRQ_HANDLED; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci wilc_handle_isr(wilc); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci return IRQ_HANDLED; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic int init_irq(struct net_device *dev) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci struct wilc_vif *vif = netdev_priv(dev); 5462306a36Sopenharmony_ci struct wilc *wl = vif->wilc; 5562306a36Sopenharmony_ci int ret; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci ret = request_threaded_irq(wl->dev_irq_num, isr_uh_routine, 5862306a36Sopenharmony_ci isr_bh_routine, 5962306a36Sopenharmony_ci IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 6062306a36Sopenharmony_ci dev->name, wl); 6162306a36Sopenharmony_ci if (ret) { 6262306a36Sopenharmony_ci netdev_err(dev, "Failed to request IRQ [%d]\n", ret); 6362306a36Sopenharmony_ci return ret; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci netdev_dbg(dev, "IRQ request succeeded IRQ-NUM= %d\n", wl->dev_irq_num); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic void deinit_irq(struct net_device *dev) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct wilc_vif *vif = netdev_priv(dev); 7362306a36Sopenharmony_ci struct wilc *wilc = vif->wilc; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* Deinitialize IRQ */ 7662306a36Sopenharmony_ci if (wilc->dev_irq_num) 7762306a36Sopenharmony_ci free_irq(wilc->dev_irq_num, wilc); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_civoid wilc_mac_indicate(struct wilc *wilc) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci s8 status; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci wilc_wlan_cfg_get_val(wilc, WID_STATUS, &status, 1); 8562306a36Sopenharmony_ci if (wilc->mac_status == WILC_MAC_STATUS_INIT) { 8662306a36Sopenharmony_ci wilc->mac_status = status; 8762306a36Sopenharmony_ci complete(&wilc->sync_event); 8862306a36Sopenharmony_ci } else { 8962306a36Sopenharmony_ci wilc->mac_status = status; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic struct net_device *get_if_handler(struct wilc *wilc, u8 *mac_header) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci struct net_device *ndev = NULL; 9662306a36Sopenharmony_ci struct wilc_vif *vif; 9762306a36Sopenharmony_ci struct ieee80211_hdr *h = (struct ieee80211_hdr *)mac_header; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci list_for_each_entry_rcu(vif, &wilc->vif_list, list) { 10062306a36Sopenharmony_ci if (vif->iftype == WILC_STATION_MODE) 10162306a36Sopenharmony_ci if (ether_addr_equal_unaligned(h->addr2, vif->bssid)) { 10262306a36Sopenharmony_ci ndev = vif->ndev; 10362306a36Sopenharmony_ci goto out; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci if (vif->iftype == WILC_AP_MODE) 10662306a36Sopenharmony_ci if (ether_addr_equal_unaligned(h->addr1, vif->bssid)) { 10762306a36Sopenharmony_ci ndev = vif->ndev; 10862306a36Sopenharmony_ci goto out; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ciout: 11262306a36Sopenharmony_ci return ndev; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_civoid wilc_wlan_set_bssid(struct net_device *wilc_netdev, const u8 *bssid, 11662306a36Sopenharmony_ci u8 mode) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct wilc_vif *vif = netdev_priv(wilc_netdev); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (bssid) 12162306a36Sopenharmony_ci ether_addr_copy(vif->bssid, bssid); 12262306a36Sopenharmony_ci else 12362306a36Sopenharmony_ci eth_zero_addr(vif->bssid); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci vif->iftype = mode; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ciint wilc_wlan_get_num_conn_ifcs(struct wilc *wilc) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci int srcu_idx; 13162306a36Sopenharmony_ci u8 ret_val = 0; 13262306a36Sopenharmony_ci struct wilc_vif *vif; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci srcu_idx = srcu_read_lock(&wilc->srcu); 13562306a36Sopenharmony_ci list_for_each_entry_rcu(vif, &wilc->vif_list, list) { 13662306a36Sopenharmony_ci if (!is_zero_ether_addr(vif->bssid)) 13762306a36Sopenharmony_ci ret_val++; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci srcu_read_unlock(&wilc->srcu, srcu_idx); 14062306a36Sopenharmony_ci return ret_val; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic int wilc_txq_task(void *vp) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci int ret; 14662306a36Sopenharmony_ci u32 txq_count; 14762306a36Sopenharmony_ci struct wilc *wl = vp; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci complete(&wl->txq_thread_started); 15062306a36Sopenharmony_ci while (1) { 15162306a36Sopenharmony_ci wait_for_completion(&wl->txq_event); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (wl->close) { 15462306a36Sopenharmony_ci complete(&wl->txq_thread_started); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci while (!kthread_should_stop()) 15762306a36Sopenharmony_ci schedule(); 15862306a36Sopenharmony_ci break; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci do { 16162306a36Sopenharmony_ci ret = wilc_wlan_handle_txq(wl, &txq_count); 16262306a36Sopenharmony_ci if (txq_count < FLOW_CONTROL_LOWER_THRESHOLD) { 16362306a36Sopenharmony_ci int srcu_idx; 16462306a36Sopenharmony_ci struct wilc_vif *ifc; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci srcu_idx = srcu_read_lock(&wl->srcu); 16762306a36Sopenharmony_ci list_for_each_entry_rcu(ifc, &wl->vif_list, 16862306a36Sopenharmony_ci list) { 16962306a36Sopenharmony_ci if (ifc->mac_opened && ifc->ndev) 17062306a36Sopenharmony_ci netif_wake_queue(ifc->ndev); 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci srcu_read_unlock(&wl->srcu, srcu_idx); 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci } while (ret == WILC_VMM_ENTRY_FULL_RETRY && !wl->close); 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci return 0; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic int wilc_wlan_get_firmware(struct net_device *dev) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct wilc_vif *vif = netdev_priv(dev); 18262306a36Sopenharmony_ci struct wilc *wilc = vif->wilc; 18362306a36Sopenharmony_ci int chip_id; 18462306a36Sopenharmony_ci const struct firmware *wilc_fw; 18562306a36Sopenharmony_ci int ret; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci chip_id = wilc_get_chipid(wilc, false); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci netdev_info(dev, "ChipID [%x] loading firmware [%s]\n", chip_id, 19062306a36Sopenharmony_ci WILC1000_FW(WILC1000_API_VER)); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci ret = request_firmware(&wilc_fw, WILC1000_FW(WILC1000_API_VER), 19362306a36Sopenharmony_ci wilc->dev); 19462306a36Sopenharmony_ci if (ret != 0) { 19562306a36Sopenharmony_ci netdev_err(dev, "%s - firmware not available\n", 19662306a36Sopenharmony_ci WILC1000_FW(WILC1000_API_VER)); 19762306a36Sopenharmony_ci return -EINVAL; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci wilc->firmware = wilc_fw; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic int wilc_start_firmware(struct net_device *dev) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct wilc_vif *vif = netdev_priv(dev); 20762306a36Sopenharmony_ci struct wilc *wilc = vif->wilc; 20862306a36Sopenharmony_ci int ret = 0; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci ret = wilc_wlan_start(wilc); 21162306a36Sopenharmony_ci if (ret) 21262306a36Sopenharmony_ci return ret; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (!wait_for_completion_timeout(&wilc->sync_event, 21562306a36Sopenharmony_ci msecs_to_jiffies(5000))) 21662306a36Sopenharmony_ci return -ETIME; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic int wilc1000_firmware_download(struct net_device *dev) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct wilc_vif *vif = netdev_priv(dev); 22462306a36Sopenharmony_ci struct wilc *wilc = vif->wilc; 22562306a36Sopenharmony_ci int ret = 0; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (!wilc->firmware) { 22862306a36Sopenharmony_ci netdev_err(dev, "Firmware buffer is NULL\n"); 22962306a36Sopenharmony_ci return -ENOBUFS; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci ret = wilc_wlan_firmware_download(wilc, wilc->firmware->data, 23362306a36Sopenharmony_ci wilc->firmware->size); 23462306a36Sopenharmony_ci if (ret) 23562306a36Sopenharmony_ci return ret; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci release_firmware(wilc->firmware); 23862306a36Sopenharmony_ci wilc->firmware = NULL; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci netdev_dbg(dev, "Download Succeeded\n"); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return 0; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic int wilc_init_fw_config(struct net_device *dev, struct wilc_vif *vif) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct wilc_priv *priv = &vif->priv; 24862306a36Sopenharmony_ci struct host_if_drv *hif_drv; 24962306a36Sopenharmony_ci u8 b; 25062306a36Sopenharmony_ci u16 hw; 25162306a36Sopenharmony_ci u32 w; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci netdev_dbg(dev, "Start configuring Firmware\n"); 25462306a36Sopenharmony_ci hif_drv = (struct host_if_drv *)priv->hif_drv; 25562306a36Sopenharmony_ci netdev_dbg(dev, "Host = %p\n", hif_drv); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci w = vif->iftype; 25862306a36Sopenharmony_ci cpu_to_le32s(&w); 25962306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 1, WID_SET_OPERATION_MODE, (u8 *)&w, 4, 26062306a36Sopenharmony_ci 0, 0)) 26162306a36Sopenharmony_ci goto fail; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci b = WILC_FW_BSS_TYPE_INFRA; 26462306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_BSS_TYPE, &b, 1, 0, 0)) 26562306a36Sopenharmony_ci goto fail; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci b = WILC_FW_TX_RATE_AUTO; 26862306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_CURRENT_TX_RATE, &b, 1, 0, 0)) 26962306a36Sopenharmony_ci goto fail; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci b = WILC_FW_OPER_MODE_G_MIXED_11B_2; 27262306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_11G_OPERATING_MODE, &b, 1, 0, 0)) 27362306a36Sopenharmony_ci goto fail; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci b = WILC_FW_PREAMBLE_SHORT; 27662306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_PREAMBLE, &b, 1, 0, 0)) 27762306a36Sopenharmony_ci goto fail; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci b = WILC_FW_11N_PROT_AUTO; 28062306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_11N_PROT_MECH, &b, 1, 0, 0)) 28162306a36Sopenharmony_ci goto fail; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci b = WILC_FW_ACTIVE_SCAN; 28462306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_SCAN_TYPE, &b, 1, 0, 0)) 28562306a36Sopenharmony_ci goto fail; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci b = WILC_FW_SITE_SURVEY_OFF; 28862306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_SITE_SURVEY, &b, 1, 0, 0)) 28962306a36Sopenharmony_ci goto fail; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci hw = 0xffff; 29262306a36Sopenharmony_ci cpu_to_le16s(&hw); 29362306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_RTS_THRESHOLD, (u8 *)&hw, 2, 0, 0)) 29462306a36Sopenharmony_ci goto fail; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci hw = 2346; 29762306a36Sopenharmony_ci cpu_to_le16s(&hw); 29862306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_FRAG_THRESHOLD, (u8 *)&hw, 2, 0, 0)) 29962306a36Sopenharmony_ci goto fail; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci b = 0; 30262306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_BCAST_SSID, &b, 1, 0, 0)) 30362306a36Sopenharmony_ci goto fail; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci b = 1; 30662306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_QOS_ENABLE, &b, 1, 0, 0)) 30762306a36Sopenharmony_ci goto fail; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci b = WILC_FW_NO_POWERSAVE; 31062306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_POWER_MANAGEMENT, &b, 1, 0, 0)) 31162306a36Sopenharmony_ci goto fail; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci b = WILC_FW_SEC_NO; 31462306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_11I_MODE, &b, 1, 0, 0)) 31562306a36Sopenharmony_ci goto fail; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci b = WILC_FW_AUTH_OPEN_SYSTEM; 31862306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_AUTH_TYPE, &b, 1, 0, 0)) 31962306a36Sopenharmony_ci goto fail; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci b = 3; 32262306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_LISTEN_INTERVAL, &b, 1, 0, 0)) 32362306a36Sopenharmony_ci goto fail; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci b = 3; 32662306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_DTIM_PERIOD, &b, 1, 0, 0)) 32762306a36Sopenharmony_ci goto fail; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci b = WILC_FW_ACK_POLICY_NORMAL; 33062306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_ACK_POLICY, &b, 1, 0, 0)) 33162306a36Sopenharmony_ci goto fail; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci b = 0; 33462306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_USER_CONTROL_ON_TX_POWER, &b, 1, 33562306a36Sopenharmony_ci 0, 0)) 33662306a36Sopenharmony_ci goto fail; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci b = 48; 33962306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_TX_POWER_LEVEL_11A, &b, 1, 0, 0)) 34062306a36Sopenharmony_ci goto fail; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci b = 28; 34362306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_TX_POWER_LEVEL_11B, &b, 1, 0, 0)) 34462306a36Sopenharmony_ci goto fail; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci hw = 100; 34762306a36Sopenharmony_ci cpu_to_le16s(&hw); 34862306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_BEACON_INTERVAL, (u8 *)&hw, 2, 0, 0)) 34962306a36Sopenharmony_ci goto fail; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci b = WILC_FW_REKEY_POLICY_DISABLE; 35262306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_REKEY_POLICY, &b, 1, 0, 0)) 35362306a36Sopenharmony_ci goto fail; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci w = 84600; 35662306a36Sopenharmony_ci cpu_to_le32s(&w); 35762306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_REKEY_PERIOD, (u8 *)&w, 4, 0, 0)) 35862306a36Sopenharmony_ci goto fail; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci w = 500; 36162306a36Sopenharmony_ci cpu_to_le32s(&w); 36262306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_REKEY_PACKET_COUNT, (u8 *)&w, 4, 0, 36362306a36Sopenharmony_ci 0)) 36462306a36Sopenharmony_ci goto fail; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci b = 1; 36762306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_SHORT_SLOT_ALLOWED, &b, 1, 0, 36862306a36Sopenharmony_ci 0)) 36962306a36Sopenharmony_ci goto fail; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci b = WILC_FW_ERP_PROT_SELF_CTS; 37262306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_11N_ERP_PROT_TYPE, &b, 1, 0, 0)) 37362306a36Sopenharmony_ci goto fail; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci b = 1; 37662306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_11N_ENABLE, &b, 1, 0, 0)) 37762306a36Sopenharmony_ci goto fail; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci b = WILC_FW_11N_OP_MODE_HT_MIXED; 38062306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_11N_OPERATING_MODE, &b, 1, 0, 0)) 38162306a36Sopenharmony_ci goto fail; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci b = 1; 38462306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_11N_TXOP_PROT_DISABLE, &b, 1, 0, 0)) 38562306a36Sopenharmony_ci goto fail; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci b = WILC_FW_OBBS_NONHT_DETECT_PROTECT_REPORT; 38862306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_11N_OBSS_NONHT_DETECTION, &b, 1, 38962306a36Sopenharmony_ci 0, 0)) 39062306a36Sopenharmony_ci goto fail; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci b = WILC_FW_HT_PROT_RTS_CTS_NONHT; 39362306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_11N_HT_PROT_TYPE, &b, 1, 0, 0)) 39462306a36Sopenharmony_ci goto fail; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci b = 0; 39762306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_11N_RIFS_PROT_ENABLE, &b, 1, 0, 39862306a36Sopenharmony_ci 0)) 39962306a36Sopenharmony_ci goto fail; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci b = 7; 40262306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_11N_CURRENT_TX_MCS, &b, 1, 0, 0)) 40362306a36Sopenharmony_ci goto fail; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci b = 1; 40662306a36Sopenharmony_ci if (!wilc_wlan_cfg_set(vif, 0, WID_11N_IMMEDIATE_BA_ENABLED, &b, 1, 40762306a36Sopenharmony_ci 1, 1)) 40862306a36Sopenharmony_ci goto fail; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci return 0; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cifail: 41362306a36Sopenharmony_ci return -EINVAL; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic void wlan_deinitialize_threads(struct net_device *dev) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci struct wilc_vif *vif = netdev_priv(dev); 41962306a36Sopenharmony_ci struct wilc *wl = vif->wilc; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci wl->close = 1; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci complete(&wl->txq_event); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (wl->txq_thread) { 42662306a36Sopenharmony_ci kthread_stop(wl->txq_thread); 42762306a36Sopenharmony_ci wl->txq_thread = NULL; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic void wilc_wlan_deinitialize(struct net_device *dev) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci struct wilc_vif *vif = netdev_priv(dev); 43462306a36Sopenharmony_ci struct wilc *wl = vif->wilc; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (!wl) { 43762306a36Sopenharmony_ci netdev_err(dev, "wl is NULL\n"); 43862306a36Sopenharmony_ci return; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (wl->initialized) { 44262306a36Sopenharmony_ci netdev_info(dev, "Deinitializing wilc1000...\n"); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (!wl->dev_irq_num && 44562306a36Sopenharmony_ci wl->hif_func->disable_interrupt) { 44662306a36Sopenharmony_ci mutex_lock(&wl->hif_cs); 44762306a36Sopenharmony_ci wl->hif_func->disable_interrupt(wl); 44862306a36Sopenharmony_ci mutex_unlock(&wl->hif_cs); 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci complete(&wl->txq_event); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci wlan_deinitialize_threads(dev); 45362306a36Sopenharmony_ci deinit_irq(dev); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci wilc_wlan_stop(wl, vif); 45662306a36Sopenharmony_ci wilc_wlan_cleanup(dev); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci wl->initialized = false; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci netdev_dbg(dev, "wilc1000 deinitialization Done\n"); 46162306a36Sopenharmony_ci } else { 46262306a36Sopenharmony_ci netdev_dbg(dev, "wilc1000 is not initialized\n"); 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic int wlan_initialize_threads(struct net_device *dev) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci struct wilc_vif *vif = netdev_priv(dev); 46962306a36Sopenharmony_ci struct wilc *wilc = vif->wilc; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci wilc->txq_thread = kthread_run(wilc_txq_task, (void *)wilc, 47262306a36Sopenharmony_ci "%s-tx", dev->name); 47362306a36Sopenharmony_ci if (IS_ERR(wilc->txq_thread)) { 47462306a36Sopenharmony_ci netdev_err(dev, "couldn't create TXQ thread\n"); 47562306a36Sopenharmony_ci wilc->close = 1; 47662306a36Sopenharmony_ci return PTR_ERR(wilc->txq_thread); 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci wait_for_completion(&wilc->txq_thread_started); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci return 0; 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic int wilc_wlan_initialize(struct net_device *dev, struct wilc_vif *vif) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci int ret = 0; 48662306a36Sopenharmony_ci struct wilc *wl = vif->wilc; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (!wl->initialized) { 48962306a36Sopenharmony_ci wl->mac_status = WILC_MAC_STATUS_INIT; 49062306a36Sopenharmony_ci wl->close = 0; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci ret = wilc_wlan_init(dev); 49362306a36Sopenharmony_ci if (ret) 49462306a36Sopenharmony_ci return ret; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci ret = wlan_initialize_threads(dev); 49762306a36Sopenharmony_ci if (ret) 49862306a36Sopenharmony_ci goto fail_wilc_wlan; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (wl->dev_irq_num && init_irq(dev)) { 50162306a36Sopenharmony_ci ret = -EIO; 50262306a36Sopenharmony_ci goto fail_threads; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (!wl->dev_irq_num && 50662306a36Sopenharmony_ci wl->hif_func->enable_interrupt && 50762306a36Sopenharmony_ci wl->hif_func->enable_interrupt(wl)) { 50862306a36Sopenharmony_ci ret = -EIO; 50962306a36Sopenharmony_ci goto fail_irq_init; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci ret = wilc_wlan_get_firmware(dev); 51362306a36Sopenharmony_ci if (ret) 51462306a36Sopenharmony_ci goto fail_irq_enable; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci ret = wilc1000_firmware_download(dev); 51762306a36Sopenharmony_ci if (ret) 51862306a36Sopenharmony_ci goto fail_irq_enable; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci ret = wilc_start_firmware(dev); 52162306a36Sopenharmony_ci if (ret) 52262306a36Sopenharmony_ci goto fail_irq_enable; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (wilc_wlan_cfg_get(vif, 1, WID_FIRMWARE_VERSION, 1, 0)) { 52562306a36Sopenharmony_ci int size; 52662306a36Sopenharmony_ci char firmware_ver[WILC_MAX_FW_VERSION_STR_SIZE]; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci size = wilc_wlan_cfg_get_val(wl, WID_FIRMWARE_VERSION, 52962306a36Sopenharmony_ci firmware_ver, 53062306a36Sopenharmony_ci sizeof(firmware_ver)); 53162306a36Sopenharmony_ci firmware_ver[size] = '\0'; 53262306a36Sopenharmony_ci netdev_dbg(dev, "Firmware Ver = %s\n", firmware_ver); 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci ret = wilc_init_fw_config(dev, vif); 53662306a36Sopenharmony_ci if (ret) { 53762306a36Sopenharmony_ci netdev_err(dev, "Failed to configure firmware\n"); 53862306a36Sopenharmony_ci goto fail_fw_start; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci wl->initialized = true; 54162306a36Sopenharmony_ci return 0; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cifail_fw_start: 54462306a36Sopenharmony_ci wilc_wlan_stop(wl, vif); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cifail_irq_enable: 54762306a36Sopenharmony_ci if (!wl->dev_irq_num && 54862306a36Sopenharmony_ci wl->hif_func->disable_interrupt) 54962306a36Sopenharmony_ci wl->hif_func->disable_interrupt(wl); 55062306a36Sopenharmony_cifail_irq_init: 55162306a36Sopenharmony_ci if (wl->dev_irq_num) 55262306a36Sopenharmony_ci deinit_irq(dev); 55362306a36Sopenharmony_cifail_threads: 55462306a36Sopenharmony_ci wlan_deinitialize_threads(dev); 55562306a36Sopenharmony_cifail_wilc_wlan: 55662306a36Sopenharmony_ci wilc_wlan_cleanup(dev); 55762306a36Sopenharmony_ci netdev_err(dev, "WLAN initialization FAILED\n"); 55862306a36Sopenharmony_ci } else { 55962306a36Sopenharmony_ci netdev_dbg(dev, "wilc1000 already initialized\n"); 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci return ret; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic int mac_init_fn(struct net_device *ndev) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci netif_start_queue(ndev); 56762306a36Sopenharmony_ci netif_stop_queue(ndev); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci return 0; 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic int wilc_mac_open(struct net_device *ndev) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci struct wilc_vif *vif = netdev_priv(ndev); 57562306a36Sopenharmony_ci struct wilc *wl = vif->wilc; 57662306a36Sopenharmony_ci int ret = 0; 57762306a36Sopenharmony_ci struct mgmt_frame_regs mgmt_regs = {}; 57862306a36Sopenharmony_ci u8 addr[ETH_ALEN] __aligned(2); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (!wl || !wl->dev) { 58162306a36Sopenharmony_ci netdev_err(ndev, "device not ready\n"); 58262306a36Sopenharmony_ci return -ENODEV; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci netdev_dbg(ndev, "MAC OPEN[%p]\n", ndev); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci ret = wilc_init_host_int(ndev); 58862306a36Sopenharmony_ci if (ret) 58962306a36Sopenharmony_ci return ret; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci ret = wilc_wlan_initialize(ndev, vif); 59262306a36Sopenharmony_ci if (ret) { 59362306a36Sopenharmony_ci wilc_deinit_host_int(ndev); 59462306a36Sopenharmony_ci return ret; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci wilc_set_operation_mode(vif, wilc_get_vif_idx(vif), vif->iftype, 59862306a36Sopenharmony_ci vif->idx); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if (is_valid_ether_addr(ndev->dev_addr)) { 60162306a36Sopenharmony_ci ether_addr_copy(addr, ndev->dev_addr); 60262306a36Sopenharmony_ci wilc_set_mac_address(vif, addr); 60362306a36Sopenharmony_ci } else { 60462306a36Sopenharmony_ci wilc_get_mac_address(vif, addr); 60562306a36Sopenharmony_ci eth_hw_addr_set(ndev, addr); 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci netdev_dbg(ndev, "Mac address: %pM\n", ndev->dev_addr); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (!is_valid_ether_addr(ndev->dev_addr)) { 61062306a36Sopenharmony_ci netdev_err(ndev, "Wrong MAC address\n"); 61162306a36Sopenharmony_ci wilc_deinit_host_int(ndev); 61262306a36Sopenharmony_ci wilc_wlan_deinitialize(ndev); 61362306a36Sopenharmony_ci return -EINVAL; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci mgmt_regs.interface_stypes = vif->mgmt_reg_stypes; 61762306a36Sopenharmony_ci /* so we detect a change */ 61862306a36Sopenharmony_ci vif->mgmt_reg_stypes = 0; 61962306a36Sopenharmony_ci wilc_update_mgmt_frame_registrations(vif->ndev->ieee80211_ptr->wiphy, 62062306a36Sopenharmony_ci vif->ndev->ieee80211_ptr, 62162306a36Sopenharmony_ci &mgmt_regs); 62262306a36Sopenharmony_ci netif_wake_queue(ndev); 62362306a36Sopenharmony_ci wl->open_ifcs++; 62462306a36Sopenharmony_ci vif->mac_opened = 1; 62562306a36Sopenharmony_ci return 0; 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic struct net_device_stats *mac_stats(struct net_device *dev) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci struct wilc_vif *vif = netdev_priv(dev); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci return &vif->netstats; 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic int wilc_set_mac_addr(struct net_device *dev, void *p) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci int result; 63862306a36Sopenharmony_ci struct wilc_vif *vif = netdev_priv(dev); 63962306a36Sopenharmony_ci struct wilc *wilc = vif->wilc; 64062306a36Sopenharmony_ci struct sockaddr *addr = (struct sockaddr *)p; 64162306a36Sopenharmony_ci unsigned char mac_addr[ETH_ALEN]; 64262306a36Sopenharmony_ci struct wilc_vif *tmp_vif; 64362306a36Sopenharmony_ci int srcu_idx; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 64662306a36Sopenharmony_ci return -EADDRNOTAVAIL; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (!vif->mac_opened) { 64962306a36Sopenharmony_ci eth_commit_mac_addr_change(dev, p); 65062306a36Sopenharmony_ci return 0; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci /* Verify MAC Address is not already in use: */ 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci srcu_idx = srcu_read_lock(&wilc->srcu); 65662306a36Sopenharmony_ci list_for_each_entry_rcu(tmp_vif, &wilc->vif_list, list) { 65762306a36Sopenharmony_ci wilc_get_mac_address(tmp_vif, mac_addr); 65862306a36Sopenharmony_ci if (ether_addr_equal(addr->sa_data, mac_addr)) { 65962306a36Sopenharmony_ci if (vif != tmp_vif) { 66062306a36Sopenharmony_ci srcu_read_unlock(&wilc->srcu, srcu_idx); 66162306a36Sopenharmony_ci return -EADDRNOTAVAIL; 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci srcu_read_unlock(&wilc->srcu, srcu_idx); 66462306a36Sopenharmony_ci return 0; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci srcu_read_unlock(&wilc->srcu, srcu_idx); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci result = wilc_set_mac_address(vif, (u8 *)addr->sa_data); 67062306a36Sopenharmony_ci if (result) 67162306a36Sopenharmony_ci return result; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci eth_commit_mac_addr_change(dev, p); 67462306a36Sopenharmony_ci return result; 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic void wilc_set_multicast_list(struct net_device *dev) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci struct netdev_hw_addr *ha; 68062306a36Sopenharmony_ci struct wilc_vif *vif = netdev_priv(dev); 68162306a36Sopenharmony_ci int i; 68262306a36Sopenharmony_ci u8 *mc_list; 68362306a36Sopenharmony_ci u8 *cur_mc; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if (dev->flags & IFF_PROMISC) 68662306a36Sopenharmony_ci return; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci if (dev->flags & IFF_ALLMULTI || 68962306a36Sopenharmony_ci dev->mc.count > WILC_MULTICAST_TABLE_SIZE) { 69062306a36Sopenharmony_ci wilc_setup_multicast_filter(vif, 0, 0, NULL); 69162306a36Sopenharmony_ci return; 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci if (dev->mc.count == 0) { 69562306a36Sopenharmony_ci wilc_setup_multicast_filter(vif, 1, 0, NULL); 69662306a36Sopenharmony_ci return; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci mc_list = kmalloc_array(dev->mc.count, ETH_ALEN, GFP_ATOMIC); 70062306a36Sopenharmony_ci if (!mc_list) 70162306a36Sopenharmony_ci return; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci cur_mc = mc_list; 70462306a36Sopenharmony_ci i = 0; 70562306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 70662306a36Sopenharmony_ci memcpy(cur_mc, ha->addr, ETH_ALEN); 70762306a36Sopenharmony_ci netdev_dbg(dev, "Entry[%d]: %pM\n", i, cur_mc); 70862306a36Sopenharmony_ci i++; 70962306a36Sopenharmony_ci cur_mc += ETH_ALEN; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci if (wilc_setup_multicast_filter(vif, 1, dev->mc.count, mc_list)) 71362306a36Sopenharmony_ci kfree(mc_list); 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic void wilc_tx_complete(void *priv, int status) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci struct tx_complete_data *pv_data = priv; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci dev_kfree_skb(pv_data->skb); 72162306a36Sopenharmony_ci kfree(pv_data); 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cinetdev_tx_t wilc_mac_xmit(struct sk_buff *skb, struct net_device *ndev) 72562306a36Sopenharmony_ci{ 72662306a36Sopenharmony_ci struct wilc_vif *vif = netdev_priv(ndev); 72762306a36Sopenharmony_ci struct wilc *wilc = vif->wilc; 72862306a36Sopenharmony_ci struct tx_complete_data *tx_data = NULL; 72962306a36Sopenharmony_ci int queue_count; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci if (skb->dev != ndev) { 73262306a36Sopenharmony_ci netdev_err(ndev, "Packet not destined to this device\n"); 73362306a36Sopenharmony_ci dev_kfree_skb(skb); 73462306a36Sopenharmony_ci return NETDEV_TX_OK; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci tx_data = kmalloc(sizeof(*tx_data), GFP_ATOMIC); 73862306a36Sopenharmony_ci if (!tx_data) { 73962306a36Sopenharmony_ci dev_kfree_skb(skb); 74062306a36Sopenharmony_ci netif_wake_queue(ndev); 74162306a36Sopenharmony_ci return NETDEV_TX_OK; 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci tx_data->buff = skb->data; 74562306a36Sopenharmony_ci tx_data->size = skb->len; 74662306a36Sopenharmony_ci tx_data->skb = skb; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci vif->netstats.tx_packets++; 74962306a36Sopenharmony_ci vif->netstats.tx_bytes += tx_data->size; 75062306a36Sopenharmony_ci queue_count = wilc_wlan_txq_add_net_pkt(ndev, tx_data, 75162306a36Sopenharmony_ci tx_data->buff, tx_data->size, 75262306a36Sopenharmony_ci wilc_tx_complete); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci if (queue_count > FLOW_CONTROL_UPPER_THRESHOLD) { 75562306a36Sopenharmony_ci int srcu_idx; 75662306a36Sopenharmony_ci struct wilc_vif *vif; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci srcu_idx = srcu_read_lock(&wilc->srcu); 75962306a36Sopenharmony_ci list_for_each_entry_rcu(vif, &wilc->vif_list, list) { 76062306a36Sopenharmony_ci if (vif->mac_opened) 76162306a36Sopenharmony_ci netif_stop_queue(vif->ndev); 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci srcu_read_unlock(&wilc->srcu, srcu_idx); 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci return NETDEV_TX_OK; 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cistatic int wilc_mac_close(struct net_device *ndev) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci struct wilc_vif *vif = netdev_priv(ndev); 77262306a36Sopenharmony_ci struct wilc *wl = vif->wilc; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci netdev_dbg(ndev, "Mac close\n"); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (wl->open_ifcs > 0) 77762306a36Sopenharmony_ci wl->open_ifcs--; 77862306a36Sopenharmony_ci else 77962306a36Sopenharmony_ci return 0; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci if (vif->ndev) { 78262306a36Sopenharmony_ci netif_stop_queue(vif->ndev); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci wilc_handle_disconnect(vif); 78562306a36Sopenharmony_ci wilc_deinit_host_int(vif->ndev); 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (wl->open_ifcs == 0) { 78962306a36Sopenharmony_ci netdev_dbg(ndev, "Deinitializing wilc1000\n"); 79062306a36Sopenharmony_ci wl->close = 1; 79162306a36Sopenharmony_ci wilc_wlan_deinitialize(ndev); 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci vif->mac_opened = 0; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci return 0; 79762306a36Sopenharmony_ci} 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_civoid wilc_frmw_to_host(struct wilc *wilc, u8 *buff, u32 size, 80062306a36Sopenharmony_ci u32 pkt_offset) 80162306a36Sopenharmony_ci{ 80262306a36Sopenharmony_ci unsigned int frame_len = 0; 80362306a36Sopenharmony_ci int stats; 80462306a36Sopenharmony_ci unsigned char *buff_to_send = NULL; 80562306a36Sopenharmony_ci struct sk_buff *skb; 80662306a36Sopenharmony_ci struct net_device *wilc_netdev; 80762306a36Sopenharmony_ci struct wilc_vif *vif; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci if (!wilc) 81062306a36Sopenharmony_ci return; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci wilc_netdev = get_if_handler(wilc, buff); 81362306a36Sopenharmony_ci if (!wilc_netdev) 81462306a36Sopenharmony_ci return; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci buff += pkt_offset; 81762306a36Sopenharmony_ci vif = netdev_priv(wilc_netdev); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci if (size > 0) { 82062306a36Sopenharmony_ci frame_len = size; 82162306a36Sopenharmony_ci buff_to_send = buff; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci skb = dev_alloc_skb(frame_len); 82462306a36Sopenharmony_ci if (!skb) 82562306a36Sopenharmony_ci return; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci skb->dev = wilc_netdev; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci skb_put_data(skb, buff_to_send, frame_len); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, wilc_netdev); 83262306a36Sopenharmony_ci vif->netstats.rx_packets++; 83362306a36Sopenharmony_ci vif->netstats.rx_bytes += frame_len; 83462306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 83562306a36Sopenharmony_ci stats = netif_rx(skb); 83662306a36Sopenharmony_ci netdev_dbg(wilc_netdev, "netif_rx ret value is: %d\n", stats); 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci} 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_civoid wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size, bool is_auth) 84162306a36Sopenharmony_ci{ 84262306a36Sopenharmony_ci int srcu_idx; 84362306a36Sopenharmony_ci struct wilc_vif *vif; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci srcu_idx = srcu_read_lock(&wilc->srcu); 84662306a36Sopenharmony_ci list_for_each_entry_rcu(vif, &wilc->vif_list, list) { 84762306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buff; 84862306a36Sopenharmony_ci u16 type = le16_to_cpup((__le16 *)buff); 84962306a36Sopenharmony_ci u32 type_bit = BIT(type >> 4); 85062306a36Sopenharmony_ci u32 auth_bit = BIT(IEEE80211_STYPE_AUTH >> 4); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci if ((vif->mgmt_reg_stypes & auth_bit && 85362306a36Sopenharmony_ci ieee80211_is_auth(mgmt->frame_control)) && 85462306a36Sopenharmony_ci vif->iftype == WILC_STATION_MODE && is_auth) { 85562306a36Sopenharmony_ci wilc_wfi_mgmt_frame_rx(vif, buff, size); 85662306a36Sopenharmony_ci break; 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci if (vif->priv.p2p_listen_state && 86062306a36Sopenharmony_ci vif->mgmt_reg_stypes & type_bit) 86162306a36Sopenharmony_ci wilc_wfi_p2p_rx(vif, buff, size); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci if (vif->monitor_flag) 86462306a36Sopenharmony_ci wilc_wfi_monitor_rx(wilc->monitor_dev, buff, size); 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ci srcu_read_unlock(&wilc->srcu, srcu_idx); 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_cistatic const struct net_device_ops wilc_netdev_ops = { 87062306a36Sopenharmony_ci .ndo_init = mac_init_fn, 87162306a36Sopenharmony_ci .ndo_open = wilc_mac_open, 87262306a36Sopenharmony_ci .ndo_stop = wilc_mac_close, 87362306a36Sopenharmony_ci .ndo_set_mac_address = wilc_set_mac_addr, 87462306a36Sopenharmony_ci .ndo_start_xmit = wilc_mac_xmit, 87562306a36Sopenharmony_ci .ndo_get_stats = mac_stats, 87662306a36Sopenharmony_ci .ndo_set_rx_mode = wilc_set_multicast_list, 87762306a36Sopenharmony_ci}; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_civoid wilc_netdev_cleanup(struct wilc *wilc) 88062306a36Sopenharmony_ci{ 88162306a36Sopenharmony_ci struct wilc_vif *vif, *vif_tmp; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (!wilc) 88462306a36Sopenharmony_ci return; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci if (wilc->firmware) { 88762306a36Sopenharmony_ci release_firmware(wilc->firmware); 88862306a36Sopenharmony_ci wilc->firmware = NULL; 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci list_for_each_entry_safe(vif, vif_tmp, &wilc->vif_list, list) { 89262306a36Sopenharmony_ci mutex_lock(&wilc->vif_mutex); 89362306a36Sopenharmony_ci list_del_rcu(&vif->list); 89462306a36Sopenharmony_ci wilc->vif_num--; 89562306a36Sopenharmony_ci mutex_unlock(&wilc->vif_mutex); 89662306a36Sopenharmony_ci synchronize_srcu(&wilc->srcu); 89762306a36Sopenharmony_ci if (vif->ndev) 89862306a36Sopenharmony_ci unregister_netdev(vif->ndev); 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci wilc_wfi_deinit_mon_interface(wilc, false); 90262306a36Sopenharmony_ci destroy_workqueue(wilc->hif_workqueue); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci wilc_wlan_cfg_deinit(wilc); 90562306a36Sopenharmony_ci wlan_deinit_locks(wilc); 90662306a36Sopenharmony_ci wiphy_unregister(wilc->wiphy); 90762306a36Sopenharmony_ci wiphy_free(wilc->wiphy); 90862306a36Sopenharmony_ci} 90962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wilc_netdev_cleanup); 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cistatic u8 wilc_get_available_idx(struct wilc *wl) 91262306a36Sopenharmony_ci{ 91362306a36Sopenharmony_ci int idx = 0; 91462306a36Sopenharmony_ci struct wilc_vif *vif; 91562306a36Sopenharmony_ci int srcu_idx; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci srcu_idx = srcu_read_lock(&wl->srcu); 91862306a36Sopenharmony_ci list_for_each_entry_rcu(vif, &wl->vif_list, list) { 91962306a36Sopenharmony_ci if (vif->idx == 0) 92062306a36Sopenharmony_ci idx = 1; 92162306a36Sopenharmony_ci else 92262306a36Sopenharmony_ci idx = 0; 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci srcu_read_unlock(&wl->srcu, srcu_idx); 92562306a36Sopenharmony_ci return idx; 92662306a36Sopenharmony_ci} 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_cistruct wilc_vif *wilc_netdev_ifc_init(struct wilc *wl, const char *name, 92962306a36Sopenharmony_ci int vif_type, enum nl80211_iftype type, 93062306a36Sopenharmony_ci bool rtnl_locked) 93162306a36Sopenharmony_ci{ 93262306a36Sopenharmony_ci struct net_device *ndev; 93362306a36Sopenharmony_ci struct wilc_vif *vif; 93462306a36Sopenharmony_ci int ret; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci ndev = alloc_etherdev(sizeof(*vif)); 93762306a36Sopenharmony_ci if (!ndev) 93862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci vif = netdev_priv(ndev); 94162306a36Sopenharmony_ci ndev->ieee80211_ptr = &vif->priv.wdev; 94262306a36Sopenharmony_ci strcpy(ndev->name, name); 94362306a36Sopenharmony_ci vif->wilc = wl; 94462306a36Sopenharmony_ci vif->ndev = ndev; 94562306a36Sopenharmony_ci ndev->ml_priv = vif; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci ndev->netdev_ops = &wilc_netdev_ops; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci SET_NETDEV_DEV(ndev, wiphy_dev(wl->wiphy)); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci vif->priv.wdev.wiphy = wl->wiphy; 95262306a36Sopenharmony_ci vif->priv.wdev.netdev = ndev; 95362306a36Sopenharmony_ci vif->priv.wdev.iftype = type; 95462306a36Sopenharmony_ci vif->priv.dev = ndev; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci if (rtnl_locked) 95762306a36Sopenharmony_ci ret = cfg80211_register_netdevice(ndev); 95862306a36Sopenharmony_ci else 95962306a36Sopenharmony_ci ret = register_netdev(ndev); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci if (ret) { 96262306a36Sopenharmony_ci ret = -EFAULT; 96362306a36Sopenharmony_ci goto error; 96462306a36Sopenharmony_ci } 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci ndev->needs_free_netdev = true; 96762306a36Sopenharmony_ci vif->iftype = vif_type; 96862306a36Sopenharmony_ci vif->idx = wilc_get_available_idx(wl); 96962306a36Sopenharmony_ci vif->mac_opened = 0; 97062306a36Sopenharmony_ci mutex_lock(&wl->vif_mutex); 97162306a36Sopenharmony_ci list_add_tail_rcu(&vif->list, &wl->vif_list); 97262306a36Sopenharmony_ci wl->vif_num += 1; 97362306a36Sopenharmony_ci mutex_unlock(&wl->vif_mutex); 97462306a36Sopenharmony_ci synchronize_srcu(&wl->srcu); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci return vif; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_cierror: 97962306a36Sopenharmony_ci if (rtnl_locked) 98062306a36Sopenharmony_ci cfg80211_unregister_netdevice(ndev); 98162306a36Sopenharmony_ci else 98262306a36Sopenharmony_ci unregister_netdev(ndev); 98362306a36Sopenharmony_ci free_netdev(ndev); 98462306a36Sopenharmony_ci return ERR_PTR(ret); 98562306a36Sopenharmony_ci} 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 98862306a36Sopenharmony_ciMODULE_FIRMWARE(WILC1000_FW(WILC1000_API_VER)); 989