15625f965SAjay Singh // SPDX-License-Identifier: GPL-2.0
25625f965SAjay Singh /*
35625f965SAjay Singh * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
45625f965SAjay Singh * All rights reserved.
55625f965SAjay Singh */
65625f965SAjay Singh
75625f965SAjay Singh #include <linux/irq.h>
85625f965SAjay Singh #include <linux/kthread.h>
95625f965SAjay Singh #include <linux/firmware.h>
105625f965SAjay Singh #include <linux/netdevice.h>
115625f965SAjay Singh #include <linux/inetdevice.h>
125625f965SAjay Singh
135625f965SAjay Singh #include "cfg80211.h"
145625f965SAjay Singh #include "wlan_cfg.h"
155625f965SAjay Singh
165625f965SAjay Singh #define WILC_MULTICAST_TABLE_SIZE 8
174ee8a915SAjay Singh #define WILC_MAX_FW_VERSION_STR_SIZE 50
185625f965SAjay Singh
19b52b331aSAjay Singh /* latest API version supported */
20b52b331aSAjay Singh #define WILC1000_API_VER 1
21b52b331aSAjay Singh
22b52b331aSAjay Singh #define WILC1000_FW_PREFIX "atmel/wilc1000_wifi_firmware-"
23b52b331aSAjay Singh #define __WILC1000_FW(api) WILC1000_FW_PREFIX #api ".bin"
24b52b331aSAjay Singh #define WILC1000_FW(api) __WILC1000_FW(api)
25b52b331aSAjay Singh
isr_uh_routine(int irq,void * user_data)265625f965SAjay Singh static irqreturn_t isr_uh_routine(int irq, void *user_data)
275625f965SAjay Singh {
2850773696SAjay Singh struct wilc *wilc = user_data;
295625f965SAjay Singh
305625f965SAjay Singh if (wilc->close) {
315ae66064SDavid Mosberger-Tang pr_err("Can't handle UH interrupt\n");
325625f965SAjay Singh return IRQ_HANDLED;
335625f965SAjay Singh }
345625f965SAjay Singh return IRQ_WAKE_THREAD;
355625f965SAjay Singh }
365625f965SAjay Singh
isr_bh_routine(int irq,void * userdata)375625f965SAjay Singh static irqreturn_t isr_bh_routine(int irq, void *userdata)
385625f965SAjay Singh {
3950773696SAjay Singh struct wilc *wilc = userdata;
405625f965SAjay Singh
415625f965SAjay Singh if (wilc->close) {
4250773696SAjay Singh pr_err("Can't handle BH interrupt\n");
435625f965SAjay Singh return IRQ_HANDLED;
445625f965SAjay Singh }
455625f965SAjay Singh
465625f965SAjay Singh wilc_handle_isr(wilc);
475625f965SAjay Singh
485625f965SAjay Singh return IRQ_HANDLED;
495625f965SAjay Singh }
505625f965SAjay Singh
init_irq(struct net_device * dev)515625f965SAjay Singh static int init_irq(struct net_device *dev)
525625f965SAjay Singh {
535625f965SAjay Singh struct wilc_vif *vif = netdev_priv(dev);
545625f965SAjay Singh struct wilc *wl = vif->wilc;
555625f965SAjay Singh int ret;
565625f965SAjay Singh
575625f965SAjay Singh ret = request_threaded_irq(wl->dev_irq_num, isr_uh_routine,
585625f965SAjay Singh isr_bh_routine,
595625f965SAjay Singh IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
6030e08bc0SDavid Mosberger-Tang dev->name, wl);
615625f965SAjay Singh if (ret) {
625625f965SAjay Singh netdev_err(dev, "Failed to request IRQ [%d]\n", ret);
635625f965SAjay Singh return ret;
645625f965SAjay Singh }
655625f965SAjay Singh netdev_dbg(dev, "IRQ request succeeded IRQ-NUM= %d\n", wl->dev_irq_num);
665625f965SAjay Singh
675625f965SAjay Singh return 0;
685625f965SAjay Singh }
695625f965SAjay Singh
deinit_irq(struct net_device * dev)705625f965SAjay Singh static void deinit_irq(struct net_device *dev)
715625f965SAjay Singh {
725625f965SAjay Singh struct wilc_vif *vif = netdev_priv(dev);
735625f965SAjay Singh struct wilc *wilc = vif->wilc;
745625f965SAjay Singh
755625f965SAjay Singh /* Deinitialize IRQ */
765625f965SAjay Singh if (wilc->dev_irq_num)
775625f965SAjay Singh free_irq(wilc->dev_irq_num, wilc);
785625f965SAjay Singh }
795625f965SAjay Singh
wilc_mac_indicate(struct wilc * wilc)805625f965SAjay Singh void wilc_mac_indicate(struct wilc *wilc)
815625f965SAjay Singh {
825625f965SAjay Singh s8 status;
835625f965SAjay Singh
845625f965SAjay Singh wilc_wlan_cfg_get_val(wilc, WID_STATUS, &status, 1);
855625f965SAjay Singh if (wilc->mac_status == WILC_MAC_STATUS_INIT) {
865625f965SAjay Singh wilc->mac_status = status;
875625f965SAjay Singh complete(&wilc->sync_event);
885625f965SAjay Singh } else {
895625f965SAjay Singh wilc->mac_status = status;
905625f965SAjay Singh }
915625f965SAjay Singh }
925625f965SAjay Singh
get_if_handler(struct wilc * wilc,u8 * mac_header)935625f965SAjay Singh static struct net_device *get_if_handler(struct wilc *wilc, u8 *mac_header)
945625f965SAjay Singh {
955625f965SAjay Singh struct net_device *ndev = NULL;
965625f965SAjay Singh struct wilc_vif *vif;
975625f965SAjay Singh struct ieee80211_hdr *h = (struct ieee80211_hdr *)mac_header;
985625f965SAjay Singh
995625f965SAjay Singh list_for_each_entry_rcu(vif, &wilc->vif_list, list) {
1004c274214SAjay Singh if (vif->iftype == WILC_STATION_MODE)
1015625f965SAjay Singh if (ether_addr_equal_unaligned(h->addr2, vif->bssid)) {
1025625f965SAjay Singh ndev = vif->ndev;
1035625f965SAjay Singh goto out;
1045625f965SAjay Singh }
1054c274214SAjay Singh if (vif->iftype == WILC_AP_MODE)
1065625f965SAjay Singh if (ether_addr_equal_unaligned(h->addr1, vif->bssid)) {
1075625f965SAjay Singh ndev = vif->ndev;
1085625f965SAjay Singh goto out;
1095625f965SAjay Singh }
1105625f965SAjay Singh }
1115625f965SAjay Singh out:
1125625f965SAjay Singh return ndev;
1135625f965SAjay Singh }
1145625f965SAjay Singh
wilc_wlan_set_bssid(struct net_device * wilc_netdev,const u8 * bssid,u8 mode)1150341ae70SJakub Kicinski void wilc_wlan_set_bssid(struct net_device *wilc_netdev, const u8 *bssid,
1160341ae70SJakub Kicinski u8 mode)
1175625f965SAjay Singh {
1185625f965SAjay Singh struct wilc_vif *vif = netdev_priv(wilc_netdev);
1195625f965SAjay Singh
1205625f965SAjay Singh if (bssid)
1215625f965SAjay Singh ether_addr_copy(vif->bssid, bssid);
1225625f965SAjay Singh else
1235625f965SAjay Singh eth_zero_addr(vif->bssid);
1245625f965SAjay Singh
1254c274214SAjay Singh vif->iftype = mode;
1265625f965SAjay Singh }
1275625f965SAjay Singh
wilc_wlan_get_num_conn_ifcs(struct wilc * wilc)1285625f965SAjay Singh int wilc_wlan_get_num_conn_ifcs(struct wilc *wilc)
1295625f965SAjay Singh {
1305625f965SAjay Singh int srcu_idx;
1315625f965SAjay Singh u8 ret_val = 0;
1325625f965SAjay Singh struct wilc_vif *vif;
1335625f965SAjay Singh
1345625f965SAjay Singh srcu_idx = srcu_read_lock(&wilc->srcu);
1355625f965SAjay Singh list_for_each_entry_rcu(vif, &wilc->vif_list, list) {
1365625f965SAjay Singh if (!is_zero_ether_addr(vif->bssid))
1375625f965SAjay Singh ret_val++;
1385625f965SAjay Singh }
1395625f965SAjay Singh srcu_read_unlock(&wilc->srcu, srcu_idx);
1405625f965SAjay Singh return ret_val;
1415625f965SAjay Singh }
1425625f965SAjay Singh
wilc_txq_task(void * vp)1435625f965SAjay Singh static int wilc_txq_task(void *vp)
1445625f965SAjay Singh {
1455625f965SAjay Singh int ret;
1465625f965SAjay Singh u32 txq_count;
1475625f965SAjay Singh struct wilc *wl = vp;
1485625f965SAjay Singh
1495625f965SAjay Singh complete(&wl->txq_thread_started);
1505625f965SAjay Singh while (1) {
1515625f965SAjay Singh wait_for_completion(&wl->txq_event);
1525625f965SAjay Singh
1535625f965SAjay Singh if (wl->close) {
1545625f965SAjay Singh complete(&wl->txq_thread_started);
1555625f965SAjay Singh
1565625f965SAjay Singh while (!kthread_should_stop())
1575625f965SAjay Singh schedule();
1585625f965SAjay Singh break;
1595625f965SAjay Singh }
1605625f965SAjay Singh do {
1615625f965SAjay Singh ret = wilc_wlan_handle_txq(wl, &txq_count);
1625625f965SAjay Singh if (txq_count < FLOW_CONTROL_LOWER_THRESHOLD) {
1635625f965SAjay Singh int srcu_idx;
1645625f965SAjay Singh struct wilc_vif *ifc;
1655625f965SAjay Singh
1665625f965SAjay Singh srcu_idx = srcu_read_lock(&wl->srcu);
1675625f965SAjay Singh list_for_each_entry_rcu(ifc, &wl->vif_list,
1685625f965SAjay Singh list) {
1695625f965SAjay Singh if (ifc->mac_opened && ifc->ndev)
1705625f965SAjay Singh netif_wake_queue(ifc->ndev);
1715625f965SAjay Singh }
1725625f965SAjay Singh srcu_read_unlock(&wl->srcu, srcu_idx);
1735625f965SAjay Singh }
1745625f965SAjay Singh } while (ret == WILC_VMM_ENTRY_FULL_RETRY && !wl->close);
1755625f965SAjay Singh }
1765625f965SAjay Singh return 0;
1775625f965SAjay Singh }
1785625f965SAjay Singh
wilc_wlan_get_firmware(struct net_device * dev)1795625f965SAjay Singh static int wilc_wlan_get_firmware(struct net_device *dev)
1805625f965SAjay Singh {
1815625f965SAjay Singh struct wilc_vif *vif = netdev_priv(dev);
1825625f965SAjay Singh struct wilc *wilc = vif->wilc;
1835625f965SAjay Singh int chip_id;
1840b3dd675SAjay Singh const struct firmware *wilc_fw;
185b52b331aSAjay Singh int ret;
1865625f965SAjay Singh
1875625f965SAjay Singh chip_id = wilc_get_chipid(wilc, false);
1885625f965SAjay Singh
1890b3dd675SAjay Singh netdev_info(dev, "ChipID [%x] loading firmware [%s]\n", chip_id,
190b52b331aSAjay Singh WILC1000_FW(WILC1000_API_VER));
1915625f965SAjay Singh
192b52b331aSAjay Singh ret = request_firmware(&wilc_fw, WILC1000_FW(WILC1000_API_VER),
193b52b331aSAjay Singh wilc->dev);
194b52b331aSAjay Singh if (ret != 0) {
1950b3dd675SAjay Singh netdev_err(dev, "%s - firmware not available\n",
196b52b331aSAjay Singh WILC1000_FW(WILC1000_API_VER));
1975625f965SAjay Singh return -EINVAL;
1985625f965SAjay Singh }
1990b3dd675SAjay Singh wilc->firmware = wilc_fw;
2005625f965SAjay Singh
2015625f965SAjay Singh return 0;
2025625f965SAjay Singh }
2035625f965SAjay Singh
wilc_start_firmware(struct net_device * dev)2045625f965SAjay Singh static int wilc_start_firmware(struct net_device *dev)
2055625f965SAjay Singh {
2065625f965SAjay Singh struct wilc_vif *vif = netdev_priv(dev);
2075625f965SAjay Singh struct wilc *wilc = vif->wilc;
2085625f965SAjay Singh int ret = 0;
2095625f965SAjay Singh
2105625f965SAjay Singh ret = wilc_wlan_start(wilc);
2115625f965SAjay Singh if (ret)
2125625f965SAjay Singh return ret;
2135625f965SAjay Singh
2145625f965SAjay Singh if (!wait_for_completion_timeout(&wilc->sync_event,
2155625f965SAjay Singh msecs_to_jiffies(5000)))
2165625f965SAjay Singh return -ETIME;
2175625f965SAjay Singh
2185625f965SAjay Singh return 0;
2195625f965SAjay Singh }
2205625f965SAjay Singh
wilc1000_firmware_download(struct net_device * dev)2215625f965SAjay Singh static int wilc1000_firmware_download(struct net_device *dev)
2225625f965SAjay Singh {
2235625f965SAjay Singh struct wilc_vif *vif = netdev_priv(dev);
2245625f965SAjay Singh struct wilc *wilc = vif->wilc;
2255625f965SAjay Singh int ret = 0;
2265625f965SAjay Singh
2275625f965SAjay Singh if (!wilc->firmware) {
2285625f965SAjay Singh netdev_err(dev, "Firmware buffer is NULL\n");
2295625f965SAjay Singh return -ENOBUFS;
2305625f965SAjay Singh }
2315625f965SAjay Singh
2325625f965SAjay Singh ret = wilc_wlan_firmware_download(wilc, wilc->firmware->data,
2335625f965SAjay Singh wilc->firmware->size);
2345625f965SAjay Singh if (ret)
2355625f965SAjay Singh return ret;
2365625f965SAjay Singh
2375625f965SAjay Singh release_firmware(wilc->firmware);
2385625f965SAjay Singh wilc->firmware = NULL;
2395625f965SAjay Singh
2405625f965SAjay Singh netdev_dbg(dev, "Download Succeeded\n");
2415625f965SAjay Singh
2425625f965SAjay Singh return 0;
2435625f965SAjay Singh }
2445625f965SAjay Singh
wilc_init_fw_config(struct net_device * dev,struct wilc_vif * vif)2455625f965SAjay Singh static int wilc_init_fw_config(struct net_device *dev, struct wilc_vif *vif)
2465625f965SAjay Singh {
2475625f965SAjay Singh struct wilc_priv *priv = &vif->priv;
2485625f965SAjay Singh struct host_if_drv *hif_drv;
2495625f965SAjay Singh u8 b;
2505625f965SAjay Singh u16 hw;
2515625f965SAjay Singh u32 w;
2525625f965SAjay Singh
2535625f965SAjay Singh netdev_dbg(dev, "Start configuring Firmware\n");
2545625f965SAjay Singh hif_drv = (struct host_if_drv *)priv->hif_drv;
2555625f965SAjay Singh netdev_dbg(dev, "Host = %p\n", hif_drv);
2565625f965SAjay Singh
2575625f965SAjay Singh w = vif->iftype;
2585625f965SAjay Singh cpu_to_le32s(&w);
2595625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 1, WID_SET_OPERATION_MODE, (u8 *)&w, 4,
2605625f965SAjay Singh 0, 0))
2615625f965SAjay Singh goto fail;
2625625f965SAjay Singh
2635625f965SAjay Singh b = WILC_FW_BSS_TYPE_INFRA;
2645625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_BSS_TYPE, &b, 1, 0, 0))
2655625f965SAjay Singh goto fail;
2665625f965SAjay Singh
2675625f965SAjay Singh b = WILC_FW_TX_RATE_AUTO;
2685625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_CURRENT_TX_RATE, &b, 1, 0, 0))
2695625f965SAjay Singh goto fail;
2705625f965SAjay Singh
2715625f965SAjay Singh b = WILC_FW_OPER_MODE_G_MIXED_11B_2;
2725625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_11G_OPERATING_MODE, &b, 1, 0, 0))
2735625f965SAjay Singh goto fail;
2745625f965SAjay Singh
2755625f965SAjay Singh b = WILC_FW_PREAMBLE_SHORT;
2765625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_PREAMBLE, &b, 1, 0, 0))
2775625f965SAjay Singh goto fail;
2785625f965SAjay Singh
2795625f965SAjay Singh b = WILC_FW_11N_PROT_AUTO;
2805625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_11N_PROT_MECH, &b, 1, 0, 0))
2815625f965SAjay Singh goto fail;
2825625f965SAjay Singh
2835625f965SAjay Singh b = WILC_FW_ACTIVE_SCAN;
2845625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_SCAN_TYPE, &b, 1, 0, 0))
2855625f965SAjay Singh goto fail;
2865625f965SAjay Singh
2875625f965SAjay Singh b = WILC_FW_SITE_SURVEY_OFF;
2885625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_SITE_SURVEY, &b, 1, 0, 0))
2895625f965SAjay Singh goto fail;
2905625f965SAjay Singh
2915625f965SAjay Singh hw = 0xffff;
2925625f965SAjay Singh cpu_to_le16s(&hw);
2935625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_RTS_THRESHOLD, (u8 *)&hw, 2, 0, 0))
2945625f965SAjay Singh goto fail;
2955625f965SAjay Singh
2965625f965SAjay Singh hw = 2346;
2975625f965SAjay Singh cpu_to_le16s(&hw);
2985625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_FRAG_THRESHOLD, (u8 *)&hw, 2, 0, 0))
2995625f965SAjay Singh goto fail;
3005625f965SAjay Singh
3015625f965SAjay Singh b = 0;
3025625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_BCAST_SSID, &b, 1, 0, 0))
3035625f965SAjay Singh goto fail;
3045625f965SAjay Singh
3055625f965SAjay Singh b = 1;
3065625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_QOS_ENABLE, &b, 1, 0, 0))
3075625f965SAjay Singh goto fail;
3085625f965SAjay Singh
3095625f965SAjay Singh b = WILC_FW_NO_POWERSAVE;
3105625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_POWER_MANAGEMENT, &b, 1, 0, 0))
3115625f965SAjay Singh goto fail;
3125625f965SAjay Singh
3135625f965SAjay Singh b = WILC_FW_SEC_NO;
3145625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_11I_MODE, &b, 1, 0, 0))
3155625f965SAjay Singh goto fail;
3165625f965SAjay Singh
3175625f965SAjay Singh b = WILC_FW_AUTH_OPEN_SYSTEM;
3185625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_AUTH_TYPE, &b, 1, 0, 0))
3195625f965SAjay Singh goto fail;
3205625f965SAjay Singh
3215625f965SAjay Singh b = 3;
3225625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_LISTEN_INTERVAL, &b, 1, 0, 0))
3235625f965SAjay Singh goto fail;
3245625f965SAjay Singh
3255625f965SAjay Singh b = 3;
3265625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_DTIM_PERIOD, &b, 1, 0, 0))
3275625f965SAjay Singh goto fail;
3285625f965SAjay Singh
3295625f965SAjay Singh b = WILC_FW_ACK_POLICY_NORMAL;
3305625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_ACK_POLICY, &b, 1, 0, 0))
3315625f965SAjay Singh goto fail;
3325625f965SAjay Singh
3335625f965SAjay Singh b = 0;
3345625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_USER_CONTROL_ON_TX_POWER, &b, 1,
3355625f965SAjay Singh 0, 0))
3365625f965SAjay Singh goto fail;
3375625f965SAjay Singh
3385625f965SAjay Singh b = 48;
3395625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_TX_POWER_LEVEL_11A, &b, 1, 0, 0))
3405625f965SAjay Singh goto fail;
3415625f965SAjay Singh
3425625f965SAjay Singh b = 28;
3435625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_TX_POWER_LEVEL_11B, &b, 1, 0, 0))
3445625f965SAjay Singh goto fail;
3455625f965SAjay Singh
3465625f965SAjay Singh hw = 100;
3475625f965SAjay Singh cpu_to_le16s(&hw);
3485625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_BEACON_INTERVAL, (u8 *)&hw, 2, 0, 0))
3495625f965SAjay Singh goto fail;
3505625f965SAjay Singh
3515625f965SAjay Singh b = WILC_FW_REKEY_POLICY_DISABLE;
3525625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_REKEY_POLICY, &b, 1, 0, 0))
3535625f965SAjay Singh goto fail;
3545625f965SAjay Singh
3555625f965SAjay Singh w = 84600;
3565625f965SAjay Singh cpu_to_le32s(&w);
3575625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_REKEY_PERIOD, (u8 *)&w, 4, 0, 0))
3585625f965SAjay Singh goto fail;
3595625f965SAjay Singh
3605625f965SAjay Singh w = 500;
3615625f965SAjay Singh cpu_to_le32s(&w);
3625625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_REKEY_PACKET_COUNT, (u8 *)&w, 4, 0,
3635625f965SAjay Singh 0))
3645625f965SAjay Singh goto fail;
3655625f965SAjay Singh
3665625f965SAjay Singh b = 1;
3675625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_SHORT_SLOT_ALLOWED, &b, 1, 0,
3685625f965SAjay Singh 0))
3695625f965SAjay Singh goto fail;
3705625f965SAjay Singh
3715625f965SAjay Singh b = WILC_FW_ERP_PROT_SELF_CTS;
3725625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_11N_ERP_PROT_TYPE, &b, 1, 0, 0))
3735625f965SAjay Singh goto fail;
3745625f965SAjay Singh
3755625f965SAjay Singh b = 1;
3765625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_11N_ENABLE, &b, 1, 0, 0))
3775625f965SAjay Singh goto fail;
3785625f965SAjay Singh
3795625f965SAjay Singh b = WILC_FW_11N_OP_MODE_HT_MIXED;
3805625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_11N_OPERATING_MODE, &b, 1, 0, 0))
3815625f965SAjay Singh goto fail;
3825625f965SAjay Singh
3835625f965SAjay Singh b = 1;
3845625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_11N_TXOP_PROT_DISABLE, &b, 1, 0, 0))
3855625f965SAjay Singh goto fail;
3865625f965SAjay Singh
3875625f965SAjay Singh b = WILC_FW_OBBS_NONHT_DETECT_PROTECT_REPORT;
3885625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_11N_OBSS_NONHT_DETECTION, &b, 1,
3895625f965SAjay Singh 0, 0))
3905625f965SAjay Singh goto fail;
3915625f965SAjay Singh
3925625f965SAjay Singh b = WILC_FW_HT_PROT_RTS_CTS_NONHT;
3935625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_11N_HT_PROT_TYPE, &b, 1, 0, 0))
3945625f965SAjay Singh goto fail;
3955625f965SAjay Singh
3965625f965SAjay Singh b = 0;
3975625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_11N_RIFS_PROT_ENABLE, &b, 1, 0,
3985625f965SAjay Singh 0))
3995625f965SAjay Singh goto fail;
4005625f965SAjay Singh
4015625f965SAjay Singh b = 7;
4025625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_11N_CURRENT_TX_MCS, &b, 1, 0, 0))
4035625f965SAjay Singh goto fail;
4045625f965SAjay Singh
4055625f965SAjay Singh b = 1;
4065625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_11N_IMMEDIATE_BA_ENABLED, &b, 1,
4075625f965SAjay Singh 1, 1))
4085625f965SAjay Singh goto fail;
4095625f965SAjay Singh
4105625f965SAjay Singh return 0;
4115625f965SAjay Singh
4125625f965SAjay Singh fail:
4135625f965SAjay Singh return -EINVAL;
4145625f965SAjay Singh }
4155625f965SAjay Singh
wlan_deinitialize_threads(struct net_device * dev)4165625f965SAjay Singh static void wlan_deinitialize_threads(struct net_device *dev)
4175625f965SAjay Singh {
4185625f965SAjay Singh struct wilc_vif *vif = netdev_priv(dev);
4195625f965SAjay Singh struct wilc *wl = vif->wilc;
4205625f965SAjay Singh
4215625f965SAjay Singh wl->close = 1;
4225625f965SAjay Singh
4235625f965SAjay Singh complete(&wl->txq_event);
4245625f965SAjay Singh
4255625f965SAjay Singh if (wl->txq_thread) {
4265625f965SAjay Singh kthread_stop(wl->txq_thread);
4275625f965SAjay Singh wl->txq_thread = NULL;
4285625f965SAjay Singh }
4295625f965SAjay Singh }
4305625f965SAjay Singh
wilc_wlan_deinitialize(struct net_device * dev)4315625f965SAjay Singh static void wilc_wlan_deinitialize(struct net_device *dev)
4325625f965SAjay Singh {
4335625f965SAjay Singh struct wilc_vif *vif = netdev_priv(dev);
4345625f965SAjay Singh struct wilc *wl = vif->wilc;
4355625f965SAjay Singh
4365625f965SAjay Singh if (!wl) {
4375625f965SAjay Singh netdev_err(dev, "wl is NULL\n");
4385625f965SAjay Singh return;
4395625f965SAjay Singh }
4405625f965SAjay Singh
4415625f965SAjay Singh if (wl->initialized) {
4425625f965SAjay Singh netdev_info(dev, "Deinitializing wilc1000...\n");
4435625f965SAjay Singh
4445625f965SAjay Singh if (!wl->dev_irq_num &&
4455625f965SAjay Singh wl->hif_func->disable_interrupt) {
4465625f965SAjay Singh mutex_lock(&wl->hif_cs);
4475625f965SAjay Singh wl->hif_func->disable_interrupt(wl);
4485625f965SAjay Singh mutex_unlock(&wl->hif_cs);
4495625f965SAjay Singh }
4505625f965SAjay Singh complete(&wl->txq_event);
4515625f965SAjay Singh
4525625f965SAjay Singh wlan_deinitialize_threads(dev);
4535625f965SAjay Singh deinit_irq(dev);
4545625f965SAjay Singh
4555625f965SAjay Singh wilc_wlan_stop(wl, vif);
4565625f965SAjay Singh wilc_wlan_cleanup(dev);
4575625f965SAjay Singh
4585625f965SAjay Singh wl->initialized = false;
4595625f965SAjay Singh
4605625f965SAjay Singh netdev_dbg(dev, "wilc1000 deinitialization Done\n");
4615625f965SAjay Singh } else {
4625625f965SAjay Singh netdev_dbg(dev, "wilc1000 is not initialized\n");
4635625f965SAjay Singh }
4645625f965SAjay Singh }
4655625f965SAjay Singh
wlan_initialize_threads(struct net_device * dev)4665625f965SAjay Singh static int wlan_initialize_threads(struct net_device *dev)
4675625f965SAjay Singh {
4685625f965SAjay Singh struct wilc_vif *vif = netdev_priv(dev);
4695625f965SAjay Singh struct wilc *wilc = vif->wilc;
4705625f965SAjay Singh
4715625f965SAjay Singh wilc->txq_thread = kthread_run(wilc_txq_task, (void *)wilc,
4723cc23932SDavid Mosberger-Tang "%s-tx", dev->name);
4735625f965SAjay Singh if (IS_ERR(wilc->txq_thread)) {
4745625f965SAjay Singh netdev_err(dev, "couldn't create TXQ thread\n");
475f589b5d9SAjay Singh wilc->close = 1;
4765625f965SAjay Singh return PTR_ERR(wilc->txq_thread);
4775625f965SAjay Singh }
4785625f965SAjay Singh wait_for_completion(&wilc->txq_thread_started);
4795625f965SAjay Singh
4805625f965SAjay Singh return 0;
4815625f965SAjay Singh }
4825625f965SAjay Singh
wilc_wlan_initialize(struct net_device * dev,struct wilc_vif * vif)4835625f965SAjay Singh static int wilc_wlan_initialize(struct net_device *dev, struct wilc_vif *vif)
4845625f965SAjay Singh {
4855625f965SAjay Singh int ret = 0;
4865625f965SAjay Singh struct wilc *wl = vif->wilc;
4875625f965SAjay Singh
4885625f965SAjay Singh if (!wl->initialized) {
4895625f965SAjay Singh wl->mac_status = WILC_MAC_STATUS_INIT;
4905625f965SAjay Singh wl->close = 0;
4915625f965SAjay Singh
4925625f965SAjay Singh ret = wilc_wlan_init(dev);
4935625f965SAjay Singh if (ret)
4945625f965SAjay Singh return ret;
4955625f965SAjay Singh
4965625f965SAjay Singh ret = wlan_initialize_threads(dev);
4975625f965SAjay Singh if (ret)
4985625f965SAjay Singh goto fail_wilc_wlan;
4995625f965SAjay Singh
5005625f965SAjay Singh if (wl->dev_irq_num && init_irq(dev)) {
5015625f965SAjay Singh ret = -EIO;
5025625f965SAjay Singh goto fail_threads;
5035625f965SAjay Singh }
5045625f965SAjay Singh
5055625f965SAjay Singh if (!wl->dev_irq_num &&
5065625f965SAjay Singh wl->hif_func->enable_interrupt &&
5075625f965SAjay Singh wl->hif_func->enable_interrupt(wl)) {
5085625f965SAjay Singh ret = -EIO;
5095625f965SAjay Singh goto fail_irq_init;
5105625f965SAjay Singh }
5115625f965SAjay Singh
5125625f965SAjay Singh ret = wilc_wlan_get_firmware(dev);
5135625f965SAjay Singh if (ret)
5145625f965SAjay Singh goto fail_irq_enable;
5155625f965SAjay Singh
5165625f965SAjay Singh ret = wilc1000_firmware_download(dev);
5175625f965SAjay Singh if (ret)
5185625f965SAjay Singh goto fail_irq_enable;
5195625f965SAjay Singh
5205625f965SAjay Singh ret = wilc_start_firmware(dev);
5215625f965SAjay Singh if (ret)
5225625f965SAjay Singh goto fail_irq_enable;
5235625f965SAjay Singh
5245625f965SAjay Singh if (wilc_wlan_cfg_get(vif, 1, WID_FIRMWARE_VERSION, 1, 0)) {
5255625f965SAjay Singh int size;
5264ee8a915SAjay Singh char firmware_ver[WILC_MAX_FW_VERSION_STR_SIZE];
5275625f965SAjay Singh
5285625f965SAjay Singh size = wilc_wlan_cfg_get_val(wl, WID_FIRMWARE_VERSION,
5295625f965SAjay Singh firmware_ver,
5305625f965SAjay Singh sizeof(firmware_ver));
5315625f965SAjay Singh firmware_ver[size] = '\0';
5325625f965SAjay Singh netdev_dbg(dev, "Firmware Ver = %s\n", firmware_ver);
5335625f965SAjay Singh }
5345625f965SAjay Singh
5355625f965SAjay Singh ret = wilc_init_fw_config(dev, vif);
5365625f965SAjay Singh if (ret) {
5375625f965SAjay Singh netdev_err(dev, "Failed to configure firmware\n");
5385625f965SAjay Singh goto fail_fw_start;
5395625f965SAjay Singh }
5405625f965SAjay Singh wl->initialized = true;
5415625f965SAjay Singh return 0;
5425625f965SAjay Singh
5435625f965SAjay Singh fail_fw_start:
5445625f965SAjay Singh wilc_wlan_stop(wl, vif);
5455625f965SAjay Singh
5465625f965SAjay Singh fail_irq_enable:
5475625f965SAjay Singh if (!wl->dev_irq_num &&
5485625f965SAjay Singh wl->hif_func->disable_interrupt)
5495625f965SAjay Singh wl->hif_func->disable_interrupt(wl);
5505625f965SAjay Singh fail_irq_init:
5515625f965SAjay Singh if (wl->dev_irq_num)
5525625f965SAjay Singh deinit_irq(dev);
5535625f965SAjay Singh fail_threads:
5545625f965SAjay Singh wlan_deinitialize_threads(dev);
5555625f965SAjay Singh fail_wilc_wlan:
5565625f965SAjay Singh wilc_wlan_cleanup(dev);
5575625f965SAjay Singh netdev_err(dev, "WLAN initialization FAILED\n");
5585625f965SAjay Singh } else {
5595625f965SAjay Singh netdev_dbg(dev, "wilc1000 already initialized\n");
5605625f965SAjay Singh }
5615625f965SAjay Singh return ret;
5625625f965SAjay Singh }
5635625f965SAjay Singh
mac_init_fn(struct net_device * ndev)5645625f965SAjay Singh static int mac_init_fn(struct net_device *ndev)
5655625f965SAjay Singh {
5665625f965SAjay Singh netif_start_queue(ndev);
5675625f965SAjay Singh netif_stop_queue(ndev);
5685625f965SAjay Singh
5695625f965SAjay Singh return 0;
5705625f965SAjay Singh }
5715625f965SAjay Singh
wilc_mac_open(struct net_device * ndev)5725625f965SAjay Singh static int wilc_mac_open(struct net_device *ndev)
5735625f965SAjay Singh {
5745625f965SAjay Singh struct wilc_vif *vif = netdev_priv(ndev);
5755625f965SAjay Singh struct wilc *wl = vif->wilc;
5765625f965SAjay Singh int ret = 0;
5775625f965SAjay Singh struct mgmt_frame_regs mgmt_regs = {};
578b09d5802SJakub Kicinski u8 addr[ETH_ALEN] __aligned(2);
5795625f965SAjay Singh
5805625f965SAjay Singh if (!wl || !wl->dev) {
5815625f965SAjay Singh netdev_err(ndev, "device not ready\n");
5825625f965SAjay Singh return -ENODEV;
5835625f965SAjay Singh }
5845625f965SAjay Singh
5855625f965SAjay Singh netdev_dbg(ndev, "MAC OPEN[%p]\n", ndev);
5865625f965SAjay Singh
5875625f965SAjay Singh ret = wilc_init_host_int(ndev);
5885625f965SAjay Singh if (ret)
5895625f965SAjay Singh return ret;
5905625f965SAjay Singh
5915625f965SAjay Singh ret = wilc_wlan_initialize(ndev, vif);
5925625f965SAjay Singh if (ret) {
5935625f965SAjay Singh wilc_deinit_host_int(ndev);
5945625f965SAjay Singh return ret;
5955625f965SAjay Singh }
5965625f965SAjay Singh
5975625f965SAjay Singh wilc_set_operation_mode(vif, wilc_get_vif_idx(vif), vif->iftype,
5985625f965SAjay Singh vif->idx);
599a381b78aSDavid Mosberger-Tang
6000341ae70SJakub Kicinski if (is_valid_ether_addr(ndev->dev_addr)) {
601b09d5802SJakub Kicinski ether_addr_copy(addr, ndev->dev_addr);
602b09d5802SJakub Kicinski wilc_set_mac_address(vif, addr);
6030341ae70SJakub Kicinski } else {
6040341ae70SJakub Kicinski wilc_get_mac_address(vif, addr);
6050341ae70SJakub Kicinski eth_hw_addr_set(ndev, addr);
6060341ae70SJakub Kicinski }
607a381b78aSDavid Mosberger-Tang netdev_dbg(ndev, "Mac address: %pM\n", ndev->dev_addr);
6085625f965SAjay Singh
6095625f965SAjay Singh if (!is_valid_ether_addr(ndev->dev_addr)) {
6105625f965SAjay Singh netdev_err(ndev, "Wrong MAC address\n");
6115625f965SAjay Singh wilc_deinit_host_int(ndev);
6125625f965SAjay Singh wilc_wlan_deinitialize(ndev);
6135625f965SAjay Singh return -EINVAL;
6145625f965SAjay Singh }
6155625f965SAjay Singh
6165625f965SAjay Singh mgmt_regs.interface_stypes = vif->mgmt_reg_stypes;
6175625f965SAjay Singh /* so we detect a change */
6185625f965SAjay Singh vif->mgmt_reg_stypes = 0;
6195625f965SAjay Singh wilc_update_mgmt_frame_registrations(vif->ndev->ieee80211_ptr->wiphy,
6205625f965SAjay Singh vif->ndev->ieee80211_ptr,
6215625f965SAjay Singh &mgmt_regs);
6225625f965SAjay Singh netif_wake_queue(ndev);
6235625f965SAjay Singh wl->open_ifcs++;
6245625f965SAjay Singh vif->mac_opened = 1;
6255625f965SAjay Singh return 0;
6265625f965SAjay Singh }
6275625f965SAjay Singh
mac_stats(struct net_device * dev)6285625f965SAjay Singh static struct net_device_stats *mac_stats(struct net_device *dev)
6295625f965SAjay Singh {
6305625f965SAjay Singh struct wilc_vif *vif = netdev_priv(dev);
6315625f965SAjay Singh
6325625f965SAjay Singh return &vif->netstats;
6335625f965SAjay Singh }
6345625f965SAjay Singh
wilc_set_mac_addr(struct net_device * dev,void * p)635c04fabacSAjay Singh static int wilc_set_mac_addr(struct net_device *dev, void *p)
636c04fabacSAjay Singh {
637c04fabacSAjay Singh int result;
638c04fabacSAjay Singh struct wilc_vif *vif = netdev_priv(dev);
639c04fabacSAjay Singh struct wilc *wilc = vif->wilc;
640c04fabacSAjay Singh struct sockaddr *addr = (struct sockaddr *)p;
641c04fabacSAjay Singh unsigned char mac_addr[ETH_ALEN];
642c04fabacSAjay Singh struct wilc_vif *tmp_vif;
643c04fabacSAjay Singh int srcu_idx;
644c04fabacSAjay Singh
645c04fabacSAjay Singh if (!is_valid_ether_addr(addr->sa_data))
646a381b78aSDavid Mosberger-Tang return -EADDRNOTAVAIL;
647a381b78aSDavid Mosberger-Tang
648a381b78aSDavid Mosberger-Tang if (!vif->mac_opened) {
649a381b78aSDavid Mosberger-Tang eth_commit_mac_addr_change(dev, p);
650a381b78aSDavid Mosberger-Tang return 0;
651a381b78aSDavid Mosberger-Tang }
652a381b78aSDavid Mosberger-Tang
653a381b78aSDavid Mosberger-Tang /* Verify MAC Address is not already in use: */
654c04fabacSAjay Singh
655c04fabacSAjay Singh srcu_idx = srcu_read_lock(&wilc->srcu);
656c04fabacSAjay Singh list_for_each_entry_rcu(tmp_vif, &wilc->vif_list, list) {
657c04fabacSAjay Singh wilc_get_mac_address(tmp_vif, mac_addr);
658c04fabacSAjay Singh if (ether_addr_equal(addr->sa_data, mac_addr)) {
659c04fabacSAjay Singh if (vif != tmp_vif) {
660c04fabacSAjay Singh srcu_read_unlock(&wilc->srcu, srcu_idx);
661a381b78aSDavid Mosberger-Tang return -EADDRNOTAVAIL;
662c04fabacSAjay Singh }
663c04fabacSAjay Singh srcu_read_unlock(&wilc->srcu, srcu_idx);
664c04fabacSAjay Singh return 0;
665c04fabacSAjay Singh }
666c04fabacSAjay Singh }
667c04fabacSAjay Singh srcu_read_unlock(&wilc->srcu, srcu_idx);
668c04fabacSAjay Singh
669c04fabacSAjay Singh result = wilc_set_mac_address(vif, (u8 *)addr->sa_data);
670c04fabacSAjay Singh if (result)
671c04fabacSAjay Singh return result;
672c04fabacSAjay Singh
673a381b78aSDavid Mosberger-Tang eth_commit_mac_addr_change(dev, p);
674c04fabacSAjay Singh return result;
675c04fabacSAjay Singh }
676c04fabacSAjay Singh
wilc_set_multicast_list(struct net_device * dev)6775625f965SAjay Singh static void wilc_set_multicast_list(struct net_device *dev)
6785625f965SAjay Singh {
6795625f965SAjay Singh struct netdev_hw_addr *ha;
6805625f965SAjay Singh struct wilc_vif *vif = netdev_priv(dev);
6815625f965SAjay Singh int i;
6825625f965SAjay Singh u8 *mc_list;
6835625f965SAjay Singh u8 *cur_mc;
6845625f965SAjay Singh
6855625f965SAjay Singh if (dev->flags & IFF_PROMISC)
6865625f965SAjay Singh return;
6875625f965SAjay Singh
6885625f965SAjay Singh if (dev->flags & IFF_ALLMULTI ||
6895625f965SAjay Singh dev->mc.count > WILC_MULTICAST_TABLE_SIZE) {
6905625f965SAjay Singh wilc_setup_multicast_filter(vif, 0, 0, NULL);
6915625f965SAjay Singh return;
6925625f965SAjay Singh }
6935625f965SAjay Singh
6945625f965SAjay Singh if (dev->mc.count == 0) {
6955625f965SAjay Singh wilc_setup_multicast_filter(vif, 1, 0, NULL);
6965625f965SAjay Singh return;
6975625f965SAjay Singh }
6985625f965SAjay Singh
6995625f965SAjay Singh mc_list = kmalloc_array(dev->mc.count, ETH_ALEN, GFP_ATOMIC);
7005625f965SAjay Singh if (!mc_list)
7015625f965SAjay Singh return;
7025625f965SAjay Singh
7035625f965SAjay Singh cur_mc = mc_list;
7045625f965SAjay Singh i = 0;
7055625f965SAjay Singh netdev_for_each_mc_addr(ha, dev) {
7065625f965SAjay Singh memcpy(cur_mc, ha->addr, ETH_ALEN);
7075625f965SAjay Singh netdev_dbg(dev, "Entry[%d]: %pM\n", i, cur_mc);
7085625f965SAjay Singh i++;
7095625f965SAjay Singh cur_mc += ETH_ALEN;
7105625f965SAjay Singh }
7115625f965SAjay Singh
7125625f965SAjay Singh if (wilc_setup_multicast_filter(vif, 1, dev->mc.count, mc_list))
7135625f965SAjay Singh kfree(mc_list);
7145625f965SAjay Singh }
7155625f965SAjay Singh
wilc_tx_complete(void * priv,int status)7165625f965SAjay Singh static void wilc_tx_complete(void *priv, int status)
7175625f965SAjay Singh {
7185625f965SAjay Singh struct tx_complete_data *pv_data = priv;
7195625f965SAjay Singh
7205625f965SAjay Singh dev_kfree_skb(pv_data->skb);
7215625f965SAjay Singh kfree(pv_data);
7225625f965SAjay Singh }
7235625f965SAjay Singh
wilc_mac_xmit(struct sk_buff * skb,struct net_device * ndev)7245625f965SAjay Singh netdev_tx_t wilc_mac_xmit(struct sk_buff *skb, struct net_device *ndev)
7255625f965SAjay Singh {
7265625f965SAjay Singh struct wilc_vif *vif = netdev_priv(ndev);
7275625f965SAjay Singh struct wilc *wilc = vif->wilc;
7285625f965SAjay Singh struct tx_complete_data *tx_data = NULL;
7295625f965SAjay Singh int queue_count;
7305625f965SAjay Singh
7315625f965SAjay Singh if (skb->dev != ndev) {
7325625f965SAjay Singh netdev_err(ndev, "Packet not destined to this device\n");
733deb962ecSZhang Changzhong dev_kfree_skb(skb);
734cce0e083SLuc Van Oostenryck return NETDEV_TX_OK;
7355625f965SAjay Singh }
7365625f965SAjay Singh
7375625f965SAjay Singh tx_data = kmalloc(sizeof(*tx_data), GFP_ATOMIC);
7385625f965SAjay Singh if (!tx_data) {
7395625f965SAjay Singh dev_kfree_skb(skb);
7405625f965SAjay Singh netif_wake_queue(ndev);
741cce0e083SLuc Van Oostenryck return NETDEV_TX_OK;
7425625f965SAjay Singh }
7435625f965SAjay Singh
7445625f965SAjay Singh tx_data->buff = skb->data;
7455625f965SAjay Singh tx_data->size = skb->len;
7465625f965SAjay Singh tx_data->skb = skb;
7475625f965SAjay Singh
7485625f965SAjay Singh vif->netstats.tx_packets++;
7495625f965SAjay Singh vif->netstats.tx_bytes += tx_data->size;
7506fe91b69SVsevolod Kozlov queue_count = wilc_wlan_txq_add_net_pkt(ndev, tx_data,
7515625f965SAjay Singh tx_data->buff, tx_data->size,
7525625f965SAjay Singh wilc_tx_complete);
7535625f965SAjay Singh
7545625f965SAjay Singh if (queue_count > FLOW_CONTROL_UPPER_THRESHOLD) {
7555625f965SAjay Singh int srcu_idx;
7565625f965SAjay Singh struct wilc_vif *vif;
7575625f965SAjay Singh
7585625f965SAjay Singh srcu_idx = srcu_read_lock(&wilc->srcu);
7595625f965SAjay Singh list_for_each_entry_rcu(vif, &wilc->vif_list, list) {
7605625f965SAjay Singh if (vif->mac_opened)
7615625f965SAjay Singh netif_stop_queue(vif->ndev);
7625625f965SAjay Singh }
7635625f965SAjay Singh srcu_read_unlock(&wilc->srcu, srcu_idx);
7645625f965SAjay Singh }
7655625f965SAjay Singh
766cce0e083SLuc Van Oostenryck return NETDEV_TX_OK;
7675625f965SAjay Singh }
7685625f965SAjay Singh
wilc_mac_close(struct net_device * ndev)7695625f965SAjay Singh static int wilc_mac_close(struct net_device *ndev)
7705625f965SAjay Singh {
7715625f965SAjay Singh struct wilc_vif *vif = netdev_priv(ndev);
7725625f965SAjay Singh struct wilc *wl = vif->wilc;
7735625f965SAjay Singh
7745625f965SAjay Singh netdev_dbg(ndev, "Mac close\n");
7755625f965SAjay Singh
7765625f965SAjay Singh if (wl->open_ifcs > 0)
7775625f965SAjay Singh wl->open_ifcs--;
7785625f965SAjay Singh else
7795625f965SAjay Singh return 0;
7805625f965SAjay Singh
7815625f965SAjay Singh if (vif->ndev) {
7825625f965SAjay Singh netif_stop_queue(vif->ndev);
7835625f965SAjay Singh
784ad3e683aSAjay Singh wilc_handle_disconnect(vif);
7855625f965SAjay Singh wilc_deinit_host_int(vif->ndev);
7865625f965SAjay Singh }
7875625f965SAjay Singh
7885625f965SAjay Singh if (wl->open_ifcs == 0) {
7895625f965SAjay Singh netdev_dbg(ndev, "Deinitializing wilc1000\n");
7905625f965SAjay Singh wl->close = 1;
7915625f965SAjay Singh wilc_wlan_deinitialize(ndev);
7925625f965SAjay Singh }
7935625f965SAjay Singh
7945625f965SAjay Singh vif->mac_opened = 0;
7955625f965SAjay Singh
7965625f965SAjay Singh return 0;
7975625f965SAjay Singh }
7985625f965SAjay Singh
wilc_frmw_to_host(struct wilc * wilc,u8 * buff,u32 size,u32 pkt_offset)7995625f965SAjay Singh void wilc_frmw_to_host(struct wilc *wilc, u8 *buff, u32 size,
8005625f965SAjay Singh u32 pkt_offset)
8015625f965SAjay Singh {
8025625f965SAjay Singh unsigned int frame_len = 0;
8035625f965SAjay Singh int stats;
8045625f965SAjay Singh unsigned char *buff_to_send = NULL;
8055625f965SAjay Singh struct sk_buff *skb;
8065625f965SAjay Singh struct net_device *wilc_netdev;
8075625f965SAjay Singh struct wilc_vif *vif;
8085625f965SAjay Singh
8095625f965SAjay Singh if (!wilc)
8105625f965SAjay Singh return;
8115625f965SAjay Singh
8125625f965SAjay Singh wilc_netdev = get_if_handler(wilc, buff);
8135625f965SAjay Singh if (!wilc_netdev)
8145625f965SAjay Singh return;
8155625f965SAjay Singh
8165625f965SAjay Singh buff += pkt_offset;
8175625f965SAjay Singh vif = netdev_priv(wilc_netdev);
8185625f965SAjay Singh
8195625f965SAjay Singh if (size > 0) {
8205625f965SAjay Singh frame_len = size;
8215625f965SAjay Singh buff_to_send = buff;
8225625f965SAjay Singh
8235625f965SAjay Singh skb = dev_alloc_skb(frame_len);
8245625f965SAjay Singh if (!skb)
8255625f965SAjay Singh return;
8265625f965SAjay Singh
8275625f965SAjay Singh skb->dev = wilc_netdev;
8285625f965SAjay Singh
8295625f965SAjay Singh skb_put_data(skb, buff_to_send, frame_len);
8305625f965SAjay Singh
8315625f965SAjay Singh skb->protocol = eth_type_trans(skb, wilc_netdev);
8325625f965SAjay Singh vif->netstats.rx_packets++;
8335625f965SAjay Singh vif->netstats.rx_bytes += frame_len;
8345625f965SAjay Singh skb->ip_summed = CHECKSUM_UNNECESSARY;
8355625f965SAjay Singh stats = netif_rx(skb);
8365625f965SAjay Singh netdev_dbg(wilc_netdev, "netif_rx ret value is: %d\n", stats);
8375625f965SAjay Singh }
8385625f965SAjay Singh }
8395625f965SAjay Singh
wilc_wfi_mgmt_rx(struct wilc * wilc,u8 * buff,u32 size,bool is_auth)840c5b331d4SAjay Singh void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size, bool is_auth)
8415625f965SAjay Singh {
8425625f965SAjay Singh int srcu_idx;
8435625f965SAjay Singh struct wilc_vif *vif;
8445625f965SAjay Singh
8455625f965SAjay Singh srcu_idx = srcu_read_lock(&wilc->srcu);
8465625f965SAjay Singh list_for_each_entry_rcu(vif, &wilc->vif_list, list) {
847c5b331d4SAjay Singh struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buff;
8485625f965SAjay Singh u16 type = le16_to_cpup((__le16 *)buff);
8495625f965SAjay Singh u32 type_bit = BIT(type >> 4);
850c5b331d4SAjay Singh u32 auth_bit = BIT(IEEE80211_STYPE_AUTH >> 4);
851c5b331d4SAjay Singh
852c5b331d4SAjay Singh if ((vif->mgmt_reg_stypes & auth_bit &&
853c5b331d4SAjay Singh ieee80211_is_auth(mgmt->frame_control)) &&
854c5b331d4SAjay Singh vif->iftype == WILC_STATION_MODE && is_auth) {
855c5b331d4SAjay Singh wilc_wfi_mgmt_frame_rx(vif, buff, size);
856c5b331d4SAjay Singh break;
857c5b331d4SAjay Singh }
8585625f965SAjay Singh
8595625f965SAjay Singh if (vif->priv.p2p_listen_state &&
8605625f965SAjay Singh vif->mgmt_reg_stypes & type_bit)
8615625f965SAjay Singh wilc_wfi_p2p_rx(vif, buff, size);
8625625f965SAjay Singh
8635625f965SAjay Singh if (vif->monitor_flag)
8645625f965SAjay Singh wilc_wfi_monitor_rx(wilc->monitor_dev, buff, size);
8655625f965SAjay Singh }
8665625f965SAjay Singh srcu_read_unlock(&wilc->srcu, srcu_idx);
8675625f965SAjay Singh }
8685625f965SAjay Singh
8695625f965SAjay Singh static const struct net_device_ops wilc_netdev_ops = {
8705625f965SAjay Singh .ndo_init = mac_init_fn,
8715625f965SAjay Singh .ndo_open = wilc_mac_open,
8725625f965SAjay Singh .ndo_stop = wilc_mac_close,
873c04fabacSAjay Singh .ndo_set_mac_address = wilc_set_mac_addr,
8745625f965SAjay Singh .ndo_start_xmit = wilc_mac_xmit,
8755625f965SAjay Singh .ndo_get_stats = mac_stats,
8765625f965SAjay Singh .ndo_set_rx_mode = wilc_set_multicast_list,
8775625f965SAjay Singh };
8785625f965SAjay Singh
wilc_netdev_cleanup(struct wilc * wilc)8795625f965SAjay Singh void wilc_netdev_cleanup(struct wilc *wilc)
8805625f965SAjay Singh {
881*3da9d32bSAlexis Lothoré struct wilc_vif *vif, *vif_tmp;
8825625f965SAjay Singh
8835625f965SAjay Singh if (!wilc)
8845625f965SAjay Singh return;
8855625f965SAjay Singh
8865625f965SAjay Singh if (wilc->firmware) {
8875625f965SAjay Singh release_firmware(wilc->firmware);
8885625f965SAjay Singh wilc->firmware = NULL;
8895625f965SAjay Singh }
8905625f965SAjay Singh
891*3da9d32bSAlexis Lothoré list_for_each_entry_safe(vif, vif_tmp, &wilc->vif_list, list) {
8925625f965SAjay Singh mutex_lock(&wilc->vif_mutex);
8935625f965SAjay Singh list_del_rcu(&vif->list);
8945625f965SAjay Singh wilc->vif_num--;
8955625f965SAjay Singh mutex_unlock(&wilc->vif_mutex);
8965625f965SAjay Singh synchronize_srcu(&wilc->srcu);
897*3da9d32bSAlexis Lothoré if (vif->ndev)
898*3da9d32bSAlexis Lothoré unregister_netdev(vif->ndev);
8995625f965SAjay Singh }
9005625f965SAjay Singh
901*3da9d32bSAlexis Lothoré wilc_wfi_deinit_mon_interface(wilc, false);
902*3da9d32bSAlexis Lothoré destroy_workqueue(wilc->hif_workqueue);
903*3da9d32bSAlexis Lothoré
9045625f965SAjay Singh wilc_wlan_cfg_deinit(wilc);
9055625f965SAjay Singh wlan_deinit_locks(wilc);
9065625f965SAjay Singh wiphy_unregister(wilc->wiphy);
9075625f965SAjay Singh wiphy_free(wilc->wiphy);
9085625f965SAjay Singh }
9095625f965SAjay Singh EXPORT_SYMBOL_GPL(wilc_netdev_cleanup);
9105625f965SAjay Singh
wilc_get_available_idx(struct wilc * wl)9115625f965SAjay Singh static u8 wilc_get_available_idx(struct wilc *wl)
9125625f965SAjay Singh {
9135625f965SAjay Singh int idx = 0;
9145625f965SAjay Singh struct wilc_vif *vif;
9155625f965SAjay Singh int srcu_idx;
9165625f965SAjay Singh
9175625f965SAjay Singh srcu_idx = srcu_read_lock(&wl->srcu);
9185625f965SAjay Singh list_for_each_entry_rcu(vif, &wl->vif_list, list) {
9195625f965SAjay Singh if (vif->idx == 0)
9205625f965SAjay Singh idx = 1;
9215625f965SAjay Singh else
9225625f965SAjay Singh idx = 0;
9235625f965SAjay Singh }
9245625f965SAjay Singh srcu_read_unlock(&wl->srcu, srcu_idx);
9255625f965SAjay Singh return idx;
9265625f965SAjay Singh }
9275625f965SAjay Singh
wilc_netdev_ifc_init(struct wilc * wl,const char * name,int vif_type,enum nl80211_iftype type,bool rtnl_locked)9285625f965SAjay Singh struct wilc_vif *wilc_netdev_ifc_init(struct wilc *wl, const char *name,
9295625f965SAjay Singh int vif_type, enum nl80211_iftype type,
9305625f965SAjay Singh bool rtnl_locked)
9315625f965SAjay Singh {
9325625f965SAjay Singh struct net_device *ndev;
9335625f965SAjay Singh struct wilc_vif *vif;
9345625f965SAjay Singh int ret;
9355625f965SAjay Singh
9365625f965SAjay Singh ndev = alloc_etherdev(sizeof(*vif));
9375625f965SAjay Singh if (!ndev)
9385625f965SAjay Singh return ERR_PTR(-ENOMEM);
9395625f965SAjay Singh
9405625f965SAjay Singh vif = netdev_priv(ndev);
9415625f965SAjay Singh ndev->ieee80211_ptr = &vif->priv.wdev;
9425625f965SAjay Singh strcpy(ndev->name, name);
9435625f965SAjay Singh vif->wilc = wl;
9445625f965SAjay Singh vif->ndev = ndev;
9455625f965SAjay Singh ndev->ml_priv = vif;
9465625f965SAjay Singh
9475625f965SAjay Singh ndev->netdev_ops = &wilc_netdev_ops;
9485625f965SAjay Singh
9495625f965SAjay Singh SET_NETDEV_DEV(ndev, wiphy_dev(wl->wiphy));
9505625f965SAjay Singh
9515625f965SAjay Singh vif->priv.wdev.wiphy = wl->wiphy;
9525625f965SAjay Singh vif->priv.wdev.netdev = ndev;
9535625f965SAjay Singh vif->priv.wdev.iftype = type;
9545625f965SAjay Singh vif->priv.dev = ndev;
9555625f965SAjay Singh
9565625f965SAjay Singh if (rtnl_locked)
9572fe8ef10SJohannes Berg ret = cfg80211_register_netdevice(ndev);
9585625f965SAjay Singh else
9595625f965SAjay Singh ret = register_netdev(ndev);
9605625f965SAjay Singh
9615625f965SAjay Singh if (ret) {
96209ed8bfcSDavid Mosberger-Tang ret = -EFAULT;
96309ed8bfcSDavid Mosberger-Tang goto error;
96409ed8bfcSDavid Mosberger-Tang }
96509ed8bfcSDavid Mosberger-Tang
9665625f965SAjay Singh ndev->needs_free_netdev = true;
9675625f965SAjay Singh vif->iftype = vif_type;
9685625f965SAjay Singh vif->idx = wilc_get_available_idx(wl);
9695625f965SAjay Singh vif->mac_opened = 0;
9705625f965SAjay Singh mutex_lock(&wl->vif_mutex);
9715625f965SAjay Singh list_add_tail_rcu(&vif->list, &wl->vif_list);
9725625f965SAjay Singh wl->vif_num += 1;
9735625f965SAjay Singh mutex_unlock(&wl->vif_mutex);
9745625f965SAjay Singh synchronize_srcu(&wl->srcu);
9755625f965SAjay Singh
9765625f965SAjay Singh return vif;
97709ed8bfcSDavid Mosberger-Tang
9784041c60aSAjay Singh error:
9792b88974eSWang Yufen if (rtnl_locked)
9802b88974eSWang Yufen cfg80211_unregister_netdevice(ndev);
9812b88974eSWang Yufen else
9822b88974eSWang Yufen unregister_netdev(ndev);
98309ed8bfcSDavid Mosberger-Tang free_netdev(ndev);
98409ed8bfcSDavid Mosberger-Tang return ERR_PTR(ret);
9855625f965SAjay Singh }
9865625f965SAjay Singh
9875625f965SAjay Singh MODULE_LICENSE("GPL");
988b52b331aSAjay Singh MODULE_FIRMWARE(WILC1000_FW(WILC1000_API_VER));
989