gpios = <&gpio5 3 GPIO_ACTIVE_HIGH>;
states = <1300000 0x1 1400000 0x0>;
};
+
+ reg_wlan_en: regulator@10 {
+ compatible = "regulator-fixed";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_wifi_en>;
+ regulator-name = "wlan";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-boot-on;
+ gpios = <&gpio5 7 0>;
+ startup-delay-us = <70000>;
+ };
};
spi4 {
MX6ULL_PAD_SNVS_TAMPER4__GPIO5_IO04 0x17059
>;
};
- };
+ pinctrl_wifi_en: pinctrlwifi {
+ fsl,pins = <
+ MX6ULL_PAD_SNVS_TAMPER6__GPIO5_IO06 0x10071
+ MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x10071
+ >;
+ };
+ };
};
};
&usdhc1 {
- pinctrl-names = "default", "state_100mhz", "state_200mhz";
- pinctrl-0 = <&pinctrl_usdhc1>;
- pinctrl-1 = <&pinctrl_usdhc1_100mhz>;
- pinctrl-2 = <&pinctrl_usdhc1_200mhz>;
- cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
- keep-power-in-suspend;
- enable-sdio-wakeup;
- vmmc-supply = <®_sd1_vmmc>;
- status = "okay";
+ /*pinctrl-names = "default", "state_100mhz", "state_200mhz";
+ pinctrl-0 = <&pinctrl_usdhc1>;
+ pinctrl-1 = <&pinctrl_usdhc1_100mhz>;
+ pinctrl-2 = <&pinctrl_usdhc1_200mhz>;*/
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usdhc1>;
+ //cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
+ //no-1-8-v;
+ keep-power-in-suspend;
+ enable-sdio-wakeup;
+ non-removable;
+ wifi-host;
+ //vmmc-supply = <®_sd1_vmmc>;
+ //vmmc-supply = <®_wlan_en>;
+ bus-width = <4>;
+ status = "okay";
+ wilc_sdio: wilc_sdio@0{
+ compatible = "microchip,wilc1000";
+ pinctrl-0 = <&pinctrl_wifi_en>;
+ status = "okay";
+ //interrupts = <5 IRQ_TYPE_EDGE_RISING>;
+ //gpio_irq = <&gpio5 5 0>;
+ gpio_reset = <&gpio5 7 0>;
+ //reset-gpios = <&gpio5 7 0>;
+ gpio_chip_en = <&gpio5 6 0>;
+ //chip_en-gpios = <&gpio5 6 0>;
+ reg = <0>;
+ bus-width = <4>;
+ };
};
&usdhc2 {
source "drivers/net/wireless/st/Kconfig"
source "drivers/net/wireless/ti/Kconfig"
source "drivers/net/wireless/zydas/Kconfig"
+source "drivers/net/wireless/mchp/Kconfig"
config PCMCIA_RAYCS
tristate "Aviator/Raytheon 2.4GHz wireless support"
obj-$(CONFIG_WLAN_VENDOR_ST) += st/
obj-$(CONFIG_WLAN_VENDOR_TI) += ti/
obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/
+obj-$(CONFIG_WLAN_VENDOR_MCHP) += mchp/
# 16-bit wireless PCMCIA client drivers
obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o
--- /dev/null
+# SPDX-License-Identifier: GPL-2.0
+config WILC
+ tristate
+
+config WLAN_VENDOR_MCHP
+ bool "Microhip devices"
+ default y
+ help
+ This adds support for WILC1000 & WILC3000 chips which support
+ IEEE 802.11n WiFi. WILC3000 is a WiFi-BT combo chip.
+
+if WLAN_VENDOR_MCHP
+
+config WILC_SDIO
+ tristate "WILC SDIO"
+ depends on CFG80211 && INET && MMC
+ select WILC
+ help
+ This module adds support for the SDIO interface of adapters using
+ WILC1000 & WILC3000 chipset. The Atmel WILC1000 SDIO is a full speed interface.
+ It meets SDIO card specification version 2.0. The interface supports
+ the 1-bit/4-bit SD transfer mode at the clock range of 0-50 MHz.
+ The host can use this interface to read and write from any register
+ within the chip as well as configure the WILC1000 for data DMA.
+ To use this interface, pin9 (SDIO_SPI_CFG) must be grounded. Select
+ this if your platform is using the SDIO bus.
+ WILC3000 additionally supports BT 4.0 and BLE modes.
+
+config WILC_SPI
+ tristate "WILC SPI"
+ depends on CFG80211 && INET && SPI
+ select WILC
+ help
+ This module adds support for the SPI interface of adapters using
+ WILC1000 & WILC3000 chipset. The Atmel WILC1000 has a Serial Peripheral
+ Interface (SPI) that operates as a SPI slave. This SPI interface can
+ be used for control and for serial I/O of 802.11 data. The SPI is a
+ full-duplex slave synchronous serial interface that is available
+ immediately following reset when pin 9 (SDIO_SPI_CFG) is tied to
+ VDDIO. Select this if your platform is using the SPI bus.
+ WILC3000 additionally supports BT 4.0 and BLE modes.
+
+config WILC_HW_OOB_INTR
+ bool "WILC out of band interrupt"
+ depends on WILC_SDIO
+ default n
+ help
+ This option enables out-of-band interrupt support for the WILC1000 &
+ WILC3000 chipset. This OOB interrupt is intended to provide a faster
+ interrupt mechanism for SDIO host controllers that don't support SDIO
+ interrupt. Select this option If the SDIO host controller in your
+ platform doesn't support SDIO time devision interrupt.
+endif
--- /dev/null
+# SPDX-License-Identifier: GPL-2.0
+ccflags-y += -I$(src)/ -DWILC_ASIC_A0 -DWILC_DEBUGFS
+ccflags-y += -DDISABLE_PWRSAVE_AND_SCAN_DURING_IP
+
+wilc-objs := wilc_wfi_cfgoperations.o wilc_netdev.o wilc_mon.o \
+ host_interface.o wilc_wlan_cfg.o wilc_debugfs.o \
+ wilc_wlan.o sysfs.o wilc_bt.o
+
+obj-$(CONFIG_WILC_SDIO) += wilc-sdio.o
+wilc-sdio-objs += $(wilc-objs)
+wilc-sdio-objs += wilc_sdio.o
+
+obj-$(CONFIG_WILC_SPI) += wilc-spi.o
+wilc-spi-objs += $(wilc-objs)
+wilc-spi-objs += wilc_spi.o
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include <linux/etherdevice.h>
+
+#include "wilc_wfi_netdevice.h"
+#include "wilc_netdev.h"
+#include "wilc_wfi_cfgoperations.h"
+
+#define WILC_HIF_SCAN_TIMEOUT_MS 5000
+#define WILC_HIF_CONNECT_TIMEOUT_MS 9500
+
+#define WILC_FALSE_FRMWR_CHANNEL 100
+#define WILC_MAX_RATES_SUPPORTED 12
+
+/* Generic success will return 0 */
+#define WILC_SUCCESS 0 /* Generic success */
+
+/* Negative numbers to indicate failures */
+/* Generic Fail */
+#define WILC_FAIL -100
+/* Busy with another operation*/
+#define WILC_BUSY -101
+/* A given argument is invalid*/
+#define WILC_INVALID_ARGUMENT -102
+/* An API request would violate the Driver state machine
+ * (i.e. to start PID while not camped)
+ */
+#define WILC_INVALID_STATE -103
+/* In copy operations if the copied data is larger than the allocated buffer*/
+#define WILC_BUFFER_OVERFLOW -104
+/* null pointer is passed or used */
+#define WILC_NULL_PTR -105
+#define WILC_EMPTY -107
+#define WILC_FULL -108
+#define WILC_TIMEOUT -109
+/* The required operation have been canceled by the user*/
+#define WILC_CANCELED -110
+/* The Loaded file is corruped or having an invalid format */
+#define WILC_INVALID_FILE -112
+/* Cant find the file to load */
+#define WILC_NOT_FOUND -113
+#define WILC_NO_MEM -114
+#define WILC_UNSUPPORTED_VERSION -115
+#define WILC_FILE_EOF -116
+
+#if KERNEL_VERSION(3, 17, 0) > LINUX_VERSION_CODE
+struct ieee80211_wmm_ac_param {
+ u8 aci_aifsn; /* AIFSN, ACM, ACI */
+ u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */
+ __le16 txop_limit;
+} __packed;
+
+struct ieee80211_wmm_param_ie {
+ u8 element_id; /* Element ID: 221 (0xdd); */
+ u8 len; /* Length: 24 */
+ u8 oui[3]; /* 00:50:f2 */
+ u8 oui_type; /* 2 */
+ u8 oui_subtype; /* 1 */
+ u8 version; /* 1 for WMM version 1.0 */
+ u8 qos_info; /* AP/STA specific QoS info */
+ u8 reserved; /* 0 */
+ /* AC_BE, AC_BK, AC_VI, AC_VO */
+ struct ieee80211_wmm_ac_param ac[4];
+} __packed;
+#endif
+
+struct send_buffered_eap {
+ void (*deliver_to_stack)(struct wilc_vif *vif, u8 *buff, u32 size,
+ u32 pkt_offset, u8 status);
+ void (*eap_buf_param)(void *priv);
+ u8 *buff;
+ unsigned int size;
+ unsigned int pkt_offset;
+ void *user_arg;
+};
+
+struct wilc_rcvd_mac_info {
+ u8 status;
+};
+
+struct wilc_set_multicast {
+ u32 enabled;
+ u32 cnt;
+ u8 *mc_list;
+};
+
+struct host_if_wowlan_trigger {
+ u8 wowlan_trigger;
+};
+
+struct bt_coex_mode {
+ u8 bt_coex;
+};
+
+struct host_if_set_ant {
+ u8 mode;
+ u8 antenna1;
+ u8 antenna2;
+ u8 gpio_mode;
+};
+
+struct wilc_del_all_sta {
+ u8 assoc_sta;
+ u8 mac[WILC_MAX_NUM_STA][ETH_ALEN];
+};
+
+struct wilc_op_mode {
+ __le32 mode;
+};
+
+struct wilc_reg_frame {
+ bool reg;
+ u8 reg_id;
+ __le16 frame_type;
+} __packed;
+
+struct wilc_drv_handler {
+ __le32 handler;
+ u8 mode;
+} __packed;
+
+struct wilc_wep_key {
+ u8 index;
+ u8 key_len;
+ u8 key[0];
+} __packed;
+
+struct wilc_sta_wpa_ptk {
+ u8 mac_addr[ETH_ALEN];
+ u8 key_len;
+ u8 key[0];
+} __packed;
+
+struct wilc_ap_wpa_ptk {
+ u8 mac_addr[ETH_ALEN];
+ u8 index;
+ u8 key_len;
+ u8 key[0];
+} __packed;
+
+struct wilc_gtk_key {
+ u8 mac_addr[ETH_ALEN];
+ u8 rsc[8];
+ u8 index;
+ u8 key_len;
+ u8 key[0];
+} __packed;
+
+union wilc_message_body {
+ struct wilc_rcvd_net_info net_info;
+ struct wilc_rcvd_mac_info mac_info;
+ struct wilc_set_multicast mc_info;
+ struct wilc_remain_ch remain_on_ch;
+ char *data;
+ struct send_buffered_eap send_buff_eap;
+ struct host_if_set_ant set_ant;
+ struct host_if_wowlan_trigger wow_trigger;
+ struct bt_coex_mode bt_coex_mode;
+};
+
+struct host_if_msg {
+ union wilc_message_body body;
+ struct wilc_vif *vif;
+ struct work_struct work;
+ void (*fn)(struct work_struct *ws);
+ struct completion work_comp;
+ bool is_sync;
+};
+
+struct wilc_noa_opp_enable {
+ u8 ct_window;
+ u8 cnt;
+ __le32 duration;
+ __le32 interval;
+ __le32 start_time;
+} __packed;
+
+struct wilc_noa_opp_disable {
+ u8 cnt;
+ __le32 duration;
+ __le32 interval;
+ __le32 start_time;
+} __packed;
+
+struct wilc_join_bss_param {
+ char ssid[IEEE80211_MAX_SSID_LEN];
+ u8 ssid_terminator;
+ u8 bss_type;
+ u8 ch;
+ __le16 cap_info;
+ u8 sa[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ __le16 beacon_period;
+ u8 dtim_period;
+ u8 supp_rates[WILC_MAX_RATES_SUPPORTED + 1];
+ u8 wmm_cap;
+ u8 uapsd_cap;
+ u8 ht_capable;
+ u8 rsn_found;
+ u8 rsn_grp_policy;
+ u8 mode_802_11i;
+ u8 p_suites[3];
+ u8 akm_suites[3];
+ u8 rsn_cap[2];
+ u8 noa_enabled;
+ __le32 tsf_lo;
+ u8 idx;
+ u8 opp_enabled;
+ union {
+ struct wilc_noa_opp_disable opp_dis;
+ struct wilc_noa_opp_enable opp_en;
+ };
+} __packed;
+
+/* 'msg' should be free by the caller for syc */
+static struct host_if_msg*
+wilc_alloc_work(struct wilc_vif *vif, void (*work_fun)(struct work_struct *),
+ bool is_sync)
+{
+ struct host_if_msg *msg;
+
+ if (!work_fun)
+ return ERR_PTR(-EINVAL);
+
+ msg = kzalloc(sizeof(*msg), GFP_ATOMIC);
+ if (!msg)
+ return ERR_PTR(-ENOMEM);
+ msg->fn = work_fun;
+ msg->vif = vif;
+ msg->is_sync = is_sync;
+ if (is_sync)
+ init_completion(&msg->work_comp);
+
+ return msg;
+}
+
+static int wilc_enqueue_work(struct host_if_msg *msg)
+{
+ INIT_WORK(&msg->work, msg->fn);
+
+ if (!msg->vif || !msg->vif->wilc || !msg->vif->wilc->hif_workqueue)
+ return -EINVAL;
+
+ if (!queue_work(msg->vif->wilc->hif_workqueue, &msg->work))
+ return -EINVAL;
+
+ return 0;
+}
+
+/* The idx starts from 0 to (NUM_CONCURRENT_IFC - 1), but 0 index used as
+ * special purpose in wilc device, so we add 1 to the index to starts from 1.
+ * As a result, the returned index will be 1 to NUM_CONCURRENT_IFC.
+ */
+int wilc_get_vif_idx(struct wilc_vif *vif)
+{
+ return vif->idx + 1;
+}
+
+/* We need to minus 1 from idx which is from wilc device to get real index
+ * of wilc->vif[], because we add 1 when pass to wilc device in the function
+ * wilc_get_vif_idx.
+ * As a result, the index should be between 0 and (NUM_CONCURRENT_IFC - 1).
+ */
+static struct wilc_vif *wilc_get_vif_from_idx(struct wilc *wilc, int idx)
+{
+ int index = idx - 1;
+
+ if (index < 0 || index >= WILC_NUM_CONCURRENT_IFC)
+ return NULL;
+
+ return wilc->vif[index];
+}
+
+static void handle_send_buffered_eap(struct work_struct *work)
+{
+ struct host_if_msg *msg = container_of(work, struct host_if_msg, work);
+ struct wilc_vif *vif = msg->vif;
+ struct send_buffered_eap *hif_buff_eap = &msg->body.send_buff_eap;
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG, "Sending bufferd eapol to WPAS\n");
+ if (!hif_buff_eap->buff)
+ goto out;
+
+ if (hif_buff_eap->deliver_to_stack)
+ hif_buff_eap->deliver_to_stack(vif, hif_buff_eap->buff,
+ hif_buff_eap->size,
+ hif_buff_eap->pkt_offset,
+ PKT_STATUS_BUFFERED);
+ if (hif_buff_eap->eap_buf_param)
+ hif_buff_eap->eap_buf_param(hif_buff_eap->user_arg);
+
+ if (hif_buff_eap->buff != NULL) {
+ kfree(hif_buff_eap->buff);
+ hif_buff_eap->buff = NULL;
+ }
+
+out:
+ kfree(msg);
+}
+
+int wilc_scan(struct wilc_vif *vif, u8 scan_source, u8 scan_type,
+ u8 *ch_freq_list, u8 ch_list_len,
+ void (*scan_result_fn)(enum scan_event,
+ struct wilc_rcvd_net_info *, void *),
+ void *user_arg, struct cfg80211_scan_request *request)
+{
+ int result = 0;
+ struct wid wid_list[5];
+ u32 index = 0;
+ u32 i, scan_timeout;
+ u8 *buffer;
+ u8 valuesize = 0;
+ u8 *search_ssid_vals = NULL;
+ struct host_if_drv *hif_drv = vif->hif_drv;
+ struct host_if_drv *hif_drv_p2p = get_drv_hndl_by_ifc(vif->wilc,
+ WILC_P2P_IFC);
+ struct host_if_drv *hif_drv_wlan = get_drv_hndl_by_ifc(vif->wilc,
+ WILC_WLAN_IFC);
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG, "Setting SCAN params\n");
+ PRINT_INFO(vif->ndev, HOSTINF_DBG, "Scanning: In [%d] state\n",
+ hif_drv->hif_state);
+
+ if (hif_drv_p2p != NULL) {
+ if (hif_drv_p2p->hif_state != HOST_IF_IDLE &&
+ hif_drv_p2p->hif_state != HOST_IF_CONNECTED) {
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Don't scan. P2P_IFC is in state [%d]\n",
+ hif_drv_p2p->hif_state);
+ result = -EBUSY;
+ goto error;
+ }
+ }
+
+ if (hif_drv_wlan != NULL) {
+ if (hif_drv_wlan->hif_state != HOST_IF_IDLE &&
+ hif_drv_wlan->hif_state != HOST_IF_CONNECTED) {
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Don't scan. WLAN_IFC is in state [%d]\n",
+ hif_drv_wlan->hif_state);
+ result = -EBUSY;
+ goto error;
+ }
+ }
+ if (vif->connecting) {
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Don't do scan in (CONNECTING) state\n");
+ result = -EBUSY;
+ goto error;
+ }
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+ if (vif->obtaining_ip) {
+ PRINT_ER(vif->ndev, "Don't do obss scan\n");
+ result = -EBUSY;
+ goto error;
+ }
+#endif
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG, "Setting SCAN params\n");
+ hif_drv->usr_scan_req.ch_cnt = 0;
+
+ if (request->n_ssids) {
+ for (i = 0; i < request->n_ssids; i++)
+ valuesize += ((request->ssids[i].ssid_len) + 1);
+ search_ssid_vals = kmalloc(valuesize + 1, GFP_KERNEL);
+ if (search_ssid_vals) {
+ wid_list[index].id = WID_SSID_PROBE_REQ;
+ wid_list[index].type = WID_STR;
+ wid_list[index].val = search_ssid_vals;
+ buffer = wid_list[index].val;
+
+ *buffer++ = request->n_ssids;
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG,
+ "In Handle_ProbeRequest number of ssid %d\n",
+ request->n_ssids);
+ for (i = 0; i < request->n_ssids; i++) {
+ *buffer++ = request->ssids[i].ssid_len;
+ memcpy(buffer, request->ssids[i].ssid,
+ request->ssids[i].ssid_len);
+ buffer += request->ssids[i].ssid_len;
+ }
+ wid_list[index].size = (s32)(valuesize + 1);
+ index++;
+ }
+ }
+
+ wid_list[index].id = WID_INFO_ELEMENT_PROBE;
+ wid_list[index].type = WID_BIN_DATA;
+ wid_list[index].val = (s8 *)request->ie;
+ wid_list[index].size = request->ie_len;
+ index++;
+
+ wid_list[index].id = WID_SCAN_TYPE;
+ wid_list[index].type = WID_CHAR;
+ wid_list[index].size = sizeof(char);
+ wid_list[index].val = (s8 *)&scan_type;
+ index++;
+
+#if KERNEL_VERSION(4, 8, 0) > LINUX_VERSION_CODE
+ scan_timeout = WILC_HIF_SCAN_TIMEOUT_MS;
+#else
+ if (scan_type == WILC_FW_PASSIVE_SCAN && request->duration) {
+ wid_list[index].id = WID_PASSIVE_SCAN_TIME;
+ wid_list[index].type = WID_SHORT;
+ wid_list[index].size = sizeof(u16);
+ wid_list[index].val = (s8 *)&request->duration;
+ index++;
+
+ scan_timeout = (request->duration * ch_list_len) + 500;
+ } else {
+ scan_timeout = WILC_HIF_SCAN_TIMEOUT_MS;
+ }
+#endif
+ wid_list[index].id = WID_SCAN_CHANNEL_LIST;
+ wid_list[index].type = WID_BIN_DATA;
+
+ if (ch_freq_list && ch_list_len > 0) {
+ for (i = 0; i < ch_list_len; i++) {
+ if (ch_freq_list[i] > 0)
+ ch_freq_list[i] -= 1;
+ }
+ }
+
+ wid_list[index].val = ch_freq_list;
+ wid_list[index].size = ch_list_len;
+ index++;
+
+ wid_list[index].id = WID_START_SCAN_REQ;
+ wid_list[index].type = WID_CHAR;
+ wid_list[index].size = sizeof(char);
+ wid_list[index].val = (s8 *)&scan_source;
+ index++;
+
+ hif_drv->usr_scan_req.scan_result = scan_result_fn;
+ hif_drv->usr_scan_req.arg = user_arg;
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, wid_list,
+ index,
+ wilc_get_vif_idx(vif));
+ if (result) {
+ PRINT_ER(vif->ndev, "Failed to send scan parameters\n");
+ goto error;
+ } else {
+ hif_drv->scan_timer_vif = vif;
+ PRINT_INFO(vif->ndev, HOSTINF_DBG,
+ ">> Starting the SCAN timer\n");
+#if KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE
+ hif_drv->scan_timer.data = (unsigned long)hif_drv;
+#endif
+ mod_timer(&hif_drv->scan_timer,
+ jiffies + msecs_to_jiffies(scan_timeout));
+ }
+
+error:
+
+ kfree(search_ssid_vals);
+
+ return result;
+}
+
+s32 handle_scan_done(struct wilc_vif *vif, enum scan_event evt)
+{
+ s32 result = 0;
+ u8 abort_running_scan;
+ struct wid wid;
+ struct host_if_drv *hif_drv = vif->hif_drv;
+ struct wilc_user_scan_req *scan_req;
+ u8 null_bssid[6] = {0};
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG, "handling scan done\n");
+
+ if (!hif_drv) {
+ PRINT_ER(vif->ndev, "hif driver is NULL\n");
+ return result;
+ }
+
+ if (evt == SCAN_EVENT_DONE) {
+ if (memcmp(hif_drv->assoc_bssid, null_bssid, ETH_ALEN) == 0)
+ hif_drv->hif_state = HOST_IF_IDLE;
+ else
+ hif_drv->hif_state = HOST_IF_CONNECTED;
+ } else if (evt == SCAN_EVENT_ABORTED) {
+ PRINT_INFO(vif->ndev, GENERIC_DBG, "Abort running scan\n");
+ abort_running_scan = 1;
+ wid.id = WID_ABORT_RUNNING_SCAN;
+ wid.type = WID_CHAR;
+ wid.val = (s8 *)&abort_running_scan;
+ wid.size = sizeof(char);
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+
+ if (result) {
+ PRINT_ER(vif->ndev, "Failed to set abort running\n");
+ result = -EFAULT;
+ }
+ }
+
+ scan_req = &hif_drv->usr_scan_req;
+ if (scan_req->scan_result) {
+ scan_req->scan_result(evt, NULL, scan_req->arg);
+ scan_req->scan_result = NULL;
+ }
+
+ return result;
+}
+
+static int wilc_send_connect_wid(struct wilc_vif *vif)
+{
+ int result = 0;
+ struct wid wid_list[4];
+ u32 wid_cnt = 0;
+ struct host_if_drv *hif_drv = vif->hif_drv;
+ struct wilc_conn_info *conn_attr = &hif_drv->conn_info;
+ struct wilc_join_bss_param *bss_param = hif_drv->conn_info.param;
+ struct host_if_drv *hif_drv_p2p = get_drv_hndl_by_ifc(vif->wilc,
+ WILC_P2P_IFC);
+ struct host_if_drv *hif_drv_wlan = get_drv_hndl_by_ifc(vif->wilc,
+ WILC_WLAN_IFC);
+
+ if (hif_drv_p2p != NULL) {
+ if (hif_drv_p2p->hif_state == HOST_IF_SCANNING) {
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Don't scan. P2P_IFC is in state [%d]\n",
+ hif_drv_p2p->hif_state);
+ result = -EFAULT;
+ goto error;
+ }
+ }
+ if (hif_drv_wlan != NULL) {
+ if (hif_drv_wlan->hif_state == HOST_IF_SCANNING) {
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Don't scan. WLAN_IFC is in state [%d]\n",
+ hif_drv_wlan->hif_state);
+ result = -EFAULT;
+ goto error;
+ }
+ }
+
+ wid_list[wid_cnt].id = WID_INFO_ELEMENT_ASSOCIATE;
+ wid_list[wid_cnt].type = WID_BIN_DATA;
+ wid_list[wid_cnt].val = conn_attr->req_ies;
+ wid_list[wid_cnt].size = conn_attr->req_ies_len;
+ wid_cnt++;
+
+ wid_list[wid_cnt].id = WID_11I_MODE;
+ wid_list[wid_cnt].type = WID_CHAR;
+ wid_list[wid_cnt].size = sizeof(char);
+ wid_list[wid_cnt].val = (s8 *)&conn_attr->security;
+ wid_cnt++;
+
+ PRINT_D(vif->ndev, HOSTINF_DBG, "Encrypt Mode = %x\n",
+ conn_attr->security);
+ wid_list[wid_cnt].id = WID_AUTH_TYPE;
+ wid_list[wid_cnt].type = WID_CHAR;
+ wid_list[wid_cnt].size = sizeof(char);
+ wid_list[wid_cnt].val = (s8 *)&conn_attr->auth_type;
+ wid_cnt++;
+
+ PRINT_D(vif->ndev, HOSTINF_DBG, "Authentication Type = %x\n",
+ conn_attr->auth_type);
+ PRINT_INFO(vif->ndev, HOSTINF_DBG,
+ "Connecting to network on channel %d\n", conn_attr->ch);
+
+ wid_list[wid_cnt].id = WID_JOIN_REQ_EXTENDED;
+ wid_list[wid_cnt].type = WID_STR;
+ wid_list[wid_cnt].size = sizeof(*bss_param);
+ wid_list[wid_cnt].val = (u8 *)bss_param;
+ wid_cnt++;
+
+ PRINT_INFO(vif->ndev, GENERIC_DBG, "send HOST_IF_WAITING_CONN_RESP\n");
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, wid_list,
+ wid_cnt,
+ wilc_get_vif_idx(vif));
+ if (result) {
+ PRINT_ER(vif->ndev, "failed to send config packet\n");
+ goto error;
+ } else {
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "set HOST_IF_WAITING_CONN_RESP\n");
+ hif_drv->hif_state = HOST_IF_WAITING_CONN_RESP;
+ }
+
+ return 0;
+
+error:
+
+ kfree(conn_attr->req_ies);
+ conn_attr->req_ies = NULL;
+
+ return result;
+}
+
+static void handle_connect_timeout(struct work_struct *work)
+{
+ struct host_if_msg *msg = container_of(work, struct host_if_msg, work);
+ struct wilc_vif *vif = msg->vif;
+ int result;
+ struct wid wid;
+ u16 dummy_reason_code = 0;
+ struct host_if_drv *hif_drv = vif->hif_drv;
+
+ if (!hif_drv) {
+ PRINT_ER(vif->ndev, "hif driver is NULL\n");
+ goto out;
+ }
+
+ hif_drv->hif_state = HOST_IF_IDLE;
+
+ if (hif_drv->conn_info.conn_result) {
+ hif_drv->conn_info.conn_result(EVENT_CONN_RESP,
+ WILC_MAC_STATUS_DISCONNECTED,
+ hif_drv->conn_info.arg);
+
+ } else {
+ PRINT_ER(vif->ndev, "conn_result is NULL\n");
+ }
+
+ wid.id = WID_DISCONNECT;
+ wid.type = WID_CHAR;
+ wid.val = (s8 *)&dummy_reason_code;
+ wid.size = sizeof(char);
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG, "Sending disconnect request\n");
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ if (result)
+ PRINT_ER(vif->ndev, "Failed to send disconect\n");
+
+ hif_drv->conn_info.req_ies_len = 0;
+ kfree(hif_drv->conn_info.req_ies);
+ hif_drv->conn_info.req_ies = NULL;
+
+out:
+ kfree(msg);
+}
+
+void *wilc_parse_join_bss_param(struct cfg80211_bss *bss,
+ struct cfg80211_crypto_settings *crypto)
+{
+ struct wilc_join_bss_param *param;
+ struct ieee80211_p2p_noa_attr noa_attr;
+ u8 rates_len = 0;
+ const u8 *tim_elm, *ssid_elm, *rates_ie, *supp_rates_ie;
+ const u8 *ht_ie, *wpa_ie, *wmm_ie, *rsn_ie;
+ int ret;
+ const struct cfg80211_bss_ies *ies = rcu_dereference(bss->ies);
+
+ param = kzalloc(sizeof(*param), GFP_KERNEL);
+ if (!param)
+ return NULL;
+
+ param->beacon_period = cpu_to_le16(bss->beacon_interval);
+ param->cap_info = cpu_to_le16(bss->capability);
+ param->bss_type = WILC_FW_BSS_TYPE_INFRA;
+ param->ch = ieee80211_frequency_to_channel(bss->channel->center_freq);
+ ether_addr_copy(param->bssid, bss->bssid);
+
+ ssid_elm = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
+ if (ssid_elm) {
+ if (ssid_elm[1] <= IEEE80211_MAX_SSID_LEN)
+ memcpy(param->ssid, ssid_elm + 2, ssid_elm[1]);
+ }
+
+ tim_elm = cfg80211_find_ie(WLAN_EID_TIM, ies->data, ies->len);
+ if (tim_elm && tim_elm[1] >= 2)
+ param->dtim_period = tim_elm[3];
+
+ memset(param->p_suites, 0xFF, 3);
+ memset(param->akm_suites, 0xFF, 3);
+
+ rates_ie = cfg80211_find_ie(WLAN_EID_SUPP_RATES, ies->data, ies->len);
+ if (rates_ie) {
+ rates_len = rates_ie[1];
+ param->supp_rates[0] = rates_len;
+ memcpy(¶m->supp_rates[1], rates_ie + 2, rates_len);
+ }
+
+ supp_rates_ie = cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES, ies->data,
+ ies->len);
+ if (supp_rates_ie) {
+ if (supp_rates_ie[1] > (WILC_MAX_RATES_SUPPORTED - rates_len))
+ param->supp_rates[0] = WILC_MAX_RATES_SUPPORTED;
+ else
+ param->supp_rates[0] += supp_rates_ie[1];
+
+ memcpy(¶m->supp_rates[rates_len + 1], supp_rates_ie + 2,
+ (param->supp_rates[0] - rates_len));
+ }
+
+ ht_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies->data, ies->len);
+ if (ht_ie)
+ param->ht_capable = true;
+
+ ret = cfg80211_get_p2p_attr(ies->data, ies->len,
+ IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
+ (u8 *)&noa_attr, sizeof(noa_attr));
+ if (ret > 0) {
+ param->tsf_lo = cpu_to_le32(ies->tsf);
+ param->noa_enabled = 1;
+ param->idx = noa_attr.index;
+ if (noa_attr.oppps_ctwindow & IEEE80211_P2P_OPPPS_ENABLE_BIT) {
+ param->opp_enabled = 1;
+ param->opp_en.ct_window = noa_attr.oppps_ctwindow;
+ param->opp_en.cnt = noa_attr.desc[0].count;
+ param->opp_en.duration = noa_attr.desc[0].duration;
+ param->opp_en.interval = noa_attr.desc[0].interval;
+ param->opp_en.start_time = noa_attr.desc[0].start_time;
+ } else {
+ param->opp_enabled = 0;
+ param->opp_dis.cnt = noa_attr.desc[0].count;
+ param->opp_dis.duration = noa_attr.desc[0].duration;
+ param->opp_dis.interval = noa_attr.desc[0].interval;
+ param->opp_dis.start_time = noa_attr.desc[0].start_time;
+ }
+ }
+ wmm_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WMM,
+ ies->data, ies->len);
+ if (wmm_ie) {
+ struct ieee80211_wmm_param_ie *ie;
+
+ ie = (struct ieee80211_wmm_param_ie *)wmm_ie;
+ if ((ie->oui_subtype == 0 || ie->oui_subtype == 1) &&
+ ie->version == 1) {
+ param->wmm_cap = true;
+ if (ie->qos_info & BIT(7))
+ param->uapsd_cap = true;
+ }
+ }
+
+ wpa_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPA,
+ ies->data, ies->len);
+ if (wpa_ie) {
+ param->mode_802_11i = 1;
+ param->rsn_found = true;
+ }
+
+ rsn_ie = cfg80211_find_ie(WLAN_EID_RSN, ies->data, ies->len);
+ if (rsn_ie) {
+ int offset = 8;
+
+ param->mode_802_11i = 2;
+ param->rsn_found = true;
+ //extract RSN capabilities
+ offset += (rsn_ie[offset] * 4) + 2;
+ offset += (rsn_ie[offset] * 4) + 2;
+ memcpy(param->rsn_cap, &rsn_ie[offset], 2);
+ }
+
+ if (param->rsn_found) {
+ int i;
+
+ param->rsn_grp_policy = crypto->cipher_group & 0xFF;
+ for (i = 0; i < crypto->n_ciphers_pairwise && i < 3; i++)
+ param->p_suites[i] = crypto->ciphers_pairwise[i] & 0xFF;
+
+ for (i = 0; i < crypto->n_akm_suites && i < 3; i++)
+ param->akm_suites[i] = crypto->akm_suites[i] & 0xFF;
+ }
+
+ return (void *)param;
+}
+
+static void handle_rcvd_ntwrk_info(struct work_struct *work)
+{
+ struct host_if_msg *msg = container_of(work, struct host_if_msg, work);
+ struct wilc_rcvd_net_info *rcvd_info = &msg->body.net_info;
+ struct wilc_user_scan_req *scan_req = &msg->vif->hif_drv->usr_scan_req;
+ const u8 *ch_elm;
+ u8 *ies;
+ int ies_len;
+ size_t offset;
+
+ PRINT_D(msg->vif->ndev, HOSTINF_DBG,
+ "Handling received network info\n");
+
+ if (ieee80211_is_probe_resp(rcvd_info->mgmt->frame_control))
+ offset = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+ else if (ieee80211_is_beacon(rcvd_info->mgmt->frame_control))
+ offset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+ else
+ goto done;
+
+ ies = rcvd_info->mgmt->u.beacon.variable;
+ ies_len = rcvd_info->frame_len - offset;
+ if (ies_len <= 0)
+ goto done;
+
+ PRINT_INFO(msg->vif->ndev, HOSTINF_DBG, "New network found\n");
+ /* extract the channel from recevied mgmt frame */
+ ch_elm = cfg80211_find_ie(WLAN_EID_DS_PARAMS, ies, ies_len);
+ if (ch_elm && ch_elm[1] > 0)
+ rcvd_info->ch = ch_elm[2];
+
+ if (scan_req->scan_result)
+ scan_req->scan_result(SCAN_EVENT_NETWORK_FOUND,
+ rcvd_info, scan_req->arg);
+
+done:
+ kfree(rcvd_info->mgmt);
+ kfree(msg);
+}
+
+static void host_int_get_assoc_res_info(struct wilc_vif *vif,
+ u8 *assoc_resp_info,
+ u32 max_assoc_resp_info_len,
+ u32 *rcvd_assoc_resp_info_len)
+{
+ int result;
+ struct wid wid;
+
+ wid.id = WID_ASSOC_RES_INFO;
+ wid.type = WID_STR;
+ wid.val = assoc_resp_info;
+ wid.size = max_assoc_resp_info_len;
+
+ result = wilc_send_config_pkt(vif, WILC_GET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ if (result) {
+ *rcvd_assoc_resp_info_len = 0;
+ PRINT_ER(vif->ndev, "Failed to send association response\n");
+ return;
+ }
+
+ *rcvd_assoc_resp_info_len = wid.size;
+}
+
+static s32 wilc_parse_assoc_resp_info(u8 *buffer, u32 buffer_len,
+ struct wilc_conn_info *ret_conn_info)
+{
+ u8 *ies;
+ u16 ies_len;
+ struct assoc_resp *res = (struct assoc_resp *)buffer;
+
+ ret_conn_info->status = le16_to_cpu(res->status_code);
+ if (ret_conn_info->status == WLAN_STATUS_SUCCESS) {
+ ies = &buffer[sizeof(*res)];
+ ies_len = buffer_len - sizeof(*res);
+
+ ret_conn_info->resp_ies = kmemdup(ies, ies_len, GFP_KERNEL);
+ if (!ret_conn_info->resp_ies)
+ return -ENOMEM;
+
+ ret_conn_info->resp_ies_len = ies_len;
+ }
+
+ return 0;
+}
+
+static inline void host_int_parse_assoc_resp_info(struct wilc_vif *vif,
+ u8 mac_status)
+{
+ struct host_if_drv *hif_drv = vif->hif_drv;
+ struct wilc_conn_info *conn_info = &hif_drv->conn_info;
+
+ if (mac_status == WILC_MAC_STATUS_CONNECTED) {
+ u32 assoc_resp_info_len;
+
+ memset(hif_drv->assoc_resp, 0, WILC_MAX_ASSOC_RESP_FRAME_SIZE);
+
+ host_int_get_assoc_res_info(vif, hif_drv->assoc_resp,
+ WILC_MAX_ASSOC_RESP_FRAME_SIZE,
+ &assoc_resp_info_len);
+
+ PRINT_D(vif->ndev, HOSTINF_DBG,
+ "Received association response = %d\n",
+ assoc_resp_info_len);
+ if (assoc_resp_info_len != 0) {
+ s32 err = 0;
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG,
+ "Parsing association response\n");
+ err = wilc_parse_assoc_resp_info(hif_drv->assoc_resp,
+ assoc_resp_info_len,
+ conn_info);
+ if (err)
+ PRINT_ER(vif->ndev,
+ "wilc_parse_assoc_resp_info() returned error %d\n",
+ err);
+ }
+ }
+
+ del_timer(&hif_drv->connect_timer);
+ conn_info->conn_result(EVENT_CONN_RESP, mac_status, conn_info->arg);
+
+ if (mac_status == WILC_MAC_STATUS_CONNECTED &&
+ conn_info->status == WLAN_STATUS_SUCCESS) {
+ PRINT_INFO(vif->ndev, HOSTINF_DBG,
+ "MAC status : CONNECTED and Connect Status : Successful\n");
+ hif_drv->hif_state = HOST_IF_CONNECTED;
+ ether_addr_copy(hif_drv->assoc_bssid, conn_info->bssid);
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+ handle_pwrsave_for_IP(vif, IP_STATE_OBTAINING);
+#endif
+ } else {
+ PRINT_INFO(vif->ndev, HOSTINF_DBG,
+ "MAC status : %d and Connect Status : %d\n",
+ mac_status, conn_info->status);
+ hif_drv->hif_state = HOST_IF_IDLE;
+ }
+
+ kfree(conn_info->resp_ies);
+ conn_info->resp_ies = NULL;
+ conn_info->resp_ies_len = 0;
+
+ kfree(conn_info->req_ies);
+ conn_info->req_ies = NULL;
+ conn_info->req_ies_len = 0;
+}
+
+static inline void host_int_handle_disconnect(struct wilc_vif *vif)
+{
+ struct host_if_drv *hif_drv = vif->hif_drv;
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG,
+ "Received WILC_MAC_STATUS_DISCONNECTED from the FW\n");
+ if (hif_drv->usr_scan_req.scan_result) {
+ PRINT_INFO(vif->ndev, HOSTINF_DBG,
+ "\n\n<< Abort the running OBSS Scan >>\n\n");
+ del_timer(&hif_drv->scan_timer);
+ handle_scan_done(vif, SCAN_EVENT_ABORTED);
+ }
+
+ if (hif_drv->conn_info.conn_result) {
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+ handle_pwrsave_for_IP(vif, IP_STATE_DEFAULT);
+#endif
+
+ hif_drv->conn_info.conn_result(EVENT_DISCONN_NOTIF,
+ 0, hif_drv->conn_info.arg);
+ } else {
+ PRINT_ER(vif->ndev, "Connect result NULL\n");
+ }
+
+ eth_zero_addr(hif_drv->assoc_bssid);
+
+ hif_drv->conn_info.req_ies_len = 0;
+ kfree(hif_drv->conn_info.req_ies);
+ hif_drv->conn_info.req_ies = NULL;
+ hif_drv->hif_state = HOST_IF_IDLE;
+}
+
+static void handle_rcvd_gnrl_async_info(struct work_struct *work)
+{
+ struct host_if_msg *msg = container_of(work, struct host_if_msg, work);
+ struct wilc_vif *vif = msg->vif;
+ struct wilc_rcvd_mac_info *mac_info = &msg->body.mac_info;
+ struct host_if_drv *hif_drv = vif->hif_drv;
+
+ if (!hif_drv) {
+ netdev_err(vif->ndev, "%s: hif driver is NULL\n", __func__);
+ goto free_msg;
+ }
+
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Current State = %d,Received state = %d\n",
+ hif_drv->hif_state, mac_info->status);
+
+ if (!hif_drv->conn_info.conn_result) {
+ PRINT_ER(vif->ndev, "conn_result is NULL\n");
+ goto free_msg;
+ }
+ if (hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP) {
+ host_int_parse_assoc_resp_info(vif, mac_info->status);
+ } else if (mac_info->status == WILC_MAC_STATUS_DISCONNECTED) {
+ if (hif_drv->hif_state == HOST_IF_CONNECTED) {
+ host_int_handle_disconnect(vif);
+ } else if (hif_drv->usr_scan_req.scan_result) {
+ PRINT_WRN(vif->ndev, HOSTINF_DBG,
+ "Received WILC_MAC_STATUS_DISCONNECTED. Abort the running Scan");
+ del_timer(&hif_drv->scan_timer);
+ handle_scan_done(vif, SCAN_EVENT_ABORTED);
+ }
+ }
+
+free_msg:
+ kfree(msg);
+}
+
+int wilc_disconnect(struct wilc_vif *vif)
+{
+ struct wid wid;
+ struct host_if_drv *hif_drv = vif->hif_drv;
+ struct wilc_user_scan_req *scan_req;
+ struct wilc_conn_info *conn_info;
+ int result;
+ u16 dummy_reason_code = 0;
+ struct host_if_drv *hif_drv_p2p = get_drv_hndl_by_ifc(vif->wilc,
+ WILC_P2P_IFC);
+ struct host_if_drv *hif_drv_wlan = get_drv_hndl_by_ifc(vif->wilc,
+ WILC_WLAN_IFC);
+
+ if (hif_drv_wlan != NULL) {
+ if (hif_drv_wlan->hif_state == HOST_IF_SCANNING) {
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Abort Scan. WLAN_IFC is in state [%d]\n",
+ hif_drv_wlan->hif_state);
+ del_timer(&hif_drv_wlan->scan_timer);
+ handle_scan_done(vif, SCAN_EVENT_ABORTED);
+ }
+ }
+ if (hif_drv_p2p != NULL) {
+ if (hif_drv_p2p->hif_state == HOST_IF_SCANNING) {
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Abort Scan. P2P_IFC is in state [%d]\n",
+ hif_drv_p2p->hif_state);
+ del_timer(&hif_drv_p2p->scan_timer);
+ handle_scan_done(vif, SCAN_EVENT_ABORTED);
+ }
+ }
+ wid.id = WID_DISCONNECT;
+ wid.type = WID_CHAR;
+ wid.val = (s8 *)&dummy_reason_code;
+ wid.size = sizeof(char);
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG, "Sending disconnect request\n");
+
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+ handle_pwrsave_for_IP(vif, IP_STATE_DEFAULT);
+#endif
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+
+ if (result) {
+ PRINT_ER(vif->ndev, "Failed to send disconnect\n");
+ return -ENOMEM;
+ }
+
+ scan_req = &hif_drv->usr_scan_req;
+ conn_info = &hif_drv->conn_info;
+
+ if (scan_req->scan_result) {
+ del_timer(&hif_drv->scan_timer);
+ scan_req->scan_result(SCAN_EVENT_ABORTED, NULL, scan_req->arg);
+ scan_req->scan_result = NULL;
+ }
+
+ if (conn_info->conn_result) {
+ if (hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP) {
+ PRINT_INFO(vif->ndev, HOSTINF_DBG,
+ "supplicant requested disconnection\n");
+ del_timer(&hif_drv->connect_timer);
+ conn_info->conn_result(EVENT_CONN_RESP,
+ WILC_MAC_STATUS_DISCONNECTED,
+ conn_info->arg);
+
+ } else if (hif_drv->hif_state == HOST_IF_CONNECTED) {
+ conn_info->conn_result(EVENT_DISCONN_NOTIF,
+ WILC_MAC_STATUS_DISCONNECTED,
+ conn_info->arg);
+ }
+ } else {
+ PRINT_ER(vif->ndev, "conn_result = NULL\n");
+ }
+
+ hif_drv->hif_state = HOST_IF_IDLE;
+
+ eth_zero_addr(hif_drv->assoc_bssid);
+
+ conn_info->req_ies_len = 0;
+ kfree(conn_info->req_ies);
+ conn_info->req_ies = NULL;
+
+ return 0;
+}
+
+void wilc_resolve_disconnect_aberration(struct wilc_vif *vif)
+{
+ if (!vif->hif_drv)
+ return;
+ if (vif->hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP ||
+ vif->hif_drv->hif_state == HOST_IF_CONNECTING) {
+ PRINT_INFO(vif->ndev, HOSTINF_DBG,
+ "\n\n<< correcting Supplicant state machine >>\n\n");
+ wilc_disconnect(vif);
+ }
+}
+
+int wilc_get_statistics(struct wilc_vif *vif, struct rf_info *stats)
+{
+ struct wid wid_list[5];
+ u32 wid_cnt = 0, result;
+
+ wid_list[wid_cnt].id = WID_LINKSPEED;
+ wid_list[wid_cnt].type = WID_CHAR;
+ wid_list[wid_cnt].size = sizeof(char);
+ wid_list[wid_cnt].val = (s8 *)&stats->link_speed;
+ wid_cnt++;
+
+ wid_list[wid_cnt].id = WID_RSSI;
+ wid_list[wid_cnt].type = WID_CHAR;
+ wid_list[wid_cnt].size = sizeof(char);
+ wid_list[wid_cnt].val = (s8 *)&stats->rssi;
+ wid_cnt++;
+
+ wid_list[wid_cnt].id = WID_SUCCESS_FRAME_COUNT;
+ wid_list[wid_cnt].type = WID_INT;
+ wid_list[wid_cnt].size = sizeof(u32);
+ wid_list[wid_cnt].val = (s8 *)&stats->tx_cnt;
+ wid_cnt++;
+
+ wid_list[wid_cnt].id = WID_RECEIVED_FRAGMENT_COUNT;
+ wid_list[wid_cnt].type = WID_INT;
+ wid_list[wid_cnt].size = sizeof(u32);
+ wid_list[wid_cnt].val = (s8 *)&stats->rx_cnt;
+ wid_cnt++;
+
+ wid_list[wid_cnt].id = WID_FAILED_COUNT;
+ wid_list[wid_cnt].type = WID_INT;
+ wid_list[wid_cnt].size = sizeof(u32);
+ wid_list[wid_cnt].val = (s8 *)&stats->tx_fail_cnt;
+ wid_cnt++;
+
+ result = wilc_send_config_pkt(vif, WILC_GET_CFG, wid_list,
+ wid_cnt,
+ wilc_get_vif_idx(vif));
+
+ if (result) {
+ PRINT_ER(vif->ndev, "Failed to send scan parameters\n");
+ return result;
+ }
+
+ if (stats->link_speed > TCP_ACK_FILTER_LINK_SPEED_THRESH &&
+ stats->link_speed != DEFAULT_LINK_SPEED) {
+ PRINT_INFO(vif->ndev, HOSTINF_DBG, "Enable TCP filter\n");
+ wilc_enable_tcp_ack_filter(vif, true);
+ } else if (stats->link_speed != DEFAULT_LINK_SPEED) {
+ PRINT_INFO(vif->ndev, HOSTINF_DBG, "Disable TCP filter %d\n",
+ stats->link_speed);
+ wilc_enable_tcp_ack_filter(vif, false);
+ }
+
+ return result;
+}
+
+static void handle_get_statistics(struct work_struct *work)
+{
+ struct host_if_msg *msg = container_of(work, struct host_if_msg, work);
+ struct wilc_vif *vif = msg->vif;
+ struct rf_info *stats = (struct rf_info *)msg->body.data;
+
+ wilc_get_statistics(vif, stats);
+ kfree(msg);
+}
+
+static void wilc_hif_pack_sta_param(struct wilc_vif *vif, u8 *cur_byte,
+ const u8 *mac,
+ struct station_parameters *params)
+{
+ PRINT_INFO(vif->ndev, HOSTINF_DBG, "Packing STA params\n");
+ ether_addr_copy(cur_byte, mac);
+ cur_byte += ETH_ALEN;
+
+ put_unaligned_le16(params->aid, cur_byte);
+ cur_byte += 2;
+
+ *cur_byte++ = params->supported_rates_len;
+ if (params->supported_rates_len > 0)
+ memcpy(cur_byte, params->supported_rates,
+ params->supported_rates_len);
+ cur_byte += params->supported_rates_len;
+
+ if (params->ht_capa) {
+ *cur_byte++ = true;
+ memcpy(cur_byte, ¶ms->ht_capa,
+ sizeof(struct ieee80211_ht_cap));
+ } else {
+ *cur_byte++ = false;
+ }
+ cur_byte += sizeof(struct ieee80211_ht_cap);
+
+ put_unaligned_le16(params->sta_flags_mask, cur_byte);
+ cur_byte += 2;
+ put_unaligned_le16(params->sta_flags_set, cur_byte);
+}
+
+static int handle_remain_on_chan(struct wilc_vif *vif,
+ struct wilc_remain_ch *hif_remain_ch)
+{
+ int result;
+ u8 remain_on_chan_flag;
+ struct wid wid;
+ struct host_if_drv *hif_drv = vif->hif_drv;
+ struct host_if_drv *hif_drv_p2p = get_drv_hndl_by_ifc(vif->wilc,
+ WILC_P2P_IFC);
+ struct host_if_drv *hif_drv_wlan = get_drv_hndl_by_ifc(vif->wilc,
+ WILC_WLAN_IFC);
+
+ if (!hif_drv) {
+ PRINT_ER(vif->ndev, "Driver is null\n");
+ return -EFAULT;
+ }
+
+ if (hif_drv_p2p != NULL) {
+ if (hif_drv_p2p->hif_state == HOST_IF_SCANNING) {
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "IFC busy scanning P2P_IFC state %d\n",
+ hif_drv_p2p->hif_state);
+ return -EBUSY;
+ } else if ((hif_drv_p2p->hif_state != HOST_IF_IDLE) &&
+ (hif_drv_p2p->hif_state != HOST_IF_CONNECTED)) {
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "IFC busy connecting. P2P_IFC state %d\n",
+ hif_drv_p2p->hif_state);
+ return -EBUSY;
+ }
+ }
+ if (hif_drv_wlan != NULL) {
+ if (hif_drv_wlan->hif_state == HOST_IF_SCANNING) {
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "IFC busy scanning. WLAN_IFC state %d\n",
+ hif_drv_wlan->hif_state);
+ return -EBUSY;
+ } else if ((hif_drv_wlan->hif_state != HOST_IF_IDLE) &&
+ (hif_drv_wlan->hif_state != HOST_IF_CONNECTED)) {
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "IFC busy connecting. WLAN_IFC %d\n",
+ hif_drv_wlan->hif_state);
+ return -EBUSY;
+ }
+ }
+
+ if (vif->connecting) {
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Don't do scan in (CONNECTING) state\n");
+ return -EBUSY;
+ }
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+ if (vif->obtaining_ip) {
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Don't obss scan until IP adresss is obtained\n");
+ return -EBUSY;
+ }
+#endif
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG,
+ "Setting channel [%d] duration[%d] [%llu]\n",
+ hif_remain_ch->ch, hif_remain_ch->duration,
+ hif_remain_ch->cookie);
+ remain_on_chan_flag = true;
+ wid.id = WID_REMAIN_ON_CHAN;
+ wid.type = WID_STR;
+ wid.size = 2;
+ wid.val = kmalloc(wid.size, GFP_KERNEL);
+ if (!wid.val)
+ return -ENOMEM;
+
+ wid.val[0] = remain_on_chan_flag;
+ wid.val[1] = (s8)hif_remain_ch->ch;
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ kfree(wid.val);
+ if (result) {
+ PRINT_ER(vif->ndev, "Failed to set remain on channel\n");
+ return -EBUSY;
+ }
+
+ hif_drv->remain_on_ch.arg = hif_remain_ch->arg;
+ hif_drv->remain_on_ch.expired = hif_remain_ch->expired;
+ hif_drv->remain_on_ch.ch = hif_remain_ch->ch;
+ hif_drv->remain_on_ch.cookie = hif_remain_ch->cookie;
+ hif_drv->hif_state = HOST_IF_P2P_LISTEN;
+
+ hif_drv->remain_on_ch_timer_vif = vif;
+
+ return result;
+}
+
+static void handle_listen_state_expired(struct work_struct *work)
+{
+ struct host_if_msg *msg = container_of(work, struct host_if_msg, work);
+ struct wilc_vif *vif = msg->vif;
+ struct wilc_remain_ch *hif_remain_ch = &msg->body.remain_on_ch;
+ u8 remain_on_chan_flag;
+ struct wid wid;
+ int result;
+ struct host_if_drv *hif_drv = vif->hif_drv;
+ u8 null_bssid[6] = {0};
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG, "CANCEL REMAIN ON CHAN\n");
+
+ if (hif_drv->hif_state == HOST_IF_P2P_LISTEN) {
+ remain_on_chan_flag = false;
+ wid.id = WID_REMAIN_ON_CHAN;
+ wid.type = WID_STR;
+ wid.size = 2;
+ wid.val = kmalloc(wid.size, GFP_KERNEL);
+
+ if (!wid.val) {
+ PRINT_ER(vif->ndev, "Failed to allocate memory\n");
+ goto free_msg;
+ }
+
+ wid.val[0] = remain_on_chan_flag;
+ wid.val[1] = WILC_FALSE_FRMWR_CHANNEL;
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ kfree(wid.val);
+ if (result != 0) {
+ PRINT_ER(vif->ndev, "Failed to set remain channel\n");
+ goto free_msg;
+ }
+
+ if (hif_drv->remain_on_ch.expired)
+ hif_drv->remain_on_ch.expired(hif_drv->remain_on_ch.arg,
+ hif_remain_ch->cookie);
+
+ if (memcmp(hif_drv->assoc_bssid, null_bssid, ETH_ALEN) == 0)
+ hif_drv->hif_state = HOST_IF_IDLE;
+ else
+ hif_drv->hif_state = HOST_IF_CONNECTED;
+ } else {
+ PRINT_D(vif->ndev, GENERIC_DBG, "Not in listen state\n");
+ }
+
+free_msg:
+ kfree(msg);
+}
+
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+static void listen_timer_cb(struct timer_list *t)
+#else
+static void listen_timer_cb(unsigned long arg)
+#endif
+{
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+ struct host_if_drv *hif_drv = from_timer(hif_drv, t,
+ remain_on_ch_timer);
+#else
+ struct host_if_drv *hif_drv = (struct host_if_drv *)arg;
+#endif
+ struct wilc_vif *vif = hif_drv->remain_on_ch_timer_vif;
+ int result;
+ struct host_if_msg *msg;
+
+ del_timer(&vif->hif_drv->remain_on_ch_timer);
+
+ msg = wilc_alloc_work(vif, handle_listen_state_expired, false);
+ if (IS_ERR(msg))
+ return;
+
+ msg->body.remain_on_ch.cookie = vif->hif_drv->remain_on_ch.cookie;
+
+ result = wilc_enqueue_work(msg);
+ if (result) {
+ PRINT_ER(vif->ndev, "wilc_mq_send fail\n");
+ kfree(msg);
+ }
+}
+
+static void handle_set_mcast_filter(struct work_struct *work)
+{
+ struct host_if_msg *msg = container_of(work, struct host_if_msg, work);
+ struct wilc_vif *vif = msg->vif;
+ struct wilc_set_multicast *set_mc = &msg->body.mc_info;
+ int result;
+ struct wid wid;
+ u8 *cur_byte;
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG, "Setup Multicast Filter\n");
+
+ wid.id = WID_SETUP_MULTICAST_FILTER;
+ wid.type = WID_BIN;
+ wid.size = sizeof(struct wilc_set_multicast) + (set_mc->cnt * ETH_ALEN);
+ wid.val = kmalloc(wid.size, GFP_KERNEL);
+ if (!wid.val)
+ goto error;
+
+ cur_byte = wid.val;
+ put_unaligned_le32(set_mc->enabled, cur_byte);
+ cur_byte += 4;
+
+ put_unaligned_le32(set_mc->cnt, cur_byte);
+ cur_byte += 4;
+
+ if (set_mc->cnt > 0 && set_mc->mc_list)
+ memcpy(cur_byte, set_mc->mc_list, set_mc->cnt * ETH_ALEN);
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ if (result)
+ PRINT_ER(vif->ndev, "Failed to send setup multicast\n");
+
+error:
+ kfree(set_mc->mc_list);
+ kfree(wid.val);
+ kfree(msg);
+}
+
+void wilc_set_wowlan_trigger(struct wilc_vif *vif, u8 wowlan_trigger)
+{
+ int ret;
+ struct wid wid;
+
+ wid.id = WID_WOWLAN_TRIGGER;
+ wid.type = WID_CHAR;
+ wid.val = &wowlan_trigger;
+ wid.size = sizeof(s8);
+
+ ret = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+
+ if (ret)
+ PRINT_ER(vif->ndev,
+ "Failed to send wowlan trigger config packet\n");
+}
+
+static void handle_scan_timer(struct work_struct *work)
+{
+ struct host_if_msg *msg = container_of(work, struct host_if_msg, work);
+ int ret;
+
+ PRINT_INFO(msg->vif->ndev, HOSTINF_DBG, "handling scan timer\n");
+ ret = handle_scan_done(msg->vif, SCAN_EVENT_ABORTED);
+ if (ret)
+ PRINT_ER(msg->vif->ndev, "Failed to handle scan done\n");
+ kfree(msg);
+}
+
+static void handle_scan_complete(struct work_struct *work)
+{
+ struct host_if_msg *msg = container_of(work, struct host_if_msg, work);
+
+ del_timer(&msg->vif->hif_drv->scan_timer);
+ PRINT_INFO(msg->vif->ndev, HOSTINF_DBG, "scan completed\n");
+
+ handle_scan_done(msg->vif, SCAN_EVENT_DONE);
+
+ kfree(msg);
+}
+
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+static void timer_scan_cb(struct timer_list *t)
+#else
+static void timer_scan_cb(unsigned long arg)
+#endif
+{
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+ struct host_if_drv *hif_drv = from_timer(hif_drv, t, scan_timer);
+#else
+ struct host_if_drv *hif_drv = (struct host_if_drv *)arg;
+#endif
+ struct wilc_vif *vif = hif_drv->scan_timer_vif;
+ struct host_if_msg *msg;
+ int result;
+
+ msg = wilc_alloc_work(vif, handle_scan_timer, false);
+ if (IS_ERR(msg))
+ return;
+
+ result = wilc_enqueue_work(msg);
+ if (result)
+ kfree(msg);
+}
+
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+static void timer_connect_cb(struct timer_list *t)
+#else
+static void timer_connect_cb(unsigned long arg)
+#endif
+{
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+ struct host_if_drv *hif_drv = from_timer(hif_drv, t, connect_timer);
+#else
+ struct host_if_drv *hif_drv = (struct host_if_drv *)arg;
+#endif
+ struct wilc_vif *vif = hif_drv->connect_timer_vif;
+ struct host_if_msg *msg;
+ int result;
+
+ msg = wilc_alloc_work(vif, handle_connect_timeout, false);
+ if (IS_ERR(msg))
+ return;
+
+ result = wilc_enqueue_work(msg);
+ if (result)
+ kfree(msg);
+}
+
+signed int wilc_send_buffered_eap(struct wilc_vif *vif,
+ void (*deliver_to_stack)(struct wilc_vif *,
+ u8 *, u32, u32, u8),
+ void (*eap_buf_param)(void *), u8 *buff,
+ unsigned int size, unsigned int pkt_offset,
+ void *user_arg)
+{
+ int result;
+ struct host_if_msg *msg;
+
+ if (!vif || !deliver_to_stack || !eap_buf_param)
+ return -EFAULT;
+
+ msg = wilc_alloc_work(vif, handle_send_buffered_eap, false);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+ msg->body.send_buff_eap.deliver_to_stack = deliver_to_stack;
+ msg->body.send_buff_eap.eap_buf_param = eap_buf_param;
+ msg->body.send_buff_eap.size = size;
+ msg->body.send_buff_eap.pkt_offset = pkt_offset;
+ msg->body.send_buff_eap.buff = kmalloc(size + pkt_offset,
+ GFP_ATOMIC);
+ memcpy(msg->body.send_buff_eap.buff, buff, size + pkt_offset);
+ msg->body.send_buff_eap.user_arg = user_arg;
+
+ result = wilc_enqueue_work(msg);
+ if (result) {
+ PRINT_ER(vif->ndev, "enqueue work failed\n");
+ kfree(msg->body.send_buff_eap.buff);
+ kfree(msg);
+ }
+ return result;
+}
+
+int wilc_remove_wep_key(struct wilc_vif *vif, u8 index)
+{
+ struct wid wid;
+ int result;
+
+ wid.id = WID_REMOVE_WEP_KEY;
+ wid.type = WID_STR;
+ wid.size = sizeof(char);
+ wid.val = &index;
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ if (result)
+ PRINT_ER(vif->ndev,
+ "Failed to send remove wep key config packet\n");
+ return result;
+}
+
+int wilc_set_wep_default_keyid(struct wilc_vif *vif, u8 index)
+{
+ struct wid wid;
+ int result;
+
+ wid.id = WID_KEY_ID;
+ wid.type = WID_CHAR;
+ wid.size = sizeof(char);
+ wid.val = &index;
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ if (result)
+ PRINT_ER(vif->ndev,
+ "Failed to send wep default key config packet\n");
+
+ return result;
+}
+
+int wilc_add_wep_key_bss_sta(struct wilc_vif *vif, const u8 *key, u8 len,
+ u8 index)
+{
+ struct wid wid;
+ int result;
+ struct wilc_wep_key *wep_key;
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG, "Handling WEP key\n");
+ wid.id = WID_ADD_WEP_KEY;
+ wid.type = WID_STR;
+ wid.size = sizeof(*wep_key) + len;
+ wep_key = kzalloc(wid.size, GFP_KERNEL);
+ if (!wep_key) {
+ PRINT_ER(vif->ndev, "No buffer to send Key\n");
+ return -ENOMEM;
+ }
+ wid.val = (u8 *)wep_key;
+
+ wep_key->index = index;
+ wep_key->key_len = len;
+ memcpy(wep_key->key, key, len);
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ if (result)
+ netdev_err(vif->ndev,
+ "Failed to add wep key config packet\n");
+
+ kfree(wep_key);
+ return result;
+}
+
+int wilc_add_wep_key_bss_ap(struct wilc_vif *vif, const u8 *key, u8 len,
+ u8 index, u8 mode, enum authtype auth_type)
+{
+ struct wid wid_list[3];
+ int result;
+ struct wilc_wep_key *wep_key;
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG, "Handling WEP key index: %d\n",
+ index);
+ wid_list[0].id = WID_11I_MODE;
+ wid_list[0].type = WID_CHAR;
+ wid_list[0].size = sizeof(char);
+ wid_list[0].val = &mode;
+
+ wid_list[1].id = WID_AUTH_TYPE;
+ wid_list[1].type = WID_CHAR;
+ wid_list[1].size = sizeof(char);
+ wid_list[1].val = (s8 *)&auth_type;
+
+ wid_list[2].id = WID_WEP_KEY_VALUE;
+ wid_list[2].type = WID_STR;
+ wid_list[2].size = sizeof(*wep_key) + len;
+ wep_key = kzalloc(wid_list[2].size, GFP_KERNEL);
+ if (!wep_key) {
+ PRINT_ER(vif->ndev, "No buffer to send Key\n");
+ return -ENOMEM;
+ }
+
+ wid_list[2].val = (u8 *)wep_key;
+
+ wep_key->index = index;
+ wep_key->key_len = len;
+ memcpy(wep_key->key, key, len);
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, wid_list,
+ ARRAY_SIZE(wid_list),
+ wilc_get_vif_idx(vif));
+ if (result)
+ PRINT_ER(vif->ndev,
+ "Failed to add wep ap key config packet\n");
+
+ kfree(wep_key);
+ return result;
+}
+
+int wilc_add_ptk(struct wilc_vif *vif, const u8 *ptk, u8 ptk_key_len,
+ const u8 *mac_addr, const u8 *rx_mic, const u8 *tx_mic,
+ u8 mode, u8 cipher_mode, u8 index)
+{
+ int result = 0;
+ u8 t_key_len = ptk_key_len + WILC_RX_MIC_KEY_LEN + WILC_TX_MIC_KEY_LEN;
+
+ if (mode == WILC_AP_MODE) {
+ struct wid wid_list[2];
+ struct wilc_ap_wpa_ptk *key_buf;
+
+ wid_list[0].id = WID_11I_MODE;
+ wid_list[0].type = WID_CHAR;
+ wid_list[0].size = sizeof(char);
+ wid_list[0].val = (s8 *)&cipher_mode;
+
+ key_buf = kzalloc(sizeof(*key_buf) + t_key_len, GFP_KERNEL);
+ if (!key_buf) {
+ PRINT_ER(vif->ndev,
+ "NO buffer to keep Key buffer - AP\n");
+ return -ENOMEM;
+ }
+ ether_addr_copy(key_buf->mac_addr, mac_addr);
+ key_buf->index = index;
+ key_buf->key_len = t_key_len;
+ memcpy(&key_buf->key[0], ptk, ptk_key_len);
+
+ if (rx_mic)
+ memcpy(&key_buf->key[ptk_key_len], rx_mic,
+ WILC_RX_MIC_KEY_LEN);
+
+ if (tx_mic)
+ memcpy(&key_buf->key[ptk_key_len + WILC_RX_MIC_KEY_LEN],
+ tx_mic, WILC_TX_MIC_KEY_LEN);
+
+ wid_list[1].id = WID_ADD_PTK;
+ wid_list[1].type = WID_STR;
+ wid_list[1].size = sizeof(*key_buf) + t_key_len;
+ wid_list[1].val = (u8 *)key_buf;
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, wid_list,
+ ARRAY_SIZE(wid_list),
+ wilc_get_vif_idx(vif));
+ kfree(key_buf);
+ } else if (mode == WILC_STATION_MODE) {
+ struct wid wid;
+ struct wilc_sta_wpa_ptk *key_buf;
+
+ key_buf = kzalloc(sizeof(*key_buf) + t_key_len, GFP_KERNEL);
+ if (!key_buf) {
+ PRINT_ER(vif->ndev,
+ "No buffer to keep Key buffer - Station\n");
+ return -ENOMEM;
+ }
+
+ ether_addr_copy(key_buf->mac_addr, mac_addr);
+ key_buf->key_len = t_key_len;
+ memcpy(&key_buf->key[0], ptk, ptk_key_len);
+
+ if (rx_mic)
+ memcpy(&key_buf->key[ptk_key_len], rx_mic,
+ WILC_RX_MIC_KEY_LEN);
+
+ if (tx_mic)
+ memcpy(&key_buf->key[ptk_key_len + WILC_RX_MIC_KEY_LEN],
+ tx_mic, WILC_TX_MIC_KEY_LEN);
+
+ wid.id = WID_ADD_PTK;
+ wid.type = WID_STR;
+ wid.size = sizeof(*key_buf) + t_key_len;
+ wid.val = (s8 *)key_buf;
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ kfree(key_buf);
+ }
+
+ return result;
+}
+
+int wilc_add_rx_gtk(struct wilc_vif *vif, const u8 *rx_gtk, u8 gtk_key_len,
+ u8 index, u32 key_rsc_len, const u8 *key_rsc,
+ const u8 *rx_mic, const u8 *tx_mic, u8 mode,
+ u8 cipher_mode)
+{
+ int result = 0;
+ struct wilc_gtk_key *gtk_key;
+ int t_key_len = gtk_key_len + WILC_RX_MIC_KEY_LEN + WILC_TX_MIC_KEY_LEN;
+
+ gtk_key = kzalloc(sizeof(*gtk_key) + t_key_len, GFP_KERNEL);
+ if (!gtk_key) {
+ PRINT_ER(vif->ndev, "No buffer to send GTK Key\n");
+ return -ENOMEM;
+ }
+
+ /* fill bssid value only in station mode */
+ if (mode == WILC_STATION_MODE &&
+ vif->hif_drv->hif_state == HOST_IF_CONNECTED)
+ memcpy(gtk_key->mac_addr, vif->hif_drv->assoc_bssid, ETH_ALEN);
+
+ if (key_rsc)
+ memcpy(gtk_key->rsc, key_rsc, 8);
+ gtk_key->index = index;
+ gtk_key->key_len = t_key_len;
+ memcpy(>k_key->key[0], rx_gtk, gtk_key_len);
+
+ if (rx_mic)
+ memcpy(>k_key->key[gtk_key_len], rx_mic, WILC_RX_MIC_KEY_LEN);
+
+ if (tx_mic)
+ memcpy(>k_key->key[gtk_key_len + WILC_RX_MIC_KEY_LEN],
+ tx_mic, WILC_TX_MIC_KEY_LEN);
+
+ if (mode == WILC_AP_MODE) {
+ struct wid wid_list[2];
+
+ wid_list[0].id = WID_11I_MODE;
+ wid_list[0].type = WID_CHAR;
+ wid_list[0].size = sizeof(char);
+ wid_list[0].val = (s8 *)&cipher_mode;
+
+ wid_list[1].id = WID_ADD_RX_GTK;
+ wid_list[1].type = WID_STR;
+ wid_list[1].size = sizeof(*gtk_key) + t_key_len;
+ wid_list[1].val = (u8 *)gtk_key;
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, wid_list,
+ ARRAY_SIZE(wid_list),
+ wilc_get_vif_idx(vif));
+ kfree(gtk_key);
+ } else if (mode == WILC_STATION_MODE) {
+ struct wid wid;
+
+ wid.id = WID_ADD_RX_GTK;
+ wid.type = WID_STR;
+ wid.size = sizeof(*gtk_key) + t_key_len;
+ wid.val = (u8 *)gtk_key;
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ kfree(gtk_key);
+ }
+
+ return result;
+}
+
+int wilc_set_pmkid_info(struct wilc_vif *vif, struct wilc_pmkid_attr *pmkid)
+{
+ struct wid wid;
+
+ wid.id = WID_PMKID_INFO;
+ wid.type = WID_STR;
+ wid.size = (pmkid->numpmkid * sizeof(struct wilc_pmkid)) + 1;
+ wid.val = (u8 *)pmkid;
+
+ return wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+}
+
+int wilc_get_mac_address(struct wilc_vif *vif, u8 *mac_addr)
+{
+ int result;
+ struct wid wid;
+
+ wid.id = WID_MAC_ADDR;
+ wid.type = WID_STR;
+ wid.size = ETH_ALEN;
+ wid.val = mac_addr;
+
+ result = wilc_send_config_pkt(vif, WILC_GET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ if (result)
+ netdev_err(vif->ndev, "Failed to get mac address\n");
+
+ return result;
+}
+
+int wilc_set_mac_address(struct wilc_vif *vif, u8 *mac_addr)
+{
+ struct wid wid;
+ int result;
+
+ wid.id = WID_MAC_ADDR;
+ wid.type = WID_STR;
+ wid.size = ETH_ALEN;
+ wid.val = mac_addr;
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ if (result)
+ PRINT_ER(vif->ndev, "Failed to set mac address\n");
+
+ return result;
+}
+
+int wilc_set_join_req(struct wilc_vif *vif, u8 *bssid, const u8 *ies,
+ size_t ies_len)
+{
+ int result;
+ struct host_if_drv *hif_drv = vif->hif_drv;
+ struct wilc_conn_info *conn_info = &hif_drv->conn_info;
+
+ if (bssid)
+ ether_addr_copy(conn_info->bssid, bssid);
+
+ if (ies) {
+ conn_info->req_ies_len = ies_len;
+ conn_info->req_ies = kmemdup(ies, ies_len, GFP_KERNEL);
+ if (!conn_info->req_ies)
+ return -ENOMEM;
+ }
+
+ result = wilc_send_connect_wid(vif);
+ if (result) {
+ PRINT_ER(vif->ndev, "Failed to send connect wid\n");
+ goto free_ies;
+ }
+
+#if KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE
+ hif_drv->connect_timer.data = (unsigned long)hif_drv;
+#endif
+ hif_drv->connect_timer_vif = vif;
+ mod_timer(&hif_drv->connect_timer,
+ jiffies + msecs_to_jiffies(WILC_HIF_CONNECT_TIMEOUT_MS));
+
+ return 0;
+
+free_ies:
+ kfree(conn_info->req_ies);
+
+ return result;
+}
+
+int wilc_set_mac_chnl_num(struct wilc_vif *vif, u8 channel)
+{
+ struct wid wid;
+ int result;
+
+ wid.id = WID_CURRENT_CHANNEL;
+ wid.type = WID_CHAR;
+ wid.size = sizeof(char);
+ wid.val = &channel;
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ if (result)
+ PRINT_ER(vif->ndev, "Failed to set channel\n");
+
+ return result;
+}
+
+int wilc_set_wfi_drv_handler(struct wilc_vif *vif, int index, u8 mode,
+ u8 ifc_id)
+{
+ struct wid wid;
+ struct host_if_drv *hif_drv = vif->hif_drv;
+ int result;
+ struct wilc_drv_handler drv;
+
+ if (!hif_drv)
+ return -EFAULT;
+
+ wid.id = WID_SET_DRV_HANDLER;
+ wid.type = WID_STR;
+ wid.size = sizeof(drv);
+ wid.val = (u8 *)&drv;
+
+ drv.handler = cpu_to_le32(index);
+ drv.mode = (ifc_id | (mode << 1));
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ hif_drv->driver_handler_id);
+ if (result)
+ PRINT_ER(vif->ndev, "Failed to set driver handler\n");
+
+ return result;
+}
+
+int wilc_set_operation_mode(struct wilc_vif *vif, u32 mode)
+{
+ struct wid wid;
+ struct wilc_op_mode op_mode;
+ int result;
+
+ wid.id = WID_SET_OPERATION_MODE;
+ wid.type = WID_INT;
+ wid.size = sizeof(op_mode);
+ wid.val = (u8 *)&op_mode;
+
+ op_mode.mode = cpu_to_le32(mode);
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ if (result)
+ PRINT_ER(vif->ndev, "Failed to set operation mode\n");
+
+ return result;
+}
+
+s32 wilc_get_inactive_time(struct wilc_vif *vif, const u8 *mac, u32 *out_val)
+{
+ struct wid wid;
+ s32 result;
+
+ wid.id = WID_SET_STA_MAC_INACTIVE_TIME;
+ wid.type = WID_STR;
+ wid.size = ETH_ALEN;
+ wid.val = kzalloc(wid.size, GFP_KERNEL);
+ if (!wid.val) {
+ PRINT_ER(vif->ndev, "Failed to allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ether_addr_copy(wid.val, mac);
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ kfree(wid.val);
+ if (result) {
+ PRINT_ER(vif->ndev, "Failed to set inactive mac\n");
+ return result;
+ }
+
+ wid.id = WID_GET_INACTIVE_TIME;
+ wid.type = WID_INT;
+ wid.val = (s8 *)out_val;
+ wid.size = sizeof(u32);
+ result = wilc_send_config_pkt(vif, WILC_GET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ if (result)
+ PRINT_ER(vif->ndev, "Failed to get inactive time\n");
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "Getting inactive time : %d\n",
+ *out_val);
+
+ return result;
+}
+
+int wilc_get_rssi(struct wilc_vif *vif, s8 *rssi_level)
+{
+ struct wid wid;
+ int result;
+
+ if (!rssi_level) {
+ PRINT_ER(vif->ndev, "RSS pointer value is null\n");
+ return -EFAULT;
+ }
+
+ wid.id = WID_RSSI;
+ wid.type = WID_CHAR;
+ wid.size = sizeof(char);
+ wid.val = rssi_level;
+ result = wilc_send_config_pkt(vif, WILC_GET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ if (result)
+ netdev_err(vif->ndev, "Failed to get RSSI value\n");
+
+ return result;
+}
+
+int wilc_get_stats_async(struct wilc_vif *vif, struct rf_info *stats)
+{
+ int result;
+ struct host_if_msg *msg;
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG, " getting async statistics\n");
+ msg = wilc_alloc_work(vif, handle_get_statistics, false);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->body.data = (char *)stats;
+
+ result = wilc_enqueue_work(msg);
+ if (result) {
+ PRINT_ER(vif->ndev, "enqueue work failed\n");
+ kfree(msg);
+ return result;
+ }
+
+ return result;
+}
+
+int wilc_hif_set_cfg(struct wilc_vif *vif, struct cfg_param_attr *param)
+{
+ struct wid wid_list[4];
+ int i = 0;
+
+ if (param->flag & WILC_CFG_PARAM_RETRY_SHORT) {
+ wid_list[i].id = WID_SHORT_RETRY_LIMIT;
+ wid_list[i].val = (s8 *)¶m->short_retry_limit;
+ wid_list[i].type = WID_SHORT;
+ wid_list[i].size = sizeof(u16);
+ i++;
+ }
+ if (param->flag & WILC_CFG_PARAM_RETRY_LONG) {
+ wid_list[i].id = WID_LONG_RETRY_LIMIT;
+ wid_list[i].val = (s8 *)¶m->long_retry_limit;
+ wid_list[i].type = WID_SHORT;
+ wid_list[i].size = sizeof(u16);
+ i++;
+ }
+ if (param->flag & WILC_CFG_PARAM_FRAG_THRESHOLD) {
+ wid_list[i].id = WID_FRAG_THRESHOLD;
+ wid_list[i].val = (s8 *)¶m->frag_threshold;
+ wid_list[i].type = WID_SHORT;
+ wid_list[i].size = sizeof(u16);
+ i++;
+ }
+ if (param->flag & WILC_CFG_PARAM_RTS_THRESHOLD) {
+ wid_list[i].id = WID_RTS_THRESHOLD;
+ wid_list[i].val = (s8 *)¶m->rts_threshold;
+ wid_list[i].type = WID_SHORT;
+ wid_list[i].size = sizeof(u16);
+ i++;
+ }
+
+ return wilc_send_config_pkt(vif, WILC_SET_CFG, wid_list,
+ i, wilc_get_vif_idx(vif));
+}
+
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+static void get_periodic_rssi(struct timer_list *t)
+#else
+static void get_periodic_rssi(unsigned long arg)
+#endif
+{
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+ struct wilc_vif *vif = from_timer(vif, t, periodic_rssi);
+#else
+ struct wilc_vif *vif = (struct wilc_vif *)arg;
+#endif
+
+ if (!vif->hif_drv) {
+ PRINT_ER(vif->ndev, "hif driver is NULL\n");
+ return;
+ }
+
+ if (vif->hif_drv->hif_state == HOST_IF_CONNECTED)
+ wilc_get_stats_async(vif, &vif->periodic_stats);
+
+ mod_timer(&vif->periodic_rssi, jiffies + msecs_to_jiffies(5000));
+}
+
+int wilc_init(struct net_device *dev, struct host_if_drv **hif_drv_handler)
+{
+ struct host_if_drv *hif_drv;
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wilc = vif->wilc;
+ int i;
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG,
+ "Initializing host interface for client %d\n",
+ wilc->clients_count + 1);
+
+ hif_drv = kzalloc(sizeof(*hif_drv), GFP_KERNEL);
+ if (!hif_drv) {
+ PRINT_ER(dev, "hif driver is NULL\n");
+ return -ENOMEM;
+ }
+ *hif_drv_handler = hif_drv;
+ for (i = 0; i < wilc->vif_num; i++)
+ if (dev == wilc->vif[i]->ndev) {
+ wilc->vif[i]->hif_drv = hif_drv;
+ hif_drv->driver_handler_id = i + 1;
+ break;
+ }
+
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+ vif->obtaining_ip = false;
+#endif
+
+ if (wilc->clients_count == 0)
+ mutex_init(&wilc->deinit_lock);
+
+ #if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+ timer_setup(&vif->periodic_rssi, get_periodic_rssi, 0);
+ #else
+ setup_timer(&vif->periodic_rssi, get_periodic_rssi,
+ (unsigned long)vif);
+ #endif
+ mod_timer(&vif->periodic_rssi,
+ jiffies + msecs_to_jiffies(5000));
+
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+ timer_setup(&hif_drv->scan_timer, timer_scan_cb, 0);
+ timer_setup(&hif_drv->connect_timer, timer_connect_cb, 0);
+ timer_setup(&hif_drv->remain_on_ch_timer, listen_timer_cb, 0);
+#else
+ setup_timer(&hif_drv->scan_timer, timer_scan_cb, 0);
+ setup_timer(&hif_drv->connect_timer, timer_connect_cb, 0);
+ setup_timer(&hif_drv->remain_on_ch_timer, listen_timer_cb, 0);
+#endif
+
+ hif_drv->hif_state = HOST_IF_IDLE;
+
+ hif_drv->p2p_timeout = 0;
+
+ wilc->clients_count++;
+
+ return 0;
+}
+
+int wilc_deinit(struct wilc_vif *vif)
+{
+ int result = 0;
+ struct host_if_drv *hif_drv = vif->hif_drv;
+
+ if (!hif_drv) {
+ PRINT_ER(vif->ndev, "hif driver is NULL\n");
+ return -EFAULT;
+ }
+
+ mutex_lock(&vif->wilc->deinit_lock);
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG,
+ "De-initializing host interface for client %d\n",
+ vif->wilc->clients_count);
+
+ del_timer_sync(&hif_drv->scan_timer);
+ del_timer_sync(&hif_drv->connect_timer);
+ del_timer_sync(&vif->periodic_rssi);
+ del_timer_sync(&hif_drv->remain_on_ch_timer);
+
+ if (hif_drv->usr_scan_req.scan_result) {
+ hif_drv->usr_scan_req.scan_result(SCAN_EVENT_ABORTED, NULL,
+ hif_drv->usr_scan_req.arg);
+ hif_drv->usr_scan_req.scan_result = NULL;
+ }
+
+ hif_drv->hif_state = HOST_IF_IDLE;
+
+ kfree(hif_drv);
+ vif->hif_drv = NULL;
+
+ vif->wilc->clients_count--;
+ mutex_unlock(&vif->wilc->deinit_lock);
+ return result;
+}
+
+void wilc_network_info_received(struct wilc *wilc, u8 *buffer, u32 length)
+{
+ int result;
+ struct host_if_msg *msg;
+ int id;
+ struct host_if_drv *hif_drv;
+ struct wilc_vif *vif;
+
+ id = get_unaligned_le32(&buffer[length - 4]);
+ vif = wilc_get_vif_from_idx(wilc, id);
+ if (!vif)
+ return;
+ hif_drv = vif->hif_drv;
+
+ if (!hif_drv) {
+ PRINT_ER(vif->ndev, "driver not init[%p]\n", hif_drv);
+ return;
+ }
+
+ msg = wilc_alloc_work(vif, handle_rcvd_ntwrk_info, false);
+ if (IS_ERR(msg))
+ return;
+
+ msg->body.net_info.frame_len = get_unaligned_le16(&buffer[6]) - 1;
+ msg->body.net_info.rssi = buffer[8];
+ msg->body.net_info.mgmt = kmemdup(&buffer[9],
+ msg->body.net_info.frame_len,
+ GFP_KERNEL);
+ if (!msg->body.net_info.mgmt) {
+ kfree(msg);
+ return;
+ }
+
+ result = wilc_enqueue_work(msg);
+ if (result) {
+ PRINT_ER(vif->ndev, "message parameters (%d)\n", result);
+ kfree(msg->body.net_info.mgmt);
+ kfree(msg);
+ }
+}
+
+void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length)
+{
+ int result;
+ struct host_if_msg *msg;
+ int id;
+ struct host_if_drv *hif_drv;
+ struct wilc_vif *vif;
+
+ mutex_lock(&wilc->deinit_lock);
+
+ id = get_unaligned_le32(&buffer[length - 4]);
+ vif = wilc_get_vif_from_idx(wilc, id);
+ if (!vif) {
+ mutex_unlock(&wilc->deinit_lock);
+ return;
+ }
+ PRINT_INFO(vif->ndev, HOSTINF_DBG,
+ "General asynchronous info packet received\n");
+
+ hif_drv = vif->hif_drv;
+
+ if (!hif_drv) {
+ PRINT_ER(vif->ndev, "hif driver is NULL\n");
+ mutex_unlock(&wilc->deinit_lock);
+ return;
+ }
+
+ if (!hif_drv->conn_info.conn_result) {
+ PRINT_ER(vif->ndev, "there is no current Connect Request\n");
+ mutex_unlock(&wilc->deinit_lock);
+ return;
+ }
+
+ msg = wilc_alloc_work(vif, handle_rcvd_gnrl_async_info, false);
+ if (IS_ERR(msg)) {
+ mutex_unlock(&wilc->deinit_lock);
+ return;
+ }
+
+ msg->body.mac_info.status = buffer[7];
+ PRINT_INFO(vif->ndev, HOSTINF_DBG,
+ "Received MAC status= %d Reason= %d Info = %d\n",
+ buffer[7], buffer[8], buffer[9]);
+ result = wilc_enqueue_work(msg);
+ if (result) {
+ PRINT_ER(vif->ndev, "enqueue work failed\n");
+ kfree(msg);
+ }
+
+ mutex_unlock(&wilc->deinit_lock);
+}
+
+void wilc_scan_complete_received(struct wilc *wilc, u8 *buffer, u32 length)
+{
+ int result;
+ int id;
+ struct host_if_drv *hif_drv;
+ struct wilc_vif *vif;
+
+ id = get_unaligned_le32(&buffer[length - 4]);
+ vif = wilc_get_vif_from_idx(wilc, id);
+ if (!vif)
+ return;
+ hif_drv = vif->hif_drv;
+ PRINT_INFO(vif->ndev, GENERIC_DBG, "Scan notification received\n");
+
+ if (!hif_drv) {
+ PRINT_ER(vif->ndev, "hif driver is NULL\n");
+ return;
+ }
+
+ if (hif_drv->usr_scan_req.scan_result) {
+ struct host_if_msg *msg;
+
+ msg = wilc_alloc_work(vif, handle_scan_complete, false);
+ if (IS_ERR(msg))
+ return;
+
+ result = wilc_enqueue_work(msg);
+ if (result) {
+ PRINT_ER(vif->ndev, "enqueue work failed\n");
+ kfree(msg);
+ }
+ }
+}
+
+int wilc_remain_on_channel(struct wilc_vif *vif, u64 cookie,
+ u32 duration, u16 chan,
+ void (*expired)(void *, u64), void *user_arg)
+{
+ struct wilc_remain_ch roc;
+ int result;
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "%s called\n", __func__);
+ roc.ch = chan;
+ roc.expired = expired;
+ roc.arg = user_arg;
+ roc.duration = duration;
+ roc.cookie = cookie;
+ result = handle_remain_on_chan(vif, &roc);
+ if (result)
+ PRINT_ER(vif->ndev, "%s: failed to set remain on channel\n",
+ __func__);
+
+ return result;
+}
+
+int wilc_listen_state_expired(struct wilc_vif *vif, u64 cookie)
+{
+ int result;
+ struct host_if_msg *msg;
+ struct host_if_drv *hif_drv = vif->hif_drv;
+
+ if (!hif_drv) {
+ PRINT_ER(vif->ndev, "hif driver is NULL\n");
+ return -EFAULT;
+ }
+
+ del_timer(&hif_drv->remain_on_ch_timer);
+
+ msg = wilc_alloc_work(vif, handle_listen_state_expired, false);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->body.remain_on_ch.cookie = cookie;
+
+ result = wilc_enqueue_work(msg);
+ if (result) {
+ PRINT_ER(vif->ndev, "enqueue work failed\n");
+ kfree(msg);
+ }
+
+ return result;
+}
+
+void wilc_frame_register(struct wilc_vif *vif, u16 frame_type, bool reg)
+{
+ struct wid wid;
+ int result;
+ struct wilc_reg_frame reg_frame;
+
+ wid.id = WID_REGISTER_FRAME;
+ wid.type = WID_STR;
+ wid.size = sizeof(reg_frame);
+ wid.val = (u8 *)®_frame;
+
+ memset(®_frame, 0x0, sizeof(reg_frame));
+ reg_frame.reg = reg;
+
+ switch (frame_type) {
+ case IEEE80211_STYPE_ACTION:
+ PRINT_INFO(vif->ndev, HOSTINF_DBG, "ACTION\n");
+ reg_frame.reg_id = WILC_FW_ACTION_FRM_IDX;
+ break;
+
+ case IEEE80211_STYPE_PROBE_REQ:
+ PRINT_INFO(vif->ndev, HOSTINF_DBG, "PROBE REQ\n");
+ reg_frame.reg_id = WILC_FW_PROBE_REQ_IDX;
+ break;
+
+ default:
+ PRINT_INFO(vif->ndev, HOSTINF_DBG, "Not valid frame type\n");
+ break;
+ }
+ reg_frame.frame_type = cpu_to_le16(frame_type);
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ if (result)
+ PRINT_ER(vif->ndev, "Failed to frame register\n");
+}
+
+int wilc_add_beacon(struct wilc_vif *vif, u32 interval, u32 dtim_period,
+ struct cfg80211_beacon_data *params)
+{
+ struct wid wid;
+ int result;
+ u8 *cur_byte;
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG,
+ "Setting adding beacon\n");
+
+ wid.id = WID_ADD_BEACON;
+ wid.type = WID_BIN;
+ wid.size = params->head_len + params->tail_len + 16;
+ wid.val = kzalloc(wid.size, GFP_KERNEL);
+ if (!wid.val) {
+ PRINT_ER(vif->ndev, "Failed to allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ cur_byte = wid.val;
+ put_unaligned_le32(interval, cur_byte);
+ cur_byte += 4;
+ put_unaligned_le32(dtim_period, cur_byte);
+ cur_byte += 4;
+ put_unaligned_le32(params->head_len, cur_byte);
+ cur_byte += 4;
+
+ if (params->head_len > 0)
+ memcpy(cur_byte, params->head, params->head_len);
+ cur_byte += params->head_len;
+
+ put_unaligned_le32(params->tail_len, cur_byte);
+ cur_byte += 4;
+
+ if (params->tail_len > 0)
+ memcpy(cur_byte, params->tail, params->tail_len);
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ if (result)
+ PRINT_ER(vif->ndev, "Failed to send add beacon\n");
+
+ kfree(wid.val);
+
+ return result;
+}
+
+int wilc_del_beacon(struct wilc_vif *vif)
+{
+ int result;
+ struct wid wid;
+ u8 del_beacon = 0;
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG,
+ "Setting deleting beacon message queue params\n");
+
+ wid.id = WID_DEL_BEACON;
+ wid.type = WID_CHAR;
+ wid.size = sizeof(char);
+ wid.val = &del_beacon;
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ if (result)
+ PRINT_ER(vif->ndev, "Failed to send delete beacon\n");
+
+ return result;
+}
+
+int wilc_add_station(struct wilc_vif *vif, const u8 *mac,
+ struct station_parameters *params)
+{
+ struct wid wid;
+ int result;
+ u8 *cur_byte;
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG,
+ "Setting adding station message queue params\n");
+
+ wid.id = WID_ADD_STA;
+ wid.type = WID_BIN;
+ wid.size = WILC_ADD_STA_LENGTH + params->supported_rates_len;
+ wid.val = kmalloc(wid.size, GFP_KERNEL);
+ if (!wid.val)
+ return -ENOMEM;
+
+ cur_byte = wid.val;
+ wilc_hif_pack_sta_param(vif, cur_byte, mac, params);
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ if (result != 0)
+ PRINT_ER(vif->ndev, "Failed to send add station\n");
+
+ kfree(wid.val);
+
+ return result;
+}
+
+int wilc_del_station(struct wilc_vif *vif, const u8 *mac_addr)
+{
+ struct wid wid;
+ int result;
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG,
+ "Setting deleting station message queue params\n");
+
+ wid.id = WID_REMOVE_STA;
+ wid.type = WID_BIN;
+ wid.size = ETH_ALEN;
+ wid.val = kzalloc(wid.size, GFP_KERNEL);
+ if (!wid.val) {
+ PRINT_ER(vif->ndev, "Failed to allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ if (!mac_addr)
+ eth_broadcast_addr(wid.val);
+ else
+ ether_addr_copy(wid.val, mac_addr);
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ if (result)
+ PRINT_ER(vif->ndev, "Failed to del station\n");
+
+ kfree(wid.val);
+
+ return result;
+}
+
+int wilc_del_allstation(struct wilc_vif *vif, u8 mac_addr[][ETH_ALEN])
+{
+ struct wid wid;
+ int result;
+ int i;
+ u8 assoc_sta = 0;
+ struct wilc_del_all_sta del_sta;
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG,
+ "Setting deauthenticating station message queue params\n");
+ memset(&del_sta, 0x0, sizeof(del_sta));
+ for (i = 0; i < WILC_MAX_NUM_STA; i++) {
+ if (!is_zero_ether_addr(mac_addr[i])) {
+ PRINT_INFO(vif->ndev,
+ CFG80211_DBG, "BSSID = %x%x%x%x%x%x\n",
+ mac_addr[i][0], mac_addr[i][1],
+ mac_addr[i][2], mac_addr[i][3],
+ mac_addr[i][4], mac_addr[i][5]);
+ assoc_sta++;
+ ether_addr_copy(del_sta.mac[i], mac_addr[i]);
+ }
+ }
+ if (!assoc_sta) {
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "NO ASSOCIATED STAS\n");
+ return 0;
+ }
+ del_sta.assoc_sta = assoc_sta;
+
+ wid.id = WID_DEL_ALL_STA;
+ wid.type = WID_STR;
+ wid.size = (assoc_sta * ETH_ALEN) + 1;
+ wid.val = (u8 *)&del_sta;
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ if (result)
+ PRINT_ER(vif->ndev, "Failed to send delete all station\n");
+
+ return result;
+}
+
+int wilc_edit_station(struct wilc_vif *vif, const u8 *mac,
+ struct station_parameters *params)
+{
+ struct wid wid;
+ int result;
+ u8 *cur_byte;
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG,
+ "Setting editing station message queue params\n");
+
+ wid.id = WID_EDIT_STA;
+ wid.type = WID_BIN;
+ wid.size = WILC_ADD_STA_LENGTH + params->supported_rates_len;
+ wid.val = kmalloc(wid.size, GFP_KERNEL);
+ if (!wid.val)
+ return -ENOMEM;
+
+ cur_byte = wid.val;
+ wilc_hif_pack_sta_param(vif, cur_byte, mac, params);
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ if (result)
+ PRINT_ER(vif->ndev, "Failed to send edit station\n");
+
+ kfree(wid.val);
+ return result;
+}
+
+int wilc_set_power_mgmt(struct wilc_vif *vif, bool enabled, u32 timeout)
+{
+ struct wid wid;
+ int result;
+ s8 power_mode;
+
+ if (wilc_wlan_get_num_conn_ifcs(vif->wilc) == 2 && enabled)
+ return 0;
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG, "\n\n>> Setting PS to %d <<\n\n",
+ enabled);
+ if (enabled)
+ power_mode = WILC_FW_MIN_FAST_PS;
+ else
+ power_mode = WILC_FW_NO_POWERSAVE;
+
+ wid.id = WID_POWER_MANAGEMENT;
+ wid.val = &power_mode;
+ wid.size = sizeof(char);
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ if (result)
+ PRINT_ER(vif->ndev, "Failed to send power management\n");
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+ else
+ store_power_save_current_state(vif, power_mode);
+#endif
+
+ return result;
+}
+
+int wilc_setup_multicast_filter(struct wilc_vif *vif, u32 enabled, u32 count,
+ u8 *mc_list)
+{
+ int result;
+ struct host_if_msg *msg;
+
+ PRINT_INFO(vif->ndev, HOSTINF_DBG,
+ "Setting Multicast Filter params\n");
+ msg = wilc_alloc_work(vif, handle_set_mcast_filter, false);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->body.mc_info.enabled = enabled;
+ msg->body.mc_info.cnt = count;
+ msg->body.mc_info.mc_list = mc_list;
+
+ result = wilc_enqueue_work(msg);
+ if (result) {
+ PRINT_ER(vif->ndev, "enqueue work failed\n");
+ kfree(msg);
+ }
+ return result;
+}
+
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+void handle_powersave_state_changes(struct work_struct *work)
+{
+ struct host_if_msg *msg = container_of(work, struct host_if_msg, work);
+ struct wilc_vif *vif = msg->vif;
+
+ PRINT_INFO(vif->ndev, GENERIC_DBG, "Recover PS = %d\n",
+ vif->pwrsave_current_state);
+
+ /* Recover PS previous state */
+ wilc_set_power_mgmt(vif, vif->pwrsave_current_state, 0);
+}
+
+void wilc_powersave_state_changes(struct wilc_vif *vif)
+{
+ int result;
+ struct host_if_msg *msg;
+
+ msg = wilc_alloc_work(vif, handle_powersave_state_changes, false);
+ if (IS_ERR(msg))
+ return;
+
+ result = wilc_enqueue_work(msg);
+ if (result) {
+ PRINT_ER(vif->ndev, "enqueue work failed\n");
+ kfree(msg);
+ }
+}
+#endif
+
+int wilc_set_tx_power(struct wilc_vif *vif, u8 tx_power)
+{
+ struct wid wid;
+
+ wid.id = WID_TX_POWER;
+ wid.type = WID_CHAR;
+ wid.val = &tx_power;
+ wid.size = sizeof(char);
+
+ return wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+}
+
+int wilc_get_tx_power(struct wilc_vif *vif, u8 *tx_power)
+{
+ struct wid wid;
+
+ wid.id = WID_TX_POWER;
+ wid.type = WID_CHAR;
+ wid.val = tx_power;
+ wid.size = sizeof(char);
+
+ return wilc_send_config_pkt(vif, WILC_GET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+}
+
+bool is_valid_gpio(struct wilc_vif *vif, u8 gpio)
+{
+ switch (vif->wilc->chip) {
+ case WILC_1000:
+ if (gpio == 0 || gpio == 1 || gpio == 4 || gpio == 6)
+ return true;
+ else
+ return false;
+ case WILC_3000:
+ if (gpio == 0 || gpio == 3 || gpio == 4 ||
+ (gpio >= 17 && gpio <= 20))
+ return true;
+ else
+ return false;
+ default:
+ return false;
+ }
+}
+
+int wilc_set_antenna(struct wilc_vif *vif, u8 mode)
+{
+ struct wid wid;
+ int ret;
+ struct sysfs_attr_group *attr_syfs_p = &vif->attr_sysfs;
+ struct host_if_set_ant set_ant;
+
+ set_ant.mode = mode;
+
+ if (attr_syfs_p->ant_swtch_mode == ANT_SWTCH_INVALID_GPIO_CTRL) {
+ PRINT_ER(vif->ndev, "Ant switch GPIO mode is invalid.\n");
+ PRINT_ER(vif->ndev, "Set it using /sys/wilc/ant_swtch_mode\n");
+ return WILC_FAIL;
+ }
+
+ if (is_valid_gpio(vif, attr_syfs_p->antenna1)) {
+ set_ant.antenna1 = attr_syfs_p->antenna1;
+ } else {
+ PRINT_ER(vif->ndev, "Invalid GPIO%d\n", attr_syfs_p->antenna1);
+ return WILC_FAIL;
+ }
+
+ if (attr_syfs_p->ant_swtch_mode == ANT_SWTCH_DUAL_GPIO_CTRL) {
+ if ((attr_syfs_p->antenna2 != attr_syfs_p->antenna1) &&
+ is_valid_gpio(vif, attr_syfs_p->antenna2)) {
+ set_ant.antenna2 = attr_syfs_p->antenna2;
+ } else {
+ PRINT_ER(vif->ndev, "Invalid GPIO %d\n",
+ attr_syfs_p->antenna2);
+ return WILC_FAIL;
+ }
+ }
+
+ set_ant.gpio_mode = attr_syfs_p->ant_swtch_mode;
+
+ wid.id = WID_ANTENNA_SELECTION;
+ wid.type = WID_BIN;
+ wid.val = (u8 *)&set_ant;
+ wid.size = sizeof(struct host_if_set_ant);
+ if (attr_syfs_p->ant_swtch_mode == ANT_SWTCH_SNGL_GPIO_CTRL)
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "set antenna %d on GPIO %d\n", set_ant.mode,
+ set_ant.antenna1);
+ else if (attr_syfs_p->ant_swtch_mode == ANT_SWTCH_DUAL_GPIO_CTRL)
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "set antenna %d on GPIOs %d and %d\n",
+ set_ant.mode, set_ant.antenna1,
+ set_ant.antenna2);
+
+ ret = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
+ if (ret)
+ PRINT_ER(vif->ndev, "Failed to set antenna mode\n");
+
+ return ret;
+}
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#ifndef HOST_INT_H
+#define HOST_INT_H
+#include <linux/ieee80211.h>
+#include "wilc_wlan_if.h"
+
+enum {
+ WILC_IDLE_MODE = 0x0,
+ WILC_AP_MODE = 0x1,
+ WILC_STATION_MODE = 0x2,
+ WILC_GO_MODE = 0x3,
+ WILC_CLIENT_MODE = 0x4,
+ WILC_MONITOR_MODE = 0x5
+};
+
+enum {
+ WILC_P2P_IFC = 0x00,
+ WILC_WLAN_IFC = 0x01,
+};
+
+#define IFC_0 "wlan0"
+#define IFC_1 "p2p0"
+
+#define WILC_MAX_NUM_STA 9
+#define WILC_MAX_NUM_SCANNED_CH 14
+#define WILC_MAX_NUM_PROBED_SSID 10
+
+#define WILC_TX_MIC_KEY_LEN 8
+#define WILC_RX_MIC_KEY_LEN 8
+
+#define WILC_MAX_NUM_PMKIDS 16
+#define WILC_ADD_STA_LENGTH 40
+#define WILC_NUM_CONCURRENT_IFC 2
+
+enum {
+ WILC_SET_CFG = 0,
+ WILC_GET_CFG
+};
+
+#define WILC_MAX_ASSOC_RESP_FRAME_SIZE 256
+extern uint32_t cfg_packet_timeout;
+
+struct assoc_resp {
+ __le16 capab_info;
+ __le16 status_code;
+ __le16 aid;
+} __packed;
+
+struct rf_info {
+ u8 link_speed;
+ s8 rssi;
+ u32 tx_cnt;
+ u32 rx_cnt;
+ u32 tx_fail_cnt;
+};
+
+enum host_if_state {
+ HOST_IF_IDLE = 0,
+ HOST_IF_SCANNING = 1,
+ HOST_IF_CONNECTING = 2,
+ HOST_IF_WAITING_CONN_RESP = 3,
+ HOST_IF_CONNECTED = 4,
+ HOST_IF_P2P_LISTEN = 5,
+ HOST_IF_FORCE_32BIT = 0xFFFFFFFF
+};
+
+struct wilc_pmkid {
+ u8 bssid[ETH_ALEN];
+ u8 pmkid[WLAN_PMKID_LEN];
+} __packed;
+
+struct wilc_pmkid_attr {
+ u8 numpmkid;
+ struct wilc_pmkid pmkidlist[WILC_MAX_NUM_PMKIDS];
+} __packed;
+
+struct cfg_param_attr {
+ u32 flag;
+ u16 short_retry_limit;
+ u16 long_retry_limit;
+ u16 frag_threshold;
+ u16 rts_threshold;
+};
+
+enum cfg_param {
+ WILC_CFG_PARAM_RETRY_SHORT = BIT(0),
+ WILC_CFG_PARAM_RETRY_LONG = BIT(1),
+ WILC_CFG_PARAM_FRAG_THRESHOLD = BIT(2),
+ WILC_CFG_PARAM_RTS_THRESHOLD = BIT(3)
+};
+
+enum scan_event {
+ SCAN_EVENT_NETWORK_FOUND = 0,
+ SCAN_EVENT_DONE = 1,
+ SCAN_EVENT_ABORTED = 2,
+ SCAN_EVENT_FORCE_32BIT = 0xFFFFFFFF
+};
+
+enum conn_event {
+ EVENT_CONN_RESP = 0,
+ EVENT_DISCONN_NOTIF = 1,
+ EVENT_FORCE_32BIT = 0xFFFFFFFF
+};
+
+enum {
+ WILC_HIF_SDIO = 0,
+ WILC_HIF_SPI = BIT(0),
+ WILC_HIF_SDIO_GPIO_IRQ = BIT(1)
+};
+
+enum {
+ WILC_MAC_STATUS_INIT = -1,
+ WILC_MAC_STATUS_DISCONNECTED = 0,
+ WILC_MAC_STATUS_CONNECTED = 1
+};
+
+struct wilc_rcvd_net_info {
+ s8 rssi;
+ u8 ch;
+ u16 frame_len;
+ struct ieee80211_mgmt *mgmt;
+};
+
+typedef void (*wilc_remain_on_chan_ready)(void *);
+
+struct wilc_user_scan_req {
+ void (*scan_result)(enum scan_event evt,
+ struct wilc_rcvd_net_info *info, void *priv);
+ void *arg;
+ u32 ch_cnt;
+};
+
+struct wilc_conn_info {
+ u8 bssid[ETH_ALEN];
+ u8 security;
+ enum authtype auth_type;
+ u8 ch;
+ u8 *req_ies;
+ size_t req_ies_len;
+ u8 *resp_ies;
+ u16 resp_ies_len;
+ u16 status;
+ void (*conn_result)(enum conn_event evt, u8 status, void *priv);
+ void *arg;
+ void *param;
+};
+
+struct wilc_remain_ch {
+ u16 ch;
+ u32 duration;
+ void (*expired)(void *priv, u64 cookie);
+ void *arg;
+ u64 cookie;
+};
+
+struct host_if_drv {
+ struct wilc_user_scan_req usr_scan_req;
+ struct wilc_conn_info conn_info;
+ struct wilc_remain_ch remain_on_ch;
+ u64 p2p_timeout;
+
+ enum host_if_state hif_state;
+
+ u8 assoc_bssid[ETH_ALEN];
+ struct completion comp_test_key_block;
+ struct completion comp_test_disconn_block;
+ struct completion comp_get_rssi;
+ struct completion comp_inactive_time;
+
+ struct timer_list scan_timer;
+ struct wilc_vif *scan_timer_vif;
+
+ struct timer_list connect_timer;
+ struct wilc_vif *connect_timer_vif;
+
+ struct timer_list remain_on_ch_timer;
+ struct wilc_vif *remain_on_ch_timer_vif;
+
+ bool ifc_up;
+ int driver_handler_id;
+ u8 assoc_resp[WILC_MAX_ASSOC_RESP_FRAME_SIZE];
+};
+
+struct wilc_vif;
+
+signed int wilc_send_buffered_eap(struct wilc_vif *vif,
+ void (*deliver_to_stack)(struct wilc_vif *,
+ u8 *, u32, u32, u8),
+ void (*eap_buf_param)(void *), u8 *buff,
+ unsigned int size, unsigned int pkt_offset,
+ void *user_arg);
+int wilc_remove_wep_key(struct wilc_vif *vif, u8 index);
+int wilc_set_wep_default_keyid(struct wilc_vif *vif, u8 index);
+int wilc_add_wep_key_bss_sta(struct wilc_vif *vif, const u8 *key, u8 len,
+ u8 index);
+int wilc_add_wep_key_bss_ap(struct wilc_vif *vif, const u8 *key, u8 len,
+ u8 index, u8 mode, enum authtype auth_type);
+int wilc_add_ptk(struct wilc_vif *vif, const u8 *ptk, u8 ptk_key_len,
+ const u8 *mac_addr, const u8 *rx_mic, const u8 *tx_mic,
+ u8 mode, u8 cipher_mode, u8 index);
+s32 wilc_get_inactive_time(struct wilc_vif *vif, const u8 *mac,
+ u32 *out_val);
+int wilc_add_rx_gtk(struct wilc_vif *vif, const u8 *rx_gtk, u8 gtk_key_len,
+ u8 index, u32 key_rsc_len, const u8 *key_rsc,
+ const u8 *rx_mic, const u8 *tx_mic, u8 mode,
+ u8 cipher_mode);
+int wilc_set_pmkid_info(struct wilc_vif *vif, struct wilc_pmkid_attr *pmkid);
+int wilc_get_mac_address(struct wilc_vif *vif, u8 *mac_addr);
+int wilc_set_mac_address(struct wilc_vif *vif, u8 *mac_addr);
+int wilc_set_join_req(struct wilc_vif *vif, u8 *bssid, const u8 *ies,
+ size_t ies_len);
+int wilc_disconnect(struct wilc_vif *vif);
+int wilc_set_mac_chnl_num(struct wilc_vif *vif, u8 channel);
+int wilc_get_rssi(struct wilc_vif *vif, s8 *rssi_level);
+int wilc_scan(struct wilc_vif *vif, u8 scan_source, u8 scan_type,
+ u8 *ch_freq_list, u8 ch_list_len,
+ void (*scan_result_fn)(enum scan_event,
+ struct wilc_rcvd_net_info *, void *),
+ void *user_arg, struct cfg80211_scan_request *request);
+int wilc_hif_set_cfg(struct wilc_vif *vif,
+ struct cfg_param_attr *cfg_param);
+int wilc_init(struct net_device *dev, struct host_if_drv **hif_drv_handler);
+int wilc_deinit(struct wilc_vif *vif);
+int wilc_add_beacon(struct wilc_vif *vif, u32 interval, u32 dtim_period,
+ struct cfg80211_beacon_data *params);
+int wilc_del_beacon(struct wilc_vif *vif);
+int wilc_add_station(struct wilc_vif *vif, const u8 *mac,
+ struct station_parameters *params);
+int wilc_del_allstation(struct wilc_vif *vif, u8 mac_addr[][ETH_ALEN]);
+int wilc_del_station(struct wilc_vif *vif, const u8 *mac_addr);
+int wilc_edit_station(struct wilc_vif *vif, const u8 *mac,
+ struct station_parameters *params);
+int wilc_set_power_mgmt(struct wilc_vif *vif, bool enabled, u32 timeout);
+int wilc_setup_multicast_filter(struct wilc_vif *vif, u32 enabled, u32 count,
+ u8 *mc_list);
+int wilc_remain_on_channel(struct wilc_vif *vif, u64 cookie,
+ u32 duration, u16 chan,
+ void (*expired)(void *, u64), void *user_arg);
+int wilc_listen_state_expired(struct wilc_vif *vif, u64 cookie);
+void wilc_frame_register(struct wilc_vif *vif, u16 frame_type, bool reg);
+int wilc_set_wfi_drv_handler(struct wilc_vif *vif, int index, u8 mode,
+ u8 ifc_id);
+int wilc_set_operation_mode(struct wilc_vif *vif, u32 mode);
+int wilc_get_statistics(struct wilc_vif *vif, struct rf_info *stats);
+void wilc_resolve_disconnect_aberration(struct wilc_vif *vif);
+int wilc_get_vif_idx(struct wilc_vif *vif);
+int wilc_set_tx_power(struct wilc_vif *vif, u8 tx_power);
+int wilc_get_tx_power(struct wilc_vif *vif, u8 *tx_power);
+/*0 select antenna 1 , 2 select antenna mode , 2 allow the firmware to choose
+ * the best antenna
+ */
+int wilc_set_antenna(struct wilc_vif *vif, u8 mode);
+
+void wilc_set_wowlan_trigger(struct wilc_vif *vif, u8 wowlan_trigger);
+
+extern u8 wilc_initialized;
+s32 handle_scan_done(struct wilc_vif *vif, enum scan_event evt);
+void wilc_scan_complete_received(struct wilc *wilc, u8 *buffer, u32 length);
+void wilc_network_info_received(struct wilc *wilc, u8 *buffer, u32 length);
+void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length);
+void *wilc_parse_join_bss_param(struct cfg80211_bss *bss,
+ struct cfg80211_crypto_settings *crypto);
+void wilc_powersave_state_changes(struct wilc_vif *vif);
+#endif
--- /dev/null
+* Microchip WILC wireless SDIO device
+
+The wilc1000 chips can be connected via SDIO. The node is used to specifiy
+child node to the SDIO controller that connects the device to the system.
+
+Required properties:
+- compatible : Should be "microchip,wilc1000-sdio"
+- irq-gpios : Connect to a host IRQ
+- reset-gpios : Reset module GPIO
+- chip_en-gpios : Chip enable GPIO
+- reg : Slot ID used in the controller
+
+Optional:
+- bus-width : Number of data lines wired up the slot. Default 1 bit.
+
+
+Examples:
+mmc1: mmc@fc000000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_mmc1_clk_cmd_dat0 &pinctrl_mmc1_dat1_3>;
+ non-removable;
+ vmmc-supply = <&vcc_mmc1_reg>;
+ vqmmc-supply = <&vcc_3v3_reg>;
+ status = "okay";
+
+ wilc_sdio@0 {
+ compatible = "microchip,wilc1000", "microchip,wilc3000";
+ irq-gpios = <&pioC 27 0>;
+ reset-gpios = <&pioB 28 0>;
+ chip_en-gpios = <&pioC 30 0>;
+ status = "okay";
+ reg = <0>;
+ bus-width = <4>;
+ }
+ };
+}
--- /dev/null
+* Microchip WILC wireless SPI device
+
+The wilc1000 chips can be connected via SPI. This document describes
+the binding for the SPI connected module.
+
+Required properties:
+- compatible : Should be "microchip,wilc1000-spi"
+- spi-max-frequency : Maximum SPI clocking speed of device in Hz
+- reg : Chip select address of device
+- irq-gpios : Connect to a host IRQ
+- reset-gpios : Reset module GPIO
+- chip_en-gpios: : Chip enable GPIO
+
+
+Examples:
+
+spi1: spi@fc018000 {
+ cs-gpios = <&pioB 21 0>;
+ status = "okay";
+
+ wilc_spi@0 {
+ compatible = "microchip,wilc1000", "microchip,wilc3000";
+ spi-max-frequency = <48000000>;
+ reg = <0>;
+ irq-gpios = <&pioC 27 0>;
+ reset-gpios = <&pioB 28 0>;
+ chip_en-gpios = <&pioC 30 0>;
+ status = "okay";
+ };
+};
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include <linux/kobject.h>
+#include "wilc_wfi_cfgoperations.h"
+
+static struct kobject *wilc_kobj;
+static int device_created;
+static struct wilc_vif *vif[WILC_NUM_CONCURRENT_IFC];
+
+static ssize_t wilc_sysfs_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int attr_val = -1;
+
+ if (strcmp(attr->attr.name, "p2p_mode") == 0)
+ attr_val = vif[0]->attr_sysfs.p2p_mode;
+ if (strcmp(attr->attr.name, "ant_swtch_mode") == 0)
+ attr_val = vif[0]->attr_sysfs.ant_swtch_mode;
+ else if (strcmp(attr->attr.name, "antenna1") == 0)
+ attr_val = vif[0]->attr_sysfs.antenna1;
+ else if (strcmp(attr->attr.name, "antenna2") == 0)
+ attr_val = vif[0]->attr_sysfs.antenna2;
+
+ return sprintf(buf, "%d\n", attr_val);
+}
+
+static ssize_t wilc_sysfs_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf,
+ size_t count)
+{
+ int attr_val;
+ int i;
+
+ for (i = 0; i < WILC_NUM_CONCURRENT_IFC; i++) {
+ if (kstrtoint(buf, 10, &attr_val))
+ PRINT_ER(vif[i]->ndev,
+ "Failed to convert p2p_mode string");
+ if (strcmp(attr->attr.name, "p2p_mode") == 0) {
+ vif[i]->attr_sysfs.p2p_mode = (attr_val?1:0);
+ } else if (strcmp(attr->attr.name, "ant_swtch_mode") == 0) {
+ if (attr_val > ANT_SWTCH_DUAL_GPIO_CTRL)
+ PRINT_ER(vif[i]->ndev,
+ "Valid antenna switch modes:\n1-Single Antenna, 2-Dual Antenna\n");
+ else
+ vif[i]->attr_sysfs.ant_swtch_mode = attr_val;
+ } else if (strcmp(attr->attr.name, "antenna1") == 0) {
+ vif[i]->attr_sysfs.antenna1 = attr_val;
+ } else if (strcmp(attr->attr.name, "antenna2") == 0) {
+ vif[i]->attr_sysfs.antenna2 = attr_val;
+ }
+ }
+
+ return count;
+}
+
+static struct kobj_attribute p2p_mode_attr =
+ __ATTR(p2p_mode, 0664, wilc_sysfs_show, wilc_sysfs_store);
+
+static struct kobj_attribute ant_swtch_mode_attr =
+ __ATTR(ant_swtch_mode, 0664, wilc_sysfs_show, wilc_sysfs_store);
+
+static struct kobj_attribute ant_swtch_antenna1_attr =
+ __ATTR(antenna1, 0664, wilc_sysfs_show, wilc_sysfs_store);
+
+static struct kobj_attribute ant_swtch_antenna2_attr =
+ __ATTR(antenna2, 0664, wilc_sysfs_show, wilc_sysfs_store);
+
+
+static struct attribute *wilc_attrs[] = {
+ &p2p_mode_attr.attr,
+ &ant_swtch_mode_attr.attr,
+ &ant_swtch_antenna1_attr.attr,
+ &ant_swtch_antenna2_attr.attr,
+ NULL
+};
+
+static struct attribute_group attr_group = {
+ .attrs = wilc_attrs,
+};
+
+void wilc_sysfs_init(struct wilc_vif *vif1, struct wilc_vif *vif2)
+{
+ int retval;
+ int i;
+
+ vif[0] = vif1;
+ vif[1] = vif2;
+
+ if (device_created)
+ return;
+
+ wilc_kobj = kobject_create_and_add("wilc", NULL);
+ if (!wilc_kobj) {
+ retval = -ENOMEM;
+ return;
+ }
+
+ for (i = 0; i < WILC_NUM_CONCURRENT_IFC; i++) {
+ /* By default p2p mode is Group Owner */
+ vif[i]->attr_sysfs.p2p_mode = 1;
+ vif[i]->attr_sysfs.ant_swtch_mode = ANT_SWTCH_INVALID_GPIO_CTRL;
+ vif[i]->attr_sysfs.antenna1 = 0xFF;
+ vif[i]->attr_sysfs.antenna2 = 0xFF;
+ }
+ retval = sysfs_create_group(wilc_kobj, &attr_group);
+ device_created = 1;
+}
+
+void wilc_sysfs_exit(void)
+{
+ device_created = 0;
+ sysfs_remove_group(wilc_kobj, &attr_group);
+ kobject_put(wilc_kobj);
+}
+
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+#include <linux/firmware.h>
+
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/host.h>
+
+#include "wilc_wfi_netdevice.h"
+
+static struct wilc *wilc_bt;
+static dev_t chc_dev_no; /* Global variable for the first device number */
+static struct cdev str_chc_dev; /* Global variable for the character */
+struct device *dev;
+static struct class *chc_dev_class; /* Global variable for the device class */
+static bool device_created;
+int bt_init_done;
+
+typedef void (wilc_cmd_handler)(char *);
+
+static void handle_cmd_bt_enable(char *param);
+static void handle_cmd_pwr_up(char *param);
+static void handle_cmd_pwr_down(char *param);
+static void handle_cmd_chip_wake_up(char *param);
+static void handle_cmd_chip_allow_sleep(char *param);
+static void handle_cmd_download_fw(char *param);
+static void handle_cmd_cca_thrshld(char *param);
+
+static void wilc_bt_firmware_download(struct wilc *);
+static void wilc_bt_start(struct wilc *);
+static int wilc_bt_dev_open(struct inode *i, struct file *f);
+static int wilc_bt_dev_close(struct inode *i, struct file *f);
+static ssize_t wilc_bt_dev_read(struct file *f, char __user *buf, size_t len,
+ loff_t *off);
+static ssize_t wilc_bt_dev_write(struct file *f, const char __user *buff,
+ size_t len, loff_t *off);
+
+static const struct file_operations pugs_fops = {
+ .owner = THIS_MODULE,
+ .open = wilc_bt_dev_open,
+ .release = wilc_bt_dev_close,
+ .read = wilc_bt_dev_read,
+ .write = wilc_bt_dev_write
+};
+
+struct cmd_entry {
+ const char *str;
+ wilc_cmd_handler *wilc_handle_cmd;
+};
+
+static const struct cmd_entry cmd_table[] = {
+ {"BT_DOWNLOAD_FW", handle_cmd_download_fw},
+ {"BT_POWER_UP", handle_cmd_pwr_up},
+ {"BT_POWER_DOWN", handle_cmd_pwr_down},
+ {"BT_FW_CHIP_WAKEUP", handle_cmd_chip_wake_up},
+ {"BT_FW_CHIP_ALLOW_SLEEP", handle_cmd_chip_allow_sleep},
+ {"BT_ENABLE", handle_cmd_bt_enable},
+ {"CCA_THRESHOLD", handle_cmd_cca_thrshld},
+ /* Keep the NULL handler at the end of the table */
+ {(const char *)NULL, NULL},
+};
+
+static int wilc_bt_dev_open(struct inode *i, struct file *f)
+{
+ pr_err("at_pwr_dev: open()\n");
+ return 0;
+}
+
+static int wilc_bt_dev_close(struct inode *i, struct file *f)
+{
+ pr_info("at_pwr_dev: close()\n");
+ return 0;
+}
+
+static ssize_t wilc_bt_dev_read(struct file *f, char __user *buf, size_t len,
+ loff_t *off)
+{
+ pr_debug("at_pwr_dev: read()\n");
+ return 0;
+}
+
+static ssize_t wilc_bt_dev_write(struct file *f, const char __user *buff,
+ size_t len, loff_t *off)
+{
+ struct cmd_entry *cmd;
+ char *usr_str;
+
+
+ if (len == 0) {
+ pr_debug("received invalid size <=0: %d\n", len);
+ return len;
+ }
+
+ usr_str = kmalloc(len, GFP_KERNEL);
+
+ if (copy_from_user(usr_str, buff, len))
+ return -EIO;
+
+ pr_debug("received %s, len %d\n", usr_str, len);
+ // call the appropriate command handler
+ cmd = (struct cmd_entry *)cmd_table;
+ while (cmd->wilc_handle_cmd != NULL) {
+ if (strncmp(cmd->str, usr_str, strlen(cmd->str)) == 0) {
+ pr_debug("param len: %d, string: %s\n",
+ len - strlen(cmd->str), usr_str);
+ cmd->wilc_handle_cmd(usr_str + strlen(cmd->str));
+ break;
+ }
+ cmd++;
+ }
+
+ kfree(usr_str);
+ return len;
+}
+
+static void wilc_bt_create_device(void)
+{
+ int ret = 0;
+
+ if (device_created)
+ return;
+
+ ret = alloc_chrdev_region(&chc_dev_no, 0, 1, "atmel");
+ if (ret < 0)
+ return;
+ chc_dev_class = class_create(THIS_MODULE, "atmel");
+ if (IS_ERR(chc_dev_class)) {
+ unregister_chrdev_region(chc_dev_no, 1);
+ return;
+ }
+ dev = device_create(chc_dev_class, NULL, chc_dev_no, NULL,
+ "wilc_bt");
+ if (IS_ERR(dev)) {
+ class_destroy(chc_dev_class);
+ unregister_chrdev_region(chc_dev_no, 1);
+ return;
+ }
+
+ cdev_init(&str_chc_dev, &pugs_fops);
+ ret = cdev_add(&str_chc_dev, chc_dev_no, 1);
+ if (ret < 0) {
+ device_destroy(chc_dev_class, chc_dev_no);
+ class_destroy(chc_dev_class);
+ unregister_chrdev_region(chc_dev_no, 1);
+ return;
+ }
+ mutex_init(&wilc_bt->cs);
+ device_created = 1;
+}
+
+static void handle_cmd_cca_thrshld(char *param)
+{
+ int carrier_thrshld, noise_thrshld;
+ unsigned int carr_thrshld_frac, noise_thrshld_frac, carr_thrshld_int,
+ noise_thrshld_int, reg;
+
+ if (param == NULL) {
+ pr_err("Invalid parameter\n");
+ return;
+ }
+
+ if (sscanf(param, " %d %d", &noise_thrshld, &carrier_thrshld) != 2) {
+ pr_err("Failed to parse input parameters. Usage:\n");
+ pr_err("echo CCA_THRESHOLD NOISE_THRESHOLD CARRIER_THRESHOLD > /dev/at_pwr_dev\n");
+ pr_err("where threshold values are in dB * 10\n");
+ pr_err("e.g. echo CCA_THRESHOLD -625 -826 > /dev/at_pwr_dev to set thresholds to -62.5 and -82.6\n\n");
+ return;
+ }
+
+ pr_info("Changing CCA noise threshold to %d and carrier thresholds to %d\n",
+ noise_thrshld, carrier_thrshld);
+
+ carr_thrshld_int = carrier_thrshld/10;
+ if (carrier_thrshld < 0)
+ carr_thrshld_frac = (carr_thrshld_int * 10) - carrier_thrshld;
+ else
+ carr_thrshld_frac = carrier_thrshld - (carr_thrshld_int * 10);
+
+ noise_thrshld_int = noise_thrshld/10;
+ if (noise_thrshld < 0)
+ noise_thrshld_frac = (noise_thrshld_int * 10) - noise_thrshld;
+ else
+ noise_thrshld_frac = noise_thrshld - (noise_thrshld_int * 10);
+
+ wilc_bt->hif_func->hif_read_reg(wilc_bt, CCA_CTL_2, ®);
+ reg &= ~(0x7FF0000);
+ reg |= ((noise_thrshld_frac & 0x7) | ((noise_thrshld_int & 0x1FF)
+ << 3)) << 16;
+ wilc_bt->hif_func->hif_write_reg(wilc_bt, CCA_CTL_2, reg);
+
+ wilc_bt->hif_func->hif_read_reg(wilc_bt, CCA_CTL_7, ®);
+ reg &= ~(0x7FF0000);
+ reg |= ((carr_thrshld_frac & 0x7) | ((carr_thrshld_int & 0x1FF) << 3))
+ << 16;
+ wilc_bt->hif_func->hif_write_reg(wilc_bt, CCA_CTL_7, reg);
+}
+
+int wilc_bt_power_down(struct wilc *wilc, int source)
+{
+ const struct wilc_hif_func *hif_func = wilc->hif_func;
+
+ if (source == DEV_BT) {
+ int ret;
+ u32 reg;
+
+ pr_info("AT PWR: bt_power_down\n");
+
+ /* Adjust coexistence module. This should be done from the FW
+ * in the future
+ */
+ acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_BT);
+
+ ret = hif_func->hif_read_reg(wilc, GLOBAL_MODE_CONTROL,
+ ®);
+ if (!ret) {
+ pr_err("[wilc start]: fail read reg %x\n",
+ GLOBAL_MODE_CONTROL);
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+ return ret;
+ }
+ /* Clear BT mode*/
+ reg &= ~BIT(1);
+ ret = hif_func->hif_write_reg(wilc, GLOBAL_MODE_CONTROL,
+ reg);
+ if (!ret) {
+ pr_err("[wilc start]: fail write reg %x\n",
+ GLOBAL_MODE_CONTROL);
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+ return ret;
+ }
+
+
+ /*TicketId1115*/
+ /*Disable awake coex null frames*/
+ ret = hif_func->hif_read_reg(wilc, COE_AUTO_PS_ON_NULL_PKT,
+ ®);
+ if (!ret) {
+ pr_err("[wilc start]: fail read reg %x\n",
+ COE_AUTO_PS_ON_NULL_PKT);
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+ return ret;
+ }
+ reg &= ~BIT(30);
+ ret = hif_func->hif_write_reg(wilc, COE_AUTO_PS_ON_NULL_PKT,
+ reg);
+ if (!ret) {
+ pr_err("[wilc start]: fail write reg %x\n",
+ COE_AUTO_PS_ON_NULL_PKT);
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+ return ret;
+ }
+
+ /*TicketId1115*/
+ /*Disable doze coex null frames*/
+ ret = hif_func->hif_read_reg(wilc, COE_AUTO_PS_OFF_NULL_PKT,
+ ®);
+ if (!ret) {
+ pr_err("[wilc start]: fail read reg %x\n",
+ COE_AUTO_PS_OFF_NULL_PKT);
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+ return ret;
+ }
+ reg &= ~BIT(30);
+ ret = hif_func->hif_write_reg(wilc, COE_AUTO_PS_OFF_NULL_PKT,
+ reg);
+ if (!ret) {
+ pr_err("[wilc start]: fail write reg %x\n",
+ COE_AUTO_PS_OFF_NULL_PKT);
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+ return ret;
+ }
+ // Disable BT wakeup
+ ret = hif_func->hif_read_reg(wilc, PWR_SEQ_MISC_CTRL,
+ ®);
+ if (!ret) {
+ pr_err("[wilc start]: fail read reg %x\n",
+ PWR_SEQ_MISC_CTRL);
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+ return ret;
+ }
+ reg &= ~BIT(29);
+ ret = hif_func->hif_write_reg(wilc, PWR_SEQ_MISC_CTRL,
+ reg);
+ if (!ret) {
+ pr_err("[wilc start]: fail write reg %x\n",
+ PWR_SEQ_MISC_CTRL);
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+ return ret;
+ }
+
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+
+ bt_init_done = 0;
+ }
+
+ mutex_lock(&wilc->cs);
+
+ pr_info("source: %s, current bus status Wifi: %d, BT: %d\n",
+ (source == DEV_WIFI ? "Wifi" : "BT"),
+ wilc->power_status[DEV_WIFI],
+ wilc->power_status[DEV_BT]);
+
+ if (wilc->power_status[source] == false) {
+ pr_err("power down request for already powered down source %s\n",
+ (source == DEV_WIFI ? "Wifi" : "BT"));
+ } else if (((source == DEV_WIFI) &&
+ (wilc->power_status[DEV_BT] == true)) ||
+ ((source == DEV_BT) &&
+ (wilc->power_status[DEV_WIFI] == true))) {
+ pr_warn("Another device is preventing power down. request source is %s\n",
+ (source == DEV_WIFI ? "Wifi" : "BT"));
+ } else {
+ wilc_wlan_power_off_sequence(wilc);
+ }
+ wilc->power_status[source] = false;
+
+ mutex_unlock(&wilc->cs);
+
+ return 0;
+}
+
+int wilc_bt_power_up(struct wilc *wilc, int source)
+{
+ int count = 0;
+ int ret;
+ int reg;
+ const struct wilc_hif_func *hif_func = wilc->hif_func;
+
+ mutex_lock(&wilc->cs);
+
+ pr_debug("source: %s, current bus status Wifi: %d, BT: %d\n",
+ (source == DEV_WIFI ? "Wifi" : "BT"),
+ wilc->power_status[DEV_WIFI],
+ wilc->power_status[DEV_BT]);
+
+ if (wilc->power_status[source] == true) {
+ pr_err("power up request for already powered up source %s\n",
+ (source == DEV_WIFI ? "Wifi" : "BT"));
+ } else {
+ /*Bug 215*/
+ /*Avoid overlapping between BT and Wifi intialization*/
+ if (wilc->power_status[DEV_WIFI] == true) {
+ while (!wilc->initialized) {
+ msleep(100);
+ if (++count > 20) {
+ pr_warn("Wifi initialize timeout\n");
+ break;
+ }
+ }
+ } else if (wilc->power_status[DEV_BT] == true) {
+ while (!bt_init_done) {
+ msleep(200);
+ if (++count > 30) {
+ pr_warn("BT initialize timeout\n");
+ break;
+ }
+ }
+ /* An additional wait to give BT firmware time to do
+ * CPLL update as the time measured since the start of
+ * BT Fw till the end of function "rf_nmi_init_tuner"
+ * was 71.2 ms
+ */
+ msleep(100);
+ }
+ }
+
+ if ((wilc->power_status[DEV_WIFI] == true) ||
+ (wilc->power_status[DEV_BT] == true)) {
+ pr_info("Device already up. request source is %s\n",
+ (source == DEV_WIFI ? "Wifi" : "BT"));
+ } else {
+ pr_info("WILC POWER UP\n");
+ }
+ wilc->power_status[source] = true;
+ mutex_unlock(&wilc->cs);
+
+ if (source == DEV_BT) {
+ /*TicketId1092*/
+ /*If WiFi is off, force BT*/
+ if (wilc->power_status[DEV_WIFI] == false) {
+ acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_BT);
+
+ /*TicketId1115*/
+ /*Disable awake coex null frames*/
+ ret = hif_func->hif_read_reg(wilc,
+ COE_AUTO_PS_ON_NULL_PKT,
+ ®);
+ if (!ret) {
+ pr_err("[wilc start]: fail read reg %x\n",
+ COE_AUTO_PS_ON_NULL_PKT);
+ goto fail;
+ }
+ reg &= ~BIT(30);
+ ret = hif_func->hif_write_reg(wilc,
+ COE_AUTO_PS_ON_NULL_PKT,
+ reg);
+ if (!ret) {
+ pr_err("[wilc start]: fail write reg %x\n",
+ COE_AUTO_PS_ON_NULL_PKT);
+ goto fail;
+ }
+
+ /*TicketId1115*/
+ /*Disable doze coex null frames*/
+ ret = hif_func->hif_read_reg(wilc,
+ COE_AUTO_PS_OFF_NULL_PKT,
+ ®);
+ if (!ret) {
+ pr_err("[wilc start]: fail read reg %x\n",
+ COE_AUTO_PS_OFF_NULL_PKT);
+ goto fail;
+ }
+ reg &= ~BIT(30);
+ ret = hif_func->hif_write_reg(wilc,
+ COE_AUTO_PS_OFF_NULL_PKT,
+ reg);
+ if (!ret) {
+ pr_err("[wilc start]: fail write reg %x\n",
+ COE_AUTO_PS_OFF_NULL_PKT);
+ goto fail;
+ }
+
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+ }
+
+ // Enable BT wakeup
+ acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_BT);
+
+ ret = hif_func->hif_read_reg(wilc, PWR_SEQ_MISC_CTRL,
+ ®);
+ if (!ret) {
+ pr_err("[wilc start]: fail read reg %x ...\n",
+ PWR_SEQ_MISC_CTRL);
+ goto fail;
+ }
+ reg |= BIT(29);
+ ret = hif_func->hif_write_reg(wilc, PWR_SEQ_MISC_CTRL,
+ reg);
+ if (!ret) {
+ pr_err("[wilc start]: fail write reg %x ...\n",
+ PWR_SEQ_MISC_CTRL);
+ goto fail;
+ }
+
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+ }
+
+ return 0;
+
+fail:
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+ wilc_bt_power_down(wilc, DEV_BT);
+ return ret;
+}
+
+static void wilc_bt_firmware_download(struct wilc *wilc)
+{
+ u32 offset;
+ u32 addr, size, size2, blksz;
+ u8 *dma_buffer;
+ const struct firmware *wilc_bt_firmware;
+ const u8 *buffer;
+ size_t buffer_size;
+ int ret = 0;
+ u32 reg;
+ const struct wilc_hif_func *hif_func;
+
+ hif_func = wilc->hif_func;
+
+ pr_info("Bluetooth firmware: %s\n", FW_WILC3000_BLE);
+ if (request_firmware(&wilc_bt_firmware, FW_WILC3000_BLE, dev) != 0) {
+ pr_err("%s - firmare not available. Skip!\n", FW_WILC3000_BLE);
+ ret = -1;
+ goto fail_1;
+ }
+
+ buffer = wilc_bt_firmware->data;
+ buffer_size = (size_t)wilc_bt_firmware->size;
+ if (buffer_size <= 0) {
+ pr_err("Firmware size = 0!\n");
+ ret = -1;
+ goto fail_1;
+ }
+
+ acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_BT);
+
+ ret = hif_func->hif_write_reg(wilc, 0x4f0000, 0x71);
+ if (!ret) {
+ pr_err("[wilc start]: fail write reg 0x4f0000 ...\n");
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+ goto fail_1;
+ }
+
+ /*
+ * Avoid booting from BT boot ROM. Make sure that Drive IRQN
+ * [SDIO platform] or SD_DAT3 [SPI platform] to ?1?
+ */
+ /* Set cortus reset register to register control. */
+ ret = hif_func->hif_read_reg(wilc, 0x3b0090, ®);
+ if (!ret) {
+ pr_err("[wilc start]: fail read reg 0x3b0090 ...\n");
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+ goto fail_1;
+ }
+
+ reg |= (1 << 0);
+ ret = hif_func->hif_write_reg(wilc, 0x3b0090, reg);
+ if (!ret) {
+ pr_err("[wilc start]: fail write reg 0x3b0090 ...\n");
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+ goto fail_1;
+ }
+
+ hif_func->hif_read_reg(wilc, 0x3B0400, ®);
+
+ if (reg & (1ul << 2)) {
+ reg &= ~(1ul << 2);
+ } else {
+ reg |= (1ul << 2);
+ hif_func->hif_write_reg(wilc, 0x3B0400, reg);
+ reg &= ~(1ul << 2);
+ }
+ hif_func->hif_write_reg(wilc, 0x3B0400, reg);
+
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+
+ /* blocks of sizes > 512 causes the wifi to hang! */
+ blksz = (1ul << 9);
+ /* Allocate a DMA coherent buffer. */
+ dma_buffer = kmalloc(blksz, GFP_KERNEL);
+ if (dma_buffer == NULL) {
+ ret = -5;
+ pr_err("Can't allocate buffer for BT firmware download IO error\n");
+ goto fail_1;
+ }
+ pr_info("Downloading BT firmware size = %d ...\n", buffer_size);
+
+ offset = 0;
+ addr = 0x400000;
+ size = buffer_size;
+ addr = cpu_to_le32(addr);
+ size = cpu_to_le32(size);
+ offset = 0;
+
+ while (((int)size) && (offset < buffer_size)) {
+ if (size <= blksz)
+ size2 = size;
+ else
+ size2 = blksz;
+
+ /* Copy firmware into a DMA coherent buffer */
+ memcpy(dma_buffer, &buffer[offset], size2);
+
+ acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_BT);
+
+ ret = hif_func->hif_block_tx(wilc, addr, dma_buffer, size2);
+
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+
+ if (!ret)
+ break;
+
+ addr += size2;
+ offset += size2;
+ size -= size2;
+ }
+
+ if (!ret) {
+ ret = -5;
+ pr_err("Can't download BT firmware IO error\n");
+ goto fail;
+ }
+
+fail:
+ kfree(dma_buffer);
+fail_1:
+ pr_debug("Freeing BT FW buffer ...\n");
+ pr_debug("Releasing BT firmware\n");
+ release_firmware(wilc_bt_firmware);
+}
+
+static void wilc_bt_start(struct wilc *wilc)
+{
+ u32 val32 = 0;
+
+ acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_BT);
+
+ pr_info("Starting BT firmware\n");
+ /*
+ * Write the firmware download complete magic value 0x10ADD09E at
+ * location 0xFFFF000C (Cortus map) or C000C (AHB map).
+ * This will let the boot-rom code execute from RAM.
+ */
+ wilc->hif_func->hif_write_reg(wilc, 0x4F000c, 0x10add09e);
+
+ wilc->hif_func->hif_read_reg(wilc, 0x3B0400, &val32);
+ val32 &= ~((1ul << 2) | (1ul << 3));
+ wilc->hif_func->hif_write_reg(wilc, 0x3B0400, val32);
+
+ msleep(100);
+
+ val32 |= ((1ul << 2) | (1ul << 3));
+
+ wilc->hif_func->hif_write_reg(wilc, 0x3B0400, val32);
+
+ pr_info("BT Start Succeeded\n");
+
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+}
+
+static void handle_cmd_pwr_up(char *param)
+{
+ pr_info("AT PWR: bt_power_up\n");
+ bt_init_done = 0;
+
+ if (!wilc_bt->initialized && !wilc_bt->hif_func->hif_is_init(wilc_bt)) {
+ acquire_bus(wilc_bt, WILC_BUS_ACQUIRE_ONLY, DEV_BT);
+ if (!wilc_bt->hif_func->hif_init(wilc_bt, false)) {
+ release_bus(wilc_bt, WILC_BUS_RELEASE_ONLY, DEV_BT);
+ return;
+ }
+ release_bus(wilc_bt, WILC_BUS_RELEASE_ONLY, DEV_BT);
+ }
+
+ wilc_bt_power_up(wilc_bt, DEV_BT);
+}
+
+static void handle_cmd_pwr_down(char *param)
+{
+ wilc_bt_power_down(wilc_bt, DEV_BT);
+}
+
+static void handle_cmd_chip_wake_up(char *param)
+{
+ chip_wakeup(wilc_bt, DEV_BT);
+}
+
+static void handle_cmd_chip_allow_sleep(char *param)
+{
+ bt_init_done = 1;
+ chip_allow_sleep(wilc_bt, DEV_BT);
+}
+
+static void handle_cmd_download_fw(char *param)
+{
+ pr_info("AT PWR: bt_download_fw\n");
+
+ wilc_bt_firmware_download(wilc_bt);
+ wilc_bt_start(wilc_bt);
+}
+
+static void handle_cmd_bt_enable(char *param)
+{
+ wilc_bt_power_up(wilc_bt, DEV_BT);
+ wilc_bt_firmware_download(wilc_bt);
+ wilc_bt_start(wilc_bt);
+}
+
+void wilc_bt_init(struct wilc *wilc)
+{
+ wilc_bt = wilc;
+ pr_debug("at_pwr_dev: init\n");
+ wilc_bt_create_device();
+}
+
+void wilc_bt_deinit(void)
+{
+ pr_info("at_pwr_dev: deinit\n");
+
+ if (&wilc_bt->cs != NULL)
+ mutex_destroy(&wilc_bt->cs);
+
+ cdev_del(&str_chc_dev);
+ device_created = 0;
+ device_destroy(chc_dev_class, chc_dev_no);
+ class_destroy(chc_dev_class);
+ unregister_chrdev_region(chc_dev_no, 1);
+ pr_info("at_pwr_dev: unregistered\n");
+}
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+
+#include "wilc_debugfs.h"
+
+atomic_t WILC_DEBUG_REGION = ATOMIC_INIT(INIT_DBG | GENERIC_DBG |
+ CFG80211_DBG | HOSTAPD_DBG |
+ PWRDEV_DBG);
+
+#if defined(WILC_DEBUGFS)
+static struct dentry *wilc_dir;
+
+static ssize_t wilc_debug_region_read(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ char buf[128];
+ int res = 0;
+
+ /* only allow read from start */
+ if (*ppos > 0)
+ return 0;
+
+ res = scnprintf(buf, sizeof(buf), "Debug Region: (0x%08x)\n",
+ atomic_read(&WILC_DEBUG_REGION));
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, res);
+}
+
+static ssize_t wilc_debug_region_write(struct file *filp,
+ const char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ int flag = 0;
+ int ret;
+
+ ret = kstrtouint_from_user(buf, count, 16, &flag);
+ if (ret)
+ return ret;
+
+ if (flag > DBG_REGION_ALL) {
+ pr_err("%s, value (0x%08x) is out of range, stay previous flag (0x%08x)\n",
+ __func__, flag, atomic_read(&WILC_DEBUG_REGION));
+ pr_err("allowed bits are 0 to 15\n");
+ return -EINVAL;
+ }
+
+ atomic_set(&WILC_DEBUG_REGION, (int)flag);
+
+ pr_info("Debug region set to %x\n", atomic_read(&WILC_DEBUG_REGION));
+
+ return count;
+}
+
+#define FOPS(_open, _read, _write, _poll) { \
+ .owner = THIS_MODULE, \
+ .open = (_open), \
+ .read = (_read), \
+ .write = (_write), \
+ .poll = (_poll), \
+}
+
+struct wilc_debugfs_info_t {
+ const char *name;
+ int perm;
+ unsigned int data;
+ const struct file_operations fops;
+};
+
+static struct wilc_debugfs_info_t debugfs_info[] = {
+ {
+ "wilc_debug_region",
+ 0666,
+ 0,
+ FOPS(NULL, wilc_debug_region_read, wilc_debug_region_write,
+ NULL),
+ },
+};
+
+int wilc_debugfs_init(void)
+{
+ int i;
+ struct wilc_debugfs_info_t *info;
+
+ wilc_dir = debugfs_create_dir("wilc", NULL);
+ if (wilc_dir == NULL) {
+ pr_err("Error creating debugfs\n");
+ return -EFAULT;
+ }
+ for (i = 0; i < ARRAY_SIZE(debugfs_info); i++) {
+ info = &debugfs_info[i];
+ debugfs_create_file(info->name,
+ info->perm,
+ wilc_dir,
+ &info->data,
+ &info->fops);
+ }
+ return 0;
+}
+
+void wilc_debugfs_remove(void)
+{
+ debugfs_remove_recursive(wilc_dir);
+}
+
+#endif
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#ifndef WILC_DEBUGFS_H
+#define WILC_DEBUGFS_H
+
+#include <linux/kern_levels.h>
+
+#define GENERIC_DBG BIT(0)
+#define HOSTAPD_DBG BIT(1)
+#define HOSTINF_DBG BIT(2)
+#define CORECONFIG_DBG BIT(3)
+#define CFG80211_DBG BIT(4)
+#define INT_DBG BIT(5)
+#define TX_DBG BIT(6)
+#define RX_DBG BIT(7)
+#define TCP_ENH BIT(8)
+#define INIT_DBG BIT(9)
+#define PWRDEV_DBG BIT(10)
+#define DBG_REGION_ALL (BIT(11)-1)
+
+extern atomic_t WILC_DEBUG_REGION;
+
+#define PRINT_D(netdev, region, format, ...) do { \
+ if (atomic_read(&WILC_DEBUG_REGION)&(region))\
+ netdev_dbg(netdev, "DBG [%s: %d] "format, __func__, __LINE__,\
+ ##__VA_ARGS__); } \
+ while (0)
+
+#define PRINT_INFO(netdev, region, format, ...) do { \
+ if (atomic_read(&WILC_DEBUG_REGION)&(region))\
+ netdev_info(netdev, "INFO [%s]"format, __func__, \
+ ##__VA_ARGS__); } \
+ while (0)
+
+#define PRINT_WRN(netdev, region, format, ...) do { \
+ if (atomic_read(&WILC_DEBUG_REGION)&(region))\
+ netdev_warn(netdev, "WRN [%s: %d]"format, __func__, __LINE__,\
+ ##__VA_ARGS__); } \
+ while (0)
+
+#define PRINT_ER(netdev, format, ...) netdev_err(netdev, "ERR [%s:%d] "format,\
+ __func__, __LINE__, ##__VA_ARGS__)
+
+int wilc_debugfs_init(void);
+void wilc_debugfs_remove(void);
+#endif /* WILC_DEBUGFS_H */
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include <linux/etherdevice.h>
+
+#include "wilc_netdev.h"
+#include "wilc_wfi_cfgoperations.h"
+
+struct wfi_rtap_hdr {
+ struct ieee80211_radiotap_header hdr;
+ u8 rate;
+} __packed;
+
+struct wfi_rtap_cb_hdr {
+ struct ieee80211_radiotap_header hdr;
+ u8 rate;
+ u8 dump;
+ u16 tx_flags;
+} __packed;
+
+#define TX_RADIOTAP_PRESENT ((1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_TX_FLAGS))
+
+void wilc_wfi_handle_monitor_rx(struct wilc *wilc, u8 *buff, u32 size)
+{
+ struct wilc_vif *vif = 0;
+ struct sk_buff *skb = NULL;
+ struct wfi_rtap_hdr *hdr;
+ int i;
+
+ for (i = 0; i < wilc->vif_num; i++) {
+ if (wilc->vif[i]->iftype == WILC_MONITOR_MODE) {
+ vif = wilc->vif[i];
+ break;
+ }
+ }
+
+ if (!vif) {
+ PRINT_D(vif->ndev, HOSTAPD_DBG, "Monitor interface not up\n");
+ return;
+ }
+
+ skb = dev_alloc_skb(size + sizeof(*hdr));
+
+ if (!skb) {
+ PRINT_D(vif->ndev, HOSTAPD_DBG,
+ "Monitor if: No memory to allocate skb");
+ return;
+ }
+#if KERNEL_VERSION(4, 13, 0) <= LINUX_VERSION_CODE
+ skb_put_data(skb, buff, size);
+ hdr = skb_push(skb, sizeof(*hdr));
+#else
+ memcpy(skb_put(skb, size), buff, size);
+ hdr = (struct wfi_rtap_hdr *)skb_push(skb, sizeof(*hdr));
+#endif
+ memset(hdr, 0, sizeof(*hdr));
+ hdr->hdr.it_version = 0; /* PKTHDR_RADIOTAP_VERSION; */
+ hdr->hdr.it_len = cpu_to_le16(sizeof(*hdr));
+ PRINT_D(vif->ndev, HOSTAPD_DBG,
+ "Radiotap len %d\n", hdr->hdr.it_len);
+ hdr->hdr.it_present = cpu_to_le32
+ (1 << IEEE80211_RADIOTAP_RATE); /* | */
+ PRINT_D(vif->ndev, HOSTAPD_DBG, "Presentflags %d\n",
+ hdr->hdr.it_present);
+ hdr->rate = 5; /* txrate->bitrate / 5; */
+ skb->dev = vif->ndev;
+ skb_reset_mac_header(skb);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = htons(ETH_P_802_2);
+ memset(skb->cb, 0, sizeof(skb->cb));
+ netif_rx(skb);
+}
+
+void wilc_wfi_monitor_rx(struct net_device *mon_dev, u8 *buff, u32 size)
+{
+ u32 header, pkt_offset;
+ struct sk_buff *skb = NULL;
+ struct wfi_rtap_hdr *hdr;
+ struct wfi_rtap_cb_hdr *cb_hdr;
+
+ if (!mon_dev)
+ return;
+
+ if (!netif_running(mon_dev)) {
+ PRINT_D(mon_dev, HOSTAPD_DBG,
+ "Monitor interface already RUNNING\n");
+ return;
+ }
+
+ /* Get WILC header */
+ memcpy(&header, (buff - HOST_HDR_OFFSET), HOST_HDR_OFFSET);
+ le32_to_cpus(&header);
+ /*
+ * The packet offset field contain info about what type of management
+ * the frame we are dealing with and ack status
+ */
+ pkt_offset = GET_PKT_OFFSET(header);
+
+ if (pkt_offset & IS_MANAGMEMENT_CALLBACK) {
+ /* hostapd callback mgmt frame */
+
+ skb = dev_alloc_skb(size + sizeof(*cb_hdr));
+ if (!skb) {
+ PRINT_D(mon_dev, HOSTAPD_DBG,
+ "Monitor if : No memory to allocate skb");
+ return;
+ }
+ #if KERNEL_VERSION(4, 13, 0) <= LINUX_VERSION_CODE
+ skb_put_data(skb, buff, size);
+
+ cb_hdr = skb_push(skb, sizeof(*cb_hdr));
+ #else
+ memcpy(skb_put(skb, size), buff, size);
+ cb_hdr = (struct wfi_rtap_cb_hdr *)skb_push(skb,
+ sizeof(*cb_hdr));
+ #endif
+ memset(cb_hdr, 0, sizeof(*cb_hdr));
+
+ cb_hdr->hdr.it_version = 0; /* PKTHDR_RADIOTAP_VERSION; */
+
+ cb_hdr->hdr.it_len = cpu_to_le16(sizeof(*cb_hdr));
+
+ cb_hdr->hdr.it_present = cpu_to_le32(TX_RADIOTAP_PRESENT);
+
+ cb_hdr->rate = 5;
+
+ if (pkt_offset & IS_MGMT_STATUS_SUCCES) {
+ /* success */
+ cb_hdr->tx_flags = IEEE80211_RADIOTAP_F_TX_RTS;
+ } else {
+ cb_hdr->tx_flags = IEEE80211_RADIOTAP_F_TX_FAIL;
+ }
+
+ } else {
+ skb = dev_alloc_skb(size + sizeof(*hdr));
+
+ if (!skb) {
+ PRINT_D(mon_dev, HOSTAPD_DBG,
+ "Monitor if : No memory to allocate skb");
+ return;
+ }
+ #if KERNEL_VERSION(4, 13, 0) <= LINUX_VERSION_CODE
+ skb_put_data(skb, buff, size);
+ hdr = skb_push(skb, sizeof(*hdr));
+ #else
+ memcpy(skb_put(skb, size), buff, size);
+ hdr = (struct wfi_rtap_hdr *)skb_push(skb,
+ sizeof(*hdr));
+ #endif
+ memset(hdr, 0, sizeof(*hdr));
+ hdr->hdr.it_version = 0; /* PKTHDR_RADIOTAP_VERSION; */
+ hdr->hdr.it_len = cpu_to_le16(sizeof(*hdr));
+ PRINT_D(mon_dev, HOSTAPD_DBG,
+ "Radiotap len %d\n", hdr->hdr.it_len);
+ hdr->hdr.it_present = cpu_to_le32
+ (1 << IEEE80211_RADIOTAP_RATE); /* | */
+ PRINT_D(mon_dev, HOSTAPD_DBG, "Presentflags %d\n",
+ hdr->hdr.it_present);
+ hdr->rate = 5;
+ }
+
+ skb->dev = mon_dev;
+ skb_reset_mac_header(skb);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = htons(ETH_P_802_2);
+ memset(skb->cb, 0, sizeof(skb->cb));
+
+ netif_rx(skb);
+}
+
+struct tx_complete_mon_data {
+ int size;
+ void *buff;
+};
+
+static void mgmt_tx_complete(void *priv, int status)
+{
+ struct tx_complete_mon_data *pv_data = priv;
+ u8 *buf = pv_data->buff;
+
+ if (status == 1) {
+ if (buf[0] == 0x10 || buf[0] == 0xb0)
+ pr_info("Packet sent Size = %d Add = %p.\n",
+ pv_data->size, pv_data->buff);
+ } else {
+ pr_info("Couldn't send packet Size = %d Add = %p.\n",
+ pv_data->size, pv_data->buff);
+ }
+ /*
+ * in case of fully hosting mode, the freeing will be done
+ * in response to the cfg packet
+ */
+ kfree(pv_data->buff);
+
+ kfree(pv_data);
+}
+
+static int mon_mgmt_tx(struct net_device *dev, const u8 *buf, size_t len)
+{
+ struct tx_complete_mon_data *mgmt_tx = NULL;
+
+ if (!dev) {
+ PRINT_ER(dev, "ERROR: dev == NULL\n");
+ return -EFAULT;
+ }
+
+ netif_stop_queue(dev);
+ mgmt_tx = kmalloc(sizeof(*mgmt_tx), GFP_ATOMIC);
+ if (!mgmt_tx) {
+ PRINT_ER(dev,
+ "Failed to allocate memory for mgmt_tx structure\n");
+ return -ENOMEM;
+ }
+
+ mgmt_tx->buff = kmemdup(buf, len, GFP_ATOMIC);
+ if (!mgmt_tx->buff) {
+ kfree(mgmt_tx);
+ return -ENOMEM;
+ }
+
+ mgmt_tx->size = len;
+
+ txq_add_mgmt_pkt(dev, mgmt_tx, mgmt_tx->buff, mgmt_tx->size,
+ mgmt_tx_complete);
+
+ netif_wake_queue(dev);
+ return 0;
+}
+
+static netdev_tx_t wilc_wfi_mon_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ u32 rtap_len, ret = 0;
+ struct wilc_wfi_mon_priv *mon_priv;
+ u8 srcadd[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+
+ mon_priv = netdev_priv(dev);
+ if (!mon_priv) {
+ PRINT_ER(dev, "Monitor interface private structure is NULL\n");
+ return -EFAULT;
+ }
+ rtap_len = ieee80211_get_radiotap_len(skb->data);
+ if (skb->len < rtap_len) {
+ PRINT_ER(dev, "Error in radiotap header\n");
+ return -1;
+ }
+
+ skb_pull(skb, rtap_len);
+
+ skb->dev = mon_priv->real_ndev;
+
+ PRINT_D(dev, HOSTAPD_DBG, "Skipping the radiotap header\n");
+ PRINT_D(dev, HOSTAPD_DBG, "SKB netdevice name = %s\n", skb->dev->name);
+ PRINT_D(dev, HOSTAPD_DBG, "MONITOR real dev name = %s\n",
+ mon_priv->real_ndev->name);
+ ether_addr_copy(srcadd, &skb->data[10]);
+ ether_addr_copy(bssid, &skb->data[16]);
+ /*
+ * Identify if data or mgmt packet, if source address and bssid
+ * fields are equal send it to mgmt frames handler
+ */
+ if (!(memcmp(srcadd, bssid, 6))) {
+ ret = mon_mgmt_tx(mon_priv->real_ndev, skb->data, skb->len);
+ if (ret)
+ PRINT_ER(dev, "fail to mgmt tx\n");
+ dev_kfree_skb(skb);
+ } else {
+ ret = wilc_mac_xmit(skb, mon_priv->real_ndev);
+ }
+
+ return ret;
+}
+
+static const struct net_device_ops wilc_wfi_netdev_ops = {
+ .ndo_start_xmit = wilc_wfi_mon_xmit,
+
+};
+
+struct net_device *wilc_wfi_init_mon_interface(struct wilc *wl,
+ const char *name,
+ struct net_device *real_dev)
+{
+ struct wilc_wfi_mon_priv *priv;
+
+ /*If monitor interface is already initialized, return it*/
+ if (wl->monitor_dev)
+ return wl->monitor_dev;
+
+ wl->monitor_dev = alloc_etherdev(sizeof(struct wilc_wfi_mon_priv));
+ if (!wl->monitor_dev) {
+ PRINT_ER(real_dev, "failed to allocate memory\n");
+ return NULL;
+ }
+ wl->monitor_dev->type = ARPHRD_IEEE80211_RADIOTAP;
+ strncpy(wl->monitor_dev->name, name, IFNAMSIZ);
+ wl->monitor_dev->name[IFNAMSIZ - 1] = 0;
+ wl->monitor_dev->netdev_ops = &wilc_wfi_netdev_ops;
+
+ if (register_netdevice(wl->monitor_dev)) {
+ PRINT_ER(real_dev, "register_netdevice failed\n");
+ return NULL;
+ }
+ priv = netdev_priv(wl->monitor_dev);
+ if (!priv) {
+ PRINT_ER(real_dev, "private structure is NULL\n");
+ return NULL;
+ }
+
+ priv->real_ndev = real_dev;
+
+ return wl->monitor_dev;
+}
+
+void wilc_wfi_deinit_mon_interface(struct wilc *wl)
+{
+ if (!wl->monitor_dev)
+ return;
+
+ PRINT_INFO(wl->monitor_dev, HOSTAPD_DBG,
+ "In Deinit monitor interface\n");
+ PRINT_INFO(wl->monitor_dev, HOSTAPD_DBG, "Unregister monitor netdev\n");
+ unregister_netdev(wl->monitor_dev);
+ free_netdev(wl->monitor_dev);
+ PRINT_INFO(wl->monitor_dev, HOSTAPD_DBG,
+ "Deinit monitor interface done\n");
+ wl->monitor_dev = NULL;
+}
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include <linux/irq.h>
+#include <linux/kthread.h>
+#include <linux/firmware.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/interrupt.h>
+#include <net/ip.h>
+#include <linux/module.h>
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+#include <linux/inetdevice.h>
+#endif /* DISABLE_PWRSAVE_AND_SCAN_DURING_IP */
+
+#include "wilc_netdev.h"
+#include "wilc_wfi_cfgoperations.h"
+
+#define WILC_MULTICAST_TABLE_SIZE 8
+
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+bool g_ignore_PS_state;
+#define WILC_IP_TIMEOUT_MS 15000
+
+void handle_pwrsave_for_IP(struct wilc_vif *vif, uint8_t state)
+{
+ struct wilc_priv *priv;
+
+ priv = wdev_priv(vif->ndev->ieee80211_ptr);
+
+ switch (state) {
+ case IP_STATE_OBTAINING:
+
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Obtaining IP, Disable (Scan-Set PowerSave)\n");
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Save the Current state of the PS = %d\n",
+ vif->pwrsave_current_state);
+
+ vif->obtaining_ip = true;
+
+ /* Set this flag to avoid storing the disabled case of PS which
+ * occurs duringIP
+ */
+ g_ignore_PS_state = true;
+
+ wilc_set_power_mgmt(vif, 0, 0);
+
+ /* Start the DuringIPTimer */
+ #if KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE
+ vif->during_ip_timer.data = (uint32_t)vif;
+ #endif
+ mod_timer(&vif->during_ip_timer,
+ (jiffies + msecs_to_jiffies(20000)));
+
+ break;
+
+ case IP_STATE_OBTAINED:
+
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "IP obtained , Enable (Scan-Set PowerSave)\n");
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Recover the state of the PS = %d\n",
+ vif->pwrsave_current_state);
+
+ vif->obtaining_ip = false;
+
+ wilc_set_power_mgmt(vif, vif->pwrsave_current_state, 0);
+
+ del_timer(&vif->during_ip_timer);
+
+ break;
+
+ case IP_STATE_GO_ASSIGNING:
+
+ vif->obtaining_ip = true;
+
+ /* Start the DuringIPTimer */
+ #if KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE
+ vif->during_ip_timer.data = (uint32_t)vif;
+ #endif
+ mod_timer(&vif->during_ip_timer,
+ (jiffies + msecs_to_jiffies(WILC_IP_TIMEOUT_MS)));
+
+ break;
+
+ default: //IP_STATE_DEFAULT
+
+ vif->obtaining_ip = false;
+
+ /* Stop the DuringIPTimer */
+ del_timer(&vif->during_ip_timer);
+
+ break;
+ }
+}
+
+void store_power_save_current_state(struct wilc_vif *vif, bool val)
+{
+ if (g_ignore_PS_state) {
+ g_ignore_PS_state = false;
+ return;
+ }
+ vif->pwrsave_current_state = val;
+}
+
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+void clear_during_ip(struct timer_list *t)
+#else
+void clear_during_ip(unsigned long arg)
+#endif
+{
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+ struct wilc_vif *vif = from_timer(vif, t, during_ip_timer);
+#else
+ struct wilc_vif *vif = (struct wilc_vif *)arg;
+#endif
+
+ PRINT_ER(vif->ndev, "Unable to Obtain IP\n");
+
+ vif->obtaining_ip = false;
+
+ wilc_powersave_state_changes(vif);
+}
+#endif /* DISABLE_PWRSAVE_AND_SCAN_DURING_IP */
+
+static int wilc_mac_open(struct net_device *ndev);
+static int wilc_mac_close(struct net_device *ndev);
+
+int debug_running;
+int recovery_on;
+int wait_for_recovery;
+static int debug_thread(void *arg)
+{
+ struct net_device *dev = arg;
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wl;
+ struct wilc_priv *priv = wiphy_priv(vif->ndev->ieee80211_ptr->wiphy);
+ signed long timeout;
+ struct host_if_drv *hif_drv = (struct host_if_drv *)priv->hif_drv;
+ int i = 0;
+
+ if (!vif)
+ return -1;
+
+ wl = vif->wilc;
+ if (!wl)
+ return -1;
+
+ complete(&wl->debug_thread_started);
+
+ while (1) {
+ if (!wl->initialized && !kthread_should_stop()) {
+ msleep(1000);
+ continue;
+ } else if (!wl->initialized) {
+ break;
+ }
+
+ if (wait_for_completion_timeout(&wl->debug_thread_started,
+ msecs_to_jiffies(6000))) {
+ while (!kthread_should_stop())
+ schedule();
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Exit debug thread\n");
+ return 0;
+ }
+
+ if (!debug_running)
+ continue;
+ PRINT_INFO(dev, GENERIC_DBG,
+ "*** Debug Thread Running ***\n");
+ if (cfg_packet_timeout < 5)
+ continue;
+
+ PRINT_INFO(dev, GENERIC_DBG,
+ "<Recover>\n");
+ cfg_packet_timeout = 0;
+ timeout = 10;
+ recovery_on = 1;
+ wait_for_recovery = 1;
+ for (i = 0; i < WILC_NUM_CONCURRENT_IFC; i++)
+ wilc_mac_close(wl->vif[i]->ndev);
+ for (i = WILC_NUM_CONCURRENT_IFC; i > 0; i--) {
+ while (wilc_mac_open(wl->vif[i-1]->ndev) && --timeout)
+ msleep(100);
+
+ if (timeout == 0)
+ PRINT_WRN(vif->ndev, GENERIC_DBG,
+ "Couldn't restart ifc %d\n", i);
+ }
+ if (hif_drv->hif_state == HOST_IF_CONNECTED) {
+ struct wilc_conn_info *conn_info = &hif_drv->conn_info;
+
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "notify the user with the Disconnection\n");
+ if (hif_drv->usr_scan_req.scan_result) {
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Abort the running OBSS Scan\n");
+ del_timer(&hif_drv->scan_timer);
+ handle_scan_done(vif, SCAN_EVENT_ABORTED);
+ }
+ if (conn_info->conn_result) {
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+
+ handle_pwrsave_for_IP(vif, IP_STATE_DEFAULT);
+#endif
+
+ conn_info->conn_result(EVENT_DISCONN_NOTIF,
+ 0, conn_info->arg);
+ } else {
+ PRINT_ER(vif->ndev, "Connect result NULL\n");
+ }
+ eth_zero_addr(hif_drv->assoc_bssid);
+
+ conn_info->req_ies_len = 0;
+ kfree(conn_info->req_ies);
+ conn_info->req_ies = NULL;
+
+ hif_drv->hif_state = HOST_IF_IDLE;
+ }
+ recovery_on = 0;
+ }
+ return 0;
+}
+
+void wilc_disable_irq(struct wilc *wilc, int wait)
+{
+ if (wait) {
+ PRINT_INFO(wilc->vif[0]->ndev, INT_DBG, "Disabling IRQ ...\n");
+ disable_irq(wilc->dev_irq_num);
+ } else {
+ PRINT_INFO(wilc->vif[0]->ndev, INT_DBG, "Disabling IRQ ...\n");
+ disable_irq_nosync(wilc->dev_irq_num);
+ }
+}
+
+static irqreturn_t host_wakeup_isr(int irq, void *user_data)
+{
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t isr_uh_routine(int irq, void *user_data)
+{
+ struct wilc *wilc = (struct wilc *)user_data;
+ struct net_device *dev = wilc->vif[0]->ndev;
+
+
+ PRINT_INFO(dev, INT_DBG, "Interrupt received UH\n");
+
+ if (wilc->close) {
+ PRINT_ER(dev, "Can't handle UH interrupt\n");
+ return IRQ_HANDLED;
+ }
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t isr_bh_routine(int irq, void *userdata)
+{
+ struct wilc *wilc = (struct wilc *)userdata;
+ struct net_device *dev = wilc->vif[0]->ndev;
+
+
+ if (wilc->close) {
+ PRINT_ER(dev, "Can't handle BH interrupt\n");
+ return IRQ_HANDLED;
+ }
+
+ PRINT_INFO(dev, INT_DBG, "Interrupt received BH\n");
+ wilc_handle_isr(wilc);
+
+ return IRQ_HANDLED;
+}
+
+static int init_irq(struct net_device *dev)
+{
+ int ret = 0;
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wl = vif->wilc;
+
+#if KERNEL_VERSION(3, 13, 0) < LINUX_VERSION_CODE
+
+ wl->gpio_irq = gpiod_get(wl->dt_dev, "irq", GPIOD_IN);
+ if (IS_ERR(wl->gpio_irq)) {
+ dev_warn(wl->dev, "failed to get IRQ GPIO, load default\r\n");
+ wl->gpio_irq = gpio_to_desc(GPIO_NUM);
+ if (!wl->gpio_irq) {
+ dev_warn(wl->dev, "failed to load default irq\r\n");
+ return -1;
+ }
+ } else {
+ dev_info(wl->dev, "got gpio_irq successfully\r\n");
+ }
+
+ ret = gpiod_direction_input(wl->gpio_irq);
+ if (ret) {
+ PRINT_ER(dev, "could not obtain gpio for WILC_INTR\n");
+ return ret;
+ }
+
+ wl->dev_irq_num = gpiod_to_irq(wl->gpio_irq);
+ if (wl->dev_irq_num < 0) {
+ PRINT_ER(dev, "could not the IRQ\n");
+ goto free_gpio;
+ }
+#else
+ wl->gpio_irq = of_get_named_gpio_flags(wl->dt_dev->of_node,
+ "irq-gpios", 0, NULL);
+ if (wl->gpio_irq < 0) {
+ wl->gpio_irq = GPIO_NUM;
+ dev_warn(wl->dev, "failed to get IRQ GPIO, load default\r\n");
+ }
+
+ if ((gpio_request(wl->gpio_irq, "WILC_INTR") == 0) &&
+ (gpio_direction_input(wl->gpio_irq) == 0)) {
+ wl->dev_irq_num = gpio_to_irq(wl->gpio_irq);
+ } else {
+ dev_err(wl->dev, "could not obtain gpio for WILC_INTR\n");
+ wl->gpio_irq = 0;
+ return -1;
+ }
+
+#endif
+
+ if (wl->io_type == WILC_HIF_SPI ||
+ wl->io_type == WILC_HIF_SDIO_GPIO_IRQ) {
+ if (request_threaded_irq(wl->dev_irq_num, isr_uh_routine,
+ isr_bh_routine, IRQF_TRIGGER_LOW |
+ IRQF_ONESHOT |
+ IRQF_NO_SUSPEND,
+ "WILC_IRQ", wl) < 0) {
+ PRINT_ER(dev, "Failed to request IRQ\n");
+ goto free_gpio;
+ }
+ } else {
+ if (request_irq(wl->dev_irq_num, host_wakeup_isr,
+ IRQF_TRIGGER_FALLING |
+ IRQF_NO_SUSPEND,
+ "WILC_IRQ", wl) < 0) {
+ PRINT_ER(dev, "Failed to request IRQ\n");
+ goto free_gpio;
+ }
+ }
+
+ PRINT_INFO(dev, GENERIC_DBG, "IRQ request succeeded IRQ-NUM= %d\n",
+ wl->dev_irq_num);
+ enable_irq_wake(wl->dev_irq_num);
+ return ret;
+
+free_gpio:
+#if KERNEL_VERSION(3, 13, 0) < LINUX_VERSION_CODE
+ gpiod_put(wl->gpio_irq);
+ wl->gpio_irq = NULL;
+#else
+ gpio_free(wl->gpio_irq);
+ wl->gpio_irq = 0;
+#endif
+ return -1;
+}
+
+static void deinit_irq(struct net_device *dev)
+{
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wilc = vif->wilc;
+
+ /* Deinitialize IRQ */
+ if (wilc->dev_irq_num > 0) {
+ free_irq(wilc->dev_irq_num, wilc);
+ wilc->dev_irq_num = -1;
+ }
+
+#if KERNEL_VERSION(3, 13, 0) < LINUX_VERSION_CODE
+ if (wilc->gpio_irq) {
+ gpiod_put(wilc->gpio_irq);
+ wilc->gpio_irq = NULL;
+ }
+#else
+ if (wilc->gpio_irq > 0) {
+ gpio_free(wilc->gpio_irq);
+ wilc->gpio_irq = 0;
+ }
+
+#endif
+
+}
+
+void wilc_mac_indicate(struct wilc *wilc)
+{
+ s8 status;
+
+ cfg_get_val(wilc, WID_STATUS, &status, 1);
+ if (wilc->mac_status == WILC_MAC_STATUS_INIT) {
+ wilc->mac_status = status;
+ complete(&wilc->sync_event);
+ } else {
+ wilc->mac_status = status;
+ }
+}
+
+void wilc_frmw_to_host(struct wilc_vif *vif, u8 *buff, u32 size,
+ u32 pkt_offset, u8 status)
+{
+ unsigned int frame_len = 0;
+ int stats;
+ unsigned char *buff_to_send = NULL;
+ struct sk_buff *skb;
+ struct wilc_priv *priv;
+ u8 null_bssid[ETH_ALEN] = {0};
+
+ buff += pkt_offset;
+ priv = wiphy_priv(vif->ndev->ieee80211_ptr->wiphy);
+
+ if (size == 0) {
+ PRINT_ER(vif->ndev,
+ "Discard sending packet with len = %d\n", size);
+ return;
+ }
+
+ frame_len = size;
+ buff_to_send = buff;
+
+ if (status == PKT_STATUS_NEW && buff_to_send[12] == 0x88 &&
+ buff_to_send[13] == 0x8e &&
+ (vif->iftype == WILC_STATION_MODE ||
+ vif->iftype == WILC_CLIENT_MODE) &&
+ ether_addr_equal_unaligned(priv->associated_bss, null_bssid)) {
+ if (!priv->buffered_eap) {
+ priv->buffered_eap = kmalloc(sizeof(struct
+ wilc_buffered_eap),
+ GFP_ATOMIC);
+ if (priv->buffered_eap) {
+ priv->buffered_eap->buff = NULL;
+ priv->buffered_eap->size = 0;
+ priv->buffered_eap->pkt_offset = 0;
+ } else {
+ PRINT_ER(vif->ndev,
+ "failed to alloc buffered_eap\n");
+ return;
+ }
+ } else {
+ kfree(priv->buffered_eap->buff);
+ }
+ priv->buffered_eap->buff = kmalloc(size + pkt_offset,
+ GFP_ATOMIC);
+ priv->buffered_eap->size = size;
+ priv->buffered_eap->pkt_offset = pkt_offset;
+ memcpy(priv->buffered_eap->buff, buff -
+ pkt_offset, size + pkt_offset);
+ #if KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE
+ priv->eap_buff_timer.data = (unsigned long) priv;
+ #endif
+ mod_timer(&priv->eap_buff_timer, (jiffies +
+ msecs_to_jiffies(10)));
+ return;
+ }
+ skb = dev_alloc_skb(frame_len);
+ if (!skb) {
+ PRINT_ER(vif->ndev, "Low memory - packet droped\n");
+ return;
+ }
+
+ skb->dev = vif->ndev;
+#if KERNEL_VERSION(4, 13, 0) <= LINUX_VERSION_CODE
+ skb_put_data(skb, buff_to_send, frame_len);
+#else
+ memcpy(skb_put(skb, frame_len), buff_to_send, frame_len);
+#endif
+
+ skb->protocol = eth_type_trans(skb, vif->ndev);
+ vif->netstats.rx_packets++;
+ vif->netstats.rx_bytes += frame_len;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ stats = netif_rx(skb);
+ PRINT_D(vif->ndev, RX_DBG, "netif_rx ret value: %d\n", stats);
+}
+
+void free_eap_buff_params(void *vp)
+{
+ struct wilc_priv *priv;
+
+ priv = (struct wilc_priv *)vp;
+
+ if (priv->buffered_eap) {
+ kfree(priv->buffered_eap->buff);
+ priv->buffered_eap->buff = NULL;
+
+ kfree(priv->buffered_eap);
+ priv->buffered_eap = NULL;
+ }
+}
+
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+void eap_buff_timeout(struct timer_list *t)
+#else
+void eap_buff_timeout(unsigned long user)
+#endif
+{
+ u8 null_bssid[ETH_ALEN] = {0};
+ u8 *assoc_bss;
+ static u8 timeout = 5;
+ int status = -1;
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+ struct wilc_priv *priv = from_timer(priv, t, eap_buff_timer);
+#else
+ struct wilc_priv *priv = (struct wilc_priv *)user;
+#endif
+ struct wilc_vif *vif = netdev_priv(priv->dev);
+
+ assoc_bss = priv->associated_bss;
+ if (!(memcmp(assoc_bss, null_bssid, ETH_ALEN)) && (timeout-- > 0)) {
+ mod_timer(&priv->eap_buff_timer,
+ (jiffies + msecs_to_jiffies(10)));
+ return;
+ }
+ del_timer(&priv->eap_buff_timer);
+ timeout = 5;
+
+ status = wilc_send_buffered_eap(vif, wilc_frmw_to_host,
+ free_eap_buff_params,
+ priv->buffered_eap->buff,
+ priv->buffered_eap->size,
+ priv->buffered_eap->pkt_offset,
+ (void *)priv);
+ if (status)
+ PRINT_ER(vif->ndev, "Failed so send buffered eap\n");
+}
+
+void wilc_wlan_set_bssid(struct net_device *wilc_netdev, u8 *bssid, u8 mode)
+{
+ struct wilc_vif *vif = netdev_priv(wilc_netdev);
+ struct wilc *wilc = vif->wilc;
+ u8 i = 0;
+
+ PRINT_INFO(vif->ndev, GENERIC_DBG, "set bssid on[%p]\n", wilc_netdev);
+ for (i = 0; i < wilc->vif_num; i++) {
+ if (wilc_netdev == wilc->vif[i]->ndev) {
+ if (bssid)
+ ether_addr_copy(wilc->vif[i]->bssid, bssid);
+ else
+ eth_zero_addr(wilc->vif[i]->bssid);
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "set bssid [%pM]\n", wilc->vif[i]->bssid);
+ wilc->vif[i]->iftype = mode;
+ }
+ }
+}
+
+int wilc_wlan_get_num_conn_ifcs(struct wilc *wilc)
+{
+ u8 i = 0;
+ u8 ret_val = 0;
+
+ for (i = 0; i < wilc->vif_num; i++)
+ if (!is_zero_ether_addr(wilc->vif[i]->bssid))
+ ret_val++;
+
+ return ret_val;
+}
+
+struct net_device *wilc_get_if_netdev(struct wilc *wilc, uint8_t ifc)
+{
+ return wilc->vif[ifc]->ndev;
+}
+
+struct host_if_drv *get_drv_hndl_by_ifc(struct wilc *wilc, uint8_t ifc)
+{
+ return wilc->vif[ifc]->hif_drv;
+}
+
+#define TX_BACKOFF_WEIGHT_INCR_STEP (1)
+#define TX_BACKOFF_WEIGHT_DECR_STEP (1)
+#define TX_BACKOFF_WEIGHT_MAX (0)
+#define TX_BACKOFF_WEIGHT_MIN (0)
+#define TX_BCKOFF_WGHT_MS (1)
+
+
+static int wilc_txq_task(void *vp)
+{
+ int ret;
+ u32 txq_count;
+ struct net_device *ndev = vp;
+ int backoff_weight = TX_BACKOFF_WEIGHT_MIN;
+ signed long timeout;
+ struct wilc_vif *vif = netdev_priv(ndev);
+ struct wilc *wl = vif->wilc;
+
+ complete(&wl->txq_thread_started);
+ while (1) {
+ PRINT_INFO(ndev, TX_DBG, "txq_task Taking a nap\n");
+ wait_for_completion(&wl->txq_event);
+ PRINT_INFO(ndev, TX_DBG, "txq_task Who waked me up\n");
+ if (wl->close) {
+ complete(&wl->txq_thread_started);
+
+ while (!kthread_should_stop())
+ schedule();
+ PRINT_INFO(ndev, TX_DBG, "TX thread stopped\n");
+ break;
+ }
+ PRINT_INFO(ndev, TX_DBG, "handle the tx packet\n");
+ do {
+ ret = wilc_wlan_handle_txq(ndev, &txq_count);
+ if (txq_count < FLOW_CTRL_LOW_THRESHLD) {
+ PRINT_INFO(ndev, TX_DBG, "Waking up queue\n");
+ if (wl->vif[0]->mac_opened &&
+ netif_queue_stopped(wl->vif[0]->ndev))
+ netif_wake_queue(wl->vif[0]->ndev);
+
+ if (wl->vif[1]->mac_opened &&
+ netif_queue_stopped(wl->vif[1]->ndev))
+ netif_wake_queue(wl->vif[1]->ndev);
+ }
+
+ if (ret == -ENOBUFS) {
+ timeout = msecs_to_jiffies(TX_BCKOFF_WGHT_MS <<
+ backoff_weight);
+ do {
+ /* Back off from sending packets for some time.
+ * schedule_timeout will allow RX task to run and free
+ * buffers. Setting state to TASK_INTERRUPTIBLE will
+ * put the thread back to CPU running queue when it's
+ * signaled even if 'timeout' isn't elapsed. This gives
+ * faster chance for reserved SK buffers to be freed
+ */
+ set_current_state(TASK_INTERRUPTIBLE);
+ timeout = schedule_timeout(timeout);
+ } while (/*timeout*/0);
+ backoff_weight += TX_BACKOFF_WEIGHT_INCR_STEP;
+ if (backoff_weight > TX_BACKOFF_WEIGHT_MAX)
+ backoff_weight = TX_BACKOFF_WEIGHT_MAX;
+ } else if (backoff_weight > TX_BACKOFF_WEIGHT_MIN) {
+ backoff_weight -= TX_BACKOFF_WEIGHT_DECR_STEP;
+ if (backoff_weight < TX_BACKOFF_WEIGHT_MIN)
+ backoff_weight = TX_BACKOFF_WEIGHT_MIN;
+ }
+ } while (ret == -ENOBUFS && !wl->close);
+ }
+ return 0;
+}
+
+static int wilc_wlan_get_firmware(struct net_device *dev)
+{
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wilc = vif->wilc;
+ int ret = 0;
+ const struct firmware *wilc_firmware;
+ char *firmware;
+
+
+ if (wilc->chip == WILC_3000) {
+ PRINT_INFO(dev, INIT_DBG, "Detect chip WILC3000\n");
+ firmware = FW_WILC3000_WIFI;
+ } else if (wilc->chip == WILC_1000) {
+ PRINT_INFO(dev, INIT_DBG, "Detect chip WILC1000\n");
+ firmware = FW_WILC1000_WIFi;
+ } else {
+ return -1;
+ }
+
+ PRINT_INFO(dev, INIT_DBG, "loading firmware %s\n", firmware);
+
+ if (!(&vif->ndev->dev)) {
+ PRINT_ER(dev, "Dev is NULL\n");
+ goto fail;
+ }
+
+ PRINT_INFO(vif->ndev, INIT_DBG, "WLAN firmware: %s\n", firmware);
+ if (request_firmware(&wilc_firmware, firmware, wilc->dev) != 0) {
+ PRINT_ER(dev, "%s - firmware not available\n", firmware);
+ ret = -1;
+ goto fail;
+ }
+ wilc->firmware = wilc_firmware;
+
+fail:
+
+ return ret;
+}
+
+static int wilc_start_firmware(struct net_device *dev)
+{
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wilc = vif->wilc;
+ int ret = 0;
+
+ PRINT_INFO(vif->ndev, INIT_DBG, "Starting Firmware ...\n");
+
+ ret = wilc_wlan_start(wilc);
+ if (ret < 0) {
+ PRINT_ER(dev, "Failed to start Firmware\n");
+ return ret;
+ }
+ PRINT_INFO(vif->ndev, INIT_DBG, "Waiting for FW to get ready ...\n");
+
+ if (!wait_for_completion_timeout(&wilc->sync_event,
+ msecs_to_jiffies(500))) {
+ PRINT_INFO(vif->ndev, INIT_DBG, "Firmware start timed out\n");
+ return -ETIME;
+ }
+ PRINT_INFO(vif->ndev, INIT_DBG, "Firmware successfully started\n");
+
+ return 0;
+}
+
+static int wilc_firmware_download(struct net_device *dev)
+{
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wilc = vif->wilc;
+ int ret = 0;
+
+ if (!wilc->firmware) {
+ PRINT_ER(dev, "Firmware buffer is NULL\n");
+ ret = -ENOBUFS;
+ }
+ PRINT_INFO(vif->ndev, INIT_DBG, "Downloading Firmware ...\n");
+ ret = wilc_wlan_firmware_download(wilc, wilc->firmware->data,
+ wilc->firmware->size);
+ if (ret < 0)
+ goto fail;
+
+ PRINT_INFO(vif->ndev, INIT_DBG, "Download Succeeded\n");
+
+fail:
+ release_firmware(wilc->firmware);
+ wilc->firmware = NULL;
+
+ return ret;
+}
+
+static int wilc_init_fw_config(struct net_device *dev, struct wilc_vif *vif)
+{
+ struct wilc_priv *priv;
+ struct host_if_drv *hif_drv;
+ u8 b;
+ u16 hw;
+ u32 w;
+
+ PRINT_INFO(vif->ndev, INIT_DBG, "Start configuring Firmware\n");
+ priv = wiphy_priv(dev->ieee80211_ptr->wiphy);
+ hif_drv = (struct host_if_drv *)priv->hif_drv;
+ PRINT_D(vif->ndev, INIT_DBG, "Host = %p\n", hif_drv);
+
+ w = vif->iftype;
+ cpu_to_le32s(&w);
+ if (!cfg_set(vif, 1, WID_SET_OPERATION_MODE, (u8 *)&w, 4, 0, 0))
+ goto fail;
+
+ b = WILC_FW_BSS_TYPE_INFRA;
+ if (!cfg_set(vif, 0, WID_BSS_TYPE, &b, 1, 0, 0))
+ goto fail;
+
+ b = WILC_FW_TX_RATE_AUTO;
+ if (!cfg_set(vif, 0, WID_CURRENT_TX_RATE, &b, 1, 0, 0))
+ goto fail;
+
+ b = WILC_FW_OPER_MODE_G_MIXED_11B_2;
+ if (!cfg_set(vif, 0, WID_11G_OPERATING_MODE, &b, 1, 0,
+ 0))
+ goto fail;
+
+ b = WILC_FW_PREAMBLE_AUTO;
+ if (!cfg_set(vif, 0, WID_PREAMBLE, &b, 1, 0, 0))
+ goto fail;
+
+ b = WILC_FW_11N_PROT_AUTO;
+ if (!cfg_set(vif, 0, WID_11N_PROT_MECH, &b, 1, 0, 0))
+ goto fail;
+
+ b = WILC_FW_ACTIVE_SCAN;
+ if (!cfg_set(vif, 0, WID_SCAN_TYPE, &b, 1, 0, 0))
+ goto fail;
+
+ b = WILC_FW_SITE_SURVEY_OFF;
+ if (!cfg_set(vif, 0, WID_SITE_SURVEY, &b, 1, 0, 0))
+ goto fail;
+
+ hw = 0xffff;
+ cpu_to_le16s(&hw);
+ if (!cfg_set(vif, 0, WID_RTS_THRESHOLD, (u8 *)&hw, 2, 0, 0))
+ goto fail;
+
+ hw = 2346;
+ cpu_to_le16s(&hw);
+ if (!cfg_set(vif, 0, WID_FRAG_THRESHOLD, (u8 *)&hw, 2, 0, 0))
+ goto fail;
+
+ b = 0;
+ if (!cfg_set(vif, 0, WID_BCAST_SSID, &b, 1, 0, 0))
+ goto fail;
+
+ b = 1;
+ if (!cfg_set(vif, 0, WID_QOS_ENABLE, &b, 1, 0, 0))
+ goto fail;
+
+ b = WILC_FW_NO_POWERSAVE;
+ if (!cfg_set(vif, 0, WID_POWER_MANAGEMENT, &b, 1, 0, 0))
+ goto fail;
+
+ b = WILC_FW_SEC_NO;
+ if (!cfg_set(vif, 0, WID_11I_MODE, &b, 1, 0, 0))
+ goto fail;
+
+ b = WILC_FW_AUTH_OPEN_SYSTEM;
+ if (!cfg_set(vif, 0, WID_AUTH_TYPE, &b, 1, 0, 0))
+ goto fail;
+
+ b = 3;
+ if (!cfg_set(vif, 0, WID_LISTEN_INTERVAL, &b, 1, 0, 0))
+ goto fail;
+
+ b = 3;
+ if (!cfg_set(vif, 0, WID_DTIM_PERIOD, &b, 1, 0, 0))
+ goto fail;
+
+ b = WILC_FW_ACK_POLICY_NORMAL;
+ if (!cfg_set(vif, 0, WID_ACK_POLICY, &b, 1, 0, 0))
+ goto fail;
+
+ b = 0;
+ if (!cfg_set(vif, 0, WID_USER_CONTROL_ON_TX_POWER, &b, 1,
+ 0, 0))
+ goto fail;
+
+ b = 48;
+ if (!cfg_set(vif, 0, WID_TX_POWER_LEVEL_11A, &b, 1, 0,
+ 0))
+ goto fail;
+
+ b = 28;
+ if (!cfg_set(vif, 0, WID_TX_POWER_LEVEL_11B, &b, 1, 0,
+ 0))
+ goto fail;
+
+ hw = 100;
+ cpu_to_le16s(&hw);
+ if (!cfg_set(vif, 0, WID_BEACON_INTERVAL, (u8 *)&hw, 2, 0, 0))
+ goto fail;
+
+ b = WILC_FW_REKEY_POLICY_DISABLE;
+ if (!cfg_set(vif, 0, WID_REKEY_POLICY, &b, 1, 0, 0))
+ goto fail;
+
+ w = 84600;
+ cpu_to_le32s(&w);
+ if (!cfg_set(vif, 0, WID_REKEY_PERIOD, (u8 *)&w, 4, 0, 0))
+ goto fail;
+
+ w = 500;
+ cpu_to_le32s(&w);
+ if (!cfg_set(vif, 0, WID_REKEY_PACKET_COUNT, (u8 *)&w, 4, 0,
+ 0))
+ goto fail;
+
+ b = 1;
+ if (!cfg_set(vif, 0, WID_SHORT_SLOT_ALLOWED, &b, 1, 0,
+ 0))
+ goto fail;
+
+ b = WILC_FW_ERP_PROT_SELF_CTS;
+ if (!cfg_set(vif, 0, WID_11N_ERP_PROT_TYPE, &b, 1, 0, 0))
+ goto fail;
+
+ b = 1;
+ if (!cfg_set(vif, 0, WID_11N_ENABLE, &b, 1, 0, 0))
+ goto fail;
+
+ b = WILC_FW_11N_OP_MODE_HT_MIXED;
+ if (!cfg_set(vif, 0, WID_11N_OPERATING_MODE, &b, 1, 0,
+ 0))
+ goto fail;
+
+ b = 1;
+ if (!cfg_set(vif, 0, WID_11N_TXOP_PROT_DISABLE, &b, 1, 0,
+ 0))
+ goto fail;
+
+ b = WILC_FW_OBBS_NONHT_DETECT_PROTECT_REPORT;
+ if (!cfg_set(vif, 0, WID_11N_OBSS_NONHT_DETECTION, &b, 1,
+ 0, 0))
+ goto fail;
+
+ b = WILC_FW_HT_PROT_RTS_CTS_NONHT;
+ if (!cfg_set(vif, 0, WID_11N_HT_PROT_TYPE, &b, 1, 0, 0))
+ goto fail;
+
+ b = 0;
+ if (!cfg_set(vif, 0, WID_11N_RIFS_PROT_ENABLE, &b, 1, 0,
+ 0))
+ goto fail;
+
+ b = 7;
+ if (!cfg_set(vif, 0, WID_11N_CURRENT_TX_MCS, &b, 1, 0,
+ 0))
+ goto fail;
+
+ b = 1;
+ if (!cfg_set(vif, 0, WID_11N_IMMEDIATE_BA_ENABLED, &b, 1,
+ 1, 0))
+ goto fail;
+
+ return 0;
+
+fail:
+ return -1;
+}
+
+static void wlan_deinit_locks(struct wilc *wilc)
+{
+ pr_info("De-Initializing Locks\n");
+
+ mutex_destroy(&wilc->hif_cs);
+ mutex_destroy(&wilc->rxq_cs);
+ mutex_destroy(&wilc->cfg_cmd_lock);
+ mutex_destroy(&wilc->txq_add_to_head_cs);
+ mutex_destroy(&wilc->cs);
+}
+
+static void wlan_deinitialize_threads(struct net_device *dev)
+{
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wl = vif->wilc;
+
+ PRINT_INFO(vif->ndev, INIT_DBG, "Deinitializing Threads\n");
+ if (!recovery_on) {
+ PRINT_INFO(vif->ndev, INIT_DBG, "Deinit debug Thread\n");
+ debug_running = false;
+ if (&wl->debug_thread_started)
+ complete(&wl->debug_thread_started);
+ if (wl->debug_thread) {
+ kthread_stop(wl->debug_thread);
+ wl->debug_thread = NULL;
+ }
+ }
+
+ wl->close = 1;
+ PRINT_INFO(vif->ndev, INIT_DBG, "Deinitializing Threads\n");
+
+ complete(&wl->txq_event);
+
+ if (wl->txq_thread) {
+ kthread_stop(wl->txq_thread);
+ wl->txq_thread = NULL;
+ }
+}
+
+static void wilc_wlan_deinitialize(struct net_device *dev)
+{
+ int ret;
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wl = vif->wilc;
+
+ if (wl->initialized) {
+ PRINT_INFO(vif->ndev, INIT_DBG, "Deinitializing wilc ...\n");
+
+ if (!wl) {
+ PRINT_ER(dev, "wl is NULL\n");
+ return;
+ }
+
+ PRINT_D(vif->ndev, INIT_DBG, "destroy aging timer\n");
+
+ PRINT_INFO(vif->ndev, INIT_DBG, "Disabling IRQ\n");
+ if (wl->io_type == WILC_HIF_SPI ||
+ wl->io_type == WILC_HIF_SDIO_GPIO_IRQ) {
+ wilc_disable_irq(wl, 1);
+ } else {
+ if (wl->hif_func->disable_interrupt) {
+ mutex_lock(&wl->hif_cs);
+ wl->hif_func->disable_interrupt(wl);
+ mutex_unlock(&wl->hif_cs);
+ }
+ }
+ complete(&wl->txq_event);
+
+ PRINT_INFO(vif->ndev, INIT_DBG, "Deinitializing Threads\n");
+ wlan_deinitialize_threads(dev);
+ PRINT_INFO(vif->ndev, INIT_DBG, "Deinitializing IRQ\n");
+ deinit_irq(dev);
+
+ ret = wilc_wlan_stop(wl, vif);
+ if (ret == 0)
+ PRINT_ER(dev, "failed in wlan_stop\n");
+
+ PRINT_INFO(vif->ndev, INIT_DBG, "Deinitializing WILC Wlan\n");
+ wilc_wlan_cleanup(dev);
+
+ wl->initialized = false;
+
+ PRINT_INFO(dev, INIT_DBG, "wilc deinitialization Done\n");
+ } else {
+ PRINT_INFO(dev, INIT_DBG, "wilc is not initialized\n");
+ }
+}
+
+static void wlan_init_locks(struct wilc *wl)
+{
+ pr_info("Initializing Locks ...\n");
+
+ mutex_init(&wl->rxq_cs);
+ mutex_init(&wl->cfg_cmd_lock);
+
+ spin_lock_init(&wl->txq_spinlock);
+ mutex_init(&wl->txq_add_to_head_cs);
+ mutex_init(&wl->hif_cs);
+ mutex_init(&wl->cs);
+
+ init_completion(&wl->txq_event);
+
+ init_completion(&wl->cfg_event);
+ init_completion(&wl->sync_event);
+ init_completion(&wl->txq_thread_started);
+ init_completion(&wl->debug_thread_started);
+}
+
+static int wlan_initialize_threads(struct net_device *dev)
+{
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wilc = vif->wilc;
+
+ PRINT_INFO(vif->ndev, INIT_DBG, "Initializing Threads ...\n");
+ PRINT_INFO(vif->ndev, INIT_DBG, "Creating kthread for transmission\n");
+ wilc->txq_thread = kthread_run(wilc_txq_task, (void *)dev,
+ "K_TXQ_TASK");
+ if (IS_ERR(wilc->txq_thread)) {
+ PRINT_ER(dev, "couldn't create TXQ thread\n");
+ wilc->close = 1;
+ return PTR_ERR(wilc->txq_thread);
+ }
+ wait_for_completion(&wilc->txq_thread_started);
+
+ if (!debug_running) {
+ PRINT_INFO(vif->ndev, INIT_DBG,
+ "Creating kthread for Debugging\n");
+ wilc->debug_thread = kthread_run(debug_thread, (void *)dev,
+ "WILC_DEBUG");
+ if (IS_ERR(wilc->debug_thread)) {
+ PRINT_ER(dev, "couldn't create debug thread\n");
+ wilc->close = 1;
+ kthread_stop(wilc->txq_thread);
+ return PTR_ERR(wilc->debug_thread);
+ }
+ debug_running = true;
+ wait_for_completion(&wilc->debug_thread_started);
+ }
+
+ return 0;
+}
+
+static int wilc_wlan_initialize(struct net_device *dev, struct wilc_vif *vif)
+{
+ int ret = 0;
+ struct wilc *wl = vif->wilc;
+
+ if (!wl->initialized) {
+ wl->mac_status = WILC_MAC_STATUS_INIT;
+ wl->close = 0;
+ wl->initialized = 0;
+
+ ret = wilc_wlan_init(dev);
+ if (ret < 0) {
+ PRINT_ER(dev, "Initializing WILC_Wlan FAILED\n");
+ ret = -EIO;
+ goto fail;
+ }
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "WILC Initialization done\n");
+ if (init_irq(dev)) {
+ ret = -EIO;
+ goto fail;
+ }
+
+ ret = wlan_initialize_threads(dev);
+ if (ret < 0) {
+ PRINT_ER(dev, "Initializing Threads FAILED\n");
+ ret = -EIO;
+ goto fail_wilc_wlan;
+ }
+
+ if (wl->io_type == WILC_HIF_SDIO &&
+ wl->hif_func->enable_interrupt(wl)) {
+ PRINT_ER(dev, "couldn't initialize IRQ\n");
+ ret = -EIO;
+ goto fail_irq_init;
+ }
+
+ if (wilc_wlan_get_firmware(dev)) {
+ PRINT_ER(dev, "Can't get firmware\n");
+ ret = -EIO;
+ goto fail_irq_enable;
+ }
+
+ ret = wilc_firmware_download(dev);
+ if (ret < 0) {
+ PRINT_ER(dev, "Failed to download firmware\n");
+ ret = -EIO;
+ goto fail_irq_enable;
+ }
+
+ ret = wilc_start_firmware(dev);
+ if (ret < 0) {
+ PRINT_ER(dev, "Failed to start firmware\n");
+ ret = -EIO;
+ goto fail_irq_enable;
+ }
+
+ if (cfg_get(vif, 1, WID_FIRMWARE_VERSION, 1, 0)) {
+ int size;
+ char firmware_ver[50];
+
+ size = cfg_get_val(wl, WID_FIRMWARE_VERSION,
+ firmware_ver,
+ sizeof(firmware_ver));
+ firmware_ver[size] = '\0';
+ PRINT_INFO(dev, INIT_DBG, "WILC Firmware Ver = %s\n",
+ firmware_ver);
+ }
+
+ ret = wilc_init_fw_config(dev, vif);
+ if (ret < 0) {
+ PRINT_ER(dev, "Failed to configure firmware\n");
+ ret = -EIO;
+ goto fail_fw_start;
+ }
+
+ wl->initialized = true;
+ return 0;
+
+fail_fw_start:
+ wilc_wlan_stop(wl, vif);
+
+fail_irq_enable:
+ if (wl->io_type == WILC_HIF_SDIO)
+ wl->hif_func->disable_interrupt(wl);
+fail_irq_init:
+ deinit_irq(dev);
+
+ wlan_deinitialize_threads(dev);
+fail_wilc_wlan:
+ wilc_wlan_cleanup(dev);
+fail:
+ PRINT_ER(dev, "WLAN initialization FAILED\n");
+ } else {
+ PRINT_WRN(vif->ndev, INIT_DBG, "wilc already initialized\n");
+ }
+ return ret;
+}
+
+static int mac_init_fn(struct net_device *ndev)
+{
+ netif_start_queue(ndev);
+ netif_stop_queue(ndev);
+
+ return 0;
+}
+
+static int wilc_mac_open(struct net_device *ndev)
+{
+ struct wilc_vif *vif = netdev_priv(ndev);
+ struct wilc *wl = vif->wilc;
+ struct wilc_priv *priv = wdev_priv(vif->ndev->ieee80211_ptr);
+ unsigned char mac_add[ETH_ALEN] = {0};
+ int ret = 0;
+
+ if (!wl || !wl->dev) {
+ PRINT_ER(ndev, "device not ready\n");
+ return -ENODEV;
+ }
+
+ PRINT_INFO(ndev, INIT_DBG, "MAC OPEN[%p] %s\n", ndev, ndev->name);
+
+ if (wl->open_ifcs == 0)
+ wilc_bt_power_up(wl, DEV_WIFI);
+
+ if (!recovery_on) {
+ ret = wilc_init_host_int(ndev);
+ if (ret < 0) {
+ PRINT_ER(ndev, "Failed to initialize host interface\n");
+ return ret;
+ }
+ }
+
+ PRINT_INFO(vif->ndev, INIT_DBG, "*** re-init ***\n");
+ ret = wilc_wlan_initialize(ndev, vif);
+ if (ret < 0) {
+ PRINT_ER(ndev, "Failed to initialize wilc\n");
+ if (!recovery_on)
+ wilc_deinit_host_int(ndev);
+ return ret;
+ }
+
+ wait_for_recovery = 0;
+ if (!(memcmp(ndev->name, IFC_0, 5))) {
+ vif->ifc_id = WILC_WLAN_IFC;
+ } else if (!(memcmp(ndev->name, IFC_1, 4))) {
+ vif->ifc_id = WILC_P2P_IFC;
+ } else {
+ PRINT_ER(vif->ndev, "Unknown interface name\n");
+ wilc_deinit_host_int(ndev);
+ wilc_wlan_deinitialize(ndev);
+ return -ENODEV;
+ }
+ wilc_set_wfi_drv_handler(vif, wilc_get_vif_idx(vif),
+ vif->iftype, vif->ifc_id);
+ wilc_set_operation_mode(vif, vif->iftype);
+ wilc_get_mac_address(vif, mac_add);
+ PRINT_INFO(vif->ndev, INIT_DBG, "Mac address: %pM\n", mac_add);
+
+ if (!is_valid_ether_addr(mac_add)) {
+ PRINT_ER(ndev, "Wrong MAC address\n");
+ wilc_deinit_host_int(ndev);
+ wilc_wlan_deinitialize(ndev);
+ return -EINVAL;
+ }
+ ether_addr_copy(ndev->dev_addr, mac_add);
+
+ wilc_mgmt_frame_register(vif->ndev->ieee80211_ptr->wiphy,
+ vif->ndev->ieee80211_ptr,
+ vif->frame_reg[0].type,
+ vif->frame_reg[0].reg);
+ wilc_mgmt_frame_register(vif->ndev->ieee80211_ptr->wiphy,
+ vif->ndev->ieee80211_ptr,
+ vif->frame_reg[1].type,
+ vif->frame_reg[1].reg);
+ netif_wake_queue(ndev);
+ wl->open_ifcs++;
+ priv->p2p.local_random = 0x01;
+ vif->mac_opened = 1;
+ return 0;
+}
+
+static struct net_device_stats *mac_stats(struct net_device *dev)
+{
+ struct wilc_vif *vif = netdev_priv(dev);
+
+ return &vif->netstats;
+}
+
+static int wilc_set_mac_addr(struct net_device *dev, void *p)
+{
+ int result;
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct sockaddr *addr = (struct sockaddr *)p;
+ struct wilc *wilc = vif->wilc;
+ unsigned char mac_addr[6] = {0};
+ int i;
+
+ if (!is_valid_ether_addr(addr->sa_data)) {
+ PRINT_INFO(vif->ndev, INIT_DBG, "Invalid MAC address\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < wilc->vif_num; i++) {
+ wilc_get_mac_address(wilc->vif[i], mac_addr);
+ if (ether_addr_equal(addr->sa_data, mac_addr)) {
+ if (vif != wilc->vif[i]) {
+ PRINT_INFO(vif->ndev, INIT_DBG,
+ "MAC address is alredy in use\n");
+ return -EINVAL;
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ /* configure new MAC address */
+ result = wilc_set_mac_address(vif, (u8 *)addr->sa_data);
+ ether_addr_copy(vif->bssid, addr->sa_data);
+ ether_addr_copy(vif->ndev->dev_addr, vif->bssid);
+
+ return result;
+}
+
+static void wilc_set_multicast_list(struct net_device *dev)
+{
+ struct netdev_hw_addr *ha;
+ struct wilc_vif *vif = netdev_priv(dev);
+ int i;
+ u8 *mc_list;
+ u8 *cur_mc;
+
+ PRINT_INFO(vif->ndev, INIT_DBG,
+ "Setting mcast List with count = %d.\n", dev->mc.count);
+ if (dev->flags & IFF_PROMISC) {
+ PRINT_INFO(vif->ndev, INIT_DBG,
+ "Set promiscuous mode ON retrive all pkts\n");
+ return;
+ }
+
+ if (dev->flags & IFF_ALLMULTI ||
+ dev->mc.count > WILC_MULTICAST_TABLE_SIZE) {
+ PRINT_INFO(vif->ndev, INIT_DBG,
+ "Disable mcast filter retrive multicast pkts\n");
+ wilc_setup_multicast_filter(vif, 0, 0, NULL);
+ return;
+ }
+
+ if (dev->mc.count == 0) {
+ PRINT_INFO(vif->ndev, INIT_DBG,
+ "Enable mcast filter retrive directed pkts only\n");
+ wilc_setup_multicast_filter(vif, 1, 0, NULL);
+ return;
+ }
+
+ mc_list = kmalloc_array(dev->mc.count, ETH_ALEN, GFP_ATOMIC);
+ if (!mc_list)
+ return;
+
+ cur_mc = mc_list;
+ i = 0;
+ netdev_for_each_mc_addr(ha, dev) {
+ memcpy(cur_mc, ha->addr, ETH_ALEN);
+ PRINT_INFO(vif->ndev, INIT_DBG, "Entry[%d]: %pM\n", i, cur_mc);
+ i++;
+ cur_mc += ETH_ALEN;
+ }
+
+ if (wilc_setup_multicast_filter(vif, 1, dev->mc.count, mc_list))
+ kfree(mc_list);
+}
+
+static void wilc_tx_complete(void *priv, int status)
+{
+ struct tx_complete_data *pv_data = priv;
+
+ if (status == 1)
+ PRINT_INFO(pv_data->vif->ndev, TX_DBG,
+ "Packet sentSize= %d Add= %p SKB= %p\n",
+ pv_data->size, pv_data->buff, pv_data->skb);
+ else
+ PRINT_INFO(pv_data->vif->ndev, TX_DBG,
+ "Couldn't send pkt Size= %d Add= %p SKB= %p\n",
+ pv_data->size, pv_data->buff, pv_data->skb);
+ dev_kfree_skb(pv_data->skb);
+ kfree(pv_data);
+}
+
+netdev_tx_t wilc_mac_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct wilc_vif *vif = netdev_priv(ndev);
+ struct wilc *wilc = vif->wilc;
+ struct tx_complete_data *tx_data = NULL;
+ int queue_count;
+
+ PRINT_INFO(vif->ndev, TX_DBG,
+ "Sending packet just received from TCP/IP\n");
+ if (skb->dev != ndev) {
+ PRINT_ER(ndev, "Packet not destined to this device\n");
+ return NETDEV_TX_OK;
+ }
+
+ tx_data = kmalloc(sizeof(*tx_data), GFP_ATOMIC);
+ if (!tx_data) {
+ PRINT_ER(ndev, "Failed to alloc memory for tx_data struct\n");
+ dev_kfree_skb(skb);
+ netif_wake_queue(ndev);
+ return NETDEV_TX_OK;
+ }
+
+ tx_data->buff = skb->data;
+ tx_data->size = skb->len;
+ tx_data->skb = skb;
+
+ PRINT_D(vif->ndev, TX_DBG, "Sending pkt Size= %d Add= %p SKB= %p\n",
+ tx_data->size, tx_data->buff, tx_data->skb);
+ PRINT_D(vif->ndev, TX_DBG, "Adding tx pkt to TX Queue\n");
+ vif->netstats.tx_packets++;
+ vif->netstats.tx_bytes += tx_data->size;
+ tx_data->bssid = wilc->vif[vif->idx]->bssid;
+ tx_data->vif = vif;
+ queue_count = txq_add_net_pkt(ndev, (void *)tx_data,
+ tx_data->buff, tx_data->size,
+ wilc_tx_complete);
+
+ if (queue_count > FLOW_CTRL_UP_THRESHLD) {
+ if (wilc->vif[0]->mac_opened)
+ netif_stop_queue(wilc->vif[0]->ndev);
+ if (wilc->vif[1]->mac_opened)
+ netif_stop_queue(wilc->vif[1]->ndev);
+ }
+
+ return NETDEV_TX_OK;
+}
+
+static int wilc_mac_close(struct net_device *ndev)
+{
+ struct wilc_vif *vif = netdev_priv(ndev);
+ struct wilc *wl = vif->wilc;
+
+ PRINT_INFO(vif->ndev, GENERIC_DBG, "Mac close\n");
+
+ if (wl->open_ifcs > 0) {
+ wl->open_ifcs--;
+ } else {
+ PRINT_ER(ndev, "MAC close called with no opened interfaces\n");
+ return 0;
+ }
+
+ if (vif->ndev) {
+ netif_stop_queue(vif->ndev);
+
+ if (!recovery_on)
+ wilc_deinit_host_int(vif->ndev);
+ }
+
+ if (wl->open_ifcs == 0) {
+ PRINT_INFO(ndev, GENERIC_DBG, "Deinitializing wilc\n");
+ wl->close = 1;
+ wilc_wlan_deinitialize(ndev);
+ }
+
+ vif->mac_opened = 0;
+
+ return 0;
+}
+
+void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size)
+{
+ int i = 0;
+ struct wilc_vif *vif;
+
+ for (i = 0; i < wilc->vif_num; i++) {
+ u16 type;
+
+ vif = netdev_priv(wilc->vif[i]->ndev);
+ if (vif->monitor_flag) {
+ wilc_wfi_monitor_rx(wilc->monitor_dev, buff, size);
+ return;
+ }
+ type = le16_to_cpup((__le16 *)buff);
+ if ((type == vif->frame_reg[0].type && vif->frame_reg[0].reg) ||
+ (type == vif->frame_reg[1].type && vif->frame_reg[1].reg))
+ wilc_wfi_p2p_rx(vif->ndev, buff, size);
+ }
+}
+
+static const struct net_device_ops wilc_netdev_ops = {
+ .ndo_init = mac_init_fn,
+ .ndo_open = wilc_mac_open,
+ .ndo_stop = wilc_mac_close,
+ .ndo_set_mac_address = wilc_set_mac_addr,
+ .ndo_start_xmit = wilc_mac_xmit,
+ .ndo_get_stats = mac_stats,
+ .ndo_set_rx_mode = wilc_set_multicast_list,
+};
+
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+static int dev_state_ev_handler(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct in_ifaddr *dev_iface = ptr;
+ struct wilc_priv *priv;
+ struct host_if_drv *hif_drv;
+ struct net_device *dev;
+ struct wilc_vif *vif;
+
+ if (!dev_iface || !dev_iface->ifa_dev || !dev_iface->ifa_dev->dev) {
+ pr_err("dev_iface = NULL\n");
+ return NOTIFY_DONE;
+ }
+
+ dev = (struct net_device *)dev_iface->ifa_dev->dev;
+ if (dev->netdev_ops != &wilc_netdev_ops) {
+ pr_info("interface is not ours\n");
+ return NOTIFY_DONE;
+ }
+
+ if (!dev->ieee80211_ptr || !dev->ieee80211_ptr->wiphy) {
+ pr_err("No Wireless registerd\n");
+ return NOTIFY_DONE;
+ }
+
+ priv = wiphy_priv(dev->ieee80211_ptr->wiphy);
+ if (!priv) {
+ pr_err("No Wireless Priv\n");
+ return NOTIFY_DONE;
+ }
+ vif = netdev_priv(dev);
+ hif_drv = (struct host_if_drv *)priv->hif_drv;
+ if (!vif || !hif_drv) {
+ PRINT_WRN(vif->ndev, GENERIC_DBG, "No Wireless Priv\n");
+ return NOTIFY_DONE;
+ }
+
+ switch (event) {
+ case NETDEV_UP:
+ PRINT_INFO(vif->ndev, GENERIC_DBG, "event NETDEV_UP%p\n", dev);
+ PRINT_D(vif->ndev, GENERIC_DBG,
+ "\n =========== IP Address Obtained ============\n\n");
+ if (vif->iftype == WILC_STATION_MODE ||
+ vif->iftype == WILC_CLIENT_MODE) {
+ hif_drv->ifc_up = 1;
+
+ handle_pwrsave_for_IP(vif, IP_STATE_OBTAINED);
+ }
+ break;
+
+ case NETDEV_DOWN:
+ PRINT_INFO(vif->ndev, GENERIC_DBG, "event=NETDEV_DOWN %p\n",
+ dev);
+ if (vif->iftype == WILC_STATION_MODE ||
+ vif->iftype == WILC_CLIENT_MODE) {
+ hif_drv->ifc_up = 0;
+ handle_pwrsave_for_IP(vif, IP_STATE_DEFAULT);
+ }
+ break;
+
+ default:
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "[%s] unknown dev event %lu\n",
+ dev_iface->ifa_label, event);
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+static struct notifier_block g_dev_notifier = {
+ .notifier_call = dev_state_ev_handler
+};
+#endif
+
+void wilc_netdev_cleanup(struct wilc *wilc)
+{
+ int i;
+
+ if (!wilc)
+ return;
+
+ if (wilc->firmware) {
+ release_firmware(wilc->firmware);
+ wilc->firmware = NULL;
+ }
+
+ for (i = WILC_NUM_CONCURRENT_IFC - 1 ; i >= 0; i--)
+ if (wilc->vif[i] && wilc->vif[i]->ndev) {
+ PRINT_INFO(wilc->vif[i]->ndev, INIT_DBG,
+ "Unregistering netdev %p\n",
+ wilc->vif[i]->ndev);
+ unregister_netdev(wilc->vif[i]->ndev);
+ PRINT_INFO(wilc->vif[i]->ndev, INIT_DBG,
+ "Freeing Wiphy...\n");
+ wilc_free_wiphy(wilc->vif[i]->ndev);
+ PRINT_INFO(wilc->vif[i]->ndev, INIT_DBG,
+ "Freeing netdev...\n");
+ free_netdev(wilc->vif[i]->ndev);
+ }
+
+ wilc_wfi_deinit_mon_interface(wilc);
+ #ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+ unregister_inetaddr_notifier(&g_dev_notifier);
+ #endif
+
+ flush_workqueue(wilc->hif_workqueue);
+ destroy_workqueue(wilc->hif_workqueue);
+ wilc->hif_workqueue = NULL;
+ cfg_deinit(wilc);
+#ifdef WILC_DEBUGFS
+ wilc_debugfs_remove();
+#endif
+ wilc_sysfs_exit();
+ wlan_deinit_locks(wilc);
+ kfree(wilc->bus_data);
+ kfree(wilc);
+ pr_info("Module_exit Done.\n");
+}
+
+int wilc_netdev_init(struct wilc **wilc, struct device *dev, int io_type,
+ const struct wilc_hif_func *ops)
+{
+ int i, ret;
+ struct wilc_vif *vif;
+ struct net_device *ndev;
+ struct wilc *wl;
+ struct wireless_dev *wdev;
+
+ wl = kzalloc(sizeof(*wl), GFP_KERNEL);
+ if (!wl)
+ return -ENOMEM;
+
+ *wilc = wl;
+
+ wlan_init_locks(wl);
+
+ ret = cfg_init(wl);
+ if (ret)
+ goto free_locks;
+
+#ifdef WILC_DEBUGFS
+ if (wilc_debugfs_init()) {
+ ret = -ENOMEM;
+ goto free_cfg;
+ }
+#endif
+ wl->io_type = io_type;
+ wl->hif_func = ops;
+
+ for (i = 0; i < NQUEUES; i++)
+ INIT_LIST_HEAD(&wl->txq[i].txq_head.list);
+
+ INIT_LIST_HEAD(&wl->rxq_head.list);
+
+ wl->hif_workqueue = create_singlethread_workqueue("WILC_wq");
+ if (!wl->hif_workqueue) {
+ ret = -ENOMEM;
+ goto free_debug_fs;
+ }
+
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+ register_inetaddr_notifier(&g_dev_notifier);
+#endif
+
+ for (i = 0; i < WILC_NUM_CONCURRENT_IFC; i++) {
+ ndev = alloc_etherdev(sizeof(struct wilc_vif));
+ if (!ndev) {
+ ret = -ENOMEM;
+ goto free_ndev;
+ }
+
+ vif = netdev_priv(ndev);
+
+ if (i == 0)
+ strcpy(ndev->name, "wlan%d");
+ else
+ strcpy(ndev->name, "p2p%d");
+
+ vif->idx = wl->vif_num;
+ vif->wilc = *wilc;
+ vif->ndev = ndev;
+ wl->vif[i] = vif;
+ wl->vif_num = i + 1;
+
+ ndev->netdev_ops = &wilc_netdev_ops;
+
+ wdev = wilc_create_wiphy(ndev, dev);
+ if (!wdev) {
+ PRINT_ER(ndev, "Can't register WILC Wiphy\n");
+ ret = -ENOMEM;
+ goto free_ndev;
+ }
+
+ SET_NETDEV_DEV(ndev, dev);
+
+ vif->ndev->ieee80211_ptr = wdev;
+ vif->ndev->ml_priv = vif;
+ wdev->netdev = vif->ndev;
+ vif->netstats.rx_packets = 0;
+ vif->netstats.tx_packets = 0;
+ vif->netstats.rx_bytes = 0;
+ vif->netstats.tx_bytes = 0;
+
+ ret = register_netdev(ndev);
+ if (ret) {
+ PRINT_ER(ndev, "Device couldn't be registered - %s\n",
+ ndev->name);
+ goto free_ndev;
+ }
+
+ vif->iftype = WILC_STATION_MODE;
+ vif->mac_opened = 0;
+ }
+ wilc_sysfs_init(wl->vif[0], wl->vif[1]);
+
+ return 0;
+free_ndev:
+ for (; i >= 0; i--) {
+ if (wl->vif[i]) {
+ if (wl->vif[i]->iftype == WILC_STATION_MODE)
+ unregister_netdev(wl->vif[i]->ndev);
+
+ if (wl->vif[i]->ndev) {
+ wilc_free_wiphy(wl->vif[i]->ndev);
+ free_netdev(wl->vif[i]->ndev);
+ }
+ }
+ }
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+ unregister_inetaddr_notifier(&g_dev_notifier);
+#endif
+ destroy_workqueue(wl->hif_workqueue);
+
+free_debug_fs:
+#ifdef WILC_DEBUGFS
+ wilc_debugfs_remove();
+free_cfg:
+#endif
+ cfg_deinit(wl);
+free_locks:
+ wlan_deinit_locks(wl);
+ kfree(wl);
+ return ret;
+}
+
+#if KERNEL_VERSION(3, 13, 0) < LINUX_VERSION_CODE
+static void wilc_wlan_power(struct wilc *wilc, int power)
+{
+ struct gpio_desc *gpio_reset;
+ struct gpio_desc *gpio_chip_en;
+
+ pr_info("wifi_pm : %d\n", power);
+
+ gpio_reset = gpiod_get(wilc->dt_dev, "reset", GPIOD_ASIS);
+ if (IS_ERR(gpio_reset)) {
+ dev_warn(wilc->dev, "failed to get Reset GPIO, try default\r\n");
+ gpio_reset = gpio_to_desc(GPIO_NUM_RESET);
+ if (!gpio_reset) {
+ dev_warn(wilc->dev,
+ "failed to get default Reset GPIO\r\n");
+ return;
+ }
+ } else {
+ dev_info(wilc->dev, "succesfully got gpio_reset\r\n");
+ }
+
+ gpio_chip_en = gpiod_get(wilc->dt_dev, "chip_en", GPIOD_ASIS);
+ if (IS_ERR(gpio_chip_en)) {
+ gpio_chip_en = gpio_to_desc(GPIO_NUM_CHIP_EN);
+ if (!gpio_chip_en) {
+ dev_warn(wilc->dev,
+ "failed to get default chip_en GPIO\r\n");
+ gpiod_put(gpio_reset);
+ return;
+ }
+ } else {
+ dev_info(wilc->dev, "succesfully got gpio_chip_en\r\n");
+ }
+
+ if (power) {
+ gpiod_direction_output(gpio_chip_en, 1);
+ mdelay(5);
+ gpiod_direction_output(gpio_reset, 1);
+ } else {
+ gpiod_direction_output(gpio_reset, 0);
+ gpiod_direction_output(gpio_chip_en, 0);
+ }
+ gpiod_put(gpio_chip_en);
+ gpiod_put(gpio_reset);
+}
+#else
+static void wilc_wlan_power(struct wilc *wilc, int power)
+{
+ int gpio_reset;
+ int gpio_chip_en;
+ struct device_node *of_node = wilc->dt_dev->of_node;
+
+ pr_info("wifi_pm : %d\n", power);
+
+ gpio_reset = of_get_named_gpio_flags(of_node, "reset-gpios", 0, NULL);
+
+ if (gpio_reset < 0) {
+ gpio_reset = GPIO_NUM_RESET;
+ pr_info("wifi_pm : load default reset GPIO %d\n", gpio_reset);
+ }
+
+ gpio_chip_en = of_get_named_gpio_flags(of_node, "chip_en-gpios", 0,
+ NULL);
+
+ if (gpio_chip_en < 0) {
+ gpio_chip_en = GPIO_NUM_CHIP_EN;
+ pr_info("wifi_pm : load default chip_en GPIO %d\n",
+ gpio_chip_en);
+ }
+
+ if (gpio_request(gpio_chip_en, "CHIP_EN") == 0 &&
+ gpio_request(gpio_reset, "RESET") == 0) {
+ gpio_direction_output(gpio_chip_en, 0);
+ gpio_direction_output(gpio_reset, 0);
+ if (power) {
+ gpio_set_value(gpio_chip_en, 1);
+ mdelay(5);
+ gpio_set_value(gpio_reset, 1);
+ } else {
+ gpio_set_value(gpio_reset, 0);
+ gpio_set_value(gpio_chip_en, 0);
+ }
+ gpio_free(gpio_chip_en);
+ gpio_free(gpio_reset);
+ } else {
+ dev_err(wilc->dev,
+ "Error requesting GPIOs for CHIP_EN and RESET");
+ }
+
+}
+#endif
+
+void wilc_wlan_power_on_sequence(struct wilc *wilc)
+{
+ wilc_wlan_power(wilc, 0);
+ wilc_wlan_power(wilc, 1);
+}
+
+void wilc_wlan_power_off_sequence(struct wilc *wilc)
+{
+ wilc_wlan_power(wilc, 0);
+}
+
+MODULE_LICENSE("GPL");
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#ifndef WILC_NETDEV_H
+#define WILC_NETDEV_H
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#include "wilc_wfi_netdevice.h"
+#include "wilc_wlan_if.h"
+
+#define IP_STATE_OBTAINING 1
+#define IP_STATE_OBTAINED 2
+#define IP_STATE_GO_ASSIGNING 3
+#define IP_STATE_DEFAULT 4
+
+extern int wait_for_recovery;
+
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+void handle_pwrsave_for_IP(struct wilc_vif *vif, uint8_t state);
+void store_power_save_current_state(struct wilc_vif *vif, bool val);
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+void clear_during_ip(struct timer_list *t);
+#else
+void clear_during_ip(unsigned long arg);
+#endif
+#endif //DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+struct net_device *wilc_get_if_netdev(struct wilc *wilc, uint8_t ifc);
+struct host_if_drv *get_drv_hndl_by_ifc(struct wilc *wilc, uint8_t ifc);
+
+#if KERNEL_VERSION(3, 14, 0) > LINUX_VERSION_CODE
+static inline void ether_addr_copy(u8 *dst, const u8 *src)
+{
+#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
+ *(u32 *)dst = *(const u32 *)src;
+ *(u16 *)(dst + 4) = *(const u16 *)(src + 4);
+#else
+ u16 *a = (u16 *)dst;
+ const u16 *b = (const u16 *)src;
+
+ a[0] = b[0];
+ a[1] = b[1];
+ a[2] = b[2];
+#endif
+}
+
+static inline bool ether_addr_equal_unaligned(const u8 *addr1, const u8 *addr2)
+{
+#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
+ return ether_addr_equal(addr1, addr2);
+#else
+ return memcmp(addr1, addr2, ETH_ALEN) == 0;
+#endif
+}
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0) */
+
+int wilc_bt_power_up(struct wilc *wilc, int source);
+int wilc_bt_power_down(struct wilc *wilc, int source);
+void wilc_wfi_monitor_rx(struct net_device *mon_dev, u8 *buff, u32 size);
+
+#endif /* WILC_NETDEV_H */
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+
+#include "wilc_wfi_netdevice.h"
+#include "wilc_wlan.h"
+
+enum sdio_host_lock {
+ WILC_SDIO_HOST_NO_TAKEN = 0,
+ WILC_SDIO_HOST_IRQ_TAKEN = 1,
+ WILC_SDIO_HOST_DIS_TAKEN = 2,
+};
+
+static enum sdio_host_lock sdio_intr_lock = WILC_SDIO_HOST_NO_TAKEN;
+static wait_queue_head_t sdio_intr_waitqueue;
+
+#define SDIO_MODALIAS "wilc_sdio"
+
+#define SDIO_VENDOR_ID_WILC 0x0296
+#define SDIO_DEVICE_ID_WILC 0x5347
+
+static const struct sdio_device_id wilc_sdio_ids[] = {
+ { SDIO_DEVICE(SDIO_VENDOR_ID_WILC, SDIO_DEVICE_ID_WILC) },
+ { },
+};
+
+#define WILC_SDIO_BLOCK_SIZE 512
+
+struct wilc_sdio {
+ bool irq_gpio;
+ u32 block_size;
+ int nint;
+ bool is_init;
+};
+
+struct sdio_cmd52 {
+ u32 read_write: 1;
+ u32 function: 3;
+ u32 raw: 1;
+ u32 address: 17;
+ u32 data: 8;
+};
+
+struct sdio_cmd53 {
+ u32 read_write: 1;
+ u32 function: 3;
+ u32 block_mode: 1;
+ u32 increment: 1;
+ u32 address: 17;
+ u32 count: 9;
+ u8 *buffer;
+ u32 block_size;
+};
+
+static const struct wilc_hif_func wilc_hif_sdio;
+
+static void wilc_sdio_interrupt(struct sdio_func *func)
+{
+ if (sdio_intr_lock == WILC_SDIO_HOST_DIS_TAKEN)
+ return;
+ sdio_intr_lock = WILC_SDIO_HOST_IRQ_TAKEN;
+ sdio_release_host(func);
+ wilc_handle_isr(sdio_get_drvdata(func));
+ sdio_claim_host(func);
+ sdio_intr_lock = WILC_SDIO_HOST_NO_TAKEN;
+ wake_up_interruptible(&sdio_intr_waitqueue);
+}
+
+static int wilc_sdio_cmd52(struct wilc *wilc, struct sdio_cmd52 *cmd)
+{
+ struct sdio_func *func = container_of(wilc->dev, struct sdio_func, dev);
+ int ret;
+ u8 data;
+
+ sdio_claim_host(func);
+
+ func->num = cmd->function;
+ if (cmd->read_write) { /* write */
+ if (cmd->raw) {
+ sdio_writeb(func, cmd->data, cmd->address, &ret);
+ data = sdio_readb(func, cmd->address, &ret);
+ cmd->data = data;
+ } else {
+ sdio_writeb(func, cmd->data, cmd->address, &ret);
+ }
+ } else { /* read */
+ data = sdio_readb(func, cmd->address, &ret);
+ cmd->data = data;
+ }
+
+ sdio_release_host(func);
+
+ if (ret)
+ dev_err(&func->dev, "%s..failed, err(%d)\n", __func__, ret);
+ return ret;
+}
+
+static int wilc_sdio_cmd53(struct wilc *wilc, struct sdio_cmd53 *cmd)
+{
+ struct sdio_func *func = container_of(wilc->dev, struct sdio_func, dev);
+ int size, ret;
+
+ sdio_claim_host(func);
+
+ func->num = cmd->function;
+ func->cur_blksize = cmd->block_size;
+ if (cmd->block_mode)
+ size = cmd->count * cmd->block_size;
+ else
+ size = cmd->count;
+
+ if (cmd->read_write) { /* write */
+ ret = sdio_memcpy_toio(func, cmd->address,
+ (void *)cmd->buffer, size);
+ } else { /* read */
+ ret = sdio_memcpy_fromio(func, (void *)cmd->buffer,
+ cmd->address, size);
+ }
+
+ sdio_release_host(func);
+
+ if (ret)
+ dev_err(&func->dev, "%s..failed, err(%d)\n", __func__, ret);
+
+ return ret;
+}
+
+static int wilc_sdio_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ struct wilc *wilc;
+ int ret, io_type;
+ static bool init_power;
+ struct wilc_sdio *sdio_priv;
+
+ sdio_priv = kzalloc(sizeof(*sdio_priv), GFP_KERNEL);
+ if (!sdio_priv)
+ return -ENOMEM;
+
+ if (IS_ENABLED(CONFIG_WILC_HW_OOB_INTR))
+ io_type = WILC_HIF_SDIO_GPIO_IRQ;
+ else
+ io_type = WILC_HIF_SDIO;
+ dev_dbg(&func->dev, "Initializing netdev\n");
+ ret = wilc_netdev_init(&wilc, &func->dev, io_type, &wilc_hif_sdio);
+ if (ret) {
+ dev_err(&func->dev, "Couldn't initialize netdev\n");
+ kfree(sdio_priv);
+ return ret;
+ }
+ sdio_set_drvdata(func, wilc);
+ wilc->bus_data = sdio_priv;
+ wilc->dev = &func->dev;
+ wilc->dt_dev = &func->card->dev;
+
+ if (!init_power) {
+ wilc_wlan_power_on_sequence(wilc);
+ init_power = 1;
+ }
+
+ wilc_bt_init(wilc);
+
+ dev_info(&func->dev, "Driver Initializing success\n");
+ return 0;
+}
+
+static void wilc_sdio_remove(struct sdio_func *func)
+{
+ struct wilc *wilc = sdio_get_drvdata(func);
+
+ wilc_netdev_cleanup(wilc);
+ wilc_bt_deinit();
+}
+
+static int wilc_sdio_reset(struct wilc *wilc)
+{
+ struct sdio_cmd52 cmd;
+ int ret;
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+
+ dev_info(&func->dev, "De Init SDIO\n");
+
+ cmd.read_write = 1;
+ cmd.function = 0;
+ cmd.raw = 0;
+ cmd.address = 0x6;
+ cmd.data = 0x8;
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret)
+ dev_err(&func->dev, "Fail cmd 52, reset cmd\n");
+ return ret;
+}
+
+static bool wilc_sdio_is_init(struct wilc *wilc)
+{
+ struct wilc_sdio *sdio_priv = wilc->bus_data;
+
+ return sdio_priv->is_init;
+}
+
+static int wilc_sdio_suspend(struct device *dev)
+{
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ struct wilc *wilc = sdio_get_drvdata(func);
+ int ret;
+
+ dev_info(&func->dev, "sdio suspend\n");
+ mutex_lock(&wilc->hif_cs);
+
+ chip_wakeup(wilc, 0);
+
+ if (mutex_is_locked(&wilc->hif_cs))
+ mutex_unlock(&wilc->hif_cs);
+
+ host_sleep_notify(wilc, 0);
+ chip_allow_sleep(wilc, 0);
+
+ mutex_lock(&wilc->hif_cs);
+
+ ret = wilc_sdio_reset(wilc);
+
+ return 0;
+}
+
+static int wilc_sdio_enable_interrupt(struct wilc *dev)
+{
+ struct sdio_func *func = container_of(dev->dev, struct sdio_func, dev);
+ int ret = 0;
+
+ sdio_intr_lock = WILC_SDIO_HOST_NO_TAKEN;
+
+ sdio_claim_host(func);
+ ret = sdio_claim_irq(func, wilc_sdio_interrupt);
+ sdio_release_host(func);
+
+ if (ret < 0) {
+ dev_err(&func->dev, "can't claim sdio_irq, err(%d)\n", ret);
+ ret = -EIO;
+ }
+ return ret;
+}
+
+static void wilc_sdio_disable_interrupt(struct wilc *dev)
+{
+ struct sdio_func *func = container_of(dev->dev, struct sdio_func, dev);
+ int ret;
+
+ dev_info(&func->dev, "%s\n", __func__);
+
+ if (sdio_intr_lock == WILC_SDIO_HOST_IRQ_TAKEN)
+ wait_event_interruptible(sdio_intr_waitqueue,
+ sdio_intr_lock == WILC_SDIO_HOST_NO_TAKEN);
+ sdio_intr_lock = WILC_SDIO_HOST_DIS_TAKEN;
+
+ sdio_claim_host(func);
+ ret = sdio_release_irq(func);
+ if (ret < 0)
+ dev_err(&func->dev, "can't release sdio_irq, err(%d)\n", ret);
+ sdio_release_host(func);
+ sdio_intr_lock = WILC_SDIO_HOST_NO_TAKEN;
+}
+
+/********************************************
+ *
+ * Function 0
+ *
+ ********************************************/
+
+static int wilc_sdio_set_func0_csa_address(struct wilc *wilc, u32 adr)
+{
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+ struct sdio_cmd52 cmd;
+ int ret;
+
+ /**
+ * Review: BIG ENDIAN
+ **/
+ cmd.read_write = 1;
+ cmd.function = 0;
+ cmd.raw = 0;
+ cmd.address = 0x10c;
+ cmd.data = (u8)adr;
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Failed cmd52, set 0x10c data...\n");
+ goto fail;
+ }
+
+ cmd.address = 0x10d;
+ cmd.data = (u8)(adr >> 8);
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Failed cmd52, set 0x10d data...\n");
+ goto fail;
+ }
+
+ cmd.address = 0x10e;
+ cmd.data = (u8)(adr >> 16);
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Failed cmd52, set 0x10e data...\n");
+ goto fail;
+ }
+
+ return 1;
+fail:
+ return 0;
+}
+
+static int wilc_sdio_set_func0_block_size(struct wilc *wilc, u32 block_size)
+{
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+ struct sdio_cmd52 cmd;
+ int ret;
+
+ cmd.read_write = 1;
+ cmd.function = 0;
+ cmd.raw = 0;
+ cmd.address = 0x10;
+ cmd.data = (u8)block_size;
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Failed cmd52, set 0x10 data...\n");
+ goto fail;
+ }
+
+ cmd.address = 0x11;
+ cmd.data = (u8)(block_size >> 8);
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Failed cmd52, set 0x11 data...\n");
+ goto fail;
+ }
+
+ return 1;
+fail:
+ return 0;
+}
+
+/********************************************
+ *
+ * Function 1
+ *
+ ********************************************/
+
+static int wilc_sdio_set_func1_block_size(struct wilc *wilc, u32 block_size)
+{
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+ struct sdio_cmd52 cmd;
+ int ret;
+
+ cmd.read_write = 1;
+ cmd.function = 0;
+ cmd.raw = 0;
+ cmd.address = 0x110;
+ cmd.data = (u8)block_size;
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Failed cmd52, set 0x110 data...\n");
+ goto fail;
+ }
+ cmd.address = 0x111;
+ cmd.data = (u8)(block_size >> 8);
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Failed cmd52, set 0x111 data...\n");
+ goto fail;
+ }
+
+ return 1;
+fail:
+ return 0;
+}
+
+/********************************************
+ *
+ * Sdio interfaces
+ *
+ ********************************************/
+static int wilc_sdio_write_reg(struct wilc *wilc, u32 addr, u32 data)
+{
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+ struct wilc_sdio *sdio_priv = wilc->bus_data;
+ int ret;
+
+ cpu_to_le32s(&data);
+
+ if (addr >= 0xf0 && addr <= 0xff) {
+ struct sdio_cmd52 cmd;
+
+ cmd.read_write = 1;
+ cmd.function = 0;
+ cmd.raw = 0;
+ cmd.address = addr;
+ cmd.data = data;
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd 52, write reg %08x ...\n", addr);
+ goto fail;
+ }
+ } else {
+ struct sdio_cmd53 cmd;
+
+ /**
+ * set the AHB address
+ **/
+ if (!wilc_sdio_set_func0_csa_address(wilc, addr))
+ goto fail;
+
+ cmd.read_write = 1;
+ cmd.function = 0;
+ cmd.address = 0x10f;
+ cmd.block_mode = 0;
+ cmd.increment = 1;
+ cmd.count = 4;
+ cmd.buffer = (u8 *)&data;
+ cmd.block_size = sdio_priv->block_size;
+ ret = wilc_sdio_cmd53(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd53, write reg (%08x)...\n", addr);
+ goto fail;
+ }
+ }
+
+ return 1;
+
+fail:
+
+ return 0;
+}
+
+static int wilc_sdio_write(struct wilc *wilc, u32 addr, u8 *buf, u32 size)
+{
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+ struct wilc_sdio *sdio_priv = wilc->bus_data;
+ u32 block_size = sdio_priv->block_size;
+ struct sdio_cmd53 cmd;
+ int nblk, nleft, ret;
+
+ cmd.read_write = 1;
+ if (addr > 0) {
+ /**
+ * has to be word aligned...
+ **/
+ if (size & 0x3) {
+ size += 4;
+ size &= ~0x3;
+ }
+
+ /**
+ * func 0 access
+ **/
+ cmd.function = 0;
+ cmd.address = 0x10f;
+ } else {
+ /**
+ * has to be word aligned...
+ **/
+ if (size & 0x3) {
+ size += 4;
+ size &= ~0x3;
+ }
+
+ /**
+ * func 1 access
+ **/
+ cmd.function = 1;
+ cmd.address = 0;
+ }
+
+ nblk = size / block_size;
+ nleft = size % block_size;
+
+ if (nblk > 0) {
+ cmd.block_mode = 1;
+ cmd.increment = 1;
+ cmd.count = nblk;
+ cmd.buffer = buf;
+ cmd.block_size = block_size;
+ if (addr > 0) {
+ if (!wilc_sdio_set_func0_csa_address(wilc, addr))
+ goto fail;
+ }
+ ret = wilc_sdio_cmd53(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd53 [%x], block send...\n", addr);
+ goto fail;
+ }
+ if (addr > 0)
+ addr += nblk * block_size;
+ buf += nblk * block_size;
+ }
+
+ if (nleft > 0) {
+ cmd.block_mode = 0;
+ cmd.increment = 1;
+ cmd.count = nleft;
+ cmd.buffer = buf;
+
+ cmd.block_size = block_size;
+
+ if (addr > 0) {
+ if (!wilc_sdio_set_func0_csa_address(wilc, addr))
+ goto fail;
+ }
+ ret = wilc_sdio_cmd53(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd53 [%x], bytes send...\n", addr);
+ goto fail;
+ }
+ }
+
+ return 1;
+
+fail:
+
+ return 0;
+}
+
+static int wilc_sdio_read_reg(struct wilc *wilc, u32 addr, u32 *data)
+{
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+ struct wilc_sdio *sdio_priv = wilc->bus_data;
+ int ret;
+
+ if (addr >= 0xf0 && addr <= 0xff) {
+ struct sdio_cmd52 cmd;
+
+ cmd.read_write = 0;
+ cmd.function = 0;
+ cmd.raw = 0;
+ cmd.address = addr;
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd 52, read reg (%08x) ...\n", addr);
+ goto fail;
+ }
+ *data = cmd.data;
+ } else {
+ struct sdio_cmd53 cmd;
+
+ if (!wilc_sdio_set_func0_csa_address(wilc, addr))
+ goto fail;
+
+ cmd.read_write = 0;
+ cmd.function = 0;
+ cmd.address = 0x10f;
+ cmd.block_mode = 0;
+ cmd.increment = 1;
+ cmd.count = 4;
+ cmd.buffer = (u8 *)data;
+
+ cmd.block_size = sdio_priv->block_size;
+ ret = wilc_sdio_cmd53(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd53, read reg (%08x)...\n", addr);
+ goto fail;
+ }
+ }
+
+ le32_to_cpus(data);
+
+ return 1;
+
+fail:
+
+ return 0;
+}
+
+static int wilc_sdio_read(struct wilc *wilc, u32 addr, u8 *buf, u32 size)
+{
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+ struct wilc_sdio *sdio_priv = wilc->bus_data;
+ u32 block_size = sdio_priv->block_size;
+ struct sdio_cmd53 cmd;
+ int nblk, nleft, ret;
+
+ cmd.read_write = 0;
+ if (addr > 0) {
+ /**
+ * has to be word aligned...
+ **/
+ if (size & 0x3) {
+ size += 4;
+ size &= ~0x3;
+ }
+
+ /**
+ * func 0 access
+ **/
+ cmd.function = 0;
+ cmd.address = 0x10f;
+ } else {
+ /**
+ * has to be word aligned...
+ **/
+ if (size & 0x3) {
+ size += 4;
+ size &= ~0x3;
+ }
+
+ /**
+ * func 1 access
+ **/
+ cmd.function = 1;
+ cmd.address = 0;
+ }
+
+ nblk = size / block_size;
+ nleft = size % block_size;
+
+ if (nblk > 0) {
+ cmd.block_mode = 1;
+ cmd.increment = 1;
+ cmd.count = nblk;
+ cmd.buffer = buf;
+ cmd.block_size = block_size;
+ if (addr > 0) {
+ if (!wilc_sdio_set_func0_csa_address(wilc, addr))
+ goto fail;
+ }
+ ret = wilc_sdio_cmd53(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd53 [%x], block read...\n", addr);
+ goto fail;
+ }
+ if (addr > 0)
+ addr += nblk * block_size;
+ buf += nblk * block_size;
+ } /* if (nblk > 0) */
+
+ if (nleft > 0) {
+ cmd.block_mode = 0;
+ cmd.increment = 1;
+ cmd.count = nleft;
+ cmd.buffer = buf;
+
+ cmd.block_size = block_size;
+
+ if (addr > 0) {
+ if (!wilc_sdio_set_func0_csa_address(wilc, addr))
+ goto fail;
+ }
+ ret = wilc_sdio_cmd53(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd53 [%x], bytes read...\n", addr);
+ goto fail;
+ }
+ }
+
+ return 1;
+
+fail:
+
+ return 0;
+}
+
+/********************************************
+ *
+ * Bus interfaces
+ *
+ ********************************************/
+
+static int wilc_sdio_deinit(struct wilc *wilc)
+{
+ struct wilc_sdio *sdio_priv = wilc->bus_data;
+
+ sdio_priv->is_init = false;
+
+ return 1;
+}
+
+static int wilc_sdio_init(struct wilc *wilc, bool resume)
+{
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+ struct wilc_sdio *sdio_priv = wilc->bus_data;
+ struct sdio_cmd52 cmd;
+ int loop, ret;
+ u32 chipid;
+
+ dev_info(&func->dev, "SDIO speed: %d\n",
+ func->card->host->ios.clock);
+
+ /* Patch for sdio interrupt latency issue */
+ pm_runtime_get_sync(mmc_dev(func->card->host));
+
+ init_waitqueue_head(&sdio_intr_waitqueue);
+ sdio_priv->irq_gpio = (wilc->io_type == WILC_HIF_SDIO_GPIO_IRQ);
+
+ /**
+ * function 0 csa enable
+ **/
+ cmd.read_write = 1;
+ cmd.function = 0;
+ cmd.raw = 1;
+ cmd.address = 0x100;
+ cmd.data = 0x80;
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Fail cmd 52, enable csa...\n");
+ goto fail;
+ }
+
+ /**
+ * function 0 block size
+ **/
+ if (!wilc_sdio_set_func0_block_size(wilc, WILC_SDIO_BLOCK_SIZE)) {
+ dev_err(&func->dev, "Fail cmd 52, set func 0 block size...\n");
+ goto fail;
+ }
+ sdio_priv->block_size = WILC_SDIO_BLOCK_SIZE;
+
+ /**
+ * enable func1 IO
+ **/
+ cmd.read_write = 1;
+ cmd.function = 0;
+ cmd.raw = 1;
+ cmd.address = 0x2;
+ cmd.data = 0x2;
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Fail cmd 52, set IOE register...\n");
+ goto fail;
+ }
+
+ /**
+ * make sure func 1 is up
+ **/
+ cmd.read_write = 0;
+ cmd.function = 0;
+ cmd.raw = 0;
+ cmd.address = 0x3;
+ loop = 3;
+ do {
+ cmd.data = 0;
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Fail cmd 52, get IOR register...\n");
+ goto fail;
+ }
+ if (cmd.data == 0x2)
+ break;
+ } while (loop--);
+
+ if (loop <= 0) {
+ dev_err(&func->dev, "Fail func 1 is not ready...\n");
+ goto fail;
+ }
+
+ /**
+ * func 1 is ready, set func 1 block size
+ **/
+ if (!wilc_sdio_set_func1_block_size(wilc, WILC_SDIO_BLOCK_SIZE)) {
+ dev_err(&func->dev, "Fail set func 1 block size...\n");
+ goto fail;
+ }
+
+ /**
+ * func 1 interrupt enable
+ **/
+ cmd.read_write = 1;
+ cmd.function = 0;
+ cmd.raw = 1;
+ cmd.address = 0x4;
+ cmd.data = 0x3;
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Fail cmd 52, set IEN register...\n");
+ goto fail;
+ }
+
+ /**
+ * make sure can read back chip id correctly
+ **/
+ if (!resume) {
+ chipid = wilc_get_chipid(wilc, true);
+ if (is_wilc3000(chipid)) {
+ wilc->chip = WILC_3000;
+ } else if (is_wilc1000(chipid)) {
+ wilc->chip = WILC_1000;
+ } else {
+ dev_err(&func->dev, "Unsupported chipid: %x\n", chipid);
+ goto fail;
+ }
+ dev_info(&func->dev, "chipid %08x\n", chipid);
+ }
+
+ sdio_priv->is_init = true;
+
+ return 1;
+
+fail:
+
+ return 0;
+}
+
+static int wilc_sdio_read_size(struct wilc *wilc, u32 *size)
+{
+ u32 tmp;
+ struct sdio_cmd52 cmd;
+
+ /**
+ * Read DMA count in words
+ **/
+ cmd.read_write = 0;
+ cmd.function = 0;
+ cmd.raw = 0;
+ cmd.address = 0xf2;
+ cmd.data = 0;
+ wilc_sdio_cmd52(wilc, &cmd);
+ tmp = cmd.data;
+
+ cmd.address = 0xf3;
+ cmd.data = 0;
+ wilc_sdio_cmd52(wilc, &cmd);
+ tmp |= (cmd.data << 8);
+
+ *size = tmp;
+ return 1;
+}
+
+static int wilc_sdio_read_int(struct wilc *wilc, u32 *int_status)
+{
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+ struct wilc_sdio *sdio_priv = wilc->bus_data;
+ u32 tmp;
+ struct sdio_cmd52 cmd;
+ u32 irq_flags;
+ int i;
+
+ if (sdio_priv->irq_gpio) {
+ wilc_sdio_read_size(wilc, &tmp);
+
+ cmd.read_write = 0;
+ cmd.function = 1;
+ cmd.raw = 0;
+ cmd.data = 0;
+ if (wilc->chip == WILC_1000) {
+ cmd.address = 0xf7;
+ wilc_sdio_cmd52(wilc, &cmd);
+ irq_flags = cmd.data & 0x1f;
+ } else {
+ cmd.address = 0xfe;
+ wilc_sdio_cmd52(wilc, &cmd);
+ irq_flags = cmd.data & 0x0f;
+ }
+ tmp |= ((irq_flags >> 0) << IRG_FLAGS_OFFSET);
+
+ *int_status = tmp;
+ } else {
+ wilc_sdio_read_size(wilc, &tmp);
+ cmd.read_write = 0;
+ cmd.function = 1;
+ cmd.address = 0x04;
+ cmd.data = 0;
+ wilc_sdio_cmd52(wilc, &cmd);
+
+ if (cmd.data & BIT(0))
+ tmp |= INT_0;
+ if (cmd.data & BIT(2))
+ tmp |= INT_1;
+ if (cmd.data & BIT(3))
+ tmp |= INT_2;
+ if (cmd.data & BIT(4))
+ tmp |= INT_3;
+ if (cmd.data & BIT(5))
+ tmp |= INT_4;
+
+ for (i = sdio_priv->nint; i < MAX_NUM_INT; i++) {
+ if ((tmp >> (IRG_FLAGS_OFFSET + i)) & 0x1) {
+ dev_err(&func->dev,
+ "Unexpected interrupt (1) : tmp=%x, data=%x\n",
+ tmp, cmd.data);
+ break;
+ }
+ }
+
+ *int_status = tmp;
+
+ }
+
+ return 1;
+}
+
+static int wilc_sdio_clear_int_ext(struct wilc *wilc, u32 val)
+{
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+ struct wilc_sdio *sdio_priv = wilc->bus_data;
+ int ret;
+ u32 reg = 0;
+
+ if (wilc->chip == WILC_1000) {
+ if (sdio_priv->irq_gpio)
+ reg = val & (BIT(MAX_NUM_INT) - 1);
+
+ /* select VMM table 0 */
+ if (val & SEL_VMM_TBL0)
+ reg |= BIT(5);
+ /* select VMM table 1 */
+ if (val & SEL_VMM_TBL1)
+ reg |= BIT(6);
+ /* enable VMM */
+ if (val & EN_VMM)
+ reg |= BIT(7);
+ if (reg) {
+ struct sdio_cmd52 cmd;
+
+ cmd.read_write = 1;
+ cmd.function = 0;
+ cmd.raw = 0;
+ cmd.address = 0xf8;
+ cmd.data = reg;
+
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd52, set 0xf8 data (%d) ...\n",
+ __LINE__);
+ goto fail;
+ }
+ }
+ } else {
+ if (sdio_priv->irq_gpio) {
+ reg = val & (BIT(MAX_NUM_INT) - 1);
+ if (reg) {
+ struct sdio_cmd52 cmd;
+
+ cmd.read_write = 1;
+ cmd.function = 0;
+ cmd.raw = 0;
+ cmd.address = 0xfe;
+ cmd.data = reg;
+
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd52, set 0xf8 data (%d) ...\n",
+ __LINE__);
+ goto fail;
+ }
+ }
+ }
+ /* select VMM table 0 */
+ if (val & SEL_VMM_TBL0)
+ reg |= BIT(0);
+ /* select VMM table 1 */
+ if (val & SEL_VMM_TBL1)
+ reg |= BIT(1);
+ /* enable VMM */
+ if (val & EN_VMM)
+ reg |= BIT(2);
+
+ if (reg) {
+ struct sdio_cmd52 cmd;
+
+ cmd.read_write = 1;
+ cmd.function = 0;
+ cmd.raw = 0;
+ cmd.address = 0xf1;
+ cmd.data = reg;
+
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd52, set 0xf6 data (%d) ...\n",
+ __LINE__);
+ goto fail;
+ }
+ }
+ }
+
+ return 1;
+fail:
+ return 0;
+}
+
+static int wilc_sdio_sync_ext(struct wilc *wilc, int nint)
+{
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+ struct wilc_sdio *sdio_priv = wilc->bus_data;
+ u32 reg;
+ int ret, i;
+
+ if (nint > MAX_NUM_INT) {
+ dev_err(&func->dev, "Too many interrupts %d\n", nint);
+ return 0;
+ }
+
+ sdio_priv->nint = nint;
+
+/* WILC3000 only. Was removed in WILC1000 on revision 6200.
+ * Might be related to suspend/resume
+ */
+ if (wilc->chip == WILC_3000) {
+ /**
+ * Disable power sequencer
+ **/
+ if (!wilc_sdio_read_reg(wilc, WILC_MISC, ®)) {
+ dev_err(&func->dev, "Failed read misc reg\n");
+ return 0;
+ }
+ reg &= ~BIT(8);
+ if (!wilc_sdio_write_reg(wilc, WILC_MISC, reg)) {
+ dev_err(&func->dev, "Failed write misc reg\n");
+ return 0;
+ }
+ }
+
+ if (sdio_priv->irq_gpio) {
+ /**
+ * interrupt pin mux select
+ **/
+ ret = wilc_sdio_read_reg(wilc, WILC_PIN_MUX_0, ®);
+ if (!ret) {
+ dev_err(&func->dev, "Failed read reg (%08x)...\n",
+ WILC_PIN_MUX_0);
+ return 0;
+ }
+ reg |= BIT(8);
+ ret = wilc_sdio_write_reg(wilc, WILC_PIN_MUX_0, reg);
+ if (!ret) {
+ dev_err(&func->dev, "Failed write reg (%08x)...\n",
+ WILC_PIN_MUX_0);
+ return 0;
+ }
+
+ /**
+ * interrupt enable
+ **/
+ ret = wilc_sdio_read_reg(wilc, WILC_INTR_ENABLE, ®);
+ if (!ret) {
+ dev_err(&func->dev, "Failed read reg (%08x)...\n",
+ WILC_INTR_ENABLE);
+ return 0;
+ }
+
+ for (i = 0; (i < 5) && (nint > 0); i++, nint--)
+ reg |= BIT((27 + i));
+ ret = wilc_sdio_write_reg(wilc, WILC_INTR_ENABLE, reg);
+ if (!ret) {
+ dev_err(&func->dev, "Failed write reg (%08x)...\n",
+ WILC_INTR_ENABLE);
+ return 0;
+ }
+ if (nint) {
+ ret = wilc_sdio_read_reg(wilc, WILC_INTR2_ENABLE, ®);
+ if (!ret) {
+ dev_err(&func->dev,
+ "Failed read reg (%08x)...\n",
+ WILC_INTR2_ENABLE);
+ return 0;
+ }
+
+ for (i = 0; (i < 3) && (nint > 0); i++, nint--)
+ reg |= BIT(i);
+
+ ret = wilc_sdio_write_reg(wilc, WILC_INTR2_ENABLE, reg);
+ if (!ret) {
+ dev_err(&func->dev,
+ "Failed write reg (%08x)...\n",
+ WILC_INTR2_ENABLE);
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+/* Global sdio HIF function table */
+static const struct wilc_hif_func wilc_hif_sdio = {
+ .hif_init = wilc_sdio_init,
+ .hif_deinit = wilc_sdio_deinit,
+ .hif_read_reg = wilc_sdio_read_reg,
+ .hif_write_reg = wilc_sdio_write_reg,
+ .hif_block_rx = wilc_sdio_read,
+ .hif_block_tx = wilc_sdio_write,
+ .hif_read_int = wilc_sdio_read_int,
+ .hif_clear_int_ext = wilc_sdio_clear_int_ext,
+ .hif_read_size = wilc_sdio_read_size,
+ .hif_block_tx_ext = wilc_sdio_write,
+ .hif_block_rx_ext = wilc_sdio_read,
+ .hif_sync_ext = wilc_sdio_sync_ext,
+ .enable_interrupt = wilc_sdio_enable_interrupt,
+ .disable_interrupt = wilc_sdio_disable_interrupt,
+ .hif_reset = wilc_sdio_reset,
+ .hif_is_init = wilc_sdio_is_init,
+};
+
+static int wilc_sdio_resume(struct device *dev)
+{
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ struct wilc *wilc = sdio_get_drvdata(func);
+
+ dev_info(&func->dev, "sdio resume\n");
+ chip_wakeup(wilc, 0);
+ wilc_sdio_init(wilc, true);
+
+ if (mutex_is_locked(&wilc->hif_cs))
+ mutex_unlock(&wilc->hif_cs);
+
+ host_wakeup_notify(wilc, 0);
+
+ mutex_lock(&wilc->hif_cs);
+
+ chip_allow_sleep(wilc, 0);
+
+ if (mutex_is_locked(&wilc->hif_cs))
+ mutex_unlock(&wilc->hif_cs);
+
+ return 0;
+}
+
+static const struct of_device_id wilc_of_match[] = {
+ { .compatible = "microchip,wilc1000", },
+ { .compatible = "microchip,wilc3000", },
+ { /* sentinel */}
+};
+MODULE_DEVICE_TABLE(of, wilc_of_match);
+
+static const struct dev_pm_ops wilc_sdio_pm_ops = {
+ .suspend = wilc_sdio_suspend,
+ .resume = wilc_sdio_resume,
+};
+
+static struct sdio_driver wilc_sdio_driver = {
+ .name = SDIO_MODALIAS,
+ .id_table = wilc_sdio_ids,
+ .probe = wilc_sdio_probe,
+ .remove = wilc_sdio_remove,
+ .drv = {
+ .pm = &wilc_sdio_pm_ops,
+ .of_match_table = wilc_of_match,
+ }
+};
+module_driver(wilc_sdio_driver,
+ sdio_register_driver,
+ sdio_unregister_driver);
+MODULE_LICENSE("GPL");
+
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+
+#include "wilc_wfi_netdevice.h"
+
+struct wilc_spi {
+ int crc_off;
+ int nint;
+ bool is_init;
+};
+
+static const struct wilc_hif_func wilc_hif_spi;
+
+static int wilc_spi_rx(struct wilc *wilc, u8 *rb, u32 rlen);
+static int wilc_spi_reset(struct wilc *wilc);
+
+/********************************************
+ *
+ * Crc7
+ *
+ ********************************************/
+
+static const u8 crc7_syndrome_table[256] = {
+ 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f,
+ 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77,
+ 0x19, 0x10, 0x0b, 0x02, 0x3d, 0x34, 0x2f, 0x26,
+ 0x51, 0x58, 0x43, 0x4a, 0x75, 0x7c, 0x67, 0x6e,
+ 0x32, 0x3b, 0x20, 0x29, 0x16, 0x1f, 0x04, 0x0d,
+ 0x7a, 0x73, 0x68, 0x61, 0x5e, 0x57, 0x4c, 0x45,
+ 0x2b, 0x22, 0x39, 0x30, 0x0f, 0x06, 0x1d, 0x14,
+ 0x63, 0x6a, 0x71, 0x78, 0x47, 0x4e, 0x55, 0x5c,
+ 0x64, 0x6d, 0x76, 0x7f, 0x40, 0x49, 0x52, 0x5b,
+ 0x2c, 0x25, 0x3e, 0x37, 0x08, 0x01, 0x1a, 0x13,
+ 0x7d, 0x74, 0x6f, 0x66, 0x59, 0x50, 0x4b, 0x42,
+ 0x35, 0x3c, 0x27, 0x2e, 0x11, 0x18, 0x03, 0x0a,
+ 0x56, 0x5f, 0x44, 0x4d, 0x72, 0x7b, 0x60, 0x69,
+ 0x1e, 0x17, 0x0c, 0x05, 0x3a, 0x33, 0x28, 0x21,
+ 0x4f, 0x46, 0x5d, 0x54, 0x6b, 0x62, 0x79, 0x70,
+ 0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38,
+ 0x41, 0x48, 0x53, 0x5a, 0x65, 0x6c, 0x77, 0x7e,
+ 0x09, 0x00, 0x1b, 0x12, 0x2d, 0x24, 0x3f, 0x36,
+ 0x58, 0x51, 0x4a, 0x43, 0x7c, 0x75, 0x6e, 0x67,
+ 0x10, 0x19, 0x02, 0x0b, 0x34, 0x3d, 0x26, 0x2f,
+ 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
+ 0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04,
+ 0x6a, 0x63, 0x78, 0x71, 0x4e, 0x47, 0x5c, 0x55,
+ 0x22, 0x2b, 0x30, 0x39, 0x06, 0x0f, 0x14, 0x1d,
+ 0x25, 0x2c, 0x37, 0x3e, 0x01, 0x08, 0x13, 0x1a,
+ 0x6d, 0x64, 0x7f, 0x76, 0x49, 0x40, 0x5b, 0x52,
+ 0x3c, 0x35, 0x2e, 0x27, 0x18, 0x11, 0x0a, 0x03,
+ 0x74, 0x7d, 0x66, 0x6f, 0x50, 0x59, 0x42, 0x4b,
+ 0x17, 0x1e, 0x05, 0x0c, 0x33, 0x3a, 0x21, 0x28,
+ 0x5f, 0x56, 0x4d, 0x44, 0x7b, 0x72, 0x69, 0x60,
+ 0x0e, 0x07, 0x1c, 0x15, 0x2a, 0x23, 0x38, 0x31,
+ 0x46, 0x4f, 0x54, 0x5d, 0x62, 0x6b, 0x70, 0x79
+};
+
+static u8 crc7_byte(u8 crc, u8 data)
+{
+ return crc7_syndrome_table[(crc << 1) ^ data];
+}
+
+static u8 crc7(u8 crc, const u8 *buffer, u32 len)
+{
+ while (len--)
+ crc = crc7_byte(crc, *buffer++);
+ return crc;
+}
+
+/********************************************
+ *
+ * Spi protocol Function
+ *
+ ********************************************/
+
+#define CMD_DMA_WRITE 0xc1
+#define CMD_DMA_READ 0xc2
+#define CMD_INTERNAL_WRITE 0xc3
+#define CMD_INTERNAL_READ 0xc4
+#define CMD_TERMINATE 0xc5
+#define CMD_REPEAT 0xc6
+#define CMD_DMA_EXT_WRITE 0xc7
+#define CMD_DMA_EXT_READ 0xc8
+#define CMD_SINGLE_WRITE 0xc9
+#define CMD_SINGLE_READ 0xca
+#define CMD_RESET 0xcf
+
+#define N_OK 1
+#define N_FAIL 0
+#define N_RESET -1
+#define N_RETRY -2
+
+#define SPI_RESP_RETRY_COUNT (10)
+#define SPI_RETRY_COUNT (10)
+#define DATA_PKT_SZ_256 256
+#define DATA_PKT_SZ_512 512
+#define DATA_PKT_SZ_1K 1024
+#define DATA_PKT_SZ_2K (2 * 1024)
+#define DATA_PKT_SZ_4K (4 * 1024)
+#define DATA_PKT_SZ_8K (8 * 1024)
+#define DATA_PKT_SZ DATA_PKT_SZ_8K
+
+#define USE_SPI_DMA 0
+
+static int wilc_bus_probe(struct spi_device *spi)
+{
+ int ret;
+ static bool init_power;
+ struct wilc *wilc;
+ struct device *dev = &spi->dev;
+ struct wilc_spi *spi_priv;
+
+ dev_info(&spi->dev, "spiModalias: %s, spiMax-Speed: %d\n",
+ spi->modalias, spi->max_speed_hz);
+
+ spi_priv = kzalloc(sizeof(*spi_priv), GFP_KERNEL);
+ if (!spi_priv)
+ return -ENOMEM;
+
+ ret = wilc_netdev_init(&wilc, dev, WILC_HIF_SPI, &wilc_hif_spi);
+ if (ret) {
+ kfree(spi_priv);
+ return ret;
+ }
+
+ spi_set_drvdata(spi, wilc);
+ wilc->dev = &spi->dev;
+ wilc->bus_data = spi_priv;
+ wilc->dt_dev = &spi->dev;
+
+
+ if (!init_power) {
+ wilc_wlan_power_on_sequence(wilc);
+ init_power = 1;
+ }
+
+ wilc_bt_init(wilc);
+
+ dev_info(dev, "WILC SPI probe success\n");
+ return 0;
+}
+
+static int wilc_bus_remove(struct spi_device *spi)
+{
+ struct wilc *wilc = spi_get_drvdata(spi);
+
+ wilc_netdev_cleanup(wilc);
+ wilc_bt_deinit();
+ return 0;
+}
+
+static int wilc_spi_suspend(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct wilc *wilc = spi_get_drvdata(spi);
+
+ dev_info(&spi->dev, "\n\n << SUSPEND >>\n\n");
+ mutex_lock(&wilc->hif_cs);
+ chip_wakeup(wilc, 0);
+
+ if (mutex_is_locked(&wilc->hif_cs))
+ mutex_unlock(&wilc->hif_cs);
+
+ /*notify the chip that host will sleep*/
+ host_sleep_notify(wilc, 0);
+ chip_allow_sleep(wilc, 0);
+ mutex_lock(&wilc->hif_cs);
+
+ return 0;
+}
+
+static int wilc_spi_resume(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct wilc *wilc = spi_get_drvdata(spi);
+
+ dev_info(&spi->dev, "\n\n <<RESUME>>\n\n");
+
+ /*wake the chip to compelete the re-intialization*/
+ chip_wakeup(wilc, 0);
+
+ if (mutex_is_locked(&wilc->hif_cs))
+ mutex_unlock(&wilc->hif_cs);
+
+ host_wakeup_notify(wilc, 0);
+
+ mutex_lock(&wilc->hif_cs);
+
+ chip_allow_sleep(wilc, 0);
+
+ if (mutex_is_locked(&wilc->hif_cs))
+ mutex_unlock(&wilc->hif_cs);
+
+ return 0;
+}
+
+static const struct of_device_id wilc_of_match[] = {
+ { .compatible = "microchip,wilc1000", },
+ { .compatible = "microchip,wilc3000", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, wilc_of_match);
+static const struct dev_pm_ops wilc_spi_pm_ops = {
+ .suspend = wilc_spi_suspend,
+ .resume = wilc_spi_resume,
+};
+
+static struct spi_driver wilc_spi_driver = {
+ .driver = {
+ .name = MODALIAS,
+ .of_match_table = wilc_of_match,
+ .pm = &wilc_spi_pm_ops,
+ },
+ .probe = wilc_bus_probe,
+ .remove = wilc_bus_remove,
+};
+module_spi_driver(wilc_spi_driver);
+MODULE_LICENSE("GPL");
+
+static int spi_data_rsp(struct wilc *wilc, u8 cmd)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ struct wilc_spi *spi_priv = wilc->bus_data;
+ u8 len;
+ u8 rsp[3];
+ int result = N_OK;
+
+ if (!spi_priv->crc_off)
+ len = 2;
+ else
+ len = 3;
+
+ if (wilc_spi_rx(wilc, &rsp[0], len)) {
+ dev_err(&spi->dev, "Failed bus error...\n");
+ result = N_FAIL;
+ goto fail;
+ }
+
+ if ((rsp[len-1] != 0) || (rsp[len-2] != 0xC3)) {
+ dev_err(&spi->dev, "Failed data response read, %x %x %x\n",
+ rsp[0], rsp[1], rsp[2]);
+ result = N_FAIL;
+ goto fail;
+ }
+
+fail:
+ return result;
+}
+
+static int wilc_spi_tx(struct wilc *wilc, u8 *b, u32 len)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ int ret;
+ struct spi_message msg;
+
+ if (len > 0 && b) {
+ struct spi_transfer tr = {
+ .tx_buf = b,
+ .len = len,
+ .delay_usecs = 0,
+ };
+ char *r_buffer = kzalloc(len, GFP_KERNEL);
+
+ if (!r_buffer)
+ return -ENOMEM;
+
+ tr.rx_buf = r_buffer;
+ dev_dbg(&spi->dev, "Request writing %d bytes\n", len);
+
+ memset(&msg, 0, sizeof(msg));
+ spi_message_init(&msg);
+ msg.spi = spi;
+ msg.is_dma_mapped = USE_SPI_DMA;
+ spi_message_add_tail(&tr, &msg);
+
+ ret = spi_sync(spi, &msg);
+ if (ret < 0)
+ dev_err(&spi->dev, "SPI transaction failed\n");
+
+ kfree(r_buffer);
+ } else {
+ dev_err(&spi->dev,
+ "can't write data with the following length: %d\n",
+ len);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int wilc_spi_rx(struct wilc *wilc, u8 *rb, u32 rlen)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ int ret;
+
+ if (rlen > 0) {
+ struct spi_message msg;
+ struct spi_transfer tr = {
+ .rx_buf = rb,
+ .len = rlen,
+ .delay_usecs = 0,
+
+ };
+ char *t_buffer = kzalloc(rlen, GFP_KERNEL);
+
+ if (!t_buffer)
+ return -ENOMEM;
+
+ tr.tx_buf = t_buffer;
+
+ memset(&msg, 0, sizeof(msg));
+ spi_message_init(&msg);
+ msg.spi = spi;
+ msg.is_dma_mapped = USE_SPI_DMA;
+ spi_message_add_tail(&tr, &msg);
+
+ ret = spi_sync(spi, &msg);
+ if (ret < 0)
+ dev_err(&spi->dev, "SPI transaction failed\n");
+ kfree(t_buffer);
+ } else {
+ dev_err(&spi->dev,
+ "can't read data with the following length: %u\n",
+ rlen);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int wilc_spi_tx_rx(struct wilc *wilc, u8 *wb, u8 *rb, u32 rlen)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ int ret;
+
+ if (rlen > 0) {
+ struct spi_message msg;
+ struct spi_transfer tr = {
+ .rx_buf = rb,
+ .tx_buf = wb,
+ .len = rlen,
+ .bits_per_word = 8,
+ .delay_usecs = 0,
+
+ };
+
+ memset(&msg, 0, sizeof(msg));
+ spi_message_init(&msg);
+ msg.spi = spi;
+ msg.is_dma_mapped = USE_SPI_DMA;
+
+ spi_message_add_tail(&tr, &msg);
+ ret = spi_sync(spi, &msg);
+ if (ret < 0)
+ dev_err(&spi->dev, "SPI transaction failed\n");
+ } else {
+ dev_err(&spi->dev,
+ "can't read data with the following length: %u\n",
+ rlen);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int spi_cmd_complete(struct wilc *wilc, u8 cmd, u32 adr, u8 *b, u32 sz,
+ u8 clockless)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ struct wilc_spi *spi_priv = wilc->bus_data;
+ u8 wb[32], rb[32];
+ u8 wix, rix;
+ u32 len2;
+ u8 rsp;
+ int len = 0;
+ int result = N_OK;
+ int retry;
+ u8 crc[2];
+
+ wb[0] = cmd;
+ switch (cmd) {
+ case CMD_SINGLE_READ: /* single word (4 bytes) read */
+ wb[1] = (u8)(adr >> 16);
+ wb[2] = (u8)(adr >> 8);
+ wb[3] = (u8)adr;
+ len = 5;
+ break;
+
+ case CMD_INTERNAL_READ: /* internal register read */
+ wb[1] = (u8)(adr >> 8);
+ if (clockless == 1)
+ wb[1] |= BIT(7);
+ wb[2] = (u8)adr;
+ wb[3] = 0x00;
+ len = 5;
+ break;
+
+ case CMD_TERMINATE:
+ wb[1] = 0x00;
+ wb[2] = 0x00;
+ wb[3] = 0x00;
+ len = 5;
+ break;
+
+ case CMD_REPEAT:
+ wb[1] = 0x00;
+ wb[2] = 0x00;
+ wb[3] = 0x00;
+ len = 5;
+ break;
+
+ case CMD_RESET:
+ wb[1] = 0xff;
+ wb[2] = 0xff;
+ wb[3] = 0xff;
+ len = 5;
+ break;
+
+ case CMD_DMA_WRITE: /* dma write */
+ case CMD_DMA_READ: /* dma read */
+ wb[1] = (u8)(adr >> 16);
+ wb[2] = (u8)(adr >> 8);
+ wb[3] = (u8)adr;
+ wb[4] = (u8)(sz >> 8);
+ wb[5] = (u8)(sz);
+ len = 7;
+ break;
+
+ case CMD_DMA_EXT_WRITE: /* dma extended write */
+ case CMD_DMA_EXT_READ: /* dma extended read */
+ wb[1] = (u8)(adr >> 16);
+ wb[2] = (u8)(adr >> 8);
+ wb[3] = (u8)adr;
+ wb[4] = (u8)(sz >> 16);
+ wb[5] = (u8)(sz >> 8);
+ wb[6] = (u8)(sz);
+ len = 8;
+ break;
+
+ case CMD_INTERNAL_WRITE: /* internal register write */
+ wb[1] = (u8)(adr >> 8);
+ if (clockless == 1)
+ wb[1] |= BIT(7);
+ wb[2] = (u8)(adr);
+ wb[3] = b[3];
+ wb[4] = b[2];
+ wb[5] = b[1];
+ wb[6] = b[0];
+ len = 8;
+ break;
+
+ case CMD_SINGLE_WRITE: /* single word write */
+ wb[1] = (u8)(adr >> 16);
+ wb[2] = (u8)(adr >> 8);
+ wb[3] = (u8)(adr);
+ wb[4] = b[3];
+ wb[5] = b[2];
+ wb[6] = b[1];
+ wb[7] = b[0];
+ len = 9;
+ break;
+
+ default:
+ result = N_FAIL;
+ break;
+ }
+
+ if (result != N_OK)
+ return result;
+
+ if (!spi_priv->crc_off)
+ wb[len - 1] = (crc7(0x7f, (const u8 *)&wb[0], len - 1)) << 1;
+ else
+ len -= 1;
+
+#define NUM_SKIP_BYTES (1)
+#define NUM_RSP_BYTES (2)
+#define NUM_DATA_HDR_BYTES (1)
+#define NUM_DATA_BYTES (4)
+#define NUM_CRC_BYTES (2)
+#define NUM_DUMMY_BYTES (3)
+ if (cmd == CMD_RESET ||
+ cmd == CMD_TERMINATE ||
+ cmd == CMD_REPEAT) {
+ len2 = len + (NUM_SKIP_BYTES + NUM_RSP_BYTES + NUM_DUMMY_BYTES);
+ } else if (cmd == CMD_INTERNAL_READ || cmd == CMD_SINGLE_READ) {
+ int tmp = NUM_RSP_BYTES + NUM_DATA_HDR_BYTES + NUM_DATA_BYTES
+ + NUM_DUMMY_BYTES;
+ if (!spi_priv->crc_off)
+ len2 = len + tmp + NUM_CRC_BYTES;
+ else
+ len2 = len + tmp;
+ } else {
+ len2 = len + (NUM_RSP_BYTES + NUM_DUMMY_BYTES);
+ }
+#undef NUM_DUMMY_BYTES
+
+ if (len2 > ARRAY_SIZE(wb)) {
+ dev_err(&spi->dev, "spi buffer size too small (%d) (%zu)\n",
+ len2, ARRAY_SIZE(wb));
+ return N_FAIL;
+ }
+ /* zero spi write buffers. */
+ for (wix = len; wix < len2; wix++)
+ wb[wix] = 0;
+ rix = len;
+
+ if (wilc_spi_tx_rx(wilc, wb, rb, len2)) {
+ dev_err(&spi->dev, "Failed cmd write, bus error...\n");
+ return N_FAIL;
+ }
+
+ /*
+ * Command/Control response
+ */
+ if (cmd == CMD_RESET || cmd == CMD_TERMINATE || cmd == CMD_REPEAT)
+ rix++; /* skip 1 byte */
+
+ rsp = rb[rix++];
+
+ if (rsp != cmd) {
+ dev_err(&spi->dev,
+ "Failed cmd response, cmd (%02x), resp (%02x)\n",
+ cmd, rsp);
+ return N_FAIL;
+ }
+
+ /*
+ * State response
+ */
+ rsp = rb[rix++];
+ if (rsp != 0x00) {
+ dev_err(&spi->dev, "Failed cmd state response state (%02x)\n",
+ rsp);
+ return N_FAIL;
+ }
+
+ if (cmd == CMD_INTERNAL_READ || cmd == CMD_SINGLE_READ ||
+ cmd == CMD_DMA_READ || cmd == CMD_DMA_EXT_READ) {
+ /*
+ * Data Respnose header
+ */
+ retry = SPI_RESP_RETRY_COUNT;
+ do {
+ /*
+ * ensure there is room in buffer later
+ * to read data and crc
+ */
+ if (rix < len2) {
+ rsp = rb[rix++];
+ } else {
+ retry = 0;
+ break;
+ }
+ if (((rsp >> 4) & 0xf) == 0xf)
+ break;
+ } while (retry--);
+
+ if (retry <= 0) {
+ dev_err(&spi->dev,
+ "Error, data read response (%02x)\n", rsp);
+ return N_RESET;
+ }
+ }
+
+ if (cmd == CMD_INTERNAL_READ || cmd == CMD_SINGLE_READ) {
+ /*
+ * Read bytes
+ */
+ if ((rix + 3) < len2) {
+ b[0] = rb[rix++];
+ b[1] = rb[rix++];
+ b[2] = rb[rix++];
+ b[3] = rb[rix++];
+ } else {
+ dev_err(&spi->dev,
+ "buffer overrun when reading data.\n");
+ return N_FAIL;
+ }
+
+ if (!spi_priv->crc_off) {
+ /*
+ * Read Crc
+ */
+ if ((rix + 1) < len2) {
+ crc[0] = rb[rix++];
+ crc[1] = rb[rix++];
+ } else {
+ dev_err(&spi->dev,
+ "buffer overrun when reading crc.\n");
+ return N_FAIL;
+ }
+ }
+ } else if ((cmd == CMD_DMA_READ) || (cmd == CMD_DMA_EXT_READ)) {
+ int ix;
+
+ /* some data may be read in response to dummy bytes. */
+ for (ix = 0; (rix < len2) && (ix < sz); )
+ b[ix++] = rb[rix++];
+
+ sz -= ix;
+
+ if (sz > 0) {
+ int nbytes;
+
+ if (sz <= (DATA_PKT_SZ - ix))
+ nbytes = sz;
+ else
+ nbytes = DATA_PKT_SZ - ix;
+
+ /*
+ * Read bytes
+ */
+ if (wilc_spi_rx(wilc, &b[ix], nbytes)) {
+ dev_err(&spi->dev,
+ "Failed block read, bus err\n");
+ return N_FAIL;
+ }
+
+ /*
+ * Read Crc
+ */
+ if (!spi_priv->crc_off && wilc_spi_rx(wilc, crc, 2)) {
+ dev_err(&spi->dev,
+ "Failed block crc read, bus err\n");
+ return N_FAIL;
+ }
+
+ ix += nbytes;
+ sz -= nbytes;
+ }
+
+ /*
+ * if any data in left unread,
+ * then read the rest using normal DMA code.
+ */
+ while (sz > 0) {
+ int nbytes;
+
+ if (sz <= DATA_PKT_SZ)
+ nbytes = sz;
+ else
+ nbytes = DATA_PKT_SZ;
+
+ /*
+ * read data response only on the next DMA cycles not
+ * the first DMA since data response header is already
+ * handled above for the first DMA.
+ */
+ /*
+ * Data Respnose header
+ */
+ retry = SPI_RESP_RETRY_COUNT;
+ do {
+ if (wilc_spi_rx(wilc, &rsp, 1)) {
+ dev_err(&spi->dev,
+ "Failed resp read, bus err\n");
+ result = N_FAIL;
+ break;
+ }
+ if (((rsp >> 4) & 0xf) == 0xf)
+ break;
+ } while (retry--);
+
+ if (result == N_FAIL)
+ break;
+
+ /*
+ * Read bytes
+ */
+ if (wilc_spi_rx(wilc, &b[ix], nbytes)) {
+ dev_err(&spi->dev,
+ "Failed block read, bus err\n");
+ result = N_FAIL;
+ break;
+ }
+
+ /*
+ * Read Crc
+ */
+ if (!spi_priv->crc_off && wilc_spi_rx(wilc, crc, 2)) {
+ dev_err(&spi->dev,
+ "Failed block crc read, bus err\n");
+ result = N_FAIL;
+ break;
+ }
+
+ ix += nbytes;
+ sz -= nbytes;
+ }
+ }
+ return result;
+}
+
+static int spi_data_write(struct wilc *wilc, u8 *b, u32 sz)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ struct wilc_spi *spi_priv = wilc->bus_data;
+ int ix, nbytes;
+ int result = 1;
+ u8 cmd, order, crc[2] = {0};
+
+ /*
+ * Data
+ */
+ ix = 0;
+ do {
+ if (sz <= DATA_PKT_SZ) {
+ nbytes = sz;
+ order = 0x3;
+ } else {
+ nbytes = DATA_PKT_SZ;
+ if (ix == 0)
+ order = 0x1;
+ else
+ order = 0x02;
+ }
+
+ /*
+ * Write command
+ */
+ cmd = 0xf0;
+ cmd |= order;
+
+ if (wilc_spi_tx(wilc, &cmd, 1)) {
+ dev_err(&spi->dev,
+ "Failed data block cmd write, bus error...\n");
+ result = N_FAIL;
+ break;
+ }
+
+ /*
+ * Write data
+ */
+ if (wilc_spi_tx(wilc, &b[ix], nbytes)) {
+ dev_err(&spi->dev,
+ "Failed data block write, bus error...\n");
+ result = N_FAIL;
+ break;
+ }
+
+ /*
+ * Write Crc
+ */
+ if (!spi_priv->crc_off) {
+ if (wilc_spi_tx(wilc, crc, 2)) {
+ dev_err(&spi->dev, "Failed data block crc write, bus error...\n");
+ result = N_FAIL;
+ break;
+ }
+ }
+
+ /*
+ * No need to wait for response
+ */
+ ix += nbytes;
+ sz -= nbytes;
+ } while (sz);
+
+ return result;
+}
+
+/********************************************
+ *
+ * Spi Internal Read/Write Function
+ *
+ ********************************************/
+
+static int spi_internal_write(struct wilc *wilc, u32 adr, u32 dat)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ int result;
+ u8 retry = SPI_RETRY_COUNT;
+
+retry:
+ cpu_to_le32s(&dat);
+ result = spi_cmd_complete(wilc, CMD_INTERNAL_WRITE, adr, (u8 *)&dat, 4,
+ 0);
+ if (result != N_OK) {
+ dev_err(&spi->dev, "Failed internal write cmd...\n");
+ goto fail;
+ }
+
+fail:
+ if (result != N_OK) {
+ usleep_range(1000, 1100);
+ wilc_spi_reset(wilc);
+ dev_err(&spi->dev, "Reset and retry %d %x\n", retry, adr);
+ usleep_range(1000, 1100);
+ retry--;
+ if (retry)
+ goto retry;
+ }
+ return result;
+}
+
+static int spi_internal_read(struct wilc *wilc, u32 adr, u32 *data)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ int result = N_OK;
+ u8 retry = SPI_RETRY_COUNT;
+
+retry:
+ result = spi_cmd_complete(wilc, CMD_INTERNAL_READ, adr, (u8 *)data, 4,
+ 0);
+ if (result != N_OK) {
+ dev_err(&spi->dev, "Failed internal read cmd...\n");
+ goto fail;
+ }
+
+ le32_to_cpus(data);
+
+fail:
+ if (result != N_OK) {
+ usleep_range(1000, 1100);
+ wilc_spi_reset(wilc);
+ dev_err(&spi->dev, "Reset and retry %d %x\n", retry, adr);
+ usleep_range(1000, 1100);
+ retry--;
+ if (retry)
+ goto retry;
+ }
+ return result;
+}
+
+/********************************************
+ *
+ * Spi interfaces
+ *
+ ********************************************/
+
+static int wilc_spi_write_reg(struct wilc *wilc, u32 addr, u32 data)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ u8 retry = SPI_RETRY_COUNT;
+ int result = N_OK;
+ u8 cmd = CMD_SINGLE_WRITE;
+ u8 clockless = 0;
+
+ cpu_to_le32s(&data);
+_RETRY_:
+ if (addr <= 0x30) {
+ /* Clockless register */
+ cmd = CMD_INTERNAL_WRITE;
+ clockless = 1;
+ } else {
+ cmd = CMD_SINGLE_WRITE;
+ clockless = 0;
+ }
+
+ result = spi_cmd_complete(wilc, cmd, addr, (u8 *)&data, 4, clockless);
+ if (result != N_OK) {
+ dev_err(&spi->dev, "Failed cmd, write reg (%08x)...\n", addr);
+ goto fail;
+ }
+
+fail:
+ if (result != N_OK) {
+ usleep_range(1000, 1100);
+ wilc_spi_reset(wilc);
+ dev_err(&spi->dev,
+ "Reset and retry %d %x %d\n", retry, addr, data);
+ usleep_range(1000, 1100);
+ retry--;
+ if (retry)
+ goto _RETRY_;
+ }
+ return result;
+}
+
+static int wilc_spi_write(struct wilc *wilc, u32 addr, u8 *buf, u32 size)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ int result;
+ u8 retry = SPI_RETRY_COUNT;
+
+ /*
+ * has to be greated than 4
+ */
+ if (size <= 4)
+ return 0;
+
+retry:
+ result = spi_cmd_complete(wilc, CMD_DMA_EXT_WRITE, addr, NULL, size, 0);
+ if (result != N_OK) {
+ dev_err(&spi->dev,
+ "Failed cmd, write block (%08x)...\n", addr);
+ goto fail;
+ }
+
+ /*
+ * Data
+ */
+ result = spi_data_write(wilc, buf, size);
+ if (result != N_OK) {
+ dev_err(&spi->dev, "Failed block data write...\n");
+ goto fail;
+ }
+ /*
+ * Data RESP
+ */
+ result = spi_data_rsp(wilc, CMD_DMA_EXT_WRITE);
+ if (result != N_OK) {
+ dev_err(&spi->dev, "Failed block data write...\n");
+ goto fail;
+ }
+
+fail:
+ if (result != N_OK) {
+ usleep_range(1000, 1100);
+ wilc_spi_reset(wilc);
+ dev_err(&spi->dev,
+ "Reset and retry %d %x %d\n", retry, addr, size);
+ usleep_range(1000, 1100);
+ retry--;
+ if (retry)
+ goto retry;
+ }
+ return result;
+}
+
+static int wilc_spi_read_reg(struct wilc *wilc, u32 addr, u32 *data)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ u8 retry = SPI_RETRY_COUNT;
+ int result = N_OK;
+ u8 cmd = CMD_SINGLE_READ;
+ u8 clockless = 0;
+
+retry:
+ if (addr <= 0x30) {
+ /* Clockless register */
+ cmd = CMD_INTERNAL_READ;
+ clockless = 1;
+ } else {
+ cmd = CMD_SINGLE_READ;
+ clockless = 0;
+ }
+
+ result = spi_cmd_complete(wilc, cmd, addr, (u8 *)data, 4, clockless);
+ if (result != N_OK) {
+ dev_err(&spi->dev, "Failed cmd, read reg (%08x)...\n", addr);
+ goto fail;
+ }
+
+ le32_to_cpus(data);
+
+fail:
+ if (result != N_OK) {
+ usleep_range(1000, 1100);
+ wilc_spi_reset(wilc);
+ dev_warn(&spi->dev, "Reset and retry %d %x\n", retry, addr);
+ usleep_range(1000, 1100);
+ retry--;
+ if (retry)
+ goto retry;
+ }
+ return result;
+}
+
+static int wilc_spi_read(struct wilc *wilc, u32 addr, u8 *buf, u32 size)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ int result;
+ u8 retry = SPI_RETRY_COUNT;
+
+ if (size <= 4)
+ return 0;
+
+retry:
+ result = spi_cmd_complete(wilc, CMD_DMA_EXT_READ, addr, buf, size, 0);
+ if (result != N_OK) {
+ dev_err(&spi->dev, "Failed cmd, read block (%08x)...\n", addr);
+ goto fail;
+ }
+
+fail:
+ if (result != N_OK) {
+ usleep_range(1000, 1100);
+ wilc_spi_reset(wilc);
+ dev_warn(&spi->dev, "Reset and retry %d %x %d\n", retry, addr,
+ size);
+ usleep_range(1000, 1100);
+ retry--;
+ if (retry)
+ goto retry;
+ }
+ return result;
+}
+
+/********************************************
+ *
+ * Bus interfaces
+ *
+ ********************************************/
+
+int wilc_spi_reset(struct wilc *wilc)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ int result = N_OK;
+
+ result = spi_cmd_complete(wilc, CMD_RESET, 0, 0, 0, 0);
+ if (result != N_OK) {
+ dev_err(&spi->dev, "Failed cmd reset\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static bool wilc_spi_is_init(struct wilc *wilc)
+{
+ struct wilc_spi *spi_priv = wilc->bus_data;
+
+ return spi_priv->is_init;
+}
+
+static int wilc_spi_deinit(struct wilc *wilc)
+{
+ struct wilc_spi *spi_priv = wilc->bus_data;
+
+ /*
+ * TODO:
+ */
+ spi_priv->is_init = false;
+
+ return 1;
+}
+
+static int wilc_spi_init(struct wilc *wilc, bool resume)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ struct wilc_spi *spi_priv = wilc->bus_data;
+ u32 reg;
+ u32 chipid;
+
+ if (spi_priv->is_init) {
+ if (!wilc_spi_read_reg(wilc, 0x1000, &chipid)) {
+ dev_err(&spi->dev, "Fail cmd read chip id...\n");
+ return 0;
+ }
+ return 1;
+ }
+
+ /*
+ * configure protocol
+ */
+
+ /*
+ * TODO: We can remove the CRC trials if there is a definite
+ * way to reset
+ */
+ /* the SPI to it's initial value. */
+ if (!spi_internal_read(wilc, WILC_SPI_PROTOCOL_OFFSET, ®)) {
+ /*
+ * Read failed. Try with CRC off. This might happen when module
+ * is removed but chip isn't reset
+ */
+ spi_priv->crc_off = 1;
+ dev_err(&spi->dev,
+ "Failed read with CRC on, retrying with CRC off\n");
+ if (!spi_internal_read(wilc, WILC_SPI_PROTOCOL_OFFSET, ®)) {
+ /*
+ * Read failed with both CRC on and off,
+ * something went bad
+ */
+ dev_err(&spi->dev, "Failed internal read protocol...\n");
+ return 0;
+ }
+ }
+ if (spi_priv->crc_off == 0) {
+ reg &= ~0xc; /* disable crc checking */
+ reg &= ~0x70;
+ reg |= (0x5 << 4);
+ if (!spi_internal_write(wilc, WILC_SPI_PROTOCOL_OFFSET, reg)) {
+ dev_err(&spi->dev,
+ "[wilc spi %d]: Failed internal write reg\n",
+ __LINE__);
+ return 0;
+ }
+ spi_priv->crc_off = 1;
+ }
+
+ /*
+ * make sure can read back chip id correctly
+ */
+ if (!wilc_spi_read_reg(wilc, 0x1000, &chipid)) {
+ dev_err(&spi->dev, "Fail cmd read chip id...\n");
+ return 0;
+ }
+
+ if (!resume) {
+ chipid = wilc_get_chipid(wilc, true);
+ if (is_wilc3000(chipid)) {
+ wilc->chip = WILC_3000;
+ goto pass;
+ } else if (is_wilc1000(chipid)) {
+ wilc->chip = WILC_1000;
+ goto pass;
+ } else {
+ dev_err(&spi->dev, "Unsupported chipid: %x\n", chipid);
+ goto fail;
+ }
+ dev_dbg(&spi->dev, "chipid %08x\n", chipid);
+ }
+
+pass:
+ spi_priv->is_init = true;
+ return 1;
+
+fail:
+ return 0;
+}
+
+static int wilc_spi_read_size(struct wilc *wilc, u32 *size)
+{
+ int ret;
+
+ ret = spi_internal_read(wilc, 0xe840 - WILC_SPI_REG_BASE,
+ size);
+ *size = *size & IRQ_DMA_WD_CNT_MASK;
+
+ return ret;
+}
+
+static int wilc_spi_read_int(struct wilc *wilc, u32 *int_status)
+{
+ return spi_internal_read(wilc, 0xe840 - WILC_SPI_REG_BASE,
+ int_status);
+}
+
+static int wilc_spi_clear_int_ext(struct wilc *wilc, u32 val)
+{
+ return spi_internal_write(wilc, 0xe844 - WILC_SPI_REG_BASE, val);
+}
+
+static int wilc_spi_sync_ext(struct wilc *wilc, int nint)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ struct wilc_spi *spi_priv = wilc->bus_data;
+ u32 reg;
+ int ret, i;
+
+ if (nint > MAX_NUM_INT) {
+ dev_err(&spi->dev, "Too many interrupts (%d)...\n", nint);
+ return 0;
+ }
+
+ spi_priv->nint = nint;
+
+ /*
+ * interrupt pin mux select
+ */
+ ret = wilc_spi_read_reg(wilc, WILC_PIN_MUX_0, ®);
+ if (!ret) {
+ dev_err(&spi->dev, "Failed read reg (%08x)...\n",
+ WILC_PIN_MUX_0);
+ return 0;
+ }
+ reg |= BIT(8);
+ ret = wilc_spi_write_reg(wilc, WILC_PIN_MUX_0, reg);
+ if (!ret) {
+ dev_err(&spi->dev, "Failed write reg (%08x)...\n",
+ WILC_PIN_MUX_0);
+ return 0;
+ }
+
+ /*
+ * interrupt enable
+ */
+ ret = wilc_spi_read_reg(wilc, WILC_INTR_ENABLE, ®);
+ if (!ret) {
+ dev_err(&spi->dev, "Failed read reg (%08x)...\n",
+ WILC_INTR_ENABLE);
+ return 0;
+ }
+
+ for (i = 0; (i < 5) && (nint > 0); i++, nint--)
+ reg |= (BIT((27 + i)));
+
+ ret = wilc_spi_write_reg(wilc, WILC_INTR_ENABLE, reg);
+ if (!ret) {
+ dev_err(&spi->dev, "Failed write reg (%08x)...\n",
+ WILC_INTR_ENABLE);
+ return 0;
+ }
+ if (nint) {
+ ret = wilc_spi_read_reg(wilc, WILC_INTR2_ENABLE, ®);
+ if (!ret) {
+ dev_err(&spi->dev, "Failed read reg (%08x)...\n",
+ WILC_INTR2_ENABLE);
+ return 0;
+ }
+
+ for (i = 0; (i < 3) && (nint > 0); i++, nint--)
+ reg |= BIT(i);
+
+ ret = wilc_spi_write_reg(wilc, WILC_INTR2_ENABLE, reg);
+ if (!ret) {
+ dev_err(&spi->dev, "Failed write reg (%08x)...\n",
+ WILC_INTR2_ENABLE);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* Global spi HIF function table */
+static const struct wilc_hif_func wilc_hif_spi = {
+ .hif_init = wilc_spi_init,
+ .hif_deinit = wilc_spi_deinit,
+ .hif_read_reg = wilc_spi_read_reg,
+ .hif_write_reg = wilc_spi_write_reg,
+ .hif_block_rx = wilc_spi_read,
+ .hif_block_tx = wilc_spi_write,
+ .hif_read_int = wilc_spi_read_int,
+ .hif_clear_int_ext = wilc_spi_clear_int_ext,
+ .hif_read_size = wilc_spi_read_size,
+ .hif_block_tx_ext = wilc_spi_write,
+ .hif_block_rx_ext = wilc_spi_read,
+ .hif_sync_ext = wilc_spi_sync_ext,
+ .hif_reset = wilc_spi_reset,
+ .hif_is_init = wilc_spi_is_init,
+};
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include <linux/etherdevice.h>
+
+#include "wilc_wfi_cfgoperations.h"
+#include "wilc_netdev.h"
+
+#define ACTION_CAT_ID 24
+#define ACTION_SUBTYPE_ID 25
+#define P2P_PUB_ACTION_SUBTYPE 30
+
+#define ACTION_FRAME 0xd0
+#define GO_INTENT_ATTR_ID 0x04
+#define CHANLIST_ATTR_ID 0x0b
+#define OPERCHAN_ATTR_ID 0x11
+#define PUB_ACTION_ATTR_ID 0x04
+#define P2PELEM_ATTR_ID 0xdd
+
+#define GO_NEG_REQ 0x00
+#define GO_NEG_RSP 0x01
+#define GO_NEG_CONF 0x02
+#define P2P_INV_REQ 0x03
+#define P2P_INV_RSP 0x04
+#define PUBLIC_ACT_VENDORSPEC 0x09
+#define GAS_INITIAL_REQ 0x0a
+#define GAS_INITIAL_RSP 0x0b
+
+#define WILC_INVALID_CHANNEL 0
+
+static const struct ieee80211_txrx_stypes
+ wilc_wfi_cfg80211_mgmt_types[NUM_NL80211_IFTYPES] = {
+ [NL80211_IFTYPE_STATION] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+ },
+ [NL80211_IFTYPE_AP] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+ BIT(IEEE80211_STYPE_ACTION >> 4)
+ },
+ [NL80211_IFTYPE_P2P_CLIENT] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4)
+ }
+};
+
+static const struct wiphy_wowlan_support wowlan_support = {
+ .flags = WIPHY_WOWLAN_ANY
+};
+
+struct wilc_p2p_mgmt_data {
+ int size;
+ u8 *buff;
+};
+
+static const u8 p2p_oui[] = {0x50, 0x6f, 0x9A, 0x09};
+static const u8 p2p_vendor_spec[] = {0xdd, 0x05, 0x00, 0x08, 0x40, 0x03};
+
+static void cfg_scan_result(enum scan_event scan_event,
+ struct wilc_rcvd_net_info *info, void *user_void)
+{
+ struct wilc_priv *priv = user_void;
+
+ if (!priv->cfg_scanning)
+ return;
+
+ if (scan_event == SCAN_EVENT_NETWORK_FOUND) {
+ s32 freq;
+ struct ieee80211_channel *channel;
+ struct cfg80211_bss *bss;
+ struct wiphy *wiphy = priv->dev->ieee80211_ptr->wiphy;
+
+ if (!wiphy || !info)
+ return;
+
+ freq = ieee80211_channel_to_frequency((s32)info->ch,
+ NL80211_BAND_2GHZ);
+ channel = ieee80211_get_channel(wiphy, freq);
+ if (!channel)
+ return;
+
+ PRINT_D(priv->dev, CFG80211_DBG,
+ "Network Info:: CHANNEL Frequency: %d, RSSI: %d,\n",
+ freq, ((s32)info->rssi * 100));
+
+ bss = cfg80211_inform_bss_frame(wiphy, channel, info->mgmt,
+ info->frame_len,
+ (s32)info->rssi * 100,
+ GFP_KERNEL);
+ if (!bss)
+ cfg80211_put_bss(wiphy, bss);
+ } else if (scan_event == SCAN_EVENT_DONE) {
+ PRINT_INFO(priv->dev, CFG80211_DBG, "Scan Done[%p]\n",
+ priv->dev);
+ mutex_lock(&priv->scan_req_lock);
+
+ if (priv->scan_req) {
+#if KERNEL_VERSION(4, 7, 0) <= LINUX_VERSION_CODE
+ struct cfg80211_scan_info info = {
+ .aborted = false,
+ };
+
+ cfg80211_scan_done(priv->scan_req, &info);
+#else
+ cfg80211_scan_done(priv->scan_req, false);
+#endif
+ priv->cfg_scanning = false;
+ priv->scan_req = NULL;
+ }
+ mutex_unlock(&priv->scan_req_lock);
+ } else if (scan_event == SCAN_EVENT_ABORTED) {
+ mutex_lock(&priv->scan_req_lock);
+
+ PRINT_INFO(priv->dev, CFG80211_DBG, "Scan Aborted\n");
+ if (priv->scan_req) {
+#if KERNEL_VERSION(4, 7, 0) <= LINUX_VERSION_CODE
+ struct cfg80211_scan_info info = {
+ .aborted = false,
+ };
+ cfg80211_scan_done(priv->scan_req, &info);
+#else
+ cfg80211_scan_done(priv->scan_req, false);
+#endif
+
+ priv->cfg_scanning = false;
+ priv->scan_req = NULL;
+ }
+ mutex_unlock(&priv->scan_req_lock);
+ }
+}
+
+static void cfg_connect_result(enum conn_event conn_disconn_evt,
+ u8 mac_status, void *priv_data)
+{
+ struct wilc_priv *priv = priv_data;
+ struct net_device *dev = priv->dev;
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct host_if_drv *wfi_drv = priv->hif_drv;
+ struct wilc_conn_info *conn_info = &wfi_drv->conn_info;
+
+ vif->connecting = false;
+
+ if (conn_disconn_evt == EVENT_CONN_RESP) {
+ u16 connect_status = conn_info->status;
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Connection response received=%d connect_stat[%d]\n",
+ mac_status, connect_status);
+ if (mac_status == WILC_MAC_STATUS_DISCONNECTED &&
+ connect_status == WLAN_STATUS_SUCCESS) {
+ connect_status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ wilc_wlan_set_bssid(priv->dev, NULL, WILC_STATION_MODE);
+
+ if (vif->iftype != WILC_CLIENT_MODE)
+ vif->wilc->sta_ch = WILC_INVALID_CHANNEL;
+
+ PRINT_ER(dev, "Unspecified failure\n");
+ }
+
+ if (connect_status == WLAN_STATUS_SUCCESS) {
+ PRINT_ER(dev,
+ "Connection Successful: BSSID: %x%x%x%x%x%x\n",
+ conn_info->bssid[0], conn_info->bssid[1],
+ conn_info->bssid[2], conn_info->bssid[3],
+ conn_info->bssid[4], conn_info->bssid[5]);
+ memcpy(priv->associated_bss, conn_info->bssid,
+ ETH_ALEN);
+ }
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Association request info elements length = %d\n",
+ conn_info->req_ies_len);
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Association response info elements length = %d\n",
+ conn_info->resp_ies_len);
+ cfg80211_connect_result(dev, conn_info->bssid,
+ conn_info->req_ies,
+ conn_info->req_ies_len,
+ conn_info->resp_ies,
+ conn_info->resp_ies_len, connect_status,
+ GFP_KERNEL);
+ } else if (conn_disconn_evt == EVENT_DISCONN_NOTIF) {
+ u16 reason = 0;
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+ vif->obtaining_ip = false;
+#endif
+ PRINT_ER(vif->ndev,
+ "Received WILC_MAC_STATUS_DISCONNECTED dev [%p]\n",
+ priv->dev);
+ priv->p2p.local_random = 0x01;
+ priv->p2p.recv_random = 0x00;
+ priv->p2p.is_wilc_ie = false;
+ eth_zero_addr(priv->associated_bss);
+ wilc_wlan_set_bssid(priv->dev, NULL, WILC_STATION_MODE);
+
+ if (vif->iftype != WILC_CLIENT_MODE) {
+ vif->wilc->sta_ch = WILC_INVALID_CHANNEL;
+ } else {
+ if (wfi_drv->ifc_up)
+ reason = 3;
+ else
+ reason = 1;
+ }
+#if KERNEL_VERSION(4, 2, 0) > LINUX_VERSION_CODE
+ cfg80211_disconnected(dev, reason, NULL, 0, GFP_KERNEL);
+#else
+ cfg80211_disconnected(dev, reason, NULL, 0, false, GFP_KERNEL);
+#endif
+ }
+}
+
+static int set_channel(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef)
+{
+ u32 channelnum = 0;
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(priv->dev);
+ int result = 0;
+
+ channelnum = ieee80211_frequency_to_channel(chandef->chan->center_freq);
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Setting channel %d with frequency %d\n",
+ channelnum, chandef->chan->center_freq);
+
+ vif->wilc->op_ch = channelnum;
+ result = wilc_set_mac_chnl_num(vif, channelnum);
+
+ if (result != 0)
+ PRINT_ER(priv->dev, "Error in setting channel %d\n",
+ channelnum);
+
+ return result;
+}
+
+static int scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
+{
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(priv->dev);
+ u32 i;
+ int ret = 0;
+ u8 scan_ch_list[WILC_MAX_NUM_SCANNED_CH];
+ u8 scan_type;
+
+ if (request->n_channels > WILC_MAX_NUM_SCANNED_CH) {
+ PRINT_ER(priv->dev, "Requested scanned channels over\n");
+ return -EINVAL;
+ }
+
+ priv->scan_req = request;
+ priv->cfg_scanning = true;
+ for (i = 0; i < request->n_channels; i++) {
+ u16 freq = request->channels[i]->center_freq;
+
+ scan_ch_list[i] = (u8)ieee80211_frequency_to_channel(freq);
+ PRINT_D(vif->ndev, CFG80211_DBG,
+ "ScanChannel List[%d] = %d",
+ i, scan_ch_list[i]);
+ }
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "Requested num of channel %d\n",
+ request->n_channels);
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "Scan Request IE len = %d\n",
+ request->ie_len);
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "Number of SSIDs %d\n",
+ request->n_ssids);
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Trigger Scan Request\n");
+
+ if (request->n_ssids)
+ scan_type = WILC_FW_ACTIVE_SCAN;
+ else
+ scan_type = WILC_FW_PASSIVE_SCAN;
+
+ ret = wilc_scan(vif, WILC_FW_USER_SCAN, scan_type, scan_ch_list,
+ request->n_channels, cfg_scan_result, (void *)priv,
+ request);
+
+ if (ret) {
+ priv->scan_req = NULL;
+ priv->cfg_scanning = false;
+ PRINT_WRN(vif->ndev, CFG80211_DBG,
+ "Device is busy: Error(%d)\n", ret);
+ }
+
+ return ret;
+}
+
+static int connect(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_connect_params *sme)
+{
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(priv->dev);
+ struct host_if_drv *wfi_drv = priv->hif_drv;
+ int ret;
+ u32 i;
+ u8 security = WILC_FW_SEC_NO;
+ enum authtype auth_type = WILC_FW_AUTH_ANY;
+ u32 cipher_group;
+ struct cfg80211_bss *bss;
+ void *join_params;
+ u8 ch;
+
+ vif->connecting = true;
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Connecting to SSID [%s] on netdev [%p] host if [%x]\n",
+ sme->ssid, dev, (u32)priv->hif_drv);
+
+ if (vif->iftype == WILC_CLIENT_MODE)
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Connected to Direct network,OBSS disabled\n");
+
+ PRINT_D(vif->ndev, CFG80211_DBG, "Required SSID= %s\n, AuthType= %d\n",
+ sme->ssid, sme->auth_type);
+
+ memset(priv->wep_key, 0, sizeof(priv->wep_key));
+ memset(priv->wep_key_len, 0, sizeof(priv->wep_key_len));
+
+ PRINT_D(vif->ndev, CFG80211_DBG, "sme->crypto.wpa_versions=%x\n",
+ sme->crypto.wpa_versions);
+ PRINT_D(vif->ndev, CFG80211_DBG, "sme->crypto.cipher_group=%x\n",
+ sme->crypto.cipher_group);
+ PRINT_D(vif->ndev, CFG80211_DBG, "sme->crypto.n_ciphers_pairwise=%d\n",
+ sme->crypto.n_ciphers_pairwise);
+ for (i = 0; i < sme->crypto.n_ciphers_pairwise; i++)
+ PRINT_D(vif->ndev, CORECONFIG_DBG,
+ "sme->crypto.ciphers_pairwise[%d]=%x\n", i,
+ sme->crypto.ciphers_pairwise[i]);
+
+ cipher_group = sme->crypto.cipher_group;
+ if (cipher_group != 0) {
+ PRINT_INFO(vif->ndev, CORECONFIG_DBG,
+ ">> sme->crypto.wpa_versions: %x\n",
+ sme->crypto.wpa_versions);
+ if (cipher_group == WLAN_CIPHER_SUITE_WEP40) {
+ security = WILC_FW_SEC_WEP;
+ PRINT_D(vif->ndev, CFG80211_DBG,
+ "WEP Default Key Idx = %d\n", sme->key_idx);
+
+ for (i = 0; i < sme->key_len; i++)
+ PRINT_D(vif->ndev, CORECONFIG_DBG,
+ "WEP Key Value[%d] = %d\n", i, sme->key[i]);
+
+ priv->wep_key_len[sme->key_idx] = sme->key_len;
+ memcpy(priv->wep_key[sme->key_idx], sme->key,
+ sme->key_len);
+
+ wilc_set_wep_default_keyid(vif, sme->key_idx);
+ wilc_add_wep_key_bss_sta(vif, sme->key, sme->key_len,
+ sme->key_idx);
+ } else if (cipher_group == WLAN_CIPHER_SUITE_WEP104) {
+ security = WILC_FW_SEC_WEP_EXTENDED;
+
+ priv->wep_key_len[sme->key_idx] = sme->key_len;
+ memcpy(priv->wep_key[sme->key_idx], sme->key,
+ sme->key_len);
+
+ wilc_set_wep_default_keyid(vif, sme->key_idx);
+ wilc_add_wep_key_bss_sta(vif, sme->key, sme->key_len,
+ sme->key_idx);
+ } else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) {
+ if (cipher_group == WLAN_CIPHER_SUITE_TKIP)
+ security = WILC_FW_SEC_WPA2_TKIP;
+ else
+ security = WILC_FW_SEC_WPA2_AES;
+ } else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) {
+ if (cipher_group == WLAN_CIPHER_SUITE_TKIP)
+ security = WILC_FW_SEC_WPA_TKIP;
+ else
+ security = WILC_FW_SEC_WPA_AES;
+ } else {
+ ret = -ENOTSUPP;
+ PRINT_ER(dev, "Unsupported cipher\n");
+ goto out_error;
+ }
+ }
+
+ if ((sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) ||
+ (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)) {
+ for (i = 0; i < sme->crypto.n_ciphers_pairwise; i++) {
+ u32 ciphers_pairwise = sme->crypto.ciphers_pairwise[i];
+
+ if (ciphers_pairwise == WLAN_CIPHER_SUITE_TKIP)
+ security |= WILC_FW_TKIP;
+ else
+ security |= WILC_FW_AES;
+ }
+ }
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "Adding key with cipher group %x\n",
+ cipher_group);
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "Authentication Type = %d\n",
+ sme->auth_type);
+ switch (sme->auth_type) {
+ case NL80211_AUTHTYPE_OPEN_SYSTEM:
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "In OPEN SYSTEM\n");
+ auth_type = WILC_FW_AUTH_OPEN_SYSTEM;
+ break;
+
+ case NL80211_AUTHTYPE_SHARED_KEY:
+ auth_type = WILC_FW_AUTH_SHARED_KEY;
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "In SHARED KEY\n");
+ break;
+
+ default:
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Automatic Authentication type= %d\n",
+ sme->auth_type);
+ break;
+ }
+
+ if (sme->crypto.n_akm_suites) {
+ if (sme->crypto.akm_suites[0] == WLAN_AKM_SUITE_8021X)
+ auth_type = WILC_FW_AUTH_IEEE8021;
+ }
+
+ if (wfi_drv->usr_scan_req.scan_result) {
+ netdev_err(vif->ndev, "%s: Scan in progress\n", __func__);
+ ret = -EBUSY;
+ goto out_error;
+ }
+
+#if KERNEL_VERSION(4, 1, 0) > LINUX_VERSION_CODE
+ bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, sme->ssid,
+ sme->ssid_len, WLAN_CAPABILITY_ESS,
+ WLAN_CAPABILITY_ESS);
+#else
+ bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, sme->ssid,
+ sme->ssid_len, IEEE80211_BSS_TYPE_ANY,
+ IEEE80211_PRIVACY(sme->privacy));
+#endif
+ if (!bss) {
+ ret = -EINVAL;
+ goto out_error;
+ }
+
+ if (ether_addr_equal_unaligned(vif->bssid, bss->bssid)) {
+ ret = -EALREADY;
+ goto out_put_bss;
+ }
+
+ join_params = wilc_parse_join_bss_param(bss, &sme->crypto);
+ if (!join_params) {
+ PRINT_ER(vif->ndev, "%s: failed to construct join param\n",
+ __func__);
+ ret = -EINVAL;
+ goto out_put_bss;
+ }
+
+ ch = ieee80211_frequency_to_channel(bss->channel->center_freq);
+ PRINT_D(vif->ndev, CFG80211_DBG, "Required Channel = %d\n", ch);
+ vif->wilc->op_ch = ch;
+ if (vif->iftype != WILC_CLIENT_MODE)
+ vif->wilc->sta_ch = ch;
+
+ wilc_wlan_set_bssid(dev, bss->bssid, WILC_STATION_MODE);
+
+ wfi_drv->conn_info.security = security;
+ wfi_drv->conn_info.auth_type = auth_type;
+ wfi_drv->conn_info.ch = ch;
+ wfi_drv->conn_info.conn_result = cfg_connect_result;
+ wfi_drv->conn_info.arg = priv;
+ wfi_drv->conn_info.param = join_params;
+
+ ret = wilc_set_join_req(vif, bss->bssid, sme->ie, sme->ie_len);
+ if (ret) {
+ PRINT_ER(dev, "wilc_set_join_req(): Error(%d)\n", ret);
+ ret = -ENOENT;
+ if (vif->iftype != WILC_CLIENT_MODE)
+ vif->wilc->sta_ch = WILC_INVALID_CHANNEL;
+ wilc_wlan_set_bssid(dev, NULL, WILC_STATION_MODE);
+ wfi_drv->conn_info.conn_result = NULL;
+ kfree(join_params);
+ goto out_put_bss;
+ }
+ kfree(join_params);
+ cfg80211_put_bss(wiphy, bss);
+ return 0;
+
+out_put_bss:
+ cfg80211_put_bss(wiphy, bss);
+
+out_error:
+ vif->connecting = false;
+ return ret;
+}
+
+static int disconnect(struct wiphy *wiphy, struct net_device *dev,
+ u16 reason_code)
+{
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(priv->dev);
+ struct wilc *wilc = vif->wilc;
+ struct host_if_drv *wfi_drv;
+ int ret;
+
+ vif->connecting = false;
+
+ if (!wilc)
+ return -EIO;
+ wfi_drv = (struct host_if_drv *)priv->hif_drv;
+ if (vif->iftype != WILC_CLIENT_MODE)
+ wilc->sta_ch = WILC_INVALID_CHANNEL;
+ wilc_wlan_set_bssid(priv->dev, NULL, WILC_STATION_MODE);
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Disconnecting with reason code(%d)\n", reason_code);
+ priv->p2p.local_random = 0x01;
+ priv->p2p.recv_random = 0x00;
+ priv->p2p.is_wilc_ie = false;
+ wfi_drv->p2p_timeout = 0;
+
+ ret = wilc_disconnect(vif);
+ if (ret != 0) {
+ PRINT_ER(priv->dev, "Error in disconnecting (%d)\n", ret);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static inline void wilc_wfi_cfg_copy_wep_info(struct wilc_priv *priv,
+ u8 key_index,
+ struct key_params *params)
+{
+ priv->wep_key_len[key_index] = params->key_len;
+ memcpy(priv->wep_key[key_index], params->key, params->key_len);
+}
+
+static int wilc_wfi_cfg_allocate_wpa_entry(struct wilc_priv *priv, u8 idx)
+{
+ if (!priv->wilc_gtk[idx]) {
+ priv->wilc_gtk[idx] = kzalloc(sizeof(*priv->wilc_gtk[idx]),
+ GFP_KERNEL);
+ if (!priv->wilc_gtk[idx])
+ return -ENOMEM;
+ }
+
+ if (!priv->wilc_ptk[idx]) {
+ priv->wilc_ptk[idx] = kzalloc(sizeof(*priv->wilc_ptk[idx]),
+ GFP_KERNEL);
+ if (!priv->wilc_ptk[idx])
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int wilc_wfi_cfg_copy_wpa_info(struct wilc_wfi_key *key_info,
+ struct key_params *params)
+{
+ kfree(key_info->key);
+
+ key_info->key = kmemdup(params->key, params->key_len, GFP_KERNEL);
+ if (!key_info->key)
+ return -ENOMEM;
+
+ kfree(key_info->seq);
+
+ if (params->seq_len > 0) {
+ key_info->seq = kmemdup(params->seq, params->seq_len,
+ GFP_KERNEL);
+ if (!key_info->seq)
+ return -ENOMEM;
+ }
+
+ key_info->cipher = params->cipher;
+ key_info->key_len = params->key_len;
+ key_info->seq_len = params->seq_len;
+
+ return 0;
+}
+
+static int add_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
+ bool pairwise,
+ const u8 *mac_addr, struct key_params *params)
+
+{
+ int ret = 0, keylen = params->key_len;
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ const u8 *rx_mic = NULL;
+ const u8 *tx_mic = NULL;
+ u8 mode = WILC_FW_SEC_NO;
+ u8 op_mode;
+ int i;
+ struct wilc_vif *vif = netdev_priv(netdev);
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Adding key with cipher suite = %x\n", params->cipher);
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "%x %x %d\n", (u32)wiphy,
+ (u32)netdev, key_index);
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "key %x %x %x\n", params->key[0],
+ params->key[1],
+ params->key[2]);
+ switch (params->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ if (priv->wdev->iftype == NL80211_IFTYPE_AP) {
+ wilc_wfi_cfg_copy_wep_info(priv, key_index, params);
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Adding AP WEP Default key Idx = %d\n",
+ key_index);
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Adding AP WEP Key len= %d\n",
+ params->key_len);
+
+ for (i = 0; i < params->key_len; i++)
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "WEP AP key val[%d] = %x\n", i,
+ params->key[i]);
+
+ if (params->cipher == WLAN_CIPHER_SUITE_WEP40)
+ mode = WILC_FW_SEC_WEP;
+ else
+ mode = WILC_FW_SEC_WEP_EXTENDED;
+
+ ret = wilc_add_wep_key_bss_ap(vif, params->key,
+ params->key_len,
+ key_index, mode,
+ WILC_FW_AUTH_OPEN_SYSTEM);
+ break;
+ }
+ if (memcmp(params->key, priv->wep_key[key_index],
+ params->key_len)) {
+ wilc_wfi_cfg_copy_wep_info(priv, key_index, params);
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Adding WEP Default key Idx = %d\n",
+ key_index);
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Adding WEP Key length = %d\n",
+ params->key_len);
+ ret = wilc_add_wep_key_bss_sta(vif, params->key,
+ params->key_len,
+ key_index);
+ }
+
+ break;
+
+ case WLAN_CIPHER_SUITE_TKIP:
+ case WLAN_CIPHER_SUITE_CCMP:
+ if (priv->wdev->iftype == NL80211_IFTYPE_AP ||
+ priv->wdev->iftype == NL80211_IFTYPE_P2P_GO) {
+ struct wilc_wfi_key *key;
+
+ ret = wilc_wfi_cfg_allocate_wpa_entry(priv, key_index);
+ if (ret)
+ return -ENOMEM;
+
+ if (params->key_len > 16 &&
+ params->cipher == WLAN_CIPHER_SUITE_TKIP) {
+ tx_mic = params->key + 24;
+ rx_mic = params->key + 16;
+ keylen = params->key_len - 16;
+ }
+
+ if (!pairwise) {
+ if (params->cipher == WLAN_CIPHER_SUITE_TKIP)
+ mode = WILC_FW_SEC_WPA_TKIP;
+ else
+ mode = WILC_FW_SEC_WPA2_AES;
+
+ priv->wilc_groupkey = mode;
+
+ key = priv->wilc_gtk[key_index];
+ } else {
+ PRINT_D(vif->ndev, CFG80211_DBG,
+ "STA Address: %x%x%x%x%x\n",
+ mac_addr[0], mac_addr[1], mac_addr[2],
+ mac_addr[3], mac_addr[4]);
+ if (params->cipher == WLAN_CIPHER_SUITE_TKIP)
+ mode = WILC_FW_SEC_WPA_TKIP;
+ else
+ mode = (priv->wilc_groupkey |
+ WILC_FW_AES);
+
+ key = priv->wilc_ptk[key_index];
+ }
+ ret = wilc_wfi_cfg_copy_wpa_info(key, params);
+ if (ret)
+ return -ENOMEM;
+
+ op_mode = WILC_AP_MODE;
+ } else {
+ if (params->key_len > 16 &&
+ params->cipher == WLAN_CIPHER_SUITE_TKIP) {
+ rx_mic = params->key + 24;
+ tx_mic = params->key + 16;
+ keylen = params->key_len - 16;
+ }
+
+ op_mode = WILC_STATION_MODE;
+ }
+
+ if (!pairwise)
+ ret = wilc_add_rx_gtk(vif, params->key, keylen,
+ key_index, params->seq_len,
+ params->seq, rx_mic, tx_mic,
+ op_mode, mode);
+ else
+ ret = wilc_add_ptk(vif, params->key, keylen, mac_addr,
+ rx_mic, tx_mic, op_mode, mode,
+ key_index);
+
+ break;
+
+ default:
+ PRINT_ER(netdev, "Unsupported cipher\n");
+ ret = -ENOTSUPP;
+ }
+
+ return ret;
+}
+
+static int del_key(struct wiphy *wiphy, struct net_device *netdev,
+ u8 key_index,
+ bool pairwise,
+ const u8 *mac_addr)
+{
+ int ret = 0;
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(netdev);
+ struct wilc *wl = vif->wilc;
+
+ if (netdev == wl->vif[0]->ndev) {
+ if (priv->wilc_gtk[key_index]) {
+ kfree(priv->wilc_gtk[key_index]->key);
+ priv->wilc_gtk[key_index]->key = NULL;
+ kfree(priv->wilc_gtk[key_index]->seq);
+ priv->wilc_gtk[key_index]->seq = NULL;
+
+ kfree(priv->wilc_gtk[key_index]);
+ priv->wilc_gtk[key_index] = NULL;
+ }
+
+ if (priv->wilc_ptk[key_index]) {
+ kfree(priv->wilc_ptk[key_index]->key);
+ priv->wilc_ptk[key_index]->key = NULL;
+ kfree(priv->wilc_ptk[key_index]->seq);
+ priv->wilc_ptk[key_index]->seq = NULL;
+ kfree(priv->wilc_ptk[key_index]);
+ priv->wilc_ptk[key_index] = NULL;
+ }
+ }
+
+ if (key_index <= 3 && priv->wep_key_len[key_index]) {
+ memset(priv->wep_key[key_index], 0,
+ priv->wep_key_len[key_index]);
+ priv->wep_key_len[key_index] = 0;
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Removing WEP key with index = %d\n",
+ key_index);
+ ret = wilc_remove_wep_key(vif, key_index);
+ }
+
+ return ret;
+}
+
+static int get_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
+ bool pairwise, const u8 *mac_addr, void *cookie,
+ void (*callback)(void *cookie, struct key_params *))
+{
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct key_params key_params;
+ struct wilc_vif *vif = netdev_priv(netdev);
+
+ if (!pairwise) {
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Getting group key idx: %x\n", key_index);
+ key_params.key = priv->wilc_gtk[key_index]->key;
+ key_params.cipher = priv->wilc_gtk[key_index]->cipher;
+ key_params.key_len = priv->wilc_gtk[key_index]->key_len;
+ key_params.seq = priv->wilc_gtk[key_index]->seq;
+ key_params.seq_len = priv->wilc_gtk[key_index]->seq_len;
+ } else {
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "Getting pairwise key\n");
+ key_params.key = priv->wilc_ptk[key_index]->key;
+ key_params.cipher = priv->wilc_ptk[key_index]->cipher;
+ key_params.key_len = priv->wilc_ptk[key_index]->key_len;
+ key_params.seq = priv->wilc_ptk[key_index]->seq;
+ key_params.seq_len = priv->wilc_ptk[key_index]->seq_len;
+ }
+
+ callback(cookie, &key_params);
+
+ return 0;
+}
+
+static int set_default_key(struct wiphy *wiphy, struct net_device *netdev,
+ u8 key_index, bool unicast, bool multicast)
+{
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(priv->dev);
+
+ wilc_set_wep_default_keyid(vif, key_index);
+
+ return 0;
+}
+
+#if KERNEL_VERSION(3, 16, 0) <= LINUX_VERSION_CODE
+static int get_station(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *mac, struct station_info *sinfo)
+#else
+static int get_station(struct wiphy *wiphy, struct net_device *dev,
+ u8 *mac, struct station_info *sinfo)
+#endif
+{
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wilc = vif->wilc;
+ u32 i = 0;
+ u32 associatedsta = ~0;
+ u32 inactive_time = 0;
+
+ if (vif->iftype == WILC_AP_MODE || vif->iftype == WILC_GO_MODE) {
+ PRINT_INFO(vif->ndev, HOSTAPD_DBG,
+ "Getting station parameters\n");
+ for (i = 0; i < NUM_STA_ASSOCIATED; i++) {
+ if (!(memcmp(mac,
+ priv->assoc_stainfo.sta_associated_bss[i],
+ ETH_ALEN))) {
+ associatedsta = i;
+ break;
+ }
+ }
+
+ if (associatedsta == ~0) {
+ PRINT_ER(dev, "sta required is not associated\n");
+ return -ENOENT;
+ }
+
+#if KERNEL_VERSION(3, 19, 0) <= LINUX_VERSION_CODE
+ sinfo->filled |= BIT(NL80211_STA_INFO_INACTIVE_TIME);
+#else
+ sinfo->filled |= STATION_INFO_INACTIVE_TIME;
+#endif
+
+ wilc_get_inactive_time(vif, mac, &inactive_time);
+ sinfo->inactive_time = 1000 * inactive_time;
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "Inactive time %d\n",
+ sinfo->inactive_time);
+ } else if (vif->iftype == WILC_STATION_MODE) {
+ struct rf_info stats;
+
+ if (!wilc->initialized) {
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "driver not initialized\n");
+ return -EBUSY;
+ }
+ wilc_get_statistics(vif, &stats);
+#if KERNEL_VERSION(3, 19, 0) <= LINUX_VERSION_CODE
+ sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL) |
+ BIT(NL80211_STA_INFO_RX_PACKETS) |
+ BIT(NL80211_STA_INFO_TX_PACKETS) |
+ BIT(NL80211_STA_INFO_TX_FAILED) |
+ BIT(NL80211_STA_INFO_TX_BITRATE);
+#else
+ sinfo->filled |= STATION_INFO_SIGNAL |
+ STATION_INFO_RX_PACKETS |
+ STATION_INFO_TX_PACKETS |
+ STATION_INFO_TX_FAILED |
+ STATION_INFO_TX_BITRATE;
+#endif
+ sinfo->signal = stats.rssi;
+ sinfo->rx_packets = stats.rx_cnt;
+ sinfo->tx_packets = stats.tx_cnt + stats.tx_fail_cnt;
+ sinfo->tx_failed = stats.tx_fail_cnt;
+ sinfo->txrate.legacy = stats.link_speed * 10;
+
+ if (stats.link_speed > TCP_ACK_FILTER_LINK_SPEED_THRESH &&
+ stats.link_speed != DEFAULT_LINK_SPEED)
+ wilc_enable_tcp_ack_filter(vif, true);
+ else if (stats.link_speed != DEFAULT_LINK_SPEED)
+ wilc_enable_tcp_ack_filter(vif, false);
+
+ PRINT_INFO(vif->ndev, CORECONFIG_DBG,
+ "*** stats[%d][%d][%d][%d][%d]\n", sinfo->signal,
+ sinfo->rx_packets, sinfo->tx_packets,
+ sinfo->tx_failed, sinfo->txrate.legacy);
+ }
+ return 0;
+}
+
+static int change_bss(struct wiphy *wiphy, struct net_device *dev,
+ struct bss_parameters *params)
+{
+ PRINT_INFO(dev, CFG80211_DBG, "Changing Bss parametrs\n");
+ return 0;
+}
+
+static int set_wiphy_params(struct wiphy *wiphy, u32 changed)
+{
+ int ret;
+ struct cfg_param_attr cfg_param_val;
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(priv->dev);
+
+ cfg_param_val.flag = 0;
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "Setting Wiphy params\n");
+
+ if (changed & WIPHY_PARAM_RETRY_SHORT) {
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Setting WIPHY_PARAM_RETRY_SHORT %d\n",
+ wiphy->retry_short);
+ cfg_param_val.flag |= WILC_CFG_PARAM_RETRY_SHORT;
+ cfg_param_val.short_retry_limit = wiphy->retry_short;
+ }
+ if (changed & WIPHY_PARAM_RETRY_LONG) {
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Setting WIPHY_PARAM_RETRY_LONG %d\n",
+ wiphy->retry_long);
+ cfg_param_val.flag |= WILC_CFG_PARAM_RETRY_LONG;
+ cfg_param_val.long_retry_limit = wiphy->retry_long;
+ }
+ if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
+ if (wiphy->frag_threshold > 255 &&
+ wiphy->frag_threshold < 7937) {
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Setting WIPHY_PARAM_FRAG_THRESHOLD %d\n",
+ wiphy->frag_threshold);
+ cfg_param_val.flag |= WILC_CFG_PARAM_RETRY_LONG;
+ cfg_param_val.frag_threshold = wiphy->frag_threshold;
+ } else {
+ PRINT_ER(vif->ndev,
+ "Fragmentation threshold out of range\n");
+ return -EINVAL;
+ }
+ }
+
+ if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
+ if (wiphy->rts_threshold > 255) {
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Setting WIPHY_PARAM_RTS_THRESHOLD %d\n",
+ wiphy->rts_threshold);
+ cfg_param_val.flag |= WILC_CFG_PARAM_RTS_THRESHOLD;
+ cfg_param_val.rts_threshold = wiphy->rts_threshold;
+ } else {
+ PRINT_ER(vif->ndev, "RTS threshold out of range\n");
+ return -EINVAL;
+ }
+ }
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Setting CFG params in the host interface\n");
+ ret = wilc_hif_set_cfg(vif, &cfg_param_val);
+ if (ret)
+ PRINT_ER(priv->dev, "Error in setting WIPHY PARAMS\n");
+
+ return ret;
+}
+
+static int set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa)
+{
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(priv->dev);
+ u32 i;
+ int ret = 0;
+ u8 flag = 0;
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "Setting PMKSA\n");
+
+ for (i = 0; i < priv->pmkid_list.numpmkid; i++) {
+ if (!memcmp(pmksa->bssid, priv->pmkid_list.pmkidlist[i].bssid,
+ ETH_ALEN)) {
+ flag = PMKID_FOUND;
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "PMKID already exists\n");
+ break;
+ }
+ }
+ if (i < WILC_MAX_NUM_PMKIDS) {
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Setting PMKID in private structure\n");
+ memcpy(priv->pmkid_list.pmkidlist[i].bssid, pmksa->bssid,
+ ETH_ALEN);
+ memcpy(priv->pmkid_list.pmkidlist[i].pmkid, pmksa->pmkid,
+ WLAN_PMKID_LEN);
+ if (!(flag == PMKID_FOUND))
+ priv->pmkid_list.numpmkid++;
+ } else {
+ PRINT_ER(netdev, "Invalid PMKID index\n");
+ ret = -EINVAL;
+ }
+
+ if (!ret) {
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Setting pmkid in the host interface\n");
+ ret = wilc_set_pmkid_info(vif, &priv->pmkid_list);
+ }
+ return ret;
+}
+
+static int del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa)
+{
+ u32 i;
+ int ret = 0;
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+
+ PRINT_INFO(netdev, CFG80211_DBG, "Deleting PMKSA keys\n");
+
+ for (i = 0; i < priv->pmkid_list.numpmkid; i++) {
+ if (!memcmp(pmksa->bssid, priv->pmkid_list.pmkidlist[i].bssid,
+ ETH_ALEN)) {
+ PRINT_INFO(netdev, CFG80211_DBG,
+ "Reseting PMKID values\n");
+ memset(&priv->pmkid_list.pmkidlist[i], 0,
+ sizeof(struct wilc_pmkid));
+ break;
+ }
+ }
+
+ if (i < priv->pmkid_list.numpmkid && priv->pmkid_list.numpmkid > 0) {
+ for (; i < (priv->pmkid_list.numpmkid - 1); i++) {
+ memcpy(priv->pmkid_list.pmkidlist[i].bssid,
+ priv->pmkid_list.pmkidlist[i + 1].bssid,
+ ETH_ALEN);
+ memcpy(priv->pmkid_list.pmkidlist[i].pmkid,
+ priv->pmkid_list.pmkidlist[i + 1].pmkid,
+ WLAN_PMKID_LEN);
+ }
+ priv->pmkid_list.numpmkid--;
+ } else {
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
+{
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+
+ PRINT_INFO(netdev, CFG80211_DBG, "Flushing PMKID key values\n");
+ memset(&priv->pmkid_list, 0, sizeof(struct wilc_pmkid_attr));
+
+ return 0;
+}
+
+static inline void wilc_wfi_cfg_parse_ch_attr(struct wilc_vif *vif, u8 *buf,
+ u8 ch_list_attr_idx,
+ u8 op_ch_attr_idx, u8 sta_ch)
+{
+ int i = 0;
+ int j = 0;
+
+ if (ch_list_attr_idx) {
+ u8 limit = ch_list_attr_idx + 3 + buf[ch_list_attr_idx + 1];
+
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Modify channel list attribute [%d]\n",
+ sta_ch);
+ for (i = ch_list_attr_idx + 3; i < limit; i++) {
+ if (buf[i] == 0x51) {
+ for (j = i + 2; j < ((i + 2) + buf[i + 1]); j++)
+ buf[j] = sta_ch;
+ break;
+ }
+ }
+ }
+
+ if (op_ch_attr_idx) {
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Modify operating channel attribute %d\n",
+ sta_ch);
+ buf[op_ch_attr_idx + 6] = 0x51;
+ buf[op_ch_attr_idx + 7] = sta_ch;
+ }
+}
+
+static void wilc_wfi_cfg_parse_rx_action(struct wilc_vif *vif, u8 *buf,
+ u32 len, u8 sta_ch, bool p2p_mode)
+{
+ u32 index = 0;
+ u8 op_channel_attr_index = 0;
+ u8 channel_list_attr_index = 0;
+
+ while (index < len) {
+ if (buf[index] == GO_INTENT_ATTR_ID) {
+ if (!p2p_mode)
+ buf[index + 3] = (buf[index + 3] & 0x01) |
+ (0x0f << 1);
+ else
+ buf[index + 3] = (buf[index + 3] & 0x01) |
+ (0x00 << 1);
+ }
+ if (buf[index] == CHANLIST_ATTR_ID)
+ channel_list_attr_index = index;
+ else if (buf[index] == OPERCHAN_ATTR_ID)
+ op_channel_attr_index = index;
+ index += buf[index + 1] + 3;
+ }
+ if (sta_ch != WILC_INVALID_CHANNEL)
+ wilc_wfi_cfg_parse_ch_attr(vif, buf, channel_list_attr_index,
+ op_channel_attr_index, sta_ch);
+}
+
+static void wilc_wfi_cfg_parse_tx_action(struct wilc_vif *vif, u8 *buf,
+ u32 len, bool oper_ch, u8 sta_ch,
+ u8 p2p_mode)
+{
+ u32 index = 0;
+ u8 op_channel_attr_index = 0;
+ u8 channel_list_attr_index = 0;
+
+ while (index < len) {
+ if (buf[index] == GO_INTENT_ATTR_ID) {
+ if (!p2p_mode)
+ buf[index + 3] = (buf[index + 3] & 0x01) |
+ (0x00 << 1);
+ else
+ buf[index + 3] = (buf[index + 3] & 0x01) |
+ (0x0f << 1);
+ break;
+ }
+
+ if (buf[index] == CHANLIST_ATTR_ID)
+ channel_list_attr_index = index;
+ else if (buf[index] == OPERCHAN_ATTR_ID)
+ op_channel_attr_index = index;
+ index += buf[index + 1] + 3;
+ }
+ if (sta_ch != WILC_INVALID_CHANNEL && oper_ch)
+ wilc_wfi_cfg_parse_ch_attr(vif, buf, channel_list_attr_index,
+ op_channel_attr_index, sta_ch);
+}
+
+static void wilc_wfi_cfg_parse_rx_vendor_spec(struct wilc_priv *priv, u8 *buff,
+ u32 size)
+{
+ int i;
+ u8 subtype;
+ struct wilc_vif *vif = netdev_priv(priv->dev);
+
+ subtype = buff[P2P_PUB_ACTION_SUBTYPE];
+ if ((subtype == GO_NEG_REQ || subtype == GO_NEG_RSP) &&
+ !priv->p2p.is_wilc_ie) {
+ for (i = P2P_PUB_ACTION_SUBTYPE; i < size; i++) {
+ if (!memcmp(p2p_vendor_spec, &buff[i], 6)) {
+ priv->p2p.recv_random = buff[i + 6];
+ priv->p2p.is_wilc_ie = true;
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "WILC Vendor specific IE:%02x\n",
+ priv->p2p.recv_random);
+ break;
+ }
+ }
+ }
+
+ if (priv->p2p.local_random <= priv->p2p.recv_random) {
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "PEER WILL BE GO LocaRand=%02x RecvRand %02x\n",
+ priv->p2p.local_random, priv->p2p.recv_random);
+ return;
+ }
+
+ if (subtype == GO_NEG_REQ || subtype == GO_NEG_RSP ||
+ subtype == P2P_INV_REQ || subtype == P2P_INV_RSP) {
+ for (i = P2P_PUB_ACTION_SUBTYPE + 2; i < size; i++) {
+ if (buff[i] == P2PELEM_ATTR_ID &&
+ !(memcmp(p2p_oui, &buff[i + 2], 4))) {
+ bool p2p_mode = vif->attr_sysfs.p2p_mode;
+
+ wilc_wfi_cfg_parse_rx_action(vif, &buff[i + 6],
+ size - (i + 6),
+ vif->wilc->sta_ch,
+ p2p_mode);
+ break;
+ }
+ }
+ }
+}
+
+void wilc_wfi_p2p_rx(struct net_device *dev, u8 *buff, u32 size)
+{
+ struct wilc_priv *priv = wiphy_priv(dev->ieee80211_ptr->wiphy);
+ struct host_if_drv *wfi_drv = priv->hif_drv;
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wl = vif->wilc;
+ u32 header, pkt_offset;
+ s32 freq;
+ __le16 fc;
+
+ memcpy(&header, (buff - HOST_HDR_OFFSET), HOST_HDR_OFFSET);
+
+ pkt_offset = GET_PKT_OFFSET(header);
+
+ fc = ((struct ieee80211_hdr *)buff)->frame_control;
+ if (pkt_offset & IS_MANAGMEMENT_CALLBACK) {
+ bool ack = false;
+
+ if (ieee80211_is_probe_resp(fc) ||
+ pkt_offset & IS_MGMT_STATUS_SUCCES)
+ ack = true;
+
+ cfg80211_mgmt_tx_status(priv->wdev, priv->tx_cookie, buff, size,
+ ack, GFP_KERNEL);
+ return;
+ }
+
+ PRINT_D(vif->ndev, GENERIC_DBG, "Rx Frame Type:%x\n", fc);
+
+#if KERNEL_VERSION(4, 7, 0) <= LINUX_VERSION_CODE
+ freq = ieee80211_channel_to_frequency(wl->op_ch, NL80211_BAND_2GHZ);
+ #else
+ freq = ieee80211_channel_to_frequency(wl->op_ch, IEEE80211_BAND_2GHZ);
+ #endif
+ if (!ieee80211_is_action(fc)) {
+ cfg80211_rx_mgmt(priv->wdev, freq, 0, buff, size, 0);
+ return;
+ }
+
+ PRINT_D(vif->ndev, GENERIC_DBG,
+ "Rx Action Frame Type: %x %x\n",
+ buff[ACTION_SUBTYPE_ID],
+ buff[P2P_PUB_ACTION_SUBTYPE]);
+ if (priv->cfg_scanning &&
+ time_after_eq(jiffies, (unsigned long)wfi_drv->p2p_timeout)) {
+ PRINT_WRN(dev, GENERIC_DBG, "Receiving action wrong ch\n");
+ return;
+ }
+ if (buff[ACTION_CAT_ID] == PUB_ACTION_ATTR_ID) {
+ u8 subtype = buff[P2P_PUB_ACTION_SUBTYPE];
+
+ switch (buff[ACTION_SUBTYPE_ID]) {
+ case GAS_INITIAL_REQ:
+ PRINT_D(vif->ndev, GENERIC_DBG,
+ "GAS INITIAL REQ %x\n",
+ buff[ACTION_SUBTYPE_ID]);
+ break;
+
+ case GAS_INITIAL_RSP:
+ PRINT_D(vif->ndev, GENERIC_DBG,
+ "GAS INITIAL RSP %x\n",
+ buff[ACTION_SUBTYPE_ID]);
+ break;
+
+ case PUBLIC_ACT_VENDORSPEC:
+ if (!memcmp(p2p_oui, &buff[ACTION_SUBTYPE_ID + 1], 4))
+ wilc_wfi_cfg_parse_rx_vendor_spec(priv, buff,
+ size);
+
+ if ((subtype == GO_NEG_REQ || subtype == GO_NEG_RSP) &&
+ priv->p2p.is_wilc_ie)
+ size -= 7;
+
+ break;
+
+ default:
+ PRINT_WRN(dev, GENERIC_DBG,
+ "Not handled action frame type:%x\n",
+ buff[ACTION_SUBTYPE_ID]);
+ break;
+ }
+ }
+
+ cfg80211_rx_mgmt(priv->wdev, freq, 0, buff, size, 0);
+}
+
+static void wilc_wfi_mgmt_tx_complete(void *priv, int status)
+{
+ struct wilc_p2p_mgmt_data *pv_data = priv;
+
+ kfree(pv_data->buff);
+ kfree(pv_data);
+}
+
+static void wilc_wfi_remain_on_channel_expired(void *data, u64 cookie)
+{
+ struct wilc_priv *priv = data;
+ struct wilc_wfi_p2p_listen_params *params = &priv->remain_on_ch_params;
+
+ if (cookie != priv->remain_on_ch_params.listen_cookie) {
+ PRINT_INFO(priv->dev, GENERIC_DBG,
+ "Received cookies didn't match received[%llu] Expected[%llu]\n",
+ cookie, priv->remain_on_ch_params.listen_cookie);
+ return;
+ }
+
+ priv->p2p_listen_state = false;
+
+ cfg80211_remain_on_channel_expired(priv->wdev, cookie,
+ params->listen_ch, GFP_KERNEL);
+}
+
+static int remain_on_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct ieee80211_channel *chan,
+ unsigned int duration, u64 *cookie)
+{
+ int ret = 0;
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(priv->dev);
+ u64 id;
+
+ PRINT_INFO(vif->ndev, GENERIC_DBG, "Remaining on channel [%d]\n",
+ chan->hw_value);
+
+ if (wdev->iftype == NL80211_IFTYPE_AP) {
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Required while in AP mode\n");
+ return ret;
+ }
+
+ id = ++priv->inc_roc_cookie;
+ if (id == 0)
+ id = ++priv->inc_roc_cookie;
+
+ ret = wilc_remain_on_channel(vif, id, duration, chan->hw_value,
+ wilc_wfi_remain_on_channel_expired,
+ (void *)priv);
+ if (ret)
+ return ret;
+
+ vif->wilc->op_ch = chan->hw_value;
+ priv->remain_on_ch_params.listen_ch = chan;
+ priv->remain_on_ch_params.listen_cookie = id;
+ *cookie = id;
+ priv->remain_on_ch_params.listen_duration = duration;
+ priv->p2p_listen_state = true;
+
+ cfg80211_ready_on_channel(wdev, *cookie, chan, duration, GFP_KERNEL);
+
+#if KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE
+ vif->hif_drv->remain_on_ch_timer.data = (unsigned long)vif->hif_drv;
+#endif
+ mod_timer(&vif->hif_drv->remain_on_ch_timer,
+ jiffies + msecs_to_jiffies(duration));
+
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Remaining on duration [%d] [%llu]\n",
+ duration, priv->remain_on_ch_params.listen_cookie);
+
+ return ret;
+}
+
+static int cancel_remain_on_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ u64 cookie)
+{
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(priv->dev);
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "cookie received[%llu] expected[%llu]\n",
+ cookie, priv->remain_on_ch_params.listen_cookie);
+ if (cookie != priv->remain_on_ch_params.listen_cookie)
+ return -ENOENT;
+
+ return wilc_listen_state_expired(vif, cookie);
+}
+
+#if KERNEL_VERSION(3, 14, 0) <= LINUX_VERSION_CODE
+static void wilc_wfi_cfg_tx_vendor_spec(struct wilc_priv *priv,
+ struct wilc_p2p_mgmt_data *mgmt_tx,
+ struct cfg80211_mgmt_tx_params *params,
+ u8 iftype, u32 buf_len)
+#else
+static void wilc_wfi_cfg_tx_vendor_spec(struct wilc_priv *priv,
+ struct wilc_p2p_mgmt_data *mgmt_tx,
+ const u8 *buf, size_t len,
+ u8 iftype, u32 buf_len)
+#endif
+{
+#if KERNEL_VERSION(3, 14, 0) <= LINUX_VERSION_CODE
+ const u8 *buf = params->buf;
+ size_t len = params->len;
+#endif
+ struct wilc_vif *vif = netdev_priv(priv->dev);
+ u32 i;
+ u8 subtype = buf[P2P_PUB_ACTION_SUBTYPE];
+
+ if (subtype == GO_NEG_REQ || subtype == GO_NEG_RSP) {
+ if (priv->p2p.local_random == 1 &&
+ priv->p2p.recv_random < priv->p2p.local_random) {
+ get_random_bytes(&priv->p2p.local_random, 1);
+ priv->p2p.local_random++;
+ }
+ }
+
+ if (priv->p2p.local_random <= priv->p2p.recv_random ||
+ !(subtype == GO_NEG_REQ || subtype == GO_NEG_RSP ||
+ subtype == P2P_INV_REQ || subtype == P2P_INV_RSP))
+ return;
+
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "LOCAL WILL BE GO LocaRand=%02x RecvRand %02x\n",
+ priv->p2p.local_random, priv->p2p.recv_random);
+ for (i = P2P_PUB_ACTION_SUBTYPE + 2; i < len; i++) {
+ if (buf[i] == P2PELEM_ATTR_ID &&
+ !memcmp(p2p_oui, &buf[i + 2], 4)) {
+ bool oper_ch = false;
+ u8 *tx_buff = &mgmt_tx->buff[i + 6];
+
+ if (subtype == P2P_INV_REQ || subtype == P2P_INV_RSP)
+ oper_ch = true;
+
+ wilc_wfi_cfg_parse_tx_action(vif, tx_buff,
+ len - (i + 6), oper_ch,
+ vif->wilc->sta_ch,
+ vif->attr_sysfs.p2p_mode);
+ break;
+ }
+ }
+
+ if (subtype != P2P_INV_REQ && subtype != P2P_INV_RSP) {
+ int vendor_spec_len = sizeof(p2p_vendor_spec);
+
+ memcpy(&mgmt_tx->buff[len], p2p_vendor_spec,
+ vendor_spec_len);
+ mgmt_tx->buff[len + vendor_spec_len] = priv->p2p.local_random;
+ mgmt_tx->size = buf_len;
+ }
+}
+
+#if KERNEL_VERSION(3, 14, 0) <= LINUX_VERSION_CODE
+static int mgmt_tx(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params,
+ u64 *cookie)
+#else
+static int mgmt_tx(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct ieee80211_channel *chan, bool offchan,
+ unsigned int wait, const u8 *buf, size_t len,
+ bool no_cck, bool dont_wait_for_ack, u64 *cookie)
+#endif
+{
+#if KERNEL_VERSION(3, 14, 0) <= LINUX_VERSION_CODE
+ struct ieee80211_channel *chan = params->chan;
+ unsigned int wait = params->wait;
+ const u8 *buf = params->buf;
+ size_t len = params->len;
+#endif
+ const struct ieee80211_mgmt *mgmt;
+ struct wilc_p2p_mgmt_data *mgmt_tx;
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct host_if_drv *wfi_drv = priv->hif_drv;
+ struct wilc_vif *vif = netdev_priv(wdev->netdev);
+ u32 buf_len = len + sizeof(p2p_vendor_spec) +
+ sizeof(priv->p2p.local_random);
+ int ret = 0;
+
+ *cookie = prandom_u32();
+ priv->tx_cookie = *cookie;
+ mgmt = (const struct ieee80211_mgmt *)buf;
+
+ if (!ieee80211_is_mgmt(mgmt->frame_control)) {
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "This function transmits only management frames\n");
+ goto out;
+ }
+
+ mgmt_tx = kmalloc(sizeof(*mgmt_tx), GFP_KERNEL);
+ if (!mgmt_tx) {
+ PRINT_ER(vif->ndev,
+ "Failed to allocate memory for mgmt_tx structure\n");
+ return -ENOMEM;
+ }
+
+ mgmt_tx->buff = kmalloc(buf_len, GFP_KERNEL);
+ if (!mgmt_tx->buff) {
+ ret = -ENOMEM;
+ PRINT_ER(vif->ndev,
+ "Failed to allocate memory for mgmt_tx buff\n");
+ kfree(mgmt_tx);
+ goto out;
+ }
+
+ memcpy(mgmt_tx->buff, buf, len);
+ mgmt_tx->size = len;
+
+ if (ieee80211_is_probe_resp(mgmt->frame_control)) {
+ PRINT_INFO(vif->ndev, GENERIC_DBG, "TX: Probe Response\n");
+ PRINT_INFO(vif->ndev, GENERIC_DBG, "Setting channel: %d\n",
+ chan->hw_value);
+ wilc_set_mac_chnl_num(vif, chan->hw_value);
+ vif->wilc->op_ch = chan->hw_value;
+ goto out_txq_add_pkt;
+ }
+
+ if (!ieee80211_is_action(mgmt->frame_control))
+ goto out_txq_add_pkt;
+
+ PRINT_INFO(vif->ndev, GENERIC_DBG, "ACTION FRAME:%x\n",
+ (u16)mgmt->frame_control);
+ if (buf[ACTION_CAT_ID] == PUB_ACTION_ATTR_ID) {
+ if (buf[ACTION_SUBTYPE_ID] != PUBLIC_ACT_VENDORSPEC ||
+ buf[P2P_PUB_ACTION_SUBTYPE] != GO_NEG_CONF) {
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Setting channel: %d\n",
+ chan->hw_value);
+ wilc_set_mac_chnl_num(vif,
+ chan->hw_value);
+ vif->wilc->op_ch = chan->hw_value;
+ }
+ switch (buf[ACTION_SUBTYPE_ID]) {
+ case GAS_INITIAL_REQ:
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "GAS INITIAL REQ %x\n",
+ buf[ACTION_SUBTYPE_ID]);
+ break;
+
+ case GAS_INITIAL_RSP:
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "GAS INITIAL RSP %x\n",
+ buf[ACTION_SUBTYPE_ID]);
+ break;
+
+ case PUBLIC_ACT_VENDORSPEC:
+ if (!memcmp(p2p_oui, &buf[ACTION_SUBTYPE_ID + 1], 4))
+ #if KERNEL_VERSION(3, 14, 0) <= LINUX_VERSION_CODE
+ wilc_wfi_cfg_tx_vendor_spec(priv, mgmt_tx,
+ params,
+ vif->iftype,
+ buf_len);
+ #else
+ wilc_wfi_cfg_tx_vendor_spec(priv, mgmt_tx, buf,
+ len, vif->iftype,
+ buf_len);
+ #endif
+ else
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Not a P2P public action frame\n");
+
+ break;
+
+ default:
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Not handled action frame type:%x\n",
+ buf[ACTION_SUBTYPE_ID]);
+ break;
+ }
+ }
+
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "TX: ACTION FRAME Type:%x : Chan:%d\n",
+ buf[ACTION_SUBTYPE_ID], chan->hw_value);
+ wfi_drv->p2p_timeout = (jiffies + msecs_to_jiffies(wait));
+
+out_txq_add_pkt:
+
+ txq_add_mgmt_pkt(wdev->netdev, mgmt_tx, mgmt_tx->buff, mgmt_tx->size,
+ wilc_wfi_mgmt_tx_complete);
+
+out:
+
+ return ret;
+}
+
+static int mgmt_tx_cancel_wait(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ u64 cookie)
+{
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct host_if_drv *wfi_drv = priv->hif_drv;
+
+ wfi_drv->p2p_timeout = jiffies;
+
+ if (!priv->p2p_listen_state) {
+ struct wilc_wfi_p2p_listen_params *params;
+
+ params = &priv->remain_on_ch_params;
+
+ cfg80211_remain_on_channel_expired(priv->wdev,
+ params->listen_cookie,
+ params->listen_ch,
+ GFP_KERNEL);
+ }
+
+ return 0;
+}
+
+void wilc_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev,
+ u16 frame_type, bool reg)
+{
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(priv->wdev->netdev);
+ struct wilc *wl = vif->wilc;
+
+ if (!frame_type)
+ return;
+
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Frame registering Frame Type: %x: Boolean: %d\n",
+ frame_type, reg);
+ switch (frame_type) {
+ case IEEE80211_STYPE_PROBE_REQ:
+ vif->frame_reg[0].type = frame_type;
+ vif->frame_reg[0].reg = reg;
+ break;
+
+ case IEEE80211_STYPE_ACTION:
+ vif->frame_reg[1].type = frame_type;
+ vif->frame_reg[1].reg = reg;
+ break;
+
+ default:
+ break;
+ }
+
+ if (!wl->initialized) {
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Return since mac is closed\n");
+ return;
+ }
+ wilc_frame_register(vif, frame_type, reg);
+}
+
+static int set_cqm_rssi_config(struct wiphy *wiphy, struct net_device *dev,
+ s32 rssi_thold, u32 rssi_hyst)
+{
+ PRINT_INFO(dev, CFG80211_DBG, "Setting CQM RSSi Function\n");
+ return 0;
+}
+
+static int dump_station(struct wiphy *wiphy, struct net_device *dev,
+ int idx, u8 *mac, struct station_info *sinfo)
+{
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(priv->dev);
+ int ret;
+
+ if (idx != 0)
+ return -ENOENT;
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "Dumping station information\n");
+
+ ret = wilc_get_rssi(vif, &sinfo->signal);
+ if (ret)
+ return ret;
+
+#if KERNEL_VERSION(3, 19, 0) <= LINUX_VERSION_CODE
+ sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
+#else
+ sinfo->filled |= STATION_INFO_SIGNAL;
+#endif
+
+ return 0;
+}
+
+static int set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
+ bool enabled, int timeout)
+{
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(priv->dev);
+
+ if (!priv->hif_drv) {
+ PRINT_ER(dev, "hif driver is NULL\n");
+ return -EIO;
+ }
+
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+ /* Can't set PS during obtaining IP */
+ if (vif->obtaining_ip == true) {
+ PRINT_ER(dev,
+ "Device obtaining IP, Power Managment will be handled after IP Obtained\n");
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Save the Current state of the PS = %d\n", enabled);
+
+ /* Save the current status of the PS */
+ store_power_save_current_state(vif, enabled);
+
+ return 0;
+ }
+#endif
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ " Power save Enabled= %d , TimeOut = %d\n", enabled,
+ timeout);
+
+ wilc_set_power_mgmt(vif, enabled, timeout);
+
+ return 0;
+}
+
+#if KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE
+static int change_virtual_intf(struct wiphy *wiphy, struct net_device *dev,
+ enum nl80211_iftype type,
+ struct vif_params *params)
+#else
+static int change_virtual_intf(struct wiphy *wiphy, struct net_device *dev,
+ enum nl80211_iftype type, u32 *flags,
+ struct vif_params *params)
+#endif
+{
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wl = vif->wilc;
+ struct net_device *net_device_1 = wilc_get_if_netdev(wl, WILC_P2P_IFC);
+ struct net_device *net_device_2 = wilc_get_if_netdev(wl, WILC_WLAN_IFC);
+ struct wilc_vif *vif_1 = netdev_priv(net_device_1);
+ struct wilc_vif *vif_2 = netdev_priv(net_device_2);
+
+ PRINT_INFO(vif->ndev, HOSTAPD_DBG,
+ "In Change virtual interface function\n");
+ PRINT_INFO(vif->ndev, HOSTAPD_DBG,
+ "Wireless interface name =%s\n", dev->name);
+ priv->p2p.local_random = 0x01;
+ priv->p2p.recv_random = 0x00;
+ priv->p2p.is_wilc_ie = false;
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Changing virtual interface, enable scan\n");
+ handle_pwrsave_for_IP(vif, IP_STATE_DEFAULT);
+#endif
+
+ switch (type) {
+ case NL80211_IFTYPE_STATION:
+ vif->connecting = false;
+ PRINT_INFO(vif->ndev, HOSTAPD_DBG,
+ "Interface type = NL80211_IFTYPE_STATION\n");
+ dev->ieee80211_ptr->iftype = type;
+ priv->wdev->iftype = type;
+ vif->monitor_flag = 0;
+ vif->iftype = WILC_STATION_MODE;
+ wilc_set_wfi_drv_handler(vif, wilc_get_vif_idx(vif),
+ WILC_STATION_MODE, vif->ifc_id);
+ wilc_set_operation_mode(vif, WILC_STATION_MODE);
+
+ memset(priv->assoc_stainfo.sta_associated_bss, 0,
+ WILC_MAX_NUM_STA * ETH_ALEN);
+
+ wilc_set_power_mgmt(vif_1, 1, 0);
+ wilc_set_power_mgmt(vif_2, 1, 0);
+ break;
+
+ case NL80211_IFTYPE_P2P_CLIENT:
+ vif->connecting = false;
+ PRINT_INFO(vif->ndev, HOSTAPD_DBG,
+ "Interface type = NL80211_IFTYPE_P2P_CLIENT\n");
+ dev->ieee80211_ptr->iftype = type;
+ priv->wdev->iftype = type;
+ vif->monitor_flag = 0;
+ vif->iftype = WILC_CLIENT_MODE;
+ wilc_set_wfi_drv_handler(vif, wilc_get_vif_idx(vif),
+ WILC_STATION_MODE, vif->ifc_id);
+ wilc_set_operation_mode(vif, WILC_STATION_MODE);
+
+ wilc_set_power_mgmt(vif_1, 0, 0);
+ wilc_set_power_mgmt(vif_2, 0, 0);
+ break;
+
+ case NL80211_IFTYPE_AP:
+ PRINT_INFO(vif->ndev, HOSTAPD_DBG,
+ "Interface type = NL80211_IFTYPE_AP\n");
+ dev->ieee80211_ptr->iftype = type;
+ priv->wdev->iftype = type;
+ vif->iftype = WILC_AP_MODE;
+ if (wl->initialized) {
+ wilc_set_wfi_drv_handler(vif, wilc_get_vif_idx(vif),
+ WILC_AP_MODE, vif->ifc_id);
+ wilc_set_operation_mode(vif, WILC_AP_MODE);
+ wilc_set_power_mgmt(vif_1, 0, 0);
+ wilc_set_power_mgmt(vif_2, 0, 0);
+ }
+ break;
+
+ case NL80211_IFTYPE_P2P_GO:
+ PRINT_INFO(vif->ndev, HOSTAPD_DBG,
+ "Interface type = NL80211_IFTYPE_GO\n");
+ PRINT_INFO(vif->ndev, GENERIC_DBG, "start duringIP timer\n");
+
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+ handle_pwrsave_for_IP(vif, IP_STATE_GO_ASSIGNING);
+#endif
+ dev->ieee80211_ptr->iftype = type;
+ priv->wdev->iftype = type;
+ vif->iftype = WILC_GO_MODE;
+ wilc_set_wfi_drv_handler(vif, wilc_get_vif_idx(vif),
+ WILC_AP_MODE, vif->ifc_id);
+ wilc_set_operation_mode(vif, WILC_AP_MODE);
+ wilc_set_power_mgmt(vif_1, 0, 0);
+ wilc_set_power_mgmt(vif_2, 0, 0);
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ PRINT_INFO(vif->ndev, HOSTAPD_DBG,
+ "Interface type = NL80211_IFTYPE_MONITOR\n");
+ dev->ieee80211_ptr->iftype = type;
+ dev->type = ARPHRD_IEEE80211_RADIOTAP;
+ priv->wdev->iftype = type;
+ vif->iftype = WILC_MONITOR_MODE;
+ if (wl->initialized) {
+ wilc_set_wfi_drv_handler(vif, wilc_get_vif_idx(vif),
+ WILC_MONITOR_MODE,
+ vif->ifc_id);
+
+ wilc_set_operation_mode(vif, WILC_MONITOR_MODE);
+ wilc_set_power_mgmt(vif_1, 0, 0);
+ wilc_set_power_mgmt(vif_2, 0, 0);
+ }
+ break;
+
+ default:
+ PRINT_ER(dev, "Unknown interface type= %d\n", type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int start_ap(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_ap_settings *settings)
+{
+ struct wilc_vif *vif = netdev_priv(dev);
+ int ret;
+
+ PRINT_INFO(vif->ndev, HOSTAPD_DBG, "Starting ap\n");
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Interval= %d\n DTIM period= %d\n Head length= %d Tail length= %d\n",
+ settings->beacon_interval, settings->dtim_period,
+ settings->beacon.head_len, settings->beacon.tail_len);
+ ret = set_channel(wiphy, &settings->chandef);
+
+ if (ret != 0)
+ PRINT_ER(dev, "Error in setting channel\n");
+
+ wilc_wlan_set_bssid(dev, dev->dev_addr, WILC_AP_MODE);
+ wilc_set_power_mgmt(vif, 0, 0);
+
+ return wilc_add_beacon(vif, settings->beacon_interval,
+ settings->dtim_period, &settings->beacon);
+}
+
+static int change_beacon(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_beacon_data *beacon)
+{
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(priv->dev);
+
+ PRINT_INFO(vif->ndev, HOSTAPD_DBG, "Setting beacon\n");
+
+ return wilc_add_beacon(vif, 0, 0, beacon);
+}
+
+static int stop_ap(struct wiphy *wiphy, struct net_device *dev)
+{
+ int ret;
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(priv->dev);
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "Deleting beacon\n");
+
+ wilc_wlan_set_bssid(dev, NULL, WILC_AP_MODE);
+
+ ret = wilc_del_beacon(vif);
+
+ if (ret)
+ PRINT_ER(dev, "Host delete beacon fail\n");
+
+ return ret;
+}
+
+#if KERNEL_VERSION(3, 16, 0) <= LINUX_VERSION_CODE
+static int add_station(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *mac, struct station_parameters *params)
+#else
+static int add_station(struct wiphy *wiphy, struct net_device *dev,
+ u8 *mac, struct station_parameters *params)
+#endif
+{
+ int ret = 0;
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(dev);
+ u8 *assoc_bss = priv->assoc_stainfo.sta_associated_bss[params->aid];
+
+ if (vif->iftype == WILC_AP_MODE || vif->iftype == WILC_GO_MODE) {
+ memcpy(assoc_bss, mac, ETH_ALEN);
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Adding station parameters %d\n", params->aid);
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "BSSID = %x%x%x%x%x%x\n",
+ assoc_bss[0], assoc_bss[1], assoc_bss[2],
+ assoc_bss[3], assoc_bss[4], assoc_bss[5]);
+ PRINT_INFO(vif->ndev, HOSTAPD_DBG, "ASSOC ID = %d\n",
+ params->aid);
+ PRINT_INFO(vif->ndev, HOSTAPD_DBG,
+ "Number of supported rates = %d\n",
+ params->supported_rates_len);
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "IS HT supported = %d\n",
+ (!params->ht_capa) ? false : true);
+
+ if (params->ht_capa) {
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Capability Info = %d\n",
+ params->ht_capa->cap_info);
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "AMPDU Params = %d\n",
+ params->ht_capa->ampdu_params_info);
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "HT Extended params= %d\n",
+ params->ht_capa->extended_ht_cap_info);
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Tx Beamforming Cap= %d\n",
+ params->ht_capa->tx_BF_cap_info);
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Antenna selection info = %d\n",
+ params->ht_capa->antenna_selection_info);
+ }
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "Flag Mask = %d\n",
+ params->sta_flags_mask);
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "Flag Set = %d\n",
+ params->sta_flags_set);
+ ret = wilc_add_station(vif, (const u8 *)mac, params);
+ if (ret)
+ PRINT_ER(dev, "Host add station fail\n");
+ }
+
+ return ret;
+}
+
+#if KERNEL_VERSION(3, 19, 0) <= LINUX_VERSION_CODE
+static int del_station(struct wiphy *wiphy, struct net_device *dev,
+ struct station_del_parameters *params)
+#elif KERNEL_VERSION(3, 16, 0) <= LINUX_VERSION_CODE
+static int del_station(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *mac)
+#else
+static int del_station(struct wiphy *wiphy, struct net_device *dev,
+ u8 *mac)
+#endif
+{
+#if KERNEL_VERSION(3, 19, 0) <= LINUX_VERSION_CODE
+ const u8 *mac = params->mac;
+#endif
+ int ret = 0;
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct sta_info *info;
+
+ if (!(vif->iftype == WILC_AP_MODE || vif->iftype == WILC_GO_MODE))
+ return ret;
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "Deleting station\n");
+
+ info = &priv->assoc_stainfo;
+
+ if (!mac) {
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "All associated stations\n");
+ ret = wilc_del_allstation(vif, info->sta_associated_bss);
+ } else {
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "With mac address: %x%x%x%x%x%x\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+ }
+
+ ret = wilc_del_station(vif, mac);
+ if (ret)
+ PRINT_ER(dev, "Host delete station fail\n");
+
+ return ret;
+}
+
+#if KERNEL_VERSION(3, 16, 0) <= LINUX_VERSION_CODE
+static int change_station(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *mac, struct station_parameters *params)
+#else
+static int change_station(struct wiphy *wiphy, struct net_device *dev,
+ u8 *mac, struct station_parameters *params)
+#endif
+{
+ int ret = 0;
+ struct wilc_vif *vif = netdev_priv(dev);
+
+ PRINT_D(vif->ndev, CFG80211_DBG, "Change station paramters\n");
+
+ if (vif->iftype == WILC_AP_MODE || vif->iftype == WILC_GO_MODE) {
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "BSSID = %x%x%x%x%x%x\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "ASSOC ID = %d\n",
+ params->aid);
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Number of supported rates = %d\n",
+ params->supported_rates_len);
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "IS HT supported = %d\n",
+ (!params->ht_capa) ? false : true);
+ if (params->ht_capa) {
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Capability Info = %d\n",
+ params->ht_capa->cap_info);
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "AMPDU Params = %d\n",
+ params->ht_capa->ampdu_params_info);
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "HT Extended params= %d\n",
+ params->ht_capa->extended_ht_cap_info);
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Tx Beamforming Cap= %d\n",
+ params->ht_capa->tx_BF_cap_info);
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Antenna selection info = %d\n",
+ params->ht_capa->antenna_selection_info);
+ }
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "Flag Mask = %d\n",
+ params->sta_flags_mask);
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "Flag Set = %d\n",
+ params->sta_flags_set);
+ ret = wilc_edit_station(vif, (const u8 *)mac, params);
+ if (ret)
+ PRINT_ER(dev, "Host edit station fail\n");
+ }
+ return ret;
+}
+
+#if KERNEL_VERSION(4, 12, 0) <= LINUX_VERSION_CODE
+static struct wireless_dev *add_virtual_intf(struct wiphy *wiphy,
+ const char *name,
+ unsigned char name_assign_type,
+ enum nl80211_iftype type,
+ struct vif_params *params)
+#elif KERNEL_VERSION(4, 1, 0) <= LINUX_VERSION_CODE
+static struct wireless_dev *add_virtual_intf(struct wiphy *wiphy,
+ const char *name,
+ unsigned char name_assign_type,
+ enum nl80211_iftype type,
+ u32 *flags,
+ struct vif_params *params)
+#elif KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE
+static struct wireless_dev *add_virtual_intf(struct wiphy *wiphy,
+ const char *name,
+ enum nl80211_iftype type,
+ u32 *flags,
+ struct vif_params *params)
+#else
+static struct wireless_dev *add_virtual_intf(struct wiphy *wiphy,
+ char *name,
+ enum nl80211_iftype type,
+ u32 *flags,
+ struct vif_params *params)
+#endif
+{
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(priv->wdev->netdev);
+ struct net_device *new_ifc;
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "Adding monitor interface[%p]\n",
+ priv->wdev->netdev);
+
+ if (type == NL80211_IFTYPE_MONITOR) {
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Initializing mon ifc virtual device driver\n");
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Adding monitor interface[%p]\n", vif->ndev);
+ new_ifc = wilc_wfi_init_mon_interface(vif->wilc, name,
+ vif->ndev);
+ if (new_ifc) {
+ PRINT_INFO(vif->ndev, CFG80211_DBG,
+ "Setting monitor flag in private structure\n");
+ vif = netdev_priv(priv->wdev->netdev);
+ vif->monitor_flag = 1;
+ } else {
+ PRINT_ER(vif->ndev,
+ "Error in initializing monitor interface\n");
+ }
+ }
+ return priv->wdev;
+}
+
+static int del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+
+ PRINT_INFO(priv->dev, HOSTAPD_DBG, "Deleting virtual interface\n");
+ return 0;
+}
+
+static int wilc_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow)
+{
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+
+ if (!wow)
+ PRINT_INFO(priv->dev, GENERIC_DBG,
+ "No wake up triggers defined\n");
+ else if (wow->any == 0)
+ PRINT_INFO(priv->dev, GENERIC_DBG,
+ "The only supported wake up trigger (any) is not set\n");
+
+ return 0;
+}
+
+static int wilc_resume(struct wiphy *wiphy)
+{
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(priv->dev);
+
+ PRINT_INFO(vif->ndev, GENERIC_DBG, "cfg resume\n");
+ return 0;
+}
+
+static void wilc_set_wakeup(struct wiphy *wiphy, bool enabled)
+{
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(priv->dev);
+
+ PRINT_INFO(vif->ndev, GENERIC_DBG, "cfg set wake up = %d\n", enabled);
+ wilc_set_wowlan_trigger(vif, (u8)enabled);
+}
+
+static int set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
+ enum nl80211_tx_power_setting type, int mbm)
+{
+ int ret;
+ s32 tx_power = MBM_TO_DBM(mbm);
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(priv->dev);
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "Setting tx power %d\n", tx_power);
+ if (tx_power < 0)
+ tx_power = 0;
+ else if (tx_power > 18)
+ tx_power = 18;
+ ret = wilc_set_tx_power(vif, tx_power);
+ if (ret)
+ PRINT_ER(vif->ndev, "Failed to set tx power\n");
+
+ return ret;
+}
+
+static int get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
+ int *dbm)
+{
+ int ret;
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(priv->dev);
+ struct wilc *wl = vif->wilc;
+
+ /* If firmware is not started, return. */
+ if (!wl->initialized)
+ return -EIO;
+ *dbm = 0;
+ ret = wilc_get_tx_power(vif, (u8 *)dbm);
+ if (ret)
+ PRINT_ER(vif->ndev, "Failed to get tx power\n");
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "Got tx power %d\n", *dbm);
+
+ return ret;
+}
+
+static int set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant)
+{
+ int ret;
+ struct wilc_priv *priv = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(priv->dev);
+
+ PRINT_INFO(vif->ndev, CFG80211_DBG, "Select antenna mode %d\n", tx_ant);
+ if (!tx_ant || !rx_ant)
+ return -EINVAL;
+
+ ret = wilc_set_antenna(vif, (u8)(tx_ant-1));
+ if (ret)
+ PRINT_ER(vif->ndev, "Failed to set tx antenna\n");
+
+ return ret;
+}
+
+static const struct cfg80211_ops wilc_cfg80211_ops = {
+ .set_monitor_channel = set_channel,
+ .scan = scan,
+ .connect = connect,
+ .disconnect = disconnect,
+ .add_key = add_key,
+ .del_key = del_key,
+ .get_key = get_key,
+ .set_default_key = set_default_key,
+ .add_virtual_intf = add_virtual_intf,
+ .del_virtual_intf = del_virtual_intf,
+ .change_virtual_intf = change_virtual_intf,
+
+ .start_ap = start_ap,
+ .change_beacon = change_beacon,
+ .stop_ap = stop_ap,
+ .add_station = add_station,
+ .del_station = del_station,
+ .change_station = change_station,
+ .get_station = get_station,
+ .dump_station = dump_station,
+ .change_bss = change_bss,
+ .set_wiphy_params = set_wiphy_params,
+
+ .set_pmksa = set_pmksa,
+ .del_pmksa = del_pmksa,
+ .flush_pmksa = flush_pmksa,
+ .remain_on_channel = remain_on_channel,
+ .cancel_remain_on_channel = cancel_remain_on_channel,
+ .mgmt_tx_cancel_wait = mgmt_tx_cancel_wait,
+ .mgmt_tx = mgmt_tx,
+ .mgmt_frame_register = wilc_mgmt_frame_register,
+ .set_power_mgmt = set_power_mgmt,
+ .set_cqm_rssi_config = set_cqm_rssi_config,
+
+ .suspend = wilc_suspend,
+ .resume = wilc_resume,
+ .set_wakeup = wilc_set_wakeup,
+ .set_tx_power = set_tx_power,
+ .get_tx_power = get_tx_power,
+ .set_antenna = set_antenna,
+};
+
+static struct wireless_dev *wilc_wfi_cfg_alloc(struct net_device *net)
+{
+ struct wireless_dev *wdev;
+
+ PRINT_INFO(net, CFG80211_DBG, "Allocating wireless device\n");
+ wdev = kzalloc(sizeof(*wdev), GFP_KERNEL);
+ if (!wdev) {
+ PRINT_ER(net, "Cannot allocate wireless device\n");
+ goto out;
+ }
+
+ wdev->wiphy = wiphy_new(&wilc_cfg80211_ops, sizeof(struct wilc_priv));
+ if (!wdev->wiphy) {
+ PRINT_ER(net, "Cannot allocate wiphy\n");
+ goto free_mem;
+ }
+
+ return wdev;
+
+free_mem:
+ kfree(wdev);
+out:
+ return NULL;
+}
+
+struct wireless_dev *wilc_create_wiphy(struct net_device *net,
+ struct device *dev)
+{
+ struct wilc_priv *priv;
+ struct wireless_dev *wdev;
+ int ret;
+
+ PRINT_INFO(net, CFG80211_DBG, "Registering wifi device\n");
+ wdev = wilc_wfi_cfg_alloc(net);
+ if (!wdev) {
+ PRINT_ER(net, "wiphy new allocate failed\n");
+ return NULL;
+ }
+
+ priv = wdev_priv(wdev);
+ priv->wdev = wdev;
+
+ memcpy(priv->bitrates, wilc_bitrates, sizeof(wilc_bitrates));
+ memcpy(priv->channels, wilc_2ghz_channels, sizeof(wilc_2ghz_channels));
+ priv->band.bitrates = priv->bitrates;
+ priv->band.n_bitrates = ARRAY_SIZE(priv->bitrates);
+ priv->band.channels = priv->channels;
+ priv->band.n_channels = ARRAY_SIZE(wilc_2ghz_channels);
+
+ priv->band.ht_cap.ht_supported = 1;
+ priv->band.ht_cap.cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
+ priv->band.ht_cap.mcs.rx_mask[0] = 0xff;
+ priv->band.ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K;
+ priv->band.ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE;
+
+#if KERNEL_VERSION(4, 7, 0) <= LINUX_VERSION_CODE
+ wdev->wiphy->bands[NL80211_BAND_2GHZ] = &priv->band;
+#else
+ wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band;
+#endif
+
+ wdev->wiphy->max_scan_ssids = WILC_MAX_NUM_PROBED_SSID;
+#if KERNEL_VERSION(3, 11, 0) <= LINUX_VERSION_CODE
+ wdev->wiphy->wowlan = &wowlan_support;
+#else
+ wdev->wiphy->wowlan = wowlan_support;
+#endif
+ wdev->wiphy->max_num_pmkids = WILC_MAX_NUM_PMKIDS;
+ PRINT_D(net, CFG80211_DBG, "Max number of PMKIDs = %d\n",
+ wdev->wiphy->max_num_pmkids);
+ wdev->wiphy->max_scan_ie_len = 1000;
+ wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+ memcpy(priv->cipher_suites, wilc_cipher_suites,
+ sizeof(wilc_cipher_suites));
+ wdev->wiphy->cipher_suites = priv->cipher_suites;
+ wdev->wiphy->n_cipher_suites = ARRAY_SIZE(wilc_cipher_suites);
+ wdev->wiphy->available_antennas_tx = 0x3;
+ wdev->wiphy->available_antennas_rx = 0x3;
+ wdev->wiphy->mgmt_stypes = wilc_wfi_cfg80211_mgmt_types;
+
+ wdev->wiphy->max_remain_on_channel_duration = 500;
+ wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_MONITOR) |
+ BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT);
+ wdev->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+ wdev->iftype = NL80211_IFTYPE_STATION;
+
+ PRINT_D(net, CFG80211_DBG,
+ "Max scan ids= %d,Max scan IE len= %d,Signal Type= %d,Interface Modes= %d,Interface Type= %d\n",
+ wdev->wiphy->max_scan_ssids, wdev->wiphy->max_scan_ie_len,
+ wdev->wiphy->signal_type, wdev->wiphy->interface_modes,
+ wdev->iftype);
+
+ set_wiphy_dev(wdev->wiphy, dev);
+
+ ret = wiphy_register(wdev->wiphy);
+ if (ret) {
+ PRINT_ER(net, "Cannot register wiphy device\n");
+ wiphy_free(wdev->wiphy);
+ kfree(wdev);
+ return NULL;
+ }
+
+ priv->dev = net;
+ return wdev;
+}
+
+int wilc_init_host_int(struct net_device *net)
+{
+ int ret;
+ struct wilc_priv *priv = wdev_priv(net->ieee80211_ptr);
+ struct wilc_vif *vif = netdev_priv(priv->dev);
+
+ PRINT_INFO(net, INIT_DBG, "Host[%p][%p]\n", net, net->ieee80211_ptr);
+
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+ #ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+ timer_setup(&vif->during_ip_timer, clear_during_ip, 0);
+ #endif
+ timer_setup(&priv->eap_buff_timer, eap_buff_timeout, 0);
+#else
+ #ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+ setup_timer(&vif->during_ip_timer, clear_during_ip, 0);
+ #endif
+ setup_timer(&priv->eap_buff_timer, eap_buff_timeout, 0);
+#endif
+
+ priv->p2p_listen_state = false;
+
+ mutex_init(&priv->scan_req_lock);
+ ret = wilc_init(net, &priv->hif_drv);
+ if (ret)
+ PRINT_ER(net, "Error while initializing hostinterface\n");
+
+ return ret;
+}
+
+void wilc_deinit_host_int(struct net_device *net)
+{
+ int ret;
+ struct wilc_priv *priv = wdev_priv(net->ieee80211_ptr);
+ struct wilc_vif *vif = netdev_priv(priv->dev);
+
+ priv->p2p_listen_state = false;
+
+ mutex_destroy(&priv->scan_req_lock);
+ ret = wilc_deinit(vif);
+
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+ del_timer_sync(&vif->during_ip_timer);
+#endif
+ del_timer_sync(&priv->eap_buff_timer);
+
+ if (ret)
+ PRINT_ER(net, "Error while deinitializing host interface\n");
+}
+
+void wilc_free_wiphy(struct net_device *net)
+{
+ PRINT_INFO(net, CFG80211_DBG, "Unregistering wiphy\n");
+ if (!net) {
+ PRINT_INFO(net, INIT_DBG, "net_device is NULL\n");
+ return;
+ }
+
+ if (!net->ieee80211_ptr) {
+ PRINT_INFO(net, INIT_DBG, "ieee80211_ptr is NULL\n");
+ return;
+ }
+
+ if (!net->ieee80211_ptr->wiphy) {
+ PRINT_INFO(net, INIT_DBG, "wiphy is NULL\n");
+ return;
+ }
+
+ wiphy_unregister(net->ieee80211_ptr->wiphy);
+
+ PRINT_INFO(net, INIT_DBG, "Freeing wiphy\n");
+ wiphy_free(net->ieee80211_ptr->wiphy);
+ kfree(net->ieee80211_ptr);
+}
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#ifndef NM_WFI_CFGOPERATIONS
+#define NM_WFI_CFGOPERATIONS
+#include "wilc_wfi_netdevice.h"
+
+struct wireless_dev *wilc_create_wiphy(struct net_device *net,
+ struct device *dev);
+void wilc_free_wiphy(struct net_device *net);
+void wilc_deinit_host_int(struct net_device *net);
+int wilc_init_host_int(struct net_device *net);
+void wilc_wfi_monitor_rx(struct net_device *mon_dev, u8 *buff, u32 size);
+void wilc_wfi_deinit_mon_interface(struct wilc *wl);
+struct net_device *wilc_wfi_init_mon_interface(struct wilc *wl,
+ const char *name,
+ struct net_device *real_dev);
+void wilc_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev,
+ u16 frame_type, bool reg);
+void wilc_sysfs_init(struct wilc_vif *vif1, struct wilc_vif *vif2);
+void wilc_sysfs_exit(void);
+#endif
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#ifndef WILC_WFI_NETDEVICE
+#define WILC_WFI_NETDEVICE
+
+#include <linux/tcp.h>
+#include <linux/ieee80211.h>
+#include <net/cfg80211.h>
+#include <net/ieee80211_radiotap.h>
+#include <linux/if_arp.h>
+#include <linux/version.h>
+#if KERNEL_VERSION(3, 13, 0) < LINUX_VERSION_CODE
+#include <linux/gpio/consumer.h>
+#else
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#endif
+
+#include "host_interface.h"
+#include "wilc_wlan.h"
+#include "wilc_wlan_cfg.h"
+
+#define FLOW_CTRL_LOW_THRESHLD 128
+#define FLOW_CTRL_UP_THRESHLD 256
+
+#define WILC_MAX_NUM_PMKIDS 16
+#define PMKID_FOUND 1
+#define NUM_STA_ASSOCIATED 8
+
+#define NUM_REG_FRAME 2
+
+#define TCP_ACK_FILTER_LINK_SPEED_THRESH 54
+#define DEFAULT_LINK_SPEED 72
+
+#define GET_PKT_OFFSET(a) (((a) >> 22) & 0x1ff)
+
+#define ANT_SWTCH_INVALID_GPIO_CTRL 0
+#define ANT_SWTCH_SNGL_GPIO_CTRL 1
+#define ANT_SWTCH_DUAL_GPIO_CTRL 2
+
+struct wilc_wfi_stats {
+ unsigned long rx_packets;
+ unsigned long tx_packets;
+ unsigned long rx_bytes;
+ unsigned long tx_bytes;
+ u64 rx_time;
+ u64 tx_time;
+};
+
+struct wilc_wfi_key {
+ u8 *key;
+ u8 *seq;
+ int key_len;
+ int seq_len;
+ u32 cipher;
+};
+
+struct wilc_wfi_wep_key {
+ u8 *key;
+ u8 key_len;
+ u8 key_idx;
+};
+
+struct sta_info {
+ u8 sta_associated_bss[WILC_MAX_NUM_STA][ETH_ALEN];
+};
+
+/*Parameters needed for host interface for remaining on channel*/
+struct wilc_wfi_p2p_listen_params {
+ struct ieee80211_channel *listen_ch;
+ u32 listen_duration;
+ u64 listen_cookie;
+};
+
+/* Struct to buffer eapol 1/4 frame */
+struct wilc_buffered_eap {
+ unsigned int size;
+ unsigned int pkt_offset;
+ u8 *buff;
+};
+
+struct wilc_p2p_var {
+ u8 local_random;
+ u8 recv_random;
+ bool is_wilc_ie;
+};
+
+static const u32 wilc_cipher_suites[] = {
+ WLAN_CIPHER_SUITE_WEP40,
+ WLAN_CIPHER_SUITE_WEP104,
+ WLAN_CIPHER_SUITE_TKIP,
+ WLAN_CIPHER_SUITE_CCMP,
+ WLAN_CIPHER_SUITE_AES_CMAC
+};
+
+#if KERNEL_VERSION(4, 7, 0) > LINUX_VERSION_CODE
+#define CHAN2G(_channel, _freq, _flags) { \
+ .band = IEEE80211_BAND_2GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = (_channel), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+#else
+#define CHAN2G(_channel, _freq, _flags) { \
+ .band = NL80211_BAND_2GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = (_channel), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+#endif
+
+static const struct ieee80211_channel wilc_2ghz_channels[] = {
+ CHAN2G(1, 2412, 0),
+ CHAN2G(2, 2417, 0),
+ CHAN2G(3, 2422, 0),
+ CHAN2G(4, 2427, 0),
+ CHAN2G(5, 2432, 0),
+ CHAN2G(6, 2437, 0),
+ CHAN2G(7, 2442, 0),
+ CHAN2G(8, 2447, 0),
+ CHAN2G(9, 2452, 0),
+ CHAN2G(10, 2457, 0),
+ CHAN2G(11, 2462, 0),
+ CHAN2G(12, 2467, 0),
+ CHAN2G(13, 2472, 0),
+ CHAN2G(14, 2484, 0)
+};
+
+#define RATETAB_ENT(_rate, _hw_value, _flags) { \
+ .bitrate = (_rate), \
+ .hw_value = (_hw_value), \
+ .flags = (_flags), \
+}
+
+static struct ieee80211_rate wilc_bitrates[] = {
+ RATETAB_ENT(10, 0, 0),
+ RATETAB_ENT(20, 1, 0),
+ RATETAB_ENT(55, 2, 0),
+ RATETAB_ENT(110, 3, 0),
+ RATETAB_ENT(60, 9, 0),
+ RATETAB_ENT(90, 6, 0),
+ RATETAB_ENT(120, 7, 0),
+ RATETAB_ENT(180, 8, 0),
+ RATETAB_ENT(240, 9, 0),
+ RATETAB_ENT(360, 10, 0),
+ RATETAB_ENT(480, 11, 0),
+ RATETAB_ENT(540, 12, 0)
+};
+
+struct wilc_priv {
+ struct wireless_dev *wdev;
+ struct cfg80211_scan_request *scan_req;
+ struct wilc_wfi_p2p_listen_params remain_on_ch_params;
+ u64 tx_cookie;
+ bool cfg_scanning;
+ u8 associated_bss[ETH_ALEN];
+ struct sta_info assoc_stainfo;
+ struct sk_buff *skb;
+ struct net_device *dev;
+ struct host_if_drv *hif_drv;
+ struct wilc_pmkid_attr pmkid_list;
+ u8 wep_key[4][WLAN_KEY_LEN_WEP104];
+ u8 wep_key_len[4];
+ /* The real interface that the monitor is on */
+ struct net_device *real_ndev;
+ struct wilc_wfi_key *wilc_gtk[WILC_MAX_NUM_STA];
+ struct wilc_wfi_key *wilc_ptk[WILC_MAX_NUM_STA];
+ u8 wilc_groupkey;
+
+ struct mutex scan_req_lock;
+
+ bool p2p_listen_state;
+ struct wilc_buffered_eap *buffered_eap;
+
+ struct timer_list eap_buff_timer;
+ int scanned_cnt;
+ struct wilc_p2p_var p2p;
+ struct ieee80211_channel channels[ARRAY_SIZE(wilc_2ghz_channels)];
+ struct ieee80211_rate bitrates[ARRAY_SIZE(wilc_bitrates)];
+ struct ieee80211_supported_band band;
+ u32 cipher_suites[ARRAY_SIZE(wilc_cipher_suites)];
+ u64 inc_roc_cookie;
+};
+
+struct frame_reg {
+ u16 type;
+ bool reg;
+};
+
+#define MAX_TCP_SESSION 25
+#define MAX_PENDING_ACKS 256
+
+struct ack_session_info {
+ u32 seq_num;
+ u32 bigger_ack_num;
+ u16 src_port;
+ u16 dst_port;
+ u16 status;
+};
+
+struct pending_acks {
+ u32 ack_num;
+ u32 session_index;
+ struct txq_entry_t *txqe;
+};
+
+struct tcp_ack_filter {
+ struct ack_session_info ack_session_info[2 * MAX_TCP_SESSION];
+ struct pending_acks pending_acks[MAX_PENDING_ACKS];
+ u32 pending_base;
+ u32 tcp_session;
+ u32 pending_acks_idx;
+ bool enabled;
+};
+
+struct sysfs_attr_group {
+ bool p2p_mode;
+ u8 ant_swtch_mode;
+ u8 antenna1;
+ u8 antenna2;
+};
+
+struct wilc_vif {
+ u8 idx;
+ u8 iftype;
+ int monitor_flag;
+ int mac_opened;
+ struct frame_reg frame_reg[NUM_REG_FRAME];
+ struct net_device_stats netstats;
+ struct wilc *wilc;
+ u8 bssid[ETH_ALEN];
+ struct host_if_drv *hif_drv;
+ struct net_device *ndev;
+ u8 ifc_id;
+
+ struct sysfs_attr_group attr_sysfs;
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+ bool pwrsave_current_state;
+ struct timer_list during_ip_timer;
+ bool obtaining_ip;
+#endif
+ struct rf_info periodic_stats;
+ struct timer_list periodic_rssi;
+ struct tcp_ack_filter ack_filter;
+ bool connecting;
+};
+
+struct wilc {
+ const struct wilc_hif_func *hif_func;
+ int io_type;
+ s8 mac_status;
+#if KERNEL_VERSION(3, 13, 0) < LINUX_VERSION_CODE
+ struct gpio_desc *gpio_irq;
+#else
+ int gpio_irq;
+#endif
+ bool initialized;
+ int dev_irq_num;
+ int close;
+ u8 vif_num;
+ struct wilc_vif *vif[WILC_NUM_CONCURRENT_IFC];
+ u8 open_ifcs;
+ /*protect head of transmit queue*/
+ struct mutex txq_add_to_head_cs;
+ /*protect txq_entry_t transmit queue*/
+ spinlock_t txq_spinlock;
+ /*protect rxq_entry_t receiver queue*/
+ struct mutex rxq_cs;
+ /* lock to protect hif access */
+ struct mutex hif_cs;
+
+ struct completion cfg_event;
+ struct completion sync_event;
+ struct completion txq_event;
+ struct completion txq_thread_started;
+ struct completion debug_thread_started;
+ struct task_struct *txq_thread;
+ struct task_struct *debug_thread;
+
+ int quit;
+ /* lock to protect issue of wid command to fw */
+ struct mutex cfg_cmd_lock;
+ struct wilc_cfg_frame cfg_frame;
+ u32 cfg_frame_offset;
+ u8 cfg_seq_no;
+
+ u8 *rx_buffer;
+ u32 rx_buffer_offset;
+ u8 *tx_buffer;
+
+ struct txq_handle txq[NQUEUES];
+ int txq_entries;
+
+ struct rxq_entry_t rxq_head;
+
+ const struct firmware *firmware;
+
+ struct device *dev;
+ struct device *dt_dev;
+
+ enum wilc_chip_type chip;
+
+ uint8_t power_status[DEV_MAX];
+ uint8_t keep_awake[DEV_MAX];
+ struct mutex cs;
+ int clients_count;
+ struct workqueue_struct *hif_workqueue;
+
+ struct wilc_cfg cfg;
+ void *bus_data;
+ struct net_device *monitor_dev;
+ /* deinit lock */
+ struct mutex deinit_lock;
+ u8 sta_ch;
+ u8 op_ch;
+};
+
+struct wilc_wfi_mon_priv {
+ struct net_device *real_ndev;
+};
+
+void wilc_frmw_to_host(struct wilc_vif *vif, u8 *buff, u32 size,
+ u32 pkt_offset, u8 status);
+void wilc_mac_indicate(struct wilc *wilc);
+void wilc_netdev_cleanup(struct wilc *wilc);
+int wilc_netdev_init(struct wilc **wilc, struct device *dev, int io_type,
+ const struct wilc_hif_func *ops);
+void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size);
+void wilc_wlan_set_bssid(struct net_device *wilc_netdev, u8 *bssid, u8 mode);
+
+#endif
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
+#include <linux/ip.h>
+
+#include "wilc_wfi_netdevice.h"
+#include "wilc_wlan_cfg.h"
+#include "wilc_netdev.h"
+
+#define WAKUP_TRAILS_TIMEOUT (10000)
+
+#if KERNEL_VERSION(3, 12, 21) > LINUX_VERSION_CODE
+#define list_next_entry(pos, member) \
+ list_entry((pos)->member.next, typeof(*(pos)), member)
+#endif
+
+void acquire_bus(struct wilc *wilc, enum bus_acquire acquire, int source)
+{
+ mutex_lock(&wilc->hif_cs);
+ if (acquire == WILC_BUS_ACQUIRE_AND_WAKEUP)
+ chip_wakeup(wilc, source);
+}
+
+void release_bus(struct wilc *wilc, enum bus_release release, int source)
+{
+ if (release == WILC_BUS_RELEASE_ALLOW_SLEEP)
+ chip_allow_sleep(wilc, source);
+ mutex_unlock(&wilc->hif_cs);
+}
+
+uint8_t reset_bus(struct wilc *wilc)
+{
+ uint8_t ret = 0;
+
+ if (wilc->io_type == WILC_HIF_SPI)
+ return wilc->hif_func->hif_reset(wilc);
+ return ret;
+}
+
+static void wilc_wlan_txq_remove(struct wilc *wilc, u8 q_num,
+ struct txq_entry_t *tqe)
+{
+ list_del(&tqe->list);
+ wilc->txq_entries -= 1;
+ wilc->txq[q_num].count--;
+}
+
+static struct txq_entry_t *
+wilc_wlan_txq_remove_from_head(struct net_device *dev, u8 q_num)
+{
+ struct txq_entry_t *tqe = NULL;
+ unsigned long flags;
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wilc = vif->wilc;
+
+ spin_lock_irqsave(&wilc->txq_spinlock, flags);
+
+ if (!list_empty(&wilc->txq[q_num].txq_head.list)) {
+ tqe = list_first_entry(&wilc->txq[q_num].txq_head.list,
+ struct txq_entry_t, list);
+ list_del(&tqe->list);
+ wilc->txq_entries -= 1;
+ wilc->txq[q_num].count--;
+ }
+ spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
+ return tqe;
+}
+
+static void wilc_wlan_txq_add_to_tail(struct net_device *dev, u8 q_num,
+ struct txq_entry_t *tqe)
+{
+ unsigned long flags;
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wilc = vif->wilc;
+
+ spin_lock_irqsave(&wilc->txq_spinlock, flags);
+
+ list_add_tail(&tqe->list, &wilc->txq[q_num].txq_head.list);
+ wilc->txq_entries += 1;
+ wilc->txq[q_num].count++;
+ PRINT_INFO(vif->ndev, TX_DBG, "Number of entries in TxQ = %d\n",
+ wilc->txq_entries);
+
+ spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
+
+ PRINT_INFO(vif->ndev, TX_DBG, "Wake the txq_handling\n");
+ complete(&wilc->txq_event);
+}
+
+static void wilc_wlan_txq_add_to_head(struct wilc_vif *vif, u8 q_num,
+ struct txq_entry_t *tqe)
+{
+ unsigned long flags;
+ struct wilc *wilc = vif->wilc;
+
+ mutex_lock(&wilc->txq_add_to_head_cs);
+
+ spin_lock_irqsave(&wilc->txq_spinlock, flags);
+
+ list_add(&tqe->list, &wilc->txq[q_num].txq_head.list);
+ wilc->txq_entries += 1;
+ wilc->txq[q_num].count++;
+ PRINT_INFO(vif->ndev, TX_DBG, "Number of entries in TxQ = %d\n",
+ wilc->txq_entries);
+
+ spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
+ mutex_unlock(&wilc->txq_add_to_head_cs);
+ complete(&wilc->txq_event);
+ PRINT_INFO(vif->ndev, TX_DBG, "Wake up the txq_handler\n");
+}
+
+#define NOT_TCP_ACK (-1)
+
+static inline void add_tcp_session(struct wilc_vif *vif, u32 src_prt,
+ u32 dst_prt, u32 seq)
+{
+ struct tcp_ack_filter *f = &vif->ack_filter;
+
+ if (f->tcp_session < 2 * MAX_TCP_SESSION) {
+ f->ack_session_info[f->tcp_session].seq_num = seq;
+ f->ack_session_info[f->tcp_session].bigger_ack_num = 0;
+ f->ack_session_info[f->tcp_session].src_port = src_prt;
+ f->ack_session_info[f->tcp_session].dst_port = dst_prt;
+ f->tcp_session++;
+ PRINT_INFO(vif->ndev, TCP_ENH, "TCP Session %d to Ack %d\n",
+ f->tcp_session, seq);
+ }
+}
+
+static inline void update_tcp_session(struct wilc_vif *vif, u32 index, u32 ack)
+{
+ struct tcp_ack_filter *f = &vif->ack_filter;
+
+ if (index < 2 * MAX_TCP_SESSION &&
+ ack > f->ack_session_info[index].bigger_ack_num)
+ f->ack_session_info[index].bigger_ack_num = ack;
+}
+
+static inline void add_tcp_pending_ack(struct wilc_vif *vif, u32 ack,
+ u32 session_index,
+ struct txq_entry_t *txqe)
+{
+ struct tcp_ack_filter *f = &vif->ack_filter;
+ u32 i = f->pending_base + f->pending_acks_idx;
+
+ if (i < MAX_PENDING_ACKS) {
+ f->pending_acks[i].ack_num = ack;
+ f->pending_acks[i].txqe = txqe;
+ f->pending_acks[i].session_index = session_index;
+ txqe->ack_idx = i;
+ f->pending_acks_idx++;
+ }
+}
+
+static inline void tcp_process(struct net_device *dev, struct txq_entry_t *tqe)
+{
+ void *buffer = tqe->buffer;
+ const struct ethhdr *eth_hdr_ptr = buffer;
+ int i;
+ unsigned long flags;
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wilc = vif->wilc;
+ struct tcp_ack_filter *f = &vif->ack_filter;
+ const struct iphdr *ip_hdr_ptr;
+ const struct tcphdr *tcp_hdr_ptr;
+ u32 ihl, total_length, data_offset;
+
+ spin_lock_irqsave(&wilc->txq_spinlock, flags);
+
+ if (eth_hdr_ptr->h_proto != htons(ETH_P_IP))
+ goto out;
+
+ ip_hdr_ptr = buffer + ETH_HLEN;
+
+ if (ip_hdr_ptr->protocol != IPPROTO_TCP)
+ goto out;
+
+ ihl = ip_hdr_ptr->ihl << 2;
+ tcp_hdr_ptr = buffer + ETH_HLEN + ihl;
+ total_length = ntohs(ip_hdr_ptr->tot_len);
+
+ data_offset = tcp_hdr_ptr->doff << 2;
+ if (total_length == (ihl + data_offset)) {
+ u32 seq_no, ack_no;
+
+ seq_no = ntohl(tcp_hdr_ptr->seq);
+ ack_no = ntohl(tcp_hdr_ptr->ack_seq);
+ for (i = 0; i < f->tcp_session; i++) {
+ u32 j = f->ack_session_info[i].seq_num;
+
+ if (i < 2 * MAX_TCP_SESSION &&
+ j == seq_no) {
+ update_tcp_session(vif, i, ack_no);
+ break;
+ }
+ }
+ if (i == f->tcp_session)
+ add_tcp_session(vif, 0, 0, seq_no);
+
+ add_tcp_pending_ack(vif, ack_no, i, tqe);
+ }
+
+out:
+ spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
+}
+
+static void wilc_wlan_txq_filter_dup_tcp_ack(struct net_device *dev)
+{
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wilc = vif->wilc;
+ struct tcp_ack_filter *f = &vif->ack_filter;
+ u32 i = 0;
+ u32 dropped = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&wilc->txq_spinlock, flags);
+ for (i = f->pending_base;
+ i < (f->pending_base + f->pending_acks_idx); i++) {
+ u32 index;
+ u32 bigger_ack_num;
+
+ if (i >= MAX_PENDING_ACKS)
+ break;
+
+ index = f->pending_acks[i].session_index;
+
+ if (index >= 2 * MAX_TCP_SESSION)
+ break;
+
+ bigger_ack_num = f->ack_session_info[index].bigger_ack_num;
+
+ if (f->pending_acks[i].ack_num < bigger_ack_num) {
+ struct txq_entry_t *tqe;
+
+ PRINT_INFO(vif->ndev, TCP_ENH, "DROP ACK: %u\n",
+ f->pending_acks[i].ack_num);
+ tqe = f->pending_acks[i].txqe;
+ if (tqe) {
+ wilc_wlan_txq_remove(wilc, tqe->q_num, tqe);
+ tqe->status = 1;
+ if (tqe->tx_complete_func)
+ tqe->tx_complete_func(tqe->priv,
+ tqe->status);
+ kfree(tqe);
+ dropped++;
+ }
+ }
+ }
+ f->pending_acks_idx = 0;
+ f->tcp_session = 0;
+
+ if (f->pending_base == 0)
+ f->pending_base = MAX_TCP_SESSION;
+ else
+ f->pending_base = 0;
+
+ spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
+
+ while (dropped > 0) {
+ if (!wait_for_completion_timeout(&wilc->txq_event,
+ msecs_to_jiffies(1)))
+ PRINT_ER(vif->ndev, "completion timedout\n");
+ dropped--;
+ }
+}
+
+static struct net_device *get_if_handler(struct wilc *wilc, u8 *mac_header)
+{
+ u8 *bssid, *bssid1;
+ int i = 0;
+ struct net_device *mon_netdev = NULL;
+
+ bssid = mac_header + 10;
+ bssid1 = mac_header + 4;
+ for (i = 0; i < wilc->vif_num; i++) {
+ if (wilc->vif[i]->iftype == WILC_STATION_MODE)
+ if (ether_addr_equal_unaligned(bssid,
+ wilc->vif[i]->bssid))
+ return wilc->vif[i]->ndev;
+ if (wilc->vif[i]->iftype == WILC_AP_MODE)
+ if (ether_addr_equal_unaligned(bssid1,
+ wilc->vif[i]->bssid))
+ return wilc->vif[i]->ndev;
+ if (wilc->vif[i]->iftype == WILC_MONITOR_MODE)
+ mon_netdev = wilc->vif[i]->ndev;
+ }
+
+ if (!mon_netdev)
+ PRINT_WRN(wilc->vif[0]->ndev, GENERIC_DBG, "Invalid handle\n");
+ return mon_netdev;
+}
+
+void wilc_enable_tcp_ack_filter(struct wilc_vif *vif, bool value)
+{
+ vif->ack_filter.enabled = value;
+}
+
+static int wilc_wlan_txq_add_cfg_pkt(struct wilc_vif *vif, u8 *buffer,
+ u32 buffer_size)
+{
+ struct txq_entry_t *tqe;
+ struct wilc *wilc = vif->wilc;
+
+ PRINT_INFO(vif->ndev, TX_DBG, "Adding config packet ...\n");
+ if (wilc->quit) {
+ PRINT_INFO(vif->ndev, TX_DBG, "Return due to clear function\n");
+ complete(&wilc->cfg_event);
+ return 0;
+ }
+
+ if (!(wilc->initialized)) {
+ PRINT_INFO(vif->ndev, TX_DBG, "wilc not initialized\n");
+ complete(&wilc->cfg_event);
+ return 0;
+ }
+ tqe = kmalloc(sizeof(*tqe), GFP_KERNEL);
+ if (!tqe) {
+ complete(&wilc->cfg_event);
+ return 0;
+ }
+ tqe->type = WILC_CFG_PKT;
+ tqe->buffer = buffer;
+ tqe->buffer_size = buffer_size;
+ tqe->tx_complete_func = NULL;
+ tqe->priv = NULL;
+ tqe->q_num = AC_VO_Q;
+ tqe->ack_idx = NOT_TCP_ACK;
+
+ PRINT_INFO(vif->ndev, TX_DBG,
+ "Adding the config packet at the Queue tail\n");
+
+ wilc_wlan_txq_add_to_head(vif, AC_VO_Q, tqe);
+
+ return 1;
+}
+
+static void ac_q_limit(struct wilc *wilc, u8 ac, u16 *q_limit)
+{
+ static u8 buffer[AC_BUFFER_SIZE];
+ static u16 end_index;
+ static bool initialized;
+ static u16 cnt[NQUEUES];
+ u8 factors[NQUEUES] = {1, 1, 1, 1};
+ static u16 sum;
+ u16 i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&wilc->txq_spinlock, flags);
+ if (!initialized) {
+ for (i = 0; i < AC_BUFFER_SIZE; i++)
+ buffer[i] = i % NQUEUES;
+
+ for (i = 0; i < NQUEUES; i++) {
+ cnt[i] = AC_BUFFER_SIZE * factors[i] / NQUEUES;
+ sum += cnt[i];
+ }
+ end_index = AC_BUFFER_SIZE - 1;
+ initialized = 1;
+ }
+
+ cnt[buffer[end_index]] -= factors[buffer[end_index]];
+ cnt[ac] += factors[ac];
+ sum += (factors[ac] - factors[buffer[end_index]]);
+
+ buffer[end_index] = ac;
+ if (end_index > 0)
+ end_index--;
+ else
+ end_index = AC_BUFFER_SIZE - 1;
+
+ for (i = 0; i < NQUEUES; i++) {
+ if (!sum)
+ q_limit[i] = 1;
+ else
+ q_limit[i] = (cnt[i] * FLOW_CTRL_UP_THRESHLD / sum) + 1;
+ }
+ spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
+}
+
+static inline u8 ac_classify(struct wilc *wilc, struct txq_entry_t *tqe)
+{
+ u8 *eth_hdr_ptr;
+ u8 *buffer = tqe->buffer;
+ u8 ac;
+ u16 h_proto;
+ unsigned long flags;
+
+ spin_lock_irqsave(&wilc->txq_spinlock, flags);
+
+ eth_hdr_ptr = &buffer[0];
+ h_proto = ntohs(*((unsigned short *)ð_hdr_ptr[12]));
+ if (h_proto == ETH_P_IP) {
+ u8 *ip_hdr_ptr;
+ u32 IHL, DSCP;
+
+ ip_hdr_ptr = &buffer[ETHERNET_HDR_LEN];
+ IHL = (ip_hdr_ptr[0] & 0xf) << 2;
+ DSCP = (ip_hdr_ptr[1] & 0xfc);
+
+ switch (DSCP) {
+ case 0x20:
+ case 0x40:
+ case 0x08:
+ ac = AC_BK_Q;
+ break;
+ case 0x80:
+ case 0xA0:
+ case 0x28:
+ ac = AC_VI_Q;
+ break;
+ case 0xC0:
+ case 0xd0:
+ case 0xE0:
+ case 0x88:
+ case 0xB8:
+ ac = AC_VO_Q;
+ break;
+ default:
+ ac = AC_BE_Q;
+ break;
+ }
+ } else {
+ ac = AC_BE_Q;
+ }
+
+ tqe->q_num = ac;
+ spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
+
+ return ac;
+}
+
+static inline int ac_balance(u8 *count, u8 *ratio)
+{
+ u8 i, max_count = 0;
+
+ if (!count || !ratio)
+ return -1;
+
+ for (i = 0; i < NQUEUES; i++)
+ if (count[i] > max_count)
+ max_count = count[i];
+
+ for (i = 0; i < NQUEUES; i++)
+ ratio[i] = max_count - count[i];
+
+ return 0;
+}
+
+static inline void ac_pkt_count(u32 reg, u8 *pkt_count)
+{
+ pkt_count[AC_BK_Q] = (reg & 0x000000fa) >> BK_AC_COUNT_POS;
+ pkt_count[AC_BE_Q] = (reg & 0x0000fe00) >> BE_AC_COUNT_POS;
+ pkt_count[AC_VI_Q] = (reg & 0x00fe0000) >> VI_AC_COUNT_POS;
+ pkt_count[AC_VO_Q] = (reg & 0xfe000000) >> VO_AC_COUNT_POS;
+}
+
+static inline u8 ac_change(struct wilc *wilc, u8 *ac)
+{
+ do {
+ if (wilc->txq[*ac].acm == 0)
+ return 0;
+ (*ac)++;
+ } while (*ac < NQUEUES);
+ return 1;
+}
+
+static inline void ac_acm_bit(struct wilc *wilc, u32 reg)
+{
+ wilc->txq[AC_BK_Q].acm = (reg & 0x00000002) >> BK_AC_ACM_STAT_POS;
+ wilc->txq[AC_BE_Q].acm = (reg & 0x00000100) >> BE_AC_ACM_STAT_POS;
+ wilc->txq[AC_VI_Q].acm = (reg & 0x00010000) >> VI_AC_ACM_STAT_POS;
+ wilc->txq[AC_VO_Q].acm = (reg & 0x01000000) >> VO_AC_ACM_STAT_POS;
+}
+
+int txq_add_net_pkt(struct net_device *dev, void *priv, u8 *buffer,
+ u32 buffer_size,
+ void (*tx_complete_fn)(void *, int))
+{
+ struct txq_entry_t *tqe;
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wilc;
+ u8 q_num;
+ u16 q_limit[NQUEUES] = {0, 0, 0, 0};
+
+ if (!vif) {
+ pr_info("%s vif is NULL\n", __func__);
+ return -1;
+ }
+
+ wilc = vif->wilc;
+
+ if (wilc->quit) {
+ PRINT_INFO(vif->ndev, TX_DBG,
+ "drv is quitting, return from net_pkt\n");
+ tx_complete_fn(priv, 0);
+ return 0;
+ }
+
+ if (!(wilc->initialized)) {
+ PRINT_INFO(vif->ndev, TX_DBG,
+ "not_init, return from net_pkt\n");
+ tx_complete_fn(priv, 0);
+ return 0;
+ }
+
+ tqe = kmalloc(sizeof(*tqe), GFP_ATOMIC);
+
+ if (!tqe) {
+ PRINT_INFO(vif->ndev, TX_DBG,
+ "malloc failed, return from net_pkt\n");
+ tx_complete_fn(priv, 0);
+ return 0;
+ }
+ tqe->type = WILC_NET_PKT;
+ tqe->buffer = buffer;
+ tqe->buffer_size = buffer_size;
+ tqe->tx_complete_func = tx_complete_fn;
+ tqe->priv = priv;
+
+ q_num = ac_classify(wilc, tqe);
+ if (ac_change(wilc, &q_num)) {
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "No suitable non-ACM queue\n");
+ kfree(tqe);
+ return 0;
+ }
+ ac_q_limit(wilc, q_num, q_limit);
+
+ if ((q_num == AC_VO_Q && wilc->txq[q_num].count <= q_limit[AC_VO_Q]) ||
+ (q_num == AC_VI_Q && wilc->txq[q_num].count <= q_limit[AC_VI_Q]) ||
+ (q_num == AC_BE_Q && wilc->txq[q_num].count <= q_limit[AC_BE_Q]) ||
+ (q_num == AC_BK_Q && wilc->txq[q_num].count <= q_limit[AC_BK_Q])) {
+ PRINT_INFO(vif->ndev, TX_DBG,
+ "Adding mgmt packet at the Queue tail\n");
+ tqe->ack_idx = NOT_TCP_ACK;
+ if (vif->ack_filter.enabled)
+ tcp_process(dev, tqe);
+ wilc_wlan_txq_add_to_tail(dev, q_num, tqe);
+ } else {
+ tqe->status = 0;
+ if (tqe->tx_complete_func)
+ tqe->tx_complete_func(tqe->priv, tqe->status);
+ kfree(tqe);
+ }
+
+ return wilc->txq_entries;
+}
+
+int txq_add_mgmt_pkt(struct net_device *dev, void *priv, u8 *buffer,
+ u32 buffer_size,
+ void (*tx_complete_fn)(void *, int))
+{
+ struct txq_entry_t *tqe;
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wilc;
+
+ wilc = vif->wilc;
+
+ if (wilc->quit) {
+ PRINT_INFO(vif->ndev, TX_DBG, "drv is quitting\n");
+ tx_complete_fn(priv, 0);
+ return 0;
+ }
+
+ if (!(wilc->initialized)) {
+ PRINT_INFO(vif->ndev, TX_DBG, "wilc not_init\n");
+ tx_complete_fn(priv, 0);
+ return 0;
+ }
+ tqe = kmalloc(sizeof(*tqe), GFP_ATOMIC);
+
+ if (!tqe) {
+ PRINT_INFO(vif->ndev, TX_DBG, "Queue malloc failed\n");
+ tx_complete_fn(priv, 0);
+ return 0;
+ }
+ tqe->type = WILC_MGMT_PKT;
+ tqe->buffer = buffer;
+ tqe->buffer_size = buffer_size;
+ tqe->tx_complete_func = tx_complete_fn;
+ tqe->priv = priv;
+ tqe->q_num = AC_BE_Q;
+ tqe->ack_idx = NOT_TCP_ACK;
+
+ PRINT_INFO(vif->ndev, TX_DBG, "Adding Mgmt packet to Queue tail\n");
+ wilc_wlan_txq_add_to_tail(dev, AC_VO_Q, tqe);
+ return 1;
+}
+
+static struct txq_entry_t *txq_get_first(struct wilc *wilc, u8 q_num)
+{
+ struct txq_entry_t *tqe = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&wilc->txq_spinlock, flags);
+
+ if (!list_empty(&wilc->txq[q_num].txq_head.list))
+ tqe = list_first_entry(&wilc->txq[q_num].txq_head.list,
+ struct txq_entry_t, list);
+
+ spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
+
+ return tqe;
+}
+
+static struct txq_entry_t *txq_get_next(struct wilc *wilc,
+ struct txq_entry_t *tqe, u8 q_num)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&wilc->txq_spinlock, flags);
+
+ if (!list_is_last(&tqe->list, &wilc->txq[q_num].txq_head.list))
+ tqe = list_next_entry(tqe, list);
+ else
+ tqe = NULL;
+ spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
+
+ return tqe;
+}
+
+static void rxq_add(struct wilc *wilc, struct rxq_entry_t *rqe)
+{
+ struct wilc_vif *vif = wilc->vif[0];
+
+ if (wilc->quit)
+ return;
+
+ mutex_lock(&wilc->rxq_cs);
+ list_add_tail(&rqe->list, &wilc->rxq_head.list);
+ PRINT_INFO(vif->ndev, RX_DBG, "Added to RX queue\n");
+ mutex_unlock(&wilc->rxq_cs);
+}
+
+static struct rxq_entry_t *rxq_remove(struct wilc *wilc)
+{
+ struct wilc_vif *vif = wilc->vif[0];
+ struct rxq_entry_t *rqe = NULL;
+
+ PRINT_INFO(vif->ndev, RX_DBG, "Getting rxQ element\n");
+
+ mutex_lock(&wilc->rxq_cs);
+ if (!list_empty(&wilc->rxq_head.list)) {
+ rqe = list_first_entry(&wilc->rxq_head.list, struct rxq_entry_t,
+ list);
+ list_del(&rqe->list);
+ } else {
+ PRINT_INFO(vif->ndev, RX_DBG, "Nothing to get from Q\n");
+ }
+ mutex_unlock(&wilc->rxq_cs);
+ return rqe;
+}
+
+static int chip_allow_sleep_wilc1000(struct wilc *wilc, int source)
+{
+ u32 reg = 0;
+ const struct wilc_hif_func *hif_func = wilc->hif_func;
+ u32 wakeup_reg, wakeup_bit;
+ u32 to_host_from_fw_reg, to_host_from_fw_bit;
+ u32 from_host_to_fw_reg, from_host_to_fw_bit;
+ u32 trials = 100;
+ int ret;
+
+ if (wilc->io_type == WILC_HIF_SDIO ||
+ wilc->io_type == WILC_HIF_SDIO_GPIO_IRQ) {
+ wakeup_reg = 0xf0;
+ wakeup_bit = BIT(0);
+ from_host_to_fw_reg = 0xfa;
+ from_host_to_fw_bit = BIT(0);
+ to_host_from_fw_reg = 0xfc;
+ to_host_from_fw_bit = BIT(0);
+ } else {
+ wakeup_reg = 0x1;
+ wakeup_bit = BIT(1);
+ from_host_to_fw_reg = 0x0b;
+ from_host_to_fw_bit = BIT(0);
+ to_host_from_fw_reg = 0xfc;
+ to_host_from_fw_bit = BIT(0);
+ }
+
+ while (trials--) {
+ ret = hif_func->hif_read_reg(wilc, to_host_from_fw_reg, ®);
+ if (!ret)
+ return -EIO;
+ if ((reg & to_host_from_fw_bit) == 0)
+ break;
+ }
+ if (!trials)
+ pr_warn("FW not responding\n");
+
+ /* Clear bit 1 */
+ ret = hif_func->hif_read_reg(wilc, wakeup_reg, ®);
+ if (!ret)
+ return -EIO;
+ if (reg & wakeup_bit) {
+ reg &= ~wakeup_bit;
+ ret = hif_func->hif_write_reg(wilc, wakeup_reg, reg);
+ if (!ret)
+ return -EIO;
+ }
+
+ ret = hif_func->hif_read_reg(wilc, from_host_to_fw_reg, ®);
+ if (!ret)
+ return -EIO;
+ if (reg & from_host_to_fw_bit) {
+ reg &= ~from_host_to_fw_bit;
+ ret = hif_func->hif_write_reg(wilc, from_host_to_fw_reg, reg);
+ if (!ret)
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int chip_allow_sleep_wilc3000(struct wilc *wilc, int source)
+{
+ u32 reg = 0;
+ int ret;
+ const struct wilc_hif_func *hif_func = wilc->hif_func;
+
+ if (wilc->io_type == WILC_HIF_SDIO ||
+ wilc->io_type == WILC_HIF_SDIO_GPIO_IRQ) {
+ ret = hif_func->hif_read_reg(wilc, 0xf0, ®);
+ if (!ret)
+ return -EIO;
+ ret = hif_func->hif_write_reg(wilc, 0xf0, reg & ~BIT(0));
+ if (!ret)
+ return -EIO;
+ } else {
+ ret = hif_func->hif_read_reg(wilc, 0x1, ®);
+ if (!ret)
+ return -EIO;
+ ret = hif_func->hif_write_reg(wilc, 0x1, reg & ~BIT(1));
+ if (!ret)
+ return -EIO;
+ }
+ return 0;
+}
+
+void chip_allow_sleep(struct wilc *wilc, int source)
+{
+ int ret = 0;
+
+ if (((source == DEV_WIFI) && (wilc->keep_awake[DEV_BT] == true)) ||
+ ((source == DEV_BT) && (wilc->keep_awake[DEV_WIFI] == true)))
+ pr_warn("Another device is preventing allow sleep operation. request source is %s\n",
+ (source == DEV_WIFI ? "Wifi" : "BT"));
+ else
+ if (wilc->chip == WILC_1000)
+ ret = chip_allow_sleep_wilc1000(wilc, source);
+ else
+ ret = chip_allow_sleep_wilc3000(wilc, source);
+ if (!ret)
+ wilc->keep_awake[source] = false;
+}
+
+void chip_wakeup_wilc1000(struct wilc *wilc, int source)
+{
+ u32 ret = 0;
+ u32 reg = 0, clk_status_val = 0, trials = 0;
+ u32 wakeup_reg, wakeup_bit;
+ u32 clk_status_reg, clk_status_bit;
+ u32 to_host_from_fw_reg, to_host_from_fw_bit;
+ u32 from_host_to_fw_reg, from_host_to_fw_bit;
+ const struct wilc_hif_func *hif_func = wilc->hif_func;
+
+ if (wilc->io_type == WILC_HIF_SDIO ||
+ wilc->io_type == WILC_HIF_SDIO_GPIO_IRQ) {
+ wakeup_reg = 0xf0;
+ clk_status_reg = 0xf1;
+ wakeup_bit = BIT(0);
+ clk_status_bit = BIT(0);
+ from_host_to_fw_reg = 0xfa;
+ from_host_to_fw_bit = BIT(0);
+ to_host_from_fw_reg = 0xfc;
+ to_host_from_fw_bit = BIT(0);
+ } else {
+ wakeup_reg = 0x1;
+ clk_status_reg = 0x0f;
+ wakeup_bit = BIT(1);
+ clk_status_bit = BIT(2);
+ from_host_to_fw_reg = 0x0b;
+ from_host_to_fw_bit = BIT(0);
+ to_host_from_fw_reg = 0xfc;
+ to_host_from_fw_bit = BIT(0);
+ }
+
+ ret = hif_func->hif_read_reg(wilc, from_host_to_fw_reg, ®);
+ if (!ret)
+ goto _fail_;
+
+ if (!(reg & from_host_to_fw_bit)) {
+ /*USE bit 0 to indicate host wakeup*/
+ ret = hif_func->hif_write_reg(wilc, from_host_to_fw_reg,
+ reg | from_host_to_fw_bit);
+ if (!ret)
+ goto _fail_;
+ }
+
+ ret = hif_func->hif_read_reg(wilc, wakeup_reg, ®);
+ if (!ret)
+ goto _fail_;
+ /* Set bit 1 */
+ if (!(reg & wakeup_bit)) {
+ ret = hif_func->hif_write_reg(wilc, wakeup_reg,
+ reg | wakeup_bit);
+ if (!ret)
+ goto _fail_;
+ }
+
+ do {
+ ret = hif_func->hif_read_reg(wilc, clk_status_reg,
+ &clk_status_val);
+ if (!ret) {
+ pr_err("Bus error (5).%d %x\n", ret, clk_status_val);
+ goto _fail_;
+ }
+ if (clk_status_val & clk_status_bit)
+ break;
+
+ //nm_bsp_sleep(2);
+ trials++;
+ if (trials > WAKUP_TRAILS_TIMEOUT) {
+ pr_err("Failed to wakup the chip\n");
+ ret = -1;
+ goto _fail_;
+ }
+ } while (1);
+
+ if (wilc_get_chipid(wilc, false) < 0x1002b0) {
+ uint32_t val32;
+ /* Enable PALDO back right after wakeup */
+ hif_func->hif_read_reg(wilc, 0x1e1c, &val32);
+ val32 |= BIT(6);
+ hif_func->hif_write_reg(wilc, 0x1e1c, val32);
+
+ hif_func->hif_read_reg(wilc, 0x1e9c, &val32);
+ val32 |= BIT(6);
+ hif_func->hif_write_reg(wilc, 0x1e9c, val32);
+ }
+ /*workaround sometimes spi fail to read clock regs after reading
+ * writing clockless registers
+ */
+ reset_bus(wilc);
+
+_fail_:
+ return;
+}
+
+void chip_wakeup_wilc3000(struct wilc *wilc, int source)
+{
+ u32 wakeup_reg_val, clk_status_reg_val, trials = 0;
+ u32 wakeup_reg, wakeup_bit;
+ u32 clk_status_reg, clk_status_bit;
+ int wake_seq_trials = 5;
+ const struct wilc_hif_func *hif_func = wilc->hif_func;
+
+ if (wilc->io_type == WILC_HIF_SDIO ||
+ wilc->io_type == WILC_HIF_SDIO_GPIO_IRQ) {
+ wakeup_reg = 0xf0;
+ clk_status_reg = 0xf0;
+ wakeup_bit = BIT(0);
+ clk_status_bit = BIT(4);
+ } else {
+ wakeup_reg = 0x1;
+ clk_status_reg = 0x13;
+ wakeup_bit = BIT(1);
+ clk_status_bit = BIT(2);
+ }
+
+ hif_func->hif_read_reg(wilc, wakeup_reg, &wakeup_reg_val);
+ do {
+ hif_func->hif_write_reg(wilc, wakeup_reg, wakeup_reg_val |
+ wakeup_bit);
+ /* Check the clock status */
+ hif_func->hif_read_reg(wilc, clk_status_reg,
+ &clk_status_reg_val);
+
+ /*
+ * in case of clocks off, wait 1ms, and check it again.
+ * if still off, wait for another 1ms, for a total wait of 3ms.
+ * If still off, redo the wake up sequence
+ */
+ while ((clk_status_reg_val & clk_status_bit) == 0 &&
+ (++trials % 4) != 0) {
+ /* Wait for the chip to stabilize*/
+ usleep_range(1000, 1100);
+
+ /*
+ * Make sure chip is awake. This is an extra step that
+ * can be removed later to avoid the bus access
+ * overhead
+ */
+ hif_func->hif_read_reg(wilc, clk_status_reg,
+ &clk_status_reg_val);
+ }
+ /* in case of failure, Reset the wakeup bit to introduce a new
+ * edge on the next loop
+ */
+ if ((clk_status_reg_val & clk_status_bit) == 0)
+ hif_func->hif_write_reg(wilc, wakeup_reg,
+ wakeup_reg_val & (~wakeup_bit));
+ } while (((clk_status_reg_val & clk_status_bit) == 0)
+ && (wake_seq_trials-- > 0));
+ if (!wake_seq_trials)
+ dev_err(wilc->dev, "clocks still OFF. Wake up failed\n");
+ wilc->keep_awake[source] = true;
+}
+
+void chip_wakeup(struct wilc *wilc, int source)
+{
+ if (wilc->chip == WILC_1000)
+ chip_wakeup_wilc1000(wilc, source);
+ else
+ chip_wakeup_wilc3000(wilc, source);
+}
+
+void host_wakeup_notify(struct wilc *wilc, int source)
+{
+ acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY, source);
+ if (wilc->chip == WILC_1000)
+ wilc->hif_func->hif_write_reg(wilc, 0x10b0, 1);
+ else
+ wilc->hif_func->hif_write_reg(wilc, 0x10c0, 1);
+ release_bus(wilc, WILC_BUS_RELEASE_ONLY, source);
+}
+
+void host_sleep_notify(struct wilc *wilc, int source)
+{
+ acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY, source);
+ if (wilc->chip == WILC_1000)
+ wilc->hif_func->hif_write_reg(wilc, 0x10ac, 1);
+ else
+ wilc->hif_func->hif_write_reg(wilc, 0x10bc, 1);
+ release_bus(wilc, WILC_BUS_RELEASE_ONLY, source);
+}
+
+static u8 ac_fw_count[NQUEUES] = {0, 0, 0, 0};
+int wilc_wlan_handle_txq(struct net_device *dev, u32 *txq_count)
+{
+ int i, entries = 0;
+ u8 k, ac;
+ u32 sum;
+ u32 reg;
+ u8 ac_desired_ratio[NQUEUES] = {0, 0, 0, 0};
+ u8 ac_preserve_ratio[NQUEUES] = {1, 1, 1, 1};
+ u8 *num_pkts_to_add;
+ u8 vmm_entries_ac[WILC_VMM_TBL_SIZE];
+ u8 *txb;
+ u32 offset = 0;
+ bool max_size_over = 0, ac_exist = 0;
+ int vmm_sz = 0;
+ struct txq_entry_t *tqe_q[NQUEUES];
+ int ret = 0;
+ int counter;
+ int timeout;
+ u32 vmm_table[WILC_VMM_TBL_SIZE];
+ u8 ac_pkt_num_to_chip[NQUEUES] = {0, 0, 0, 0};
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wilc = vif->wilc;
+ const struct wilc_hif_func *func;
+
+ txb = wilc->tx_buffer;
+ if (!wilc->txq_entries) {
+ *txq_count = 0;
+ return 0;
+ }
+
+ if (wilc->quit)
+ goto out;
+ if (ac_balance(ac_fw_count, ac_desired_ratio))
+ return -1;
+
+ mutex_lock(&wilc->txq_add_to_head_cs);
+ wilc_wlan_txq_filter_dup_tcp_ack(dev);
+
+ PRINT_INFO(vif->ndev, TX_DBG, "Getting the head of the TxQ\n");
+ for (ac = 0; ac < NQUEUES; ac++)
+ tqe_q[ac] = txq_get_first(wilc, ac);
+ i = 0;
+ sum = 0;
+ max_size_over = 0;
+ num_pkts_to_add = ac_desired_ratio;
+ do {
+ ac_exist = 0;
+ for (ac = 0; (ac < NQUEUES) && (!max_size_over); ac++) {
+ if (!tqe_q[ac])
+ continue;
+
+ ac_exist = 1;
+ for (k = 0; (k < num_pkts_to_add[ac]) &&
+ (!max_size_over) && tqe_q[ac]; k++) {
+ if (i >= (WILC_VMM_TBL_SIZE - 1)) {
+ max_size_over = 1;
+ break;
+ }
+
+ if (tqe_q[ac]->type == WILC_CFG_PKT)
+ vmm_sz = ETH_CONFIG_PKT_HDR_OFFSET;
+ else if (tqe_q[ac]->type == WILC_NET_PKT)
+ vmm_sz = ETH_ETHERNET_HDR_OFFSET;
+ else
+ vmm_sz = HOST_HDR_OFFSET;
+
+ vmm_sz += tqe_q[ac]->buffer_size;
+ PRINT_INFO(vif->ndev, TX_DBG,
+ "VMM Size before alignment = %d\n",
+ vmm_sz);
+ if (vmm_sz & 0x3)
+ vmm_sz = (vmm_sz + 4) & ~0x3;
+
+ if ((sum + vmm_sz) > WILC_TX_BUFF_SIZE) {
+ max_size_over = 1;
+ break;
+ }
+ PRINT_INFO(vif->ndev, TX_DBG,
+ "VMM Size AFTER alignment = %d\n",
+ vmm_sz);
+ vmm_table[i] = vmm_sz / 4;
+ PRINT_INFO(vif->ndev, TX_DBG,
+ "VMMTable entry size = %d\n",
+ vmm_table[i]);
+ if (tqe_q[ac]->type == WILC_CFG_PKT) {
+ vmm_table[i] |= BIT(10);
+ PRINT_INFO(vif->ndev, TX_DBG,
+ "VMMTable entry changed for CFG packet = %d\n",
+ vmm_table[i]);
+ }
+ cpu_to_le32s(&vmm_table[i]);
+ vmm_entries_ac[i] = ac;
+
+ i++;
+ sum += vmm_sz;
+ PRINT_INFO(vif->ndev, TX_DBG, "sum = %d\n",
+ sum);
+ tqe_q[ac] = txq_get_next(wilc, tqe_q[ac], ac);
+ }
+ }
+ num_pkts_to_add = ac_preserve_ratio;
+ } while (!max_size_over && ac_exist);
+
+ if (i == 0) {
+ PRINT_INFO(vif->ndev, TX_DBG, "Nothing in TX-Q\n");
+ goto out;
+ }
+ vmm_table[i] = 0x0;
+
+ acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI);
+ counter = 0;
+ func = wilc->hif_func;
+ do {
+ ret = func->hif_read_reg(wilc, WILC_HOST_TX_CTRL, ®);
+ if (!ret) {
+ PRINT_ER(vif->ndev, "fail read reg vmm_tbl_entry..\n");
+ break;
+ }
+ if ((reg & 0x1) == 0) {
+ ac_pkt_count(reg, ac_fw_count);
+ ac_acm_bit(wilc, reg);
+ break;
+ }
+
+ counter++;
+ if (counter > 200) {
+ counter = 0;
+ PRINT_INFO(vif->ndev, TX_DBG,
+ "Looping in tx ctrl , force quit\n");
+ ret = func->hif_write_reg(wilc, WILC_HOST_TX_CTRL, 0);
+ break;
+ }
+ } while (!wilc->quit);
+
+ if (!ret)
+ goto out_release_bus;
+
+ timeout = 200;
+ do {
+ ret = func->hif_block_tx(wilc,
+ VMM_TBL_RX_SHADOW_BASE,
+ (u8 *)vmm_table,
+ ((i + 1) * 4));
+ if (!ret) {
+ PRINT_ER(vif->ndev, "ERR block TX of VMM table.\n");
+ break;
+ }
+
+ if (wilc->chip == WILC_1000) {
+ ret = wilc->hif_func->hif_write_reg(wilc,
+ WILC_HOST_VMM_CTL,
+ 0x2);
+ if (!ret) {
+ PRINT_ER(vif->ndev,
+ "fail write reg host_vmm_ctl..\n");
+ break;
+ }
+
+ do {
+ ret = func->hif_read_reg(wilc,
+ WILC_HOST_VMM_CTL,
+ ®);
+ if (!ret)
+ break;
+ if ((reg >> 2) & 0x1) {
+ entries = ((reg >> 3) & 0x3f);
+ break;
+ }
+ } while (--timeout);
+ } else {
+ ret = func->hif_write_reg(wilc,
+ WILC_HOST_VMM_CTL,
+ 0);
+ if (!ret) {
+ PRINT_ER(vif->ndev,
+ "fail write reg host_vmm_ctl..\n");
+ break;
+ }
+ /* interrupt firmware */
+ ret = func->hif_write_reg(wilc,
+ WILC_INTERRUPT_CORTUS_0,
+ 1);
+ if (!ret) {
+ PRINT_ER(vif->ndev,
+ "fail write reg WILC_INTERRUPT_CORTUS_0..\n");
+ break;
+ }
+
+ do {
+ ret = func->hif_read_reg(wilc,
+ WILC_INTERRUPT_CORTUS_0,
+ ®);
+ if (!ret) {
+ PRINT_ER(vif->ndev,
+ "fail read reg WILC_INTERRUPT_CORTUS_0..\n");
+ break;
+ }
+ if (reg == 0) {
+ // Get the entries
+
+ ret = func->hif_read_reg(wilc,
+ WILC_HOST_VMM_CTL,
+ ®);
+ if (!ret) {
+ PRINT_ER(vif->ndev,
+ "fail read reg host_vmm_ctl..\n");
+ break;
+ }
+ entries = ((reg >> 3) & 0x3f);
+ break;
+ }
+ } while (--timeout);
+ }
+ if (timeout <= 0) {
+ ret = func->hif_write_reg(wilc, WILC_HOST_VMM_CTL, 0x0);
+ break;
+ }
+
+ if (!ret)
+ break;
+
+ if (entries == 0) {
+ PRINT_INFO(vif->ndev, TX_DBG,
+ "no buffer in the chip (reg: %08x), retry later [[ %d, %x ]]\n",
+ reg, i, vmm_table[i-1]);
+ ret = func->hif_read_reg(wilc, WILC_HOST_TX_CTRL, ®);
+ if (!ret) {
+ PRINT_ER(vif->ndev,
+ "fail read reg WILC_HOST_TX_CTRL..\n");
+ break;
+ }
+ reg &= ~BIT(0);
+ ret = func->hif_write_reg(wilc, WILC_HOST_TX_CTRL, reg);
+ if (!ret) {
+ PRINT_ER(vif->ndev,
+ "fail write reg WILC_HOST_TX_CTRL..\n");
+ break;
+ }
+ break;
+ }
+ break;
+ } while (1);
+
+ if (!ret)
+ goto out_release_bus;
+
+ if (entries == 0) {
+ ret = -ENOBUFS;
+ goto out_release_bus;
+ }
+
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+ schedule();
+ offset = 0;
+ i = 0;
+ do {
+ struct txq_entry_t *tqe;
+ u32 header, buffer_offset;
+
+ tqe = wilc_wlan_txq_remove_from_head(dev, vmm_entries_ac[i]);
+ ac_pkt_num_to_chip[vmm_entries_ac[i]]++;
+ if (!tqe)
+ break;
+
+ if (vmm_table[i] == 0)
+ break;
+
+ le32_to_cpus(&vmm_table[i]);
+ vmm_sz = (vmm_table[i] & 0x3ff);
+ vmm_sz *= 4;
+ header = (tqe->type << 31) |
+ (tqe->buffer_size << 15) |
+ vmm_sz;
+ if (tqe->type == WILC_MGMT_PKT)
+ header |= BIT(30);
+ else
+ header &= ~BIT(30);
+
+ cpu_to_le32s(&header);
+ memcpy(&txb[offset], &header, 4);
+ if (tqe->type == WILC_CFG_PKT) {
+ buffer_offset = ETH_CONFIG_PKT_HDR_OFFSET;
+ } else if (tqe->type == WILC_NET_PKT) {
+ char *bssid = ((struct tx_complete_data *)
+ (tqe->priv))->bssid;
+ int prio = tqe->q_num;
+
+ buffer_offset = ETH_ETHERNET_HDR_OFFSET;
+ memcpy(&txb[offset + 4], &prio, sizeof(prio));
+ memcpy(&txb[offset + 8], bssid, 6);
+ } else {
+ buffer_offset = HOST_HDR_OFFSET;
+ }
+
+ memcpy(&txb[offset + buffer_offset],
+ tqe->buffer, tqe->buffer_size);
+ offset += vmm_sz;
+ i++;
+ tqe->status = 1;
+ if (tqe->tx_complete_func)
+ tqe->tx_complete_func(tqe->priv,
+ tqe->status);
+ if (tqe->ack_idx != NOT_TCP_ACK &&
+ tqe->ack_idx < MAX_PENDING_ACKS)
+ vif->ack_filter.pending_acks[tqe->ack_idx].txqe = NULL;
+ kfree(tqe);
+ } while (--entries);
+ for (i = 0; i < NQUEUES; i++)
+ ac_fw_count[i] += ac_pkt_num_to_chip[i];
+
+ acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI);
+
+ ret = func->hif_clear_int_ext(wilc, ENABLE_TX_VMM);
+ if (!ret) {
+ PRINT_ER(vif->ndev, "fail start tx VMM ...\n");
+ goto out_release_bus;
+ }
+
+ ret = func->hif_block_tx_ext(wilc, 0, txb, offset);
+ if (!ret)
+ PRINT_ER(vif->ndev, "fail block tx ext...\n");
+
+out_release_bus:
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+ schedule();
+
+out:
+ mutex_unlock(&wilc->txq_add_to_head_cs);
+
+ PRINT_INFO(vif->ndev, TX_DBG, "THREAD: Exiting txq\n");
+ *txq_count = wilc->txq_entries;
+ if (ret == 1)
+ cfg_packet_timeout = 0;
+ return ret;
+}
+
+static void wilc_wlan_handle_rx_buff(struct wilc *wilc, u8 *buffer, int size)
+{
+ int offset = 0;
+ u32 header;
+ u32 pkt_len, pkt_offset, tp_len;
+ int is_cfg_packet;
+ u8 *buff_ptr;
+ struct wilc_vif *vif = wilc->vif[0];
+
+ do {
+ PRINT_INFO(vif->ndev, RX_DBG, "Handling rx buffer\n");
+ buff_ptr = buffer + offset;
+ memcpy(&header, buff_ptr, 4);
+ le32_to_cpus(&header);
+ PRINT_INFO(vif->ndev, RX_DBG,
+ "Header = %04x - Offset = %d\n", header, offset);
+
+ is_cfg_packet = (header >> 31) & 0x1;
+ pkt_offset = (header >> 22) & 0x1ff;
+ tp_len = (header >> 11) & 0x7ff;
+ pkt_len = header & 0x7ff;
+
+ if (pkt_len == 0 || tp_len == 0) {
+ PRINT_INFO(vif->ndev, RX_DBG,
+ "Data corrupted %d, %d\n",
+ pkt_len, tp_len);
+ break;
+ }
+
+ if (is_cfg_packet) {
+ struct wilc_cfg_rsp rsp;
+
+ buff_ptr += pkt_offset;
+
+ cfg_indicate_rx(wilc, buff_ptr, pkt_len,
+ &rsp);
+ if (rsp.type == WILC_CFG_RSP) {
+ PRINT_INFO(vif->ndev, RX_DBG,
+ "cfg_seq %d rsp.seq %d\n",
+ wilc->cfg_seq_no, rsp.seq_no);
+
+ if (wilc->cfg_seq_no == rsp.seq_no)
+ complete(&wilc->cfg_event);
+ } else if (rsp.type == WILC_CFG_RSP_STATUS) {
+ wilc_mac_indicate(wilc);
+ }
+ } else if (pkt_offset & IS_MANAGMEMENT) {
+ buff_ptr += HOST_HDR_OFFSET;
+ wilc_wfi_mgmt_rx(wilc, buff_ptr, pkt_len);
+ } else if (pkt_offset & IS_MON_PKT) {
+ /* packet received on monitor interface */
+ buff_ptr += HOST_HDR_OFFSET;
+ wilc_wfi_handle_monitor_rx(wilc, buff_ptr, pkt_len);
+ } else if (pkt_len > 0) {
+ struct net_device *wilc_netdev;
+
+ wilc_netdev = get_if_handler(wilc, buff_ptr);
+ if (!wilc_netdev) {
+ PRINT_ER(vif->ndev,
+ "wilc_netdev in wilc is NULL");
+ return;
+ }
+ vif = netdev_priv(wilc_netdev);
+ wilc_frmw_to_host(vif, buff_ptr, pkt_len,
+ pkt_offset, PKT_STATUS_NEW);
+ }
+
+ offset += tp_len;
+ if (offset >= size)
+ break;
+ } while (1);
+}
+
+static void wilc_wlan_handle_rxq(struct wilc *wilc)
+{
+ int size;
+ u8 *buffer;
+ struct rxq_entry_t *rqe;
+ struct wilc_vif *vif = wilc->vif[0];
+
+ do {
+ if (wilc->quit) {
+ PRINT_INFO(vif->ndev, RX_DBG,
+ "Quitting. Exit handle RX queue\n");
+ complete(&wilc->cfg_event);
+ break;
+ }
+ rqe = rxq_remove(wilc);
+ if (!rqe) {
+ PRINT_INFO(vif->ndev, RX_DBG,
+ "nothing in RX queue\n");
+ break;
+ }
+
+ buffer = rqe->buffer;
+ size = rqe->buffer_size;
+ PRINT_INFO(vif->ndev, RX_DBG,
+ "rxQ entery Size = %d - Address = %p\n",
+ size, buffer);
+
+ wilc_wlan_handle_rx_buff(wilc, buffer, size);
+
+ kfree(rqe);
+ } while (1);
+
+ PRINT_INFO(vif->ndev, RX_DBG, "THREAD: Exiting RX thread\n");
+}
+
+static void wilc_unknown_isr_ext(struct wilc *wilc)
+{
+ wilc->hif_func->hif_clear_int_ext(wilc, 0);
+}
+
+static void wilc_wlan_handle_isr_ext(struct wilc *wilc, u32 int_status)
+{
+ u32 offset = wilc->rx_buffer_offset;
+ u8 *buffer = NULL;
+ u32 size;
+ u32 retries = 0;
+ int ret = 0;
+ struct rxq_entry_t *rqe;
+ struct wilc_vif *vif = wilc->vif[0];
+
+ size = (int_status & 0x7fff) << 2;
+
+ while (!size && retries < 10) {
+ PRINT_ER(vif->ndev,
+ "RX Size equal zero Trying to read it again\n");
+ wilc->hif_func->hif_read_size(wilc, &size);
+ size = (size & 0x7fff) << 2;
+ retries++;
+ }
+
+ if (size <= 0)
+ return;
+
+ if (WILC_RX_BUFF_SIZE - offset < size)
+ offset = 0;
+
+ buffer = &wilc->rx_buffer[offset];
+
+ wilc->hif_func->hif_clear_int_ext(wilc, DATA_INT_CLR | ENABLE_RX_VMM);
+
+ ret = wilc->hif_func->hif_block_rx_ext(wilc, 0, buffer, size);
+ if (!ret) {
+ PRINT_ER(vif->ndev, "fail block rx\n");
+ return;
+ }
+
+ offset += size;
+ wilc->rx_buffer_offset = offset;
+ rqe = kmalloc(sizeof(*rqe), GFP_KERNEL);
+ if (!rqe)
+ return;
+
+ rqe->buffer = buffer;
+ rqe->buffer_size = size;
+ PRINT_INFO(vif->ndev, RX_DBG,
+ "rxq entery Size= %d Address= %p\n",
+ rqe->buffer_size, rqe->buffer);
+ rxq_add(wilc, rqe);
+ wilc_wlan_handle_rxq(wilc);
+}
+
+void wilc_handle_isr(struct wilc *wilc)
+{
+ u32 int_status;
+ struct wilc_vif *vif = wilc->vif[0];
+
+ acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI);
+ wilc->hif_func->hif_read_int(wilc, &int_status);
+
+ if (int_status & DATA_INT_EXT)
+ wilc_wlan_handle_isr_ext(wilc, int_status);
+
+ if (!(int_status & (ALL_INT_EXT))) {
+ PRINT_WRN(vif->ndev, TX_DBG, ">> UNKNOWN_INTERRUPT - 0x%08x\n",
+ int_status);
+ wilc_unknown_isr_ext(wilc);
+ }
+
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+}
+
+int wilc_wlan_firmware_download(struct wilc *wilc, const u8 *buffer,
+ u32 buffer_size)
+{
+ u32 offset;
+ u32 addr, size, size2, blksz;
+ u8 *dma_buffer;
+ int ret = 0;
+ u32 reg = 0;
+ struct wilc_vif *vif = wilc->vif[0];
+
+ blksz = BIT(12);
+
+ dma_buffer = kmalloc(blksz, GFP_KERNEL);
+ if (!dma_buffer) {
+ PRINT_ER(vif->ndev,
+ "Can't allocate buffer for fw download IO error\n");
+ return -EIO;
+ }
+
+ offset = 0;
+ PRINT_INFO(vif->ndev, INIT_DBG, "Downloading firmware size = %d\n",
+ buffer_size);
+
+ acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI);
+
+ wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, ®);
+ reg &= ~(1ul << 10);
+ ret = wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg);
+ wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, ®);
+ if ((reg & (1ul << 10)) != 0)
+ PRINT_ER(vif->ndev, "Failed to reset Wifi CPU\n");
+
+ release_bus(wilc, WILC_BUS_RELEASE_ONLY, DEV_WIFI);
+ do {
+ memcpy(&addr, &buffer[offset], 4);
+ memcpy(&size, &buffer[offset + 4], 4);
+ le32_to_cpus(&addr);
+ le32_to_cpus(&size);
+ acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI);
+ offset += 8;
+ while (((int)size) && (offset < buffer_size)) {
+ if (size <= blksz)
+ size2 = size;
+ else
+ size2 = blksz;
+
+ memcpy(dma_buffer, &buffer[offset], size2);
+ ret = wilc->hif_func->hif_block_tx(wilc, addr,
+ dma_buffer, size2);
+ if (!ret)
+ break;
+
+ addr += size2;
+ offset += size2;
+ size -= size2;
+ }
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+
+ if (!ret) {
+ ret = -EIO;
+ PRINT_ER(vif->ndev, "Bus error\n");
+ goto fail;
+ }
+ PRINT_INFO(vif->ndev, INIT_DBG, "Offset = %d\n", offset);
+ } while (offset < buffer_size);
+
+fail:
+
+ kfree(dma_buffer);
+
+ return (ret < 0) ? ret : 0;
+}
+
+int wilc_wlan_start(struct wilc *wilc)
+{
+ u32 reg = 0;
+ int ret;
+ struct wilc_vif *vif = wilc->vif[0];
+
+ if (wilc->io_type == WILC_HIF_SDIO ||
+ wilc->io_type == WILC_HIF_SDIO_GPIO_IRQ)
+ reg |= BIT(3);
+ else if (wilc->io_type == WILC_HIF_SPI)
+ reg = 1;
+
+ acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI);
+ ret = wilc->hif_func->hif_write_reg(wilc, WILC_VMM_CORE_CFG, reg);
+ if (!ret) {
+ PRINT_ER(vif->ndev,
+ "[wilc start]: fail write reg vmm_core_cfg...\n");
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+ return -EIO;
+ }
+ reg = 0;
+ if (wilc->io_type == WILC_HIF_SDIO_GPIO_IRQ)
+ reg |= WILC_HAVE_SDIO_IRQ_GPIO;
+
+ if (wilc->chip == WILC_3000)
+ reg |= WILC_HAVE_SLEEP_CLK_SRC_RTC;
+
+ ret = wilc->hif_func->hif_write_reg(wilc, WILC_GP_REG_1, reg);
+ if (!ret) {
+ PRINT_ER(vif->ndev,
+ "[wilc start]: fail write WILC_GP_REG_1...\n");
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+ return -EIO;
+ }
+
+ wilc->hif_func->hif_sync_ext(wilc, NUM_INT_EXT);
+
+
+ wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, ®);
+ if ((reg & BIT(10)) == BIT(10)) {
+ reg &= ~BIT(10);
+ wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg);
+ wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, ®);
+ }
+
+ reg |= BIT(10);
+ ret = wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg);
+ wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, ®);
+
+ if (ret >= 0)
+ wilc->initialized = 1;
+ else
+ wilc->initialized = 0;
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+
+ return (ret < 0) ? ret : 0;
+}
+
+int wilc_wlan_stop(struct wilc *wilc, struct wilc_vif *vif)
+{
+ u32 reg = 0;
+ int ret;
+ u8 timeout = 10;
+
+ acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI);
+
+ /* Clear Wifi mode*/
+ ret = wilc->hif_func->hif_read_reg(wilc, GLOBAL_MODE_CONTROL, ®);
+ if (!ret) {
+ PRINT_ER(vif->ndev, "Error while reading reg\n");
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+ return ret;
+ }
+
+ reg &= ~BIT(0);
+ ret = wilc->hif_func->hif_write_reg(wilc, GLOBAL_MODE_CONTROL, reg);
+ if (!ret) {
+ PRINT_ER(vif->ndev, "Error while writing reg\n");
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+ return ret;
+ }
+
+ /* Configure the power sequencer to ignore WIFI sleep signal on making
+ * chip sleep decision
+ */
+ ret = wilc->hif_func->hif_read_reg(wilc, PWR_SEQ_MISC_CTRL, ®);
+ if (!ret) {
+ PRINT_ER(vif->ndev, "Error while reading reg\n");
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+ return ret;
+ }
+
+ reg &= ~BIT(28);
+ ret = wilc->hif_func->hif_write_reg(wilc, PWR_SEQ_MISC_CTRL, reg);
+ if (!ret) {
+ PRINT_ER(vif->ndev, "Error while writing reg\n");
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+ return ret;
+ }
+
+ ret = wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, ®);
+ if (!ret) {
+ PRINT_ER(vif->ndev, "Error while reading reg\n");
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+ return ret;
+ }
+
+ reg &= ~BIT(10);
+ ret = wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg);
+ if (!ret) {
+ PRINT_ER(vif->ndev, "Error while writing reg\n");
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+ return ret;
+ }
+
+ do {
+ ret = wilc->hif_func->hif_read_reg(wilc,
+ WILC_GLB_RESET_0, ®);
+ if (!ret) {
+ PRINT_ER(vif->ndev, "Error while reading reg\n");
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP,
+ DEV_WIFI);
+ return ret;
+ }
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Read RESET Reg %x : Retry%d\n", reg, timeout);
+ if ((reg & BIT(10))) {
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Bit 10 not reset : Retry %d\n", timeout);
+ reg &= ~BIT(10);
+ ret = wilc->hif_func->hif_write_reg(wilc,
+ WILC_GLB_RESET_0,
+ reg);
+ timeout--;
+ } else {
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Bit 10 reset after : Retry %d\n", timeout);
+ ret = wilc->hif_func->hif_read_reg(wilc,
+ WILC_GLB_RESET_0,
+ ®);
+ if (!ret) {
+ PRINT_ER(vif->ndev, "Error reading reg\n");
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP,
+ DEV_WIFI);
+ return ret;
+ }
+ PRINT_INFO(vif->ndev, GENERIC_DBG,
+ "Read RESET Reg %x : Retry%d\n", reg,
+ timeout);
+ break;
+ }
+
+ } while (timeout);
+
+ if (wilc->chip == WILC_1000) {
+ reg = (BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(8) | BIT(9) |
+ BIT(26) | BIT(29) | BIT(30) | BIT(31));
+ } else {
+ reg = (BIT(0) | BIT(2) | BIT(3) | BIT(8) | BIT(9) |
+ BIT(20) | BIT(26) | BIT(29) | BIT(30) | BIT(31));
+ }
+
+ wilc->hif_func->hif_read_reg(wilc, WILC_FW_HOST_COMM, ®);
+ reg = BIT(0);
+
+ ret = wilc->hif_func->hif_write_reg(wilc, WILC_FW_HOST_COMM, reg);
+
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+
+ return ret;
+}
+
+void wilc_wlan_cleanup(struct net_device *dev)
+{
+ struct txq_entry_t *tqe;
+ struct rxq_entry_t *rqe;
+ u8 ac;
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wilc = vif->wilc;
+
+ wilc->quit = 1;
+ for (ac = 0; ac < NQUEUES; ac++) {
+ do {
+ tqe = wilc_wlan_txq_remove_from_head(dev, ac);
+ if (!tqe)
+ break;
+ if (tqe->tx_complete_func)
+ tqe->tx_complete_func(tqe->priv, 0);
+ kfree(tqe);
+ } while (1);
+ }
+
+ do {
+ rqe = rxq_remove(wilc);
+ if (!rqe)
+ break;
+ kfree(rqe);
+ } while (1);
+
+ kfree(wilc->rx_buffer);
+ wilc->rx_buffer = NULL;
+ kfree(wilc->tx_buffer);
+ wilc->tx_buffer = NULL;
+}
+
+static int wilc_wlan_cfg_commit(struct wilc_vif *vif, int type,
+ u32 drv_handler)
+{
+ struct wilc *wilc = vif->wilc;
+ struct wilc_cfg_frame *cfg = &wilc->cfg_frame;
+ int t_len = wilc->cfg_frame_offset + sizeof(struct wilc_cfg_cmd_hdr);
+
+ if (type == WILC_CFG_SET)
+ cfg->hdr.cmd_type = 'W';
+ else
+ cfg->hdr.cmd_type = 'Q';
+
+ cfg->hdr.seq_no = wilc->cfg_seq_no % 256;
+ cfg->hdr.total_len = cpu_to_le16(t_len);
+ cfg->hdr.driver_handler = cpu_to_le32(drv_handler);
+ wilc->cfg_seq_no = cfg->hdr.seq_no;
+
+ if (!wilc_wlan_txq_add_cfg_pkt(vif, (u8 *)&cfg->hdr, t_len))
+ return -1;
+
+ return 0;
+}
+
+int cfg_set(struct wilc_vif *vif, int start, u16 wid, u8 *buffer,
+ u32 buffer_size, int commit, u32 drv_handler)
+{
+ u32 offset;
+ int ret_size;
+ struct wilc *wilc = vif->wilc;
+
+ mutex_lock(&wilc->cfg_cmd_lock);
+
+ if (start)
+ wilc->cfg_frame_offset = 0;
+
+ offset = wilc->cfg_frame_offset;
+ ret_size = cfg_set_wid(vif, wilc->cfg_frame.frame, offset,
+ wid, buffer, buffer_size);
+ offset += ret_size;
+ wilc->cfg_frame_offset = offset;
+
+ if (!commit) {
+ mutex_unlock(&wilc->cfg_cmd_lock);
+ return ret_size;
+ }
+
+ PRINT_INFO(vif->ndev, TX_DBG,
+ "[WILC]PACKET Commit with sequence number%d\n",
+ wilc->cfg_seq_no);
+
+ if (wilc_wlan_cfg_commit(vif, WILC_CFG_SET, drv_handler))
+ ret_size = 0;
+
+ if (!wait_for_completion_timeout(&wilc->cfg_event,
+ WILC_CFG_PKTS_TIMEOUT)) {
+ PRINT_ER(vif->ndev, "Timed Out\n");
+ ret_size = 0;
+ }
+
+ wilc->cfg_frame_offset = 0;
+ wilc->cfg_seq_no += 1;
+ mutex_unlock(&wilc->cfg_cmd_lock);
+
+ return ret_size;
+}
+
+int cfg_get(struct wilc_vif *vif, int start, u16 wid, int commit,
+ u32 drv_handler)
+{
+ u32 offset;
+ int ret_size;
+ struct wilc *wilc = vif->wilc;
+
+ mutex_lock(&wilc->cfg_cmd_lock);
+
+ if (start)
+ wilc->cfg_frame_offset = 0;
+
+ offset = wilc->cfg_frame_offset;
+ ret_size = cfg_get_wid(wilc->cfg_frame.frame, offset, wid);
+ offset += ret_size;
+ wilc->cfg_frame_offset = offset;
+
+ if (!commit) {
+ mutex_unlock(&wilc->cfg_cmd_lock);
+ return ret_size;
+ }
+
+ if (wilc_wlan_cfg_commit(vif, WILC_CFG_QUERY, drv_handler))
+ ret_size = 0;
+
+ if (!wait_for_completion_timeout(&wilc->cfg_event,
+ WILC_CFG_PKTS_TIMEOUT)) {
+ PRINT_INFO(vif->ndev, TX_DBG, "Timed Out\n");
+ ret_size = 0;
+ }
+ PRINT_INFO(vif->ndev, TX_DBG, "Get Response received\n");
+ wilc->cfg_frame_offset = 0;
+ wilc->cfg_seq_no += 1;
+ mutex_unlock(&wilc->cfg_cmd_lock);
+
+ return ret_size;
+}
+
+int cfg_get_val(struct wilc *wl, u16 wid, u8 *buffer, u32 buffer_size)
+{
+ return cfg_get_wid_value(wl, wid, buffer, buffer_size);
+}
+unsigned int cfg_packet_timeout;
+int wilc_send_config_pkt(struct wilc_vif *vif, u8 mode, struct wid *wids,
+ u32 count, u32 drv)
+{
+ int i;
+ int ret = 0;
+
+ if (wait_for_recovery) {
+ PRINT_INFO(vif->ndev, CORECONFIG_DBG,
+ "Host interface is suspended\n");
+ while (wait_for_recovery)
+ msleep(300);
+ PRINT_INFO(vif->ndev, CORECONFIG_DBG,
+ "Host interface is resumed\n");
+ }
+
+ if (mode == WILC_GET_CFG) {
+ for (i = 0; i < count; i++) {
+ PRINT_D(vif->ndev, CORECONFIG_DBG,
+ "Sending CFG packet [%d][%d]\n", !i,
+ (i == count - 1));
+ if (!cfg_get(vif, !i, wids[i].id, (i == count - 1),
+ drv)) {
+ ret = -ETIMEDOUT;
+ PRINT_ER(vif->ndev, "Get Timed out\n");
+ break;
+ }
+ }
+ for (i = 0; i < count; i++) {
+ wids[i].size = cfg_get_val(vif->wilc, wids[i].id,
+ wids[i].val,
+ wids[i].size);
+ }
+ } else if (mode == WILC_SET_CFG) {
+ for (i = 0; i < count; i++) {
+ PRINT_INFO(vif->ndev, CORECONFIG_DBG,
+ "Sending config SET PACKET WID:%x\n",
+ wids[i].id);
+ if (!cfg_set(vif, !i, wids[i].id, wids[i].val,
+ wids[i].size, (i == count - 1), drv)) {
+ ret = -ETIMEDOUT;
+ PRINT_ER(vif->ndev, "Set Timed out\n");
+ break;
+ }
+ }
+ }
+ cfg_packet_timeout = (ret < 0) ? cfg_packet_timeout + 1 : 0;
+ return ret;
+}
+
+static u32 init_chip(struct net_device *dev)
+{
+ u32 chipid;
+ u32 reg, ret = 0;
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wilc = vif->wilc;
+
+ acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI);
+
+ chipid = wilc_get_chipid(wilc, true);
+
+ ret = wilc->hif_func->hif_read_reg(wilc, 0x1118, ®);
+ if (!ret) {
+ PRINT_ER(vif->ndev, "fail read reg 0x1118\n");
+ goto end;
+ }
+
+ reg |= BIT(0);
+ ret = wilc->hif_func->hif_write_reg(wilc, 0x1118, reg);
+ if (!ret) {
+ PRINT_ER(vif->ndev, "fail write reg 0x1118\n");
+ goto end;
+ }
+ ret = wilc->hif_func->hif_write_reg(wilc, 0xc0000, 0x71);
+ if (!ret) {
+ PRINT_ER(vif->ndev, "fail write reg 0xc0000 ...\n");
+ goto end;
+ }
+
+ if (wilc->chip == WILC_3000) {
+ ret = wilc->hif_func->hif_read_reg(wilc, 0x207ac, ®);
+ PRINT_INFO(vif->ndev, INIT_DBG, "Bootrom sts = %x\n", reg);
+ ret = wilc->hif_func->hif_write_reg(wilc, 0x4f0000,
+ 0x71);
+ if (!ret) {
+ PRINT_ER(vif->ndev, "fail write reg 0x4f0000 ...\n");
+ goto end;
+ }
+ }
+
+end:
+ release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+
+ return ret;
+}
+
+u32 wilc_get_chipid(struct wilc *wilc, bool update)
+{
+ static u32 chipid;
+ int ret;
+ u32 tempchipid = 0;
+
+ if (chipid == 0 || update) {
+ ret = wilc->hif_func->hif_read_reg(wilc, 0x3b0000,
+ &tempchipid);
+ if (!ret)
+ pr_err("[wilc start]: fail read reg 0x3b0000\n");
+ if (!is_wilc3000(tempchipid)) {
+ wilc->hif_func->hif_read_reg(wilc, 0x1000,
+ &tempchipid);
+ if (!is_wilc1000(tempchipid)) {
+ chipid = 0;
+ return chipid;
+ }
+ if (tempchipid < 0x1003a0) {
+ pr_err("WILC1002 isn't suported %x\n", chipid);
+ chipid = 0;
+ return chipid;
+ }
+ }
+ chipid = tempchipid;
+ }
+
+ return chipid;
+}
+
+int wilc_wlan_init(struct net_device *dev)
+{
+ int ret = 0;
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wilc;
+
+ wilc = vif->wilc;
+
+ wilc->quit = 0;
+
+ PRINT_INFO(vif->ndev, INIT_DBG, "Initializing WILC_Wlan\n");
+
+ if (!wilc->hif_func->hif_is_init(wilc)) {
+ acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY, DEV_WIFI);
+ if (!wilc->hif_func->hif_init(wilc, false)) {
+ ret = -EIO;
+ release_bus(wilc, WILC_BUS_RELEASE_ONLY, DEV_WIFI);
+ goto fail;
+ }
+ release_bus(wilc, WILC_BUS_RELEASE_ONLY, DEV_WIFI);
+ }
+
+ if (!wilc->tx_buffer)
+ wilc->tx_buffer = kmalloc(WILC_TX_BUFF_SIZE, GFP_KERNEL);
+
+ if (!wilc->tx_buffer) {
+ ret = -ENOBUFS;
+ PRINT_ER(vif->ndev, "Can't allocate Tx Buffer");
+ goto fail;
+ }
+
+ if (!wilc->rx_buffer)
+ wilc->rx_buffer = kmalloc(WILC_RX_BUFF_SIZE, GFP_KERNEL);
+ PRINT_D(vif->ndev, TX_DBG, "g_wlan.rx_buffer =%p\n", wilc->rx_buffer);
+ if (!wilc->rx_buffer) {
+ ret = -ENOBUFS;
+ PRINT_ER(vif->ndev, "Can't allocate Rx Buffer");
+ goto fail;
+ }
+
+ if (!init_chip(dev)) {
+ ret = -EIO;
+ goto fail;
+ }
+
+ return 1;
+
+fail:
+
+ kfree(wilc->rx_buffer);
+ wilc->rx_buffer = NULL;
+ kfree(wilc->tx_buffer);
+ wilc->tx_buffer = NULL;
+
+ return ret;
+}
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#ifndef WILC_WLAN_H
+#define WILC_WLAN_H
+
+#include <linux/types.h>
+#include <linux/version.h>
+
+static inline bool is_wilc1000(u32 id)
+{
+ return (id & 0xfffff000) == 0x100000;
+}
+
+static inline bool is_wilc3000(u32 id)
+{
+ return (id & 0xfffff000) == 0x300000;
+}
+
+/********************************************
+ *
+ * Mac eth header length
+ *
+ ********************************************/
+#define MAX_MAC_HDR_LEN 26 /* QOS_MAC_HDR_LEN */
+#define SUB_MSDU_HEADER_LENGTH 14
+#define SNAP_HDR_LEN 8
+#define ETHERNET_HDR_LEN 14
+#define WORD_ALIGNMENT_PAD 0
+
+#define ETH_ETHERNET_HDR_OFFSET (MAX_MAC_HDR_LEN + \
+ SUB_MSDU_HEADER_LENGTH + \
+ SNAP_HDR_LEN - \
+ ETHERNET_HDR_LEN + \
+ WORD_ALIGNMENT_PAD)
+
+#define HOST_HDR_OFFSET 4
+#define ETHERNET_HDR_LEN 14
+#define IP_HDR_LEN 20
+#define IP_HDR_OFFSET ETHERNET_HDR_LEN
+#define UDP_HDR_OFFSET (IP_HDR_LEN + IP_HDR_OFFSET)
+#define UDP_HDR_LEN 8
+#define UDP_DATA_OFFSET (UDP_HDR_OFFSET + UDP_HDR_LEN)
+#define ETH_CONFIG_PKT_HDR_LEN UDP_DATA_OFFSET
+
+#define ETH_CONFIG_PKT_HDR_OFFSET (ETH_ETHERNET_HDR_OFFSET + \
+ ETH_CONFIG_PKT_HDR_LEN)
+#define PKT_STATUS_NEW 0
+#define PKT_STATUS_BUFFERED 1
+
+/********************************************
+ *
+ * Register Defines
+ *
+ ********************************************/
+#define WILC_PERIPH_REG_BASE 0x1000
+#define WILC_CHIPID WILC_PERIPH_REG_BASE
+#define WILC_GLB_RESET_0 (WILC_PERIPH_REG_BASE + 0x400)
+#define WILC_PIN_MUX_0 (WILC_PERIPH_REG_BASE + 0x408)
+#define WILC_HOST_TX_CTRL (WILC_PERIPH_REG_BASE + 0x6c)
+#define WILC_HOST_RX_CTRL_0 (WILC_PERIPH_REG_BASE + 0x70)
+#define WILC_HOST_RX_CTRL_1 (WILC_PERIPH_REG_BASE + 0x74)
+#define WILC_HOST_VMM_CTL (WILC_PERIPH_REG_BASE + 0x78)
+#define WILC_HOST_RX_CTRL (WILC_PERIPH_REG_BASE + 0x80)
+#define WILC_HOST_RX_EXTRA_SIZE (WILC_PERIPH_REG_BASE + 0x84)
+#define WILC_HOST_TX_CTRL_1 (WILC_PERIPH_REG_BASE + 0x88)
+#define WILC_INTERRUPT_CORTUS_0 (WILC_PERIPH_REG_BASE + 0xa8)
+#define WILC_MISC (WILC_PERIPH_REG_BASE + 0x428)
+#define WILC_INTR_REG_BASE (WILC_PERIPH_REG_BASE + 0xa00)
+#define WILC_INTR_ENABLE WILC_INTR_REG_BASE
+#define WILC_INTR2_ENABLE (WILC_INTR_REG_BASE + 4)
+
+#define WILC_INTR_POLARITY (WILC_INTR_REG_BASE + 0x10)
+#define WILC_INTR_TYPE (WILC_INTR_REG_BASE + 0x20)
+#define WILC_INTR_CLEAR (WILC_INTR_REG_BASE + 0x30)
+#define WILC_INTR_STATUS (WILC_INTR_REG_BASE + 0x40)
+
+#define WILC_VMM_TBL_SIZE 64
+#define WILC_VMM_TX_TBL_BASE 0x150400
+#define WILC_VMM_RX_TBL_BASE 0x150500
+
+#define WILC_VMM_BASE 0x150000
+#define WILC_VMM_CORE_CTL WILC_VMM_BASE
+#define WILC_VMM_TBL_CTL (WILC_VMM_BASE + 0x4)
+#define WILC_VMM_TBL_ENTRY (WILC_VMM_BASE + 0x8)
+#define WILC_VMM_TBL0_SIZE (WILC_VMM_BASE + 0xc)
+#define WILC_VMM_TO_HOST_SIZE (WILC_VMM_BASE + 0x10)
+#define WILC_VMM_CORE_CFG (WILC_VMM_BASE + 0x14)
+#define WILC_VMM_TBL_ACTIVE (WILC_VMM_BASE + 040)
+#define WILC_VMM_TBL_STATUS (WILC_VMM_BASE + 0x44)
+
+#define WILC_SPI_REG_BASE 0xe800
+#define WILC_SPI_CTL WILC_SPI_REG_BASE
+#define WILC_SPI_MASTER_DMA_ADDR (WILC_SPI_REG_BASE + 0x4)
+#define WILC_SPI_MASTER_DMA_COUNT (WILC_SPI_REG_BASE + 0x8)
+#define WILC_SPI_SLAVE_DMA_ADDR (WILC_SPI_REG_BASE + 0xc)
+#define WILC_SPI_SLAVE_DMA_COUNT (WILC_SPI_REG_BASE + 0x10)
+#define WILC_SPI_TX_MODE (WILC_SPI_REG_BASE + 0x20)
+#define WILC_SPI_PROTOCOL_CONFIG (WILC_SPI_REG_BASE + 0x24)
+#define WILC_SPI_INTR_CTL (WILC_SPI_REG_BASE + 0x2c)
+
+#define WILC_SPI_PROTOCOL_OFFSET (WILC_SPI_PROTOCOL_CONFIG - \
+ WILC_SPI_REG_BASE)
+
+#define WILC_AHB_DATA_MEM_BASE 0x30000
+#define WILC_AHB_SHARE_MEM_BASE 0xd0000
+
+#define VMM_TBL_RX_SHADOW_BASE WILC_AHB_SHARE_MEM_BASE
+#define VMM_TBL_RX_SHADOW_SIZE 256
+
+#define WILC_FW_HOST_COMM 0x13c0
+#define WILC_GP_REG_0 0x149c
+#define WILC_GP_REG_1 0x14a0
+
+#define WILC_COEXIST_CTL 0x161E00
+#define GLOBAL_MODE_CONTROL 0x1614
+#define PWR_SEQ_MISC_CTRL 0x3008
+#define COE_AUTO_PS_ON_NULL_PKT 0x160468
+#define COE_AUTO_PS_OFF_NULL_PKT 0x16046C
+#define CCA_CTL_2 (0x160EF4)
+#define CCA_CTL_7 (0x160F08)
+
+#define WILC_HAVE_SDIO_IRQ_GPIO BIT(0)
+#define WILC_HAVE_SLEEP_CLK_SRC_RTC BIT(2)
+#define WILC_HAVE_SLEEP_CLK_SRC_XO BIT(3)
+
+/********************************************
+ *
+ * Wlan Defines
+ *
+ ********************************************/
+#define WILC_CFG_PKT 1
+#define WILC_NET_PKT 0
+#define WILC_MGMT_PKT 2
+
+#define WILC_CFG_SET 1
+#define WILC_CFG_QUERY 0
+
+#define WILC_CFG_RSP 1
+#define WILC_CFG_RSP_STATUS 2
+#define WILC_CFG_RSP_SCAN 3
+#define ABORT_INT BIT(31)
+
+#define WILC_RX_BUFF_SIZE (96 * 1024)
+#define WILC_TX_BUFF_SIZE (64 * 1024)
+
+#define MODALIAS "WILC_SPI"
+#define GPIO_NUM 0x5B
+#define GPIO_NUM_CHIP_EN 94
+#define GPIO_NUM_RESET 60
+
+#define NQUEUES 4
+#define VO_AC_COUNT_POS 25
+#define VO_AC_ACM_STAT_POS 24
+#define VI_AC_COUNT_POS 17
+#define VI_AC_ACM_STAT_POS 16
+#define BE_AC_COUNT_POS 9
+#define BE_AC_ACM_STAT_POS 8
+#define BK_AC_COUNT_POS 2
+#define BK_AC_ACM_STAT_POS 1
+#define AC_BUFFER_SIZE 1000
+/*******************************************/
+/* E0 and later Interrupt flags. */
+/*******************************************/
+/*******************************************/
+/* E0 and later Interrupt flags. */
+/* IRQ Status word */
+/* 15:0 = DMA count in words. */
+/* 16: INT0 flag */
+/* 17: INT1 flag */
+/* 18: INT2 flag */
+/* 19: INT3 flag */
+/* 20: INT4 flag */
+/* 21: INT5 flag */
+/*******************************************/
+#define IRG_FLAGS_OFFSET 16
+#define IRQ_DMA_WD_CNT_MASK ((1ul << IRG_FLAGS_OFFSET) - 1)
+#define INT_0 BIT(IRG_FLAGS_OFFSET)
+#define INT_1 BIT(IRG_FLAGS_OFFSET + 1)
+#define INT_2 BIT(IRG_FLAGS_OFFSET + 2)
+#define INT_3 BIT(IRG_FLAGS_OFFSET + 3)
+#define INT_4 BIT(IRG_FLAGS_OFFSET + 4)
+#define MAX_NUM_INT 5
+
+/*******************************************/
+/* E0 and later Interrupt flags. */
+/* IRQ Clear word */
+/* 0: Clear INT0 */
+/* 1: Clear INT1 */
+/* 2: Clear INT2 */
+/* 3: Clear INT3 */
+/* 4: Clear INT4 */
+/* 5: Clear INT5 */
+/* 6: Select VMM table 1 */
+/* 7: Select VMM table 2 */
+/* 8: Enable VMM */
+/*******************************************/
+#define CLR_INT0 BIT(0)
+#define CLR_INT1 BIT(1)
+#define CLR_INT2 BIT(2)
+#define CLR_INT3 BIT(3)
+#define CLR_INT4 BIT(4)
+#define CLR_INT5 BIT(5)
+#define SEL_VMM_TBL0 BIT(6)
+#define SEL_VMM_TBL1 BIT(7)
+#define EN_VMM BIT(8)
+
+#define DATA_INT_EXT INT_0
+#define ALL_INT_EXT (DATA_INT_EXT)
+#define NUM_INT_EXT 1
+
+#define DATA_INT_CLR CLR_INT0
+
+#define ENABLE_RX_VMM (SEL_VMM_TBL1 | EN_VMM)
+#define ENABLE_TX_VMM (SEL_VMM_TBL0 | EN_VMM)
+/*time for expiring the completion of cfg packets*/
+#define WILC_CFG_PKTS_TIMEOUT msecs_to_jiffies(3000)
+
+#define IS_MANAGMEMENT 0x100
+#define IS_MANAGMEMENT_CALLBACK 0x080
+#define IS_MGMT_STATUS_SUCCES 0x040
+#define IS_MON_PKT 0x020
+
+/********************************************
+ *
+ * Tx/Rx Queue Structure
+ *
+ ********************************************/
+
+enum ip_pkt_priority {
+ AC_VO_Q = 0,
+ AC_VI_Q = 1,
+ AC_BE_Q = 2,
+ AC_BK_Q = 3
+};
+
+struct txq_entry_t {
+ struct list_head list;
+ int type;
+ u8 q_num;
+ int ack_idx;
+ u8 *buffer;
+ int buffer_size;
+ void *priv;
+ int status;
+ void (*tx_complete_func)(void *priv, int status);
+};
+
+struct txq_handle {
+ struct txq_entry_t txq_head;
+ u16 count;
+ u8 acm;
+};
+
+struct rxq_entry_t {
+ struct list_head list;
+ u8 *buffer;
+ int buffer_size;
+};
+
+enum wilc_chip_type {
+ WILC_1000,
+ WILC_3000,
+};
+
+/********************************************
+ *
+ * Host IF Structure
+ *
+ ********************************************/
+struct wilc;
+struct wilc_hif_func {
+ int (*hif_init)(struct wilc *wilc, bool resume);
+ int (*hif_deinit)(struct wilc *wilc);
+ int (*hif_read_reg)(struct wilc *wilc, u32 addr, u32 *data);
+ int (*hif_write_reg)(struct wilc *wilc, u32 addr, u32 data);
+ int (*hif_block_rx)(struct wilc *wilc, u32 addr, u8 *buf, u32 size);
+ int (*hif_block_tx)(struct wilc *wilc, u32 addr, u8 *buf, u32 size);
+ int (*hif_read_int)(struct wilc *wilc, u32 *int_status);
+ int (*hif_clear_int_ext)(struct wilc *wilc, u32 val);
+ int (*hif_read_size)(struct wilc *wilc, u32 *size);
+ int (*hif_block_tx_ext)(struct wilc *wilc, u32 addr, u8 *buf, u32 size);
+ int (*hif_block_rx_ext)(struct wilc *wilc, u32 addr, u8 *buf, u32 size);
+ int (*hif_sync_ext)(struct wilc *wilc, int nint);
+ int (*enable_interrupt)(struct wilc *nic);
+ void (*disable_interrupt)(struct wilc *nic);
+ int (*hif_reset)(struct wilc *wilc);
+ bool (*hif_is_init)(struct wilc *wilc);
+};
+
+#define WILC_MAX_CFG_FRAME_SIZE 1468
+
+struct tx_complete_data {
+ int size;
+ void *buff;
+ u8 *bssid;
+ struct sk_buff *skb;
+ struct wilc_vif *vif;
+};
+
+struct wilc_cfg_cmd_hdr {
+ u8 cmd_type;
+ u8 seq_no;
+ __le16 total_len;
+ __le32 driver_handler;
+};
+
+struct wilc_cfg_frame {
+ struct wilc_cfg_cmd_hdr hdr;
+ u8 frame[WILC_MAX_CFG_FRAME_SIZE];
+};
+
+struct wilc_cfg_rsp {
+ u8 type;
+ u8 seq_no;
+};
+
+struct wilc;
+struct wilc_vif;
+
+int wilc_wlan_firmware_download(struct wilc *wilc, const u8 *buffer,
+ u32 buffer_size);
+int wilc_wlan_start(struct wilc *wilc);
+int wilc_wlan_stop(struct wilc *wilc, struct wilc_vif *vif);
+int txq_add_net_pkt(struct net_device *dev, void *priv, u8 *buffer,
+ u32 buffer_size,
+ void (*tx_complete_fn)(void *, int));
+int wilc_wlan_handle_txq(struct net_device *dev, u32 *txq_count);
+void wilc_handle_isr(struct wilc *wilc);
+void wilc_wlan_cleanup(struct net_device *dev);
+int cfg_set(struct wilc_vif *vif, int start, u16 wid, u8 *buffer,
+ u32 buffer_size, int commit, u32 drv_handler);
+int cfg_get(struct wilc_vif *vif, int start, u16 wid, int commit,
+ u32 drv_handler);
+int cfg_get_val(struct wilc *wl, u16 wid, u8 *buffer, u32 buffer_size);
+int txq_add_mgmt_pkt(struct net_device *dev, void *priv, u8 *buffer,
+ u32 buffer_size,
+ void (*tx_complete_fn)(void *, int));
+
+void wilc_enable_tcp_ack_filter(struct wilc_vif *vif, bool value);
+int wilc_wlan_get_num_conn_ifcs(struct wilc *wilc);
+netdev_tx_t wilc_mac_xmit(struct sk_buff *skb, struct net_device *dev);
+
+void wilc_wfi_p2p_rx(struct net_device *dev, u8 *buff, u32 size);
+void host_wakeup_notify(struct wilc *wilc, int source);
+void host_sleep_notify(struct wilc *wilc, int source);
+void chip_allow_sleep(struct wilc *wilc, int source);
+void chip_wakeup(struct wilc *wilc, int source);
+int wilc_send_config_pkt(struct wilc_vif *vif, u8 mode, struct wid *wids,
+ u32 count, u32 drv);
+void wilc_wlan_power_on_sequence(struct wilc *wilc);
+void wilc_wlan_power_off_sequence(struct wilc *wilc);
+
+void wilc_bt_init(struct wilc *wilc);
+void wilc_bt_deinit(void);
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+void eap_buff_timeout(struct timer_list *t);
+#else
+void eap_buff_timeout(unsigned long user);
+#endif
+void acquire_bus(struct wilc *wilc, enum bus_acquire acquire, int source);
+void release_bus(struct wilc *wilc, enum bus_release release, int source);
+int wilc_wlan_init(struct net_device *dev);
+u32 wilc_get_chipid(struct wilc *wilc, bool update);
+void wilc_wfi_handle_monitor_rx(struct wilc *wilc, u8 *buff, u32 size);
+#endif
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include "wilc_wlan_if.h"
+#include "wilc_wlan.h"
+#include "wilc_wlan_cfg.h"
+#include "wilc_wfi_netdevice.h"
+
+enum cfg_cmd_type {
+ CFG_BYTE_CMD = 0,
+ CFG_HWORD_CMD = 1,
+ CFG_WORD_CMD = 2,
+ CFG_STR_CMD = 3,
+ CFG_BIN_CMD = 4
+};
+
+static struct wilc_cfg_byte g_cfg_byte[] = {
+ {WID_STATUS, 0},
+ {WID_RSSI, 0},
+ {WID_LINKSPEED, 0},
+ {WID_TX_POWER, 0},
+ {WID_WOWLAN_TRIGGER, 0},
+ {WID_NIL, 0}
+};
+
+static struct wilc_cfg_hword g_cfg_hword[] = {
+ {WID_NIL, 0}
+};
+
+static struct wilc_cfg_word g_cfg_word[] = {
+ {WID_FAILED_COUNT, 0},
+ {WID_RECEIVED_FRAGMENT_COUNT, 0},
+ {WID_SUCCESS_FRAME_COUNT, 0},
+ {WID_GET_INACTIVE_TIME, 0},
+ {WID_NIL, 0}
+
+};
+
+static struct wilc_cfg_str g_cfg_str[] = {
+ {WID_FIRMWARE_VERSION, NULL},
+ {WID_MAC_ADDR, NULL},
+ {WID_ASSOC_RES_INFO, NULL},
+ {WID_NIL, NULL}
+};
+
+static struct wilc_cfg_bin g_cfg_bin[] = {
+ {WID_ANTENNA_SELECTION, NULL},
+ {WID_NIL, NULL}
+};
+
+/********************************************
+ *
+ * Configuration Functions
+ *
+ ********************************************/
+
+static int wilc_wlan_cfg_set_byte(u8 *frame, u32 offset, u16 id, u8 val8)
+{
+ u8 *buf;
+
+ if ((offset + 4) >= WILC_MAX_CFG_FRAME_SIZE)
+ return 0;
+
+ buf = &frame[offset];
+
+ buf[0] = (u8)id;
+ buf[1] = (u8)(id >> 8);
+ buf[2] = 1;
+ buf[3] = 0;
+ buf[4] = val8;
+ return 5;
+}
+
+static int wilc_wlan_cfg_set_hword(u8 *frame, u32 offset, u16 id, u16 val16)
+{
+ u8 *buf;
+
+ if ((offset + 5) >= WILC_MAX_CFG_FRAME_SIZE)
+ return 0;
+
+ buf = &frame[offset];
+
+ buf[0] = (u8)id;
+ buf[1] = (u8)(id >> 8);
+ buf[2] = 2;
+ buf[3] = 0;
+ buf[4] = (u8)val16;
+ buf[5] = (u8)(val16 >> 8);
+
+ return 6;
+}
+
+static int wilc_wlan_cfg_set_word(u8 *frame, u32 offset, u16 id, u32 val32)
+{
+ u8 *buf;
+
+ if ((offset + 7) >= WILC_MAX_CFG_FRAME_SIZE)
+ return 0;
+
+ buf = &frame[offset];
+
+ buf[0] = (u8)id;
+ buf[1] = (u8)(id >> 8);
+ buf[2] = 4;
+ buf[3] = 0;
+ buf[4] = (u8)val32;
+ buf[5] = (u8)(val32 >> 8);
+ buf[6] = (u8)(val32 >> 16);
+ buf[7] = (u8)(val32 >> 24);
+
+ return 8;
+}
+
+static int wilc_wlan_cfg_set_str(u8 *frame, u32 offset, u16 id, u8 *str,
+ u32 size)
+{
+ u8 *buf;
+
+ if ((offset + size + 4) >= WILC_MAX_CFG_FRAME_SIZE)
+ return 0;
+
+ buf = &frame[offset];
+
+ buf[0] = (u8)id;
+ buf[1] = (u8)(id >> 8);
+ buf[2] = (u8)size;
+ buf[3] = (u8)(size >> 8);
+
+ if (str && size != 0)
+ memcpy(&buf[4], str, size);
+
+ return (size + 4);
+}
+
+static int wilc_wlan_cfg_set_bin(u8 *frame, u32 offset, u16 id, u8 *b, u32 size)
+{
+ u8 *buf;
+ u32 i;
+ u8 checksum = 0;
+
+ if ((offset + size + 5) >= WILC_MAX_CFG_FRAME_SIZE)
+ return 0;
+
+ buf = &frame[offset];
+ buf[0] = (u8)id;
+ buf[1] = (u8)(id >> 8);
+ buf[2] = (u8)size;
+ buf[3] = (u8)(size >> 8);
+
+ if ((b) && size != 0) {
+ memcpy(&buf[4], b, size);
+ for (i = 0; i < size; i++)
+ checksum += buf[i + 4];
+ }
+
+ buf[size + 4] = checksum;
+
+ return (size + 5);
+}
+
+/********************************************
+ *
+ * Configuration Response Functions
+ *
+ ********************************************/
+
+#define GET_WID_TYPE(wid) (((wid) >> 12) & 0x7)
+static void wilc_wlan_parse_response_frame(struct wilc *wl, u8 *info,
+ int size)
+{
+ u16 wid;
+ u32 len = 0, i = 0;
+ struct wilc_vif *vif = wl->vif[0];
+
+ while (size > 0) {
+ i = 0;
+ wid = get_unaligned_le16(info);
+
+ PRINT_D(vif->ndev, GENERIC_DBG, "Processing response for %d\n",
+ wid);
+ switch (GET_WID_TYPE(wid)) {
+ case WID_CHAR:
+ do {
+ if (wl->cfg.b[i].id == WID_NIL)
+ break;
+
+ if (wl->cfg.b[i].id == wid) {
+ wl->cfg.b[i].val = info[4];
+ break;
+ }
+ i++;
+ } while (1);
+ len = 3;
+ break;
+
+ case WID_SHORT:
+ do {
+ struct wilc_cfg_hword *hw = &wl->cfg.hw[i];
+
+ if (hw->id == WID_NIL)
+ break;
+
+ if (hw->id == wid) {
+ hw->val = get_unaligned_le16(&info[4]);
+ break;
+ }
+ i++;
+ } while (1);
+ len = 4;
+ break;
+
+ case WID_INT:
+ do {
+ struct wilc_cfg_word *w = &wl->cfg.w[i];
+
+ if (w->id == WID_NIL)
+ break;
+
+ if (w->id == wid) {
+ w->val = get_unaligned_le32(&info[4]);
+ break;
+ }
+ i++;
+ } while (1);
+ len = 6;
+ break;
+
+ case WID_STR:
+ do {
+ if (wl->cfg.s[i].id == WID_NIL)
+ break;
+
+ if (wl->cfg.s[i].id == wid) {
+ memcpy(wl->cfg.s[i].str, &info[2],
+ (2+((info[3] << 8) | info[2])));
+ break;
+ }
+ i++;
+ } while (1);
+ len = 2+((info[3] << 8) | info[2]);
+ break;
+ case WID_BIN_DATA:
+ do {
+ u16 length = (info[3] << 8) | info[2];
+ u8 checksum = 0;
+ int j = 0;
+
+ if (wl->cfg.bin[i].id == WID_NIL)
+ break;
+
+ if (wl->cfg.bin[i].id != wid) {
+ i++;
+ continue;
+ }
+
+ /*
+ * Compute the Checksum of received
+ * data field
+ */
+ for (j = 0; j < length; j++)
+ checksum += info[4 + j];
+ /*
+ * Verify the checksum of recieved BIN
+ * DATA
+ */
+ if (checksum != info[4 + length]) {
+ PRINT_ER(vif->ndev, "Checksum Failed");
+ return;
+ }
+
+ memcpy(wl->cfg.bin[i].bin, &info[2], length+2);
+ /*
+ * value length + data length +
+ * checksum
+ */
+ len = 2 + length + 1;
+ break;
+
+ } while (1);
+ break;
+ default:
+ break;
+ }
+ size -= (2 + len);
+ info += (2 + len);
+ }
+}
+
+static void wilc_wlan_parse_info_frame(struct wilc *wl, u8 *info)
+{
+ struct wilc_vif *vif = wl->vif[0];
+ u32 wid, len;
+
+ wid = get_unaligned_le16(info);
+
+ len = info[2];
+ PRINT_D(vif->ndev, GENERIC_DBG, "Status Len = %d Id= %d\n", len, wid);
+
+ if (len == 1 && wid == WID_STATUS) {
+ int i = 0;
+
+ do {
+ if (wl->cfg.b[i].id == WID_NIL)
+ break;
+
+ if (wl->cfg.b[i].id == wid) {
+ wl->cfg.b[i].val = info[3];
+ break;
+ }
+ i++;
+ } while (1);
+ }
+}
+
+/********************************************
+ *
+ * Configuration Exported Functions
+ *
+ ********************************************/
+
+int cfg_set_wid(struct wilc_vif *vif, u8 *frame, u32 offset, u16 id, u8 *buf,
+ int size)
+{
+ u8 type = (id >> 12) & 0xf;
+ int ret = 0;
+
+ switch (type) {
+ case CFG_BYTE_CMD:
+ if (size >= 1)
+ ret = wilc_wlan_cfg_set_byte(frame, offset, id, *buf);
+ break;
+
+ case CFG_HWORD_CMD:
+ if (size >= 2)
+ ret = wilc_wlan_cfg_set_hword(frame, offset, id,
+ *((u16 *)buf));
+ break;
+
+ case CFG_WORD_CMD:
+ if (size >= 4)
+ ret = wilc_wlan_cfg_set_word(frame, offset, id,
+ *((u32 *)buf));
+ break;
+
+ case CFG_STR_CMD:
+ ret = wilc_wlan_cfg_set_str(frame, offset, id, buf, size);
+ break;
+
+ case CFG_BIN_CMD:
+ ret = wilc_wlan_cfg_set_bin(frame, offset, id, buf, size);
+ break;
+ default:
+ PRINT_ER(vif->ndev, "illegal id\n");
+ }
+
+ return ret;
+}
+
+int cfg_get_wid(u8 *frame, u32 offset, u16 id)
+{
+ u8 *buf;
+
+ if ((offset + 2) >= WILC_MAX_CFG_FRAME_SIZE)
+ return 0;
+
+ buf = &frame[offset];
+
+ buf[0] = (u8)id;
+ buf[1] = (u8)(id >> 8);
+
+ return 2;
+}
+
+int cfg_get_wid_value(struct wilc *wl, u16 wid, u8 *buffer, u32 buffer_size)
+{
+ u32 type = (wid >> 12) & 0xf;
+ int i, ret = 0;
+
+ i = 0;
+ if (type == CFG_BYTE_CMD) {
+ do {
+ if (wl->cfg.b[i].id == WID_NIL)
+ break;
+
+ if (wl->cfg.b[i].id == wid) {
+ memcpy(buffer, &wl->cfg.b[i].val, 1);
+ ret = 1;
+ break;
+ }
+ i++;
+ } while (1);
+ } else if (type == CFG_HWORD_CMD) {
+ do {
+ if (wl->cfg.hw[i].id == WID_NIL)
+ break;
+
+ if (wl->cfg.hw[i].id == wid) {
+ memcpy(buffer, &wl->cfg.hw[i].val, 2);
+ ret = 2;
+ break;
+ }
+ i++;
+ } while (1);
+ } else if (type == CFG_WORD_CMD) {
+ do {
+ if (wl->cfg.w[i].id == WID_NIL)
+ break;
+
+ if (wl->cfg.w[i].id == wid) {
+ memcpy(buffer, &wl->cfg.w[i].val, 4);
+ ret = 4;
+ break;
+ }
+ i++;
+ } while (1);
+ } else if (type == CFG_STR_CMD) {
+ do {
+ u32 id = wl->cfg.s[i].id;
+
+ if (id == WID_NIL)
+ break;
+
+ if (id == wid) {
+ u16 size = get_unaligned_le16(wl->cfg.s[i].str);
+
+ if (buffer_size >= size) {
+ memcpy(buffer, &wl->cfg.s[i].str[2],
+ size);
+ ret = size;
+ }
+ break;
+ }
+ i++;
+ } while (1);
+ } else if (type == CFG_BIN_CMD) { /* binary command */
+ do {
+ if (wl->cfg.bin[i].id == WID_NIL)
+ break;
+
+ if (wl->cfg.bin[i].id == wid) {
+ uint32_t size = wl->cfg.bin[i].bin[0] |
+ (wl->cfg.bin[i].bin[1]<<8);
+ if (buffer_size >= size) {
+ memcpy(buffer, &wl->cfg.bin[i].bin[2],
+ size);
+ ret = size;
+ }
+ break;
+ }
+ i++;
+ } while (1);
+ } else {
+ PRINT_ER(wl->vif[0]->ndev, "[CFG]: illegal type (%08x)\n", wid);
+ }
+
+ return ret;
+}
+
+void cfg_indicate_rx(struct wilc *wilc, u8 *frame, int size,
+ struct wilc_cfg_rsp *rsp)
+{
+ u8 msg_type;
+ u8 msg_id;
+
+ msg_type = frame[0];
+ msg_id = frame[1]; /* seq no */
+ frame += 4;
+ size -= 4;
+ rsp->type = 0;
+
+ /*
+ * The valid types of response messages are
+ * 'R' (Response),
+ * 'I' (Information), and
+ * 'N' (Network Information)
+ */
+
+ switch (msg_type) {
+ case 'R':
+ wilc_wlan_parse_response_frame(wilc, frame, size);
+ rsp->type = WILC_CFG_RSP;
+ rsp->seq_no = msg_id;
+ break;
+
+ case 'I':
+ wilc_wlan_parse_info_frame(wilc, frame);
+ rsp->type = WILC_CFG_RSP_STATUS;
+ rsp->seq_no = msg_id;
+ /*call host interface info parse as well*/
+ PRINT_D(wilc->vif[0]->ndev, RX_DBG, "Info message received\n");
+ wilc_gnrl_async_info_received(wilc, frame - 4, size + 4);
+ break;
+
+ case 'N':
+ wilc_network_info_received(wilc, frame - 4, size + 4);
+ break;
+
+ case 'S':
+ PRINT_D(wilc->vif[0]->ndev, RX_DBG,
+ "Scan Notification Received\n");
+ wilc_scan_complete_received(wilc, frame - 4, size + 4);
+ break;
+
+ default:
+ PRINT_D(wilc->vif[0]->ndev, RX_DBG,
+ "Receive unknown message %d-%d-%d-%d-%d-%d-%d-%d\n",
+ frame[0], frame[1], frame[2], frame[3], frame[4],
+ frame[5], frame[6], frame[7]);
+ rsp->seq_no = msg_id;
+ break;
+ }
+}
+
+int cfg_init(struct wilc *wl)
+{
+ struct wilc_cfg_str_vals *str_vals;
+ struct wilc_bin_vals *bin_vals;
+ int i = 0;
+
+ wl->cfg.b = kmemdup(g_cfg_byte, sizeof(g_cfg_byte), GFP_KERNEL);
+ if (!wl->cfg.b)
+ return -ENOMEM;
+
+ wl->cfg.hw = kmemdup(g_cfg_hword, sizeof(g_cfg_hword), GFP_KERNEL);
+ if (!wl->cfg.hw)
+ goto out_b;
+
+ wl->cfg.w = kmemdup(g_cfg_word, sizeof(g_cfg_word), GFP_KERNEL);
+ if (!wl->cfg.w)
+ goto out_hw;
+
+ wl->cfg.s = kmemdup(g_cfg_str, sizeof(g_cfg_str), GFP_KERNEL);
+ if (!wl->cfg.s)
+ goto out_w;
+
+ str_vals = kzalloc(sizeof(*str_vals), GFP_KERNEL);
+ if (!str_vals)
+ goto out_s;
+
+
+ wl->cfg.bin = kmemdup(g_cfg_bin, sizeof(g_cfg_bin), GFP_KERNEL);
+ if (!wl->cfg.bin)
+ goto out_str_val;
+
+ bin_vals = kzalloc(sizeof(*bin_vals), GFP_KERNEL);
+ if (!bin_vals)
+ goto out_bin;
+
+ /* store the string cfg parameters */
+ wl->cfg.str_vals = str_vals;
+ wl->cfg.s[i].id = WID_FIRMWARE_VERSION;
+ wl->cfg.s[i].str = str_vals->firmware_version;
+ i++;
+ wl->cfg.s[i].id = WID_MAC_ADDR;
+ wl->cfg.s[i].str = str_vals->mac_address;
+ i++;
+ wl->cfg.s[i].id = WID_ASSOC_RES_INFO;
+ wl->cfg.s[i].str = str_vals->assoc_rsp;
+ i++;
+ wl->cfg.s[i].id = WID_NIL;
+ wl->cfg.s[i].str = NULL;
+
+ /* store the bin parameters */
+ i = 0;
+ wl->cfg.bin[i].id = WID_ANTENNA_SELECTION;
+ wl->cfg.bin[i].bin = bin_vals->antenna_param;
+ i++;
+
+ wl->cfg.bin[i].id = WID_NIL;
+ wl->cfg.bin[i].bin = NULL;
+
+ return 0;
+
+out_bin:
+ kfree(wl->cfg.bin);
+out_str_val:
+ kfree(str_vals);
+out_s:
+ kfree(wl->cfg.s);
+out_w:
+ kfree(wl->cfg.w);
+out_hw:
+ kfree(wl->cfg.hw);
+out_b:
+ kfree(wl->cfg.b);
+ return -ENOMEM;
+}
+
+void cfg_deinit(struct wilc *wl)
+{
+ kfree(wl->cfg.b);
+ kfree(wl->cfg.hw);
+ kfree(wl->cfg.w);
+ kfree(wl->cfg.s);
+ kfree(wl->cfg.str_vals);
+ kfree(wl->cfg.bin);
+ kfree(wl->cfg.bin_vals);
+}
+
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#ifndef WILC_WLAN_CFG_H
+#define WILC_WLAN_CFG_H
+
+struct wilc_cfg_byte {
+ u16 id;
+ u8 val;
+};
+
+struct wilc_cfg_hword {
+ u16 id;
+ u16 val;
+};
+
+struct wilc_cfg_word {
+ u32 id;
+ u32 val;
+};
+
+struct wilc_cfg_str {
+ u16 id;
+ u8 *str;
+};
+
+struct wilc_cfg_bin {
+ u16 id;
+ u8 *bin;
+};
+
+struct wilc_cfg_str_vals {
+ u8 mac_address[7];
+ u8 firmware_version[129];
+ u8 assoc_rsp[256];
+};
+
+struct wilc_bin_vals {
+ u8 antenna_param[5];
+};
+
+struct wilc_cfg {
+ struct wilc_cfg_byte *b;
+ struct wilc_cfg_hword *hw;
+ struct wilc_cfg_word *w;
+ struct wilc_cfg_str *s;
+ struct wilc_cfg_str_vals *str_vals;
+ struct wilc_cfg_bin *bin;
+ struct wilc_bin_vals *bin_vals;
+};
+
+struct wilc;
+int cfg_set_wid(struct wilc_vif *vif, u8 *frame, u32 offset, u16 id, u8 *buf,
+ int size);
+int cfg_get_wid(u8 *frame, u32 offset, u16 id);
+int cfg_get_wid_value(struct wilc *wl, u16 wid, u8 *buffer, u32 buffer_size);
+void cfg_indicate_rx(struct wilc *wilc, u8 *frame, int size,
+ struct wilc_cfg_rsp *rsp);
+int cfg_init(struct wilc *wl);
+void cfg_deinit(struct wilc *wl);
+
+#endif
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#ifndef WILC_WLAN_IF_H
+#define WILC_WLAN_IF_H
+
+#include <linux/netdevice.h>
+#include "wilc_debugfs.h"
+
+/********************************************
+ *
+ * Host Interface Defines
+ *
+ ********************************************/
+
+#define FW_WILC1000_WIFi "mchp/wilc1000_wifi_firmware.bin"
+#define FW_WILC3000_WIFI "mchp/wilc3000_wifi_firmware.bin"
+#define FW_WILC3000_BLE "mchp/wilc3000_ble_firmware.bin"
+
+enum bss_types {
+ WILC_FW_BSS_TYPE_INFRA = 0,
+ WILC_FW_BSS_TYPE_INDEPENDENT,
+ WILC_FW_BSS_TYPE_AP,
+};
+
+enum {
+ WILC_FW_OPER_MODE_B_ONLY = 0, /* 1, 2 M, otherwise 5, 11 M */
+ WILC_FW_OPER_MODE_G_ONLY, /* 6,12,24 otherwise 9,18,36,48,54 */
+ WILC_FW_OPER_MODE_G_MIXED_11B_1, /* 1,2,5.5,11 otherwise all on */
+ WILC_FW_OPER_MODE_G_MIXED_11B_2, /* 1,2,5,11,6,12,24 otherwise all on */
+};
+
+enum {
+ WILC_FW_PREAMBLE_SHORT = 0, /* Short Preamble */
+ WILC_FW_PREAMBLE_LONG = 1, /* Long Preamble */
+ WILC_FW_PREAMBLE_AUTO = 2, /* Auto Preamble Selection */
+};
+
+#define DEV_WIFI 0
+#define DEV_BT 1
+#define DEV_MAX 2
+
+enum {
+ WILC_FW_PASSIVE_SCAN = 0,
+ WILC_FW_ACTIVE_SCAN = 1,
+};
+
+enum {
+ WILC_FW_NO_POWERSAVE = 0,
+ WILC_FW_MIN_FAST_PS = 1,
+ WILC_FW_MAX_FAST_PS = 2,
+ WILC_FW_MIN_PSPOLL_PS = 3,
+ WILC_FW_MAX_PSPOLL_PS = 4
+};
+
+enum bus_acquire {
+ WILC_BUS_ACQUIRE_ONLY = 0,
+ WILC_BUS_ACQUIRE_AND_WAKEUP = 1,
+};
+
+enum bus_release {
+ WILC_BUS_RELEASE_ONLY = 0,
+ WILC_BUS_RELEASE_ALLOW_SLEEP = 1,
+};
+
+enum {
+ WILC_FW_NO_ENCRYPT = 0,
+ WILC_FW_ENCRYPT_ENABLED = BIT(0),
+ WILC_FW_WEP = BIT(1),
+ WILC_FW_WEP_EXTENDED = BIT(2),
+ WILC_FW_WPA = BIT(3),
+ WILC_FW_WPA2 = BIT(4),
+ WILC_FW_AES = BIT(5),
+ WILC_FW_TKIP = BIT(6)
+};
+
+enum {
+ WILC_FW_SEC_NO = WILC_FW_NO_ENCRYPT,
+ WILC_FW_SEC_WEP = WILC_FW_WEP | WILC_FW_ENCRYPT_ENABLED,
+ WILC_FW_SEC_WEP_EXTENDED = WILC_FW_WEP_EXTENDED | WILC_FW_SEC_WEP,
+ WILC_FW_SEC_WPA = WILC_FW_WPA | WILC_FW_ENCRYPT_ENABLED,
+ WILC_FW_SEC_WPA_AES = WILC_FW_AES | WILC_FW_SEC_WPA,
+ WILC_FW_SEC_WPA_TKIP = WILC_FW_TKIP | WILC_FW_SEC_WPA,
+ WILC_FW_SEC_WPA2 = WILC_FW_WPA2 | WILC_FW_ENCRYPT_ENABLED,
+ WILC_FW_SEC_WPA2_AES = WILC_FW_AES | WILC_FW_SEC_WPA2,
+ WILC_FW_SEC_WPA2_TKIP = WILC_FW_TKIP | WILC_FW_SEC_WPA2
+};
+
+enum authtype {
+ WILC_FW_AUTH_OPEN_SYSTEM = 1,
+ WILC_FW_AUTH_SHARED_KEY = 2,
+ WILC_FW_AUTH_ANY = 3,
+ WILC_FW_AUTH_IEEE8021 = 5
+};
+
+enum site_survey {
+ WILC_FW_SITE_SURVEY_1CH = 0,
+ WILC_FW_SITE_SURVEY_ALL_CH = 1,
+ WILC_FW_SITE_SURVEY_OFF = 2
+};
+
+enum {
+ WILC_FW_ACK_POLICY_NORMAL = 0,
+ WILC_FW_ACK_NO_POLICY,
+};
+
+enum {
+ WILC_FW_REKEY_POLICY_DISABLE = 1,
+ WILC_FW_REKEY_POLICY_TIME_BASE,
+ WILC_FW_REKEY_POLICY_PKT_BASE,
+ WILC_FW_REKEY_POLICY_TIME_PKT_BASE
+};
+
+enum {
+ WILC_FW_FILTER_NO = 0x00,
+ WILC_FW_FILTER_AP_ONLY = 0x01,
+ WILC_FW_FILTER_STA_ONLY = 0x02
+};
+
+enum {
+ WILC_FW_11N_PROT_AUTO = 0, /* Auto */
+ WILC_FW_11N_NO_PROT, /* Do not use any protection */
+ WILC_FW_11N_PROT_ERP, /* Protect all ERP frame exchanges */
+ WILC_FW_11N_PROT_HT, /* Protect all HT frame exchanges */
+ WILC_FW_11N_PROT_GF /* Protect all GF frame exchanges */
+};
+
+enum {
+ WILC_FW_ERP_PROT_SELF_CTS,
+ WILC_FW_ERP_PROT_RTS_CTS,
+};
+
+enum {
+ WILC_FW_11N_OP_MODE_HT_MIXED = 1,
+ WILC_FW_11N_OP_MODE_HT_ONLY_20MHZ,
+ WILC_FW_11N_OP_MODE_HT_ONLY_20_40MHZ,
+};
+
+enum {
+ WILC_FW_OBBS_NONHT_NO_DETECT = 0,
+ WILC_FW_OBBS_NONHT_DETECT_ONLY = 1,
+ WILC_FW_OBBS_NONHT_DETECT_PROTECT = 2,
+ WILC_FW_OBBS_NONHT_DETECT_PROTECT_REPORT = 3,
+};
+
+enum {
+ WILC_FW_HT_PROT_RTS_CTS_NONHT = 0, /* RTS-CTS at non-HT rate */
+ WILC_FW_HT_PROT_FIRST_FRAME_NONHT, /* First frame at non-HT rate */
+ WILC_FW_HT_PROT_LSIG_TXOP, /* LSIG TXOP Protection */
+ WILC_FW_HT_PROT_FIRST_FRAME_MIXED, /* First frame at Mixed format */
+};
+
+enum {
+ WILC_FW_SMPS_MODE_STATIC = 1,
+ WILC_FW_SMPS_MODE_DYNAMIC = 2,
+ WILC_FW_SMPS_MODE_MIMO = 3, /* power save disable */
+};
+
+enum {
+ WILC_FW_TX_RATE_AUTO = 0,
+ WILC_FW_TX_RATE_MBPS_1 = 1,
+ WILC_FW_TX_RATE_MBPS_2 = 2,
+ WILC_FW_TX_RATE_MBPS_5_5 = 5,
+ WILC_FW_TX_RATE_MBPS_11 = 11,
+ WILC_FW_TX_RATE_MBPS_6 = 6,
+ WILC_FW_TX_RATE_MBPS_9 = 9,
+ WILC_FW_TX_RATE_MBPS_12 = 12,
+ WILC_FW_TX_RATE_MBPS_18 = 18,
+ WILC_FW_TX_RATE_MBPS_24 = 24,
+ WILC_FW_TX_RATE_MBPS_36 = 36,
+ WILC_FW_TX_RATE_MBPS_48 = 48,
+ WILC_FW_TX_RATE_MBPS_54 = 54
+};
+
+enum {
+ WILC_FW_DEFAULT_SCAN = 0,
+ WILC_FW_USER_SCAN = BIT(0),
+ WILC_FW_OBSS_PERIODIC_SCAN = BIT(1),
+ WILC_FW_OBSS_ONETIME_SCAN = BIT(2)
+};
+
+enum {
+ WILC_FW_ACTION_FRM_IDX = 0,
+ WILC_FW_PROBE_REQ_IDX = 1
+};
+
+enum wid_type {
+ WID_CHAR = 0,
+ WID_SHORT = 1,
+ WID_INT = 2,
+ WID_STR = 3,
+ WID_BIN_DATA = 4,
+ WID_BIN = 5,
+};
+
+enum {
+ ANTENNA1 = 0,
+ ANTENNA2 = 1,
+ DIVERSITY = 2,
+ NUM_ANT_MODE
+};
+
+struct wid {
+ u16 id;
+ enum wid_type type;
+ s32 size;
+ s8 *val;
+};
+
+enum {
+ WID_NIL = 0xffff,
+
+ /*
+ * BSS Type
+ * -----------------------------------------------------------
+ * Configuration : Infrastructure Independent Access Point
+ * Values to set : 0 1 2
+ * -----------------------------------------------------------
+ */
+ WID_BSS_TYPE = 0x0000,
+
+ /*
+ * Transmit Rate
+ * -----------------------------------------------------------
+ * Configuration : 1 2 5.5 11 6 9 12 18 24 36 48 54
+ * Values to set : 1 2 5 11 6 9 12 18 24 36 48 54
+ * -----------------------------------------------------------
+ */
+ WID_CURRENT_TX_RATE = 0x0001,
+
+ /*
+ * Channel
+ * -----------------------------------------------------------
+ * Configuration(g) : 1 2 3 4 5 6 7 8 9 10 11 12 13 14
+ * Values to set : 1 2 3 4 5 6 7 8 9 10 11 12 13 14
+ * -----------------------------------------------------------
+ */
+ WID_CURRENT_CHANNEL = 0x0002,
+
+ /*
+ * Preamble
+ * -----------------------------------------------------------
+ * Configuration : short long Auto
+ * Values to set : 0 1 2
+ * -----------------------------------------------------------
+ */
+ WID_PREAMBLE = 0x0003,
+
+ /*
+ * 11g operating mode (ignored if 11g not present)
+ * -----------------------------------------------------------
+ * Configuration : HighPerf Compat(RSet #1) Compat(RSet #2)
+ * Values to set : 1 2 3
+ * -----------------------------------------------------------
+ */
+ WID_11G_OPERATING_MODE = 0x0004,
+
+ /*
+ * Mac status (response only)
+ * -----------------------------------------------------------
+ * Configuration : disconnect connect
+ * Values to get : 0 1
+ * -----------------------------------------------------------
+ */
+ WID_STATUS = 0x0005,
+
+ /*
+ * Scan type
+ * -----------------------------------------------------------
+ * Configuration : Passive Scanning Active Scanning
+ * Values to set : 0 1
+ * -----------------------------------------------------------
+ */
+ WID_SCAN_TYPE = 0x0007,
+
+ /*
+ * Key Id (WEP default key Id)
+ * -----------------------------------------------------------
+ * Configuration : Any value between 0 to 3
+ * Values to set : Same value. Default is 0
+ * -----------------------------------------------------------
+ */
+ WID_KEY_ID = 0x0009,
+
+ /*
+ * QoS Enable
+ * -----------------------------------------------------------
+ * Configuration : QoS Disable WMM Enable
+ * Values to set : 0 1
+ * -----------------------------------------------------------
+ */
+ WID_QOS_ENABLE = 0x000A,
+
+ /*
+ * Power Management
+ * -----------------------------------------------------------
+ * Configuration : NO_POWERSAVE MIN_POWERSAVE MAX_POWERSAVE
+ * Values to set : 0 1 2
+ * -----------------------------------------------------------
+ */
+ WID_POWER_MANAGEMENT = 0x000B,
+
+ /*
+ * WEP/802 11I Configuration
+ * -----------------------------------------------------------
+ * Configuration:Disable WP40 WP104 WPA-AES WPA-TKIP RSN-AES RSN-TKIP
+ * Values (0x) : 00 03 07 29 49 31 51
+ * Configuration:WPA-AES+TKIP RSN-AES+TKIP
+ * Values (0x) : 69 71
+ * -----------------------------------------------------------
+ */
+ WID_11I_MODE = 0x000C,
+
+ /*
+ * WEP Configuration: Used in BSS STA mode only when WEP is enabled
+ * -----------------------------------------------------------
+ * Configuration : Open System Shared Key Any Type | 802.1x Auth
+ * Values (0x) : 01 02 03 | BIT2
+ * -----------------------------------------------------------
+ */
+ WID_AUTH_TYPE = 0x000D,
+
+ /*
+ * Site Survey Type
+ * -----------------------------------------------------------
+ * Configuration : Values to set
+ * Survey 1 Channel : 0
+ * survey all Channels : 1
+ * Disable Site Survey : 2
+ * -----------------------------------------------------------
+ */
+ WID_SITE_SURVEY = 0x000E,
+
+ /*
+ * Listen Interval
+ * -----------------------------------------------------------
+ * Configuration : Any value between 1 to 255
+ * Values to set : Same value. Default is 3
+ * -----------------------------------------------------------
+ */
+ WID_LISTEN_INTERVAL = 0x000F,
+
+ /*
+ * DTIM Period
+ * -----------------------------------------------------------
+ * Configuration : Any value between 1 to 255
+ * Values to set : Same value. Default is 3
+ * -----------------------------------------------------------
+ */
+ WID_DTIM_PERIOD = 0x0010,
+
+ /*
+ * ACK Policy
+ * -----------------------------------------------------------
+ * Configuration : Normal Ack No Ack
+ * Values to set : 0 1
+ * -----------------------------------------------------------
+ */
+ WID_ACK_POLICY = 0x0011,
+
+ /*
+ * Reset MAC (Set only)
+ * -----------------------------------------------------------
+ * Configuration : Don't Reset Reset No Request
+ * Values to set : 0 1 2
+ * -----------------------------------------------------------
+ */
+ WID_RESET = 0x0012,
+
+ /*
+ * Broadcast SSID Option: Setting this will adhere to "" SSID element
+ * -----------------------------------------------------------
+ * Configuration : Enable Disable
+ * Values to set : 1 0
+ * -----------------------------------------------------------
+ */
+ WID_BCAST_SSID = 0x0015,
+
+ /*
+ * Disconnect (Station)
+ * -----------------------------------------------------------
+ * Configuration : Association ID
+ * Values to set : Association ID
+ * -----------------------------------------------------------
+ */
+ WID_DISCONNECT = 0x0016,
+
+ /*
+ * 11a Tx Power Level
+ * -----------------------------------------------------------
+ * Configuration : Sets TX Power (Higher the value greater the power)
+ * Values to set : Any value between 0 and 63 (inclusive Default 48)
+ * -----------------------------------------------------------
+ */
+ WID_TX_POWER_LEVEL_11A = 0x0018,
+
+ /*
+ * Group Key Update Policy Selection
+ * -----------------------------------------------------------
+ * Configuration : Disabled timeBased packetBased timePacketBased
+ * Values to set : 1 2 3 4
+ * -----------------------------------------------------------
+ */
+ WID_REKEY_POLICY = 0x0019,
+
+ /*
+ * Allow Short Slot
+ * -----------------------------------------------------------
+ * Configuration : Disallow Short Slot Allow Short Slot
+ * (Enable Only Long Slot) (Enable Short Slot if applicable)
+ * Values to set : 0 1
+ * -----------------------------------------------------------
+ */
+ WID_SHORT_SLOT_ALLOWED = 0x001A,
+
+ WID_PHY_ACTIVE_REG = 0x001B,
+
+ /*
+ * 11b Tx Power Level
+ * -----------------------------------------------------------
+ * Configuration : Sets TX Power (Higher the value greater the power)
+ * Values to set : Any value between 0 and 63 (inclusive Default 48)
+ * -----------------------------------------------------------
+ */
+ WID_TX_POWER_LEVEL_11B = 0x001D,
+
+ /*
+ * Scan Request
+ * -----------------------------------------------------------
+ * Configuration : Request default scan
+ * Values to set : 0
+ * -----------------------------------------------------------
+ */
+ WID_START_SCAN_REQ = 0x001E,
+
+ /*
+ * Rssi (get only)
+ * -----------------------------------------------------------
+ * Configuration :
+ * Values to get : Rssi value
+ * -----------------------------------------------------------
+ */
+ WID_RSSI = 0x001F,
+
+ /*
+ * Join Request
+ * -----------------------------------------------------------
+ * Configuration : Request to join
+ * Values to set : index of scan result
+ * -----------------------------------------------------------
+ */
+ WID_JOIN_REQ = 0x0020,
+
+ WID_LINKSPEED = 0x0026,
+
+ /*
+ * Enable User Control of TX Power
+ * -----------------------------------------------------------
+ * Configuration : Disable Enable
+ * Values to set : 0 1
+ * -----------------------------------------------------------
+ */
+ WID_USER_CONTROL_ON_TX_POWER = 0x0027,
+
+ WID_MEMORY_ACCESS_8BIT = 0x0029,
+
+ /*
+ * Enable Auto RX Sensitivity feature
+ * -----------------------------------------------------------
+ * Configuration : Disable Enable
+ * Values to set : 0 1
+ * -----------------------------------------------------------
+ */
+ WID_AUTO_RX_SENSITIVITY = 0x0032,
+
+ /*
+ * Receive Buffer Based Ack
+ * -----------------------------------------------------------
+ * Configuration : Disable Enable
+ * Values to set : 0 1
+ * -----------------------------------------------------------
+ */
+ WID_DATAFLOW_CONTROL = 0x0033,
+
+ /*
+ * Scan Filter
+ * -----------------------------------------------------------
+ * Configuration : Class No filter AP only Station Only
+ * Values to set : 0 1 2
+ * Configuration : Priority High Rssi Low Rssi Detect
+ * Values to set : 0 0x4 0x0
+ * Configuration : Channel filter off filter on
+ * Values to set : 0 0x10
+ * -----------------------------------------------------------
+ */
+ WID_SCAN_FILTER = 0x0036,
+
+ /*
+ * Link Loss Threshold (measure in the beacon period)
+ * -----------------------------------------------------------
+ * Configuration : Any value between 10 and 254(Set to 255 disable)
+ * Values to set : Same value. Default is 10
+ * -----------------------------------------------------------
+ */
+ WID_LINK_LOSS_THRESHOLD = 0x0037,
+
+ WID_ABORT_RUNNING_SCAN = 0x003E,
+
+ /* NMAC Character WID list */
+ WID_WPS_START = 0x0043,
+
+ /*
+ * Protection mode for MAC
+ * -----------------------------------------------------------
+ * Configuration : Auto No protection ERP HT GF
+ * Values to set : 0 1 2 3 4
+ * -----------------------------------------------------------
+ */
+ WID_11N_PROT_MECH = 0x0080,
+
+ /*
+ * ERP Protection type for MAC
+ * -----------------------------------------------------------
+ * Configuration : Self-CTS RTS-CTS
+ * Values to set : 0 1
+ * -----------------------------------------------------------
+ */
+ WID_11N_ERP_PROT_TYPE = 0x0081,
+
+ /*
+ * HT Option Enable
+ * -----------------------------------------------------------
+ * Configuration : HT Enable HT Disable
+ * Values to set : 1 0
+ * -----------------------------------------------------------
+ */
+ WID_11N_ENABLE = 0x0082,
+
+ /*
+ * 11n Operating mode (Note that 11g operating mode will also be
+ * used in addition to this, if this is set to HT Mixed mode)
+ * -----------------------------------------------------------
+ * Configuration : HT Mixed HT Only-20MHz HT Only-20/40MHz
+ * Values to set : 1 2 3
+ * -----------------------------------------------------------
+ */
+ WID_11N_OPERATING_MODE = 0x0083,
+
+ /*
+ * 11n OBSS non-HT STA Detection flag
+ * -----------------------------------------------------------
+ * Configuration : Do not detect
+ * Values to set : 0
+ * Configuration : Detect, do not protect or report
+ * Values to set : 1
+ * Configuration : Detect, protect and do not report
+ * Values to set : 2
+ * Configuration : Detect, protect and report to other BSS
+ * Values to set : 3
+ * -----------------------------------------------------------
+ */
+ WID_11N_OBSS_NONHT_DETECTION = 0x0084,
+
+ /*
+ * 11n HT Protection Type
+ * -----------------------------------------------------------
+ * Configuration : RTS-CTS First Frame Exchange at non-HT-rate
+ * Values to set : 0 1
+ * Configuration : LSIG TXOP First Frame Exchange in Mixed Fmt
+ * Values to set : 2 3
+ * -----------------------------------------------------------
+ */
+ WID_11N_HT_PROT_TYPE = 0x0085,
+
+ /*
+ * 11n RIFS Protection Enable Flag
+ * -----------------------------------------------------------
+ * Configuration : Disable Enable
+ * Values to set : 0 1
+ * -----------------------------------------------------------
+ */
+ WID_11N_RIFS_PROT_ENABLE = 0x0086,
+
+ /*
+ * SMPS Mode
+ * -----------------------------------------------------------
+ * Configuration : Static Dynamic MIMO (Power Save Disabled)
+ * Values to set : 1 2 3
+ * -----------------------------------------------------------
+ */
+ WID_11N_SMPS_MODE = 0x0087,
+
+ /*
+ * Current transmit MCS
+ * -----------------------------------------------------------
+ * Configuration : MCS Index for data rate
+ * Values to set : 0 to 7
+ * -----------------------------------------------------------
+ */
+ WID_11N_CURRENT_TX_MCS = 0x0088,
+
+ WID_11N_PRINT_STATS = 0x0089,
+
+ /*
+ * 11n Short GI Enable Flag
+ * -----------------------------------------------------------
+ * Configuration : Disable Enable
+ * Values to set : 0 1
+ * -----------------------------------------------------------
+ */
+ WID_11N_SHORT_GI_ENABLE = 0x008D,
+
+ /*
+ * 11n RIFS Enable Flag
+ * -----------------------------------------------------------
+ * Configuration : Disable Enable
+ * Values to set : 0 1
+ * -----------------------------------------------------------
+ */
+ WID_RIFS_MODE = 0x0094,
+
+ /*
+ * TX Abort Feature
+ * -----------------------------------------------------------
+ * Configuration : Disable Self CTS Enable Self CTS
+ * Values to set : 0 1
+ * Configuration : Disable TX Abort Enable TX Abort
+ * Values to set : 2 3
+ * Configuration : Enable HW TX Abort Enable SW TX Abort
+ * Values to set : 4 5
+ * -----------------------------------------------------------
+ */
+ WID_TX_ABORT_CONFIG = 0x00A1,
+
+ WID_REG_TSSI_11B_VALUE = 0x00A6,
+ WID_REG_TSSI_11G_VALUE = 0x00A7,
+ WID_REG_TSSI_11N_VALUE = 0x00A8,
+ WID_TX_CALIBRATION = 0x00A9,
+ WID_DSCR_TSSI_11B_VALUE = 0x00AA,
+ WID_DSCR_TSSI_11G_VALUE = 0x00AB,
+ WID_DSCR_TSSI_11N_VALUE = 0x00AC,
+
+ /*
+ * Immediate Block-Ack Support
+ * -----------------------------------------------------------
+ * Configuration : Disable Enable
+ * Values to set : 0 1
+ * -----------------------------------------------------------
+ */
+ WID_11N_IMMEDIATE_BA_ENABLED = 0x00AF,
+
+ /*
+ * TXOP Disable Flag
+ * -----------------------------------------------------------
+ * Configuration : Disable Enable
+ * Values to set : 1 0
+ * -----------------------------------------------------------
+ */
+ WID_11N_TXOP_PROT_DISABLE = 0x00B0,
+
+ WID_TX_POWER_LEVEL_11N = 0x00B1,
+
+ /* Custom Character WID list */
+ /* SCAN Complete notification WID*/
+ WID_SCAN_COMPLETE = 0x00C9,
+
+ WID_DEL_BEACON = 0x00CA,
+
+ WID_LOG_TERMINAL_SWITCH = 0x00CD,
+ WID_TX_POWER = 0x00CE,
+ WID_WOWLAN_TRIGGER = 0X00CF,
+ /* EMAC Short WID list */
+ /* RTS Threshold */
+ /*
+ * -----------------------------------------------------------
+ * Configuration : Any value between 256 to 2347
+ * Values to set : Same value. Default is 2347
+ * -----------------------------------------------------------
+ */
+ WID_RTS_THRESHOLD = 0x1000,
+
+ /*
+ * Fragmentation Threshold
+ * -----------------------------------------------------------
+ * Configuration : Any value between 256 to 2346
+ * Values to set : Same value. Default is 2346
+ * -----------------------------------------------------------
+ */
+ WID_FRAG_THRESHOLD = 0x1001,
+
+ WID_SHORT_RETRY_LIMIT = 0x1002,
+ WID_LONG_RETRY_LIMIT = 0x1003,
+ WID_BEACON_INTERVAL = 0x1006,
+ WID_MEMORY_ACCESS_16BIT = 0x1008,
+ WID_PASSIVE_SCAN_TIME = 0x100D,
+ WID_JOIN_START_TIMEOUT = 0x100F,
+ WID_ASOC_TIMEOUT = 0x1011,
+ WID_11I_PROTOCOL_TIMEOUT = 0x1012,
+ WID_EAPOL_RESPONSE_TIMEOUT = 0x1013,
+
+ /* NMAC Short WID list */
+ WID_11N_SIG_QUAL_VAL = 0x1085,
+ WID_CCA_THRESHOLD = 0x1087,
+
+ /* Custom Short WID list */
+
+ /* EMAC Integer WID list */
+ WID_FAILED_COUNT = 0x2000,
+ WID_RETRY_COUNT = 0x2001,
+ WID_MULTIPLE_RETRY_COUNT = 0x2002,
+ WID_FRAME_DUPLICATE_COUNT = 0x2003,
+ WID_ACK_FAILURE_COUNT = 0x2004,
+ WID_RECEIVED_FRAGMENT_COUNT = 0x2005,
+ WID_MCAST_RECEIVED_FRAME_COUNT = 0x2006,
+ WID_FCS_ERROR_COUNT = 0x2007,
+ WID_SUCCESS_FRAME_COUNT = 0x2008,
+ WID_HUT_TX_COUNT = 0x200A,
+ WID_TX_FRAGMENT_COUNT = 0x200B,
+ WID_TX_MULTICAST_FRAME_COUNT = 0x200C,
+ WID_RTS_SUCCESS_COUNT = 0x200D,
+ WID_RTS_FAILURE_COUNT = 0x200E,
+ WID_WEP_UNDECRYPTABLE_COUNT = 0x200F,
+ WID_REKEY_PERIOD = 0x2010,
+ WID_REKEY_PACKET_COUNT = 0x2011,
+ WID_1X_SERV_ADDR = 0x2012,
+ WID_STACK_IP_ADDR = 0x2013,
+ WID_STACK_NETMASK_ADDR = 0x2014,
+ WID_HW_RX_COUNT = 0x2015,
+ WID_MEMORY_ADDRESS = 0x201E,
+ WID_MEMORY_ACCESS_32BIT = 0x201F,
+
+ /* NMAC Integer WID list */
+ /* Custom Integer WID list */
+ WID_GET_INACTIVE_TIME = 0x2084,
+ WID_SET_OPERATION_MODE = 0X2086,
+ /* EMAC String WID list */
+ WID_SSID = 0x3000,
+ WID_FIRMWARE_VERSION = 0x3001,
+ WID_OPERATIONAL_RATE_SET = 0x3002,
+ WID_BSSID = 0x3003,
+ WID_WEP_KEY_VALUE = 0x3004,
+ WID_11I_PSK = 0x3008,
+ WID_11E_P_ACTION_REQ = 0x3009,
+ WID_1X_KEY = 0x300A,
+ WID_HARDWARE_VERSION = 0x300B,
+ WID_MAC_ADDR = 0x300C,
+ WID_HUT_DEST_ADDR = 0x300D,
+ WID_PHY_VERSION = 0x300F,
+ WID_SUPP_USERNAME = 0x3010,
+ WID_SUPP_PASSWORD = 0x3011,
+ WID_SITE_SURVEY_RESULTS = 0x3012,
+ WID_RX_POWER_LEVEL = 0x3013,
+ WID_SET_STA_MAC_INACTIVE_TIME = 0x3017,
+ WID_ADD_WEP_KEY = 0x3019,
+ WID_REMOVE_WEP_KEY = 0x301A,
+ WID_ADD_PTK = 0x301B,
+ WID_ADD_RX_GTK = 0x301C,
+ WID_ADD_TX_GTK = 0x301D,
+ WID_REMOVE_KEY = 0x301E,
+ WID_ASSOC_REQ_INFO = 0x301F,
+ WID_ASSOC_RES_INFO = 0x3020,
+ WID_MANUFACTURER = 0x3026, /*Added for CAPI tool */
+ WID_MODEL_NAME = 0x3027, /*Added for CAPI tool */
+ WID_MODEL_NUM = 0x3028, /*Added for CAPI tool */
+ WID_DEVICE_NAME = 0x3029, /*Added for CAPI tool */
+
+ /* NMAC String WID list */
+ WID_SET_DRV_HANDLER = 0x3079,
+ WID_11N_P_ACTION_REQ = 0x3080,
+ WID_HUT_TEST_ID = 0x3081,
+ WID_PMKID_INFO = 0x3082,
+ WID_FIRMWARE_INFO = 0x3083,
+ WID_REGISTER_FRAME = 0x3084,
+ WID_DEL_ALL_STA = 0x3085,
+ WID_REMAIN_ON_CHAN = 0x3996,
+ WID_SSID_PROBE_REQ = 0x3997,
+ WID_JOIN_REQ_EXTENDED = 0x3998,
+
+ WID_IP_ADDRESS = 0x3999,
+
+ /* Custom String WID list */
+
+ /* EMAC Binary WID list */
+ WID_UAPSD_CONFIG = 0x4001,
+ WID_UAPSD_STATUS = 0x4002,
+ WID_WMM_AP_AC_PARAMS = 0x4003,
+ WID_WMM_STA_AC_PARAMS = 0x4004,
+ WID_NETWORK_INFO = 0x4005,
+ WID_STA_JOIN_INFO = 0x4006,
+ WID_CONNECTED_STA_LIST = 0x4007,
+
+ /* NMAC Binary WID list */
+ WID_11N_AUTORATE_TABLE = 0x4080,
+
+ WID_SCAN_CHANNEL_LIST = 0x4084,
+
+ WID_INFO_ELEMENT_PROBE = 0x4085,
+ WID_INFO_ELEMENT_ASSOCIATE = 0x4086,
+ WID_ADD_STA = 0X4087,
+ WID_REMOVE_STA = 0X4088,
+ WID_EDIT_STA = 0X4089,
+ WID_ADD_BEACON = 0x408a,
+
+ WID_SETUP_MULTICAST_FILTER = 0x408b,
+
+ WID_ANTENNA_SELECTION = 0x408c,
+ /* Miscellaneous WIDs */
+ WID_ALL = 0x7FFE,
+ WID_MAX = 0xFFFF
+};
+
+struct wilc;
+
+#endif