xref: /openbmc/linux/drivers/net/wireless/microchip/wilc1000/netdev.c (revision 5ee9cd065836e5934710ca35653bce7905add20b)
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