Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932116AbdGUOrh (ORCPT ); Fri, 21 Jul 2017 10:47:37 -0400 Received: from mail.free-electrons.com ([62.4.15.54]:43798 "EHLO mail.free-electrons.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750849AbdGUOfV (ORCPT ); Fri, 21 Jul 2017 10:35:21 -0400 From: Quentin Schulz To: ulf.hansson@linaro.org, gregkh@linuxfoundation.org Cc: Quentin Schulz , linus.walleij@linaro.org, shawn.lin@rock-chips.com, adrian.hunter@intel.com, baolin.wang@linaro.org, hdegoede@redhat.com, maxime.ripard@free-electrons.com, thomas.petazzoni@free-electrons.com, linux-kernel@vger.kernel.org, linux-mmc@vger.kernel.org, devel@driverdev.osuosl.org, icenowy@aosc.xyz, wens@csie.org Subject: [PATCH 1/2] staging: net: wireless: add ESP8089 WiFi driver Date: Fri, 21 Jul 2017 16:35:01 +0200 Message-Id: <20170721143502.1991-2-quentin.schulz@free-electrons.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20170721143502.1991-1-quentin.schulz@free-electrons.com> References: <20170721143502.1991-1-quentin.schulz@free-electrons.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 191823 Lines: 7105 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 Cc: Icenowy Zheng Signed-off-by: Quentin Schulz --- 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 + * + * 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 +#include +#include +#include +#include + +#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 + * + * 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 + * + * 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 +#include + +#include +#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 + * + * 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 +#include +#include + +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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "esp_file.h" +#include "esp_debug.h" +#include "esp_sif.h" + +/* TODO use proper module param for each value instead of a big one */ +static char *modparam_init_data_conf; +module_param_named(config, modparam_init_data_conf, charp, 0444); +MODULE_PARM_DESC(config, "Firmware init config string (format: key=value;)"); + +struct esp_init_table_elem esp_init_table[MAX_ATTR_NUM] = { + /* Crystal type: + * 0: 40MHz (default) + * 1: 26MHz (ESP8266 ESP-12F) + */ + {"crystal_26M_en", 48, 0}, + /* Output crystal clock to pin: + * 0: None + * 1: GPIO1 + * 2: URXD0 + */ + {"test_xtal", 49, 0}, + /* Host SDIO mode: + * 0: Auto by pin strapping + * 1: SDIO data output on negative edges (SDIO v1.1) + * 2: SDIO data output on positive edges (SDIO v2.0) + */ + {"sdio_configure", 50, 2}, + /* WiFi/Bluetooth co-existence with BK3515A BT chip + * 0: None + * 1: GPIO0->WLAN_ACTIVE, MTMS->BT_ACTIVE, MTDI->BT_PRIORITY, + * U0TXD->ANT_SEL_BT, U0RXD->ANT_SEL_WIFI + */ + {"bt_configure", 51, 0}, + /* Antenna selection: + * 0: Antenna is for WiFi + * 1: Antenna is for Bluetooth + */ + {"bt_protocol", 52, 0}, + /* Dual antenna configuration mode: + * 0: None + * 1: U0RXD + XPD_DCDC + * 2: U0RXD + GPIO0 + * 3: U0RXD + U0TXD + */ + {"dual_ant_configure", 53, 0}, + /* Firmware debugging output pin: + * 0: None + * 1: UART TX on GPIO2 + * 2: UART TX on U0TXD + */ + {"test_uart_configure", 54, 2}, + /* Whether to share crystal clock with BT (in sleep mode): + * 0: no + * 1: always on + * 2: automatically on according to XPD_DCDC + */ + {"share_xtal", 55, 0}, + /* Allow chip to be woken up during sleep on pin: + * 0: None + * 1: XPD_DCDC + * 2: GPIO0 + * 3: Both XPD_DCDC and GPIO0 + */ + {"gpio_wake", 56, 0}, + {"no_auto_sleep", 57, 0}, + {"speed_suspend", 58, 0}, + {"attr11", -1, -1}, + {"attr12", -1, -1}, + {"attr13", -1, -1}, + {"attr14", -1, -1}, + {"attr15", -1, -1}, + //attr that is not send to target + /* Allow chip to be reset by GPIO pin: + * 0: no + * 1: yes + */ + {"ext_rst", -1, 0}, + {"wakeup_gpio", -1, 12}, + {"ate_test", -1, 0}, + {"attr19", -1, -1}, + {"attr20", -1, -1}, + {"attr21", -1, -1}, + {"attr22", -1, -1}, + {"attr23", -1, -1}, +}; + +/* update init config table */ +static int set_init_config_attr(const char *attr, int attr_len, short value) +{ + int i; + + for (i = 0; i < MAX_ATTR_NUM; i++) + if (!memcmp(esp_init_table[i].attr, attr, attr_len)) { + if (value < 0 || value > 255) { + printk("%s: attribute value for %s is out of range", + __func__, esp_init_table[i].attr); + return -1; + } + + esp_init_table[i].value = value; + return 0; + } + + return -1; +} + +static int update_init_config_attr(const char *attr, int attr_len, + const char *val, int val_len) +{ + char digits[4]; + short value; + int i; + + for (i = 0; i < sizeof(digits) - 1 && i < val_len; i++) + digits[i] = val[i]; + + digits[i] = 0; + + if (kstrtou16(digits, 10, &value) < 0) { + printk("%s: invalid attribute value: %s", __func__, digits); + return -1; + } + + return set_init_config_attr(attr, attr_len, value); +} + +/* export config table settings to SDIO driver */ +static void record_init_config(void) +{ + int i; + + for (i = 0; i < MAX_ATTR_NUM; i++) { + if (esp_init_table[i].value < 0) + continue; + + if (!strcmp(esp_init_table[i].attr, "share_xtal")) + sif_record_bt_config(esp_init_table[i].value); + else if (!strcmp(esp_init_table[i].attr, "ext_rst")) + sif_record_rst_config(esp_init_table[i].value); + else if (!strcmp(esp_init_table[i].attr, "wakeup_gpio")) + sif_record_wakeup_gpio_config(esp_init_table[i].value); + else if (!strcmp(esp_init_table[i].attr, "ate_test")) + sif_record_ate_config(esp_init_table[i].value); + } +} + +int request_init_conf(struct device *dev) +{ + char *attr, *str, *p; + int attr_len, str_len; + int ret = 0; + u32 val; + + /* Check for any parameters passed through devicetree (or acpi) */ + if (!device_property_read_u32(dev, "esp,crystal-26M-en", &val)) + set_init_config_attr("crystal_26M_en", strlen("crystal_26M_en"), + val); + + /* parse optional parameter in the form of key1=value,key2=value,.. */ + attr = NULL; + attr_len = 0; + str_len = 0; + for (p = str = modparam_init_data_conf; p && *p; p++) { + if (*p == '=') { + attr = str; + attr_len = str_len; + + str = p + 1; + str_len = 0; + } else if (*p == ',' || *p == ';') { + if (attr_len) + ret |= update_init_config_attr(attr, attr_len, + str, str_len); + + str = p + 1; + attr_len = 0; + str_len = 0; + } else { + str_len++; + } + } + + if (attr_len && str != attr) + ret |= update_init_config_attr(attr, attr_len, str, str_len); + + record_init_config(); + + return ret; +} + +void fix_init_data(u8 *init_data_buf, int buf_size) +{ + int i; + + for (i = 0; i < MAX_FIX_ATTR_NUM; i++) { + if (esp_init_table[i].offset > -1 && + esp_init_table[i].offset < buf_size && + esp_init_table[i].value > -1) + *(u8 *)(init_data_buf + esp_init_table[i].offset) = esp_init_table[i].value; + } +} diff --git a/drivers/staging/esp8089/esp_file.h b/drivers/staging/esp8089/esp_file.h new file mode 100644 index 000000000000..ba9946d64c32 --- /dev/null +++ b/drivers/staging/esp8089/esp_file.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2010 - 2014 Espressif System. + * Copyright (c) 2017 Free Electrons + * Quentin Schulz + * + * 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 + +#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 + * + * 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 + * + * 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 +#include "esp_sif.h" +#include "slc_host_register.h" +#include "esp_debug.h" + +int esp_common_read(struct esp_pub *epub, u8 *buf, u32 len, int sync, + bool noround) +{ + if (sync) + return sif_lldesc_read_sync(epub, buf, len); + + return sif_lldesc_read_raw(epub, buf, len, noround); +} + +int esp_common_write(struct esp_pub *epub, u8 *buf, u32 len, int sync) +{ + if (sync) + return sif_lldesc_write_sync(epub, buf, len); + + return sif_lldesc_write_raw(epub, buf, len); +} + +int esp_common_read_with_addr(struct esp_pub *epub, u32 addr, u8 *buf, u32 len, + int sync) +{ + if (sync) + return sif_io_sync(epub, addr, buf, len, SIF_FROM_DEVICE | + SIF_SYNC | SIF_BYTE_BASIS | SIF_INC_ADDR); + + return sif_io_raw(epub, addr, buf, len, SIF_FROM_DEVICE | + SIF_BYTE_BASIS | SIF_INC_ADDR); +} + +int esp_common_write_with_addr(struct esp_pub *epub, u32 addr, u8 *buf, + u32 len, int sync) +{ + if (sync) + return sif_io_sync(epub, addr, buf, len, SIF_TO_DEVICE | + SIF_SYNC | SIF_BYTE_BASIS | SIF_INC_ADDR); + + return sif_io_raw(epub, addr, buf, len, SIF_TO_DEVICE | SIF_BYTE_BASIS | + SIF_INC_ADDR); +} + +int esp_common_readbyte_with_addr(struct esp_pub *epub, u32 addr, u8 *buf, + int sync) +{ + int res; + + if (sync) + sif_lock_bus(epub); + + *buf = sdio_io_readb(epub, addr, &res); + + if (sync) + sif_unlock_bus(epub); + + return res; +} + +int esp_common_writebyte_with_addr(struct esp_pub *epub, u32 addr, u8 buf, + int sync) +{ + int res; + + if (sync) + sif_lock_bus(epub); + + sdio_io_writeb(epub, buf, addr, &res); + + if (sync) + sif_unlock_bus(epub); + + return res; +} + +enum _SDIO_INTR_MODE { + SDIO_INTR_IB = 0, + SDIO_INTR_OOB_TOGGLE, + SDIO_INTR_OOB_HIGH_LEVEL, + SDIO_INTR_OOB_LOW_LEVEL, +}; + +#define GEN_GPIO_SEL(_gpio_num, _sel_func, _intr_mode, _offset) (((_offset) << 9) | ((_intr_mode) << 7) | ((_sel_func) << 4) | (_gpio_num)) +//bit[3:0] = gpio num, 2 +//bit[6:4] = gpio sel func, 0 +//bit[8:7] = gpio intr mode, SDIO_INTR_OOB_TOGGLE +//bit[15:9] = register offset, 0x38 + +u16 gpio_sel_sets[17] = { + GEN_GPIO_SEL(0, 0, SDIO_INTR_OOB_TOGGLE, 0x34), //GPIO0 + GEN_GPIO_SEL(1, 3, SDIO_INTR_OOB_TOGGLE, 0x18), //U0TXD + GEN_GPIO_SEL(2, 0, SDIO_INTR_OOB_TOGGLE, 0x38), //GPIO2 + GEN_GPIO_SEL(3, 3, SDIO_INTR_OOB_TOGGLE, 0x14), //U0RXD + GEN_GPIO_SEL(4, 0, SDIO_INTR_OOB_TOGGLE, 0x3C), //GPIO4 + GEN_GPIO_SEL(5, 0, SDIO_INTR_OOB_TOGGLE, 0x40), //GPIO5 + GEN_GPIO_SEL(6, 3, SDIO_INTR_OOB_TOGGLE, 0x1C), //SD_CLK + GEN_GPIO_SEL(7, 3, SDIO_INTR_OOB_TOGGLE, 0x20), //SD_DATA0 + GEN_GPIO_SEL(8, 3, SDIO_INTR_OOB_TOGGLE, 0x24), //SD_DATA1 + GEN_GPIO_SEL(9, 3, SDIO_INTR_OOB_TOGGLE, 0x28), //SD_DATA2 + GEN_GPIO_SEL(10, 3, SDIO_INTR_OOB_TOGGLE, 0x2C), //SD_DATA3 + GEN_GPIO_SEL(11, 3, SDIO_INTR_OOB_TOGGLE, 0x30), //SD_CMD + GEN_GPIO_SEL(12, 3, SDIO_INTR_OOB_TOGGLE, 0x04), //MTDI + GEN_GPIO_SEL(13, 3, SDIO_INTR_OOB_TOGGLE, 0x08), //MTCK + GEN_GPIO_SEL(14, 3, SDIO_INTR_OOB_TOGGLE, 0x0C), //MTMS + GEN_GPIO_SEL(15, 3, SDIO_INTR_OOB_TOGGLE, 0x10), //MTDO + //pls do not change sel before, if you want to change intr mode,change the one blow + //GEN_GPIO_SEL(2, 0, SDIO_INTR_OOB_TOGGLE, 0x38) + GEN_GPIO_SEL(2, 0, SDIO_INTR_OOB_LOW_LEVEL, 0x38) +}; + +int sif_interrupt_target(struct esp_pub *epub, u8 index) +{ + u8 low_byte = BIT(index); + return esp_common_writebyte_with_addr(epub, SLC_HOST_CONF_W4 + 2, + low_byte, ESP_SIF_NOSYNC); +} + +void check_target_id(struct esp_pub *epub) +{ + u32 date; + int i; + u16 gpio_sel; + u8 byte2 = 0, byte3 = 0; + + if (!epub || !epub->sif) + return; + + sif_lock_bus(epub); + + for (i = 0; i < 4; i++) { + esp_common_readbyte_with_addr(epub, SLC_HOST_DATE + i, + (u8 *)&date + i, ESP_SIF_NOSYNC); + esp_common_readbyte_with_addr(epub, SLC_HOST_ID + i, + (u8 *)&epub->sif->target_id + i, + ESP_SIF_NOSYNC); + } + + sif_unlock_bus(epub); + + switch (epub->sif->target_id) { + case 0x100: + epub->sif->slc_window_end_addr = 0x20000; + break; + case 0x600: + epub->sif->slc_window_end_addr = 0x20000 - 0x800; + + if (sif_get_bt_config() == 1 && sif_get_rst_config() != 1) { + u8 gpio_num = sif_get_wakeup_gpio_config(); + gpio_sel = gpio_sel_sets[gpio_num]; + byte2 = gpio_sel; + byte3 = gpio_sel >> 8; + } + + sif_lock_bus(epub); + esp_common_writebyte_with_addr(epub, SLC_HOST_CONF_W1, 0, + ESP_SIF_NOSYNC); + esp_common_writebyte_with_addr(epub, SLC_HOST_CONF_W1 + 1, 0, + ESP_SIF_NOSYNC); + esp_common_writebyte_with_addr(epub, SLC_HOST_CONF_W1 + 2, + byte2, ESP_SIF_NOSYNC); + esp_common_writebyte_with_addr(epub, SLC_HOST_CONF_W1 + 3, + byte3, ESP_SIF_NOSYNC); + sif_unlock_bus(epub); + break; + default: + epub->sif->slc_window_end_addr = 0x20000; + break; + } +} + +u32 sif_get_blksz(struct esp_pub *epub) +{ + if (!epub || !epub->sif) + return 512; + + return epub->sif->slc_blk_sz; +} + +void sif_dsr(struct sdio_func *func) +{ + struct esp_sdio_ctrl *sctrl = sdio_get_drvdata(func); + struct slc_host_regs *regs = &sctrl->slc_regs; + int ret; + + sdio_release_host(sctrl->func); + + sif_lock_bus(sctrl->epub); + memset(regs, 0, sizeof(struct slc_host_regs)); + + ret = esp_common_read_with_addr(sctrl->epub, REG_SLC_HOST_BASE + 8, + (u8 *)regs, sizeof(*regs), + ESP_SIF_NOSYNC); + + if (regs->intr_raw & SLC_HOST_RX_ST && !ret) + esp_dsr(sctrl->epub); + else + sif_unlock_bus(sctrl->epub); + + /* FIXME: missing unlock_bus? */ + sdio_claim_host(func); + atomic_set(&sctrl->irq_handling, 0); +} + +struct slc_host_regs *sif_get_regs(struct esp_pub *epub) +{ + if (!epub || !epub->sif) + return NULL; + + return &epub->sif->slc_regs; +} + +void sif_disable_target_interrupt(struct esp_pub *epub) +{ + if (!epub || !epub->sif || !epub->sif->func) + return; + + sif_lock_bus(epub); +#ifdef HOST_RESET_BUG + mdelay(10); +#endif + memset(epub->sif->dma_buffer, 0, sizeof(u32)); + esp_common_write_with_addr(epub, SLC_HOST_INT_ENA, + epub->sif->dma_buffer, sizeof(u32), + ESP_SIF_NOSYNC); +#ifdef HOST_RESET_BUG + mdelay(10); +#endif + + sif_unlock_bus(epub); + + mdelay(1); + + sif_lock_bus(epub); + sif_interrupt_target(epub, 7); + sif_unlock_bus(epub); +} + +/* FIXME: MEH */ + +static int bt_config; +void sif_record_bt_config(int value) +{ + bt_config = value; +} + +int sif_get_bt_config(void) +{ + return bt_config; +} + +static int rst_config; +void sif_record_rst_config(int value) +{ + rst_config = value; +} + +int sif_get_rst_config(void) +{ + return rst_config; +} + +static int ate_test; +void sif_record_ate_config(int value) +{ + ate_test = value; +} + +int sif_get_ate_config(void) +{ + return ate_test; +} + +static int wakeup_gpio = 12; +void sif_record_wakeup_gpio_config(int value) +{ + wakeup_gpio = value; +} + +int sif_get_wakeup_gpio_config(void) +{ + return wakeup_gpio; +} diff --git a/drivers/staging/esp8089/esp_mac80211.c b/drivers/staging/esp8089/esp_mac80211.c new file mode 100644 index 000000000000..fd5049fc1f6b --- /dev/null +++ b/drivers/staging/esp8089/esp_mac80211.c @@ -0,0 +1,1496 @@ +/* + * Copyright (c) 2011 - 2014 Espressif System. + * Copyright (c) 2017 Free Electrons + * Quentin Schulz + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "esp_pub.h" +#include "esp_sip.h" +#include "esp_ctrl.h" +#include "esp_sif.h" +#include "esp_debug.h" +#include "esp_wl.h" +#include "esp_utils.h" + +static u8 esp_mac_addr[ETH_ALEN * 2]; +static u8 getaddr_index(u8 *addr, struct esp_pub *epub); + +/*Handler that 802.11 module calls for each transmitted frame. +skb contains the buffer starting from the IEEE 802.11 header. +The low-level driver should send the frame out based on +configuration in the TX control data. This handler should, +preferably, never fail and stop queues appropriately. +Must be atomic.*/ +static void esp_op_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, struct sk_buff *skb) +{ + struct esp_pub *epub = (struct esp_pub *)hw->priv; + + sip_tx_data_pkt_enqueue(epub, skb); + if (epub) + ieee80211_queue_work(hw, &epub->tx_work); +} + +/* + Called before the first netdevice attached to the hardware + 2934 * is enabled. This should turn on the hardware and must turn on + 2935 * frame reception (for possibly enabled monitor interfaces.) + 2936 * Returns negative error codes, these may be seen in userspace, + 2937 * or zero. + 2938 * When the device is started it should not have a MAC address + 2939 * to avoid acknowledging frames before a non-monitor device + 2940 * is added. + 2941 * Must be implemented and can sleep. +*/ +static int esp_op_start(struct ieee80211_hw *hw) +{ + struct esp_pub *epub; + + if (!hw || !hw->priv) { + return -EINVAL; + } + + epub = (struct esp_pub *)hw->priv; + + /*add rfkill poll function */ + + atomic_set(&epub->wl.off, 0); + wiphy_rfkill_start_polling(hw->wiphy); + + return 0; +} + +/* +Called after last netdevice attached to the hardware +2944 * is disabled. This should turn off the hardware (at least +2945 * it must turn off frame reception.) +2946 * May be called right after add_interface if that rejects +2947 * an interface. If you added any work onto the mac80211 workqueue +2948 * you should ensure to cancel it on this callback. +2949 * Must be implemented and can sleep. +*/ +static void esp_op_stop(struct ieee80211_hw *hw) +{ + struct esp_pub *epub; + + if (!hw || !hw->priv) { + return; + } + + epub = (struct esp_pub *)hw->priv; + atomic_set(&epub->wl.off, 1); + +#ifdef HOST_RESET_BUG + mdelay(200); +#endif + + if (epub->wl.scan_req) { + hw_scan_done(epub, true); + epub->wl.scan_req = NULL; + //msleep(2); + } + + /* FIXME: does this 'turn off frame reception'? */ + wiphy_rfkill_stop_polling(hw->wiphy); + /* FIXME: flush queues? */ +} + +static int esp_set_svif_mode(struct sip_cmd_setvif *svif, + enum nl80211_iftype type, bool p2p) +{ + switch (type) { + case NL80211_IFTYPE_STATION: + svif->op_mode = 0; + svif->is_p2p = p2p; + break; + + case NL80211_IFTYPE_AP: + svif->op_mode = 1; + svif->is_p2p = p2p; + break; + + case NL80211_IFTYPE_P2P_CLIENT: + svif->op_mode = 0; + svif->is_p2p = 1; + break; + + case NL80211_IFTYPE_P2P_GO: + svif->op_mode = 1; + svif->is_p2p = 1; + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +/* +Called when a netdevice attached to the hardware is +2973 * enabled. Because it is not called for monitor mode devices, @start +2974 * and @stop must be implemented. +2975 * The driver should perform any initialization it needs before +2976 * the device can be enabled. The initial configuration for the +2977 * interface is given in the conf parameter. +2978 * The callback may refuse to add an interface by returning a +2979 * negative error code (which will be seen in userspace.) +2980 * Must be implemented and can sleep. + */ +static int esp_op_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct esp_pub *epub = (struct esp_pub *)hw->priv; + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; + struct sip_cmd_setvif svif; + int ret; + + memcpy(svif.mac, vif->addr, ETH_ALEN); + svif.index = getaddr_index(vif->addr, epub); + evif->index = svif.index; + evif->epub = epub; + /* FIXME: why a need for evif? */ + epub->vif = vif; + svif.set = 1; + + if (svif.index == ESP_PUB_MAX_VIF) { + dev_err(epub->dev, "support for MAX %d interfaces\n", + ESP_PUB_MAX_VIF); + return -EOPNOTSUPP; + } + + if (BIT(svif.index) & epub->vif_slot) { + dev_err(epub->dev, "interface %d already used\n", svif.index); + return -EOPNOTSUPP; + } + + epub->vif_slot |= BIT(svif.index); + + ret = esp_set_svif_mode(&svif, vif->type, false); + if (ret < 0) { + dev_err(epub->dev, "no support for interface type %d\n", + vif->type); + return ret; + } + + sip_cmd(epub, SIP_CMD_SETVIF, (u8 *)&svif, sizeof(svif)); + + return 0; +} + +/* +Called when a netdevice changes type. This callback +2983 * is optional, but only if it is supported can interface types be +2984 * switched while the interface is UP. The callback may sleep. +2985 * Note that while an interface is being switched, it will not be +2986 * found by the interface iteration callbacks. + */ +static int esp_op_change_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum nl80211_iftype new_type, bool p2p) +{ + struct esp_pub *epub = (struct esp_pub *)hw->priv; + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; + struct sip_cmd_setvif svif; + int ret; + + memcpy(svif.mac, vif->addr, ETH_ALEN); + svif.index = evif->index; + svif.set = 2; + + ret = esp_set_svif_mode(&svif, new_type, p2p); + if (ret < 0) + return ret; + + sip_cmd(epub, SIP_CMD_SETVIF, (u8 *)&svif, sizeof(svif)); + + return 0; +} + +/* + Notifies a driver that an interface is going down. + 2989 * The @stop callback is called after this if it is the last interface + 2990 * and no monitor interfaces are present. + 2991 * When all interfaces are removed, the MAC address in the hardware + 2992 * must be cleared so the device no longer acknowledges packets, + 2993 * the mac_addr member of the conf structure is, however, set to the + 2994 * MAC address of the device going away. + 2995 * Hence, this callback must be implemented. It can sleep. + */ +static void esp_op_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct esp_pub *epub = (struct esp_pub *)hw->priv; + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; + struct sip_cmd_setvif svif = {0}; + + svif.index = evif->index; + epub->vif_slot &= ~BIT(svif.index); + + if (evif->ap_up) { + evif->beacon_interval = 0; + del_timer_sync(&evif->beacon_timer); + evif->ap_up = false; + } + + epub->vif = NULL; + evif->epub = NULL; + + sip_cmd(epub, SIP_CMD_SETVIF, (u8 *)&svif, sizeof(svif)); + /* TODO: clean up tx/rx queue */ +} + +/* FIXME: WTF? */ + +#define BEACON_TIM_SAVE_MAX 20 +u8 beacon_tim_saved[BEACON_TIM_SAVE_MAX]; +int beacon_tim_count; +static void beacon_tim_init(void) +{ + memset(beacon_tim_saved, 0, BEACON_TIM_SAVE_MAX); + beacon_tim_count = 0; +} + +static u8 beacon_tim_save(u8 this_tim) +{ + u8 all_tim = 0; + int i; + + beacon_tim_saved[beacon_tim_count] = this_tim; + + beacon_tim_count = (beacon_tim_count + 1) % BEACON_TIM_SAVE_MAX; + + for (i = 0; i < BEACON_TIM_SAVE_MAX; i++) + all_tim |= beacon_tim_saved[i]; + + return all_tim; +} + +static bool beacon_tim_alter(struct sk_buff *beacon) +{ + u8 *p, *tim_end; + u8 tim_count; + int len, remain_len; + struct ieee80211_mgmt *mgmt; + + if (!beacon) + return false; + + mgmt = (struct ieee80211_mgmt *)((u8 *)beacon->data); + + remain_len = beacon->len - ((u8 *)mgmt->u.beacon.variable - + (u8 *)mgmt + 12); + p = mgmt->u.beacon.variable; + + while (remain_len > 0) { + len = *(++p); + + if (*p == WLAN_EID_TIM) { // tim field + tim_end = p + len; + tim_count = *(++p); + p += 2; + //multicast + if (!tim_count) + *p |= 0x1; + + if (!(*p & 0xfe) && tim_end >= p + 1) { // we only support 8 sta in this case + p++; + *p = beacon_tim_save(*p); + } + + return tim_count == 0; + } + + p += len + 1; + remain_len -= 2 + len; + } + + return false; +} + +unsigned long init_jiffies; +unsigned long cycle_beacon_count; +static void drv_handle_beacon(unsigned long data) +{ + struct ieee80211_vif *vif = (struct ieee80211_vif *)data; + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; + struct sk_buff *beacon; + struct sk_buff *skb; + bool tim_reach; + + if (!evif->epub) + return; + + mdelay(2400 * (cycle_beacon_count % 25) % 10000 / 1000); + + beacon = ieee80211_beacon_get(evif->epub->hw, vif); + + tim_reach = beacon_tim_alter(beacon); + + if (beacon) + sip_tx_data_pkt_enqueue(evif->epub, beacon); + + if (cycle_beacon_count++ == 100) { + init_jiffies = jiffies; + cycle_beacon_count -= 100; + } + + mod_timer(&evif->beacon_timer, init_jiffies + + msecs_to_jiffies(cycle_beacon_count * + vif->bss_conf.beacon_int * 1024 / 1000)); + //FIXME:the packets must be sent at home channel + //send buffer mcast frames + if (tim_reach) { + skb = ieee80211_get_buffered_bc(evif->epub->hw, vif); + while (skb) { + sip_tx_data_pkt_enqueue(evif->epub, skb); + skb = ieee80211_get_buffered_bc(evif->epub->hw, vif); + } + } +} + +static void init_beacon_timer(struct ieee80211_vif *vif) +{ + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; + + beacon_tim_init(); + setup_timer(&evif->beacon_timer, drv_handle_beacon, (unsigned long)vif); //TBD, not init here... + cycle_beacon_count = 1; + init_jiffies = jiffies; + evif->beacon_timer.expires = init_jiffies + + msecs_to_jiffies(cycle_beacon_count * vif->bss_conf.beacon_int * + 1024 / 1000); + add_timer(&evif->beacon_timer); +} + +/* + Handler for configuration requests. IEEE 802.11 code calls this + 2998 * function to change hardware configuration, e.g., channel. + 2999 * This function should never fail but returns a negative error code + 3000 * if it does. The callback can sleep. + */ +static int esp_op_config(struct ieee80211_hw *hw, u32 changed) +{ + struct esp_pub *epub = (struct esp_pub *)hw->priv; + + if (changed & (IEEE80211_CONF_CHANGE_CHANNEL | + IEEE80211_CONF_CHANGE_IDLE)) + sip_send_config(epub, &hw->conf); + + return 0; +} + +/* +Handler for configuration requests related to BSS +3003 * parameters that may vary during BSS's lifespan, and may affect low +3004 * level driver (e.g. assoc/disassoc status, erp parameters). +3005 * This function should not be used if no BSS has been set, unless +3006 * for association indication. The @changed parameter indicates which +3007 * of the bss parameters has changed when a call is made. The callback +3008 * can sleep. + */ +static void esp_op_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed) +{ + struct esp_pub *epub = (struct esp_pub *)hw->priv; + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; + u8 *bssid = (u8 *)info->bssid; + bool assoc = info->assoc; + + // ieee80211_bss_conf(include/net/mac80211.h) is included in ieee80211_sub_if_data(net/mac80211/ieee80211_i.h) , does bssid=ieee80211_if_ap's ssid ? + + if (vif->type == NL80211_IFTYPE_STATION) { + if (changed & BSS_CHANGED_BSSID || + ((changed & BSS_CHANGED_ASSOC) && assoc)) { + evif->beacon_interval = info->aid; + memcpy(epub->wl.bssid, bssid, ETH_ALEN); + sip_send_bss_info_update(epub, evif, bssid, assoc); + } else if ((changed & BSS_CHANGED_ASSOC) && !assoc) { + evif->beacon_interval = 0; + memset(epub->wl.bssid, 0, ETH_ALEN); + sip_send_bss_info_update(epub, evif, bssid, assoc); + } + } else if (vif->type == NL80211_IFTYPE_AP) { + if (!(changed & BSS_CHANGED_BEACON_ENABLED) && + !(changed & BSS_CHANGED_BEACON_INT)) + return; + + if (info->enable_beacon && !evif->ap_up) { + evif->beacon_interval = info->beacon_int; + init_beacon_timer(vif); + sip_send_bss_info_update(epub, evif, bssid, 2); + evif->ap_up = true; + } else if (!info->enable_beacon && evif->ap_up && + !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) { + evif->beacon_interval = 0; + del_timer_sync(&evif->beacon_timer); + sip_send_bss_info_update(epub, evif, bssid, 2); + evif->ap_up = false; + } + } +} + +/* + Configure the device's RX filter. + 3015 * See the section "Frame filtering" for more information. + 3016 * This callback must be implemented and can sleep. + */ +static void esp_op_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, u64 multicast) +{ + struct esp_pub *epub = (struct esp_pub *)hw->priv; + + epub->rx_filter = 0; + + if (*total_flags & FIF_ALLMULTI) + epub->rx_filter |= FIF_ALLMULTI; + + *total_flags = epub->rx_filter; +} + +static bool is_cipher_suite_wep(u32 cipher) +{ + return (cipher == WLAN_CIPHER_SUITE_WEP40) || + (cipher == WLAN_CIPHER_SUITE_WEP104); +} + +/* + See the section "Hardware crypto acceleration" + 3029 * This callback is only called between add_interface and + 3030 * remove_interface calls, i.e. while the given virtual interface + 3031 * is enabled. + 3032 * Returns a negative error code if the key can't be added. + 3033 * The callback can sleep. + */ +static int esp_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct esp_pub *epub = (struct esp_pub *)hw->priv; + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; + struct esp_hw_idx_map *map; + atomic_t *cnt1, *cnt2; + u8 i, ifidx = evif->index, isvalid, index; + u8 *peer_addr; + int ret, counter; + + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + + if (sta && memcmp(sta->addr, epub->wl.bssid, ETH_ALEN)) + peer_addr = sta->addr; + else + peer_addr = epub->wl.bssid; + + isvalid = !!(cmd == SET_KEY); + + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE || + is_cipher_suite_wep(key->cipher)) + map = epub->low_map[ifidx]; + else + map = epub->hi_map; + + if (isvalid) { + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE || + is_cipher_suite_wep(key->cipher)) + counter = 2; + else + counter = 19; + + for (i = 0; i < counter; i++) { + if (map[i].flag) + continue; + + map[i].flag = 1; + memcpy(map[i].mac, peer_addr, ETH_ALEN); + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE || + is_cipher_suite_wep(key->cipher)) + key->hw_key_idx = i + 6; + else + key->hw_key_idx = i + ifidx * 2 + 2; + break; + } + } else { + map[index].flag = 0; + memset(map[index].mac, 0, ETH_ALEN); + + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE || + is_cipher_suite_wep(key->cipher)) + index = key->hw_key_idx - 6; + else + index = key->hw_key_idx - 2 - ifidx * 2; + } + + if (key->hw_key_idx >= 6) { + cnt1 = &epub->wl.ptk_cnt; + cnt2 = &epub->wl.gtk_cnt; + } else { + cnt2 = &epub->wl.ptk_cnt; + cnt1 = &epub->wl.gtk_cnt; + } + + /*send sub_scan task to target */ + if (isvalid) + atomic_inc(cnt1); + else + atomic_dec(cnt1); + + if (is_cipher_suite_wep(key->cipher)) { + if (isvalid) + atomic_inc(cnt2); + else + atomic_dec(cnt2); + } + + ret = sip_send_setkey(epub, ifidx, peer_addr, key, isvalid); + if (ret) + return ret; + + if (key->cipher == WLAN_CIPHER_SUITE_TKIP && !ret) + atomic_set(&epub->wl.tkip_key_set, 1); + + return 0; +} + +void hw_scan_done(struct esp_pub *epub, bool aborted) +{ + struct cfg80211_scan_info info = { + .aborted = aborted, + }; + + cancel_delayed_work_sync(&epub->scan_timeout_work); + + ESSERT(epub->wl.scan_req); + + ieee80211_scan_completed(epub->hw, &info); + + if (test_and_clear_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags)) + sip_trigger_txq_process(epub->sip); +} + +static void hw_scan_timeout_report(struct work_struct *work) +{ + struct esp_pub *epub = container_of(work, struct esp_pub, + scan_timeout_work.work); + bool aborted; + struct cfg80211_scan_info info = {}; + + if (test_and_clear_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags)) + sip_trigger_txq_process(epub->sip); + /*check if normally complete or aborted like timeout/hw error */ + aborted = (epub->wl.scan_req != 0); + + if (aborted) + epub->wl.scan_req = NULL; + + info.aborted = aborted; + + ieee80211_scan_completed(epub->hw, &info); +} + +/* + Configuration of RTS threshold (if device needs it) + 3106 * The callback can sleep. + */ +static int esp_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + return 0; +} + +static int esp_node_attach(struct ieee80211_hw *hw, u8 ifidx, + struct ieee80211_sta *sta) +{ + struct esp_pub *epub = (struct esp_pub *)hw->priv; + struct esp_node *node; + struct esp_tx_tid *tid; + u8 tidno = 0; + int i; + + spin_lock_bh(&epub->tx_ampdu_lock); + + /* ffz(x) needs at least one zero or results in undefined behaviour. */ + if ((~epub->enodes_map) == 0) + return -EINVAL; + + i = ffz(epub->enodes_map); + + if (hweight32(epub->enodes_maps[ifidx]) >= ESP_PUB_MAX_STA || + i > ESP_PUB_MAX_STA) { + i = -1; + goto out; + } + + epub->enodes_map |= BIT(i); + epub->enodes_maps[ifidx] |= BIT(i); + node = (struct esp_node *)sta->drv_priv; + epub->enodes[i] = node; + node->sta = sta; + node->ifidx = ifidx; + node->index = i; + + while (tidno < WME_NUM_TID) { + tid = &node->tid[tidno]; + tid->ssn = 0; + tid->cnt = 0; + tid->state = ESP_TID_STATE_INIT; + tidno++; + } + +out: + spin_unlock_bh(&epub->tx_ampdu_lock); + + return i; +} + +static int esp_node_detach(struct ieee80211_hw *hw, u8 ifidx, + struct ieee80211_sta *sta) +{ + struct esp_pub *epub = (struct esp_pub *)hw->priv; + u32 map; + int i; + + spin_lock_bh(&epub->tx_ampdu_lock); + + map = epub->enodes_maps[ifidx]; + + while (map) { + i = ffs(map) - 1; + if (epub->enodes[i]->sta == sta) { + epub->enodes[i]->sta = NULL; + epub->enodes[i] = NULL; + epub->enodes_map &= ~BIT(i); + epub->enodes_maps[ifidx] &= ~BIT(i); + + goto out; + } + + map &= ~BIT(i); + } + + i = -1; + +out: + spin_unlock_bh(&epub->tx_ampdu_lock); + + return i; +} + +struct esp_node *esp_get_node_by_addr(struct esp_pub *epub, const u8 *addr) +{ + struct esp_node *node = NULL; + int i; + u32 map; + + if (!addr) + return NULL; + + spin_lock_bh(&epub->tx_ampdu_lock); + map = epub->enodes_map; + + while (map) { + i = ffs(map) - 1; + + if (!memcmp(epub->enodes[i]->sta->addr, addr, ETH_ALEN)) { + node = epub->enodes[i]; + goto out; + } + + map &= ~BIT(i); + } + +out: + spin_unlock_bh(&epub->tx_ampdu_lock); + + return node; +} + +int esp_get_empty_rxampdu(struct esp_pub *epub, const u8 *addr, u8 tid) +{ + int index; + + if (!addr) + return -1; + + spin_lock_bh(&epub->rx_ampdu_lock); + + index = ffz(epub->rxampdu_map); + + if (index >= ESP_PUB_MAX_RXAMPDU) { + index = -1; + goto out; + } + + epub->rxampdu_map |= BIT(index); + epub->rxampdu_node[index] = esp_get_node_by_addr(epub, addr); + epub->rxampdu_tid[index] = tid; + +out: + spin_unlock_bh(&epub->rx_ampdu_lock); + + return index; +} + +int esp_get_exist_rxampdu(struct esp_pub *epub, const u8 *addr, u8 tid) +{ + u8 map; + int index; + + if (!addr) + return -1; + + spin_lock_bh(&epub->rx_ampdu_lock); + map = epub->rxampdu_map; + + while (map) { + index = ffs(map) - 1; + + if (epub->rxampdu_tid[index] == tid && + !memcmp(epub->rxampdu_node[index]->sta->addr, addr, + ETH_ALEN)) { + epub->rxampdu_map &= ~BIT(index); + goto out; + } + + map &= ~BIT(index); + } + + index = -1; + +out: + spin_unlock_bh(&epub->rx_ampdu_lock); + return index; +} + +/* + Notifies low level driver about addition of an associated station, + 3109 * AP, IBSS/WDS/mesh peer etc. This callback can sleep. + */ +static int esp_op_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct esp_pub *epub = (struct esp_pub *)hw->priv; + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; + int index; + + index = esp_node_attach(hw, evif->index, sta); + if (index < 0) + return index; + + sip_send_set_sta(epub, evif->index, 1, sta, vif, (u8)index); + + return 0; +} + +/* + Notifies low level driver about removal of an associated + 3112 * station, AP, IBSS/WDS/mesh peer etc. Note that after the callback + 3113 * returns it isn't safe to use the pointer, not even RCU protected; + 3114 * no RCU grace period is guaranteed between returning here and freeing + 3115 * the station. See @sta_pre_rcu_remove if needed. + 3116 * This callback can sleep + */ +static int esp_op_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct esp_pub *epub = (struct esp_pub *)hw->priv; + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; + int index; + + //remove a connect in target + index = esp_node_detach(hw, evif->index, sta); + sip_send_set_sta(epub, evif->index, 0, sta, vif, (u8)index); + + return 0; +} + +/* + Notifies low level driver about power state transition of an + 3124 * associated station, AP, IBSS/WDS/mesh peer etc. For a VIF operating + 3125 * in AP mode, this callback will not be called when the flag + 3126 * %IEEE80211_HW_AP_LINK_PS is set. Must be atomic. + */ +static void esp_op_sta_notify(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum sta_notify_cmd cmd, + struct ieee80211_sta *sta) +{ +} + +/* + Configure TX queue parameters (EDCF (aifs, cw_min, cw_max), + 3165 * bursting) for a hardware TX queue. + 3166 * Returns a negative error code on failure. + 3167 * The callback can sleep. + */ +static int esp_op_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct esp_pub *epub = (struct esp_pub *)hw->priv; + + return sip_send_wmm_params(epub, queue, params); +} + +/* + Get the current TSF timer value from firmware/hardware. Currently, + 3170 * this is only used for IBSS mode BSSID merging and debugging. Is not a + 3171 * required function. + 3172 * The callback can sleep. + */ +static u64 esp_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + return 0; +} + +/* + Set the TSF timer to the specified value in the firmware/hardware. + 3175 * Currently, this is only used for IBSS mode debugging. Is not a + 3176 * required function. + 3177 * The callback can sleep. + */ +static void esp_op_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u64 tsf) +{ +} + +/* + Reset the TSF timer and allow firmware/hardware to synchronize + 3186 * with other STAs in the IBSS. This is only used in IBSS mode. This + 3187 * function is optional if the firmware/hardware takes full care of + 3188 * TSF synchronization. + 3189 * The callback can sleep. + */ +static void esp_op_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ +} + +/* + Poll rfkill hardware state. If you need this, you also + 3220 * need to set wiphy->rfkill_poll to %true before registration, + 3221 * and need to call wiphy_rfkill_set_hw_state() in the callback. + 3222 * The callback can sleep. + */ +static void esp_op_rfkill_poll(struct ieee80211_hw *hw) +{ + struct esp_pub *epub = (struct esp_pub *)hw->priv; + + wiphy_rfkill_set_hw_state(hw->wiphy, test_bit(ESP_WL_FLAG_RFKILL, + &epub->wl.flags)); +} + +#ifdef HW_SCAN +/* + Ask the hardware to service the scan request, no need to start + 3051 * the scan state machine in stack. The scan must honour the channel + 3052 * configuration done by the regulatory agent in the wiphy's + 3053 * registered bands. The hardware (or the driver) needs to make sure + 3054 * that power save is disabled. + 3055 * The @req ie/ie_len members are rewritten by mac80211 to contain the + 3056 * entire IEs after the SSID, so that drivers need not look at these + 3057 * at all but just send them after the SSID -- mac80211 includes the + 3058 * (extended) supported rates and HT information (where applicable). + 3059 * When the scan finishes, ieee80211_scan_completed() must be called; + 3060 * note that it also must be called when the scan cannot finish due to + 3061 * any error unless this callback returned a negative error code. + 3062 * The callback can sleep. + */ +static int esp_op_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct cfg80211_scan_request *req) +{ + struct esp_pub *epub = (struct esp_pub *)hw->priv; + struct cfg80211_ssid *ssid2 = req->ssids + 1; + int i, ret; + bool scan_often; + + /* scan_request is keep allocate until scan_done,record it + * to split request into multi sdio_cmd + */ + if (atomic_read(&epub->wl.off)) { + dev_err(epub->dev, "hw_scan but wl off\n"); + return -EPERM; + } + + if (req->n_ssids > 1) + if ((req->ssids->ssid_len > 0 && ssid2->ssid_len > 0) || + req->n_ssids > 2) { + dev_err(epub->dev, "cannot scan two SSIDs\n"); + return -EINVAL; + } + + epub->wl.scan_req = req; + + /*in connect state, suspend tx data */ + if (epub->sip->support_bgscan && + test_bit(ESP_WL_FLAG_CONNECT, &epub->wl.flags) && req->n_channels) { + scan_often = epub->scan_permit_valid && + time_before(jiffies, epub->scan_permit); + epub->scan_permit_valid = true; + + if (!scan_often) { + /* epub->scan_permit = jiffies + msecs_to_jiffies(900); + * set_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags); + * if (atomic_read(&epub->txq_stopped) == false) { + * atomic_set(&epub->txq_stopped, true); + * ieee80211_stop_queues(hw); + * } + */ + } else { + dev_err(epub->dev, "scan too often\n"); + return -EACCES; + } + } else { + scan_often = false; + } + + /*send sub_scan task to target */ + ret = sip_send_scan(epub); + if (ret) { + dev_err(epub->dev, "failed to send scan_cmd: %d\n", ret); + return ret; + } + + if (scan_often) + return 0; + + epub->scan_permit = jiffies + msecs_to_jiffies(900); + set_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags); + if (!atomic_read(&epub->txq_stopped)) { + atomic_set(&epub->txq_stopped, true); + ieee80211_stop_queues(hw); + } + + /*force scan complete in case target fail to report in time */ + ieee80211_queue_delayed_work(hw, &epub->scan_timeout_work, + req->n_channels * HZ / 4); + + return 0; +} + +/* + Starts an off-channel period on the given channel, must + 3255 * call back to ieee80211_ready_on_channel() when on that channel. Note + 3256 * that normal channel traffic is not stopped as this is intended for hw + 3257 * offload. Frames to transmit on the off-channel channel are transmitted + 3258 * normally except for the %IEEE80211_TX_CTL_TX_OFFCHAN flag. When the + 3259 * duration (which will always be non-zero) expires, the driver must call + 3260 * ieee80211_remain_on_channel_expired(). + 3261 * Note that this callback may be called while the device is in IDLE and + 3262 * must be accepted in this case. + 3263 * This callback may sleep. + */ +static int esp_op_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + int duration) +{ + struct esp_pub *epub = (struct esp_pub *)hw->priv; + + sip_send_roc(epub, chan->center_freq, duration); + + return 0; +} + +/* + 3264 * @cancel_remain_on_channel: Requests that an ongoing off-channel period is + 3265 * aborted before it expires. This callback may sleep. + */ +static int esp_op_cancel_remain_on_channel(struct ieee80211_hw *hw) +{ + struct esp_pub *epub = (struct esp_pub *)hw->priv; + + epub->roc_flags = 0; // to disable roc state + sip_send_roc(epub, 0, 0); + + return 0; +} +#endif + +void esp_rocdone_process(struct ieee80211_hw *hw, struct sip_evt_roc *report) +{ + struct esp_pub *epub = (struct esp_pub *)hw->priv; + + if (report->is_ok != 1) + return; + + if (report->state == 1) { + epub->roc_flags = 1; //flags in roc state, to fix channel, not change + ieee80211_ready_on_channel(hw); + } else if (!report->state) { + epub->roc_flags = 0; + ieee80211_remain_on_channel_expired(hw); + } +} + +/* + Set a mask of rates to be used for rate control selection + 3275 * when transmitting a frame. Currently only legacy rates are handled. + 3276 * The callback can sleep. + */ +static int esp_op_set_bitrate_mask(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const struct cfg80211_bitrate_mask *mask) +{ + return 0; +} + +/* + Flush all pending frames from the hardware queue, making sure + 3235 * that the hardware queues are empty. The @queues parameter is a bitmap + 3236 * of queues to flush, which is useful if different virtual interfaces + 3237 * use different hardware queues; it may also indicate all queues. + 3238 * If the parameter @drop is set to %true, pending frames may be dropped. + 3239 * Note that vif can be NULL. + 3240 * The callback can sleep. + */ +void esp_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) +{ + struct esp_pub *epub = (struct esp_pub *)hw->priv; + unsigned long time = jiffies + msecs_to_jiffies(15); + + while (atomic_read(&epub->sip->tx_data_pkt_queued)) { + if (!time_before(jiffies, time)) + break; + + if (!sif_get_ate_config()) + ieee80211_queue_work(epub->hw, &epub->tx_work); + else + queue_work(epub->esp_wkq, &epub->tx_work); + } + + mdelay(10); +} + +/* + Perform a certain A-MPDU action + 3198 * The RA/TID combination determines the destination and TID we want + 3199 * the ampdu action to be performed for. The action is defined through + 3200 * ieee80211_ampdu_mlme_action. + 3201 * When the action is set to %IEEE80211_AMPDU_TX_OPERATIONAL the driver + 3202 * may neither send aggregates containing more subframes than @buf_size + 3203 * nor send aggregates in a way that lost frames would exceed the + 3204 * buffer size. If just limiting the aggregate size, this would be + 3205 * possible with a buf_size of 8: + 3206 * - TX: 1.....7 + 3207 * - RX: 2....7 (lost frame #1) + 3208 * - TX: 8..1... + 3209 * which is invalid since #1 was now re-transmitted well past the + 3210 * buffer size of 8. Correct ways to retransmit #1 would be: + 3211 * - TX: 1 or 18 or 81 + 3212 * Even "189" would be wrong since 1 could be lost again. + 3213 * + 3214 * Returns a negative error code on failure. + 3215 * The callback can sleep. + */ +static int esp_op_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params) +{ + enum ieee80211_ampdu_mlme_action action = params->action; + struct ieee80211_sta *sta = params->sta; + struct esp_pub *epub = (struct esp_pub *)hw->priv; + struct esp_node *node = (struct esp_node *)sta->drv_priv; + struct cfg80211_chan_def *chandef; + u16 tid = params->tid; + struct esp_tx_tid *tid_info = &node->tid[tid]; + u16 *ssn = ¶ms->ssn; + u8 buf_size = params->buf_size; + + switch (action) { + case IEEE80211_AMPDU_TX_START: + chandef = &epub->hw->conf.chandef; + if (mod_support_no_txampdu() || !sta->ht_cap.ht_supported || + cfg80211_get_chandef_type(chandef) == NL80211_CHAN_NO_HT) + return -EOPNOTSUPP; + + dev_dbg(epub->dev, "%s TX START, addr:%pM,tid:%u,state:%d\n", + __func__, sta->addr, tid, tid_info->state); + + spin_lock_bh(&epub->tx_ampdu_lock); + + ESSERT(tid_info->state == ESP_TID_STATE_TRIGGER); + *ssn = tid_info->ssn; + tid_info->state = ESP_TID_STATE_PROGRESS; + + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); + spin_unlock_bh(&epub->tx_ampdu_lock); + + return 0; + + case IEEE80211_AMPDU_TX_STOP_CONT: + dev_dbg(epub->dev, "%s TX STOP, addr:%pM,tid:%u,state:%d\n", + __func__, sta->addr, tid, tid_info->state); + + spin_lock_bh(&epub->tx_ampdu_lock); + + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + if (tid_info->state == ESP_TID_STATE_WAIT_STOP) + tid_info->state = ESP_TID_STATE_STOP; + else + tid_info->state = ESP_TID_STATE_INIT; + + if (action == IEEE80211_AMPDU_TX_STOP_CONT) { + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + spin_unlock_bh(&epub->tx_ampdu_lock); + } + + return sip_send_ampdu_action(epub, SIP_AMPDU_TX_STOP, sta->addr, + tid, node->ifidx, 0); + + case IEEE80211_AMPDU_TX_OPERATIONAL: + dev_dbg(epub->dev, + "%s TX OPERATION, addr:%pM,tid:%u,state:%d\n", __func__, + sta->addr, tid, tid_info->state); + + spin_lock_bh(&epub->tx_ampdu_lock); + + if (tid_info->state != ESP_TID_STATE_PROGRESS) { + if (tid_info->state == ESP_TID_STATE_INIT) { + printk(KERN_ERR "%s WIFI RESET, IGNORE\n", + __func__); + spin_unlock_bh(&epub->tx_ampdu_lock); + return -ENETRESET; + } + + ESSERT(0); + } + + tid_info->state = ESP_TID_STATE_OPERATIONAL; + spin_unlock_bh(&epub->tx_ampdu_lock); + + return sip_send_ampdu_action(epub, SIP_AMPDU_TX_OPERATIONAL, + sta->addr, tid, node->ifidx, + buf_size); + + case IEEE80211_AMPDU_RX_START: + chandef = &epub->hw->conf.chandef; + if (mod_support_no_rxampdu() || !sta->ht_cap.ht_supported || + cfg80211_get_chandef_type(chandef) == NL80211_CHAN_NO_HT) + return -EOPNOTSUPP; + + dev_dbg(epub->dev, "%s RX START %pM tid %u %u\n", __func__, + sta->addr, tid, *ssn); + + return sip_send_ampdu_action(epub, SIP_AMPDU_RX_START, sta->addr, + tid, *ssn, 64); + + case IEEE80211_AMPDU_RX_STOP: + dev_dbg(epub->dev, "%s RX STOP %pM tid %u\n", __func__, + sta->addr, tid); + + return sip_send_ampdu_action(epub, SIP_AMPDU_RX_STOP, sta->addr, + tid, 0, 0); + + default: + return -EINVAL; + } +} + +static void esp_tx_work(struct work_struct *work) +{ + struct esp_pub *epub = container_of(work, struct esp_pub, tx_work); + + mutex_lock(&epub->tx_mtx); + sip_txq_process(epub); + mutex_unlock(&epub->tx_mtx); +} + +static const struct ieee80211_ops esp_mac80211_ops = { + .tx = esp_op_tx, + .start = esp_op_start, + .stop = esp_op_stop, + .add_interface = esp_op_add_interface, + .remove_interface = esp_op_remove_interface, + .config = esp_op_config, + + .bss_info_changed = esp_op_bss_info_changed, + .configure_filter = esp_op_configure_filter, + .set_key = esp_op_set_key, + .set_rts_threshold = esp_op_set_rts_threshold, + .sta_notify = esp_op_sta_notify, + .conf_tx = esp_op_conf_tx, + .change_interface = esp_op_change_interface, + .get_tsf = esp_op_get_tsf, + .set_tsf = esp_op_set_tsf, + .reset_tsf = esp_op_reset_tsf, + .rfkill_poll = esp_op_rfkill_poll, +#ifdef HW_SCAN + .hw_scan = esp_op_hw_scan, + .remain_on_channel = esp_op_remain_on_channel, + .cancel_remain_on_channel = esp_op_cancel_remain_on_channel, +#endif + .ampdu_action = esp_op_ampdu_action, + .sta_add = esp_op_sta_add, + .sta_remove = esp_op_sta_remove, + .set_bitrate_mask = esp_op_set_bitrate_mask, + .flush = esp_op_flush, +}; + +struct esp_pub *esp_pub_alloc_mac80211(struct device *dev) +{ + struct ieee80211_hw *hw; + struct esp_pub *epub; + + hw = ieee80211_alloc_hw(sizeof(struct esp_pub), &esp_mac80211_ops); + if (!hw) + return ERR_PTR(-ENOMEM); + + /* FIXME: useless if hw_scan is defined, incorrect if hw_scan is undefined*/ +#ifdef HW_SCAN + hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; +#endif + + epub = hw->priv; + memset(epub, 0, sizeof(*epub)); + + epub->hw = hw; + SET_IEEE80211_DEV(hw, dev); + epub->dev = dev; + + skb_queue_head_init(&epub->txq); + skb_queue_head_init(&epub->txdoneq); + skb_queue_head_init(&epub->rxq); + + spin_lock_init(&epub->tx_ampdu_lock); + spin_lock_init(&epub->rx_ampdu_lock); + spin_lock_init(&epub->tx_lock); + mutex_init(&epub->tx_mtx); + spin_lock_init(&epub->rx_lock); + + INIT_WORK(&epub->tx_work, esp_tx_work); + + epub->esp_wkq = create_singlethread_workqueue("esp_wkq"); + + if (!epub->esp_wkq) + return ERR_PTR(-ENOMEM); + + epub->scan_permit_valid = false; + INIT_DELAYED_WORK(&epub->scan_timeout_work, hw_scan_timeout_report); + + return epub; +} + +int esp_pub_dealloc_mac80211(struct esp_pub *epub) +{ + set_bit(ESP_WL_FLAG_RFKILL, &epub->wl.flags); + + destroy_workqueue(epub->esp_wkq); + mutex_destroy(&epub->tx_mtx); + + if (epub->hw) + ieee80211_free_hw(epub->hw); + + return 0; +} + +/* 2G band channels */ +static struct ieee80211_channel esp_channels_2ghz[] = { + {.hw_value = 1, .center_freq = 2412, .max_power = 25}, + {.hw_value = 2, .center_freq = 2417, .max_power = 25}, + {.hw_value = 3, .center_freq = 2422, .max_power = 25}, + {.hw_value = 4, .center_freq = 2427, .max_power = 25}, + {.hw_value = 5, .center_freq = 2432, .max_power = 25}, + {.hw_value = 6, .center_freq = 2437, .max_power = 25}, + {.hw_value = 7, .center_freq = 2442, .max_power = 25}, + {.hw_value = 8, .center_freq = 2447, .max_power = 25}, + {.hw_value = 9, .center_freq = 2452, .max_power = 25}, + {.hw_value = 10, .center_freq = 2457, .max_power = 25}, + {.hw_value = 11, .center_freq = 2462, .max_power = 25}, + {.hw_value = 12, .center_freq = 2467, .max_power = 25}, + {.hw_value = 13, .center_freq = 2472, .max_power = 25}, +}; + +/* 11G rate */ +static struct ieee80211_rate esp_rates_2ghz[] = { + { + .bitrate = 10, + .hw_value = CONF_HW_BIT_RATE_1MBPS, + .hw_value_short = CONF_HW_BIT_RATE_1MBPS, + }, + { + .bitrate = 20, + .hw_value = CONF_HW_BIT_RATE_2MBPS, + .hw_value_short = CONF_HW_BIT_RATE_2MBPS, + .flags = IEEE80211_RATE_SHORT_PREAMBLE}, + { + .bitrate = 55, + .hw_value = CONF_HW_BIT_RATE_5_5MBPS, + .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS, + .flags = IEEE80211_RATE_SHORT_PREAMBLE}, + { + .bitrate = 110, + .hw_value = CONF_HW_BIT_RATE_11MBPS, + .hw_value_short = CONF_HW_BIT_RATE_11MBPS, + .flags = IEEE80211_RATE_SHORT_PREAMBLE}, + { + .bitrate = 60, + .hw_value = CONF_HW_BIT_RATE_6MBPS, + .hw_value_short = CONF_HW_BIT_RATE_6MBPS, + }, + { + .bitrate = 90, + .hw_value = CONF_HW_BIT_RATE_9MBPS, + .hw_value_short = CONF_HW_BIT_RATE_9MBPS, + }, + { + .bitrate = 120, + .hw_value = CONF_HW_BIT_RATE_12MBPS, + .hw_value_short = CONF_HW_BIT_RATE_12MBPS, + }, + { + .bitrate = 180, + .hw_value = CONF_HW_BIT_RATE_18MBPS, + .hw_value_short = CONF_HW_BIT_RATE_18MBPS, + }, + { + .bitrate = 240, + .hw_value = CONF_HW_BIT_RATE_24MBPS, + .hw_value_short = CONF_HW_BIT_RATE_24MBPS, + }, + { + .bitrate = 360, + .hw_value = CONF_HW_BIT_RATE_36MBPS, + .hw_value_short = CONF_HW_BIT_RATE_36MBPS, + }, + { + .bitrate = 480, + .hw_value = CONF_HW_BIT_RATE_48MBPS, + .hw_value_short = CONF_HW_BIT_RATE_48MBPS, + }, + { + .bitrate = 540, + .hw_value = CONF_HW_BIT_RATE_54MBPS, + .hw_value_short = CONF_HW_BIT_RATE_54MBPS, + }, +}; + +static struct ieee80211_sta_ht_cap esp_ht_cap_2ghz = { + .cap = IEEE80211_HT_CAP_DSSSCCK40 | IEEE80211_HT_CAP_SM_PS | + IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20, + .ht_supported = true, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, + .mcs = { + .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + }, +}; + +static void esp_pub_init_mac80211(struct esp_pub *epub) +{ + struct ieee80211_hw *hw = epub->hw; + struct ieee80211_supported_band *sbands = + &epub->wl.sbands[NL80211_BAND_2GHZ]; + + static const u32 cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, + }; + + hw->max_listen_interval = 10; + + ieee80211_hw_set(hw, SIGNAL_DBM); + ieee80211_hw_set(hw, HAS_RATE_CONTROL); + ieee80211_hw_set(hw, SUPPORTS_PS); + ieee80211_hw_set(hw, AMPDU_AGGREGATION); + ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING); + hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF; + hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF; + + hw->wiphy->cipher_suites = cipher_suites; + hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); + hw->wiphy->max_scan_ie_len = epub->sip->tx_blksz - + sizeof(struct sip_hdr) - sizeof(struct sip_cmd_scan); + + /* ONLY station for now, support P2P soon... */ + /* FIXME: is p2p really supported? */ + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP); + + hw->wiphy->max_scan_ssids = 2; + hw->wiphy->max_remain_on_channel_duration = 5000; + + atomic_set(&epub->wl.off, 1); + + sbands->band = NL80211_BAND_2GHZ; + sbands->channels = esp_channels_2ghz; + sbands->bitrates = esp_rates_2ghz; + sbands->n_channels = ARRAY_SIZE(esp_channels_2ghz); + sbands->n_bitrates = ARRAY_SIZE(esp_rates_2ghz); + sbands->ht_cap = esp_ht_cap_2ghz; + + hw->wiphy->bands[NL80211_BAND_2GHZ] = sbands; + + /*no fragment */ + hw->wiphy->frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD; + + /* handle AC queue in f/w */ + hw->queues = 4; + hw->max_rates = 4; + + hw->vif_data_size = sizeof(struct esp_vif); + hw->sta_data_size = sizeof(struct esp_node); +} + +int esp_register_mac80211(struct esp_pub *epub) +{ + int ret, idx; + u8 *p2p_addr; + + esp_pub_init_mac80211(epub); + + /* FIXME: esp_mac_addr */ + epub->hw->wiphy->addresses = (struct mac_address *)esp_mac_addr; + + memcpy(epub->hw->wiphy->addresses[0].addr, epub->mac_addr, ETH_ALEN); + memcpy(epub->hw->wiphy->addresses[1].addr, epub->mac_addr, ETH_ALEN); + p2p_addr = epub->hw->wiphy->addresses[1].addr; + + for (idx = 0; idx < 8 * ETH_ALEN; idx++) { + p2p_addr[0] = epub->mac_addr[0] | 0x02; + p2p_addr[0] ^= idx << 2; + if (strncmp(p2p_addr, epub->mac_addr, 6)) + break; + } + + epub->hw->wiphy->n_addresses = 2; + + ret = ieee80211_register_hw(epub->hw); + if (ret < 0) { + printk("unable to register mac80211 hw: %d\n", ret); + return ret; + } + + set_bit(ESP_WL_FLAG_HW_REGISTERED, &epub->wl.flags); + + return ret; +} + +static u8 getaddr_index(u8 *addr, struct esp_pub *epub) +{ + int i; + + for (i = 0; i < ESP_PUB_MAX_VIF; i++) + if (!memcmp(addr, epub->hw->wiphy->addresses[i].addr, ETH_ALEN)) + return i; + + return ESP_PUB_MAX_VIF; +} diff --git a/drivers/staging/esp8089/esp_mac80211.h b/drivers/staging/esp8089/esp_mac80211.h new file mode 100644 index 000000000000..03de0b0a4223 --- /dev/null +++ b/drivers/staging/esp8089/esp_mac80211.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2011 - 2014 Espressif System. + * Copyright (c) 2017 Free Electrons + * Quentin Schulz + * + * 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "esp_pub.h" +#include "esp_sip.h" +#include "esp_sif.h" +#include "esp_debug.h" +#include "esp_file.h" +#include "esp_wl.h" + +struct completion *gl_bootup_cplx; + +static int esp_download_fw(struct esp_pub *epub); + +static int modparam_no_txampdu; +static int modparam_no_rxampdu; + +module_param_named(no_txampdu, modparam_no_txampdu, int, 0444); +MODULE_PARM_DESC(no_txampdu, "Disable tx ampdu."); + +module_param_named(no_rxampdu, modparam_no_rxampdu, int, 0444); +MODULE_PARM_DESC(no_rxampdu, "Disable rx ampdu."); + +static char *modparam_eagle_path = "/lib/firmware"; + +module_param_named(eagle_path, modparam_eagle_path, charp, 0444); +MODULE_PARM_DESC(eagle_path, "eagle path"); + +bool mod_support_no_txampdu(void) +{ + return modparam_no_txampdu; +} + +bool mod_support_no_rxampdu(void) +{ + return modparam_no_rxampdu; +} + +int esp_pub_init_all(struct esp_pub *epub) +{ + int ret; + + /* completion for bootup event poll */ + DECLARE_COMPLETION_ONSTACK(complete); + + atomic_set(&epub->ps.state, ESP_PM_OFF); + + if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) { + epub->sip = sip_attach(epub); + if (!epub->sip) { + printk(KERN_ERR "%s sip alloc failed\n", __func__); + return -ENOMEM; + } + } else { + atomic_set(&epub->sip->state, SIP_PREPARE_BOOT); + atomic_set(&epub->sip->tx_credits, 0); + } + + epub->sip->to_host_seq = 0; + + ret = esp_download_fw(epub); + if (ret) { + printk("download firmware failed\n"); + return ret; + } + + gl_bootup_cplx = &complete; + epub->wait_reset = 0; + sif_enable_irq(epub); + + if (epub->sdio_state == ESP_SDIO_STATE_SECOND_INIT || + sif_get_ate_config() == 1) { + ret = sip_poll_bootup_event(epub->sip); + } else { + ret = sip_poll_resetting_event(epub->sip); + if (!ret) { + sif_lock_bus(epub); + sif_interrupt_target(epub, 7); + sif_unlock_bus(epub); + } + } + + gl_bootup_cplx = NULL; + + if (sif_get_ate_config() == 1) + ret = -EOPNOTSUPP; + + return ret; +} + +void esp_dsr(struct esp_pub *epub) +{ + sip_rx(epub); +} + +struct esp_fw_hdr { + u8 magic; + u8 blocks; + u8 pad[2]; + u32 entry_addr; +} __packed; + +struct esp_fw_blk_hdr { + u32 load_addr; + u32 data_len; +} __packed; + +#define ESP_FW_NAME1 "eagle_fw_ate_config_v19.bin" +#define ESP_FW_NAME2 "eagle_fw_first_init_v19.bin" +#define ESP_FW_NAME3 "eagle_fw_second_init_v19.bin" + +static int esp_download_fw(struct esp_pub *epub) +{ + const struct firmware *fw_entry; + struct esp_fw_hdr *fhdr; + struct esp_fw_blk_hdr *bhdr; + struct sip_cmd_bootup bootcmd; + u8 *fw_buf; + u32 offset; + int ret; + u8 blocks; + char *esp_fw_name; + + if (sif_get_ate_config() == 1) + esp_fw_name = ESP_FW_NAME3; + else + esp_fw_name = epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT ? ESP_FW_NAME1 : ESP_FW_NAME2; + + ret = request_firmware(&fw_entry, esp_fw_name, epub->dev); + if (ret) + return ret; + + fw_buf = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL); + + release_firmware(fw_entry); + + if (!fw_buf) + return -ENOMEM; + + fhdr = (struct esp_fw_hdr *)fw_buf; + if (fhdr->magic != 0xE9) { + printk("%s wrong firmware magic!\n", __func__); + goto _err; + } + + blocks = fhdr->blocks; + offset = sizeof(struct esp_fw_hdr); + + while (blocks) { + bhdr = (struct esp_fw_blk_hdr *)&fw_buf[offset]; + offset += sizeof(struct esp_fw_blk_hdr); + + ret = sip_write_memory(epub->sip, bhdr->load_addr, + &fw_buf[offset], bhdr->data_len); + if (ret) { + printk("failed to write firmware: %d\n", ret); + goto _err; + } + + blocks--; + offset += bhdr->data_len; + } + + /* TODO: last byte should be the checksum and skip checksum for now */ + + bootcmd.boot_addr = fhdr->entry_addr; + ret = sip_send_cmd(epub->sip, SIP_CMD_BOOTUP, + sizeof(struct sip_cmd_bootup), &bootcmd); + if (ret) + goto _err; + +_err: + kfree(fw_buf); + + return ret; +} + +MODULE_FIRMWARE(ESP_FW_NAME1); +MODULE_FIRMWARE(ESP_FW_NAME2); +MODULE_FIRMWARE(ESP_FW_NAME3); diff --git a/drivers/staging/esp8089/esp_pub.h b/drivers/staging/esp8089/esp_pub.h new file mode 100644 index 000000000000..e329fb0999e9 --- /dev/null +++ b/drivers/staging/esp8089/esp_pub.h @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2011 - 2014 Espressif System. + * Copyright (c) 2017 Free Electrons + * Quentin Schulz + * + * 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 +#include +#include +#include +#include +#include +#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 + * + * 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 +#include + +/* 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "esp_mac80211.h" +#include "esp_pub.h" +#include "esp_sip.h" +#include "esp_ctrl.h" +#include "esp_sif.h" +#include "esp_debug.h" +#include "slc_host_register.h" +#include "esp_wmac.h" +#include "esp_utils.h" + +extern struct completion *gl_bootup_cplx; + +static int old_signal = -35; +static int avg_signal; +static int signal_loop; + +struct esp_mac_prefix esp_mac_prefix_table[] = { + {0, {0x18, 0xfe, 0x34} }, + {1, {0xac, 0xd0, 0x74} }, + {255, {0x18, 0xfe, 0x34} }, +}; + +#define SIGNAL_COUNT 300 + +/* FIXME: Incomplete ternary condition */ +#define TID_TO_AC(_tid) (!(_tid) || (_tid) == 3 ? WME_AC_BE : ((_tid) < 3 ? WME_AC_BK : ((_tid) < 6 ? WME_AC_VI : WME_AC_VO))) + +#define esp_sip_dbg esp_dbg +struct sip_trace { + u32 tx_data; + u32 tx_cmd; + u32 rx_data; + u32 rx_evt; + u32 rx_tx_status; + u32 tx_out_of_credit; + u32 tx_one_shot_overflow; +}; + +static struct sip_trace str; + +#define STRACE_TX_DATA_INC() (str.tx_data++) +#define STRACE_TX_CMD_INC() (str.tx_cmd++) +#define STRACE_RX_DATA_INC() (str.rx_data++) +#define STRACE_RX_EVENT_INC() (str.rx_evt++) +#define STRACE_RX_TXSTATUS_INC() (str.rx_tx_status++) +#define STRACE_TX_OUT_OF_CREDIT_INC() (str.tx_out_of_credit++) +#define STRACE_TX_ONE_SHOT_INC() (str.tx_one_shot_overflow++) + +#define SIP_STOP_QUEUE_THRESHOLD 48 +#define SIP_RESUME_QUEUE_THRESHOLD 12 + +#define SIP_MIN_DATA_PKT_LEN (sizeof(struct esp_mac_rx_ctrl) + 24) //24 is min 80211hdr + +static void sip_recalc_credit_init(struct esp_sip *sip); + +static int sip_recalc_credit_claim(struct esp_sip *sip, int force); + +static void sip_recalc_credit_release(struct esp_sip *sip); + +static struct sip_pkt *sip_get_ctrl_buf(struct esp_sip *sip, + enum sip_buf_type bftype); + +static void sip_reclaim_ctrl_buf(struct esp_sip *sip, struct sip_pkt *pkt, + enum sip_buf_type bftype); + +static void sip_free_init_ctrl_buf(struct esp_sip *sip); + +static int sip_pack_pkt(struct esp_sip *sip, struct sk_buff *skb, + int *pm_state); + +static struct esp_mac_rx_ctrl *sip_parse_normal_mac_ctrl(struct sk_buff *skb, + int *pkt_len_enc, + int *buf_len, + int *pulled_len); + +static struct sk_buff *sip_parse_data_rx_info(struct esp_sip *sip, + struct sk_buff *skb, + int pkt_len_enc, int buf_len, + struct esp_mac_rx_ctrl *mac_ctrl, + int *pulled_len); + +static inline void sip_rx_pkt_enqueue(struct esp_sip *sip, struct sk_buff *skb); + +static void sip_update_tx_credits(struct esp_sip *sip, u16 recycled_credits); + +static bool sip_rx_pkt_process(struct esp_sip *sip, struct sk_buff *skb); + +static void sip_tx_status_report(struct esp_sip *sip, struct sk_buff *skb, + struct ieee80211_tx_info *tx_info, + bool success); + +static bool check_ac_tid(u8 *pkt, u8 ac, u8 tid) +{ + struct ieee80211_hdr *wh = (struct ieee80211_hdr *)pkt; + + if (!ieee80211_is_data_qos(wh->frame_control) && + !ieee80211_is_mgmt(wh->frame_control) && + !ieee80211_is_ctl(wh->frame_control)) { + if (tid || ac != WME_AC_BE) { + pr_info("444 ac:%u, tid:%u\n", ac, tid); + + if (tid == 7 && ac == WME_AC_VO) + return false; + } + + return true; //hack to modify non-qos null data. + } + + return false; +} + +static void sip_recalc_credit_timeout(unsigned long data) +{ + struct esp_sip *sip = (struct esp_sip *)data; + + sip_recalc_credit_claim(sip, 1); /* recalc again */ +} + +static void sip_recalc_credit_init(struct esp_sip *sip) +{ + atomic_set(&sip->credit_status, RECALC_CREDIT_DISABLE); //set it disable + + setup_timer(&sip->credit_timer, sip_recalc_credit_timeout, + (unsigned long)sip); +} + +static int sip_recalc_credit_claim(struct esp_sip *sip, int force) +{ + int ret; + + if (atomic_read(&sip->credit_status) == RECALC_CREDIT_ENABLE && !force) + return 1; + + atomic_set(&sip->credit_status, RECALC_CREDIT_ENABLE); + ret = sip_send_recalc_credit(sip->epub); + if (ret) { + dev_err(sip->epub->dev, "sending recalc credit failed: %d", + ret); + return ret; + } + + /*setup a timer for handle the abs_credit not receive */ + mod_timer(&sip->credit_timer, jiffies + msecs_to_jiffies(2000)); + + return ret; +} + +static void sip_recalc_credit_release(struct esp_sip *sip) +{ + if (atomic_read(&sip->credit_status) == RECALC_CREDIT_ENABLE) { + atomic_set(&sip->credit_status, RECALC_CREDIT_DISABLE); + del_timer_sync(&sip->credit_timer); + } else { + dev_dbg(sip->epub->dev, "maybe bogus credit"); + } +} + +static void sip_update_tx_credits(struct esp_sip *sip, u16 recycled_credits) +{ + if (recycled_credits & 0x800) { + atomic_set(&sip->tx_credits, (recycled_credits & 0x7ff)); + sip_recalc_credit_release(sip); + } else { + atomic_add(recycled_credits, &sip->tx_credits); + } +} + +void sip_trigger_txq_process(struct esp_sip *sip) +{ + if (atomic_read(&sip->tx_credits) <= sip->credit_to_reserve + SIP_CTRL_CREDIT_RESERVE || + atomic_read(&sip->credit_status) == RECALC_CREDIT_ENABLE) + return; + + if (sip_queue_may_resume(sip)) { + /* wakeup upper queue only if we have sufficient credits */ + atomic_set(&sip->epub->txq_stopped, false); + ieee80211_wake_queues(sip->epub->hw); + } else if (atomic_read(&sip->epub->txq_stopped)) { + dev_err(sip->epub->dev, "%s can't wake txq, credits: %d\n", + __func__, atomic_read(&sip->tx_credits)); + } + + if (!skb_queue_empty(&sip->epub->txq)) { + /* try to send out pkt already in sip queue once we have credits */ +#if !defined(FPGA_TXDATA) + if (!sif_get_ate_config()) + ieee80211_queue_work(sip->epub->hw, + &sip->epub->tx_work); + else + queue_work(sip->epub->esp_wkq, &sip->epub->tx_work); +#else + queue_work(sip->epub->esp_wkq, &sip->epub->tx_work); +#endif + } +} + +static bool sip_ampdu_occupy_buf(struct esp_sip *sip, + struct esp_rx_ampdu_len *ampdu_len) +{ + return (!ampdu_len->substate || + esp_wmac_rxsec_error(ampdu_len->substate) || + (sip->dump_rpbm_err && ampdu_len->substate == RX_RPBM_ERR)); +} + +/* TODO: HARDCORE CLEANUP */ + +static bool sip_rx_pkt_process(struct esp_sip *sip, struct sk_buff *skb) +{ + struct sip_hdr *hdr; + struct sk_buff *rskb; + struct esp_mac_rx_ctrl *mac_ctrl, new_mac_ctrl; + int remains_len, first_pkt_len, ret = 0, pkt_len_enc = 0, buf_len = 0, + pulled_len = 0, pkt_num; + u8 *bufptr; + bool trigger_rxq = false, have_rxabort = false, have_goodpkt = false; + struct esp_rx_ampdu_len *ampdu_len; + static int pkt_dropped, pkt_total; + static u8 frame_head[16]; + static u8 frame_buf_ttl; + + if (!skb) + return trigger_rxq; + + hdr = (struct sip_hdr *)skb->data; + bufptr = skb->data; + + if (hdr->h_credits & SIP_CREDITS_MASK) + sip_update_tx_credits(sip, hdr->h_credits & SIP_CREDITS_MASK); + + hdr->h_credits &= ~SIP_CREDITS_MASK; /* clean credits in sip_hdr, prevent over-add */ + + /* first pkt's length is stored in recycled_credits first 20 bits + * config w3 [31:12] + * repair hdr->len of first pkt + */ + remains_len = hdr->len; + first_pkt_len = hdr->h_credits >> 12; + hdr->len = first_pkt_len; + + if (first_pkt_len > remains_len) { + sip_recalc_credit_claim(sip, 0); + show_buf((u8 *)hdr, first_pkt_len); + ESSERT(0); + goto _exit; + } + + /* pkts handling, including the first pkt, should alloc new skb for each data pkt. + * free the original whole skb after parsing is done. + */ + while (remains_len) { + if (remains_len < sizeof(struct sip_hdr)) { + sip_recalc_credit_claim(sip, 0); + ESSERT(0); + show_buf((u8 *)hdr, 512); + goto _exit; + } + + hdr = (struct sip_hdr *)bufptr; + if (hdr->len <= 0 || hdr->len & 3) { + sip_recalc_credit_claim(sip, 0); + show_buf((u8 *)hdr, 512); + ESSERT(0); + goto _exit; + } + + if (unlikely(hdr->seq != sip->rxseq++)) { + sip_recalc_credit_claim(sip, 0); + dev_dbg(sip->epub->dev, + "%s seq mismatch! got %u, expect %u\n", + __func__, hdr->seq, sip->rxseq - 1); + sip->rxseq = hdr->seq + 1; + show_buf(bufptr, 32); + ESSERT(0); + } + + if (SIP_HDR_IS_CTRL(hdr)) { + STRACE_RX_EVENT_INC(); + + ret = sip_parse_events(sip, bufptr); + skb_pull(skb, hdr->len); + } else if (SIP_HDR_IS_DATA(hdr)) { + STRACE_RX_DATA_INC(); + mac_ctrl = sip_parse_normal_mac_ctrl(skb, &pkt_len_enc, + &buf_len, + &pulled_len); + rskb = sip_parse_data_rx_info(sip, skb, pkt_len_enc, + buf_len, mac_ctrl, + &pulled_len); + + if (!rskb) + goto _move_on; + + if (likely(atomic_read(&sip->epub->wl.off) == 0)) { + local_bh_disable(); + ieee80211_rx(sip->epub->hw, rskb); + local_bh_enable(); + } else { + /* still need go thro parsing as skb_pull should invoke */ + kfree_skb(rskb); + } + } else if (SIP_HDR_IS_AMPDU(hdr)) { + ampdu_len = (struct esp_rx_ampdu_len *)(skb->data + hdr->len / sip->rx_blksz * sip->rx_blksz); + dev_dbg(sip->epub->dev, "%s rx ampdu total len %u\n", + __func__, hdr->len); + if (skb->data != (u8 *)hdr) { + printk("%p %p\n", skb->data, hdr); + show_buf(skb->data, 512); + show_buf((u8 *)hdr, 512); + ESSERT(0); + goto _exit; + } + mac_ctrl = sip_parse_normal_mac_ctrl(skb, NULL, NULL, + &pulled_len); + memcpy(&new_mac_ctrl, mac_ctrl, + sizeof(struct esp_mac_rx_ctrl)); + mac_ctrl = &new_mac_ctrl; + pkt_num = mac_ctrl->ampdu_cnt; + dev_dbg(sip->epub->dev, + "%s %d rx ampdu %u pkts, %d pkts dumped, first len %u\n", + __func__, __LINE__, + (unsigned int)((hdr->len % sip->rx_blksz) / + sizeof(struct esp_rx_ampdu_len)), + pkt_num, (unsigned int)ampdu_len->sublen); + + pkt_total += mac_ctrl->ampdu_cnt; + while (pkt_num > 0) { + dev_dbg(sip->epub->dev, + "%s %d ampdu sub state %02x,\n", + __func__, __LINE__, + ampdu_len->substate); + + if (sip_ampdu_occupy_buf(sip, ampdu_len)) { //pkt is dumped + rskb = sip_parse_data_rx_info(sip, skb, + ampdu_len->sublen - FCS_LEN, + 0, + mac_ctrl, + &pulled_len); + if (!rskb) { + ESSERT(0); + goto _exit; + } + + if (likely(atomic_read(&sip->epub->wl.off) == 0) && + (ampdu_len->substate == 0 || + ampdu_len->substate == RX_TKIPMIC_ERR || + (sip->sendup_rpbm_pkt && + ampdu_len->substate == RX_RPBM_ERR)) && + (sip->rxabort_fixed || + !have_rxabort)) { + if (!have_goodpkt) { + have_goodpkt = true; + memcpy(frame_head, + rskb->data, + 16); + frame_head[1] &= ~0x80; + frame_buf_ttl = 3; + } + local_bh_disable(); + ieee80211_rx(sip->epub->hw, + rskb); + local_bh_enable(); + } else { + kfree_skb(rskb); + } + } else { + if (ampdu_len->substate == RX_ABORT) { + u8 *a; + have_rxabort = true; + dev_dbg(sip->epub->dev, + "rx abort %d %d\n", + frame_buf_ttl, pkt_num); + if (frame_buf_ttl && + !sip->rxabort_fixed) { + struct esp_rx_ampdu_len *next_good_ampdu_len = ampdu_len + 1; + a = frame_head; + while (!sip_ampdu_occupy_buf(sip, next_good_ampdu_len)) { + if (next_good_ampdu_len > ampdu_len + pkt_num - 1) + break; + next_good_ampdu_len++; + } + if (next_good_ampdu_len <= ampdu_len + pkt_num - 1) { + bool b0, b10, b11; + a = skb->data; + b0 = memcmp(frame_head + 4, skb->data + 4, 12) == 0; + b10 = memcmp(frame_head + 10, skb->data, 6) == 0; + b11 = memcpy(frame_head + 11, skb->data, 5) == 0; + if (b0 && !b10 && + !b11) { + have_rxabort = false; + } else if (!b0 && + b10 && + !b11) { + skb_push(skb, 10); + memcpy(skb->data, + frame_head, + 10); + have_rxabort = false; + pulled_len -= 10; + } else if (!b0 && !b10 && b11) { + skb_push(skb, 11); + memcpy(skb->data, + frame_head, + 11); + have_rxabort = false; + pulled_len -= 11; + } + } + } + } + pkt_dropped++; + dev_dbg(sip->epub->dev, + "%s ampdu dropped %d/%d\n", + __func__, pkt_dropped, + pkt_total); + } + pkt_num--; + ampdu_len++; + } + if (frame_buf_ttl) + frame_buf_ttl--; + skb_pull(skb, hdr->len - pulled_len); + } else { + dev_err(sip->epub->dev, "unknown SIP HDR type\n"); + } + +_move_on: + if (hdr->len < remains_len) + remains_len -= hdr->len; + else + break; + bufptr += hdr->len; + } + +_exit: + kfree_skb(skb); + + return trigger_rxq; +} + +static void _sip_rxq_process(struct esp_sip *sip) +{ + struct sk_buff *skb; + bool sendup = false; + + while ((skb = skb_dequeue(&sip->rxq))) + if (sip_rx_pkt_process(sip, skb)) + sendup = true; + + if (sendup) + queue_work(sip->epub->esp_wkq, &sip->epub->sendup_work); + + /* probably tx_credit is updated, try txq */ + sip_trigger_txq_process(sip); +} + +void sip_rxq_process(struct work_struct *work) +{ + struct esp_sip *sip = container_of(work, struct esp_sip, + rx_process_work); + + if (!sip) { + ESSERT(0); + return; + } + + if (unlikely(atomic_read(&sip->state) == SIP_SEND_INIT)) { + sip_send_chip_init(sip); + atomic_set(&sip->state, SIP_WAIT_BOOTUP); + return; + } + + mutex_lock(&sip->rx_mtx); + _sip_rxq_process(sip); + mutex_unlock(&sip->rx_mtx); +} + +static inline void sip_rx_pkt_enqueue(struct esp_sip *sip, struct sk_buff *skb) +{ + skb_queue_tail(&sip->rxq, skb); +} + +static u32 sip_rx_count; + +int sip_rx(struct esp_pub *epub) +{ + struct sip_hdr *shdr; + struct esp_sip *sip = epub->sip; + struct sk_buff *first_skb, *rx_skb; + u8 *rx_buf; + u32 rx_blksz, first_sz; + int err; + u8 raw_seq; + + if (likely(sif_get_ate_config() != 1)) { + raw_seq = sif_get_regs(epub)->intr_raw & 0xff; + + if (raw_seq != sip->to_host_seq) { + if (raw_seq == sip->to_host_seq + 1) { /* when last read pkt crc err, this situation may occur, but raw_seq mustn't < to_host_Seq */ + sip->to_host_seq = raw_seq; + dev_dbg(epub->dev, + "warn: to_host_seq reg 0x%02x, seq 0x%02x", + raw_seq, sip->to_host_seq); + } else { + dev_err(epub->dev, + "err: to_host_seq reg 0x%02x, seq 0x%02x", + raw_seq, sip->to_host_seq); + err = 0; + goto _err; + } + } + } + + /* first read one block out, if we luck enough, that's it + * + * To make design as simple as possible, we allocate skb(s) + * separately for each sif read operation to avoid global + * read_buf_pointe access. It coule be optimized late. + */ + + first_sz = sif_get_regs(epub)->config_w0; + rx_blksz = sif_get_blksz(epub); + first_skb = __dev_alloc_skb(roundup(first_sz, rx_blksz), GFP_KERNEL); + + if (!first_skb) { + sif_unlock_bus(epub); + err = 0; + goto _err; + } + + rx_buf = skb_put(first_skb, first_sz); + + err = esp_common_read(epub, rx_buf, first_sz, ESP_SIF_NOSYNC, false); + sip_rx_count++; + if (unlikely(err)) { + dev_err(epub->dev, " first read err %d %d\n", err, + sif_get_regs(epub)->config_w0); + kfree_skb(first_skb); + sif_unlock_bus(epub); + goto _err; + } + + shdr = (struct sip_hdr *)rx_buf; + if (SIP_HDR_IS_CTRL(shdr) && shdr->c_evtid == SIP_EVT_SLEEP) { + atomic_set(&sip->epub->ps.state, ESP_PM_ON); + } + + if (likely(sif_get_ate_config() != 1)) + sip->to_host_seq++; + + if (shdr->len & 3) { + dev_err(epub->dev, "shdr->len[%d] error\n", shdr->len); + kfree_skb(first_skb); + sif_unlock_bus(epub); + err = -EIO; + goto _err; + } + + if (shdr->len != first_sz) { + dev_err(epub->dev, "shdr->len[%d] first_size[%d] error\n", + shdr->len, first_sz); + kfree_skb(first_skb); + sif_unlock_bus(epub); + err = -EIO; + goto _err; + } else { + sif_unlock_bus(epub); + skb_trim(first_skb, shdr->len); + dev_dbg(epub->dev, "first_skb only\n"); + + rx_skb = first_skb; + } + + if (atomic_read(&sip->state) == SIP_STOP) { + kfree_skb(rx_skb); + dev_err(epub->dev, "rx packet while sip stopped\n"); + return 0; + } + + sip_rx_pkt_enqueue(sip, rx_skb); + queue_work(sip->epub->esp_wkq, &sip->rx_process_work); + +_err: + return err; +} + +int sip_post_init(struct esp_sip *sip, struct sip_evt_bootup2 *bevt) +{ + struct esp_pub *epub; + u8 mac_id = bevt->mac_addr[0]; + int mac_index = 0, i; + + if (!sip) { + ESSERT(0); + return -EINVAL; + } + + epub = sip->epub; + + sip->tx_aggr_write_ptr = sip->tx_aggr_buf; + sip->tx_blksz = bevt->tx_blksz; + sip->rx_blksz = bevt->rx_blksz; + sip->credit_to_reserve = bevt->credit_to_reserve; + sip->dump_rpbm_err = bevt->options & SIP_DUMP_RPBM_ERR; + sip->rxabort_fixed = bevt->options & SIP_RXABORT_FIXED; + sip->support_bgscan = bevt->options & SIP_SUPPORT_BGSCAN; + sip->sendup_rpbm_pkt = 0; + + /* print out MAC addr... */ + memcpy(epub->mac_addr, bevt->mac_addr, ETH_ALEN); + for (i = 0; + i < sizeof(esp_mac_prefix_table) / sizeof(struct esp_mac_prefix); + i++) + if (esp_mac_prefix_table[i].mac_index == mac_id) { + mac_index = i; + break; + } + + epub->mac_addr[0] = esp_mac_prefix_table[mac_index].mac_addr_prefix[0]; + epub->mac_addr[1] = esp_mac_prefix_table[mac_index].mac_addr_prefix[1]; + epub->mac_addr[2] = esp_mac_prefix_table[mac_index].mac_addr_prefix[2]; + + atomic_set(&sip->noise_floor, bevt->noise_floor); + + sip_recalc_credit_init(sip); + + return 0; +} + +/* write pkts in aggr buf to target memory */ +static void sip_write_pkts(struct esp_sip *sip, int pm_state) +{ + int tx_aggr_len, err; + struct sip_hdr *first_shdr; + + tx_aggr_len = sip->tx_aggr_write_ptr - sip->tx_aggr_buf; + if (tx_aggr_len < sizeof(struct sip_hdr)) { + dev_err(sip->epub->dev, "[tx_aggr_len] %d < sizeof(sip_hdr)\n", + tx_aggr_len); + ESSERT(0); + return; + } + + if (tx_aggr_len & 0x3) { + ESSERT(0); + return; + } + + first_shdr = (struct sip_hdr *)sip->tx_aggr_buf; + + if (atomic_read(&sip->tx_credits) <= SIP_CREDITS_LOW_THRESHOLD) + first_shdr->fc[1] |= SIP_HDR_F_NEED_CRDT_RPT; + + /* still use lock bus instead of sif_lldesc_write_sync since we want to protect several global varibles assignments */ + sif_lock_bus(sip->epub); + + err = esp_common_write(sip->epub, sip->tx_aggr_buf, tx_aggr_len, + ESP_SIF_NOSYNC); + if (err) + dev_err(sip->epub->dev, "error while writing pkts: %d\n", err); + + sip->tx_aggr_write_ptr = sip->tx_aggr_buf; + sip->tx_tot_len = 0; + + sif_unlock_bus(sip->epub); +} + +/* setup sip header and tx info, copy pkt into aggr buf */ +static int sip_pack_pkt(struct esp_sip *sip, struct sk_buff *skb, int *pm_state) +{ + struct ieee80211_tx_info *itx_info; + struct sip_hdr *shdr; + struct ieee80211_hdr *wh; + struct esp_vif *evif; + struct esp_node *node; + u32 tx_len, offset; + bool is_data = true; + u8 sta_index; + int alg; + + itx_info = IEEE80211_SKB_CB(skb); + if (itx_info->flags == 0xffffffff) { + shdr = (struct sip_hdr *)skb->data; + is_data = false; + tx_len = skb->len; + } else { + wh = (struct ieee80211_hdr *)skb->data; + evif = (struct esp_vif *)itx_info->control.vif->drv_priv; + /* update sip header */ + shdr = (struct sip_hdr *)sip->tx_aggr_write_ptr; + + shdr->fc[0] = 0; + shdr->fc[1] = 0; + + if (itx_info->flags & IEEE80211_TX_CTL_AMPDU) + SIP_HDR_SET_TYPE(shdr->fc[0], SIP_DATA_AMPDU); + else + SIP_HDR_SET_TYPE(shdr->fc[0], SIP_DATA); + + if (!evif->epub) { + sip_tx_status_report(sip, skb, itx_info, false); + atomic_dec(&sip->tx_data_pkt_queued); + return -EINVAL; + } + + /* make room for encrypted pkt */ + if (itx_info->control.hw_key) { + alg = esp_cipher2alg(itx_info->control.hw_key->cipher); + if (unlikely(alg == -1)) { + sip_tx_status_report(sip, skb, itx_info, false); + atomic_dec(&sip->tx_data_pkt_queued); + return -1; + } + + shdr->d_enc_flag = alg + 1; + shdr->d_hw_kid = itx_info->control.hw_key->hw_key_idx | + (evif->index << 7); + } else { + shdr->d_enc_flag = 0; + shdr->d_hw_kid = evif->index << 7 | evif->index; + } + + /* update sip tx info */ + node = esp_get_node_by_addr(sip->epub, wh->addr1); + if (node) + sta_index = node->index; + else + sta_index = ESP_PUB_MAX_STA + 1; + + SIP_HDR_SET_IFIDX(shdr->fc[0], evif->index << 3 | sta_index); + shdr->d_p2p = itx_info->control.vif->p2p; + + if (evif->index == 1) + shdr->d_p2p = 1; + + shdr->d_ac = skb_get_queue_mapping(skb); + shdr->d_tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; + + wh = (struct ieee80211_hdr *)skb->data; + + if (ieee80211_is_mgmt(wh->frame_control)) { + /* addba/delba/bar may use different tid/ac */ + if (shdr->d_ac == WME_AC_VO) + shdr->d_tid = 7; + + if (ieee80211_is_beacon(wh->frame_control)) { + shdr->d_tid = 8; + shdr->d_ac = 4; + } + } + + if (check_ac_tid(skb->data, shdr->d_ac, shdr->d_tid)) { + shdr->d_ac = WME_AC_BE; + shdr->d_tid = 0; + } + + /* make sure data is start at 4 bytes aligned addr. */ + offset = roundup(sizeof(struct sip_hdr), 4); + + if (SIP_HDR_IS_AMPDU(shdr)) { + memset(sip->tx_aggr_write_ptr + offset, 0, + sizeof(struct esp_tx_ampdu_entry)); + offset += roundup(sizeof(struct esp_tx_ampdu_entry), 4); + } + + tx_len = offset + skb->len; + shdr->len = tx_len; /* actual len */ + } + + shdr->seq = sip->txseq++; + + /* copy skb to aggr buf */ + memcpy(sip->tx_aggr_write_ptr + offset, skb->data, skb->len); + + if (is_data) { + spin_lock_bh(&sip->epub->tx_lock); + sip->txdataseq = shdr->seq; + spin_unlock_bh(&sip->epub->tx_lock); + + /* fake a tx_status and report to mac80211 stack to speed up tx, may affect + * 1) rate control (now it's all in target, so should be OK) + * 2) ps mode, mac80211 want to check ACK of ps/nulldata to see if AP is awake + * 3) BAR, mac80211 do BAR by checking ACK + * + * XXX: need to adjust for 11n, e.g. report tx_status according to BA received in target + */ + sip_tx_status_report(sip, skb, itx_info, true); + atomic_dec(&sip->tx_data_pkt_queued); + + STRACE_TX_DATA_INC(); + } else { + /* check pm state here */ + + /* no need to hold ctrl skb */ + sip_free_ctrl_skbuff(sip, skb); + STRACE_TX_CMD_INC(); + } + + /* TBD: roundup here or whole aggr-buf */ + tx_len = roundup(tx_len, sip->tx_blksz); + + sip->tx_aggr_write_ptr += tx_len; + sip->tx_tot_len += tx_len; + + return 0; +} + +static void sip_tx_status_report(struct esp_sip *sip, struct sk_buff *skb, + struct ieee80211_tx_info *tx_info, + bool success) +{ + struct ieee80211_hdr *wh; + struct esp_node *node; + struct esp_tx_tid *tid; + struct ieee80211_sta *sta; + u8 tidno; + + if (!(tx_info->flags & IEEE80211_TX_CTL_AMPDU)) { + if (likely(success)) + tx_info->flags |= IEEE80211_TX_STAT_ACK; + else + tx_info->flags &= ~IEEE80211_TX_STAT_ACK; + + /* manipulate rate status... */ + tx_info->status.rates[0].idx = 11; + tx_info->status.rates[0].count = 1; + tx_info->status.rates[0].flags = 0; + tx_info->status.rates[1].idx = -1; + } else { + tx_info->flags |= IEEE80211_TX_STAT_AMPDU | + IEEE80211_TX_STAT_ACK; + tx_info->status.ampdu_len = 1; + tx_info->status.ampdu_ack_len = 1; + + /* manipulate rate status... */ + tx_info->status.rates[0].idx = 7; + tx_info->status.rates[0].count = 1; + tx_info->status.rates[0].flags = IEEE80211_TX_RC_MCS | + IEEE80211_TX_RC_SHORT_GI; + tx_info->status.rates[1].idx = -1; + } + + if (!mod_support_no_txampdu() && + cfg80211_get_chandef_type(&sip->epub->hw->conf.chandef) != NL80211_CHAN_NO_HT) { + wh = (struct ieee80211_hdr *)skb->data; + + if (ieee80211_is_data_qos(wh->frame_control) && + !(IEEE80211_SKB_CB(skb)->flags & IEEE80211_TX_CTL_AMPDU)) { + tidno = ieee80211_get_qos_ctl(wh)[0] & + IEEE80211_QOS_CTL_TID_MASK; + + node = esp_get_node_by_addr(sip->epub, wh->addr1); + if (!node || !node->sta) + goto _exit; + + sta = node->sta; + tid = &node->tid[tidno]; + if (!tid) { + ESSERT(0); + goto _exit; + } + + spin_lock_bh(&sip->epub->tx_ampdu_lock); + + if (tid->state == ESP_TID_STATE_INIT && + TID_TO_AC(tidno) != WME_AC_VO && tid->cnt >= 10) { + tid->state = ESP_TID_STATE_TRIGGER; + dev_dbg(sip->epub->dev, + "start tx ba session,addr:%pM,tid:%u\n", + wh->addr1, tidno); + spin_unlock_bh(&sip->epub->tx_ampdu_lock); + ieee80211_start_tx_ba_session(sta, tidno, 0); + } else { + if (tid->state == ESP_TID_STATE_INIT) + tid->cnt++; + else + tid->cnt = 0; + + spin_unlock_bh(&sip->epub->tx_ampdu_lock); + } + } + } + +_exit: + ieee80211_tx_status(sip->epub->hw, skb); +} + +/* NB: this routine should be locked when calling + */ +void sip_txq_process(struct esp_pub *epub) +{ + struct sk_buff *skb; + struct sip_hdr *hdr; + struct esp_sip *sip = epub->sip; + struct ieee80211_tx_info *itx_info; + u32 pkt_len, tx_len = 0; + int blknum = 0, pm_state = 0; + bool queued_back = false, out_of_credits = false; + + while ((skb = skb_dequeue(&epub->txq))) { + /* cmd skb->len does not include sip_hdr too */ + pkt_len = skb->len; + itx_info = IEEE80211_SKB_CB(skb); + if (itx_info->flags != 0xffffffff) { + pkt_len += roundup(sizeof(struct sip_hdr), 4); + if (itx_info->flags & IEEE80211_TX_CTL_AMPDU) + pkt_len += roundup(sizeof(struct esp_tx_ampdu_entry), + 4); + } + + /* current design simply requires every sip_hdr must be at the begin of mblk, that definitely + * need to be optimized, e.g. calculate remain length in the previous mblk, if it larger than + * certain threshold (e.g, whole pkt or > 50% of pkt or 2 x sizeof(struct sip_hdr), append pkt + * to the previous mblk. This might be done in sip_pack_pkt() + */ + pkt_len = roundup(pkt_len, sip->tx_blksz); + blknum = pkt_len / sip->tx_blksz; + + /* + * FIXME: Empirical delay. Without this delay, the connection to + * a WiFi network crashes the kernel (sometimes at the second + * connection). + */ + udelay(2000); + + if (unlikely(atomic_read(&sip->credit_status) == RECALC_CREDIT_ENABLE)) { /* need recalc credit */ + hdr = (struct sip_hdr *)skb->data; + itx_info = IEEE80211_SKB_CB(skb); + + if (!(itx_info->flags == 0xffffffff && + SIP_HDR_GET_TYPE(hdr->fc[0]) == SIP_CTRL && + hdr->c_cmdid == SIP_CMD_RECALC_CREDIT && + blknum <= atomic_read(&sip->tx_credits) - sip->credit_to_reserve)) { /* except cmd recalc credit */ + dev_dbg(epub->dev, "recalc credits!\n"); + STRACE_TX_OUT_OF_CREDIT_INC(); + queued_back = true; + out_of_credits = true; + break; + } + } else { + if (unlikely(blknum > (atomic_read(&sip->tx_credits) - sip->credit_to_reserve - SIP_CTRL_CREDIT_RESERVE))) { + itx_info = IEEE80211_SKB_CB(skb); + if (itx_info->flags == 0xffffffff) { /* priv ctrl pkt */ + if (blknum > atomic_read(&sip->tx_credits) - sip->credit_to_reserve) { + dev_dbg(epub->dev, + "%s cmd pkt out of credits!\n", + __func__); + STRACE_TX_OUT_OF_CREDIT_INC(); + queued_back = true; + out_of_credits = true; + break; + } + } else { + dev_dbg(epub->dev, + "%s out of credits!\n", + __func__); + STRACE_TX_OUT_OF_CREDIT_INC(); + queued_back = true; + out_of_credits = true; + break; + } + } + } + + tx_len += pkt_len; + if (tx_len >= SIP_TX_AGGR_BUF_SIZE) { + /* do we need to have limitation likemax 8 pkts in a row? */ + dev_dbg(epub->dev, "%s too much pkts in one shot!\n", + __func__); + STRACE_TX_ONE_SHOT_INC(); + tx_len -= pkt_len; + queued_back = true; + break; + } + + if (sip_pack_pkt(sip, skb, &pm_state) != 0) { + /* wrong pkt, won't send to target */ + tx_len -= pkt_len; + continue; + } + + atomic_sub(blknum, &sip->tx_credits); + /* + * FIXME: Empirical delay. Without this delay, the connection to + * a WiFi network crashes the kernel (sometimes at the second + * connection). + */ + udelay(2000); + + } + + if (queued_back) + skb_queue_head(&epub->txq, skb); + + if (atomic_read(&sip->state) == SIP_STOP +#ifdef HOST_RESET_BUG + || atomic_read(&epub->wl.off) == 1 +#endif + ) { + queued_back = 1; + tx_len = 0; + } + + if (tx_len) + sip_write_pkts(sip, pm_state); + + if (queued_back && !out_of_credits) + /* skb pending, do async process again */ + sip_trigger_txq_process(sip); +} + +#ifndef NO_WMM_DUMMY +static struct esp_80211_wmm_param_element esp_wmm_param = { + .oui = {0x00, 0x50, 0xf2}, + .oui_type = 0x02, + .oui_subtype = 0x01, + .version = 0x01, + .qos_info = 0x00, + .reserved = 0x00, + .ac = { + { + .aci_aifsn = 0x03, + .cw = 0xa4, + .txop_limit = 0x0000, + }, + { + .aci_aifsn = 0x27, + .cw = 0xa4, + .txop_limit = 0x0000, + }, + { + .aci_aifsn = 0x42, + .cw = 0x43, + .txop_limit = 0x005e, + }, + { + .aci_aifsn = 0x62, + .cw = 0x32, + .txop_limit = 0x002f, + }, + }, +}; + +static int esp_add_wmm(struct sk_buff *skb) +{ + u8 *p; + int flag = 0; + int remain_len; + int base_len; + int len; + struct ieee80211_mgmt *mgmt; + struct ieee80211_hdr *wh; + + if (!skb) + return -1; + + wh = (struct ieee80211_hdr *)skb->data; + mgmt = (struct ieee80211_mgmt *)((u8 *)skb->data); + + if (ieee80211_is_assoc_resp(wh->frame_control)) { + p = mgmt->u.assoc_resp.variable; + base_len = (u8 *)mgmt->u.assoc_resp.variable - (u8 *)mgmt; + } else if (ieee80211_is_reassoc_resp(wh->frame_control)) { + p = mgmt->u.reassoc_resp.variable; + base_len = (u8 *)mgmt->u.reassoc_resp.variable - (u8 *)mgmt; + } else if (ieee80211_is_probe_resp(wh->frame_control)) { + p = mgmt->u.probe_resp.variable; + base_len = (u8 *)mgmt->u.probe_resp.variable - (u8 *)mgmt; + } else if (ieee80211_is_beacon(wh->frame_control)) { + p = mgmt->u.beacon.variable; + base_len = (u8 *)mgmt->u.beacon.variable - (u8 *)mgmt; + } else { + return 1; + } + + remain_len = skb->len - base_len; + + while (remain_len > 0) { + if (*p == 0xdd && *(p + 5) == 0x02) //wmm type + return 0; + else if (*p == 0x2d) //has ht cap + flag = 1; + + len = *(++p); + p += (len + 1); + remain_len -= (len + 2); + } + + if (remain_len < 0) + return -2; + + if (flag == 1) { + skb_put(skb, 2 + sizeof(esp_wmm_param)); + + memset(p, 0xdd, sizeof(u8)); + memset(p + 1, sizeof(esp_wmm_param), sizeof(u8)); + memcpy(p + 2, &esp_wmm_param, sizeof(esp_wmm_param)); + } + + return 0; +} +#endif /* NO_WMM_DUMMY */ + +/* parse mac_rx_ctrl and return length */ +static int sip_parse_mac_rx_info(struct esp_sip *sip, + struct esp_mac_rx_ctrl *mac_ctrl, + struct sk_buff *skb) +{ + struct ieee80211_rx_status *rx_status; + struct ieee80211_hdr *hdr; + struct ieee80211_hdr *wh; + + rx_status = IEEE80211_SKB_RXCB(skb); + rx_status->freq = esp_ieee2mhz(mac_ctrl->channel); + rx_status->signal = mac_ctrl->rssi + mac_ctrl->noise_floor; /* snr actually, need to offset noise floor e.g. -85 */ + + hdr = (struct ieee80211_hdr *)skb->data; + if (mac_ctrl->damatch0 == 1 && mac_ctrl->bssidmatch0 == 1 && /*match bssid and da, but beacon package contain other bssid */ + !strncmp(hdr->addr2, sip->epub->wl.bssid, ETH_ALEN)) { /* force match addr2 */ + if (++signal_loop >= SIGNAL_COUNT) { + avg_signal += rx_status->signal; + avg_signal /= SIGNAL_COUNT; + rx_status->signal = avg_signal + 5; + old_signal = rx_status->signal; + signal_loop = 0; + avg_signal = 0; + } else { + avg_signal += rx_status->signal; + rx_status->signal = old_signal; + } + } + + rx_status->antenna = 0; /* one antenna for now */ + rx_status->band = NL80211_BAND_2GHZ; + rx_status->flag = RX_FLAG_DECRYPTED | RX_FLAG_MMIC_STRIPPED; + if (mac_ctrl->sig_mode) { + rx_status->encoding |= RX_ENC_HT; + rx_status->rate_idx = mac_ctrl->MCS; + if (mac_ctrl->SGI) + rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + } else { + rx_status->rate_idx = esp_wmac_rate2idx(mac_ctrl->rate); + } + + if (mac_ctrl->rxend_state == RX_FCS_ERR) + rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; + + /* Mic error frame flag */ + if (mac_ctrl->rxend_state == RX_TKIPMIC_ERR || + mac_ctrl->rxend_state == RX_CCMPMIC_ERR) { + if (atomic_read(&sip->epub->wl.tkip_key_set) == 1) { + rx_status->flag |= RX_FLAG_MMIC_ERROR; + atomic_set(&sip->epub->wl.tkip_key_set, 0); + printk("mic err\n"); + } else { + printk("mic err discard\n"); + } + } + + wh = (struct ieee80211_hdr *)((u8 *)skb->data); + +#ifndef NO_WMM_DUMMY + if (ieee80211_is_mgmt(wh->frame_control)) + esp_add_wmm(skb); +#endif + + /* some kernel e.g. 3.0.8 wrongly handles non-encrypted pkt like eapol */ + if (ieee80211_is_data(wh->frame_control)) { + if (!ieee80211_has_protected(wh->frame_control)) + rx_status->flag |= RX_FLAG_IV_STRIPPED; + else if (!atomic_read(&sip->epub->wl.ptk_cnt)) + rx_status->flag |= RX_FLAG_IV_STRIPPED; + } + + return 0; +} + +static struct esp_mac_rx_ctrl *sip_parse_normal_mac_ctrl(struct sk_buff *skb, + int *pkt_len_enc, + int *buf_len, + int *pulled_len) +{ + struct sip_hdr *hdr = (struct sip_hdr *)skb->data; + struct esp_mac_rx_ctrl *mac_ctrl; + int len_in_hdr = hdr->len; + + ESSERT(skb); + ESSERT(skb->len > SIP_MIN_DATA_PKT_LEN); + + skb_pull(skb, sizeof(struct sip_hdr)); + *pulled_len += sizeof(struct sip_hdr); + mac_ctrl = (struct esp_mac_rx_ctrl *)skb->data; + if (!mac_ctrl->aggregation) { + ESSERT(pkt_len_enc); + ESSERT(buf_len); + *pkt_len_enc = (mac_ctrl->sig_mode ? mac_ctrl->HT_length : mac_ctrl->legacy_length) - FCS_LEN; + *buf_len = len_in_hdr - sizeof(struct sip_hdr) - + sizeof(struct esp_mac_rx_ctrl); + } + + skb_pull(skb, sizeof(struct esp_mac_rx_ctrl)); + *pulled_len += sizeof(struct esp_mac_rx_ctrl); + + return mac_ctrl; +} + +/* for one MPDU (including subframe in AMPDU) */ +static struct sk_buff *sip_parse_data_rx_info(struct esp_sip *sip, + struct sk_buff *skb, + int pkt_len_enc, int buf_len, + struct esp_mac_rx_ctrl *mac_ctrl, + int *pulled_len) +{ + /* | mac_rx_ctrl | real_data_payload | ampdu_entries | */ + struct sk_buff *rskb; + struct ieee80211_hdr *wh; + int pkt_len, ret; + + if (mac_ctrl->aggregation) { + wh = (struct ieee80211_hdr *)skb->data; + pkt_len = pkt_len_enc; + if (ieee80211_has_protected(wh->frame_control)) //ampdu, it is CCMP enc + pkt_len -= 8; + + buf_len = roundup(pkt_len, 4); + } else { + pkt_len = buf_len - 3 + ((pkt_len_enc - 1) & 0x3); + } + +#ifndef NO_WMM_DUMMY + rskb = __dev_alloc_skb(pkt_len_enc + sizeof(esp_wmm_param) + 2, + GFP_ATOMIC); +#else + rskb = __dev_alloc_skb(pkt_len_enc, GFP_ATOMIC); +#endif /* NO_WMM_DUMMY */ + if (unlikely(!rskb)) { + dev_err(sip->epub->dev, "no mem for rskb\n"); + return NULL; + } + + skb_put(rskb, pkt_len_enc); + + memcpy(rskb->data, skb->data, pkt_len); + + if (pkt_len_enc > pkt_len) + memset(rskb->data + pkt_len, 0, pkt_len_enc - pkt_len); + + /* strip out current pkt, move to the next one */ + skb_pull(skb, buf_len); + *pulled_len += buf_len; + + ret = sip_parse_mac_rx_info(sip, mac_ctrl, rskb); + if (ret == -1 && !mac_ctrl->aggregation) { + kfree_skb(rskb); + return NULL; + } + + return rskb; +} + +struct esp_sip *sip_attach(struct esp_pub *epub) +{ + struct esp_sip *sip; + struct sip_pkt *pkt; + int i, po; + + sip = kzalloc(sizeof(*sip), GFP_KERNEL); + if (!sip) { + dev_dbg(epub->dev, "no mem for sip!\n"); + goto _err_sip; + } + + /* Finding the smalest available space to allocate this variavle */ + po = get_order(SIP_TX_AGGR_BUF_SIZE); + + sip->tx_aggr_buf = (u8 *)__get_free_pages(GFP_ATOMIC, po); + if (!sip->tx_aggr_buf) { + dev_err(epub->dev, "no mem for tx_aggr_buf!\n"); + goto _err_aggr; + } + + spin_lock_init(&sip->lock); + + INIT_LIST_HEAD(&sip->free_ctrl_txbuf); + INIT_LIST_HEAD(&sip->free_ctrl_rxbuf); + + for (i = 0; i < SIP_CTRL_BUF_N; i++) { + pkt = kzalloc(sizeof(*pkt), GFP_KERNEL); + if (!pkt) + goto _err_pkt; + + pkt->buf_begin = kzalloc(SIP_CTRL_BUF_SZ, GFP_KERNEL); + if (!pkt->buf_begin) { + kfree(pkt); + pkt = NULL; + goto _err_pkt; + } + + pkt->buf_len = SIP_CTRL_BUF_SZ; + pkt->buf = pkt->buf_begin; + + if (i < SIP_CTRL_TXBUF_N) + list_add_tail(&pkt->list, &sip->free_ctrl_txbuf); + else + list_add_tail(&pkt->list, &sip->free_ctrl_rxbuf); + } + + mutex_init(&sip->rx_mtx); + skb_queue_head_init(&sip->rxq); + INIT_WORK(&sip->rx_process_work, sip_rxq_process); + + sip->epub = epub; + atomic_set(&sip->noise_floor, -96); + + atomic_set(&sip->state, SIP_INIT); + atomic_set(&sip->tx_credits, 0); + + if (!sip->rawbuf) { + sip->rawbuf = kzalloc(SIP_BOOT_BUF_SIZE, GFP_KERNEL); + if (!sip->rawbuf) { + dev_err(epub->dev, "no mem for rawbuf!\n"); + goto _err_pkt; + } + } + + atomic_set(&sip->state, SIP_PREPARE_BOOT); + + return sip; + +_err_pkt: + sip_free_init_ctrl_buf(sip); + + if (sip->tx_aggr_buf) { + po = get_order(SIP_TX_AGGR_BUF_SIZE); + free_pages((unsigned long)sip->tx_aggr_buf, po); + sip->tx_aggr_buf = NULL; + } + +_err_aggr: + kfree(sip); + sip = NULL; + +_err_sip: + return NULL; +} + +static void sip_free_init_ctrl_buf(struct esp_sip *sip) +{ + struct sip_pkt *pkt, *tpkt; + + list_for_each_entry_safe(pkt, tpkt, &sip->free_ctrl_txbuf, list) { + list_del(&pkt->list); + kfree(pkt->buf_begin); + kfree(pkt); + } + + list_for_each_entry_safe(pkt, tpkt, &sip->free_ctrl_rxbuf, list) { + list_del(&pkt->list); + kfree(pkt->buf_begin); + kfree(pkt); + } +} + +void sip_detach(struct esp_sip *sip) +{ + if (!sip) + return; + + sip_free_init_ctrl_buf(sip); + + if (atomic_read(&sip->state) == SIP_RUN) { + sif_disable_target_interrupt(sip->epub); + + atomic_set(&sip->state, SIP_STOP); + + /* disable irq here */ + sif_disable_irq(sip->epub); + cancel_work_sync(&sip->rx_process_work); + + skb_queue_purge(&sip->rxq); + mutex_destroy(&sip->rx_mtx); + cancel_work_sync(&sip->epub->sendup_work); + skb_queue_purge(&sip->epub->rxq); + + if (test_and_clear_bit(ESP_WL_FLAG_HW_REGISTERED, + &sip->epub->wl.flags)) + ieee80211_unregister_hw(sip->epub->hw); + + /* cancel all worker/timer */ + cancel_work_sync(&sip->epub->tx_work); + skb_queue_purge(&sip->epub->txq); + skb_queue_purge(&sip->epub->txdoneq); + + free_pages((unsigned long)sip->tx_aggr_buf, + get_order(SIP_TX_AGGR_BUF_SIZE)); + sip->tx_aggr_buf = NULL; + + atomic_set(&sip->state, SIP_INIT); + } else if (atomic_read(&sip->state) >= SIP_BOOT && + atomic_read(&sip->state) <= SIP_WAIT_BOOTUP) { + sif_disable_target_interrupt(sip->epub); + atomic_set(&sip->state, SIP_STOP); + sif_disable_irq(sip->epub); + kfree(sip->rawbuf); + + if (atomic_read(&sip->state) == SIP_SEND_INIT) { + cancel_work_sync(&sip->rx_process_work); + skb_queue_purge(&sip->rxq); + mutex_destroy(&sip->rx_mtx); + cancel_work_sync(&sip->epub->sendup_work); + skb_queue_purge(&sip->epub->rxq); + } + + if (test_and_clear_bit(ESP_WL_FLAG_HW_REGISTERED, + &sip->epub->wl.flags)) + ieee80211_unregister_hw(sip->epub->hw); + atomic_set(&sip->state, SIP_INIT); + } else { + dev_err(sip->epub->dev, "wrong state (%d) for detaching sip\n", + atomic_read(&sip->state)); + } + + kfree(sip); +} + +int sip_write_memory(struct esp_sip *sip, u32 addr, u8 *buf, u16 len) +{ + struct sip_cmd_write_memory *cmd; + struct sip_hdr *chdr; + u16 remains, hdrs, bufsize; + u32 loadaddr; + u8 *src; + int err = 0; + u32 *t; + + if (!sip || !sip->rawbuf) { + ESSERT(sip); + ESSERT(sip->rawbuf); + return -EINVAL; + } + + memset(sip->rawbuf, 0, SIP_BOOT_BUF_SIZE); + + chdr = (struct sip_hdr *)sip->rawbuf; + SIP_HDR_SET_TYPE(chdr->fc[0], SIP_CTRL); + chdr->c_cmdid = SIP_CMD_WRITE_MEMORY; + + remains = len; + hdrs = sizeof(struct sip_hdr) + sizeof(struct sip_cmd_write_memory); + + while (remains) { + src = &buf[len - remains]; + loadaddr = addr + (len - remains); + + if (remains < (SIP_BOOT_BUF_SIZE - hdrs)) { + /* aligned with 4 bytes */ + bufsize = roundup(remains, 4); + memset(sip->rawbuf + hdrs, 0, bufsize); + remains = 0; + } else { + bufsize = SIP_BOOT_BUF_SIZE - hdrs; + remains -= bufsize; + } + + chdr->len = bufsize + hdrs; + chdr->seq = sip->txseq++; + cmd = (struct sip_cmd_write_memory *)(sip->rawbuf + SIP_CTRL_HDR_LEN); + cmd->len = bufsize; + cmd->addr = loadaddr; + memcpy(sip->rawbuf + hdrs, src, bufsize); + + t = (u32 *)sip->rawbuf; + err = esp_common_write(sip->epub, sip->rawbuf, chdr->len, + ESP_SIF_SYNC); + if (err) { + dev_err(sip->epub->dev, "send buffer failed\n"); + return err; + } + // 1ms is enough, in fact on dell-d430, need not delay at all. + mdelay(1); + } + + return err; +} + +int sip_send_cmd(struct esp_sip *sip, int cid, u32 cmdlen, void *cmd) +{ + struct sip_hdr *chdr; + struct sip_pkt *pkt; + int ret; + + pkt = sip_get_ctrl_buf(sip, SIP_TX_CTRL_BUF); + if (!pkt) + return -ENOMEM; + + chdr = (struct sip_hdr *)pkt->buf_begin; + chdr->len = SIP_CTRL_HDR_LEN + cmdlen; + chdr->seq = sip->txseq++; + chdr->c_cmdid = cid; + + if (cmd) { + memset(pkt->buf, 0, cmdlen); + memcpy(pkt->buf, (u8 *)cmd, cmdlen); + } + + ret = esp_common_write(sip->epub, pkt->buf_begin, chdr->len, + ESP_SIF_SYNC); + if (ret) + dev_err(sip->epub->dev, "send cmd %d failed\n", cid); + + sip_reclaim_ctrl_buf(sip, pkt, SIP_TX_CTRL_BUF); + + /* Hack here: reset tx/rx seq before target ram code is up... */ + if (cid == SIP_CMD_BOOTUP) { + sip->rxseq = 0; + sip->txseq = 0; + sip->txdataseq = 0; + } + + return ret; +} + +struct sk_buff *sip_alloc_ctrl_skbuf(struct esp_sip *sip, u16 len, u32 cid) +{ + struct sip_hdr *si; + struct ieee80211_tx_info *ti; + struct sk_buff *skb; + + ESSERT(len <= sip->tx_blksz); + + /* no need to reserve space for net stack */ + skb = __dev_alloc_skb(len, GFP_KERNEL); + if (!skb) { + dev_err(sip->epub->dev, "no skb for ctrl!\n"); + return NULL; + } + + skb->len = len; + + ti = IEEE80211_SKB_CB(skb); + /* set tx_info flags to 0xffffffff to indicate sip_ctrl pkt */ + ti->flags = 0xffffffff; + + si = (struct sip_hdr *)skb->data; + memset(si, 0, sizeof(struct sip_hdr)); + SIP_HDR_SET_TYPE(si->fc[0], SIP_CTRL); + si->len = len; + si->c_cmdid = cid; + + return skb; +} + +void sip_free_ctrl_skbuff(struct esp_sip *sip, struct sk_buff *skb) +{ + memset(IEEE80211_SKB_CB(skb), 0, sizeof(struct ieee80211_tx_info)); + kfree_skb(skb); +} + +static struct sip_pkt *sip_get_ctrl_buf(struct esp_sip *sip, + enum sip_buf_type bftype) +{ + struct sip_pkt *pkt; + struct list_head *bflist; + struct sip_hdr *chdr; + + /* FIXME: Why taking spinlock to check list_empty? */ + bflist = (bftype == SIP_TX_CTRL_BUF) ? &sip->free_ctrl_txbuf : &sip->free_ctrl_rxbuf; + + spin_lock_bh(&sip->lock); + + if (list_empty(bflist)) { + spin_unlock_bh(&sip->lock); + return NULL; + } + + pkt = list_first_entry(bflist, struct sip_pkt, list); + list_del(&pkt->list); + spin_unlock_bh(&sip->lock); + + if (bftype == SIP_TX_CTRL_BUF) { + chdr = (struct sip_hdr *)pkt->buf_begin; + SIP_HDR_SET_TYPE(chdr->fc[0], SIP_CTRL); + pkt->buf = pkt->buf_begin + SIP_CTRL_HDR_LEN; + } else { + pkt->buf = pkt->buf_begin; + } + + return pkt; +} + +static void sip_reclaim_ctrl_buf(struct esp_sip *sip, struct sip_pkt *pkt, + enum sip_buf_type bftype) +{ + struct list_head *bflist; + + if (bftype == SIP_TX_CTRL_BUF) + bflist = &sip->free_ctrl_txbuf; + else if (bftype == SIP_RX_CTRL_BUF) + bflist = &sip->free_ctrl_rxbuf; + else + return; + + pkt->buf = pkt->buf_begin; + + spin_lock_bh(&sip->lock); + list_add_tail(&pkt->list, bflist); + spin_unlock_bh(&sip->lock); +} + +int sip_poll_bootup_event(struct esp_sip *sip) +{ + int ret = 0; + + if (gl_bootup_cplx) + ret = wait_for_completion_timeout(gl_bootup_cplx, 2 * HZ); + + if (!ret) { + dev_err(sip->epub->dev, "bootup event timeout\n"); + return -ETIMEDOUT; + } + + if (!sif_get_ate_config()) + ret = esp_register_mac80211(sip->epub); + + atomic_set(&sip->state, SIP_RUN); + + return ret; +} + +/* FIXME: always returning 0 ? */ +int sip_poll_resetting_event(struct esp_sip *sip) +{ + unsigned int ret; + + if (gl_bootup_cplx) + ret = wait_for_completion_timeout(gl_bootup_cplx, 10 * HZ); + + if (!ret) { + dev_err(sip->epub->dev, "resetting event timeout\n"); + return -ETIMEDOUT; + } + + return 0; +} + +bool sip_queue_need_stop(struct esp_sip *sip) +{ + return atomic_read(&sip->tx_data_pkt_queued) >= SIP_STOP_QUEUE_THRESHOLD || + (atomic_read(&sip->tx_credits) < 8 && + atomic_read(&sip->tx_data_pkt_queued) >= SIP_STOP_QUEUE_THRESHOLD / 4 * 3); +} + +bool sip_queue_may_resume(struct esp_sip *sip) +{ + return atomic_read(&sip->epub->txq_stopped) && + !test_bit(ESP_WL_FLAG_STOP_TXQ, &sip->epub->wl.flags) && + ((atomic_read(&sip->tx_credits) >= 16 && + atomic_read(&sip->tx_data_pkt_queued) < SIP_RESUME_QUEUE_THRESHOLD * 2) || + atomic_read(&sip->tx_data_pkt_queued) < SIP_RESUME_QUEUE_THRESHOLD); +} + +int sip_cmd_enqueue(struct esp_sip *sip, struct sk_buff *skb, int prior) +{ + if (!sip || !sip->epub || !skb) { + return -EINVAL; + } + + if (prior == ENQUEUE_PRIOR_HEAD) + skb_queue_head(&sip->epub->txq, skb); + else + skb_queue_tail(&sip->epub->txq, skb); + + if (!sif_get_ate_config()) + ieee80211_queue_work(sip->epub->hw, &sip->epub->tx_work); + else + queue_work(sip->epub->esp_wkq, &sip->epub->tx_work); + + return 0; +} + +void sip_tx_data_pkt_enqueue(struct esp_pub *epub, struct sk_buff *skb) +{ + if (!epub || !epub->sip || !skb) + return; + + skb_queue_tail(&epub->txq, skb); + atomic_inc(&epub->sip->tx_data_pkt_queued); + + if (sip_queue_need_stop(epub->sip)) + if (epub->hw) { + ieee80211_stop_queues(epub->hw); + atomic_set(&epub->txq_stopped, true); + } +} diff --git a/drivers/staging/esp8089/esp_sip.h b/drivers/staging/esp8089/esp_sip.h new file mode 100644 index 000000000000..fe58aae4ba54 --- /dev/null +++ b/drivers/staging/esp8089/esp_sip.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2009 - 2014 Espressif System. + * Copyright (c) 2017 Free Electrons + * Quentin Schulz + * + * 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 + * + * 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 +#include +#include + +#include +#include +#include + +#include "esp_pub.h" +#include "esp_utils.h" +#include "esp_wmac.h" +#include "esp_debug.h" + +/* Convert IEEE channel number to MHz frequency. */ +u32 esp_ieee2mhz(u8 chan) +{ + if (chan == 14) + return 2484; + + if (chan < 14) + return 2407 + chan * 5; + + return 2512 + ((chan - 15) * 20); + //TODO, add 5GHz +} + +enum { + ESP_RATE_1_LONG = 0x0, + ESP_RATE_2_LONG = 0x1, + ESP_RATE_2_SHORT = 0x5, + ESP_RATE_5_SHORT = 0x6, + ESP_RATE_5_LONG = 0x2, + ESP_RATE_11_SHORT = 0x7, + ESP_RATE_11_LONG = 0x3, + ESP_RATE_6 = 0xb, + ESP_RATE_9 = 0xf, + ESP_RATE_12 = 0xa, + ESP_RATE_18 = 0xe, + ESP_RATE_24 = 0x9, + ESP_RATE_36 = 0xd, + ESP_RATE_48 = 0x8, + ESP_RATE_54 = 0xc, + /* ESP_RATE_MCS0 =0x10, + * ESP_RATE_MCS1 =0x11, + * ESP_RATE_MCS2 =0x12, + * ESP_RATE_MCS3 =0x13, + * ESP_RATE_MCS4 =0x14, + * ESP_RATE_MCS5 =0x15, + * ESP_RATE_MCS6 =0x16, + * ESP_RATE_MCS7 =0x17, + */ +}; + +static u8 esp_rate_table[20] = { + ESP_RATE_1_LONG, + ESP_RATE_2_SHORT, + ESP_RATE_5_SHORT, + ESP_RATE_11_SHORT, + ESP_RATE_6, + ESP_RATE_9, + ESP_RATE_12, + ESP_RATE_18, + ESP_RATE_24, + ESP_RATE_36, + ESP_RATE_48, + ESP_RATE_54, + /* ESP_RATE_MCS0, + * ESP_RATE_MCS1, + * ESP_RATE_MCS2, + * ESP_RATE_MCS3, + * ESP_RATE_MCS4, + * ESP_RATE_MCS5, + * ESP_RATE_MCS6, + * ESP_RATE_MCS7, + */ +}; + +s8 esp_wmac_rate2idx(u8 rate) +{ + int i; + + if (rate == ESP_RATE_2_LONG) + return 1; + + if (rate == ESP_RATE_5_LONG) + return 2; + + if (rate == ESP_RATE_11_LONG) + return 3; + + for (i = 0; i < 20; i++) + if (rate == esp_rate_table[i]) + return i; + + return 0; +} + +bool esp_wmac_rxsec_error(u8 error) +{ + return (error >= RX_SECOV_ERR && error <= RX_SECFIFO_TIMEOUT) || + (error >= RX_WEPICV_ERR && error <= RX_WAPIMIC_ERR); +} + +int esp_cipher2alg(int cipher) +{ + if (cipher == WLAN_CIPHER_SUITE_TKIP) + return ALG_TKIP; + + if (cipher == WLAN_CIPHER_SUITE_CCMP) + return ALG_CCMP; + + if (cipher == WLAN_CIPHER_SUITE_WEP40 || + cipher == WLAN_CIPHER_SUITE_WEP104) + return ALG_WEP; + + if (cipher == WLAN_CIPHER_SUITE_AES_CMAC) + return ALG_AES_CMAC; + + return -1; +} diff --git a/drivers/staging/esp8089/esp_utils.h b/drivers/staging/esp8089/esp_utils.h new file mode 100644 index 000000000000..3a4d461a4e70 --- /dev/null +++ b/drivers/staging/esp8089/esp_utils.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2011 - 2012 Espressif System. + * Copyright (c) 2017 Free Electrons + * Quentin Schulz + * + * 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 + * + * 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 + * + * 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "esp_pub.h" +#include "esp_sif.h" +#include "esp_sip.h" +#include "esp_debug.h" +#include "slc_host_register.h" +#include "esp_version.h" +#include "esp_ctrl.h" +#include "esp_file.h" + +unsigned int esp_msg_level = ESP_DBG_ERROR | ESP_SHOW; + +/* HdG: Note: + * 1) MMC_HAS_FORCE_DETECT_CHANGE is a hack which is set by my sunxi-wip + * tree. FIXME replace with a version check once mmc_force_detect_change() + * is added to the mainline kernel. + * 2) This version does NOT implement keep_power, the dts must mark the + * regulators as regulator-always-on and not use mmc-pwrseq for this stub + * to work. + */ +#ifndef MMC_HAS_FORCE_DETECT_CHANGE +void mmc_force_detect_change(struct mmc_host *host, unsigned long delay, + bool keep_power) +{ + host->caps &= ~MMC_CAP_NONREMOVABLE; + host->caps |= MMC_CAP_NEEDS_POLL; + mmc_detect_change(host, delay); +} +#endif + +#define ESP_DMA_IBUFSZ 2048 + +struct esp_sdio_ctrl *sif_sctrl; + +static int esdio_power_off(struct esp_sdio_ctrl *sctrl); +static int esdio_power_on(struct esp_sdio_ctrl *sctrl); + +void sif_set_clock(struct sdio_func *func, int clk); + +void sif_lock_bus(struct esp_pub *epub) +{ + if (!epub || !epub->sif || !epub->sif->func) + return; + sdio_claim_host(epub->sif->func); +} + +void sif_unlock_bus(struct esp_pub *epub) +{ + if (!epub || !epub->sif || !epub->sif->func) + return; + sdio_release_host(epub->sif->func); +} + +static inline bool bad_buf(u8 *buf) +{ + return (unsigned long)buf & 0x3 || !virt_addr_valid(buf); +} + +u8 sdio_io_readb(struct esp_pub *epub, int addr, int *res) +{ + struct sdio_func *func = epub->sif->func; + + if (!func->num) + return sdio_f0_readb(func, addr, res); + + return sdio_readb(func, addr, res); +} + +void sdio_io_writeb(struct esp_pub *epub, u8 value, int addr, int *res) +{ + struct sdio_func *func = epub->sif->func; + + if (!func->num) + sdio_f0_writeb(func, value, addr, res); + else + sdio_writeb(func, value, addr, res); +} + +int sif_io(struct esp_pub *epub, u32 addr, u8 *buf, u32 len, u32 flag, + bool sync) +{ + struct sdio_func *func; + int err = 0; + u8 *ibuf; + bool need_ibuf = false; + + if (!epub || !buf || !epub->sif || !epub->sif->func) { + ESSERT(0); + return -EINVAL; + } + + func = epub->sif->func; + + if (bad_buf(buf)) { + need_ibuf = true; + ibuf = epub->sif->dma_buffer; + } else { + ibuf = buf; + } + + if (flag & SIF_TO_DEVICE) { + if (need_ibuf) + memcpy(ibuf, buf, len); + + if (sync) + sdio_claim_host(func); + + if (flag & SIF_FIXED_ADDR) + err = sdio_writesb(func, addr, ibuf, len); + else if (flag & SIF_INC_ADDR) + err = sdio_memcpy_toio(func, addr, ibuf, len); + + if (sync) + sdio_release_host(func); + + } else if (flag & SIF_FROM_DEVICE) { + if (sync) + sdio_claim_host(func); + + if (flag & SIF_FIXED_ADDR) + err = sdio_readsb(func, ibuf, addr, len); + else if (flag & SIF_INC_ADDR) + err = sdio_memcpy_fromio(func, ibuf, addr, len); + + if (sync) + sdio_release_host(func); + + if (!err && need_ibuf) + memcpy(buf, ibuf, len); + } + + return err; +} + +int sif_io_raw(struct esp_pub *epub, u32 addr, u8 *buf, u32 len, u32 flag) +{ + return sif_io(epub, addr, buf, len, flag, false); +} + +int sif_io_sync(struct esp_pub *epub, u32 addr, u8 *buf, u32 len, u32 flag) +{ + return sif_io(epub, addr, buf, len, flag, true); +} + +int sif_lldesc(struct esp_pub *epub, u8 *buf, u32 len, bool sync, bool read, + bool noround) +{ + u32 read_len, addr, flags; + + if (!epub || !buf || !epub->sif || !epub->sif->target_id) { + ESSERT(0); + return -EINVAL; + } + + if (epub->sif->target_id == 0x600 && !noround) + read_len = roundup(len, epub->sif->slc_blk_sz); + else + read_len = len; + + if (read) { + addr = epub->sif->slc_window_end_addr - 2 - len; + flags = SIF_FROM_DEVICE | SIF_BYTE_BASIS | SIF_INC_ADDR; + } else { + addr = epub->sif->slc_window_end_addr - len; + flags = SIF_TO_DEVICE | SIF_BYTE_BASIS | SIF_INC_ADDR; + } + + if (sync) + return sif_io_sync(epub, addr, buf, read_len, flags); + return sif_io_raw(epub, addr, buf, read_len, flags); +} + +int sif_lldesc_read_sync(struct esp_pub *epub, u8 *buf, u32 len) +{ + return sif_lldesc(epub, buf, len, true, true, false); +} + +int sif_lldesc_write_sync(struct esp_pub *epub, u8 *buf, u32 len) +{ + return sif_lldesc(epub, buf, len, true, false, false); +} + +int sif_lldesc_read_raw(struct esp_pub *epub, u8 *buf, u32 len, bool noround) +{ + return sif_lldesc(epub, buf, len, false, true, noround); +} + +int sif_lldesc_write_raw(struct esp_pub *epub, u8 *buf, u32 len) +{ + return sif_lldesc(epub, buf, len, false, false, false); +} + +#define MANUFACTURER_ID_EAGLE_BASE 0x1110 +#define MANUFACTURER_CODE 0x6666 + +static const struct sdio_device_id esp_sdio_devices[] = { + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_EAGLE_BASE | 0x1))}, + {}, +}; + +static int esdio_power(struct esp_sdio_ctrl *sctrl, bool power_on) +{ + int err; + + if (sctrl->off != power_on) + return 0; + + sdio_claim_host(sctrl->func); + + if (power_on) + err = sdio_enable_func(sctrl->func); + else + err = sdio_disable_func(sctrl->func); + + sdio_release_host(sctrl->func); + + if (err) { + dev_err(sctrl->epub->dev, "unable to enable sdio func: %d\n", + err); + return err; + } + + /* ensure device is up */ + if (power_on) + msleep(5); + + sctrl->off = !sctrl->off; + + return err; +} + +static int esdio_power_on(struct esp_sdio_ctrl *sctrl) +{ + return esdio_power(sctrl, true); +} + +static int esdio_power_off(struct esp_sdio_ctrl *sctrl) +{ + return esdio_power(sctrl, false); +} + +void sif_enable_irq(struct esp_pub *epub) +{ + sdio_claim_host(epub->sif->func); + + if (sdio_claim_irq(epub->sif->func, sif_dsr)) + dev_err(epub->dev, "sif %s failed\n", __func__); + + atomic_set(&epub->sip->state, SIP_BOOT); + atomic_set(&epub->sif->irq_installed, 1); + + sdio_release_host(epub->sif->func); +} + +void sif_disable_irq(struct esp_pub *epub) +{ + int i = 0; + + if (!atomic_read(&epub->sif->irq_installed)) + return; + + sdio_claim_host(epub->sif->func); + + while (atomic_read(&epub->sif->irq_handling)) { + sdio_release_host(epub->sif->func); + schedule_timeout(HZ / 100); + sdio_claim_host(epub->sif->func); + if (i++ >= 400) + break; + } + + /* Ignore errors, we don't always use an irq. */ + sdio_release_irq(epub->sif->func); + + atomic_set(&epub->sif->irq_installed, 0); + sdio_release_host(epub->sif->func); +} + +void sif_set_clock(struct sdio_func *func, int clk) +{ + struct mmc_host *host = func->card->host; + + sdio_claim_host(func); + + //currently only set clock + host->ios.clock = clk * 1000000; + + if (host->ios.clock > host->f_max) + host->ios.clock = host->f_max; + + host->ops->set_ios(host, &host->ios); + + mdelay(2); + + sdio_release_host(func); +} + +static void esp_sdio_remove(struct sdio_func *func); + +static int esp_sdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + struct esp_pub *epub; + struct esp_sdio_ctrl *sctrl; + struct mmc_host *host = func->card->host; + int err; + + if (!sif_sctrl) { + request_init_conf(&func->dev); + + sctrl = kzalloc(sizeof(*sctrl), GFP_KERNEL); + if (!sctrl) + return -ENOMEM; + + /* temp buffer reserved for un-dma-able request */ + sctrl->dma_buffer = kzalloc(ESP_DMA_IBUFSZ, GFP_KERNEL); + if (!sctrl->dma_buffer) { + err = -ENOMEM; + goto _err_last; + } + + sif_sctrl = sctrl; + sctrl->slc_blk_sz = SIF_SLC_BLOCK_SIZE; + + epub = esp_pub_alloc_mac80211(&func->dev); + if (!epub) { + err = -ENOMEM; + goto _err_dma; + } + + epub->sif = sctrl; + epub->sdio_state = ESP_SDIO_STATE_FIRST_INIT; + sctrl->epub = epub; + } else { + /* FIXME: why duplicating sctrl and sif_sctrl? */ + sctrl = sif_sctrl; + sif_sctrl = NULL; + epub = sctrl->epub; + epub->sdio_state = ESP_SDIO_STATE_SECOND_INIT; + /* FIXME: already done in esp_pub_alloc_mac80211 which is called on first probe */ + SET_IEEE80211_DEV(epub->hw, &func->dev); + epub->dev = &func->dev; + } + + sctrl->func = func; + sdio_set_drvdata(func, sctrl); + sctrl->id = id; + sctrl->off = true; + + /* give us some time to enable, in ms */ + func->enable_timeout = 100; + + err = esdio_power_on(sctrl); + if (err) { + if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) + goto _err_ext_gpio; + + goto _err_second_init; + } + + check_target_id(epub); + + sdio_claim_host(func); + + err = sdio_set_block_size(func, sctrl->slc_blk_sz); + if (err) { + dev_err(epub->dev, "Set sdio block size %d failed: %d)\n", + sctrl->slc_blk_sz, err); + + sdio_release_host(func); + if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) + goto _err_off; + + goto _err_second_init; + } + + sdio_release_host(func); + +#ifdef LOWER_CLK + /* fix clock for dongle */ + sif_set_clock(func, 23); +#endif //LOWER_CLK + + err = esp_pub_init_all(epub); + if (err) { + dev_err(epub->dev, "esp_init_all failed: %d\n", err); + if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) { + err = 0; + goto _err_first_init; + } + + if (epub->sdio_state == ESP_SDIO_STATE_SECOND_INIT) + goto _err_second_init; + } + + if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) { + epub->sdio_state = ESP_SDIO_STATE_FIRST_NORMAL_EXIT; + /* Rescan the esp8089 after loading the initial firmware */ + mmc_force_detect_change(host, msecs_to_jiffies(100), true); + } + + return err; + +_err_off: + esdio_power_off(sctrl); + +_err_ext_gpio: + esp_pub_dealloc_mac80211(epub); + +_err_dma: + kfree(sctrl->dma_buffer); + +_err_last: + kfree(sctrl); + +_err_first_init: + if (epub && epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) + epub->sdio_state = ESP_SDIO_STATE_FIRST_ERROR_EXIT; + + return err; + +_err_second_init: + epub->sdio_state = ESP_SDIO_STATE_SECOND_ERROR_EXIT; + esp_sdio_remove(func); + + return err; +} + +static void esp_sdio_remove(struct sdio_func *func) +{ + struct esp_sdio_ctrl *sctrl = sdio_get_drvdata(func); + struct esp_pub *epub; + + if (!sctrl) { + return; + } + + epub = sctrl->epub; + if (!epub) { + goto _out; + } + + if (epub->sdio_state != ESP_SDIO_STATE_FIRST_NORMAL_EXIT) { + if (epub->sip) { + sip_detach(epub->sip); + epub->sip = NULL; + } + } else { + atomic_set(&epub->sip->state, SIP_STOP); + sif_disable_irq(epub); + } + + if (epub->sdio_state != ESP_SDIO_STATE_FIRST_NORMAL_EXIT) { + esp_pub_dealloc_mac80211(epub); + + kfree(sctrl->dma_buffer); + sctrl->dma_buffer = NULL; + + kfree(sctrl); + } + +_out: + sdio_set_drvdata(func, NULL); +} + +MODULE_DEVICE_TABLE(sdio, esp_sdio_devices); + +static int esp_sdio_suspend(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct esp_sdio_ctrl *sctrl = sdio_get_drvdata(func); + struct esp_pub *epub = sctrl->epub; + u32 sdio_flags; + + printk("%s", __func__); + atomic_set(&epub->ps.state, ESP_PM_ON); + + sdio_flags = sdio_get_host_pm_caps(func); + + if (!(sdio_flags & MMC_PM_KEEP_POWER)) + printk("%s can't keep power while host is suspended\n", + __func__); + + /* keep power while host suspended */ + if (sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER)) + printk("%s error while trying to keep power\n", __func__); + + return 0; +} + +static int esp_sdio_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops esp_sdio_pm_ops = { + .suspend = esp_sdio_suspend, + .resume = esp_sdio_resume, +}; + +static struct sdio_driver esp_sdio_driver = { + .name = "eagle_sdio", + .id_table = esp_sdio_devices, + .probe = esp_sdio_probe, + .remove = esp_sdio_remove, + .drv = {.pm = &esp_sdio_pm_ops,}, +}; + +static int /*__init*/ esp_sdio_init(void) +{ + esp_debugfs_init(); + sdio_register_driver(&esp_sdio_driver); + + return 0; +} + +static void /*__exit*/ esp_sdio_exit(void) +{ + sdio_unregister_driver(&esp_sdio_driver); + esp_debugfs_exit(); +} + +module_init(esp_sdio_init); +module_exit(esp_sdio_exit); + +MODULE_AUTHOR("Espressif System"); +MODULE_DESCRIPTION +("Driver for SDIO interconnected eagle low-power WLAN devices"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/esp8089/sip2_common.h b/drivers/staging/esp8089/sip2_common.h new file mode 100644 index 000000000000..5dfb8c492223 --- /dev/null +++ b/drivers/staging/esp8089/sip2_common.h @@ -0,0 +1,388 @@ +/* + * Copyright (c) 2010 - 2014 Espressif System. + * Copyright (c) 2017 Free Electrons + * Quentin Schulz + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + */ + +#ifndef _SIP2_COMMON_H +#define _SIP2_COMMON_H + +/* max 16 types */ +enum sip_type { + SIP_CTRL = 0, + SIP_DATA, + SIP_DATA_AMPDU +}; + +enum sip_buf_type { + SIP_TX_CTRL_BUF = 0, /* from host */ + SIP_RX_CTRL_BUF, /* to host */ + SIP_TX_DATA_BUF, /* from host */ + SIP_RX_DATA_BUF /* to host */ +}; + +enum sip_cmd_id { + SIP_CMD_GET_VER = 0, + SIP_CMD_WRITE_MEMORY, //1 ROM code + SIP_CMD_READ_MEMORY, //2 + SIP_CMD_WRITE_REG, //3 ROM code + SIP_CMD_READ_REG, //4 + SIP_CMD_BOOTUP, //5 ROM code + SIP_CMD_COPYBACK, //6 + SIP_CMD_INIT, //7 + SIP_CMD_SCAN, //8 + SIP_CMD_SETKEY, //9 + SIP_CMD_CONFIG, //10 + SIP_CMD_BSS_INFO_UPDATE, //11 + SIP_CMD_LOOPBACK, //12 ROM code + //do not add cmd before this line + SIP_CMD_SET_WMM_PARAM, + SIP_CMD_AMPDU_ACTION, + SIP_CMD_HB_REQ, //15 + SIP_CMD_RESET_MAC, //16 + SIP_CMD_PRE_DOWN, //17 + SIP_CMD_SLEEP, /* for sleep testing */ + SIP_CMD_WAKEUP, /* for sleep testing */ + SIP_CMD_DEBUG, /* for general testing */ + SIP_CMD_GET_FW_VER, /* get fw rev. */ + SIP_CMD_SETVIF, + SIP_CMD_SETSTA, + SIP_CMD_PS, + SIP_CMD_ATE, + SIP_CMD_SUSPEND, + SIP_CMD_RECALC_CREDIT, + SIP_CMD_MAX, +}; + +enum { + SIP_EVT_TARGET_ON = 0, // + SIP_EVT_BOOTUP, //1 in ROM code + SIP_EVT_COPYBACK, //2 + SIP_EVT_SCAN_RESULT, //3 + SIP_EVT_TX_STATUS, //4 + SIP_EVT_CREDIT_RPT, //5, in ROM code + SIP_EVT_ERROR, //6 + SIP_EVT_LOOPBACK, //7, in ROM code + SIP_EVT_SNPRINTF_TO_HOST, //8 in ROM code + //do not add evt before this line + SIP_EVT_HB_ACK, //9 + SIP_EVT_RESET_MAC_ACK, //10 + SIP_EVT_WAKEUP, //11 /* for sleep testing */ + SIP_EVT_DEBUG, //12 /* for general testing */ + SIP_EVT_PRINT_TO_HOST, //13 + SIP_EVT_TRC_AMPDU, //14 + SIP_EVT_ROC, //15 + SIP_EVT_RESETTING, + SIP_EVT_ATE, + SIP_EVT_EP, + SIP_EVT_INIT_EP, + SIP_EVT_SLEEP, + SIP_EVT_TXIDLE, + SIP_EVT_NOISEFLOOR, + SIP_EVT_MAX +}; + +#define SIP_IFIDX_MASK 0xf0 +#define SIP_IFIDX_S 4 +#define SIP_TYPE_MASK 0x0f +#define SIP_TYPE_S 0 + +#define SIP_HDR_GET_IFIDX(fc0) (((fc0) & SIP_IFIDX_MASK) >> SIP_IFIDX_S) +#define SIP_HDR_SET_IFIDX(fc0, ifidx) ((fc0) = ((fc0) & ~SIP_IFIDX_MASK) | ((ifidx) << SIP_IFIDX_S & SIP_IFIDX_MASK)) +#define SIP_HDR_GET_TYPE(fc0) ((fc0) & SIP_TYPE_MASK) +/* assume type field is cleared */ +#define SIP_HDR_SET_TYPE(fc0, type) ((fc0) = ((fc0) & ~SIP_TYPE_MASK) | ((type) & SIP_TYPE_MASK)) + +/* sip 2.0, not hybrid header so far */ +#define SIP_HDR_IS_CTRL(hdr) (SIP_HDR_GET_TYPE((hdr)->fc[0]) == SIP_CTRL) +#define SIP_HDR_IS_DATA(hdr) (SIP_HDR_GET_TYPE((hdr)->fc[0]) == SIP_DATA) +#define SIP_HDR_IS_AMPDU(hdr) (SIP_HDR_GET_TYPE((hdr)->fc[0]) == SIP_DATA_AMPDU) + +/* fc[1] flags, only for data pkt. Ctrl pkts use fc[1] as eventID */ +#define SIP_HDR_SET_FLAGS(hdr, flags) ((hdr)->fc[1] |= (flags)) +#define SIP_HDR_F_MORE_PKT 0x1 +#define SIP_HDR_F_NEED_CRDT_RPT 0x2 +#define SIP_HDR_F_SYNC 0x4 +#define SIP_HDR_F_SYNC_RESET 0x8 +#define SIP_HDR_F_PM_TURNING_ON 0x10 +#define SIP_HDR_F_PM_TURNING_OFF 0x20 + +#define SIP_HDR_NEED_CREDIT_UPDATE(hdr) ((hdr)->fc[1] & SIP_HDR_F_NEED_CRDT_RPT) +#define SIP_HDR_IS_MORE_PKT(hdr) ((hdr)->fc[1] & SIP_HDR_F_MORE_PKT) +#define SIP_HDR_IS_CRDT_RPT(hdr) ((hdr)->fc[1] & SIP_HDR_F_CRDT_RPT) +#define SIP_HDR_IS_SYNC(hdr) ((hdr)->fc[1] & SIP_HDR_F_SYNC) +#define SIP_HDR_IS_SYNC_RESET(hdr) ((hdr)->fc[1] & SIP_HDR_F_SYNC_RESET) +#define SIP_HDR_IS_SYNC_PKT(hdr) (SIP_HDR_IS_SYNC(hdr) | SIP_HDR_IS_SYNC_RESET(hdr)) +#define SIP_HDR_SET_SYNC(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_SYNC) +#define SIP_HDR_SET_SYNC_RESET(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_SYNC_RESET) +#define SIP_HDR_SET_MORE_PKT(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_MORE_PKT) +#define SIP_HDR_SET_PM_TURNING_ON(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_PM_TURNING_ON) +#define SIP_HDR_IS_PM_TURNING_ON(hdr) ((hdr)->fc[1] & SIP_HDR_F_PM_TURNING_ON) +#define SIP_HDR_SET_PM_TURNING_OFF(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_PM_TURNING_OFF) +#define SIP_HDR_IS_PM_TURNING_OFF(hdr) ((hdr)->fc[1] & SIP_HDR_F_PM_TURNING_OFF) + +/* fc[0]: first 4bit: ifidx; last 4bit: type + * fc[1]: flags + * + * Don't touch the header definitons + */ +struct sip_hdr_min { + u8 fc[2]; + __le16 len; +} __packed; + +/* not more than 4byte long */ +struct sip_tx_data_info { + u8 tid; + u8 ac; + u8 p2p:1, enc_flag:7; + u8 hw_kid; +} __packed; + +/* NB: this structure should be not more than 4byte !! */ +struct sip_tx_info { + union { + u32 cmdid; + struct sip_tx_data_info dinfo; + } u; +} __packed; + +struct sip_hdr { + u8 fc[2]; //fc[0]: type and ifidx ; fc[1] is eventID if the first ctrl pkt in the chain. data pkt still can use fc[1] to set flag + __le16 len; + union { + volatile u32 recycled_credits; /* last 12bits is credits, first 20 bits is actual length of the first pkt in the chain */ + struct sip_tx_info tx_info; + } u; + u32 seq; +} __packed; + +#define h_credits u.recycled_credits +#define c_evtid fc[1] +#define c_cmdid u.tx_info.u.cmdid +#define d_ac u.tx_info.u.dinfo.ac +#define d_tid u.tx_info.u.dinfo.tid +#define d_p2p u.tx_info.u.dinfo.p2p +#define d_enc_flag u.tx_info.u.dinfo.enc_flag +#define d_hw_kid u.tx_info.u.dinfo.hw_kid + +#define SIP_CREDITS_MASK 0xfff /* last 12 bits */ + +#define SIP_HDR_MIN_LEN 4 +#define SIP_HDR_LEN sizeof(struct sip_hdr) +#define SIP_CTRL_HDR_LEN SIP_HDR_LEN /* same as sip_hdr in sip2 design */ +#define SIP_BOOT_BUF_SIZE 256 +#define SIP_CTRL_BUF_SZ 256 /* too much?? */ +#define SIP_CTRL_BUF_N 6 +#define SIP_CTRL_TXBUF_N 2 +#define SIP_CTRL_RXBUF_N 4 + +/* WAR for mblk */ +#define SIP_RX_ADDR_PREFIX_MASK 0xfc000000 +#define SIP_RX_ADDR_SHIFT 6 /* [31:5], shift 6 bits */ + +struct sip_cmd_write_memory { + u32 addr; + u32 len; +} __packed; + +struct sip_cmd_read_memory { + u32 addr; + u32 len; +} __packed; + +struct sip_cmd_write_reg { + u32 addr; + u32 val; +} __packed; + +struct sip_cmd_bootup { + u32 boot_addr; +} __packed; + +struct sip_cmd_loopback { + u32 txlen; //host to target packet len, 0 means no txpacket + u32 rxlen; //target to host packet len, 0 means no rxpacket + u32 pack_id; //sequence of packet +} __packed; + +struct sip_evt_loopback { + u32 txlen; //host to target packet len, 0 means no txpacket + u32 rxlen; //target to host packet len, 0 means no rxpacket + u32 pack_id; //sequence of packet +} __packed; + +struct sip_cmd_copyback { + u32 addr; + u32 len; +} __packed; + +struct sip_cmd_scan { + // u8 ssid[32]; + u8 ssid_len; + // u8 hw_channel[14]; + u8 n_channels; + u8 ie_len; + u8 aborted; +} __packed; // ie[] append at the end + +struct sip_cmd_setkey { + u8 bssid_no; + u8 addr[ETH_ALEN]; + u8 alg; + u8 keyidx; + u8 hw_key_idx; + u8 flags; + u8 keylen; + u8 key[32]; +} __packed; + +struct sip_cmd_config { + u16 center_freq; + u16 duration; +} __packed; + +struct sip_cmd_bss_info_update { + u8 bssid[ETH_ALEN]; + u16 isassoc; + u32 beacon_int; + u8 bssid_no; +} __packed; + +struct sip_evt_bootup { + u16 tx_blksz; + u8 mac_addr[ETH_ALEN]; + /* anything else ? */ +} __packed; + +struct sip_cmd_setvif { + u8 index; + u8 mac[ETH_ALEN]; + u8 set; + u8 op_mode; + u8 is_p2p; +} __packed; + +enum esp_ieee80211_phytype { + ESP_IEEE80211_T_CCK = 0, + ESP_IEEE80211_T_OFDM = 1, + ESP_IEEE80211_T_HT20_L = 2, + ESP_IEEE80211_T_HT20_S = 3, +}; + +struct sip_cmd_setsta { + u8 ifidx; + u8 index; + u8 set; + u8 phymode; + u8 mac[ETH_ALEN]; + u16 aid; + u8 ampdu_factor; + u8 ampdu_density; + u16 resv; +} __packed; + +struct sip_cmd_ps { + u8 dtim_period; + u8 max_sleep_period; + u8 on; + u8 resv; +} __packed; + +struct sip_cmd_suspend { + u8 suspend; + u8 resv[3]; +} __packed; + +#define SIP_DUMP_RPBM_ERR BIT(0) +#define SIP_RXABORT_FIXED BIT(1) +#define SIP_SUPPORT_BGSCAN BIT(2) +struct sip_evt_bootup2 { + u16 tx_blksz; + u8 mac_addr[ETH_ALEN]; + u16 rx_blksz; + u8 credit_to_reserve; + u8 options; + s16 noise_floor; + u8 resv[2]; + /* anything else ? */ +} __packed; + +enum trc_ampdu_state { + TRC_TX_AMPDU_STOPPED = 1, + TRC_TX_AMPDU_OPERATIONAL, + TRC_TX_AMPDU_WAIT_STOP, + TRC_TX_AMPDU_WAIT_OPERATIONAL, + TRC_TX_AMPDU_START, +}; + +struct sip_evt_trc_ampdu { + u8 state; + u8 tid; + u8 addr[ETH_ALEN]; +} __packed; + +struct sip_cmd_set_wmm_params { + u8 aci; + u8 aifs; + u8 ecw_min; + u8 ecw_max; + u16 txop_us; +} __packed; + +#define SIP_AMPDU_RX_START 0 +#define SIP_AMPDU_RX_STOP 1 +#define SIP_AMPDU_TX_OPERATIONAL 2 +#define SIP_AMPDU_TX_STOP 3 + +struct sip_cmd_ampdu_action { + u8 action; + u8 index; + u8 tid; + u8 win_size; + u16 ssn; + u8 addr[ETH_ALEN]; +} __packed; + +#define SIP_TX_ST_OK 0 +#define SIP_TX_ST_NOEB 1 +#define SIP_TX_ST_ACKTO 2 +#define SIP_TX_ST_ENCERR 3 + +//NB: sip_tx_status must be 4 bytes aligned +struct sip_tx_status { + u32 sip_seq; + u8 errno; /* success or failure code */ + u8 rate_index; + char ack_signal; + u8 pad; +} __packed; + +struct sip_evt_scan_report { + u16 scan_id; + u16 aborted; +} __packed; + +struct sip_evt_roc { + u16 state; //start:1, end :0 + u16 is_ok; +} __packed; + +struct sip_evt_txidle { + u32 last_seq; +} __packed; + +struct sip_evt_noisefloor { + s16 noise_floor; + u16 pad; +} __packed; +/* for mblk direct memory access, no need for sip_hdr. tx: first 2k for contrl msg, + * rest of 14k for data. rx, same. + */ + +#endif /* _SIP_COMMON_H_ */ diff --git a/drivers/staging/esp8089/slc_host_register.h b/drivers/staging/esp8089/slc_host_register.h new file mode 100644 index 000000000000..8cf139c0d7bc --- /dev/null +++ b/drivers/staging/esp8089/slc_host_register.h @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2011 Espressif System. + * Copyright (c) 2017 Free Electrons + * Quentin Schulz + * + * 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