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 <quentin.schulz@free-electrons.com>
+ *
+ * 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 <quentin.schulz@free-electrons.com>
+ *
+ * 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 <quentin.schulz@free-electrons.com>
+ *
+ * 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 <quentin.schulz@free-electrons.com>
+ *
+ * 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 <quentin.schulz@free-electrons.com>
+ *
+ * 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 <quentin.schulz@free-electrons.com>
+ *
+ * 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 <quentin.schulz@free-electrons.com>
+ *
+ * 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 <quentin.schulz@free-electrons.com>
+ *
+ * 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 <quentin.schulz@free-electrons.com>
+ *
+ * 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 = &params->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 <quentin.schulz@free-electrons.com>
+ *
+ * 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 <quentin.schulz@free-electrons.com>
+ *
+ * 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 <quentin.schulz@free-electrons.com>
+ *
+ * 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 <quentin.schulz@free-electrons.com>
+ *
+ * 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 <quentin.schulz@free-electrons.com>
+ *
+ * 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 <quentin.schulz@free-electrons.com>
+ *
+ * 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 <quentin.schulz@free-electrons.com>
+ *
+ * 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 <quentin.schulz@free-electrons.com>
+ *
+ * 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 <quentin.schulz@free-electrons.com>
+ *
+ * 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 <quentin.schulz@free-electrons.com>
+ *
+ * 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 <quentin.schulz@free-electrons.com>
+ *
+ * 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 <quentin.schulz@free-electrons.com>
+ *
+ * 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 <quentin.schulz@free-electrons.com>
+ *
+ * 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
