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
99059d0e38SAlexis Lothoré wilc_for_each_vif(wilc, vif) {
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 {
130ebfb5e8fSAlexis Lothoré int srcu_idx;
1315625f965SAjay Singh u8 ret_val = 0;
1325625f965SAjay Singh struct wilc_vif *vif;
1335625f965SAjay Singh
134ebfb5e8fSAlexis Lothoré srcu_idx = srcu_read_lock(&wilc->srcu);
135059d0e38SAlexis Lothoré wilc_for_each_vif(wilc, vif) {
1365625f965SAjay Singh if (!is_zero_ether_addr(vif->bssid))
1375625f965SAjay Singh ret_val++;
1385625f965SAjay Singh }
139ebfb5e8fSAlexis Lothoré srcu_read_unlock(&wilc->srcu, srcu_idx);
1405625f965SAjay Singh return ret_val;
1415625f965SAjay Singh }
1425625f965SAjay Singh
wilc_wake_tx_queues(struct wilc * wl)1435d2dbcccSAlexis Lothoré static void wilc_wake_tx_queues(struct wilc *wl)
1445d2dbcccSAlexis Lothoré {
145ebfb5e8fSAlexis Lothoré int srcu_idx;
1465d2dbcccSAlexis Lothoré struct wilc_vif *ifc;
1475d2dbcccSAlexis Lothoré
148ebfb5e8fSAlexis Lothoré srcu_idx = srcu_read_lock(&wl->srcu);
149059d0e38SAlexis Lothoré wilc_for_each_vif(wl, ifc) {
1505d2dbcccSAlexis Lothoré if (ifc->mac_opened && netif_queue_stopped(ifc->ndev))
1515d2dbcccSAlexis Lothoré netif_wake_queue(ifc->ndev);
1525d2dbcccSAlexis Lothoré }
153ebfb5e8fSAlexis Lothoré srcu_read_unlock(&wl->srcu, srcu_idx);
1545d2dbcccSAlexis Lothoré }
1555d2dbcccSAlexis Lothoré
wilc_txq_task(void * vp)1565625f965SAjay Singh static int wilc_txq_task(void *vp)
1575625f965SAjay Singh {
1585625f965SAjay Singh int ret;
1595625f965SAjay Singh u32 txq_count;
1605625f965SAjay Singh struct wilc *wl = vp;
1615625f965SAjay Singh
1625625f965SAjay Singh complete(&wl->txq_thread_started);
1635625f965SAjay Singh while (1) {
164a08bb28fSPrasurjya Rohan Saikia if (wait_for_completion_interruptible(&wl->txq_event))
165a08bb28fSPrasurjya Rohan Saikia continue;
1665625f965SAjay Singh if (wl->close) {
1675625f965SAjay Singh complete(&wl->txq_thread_started);
1685625f965SAjay Singh
1695625f965SAjay Singh while (!kthread_should_stop())
1705625f965SAjay Singh schedule();
1715625f965SAjay Singh break;
1725625f965SAjay Singh }
1735625f965SAjay Singh do {
1745625f965SAjay Singh ret = wilc_wlan_handle_txq(wl, &txq_count);
1755625f965SAjay Singh if (txq_count < FLOW_CONTROL_LOWER_THRESHOLD) {
1765d2dbcccSAlexis Lothoré wilc_wake_tx_queues(wl);
1775625f965SAjay Singh }
178a08bb28fSPrasurjya Rohan Saikia if (ret != WILC_VMM_ENTRY_FULL_RETRY)
179a08bb28fSPrasurjya Rohan Saikia break;
180a08bb28fSPrasurjya Rohan Saikia /* Back off TX task from sending packets for some time.
181a08bb28fSPrasurjya Rohan Saikia * msleep_interruptible will allow RX task to run and
182a08bb28fSPrasurjya Rohan Saikia * free buffers. TX task will be in TASK_INTERRUPTIBLE
183a08bb28fSPrasurjya Rohan Saikia * state which will put the thread back to CPU running
184a08bb28fSPrasurjya Rohan Saikia * queue when it's signaled even if the timeout isn't
185a08bb28fSPrasurjya Rohan Saikia * elapsed. This gives faster chance for reserved SK
186a08bb28fSPrasurjya Rohan Saikia * buffers to be free.
187a08bb28fSPrasurjya Rohan Saikia */
188a08bb28fSPrasurjya Rohan Saikia msleep_interruptible(TX_BACKOFF_WEIGHT_MS);
189a08bb28fSPrasurjya Rohan Saikia } while (!wl->close);
1905625f965SAjay Singh }
1915625f965SAjay Singh return 0;
1925625f965SAjay Singh }
1935625f965SAjay Singh
wilc_wlan_get_firmware(struct net_device * dev)1945625f965SAjay Singh static int wilc_wlan_get_firmware(struct net_device *dev)
1955625f965SAjay Singh {
1965625f965SAjay Singh struct wilc_vif *vif = netdev_priv(dev);
1975625f965SAjay Singh struct wilc *wilc = vif->wilc;
1985625f965SAjay Singh int chip_id;
1990b3dd675SAjay Singh const struct firmware *wilc_fw;
200b52b331aSAjay Singh int ret;
2015625f965SAjay Singh
2025625f965SAjay Singh chip_id = wilc_get_chipid(wilc, false);
2035625f965SAjay Singh
2040b3dd675SAjay Singh netdev_info(dev, "ChipID [%x] loading firmware [%s]\n", chip_id,
205b52b331aSAjay Singh WILC1000_FW(WILC1000_API_VER));
2065625f965SAjay Singh
207b52b331aSAjay Singh ret = request_firmware(&wilc_fw, WILC1000_FW(WILC1000_API_VER),
208b52b331aSAjay Singh wilc->dev);
209b52b331aSAjay Singh if (ret != 0) {
2100b3dd675SAjay Singh netdev_err(dev, "%s - firmware not available\n",
211b52b331aSAjay Singh WILC1000_FW(WILC1000_API_VER));
2125625f965SAjay Singh return -EINVAL;
2135625f965SAjay Singh }
2140b3dd675SAjay Singh wilc->firmware = wilc_fw;
2155625f965SAjay Singh
2165625f965SAjay Singh return 0;
2175625f965SAjay Singh }
2185625f965SAjay Singh
wilc_start_firmware(struct net_device * dev)2195625f965SAjay Singh static int wilc_start_firmware(struct net_device *dev)
2205625f965SAjay Singh {
2215625f965SAjay Singh struct wilc_vif *vif = netdev_priv(dev);
2225625f965SAjay Singh struct wilc *wilc = vif->wilc;
2235625f965SAjay Singh int ret = 0;
2245625f965SAjay Singh
2255625f965SAjay Singh ret = wilc_wlan_start(wilc);
2265625f965SAjay Singh if (ret)
2275625f965SAjay Singh return ret;
2285625f965SAjay Singh
2295625f965SAjay Singh if (!wait_for_completion_timeout(&wilc->sync_event,
2305625f965SAjay Singh msecs_to_jiffies(5000)))
2315625f965SAjay Singh return -ETIME;
2325625f965SAjay Singh
2335625f965SAjay Singh return 0;
2345625f965SAjay Singh }
2355625f965SAjay Singh
wilc1000_firmware_download(struct net_device * dev)2365625f965SAjay Singh static int wilc1000_firmware_download(struct net_device *dev)
2375625f965SAjay Singh {
2385625f965SAjay Singh struct wilc_vif *vif = netdev_priv(dev);
2395625f965SAjay Singh struct wilc *wilc = vif->wilc;
2405625f965SAjay Singh int ret = 0;
2415625f965SAjay Singh
2425625f965SAjay Singh if (!wilc->firmware) {
2435625f965SAjay Singh netdev_err(dev, "Firmware buffer is NULL\n");
2445625f965SAjay Singh return -ENOBUFS;
2455625f965SAjay Singh }
2465625f965SAjay Singh
2475625f965SAjay Singh ret = wilc_wlan_firmware_download(wilc, wilc->firmware->data,
2485625f965SAjay Singh wilc->firmware->size);
2495625f965SAjay Singh if (ret)
2505625f965SAjay Singh return ret;
2515625f965SAjay Singh
2525625f965SAjay Singh release_firmware(wilc->firmware);
2535625f965SAjay Singh wilc->firmware = NULL;
2545625f965SAjay Singh
2555625f965SAjay Singh netdev_dbg(dev, "Download Succeeded\n");
2565625f965SAjay Singh
2575625f965SAjay Singh return 0;
2585625f965SAjay Singh }
2595625f965SAjay Singh
wilc_init_fw_config(struct net_device * dev,struct wilc_vif * vif)2605625f965SAjay Singh static int wilc_init_fw_config(struct net_device *dev, struct wilc_vif *vif)
2615625f965SAjay Singh {
2625625f965SAjay Singh struct wilc_priv *priv = &vif->priv;
2635625f965SAjay Singh struct host_if_drv *hif_drv;
2645625f965SAjay Singh u8 b;
2655625f965SAjay Singh u16 hw;
2665625f965SAjay Singh u32 w;
2675625f965SAjay Singh
2685625f965SAjay Singh netdev_dbg(dev, "Start configuring Firmware\n");
2695625f965SAjay Singh hif_drv = (struct host_if_drv *)priv->hif_drv;
2705625f965SAjay Singh netdev_dbg(dev, "Host = %p\n", hif_drv);
2715625f965SAjay Singh
2725625f965SAjay Singh w = vif->iftype;
2735625f965SAjay Singh cpu_to_le32s(&w);
2745625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 1, WID_SET_OPERATION_MODE, (u8 *)&w, 4,
2755625f965SAjay Singh 0, 0))
2765625f965SAjay Singh goto fail;
2775625f965SAjay Singh
2785625f965SAjay Singh b = WILC_FW_BSS_TYPE_INFRA;
2795625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_BSS_TYPE, &b, 1, 0, 0))
2805625f965SAjay Singh goto fail;
2815625f965SAjay Singh
2825625f965SAjay Singh b = WILC_FW_TX_RATE_AUTO;
2835625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_CURRENT_TX_RATE, &b, 1, 0, 0))
2845625f965SAjay Singh goto fail;
2855625f965SAjay Singh
2865625f965SAjay Singh b = WILC_FW_OPER_MODE_G_MIXED_11B_2;
2875625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_11G_OPERATING_MODE, &b, 1, 0, 0))
2885625f965SAjay Singh goto fail;
2895625f965SAjay Singh
290a8e5fefaSAjay Singh b = WILC_FW_PREAMBLE_AUTO;
2915625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_PREAMBLE, &b, 1, 0, 0))
2925625f965SAjay Singh goto fail;
2935625f965SAjay Singh
2945625f965SAjay Singh b = WILC_FW_11N_PROT_AUTO;
2955625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_11N_PROT_MECH, &b, 1, 0, 0))
2965625f965SAjay Singh goto fail;
2975625f965SAjay Singh
2985625f965SAjay Singh b = WILC_FW_ACTIVE_SCAN;
2995625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_SCAN_TYPE, &b, 1, 0, 0))
3005625f965SAjay Singh goto fail;
3015625f965SAjay Singh
3025625f965SAjay Singh b = WILC_FW_SITE_SURVEY_OFF;
3035625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_SITE_SURVEY, &b, 1, 0, 0))
3045625f965SAjay Singh goto fail;
3055625f965SAjay Singh
3065625f965SAjay Singh hw = 0xffff;
3075625f965SAjay Singh cpu_to_le16s(&hw);
3085625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_RTS_THRESHOLD, (u8 *)&hw, 2, 0, 0))
3095625f965SAjay Singh goto fail;
3105625f965SAjay Singh
3115625f965SAjay Singh hw = 2346;
3125625f965SAjay Singh cpu_to_le16s(&hw);
3135625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_FRAG_THRESHOLD, (u8 *)&hw, 2, 0, 0))
3145625f965SAjay Singh goto fail;
3155625f965SAjay Singh
3165625f965SAjay Singh b = 0;
3175625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_BCAST_SSID, &b, 1, 0, 0))
3185625f965SAjay Singh goto fail;
3195625f965SAjay Singh
3205625f965SAjay Singh b = 1;
3215625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_QOS_ENABLE, &b, 1, 0, 0))
3225625f965SAjay Singh goto fail;
3235625f965SAjay Singh
3245625f965SAjay Singh b = WILC_FW_NO_POWERSAVE;
3255625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_POWER_MANAGEMENT, &b, 1, 0, 0))
3265625f965SAjay Singh goto fail;
3275625f965SAjay Singh
3285625f965SAjay Singh b = WILC_FW_SEC_NO;
3295625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_11I_MODE, &b, 1, 0, 0))
3305625f965SAjay Singh goto fail;
3315625f965SAjay Singh
3325625f965SAjay Singh b = WILC_FW_AUTH_OPEN_SYSTEM;
3335625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_AUTH_TYPE, &b, 1, 0, 0))
3345625f965SAjay Singh goto fail;
3355625f965SAjay Singh
3365625f965SAjay Singh b = 3;
3375625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_LISTEN_INTERVAL, &b, 1, 0, 0))
3385625f965SAjay Singh goto fail;
3395625f965SAjay Singh
3405625f965SAjay Singh b = 3;
3415625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_DTIM_PERIOD, &b, 1, 0, 0))
3425625f965SAjay Singh goto fail;
3435625f965SAjay Singh
3445625f965SAjay Singh b = WILC_FW_ACK_POLICY_NORMAL;
3455625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_ACK_POLICY, &b, 1, 0, 0))
3465625f965SAjay Singh goto fail;
3475625f965SAjay Singh
3485625f965SAjay Singh b = 0;
3495625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_USER_CONTROL_ON_TX_POWER, &b, 1,
3505625f965SAjay Singh 0, 0))
3515625f965SAjay Singh goto fail;
3525625f965SAjay Singh
3535625f965SAjay Singh b = 48;
3545625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_TX_POWER_LEVEL_11A, &b, 1, 0, 0))
3555625f965SAjay Singh goto fail;
3565625f965SAjay Singh
3575625f965SAjay Singh b = 28;
3585625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_TX_POWER_LEVEL_11B, &b, 1, 0, 0))
3595625f965SAjay Singh goto fail;
3605625f965SAjay Singh
3615625f965SAjay Singh hw = 100;
3625625f965SAjay Singh cpu_to_le16s(&hw);
3635625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_BEACON_INTERVAL, (u8 *)&hw, 2, 0, 0))
3645625f965SAjay Singh goto fail;
3655625f965SAjay Singh
3665625f965SAjay Singh b = WILC_FW_REKEY_POLICY_DISABLE;
3675625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_REKEY_POLICY, &b, 1, 0, 0))
3685625f965SAjay Singh goto fail;
3695625f965SAjay Singh
3705625f965SAjay Singh w = 84600;
3715625f965SAjay Singh cpu_to_le32s(&w);
3725625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_REKEY_PERIOD, (u8 *)&w, 4, 0, 0))
3735625f965SAjay Singh goto fail;
3745625f965SAjay Singh
3755625f965SAjay Singh w = 500;
3765625f965SAjay Singh cpu_to_le32s(&w);
3775625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_REKEY_PACKET_COUNT, (u8 *)&w, 4, 0,
3785625f965SAjay Singh 0))
3795625f965SAjay Singh goto fail;
3805625f965SAjay Singh
3815625f965SAjay Singh b = 1;
3825625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_SHORT_SLOT_ALLOWED, &b, 1, 0,
3835625f965SAjay Singh 0))
3845625f965SAjay Singh goto fail;
3855625f965SAjay Singh
3865625f965SAjay Singh b = WILC_FW_ERP_PROT_SELF_CTS;
3875625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_11N_ERP_PROT_TYPE, &b, 1, 0, 0))
3885625f965SAjay Singh goto fail;
3895625f965SAjay Singh
3905625f965SAjay Singh b = 1;
3915625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_11N_ENABLE, &b, 1, 0, 0))
3925625f965SAjay Singh goto fail;
3935625f965SAjay Singh
3945625f965SAjay Singh b = WILC_FW_11N_OP_MODE_HT_MIXED;
3955625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_11N_OPERATING_MODE, &b, 1, 0, 0))
3965625f965SAjay Singh goto fail;
3975625f965SAjay Singh
3985625f965SAjay Singh b = 1;
3995625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_11N_TXOP_PROT_DISABLE, &b, 1, 0, 0))
4005625f965SAjay Singh goto fail;
4015625f965SAjay Singh
4025625f965SAjay Singh b = WILC_FW_OBBS_NONHT_DETECT_PROTECT_REPORT;
4035625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_11N_OBSS_NONHT_DETECTION, &b, 1,
4045625f965SAjay Singh 0, 0))
4055625f965SAjay Singh goto fail;
4065625f965SAjay Singh
4075625f965SAjay Singh b = WILC_FW_HT_PROT_RTS_CTS_NONHT;
4085625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_11N_HT_PROT_TYPE, &b, 1, 0, 0))
4095625f965SAjay Singh goto fail;
4105625f965SAjay Singh
4115625f965SAjay Singh b = 0;
4125625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_11N_RIFS_PROT_ENABLE, &b, 1, 0,
4135625f965SAjay Singh 0))
4145625f965SAjay Singh goto fail;
4155625f965SAjay Singh
4165625f965SAjay Singh b = 7;
4175625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_11N_CURRENT_TX_MCS, &b, 1, 0, 0))
4185625f965SAjay Singh goto fail;
4195625f965SAjay Singh
4205625f965SAjay Singh b = 1;
4215625f965SAjay Singh if (!wilc_wlan_cfg_set(vif, 0, WID_11N_IMMEDIATE_BA_ENABLED, &b, 1,
42252284952SAjay Singh 1, 0))
4235625f965SAjay Singh goto fail;
4245625f965SAjay Singh
4255625f965SAjay Singh return 0;
4265625f965SAjay Singh
4275625f965SAjay Singh fail:
4285625f965SAjay Singh return -EINVAL;
4295625f965SAjay Singh }
4305625f965SAjay Singh
wlan_deinitialize_threads(struct net_device * dev)4315625f965SAjay Singh static void wlan_deinitialize_threads(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 wl->close = 1;
4375625f965SAjay Singh
4385625f965SAjay Singh complete(&wl->txq_event);
4395625f965SAjay Singh
4405625f965SAjay Singh if (wl->txq_thread) {
4415625f965SAjay Singh kthread_stop(wl->txq_thread);
4425625f965SAjay Singh wl->txq_thread = NULL;
4435625f965SAjay Singh }
4445625f965SAjay Singh }
4455625f965SAjay Singh
wilc_wlan_deinitialize(struct net_device * dev)4465625f965SAjay Singh static void wilc_wlan_deinitialize(struct net_device *dev)
4475625f965SAjay Singh {
4485625f965SAjay Singh struct wilc_vif *vif = netdev_priv(dev);
4495625f965SAjay Singh struct wilc *wl = vif->wilc;
4505625f965SAjay Singh
4515625f965SAjay Singh if (!wl) {
4525625f965SAjay Singh netdev_err(dev, "wl is NULL\n");
4535625f965SAjay Singh return;
4545625f965SAjay Singh }
4555625f965SAjay Singh
4565625f965SAjay Singh if (wl->initialized) {
4575625f965SAjay Singh netdev_info(dev, "Deinitializing wilc1000...\n");
4585625f965SAjay Singh
4595625f965SAjay Singh if (!wl->dev_irq_num &&
4605625f965SAjay Singh wl->hif_func->disable_interrupt) {
4615625f965SAjay Singh mutex_lock(&wl->hif_cs);
4625625f965SAjay Singh wl->hif_func->disable_interrupt(wl);
4635625f965SAjay Singh mutex_unlock(&wl->hif_cs);
4645625f965SAjay Singh }
4655625f965SAjay Singh complete(&wl->txq_event);
4665625f965SAjay Singh
4675625f965SAjay Singh wlan_deinitialize_threads(dev);
4685625f965SAjay Singh deinit_irq(dev);
4695625f965SAjay Singh
4705625f965SAjay Singh wilc_wlan_stop(wl, vif);
4715625f965SAjay Singh wilc_wlan_cleanup(dev);
4725625f965SAjay Singh
4735625f965SAjay Singh wl->initialized = false;
4745625f965SAjay Singh
4755625f965SAjay Singh netdev_dbg(dev, "wilc1000 deinitialization Done\n");
4765625f965SAjay Singh } else {
4775625f965SAjay Singh netdev_dbg(dev, "wilc1000 is not initialized\n");
4785625f965SAjay Singh }
4795625f965SAjay Singh }
4805625f965SAjay Singh
wlan_initialize_threads(struct net_device * dev)4815625f965SAjay Singh static int wlan_initialize_threads(struct net_device *dev)
4825625f965SAjay Singh {
4835625f965SAjay Singh struct wilc_vif *vif = netdev_priv(dev);
4845625f965SAjay Singh struct wilc *wilc = vif->wilc;
4855625f965SAjay Singh
4865625f965SAjay Singh wilc->txq_thread = kthread_run(wilc_txq_task, (void *)wilc,
4873cc23932SDavid Mosberger-Tang "%s-tx", dev->name);
4885625f965SAjay Singh if (IS_ERR(wilc->txq_thread)) {
4895625f965SAjay Singh netdev_err(dev, "couldn't create TXQ thread\n");
490f589b5d9SAjay Singh wilc->close = 1;
4915625f965SAjay Singh return PTR_ERR(wilc->txq_thread);
4925625f965SAjay Singh }
4935625f965SAjay Singh wait_for_completion(&wilc->txq_thread_started);
4945625f965SAjay Singh
4955625f965SAjay Singh return 0;
4965625f965SAjay Singh }
4975625f965SAjay Singh
wilc_wlan_initialize(struct net_device * dev,struct wilc_vif * vif)4985625f965SAjay Singh static int wilc_wlan_initialize(struct net_device *dev, struct wilc_vif *vif)
4995625f965SAjay Singh {
5005625f965SAjay Singh int ret = 0;
5015625f965SAjay Singh struct wilc *wl = vif->wilc;
5025625f965SAjay Singh
5035625f965SAjay Singh if (!wl->initialized) {
5045625f965SAjay Singh wl->mac_status = WILC_MAC_STATUS_INIT;
5055625f965SAjay Singh wl->close = 0;
5065625f965SAjay Singh
5075625f965SAjay Singh ret = wilc_wlan_init(dev);
5085625f965SAjay Singh if (ret)
5095625f965SAjay Singh return ret;
5105625f965SAjay Singh
5115625f965SAjay Singh ret = wlan_initialize_threads(dev);
5125625f965SAjay Singh if (ret)
5135625f965SAjay Singh goto fail_wilc_wlan;
5145625f965SAjay Singh
5155625f965SAjay Singh if (wl->dev_irq_num && init_irq(dev)) {
5165625f965SAjay Singh ret = -EIO;
5175625f965SAjay Singh goto fail_threads;
5185625f965SAjay Singh }
5195625f965SAjay Singh
5205625f965SAjay Singh if (!wl->dev_irq_num &&
5215625f965SAjay Singh wl->hif_func->enable_interrupt &&
5225625f965SAjay Singh wl->hif_func->enable_interrupt(wl)) {
5235625f965SAjay Singh ret = -EIO;
5245625f965SAjay Singh goto fail_irq_init;
5255625f965SAjay Singh }
5265625f965SAjay Singh
5275625f965SAjay Singh ret = wilc_wlan_get_firmware(dev);
5285625f965SAjay Singh if (ret)
5295625f965SAjay Singh goto fail_irq_enable;
5305625f965SAjay Singh
5315625f965SAjay Singh ret = wilc1000_firmware_download(dev);
5325625f965SAjay Singh if (ret)
5335625f965SAjay Singh goto fail_irq_enable;
5345625f965SAjay Singh
5355625f965SAjay Singh ret = wilc_start_firmware(dev);
5365625f965SAjay Singh if (ret)
5375625f965SAjay Singh goto fail_irq_enable;
5385625f965SAjay Singh
5395625f965SAjay Singh if (wilc_wlan_cfg_get(vif, 1, WID_FIRMWARE_VERSION, 1, 0)) {
5405625f965SAjay Singh int size;
5414ee8a915SAjay Singh char firmware_ver[WILC_MAX_FW_VERSION_STR_SIZE];
5425625f965SAjay Singh
5435625f965SAjay Singh size = wilc_wlan_cfg_get_val(wl, WID_FIRMWARE_VERSION,
5445625f965SAjay Singh firmware_ver,
5455625f965SAjay Singh sizeof(firmware_ver));
5465625f965SAjay Singh firmware_ver[size] = '\0';
5475625f965SAjay Singh netdev_dbg(dev, "Firmware Ver = %s\n", firmware_ver);
5485625f965SAjay Singh }
5495625f965SAjay Singh
5505625f965SAjay Singh ret = wilc_init_fw_config(dev, vif);
5515625f965SAjay Singh if (ret) {
5525625f965SAjay Singh netdev_err(dev, "Failed to configure firmware\n");
5535625f965SAjay Singh goto fail_fw_start;
5545625f965SAjay Singh }
5555625f965SAjay Singh wl->initialized = true;
5565625f965SAjay Singh return 0;
5575625f965SAjay Singh
5585625f965SAjay Singh fail_fw_start:
5595625f965SAjay Singh wilc_wlan_stop(wl, vif);
5605625f965SAjay Singh
5615625f965SAjay Singh fail_irq_enable:
5625625f965SAjay Singh if (!wl->dev_irq_num &&
5635625f965SAjay Singh wl->hif_func->disable_interrupt)
5645625f965SAjay Singh wl->hif_func->disable_interrupt(wl);
5655625f965SAjay Singh fail_irq_init:
5665625f965SAjay Singh if (wl->dev_irq_num)
5675625f965SAjay Singh deinit_irq(dev);
5685625f965SAjay Singh fail_threads:
5695625f965SAjay Singh wlan_deinitialize_threads(dev);
5705625f965SAjay Singh fail_wilc_wlan:
5715625f965SAjay Singh wilc_wlan_cleanup(dev);
5725625f965SAjay Singh netdev_err(dev, "WLAN initialization FAILED\n");
5735625f965SAjay Singh } else {
5745625f965SAjay Singh netdev_dbg(dev, "wilc1000 already initialized\n");
5755625f965SAjay Singh }
5765625f965SAjay Singh return ret;
5775625f965SAjay Singh }
5785625f965SAjay Singh
mac_init_fn(struct net_device * ndev)5795625f965SAjay Singh static int mac_init_fn(struct net_device *ndev)
5805625f965SAjay Singh {
5815625f965SAjay Singh netif_start_queue(ndev);
5825625f965SAjay Singh netif_stop_queue(ndev);
5835625f965SAjay Singh
5845625f965SAjay Singh return 0;
5855625f965SAjay Singh }
5865625f965SAjay Singh
wilc_mac_open(struct net_device * ndev)5875625f965SAjay Singh static int wilc_mac_open(struct net_device *ndev)
5885625f965SAjay Singh {
5895625f965SAjay Singh struct wilc_vif *vif = netdev_priv(ndev);
5905625f965SAjay Singh struct wilc *wl = vif->wilc;
5915625f965SAjay Singh int ret = 0;
5925625f965SAjay Singh struct mgmt_frame_regs mgmt_regs = {};
5935625f965SAjay Singh
5945625f965SAjay Singh if (!wl || !wl->dev) {
5955625f965SAjay Singh netdev_err(ndev, "device not ready\n");
5965625f965SAjay Singh return -ENODEV;
5975625f965SAjay Singh }
5985625f965SAjay Singh
5995625f965SAjay Singh netdev_dbg(ndev, "MAC OPEN[%p]\n", ndev);
6005625f965SAjay Singh
6015625f965SAjay Singh ret = wilc_init_host_int(ndev);
6025625f965SAjay Singh if (ret)
6035625f965SAjay Singh return ret;
6045625f965SAjay Singh
6055625f965SAjay Singh ret = wilc_wlan_initialize(ndev, vif);
6065625f965SAjay Singh if (ret) {
6075625f965SAjay Singh wilc_deinit_host_int(ndev);
6085625f965SAjay Singh return ret;
6095625f965SAjay Singh }
6105625f965SAjay Singh
61183d9b54eSAjay Singh netdev_dbg(ndev, "Mac address: %pM\n", ndev->dev_addr);
61283d9b54eSAjay Singh ret = wilc_set_mac_address(vif, ndev->dev_addr);
61383d9b54eSAjay Singh if (ret) {
61483d9b54eSAjay Singh netdev_err(ndev, "Failed to enforce MAC address in chip");
61583d9b54eSAjay Singh wilc_deinit_host_int(ndev);
61683d9b54eSAjay Singh if (!wl->open_ifcs)
61783d9b54eSAjay Singh wilc_wlan_deinitialize(ndev);
61883d9b54eSAjay Singh return ret;
61983d9b54eSAjay Singh }
62083d9b54eSAjay Singh
6215625f965SAjay Singh wilc_set_operation_mode(vif, wilc_get_vif_idx(vif), vif->iftype,
6225625f965SAjay Singh vif->idx);
623a381b78aSDavid Mosberger-Tang
6245625f965SAjay Singh mgmt_regs.interface_stypes = vif->mgmt_reg_stypes;
6255625f965SAjay Singh /* so we detect a change */
6265625f965SAjay Singh vif->mgmt_reg_stypes = 0;
6275625f965SAjay Singh wilc_update_mgmt_frame_registrations(vif->ndev->ieee80211_ptr->wiphy,
6285625f965SAjay Singh vif->ndev->ieee80211_ptr,
6295625f965SAjay Singh &mgmt_regs);
6305625f965SAjay Singh netif_wake_queue(ndev);
6315625f965SAjay Singh wl->open_ifcs++;
6325625f965SAjay Singh vif->mac_opened = 1;
6335625f965SAjay Singh return 0;
6345625f965SAjay Singh }
6355625f965SAjay Singh
mac_stats(struct net_device * dev)6365625f965SAjay Singh static struct net_device_stats *mac_stats(struct net_device *dev)
6375625f965SAjay Singh {
6385625f965SAjay Singh struct wilc_vif *vif = netdev_priv(dev);
6395625f965SAjay Singh
6405625f965SAjay Singh return &vif->netstats;
6415625f965SAjay Singh }
6425625f965SAjay Singh
wilc_set_mac_addr(struct net_device * dev,void * p)643c04fabacSAjay Singh static int wilc_set_mac_addr(struct net_device *dev, void *p)
644c04fabacSAjay Singh {
645c04fabacSAjay Singh int result;
646c04fabacSAjay Singh struct wilc_vif *vif = netdev_priv(dev);
647c04fabacSAjay Singh struct wilc *wilc = vif->wilc;
648c04fabacSAjay Singh struct sockaddr *addr = (struct sockaddr *)p;
649c04fabacSAjay Singh unsigned char mac_addr[ETH_ALEN];
650c04fabacSAjay Singh struct wilc_vif *tmp_vif;
651ebfb5e8fSAlexis Lothoré int srcu_idx;
652c04fabacSAjay Singh
653c04fabacSAjay Singh if (!is_valid_ether_addr(addr->sa_data))
654a381b78aSDavid Mosberger-Tang return -EADDRNOTAVAIL;
655a381b78aSDavid Mosberger-Tang
656a381b78aSDavid Mosberger-Tang if (!vif->mac_opened) {
657a381b78aSDavid Mosberger-Tang eth_commit_mac_addr_change(dev, p);
658a381b78aSDavid Mosberger-Tang return 0;
659a381b78aSDavid Mosberger-Tang }
660a381b78aSDavid Mosberger-Tang
661a381b78aSDavid Mosberger-Tang /* Verify MAC Address is not already in use: */
662c04fabacSAjay Singh
663ebfb5e8fSAlexis Lothoré srcu_idx = srcu_read_lock(&wilc->srcu);
664059d0e38SAlexis Lothoré wilc_for_each_vif(wilc, tmp_vif) {
665c04fabacSAjay Singh wilc_get_mac_address(tmp_vif, mac_addr);
666c04fabacSAjay Singh if (ether_addr_equal(addr->sa_data, mac_addr)) {
667c04fabacSAjay Singh if (vif != tmp_vif) {
668ebfb5e8fSAlexis Lothoré srcu_read_unlock(&wilc->srcu, srcu_idx);
669a381b78aSDavid Mosberger-Tang return -EADDRNOTAVAIL;
670c04fabacSAjay Singh }
671ebfb5e8fSAlexis Lothoré srcu_read_unlock(&wilc->srcu, srcu_idx);
672c04fabacSAjay Singh return 0;
673c04fabacSAjay Singh }
674c04fabacSAjay Singh }
675ebfb5e8fSAlexis Lothoré srcu_read_unlock(&wilc->srcu, srcu_idx);
676c04fabacSAjay Singh
6775f1191edSAlexis Lothoré result = wilc_set_mac_address(vif, addr->sa_data);
678c04fabacSAjay Singh if (result)
679c04fabacSAjay Singh return result;
680c04fabacSAjay Singh
681a381b78aSDavid Mosberger-Tang eth_commit_mac_addr_change(dev, p);
682c04fabacSAjay Singh return result;
683c04fabacSAjay Singh }
684c04fabacSAjay Singh
wilc_set_multicast_list(struct net_device * dev)6855625f965SAjay Singh static void wilc_set_multicast_list(struct net_device *dev)
6865625f965SAjay Singh {
6875625f965SAjay Singh struct netdev_hw_addr *ha;
6885625f965SAjay Singh struct wilc_vif *vif = netdev_priv(dev);
6895625f965SAjay Singh int i;
6905625f965SAjay Singh u8 *mc_list;
6915625f965SAjay Singh u8 *cur_mc;
6925625f965SAjay Singh
6935625f965SAjay Singh if (dev->flags & IFF_PROMISC)
6945625f965SAjay Singh return;
6955625f965SAjay Singh
6965625f965SAjay Singh if (dev->flags & IFF_ALLMULTI ||
6975625f965SAjay Singh dev->mc.count > WILC_MULTICAST_TABLE_SIZE) {
6985625f965SAjay Singh wilc_setup_multicast_filter(vif, 0, 0, NULL);
6995625f965SAjay Singh return;
7005625f965SAjay Singh }
7015625f965SAjay Singh
7025625f965SAjay Singh if (dev->mc.count == 0) {
7035625f965SAjay Singh wilc_setup_multicast_filter(vif, 1, 0, NULL);
7045625f965SAjay Singh return;
7055625f965SAjay Singh }
7065625f965SAjay Singh
7075625f965SAjay Singh mc_list = kmalloc_array(dev->mc.count, ETH_ALEN, GFP_ATOMIC);
7085625f965SAjay Singh if (!mc_list)
7095625f965SAjay Singh return;
7105625f965SAjay Singh
7115625f965SAjay Singh cur_mc = mc_list;
7125625f965SAjay Singh i = 0;
7135625f965SAjay Singh netdev_for_each_mc_addr(ha, dev) {
7145625f965SAjay Singh memcpy(cur_mc, ha->addr, ETH_ALEN);
7155625f965SAjay Singh netdev_dbg(dev, "Entry[%d]: %pM\n", i, cur_mc);
7165625f965SAjay Singh i++;
7175625f965SAjay Singh cur_mc += ETH_ALEN;
7185625f965SAjay Singh }
7195625f965SAjay Singh
7205625f965SAjay Singh if (wilc_setup_multicast_filter(vif, 1, dev->mc.count, mc_list))
7215625f965SAjay Singh kfree(mc_list);
7225625f965SAjay Singh }
7235625f965SAjay Singh
wilc_tx_complete(void * priv,int status)7245625f965SAjay Singh static void wilc_tx_complete(void *priv, int status)
7255625f965SAjay Singh {
7265625f965SAjay Singh struct tx_complete_data *pv_data = priv;
7275625f965SAjay Singh
7285625f965SAjay Singh dev_kfree_skb(pv_data->skb);
7295625f965SAjay Singh kfree(pv_data);
7305625f965SAjay Singh }
7315625f965SAjay Singh
wilc_mac_xmit(struct sk_buff * skb,struct net_device * ndev)7325625f965SAjay Singh netdev_tx_t wilc_mac_xmit(struct sk_buff *skb, struct net_device *ndev)
7335625f965SAjay Singh {
7345625f965SAjay Singh struct wilc_vif *vif = netdev_priv(ndev);
7355625f965SAjay Singh struct wilc *wilc = vif->wilc;
7365625f965SAjay Singh struct tx_complete_data *tx_data = NULL;
7375625f965SAjay Singh int queue_count;
7385625f965SAjay Singh
7395625f965SAjay Singh if (skb->dev != ndev) {
7405625f965SAjay Singh netdev_err(ndev, "Packet not destined to this device\n");
741deb962ecSZhang Changzhong dev_kfree_skb(skb);
742cce0e083SLuc Van Oostenryck return NETDEV_TX_OK;
7435625f965SAjay Singh }
7445625f965SAjay Singh
7455625f965SAjay Singh tx_data = kmalloc(sizeof(*tx_data), GFP_ATOMIC);
7465625f965SAjay Singh if (!tx_data) {
7475625f965SAjay Singh dev_kfree_skb(skb);
7485625f965SAjay Singh netif_wake_queue(ndev);
749cce0e083SLuc Van Oostenryck return NETDEV_TX_OK;
7505625f965SAjay Singh }
7515625f965SAjay Singh
7525625f965SAjay Singh tx_data->buff = skb->data;
7535625f965SAjay Singh tx_data->size = skb->len;
7545625f965SAjay Singh tx_data->skb = skb;
7555625f965SAjay Singh
7565625f965SAjay Singh vif->netstats.tx_packets++;
7575625f965SAjay Singh vif->netstats.tx_bytes += tx_data->size;
7586fe91b69SVsevolod Kozlov queue_count = wilc_wlan_txq_add_net_pkt(ndev, tx_data,
7595625f965SAjay Singh tx_data->buff, tx_data->size,
7605625f965SAjay Singh wilc_tx_complete);
7615625f965SAjay Singh
7625625f965SAjay Singh if (queue_count > FLOW_CONTROL_UPPER_THRESHOLD) {
763ebfb5e8fSAlexis Lothoré int srcu_idx;
7645625f965SAjay Singh struct wilc_vif *vif;
7655625f965SAjay Singh
766ebfb5e8fSAlexis Lothoré srcu_idx = srcu_read_lock(&wilc->srcu);
767059d0e38SAlexis Lothoré wilc_for_each_vif(wilc, vif) {
7685625f965SAjay Singh if (vif->mac_opened)
7695625f965SAjay Singh netif_stop_queue(vif->ndev);
7705625f965SAjay Singh }
771ebfb5e8fSAlexis Lothoré srcu_read_unlock(&wilc->srcu, srcu_idx);
7725625f965SAjay Singh }
7735625f965SAjay Singh
774cce0e083SLuc Van Oostenryck return NETDEV_TX_OK;
7755625f965SAjay Singh }
7765625f965SAjay Singh
wilc_mac_close(struct net_device * ndev)7775625f965SAjay Singh static int wilc_mac_close(struct net_device *ndev)
7785625f965SAjay Singh {
7795625f965SAjay Singh struct wilc_vif *vif = netdev_priv(ndev);
7805625f965SAjay Singh struct wilc *wl = vif->wilc;
7815625f965SAjay Singh
7825625f965SAjay Singh netdev_dbg(ndev, "Mac close\n");
7835625f965SAjay Singh
7845625f965SAjay Singh if (wl->open_ifcs > 0)
7855625f965SAjay Singh wl->open_ifcs--;
7865625f965SAjay Singh else
7875625f965SAjay Singh return 0;
7885625f965SAjay Singh
7895625f965SAjay Singh if (vif->ndev) {
7905625f965SAjay Singh netif_stop_queue(vif->ndev);
7915625f965SAjay Singh
792ad3e683aSAjay Singh wilc_handle_disconnect(vif);
7935625f965SAjay Singh wilc_deinit_host_int(vif->ndev);
7945625f965SAjay Singh }
7955625f965SAjay Singh
7965625f965SAjay Singh if (wl->open_ifcs == 0) {
7975625f965SAjay Singh netdev_dbg(ndev, "Deinitializing wilc1000\n");
7985625f965SAjay Singh wl->close = 1;
7995625f965SAjay Singh wilc_wlan_deinitialize(ndev);
8005625f965SAjay Singh }
8015625f965SAjay Singh
8025625f965SAjay Singh vif->mac_opened = 0;
8035625f965SAjay Singh
8045625f965SAjay Singh return 0;
8055625f965SAjay Singh }
8065625f965SAjay Singh
wilc_frmw_to_host(struct wilc * wilc,u8 * buff,u32 size,u32 pkt_offset)8075625f965SAjay Singh void wilc_frmw_to_host(struct wilc *wilc, u8 *buff, u32 size,
8085625f965SAjay Singh u32 pkt_offset)
8095625f965SAjay Singh {
8105625f965SAjay Singh unsigned char *buff_to_send = NULL;
8115625f965SAjay Singh struct net_device *wilc_netdev;
81251e4aa8cSAlexis Lothoré unsigned int frame_len = 0;
8135625f965SAjay Singh struct wilc_vif *vif;
81451e4aa8cSAlexis Lothoré struct sk_buff *skb;
815ebfb5e8fSAlexis Lothoré int srcu_idx;
81651e4aa8cSAlexis Lothoré int stats;
8175625f965SAjay Singh
8185625f965SAjay Singh if (!wilc)
8195625f965SAjay Singh return;
8205625f965SAjay Singh
821ebfb5e8fSAlexis Lothoré srcu_idx = srcu_read_lock(&wilc->srcu);
8225625f965SAjay Singh wilc_netdev = get_if_handler(wilc, buff);
8235625f965SAjay Singh if (!wilc_netdev)
824dd66185cSAjay Singh goto out;
8255625f965SAjay Singh
8265625f965SAjay Singh buff += pkt_offset;
8275625f965SAjay Singh vif = netdev_priv(wilc_netdev);
8285625f965SAjay Singh
8295625f965SAjay Singh if (size > 0) {
8305625f965SAjay Singh frame_len = size;
8315625f965SAjay Singh buff_to_send = buff;
8325625f965SAjay Singh
8335625f965SAjay Singh skb = dev_alloc_skb(frame_len);
8345625f965SAjay Singh if (!skb)
835dd66185cSAjay Singh goto out;
8365625f965SAjay Singh
8375625f965SAjay Singh skb->dev = wilc_netdev;
8385625f965SAjay Singh
8395625f965SAjay Singh skb_put_data(skb, buff_to_send, frame_len);
8405625f965SAjay Singh
8415625f965SAjay Singh skb->protocol = eth_type_trans(skb, wilc_netdev);
8425625f965SAjay Singh vif->netstats.rx_packets++;
8435625f965SAjay Singh vif->netstats.rx_bytes += frame_len;
8445625f965SAjay Singh skb->ip_summed = CHECKSUM_UNNECESSARY;
8455625f965SAjay Singh stats = netif_rx(skb);
8465625f965SAjay Singh netdev_dbg(wilc_netdev, "netif_rx ret value is: %d\n", stats);
8475625f965SAjay Singh }
848dd66185cSAjay Singh out:
849ebfb5e8fSAlexis Lothoré srcu_read_unlock(&wilc->srcu, srcu_idx);
8505625f965SAjay Singh }
8515625f965SAjay Singh
wilc_wfi_mgmt_rx(struct wilc * wilc,u8 * buff,u32 size,bool is_auth)852c5b331d4SAjay Singh void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size, bool is_auth)
8535625f965SAjay Singh {
854ebfb5e8fSAlexis Lothoré int srcu_idx;
8555625f965SAjay Singh struct wilc_vif *vif;
8565625f965SAjay Singh
857ebfb5e8fSAlexis Lothoré srcu_idx = srcu_read_lock(&wilc->srcu);
858059d0e38SAlexis Lothoré wilc_for_each_vif(wilc, vif) {
859c5b331d4SAjay Singh struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buff;
8605625f965SAjay Singh u16 type = le16_to_cpup((__le16 *)buff);
8615625f965SAjay Singh u32 type_bit = BIT(type >> 4);
862c5b331d4SAjay Singh u32 auth_bit = BIT(IEEE80211_STYPE_AUTH >> 4);
863c5b331d4SAjay Singh
864c5b331d4SAjay Singh if ((vif->mgmt_reg_stypes & auth_bit &&
865c5b331d4SAjay Singh ieee80211_is_auth(mgmt->frame_control)) &&
866c5b331d4SAjay Singh vif->iftype == WILC_STATION_MODE && is_auth) {
867c5b331d4SAjay Singh wilc_wfi_mgmt_frame_rx(vif, buff, size);
868c5b331d4SAjay Singh break;
869c5b331d4SAjay Singh }
8705625f965SAjay Singh
8715625f965SAjay Singh if (vif->priv.p2p_listen_state &&
8725625f965SAjay Singh vif->mgmt_reg_stypes & type_bit)
8735625f965SAjay Singh wilc_wfi_p2p_rx(vif, buff, size);
8745625f965SAjay Singh
8755625f965SAjay Singh if (vif->monitor_flag)
8765625f965SAjay Singh wilc_wfi_monitor_rx(wilc->monitor_dev, buff, size);
8775625f965SAjay Singh }
878ebfb5e8fSAlexis Lothoré srcu_read_unlock(&wilc->srcu, srcu_idx);
8795625f965SAjay Singh }
8805625f965SAjay Singh
8815625f965SAjay Singh static const struct net_device_ops wilc_netdev_ops = {
8825625f965SAjay Singh .ndo_init = mac_init_fn,
8835625f965SAjay Singh .ndo_open = wilc_mac_open,
8845625f965SAjay Singh .ndo_stop = wilc_mac_close,
885c04fabacSAjay Singh .ndo_set_mac_address = wilc_set_mac_addr,
8865625f965SAjay Singh .ndo_start_xmit = wilc_mac_xmit,
8875625f965SAjay Singh .ndo_get_stats = mac_stats,
8885625f965SAjay Singh .ndo_set_rx_mode = wilc_set_multicast_list,
8895625f965SAjay Singh };
8905625f965SAjay Singh
wilc_netdev_cleanup(struct wilc * wilc)8915625f965SAjay Singh void wilc_netdev_cleanup(struct wilc *wilc)
8925625f965SAjay Singh {
893cb5942b7SAlexis Lothoré struct wilc_vif *vif, *vif_tmp;
8945625f965SAjay Singh
8955625f965SAjay Singh if (!wilc)
8965625f965SAjay Singh return;
8975625f965SAjay Singh
8985625f965SAjay Singh if (wilc->firmware) {
8995625f965SAjay Singh release_firmware(wilc->firmware);
9005625f965SAjay Singh wilc->firmware = NULL;
9015625f965SAjay Singh }
9025625f965SAjay Singh
903cb5942b7SAlexis Lothoré list_for_each_entry_safe(vif, vif_tmp, &wilc->vif_list, list) {
9045625f965SAjay Singh mutex_lock(&wilc->vif_mutex);
9055625f965SAjay Singh list_del_rcu(&vif->list);
9065625f965SAjay Singh wilc->vif_num--;
9075625f965SAjay Singh mutex_unlock(&wilc->vif_mutex);
908ebfb5e8fSAlexis Lothoré synchronize_srcu(&wilc->srcu);
909cb5942b7SAlexis Lothoré if (vif->ndev)
910cb5942b7SAlexis Lothoré unregister_netdev(vif->ndev);
9115625f965SAjay Singh }
9125625f965SAjay Singh
913cb5942b7SAlexis Lothoré wilc_wfi_deinit_mon_interface(wilc, false);
914cb5942b7SAlexis Lothoré destroy_workqueue(wilc->hif_workqueue);
915cb5942b7SAlexis Lothoré
9165625f965SAjay Singh wilc_wlan_cfg_deinit(wilc);
9175625f965SAjay Singh wlan_deinit_locks(wilc);
9185625f965SAjay Singh wiphy_unregister(wilc->wiphy);
9195625f965SAjay Singh wiphy_free(wilc->wiphy);
9205625f965SAjay Singh }
9215625f965SAjay Singh EXPORT_SYMBOL_GPL(wilc_netdev_cleanup);
9225625f965SAjay Singh
wilc_get_available_idx(struct wilc * wl)9235625f965SAjay Singh static u8 wilc_get_available_idx(struct wilc *wl)
9245625f965SAjay Singh {
9255625f965SAjay Singh int idx = 0;
9265625f965SAjay Singh struct wilc_vif *vif;
927ebfb5e8fSAlexis Lothoré int srcu_idx;
9285625f965SAjay Singh
929ebfb5e8fSAlexis Lothoré srcu_idx = srcu_read_lock(&wl->srcu);
930059d0e38SAlexis Lothoré wilc_for_each_vif(wl, vif) {
9315625f965SAjay Singh if (vif->idx == 0)
9325625f965SAjay Singh idx = 1;
9335625f965SAjay Singh else
9345625f965SAjay Singh idx = 0;
9355625f965SAjay Singh }
936ebfb5e8fSAlexis Lothoré srcu_read_unlock(&wl->srcu, srcu_idx);
9375625f965SAjay Singh return idx;
9385625f965SAjay Singh }
9395625f965SAjay Singh
wilc_netdev_ifc_init(struct wilc * wl,const char * name,int vif_type,enum nl80211_iftype type,bool rtnl_locked)9405625f965SAjay Singh struct wilc_vif *wilc_netdev_ifc_init(struct wilc *wl, const char *name,
9415625f965SAjay Singh int vif_type, enum nl80211_iftype type,
9425625f965SAjay Singh bool rtnl_locked)
9435625f965SAjay Singh {
94483d9b54eSAjay Singh u8 mac_address[ETH_ALEN];
9455625f965SAjay Singh struct net_device *ndev;
9465625f965SAjay Singh struct wilc_vif *vif;
9475625f965SAjay Singh int ret;
9485625f965SAjay Singh
9495625f965SAjay Singh ndev = alloc_etherdev(sizeof(*vif));
9505625f965SAjay Singh if (!ndev)
9515625f965SAjay Singh return ERR_PTR(-ENOMEM);
9525625f965SAjay Singh
9535625f965SAjay Singh vif = netdev_priv(ndev);
9545625f965SAjay Singh ndev->ieee80211_ptr = &vif->priv.wdev;
9555625f965SAjay Singh strcpy(ndev->name, name);
9565625f965SAjay Singh vif->wilc = wl;
9575625f965SAjay Singh vif->ndev = ndev;
9585625f965SAjay Singh ndev->ml_priv = vif;
9595625f965SAjay Singh
9605625f965SAjay Singh ndev->netdev_ops = &wilc_netdev_ops;
9615625f965SAjay Singh
9625625f965SAjay Singh SET_NETDEV_DEV(ndev, wiphy_dev(wl->wiphy));
9635625f965SAjay Singh
9645625f965SAjay Singh vif->priv.wdev.wiphy = wl->wiphy;
9655625f965SAjay Singh vif->priv.wdev.netdev = ndev;
9665625f965SAjay Singh vif->priv.wdev.iftype = type;
9675625f965SAjay Singh vif->priv.dev = ndev;
9685625f965SAjay Singh
9695625f965SAjay Singh ndev->needs_free_netdev = true;
9705625f965SAjay Singh vif->iftype = vif_type;
9715625f965SAjay Singh vif->idx = wilc_get_available_idx(wl);
9725625f965SAjay Singh vif->mac_opened = 0;
97383d9b54eSAjay Singh
97483d9b54eSAjay Singh memcpy(mac_address, wl->nv_mac_address, ETH_ALEN);
97583d9b54eSAjay Singh /* WILC firmware uses locally administered MAC address for the
97683d9b54eSAjay Singh * second virtual interface (bit 1 of first byte set), but
97783d9b54eSAjay Singh * since it is possibly not loaded/running yet, reproduce this behavior
97883d9b54eSAjay Singh * in the driver during interface creation.
97983d9b54eSAjay Singh */
98083d9b54eSAjay Singh if (vif->idx)
98183d9b54eSAjay Singh mac_address[0] |= 0x2;
98283d9b54eSAjay Singh
98383d9b54eSAjay Singh eth_hw_addr_set(vif->ndev, mac_address);
98483d9b54eSAjay Singh
9855625f965SAjay Singh mutex_lock(&wl->vif_mutex);
9865625f965SAjay Singh list_add_tail_rcu(&vif->list, &wl->vif_list);
9875625f965SAjay Singh wl->vif_num += 1;
9885625f965SAjay Singh mutex_unlock(&wl->vif_mutex);
989*93d4e8bbSJakub Kicinski synchronize_srcu(&wl->srcu);
9905625f965SAjay Singh
9915625f965SAjay Singh if (rtnl_locked)
9925625f965SAjay Singh ret = cfg80211_register_netdevice(ndev);
9935625f965SAjay Singh else
9945625f965SAjay Singh ret = register_netdev(ndev);
9955625f965SAjay Singh
9965625f965SAjay Singh if (ret) {
9975625f965SAjay Singh ret = -EFAULT;
9986fe46d5cSAlexis Lothoré goto error_remove_vif;
9995625f965SAjay Singh }
10005625f965SAjay Singh
10015625f965SAjay Singh return vif;
100209ed8bfcSDavid Mosberger-Tang
10036fe46d5cSAlexis Lothoré error_remove_vif:
10046fe46d5cSAlexis Lothoré mutex_lock(&wl->vif_mutex);
10056fe46d5cSAlexis Lothoré list_del_rcu(&vif->list);
10066fe46d5cSAlexis Lothoré wl->vif_num -= 1;
10076fe46d5cSAlexis Lothoré mutex_unlock(&wl->vif_mutex);
1008*93d4e8bbSJakub Kicinski synchronize_srcu(&wl->srcu);
100909ed8bfcSDavid Mosberger-Tang free_netdev(ndev);
101009ed8bfcSDavid Mosberger-Tang return ERR_PTR(ret);
10115625f965SAjay Singh }
1012ec999089SAlexis Lothoré EXPORT_SYMBOL_GPL(wilc_netdev_ifc_init);
10135625f965SAjay Singh
1014c9013880SBreno Leitao MODULE_DESCRIPTION("Atmel WILC1000 core wireless driver");
10155625f965SAjay Singh MODULE_LICENSE("GPL");
1016b52b331aSAjay Singh MODULE_FIRMWARE(WILC1000_FW(WILC1000_API_VER));
1017