[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20170725103140.sr5caiqq45c7gcfv@mwanda>
Date: Tue, 25 Jul 2017 13:31:41 +0300
From: Dan Carpenter <dan.carpenter@...cle.com>
To: Quentin Schulz <quentin.schulz@...e-electrons.com>
Cc: ulf.hansson@...aro.org, gregkh@...uxfoundation.org,
devel@...verdev.osuosl.org, hdegoede@...hat.com,
baolin.wang@...aro.org, linus.walleij@...aro.org,
linux-mmc@...r.kernel.org, adrian.hunter@...el.com,
linux-kernel@...r.kernel.org, wens@...e.org, icenowy@...c.xyz,
maxime.ripard@...e-electrons.com, shawn.lin@...k-chips.com
Subject: Re: [PATCH 1/2] staging: net: wireless: add ESP8089 WiFi driver
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@...hat.com>
> Cc: Icenowy Zheng <icenowy@...c.xyz>
> Signed-off-by: Quentin Schulz <quentin.schulz@...e-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@...e-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@...e-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@...e-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@...e-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@...e-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@...e-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@...e-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@...e-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@...e-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 = ¶ms->ssn;
> + u8 buf_size = params->buf_size;
> +
> + switch (action) {
> + case IEEE80211_AMPDU_TX_START:
> + chandef = &epub->hw->conf.chandef;
> + if (mod_support_no_txampdu() || !sta->ht_cap.ht_supported ||
> + cfg80211_get_chandef_type(chandef) == NL80211_CHAN_NO_HT)
> + return -EOPNOTSUPP;
> +
> + dev_dbg(epub->dev, "%s TX START, addr:%pM,tid:%u,state:%d\n",
> + __func__, sta->addr, tid, tid_info->state);
> +
> + spin_lock_bh(&epub->tx_ampdu_lock);
> +
> + ESSERT(tid_info->state == ESP_TID_STATE_TRIGGER);
> + *ssn = tid_info->ssn;
> + tid_info->state = ESP_TID_STATE_PROGRESS;
> +
> + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
> + spin_unlock_bh(&epub->tx_ampdu_lock);
> +
> + return 0;
> +
> + case IEEE80211_AMPDU_TX_STOP_CONT:
> + dev_dbg(epub->dev, "%s TX STOP, addr:%pM,tid:%u,state:%d\n",
> + __func__, sta->addr, tid, tid_info->state);
> +
> + spin_lock_bh(&epub->tx_ampdu_lock);
> +
> + case IEEE80211_AMPDU_TX_STOP_FLUSH:
> + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
> + if (tid_info->state == ESP_TID_STATE_WAIT_STOP)
> + tid_info->state = ESP_TID_STATE_STOP;
> + else
> + tid_info->state = ESP_TID_STATE_INIT;
> +
> + if (action == IEEE80211_AMPDU_TX_STOP_CONT) {
> + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
> + spin_unlock_bh(&epub->tx_ampdu_lock);
> + }
> +
> + return sip_send_ampdu_action(epub, SIP_AMPDU_TX_STOP, sta->addr,
> + tid, node->ifidx, 0);
> +
> + case IEEE80211_AMPDU_TX_OPERATIONAL:
> + dev_dbg(epub->dev,
> + "%s TX OPERATION, addr:%pM,tid:%u,state:%d\n", __func__,
> + sta->addr, tid, tid_info->state);
> +
> + spin_lock_bh(&epub->tx_ampdu_lock);
> +
> + if (tid_info->state != ESP_TID_STATE_PROGRESS) {
> + if (tid_info->state == ESP_TID_STATE_INIT) {
> + printk(KERN_ERR "%s WIFI RESET, IGNORE\n",
> + __func__);
> + spin_unlock_bh(&epub->tx_ampdu_lock);
> + return -ENETRESET;
> + }
> +
> + ESSERT(0);
> + }
> +
> + tid_info->state = ESP_TID_STATE_OPERATIONAL;
> + spin_unlock_bh(&epub->tx_ampdu_lock);
> +
> + return sip_send_ampdu_action(epub, SIP_AMPDU_TX_OPERATIONAL,
> + sta->addr, tid, node->ifidx,
> + buf_size);
> +
> + case IEEE80211_AMPDU_RX_START:
> + chandef = &epub->hw->conf.chandef;
> + if (mod_support_no_rxampdu() || !sta->ht_cap.ht_supported ||
> + cfg80211_get_chandef_type(chandef) == NL80211_CHAN_NO_HT)
> + return -EOPNOTSUPP;
> +
> + dev_dbg(epub->dev, "%s RX START %pM tid %u %u\n", __func__,
> + sta->addr, tid, *ssn);
> +
> + return sip_send_ampdu_action(epub, SIP_AMPDU_RX_START, sta->addr,
> + tid, *ssn, 64);
> +
> + case IEEE80211_AMPDU_RX_STOP:
> + dev_dbg(epub->dev, "%s RX STOP %pM tid %u\n", __func__,
> + sta->addr, tid);
> +
> + return sip_send_ampdu_action(epub, SIP_AMPDU_RX_STOP, sta->addr,
> + tid, 0, 0);
> +
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static void esp_tx_work(struct work_struct *work)
> +{
> + struct esp_pub *epub = container_of(work, struct esp_pub, tx_work);
> +
> + mutex_lock(&epub->tx_mtx);
> + sip_txq_process(epub);
> + mutex_unlock(&epub->tx_mtx);
> +}
> +
> +static const struct ieee80211_ops esp_mac80211_ops = {
> + .tx = esp_op_tx,
> + .start = esp_op_start,
> + .stop = esp_op_stop,
> + .add_interface = esp_op_add_interface,
> + .remove_interface = esp_op_remove_interface,
> + .config = esp_op_config,
> +
> + .bss_info_changed = esp_op_bss_info_changed,
> + .configure_filter = esp_op_configure_filter,
> + .set_key = esp_op_set_key,
> + .set_rts_threshold = esp_op_set_rts_threshold,
> + .sta_notify = esp_op_sta_notify,
> + .conf_tx = esp_op_conf_tx,
> + .change_interface = esp_op_change_interface,
> + .get_tsf = esp_op_get_tsf,
> + .set_tsf = esp_op_set_tsf,
> + .reset_tsf = esp_op_reset_tsf,
> + .rfkill_poll = esp_op_rfkill_poll,
> +#ifdef HW_SCAN
> + .hw_scan = esp_op_hw_scan,
> + .remain_on_channel = esp_op_remain_on_channel,
> + .cancel_remain_on_channel = esp_op_cancel_remain_on_channel,
> +#endif
> + .ampdu_action = esp_op_ampdu_action,
> + .sta_add = esp_op_sta_add,
> + .sta_remove = esp_op_sta_remove,
> + .set_bitrate_mask = esp_op_set_bitrate_mask,
> + .flush = esp_op_flush,
> +};
> +
> +struct esp_pub *esp_pub_alloc_mac80211(struct device *dev)
> +{
> + struct ieee80211_hw *hw;
> + struct esp_pub *epub;
> +
> + hw = ieee80211_alloc_hw(sizeof(struct esp_pub), &esp_mac80211_ops);
> + if (!hw)
> + return ERR_PTR(-ENOMEM);
The caller expects a NULL return.
> +
> + /* FIXME: useless if hw_scan is defined, incorrect if hw_scan is undefined*/
> +#ifdef HW_SCAN
> + hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
> +#endif
> +
> + epub = hw->priv;
> + memset(epub, 0, sizeof(*epub));
> +
> + epub->hw = hw;
> + SET_IEEE80211_DEV(hw, dev);
> + epub->dev = dev;
> +
> + skb_queue_head_init(&epub->txq);
> + skb_queue_head_init(&epub->txdoneq);
> + skb_queue_head_init(&epub->rxq);
> +
> + spin_lock_init(&epub->tx_ampdu_lock);
> + spin_lock_init(&epub->rx_ampdu_lock);
> + spin_lock_init(&epub->tx_lock);
> + mutex_init(&epub->tx_mtx);
> + spin_lock_init(&epub->rx_lock);
> +
> + INIT_WORK(&epub->tx_work, esp_tx_work);
> +
> + epub->esp_wkq = create_singlethread_workqueue("esp_wkq");
> +
> + if (!epub->esp_wkq)
> + return ERR_PTR(-ENOMEM);
goto free_hw;
> +
> + epub->scan_permit_valid = false;
> + INIT_DELAYED_WORK(&epub->scan_timeout_work, hw_scan_timeout_report);
> +
> + return epub;
> +}
> +
> +int esp_pub_dealloc_mac80211(struct esp_pub *epub)
> +{
> + set_bit(ESP_WL_FLAG_RFKILL, &epub->wl.flags);
> +
> + destroy_workqueue(epub->esp_wkq);
> + mutex_destroy(&epub->tx_mtx);
> +
> + if (epub->hw)
> + ieee80211_free_hw(epub->hw);
> +
> + return 0;
> +}
> +
> +/* 2G band channels */
> +static struct ieee80211_channel esp_channels_2ghz[] = {
> + {.hw_value = 1, .center_freq = 2412, .max_power = 25},
> + {.hw_value = 2, .center_freq = 2417, .max_power = 25},
> + {.hw_value = 3, .center_freq = 2422, .max_power = 25},
> + {.hw_value = 4, .center_freq = 2427, .max_power = 25},
> + {.hw_value = 5, .center_freq = 2432, .max_power = 25},
> + {.hw_value = 6, .center_freq = 2437, .max_power = 25},
> + {.hw_value = 7, .center_freq = 2442, .max_power = 25},
> + {.hw_value = 8, .center_freq = 2447, .max_power = 25},
> + {.hw_value = 9, .center_freq = 2452, .max_power = 25},
> + {.hw_value = 10, .center_freq = 2457, .max_power = 25},
> + {.hw_value = 11, .center_freq = 2462, .max_power = 25},
> + {.hw_value = 12, .center_freq = 2467, .max_power = 25},
> + {.hw_value = 13, .center_freq = 2472, .max_power = 25},
> +};
> +
> +/* 11G rate */
> +static struct ieee80211_rate esp_rates_2ghz[] = {
> + {
> + .bitrate = 10,
> + .hw_value = CONF_HW_BIT_RATE_1MBPS,
> + .hw_value_short = CONF_HW_BIT_RATE_1MBPS,
> + },
> + {
> + .bitrate = 20,
> + .hw_value = CONF_HW_BIT_RATE_2MBPS,
> + .hw_value_short = CONF_HW_BIT_RATE_2MBPS,
> + .flags = IEEE80211_RATE_SHORT_PREAMBLE},
> + {
> + .bitrate = 55,
> + .hw_value = CONF_HW_BIT_RATE_5_5MBPS,
> + .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS,
> + .flags = IEEE80211_RATE_SHORT_PREAMBLE},
> + {
> + .bitrate = 110,
> + .hw_value = CONF_HW_BIT_RATE_11MBPS,
> + .hw_value_short = CONF_HW_BIT_RATE_11MBPS,
> + .flags = IEEE80211_RATE_SHORT_PREAMBLE},
> + {
> + .bitrate = 60,
> + .hw_value = CONF_HW_BIT_RATE_6MBPS,
> + .hw_value_short = CONF_HW_BIT_RATE_6MBPS,
> + },
> + {
> + .bitrate = 90,
> + .hw_value = CONF_HW_BIT_RATE_9MBPS,
> + .hw_value_short = CONF_HW_BIT_RATE_9MBPS,
> + },
> + {
> + .bitrate = 120,
> + .hw_value = CONF_HW_BIT_RATE_12MBPS,
> + .hw_value_short = CONF_HW_BIT_RATE_12MBPS,
> + },
> + {
> + .bitrate = 180,
> + .hw_value = CONF_HW_BIT_RATE_18MBPS,
> + .hw_value_short = CONF_HW_BIT_RATE_18MBPS,
> + },
> + {
> + .bitrate = 240,
> + .hw_value = CONF_HW_BIT_RATE_24MBPS,
> + .hw_value_short = CONF_HW_BIT_RATE_24MBPS,
> + },
> + {
> + .bitrate = 360,
> + .hw_value = CONF_HW_BIT_RATE_36MBPS,
> + .hw_value_short = CONF_HW_BIT_RATE_36MBPS,
> + },
> + {
> + .bitrate = 480,
> + .hw_value = CONF_HW_BIT_RATE_48MBPS,
> + .hw_value_short = CONF_HW_BIT_RATE_48MBPS,
> + },
> + {
> + .bitrate = 540,
> + .hw_value = CONF_HW_BIT_RATE_54MBPS,
> + .hw_value_short = CONF_HW_BIT_RATE_54MBPS,
> + },
> +};
> +
> +static struct ieee80211_sta_ht_cap esp_ht_cap_2ghz = {
> + .cap = IEEE80211_HT_CAP_DSSSCCK40 | IEEE80211_HT_CAP_SM_PS |
> + IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20,
> + .ht_supported = true,
> + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
> + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
> + .mcs = {
> + .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
> + },
> +};
> +
> +static void esp_pub_init_mac80211(struct esp_pub *epub)
> +{
> + struct ieee80211_hw *hw = epub->hw;
> + struct ieee80211_supported_band *sbands =
> + &epub->wl.sbands[NL80211_BAND_2GHZ];
> +
> + static const u32 cipher_suites[] = {
> + WLAN_CIPHER_SUITE_WEP40,
> + WLAN_CIPHER_SUITE_WEP104,
> + WLAN_CIPHER_SUITE_TKIP,
> + WLAN_CIPHER_SUITE_CCMP,
> + };
> +
> + hw->max_listen_interval = 10;
> +
> + ieee80211_hw_set(hw, SIGNAL_DBM);
> + ieee80211_hw_set(hw, HAS_RATE_CONTROL);
> + ieee80211_hw_set(hw, SUPPORTS_PS);
> + ieee80211_hw_set(hw, AMPDU_AGGREGATION);
> + ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
> + hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF;
> + hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF;
> +
> + hw->wiphy->cipher_suites = cipher_suites;
> + hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
> + hw->wiphy->max_scan_ie_len = epub->sip->tx_blksz -
> + sizeof(struct sip_hdr) - sizeof(struct sip_cmd_scan);
> +
> + /* ONLY station for now, support P2P soon... */
> + /* FIXME: is p2p really supported? */
> + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_P2P_GO) |
> + BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_STATION) |
> + BIT(NL80211_IFTYPE_AP);
> +
> + hw->wiphy->max_scan_ssids = 2;
> + hw->wiphy->max_remain_on_channel_duration = 5000;
> +
> + atomic_set(&epub->wl.off, 1);
> +
> + sbands->band = NL80211_BAND_2GHZ;
> + sbands->channels = esp_channels_2ghz;
> + sbands->bitrates = esp_rates_2ghz;
> + sbands->n_channels = ARRAY_SIZE(esp_channels_2ghz);
> + sbands->n_bitrates = ARRAY_SIZE(esp_rates_2ghz);
> + sbands->ht_cap = esp_ht_cap_2ghz;
> +
> + hw->wiphy->bands[NL80211_BAND_2GHZ] = sbands;
> +
> + /*no fragment */
> + hw->wiphy->frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
> +
> + /* handle AC queue in f/w */
> + hw->queues = 4;
> + hw->max_rates = 4;
> +
> + hw->vif_data_size = sizeof(struct esp_vif);
> + hw->sta_data_size = sizeof(struct esp_node);
> +}
> +
> +int esp_register_mac80211(struct esp_pub *epub)
> +{
> + int ret, idx;
> + u8 *p2p_addr;
> +
> + esp_pub_init_mac80211(epub);
> +
> + /* FIXME: esp_mac_addr */
> + epub->hw->wiphy->addresses = (struct mac_address *)esp_mac_addr;
> +
> + memcpy(epub->hw->wiphy->addresses[0].addr, epub->mac_addr, ETH_ALEN);
> + memcpy(epub->hw->wiphy->addresses[1].addr, epub->mac_addr, ETH_ALEN);
> + p2p_addr = epub->hw->wiphy->addresses[1].addr;
> +
> + for (idx = 0; idx < 8 * ETH_ALEN; idx++) {
> + p2p_addr[0] = epub->mac_addr[0] | 0x02;
> + p2p_addr[0] ^= idx << 2;
> + if (strncmp(p2p_addr, epub->mac_addr, 6))
> + break;
> + }
> +
> + epub->hw->wiphy->n_addresses = 2;
> +
> + ret = ieee80211_register_hw(epub->hw);
> + if (ret < 0) {
> + printk("unable to register mac80211 hw: %d\n", ret);
> + return ret;
> + }
> +
> + set_bit(ESP_WL_FLAG_HW_REGISTERED, &epub->wl.flags);
> +
> + return ret;
return 0;
> +}
> +
> +static u8 getaddr_index(u8 *addr, struct esp_pub *epub)
> +{
> + int i;
> +
> + for (i = 0; i < ESP_PUB_MAX_VIF; i++)
> + if (!memcmp(addr, epub->hw->wiphy->addresses[i].addr, ETH_ALEN))
> + return i;
> +
> + return ESP_PUB_MAX_VIF;
> +}
> diff --git a/drivers/staging/esp8089/esp_mac80211.h b/drivers/staging/esp8089/esp_mac80211.h
> new file mode 100644
> index 000000000000..03de0b0a4223
> --- /dev/null
> +++ b/drivers/staging/esp8089/esp_mac80211.h
> @@ -0,0 +1,33 @@
> +/*
> + * Copyright (c) 2011 - 2014 Espressif System.
> + * Copyright (c) 2017 Free Electrons
> + * Quentin Schulz <quentin.schulz@...e-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@...e-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@...e-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@...e-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@...e-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@...e-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@...e-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@...e-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@...e-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@...e-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@...e-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@...e-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@...e-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@...uxdriverproject.org
> http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
Powered by blists - more mailing lists