[PATCH 1/2] staging: net: wireless: add ESP8089 WiFi driver

Dan Carpenter dan.carpenter at oracle.com
Tue Jul 25 10:31:41 UTC 2017


Yeah.  This feels like it could go into the main linux-wireless tree
with a little work.  I put some random review comments inline.  Nothing
super drastic stands out.

regards,
dan carpenter


On Fri, Jul 21, 2017 at 04:35:01PM +0200, Quentin Schulz wrote:
> The Espressif ESP8089 WiFi chips can be often found in cheap tablets.
> There is one in A23 Polaroid tablets for example.
> 
> The chip is often embedded as an eMMC SDIO device.
> 
> The code was taken from an out-of-tree repository and has seen a first
> pass in the cleanup process.
> 
> At the moment, there is no publicly available datasheet for this chip.
> 
> Cc: Hans de Goede <hdegoede at redhat.com>
> Cc: Icenowy Zheng <icenowy at aosc.xyz>
> Signed-off-by: Quentin Schulz <quentin.schulz at free-electrons.com>
> ---
>  drivers/staging/Kconfig                     |    2 +
>  drivers/staging/Makefile                    |    1 +
>  drivers/staging/esp8089/Kconfig             |   13 +
>  drivers/staging/esp8089/Makefile            |    7 +
>  drivers/staging/esp8089/esp_ctrl.c          |  527 ++++++++
>  drivers/staging/esp8089/esp_ctrl.h          |   48 +
>  drivers/staging/esp8089/esp_debug.c         |  247 ++++
>  drivers/staging/esp8089/esp_debug.h         |   69 ++
>  drivers/staging/esp8089/esp_file.c          |  221 ++++
>  drivers/staging/esp8089/esp_file.h          |   30 +
>  drivers/staging/esp8089/esp_init_data.h     |   17 +
>  drivers/staging/esp8089/esp_io.c            |  294 +++++
>  drivers/staging/esp8089/esp_mac80211.c      | 1496 +++++++++++++++++++++++
>  drivers/staging/esp8089/esp_mac80211.h      |   33 +
>  drivers/staging/esp8089/esp_main.c          |  199 ++++
>  drivers/staging/esp8089/esp_pub.h           |  188 +++
>  drivers/staging/esp8089/esp_sif.h           |  131 ++
>  drivers/staging/esp8089/esp_sip.c           | 1718 +++++++++++++++++++++++++++
>  drivers/staging/esp8089/esp_sip.h           |  150 +++
>  drivers/staging/esp8089/esp_utils.c         |  133 +++
>  drivers/staging/esp8089/esp_utils.h         |   27 +
>  drivers/staging/esp8089/esp_wl.h            |   35 +
>  drivers/staging/esp8089/esp_wmac.h          |   87 ++
>  drivers/staging/esp8089/sdio_sif_esp.c      |  552 +++++++++
>  drivers/staging/esp8089/sip2_common.h       |  388 ++++++
>  drivers/staging/esp8089/slc_host_register.h |  263 ++++
>  26 files changed, 6876 insertions(+)
>  create mode 100644 drivers/staging/esp8089/Kconfig
>  create mode 100644 drivers/staging/esp8089/Makefile
>  create mode 100644 drivers/staging/esp8089/esp_ctrl.c
>  create mode 100644 drivers/staging/esp8089/esp_ctrl.h
>  create mode 100644 drivers/staging/esp8089/esp_debug.c
>  create mode 100644 drivers/staging/esp8089/esp_debug.h
>  create mode 100644 drivers/staging/esp8089/esp_file.c
>  create mode 100644 drivers/staging/esp8089/esp_file.h
>  create mode 100644 drivers/staging/esp8089/esp_init_data.h
>  create mode 100644 drivers/staging/esp8089/esp_io.c
>  create mode 100644 drivers/staging/esp8089/esp_mac80211.c
>  create mode 100644 drivers/staging/esp8089/esp_mac80211.h
>  create mode 100644 drivers/staging/esp8089/esp_main.c
>  create mode 100644 drivers/staging/esp8089/esp_pub.h
>  create mode 100644 drivers/staging/esp8089/esp_sif.h
>  create mode 100644 drivers/staging/esp8089/esp_sip.c
>  create mode 100644 drivers/staging/esp8089/esp_sip.h
>  create mode 100644 drivers/staging/esp8089/esp_utils.c
>  create mode 100644 drivers/staging/esp8089/esp_utils.h
>  create mode 100644 drivers/staging/esp8089/esp_wl.h
>  create mode 100644 drivers/staging/esp8089/esp_wmac.h
>  create mode 100644 drivers/staging/esp8089/sdio_sif_esp.c
>  create mode 100644 drivers/staging/esp8089/sip2_common.h
>  create mode 100644 drivers/staging/esp8089/slc_host_register.h
> 
> diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
> index e97d72e3bc40..0bd007837429 100644
> --- a/drivers/staging/Kconfig
> +++ b/drivers/staging/Kconfig
> @@ -114,4 +114,6 @@ source "drivers/staging/vboxvideo/Kconfig"
>  
>  source "drivers/staging/pi433/Kconfig"
>  
> +source "drivers/staging/esp8089/Kconfig"
> +
>  endif # STAGING
> diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
> index 993ed0c1556c..23cfa1258c4e 100644
> --- a/drivers/staging/Makefile
> +++ b/drivers/staging/Makefile
> @@ -46,3 +46,4 @@ obj-$(CONFIG_BCM2835_VCHIQ)	+= vc04_services/
>  obj-$(CONFIG_CRYPTO_DEV_CCREE)	+= ccree/
>  obj-$(CONFIG_DRM_VBOXVIDEO)	+= vboxvideo/
>  obj-$(CONFIG_PI433)		+= pi433/
> +obj-$(CONFIG_ESP8089)		+= esp8089/
> diff --git a/drivers/staging/esp8089/Kconfig b/drivers/staging/esp8089/Kconfig
> new file mode 100644
> index 000000000000..8fdd8fa828ad
> --- /dev/null
> +++ b/drivers/staging/esp8089/Kconfig
> @@ -0,0 +1,13 @@
> +config ESP8089
> +	tristate "Espressif ESP8089 SDIO WiFi"
> +	depends on MAC80211
> +	---help---
> +	  ESP8089 is a low-budget 2.4GHz WiFi chip by Espressif, used in many
> +	  cheap tablets with Allwinner or Rockchip SoC
> +
> +config ESP8089_DEBUG_FS
> +	bool "Enable DebugFS support for ESP8089"
> +	depends on ESP8089
> +	default y
> +	---help---
> +	  DebugFS support for ESP8089
> diff --git a/drivers/staging/esp8089/Makefile b/drivers/staging/esp8089/Makefile
> new file mode 100644
> index 000000000000..827c66d8b5d5
> --- /dev/null
> +++ b/drivers/staging/esp8089/Makefile
> @@ -0,0 +1,7 @@
> +MODULE_NAME = esp8089
> +
> +$(MODULE_NAME)-y := esp_debug.o sdio_sif_esp.o esp_io.o \
> +    esp_file.o esp_main.o esp_sip.o esp_ctrl.o \
> +    esp_mac80211.o esp_debug.o esp_utils.o
> +
> +obj-$(CONFIG_ESP8089) := esp8089.o
> diff --git a/drivers/staging/esp8089/esp_ctrl.c b/drivers/staging/esp8089/esp_ctrl.c
> new file mode 100644
> index 000000000000..e5a95e7b313f
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_ctrl.c
> @@ -0,0 +1,527 @@
> +/*
> + * Copyright (c) 2009 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + *      Quentin Schulz <quentin.schulz at 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 at 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 at 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 at 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 at 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];

I don't think we really need to memcpy() val to digits[].

> +
> +	digits[i] = 0;
> +
> +	if (kstrtou16(digits, 10, &value) < 0) {
> +		printk("%s: invalid attribute value: %s", __func__, digits);
> +		return -1;

propogate the error code.

> +	}
> +
> +	return set_init_config_attr(attr, attr_len, value);
> +}
> +
> +/* export config table settings to SDIO driver */
> +static void record_init_config(void)
> +{
> +	int i;
> +
> +	for (i = 0; i < MAX_ATTR_NUM; i++) {
> +		if (esp_init_table[i].value < 0)
> +			continue;
> +
> +		if (!strcmp(esp_init_table[i].attr, "share_xtal"))
> +			sif_record_bt_config(esp_init_table[i].value);
> +		else if (!strcmp(esp_init_table[i].attr, "ext_rst"))
> +			sif_record_rst_config(esp_init_table[i].value);
> +		else if (!strcmp(esp_init_table[i].attr, "wakeup_gpio"))
> +			sif_record_wakeup_gpio_config(esp_init_table[i].value);
> +		else if (!strcmp(esp_init_table[i].attr, "ate_test"))
> +			sif_record_ate_config(esp_init_table[i].value);
> +	}
> +}
> +
> +int request_init_conf(struct device *dev)
> +{
> +	char *attr, *str, *p;
> +	int attr_len, str_len;
> +	int ret = 0;
> +	u32 val;
> +
> +	/* Check for any parameters passed through devicetree (or acpi) */
> +	if (!device_property_read_u32(dev, "esp,crystal-26M-en", &val))
> +		set_init_config_attr("crystal_26M_en", strlen("crystal_26M_en"),
> +				     val);
> +
> +	/* parse optional parameter in the form of key1=value,key2=value,.. */
> +	attr = NULL;
> +	attr_len = 0;
> +	str_len = 0;
> +	for (p = str = modparam_init_data_conf; p && *p; p++) {
> +		if (*p == '=') {
> +			attr = str;
> +			attr_len = str_len;
> +
> +			str = p + 1;
> +			str_len = 0;
> +		} else if (*p == ',' || *p == ';') {
> +			if (attr_len)
> +				ret |= update_init_config_attr(attr, attr_len,
> +							       str, str_len);
> +
> +			str = p + 1;
> +			attr_len = 0;
> +			str_len = 0;
> +		} else {
> +			str_len++;
> +		}
> +	}
> +
> +	if (attr_len && str != attr)
> +		ret |= update_init_config_attr(attr, attr_len, str, str_len);
> +
> +	record_init_config();
> +
> +	return ret;
> +}
> +
> +void fix_init_data(u8 *init_data_buf, int buf_size)
> +{
> +	int i;
> +
> +	for (i = 0; i < MAX_FIX_ATTR_NUM; i++) {
> +		if (esp_init_table[i].offset > -1 &&
> +		    esp_init_table[i].offset < buf_size &&
> +		    esp_init_table[i].value > -1)
> +			*(u8 *)(init_data_buf + esp_init_table[i].offset) = esp_init_table[i].value;
> +	}
> +}
> diff --git a/drivers/staging/esp8089/esp_file.h b/drivers/staging/esp8089/esp_file.h
> new file mode 100644
> index 000000000000..ba9946d64c32
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_file.h
> @@ -0,0 +1,30 @@
> +/*
> + * Copyright (c) 2010 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + *      Quentin Schulz <quentin.schulz at 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 at 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 at 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? */

I think the bus mostly gets unlocked in esp_dsr() but it's not clear
how that happens on the success path.  But that's the common path so
it must happen.  I don't like that we mix sif_lock_bus() and
sdio_claim_host().  I guess I would prefer that we could get rid of
sif_lock_bus() because we should be locking instead of saying "We have
too many NULL pointers so let's forget about locking for now."

> +	sdio_claim_host(func);
> +	atomic_set(&sctrl->irq_handling, 0);
> +}
> +
> +struct slc_host_regs *sif_get_regs(struct esp_pub *epub)
> +{
> +	if (!epub || !epub->sif)
> +		return NULL;
> +
> +	return &epub->sif->slc_regs;
> +}
> +
> +void sif_disable_target_interrupt(struct esp_pub *epub)
> +{
> +	if (!epub || !epub->sif || !epub->sif->func)
> +		return;
> +
> +	sif_lock_bus(epub);
> +#ifdef HOST_RESET_BUG
> +	mdelay(10);
> +#endif
> +	memset(epub->sif->dma_buffer, 0, sizeof(u32));
> +	esp_common_write_with_addr(epub, SLC_HOST_INT_ENA,
> +				   epub->sif->dma_buffer, sizeof(u32),
> +				   ESP_SIF_NOSYNC);
> +#ifdef HOST_RESET_BUG
> +	mdelay(10);
> +#endif
> +
> +	sif_unlock_bus(epub);
> +
> +	mdelay(1);
> +
> +	sif_lock_bus(epub);
> +	sif_interrupt_target(epub, 7);
> +	sif_unlock_bus(epub);
> +}
> +
> +/* FIXME: MEH */
> +
> +static int bt_config;
> +void sif_record_bt_config(int value)
> +{
> +	bt_config = value;
> +}
> +
> +int sif_get_bt_config(void)
> +{
> +	return bt_config;
> +}
> +
> +static int rst_config;
> +void sif_record_rst_config(int value)
> +{
> +	rst_config = value;
> +}
> +
> +int sif_get_rst_config(void)
> +{
> +	return rst_config;
> +}
> +
> +static int ate_test;
> +void sif_record_ate_config(int value)
> +{
> +	ate_test = value;
> +}
> +
> +int sif_get_ate_config(void)
> +{
> +	return ate_test;
> +}
> +
> +static int wakeup_gpio = 12;
> +void sif_record_wakeup_gpio_config(int value)
> +{
> +	wakeup_gpio = value;
> +}
> +
> +int sif_get_wakeup_gpio_config(void)
> +{
> +	return wakeup_gpio;
> +}
> diff --git a/drivers/staging/esp8089/esp_mac80211.c b/drivers/staging/esp8089/esp_mac80211.c
> new file mode 100644
> index 000000000000..fd5049fc1f6b
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_mac80211.c
> @@ -0,0 +1,1496 @@
> +/*
> + * Copyright (c) 2011 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + *      Quentin Schulz <quentin.schulz at 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) {

Move this error handling next to the getaddr_index() call.

> +		dev_err(epub->dev, "support for MAX %d interfaces\n",
> +			ESP_PUB_MAX_VIF);
> +		return -EOPNOTSUPP;
> +	}
> +
> +	if (BIT(svif.index) & epub->vif_slot) {
> +		dev_err(epub->dev, "interface %d already used\n", svif.index);
> +		return -EOPNOTSUPP;
> +	}
> +
> +	epub->vif_slot |= BIT(svif.index);
> +
> +	ret = esp_set_svif_mode(&svif, vif->type, false);
> +	if (ret < 0) {
> +		dev_err(epub->dev, "no support for interface type %d\n",
> +			vif->type);
> +		return ret;
> +	}
> +
> +	sip_cmd(epub, SIP_CMD_SETVIF, (u8 *)&svif, sizeof(svif));
> +
> +	return 0;
> +}
> +
> +/*
> +Called when a netdevice changes type. This callback
> +2983  *      is optional, but only if it is supported can interface types be
> +2984  *      switched while the interface is UP. The callback may sleep.
> +2985  *      Note that while an interface is being switched, it will not be
> +2986  *      found by the interface iteration callbacks.
> +   */
> +static int esp_op_change_interface(struct ieee80211_hw *hw,
> +				   struct ieee80211_vif *vif,
> +				   enum nl80211_iftype new_type, bool p2p)
> +{
> +	struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +	struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
> +	struct sip_cmd_setvif svif;
> +	int ret;
> +
> +	memcpy(svif.mac, vif->addr, ETH_ALEN);
> +	svif.index = evif->index;
> +	svif.set = 2;
> +
> +	ret = esp_set_svif_mode(&svif, new_type, p2p);
> +	if (ret < 0)
> +		return ret;
> +
> +	sip_cmd(epub, SIP_CMD_SETVIF, (u8 *)&svif, sizeof(svif));
> +
> +	return 0;
> +}
> +
> +/*
> +   Notifies a driver that an interface is going down.
> +   2989  *      The @stop callback is called after this if it is the last interface
> +   2990  *      and no monitor interfaces are present.
> +   2991  *      When all interfaces are removed, the MAC address in the hardware
> +   2992  *      must be cleared so the device no longer acknowledges packets,
> +   2993  *      the mac_addr member of the conf structure is, however, set to the
> +   2994  *      MAC address of the device going away.
> +   2995  *      Hence, this callback must be implemented. It can sleep.
> +   */
> +static void esp_op_remove_interface(struct ieee80211_hw *hw,
> +				    struct ieee80211_vif *vif)
> +{
> +	struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +	struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
> +	struct sip_cmd_setvif svif = {0};
> +
> +	svif.index = evif->index;
> +	epub->vif_slot &= ~BIT(svif.index);
> +
> +	if (evif->ap_up) {
> +		evif->beacon_interval = 0;
> +		del_timer_sync(&evif->beacon_timer);
> +		evif->ap_up = false;
> +	}
> +
> +	epub->vif = NULL;
> +	evif->epub = NULL;
> +
> +	sip_cmd(epub, SIP_CMD_SETVIF, (u8 *)&svif, sizeof(svif));
> +	/* TODO: clean up tx/rx queue */
> +}
> +
> +/* FIXME: WTF? */
> +
> +#define BEACON_TIM_SAVE_MAX 20
> +u8 beacon_tim_saved[BEACON_TIM_SAVE_MAX];
> +int beacon_tim_count;
> +static void beacon_tim_init(void)
> +{
> +	memset(beacon_tim_saved, 0, BEACON_TIM_SAVE_MAX);
> +	beacon_tim_count = 0;
> +}
> +
> +static u8 beacon_tim_save(u8 this_tim)
> +{
> +	u8 all_tim = 0;
> +	int i;
> +
> +	beacon_tim_saved[beacon_tim_count] = this_tim;
> +
> +	beacon_tim_count = (beacon_tim_count + 1) % BEACON_TIM_SAVE_MAX;
> +
> +	for (i = 0; i < BEACON_TIM_SAVE_MAX; i++)
> +		all_tim |= beacon_tim_saved[i];
> +
> +	return all_tim;
> +}
> +
> +static bool beacon_tim_alter(struct sk_buff *beacon)
> +{
> +	u8 *p, *tim_end;
> +	u8 tim_count;
> +	int len, remain_len;
> +	struct ieee80211_mgmt *mgmt;
> +
> +	if (!beacon)
> +		return false;
> +
> +	mgmt = (struct ieee80211_mgmt *)((u8 *)beacon->data);
> +
> +	remain_len = beacon->len - ((u8 *)mgmt->u.beacon.variable -
> +				    (u8 *)mgmt + 12);
> +	p = mgmt->u.beacon.variable;
> +
> +	while (remain_len > 0) {
> +		len = *(++p);
> +
> +		if (*p == WLAN_EID_TIM) {	// tim field
> +			tim_end = p + len;
> +			tim_count = *(++p);
> +			p += 2;
> +			//multicast
> +			if (!tim_count)
> +				*p |= 0x1;
> +
> +			if (!(*p & 0xfe) && tim_end >= p + 1) {	// we only support 8 sta in this case
> +				p++;
> +				*p = beacon_tim_save(*p);
> +			}
> +
> +			return tim_count == 0;
> +		}
> +
> +		p += len + 1;
> +		remain_len -= 2 + len;
> +	}
> +
> +	return false;
> +}
> +
> +unsigned long init_jiffies;
> +unsigned long cycle_beacon_count;
> +static void drv_handle_beacon(unsigned long data)
> +{
> +	struct ieee80211_vif *vif = (struct ieee80211_vif *)data;
> +	struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
> +	struct sk_buff *beacon;
> +	struct sk_buff *skb;
> +	bool tim_reach;
> +
> +	if (!evif->epub)
> +		return;
> +
> +	mdelay(2400 * (cycle_beacon_count % 25) % 10000 / 1000);
> +
> +	beacon = ieee80211_beacon_get(evif->epub->hw, vif);
> +
> +	tim_reach = beacon_tim_alter(beacon);
> +
> +	if (beacon)
> +		sip_tx_data_pkt_enqueue(evif->epub, beacon);
> +
> +	if (cycle_beacon_count++ == 100) {
> +		init_jiffies = jiffies;
> +		cycle_beacon_count -= 100;
> +	}
> +
> +	mod_timer(&evif->beacon_timer, init_jiffies +
> +		  msecs_to_jiffies(cycle_beacon_count *
> +				   vif->bss_conf.beacon_int * 1024 / 1000));
> +	//FIXME:the packets must be sent at home channel
> +	//send buffer mcast frames
> +	if (tim_reach) {
> +		skb = ieee80211_get_buffered_bc(evif->epub->hw, vif);
> +		while (skb) {
> +			sip_tx_data_pkt_enqueue(evif->epub, skb);
> +			skb = ieee80211_get_buffered_bc(evif->epub->hw, vif);
> +		}
> +	}
> +}
> +
> +static void init_beacon_timer(struct ieee80211_vif *vif)
> +{
> +	struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
> +
> +	beacon_tim_init();
> +	setup_timer(&evif->beacon_timer, drv_handle_beacon, (unsigned long)vif);	//TBD, not init here...
> +	cycle_beacon_count = 1;
> +	init_jiffies = jiffies;
> +	evif->beacon_timer.expires = init_jiffies +
> +		msecs_to_jiffies(cycle_beacon_count * vif->bss_conf.beacon_int *
> +				 1024 / 1000);
> +	add_timer(&evif->beacon_timer);
> +}
> +
> +/*
> +   Handler for configuration requests. IEEE 802.11 code calls this
> +   2998  *      function to change hardware configuration, e.g., channel.
> +   2999  *      This function should never fail but returns a negative error code
> +   3000  *      if it does. The callback can sleep.
> +   */
> +static int esp_op_config(struct ieee80211_hw *hw, u32 changed)
> +{
> +	struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +
> +	if (changed & (IEEE80211_CONF_CHANGE_CHANNEL |
> +		       IEEE80211_CONF_CHANGE_IDLE))
> +		sip_send_config(epub, &hw->conf);
> +
> +	return 0;
> +}
> +
> +/*
> +Handler for configuration requests related to BSS
> +3003  *      parameters that may vary during BSS's lifespan, and may affect low
> +3004  *      level driver (e.g. assoc/disassoc status, erp parameters).
> +3005  *      This function should not be used if no BSS has been set, unless
> +3006  *      for association indication. The @changed parameter indicates which
> +3007  *      of the bss parameters has changed when a call is made. The callback
> +3008  *      can sleep.
> +   */
> +static void esp_op_bss_info_changed(struct ieee80211_hw *hw,
> +				    struct ieee80211_vif *vif,
> +				    struct ieee80211_bss_conf *info,
> +				    u32 changed)
> +{
> +	struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +	struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
> +	u8 *bssid = (u8 *)info->bssid;
> +	bool assoc = info->assoc;
> +
> +	// ieee80211_bss_conf(include/net/mac80211.h) is included in ieee80211_sub_if_data(net/mac80211/ieee80211_i.h) , does bssid=ieee80211_if_ap's ssid ?
> +
> +	if (vif->type == NL80211_IFTYPE_STATION) {
> +		if (changed & BSS_CHANGED_BSSID ||
> +		    ((changed & BSS_CHANGED_ASSOC) && assoc)) {
> +			evif->beacon_interval = info->aid;
> +			memcpy(epub->wl.bssid, bssid, ETH_ALEN);
> +			sip_send_bss_info_update(epub, evif, bssid, assoc);
> +		} else if ((changed & BSS_CHANGED_ASSOC) && !assoc) {
> +			evif->beacon_interval = 0;
> +			memset(epub->wl.bssid, 0, ETH_ALEN);
> +			sip_send_bss_info_update(epub, evif, bssid, assoc);
> +		}
> +	} else if (vif->type == NL80211_IFTYPE_AP) {
> +		if (!(changed & BSS_CHANGED_BEACON_ENABLED) &&
> +		    !(changed & BSS_CHANGED_BEACON_INT))
> +			return;
> +
> +		if (info->enable_beacon && !evif->ap_up) {
> +			evif->beacon_interval = info->beacon_int;
> +			init_beacon_timer(vif);
> +			sip_send_bss_info_update(epub, evif, bssid, 2);
> +			evif->ap_up = true;
> +		} else if (!info->enable_beacon && evif->ap_up &&
> +			   !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) {
> +			evif->beacon_interval = 0;
> +			del_timer_sync(&evif->beacon_timer);
> +			sip_send_bss_info_update(epub, evif, bssid, 2);
> +			evif->ap_up = false;
> +		}
> +	}
> +}
> +
> +/*
> +   Configure the device's RX filter.
> +   3015  *      See the section "Frame filtering" for more information.
> +   3016  *      This callback must be implemented and can sleep.
> + */
> +static void esp_op_configure_filter(struct ieee80211_hw *hw,
> +				    unsigned int changed_flags,
> +				    unsigned int *total_flags, u64 multicast)
> +{
> +	struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +
> +	epub->rx_filter = 0;
> +
> +	if (*total_flags & FIF_ALLMULTI)
> +		epub->rx_filter |= FIF_ALLMULTI;
> +
> +	*total_flags = epub->rx_filter;
> +}
> +
> +static bool is_cipher_suite_wep(u32 cipher)
> +{
> +	return (cipher == WLAN_CIPHER_SUITE_WEP40) ||
> +		(cipher == WLAN_CIPHER_SUITE_WEP104);
> +}
> +
> +/*
> +   See the section "Hardware crypto acceleration"
> +   3029  *      This callback is only called between add_interface and
> +   3030  *      remove_interface calls, i.e. while the given virtual interface
> +   3031  *      is enabled.
> +   3032  *      Returns a negative error code if the key can't be added.
> +   3033  *      The callback can sleep.
> + */
> +static int esp_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
> +			  struct ieee80211_vif *vif, struct ieee80211_sta *sta,
> +			  struct ieee80211_key_conf *key)
> +{
> +	struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +	struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
> +	struct esp_hw_idx_map *map;
> +	atomic_t *cnt1, *cnt2;
> +	u8 i, ifidx = evif->index, isvalid, index;
> +	u8 *peer_addr;
> +	int ret, counter;
> +
> +	key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
> +
> +	if (sta && memcmp(sta->addr, epub->wl.bssid, ETH_ALEN))
> +		peer_addr = sta->addr;
> +	else
> +		peer_addr = epub->wl.bssid;
> +
> +	isvalid = !!(cmd == SET_KEY);
> +
> +	if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE ||
> +	    is_cipher_suite_wep(key->cipher))
> +		map = epub->low_map[ifidx];
> +	else
> +		map = epub->hi_map;
> +
> +	if (isvalid) {
> +		if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE ||
> +		    is_cipher_suite_wep(key->cipher))
> +			counter = 2;
> +		else
> +			counter = 19;
> +
> +		for (i = 0; i < counter; i++) {
> +			if (map[i].flag)
> +				continue;
> +
> +			map[i].flag = 1;
> +			memcpy(map[i].mac, peer_addr, ETH_ALEN);
> +			if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE ||
> +			    is_cipher_suite_wep(key->cipher))
> +				key->hw_key_idx = i + 6;
> +			else
> +				key->hw_key_idx = i + ifidx * 2 + 2;
> +			break;
> +		}
> +	} else {
> +		map[index].flag = 0;
> +		memset(map[index].mac, 0, ETH_ALEN);
> +
> +		if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE ||
> +		    is_cipher_suite_wep(key->cipher))
> +			index = key->hw_key_idx - 6;
> +		else
> +			index = key->hw_key_idx - 2 - ifidx * 2;
> +	}
> +
> +	if (key->hw_key_idx >= 6) {
> +		cnt1 = &epub->wl.ptk_cnt;
> +		cnt2 = &epub->wl.gtk_cnt;
> +	} else {
> +		cnt2 = &epub->wl.ptk_cnt;
> +		cnt1 = &epub->wl.gtk_cnt;
> +	}
> +
> +	/*send sub_scan task to target */
> +	if (isvalid)
> +		atomic_inc(cnt1);
> +	else
> +		atomic_dec(cnt1);
> +
> +	if (is_cipher_suite_wep(key->cipher)) {
> +		if (isvalid)
> +			atomic_inc(cnt2);
> +		else
> +			atomic_dec(cnt2);
> +	}
> +
> +	ret = sip_send_setkey(epub, ifidx, peer_addr, key, isvalid);
> +	if (ret)
> +		return ret;
> +
> +	if (key->cipher == WLAN_CIPHER_SUITE_TKIP && !ret)
> +		atomic_set(&epub->wl.tkip_key_set, 1);
> +
> +	return 0;
> +}
> +
> +void hw_scan_done(struct esp_pub *epub, bool aborted)
> +{
> +	struct cfg80211_scan_info info = {
> +		.aborted = aborted,
> +	};
> +
> +	cancel_delayed_work_sync(&epub->scan_timeout_work);
> +
> +	ESSERT(epub->wl.scan_req);
> +
> +	ieee80211_scan_completed(epub->hw, &info);
> +
> +	if (test_and_clear_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags))
> +		sip_trigger_txq_process(epub->sip);
> +}
> +
> +static void hw_scan_timeout_report(struct work_struct *work)
> +{
> +	struct esp_pub *epub = container_of(work, struct esp_pub,
> +					    scan_timeout_work.work);
> +	bool aborted;
> +	struct cfg80211_scan_info info = {};
> +
> +	if (test_and_clear_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags))
> +		sip_trigger_txq_process(epub->sip);
> +	/*check if normally complete or aborted like timeout/hw error */
> +	aborted = (epub->wl.scan_req != 0);
> +
> +	if (aborted)
> +		epub->wl.scan_req = NULL;
> +
> +	info.aborted = aborted;
> +
> +	ieee80211_scan_completed(epub->hw, &info);
> +}
> +
> +/*
> +   Configuration of RTS threshold (if device needs it)
> +   3106  *      The callback can sleep.
> +   */
> +static int esp_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
> +{
> +	return 0;
> +}
> +
> +static int esp_node_attach(struct ieee80211_hw *hw, u8 ifidx,
> +			   struct ieee80211_sta *sta)
> +{
> +	struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +	struct esp_node *node;
> +	struct esp_tx_tid *tid;
> +	u8 tidno = 0;
> +	int i;
> +
> +	spin_lock_bh(&epub->tx_ampdu_lock);
> +
> +	/* ffz(x) needs at least one zero or results in undefined behaviour. */
> +	if ((~epub->enodes_map) == 0)
> +		return -EINVAL;
> +
> +	i = ffz(epub->enodes_map);
> +
> +	if (hweight32(epub->enodes_maps[ifidx]) >= ESP_PUB_MAX_STA ||
> +	    i > ESP_PUB_MAX_STA) {
> +		i = -1;
> +		goto out;
> +	}
> +
> +	epub->enodes_map |= BIT(i);
> +	epub->enodes_maps[ifidx] |= BIT(i);
> +	node = (struct esp_node *)sta->drv_priv;
> +	epub->enodes[i] = node;
> +	node->sta = sta;
> +	node->ifidx = ifidx;
> +	node->index = i;
> +
> +	while (tidno < WME_NUM_TID) {
> +		tid = &node->tid[tidno];
> +		tid->ssn = 0;
> +		tid->cnt = 0;
> +		tid->state = ESP_TID_STATE_INIT;
> +		tidno++;
> +	}
> +
> +out:
> +	spin_unlock_bh(&epub->tx_ampdu_lock);
> +
> +	return i;
> +}
> +
> +static int esp_node_detach(struct ieee80211_hw *hw, u8 ifidx,
> +			   struct ieee80211_sta *sta)
> +{
> +	struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +	u32 map;
> +	int i;
> +
> +	spin_lock_bh(&epub->tx_ampdu_lock);
> +
> +	map = epub->enodes_maps[ifidx];
> +
> +	while (map) {
> +		i = ffs(map) - 1;
> +		if (epub->enodes[i]->sta == sta) {
> +			epub->enodes[i]->sta = NULL;
> +			epub->enodes[i] = NULL;
> +			epub->enodes_map &= ~BIT(i);
> +			epub->enodes_maps[ifidx] &= ~BIT(i);
> +
> +			goto out;
> +		}
> +
> +		map &= ~BIT(i);
> +	}
> +
> +	i = -1;
> +
> +out:
> +	spin_unlock_bh(&epub->tx_ampdu_lock);
> +
> +	return i;
> +}
> +
> +struct esp_node *esp_get_node_by_addr(struct esp_pub *epub, const u8 *addr)
> +{
> +	struct esp_node *node = NULL;
> +	int i;
> +	u32 map;
> +
> +	if (!addr)
> +		return NULL;
> +
> +	spin_lock_bh(&epub->tx_ampdu_lock);
> +	map = epub->enodes_map;
> +
> +	while (map) {
> +		i = ffs(map) - 1;
> +
> +		if (!memcmp(epub->enodes[i]->sta->addr, addr, ETH_ALEN)) {
> +			node = epub->enodes[i];
> +			goto out;
> +		}
> +
> +		map &= ~BIT(i);
> +	}
> +
> +out:
> +	spin_unlock_bh(&epub->tx_ampdu_lock);
> +
> +	return node;
> +}
> +
> +int esp_get_empty_rxampdu(struct esp_pub *epub, const u8 *addr, u8 tid)
> +{
> +	int index;
> +
> +	if (!addr)
> +		return -1;
> +
> +	spin_lock_bh(&epub->rx_ampdu_lock);
> +
> +	index = ffz(epub->rxampdu_map);
> +
> +	if (index >= ESP_PUB_MAX_RXAMPDU) {
> +		index = -1;
> +		goto out;
> +	}
> +
> +	epub->rxampdu_map |= BIT(index);
> +	epub->rxampdu_node[index] = esp_get_node_by_addr(epub, addr);
> +	epub->rxampdu_tid[index] = tid;
> +
> +out:
> +	spin_unlock_bh(&epub->rx_ampdu_lock);
> +
> +	return index;
> +}
> +
> +int esp_get_exist_rxampdu(struct esp_pub *epub, const u8 *addr, u8 tid)
> +{
> +	u8 map;
> +	int index;
> +
> +	if (!addr)
> +		return -1;
> +
> +	spin_lock_bh(&epub->rx_ampdu_lock);
> +	map = epub->rxampdu_map;
> +
> +	while (map) {
> +		index = ffs(map) - 1;
> +
> +		if (epub->rxampdu_tid[index] == tid &&
> +		    !memcmp(epub->rxampdu_node[index]->sta->addr, addr,
> +			    ETH_ALEN)) {
> +			epub->rxampdu_map &= ~BIT(index);
> +			goto out;
> +		}
> +
> +		map &= ~BIT(index);
> +	}
> +
> +	index = -1;
> +
> +out:
> +	spin_unlock_bh(&epub->rx_ampdu_lock);
> +	return index;
> +}
> +
> +/*
> +   Notifies low level driver about addition of an associated station,
> +   3109  *      AP, IBSS/WDS/mesh peer etc. This callback can sleep.
> +   */
> +static int esp_op_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
> +			  struct ieee80211_sta *sta)
> +{
> +	struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +	struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
> +	int index;
> +
> +	index = esp_node_attach(hw, evif->index, sta);
> +	if (index < 0)
> +		return index;
> +
> +	sip_send_set_sta(epub, evif->index, 1, sta, vif, (u8)index);
> +
> +	return 0;
> +}
> +
> +/*
> + Notifies low level driver about removal of an associated
> + 3112  *      station, AP, IBSS/WDS/mesh peer etc. Note that after the callback
> + 3113  *      returns it isn't safe to use the pointer, not even RCU protected;
> + 3114  *      no RCU grace period is guaranteed between returning here and freeing
> + 3115  *      the station. See @sta_pre_rcu_remove if needed.
> + 3116  *      This callback can sleep
> + */
> +static int esp_op_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
> +			     struct ieee80211_sta *sta)
> +{
> +	struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +	struct esp_vif *evif = (struct esp_vif *)vif->drv_priv;
> +	int index;
> +
> +	//remove a connect in target
> +	index = esp_node_detach(hw, evif->index, sta);

Why do we think "index" is not -1?

> +	sip_send_set_sta(epub, evif->index, 0, sta, vif, (u8)index);
> +
> +	return 0;
> +}
> +
> +/*
> + Notifies low level driver about power state transition of an
> + 3124  *      associated station, AP,  IBSS/WDS/mesh peer etc. For a VIF operating
> + 3125  *      in AP mode, this callback will not be called when the flag
> + 3126  *      %IEEE80211_HW_AP_LINK_PS is set. Must be atomic.
> + */
> +static void esp_op_sta_notify(struct ieee80211_hw *hw,
> +			      struct ieee80211_vif *vif,
> +			      enum sta_notify_cmd cmd,
> +			      struct ieee80211_sta *sta)
> +{
> +}
> +
> +/*
> + Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
> + 3165  *      bursting) for a hardware TX queue.
> + 3166  *      Returns a negative error code on failure.
> + 3167  *      The callback can sleep.
> + */
> +static int esp_op_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
> +			  u16 queue,
> +			  const struct ieee80211_tx_queue_params *params)
> +{
> +	struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +
> +	return sip_send_wmm_params(epub, queue, params);
> +}
> +
> +/*
> + Get the current TSF timer value from firmware/hardware. Currently,
> + 3170  *      this is only used for IBSS mode BSSID merging and debugging. Is not a
> + 3171  *      required function.
> + 3172  *      The callback can sleep.
> + */
> +static u64 esp_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
> +{
> +	return 0;
> +}
> +
> +/*
> + Set the TSF timer to the specified value in the firmware/hardware.
> + 3175  *      Currently, this is only used for IBSS mode debugging. Is not a
> + 3176  *      required function.
> + 3177  *      The callback can sleep.
> + */
> +static void esp_op_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
> +			   u64 tsf)
> +{
> +}
> +
> +/*
> + Reset the TSF timer and allow firmware/hardware to synchronize
> + 3186  *      with other STAs in the IBSS. This is only used in IBSS mode. This
> + 3187  *      function is optional if the firmware/hardware takes full care of
> + 3188  *      TSF synchronization.
> + 3189  *      The callback can sleep.
> + */
> +static void esp_op_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
> +{
> +}
> +
> +/*
> + Poll rfkill hardware state. If you need this, you also
> + 3220  *      need to set wiphy->rfkill_poll to %true before registration,
> + 3221  *      and need to call wiphy_rfkill_set_hw_state() in the callback.
> + 3222  *      The callback can sleep.
> + */
> +static void esp_op_rfkill_poll(struct ieee80211_hw *hw)
> +{
> +	struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +
> +	wiphy_rfkill_set_hw_state(hw->wiphy, test_bit(ESP_WL_FLAG_RFKILL,
> +						      &epub->wl.flags));
> +}
> +
> +#ifdef HW_SCAN
> +/*
> + Ask the hardware to service the scan request, no need to start
> + 3051  *      the scan state machine in stack. The scan must honour the channel
> + 3052  *      configuration done by the regulatory agent in the wiphy's
> + 3053  *      registered bands. The hardware (or the driver) needs to make sure
> + 3054  *      that power save is disabled.
> + 3055  *      The @req ie/ie_len members are rewritten by mac80211 to contain the
> + 3056  *      entire IEs after the SSID, so that drivers need not look at these
> + 3057  *      at all but just send them after the SSID -- mac80211 includes the
> + 3058  *      (extended) supported rates and HT information (where applicable).
> + 3059  *      When the scan finishes, ieee80211_scan_completed() must be called;
> + 3060  *      note that it also must be called when the scan cannot finish due to
> + 3061  *      any error unless this callback returned a negative error code.
> + 3062  *      The callback can sleep.
> + */
> +static int esp_op_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
> +			  struct cfg80211_scan_request *req)
> +{
> +	struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +	struct cfg80211_ssid *ssid2 = req->ssids + 1;
> +	int i, ret;
> +	bool scan_often;
> +
> +	/* scan_request is keep allocate until scan_done,record it
> +	 * to split request into multi sdio_cmd
> +	 */
> +	if (atomic_read(&epub->wl.off)) {
> +		dev_err(epub->dev, "hw_scan but wl off\n");
> +		return -EPERM;
> +	}
> +
> +	if (req->n_ssids > 1)
> +		if ((req->ssids->ssid_len > 0 && ssid2->ssid_len > 0) ||
> +		    req->n_ssids > 2) {
> +			dev_err(epub->dev, "cannot scan two SSIDs\n");
> +			return -EINVAL;
> +		}
> +
> +	epub->wl.scan_req = req;
> +
> +	/*in connect state, suspend tx data */
> +	if (epub->sip->support_bgscan &&
> +	    test_bit(ESP_WL_FLAG_CONNECT, &epub->wl.flags) && req->n_channels) {
> +		scan_often = epub->scan_permit_valid &&
> +			time_before(jiffies, epub->scan_permit);
> +		epub->scan_permit_valid = true;
> +
> +		if (!scan_often) {
> +			/* epub->scan_permit = jiffies + msecs_to_jiffies(900);
> +			 *  set_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags);
> +			 *  if (atomic_read(&epub->txq_stopped) == false) {
> +			 *  atomic_set(&epub->txq_stopped, true);
> +			 *  ieee80211_stop_queues(hw);
> +			 *  }
> +			 */
> +		} else {
> +			dev_err(epub->dev, "scan too often\n");
> +			return -EACCES;
> +		}
> +	} else {
> +		scan_often = false;
> +	}
> +
> +	/*send sub_scan task to target */
> +	ret = sip_send_scan(epub);
> +	if (ret) {
> +		dev_err(epub->dev, "failed to send scan_cmd: %d\n", ret);
> +		return ret;
> +	}
> +
> +	if (scan_often)
> +		return 0;
> +
> +	epub->scan_permit = jiffies + msecs_to_jiffies(900);
> +	set_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags);
> +	if (!atomic_read(&epub->txq_stopped)) {
> +		atomic_set(&epub->txq_stopped, true);
> +		ieee80211_stop_queues(hw);
> +	}
> +
> +	/*force scan complete in case target fail to report in time */
> +	ieee80211_queue_delayed_work(hw, &epub->scan_timeout_work,
> +				     req->n_channels * HZ / 4);
> +
> +	return 0;
> +}
> +
> +/*
> + Starts an off-channel period on the given channel, must
> + 3255  *      call back to ieee80211_ready_on_channel() when on that channel. Note
> + 3256  *      that normal channel traffic is not stopped as this is intended for hw
> + 3257  *      offload. Frames to transmit on the off-channel channel are transmitted
> + 3258  *      normally except for the %IEEE80211_TX_CTL_TX_OFFCHAN flag. When the
> + 3259  *      duration (which will always be non-zero) expires, the driver must call
> + 3260  *      ieee80211_remain_on_channel_expired().
> + 3261  *      Note that this callback may be called while the device is in IDLE and
> + 3262  *      must be accepted in this case.
> + 3263  *      This callback may sleep.
> + */
> +static int esp_op_remain_on_channel(struct ieee80211_hw *hw,
> +				    struct ieee80211_channel *chan,
> +				    enum nl80211_channel_type channel_type,
> +				    int duration)
> +{
> +	struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +
> +	sip_send_roc(epub, chan->center_freq, duration);
> +
> +	return 0;
> +}
> +
> +/*
> + 3264  * @cancel_remain_on_channel: Requests that an ongoing off-channel period is
> + 3265  *      aborted before it expires. This callback may sleep.
> + */
> +static int esp_op_cancel_remain_on_channel(struct ieee80211_hw *hw)
> +{
> +	struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +
> +	epub->roc_flags = 0;	// to disable roc state
> +	sip_send_roc(epub, 0, 0);
> +
> +	return 0;
> +}
> +#endif
> +
> +void esp_rocdone_process(struct ieee80211_hw *hw, struct sip_evt_roc *report)
> +{
> +	struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +
> +	if (report->is_ok != 1)
> +		return;
> +
> +	if (report->state == 1) {
> +		epub->roc_flags = 1;	//flags in roc state, to fix channel, not change
> +		ieee80211_ready_on_channel(hw);
> +	} else if (!report->state) {
> +		epub->roc_flags = 0;
> +		ieee80211_remain_on_channel_expired(hw);
> +	}
> +}
> +
> +/*
> + Set a mask of rates to be used for rate control selection
> + 3275  *      when transmitting a frame. Currently only legacy rates are handled.
> + 3276  *      The callback can sleep.
> + */
> +static int esp_op_set_bitrate_mask(struct ieee80211_hw *hw,
> +				   struct ieee80211_vif *vif,
> +				   const struct cfg80211_bitrate_mask *mask)
> +{
> +	return 0;
> +}
> +
> +/*
> + Flush all pending frames from the hardware queue, making sure
> + 3235  *      that the hardware queues are empty. The @queues parameter is a bitmap
> + 3236  *      of queues to flush, which is useful if different virtual interfaces
> + 3237  *      use different hardware queues; it may also indicate all queues.
> + 3238  *      If the parameter @drop is set to %true, pending frames may be dropped.
> + 3239  *      Note that vif can be NULL.
> + 3240  *      The callback can sleep.
> + */
> +void esp_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
> +		  u32 queues, bool drop)
> +{
> +	struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +	unsigned long time = jiffies + msecs_to_jiffies(15);
> +
> +	while (atomic_read(&epub->sip->tx_data_pkt_queued)) {
> +		if (!time_before(jiffies, time))
> +			break;
> +
> +		if (!sif_get_ate_config())
> +			ieee80211_queue_work(epub->hw, &epub->tx_work);
> +		else
> +			queue_work(epub->esp_wkq, &epub->tx_work);
> +	}
> +
> +	mdelay(10);
> +}
> +
> +/*
> + Perform a certain A-MPDU action
> + 3198  *      The RA/TID combination determines the destination and TID we want
> + 3199  *      the ampdu action to be performed for. The action is defined through
> + 3200  *      ieee80211_ampdu_mlme_action.
> + 3201  *      When the action is set to %IEEE80211_AMPDU_TX_OPERATIONAL the driver
> + 3202  *      may neither send aggregates containing more subframes than @buf_size
> + 3203  *      nor send aggregates in a way that lost frames would exceed the
> + 3204  *      buffer size. If just limiting the aggregate size, this would be
> + 3205  *      possible with a buf_size of 8:
> + 3206  *       - TX: 1.....7
> + 3207  *       - RX:  2....7 (lost frame #1)
> + 3208  *       - TX:        8..1...
> + 3209  *      which is invalid since #1 was now re-transmitted well past the
> + 3210  *      buffer size of 8. Correct ways to retransmit #1 would be:
> + 3211  *       - TX:       1 or 18 or 81
> + 3212  *      Even "189" would be wrong since 1 could be lost again.
> + 3213  *
> + 3214  *      Returns a negative error code on failure.
> + 3215  *      The callback can sleep.
> + */
> +static int esp_op_ampdu_action(struct ieee80211_hw *hw,
> +			       struct ieee80211_vif *vif,
> +			       struct ieee80211_ampdu_params *params)
> +{
> +	enum ieee80211_ampdu_mlme_action action = params->action;
> +	struct ieee80211_sta *sta = params->sta;
> +	struct esp_pub *epub = (struct esp_pub *)hw->priv;
> +	struct esp_node *node = (struct esp_node *)sta->drv_priv;
> +	struct cfg80211_chan_def *chandef;
> +	u16 tid = params->tid;
> +	struct esp_tx_tid *tid_info = &node->tid[tid];
> +	u16 *ssn = &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);

The caller expects a NULL return.

> +
> +	/* FIXME: useless if hw_scan is defined, incorrect if hw_scan is undefined*/
> +#ifdef HW_SCAN
> +	hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
> +#endif
> +
> +	epub = hw->priv;
> +	memset(epub, 0, sizeof(*epub));
> +
> +	epub->hw = hw;
> +	SET_IEEE80211_DEV(hw, dev);
> +	epub->dev = dev;
> +
> +	skb_queue_head_init(&epub->txq);
> +	skb_queue_head_init(&epub->txdoneq);
> +	skb_queue_head_init(&epub->rxq);
> +
> +	spin_lock_init(&epub->tx_ampdu_lock);
> +	spin_lock_init(&epub->rx_ampdu_lock);
> +	spin_lock_init(&epub->tx_lock);
> +	mutex_init(&epub->tx_mtx);
> +	spin_lock_init(&epub->rx_lock);
> +
> +	INIT_WORK(&epub->tx_work, esp_tx_work);
> +
> +	epub->esp_wkq = create_singlethread_workqueue("esp_wkq");
> +
> +	if (!epub->esp_wkq)
> +		return ERR_PTR(-ENOMEM);

goto free_hw;

> +
> +	epub->scan_permit_valid = false;
> +	INIT_DELAYED_WORK(&epub->scan_timeout_work, hw_scan_timeout_report);
> +
> +	return epub;
> +}
> +
> +int esp_pub_dealloc_mac80211(struct esp_pub *epub)
> +{
> +	set_bit(ESP_WL_FLAG_RFKILL, &epub->wl.flags);
> +
> +	destroy_workqueue(epub->esp_wkq);
> +	mutex_destroy(&epub->tx_mtx);
> +
> +	if (epub->hw)
> +		ieee80211_free_hw(epub->hw);
> +
> +	return 0;
> +}
> +
> +/* 2G band channels */
> +static struct ieee80211_channel esp_channels_2ghz[] = {
> +	{.hw_value = 1, .center_freq = 2412, .max_power = 25},
> +	{.hw_value = 2, .center_freq = 2417, .max_power = 25},
> +	{.hw_value = 3, .center_freq = 2422, .max_power = 25},
> +	{.hw_value = 4, .center_freq = 2427, .max_power = 25},
> +	{.hw_value = 5, .center_freq = 2432, .max_power = 25},
> +	{.hw_value = 6, .center_freq = 2437, .max_power = 25},
> +	{.hw_value = 7, .center_freq = 2442, .max_power = 25},
> +	{.hw_value = 8, .center_freq = 2447, .max_power = 25},
> +	{.hw_value = 9, .center_freq = 2452, .max_power = 25},
> +	{.hw_value = 10, .center_freq = 2457, .max_power = 25},
> +	{.hw_value = 11, .center_freq = 2462, .max_power = 25},
> +	{.hw_value = 12, .center_freq = 2467, .max_power = 25},
> +	{.hw_value = 13, .center_freq = 2472, .max_power = 25},
> +};
> +
> +/* 11G rate */
> +static struct ieee80211_rate esp_rates_2ghz[] = {
> +	{
> +		.bitrate = 10,
> +		.hw_value = CONF_HW_BIT_RATE_1MBPS,
> +		.hw_value_short = CONF_HW_BIT_RATE_1MBPS,
> +	},
> +	{
> +		.bitrate = 20,
> +		.hw_value = CONF_HW_BIT_RATE_2MBPS,
> +		.hw_value_short = CONF_HW_BIT_RATE_2MBPS,
> +		.flags = IEEE80211_RATE_SHORT_PREAMBLE},
> +	{
> +		.bitrate = 55,
> +		.hw_value = CONF_HW_BIT_RATE_5_5MBPS,
> +		.hw_value_short = CONF_HW_BIT_RATE_5_5MBPS,
> +		.flags = IEEE80211_RATE_SHORT_PREAMBLE},
> +	{
> +		.bitrate = 110,
> +		.hw_value = CONF_HW_BIT_RATE_11MBPS,
> +		.hw_value_short = CONF_HW_BIT_RATE_11MBPS,
> +		.flags = IEEE80211_RATE_SHORT_PREAMBLE},
> +	{
> +		.bitrate = 60,
> +		.hw_value = CONF_HW_BIT_RATE_6MBPS,
> +		.hw_value_short = CONF_HW_BIT_RATE_6MBPS,
> +	},
> +	{
> +		.bitrate = 90,
> +		.hw_value = CONF_HW_BIT_RATE_9MBPS,
> +		.hw_value_short = CONF_HW_BIT_RATE_9MBPS,
> +	},
> +	{
> +		.bitrate = 120,
> +		.hw_value = CONF_HW_BIT_RATE_12MBPS,
> +		.hw_value_short = CONF_HW_BIT_RATE_12MBPS,
> +	},
> +	{
> +		.bitrate = 180,
> +		.hw_value = CONF_HW_BIT_RATE_18MBPS,
> +		.hw_value_short = CONF_HW_BIT_RATE_18MBPS,
> +	},
> +	{
> +		.bitrate = 240,
> +		.hw_value = CONF_HW_BIT_RATE_24MBPS,
> +		.hw_value_short = CONF_HW_BIT_RATE_24MBPS,
> +	},
> +	{
> +		.bitrate = 360,
> +		.hw_value = CONF_HW_BIT_RATE_36MBPS,
> +		.hw_value_short = CONF_HW_BIT_RATE_36MBPS,
> +	},
> +	{
> +		.bitrate = 480,
> +		.hw_value = CONF_HW_BIT_RATE_48MBPS,
> +		.hw_value_short = CONF_HW_BIT_RATE_48MBPS,
> +	},
> +	{
> +		.bitrate = 540,
> +		.hw_value = CONF_HW_BIT_RATE_54MBPS,
> +		.hw_value_short = CONF_HW_BIT_RATE_54MBPS,
> +	},
> +};
> +
> +static struct ieee80211_sta_ht_cap esp_ht_cap_2ghz = {
> +	.cap = IEEE80211_HT_CAP_DSSSCCK40 | IEEE80211_HT_CAP_SM_PS |
> +		IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20,
> +	.ht_supported = true,
> +	.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
> +	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
> +	.mcs = {
> +		.rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
> +	},
> +};
> +
> +static void esp_pub_init_mac80211(struct esp_pub *epub)
> +{
> +	struct ieee80211_hw *hw = epub->hw;
> +	struct ieee80211_supported_band *sbands =
> +		&epub->wl.sbands[NL80211_BAND_2GHZ];
> +
> +	static const u32 cipher_suites[] = {
> +		WLAN_CIPHER_SUITE_WEP40,
> +		WLAN_CIPHER_SUITE_WEP104,
> +		WLAN_CIPHER_SUITE_TKIP,
> +		WLAN_CIPHER_SUITE_CCMP,
> +	};
> +
> +	hw->max_listen_interval = 10;
> +
> +	ieee80211_hw_set(hw, SIGNAL_DBM);
> +	ieee80211_hw_set(hw, HAS_RATE_CONTROL);
> +	ieee80211_hw_set(hw, SUPPORTS_PS);
> +	ieee80211_hw_set(hw, AMPDU_AGGREGATION);
> +	ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
> +	hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF;
> +	hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF;
> +
> +	hw->wiphy->cipher_suites = cipher_suites;
> +	hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
> +	hw->wiphy->max_scan_ie_len = epub->sip->tx_blksz -
> +		sizeof(struct sip_hdr) - sizeof(struct sip_cmd_scan);
> +
> +	/* ONLY station for now, support P2P soon... */
> +	/* FIXME: is p2p really supported? */
> +	hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_P2P_GO) |
> +		BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_STATION) |
> +		BIT(NL80211_IFTYPE_AP);
> +
> +	hw->wiphy->max_scan_ssids = 2;
> +	hw->wiphy->max_remain_on_channel_duration = 5000;
> +
> +	atomic_set(&epub->wl.off, 1);
> +
> +	sbands->band = NL80211_BAND_2GHZ;
> +	sbands->channels = esp_channels_2ghz;
> +	sbands->bitrates = esp_rates_2ghz;
> +	sbands->n_channels = ARRAY_SIZE(esp_channels_2ghz);
> +	sbands->n_bitrates = ARRAY_SIZE(esp_rates_2ghz);
> +	sbands->ht_cap = esp_ht_cap_2ghz;
> +
> +	hw->wiphy->bands[NL80211_BAND_2GHZ] = sbands;
> +
> +	/*no fragment */
> +	hw->wiphy->frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
> +
> +	/* handle AC queue in f/w */
> +	hw->queues = 4;
> +	hw->max_rates = 4;
> +
> +	hw->vif_data_size = sizeof(struct esp_vif);
> +	hw->sta_data_size = sizeof(struct esp_node);
> +}
> +
> +int esp_register_mac80211(struct esp_pub *epub)
> +{
> +	int ret, idx;
> +	u8 *p2p_addr;
> +
> +	esp_pub_init_mac80211(epub);
> +
> +	/* FIXME: esp_mac_addr */
> +	epub->hw->wiphy->addresses = (struct mac_address *)esp_mac_addr;
> +
> +	memcpy(epub->hw->wiphy->addresses[0].addr, epub->mac_addr, ETH_ALEN);
> +	memcpy(epub->hw->wiphy->addresses[1].addr, epub->mac_addr, ETH_ALEN);
> +	p2p_addr = epub->hw->wiphy->addresses[1].addr;
> +
> +	for (idx = 0; idx < 8 * ETH_ALEN; idx++) {
> +		p2p_addr[0] = epub->mac_addr[0] | 0x02;
> +		p2p_addr[0] ^= idx << 2;
> +		if (strncmp(p2p_addr, epub->mac_addr, 6))
> +			break;
> +	}
> +
> +	epub->hw->wiphy->n_addresses = 2;
> +
> +	ret = ieee80211_register_hw(epub->hw);
> +	if (ret < 0) {
> +		printk("unable to register mac80211 hw: %d\n", ret);
> +		return ret;
> +	}
> +
> +	set_bit(ESP_WL_FLAG_HW_REGISTERED, &epub->wl.flags);
> +
> +	return ret;

return 0;

> +}
> +
> +static u8 getaddr_index(u8 *addr, struct esp_pub *epub)
> +{
> +	int i;
> +
> +	for (i = 0; i < ESP_PUB_MAX_VIF; i++)
> +		if (!memcmp(addr, epub->hw->wiphy->addresses[i].addr, ETH_ALEN))
> +			return i;
> +
> +	return ESP_PUB_MAX_VIF;
> +}
> diff --git a/drivers/staging/esp8089/esp_mac80211.h b/drivers/staging/esp8089/esp_mac80211.h
> new file mode 100644
> index 000000000000..03de0b0a4223
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_mac80211.h
> @@ -0,0 +1,33 @@
> +/*
> + * Copyright (c) 2011 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + *      Quentin Schulz <quentin.schulz at 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 at 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) {

Drop the == 1.  We mostly treat sif_get_ate_config() as bool, so let's
do that consistently everywhere.

> +		ret = sip_poll_bootup_event(epub->sip);
> +	} else {
> +		ret = sip_poll_resetting_event(epub->sip);
> +		if (!ret) {
> +			sif_lock_bus(epub);
> +			sif_interrupt_target(epub, 7);
> +			sif_unlock_bus(epub);
> +		}
> +	}
> +
> +	gl_bootup_cplx = NULL;
> +
> +	if (sif_get_ate_config() == 1)
> +		ret = -EOPNOTSUPP;
> +
> +	return ret;
> +}
> +
> +void esp_dsr(struct esp_pub *epub)

Can this function be removed?

> +{
> +	sip_rx(epub);
> +}
> +
> +struct esp_fw_hdr {
> +	u8 magic;
> +	u8 blocks;
> +	u8 pad[2];
> +	u32 entry_addr;
> +} __packed;
> +
> +struct esp_fw_blk_hdr {
> +	u32 load_addr;
> +	u32 data_len;
> +} __packed;
> +
> +#define ESP_FW_NAME1 "eagle_fw_ate_config_v19.bin"
> +#define ESP_FW_NAME2 "eagle_fw_first_init_v19.bin"
> +#define ESP_FW_NAME3 "eagle_fw_second_init_v19.bin"
> +
> +static int esp_download_fw(struct esp_pub *epub)
> +{
> +	const struct firmware *fw_entry;
> +	struct esp_fw_hdr *fhdr;
> +	struct esp_fw_blk_hdr *bhdr;
> +	struct sip_cmd_bootup bootcmd;
> +	u8 *fw_buf;
> +	u32 offset;
> +	int ret;
> +	u8 blocks;
> +	char *esp_fw_name;
> +
> +	if (sif_get_ate_config() == 1)
> +		esp_fw_name = ESP_FW_NAME3;
> +	else
> +		esp_fw_name = epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT ? ESP_FW_NAME1 : ESP_FW_NAME2;
> +
> +	ret = request_firmware(&fw_entry, esp_fw_name, epub->dev);
> +	if (ret)
> +		return ret;
> +
> +	fw_buf = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL);
> +
> +	release_firmware(fw_entry);
> +
> +	if (!fw_buf)
> +		return -ENOMEM;
> +
> +	fhdr = (struct esp_fw_hdr *)fw_buf;
> +	if (fhdr->magic != 0xE9) {
> +		printk("%s wrong firmware magic!\n", __func__);
> +		goto _err;

Choose a label name based on what the goto does.  For example, here if
we hit the goto then we kfree(fw_buf); so "goto free_fw;" would be a
descriptive name.

Also don't forget to set the error code.

> +	}
> +
> +	blocks = fhdr->blocks;
> +	offset = sizeof(struct esp_fw_hdr);
> +
> +	while (blocks) {
> +		bhdr = (struct esp_fw_blk_hdr *)&fw_buf[offset];
> +		offset += sizeof(struct esp_fw_blk_hdr);
> +
> +		ret = sip_write_memory(epub->sip, bhdr->load_addr,
> +				       &fw_buf[offset], bhdr->data_len);
> +		if (ret) {
> +			printk("failed to write firmware: %d\n", ret);
> +			goto _err;
> +		}
> +
> +		blocks--;
> +		offset += bhdr->data_len;
> +	}
> +
> +	/* TODO: last byte should be the checksum and skip checksum for now */
> +
> +	bootcmd.boot_addr = fhdr->entry_addr;
> +	ret = sip_send_cmd(epub->sip, SIP_CMD_BOOTUP,
> +			   sizeof(struct sip_cmd_bootup), &bootcmd);
> +	if (ret)
> +		goto _err;
> +
> +_err:
> +	kfree(fw_buf);
> +
> +	return ret;
> +}
> +
> +MODULE_FIRMWARE(ESP_FW_NAME1);
> +MODULE_FIRMWARE(ESP_FW_NAME2);
> +MODULE_FIRMWARE(ESP_FW_NAME3);
> diff --git a/drivers/staging/esp8089/esp_pub.h b/drivers/staging/esp8089/esp_pub.h
> new file mode 100644
> index 000000000000..e329fb0999e9
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_pub.h
> @@ -0,0 +1,188 @@
> +/*
> + * Copyright (c) 2011 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + *      Quentin Schulz <quentin.schulz at 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 at 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 at 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;

Put a comment to describe what why we are returning a mix of 1, zero and
negative error codes.  It's impossible to tell from the context because
none of the callers check.

> +
> +	atomic_set(&sip->credit_status, RECALC_CREDIT_ENABLE);
> +	ret = sip_send_recalc_credit(sip->epub);
> +	if (ret) {
> +		dev_err(sip->epub->dev, "sending recalc credit failed: %d",
> +			ret);
> +		return ret;
> +	}
> +
> +	/*setup a timer for handle the abs_credit not receive */
> +	mod_timer(&sip->credit_timer, jiffies + msecs_to_jiffies(2000));
> +
> +	return ret;

return 0;

> +}
> +
> +static void sip_recalc_credit_release(struct esp_sip *sip)
> +{
> +	if (atomic_read(&sip->credit_status) == RECALC_CREDIT_ENABLE) {
> +		atomic_set(&sip->credit_status, RECALC_CREDIT_DISABLE);
> +		del_timer_sync(&sip->credit_timer);
> +	} else {
> +		dev_dbg(sip->epub->dev, "maybe bogus credit");
> +	}
> +}
> +
> +static void sip_update_tx_credits(struct esp_sip *sip, u16 recycled_credits)
> +{
> +	if (recycled_credits & 0x800) {
> +		atomic_set(&sip->tx_credits, (recycled_credits & 0x7ff));
> +		sip_recalc_credit_release(sip);
> +	} else {
> +		atomic_add(recycled_credits, &sip->tx_credits);
> +	}
> +}
> +
> +void sip_trigger_txq_process(struct esp_sip *sip)
> +{
> +	if (atomic_read(&sip->tx_credits) <= sip->credit_to_reserve + SIP_CTRL_CREDIT_RESERVE ||
> +	    atomic_read(&sip->credit_status) == RECALC_CREDIT_ENABLE)
> +		return;
> +
> +	if (sip_queue_may_resume(sip)) {
> +		/* wakeup upper queue only if we have sufficient credits */
> +		atomic_set(&sip->epub->txq_stopped, false);
> +		ieee80211_wake_queues(sip->epub->hw);
> +	} else if (atomic_read(&sip->epub->txq_stopped)) {
> +		dev_err(sip->epub->dev, "%s can't wake txq, credits: %d\n",
> +			__func__, atomic_read(&sip->tx_credits));
> +	}
> +
> +	if (!skb_queue_empty(&sip->epub->txq)) {
> +		/* try to send out pkt already in sip queue once we have credits */
> +#if !defined(FPGA_TXDATA)
> +		if (!sif_get_ate_config())
> +			ieee80211_queue_work(sip->epub->hw,
> +					     &sip->epub->tx_work);
> +		else
> +			queue_work(sip->epub->esp_wkq, &sip->epub->tx_work);
> +#else
> +		queue_work(sip->epub->esp_wkq, &sip->epub->tx_work);
> +#endif
> +	}
> +}
> +
> +static bool sip_ampdu_occupy_buf(struct esp_sip *sip,
> +				 struct esp_rx_ampdu_len *ampdu_len)
> +{
> +	return (!ampdu_len->substate ||
> +		esp_wmac_rxsec_error(ampdu_len->substate) ||
> +		(sip->dump_rpbm_err && ampdu_len->substate == RX_RPBM_ERR));
> +}
> +
> +/* TODO: HARDCORE CLEANUP */
> +
> +static bool sip_rx_pkt_process(struct esp_sip *sip, struct sk_buff *skb)
> +{
> +	struct sip_hdr *hdr;
> +	struct sk_buff *rskb;
> +	struct esp_mac_rx_ctrl *mac_ctrl, new_mac_ctrl;
> +	int remains_len, first_pkt_len, ret = 0, pkt_len_enc = 0, buf_len = 0,
> +	    pulled_len = 0, pkt_num;
> +	u8 *bufptr;
> +	bool trigger_rxq = false, have_rxabort = false, have_goodpkt = false;
> +	struct esp_rx_ampdu_len *ampdu_len;
> +	static int pkt_dropped, pkt_total;
> +	static u8 frame_head[16];
> +	static u8 frame_buf_ttl;
> +
> +	if (!skb)
> +		return trigger_rxq;
> +
> +	hdr = (struct sip_hdr *)skb->data;
> +	bufptr = skb->data;
> +
> +	if (hdr->h_credits & SIP_CREDITS_MASK)
> +		sip_update_tx_credits(sip, hdr->h_credits & SIP_CREDITS_MASK);
> +
> +	hdr->h_credits &= ~SIP_CREDITS_MASK;	/* clean credits in sip_hdr, prevent over-add */
> +
> +	/* first pkt's length is stored in  recycled_credits first 20 bits
> +	 * config w3 [31:12]
> +	 * repair hdr->len of first pkt
> +	 */
> +	remains_len = hdr->len;
> +	first_pkt_len = hdr->h_credits >> 12;
> +	hdr->len = first_pkt_len;
> +
> +	if (first_pkt_len > remains_len) {
> +		sip_recalc_credit_claim(sip, 0);
> +		show_buf((u8 *)hdr, first_pkt_len);

This feels like an info leak.  first_pkt_len comes from the network.

> +		ESSERT(0);
> +		goto _exit;
> +	}
> +
> +	/* pkts handling, including the first pkt, should alloc new skb for each data pkt.
> +	 * free the original whole skb after parsing is done.
> +	 */
> +	while (remains_len) {
> +		if (remains_len < sizeof(struct sip_hdr)) {
> +			sip_recalc_credit_claim(sip, 0);
> +			ESSERT(0);
> +			show_buf((u8 *)hdr, 512);
> +			goto _exit;
> +		}
> +
> +		hdr = (struct sip_hdr *)bufptr;
> +		if (hdr->len <= 0 || hdr->len & 3) {
> +			sip_recalc_credit_claim(sip, 0);
> +			show_buf((u8 *)hdr, 512);
> +			ESSERT(0);
> +			goto _exit;
> +		}
> +
> +		if (unlikely(hdr->seq != sip->rxseq++)) {
> +			sip_recalc_credit_claim(sip, 0);
> +			dev_dbg(sip->epub->dev,
> +				"%s seq mismatch! got %u, expect %u\n",
> +				__func__, hdr->seq, sip->rxseq - 1);
> +			sip->rxseq = hdr->seq + 1;
> +			show_buf(bufptr, 32);
> +			ESSERT(0);
> +		}
> +
> +		if (SIP_HDR_IS_CTRL(hdr)) {
> +			STRACE_RX_EVENT_INC();
> +
> +			ret = sip_parse_events(sip, bufptr);
> +			skb_pull(skb, hdr->len);
> +		} else if (SIP_HDR_IS_DATA(hdr)) {
> +			STRACE_RX_DATA_INC();
> +			mac_ctrl = sip_parse_normal_mac_ctrl(skb, &pkt_len_enc,
> +							     &buf_len,
> +							     &pulled_len);
> +			rskb = sip_parse_data_rx_info(sip, skb, pkt_len_enc,
> +						      buf_len, mac_ctrl,
> +						      &pulled_len);
> +
> +			if (!rskb)
> +				goto _move_on;
> +
> +			if (likely(atomic_read(&sip->epub->wl.off) == 0)) {
> +				local_bh_disable();
> +				ieee80211_rx(sip->epub->hw, rskb);
> +				local_bh_enable();
> +			} else {
> +				/* still need go thro parsing as skb_pull should invoke */
> +				kfree_skb(rskb);
> +			}
> +		} else if (SIP_HDR_IS_AMPDU(hdr)) {
> +			ampdu_len = (struct esp_rx_ampdu_len *)(skb->data + hdr->len / sip->rx_blksz * sip->rx_blksz);
> +			dev_dbg(sip->epub->dev, "%s rx ampdu total len %u\n",
> +				    __func__, hdr->len);
> +			if (skb->data != (u8 *)hdr) {
> +				printk("%p %p\n", skb->data, hdr);
> +				show_buf(skb->data, 512);
> +				show_buf((u8 *)hdr, 512);
> +				ESSERT(0);
> +				goto _exit;
> +			}
> +			mac_ctrl = sip_parse_normal_mac_ctrl(skb, NULL, NULL,
> +							     &pulled_len);
> +			memcpy(&new_mac_ctrl, mac_ctrl,
> +			       sizeof(struct esp_mac_rx_ctrl));
> +			mac_ctrl = &new_mac_ctrl;
> +			pkt_num = mac_ctrl->ampdu_cnt;
> +			dev_dbg(sip->epub->dev,
> +				"%s %d rx ampdu %u pkts, %d pkts dumped, first len %u\n",
> +				__func__, __LINE__,
> +				(unsigned int)((hdr->len % sip->rx_blksz) /
> +					       sizeof(struct esp_rx_ampdu_len)),
> +				pkt_num, (unsigned int)ampdu_len->sublen);
> +
> +			pkt_total += mac_ctrl->ampdu_cnt;
> +			while (pkt_num > 0) {
> +				dev_dbg(sip->epub->dev,
> +					"%s %d ampdu sub state %02x,\n",
> +					__func__, __LINE__,
> +					ampdu_len->substate);
> +
> +				if (sip_ampdu_occupy_buf(sip, ampdu_len)) {	//pkt is dumped
> +					rskb = sip_parse_data_rx_info(sip, skb,
> +								      ampdu_len->sublen - FCS_LEN,
> +								      0,
> +								      mac_ctrl,
> +								      &pulled_len);
> +					if (!rskb) {
> +						ESSERT(0);
> +						goto _exit;
> +					}
> +
> +					if (likely(atomic_read(&sip->epub->wl.off) == 0) &&
> +					    (ampdu_len->substate == 0 ||
> +					     ampdu_len->substate == RX_TKIPMIC_ERR ||
> +					     (sip->sendup_rpbm_pkt &&
> +					      ampdu_len->substate == RX_RPBM_ERR)) &&
> +					    (sip->rxabort_fixed ||
> +					     !have_rxabort)) {
> +						if (!have_goodpkt) {
> +							have_goodpkt = true;
> +							memcpy(frame_head,
> +							       rskb->data,
> +							       16);
> +							frame_head[1] &= ~0x80;
> +							frame_buf_ttl = 3;
> +						}
> +						local_bh_disable();
> +						ieee80211_rx(sip->epub->hw,
> +							     rskb);
> +						local_bh_enable();
> +					} else {
> +						kfree_skb(rskb);
> +					}
> +				} else {
> +					if (ampdu_len->substate == RX_ABORT) {
> +						u8 *a;
> +						have_rxabort = true;
> +						dev_dbg(sip->epub->dev,
> +							"rx abort %d %d\n",
> +							frame_buf_ttl, pkt_num);
> +						if (frame_buf_ttl &&
> +						    !sip->rxabort_fixed) {
> +							struct esp_rx_ampdu_len *next_good_ampdu_len = ampdu_len + 1;
> +							a = frame_head;
> +							while (!sip_ampdu_occupy_buf(sip, next_good_ampdu_len)) {
> +								if (next_good_ampdu_len > ampdu_len + pkt_num - 1)
> +									break;
> +								next_good_ampdu_len++;
> +							}
> +							if (next_good_ampdu_len <= ampdu_len + pkt_num - 1) {
> +								bool b0, b10, b11;
> +								a = skb->data;
> +								b0 = memcmp(frame_head + 4, skb->data + 4, 12) == 0;
> +								b10 = memcmp(frame_head + 10, skb->data, 6) == 0;
> +								b11 = memcpy(frame_head + 11, skb->data, 5) == 0;
> +								if (b0 && !b10 &&
> +								    !b11) {
> +									have_rxabort = false;
> +								} else if (!b0 &&
> +									   b10 &&
> +									   !b11) {
> +									skb_push(skb, 10);
> +									memcpy(skb->data,
> +									       frame_head,
> +									       10);
> +									have_rxabort = false;
> +									pulled_len -= 10;
> +								} else if (!b0 && !b10 && b11) {
> +									skb_push(skb, 11);
> +									memcpy(skb->data,
> +									       frame_head,
> +									       11);
> +									have_rxabort = false;
> +									pulled_len -= 11;
> +								}
> +							}
> +						}
> +					}
> +					pkt_dropped++;
> +					dev_dbg(sip->epub->dev,
> +						"%s ampdu dropped %d/%d\n",
> +						__func__, pkt_dropped,
> +						pkt_total);
> +				}
> +				pkt_num--;
> +				ampdu_len++;
> +			}
> +			if (frame_buf_ttl)
> +				frame_buf_ttl--;
> +			skb_pull(skb, hdr->len - pulled_len);
> +		} else {
> +			dev_err(sip->epub->dev, "unknown SIP HDR type\n");
> +		}
> +
> +_move_on:
> +		if (hdr->len < remains_len)
> +			remains_len -= hdr->len;
> +		else
> +			break;
> +		bufptr += hdr->len;
> +	}
> +
> +_exit:
> +	kfree_skb(skb);
> +
> +	return trigger_rxq;
> +}
> +
> +static void _sip_rxq_process(struct esp_sip *sip)
> +{
> +	struct sk_buff *skb;
> +	bool sendup = false;
> +
> +	while ((skb = skb_dequeue(&sip->rxq)))
> +		if (sip_rx_pkt_process(sip, skb))
> +			sendup = true;
> +
> +	if (sendup)
> +		queue_work(sip->epub->esp_wkq, &sip->epub->sendup_work);
> +
> +	/* probably tx_credit is updated, try txq */
> +	sip_trigger_txq_process(sip);
> +}
> +
> +void sip_rxq_process(struct work_struct *work)
> +{
> +	struct esp_sip *sip = container_of(work, struct esp_sip,
> +					   rx_process_work);
> +
> +	if (!sip) {
> +		ESSERT(0);
> +		return;
> +	}
> +
> +	if (unlikely(atomic_read(&sip->state) == SIP_SEND_INIT)) {
> +		sip_send_chip_init(sip);
> +		atomic_set(&sip->state, SIP_WAIT_BOOTUP);
> +		return;
> +	}
> +
> +	mutex_lock(&sip->rx_mtx);
> +	_sip_rxq_process(sip);
> +	mutex_unlock(&sip->rx_mtx);
> +}
> +
> +static inline void sip_rx_pkt_enqueue(struct esp_sip *sip, struct sk_buff *skb)
> +{
> +	skb_queue_tail(&sip->rxq, skb);
> +}
> +
> +static u32 sip_rx_count;
> +
> +int sip_rx(struct esp_pub *epub)
> +{
> +	struct sip_hdr *shdr;
> +	struct esp_sip *sip = epub->sip;
> +	struct sk_buff *first_skb, *rx_skb;
> +	u8 *rx_buf;
> +	u32 rx_blksz, first_sz;
> +	int err;
> +	u8 raw_seq;
> +
> +	if (likely(sif_get_ate_config() != 1)) {
> +		raw_seq = sif_get_regs(epub)->intr_raw & 0xff;
> +
> +		if (raw_seq != sip->to_host_seq) {
> +			if (raw_seq == sip->to_host_seq + 1) {	/* when last read pkt crc err, this situation may occur, but raw_seq mustn't < to_host_Seq */
> +				sip->to_host_seq = raw_seq;
> +				dev_dbg(epub->dev,
> +					"warn: to_host_seq reg 0x%02x, seq 0x%02x",
> +					raw_seq, sip->to_host_seq);
> +			} else {
> +				dev_err(epub->dev,
> +					"err: to_host_seq reg 0x%02x, seq 0x%02x",
> +					raw_seq, sip->to_host_seq);
> +				err = 0;
> +				goto _err;

This function uses a mix of direct returns and goto _err.  Just use
direct returns unless there is a good reason in the present (not future)
for the goto.  It just hurts readability to have to scroll 2 pages down
to see what the goto does.  "return 0;" is much quicker.

> +			}
> +		}
> +	}
> +
> +	/* first read one block out, if we luck enough, that's it
> +	 *
> +	 *  To make design as simple as possible, we allocate skb(s)
> +	 *  separately for each sif read operation to avoid global
> +	 *  read_buf_pointe access.  It coule be optimized late.
> +	 */
> +
> +	first_sz = sif_get_regs(epub)->config_w0;
> +	rx_blksz = sif_get_blksz(epub);
> +	first_skb = __dev_alloc_skb(roundup(first_sz, rx_blksz), GFP_KERNEL);
> +
> +	if (!first_skb) {
> +		sif_unlock_bus(epub);
> +		err = 0;
> +		goto _err;
> +	}
> +
> +	rx_buf = skb_put(first_skb, first_sz);
> +
> +	err = esp_common_read(epub, rx_buf, first_sz, ESP_SIF_NOSYNC, false);
> +	sip_rx_count++;
> +	if (unlikely(err)) {
> +		dev_err(epub->dev, " first read err %d %d\n", err,
> +			sif_get_regs(epub)->config_w0);
> +		kfree_skb(first_skb);
> +		sif_unlock_bus(epub);
> +		goto _err;


I feel like we should create a error goto the unlocks and frees
first_skb.

> +	}
> +
> +	shdr = (struct sip_hdr *)rx_buf;
> +	if (SIP_HDR_IS_CTRL(shdr) && shdr->c_evtid == SIP_EVT_SLEEP) {
> +		atomic_set(&sip->epub->ps.state, ESP_PM_ON);
> +	}
> +
> +	if (likely(sif_get_ate_config() != 1))
> +		sip->to_host_seq++;
> +
> +	if (shdr->len & 3) {
> +		dev_err(epub->dev, "shdr->len[%d] error\n", shdr->len);
> +		kfree_skb(first_skb);
> +		sif_unlock_bus(epub);
> +		err = -EIO;
> +		goto _err;
> +	}
> +
> +	if (shdr->len != first_sz) {
> +		dev_err(epub->dev, "shdr->len[%d]  first_size[%d] error\n",
> +			shdr->len, first_sz);
> +		kfree_skb(first_skb);
> +		sif_unlock_bus(epub);
> +		err = -EIO;
> +		goto _err;
> +	} else {
> +		sif_unlock_bus(epub);
> +		skb_trim(first_skb, shdr->len);
> +		dev_dbg(epub->dev, "first_skb only\n");
> +
> +		rx_skb = first_skb;
> +	}
> +
> +	if (atomic_read(&sip->state) == SIP_STOP) {
> +		kfree_skb(rx_skb);
> +		dev_err(epub->dev, "rx packet while sip stopped\n");

We don't unlock on this path?

> +		return 0;
> +	}
> +
> +	sip_rx_pkt_enqueue(sip, rx_skb);
> +	queue_work(sip->epub->esp_wkq, &sip->rx_process_work);
> +
> +_err:
> +	return err;
> +}
> +
> +int sip_post_init(struct esp_sip *sip, struct sip_evt_bootup2 *bevt)
> +{
> +	struct esp_pub *epub;
> +	u8 mac_id = bevt->mac_addr[0];
> +	int mac_index = 0, i;
> +
> +	if (!sip) {
> +		ESSERT(0);
> +		return -EINVAL;
> +	}
> +
> +	epub = sip->epub;
> +
> +	sip->tx_aggr_write_ptr = sip->tx_aggr_buf;
> +	sip->tx_blksz = bevt->tx_blksz;
> +	sip->rx_blksz = bevt->rx_blksz;
> +	sip->credit_to_reserve = bevt->credit_to_reserve;
> +	sip->dump_rpbm_err = bevt->options & SIP_DUMP_RPBM_ERR;
> +	sip->rxabort_fixed = bevt->options & SIP_RXABORT_FIXED;
> +	sip->support_bgscan = bevt->options & SIP_SUPPORT_BGSCAN;
> +	sip->sendup_rpbm_pkt = 0;
> +
> +	/* print out MAC addr... */
> +	memcpy(epub->mac_addr, bevt->mac_addr, ETH_ALEN);
> +	for (i = 0;
> +	     i < sizeof(esp_mac_prefix_table) / sizeof(struct esp_mac_prefix);
> +	     i++)
> +		if (esp_mac_prefix_table[i].mac_index == mac_id) {
> +			mac_index = i;
> +			break;
> +		}
> +
> +	epub->mac_addr[0] = esp_mac_prefix_table[mac_index].mac_addr_prefix[0];
> +	epub->mac_addr[1] = esp_mac_prefix_table[mac_index].mac_addr_prefix[1];
> +	epub->mac_addr[2] = esp_mac_prefix_table[mac_index].mac_addr_prefix[2];
> +
> +	atomic_set(&sip->noise_floor, bevt->noise_floor);
> +
> +	sip_recalc_credit_init(sip);
> +
> +	return 0;
> +}
> +
> +/* write pkts in aggr buf to target memory */
> +static void sip_write_pkts(struct esp_sip *sip, int pm_state)
> +{
> +	int tx_aggr_len, err;
> +	struct sip_hdr *first_shdr;
> +
> +	tx_aggr_len = sip->tx_aggr_write_ptr - sip->tx_aggr_buf;
> +	if (tx_aggr_len < sizeof(struct sip_hdr)) {
> +		dev_err(sip->epub->dev, "[tx_aggr_len] %d < sizeof(sip_hdr)\n",
> +			tx_aggr_len);
> +		ESSERT(0);
> +		return;
> +	}
> +
> +	if (tx_aggr_len & 0x3) {
> +		ESSERT(0);
> +		return;
> +	}
> +
> +	first_shdr = (struct sip_hdr *)sip->tx_aggr_buf;
> +
> +	if (atomic_read(&sip->tx_credits) <= SIP_CREDITS_LOW_THRESHOLD)
> +		first_shdr->fc[1] |= SIP_HDR_F_NEED_CRDT_RPT;
> +
> +	/* still use lock bus instead of sif_lldesc_write_sync since we want to protect several global varibles assignments */
> +	sif_lock_bus(sip->epub);
> +
> +	err = esp_common_write(sip->epub, sip->tx_aggr_buf, tx_aggr_len,
> +			       ESP_SIF_NOSYNC);
> +	if (err)
> +		dev_err(sip->epub->dev, "error while writing pkts: %d\n", err);
> +
> +	sip->tx_aggr_write_ptr = sip->tx_aggr_buf;
> +	sip->tx_tot_len = 0;
> +
> +	sif_unlock_bus(sip->epub);
> +}
> +
> +/* setup sip header and tx info, copy pkt into aggr buf */
> +static int sip_pack_pkt(struct esp_sip *sip, struct sk_buff *skb, int *pm_state)
> +{
> +	struct ieee80211_tx_info *itx_info;
> +	struct sip_hdr *shdr;
> +	struct ieee80211_hdr *wh;
> +	struct esp_vif *evif;
> +	struct esp_node *node;
> +	u32 tx_len, offset;
> +	bool is_data = true;
> +	u8 sta_index;
> +	int alg;
> +
> +	itx_info = IEEE80211_SKB_CB(skb);
> +	if (itx_info->flags == 0xffffffff) {
> +		shdr = (struct sip_hdr *)skb->data;
> +		is_data = false;
> +		tx_len = skb->len;
> +	} else {
> +		wh = (struct ieee80211_hdr *)skb->data;
> +		evif = (struct esp_vif *)itx_info->control.vif->drv_priv;
> +		/* update sip header */
> +		shdr = (struct sip_hdr *)sip->tx_aggr_write_ptr;
> +
> +		shdr->fc[0] = 0;
> +		shdr->fc[1] = 0;
> +
> +		if (itx_info->flags & IEEE80211_TX_CTL_AMPDU)
> +			SIP_HDR_SET_TYPE(shdr->fc[0], SIP_DATA_AMPDU);
> +		else
> +			SIP_HDR_SET_TYPE(shdr->fc[0], SIP_DATA);
> +
> +		if (!evif->epub) {
> +			sip_tx_status_report(sip, skb, itx_info, false);
> +			atomic_dec(&sip->tx_data_pkt_queued);
> +			return -EINVAL;
> +		}
> +
> +		/* make room for encrypted pkt */
> +		if (itx_info->control.hw_key) {
> +			alg = esp_cipher2alg(itx_info->control.hw_key->cipher);
> +			if (unlikely(alg == -1)) {
> +				sip_tx_status_report(sip, skb, itx_info, false);
> +				atomic_dec(&sip->tx_data_pkt_queued);
> +				return -1;
> +			}
> +
> +			shdr->d_enc_flag = alg + 1;
> +			shdr->d_hw_kid = itx_info->control.hw_key->hw_key_idx |
> +				(evif->index << 7);
> +		} else {
> +			shdr->d_enc_flag = 0;
> +			shdr->d_hw_kid = evif->index << 7 | evif->index;
> +		}
> +
> +		/* update sip tx info */
> +		node = esp_get_node_by_addr(sip->epub, wh->addr1);
> +		if (node)
> +			sta_index = node->index;
> +		else
> +			sta_index = ESP_PUB_MAX_STA + 1;
> +
> +		SIP_HDR_SET_IFIDX(shdr->fc[0], evif->index << 3 | sta_index);
> +		shdr->d_p2p = itx_info->control.vif->p2p;
> +
> +		if (evif->index == 1)
> +			shdr->d_p2p = 1;
> +
> +		shdr->d_ac = skb_get_queue_mapping(skb);
> +		shdr->d_tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
> +
> +		wh = (struct ieee80211_hdr *)skb->data;
> +
> +		if (ieee80211_is_mgmt(wh->frame_control)) {
> +			/* addba/delba/bar may use different tid/ac */
> +			if (shdr->d_ac == WME_AC_VO)
> +				shdr->d_tid = 7;
> +
> +			if (ieee80211_is_beacon(wh->frame_control)) {
> +				shdr->d_tid = 8;
> +				shdr->d_ac = 4;
> +			}
> +		}
> +
> +		if (check_ac_tid(skb->data, shdr->d_ac, shdr->d_tid)) {
> +			shdr->d_ac = WME_AC_BE;
> +			shdr->d_tid = 0;
> +		}
> +
> +		/* make sure data is start at 4 bytes aligned addr. */
> +		offset = roundup(sizeof(struct sip_hdr), 4);
> +
> +		if (SIP_HDR_IS_AMPDU(shdr)) {
> +			memset(sip->tx_aggr_write_ptr + offset, 0,
> +			       sizeof(struct esp_tx_ampdu_entry));
> +			offset += roundup(sizeof(struct esp_tx_ampdu_entry), 4);
> +		}
> +
> +		tx_len = offset + skb->len;
> +		shdr->len = tx_len;	/* actual len */
> +	}
> +
> +	shdr->seq = sip->txseq++;
> +
> +	/* copy skb to aggr buf */
> +	memcpy(sip->tx_aggr_write_ptr + offset, skb->data, skb->len);
> +
> +	if (is_data) {
> +		spin_lock_bh(&sip->epub->tx_lock);
> +		sip->txdataseq = shdr->seq;
> +		spin_unlock_bh(&sip->epub->tx_lock);
> +
> +		/* fake a tx_status and report to mac80211 stack to speed up tx, may affect
> +		 *  1) rate control (now it's all in target, so should be OK)
> +		 *  2) ps mode, mac80211 want to check ACK of ps/nulldata to see if AP is awake
> +		 *  3) BAR, mac80211 do BAR by checking ACK
> +		 *
> +		 *  XXX: need to adjust for 11n, e.g. report tx_status according to BA received in target
> +		 */
> +		sip_tx_status_report(sip, skb, itx_info, true);
> +		atomic_dec(&sip->tx_data_pkt_queued);
> +
> +		STRACE_TX_DATA_INC();
> +	} else {
> +		/* check pm state here */
> +
> +		/* no need to hold ctrl skb */
> +		sip_free_ctrl_skbuff(sip, skb);
> +		STRACE_TX_CMD_INC();
> +	}
> +
> +	/* TBD: roundup here or whole aggr-buf */
> +	tx_len = roundup(tx_len, sip->tx_blksz);
> +
> +	sip->tx_aggr_write_ptr += tx_len;
> +	sip->tx_tot_len += tx_len;
> +
> +	return 0;
> +}
> +
> +static void sip_tx_status_report(struct esp_sip *sip, struct sk_buff *skb,
> +				 struct ieee80211_tx_info *tx_info,
> +				 bool success)
> +{
> +	struct ieee80211_hdr *wh;
> +	struct esp_node *node;
> +	struct esp_tx_tid *tid;
> +	struct ieee80211_sta *sta;
> +	u8 tidno;
> +
> +	if (!(tx_info->flags & IEEE80211_TX_CTL_AMPDU)) {
> +		if (likely(success))
> +			tx_info->flags |= IEEE80211_TX_STAT_ACK;
> +		else
> +			tx_info->flags &= ~IEEE80211_TX_STAT_ACK;
> +
> +		/* manipulate rate status... */
> +		tx_info->status.rates[0].idx = 11;
> +		tx_info->status.rates[0].count = 1;
> +		tx_info->status.rates[0].flags = 0;
> +		tx_info->status.rates[1].idx = -1;
> +	} else {
> +		tx_info->flags |= IEEE80211_TX_STAT_AMPDU |
> +			IEEE80211_TX_STAT_ACK;
> +		tx_info->status.ampdu_len = 1;
> +		tx_info->status.ampdu_ack_len = 1;
> +
> +		/* manipulate rate status... */
> +		tx_info->status.rates[0].idx = 7;
> +		tx_info->status.rates[0].count = 1;
> +		tx_info->status.rates[0].flags = IEEE80211_TX_RC_MCS |
> +			IEEE80211_TX_RC_SHORT_GI;
> +		tx_info->status.rates[1].idx = -1;
> +	}
> +
> +	if (!mod_support_no_txampdu() &&
> +	    cfg80211_get_chandef_type(&sip->epub->hw->conf.chandef) != NL80211_CHAN_NO_HT) {
> +		wh = (struct ieee80211_hdr *)skb->data;
> +
> +		if (ieee80211_is_data_qos(wh->frame_control) &&
> +		    !(IEEE80211_SKB_CB(skb)->flags & IEEE80211_TX_CTL_AMPDU)) {
> +			tidno = ieee80211_get_qos_ctl(wh)[0] &
> +				IEEE80211_QOS_CTL_TID_MASK;
> +
> +			node = esp_get_node_by_addr(sip->epub, wh->addr1);
> +			if (!node || !node->sta)
> +				goto _exit;
> +
> +			sta = node->sta;
> +			tid = &node->tid[tidno];
> +			if (!tid) {
> +				ESSERT(0);
> +				goto _exit;
> +			}
> +
> +			spin_lock_bh(&sip->epub->tx_ampdu_lock);
> +
> +			if (tid->state == ESP_TID_STATE_INIT &&
> +			    TID_TO_AC(tidno) != WME_AC_VO && tid->cnt >= 10) {
> +				tid->state = ESP_TID_STATE_TRIGGER;
> +				dev_dbg(sip->epub->dev,
> +					"start tx ba session,addr:%pM,tid:%u\n",
> +					wh->addr1, tidno);
> +				spin_unlock_bh(&sip->epub->tx_ampdu_lock);
> +				ieee80211_start_tx_ba_session(sta, tidno, 0);
> +			} else {
> +				if (tid->state == ESP_TID_STATE_INIT)
> +					tid->cnt++;
> +				else
> +					tid->cnt = 0;
> +
> +				spin_unlock_bh(&sip->epub->tx_ampdu_lock);
> +			}
> +		}
> +	}
> +
> +_exit:
> +	ieee80211_tx_status(sip->epub->hw, skb);
> +}
> +
> +/*  NB: this routine should be locked when calling
> + */
> +void sip_txq_process(struct esp_pub *epub)
> +{
> +	struct sk_buff *skb;
> +	struct sip_hdr *hdr;
> +	struct esp_sip *sip = epub->sip;
> +	struct ieee80211_tx_info *itx_info;
> +	u32 pkt_len, tx_len = 0;
> +	int blknum = 0, pm_state = 0;
> +	bool queued_back = false, out_of_credits = false;
> +
> +	while ((skb = skb_dequeue(&epub->txq))) {
> +		/* cmd skb->len does not include sip_hdr too */
> +		pkt_len = skb->len;
> +		itx_info = IEEE80211_SKB_CB(skb);
> +		if (itx_info->flags != 0xffffffff) {
> +			pkt_len += roundup(sizeof(struct sip_hdr), 4);
> +			if (itx_info->flags & IEEE80211_TX_CTL_AMPDU)
> +				pkt_len += roundup(sizeof(struct esp_tx_ampdu_entry),
> +						   4);
> +		}
> +
> +		/* current design simply requires every sip_hdr must be at the begin of mblk, that definitely
> +		 * need to be optimized, e.g. calculate remain length in the previous mblk, if it larger than
> +		 * certain threshold (e.g, whole pkt or > 50% of pkt or 2 x sizeof(struct sip_hdr), append pkt
> +		 * to the previous mblk.  This might be done in sip_pack_pkt()
> +		 */
> +		pkt_len = roundup(pkt_len, sip->tx_blksz);
> +		blknum = pkt_len / sip->tx_blksz;
> +
> +		/*
> +		 * FIXME: Empirical delay. Without this delay, the connection to
> +		 * a WiFi network crashes the kernel (sometimes at the second
> +		 * connection).
> +		 */
> +		udelay(2000);
> +
> +		if (unlikely(atomic_read(&sip->credit_status) == RECALC_CREDIT_ENABLE)) {	/* need recalc credit */
> +			hdr = (struct sip_hdr *)skb->data;
> +			itx_info = IEEE80211_SKB_CB(skb);
> +
> +			if (!(itx_info->flags == 0xffffffff &&
> +			      SIP_HDR_GET_TYPE(hdr->fc[0]) == SIP_CTRL &&
> +			      hdr->c_cmdid == SIP_CMD_RECALC_CREDIT &&
> +			      blknum <= atomic_read(&sip->tx_credits) - sip->credit_to_reserve)) {	/* except cmd recalc credit */
> +				dev_dbg(epub->dev, "recalc credits!\n");
> +				STRACE_TX_OUT_OF_CREDIT_INC();
> +				queued_back = true;
> +				out_of_credits = true;
> +				break;
> +			}
> +		} else {
> +			if (unlikely(blknum > (atomic_read(&sip->tx_credits) - sip->credit_to_reserve - SIP_CTRL_CREDIT_RESERVE))) {
> +				itx_info = IEEE80211_SKB_CB(skb);
> +				if (itx_info->flags == 0xffffffff) {	/* priv ctrl pkt */
> +					if (blknum > atomic_read(&sip->tx_credits) - sip->credit_to_reserve) {
> +						dev_dbg(epub->dev,
> +							"%s cmd pkt out of credits!\n",
> +							__func__);
> +						STRACE_TX_OUT_OF_CREDIT_INC();
> +						queued_back = true;
> +						out_of_credits = true;
> +						break;
> +					}
> +				} else {
> +					dev_dbg(epub->dev,
> +						"%s out of credits!\n",
> +						__func__);
> +					STRACE_TX_OUT_OF_CREDIT_INC();
> +					queued_back = true;
> +					out_of_credits = true;
> +					break;
> +				}
> +			}
> +		}
> +
> +		tx_len += pkt_len;
> +		if (tx_len >= SIP_TX_AGGR_BUF_SIZE) {
> +			/* do we need to have limitation likemax 8 pkts in a row? */
> +			dev_dbg(epub->dev, "%s too much pkts in one shot!\n",
> +				__func__);
> +			STRACE_TX_ONE_SHOT_INC();
> +			tx_len -= pkt_len;
> +			queued_back = true;
> +			break;
> +		}
> +
> +		if (sip_pack_pkt(sip, skb, &pm_state) != 0) {
> +			/* wrong pkt, won't send to target */
> +			tx_len -= pkt_len;
> +			continue;
> +		}
> +
> +		atomic_sub(blknum, &sip->tx_credits);
> +		/*
> +		 * FIXME: Empirical delay. Without this delay, the connection to
> +		 * a WiFi network crashes the kernel (sometimes at the second
> +		 * connection).
> +		 */
> +		udelay(2000);
> +
> +	}
> +
> +	if (queued_back)
> +		skb_queue_head(&epub->txq, skb);
> +
> +	if (atomic_read(&sip->state) == SIP_STOP
> +#ifdef HOST_RESET_BUG
> +	    || atomic_read(&epub->wl.off) == 1
> +#endif
> +	   ) {
> +		queued_back = 1;
> +		tx_len = 0;
> +	}
> +
> +	if (tx_len)
> +		sip_write_pkts(sip, pm_state);
> +
> +	if (queued_back && !out_of_credits)
> +		/* skb pending, do async process again */
> +		sip_trigger_txq_process(sip);
> +}
> +
> +#ifndef NO_WMM_DUMMY
> +static struct esp_80211_wmm_param_element esp_wmm_param = {
> +	.oui = {0x00, 0x50, 0xf2},
> +	.oui_type = 0x02,
> +	.oui_subtype = 0x01,
> +	.version = 0x01,
> +	.qos_info = 0x00,
> +	.reserved = 0x00,
> +	.ac = {
> +		{
> +			.aci_aifsn = 0x03,
> +			.cw = 0xa4,
> +			.txop_limit = 0x0000,
> +		},
> +		{
> +			.aci_aifsn = 0x27,
> +			.cw = 0xa4,
> +			.txop_limit = 0x0000,
> +		},
> +		{
> +			.aci_aifsn = 0x42,
> +			.cw = 0x43,
> +			.txop_limit = 0x005e,
> +		},
> +		{
> +			.aci_aifsn = 0x62,
> +			.cw = 0x32,
> +			.txop_limit = 0x002f,
> +		},
> +	},
> +};
> +
> +static int esp_add_wmm(struct sk_buff *skb)
> +{
> +	u8 *p;
> +	int flag = 0;
> +	int remain_len;
> +	int base_len;
> +	int len;
> +	struct ieee80211_mgmt *mgmt;
> +	struct ieee80211_hdr *wh;
> +
> +	if (!skb)
> +		return -1;
> +
> +	wh = (struct ieee80211_hdr *)skb->data;
> +	mgmt = (struct ieee80211_mgmt *)((u8 *)skb->data);
> +
> +	if (ieee80211_is_assoc_resp(wh->frame_control)) {
> +		p = mgmt->u.assoc_resp.variable;
> +		base_len = (u8 *)mgmt->u.assoc_resp.variable - (u8 *)mgmt;
> +	} else if (ieee80211_is_reassoc_resp(wh->frame_control)) {
> +		p = mgmt->u.reassoc_resp.variable;
> +		base_len = (u8 *)mgmt->u.reassoc_resp.variable - (u8 *)mgmt;
> +	} else if (ieee80211_is_probe_resp(wh->frame_control)) {
> +		p = mgmt->u.probe_resp.variable;
> +		base_len = (u8 *)mgmt->u.probe_resp.variable - (u8 *)mgmt;
> +	} else if (ieee80211_is_beacon(wh->frame_control)) {
> +		p = mgmt->u.beacon.variable;
> +		base_len = (u8 *)mgmt->u.beacon.variable - (u8 *)mgmt;
> +	} else {
> +		return 1;
> +	}
> +
> +	remain_len = skb->len - base_len;
> +
> +	while (remain_len > 0) {
> +		if (*p == 0xdd && *(p + 5) == 0x02)	//wmm type
> +			return 0;
> +		else if (*p == 0x2d)	//has ht cap
> +			flag = 1;
> +
> +		len = *(++p);
> +		p += (len + 1);
> +		remain_len -= (len + 2);
> +	}
> +
> +	if (remain_len < 0)
> +		return -2;

-2 is not a valid error code.

> +
> +	if (flag == 1) {
> +		skb_put(skb, 2 + sizeof(esp_wmm_param));
> +
> +		memset(p, 0xdd, sizeof(u8));
> +		memset(p + 1, sizeof(esp_wmm_param), sizeof(u8));
> +		memcpy(p + 2, &esp_wmm_param, sizeof(esp_wmm_param));
> +	}
> +
> +	return 0;
> +}
> +#endif				/* NO_WMM_DUMMY */
> +
> +/*  parse mac_rx_ctrl and return length */
> +static int sip_parse_mac_rx_info(struct esp_sip *sip,
> +				 struct esp_mac_rx_ctrl *mac_ctrl,
> +				 struct sk_buff *skb)
> +{
> +	struct ieee80211_rx_status *rx_status;
> +	struct ieee80211_hdr *hdr;
> +	struct ieee80211_hdr *wh;
> +
> +	rx_status = IEEE80211_SKB_RXCB(skb);
> +	rx_status->freq = esp_ieee2mhz(mac_ctrl->channel);
> +	rx_status->signal = mac_ctrl->rssi + mac_ctrl->noise_floor;	/* snr actually, need to offset noise floor e.g. -85 */
> +
> +	hdr = (struct ieee80211_hdr *)skb->data;
> +	if (mac_ctrl->damatch0 == 1 && mac_ctrl->bssidmatch0 == 1 &&	/*match bssid and da, but beacon package contain other bssid */
> +	    !strncmp(hdr->addr2, sip->epub->wl.bssid, ETH_ALEN)) {	/* force match addr2 */
> +		if (++signal_loop >= SIGNAL_COUNT) {
> +			avg_signal += rx_status->signal;
> +			avg_signal /= SIGNAL_COUNT;
> +			rx_status->signal = avg_signal + 5;
> +			old_signal = rx_status->signal;
> +			signal_loop = 0;
> +			avg_signal = 0;
> +		} else {
> +			avg_signal += rx_status->signal;
> +			rx_status->signal = old_signal;
> +		}
> +	}
> +
> +	rx_status->antenna = 0;	/* one antenna for now */
> +	rx_status->band = NL80211_BAND_2GHZ;
> +	rx_status->flag = RX_FLAG_DECRYPTED | RX_FLAG_MMIC_STRIPPED;
> +	if (mac_ctrl->sig_mode) {
> +		rx_status->encoding |= RX_ENC_HT;
> +		rx_status->rate_idx = mac_ctrl->MCS;
> +		if (mac_ctrl->SGI)
> +			rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
> +	} else {
> +		rx_status->rate_idx = esp_wmac_rate2idx(mac_ctrl->rate);
> +	}
> +
> +	if (mac_ctrl->rxend_state == RX_FCS_ERR)
> +		rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
> +
> +	/* Mic error frame flag */
> +	if (mac_ctrl->rxend_state == RX_TKIPMIC_ERR ||
> +	    mac_ctrl->rxend_state == RX_CCMPMIC_ERR) {
> +		if (atomic_read(&sip->epub->wl.tkip_key_set) == 1) {
> +			rx_status->flag |= RX_FLAG_MMIC_ERROR;
> +			atomic_set(&sip->epub->wl.tkip_key_set, 0);
> +			printk("mic err\n");
> +		} else {
> +			printk("mic err discard\n");
> +		}
> +	}
> +
> +	wh = (struct ieee80211_hdr *)((u8 *)skb->data);
> +
> +#ifndef NO_WMM_DUMMY
> +	if (ieee80211_is_mgmt(wh->frame_control))
> +		esp_add_wmm(skb);
> +#endif
> +
> +	/* some kernel e.g. 3.0.8 wrongly handles non-encrypted pkt like eapol */
> +	if (ieee80211_is_data(wh->frame_control)) {
> +		if (!ieee80211_has_protected(wh->frame_control))
> +			rx_status->flag |= RX_FLAG_IV_STRIPPED;
> +		else if (!atomic_read(&sip->epub->wl.ptk_cnt))
> +				rx_status->flag |= RX_FLAG_IV_STRIPPED;
> +	}
> +
> +	return 0;
> +}
> +
> +static struct esp_mac_rx_ctrl *sip_parse_normal_mac_ctrl(struct sk_buff *skb,
> +							 int *pkt_len_enc,
> +							 int *buf_len,
> +							 int *pulled_len)
> +{
> +	struct sip_hdr *hdr = (struct sip_hdr *)skb->data;
> +	struct esp_mac_rx_ctrl *mac_ctrl;
> +	int len_in_hdr = hdr->len;
> +
> +	ESSERT(skb);
> +	ESSERT(skb->len > SIP_MIN_DATA_PKT_LEN);
> +
> +	skb_pull(skb, sizeof(struct sip_hdr));
> +	*pulled_len += sizeof(struct sip_hdr);
> +	mac_ctrl = (struct esp_mac_rx_ctrl *)skb->data;
> +	if (!mac_ctrl->aggregation) {
> +		ESSERT(pkt_len_enc);
> +		ESSERT(buf_len);
> +		*pkt_len_enc = (mac_ctrl->sig_mode ? mac_ctrl->HT_length : mac_ctrl->legacy_length) - FCS_LEN;
> +		*buf_len = len_in_hdr - sizeof(struct sip_hdr) -
> +			sizeof(struct esp_mac_rx_ctrl);
> +	}
> +
> +	skb_pull(skb, sizeof(struct esp_mac_rx_ctrl));
> +	*pulled_len += sizeof(struct esp_mac_rx_ctrl);
> +
> +	return mac_ctrl;
> +}
> +
> +/* for one MPDU (including subframe in AMPDU) */
> +static struct sk_buff *sip_parse_data_rx_info(struct esp_sip *sip,
> +					      struct sk_buff *skb,
> +					      int pkt_len_enc, int buf_len,
> +					      struct esp_mac_rx_ctrl *mac_ctrl,
> +					      int *pulled_len)
> +{
> +	/*   | mac_rx_ctrl | real_data_payload | ampdu_entries | */
> +	struct sk_buff *rskb;
> +	struct ieee80211_hdr *wh;
> +	int pkt_len, ret;
> +
> +	if (mac_ctrl->aggregation) {
> +		wh = (struct ieee80211_hdr *)skb->data;
> +		pkt_len = pkt_len_enc;
> +		if (ieee80211_has_protected(wh->frame_control))	//ampdu, it is CCMP enc
> +			pkt_len -= 8;
> +
> +		buf_len = roundup(pkt_len, 4);
> +	} else {
> +		pkt_len = buf_len - 3 + ((pkt_len_enc - 1) & 0x3);
> +	}
> +
> +#ifndef NO_WMM_DUMMY
> +	rskb = __dev_alloc_skb(pkt_len_enc + sizeof(esp_wmm_param) + 2,
> +			       GFP_ATOMIC);
> +#else
> +	rskb = __dev_alloc_skb(pkt_len_enc, GFP_ATOMIC);
> +#endif				/* NO_WMM_DUMMY */
> +	if (unlikely(!rskb)) {

Remove all likely/unlikely.

> +		dev_err(sip->epub->dev, "no mem for rskb\n");

No need for this printk.

> +		return NULL;
> +	}
> +
> +	skb_put(rskb, pkt_len_enc);
> +
> +	memcpy(rskb->data, skb->data, pkt_len);
> +
> +	if (pkt_len_enc > pkt_len)
> +		memset(rskb->data + pkt_len, 0, pkt_len_enc - pkt_len);
> +
> +	/* strip out current pkt, move to the next one */
> +	skb_pull(skb, buf_len);
> +	*pulled_len += buf_len;
> +
> +	ret = sip_parse_mac_rx_info(sip, mac_ctrl, rskb);
> +	if (ret == -1 && !mac_ctrl->aggregation) {


sip_parse_mac_rx_info() never returns -1.  Also -1 is not a the correct
error code.

> +		kfree_skb(rskb);
> +		return NULL;
> +	}
> +
> +	return rskb;
> +}
> +
> +struct esp_sip *sip_attach(struct esp_pub *epub)
> +{
> +	struct esp_sip *sip;
> +	struct sip_pkt *pkt;
> +	int i, po;
> +
> +	sip = kzalloc(sizeof(*sip), GFP_KERNEL);
> +	if (!sip) {
> +		dev_dbg(epub->dev, "no mem for sip!\n");
> +		goto _err_sip;

No need for the error message or the goto.

> +	}
> +
> +	/* Finding the smalest available space to allocate this variavle */
> +	po = get_order(SIP_TX_AGGR_BUF_SIZE);
> +
> +	sip->tx_aggr_buf = (u8 *)__get_free_pages(GFP_ATOMIC, po);
> +	if (!sip->tx_aggr_buf) {
> +		dev_err(epub->dev, "no mem for tx_aggr_buf!\n");
> +		goto _err_aggr;
> +	}
> +
> +	spin_lock_init(&sip->lock);
> +
> +	INIT_LIST_HEAD(&sip->free_ctrl_txbuf);
> +	INIT_LIST_HEAD(&sip->free_ctrl_rxbuf);
> +
> +	for (i = 0; i < SIP_CTRL_BUF_N; i++) {
> +		pkt = kzalloc(sizeof(*pkt), GFP_KERNEL);
> +		if (!pkt)
> +			goto _err_pkt;
> +
> +		pkt->buf_begin = kzalloc(SIP_CTRL_BUF_SZ, GFP_KERNEL);
> +		if (!pkt->buf_begin) {
> +			kfree(pkt);
> +			pkt = NULL;

No need to set "pkt" to NULL

> +			goto _err_pkt;
> +		}
> +
> +		pkt->buf_len = SIP_CTRL_BUF_SZ;
> +		pkt->buf = pkt->buf_begin;
> +
> +		if (i < SIP_CTRL_TXBUF_N)
> +			list_add_tail(&pkt->list, &sip->free_ctrl_txbuf);
> +		else
> +			list_add_tail(&pkt->list, &sip->free_ctrl_rxbuf);
> +	}
> +
> +	mutex_init(&sip->rx_mtx);
> +	skb_queue_head_init(&sip->rxq);
> +	INIT_WORK(&sip->rx_process_work, sip_rxq_process);
> +
> +	sip->epub = epub;
> +	atomic_set(&sip->noise_floor, -96);
> +
> +	atomic_set(&sip->state, SIP_INIT);
> +	atomic_set(&sip->tx_credits, 0);
> +
> +	if (!sip->rawbuf) {
> +		sip->rawbuf = kzalloc(SIP_BOOT_BUF_SIZE, GFP_KERNEL);
> +		if (!sip->rawbuf) {
> +			dev_err(epub->dev, "no mem for rawbuf!\n");
> +			goto _err_pkt;
> +		}
> +	}
> +
> +	atomic_set(&sip->state, SIP_PREPARE_BOOT);
> +
> +	return sip;
> +
> +_err_pkt:
> +	sip_free_init_ctrl_buf(sip);
> +
> +	if (sip->tx_aggr_buf) {

No need for this test.  We know it's non-NULL.

> +		po = get_order(SIP_TX_AGGR_BUF_SIZE);

po is already set.  No need to calculate it again.

> +		free_pages((unsigned long)sip->tx_aggr_buf, po);
> +		sip->tx_aggr_buf = NULL;

No need to set this to NULL.  We're just going to free "sip" on the
next line.

> +	}
> +
> +_err_aggr:
> +	kfree(sip);
> +	sip = NULL;

No need to set this to NULL

> +
> +_err_sip:
> +	return NULL;
> +}
> +
> +static void sip_free_init_ctrl_buf(struct esp_sip *sip)
> +{
> +	struct sip_pkt *pkt, *tpkt;
> +
> +	list_for_each_entry_safe(pkt, tpkt, &sip->free_ctrl_txbuf, list) {
> +		list_del(&pkt->list);
> +		kfree(pkt->buf_begin);
> +		kfree(pkt);
> +	}
> +
> +	list_for_each_entry_safe(pkt, tpkt, &sip->free_ctrl_rxbuf, list) {
> +		list_del(&pkt->list);
> +		kfree(pkt->buf_begin);
> +		kfree(pkt);
> +	}
> +}
> +
> +void sip_detach(struct esp_sip *sip)
> +{
> +	if (!sip)
> +		return;
> +
> +	sip_free_init_ctrl_buf(sip);
> +
> +	if (atomic_read(&sip->state) == SIP_RUN) {
> +		sif_disable_target_interrupt(sip->epub);
> +
> +		atomic_set(&sip->state, SIP_STOP);
> +
> +		/* disable irq here */
> +		sif_disable_irq(sip->epub);
> +		cancel_work_sync(&sip->rx_process_work);
> +
> +		skb_queue_purge(&sip->rxq);
> +		mutex_destroy(&sip->rx_mtx);
> +		cancel_work_sync(&sip->epub->sendup_work);
> +		skb_queue_purge(&sip->epub->rxq);
> +
> +		if (test_and_clear_bit(ESP_WL_FLAG_HW_REGISTERED,
> +				       &sip->epub->wl.flags))
> +			ieee80211_unregister_hw(sip->epub->hw);
> +
> +		/* cancel all worker/timer */
> +		cancel_work_sync(&sip->epub->tx_work);
> +		skb_queue_purge(&sip->epub->txq);
> +		skb_queue_purge(&sip->epub->txdoneq);
> +
> +		free_pages((unsigned long)sip->tx_aggr_buf,
> +			   get_order(SIP_TX_AGGR_BUF_SIZE));
> +		sip->tx_aggr_buf = NULL;
> +
> +		atomic_set(&sip->state, SIP_INIT);
> +	} else if (atomic_read(&sip->state) >= SIP_BOOT &&
> +		   atomic_read(&sip->state) <= SIP_WAIT_BOOTUP) {
> +		sif_disable_target_interrupt(sip->epub);
> +		atomic_set(&sip->state, SIP_STOP);
> +		sif_disable_irq(sip->epub);
> +		kfree(sip->rawbuf);
> +
> +		if (atomic_read(&sip->state) == SIP_SEND_INIT) {
> +			cancel_work_sync(&sip->rx_process_work);
> +			skb_queue_purge(&sip->rxq);
> +			mutex_destroy(&sip->rx_mtx);
> +			cancel_work_sync(&sip->epub->sendup_work);
> +			skb_queue_purge(&sip->epub->rxq);
> +		}
> +
> +		if (test_and_clear_bit(ESP_WL_FLAG_HW_REGISTERED,
> +				       &sip->epub->wl.flags))
> +			ieee80211_unregister_hw(sip->epub->hw);
> +		atomic_set(&sip->state, SIP_INIT);
> +	} else {
> +		dev_err(sip->epub->dev, "wrong state (%d) for detaching sip\n",
> +			atomic_read(&sip->state));
> +	}
> +
> +	kfree(sip);
> +}
> +
> +int sip_write_memory(struct esp_sip *sip, u32 addr, u8 *buf, u16 len)
> +{
> +	struct sip_cmd_write_memory *cmd;
> +	struct sip_hdr *chdr;
> +	u16 remains, hdrs, bufsize;
> +	u32 loadaddr;
> +	u8 *src;
> +	int err = 0;

No need to initialize "err".

> +	u32 *t;
> +
> +	if (!sip || !sip->rawbuf) {
> +		ESSERT(sip);
> +		ESSERT(sip->rawbuf);
> +		return -EINVAL;
> +	}
> +
> +	memset(sip->rawbuf, 0, SIP_BOOT_BUF_SIZE);
> +
> +	chdr = (struct sip_hdr *)sip->rawbuf;
> +	SIP_HDR_SET_TYPE(chdr->fc[0], SIP_CTRL);
> +	chdr->c_cmdid = SIP_CMD_WRITE_MEMORY;
> +
> +	remains = len;
> +	hdrs = sizeof(struct sip_hdr) + sizeof(struct sip_cmd_write_memory);
> +
> +	while (remains) {
> +		src = &buf[len - remains];
> +		loadaddr = addr + (len - remains);
> +
> +		if (remains < (SIP_BOOT_BUF_SIZE - hdrs)) {
> +			/* aligned with 4 bytes */
> +			bufsize = roundup(remains, 4);
> +			memset(sip->rawbuf + hdrs, 0, bufsize);
> +			remains = 0;
> +		} else {
> +			bufsize = SIP_BOOT_BUF_SIZE - hdrs;
> +			remains -= bufsize;
> +		}
> +
> +		chdr->len = bufsize + hdrs;
> +		chdr->seq = sip->txseq++;
> +		cmd = (struct sip_cmd_write_memory *)(sip->rawbuf + SIP_CTRL_HDR_LEN);
> +		cmd->len = bufsize;
> +		cmd->addr = loadaddr;
> +		memcpy(sip->rawbuf + hdrs, src, bufsize);
> +
> +		t = (u32 *)sip->rawbuf;
> +		err = esp_common_write(sip->epub, sip->rawbuf, chdr->len,
> +				       ESP_SIF_SYNC);
> +		if (err) {
> +			dev_err(sip->epub->dev, "send buffer failed\n");
> +			return err;
> +		}
> +		// 1ms is enough, in fact on dell-d430, need not delay at all.
> +		mdelay(1);
> +	}
> +
> +	return err;

return 0;

> +}
> +
> +int sip_send_cmd(struct esp_sip *sip, int cid, u32 cmdlen, void *cmd)
> +{
> +	struct sip_hdr *chdr;
> +	struct sip_pkt *pkt;
> +	int ret;
> +
> +	pkt = sip_get_ctrl_buf(sip, SIP_TX_CTRL_BUF);
> +	if (!pkt)
> +		return -ENOMEM;
> +
> +	chdr = (struct sip_hdr *)pkt->buf_begin;
> +	chdr->len = SIP_CTRL_HDR_LEN + cmdlen;
> +	chdr->seq = sip->txseq++;
> +	chdr->c_cmdid = cid;
> +
> +	if (cmd) {
> +		memset(pkt->buf, 0, cmdlen);

No need for this memset() because we memcpy() over it on the next line.

> +		memcpy(pkt->buf, (u8 *)cmd, cmdlen);
> +	}
> +
> +	ret = esp_common_write(sip->epub, pkt->buf_begin, chdr->len,
> +			       ESP_SIF_SYNC);
> +	if (ret)
> +		dev_err(sip->epub->dev, "send cmd %d failed\n", cid);
> +
> +	sip_reclaim_ctrl_buf(sip, pkt, SIP_TX_CTRL_BUF);
> +
> +	/*  Hack here: reset tx/rx seq before target ram code is up... */
> +	if (cid == SIP_CMD_BOOTUP) {
> +		sip->rxseq = 0;
> +		sip->txseq = 0;
> +		sip->txdataseq = 0;
> +	}
> +
> +	return ret;
> +}
> +
> +struct sk_buff *sip_alloc_ctrl_skbuf(struct esp_sip *sip, u16 len, u32 cid)
> +{
> +	struct sip_hdr *si;
> +	struct ieee80211_tx_info *ti;
> +	struct sk_buff *skb;
> +
> +	ESSERT(len <= sip->tx_blksz);
> +
> +	/* no need to reserve space for net stack */
> +	skb = __dev_alloc_skb(len, GFP_KERNEL);
> +	if (!skb) {
> +		dev_err(sip->epub->dev, "no skb for ctrl!\n");
> +		return NULL;
> +	}
> +
> +	skb->len = len;
> +
> +	ti = IEEE80211_SKB_CB(skb);
> +	/* set tx_info flags to 0xffffffff to indicate sip_ctrl pkt */
> +	ti->flags = 0xffffffff;
> +
> +	si = (struct sip_hdr *)skb->data;
> +	memset(si, 0, sizeof(struct sip_hdr));
> +	SIP_HDR_SET_TYPE(si->fc[0], SIP_CTRL);
> +	si->len = len;
> +	si->c_cmdid = cid;
> +
> +	return skb;
> +}
> +
> +void sip_free_ctrl_skbuff(struct esp_sip *sip, struct sk_buff *skb)
> +{
> +	memset(IEEE80211_SKB_CB(skb), 0, sizeof(struct ieee80211_tx_info));
> +	kfree_skb(skb);
> +}
> +
> +static struct sip_pkt *sip_get_ctrl_buf(struct esp_sip *sip,
> +					enum sip_buf_type bftype)
> +{
> +	struct sip_pkt *pkt;
> +	struct list_head *bflist;
> +	struct sip_hdr *chdr;
> +
> +	/* FIXME: Why taking spinlock to check list_empty? */
> +	bflist = (bftype == SIP_TX_CTRL_BUF) ? &sip->free_ctrl_txbuf : &sip->free_ctrl_rxbuf;
> +
> +	spin_lock_bh(&sip->lock);
> +
> +	if (list_empty(bflist)) {
> +		spin_unlock_bh(&sip->lock);
> +		return NULL;
> +	}
> +
> +	pkt = list_first_entry(bflist, struct sip_pkt, list);
> +	list_del(&pkt->list);
> +	spin_unlock_bh(&sip->lock);
> +
> +	if (bftype == SIP_TX_CTRL_BUF) {
> +		chdr = (struct sip_hdr *)pkt->buf_begin;
> +		SIP_HDR_SET_TYPE(chdr->fc[0], SIP_CTRL);
> +		pkt->buf = pkt->buf_begin + SIP_CTRL_HDR_LEN;
> +	} else {
> +		pkt->buf = pkt->buf_begin;
> +	}
> +
> +	return pkt;
> +}
> +
> +static void sip_reclaim_ctrl_buf(struct esp_sip *sip, struct sip_pkt *pkt,
> +				 enum sip_buf_type bftype)
> +{
> +	struct list_head *bflist;
> +
> +	if (bftype == SIP_TX_CTRL_BUF)
> +		bflist = &sip->free_ctrl_txbuf;
> +	else if (bftype == SIP_RX_CTRL_BUF)
> +		bflist = &sip->free_ctrl_rxbuf;
> +	else
> +		return;
> +
> +	pkt->buf = pkt->buf_begin;
> +
> +	spin_lock_bh(&sip->lock);
> +	list_add_tail(&pkt->list, bflist);
> +	spin_unlock_bh(&sip->lock);
> +}
> +
> +int sip_poll_bootup_event(struct esp_sip *sip)
> +{
> +	int ret = 0;
> +
> +	if (gl_bootup_cplx)
> +		ret = wait_for_completion_timeout(gl_bootup_cplx, 2 * HZ);
> +
> +	if (!ret) {
> +		dev_err(sip->epub->dev, "bootup event timeout\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	if (!sif_get_ate_config())
> +		ret = esp_register_mac80211(sip->epub);
> +
> +	atomic_set(&sip->state, SIP_RUN);
> +
> +	return ret;
> +}
> +
> +/* FIXME: always returning 0 ? */
> +int sip_poll_resetting_event(struct esp_sip *sip)
> +{
> +	unsigned int ret;
> +
> +	if (gl_bootup_cplx)
> +		ret = wait_for_completion_timeout(gl_bootup_cplx, 10 * HZ);
> +
> +	if (!ret) {
> +		dev_err(sip->epub->dev, "resetting event timeout\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	return 0;
> +}
> +
> +bool sip_queue_need_stop(struct esp_sip *sip)
> +{
> +	return atomic_read(&sip->tx_data_pkt_queued) >= SIP_STOP_QUEUE_THRESHOLD ||
> +		(atomic_read(&sip->tx_credits) < 8 &&
> +		 atomic_read(&sip->tx_data_pkt_queued) >= SIP_STOP_QUEUE_THRESHOLD / 4 * 3);
> +}
> +
> +bool sip_queue_may_resume(struct esp_sip *sip)
> +{
> +	return atomic_read(&sip->epub->txq_stopped) &&
> +		!test_bit(ESP_WL_FLAG_STOP_TXQ, &sip->epub->wl.flags) &&
> +		((atomic_read(&sip->tx_credits) >= 16 &&
> +		  atomic_read(&sip->tx_data_pkt_queued) < SIP_RESUME_QUEUE_THRESHOLD * 2) ||
> +		 atomic_read(&sip->tx_data_pkt_queued) < SIP_RESUME_QUEUE_THRESHOLD);
> +}
> +
> +int sip_cmd_enqueue(struct esp_sip *sip, struct sk_buff *skb, int prior)
> +{
> +	if (!sip || !sip->epub || !skb) {
> +		return -EINVAL;
> +	}
> +
> +	if (prior == ENQUEUE_PRIOR_HEAD)
> +		skb_queue_head(&sip->epub->txq, skb);
> +	else
> +		skb_queue_tail(&sip->epub->txq, skb);
> +
> +	if (!sif_get_ate_config())
> +		ieee80211_queue_work(sip->epub->hw, &sip->epub->tx_work);
> +	else
> +		queue_work(sip->epub->esp_wkq, &sip->epub->tx_work);
> +
> +	return 0;
> +}
> +
> +void sip_tx_data_pkt_enqueue(struct esp_pub *epub, struct sk_buff *skb)
> +{
> +	if (!epub || !epub->sip || !skb)
> +		return;
> +
> +	skb_queue_tail(&epub->txq, skb);
> +	atomic_inc(&epub->sip->tx_data_pkt_queued);
> +
> +	if (sip_queue_need_stop(epub->sip))
> +		if (epub->hw) {
> +			ieee80211_stop_queues(epub->hw);
> +			atomic_set(&epub->txq_stopped, true);
> +		}

Multi-line indents get curly braces for readability.

> +}
> diff --git a/drivers/staging/esp8089/esp_sip.h b/drivers/staging/esp8089/esp_sip.h
> new file mode 100644
> index 000000000000..fe58aae4ba54
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_sip.h
> @@ -0,0 +1,150 @@
> +/*
> + * Copyright (c) 2009 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + *      Quentin Schulz <quentin.schulz at 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 at 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)

Just make it return int or maybe u8.

> +{
> +	int i;
> +
> +	if (rate == ESP_RATE_2_LONG)
> +		return 1;
> +
> +	if (rate == ESP_RATE_5_LONG)
> +		return 2;
> +
> +	if (rate == ESP_RATE_11_LONG)
> +		return 3;
> +
> +	for (i = 0; i < 20; i++)
> +		if (rate == esp_rate_table[i])
> +			return i;
> +
> +	return 0;
> +}
> +
> +bool esp_wmac_rxsec_error(u8 error)
> +{
> +	return (error >= RX_SECOV_ERR && error <= RX_SECFIFO_TIMEOUT) ||
> +		(error >= RX_WEPICV_ERR && error <= RX_WAPIMIC_ERR);
> +}
> +
> +int esp_cipher2alg(int cipher)
> +{
> +	if (cipher == WLAN_CIPHER_SUITE_TKIP)
> +		return ALG_TKIP;
> +
> +	if (cipher == WLAN_CIPHER_SUITE_CCMP)
> +		return ALG_CCMP;
> +
> +	if (cipher == WLAN_CIPHER_SUITE_WEP40 ||
> +	    cipher == WLAN_CIPHER_SUITE_WEP104)
> +		return ALG_WEP;
> +
> +	if (cipher == WLAN_CIPHER_SUITE_AES_CMAC)
> +		return ALG_AES_CMAC;
> +
> +	return -1;
> +}
> diff --git a/drivers/staging/esp8089/esp_utils.h b/drivers/staging/esp8089/esp_utils.h
> new file mode 100644
> index 000000000000..3a4d461a4e70
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_utils.h
> @@ -0,0 +1,27 @@
> +/*
> + * Copyright (c) 2011 - 2012 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + *      Quentin Schulz <quentin.schulz at 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 at 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 at 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 at 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;

return 0;

> +}
> +
> +static int esdio_power_on(struct esp_sdio_ctrl *sctrl)
> +{
> +	return esdio_power(sctrl, true);
> +}
> +
> +static int esdio_power_off(struct esp_sdio_ctrl *sctrl)
> +{
> +	return esdio_power(sctrl, false);
> +}
> +
> +void sif_enable_irq(struct esp_pub *epub)
> +{
> +	sdio_claim_host(epub->sif->func);
> +
> +	if (sdio_claim_irq(epub->sif->func, sif_dsr))
> +		dev_err(epub->dev, "sif %s failed\n", __func__);
> +
> +	atomic_set(&epub->sip->state, SIP_BOOT);
> +	atomic_set(&epub->sif->irq_installed, 1);
> +
> +	sdio_release_host(epub->sif->func);
> +}
> +
> +void sif_disable_irq(struct esp_pub *epub)
> +{
> +	int i = 0;
> +
> +	if (!atomic_read(&epub->sif->irq_installed))
> +		return;
> +
> +	sdio_claim_host(epub->sif->func);
> +
> +	while (atomic_read(&epub->sif->irq_handling)) {
> +		sdio_release_host(epub->sif->func);
> +		schedule_timeout(HZ / 100);
> +		sdio_claim_host(epub->sif->func);
> +		if (i++ >= 400)
> +			break;
> +	}
> +
> +	/* Ignore errors, we don't always use an irq. */
> +	sdio_release_irq(epub->sif->func);
> +
> +	atomic_set(&epub->sif->irq_installed, 0);
> +	sdio_release_host(epub->sif->func);
> +}
> +
> +void sif_set_clock(struct sdio_func *func, int clk)
> +{
> +	struct mmc_host *host = func->card->host;
> +
> +	sdio_claim_host(func);
> +
> +	//currently only set clock
> +	host->ios.clock = clk * 1000000;
> +
> +	if (host->ios.clock > host->f_max)
> +		host->ios.clock = host->f_max;
> +
> +	host->ops->set_ios(host, &host->ios);
> +
> +	mdelay(2);
> +
> +	sdio_release_host(func);
> +}
> +
> +static void esp_sdio_remove(struct sdio_func *func);
> +
> +static int esp_sdio_probe(struct sdio_func *func,
> +			  const struct sdio_device_id *id)
> +{
> +	struct esp_pub *epub;
> +	struct esp_sdio_ctrl *sctrl;
> +	struct mmc_host *host = func->card->host;
> +	int err;
> +
> +	if (!sif_sctrl) {
> +		request_init_conf(&func->dev);
> +
> +		sctrl = kzalloc(sizeof(*sctrl), GFP_KERNEL);
> +		if (!sctrl)
> +			return -ENOMEM;
> +
> +		/* temp buffer reserved for un-dma-able request */
> +		sctrl->dma_buffer = kzalloc(ESP_DMA_IBUFSZ, GFP_KERNEL);
> +		if (!sctrl->dma_buffer) {
> +			err = -ENOMEM;
> +			goto _err_last;
> +		}
> +
> +		sif_sctrl = sctrl;
> +		sctrl->slc_blk_sz = SIF_SLC_BLOCK_SIZE;
> +
> +		epub = esp_pub_alloc_mac80211(&func->dev);
> +		if (!epub) {
> +			err = -ENOMEM;
> +			goto _err_dma;
> +		}
> +
> +		epub->sif = sctrl;
> +		epub->sdio_state = ESP_SDIO_STATE_FIRST_INIT;
> +		sctrl->epub = epub;
> +	} else {
> +		/* FIXME: why duplicating sctrl and sif_sctrl? */
> +		sctrl = sif_sctrl;
> +		sif_sctrl = NULL;
> +		epub = sctrl->epub;
> +		epub->sdio_state = ESP_SDIO_STATE_SECOND_INIT;
> +		/* FIXME: already done in esp_pub_alloc_mac80211 which is called on first probe */
> +		SET_IEEE80211_DEV(epub->hw, &func->dev);
> +		epub->dev = &func->dev;
> +	}
> +
> +	sctrl->func = func;
> +	sdio_set_drvdata(func, sctrl);
> +	sctrl->id = id;
> +	sctrl->off = true;
> +
> +	/* give us some time to enable, in ms */
> +	func->enable_timeout = 100;
> +
> +	err = esdio_power_on(sctrl);
> +	if (err) {
> +		if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT)
> +			goto _err_ext_gpio;
> +
> +		goto _err_second_init;
> +	}
> +
> +	check_target_id(epub);
> +
> +	sdio_claim_host(func);
> +
> +	err = sdio_set_block_size(func, sctrl->slc_blk_sz);
> +	if (err) {
> +		dev_err(epub->dev, "Set sdio block size %d failed: %d)\n",
> +			sctrl->slc_blk_sz, err);
> +
> +		sdio_release_host(func);
> +		if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT)
> +			goto _err_off;
> +
> +		goto _err_second_init;
> +	}
> +
> +	sdio_release_host(func);
> +
> +#ifdef LOWER_CLK
> +	/* fix clock for dongle */
> +	sif_set_clock(func, 23);
> +#endif				//LOWER_CLK
> +
> +	err = esp_pub_init_all(epub);
> +	if (err) {
> +		dev_err(epub->dev, "esp_init_all failed: %d\n", err);
> +		if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) {
> +			err = 0;

Why is this success?

> +			goto _err_first_init;
> +		}
> +
> +		if (epub->sdio_state == ESP_SDIO_STATE_SECOND_INIT)
> +			goto _err_second_init;
> +	}
> +
> +	if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) {
> +		epub->sdio_state = ESP_SDIO_STATE_FIRST_NORMAL_EXIT;
> +		/* Rescan the esp8089 after loading the initial firmware */
> +		mmc_force_detect_change(host, msecs_to_jiffies(100), true);
> +	}
> +
> +	return err;

return 0;

> +
> +_err_off:
> +	esdio_power_off(sctrl);
> +
> +_err_ext_gpio:
> +	esp_pub_dealloc_mac80211(epub);
> +
> +_err_dma:
> +	kfree(sctrl->dma_buffer);
> +
> +_err_last:
> +	kfree(sctrl);
> +
> +_err_first_init:
> +	if (epub && epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT)
> +		epub->sdio_state = ESP_SDIO_STATE_FIRST_ERROR_EXIT;
> +
> +	return err;
> +
> +_err_second_init:
> +	epub->sdio_state = ESP_SDIO_STATE_SECOND_ERROR_EXIT;
> +	esp_sdio_remove(func);
> +
> +	return err;
> +}
> +
> +static void esp_sdio_remove(struct sdio_func *func)
> +{
> +	struct esp_sdio_ctrl *sctrl = sdio_get_drvdata(func);
> +	struct esp_pub *epub;
> +
> +	if (!sctrl) {
> +		return;
> +	}
> +
> +	epub = sctrl->epub;
> +	if (!epub) {
> +		goto _out;
> +	}
> +
> +	if (epub->sdio_state != ESP_SDIO_STATE_FIRST_NORMAL_EXIT) {
> +		if (epub->sip) {
> +			sip_detach(epub->sip);
> +			epub->sip = NULL;
> +		}
> +	} else {
> +		atomic_set(&epub->sip->state, SIP_STOP);
> +		sif_disable_irq(epub);
> +	}
> +
> +	if (epub->sdio_state != ESP_SDIO_STATE_FIRST_NORMAL_EXIT) {
> +		esp_pub_dealloc_mac80211(epub);
> +
> +		kfree(sctrl->dma_buffer);
> +		sctrl->dma_buffer = NULL;
> +
> +		kfree(sctrl);
> +	}
> +
> +_out:
> +	sdio_set_drvdata(func, NULL);
> +}
> +
> +MODULE_DEVICE_TABLE(sdio, esp_sdio_devices);
> +
> +static int esp_sdio_suspend(struct device *dev)
> +{
> +	struct sdio_func *func = dev_to_sdio_func(dev);
> +	struct esp_sdio_ctrl *sctrl = sdio_get_drvdata(func);
> +	struct esp_pub *epub = sctrl->epub;
> +	u32 sdio_flags;
> +
> +	printk("%s", __func__);
> +	atomic_set(&epub->ps.state, ESP_PM_ON);
> +
> +	sdio_flags = sdio_get_host_pm_caps(func);
> +
> +	if (!(sdio_flags & MMC_PM_KEEP_POWER))
> +		printk("%s can't keep power while host is suspended\n",
> +		       __func__);
> +
> +	/* keep power while host suspended */
> +	if (sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER))
> +		printk("%s error while trying to keep power\n", __func__);
> +
> +	return 0;
> +}
> +
> +static int esp_sdio_resume(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops esp_sdio_pm_ops = {
> +	.suspend = esp_sdio_suspend,
> +	.resume = esp_sdio_resume,
> +};
> +
> +static struct sdio_driver esp_sdio_driver = {
> +	.name = "eagle_sdio",
> +	.id_table = esp_sdio_devices,
> +	.probe = esp_sdio_probe,
> +	.remove = esp_sdio_remove,
> +	.drv = {.pm = &esp_sdio_pm_ops,},
> +};
> +
> +static int /*__init*/ esp_sdio_init(void)
> +{
> +	esp_debugfs_init();
> +	sdio_register_driver(&esp_sdio_driver);
> +
> +	return 0;
> +}
> +
> +static void /*__exit*/ esp_sdio_exit(void)
> +{
> +	sdio_unregister_driver(&esp_sdio_driver);
> +	esp_debugfs_exit();
> +}
> +
> +module_init(esp_sdio_init);
> +module_exit(esp_sdio_exit);
> +
> +MODULE_AUTHOR("Espressif System");
> +MODULE_DESCRIPTION
> +("Driver for SDIO interconnected eagle low-power WLAN devices");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/staging/esp8089/sip2_common.h b/drivers/staging/esp8089/sip2_common.h
> new file mode 100644
> index 000000000000..5dfb8c492223
> --- /dev/null
> +++ b/drivers/staging/esp8089/sip2_common.h
> @@ -0,0 +1,388 @@
> +/*
> + * Copyright (c) 2010 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + *      Quentin Schulz <quentin.schulz at 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 */

Remove the volatile.

> +		struct sip_tx_info tx_info;
> +	} u;

If you just remove the "u" and make this an anonymous union then
it makes everything a easier.

> +	u32 seq;
> +} __packed;
> +
> +#define h_credits u.recycled_credits

It means we can get rid of these ugly defines.

> +#define c_evtid fc[1]
> +#define c_cmdid u.tx_info.u.cmdid
> +#define d_ac u.tx_info.u.dinfo.ac
> +#define d_tid  u.tx_info.u.dinfo.tid
> +#define d_p2p   u.tx_info.u.dinfo.p2p
> +#define d_enc_flag u.tx_info.u.dinfo.enc_flag
> +#define d_hw_kid   u.tx_info.u.dinfo.hw_kid
> +
> +#define SIP_CREDITS_MASK  0xfff	/* last 12 bits */
> +
> +#define SIP_HDR_MIN_LEN		4
> +#define SIP_HDR_LEN		sizeof(struct sip_hdr)
> +#define SIP_CTRL_HDR_LEN	SIP_HDR_LEN	/* same as sip_hdr in sip2 design */
> +#define SIP_BOOT_BUF_SIZE	256
> +#define SIP_CTRL_BUF_SZ		256	/* too much?? */
> +#define SIP_CTRL_BUF_N		6
> +#define SIP_CTRL_TXBUF_N	2
> +#define SIP_CTRL_RXBUF_N	4
> +
> +/* WAR for mblk */
> +#define SIP_RX_ADDR_PREFIX_MASK	0xfc000000
> +#define SIP_RX_ADDR_SHIFT	6	/* [31:5],  shift 6 bits */
> +
> +struct sip_cmd_write_memory {
> +	u32 addr;
> +	u32 len;
> +} __packed;
> +
> +struct sip_cmd_read_memory {
> +	u32 addr;
> +	u32 len;
> +} __packed;
> +
> +struct sip_cmd_write_reg {
> +	u32 addr;
> +	u32 val;
> +} __packed;
> +
> +struct sip_cmd_bootup {
> +	u32 boot_addr;
> +} __packed;
> +
> +struct sip_cmd_loopback {
> +	u32 txlen;		//host to target packet len, 0 means no txpacket
> +	u32 rxlen;		//target to host packet len, 0 means no rxpacket
> +	u32 pack_id;		//sequence of packet
> +} __packed;
> +
> +struct sip_evt_loopback {
> +	u32 txlen;		//host to target packet len, 0 means no txpacket
> +	u32 rxlen;		//target to host packet len, 0 means no rxpacket
> +	u32 pack_id;		//sequence of packet
> +} __packed;
> +
> +struct sip_cmd_copyback {
> +	u32 addr;
> +	u32 len;
> +} __packed;
> +
> +struct sip_cmd_scan {
> +	//        u8  ssid[32];
> +	u8 ssid_len;
> +	//        u8 hw_channel[14];
> +	u8 n_channels;
> +	u8 ie_len;
> +	u8 aborted;
> +} __packed;			// ie[] append at the end
> +
> +struct sip_cmd_setkey {
> +	u8 bssid_no;
> +	u8 addr[ETH_ALEN];
> +	u8 alg;
> +	u8 keyidx;
> +	u8 hw_key_idx;
> +	u8 flags;
> +	u8 keylen;
> +	u8 key[32];
> +} __packed;
> +
> +struct sip_cmd_config {
> +	u16 center_freq;
> +	u16 duration;
> +} __packed;
> +
> +struct sip_cmd_bss_info_update {
> +	u8 bssid[ETH_ALEN];
> +	u16 isassoc;
> +	u32 beacon_int;
> +	u8 bssid_no;
> +} __packed;
> +
> +struct sip_evt_bootup {
> +	u16 tx_blksz;
> +	u8 mac_addr[ETH_ALEN];
> +	/* anything else ? */
> +} __packed;
> +
> +struct sip_cmd_setvif {
> +	u8 index;
> +	u8 mac[ETH_ALEN];
> +	u8 set;
> +	u8 op_mode;
> +	u8 is_p2p;
> +} __packed;
> +
> +enum esp_ieee80211_phytype {
> +	ESP_IEEE80211_T_CCK = 0,
> +	ESP_IEEE80211_T_OFDM = 1,
> +	ESP_IEEE80211_T_HT20_L = 2,
> +	ESP_IEEE80211_T_HT20_S = 3,
> +};
> +
> +struct sip_cmd_setsta {
> +	u8 ifidx;
> +	u8 index;
> +	u8 set;
> +	u8 phymode;
> +	u8 mac[ETH_ALEN];
> +	u16 aid;
> +	u8 ampdu_factor;
> +	u8 ampdu_density;
> +	u16 resv;
> +} __packed;
> +
> +struct sip_cmd_ps {
> +	u8 dtim_period;
> +	u8 max_sleep_period;
> +	u8 on;
> +	u8 resv;
> +} __packed;
> +
> +struct sip_cmd_suspend {
> +	u8 suspend;
> +	u8 resv[3];
> +} __packed;
> +
> +#define SIP_DUMP_RPBM_ERR	BIT(0)
> +#define SIP_RXABORT_FIXED	BIT(1)
> +#define SIP_SUPPORT_BGSCAN	BIT(2)
> +struct sip_evt_bootup2 {
> +	u16 tx_blksz;
> +	u8 mac_addr[ETH_ALEN];
> +	u16 rx_blksz;
> +	u8 credit_to_reserve;
> +	u8 options;
> +	s16 noise_floor;
> +	u8 resv[2];
> +	/* anything else ? */
> +} __packed;
> +
> +enum trc_ampdu_state {
> +	TRC_TX_AMPDU_STOPPED = 1,
> +	TRC_TX_AMPDU_OPERATIONAL,
> +	TRC_TX_AMPDU_WAIT_STOP,
> +	TRC_TX_AMPDU_WAIT_OPERATIONAL,
> +	TRC_TX_AMPDU_START,
> +};
> +
> +struct sip_evt_trc_ampdu {
> +	u8 state;
> +	u8 tid;
> +	u8 addr[ETH_ALEN];
> +} __packed;
> +
> +struct sip_cmd_set_wmm_params {
> +	u8 aci;
> +	u8 aifs;
> +	u8 ecw_min;
> +	u8 ecw_max;
> +	u16 txop_us;
> +} __packed;
> +
> +#define SIP_AMPDU_RX_START		0
> +#define SIP_AMPDU_RX_STOP		1
> +#define SIP_AMPDU_TX_OPERATIONAL	2
> +#define SIP_AMPDU_TX_STOP		3
> +
> +struct sip_cmd_ampdu_action {
> +	u8 action;
> +	u8 index;
> +	u8 tid;
> +	u8 win_size;
> +	u16 ssn;
> +	u8 addr[ETH_ALEN];
> +} __packed;
> +
> +#define SIP_TX_ST_OK		0
> +#define SIP_TX_ST_NOEB		1
> +#define SIP_TX_ST_ACKTO		2
> +#define SIP_TX_ST_ENCERR	3
> +
> +//NB: sip_tx_status must be 4 bytes aligned
> +struct sip_tx_status {
> +	u32 sip_seq;
> +	u8 errno;		/* success or failure code */
> +	u8 rate_index;
> +	char ack_signal;
> +	u8 pad;
> +} __packed;
> +
> +struct sip_evt_scan_report {
> +	u16 scan_id;
> +	u16 aborted;
> +} __packed;
> +
> +struct sip_evt_roc {
> +	u16 state;		//start:1, end :0
> +	u16 is_ok;
> +} __packed;
> +
> +struct sip_evt_txidle {
> +	u32 last_seq;
> +} __packed;
> +
> +struct sip_evt_noisefloor {
> +	s16 noise_floor;
> +	u16 pad;
> +} __packed;
> +/*  for mblk direct memory access, no need for sip_hdr. tx: first 2k for contrl msg,
> + *  rest of 14k for data.  rx, same.
> + */
> +
> +#endif				/* _SIP_COMMON_H_ */
> diff --git a/drivers/staging/esp8089/slc_host_register.h b/drivers/staging/esp8089/slc_host_register.h
> new file mode 100644
> index 000000000000..8cf139c0d7bc
> --- /dev/null
> +++ b/drivers/staging/esp8089/slc_host_register.h
> @@ -0,0 +1,263 @@
> +/*
> + * Copyright (c) 2011 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + *      Quentin Schulz <quentin.schulz at 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
> -- 
> 2.11.0
> 
> 
> _______________________________________________
> devel mailing list
> devel at linuxdriverproject.org
> http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


More information about the devel mailing list