The Espressif ESP8089 WiFi chips can be often found in cheap tablets.
There is one in A23 Polaroid tablets for example.
The chip is often embedded as an eMMC SDIO device.
At the moment, there is no publicly available datasheet for this chip.
The code was taken from an out-of-tree repository and has seen a first
pass in the cleanup process.
Three firmwares are needed and will be soon posted to linux-firmware.
Hans already tried to push the first patch and there had been some
discussions going on with Ulf here[1]. Basically, Ulf proposed to send
an RFC of a better solution to the problem Hans exposed in his cover
letter.
I've been looking for a few hours where this RFC could be and I
unfortunately haven't found it. Moreover, nothing recently submitted
matches a potential solution to this problem (but I may be wrong). I'm
basically trying to revive the discussion on this matter since only a
hack can make this driver work ATM (see Hans' cover letter).
I am posting this driver to staging as it still requires some cleanup
but can be used as is.
Thanks,
Quentin
[1] http://www.spinics.net/lists/linux-mmc/msg38410.html
Hans de Goede (1):
mmc: Add mmc_force_detect_change_begin / _end functions
Quentin Schulz (1):
staging: net: wireless: add ESP8089 WiFi driver
drivers/mmc/core/core.c | 47 +-
drivers/staging/Kconfig | 2 +
drivers/staging/Makefile | 1 +
drivers/staging/esp8089/Kconfig | 13 +
drivers/staging/esp8089/Makefile | 7 +
drivers/staging/esp8089/esp_ctrl.c | 527 ++++++++
drivers/staging/esp8089/esp_ctrl.h | 48 +
drivers/staging/esp8089/esp_debug.c | 247 ++++
drivers/staging/esp8089/esp_debug.h | 69 ++
drivers/staging/esp8089/esp_file.c | 221 ++++
drivers/staging/esp8089/esp_file.h | 30 +
drivers/staging/esp8089/esp_init_data.h | 17 +
drivers/staging/esp8089/esp_io.c | 294 +++++
drivers/staging/esp8089/esp_mac80211.c | 1496 +++++++++++++++++++++++
drivers/staging/esp8089/esp_mac80211.h | 33 +
drivers/staging/esp8089/esp_main.c | 199 ++++
drivers/staging/esp8089/esp_pub.h | 188 +++
drivers/staging/esp8089/esp_sif.h | 131 ++
drivers/staging/esp8089/esp_sip.c | 1718 +++++++++++++++++++++++++++
drivers/staging/esp8089/esp_sip.h | 150 +++
drivers/staging/esp8089/esp_utils.c | 133 +++
drivers/staging/esp8089/esp_utils.h | 27 +
drivers/staging/esp8089/esp_wl.h | 35 +
drivers/staging/esp8089/esp_wmac.h | 87 ++
drivers/staging/esp8089/sdio_sif_esp.c | 552 +++++++++
drivers/staging/esp8089/sip2_common.h | 388 ++++++
drivers/staging/esp8089/slc_host_register.h | 263 ++++
include/linux/mmc/host.h | 7 +
28 files changed, 6925 insertions(+), 5 deletions(-)
create mode 100644 drivers/staging/esp8089/Kconfig
create mode 100644 drivers/staging/esp8089/Makefile
create mode 100644 drivers/staging/esp8089/esp_ctrl.c
create mode 100644 drivers/staging/esp8089/esp_ctrl.h
create mode 100644 drivers/staging/esp8089/esp_debug.c
create mode 100644 drivers/staging/esp8089/esp_debug.h
create mode 100644 drivers/staging/esp8089/esp_file.c
create mode 100644 drivers/staging/esp8089/esp_file.h
create mode 100644 drivers/staging/esp8089/esp_init_data.h
create mode 100644 drivers/staging/esp8089/esp_io.c
create mode 100644 drivers/staging/esp8089/esp_mac80211.c
create mode 100644 drivers/staging/esp8089/esp_mac80211.h
create mode 100644 drivers/staging/esp8089/esp_main.c
create mode 100644 drivers/staging/esp8089/esp_pub.h
create mode 100644 drivers/staging/esp8089/esp_sif.h
create mode 100644 drivers/staging/esp8089/esp_sip.c
create mode 100644 drivers/staging/esp8089/esp_sip.h
create mode 100644 drivers/staging/esp8089/esp_utils.c
create mode 100644 drivers/staging/esp8089/esp_utils.h
create mode 100644 drivers/staging/esp8089/esp_wl.h
create mode 100644 drivers/staging/esp8089/esp_wmac.h
create mode 100644 drivers/staging/esp8089/sdio_sif_esp.c
create mode 100644 drivers/staging/esp8089/sip2_common.h
create mode 100644 drivers/staging/esp8089/slc_host_register.h
--
2.11.0
The Espressif ESP8089 WiFi chips can be often found in cheap tablets.
There is one in A23 Polaroid tablets for example.
The chip is often embedded as an eMMC SDIO device.
The code was taken from an out-of-tree repository and has seen a first
pass in the cleanup process.
At the moment, there is no publicly available datasheet for this chip.
Cc: Hans de Goede <[email protected]>
Cc: Icenowy Zheng <[email protected]>
Signed-off-by: Quentin Schulz <[email protected]>
---
drivers/staging/Kconfig | 2 +
drivers/staging/Makefile | 1 +
drivers/staging/esp8089/Kconfig | 13 +
drivers/staging/esp8089/Makefile | 7 +
drivers/staging/esp8089/esp_ctrl.c | 527 ++++++++
drivers/staging/esp8089/esp_ctrl.h | 48 +
drivers/staging/esp8089/esp_debug.c | 247 ++++
drivers/staging/esp8089/esp_debug.h | 69 ++
drivers/staging/esp8089/esp_file.c | 221 ++++
drivers/staging/esp8089/esp_file.h | 30 +
drivers/staging/esp8089/esp_init_data.h | 17 +
drivers/staging/esp8089/esp_io.c | 294 +++++
drivers/staging/esp8089/esp_mac80211.c | 1496 +++++++++++++++++++++++
drivers/staging/esp8089/esp_mac80211.h | 33 +
drivers/staging/esp8089/esp_main.c | 199 ++++
drivers/staging/esp8089/esp_pub.h | 188 +++
drivers/staging/esp8089/esp_sif.h | 131 ++
drivers/staging/esp8089/esp_sip.c | 1718 +++++++++++++++++++++++++++
drivers/staging/esp8089/esp_sip.h | 150 +++
drivers/staging/esp8089/esp_utils.c | 133 +++
drivers/staging/esp8089/esp_utils.h | 27 +
drivers/staging/esp8089/esp_wl.h | 35 +
drivers/staging/esp8089/esp_wmac.h | 87 ++
drivers/staging/esp8089/sdio_sif_esp.c | 552 +++++++++
drivers/staging/esp8089/sip2_common.h | 388 ++++++
drivers/staging/esp8089/slc_host_register.h | 263 ++++
26 files changed, 6876 insertions(+)
create mode 100644 drivers/staging/esp8089/Kconfig
create mode 100644 drivers/staging/esp8089/Makefile
create mode 100644 drivers/staging/esp8089/esp_ctrl.c
create mode 100644 drivers/staging/esp8089/esp_ctrl.h
create mode 100644 drivers/staging/esp8089/esp_debug.c
create mode 100644 drivers/staging/esp8089/esp_debug.h
create mode 100644 drivers/staging/esp8089/esp_file.c
create mode 100644 drivers/staging/esp8089/esp_file.h
create mode 100644 drivers/staging/esp8089/esp_init_data.h
create mode 100644 drivers/staging/esp8089/esp_io.c
create mode 100644 drivers/staging/esp8089/esp_mac80211.c
create mode 100644 drivers/staging/esp8089/esp_mac80211.h
create mode 100644 drivers/staging/esp8089/esp_main.c
create mode 100644 drivers/staging/esp8089/esp_pub.h
create mode 100644 drivers/staging/esp8089/esp_sif.h
create mode 100644 drivers/staging/esp8089/esp_sip.c
create mode 100644 drivers/staging/esp8089/esp_sip.h
create mode 100644 drivers/staging/esp8089/esp_utils.c
create mode 100644 drivers/staging/esp8089/esp_utils.h
create mode 100644 drivers/staging/esp8089/esp_wl.h
create mode 100644 drivers/staging/esp8089/esp_wmac.h
create mode 100644 drivers/staging/esp8089/sdio_sif_esp.c
create mode 100644 drivers/staging/esp8089/sip2_common.h
create mode 100644 drivers/staging/esp8089/slc_host_register.h
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index e97d72e3bc40..0bd007837429 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -114,4 +114,6 @@ source "drivers/staging/vboxvideo/Kconfig"
source "drivers/staging/pi433/Kconfig"
+source "drivers/staging/esp8089/Kconfig"
+
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 993ed0c1556c..23cfa1258c4e 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -46,3 +46,4 @@ obj-$(CONFIG_BCM2835_VCHIQ) += vc04_services/
obj-$(CONFIG_CRYPTO_DEV_CCREE) += ccree/
obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/
obj-$(CONFIG_PI433) += pi433/
+obj-$(CONFIG_ESP8089) += esp8089/
diff --git a/drivers/staging/esp8089/Kconfig b/drivers/staging/esp8089/Kconfig
new file mode 100644
index 000000000000..8fdd8fa828ad
--- /dev/null
+++ b/drivers/staging/esp8089/Kconfig
@@ -0,0 +1,13 @@
+config ESP8089
+ tristate "Espressif ESP8089 SDIO WiFi"
+ depends on MAC80211
+ ---help---
+ ESP8089 is a low-budget 2.4GHz WiFi chip by Espressif, used in many
+ cheap tablets with Allwinner or Rockchip SoC
+
+config ESP8089_DEBUG_FS
+ bool "Enable DebugFS support for ESP8089"
+ depends on ESP8089
+ default y
+ ---help---
+ DebugFS support for ESP8089
diff --git a/drivers/staging/esp8089/Makefile b/drivers/staging/esp8089/Makefile
new file mode 100644
index 000000000000..827c66d8b5d5
--- /dev/null
+++ b/drivers/staging/esp8089/Makefile
@@ -0,0 +1,7 @@
+MODULE_NAME = esp8089
+
+$(MODULE_NAME)-y := esp_debug.o sdio_sif_esp.o esp_io.o \
+ esp_file.o esp_main.o esp_sip.o esp_ctrl.o \
+ esp_mac80211.o esp_debug.o esp_utils.o
+
+obj-$(CONFIG_ESP8089) := esp8089.o
diff --git a/drivers/staging/esp8089/esp_ctrl.c b/drivers/staging/esp8089/esp_ctrl.c
new file mode 100644
index 000000000000..e5a95e7b313f
--- /dev/null
+++ b/drivers/staging/esp8089/esp_ctrl.c
@@ -0,0 +1,527 @@
+/*
+ * Copyright (c) 2009 - 2014 Espressif System.
+ * Copyright (c) 2017 Free Electrons
+ * Quentin Schulz <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ */
+
+#include <net/mac80211.h>
+#include <net/cfg80211.h>
+#include <linux/skbuff.h>
+#include <linux/bitops.h>
+#include <linux/firmware.h>
+
+#include "esp_init_data.h"
+#include "esp_pub.h"
+#include "esp_sip.h"
+#include "esp_ctrl.h"
+#include "esp_sif.h"
+#include "esp_debug.h"
+#include "esp_wmac.h"
+#include "esp_utils.h"
+#include "esp_wl.h"
+#include "esp_file.h"
+#include "esp_version.h"
+
+#define DRIVER_VER 0xbdf5087c3debll
+
+static void esp_tx_ba_session_op(struct esp_sip *sip, struct esp_node *node,
+ enum trc_ampdu_state state, u8 tid)
+{
+ struct esp_tx_tid *txtid = &node->tid[tid];
+
+ if (state != TRC_TX_AMPDU_STOPPED && state != TRC_TX_AMPDU_OPERATIONAL)
+ return;
+
+ if (state == TRC_TX_AMPDU_STOPPED) {
+ if (txtid->state != ESP_TID_STATE_OPERATIONAL) {
+ dev_err(sip->epub->dev,
+ "%s tid %d TXAMPDU GOT STOP EVT IN WRONG STATE %d\n",
+ __func__, tid, txtid->state);
+ return;
+ }
+
+ dev_dbg(sip->epub->dev, "%s tid %d TXAMPDU GOT STOP EVT\n",
+ __func__, tid);
+
+ spin_lock_bh(&sip->epub->tx_ampdu_lock);
+ txtid->state = ESP_TID_STATE_WAIT_STOP;
+ spin_unlock_bh(&sip->epub->tx_ampdu_lock);
+
+ ieee80211_stop_tx_ba_session(node->sta, (u16)tid);
+ return;
+ }
+
+ if (txtid->state == ESP_TID_STATE_STOP) {
+ dev_dbg(sip->epub->dev, "%s tid %d TXAMPDU GOT OPERATIONAL\n",
+ __func__, tid);
+
+ spin_lock_bh(&sip->epub->tx_ampdu_lock);
+ txtid->state = ESP_TID_STATE_TRIGGER;
+ spin_unlock_bh(&sip->epub->tx_ampdu_lock);
+
+ ieee80211_start_tx_ba_session(node->sta, (u16)tid, 0);
+ return;
+ } else if (txtid->state == ESP_TID_STATE_OPERATIONAL) {
+ sip_send_ampdu_action(sip->epub, SIP_AMPDU_TX_OPERATIONAL,
+ node->sta->addr, tid, node->ifidx, 0);
+ return;
+ }
+
+ dev_err(sip->epub->dev,
+ "%s tid %d TXAMPDU GOT OPERATIONAL EVT IN WRONG STATE %d\n",
+ __func__, tid, txtid->state);
+}
+
+int sip_parse_events(struct esp_sip *sip, u8 *buf)
+{
+ struct sip_hdr *hdr = (struct sip_hdr *)buf;
+ struct sip_evt_bootup2 *bootup_evt;
+ struct sip_evt_scan_report *report;
+ struct sip_evt_roc *roc;
+ struct sip_evt_trc_ampdu *ampdu;
+ struct sip_evt_noisefloor *noisefloor;
+ struct esp_node *node;
+ char *ep;
+ u8 *p;
+ u16 *len;
+ int i;
+
+ switch (hdr->c_evtid) {
+ case SIP_EVT_TARGET_ON:
+ /* use rx work queue to send... */
+ if (atomic_read(&sip->state) == SIP_PREPARE_BOOT ||
+ atomic_read(&sip->state) == SIP_BOOT) {
+ atomic_set(&sip->state, SIP_SEND_INIT);
+ queue_work(sip->epub->esp_wkq, &sip->rx_process_work);
+ break;
+ }
+ dev_err(sip->epub->dev, "%s boot during wrong state %d\n",
+ __func__, atomic_read(&sip->state));
+ break;
+
+ case SIP_EVT_BOOTUP:
+ bootup_evt = (struct sip_evt_bootup2 *)(buf + SIP_CTRL_HDR_LEN);
+
+ kfree(sip->rawbuf);
+
+ sip_post_init(sip, bootup_evt);
+ if (gl_bootup_cplx)
+ complete(gl_bootup_cplx);
+
+ break;
+
+ case SIP_EVT_RESETTING:
+ sip->epub->wait_reset = 1;
+ if (gl_bootup_cplx)
+ complete(gl_bootup_cplx);
+ break;
+
+ case SIP_EVT_SCAN_RESULT:
+ if (!atomic_read(&sip->epub->wl.off)) {
+ report = (struct sip_evt_scan_report *)(buf + SIP_CTRL_HDR_LEN);
+ sip_scandone_process(sip, report);
+ break;
+ }
+
+ dev_err(sip->epub->dev, "%s scan result while wlan off\n",
+ __func__);
+ break;
+
+ case SIP_EVT_ROC:
+ roc = (struct sip_evt_roc *)(buf + SIP_CTRL_HDR_LEN);
+ esp_rocdone_process(sip->epub->hw, roc);
+ break;
+
+ case SIP_EVT_SNPRINTF_TO_HOST:
+ p = buf + sizeof(struct sip_hdr) + sizeof(u16);
+ len = (u16 *)(buf + sizeof(struct sip_hdr));
+ dev_dbg(sip->epub->dev, "esp_host:%llx\nesp_target: %.*s\n",
+ DRIVER_VER, *len, p);
+
+ if (!*len || sip->epub->sdio_state != ESP_SDIO_STATE_FIRST_INIT)
+ break;
+
+ dev_dbg(sip->epub->dev,
+ "SNPRINTF TO HOST: esp_host:%llx\nesp_target: %.*s\n",
+ DRIVER_VER, *len, p);
+ break;
+
+ case SIP_EVT_TRC_AMPDU:
+ if (atomic_read(&sip->epub->wl.off)) {
+ dev_err(sip->epub->dev,
+ "%s scan result while wlan off\n", __func__);
+ break;
+ }
+
+ ampdu = (struct sip_evt_trc_ampdu *)(buf + SIP_CTRL_HDR_LEN);
+ node = esp_get_node_by_addr(sip->epub, ampdu->addr);
+ if (!node)
+ break;
+ for (i = 0; i < 8; i++)
+ if (ampdu->tid & BIT(i))
+ esp_tx_ba_session_op(sip, node, ampdu->state,
+ i);
+ break;
+
+ case SIP_EVT_INIT_EP:
+ ep = (char *)(buf + SIP_CTRL_HDR_LEN);
+ dev_dbg(sip->epub->dev, "Phy Init: %s\n", ep);
+ break;
+
+ case SIP_EVT_NOISEFLOOR:
+ noisefloor = (struct sip_evt_noisefloor *)(buf + SIP_CTRL_HDR_LEN);
+ atomic_set(&sip->noise_floor, noisefloor->noise_floor);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* FIXME: ugly af */
+void sip_send_chip_init(struct esp_sip *sip)
+{
+ size_t size = sizeof(esp_init_data);
+
+ fix_init_data(esp_init_data, size);
+ atomic_sub(1, &sip->tx_credits);
+ sip_send_cmd(sip, SIP_CMD_INIT, size, (void *)esp_init_data);
+}
+
+int sip_send_config(struct esp_pub *epub, struct ieee80211_conf *conf)
+{
+ struct sk_buff *skb;
+ struct sip_cmd_config *configcmd;
+
+ skb = sip_alloc_ctrl_skbuf(epub->sip, sizeof(struct sip_cmd_config) +
+ sizeof(struct sip_hdr), SIP_CMD_CONFIG);
+ if (!skb)
+ return -ENOMEM;
+
+ dev_dbg(epub->dev, "%s config center freq %d\n", __func__,
+ conf->chandef.chan->center_freq);
+
+ configcmd = (struct sip_cmd_config *)(skb->data + sizeof(struct sip_hdr));
+ configcmd->center_freq = conf->chandef.chan->center_freq;
+ configcmd->duration = 0;
+
+ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
+}
+
+int sip_send_bss_info_update(struct esp_pub *epub, struct esp_vif *evif,
+ u8 *bssid, int assoc)
+{
+ struct sk_buff *skb;
+ struct sip_cmd_bss_info_update *bsscmd;
+
+ skb = sip_alloc_ctrl_skbuf(epub->sip,
+ sizeof(struct sip_cmd_bss_info_update) +
+ sizeof(struct sip_hdr),
+ SIP_CMD_BSS_INFO_UPDATE);
+ if (!skb)
+ return -ENOMEM;
+
+ bsscmd = (struct sip_cmd_bss_info_update *)(skb->data + sizeof(struct sip_hdr));
+
+ if (assoc == 2) //hack for softAP mode
+ bsscmd->beacon_int = evif->beacon_interval;
+ else if (assoc == 1)
+ set_bit(ESP_WL_FLAG_CONNECT, &epub->wl.flags);
+ else
+ clear_bit(ESP_WL_FLAG_CONNECT, &epub->wl.flags);
+
+ bsscmd->bssid_no = evif->index;
+ bsscmd->isassoc = assoc;
+ bsscmd->beacon_int = evif->beacon_interval;
+ memcpy(bsscmd->bssid, bssid, ETH_ALEN);
+
+ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
+}
+
+int sip_send_wmm_params(struct esp_pub *epub, u8 aci,
+ const struct ieee80211_tx_queue_params *params)
+{
+ struct sk_buff *skb;
+ struct sip_cmd_set_wmm_params *bsscmd;
+
+ skb = sip_alloc_ctrl_skbuf(epub->sip,
+ sizeof(struct sip_cmd_set_wmm_params) +
+ sizeof(struct sip_hdr),
+ SIP_CMD_SET_WMM_PARAM);
+ if (!skb)
+ return -ENOMEM;
+
+ bsscmd = (struct sip_cmd_set_wmm_params *)(skb->data + sizeof(struct sip_hdr));
+ bsscmd->aci = aci;
+ bsscmd->aifs = params->aifs;
+ bsscmd->txop_us = params->txop * 32;
+ bsscmd->ecw_min = 32 - __builtin_clz(params->cw_min);
+ bsscmd->ecw_max = 32 - __builtin_clz(params->cw_max);
+
+ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
+}
+
+int sip_send_ampdu_action(struct esp_pub *epub, u8 action_num, const u8 *addr,
+ u16 tid, u16 ssn, u8 buf_size)
+{
+ int index = 0;
+ struct sk_buff *skb;
+ struct sip_cmd_ampdu_action *action;
+
+ if (action_num == SIP_AMPDU_RX_START)
+ index = esp_get_empty_rxampdu(epub, addr, tid);
+ else if (action_num == SIP_AMPDU_RX_STOP)
+ index = esp_get_exist_rxampdu(epub, addr, tid);
+
+ if (index < 0)
+ return -EACCES;
+
+ skb = sip_alloc_ctrl_skbuf(epub->sip,
+ sizeof(struct sip_cmd_ampdu_action) +
+ sizeof(struct sip_hdr),
+ SIP_CMD_AMPDU_ACTION);
+ if (!skb)
+ return -ENOMEM;
+
+ action = (struct sip_cmd_ampdu_action *)(skb->data + sizeof(struct sip_hdr));
+ action->action = action_num;
+ //for TX, it means interface index
+ action->index = ssn;
+
+ switch (action_num) {
+ case SIP_AMPDU_RX_START:
+ action->ssn = ssn;
+ case SIP_AMPDU_RX_STOP:
+ action->index = index;
+ case SIP_AMPDU_TX_OPERATIONAL:
+ case SIP_AMPDU_TX_STOP:
+ action->win_size = buf_size;
+ action->tid = tid;
+ memcpy(action->addr, addr, ETH_ALEN);
+ break;
+ }
+
+ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
+}
+
+#ifdef HW_SCAN
+/* send cmd to target, if aborted is true, inform target stop scan, report scan complete imediately
+ * return 1: complete over, 0: success, still have next scan, -1: hardware failure
+ */
+int sip_send_scan(struct esp_pub *epub)
+{
+ struct cfg80211_scan_request *scan_req = epub->wl.scan_req;
+ struct sk_buff *skb;
+ struct sip_cmd_scan *scancmd;
+ u8 *ptr;
+ int i;
+ u8 append_len, ssid_len;
+
+ ESSERT(scan_req);
+ if (!scan_req->n_ssids)
+ ssid_len = 0;
+ else if (scan_req->n_ssids == 1)
+ ssid_len = scan_req->ssids->ssid_len;
+ /* Limit to two SSIDs */
+ else
+ ssid_len = scan_req->ssids->ssid_len +
+ (scan_req->ssids + 1)->ssid_len;
+
+ append_len = ssid_len + scan_req->n_channels + scan_req->ie_len;
+
+ skb = sip_alloc_ctrl_skbuf(epub->sip, sizeof(struct sip_cmd_scan) +
+ sizeof(struct sip_hdr) + append_len,
+ SIP_CMD_SCAN);
+
+ if (!skb)
+ return -ENOMEM;
+
+ ptr = skb->data;
+ scancmd = (struct sip_cmd_scan *)(ptr + sizeof(struct sip_hdr));
+
+ ptr += sizeof(struct sip_hdr) + sizeof(struct sip_cmd_scan);
+ scancmd->aborted = false;
+
+ /* FIXME: meh */
+ if (scan_req->n_ssids <= 0) {
+ scancmd->ssid_len = 0;
+ } else {
+ scancmd->ssid_len = ssid_len;
+ if (scan_req->n_ssids == 1)
+ memcpy(ptr, scan_req->ssids->ssid, scancmd->ssid_len);
+ /* Limit to two SSIDs */
+ else
+ memcpy(ptr, (scan_req->ssids + 1)->ssid,
+ scancmd->ssid_len);
+ }
+
+ ptr += scancmd->ssid_len;
+ scancmd->n_channels = scan_req->n_channels;
+ for (i = 0; i < scan_req->n_channels; i++)
+ ptr[i] = scan_req->channels[i]->hw_value;
+
+ ptr += scancmd->n_channels;
+ if (scan_req->ie_len && scan_req->ie) {
+ scancmd->ie_len = scan_req->ie_len;
+ memcpy(ptr, scan_req->ie, scan_req->ie_len);
+ } else {
+ scancmd->ie_len = 0;
+ }
+ //add a flag that support two ssids,
+ if (scan_req->n_ssids > 1)
+ scancmd->ssid_len |= 0x80;
+
+ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
+}
+#endif
+
+void sip_scandone_process(struct esp_sip *sip,
+ struct sip_evt_scan_report *scan_report)
+{
+ struct esp_pub *epub = sip->epub;
+
+ if (epub->wl.scan_req) {
+ hw_scan_done(epub, scan_report->aborted);
+ epub->wl.scan_req = NULL;
+ }
+}
+
+int sip_send_setkey(struct esp_pub *epub, u8 bssid_no, u8 *peer_addr,
+ struct ieee80211_key_conf *key, u8 isvalid)
+{
+ struct sip_cmd_setkey *setkeycmd;
+ struct sk_buff *skb;
+
+ skb = sip_alloc_ctrl_skbuf(epub->sip, sizeof(struct sip_cmd_setkey) +
+ sizeof(struct sip_hdr), SIP_CMD_SETKEY);
+
+ if (!skb)
+ return -ENOMEM;
+
+ setkeycmd = (struct sip_cmd_setkey *)(skb->data + sizeof(struct sip_hdr));
+
+ if (peer_addr)
+ memcpy(setkeycmd->addr, peer_addr, ETH_ALEN);
+ else
+ memset(setkeycmd->addr, 0, ETH_ALEN);
+
+ setkeycmd->bssid_no = bssid_no;
+ setkeycmd->hw_key_idx = key->hw_key_idx;
+
+ if (isvalid) {
+ setkeycmd->alg = esp_cipher2alg(key->cipher);
+ setkeycmd->keyidx = key->keyidx;
+ setkeycmd->keylen = key->keylen;
+ if (key->cipher == WLAN_CIPHER_SUITE_TKIP) {
+ memcpy(setkeycmd->key, key->key, 16);
+ memcpy(setkeycmd->key + 16, key->key + 24, 8);
+ memcpy(setkeycmd->key + 24, key->key + 16, 8);
+ } else {
+ memcpy(setkeycmd->key, key->key, key->keylen);
+ }
+
+ setkeycmd->flags = 1;
+ } else {
+ setkeycmd->flags = 0;
+ }
+
+ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
+}
+
+//remain_on_channel
+int sip_send_roc(struct esp_pub *epub, u16 center_freq, u16 duration)
+{
+ struct sk_buff *skb;
+ struct sip_cmd_config *configcmd;
+
+ skb = sip_alloc_ctrl_skbuf(epub->sip, sizeof(struct sip_cmd_config) +
+ sizeof(struct sip_hdr), SIP_CMD_CONFIG);
+ if (!skb)
+ return -ENOMEM;
+
+ configcmd = (struct sip_cmd_config *)(skb->data + sizeof(struct sip_hdr));
+ configcmd->center_freq = center_freq;
+ configcmd->duration = duration;
+
+ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
+}
+
+int sip_send_set_sta(struct esp_pub *epub, u8 ifidx, u8 set,
+ struct ieee80211_sta *sta, struct ieee80211_vif *vif,
+ u8 index)
+{
+ struct sk_buff *skb;
+ struct sip_cmd_setsta *setstacmd;
+
+ skb = sip_alloc_ctrl_skbuf(epub->sip, sizeof(struct sip_cmd_setsta) +
+ sizeof(struct sip_hdr), SIP_CMD_SETSTA);
+ if (!skb)
+ return -ENOMEM;
+
+ setstacmd = (struct sip_cmd_setsta *)(skb->data + sizeof(struct sip_hdr));
+ setstacmd->ifidx = ifidx;
+ setstacmd->index = index;
+ setstacmd->set = set;
+
+ if (!sta->aid)
+ setstacmd->aid = vif->bss_conf.aid;
+ else
+ setstacmd->aid = sta->aid;
+
+ memcpy(setstacmd->mac, sta->addr, ETH_ALEN);
+
+ if (!set)
+ goto send;
+
+ if (sta->ht_cap.ht_supported) {
+ if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20)
+ setstacmd->phymode = ESP_IEEE80211_T_HT20_S;
+ else
+ setstacmd->phymode = ESP_IEEE80211_T_HT20_L;
+ setstacmd->ampdu_factor = sta->ht_cap.ampdu_factor;
+ setstacmd->ampdu_density = sta->ht_cap.ampdu_density;
+ goto send;
+ }
+
+ if (sta->supp_rates[NL80211_BAND_2GHZ] & ~CONF_HW_BIT_RATE_11B_MASK)
+ setstacmd->phymode = ESP_IEEE80211_T_OFDM;
+ else
+ setstacmd->phymode = ESP_IEEE80211_T_CCK;
+
+send:
+ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
+}
+
+int sip_send_recalc_credit(struct esp_pub *epub)
+{
+ struct sk_buff *skb;
+
+ skb = sip_alloc_ctrl_skbuf(epub->sip, sizeof(struct sip_hdr),
+ SIP_CMD_RECALC_CREDIT);
+ if (!skb)
+ return -ENOMEM;
+
+ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_HEAD);
+}
+
+int sip_cmd(struct esp_pub *epub, enum sip_cmd_id cmd_id, u8 *cmd_buf,
+ u8 cmd_len)
+{
+ struct sk_buff *skb;
+
+ skb = sip_alloc_ctrl_skbuf(epub->sip, cmd_len + sizeof(struct sip_hdr),
+ cmd_id);
+ if (!skb)
+ return -ENOMEM;
+
+ memcpy(skb->data + sizeof(struct sip_hdr), cmd_buf, cmd_len);
+
+ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
+}
diff --git a/drivers/staging/esp8089/esp_ctrl.h b/drivers/staging/esp8089/esp_ctrl.h
new file mode 100644
index 000000000000..b9143bb135b3
--- /dev/null
+++ b/drivers/staging/esp8089/esp_ctrl.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2009 - 2014 Espressif System.
+ * Copyright (c) 2017 Free Electrons
+ * Quentin Schulz <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ */
+#ifndef _ESP_CTRL_H_
+#define _ESP_CTRL_H_
+
+extern struct completion *gl_bootup_cplx;
+
+int sip_send_config(struct esp_pub *epub, struct ieee80211_conf *conf);
+
+int sip_send_setkey(struct esp_pub *epub, u8 bssid_no, u8 *peer_addr,
+ struct ieee80211_key_conf *key, u8 isvalid);
+
+int sip_send_scan(struct esp_pub *epub);
+
+void sip_scandone_process(struct esp_sip *sip,
+ struct sip_evt_scan_report *scan_report);
+
+int sip_send_bss_info_update(struct esp_pub *epub, struct esp_vif *evif,
+ u8 *bssid, int assoc);
+
+int sip_send_wmm_params(struct esp_pub *epub, u8 aci,
+ const struct ieee80211_tx_queue_params *params);
+
+int sip_send_ampdu_action(struct esp_pub *epub, u8 action_num, const u8 *addr,
+ u16 tid, u16 ssn, u8 buf_size);
+
+int sip_send_roc(struct esp_pub *epub, u16 center_freq, u16 duration);
+
+int sip_send_set_sta(struct esp_pub *epub, u8 ifidx, u8 set,
+ struct ieee80211_sta *sta, struct ieee80211_vif *vif,
+ u8 index);
+
+int sip_parse_events(struct esp_sip *sip, u8 *buf);
+
+int sip_send_recalc_credit(struct esp_pub *epub);
+
+int sip_cmd(struct esp_pub *epub, enum sip_cmd_id cmd_id, u8 *cmd_buf,
+ u8 cmd_len);
+
+#endif /* _ESP_CTRL_H_ */
diff --git a/drivers/staging/esp8089/esp_debug.c b/drivers/staging/esp8089/esp_debug.c
new file mode 100644
index 000000000000..26472b433768
--- /dev/null
+++ b/drivers/staging/esp8089/esp_debug.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2011 - 2014 Espressif System.
+ * Copyright (c) 2017 Free Electrons
+ * Quentin Schulz <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <net/mac80211.h>
+#include "sip2_common.h"
+
+#include "esp_debug.h"
+
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_ESP8089_DEBUG_FS)
+
+static struct dentry *esp_debugfs_root;
+
+static ssize_t esp_debugfs_read(struct file *filp, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ if (*ppos >= 32)
+ return 0;
+
+ if (*ppos + count > 32)
+ count = 32 - *ppos;
+
+ if (copy_to_user(buffer, filp->private_data + *ppos, count))
+ return -EFAULT;
+
+ *ppos += count;
+
+ return count;
+}
+
+static ssize_t esp_debugfs_write(struct file *filp, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ if (*ppos >= 32)
+ return 0;
+
+ if (*ppos + count > 32)
+ count = 32 - *ppos;
+
+ if (copy_from_user(filp->private_data + *ppos, buffer, count))
+ return -EFAULT;
+
+ *ppos += count;
+
+ return count;
+}
+
+const struct file_operations esp_debugfs_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = esp_debugfs_read,
+ .write = esp_debugfs_write,
+};
+
+struct dentry *esp_dump_var(const char *name, struct dentry *parent,
+ void *value, enum esp_type type)
+{
+ struct dentry *rc = NULL;
+ umode_t mode = 0644;
+
+ if (!esp_debugfs_root)
+ return NULL;
+
+ if (!parent)
+ parent = esp_debugfs_root;
+
+ switch (type) {
+ case ESP_U8:
+ rc = debugfs_create_u8(name, mode, parent, (u8 *)value);
+ break;
+ case ESP_U16:
+ rc = debugfs_create_u16(name, mode, parent, (u16 *)value);
+ break;
+ case ESP_U32:
+ rc = debugfs_create_u32(name, mode, parent, (u32 *)value);
+ break;
+ case ESP_U64:
+ rc = debugfs_create_u64(name, mode, parent, (u64 *)value);
+ break;
+ case ESP_BOOL:
+ rc = debugfs_create_bool(name, mode, parent, (bool *)value);
+ break;
+ default: //32
+ rc = debugfs_create_u32(name, mode, parent, (u32 *)value);
+ }
+
+ if (!rc)
+ goto _fail;
+
+ return rc;
+
+_fail:
+ debugfs_remove_recursive(esp_debugfs_root);
+ esp_debugfs_root = NULL;
+ printk("%s failed, debugfs root removed; var name: %s\n", __func__,
+ name);
+ return NULL;
+}
+
+struct dentry *esp_dump_array(const char *name, struct dentry *parent,
+ struct debugfs_blob_wrapper *blob)
+{
+ struct dentry *rc;
+ umode_t mode = 0644;
+
+ if (!esp_debugfs_root)
+ return NULL;
+
+ if (!parent)
+ parent = esp_debugfs_root;
+
+ rc = debugfs_create_blob(name, mode, parent, blob);
+ if (!rc)
+ goto _fail;
+
+ return rc;
+
+_fail:
+ debugfs_remove_recursive(esp_debugfs_root);
+ esp_debugfs_root = NULL;
+ printk("%s failed, debugfs root removed; var name: %s\n", __func__,
+ name);
+ return NULL;
+}
+
+struct dentry *esp_dump(const char *name, struct dentry *parent,
+ void *data, int size)
+{
+ struct dentry *rc;
+ umode_t mode = 0644;
+
+ if (!esp_debugfs_root)
+ return NULL;
+
+ if (!parent)
+ parent = esp_debugfs_root;
+
+ rc = debugfs_create_file(name, mode, parent, data, &esp_debugfs_fops);
+ if (!rc)
+ goto _fail;
+
+ return rc;
+
+_fail:
+ debugfs_remove_recursive(esp_debugfs_root);
+ esp_debugfs_root = NULL;
+ printk("%s failed, debugfs root removed; var name: %s\n", __func__,
+ name);
+ return NULL;
+}
+
+struct dentry *esp_debugfs_add_sub_dir(const char *name)
+{
+ struct dentry *sub_dir;
+
+ sub_dir = debugfs_create_dir(name, esp_debugfs_root);
+ if (!sub_dir)
+ goto _fail;
+
+ return sub_dir;
+
+_fail:
+ debugfs_remove_recursive(esp_debugfs_root);
+ esp_debugfs_root = NULL;
+ printk("%s failed, debugfs root removed; dir name: %s\n", __func__,
+ name);
+ return NULL;
+}
+
+int esp_debugfs_init(void)
+{
+ esp_debugfs_root = debugfs_create_dir("esp_debug", NULL);
+
+ if (IS_ERR_OR_NULL(esp_debugfs_root))
+ return -ENOENT;
+
+ return 0;
+}
+
+void esp_debugfs_exit(void)
+{
+ debugfs_remove_recursive(esp_debugfs_root);
+}
+
+#else
+
+inline struct dentry *esp_dump_var(const char *name, struct dentry *parent,
+ void *value, enum esp_type type)
+{
+ return NULL;
+}
+
+inline struct dentry *esp_dump_array(const char *name, struct dentry *parent,
+ struct debugfs_blob_wrapper *blob)
+{
+ return NULL;
+}
+
+inline struct dentry *esp_dump(const char *name, struct dentry *parent,
+ void *data, int size)
+{
+ return NULL;
+}
+
+struct dentry *esp_debugfs_add_sub_dir(const char *name)
+{
+ return NULL;
+}
+
+inline int esp_debugfs_init(void)
+{
+ return -EPERM;
+}
+
+inline void esp_debugfs_exit(void)
+{
+}
+
+#endif
+
+/* FIXME: What's the actual usecase? */
+void show_buf(u8 *buf, u32 len)
+{
+ int i = 0, j;
+
+ printk(KERN_INFO "\n++++++++++++++++show rbuf+++++++++++++++\n");
+ for (i = 0; i < (len / 16); i++) {
+ j = i * 16;
+ printk(KERN_INFO
+ "0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
+ buf[j], buf[j + 1], buf[j + 2], buf[j + 3],
+ buf[j + 4], buf[j + 5], buf[j + 6], buf[j + 7],
+ buf[j + 8], buf[j + 9], buf[j + 10], buf[j + 11],
+ buf[j + 12], buf[j + 13], buf[j + 14], buf[j + 15]);
+ }
+ printk(KERN_INFO "\n++++++++++++++++++++++++++++++++++++++++\n");
+}
diff --git a/drivers/staging/esp8089/esp_debug.h b/drivers/staging/esp8089/esp_debug.h
new file mode 100644
index 000000000000..eb9bddfe9842
--- /dev/null
+++ b/drivers/staging/esp8089/esp_debug.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2011 - 2014 Espressif System.
+ * Copyright (c) 2017 Free Electrons
+ * Quentin Schulz <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ */
+
+#ifndef _DEBUG_H_
+
+#ifdef ASSERT_PANIC
+#define ESSERT(v) BUG_ON(!(v))
+#else
+#define ESSERT(v) if(!(v)) printk("ESSERT:%s %d\n", __FILE__, __LINE__)
+#endif
+
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+
+enum esp_type {
+ ESP_BOOL,
+ ESP_U8,
+ ESP_U16,
+ ESP_U32,
+ ESP_U64
+};
+
+struct dentry *esp_dump_var(const char *name, struct dentry *parent,
+ void *value, enum esp_type type);
+
+struct dentry *esp_dump_array(const char *name, struct dentry *parent,
+ struct debugfs_blob_wrapper *blob);
+
+struct dentry *esp_dump(const char *name, struct dentry *parent, void *data,
+ int size);
+
+struct dentry *esp_debugfs_add_sub_dir(const char *name);
+
+int esp_debugfs_init(void);
+
+void esp_debugfs_exit(void);
+
+enum {
+ ESP_DBG_ERROR = BIT(0),
+ ESP_DBG_TRACE = BIT(1),
+ ESP_DBG_LOG = BIT(2),
+ ESP_DBG = BIT(3),
+ ESP_SHOW = BIT(4),
+ ESP_DBG_TXAMPDU = BIT(5),
+ ESP_DBG_OP = BIT(6),
+ ESP_DBG_PS = BIT(7),
+ ESP_ATE = BIT(8),
+ ESP_DBG_ALL = GENMASK(31, 0)
+};
+
+extern unsigned int esp_msg_level;
+
+#define esp_dbg(mask, fmt, args...) do { \
+ if (esp_msg_level & mask) \
+ printk(fmt, ##args); \
+} while (0)
+
+void show_buf(u8 *buf, u32 len);
+
+#endif /* _DEBUG_H_ */
diff --git a/drivers/staging/esp8089/esp_file.c b/drivers/staging/esp8089/esp_file.c
new file mode 100644
index 000000000000..7c7ef0d83693
--- /dev/null
+++ b/drivers/staging/esp8089/esp_file.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2010 - 2014 Espressif System.
+ * Copyright (c) 2017 Free Electrons
+ * Quentin Schulz <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/kernel.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <linux/netdevice.h>
+#include <linux/aio.h>
+#include <linux/property.h>
+
+#include "esp_file.h"
+#include "esp_debug.h"
+#include "esp_sif.h"
+
+/* TODO use proper module param for each value instead of a big one */
+static char *modparam_init_data_conf;
+module_param_named(config, modparam_init_data_conf, charp, 0444);
+MODULE_PARM_DESC(config, "Firmware init config string (format: key=value;)");
+
+struct esp_init_table_elem esp_init_table[MAX_ATTR_NUM] = {
+ /* Crystal type:
+ * 0: 40MHz (default)
+ * 1: 26MHz (ESP8266 ESP-12F)
+ */
+ {"crystal_26M_en", 48, 0},
+ /* Output crystal clock to pin:
+ * 0: None
+ * 1: GPIO1
+ * 2: URXD0
+ */
+ {"test_xtal", 49, 0},
+ /* Host SDIO mode:
+ * 0: Auto by pin strapping
+ * 1: SDIO data output on negative edges (SDIO v1.1)
+ * 2: SDIO data output on positive edges (SDIO v2.0)
+ */
+ {"sdio_configure", 50, 2},
+ /* WiFi/Bluetooth co-existence with BK3515A BT chip
+ * 0: None
+ * 1: GPIO0->WLAN_ACTIVE, MTMS->BT_ACTIVE, MTDI->BT_PRIORITY,
+ * U0TXD->ANT_SEL_BT, U0RXD->ANT_SEL_WIFI
+ */
+ {"bt_configure", 51, 0},
+ /* Antenna selection:
+ * 0: Antenna is for WiFi
+ * 1: Antenna is for Bluetooth
+ */
+ {"bt_protocol", 52, 0},
+ /* Dual antenna configuration mode:
+ * 0: None
+ * 1: U0RXD + XPD_DCDC
+ * 2: U0RXD + GPIO0
+ * 3: U0RXD + U0TXD
+ */
+ {"dual_ant_configure", 53, 0},
+ /* Firmware debugging output pin:
+ * 0: None
+ * 1: UART TX on GPIO2
+ * 2: UART TX on U0TXD
+ */
+ {"test_uart_configure", 54, 2},
+ /* Whether to share crystal clock with BT (in sleep mode):
+ * 0: no
+ * 1: always on
+ * 2: automatically on according to XPD_DCDC
+ */
+ {"share_xtal", 55, 0},
+ /* Allow chip to be woken up during sleep on pin:
+ * 0: None
+ * 1: XPD_DCDC
+ * 2: GPIO0
+ * 3: Both XPD_DCDC and GPIO0
+ */
+ {"gpio_wake", 56, 0},
+ {"no_auto_sleep", 57, 0},
+ {"speed_suspend", 58, 0},
+ {"attr11", -1, -1},
+ {"attr12", -1, -1},
+ {"attr13", -1, -1},
+ {"attr14", -1, -1},
+ {"attr15", -1, -1},
+ //attr that is not send to target
+ /* Allow chip to be reset by GPIO pin:
+ * 0: no
+ * 1: yes
+ */
+ {"ext_rst", -1, 0},
+ {"wakeup_gpio", -1, 12},
+ {"ate_test", -1, 0},
+ {"attr19", -1, -1},
+ {"attr20", -1, -1},
+ {"attr21", -1, -1},
+ {"attr22", -1, -1},
+ {"attr23", -1, -1},
+};
+
+/* update init config table */
+static int set_init_config_attr(const char *attr, int attr_len, short value)
+{
+ int i;
+
+ for (i = 0; i < MAX_ATTR_NUM; i++)
+ if (!memcmp(esp_init_table[i].attr, attr, attr_len)) {
+ if (value < 0 || value > 255) {
+ printk("%s: attribute value for %s is out of range",
+ __func__, esp_init_table[i].attr);
+ return -1;
+ }
+
+ esp_init_table[i].value = value;
+ return 0;
+ }
+
+ return -1;
+}
+
+static int update_init_config_attr(const char *attr, int attr_len,
+ const char *val, int val_len)
+{
+ char digits[4];
+ short value;
+ int i;
+
+ for (i = 0; i < sizeof(digits) - 1 && i < val_len; i++)
+ digits[i] = val[i];
+
+ digits[i] = 0;
+
+ if (kstrtou16(digits, 10, &value) < 0) {
+ printk("%s: invalid attribute value: %s", __func__, digits);
+ return -1;
+ }
+
+ return set_init_config_attr(attr, attr_len, value);
+}
+
+/* export config table settings to SDIO driver */
+static void record_init_config(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_ATTR_NUM; i++) {
+ if (esp_init_table[i].value < 0)
+ continue;
+
+ if (!strcmp(esp_init_table[i].attr, "share_xtal"))
+ sif_record_bt_config(esp_init_table[i].value);
+ else if (!strcmp(esp_init_table[i].attr, "ext_rst"))
+ sif_record_rst_config(esp_init_table[i].value);
+ else if (!strcmp(esp_init_table[i].attr, "wakeup_gpio"))
+ sif_record_wakeup_gpio_config(esp_init_table[i].value);
+ else if (!strcmp(esp_init_table[i].attr, "ate_test"))
+ sif_record_ate_config(esp_init_table[i].value);
+ }
+}
+
+int request_init_conf(struct device *dev)
+{
+ char *attr, *str, *p;
+ int attr_len, str_len;
+ int ret = 0;
+ u32 val;
+
+ /* Check for any parameters passed through devicetree (or acpi) */
+ if (!device_property_read_u32(dev, "esp,crystal-26M-en", &val))
+ set_init_config_attr("crystal_26M_en", strlen("crystal_26M_en"),
+ val);
+
+ /* parse optional parameter in the form of key1=value,key2=value,.. */
+ attr = NULL;
+ attr_len = 0;
+ str_len = 0;
+ for (p = str = modparam_init_data_conf; p && *p; p++) {
+ if (*p == '=') {
+ attr = str;
+ attr_len = str_len;
+
+ str = p + 1;
+ str_len = 0;
+ } else if (*p == ',' || *p == ';') {
+ if (attr_len)
+ ret |= update_init_config_attr(attr, attr_len,
+ str, str_len);
+
+ str = p + 1;
+ attr_len = 0;
+ str_len = 0;
+ } else {
+ str_len++;
+ }
+ }
+
+ if (attr_len && str != attr)
+ ret |= update_init_config_attr(attr, attr_len, str, str_len);
+
+ record_init_config();
+
+ return ret;
+}
+
+void fix_init_data(u8 *init_data_buf, int buf_size)
+{
+ int i;
+
+ for (i = 0; i < MAX_FIX_ATTR_NUM; i++) {
+ if (esp_init_table[i].offset > -1 &&
+ esp_init_table[i].offset < buf_size &&
+ esp_init_table[i].value > -1)
+ *(u8 *)(init_data_buf + esp_init_table[i].offset) = esp_init_table[i].value;
+ }
+}
diff --git a/drivers/staging/esp8089/esp_file.h b/drivers/staging/esp8089/esp_file.h
new file mode 100644
index 000000000000..ba9946d64c32
--- /dev/null
+++ b/drivers/staging/esp8089/esp_file.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2010 - 2014 Espressif System.
+ * Copyright (c) 2017 Free Electrons
+ * Quentin Schulz <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ */
+
+#ifndef _ESP_FILE_H_
+#define _ESP_FILE_H_
+
+#include <linux/firmware.h>
+
+#define CONF_ATTR_LEN 24
+#define MAX_ATTR_NUM 24
+#define MAX_FIX_ATTR_NUM 16
+
+struct esp_init_table_elem {
+ char attr[CONF_ATTR_LEN];
+ int offset;
+ short value;
+};
+
+int request_init_conf(struct device *dev);
+void fix_init_data(u8 *init_data_buf, int buf_size);
+
+#endif /* _ESP_FILE_H_ */
diff --git a/drivers/staging/esp8089/esp_init_data.h b/drivers/staging/esp8089/esp_init_data.h
new file mode 100644
index 000000000000..2494c02a10be
--- /dev/null
+++ b/drivers/staging/esp8089/esp_init_data.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2009 - 2014 Espressif System.
+ * Copyright (c) 2017 Free Electrons
+ * Quentin Schulz <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ */
+
+static char esp_init_data[] = { 0x5, 0x0, 4, 2, 5, 5, 5, 2, 5, 0, 4, 5, 5, 4, 5, 5, 4, -2, -3, -1,
+ -16, -16, -16, -32, -32, -32, 204, 1, 0xff, 0xff, 0, 0, 0, 0, 82, 78, 74, 68, 64, 56, 0,
+ 0, 1, 1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 10, 0x0, 0x0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
diff --git a/drivers/staging/esp8089/esp_io.c b/drivers/staging/esp8089/esp_io.c
new file mode 100644
index 000000000000..756bd6260b24
--- /dev/null
+++ b/drivers/staging/esp8089/esp_io.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2009 - 2014 Espressif System.
+ * Copyright (c) 2017 Free Electrons
+ * Quentin Schulz <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ */
+
+#include <linux/mmc/sdio_func.h>
+#include "esp_sif.h"
+#include "slc_host_register.h"
+#include "esp_debug.h"
+
+int esp_common_read(struct esp_pub *epub, u8 *buf, u32 len, int sync,
+ bool noround)
+{
+ if (sync)
+ return sif_lldesc_read_sync(epub, buf, len);
+
+ return sif_lldesc_read_raw(epub, buf, len, noround);
+}
+
+int esp_common_write(struct esp_pub *epub, u8 *buf, u32 len, int sync)
+{
+ if (sync)
+ return sif_lldesc_write_sync(epub, buf, len);
+
+ return sif_lldesc_write_raw(epub, buf, len);
+}
+
+int esp_common_read_with_addr(struct esp_pub *epub, u32 addr, u8 *buf, u32 len,
+ int sync)
+{
+ if (sync)
+ return sif_io_sync(epub, addr, buf, len, SIF_FROM_DEVICE |
+ SIF_SYNC | SIF_BYTE_BASIS | SIF_INC_ADDR);
+
+ return sif_io_raw(epub, addr, buf, len, SIF_FROM_DEVICE |
+ SIF_BYTE_BASIS | SIF_INC_ADDR);
+}
+
+int esp_common_write_with_addr(struct esp_pub *epub, u32 addr, u8 *buf,
+ u32 len, int sync)
+{
+ if (sync)
+ return sif_io_sync(epub, addr, buf, len, SIF_TO_DEVICE |
+ SIF_SYNC | SIF_BYTE_BASIS | SIF_INC_ADDR);
+
+ return sif_io_raw(epub, addr, buf, len, SIF_TO_DEVICE | SIF_BYTE_BASIS |
+ SIF_INC_ADDR);
+}
+
+int esp_common_readbyte_with_addr(struct esp_pub *epub, u32 addr, u8 *buf,
+ int sync)
+{
+ int res;
+
+ if (sync)
+ sif_lock_bus(epub);
+
+ *buf = sdio_io_readb(epub, addr, &res);
+
+ if (sync)
+ sif_unlock_bus(epub);
+
+ return res;
+}
+
+int esp_common_writebyte_with_addr(struct esp_pub *epub, u32 addr, u8 buf,
+ int sync)
+{
+ int res;
+
+ if (sync)
+ sif_lock_bus(epub);
+
+ sdio_io_writeb(epub, buf, addr, &res);
+
+ if (sync)
+ sif_unlock_bus(epub);
+
+ return res;
+}
+
+enum _SDIO_INTR_MODE {
+ SDIO_INTR_IB = 0,
+ SDIO_INTR_OOB_TOGGLE,
+ SDIO_INTR_OOB_HIGH_LEVEL,
+ SDIO_INTR_OOB_LOW_LEVEL,
+};
+
+#define GEN_GPIO_SEL(_gpio_num, _sel_func, _intr_mode, _offset) (((_offset) << 9) | ((_intr_mode) << 7) | ((_sel_func) << 4) | (_gpio_num))
+//bit[3:0] = gpio num, 2
+//bit[6:4] = gpio sel func, 0
+//bit[8:7] = gpio intr mode, SDIO_INTR_OOB_TOGGLE
+//bit[15:9] = register offset, 0x38
+
+u16 gpio_sel_sets[17] = {
+ GEN_GPIO_SEL(0, 0, SDIO_INTR_OOB_TOGGLE, 0x34), //GPIO0
+ GEN_GPIO_SEL(1, 3, SDIO_INTR_OOB_TOGGLE, 0x18), //U0TXD
+ GEN_GPIO_SEL(2, 0, SDIO_INTR_OOB_TOGGLE, 0x38), //GPIO2
+ GEN_GPIO_SEL(3, 3, SDIO_INTR_OOB_TOGGLE, 0x14), //U0RXD
+ GEN_GPIO_SEL(4, 0, SDIO_INTR_OOB_TOGGLE, 0x3C), //GPIO4
+ GEN_GPIO_SEL(5, 0, SDIO_INTR_OOB_TOGGLE, 0x40), //GPIO5
+ GEN_GPIO_SEL(6, 3, SDIO_INTR_OOB_TOGGLE, 0x1C), //SD_CLK
+ GEN_GPIO_SEL(7, 3, SDIO_INTR_OOB_TOGGLE, 0x20), //SD_DATA0
+ GEN_GPIO_SEL(8, 3, SDIO_INTR_OOB_TOGGLE, 0x24), //SD_DATA1
+ GEN_GPIO_SEL(9, 3, SDIO_INTR_OOB_TOGGLE, 0x28), //SD_DATA2
+ GEN_GPIO_SEL(10, 3, SDIO_INTR_OOB_TOGGLE, 0x2C), //SD_DATA3
+ GEN_GPIO_SEL(11, 3, SDIO_INTR_OOB_TOGGLE, 0x30), //SD_CMD
+ GEN_GPIO_SEL(12, 3, SDIO_INTR_OOB_TOGGLE, 0x04), //MTDI
+ GEN_GPIO_SEL(13, 3, SDIO_INTR_OOB_TOGGLE, 0x08), //MTCK
+ GEN_GPIO_SEL(14, 3, SDIO_INTR_OOB_TOGGLE, 0x0C), //MTMS
+ GEN_GPIO_SEL(15, 3, SDIO_INTR_OOB_TOGGLE, 0x10), //MTDO
+ //pls do not change sel before, if you want to change intr mode,change the one blow
+ //GEN_GPIO_SEL(2, 0, SDIO_INTR_OOB_TOGGLE, 0x38)
+ GEN_GPIO_SEL(2, 0, SDIO_INTR_OOB_LOW_LEVEL, 0x38)
+};
+
+int sif_interrupt_target(struct esp_pub *epub, u8 index)
+{
+ u8 low_byte = BIT(index);
+ return esp_common_writebyte_with_addr(epub, SLC_HOST_CONF_W4 + 2,
+ low_byte, ESP_SIF_NOSYNC);
+}
+
+void check_target_id(struct esp_pub *epub)
+{
+ u32 date;
+ int i;
+ u16 gpio_sel;
+ u8 byte2 = 0, byte3 = 0;
+
+ if (!epub || !epub->sif)
+ return;
+
+ sif_lock_bus(epub);
+
+ for (i = 0; i < 4; i++) {
+ esp_common_readbyte_with_addr(epub, SLC_HOST_DATE + i,
+ (u8 *)&date + i, ESP_SIF_NOSYNC);
+ esp_common_readbyte_with_addr(epub, SLC_HOST_ID + i,
+ (u8 *)&epub->sif->target_id + i,
+ ESP_SIF_NOSYNC);
+ }
+
+ sif_unlock_bus(epub);
+
+ switch (epub->sif->target_id) {
+ case 0x100:
+ epub->sif->slc_window_end_addr = 0x20000;
+ break;
+ case 0x600:
+ epub->sif->slc_window_end_addr = 0x20000 - 0x800;
+
+ if (sif_get_bt_config() == 1 && sif_get_rst_config() != 1) {
+ u8 gpio_num = sif_get_wakeup_gpio_config();
+ gpio_sel = gpio_sel_sets[gpio_num];
+ byte2 = gpio_sel;
+ byte3 = gpio_sel >> 8;
+ }
+
+ sif_lock_bus(epub);
+ esp_common_writebyte_with_addr(epub, SLC_HOST_CONF_W1, 0,
+ ESP_SIF_NOSYNC);
+ esp_common_writebyte_with_addr(epub, SLC_HOST_CONF_W1 + 1, 0,
+ ESP_SIF_NOSYNC);
+ esp_common_writebyte_with_addr(epub, SLC_HOST_CONF_W1 + 2,
+ byte2, ESP_SIF_NOSYNC);
+ esp_common_writebyte_with_addr(epub, SLC_HOST_CONF_W1 + 3,
+ byte3, ESP_SIF_NOSYNC);
+ sif_unlock_bus(epub);
+ break;
+ default:
+ epub->sif->slc_window_end_addr = 0x20000;
+ break;
+ }
+}
+
+u32 sif_get_blksz(struct esp_pub *epub)
+{
+ if (!epub || !epub->sif)
+ return 512;
+
+ return epub->sif->slc_blk_sz;
+}
+
+void sif_dsr(struct sdio_func *func)
+{
+ struct esp_sdio_ctrl *sctrl = sdio_get_drvdata(func);
+ struct slc_host_regs *regs = &sctrl->slc_regs;
+ int ret;
+
+ sdio_release_host(sctrl->func);
+
+ sif_lock_bus(sctrl->epub);
+ memset(regs, 0, sizeof(struct slc_host_regs));
+
+ ret = esp_common_read_with_addr(sctrl->epub, REG_SLC_HOST_BASE + 8,
+ (u8 *)regs, sizeof(*regs),
+ ESP_SIF_NOSYNC);
+
+ if (regs->intr_raw & SLC_HOST_RX_ST && !ret)
+ esp_dsr(sctrl->epub);
+ else
+ sif_unlock_bus(sctrl->epub);
+
+ /* FIXME: missing unlock_bus? */
+ sdio_claim_host(func);
+ atomic_set(&sctrl->irq_handling, 0);
+}
+
+struct slc_host_regs *sif_get_regs(struct esp_pub *epub)
+{
+ if (!epub || !epub->sif)
+ return NULL;
+
+ return &epub->sif->slc_regs;
+}
+
+void sif_disable_target_interrupt(struct esp_pub *epub)
+{
+ if (!epub || !epub->sif || !epub->sif->func)
+ return;
+
+ sif_lock_bus(epub);
+#ifdef HOST_RESET_BUG
+ mdelay(10);
+#endif
+ memset(epub->sif->dma_buffer, 0, sizeof(u32));
+ esp_common_write_with_addr(epub, SLC_HOST_INT_ENA,
+ epub->sif->dma_buffer, sizeof(u32),
+ ESP_SIF_NOSYNC);
+#ifdef HOST_RESET_BUG
+ mdelay(10);
+#endif
+
+ sif_unlock_bus(epub);
+
+ mdelay(1);
+
+ sif_lock_bus(epub);
+ sif_interrupt_target(epub, 7);
+ sif_unlock_bus(epub);
+}
+
+/* FIXME: MEH */
+
+static int bt_config;
+void sif_record_bt_config(int value)
+{
+ bt_config = value;
+}
+
+int sif_get_bt_config(void)
+{
+ return bt_config;
+}
+
+static int rst_config;
+void sif_record_rst_config(int value)
+{
+ rst_config = value;
+}
+
+int sif_get_rst_config(void)
+{
+ return rst_config;
+}
+
+static int ate_test;
+void sif_record_ate_config(int value)
+{
+ ate_test = value;
+}
+
+int sif_get_ate_config(void)
+{
+ return ate_test;
+}
+
+static int wakeup_gpio = 12;
+void sif_record_wakeup_gpio_config(int value)
+{
+ wakeup_gpio = value;
+}
+
+int sif_get_wakeup_gpio_config(void)
+{
+ return wakeup_gpio;
+}
diff --git a/drivers/staging/esp8089/esp_mac80211.c b/drivers/staging/esp8089/esp_mac80211.c
new file mode 100644
index 000000000000..fd5049fc1f6b
--- /dev/null
+++ b/drivers/staging/esp8089/esp_mac80211.c
@@ -0,0 +1,1496 @@
+/*
+ * Copyright (c) 2011 - 2014 Espressif System.
+ * Copyright (c) 2017 Free Electrons
+ * Quentin Schulz <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/workqueue.h>
+#include <linux/nl80211.h>
+#include <linux/ieee80211.h>
+#include <linux/slab.h>
+#include <net/cfg80211.h>
+#include <net/mac80211.h>
+#include <net/regulatory.h>
+#include "esp_pub.h"
+#include "esp_sip.h"
+#include "esp_ctrl.h"
+#include "esp_sif.h"
+#include "esp_debug.h"
+#include "esp_wl.h"
+#include "esp_utils.h"
+
+static u8 esp_mac_addr[ETH_ALEN * 2];
+static u8 getaddr_index(u8 *addr, struct esp_pub *epub);
+
+/*Handler that 802.11 module calls for each transmitted frame.
+skb contains the buffer starting from the IEEE 802.11 header.
+The low-level driver should send the frame out based on
+configuration in the TX control data. This handler should,
+preferably, never fail and stop queues appropriately.
+Must be atomic.*/
+static void esp_op_tx(struct ieee80211_hw *hw,
+ struct ieee80211_tx_control *control, struct sk_buff *skb)
+{
+ struct esp_pub *epub = (struct esp_pub *)hw->priv;
+
+ sip_tx_data_pkt_enqueue(epub, skb);
+ if (epub)
+ ieee80211_queue_work(hw, &epub->tx_work);
+}
+
+/*
+ Called before the first netdevice attached to the hardware
+ 2934 * is enabled. This should turn on the hardware and must turn on
+ 2935 * frame reception (for possibly enabled monitor interfaces.)
+ 2936 * Returns negative error codes, these may be seen in userspace,
+ 2937 * or zero.
+ 2938 * When the device is started it should not have a MAC address
+ 2939 * to avoid acknowledging frames before a non-monitor device
+ 2940 * is added.
+ 2941 * Must be implemented and can sleep.
+*/
+static int esp_op_start(struct ieee80211_hw *hw)
+{
+ struct esp_pub *epub;
+
+ if (!hw || !hw->priv) {
+ return -EINVAL;
+ }
+
+ epub = (struct esp_pub *)hw->priv;
+
+ /*add rfkill poll function */
+
+ atomic_set(&epub->wl.off, 0);
+ wiphy_rfkill_start_polling(hw->wiphy);
+
+ return 0;
+}
+
+/*
+Called after last netdevice attached to the hardware
+2944 * is disabled. This should turn off the hardware (at least
+2945 * it must turn off frame reception.)
+2946 * May be called right after add_interface if that rejects
+2947 * an interface. If you added any work onto the mac80211 workqueue
+2948 * you should ensure to cancel it on this callback.
+2949 * Must be implemented and can sleep.
+*/
+static void esp_op_stop(struct ieee80211_hw *hw)
+{
+ struct esp_pub *epub;
+
+ if (!hw || !hw->priv) {
+ return;
+ }
+
+ epub = (struct esp_pub *)hw->priv;
+ atomic_set(&epub->wl.off, 1);
+
+#ifdef HOST_RESET_BUG
+ mdelay(200);
+#endif
+
+ if (epub->wl.scan_req) {
+ hw_scan_done(epub, true);
+ epub->wl.scan_req = NULL;
+ //msleep(2);
+ }
+
+ /* FIXME: does this 'turn off frame reception'? */
+ wiphy_rfkill_stop_polling(hw->wiphy);
+ /* FIXME: flush queues? */
+}
+
+static int esp_set_svif_mode(struct sip_cmd_setvif *svif,
+ enum nl80211_iftype type, bool p2p)
+{
+ switch (type) {
+ case NL80211_IFTYPE_STATION:
+ svif->op_mode = 0;
+ svif->is_p2p = p2p;
+ break;
+
+ case NL80211_IFTYPE_AP:
+ svif->op_mode = 1;
+ svif->is_p2p = p2p;
+ break;
+
+ case NL80211_IFTYPE_P2P_CLIENT:
+ svif->op_mode = 0;
+ svif->is_p2p = 1;
+ break;
+
+ case NL80211_IFTYPE_P2P_GO:
+ svif->op_mode = 1;
+ svif->is_p2p = 1;
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/*
+Called when a netdevice attached to the hardware is
+2973 * enabled. Because it is not called for monitor mode devices, @start
+2974 * and @stop must be implemented.
+2975 * The driver should perform any initialization it needs before
+2976 * the device can be enabled. The initial configuration for the
+2977 * interface is given in the conf parameter.
+2978 * The callback may refuse to add an interface by returning a
+2979 * negative error code (which will be seen in userspace.)
+2980 * Must be implemented and can sleep.
+ */
+static int esp_op_add_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct esp_pub *epub = (struct esp_pub *)hw->priv;
+ struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
+ struct sip_cmd_setvif svif;
+ int ret;
+
+ memcpy(svif.mac, vif->addr, ETH_ALEN);
+ svif.index = getaddr_index(vif->addr, epub);
+ evif->index = svif.index;
+ evif->epub = epub;
+ /* FIXME: why a need for evif? */
+ epub->vif = vif;
+ svif.set = 1;
+
+ if (svif.index == ESP_PUB_MAX_VIF) {
+ dev_err(epub->dev, "support for MAX %d interfaces\n",
+ ESP_PUB_MAX_VIF);
+ return -EOPNOTSUPP;
+ }
+
+ if (BIT(svif.index) & epub->vif_slot) {
+ dev_err(epub->dev, "interface %d already used\n", svif.index);
+ return -EOPNOTSUPP;
+ }
+
+ epub->vif_slot |= BIT(svif.index);
+
+ ret = esp_set_svif_mode(&svif, vif->type, false);
+ if (ret < 0) {
+ dev_err(epub->dev, "no support for interface type %d\n",
+ vif->type);
+ return ret;
+ }
+
+ sip_cmd(epub, SIP_CMD_SETVIF, (u8 *)&svif, sizeof(svif));
+
+ return 0;
+}
+
+/*
+Called when a netdevice changes type. This callback
+2983 * is optional, but only if it is supported can interface types be
+2984 * switched while the interface is UP. The callback may sleep.
+2985 * Note that while an interface is being switched, it will not be
+2986 * found by the interface iteration callbacks.
+ */
+static int esp_op_change_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum nl80211_iftype new_type, bool p2p)
+{
+ struct esp_pub *epub = (struct esp_pub *)hw->priv;
+ struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
+ struct sip_cmd_setvif svif;
+ int ret;
+
+ memcpy(svif.mac, vif->addr, ETH_ALEN);
+ svif.index = evif->index;
+ svif.set = 2;
+
+ ret = esp_set_svif_mode(&svif, new_type, p2p);
+ if (ret < 0)
+ return ret;
+
+ sip_cmd(epub, SIP_CMD_SETVIF, (u8 *)&svif, sizeof(svif));
+
+ return 0;
+}
+
+/*
+ Notifies a driver that an interface is going down.
+ 2989 * The @stop callback is called after this if it is the last interface
+ 2990 * and no monitor interfaces are present.
+ 2991 * When all interfaces are removed, the MAC address in the hardware
+ 2992 * must be cleared so the device no longer acknowledges packets,
+ 2993 * the mac_addr member of the conf structure is, however, set to the
+ 2994 * MAC address of the device going away.
+ 2995 * Hence, this callback must be implemented. It can sleep.
+ */
+static void esp_op_remove_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct esp_pub *epub = (struct esp_pub *)hw->priv;
+ struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
+ struct sip_cmd_setvif svif = {0};
+
+ svif.index = evif->index;
+ epub->vif_slot &= ~BIT(svif.index);
+
+ if (evif->ap_up) {
+ evif->beacon_interval = 0;
+ del_timer_sync(&evif->beacon_timer);
+ evif->ap_up = false;
+ }
+
+ epub->vif = NULL;
+ evif->epub = NULL;
+
+ sip_cmd(epub, SIP_CMD_SETVIF, (u8 *)&svif, sizeof(svif));
+ /* TODO: clean up tx/rx queue */
+}
+
+/* FIXME: WTF? */
+
+#define BEACON_TIM_SAVE_MAX 20
+u8 beacon_tim_saved[BEACON_TIM_SAVE_MAX];
+int beacon_tim_count;
+static void beacon_tim_init(void)
+{
+ memset(beacon_tim_saved, 0, BEACON_TIM_SAVE_MAX);
+ beacon_tim_count = 0;
+}
+
+static u8 beacon_tim_save(u8 this_tim)
+{
+ u8 all_tim = 0;
+ int i;
+
+ beacon_tim_saved[beacon_tim_count] = this_tim;
+
+ beacon_tim_count = (beacon_tim_count + 1) % BEACON_TIM_SAVE_MAX;
+
+ for (i = 0; i < BEACON_TIM_SAVE_MAX; i++)
+ all_tim |= beacon_tim_saved[i];
+
+ return all_tim;
+}
+
+static bool beacon_tim_alter(struct sk_buff *beacon)
+{
+ u8 *p, *tim_end;
+ u8 tim_count;
+ int len, remain_len;
+ struct ieee80211_mgmt *mgmt;
+
+ if (!beacon)
+ return false;
+
+ mgmt = (struct ieee80211_mgmt *)((u8 *)beacon->data);
+
+ remain_len = beacon->len - ((u8 *)mgmt->u.beacon.variable -
+ (u8 *)mgmt + 12);
+ p = mgmt->u.beacon.variable;
+
+ while (remain_len > 0) {
+ len = *(++p);
+
+ if (*p == WLAN_EID_TIM) { // tim field
+ tim_end = p + len;
+ tim_count = *(++p);
+ p += 2;
+ //multicast
+ if (!tim_count)
+ *p |= 0x1;
+
+ if (!(*p & 0xfe) && tim_end >= p + 1) { // we only support 8 sta in this case
+ p++;
+ *p = beacon_tim_save(*p);
+ }
+
+ return tim_count == 0;
+ }
+
+ p += len + 1;
+ remain_len -= 2 + len;
+ }
+
+ return false;
+}
+
+unsigned long init_jiffies;
+unsigned long cycle_beacon_count;
+static void drv_handle_beacon(unsigned long data)
+{
+ struct ieee80211_vif *vif = (struct ieee80211_vif *)data;
+ struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
+ struct sk_buff *beacon;
+ struct sk_buff *skb;
+ bool tim_reach;
+
+ if (!evif->epub)
+ return;
+
+ mdelay(2400 * (cycle_beacon_count % 25) % 10000 / 1000);
+
+ beacon = ieee80211_beacon_get(evif->epub->hw, vif);
+
+ tim_reach = beacon_tim_alter(beacon);
+
+ if (beacon)
+ sip_tx_data_pkt_enqueue(evif->epub, beacon);
+
+ if (cycle_beacon_count++ == 100) {
+ init_jiffies = jiffies;
+ cycle_beacon_count -= 100;
+ }
+
+ mod_timer(&evif->beacon_timer, init_jiffies +
+ msecs_to_jiffies(cycle_beacon_count *
+ vif->bss_conf.beacon_int * 1024 / 1000));
+ //FIXME:the packets must be sent at home channel
+ //send buffer mcast frames
+ if (tim_reach) {
+ skb = ieee80211_get_buffered_bc(evif->epub->hw, vif);
+ while (skb) {
+ sip_tx_data_pkt_enqueue(evif->epub, skb);
+ skb = ieee80211_get_buffered_bc(evif->epub->hw, vif);
+ }
+ }
+}
+
+static void init_beacon_timer(struct ieee80211_vif *vif)
+{
+ struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
+
+ beacon_tim_init();
+ setup_timer(&evif->beacon_timer, drv_handle_beacon, (unsigned long)vif); //TBD, not init here...
+ cycle_beacon_count = 1;
+ init_jiffies = jiffies;
+ evif->beacon_timer.expires = init_jiffies +
+ msecs_to_jiffies(cycle_beacon_count * vif->bss_conf.beacon_int *
+ 1024 / 1000);
+ add_timer(&evif->beacon_timer);
+}
+
+/*
+ Handler for configuration requests. IEEE 802.11 code calls this
+ 2998 * function to change hardware configuration, e.g., channel.
+ 2999 * This function should never fail but returns a negative error code
+ 3000 * if it does. The callback can sleep.
+ */
+static int esp_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+ struct esp_pub *epub = (struct esp_pub *)hw->priv;
+
+ if (changed & (IEEE80211_CONF_CHANGE_CHANNEL |
+ IEEE80211_CONF_CHANGE_IDLE))
+ sip_send_config(epub, &hw->conf);
+
+ return 0;
+}
+
+/*
+Handler for configuration requests related to BSS
+3003 * parameters that may vary during BSS's lifespan, and may affect low
+3004 * level driver (e.g. assoc/disassoc status, erp parameters).
+3005 * This function should not be used if no BSS has been set, unless
+3006 * for association indication. The @changed parameter indicates which
+3007 * of the bss parameters has changed when a call is made. The callback
+3008 * can sleep.
+ */
+static void esp_op_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+ u32 changed)
+{
+ struct esp_pub *epub = (struct esp_pub *)hw->priv;
+ struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
+ u8 *bssid = (u8 *)info->bssid;
+ bool assoc = info->assoc;
+
+ // ieee80211_bss_conf(include/net/mac80211.h) is included in ieee80211_sub_if_data(net/mac80211/ieee80211_i.h) , does bssid=ieee80211_if_ap's ssid ?
+
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ if (changed & BSS_CHANGED_BSSID ||
+ ((changed & BSS_CHANGED_ASSOC) && assoc)) {
+ evif->beacon_interval = info->aid;
+ memcpy(epub->wl.bssid, bssid, ETH_ALEN);
+ sip_send_bss_info_update(epub, evif, bssid, assoc);
+ } else if ((changed & BSS_CHANGED_ASSOC) && !assoc) {
+ evif->beacon_interval = 0;
+ memset(epub->wl.bssid, 0, ETH_ALEN);
+ sip_send_bss_info_update(epub, evif, bssid, assoc);
+ }
+ } else if (vif->type == NL80211_IFTYPE_AP) {
+ if (!(changed & BSS_CHANGED_BEACON_ENABLED) &&
+ !(changed & BSS_CHANGED_BEACON_INT))
+ return;
+
+ if (info->enable_beacon && !evif->ap_up) {
+ evif->beacon_interval = info->beacon_int;
+ init_beacon_timer(vif);
+ sip_send_bss_info_update(epub, evif, bssid, 2);
+ evif->ap_up = true;
+ } else if (!info->enable_beacon && evif->ap_up &&
+ !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) {
+ evif->beacon_interval = 0;
+ del_timer_sync(&evif->beacon_timer);
+ sip_send_bss_info_update(epub, evif, bssid, 2);
+ evif->ap_up = false;
+ }
+ }
+}
+
+/*
+ Configure the device's RX filter.
+ 3015 * See the section "Frame filtering" for more information.
+ 3016 * This callback must be implemented and can sleep.
+ */
+static void esp_op_configure_filter(struct ieee80211_hw *hw,
+ unsigned int changed_flags,
+ unsigned int *total_flags, u64 multicast)
+{
+ struct esp_pub *epub = (struct esp_pub *)hw->priv;
+
+ epub->rx_filter = 0;
+
+ if (*total_flags & FIF_ALLMULTI)
+ epub->rx_filter |= FIF_ALLMULTI;
+
+ *total_flags = epub->rx_filter;
+}
+
+static bool is_cipher_suite_wep(u32 cipher)
+{
+ return (cipher == WLAN_CIPHER_SUITE_WEP40) ||
+ (cipher == WLAN_CIPHER_SUITE_WEP104);
+}
+
+/*
+ See the section "Hardware crypto acceleration"
+ 3029 * This callback is only called between add_interface and
+ 3030 * remove_interface calls, i.e. while the given virtual interface
+ 3031 * is enabled.
+ 3032 * Returns a negative error code if the key can't be added.
+ 3033 * The callback can sleep.
+ */
+static int esp_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ struct esp_pub *epub = (struct esp_pub *)hw->priv;
+ struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
+ struct esp_hw_idx_map *map;
+ atomic_t *cnt1, *cnt2;
+ u8 i, ifidx = evif->index, isvalid, index;
+ u8 *peer_addr;
+ int ret, counter;
+
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+
+ if (sta && memcmp(sta->addr, epub->wl.bssid, ETH_ALEN))
+ peer_addr = sta->addr;
+ else
+ peer_addr = epub->wl.bssid;
+
+ isvalid = !!(cmd == SET_KEY);
+
+ if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE ||
+ is_cipher_suite_wep(key->cipher))
+ map = epub->low_map[ifidx];
+ else
+ map = epub->hi_map;
+
+ if (isvalid) {
+ if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE ||
+ is_cipher_suite_wep(key->cipher))
+ counter = 2;
+ else
+ counter = 19;
+
+ for (i = 0; i < counter; i++) {
+ if (map[i].flag)
+ continue;
+
+ map[i].flag = 1;
+ memcpy(map[i].mac, peer_addr, ETH_ALEN);
+ if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE ||
+ is_cipher_suite_wep(key->cipher))
+ key->hw_key_idx = i + 6;
+ else
+ key->hw_key_idx = i + ifidx * 2 + 2;
+ break;
+ }
+ } else {
+ map[index].flag = 0;
+ memset(map[index].mac, 0, ETH_ALEN);
+
+ if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE ||
+ is_cipher_suite_wep(key->cipher))
+ index = key->hw_key_idx - 6;
+ else
+ index = key->hw_key_idx - 2 - ifidx * 2;
+ }
+
+ if (key->hw_key_idx >= 6) {
+ cnt1 = &epub->wl.ptk_cnt;
+ cnt2 = &epub->wl.gtk_cnt;
+ } else {
+ cnt2 = &epub->wl.ptk_cnt;
+ cnt1 = &epub->wl.gtk_cnt;
+ }
+
+ /*send sub_scan task to target */
+ if (isvalid)
+ atomic_inc(cnt1);
+ else
+ atomic_dec(cnt1);
+
+ if (is_cipher_suite_wep(key->cipher)) {
+ if (isvalid)
+ atomic_inc(cnt2);
+ else
+ atomic_dec(cnt2);
+ }
+
+ ret = sip_send_setkey(epub, ifidx, peer_addr, key, isvalid);
+ if (ret)
+ return ret;
+
+ if (key->cipher == WLAN_CIPHER_SUITE_TKIP && !ret)
+ atomic_set(&epub->wl.tkip_key_set, 1);
+
+ return 0;
+}
+
+void hw_scan_done(struct esp_pub *epub, bool aborted)
+{
+ struct cfg80211_scan_info info = {
+ .aborted = aborted,
+ };
+
+ cancel_delayed_work_sync(&epub->scan_timeout_work);
+
+ ESSERT(epub->wl.scan_req);
+
+ ieee80211_scan_completed(epub->hw, &info);
+
+ if (test_and_clear_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags))
+ sip_trigger_txq_process(epub->sip);
+}
+
+static void hw_scan_timeout_report(struct work_struct *work)
+{
+ struct esp_pub *epub = container_of(work, struct esp_pub,
+ scan_timeout_work.work);
+ bool aborted;
+ struct cfg80211_scan_info info = {};
+
+ if (test_and_clear_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags))
+ sip_trigger_txq_process(epub->sip);
+ /*check if normally complete or aborted like timeout/hw error */
+ aborted = (epub->wl.scan_req != 0);
+
+ if (aborted)
+ epub->wl.scan_req = NULL;
+
+ info.aborted = aborted;
+
+ ieee80211_scan_completed(epub->hw, &info);
+}
+
+/*
+ Configuration of RTS threshold (if device needs it)
+ 3106 * The callback can sleep.
+ */
+static int esp_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+ return 0;
+}
+
+static int esp_node_attach(struct ieee80211_hw *hw, u8 ifidx,
+ struct ieee80211_sta *sta)
+{
+ struct esp_pub *epub = (struct esp_pub *)hw->priv;
+ struct esp_node *node;
+ struct esp_tx_tid *tid;
+ u8 tidno = 0;
+ int i;
+
+ spin_lock_bh(&epub->tx_ampdu_lock);
+
+ /* ffz(x) needs at least one zero or results in undefined behaviour. */
+ if ((~epub->enodes_map) == 0)
+ return -EINVAL;
+
+ i = ffz(epub->enodes_map);
+
+ if (hweight32(epub->enodes_maps[ifidx]) >= ESP_PUB_MAX_STA ||
+ i > ESP_PUB_MAX_STA) {
+ i = -1;
+ goto out;
+ }
+
+ epub->enodes_map |= BIT(i);
+ epub->enodes_maps[ifidx] |= BIT(i);
+ node = (struct esp_node *)sta->drv_priv;
+ epub->enodes[i] = node;
+ node->sta = sta;
+ node->ifidx = ifidx;
+ node->index = i;
+
+ while (tidno < WME_NUM_TID) {
+ tid = &node->tid[tidno];
+ tid->ssn = 0;
+ tid->cnt = 0;
+ tid->state = ESP_TID_STATE_INIT;
+ tidno++;
+ }
+
+out:
+ spin_unlock_bh(&epub->tx_ampdu_lock);
+
+ return i;
+}
+
+static int esp_node_detach(struct ieee80211_hw *hw, u8 ifidx,
+ struct ieee80211_sta *sta)
+{
+ struct esp_pub *epub = (struct esp_pub *)hw->priv;
+ u32 map;
+ int i;
+
+ spin_lock_bh(&epub->tx_ampdu_lock);
+
+ map = epub->enodes_maps[ifidx];
+
+ while (map) {
+ i = ffs(map) - 1;
+ if (epub->enodes[i]->sta == sta) {
+ epub->enodes[i]->sta = NULL;
+ epub->enodes[i] = NULL;
+ epub->enodes_map &= ~BIT(i);
+ epub->enodes_maps[ifidx] &= ~BIT(i);
+
+ goto out;
+ }
+
+ map &= ~BIT(i);
+ }
+
+ i = -1;
+
+out:
+ spin_unlock_bh(&epub->tx_ampdu_lock);
+
+ return i;
+}
+
+struct esp_node *esp_get_node_by_addr(struct esp_pub *epub, const u8 *addr)
+{
+ struct esp_node *node = NULL;
+ int i;
+ u32 map;
+
+ if (!addr)
+ return NULL;
+
+ spin_lock_bh(&epub->tx_ampdu_lock);
+ map = epub->enodes_map;
+
+ while (map) {
+ i = ffs(map) - 1;
+
+ if (!memcmp(epub->enodes[i]->sta->addr, addr, ETH_ALEN)) {
+ node = epub->enodes[i];
+ goto out;
+ }
+
+ map &= ~BIT(i);
+ }
+
+out:
+ spin_unlock_bh(&epub->tx_ampdu_lock);
+
+ return node;
+}
+
+int esp_get_empty_rxampdu(struct esp_pub *epub, const u8 *addr, u8 tid)
+{
+ int index;
+
+ if (!addr)
+ return -1;
+
+ spin_lock_bh(&epub->rx_ampdu_lock);
+
+ index = ffz(epub->rxampdu_map);
+
+ if (index >= ESP_PUB_MAX_RXAMPDU) {
+ index = -1;
+ goto out;
+ }
+
+ epub->rxampdu_map |= BIT(index);
+ epub->rxampdu_node[index] = esp_get_node_by_addr(epub, addr);
+ epub->rxampdu_tid[index] = tid;
+
+out:
+ spin_unlock_bh(&epub->rx_ampdu_lock);
+
+ return index;
+}
+
+int esp_get_exist_rxampdu(struct esp_pub *epub, const u8 *addr, u8 tid)
+{
+ u8 map;
+ int index;
+
+ if (!addr)
+ return -1;
+
+ spin_lock_bh(&epub->rx_ampdu_lock);
+ map = epub->rxampdu_map;
+
+ while (map) {
+ index = ffs(map) - 1;
+
+ if (epub->rxampdu_tid[index] == tid &&
+ !memcmp(epub->rxampdu_node[index]->sta->addr, addr,
+ ETH_ALEN)) {
+ epub->rxampdu_map &= ~BIT(index);
+ goto out;
+ }
+
+ map &= ~BIT(index);
+ }
+
+ index = -1;
+
+out:
+ spin_unlock_bh(&epub->rx_ampdu_lock);
+ return index;
+}
+
+/*
+ Notifies low level driver about addition of an associated station,
+ 3109 * AP, IBSS/WDS/mesh peer etc. This callback can sleep.
+ */
+static int esp_op_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct esp_pub *epub = (struct esp_pub *)hw->priv;
+ struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
+ int index;
+
+ index = esp_node_attach(hw, evif->index, sta);
+ if (index < 0)
+ return index;
+
+ sip_send_set_sta(epub, evif->index, 1, sta, vif, (u8)index);
+
+ return 0;
+}
+
+/*
+ Notifies low level driver about removal of an associated
+ 3112 * station, AP, IBSS/WDS/mesh peer etc. Note that after the callback
+ 3113 * returns it isn't safe to use the pointer, not even RCU protected;
+ 3114 * no RCU grace period is guaranteed between returning here and freeing
+ 3115 * the station. See @sta_pre_rcu_remove if needed.
+ 3116 * This callback can sleep
+ */
+static int esp_op_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct esp_pub *epub = (struct esp_pub *)hw->priv;
+ struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
+ int index;
+
+ //remove a connect in target
+ index = esp_node_detach(hw, evif->index, sta);
+ sip_send_set_sta(epub, evif->index, 0, sta, vif, (u8)index);
+
+ return 0;
+}
+
+/*
+ Notifies low level driver about power state transition of an
+ 3124 * associated station, AP, IBSS/WDS/mesh peer etc. For a VIF operating
+ 3125 * in AP mode, this callback will not be called when the flag
+ 3126 * %IEEE80211_HW_AP_LINK_PS is set. Must be atomic.
+ */
+static void esp_op_sta_notify(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum sta_notify_cmd cmd,
+ struct ieee80211_sta *sta)
+{
+}
+
+/*
+ Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
+ 3165 * bursting) for a hardware TX queue.
+ 3166 * Returns a negative error code on failure.
+ 3167 * The callback can sleep.
+ */
+static int esp_op_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ u16 queue,
+ const struct ieee80211_tx_queue_params *params)
+{
+ struct esp_pub *epub = (struct esp_pub *)hw->priv;
+
+ return sip_send_wmm_params(epub, queue, params);
+}
+
+/*
+ Get the current TSF timer value from firmware/hardware. Currently,
+ 3170 * this is only used for IBSS mode BSSID merging and debugging. Is not a
+ 3171 * required function.
+ 3172 * The callback can sleep.
+ */
+static u64 esp_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+ return 0;
+}
+
+/*
+ Set the TSF timer to the specified value in the firmware/hardware.
+ 3175 * Currently, this is only used for IBSS mode debugging. Is not a
+ 3176 * required function.
+ 3177 * The callback can sleep.
+ */
+static void esp_op_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ u64 tsf)
+{
+}
+
+/*
+ Reset the TSF timer and allow firmware/hardware to synchronize
+ 3186 * with other STAs in the IBSS. This is only used in IBSS mode. This
+ 3187 * function is optional if the firmware/hardware takes full care of
+ 3188 * TSF synchronization.
+ 3189 * The callback can sleep.
+ */
+static void esp_op_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+}
+
+/*
+ Poll rfkill hardware state. If you need this, you also
+ 3220 * need to set wiphy->rfkill_poll to %true before registration,
+ 3221 * and need to call wiphy_rfkill_set_hw_state() in the callback.
+ 3222 * The callback can sleep.
+ */
+static void esp_op_rfkill_poll(struct ieee80211_hw *hw)
+{
+ struct esp_pub *epub = (struct esp_pub *)hw->priv;
+
+ wiphy_rfkill_set_hw_state(hw->wiphy, test_bit(ESP_WL_FLAG_RFKILL,
+ &epub->wl.flags));
+}
+
+#ifdef HW_SCAN
+/*
+ Ask the hardware to service the scan request, no need to start
+ 3051 * the scan state machine in stack. The scan must honour the channel
+ 3052 * configuration done by the regulatory agent in the wiphy's
+ 3053 * registered bands. The hardware (or the driver) needs to make sure
+ 3054 * that power save is disabled.
+ 3055 * The @req ie/ie_len members are rewritten by mac80211 to contain the
+ 3056 * entire IEs after the SSID, so that drivers need not look at these
+ 3057 * at all but just send them after the SSID -- mac80211 includes the
+ 3058 * (extended) supported rates and HT information (where applicable).
+ 3059 * When the scan finishes, ieee80211_scan_completed() must be called;
+ 3060 * note that it also must be called when the scan cannot finish due to
+ 3061 * any error unless this callback returned a negative error code.
+ 3062 * The callback can sleep.
+ */
+static int esp_op_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct cfg80211_scan_request *req)
+{
+ struct esp_pub *epub = (struct esp_pub *)hw->priv;
+ struct cfg80211_ssid *ssid2 = req->ssids + 1;
+ int i, ret;
+ bool scan_often;
+
+ /* scan_request is keep allocate until scan_done,record it
+ * to split request into multi sdio_cmd
+ */
+ if (atomic_read(&epub->wl.off)) {
+ dev_err(epub->dev, "hw_scan but wl off\n");
+ return -EPERM;
+ }
+
+ if (req->n_ssids > 1)
+ if ((req->ssids->ssid_len > 0 && ssid2->ssid_len > 0) ||
+ req->n_ssids > 2) {
+ dev_err(epub->dev, "cannot scan two SSIDs\n");
+ return -EINVAL;
+ }
+
+ epub->wl.scan_req = req;
+
+ /*in connect state, suspend tx data */
+ if (epub->sip->support_bgscan &&
+ test_bit(ESP_WL_FLAG_CONNECT, &epub->wl.flags) && req->n_channels) {
+ scan_often = epub->scan_permit_valid &&
+ time_before(jiffies, epub->scan_permit);
+ epub->scan_permit_valid = true;
+
+ if (!scan_often) {
+ /* epub->scan_permit = jiffies + msecs_to_jiffies(900);
+ * set_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags);
+ * if (atomic_read(&epub->txq_stopped) == false) {
+ * atomic_set(&epub->txq_stopped, true);
+ * ieee80211_stop_queues(hw);
+ * }
+ */
+ } else {
+ dev_err(epub->dev, "scan too often\n");
+ return -EACCES;
+ }
+ } else {
+ scan_often = false;
+ }
+
+ /*send sub_scan task to target */
+ ret = sip_send_scan(epub);
+ if (ret) {
+ dev_err(epub->dev, "failed to send scan_cmd: %d\n", ret);
+ return ret;
+ }
+
+ if (scan_often)
+ return 0;
+
+ epub->scan_permit = jiffies + msecs_to_jiffies(900);
+ set_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags);
+ if (!atomic_read(&epub->txq_stopped)) {
+ atomic_set(&epub->txq_stopped, true);
+ ieee80211_stop_queues(hw);
+ }
+
+ /*force scan complete in case target fail to report in time */
+ ieee80211_queue_delayed_work(hw, &epub->scan_timeout_work,
+ req->n_channels * HZ / 4);
+
+ return 0;
+}
+
+/*
+ Starts an off-channel period on the given channel, must
+ 3255 * call back to ieee80211_ready_on_channel() when on that channel. Note
+ 3256 * that normal channel traffic is not stopped as this is intended for hw
+ 3257 * offload. Frames to transmit on the off-channel channel are transmitted
+ 3258 * normally except for the %IEEE80211_TX_CTL_TX_OFFCHAN flag. When the
+ 3259 * duration (which will always be non-zero) expires, the driver must call
+ 3260 * ieee80211_remain_on_channel_expired().
+ 3261 * Note that this callback may be called while the device is in IDLE and
+ 3262 * must be accepted in this case.
+ 3263 * This callback may sleep.
+ */
+static int esp_op_remain_on_channel(struct ieee80211_hw *hw,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type channel_type,
+ int duration)
+{
+ struct esp_pub *epub = (struct esp_pub *)hw->priv;
+
+ sip_send_roc(epub, chan->center_freq, duration);
+
+ return 0;
+}
+
+/*
+ 3264 * @cancel_remain_on_channel: Requests that an ongoing off-channel period is
+ 3265 * aborted before it expires. This callback may sleep.
+ */
+static int esp_op_cancel_remain_on_channel(struct ieee80211_hw *hw)
+{
+ struct esp_pub *epub = (struct esp_pub *)hw->priv;
+
+ epub->roc_flags = 0; // to disable roc state
+ sip_send_roc(epub, 0, 0);
+
+ return 0;
+}
+#endif
+
+void esp_rocdone_process(struct ieee80211_hw *hw, struct sip_evt_roc *report)
+{
+ struct esp_pub *epub = (struct esp_pub *)hw->priv;
+
+ if (report->is_ok != 1)
+ return;
+
+ if (report->state == 1) {
+ epub->roc_flags = 1; //flags in roc state, to fix channel, not change
+ ieee80211_ready_on_channel(hw);
+ } else if (!report->state) {
+ epub->roc_flags = 0;
+ ieee80211_remain_on_channel_expired(hw);
+ }
+}
+
+/*
+ Set a mask of rates to be used for rate control selection
+ 3275 * when transmitting a frame. Currently only legacy rates are handled.
+ 3276 * The callback can sleep.
+ */
+static int esp_op_set_bitrate_mask(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const struct cfg80211_bitrate_mask *mask)
+{
+ return 0;
+}
+
+/*
+ Flush all pending frames from the hardware queue, making sure
+ 3235 * that the hardware queues are empty. The @queues parameter is a bitmap
+ 3236 * of queues to flush, which is useful if different virtual interfaces
+ 3237 * use different hardware queues; it may also indicate all queues.
+ 3238 * If the parameter @drop is set to %true, pending frames may be dropped.
+ 3239 * Note that vif can be NULL.
+ 3240 * The callback can sleep.
+ */
+void esp_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ u32 queues, bool drop)
+{
+ struct esp_pub *epub = (struct esp_pub *)hw->priv;
+ unsigned long time = jiffies + msecs_to_jiffies(15);
+
+ while (atomic_read(&epub->sip->tx_data_pkt_queued)) {
+ if (!time_before(jiffies, time))
+ break;
+
+ if (!sif_get_ate_config())
+ ieee80211_queue_work(epub->hw, &epub->tx_work);
+ else
+ queue_work(epub->esp_wkq, &epub->tx_work);
+ }
+
+ mdelay(10);
+}
+
+/*
+ Perform a certain A-MPDU action
+ 3198 * The RA/TID combination determines the destination and TID we want
+ 3199 * the ampdu action to be performed for. The action is defined through
+ 3200 * ieee80211_ampdu_mlme_action.
+ 3201 * When the action is set to %IEEE80211_AMPDU_TX_OPERATIONAL the driver
+ 3202 * may neither send aggregates containing more subframes than @buf_size
+ 3203 * nor send aggregates in a way that lost frames would exceed the
+ 3204 * buffer size. If just limiting the aggregate size, this would be
+ 3205 * possible with a buf_size of 8:
+ 3206 * - TX: 1.....7
+ 3207 * - RX: 2....7 (lost frame #1)
+ 3208 * - TX: 8..1...
+ 3209 * which is invalid since #1 was now re-transmitted well past the
+ 3210 * buffer size of 8. Correct ways to retransmit #1 would be:
+ 3211 * - TX: 1 or 18 or 81
+ 3212 * Even "189" would be wrong since 1 could be lost again.
+ 3213 *
+ 3214 * Returns a negative error code on failure.
+ 3215 * The callback can sleep.
+ */
+static int esp_op_ampdu_action(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_ampdu_params *params)
+{
+ enum ieee80211_ampdu_mlme_action action = params->action;
+ struct ieee80211_sta *sta = params->sta;
+ struct esp_pub *epub = (struct esp_pub *)hw->priv;
+ struct esp_node *node = (struct esp_node *)sta->drv_priv;
+ struct cfg80211_chan_def *chandef;
+ u16 tid = params->tid;
+ struct esp_tx_tid *tid_info = &node->tid[tid];
+ u16 *ssn = ¶ms->ssn;
+ u8 buf_size = params->buf_size;
+
+ switch (action) {
+ case IEEE80211_AMPDU_TX_START:
+ chandef = &epub->hw->conf.chandef;
+ if (mod_support_no_txampdu() || !sta->ht_cap.ht_supported ||
+ cfg80211_get_chandef_type(chandef) == NL80211_CHAN_NO_HT)
+ return -EOPNOTSUPP;
+
+ dev_dbg(epub->dev, "%s TX START, addr:%pM,tid:%u,state:%d\n",
+ __func__, sta->addr, tid, tid_info->state);
+
+ spin_lock_bh(&epub->tx_ampdu_lock);
+
+ ESSERT(tid_info->state == ESP_TID_STATE_TRIGGER);
+ *ssn = tid_info->ssn;
+ tid_info->state = ESP_TID_STATE_PROGRESS;
+
+ ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+ spin_unlock_bh(&epub->tx_ampdu_lock);
+
+ return 0;
+
+ case IEEE80211_AMPDU_TX_STOP_CONT:
+ dev_dbg(epub->dev, "%s TX STOP, addr:%pM,tid:%u,state:%d\n",
+ __func__, sta->addr, tid, tid_info->state);
+
+ spin_lock_bh(&epub->tx_ampdu_lock);
+
+ case IEEE80211_AMPDU_TX_STOP_FLUSH:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+ if (tid_info->state == ESP_TID_STATE_WAIT_STOP)
+ tid_info->state = ESP_TID_STATE_STOP;
+ else
+ tid_info->state = ESP_TID_STATE_INIT;
+
+ if (action == IEEE80211_AMPDU_TX_STOP_CONT) {
+ ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+ spin_unlock_bh(&epub->tx_ampdu_lock);
+ }
+
+ return sip_send_ampdu_action(epub, SIP_AMPDU_TX_STOP, sta->addr,
+ tid, node->ifidx, 0);
+
+ case IEEE80211_AMPDU_TX_OPERATIONAL:
+ dev_dbg(epub->dev,
+ "%s TX OPERATION, addr:%pM,tid:%u,state:%d\n", __func__,
+ sta->addr, tid, tid_info->state);
+
+ spin_lock_bh(&epub->tx_ampdu_lock);
+
+ if (tid_info->state != ESP_TID_STATE_PROGRESS) {
+ if (tid_info->state == ESP_TID_STATE_INIT) {
+ printk(KERN_ERR "%s WIFI RESET, IGNORE\n",
+ __func__);
+ spin_unlock_bh(&epub->tx_ampdu_lock);
+ return -ENETRESET;
+ }
+
+ ESSERT(0);
+ }
+
+ tid_info->state = ESP_TID_STATE_OPERATIONAL;
+ spin_unlock_bh(&epub->tx_ampdu_lock);
+
+ return sip_send_ampdu_action(epub, SIP_AMPDU_TX_OPERATIONAL,
+ sta->addr, tid, node->ifidx,
+ buf_size);
+
+ case IEEE80211_AMPDU_RX_START:
+ chandef = &epub->hw->conf.chandef;
+ if (mod_support_no_rxampdu() || !sta->ht_cap.ht_supported ||
+ cfg80211_get_chandef_type(chandef) == NL80211_CHAN_NO_HT)
+ return -EOPNOTSUPP;
+
+ dev_dbg(epub->dev, "%s RX START %pM tid %u %u\n", __func__,
+ sta->addr, tid, *ssn);
+
+ return sip_send_ampdu_action(epub, SIP_AMPDU_RX_START, sta->addr,
+ tid, *ssn, 64);
+
+ case IEEE80211_AMPDU_RX_STOP:
+ dev_dbg(epub->dev, "%s RX STOP %pM tid %u\n", __func__,
+ sta->addr, tid);
+
+ return sip_send_ampdu_action(epub, SIP_AMPDU_RX_STOP, sta->addr,
+ tid, 0, 0);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static void esp_tx_work(struct work_struct *work)
+{
+ struct esp_pub *epub = container_of(work, struct esp_pub, tx_work);
+
+ mutex_lock(&epub->tx_mtx);
+ sip_txq_process(epub);
+ mutex_unlock(&epub->tx_mtx);
+}
+
+static const struct ieee80211_ops esp_mac80211_ops = {
+ .tx = esp_op_tx,
+ .start = esp_op_start,
+ .stop = esp_op_stop,
+ .add_interface = esp_op_add_interface,
+ .remove_interface = esp_op_remove_interface,
+ .config = esp_op_config,
+
+ .bss_info_changed = esp_op_bss_info_changed,
+ .configure_filter = esp_op_configure_filter,
+ .set_key = esp_op_set_key,
+ .set_rts_threshold = esp_op_set_rts_threshold,
+ .sta_notify = esp_op_sta_notify,
+ .conf_tx = esp_op_conf_tx,
+ .change_interface = esp_op_change_interface,
+ .get_tsf = esp_op_get_tsf,
+ .set_tsf = esp_op_set_tsf,
+ .reset_tsf = esp_op_reset_tsf,
+ .rfkill_poll = esp_op_rfkill_poll,
+#ifdef HW_SCAN
+ .hw_scan = esp_op_hw_scan,
+ .remain_on_channel = esp_op_remain_on_channel,
+ .cancel_remain_on_channel = esp_op_cancel_remain_on_channel,
+#endif
+ .ampdu_action = esp_op_ampdu_action,
+ .sta_add = esp_op_sta_add,
+ .sta_remove = esp_op_sta_remove,
+ .set_bitrate_mask = esp_op_set_bitrate_mask,
+ .flush = esp_op_flush,
+};
+
+struct esp_pub *esp_pub_alloc_mac80211(struct device *dev)
+{
+ struct ieee80211_hw *hw;
+ struct esp_pub *epub;
+
+ hw = ieee80211_alloc_hw(sizeof(struct esp_pub), &esp_mac80211_ops);
+ if (!hw)
+ return ERR_PTR(-ENOMEM);
+
+ /* FIXME: useless if hw_scan is defined, incorrect if hw_scan is undefined*/
+#ifdef HW_SCAN
+ hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+#endif
+
+ epub = hw->priv;
+ memset(epub, 0, sizeof(*epub));
+
+ epub->hw = hw;
+ SET_IEEE80211_DEV(hw, dev);
+ epub->dev = dev;
+
+ skb_queue_head_init(&epub->txq);
+ skb_queue_head_init(&epub->txdoneq);
+ skb_queue_head_init(&epub->rxq);
+
+ spin_lock_init(&epub->tx_ampdu_lock);
+ spin_lock_init(&epub->rx_ampdu_lock);
+ spin_lock_init(&epub->tx_lock);
+ mutex_init(&epub->tx_mtx);
+ spin_lock_init(&epub->rx_lock);
+
+ INIT_WORK(&epub->tx_work, esp_tx_work);
+
+ epub->esp_wkq = create_singlethread_workqueue("esp_wkq");
+
+ if (!epub->esp_wkq)
+ return ERR_PTR(-ENOMEM);
+
+ epub->scan_permit_valid = false;
+ INIT_DELAYED_WORK(&epub->scan_timeout_work, hw_scan_timeout_report);
+
+ return epub;
+}
+
+int esp_pub_dealloc_mac80211(struct esp_pub *epub)
+{
+ set_bit(ESP_WL_FLAG_RFKILL, &epub->wl.flags);
+
+ destroy_workqueue(epub->esp_wkq);
+ mutex_destroy(&epub->tx_mtx);
+
+ if (epub->hw)
+ ieee80211_free_hw(epub->hw);
+
+ return 0;
+}
+
+/* 2G band channels */
+static struct ieee80211_channel esp_channels_2ghz[] = {
+ {.hw_value = 1, .center_freq = 2412, .max_power = 25},
+ {.hw_value = 2, .center_freq = 2417, .max_power = 25},
+ {.hw_value = 3, .center_freq = 2422, .max_power = 25},
+ {.hw_value = 4, .center_freq = 2427, .max_power = 25},
+ {.hw_value = 5, .center_freq = 2432, .max_power = 25},
+ {.hw_value = 6, .center_freq = 2437, .max_power = 25},
+ {.hw_value = 7, .center_freq = 2442, .max_power = 25},
+ {.hw_value = 8, .center_freq = 2447, .max_power = 25},
+ {.hw_value = 9, .center_freq = 2452, .max_power = 25},
+ {.hw_value = 10, .center_freq = 2457, .max_power = 25},
+ {.hw_value = 11, .center_freq = 2462, .max_power = 25},
+ {.hw_value = 12, .center_freq = 2467, .max_power = 25},
+ {.hw_value = 13, .center_freq = 2472, .max_power = 25},
+};
+
+/* 11G rate */
+static struct ieee80211_rate esp_rates_2ghz[] = {
+ {
+ .bitrate = 10,
+ .hw_value = CONF_HW_BIT_RATE_1MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_1MBPS,
+ },
+ {
+ .bitrate = 20,
+ .hw_value = CONF_HW_BIT_RATE_2MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_2MBPS,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE},
+ {
+ .bitrate = 55,
+ .hw_value = CONF_HW_BIT_RATE_5_5MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE},
+ {
+ .bitrate = 110,
+ .hw_value = CONF_HW_BIT_RATE_11MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_11MBPS,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE},
+ {
+ .bitrate = 60,
+ .hw_value = CONF_HW_BIT_RATE_6MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_6MBPS,
+ },
+ {
+ .bitrate = 90,
+ .hw_value = CONF_HW_BIT_RATE_9MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_9MBPS,
+ },
+ {
+ .bitrate = 120,
+ .hw_value = CONF_HW_BIT_RATE_12MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_12MBPS,
+ },
+ {
+ .bitrate = 180,
+ .hw_value = CONF_HW_BIT_RATE_18MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_18MBPS,
+ },
+ {
+ .bitrate = 240,
+ .hw_value = CONF_HW_BIT_RATE_24MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_24MBPS,
+ },
+ {
+ .bitrate = 360,
+ .hw_value = CONF_HW_BIT_RATE_36MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_36MBPS,
+ },
+ {
+ .bitrate = 480,
+ .hw_value = CONF_HW_BIT_RATE_48MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_48MBPS,
+ },
+ {
+ .bitrate = 540,
+ .hw_value = CONF_HW_BIT_RATE_54MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_54MBPS,
+ },
+};
+
+static struct ieee80211_sta_ht_cap esp_ht_cap_2ghz = {
+ .cap = IEEE80211_HT_CAP_DSSSCCK40 | IEEE80211_HT_CAP_SM_PS |
+ IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20,
+ .ht_supported = true,
+ .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
+ .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
+ .mcs = {
+ .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+ },
+};
+
+static void esp_pub_init_mac80211(struct esp_pub *epub)
+{
+ struct ieee80211_hw *hw = epub->hw;
+ struct ieee80211_supported_band *sbands =
+ &epub->wl.sbands[NL80211_BAND_2GHZ];
+
+ static const u32 cipher_suites[] = {
+ WLAN_CIPHER_SUITE_WEP40,
+ WLAN_CIPHER_SUITE_WEP104,
+ WLAN_CIPHER_SUITE_TKIP,
+ WLAN_CIPHER_SUITE_CCMP,
+ };
+
+ hw->max_listen_interval = 10;
+
+ ieee80211_hw_set(hw, SIGNAL_DBM);
+ ieee80211_hw_set(hw, HAS_RATE_CONTROL);
+ ieee80211_hw_set(hw, SUPPORTS_PS);
+ ieee80211_hw_set(hw, AMPDU_AGGREGATION);
+ ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
+ hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF;
+ hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF;
+
+ hw->wiphy->cipher_suites = cipher_suites;
+ hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+ hw->wiphy->max_scan_ie_len = epub->sip->tx_blksz -
+ sizeof(struct sip_hdr) - sizeof(struct sip_cmd_scan);
+
+ /* ONLY station for now, support P2P soon... */
+ /* FIXME: is p2p really supported? */
+ hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_AP);
+
+ hw->wiphy->max_scan_ssids = 2;
+ hw->wiphy->max_remain_on_channel_duration = 5000;
+
+ atomic_set(&epub->wl.off, 1);
+
+ sbands->band = NL80211_BAND_2GHZ;
+ sbands->channels = esp_channels_2ghz;
+ sbands->bitrates = esp_rates_2ghz;
+ sbands->n_channels = ARRAY_SIZE(esp_channels_2ghz);
+ sbands->n_bitrates = ARRAY_SIZE(esp_rates_2ghz);
+ sbands->ht_cap = esp_ht_cap_2ghz;
+
+ hw->wiphy->bands[NL80211_BAND_2GHZ] = sbands;
+
+ /*no fragment */
+ hw->wiphy->frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
+
+ /* handle AC queue in f/w */
+ hw->queues = 4;
+ hw->max_rates = 4;
+
+ hw->vif_data_size = sizeof(struct esp_vif);
+ hw->sta_data_size = sizeof(struct esp_node);
+}
+
+int esp_register_mac80211(struct esp_pub *epub)
+{
+ int ret, idx;
+ u8 *p2p_addr;
+
+ esp_pub_init_mac80211(epub);
+
+ /* FIXME: esp_mac_addr */
+ epub->hw->wiphy->addresses = (struct mac_address *)esp_mac_addr;
+
+ memcpy(epub->hw->wiphy->addresses[0].addr, epub->mac_addr, ETH_ALEN);
+ memcpy(epub->hw->wiphy->addresses[1].addr, epub->mac_addr, ETH_ALEN);
+ p2p_addr = epub->hw->wiphy->addresses[1].addr;
+
+ for (idx = 0; idx < 8 * ETH_ALEN; idx++) {
+ p2p_addr[0] = epub->mac_addr[0] | 0x02;
+ p2p_addr[0] ^= idx << 2;
+ if (strncmp(p2p_addr, epub->mac_addr, 6))
+ break;
+ }
+
+ epub->hw->wiphy->n_addresses = 2;
+
+ ret = ieee80211_register_hw(epub->hw);
+ if (ret < 0) {
+ printk("unable to register mac80211 hw: %d\n", ret);
+ return ret;
+ }
+
+ set_bit(ESP_WL_FLAG_HW_REGISTERED, &epub->wl.flags);
+
+ return ret;
+}
+
+static u8 getaddr_index(u8 *addr, struct esp_pub *epub)
+{
+ int i;
+
+ for (i = 0; i < ESP_PUB_MAX_VIF; i++)
+ if (!memcmp(addr, epub->hw->wiphy->addresses[i].addr, ETH_ALEN))
+ return i;
+
+ return ESP_PUB_MAX_VIF;
+}
diff --git a/drivers/staging/esp8089/esp_mac80211.h b/drivers/staging/esp8089/esp_mac80211.h
new file mode 100644
index 000000000000..03de0b0a4223
--- /dev/null
+++ b/drivers/staging/esp8089/esp_mac80211.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2011 - 2014 Espressif System.
+ * Copyright (c) 2017 Free Electrons
+ * Quentin Schulz <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ */
+
+#ifndef _ESP_MAC80211_H_
+#define _ESP_MAC80211_H_
+
+struct esp_80211_wmm_ac_param {
+ u8 aci_aifsn; /* AIFSN, ACM, ACI */
+ u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */
+ u16 txop_limit;
+};
+
+struct esp_80211_wmm_param_element {
+ /* Element ID: 221 (0xdd); length: 24 */
+ /* required fields for WMM version 1 */
+ 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 specif QoS info */
+ u8 reserved; /* 0 */
+ struct esp_80211_wmm_ac_param ac[4]; /* AC_BE, AC_BK, AC_VI, AC_VO */
+};
+
+#endif /* _ESP_MAC80211_H_ */
diff --git a/drivers/staging/esp8089/esp_main.c b/drivers/staging/esp8089/esp_main.c
new file mode 100644
index 000000000000..c4621847926c
--- /dev/null
+++ b/drivers/staging/esp8089/esp_main.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2010 - 2014 Espressif System.
+ * Copyright (c) 2017 Free Electrons
+ * Quentin Schulz <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/firmware.h>
+#include <linux/sched.h>
+#include <net/cfg80211.h>
+#include <net/mac80211.h>
+#include <linux/time.h>
+#include <linux/moduleparam.h>
+#include <linux/module.h>
+
+#include "esp_pub.h"
+#include "esp_sip.h"
+#include "esp_sif.h"
+#include "esp_debug.h"
+#include "esp_file.h"
+#include "esp_wl.h"
+
+struct completion *gl_bootup_cplx;
+
+static int esp_download_fw(struct esp_pub *epub);
+
+static int modparam_no_txampdu;
+static int modparam_no_rxampdu;
+
+module_param_named(no_txampdu, modparam_no_txampdu, int, 0444);
+MODULE_PARM_DESC(no_txampdu, "Disable tx ampdu.");
+
+module_param_named(no_rxampdu, modparam_no_rxampdu, int, 0444);
+MODULE_PARM_DESC(no_rxampdu, "Disable rx ampdu.");
+
+static char *modparam_eagle_path = "/lib/firmware";
+
+module_param_named(eagle_path, modparam_eagle_path, charp, 0444);
+MODULE_PARM_DESC(eagle_path, "eagle path");
+
+bool mod_support_no_txampdu(void)
+{
+ return modparam_no_txampdu;
+}
+
+bool mod_support_no_rxampdu(void)
+{
+ return modparam_no_rxampdu;
+}
+
+int esp_pub_init_all(struct esp_pub *epub)
+{
+ int ret;
+
+ /* completion for bootup event poll */
+ DECLARE_COMPLETION_ONSTACK(complete);
+
+ atomic_set(&epub->ps.state, ESP_PM_OFF);
+
+ if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) {
+ epub->sip = sip_attach(epub);
+ if (!epub->sip) {
+ printk(KERN_ERR "%s sip alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+ } else {
+ atomic_set(&epub->sip->state, SIP_PREPARE_BOOT);
+ atomic_set(&epub->sip->tx_credits, 0);
+ }
+
+ epub->sip->to_host_seq = 0;
+
+ ret = esp_download_fw(epub);
+ if (ret) {
+ printk("download firmware failed\n");
+ return ret;
+ }
+
+ gl_bootup_cplx = &complete;
+ epub->wait_reset = 0;
+ sif_enable_irq(epub);
+
+ if (epub->sdio_state == ESP_SDIO_STATE_SECOND_INIT ||
+ sif_get_ate_config() == 1) {
+ ret = sip_poll_bootup_event(epub->sip);
+ } else {
+ ret = sip_poll_resetting_event(epub->sip);
+ if (!ret) {
+ sif_lock_bus(epub);
+ sif_interrupt_target(epub, 7);
+ sif_unlock_bus(epub);
+ }
+ }
+
+ gl_bootup_cplx = NULL;
+
+ if (sif_get_ate_config() == 1)
+ ret = -EOPNOTSUPP;
+
+ return ret;
+}
+
+void esp_dsr(struct esp_pub *epub)
+{
+ sip_rx(epub);
+}
+
+struct esp_fw_hdr {
+ u8 magic;
+ u8 blocks;
+ u8 pad[2];
+ u32 entry_addr;
+} __packed;
+
+struct esp_fw_blk_hdr {
+ u32 load_addr;
+ u32 data_len;
+} __packed;
+
+#define ESP_FW_NAME1 "eagle_fw_ate_config_v19.bin"
+#define ESP_FW_NAME2 "eagle_fw_first_init_v19.bin"
+#define ESP_FW_NAME3 "eagle_fw_second_init_v19.bin"
+
+static int esp_download_fw(struct esp_pub *epub)
+{
+ const struct firmware *fw_entry;
+ struct esp_fw_hdr *fhdr;
+ struct esp_fw_blk_hdr *bhdr;
+ struct sip_cmd_bootup bootcmd;
+ u8 *fw_buf;
+ u32 offset;
+ int ret;
+ u8 blocks;
+ char *esp_fw_name;
+
+ if (sif_get_ate_config() == 1)
+ esp_fw_name = ESP_FW_NAME3;
+ else
+ esp_fw_name = epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT ? ESP_FW_NAME1 : ESP_FW_NAME2;
+
+ ret = request_firmware(&fw_entry, esp_fw_name, epub->dev);
+ if (ret)
+ return ret;
+
+ fw_buf = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL);
+
+ release_firmware(fw_entry);
+
+ if (!fw_buf)
+ return -ENOMEM;
+
+ fhdr = (struct esp_fw_hdr *)fw_buf;
+ if (fhdr->magic != 0xE9) {
+ printk("%s wrong firmware magic!\n", __func__);
+ goto _err;
+ }
+
+ blocks = fhdr->blocks;
+ offset = sizeof(struct esp_fw_hdr);
+
+ while (blocks) {
+ bhdr = (struct esp_fw_blk_hdr *)&fw_buf[offset];
+ offset += sizeof(struct esp_fw_blk_hdr);
+
+ ret = sip_write_memory(epub->sip, bhdr->load_addr,
+ &fw_buf[offset], bhdr->data_len);
+ if (ret) {
+ printk("failed to write firmware: %d\n", ret);
+ goto _err;
+ }
+
+ blocks--;
+ offset += bhdr->data_len;
+ }
+
+ /* TODO: last byte should be the checksum and skip checksum for now */
+
+ bootcmd.boot_addr = fhdr->entry_addr;
+ ret = sip_send_cmd(epub->sip, SIP_CMD_BOOTUP,
+ sizeof(struct sip_cmd_bootup), &bootcmd);
+ if (ret)
+ goto _err;
+
+_err:
+ kfree(fw_buf);
+
+ return ret;
+}
+
+MODULE_FIRMWARE(ESP_FW_NAME1);
+MODULE_FIRMWARE(ESP_FW_NAME2);
+MODULE_FIRMWARE(ESP_FW_NAME3);
diff --git a/drivers/staging/esp8089/esp_pub.h b/drivers/staging/esp8089/esp_pub.h
new file mode 100644
index 000000000000..e329fb0999e9
--- /dev/null
+++ b/drivers/staging/esp8089/esp_pub.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2011 - 2014 Espressif System.
+ * Copyright (c) 2017 Free Electrons
+ * Quentin Schulz <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ */
+
+#ifndef _ESP_PUB_H_
+#define _ESP_PUB_H_
+
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/firmware.h>
+#include <linux/sched.h>
+#include <net/mac80211.h>
+#include <net/cfg80211.h>
+#include "sip2_common.h"
+
+enum esp_sdio_state {
+ ESP_SDIO_STATE_FIRST_INIT,
+ ESP_SDIO_STATE_FIRST_NORMAL_EXIT,
+ ESP_SDIO_STATE_FIRST_ERROR_EXIT,
+ ESP_SDIO_STATE_SECOND_INIT,
+ ESP_SDIO_STATE_SECOND_ERROR_EXIT,
+};
+
+enum esp_tid_state {
+ ESP_TID_STATE_INIT,
+ ESP_TID_STATE_TRIGGER,
+ ESP_TID_STATE_PROGRESS,
+ ESP_TID_STATE_OPERATIONAL,
+ ESP_TID_STATE_WAIT_STOP,
+ ESP_TID_STATE_STOP,
+};
+
+struct esp_tx_tid {
+ u8 state;
+ u8 cnt;
+ u16 ssn;
+};
+
+#define WME_NUM_TID 16
+struct esp_node {
+ struct esp_tx_tid tid[WME_NUM_TID];
+ struct ieee80211_sta *sta;
+ u8 ifidx;
+ u8 index;
+};
+
+#define WME_AC_BE 2
+#define WME_AC_BK 3
+#define WME_AC_VI 1
+#define WME_AC_VO 0
+
+struct esp_vif {
+ struct esp_pub *epub;
+ u8 index;
+ u32 beacon_interval;
+ bool ap_up;
+ struct timer_list beacon_timer;
+};
+
+/* WLAN related, mostly... */
+/*struct hw_scan_timeout {
+ * struct delayed_work w;
+ * struct ieee80211_hw *hw;
+ * };
+ */
+
+struct esp_wl {
+ u8 bssid[ETH_ALEN];
+ u8 req_bssid[ETH_ALEN];
+
+ //struct hw_scan_timeout *hsd;
+ struct cfg80211_scan_request *scan_req;
+ atomic_t ptk_cnt;
+ atomic_t gtk_cnt;
+ atomic_t tkip_key_set;
+
+ /* so far only 2G band */
+ struct ieee80211_supported_band sbands[NUM_NL80211_BANDS];
+
+ unsigned long flags;
+ atomic_t off;
+};
+
+struct esp_hw_idx_map {
+ u8 mac[ETH_ALEN];
+ u8 flag;
+};
+
+#define ESP_WL_FLAG_RFKILL BIT(0)
+#define ESP_WL_FLAG_HW_REGISTERED BIT(1)
+#define ESP_WL_FLAG_CONNECT BIT(2)
+#define ESP_WL_FLAG_STOP_TXQ BIT(3)
+
+#define ESP_PUB_MAX_VIF 2
+#define ESP_PUB_MAX_STA 4 //for one interface
+#define ESP_PUB_MAX_RXAMPDU 8 //for all interfaces
+
+enum {
+ ESP_PM_OFF = 0,
+ ESP_PM_TURNING_ON,
+ ESP_PM_ON,
+ ESP_PM_TURNING_OFF, /* Do NOT change the order */
+};
+
+struct esp_ps {
+ u32 dtim_period;
+ u32 max_sleep_period;
+ unsigned long last_config_time;
+ atomic_t state;
+ bool nulldata_pm_on;
+};
+
+struct esp_mac_prefix {
+ u8 mac_index;
+ u8 mac_addr_prefix[3];
+};
+
+struct esp_pub {
+ struct device *dev;
+ struct ieee80211_hw *hw;
+ struct ieee80211_vif *vif;
+ u8 vif_slot;
+
+ struct esp_sdio_ctrl *sif; /* serial interface control block, e.g. sdio */
+ enum esp_sdio_state sdio_state;
+ struct esp_sip *sip;
+ struct esp_wl wl;
+ struct esp_hw_idx_map hi_map[19];
+ struct esp_hw_idx_map low_map[ESP_PUB_MAX_VIF][2];
+ //u32 flags; //flags to represent rfkill switch,start
+ u8 roc_flags; //0: not in remain on channel state, 1: in roc state
+
+ struct work_struct tx_work; /* attach to ieee80211 workqueue */
+ /* latest mac80211 has multiple tx queue, but we stick with single queue now */
+ spinlock_t rx_lock;
+ spinlock_t tx_ampdu_lock;
+ spinlock_t rx_ampdu_lock;
+ spinlock_t tx_lock;
+ struct mutex tx_mtx;
+ struct sk_buff_head txq;
+ atomic_t txq_stopped;
+
+ struct work_struct sendup_work; /* attach to ieee80211 workqueue */
+ struct sk_buff_head txdoneq;
+ struct sk_buff_head rxq;
+
+ struct workqueue_struct *esp_wkq;
+
+ //u8 bssid[ETH_ALEN];
+ u8 mac_addr[ETH_ALEN];
+
+ u32 rx_filter;
+ unsigned long scan_permit;
+ bool scan_permit_valid;
+ struct delayed_work scan_timeout_work;
+ u32 enodes_map;
+ u8 rxampdu_map;
+ u32 enodes_maps[ESP_PUB_MAX_VIF];
+ struct esp_node *enodes[ESP_PUB_MAX_STA + 1];
+ struct esp_node *rxampdu_node[ESP_PUB_MAX_RXAMPDU];
+ u8 rxampdu_tid[ESP_PUB_MAX_RXAMPDU];
+ struct esp_ps ps;
+ int enable_int;
+ int wait_reset;
+};
+
+struct esp_pub *esp_pub_alloc_mac80211(struct device *dev);
+int esp_pub_dealloc_mac80211(struct esp_pub *epub);
+int esp_register_mac80211(struct esp_pub *epub);
+
+int esp_pub_init_all(struct esp_pub *epub);
+
+void esp_dsr(struct esp_pub *epub);
+void hw_scan_done(struct esp_pub *epub, bool aborted);
+void esp_rocdone_process(struct ieee80211_hw *hw, struct sip_evt_roc *report);
+
+struct esp_node *esp_get_node_by_addr(struct esp_pub *epub, const u8 *addr);
+int esp_get_empty_rxampdu(struct esp_pub *epub, const u8 *addr, u8 tid);
+int esp_get_exist_rxampdu(struct esp_pub *epub, const u8 *addr, u8 tid);
+
+#endif /* _ESP_PUB_H_ */
diff --git a/drivers/staging/esp8089/esp_sif.h b/drivers/staging/esp8089/esp_sif.h
new file mode 100644
index 000000000000..293640b11b0c
--- /dev/null
+++ b/drivers/staging/esp8089/esp_sif.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2011 - 2014 Espressif System.
+ * Copyright (c) 2017 Free Electrons
+ * Quentin Schulz <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ */
+
+#ifndef _ESP_SIF_H_
+#define _ESP_SIF_H_
+
+#include "esp_pub.h"
+#include <linux/mmc/host.h>
+#include <linux/spi/spi.h>
+
+/* H/W SLC module definitions */
+
+#define SIF_SLC_BLOCK_SIZE 512
+
+/* S/W struct mapping to slc registers */
+struct slc_host_regs {
+ /* do NOT read token_rdata
+ *
+ * u32 pf_data;
+ * u32 token_rdata;
+ */
+ u32 intr_raw;
+ u32 state_w0;
+ u32 state_w1;
+ u32 config_w0;
+ u32 config_w1;
+ u32 intr_status;
+ u32 config_w2;
+ u32 config_w3;
+ u32 config_w4;
+ u32 token_wdata;
+ u32 intr_clear;
+ u32 intr_enable;
+};
+
+enum io_sync_type {
+ ESP_SIF_NOSYNC = 0,
+ ESP_SIF_SYNC,
+};
+
+struct esp_sdio_ctrl {
+ struct sdio_func *func;
+ struct esp_pub *epub;
+
+ struct list_head free_req;
+
+ u8 *dma_buffer;
+
+ spinlock_t scat_lock;
+ struct list_head scat_req;
+
+ bool off;
+ atomic_t irq_handling;
+ const struct sdio_device_id *id;
+ u32 slc_blk_sz;
+ u32 target_id;
+ u32 slc_window_end_addr;
+
+ struct slc_host_regs slc_regs;
+ atomic_t irq_installed;
+
+};
+
+#define SIF_TO_DEVICE 0x1
+#define SIF_FROM_DEVICE 0x2
+
+#define SIF_SYNC 0x00000010
+#define SIF_ASYNC 0x00000020
+
+#define SIF_BYTE_BASIS 0x00000040
+#define SIF_BLOCK_BASIS 0x00000080
+
+#define SIF_FIXED_ADDR 0x00000100
+#define SIF_INC_ADDR 0x00000200
+
+void sdio_io_writeb(struct esp_pub *epub, u8 value, int addr, int *res);
+u8 sdio_io_readb(struct esp_pub *epub, int addr, int *res);
+
+void sif_enable_irq(struct esp_pub *epub);
+void sif_disable_irq(struct esp_pub *epub);
+void sif_disable_target_interrupt(struct esp_pub *epub);
+
+u32 sif_get_blksz(struct esp_pub *epub);
+
+void sif_dsr(struct sdio_func *func);
+int sif_io_raw(struct esp_pub *epub, u32 addr, u8 *buf, u32 len, u32 flag);
+int sif_io_sync(struct esp_pub *epub, u32 addr, u8 *buf, u32 len, u32 flag);
+int sif_lldesc_read_sync(struct esp_pub *epub, u8 *buf, u32 len);
+int sif_lldesc_write_sync(struct esp_pub *epub, u8 *buf, u32 len);
+int sif_lldesc_read_raw(struct esp_pub *epub, u8 *buf, u32 len, bool noround);
+int sif_lldesc_write_raw(struct esp_pub *epub, u8 *buf, u32 len);
+
+int esp_common_read(struct esp_pub *epub, u8 *buf, u32 len, int sync,
+ bool noround);
+int esp_common_write(struct esp_pub *epub, u8 *buf, u32 len, int sync);
+int esp_common_read_with_addr(struct esp_pub *epub, u32 addr, u8 *buf, u32 len,
+ int sync);
+int esp_common_write_with_addr(struct esp_pub *epub, u32 addr, u8 *buf,
+ u32 len, int sync);
+
+int esp_common_readbyte_with_addr(struct esp_pub *epub, u32 addr, u8 *buf,
+ int sync);
+int esp_common_writebyte_with_addr(struct esp_pub *epub, u32 addr, u8 buf,
+ int sync);
+
+struct slc_host_regs *sif_get_regs(struct esp_pub *epub);
+
+void sif_lock_bus(struct esp_pub *epub);
+void sif_unlock_bus(struct esp_pub *epub);
+
+int sif_interrupt_target(struct esp_pub *epub, u8 index);
+
+void check_target_id(struct esp_pub *epub);
+
+void sif_record_bt_config(int value);
+int sif_get_bt_config(void);
+void sif_record_rst_config(int value);
+int sif_get_rst_config(void);
+void sif_record_ate_config(int value);
+int sif_get_ate_config(void);
+void sif_record_wakeup_gpio_config(int value);
+int sif_get_wakeup_gpio_config(void);
+#endif /* _ESP_SIF_H_ */
diff --git a/drivers/staging/esp8089/esp_sip.c b/drivers/staging/esp8089/esp_sip.c
new file mode 100644
index 000000000000..91fb64c9b794
--- /dev/null
+++ b/drivers/staging/esp8089/esp_sip.c
@@ -0,0 +1,1718 @@
+/*
+ * Copyright (c) 2009 - 2014 Espressif System.
+ * Copyright (c) 2017 Free Electrons
+ * Quentin Schulz <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ */
+
+#include <linux/ieee80211.h>
+#include <net/mac80211.h>
+#include <net/cfg80211.h>
+#include <linux/skbuff.h>
+#include <linux/bitops.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sd.h>
+#include <linux/completion.h>
+
+#include "esp_mac80211.h"
+#include "esp_pub.h"
+#include "esp_sip.h"
+#include "esp_ctrl.h"
+#include "esp_sif.h"
+#include "esp_debug.h"
+#include "slc_host_register.h"
+#include "esp_wmac.h"
+#include "esp_utils.h"
+
+extern struct completion *gl_bootup_cplx;
+
+static int old_signal = -35;
+static int avg_signal;
+static int signal_loop;
+
+struct esp_mac_prefix esp_mac_prefix_table[] = {
+ {0, {0x18, 0xfe, 0x34} },
+ {1, {0xac, 0xd0, 0x74} },
+ {255, {0x18, 0xfe, 0x34} },
+};
+
+#define SIGNAL_COUNT 300
+
+/* FIXME: Incomplete ternary condition */
+#define TID_TO_AC(_tid) (!(_tid) || (_tid) == 3 ? WME_AC_BE : ((_tid) < 3 ? WME_AC_BK : ((_tid) < 6 ? WME_AC_VI : WME_AC_VO)))
+
+#define esp_sip_dbg esp_dbg
+struct sip_trace {
+ u32 tx_data;
+ u32 tx_cmd;
+ u32 rx_data;
+ u32 rx_evt;
+ u32 rx_tx_status;
+ u32 tx_out_of_credit;
+ u32 tx_one_shot_overflow;
+};
+
+static struct sip_trace str;
+
+#define STRACE_TX_DATA_INC() (str.tx_data++)
+#define STRACE_TX_CMD_INC() (str.tx_cmd++)
+#define STRACE_RX_DATA_INC() (str.rx_data++)
+#define STRACE_RX_EVENT_INC() (str.rx_evt++)
+#define STRACE_RX_TXSTATUS_INC() (str.rx_tx_status++)
+#define STRACE_TX_OUT_OF_CREDIT_INC() (str.tx_out_of_credit++)
+#define STRACE_TX_ONE_SHOT_INC() (str.tx_one_shot_overflow++)
+
+#define SIP_STOP_QUEUE_THRESHOLD 48
+#define SIP_RESUME_QUEUE_THRESHOLD 12
+
+#define SIP_MIN_DATA_PKT_LEN (sizeof(struct esp_mac_rx_ctrl) + 24) //24 is min 80211hdr
+
+static void sip_recalc_credit_init(struct esp_sip *sip);
+
+static int sip_recalc_credit_claim(struct esp_sip *sip, int force);
+
+static void sip_recalc_credit_release(struct esp_sip *sip);
+
+static struct sip_pkt *sip_get_ctrl_buf(struct esp_sip *sip,
+ enum sip_buf_type bftype);
+
+static void sip_reclaim_ctrl_buf(struct esp_sip *sip, struct sip_pkt *pkt,
+ enum sip_buf_type bftype);
+
+static void sip_free_init_ctrl_buf(struct esp_sip *sip);
+
+static int sip_pack_pkt(struct esp_sip *sip, struct sk_buff *skb,
+ int *pm_state);
+
+static struct esp_mac_rx_ctrl *sip_parse_normal_mac_ctrl(struct sk_buff *skb,
+ int *pkt_len_enc,
+ int *buf_len,
+ int *pulled_len);
+
+static struct sk_buff *sip_parse_data_rx_info(struct esp_sip *sip,
+ struct sk_buff *skb,
+ int pkt_len_enc, int buf_len,
+ struct esp_mac_rx_ctrl *mac_ctrl,
+ int *pulled_len);
+
+static inline void sip_rx_pkt_enqueue(struct esp_sip *sip, struct sk_buff *skb);
+
+static void sip_update_tx_credits(struct esp_sip *sip, u16 recycled_credits);
+
+static bool sip_rx_pkt_process(struct esp_sip *sip, struct sk_buff *skb);
+
+static void sip_tx_status_report(struct esp_sip *sip, struct sk_buff *skb,
+ struct ieee80211_tx_info *tx_info,
+ bool success);
+
+static bool check_ac_tid(u8 *pkt, u8 ac, u8 tid)
+{
+ struct ieee80211_hdr *wh = (struct ieee80211_hdr *)pkt;
+
+ if (!ieee80211_is_data_qos(wh->frame_control) &&
+ !ieee80211_is_mgmt(wh->frame_control) &&
+ !ieee80211_is_ctl(wh->frame_control)) {
+ if (tid || ac != WME_AC_BE) {
+ pr_info("444 ac:%u, tid:%u\n", ac, tid);
+
+ if (tid == 7 && ac == WME_AC_VO)
+ return false;
+ }
+
+ return true; //hack to modify non-qos null data.
+ }
+
+ return false;
+}
+
+static void sip_recalc_credit_timeout(unsigned long data)
+{
+ struct esp_sip *sip = (struct esp_sip *)data;
+
+ sip_recalc_credit_claim(sip, 1); /* recalc again */
+}
+
+static void sip_recalc_credit_init(struct esp_sip *sip)
+{
+ atomic_set(&sip->credit_status, RECALC_CREDIT_DISABLE); //set it disable
+
+ setup_timer(&sip->credit_timer, sip_recalc_credit_timeout,
+ (unsigned long)sip);
+}
+
+static int sip_recalc_credit_claim(struct esp_sip *sip, int force)
+{
+ int ret;
+
+ if (atomic_read(&sip->credit_status) == RECALC_CREDIT_ENABLE && !force)
+ return 1;
+
+ atomic_set(&sip->credit_status, RECALC_CREDIT_ENABLE);
+ ret = sip_send_recalc_credit(sip->epub);
+ if (ret) {
+ dev_err(sip->epub->dev, "sending recalc credit failed: %d",
+ ret);
+ return ret;
+ }
+
+ /*setup a timer for handle the abs_credit not receive */
+ mod_timer(&sip->credit_timer, jiffies + msecs_to_jiffies(2000));
+
+ return ret;
+}
+
+static void sip_recalc_credit_release(struct esp_sip *sip)
+{
+ if (atomic_read(&sip->credit_status) == RECALC_CREDIT_ENABLE) {
+ atomic_set(&sip->credit_status, RECALC_CREDIT_DISABLE);
+ del_timer_sync(&sip->credit_timer);
+ } else {
+ dev_dbg(sip->epub->dev, "maybe bogus credit");
+ }
+}
+
+static void sip_update_tx_credits(struct esp_sip *sip, u16 recycled_credits)
+{
+ if (recycled_credits & 0x800) {
+ atomic_set(&sip->tx_credits, (recycled_credits & 0x7ff));
+ sip_recalc_credit_release(sip);
+ } else {
+ atomic_add(recycled_credits, &sip->tx_credits);
+ }
+}
+
+void sip_trigger_txq_process(struct esp_sip *sip)
+{
+ if (atomic_read(&sip->tx_credits) <= sip->credit_to_reserve + SIP_CTRL_CREDIT_RESERVE ||
+ atomic_read(&sip->credit_status) == RECALC_CREDIT_ENABLE)
+ return;
+
+ if (sip_queue_may_resume(sip)) {
+ /* wakeup upper queue only if we have sufficient credits */
+ atomic_set(&sip->epub->txq_stopped, false);
+ ieee80211_wake_queues(sip->epub->hw);
+ } else if (atomic_read(&sip->epub->txq_stopped)) {
+ dev_err(sip->epub->dev, "%s can't wake txq, credits: %d\n",
+ __func__, atomic_read(&sip->tx_credits));
+ }
+
+ if (!skb_queue_empty(&sip->epub->txq)) {
+ /* try to send out pkt already in sip queue once we have credits */
+#if !defined(FPGA_TXDATA)
+ if (!sif_get_ate_config())
+ ieee80211_queue_work(sip->epub->hw,
+ &sip->epub->tx_work);
+ else
+ queue_work(sip->epub->esp_wkq, &sip->epub->tx_work);
+#else
+ queue_work(sip->epub->esp_wkq, &sip->epub->tx_work);
+#endif
+ }
+}
+
+static bool sip_ampdu_occupy_buf(struct esp_sip *sip,
+ struct esp_rx_ampdu_len *ampdu_len)
+{
+ return (!ampdu_len->substate ||
+ esp_wmac_rxsec_error(ampdu_len->substate) ||
+ (sip->dump_rpbm_err && ampdu_len->substate == RX_RPBM_ERR));
+}
+
+/* TODO: HARDCORE CLEANUP */
+
+static bool sip_rx_pkt_process(struct esp_sip *sip, struct sk_buff *skb)
+{
+ struct sip_hdr *hdr;
+ struct sk_buff *rskb;
+ struct esp_mac_rx_ctrl *mac_ctrl, new_mac_ctrl;
+ int remains_len, first_pkt_len, ret = 0, pkt_len_enc = 0, buf_len = 0,
+ pulled_len = 0, pkt_num;
+ u8 *bufptr;
+ bool trigger_rxq = false, have_rxabort = false, have_goodpkt = false;
+ struct esp_rx_ampdu_len *ampdu_len;
+ static int pkt_dropped, pkt_total;
+ static u8 frame_head[16];
+ static u8 frame_buf_ttl;
+
+ if (!skb)
+ return trigger_rxq;
+
+ hdr = (struct sip_hdr *)skb->data;
+ bufptr = skb->data;
+
+ if (hdr->h_credits & SIP_CREDITS_MASK)
+ sip_update_tx_credits(sip, hdr->h_credits & SIP_CREDITS_MASK);
+
+ hdr->h_credits &= ~SIP_CREDITS_MASK; /* clean credits in sip_hdr, prevent over-add */
+
+ /* first pkt's length is stored in recycled_credits first 20 bits
+ * config w3 [31:12]
+ * repair hdr->len of first pkt
+ */
+ remains_len = hdr->len;
+ first_pkt_len = hdr->h_credits >> 12;
+ hdr->len = first_pkt_len;
+
+ if (first_pkt_len > remains_len) {
+ sip_recalc_credit_claim(sip, 0);
+ show_buf((u8 *)hdr, first_pkt_len);
+ ESSERT(0);
+ goto _exit;
+ }
+
+ /* pkts handling, including the first pkt, should alloc new skb for each data pkt.
+ * free the original whole skb after parsing is done.
+ */
+ while (remains_len) {
+ if (remains_len < sizeof(struct sip_hdr)) {
+ sip_recalc_credit_claim(sip, 0);
+ ESSERT(0);
+ show_buf((u8 *)hdr, 512);
+ goto _exit;
+ }
+
+ hdr = (struct sip_hdr *)bufptr;
+ if (hdr->len <= 0 || hdr->len & 3) {
+ sip_recalc_credit_claim(sip, 0);
+ show_buf((u8 *)hdr, 512);
+ ESSERT(0);
+ goto _exit;
+ }
+
+ if (unlikely(hdr->seq != sip->rxseq++)) {
+ sip_recalc_credit_claim(sip, 0);
+ dev_dbg(sip->epub->dev,
+ "%s seq mismatch! got %u, expect %u\n",
+ __func__, hdr->seq, sip->rxseq - 1);
+ sip->rxseq = hdr->seq + 1;
+ show_buf(bufptr, 32);
+ ESSERT(0);
+ }
+
+ if (SIP_HDR_IS_CTRL(hdr)) {
+ STRACE_RX_EVENT_INC();
+
+ ret = sip_parse_events(sip, bufptr);
+ skb_pull(skb, hdr->len);
+ } else if (SIP_HDR_IS_DATA(hdr)) {
+ STRACE_RX_DATA_INC();
+ mac_ctrl = sip_parse_normal_mac_ctrl(skb, &pkt_len_enc,
+ &buf_len,
+ &pulled_len);
+ rskb = sip_parse_data_rx_info(sip, skb, pkt_len_enc,
+ buf_len, mac_ctrl,
+ &pulled_len);
+
+ if (!rskb)
+ goto _move_on;
+
+ if (likely(atomic_read(&sip->epub->wl.off) == 0)) {
+ local_bh_disable();
+ ieee80211_rx(sip->epub->hw, rskb);
+ local_bh_enable();
+ } else {
+ /* still need go thro parsing as skb_pull should invoke */
+ kfree_skb(rskb);
+ }
+ } else if (SIP_HDR_IS_AMPDU(hdr)) {
+ ampdu_len = (struct esp_rx_ampdu_len *)(skb->data + hdr->len / sip->rx_blksz * sip->rx_blksz);
+ dev_dbg(sip->epub->dev, "%s rx ampdu total len %u\n",
+ __func__, hdr->len);
+ if (skb->data != (u8 *)hdr) {
+ printk("%p %p\n", skb->data, hdr);
+ show_buf(skb->data, 512);
+ show_buf((u8 *)hdr, 512);
+ ESSERT(0);
+ goto _exit;
+ }
+ mac_ctrl = sip_parse_normal_mac_ctrl(skb, NULL, NULL,
+ &pulled_len);
+ memcpy(&new_mac_ctrl, mac_ctrl,
+ sizeof(struct esp_mac_rx_ctrl));
+ mac_ctrl = &new_mac_ctrl;
+ pkt_num = mac_ctrl->ampdu_cnt;
+ dev_dbg(sip->epub->dev,
+ "%s %d rx ampdu %u pkts, %d pkts dumped, first len %u\n",
+ __func__, __LINE__,
+ (unsigned int)((hdr->len % sip->rx_blksz) /
+ sizeof(struct esp_rx_ampdu_len)),
+ pkt_num, (unsigned int)ampdu_len->sublen);
+
+ pkt_total += mac_ctrl->ampdu_cnt;
+ while (pkt_num > 0) {
+ dev_dbg(sip->epub->dev,
+ "%s %d ampdu sub state %02x,\n",
+ __func__, __LINE__,
+ ampdu_len->substate);
+
+ if (sip_ampdu_occupy_buf(sip, ampdu_len)) { //pkt is dumped
+ rskb = sip_parse_data_rx_info(sip, skb,
+ ampdu_len->sublen - FCS_LEN,
+ 0,
+ mac_ctrl,
+ &pulled_len);
+ if (!rskb) {
+ ESSERT(0);
+ goto _exit;
+ }
+
+ if (likely(atomic_read(&sip->epub->wl.off) == 0) &&
+ (ampdu_len->substate == 0 ||
+ ampdu_len->substate == RX_TKIPMIC_ERR ||
+ (sip->sendup_rpbm_pkt &&
+ ampdu_len->substate == RX_RPBM_ERR)) &&
+ (sip->rxabort_fixed ||
+ !have_rxabort)) {
+ if (!have_goodpkt) {
+ have_goodpkt = true;
+ memcpy(frame_head,
+ rskb->data,
+ 16);
+ frame_head[1] &= ~0x80;
+ frame_buf_ttl = 3;
+ }
+ local_bh_disable();
+ ieee80211_rx(sip->epub->hw,
+ rskb);
+ local_bh_enable();
+ } else {
+ kfree_skb(rskb);
+ }
+ } else {
+ if (ampdu_len->substate == RX_ABORT) {
+ u8 *a;
+ have_rxabort = true;
+ dev_dbg(sip->epub->dev,
+ "rx abort %d %d\n",
+ frame_buf_ttl, pkt_num);
+ if (frame_buf_ttl &&
+ !sip->rxabort_fixed) {
+ struct esp_rx_ampdu_len *next_good_ampdu_len = ampdu_len + 1;
+ a = frame_head;
+ while (!sip_ampdu_occupy_buf(sip, next_good_ampdu_len)) {
+ if (next_good_ampdu_len > ampdu_len + pkt_num - 1)
+ break;
+ next_good_ampdu_len++;
+ }
+ if (next_good_ampdu_len <= ampdu_len + pkt_num - 1) {
+ bool b0, b10, b11;
+ a = skb->data;
+ b0 = memcmp(frame_head + 4, skb->data + 4, 12) == 0;
+ b10 = memcmp(frame_head + 10, skb->data, 6) == 0;
+ b11 = memcpy(frame_head + 11, skb->data, 5) == 0;
+ if (b0 && !b10 &&
+ !b11) {
+ have_rxabort = false;
+ } else if (!b0 &&
+ b10 &&
+ !b11) {
+ skb_push(skb, 10);
+ memcpy(skb->data,
+ frame_head,
+ 10);
+ have_rxabort = false;
+ pulled_len -= 10;
+ } else if (!b0 && !b10 && b11) {
+ skb_push(skb, 11);
+ memcpy(skb->data,
+ frame_head,
+ 11);
+ have_rxabort = false;
+ pulled_len -= 11;
+ }
+ }
+ }
+ }
+ pkt_dropped++;
+ dev_dbg(sip->epub->dev,
+ "%s ampdu dropped %d/%d\n",
+ __func__, pkt_dropped,
+ pkt_total);
+ }
+ pkt_num--;
+ ampdu_len++;
+ }
+ if (frame_buf_ttl)
+ frame_buf_ttl--;
+ skb_pull(skb, hdr->len - pulled_len);
+ } else {
+ dev_err(sip->epub->dev, "unknown SIP HDR type\n");
+ }
+
+_move_on:
+ if (hdr->len < remains_len)
+ remains_len -= hdr->len;
+ else
+ break;
+ bufptr += hdr->len;
+ }
+
+_exit:
+ kfree_skb(skb);
+
+ return trigger_rxq;
+}
+
+static void _sip_rxq_process(struct esp_sip *sip)
+{
+ struct sk_buff *skb;
+ bool sendup = false;
+
+ while ((skb = skb_dequeue(&sip->rxq)))
+ if (sip_rx_pkt_process(sip, skb))
+ sendup = true;
+
+ if (sendup)
+ queue_work(sip->epub->esp_wkq, &sip->epub->sendup_work);
+
+ /* probably tx_credit is updated, try txq */
+ sip_trigger_txq_process(sip);
+}
+
+void sip_rxq_process(struct work_struct *work)
+{
+ struct esp_sip *sip = container_of(work, struct esp_sip,
+ rx_process_work);
+
+ if (!sip) {
+ ESSERT(0);
+ return;
+ }
+
+ if (unlikely(atomic_read(&sip->state) == SIP_SEND_INIT)) {
+ sip_send_chip_init(sip);
+ atomic_set(&sip->state, SIP_WAIT_BOOTUP);
+ return;
+ }
+
+ mutex_lock(&sip->rx_mtx);
+ _sip_rxq_process(sip);
+ mutex_unlock(&sip->rx_mtx);
+}
+
+static inline void sip_rx_pkt_enqueue(struct esp_sip *sip, struct sk_buff *skb)
+{
+ skb_queue_tail(&sip->rxq, skb);
+}
+
+static u32 sip_rx_count;
+
+int sip_rx(struct esp_pub *epub)
+{
+ struct sip_hdr *shdr;
+ struct esp_sip *sip = epub->sip;
+ struct sk_buff *first_skb, *rx_skb;
+ u8 *rx_buf;
+ u32 rx_blksz, first_sz;
+ int err;
+ u8 raw_seq;
+
+ if (likely(sif_get_ate_config() != 1)) {
+ raw_seq = sif_get_regs(epub)->intr_raw & 0xff;
+
+ if (raw_seq != sip->to_host_seq) {
+ if (raw_seq == sip->to_host_seq + 1) { /* when last read pkt crc err, this situation may occur, but raw_seq mustn't < to_host_Seq */
+ sip->to_host_seq = raw_seq;
+ dev_dbg(epub->dev,
+ "warn: to_host_seq reg 0x%02x, seq 0x%02x",
+ raw_seq, sip->to_host_seq);
+ } else {
+ dev_err(epub->dev,
+ "err: to_host_seq reg 0x%02x, seq 0x%02x",
+ raw_seq, sip->to_host_seq);
+ err = 0;
+ goto _err;
+ }
+ }
+ }
+
+ /* first read one block out, if we luck enough, that's it
+ *
+ * To make design as simple as possible, we allocate skb(s)
+ * separately for each sif read operation to avoid global
+ * read_buf_pointe access. It coule be optimized late.
+ */
+
+ first_sz = sif_get_regs(epub)->config_w0;
+ rx_blksz = sif_get_blksz(epub);
+ first_skb = __dev_alloc_skb(roundup(first_sz, rx_blksz), GFP_KERNEL);
+
+ if (!first_skb) {
+ sif_unlock_bus(epub);
+ err = 0;
+ goto _err;
+ }
+
+ rx_buf = skb_put(first_skb, first_sz);
+
+ err = esp_common_read(epub, rx_buf, first_sz, ESP_SIF_NOSYNC, false);
+ sip_rx_count++;
+ if (unlikely(err)) {
+ dev_err(epub->dev, " first read err %d %d\n", err,
+ sif_get_regs(epub)->config_w0);
+ kfree_skb(first_skb);
+ sif_unlock_bus(epub);
+ goto _err;
+ }
+
+ shdr = (struct sip_hdr *)rx_buf;
+ if (SIP_HDR_IS_CTRL(shdr) && shdr->c_evtid == SIP_EVT_SLEEP) {
+ atomic_set(&sip->epub->ps.state, ESP_PM_ON);
+ }
+
+ if (likely(sif_get_ate_config() != 1))
+ sip->to_host_seq++;
+
+ if (shdr->len & 3) {
+ dev_err(epub->dev, "shdr->len[%d] error\n", shdr->len);
+ kfree_skb(first_skb);
+ sif_unlock_bus(epub);
+ err = -EIO;
+ goto _err;
+ }
+
+ if (shdr->len != first_sz) {
+ dev_err(epub->dev, "shdr->len[%d] first_size[%d] error\n",
+ shdr->len, first_sz);
+ kfree_skb(first_skb);
+ sif_unlock_bus(epub);
+ err = -EIO;
+ goto _err;
+ } else {
+ sif_unlock_bus(epub);
+ skb_trim(first_skb, shdr->len);
+ dev_dbg(epub->dev, "first_skb only\n");
+
+ rx_skb = first_skb;
+ }
+
+ if (atomic_read(&sip->state) == SIP_STOP) {
+ kfree_skb(rx_skb);
+ dev_err(epub->dev, "rx packet while sip stopped\n");
+ return 0;
+ }
+
+ sip_rx_pkt_enqueue(sip, rx_skb);
+ queue_work(sip->epub->esp_wkq, &sip->rx_process_work);
+
+_err:
+ return err;
+}
+
+int sip_post_init(struct esp_sip *sip, struct sip_evt_bootup2 *bevt)
+{
+ struct esp_pub *epub;
+ u8 mac_id = bevt->mac_addr[0];
+ int mac_index = 0, i;
+
+ if (!sip) {
+ ESSERT(0);
+ return -EINVAL;
+ }
+
+ epub = sip->epub;
+
+ sip->tx_aggr_write_ptr = sip->tx_aggr_buf;
+ sip->tx_blksz = bevt->tx_blksz;
+ sip->rx_blksz = bevt->rx_blksz;
+ sip->credit_to_reserve = bevt->credit_to_reserve;
+ sip->dump_rpbm_err = bevt->options & SIP_DUMP_RPBM_ERR;
+ sip->rxabort_fixed = bevt->options & SIP_RXABORT_FIXED;
+ sip->support_bgscan = bevt->options & SIP_SUPPORT_BGSCAN;
+ sip->sendup_rpbm_pkt = 0;
+
+ /* print out MAC addr... */
+ memcpy(epub->mac_addr, bevt->mac_addr, ETH_ALEN);
+ for (i = 0;
+ i < sizeof(esp_mac_prefix_table) / sizeof(struct esp_mac_prefix);
+ i++)
+ if (esp_mac_prefix_table[i].mac_index == mac_id) {
+ mac_index = i;
+ break;
+ }
+
+ epub->mac_addr[0] = esp_mac_prefix_table[mac_index].mac_addr_prefix[0];
+ epub->mac_addr[1] = esp_mac_prefix_table[mac_index].mac_addr_prefix[1];
+ epub->mac_addr[2] = esp_mac_prefix_table[mac_index].mac_addr_prefix[2];
+
+ atomic_set(&sip->noise_floor, bevt->noise_floor);
+
+ sip_recalc_credit_init(sip);
+
+ return 0;
+}
+
+/* write pkts in aggr buf to target memory */
+static void sip_write_pkts(struct esp_sip *sip, int pm_state)
+{
+ int tx_aggr_len, err;
+ struct sip_hdr *first_shdr;
+
+ tx_aggr_len = sip->tx_aggr_write_ptr - sip->tx_aggr_buf;
+ if (tx_aggr_len < sizeof(struct sip_hdr)) {
+ dev_err(sip->epub->dev, "[tx_aggr_len] %d < sizeof(sip_hdr)\n",
+ tx_aggr_len);
+ ESSERT(0);
+ return;
+ }
+
+ if (tx_aggr_len & 0x3) {
+ ESSERT(0);
+ return;
+ }
+
+ first_shdr = (struct sip_hdr *)sip->tx_aggr_buf;
+
+ if (atomic_read(&sip->tx_credits) <= SIP_CREDITS_LOW_THRESHOLD)
+ first_shdr->fc[1] |= SIP_HDR_F_NEED_CRDT_RPT;
+
+ /* still use lock bus instead of sif_lldesc_write_sync since we want to protect several global varibles assignments */
+ sif_lock_bus(sip->epub);
+
+ err = esp_common_write(sip->epub, sip->tx_aggr_buf, tx_aggr_len,
+ ESP_SIF_NOSYNC);
+ if (err)
+ dev_err(sip->epub->dev, "error while writing pkts: %d\n", err);
+
+ sip->tx_aggr_write_ptr = sip->tx_aggr_buf;
+ sip->tx_tot_len = 0;
+
+ sif_unlock_bus(sip->epub);
+}
+
+/* setup sip header and tx info, copy pkt into aggr buf */
+static int sip_pack_pkt(struct esp_sip *sip, struct sk_buff *skb, int *pm_state)
+{
+ struct ieee80211_tx_info *itx_info;
+ struct sip_hdr *shdr;
+ struct ieee80211_hdr *wh;
+ struct esp_vif *evif;
+ struct esp_node *node;
+ u32 tx_len, offset;
+ bool is_data = true;
+ u8 sta_index;
+ int alg;
+
+ itx_info = IEEE80211_SKB_CB(skb);
+ if (itx_info->flags == 0xffffffff) {
+ shdr = (struct sip_hdr *)skb->data;
+ is_data = false;
+ tx_len = skb->len;
+ } else {
+ wh = (struct ieee80211_hdr *)skb->data;
+ evif = (struct esp_vif *)itx_info->control.vif->drv_priv;
+ /* update sip header */
+ shdr = (struct sip_hdr *)sip->tx_aggr_write_ptr;
+
+ shdr->fc[0] = 0;
+ shdr->fc[1] = 0;
+
+ if (itx_info->flags & IEEE80211_TX_CTL_AMPDU)
+ SIP_HDR_SET_TYPE(shdr->fc[0], SIP_DATA_AMPDU);
+ else
+ SIP_HDR_SET_TYPE(shdr->fc[0], SIP_DATA);
+
+ if (!evif->epub) {
+ sip_tx_status_report(sip, skb, itx_info, false);
+ atomic_dec(&sip->tx_data_pkt_queued);
+ return -EINVAL;
+ }
+
+ /* make room for encrypted pkt */
+ if (itx_info->control.hw_key) {
+ alg = esp_cipher2alg(itx_info->control.hw_key->cipher);
+ if (unlikely(alg == -1)) {
+ sip_tx_status_report(sip, skb, itx_info, false);
+ atomic_dec(&sip->tx_data_pkt_queued);
+ return -1;
+ }
+
+ shdr->d_enc_flag = alg + 1;
+ shdr->d_hw_kid = itx_info->control.hw_key->hw_key_idx |
+ (evif->index << 7);
+ } else {
+ shdr->d_enc_flag = 0;
+ shdr->d_hw_kid = evif->index << 7 | evif->index;
+ }
+
+ /* update sip tx info */
+ node = esp_get_node_by_addr(sip->epub, wh->addr1);
+ if (node)
+ sta_index = node->index;
+ else
+ sta_index = ESP_PUB_MAX_STA + 1;
+
+ SIP_HDR_SET_IFIDX(shdr->fc[0], evif->index << 3 | sta_index);
+ shdr->d_p2p = itx_info->control.vif->p2p;
+
+ if (evif->index == 1)
+ shdr->d_p2p = 1;
+
+ shdr->d_ac = skb_get_queue_mapping(skb);
+ shdr->d_tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
+
+ wh = (struct ieee80211_hdr *)skb->data;
+
+ if (ieee80211_is_mgmt(wh->frame_control)) {
+ /* addba/delba/bar may use different tid/ac */
+ if (shdr->d_ac == WME_AC_VO)
+ shdr->d_tid = 7;
+
+ if (ieee80211_is_beacon(wh->frame_control)) {
+ shdr->d_tid = 8;
+ shdr->d_ac = 4;
+ }
+ }
+
+ if (check_ac_tid(skb->data, shdr->d_ac, shdr->d_tid)) {
+ shdr->d_ac = WME_AC_BE;
+ shdr->d_tid = 0;
+ }
+
+ /* make sure data is start at 4 bytes aligned addr. */
+ offset = roundup(sizeof(struct sip_hdr), 4);
+
+ if (SIP_HDR_IS_AMPDU(shdr)) {
+ memset(sip->tx_aggr_write_ptr + offset, 0,
+ sizeof(struct esp_tx_ampdu_entry));
+ offset += roundup(sizeof(struct esp_tx_ampdu_entry), 4);
+ }
+
+ tx_len = offset + skb->len;
+ shdr->len = tx_len; /* actual len */
+ }
+
+ shdr->seq = sip->txseq++;
+
+ /* copy skb to aggr buf */
+ memcpy(sip->tx_aggr_write_ptr + offset, skb->data, skb->len);
+
+ if (is_data) {
+ spin_lock_bh(&sip->epub->tx_lock);
+ sip->txdataseq = shdr->seq;
+ spin_unlock_bh(&sip->epub->tx_lock);
+
+ /* fake a tx_status and report to mac80211 stack to speed up tx, may affect
+ * 1) rate control (now it's all in target, so should be OK)
+ * 2) ps mode, mac80211 want to check ACK of ps/nulldata to see if AP is awake
+ * 3) BAR, mac80211 do BAR by checking ACK
+ *
+ * XXX: need to adjust for 11n, e.g. report tx_status according to BA received in target
+ */
+ sip_tx_status_report(sip, skb, itx_info, true);
+ atomic_dec(&sip->tx_data_pkt_queued);
+
+ STRACE_TX_DATA_INC();
+ } else {
+ /* check pm state here */
+
+ /* no need to hold ctrl skb */
+ sip_free_ctrl_skbuff(sip, skb);
+ STRACE_TX_CMD_INC();
+ }
+
+ /* TBD: roundup here or whole aggr-buf */
+ tx_len = roundup(tx_len, sip->tx_blksz);
+
+ sip->tx_aggr_write_ptr += tx_len;
+ sip->tx_tot_len += tx_len;
+
+ return 0;
+}
+
+static void sip_tx_status_report(struct esp_sip *sip, struct sk_buff *skb,
+ struct ieee80211_tx_info *tx_info,
+ bool success)
+{
+ struct ieee80211_hdr *wh;
+ struct esp_node *node;
+ struct esp_tx_tid *tid;
+ struct ieee80211_sta *sta;
+ u8 tidno;
+
+ if (!(tx_info->flags & IEEE80211_TX_CTL_AMPDU)) {
+ if (likely(success))
+ tx_info->flags |= IEEE80211_TX_STAT_ACK;
+ else
+ tx_info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+ /* manipulate rate status... */
+ tx_info->status.rates[0].idx = 11;
+ tx_info->status.rates[0].count = 1;
+ tx_info->status.rates[0].flags = 0;
+ tx_info->status.rates[1].idx = -1;
+ } else {
+ tx_info->flags |= IEEE80211_TX_STAT_AMPDU |
+ IEEE80211_TX_STAT_ACK;
+ tx_info->status.ampdu_len = 1;
+ tx_info->status.ampdu_ack_len = 1;
+
+ /* manipulate rate status... */
+ tx_info->status.rates[0].idx = 7;
+ tx_info->status.rates[0].count = 1;
+ tx_info->status.rates[0].flags = IEEE80211_TX_RC_MCS |
+ IEEE80211_TX_RC_SHORT_GI;
+ tx_info->status.rates[1].idx = -1;
+ }
+
+ if (!mod_support_no_txampdu() &&
+ cfg80211_get_chandef_type(&sip->epub->hw->conf.chandef) != NL80211_CHAN_NO_HT) {
+ wh = (struct ieee80211_hdr *)skb->data;
+
+ if (ieee80211_is_data_qos(wh->frame_control) &&
+ !(IEEE80211_SKB_CB(skb)->flags & IEEE80211_TX_CTL_AMPDU)) {
+ tidno = ieee80211_get_qos_ctl(wh)[0] &
+ IEEE80211_QOS_CTL_TID_MASK;
+
+ node = esp_get_node_by_addr(sip->epub, wh->addr1);
+ if (!node || !node->sta)
+ goto _exit;
+
+ sta = node->sta;
+ tid = &node->tid[tidno];
+ if (!tid) {
+ ESSERT(0);
+ goto _exit;
+ }
+
+ spin_lock_bh(&sip->epub->tx_ampdu_lock);
+
+ if (tid->state == ESP_TID_STATE_INIT &&
+ TID_TO_AC(tidno) != WME_AC_VO && tid->cnt >= 10) {
+ tid->state = ESP_TID_STATE_TRIGGER;
+ dev_dbg(sip->epub->dev,
+ "start tx ba session,addr:%pM,tid:%u\n",
+ wh->addr1, tidno);
+ spin_unlock_bh(&sip->epub->tx_ampdu_lock);
+ ieee80211_start_tx_ba_session(sta, tidno, 0);
+ } else {
+ if (tid->state == ESP_TID_STATE_INIT)
+ tid->cnt++;
+ else
+ tid->cnt = 0;
+
+ spin_unlock_bh(&sip->epub->tx_ampdu_lock);
+ }
+ }
+ }
+
+_exit:
+ ieee80211_tx_status(sip->epub->hw, skb);
+}
+
+/* NB: this routine should be locked when calling
+ */
+void sip_txq_process(struct esp_pub *epub)
+{
+ struct sk_buff *skb;
+ struct sip_hdr *hdr;
+ struct esp_sip *sip = epub->sip;
+ struct ieee80211_tx_info *itx_info;
+ u32 pkt_len, tx_len = 0;
+ int blknum = 0, pm_state = 0;
+ bool queued_back = false, out_of_credits = false;
+
+ while ((skb = skb_dequeue(&epub->txq))) {
+ /* cmd skb->len does not include sip_hdr too */
+ pkt_len = skb->len;
+ itx_info = IEEE80211_SKB_CB(skb);
+ if (itx_info->flags != 0xffffffff) {
+ pkt_len += roundup(sizeof(struct sip_hdr), 4);
+ if (itx_info->flags & IEEE80211_TX_CTL_AMPDU)
+ pkt_len += roundup(sizeof(struct esp_tx_ampdu_entry),
+ 4);
+ }
+
+ /* current design simply requires every sip_hdr must be at the begin of mblk, that definitely
+ * need to be optimized, e.g. calculate remain length in the previous mblk, if it larger than
+ * certain threshold (e.g, whole pkt or > 50% of pkt or 2 x sizeof(struct sip_hdr), append pkt
+ * to the previous mblk. This might be done in sip_pack_pkt()
+ */
+ pkt_len = roundup(pkt_len, sip->tx_blksz);
+ blknum = pkt_len / sip->tx_blksz;
+
+ /*
+ * FIXME: Empirical delay. Without this delay, the connection to
+ * a WiFi network crashes the kernel (sometimes at the second
+ * connection).
+ */
+ udelay(2000);
+
+ if (unlikely(atomic_read(&sip->credit_status) == RECALC_CREDIT_ENABLE)) { /* need recalc credit */
+ hdr = (struct sip_hdr *)skb->data;
+ itx_info = IEEE80211_SKB_CB(skb);
+
+ if (!(itx_info->flags == 0xffffffff &&
+ SIP_HDR_GET_TYPE(hdr->fc[0]) == SIP_CTRL &&
+ hdr->c_cmdid == SIP_CMD_RECALC_CREDIT &&
+ blknum <= atomic_read(&sip->tx_credits) - sip->credit_to_reserve)) { /* except cmd recalc credit */
+ dev_dbg(epub->dev, "recalc credits!\n");
+ STRACE_TX_OUT_OF_CREDIT_INC();
+ queued_back = true;
+ out_of_credits = true;
+ break;
+ }
+ } else {
+ if (unlikely(blknum > (atomic_read(&sip->tx_credits) - sip->credit_to_reserve - SIP_CTRL_CREDIT_RESERVE))) {
+ itx_info = IEEE80211_SKB_CB(skb);
+ if (itx_info->flags == 0xffffffff) { /* priv ctrl pkt */
+ if (blknum > atomic_read(&sip->tx_credits) - sip->credit_to_reserve) {
+ dev_dbg(epub->dev,
+ "%s cmd pkt out of credits!\n",
+ __func__);
+ STRACE_TX_OUT_OF_CREDIT_INC();
+ queued_back = true;
+ out_of_credits = true;
+ break;
+ }
+ } else {
+ dev_dbg(epub->dev,
+ "%s out of credits!\n",
+ __func__);
+ STRACE_TX_OUT_OF_CREDIT_INC();
+ queued_back = true;
+ out_of_credits = true;
+ break;
+ }
+ }
+ }
+
+ tx_len += pkt_len;
+ if (tx_len >= SIP_TX_AGGR_BUF_SIZE) {
+ /* do we need to have limitation likemax 8 pkts in a row? */
+ dev_dbg(epub->dev, "%s too much pkts in one shot!\n",
+ __func__);
+ STRACE_TX_ONE_SHOT_INC();
+ tx_len -= pkt_len;
+ queued_back = true;
+ break;
+ }
+
+ if (sip_pack_pkt(sip, skb, &pm_state) != 0) {
+ /* wrong pkt, won't send to target */
+ tx_len -= pkt_len;
+ continue;
+ }
+
+ atomic_sub(blknum, &sip->tx_credits);
+ /*
+ * FIXME: Empirical delay. Without this delay, the connection to
+ * a WiFi network crashes the kernel (sometimes at the second
+ * connection).
+ */
+ udelay(2000);
+
+ }
+
+ if (queued_back)
+ skb_queue_head(&epub->txq, skb);
+
+ if (atomic_read(&sip->state) == SIP_STOP
+#ifdef HOST_RESET_BUG
+ || atomic_read(&epub->wl.off) == 1
+#endif
+ ) {
+ queued_back = 1;
+ tx_len = 0;
+ }
+
+ if (tx_len)
+ sip_write_pkts(sip, pm_state);
+
+ if (queued_back && !out_of_credits)
+ /* skb pending, do async process again */
+ sip_trigger_txq_process(sip);
+}
+
+#ifndef NO_WMM_DUMMY
+static struct esp_80211_wmm_param_element esp_wmm_param = {
+ .oui = {0x00, 0x50, 0xf2},
+ .oui_type = 0x02,
+ .oui_subtype = 0x01,
+ .version = 0x01,
+ .qos_info = 0x00,
+ .reserved = 0x00,
+ .ac = {
+ {
+ .aci_aifsn = 0x03,
+ .cw = 0xa4,
+ .txop_limit = 0x0000,
+ },
+ {
+ .aci_aifsn = 0x27,
+ .cw = 0xa4,
+ .txop_limit = 0x0000,
+ },
+ {
+ .aci_aifsn = 0x42,
+ .cw = 0x43,
+ .txop_limit = 0x005e,
+ },
+ {
+ .aci_aifsn = 0x62,
+ .cw = 0x32,
+ .txop_limit = 0x002f,
+ },
+ },
+};
+
+static int esp_add_wmm(struct sk_buff *skb)
+{
+ u8 *p;
+ int flag = 0;
+ int remain_len;
+ int base_len;
+ int len;
+ struct ieee80211_mgmt *mgmt;
+ struct ieee80211_hdr *wh;
+
+ if (!skb)
+ return -1;
+
+ wh = (struct ieee80211_hdr *)skb->data;
+ mgmt = (struct ieee80211_mgmt *)((u8 *)skb->data);
+
+ if (ieee80211_is_assoc_resp(wh->frame_control)) {
+ p = mgmt->u.assoc_resp.variable;
+ base_len = (u8 *)mgmt->u.assoc_resp.variable - (u8 *)mgmt;
+ } else if (ieee80211_is_reassoc_resp(wh->frame_control)) {
+ p = mgmt->u.reassoc_resp.variable;
+ base_len = (u8 *)mgmt->u.reassoc_resp.variable - (u8 *)mgmt;
+ } else if (ieee80211_is_probe_resp(wh->frame_control)) {
+ p = mgmt->u.probe_resp.variable;
+ base_len = (u8 *)mgmt->u.probe_resp.variable - (u8 *)mgmt;
+ } else if (ieee80211_is_beacon(wh->frame_control)) {
+ p = mgmt->u.beacon.variable;
+ base_len = (u8 *)mgmt->u.beacon.variable - (u8 *)mgmt;
+ } else {
+ return 1;
+ }
+
+ remain_len = skb->len - base_len;
+
+ while (remain_len > 0) {
+ if (*p == 0xdd && *(p + 5) == 0x02) //wmm type
+ return 0;
+ else if (*p == 0x2d) //has ht cap
+ flag = 1;
+
+ len = *(++p);
+ p += (len + 1);
+ remain_len -= (len + 2);
+ }
+
+ if (remain_len < 0)
+ return -2;
+
+ if (flag == 1) {
+ skb_put(skb, 2 + sizeof(esp_wmm_param));
+
+ memset(p, 0xdd, sizeof(u8));
+ memset(p + 1, sizeof(esp_wmm_param), sizeof(u8));
+ memcpy(p + 2, &esp_wmm_param, sizeof(esp_wmm_param));
+ }
+
+ return 0;
+}
+#endif /* NO_WMM_DUMMY */
+
+/* parse mac_rx_ctrl and return length */
+static int sip_parse_mac_rx_info(struct esp_sip *sip,
+ struct esp_mac_rx_ctrl *mac_ctrl,
+ struct sk_buff *skb)
+{
+ struct ieee80211_rx_status *rx_status;
+ struct ieee80211_hdr *hdr;
+ struct ieee80211_hdr *wh;
+
+ rx_status = IEEE80211_SKB_RXCB(skb);
+ rx_status->freq = esp_ieee2mhz(mac_ctrl->channel);
+ rx_status->signal = mac_ctrl->rssi + mac_ctrl->noise_floor; /* snr actually, need to offset noise floor e.g. -85 */
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+ if (mac_ctrl->damatch0 == 1 && mac_ctrl->bssidmatch0 == 1 && /*match bssid and da, but beacon package contain other bssid */
+ !strncmp(hdr->addr2, sip->epub->wl.bssid, ETH_ALEN)) { /* force match addr2 */
+ if (++signal_loop >= SIGNAL_COUNT) {
+ avg_signal += rx_status->signal;
+ avg_signal /= SIGNAL_COUNT;
+ rx_status->signal = avg_signal + 5;
+ old_signal = rx_status->signal;
+ signal_loop = 0;
+ avg_signal = 0;
+ } else {
+ avg_signal += rx_status->signal;
+ rx_status->signal = old_signal;
+ }
+ }
+
+ rx_status->antenna = 0; /* one antenna for now */
+ rx_status->band = NL80211_BAND_2GHZ;
+ rx_status->flag = RX_FLAG_DECRYPTED | RX_FLAG_MMIC_STRIPPED;
+ if (mac_ctrl->sig_mode) {
+ rx_status->encoding |= RX_ENC_HT;
+ rx_status->rate_idx = mac_ctrl->MCS;
+ if (mac_ctrl->SGI)
+ rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
+ } else {
+ rx_status->rate_idx = esp_wmac_rate2idx(mac_ctrl->rate);
+ }
+
+ if (mac_ctrl->rxend_state == RX_FCS_ERR)
+ rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
+
+ /* Mic error frame flag */
+ if (mac_ctrl->rxend_state == RX_TKIPMIC_ERR ||
+ mac_ctrl->rxend_state == RX_CCMPMIC_ERR) {
+ if (atomic_read(&sip->epub->wl.tkip_key_set) == 1) {
+ rx_status->flag |= RX_FLAG_MMIC_ERROR;
+ atomic_set(&sip->epub->wl.tkip_key_set, 0);
+ printk("mic err\n");
+ } else {
+ printk("mic err discard\n");
+ }
+ }
+
+ wh = (struct ieee80211_hdr *)((u8 *)skb->data);
+
+#ifndef NO_WMM_DUMMY
+ if (ieee80211_is_mgmt(wh->frame_control))
+ esp_add_wmm(skb);
+#endif
+
+ /* some kernel e.g. 3.0.8 wrongly handles non-encrypted pkt like eapol */
+ if (ieee80211_is_data(wh->frame_control)) {
+ if (!ieee80211_has_protected(wh->frame_control))
+ rx_status->flag |= RX_FLAG_IV_STRIPPED;
+ else if (!atomic_read(&sip->epub->wl.ptk_cnt))
+ rx_status->flag |= RX_FLAG_IV_STRIPPED;
+ }
+
+ return 0;
+}
+
+static struct esp_mac_rx_ctrl *sip_parse_normal_mac_ctrl(struct sk_buff *skb,
+ int *pkt_len_enc,
+ int *buf_len,
+ int *pulled_len)
+{
+ struct sip_hdr *hdr = (struct sip_hdr *)skb->data;
+ struct esp_mac_rx_ctrl *mac_ctrl;
+ int len_in_hdr = hdr->len;
+
+ ESSERT(skb);
+ ESSERT(skb->len > SIP_MIN_DATA_PKT_LEN);
+
+ skb_pull(skb, sizeof(struct sip_hdr));
+ *pulled_len += sizeof(struct sip_hdr);
+ mac_ctrl = (struct esp_mac_rx_ctrl *)skb->data;
+ if (!mac_ctrl->aggregation) {
+ ESSERT(pkt_len_enc);
+ ESSERT(buf_len);
+ *pkt_len_enc = (mac_ctrl->sig_mode ? mac_ctrl->HT_length : mac_ctrl->legacy_length) - FCS_LEN;
+ *buf_len = len_in_hdr - sizeof(struct sip_hdr) -
+ sizeof(struct esp_mac_rx_ctrl);
+ }
+
+ skb_pull(skb, sizeof(struct esp_mac_rx_ctrl));
+ *pulled_len += sizeof(struct esp_mac_rx_ctrl);
+
+ return mac_ctrl;
+}
+
+/* for one MPDU (including subframe in AMPDU) */
+static struct sk_buff *sip_parse_data_rx_info(struct esp_sip *sip,
+ struct sk_buff *skb,
+ int pkt_len_enc, int buf_len,
+ struct esp_mac_rx_ctrl *mac_ctrl,
+ int *pulled_len)
+{
+ /* | mac_rx_ctrl | real_data_payload | ampdu_entries | */
+ struct sk_buff *rskb;
+ struct ieee80211_hdr *wh;
+ int pkt_len, ret;
+
+ if (mac_ctrl->aggregation) {
+ wh = (struct ieee80211_hdr *)skb->data;
+ pkt_len = pkt_len_enc;
+ if (ieee80211_has_protected(wh->frame_control)) //ampdu, it is CCMP enc
+ pkt_len -= 8;
+
+ buf_len = roundup(pkt_len, 4);
+ } else {
+ pkt_len = buf_len - 3 + ((pkt_len_enc - 1) & 0x3);
+ }
+
+#ifndef NO_WMM_DUMMY
+ rskb = __dev_alloc_skb(pkt_len_enc + sizeof(esp_wmm_param) + 2,
+ GFP_ATOMIC);
+#else
+ rskb = __dev_alloc_skb(pkt_len_enc, GFP_ATOMIC);
+#endif /* NO_WMM_DUMMY */
+ if (unlikely(!rskb)) {
+ dev_err(sip->epub->dev, "no mem for rskb\n");
+ return NULL;
+ }
+
+ skb_put(rskb, pkt_len_enc);
+
+ memcpy(rskb->data, skb->data, pkt_len);
+
+ if (pkt_len_enc > pkt_len)
+ memset(rskb->data + pkt_len, 0, pkt_len_enc - pkt_len);
+
+ /* strip out current pkt, move to the next one */
+ skb_pull(skb, buf_len);
+ *pulled_len += buf_len;
+
+ ret = sip_parse_mac_rx_info(sip, mac_ctrl, rskb);
+ if (ret == -1 && !mac_ctrl->aggregation) {
+ kfree_skb(rskb);
+ return NULL;
+ }
+
+ return rskb;
+}
+
+struct esp_sip *sip_attach(struct esp_pub *epub)
+{
+ struct esp_sip *sip;
+ struct sip_pkt *pkt;
+ int i, po;
+
+ sip = kzalloc(sizeof(*sip), GFP_KERNEL);
+ if (!sip) {
+ dev_dbg(epub->dev, "no mem for sip!\n");
+ goto _err_sip;
+ }
+
+ /* Finding the smalest available space to allocate this variavle */
+ po = get_order(SIP_TX_AGGR_BUF_SIZE);
+
+ sip->tx_aggr_buf = (u8 *)__get_free_pages(GFP_ATOMIC, po);
+ if (!sip->tx_aggr_buf) {
+ dev_err(epub->dev, "no mem for tx_aggr_buf!\n");
+ goto _err_aggr;
+ }
+
+ spin_lock_init(&sip->lock);
+
+ INIT_LIST_HEAD(&sip->free_ctrl_txbuf);
+ INIT_LIST_HEAD(&sip->free_ctrl_rxbuf);
+
+ for (i = 0; i < SIP_CTRL_BUF_N; i++) {
+ pkt = kzalloc(sizeof(*pkt), GFP_KERNEL);
+ if (!pkt)
+ goto _err_pkt;
+
+ pkt->buf_begin = kzalloc(SIP_CTRL_BUF_SZ, GFP_KERNEL);
+ if (!pkt->buf_begin) {
+ kfree(pkt);
+ pkt = NULL;
+ goto _err_pkt;
+ }
+
+ pkt->buf_len = SIP_CTRL_BUF_SZ;
+ pkt->buf = pkt->buf_begin;
+
+ if (i < SIP_CTRL_TXBUF_N)
+ list_add_tail(&pkt->list, &sip->free_ctrl_txbuf);
+ else
+ list_add_tail(&pkt->list, &sip->free_ctrl_rxbuf);
+ }
+
+ mutex_init(&sip->rx_mtx);
+ skb_queue_head_init(&sip->rxq);
+ INIT_WORK(&sip->rx_process_work, sip_rxq_process);
+
+ sip->epub = epub;
+ atomic_set(&sip->noise_floor, -96);
+
+ atomic_set(&sip->state, SIP_INIT);
+ atomic_set(&sip->tx_credits, 0);
+
+ if (!sip->rawbuf) {
+ sip->rawbuf = kzalloc(SIP_BOOT_BUF_SIZE, GFP_KERNEL);
+ if (!sip->rawbuf) {
+ dev_err(epub->dev, "no mem for rawbuf!\n");
+ goto _err_pkt;
+ }
+ }
+
+ atomic_set(&sip->state, SIP_PREPARE_BOOT);
+
+ return sip;
+
+_err_pkt:
+ sip_free_init_ctrl_buf(sip);
+
+ if (sip->tx_aggr_buf) {
+ po = get_order(SIP_TX_AGGR_BUF_SIZE);
+ free_pages((unsigned long)sip->tx_aggr_buf, po);
+ sip->tx_aggr_buf = NULL;
+ }
+
+_err_aggr:
+ kfree(sip);
+ sip = NULL;
+
+_err_sip:
+ return NULL;
+}
+
+static void sip_free_init_ctrl_buf(struct esp_sip *sip)
+{
+ struct sip_pkt *pkt, *tpkt;
+
+ list_for_each_entry_safe(pkt, tpkt, &sip->free_ctrl_txbuf, list) {
+ list_del(&pkt->list);
+ kfree(pkt->buf_begin);
+ kfree(pkt);
+ }
+
+ list_for_each_entry_safe(pkt, tpkt, &sip->free_ctrl_rxbuf, list) {
+ list_del(&pkt->list);
+ kfree(pkt->buf_begin);
+ kfree(pkt);
+ }
+}
+
+void sip_detach(struct esp_sip *sip)
+{
+ if (!sip)
+ return;
+
+ sip_free_init_ctrl_buf(sip);
+
+ if (atomic_read(&sip->state) == SIP_RUN) {
+ sif_disable_target_interrupt(sip->epub);
+
+ atomic_set(&sip->state, SIP_STOP);
+
+ /* disable irq here */
+ sif_disable_irq(sip->epub);
+ cancel_work_sync(&sip->rx_process_work);
+
+ skb_queue_purge(&sip->rxq);
+ mutex_destroy(&sip->rx_mtx);
+ cancel_work_sync(&sip->epub->sendup_work);
+ skb_queue_purge(&sip->epub->rxq);
+
+ if (test_and_clear_bit(ESP_WL_FLAG_HW_REGISTERED,
+ &sip->epub->wl.flags))
+ ieee80211_unregister_hw(sip->epub->hw);
+
+ /* cancel all worker/timer */
+ cancel_work_sync(&sip->epub->tx_work);
+ skb_queue_purge(&sip->epub->txq);
+ skb_queue_purge(&sip->epub->txdoneq);
+
+ free_pages((unsigned long)sip->tx_aggr_buf,
+ get_order(SIP_TX_AGGR_BUF_SIZE));
+ sip->tx_aggr_buf = NULL;
+
+ atomic_set(&sip->state, SIP_INIT);
+ } else if (atomic_read(&sip->state) >= SIP_BOOT &&
+ atomic_read(&sip->state) <= SIP_WAIT_BOOTUP) {
+ sif_disable_target_interrupt(sip->epub);
+ atomic_set(&sip->state, SIP_STOP);
+ sif_disable_irq(sip->epub);
+ kfree(sip->rawbuf);
+
+ if (atomic_read(&sip->state) == SIP_SEND_INIT) {
+ cancel_work_sync(&sip->rx_process_work);
+ skb_queue_purge(&sip->rxq);
+ mutex_destroy(&sip->rx_mtx);
+ cancel_work_sync(&sip->epub->sendup_work);
+ skb_queue_purge(&sip->epub->rxq);
+ }
+
+ if (test_and_clear_bit(ESP_WL_FLAG_HW_REGISTERED,
+ &sip->epub->wl.flags))
+ ieee80211_unregister_hw(sip->epub->hw);
+ atomic_set(&sip->state, SIP_INIT);
+ } else {
+ dev_err(sip->epub->dev, "wrong state (%d) for detaching sip\n",
+ atomic_read(&sip->state));
+ }
+
+ kfree(sip);
+}
+
+int sip_write_memory(struct esp_sip *sip, u32 addr, u8 *buf, u16 len)
+{
+ struct sip_cmd_write_memory *cmd;
+ struct sip_hdr *chdr;
+ u16 remains, hdrs, bufsize;
+ u32 loadaddr;
+ u8 *src;
+ int err = 0;
+ u32 *t;
+
+ if (!sip || !sip->rawbuf) {
+ ESSERT(sip);
+ ESSERT(sip->rawbuf);
+ return -EINVAL;
+ }
+
+ memset(sip->rawbuf, 0, SIP_BOOT_BUF_SIZE);
+
+ chdr = (struct sip_hdr *)sip->rawbuf;
+ SIP_HDR_SET_TYPE(chdr->fc[0], SIP_CTRL);
+ chdr->c_cmdid = SIP_CMD_WRITE_MEMORY;
+
+ remains = len;
+ hdrs = sizeof(struct sip_hdr) + sizeof(struct sip_cmd_write_memory);
+
+ while (remains) {
+ src = &buf[len - remains];
+ loadaddr = addr + (len - remains);
+
+ if (remains < (SIP_BOOT_BUF_SIZE - hdrs)) {
+ /* aligned with 4 bytes */
+ bufsize = roundup(remains, 4);
+ memset(sip->rawbuf + hdrs, 0, bufsize);
+ remains = 0;
+ } else {
+ bufsize = SIP_BOOT_BUF_SIZE - hdrs;
+ remains -= bufsize;
+ }
+
+ chdr->len = bufsize + hdrs;
+ chdr->seq = sip->txseq++;
+ cmd = (struct sip_cmd_write_memory *)(sip->rawbuf + SIP_CTRL_HDR_LEN);
+ cmd->len = bufsize;
+ cmd->addr = loadaddr;
+ memcpy(sip->rawbuf + hdrs, src, bufsize);
+
+ t = (u32 *)sip->rawbuf;
+ err = esp_common_write(sip->epub, sip->rawbuf, chdr->len,
+ ESP_SIF_SYNC);
+ if (err) {
+ dev_err(sip->epub->dev, "send buffer failed\n");
+ return err;
+ }
+ // 1ms is enough, in fact on dell-d430, need not delay at all.
+ mdelay(1);
+ }
+
+ return err;
+}
+
+int sip_send_cmd(struct esp_sip *sip, int cid, u32 cmdlen, void *cmd)
+{
+ struct sip_hdr *chdr;
+ struct sip_pkt *pkt;
+ int ret;
+
+ pkt = sip_get_ctrl_buf(sip, SIP_TX_CTRL_BUF);
+ if (!pkt)
+ return -ENOMEM;
+
+ chdr = (struct sip_hdr *)pkt->buf_begin;
+ chdr->len = SIP_CTRL_HDR_LEN + cmdlen;
+ chdr->seq = sip->txseq++;
+ chdr->c_cmdid = cid;
+
+ if (cmd) {
+ memset(pkt->buf, 0, cmdlen);
+ memcpy(pkt->buf, (u8 *)cmd, cmdlen);
+ }
+
+ ret = esp_common_write(sip->epub, pkt->buf_begin, chdr->len,
+ ESP_SIF_SYNC);
+ if (ret)
+ dev_err(sip->epub->dev, "send cmd %d failed\n", cid);
+
+ sip_reclaim_ctrl_buf(sip, pkt, SIP_TX_CTRL_BUF);
+
+ /* Hack here: reset tx/rx seq before target ram code is up... */
+ if (cid == SIP_CMD_BOOTUP) {
+ sip->rxseq = 0;
+ sip->txseq = 0;
+ sip->txdataseq = 0;
+ }
+
+ return ret;
+}
+
+struct sk_buff *sip_alloc_ctrl_skbuf(struct esp_sip *sip, u16 len, u32 cid)
+{
+ struct sip_hdr *si;
+ struct ieee80211_tx_info *ti;
+ struct sk_buff *skb;
+
+ ESSERT(len <= sip->tx_blksz);
+
+ /* no need to reserve space for net stack */
+ skb = __dev_alloc_skb(len, GFP_KERNEL);
+ if (!skb) {
+ dev_err(sip->epub->dev, "no skb for ctrl!\n");
+ return NULL;
+ }
+
+ skb->len = len;
+
+ ti = IEEE80211_SKB_CB(skb);
+ /* set tx_info flags to 0xffffffff to indicate sip_ctrl pkt */
+ ti->flags = 0xffffffff;
+
+ si = (struct sip_hdr *)skb->data;
+ memset(si, 0, sizeof(struct sip_hdr));
+ SIP_HDR_SET_TYPE(si->fc[0], SIP_CTRL);
+ si->len = len;
+ si->c_cmdid = cid;
+
+ return skb;
+}
+
+void sip_free_ctrl_skbuff(struct esp_sip *sip, struct sk_buff *skb)
+{
+ memset(IEEE80211_SKB_CB(skb), 0, sizeof(struct ieee80211_tx_info));
+ kfree_skb(skb);
+}
+
+static struct sip_pkt *sip_get_ctrl_buf(struct esp_sip *sip,
+ enum sip_buf_type bftype)
+{
+ struct sip_pkt *pkt;
+ struct list_head *bflist;
+ struct sip_hdr *chdr;
+
+ /* FIXME: Why taking spinlock to check list_empty? */
+ bflist = (bftype == SIP_TX_CTRL_BUF) ? &sip->free_ctrl_txbuf : &sip->free_ctrl_rxbuf;
+
+ spin_lock_bh(&sip->lock);
+
+ if (list_empty(bflist)) {
+ spin_unlock_bh(&sip->lock);
+ return NULL;
+ }
+
+ pkt = list_first_entry(bflist, struct sip_pkt, list);
+ list_del(&pkt->list);
+ spin_unlock_bh(&sip->lock);
+
+ if (bftype == SIP_TX_CTRL_BUF) {
+ chdr = (struct sip_hdr *)pkt->buf_begin;
+ SIP_HDR_SET_TYPE(chdr->fc[0], SIP_CTRL);
+ pkt->buf = pkt->buf_begin + SIP_CTRL_HDR_LEN;
+ } else {
+ pkt->buf = pkt->buf_begin;
+ }
+
+ return pkt;
+}
+
+static void sip_reclaim_ctrl_buf(struct esp_sip *sip, struct sip_pkt *pkt,
+ enum sip_buf_type bftype)
+{
+ struct list_head *bflist;
+
+ if (bftype == SIP_TX_CTRL_BUF)
+ bflist = &sip->free_ctrl_txbuf;
+ else if (bftype == SIP_RX_CTRL_BUF)
+ bflist = &sip->free_ctrl_rxbuf;
+ else
+ return;
+
+ pkt->buf = pkt->buf_begin;
+
+ spin_lock_bh(&sip->lock);
+ list_add_tail(&pkt->list, bflist);
+ spin_unlock_bh(&sip->lock);
+}
+
+int sip_poll_bootup_event(struct esp_sip *sip)
+{
+ int ret = 0;
+
+ if (gl_bootup_cplx)
+ ret = wait_for_completion_timeout(gl_bootup_cplx, 2 * HZ);
+
+ if (!ret) {
+ dev_err(sip->epub->dev, "bootup event timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ if (!sif_get_ate_config())
+ ret = esp_register_mac80211(sip->epub);
+
+ atomic_set(&sip->state, SIP_RUN);
+
+ return ret;
+}
+
+/* FIXME: always returning 0 ? */
+int sip_poll_resetting_event(struct esp_sip *sip)
+{
+ unsigned int ret;
+
+ if (gl_bootup_cplx)
+ ret = wait_for_completion_timeout(gl_bootup_cplx, 10 * HZ);
+
+ if (!ret) {
+ dev_err(sip->epub->dev, "resetting event timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+bool sip_queue_need_stop(struct esp_sip *sip)
+{
+ return atomic_read(&sip->tx_data_pkt_queued) >= SIP_STOP_QUEUE_THRESHOLD ||
+ (atomic_read(&sip->tx_credits) < 8 &&
+ atomic_read(&sip->tx_data_pkt_queued) >= SIP_STOP_QUEUE_THRESHOLD / 4 * 3);
+}
+
+bool sip_queue_may_resume(struct esp_sip *sip)
+{
+ return atomic_read(&sip->epub->txq_stopped) &&
+ !test_bit(ESP_WL_FLAG_STOP_TXQ, &sip->epub->wl.flags) &&
+ ((atomic_read(&sip->tx_credits) >= 16 &&
+ atomic_read(&sip->tx_data_pkt_queued) < SIP_RESUME_QUEUE_THRESHOLD * 2) ||
+ atomic_read(&sip->tx_data_pkt_queued) < SIP_RESUME_QUEUE_THRESHOLD);
+}
+
+int sip_cmd_enqueue(struct esp_sip *sip, struct sk_buff *skb, int prior)
+{
+ if (!sip || !sip->epub || !skb) {
+ return -EINVAL;
+ }
+
+ if (prior == ENQUEUE_PRIOR_HEAD)
+ skb_queue_head(&sip->epub->txq, skb);
+ else
+ skb_queue_tail(&sip->epub->txq, skb);
+
+ if (!sif_get_ate_config())
+ ieee80211_queue_work(sip->epub->hw, &sip->epub->tx_work);
+ else
+ queue_work(sip->epub->esp_wkq, &sip->epub->tx_work);
+
+ return 0;
+}
+
+void sip_tx_data_pkt_enqueue(struct esp_pub *epub, struct sk_buff *skb)
+{
+ if (!epub || !epub->sip || !skb)
+ return;
+
+ skb_queue_tail(&epub->txq, skb);
+ atomic_inc(&epub->sip->tx_data_pkt_queued);
+
+ if (sip_queue_need_stop(epub->sip))
+ if (epub->hw) {
+ ieee80211_stop_queues(epub->hw);
+ atomic_set(&epub->txq_stopped, true);
+ }
+}
diff --git a/drivers/staging/esp8089/esp_sip.h b/drivers/staging/esp8089/esp_sip.h
new file mode 100644
index 000000000000..fe58aae4ba54
--- /dev/null
+++ b/drivers/staging/esp8089/esp_sip.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2009 - 2014 Espressif System.
+ * Copyright (c) 2017 Free Electrons
+ * Quentin Schulz <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ */
+
+#ifndef _ESP_SIP_H
+#define _ESP_SIP_H
+
+#include "sip2_common.h"
+
+#define SIP_CTRL_CREDIT_RESERVE 2
+
+#define SIP_PKT_MAX_LEN (1024 * 16)
+
+/* 16KB on normal X86 system, should check before porting to orhters */
+
+#define SIP_TX_AGGR_BUF_SIZE (4 * PAGE_SIZE)
+#define SIP_RX_AGGR_BUF_SIZE (4 * PAGE_SIZE)
+
+struct sk_buff;
+
+struct sip_pkt {
+ struct list_head list;
+ u8 *buf_begin;
+ u32 buf_len;
+ u8 *buf;
+};
+
+enum RECALC_CREDIT_STATE {
+ RECALC_CREDIT_DISABLE = 0,
+ RECALC_CREDIT_ENABLE = 1,
+};
+
+enum ENQUEUE_PRIOR {
+ ENQUEUE_PRIOR_TAIL = 0,
+ ENQUEUE_PRIOR_HEAD,
+};
+
+enum SIP_STATE {
+ SIP_INIT = 0,
+ SIP_PREPARE_BOOT,
+ SIP_BOOT,
+ SIP_SEND_INIT,
+ SIP_WAIT_BOOTUP,
+ SIP_RUN,
+ SIP_SUSPEND,
+ SIP_STOP
+};
+
+enum sip_notifier {
+ SIP_TX_DONE = 1,
+ SIP_RX_DONE = 2,
+};
+
+#define SIP_CREDITS_LOW_THRESHOLD 64 //i.e. 4k
+
+struct esp_sip {
+ struct list_head free_ctrl_txbuf;
+ struct list_head free_ctrl_rxbuf;
+
+ u32 rxseq; /* sip pkt seq, should match target side */
+ u32 txseq;
+ u32 txdataseq;
+
+ u8 to_host_seq;
+
+ atomic_t state;
+ spinlock_t lock;
+ atomic_t tx_credits;
+
+ atomic_t tx_ask_credit_update;
+
+ u8 *rawbuf; /* used in boot stage, free once chip is fully up */
+ u8 *tx_aggr_buf;
+ u8 *tx_aggr_write_ptr; /* update after insertion of each pkt */
+ u8 *tx_aggr_lastpkt_ptr;
+
+ struct mutex rx_mtx;
+ struct sk_buff_head rxq;
+ struct work_struct rx_process_work;
+
+ u16 tx_blksz;
+ u16 rx_blksz;
+
+ bool dump_rpbm_err;
+ bool sendup_rpbm_pkt;
+ bool rxabort_fixed;
+ bool support_bgscan;
+ u8 credit_to_reserve;
+
+ atomic_t credit_status;
+ struct timer_list credit_timer;
+
+ atomic_t noise_floor;
+
+ u32 tx_tot_len; /* total len for one transaction */
+ u32 rx_tot_len;
+
+ atomic_t rx_handling;
+ atomic_t tx_data_pkt_queued;
+
+ atomic_t data_tx_stopped;
+ atomic_t tx_stopped;
+
+ struct esp_pub *epub;
+};
+
+int sip_rx(struct esp_pub *epub);
+
+int sip_write_memory(struct esp_sip *sip, u32 addr, u8 *buf, u16 len);
+
+int sip_send_cmd(struct esp_sip *sip, int cid, u32 cmdlen, void *cmd);
+
+struct esp_sip *sip_attach(struct esp_pub *epub);
+
+int sip_post_init(struct esp_sip *sip, struct sip_evt_bootup2 *bevt);
+
+void sip_detach(struct esp_sip *sip);
+
+void sip_txq_process(struct esp_pub *epub);
+
+struct sk_buff *sip_alloc_ctrl_skbuf(struct esp_sip *sip, u16 len, u32 cid);
+
+void sip_free_ctrl_skbuff(struct esp_sip *sip, struct sk_buff *skb);
+
+bool sip_queue_need_stop(struct esp_sip *sip);
+bool sip_queue_may_resume(struct esp_sip *sip);
+
+void sip_tx_data_pkt_enqueue(struct esp_pub *epub, struct sk_buff *skb);
+
+int sip_cmd_enqueue(struct esp_sip *sip, struct sk_buff *skb, int prior);
+
+int sip_poll_bootup_event(struct esp_sip *sip);
+
+int sip_poll_resetting_event(struct esp_sip *sip);
+
+void sip_trigger_txq_process(struct esp_sip *sip);
+
+void sip_send_chip_init(struct esp_sip *sip);
+
+bool mod_support_no_txampdu(void);
+
+bool mod_support_no_rxampdu(void);
+#endif
diff --git a/drivers/staging/esp8089/esp_utils.c b/drivers/staging/esp8089/esp_utils.c
new file mode 100644
index 000000000000..b35428d70c91
--- /dev/null
+++ b/drivers/staging/esp8089/esp_utils.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2009 - 2014 Espressif System.
+ * Copyright (c) 2017 Free Electrons
+ * Quentin Schulz <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ */
+
+#include "linux/types.h"
+#include "linux/kernel.h"
+#include <linux/ieee80211.h>
+#include <net/mac80211.h>
+#include <linux/skbuff.h>
+
+#include <net/tcp.h>
+#include <linux/ip.h>
+#include <asm/checksum.h>
+
+#include "esp_pub.h"
+#include "esp_utils.h"
+#include "esp_wmac.h"
+#include "esp_debug.h"
+
+/* Convert IEEE channel number to MHz frequency. */
+u32 esp_ieee2mhz(u8 chan)
+{
+ if (chan == 14)
+ return 2484;
+
+ if (chan < 14)
+ return 2407 + chan * 5;
+
+ return 2512 + ((chan - 15) * 20);
+ //TODO, add 5GHz
+}
+
+enum {
+ ESP_RATE_1_LONG = 0x0,
+ ESP_RATE_2_LONG = 0x1,
+ ESP_RATE_2_SHORT = 0x5,
+ ESP_RATE_5_SHORT = 0x6,
+ ESP_RATE_5_LONG = 0x2,
+ ESP_RATE_11_SHORT = 0x7,
+ ESP_RATE_11_LONG = 0x3,
+ ESP_RATE_6 = 0xb,
+ ESP_RATE_9 = 0xf,
+ ESP_RATE_12 = 0xa,
+ ESP_RATE_18 = 0xe,
+ ESP_RATE_24 = 0x9,
+ ESP_RATE_36 = 0xd,
+ ESP_RATE_48 = 0x8,
+ ESP_RATE_54 = 0xc,
+ /* ESP_RATE_MCS0 =0x10,
+ * ESP_RATE_MCS1 =0x11,
+ * ESP_RATE_MCS2 =0x12,
+ * ESP_RATE_MCS3 =0x13,
+ * ESP_RATE_MCS4 =0x14,
+ * ESP_RATE_MCS5 =0x15,
+ * ESP_RATE_MCS6 =0x16,
+ * ESP_RATE_MCS7 =0x17,
+ */
+};
+
+static u8 esp_rate_table[20] = {
+ ESP_RATE_1_LONG,
+ ESP_RATE_2_SHORT,
+ ESP_RATE_5_SHORT,
+ ESP_RATE_11_SHORT,
+ ESP_RATE_6,
+ ESP_RATE_9,
+ ESP_RATE_12,
+ ESP_RATE_18,
+ ESP_RATE_24,
+ ESP_RATE_36,
+ ESP_RATE_48,
+ ESP_RATE_54,
+ /* ESP_RATE_MCS0,
+ * ESP_RATE_MCS1,
+ * ESP_RATE_MCS2,
+ * ESP_RATE_MCS3,
+ * ESP_RATE_MCS4,
+ * ESP_RATE_MCS5,
+ * ESP_RATE_MCS6,
+ * ESP_RATE_MCS7,
+ */
+};
+
+s8 esp_wmac_rate2idx(u8 rate)
+{
+ int i;
+
+ if (rate == ESP_RATE_2_LONG)
+ return 1;
+
+ if (rate == ESP_RATE_5_LONG)
+ return 2;
+
+ if (rate == ESP_RATE_11_LONG)
+ return 3;
+
+ for (i = 0; i < 20; i++)
+ if (rate == esp_rate_table[i])
+ return i;
+
+ return 0;
+}
+
+bool esp_wmac_rxsec_error(u8 error)
+{
+ return (error >= RX_SECOV_ERR && error <= RX_SECFIFO_TIMEOUT) ||
+ (error >= RX_WEPICV_ERR && error <= RX_WAPIMIC_ERR);
+}
+
+int esp_cipher2alg(int cipher)
+{
+ if (cipher == WLAN_CIPHER_SUITE_TKIP)
+ return ALG_TKIP;
+
+ if (cipher == WLAN_CIPHER_SUITE_CCMP)
+ return ALG_CCMP;
+
+ if (cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ cipher == WLAN_CIPHER_SUITE_WEP104)
+ return ALG_WEP;
+
+ if (cipher == WLAN_CIPHER_SUITE_AES_CMAC)
+ return ALG_AES_CMAC;
+
+ return -1;
+}
diff --git a/drivers/staging/esp8089/esp_utils.h b/drivers/staging/esp8089/esp_utils.h
new file mode 100644
index 000000000000..3a4d461a4e70
--- /dev/null
+++ b/drivers/staging/esp8089/esp_utils.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2011 - 2012 Espressif System.
+ * Copyright (c) 2017 Free Electrons
+ * Quentin Schulz <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ */
+
+#ifndef _ESP_UTILS_H_
+#define _ESP_UTILS_H_
+
+#include "linux/types.h"
+
+u32 esp_ieee2mhz(u8 chan);
+
+enum ieee80211_key_alg {
+ ALG_WEP,
+ ALG_TKIP,
+ ALG_CCMP,
+ ALG_AES_CMAC
+};
+
+int esp_cipher2alg(int cipher);
+#endif
diff --git a/drivers/staging/esp8089/esp_wl.h b/drivers/staging/esp8089/esp_wl.h
new file mode 100644
index 000000000000..9eb1f6421947
--- /dev/null
+++ b/drivers/staging/esp8089/esp_wl.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2009 - 2014 Espressif System.
+ * Copyright (c) 2017 Free Electrons
+ * Quentin Schulz <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ */
+
+#ifndef _ESP_WL_H_
+#define _ESP_WL_H_
+
+//#define MAX_PROBED_SSID_INDEX 9
+
+enum {
+ CONF_HW_BIT_RATE_1MBPS = BIT(0),
+ CONF_HW_BIT_RATE_2MBPS = BIT(1),
+ CONF_HW_BIT_RATE_5_5MBPS = BIT(2),
+ CONF_HW_BIT_RATE_11MBPS = BIT(3),
+ CONF_HW_BIT_RATE_6MBPS = BIT(4),
+ CONF_HW_BIT_RATE_9MBPS = BIT(5),
+ CONF_HW_BIT_RATE_12MBPS = BIT(6),
+ CONF_HW_BIT_RATE_18MBPS = BIT(7),
+ CONF_HW_BIT_RATE_22MBPS = BIT(8),
+ CONF_HW_BIT_RATE_24MBPS = BIT(9),
+ CONF_HW_BIT_RATE_36MBPS = BIT(10),
+ CONF_HW_BIT_RATE_48MBPS = BIT(11),
+ CONF_HW_BIT_RATE_54MBPS = BIT(12),
+};
+
+#define CONF_HW_BIT_RATE_11B_MASK (CONF_HW_BIT_RATE_1MBPS | CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | CONF_HW_BIT_RATE_11MBPS)
+
+#endif /* _ESP_WL_H_ */
diff --git a/drivers/staging/esp8089/esp_wmac.h b/drivers/staging/esp8089/esp_wmac.h
new file mode 100644
index 000000000000..67ea8e147c43
--- /dev/null
+++ b/drivers/staging/esp8089/esp_wmac.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2011 - 2012 Espressif System.
+ * Copyright (c) 2017 Free Electrons
+ * Quentin Schulz <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ */
+
+#ifndef _ESP_WMAC_H_
+#define _ESP_WMAC_H_
+
+struct esp_mac_rx_ctrl {
+ signed rssi:8;
+ unsigned rate:4;
+ unsigned is_group:1;
+ unsigned:1;
+ unsigned sig_mode:2;
+ unsigned legacy_length:12;
+ unsigned damatch0:1;
+ unsigned damatch1:1;
+ unsigned bssidmatch0:1;
+ unsigned bssidmatch1:1;
+ unsigned MCS:7;
+ unsigned CWB:1;
+ unsigned HT_length:16;
+ unsigned smoothing:1;
+ unsigned not_sounding:1;
+ unsigned:1;
+ unsigned aggregation:1;
+ unsigned STBC:2;
+ unsigned FEC_CODING:1;
+ unsigned SGI:1;
+ unsigned rxend_state:8;
+ unsigned ampdu_cnt:8;
+ unsigned channel:4;
+ unsigned:4;
+ signed noise_floor:8;
+};
+
+struct esp_rx_ampdu_len {
+ unsigned substate:8;
+ unsigned sublen:12;
+ unsigned:12;
+};
+
+struct esp_tx_ampdu_entry {
+ u32 sub_len:12, dili_num:7, :1, null_byte:2, data:1, enc:1, seq:8;
+};
+
+//rxend_state flags
+#define RX_PYH_ERR_MIN 0x42
+#define RX_AGC_ERR_MIN 0x42
+#define RX_AGC_ERR_MAX 0x47
+#define RX_OFDM_ERR_MIN 0x50
+#define RX_OFDM_ERR_MAX 0x58
+#define RX_CCK_ERR_MIN 0x59
+#define RX_CCK_ERR_MAX 0x5F
+#define RX_ABORT 0x80
+#define RX_SF_ERR 0x40
+#define RX_FCS_ERR 0x41
+#define RX_AHBOV_ERR 0xC0
+#define RX_BUFOV_ERR 0xC1
+#define RX_BUFINV_ERR 0xC2
+#define RX_AMPDUSF_ERR 0xC3
+#define RX_AMPDUBUFOV_ERR 0xC4
+#define RX_MACBBFIFOOV_ERR 0xC5
+#define RX_RPBM_ERR 0xC6
+#define RX_BTFORCE_ERR 0xC7
+#define RX_SECOV_ERR 0xE1
+#define RX_SECPROT_ERR0 0xE2
+#define RX_SECPROT_ERR1 0xE3
+#define RX_SECKEY_ERR 0xE4
+#define RX_SECCRLEN_ERR 0xE5
+#define RX_SECFIFO_TIMEOUT 0xE6
+#define RX_WEPICV_ERR 0xF0
+#define RX_TKIPICV_ERR 0xF4
+#define RX_TKIPMIC_ERR 0xF5
+#define RX_CCMPMIC_ERR 0xF8
+#define RX_WAPIMIC_ERR 0xFC
+
+s8 esp_wmac_rate2idx(u8 rate);
+bool esp_wmac_rxsec_error(u8 error);
+
+#endif /* _ESP_WMAC_H_ */
diff --git a/drivers/staging/esp8089/sdio_sif_esp.c b/drivers/staging/esp8089/sdio_sif_esp.c
new file mode 100644
index 000000000000..d8842f5085ce
--- /dev/null
+++ b/drivers/staging/esp8089/sdio_sif_esp.c
@@ -0,0 +1,552 @@
+/*
+ * Copyright (c) 2010 - 2013 Espressif System.
+ * Copyright (c) 2017 Free Electrons
+ * Quentin Schulz <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ */
+
+#include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/core.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sd.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <linux/time.h>
+#include <linux/pm.h>
+
+#include "esp_pub.h"
+#include "esp_sif.h"
+#include "esp_sip.h"
+#include "esp_debug.h"
+#include "slc_host_register.h"
+#include "esp_version.h"
+#include "esp_ctrl.h"
+#include "esp_file.h"
+
+unsigned int esp_msg_level = ESP_DBG_ERROR | ESP_SHOW;
+
+/* HdG: Note:
+ * 1) MMC_HAS_FORCE_DETECT_CHANGE is a hack which is set by my sunxi-wip
+ * tree. FIXME replace with a version check once mmc_force_detect_change()
+ * is added to the mainline kernel.
+ * 2) This version does NOT implement keep_power, the dts must mark the
+ * regulators as regulator-always-on and not use mmc-pwrseq for this stub
+ * to work.
+ */
+#ifndef MMC_HAS_FORCE_DETECT_CHANGE
+void mmc_force_detect_change(struct mmc_host *host, unsigned long delay,
+ bool keep_power)
+{
+ host->caps &= ~MMC_CAP_NONREMOVABLE;
+ host->caps |= MMC_CAP_NEEDS_POLL;
+ mmc_detect_change(host, delay);
+}
+#endif
+
+#define ESP_DMA_IBUFSZ 2048
+
+struct esp_sdio_ctrl *sif_sctrl;
+
+static int esdio_power_off(struct esp_sdio_ctrl *sctrl);
+static int esdio_power_on(struct esp_sdio_ctrl *sctrl);
+
+void sif_set_clock(struct sdio_func *func, int clk);
+
+void sif_lock_bus(struct esp_pub *epub)
+{
+ if (!epub || !epub->sif || !epub->sif->func)
+ return;
+ sdio_claim_host(epub->sif->func);
+}
+
+void sif_unlock_bus(struct esp_pub *epub)
+{
+ if (!epub || !epub->sif || !epub->sif->func)
+ return;
+ sdio_release_host(epub->sif->func);
+}
+
+static inline bool bad_buf(u8 *buf)
+{
+ return (unsigned long)buf & 0x3 || !virt_addr_valid(buf);
+}
+
+u8 sdio_io_readb(struct esp_pub *epub, int addr, int *res)
+{
+ struct sdio_func *func = epub->sif->func;
+
+ if (!func->num)
+ return sdio_f0_readb(func, addr, res);
+
+ return sdio_readb(func, addr, res);
+}
+
+void sdio_io_writeb(struct esp_pub *epub, u8 value, int addr, int *res)
+{
+ struct sdio_func *func = epub->sif->func;
+
+ if (!func->num)
+ sdio_f0_writeb(func, value, addr, res);
+ else
+ sdio_writeb(func, value, addr, res);
+}
+
+int sif_io(struct esp_pub *epub, u32 addr, u8 *buf, u32 len, u32 flag,
+ bool sync)
+{
+ struct sdio_func *func;
+ int err = 0;
+ u8 *ibuf;
+ bool need_ibuf = false;
+
+ if (!epub || !buf || !epub->sif || !epub->sif->func) {
+ ESSERT(0);
+ return -EINVAL;
+ }
+
+ func = epub->sif->func;
+
+ if (bad_buf(buf)) {
+ need_ibuf = true;
+ ibuf = epub->sif->dma_buffer;
+ } else {
+ ibuf = buf;
+ }
+
+ if (flag & SIF_TO_DEVICE) {
+ if (need_ibuf)
+ memcpy(ibuf, buf, len);
+
+ if (sync)
+ sdio_claim_host(func);
+
+ if (flag & SIF_FIXED_ADDR)
+ err = sdio_writesb(func, addr, ibuf, len);
+ else if (flag & SIF_INC_ADDR)
+ err = sdio_memcpy_toio(func, addr, ibuf, len);
+
+ if (sync)
+ sdio_release_host(func);
+
+ } else if (flag & SIF_FROM_DEVICE) {
+ if (sync)
+ sdio_claim_host(func);
+
+ if (flag & SIF_FIXED_ADDR)
+ err = sdio_readsb(func, ibuf, addr, len);
+ else if (flag & SIF_INC_ADDR)
+ err = sdio_memcpy_fromio(func, ibuf, addr, len);
+
+ if (sync)
+ sdio_release_host(func);
+
+ if (!err && need_ibuf)
+ memcpy(buf, ibuf, len);
+ }
+
+ return err;
+}
+
+int sif_io_raw(struct esp_pub *epub, u32 addr, u8 *buf, u32 len, u32 flag)
+{
+ return sif_io(epub, addr, buf, len, flag, false);
+}
+
+int sif_io_sync(struct esp_pub *epub, u32 addr, u8 *buf, u32 len, u32 flag)
+{
+ return sif_io(epub, addr, buf, len, flag, true);
+}
+
+int sif_lldesc(struct esp_pub *epub, u8 *buf, u32 len, bool sync, bool read,
+ bool noround)
+{
+ u32 read_len, addr, flags;
+
+ if (!epub || !buf || !epub->sif || !epub->sif->target_id) {
+ ESSERT(0);
+ return -EINVAL;
+ }
+
+ if (epub->sif->target_id == 0x600 && !noround)
+ read_len = roundup(len, epub->sif->slc_blk_sz);
+ else
+ read_len = len;
+
+ if (read) {
+ addr = epub->sif->slc_window_end_addr - 2 - len;
+ flags = SIF_FROM_DEVICE | SIF_BYTE_BASIS | SIF_INC_ADDR;
+ } else {
+ addr = epub->sif->slc_window_end_addr - len;
+ flags = SIF_TO_DEVICE | SIF_BYTE_BASIS | SIF_INC_ADDR;
+ }
+
+ if (sync)
+ return sif_io_sync(epub, addr, buf, read_len, flags);
+ return sif_io_raw(epub, addr, buf, read_len, flags);
+}
+
+int sif_lldesc_read_sync(struct esp_pub *epub, u8 *buf, u32 len)
+{
+ return sif_lldesc(epub, buf, len, true, true, false);
+}
+
+int sif_lldesc_write_sync(struct esp_pub *epub, u8 *buf, u32 len)
+{
+ return sif_lldesc(epub, buf, len, true, false, false);
+}
+
+int sif_lldesc_read_raw(struct esp_pub *epub, u8 *buf, u32 len, bool noround)
+{
+ return sif_lldesc(epub, buf, len, false, true, noround);
+}
+
+int sif_lldesc_write_raw(struct esp_pub *epub, u8 *buf, u32 len)
+{
+ return sif_lldesc(epub, buf, len, false, false, false);
+}
+
+#define MANUFACTURER_ID_EAGLE_BASE 0x1110
+#define MANUFACTURER_CODE 0x6666
+
+static const struct sdio_device_id esp_sdio_devices[] = {
+ {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_EAGLE_BASE | 0x1))},
+ {},
+};
+
+static int esdio_power(struct esp_sdio_ctrl *sctrl, bool power_on)
+{
+ int err;
+
+ if (sctrl->off != power_on)
+ return 0;
+
+ sdio_claim_host(sctrl->func);
+
+ if (power_on)
+ err = sdio_enable_func(sctrl->func);
+ else
+ err = sdio_disable_func(sctrl->func);
+
+ sdio_release_host(sctrl->func);
+
+ if (err) {
+ dev_err(sctrl->epub->dev, "unable to enable sdio func: %d\n",
+ err);
+ return err;
+ }
+
+ /* ensure device is up */
+ if (power_on)
+ msleep(5);
+
+ sctrl->off = !sctrl->off;
+
+ return err;
+}
+
+static int esdio_power_on(struct esp_sdio_ctrl *sctrl)
+{
+ return esdio_power(sctrl, true);
+}
+
+static int esdio_power_off(struct esp_sdio_ctrl *sctrl)
+{
+ return esdio_power(sctrl, false);
+}
+
+void sif_enable_irq(struct esp_pub *epub)
+{
+ sdio_claim_host(epub->sif->func);
+
+ if (sdio_claim_irq(epub->sif->func, sif_dsr))
+ dev_err(epub->dev, "sif %s failed\n", __func__);
+
+ atomic_set(&epub->sip->state, SIP_BOOT);
+ atomic_set(&epub->sif->irq_installed, 1);
+
+ sdio_release_host(epub->sif->func);
+}
+
+void sif_disable_irq(struct esp_pub *epub)
+{
+ int i = 0;
+
+ if (!atomic_read(&epub->sif->irq_installed))
+ return;
+
+ sdio_claim_host(epub->sif->func);
+
+ while (atomic_read(&epub->sif->irq_handling)) {
+ sdio_release_host(epub->sif->func);
+ schedule_timeout(HZ / 100);
+ sdio_claim_host(epub->sif->func);
+ if (i++ >= 400)
+ break;
+ }
+
+ /* Ignore errors, we don't always use an irq. */
+ sdio_release_irq(epub->sif->func);
+
+ atomic_set(&epub->sif->irq_installed, 0);
+ sdio_release_host(epub->sif->func);
+}
+
+void sif_set_clock(struct sdio_func *func, int clk)
+{
+ struct mmc_host *host = func->card->host;
+
+ sdio_claim_host(func);
+
+ //currently only set clock
+ host->ios.clock = clk * 1000000;
+
+ if (host->ios.clock > host->f_max)
+ host->ios.clock = host->f_max;
+
+ host->ops->set_ios(host, &host->ios);
+
+ mdelay(2);
+
+ sdio_release_host(func);
+}
+
+static void esp_sdio_remove(struct sdio_func *func);
+
+static int esp_sdio_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ struct esp_pub *epub;
+ struct esp_sdio_ctrl *sctrl;
+ struct mmc_host *host = func->card->host;
+ int err;
+
+ if (!sif_sctrl) {
+ request_init_conf(&func->dev);
+
+ sctrl = kzalloc(sizeof(*sctrl), GFP_KERNEL);
+ if (!sctrl)
+ return -ENOMEM;
+
+ /* temp buffer reserved for un-dma-able request */
+ sctrl->dma_buffer = kzalloc(ESP_DMA_IBUFSZ, GFP_KERNEL);
+ if (!sctrl->dma_buffer) {
+ err = -ENOMEM;
+ goto _err_last;
+ }
+
+ sif_sctrl = sctrl;
+ sctrl->slc_blk_sz = SIF_SLC_BLOCK_SIZE;
+
+ epub = esp_pub_alloc_mac80211(&func->dev);
+ if (!epub) {
+ err = -ENOMEM;
+ goto _err_dma;
+ }
+
+ epub->sif = sctrl;
+ epub->sdio_state = ESP_SDIO_STATE_FIRST_INIT;
+ sctrl->epub = epub;
+ } else {
+ /* FIXME: why duplicating sctrl and sif_sctrl? */
+ sctrl = sif_sctrl;
+ sif_sctrl = NULL;
+ epub = sctrl->epub;
+ epub->sdio_state = ESP_SDIO_STATE_SECOND_INIT;
+ /* FIXME: already done in esp_pub_alloc_mac80211 which is called on first probe */
+ SET_IEEE80211_DEV(epub->hw, &func->dev);
+ epub->dev = &func->dev;
+ }
+
+ sctrl->func = func;
+ sdio_set_drvdata(func, sctrl);
+ sctrl->id = id;
+ sctrl->off = true;
+
+ /* give us some time to enable, in ms */
+ func->enable_timeout = 100;
+
+ err = esdio_power_on(sctrl);
+ if (err) {
+ if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT)
+ goto _err_ext_gpio;
+
+ goto _err_second_init;
+ }
+
+ check_target_id(epub);
+
+ sdio_claim_host(func);
+
+ err = sdio_set_block_size(func, sctrl->slc_blk_sz);
+ if (err) {
+ dev_err(epub->dev, "Set sdio block size %d failed: %d)\n",
+ sctrl->slc_blk_sz, err);
+
+ sdio_release_host(func);
+ if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT)
+ goto _err_off;
+
+ goto _err_second_init;
+ }
+
+ sdio_release_host(func);
+
+#ifdef LOWER_CLK
+ /* fix clock for dongle */
+ sif_set_clock(func, 23);
+#endif //LOWER_CLK
+
+ err = esp_pub_init_all(epub);
+ if (err) {
+ dev_err(epub->dev, "esp_init_all failed: %d\n", err);
+ if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) {
+ err = 0;
+ goto _err_first_init;
+ }
+
+ if (epub->sdio_state == ESP_SDIO_STATE_SECOND_INIT)
+ goto _err_second_init;
+ }
+
+ if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) {
+ epub->sdio_state = ESP_SDIO_STATE_FIRST_NORMAL_EXIT;
+ /* Rescan the esp8089 after loading the initial firmware */
+ mmc_force_detect_change(host, msecs_to_jiffies(100), true);
+ }
+
+ return err;
+
+_err_off:
+ esdio_power_off(sctrl);
+
+_err_ext_gpio:
+ esp_pub_dealloc_mac80211(epub);
+
+_err_dma:
+ kfree(sctrl->dma_buffer);
+
+_err_last:
+ kfree(sctrl);
+
+_err_first_init:
+ if (epub && epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT)
+ epub->sdio_state = ESP_SDIO_STATE_FIRST_ERROR_EXIT;
+
+ return err;
+
+_err_second_init:
+ epub->sdio_state = ESP_SDIO_STATE_SECOND_ERROR_EXIT;
+ esp_sdio_remove(func);
+
+ return err;
+}
+
+static void esp_sdio_remove(struct sdio_func *func)
+{
+ struct esp_sdio_ctrl *sctrl = sdio_get_drvdata(func);
+ struct esp_pub *epub;
+
+ if (!sctrl) {
+ return;
+ }
+
+ epub = sctrl->epub;
+ if (!epub) {
+ goto _out;
+ }
+
+ if (epub->sdio_state != ESP_SDIO_STATE_FIRST_NORMAL_EXIT) {
+ if (epub->sip) {
+ sip_detach(epub->sip);
+ epub->sip = NULL;
+ }
+ } else {
+ atomic_set(&epub->sip->state, SIP_STOP);
+ sif_disable_irq(epub);
+ }
+
+ if (epub->sdio_state != ESP_SDIO_STATE_FIRST_NORMAL_EXIT) {
+ esp_pub_dealloc_mac80211(epub);
+
+ kfree(sctrl->dma_buffer);
+ sctrl->dma_buffer = NULL;
+
+ kfree(sctrl);
+ }
+
+_out:
+ sdio_set_drvdata(func, NULL);
+}
+
+MODULE_DEVICE_TABLE(sdio, esp_sdio_devices);
+
+static int esp_sdio_suspend(struct device *dev)
+{
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ struct esp_sdio_ctrl *sctrl = sdio_get_drvdata(func);
+ struct esp_pub *epub = sctrl->epub;
+ u32 sdio_flags;
+
+ printk("%s", __func__);
+ atomic_set(&epub->ps.state, ESP_PM_ON);
+
+ sdio_flags = sdio_get_host_pm_caps(func);
+
+ if (!(sdio_flags & MMC_PM_KEEP_POWER))
+ printk("%s can't keep power while host is suspended\n",
+ __func__);
+
+ /* keep power while host suspended */
+ if (sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER))
+ printk("%s error while trying to keep power\n", __func__);
+
+ return 0;
+}
+
+static int esp_sdio_resume(struct device *dev)
+{
+ return 0;
+}
+
+static const struct dev_pm_ops esp_sdio_pm_ops = {
+ .suspend = esp_sdio_suspend,
+ .resume = esp_sdio_resume,
+};
+
+static struct sdio_driver esp_sdio_driver = {
+ .name = "eagle_sdio",
+ .id_table = esp_sdio_devices,
+ .probe = esp_sdio_probe,
+ .remove = esp_sdio_remove,
+ .drv = {.pm = &esp_sdio_pm_ops,},
+};
+
+static int /*__init*/ esp_sdio_init(void)
+{
+ esp_debugfs_init();
+ sdio_register_driver(&esp_sdio_driver);
+
+ return 0;
+}
+
+static void /*__exit*/ esp_sdio_exit(void)
+{
+ sdio_unregister_driver(&esp_sdio_driver);
+ esp_debugfs_exit();
+}
+
+module_init(esp_sdio_init);
+module_exit(esp_sdio_exit);
+
+MODULE_AUTHOR("Espressif System");
+MODULE_DESCRIPTION
+("Driver for SDIO interconnected eagle low-power WLAN devices");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/esp8089/sip2_common.h b/drivers/staging/esp8089/sip2_common.h
new file mode 100644
index 000000000000..5dfb8c492223
--- /dev/null
+++ b/drivers/staging/esp8089/sip2_common.h
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2010 - 2014 Espressif System.
+ * Copyright (c) 2017 Free Electrons
+ * Quentin Schulz <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ */
+
+#ifndef _SIP2_COMMON_H
+#define _SIP2_COMMON_H
+
+/* max 16 types */
+enum sip_type {
+ SIP_CTRL = 0,
+ SIP_DATA,
+ SIP_DATA_AMPDU
+};
+
+enum sip_buf_type {
+ SIP_TX_CTRL_BUF = 0, /* from host */
+ SIP_RX_CTRL_BUF, /* to host */
+ SIP_TX_DATA_BUF, /* from host */
+ SIP_RX_DATA_BUF /* to host */
+};
+
+enum sip_cmd_id {
+ SIP_CMD_GET_VER = 0,
+ SIP_CMD_WRITE_MEMORY, //1 ROM code
+ SIP_CMD_READ_MEMORY, //2
+ SIP_CMD_WRITE_REG, //3 ROM code
+ SIP_CMD_READ_REG, //4
+ SIP_CMD_BOOTUP, //5 ROM code
+ SIP_CMD_COPYBACK, //6
+ SIP_CMD_INIT, //7
+ SIP_CMD_SCAN, //8
+ SIP_CMD_SETKEY, //9
+ SIP_CMD_CONFIG, //10
+ SIP_CMD_BSS_INFO_UPDATE, //11
+ SIP_CMD_LOOPBACK, //12 ROM code
+ //do not add cmd before this line
+ SIP_CMD_SET_WMM_PARAM,
+ SIP_CMD_AMPDU_ACTION,
+ SIP_CMD_HB_REQ, //15
+ SIP_CMD_RESET_MAC, //16
+ SIP_CMD_PRE_DOWN, //17
+ SIP_CMD_SLEEP, /* for sleep testing */
+ SIP_CMD_WAKEUP, /* for sleep testing */
+ SIP_CMD_DEBUG, /* for general testing */
+ SIP_CMD_GET_FW_VER, /* get fw rev. */
+ SIP_CMD_SETVIF,
+ SIP_CMD_SETSTA,
+ SIP_CMD_PS,
+ SIP_CMD_ATE,
+ SIP_CMD_SUSPEND,
+ SIP_CMD_RECALC_CREDIT,
+ SIP_CMD_MAX,
+};
+
+enum {
+ SIP_EVT_TARGET_ON = 0, //
+ SIP_EVT_BOOTUP, //1 in ROM code
+ SIP_EVT_COPYBACK, //2
+ SIP_EVT_SCAN_RESULT, //3
+ SIP_EVT_TX_STATUS, //4
+ SIP_EVT_CREDIT_RPT, //5, in ROM code
+ SIP_EVT_ERROR, //6
+ SIP_EVT_LOOPBACK, //7, in ROM code
+ SIP_EVT_SNPRINTF_TO_HOST, //8 in ROM code
+ //do not add evt before this line
+ SIP_EVT_HB_ACK, //9
+ SIP_EVT_RESET_MAC_ACK, //10
+ SIP_EVT_WAKEUP, //11 /* for sleep testing */
+ SIP_EVT_DEBUG, //12 /* for general testing */
+ SIP_EVT_PRINT_TO_HOST, //13
+ SIP_EVT_TRC_AMPDU, //14
+ SIP_EVT_ROC, //15
+ SIP_EVT_RESETTING,
+ SIP_EVT_ATE,
+ SIP_EVT_EP,
+ SIP_EVT_INIT_EP,
+ SIP_EVT_SLEEP,
+ SIP_EVT_TXIDLE,
+ SIP_EVT_NOISEFLOOR,
+ SIP_EVT_MAX
+};
+
+#define SIP_IFIDX_MASK 0xf0
+#define SIP_IFIDX_S 4
+#define SIP_TYPE_MASK 0x0f
+#define SIP_TYPE_S 0
+
+#define SIP_HDR_GET_IFIDX(fc0) (((fc0) & SIP_IFIDX_MASK) >> SIP_IFIDX_S)
+#define SIP_HDR_SET_IFIDX(fc0, ifidx) ((fc0) = ((fc0) & ~SIP_IFIDX_MASK) | ((ifidx) << SIP_IFIDX_S & SIP_IFIDX_MASK))
+#define SIP_HDR_GET_TYPE(fc0) ((fc0) & SIP_TYPE_MASK)
+/* assume type field is cleared */
+#define SIP_HDR_SET_TYPE(fc0, type) ((fc0) = ((fc0) & ~SIP_TYPE_MASK) | ((type) & SIP_TYPE_MASK))
+
+/* sip 2.0, not hybrid header so far */
+#define SIP_HDR_IS_CTRL(hdr) (SIP_HDR_GET_TYPE((hdr)->fc[0]) == SIP_CTRL)
+#define SIP_HDR_IS_DATA(hdr) (SIP_HDR_GET_TYPE((hdr)->fc[0]) == SIP_DATA)
+#define SIP_HDR_IS_AMPDU(hdr) (SIP_HDR_GET_TYPE((hdr)->fc[0]) == SIP_DATA_AMPDU)
+
+/* fc[1] flags, only for data pkt. Ctrl pkts use fc[1] as eventID */
+#define SIP_HDR_SET_FLAGS(hdr, flags) ((hdr)->fc[1] |= (flags))
+#define SIP_HDR_F_MORE_PKT 0x1
+#define SIP_HDR_F_NEED_CRDT_RPT 0x2
+#define SIP_HDR_F_SYNC 0x4
+#define SIP_HDR_F_SYNC_RESET 0x8
+#define SIP_HDR_F_PM_TURNING_ON 0x10
+#define SIP_HDR_F_PM_TURNING_OFF 0x20
+
+#define SIP_HDR_NEED_CREDIT_UPDATE(hdr) ((hdr)->fc[1] & SIP_HDR_F_NEED_CRDT_RPT)
+#define SIP_HDR_IS_MORE_PKT(hdr) ((hdr)->fc[1] & SIP_HDR_F_MORE_PKT)
+#define SIP_HDR_IS_CRDT_RPT(hdr) ((hdr)->fc[1] & SIP_HDR_F_CRDT_RPT)
+#define SIP_HDR_IS_SYNC(hdr) ((hdr)->fc[1] & SIP_HDR_F_SYNC)
+#define SIP_HDR_IS_SYNC_RESET(hdr) ((hdr)->fc[1] & SIP_HDR_F_SYNC_RESET)
+#define SIP_HDR_IS_SYNC_PKT(hdr) (SIP_HDR_IS_SYNC(hdr) | SIP_HDR_IS_SYNC_RESET(hdr))
+#define SIP_HDR_SET_SYNC(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_SYNC)
+#define SIP_HDR_SET_SYNC_RESET(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_SYNC_RESET)
+#define SIP_HDR_SET_MORE_PKT(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_MORE_PKT)
+#define SIP_HDR_SET_PM_TURNING_ON(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_PM_TURNING_ON)
+#define SIP_HDR_IS_PM_TURNING_ON(hdr) ((hdr)->fc[1] & SIP_HDR_F_PM_TURNING_ON)
+#define SIP_HDR_SET_PM_TURNING_OFF(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_PM_TURNING_OFF)
+#define SIP_HDR_IS_PM_TURNING_OFF(hdr) ((hdr)->fc[1] & SIP_HDR_F_PM_TURNING_OFF)
+
+/* fc[0]: first 4bit: ifidx; last 4bit: type
+ * fc[1]: flags
+ *
+ * Don't touch the header definitons
+ */
+struct sip_hdr_min {
+ u8 fc[2];
+ __le16 len;
+} __packed;
+
+/* not more than 4byte long */
+struct sip_tx_data_info {
+ u8 tid;
+ u8 ac;
+ u8 p2p:1, enc_flag:7;
+ u8 hw_kid;
+} __packed;
+
+/* NB: this structure should be not more than 4byte !! */
+struct sip_tx_info {
+ union {
+ u32 cmdid;
+ struct sip_tx_data_info dinfo;
+ } u;
+} __packed;
+
+struct sip_hdr {
+ u8 fc[2]; //fc[0]: type and ifidx ; fc[1] is eventID if the first ctrl pkt in the chain. data pkt still can use fc[1] to set flag
+ __le16 len;
+ union {
+ volatile u32 recycled_credits; /* last 12bits is credits, first 20 bits is actual length of the first pkt in the chain */
+ struct sip_tx_info tx_info;
+ } u;
+ u32 seq;
+} __packed;
+
+#define h_credits u.recycled_credits
+#define c_evtid fc[1]
+#define c_cmdid u.tx_info.u.cmdid
+#define d_ac u.tx_info.u.dinfo.ac
+#define d_tid u.tx_info.u.dinfo.tid
+#define d_p2p u.tx_info.u.dinfo.p2p
+#define d_enc_flag u.tx_info.u.dinfo.enc_flag
+#define d_hw_kid u.tx_info.u.dinfo.hw_kid
+
+#define SIP_CREDITS_MASK 0xfff /* last 12 bits */
+
+#define SIP_HDR_MIN_LEN 4
+#define SIP_HDR_LEN sizeof(struct sip_hdr)
+#define SIP_CTRL_HDR_LEN SIP_HDR_LEN /* same as sip_hdr in sip2 design */
+#define SIP_BOOT_BUF_SIZE 256
+#define SIP_CTRL_BUF_SZ 256 /* too much?? */
+#define SIP_CTRL_BUF_N 6
+#define SIP_CTRL_TXBUF_N 2
+#define SIP_CTRL_RXBUF_N 4
+
+/* WAR for mblk */
+#define SIP_RX_ADDR_PREFIX_MASK 0xfc000000
+#define SIP_RX_ADDR_SHIFT 6 /* [31:5], shift 6 bits */
+
+struct sip_cmd_write_memory {
+ u32 addr;
+ u32 len;
+} __packed;
+
+struct sip_cmd_read_memory {
+ u32 addr;
+ u32 len;
+} __packed;
+
+struct sip_cmd_write_reg {
+ u32 addr;
+ u32 val;
+} __packed;
+
+struct sip_cmd_bootup {
+ u32 boot_addr;
+} __packed;
+
+struct sip_cmd_loopback {
+ u32 txlen; //host to target packet len, 0 means no txpacket
+ u32 rxlen; //target to host packet len, 0 means no rxpacket
+ u32 pack_id; //sequence of packet
+} __packed;
+
+struct sip_evt_loopback {
+ u32 txlen; //host to target packet len, 0 means no txpacket
+ u32 rxlen; //target to host packet len, 0 means no rxpacket
+ u32 pack_id; //sequence of packet
+} __packed;
+
+struct sip_cmd_copyback {
+ u32 addr;
+ u32 len;
+} __packed;
+
+struct sip_cmd_scan {
+ // u8 ssid[32];
+ u8 ssid_len;
+ // u8 hw_channel[14];
+ u8 n_channels;
+ u8 ie_len;
+ u8 aborted;
+} __packed; // ie[] append at the end
+
+struct sip_cmd_setkey {
+ u8 bssid_no;
+ u8 addr[ETH_ALEN];
+ u8 alg;
+ u8 keyidx;
+ u8 hw_key_idx;
+ u8 flags;
+ u8 keylen;
+ u8 key[32];
+} __packed;
+
+struct sip_cmd_config {
+ u16 center_freq;
+ u16 duration;
+} __packed;
+
+struct sip_cmd_bss_info_update {
+ u8 bssid[ETH_ALEN];
+ u16 isassoc;
+ u32 beacon_int;
+ u8 bssid_no;
+} __packed;
+
+struct sip_evt_bootup {
+ u16 tx_blksz;
+ u8 mac_addr[ETH_ALEN];
+ /* anything else ? */
+} __packed;
+
+struct sip_cmd_setvif {
+ u8 index;
+ u8 mac[ETH_ALEN];
+ u8 set;
+ u8 op_mode;
+ u8 is_p2p;
+} __packed;
+
+enum esp_ieee80211_phytype {
+ ESP_IEEE80211_T_CCK = 0,
+ ESP_IEEE80211_T_OFDM = 1,
+ ESP_IEEE80211_T_HT20_L = 2,
+ ESP_IEEE80211_T_HT20_S = 3,
+};
+
+struct sip_cmd_setsta {
+ u8 ifidx;
+ u8 index;
+ u8 set;
+ u8 phymode;
+ u8 mac[ETH_ALEN];
+ u16 aid;
+ u8 ampdu_factor;
+ u8 ampdu_density;
+ u16 resv;
+} __packed;
+
+struct sip_cmd_ps {
+ u8 dtim_period;
+ u8 max_sleep_period;
+ u8 on;
+ u8 resv;
+} __packed;
+
+struct sip_cmd_suspend {
+ u8 suspend;
+ u8 resv[3];
+} __packed;
+
+#define SIP_DUMP_RPBM_ERR BIT(0)
+#define SIP_RXABORT_FIXED BIT(1)
+#define SIP_SUPPORT_BGSCAN BIT(2)
+struct sip_evt_bootup2 {
+ u16 tx_blksz;
+ u8 mac_addr[ETH_ALEN];
+ u16 rx_blksz;
+ u8 credit_to_reserve;
+ u8 options;
+ s16 noise_floor;
+ u8 resv[2];
+ /* anything else ? */
+} __packed;
+
+enum trc_ampdu_state {
+ TRC_TX_AMPDU_STOPPED = 1,
+ TRC_TX_AMPDU_OPERATIONAL,
+ TRC_TX_AMPDU_WAIT_STOP,
+ TRC_TX_AMPDU_WAIT_OPERATIONAL,
+ TRC_TX_AMPDU_START,
+};
+
+struct sip_evt_trc_ampdu {
+ u8 state;
+ u8 tid;
+ u8 addr[ETH_ALEN];
+} __packed;
+
+struct sip_cmd_set_wmm_params {
+ u8 aci;
+ u8 aifs;
+ u8 ecw_min;
+ u8 ecw_max;
+ u16 txop_us;
+} __packed;
+
+#define SIP_AMPDU_RX_START 0
+#define SIP_AMPDU_RX_STOP 1
+#define SIP_AMPDU_TX_OPERATIONAL 2
+#define SIP_AMPDU_TX_STOP 3
+
+struct sip_cmd_ampdu_action {
+ u8 action;
+ u8 index;
+ u8 tid;
+ u8 win_size;
+ u16 ssn;
+ u8 addr[ETH_ALEN];
+} __packed;
+
+#define SIP_TX_ST_OK 0
+#define SIP_TX_ST_NOEB 1
+#define SIP_TX_ST_ACKTO 2
+#define SIP_TX_ST_ENCERR 3
+
+//NB: sip_tx_status must be 4 bytes aligned
+struct sip_tx_status {
+ u32 sip_seq;
+ u8 errno; /* success or failure code */
+ u8 rate_index;
+ char ack_signal;
+ u8 pad;
+} __packed;
+
+struct sip_evt_scan_report {
+ u16 scan_id;
+ u16 aborted;
+} __packed;
+
+struct sip_evt_roc {
+ u16 state; //start:1, end :0
+ u16 is_ok;
+} __packed;
+
+struct sip_evt_txidle {
+ u32 last_seq;
+} __packed;
+
+struct sip_evt_noisefloor {
+ s16 noise_floor;
+ u16 pad;
+} __packed;
+/* for mblk direct memory access, no need for sip_hdr. tx: first 2k for contrl msg,
+ * rest of 14k for data. rx, same.
+ */
+
+#endif /* _SIP_COMMON_H_ */
diff --git a/drivers/staging/esp8089/slc_host_register.h b/drivers/staging/esp8089/slc_host_register.h
new file mode 100644
index 000000000000..8cf139c0d7bc
--- /dev/null
+++ b/drivers/staging/esp8089/slc_host_register.h
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2011 Espressif System.
+ * Copyright (c) 2017 Free Electrons
+ * Quentin Schulz <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ */
+
+#ifndef SLC_HOST_REGISTER_H_INCLUDED
+#define SLC_HOST_REGISTER_H_INCLUDED
+
+/* #define REG_SLC_HOST_BASE 0x00000000 */
+/* skip the token1, since reading it will clean the credit */
+#define REG_SLC_HOST_BASE 0x00000000
+#define REG_SLC_BASE 0x00000000
+
+#define SLC_HOST_PF (REG_SLC_HOST_BASE + 0x0)
+#define SLC_HOST_TOKEN_RDATA (REG_SLC_HOST_BASE + 0x4)
+#define SLC_HOST_RX_PF_EOF 0x0000000F
+#define SLC_HOST_RX_PF_EOF_S 28
+#define SLC_HOST_TOKEN1 0x00000FFF
+#define SLC_HOST_TOKEN1_S 16
+#define SLC_HOST_RX_PF_VALID BIT(15)
+#define SLC_HOST_TOKEN0 0x00000FFF
+#define SLC_HOST_TOKEN0_S 0
+
+#define SLC_HOST_TOKEN0_MASK SLC_HOST_TOKEN0
+#define SLC_HOST_INT_RAW (REG_SLC_HOST_BASE + 0x8)
+#define SLC_HOST_EXT_BIT3_INT_RAW BIT(22)
+#define SLC_HOST_EXT_BIT2_INT_RAW BIT(21)
+#define SLC_HOST_EXT_BIT1_INT_RAW BIT(20)
+#define SLC_HOST_RXFIFO_NOT_EMPTY_INT_RAW BIT(19)
+#define SLC_HOST_RX_PF_VALID_INT_RAW BIT(18)
+#define SLC_HOST_TX_OVF_INT_RAW BIT(17)
+#define SLC_HOST_RX_UDF_INT_RAW BIT(16)
+#define SLC_HOST_TX_START_INT_RAW BIT(15)
+#define SLC_HOST_RX_START_INT_RAW BIT(14)
+#define SLC_HOST_RX_EOF_INT_RAW BIT(13)
+#define SLC_HOST_RX_SOF_INT_RAW BIT(12)
+#define SLC_HOST_TOKEN1_0TO1_INT_RAW BIT(11)
+#define SLC_HOST_TOKEN0_0TO1_INT_RAW BIT(10)
+#define SLC_HOST_TOKEN1_1TO0_INT_RAW BIT(9)
+#define SLC_HOST_TOKEN0_1TO0_INT_RAW BIT(8)
+#define SLC_HOST_TOHOST_BIT7_INT_RAW BIT(7)
+#define SLC_HOST_TOHOST_BIT6_INT_RAW BIT(6)
+#define SLC_HOST_TOHOST_BIT5_INT_RAW BIT(5)
+#define SLC_HOST_TOHOST_BIT4_INT_RAW BIT(4)
+#define SLC_HOST_TOHOST_BIT3_INT_RAW BIT(3)
+#define SLC_HOST_TOHOST_BIT2_INT_RAW BIT(2)
+#define SLC_HOST_TOHOST_BIT1_INT_RAW BIT(1)
+#define SLC_HOST_TOHOST_BIT0_INT_RAW BIT(0)
+
+#define SLC_HOST_STATE_W0 (REG_SLC_HOST_BASE + 0xC)
+#define SLC_HOST_STATE3 0x000000FF
+#define SLC_HOST_STATE3_S 24
+#define SLC_HOST_STATE2 0x000000FF
+#define SLC_HOST_STATE2_S 16
+#define SLC_HOST_STATE1 0x000000FF
+#define SLC_HOST_STATE1_S 8
+#define SLC_HOST_STATE0 0x000000FF
+#define SLC_HOST_STATE0_S 0
+
+#define SLC_HOST_STATE_W1 (REG_SLC_HOST_BASE + 0x10)
+#define SLC_HOST_STATE7 0x000000FF
+#define SLC_HOST_STATE7_S 24
+#define SLC_HOST_STATE6 0x000000FF
+#define SLC_HOST_STATE6_S 16
+#define SLC_HOST_STATE5 0x000000FF
+#define SLC_HOST_STATE5_S 8
+#define SLC_HOST_STATE4 0x000000FF
+#define SLC_HOST_STATE4_S 0
+
+#define SLC_HOST_CONF_W0 (REG_SLC_HOST_BASE + 0x14)
+#define SLC_HOST_CONF3 0x000000FF
+#define SLC_HOST_CONF3_S 24
+#define SLC_HOST_CONF2 0x000000FF
+#define SLC_HOST_CONF2_S 16
+#define SLC_HOST_CONF1 0x000000FF
+#define SLC_HOST_CONF1_S 8
+#define SLC_HOST_CONF0 0x000000FF
+#define SLC_HOST_CONF0_S 0
+
+#define SLC_HOST_CONF_W1 (REG_SLC_HOST_BASE + 0x18)
+#define SLC_HOST_CONF7 0x000000FF
+#define SLC_HOST_CONF7_S 24
+#define SLC_HOST_CONF6 0x000000FF
+#define SLC_HOST_CONF6_S 16
+#define SLC_HOST_CONF5 0x000000FF
+#define SLC_HOST_CONF5_S 8
+#define SLC_HOST_CONF4 0x000000FF
+#define SLC_HOST_CONF4_S 0
+
+#define SLC_HOST_INT_ST (REG_SLC_HOST_BASE + 0x1C)
+#define SLC_HOST_RX_ST BIT(23)
+#define SLC_HOST_EXT_BIT3_INT_ST BIT(22)
+#define SLC_HOST_EXT_BIT2_INT_ST BIT(21)
+#define SLC_HOST_EXT_BIT1_INT_ST BIT(20)
+#define SLC_HOST_RXFIFO_NOT_EMPTY_INT_ST BIT(19)
+#define SLC_HOST_RX_PF_VALID_INT_ST BIT(18)
+#define SLC_HOST_TX_OVF_INT_ST BIT(17)
+#define SLC_HOST_RX_UDF_INT_ST BIT(16)
+#define SLC_HOST_TX_START_INT_ST BIT(15)
+#define SLC_HOST_RX_START_INT_ST BIT(14)
+#define SLC_HOST_RX_EOF_INT_ST BIT(13)
+#define SLC_HOST_RX_SOF_INT_ST BIT(12)
+#define SLC_HOST_TOKEN1_0TO1_INT_ST BIT(11)
+#define SLC_HOST_TOKEN0_0TO1_INT_ST BIT(10)
+#define SLC_HOST_TOKEN1_1TO0_INT_ST BIT(9)
+#define SLC_HOST_TOKEN0_1TO0_INT_ST BIT(8)
+#define SLC_HOST_TOHOST_BIT7_INT_ST BIT(7)
+#define SLC_HOST_TOHOST_BIT6_INT_ST BIT(6)
+#define SLC_HOST_TOHOST_BIT5_INT_ST BIT(5)
+#define SLC_HOST_TOHOST_BIT4_INT_ST BIT(4)
+#define SLC_HOST_TOHOST_BIT3_INT_ST BIT(3)
+#define SLC_HOST_TOHOST_BIT2_INT_ST BIT(2)
+#define SLC_HOST_TOHOST_BIT1_INT_ST BIT(1)
+#define SLC_HOST_TOHOST_BIT0_INT_ST BIT(0)
+
+#define SLC_HOST_CONF_W2 (REG_SLC_HOST_BASE + 0x20)
+#define SLC_HOST_CONF11 0x000000FF
+#define SLC_HOST_CONF11_S 24
+#define SLC_HOST_CONF10 0x000000FF
+#define SLC_HOST_CONF10_S 16
+#define SLC_HOST_CONF9 0x000000FF
+#define SLC_HOST_CONF9_S 8
+#define SLC_HOST_CONF8 0x000000FF
+#define SLC_HOST_CONF8_S 0
+
+#define SLC_HOST_CONF_W3 (REG_SLC_HOST_BASE + 0x24)
+#define SLC_HOST_CONF15 0x000000FF
+#define SLC_HOST_CONF15_S 24
+#define SLC_HOST_CONF14 0x000000FF
+#define SLC_HOST_CONF14_S 16
+#define SLC_HOST_CONF13 0x000000FF
+#define SLC_HOST_CONF13_S 8
+#define SLC_HOST_CONF12 0x000000FF
+#define SLC_HOST_CONF12_S 0
+
+#define SLC_HOST_GEN_TXDONE_INT BIT(16)
+#define SLC_HOST_GEN_RXDONE_INT BIT(17)
+
+#define SLC_HOST_CONF_W4 (REG_SLC_HOST_BASE + 0x28)
+#define SLC_HOST_CONF19 0x000000FF
+#define SLC_HOST_CONF19_S 24
+#define SLC_HOST_CONF18 0x000000FF
+#define SLC_HOST_CONF18_S 16
+#define SLC_HOST_CONF17 0x000000FF
+#define SLC_HOST_CONF17_S 8
+#define SLC_HOST_CONF16 0x000000FF
+#define SLC_HOST_CONF16_S 0
+
+#define SLC_HOST_TOKEN_WDATA (REG_SLC_HOST_BASE + 0x2C)
+#define SLC_HOST_TOKEN1_WD 0x00000FFF
+#define SLC_HOST_TOKEN1_WD_S 16
+#define SLC_HOST_TOKEN0_WD 0x00000FFF
+#define SLC_HOST_TOKEN0_WD_S 0
+
+#define SLC_HOST_INT_CLR (REG_SLC_HOST_BASE + 0x30)
+#define SLC_HOST_TOKEN1_WR BIT(31)
+#define SLC_HOST_TOKEN0_WR BIT(30)
+#define SLC_HOST_TOKEN1_DEC BIT(29)
+#define SLC_HOST_TOKEN0_DEC BIT(28)
+#define SLC_HOST_EXT_BIT3_INT_CLR BIT(22)
+#define SLC_HOST_EXT_BIT2_INT_CLR BIT(21)
+#define SLC_HOST_EXT_BIT1_INT_CLR BIT(20)
+#define SLC_HOST_EXT_BIT0_INT_CLR BIT(19)
+#define SLC_HOST_RX_PF_VALID_INT_CLR BIT(18)
+#define SLC_HOST_TX_OVF_INT_CLR BIT(17)
+#define SLC_HOST_RX_UDF_INT_CLR BIT(16)
+#define SLC_HOST_TX_START_INT_CLR BIT(15)
+#define SLC_HOST_RX_START_INT_CLR BIT(14)
+#define SLC_HOST_RX_EOF_INT_CLR BIT(13)
+#define SLC_HOST_RX_SOF_INT_CLR BIT(12)
+#define SLC_HOST_TOKEN1_0TO1_INT_CLR BIT(11)
+#define SLC_HOST_TOKEN0_0TO1_INT_CLR BIT(10)
+#define SLC_HOST_TOKEN1_1TO0_INT_CLR BIT(9)
+#define SLC_HOST_TOKEN0_1TO0_INT_CLR BIT(8)
+#define SLC_HOST_TOHOST_BIT7_INT_CLR BIT(7)
+#define SLC_HOST_TOHOST_BIT6_INT_CLR BIT(6)
+#define SLC_HOST_TOHOST_BIT5_INT_CLR BIT(5)
+#define SLC_HOST_TOHOST_BIT4_INT_CLR BIT(4)
+#define SLC_HOST_TOHOST_BIT3_INT_CLR BIT(3)
+#define SLC_HOST_TOHOST_BIT2_INT_CLR BIT(2)
+#define SLC_HOST_TOHOST_BIT1_INT_CLR BIT(1)
+#define SLC_HOST_TOHOST_BIT0_INT_CLR BIT(0)
+
+#define SLC_HOST_INT_ENA (REG_SLC_HOST_BASE + 0x34)
+#define SLC_HOST_EXT_BIT3_INT_ENA BIT(22)
+#define SLC_HOST_EXT_BIT2_INT_ENA BIT(21)
+#define SLC_HOST_EXT_BIT1_INT_ENA BIT(20)
+#define SLC_HOST_EXT_BIT0_INT_ENA BIT(19)
+#define SLC_HOST_RX_PF_VALID_INT_ENA BIT(18)
+#define SLC_HOST_TX_OVF_INT_ENA BIT(17)
+#define SLC_HOST_RX_UDF_INT_ENA BIT(16)
+#define SLC_HOST_TX_START_INT_ENA BIT(15)
+#define SLC_HOST_RX_START_INT_ENA BIT(14)
+#define SLC_HOST_RX_EOF_INT_ENA BIT(13)
+#define SLC_HOST_RX_SOF_INT_ENA BIT(12)
+#define SLC_HOST_TOKEN1_0TO1_INT_ENA BIT(11)
+#define SLC_HOST_TOKEN0_0TO1_INT_ENA BIT(10)
+#define SLC_HOST_TOKEN1_1TO0_INT_ENA BIT(9)
+#define SLC_HOST_TOKEN0_1TO0_INT_ENA BIT(8)
+#define SLC_HOST_TOHOST_BIT7_INT_ENA BIT(7)
+#define SLC_HOST_TOHOST_BIT6_INT_ENA BIT(6)
+#define SLC_HOST_TOHOST_BIT5_INT_ENA BIT(5)
+#define SLC_HOST_TOHOST_BIT4_INT_ENA BIT(4)
+#define SLC_HOST_TOHOST_BIT3_INT_ENA BIT(3)
+#define SLC_HOST_TOHOST_BIT2_INT_ENA BIT(2)
+#define SLC_HOST_TOHOST_BIT1_INT_ENA BIT(1)
+#define SLC_HOST_TOHOST_BIT0_INT_ENA BIT(0)
+
+#define SLC_HOST_CONF_W5 (REG_SLC_HOST_BASE + 0x3C)
+#define SLC_HOST_CONF23 0x000000FF
+#define SLC_HOST_CONF23_S 24
+#define SLC_HOST_CONF22 0x000000FF
+#define SLC_HOST_CONF22_S 16
+#define SLC_HOST_CONF21 0x000000FF
+#define SLC_HOST_CONF21_S 8
+#define SLC_HOST_CONF20 0x000000FF
+#define SLC_HOST_CONF20_S 0
+
+#define SLC_HOST_WIN_CMD (REG_SLC_HOST_BASE + 0x40)
+
+#define SLC_HOST_DATE (REG_SLC_HOST_BASE + 0x78)
+#define SLC_HOST_ID (REG_SLC_HOST_BASE + 0x7C)
+
+#define SLC_ADDR_WINDOW_CLEAR_MASK (~(0xf << 12))
+#define SLC_FROM_HOST_ADDR_WINDOW BIT(12)
+#define SLC_TO_HOST_ADDR_WINDOW (0x3 << 12)
+
+#define SLC_SET_FROM_HOST_ADDR_WINDOW(v) do { \
+ (v) &= 0xffff; \
+ (v) &= SLC_ADDR_WINDOW_CLEAR_MASK; \
+ (v) |= SLC_FROM_HOST_ADDR_WINDOW; \
+} while (0)
+
+#define SLC_SET_TO_HOST_ADDR_WINDOW(v) do { \
+ (v) &= 0xffff; \
+ (v) &= SLC_ADDR_WINDOW_CLEAR_MASK; \
+ (v) |= SLC_TO_HOST_ADDR_WINDOW; \
+} while (0)
+
+#define SLC_INT_ENA (REG_SLC_BASE + 0xC)
+#define SLC_RX_EOF_INT_ENA BIT(17)
+#define SLC_FRHOST_BIT2_INT_ENA BIT(2)
+
+#define SLC_RX_LINK (REG_SLC_BASE + 0x24)
+#define SLC_RXLINK_START BIT(29)
+
+#define SLC_BRIDGE_CONF (REG_SLC_BASE + 0x44)
+#define SLC_TX_PUSH_IDLE_NUM 0xFFFF
+#define SLC_TX_PUSH_IDLE_NUM_S 16
+#define SLC_HDA_MAP_128K BIT(13)
+#define SLC_TX_DUMMY_MODE BIT(12)
+#define SLC_FIFO_MAP_ENA 0x0000000F
+#define SLC_FIFO_MAP_ENA_S 8
+#define SLC_TXEOF_ENA 0x0000003F
+#define SLC_TXEOF_ENA_S
+
+#endif // SLC_HOST_REGISTER_H_INCLUDED
--
2.11.0
On Fri, Jul 21, 2017 at 04:35:01PM +0200, Quentin Schulz wrote:
> The Espressif ESP8089 WiFi chips can be often found in cheap tablets.
> There is one in A23 Polaroid tablets for example.
>
> The chip is often embedded as an eMMC SDIO device.
>
> The code was taken from an out-of-tree repository and has seen a first
> pass in the cleanup process.
>
> At the moment, there is no publicly available datasheet for this chip.
>
> Cc: Hans de Goede <[email protected]>
> Cc: Icenowy Zheng <[email protected]>
> Signed-off-by: Quentin Schulz <[email protected]>
Staging drivers need a TODO file that lists what has to be done to the
code to get it out of staging. Why not just take a day or so and fix up
the remaining issues and get it into the "real" part of the kernel
correctly?
Also, staging drivers have to be "stand-alone", I can't take stuff that
requires core changes only for one staging driver.
thanks,
greg k-h
Hi Quentin,
> The Espressif ESP8089 WiFi chips can be often found in cheap tablets.
> There is one in A23 Polaroid tablets for example.
>
> The chip is often embedded as an eMMC SDIO device.
>
> The code was taken from an out-of-tree repository and has seen a first
> pass in the cleanup process.
>
> At the moment, there is no publicly available datasheet for this chip.
>
> Cc: Hans de Goede <[email protected]>
> Cc: Icenowy Zheng <[email protected]>
> Signed-off-by: Quentin Schulz <[email protected]>
> ---
> drivers/staging/Kconfig | 2 +
> drivers/staging/Makefile | 1 +
> drivers/staging/esp8089/Kconfig | 13 +
> drivers/staging/esp8089/Makefile | 7 +
> drivers/staging/esp8089/esp_ctrl.c | 527 ++++++++
> drivers/staging/esp8089/esp_ctrl.h | 48 +
> drivers/staging/esp8089/esp_debug.c | 247 ++++
> drivers/staging/esp8089/esp_debug.h | 69 ++
> drivers/staging/esp8089/esp_file.c | 221 ++++
> drivers/staging/esp8089/esp_file.h | 30 +
> drivers/staging/esp8089/esp_init_data.h | 17 +
> drivers/staging/esp8089/esp_io.c | 294 +++++
> drivers/staging/esp8089/esp_mac80211.c | 1496 +++++++++++++++++++++++
> drivers/staging/esp8089/esp_mac80211.h | 33 +
> drivers/staging/esp8089/esp_main.c | 199 ++++
> drivers/staging/esp8089/esp_pub.h | 188 +++
> drivers/staging/esp8089/esp_sif.h | 131 ++
> drivers/staging/esp8089/esp_sip.c | 1718 +++++++++++++++++++++++++++
> drivers/staging/esp8089/esp_sip.h | 150 +++
> drivers/staging/esp8089/esp_utils.c | 133 +++
> drivers/staging/esp8089/esp_utils.h | 27 +
> drivers/staging/esp8089/esp_wl.h | 35 +
> drivers/staging/esp8089/esp_wmac.h | 87 ++
> drivers/staging/esp8089/sdio_sif_esp.c | 552 +++++++++
> drivers/staging/esp8089/sip2_common.h | 388 ++++++
> drivers/staging/esp8089/slc_host_register.h | 263 ++++
> 26 files changed, 6876 insertions(+)
> create mode 100644 drivers/staging/esp8089/Kconfig
> create mode 100644 drivers/staging/esp8089/Makefile
> create mode 100644 drivers/staging/esp8089/esp_ctrl.c
> create mode 100644 drivers/staging/esp8089/esp_ctrl.h
> create mode 100644 drivers/staging/esp8089/esp_debug.c
> create mode 100644 drivers/staging/esp8089/esp_debug.h
> create mode 100644 drivers/staging/esp8089/esp_file.c
> create mode 100644 drivers/staging/esp8089/esp_file.h
> create mode 100644 drivers/staging/esp8089/esp_init_data.h
> create mode 100644 drivers/staging/esp8089/esp_io.c
> create mode 100644 drivers/staging/esp8089/esp_mac80211.c
> create mode 100644 drivers/staging/esp8089/esp_mac80211.h
> create mode 100644 drivers/staging/esp8089/esp_main.c
> create mode 100644 drivers/staging/esp8089/esp_pub.h
> create mode 100644 drivers/staging/esp8089/esp_sif.h
> create mode 100644 drivers/staging/esp8089/esp_sip.c
> create mode 100644 drivers/staging/esp8089/esp_sip.h
> create mode 100644 drivers/staging/esp8089/esp_utils.c
> create mode 100644 drivers/staging/esp8089/esp_utils.h
> create mode 100644 drivers/staging/esp8089/esp_wl.h
> create mode 100644 drivers/staging/esp8089/esp_wmac.h
> create mode 100644 drivers/staging/esp8089/sdio_sif_esp.c
> create mode 100644 drivers/staging/esp8089/sip2_common.h
> create mode 100644 drivers/staging/esp8089/slc_host_register.h
why are you putting this into staging? Is it that bad?
Regards
Marcel
From: Hans de Goede <[email protected]>
Some sdio devices have a multiple stage bring-up process. Specifically
the esp8089 (for which an out of tree driver is available) loads firmware
on the first call to its sdio-drivers' probe function and then resets
the device causing it to reboot from its RAM with the new firmware.
When this sdio device reboots it comes back up in 1 bit 400 KHz mode
again, and we need to walk through the whole ios negatiation and sdio setup
again.
There are 2 problems with this:
1) Typically these devices are soldered onto some (ARM) tablet / SBC
PCB and as such are described in devicetree as "non-removable", which
causes the mmc-core to scan them only once and not poll for the device
dropping of the bus. Normally this is the right thing todo but in the
eso8089 example we need the mmc-core to notice the module has disconnected
(since it is now in 1 bit mode again it will not talk to the host in 4 bit
mode). This can be worked around by using "broken-cd" in devicetree
instead of "non-removable", but that is not a proper fix since the device
really is non-removable.
2) When the mmc-core detects the device has disconnected it will poweroff
the device, causing the RAM loaded firmware to be lost. This can be worked
around in devicetree by using regulator-always-on (and avoiding the use of
mmc-pwrseq), but again that is more of a hack then a proper fix.
This commmit fixes 1) by adding a mmc_force_detect_change function which
will cause scanning for device removal / insertion until a new device is
detected. 2) Is fixed by a keep_power flag to the mmc_force_detect_change
function which when set causes the mmc-core to keep the power to the device
on during the rescan.
Cc: Icenowy Zheng <[email protected]>
Cc: Maxime Ripard <[email protected]>
Cc: Chen-Yu Tsai <[email protected]>
Signed-off-by: Hans de Goede <[email protected]>
---
drivers/mmc/core/core.c | 47 ++++++++++++++++++++++++++++++++++++++++++-----
include/linux/mmc/host.h | 7 +++++++
2 files changed, 49 insertions(+), 5 deletions(-)
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 26431267a3e2..103badde910b 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1620,8 +1620,11 @@ int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr,
*/
void mmc_power_up(struct mmc_host *host, u32 ocr)
{
- if (host->ios.power_mode == MMC_POWER_ON)
+ if (host->ios.power_mode == MMC_POWER_ON) {
+ if (host->ios.clock == 0)
+ goto set_clock;
return;
+ }
mmc_pwrseq_pre_power_on(host);
@@ -1646,6 +1649,7 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
mmc_pwrseq_post_power_on(host);
+set_clock:
host->ios.clock = host->f_init;
host->ios.power_mode = MMC_POWER_ON;
@@ -1663,6 +1667,11 @@ void mmc_power_off(struct mmc_host *host)
if (host->ios.power_mode == MMC_POWER_OFF)
return;
+ if (host->rescan_keep_power) {
+ mmc_set_clock(host, 0);
+ return;
+ }
+
mmc_pwrseq_power_off(host);
host->ios.clock = 0;
@@ -1804,6 +1813,27 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay)
}
EXPORT_SYMBOL(mmc_detect_change);
+/**
+ * mmc_force_detect_change - force rescanning of a MMC socket even if
+ * it is non-removable
+ * @host: host to rescan
+ * @delay: optional delay to wait before detection (jiffies)
+ * @keep_power: if set do not turn of vdd / call pwrseq_off during rescan
+ *
+ * MMC drivers which need non-removable sdio devices to be rescanned
+ * (e.g. because the device reboots its fw after a firmware upload),
+ * can call this to force scanning the MMC socket for changes, even
+ * if it is non-removable.
+ */
+void mmc_force_detect_change(struct mmc_host *host, unsigned long delay,
+ bool keep_power)
+{
+ host->rescan_force = 1;
+ host->rescan_keep_power = keep_power;
+ _mmc_detect_change(host, delay, false);
+}
+EXPORT_SYMBOL(mmc_force_detect_change);
+
void mmc_init_erase(struct mmc_card *card)
{
unsigned int sz;
@@ -2566,7 +2596,8 @@ void mmc_rescan(struct work_struct *work)
return;
/* If there is a non-removable card registered, only scan once */
- if (!mmc_card_is_removable(host) && host->rescan_entered)
+ if (!mmc_card_is_removable(host) && host->rescan_entered &&
+ !host->rescan_force)
return;
host->rescan_entered = 1;
@@ -2583,7 +2614,8 @@ void mmc_rescan(struct work_struct *work)
* if there is a _removable_ card registered, check whether it is
* still present
*/
- if (host->bus_ops && !host->bus_dead && mmc_card_is_removable(host))
+ if (host->bus_ops && !host->bus_dead &&
+ (mmc_card_is_removable(host) || host->rescan_force))
host->bus_ops->detect(host);
host->detect_change = 0;
@@ -2616,15 +2648,20 @@ void mmc_rescan(struct work_struct *work)
}
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
- if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
+ if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) {
+ if (host->rescan_force) {
+ host->rescan_force = 0;
+ host->rescan_keep_power = 0;
+ }
break;
+ }
if (freqs[i] <= host->f_min)
break;
}
mmc_release_host(host);
out:
- if (host->caps & MMC_CAP_NEEDS_POLL)
+ if ((host->caps & MMC_CAP_NEEDS_POLL) || host->rescan_force)
mmc_schedule_delayed_work(&host->detect, HZ);
}
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index ebd1cebbef0c..d56d79867bbd 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -338,6 +338,8 @@ struct mmc_host {
int rescan_disable; /* disable card detection */
int rescan_entered; /* used with nonremovable devices */
+ int rescan_force; /* force rescan of (nonremovable) devices */
+ int rescan_keep_power; /* Do not power off card */
int need_retune; /* re-tuning is needed */
int hold_retune; /* hold off re-tuning */
@@ -420,6 +422,11 @@ int mmc_power_save_host(struct mmc_host *host);
int mmc_power_restore_host(struct mmc_host *host);
void mmc_detect_change(struct mmc_host *, unsigned long delay);
+
+/* HdG: HACK HACK HACK do not upstream */
+#define MMC_HAS_FORCE_DETECT_CHANGE
+void mmc_force_detect_change(struct mmc_host *host, unsigned long delay,
+ bool keep_power);
void mmc_request_done(struct mmc_host *, struct mmc_request *);
void mmc_command_done(struct mmc_host *host, struct mmc_request *mrq);
--
2.11.0
Hi Greg,
On 21/07/2017 17:01, Greg KH wrote:
> On Fri, Jul 21, 2017 at 04:35:01PM +0200, Quentin Schulz wrote:
>> The Espressif ESP8089 WiFi chips can be often found in cheap tablets.
>> There is one in A23 Polaroid tablets for example.
>>
>> The chip is often embedded as an eMMC SDIO device.
>>
>> The code was taken from an out-of-tree repository and has seen a first
>> pass in the cleanup process.
>>
>> At the moment, there is no publicly available datasheet for this chip.
>>
>> Cc: Hans de Goede <[email protected]>
>> Cc: Icenowy Zheng <[email protected]>
>> Signed-off-by: Quentin Schulz <[email protected]>
>
> Staging drivers need a TODO file that lists what has to be done to the
> code to get it out of staging. Why not just take a day or so and fix up
> the remaining issues and get it into the "real" part of the kernel
> correctly?
>
OK, I'll work on a TODO list. Is there anything else I should know about
staging drivers so I can address everything at the same time?
>From a driver that has already been cleaned up a bit by Icenowy and
Hans, it took me between 10 and 15 working days to this step, which I
estimate to be around 50% of total clean up (and we're only speaking
about coding style and dead code mainly, nothing about a bit of code
review, code robustness...). I find the code not really easy to follow
(might be because I'm a beginner in the subsystem as well).
I might not be the most efficient person in cleaning up drivers but I'm
pretty sure this isn't a one day cleanup. (Would be happy to be proven
otherwise :) ), else I would have done it as you suggest.
> Also, staging drivers have to be "stand-alone", I can't take stuff that
> requires core changes only for one staging driver.
>
Yes, I didn't expect the first version to go through, the goal was to
revive the discussion on this core patch as there was a consensus that
the requested feature was needed.
Thanks,
Quentin
> thanks,
>
> greg k-h
>
--
Quentin Schulz, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
Hi Quentin,
>>> The Espressif ESP8089 WiFi chips can be often found in cheap tablets.
>>> There is one in A23 Polaroid tablets for example.
>>>
>>> The chip is often embedded as an eMMC SDIO device.
>>>
>>> The code was taken from an out-of-tree repository and has seen a first
>>> pass in the cleanup process.
>>>
>>> At the moment, there is no publicly available datasheet for this chip.
>>>
>>> Cc: Hans de Goede <[email protected]>
>>> Cc: Icenowy Zheng <[email protected]>
>>> Signed-off-by: Quentin Schulz <[email protected]>
>>
>> Staging drivers need a TODO file that lists what has to be done to the
>> code to get it out of staging. Why not just take a day or so and fix up
>> the remaining issues and get it into the "real" part of the kernel
>> correctly?
>>
>
> OK, I'll work on a TODO list. Is there anything else I should know about
> staging drivers so I can address everything at the same time?
>
> From a driver that has already been cleaned up a bit by Icenowy and
> Hans, it took me between 10 and 15 working days to this step, which I
> estimate to be around 50% of total clean up (and we're only speaking
> about coding style and dead code mainly, nothing about a bit of code
> review, code robustness...). I find the code not really easy to follow
> (might be because I'm a beginner in the subsystem as well).
>
> I might not be the most efficient person in cleaning up drivers but I'm
> pretty sure this isn't a one day cleanup. (Would be happy to be proven
> otherwise :) ), else I would have done it as you suggest.
even if it takes you 1 month to clean it up, get it reviewed on linux-wireless and target wireless-drivers instead of staging. When I had a brief a look at your patch, it didn't look like staging material to me.
Regards
Marcel
Hi Marcel,
On 21/07/2017 18:52, Marcel Holtmann wrote:
> Hi Quentin,
>
>>>> The Espressif ESP8089 WiFi chips can be often found in cheap tablets.
>>>> There is one in A23 Polaroid tablets for example.
>>>>
>>>> The chip is often embedded as an eMMC SDIO device.
>>>>
>>>> The code was taken from an out-of-tree repository and has seen a first
>>>> pass in the cleanup process.
>>>>
>>>> At the moment, there is no publicly available datasheet for this chip.
>>>>
>>>> Cc: Hans de Goede <[email protected]>
>>>> Cc: Icenowy Zheng <[email protected]>
>>>> Signed-off-by: Quentin Schulz <[email protected]>
>>>
>>> Staging drivers need a TODO file that lists what has to be done to the
>>> code to get it out of staging. Why not just take a day or so and fix up
>>> the remaining issues and get it into the "real" part of the kernel
>>> correctly?
>>>
>>
>> OK, I'll work on a TODO list. Is there anything else I should know about
>> staging drivers so I can address everything at the same time?
>>
>> From a driver that has already been cleaned up a bit by Icenowy and
>> Hans, it took me between 10 and 15 working days to this step, which I
>> estimate to be around 50% of total clean up (and we're only speaking
>> about coding style and dead code mainly, nothing about a bit of code
>> review, code robustness...). I find the code not really easy to follow
>> (might be because I'm a beginner in the subsystem as well).
>>
>> I might not be the most efficient person in cleaning up drivers but I'm
>> pretty sure this isn't a one day cleanup. (Would be happy to be proven
>> otherwise :) ), else I would have done it as you suggest.
>
> even if it takes you 1 month to clean it up, get it reviewed on linux-wireless and target wireless-drivers instead of staging. When I had a brief a look at your patch, it didn't look like staging material to me.
>
We don't have a client supporting this effort and I don't think the
company I work for would support this effort (maybe it would, but
definitely spread over a long long period), so we're talking about 10-15
working days spread over my free time/week-end, that isn't for in one
month :) I could use help for sure on this driver, that's why I posted
it in staging.
I've done the cleanup on a per-file basis so maybe you looked at one of
the cleaned up files?
Just to be sure, you're telling me that I should post it as is on
linux-wireless and then work with the reviews? Or are you telling me to
take "1 month to clean it up" and then post it on linux-wireless?
Thanks,
Quentin
> Regards
>
> Marcel
>
--
Quentin Schulz, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
invite Jack from expressif
?? 2017/7/21 22:35, Quentin Schulz д??:
> From: Hans de Goede <[email protected]>
>
> Some sdio devices have a multiple stage bring-up process. Specifically
> the esp8089 (for which an out of tree driver is available) loads firmware
> on the first call to its sdio-drivers' probe function and then resets
> the device causing it to reboot from its RAM with the new firmware.
>
Nice to see finally someone get into here!
I was bringing up ESP8089 for rockchip platforms 4 yeas ago with
Jack from espressif, the ESP8089 RD team, face 2 face. And I forgot
most the details but it seems indeed the limitation of RAM size so that
it has to use 2 stages boot-up method.
I hople Jack could give some suggestion or details about this.
> When this sdio device reboots it comes back up in 1 bit 400 KHz mode
> again, and we need to walk through the whole ios negatiation and sdio setup
> again.
>
> There are 2 problems with this:
>
> 1) Typically these devices are soldered onto some (ARM) tablet / SBC
> PCB and as such are described in devicetree as "non-removable", which
> causes the mmc-core to scan them only once and not poll for the device
> dropping of the bus. Normally this is the right thing todo but in the
> eso8089 example we need the mmc-core to notice the module has disconnected
> (since it is now in 1 bit mode again it will not talk to the host in 4 bit
> mode). This can be worked around by using "broken-cd" in devicetree
> instead of "non-removable", but that is not a proper fix since the device
> really is non-removable.
>
> 2) When the mmc-core detects the device has disconnected it will poweroff
> the device, causing the RAM loaded firmware to be lost. This can be worked
> around in devicetree by using regulator-always-on (and avoiding the use of
> mmc-pwrseq), but again that is more of a hack then a proper fix.
>
> This commmit fixes 1) by adding a mmc_force_detect_change function which
> will cause scanning for device removal / insertion until a new device is
> detected. 2) Is fixed by a keep_power flag to the mmc_force_detect_change
> function which when set causes the mmc-core to keep the power to the device
> on during the rescan.
>
> Cc: Icenowy Zheng <[email protected]>
> Cc: Maxime Ripard <[email protected]>
> Cc: Chen-Yu Tsai <[email protected]>
> Signed-off-by: Hans de Goede <[email protected]>
> ---
> drivers/mmc/core/core.c | 47 ++++++++++++++++++++++++++++++++++++++++++-----
> include/linux/mmc/host.h | 7 +++++++
> 2 files changed, 49 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 26431267a3e2..103badde910b 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -1620,8 +1620,11 @@ int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr,
> */
> void mmc_power_up(struct mmc_host *host, u32 ocr)
> {
> - if (host->ios.power_mode == MMC_POWER_ON)
> + if (host->ios.power_mode == MMC_POWER_ON) {
> + if (host->ios.clock == 0)
> + goto set_clock;
> return;
> + }
>
> mmc_pwrseq_pre_power_on(host);
>
> @@ -1646,6 +1649,7 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
>
> mmc_pwrseq_post_power_on(host);
>
> +set_clock:
> host->ios.clock = host->f_init;
>
> host->ios.power_mode = MMC_POWER_ON;
> @@ -1663,6 +1667,11 @@ void mmc_power_off(struct mmc_host *host)
> if (host->ios.power_mode == MMC_POWER_OFF)
> return;
>
> + if (host->rescan_keep_power) {
> + mmc_set_clock(host, 0);
> + return;
> + }
> +
> mmc_pwrseq_power_off(host);
>
> host->ios.clock = 0;
> @@ -1804,6 +1813,27 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay)
> }
> EXPORT_SYMBOL(mmc_detect_change);
>
> +/**
> + * mmc_force_detect_change - force rescanning of a MMC socket even if
> + * it is non-removable
> + * @host: host to rescan
> + * @delay: optional delay to wait before detection (jiffies)
> + * @keep_power: if set do not turn of vdd / call pwrseq_off during rescan
> + *
> + * MMC drivers which need non-removable sdio devices to be rescanned
> + * (e.g. because the device reboots its fw after a firmware upload),
> + * can call this to force scanning the MMC socket for changes, even
> + * if it is non-removable.
> + */
> +void mmc_force_detect_change(struct mmc_host *host, unsigned long delay,
> + bool keep_power)
> +{
> + host->rescan_force = 1;
> + host->rescan_keep_power = keep_power;
> + _mmc_detect_change(host, delay, false);
> +}
> +EXPORT_SYMBOL(mmc_force_detect_change);
> +
> void mmc_init_erase(struct mmc_card *card)
> {
> unsigned int sz;
> @@ -2566,7 +2596,8 @@ void mmc_rescan(struct work_struct *work)
> return;
>
> /* If there is a non-removable card registered, only scan once */
> - if (!mmc_card_is_removable(host) && host->rescan_entered)
> + if (!mmc_card_is_removable(host) && host->rescan_entered &&
> + !host->rescan_force)
> return;
> host->rescan_entered = 1;
>
> @@ -2583,7 +2614,8 @@ void mmc_rescan(struct work_struct *work)
> * if there is a _removable_ card registered, check whether it is
> * still present
> */
> - if (host->bus_ops && !host->bus_dead && mmc_card_is_removable(host))
> + if (host->bus_ops && !host->bus_dead &&
> + (mmc_card_is_removable(host) || host->rescan_force))
> host->bus_ops->detect(host);
>
> host->detect_change = 0;
> @@ -2616,15 +2648,20 @@ void mmc_rescan(struct work_struct *work)
> }
>
> for (i = 0; i < ARRAY_SIZE(freqs); i++) {
> - if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
> + if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) {
> + if (host->rescan_force) {
> + host->rescan_force = 0;
> + host->rescan_keep_power = 0;
> + }
> break;
> + }
> if (freqs[i] <= host->f_min)
> break;
> }
> mmc_release_host(host);
>
> out:
> - if (host->caps & MMC_CAP_NEEDS_POLL)
> + if ((host->caps & MMC_CAP_NEEDS_POLL) || host->rescan_force)
> mmc_schedule_delayed_work(&host->detect, HZ);
> }
>
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index ebd1cebbef0c..d56d79867bbd 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -338,6 +338,8 @@ struct mmc_host {
>
> int rescan_disable; /* disable card detection */
> int rescan_entered; /* used with nonremovable devices */
> + int rescan_force; /* force rescan of (nonremovable) devices */
> + int rescan_keep_power; /* Do not power off card */
>
> int need_retune; /* re-tuning is needed */
> int hold_retune; /* hold off re-tuning */
> @@ -420,6 +422,11 @@ int mmc_power_save_host(struct mmc_host *host);
> int mmc_power_restore_host(struct mmc_host *host);
>
> void mmc_detect_change(struct mmc_host *, unsigned long delay);
> +
> +/* HdG: HACK HACK HACK do not upstream */
> +#define MMC_HAS_FORCE_DETECT_CHANGE
> +void mmc_force_detect_change(struct mmc_host *host, unsigned long delay,
> + bool keep_power);
> void mmc_request_done(struct mmc_host *, struct mmc_request *);
> void mmc_command_done(struct mmc_host *host, struct mmc_request *mrq);
>
>
--
Best Regards
Shawn Lin
Hi Quentin,
[auto build test WARNING on next-20170719]
[cannot apply to staging/staging-testing linus/master linux/master v4.13-rc1 v4.12 v4.12-rc7 v4.13-rc1]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Quentin-Schulz/add-ESP8089-WiFi-chip-driver/20170723-143744
config: blackfin-allyesconfig (attached as .config)
compiler: bfin-uclinux-gcc (GCC) 6.2.0
reproduce:
wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=blackfin
Note: it may well be a FALSE warning. FWIW you are at least aware of it now.
http://gcc.gnu.org/wiki/Better_Uninitialized_Warnings
All warnings (new ones prefixed by >>):
drivers/staging//esp8089/esp_sip.c: In function 'sip_txq_process':
>> drivers/staging//esp8089/esp_sip.c:797:32: warning: 'offset' may be used uninitialized in this function [-Wmaybe-uninitialized]
memcpy(sip->tx_aggr_write_ptr + offset, skb->data, skb->len);
~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~
drivers/staging//esp8089/esp_sip.c:700:14: note: 'offset' was declared here
u32 tx_len, offset;
^~~~~~
drivers/staging//esp8089/esp_sip.c: In function 'sip_poll_resetting_event':
>> drivers/staging//esp8089/esp_sip.c:1662:5: warning: 'ret' may be used uninitialized in this function [-Wmaybe-uninitialized]
if (!ret) {
^
--
drivers/staging//esp8089/esp_mac80211.c: In function 'esp_op_set_key':
>> drivers/staging//esp8089/esp_mac80211.c:529:6: warning: 'index' may be used uninitialized in this function [-Wmaybe-uninitialized]
map[index].flag = 0;
^
vim +/offset +797 drivers/staging//esp8089/esp_sip.c
691
692 /* setup sip header and tx info, copy pkt into aggr buf */
693 static int sip_pack_pkt(struct esp_sip *sip, struct sk_buff *skb, int *pm_state)
694 {
695 struct ieee80211_tx_info *itx_info;
696 struct sip_hdr *shdr;
697 struct ieee80211_hdr *wh;
698 struct esp_vif *evif;
699 struct esp_node *node;
700 u32 tx_len, offset;
701 bool is_data = true;
702 u8 sta_index;
703 int alg;
704
705 itx_info = IEEE80211_SKB_CB(skb);
706 if (itx_info->flags == 0xffffffff) {
707 shdr = (struct sip_hdr *)skb->data;
708 is_data = false;
709 tx_len = skb->len;
710 } else {
711 wh = (struct ieee80211_hdr *)skb->data;
712 evif = (struct esp_vif *)itx_info->control.vif->drv_priv;
713 /* update sip header */
714 shdr = (struct sip_hdr *)sip->tx_aggr_write_ptr;
715
716 shdr->fc[0] = 0;
717 shdr->fc[1] = 0;
718
719 if (itx_info->flags & IEEE80211_TX_CTL_AMPDU)
720 SIP_HDR_SET_TYPE(shdr->fc[0], SIP_DATA_AMPDU);
721 else
722 SIP_HDR_SET_TYPE(shdr->fc[0], SIP_DATA);
723
724 if (!evif->epub) {
725 sip_tx_status_report(sip, skb, itx_info, false);
726 atomic_dec(&sip->tx_data_pkt_queued);
727 return -EINVAL;
728 }
729
730 /* make room for encrypted pkt */
731 if (itx_info->control.hw_key) {
732 alg = esp_cipher2alg(itx_info->control.hw_key->cipher);
733 if (unlikely(alg == -1)) {
734 sip_tx_status_report(sip, skb, itx_info, false);
735 atomic_dec(&sip->tx_data_pkt_queued);
736 return -1;
737 }
738
739 shdr->d_enc_flag = alg + 1;
740 shdr->d_hw_kid = itx_info->control.hw_key->hw_key_idx |
741 (evif->index << 7);
742 } else {
743 shdr->d_enc_flag = 0;
744 shdr->d_hw_kid = evif->index << 7 | evif->index;
745 }
746
747 /* update sip tx info */
748 node = esp_get_node_by_addr(sip->epub, wh->addr1);
749 if (node)
750 sta_index = node->index;
751 else
752 sta_index = ESP_PUB_MAX_STA + 1;
753
754 SIP_HDR_SET_IFIDX(shdr->fc[0], evif->index << 3 | sta_index);
755 shdr->d_p2p = itx_info->control.vif->p2p;
756
757 if (evif->index == 1)
758 shdr->d_p2p = 1;
759
760 shdr->d_ac = skb_get_queue_mapping(skb);
761 shdr->d_tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
762
763 wh = (struct ieee80211_hdr *)skb->data;
764
765 if (ieee80211_is_mgmt(wh->frame_control)) {
766 /* addba/delba/bar may use different tid/ac */
767 if (shdr->d_ac == WME_AC_VO)
768 shdr->d_tid = 7;
769
770 if (ieee80211_is_beacon(wh->frame_control)) {
771 shdr->d_tid = 8;
772 shdr->d_ac = 4;
773 }
774 }
775
776 if (check_ac_tid(skb->data, shdr->d_ac, shdr->d_tid)) {
777 shdr->d_ac = WME_AC_BE;
778 shdr->d_tid = 0;
779 }
780
781 /* make sure data is start at 4 bytes aligned addr. */
782 offset = roundup(sizeof(struct sip_hdr), 4);
783
784 if (SIP_HDR_IS_AMPDU(shdr)) {
785 memset(sip->tx_aggr_write_ptr + offset, 0,
786 sizeof(struct esp_tx_ampdu_entry));
787 offset += roundup(sizeof(struct esp_tx_ampdu_entry), 4);
788 }
789
790 tx_len = offset + skb->len;
791 shdr->len = tx_len; /* actual len */
792 }
793
794 shdr->seq = sip->txseq++;
795
796 /* copy skb to aggr buf */
> 797 memcpy(sip->tx_aggr_write_ptr + offset, skb->data, skb->len);
798
799 if (is_data) {
800 spin_lock_bh(&sip->epub->tx_lock);
801 sip->txdataseq = shdr->seq;
802 spin_unlock_bh(&sip->epub->tx_lock);
803
804 /* fake a tx_status and report to mac80211 stack to speed up tx, may affect
805 * 1) rate control (now it's all in target, so should be OK)
806 * 2) ps mode, mac80211 want to check ACK of ps/nulldata to see if AP is awake
807 * 3) BAR, mac80211 do BAR by checking ACK
808 *
809 * XXX: need to adjust for 11n, e.g. report tx_status according to BA received in target
810 */
811 sip_tx_status_report(sip, skb, itx_info, true);
812 atomic_dec(&sip->tx_data_pkt_queued);
813
814 STRACE_TX_DATA_INC();
815 } else {
816 /* check pm state here */
817
818 /* no need to hold ctrl skb */
819 sip_free_ctrl_skbuff(sip, skb);
820 STRACE_TX_CMD_INC();
821 }
822
823 /* TBD: roundup here or whole aggr-buf */
824 tx_len = roundup(tx_len, sip->tx_blksz);
825
826 sip->tx_aggr_write_ptr += tx_len;
827 sip->tx_tot_len += tx_len;
828
829 return 0;
830 }
831
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
drivers/staging/esp8089/esp_mac80211.c:596:33-34: WARNING comparing pointer to 0
Compare pointer-typed values to NULL rather than 0
Semantic patch information:
This makes an effort to choose between !x and x == NULL. !x is used
if it has previously been used with the function used to initialize x.
This relies on type information. More type information can be obtained
using the option -all_includes and the option -I to specify an
include path.
Generated by: scripts/coccinelle/null/badzero.cocci
Fixes: 5ebdb57b0b15 ("staging: net: wireless: add ESP8089 WiFi driver")
CC: Quentin Schulz <[email protected]>
Signed-off-by: Fengguang Wu <[email protected]>
---
esp_mac80211.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/drivers/staging/esp8089/esp_mac80211.c
+++ b/drivers/staging/esp8089/esp_mac80211.c
@@ -593,7 +593,7 @@ static void hw_scan_timeout_report(struc
if (test_and_clear_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags))
sip_trigger_txq_process(epub->sip);
/*check if normally complete or aborted like timeout/hw error */
- aborted = (epub->wl.scan_req != 0);
+ aborted = (epub->wl.scan_req != NULL);
if (aborted)
epub->wl.scan_req = NULL;
Hi Quentin,
[auto build test ERROR on next-20170719]
[cannot apply to staging/staging-testing linus/master linux/master v4.13-rc1 v4.12 v4.12-rc7 v4.13-rc1]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Quentin-Schulz/add-ESP8089-WiFi-chip-driver/20170723-143744
config: i386-allmodconfig (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
# save the attached .config to linux build tree
make ARCH=i386
All errors (new ones prefixed by >>):
>> drivers/staging/esp8089/sdio_sif_esp.c:30:25: fatal error: esp_version.h: No such file or directory
#include "esp_version.h"
^
compilation terminated.
--
>> drivers/staging/esp8089/esp_ctrl.c:28:25: fatal error: esp_version.h: No such file or directory
#include "esp_version.h"
^
compilation terminated.
coccinelle warnings: (new ones prefixed by >>)
>> drivers/staging/esp8089/esp_mac80211.c:596:33-34: WARNING comparing pointer to 0
--
>> drivers/staging/esp8089/esp_sip.c:1462:14-20: ERROR: sip is NULL but dereferenced.
Please review and possibly fold the followup patch.
vim +30 drivers/staging/esp8089/sdio_sif_esp.c
24
25 #include "esp_pub.h"
26 #include "esp_sif.h"
27 #include "esp_sip.h"
28 #include "esp_debug.h"
29 #include "slc_host_register.h"
> 30 #include "esp_version.h"
31 #include "esp_ctrl.h"
32 #include "esp_file.h"
33
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
(adding linux-wireless)
Quentin Schulz <[email protected]> writes:
> Hi Marcel,
>
> On 21/07/2017 18:52, Marcel Holtmann wrote:
>> Hi Quentin,
>>
>>>>> The Espressif ESP8089 WiFi chips can be often found in cheap tablets.
>>>>> There is one in A23 Polaroid tablets for example.
>>>>>
>>>>> The chip is often embedded as an eMMC SDIO device.
>>>>>
>>>>> The code was taken from an out-of-tree repository and has seen a first
>>>>> pass in the cleanup process.
>>>>>
>>>>> At the moment, there is no publicly available datasheet for this chip.
>>>>>
>>>>> Cc: Hans de Goede <[email protected]>
>>>>> Cc: Icenowy Zheng <[email protected]>
>>>>> Signed-off-by: Quentin Schulz <[email protected]>
>>>>
>>>> Staging drivers need a TODO file that lists what has to be done to the
>>>> code to get it out of staging. Why not just take a day or so and fix up
>>>> the remaining issues and get it into the "real" part of the kernel
>>>> correctly?
>>>>
>>>
>>> OK, I'll work on a TODO list. Is there anything else I should know about
>>> staging drivers so I can address everything at the same time?
>>>
>>> From a driver that has already been cleaned up a bit by Icenowy and
>>> Hans, it took me between 10 and 15 working days to this step, which I
>>> estimate to be around 50% of total clean up (and we're only speaking
>>> about coding style and dead code mainly, nothing about a bit of code
>>> review, code robustness...). I find the code not really easy to follow
>>> (might be because I'm a beginner in the subsystem as well).
>>>
>>> I might not be the most efficient person in cleaning up drivers but I'm
>>> pretty sure this isn't a one day cleanup. (Would be happy to be proven
>>> otherwise :) ), else I would have done it as you suggest.
>>
>> even if it takes you 1 month to clean it up, get it reviewed on
>> linux-wireless and target wireless-drivers instead of staging. When I
>> had a brief a look at your patch, it didn't look like staging
>> material to me.
I did only a 30 sec review (right now no time for proper review because
I have quite a lot of catching up after vacation) but based on what I
saw the driver looks promising. So I agree with Marcel, you should try
to submit this via linux-wireless first and only use staging as the last
resort.
> We don't have a client supporting this effort and I don't think the
> company I work for would support this effort (maybe it would, but
> definitely spread over a long long period), so we're talking about 10-15
> working days spread over my free time/week-end, that isn't for in one
> month :) I could use help for sure on this driver, that's why I posted
> it in staging.
>
> I've done the cleanup on a per-file basis so maybe you looked at one of
> the cleaned up files?
>
> Just to be sure, you're telling me that I should post it as is on
> linux-wireless and then work with the reviews? Or are you telling me to
> take "1 month to clean it up" and then post it on linux-wireless?
I recommend to post the patch to linux-wireless now and see what
comments you get. Then I can also when a proper review and have better
guidance.
--
Kalle Valo
Yeah. This feels like it could go into the main linux-wireless tree
with a little work. I put some random review comments inline. Nothing
super drastic stands out.
regards,
dan carpenter
On Fri, Jul 21, 2017 at 04:35:01PM +0200, Quentin Schulz wrote:
> The Espressif ESP8089 WiFi chips can be often found in cheap tablets.
> There is one in A23 Polaroid tablets for example.
>
> The chip is often embedded as an eMMC SDIO device.
>
> The code was taken from an out-of-tree repository and has seen a first
> pass in the cleanup process.
>
> At the moment, there is no publicly available datasheet for this chip.
>
> Cc: Hans de Goede <[email protected]>
> Cc: Icenowy Zheng <[email protected]>
> Signed-off-by: Quentin Schulz <[email protected]>
> ---
> drivers/staging/Kconfig | 2 +
> drivers/staging/Makefile | 1 +
> drivers/staging/esp8089/Kconfig | 13 +
> drivers/staging/esp8089/Makefile | 7 +
> drivers/staging/esp8089/esp_ctrl.c | 527 ++++++++
> drivers/staging/esp8089/esp_ctrl.h | 48 +
> drivers/staging/esp8089/esp_debug.c | 247 ++++
> drivers/staging/esp8089/esp_debug.h | 69 ++
> drivers/staging/esp8089/esp_file.c | 221 ++++
> drivers/staging/esp8089/esp_file.h | 30 +
> drivers/staging/esp8089/esp_init_data.h | 17 +
> drivers/staging/esp8089/esp_io.c | 294 +++++
> drivers/staging/esp8089/esp_mac80211.c | 1496 +++++++++++++++++++++++
> drivers/staging/esp8089/esp_mac80211.h | 33 +
> drivers/staging/esp8089/esp_main.c | 199 ++++
> drivers/staging/esp8089/esp_pub.h | 188 +++
> drivers/staging/esp8089/esp_sif.h | 131 ++
> drivers/staging/esp8089/esp_sip.c | 1718 +++++++++++++++++++++++++++
> drivers/staging/esp8089/esp_sip.h | 150 +++
> drivers/staging/esp8089/esp_utils.c | 133 +++
> drivers/staging/esp8089/esp_utils.h | 27 +
> drivers/staging/esp8089/esp_wl.h | 35 +
> drivers/staging/esp8089/esp_wmac.h | 87 ++
> drivers/staging/esp8089/sdio_sif_esp.c | 552 +++++++++
> drivers/staging/esp8089/sip2_common.h | 388 ++++++
> drivers/staging/esp8089/slc_host_register.h | 263 ++++
> 26 files changed, 6876 insertions(+)
> create mode 100644 drivers/staging/esp8089/Kconfig
> create mode 100644 drivers/staging/esp8089/Makefile
> create mode 100644 drivers/staging/esp8089/esp_ctrl.c
> create mode 100644 drivers/staging/esp8089/esp_ctrl.h
> create mode 100644 drivers/staging/esp8089/esp_debug.c
> create mode 100644 drivers/staging/esp8089/esp_debug.h
> create mode 100644 drivers/staging/esp8089/esp_file.c
> create mode 100644 drivers/staging/esp8089/esp_file.h
> create mode 100644 drivers/staging/esp8089/esp_init_data.h
> create mode 100644 drivers/staging/esp8089/esp_io.c
> create mode 100644 drivers/staging/esp8089/esp_mac80211.c
> create mode 100644 drivers/staging/esp8089/esp_mac80211.h
> create mode 100644 drivers/staging/esp8089/esp_main.c
> create mode 100644 drivers/staging/esp8089/esp_pub.h
> create mode 100644 drivers/staging/esp8089/esp_sif.h
> create mode 100644 drivers/staging/esp8089/esp_sip.c
> create mode 100644 drivers/staging/esp8089/esp_sip.h
> create mode 100644 drivers/staging/esp8089/esp_utils.c
> create mode 100644 drivers/staging/esp8089/esp_utils.h
> create mode 100644 drivers/staging/esp8089/esp_wl.h
> create mode 100644 drivers/staging/esp8089/esp_wmac.h
> create mode 100644 drivers/staging/esp8089/sdio_sif_esp.c
> create mode 100644 drivers/staging/esp8089/sip2_common.h
> create mode 100644 drivers/staging/esp8089/slc_host_register.h
>
> diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
> index e97d72e3bc40..0bd007837429 100644
> --- a/drivers/staging/Kconfig
> +++ b/drivers/staging/Kconfig
> @@ -114,4 +114,6 @@ source "drivers/staging/vboxvideo/Kconfig"
>
> source "drivers/staging/pi433/Kconfig"
>
> +source "drivers/staging/esp8089/Kconfig"
> +
> endif # STAGING
> diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
> index 993ed0c1556c..23cfa1258c4e 100644
> --- a/drivers/staging/Makefile
> +++ b/drivers/staging/Makefile
> @@ -46,3 +46,4 @@ obj-$(CONFIG_BCM2835_VCHIQ) += vc04_services/
> obj-$(CONFIG_CRYPTO_DEV_CCREE) += ccree/
> obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/
> obj-$(CONFIG_PI433) += pi433/
> +obj-$(CONFIG_ESP8089) += esp8089/
> diff --git a/drivers/staging/esp8089/Kconfig b/drivers/staging/esp8089/Kconfig
> new file mode 100644
> index 000000000000..8fdd8fa828ad
> --- /dev/null
> +++ b/drivers/staging/esp8089/Kconfig
> @@ -0,0 +1,13 @@
> +config ESP8089
> + tristate "Espressif ESP8089 SDIO WiFi"
> + depends on MAC80211
> + ---help---
> + ESP8089 is a low-budget 2.4GHz WiFi chip by Espressif, used in many
> + cheap tablets with Allwinner or Rockchip SoC
> +
> +config ESP8089_DEBUG_FS
> + bool "Enable DebugFS support for ESP8089"
> + depends on ESP8089
> + default y
> + ---help---
> + DebugFS support for ESP8089
> diff --git a/drivers/staging/esp8089/Makefile b/drivers/staging/esp8089/Makefile
> new file mode 100644
> index 000000000000..827c66d8b5d5
> --- /dev/null
> +++ b/drivers/staging/esp8089/Makefile
> @@ -0,0 +1,7 @@
> +MODULE_NAME = esp8089
> +
> +$(MODULE_NAME)-y := esp_debug.o sdio_sif_esp.o esp_io.o \
> + esp_file.o esp_main.o esp_sip.o esp_ctrl.o \
> + esp_mac80211.o esp_debug.o esp_utils.o
> +
> +obj-$(CONFIG_ESP8089) := esp8089.o
> diff --git a/drivers/staging/esp8089/esp_ctrl.c b/drivers/staging/esp8089/esp_ctrl.c
> new file mode 100644
> index 000000000000..e5a95e7b313f
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_ctrl.c
> @@ -0,0 +1,527 @@
> +/*
> + * Copyright (c) 2009 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + * Quentin Schulz <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + */
> +
> +#include <net/mac80211.h>
> +#include <net/cfg80211.h>
> +#include <linux/skbuff.h>
> +#include <linux/bitops.h>
> +#include <linux/firmware.h>
> +
> +#include "esp_init_data.h"
> +#include "esp_pub.h"
> +#include "esp_sip.h"
> +#include "esp_ctrl.h"
> +#include "esp_sif.h"
> +#include "esp_debug.h"
> +#include "esp_wmac.h"
> +#include "esp_utils.h"
> +#include "esp_wl.h"
> +#include "esp_file.h"
> +#include "esp_version.h"
> +
> +#define DRIVER_VER 0xbdf5087c3debll
> +
> +static void esp_tx_ba_session_op(struct esp_sip *sip, struct esp_node *node,
> + enum trc_ampdu_state state, u8 tid)
> +{
> + struct esp_tx_tid *txtid = &node->tid[tid];
> +
> + if (state != TRC_TX_AMPDU_STOPPED && state != TRC_TX_AMPDU_OPERATIONAL)
> + return;
> +
> + if (state == TRC_TX_AMPDU_STOPPED) {
> + if (txtid->state != ESP_TID_STATE_OPERATIONAL) {
> + dev_err(sip->epub->dev,
> + "%s tid %d TXAMPDU GOT STOP EVT IN WRONG STATE %d\n",
> + __func__, tid, txtid->state);
> + return;
> + }
> +
> + dev_dbg(sip->epub->dev, "%s tid %d TXAMPDU GOT STOP EVT\n",
> + __func__, tid);
> +
> + spin_lock_bh(&sip->epub->tx_ampdu_lock);
> + txtid->state = ESP_TID_STATE_WAIT_STOP;
> + spin_unlock_bh(&sip->epub->tx_ampdu_lock);
> +
> + ieee80211_stop_tx_ba_session(node->sta, (u16)tid);
> + return;
> + }
> +
> + if (txtid->state == ESP_TID_STATE_STOP) {
> + dev_dbg(sip->epub->dev, "%s tid %d TXAMPDU GOT OPERATIONAL\n",
> + __func__, tid);
> +
> + spin_lock_bh(&sip->epub->tx_ampdu_lock);
> + txtid->state = ESP_TID_STATE_TRIGGER;
> + spin_unlock_bh(&sip->epub->tx_ampdu_lock);
> +
> + ieee80211_start_tx_ba_session(node->sta, (u16)tid, 0);
> + return;
> + } else if (txtid->state == ESP_TID_STATE_OPERATIONAL) {
> + sip_send_ampdu_action(sip->epub, SIP_AMPDU_TX_OPERATIONAL,
> + node->sta->addr, tid, node->ifidx, 0);
> + return;
> + }
> +
> + dev_err(sip->epub->dev,
> + "%s tid %d TXAMPDU GOT OPERATIONAL EVT IN WRONG STATE %d\n",
> + __func__, tid, txtid->state);
> +}
> +
> +int sip_parse_events(struct esp_sip *sip, u8 *buf)
> +{
> + struct sip_hdr *hdr = (struct sip_hdr *)buf;
> + struct sip_evt_bootup2 *bootup_evt;
> + struct sip_evt_scan_report *report;
> + struct sip_evt_roc *roc;
> + struct sip_evt_trc_ampdu *ampdu;
> + struct sip_evt_noisefloor *noisefloor;
> + struct esp_node *node;
> + char *ep;
> + u8 *p;
> + u16 *len;
> + int i;
> +
> + switch (hdr->c_evtid) {
> + case SIP_EVT_TARGET_ON:
> + /* use rx work queue to send... */
> + if (atomic_read(&sip->state) == SIP_PREPARE_BOOT ||
> + atomic_read(&sip->state) == SIP_BOOT) {
> + atomic_set(&sip->state, SIP_SEND_INIT);
> + queue_work(sip->epub->esp_wkq, &sip->rx_process_work);
> + break;
> + }
> + dev_err(sip->epub->dev, "%s boot during wrong state %d\n",
> + __func__, atomic_read(&sip->state));
> + break;
> +
> + case SIP_EVT_BOOTUP:
> + bootup_evt = (struct sip_evt_bootup2 *)(buf + SIP_CTRL_HDR_LEN);
> +
> + kfree(sip->rawbuf);
> +
> + sip_post_init(sip, bootup_evt);
> + if (gl_bootup_cplx)
> + complete(gl_bootup_cplx);
> +
> + break;
> +
> + case SIP_EVT_RESETTING:
> + sip->epub->wait_reset = 1;
> + if (gl_bootup_cplx)
> + complete(gl_bootup_cplx);
> + break;
> +
> + case SIP_EVT_SCAN_RESULT:
> + if (!atomic_read(&sip->epub->wl.off)) {
> + report = (struct sip_evt_scan_report *)(buf + SIP_CTRL_HDR_LEN);
> + sip_scandone_process(sip, report);
> + break;
> + }
> +
> + dev_err(sip->epub->dev, "%s scan result while wlan off\n",
> + __func__);
> + break;
> +
> + case SIP_EVT_ROC:
> + roc = (struct sip_evt_roc *)(buf + SIP_CTRL_HDR_LEN);
> + esp_rocdone_process(sip->epub->hw, roc);
> + break;
> +
> + case SIP_EVT_SNPRINTF_TO_HOST:
> + p = buf + sizeof(struct sip_hdr) + sizeof(u16);
> + len = (u16 *)(buf + sizeof(struct sip_hdr));
> + dev_dbg(sip->epub->dev, "esp_host:%llx\nesp_target: %.*s\n",
> + DRIVER_VER, *len, p);
> +
> + if (!*len || sip->epub->sdio_state != ESP_SDIO_STATE_FIRST_INIT)
> + break;
> +
> + dev_dbg(sip->epub->dev,
> + "SNPRINTF TO HOST: esp_host:%llx\nesp_target: %.*s\n",
> + DRIVER_VER, *len, p);
> + break;
> +
> + case SIP_EVT_TRC_AMPDU:
> + if (atomic_read(&sip->epub->wl.off)) {
> + dev_err(sip->epub->dev,
> + "%s scan result while wlan off\n", __func__);
> + break;
> + }
> +
> + ampdu = (struct sip_evt_trc_ampdu *)(buf + SIP_CTRL_HDR_LEN);
> + node = esp_get_node_by_addr(sip->epub, ampdu->addr);
> + if (!node)
> + break;
> + for (i = 0; i < 8; i++)
> + if (ampdu->tid & BIT(i))
> + esp_tx_ba_session_op(sip, node, ampdu->state,
> + i);
> + break;
> +
> + case SIP_EVT_INIT_EP:
> + ep = (char *)(buf + SIP_CTRL_HDR_LEN);
> + dev_dbg(sip->epub->dev, "Phy Init: %s\n", ep);
> + break;
> +
> + case SIP_EVT_NOISEFLOOR:
> + noisefloor = (struct sip_evt_noisefloor *)(buf + SIP_CTRL_HDR_LEN);
> + atomic_set(&sip->noise_floor, noisefloor->noise_floor);
> + break;
> +
> + default:
> + break;
> + }
> +
> + return 0;
> +}
> +
> +/* FIXME: ugly af */
> +void sip_send_chip_init(struct esp_sip *sip)
> +{
> + size_t size = sizeof(esp_init_data);
> +
> + fix_init_data(esp_init_data, size);
> + atomic_sub(1, &sip->tx_credits);
> + sip_send_cmd(sip, SIP_CMD_INIT, size, (void *)esp_init_data);
> +}
> +
> +int sip_send_config(struct esp_pub *epub, struct ieee80211_conf *conf)
> +{
> + struct sk_buff *skb;
> + struct sip_cmd_config *configcmd;
> +
> + skb = sip_alloc_ctrl_skbuf(epub->sip, sizeof(struct sip_cmd_config) +
> + sizeof(struct sip_hdr), SIP_CMD_CONFIG);
> + if (!skb)
> + return -ENOMEM;
> +
> + dev_dbg(epub->dev, "%s config center freq %d\n", __func__,
> + conf->chandef.chan->center_freq);
> +
> + configcmd = (struct sip_cmd_config *)(skb->data + sizeof(struct sip_hdr));
> + configcmd->center_freq = conf->chandef.chan->center_freq;
> + configcmd->duration = 0;
> +
> + return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
> +}
> +
> +int sip_send_bss_info_update(struct esp_pub *epub, struct esp_vif *evif,
> + u8 *bssid, int assoc)
> +{
> + struct sk_buff *skb;
> + struct sip_cmd_bss_info_update *bsscmd;
> +
> + skb = sip_alloc_ctrl_skbuf(epub->sip,
> + sizeof(struct sip_cmd_bss_info_update) +
> + sizeof(struct sip_hdr),
> + SIP_CMD_BSS_INFO_UPDATE);
> + if (!skb)
> + return -ENOMEM;
> +
> + bsscmd = (struct sip_cmd_bss_info_update *)(skb->data + sizeof(struct sip_hdr));
> +
> + if (assoc == 2) //hack for softAP mode
> + bsscmd->beacon_int = evif->beacon_interval;
> + else if (assoc == 1)
> + set_bit(ESP_WL_FLAG_CONNECT, &epub->wl.flags);
> + else
> + clear_bit(ESP_WL_FLAG_CONNECT, &epub->wl.flags);
> +
> + bsscmd->bssid_no = evif->index;
> + bsscmd->isassoc = assoc;
> + bsscmd->beacon_int = evif->beacon_interval;
> + memcpy(bsscmd->bssid, bssid, ETH_ALEN);
> +
> + return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
> +}
> +
> +int sip_send_wmm_params(struct esp_pub *epub, u8 aci,
> + const struct ieee80211_tx_queue_params *params)
> +{
> + struct sk_buff *skb;
> + struct sip_cmd_set_wmm_params *bsscmd;
> +
> + skb = sip_alloc_ctrl_skbuf(epub->sip,
> + sizeof(struct sip_cmd_set_wmm_params) +
> + sizeof(struct sip_hdr),
> + SIP_CMD_SET_WMM_PARAM);
> + if (!skb)
> + return -ENOMEM;
> +
> + bsscmd = (struct sip_cmd_set_wmm_params *)(skb->data + sizeof(struct sip_hdr));
> + bsscmd->aci = aci;
> + bsscmd->aifs = params->aifs;
> + bsscmd->txop_us = params->txop * 32;
> + bsscmd->ecw_min = 32 - __builtin_clz(params->cw_min);
> + bsscmd->ecw_max = 32 - __builtin_clz(params->cw_max);
> +
> + return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
> +}
> +
> +int sip_send_ampdu_action(struct esp_pub *epub, u8 action_num, const u8 *addr,
> + u16 tid, u16 ssn, u8 buf_size)
> +{
> + int index = 0;
> + struct sk_buff *skb;
> + struct sip_cmd_ampdu_action *action;
> +
> + if (action_num == SIP_AMPDU_RX_START)
> + index = esp_get_empty_rxampdu(epub, addr, tid);
> + else if (action_num == SIP_AMPDU_RX_STOP)
> + index = esp_get_exist_rxampdu(epub, addr, tid);
> +
> + if (index < 0)
> + return -EACCES;
> +
> + skb = sip_alloc_ctrl_skbuf(epub->sip,
> + sizeof(struct sip_cmd_ampdu_action) +
> + sizeof(struct sip_hdr),
> + SIP_CMD_AMPDU_ACTION);
> + if (!skb)
> + return -ENOMEM;
> +
> + action = (struct sip_cmd_ampdu_action *)(skb->data + sizeof(struct sip_hdr));
> + action->action = action_num;
> + //for TX, it means interface index
> + action->index = ssn;
> +
> + switch (action_num) {
> + case SIP_AMPDU_RX_START:
> + action->ssn = ssn;
> + case SIP_AMPDU_RX_STOP:
> + action->index = index;
> + case SIP_AMPDU_TX_OPERATIONAL:
> + case SIP_AMPDU_TX_STOP:
> + action->win_size = buf_size;
> + action->tid = tid;
> + memcpy(action->addr, addr, ETH_ALEN);
> + break;
> + }
> +
> + return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
> +}
> +
> +#ifdef HW_SCAN
> +/* send cmd to target, if aborted is true, inform target stop scan, report scan complete imediately
> + * return 1: complete over, 0: success, still have next scan, -1: hardware failure
> + */
> +int sip_send_scan(struct esp_pub *epub)
> +{
> + struct cfg80211_scan_request *scan_req = epub->wl.scan_req;
> + struct sk_buff *skb;
> + struct sip_cmd_scan *scancmd;
> + u8 *ptr;
> + int i;
> + u8 append_len, ssid_len;
> +
> + ESSERT(scan_req);
> + if (!scan_req->n_ssids)
> + ssid_len = 0;
> + else if (scan_req->n_ssids == 1)
> + ssid_len = scan_req->ssids->ssid_len;
> + /* Limit to two SSIDs */
> + else
> + ssid_len = scan_req->ssids->ssid_len +
> + (scan_req->ssids + 1)->ssid_len;
> +
> + append_len = ssid_len + scan_req->n_channels + scan_req->ie_len;
> +
> + skb = sip_alloc_ctrl_skbuf(epub->sip, sizeof(struct sip_cmd_scan) +
> + sizeof(struct sip_hdr) + append_len,
> + SIP_CMD_SCAN);
> +
> + if (!skb)
> + return -ENOMEM;
> +
> + ptr = skb->data;
> + scancmd = (struct sip_cmd_scan *)(ptr + sizeof(struct sip_hdr));
> +
> + ptr += sizeof(struct sip_hdr) + sizeof(struct sip_cmd_scan);
> + scancmd->aborted = false;
> +
> + /* FIXME: meh */
> + if (scan_req->n_ssids <= 0) {
> + scancmd->ssid_len = 0;
> + } else {
> + scancmd->ssid_len = ssid_len;
> + if (scan_req->n_ssids == 1)
> + memcpy(ptr, scan_req->ssids->ssid, scancmd->ssid_len);
> + /* Limit to two SSIDs */
> + else
> + memcpy(ptr, (scan_req->ssids + 1)->ssid,
> + scancmd->ssid_len);
> + }
> +
> + ptr += scancmd->ssid_len;
> + scancmd->n_channels = scan_req->n_channels;
> + for (i = 0; i < scan_req->n_channels; i++)
> + ptr[i] = scan_req->channels[i]->hw_value;
> +
> + ptr += scancmd->n_channels;
> + if (scan_req->ie_len && scan_req->ie) {
> + scancmd->ie_len = scan_req->ie_len;
> + memcpy(ptr, scan_req->ie, scan_req->ie_len);
> + } else {
> + scancmd->ie_len = 0;
> + }
> + //add a flag that support two ssids,
> + if (scan_req->n_ssids > 1)
> + scancmd->ssid_len |= 0x80;
> +
> + return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
> +}
> +#endif
> +
> +void sip_scandone_process(struct esp_sip *sip,
> + struct sip_evt_scan_report *scan_report)
> +{
> + struct esp_pub *epub = sip->epub;
> +
> + if (epub->wl.scan_req) {
> + hw_scan_done(epub, scan_report->aborted);
> + epub->wl.scan_req = NULL;
> + }
> +}
> +
> +int sip_send_setkey(struct esp_pub *epub, u8 bssid_no, u8 *peer_addr,
> + struct ieee80211_key_conf *key, u8 isvalid)
> +{
> + struct sip_cmd_setkey *setkeycmd;
> + struct sk_buff *skb;
> +
> + skb = sip_alloc_ctrl_skbuf(epub->sip, sizeof(struct sip_cmd_setkey) +
> + sizeof(struct sip_hdr), SIP_CMD_SETKEY);
> +
> + if (!skb)
> + return -ENOMEM;
> +
> + setkeycmd = (struct sip_cmd_setkey *)(skb->data + sizeof(struct sip_hdr));
> +
> + if (peer_addr)
> + memcpy(setkeycmd->addr, peer_addr, ETH_ALEN);
> + else
> + memset(setkeycmd->addr, 0, ETH_ALEN);
> +
> + setkeycmd->bssid_no = bssid_no;
> + setkeycmd->hw_key_idx = key->hw_key_idx;
> +
> + if (isvalid) {
> + setkeycmd->alg = esp_cipher2alg(key->cipher);
> + setkeycmd->keyidx = key->keyidx;
> + setkeycmd->keylen = key->keylen;
> + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) {
> + memcpy(setkeycmd->key, key->key, 16);
> + memcpy(setkeycmd->key + 16, key->key + 24, 8);
> + memcpy(setkeycmd->key + 24, key->key + 16, 8);
> + } else {
> + memcpy(setkeycmd->key, key->key, key->keylen);
> + }
> +
> + setkeycmd->flags = 1;
> + } else {
> + setkeycmd->flags = 0;
> + }
> +
> + return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
> +}
> +
> +//remain_on_channel
> +int sip_send_roc(struct esp_pub *epub, u16 center_freq, u16 duration)
> +{
> + struct sk_buff *skb;
> + struct sip_cmd_config *configcmd;
> +
> + skb = sip_alloc_ctrl_skbuf(epub->sip, sizeof(struct sip_cmd_config) +
> + sizeof(struct sip_hdr), SIP_CMD_CONFIG);
> + if (!skb)
> + return -ENOMEM;
> +
> + configcmd = (struct sip_cmd_config *)(skb->data + sizeof(struct sip_hdr));
> + configcmd->center_freq = center_freq;
> + configcmd->duration = duration;
> +
> + return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
> +}
> +
> +int sip_send_set_sta(struct esp_pub *epub, u8 ifidx, u8 set,
> + struct ieee80211_sta *sta, struct ieee80211_vif *vif,
> + u8 index)
> +{
> + struct sk_buff *skb;
> + struct sip_cmd_setsta *setstacmd;
> +
> + skb = sip_alloc_ctrl_skbuf(epub->sip, sizeof(struct sip_cmd_setsta) +
> + sizeof(struct sip_hdr), SIP_CMD_SETSTA);
> + if (!skb)
> + return -ENOMEM;
> +
> + setstacmd = (struct sip_cmd_setsta *)(skb->data + sizeof(struct sip_hdr));
> + setstacmd->ifidx = ifidx;
> + setstacmd->index = index;
> + setstacmd->set = set;
> +
> + if (!sta->aid)
> + setstacmd->aid = vif->bss_conf.aid;
> + else
> + setstacmd->aid = sta->aid;
> +
> + memcpy(setstacmd->mac, sta->addr, ETH_ALEN);
> +
> + if (!set)
> + goto send;
> +
> + if (sta->ht_cap.ht_supported) {
> + if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20)
> + setstacmd->phymode = ESP_IEEE80211_T_HT20_S;
> + else
> + setstacmd->phymode = ESP_IEEE80211_T_HT20_L;
> + setstacmd->ampdu_factor = sta->ht_cap.ampdu_factor;
> + setstacmd->ampdu_density = sta->ht_cap.ampdu_density;
> + goto send;
> + }
> +
> + if (sta->supp_rates[NL80211_BAND_2GHZ] & ~CONF_HW_BIT_RATE_11B_MASK)
> + setstacmd->phymode = ESP_IEEE80211_T_OFDM;
> + else
> + setstacmd->phymode = ESP_IEEE80211_T_CCK;
> +
> +send:
> + return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
> +}
> +
> +int sip_send_recalc_credit(struct esp_pub *epub)
> +{
> + struct sk_buff *skb;
> +
> + skb = sip_alloc_ctrl_skbuf(epub->sip, sizeof(struct sip_hdr),
> + SIP_CMD_RECALC_CREDIT);
> + if (!skb)
> + return -ENOMEM;
> +
> + return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_HEAD);
> +}
> +
> +int sip_cmd(struct esp_pub *epub, enum sip_cmd_id cmd_id, u8 *cmd_buf,
> + u8 cmd_len)
> +{
> + struct sk_buff *skb;
> +
> + skb = sip_alloc_ctrl_skbuf(epub->sip, cmd_len + sizeof(struct sip_hdr),
> + cmd_id);
> + if (!skb)
> + return -ENOMEM;
> +
> + memcpy(skb->data + sizeof(struct sip_hdr), cmd_buf, cmd_len);
> +
> + return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
> +}
> diff --git a/drivers/staging/esp8089/esp_ctrl.h b/drivers/staging/esp8089/esp_ctrl.h
> new file mode 100644
> index 000000000000..b9143bb135b3
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_ctrl.h
> @@ -0,0 +1,48 @@
> +/*
> + * Copyright (c) 2009 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + * Quentin Schulz <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + */
> +#ifndef _ESP_CTRL_H_
> +#define _ESP_CTRL_H_
> +
> +extern struct completion *gl_bootup_cplx;
> +
> +int sip_send_config(struct esp_pub *epub, struct ieee80211_conf *conf);
> +
> +int sip_send_setkey(struct esp_pub *epub, u8 bssid_no, u8 *peer_addr,
> + struct ieee80211_key_conf *key, u8 isvalid);
> +
> +int sip_send_scan(struct esp_pub *epub);
> +
> +void sip_scandone_process(struct esp_sip *sip,
> + struct sip_evt_scan_report *scan_report);
> +
> +int sip_send_bss_info_update(struct esp_pub *epub, struct esp_vif *evif,
> + u8 *bssid, int assoc);
> +
> +int sip_send_wmm_params(struct esp_pub *epub, u8 aci,
> + const struct ieee80211_tx_queue_params *params);
> +
> +int sip_send_ampdu_action(struct esp_pub *epub, u8 action_num, const u8 *addr,
> + u16 tid, u16 ssn, u8 buf_size);
> +
> +int sip_send_roc(struct esp_pub *epub, u16 center_freq, u16 duration);
> +
> +int sip_send_set_sta(struct esp_pub *epub, u8 ifidx, u8 set,
> + struct ieee80211_sta *sta, struct ieee80211_vif *vif,
> + u8 index);
> +
> +int sip_parse_events(struct esp_sip *sip, u8 *buf);
> +
> +int sip_send_recalc_credit(struct esp_pub *epub);
> +
> +int sip_cmd(struct esp_pub *epub, enum sip_cmd_id cmd_id, u8 *cmd_buf,
> + u8 cmd_len);
> +
> +#endif /* _ESP_CTRL_H_ */
> diff --git a/drivers/staging/esp8089/esp_debug.c b/drivers/staging/esp8089/esp_debug.c
> new file mode 100644
> index 000000000000..26472b433768
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_debug.c
> @@ -0,0 +1,247 @@
> +/*
> + * Copyright (c) 2011 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + * Quentin Schulz <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + */
> +
> +#include <linux/types.h>
> +#include <linux/kernel.h>
> +
> +#include <net/mac80211.h>
> +#include "sip2_common.h"
> +
> +#include "esp_debug.h"
> +
> +#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_ESP8089_DEBUG_FS)
> +
> +static struct dentry *esp_debugfs_root;
> +
> +static ssize_t esp_debugfs_read(struct file *filp, char __user *buffer,
> + size_t count, loff_t *ppos)
> +{
> + if (*ppos >= 32)
> + return 0;
> +
> + if (*ppos + count > 32)
> + count = 32 - *ppos;
> +
> + if (copy_to_user(buffer, filp->private_data + *ppos, count))
> + return -EFAULT;
> +
> + *ppos += count;
> +
> + return count;
> +}
> +
> +static ssize_t esp_debugfs_write(struct file *filp, const char __user *buffer,
> + size_t count, loff_t *ppos)
> +{
> + if (*ppos >= 32)
> + return 0;
> +
> + if (*ppos + count > 32)
> + count = 32 - *ppos;
> +
> + if (copy_from_user(filp->private_data + *ppos, buffer, count))
> + return -EFAULT;
> +
> + *ppos += count;
> +
> + return count;
> +}
> +
> +const struct file_operations esp_debugfs_fops = {
> + .owner = THIS_MODULE,
> + .open = simple_open,
> + .read = esp_debugfs_read,
> + .write = esp_debugfs_write,
> +};
> +
> +struct dentry *esp_dump_var(const char *name, struct dentry *parent,
> + void *value, enum esp_type type)
> +{
> + struct dentry *rc = NULL;
> + umode_t mode = 0644;
> +
> + if (!esp_debugfs_root)
> + return NULL;
> +
> + if (!parent)
> + parent = esp_debugfs_root;
> +
> + switch (type) {
> + case ESP_U8:
> + rc = debugfs_create_u8(name, mode, parent, (u8 *)value);
> + break;
> + case ESP_U16:
> + rc = debugfs_create_u16(name, mode, parent, (u16 *)value);
> + break;
> + case ESP_U32:
> + rc = debugfs_create_u32(name, mode, parent, (u32 *)value);
> + break;
> + case ESP_U64:
> + rc = debugfs_create_u64(name, mode, parent, (u64 *)value);
> + break;
> + case ESP_BOOL:
> + rc = debugfs_create_bool(name, mode, parent, (bool *)value);
> + break;
> + default: //32
> + rc = debugfs_create_u32(name, mode, parent, (u32 *)value);
> + }
> +
> + if (!rc)
> + goto _fail;
> +
> + return rc;
> +
> +_fail:
> + debugfs_remove_recursive(esp_debugfs_root);
> + esp_debugfs_root = NULL;
> + printk("%s failed, debugfs root removed; var name: %s\n", __func__,
> + name);
> + return NULL;
> +}
> +
> +struct dentry *esp_dump_array(const char *name, struct dentry *parent,
> + struct debugfs_blob_wrapper *blob)
> +{
> + struct dentry *rc;
> + umode_t mode = 0644;
> +
> + if (!esp_debugfs_root)
> + return NULL;
> +
> + if (!parent)
> + parent = esp_debugfs_root;
> +
> + rc = debugfs_create_blob(name, mode, parent, blob);
> + if (!rc)
> + goto _fail;
> +
> + return rc;
> +
> +_fail:
> + debugfs_remove_recursive(esp_debugfs_root);
> + esp_debugfs_root = NULL;
> + printk("%s failed, debugfs root removed; var name: %s\n", __func__,
> + name);
> + return NULL;
> +}
> +
> +struct dentry *esp_dump(const char *name, struct dentry *parent,
> + void *data, int size)
> +{
> + struct dentry *rc;
> + umode_t mode = 0644;
> +
> + if (!esp_debugfs_root)
> + return NULL;
> +
> + if (!parent)
> + parent = esp_debugfs_root;
> +
> + rc = debugfs_create_file(name, mode, parent, data, &esp_debugfs_fops);
> + if (!rc)
> + goto _fail;
> +
> + return rc;
> +
> +_fail:
> + debugfs_remove_recursive(esp_debugfs_root);
> + esp_debugfs_root = NULL;
> + printk("%s failed, debugfs root removed; var name: %s\n", __func__,
> + name);
> + return NULL;
> +}
> +
> +struct dentry *esp_debugfs_add_sub_dir(const char *name)
> +{
> + struct dentry *sub_dir;
> +
> + sub_dir = debugfs_create_dir(name, esp_debugfs_root);
> + if (!sub_dir)
> + goto _fail;
> +
> + return sub_dir;
> +
> +_fail:
> + debugfs_remove_recursive(esp_debugfs_root);
> + esp_debugfs_root = NULL;
> + printk("%s failed, debugfs root removed; dir name: %s\n", __func__,
> + name);
> + return NULL;
> +}
> +
> +int esp_debugfs_init(void)
> +{
> + esp_debugfs_root = debugfs_create_dir("esp_debug", NULL);
> +
> + if (IS_ERR_OR_NULL(esp_debugfs_root))
> + return -ENOENT;
> +
> + return 0;
> +}
> +
> +void esp_debugfs_exit(void)
> +{
> + debugfs_remove_recursive(esp_debugfs_root);
> +}
> +
> +#else
> +
> +inline struct dentry *esp_dump_var(const char *name, struct dentry *parent,
> + void *value, enum esp_type type)
> +{
> + return NULL;
> +}
> +
> +inline struct dentry *esp_dump_array(const char *name, struct dentry *parent,
> + struct debugfs_blob_wrapper *blob)
> +{
> + return NULL;
> +}
> +
> +inline struct dentry *esp_dump(const char *name, struct dentry *parent,
> + void *data, int size)
> +{
> + return NULL;
> +}
> +
> +struct dentry *esp_debugfs_add_sub_dir(const char *name)
> +{
> + return NULL;
> +}
> +
> +inline int esp_debugfs_init(void)
> +{
> + return -EPERM;
> +}
> +
> +inline void esp_debugfs_exit(void)
> +{
> +}
> +
> +#endif
> +
> +/* FIXME: What's the actual usecase? */
> +void show_buf(u8 *buf, u32 len)
> +{
> + int i = 0, j;
> +
> + printk(KERN_INFO "\n++++++++++++++++show rbuf+++++++++++++++\n");
> + for (i = 0; i < (len / 16); i++) {
> + j = i * 16;
> + printk(KERN_INFO
> + "0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
> + buf[j], buf[j + 1], buf[j + 2], buf[j + 3],
> + buf[j + 4], buf[j + 5], buf[j + 6], buf[j + 7],
> + buf[j + 8], buf[j + 9], buf[j + 10], buf[j + 11],
> + buf[j + 12], buf[j + 13], buf[j + 14], buf[j + 15]);
> + }
> + printk(KERN_INFO "\n++++++++++++++++++++++++++++++++++++++++\n");
> +}
> diff --git a/drivers/staging/esp8089/esp_debug.h b/drivers/staging/esp8089/esp_debug.h
> new file mode 100644
> index 000000000000..eb9bddfe9842
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_debug.h
> @@ -0,0 +1,69 @@
> +/*
> + * Copyright (c) 2011 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + * Quentin Schulz <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + */
> +
> +#ifndef _DEBUG_H_
> +
> +#ifdef ASSERT_PANIC
> +#define ESSERT(v) BUG_ON(!(v))
> +#else
> +#define ESSERT(v) if(!(v)) printk("ESSERT:%s %d\n", __FILE__, __LINE__)
> +#endif
> +
> +#include <linux/slab.h>
> +#include <linux/debugfs.h>
> +#include <linux/uaccess.h>
> +
> +enum esp_type {
> + ESP_BOOL,
> + ESP_U8,
> + ESP_U16,
> + ESP_U32,
> + ESP_U64
> +};
> +
> +struct dentry *esp_dump_var(const char *name, struct dentry *parent,
> + void *value, enum esp_type type);
> +
> +struct dentry *esp_dump_array(const char *name, struct dentry *parent,
> + struct debugfs_blob_wrapper *blob);
> +
> +struct dentry *esp_dump(const char *name, struct dentry *parent, void *data,
> + int size);
> +
> +struct dentry *esp_debugfs_add_sub_dir(const char *name);
> +
> +int esp_debugfs_init(void);
> +
> +void esp_debugfs_exit(void);
> +
> +enum {
> + ESP_DBG_ERROR = BIT(0),
> + ESP_DBG_TRACE = BIT(1),
> + ESP_DBG_LOG = BIT(2),
> + ESP_DBG = BIT(3),
> + ESP_SHOW = BIT(4),
> + ESP_DBG_TXAMPDU = BIT(5),
> + ESP_DBG_OP = BIT(6),
> + ESP_DBG_PS = BIT(7),
> + ESP_ATE = BIT(8),
> + ESP_DBG_ALL = GENMASK(31, 0)
> +};
> +
> +extern unsigned int esp_msg_level;
> +
> +#define esp_dbg(mask, fmt, args...) do { \
> + if (esp_msg_level & mask) \
> + printk(fmt, ##args); \
> +} while (0)
> +
> +void show_buf(u8 *buf, u32 len);
> +
> +#endif /* _DEBUG_H_ */
> diff --git a/drivers/staging/esp8089/esp_file.c b/drivers/staging/esp8089/esp_file.c
> new file mode 100644
> index 000000000000..7c7ef0d83693
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_file.c
> @@ -0,0 +1,221 @@
> +/*
> + * Copyright (c) 2010 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + * Quentin Schulz <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + */
> +
> +#include <linux/fs.h>
> +#include <linux/vmalloc.h>
> +#include <linux/kernel.h>
> +#include <linux/moduleparam.h>
> +#include <linux/firmware.h>
> +#include <linux/netdevice.h>
> +#include <linux/aio.h>
> +#include <linux/property.h>
> +
> +#include "esp_file.h"
> +#include "esp_debug.h"
> +#include "esp_sif.h"
> +
> +/* TODO use proper module param for each value instead of a big one */
> +static char *modparam_init_data_conf;
> +module_param_named(config, modparam_init_data_conf, charp, 0444);
> +MODULE_PARM_DESC(config, "Firmware init config string (format: key=value;)");
> +
> +struct esp_init_table_elem esp_init_table[MAX_ATTR_NUM] = {
> + /* Crystal type:
> + * 0: 40MHz (default)
> + * 1: 26MHz (ESP8266 ESP-12F)
> + */
> + {"crystal_26M_en", 48, 0},
> + /* Output crystal clock to pin:
> + * 0: None
> + * 1: GPIO1
> + * 2: URXD0
> + */
> + {"test_xtal", 49, 0},
> + /* Host SDIO mode:
> + * 0: Auto by pin strapping
> + * 1: SDIO data output on negative edges (SDIO v1.1)
> + * 2: SDIO data output on positive edges (SDIO v2.0)
> + */
> + {"sdio_configure", 50, 2},
> + /* WiFi/Bluetooth co-existence with BK3515A BT chip
> + * 0: None
> + * 1: GPIO0->WLAN_ACTIVE, MTMS->BT_ACTIVE, MTDI->BT_PRIORITY,
> + * U0TXD->ANT_SEL_BT, U0RXD->ANT_SEL_WIFI
> + */
> + {"bt_configure", 51, 0},
> + /* Antenna selection:
> + * 0: Antenna is for WiFi
> + * 1: Antenna is for Bluetooth
> + */
> + {"bt_protocol", 52, 0},
> + /* Dual antenna configuration mode:
> + * 0: None
> + * 1: U0RXD + XPD_DCDC
> + * 2: U0RXD + GPIO0
> + * 3: U0RXD + U0TXD
> + */
> + {"dual_ant_configure", 53, 0},
> + /* Firmware debugging output pin:
> + * 0: None
> + * 1: UART TX on GPIO2
> + * 2: UART TX on U0TXD
> + */
> + {"test_uart_configure", 54, 2},
> + /* Whether to share crystal clock with BT (in sleep mode):
> + * 0: no
> + * 1: always on
> + * 2: automatically on according to XPD_DCDC
> + */
> + {"share_xtal", 55, 0},
> + /* Allow chip to be woken up during sleep on pin:
> + * 0: None
> + * 1: XPD_DCDC
> + * 2: GPIO0
> + * 3: Both XPD_DCDC and GPIO0
> + */
> + {"gpio_wake", 56, 0},
> + {"no_auto_sleep", 57, 0},
> + {"speed_suspend", 58, 0},
> + {"attr11", -1, -1},
> + {"attr12", -1, -1},
> + {"attr13", -1, -1},
> + {"attr14", -1, -1},
> + {"attr15", -1, -1},
> + //attr that is not send to target
> + /* Allow chip to be reset by GPIO pin:
> + * 0: no
> + * 1: yes
> + */
> + {"ext_rst", -1, 0},
> + {"wakeup_gpio", -1, 12},
> + {"ate_test", -1, 0},
> + {"attr19", -1, -1},
> + {"attr20", -1, -1},
> + {"attr21", -1, -1},
> + {"attr22", -1, -1},
> + {"attr23", -1, -1},
> +};
> +
> +/* update init config table */
> +static int set_init_config_attr(const char *attr, int attr_len, short value)
> +{
> + int i;
> +
> + for (i = 0; i < MAX_ATTR_NUM; i++)
> + if (!memcmp(esp_init_table[i].attr, attr, attr_len)) {
> + if (value < 0 || value > 255) {
> + printk("%s: attribute value for %s is out of range",
> + __func__, esp_init_table[i].attr);
> + return -1;
> + }
> +
> + esp_init_table[i].value = value;
> + return 0;
> + }
> +
> + return -1;
> +}
> +
> +static int update_init_config_attr(const char *attr, int attr_len,
> + const char *val, int val_len)
> +{
> + char digits[4];
> + short value;
> + int i;
> +
> + for (i = 0; i < sizeof(digits) - 1 && i < val_len; i++)
> + digits[i] = val[i];
I don't think we really need to memcpy() val to digits[].
> +
> + digits[i] = 0;
> +
> + if (kstrtou16(digits, 10, &value) < 0) {
> + printk("%s: invalid attribute value: %s", __func__, digits);
> + return -1;
propogate the error code.
> + }
> +
> + return set_init_config_attr(attr, attr_len, value);
> +}
> +
> +/* export config table settings to SDIO driver */
> +static void record_init_config(void)
> +{
> + int i;
> +
> + for (i = 0; i < MAX_ATTR_NUM; i++) {
> + if (esp_init_table[i].value < 0)
> + continue;
> +
> + if (!strcmp(esp_init_table[i].attr, "share_xtal"))
> + sif_record_bt_config(esp_init_table[i].value);
> + else if (!strcmp(esp_init_table[i].attr, "ext_rst"))
> + sif_record_rst_config(esp_init_table[i].value);
> + else if (!strcmp(esp_init_table[i].attr, "wakeup_gpio"))
> + sif_record_wakeup_gpio_config(esp_init_table[i].value);
> + else if (!strcmp(esp_init_table[i].attr, "ate_test"))
> + sif_record_ate_config(esp_init_table[i].value);
> + }
> +}
> +
> +int request_init_conf(struct device *dev)
> +{
> + char *attr, *str, *p;
> + int attr_len, str_len;
> + int ret = 0;
> + u32 val;
> +
> + /* Check for any parameters passed through devicetree (or acpi) */
> + if (!device_property_read_u32(dev, "esp,crystal-26M-en", &val))
> + set_init_config_attr("crystal_26M_en", strlen("crystal_26M_en"),
> + val);
> +
> + /* parse optional parameter in the form of key1=value,key2=value,.. */
> + attr = NULL;
> + attr_len = 0;
> + str_len = 0;
> + for (p = str = modparam_init_data_conf; p && *p; p++) {
> + if (*p == '=') {
> + attr = str;
> + attr_len = str_len;
> +
> + str = p + 1;
> + str_len = 0;
> + } else if (*p == ',' || *p == ';') {
> + if (attr_len)
> + ret |= update_init_config_attr(attr, attr_len,
> + str, str_len);
> +
> + str = p + 1;
> + attr_len = 0;
> + str_len = 0;
> + } else {
> + str_len++;
> + }
> + }
> +
> + if (attr_len && str != attr)
> + ret |= update_init_config_attr(attr, attr_len, str, str_len);
> +
> + record_init_config();
> +
> + return ret;
> +}
> +
> +void fix_init_data(u8 *init_data_buf, int buf_size)
> +{
> + int i;
> +
> + for (i = 0; i < MAX_FIX_ATTR_NUM; i++) {
> + if (esp_init_table[i].offset > -1 &&
> + esp_init_table[i].offset < buf_size &&
> + esp_init_table[i].value > -1)
> + *(u8 *)(init_data_buf + esp_init_table[i].offset) = esp_init_table[i].value;
> + }
> +}
> diff --git a/drivers/staging/esp8089/esp_file.h b/drivers/staging/esp8089/esp_file.h
> new file mode 100644
> index 000000000000..ba9946d64c32
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_file.h
> @@ -0,0 +1,30 @@
> +/*
> + * Copyright (c) 2010 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + * Quentin Schulz <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + */
> +
> +#ifndef _ESP_FILE_H_
> +#define _ESP_FILE_H_
> +
> +#include <linux/firmware.h>
> +
> +#define CONF_ATTR_LEN 24
> +#define MAX_ATTR_NUM 24
> +#define MAX_FIX_ATTR_NUM 16
> +
> +struct esp_init_table_elem {
> + char attr[CONF_ATTR_LEN];
> + int offset;
> + short value;
> +};
> +
> +int request_init_conf(struct device *dev);
> +void fix_init_data(u8 *init_data_buf, int buf_size);
> +
> +#endif /* _ESP_FILE_H_ */
> diff --git a/drivers/staging/esp8089/esp_init_data.h b/drivers/staging/esp8089/esp_init_data.h
> new file mode 100644
> index 000000000000..2494c02a10be
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_init_data.h
> @@ -0,0 +1,17 @@
> +/*
> + * Copyright (c) 2009 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + * Quentin Schulz <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + */
> +
> +static char esp_init_data[] = { 0x5, 0x0, 4, 2, 5, 5, 5, 2, 5, 0, 4, 5, 5, 4, 5, 5, 4, -2, -3, -1,
> + -16, -16, -16, -32, -32, -32, 204, 1, 0xff, 0xff, 0, 0, 0, 0, 82, 78, 74, 68, 64, 56, 0,
> + 0, 1, 1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 10, 0x0, 0x0,
> + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> + 0 };
> diff --git a/drivers/staging/esp8089/esp_io.c b/drivers/staging/esp8089/esp_io.c
> new file mode 100644
> index 000000000000..756bd6260b24
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_io.c
> @@ -0,0 +1,294 @@
> +/*
> + * Copyright (c) 2009 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + * Quentin Schulz <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + */
> +
> +#include <linux/mmc/sdio_func.h>
> +#include "esp_sif.h"
> +#include "slc_host_register.h"
> +#include "esp_debug.h"
> +
> +int esp_common_read(struct esp_pub *epub, u8 *buf, u32 len, int sync,
> + bool noround)
> +{
> + if (sync)
> + return sif_lldesc_read_sync(epub, buf, len);
> +
> + return sif_lldesc_read_raw(epub, buf, len, noround);
> +}
> +
> +int esp_common_write(struct esp_pub *epub, u8 *buf, u32 len, int sync)
> +{
> + if (sync)
> + return sif_lldesc_write_sync(epub, buf, len);
> +
> + return sif_lldesc_write_raw(epub, buf, len);
> +}
> +
> +int esp_common_read_with_addr(struct esp_pub *epub, u32 addr, u8 *buf, u32 len,
> + int sync)
> +{
> + if (sync)
> + return sif_io_sync(epub, addr, buf, len, SIF_FROM_DEVICE |
> + SIF_SYNC | SIF_BYTE_BASIS | SIF_INC_ADDR);
> +
> + return sif_io_raw(epub, addr, buf, len, SIF_FROM_DEVICE |
> + SIF_BYTE_BASIS | SIF_INC_ADDR);
> +}
> +
> +int esp_common_write_with_addr(struct esp_pub *epub, u32 addr, u8 *buf,
> + u32 len, int sync)
> +{
> + if (sync)
> + return sif_io_sync(epub, addr, buf, len, SIF_TO_DEVICE |
> + SIF_SYNC | SIF_BYTE_BASIS | SIF_INC_ADDR);
> +
> + return sif_io_raw(epub, addr, buf, len, SIF_TO_DEVICE | SIF_BYTE_BASIS |
> + SIF_INC_ADDR);
> +}
> +
> +int esp_common_readbyte_with_addr(struct esp_pub *epub, u32 addr, u8 *buf,
> + int sync)
> +{
> + int res;
> +
> + if (sync)
> + sif_lock_bus(epub);
> +
> + *buf = sdio_io_readb(epub, addr, &res);
> +
> + if (sync)
> + sif_unlock_bus(epub);
> +
> + return res;
> +}
> +
> +int esp_common_writebyte_with_addr(struct esp_pub *epub, u32 addr, u8 buf,
> + int sync)
> +{
> + int res;
> +
> + if (sync)
> + sif_lock_bus(epub);
> +
> + sdio_io_writeb(epub, buf, addr, &res);
> +
> + if (sync)
> + sif_unlock_bus(epub);
> +
> + return res;
> +}
> +
> +enum _SDIO_INTR_MODE {
> + SDIO_INTR_IB = 0,
> + SDIO_INTR_OOB_TOGGLE,
> + SDIO_INTR_OOB_HIGH_LEVEL,
> + SDIO_INTR_OOB_LOW_LEVEL,
> +};
> +
> +#define GEN_GPIO_SEL(_gpio_num, _sel_func, _intr_mode, _offset) (((_offset) << 9) | ((_intr_mode) << 7) | ((_sel_func) << 4) | (_gpio_num))
> +//bit[3:0] = gpio num, 2
> +//bit[6:4] = gpio sel func, 0
> +//bit[8:7] = gpio intr mode, SDIO_INTR_OOB_TOGGLE
> +//bit[15:9] = register offset, 0x38
> +
> +u16 gpio_sel_sets[17] = {
> + GEN_GPIO_SEL(0, 0, SDIO_INTR_OOB_TOGGLE, 0x34), //GPIO0
> + GEN_GPIO_SEL(1, 3, SDIO_INTR_OOB_TOGGLE, 0x18), //U0TXD
> + GEN_GPIO_SEL(2, 0, SDIO_INTR_OOB_TOGGLE, 0x38), //GPIO2
> + GEN_GPIO_SEL(3, 3, SDIO_INTR_OOB_TOGGLE, 0x14), //U0RXD
> + GEN_GPIO_SEL(4, 0, SDIO_INTR_OOB_TOGGLE, 0x3C), //GPIO4
> + GEN_GPIO_SEL(5, 0, SDIO_INTR_OOB_TOGGLE, 0x40), //GPIO5
> + GEN_GPIO_SEL(6, 3, SDIO_INTR_OOB_TOGGLE, 0x1C), //SD_CLK
> + GEN_GPIO_SEL(7, 3, SDIO_INTR_OOB_TOGGLE, 0x20), //SD_DATA0
> + GEN_GPIO_SEL(8, 3, SDIO_INTR_OOB_TOGGLE, 0x24), //SD_DATA1
> + GEN_GPIO_SEL(9, 3, SDIO_INTR_OOB_TOGGLE, 0x28), //SD_DATA2
> + GEN_GPIO_SEL(10, 3, SDIO_INTR_OOB_TOGGLE, 0x2C), //SD_DATA3
> + GEN_GPIO_SEL(11, 3, SDIO_INTR_OOB_TOGGLE, 0x30), //SD_CMD
> + GEN_GPIO_SEL(12, 3, SDIO_INTR_OOB_TOGGLE, 0x04), //MTDI
> + GEN_GPIO_SEL(13, 3, SDIO_INTR_OOB_TOGGLE, 0x08), //MTCK
> + GEN_GPIO_SEL(14, 3, SDIO_INTR_OOB_TOGGLE, 0x0C), //MTMS
> + GEN_GPIO_SEL(15, 3, SDIO_INTR_OOB_TOGGLE, 0x10), //MTDO
> + //pls do not change sel before, if you want to change intr mode,change the one blow
> + //GEN_GPIO_SEL(2, 0, SDIO_INTR_OOB_TOGGLE, 0x38)
> + GEN_GPIO_SEL(2, 0, SDIO_INTR_OOB_LOW_LEVEL, 0x38)
> +};
> +
> +int sif_interrupt_target(struct esp_pub *epub, u8 index)
> +{
> + u8 low_byte = BIT(index);
> + return esp_common_writebyte_with_addr(epub, SLC_HOST_CONF_W4 + 2,
> + low_byte, ESP_SIF_NOSYNC);
> +}
> +
> +void check_target_id(struct esp_pub *epub)
> +{
> + u32 date;
> + int i;
> + u16 gpio_sel;
> + u8 byte2 = 0, byte3 = 0;
> +
> + if (!epub || !epub->sif)
> + return;
> +
> + sif_lock_bus(epub);
> +
> + for (i = 0; i < 4; i++) {
> + esp_common_readbyte_with_addr(epub, SLC_HOST_DATE + i,
> + (u8 *)&date + i, ESP_SIF_NOSYNC);
> + esp_common_readbyte_with_addr(epub, SLC_HOST_ID + i,
> + (u8 *)&epub->sif->target_id + i,
> + ESP_SIF_NOSYNC);
> + }
> +
> + sif_unlock_bus(epub);
> +
> + switch (epub->sif->target_id) {
> + case 0x100:
> + epub->sif->slc_window_end_addr = 0x20000;
> + break;
> + case 0x600:
> + epub->sif->slc_window_end_addr = 0x20000 - 0x800;
> +
> + if (sif_get_bt_config() == 1 && sif_get_rst_config() != 1) {
> + u8 gpio_num = sif_get_wakeup_gpio_config();
> + gpio_sel = gpio_sel_sets[gpio_num];
> + byte2 = gpio_sel;
> + byte3 = gpio_sel >> 8;
> + }
> +
> + sif_lock_bus(epub);
> + esp_common_writebyte_with_addr(epub, SLC_HOST_CONF_W1, 0,
> + ESP_SIF_NOSYNC);
> + esp_common_writebyte_with_addr(epub, SLC_HOST_CONF_W1 + 1, 0,
> + ESP_SIF_NOSYNC);
> + esp_common_writebyte_with_addr(epub, SLC_HOST_CONF_W1 + 2,
> + byte2, ESP_SIF_NOSYNC);
> + esp_common_writebyte_with_addr(epub, SLC_HOST_CONF_W1 + 3,
> + byte3, ESP_SIF_NOSYNC);
> + sif_unlock_bus(epub);
> + break;
> + default:
> + epub->sif->slc_window_end_addr = 0x20000;
> + break;
> + }
> +}
> +
> +u32 sif_get_blksz(struct esp_pub *epub)
> +{
> + if (!epub || !epub->sif)
> + return 512;
> +
> + return epub->sif->slc_blk_sz;
> +}
> +
> +void sif_dsr(struct sdio_func *func)
> +{
> + struct esp_sdio_ctrl *sctrl = sdio_get_drvdata(func);
> + struct slc_host_regs *regs = &sctrl->slc_regs;
> + int ret;
> +
> + sdio_release_host(sctrl->func);
> +
> + sif_lock_bus(sctrl->epub);
> + memset(regs, 0, sizeof(struct slc_host_regs));
> +
> + ret = esp_common_read_with_addr(sctrl->epub, REG_SLC_HOST_BASE + 8,
> + (u8 *)regs, sizeof(*regs),
> + ESP_SIF_NOSYNC);
> +
> + if (regs->intr_raw & SLC_HOST_RX_ST && !ret)
> + esp_dsr(sctrl->epub);
> + else
> + sif_unlock_bus(sctrl->epub);
> +
> + /* FIXME: missing unlock_bus? */
I think the bus mostly gets unlocked in esp_dsr() but it's not clear
how that happens on the success path. But that's the common path so
it must happen. I don't like that we mix sif_lock_bus() and
sdio_claim_host(). I guess I would prefer that we could get rid of
sif_lock_bus() because we should be locking instead of saying "We have
too many NULL pointers so let's forget about locking for now."
> + sdio_claim_host(func);
> + atomic_set(&sctrl->irq_handling, 0);
> +}
> +
> +struct slc_host_regs *sif_get_regs(struct esp_pub *epub)
> +{
> + if (!epub || !epub->sif)
> + return NULL;
> +
> + return &epub->sif->slc_regs;
> +}
> +
> +void sif_disable_target_interrupt(struct esp_pub *epub)
> +{
> + if (!epub || !epub->sif || !epub->sif->func)
> + return;
> +
> + sif_lock_bus(epub);
> +#ifdef HOST_RESET_BUG
> + mdelay(10);
> +#endif
> + memset(epub->sif->dma_buffer, 0, sizeof(u32));
> + esp_common_write_with_addr(epub, SLC_HOST_INT_ENA,
> + epub->sif->dma_buffer, sizeof(u32),
> + ESP_SIF_NOSYNC);
> +#ifdef HOST_RESET_BUG
> + mdelay(10);
> +#endif
> +
> + sif_unlock_bus(epub);
> +
> + mdelay(1);
> +
> + sif_lock_bus(epub);
> + sif_interrupt_target(epub, 7);
> + sif_unlock_bus(epub);
> +}
> +
> +/* FIXME: MEH */
> +
> +static int bt_config;
> +void sif_record_bt_config(int value)
> +{
> + bt_config = value;
> +}
> +
> +int sif_get_bt_config(void)
> +{
> + return bt_config;
> +}
> +
> +static int rst_config;
> +void sif_record_rst_config(int value)
> +{
> + rst_config = value;
> +}
> +
> +int sif_get_rst_config(void)
> +{
> + return rst_config;
> +}
> +
> +static int ate_test;
> +void sif_record_ate_config(int value)
> +{
> + ate_test = value;
> +}
> +
> +int sif_get_ate_config(void)
> +{
> + return ate_test;
> +}
> +
> +static int wakeup_gpio = 12;
> +void sif_record_wakeup_gpio_config(int value)
> +{
> + wakeup_gpio = value;
> +}
> +
> +int sif_get_wakeup_gpio_config(void)
> +{
> + return wakeup_gpio;
> +}
> diff --git a/drivers/staging/esp8089/esp_mac80211.c b/drivers/staging/esp8089/esp_mac80211.c
> new file mode 100644
> index 000000000000..fd5049fc1f6b
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_mac80211.c
> @@ -0,0 +1,1496 @@
> +/*
> + * Copyright (c) 2011 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + * Quentin Schulz <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + */
> +
> +#include <linux/etherdevice.h>
> +#include <linux/workqueue.h>
> +#include <linux/nl80211.h>
> +#include <linux/ieee80211.h>
> +#include <linux/slab.h>
> +#include <net/cfg80211.h>
> +#include <net/mac80211.h>
> +#include <net/regulatory.h>
> +#include "esp_pub.h"
> +#include "esp_sip.h"
> +#include "esp_ctrl.h"
> +#include "esp_sif.h"
> +#include "esp_debug.h"
> +#include "esp_wl.h"
> +#include "esp_utils.h"
> +
> +static u8 esp_mac_addr[ETH_ALEN * 2];
> +static u8 getaddr_index(u8 *addr, struct esp_pub *epub);
> +
> +/*Handler that 802.11 module calls for each transmitted frame.
> +skb contains the buffer starting from the IEEE 802.11 header.
> +The low-level driver should send the frame out based on
> +configuration in the TX control data. This handler should,
> +preferably, never fail and stop queues appropriately.
> +Must be atomic.*/
> +static void esp_op_tx(struct ieee80211_hw *hw,
> + struct ieee80211_tx_control *control, struct sk_buff *skb)
> +{
> + struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +
> + sip_tx_data_pkt_enqueue(epub, skb);
> + if (epub)
> + ieee80211_queue_work(hw, &epub->tx_work);
> +}
> +
> +/*
> + Called before the first netdevice attached to the hardware
> + 2934 * is enabled. This should turn on the hardware and must turn on
> + 2935 * frame reception (for possibly enabled monitor interfaces.)
> + 2936 * Returns negative error codes, these may be seen in userspace,
> + 2937 * or zero.
> + 2938 * When the device is started it should not have a MAC address
> + 2939 * to avoid acknowledging frames before a non-monitor device
> + 2940 * is added.
> + 2941 * Must be implemented and can sleep.
> +*/
> +static int esp_op_start(struct ieee80211_hw *hw)
> +{
> + struct esp_pub *epub;
> +
> + if (!hw || !hw->priv) {
> + return -EINVAL;
> + }
> +
> + epub = (struct esp_pub *)hw->priv;
> +
> + /*add rfkill poll function */
> +
> + atomic_set(&epub->wl.off, 0);
> + wiphy_rfkill_start_polling(hw->wiphy);
> +
> + return 0;
> +}
> +
> +/*
> +Called after last netdevice attached to the hardware
> +2944 * is disabled. This should turn off the hardware (at least
> +2945 * it must turn off frame reception.)
> +2946 * May be called right after add_interface if that rejects
> +2947 * an interface. If you added any work onto the mac80211 workqueue
> +2948 * you should ensure to cancel it on this callback.
> +2949 * Must be implemented and can sleep.
> +*/
> +static void esp_op_stop(struct ieee80211_hw *hw)
> +{
> + struct esp_pub *epub;
> +
> + if (!hw || !hw->priv) {
> + return;
> + }
> +
> + epub = (struct esp_pub *)hw->priv;
> + atomic_set(&epub->wl.off, 1);
> +
> +#ifdef HOST_RESET_BUG
> + mdelay(200);
> +#endif
> +
> + if (epub->wl.scan_req) {
> + hw_scan_done(epub, true);
> + epub->wl.scan_req = NULL;
> + //msleep(2);
> + }
> +
> + /* FIXME: does this 'turn off frame reception'? */
> + wiphy_rfkill_stop_polling(hw->wiphy);
> + /* FIXME: flush queues? */
> +}
> +
> +static int esp_set_svif_mode(struct sip_cmd_setvif *svif,
> + enum nl80211_iftype type, bool p2p)
> +{
> + switch (type) {
> + case NL80211_IFTYPE_STATION:
> + svif->op_mode = 0;
> + svif->is_p2p = p2p;
> + break;
> +
> + case NL80211_IFTYPE_AP:
> + svif->op_mode = 1;
> + svif->is_p2p = p2p;
> + break;
> +
> + case NL80211_IFTYPE_P2P_CLIENT:
> + svif->op_mode = 0;
> + svif->is_p2p = 1;
> + break;
> +
> + case NL80211_IFTYPE_P2P_GO:
> + svif->op_mode = 1;
> + svif->is_p2p = 1;
> + break;
> +
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return 0;
> +}
> +
> +/*
> +Called when a netdevice attached to the hardware is
> +2973 * enabled. Because it is not called for monitor mode devices, @start
> +2974 * and @stop must be implemented.
> +2975 * The driver should perform any initialization it needs before
> +2976 * the device can be enabled. The initial configuration for the
> +2977 * interface is given in the conf parameter.
> +2978 * The callback may refuse to add an interface by returning a
> +2979 * negative error code (which will be seen in userspace.)
> +2980 * Must be implemented and can sleep.
> + */
> +static int esp_op_add_interface(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif)
> +{
> + struct esp_pub *epub = (struct esp_pub *)hw->priv;
> + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
> + struct sip_cmd_setvif svif;
> + int ret;
> +
> + memcpy(svif.mac, vif->addr, ETH_ALEN);
> + svif.index = getaddr_index(vif->addr, epub);
> + evif->index = svif.index;
> + evif->epub = epub;
> + /* FIXME: why a need for evif? */
> + epub->vif = vif;
> + svif.set = 1;
> +
> + if (svif.index == ESP_PUB_MAX_VIF) {
Move this error handling next to the getaddr_index() call.
> + dev_err(epub->dev, "support for MAX %d interfaces\n",
> + ESP_PUB_MAX_VIF);
> + return -EOPNOTSUPP;
> + }
> +
> + if (BIT(svif.index) & epub->vif_slot) {
> + dev_err(epub->dev, "interface %d already used\n", svif.index);
> + return -EOPNOTSUPP;
> + }
> +
> + epub->vif_slot |= BIT(svif.index);
> +
> + ret = esp_set_svif_mode(&svif, vif->type, false);
> + if (ret < 0) {
> + dev_err(epub->dev, "no support for interface type %d\n",
> + vif->type);
> + return ret;
> + }
> +
> + sip_cmd(epub, SIP_CMD_SETVIF, (u8 *)&svif, sizeof(svif));
> +
> + return 0;
> +}
> +
> +/*
> +Called when a netdevice changes type. This callback
> +2983 * is optional, but only if it is supported can interface types be
> +2984 * switched while the interface is UP. The callback may sleep.
> +2985 * Note that while an interface is being switched, it will not be
> +2986 * found by the interface iteration callbacks.
> + */
> +static int esp_op_change_interface(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif,
> + enum nl80211_iftype new_type, bool p2p)
> +{
> + struct esp_pub *epub = (struct esp_pub *)hw->priv;
> + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
> + struct sip_cmd_setvif svif;
> + int ret;
> +
> + memcpy(svif.mac, vif->addr, ETH_ALEN);
> + svif.index = evif->index;
> + svif.set = 2;
> +
> + ret = esp_set_svif_mode(&svif, new_type, p2p);
> + if (ret < 0)
> + return ret;
> +
> + sip_cmd(epub, SIP_CMD_SETVIF, (u8 *)&svif, sizeof(svif));
> +
> + return 0;
> +}
> +
> +/*
> + Notifies a driver that an interface is going down.
> + 2989 * The @stop callback is called after this if it is the last interface
> + 2990 * and no monitor interfaces are present.
> + 2991 * When all interfaces are removed, the MAC address in the hardware
> + 2992 * must be cleared so the device no longer acknowledges packets,
> + 2993 * the mac_addr member of the conf structure is, however, set to the
> + 2994 * MAC address of the device going away.
> + 2995 * Hence, this callback must be implemented. It can sleep.
> + */
> +static void esp_op_remove_interface(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif)
> +{
> + struct esp_pub *epub = (struct esp_pub *)hw->priv;
> + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
> + struct sip_cmd_setvif svif = {0};
> +
> + svif.index = evif->index;
> + epub->vif_slot &= ~BIT(svif.index);
> +
> + if (evif->ap_up) {
> + evif->beacon_interval = 0;
> + del_timer_sync(&evif->beacon_timer);
> + evif->ap_up = false;
> + }
> +
> + epub->vif = NULL;
> + evif->epub = NULL;
> +
> + sip_cmd(epub, SIP_CMD_SETVIF, (u8 *)&svif, sizeof(svif));
> + /* TODO: clean up tx/rx queue */
> +}
> +
> +/* FIXME: WTF? */
> +
> +#define BEACON_TIM_SAVE_MAX 20
> +u8 beacon_tim_saved[BEACON_TIM_SAVE_MAX];
> +int beacon_tim_count;
> +static void beacon_tim_init(void)
> +{
> + memset(beacon_tim_saved, 0, BEACON_TIM_SAVE_MAX);
> + beacon_tim_count = 0;
> +}
> +
> +static u8 beacon_tim_save(u8 this_tim)
> +{
> + u8 all_tim = 0;
> + int i;
> +
> + beacon_tim_saved[beacon_tim_count] = this_tim;
> +
> + beacon_tim_count = (beacon_tim_count + 1) % BEACON_TIM_SAVE_MAX;
> +
> + for (i = 0; i < BEACON_TIM_SAVE_MAX; i++)
> + all_tim |= beacon_tim_saved[i];
> +
> + return all_tim;
> +}
> +
> +static bool beacon_tim_alter(struct sk_buff *beacon)
> +{
> + u8 *p, *tim_end;
> + u8 tim_count;
> + int len, remain_len;
> + struct ieee80211_mgmt *mgmt;
> +
> + if (!beacon)
> + return false;
> +
> + mgmt = (struct ieee80211_mgmt *)((u8 *)beacon->data);
> +
> + remain_len = beacon->len - ((u8 *)mgmt->u.beacon.variable -
> + (u8 *)mgmt + 12);
> + p = mgmt->u.beacon.variable;
> +
> + while (remain_len > 0) {
> + len = *(++p);
> +
> + if (*p == WLAN_EID_TIM) { // tim field
> + tim_end = p + len;
> + tim_count = *(++p);
> + p += 2;
> + //multicast
> + if (!tim_count)
> + *p |= 0x1;
> +
> + if (!(*p & 0xfe) && tim_end >= p + 1) { // we only support 8 sta in this case
> + p++;
> + *p = beacon_tim_save(*p);
> + }
> +
> + return tim_count == 0;
> + }
> +
> + p += len + 1;
> + remain_len -= 2 + len;
> + }
> +
> + return false;
> +}
> +
> +unsigned long init_jiffies;
> +unsigned long cycle_beacon_count;
> +static void drv_handle_beacon(unsigned long data)
> +{
> + struct ieee80211_vif *vif = (struct ieee80211_vif *)data;
> + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
> + struct sk_buff *beacon;
> + struct sk_buff *skb;
> + bool tim_reach;
> +
> + if (!evif->epub)
> + return;
> +
> + mdelay(2400 * (cycle_beacon_count % 25) % 10000 / 1000);
> +
> + beacon = ieee80211_beacon_get(evif->epub->hw, vif);
> +
> + tim_reach = beacon_tim_alter(beacon);
> +
> + if (beacon)
> + sip_tx_data_pkt_enqueue(evif->epub, beacon);
> +
> + if (cycle_beacon_count++ == 100) {
> + init_jiffies = jiffies;
> + cycle_beacon_count -= 100;
> + }
> +
> + mod_timer(&evif->beacon_timer, init_jiffies +
> + msecs_to_jiffies(cycle_beacon_count *
> + vif->bss_conf.beacon_int * 1024 / 1000));
> + //FIXME:the packets must be sent at home channel
> + //send buffer mcast frames
> + if (tim_reach) {
> + skb = ieee80211_get_buffered_bc(evif->epub->hw, vif);
> + while (skb) {
> + sip_tx_data_pkt_enqueue(evif->epub, skb);
> + skb = ieee80211_get_buffered_bc(evif->epub->hw, vif);
> + }
> + }
> +}
> +
> +static void init_beacon_timer(struct ieee80211_vif *vif)
> +{
> + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
> +
> + beacon_tim_init();
> + setup_timer(&evif->beacon_timer, drv_handle_beacon, (unsigned long)vif); //TBD, not init here...
> + cycle_beacon_count = 1;
> + init_jiffies = jiffies;
> + evif->beacon_timer.expires = init_jiffies +
> + msecs_to_jiffies(cycle_beacon_count * vif->bss_conf.beacon_int *
> + 1024 / 1000);
> + add_timer(&evif->beacon_timer);
> +}
> +
> +/*
> + Handler for configuration requests. IEEE 802.11 code calls this
> + 2998 * function to change hardware configuration, e.g., channel.
> + 2999 * This function should never fail but returns a negative error code
> + 3000 * if it does. The callback can sleep.
> + */
> +static int esp_op_config(struct ieee80211_hw *hw, u32 changed)
> +{
> + struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +
> + if (changed & (IEEE80211_CONF_CHANGE_CHANNEL |
> + IEEE80211_CONF_CHANGE_IDLE))
> + sip_send_config(epub, &hw->conf);
> +
> + return 0;
> +}
> +
> +/*
> +Handler for configuration requests related to BSS
> +3003 * parameters that may vary during BSS's lifespan, and may affect low
> +3004 * level driver (e.g. assoc/disassoc status, erp parameters).
> +3005 * This function should not be used if no BSS has been set, unless
> +3006 * for association indication. The @changed parameter indicates which
> +3007 * of the bss parameters has changed when a call is made. The callback
> +3008 * can sleep.
> + */
> +static void esp_op_bss_info_changed(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif,
> + struct ieee80211_bss_conf *info,
> + u32 changed)
> +{
> + struct esp_pub *epub = (struct esp_pub *)hw->priv;
> + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
> + u8 *bssid = (u8 *)info->bssid;
> + bool assoc = info->assoc;
> +
> + // ieee80211_bss_conf(include/net/mac80211.h) is included in ieee80211_sub_if_data(net/mac80211/ieee80211_i.h) , does bssid=ieee80211_if_ap's ssid ?
> +
> + if (vif->type == NL80211_IFTYPE_STATION) {
> + if (changed & BSS_CHANGED_BSSID ||
> + ((changed & BSS_CHANGED_ASSOC) && assoc)) {
> + evif->beacon_interval = info->aid;
> + memcpy(epub->wl.bssid, bssid, ETH_ALEN);
> + sip_send_bss_info_update(epub, evif, bssid, assoc);
> + } else if ((changed & BSS_CHANGED_ASSOC) && !assoc) {
> + evif->beacon_interval = 0;
> + memset(epub->wl.bssid, 0, ETH_ALEN);
> + sip_send_bss_info_update(epub, evif, bssid, assoc);
> + }
> + } else if (vif->type == NL80211_IFTYPE_AP) {
> + if (!(changed & BSS_CHANGED_BEACON_ENABLED) &&
> + !(changed & BSS_CHANGED_BEACON_INT))
> + return;
> +
> + if (info->enable_beacon && !evif->ap_up) {
> + evif->beacon_interval = info->beacon_int;
> + init_beacon_timer(vif);
> + sip_send_bss_info_update(epub, evif, bssid, 2);
> + evif->ap_up = true;
> + } else if (!info->enable_beacon && evif->ap_up &&
> + !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) {
> + evif->beacon_interval = 0;
> + del_timer_sync(&evif->beacon_timer);
> + sip_send_bss_info_update(epub, evif, bssid, 2);
> + evif->ap_up = false;
> + }
> + }
> +}
> +
> +/*
> + Configure the device's RX filter.
> + 3015 * See the section "Frame filtering" for more information.
> + 3016 * This callback must be implemented and can sleep.
> + */
> +static void esp_op_configure_filter(struct ieee80211_hw *hw,
> + unsigned int changed_flags,
> + unsigned int *total_flags, u64 multicast)
> +{
> + struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +
> + epub->rx_filter = 0;
> +
> + if (*total_flags & FIF_ALLMULTI)
> + epub->rx_filter |= FIF_ALLMULTI;
> +
> + *total_flags = epub->rx_filter;
> +}
> +
> +static bool is_cipher_suite_wep(u32 cipher)
> +{
> + return (cipher == WLAN_CIPHER_SUITE_WEP40) ||
> + (cipher == WLAN_CIPHER_SUITE_WEP104);
> +}
> +
> +/*
> + See the section "Hardware crypto acceleration"
> + 3029 * This callback is only called between add_interface and
> + 3030 * remove_interface calls, i.e. while the given virtual interface
> + 3031 * is enabled.
> + 3032 * Returns a negative error code if the key can't be added.
> + 3033 * The callback can sleep.
> + */
> +static int esp_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
> + struct ieee80211_vif *vif, struct ieee80211_sta *sta,
> + struct ieee80211_key_conf *key)
> +{
> + struct esp_pub *epub = (struct esp_pub *)hw->priv;
> + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
> + struct esp_hw_idx_map *map;
> + atomic_t *cnt1, *cnt2;
> + u8 i, ifidx = evif->index, isvalid, index;
> + u8 *peer_addr;
> + int ret, counter;
> +
> + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
> +
> + if (sta && memcmp(sta->addr, epub->wl.bssid, ETH_ALEN))
> + peer_addr = sta->addr;
> + else
> + peer_addr = epub->wl.bssid;
> +
> + isvalid = !!(cmd == SET_KEY);
> +
> + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE ||
> + is_cipher_suite_wep(key->cipher))
> + map = epub->low_map[ifidx];
> + else
> + map = epub->hi_map;
> +
> + if (isvalid) {
> + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE ||
> + is_cipher_suite_wep(key->cipher))
> + counter = 2;
> + else
> + counter = 19;
> +
> + for (i = 0; i < counter; i++) {
> + if (map[i].flag)
> + continue;
> +
> + map[i].flag = 1;
> + memcpy(map[i].mac, peer_addr, ETH_ALEN);
> + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE ||
> + is_cipher_suite_wep(key->cipher))
> + key->hw_key_idx = i + 6;
> + else
> + key->hw_key_idx = i + ifidx * 2 + 2;
> + break;
> + }
> + } else {
> + map[index].flag = 0;
> + memset(map[index].mac, 0, ETH_ALEN);
> +
> + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE ||
> + is_cipher_suite_wep(key->cipher))
> + index = key->hw_key_idx - 6;
> + else
> + index = key->hw_key_idx - 2 - ifidx * 2;
> + }
> +
> + if (key->hw_key_idx >= 6) {
> + cnt1 = &epub->wl.ptk_cnt;
> + cnt2 = &epub->wl.gtk_cnt;
> + } else {
> + cnt2 = &epub->wl.ptk_cnt;
> + cnt1 = &epub->wl.gtk_cnt;
> + }
> +
> + /*send sub_scan task to target */
> + if (isvalid)
> + atomic_inc(cnt1);
> + else
> + atomic_dec(cnt1);
> +
> + if (is_cipher_suite_wep(key->cipher)) {
> + if (isvalid)
> + atomic_inc(cnt2);
> + else
> + atomic_dec(cnt2);
> + }
> +
> + ret = sip_send_setkey(epub, ifidx, peer_addr, key, isvalid);
> + if (ret)
> + return ret;
> +
> + if (key->cipher == WLAN_CIPHER_SUITE_TKIP && !ret)
> + atomic_set(&epub->wl.tkip_key_set, 1);
> +
> + return 0;
> +}
> +
> +void hw_scan_done(struct esp_pub *epub, bool aborted)
> +{
> + struct cfg80211_scan_info info = {
> + .aborted = aborted,
> + };
> +
> + cancel_delayed_work_sync(&epub->scan_timeout_work);
> +
> + ESSERT(epub->wl.scan_req);
> +
> + ieee80211_scan_completed(epub->hw, &info);
> +
> + if (test_and_clear_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags))
> + sip_trigger_txq_process(epub->sip);
> +}
> +
> +static void hw_scan_timeout_report(struct work_struct *work)
> +{
> + struct esp_pub *epub = container_of(work, struct esp_pub,
> + scan_timeout_work.work);
> + bool aborted;
> + struct cfg80211_scan_info info = {};
> +
> + if (test_and_clear_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags))
> + sip_trigger_txq_process(epub->sip);
> + /*check if normally complete or aborted like timeout/hw error */
> + aborted = (epub->wl.scan_req != 0);
> +
> + if (aborted)
> + epub->wl.scan_req = NULL;
> +
> + info.aborted = aborted;
> +
> + ieee80211_scan_completed(epub->hw, &info);
> +}
> +
> +/*
> + Configuration of RTS threshold (if device needs it)
> + 3106 * The callback can sleep.
> + */
> +static int esp_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
> +{
> + return 0;
> +}
> +
> +static int esp_node_attach(struct ieee80211_hw *hw, u8 ifidx,
> + struct ieee80211_sta *sta)
> +{
> + struct esp_pub *epub = (struct esp_pub *)hw->priv;
> + struct esp_node *node;
> + struct esp_tx_tid *tid;
> + u8 tidno = 0;
> + int i;
> +
> + spin_lock_bh(&epub->tx_ampdu_lock);
> +
> + /* ffz(x) needs at least one zero or results in undefined behaviour. */
> + if ((~epub->enodes_map) == 0)
> + return -EINVAL;
> +
> + i = ffz(epub->enodes_map);
> +
> + if (hweight32(epub->enodes_maps[ifidx]) >= ESP_PUB_MAX_STA ||
> + i > ESP_PUB_MAX_STA) {
> + i = -1;
> + goto out;
> + }
> +
> + epub->enodes_map |= BIT(i);
> + epub->enodes_maps[ifidx] |= BIT(i);
> + node = (struct esp_node *)sta->drv_priv;
> + epub->enodes[i] = node;
> + node->sta = sta;
> + node->ifidx = ifidx;
> + node->index = i;
> +
> + while (tidno < WME_NUM_TID) {
> + tid = &node->tid[tidno];
> + tid->ssn = 0;
> + tid->cnt = 0;
> + tid->state = ESP_TID_STATE_INIT;
> + tidno++;
> + }
> +
> +out:
> + spin_unlock_bh(&epub->tx_ampdu_lock);
> +
> + return i;
> +}
> +
> +static int esp_node_detach(struct ieee80211_hw *hw, u8 ifidx,
> + struct ieee80211_sta *sta)
> +{
> + struct esp_pub *epub = (struct esp_pub *)hw->priv;
> + u32 map;
> + int i;
> +
> + spin_lock_bh(&epub->tx_ampdu_lock);
> +
> + map = epub->enodes_maps[ifidx];
> +
> + while (map) {
> + i = ffs(map) - 1;
> + if (epub->enodes[i]->sta == sta) {
> + epub->enodes[i]->sta = NULL;
> + epub->enodes[i] = NULL;
> + epub->enodes_map &= ~BIT(i);
> + epub->enodes_maps[ifidx] &= ~BIT(i);
> +
> + goto out;
> + }
> +
> + map &= ~BIT(i);
> + }
> +
> + i = -1;
> +
> +out:
> + spin_unlock_bh(&epub->tx_ampdu_lock);
> +
> + return i;
> +}
> +
> +struct esp_node *esp_get_node_by_addr(struct esp_pub *epub, const u8 *addr)
> +{
> + struct esp_node *node = NULL;
> + int i;
> + u32 map;
> +
> + if (!addr)
> + return NULL;
> +
> + spin_lock_bh(&epub->tx_ampdu_lock);
> + map = epub->enodes_map;
> +
> + while (map) {
> + i = ffs(map) - 1;
> +
> + if (!memcmp(epub->enodes[i]->sta->addr, addr, ETH_ALEN)) {
> + node = epub->enodes[i];
> + goto out;
> + }
> +
> + map &= ~BIT(i);
> + }
> +
> +out:
> + spin_unlock_bh(&epub->tx_ampdu_lock);
> +
> + return node;
> +}
> +
> +int esp_get_empty_rxampdu(struct esp_pub *epub, const u8 *addr, u8 tid)
> +{
> + int index;
> +
> + if (!addr)
> + return -1;
> +
> + spin_lock_bh(&epub->rx_ampdu_lock);
> +
> + index = ffz(epub->rxampdu_map);
> +
> + if (index >= ESP_PUB_MAX_RXAMPDU) {
> + index = -1;
> + goto out;
> + }
> +
> + epub->rxampdu_map |= BIT(index);
> + epub->rxampdu_node[index] = esp_get_node_by_addr(epub, addr);
> + epub->rxampdu_tid[index] = tid;
> +
> +out:
> + spin_unlock_bh(&epub->rx_ampdu_lock);
> +
> + return index;
> +}
> +
> +int esp_get_exist_rxampdu(struct esp_pub *epub, const u8 *addr, u8 tid)
> +{
> + u8 map;
> + int index;
> +
> + if (!addr)
> + return -1;
> +
> + spin_lock_bh(&epub->rx_ampdu_lock);
> + map = epub->rxampdu_map;
> +
> + while (map) {
> + index = ffs(map) - 1;
> +
> + if (epub->rxampdu_tid[index] == tid &&
> + !memcmp(epub->rxampdu_node[index]->sta->addr, addr,
> + ETH_ALEN)) {
> + epub->rxampdu_map &= ~BIT(index);
> + goto out;
> + }
> +
> + map &= ~BIT(index);
> + }
> +
> + index = -1;
> +
> +out:
> + spin_unlock_bh(&epub->rx_ampdu_lock);
> + return index;
> +}
> +
> +/*
> + Notifies low level driver about addition of an associated station,
> + 3109 * AP, IBSS/WDS/mesh peer etc. This callback can sleep.
> + */
> +static int esp_op_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
> + struct ieee80211_sta *sta)
> +{
> + struct esp_pub *epub = (struct esp_pub *)hw->priv;
> + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
> + int index;
> +
> + index = esp_node_attach(hw, evif->index, sta);
> + if (index < 0)
> + return index;
> +
> + sip_send_set_sta(epub, evif->index, 1, sta, vif, (u8)index);
> +
> + return 0;
> +}
> +
> +/*
> + Notifies low level driver about removal of an associated
> + 3112 * station, AP, IBSS/WDS/mesh peer etc. Note that after the callback
> + 3113 * returns it isn't safe to use the pointer, not even RCU protected;
> + 3114 * no RCU grace period is guaranteed between returning here and freeing
> + 3115 * the station. See @sta_pre_rcu_remove if needed.
> + 3116 * This callback can sleep
> + */
> +static int esp_op_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
> + struct ieee80211_sta *sta)
> +{
> + struct esp_pub *epub = (struct esp_pub *)hw->priv;
> + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
> + int index;
> +
> + //remove a connect in target
> + index = esp_node_detach(hw, evif->index, sta);
Why do we think "index" is not -1?
> + sip_send_set_sta(epub, evif->index, 0, sta, vif, (u8)index);
> +
> + return 0;
> +}
> +
> +/*
> + Notifies low level driver about power state transition of an
> + 3124 * associated station, AP, IBSS/WDS/mesh peer etc. For a VIF operating
> + 3125 * in AP mode, this callback will not be called when the flag
> + 3126 * %IEEE80211_HW_AP_LINK_PS is set. Must be atomic.
> + */
> +static void esp_op_sta_notify(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif,
> + enum sta_notify_cmd cmd,
> + struct ieee80211_sta *sta)
> +{
> +}
> +
> +/*
> + Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
> + 3165 * bursting) for a hardware TX queue.
> + 3166 * Returns a negative error code on failure.
> + 3167 * The callback can sleep.
> + */
> +static int esp_op_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
> + u16 queue,
> + const struct ieee80211_tx_queue_params *params)
> +{
> + struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +
> + return sip_send_wmm_params(epub, queue, params);
> +}
> +
> +/*
> + Get the current TSF timer value from firmware/hardware. Currently,
> + 3170 * this is only used for IBSS mode BSSID merging and debugging. Is not a
> + 3171 * required function.
> + 3172 * The callback can sleep.
> + */
> +static u64 esp_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
> +{
> + return 0;
> +}
> +
> +/*
> + Set the TSF timer to the specified value in the firmware/hardware.
> + 3175 * Currently, this is only used for IBSS mode debugging. Is not a
> + 3176 * required function.
> + 3177 * The callback can sleep.
> + */
> +static void esp_op_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
> + u64 tsf)
> +{
> +}
> +
> +/*
> + Reset the TSF timer and allow firmware/hardware to synchronize
> + 3186 * with other STAs in the IBSS. This is only used in IBSS mode. This
> + 3187 * function is optional if the firmware/hardware takes full care of
> + 3188 * TSF synchronization.
> + 3189 * The callback can sleep.
> + */
> +static void esp_op_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
> +{
> +}
> +
> +/*
> + Poll rfkill hardware state. If you need this, you also
> + 3220 * need to set wiphy->rfkill_poll to %true before registration,
> + 3221 * and need to call wiphy_rfkill_set_hw_state() in the callback.
> + 3222 * The callback can sleep.
> + */
> +static void esp_op_rfkill_poll(struct ieee80211_hw *hw)
> +{
> + struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +
> + wiphy_rfkill_set_hw_state(hw->wiphy, test_bit(ESP_WL_FLAG_RFKILL,
> + &epub->wl.flags));
> +}
> +
> +#ifdef HW_SCAN
> +/*
> + Ask the hardware to service the scan request, no need to start
> + 3051 * the scan state machine in stack. The scan must honour the channel
> + 3052 * configuration done by the regulatory agent in the wiphy's
> + 3053 * registered bands. The hardware (or the driver) needs to make sure
> + 3054 * that power save is disabled.
> + 3055 * The @req ie/ie_len members are rewritten by mac80211 to contain the
> + 3056 * entire IEs after the SSID, so that drivers need not look at these
> + 3057 * at all but just send them after the SSID -- mac80211 includes the
> + 3058 * (extended) supported rates and HT information (where applicable).
> + 3059 * When the scan finishes, ieee80211_scan_completed() must be called;
> + 3060 * note that it also must be called when the scan cannot finish due to
> + 3061 * any error unless this callback returned a negative error code.
> + 3062 * The callback can sleep.
> + */
> +static int esp_op_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
> + struct cfg80211_scan_request *req)
> +{
> + struct esp_pub *epub = (struct esp_pub *)hw->priv;
> + struct cfg80211_ssid *ssid2 = req->ssids + 1;
> + int i, ret;
> + bool scan_often;
> +
> + /* scan_request is keep allocate until scan_done,record it
> + * to split request into multi sdio_cmd
> + */
> + if (atomic_read(&epub->wl.off)) {
> + dev_err(epub->dev, "hw_scan but wl off\n");
> + return -EPERM;
> + }
> +
> + if (req->n_ssids > 1)
> + if ((req->ssids->ssid_len > 0 && ssid2->ssid_len > 0) ||
> + req->n_ssids > 2) {
> + dev_err(epub->dev, "cannot scan two SSIDs\n");
> + return -EINVAL;
> + }
> +
> + epub->wl.scan_req = req;
> +
> + /*in connect state, suspend tx data */
> + if (epub->sip->support_bgscan &&
> + test_bit(ESP_WL_FLAG_CONNECT, &epub->wl.flags) && req->n_channels) {
> + scan_often = epub->scan_permit_valid &&
> + time_before(jiffies, epub->scan_permit);
> + epub->scan_permit_valid = true;
> +
> + if (!scan_often) {
> + /* epub->scan_permit = jiffies + msecs_to_jiffies(900);
> + * set_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags);
> + * if (atomic_read(&epub->txq_stopped) == false) {
> + * atomic_set(&epub->txq_stopped, true);
> + * ieee80211_stop_queues(hw);
> + * }
> + */
> + } else {
> + dev_err(epub->dev, "scan too often\n");
> + return -EACCES;
> + }
> + } else {
> + scan_often = false;
> + }
> +
> + /*send sub_scan task to target */
> + ret = sip_send_scan(epub);
> + if (ret) {
> + dev_err(epub->dev, "failed to send scan_cmd: %d\n", ret);
> + return ret;
> + }
> +
> + if (scan_often)
> + return 0;
> +
> + epub->scan_permit = jiffies + msecs_to_jiffies(900);
> + set_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags);
> + if (!atomic_read(&epub->txq_stopped)) {
> + atomic_set(&epub->txq_stopped, true);
> + ieee80211_stop_queues(hw);
> + }
> +
> + /*force scan complete in case target fail to report in time */
> + ieee80211_queue_delayed_work(hw, &epub->scan_timeout_work,
> + req->n_channels * HZ / 4);
> +
> + return 0;
> +}
> +
> +/*
> + Starts an off-channel period on the given channel, must
> + 3255 * call back to ieee80211_ready_on_channel() when on that channel. Note
> + 3256 * that normal channel traffic is not stopped as this is intended for hw
> + 3257 * offload. Frames to transmit on the off-channel channel are transmitted
> + 3258 * normally except for the %IEEE80211_TX_CTL_TX_OFFCHAN flag. When the
> + 3259 * duration (which will always be non-zero) expires, the driver must call
> + 3260 * ieee80211_remain_on_channel_expired().
> + 3261 * Note that this callback may be called while the device is in IDLE and
> + 3262 * must be accepted in this case.
> + 3263 * This callback may sleep.
> + */
> +static int esp_op_remain_on_channel(struct ieee80211_hw *hw,
> + struct ieee80211_channel *chan,
> + enum nl80211_channel_type channel_type,
> + int duration)
> +{
> + struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +
> + sip_send_roc(epub, chan->center_freq, duration);
> +
> + return 0;
> +}
> +
> +/*
> + 3264 * @cancel_remain_on_channel: Requests that an ongoing off-channel period is
> + 3265 * aborted before it expires. This callback may sleep.
> + */
> +static int esp_op_cancel_remain_on_channel(struct ieee80211_hw *hw)
> +{
> + struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +
> + epub->roc_flags = 0; // to disable roc state
> + sip_send_roc(epub, 0, 0);
> +
> + return 0;
> +}
> +#endif
> +
> +void esp_rocdone_process(struct ieee80211_hw *hw, struct sip_evt_roc *report)
> +{
> + struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +
> + if (report->is_ok != 1)
> + return;
> +
> + if (report->state == 1) {
> + epub->roc_flags = 1; //flags in roc state, to fix channel, not change
> + ieee80211_ready_on_channel(hw);
> + } else if (!report->state) {
> + epub->roc_flags = 0;
> + ieee80211_remain_on_channel_expired(hw);
> + }
> +}
> +
> +/*
> + Set a mask of rates to be used for rate control selection
> + 3275 * when transmitting a frame. Currently only legacy rates are handled.
> + 3276 * The callback can sleep.
> + */
> +static int esp_op_set_bitrate_mask(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif,
> + const struct cfg80211_bitrate_mask *mask)
> +{
> + return 0;
> +}
> +
> +/*
> + Flush all pending frames from the hardware queue, making sure
> + 3235 * that the hardware queues are empty. The @queues parameter is a bitmap
> + 3236 * of queues to flush, which is useful if different virtual interfaces
> + 3237 * use different hardware queues; it may also indicate all queues.
> + 3238 * If the parameter @drop is set to %true, pending frames may be dropped.
> + 3239 * Note that vif can be NULL.
> + 3240 * The callback can sleep.
> + */
> +void esp_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
> + u32 queues, bool drop)
> +{
> + struct esp_pub *epub = (struct esp_pub *)hw->priv;
> + unsigned long time = jiffies + msecs_to_jiffies(15);
> +
> + while (atomic_read(&epub->sip->tx_data_pkt_queued)) {
> + if (!time_before(jiffies, time))
> + break;
> +
> + if (!sif_get_ate_config())
> + ieee80211_queue_work(epub->hw, &epub->tx_work);
> + else
> + queue_work(epub->esp_wkq, &epub->tx_work);
> + }
> +
> + mdelay(10);
> +}
> +
> +/*
> + Perform a certain A-MPDU action
> + 3198 * The RA/TID combination determines the destination and TID we want
> + 3199 * the ampdu action to be performed for. The action is defined through
> + 3200 * ieee80211_ampdu_mlme_action.
> + 3201 * When the action is set to %IEEE80211_AMPDU_TX_OPERATIONAL the driver
> + 3202 * may neither send aggregates containing more subframes than @buf_size
> + 3203 * nor send aggregates in a way that lost frames would exceed the
> + 3204 * buffer size. If just limiting the aggregate size, this would be
> + 3205 * possible with a buf_size of 8:
> + 3206 * - TX: 1.....7
> + 3207 * - RX: 2....7 (lost frame #1)
> + 3208 * - TX: 8..1...
> + 3209 * which is invalid since #1 was now re-transmitted well past the
> + 3210 * buffer size of 8. Correct ways to retransmit #1 would be:
> + 3211 * - TX: 1 or 18 or 81
> + 3212 * Even "189" would be wrong since 1 could be lost again.
> + 3213 *
> + 3214 * Returns a negative error code on failure.
> + 3215 * The callback can sleep.
> + */
> +static int esp_op_ampdu_action(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif,
> + struct ieee80211_ampdu_params *params)
> +{
> + enum ieee80211_ampdu_mlme_action action = params->action;
> + struct ieee80211_sta *sta = params->sta;
> + struct esp_pub *epub = (struct esp_pub *)hw->priv;
> + struct esp_node *node = (struct esp_node *)sta->drv_priv;
> + struct cfg80211_chan_def *chandef;
> + u16 tid = params->tid;
> + struct esp_tx_tid *tid_info = &node->tid[tid];
> + u16 *ssn = ¶ms->ssn;
> + u8 buf_size = params->buf_size;
> +
> + switch (action) {
> + case IEEE80211_AMPDU_TX_START:
> + chandef = &epub->hw->conf.chandef;
> + if (mod_support_no_txampdu() || !sta->ht_cap.ht_supported ||
> + cfg80211_get_chandef_type(chandef) == NL80211_CHAN_NO_HT)
> + return -EOPNOTSUPP;
> +
> + dev_dbg(epub->dev, "%s TX START, addr:%pM,tid:%u,state:%d\n",
> + __func__, sta->addr, tid, tid_info->state);
> +
> + spin_lock_bh(&epub->tx_ampdu_lock);
> +
> + ESSERT(tid_info->state == ESP_TID_STATE_TRIGGER);
> + *ssn = tid_info->ssn;
> + tid_info->state = ESP_TID_STATE_PROGRESS;
> +
> + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
> + spin_unlock_bh(&epub->tx_ampdu_lock);
> +
> + return 0;
> +
> + case IEEE80211_AMPDU_TX_STOP_CONT:
> + dev_dbg(epub->dev, "%s TX STOP, addr:%pM,tid:%u,state:%d\n",
> + __func__, sta->addr, tid, tid_info->state);
> +
> + spin_lock_bh(&epub->tx_ampdu_lock);
> +
> + case IEEE80211_AMPDU_TX_STOP_FLUSH:
> + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
> + if (tid_info->state == ESP_TID_STATE_WAIT_STOP)
> + tid_info->state = ESP_TID_STATE_STOP;
> + else
> + tid_info->state = ESP_TID_STATE_INIT;
> +
> + if (action == IEEE80211_AMPDU_TX_STOP_CONT) {
> + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
> + spin_unlock_bh(&epub->tx_ampdu_lock);
> + }
> +
> + return sip_send_ampdu_action(epub, SIP_AMPDU_TX_STOP, sta->addr,
> + tid, node->ifidx, 0);
> +
> + case IEEE80211_AMPDU_TX_OPERATIONAL:
> + dev_dbg(epub->dev,
> + "%s TX OPERATION, addr:%pM,tid:%u,state:%d\n", __func__,
> + sta->addr, tid, tid_info->state);
> +
> + spin_lock_bh(&epub->tx_ampdu_lock);
> +
> + if (tid_info->state != ESP_TID_STATE_PROGRESS) {
> + if (tid_info->state == ESP_TID_STATE_INIT) {
> + printk(KERN_ERR "%s WIFI RESET, IGNORE\n",
> + __func__);
> + spin_unlock_bh(&epub->tx_ampdu_lock);
> + return -ENETRESET;
> + }
> +
> + ESSERT(0);
> + }
> +
> + tid_info->state = ESP_TID_STATE_OPERATIONAL;
> + spin_unlock_bh(&epub->tx_ampdu_lock);
> +
> + return sip_send_ampdu_action(epub, SIP_AMPDU_TX_OPERATIONAL,
> + sta->addr, tid, node->ifidx,
> + buf_size);
> +
> + case IEEE80211_AMPDU_RX_START:
> + chandef = &epub->hw->conf.chandef;
> + if (mod_support_no_rxampdu() || !sta->ht_cap.ht_supported ||
> + cfg80211_get_chandef_type(chandef) == NL80211_CHAN_NO_HT)
> + return -EOPNOTSUPP;
> +
> + dev_dbg(epub->dev, "%s RX START %pM tid %u %u\n", __func__,
> + sta->addr, tid, *ssn);
> +
> + return sip_send_ampdu_action(epub, SIP_AMPDU_RX_START, sta->addr,
> + tid, *ssn, 64);
> +
> + case IEEE80211_AMPDU_RX_STOP:
> + dev_dbg(epub->dev, "%s RX STOP %pM tid %u\n", __func__,
> + sta->addr, tid);
> +
> + return sip_send_ampdu_action(epub, SIP_AMPDU_RX_STOP, sta->addr,
> + tid, 0, 0);
> +
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static void esp_tx_work(struct work_struct *work)
> +{
> + struct esp_pub *epub = container_of(work, struct esp_pub, tx_work);
> +
> + mutex_lock(&epub->tx_mtx);
> + sip_txq_process(epub);
> + mutex_unlock(&epub->tx_mtx);
> +}
> +
> +static const struct ieee80211_ops esp_mac80211_ops = {
> + .tx = esp_op_tx,
> + .start = esp_op_start,
> + .stop = esp_op_stop,
> + .add_interface = esp_op_add_interface,
> + .remove_interface = esp_op_remove_interface,
> + .config = esp_op_config,
> +
> + .bss_info_changed = esp_op_bss_info_changed,
> + .configure_filter = esp_op_configure_filter,
> + .set_key = esp_op_set_key,
> + .set_rts_threshold = esp_op_set_rts_threshold,
> + .sta_notify = esp_op_sta_notify,
> + .conf_tx = esp_op_conf_tx,
> + .change_interface = esp_op_change_interface,
> + .get_tsf = esp_op_get_tsf,
> + .set_tsf = esp_op_set_tsf,
> + .reset_tsf = esp_op_reset_tsf,
> + .rfkill_poll = esp_op_rfkill_poll,
> +#ifdef HW_SCAN
> + .hw_scan = esp_op_hw_scan,
> + .remain_on_channel = esp_op_remain_on_channel,
> + .cancel_remain_on_channel = esp_op_cancel_remain_on_channel,
> +#endif
> + .ampdu_action = esp_op_ampdu_action,
> + .sta_add = esp_op_sta_add,
> + .sta_remove = esp_op_sta_remove,
> + .set_bitrate_mask = esp_op_set_bitrate_mask,
> + .flush = esp_op_flush,
> +};
> +
> +struct esp_pub *esp_pub_alloc_mac80211(struct device *dev)
> +{
> + struct ieee80211_hw *hw;
> + struct esp_pub *epub;
> +
> + hw = ieee80211_alloc_hw(sizeof(struct esp_pub), &esp_mac80211_ops);
> + if (!hw)
> + return ERR_PTR(-ENOMEM);
The caller expects a NULL return.
> +
> + /* FIXME: useless if hw_scan is defined, incorrect if hw_scan is undefined*/
> +#ifdef HW_SCAN
> + hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
> +#endif
> +
> + epub = hw->priv;
> + memset(epub, 0, sizeof(*epub));
> +
> + epub->hw = hw;
> + SET_IEEE80211_DEV(hw, dev);
> + epub->dev = dev;
> +
> + skb_queue_head_init(&epub->txq);
> + skb_queue_head_init(&epub->txdoneq);
> + skb_queue_head_init(&epub->rxq);
> +
> + spin_lock_init(&epub->tx_ampdu_lock);
> + spin_lock_init(&epub->rx_ampdu_lock);
> + spin_lock_init(&epub->tx_lock);
> + mutex_init(&epub->tx_mtx);
> + spin_lock_init(&epub->rx_lock);
> +
> + INIT_WORK(&epub->tx_work, esp_tx_work);
> +
> + epub->esp_wkq = create_singlethread_workqueue("esp_wkq");
> +
> + if (!epub->esp_wkq)
> + return ERR_PTR(-ENOMEM);
goto free_hw;
> +
> + epub->scan_permit_valid = false;
> + INIT_DELAYED_WORK(&epub->scan_timeout_work, hw_scan_timeout_report);
> +
> + return epub;
> +}
> +
> +int esp_pub_dealloc_mac80211(struct esp_pub *epub)
> +{
> + set_bit(ESP_WL_FLAG_RFKILL, &epub->wl.flags);
> +
> + destroy_workqueue(epub->esp_wkq);
> + mutex_destroy(&epub->tx_mtx);
> +
> + if (epub->hw)
> + ieee80211_free_hw(epub->hw);
> +
> + return 0;
> +}
> +
> +/* 2G band channels */
> +static struct ieee80211_channel esp_channels_2ghz[] = {
> + {.hw_value = 1, .center_freq = 2412, .max_power = 25},
> + {.hw_value = 2, .center_freq = 2417, .max_power = 25},
> + {.hw_value = 3, .center_freq = 2422, .max_power = 25},
> + {.hw_value = 4, .center_freq = 2427, .max_power = 25},
> + {.hw_value = 5, .center_freq = 2432, .max_power = 25},
> + {.hw_value = 6, .center_freq = 2437, .max_power = 25},
> + {.hw_value = 7, .center_freq = 2442, .max_power = 25},
> + {.hw_value = 8, .center_freq = 2447, .max_power = 25},
> + {.hw_value = 9, .center_freq = 2452, .max_power = 25},
> + {.hw_value = 10, .center_freq = 2457, .max_power = 25},
> + {.hw_value = 11, .center_freq = 2462, .max_power = 25},
> + {.hw_value = 12, .center_freq = 2467, .max_power = 25},
> + {.hw_value = 13, .center_freq = 2472, .max_power = 25},
> +};
> +
> +/* 11G rate */
> +static struct ieee80211_rate esp_rates_2ghz[] = {
> + {
> + .bitrate = 10,
> + .hw_value = CONF_HW_BIT_RATE_1MBPS,
> + .hw_value_short = CONF_HW_BIT_RATE_1MBPS,
> + },
> + {
> + .bitrate = 20,
> + .hw_value = CONF_HW_BIT_RATE_2MBPS,
> + .hw_value_short = CONF_HW_BIT_RATE_2MBPS,
> + .flags = IEEE80211_RATE_SHORT_PREAMBLE},
> + {
> + .bitrate = 55,
> + .hw_value = CONF_HW_BIT_RATE_5_5MBPS,
> + .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS,
> + .flags = IEEE80211_RATE_SHORT_PREAMBLE},
> + {
> + .bitrate = 110,
> + .hw_value = CONF_HW_BIT_RATE_11MBPS,
> + .hw_value_short = CONF_HW_BIT_RATE_11MBPS,
> + .flags = IEEE80211_RATE_SHORT_PREAMBLE},
> + {
> + .bitrate = 60,
> + .hw_value = CONF_HW_BIT_RATE_6MBPS,
> + .hw_value_short = CONF_HW_BIT_RATE_6MBPS,
> + },
> + {
> + .bitrate = 90,
> + .hw_value = CONF_HW_BIT_RATE_9MBPS,
> + .hw_value_short = CONF_HW_BIT_RATE_9MBPS,
> + },
> + {
> + .bitrate = 120,
> + .hw_value = CONF_HW_BIT_RATE_12MBPS,
> + .hw_value_short = CONF_HW_BIT_RATE_12MBPS,
> + },
> + {
> + .bitrate = 180,
> + .hw_value = CONF_HW_BIT_RATE_18MBPS,
> + .hw_value_short = CONF_HW_BIT_RATE_18MBPS,
> + },
> + {
> + .bitrate = 240,
> + .hw_value = CONF_HW_BIT_RATE_24MBPS,
> + .hw_value_short = CONF_HW_BIT_RATE_24MBPS,
> + },
> + {
> + .bitrate = 360,
> + .hw_value = CONF_HW_BIT_RATE_36MBPS,
> + .hw_value_short = CONF_HW_BIT_RATE_36MBPS,
> + },
> + {
> + .bitrate = 480,
> + .hw_value = CONF_HW_BIT_RATE_48MBPS,
> + .hw_value_short = CONF_HW_BIT_RATE_48MBPS,
> + },
> + {
> + .bitrate = 540,
> + .hw_value = CONF_HW_BIT_RATE_54MBPS,
> + .hw_value_short = CONF_HW_BIT_RATE_54MBPS,
> + },
> +};
> +
> +static struct ieee80211_sta_ht_cap esp_ht_cap_2ghz = {
> + .cap = IEEE80211_HT_CAP_DSSSCCK40 | IEEE80211_HT_CAP_SM_PS |
> + IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20,
> + .ht_supported = true,
> + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
> + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
> + .mcs = {
> + .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
> + },
> +};
> +
> +static void esp_pub_init_mac80211(struct esp_pub *epub)
> +{
> + struct ieee80211_hw *hw = epub->hw;
> + struct ieee80211_supported_band *sbands =
> + &epub->wl.sbands[NL80211_BAND_2GHZ];
> +
> + static const u32 cipher_suites[] = {
> + WLAN_CIPHER_SUITE_WEP40,
> + WLAN_CIPHER_SUITE_WEP104,
> + WLAN_CIPHER_SUITE_TKIP,
> + WLAN_CIPHER_SUITE_CCMP,
> + };
> +
> + hw->max_listen_interval = 10;
> +
> + ieee80211_hw_set(hw, SIGNAL_DBM);
> + ieee80211_hw_set(hw, HAS_RATE_CONTROL);
> + ieee80211_hw_set(hw, SUPPORTS_PS);
> + ieee80211_hw_set(hw, AMPDU_AGGREGATION);
> + ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
> + hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF;
> + hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF;
> +
> + hw->wiphy->cipher_suites = cipher_suites;
> + hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
> + hw->wiphy->max_scan_ie_len = epub->sip->tx_blksz -
> + sizeof(struct sip_hdr) - sizeof(struct sip_cmd_scan);
> +
> + /* ONLY station for now, support P2P soon... */
> + /* FIXME: is p2p really supported? */
> + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_P2P_GO) |
> + BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_STATION) |
> + BIT(NL80211_IFTYPE_AP);
> +
> + hw->wiphy->max_scan_ssids = 2;
> + hw->wiphy->max_remain_on_channel_duration = 5000;
> +
> + atomic_set(&epub->wl.off, 1);
> +
> + sbands->band = NL80211_BAND_2GHZ;
> + sbands->channels = esp_channels_2ghz;
> + sbands->bitrates = esp_rates_2ghz;
> + sbands->n_channels = ARRAY_SIZE(esp_channels_2ghz);
> + sbands->n_bitrates = ARRAY_SIZE(esp_rates_2ghz);
> + sbands->ht_cap = esp_ht_cap_2ghz;
> +
> + hw->wiphy->bands[NL80211_BAND_2GHZ] = sbands;
> +
> + /*no fragment */
> + hw->wiphy->frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
> +
> + /* handle AC queue in f/w */
> + hw->queues = 4;
> + hw->max_rates = 4;
> +
> + hw->vif_data_size = sizeof(struct esp_vif);
> + hw->sta_data_size = sizeof(struct esp_node);
> +}
> +
> +int esp_register_mac80211(struct esp_pub *epub)
> +{
> + int ret, idx;
> + u8 *p2p_addr;
> +
> + esp_pub_init_mac80211(epub);
> +
> + /* FIXME: esp_mac_addr */
> + epub->hw->wiphy->addresses = (struct mac_address *)esp_mac_addr;
> +
> + memcpy(epub->hw->wiphy->addresses[0].addr, epub->mac_addr, ETH_ALEN);
> + memcpy(epub->hw->wiphy->addresses[1].addr, epub->mac_addr, ETH_ALEN);
> + p2p_addr = epub->hw->wiphy->addresses[1].addr;
> +
> + for (idx = 0; idx < 8 * ETH_ALEN; idx++) {
> + p2p_addr[0] = epub->mac_addr[0] | 0x02;
> + p2p_addr[0] ^= idx << 2;
> + if (strncmp(p2p_addr, epub->mac_addr, 6))
> + break;
> + }
> +
> + epub->hw->wiphy->n_addresses = 2;
> +
> + ret = ieee80211_register_hw(epub->hw);
> + if (ret < 0) {
> + printk("unable to register mac80211 hw: %d\n", ret);
> + return ret;
> + }
> +
> + set_bit(ESP_WL_FLAG_HW_REGISTERED, &epub->wl.flags);
> +
> + return ret;
return 0;
> +}
> +
> +static u8 getaddr_index(u8 *addr, struct esp_pub *epub)
> +{
> + int i;
> +
> + for (i = 0; i < ESP_PUB_MAX_VIF; i++)
> + if (!memcmp(addr, epub->hw->wiphy->addresses[i].addr, ETH_ALEN))
> + return i;
> +
> + return ESP_PUB_MAX_VIF;
> +}
> diff --git a/drivers/staging/esp8089/esp_mac80211.h b/drivers/staging/esp8089/esp_mac80211.h
> new file mode 100644
> index 000000000000..03de0b0a4223
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_mac80211.h
> @@ -0,0 +1,33 @@
> +/*
> + * Copyright (c) 2011 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + * Quentin Schulz <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + */
> +
> +#ifndef _ESP_MAC80211_H_
> +#define _ESP_MAC80211_H_
> +
> +struct esp_80211_wmm_ac_param {
> + u8 aci_aifsn; /* AIFSN, ACM, ACI */
> + u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */
> + u16 txop_limit;
> +};
> +
> +struct esp_80211_wmm_param_element {
> + /* Element ID: 221 (0xdd); length: 24 */
> + /* required fields for WMM version 1 */
> + 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 specif QoS info */
> + u8 reserved; /* 0 */
> + struct esp_80211_wmm_ac_param ac[4]; /* AC_BE, AC_BK, AC_VI, AC_VO */
> +};
> +
> +#endif /* _ESP_MAC80211_H_ */
> diff --git a/drivers/staging/esp8089/esp_main.c b/drivers/staging/esp8089/esp_main.c
> new file mode 100644
> index 000000000000..c4621847926c
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_main.c
> @@ -0,0 +1,199 @@
> +/*
> + * Copyright (c) 2010 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + * Quentin Schulz <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + */
> +
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <linux/rtnetlink.h>
> +#include <linux/firmware.h>
> +#include <linux/sched.h>
> +#include <net/cfg80211.h>
> +#include <net/mac80211.h>
> +#include <linux/time.h>
> +#include <linux/moduleparam.h>
> +#include <linux/module.h>
> +
> +#include "esp_pub.h"
> +#include "esp_sip.h"
> +#include "esp_sif.h"
> +#include "esp_debug.h"
> +#include "esp_file.h"
> +#include "esp_wl.h"
> +
> +struct completion *gl_bootup_cplx;
> +
> +static int esp_download_fw(struct esp_pub *epub);
> +
> +static int modparam_no_txampdu;
> +static int modparam_no_rxampdu;
> +
> +module_param_named(no_txampdu, modparam_no_txampdu, int, 0444);
> +MODULE_PARM_DESC(no_txampdu, "Disable tx ampdu.");
> +
> +module_param_named(no_rxampdu, modparam_no_rxampdu, int, 0444);
> +MODULE_PARM_DESC(no_rxampdu, "Disable rx ampdu.");
> +
> +static char *modparam_eagle_path = "/lib/firmware";
> +
> +module_param_named(eagle_path, modparam_eagle_path, charp, 0444);
> +MODULE_PARM_DESC(eagle_path, "eagle path");
> +
> +bool mod_support_no_txampdu(void)
> +{
> + return modparam_no_txampdu;
> +}
> +
> +bool mod_support_no_rxampdu(void)
> +{
> + return modparam_no_rxampdu;
> +}
> +
> +int esp_pub_init_all(struct esp_pub *epub)
> +{
> + int ret;
> +
> + /* completion for bootup event poll */
> + DECLARE_COMPLETION_ONSTACK(complete);
> +
> + atomic_set(&epub->ps.state, ESP_PM_OFF);
> +
> + if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) {
> + epub->sip = sip_attach(epub);
> + if (!epub->sip) {
> + printk(KERN_ERR "%s sip alloc failed\n", __func__);
> + return -ENOMEM;
> + }
> + } else {
> + atomic_set(&epub->sip->state, SIP_PREPARE_BOOT);
> + atomic_set(&epub->sip->tx_credits, 0);
> + }
> +
> + epub->sip->to_host_seq = 0;
> +
> + ret = esp_download_fw(epub);
> + if (ret) {
> + printk("download firmware failed\n");
> + return ret;
> + }
> +
> + gl_bootup_cplx = &complete;
> + epub->wait_reset = 0;
> + sif_enable_irq(epub);
> +
> + if (epub->sdio_state == ESP_SDIO_STATE_SECOND_INIT ||
> + sif_get_ate_config() == 1) {
Drop the == 1. We mostly treat sif_get_ate_config() as bool, so let's
do that consistently everywhere.
> + ret = sip_poll_bootup_event(epub->sip);
> + } else {
> + ret = sip_poll_resetting_event(epub->sip);
> + if (!ret) {
> + sif_lock_bus(epub);
> + sif_interrupt_target(epub, 7);
> + sif_unlock_bus(epub);
> + }
> + }
> +
> + gl_bootup_cplx = NULL;
> +
> + if (sif_get_ate_config() == 1)
> + ret = -EOPNOTSUPP;
> +
> + return ret;
> +}
> +
> +void esp_dsr(struct esp_pub *epub)
Can this function be removed?
> +{
> + sip_rx(epub);
> +}
> +
> +struct esp_fw_hdr {
> + u8 magic;
> + u8 blocks;
> + u8 pad[2];
> + u32 entry_addr;
> +} __packed;
> +
> +struct esp_fw_blk_hdr {
> + u32 load_addr;
> + u32 data_len;
> +} __packed;
> +
> +#define ESP_FW_NAME1 "eagle_fw_ate_config_v19.bin"
> +#define ESP_FW_NAME2 "eagle_fw_first_init_v19.bin"
> +#define ESP_FW_NAME3 "eagle_fw_second_init_v19.bin"
> +
> +static int esp_download_fw(struct esp_pub *epub)
> +{
> + const struct firmware *fw_entry;
> + struct esp_fw_hdr *fhdr;
> + struct esp_fw_blk_hdr *bhdr;
> + struct sip_cmd_bootup bootcmd;
> + u8 *fw_buf;
> + u32 offset;
> + int ret;
> + u8 blocks;
> + char *esp_fw_name;
> +
> + if (sif_get_ate_config() == 1)
> + esp_fw_name = ESP_FW_NAME3;
> + else
> + esp_fw_name = epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT ? ESP_FW_NAME1 : ESP_FW_NAME2;
> +
> + ret = request_firmware(&fw_entry, esp_fw_name, epub->dev);
> + if (ret)
> + return ret;
> +
> + fw_buf = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL);
> +
> + release_firmware(fw_entry);
> +
> + if (!fw_buf)
> + return -ENOMEM;
> +
> + fhdr = (struct esp_fw_hdr *)fw_buf;
> + if (fhdr->magic != 0xE9) {
> + printk("%s wrong firmware magic!\n", __func__);
> + goto _err;
Choose a label name based on what the goto does. For example, here if
we hit the goto then we kfree(fw_buf); so "goto free_fw;" would be a
descriptive name.
Also don't forget to set the error code.
> + }
> +
> + blocks = fhdr->blocks;
> + offset = sizeof(struct esp_fw_hdr);
> +
> + while (blocks) {
> + bhdr = (struct esp_fw_blk_hdr *)&fw_buf[offset];
> + offset += sizeof(struct esp_fw_blk_hdr);
> +
> + ret = sip_write_memory(epub->sip, bhdr->load_addr,
> + &fw_buf[offset], bhdr->data_len);
> + if (ret) {
> + printk("failed to write firmware: %d\n", ret);
> + goto _err;
> + }
> +
> + blocks--;
> + offset += bhdr->data_len;
> + }
> +
> + /* TODO: last byte should be the checksum and skip checksum for now */
> +
> + bootcmd.boot_addr = fhdr->entry_addr;
> + ret = sip_send_cmd(epub->sip, SIP_CMD_BOOTUP,
> + sizeof(struct sip_cmd_bootup), &bootcmd);
> + if (ret)
> + goto _err;
> +
> +_err:
> + kfree(fw_buf);
> +
> + return ret;
> +}
> +
> +MODULE_FIRMWARE(ESP_FW_NAME1);
> +MODULE_FIRMWARE(ESP_FW_NAME2);
> +MODULE_FIRMWARE(ESP_FW_NAME3);
> diff --git a/drivers/staging/esp8089/esp_pub.h b/drivers/staging/esp8089/esp_pub.h
> new file mode 100644
> index 000000000000..e329fb0999e9
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_pub.h
> @@ -0,0 +1,188 @@
> +/*
> + * Copyright (c) 2011 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + * Quentin Schulz <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + */
> +
> +#ifndef _ESP_PUB_H_
> +#define _ESP_PUB_H_
> +
> +#include <linux/etherdevice.h>
> +#include <linux/rtnetlink.h>
> +#include <linux/firmware.h>
> +#include <linux/sched.h>
> +#include <net/mac80211.h>
> +#include <net/cfg80211.h>
> +#include "sip2_common.h"
> +
> +enum esp_sdio_state {
> + ESP_SDIO_STATE_FIRST_INIT,
> + ESP_SDIO_STATE_FIRST_NORMAL_EXIT,
> + ESP_SDIO_STATE_FIRST_ERROR_EXIT,
> + ESP_SDIO_STATE_SECOND_INIT,
> + ESP_SDIO_STATE_SECOND_ERROR_EXIT,
> +};
> +
> +enum esp_tid_state {
> + ESP_TID_STATE_INIT,
> + ESP_TID_STATE_TRIGGER,
> + ESP_TID_STATE_PROGRESS,
> + ESP_TID_STATE_OPERATIONAL,
> + ESP_TID_STATE_WAIT_STOP,
> + ESP_TID_STATE_STOP,
> +};
> +
> +struct esp_tx_tid {
> + u8 state;
> + u8 cnt;
> + u16 ssn;
> +};
> +
> +#define WME_NUM_TID 16
> +struct esp_node {
> + struct esp_tx_tid tid[WME_NUM_TID];
> + struct ieee80211_sta *sta;
> + u8 ifidx;
> + u8 index;
> +};
> +
> +#define WME_AC_BE 2
> +#define WME_AC_BK 3
> +#define WME_AC_VI 1
> +#define WME_AC_VO 0
> +
> +struct esp_vif {
> + struct esp_pub *epub;
> + u8 index;
> + u32 beacon_interval;
> + bool ap_up;
> + struct timer_list beacon_timer;
> +};
> +
> +/* WLAN related, mostly... */
> +/*struct hw_scan_timeout {
> + * struct delayed_work w;
> + * struct ieee80211_hw *hw;
> + * };
> + */
> +
> +struct esp_wl {
> + u8 bssid[ETH_ALEN];
> + u8 req_bssid[ETH_ALEN];
> +
> + //struct hw_scan_timeout *hsd;
> + struct cfg80211_scan_request *scan_req;
> + atomic_t ptk_cnt;
> + atomic_t gtk_cnt;
> + atomic_t tkip_key_set;
> +
> + /* so far only 2G band */
> + struct ieee80211_supported_band sbands[NUM_NL80211_BANDS];
> +
> + unsigned long flags;
> + atomic_t off;
> +};
> +
> +struct esp_hw_idx_map {
> + u8 mac[ETH_ALEN];
> + u8 flag;
> +};
> +
> +#define ESP_WL_FLAG_RFKILL BIT(0)
> +#define ESP_WL_FLAG_HW_REGISTERED BIT(1)
> +#define ESP_WL_FLAG_CONNECT BIT(2)
> +#define ESP_WL_FLAG_STOP_TXQ BIT(3)
> +
> +#define ESP_PUB_MAX_VIF 2
> +#define ESP_PUB_MAX_STA 4 //for one interface
> +#define ESP_PUB_MAX_RXAMPDU 8 //for all interfaces
> +
> +enum {
> + ESP_PM_OFF = 0,
> + ESP_PM_TURNING_ON,
> + ESP_PM_ON,
> + ESP_PM_TURNING_OFF, /* Do NOT change the order */
> +};
> +
> +struct esp_ps {
> + u32 dtim_period;
> + u32 max_sleep_period;
> + unsigned long last_config_time;
> + atomic_t state;
> + bool nulldata_pm_on;
> +};
> +
> +struct esp_mac_prefix {
> + u8 mac_index;
> + u8 mac_addr_prefix[3];
> +};
> +
> +struct esp_pub {
> + struct device *dev;
> + struct ieee80211_hw *hw;
> + struct ieee80211_vif *vif;
> + u8 vif_slot;
> +
> + struct esp_sdio_ctrl *sif; /* serial interface control block, e.g. sdio */
> + enum esp_sdio_state sdio_state;
> + struct esp_sip *sip;
> + struct esp_wl wl;
> + struct esp_hw_idx_map hi_map[19];
> + struct esp_hw_idx_map low_map[ESP_PUB_MAX_VIF][2];
> + //u32 flags; //flags to represent rfkill switch,start
> + u8 roc_flags; //0: not in remain on channel state, 1: in roc state
> +
> + struct work_struct tx_work; /* attach to ieee80211 workqueue */
> + /* latest mac80211 has multiple tx queue, but we stick with single queue now */
> + spinlock_t rx_lock;
> + spinlock_t tx_ampdu_lock;
> + spinlock_t rx_ampdu_lock;
> + spinlock_t tx_lock;
> + struct mutex tx_mtx;
> + struct sk_buff_head txq;
> + atomic_t txq_stopped;
> +
> + struct work_struct sendup_work; /* attach to ieee80211 workqueue */
> + struct sk_buff_head txdoneq;
> + struct sk_buff_head rxq;
> +
> + struct workqueue_struct *esp_wkq;
> +
> + //u8 bssid[ETH_ALEN];
> + u8 mac_addr[ETH_ALEN];
> +
> + u32 rx_filter;
> + unsigned long scan_permit;
> + bool scan_permit_valid;
> + struct delayed_work scan_timeout_work;
> + u32 enodes_map;
> + u8 rxampdu_map;
> + u32 enodes_maps[ESP_PUB_MAX_VIF];
> + struct esp_node *enodes[ESP_PUB_MAX_STA + 1];
> + struct esp_node *rxampdu_node[ESP_PUB_MAX_RXAMPDU];
> + u8 rxampdu_tid[ESP_PUB_MAX_RXAMPDU];
> + struct esp_ps ps;
> + int enable_int;
> + int wait_reset;
> +};
> +
> +struct esp_pub *esp_pub_alloc_mac80211(struct device *dev);
> +int esp_pub_dealloc_mac80211(struct esp_pub *epub);
> +int esp_register_mac80211(struct esp_pub *epub);
> +
> +int esp_pub_init_all(struct esp_pub *epub);
> +
> +void esp_dsr(struct esp_pub *epub);
> +void hw_scan_done(struct esp_pub *epub, bool aborted);
> +void esp_rocdone_process(struct ieee80211_hw *hw, struct sip_evt_roc *report);
> +
> +struct esp_node *esp_get_node_by_addr(struct esp_pub *epub, const u8 *addr);
> +int esp_get_empty_rxampdu(struct esp_pub *epub, const u8 *addr, u8 tid);
> +int esp_get_exist_rxampdu(struct esp_pub *epub, const u8 *addr, u8 tid);
> +
> +#endif /* _ESP_PUB_H_ */
> diff --git a/drivers/staging/esp8089/esp_sif.h b/drivers/staging/esp8089/esp_sif.h
> new file mode 100644
> index 000000000000..293640b11b0c
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_sif.h
> @@ -0,0 +1,131 @@
> +/*
> + * Copyright (c) 2011 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + * Quentin Schulz <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + */
> +
> +#ifndef _ESP_SIF_H_
> +#define _ESP_SIF_H_
> +
> +#include "esp_pub.h"
> +#include <linux/mmc/host.h>
> +#include <linux/spi/spi.h>
> +
> +/* H/W SLC module definitions */
> +
> +#define SIF_SLC_BLOCK_SIZE 512
> +
> +/* S/W struct mapping to slc registers */
> +struct slc_host_regs {
> + /* do NOT read token_rdata
> + *
> + * u32 pf_data;
> + * u32 token_rdata;
> + */
> + u32 intr_raw;
> + u32 state_w0;
> + u32 state_w1;
> + u32 config_w0;
> + u32 config_w1;
> + u32 intr_status;
> + u32 config_w2;
> + u32 config_w3;
> + u32 config_w4;
> + u32 token_wdata;
> + u32 intr_clear;
> + u32 intr_enable;
> +};
> +
> +enum io_sync_type {
> + ESP_SIF_NOSYNC = 0,
> + ESP_SIF_SYNC,
> +};
> +
> +struct esp_sdio_ctrl {
> + struct sdio_func *func;
> + struct esp_pub *epub;
> +
> + struct list_head free_req;
> +
> + u8 *dma_buffer;
> +
> + spinlock_t scat_lock;
> + struct list_head scat_req;
> +
> + bool off;
> + atomic_t irq_handling;
> + const struct sdio_device_id *id;
> + u32 slc_blk_sz;
> + u32 target_id;
> + u32 slc_window_end_addr;
> +
> + struct slc_host_regs slc_regs;
> + atomic_t irq_installed;
> +
> +};
> +
> +#define SIF_TO_DEVICE 0x1
> +#define SIF_FROM_DEVICE 0x2
> +
> +#define SIF_SYNC 0x00000010
> +#define SIF_ASYNC 0x00000020
> +
> +#define SIF_BYTE_BASIS 0x00000040
> +#define SIF_BLOCK_BASIS 0x00000080
> +
> +#define SIF_FIXED_ADDR 0x00000100
> +#define SIF_INC_ADDR 0x00000200
> +
> +void sdio_io_writeb(struct esp_pub *epub, u8 value, int addr, int *res);
> +u8 sdio_io_readb(struct esp_pub *epub, int addr, int *res);
> +
> +void sif_enable_irq(struct esp_pub *epub);
> +void sif_disable_irq(struct esp_pub *epub);
> +void sif_disable_target_interrupt(struct esp_pub *epub);
> +
> +u32 sif_get_blksz(struct esp_pub *epub);
> +
> +void sif_dsr(struct sdio_func *func);
> +int sif_io_raw(struct esp_pub *epub, u32 addr, u8 *buf, u32 len, u32 flag);
> +int sif_io_sync(struct esp_pub *epub, u32 addr, u8 *buf, u32 len, u32 flag);
> +int sif_lldesc_read_sync(struct esp_pub *epub, u8 *buf, u32 len);
> +int sif_lldesc_write_sync(struct esp_pub *epub, u8 *buf, u32 len);
> +int sif_lldesc_read_raw(struct esp_pub *epub, u8 *buf, u32 len, bool noround);
> +int sif_lldesc_write_raw(struct esp_pub *epub, u8 *buf, u32 len);
> +
> +int esp_common_read(struct esp_pub *epub, u8 *buf, u32 len, int sync,
> + bool noround);
> +int esp_common_write(struct esp_pub *epub, u8 *buf, u32 len, int sync);
> +int esp_common_read_with_addr(struct esp_pub *epub, u32 addr, u8 *buf, u32 len,
> + int sync);
> +int esp_common_write_with_addr(struct esp_pub *epub, u32 addr, u8 *buf,
> + u32 len, int sync);
> +
> +int esp_common_readbyte_with_addr(struct esp_pub *epub, u32 addr, u8 *buf,
> + int sync);
> +int esp_common_writebyte_with_addr(struct esp_pub *epub, u32 addr, u8 buf,
> + int sync);
> +
> +struct slc_host_regs *sif_get_regs(struct esp_pub *epub);
> +
> +void sif_lock_bus(struct esp_pub *epub);
> +void sif_unlock_bus(struct esp_pub *epub);
> +
> +int sif_interrupt_target(struct esp_pub *epub, u8 index);
> +
> +void check_target_id(struct esp_pub *epub);
> +
> +void sif_record_bt_config(int value);
> +int sif_get_bt_config(void);
> +void sif_record_rst_config(int value);
> +int sif_get_rst_config(void);
> +void sif_record_ate_config(int value);
> +int sif_get_ate_config(void);
> +void sif_record_wakeup_gpio_config(int value);
> +int sif_get_wakeup_gpio_config(void);
> +#endif /* _ESP_SIF_H_ */
> diff --git a/drivers/staging/esp8089/esp_sip.c b/drivers/staging/esp8089/esp_sip.c
> new file mode 100644
> index 000000000000..91fb64c9b794
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_sip.c
> @@ -0,0 +1,1718 @@
> +/*
> + * Copyright (c) 2009 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + * Quentin Schulz <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + */
> +
> +#include <linux/ieee80211.h>
> +#include <net/mac80211.h>
> +#include <net/cfg80211.h>
> +#include <linux/skbuff.h>
> +#include <linux/bitops.h>
> +#include <linux/mmc/card.h>
> +#include <linux/mmc/mmc.h>
> +#include <linux/mmc/host.h>
> +#include <linux/mmc/sdio_func.h>
> +#include <linux/mmc/sdio_ids.h>
> +#include <linux/mmc/sdio.h>
> +#include <linux/mmc/sd.h>
> +#include <linux/completion.h>
> +
> +#include "esp_mac80211.h"
> +#include "esp_pub.h"
> +#include "esp_sip.h"
> +#include "esp_ctrl.h"
> +#include "esp_sif.h"
> +#include "esp_debug.h"
> +#include "slc_host_register.h"
> +#include "esp_wmac.h"
> +#include "esp_utils.h"
> +
> +extern struct completion *gl_bootup_cplx;
> +
> +static int old_signal = -35;
> +static int avg_signal;
> +static int signal_loop;
> +
> +struct esp_mac_prefix esp_mac_prefix_table[] = {
> + {0, {0x18, 0xfe, 0x34} },
> + {1, {0xac, 0xd0, 0x74} },
> + {255, {0x18, 0xfe, 0x34} },
> +};
> +
> +#define SIGNAL_COUNT 300
> +
> +/* FIXME: Incomplete ternary condition */
> +#define TID_TO_AC(_tid) (!(_tid) || (_tid) == 3 ? WME_AC_BE : ((_tid) < 3 ? WME_AC_BK : ((_tid) < 6 ? WME_AC_VI : WME_AC_VO)))
> +
> +#define esp_sip_dbg esp_dbg
> +struct sip_trace {
> + u32 tx_data;
> + u32 tx_cmd;
> + u32 rx_data;
> + u32 rx_evt;
> + u32 rx_tx_status;
> + u32 tx_out_of_credit;
> + u32 tx_one_shot_overflow;
> +};
> +
> +static struct sip_trace str;
> +
> +#define STRACE_TX_DATA_INC() (str.tx_data++)
> +#define STRACE_TX_CMD_INC() (str.tx_cmd++)
> +#define STRACE_RX_DATA_INC() (str.rx_data++)
> +#define STRACE_RX_EVENT_INC() (str.rx_evt++)
> +#define STRACE_RX_TXSTATUS_INC() (str.rx_tx_status++)
> +#define STRACE_TX_OUT_OF_CREDIT_INC() (str.tx_out_of_credit++)
> +#define STRACE_TX_ONE_SHOT_INC() (str.tx_one_shot_overflow++)
> +
> +#define SIP_STOP_QUEUE_THRESHOLD 48
> +#define SIP_RESUME_QUEUE_THRESHOLD 12
> +
> +#define SIP_MIN_DATA_PKT_LEN (sizeof(struct esp_mac_rx_ctrl) + 24) //24 is min 80211hdr
> +
> +static void sip_recalc_credit_init(struct esp_sip *sip);
> +
> +static int sip_recalc_credit_claim(struct esp_sip *sip, int force);
> +
> +static void sip_recalc_credit_release(struct esp_sip *sip);
> +
> +static struct sip_pkt *sip_get_ctrl_buf(struct esp_sip *sip,
> + enum sip_buf_type bftype);
> +
> +static void sip_reclaim_ctrl_buf(struct esp_sip *sip, struct sip_pkt *pkt,
> + enum sip_buf_type bftype);
> +
> +static void sip_free_init_ctrl_buf(struct esp_sip *sip);
> +
> +static int sip_pack_pkt(struct esp_sip *sip, struct sk_buff *skb,
> + int *pm_state);
> +
> +static struct esp_mac_rx_ctrl *sip_parse_normal_mac_ctrl(struct sk_buff *skb,
> + int *pkt_len_enc,
> + int *buf_len,
> + int *pulled_len);
> +
> +static struct sk_buff *sip_parse_data_rx_info(struct esp_sip *sip,
> + struct sk_buff *skb,
> + int pkt_len_enc, int buf_len,
> + struct esp_mac_rx_ctrl *mac_ctrl,
> + int *pulled_len);
> +
> +static inline void sip_rx_pkt_enqueue(struct esp_sip *sip, struct sk_buff *skb);
> +
> +static void sip_update_tx_credits(struct esp_sip *sip, u16 recycled_credits);
> +
> +static bool sip_rx_pkt_process(struct esp_sip *sip, struct sk_buff *skb);
> +
> +static void sip_tx_status_report(struct esp_sip *sip, struct sk_buff *skb,
> + struct ieee80211_tx_info *tx_info,
> + bool success);
> +
> +static bool check_ac_tid(u8 *pkt, u8 ac, u8 tid)
> +{
> + struct ieee80211_hdr *wh = (struct ieee80211_hdr *)pkt;
> +
> + if (!ieee80211_is_data_qos(wh->frame_control) &&
> + !ieee80211_is_mgmt(wh->frame_control) &&
> + !ieee80211_is_ctl(wh->frame_control)) {
> + if (tid || ac != WME_AC_BE) {
> + pr_info("444 ac:%u, tid:%u\n", ac, tid);
> +
> + if (tid == 7 && ac == WME_AC_VO)
> + return false;
> + }
> +
> + return true; //hack to modify non-qos null data.
> + }
> +
> + return false;
> +}
> +
> +static void sip_recalc_credit_timeout(unsigned long data)
> +{
> + struct esp_sip *sip = (struct esp_sip *)data;
> +
> + sip_recalc_credit_claim(sip, 1); /* recalc again */
> +}
> +
> +static void sip_recalc_credit_init(struct esp_sip *sip)
> +{
> + atomic_set(&sip->credit_status, RECALC_CREDIT_DISABLE); //set it disable
> +
> + setup_timer(&sip->credit_timer, sip_recalc_credit_timeout,
> + (unsigned long)sip);
> +}
> +
> +static int sip_recalc_credit_claim(struct esp_sip *sip, int force)
> +{
> + int ret;
> +
> + if (atomic_read(&sip->credit_status) == RECALC_CREDIT_ENABLE && !force)
> + return 1;
Put a comment to describe what why we are returning a mix of 1, zero and
negative error codes. It's impossible to tell from the context because
none of the callers check.
> +
> + atomic_set(&sip->credit_status, RECALC_CREDIT_ENABLE);
> + ret = sip_send_recalc_credit(sip->epub);
> + if (ret) {
> + dev_err(sip->epub->dev, "sending recalc credit failed: %d",
> + ret);
> + return ret;
> + }
> +
> + /*setup a timer for handle the abs_credit not receive */
> + mod_timer(&sip->credit_timer, jiffies + msecs_to_jiffies(2000));
> +
> + return ret;
return 0;
> +}
> +
> +static void sip_recalc_credit_release(struct esp_sip *sip)
> +{
> + if (atomic_read(&sip->credit_status) == RECALC_CREDIT_ENABLE) {
> + atomic_set(&sip->credit_status, RECALC_CREDIT_DISABLE);
> + del_timer_sync(&sip->credit_timer);
> + } else {
> + dev_dbg(sip->epub->dev, "maybe bogus credit");
> + }
> +}
> +
> +static void sip_update_tx_credits(struct esp_sip *sip, u16 recycled_credits)
> +{
> + if (recycled_credits & 0x800) {
> + atomic_set(&sip->tx_credits, (recycled_credits & 0x7ff));
> + sip_recalc_credit_release(sip);
> + } else {
> + atomic_add(recycled_credits, &sip->tx_credits);
> + }
> +}
> +
> +void sip_trigger_txq_process(struct esp_sip *sip)
> +{
> + if (atomic_read(&sip->tx_credits) <= sip->credit_to_reserve + SIP_CTRL_CREDIT_RESERVE ||
> + atomic_read(&sip->credit_status) == RECALC_CREDIT_ENABLE)
> + return;
> +
> + if (sip_queue_may_resume(sip)) {
> + /* wakeup upper queue only if we have sufficient credits */
> + atomic_set(&sip->epub->txq_stopped, false);
> + ieee80211_wake_queues(sip->epub->hw);
> + } else if (atomic_read(&sip->epub->txq_stopped)) {
> + dev_err(sip->epub->dev, "%s can't wake txq, credits: %d\n",
> + __func__, atomic_read(&sip->tx_credits));
> + }
> +
> + if (!skb_queue_empty(&sip->epub->txq)) {
> + /* try to send out pkt already in sip queue once we have credits */
> +#if !defined(FPGA_TXDATA)
> + if (!sif_get_ate_config())
> + ieee80211_queue_work(sip->epub->hw,
> + &sip->epub->tx_work);
> + else
> + queue_work(sip->epub->esp_wkq, &sip->epub->tx_work);
> +#else
> + queue_work(sip->epub->esp_wkq, &sip->epub->tx_work);
> +#endif
> + }
> +}
> +
> +static bool sip_ampdu_occupy_buf(struct esp_sip *sip,
> + struct esp_rx_ampdu_len *ampdu_len)
> +{
> + return (!ampdu_len->substate ||
> + esp_wmac_rxsec_error(ampdu_len->substate) ||
> + (sip->dump_rpbm_err && ampdu_len->substate == RX_RPBM_ERR));
> +}
> +
> +/* TODO: HARDCORE CLEANUP */
> +
> +static bool sip_rx_pkt_process(struct esp_sip *sip, struct sk_buff *skb)
> +{
> + struct sip_hdr *hdr;
> + struct sk_buff *rskb;
> + struct esp_mac_rx_ctrl *mac_ctrl, new_mac_ctrl;
> + int remains_len, first_pkt_len, ret = 0, pkt_len_enc = 0, buf_len = 0,
> + pulled_len = 0, pkt_num;
> + u8 *bufptr;
> + bool trigger_rxq = false, have_rxabort = false, have_goodpkt = false;
> + struct esp_rx_ampdu_len *ampdu_len;
> + static int pkt_dropped, pkt_total;
> + static u8 frame_head[16];
> + static u8 frame_buf_ttl;
> +
> + if (!skb)
> + return trigger_rxq;
> +
> + hdr = (struct sip_hdr *)skb->data;
> + bufptr = skb->data;
> +
> + if (hdr->h_credits & SIP_CREDITS_MASK)
> + sip_update_tx_credits(sip, hdr->h_credits & SIP_CREDITS_MASK);
> +
> + hdr->h_credits &= ~SIP_CREDITS_MASK; /* clean credits in sip_hdr, prevent over-add */
> +
> + /* first pkt's length is stored in recycled_credits first 20 bits
> + * config w3 [31:12]
> + * repair hdr->len of first pkt
> + */
> + remains_len = hdr->len;
> + first_pkt_len = hdr->h_credits >> 12;
> + hdr->len = first_pkt_len;
> +
> + if (first_pkt_len > remains_len) {
> + sip_recalc_credit_claim(sip, 0);
> + show_buf((u8 *)hdr, first_pkt_len);
This feels like an info leak. first_pkt_len comes from the network.
> + ESSERT(0);
> + goto _exit;
> + }
> +
> + /* pkts handling, including the first pkt, should alloc new skb for each data pkt.
> + * free the original whole skb after parsing is done.
> + */
> + while (remains_len) {
> + if (remains_len < sizeof(struct sip_hdr)) {
> + sip_recalc_credit_claim(sip, 0);
> + ESSERT(0);
> + show_buf((u8 *)hdr, 512);
> + goto _exit;
> + }
> +
> + hdr = (struct sip_hdr *)bufptr;
> + if (hdr->len <= 0 || hdr->len & 3) {
> + sip_recalc_credit_claim(sip, 0);
> + show_buf((u8 *)hdr, 512);
> + ESSERT(0);
> + goto _exit;
> + }
> +
> + if (unlikely(hdr->seq != sip->rxseq++)) {
> + sip_recalc_credit_claim(sip, 0);
> + dev_dbg(sip->epub->dev,
> + "%s seq mismatch! got %u, expect %u\n",
> + __func__, hdr->seq, sip->rxseq - 1);
> + sip->rxseq = hdr->seq + 1;
> + show_buf(bufptr, 32);
> + ESSERT(0);
> + }
> +
> + if (SIP_HDR_IS_CTRL(hdr)) {
> + STRACE_RX_EVENT_INC();
> +
> + ret = sip_parse_events(sip, bufptr);
> + skb_pull(skb, hdr->len);
> + } else if (SIP_HDR_IS_DATA(hdr)) {
> + STRACE_RX_DATA_INC();
> + mac_ctrl = sip_parse_normal_mac_ctrl(skb, &pkt_len_enc,
> + &buf_len,
> + &pulled_len);
> + rskb = sip_parse_data_rx_info(sip, skb, pkt_len_enc,
> + buf_len, mac_ctrl,
> + &pulled_len);
> +
> + if (!rskb)
> + goto _move_on;
> +
> + if (likely(atomic_read(&sip->epub->wl.off) == 0)) {
> + local_bh_disable();
> + ieee80211_rx(sip->epub->hw, rskb);
> + local_bh_enable();
> + } else {
> + /* still need go thro parsing as skb_pull should invoke */
> + kfree_skb(rskb);
> + }
> + } else if (SIP_HDR_IS_AMPDU(hdr)) {
> + ampdu_len = (struct esp_rx_ampdu_len *)(skb->data + hdr->len / sip->rx_blksz * sip->rx_blksz);
> + dev_dbg(sip->epub->dev, "%s rx ampdu total len %u\n",
> + __func__, hdr->len);
> + if (skb->data != (u8 *)hdr) {
> + printk("%p %p\n", skb->data, hdr);
> + show_buf(skb->data, 512);
> + show_buf((u8 *)hdr, 512);
> + ESSERT(0);
> + goto _exit;
> + }
> + mac_ctrl = sip_parse_normal_mac_ctrl(skb, NULL, NULL,
> + &pulled_len);
> + memcpy(&new_mac_ctrl, mac_ctrl,
> + sizeof(struct esp_mac_rx_ctrl));
> + mac_ctrl = &new_mac_ctrl;
> + pkt_num = mac_ctrl->ampdu_cnt;
> + dev_dbg(sip->epub->dev,
> + "%s %d rx ampdu %u pkts, %d pkts dumped, first len %u\n",
> + __func__, __LINE__,
> + (unsigned int)((hdr->len % sip->rx_blksz) /
> + sizeof(struct esp_rx_ampdu_len)),
> + pkt_num, (unsigned int)ampdu_len->sublen);
> +
> + pkt_total += mac_ctrl->ampdu_cnt;
> + while (pkt_num > 0) {
> + dev_dbg(sip->epub->dev,
> + "%s %d ampdu sub state %02x,\n",
> + __func__, __LINE__,
> + ampdu_len->substate);
> +
> + if (sip_ampdu_occupy_buf(sip, ampdu_len)) { //pkt is dumped
> + rskb = sip_parse_data_rx_info(sip, skb,
> + ampdu_len->sublen - FCS_LEN,
> + 0,
> + mac_ctrl,
> + &pulled_len);
> + if (!rskb) {
> + ESSERT(0);
> + goto _exit;
> + }
> +
> + if (likely(atomic_read(&sip->epub->wl.off) == 0) &&
> + (ampdu_len->substate == 0 ||
> + ampdu_len->substate == RX_TKIPMIC_ERR ||
> + (sip->sendup_rpbm_pkt &&
> + ampdu_len->substate == RX_RPBM_ERR)) &&
> + (sip->rxabort_fixed ||
> + !have_rxabort)) {
> + if (!have_goodpkt) {
> + have_goodpkt = true;
> + memcpy(frame_head,
> + rskb->data,
> + 16);
> + frame_head[1] &= ~0x80;
> + frame_buf_ttl = 3;
> + }
> + local_bh_disable();
> + ieee80211_rx(sip->epub->hw,
> + rskb);
> + local_bh_enable();
> + } else {
> + kfree_skb(rskb);
> + }
> + } else {
> + if (ampdu_len->substate == RX_ABORT) {
> + u8 *a;
> + have_rxabort = true;
> + dev_dbg(sip->epub->dev,
> + "rx abort %d %d\n",
> + frame_buf_ttl, pkt_num);
> + if (frame_buf_ttl &&
> + !sip->rxabort_fixed) {
> + struct esp_rx_ampdu_len *next_good_ampdu_len = ampdu_len + 1;
> + a = frame_head;
> + while (!sip_ampdu_occupy_buf(sip, next_good_ampdu_len)) {
> + if (next_good_ampdu_len > ampdu_len + pkt_num - 1)
> + break;
> + next_good_ampdu_len++;
> + }
> + if (next_good_ampdu_len <= ampdu_len + pkt_num - 1) {
> + bool b0, b10, b11;
> + a = skb->data;
> + b0 = memcmp(frame_head + 4, skb->data + 4, 12) == 0;
> + b10 = memcmp(frame_head + 10, skb->data, 6) == 0;
> + b11 = memcpy(frame_head + 11, skb->data, 5) == 0;
> + if (b0 && !b10 &&
> + !b11) {
> + have_rxabort = false;
> + } else if (!b0 &&
> + b10 &&
> + !b11) {
> + skb_push(skb, 10);
> + memcpy(skb->data,
> + frame_head,
> + 10);
> + have_rxabort = false;
> + pulled_len -= 10;
> + } else if (!b0 && !b10 && b11) {
> + skb_push(skb, 11);
> + memcpy(skb->data,
> + frame_head,
> + 11);
> + have_rxabort = false;
> + pulled_len -= 11;
> + }
> + }
> + }
> + }
> + pkt_dropped++;
> + dev_dbg(sip->epub->dev,
> + "%s ampdu dropped %d/%d\n",
> + __func__, pkt_dropped,
> + pkt_total);
> + }
> + pkt_num--;
> + ampdu_len++;
> + }
> + if (frame_buf_ttl)
> + frame_buf_ttl--;
> + skb_pull(skb, hdr->len - pulled_len);
> + } else {
> + dev_err(sip->epub->dev, "unknown SIP HDR type\n");
> + }
> +
> +_move_on:
> + if (hdr->len < remains_len)
> + remains_len -= hdr->len;
> + else
> + break;
> + bufptr += hdr->len;
> + }
> +
> +_exit:
> + kfree_skb(skb);
> +
> + return trigger_rxq;
> +}
> +
> +static void _sip_rxq_process(struct esp_sip *sip)
> +{
> + struct sk_buff *skb;
> + bool sendup = false;
> +
> + while ((skb = skb_dequeue(&sip->rxq)))
> + if (sip_rx_pkt_process(sip, skb))
> + sendup = true;
> +
> + if (sendup)
> + queue_work(sip->epub->esp_wkq, &sip->epub->sendup_work);
> +
> + /* probably tx_credit is updated, try txq */
> + sip_trigger_txq_process(sip);
> +}
> +
> +void sip_rxq_process(struct work_struct *work)
> +{
> + struct esp_sip *sip = container_of(work, struct esp_sip,
> + rx_process_work);
> +
> + if (!sip) {
> + ESSERT(0);
> + return;
> + }
> +
> + if (unlikely(atomic_read(&sip->state) == SIP_SEND_INIT)) {
> + sip_send_chip_init(sip);
> + atomic_set(&sip->state, SIP_WAIT_BOOTUP);
> + return;
> + }
> +
> + mutex_lock(&sip->rx_mtx);
> + _sip_rxq_process(sip);
> + mutex_unlock(&sip->rx_mtx);
> +}
> +
> +static inline void sip_rx_pkt_enqueue(struct esp_sip *sip, struct sk_buff *skb)
> +{
> + skb_queue_tail(&sip->rxq, skb);
> +}
> +
> +static u32 sip_rx_count;
> +
> +int sip_rx(struct esp_pub *epub)
> +{
> + struct sip_hdr *shdr;
> + struct esp_sip *sip = epub->sip;
> + struct sk_buff *first_skb, *rx_skb;
> + u8 *rx_buf;
> + u32 rx_blksz, first_sz;
> + int err;
> + u8 raw_seq;
> +
> + if (likely(sif_get_ate_config() != 1)) {
> + raw_seq = sif_get_regs(epub)->intr_raw & 0xff;
> +
> + if (raw_seq != sip->to_host_seq) {
> + if (raw_seq == sip->to_host_seq + 1) { /* when last read pkt crc err, this situation may occur, but raw_seq mustn't < to_host_Seq */
> + sip->to_host_seq = raw_seq;
> + dev_dbg(epub->dev,
> + "warn: to_host_seq reg 0x%02x, seq 0x%02x",
> + raw_seq, sip->to_host_seq);
> + } else {
> + dev_err(epub->dev,
> + "err: to_host_seq reg 0x%02x, seq 0x%02x",
> + raw_seq, sip->to_host_seq);
> + err = 0;
> + goto _err;
This function uses a mix of direct returns and goto _err. Just use
direct returns unless there is a good reason in the present (not future)
for the goto. It just hurts readability to have to scroll 2 pages down
to see what the goto does. "return 0;" is much quicker.
> + }
> + }
> + }
> +
> + /* first read one block out, if we luck enough, that's it
> + *
> + * To make design as simple as possible, we allocate skb(s)
> + * separately for each sif read operation to avoid global
> + * read_buf_pointe access. It coule be optimized late.
> + */
> +
> + first_sz = sif_get_regs(epub)->config_w0;
> + rx_blksz = sif_get_blksz(epub);
> + first_skb = __dev_alloc_skb(roundup(first_sz, rx_blksz), GFP_KERNEL);
> +
> + if (!first_skb) {
> + sif_unlock_bus(epub);
> + err = 0;
> + goto _err;
> + }
> +
> + rx_buf = skb_put(first_skb, first_sz);
> +
> + err = esp_common_read(epub, rx_buf, first_sz, ESP_SIF_NOSYNC, false);
> + sip_rx_count++;
> + if (unlikely(err)) {
> + dev_err(epub->dev, " first read err %d %d\n", err,
> + sif_get_regs(epub)->config_w0);
> + kfree_skb(first_skb);
> + sif_unlock_bus(epub);
> + goto _err;
I feel like we should create a error goto the unlocks and frees
first_skb.
> + }
> +
> + shdr = (struct sip_hdr *)rx_buf;
> + if (SIP_HDR_IS_CTRL(shdr) && shdr->c_evtid == SIP_EVT_SLEEP) {
> + atomic_set(&sip->epub->ps.state, ESP_PM_ON);
> + }
> +
> + if (likely(sif_get_ate_config() != 1))
> + sip->to_host_seq++;
> +
> + if (shdr->len & 3) {
> + dev_err(epub->dev, "shdr->len[%d] error\n", shdr->len);
> + kfree_skb(first_skb);
> + sif_unlock_bus(epub);
> + err = -EIO;
> + goto _err;
> + }
> +
> + if (shdr->len != first_sz) {
> + dev_err(epub->dev, "shdr->len[%d] first_size[%d] error\n",
> + shdr->len, first_sz);
> + kfree_skb(first_skb);
> + sif_unlock_bus(epub);
> + err = -EIO;
> + goto _err;
> + } else {
> + sif_unlock_bus(epub);
> + skb_trim(first_skb, shdr->len);
> + dev_dbg(epub->dev, "first_skb only\n");
> +
> + rx_skb = first_skb;
> + }
> +
> + if (atomic_read(&sip->state) == SIP_STOP) {
> + kfree_skb(rx_skb);
> + dev_err(epub->dev, "rx packet while sip stopped\n");
We don't unlock on this path?
> + return 0;
> + }
> +
> + sip_rx_pkt_enqueue(sip, rx_skb);
> + queue_work(sip->epub->esp_wkq, &sip->rx_process_work);
> +
> +_err:
> + return err;
> +}
> +
> +int sip_post_init(struct esp_sip *sip, struct sip_evt_bootup2 *bevt)
> +{
> + struct esp_pub *epub;
> + u8 mac_id = bevt->mac_addr[0];
> + int mac_index = 0, i;
> +
> + if (!sip) {
> + ESSERT(0);
> + return -EINVAL;
> + }
> +
> + epub = sip->epub;
> +
> + sip->tx_aggr_write_ptr = sip->tx_aggr_buf;
> + sip->tx_blksz = bevt->tx_blksz;
> + sip->rx_blksz = bevt->rx_blksz;
> + sip->credit_to_reserve = bevt->credit_to_reserve;
> + sip->dump_rpbm_err = bevt->options & SIP_DUMP_RPBM_ERR;
> + sip->rxabort_fixed = bevt->options & SIP_RXABORT_FIXED;
> + sip->support_bgscan = bevt->options & SIP_SUPPORT_BGSCAN;
> + sip->sendup_rpbm_pkt = 0;
> +
> + /* print out MAC addr... */
> + memcpy(epub->mac_addr, bevt->mac_addr, ETH_ALEN);
> + for (i = 0;
> + i < sizeof(esp_mac_prefix_table) / sizeof(struct esp_mac_prefix);
> + i++)
> + if (esp_mac_prefix_table[i].mac_index == mac_id) {
> + mac_index = i;
> + break;
> + }
> +
> + epub->mac_addr[0] = esp_mac_prefix_table[mac_index].mac_addr_prefix[0];
> + epub->mac_addr[1] = esp_mac_prefix_table[mac_index].mac_addr_prefix[1];
> + epub->mac_addr[2] = esp_mac_prefix_table[mac_index].mac_addr_prefix[2];
> +
> + atomic_set(&sip->noise_floor, bevt->noise_floor);
> +
> + sip_recalc_credit_init(sip);
> +
> + return 0;
> +}
> +
> +/* write pkts in aggr buf to target memory */
> +static void sip_write_pkts(struct esp_sip *sip, int pm_state)
> +{
> + int tx_aggr_len, err;
> + struct sip_hdr *first_shdr;
> +
> + tx_aggr_len = sip->tx_aggr_write_ptr - sip->tx_aggr_buf;
> + if (tx_aggr_len < sizeof(struct sip_hdr)) {
> + dev_err(sip->epub->dev, "[tx_aggr_len] %d < sizeof(sip_hdr)\n",
> + tx_aggr_len);
> + ESSERT(0);
> + return;
> + }
> +
> + if (tx_aggr_len & 0x3) {
> + ESSERT(0);
> + return;
> + }
> +
> + first_shdr = (struct sip_hdr *)sip->tx_aggr_buf;
> +
> + if (atomic_read(&sip->tx_credits) <= SIP_CREDITS_LOW_THRESHOLD)
> + first_shdr->fc[1] |= SIP_HDR_F_NEED_CRDT_RPT;
> +
> + /* still use lock bus instead of sif_lldesc_write_sync since we want to protect several global varibles assignments */
> + sif_lock_bus(sip->epub);
> +
> + err = esp_common_write(sip->epub, sip->tx_aggr_buf, tx_aggr_len,
> + ESP_SIF_NOSYNC);
> + if (err)
> + dev_err(sip->epub->dev, "error while writing pkts: %d\n", err);
> +
> + sip->tx_aggr_write_ptr = sip->tx_aggr_buf;
> + sip->tx_tot_len = 0;
> +
> + sif_unlock_bus(sip->epub);
> +}
> +
> +/* setup sip header and tx info, copy pkt into aggr buf */
> +static int sip_pack_pkt(struct esp_sip *sip, struct sk_buff *skb, int *pm_state)
> +{
> + struct ieee80211_tx_info *itx_info;
> + struct sip_hdr *shdr;
> + struct ieee80211_hdr *wh;
> + struct esp_vif *evif;
> + struct esp_node *node;
> + u32 tx_len, offset;
> + bool is_data = true;
> + u8 sta_index;
> + int alg;
> +
> + itx_info = IEEE80211_SKB_CB(skb);
> + if (itx_info->flags == 0xffffffff) {
> + shdr = (struct sip_hdr *)skb->data;
> + is_data = false;
> + tx_len = skb->len;
> + } else {
> + wh = (struct ieee80211_hdr *)skb->data;
> + evif = (struct esp_vif *)itx_info->control.vif->drv_priv;
> + /* update sip header */
> + shdr = (struct sip_hdr *)sip->tx_aggr_write_ptr;
> +
> + shdr->fc[0] = 0;
> + shdr->fc[1] = 0;
> +
> + if (itx_info->flags & IEEE80211_TX_CTL_AMPDU)
> + SIP_HDR_SET_TYPE(shdr->fc[0], SIP_DATA_AMPDU);
> + else
> + SIP_HDR_SET_TYPE(shdr->fc[0], SIP_DATA);
> +
> + if (!evif->epub) {
> + sip_tx_status_report(sip, skb, itx_info, false);
> + atomic_dec(&sip->tx_data_pkt_queued);
> + return -EINVAL;
> + }
> +
> + /* make room for encrypted pkt */
> + if (itx_info->control.hw_key) {
> + alg = esp_cipher2alg(itx_info->control.hw_key->cipher);
> + if (unlikely(alg == -1)) {
> + sip_tx_status_report(sip, skb, itx_info, false);
> + atomic_dec(&sip->tx_data_pkt_queued);
> + return -1;
> + }
> +
> + shdr->d_enc_flag = alg + 1;
> + shdr->d_hw_kid = itx_info->control.hw_key->hw_key_idx |
> + (evif->index << 7);
> + } else {
> + shdr->d_enc_flag = 0;
> + shdr->d_hw_kid = evif->index << 7 | evif->index;
> + }
> +
> + /* update sip tx info */
> + node = esp_get_node_by_addr(sip->epub, wh->addr1);
> + if (node)
> + sta_index = node->index;
> + else
> + sta_index = ESP_PUB_MAX_STA + 1;
> +
> + SIP_HDR_SET_IFIDX(shdr->fc[0], evif->index << 3 | sta_index);
> + shdr->d_p2p = itx_info->control.vif->p2p;
> +
> + if (evif->index == 1)
> + shdr->d_p2p = 1;
> +
> + shdr->d_ac = skb_get_queue_mapping(skb);
> + shdr->d_tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
> +
> + wh = (struct ieee80211_hdr *)skb->data;
> +
> + if (ieee80211_is_mgmt(wh->frame_control)) {
> + /* addba/delba/bar may use different tid/ac */
> + if (shdr->d_ac == WME_AC_VO)
> + shdr->d_tid = 7;
> +
> + if (ieee80211_is_beacon(wh->frame_control)) {
> + shdr->d_tid = 8;
> + shdr->d_ac = 4;
> + }
> + }
> +
> + if (check_ac_tid(skb->data, shdr->d_ac, shdr->d_tid)) {
> + shdr->d_ac = WME_AC_BE;
> + shdr->d_tid = 0;
> + }
> +
> + /* make sure data is start at 4 bytes aligned addr. */
> + offset = roundup(sizeof(struct sip_hdr), 4);
> +
> + if (SIP_HDR_IS_AMPDU(shdr)) {
> + memset(sip->tx_aggr_write_ptr + offset, 0,
> + sizeof(struct esp_tx_ampdu_entry));
> + offset += roundup(sizeof(struct esp_tx_ampdu_entry), 4);
> + }
> +
> + tx_len = offset + skb->len;
> + shdr->len = tx_len; /* actual len */
> + }
> +
> + shdr->seq = sip->txseq++;
> +
> + /* copy skb to aggr buf */
> + memcpy(sip->tx_aggr_write_ptr + offset, skb->data, skb->len);
> +
> + if (is_data) {
> + spin_lock_bh(&sip->epub->tx_lock);
> + sip->txdataseq = shdr->seq;
> + spin_unlock_bh(&sip->epub->tx_lock);
> +
> + /* fake a tx_status and report to mac80211 stack to speed up tx, may affect
> + * 1) rate control (now it's all in target, so should be OK)
> + * 2) ps mode, mac80211 want to check ACK of ps/nulldata to see if AP is awake
> + * 3) BAR, mac80211 do BAR by checking ACK
> + *
> + * XXX: need to adjust for 11n, e.g. report tx_status according to BA received in target
> + */
> + sip_tx_status_report(sip, skb, itx_info, true);
> + atomic_dec(&sip->tx_data_pkt_queued);
> +
> + STRACE_TX_DATA_INC();
> + } else {
> + /* check pm state here */
> +
> + /* no need to hold ctrl skb */
> + sip_free_ctrl_skbuff(sip, skb);
> + STRACE_TX_CMD_INC();
> + }
> +
> + /* TBD: roundup here or whole aggr-buf */
> + tx_len = roundup(tx_len, sip->tx_blksz);
> +
> + sip->tx_aggr_write_ptr += tx_len;
> + sip->tx_tot_len += tx_len;
> +
> + return 0;
> +}
> +
> +static void sip_tx_status_report(struct esp_sip *sip, struct sk_buff *skb,
> + struct ieee80211_tx_info *tx_info,
> + bool success)
> +{
> + struct ieee80211_hdr *wh;
> + struct esp_node *node;
> + struct esp_tx_tid *tid;
> + struct ieee80211_sta *sta;
> + u8 tidno;
> +
> + if (!(tx_info->flags & IEEE80211_TX_CTL_AMPDU)) {
> + if (likely(success))
> + tx_info->flags |= IEEE80211_TX_STAT_ACK;
> + else
> + tx_info->flags &= ~IEEE80211_TX_STAT_ACK;
> +
> + /* manipulate rate status... */
> + tx_info->status.rates[0].idx = 11;
> + tx_info->status.rates[0].count = 1;
> + tx_info->status.rates[0].flags = 0;
> + tx_info->status.rates[1].idx = -1;
> + } else {
> + tx_info->flags |= IEEE80211_TX_STAT_AMPDU |
> + IEEE80211_TX_STAT_ACK;
> + tx_info->status.ampdu_len = 1;
> + tx_info->status.ampdu_ack_len = 1;
> +
> + /* manipulate rate status... */
> + tx_info->status.rates[0].idx = 7;
> + tx_info->status.rates[0].count = 1;
> + tx_info->status.rates[0].flags = IEEE80211_TX_RC_MCS |
> + IEEE80211_TX_RC_SHORT_GI;
> + tx_info->status.rates[1].idx = -1;
> + }
> +
> + if (!mod_support_no_txampdu() &&
> + cfg80211_get_chandef_type(&sip->epub->hw->conf.chandef) != NL80211_CHAN_NO_HT) {
> + wh = (struct ieee80211_hdr *)skb->data;
> +
> + if (ieee80211_is_data_qos(wh->frame_control) &&
> + !(IEEE80211_SKB_CB(skb)->flags & IEEE80211_TX_CTL_AMPDU)) {
> + tidno = ieee80211_get_qos_ctl(wh)[0] &
> + IEEE80211_QOS_CTL_TID_MASK;
> +
> + node = esp_get_node_by_addr(sip->epub, wh->addr1);
> + if (!node || !node->sta)
> + goto _exit;
> +
> + sta = node->sta;
> + tid = &node->tid[tidno];
> + if (!tid) {
> + ESSERT(0);
> + goto _exit;
> + }
> +
> + spin_lock_bh(&sip->epub->tx_ampdu_lock);
> +
> + if (tid->state == ESP_TID_STATE_INIT &&
> + TID_TO_AC(tidno) != WME_AC_VO && tid->cnt >= 10) {
> + tid->state = ESP_TID_STATE_TRIGGER;
> + dev_dbg(sip->epub->dev,
> + "start tx ba session,addr:%pM,tid:%u\n",
> + wh->addr1, tidno);
> + spin_unlock_bh(&sip->epub->tx_ampdu_lock);
> + ieee80211_start_tx_ba_session(sta, tidno, 0);
> + } else {
> + if (tid->state == ESP_TID_STATE_INIT)
> + tid->cnt++;
> + else
> + tid->cnt = 0;
> +
> + spin_unlock_bh(&sip->epub->tx_ampdu_lock);
> + }
> + }
> + }
> +
> +_exit:
> + ieee80211_tx_status(sip->epub->hw, skb);
> +}
> +
> +/* NB: this routine should be locked when calling
> + */
> +void sip_txq_process(struct esp_pub *epub)
> +{
> + struct sk_buff *skb;
> + struct sip_hdr *hdr;
> + struct esp_sip *sip = epub->sip;
> + struct ieee80211_tx_info *itx_info;
> + u32 pkt_len, tx_len = 0;
> + int blknum = 0, pm_state = 0;
> + bool queued_back = false, out_of_credits = false;
> +
> + while ((skb = skb_dequeue(&epub->txq))) {
> + /* cmd skb->len does not include sip_hdr too */
> + pkt_len = skb->len;
> + itx_info = IEEE80211_SKB_CB(skb);
> + if (itx_info->flags != 0xffffffff) {
> + pkt_len += roundup(sizeof(struct sip_hdr), 4);
> + if (itx_info->flags & IEEE80211_TX_CTL_AMPDU)
> + pkt_len += roundup(sizeof(struct esp_tx_ampdu_entry),
> + 4);
> + }
> +
> + /* current design simply requires every sip_hdr must be at the begin of mblk, that definitely
> + * need to be optimized, e.g. calculate remain length in the previous mblk, if it larger than
> + * certain threshold (e.g, whole pkt or > 50% of pkt or 2 x sizeof(struct sip_hdr), append pkt
> + * to the previous mblk. This might be done in sip_pack_pkt()
> + */
> + pkt_len = roundup(pkt_len, sip->tx_blksz);
> + blknum = pkt_len / sip->tx_blksz;
> +
> + /*
> + * FIXME: Empirical delay. Without this delay, the connection to
> + * a WiFi network crashes the kernel (sometimes at the second
> + * connection).
> + */
> + udelay(2000);
> +
> + if (unlikely(atomic_read(&sip->credit_status) == RECALC_CREDIT_ENABLE)) { /* need recalc credit */
> + hdr = (struct sip_hdr *)skb->data;
> + itx_info = IEEE80211_SKB_CB(skb);
> +
> + if (!(itx_info->flags == 0xffffffff &&
> + SIP_HDR_GET_TYPE(hdr->fc[0]) == SIP_CTRL &&
> + hdr->c_cmdid == SIP_CMD_RECALC_CREDIT &&
> + blknum <= atomic_read(&sip->tx_credits) - sip->credit_to_reserve)) { /* except cmd recalc credit */
> + dev_dbg(epub->dev, "recalc credits!\n");
> + STRACE_TX_OUT_OF_CREDIT_INC();
> + queued_back = true;
> + out_of_credits = true;
> + break;
> + }
> + } else {
> + if (unlikely(blknum > (atomic_read(&sip->tx_credits) - sip->credit_to_reserve - SIP_CTRL_CREDIT_RESERVE))) {
> + itx_info = IEEE80211_SKB_CB(skb);
> + if (itx_info->flags == 0xffffffff) { /* priv ctrl pkt */
> + if (blknum > atomic_read(&sip->tx_credits) - sip->credit_to_reserve) {
> + dev_dbg(epub->dev,
> + "%s cmd pkt out of credits!\n",
> + __func__);
> + STRACE_TX_OUT_OF_CREDIT_INC();
> + queued_back = true;
> + out_of_credits = true;
> + break;
> + }
> + } else {
> + dev_dbg(epub->dev,
> + "%s out of credits!\n",
> + __func__);
> + STRACE_TX_OUT_OF_CREDIT_INC();
> + queued_back = true;
> + out_of_credits = true;
> + break;
> + }
> + }
> + }
> +
> + tx_len += pkt_len;
> + if (tx_len >= SIP_TX_AGGR_BUF_SIZE) {
> + /* do we need to have limitation likemax 8 pkts in a row? */
> + dev_dbg(epub->dev, "%s too much pkts in one shot!\n",
> + __func__);
> + STRACE_TX_ONE_SHOT_INC();
> + tx_len -= pkt_len;
> + queued_back = true;
> + break;
> + }
> +
> + if (sip_pack_pkt(sip, skb, &pm_state) != 0) {
> + /* wrong pkt, won't send to target */
> + tx_len -= pkt_len;
> + continue;
> + }
> +
> + atomic_sub(blknum, &sip->tx_credits);
> + /*
> + * FIXME: Empirical delay. Without this delay, the connection to
> + * a WiFi network crashes the kernel (sometimes at the second
> + * connection).
> + */
> + udelay(2000);
> +
> + }
> +
> + if (queued_back)
> + skb_queue_head(&epub->txq, skb);
> +
> + if (atomic_read(&sip->state) == SIP_STOP
> +#ifdef HOST_RESET_BUG
> + || atomic_read(&epub->wl.off) == 1
> +#endif
> + ) {
> + queued_back = 1;
> + tx_len = 0;
> + }
> +
> + if (tx_len)
> + sip_write_pkts(sip, pm_state);
> +
> + if (queued_back && !out_of_credits)
> + /* skb pending, do async process again */
> + sip_trigger_txq_process(sip);
> +}
> +
> +#ifndef NO_WMM_DUMMY
> +static struct esp_80211_wmm_param_element esp_wmm_param = {
> + .oui = {0x00, 0x50, 0xf2},
> + .oui_type = 0x02,
> + .oui_subtype = 0x01,
> + .version = 0x01,
> + .qos_info = 0x00,
> + .reserved = 0x00,
> + .ac = {
> + {
> + .aci_aifsn = 0x03,
> + .cw = 0xa4,
> + .txop_limit = 0x0000,
> + },
> + {
> + .aci_aifsn = 0x27,
> + .cw = 0xa4,
> + .txop_limit = 0x0000,
> + },
> + {
> + .aci_aifsn = 0x42,
> + .cw = 0x43,
> + .txop_limit = 0x005e,
> + },
> + {
> + .aci_aifsn = 0x62,
> + .cw = 0x32,
> + .txop_limit = 0x002f,
> + },
> + },
> +};
> +
> +static int esp_add_wmm(struct sk_buff *skb)
> +{
> + u8 *p;
> + int flag = 0;
> + int remain_len;
> + int base_len;
> + int len;
> + struct ieee80211_mgmt *mgmt;
> + struct ieee80211_hdr *wh;
> +
> + if (!skb)
> + return -1;
> +
> + wh = (struct ieee80211_hdr *)skb->data;
> + mgmt = (struct ieee80211_mgmt *)((u8 *)skb->data);
> +
> + if (ieee80211_is_assoc_resp(wh->frame_control)) {
> + p = mgmt->u.assoc_resp.variable;
> + base_len = (u8 *)mgmt->u.assoc_resp.variable - (u8 *)mgmt;
> + } else if (ieee80211_is_reassoc_resp(wh->frame_control)) {
> + p = mgmt->u.reassoc_resp.variable;
> + base_len = (u8 *)mgmt->u.reassoc_resp.variable - (u8 *)mgmt;
> + } else if (ieee80211_is_probe_resp(wh->frame_control)) {
> + p = mgmt->u.probe_resp.variable;
> + base_len = (u8 *)mgmt->u.probe_resp.variable - (u8 *)mgmt;
> + } else if (ieee80211_is_beacon(wh->frame_control)) {
> + p = mgmt->u.beacon.variable;
> + base_len = (u8 *)mgmt->u.beacon.variable - (u8 *)mgmt;
> + } else {
> + return 1;
> + }
> +
> + remain_len = skb->len - base_len;
> +
> + while (remain_len > 0) {
> + if (*p == 0xdd && *(p + 5) == 0x02) //wmm type
> + return 0;
> + else if (*p == 0x2d) //has ht cap
> + flag = 1;
> +
> + len = *(++p);
> + p += (len + 1);
> + remain_len -= (len + 2);
> + }
> +
> + if (remain_len < 0)
> + return -2;
-2 is not a valid error code.
> +
> + if (flag == 1) {
> + skb_put(skb, 2 + sizeof(esp_wmm_param));
> +
> + memset(p, 0xdd, sizeof(u8));
> + memset(p + 1, sizeof(esp_wmm_param), sizeof(u8));
> + memcpy(p + 2, &esp_wmm_param, sizeof(esp_wmm_param));
> + }
> +
> + return 0;
> +}
> +#endif /* NO_WMM_DUMMY */
> +
> +/* parse mac_rx_ctrl and return length */
> +static int sip_parse_mac_rx_info(struct esp_sip *sip,
> + struct esp_mac_rx_ctrl *mac_ctrl,
> + struct sk_buff *skb)
> +{
> + struct ieee80211_rx_status *rx_status;
> + struct ieee80211_hdr *hdr;
> + struct ieee80211_hdr *wh;
> +
> + rx_status = IEEE80211_SKB_RXCB(skb);
> + rx_status->freq = esp_ieee2mhz(mac_ctrl->channel);
> + rx_status->signal = mac_ctrl->rssi + mac_ctrl->noise_floor; /* snr actually, need to offset noise floor e.g. -85 */
> +
> + hdr = (struct ieee80211_hdr *)skb->data;
> + if (mac_ctrl->damatch0 == 1 && mac_ctrl->bssidmatch0 == 1 && /*match bssid and da, but beacon package contain other bssid */
> + !strncmp(hdr->addr2, sip->epub->wl.bssid, ETH_ALEN)) { /* force match addr2 */
> + if (++signal_loop >= SIGNAL_COUNT) {
> + avg_signal += rx_status->signal;
> + avg_signal /= SIGNAL_COUNT;
> + rx_status->signal = avg_signal + 5;
> + old_signal = rx_status->signal;
> + signal_loop = 0;
> + avg_signal = 0;
> + } else {
> + avg_signal += rx_status->signal;
> + rx_status->signal = old_signal;
> + }
> + }
> +
> + rx_status->antenna = 0; /* one antenna for now */
> + rx_status->band = NL80211_BAND_2GHZ;
> + rx_status->flag = RX_FLAG_DECRYPTED | RX_FLAG_MMIC_STRIPPED;
> + if (mac_ctrl->sig_mode) {
> + rx_status->encoding |= RX_ENC_HT;
> + rx_status->rate_idx = mac_ctrl->MCS;
> + if (mac_ctrl->SGI)
> + rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
> + } else {
> + rx_status->rate_idx = esp_wmac_rate2idx(mac_ctrl->rate);
> + }
> +
> + if (mac_ctrl->rxend_state == RX_FCS_ERR)
> + rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
> +
> + /* Mic error frame flag */
> + if (mac_ctrl->rxend_state == RX_TKIPMIC_ERR ||
> + mac_ctrl->rxend_state == RX_CCMPMIC_ERR) {
> + if (atomic_read(&sip->epub->wl.tkip_key_set) == 1) {
> + rx_status->flag |= RX_FLAG_MMIC_ERROR;
> + atomic_set(&sip->epub->wl.tkip_key_set, 0);
> + printk("mic err\n");
> + } else {
> + printk("mic err discard\n");
> + }
> + }
> +
> + wh = (struct ieee80211_hdr *)((u8 *)skb->data);
> +
> +#ifndef NO_WMM_DUMMY
> + if (ieee80211_is_mgmt(wh->frame_control))
> + esp_add_wmm(skb);
> +#endif
> +
> + /* some kernel e.g. 3.0.8 wrongly handles non-encrypted pkt like eapol */
> + if (ieee80211_is_data(wh->frame_control)) {
> + if (!ieee80211_has_protected(wh->frame_control))
> + rx_status->flag |= RX_FLAG_IV_STRIPPED;
> + else if (!atomic_read(&sip->epub->wl.ptk_cnt))
> + rx_status->flag |= RX_FLAG_IV_STRIPPED;
> + }
> +
> + return 0;
> +}
> +
> +static struct esp_mac_rx_ctrl *sip_parse_normal_mac_ctrl(struct sk_buff *skb,
> + int *pkt_len_enc,
> + int *buf_len,
> + int *pulled_len)
> +{
> + struct sip_hdr *hdr = (struct sip_hdr *)skb->data;
> + struct esp_mac_rx_ctrl *mac_ctrl;
> + int len_in_hdr = hdr->len;
> +
> + ESSERT(skb);
> + ESSERT(skb->len > SIP_MIN_DATA_PKT_LEN);
> +
> + skb_pull(skb, sizeof(struct sip_hdr));
> + *pulled_len += sizeof(struct sip_hdr);
> + mac_ctrl = (struct esp_mac_rx_ctrl *)skb->data;
> + if (!mac_ctrl->aggregation) {
> + ESSERT(pkt_len_enc);
> + ESSERT(buf_len);
> + *pkt_len_enc = (mac_ctrl->sig_mode ? mac_ctrl->HT_length : mac_ctrl->legacy_length) - FCS_LEN;
> + *buf_len = len_in_hdr - sizeof(struct sip_hdr) -
> + sizeof(struct esp_mac_rx_ctrl);
> + }
> +
> + skb_pull(skb, sizeof(struct esp_mac_rx_ctrl));
> + *pulled_len += sizeof(struct esp_mac_rx_ctrl);
> +
> + return mac_ctrl;
> +}
> +
> +/* for one MPDU (including subframe in AMPDU) */
> +static struct sk_buff *sip_parse_data_rx_info(struct esp_sip *sip,
> + struct sk_buff *skb,
> + int pkt_len_enc, int buf_len,
> + struct esp_mac_rx_ctrl *mac_ctrl,
> + int *pulled_len)
> +{
> + /* | mac_rx_ctrl | real_data_payload | ampdu_entries | */
> + struct sk_buff *rskb;
> + struct ieee80211_hdr *wh;
> + int pkt_len, ret;
> +
> + if (mac_ctrl->aggregation) {
> + wh = (struct ieee80211_hdr *)skb->data;
> + pkt_len = pkt_len_enc;
> + if (ieee80211_has_protected(wh->frame_control)) //ampdu, it is CCMP enc
> + pkt_len -= 8;
> +
> + buf_len = roundup(pkt_len, 4);
> + } else {
> + pkt_len = buf_len - 3 + ((pkt_len_enc - 1) & 0x3);
> + }
> +
> +#ifndef NO_WMM_DUMMY
> + rskb = __dev_alloc_skb(pkt_len_enc + sizeof(esp_wmm_param) + 2,
> + GFP_ATOMIC);
> +#else
> + rskb = __dev_alloc_skb(pkt_len_enc, GFP_ATOMIC);
> +#endif /* NO_WMM_DUMMY */
> + if (unlikely(!rskb)) {
Remove all likely/unlikely.
> + dev_err(sip->epub->dev, "no mem for rskb\n");
No need for this printk.
> + return NULL;
> + }
> +
> + skb_put(rskb, pkt_len_enc);
> +
> + memcpy(rskb->data, skb->data, pkt_len);
> +
> + if (pkt_len_enc > pkt_len)
> + memset(rskb->data + pkt_len, 0, pkt_len_enc - pkt_len);
> +
> + /* strip out current pkt, move to the next one */
> + skb_pull(skb, buf_len);
> + *pulled_len += buf_len;
> +
> + ret = sip_parse_mac_rx_info(sip, mac_ctrl, rskb);
> + if (ret == -1 && !mac_ctrl->aggregation) {
sip_parse_mac_rx_info() never returns -1. Also -1 is not a the correct
error code.
> + kfree_skb(rskb);
> + return NULL;
> + }
> +
> + return rskb;
> +}
> +
> +struct esp_sip *sip_attach(struct esp_pub *epub)
> +{
> + struct esp_sip *sip;
> + struct sip_pkt *pkt;
> + int i, po;
> +
> + sip = kzalloc(sizeof(*sip), GFP_KERNEL);
> + if (!sip) {
> + dev_dbg(epub->dev, "no mem for sip!\n");
> + goto _err_sip;
No need for the error message or the goto.
> + }
> +
> + /* Finding the smalest available space to allocate this variavle */
> + po = get_order(SIP_TX_AGGR_BUF_SIZE);
> +
> + sip->tx_aggr_buf = (u8 *)__get_free_pages(GFP_ATOMIC, po);
> + if (!sip->tx_aggr_buf) {
> + dev_err(epub->dev, "no mem for tx_aggr_buf!\n");
> + goto _err_aggr;
> + }
> +
> + spin_lock_init(&sip->lock);
> +
> + INIT_LIST_HEAD(&sip->free_ctrl_txbuf);
> + INIT_LIST_HEAD(&sip->free_ctrl_rxbuf);
> +
> + for (i = 0; i < SIP_CTRL_BUF_N; i++) {
> + pkt = kzalloc(sizeof(*pkt), GFP_KERNEL);
> + if (!pkt)
> + goto _err_pkt;
> +
> + pkt->buf_begin = kzalloc(SIP_CTRL_BUF_SZ, GFP_KERNEL);
> + if (!pkt->buf_begin) {
> + kfree(pkt);
> + pkt = NULL;
No need to set "pkt" to NULL
> + goto _err_pkt;
> + }
> +
> + pkt->buf_len = SIP_CTRL_BUF_SZ;
> + pkt->buf = pkt->buf_begin;
> +
> + if (i < SIP_CTRL_TXBUF_N)
> + list_add_tail(&pkt->list, &sip->free_ctrl_txbuf);
> + else
> + list_add_tail(&pkt->list, &sip->free_ctrl_rxbuf);
> + }
> +
> + mutex_init(&sip->rx_mtx);
> + skb_queue_head_init(&sip->rxq);
> + INIT_WORK(&sip->rx_process_work, sip_rxq_process);
> +
> + sip->epub = epub;
> + atomic_set(&sip->noise_floor, -96);
> +
> + atomic_set(&sip->state, SIP_INIT);
> + atomic_set(&sip->tx_credits, 0);
> +
> + if (!sip->rawbuf) {
> + sip->rawbuf = kzalloc(SIP_BOOT_BUF_SIZE, GFP_KERNEL);
> + if (!sip->rawbuf) {
> + dev_err(epub->dev, "no mem for rawbuf!\n");
> + goto _err_pkt;
> + }
> + }
> +
> + atomic_set(&sip->state, SIP_PREPARE_BOOT);
> +
> + return sip;
> +
> +_err_pkt:
> + sip_free_init_ctrl_buf(sip);
> +
> + if (sip->tx_aggr_buf) {
No need for this test. We know it's non-NULL.
> + po = get_order(SIP_TX_AGGR_BUF_SIZE);
po is already set. No need to calculate it again.
> + free_pages((unsigned long)sip->tx_aggr_buf, po);
> + sip->tx_aggr_buf = NULL;
No need to set this to NULL. We're just going to free "sip" on the
next line.
> + }
> +
> +_err_aggr:
> + kfree(sip);
> + sip = NULL;
No need to set this to NULL
> +
> +_err_sip:
> + return NULL;
> +}
> +
> +static void sip_free_init_ctrl_buf(struct esp_sip *sip)
> +{
> + struct sip_pkt *pkt, *tpkt;
> +
> + list_for_each_entry_safe(pkt, tpkt, &sip->free_ctrl_txbuf, list) {
> + list_del(&pkt->list);
> + kfree(pkt->buf_begin);
> + kfree(pkt);
> + }
> +
> + list_for_each_entry_safe(pkt, tpkt, &sip->free_ctrl_rxbuf, list) {
> + list_del(&pkt->list);
> + kfree(pkt->buf_begin);
> + kfree(pkt);
> + }
> +}
> +
> +void sip_detach(struct esp_sip *sip)
> +{
> + if (!sip)
> + return;
> +
> + sip_free_init_ctrl_buf(sip);
> +
> + if (atomic_read(&sip->state) == SIP_RUN) {
> + sif_disable_target_interrupt(sip->epub);
> +
> + atomic_set(&sip->state, SIP_STOP);
> +
> + /* disable irq here */
> + sif_disable_irq(sip->epub);
> + cancel_work_sync(&sip->rx_process_work);
> +
> + skb_queue_purge(&sip->rxq);
> + mutex_destroy(&sip->rx_mtx);
> + cancel_work_sync(&sip->epub->sendup_work);
> + skb_queue_purge(&sip->epub->rxq);
> +
> + if (test_and_clear_bit(ESP_WL_FLAG_HW_REGISTERED,
> + &sip->epub->wl.flags))
> + ieee80211_unregister_hw(sip->epub->hw);
> +
> + /* cancel all worker/timer */
> + cancel_work_sync(&sip->epub->tx_work);
> + skb_queue_purge(&sip->epub->txq);
> + skb_queue_purge(&sip->epub->txdoneq);
> +
> + free_pages((unsigned long)sip->tx_aggr_buf,
> + get_order(SIP_TX_AGGR_BUF_SIZE));
> + sip->tx_aggr_buf = NULL;
> +
> + atomic_set(&sip->state, SIP_INIT);
> + } else if (atomic_read(&sip->state) >= SIP_BOOT &&
> + atomic_read(&sip->state) <= SIP_WAIT_BOOTUP) {
> + sif_disable_target_interrupt(sip->epub);
> + atomic_set(&sip->state, SIP_STOP);
> + sif_disable_irq(sip->epub);
> + kfree(sip->rawbuf);
> +
> + if (atomic_read(&sip->state) == SIP_SEND_INIT) {
> + cancel_work_sync(&sip->rx_process_work);
> + skb_queue_purge(&sip->rxq);
> + mutex_destroy(&sip->rx_mtx);
> + cancel_work_sync(&sip->epub->sendup_work);
> + skb_queue_purge(&sip->epub->rxq);
> + }
> +
> + if (test_and_clear_bit(ESP_WL_FLAG_HW_REGISTERED,
> + &sip->epub->wl.flags))
> + ieee80211_unregister_hw(sip->epub->hw);
> + atomic_set(&sip->state, SIP_INIT);
> + } else {
> + dev_err(sip->epub->dev, "wrong state (%d) for detaching sip\n",
> + atomic_read(&sip->state));
> + }
> +
> + kfree(sip);
> +}
> +
> +int sip_write_memory(struct esp_sip *sip, u32 addr, u8 *buf, u16 len)
> +{
> + struct sip_cmd_write_memory *cmd;
> + struct sip_hdr *chdr;
> + u16 remains, hdrs, bufsize;
> + u32 loadaddr;
> + u8 *src;
> + int err = 0;
No need to initialize "err".
> + u32 *t;
> +
> + if (!sip || !sip->rawbuf) {
> + ESSERT(sip);
> + ESSERT(sip->rawbuf);
> + return -EINVAL;
> + }
> +
> + memset(sip->rawbuf, 0, SIP_BOOT_BUF_SIZE);
> +
> + chdr = (struct sip_hdr *)sip->rawbuf;
> + SIP_HDR_SET_TYPE(chdr->fc[0], SIP_CTRL);
> + chdr->c_cmdid = SIP_CMD_WRITE_MEMORY;
> +
> + remains = len;
> + hdrs = sizeof(struct sip_hdr) + sizeof(struct sip_cmd_write_memory);
> +
> + while (remains) {
> + src = &buf[len - remains];
> + loadaddr = addr + (len - remains);
> +
> + if (remains < (SIP_BOOT_BUF_SIZE - hdrs)) {
> + /* aligned with 4 bytes */
> + bufsize = roundup(remains, 4);
> + memset(sip->rawbuf + hdrs, 0, bufsize);
> + remains = 0;
> + } else {
> + bufsize = SIP_BOOT_BUF_SIZE - hdrs;
> + remains -= bufsize;
> + }
> +
> + chdr->len = bufsize + hdrs;
> + chdr->seq = sip->txseq++;
> + cmd = (struct sip_cmd_write_memory *)(sip->rawbuf + SIP_CTRL_HDR_LEN);
> + cmd->len = bufsize;
> + cmd->addr = loadaddr;
> + memcpy(sip->rawbuf + hdrs, src, bufsize);
> +
> + t = (u32 *)sip->rawbuf;
> + err = esp_common_write(sip->epub, sip->rawbuf, chdr->len,
> + ESP_SIF_SYNC);
> + if (err) {
> + dev_err(sip->epub->dev, "send buffer failed\n");
> + return err;
> + }
> + // 1ms is enough, in fact on dell-d430, need not delay at all.
> + mdelay(1);
> + }
> +
> + return err;
return 0;
> +}
> +
> +int sip_send_cmd(struct esp_sip *sip, int cid, u32 cmdlen, void *cmd)
> +{
> + struct sip_hdr *chdr;
> + struct sip_pkt *pkt;
> + int ret;
> +
> + pkt = sip_get_ctrl_buf(sip, SIP_TX_CTRL_BUF);
> + if (!pkt)
> + return -ENOMEM;
> +
> + chdr = (struct sip_hdr *)pkt->buf_begin;
> + chdr->len = SIP_CTRL_HDR_LEN + cmdlen;
> + chdr->seq = sip->txseq++;
> + chdr->c_cmdid = cid;
> +
> + if (cmd) {
> + memset(pkt->buf, 0, cmdlen);
No need for this memset() because we memcpy() over it on the next line.
> + memcpy(pkt->buf, (u8 *)cmd, cmdlen);
> + }
> +
> + ret = esp_common_write(sip->epub, pkt->buf_begin, chdr->len,
> + ESP_SIF_SYNC);
> + if (ret)
> + dev_err(sip->epub->dev, "send cmd %d failed\n", cid);
> +
> + sip_reclaim_ctrl_buf(sip, pkt, SIP_TX_CTRL_BUF);
> +
> + /* Hack here: reset tx/rx seq before target ram code is up... */
> + if (cid == SIP_CMD_BOOTUP) {
> + sip->rxseq = 0;
> + sip->txseq = 0;
> + sip->txdataseq = 0;
> + }
> +
> + return ret;
> +}
> +
> +struct sk_buff *sip_alloc_ctrl_skbuf(struct esp_sip *sip, u16 len, u32 cid)
> +{
> + struct sip_hdr *si;
> + struct ieee80211_tx_info *ti;
> + struct sk_buff *skb;
> +
> + ESSERT(len <= sip->tx_blksz);
> +
> + /* no need to reserve space for net stack */
> + skb = __dev_alloc_skb(len, GFP_KERNEL);
> + if (!skb) {
> + dev_err(sip->epub->dev, "no skb for ctrl!\n");
> + return NULL;
> + }
> +
> + skb->len = len;
> +
> + ti = IEEE80211_SKB_CB(skb);
> + /* set tx_info flags to 0xffffffff to indicate sip_ctrl pkt */
> + ti->flags = 0xffffffff;
> +
> + si = (struct sip_hdr *)skb->data;
> + memset(si, 0, sizeof(struct sip_hdr));
> + SIP_HDR_SET_TYPE(si->fc[0], SIP_CTRL);
> + si->len = len;
> + si->c_cmdid = cid;
> +
> + return skb;
> +}
> +
> +void sip_free_ctrl_skbuff(struct esp_sip *sip, struct sk_buff *skb)
> +{
> + memset(IEEE80211_SKB_CB(skb), 0, sizeof(struct ieee80211_tx_info));
> + kfree_skb(skb);
> +}
> +
> +static struct sip_pkt *sip_get_ctrl_buf(struct esp_sip *sip,
> + enum sip_buf_type bftype)
> +{
> + struct sip_pkt *pkt;
> + struct list_head *bflist;
> + struct sip_hdr *chdr;
> +
> + /* FIXME: Why taking spinlock to check list_empty? */
> + bflist = (bftype == SIP_TX_CTRL_BUF) ? &sip->free_ctrl_txbuf : &sip->free_ctrl_rxbuf;
> +
> + spin_lock_bh(&sip->lock);
> +
> + if (list_empty(bflist)) {
> + spin_unlock_bh(&sip->lock);
> + return NULL;
> + }
> +
> + pkt = list_first_entry(bflist, struct sip_pkt, list);
> + list_del(&pkt->list);
> + spin_unlock_bh(&sip->lock);
> +
> + if (bftype == SIP_TX_CTRL_BUF) {
> + chdr = (struct sip_hdr *)pkt->buf_begin;
> + SIP_HDR_SET_TYPE(chdr->fc[0], SIP_CTRL);
> + pkt->buf = pkt->buf_begin + SIP_CTRL_HDR_LEN;
> + } else {
> + pkt->buf = pkt->buf_begin;
> + }
> +
> + return pkt;
> +}
> +
> +static void sip_reclaim_ctrl_buf(struct esp_sip *sip, struct sip_pkt *pkt,
> + enum sip_buf_type bftype)
> +{
> + struct list_head *bflist;
> +
> + if (bftype == SIP_TX_CTRL_BUF)
> + bflist = &sip->free_ctrl_txbuf;
> + else if (bftype == SIP_RX_CTRL_BUF)
> + bflist = &sip->free_ctrl_rxbuf;
> + else
> + return;
> +
> + pkt->buf = pkt->buf_begin;
> +
> + spin_lock_bh(&sip->lock);
> + list_add_tail(&pkt->list, bflist);
> + spin_unlock_bh(&sip->lock);
> +}
> +
> +int sip_poll_bootup_event(struct esp_sip *sip)
> +{
> + int ret = 0;
> +
> + if (gl_bootup_cplx)
> + ret = wait_for_completion_timeout(gl_bootup_cplx, 2 * HZ);
> +
> + if (!ret) {
> + dev_err(sip->epub->dev, "bootup event timeout\n");
> + return -ETIMEDOUT;
> + }
> +
> + if (!sif_get_ate_config())
> + ret = esp_register_mac80211(sip->epub);
> +
> + atomic_set(&sip->state, SIP_RUN);
> +
> + return ret;
> +}
> +
> +/* FIXME: always returning 0 ? */
> +int sip_poll_resetting_event(struct esp_sip *sip)
> +{
> + unsigned int ret;
> +
> + if (gl_bootup_cplx)
> + ret = wait_for_completion_timeout(gl_bootup_cplx, 10 * HZ);
> +
> + if (!ret) {
> + dev_err(sip->epub->dev, "resetting event timeout\n");
> + return -ETIMEDOUT;
> + }
> +
> + return 0;
> +}
> +
> +bool sip_queue_need_stop(struct esp_sip *sip)
> +{
> + return atomic_read(&sip->tx_data_pkt_queued) >= SIP_STOP_QUEUE_THRESHOLD ||
> + (atomic_read(&sip->tx_credits) < 8 &&
> + atomic_read(&sip->tx_data_pkt_queued) >= SIP_STOP_QUEUE_THRESHOLD / 4 * 3);
> +}
> +
> +bool sip_queue_may_resume(struct esp_sip *sip)
> +{
> + return atomic_read(&sip->epub->txq_stopped) &&
> + !test_bit(ESP_WL_FLAG_STOP_TXQ, &sip->epub->wl.flags) &&
> + ((atomic_read(&sip->tx_credits) >= 16 &&
> + atomic_read(&sip->tx_data_pkt_queued) < SIP_RESUME_QUEUE_THRESHOLD * 2) ||
> + atomic_read(&sip->tx_data_pkt_queued) < SIP_RESUME_QUEUE_THRESHOLD);
> +}
> +
> +int sip_cmd_enqueue(struct esp_sip *sip, struct sk_buff *skb, int prior)
> +{
> + if (!sip || !sip->epub || !skb) {
> + return -EINVAL;
> + }
> +
> + if (prior == ENQUEUE_PRIOR_HEAD)
> + skb_queue_head(&sip->epub->txq, skb);
> + else
> + skb_queue_tail(&sip->epub->txq, skb);
> +
> + if (!sif_get_ate_config())
> + ieee80211_queue_work(sip->epub->hw, &sip->epub->tx_work);
> + else
> + queue_work(sip->epub->esp_wkq, &sip->epub->tx_work);
> +
> + return 0;
> +}
> +
> +void sip_tx_data_pkt_enqueue(struct esp_pub *epub, struct sk_buff *skb)
> +{
> + if (!epub || !epub->sip || !skb)
> + return;
> +
> + skb_queue_tail(&epub->txq, skb);
> + atomic_inc(&epub->sip->tx_data_pkt_queued);
> +
> + if (sip_queue_need_stop(epub->sip))
> + if (epub->hw) {
> + ieee80211_stop_queues(epub->hw);
> + atomic_set(&epub->txq_stopped, true);
> + }
Multi-line indents get curly braces for readability.
> +}
> diff --git a/drivers/staging/esp8089/esp_sip.h b/drivers/staging/esp8089/esp_sip.h
> new file mode 100644
> index 000000000000..fe58aae4ba54
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_sip.h
> @@ -0,0 +1,150 @@
> +/*
> + * Copyright (c) 2009 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + * Quentin Schulz <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + */
> +
> +#ifndef _ESP_SIP_H
> +#define _ESP_SIP_H
> +
> +#include "sip2_common.h"
> +
> +#define SIP_CTRL_CREDIT_RESERVE 2
> +
> +#define SIP_PKT_MAX_LEN (1024 * 16)
> +
> +/* 16KB on normal X86 system, should check before porting to orhters */
> +
> +#define SIP_TX_AGGR_BUF_SIZE (4 * PAGE_SIZE)
> +#define SIP_RX_AGGR_BUF_SIZE (4 * PAGE_SIZE)
> +
> +struct sk_buff;
> +
> +struct sip_pkt {
> + struct list_head list;
> + u8 *buf_begin;
> + u32 buf_len;
> + u8 *buf;
> +};
> +
> +enum RECALC_CREDIT_STATE {
> + RECALC_CREDIT_DISABLE = 0,
> + RECALC_CREDIT_ENABLE = 1,
> +};
> +
> +enum ENQUEUE_PRIOR {
> + ENQUEUE_PRIOR_TAIL = 0,
> + ENQUEUE_PRIOR_HEAD,
> +};
> +
> +enum SIP_STATE {
> + SIP_INIT = 0,
> + SIP_PREPARE_BOOT,
> + SIP_BOOT,
> + SIP_SEND_INIT,
> + SIP_WAIT_BOOTUP,
> + SIP_RUN,
> + SIP_SUSPEND,
> + SIP_STOP
> +};
> +
> +enum sip_notifier {
> + SIP_TX_DONE = 1,
> + SIP_RX_DONE = 2,
> +};
> +
> +#define SIP_CREDITS_LOW_THRESHOLD 64 //i.e. 4k
> +
> +struct esp_sip {
> + struct list_head free_ctrl_txbuf;
> + struct list_head free_ctrl_rxbuf;
> +
> + u32 rxseq; /* sip pkt seq, should match target side */
> + u32 txseq;
> + u32 txdataseq;
> +
> + u8 to_host_seq;
> +
> + atomic_t state;
> + spinlock_t lock;
> + atomic_t tx_credits;
> +
> + atomic_t tx_ask_credit_update;
> +
> + u8 *rawbuf; /* used in boot stage, free once chip is fully up */
> + u8 *tx_aggr_buf;
> + u8 *tx_aggr_write_ptr; /* update after insertion of each pkt */
> + u8 *tx_aggr_lastpkt_ptr;
> +
> + struct mutex rx_mtx;
> + struct sk_buff_head rxq;
> + struct work_struct rx_process_work;
> +
> + u16 tx_blksz;
> + u16 rx_blksz;
> +
> + bool dump_rpbm_err;
> + bool sendup_rpbm_pkt;
> + bool rxabort_fixed;
> + bool support_bgscan;
> + u8 credit_to_reserve;
> +
> + atomic_t credit_status;
> + struct timer_list credit_timer;
> +
> + atomic_t noise_floor;
> +
> + u32 tx_tot_len; /* total len for one transaction */
> + u32 rx_tot_len;
> +
> + atomic_t rx_handling;
> + atomic_t tx_data_pkt_queued;
> +
> + atomic_t data_tx_stopped;
> + atomic_t tx_stopped;
> +
> + struct esp_pub *epub;
> +};
> +
> +int sip_rx(struct esp_pub *epub);
> +
> +int sip_write_memory(struct esp_sip *sip, u32 addr, u8 *buf, u16 len);
> +
> +int sip_send_cmd(struct esp_sip *sip, int cid, u32 cmdlen, void *cmd);
> +
> +struct esp_sip *sip_attach(struct esp_pub *epub);
> +
> +int sip_post_init(struct esp_sip *sip, struct sip_evt_bootup2 *bevt);
> +
> +void sip_detach(struct esp_sip *sip);
> +
> +void sip_txq_process(struct esp_pub *epub);
> +
> +struct sk_buff *sip_alloc_ctrl_skbuf(struct esp_sip *sip, u16 len, u32 cid);
> +
> +void sip_free_ctrl_skbuff(struct esp_sip *sip, struct sk_buff *skb);
> +
> +bool sip_queue_need_stop(struct esp_sip *sip);
> +bool sip_queue_may_resume(struct esp_sip *sip);
> +
> +void sip_tx_data_pkt_enqueue(struct esp_pub *epub, struct sk_buff *skb);
> +
> +int sip_cmd_enqueue(struct esp_sip *sip, struct sk_buff *skb, int prior);
> +
> +int sip_poll_bootup_event(struct esp_sip *sip);
> +
> +int sip_poll_resetting_event(struct esp_sip *sip);
> +
> +void sip_trigger_txq_process(struct esp_sip *sip);
> +
> +void sip_send_chip_init(struct esp_sip *sip);
> +
> +bool mod_support_no_txampdu(void);
> +
> +bool mod_support_no_rxampdu(void);
> +#endif
> diff --git a/drivers/staging/esp8089/esp_utils.c b/drivers/staging/esp8089/esp_utils.c
> new file mode 100644
> index 000000000000..b35428d70c91
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_utils.c
> @@ -0,0 +1,133 @@
> +/*
> + * Copyright (c) 2009 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + * Quentin Schulz <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + */
> +
> +#include "linux/types.h"
> +#include "linux/kernel.h"
> +#include <linux/ieee80211.h>
> +#include <net/mac80211.h>
> +#include <linux/skbuff.h>
> +
> +#include <net/tcp.h>
> +#include <linux/ip.h>
> +#include <asm/checksum.h>
> +
> +#include "esp_pub.h"
> +#include "esp_utils.h"
> +#include "esp_wmac.h"
> +#include "esp_debug.h"
> +
> +/* Convert IEEE channel number to MHz frequency. */
> +u32 esp_ieee2mhz(u8 chan)
> +{
> + if (chan == 14)
> + return 2484;
> +
> + if (chan < 14)
> + return 2407 + chan * 5;
> +
> + return 2512 + ((chan - 15) * 20);
> + //TODO, add 5GHz
> +}
> +
> +enum {
> + ESP_RATE_1_LONG = 0x0,
> + ESP_RATE_2_LONG = 0x1,
> + ESP_RATE_2_SHORT = 0x5,
> + ESP_RATE_5_SHORT = 0x6,
> + ESP_RATE_5_LONG = 0x2,
> + ESP_RATE_11_SHORT = 0x7,
> + ESP_RATE_11_LONG = 0x3,
> + ESP_RATE_6 = 0xb,
> + ESP_RATE_9 = 0xf,
> + ESP_RATE_12 = 0xa,
> + ESP_RATE_18 = 0xe,
> + ESP_RATE_24 = 0x9,
> + ESP_RATE_36 = 0xd,
> + ESP_RATE_48 = 0x8,
> + ESP_RATE_54 = 0xc,
> + /* ESP_RATE_MCS0 =0x10,
> + * ESP_RATE_MCS1 =0x11,
> + * ESP_RATE_MCS2 =0x12,
> + * ESP_RATE_MCS3 =0x13,
> + * ESP_RATE_MCS4 =0x14,
> + * ESP_RATE_MCS5 =0x15,
> + * ESP_RATE_MCS6 =0x16,
> + * ESP_RATE_MCS7 =0x17,
> + */
> +};
> +
> +static u8 esp_rate_table[20] = {
> + ESP_RATE_1_LONG,
> + ESP_RATE_2_SHORT,
> + ESP_RATE_5_SHORT,
> + ESP_RATE_11_SHORT,
> + ESP_RATE_6,
> + ESP_RATE_9,
> + ESP_RATE_12,
> + ESP_RATE_18,
> + ESP_RATE_24,
> + ESP_RATE_36,
> + ESP_RATE_48,
> + ESP_RATE_54,
> + /* ESP_RATE_MCS0,
> + * ESP_RATE_MCS1,
> + * ESP_RATE_MCS2,
> + * ESP_RATE_MCS3,
> + * ESP_RATE_MCS4,
> + * ESP_RATE_MCS5,
> + * ESP_RATE_MCS6,
> + * ESP_RATE_MCS7,
> + */
> +};
> +
> +s8 esp_wmac_rate2idx(u8 rate)
Just make it return int or maybe u8.
> +{
> + int i;
> +
> + if (rate == ESP_RATE_2_LONG)
> + return 1;
> +
> + if (rate == ESP_RATE_5_LONG)
> + return 2;
> +
> + if (rate == ESP_RATE_11_LONG)
> + return 3;
> +
> + for (i = 0; i < 20; i++)
> + if (rate == esp_rate_table[i])
> + return i;
> +
> + return 0;
> +}
> +
> +bool esp_wmac_rxsec_error(u8 error)
> +{
> + return (error >= RX_SECOV_ERR && error <= RX_SECFIFO_TIMEOUT) ||
> + (error >= RX_WEPICV_ERR && error <= RX_WAPIMIC_ERR);
> +}
> +
> +int esp_cipher2alg(int cipher)
> +{
> + if (cipher == WLAN_CIPHER_SUITE_TKIP)
> + return ALG_TKIP;
> +
> + if (cipher == WLAN_CIPHER_SUITE_CCMP)
> + return ALG_CCMP;
> +
> + if (cipher == WLAN_CIPHER_SUITE_WEP40 ||
> + cipher == WLAN_CIPHER_SUITE_WEP104)
> + return ALG_WEP;
> +
> + if (cipher == WLAN_CIPHER_SUITE_AES_CMAC)
> + return ALG_AES_CMAC;
> +
> + return -1;
> +}
> diff --git a/drivers/staging/esp8089/esp_utils.h b/drivers/staging/esp8089/esp_utils.h
> new file mode 100644
> index 000000000000..3a4d461a4e70
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_utils.h
> @@ -0,0 +1,27 @@
> +/*
> + * Copyright (c) 2011 - 2012 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + * Quentin Schulz <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + */
> +
> +#ifndef _ESP_UTILS_H_
> +#define _ESP_UTILS_H_
> +
> +#include "linux/types.h"
> +
> +u32 esp_ieee2mhz(u8 chan);
> +
> +enum ieee80211_key_alg {
> + ALG_WEP,
> + ALG_TKIP,
> + ALG_CCMP,
> + ALG_AES_CMAC
> +};
> +
> +int esp_cipher2alg(int cipher);
> +#endif
> diff --git a/drivers/staging/esp8089/esp_wl.h b/drivers/staging/esp8089/esp_wl.h
> new file mode 100644
> index 000000000000..9eb1f6421947
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_wl.h
> @@ -0,0 +1,35 @@
> +/*
> + * Copyright (c) 2009 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + * Quentin Schulz <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + */
> +
> +#ifndef _ESP_WL_H_
> +#define _ESP_WL_H_
> +
> +//#define MAX_PROBED_SSID_INDEX 9
> +
> +enum {
> + CONF_HW_BIT_RATE_1MBPS = BIT(0),
> + CONF_HW_BIT_RATE_2MBPS = BIT(1),
> + CONF_HW_BIT_RATE_5_5MBPS = BIT(2),
> + CONF_HW_BIT_RATE_11MBPS = BIT(3),
> + CONF_HW_BIT_RATE_6MBPS = BIT(4),
> + CONF_HW_BIT_RATE_9MBPS = BIT(5),
> + CONF_HW_BIT_RATE_12MBPS = BIT(6),
> + CONF_HW_BIT_RATE_18MBPS = BIT(7),
> + CONF_HW_BIT_RATE_22MBPS = BIT(8),
> + CONF_HW_BIT_RATE_24MBPS = BIT(9),
> + CONF_HW_BIT_RATE_36MBPS = BIT(10),
> + CONF_HW_BIT_RATE_48MBPS = BIT(11),
> + CONF_HW_BIT_RATE_54MBPS = BIT(12),
> +};
> +
> +#define CONF_HW_BIT_RATE_11B_MASK (CONF_HW_BIT_RATE_1MBPS | CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | CONF_HW_BIT_RATE_11MBPS)
> +
> +#endif /* _ESP_WL_H_ */
> diff --git a/drivers/staging/esp8089/esp_wmac.h b/drivers/staging/esp8089/esp_wmac.h
> new file mode 100644
> index 000000000000..67ea8e147c43
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_wmac.h
> @@ -0,0 +1,87 @@
> +/*
> + * Copyright (c) 2011 - 2012 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + * Quentin Schulz <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + */
> +
> +#ifndef _ESP_WMAC_H_
> +#define _ESP_WMAC_H_
> +
> +struct esp_mac_rx_ctrl {
> + signed rssi:8;
> + unsigned rate:4;
> + unsigned is_group:1;
> + unsigned:1;
> + unsigned sig_mode:2;
> + unsigned legacy_length:12;
> + unsigned damatch0:1;
> + unsigned damatch1:1;
> + unsigned bssidmatch0:1;
> + unsigned bssidmatch1:1;
> + unsigned MCS:7;
> + unsigned CWB:1;
> + unsigned HT_length:16;
> + unsigned smoothing:1;
> + unsigned not_sounding:1;
> + unsigned:1;
> + unsigned aggregation:1;
> + unsigned STBC:2;
> + unsigned FEC_CODING:1;
> + unsigned SGI:1;
> + unsigned rxend_state:8;
> + unsigned ampdu_cnt:8;
> + unsigned channel:4;
> + unsigned:4;
> + signed noise_floor:8;
> +};
> +
> +struct esp_rx_ampdu_len {
> + unsigned substate:8;
> + unsigned sublen:12;
> + unsigned:12;
> +};
> +
> +struct esp_tx_ampdu_entry {
> + u32 sub_len:12, dili_num:7, :1, null_byte:2, data:1, enc:1, seq:8;
> +};
> +
> +//rxend_state flags
> +#define RX_PYH_ERR_MIN 0x42
> +#define RX_AGC_ERR_MIN 0x42
> +#define RX_AGC_ERR_MAX 0x47
> +#define RX_OFDM_ERR_MIN 0x50
> +#define RX_OFDM_ERR_MAX 0x58
> +#define RX_CCK_ERR_MIN 0x59
> +#define RX_CCK_ERR_MAX 0x5F
> +#define RX_ABORT 0x80
> +#define RX_SF_ERR 0x40
> +#define RX_FCS_ERR 0x41
> +#define RX_AHBOV_ERR 0xC0
> +#define RX_BUFOV_ERR 0xC1
> +#define RX_BUFINV_ERR 0xC2
> +#define RX_AMPDUSF_ERR 0xC3
> +#define RX_AMPDUBUFOV_ERR 0xC4
> +#define RX_MACBBFIFOOV_ERR 0xC5
> +#define RX_RPBM_ERR 0xC6
> +#define RX_BTFORCE_ERR 0xC7
> +#define RX_SECOV_ERR 0xE1
> +#define RX_SECPROT_ERR0 0xE2
> +#define RX_SECPROT_ERR1 0xE3
> +#define RX_SECKEY_ERR 0xE4
> +#define RX_SECCRLEN_ERR 0xE5
> +#define RX_SECFIFO_TIMEOUT 0xE6
> +#define RX_WEPICV_ERR 0xF0
> +#define RX_TKIPICV_ERR 0xF4
> +#define RX_TKIPMIC_ERR 0xF5
> +#define RX_CCMPMIC_ERR 0xF8
> +#define RX_WAPIMIC_ERR 0xFC
> +
> +s8 esp_wmac_rate2idx(u8 rate);
> +bool esp_wmac_rxsec_error(u8 error);
> +
> +#endif /* _ESP_WMAC_H_ */
> diff --git a/drivers/staging/esp8089/sdio_sif_esp.c b/drivers/staging/esp8089/sdio_sif_esp.c
> new file mode 100644
> index 000000000000..d8842f5085ce
> --- /dev/null
> +++ b/drivers/staging/esp8089/sdio_sif_esp.c
> @@ -0,0 +1,552 @@
> +/*
> + * Copyright (c) 2010 - 2013 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + * Quentin Schulz <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + */
> +
> +#include <linux/mmc/card.h>
> +#include <linux/mmc/mmc.h>
> +#include <linux/mmc/core.h>
> +#include <linux/mmc/host.h>
> +#include <linux/mmc/sdio_func.h>
> +#include <linux/mmc/sdio_ids.h>
> +#include <linux/mmc/sdio.h>
> +#include <linux/mmc/sd.h>
> +#include <linux/module.h>
> +#include <net/mac80211.h>
> +#include <linux/time.h>
> +#include <linux/pm.h>
> +
> +#include "esp_pub.h"
> +#include "esp_sif.h"
> +#include "esp_sip.h"
> +#include "esp_debug.h"
> +#include "slc_host_register.h"
> +#include "esp_version.h"
> +#include "esp_ctrl.h"
> +#include "esp_file.h"
> +
> +unsigned int esp_msg_level = ESP_DBG_ERROR | ESP_SHOW;
> +
> +/* HdG: Note:
> + * 1) MMC_HAS_FORCE_DETECT_CHANGE is a hack which is set by my sunxi-wip
> + * tree. FIXME replace with a version check once mmc_force_detect_change()
> + * is added to the mainline kernel.
> + * 2) This version does NOT implement keep_power, the dts must mark the
> + * regulators as regulator-always-on and not use mmc-pwrseq for this stub
> + * to work.
> + */
> +#ifndef MMC_HAS_FORCE_DETECT_CHANGE
> +void mmc_force_detect_change(struct mmc_host *host, unsigned long delay,
> + bool keep_power)
> +{
> + host->caps &= ~MMC_CAP_NONREMOVABLE;
> + host->caps |= MMC_CAP_NEEDS_POLL;
> + mmc_detect_change(host, delay);
> +}
> +#endif
> +
> +#define ESP_DMA_IBUFSZ 2048
> +
> +struct esp_sdio_ctrl *sif_sctrl;
> +
> +static int esdio_power_off(struct esp_sdio_ctrl *sctrl);
> +static int esdio_power_on(struct esp_sdio_ctrl *sctrl);
> +
> +void sif_set_clock(struct sdio_func *func, int clk);
> +
> +void sif_lock_bus(struct esp_pub *epub)
> +{
> + if (!epub || !epub->sif || !epub->sif->func)
> + return;
> + sdio_claim_host(epub->sif->func);
> +}
> +
> +void sif_unlock_bus(struct esp_pub *epub)
> +{
> + if (!epub || !epub->sif || !epub->sif->func)
> + return;
> + sdio_release_host(epub->sif->func);
> +}
> +
> +static inline bool bad_buf(u8 *buf)
> +{
> + return (unsigned long)buf & 0x3 || !virt_addr_valid(buf);
> +}
> +
> +u8 sdio_io_readb(struct esp_pub *epub, int addr, int *res)
> +{
> + struct sdio_func *func = epub->sif->func;
> +
> + if (!func->num)
> + return sdio_f0_readb(func, addr, res);
> +
> + return sdio_readb(func, addr, res);
> +}
> +
> +void sdio_io_writeb(struct esp_pub *epub, u8 value, int addr, int *res)
> +{
> + struct sdio_func *func = epub->sif->func;
> +
> + if (!func->num)
> + sdio_f0_writeb(func, value, addr, res);
> + else
> + sdio_writeb(func, value, addr, res);
> +}
> +
> +int sif_io(struct esp_pub *epub, u32 addr, u8 *buf, u32 len, u32 flag,
> + bool sync)
> +{
> + struct sdio_func *func;
> + int err = 0;
> + u8 *ibuf;
> + bool need_ibuf = false;
> +
> + if (!epub || !buf || !epub->sif || !epub->sif->func) {
> + ESSERT(0);
> + return -EINVAL;
> + }
> +
> + func = epub->sif->func;
> +
> + if (bad_buf(buf)) {
> + need_ibuf = true;
> + ibuf = epub->sif->dma_buffer;
> + } else {
> + ibuf = buf;
> + }
> +
> + if (flag & SIF_TO_DEVICE) {
> + if (need_ibuf)
> + memcpy(ibuf, buf, len);
> +
> + if (sync)
> + sdio_claim_host(func);
> +
> + if (flag & SIF_FIXED_ADDR)
> + err = sdio_writesb(func, addr, ibuf, len);
> + else if (flag & SIF_INC_ADDR)
> + err = sdio_memcpy_toio(func, addr, ibuf, len);
> +
> + if (sync)
> + sdio_release_host(func);
> +
> + } else if (flag & SIF_FROM_DEVICE) {
> + if (sync)
> + sdio_claim_host(func);
> +
> + if (flag & SIF_FIXED_ADDR)
> + err = sdio_readsb(func, ibuf, addr, len);
> + else if (flag & SIF_INC_ADDR)
> + err = sdio_memcpy_fromio(func, ibuf, addr, len);
> +
> + if (sync)
> + sdio_release_host(func);
> +
> + if (!err && need_ibuf)
> + memcpy(buf, ibuf, len);
> + }
> +
> + return err;
> +}
> +
> +int sif_io_raw(struct esp_pub *epub, u32 addr, u8 *buf, u32 len, u32 flag)
> +{
> + return sif_io(epub, addr, buf, len, flag, false);
> +}
> +
> +int sif_io_sync(struct esp_pub *epub, u32 addr, u8 *buf, u32 len, u32 flag)
> +{
> + return sif_io(epub, addr, buf, len, flag, true);
> +}
> +
> +int sif_lldesc(struct esp_pub *epub, u8 *buf, u32 len, bool sync, bool read,
> + bool noround)
> +{
> + u32 read_len, addr, flags;
> +
> + if (!epub || !buf || !epub->sif || !epub->sif->target_id) {
> + ESSERT(0);
> + return -EINVAL;
> + }
> +
> + if (epub->sif->target_id == 0x600 && !noround)
> + read_len = roundup(len, epub->sif->slc_blk_sz);
> + else
> + read_len = len;
> +
> + if (read) {
> + addr = epub->sif->slc_window_end_addr - 2 - len;
> + flags = SIF_FROM_DEVICE | SIF_BYTE_BASIS | SIF_INC_ADDR;
> + } else {
> + addr = epub->sif->slc_window_end_addr - len;
> + flags = SIF_TO_DEVICE | SIF_BYTE_BASIS | SIF_INC_ADDR;
> + }
> +
> + if (sync)
> + return sif_io_sync(epub, addr, buf, read_len, flags);
> + return sif_io_raw(epub, addr, buf, read_len, flags);
> +}
> +
> +int sif_lldesc_read_sync(struct esp_pub *epub, u8 *buf, u32 len)
> +{
> + return sif_lldesc(epub, buf, len, true, true, false);
> +}
> +
> +int sif_lldesc_write_sync(struct esp_pub *epub, u8 *buf, u32 len)
> +{
> + return sif_lldesc(epub, buf, len, true, false, false);
> +}
> +
> +int sif_lldesc_read_raw(struct esp_pub *epub, u8 *buf, u32 len, bool noround)
> +{
> + return sif_lldesc(epub, buf, len, false, true, noround);
> +}
> +
> +int sif_lldesc_write_raw(struct esp_pub *epub, u8 *buf, u32 len)
> +{
> + return sif_lldesc(epub, buf, len, false, false, false);
> +}
> +
> +#define MANUFACTURER_ID_EAGLE_BASE 0x1110
> +#define MANUFACTURER_CODE 0x6666
> +
> +static const struct sdio_device_id esp_sdio_devices[] = {
> + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_EAGLE_BASE | 0x1))},
> + {},
> +};
> +
> +static int esdio_power(struct esp_sdio_ctrl *sctrl, bool power_on)
> +{
> + int err;
> +
> + if (sctrl->off != power_on)
> + return 0;
> +
> + sdio_claim_host(sctrl->func);
> +
> + if (power_on)
> + err = sdio_enable_func(sctrl->func);
> + else
> + err = sdio_disable_func(sctrl->func);
> +
> + sdio_release_host(sctrl->func);
> +
> + if (err) {
> + dev_err(sctrl->epub->dev, "unable to enable sdio func: %d\n",
> + err);
> + return err;
> + }
> +
> + /* ensure device is up */
> + if (power_on)
> + msleep(5);
> +
> + sctrl->off = !sctrl->off;
> +
> + return err;
return 0;
> +}
> +
> +static int esdio_power_on(struct esp_sdio_ctrl *sctrl)
> +{
> + return esdio_power(sctrl, true);
> +}
> +
> +static int esdio_power_off(struct esp_sdio_ctrl *sctrl)
> +{
> + return esdio_power(sctrl, false);
> +}
> +
> +void sif_enable_irq(struct esp_pub *epub)
> +{
> + sdio_claim_host(epub->sif->func);
> +
> + if (sdio_claim_irq(epub->sif->func, sif_dsr))
> + dev_err(epub->dev, "sif %s failed\n", __func__);
> +
> + atomic_set(&epub->sip->state, SIP_BOOT);
> + atomic_set(&epub->sif->irq_installed, 1);
> +
> + sdio_release_host(epub->sif->func);
> +}
> +
> +void sif_disable_irq(struct esp_pub *epub)
> +{
> + int i = 0;
> +
> + if (!atomic_read(&epub->sif->irq_installed))
> + return;
> +
> + sdio_claim_host(epub->sif->func);
> +
> + while (atomic_read(&epub->sif->irq_handling)) {
> + sdio_release_host(epub->sif->func);
> + schedule_timeout(HZ / 100);
> + sdio_claim_host(epub->sif->func);
> + if (i++ >= 400)
> + break;
> + }
> +
> + /* Ignore errors, we don't always use an irq. */
> + sdio_release_irq(epub->sif->func);
> +
> + atomic_set(&epub->sif->irq_installed, 0);
> + sdio_release_host(epub->sif->func);
> +}
> +
> +void sif_set_clock(struct sdio_func *func, int clk)
> +{
> + struct mmc_host *host = func->card->host;
> +
> + sdio_claim_host(func);
> +
> + //currently only set clock
> + host->ios.clock = clk * 1000000;
> +
> + if (host->ios.clock > host->f_max)
> + host->ios.clock = host->f_max;
> +
> + host->ops->set_ios(host, &host->ios);
> +
> + mdelay(2);
> +
> + sdio_release_host(func);
> +}
> +
> +static void esp_sdio_remove(struct sdio_func *func);
> +
> +static int esp_sdio_probe(struct sdio_func *func,
> + const struct sdio_device_id *id)
> +{
> + struct esp_pub *epub;
> + struct esp_sdio_ctrl *sctrl;
> + struct mmc_host *host = func->card->host;
> + int err;
> +
> + if (!sif_sctrl) {
> + request_init_conf(&func->dev);
> +
> + sctrl = kzalloc(sizeof(*sctrl), GFP_KERNEL);
> + if (!sctrl)
> + return -ENOMEM;
> +
> + /* temp buffer reserved for un-dma-able request */
> + sctrl->dma_buffer = kzalloc(ESP_DMA_IBUFSZ, GFP_KERNEL);
> + if (!sctrl->dma_buffer) {
> + err = -ENOMEM;
> + goto _err_last;
> + }
> +
> + sif_sctrl = sctrl;
> + sctrl->slc_blk_sz = SIF_SLC_BLOCK_SIZE;
> +
> + epub = esp_pub_alloc_mac80211(&func->dev);
> + if (!epub) {
> + err = -ENOMEM;
> + goto _err_dma;
> + }
> +
> + epub->sif = sctrl;
> + epub->sdio_state = ESP_SDIO_STATE_FIRST_INIT;
> + sctrl->epub = epub;
> + } else {
> + /* FIXME: why duplicating sctrl and sif_sctrl? */
> + sctrl = sif_sctrl;
> + sif_sctrl = NULL;
> + epub = sctrl->epub;
> + epub->sdio_state = ESP_SDIO_STATE_SECOND_INIT;
> + /* FIXME: already done in esp_pub_alloc_mac80211 which is called on first probe */
> + SET_IEEE80211_DEV(epub->hw, &func->dev);
> + epub->dev = &func->dev;
> + }
> +
> + sctrl->func = func;
> + sdio_set_drvdata(func, sctrl);
> + sctrl->id = id;
> + sctrl->off = true;
> +
> + /* give us some time to enable, in ms */
> + func->enable_timeout = 100;
> +
> + err = esdio_power_on(sctrl);
> + if (err) {
> + if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT)
> + goto _err_ext_gpio;
> +
> + goto _err_second_init;
> + }
> +
> + check_target_id(epub);
> +
> + sdio_claim_host(func);
> +
> + err = sdio_set_block_size(func, sctrl->slc_blk_sz);
> + if (err) {
> + dev_err(epub->dev, "Set sdio block size %d failed: %d)\n",
> + sctrl->slc_blk_sz, err);
> +
> + sdio_release_host(func);
> + if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT)
> + goto _err_off;
> +
> + goto _err_second_init;
> + }
> +
> + sdio_release_host(func);
> +
> +#ifdef LOWER_CLK
> + /* fix clock for dongle */
> + sif_set_clock(func, 23);
> +#endif //LOWER_CLK
> +
> + err = esp_pub_init_all(epub);
> + if (err) {
> + dev_err(epub->dev, "esp_init_all failed: %d\n", err);
> + if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) {
> + err = 0;
Why is this success?
> + goto _err_first_init;
> + }
> +
> + if (epub->sdio_state == ESP_SDIO_STATE_SECOND_INIT)
> + goto _err_second_init;
> + }
> +
> + if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) {
> + epub->sdio_state = ESP_SDIO_STATE_FIRST_NORMAL_EXIT;
> + /* Rescan the esp8089 after loading the initial firmware */
> + mmc_force_detect_change(host, msecs_to_jiffies(100), true);
> + }
> +
> + return err;
return 0;
> +
> +_err_off:
> + esdio_power_off(sctrl);
> +
> +_err_ext_gpio:
> + esp_pub_dealloc_mac80211(epub);
> +
> +_err_dma:
> + kfree(sctrl->dma_buffer);
> +
> +_err_last:
> + kfree(sctrl);
> +
> +_err_first_init:
> + if (epub && epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT)
> + epub->sdio_state = ESP_SDIO_STATE_FIRST_ERROR_EXIT;
> +
> + return err;
> +
> +_err_second_init:
> + epub->sdio_state = ESP_SDIO_STATE_SECOND_ERROR_EXIT;
> + esp_sdio_remove(func);
> +
> + return err;
> +}
> +
> +static void esp_sdio_remove(struct sdio_func *func)
> +{
> + struct esp_sdio_ctrl *sctrl = sdio_get_drvdata(func);
> + struct esp_pub *epub;
> +
> + if (!sctrl) {
> + return;
> + }
> +
> + epub = sctrl->epub;
> + if (!epub) {
> + goto _out;
> + }
> +
> + if (epub->sdio_state != ESP_SDIO_STATE_FIRST_NORMAL_EXIT) {
> + if (epub->sip) {
> + sip_detach(epub->sip);
> + epub->sip = NULL;
> + }
> + } else {
> + atomic_set(&epub->sip->state, SIP_STOP);
> + sif_disable_irq(epub);
> + }
> +
> + if (epub->sdio_state != ESP_SDIO_STATE_FIRST_NORMAL_EXIT) {
> + esp_pub_dealloc_mac80211(epub);
> +
> + kfree(sctrl->dma_buffer);
> + sctrl->dma_buffer = NULL;
> +
> + kfree(sctrl);
> + }
> +
> +_out:
> + sdio_set_drvdata(func, NULL);
> +}
> +
> +MODULE_DEVICE_TABLE(sdio, esp_sdio_devices);
> +
> +static int esp_sdio_suspend(struct device *dev)
> +{
> + struct sdio_func *func = dev_to_sdio_func(dev);
> + struct esp_sdio_ctrl *sctrl = sdio_get_drvdata(func);
> + struct esp_pub *epub = sctrl->epub;
> + u32 sdio_flags;
> +
> + printk("%s", __func__);
> + atomic_set(&epub->ps.state, ESP_PM_ON);
> +
> + sdio_flags = sdio_get_host_pm_caps(func);
> +
> + if (!(sdio_flags & MMC_PM_KEEP_POWER))
> + printk("%s can't keep power while host is suspended\n",
> + __func__);
> +
> + /* keep power while host suspended */
> + if (sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER))
> + printk("%s error while trying to keep power\n", __func__);
> +
> + return 0;
> +}
> +
> +static int esp_sdio_resume(struct device *dev)
> +{
> + return 0;
> +}
> +
> +static const struct dev_pm_ops esp_sdio_pm_ops = {
> + .suspend = esp_sdio_suspend,
> + .resume = esp_sdio_resume,
> +};
> +
> +static struct sdio_driver esp_sdio_driver = {
> + .name = "eagle_sdio",
> + .id_table = esp_sdio_devices,
> + .probe = esp_sdio_probe,
> + .remove = esp_sdio_remove,
> + .drv = {.pm = &esp_sdio_pm_ops,},
> +};
> +
> +static int /*__init*/ esp_sdio_init(void)
> +{
> + esp_debugfs_init();
> + sdio_register_driver(&esp_sdio_driver);
> +
> + return 0;
> +}
> +
> +static void /*__exit*/ esp_sdio_exit(void)
> +{
> + sdio_unregister_driver(&esp_sdio_driver);
> + esp_debugfs_exit();
> +}
> +
> +module_init(esp_sdio_init);
> +module_exit(esp_sdio_exit);
> +
> +MODULE_AUTHOR("Espressif System");
> +MODULE_DESCRIPTION
> +("Driver for SDIO interconnected eagle low-power WLAN devices");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/staging/esp8089/sip2_common.h b/drivers/staging/esp8089/sip2_common.h
> new file mode 100644
> index 000000000000..5dfb8c492223
> --- /dev/null
> +++ b/drivers/staging/esp8089/sip2_common.h
> @@ -0,0 +1,388 @@
> +/*
> + * Copyright (c) 2010 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + * Quentin Schulz <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + */
> +
> +#ifndef _SIP2_COMMON_H
> +#define _SIP2_COMMON_H
> +
> +/* max 16 types */
> +enum sip_type {
> + SIP_CTRL = 0,
> + SIP_DATA,
> + SIP_DATA_AMPDU
> +};
> +
> +enum sip_buf_type {
> + SIP_TX_CTRL_BUF = 0, /* from host */
> + SIP_RX_CTRL_BUF, /* to host */
> + SIP_TX_DATA_BUF, /* from host */
> + SIP_RX_DATA_BUF /* to host */
> +};
> +
> +enum sip_cmd_id {
> + SIP_CMD_GET_VER = 0,
> + SIP_CMD_WRITE_MEMORY, //1 ROM code
> + SIP_CMD_READ_MEMORY, //2
> + SIP_CMD_WRITE_REG, //3 ROM code
> + SIP_CMD_READ_REG, //4
> + SIP_CMD_BOOTUP, //5 ROM code
> + SIP_CMD_COPYBACK, //6
> + SIP_CMD_INIT, //7
> + SIP_CMD_SCAN, //8
> + SIP_CMD_SETKEY, //9
> + SIP_CMD_CONFIG, //10
> + SIP_CMD_BSS_INFO_UPDATE, //11
> + SIP_CMD_LOOPBACK, //12 ROM code
> + //do not add cmd before this line
> + SIP_CMD_SET_WMM_PARAM,
> + SIP_CMD_AMPDU_ACTION,
> + SIP_CMD_HB_REQ, //15
> + SIP_CMD_RESET_MAC, //16
> + SIP_CMD_PRE_DOWN, //17
> + SIP_CMD_SLEEP, /* for sleep testing */
> + SIP_CMD_WAKEUP, /* for sleep testing */
> + SIP_CMD_DEBUG, /* for general testing */
> + SIP_CMD_GET_FW_VER, /* get fw rev. */
> + SIP_CMD_SETVIF,
> + SIP_CMD_SETSTA,
> + SIP_CMD_PS,
> + SIP_CMD_ATE,
> + SIP_CMD_SUSPEND,
> + SIP_CMD_RECALC_CREDIT,
> + SIP_CMD_MAX,
> +};
> +
> +enum {
> + SIP_EVT_TARGET_ON = 0, //
> + SIP_EVT_BOOTUP, //1 in ROM code
> + SIP_EVT_COPYBACK, //2
> + SIP_EVT_SCAN_RESULT, //3
> + SIP_EVT_TX_STATUS, //4
> + SIP_EVT_CREDIT_RPT, //5, in ROM code
> + SIP_EVT_ERROR, //6
> + SIP_EVT_LOOPBACK, //7, in ROM code
> + SIP_EVT_SNPRINTF_TO_HOST, //8 in ROM code
> + //do not add evt before this line
> + SIP_EVT_HB_ACK, //9
> + SIP_EVT_RESET_MAC_ACK, //10
> + SIP_EVT_WAKEUP, //11 /* for sleep testing */
> + SIP_EVT_DEBUG, //12 /* for general testing */
> + SIP_EVT_PRINT_TO_HOST, //13
> + SIP_EVT_TRC_AMPDU, //14
> + SIP_EVT_ROC, //15
> + SIP_EVT_RESETTING,
> + SIP_EVT_ATE,
> + SIP_EVT_EP,
> + SIP_EVT_INIT_EP,
> + SIP_EVT_SLEEP,
> + SIP_EVT_TXIDLE,
> + SIP_EVT_NOISEFLOOR,
> + SIP_EVT_MAX
> +};
> +
> +#define SIP_IFIDX_MASK 0xf0
> +#define SIP_IFIDX_S 4
> +#define SIP_TYPE_MASK 0x0f
> +#define SIP_TYPE_S 0
> +
> +#define SIP_HDR_GET_IFIDX(fc0) (((fc0) & SIP_IFIDX_MASK) >> SIP_IFIDX_S)
> +#define SIP_HDR_SET_IFIDX(fc0, ifidx) ((fc0) = ((fc0) & ~SIP_IFIDX_MASK) | ((ifidx) << SIP_IFIDX_S & SIP_IFIDX_MASK))
> +#define SIP_HDR_GET_TYPE(fc0) ((fc0) & SIP_TYPE_MASK)
> +/* assume type field is cleared */
> +#define SIP_HDR_SET_TYPE(fc0, type) ((fc0) = ((fc0) & ~SIP_TYPE_MASK) | ((type) & SIP_TYPE_MASK))
> +
> +/* sip 2.0, not hybrid header so far */
> +#define SIP_HDR_IS_CTRL(hdr) (SIP_HDR_GET_TYPE((hdr)->fc[0]) == SIP_CTRL)
> +#define SIP_HDR_IS_DATA(hdr) (SIP_HDR_GET_TYPE((hdr)->fc[0]) == SIP_DATA)
> +#define SIP_HDR_IS_AMPDU(hdr) (SIP_HDR_GET_TYPE((hdr)->fc[0]) == SIP_DATA_AMPDU)
> +
> +/* fc[1] flags, only for data pkt. Ctrl pkts use fc[1] as eventID */
> +#define SIP_HDR_SET_FLAGS(hdr, flags) ((hdr)->fc[1] |= (flags))
> +#define SIP_HDR_F_MORE_PKT 0x1
> +#define SIP_HDR_F_NEED_CRDT_RPT 0x2
> +#define SIP_HDR_F_SYNC 0x4
> +#define SIP_HDR_F_SYNC_RESET 0x8
> +#define SIP_HDR_F_PM_TURNING_ON 0x10
> +#define SIP_HDR_F_PM_TURNING_OFF 0x20
> +
> +#define SIP_HDR_NEED_CREDIT_UPDATE(hdr) ((hdr)->fc[1] & SIP_HDR_F_NEED_CRDT_RPT)
> +#define SIP_HDR_IS_MORE_PKT(hdr) ((hdr)->fc[1] & SIP_HDR_F_MORE_PKT)
> +#define SIP_HDR_IS_CRDT_RPT(hdr) ((hdr)->fc[1] & SIP_HDR_F_CRDT_RPT)
> +#define SIP_HDR_IS_SYNC(hdr) ((hdr)->fc[1] & SIP_HDR_F_SYNC)
> +#define SIP_HDR_IS_SYNC_RESET(hdr) ((hdr)->fc[1] & SIP_HDR_F_SYNC_RESET)
> +#define SIP_HDR_IS_SYNC_PKT(hdr) (SIP_HDR_IS_SYNC(hdr) | SIP_HDR_IS_SYNC_RESET(hdr))
> +#define SIP_HDR_SET_SYNC(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_SYNC)
> +#define SIP_HDR_SET_SYNC_RESET(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_SYNC_RESET)
> +#define SIP_HDR_SET_MORE_PKT(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_MORE_PKT)
> +#define SIP_HDR_SET_PM_TURNING_ON(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_PM_TURNING_ON)
> +#define SIP_HDR_IS_PM_TURNING_ON(hdr) ((hdr)->fc[1] & SIP_HDR_F_PM_TURNING_ON)
> +#define SIP_HDR_SET_PM_TURNING_OFF(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_PM_TURNING_OFF)
> +#define SIP_HDR_IS_PM_TURNING_OFF(hdr) ((hdr)->fc[1] & SIP_HDR_F_PM_TURNING_OFF)
> +
> +/* fc[0]: first 4bit: ifidx; last 4bit: type
> + * fc[1]: flags
> + *
> + * Don't touch the header definitons
> + */
> +struct sip_hdr_min {
> + u8 fc[2];
> + __le16 len;
> +} __packed;
> +
> +/* not more than 4byte long */
> +struct sip_tx_data_info {
> + u8 tid;
> + u8 ac;
> + u8 p2p:1, enc_flag:7;
> + u8 hw_kid;
> +} __packed;
> +
> +/* NB: this structure should be not more than 4byte !! */
> +struct sip_tx_info {
> + union {
> + u32 cmdid;
> + struct sip_tx_data_info dinfo;
> + } u;
> +} __packed;
> +
> +struct sip_hdr {
> + u8 fc[2]; //fc[0]: type and ifidx ; fc[1] is eventID if the first ctrl pkt in the chain. data pkt still can use fc[1] to set flag
> + __le16 len;
> + union {
> + volatile u32 recycled_credits; /* last 12bits is credits, first 20 bits is actual length of the first pkt in the chain */
Remove the volatile.
> + struct sip_tx_info tx_info;
> + } u;
If you just remove the "u" and make this an anonymous union then
it makes everything a easier.
> + u32 seq;
> +} __packed;
> +
> +#define h_credits u.recycled_credits
It means we can get rid of these ugly defines.
> +#define c_evtid fc[1]
> +#define c_cmdid u.tx_info.u.cmdid
> +#define d_ac u.tx_info.u.dinfo.ac
> +#define d_tid u.tx_info.u.dinfo.tid
> +#define d_p2p u.tx_info.u.dinfo.p2p
> +#define d_enc_flag u.tx_info.u.dinfo.enc_flag
> +#define d_hw_kid u.tx_info.u.dinfo.hw_kid
> +
> +#define SIP_CREDITS_MASK 0xfff /* last 12 bits */
> +
> +#define SIP_HDR_MIN_LEN 4
> +#define SIP_HDR_LEN sizeof(struct sip_hdr)
> +#define SIP_CTRL_HDR_LEN SIP_HDR_LEN /* same as sip_hdr in sip2 design */
> +#define SIP_BOOT_BUF_SIZE 256
> +#define SIP_CTRL_BUF_SZ 256 /* too much?? */
> +#define SIP_CTRL_BUF_N 6
> +#define SIP_CTRL_TXBUF_N 2
> +#define SIP_CTRL_RXBUF_N 4
> +
> +/* WAR for mblk */
> +#define SIP_RX_ADDR_PREFIX_MASK 0xfc000000
> +#define SIP_RX_ADDR_SHIFT 6 /* [31:5], shift 6 bits */
> +
> +struct sip_cmd_write_memory {
> + u32 addr;
> + u32 len;
> +} __packed;
> +
> +struct sip_cmd_read_memory {
> + u32 addr;
> + u32 len;
> +} __packed;
> +
> +struct sip_cmd_write_reg {
> + u32 addr;
> + u32 val;
> +} __packed;
> +
> +struct sip_cmd_bootup {
> + u32 boot_addr;
> +} __packed;
> +
> +struct sip_cmd_loopback {
> + u32 txlen; //host to target packet len, 0 means no txpacket
> + u32 rxlen; //target to host packet len, 0 means no rxpacket
> + u32 pack_id; //sequence of packet
> +} __packed;
> +
> +struct sip_evt_loopback {
> + u32 txlen; //host to target packet len, 0 means no txpacket
> + u32 rxlen; //target to host packet len, 0 means no rxpacket
> + u32 pack_id; //sequence of packet
> +} __packed;
> +
> +struct sip_cmd_copyback {
> + u32 addr;
> + u32 len;
> +} __packed;
> +
> +struct sip_cmd_scan {
> + // u8 ssid[32];
> + u8 ssid_len;
> + // u8 hw_channel[14];
> + u8 n_channels;
> + u8 ie_len;
> + u8 aborted;
> +} __packed; // ie[] append at the end
> +
> +struct sip_cmd_setkey {
> + u8 bssid_no;
> + u8 addr[ETH_ALEN];
> + u8 alg;
> + u8 keyidx;
> + u8 hw_key_idx;
> + u8 flags;
> + u8 keylen;
> + u8 key[32];
> +} __packed;
> +
> +struct sip_cmd_config {
> + u16 center_freq;
> + u16 duration;
> +} __packed;
> +
> +struct sip_cmd_bss_info_update {
> + u8 bssid[ETH_ALEN];
> + u16 isassoc;
> + u32 beacon_int;
> + u8 bssid_no;
> +} __packed;
> +
> +struct sip_evt_bootup {
> + u16 tx_blksz;
> + u8 mac_addr[ETH_ALEN];
> + /* anything else ? */
> +} __packed;
> +
> +struct sip_cmd_setvif {
> + u8 index;
> + u8 mac[ETH_ALEN];
> + u8 set;
> + u8 op_mode;
> + u8 is_p2p;
> +} __packed;
> +
> +enum esp_ieee80211_phytype {
> + ESP_IEEE80211_T_CCK = 0,
> + ESP_IEEE80211_T_OFDM = 1,
> + ESP_IEEE80211_T_HT20_L = 2,
> + ESP_IEEE80211_T_HT20_S = 3,
> +};
> +
> +struct sip_cmd_setsta {
> + u8 ifidx;
> + u8 index;
> + u8 set;
> + u8 phymode;
> + u8 mac[ETH_ALEN];
> + u16 aid;
> + u8 ampdu_factor;
> + u8 ampdu_density;
> + u16 resv;
> +} __packed;
> +
> +struct sip_cmd_ps {
> + u8 dtim_period;
> + u8 max_sleep_period;
> + u8 on;
> + u8 resv;
> +} __packed;
> +
> +struct sip_cmd_suspend {
> + u8 suspend;
> + u8 resv[3];
> +} __packed;
> +
> +#define SIP_DUMP_RPBM_ERR BIT(0)
> +#define SIP_RXABORT_FIXED BIT(1)
> +#define SIP_SUPPORT_BGSCAN BIT(2)
> +struct sip_evt_bootup2 {
> + u16 tx_blksz;
> + u8 mac_addr[ETH_ALEN];
> + u16 rx_blksz;
> + u8 credit_to_reserve;
> + u8 options;
> + s16 noise_floor;
> + u8 resv[2];
> + /* anything else ? */
> +} __packed;
> +
> +enum trc_ampdu_state {
> + TRC_TX_AMPDU_STOPPED = 1,
> + TRC_TX_AMPDU_OPERATIONAL,
> + TRC_TX_AMPDU_WAIT_STOP,
> + TRC_TX_AMPDU_WAIT_OPERATIONAL,
> + TRC_TX_AMPDU_START,
> +};
> +
> +struct sip_evt_trc_ampdu {
> + u8 state;
> + u8 tid;
> + u8 addr[ETH_ALEN];
> +} __packed;
> +
> +struct sip_cmd_set_wmm_params {
> + u8 aci;
> + u8 aifs;
> + u8 ecw_min;
> + u8 ecw_max;
> + u16 txop_us;
> +} __packed;
> +
> +#define SIP_AMPDU_RX_START 0
> +#define SIP_AMPDU_RX_STOP 1
> +#define SIP_AMPDU_TX_OPERATIONAL 2
> +#define SIP_AMPDU_TX_STOP 3
> +
> +struct sip_cmd_ampdu_action {
> + u8 action;
> + u8 index;
> + u8 tid;
> + u8 win_size;
> + u16 ssn;
> + u8 addr[ETH_ALEN];
> +} __packed;
> +
> +#define SIP_TX_ST_OK 0
> +#define SIP_TX_ST_NOEB 1
> +#define SIP_TX_ST_ACKTO 2
> +#define SIP_TX_ST_ENCERR 3
> +
> +//NB: sip_tx_status must be 4 bytes aligned
> +struct sip_tx_status {
> + u32 sip_seq;
> + u8 errno; /* success or failure code */
> + u8 rate_index;
> + char ack_signal;
> + u8 pad;
> +} __packed;
> +
> +struct sip_evt_scan_report {
> + u16 scan_id;
> + u16 aborted;
> +} __packed;
> +
> +struct sip_evt_roc {
> + u16 state; //start:1, end :0
> + u16 is_ok;
> +} __packed;
> +
> +struct sip_evt_txidle {
> + u32 last_seq;
> +} __packed;
> +
> +struct sip_evt_noisefloor {
> + s16 noise_floor;
> + u16 pad;
> +} __packed;
> +/* for mblk direct memory access, no need for sip_hdr. tx: first 2k for contrl msg,
> + * rest of 14k for data. rx, same.
> + */
> +
> +#endif /* _SIP_COMMON_H_ */
> diff --git a/drivers/staging/esp8089/slc_host_register.h b/drivers/staging/esp8089/slc_host_register.h
> new file mode 100644
> index 000000000000..8cf139c0d7bc
> --- /dev/null
> +++ b/drivers/staging/esp8089/slc_host_register.h
> @@ -0,0 +1,263 @@
> +/*
> + * Copyright (c) 2011 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + * Quentin Schulz <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + */
> +
> +#ifndef SLC_HOST_REGISTER_H_INCLUDED
> +#define SLC_HOST_REGISTER_H_INCLUDED
> +
> +/* #define REG_SLC_HOST_BASE 0x00000000 */
> +/* skip the token1, since reading it will clean the credit */
> +#define REG_SLC_HOST_BASE 0x00000000
> +#define REG_SLC_BASE 0x00000000
> +
> +#define SLC_HOST_PF (REG_SLC_HOST_BASE + 0x0)
> +#define SLC_HOST_TOKEN_RDATA (REG_SLC_HOST_BASE + 0x4)
> +#define SLC_HOST_RX_PF_EOF 0x0000000F
> +#define SLC_HOST_RX_PF_EOF_S 28
> +#define SLC_HOST_TOKEN1 0x00000FFF
> +#define SLC_HOST_TOKEN1_S 16
> +#define SLC_HOST_RX_PF_VALID BIT(15)
> +#define SLC_HOST_TOKEN0 0x00000FFF
> +#define SLC_HOST_TOKEN0_S 0
> +
> +#define SLC_HOST_TOKEN0_MASK SLC_HOST_TOKEN0
> +#define SLC_HOST_INT_RAW (REG_SLC_HOST_BASE + 0x8)
> +#define SLC_HOST_EXT_BIT3_INT_RAW BIT(22)
> +#define SLC_HOST_EXT_BIT2_INT_RAW BIT(21)
> +#define SLC_HOST_EXT_BIT1_INT_RAW BIT(20)
> +#define SLC_HOST_RXFIFO_NOT_EMPTY_INT_RAW BIT(19)
> +#define SLC_HOST_RX_PF_VALID_INT_RAW BIT(18)
> +#define SLC_HOST_TX_OVF_INT_RAW BIT(17)
> +#define SLC_HOST_RX_UDF_INT_RAW BIT(16)
> +#define SLC_HOST_TX_START_INT_RAW BIT(15)
> +#define SLC_HOST_RX_START_INT_RAW BIT(14)
> +#define SLC_HOST_RX_EOF_INT_RAW BIT(13)
> +#define SLC_HOST_RX_SOF_INT_RAW BIT(12)
> +#define SLC_HOST_TOKEN1_0TO1_INT_RAW BIT(11)
> +#define SLC_HOST_TOKEN0_0TO1_INT_RAW BIT(10)
> +#define SLC_HOST_TOKEN1_1TO0_INT_RAW BIT(9)
> +#define SLC_HOST_TOKEN0_1TO0_INT_RAW BIT(8)
> +#define SLC_HOST_TOHOST_BIT7_INT_RAW BIT(7)
> +#define SLC_HOST_TOHOST_BIT6_INT_RAW BIT(6)
> +#define SLC_HOST_TOHOST_BIT5_INT_RAW BIT(5)
> +#define SLC_HOST_TOHOST_BIT4_INT_RAW BIT(4)
> +#define SLC_HOST_TOHOST_BIT3_INT_RAW BIT(3)
> +#define SLC_HOST_TOHOST_BIT2_INT_RAW BIT(2)
> +#define SLC_HOST_TOHOST_BIT1_INT_RAW BIT(1)
> +#define SLC_HOST_TOHOST_BIT0_INT_RAW BIT(0)
> +
> +#define SLC_HOST_STATE_W0 (REG_SLC_HOST_BASE + 0xC)
> +#define SLC_HOST_STATE3 0x000000FF
> +#define SLC_HOST_STATE3_S 24
> +#define SLC_HOST_STATE2 0x000000FF
> +#define SLC_HOST_STATE2_S 16
> +#define SLC_HOST_STATE1 0x000000FF
> +#define SLC_HOST_STATE1_S 8
> +#define SLC_HOST_STATE0 0x000000FF
> +#define SLC_HOST_STATE0_S 0
> +
> +#define SLC_HOST_STATE_W1 (REG_SLC_HOST_BASE + 0x10)
> +#define SLC_HOST_STATE7 0x000000FF
> +#define SLC_HOST_STATE7_S 24
> +#define SLC_HOST_STATE6 0x000000FF
> +#define SLC_HOST_STATE6_S 16
> +#define SLC_HOST_STATE5 0x000000FF
> +#define SLC_HOST_STATE5_S 8
> +#define SLC_HOST_STATE4 0x000000FF
> +#define SLC_HOST_STATE4_S 0
> +
> +#define SLC_HOST_CONF_W0 (REG_SLC_HOST_BASE + 0x14)
> +#define SLC_HOST_CONF3 0x000000FF
> +#define SLC_HOST_CONF3_S 24
> +#define SLC_HOST_CONF2 0x000000FF
> +#define SLC_HOST_CONF2_S 16
> +#define SLC_HOST_CONF1 0x000000FF
> +#define SLC_HOST_CONF1_S 8
> +#define SLC_HOST_CONF0 0x000000FF
> +#define SLC_HOST_CONF0_S 0
> +
> +#define SLC_HOST_CONF_W1 (REG_SLC_HOST_BASE + 0x18)
> +#define SLC_HOST_CONF7 0x000000FF
> +#define SLC_HOST_CONF7_S 24
> +#define SLC_HOST_CONF6 0x000000FF
> +#define SLC_HOST_CONF6_S 16
> +#define SLC_HOST_CONF5 0x000000FF
> +#define SLC_HOST_CONF5_S 8
> +#define SLC_HOST_CONF4 0x000000FF
> +#define SLC_HOST_CONF4_S 0
> +
> +#define SLC_HOST_INT_ST (REG_SLC_HOST_BASE + 0x1C)
> +#define SLC_HOST_RX_ST BIT(23)
> +#define SLC_HOST_EXT_BIT3_INT_ST BIT(22)
> +#define SLC_HOST_EXT_BIT2_INT_ST BIT(21)
> +#define SLC_HOST_EXT_BIT1_INT_ST BIT(20)
> +#define SLC_HOST_RXFIFO_NOT_EMPTY_INT_ST BIT(19)
> +#define SLC_HOST_RX_PF_VALID_INT_ST BIT(18)
> +#define SLC_HOST_TX_OVF_INT_ST BIT(17)
> +#define SLC_HOST_RX_UDF_INT_ST BIT(16)
> +#define SLC_HOST_TX_START_INT_ST BIT(15)
> +#define SLC_HOST_RX_START_INT_ST BIT(14)
> +#define SLC_HOST_RX_EOF_INT_ST BIT(13)
> +#define SLC_HOST_RX_SOF_INT_ST BIT(12)
> +#define SLC_HOST_TOKEN1_0TO1_INT_ST BIT(11)
> +#define SLC_HOST_TOKEN0_0TO1_INT_ST BIT(10)
> +#define SLC_HOST_TOKEN1_1TO0_INT_ST BIT(9)
> +#define SLC_HOST_TOKEN0_1TO0_INT_ST BIT(8)
> +#define SLC_HOST_TOHOST_BIT7_INT_ST BIT(7)
> +#define SLC_HOST_TOHOST_BIT6_INT_ST BIT(6)
> +#define SLC_HOST_TOHOST_BIT5_INT_ST BIT(5)
> +#define SLC_HOST_TOHOST_BIT4_INT_ST BIT(4)
> +#define SLC_HOST_TOHOST_BIT3_INT_ST BIT(3)
> +#define SLC_HOST_TOHOST_BIT2_INT_ST BIT(2)
> +#define SLC_HOST_TOHOST_BIT1_INT_ST BIT(1)
> +#define SLC_HOST_TOHOST_BIT0_INT_ST BIT(0)
> +
> +#define SLC_HOST_CONF_W2 (REG_SLC_HOST_BASE + 0x20)
> +#define SLC_HOST_CONF11 0x000000FF
> +#define SLC_HOST_CONF11_S 24
> +#define SLC_HOST_CONF10 0x000000FF
> +#define SLC_HOST_CONF10_S 16
> +#define SLC_HOST_CONF9 0x000000FF
> +#define SLC_HOST_CONF9_S 8
> +#define SLC_HOST_CONF8 0x000000FF
> +#define SLC_HOST_CONF8_S 0
> +
> +#define SLC_HOST_CONF_W3 (REG_SLC_HOST_BASE + 0x24)
> +#define SLC_HOST_CONF15 0x000000FF
> +#define SLC_HOST_CONF15_S 24
> +#define SLC_HOST_CONF14 0x000000FF
> +#define SLC_HOST_CONF14_S 16
> +#define SLC_HOST_CONF13 0x000000FF
> +#define SLC_HOST_CONF13_S 8
> +#define SLC_HOST_CONF12 0x000000FF
> +#define SLC_HOST_CONF12_S 0
> +
> +#define SLC_HOST_GEN_TXDONE_INT BIT(16)
> +#define SLC_HOST_GEN_RXDONE_INT BIT(17)
> +
> +#define SLC_HOST_CONF_W4 (REG_SLC_HOST_BASE + 0x28)
> +#define SLC_HOST_CONF19 0x000000FF
> +#define SLC_HOST_CONF19_S 24
> +#define SLC_HOST_CONF18 0x000000FF
> +#define SLC_HOST_CONF18_S 16
> +#define SLC_HOST_CONF17 0x000000FF
> +#define SLC_HOST_CONF17_S 8
> +#define SLC_HOST_CONF16 0x000000FF
> +#define SLC_HOST_CONF16_S 0
> +
> +#define SLC_HOST_TOKEN_WDATA (REG_SLC_HOST_BASE + 0x2C)
> +#define SLC_HOST_TOKEN1_WD 0x00000FFF
> +#define SLC_HOST_TOKEN1_WD_S 16
> +#define SLC_HOST_TOKEN0_WD 0x00000FFF
> +#define SLC_HOST_TOKEN0_WD_S 0
> +
> +#define SLC_HOST_INT_CLR (REG_SLC_HOST_BASE + 0x30)
> +#define SLC_HOST_TOKEN1_WR BIT(31)
> +#define SLC_HOST_TOKEN0_WR BIT(30)
> +#define SLC_HOST_TOKEN1_DEC BIT(29)
> +#define SLC_HOST_TOKEN0_DEC BIT(28)
> +#define SLC_HOST_EXT_BIT3_INT_CLR BIT(22)
> +#define SLC_HOST_EXT_BIT2_INT_CLR BIT(21)
> +#define SLC_HOST_EXT_BIT1_INT_CLR BIT(20)
> +#define SLC_HOST_EXT_BIT0_INT_CLR BIT(19)
> +#define SLC_HOST_RX_PF_VALID_INT_CLR BIT(18)
> +#define SLC_HOST_TX_OVF_INT_CLR BIT(17)
> +#define SLC_HOST_RX_UDF_INT_CLR BIT(16)
> +#define SLC_HOST_TX_START_INT_CLR BIT(15)
> +#define SLC_HOST_RX_START_INT_CLR BIT(14)
> +#define SLC_HOST_RX_EOF_INT_CLR BIT(13)
> +#define SLC_HOST_RX_SOF_INT_CLR BIT(12)
> +#define SLC_HOST_TOKEN1_0TO1_INT_CLR BIT(11)
> +#define SLC_HOST_TOKEN0_0TO1_INT_CLR BIT(10)
> +#define SLC_HOST_TOKEN1_1TO0_INT_CLR BIT(9)
> +#define SLC_HOST_TOKEN0_1TO0_INT_CLR BIT(8)
> +#define SLC_HOST_TOHOST_BIT7_INT_CLR BIT(7)
> +#define SLC_HOST_TOHOST_BIT6_INT_CLR BIT(6)
> +#define SLC_HOST_TOHOST_BIT5_INT_CLR BIT(5)
> +#define SLC_HOST_TOHOST_BIT4_INT_CLR BIT(4)
> +#define SLC_HOST_TOHOST_BIT3_INT_CLR BIT(3)
> +#define SLC_HOST_TOHOST_BIT2_INT_CLR BIT(2)
> +#define SLC_HOST_TOHOST_BIT1_INT_CLR BIT(1)
> +#define SLC_HOST_TOHOST_BIT0_INT_CLR BIT(0)
> +
> +#define SLC_HOST_INT_ENA (REG_SLC_HOST_BASE + 0x34)
> +#define SLC_HOST_EXT_BIT3_INT_ENA BIT(22)
> +#define SLC_HOST_EXT_BIT2_INT_ENA BIT(21)
> +#define SLC_HOST_EXT_BIT1_INT_ENA BIT(20)
> +#define SLC_HOST_EXT_BIT0_INT_ENA BIT(19)
> +#define SLC_HOST_RX_PF_VALID_INT_ENA BIT(18)
> +#define SLC_HOST_TX_OVF_INT_ENA BIT(17)
> +#define SLC_HOST_RX_UDF_INT_ENA BIT(16)
> +#define SLC_HOST_TX_START_INT_ENA BIT(15)
> +#define SLC_HOST_RX_START_INT_ENA BIT(14)
> +#define SLC_HOST_RX_EOF_INT_ENA BIT(13)
> +#define SLC_HOST_RX_SOF_INT_ENA BIT(12)
> +#define SLC_HOST_TOKEN1_0TO1_INT_ENA BIT(11)
> +#define SLC_HOST_TOKEN0_0TO1_INT_ENA BIT(10)
> +#define SLC_HOST_TOKEN1_1TO0_INT_ENA BIT(9)
> +#define SLC_HOST_TOKEN0_1TO0_INT_ENA BIT(8)
> +#define SLC_HOST_TOHOST_BIT7_INT_ENA BIT(7)
> +#define SLC_HOST_TOHOST_BIT6_INT_ENA BIT(6)
> +#define SLC_HOST_TOHOST_BIT5_INT_ENA BIT(5)
> +#define SLC_HOST_TOHOST_BIT4_INT_ENA BIT(4)
> +#define SLC_HOST_TOHOST_BIT3_INT_ENA BIT(3)
> +#define SLC_HOST_TOHOST_BIT2_INT_ENA BIT(2)
> +#define SLC_HOST_TOHOST_BIT1_INT_ENA BIT(1)
> +#define SLC_HOST_TOHOST_BIT0_INT_ENA BIT(0)
> +
> +#define SLC_HOST_CONF_W5 (REG_SLC_HOST_BASE + 0x3C)
> +#define SLC_HOST_CONF23 0x000000FF
> +#define SLC_HOST_CONF23_S 24
> +#define SLC_HOST_CONF22 0x000000FF
> +#define SLC_HOST_CONF22_S 16
> +#define SLC_HOST_CONF21 0x000000FF
> +#define SLC_HOST_CONF21_S 8
> +#define SLC_HOST_CONF20 0x000000FF
> +#define SLC_HOST_CONF20_S 0
> +
> +#define SLC_HOST_WIN_CMD (REG_SLC_HOST_BASE + 0x40)
> +
> +#define SLC_HOST_DATE (REG_SLC_HOST_BASE + 0x78)
> +#define SLC_HOST_ID (REG_SLC_HOST_BASE + 0x7C)
> +
> +#define SLC_ADDR_WINDOW_CLEAR_MASK (~(0xf << 12))
> +#define SLC_FROM_HOST_ADDR_WINDOW BIT(12)
> +#define SLC_TO_HOST_ADDR_WINDOW (0x3 << 12)
> +
> +#define SLC_SET_FROM_HOST_ADDR_WINDOW(v) do { \
> + (v) &= 0xffff; \
> + (v) &= SLC_ADDR_WINDOW_CLEAR_MASK; \
> + (v) |= SLC_FROM_HOST_ADDR_WINDOW; \
> +} while (0)
> +
> +#define SLC_SET_TO_HOST_ADDR_WINDOW(v) do { \
> + (v) &= 0xffff; \
> + (v) &= SLC_ADDR_WINDOW_CLEAR_MASK; \
> + (v) |= SLC_TO_HOST_ADDR_WINDOW; \
> +} while (0)
> +
> +#define SLC_INT_ENA (REG_SLC_BASE + 0xC)
> +#define SLC_RX_EOF_INT_ENA BIT(17)
> +#define SLC_FRHOST_BIT2_INT_ENA BIT(2)
> +
> +#define SLC_RX_LINK (REG_SLC_BASE + 0x24)
> +#define SLC_RXLINK_START BIT(29)
> +
> +#define SLC_BRIDGE_CONF (REG_SLC_BASE + 0x44)
> +#define SLC_TX_PUSH_IDLE_NUM 0xFFFF
> +#define SLC_TX_PUSH_IDLE_NUM_S 16
> +#define SLC_HDA_MAP_128K BIT(13)
> +#define SLC_TX_DUMMY_MODE BIT(12)
> +#define SLC_FIFO_MAP_ENA 0x0000000F
> +#define SLC_FIFO_MAP_ENA_S 8
> +#define SLC_TXEOF_ENA 0x0000003F
> +#define SLC_TXEOF_ENA_S
> +
> +#endif // SLC_HOST_REGISTER_H_INCLUDED
> --
> 2.11.0
>
>
> _______________________________________________
> devel mailing list
> [email protected]
> http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
Hi,
On 21-07-17 16:35, Quentin Schulz wrote:
> From: Hans de Goede <[email protected]>
>
> Some sdio devices have a multiple stage bring-up process. Specifically
> the esp8089 (for which an out of tree driver is available) loads firmware
> on the first call to its sdio-drivers' probe function and then resets
> the device causing it to reboot from its RAM with the new firmware.
>
> When this sdio device reboots it comes back up in 1 bit 400 KHz mode
> again, and we need to walk through the whole ios negatiation and sdio setup
> again.
>
> There are 2 problems with this:
>
> 1) Typically these devices are soldered onto some (ARM) tablet / SBC
> PCB and as such are described in devicetree as "non-removable", which
> causes the mmc-core to scan them only once and not poll for the device
> dropping of the bus. Normally this is the right thing todo but in the
> eso8089 example we need the mmc-core to notice the module has disconnected
> (since it is now in 1 bit mode again it will not talk to the host in 4 bit
> mode). This can be worked around by using "broken-cd" in devicetree
> instead of "non-removable", but that is not a proper fix since the device
> really is non-removable.
>
> 2) When the mmc-core detects the device has disconnected it will poweroff
> the device, causing the RAM loaded firmware to be lost. This can be worked
> around in devicetree by using regulator-always-on (and avoiding the use of
> mmc-pwrseq), but again that is more of a hack then a proper fix.
>
> This commmit fixes 1) by adding a mmc_force_detect_change function which
> will cause scanning for device removal / insertion until a new device is
> detected. 2) Is fixed by a keep_power flag to the mmc_force_detect_change
> function which when set causes the mmc-core to keep the power to the device
> on during the rescan.
>
> Cc: Icenowy Zheng <[email protected]>
> Cc: Maxime Ripard <[email protected]>
> Cc: Chen-Yu Tsai <[email protected]>
> Signed-off-by: Hans de Goede <[email protected]>
So when I posted this patch quite a while back, there was some discussion
about this and a consensus that this is not the right solution.
So first of all lets describe the problem:
The esp8089 sdio wifi chip is really an ARM core with a wifi phy
connected to it (as many wifi chipsets are).
But this one comes up in some really generic sdio capable boot-loader
mode and we need to feed it firmware and then reboot it into the
new firmware.
The reboot is where the problems happens. It seems to fallback
from the negotiated 4 wire sdio mode to single wire spi mode then.
The out of tree version of the driver deals with this by not setting
the non-removable flag as well as setting the broken_cd flag so that
the mmc core polls the device, after the reboot the poll fails
because the mmc-controller and the esp8089 are using a different
amount of wires so the mmc-cmd the poll uses times out.
After which the esp8089 drivers remove function gets called, and
the mmc stack re-discovers the esp8089 by restarting the whole
number of wires (and speed) used negotiation. After which the
esp8089 driver's probe function gets called (again) and on
firmware loading is has set a global flag, so now it actually
acts as a wifi driver rather then trying to load the firmware
a second time.
Since I did not want to rely on broken_cd polling I came up
with the hack which is this patch.
So when this patch was first discussed we came to the conclusion
that what we really need is some sort of mmc_reprobe_device
function which the driver can call from probe which will
redo the number of wires (and speed) used negotiation,
while keeping the sdio_function device as is so that probe can
simply continue after this and we also don't need the ugly
global flag.
The idea would be for this function to be some wrapper
around mmc_init_card() which resets the ios settings as is
normally done on remove and then call mmc_init_card()
passing in the existing card the same way as is done
one resume, so that the existing card / sdio_function
devices get reused.
IIRC Ulf would look into writing this mmc_reprobe_device
function and then I would test it with the esp8089, but
Ulf never got around to writing the function and I ended
up working on other things too.
I HTH.
Regards,
Hans
> ---
> drivers/mmc/core/core.c | 47 ++++++++++++++++++++++++++++++++++++++++++-----
> include/linux/mmc/host.h | 7 +++++++
> 2 files changed, 49 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 26431267a3e2..103badde910b 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -1620,8 +1620,11 @@ int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr,
> */
> void mmc_power_up(struct mmc_host *host, u32 ocr)
> {
> - if (host->ios.power_mode == MMC_POWER_ON)
> + if (host->ios.power_mode == MMC_POWER_ON) {
> + if (host->ios.clock == 0)
> + goto set_clock;
> return;
> + }
>
> mmc_pwrseq_pre_power_on(host);
>
> @@ -1646,6 +1649,7 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
>
> mmc_pwrseq_post_power_on(host);
>
> +set_clock:
> host->ios.clock = host->f_init;
>
> host->ios.power_mode = MMC_POWER_ON;
> @@ -1663,6 +1667,11 @@ void mmc_power_off(struct mmc_host *host)
> if (host->ios.power_mode == MMC_POWER_OFF)
> return;
>
> + if (host->rescan_keep_power) {
> + mmc_set_clock(host, 0);
> + return;
> + }
> +
> mmc_pwrseq_power_off(host);
>
> host->ios.clock = 0;
> @@ -1804,6 +1813,27 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay)
> }
> EXPORT_SYMBOL(mmc_detect_change);
>
> +/**
> + * mmc_force_detect_change - force rescanning of a MMC socket even if
> + * it is non-removable
> + * @host: host to rescan
> + * @delay: optional delay to wait before detection (jiffies)
> + * @keep_power: if set do not turn of vdd / call pwrseq_off during rescan
> + *
> + * MMC drivers which need non-removable sdio devices to be rescanned
> + * (e.g. because the device reboots its fw after a firmware upload),
> + * can call this to force scanning the MMC socket for changes, even
> + * if it is non-removable.
> + */
> +void mmc_force_detect_change(struct mmc_host *host, unsigned long delay,
> + bool keep_power)
> +{
> + host->rescan_force = 1;
> + host->rescan_keep_power = keep_power;
> + _mmc_detect_change(host, delay, false);
> +}
> +EXPORT_SYMBOL(mmc_force_detect_change);
> +
> void mmc_init_erase(struct mmc_card *card)
> {
> unsigned int sz;
> @@ -2566,7 +2596,8 @@ void mmc_rescan(struct work_struct *work)
> return;
>
> /* If there is a non-removable card registered, only scan once */
> - if (!mmc_card_is_removable(host) && host->rescan_entered)
> + if (!mmc_card_is_removable(host) && host->rescan_entered &&
> + !host->rescan_force)
> return;
> host->rescan_entered = 1;
>
> @@ -2583,7 +2614,8 @@ void mmc_rescan(struct work_struct *work)
> * if there is a _removable_ card registered, check whether it is
> * still present
> */
> - if (host->bus_ops && !host->bus_dead && mmc_card_is_removable(host))
> + if (host->bus_ops && !host->bus_dead &&
> + (mmc_card_is_removable(host) || host->rescan_force))
> host->bus_ops->detect(host);
>
> host->detect_change = 0;
> @@ -2616,15 +2648,20 @@ void mmc_rescan(struct work_struct *work)
> }
>
> for (i = 0; i < ARRAY_SIZE(freqs); i++) {
> - if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
> + if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) {
> + if (host->rescan_force) {
> + host->rescan_force = 0;
> + host->rescan_keep_power = 0;
> + }
> break;
> + }
> if (freqs[i] <= host->f_min)
> break;
> }
> mmc_release_host(host);
>
> out:
> - if (host->caps & MMC_CAP_NEEDS_POLL)
> + if ((host->caps & MMC_CAP_NEEDS_POLL) || host->rescan_force)
> mmc_schedule_delayed_work(&host->detect, HZ);
> }
>
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index ebd1cebbef0c..d56d79867bbd 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -338,6 +338,8 @@ struct mmc_host {
>
> int rescan_disable; /* disable card detection */
> int rescan_entered; /* used with nonremovable devices */
> + int rescan_force; /* force rescan of (nonremovable) devices */
> + int rescan_keep_power; /* Do not power off card */
>
> int need_retune; /* re-tuning is needed */
> int hold_retune; /* hold off re-tuning */
> @@ -420,6 +422,11 @@ int mmc_power_save_host(struct mmc_host *host);
> int mmc_power_restore_host(struct mmc_host *host);
>
> void mmc_detect_change(struct mmc_host *, unsigned long delay);
> +
> +/* HdG: HACK HACK HACK do not upstream */
> +#define MMC_HAS_FORCE_DETECT_CHANGE
> +void mmc_force_detect_change(struct mmc_host *host, unsigned long delay,
> + bool keep_power);
> void mmc_request_done(struct mmc_host *, struct mmc_request *);
> void mmc_command_done(struct mmc_host *host, struct mmc_request *mrq);
>
>
On 30 August 2017 at 14:44, Hans de Goede <[email protected]> wrote:
> Hi,
>
>
> On 21-07-17 16:35, Quentin Schulz wrote:
>>
>> From: Hans de Goede <[email protected]>
>>
>> Some sdio devices have a multiple stage bring-up process. Specifically
>> the esp8089 (for which an out of tree driver is available) loads firmware
>> on the first call to its sdio-drivers' probe function and then resets
>> the device causing it to reboot from its RAM with the new firmware.
>>
>> When this sdio device reboots it comes back up in 1 bit 400 KHz mode
>> again, and we need to walk through the whole ios negatiation and sdio
>> setup
>> again.
>>
>> There are 2 problems with this:
>>
>> 1) Typically these devices are soldered onto some (ARM) tablet / SBC
>> PCB and as such are described in devicetree as "non-removable", which
>> causes the mmc-core to scan them only once and not poll for the device
>> dropping of the bus. Normally this is the right thing todo but in the
>> eso8089 example we need the mmc-core to notice the module has disconnected
>> (since it is now in 1 bit mode again it will not talk to the host in 4 bit
>> mode). This can be worked around by using "broken-cd" in devicetree
>> instead of "non-removable", but that is not a proper fix since the device
>> really is non-removable.
>>
>> 2) When the mmc-core detects the device has disconnected it will poweroff
>> the device, causing the RAM loaded firmware to be lost. This can be worked
>> around in devicetree by using regulator-always-on (and avoiding the use of
>> mmc-pwrseq), but again that is more of a hack then a proper fix.
>>
>> This commmit fixes 1) by adding a mmc_force_detect_change function which
>> will cause scanning for device removal / insertion until a new device is
>> detected. 2) Is fixed by a keep_power flag to the mmc_force_detect_change
>> function which when set causes the mmc-core to keep the power to the
>> device
>> on during the rescan.
>>
>> Cc: Icenowy Zheng <[email protected]>
>> Cc: Maxime Ripard <[email protected]>
>> Cc: Chen-Yu Tsai <[email protected]>
>> Signed-off-by: Hans de Goede <[email protected]>
>
>
> So when I posted this patch quite a while back, there was some discussion
> about this and a consensus that this is not the right solution.
>
> So first of all lets describe the problem:
>
> The esp8089 sdio wifi chip is really an ARM core with a wifi phy
> connected to it (as many wifi chipsets are).
>
> But this one comes up in some really generic sdio capable boot-loader
> mode and we need to feed it firmware and then reboot it into the
> new firmware.
>
> The reboot is where the problems happens. It seems to fallback
> from the negotiated 4 wire sdio mode to single wire spi mode then.
>
> The out of tree version of the driver deals with this by not setting
> the non-removable flag as well as setting the broken_cd flag so that
> the mmc core polls the device, after the reboot the poll fails
> because the mmc-controller and the esp8089 are using a different
> amount of wires so the mmc-cmd the poll uses times out.
>
> After which the esp8089 drivers remove function gets called, and
> the mmc stack re-discovers the esp8089 by restarting the whole
> number of wires (and speed) used negotiation. After which the
> esp8089 driver's probe function gets called (again) and on
> firmware loading is has set a global flag, so now it actually
> acts as a wifi driver rather then trying to load the firmware
> a second time.
>
> Since I did not want to rely on broken_cd polling I came up
> with the hack which is this patch.
>
> So when this patch was first discussed we came to the conclusion
> that what we really need is some sort of mmc_reprobe_device
> function which the driver can call from probe which will
> redo the number of wires (and speed) used negotiation,
> while keeping the sdio_function device as is so that probe can
> simply continue after this and we also don't need the ugly
> global flag.
>
> The idea would be for this function to be some wrapper
> around mmc_init_card() which resets the ios settings as is
> normally done on remove and then call mmc_init_card()
> passing in the existing card the same way as is done
> one resume, so that the existing card / sdio_function
> devices get reused.
>
> IIRC Ulf would look into writing this mmc_reprobe_device
> function and then I would test it with the esp8089, but
> Ulf never got around to writing the function and I ended
> up working on other things too.
Thanks for summary!
Just to let you know, I haven't forgot about this problem. I am
planning for a major update of the SDIO for power management support,
within a not too far future.
The issue described above, is then also one of the things I also plan
to look into.
However, if there anybody that wants to hack on this, feel free to do it!
Kind regards
Uffe
Hi Ulf,
On Wed, Aug 30, 2017 at 03:43:49PM +0200, Ulf Hansson wrote:
> On 30 August 2017 at 14:44, Hans de Goede <[email protected]> wrote:
> > Hi,
> >
> >
> > On 21-07-17 16:35, Quentin Schulz wrote:
> >>
> >> From: Hans de Goede <[email protected]>
> >>
> >> Some sdio devices have a multiple stage bring-up process. Specifically
> >> the esp8089 (for which an out of tree driver is available) loads firmware
> >> on the first call to its sdio-drivers' probe function and then resets
> >> the device causing it to reboot from its RAM with the new firmware.
> >>
> >> When this sdio device reboots it comes back up in 1 bit 400 KHz mode
> >> again, and we need to walk through the whole ios negatiation and sdio
> >> setup
> >> again.
> >>
> >> There are 2 problems with this:
> >>
> >> 1) Typically these devices are soldered onto some (ARM) tablet / SBC
> >> PCB and as such are described in devicetree as "non-removable", which
> >> causes the mmc-core to scan them only once and not poll for the device
> >> dropping of the bus. Normally this is the right thing todo but in the
> >> eso8089 example we need the mmc-core to notice the module has disconnected
> >> (since it is now in 1 bit mode again it will not talk to the host in 4 bit
> >> mode). This can be worked around by using "broken-cd" in devicetree
> >> instead of "non-removable", but that is not a proper fix since the device
> >> really is non-removable.
> >>
> >> 2) When the mmc-core detects the device has disconnected it will poweroff
> >> the device, causing the RAM loaded firmware to be lost. This can be worked
> >> around in devicetree by using regulator-always-on (and avoiding the use of
> >> mmc-pwrseq), but again that is more of a hack then a proper fix.
> >>
> >> This commmit fixes 1) by adding a mmc_force_detect_change function which
> >> will cause scanning for device removal / insertion until a new device is
> >> detected. 2) Is fixed by a keep_power flag to the mmc_force_detect_change
> >> function which when set causes the mmc-core to keep the power to the
> >> device
> >> on during the rescan.
> >>
> >> Cc: Icenowy Zheng <[email protected]>
> >> Cc: Maxime Ripard <[email protected]>
> >> Cc: Chen-Yu Tsai <[email protected]>
> >> Signed-off-by: Hans de Goede <[email protected]>
> >
> >
> > So when I posted this patch quite a while back, there was some discussion
> > about this and a consensus that this is not the right solution.
> >
> > So first of all lets describe the problem:
> >
> > The esp8089 sdio wifi chip is really an ARM core with a wifi phy
> > connected to it (as many wifi chipsets are).
> >
> > But this one comes up in some really generic sdio capable boot-loader
> > mode and we need to feed it firmware and then reboot it into the
> > new firmware.
> >
> > The reboot is where the problems happens. It seems to fallback
> > from the negotiated 4 wire sdio mode to single wire spi mode then.
> >
> > The out of tree version of the driver deals with this by not setting
> > the non-removable flag as well as setting the broken_cd flag so that
> > the mmc core polls the device, after the reboot the poll fails
> > because the mmc-controller and the esp8089 are using a different
> > amount of wires so the mmc-cmd the poll uses times out.
> >
> > After which the esp8089 drivers remove function gets called, and
> > the mmc stack re-discovers the esp8089 by restarting the whole
> > number of wires (and speed) used negotiation. After which the
> > esp8089 driver's probe function gets called (again) and on
> > firmware loading is has set a global flag, so now it actually
> > acts as a wifi driver rather then trying to load the firmware
> > a second time.
> >
> > Since I did not want to rely on broken_cd polling I came up
> > with the hack which is this patch.
> >
> > So when this patch was first discussed we came to the conclusion
> > that what we really need is some sort of mmc_reprobe_device
> > function which the driver can call from probe which will
> > redo the number of wires (and speed) used negotiation,
> > while keeping the sdio_function device as is so that probe can
> > simply continue after this and we also don't need the ugly
> > global flag.
> >
> > The idea would be for this function to be some wrapper
> > around mmc_init_card() which resets the ios settings as is
> > normally done on remove and then call mmc_init_card()
> > passing in the existing card the same way as is done
> > one resume, so that the existing card / sdio_function
> > devices get reused.
> >
> > IIRC Ulf would look into writing this mmc_reprobe_device
> > function and then I would test it with the esp8089, but
> > Ulf never got around to writing the function and I ended
> > up working on other things too.
>
> Thanks for summary!
>
> Just to let you know, I haven't forgot about this problem. I am
> planning for a major update of the SDIO for power management support,
> within a not too far future.
> The issue described above, is then also one of the things I also plan
> to look into.
>
I'd like to know if any progress has been made on that problem (I may
have missed patches).
Had you had the time to look at the issue?
Thanks and kind regards,
Quentin
On 8 February 2018 at 15:59, Quentin Schulz <[email protected]> wrote:
> Hi Ulf,
>
> On Wed, Aug 30, 2017 at 03:43:49PM +0200, Ulf Hansson wrote:
>> On 30 August 2017 at 14:44, Hans de Goede <[email protected]> wrote:
>> > Hi,
>> >
>> >
>> > On 21-07-17 16:35, Quentin Schulz wrote:
>> >>
>> >> From: Hans de Goede <[email protected]>
>> >>
>> >> Some sdio devices have a multiple stage bring-up process. Specifically
>> >> the esp8089 (for which an out of tree driver is available) loads firmware
>> >> on the first call to its sdio-drivers' probe function and then resets
>> >> the device causing it to reboot from its RAM with the new firmware.
>> >>
>> >> When this sdio device reboots it comes back up in 1 bit 400 KHz mode
>> >> again, and we need to walk through the whole ios negatiation and sdio
>> >> setup
>> >> again.
>> >>
>> >> There are 2 problems with this:
>> >>
>> >> 1) Typically these devices are soldered onto some (ARM) tablet / SBC
>> >> PCB and as such are described in devicetree as "non-removable", which
>> >> causes the mmc-core to scan them only once and not poll for the device
>> >> dropping of the bus. Normally this is the right thing todo but in the
>> >> eso8089 example we need the mmc-core to notice the module has disconnected
>> >> (since it is now in 1 bit mode again it will not talk to the host in 4 bit
>> >> mode). This can be worked around by using "broken-cd" in devicetree
>> >> instead of "non-removable", but that is not a proper fix since the device
>> >> really is non-removable.
>> >>
>> >> 2) When the mmc-core detects the device has disconnected it will poweroff
>> >> the device, causing the RAM loaded firmware to be lost. This can be worked
>> >> around in devicetree by using regulator-always-on (and avoiding the use of
>> >> mmc-pwrseq), but again that is more of a hack then a proper fix.
>> >>
>> >> This commmit fixes 1) by adding a mmc_force_detect_change function which
>> >> will cause scanning for device removal / insertion until a new device is
>> >> detected. 2) Is fixed by a keep_power flag to the mmc_force_detect_change
>> >> function which when set causes the mmc-core to keep the power to the
>> >> device
>> >> on during the rescan.
>> >>
>> >> Cc: Icenowy Zheng <[email protected]>
>> >> Cc: Maxime Ripard <[email protected]>
>> >> Cc: Chen-Yu Tsai <[email protected]>
>> >> Signed-off-by: Hans de Goede <[email protected]>
>> >
>> >
>> > So when I posted this patch quite a while back, there was some discussion
>> > about this and a consensus that this is not the right solution.
>> >
>> > So first of all lets describe the problem:
>> >
>> > The esp8089 sdio wifi chip is really an ARM core with a wifi phy
>> > connected to it (as many wifi chipsets are).
>> >
>> > But this one comes up in some really generic sdio capable boot-loader
>> > mode and we need to feed it firmware and then reboot it into the
>> > new firmware.
>> >
>> > The reboot is where the problems happens. It seems to fallback
>> > from the negotiated 4 wire sdio mode to single wire spi mode then.
>> >
>> > The out of tree version of the driver deals with this by not setting
>> > the non-removable flag as well as setting the broken_cd flag so that
>> > the mmc core polls the device, after the reboot the poll fails
>> > because the mmc-controller and the esp8089 are using a different
>> > amount of wires so the mmc-cmd the poll uses times out.
>> >
>> > After which the esp8089 drivers remove function gets called, and
>> > the mmc stack re-discovers the esp8089 by restarting the whole
>> > number of wires (and speed) used negotiation. After which the
>> > esp8089 driver's probe function gets called (again) and on
>> > firmware loading is has set a global flag, so now it actually
>> > acts as a wifi driver rather then trying to load the firmware
>> > a second time.
>> >
>> > Since I did not want to rely on broken_cd polling I came up
>> > with the hack which is this patch.
>> >
>> > So when this patch was first discussed we came to the conclusion
>> > that what we really need is some sort of mmc_reprobe_device
>> > function which the driver can call from probe which will
>> > redo the number of wires (and speed) used negotiation,
>> > while keeping the sdio_function device as is so that probe can
>> > simply continue after this and we also don't need the ugly
>> > global flag.
>> >
>> > The idea would be for this function to be some wrapper
>> > around mmc_init_card() which resets the ios settings as is
>> > normally done on remove and then call mmc_init_card()
>> > passing in the existing card the same way as is done
>> > one resume, so that the existing card / sdio_function
>> > devices get reused.
>> >
>> > IIRC Ulf would look into writing this mmc_reprobe_device
>> > function and then I would test it with the esp8089, but
>> > Ulf never got around to writing the function and I ended
>> > up working on other things too.
>>
>> Thanks for summary!
>>
>> Just to let you know, I haven't forgot about this problem. I am
>> planning for a major update of the SDIO for power management support,
>> within a not too far future.
>> The issue described above, is then also one of the things I also plan
>> to look into.
>>
>
> I'd like to know if any progress has been made on that problem (I may
> have missed patches).
> Had you had the time to look at the issue?
I have looked at the issue, but not manage to cook some patches for it.
However, it's on my top of my TODO list for mmc. No promises, but
perhaps and hopefully I manage to get something posted during the
coming release cycle.
Sorry for the delay!
Br
Uffe
Hi Ulf,
On Thu, Feb 08, 2018 at 10:31:39PM +0100, Ulf Hansson wrote:
> On 8 February 2018 at 15:59, Quentin Schulz <[email protected]> wrote:
> > Hi Ulf,
> >
> > On Wed, Aug 30, 2017 at 03:43:49PM +0200, Ulf Hansson wrote:
> >> On 30 August 2017 at 14:44, Hans de Goede <[email protected]> wrote:
> >> > Hi,
> >> >
> >> >
> >> > On 21-07-17 16:35, Quentin Schulz wrote:
> >> >>
> >> >> From: Hans de Goede <[email protected]>
> >> >>
> >> >> Some sdio devices have a multiple stage bring-up process. Specifically
> >> >> the esp8089 (for which an out of tree driver is available) loads firmware
> >> >> on the first call to its sdio-drivers' probe function and then resets
> >> >> the device causing it to reboot from its RAM with the new firmware.
> >> >>
> >> >> When this sdio device reboots it comes back up in 1 bit 400 KHz mode
> >> >> again, and we need to walk through the whole ios negatiation and sdio
> >> >> setup
> >> >> again.
> >> >>
> >> >> There are 2 problems with this:
> >> >>
> >> >> 1) Typically these devices are soldered onto some (ARM) tablet / SBC
> >> >> PCB and as such are described in devicetree as "non-removable", which
> >> >> causes the mmc-core to scan them only once and not poll for the device
> >> >> dropping of the bus. Normally this is the right thing todo but in the
> >> >> eso8089 example we need the mmc-core to notice the module has disconnected
> >> >> (since it is now in 1 bit mode again it will not talk to the host in 4 bit
> >> >> mode). This can be worked around by using "broken-cd" in devicetree
> >> >> instead of "non-removable", but that is not a proper fix since the device
> >> >> really is non-removable.
> >> >>
> >> >> 2) When the mmc-core detects the device has disconnected it will poweroff
> >> >> the device, causing the RAM loaded firmware to be lost. This can be worked
> >> >> around in devicetree by using regulator-always-on (and avoiding the use of
> >> >> mmc-pwrseq), but again that is more of a hack then a proper fix.
> >> >>
> >> >> This commmit fixes 1) by adding a mmc_force_detect_change function which
> >> >> will cause scanning for device removal / insertion until a new device is
> >> >> detected. 2) Is fixed by a keep_power flag to the mmc_force_detect_change
> >> >> function which when set causes the mmc-core to keep the power to the
> >> >> device
> >> >> on during the rescan.
> >> >>
> >> >> Cc: Icenowy Zheng <[email protected]>
> >> >> Cc: Maxime Ripard <[email protected]>
> >> >> Cc: Chen-Yu Tsai <[email protected]>
> >> >> Signed-off-by: Hans de Goede <[email protected]>
> >> >
> >> >
> >> > So when I posted this patch quite a while back, there was some discussion
> >> > about this and a consensus that this is not the right solution.
> >> >
> >> > So first of all lets describe the problem:
> >> >
> >> > The esp8089 sdio wifi chip is really an ARM core with a wifi phy
> >> > connected to it (as many wifi chipsets are).
> >> >
> >> > But this one comes up in some really generic sdio capable boot-loader
> >> > mode and we need to feed it firmware and then reboot it into the
> >> > new firmware.
> >> >
> >> > The reboot is where the problems happens. It seems to fallback
> >> > from the negotiated 4 wire sdio mode to single wire spi mode then.
> >> >
> >> > The out of tree version of the driver deals with this by not setting
> >> > the non-removable flag as well as setting the broken_cd flag so that
> >> > the mmc core polls the device, after the reboot the poll fails
> >> > because the mmc-controller and the esp8089 are using a different
> >> > amount of wires so the mmc-cmd the poll uses times out.
> >> >
> >> > After which the esp8089 drivers remove function gets called, and
> >> > the mmc stack re-discovers the esp8089 by restarting the whole
> >> > number of wires (and speed) used negotiation. After which the
> >> > esp8089 driver's probe function gets called (again) and on
> >> > firmware loading is has set a global flag, so now it actually
> >> > acts as a wifi driver rather then trying to load the firmware
> >> > a second time.
> >> >
> >> > Since I did not want to rely on broken_cd polling I came up
> >> > with the hack which is this patch.
> >> >
> >> > So when this patch was first discussed we came to the conclusion
> >> > that what we really need is some sort of mmc_reprobe_device
> >> > function which the driver can call from probe which will
> >> > redo the number of wires (and speed) used negotiation,
> >> > while keeping the sdio_function device as is so that probe can
> >> > simply continue after this and we also don't need the ugly
> >> > global flag.
> >> >
> >> > The idea would be for this function to be some wrapper
> >> > around mmc_init_card() which resets the ios settings as is
> >> > normally done on remove and then call mmc_init_card()
> >> > passing in the existing card the same way as is done
> >> > one resume, so that the existing card / sdio_function
> >> > devices get reused.
> >> >
> >> > IIRC Ulf would look into writing this mmc_reprobe_device
> >> > function and then I would test it with the esp8089, but
> >> > Ulf never got around to writing the function and I ended
> >> > up working on other things too.
> >>
> >> Thanks for summary!
> >>
> >> Just to let you know, I haven't forgot about this problem. I am
> >> planning for a major update of the SDIO for power management support,
> >> within a not too far future.
> >> The issue described above, is then also one of the things I also plan
> >> to look into.
> >>
> >
> > I'd like to know if any progress has been made on that problem (I may
> > have missed patches).
> > Had you had the time to look at the issue?
>
> I have looked at the issue, but not manage to cook some patches for it.
>
> However, it's on my top of my TODO list for mmc. No promises, but
> perhaps and hopefully I manage to get something posted during the
> coming release cycle.
>
Cool! If you ever need some testing, I'd be glad to test your patches
(even if they are in a draft/RFC state).
Also, when you send patches, I'd appreciate being Cc'ed so that I can
put my Tested-by :) Thanks!
Best regards,
Quentin
[...]
>> > I'd like to know if any progress has been made on that problem (I may
>> > have missed patches).
>> > Had you had the time to look at the issue?
>>
>> I have looked at the issue, but not manage to cook some patches for it.
>>
>> However, it's on my top of my TODO list for mmc. No promises, but
>> perhaps and hopefully I manage to get something posted during the
>> coming release cycle.
>>
>
> Cool! If you ever need some testing, I'd be glad to test your patches
> (even if they are in a draft/RFC state).
>
> Also, when you send patches, I'd appreciate being Cc'ed so that I can
> put my Tested-by :) Thanks!
Absolutely! I appreciate it.
Br
Uffe
Hi Uffe,
On Fri, Feb 09, 2018 at 03:01:00PM +0100, Ulf Hansson wrote:
> [...]
>
> >> > I'd like to know if any progress has been made on that problem (I may
> >> > have missed patches).
> >> > Had you had the time to look at the issue?
> >>
> >> I have looked at the issue, but not manage to cook some patches for it.
> >>
> >> However, it's on my top of my TODO list for mmc. No promises, but
> >> perhaps and hopefully I manage to get something posted during the
> >> coming release cycle.
> >>
> >
> > Cool! If you ever need some testing, I'd be glad to test your patches
> > (even if they are in a draft/RFC state).
> >
> > Also, when you send patches, I'd appreciate being Cc'ed so that I can
> > put my Tested-by :) Thanks!
>
> Absolutely! I appreciate it.
>
Any news? Can I help in any way?
Best regards,
Quentin
Hi,
On Fri, Feb 09, 2018 at 03:01:00PM +0100, Ulf Hansson wrote:
> [...]
>
> >> > I'd like to know if any progress has been made on that problem
(I may
> >> > have missed patches).
> >> > Had you had the time to look at the issue?
> >>
> >> I have looked at the issue, but not manage to cook some patches
for it.
> >>
> >> However, it's on my top of my TODO list for mmc. No promises, but
> >> perhaps and hopefully I manage to get something posted during the
> >> coming release cycle.
I would be interested in a ESP8089 driver in mainline and that's why I
want to pick up this discussion.
What is the current status of the "mmc_reprobe_device" implementation,
that Hans was explaining and Ulf wanted to provide some months ago?
BTW, I am not on any of the mailing lists involved, so I tried to
recreate the proper mail headers manually for the reply to the correct
thread.
Thanks,
Frieder
> >>
> >
> > Cool! If you ever need some testing, I'd be glad to test your patches
> > (even if they are in a draft/RFC state).
> >
> > Also, when you send patches, I'd appreciate being Cc'ed so that I can
> > put my Tested-by :) Thanks!
>
> Absolutely! I appreciate it.
Hi,
On 26-09-18 16:44, Frieder Schrempf wrote:
> Hi,
>
> On Fri, Feb 09, 2018 at 03:01:00PM +0100, Ulf Hansson wrote:
>> [...]
>>
>> >> > I'd like to know if any progress has been made on that problem
> (I may
>> >> > have missed patches).
>> >> > Had you had the time to look at the issue?
>> >>
>> >> I have looked at the issue, but not manage to cook some patches
> for it.
>> >>
>> >> However, it's on my top of my TODO list for mmc. No promises, but
>> >> perhaps and hopefully I manage to get something posted during the
>> >> coming release cycle.
>
> I would be interested in a ESP8089 driver in mainline and that's why I want to pick up this discussion.
>
> What is the current status of the "mmc_reprobe_device" implementation, that Hans was explaining and Ulf wanted to provide some months ago?
Ulf did eventually write a new way to deal with this and then Quentin
did manage to get the esp8089 driver to work with it, the new function
to use for this is added by this commit:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/drivers/mmc/core?id=1433269c4d2461be1f36db5dbb453976b38996ff
I'm not sure what the status of upstreaming the ep8089 driver is now
that we've this in place.
Quentin, do you have a version of the esp8089 driver somewhere
which will work correctly with the new mmc_sw_reset() function?
Also what is the status of adding this driver to say staging?
Regards,
Hans
>
> BTW, I am not on any of the mailing lists involved, so I tried to recreate the proper mail headers manually for the reply to the correct thread.
>
> Thanks,
> Frieder
>
>> >>
>> >
>> > Cool! If you ever need some testing, I'd be glad to test your patches
>> > (even if they are in a draft/RFC state).
>> >
>> > Also, when you send patches, I'd appreciate being Cc'ed so that I can
>> > put my Tested-by :) Thanks!
>>
>> Absolutely! I appreciate it.
On Wed, Sep 26, 2018 at 10:19:22PM +0200, Hans de Goede wrote:
> On 26-09-18 16:44, Frieder Schrempf wrote:
> > Hi,
> >
> > On Fri, Feb 09, 2018 at 03:01:00PM +0100, Ulf Hansson wrote:
> > > [...]
> > >
> > > >> > I'd like to know if any progress has been made on that problem
> > (I may
> > > >> > have missed patches).
> > > >> > Had you had the time to look at the issue?
> > > >>
> > > >> I have looked at the issue, but not manage to cook some patches
> > for it.
> > > >>
> > > >> However, it's on my top of my TODO list for mmc. No promises, but
> > > >> perhaps and hopefully I manage to get something posted during the
> > > >> coming release cycle.
> >
> > I would be interested in a ESP8089 driver in mainline and that's why I want to pick up this discussion.
> >
> > What is the current status of the "mmc_reprobe_device" implementation, that Hans was explaining and Ulf wanted to provide some months ago?
>
> Ulf did eventually write a new way to deal with this and then Quentin
> did manage to get the esp8089 driver to work with it, the new function
> to use for this is added by this commit:
>
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/drivers/mmc/core?id=1433269c4d2461be1f36db5dbb453976b38996ff
>
> I'm not sure what the status of upstreaming the ep8089 driver is now
> that we've this in place.
>
> Quentin, do you have a version of the esp8089 driver somewhere
> which will work correctly with the new mmc_sw_reset() function?
>
> Also what is the status of adding this driver to say staging?
IIRC, we tried to get it into staging, and we got told that it was too
nice for staging at this point. So we're basically stuck somewhere
between staging and !staging, with the driver being too nice for the
former, and not nice enough for the latter :)
Maxime
--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Hi,
On 27.09.2018 10:14, Maxime Ripard wrote:
> On Wed, Sep 26, 2018 at 10:19:22PM +0200, Hans de Goede wrote:
>> On 26-09-18 16:44, Frieder Schrempf wrote:
>>> Hi,
>>>
>>> On Fri, Feb 09, 2018 at 03:01:00PM +0100, Ulf Hansson wrote:
>>>> [...]
>>>>
>>>>>>> I'd like to know if any progress has been made on that problem
>>> (I may
>>>>>>> have missed patches).
>>>>>>> Had you had the time to look at the issue?
>>>>>>
>>>>>> I have looked at the issue, but not manage to cook some patches
>>> for it.
>>>>>>
>>>>>> However, it's on my top of my TODO list for mmc. No promises, but
>>>>>> perhaps and hopefully I manage to get something posted during the
>>>>>> coming release cycle.
>>>
>>> I would be interested in a ESP8089 driver in mainline and that's why I want to pick up this discussion.
>>>
>>> What is the current status of the "mmc_reprobe_device" implementation, that Hans was explaining and Ulf wanted to provide some months ago?
>>
>> Ulf did eventually write a new way to deal with this and then Quentin
>> did manage to get the esp8089 driver to work with it, the new function
>> to use for this is added by this commit:
>>
>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/drivers/mmc/core?id=1433269c4d2461be1f36db5dbb453976b38996ff
>>
>> I'm not sure what the status of upstreaming the ep8089 driver is now
>> that we've this in place.
>>
>> Quentin, do you have a version of the esp8089 driver somewhere
>> which will work correctly with the new mmc_sw_reset() function?
>>
>> Also what is the status of adding this driver to say staging?
>
> IIRC, we tried to get it into staging, and we got told that it was too
> nice for staging at this point. So we're basically stuck somewhere
> between staging and !staging, with the driver being too nice for the
> former, and not nice enough for the latter :)
Ok, and is there someone willing to continue upstreaming the driver?
Maybe someone can rebase and resend the latest approach?
After all it looks like a lot of work has already been done.
Thanks,
Frieder
Hi Frieder,
On Mon, Oct 08, 2018 at 11:53:21AM +0200, Frieder Schrempf wrote:
> Hi,
>
> On 27.09.2018 10:14, Maxime Ripard wrote:
> > On Wed, Sep 26, 2018 at 10:19:22PM +0200, Hans de Goede wrote:
> > > On 26-09-18 16:44, Frieder Schrempf wrote:
> > > > Hi,
> > > >
> > > > On Fri, Feb 09, 2018 at 03:01:00PM +0100, Ulf Hansson wrote:
> > > > > [...]
> > > > >
> > > > > > > > I'd like to know if any progress has been made on that problem
> > > > (I may
> > > > > > > > have missed patches).
> > > > > > > > Had you had the time to look at the issue?
> > > > > > >
> > > > > > > I have looked at the issue, but not manage to cook some patches
> > > > for it.
> > > > > > >
> > > > > > > However, it's on my top of my TODO list for mmc. No promises, but
> > > > > > > perhaps and hopefully I manage to get something posted during the
> > > > > > > coming release cycle.
> > > >
> > > > I would be interested in a ESP8089 driver in mainline and that's why I want to pick up this discussion.
> > > >
> > > > What is the current status of the "mmc_reprobe_device" implementation, that Hans was explaining and Ulf wanted to provide some months ago?
> > >
> > > Ulf did eventually write a new way to deal with this and then Quentin
> > > did manage to get the esp8089 driver to work with it, the new function
> > > to use for this is added by this commit:
> > >
> > > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/drivers/mmc/core?id=1433269c4d2461be1f36db5dbb453976b38996ff
> > >
> > > I'm not sure what the status of upstreaming the ep8089 driver is now
> > > that we've this in place.
> > >
> > > Quentin, do you have a version of the esp8089 driver somewhere
> > > which will work correctly with the new mmc_sw_reset() function?
> > >
> > > Also what is the status of adding this driver to say staging?
> >
> > IIRC, we tried to get it into staging, and we got told that it was too
> > nice for staging at this point. So we're basically stuck somewhere
> > between staging and !staging, with the driver being too nice for the
> > former, and not nice enough for the latter :)
>
> Ok, and is there someone willing to continue upstreaming the driver? Maybe
> someone can rebase and resend the latest approach?
>
> After all it looks like a lot of work has already been done.
>
There's clean up to do. It's time consuming but shouldn't be too hard to
do.
Then, we stressed the driver with an iperf test and it crashes very
often so we first need to identify if it happened with the "original"
driver before Icenowy's, Hans's and my clean-up. If it happened, since
we don't have a datasheet, it might be not that easy to fix. If it
didn't happen, then we have to find out where I cleaned up too much :D
I'm not currently working on this topic so anyone willing to take over
the work is free to do so.
Thanks,
Quentin
Hi Quentin,
On 09.10.2018 09:52, Quentin Schulz wrote:
> Hi Frieder,
>
> On Mon, Oct 08, 2018 at 11:53:21AM +0200, Frieder Schrempf wrote:
>> Hi,
>>
>> On 27.09.2018 10:14, Maxime Ripard wrote:
>>> On Wed, Sep 26, 2018 at 10:19:22PM +0200, Hans de Goede wrote:
>>>> On 26-09-18 16:44, Frieder Schrempf wrote:
>>>>> Hi,
>>>>>
>>>>> On Fri, Feb 09, 2018 at 03:01:00PM +0100, Ulf Hansson wrote:
>>>>>> [...]
>>>>>>
>>>>>>>>> I'd like to know if any progress has been made on that problem
>>>>> (I may
>>>>>>>>> have missed patches).
>>>>>>>>> Had you had the time to look at the issue?
>>>>>>>>
>>>>>>>> I have looked at the issue, but not manage to cook some patches
>>>>> for it.
>>>>>>>>
>>>>>>>> However, it's on my top of my TODO list for mmc. No promises, but
>>>>>>>> perhaps and hopefully I manage to get something posted during the
>>>>>>>> coming release cycle.
>>>>>
>>>>> I would be interested in a ESP8089 driver in mainline and that's why I want to pick up this discussion.
>>>>>
>>>>> What is the current status of the "mmc_reprobe_device" implementation, that Hans was explaining and Ulf wanted to provide some months ago?
>>>>
>>>> Ulf did eventually write a new way to deal with this and then Quentin
>>>> did manage to get the esp8089 driver to work with it, the new function
>>>> to use for this is added by this commit:
>>>>
>>>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/drivers/mmc/core?id=1433269c4d2461be1f36db5dbb453976b38996ff
>>>>
>>>> I'm not sure what the status of upstreaming the ep8089 driver is now
>>>> that we've this in place.
>>>>
>>>> Quentin, do you have a version of the esp8089 driver somewhere
>>>> which will work correctly with the new mmc_sw_reset() function?
>>>>
>>>> Also what is the status of adding this driver to say staging?
>>>
>>> IIRC, we tried to get it into staging, and we got told that it was too
>>> nice for staging at this point. So we're basically stuck somewhere
>>> between staging and !staging, with the driver being too nice for the
>>> former, and not nice enough for the latter :)
>>
>> Ok, and is there someone willing to continue upstreaming the driver? Maybe
>> someone can rebase and resend the latest approach?
>>
>> After all it looks like a lot of work has already been done.
>>
>
> There's clean up to do. It's time consuming but shouldn't be too hard to
> do.
>
> Then, we stressed the driver with an iperf test and it crashes very
> often so we first need to identify if it happened with the "original"
> driver before Icenowy's, Hans's and my clean-up. If it happened, since
> we don't have a datasheet, it might be not that easy to fix. If it
> didn't happen, then we have to find out where I cleaned up too much :D
>
> I'm not currently working on this topic so anyone willing to take over
> the work is free to do so.
Thanks a lot for providing the details. It is currently not clear on our
side, if the project with the ESP8089 will happen, but if that's the
case we might pick up the work.
Thanks
Frieder