Yeah. This feels like it could go into the main linux-wireless tree with a little work. I put some random review comments inline. Nothing super drastic stands out. regards, dan carpenter On Fri, Jul 21, 2017 at 04:35:01PM +0200, Quentin Schulz wrote: > The Espressif ESP8089 WiFi chips can be often found in cheap tablets. > There is one in A23 Polaroid tablets for example. > > The chip is often embedded as an eMMC SDIO device. > > The code was taken from an out-of-tree repository and has seen a first > pass in the cleanup process. > > At the moment, there is no publicly available datasheet for this chip. > > Cc: Hans de Goede <hdegoede@xxxxxxxxxx> > Cc: Icenowy Zheng <icenowy@xxxxxxxx> > Signed-off-by: Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxx> > --- > drivers/staging/Kconfig | 2 + > drivers/staging/Makefile | 1 + > drivers/staging/esp8089/Kconfig | 13 + > drivers/staging/esp8089/Makefile | 7 + > drivers/staging/esp8089/esp_ctrl.c | 527 ++++++++ > drivers/staging/esp8089/esp_ctrl.h | 48 + > drivers/staging/esp8089/esp_debug.c | 247 ++++ > drivers/staging/esp8089/esp_debug.h | 69 ++ > drivers/staging/esp8089/esp_file.c | 221 ++++ > drivers/staging/esp8089/esp_file.h | 30 + > drivers/staging/esp8089/esp_init_data.h | 17 + > drivers/staging/esp8089/esp_io.c | 294 +++++ > drivers/staging/esp8089/esp_mac80211.c | 1496 +++++++++++++++++++++++ > drivers/staging/esp8089/esp_mac80211.h | 33 + > drivers/staging/esp8089/esp_main.c | 199 ++++ > drivers/staging/esp8089/esp_pub.h | 188 +++ > drivers/staging/esp8089/esp_sif.h | 131 ++ > drivers/staging/esp8089/esp_sip.c | 1718 +++++++++++++++++++++++++++ > drivers/staging/esp8089/esp_sip.h | 150 +++ > drivers/staging/esp8089/esp_utils.c | 133 +++ > drivers/staging/esp8089/esp_utils.h | 27 + > drivers/staging/esp8089/esp_wl.h | 35 + > drivers/staging/esp8089/esp_wmac.h | 87 ++ > drivers/staging/esp8089/sdio_sif_esp.c | 552 +++++++++ > drivers/staging/esp8089/sip2_common.h | 388 ++++++ > drivers/staging/esp8089/slc_host_register.h | 263 ++++ > 26 files changed, 6876 insertions(+) > create mode 100644 drivers/staging/esp8089/Kconfig > create mode 100644 drivers/staging/esp8089/Makefile > create mode 100644 drivers/staging/esp8089/esp_ctrl.c > create mode 100644 drivers/staging/esp8089/esp_ctrl.h > create mode 100644 drivers/staging/esp8089/esp_debug.c > create mode 100644 drivers/staging/esp8089/esp_debug.h > create mode 100644 drivers/staging/esp8089/esp_file.c > create mode 100644 drivers/staging/esp8089/esp_file.h > create mode 100644 drivers/staging/esp8089/esp_init_data.h > create mode 100644 drivers/staging/esp8089/esp_io.c > create mode 100644 drivers/staging/esp8089/esp_mac80211.c > create mode 100644 drivers/staging/esp8089/esp_mac80211.h > create mode 100644 drivers/staging/esp8089/esp_main.c > create mode 100644 drivers/staging/esp8089/esp_pub.h > create mode 100644 drivers/staging/esp8089/esp_sif.h > create mode 100644 drivers/staging/esp8089/esp_sip.c > create mode 100644 drivers/staging/esp8089/esp_sip.h > create mode 100644 drivers/staging/esp8089/esp_utils.c > create mode 100644 drivers/staging/esp8089/esp_utils.h > create mode 100644 drivers/staging/esp8089/esp_wl.h > create mode 100644 drivers/staging/esp8089/esp_wmac.h > create mode 100644 drivers/staging/esp8089/sdio_sif_esp.c > create mode 100644 drivers/staging/esp8089/sip2_common.h > create mode 100644 drivers/staging/esp8089/slc_host_register.h > > diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig > index e97d72e3bc40..0bd007837429 100644 > --- a/drivers/staging/Kconfig > +++ b/drivers/staging/Kconfig > @@ -114,4 +114,6 @@ source "drivers/staging/vboxvideo/Kconfig" > > source "drivers/staging/pi433/Kconfig" > > +source "drivers/staging/esp8089/Kconfig" > + > endif # STAGING > diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile > index 993ed0c1556c..23cfa1258c4e 100644 > --- a/drivers/staging/Makefile > +++ b/drivers/staging/Makefile > @@ -46,3 +46,4 @@ obj-$(CONFIG_BCM2835_VCHIQ) += vc04_services/ > obj-$(CONFIG_CRYPTO_DEV_CCREE) += ccree/ > obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/ > obj-$(CONFIG_PI433) += pi433/ > +obj-$(CONFIG_ESP8089) += esp8089/ > diff --git a/drivers/staging/esp8089/Kconfig b/drivers/staging/esp8089/Kconfig > new file mode 100644 > index 000000000000..8fdd8fa828ad > --- /dev/null > +++ b/drivers/staging/esp8089/Kconfig > @@ -0,0 +1,13 @@ > +config ESP8089 > + tristate "Espressif ESP8089 SDIO WiFi" > + depends on MAC80211 > + ---help--- > + ESP8089 is a low-budget 2.4GHz WiFi chip by Espressif, used in many > + cheap tablets with Allwinner or Rockchip SoC > + > +config ESP8089_DEBUG_FS > + bool "Enable DebugFS support for ESP8089" > + depends on ESP8089 > + default y > + ---help--- > + DebugFS support for ESP8089 > diff --git a/drivers/staging/esp8089/Makefile b/drivers/staging/esp8089/Makefile > new file mode 100644 > index 000000000000..827c66d8b5d5 > --- /dev/null > +++ b/drivers/staging/esp8089/Makefile > @@ -0,0 +1,7 @@ > +MODULE_NAME = esp8089 > + > +$(MODULE_NAME)-y := esp_debug.o sdio_sif_esp.o esp_io.o \ > + esp_file.o esp_main.o esp_sip.o esp_ctrl.o \ > + esp_mac80211.o esp_debug.o esp_utils.o > + > +obj-$(CONFIG_ESP8089) := esp8089.o > diff --git a/drivers/staging/esp8089/esp_ctrl.c b/drivers/staging/esp8089/esp_ctrl.c > new file mode 100644 > index 000000000000..e5a95e7b313f > --- /dev/null > +++ b/drivers/staging/esp8089/esp_ctrl.c > @@ -0,0 +1,527 @@ > +/* > + * Copyright (c) 2009 - 2014 Espressif System. > + * Copyright (c) 2017 Free Electrons > + * Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it under > + * the terms of the GNU General Public License version 2 as published by the > + * Free Software Foundation. > + * > + */ > + > +#include <net/mac80211.h> > +#include <net/cfg80211.h> > +#include <linux/skbuff.h> > +#include <linux/bitops.h> > +#include <linux/firmware.h> > + > +#include "esp_init_data.h" > +#include "esp_pub.h" > +#include "esp_sip.h" > +#include "esp_ctrl.h" > +#include "esp_sif.h" > +#include "esp_debug.h" > +#include "esp_wmac.h" > +#include "esp_utils.h" > +#include "esp_wl.h" > +#include "esp_file.h" > +#include "esp_version.h" > + > +#define DRIVER_VER 0xbdf5087c3debll > + > +static void esp_tx_ba_session_op(struct esp_sip *sip, struct esp_node *node, > + enum trc_ampdu_state state, u8 tid) > +{ > + struct esp_tx_tid *txtid = &node->tid[tid]; > + > + if (state != TRC_TX_AMPDU_STOPPED && state != TRC_TX_AMPDU_OPERATIONAL) > + return; > + > + if (state == TRC_TX_AMPDU_STOPPED) { > + if (txtid->state != ESP_TID_STATE_OPERATIONAL) { > + dev_err(sip->epub->dev, > + "%s tid %d TXAMPDU GOT STOP EVT IN WRONG STATE %d\n", > + __func__, tid, txtid->state); > + return; > + } > + > + dev_dbg(sip->epub->dev, "%s tid %d TXAMPDU GOT STOP EVT\n", > + __func__, tid); > + > + spin_lock_bh(&sip->epub->tx_ampdu_lock); > + txtid->state = ESP_TID_STATE_WAIT_STOP; > + spin_unlock_bh(&sip->epub->tx_ampdu_lock); > + > + ieee80211_stop_tx_ba_session(node->sta, (u16)tid); > + return; > + } > + > + if (txtid->state == ESP_TID_STATE_STOP) { > + dev_dbg(sip->epub->dev, "%s tid %d TXAMPDU GOT OPERATIONAL\n", > + __func__, tid); > + > + spin_lock_bh(&sip->epub->tx_ampdu_lock); > + txtid->state = ESP_TID_STATE_TRIGGER; > + spin_unlock_bh(&sip->epub->tx_ampdu_lock); > + > + ieee80211_start_tx_ba_session(node->sta, (u16)tid, 0); > + return; > + } else if (txtid->state == ESP_TID_STATE_OPERATIONAL) { > + sip_send_ampdu_action(sip->epub, SIP_AMPDU_TX_OPERATIONAL, > + node->sta->addr, tid, node->ifidx, 0); > + return; > + } > + > + dev_err(sip->epub->dev, > + "%s tid %d TXAMPDU GOT OPERATIONAL EVT IN WRONG STATE %d\n", > + __func__, tid, txtid->state); > +} > + > +int sip_parse_events(struct esp_sip *sip, u8 *buf) > +{ > + struct sip_hdr *hdr = (struct sip_hdr *)buf; > + struct sip_evt_bootup2 *bootup_evt; > + struct sip_evt_scan_report *report; > + struct sip_evt_roc *roc; > + struct sip_evt_trc_ampdu *ampdu; > + struct sip_evt_noisefloor *noisefloor; > + struct esp_node *node; > + char *ep; > + u8 *p; > + u16 *len; > + int i; > + > + switch (hdr->c_evtid) { > + case SIP_EVT_TARGET_ON: > + /* use rx work queue to send... */ > + if (atomic_read(&sip->state) == SIP_PREPARE_BOOT || > + atomic_read(&sip->state) == SIP_BOOT) { > + atomic_set(&sip->state, SIP_SEND_INIT); > + queue_work(sip->epub->esp_wkq, &sip->rx_process_work); > + break; > + } > + dev_err(sip->epub->dev, "%s boot during wrong state %d\n", > + __func__, atomic_read(&sip->state)); > + break; > + > + case SIP_EVT_BOOTUP: > + bootup_evt = (struct sip_evt_bootup2 *)(buf + SIP_CTRL_HDR_LEN); > + > + kfree(sip->rawbuf); > + > + sip_post_init(sip, bootup_evt); > + if (gl_bootup_cplx) > + complete(gl_bootup_cplx); > + > + break; > + > + case SIP_EVT_RESETTING: > + sip->epub->wait_reset = 1; > + if (gl_bootup_cplx) > + complete(gl_bootup_cplx); > + break; > + > + case SIP_EVT_SCAN_RESULT: > + if (!atomic_read(&sip->epub->wl.off)) { > + report = (struct sip_evt_scan_report *)(buf + SIP_CTRL_HDR_LEN); > + sip_scandone_process(sip, report); > + break; > + } > + > + dev_err(sip->epub->dev, "%s scan result while wlan off\n", > + __func__); > + break; > + > + case SIP_EVT_ROC: > + roc = (struct sip_evt_roc *)(buf + SIP_CTRL_HDR_LEN); > + esp_rocdone_process(sip->epub->hw, roc); > + break; > + > + case SIP_EVT_SNPRINTF_TO_HOST: > + p = buf + sizeof(struct sip_hdr) + sizeof(u16); > + len = (u16 *)(buf + sizeof(struct sip_hdr)); > + dev_dbg(sip->epub->dev, "esp_host:%llx\nesp_target: %.*s\n", > + DRIVER_VER, *len, p); > + > + if (!*len || sip->epub->sdio_state != ESP_SDIO_STATE_FIRST_INIT) > + break; > + > + dev_dbg(sip->epub->dev, > + "SNPRINTF TO HOST: esp_host:%llx\nesp_target: %.*s\n", > + DRIVER_VER, *len, p); > + break; > + > + case SIP_EVT_TRC_AMPDU: > + if (atomic_read(&sip->epub->wl.off)) { > + dev_err(sip->epub->dev, > + "%s scan result while wlan off\n", __func__); > + break; > + } > + > + ampdu = (struct sip_evt_trc_ampdu *)(buf + SIP_CTRL_HDR_LEN); > + node = esp_get_node_by_addr(sip->epub, ampdu->addr); > + if (!node) > + break; > + for (i = 0; i < 8; i++) > + if (ampdu->tid & BIT(i)) > + esp_tx_ba_session_op(sip, node, ampdu->state, > + i); > + break; > + > + case SIP_EVT_INIT_EP: > + ep = (char *)(buf + SIP_CTRL_HDR_LEN); > + dev_dbg(sip->epub->dev, "Phy Init: %s\n", ep); > + break; > + > + case SIP_EVT_NOISEFLOOR: > + noisefloor = (struct sip_evt_noisefloor *)(buf + SIP_CTRL_HDR_LEN); > + atomic_set(&sip->noise_floor, noisefloor->noise_floor); > + break; > + > + default: > + break; > + } > + > + return 0; > +} > + > +/* FIXME: ugly af */ > +void sip_send_chip_init(struct esp_sip *sip) > +{ > + size_t size = sizeof(esp_init_data); > + > + fix_init_data(esp_init_data, size); > + atomic_sub(1, &sip->tx_credits); > + sip_send_cmd(sip, SIP_CMD_INIT, size, (void *)esp_init_data); > +} > + > +int sip_send_config(struct esp_pub *epub, struct ieee80211_conf *conf) > +{ > + struct sk_buff *skb; > + struct sip_cmd_config *configcmd; > + > + skb = sip_alloc_ctrl_skbuf(epub->sip, sizeof(struct sip_cmd_config) + > + sizeof(struct sip_hdr), SIP_CMD_CONFIG); > + if (!skb) > + return -ENOMEM; > + > + dev_dbg(epub->dev, "%s config center freq %d\n", __func__, > + conf->chandef.chan->center_freq); > + > + configcmd = (struct sip_cmd_config *)(skb->data + sizeof(struct sip_hdr)); > + configcmd->center_freq = conf->chandef.chan->center_freq; > + configcmd->duration = 0; > + > + return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL); > +} > + > +int sip_send_bss_info_update(struct esp_pub *epub, struct esp_vif *evif, > + u8 *bssid, int assoc) > +{ > + struct sk_buff *skb; > + struct sip_cmd_bss_info_update *bsscmd; > + > + skb = sip_alloc_ctrl_skbuf(epub->sip, > + sizeof(struct sip_cmd_bss_info_update) + > + sizeof(struct sip_hdr), > + SIP_CMD_BSS_INFO_UPDATE); > + if (!skb) > + return -ENOMEM; > + > + bsscmd = (struct sip_cmd_bss_info_update *)(skb->data + sizeof(struct sip_hdr)); > + > + if (assoc == 2) //hack for softAP mode > + bsscmd->beacon_int = evif->beacon_interval; > + else if (assoc == 1) > + set_bit(ESP_WL_FLAG_CONNECT, &epub->wl.flags); > + else > + clear_bit(ESP_WL_FLAG_CONNECT, &epub->wl.flags); > + > + bsscmd->bssid_no = evif->index; > + bsscmd->isassoc = assoc; > + bsscmd->beacon_int = evif->beacon_interval; > + memcpy(bsscmd->bssid, bssid, ETH_ALEN); > + > + return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL); > +} > + > +int sip_send_wmm_params(struct esp_pub *epub, u8 aci, > + const struct ieee80211_tx_queue_params *params) > +{ > + struct sk_buff *skb; > + struct sip_cmd_set_wmm_params *bsscmd; > + > + skb = sip_alloc_ctrl_skbuf(epub->sip, > + sizeof(struct sip_cmd_set_wmm_params) + > + sizeof(struct sip_hdr), > + SIP_CMD_SET_WMM_PARAM); > + if (!skb) > + return -ENOMEM; > + > + bsscmd = (struct sip_cmd_set_wmm_params *)(skb->data + sizeof(struct sip_hdr)); > + bsscmd->aci = aci; > + bsscmd->aifs = params->aifs; > + bsscmd->txop_us = params->txop * 32; > + bsscmd->ecw_min = 32 - __builtin_clz(params->cw_min); > + bsscmd->ecw_max = 32 - __builtin_clz(params->cw_max); > + > + return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL); > +} > + > +int sip_send_ampdu_action(struct esp_pub *epub, u8 action_num, const u8 *addr, > + u16 tid, u16 ssn, u8 buf_size) > +{ > + int index = 0; > + struct sk_buff *skb; > + struct sip_cmd_ampdu_action *action; > + > + if (action_num == SIP_AMPDU_RX_START) > + index = esp_get_empty_rxampdu(epub, addr, tid); > + else if (action_num == SIP_AMPDU_RX_STOP) > + index = esp_get_exist_rxampdu(epub, addr, tid); > + > + if (index < 0) > + return -EACCES; > + > + skb = sip_alloc_ctrl_skbuf(epub->sip, > + sizeof(struct sip_cmd_ampdu_action) + > + sizeof(struct sip_hdr), > + SIP_CMD_AMPDU_ACTION); > + if (!skb) > + return -ENOMEM; > + > + action = (struct sip_cmd_ampdu_action *)(skb->data + sizeof(struct sip_hdr)); > + action->action = action_num; > + //for TX, it means interface index > + action->index = ssn; > + > + switch (action_num) { > + case SIP_AMPDU_RX_START: > + action->ssn = ssn; > + case SIP_AMPDU_RX_STOP: > + action->index = index; > + case SIP_AMPDU_TX_OPERATIONAL: > + case SIP_AMPDU_TX_STOP: > + action->win_size = buf_size; > + action->tid = tid; > + memcpy(action->addr, addr, ETH_ALEN); > + break; > + } > + > + return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL); > +} > + > +#ifdef HW_SCAN > +/* send cmd to target, if aborted is true, inform target stop scan, report scan complete imediately > + * return 1: complete over, 0: success, still have next scan, -1: hardware failure > + */ > +int sip_send_scan(struct esp_pub *epub) > +{ > + struct cfg80211_scan_request *scan_req = epub->wl.scan_req; > + struct sk_buff *skb; > + struct sip_cmd_scan *scancmd; > + u8 *ptr; > + int i; > + u8 append_len, ssid_len; > + > + ESSERT(scan_req); > + if (!scan_req->n_ssids) > + ssid_len = 0; > + else if (scan_req->n_ssids == 1) > + ssid_len = scan_req->ssids->ssid_len; > + /* Limit to two SSIDs */ > + else > + ssid_len = scan_req->ssids->ssid_len + > + (scan_req->ssids + 1)->ssid_len; > + > + append_len = ssid_len + scan_req->n_channels + scan_req->ie_len; > + > + skb = sip_alloc_ctrl_skbuf(epub->sip, sizeof(struct sip_cmd_scan) + > + sizeof(struct sip_hdr) + append_len, > + SIP_CMD_SCAN); > + > + if (!skb) > + return -ENOMEM; > + > + ptr = skb->data; > + scancmd = (struct sip_cmd_scan *)(ptr + sizeof(struct sip_hdr)); > + > + ptr += sizeof(struct sip_hdr) + sizeof(struct sip_cmd_scan); > + scancmd->aborted = false; > + > + /* FIXME: meh */ > + if (scan_req->n_ssids <= 0) { > + scancmd->ssid_len = 0; > + } else { > + scancmd->ssid_len = ssid_len; > + if (scan_req->n_ssids == 1) > + memcpy(ptr, scan_req->ssids->ssid, scancmd->ssid_len); > + /* Limit to two SSIDs */ > + else > + memcpy(ptr, (scan_req->ssids + 1)->ssid, > + scancmd->ssid_len); > + } > + > + ptr += scancmd->ssid_len; > + scancmd->n_channels = scan_req->n_channels; > + for (i = 0; i < scan_req->n_channels; i++) > + ptr[i] = scan_req->channels[i]->hw_value; > + > + ptr += scancmd->n_channels; > + if (scan_req->ie_len && scan_req->ie) { > + scancmd->ie_len = scan_req->ie_len; > + memcpy(ptr, scan_req->ie, scan_req->ie_len); > + } else { > + scancmd->ie_len = 0; > + } > + //add a flag that support two ssids, > + if (scan_req->n_ssids > 1) > + scancmd->ssid_len |= 0x80; > + > + return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL); > +} > +#endif > + > +void sip_scandone_process(struct esp_sip *sip, > + struct sip_evt_scan_report *scan_report) > +{ > + struct esp_pub *epub = sip->epub; > + > + if (epub->wl.scan_req) { > + hw_scan_done(epub, scan_report->aborted); > + epub->wl.scan_req = NULL; > + } > +} > + > +int sip_send_setkey(struct esp_pub *epub, u8 bssid_no, u8 *peer_addr, > + struct ieee80211_key_conf *key, u8 isvalid) > +{ > + struct sip_cmd_setkey *setkeycmd; > + struct sk_buff *skb; > + > + skb = sip_alloc_ctrl_skbuf(epub->sip, sizeof(struct sip_cmd_setkey) + > + sizeof(struct sip_hdr), SIP_CMD_SETKEY); > + > + if (!skb) > + return -ENOMEM; > + > + setkeycmd = (struct sip_cmd_setkey *)(skb->data + sizeof(struct sip_hdr)); > + > + if (peer_addr) > + memcpy(setkeycmd->addr, peer_addr, ETH_ALEN); > + else > + memset(setkeycmd->addr, 0, ETH_ALEN); > + > + setkeycmd->bssid_no = bssid_no; > + setkeycmd->hw_key_idx = key->hw_key_idx; > + > + if (isvalid) { > + setkeycmd->alg = esp_cipher2alg(key->cipher); > + setkeycmd->keyidx = key->keyidx; > + setkeycmd->keylen = key->keylen; > + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) { > + memcpy(setkeycmd->key, key->key, 16); > + memcpy(setkeycmd->key + 16, key->key + 24, 8); > + memcpy(setkeycmd->key + 24, key->key + 16, 8); > + } else { > + memcpy(setkeycmd->key, key->key, key->keylen); > + } > + > + setkeycmd->flags = 1; > + } else { > + setkeycmd->flags = 0; > + } > + > + return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL); > +} > + > +//remain_on_channel > +int sip_send_roc(struct esp_pub *epub, u16 center_freq, u16 duration) > +{ > + struct sk_buff *skb; > + struct sip_cmd_config *configcmd; > + > + skb = sip_alloc_ctrl_skbuf(epub->sip, sizeof(struct sip_cmd_config) + > + sizeof(struct sip_hdr), SIP_CMD_CONFIG); > + if (!skb) > + return -ENOMEM; > + > + configcmd = (struct sip_cmd_config *)(skb->data + sizeof(struct sip_hdr)); > + configcmd->center_freq = center_freq; > + configcmd->duration = duration; > + > + return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL); > +} > + > +int sip_send_set_sta(struct esp_pub *epub, u8 ifidx, u8 set, > + struct ieee80211_sta *sta, struct ieee80211_vif *vif, > + u8 index) > +{ > + struct sk_buff *skb; > + struct sip_cmd_setsta *setstacmd; > + > + skb = sip_alloc_ctrl_skbuf(epub->sip, sizeof(struct sip_cmd_setsta) + > + sizeof(struct sip_hdr), SIP_CMD_SETSTA); > + if (!skb) > + return -ENOMEM; > + > + setstacmd = (struct sip_cmd_setsta *)(skb->data + sizeof(struct sip_hdr)); > + setstacmd->ifidx = ifidx; > + setstacmd->index = index; > + setstacmd->set = set; > + > + if (!sta->aid) > + setstacmd->aid = vif->bss_conf.aid; > + else > + setstacmd->aid = sta->aid; > + > + memcpy(setstacmd->mac, sta->addr, ETH_ALEN); > + > + if (!set) > + goto send; > + > + if (sta->ht_cap.ht_supported) { > + if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) > + setstacmd->phymode = ESP_IEEE80211_T_HT20_S; > + else > + setstacmd->phymode = ESP_IEEE80211_T_HT20_L; > + setstacmd->ampdu_factor = sta->ht_cap.ampdu_factor; > + setstacmd->ampdu_density = sta->ht_cap.ampdu_density; > + goto send; > + } > + > + if (sta->supp_rates[NL80211_BAND_2GHZ] & ~CONF_HW_BIT_RATE_11B_MASK) > + setstacmd->phymode = ESP_IEEE80211_T_OFDM; > + else > + setstacmd->phymode = ESP_IEEE80211_T_CCK; > + > +send: > + return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL); > +} > + > +int sip_send_recalc_credit(struct esp_pub *epub) > +{ > + struct sk_buff *skb; > + > + skb = sip_alloc_ctrl_skbuf(epub->sip, sizeof(struct sip_hdr), > + SIP_CMD_RECALC_CREDIT); > + if (!skb) > + return -ENOMEM; > + > + return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_HEAD); > +} > + > +int sip_cmd(struct esp_pub *epub, enum sip_cmd_id cmd_id, u8 *cmd_buf, > + u8 cmd_len) > +{ > + struct sk_buff *skb; > + > + skb = sip_alloc_ctrl_skbuf(epub->sip, cmd_len + sizeof(struct sip_hdr), > + cmd_id); > + if (!skb) > + return -ENOMEM; > + > + memcpy(skb->data + sizeof(struct sip_hdr), cmd_buf, cmd_len); > + > + return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL); > +} > diff --git a/drivers/staging/esp8089/esp_ctrl.h b/drivers/staging/esp8089/esp_ctrl.h > new file mode 100644 > index 000000000000..b9143bb135b3 > --- /dev/null > +++ b/drivers/staging/esp8089/esp_ctrl.h > @@ -0,0 +1,48 @@ > +/* > + * Copyright (c) 2009 - 2014 Espressif System. > + * Copyright (c) 2017 Free Electrons > + * Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it under > + * the terms of the GNU General Public License version 2 as published by the > + * Free Software Foundation. > + * > + */ > +#ifndef _ESP_CTRL_H_ > +#define _ESP_CTRL_H_ > + > +extern struct completion *gl_bootup_cplx; > + > +int sip_send_config(struct esp_pub *epub, struct ieee80211_conf *conf); > + > +int sip_send_setkey(struct esp_pub *epub, u8 bssid_no, u8 *peer_addr, > + struct ieee80211_key_conf *key, u8 isvalid); > + > +int sip_send_scan(struct esp_pub *epub); > + > +void sip_scandone_process(struct esp_sip *sip, > + struct sip_evt_scan_report *scan_report); > + > +int sip_send_bss_info_update(struct esp_pub *epub, struct esp_vif *evif, > + u8 *bssid, int assoc); > + > +int sip_send_wmm_params(struct esp_pub *epub, u8 aci, > + const struct ieee80211_tx_queue_params *params); > + > +int sip_send_ampdu_action(struct esp_pub *epub, u8 action_num, const u8 *addr, > + u16 tid, u16 ssn, u8 buf_size); > + > +int sip_send_roc(struct esp_pub *epub, u16 center_freq, u16 duration); > + > +int sip_send_set_sta(struct esp_pub *epub, u8 ifidx, u8 set, > + struct ieee80211_sta *sta, struct ieee80211_vif *vif, > + u8 index); > + > +int sip_parse_events(struct esp_sip *sip, u8 *buf); > + > +int sip_send_recalc_credit(struct esp_pub *epub); > + > +int sip_cmd(struct esp_pub *epub, enum sip_cmd_id cmd_id, u8 *cmd_buf, > + u8 cmd_len); > + > +#endif /* _ESP_CTRL_H_ */ > diff --git a/drivers/staging/esp8089/esp_debug.c b/drivers/staging/esp8089/esp_debug.c > new file mode 100644 > index 000000000000..26472b433768 > --- /dev/null > +++ b/drivers/staging/esp8089/esp_debug.c > @@ -0,0 +1,247 @@ > +/* > + * Copyright (c) 2011 - 2014 Espressif System. > + * Copyright (c) 2017 Free Electrons > + * Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it under > + * the terms of the GNU General Public License version 2 as published by the > + * Free Software Foundation. > + * > + */ > + > +#include <linux/types.h> > +#include <linux/kernel.h> > + > +#include <net/mac80211.h> > +#include "sip2_common.h" > + > +#include "esp_debug.h" > + > +#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_ESP8089_DEBUG_FS) > + > +static struct dentry *esp_debugfs_root; > + > +static ssize_t esp_debugfs_read(struct file *filp, char __user *buffer, > + size_t count, loff_t *ppos) > +{ > + if (*ppos >= 32) > + return 0; > + > + if (*ppos + count > 32) > + count = 32 - *ppos; > + > + if (copy_to_user(buffer, filp->private_data + *ppos, count)) > + return -EFAULT; > + > + *ppos += count; > + > + return count; > +} > + > +static ssize_t esp_debugfs_write(struct file *filp, const char __user *buffer, > + size_t count, loff_t *ppos) > +{ > + if (*ppos >= 32) > + return 0; > + > + if (*ppos + count > 32) > + count = 32 - *ppos; > + > + if (copy_from_user(filp->private_data + *ppos, buffer, count)) > + return -EFAULT; > + > + *ppos += count; > + > + return count; > +} > + > +const struct file_operations esp_debugfs_fops = { > + .owner = THIS_MODULE, > + .open = simple_open, > + .read = esp_debugfs_read, > + .write = esp_debugfs_write, > +}; > + > +struct dentry *esp_dump_var(const char *name, struct dentry *parent, > + void *value, enum esp_type type) > +{ > + struct dentry *rc = NULL; > + umode_t mode = 0644; > + > + if (!esp_debugfs_root) > + return NULL; > + > + if (!parent) > + parent = esp_debugfs_root; > + > + switch (type) { > + case ESP_U8: > + rc = debugfs_create_u8(name, mode, parent, (u8 *)value); > + break; > + case ESP_U16: > + rc = debugfs_create_u16(name, mode, parent, (u16 *)value); > + break; > + case ESP_U32: > + rc = debugfs_create_u32(name, mode, parent, (u32 *)value); > + break; > + case ESP_U64: > + rc = debugfs_create_u64(name, mode, parent, (u64 *)value); > + break; > + case ESP_BOOL: > + rc = debugfs_create_bool(name, mode, parent, (bool *)value); > + break; > + default: //32 > + rc = debugfs_create_u32(name, mode, parent, (u32 *)value); > + } > + > + if (!rc) > + goto _fail; > + > + return rc; > + > +_fail: > + debugfs_remove_recursive(esp_debugfs_root); > + esp_debugfs_root = NULL; > + printk("%s failed, debugfs root removed; var name: %s\n", __func__, > + name); > + return NULL; > +} > + > +struct dentry *esp_dump_array(const char *name, struct dentry *parent, > + struct debugfs_blob_wrapper *blob) > +{ > + struct dentry *rc; > + umode_t mode = 0644; > + > + if (!esp_debugfs_root) > + return NULL; > + > + if (!parent) > + parent = esp_debugfs_root; > + > + rc = debugfs_create_blob(name, mode, parent, blob); > + if (!rc) > + goto _fail; > + > + return rc; > + > +_fail: > + debugfs_remove_recursive(esp_debugfs_root); > + esp_debugfs_root = NULL; > + printk("%s failed, debugfs root removed; var name: %s\n", __func__, > + name); > + return NULL; > +} > + > +struct dentry *esp_dump(const char *name, struct dentry *parent, > + void *data, int size) > +{ > + struct dentry *rc; > + umode_t mode = 0644; > + > + if (!esp_debugfs_root) > + return NULL; > + > + if (!parent) > + parent = esp_debugfs_root; > + > + rc = debugfs_create_file(name, mode, parent, data, &esp_debugfs_fops); > + if (!rc) > + goto _fail; > + > + return rc; > + > +_fail: > + debugfs_remove_recursive(esp_debugfs_root); > + esp_debugfs_root = NULL; > + printk("%s failed, debugfs root removed; var name: %s\n", __func__, > + name); > + return NULL; > +} > + > +struct dentry *esp_debugfs_add_sub_dir(const char *name) > +{ > + struct dentry *sub_dir; > + > + sub_dir = debugfs_create_dir(name, esp_debugfs_root); > + if (!sub_dir) > + goto _fail; > + > + return sub_dir; > + > +_fail: > + debugfs_remove_recursive(esp_debugfs_root); > + esp_debugfs_root = NULL; > + printk("%s failed, debugfs root removed; dir name: %s\n", __func__, > + name); > + return NULL; > +} > + > +int esp_debugfs_init(void) > +{ > + esp_debugfs_root = debugfs_create_dir("esp_debug", NULL); > + > + if (IS_ERR_OR_NULL(esp_debugfs_root)) > + return -ENOENT; > + > + return 0; > +} > + > +void esp_debugfs_exit(void) > +{ > + debugfs_remove_recursive(esp_debugfs_root); > +} > + > +#else > + > +inline struct dentry *esp_dump_var(const char *name, struct dentry *parent, > + void *value, enum esp_type type) > +{ > + return NULL; > +} > + > +inline struct dentry *esp_dump_array(const char *name, struct dentry *parent, > + struct debugfs_blob_wrapper *blob) > +{ > + return NULL; > +} > + > +inline struct dentry *esp_dump(const char *name, struct dentry *parent, > + void *data, int size) > +{ > + return NULL; > +} > + > +struct dentry *esp_debugfs_add_sub_dir(const char *name) > +{ > + return NULL; > +} > + > +inline int esp_debugfs_init(void) > +{ > + return -EPERM; > +} > + > +inline void esp_debugfs_exit(void) > +{ > +} > + > +#endif > + > +/* FIXME: What's the actual usecase? */ > +void show_buf(u8 *buf, u32 len) > +{ > + int i = 0, j; > + > + printk(KERN_INFO "\n++++++++++++++++show rbuf+++++++++++++++\n"); > + for (i = 0; i < (len / 16); i++) { > + j = i * 16; > + printk(KERN_INFO > + "0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", > + buf[j], buf[j + 1], buf[j + 2], buf[j + 3], > + buf[j + 4], buf[j + 5], buf[j + 6], buf[j + 7], > + buf[j + 8], buf[j + 9], buf[j + 10], buf[j + 11], > + buf[j + 12], buf[j + 13], buf[j + 14], buf[j + 15]); > + } > + printk(KERN_INFO "\n++++++++++++++++++++++++++++++++++++++++\n"); > +} > diff --git a/drivers/staging/esp8089/esp_debug.h b/drivers/staging/esp8089/esp_debug.h > new file mode 100644 > index 000000000000..eb9bddfe9842 > --- /dev/null > +++ b/drivers/staging/esp8089/esp_debug.h > @@ -0,0 +1,69 @@ > +/* > + * Copyright (c) 2011 - 2014 Espressif System. > + * Copyright (c) 2017 Free Electrons > + * Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it under > + * the terms of the GNU General Public License version 2 as published by the > + * Free Software Foundation. > + * > + */ > + > +#ifndef _DEBUG_H_ > + > +#ifdef ASSERT_PANIC > +#define ESSERT(v) BUG_ON(!(v)) > +#else > +#define ESSERT(v) if(!(v)) printk("ESSERT:%s %d\n", __FILE__, __LINE__) > +#endif > + > +#include <linux/slab.h> > +#include <linux/debugfs.h> > +#include <linux/uaccess.h> > + > +enum esp_type { > + ESP_BOOL, > + ESP_U8, > + ESP_U16, > + ESP_U32, > + ESP_U64 > +}; > + > +struct dentry *esp_dump_var(const char *name, struct dentry *parent, > + void *value, enum esp_type type); > + > +struct dentry *esp_dump_array(const char *name, struct dentry *parent, > + struct debugfs_blob_wrapper *blob); > + > +struct dentry *esp_dump(const char *name, struct dentry *parent, void *data, > + int size); > + > +struct dentry *esp_debugfs_add_sub_dir(const char *name); > + > +int esp_debugfs_init(void); > + > +void esp_debugfs_exit(void); > + > +enum { > + ESP_DBG_ERROR = BIT(0), > + ESP_DBG_TRACE = BIT(1), > + ESP_DBG_LOG = BIT(2), > + ESP_DBG = BIT(3), > + ESP_SHOW = BIT(4), > + ESP_DBG_TXAMPDU = BIT(5), > + ESP_DBG_OP = BIT(6), > + ESP_DBG_PS = BIT(7), > + ESP_ATE = BIT(8), > + ESP_DBG_ALL = GENMASK(31, 0) > +}; > + > +extern unsigned int esp_msg_level; > + > +#define esp_dbg(mask, fmt, args...) do { \ > + if (esp_msg_level & mask) \ > + printk(fmt, ##args); \ > +} while (0) > + > +void show_buf(u8 *buf, u32 len); > + > +#endif /* _DEBUG_H_ */ > diff --git a/drivers/staging/esp8089/esp_file.c b/drivers/staging/esp8089/esp_file.c > new file mode 100644 > index 000000000000..7c7ef0d83693 > --- /dev/null > +++ b/drivers/staging/esp8089/esp_file.c > @@ -0,0 +1,221 @@ > +/* > + * Copyright (c) 2010 - 2014 Espressif System. > + * Copyright (c) 2017 Free Electrons > + * Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it under > + * the terms of the GNU General Public License version 2 as published by the > + * Free Software Foundation. > + * > + */ > + > +#include <linux/fs.h> > +#include <linux/vmalloc.h> > +#include <linux/kernel.h> > +#include <linux/moduleparam.h> > +#include <linux/firmware.h> > +#include <linux/netdevice.h> > +#include <linux/aio.h> > +#include <linux/property.h> > + > +#include "esp_file.h" > +#include "esp_debug.h" > +#include "esp_sif.h" > + > +/* TODO use proper module param for each value instead of a big one */ > +static char *modparam_init_data_conf; > +module_param_named(config, modparam_init_data_conf, charp, 0444); > +MODULE_PARM_DESC(config, "Firmware init config string (format: key=value;)"); > + > +struct esp_init_table_elem esp_init_table[MAX_ATTR_NUM] = { > + /* Crystal type: > + * 0: 40MHz (default) > + * 1: 26MHz (ESP8266 ESP-12F) > + */ > + {"crystal_26M_en", 48, 0}, > + /* Output crystal clock to pin: > + * 0: None > + * 1: GPIO1 > + * 2: URXD0 > + */ > + {"test_xtal", 49, 0}, > + /* Host SDIO mode: > + * 0: Auto by pin strapping > + * 1: SDIO data output on negative edges (SDIO v1.1) > + * 2: SDIO data output on positive edges (SDIO v2.0) > + */ > + {"sdio_configure", 50, 2}, > + /* WiFi/Bluetooth co-existence with BK3515A BT chip > + * 0: None > + * 1: GPIO0->WLAN_ACTIVE, MTMS->BT_ACTIVE, MTDI->BT_PRIORITY, > + * U0TXD->ANT_SEL_BT, U0RXD->ANT_SEL_WIFI > + */ > + {"bt_configure", 51, 0}, > + /* Antenna selection: > + * 0: Antenna is for WiFi > + * 1: Antenna is for Bluetooth > + */ > + {"bt_protocol", 52, 0}, > + /* Dual antenna configuration mode: > + * 0: None > + * 1: U0RXD + XPD_DCDC > + * 2: U0RXD + GPIO0 > + * 3: U0RXD + U0TXD > + */ > + {"dual_ant_configure", 53, 0}, > + /* Firmware debugging output pin: > + * 0: None > + * 1: UART TX on GPIO2 > + * 2: UART TX on U0TXD > + */ > + {"test_uart_configure", 54, 2}, > + /* Whether to share crystal clock with BT (in sleep mode): > + * 0: no > + * 1: always on > + * 2: automatically on according to XPD_DCDC > + */ > + {"share_xtal", 55, 0}, > + /* Allow chip to be woken up during sleep on pin: > + * 0: None > + * 1: XPD_DCDC > + * 2: GPIO0 > + * 3: Both XPD_DCDC and GPIO0 > + */ > + {"gpio_wake", 56, 0}, > + {"no_auto_sleep", 57, 0}, > + {"speed_suspend", 58, 0}, > + {"attr11", -1, -1}, > + {"attr12", -1, -1}, > + {"attr13", -1, -1}, > + {"attr14", -1, -1}, > + {"attr15", -1, -1}, > + //attr that is not send to target > + /* Allow chip to be reset by GPIO pin: > + * 0: no > + * 1: yes > + */ > + {"ext_rst", -1, 0}, > + {"wakeup_gpio", -1, 12}, > + {"ate_test", -1, 0}, > + {"attr19", -1, -1}, > + {"attr20", -1, -1}, > + {"attr21", -1, -1}, > + {"attr22", -1, -1}, > + {"attr23", -1, -1}, > +}; > + > +/* update init config table */ > +static int set_init_config_attr(const char *attr, int attr_len, short value) > +{ > + int i; > + > + for (i = 0; i < MAX_ATTR_NUM; i++) > + if (!memcmp(esp_init_table[i].attr, attr, attr_len)) { > + if (value < 0 || value > 255) { > + printk("%s: attribute value for %s is out of range", > + __func__, esp_init_table[i].attr); > + return -1; > + } > + > + esp_init_table[i].value = value; > + return 0; > + } > + > + return -1; > +} > + > +static int update_init_config_attr(const char *attr, int attr_len, > + const char *val, int val_len) > +{ > + char digits[4]; > + short value; > + int i; > + > + for (i = 0; i < sizeof(digits) - 1 && i < val_len; i++) > + digits[i] = val[i]; I don't think we really need to memcpy() val to digits[]. > + > + digits[i] = 0; > + > + if (kstrtou16(digits, 10, &value) < 0) { > + printk("%s: invalid attribute value: %s", __func__, digits); > + return -1; propogate the error code. > + } > + > + return set_init_config_attr(attr, attr_len, value); > +} > + > +/* export config table settings to SDIO driver */ > +static void record_init_config(void) > +{ > + int i; > + > + for (i = 0; i < MAX_ATTR_NUM; i++) { > + if (esp_init_table[i].value < 0) > + continue; > + > + if (!strcmp(esp_init_table[i].attr, "share_xtal")) > + sif_record_bt_config(esp_init_table[i].value); > + else if (!strcmp(esp_init_table[i].attr, "ext_rst")) > + sif_record_rst_config(esp_init_table[i].value); > + else if (!strcmp(esp_init_table[i].attr, "wakeup_gpio")) > + sif_record_wakeup_gpio_config(esp_init_table[i].value); > + else if (!strcmp(esp_init_table[i].attr, "ate_test")) > + sif_record_ate_config(esp_init_table[i].value); > + } > +} > + > +int request_init_conf(struct device *dev) > +{ > + char *attr, *str, *p; > + int attr_len, str_len; > + int ret = 0; > + u32 val; > + > + /* Check for any parameters passed through devicetree (or acpi) */ > + if (!device_property_read_u32(dev, "esp,crystal-26M-en", &val)) > + set_init_config_attr("crystal_26M_en", strlen("crystal_26M_en"), > + val); > + > + /* parse optional parameter in the form of key1=value,key2=value,.. */ > + attr = NULL; > + attr_len = 0; > + str_len = 0; > + for (p = str = modparam_init_data_conf; p && *p; p++) { > + if (*p == '=') { > + attr = str; > + attr_len = str_len; > + > + str = p + 1; > + str_len = 0; > + } else if (*p == ',' || *p == ';') { > + if (attr_len) > + ret |= update_init_config_attr(attr, attr_len, > + str, str_len); > + > + str = p + 1; > + attr_len = 0; > + str_len = 0; > + } else { > + str_len++; > + } > + } > + > + if (attr_len && str != attr) > + ret |= update_init_config_attr(attr, attr_len, str, str_len); > + > + record_init_config(); > + > + return ret; > +} > + > +void fix_init_data(u8 *init_data_buf, int buf_size) > +{ > + int i; > + > + for (i = 0; i < MAX_FIX_ATTR_NUM; i++) { > + if (esp_init_table[i].offset > -1 && > + esp_init_table[i].offset < buf_size && > + esp_init_table[i].value > -1) > + *(u8 *)(init_data_buf + esp_init_table[i].offset) = esp_init_table[i].value; > + } > +} > diff --git a/drivers/staging/esp8089/esp_file.h b/drivers/staging/esp8089/esp_file.h > new file mode 100644 > index 000000000000..ba9946d64c32 > --- /dev/null > +++ b/drivers/staging/esp8089/esp_file.h > @@ -0,0 +1,30 @@ > +/* > + * Copyright (c) 2010 - 2014 Espressif System. > + * Copyright (c) 2017 Free Electrons > + * Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it under > + * the terms of the GNU General Public License version 2 as published by the > + * Free Software Foundation. > + * > + */ > + > +#ifndef _ESP_FILE_H_ > +#define _ESP_FILE_H_ > + > +#include <linux/firmware.h> > + > +#define CONF_ATTR_LEN 24 > +#define MAX_ATTR_NUM 24 > +#define MAX_FIX_ATTR_NUM 16 > + > +struct esp_init_table_elem { > + char attr[CONF_ATTR_LEN]; > + int offset; > + short value; > +}; > + > +int request_init_conf(struct device *dev); > +void fix_init_data(u8 *init_data_buf, int buf_size); > + > +#endif /* _ESP_FILE_H_ */ > diff --git a/drivers/staging/esp8089/esp_init_data.h b/drivers/staging/esp8089/esp_init_data.h > new file mode 100644 > index 000000000000..2494c02a10be > --- /dev/null > +++ b/drivers/staging/esp8089/esp_init_data.h > @@ -0,0 +1,17 @@ > +/* > + * Copyright (c) 2009 - 2014 Espressif System. > + * Copyright (c) 2017 Free Electrons > + * Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it under > + * the terms of the GNU General Public License version 2 as published by the > + * Free Software Foundation. > + * > + */ > + > +static char esp_init_data[] = { 0x5, 0x0, 4, 2, 5, 5, 5, 2, 5, 0, 4, 5, 5, 4, 5, 5, 4, -2, -3, -1, > + -16, -16, -16, -32, -32, -32, 204, 1, 0xff, 0xff, 0, 0, 0, 0, 82, 78, 74, 68, 64, 56, 0, > + 0, 1, 1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 10, 0x0, 0x0, > + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, > + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, > + 0 }; > diff --git a/drivers/staging/esp8089/esp_io.c b/drivers/staging/esp8089/esp_io.c > new file mode 100644 > index 000000000000..756bd6260b24 > --- /dev/null > +++ b/drivers/staging/esp8089/esp_io.c > @@ -0,0 +1,294 @@ > +/* > + * Copyright (c) 2009 - 2014 Espressif System. > + * Copyright (c) 2017 Free Electrons > + * Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it under > + * the terms of the GNU General Public License version 2 as published by the > + * Free Software Foundation. > + * > + */ > + > +#include <linux/mmc/sdio_func.h> > +#include "esp_sif.h" > +#include "slc_host_register.h" > +#include "esp_debug.h" > + > +int esp_common_read(struct esp_pub *epub, u8 *buf, u32 len, int sync, > + bool noround) > +{ > + if (sync) > + return sif_lldesc_read_sync(epub, buf, len); > + > + return sif_lldesc_read_raw(epub, buf, len, noround); > +} > + > +int esp_common_write(struct esp_pub *epub, u8 *buf, u32 len, int sync) > +{ > + if (sync) > + return sif_lldesc_write_sync(epub, buf, len); > + > + return sif_lldesc_write_raw(epub, buf, len); > +} > + > +int esp_common_read_with_addr(struct esp_pub *epub, u32 addr, u8 *buf, u32 len, > + int sync) > +{ > + if (sync) > + return sif_io_sync(epub, addr, buf, len, SIF_FROM_DEVICE | > + SIF_SYNC | SIF_BYTE_BASIS | SIF_INC_ADDR); > + > + return sif_io_raw(epub, addr, buf, len, SIF_FROM_DEVICE | > + SIF_BYTE_BASIS | SIF_INC_ADDR); > +} > + > +int esp_common_write_with_addr(struct esp_pub *epub, u32 addr, u8 *buf, > + u32 len, int sync) > +{ > + if (sync) > + return sif_io_sync(epub, addr, buf, len, SIF_TO_DEVICE | > + SIF_SYNC | SIF_BYTE_BASIS | SIF_INC_ADDR); > + > + return sif_io_raw(epub, addr, buf, len, SIF_TO_DEVICE | SIF_BYTE_BASIS | > + SIF_INC_ADDR); > +} > + > +int esp_common_readbyte_with_addr(struct esp_pub *epub, u32 addr, u8 *buf, > + int sync) > +{ > + int res; > + > + if (sync) > + sif_lock_bus(epub); > + > + *buf = sdio_io_readb(epub, addr, &res); > + > + if (sync) > + sif_unlock_bus(epub); > + > + return res; > +} > + > +int esp_common_writebyte_with_addr(struct esp_pub *epub, u32 addr, u8 buf, > + int sync) > +{ > + int res; > + > + if (sync) > + sif_lock_bus(epub); > + > + sdio_io_writeb(epub, buf, addr, &res); > + > + if (sync) > + sif_unlock_bus(epub); > + > + return res; > +} > + > +enum _SDIO_INTR_MODE { > + SDIO_INTR_IB = 0, > + SDIO_INTR_OOB_TOGGLE, > + SDIO_INTR_OOB_HIGH_LEVEL, > + SDIO_INTR_OOB_LOW_LEVEL, > +}; > + > +#define GEN_GPIO_SEL(_gpio_num, _sel_func, _intr_mode, _offset) (((_offset) << 9) | ((_intr_mode) << 7) | ((_sel_func) << 4) | (_gpio_num)) > +//bit[3:0] = gpio num, 2 > +//bit[6:4] = gpio sel func, 0 > +//bit[8:7] = gpio intr mode, SDIO_INTR_OOB_TOGGLE > +//bit[15:9] = register offset, 0x38 > + > +u16 gpio_sel_sets[17] = { > + GEN_GPIO_SEL(0, 0, SDIO_INTR_OOB_TOGGLE, 0x34), //GPIO0 > + GEN_GPIO_SEL(1, 3, SDIO_INTR_OOB_TOGGLE, 0x18), //U0TXD > + GEN_GPIO_SEL(2, 0, SDIO_INTR_OOB_TOGGLE, 0x38), //GPIO2 > + GEN_GPIO_SEL(3, 3, SDIO_INTR_OOB_TOGGLE, 0x14), //U0RXD > + GEN_GPIO_SEL(4, 0, SDIO_INTR_OOB_TOGGLE, 0x3C), //GPIO4 > + GEN_GPIO_SEL(5, 0, SDIO_INTR_OOB_TOGGLE, 0x40), //GPIO5 > + GEN_GPIO_SEL(6, 3, SDIO_INTR_OOB_TOGGLE, 0x1C), //SD_CLK > + GEN_GPIO_SEL(7, 3, SDIO_INTR_OOB_TOGGLE, 0x20), //SD_DATA0 > + GEN_GPIO_SEL(8, 3, SDIO_INTR_OOB_TOGGLE, 0x24), //SD_DATA1 > + GEN_GPIO_SEL(9, 3, SDIO_INTR_OOB_TOGGLE, 0x28), //SD_DATA2 > + GEN_GPIO_SEL(10, 3, SDIO_INTR_OOB_TOGGLE, 0x2C), //SD_DATA3 > + GEN_GPIO_SEL(11, 3, SDIO_INTR_OOB_TOGGLE, 0x30), //SD_CMD > + GEN_GPIO_SEL(12, 3, SDIO_INTR_OOB_TOGGLE, 0x04), //MTDI > + GEN_GPIO_SEL(13, 3, SDIO_INTR_OOB_TOGGLE, 0x08), //MTCK > + GEN_GPIO_SEL(14, 3, SDIO_INTR_OOB_TOGGLE, 0x0C), //MTMS > + GEN_GPIO_SEL(15, 3, SDIO_INTR_OOB_TOGGLE, 0x10), //MTDO > + //pls do not change sel before, if you want to change intr mode,change the one blow > + //GEN_GPIO_SEL(2, 0, SDIO_INTR_OOB_TOGGLE, 0x38) > + GEN_GPIO_SEL(2, 0, SDIO_INTR_OOB_LOW_LEVEL, 0x38) > +}; > + > +int sif_interrupt_target(struct esp_pub *epub, u8 index) > +{ > + u8 low_byte = BIT(index); > + return esp_common_writebyte_with_addr(epub, SLC_HOST_CONF_W4 + 2, > + low_byte, ESP_SIF_NOSYNC); > +} > + > +void check_target_id(struct esp_pub *epub) > +{ > + u32 date; > + int i; > + u16 gpio_sel; > + u8 byte2 = 0, byte3 = 0; > + > + if (!epub || !epub->sif) > + return; > + > + sif_lock_bus(epub); > + > + for (i = 0; i < 4; i++) { > + esp_common_readbyte_with_addr(epub, SLC_HOST_DATE + i, > + (u8 *)&date + i, ESP_SIF_NOSYNC); > + esp_common_readbyte_with_addr(epub, SLC_HOST_ID + i, > + (u8 *)&epub->sif->target_id + i, > + ESP_SIF_NOSYNC); > + } > + > + sif_unlock_bus(epub); > + > + switch (epub->sif->target_id) { > + case 0x100: > + epub->sif->slc_window_end_addr = 0x20000; > + break; > + case 0x600: > + epub->sif->slc_window_end_addr = 0x20000 - 0x800; > + > + if (sif_get_bt_config() == 1 && sif_get_rst_config() != 1) { > + u8 gpio_num = sif_get_wakeup_gpio_config(); > + gpio_sel = gpio_sel_sets[gpio_num]; > + byte2 = gpio_sel; > + byte3 = gpio_sel >> 8; > + } > + > + sif_lock_bus(epub); > + esp_common_writebyte_with_addr(epub, SLC_HOST_CONF_W1, 0, > + ESP_SIF_NOSYNC); > + esp_common_writebyte_with_addr(epub, SLC_HOST_CONF_W1 + 1, 0, > + ESP_SIF_NOSYNC); > + esp_common_writebyte_with_addr(epub, SLC_HOST_CONF_W1 + 2, > + byte2, ESP_SIF_NOSYNC); > + esp_common_writebyte_with_addr(epub, SLC_HOST_CONF_W1 + 3, > + byte3, ESP_SIF_NOSYNC); > + sif_unlock_bus(epub); > + break; > + default: > + epub->sif->slc_window_end_addr = 0x20000; > + break; > + } > +} > + > +u32 sif_get_blksz(struct esp_pub *epub) > +{ > + if (!epub || !epub->sif) > + return 512; > + > + return epub->sif->slc_blk_sz; > +} > + > +void sif_dsr(struct sdio_func *func) > +{ > + struct esp_sdio_ctrl *sctrl = sdio_get_drvdata(func); > + struct slc_host_regs *regs = &sctrl->slc_regs; > + int ret; > + > + sdio_release_host(sctrl->func); > + > + sif_lock_bus(sctrl->epub); > + memset(regs, 0, sizeof(struct slc_host_regs)); > + > + ret = esp_common_read_with_addr(sctrl->epub, REG_SLC_HOST_BASE + 8, > + (u8 *)regs, sizeof(*regs), > + ESP_SIF_NOSYNC); > + > + if (regs->intr_raw & SLC_HOST_RX_ST && !ret) > + esp_dsr(sctrl->epub); > + else > + sif_unlock_bus(sctrl->epub); > + > + /* FIXME: missing unlock_bus? */ I think the bus mostly gets unlocked in esp_dsr() but it's not clear how that happens on the success path. But that's the common path so it must happen. I don't like that we mix sif_lock_bus() and sdio_claim_host(). I guess I would prefer that we could get rid of sif_lock_bus() because we should be locking instead of saying "We have too many NULL pointers so let's forget about locking for now." > + sdio_claim_host(func); > + atomic_set(&sctrl->irq_handling, 0); > +} > + > +struct slc_host_regs *sif_get_regs(struct esp_pub *epub) > +{ > + if (!epub || !epub->sif) > + return NULL; > + > + return &epub->sif->slc_regs; > +} > + > +void sif_disable_target_interrupt(struct esp_pub *epub) > +{ > + if (!epub || !epub->sif || !epub->sif->func) > + return; > + > + sif_lock_bus(epub); > +#ifdef HOST_RESET_BUG > + mdelay(10); > +#endif > + memset(epub->sif->dma_buffer, 0, sizeof(u32)); > + esp_common_write_with_addr(epub, SLC_HOST_INT_ENA, > + epub->sif->dma_buffer, sizeof(u32), > + ESP_SIF_NOSYNC); > +#ifdef HOST_RESET_BUG > + mdelay(10); > +#endif > + > + sif_unlock_bus(epub); > + > + mdelay(1); > + > + sif_lock_bus(epub); > + sif_interrupt_target(epub, 7); > + sif_unlock_bus(epub); > +} > + > +/* FIXME: MEH */ > + > +static int bt_config; > +void sif_record_bt_config(int value) > +{ > + bt_config = value; > +} > + > +int sif_get_bt_config(void) > +{ > + return bt_config; > +} > + > +static int rst_config; > +void sif_record_rst_config(int value) > +{ > + rst_config = value; > +} > + > +int sif_get_rst_config(void) > +{ > + return rst_config; > +} > + > +static int ate_test; > +void sif_record_ate_config(int value) > +{ > + ate_test = value; > +} > + > +int sif_get_ate_config(void) > +{ > + return ate_test; > +} > + > +static int wakeup_gpio = 12; > +void sif_record_wakeup_gpio_config(int value) > +{ > + wakeup_gpio = value; > +} > + > +int sif_get_wakeup_gpio_config(void) > +{ > + return wakeup_gpio; > +} > diff --git a/drivers/staging/esp8089/esp_mac80211.c b/drivers/staging/esp8089/esp_mac80211.c > new file mode 100644 > index 000000000000..fd5049fc1f6b > --- /dev/null > +++ b/drivers/staging/esp8089/esp_mac80211.c > @@ -0,0 +1,1496 @@ > +/* > + * Copyright (c) 2011 - 2014 Espressif System. > + * Copyright (c) 2017 Free Electrons > + * Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it under > + * the terms of the GNU General Public License version 2 as published by the > + * Free Software Foundation. > + * > + */ > + > +#include <linux/etherdevice.h> > +#include <linux/workqueue.h> > +#include <linux/nl80211.h> > +#include <linux/ieee80211.h> > +#include <linux/slab.h> > +#include <net/cfg80211.h> > +#include <net/mac80211.h> > +#include <net/regulatory.h> > +#include "esp_pub.h" > +#include "esp_sip.h" > +#include "esp_ctrl.h" > +#include "esp_sif.h" > +#include "esp_debug.h" > +#include "esp_wl.h" > +#include "esp_utils.h" > + > +static u8 esp_mac_addr[ETH_ALEN * 2]; > +static u8 getaddr_index(u8 *addr, struct esp_pub *epub); > + > +/*Handler that 802.11 module calls for each transmitted frame. > +skb contains the buffer starting from the IEEE 802.11 header. > +The low-level driver should send the frame out based on > +configuration in the TX control data. This handler should, > +preferably, never fail and stop queues appropriately. > +Must be atomic.*/ > +static void esp_op_tx(struct ieee80211_hw *hw, > + struct ieee80211_tx_control *control, struct sk_buff *skb) > +{ > + struct esp_pub *epub = (struct esp_pub *)hw->priv; > + > + sip_tx_data_pkt_enqueue(epub, skb); > + if (epub) > + ieee80211_queue_work(hw, &epub->tx_work); > +} > + > +/* > + Called before the first netdevice attached to the hardware > + 2934 * is enabled. This should turn on the hardware and must turn on > + 2935 * frame reception (for possibly enabled monitor interfaces.) > + 2936 * Returns negative error codes, these may be seen in userspace, > + 2937 * or zero. > + 2938 * When the device is started it should not have a MAC address > + 2939 * to avoid acknowledging frames before a non-monitor device > + 2940 * is added. > + 2941 * Must be implemented and can sleep. > +*/ > +static int esp_op_start(struct ieee80211_hw *hw) > +{ > + struct esp_pub *epub; > + > + if (!hw || !hw->priv) { > + return -EINVAL; > + } > + > + epub = (struct esp_pub *)hw->priv; > + > + /*add rfkill poll function */ > + > + atomic_set(&epub->wl.off, 0); > + wiphy_rfkill_start_polling(hw->wiphy); > + > + return 0; > +} > + > +/* > +Called after last netdevice attached to the hardware > +2944 * is disabled. This should turn off the hardware (at least > +2945 * it must turn off frame reception.) > +2946 * May be called right after add_interface if that rejects > +2947 * an interface. If you added any work onto the mac80211 workqueue > +2948 * you should ensure to cancel it on this callback. > +2949 * Must be implemented and can sleep. > +*/ > +static void esp_op_stop(struct ieee80211_hw *hw) > +{ > + struct esp_pub *epub; > + > + if (!hw || !hw->priv) { > + return; > + } > + > + epub = (struct esp_pub *)hw->priv; > + atomic_set(&epub->wl.off, 1); > + > +#ifdef HOST_RESET_BUG > + mdelay(200); > +#endif > + > + if (epub->wl.scan_req) { > + hw_scan_done(epub, true); > + epub->wl.scan_req = NULL; > + //msleep(2); > + } > + > + /* FIXME: does this 'turn off frame reception'? */ > + wiphy_rfkill_stop_polling(hw->wiphy); > + /* FIXME: flush queues? */ > +} > + > +static int esp_set_svif_mode(struct sip_cmd_setvif *svif, > + enum nl80211_iftype type, bool p2p) > +{ > + switch (type) { > + case NL80211_IFTYPE_STATION: > + svif->op_mode = 0; > + svif->is_p2p = p2p; > + break; > + > + case NL80211_IFTYPE_AP: > + svif->op_mode = 1; > + svif->is_p2p = p2p; > + break; > + > + case NL80211_IFTYPE_P2P_CLIENT: > + svif->op_mode = 0; > + svif->is_p2p = 1; > + break; > + > + case NL80211_IFTYPE_P2P_GO: > + svif->op_mode = 1; > + svif->is_p2p = 1; > + break; > + > + default: > + return -EOPNOTSUPP; > + } > + > + return 0; > +} > + > +/* > +Called when a netdevice attached to the hardware is > +2973 * enabled. Because it is not called for monitor mode devices, @start > +2974 * and @stop must be implemented. > +2975 * The driver should perform any initialization it needs before > +2976 * the device can be enabled. The initial configuration for the > +2977 * interface is given in the conf parameter. > +2978 * The callback may refuse to add an interface by returning a > +2979 * negative error code (which will be seen in userspace.) > +2980 * Must be implemented and can sleep. > + */ > +static int esp_op_add_interface(struct ieee80211_hw *hw, > + struct ieee80211_vif *vif) > +{ > + struct esp_pub *epub = (struct esp_pub *)hw->priv; > + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; > + struct sip_cmd_setvif svif; > + int ret; > + > + memcpy(svif.mac, vif->addr, ETH_ALEN); > + svif.index = getaddr_index(vif->addr, epub); > + evif->index = svif.index; > + evif->epub = epub; > + /* FIXME: why a need for evif? */ > + epub->vif = vif; > + svif.set = 1; > + > + if (svif.index == ESP_PUB_MAX_VIF) { Move this error handling next to the getaddr_index() call. > + dev_err(epub->dev, "support for MAX %d interfaces\n", > + ESP_PUB_MAX_VIF); > + return -EOPNOTSUPP; > + } > + > + if (BIT(svif.index) & epub->vif_slot) { > + dev_err(epub->dev, "interface %d already used\n", svif.index); > + return -EOPNOTSUPP; > + } > + > + epub->vif_slot |= BIT(svif.index); > + > + ret = esp_set_svif_mode(&svif, vif->type, false); > + if (ret < 0) { > + dev_err(epub->dev, "no support for interface type %d\n", > + vif->type); > + return ret; > + } > + > + sip_cmd(epub, SIP_CMD_SETVIF, (u8 *)&svif, sizeof(svif)); > + > + return 0; > +} > + > +/* > +Called when a netdevice changes type. This callback > +2983 * is optional, but only if it is supported can interface types be > +2984 * switched while the interface is UP. The callback may sleep. > +2985 * Note that while an interface is being switched, it will not be > +2986 * found by the interface iteration callbacks. > + */ > +static int esp_op_change_interface(struct ieee80211_hw *hw, > + struct ieee80211_vif *vif, > + enum nl80211_iftype new_type, bool p2p) > +{ > + struct esp_pub *epub = (struct esp_pub *)hw->priv; > + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; > + struct sip_cmd_setvif svif; > + int ret; > + > + memcpy(svif.mac, vif->addr, ETH_ALEN); > + svif.index = evif->index; > + svif.set = 2; > + > + ret = esp_set_svif_mode(&svif, new_type, p2p); > + if (ret < 0) > + return ret; > + > + sip_cmd(epub, SIP_CMD_SETVIF, (u8 *)&svif, sizeof(svif)); > + > + return 0; > +} > + > +/* > + Notifies a driver that an interface is going down. > + 2989 * The @stop callback is called after this if it is the last interface > + 2990 * and no monitor interfaces are present. > + 2991 * When all interfaces are removed, the MAC address in the hardware > + 2992 * must be cleared so the device no longer acknowledges packets, > + 2993 * the mac_addr member of the conf structure is, however, set to the > + 2994 * MAC address of the device going away. > + 2995 * Hence, this callback must be implemented. It can sleep. > + */ > +static void esp_op_remove_interface(struct ieee80211_hw *hw, > + struct ieee80211_vif *vif) > +{ > + struct esp_pub *epub = (struct esp_pub *)hw->priv; > + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; > + struct sip_cmd_setvif svif = {0}; > + > + svif.index = evif->index; > + epub->vif_slot &= ~BIT(svif.index); > + > + if (evif->ap_up) { > + evif->beacon_interval = 0; > + del_timer_sync(&evif->beacon_timer); > + evif->ap_up = false; > + } > + > + epub->vif = NULL; > + evif->epub = NULL; > + > + sip_cmd(epub, SIP_CMD_SETVIF, (u8 *)&svif, sizeof(svif)); > + /* TODO: clean up tx/rx queue */ > +} > + > +/* FIXME: WTF? */ > + > +#define BEACON_TIM_SAVE_MAX 20 > +u8 beacon_tim_saved[BEACON_TIM_SAVE_MAX]; > +int beacon_tim_count; > +static void beacon_tim_init(void) > +{ > + memset(beacon_tim_saved, 0, BEACON_TIM_SAVE_MAX); > + beacon_tim_count = 0; > +} > + > +static u8 beacon_tim_save(u8 this_tim) > +{ > + u8 all_tim = 0; > + int i; > + > + beacon_tim_saved[beacon_tim_count] = this_tim; > + > + beacon_tim_count = (beacon_tim_count + 1) % BEACON_TIM_SAVE_MAX; > + > + for (i = 0; i < BEACON_TIM_SAVE_MAX; i++) > + all_tim |= beacon_tim_saved[i]; > + > + return all_tim; > +} > + > +static bool beacon_tim_alter(struct sk_buff *beacon) > +{ > + u8 *p, *tim_end; > + u8 tim_count; > + int len, remain_len; > + struct ieee80211_mgmt *mgmt; > + > + if (!beacon) > + return false; > + > + mgmt = (struct ieee80211_mgmt *)((u8 *)beacon->data); > + > + remain_len = beacon->len - ((u8 *)mgmt->u.beacon.variable - > + (u8 *)mgmt + 12); > + p = mgmt->u.beacon.variable; > + > + while (remain_len > 0) { > + len = *(++p); > + > + if (*p == WLAN_EID_TIM) { // tim field > + tim_end = p + len; > + tim_count = *(++p); > + p += 2; > + //multicast > + if (!tim_count) > + *p |= 0x1; > + > + if (!(*p & 0xfe) && tim_end >= p + 1) { // we only support 8 sta in this case > + p++; > + *p = beacon_tim_save(*p); > + } > + > + return tim_count == 0; > + } > + > + p += len + 1; > + remain_len -= 2 + len; > + } > + > + return false; > +} > + > +unsigned long init_jiffies; > +unsigned long cycle_beacon_count; > +static void drv_handle_beacon(unsigned long data) > +{ > + struct ieee80211_vif *vif = (struct ieee80211_vif *)data; > + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; > + struct sk_buff *beacon; > + struct sk_buff *skb; > + bool tim_reach; > + > + if (!evif->epub) > + return; > + > + mdelay(2400 * (cycle_beacon_count % 25) % 10000 / 1000); > + > + beacon = ieee80211_beacon_get(evif->epub->hw, vif); > + > + tim_reach = beacon_tim_alter(beacon); > + > + if (beacon) > + sip_tx_data_pkt_enqueue(evif->epub, beacon); > + > + if (cycle_beacon_count++ == 100) { > + init_jiffies = jiffies; > + cycle_beacon_count -= 100; > + } > + > + mod_timer(&evif->beacon_timer, init_jiffies + > + msecs_to_jiffies(cycle_beacon_count * > + vif->bss_conf.beacon_int * 1024 / 1000)); > + //FIXME:the packets must be sent at home channel > + //send buffer mcast frames > + if (tim_reach) { > + skb = ieee80211_get_buffered_bc(evif->epub->hw, vif); > + while (skb) { > + sip_tx_data_pkt_enqueue(evif->epub, skb); > + skb = ieee80211_get_buffered_bc(evif->epub->hw, vif); > + } > + } > +} > + > +static void init_beacon_timer(struct ieee80211_vif *vif) > +{ > + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; > + > + beacon_tim_init(); > + setup_timer(&evif->beacon_timer, drv_handle_beacon, (unsigned long)vif); //TBD, not init here... > + cycle_beacon_count = 1; > + init_jiffies = jiffies; > + evif->beacon_timer.expires = init_jiffies + > + msecs_to_jiffies(cycle_beacon_count * vif->bss_conf.beacon_int * > + 1024 / 1000); > + add_timer(&evif->beacon_timer); > +} > + > +/* > + Handler for configuration requests. IEEE 802.11 code calls this > + 2998 * function to change hardware configuration, e.g., channel. > + 2999 * This function should never fail but returns a negative error code > + 3000 * if it does. The callback can sleep. > + */ > +static int esp_op_config(struct ieee80211_hw *hw, u32 changed) > +{ > + struct esp_pub *epub = (struct esp_pub *)hw->priv; > + > + if (changed & (IEEE80211_CONF_CHANGE_CHANNEL | > + IEEE80211_CONF_CHANGE_IDLE)) > + sip_send_config(epub, &hw->conf); > + > + return 0; > +} > + > +/* > +Handler for configuration requests related to BSS > +3003 * parameters that may vary during BSS's lifespan, and may affect low > +3004 * level driver (e.g. assoc/disassoc status, erp parameters). > +3005 * This function should not be used if no BSS has been set, unless > +3006 * for association indication. The @changed parameter indicates which > +3007 * of the bss parameters has changed when a call is made. The callback > +3008 * can sleep. > + */ > +static void esp_op_bss_info_changed(struct ieee80211_hw *hw, > + struct ieee80211_vif *vif, > + struct ieee80211_bss_conf *info, > + u32 changed) > +{ > + struct esp_pub *epub = (struct esp_pub *)hw->priv; > + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; > + u8 *bssid = (u8 *)info->bssid; > + bool assoc = info->assoc; > + > + // ieee80211_bss_conf(include/net/mac80211.h) is included in ieee80211_sub_if_data(net/mac80211/ieee80211_i.h) , does bssid=ieee80211_if_ap's ssid ? > + > + if (vif->type == NL80211_IFTYPE_STATION) { > + if (changed & BSS_CHANGED_BSSID || > + ((changed & BSS_CHANGED_ASSOC) && assoc)) { > + evif->beacon_interval = info->aid; > + memcpy(epub->wl.bssid, bssid, ETH_ALEN); > + sip_send_bss_info_update(epub, evif, bssid, assoc); > + } else if ((changed & BSS_CHANGED_ASSOC) && !assoc) { > + evif->beacon_interval = 0; > + memset(epub->wl.bssid, 0, ETH_ALEN); > + sip_send_bss_info_update(epub, evif, bssid, assoc); > + } > + } else if (vif->type == NL80211_IFTYPE_AP) { > + if (!(changed & BSS_CHANGED_BEACON_ENABLED) && > + !(changed & BSS_CHANGED_BEACON_INT)) > + return; > + > + if (info->enable_beacon && !evif->ap_up) { > + evif->beacon_interval = info->beacon_int; > + init_beacon_timer(vif); > + sip_send_bss_info_update(epub, evif, bssid, 2); > + evif->ap_up = true; > + } else if (!info->enable_beacon && evif->ap_up && > + !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) { > + evif->beacon_interval = 0; > + del_timer_sync(&evif->beacon_timer); > + sip_send_bss_info_update(epub, evif, bssid, 2); > + evif->ap_up = false; > + } > + } > +} > + > +/* > + Configure the device's RX filter. > + 3015 * See the section "Frame filtering" for more information. > + 3016 * This callback must be implemented and can sleep. > + */ > +static void esp_op_configure_filter(struct ieee80211_hw *hw, > + unsigned int changed_flags, > + unsigned int *total_flags, u64 multicast) > +{ > + struct esp_pub *epub = (struct esp_pub *)hw->priv; > + > + epub->rx_filter = 0; > + > + if (*total_flags & FIF_ALLMULTI) > + epub->rx_filter |= FIF_ALLMULTI; > + > + *total_flags = epub->rx_filter; > +} > + > +static bool is_cipher_suite_wep(u32 cipher) > +{ > + return (cipher == WLAN_CIPHER_SUITE_WEP40) || > + (cipher == WLAN_CIPHER_SUITE_WEP104); > +} > + > +/* > + See the section "Hardware crypto acceleration" > + 3029 * This callback is only called between add_interface and > + 3030 * remove_interface calls, i.e. while the given virtual interface > + 3031 * is enabled. > + 3032 * Returns a negative error code if the key can't be added. > + 3033 * The callback can sleep. > + */ > +static int esp_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, > + struct ieee80211_vif *vif, struct ieee80211_sta *sta, > + struct ieee80211_key_conf *key) > +{ > + struct esp_pub *epub = (struct esp_pub *)hw->priv; > + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; > + struct esp_hw_idx_map *map; > + atomic_t *cnt1, *cnt2; > + u8 i, ifidx = evif->index, isvalid, index; > + u8 *peer_addr; > + int ret, counter; > + > + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; > + > + if (sta && memcmp(sta->addr, epub->wl.bssid, ETH_ALEN)) > + peer_addr = sta->addr; > + else > + peer_addr = epub->wl.bssid; > + > + isvalid = !!(cmd == SET_KEY); > + > + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE || > + is_cipher_suite_wep(key->cipher)) > + map = epub->low_map[ifidx]; > + else > + map = epub->hi_map; > + > + if (isvalid) { > + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE || > + is_cipher_suite_wep(key->cipher)) > + counter = 2; > + else > + counter = 19; > + > + for (i = 0; i < counter; i++) { > + if (map[i].flag) > + continue; > + > + map[i].flag = 1; > + memcpy(map[i].mac, peer_addr, ETH_ALEN); > + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE || > + is_cipher_suite_wep(key->cipher)) > + key->hw_key_idx = i + 6; > + else > + key->hw_key_idx = i + ifidx * 2 + 2; > + break; > + } > + } else { > + map[index].flag = 0; > + memset(map[index].mac, 0, ETH_ALEN); > + > + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE || > + is_cipher_suite_wep(key->cipher)) > + index = key->hw_key_idx - 6; > + else > + index = key->hw_key_idx - 2 - ifidx * 2; > + } > + > + if (key->hw_key_idx >= 6) { > + cnt1 = &epub->wl.ptk_cnt; > + cnt2 = &epub->wl.gtk_cnt; > + } else { > + cnt2 = &epub->wl.ptk_cnt; > + cnt1 = &epub->wl.gtk_cnt; > + } > + > + /*send sub_scan task to target */ > + if (isvalid) > + atomic_inc(cnt1); > + else > + atomic_dec(cnt1); > + > + if (is_cipher_suite_wep(key->cipher)) { > + if (isvalid) > + atomic_inc(cnt2); > + else > + atomic_dec(cnt2); > + } > + > + ret = sip_send_setkey(epub, ifidx, peer_addr, key, isvalid); > + if (ret) > + return ret; > + > + if (key->cipher == WLAN_CIPHER_SUITE_TKIP && !ret) > + atomic_set(&epub->wl.tkip_key_set, 1); > + > + return 0; > +} > + > +void hw_scan_done(struct esp_pub *epub, bool aborted) > +{ > + struct cfg80211_scan_info info = { > + .aborted = aborted, > + }; > + > + cancel_delayed_work_sync(&epub->scan_timeout_work); > + > + ESSERT(epub->wl.scan_req); > + > + ieee80211_scan_completed(epub->hw, &info); > + > + if (test_and_clear_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags)) > + sip_trigger_txq_process(epub->sip); > +} > + > +static void hw_scan_timeout_report(struct work_struct *work) > +{ > + struct esp_pub *epub = container_of(work, struct esp_pub, > + scan_timeout_work.work); > + bool aborted; > + struct cfg80211_scan_info info = {}; > + > + if (test_and_clear_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags)) > + sip_trigger_txq_process(epub->sip); > + /*check if normally complete or aborted like timeout/hw error */ > + aborted = (epub->wl.scan_req != 0); > + > + if (aborted) > + epub->wl.scan_req = NULL; > + > + info.aborted = aborted; > + > + ieee80211_scan_completed(epub->hw, &info); > +} > + > +/* > + Configuration of RTS threshold (if device needs it) > + 3106 * The callback can sleep. > + */ > +static int esp_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value) > +{ > + return 0; > +} > + > +static int esp_node_attach(struct ieee80211_hw *hw, u8 ifidx, > + struct ieee80211_sta *sta) > +{ > + struct esp_pub *epub = (struct esp_pub *)hw->priv; > + struct esp_node *node; > + struct esp_tx_tid *tid; > + u8 tidno = 0; > + int i; > + > + spin_lock_bh(&epub->tx_ampdu_lock); > + > + /* ffz(x) needs at least one zero or results in undefined behaviour. */ > + if ((~epub->enodes_map) == 0) > + return -EINVAL; > + > + i = ffz(epub->enodes_map); > + > + if (hweight32(epub->enodes_maps[ifidx]) >= ESP_PUB_MAX_STA || > + i > ESP_PUB_MAX_STA) { > + i = -1; > + goto out; > + } > + > + epub->enodes_map |= BIT(i); > + epub->enodes_maps[ifidx] |= BIT(i); > + node = (struct esp_node *)sta->drv_priv; > + epub->enodes[i] = node; > + node->sta = sta; > + node->ifidx = ifidx; > + node->index = i; > + > + while (tidno < WME_NUM_TID) { > + tid = &node->tid[tidno]; > + tid->ssn = 0; > + tid->cnt = 0; > + tid->state = ESP_TID_STATE_INIT; > + tidno++; > + } > + > +out: > + spin_unlock_bh(&epub->tx_ampdu_lock); > + > + return i; > +} > + > +static int esp_node_detach(struct ieee80211_hw *hw, u8 ifidx, > + struct ieee80211_sta *sta) > +{ > + struct esp_pub *epub = (struct esp_pub *)hw->priv; > + u32 map; > + int i; > + > + spin_lock_bh(&epub->tx_ampdu_lock); > + > + map = epub->enodes_maps[ifidx]; > + > + while (map) { > + i = ffs(map) - 1; > + if (epub->enodes[i]->sta == sta) { > + epub->enodes[i]->sta = NULL; > + epub->enodes[i] = NULL; > + epub->enodes_map &= ~BIT(i); > + epub->enodes_maps[ifidx] &= ~BIT(i); > + > + goto out; > + } > + > + map &= ~BIT(i); > + } > + > + i = -1; > + > +out: > + spin_unlock_bh(&epub->tx_ampdu_lock); > + > + return i; > +} > + > +struct esp_node *esp_get_node_by_addr(struct esp_pub *epub, const u8 *addr) > +{ > + struct esp_node *node = NULL; > + int i; > + u32 map; > + > + if (!addr) > + return NULL; > + > + spin_lock_bh(&epub->tx_ampdu_lock); > + map = epub->enodes_map; > + > + while (map) { > + i = ffs(map) - 1; > + > + if (!memcmp(epub->enodes[i]->sta->addr, addr, ETH_ALEN)) { > + node = epub->enodes[i]; > + goto out; > + } > + > + map &= ~BIT(i); > + } > + > +out: > + spin_unlock_bh(&epub->tx_ampdu_lock); > + > + return node; > +} > + > +int esp_get_empty_rxampdu(struct esp_pub *epub, const u8 *addr, u8 tid) > +{ > + int index; > + > + if (!addr) > + return -1; > + > + spin_lock_bh(&epub->rx_ampdu_lock); > + > + index = ffz(epub->rxampdu_map); > + > + if (index >= ESP_PUB_MAX_RXAMPDU) { > + index = -1; > + goto out; > + } > + > + epub->rxampdu_map |= BIT(index); > + epub->rxampdu_node[index] = esp_get_node_by_addr(epub, addr); > + epub->rxampdu_tid[index] = tid; > + > +out: > + spin_unlock_bh(&epub->rx_ampdu_lock); > + > + return index; > +} > + > +int esp_get_exist_rxampdu(struct esp_pub *epub, const u8 *addr, u8 tid) > +{ > + u8 map; > + int index; > + > + if (!addr) > + return -1; > + > + spin_lock_bh(&epub->rx_ampdu_lock); > + map = epub->rxampdu_map; > + > + while (map) { > + index = ffs(map) - 1; > + > + if (epub->rxampdu_tid[index] == tid && > + !memcmp(epub->rxampdu_node[index]->sta->addr, addr, > + ETH_ALEN)) { > + epub->rxampdu_map &= ~BIT(index); > + goto out; > + } > + > + map &= ~BIT(index); > + } > + > + index = -1; > + > +out: > + spin_unlock_bh(&epub->rx_ampdu_lock); > + return index; > +} > + > +/* > + Notifies low level driver about addition of an associated station, > + 3109 * AP, IBSS/WDS/mesh peer etc. This callback can sleep. > + */ > +static int esp_op_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, > + struct ieee80211_sta *sta) > +{ > + struct esp_pub *epub = (struct esp_pub *)hw->priv; > + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; > + int index; > + > + index = esp_node_attach(hw, evif->index, sta); > + if (index < 0) > + return index; > + > + sip_send_set_sta(epub, evif->index, 1, sta, vif, (u8)index); > + > + return 0; > +} > + > +/* > + Notifies low level driver about removal of an associated > + 3112 * station, AP, IBSS/WDS/mesh peer etc. Note that after the callback > + 3113 * returns it isn't safe to use the pointer, not even RCU protected; > + 3114 * no RCU grace period is guaranteed between returning here and freeing > + 3115 * the station. See @sta_pre_rcu_remove if needed. > + 3116 * This callback can sleep > + */ > +static int esp_op_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, > + struct ieee80211_sta *sta) > +{ > + struct esp_pub *epub = (struct esp_pub *)hw->priv; > + struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; > + int index; > + > + //remove a connect in target > + index = esp_node_detach(hw, evif->index, sta); Why do we think "index" is not -1? > + sip_send_set_sta(epub, evif->index, 0, sta, vif, (u8)index); > + > + return 0; > +} > + > +/* > + Notifies low level driver about power state transition of an > + 3124 * associated station, AP, IBSS/WDS/mesh peer etc. For a VIF operating > + 3125 * in AP mode, this callback will not be called when the flag > + 3126 * %IEEE80211_HW_AP_LINK_PS is set. Must be atomic. > + */ > +static void esp_op_sta_notify(struct ieee80211_hw *hw, > + struct ieee80211_vif *vif, > + enum sta_notify_cmd cmd, > + struct ieee80211_sta *sta) > +{ > +} > + > +/* > + Configure TX queue parameters (EDCF (aifs, cw_min, cw_max), > + 3165 * bursting) for a hardware TX queue. > + 3166 * Returns a negative error code on failure. > + 3167 * The callback can sleep. > + */ > +static int esp_op_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, > + u16 queue, > + const struct ieee80211_tx_queue_params *params) > +{ > + struct esp_pub *epub = (struct esp_pub *)hw->priv; > + > + return sip_send_wmm_params(epub, queue, params); > +} > + > +/* > + Get the current TSF timer value from firmware/hardware. Currently, > + 3170 * this is only used for IBSS mode BSSID merging and debugging. Is not a > + 3171 * required function. > + 3172 * The callback can sleep. > + */ > +static u64 esp_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) > +{ > + return 0; > +} > + > +/* > + Set the TSF timer to the specified value in the firmware/hardware. > + 3175 * Currently, this is only used for IBSS mode debugging. Is not a > + 3176 * required function. > + 3177 * The callback can sleep. > + */ > +static void esp_op_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, > + u64 tsf) > +{ > +} > + > +/* > + Reset the TSF timer and allow firmware/hardware to synchronize > + 3186 * with other STAs in the IBSS. This is only used in IBSS mode. This > + 3187 * function is optional if the firmware/hardware takes full care of > + 3188 * TSF synchronization. > + 3189 * The callback can sleep. > + */ > +static void esp_op_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) > +{ > +} > + > +/* > + Poll rfkill hardware state. If you need this, you also > + 3220 * need to set wiphy->rfkill_poll to %true before registration, > + 3221 * and need to call wiphy_rfkill_set_hw_state() in the callback. > + 3222 * The callback can sleep. > + */ > +static void esp_op_rfkill_poll(struct ieee80211_hw *hw) > +{ > + struct esp_pub *epub = (struct esp_pub *)hw->priv; > + > + wiphy_rfkill_set_hw_state(hw->wiphy, test_bit(ESP_WL_FLAG_RFKILL, > + &epub->wl.flags)); > +} > + > +#ifdef HW_SCAN > +/* > + Ask the hardware to service the scan request, no need to start > + 3051 * the scan state machine in stack. The scan must honour the channel > + 3052 * configuration done by the regulatory agent in the wiphy's > + 3053 * registered bands. The hardware (or the driver) needs to make sure > + 3054 * that power save is disabled. > + 3055 * The @req ie/ie_len members are rewritten by mac80211 to contain the > + 3056 * entire IEs after the SSID, so that drivers need not look at these > + 3057 * at all but just send them after the SSID -- mac80211 includes the > + 3058 * (extended) supported rates and HT information (where applicable). > + 3059 * When the scan finishes, ieee80211_scan_completed() must be called; > + 3060 * note that it also must be called when the scan cannot finish due to > + 3061 * any error unless this callback returned a negative error code. > + 3062 * The callback can sleep. > + */ > +static int esp_op_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, > + struct cfg80211_scan_request *req) > +{ > + struct esp_pub *epub = (struct esp_pub *)hw->priv; > + struct cfg80211_ssid *ssid2 = req->ssids + 1; > + int i, ret; > + bool scan_often; > + > + /* scan_request is keep allocate until scan_done,record it > + * to split request into multi sdio_cmd > + */ > + if (atomic_read(&epub->wl.off)) { > + dev_err(epub->dev, "hw_scan but wl off\n"); > + return -EPERM; > + } > + > + if (req->n_ssids > 1) > + if ((req->ssids->ssid_len > 0 && ssid2->ssid_len > 0) || > + req->n_ssids > 2) { > + dev_err(epub->dev, "cannot scan two SSIDs\n"); > + return -EINVAL; > + } > + > + epub->wl.scan_req = req; > + > + /*in connect state, suspend tx data */ > + if (epub->sip->support_bgscan && > + test_bit(ESP_WL_FLAG_CONNECT, &epub->wl.flags) && req->n_channels) { > + scan_often = epub->scan_permit_valid && > + time_before(jiffies, epub->scan_permit); > + epub->scan_permit_valid = true; > + > + if (!scan_often) { > + /* epub->scan_permit = jiffies + msecs_to_jiffies(900); > + * set_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags); > + * if (atomic_read(&epub->txq_stopped) == false) { > + * atomic_set(&epub->txq_stopped, true); > + * ieee80211_stop_queues(hw); > + * } > + */ > + } else { > + dev_err(epub->dev, "scan too often\n"); > + return -EACCES; > + } > + } else { > + scan_often = false; > + } > + > + /*send sub_scan task to target */ > + ret = sip_send_scan(epub); > + if (ret) { > + dev_err(epub->dev, "failed to send scan_cmd: %d\n", ret); > + return ret; > + } > + > + if (scan_often) > + return 0; > + > + epub->scan_permit = jiffies + msecs_to_jiffies(900); > + set_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags); > + if (!atomic_read(&epub->txq_stopped)) { > + atomic_set(&epub->txq_stopped, true); > + ieee80211_stop_queues(hw); > + } > + > + /*force scan complete in case target fail to report in time */ > + ieee80211_queue_delayed_work(hw, &epub->scan_timeout_work, > + req->n_channels * HZ / 4); > + > + return 0; > +} > + > +/* > + Starts an off-channel period on the given channel, must > + 3255 * call back to ieee80211_ready_on_channel() when on that channel. Note > + 3256 * that normal channel traffic is not stopped as this is intended for hw > + 3257 * offload. Frames to transmit on the off-channel channel are transmitted > + 3258 * normally except for the %IEEE80211_TX_CTL_TX_OFFCHAN flag. When the > + 3259 * duration (which will always be non-zero) expires, the driver must call > + 3260 * ieee80211_remain_on_channel_expired(). > + 3261 * Note that this callback may be called while the device is in IDLE and > + 3262 * must be accepted in this case. > + 3263 * This callback may sleep. > + */ > +static int esp_op_remain_on_channel(struct ieee80211_hw *hw, > + struct ieee80211_channel *chan, > + enum nl80211_channel_type channel_type, > + int duration) > +{ > + struct esp_pub *epub = (struct esp_pub *)hw->priv; > + > + sip_send_roc(epub, chan->center_freq, duration); > + > + return 0; > +} > + > +/* > + 3264 * @cancel_remain_on_channel: Requests that an ongoing off-channel period is > + 3265 * aborted before it expires. This callback may sleep. > + */ > +static int esp_op_cancel_remain_on_channel(struct ieee80211_hw *hw) > +{ > + struct esp_pub *epub = (struct esp_pub *)hw->priv; > + > + epub->roc_flags = 0; // to disable roc state > + sip_send_roc(epub, 0, 0); > + > + return 0; > +} > +#endif > + > +void esp_rocdone_process(struct ieee80211_hw *hw, struct sip_evt_roc *report) > +{ > + struct esp_pub *epub = (struct esp_pub *)hw->priv; > + > + if (report->is_ok != 1) > + return; > + > + if (report->state == 1) { > + epub->roc_flags = 1; //flags in roc state, to fix channel, not change > + ieee80211_ready_on_channel(hw); > + } else if (!report->state) { > + epub->roc_flags = 0; > + ieee80211_remain_on_channel_expired(hw); > + } > +} > + > +/* > + Set a mask of rates to be used for rate control selection > + 3275 * when transmitting a frame. Currently only legacy rates are handled. > + 3276 * The callback can sleep. > + */ > +static int esp_op_set_bitrate_mask(struct ieee80211_hw *hw, > + struct ieee80211_vif *vif, > + const struct cfg80211_bitrate_mask *mask) > +{ > + return 0; > +} > + > +/* > + Flush all pending frames from the hardware queue, making sure > + 3235 * that the hardware queues are empty. The @queues parameter is a bitmap > + 3236 * of queues to flush, which is useful if different virtual interfaces > + 3237 * use different hardware queues; it may also indicate all queues. > + 3238 * If the parameter @drop is set to %true, pending frames may be dropped. > + 3239 * Note that vif can be NULL. > + 3240 * The callback can sleep. > + */ > +void esp_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, > + u32 queues, bool drop) > +{ > + struct esp_pub *epub = (struct esp_pub *)hw->priv; > + unsigned long time = jiffies + msecs_to_jiffies(15); > + > + while (atomic_read(&epub->sip->tx_data_pkt_queued)) { > + if (!time_before(jiffies, time)) > + break; > + > + if (!sif_get_ate_config()) > + ieee80211_queue_work(epub->hw, &epub->tx_work); > + else > + queue_work(epub->esp_wkq, &epub->tx_work); > + } > + > + mdelay(10); > +} > + > +/* > + Perform a certain A-MPDU action > + 3198 * The RA/TID combination determines the destination and TID we want > + 3199 * the ampdu action to be performed for. The action is defined through > + 3200 * ieee80211_ampdu_mlme_action. > + 3201 * When the action is set to %IEEE80211_AMPDU_TX_OPERATIONAL the driver > + 3202 * may neither send aggregates containing more subframes than @buf_size > + 3203 * nor send aggregates in a way that lost frames would exceed the > + 3204 * buffer size. If just limiting the aggregate size, this would be > + 3205 * possible with a buf_size of 8: > + 3206 * - TX: 1.....7 > + 3207 * - RX: 2....7 (lost frame #1) > + 3208 * - TX: 8..1... > + 3209 * which is invalid since #1 was now re-transmitted well past the > + 3210 * buffer size of 8. Correct ways to retransmit #1 would be: > + 3211 * - TX: 1 or 18 or 81 > + 3212 * Even "189" would be wrong since 1 could be lost again. > + 3213 * > + 3214 * Returns a negative error code on failure. > + 3215 * The callback can sleep. > + */ > +static int esp_op_ampdu_action(struct ieee80211_hw *hw, > + struct ieee80211_vif *vif, > + struct ieee80211_ampdu_params *params) > +{ > + enum ieee80211_ampdu_mlme_action action = params->action; > + struct ieee80211_sta *sta = params->sta; > + struct esp_pub *epub = (struct esp_pub *)hw->priv; > + struct esp_node *node = (struct esp_node *)sta->drv_priv; > + struct cfg80211_chan_def *chandef; > + u16 tid = params->tid; > + struct esp_tx_tid *tid_info = &node->tid[tid]; > + u16 *ssn = ¶ms->ssn; > + u8 buf_size = params->buf_size; > + > + switch (action) { > + case IEEE80211_AMPDU_TX_START: > + chandef = &epub->hw->conf.chandef; > + if (mod_support_no_txampdu() || !sta->ht_cap.ht_supported || > + cfg80211_get_chandef_type(chandef) == NL80211_CHAN_NO_HT) > + return -EOPNOTSUPP; > + > + dev_dbg(epub->dev, "%s TX START, addr:%pM,tid:%u,state:%d\n", > + __func__, sta->addr, tid, tid_info->state); > + > + spin_lock_bh(&epub->tx_ampdu_lock); > + > + ESSERT(tid_info->state == ESP_TID_STATE_TRIGGER); > + *ssn = tid_info->ssn; > + tid_info->state = ESP_TID_STATE_PROGRESS; > + > + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); > + spin_unlock_bh(&epub->tx_ampdu_lock); > + > + return 0; > + > + case IEEE80211_AMPDU_TX_STOP_CONT: > + dev_dbg(epub->dev, "%s TX STOP, addr:%pM,tid:%u,state:%d\n", > + __func__, sta->addr, tid, tid_info->state); > + > + spin_lock_bh(&epub->tx_ampdu_lock); > + > + case IEEE80211_AMPDU_TX_STOP_FLUSH: > + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: > + if (tid_info->state == ESP_TID_STATE_WAIT_STOP) > + tid_info->state = ESP_TID_STATE_STOP; > + else > + tid_info->state = ESP_TID_STATE_INIT; > + > + if (action == IEEE80211_AMPDU_TX_STOP_CONT) { > + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); > + spin_unlock_bh(&epub->tx_ampdu_lock); > + } > + > + return sip_send_ampdu_action(epub, SIP_AMPDU_TX_STOP, sta->addr, > + tid, node->ifidx, 0); > + > + case IEEE80211_AMPDU_TX_OPERATIONAL: > + dev_dbg(epub->dev, > + "%s TX OPERATION, addr:%pM,tid:%u,state:%d\n", __func__, > + sta->addr, tid, tid_info->state); > + > + spin_lock_bh(&epub->tx_ampdu_lock); > + > + if (tid_info->state != ESP_TID_STATE_PROGRESS) { > + if (tid_info->state == ESP_TID_STATE_INIT) { > + printk(KERN_ERR "%s WIFI RESET, IGNORE\n", > + __func__); > + spin_unlock_bh(&epub->tx_ampdu_lock); > + return -ENETRESET; > + } > + > + ESSERT(0); > + } > + > + tid_info->state = ESP_TID_STATE_OPERATIONAL; > + spin_unlock_bh(&epub->tx_ampdu_lock); > + > + return sip_send_ampdu_action(epub, SIP_AMPDU_TX_OPERATIONAL, > + sta->addr, tid, node->ifidx, > + buf_size); > + > + case IEEE80211_AMPDU_RX_START: > + chandef = &epub->hw->conf.chandef; > + if (mod_support_no_rxampdu() || !sta->ht_cap.ht_supported || > + cfg80211_get_chandef_type(chandef) == NL80211_CHAN_NO_HT) > + return -EOPNOTSUPP; > + > + dev_dbg(epub->dev, "%s RX START %pM tid %u %u\n", __func__, > + sta->addr, tid, *ssn); > + > + return sip_send_ampdu_action(epub, SIP_AMPDU_RX_START, sta->addr, > + tid, *ssn, 64); > + > + case IEEE80211_AMPDU_RX_STOP: > + dev_dbg(epub->dev, "%s RX STOP %pM tid %u\n", __func__, > + sta->addr, tid); > + > + return sip_send_ampdu_action(epub, SIP_AMPDU_RX_STOP, sta->addr, > + tid, 0, 0); > + > + default: > + return -EINVAL; > + } > +} > + > +static void esp_tx_work(struct work_struct *work) > +{ > + struct esp_pub *epub = container_of(work, struct esp_pub, tx_work); > + > + mutex_lock(&epub->tx_mtx); > + sip_txq_process(epub); > + mutex_unlock(&epub->tx_mtx); > +} > + > +static const struct ieee80211_ops esp_mac80211_ops = { > + .tx = esp_op_tx, > + .start = esp_op_start, > + .stop = esp_op_stop, > + .add_interface = esp_op_add_interface, > + .remove_interface = esp_op_remove_interface, > + .config = esp_op_config, > + > + .bss_info_changed = esp_op_bss_info_changed, > + .configure_filter = esp_op_configure_filter, > + .set_key = esp_op_set_key, > + .set_rts_threshold = esp_op_set_rts_threshold, > + .sta_notify = esp_op_sta_notify, > + .conf_tx = esp_op_conf_tx, > + .change_interface = esp_op_change_interface, > + .get_tsf = esp_op_get_tsf, > + .set_tsf = esp_op_set_tsf, > + .reset_tsf = esp_op_reset_tsf, > + .rfkill_poll = esp_op_rfkill_poll, > +#ifdef HW_SCAN > + .hw_scan = esp_op_hw_scan, > + .remain_on_channel = esp_op_remain_on_channel, > + .cancel_remain_on_channel = esp_op_cancel_remain_on_channel, > +#endif > + .ampdu_action = esp_op_ampdu_action, > + .sta_add = esp_op_sta_add, > + .sta_remove = esp_op_sta_remove, > + .set_bitrate_mask = esp_op_set_bitrate_mask, > + .flush = esp_op_flush, > +}; > + > +struct esp_pub *esp_pub_alloc_mac80211(struct device *dev) > +{ > + struct ieee80211_hw *hw; > + struct esp_pub *epub; > + > + hw = ieee80211_alloc_hw(sizeof(struct esp_pub), &esp_mac80211_ops); > + if (!hw) > + return ERR_PTR(-ENOMEM); The caller expects a NULL return. > + > + /* FIXME: useless if hw_scan is defined, incorrect if hw_scan is undefined*/ > +#ifdef HW_SCAN > + hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; > +#endif > + > + epub = hw->priv; > + memset(epub, 0, sizeof(*epub)); > + > + epub->hw = hw; > + SET_IEEE80211_DEV(hw, dev); > + epub->dev = dev; > + > + skb_queue_head_init(&epub->txq); > + skb_queue_head_init(&epub->txdoneq); > + skb_queue_head_init(&epub->rxq); > + > + spin_lock_init(&epub->tx_ampdu_lock); > + spin_lock_init(&epub->rx_ampdu_lock); > + spin_lock_init(&epub->tx_lock); > + mutex_init(&epub->tx_mtx); > + spin_lock_init(&epub->rx_lock); > + > + INIT_WORK(&epub->tx_work, esp_tx_work); > + > + epub->esp_wkq = create_singlethread_workqueue("esp_wkq"); > + > + if (!epub->esp_wkq) > + return ERR_PTR(-ENOMEM); goto free_hw; > + > + epub->scan_permit_valid = false; > + INIT_DELAYED_WORK(&epub->scan_timeout_work, hw_scan_timeout_report); > + > + return epub; > +} > + > +int esp_pub_dealloc_mac80211(struct esp_pub *epub) > +{ > + set_bit(ESP_WL_FLAG_RFKILL, &epub->wl.flags); > + > + destroy_workqueue(epub->esp_wkq); > + mutex_destroy(&epub->tx_mtx); > + > + if (epub->hw) > + ieee80211_free_hw(epub->hw); > + > + return 0; > +} > + > +/* 2G band channels */ > +static struct ieee80211_channel esp_channels_2ghz[] = { > + {.hw_value = 1, .center_freq = 2412, .max_power = 25}, > + {.hw_value = 2, .center_freq = 2417, .max_power = 25}, > + {.hw_value = 3, .center_freq = 2422, .max_power = 25}, > + {.hw_value = 4, .center_freq = 2427, .max_power = 25}, > + {.hw_value = 5, .center_freq = 2432, .max_power = 25}, > + {.hw_value = 6, .center_freq = 2437, .max_power = 25}, > + {.hw_value = 7, .center_freq = 2442, .max_power = 25}, > + {.hw_value = 8, .center_freq = 2447, .max_power = 25}, > + {.hw_value = 9, .center_freq = 2452, .max_power = 25}, > + {.hw_value = 10, .center_freq = 2457, .max_power = 25}, > + {.hw_value = 11, .center_freq = 2462, .max_power = 25}, > + {.hw_value = 12, .center_freq = 2467, .max_power = 25}, > + {.hw_value = 13, .center_freq = 2472, .max_power = 25}, > +}; > + > +/* 11G rate */ > +static struct ieee80211_rate esp_rates_2ghz[] = { > + { > + .bitrate = 10, > + .hw_value = CONF_HW_BIT_RATE_1MBPS, > + .hw_value_short = CONF_HW_BIT_RATE_1MBPS, > + }, > + { > + .bitrate = 20, > + .hw_value = CONF_HW_BIT_RATE_2MBPS, > + .hw_value_short = CONF_HW_BIT_RATE_2MBPS, > + .flags = IEEE80211_RATE_SHORT_PREAMBLE}, > + { > + .bitrate = 55, > + .hw_value = CONF_HW_BIT_RATE_5_5MBPS, > + .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS, > + .flags = IEEE80211_RATE_SHORT_PREAMBLE}, > + { > + .bitrate = 110, > + .hw_value = CONF_HW_BIT_RATE_11MBPS, > + .hw_value_short = CONF_HW_BIT_RATE_11MBPS, > + .flags = IEEE80211_RATE_SHORT_PREAMBLE}, > + { > + .bitrate = 60, > + .hw_value = CONF_HW_BIT_RATE_6MBPS, > + .hw_value_short = CONF_HW_BIT_RATE_6MBPS, > + }, > + { > + .bitrate = 90, > + .hw_value = CONF_HW_BIT_RATE_9MBPS, > + .hw_value_short = CONF_HW_BIT_RATE_9MBPS, > + }, > + { > + .bitrate = 120, > + .hw_value = CONF_HW_BIT_RATE_12MBPS, > + .hw_value_short = CONF_HW_BIT_RATE_12MBPS, > + }, > + { > + .bitrate = 180, > + .hw_value = CONF_HW_BIT_RATE_18MBPS, > + .hw_value_short = CONF_HW_BIT_RATE_18MBPS, > + }, > + { > + .bitrate = 240, > + .hw_value = CONF_HW_BIT_RATE_24MBPS, > + .hw_value_short = CONF_HW_BIT_RATE_24MBPS, > + }, > + { > + .bitrate = 360, > + .hw_value = CONF_HW_BIT_RATE_36MBPS, > + .hw_value_short = CONF_HW_BIT_RATE_36MBPS, > + }, > + { > + .bitrate = 480, > + .hw_value = CONF_HW_BIT_RATE_48MBPS, > + .hw_value_short = CONF_HW_BIT_RATE_48MBPS, > + }, > + { > + .bitrate = 540, > + .hw_value = CONF_HW_BIT_RATE_54MBPS, > + .hw_value_short = CONF_HW_BIT_RATE_54MBPS, > + }, > +}; > + > +static struct ieee80211_sta_ht_cap esp_ht_cap_2ghz = { > + .cap = IEEE80211_HT_CAP_DSSSCCK40 | IEEE80211_HT_CAP_SM_PS | > + IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20, > + .ht_supported = true, > + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K, > + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, > + .mcs = { > + .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, > + }, > +}; > + > +static void esp_pub_init_mac80211(struct esp_pub *epub) > +{ > + struct ieee80211_hw *hw = epub->hw; > + struct ieee80211_supported_band *sbands = > + &epub->wl.sbands[NL80211_BAND_2GHZ]; > + > + static const u32 cipher_suites[] = { > + WLAN_CIPHER_SUITE_WEP40, > + WLAN_CIPHER_SUITE_WEP104, > + WLAN_CIPHER_SUITE_TKIP, > + WLAN_CIPHER_SUITE_CCMP, > + }; > + > + hw->max_listen_interval = 10; > + > + ieee80211_hw_set(hw, SIGNAL_DBM); > + ieee80211_hw_set(hw, HAS_RATE_CONTROL); > + ieee80211_hw_set(hw, SUPPORTS_PS); > + ieee80211_hw_set(hw, AMPDU_AGGREGATION); > + ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING); > + hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF; > + hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF; > + > + hw->wiphy->cipher_suites = cipher_suites; > + hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); > + hw->wiphy->max_scan_ie_len = epub->sip->tx_blksz - > + sizeof(struct sip_hdr) - sizeof(struct sip_cmd_scan); > + > + /* ONLY station for now, support P2P soon... */ > + /* FIXME: is p2p really supported? */ > + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_P2P_GO) | > + BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_STATION) | > + BIT(NL80211_IFTYPE_AP); > + > + hw->wiphy->max_scan_ssids = 2; > + hw->wiphy->max_remain_on_channel_duration = 5000; > + > + atomic_set(&epub->wl.off, 1); > + > + sbands->band = NL80211_BAND_2GHZ; > + sbands->channels = esp_channels_2ghz; > + sbands->bitrates = esp_rates_2ghz; > + sbands->n_channels = ARRAY_SIZE(esp_channels_2ghz); > + sbands->n_bitrates = ARRAY_SIZE(esp_rates_2ghz); > + sbands->ht_cap = esp_ht_cap_2ghz; > + > + hw->wiphy->bands[NL80211_BAND_2GHZ] = sbands; > + > + /*no fragment */ > + hw->wiphy->frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD; > + > + /* handle AC queue in f/w */ > + hw->queues = 4; > + hw->max_rates = 4; > + > + hw->vif_data_size = sizeof(struct esp_vif); > + hw->sta_data_size = sizeof(struct esp_node); > +} > + > +int esp_register_mac80211(struct esp_pub *epub) > +{ > + int ret, idx; > + u8 *p2p_addr; > + > + esp_pub_init_mac80211(epub); > + > + /* FIXME: esp_mac_addr */ > + epub->hw->wiphy->addresses = (struct mac_address *)esp_mac_addr; > + > + memcpy(epub->hw->wiphy->addresses[0].addr, epub->mac_addr, ETH_ALEN); > + memcpy(epub->hw->wiphy->addresses[1].addr, epub->mac_addr, ETH_ALEN); > + p2p_addr = epub->hw->wiphy->addresses[1].addr; > + > + for (idx = 0; idx < 8 * ETH_ALEN; idx++) { > + p2p_addr[0] = epub->mac_addr[0] | 0x02; > + p2p_addr[0] ^= idx << 2; > + if (strncmp(p2p_addr, epub->mac_addr, 6)) > + break; > + } > + > + epub->hw->wiphy->n_addresses = 2; > + > + ret = ieee80211_register_hw(epub->hw); > + if (ret < 0) { > + printk("unable to register mac80211 hw: %d\n", ret); > + return ret; > + } > + > + set_bit(ESP_WL_FLAG_HW_REGISTERED, &epub->wl.flags); > + > + return ret; return 0; > +} > + > +static u8 getaddr_index(u8 *addr, struct esp_pub *epub) > +{ > + int i; > + > + for (i = 0; i < ESP_PUB_MAX_VIF; i++) > + if (!memcmp(addr, epub->hw->wiphy->addresses[i].addr, ETH_ALEN)) > + return i; > + > + return ESP_PUB_MAX_VIF; > +} > diff --git a/drivers/staging/esp8089/esp_mac80211.h b/drivers/staging/esp8089/esp_mac80211.h > new file mode 100644 > index 000000000000..03de0b0a4223 > --- /dev/null > +++ b/drivers/staging/esp8089/esp_mac80211.h > @@ -0,0 +1,33 @@ > +/* > + * Copyright (c) 2011 - 2014 Espressif System. > + * Copyright (c) 2017 Free Electrons > + * Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it under > + * the terms of the GNU General Public License version 2 as published by the > + * Free Software Foundation. > + * > + */ > + > +#ifndef _ESP_MAC80211_H_ > +#define _ESP_MAC80211_H_ > + > +struct esp_80211_wmm_ac_param { > + u8 aci_aifsn; /* AIFSN, ACM, ACI */ > + u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */ > + u16 txop_limit; > +}; > + > +struct esp_80211_wmm_param_element { > + /* Element ID: 221 (0xdd); length: 24 */ > + /* required fields for WMM version 1 */ > + u8 oui[3]; /* 00:50:f2 */ > + u8 oui_type; /* 2 */ > + u8 oui_subtype; /* 1 */ > + u8 version; /* 1 for WMM version 1.0 */ > + u8 qos_info; /* AP/STA specif QoS info */ > + u8 reserved; /* 0 */ > + struct esp_80211_wmm_ac_param ac[4]; /* AC_BE, AC_BK, AC_VI, AC_VO */ > +}; > + > +#endif /* _ESP_MAC80211_H_ */ > diff --git a/drivers/staging/esp8089/esp_main.c b/drivers/staging/esp8089/esp_main.c > new file mode 100644 > index 000000000000..c4621847926c > --- /dev/null > +++ b/drivers/staging/esp8089/esp_main.c > @@ -0,0 +1,199 @@ > +/* > + * Copyright (c) 2010 - 2014 Espressif System. > + * Copyright (c) 2017 Free Electrons > + * Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it under > + * the terms of the GNU General Public License version 2 as published by the > + * Free Software Foundation. > + * > + */ > + > +#include <linux/netdevice.h> > +#include <linux/etherdevice.h> > +#include <linux/rtnetlink.h> > +#include <linux/firmware.h> > +#include <linux/sched.h> > +#include <net/cfg80211.h> > +#include <net/mac80211.h> > +#include <linux/time.h> > +#include <linux/moduleparam.h> > +#include <linux/module.h> > + > +#include "esp_pub.h" > +#include "esp_sip.h" > +#include "esp_sif.h" > +#include "esp_debug.h" > +#include "esp_file.h" > +#include "esp_wl.h" > + > +struct completion *gl_bootup_cplx; > + > +static int esp_download_fw(struct esp_pub *epub); > + > +static int modparam_no_txampdu; > +static int modparam_no_rxampdu; > + > +module_param_named(no_txampdu, modparam_no_txampdu, int, 0444); > +MODULE_PARM_DESC(no_txampdu, "Disable tx ampdu."); > + > +module_param_named(no_rxampdu, modparam_no_rxampdu, int, 0444); > +MODULE_PARM_DESC(no_rxampdu, "Disable rx ampdu."); > + > +static char *modparam_eagle_path = "/lib/firmware"; > + > +module_param_named(eagle_path, modparam_eagle_path, charp, 0444); > +MODULE_PARM_DESC(eagle_path, "eagle path"); > + > +bool mod_support_no_txampdu(void) > +{ > + return modparam_no_txampdu; > +} > + > +bool mod_support_no_rxampdu(void) > +{ > + return modparam_no_rxampdu; > +} > + > +int esp_pub_init_all(struct esp_pub *epub) > +{ > + int ret; > + > + /* completion for bootup event poll */ > + DECLARE_COMPLETION_ONSTACK(complete); > + > + atomic_set(&epub->ps.state, ESP_PM_OFF); > + > + if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) { > + epub->sip = sip_attach(epub); > + if (!epub->sip) { > + printk(KERN_ERR "%s sip alloc failed\n", __func__); > + return -ENOMEM; > + } > + } else { > + atomic_set(&epub->sip->state, SIP_PREPARE_BOOT); > + atomic_set(&epub->sip->tx_credits, 0); > + } > + > + epub->sip->to_host_seq = 0; > + > + ret = esp_download_fw(epub); > + if (ret) { > + printk("download firmware failed\n"); > + return ret; > + } > + > + gl_bootup_cplx = &complete; > + epub->wait_reset = 0; > + sif_enable_irq(epub); > + > + if (epub->sdio_state == ESP_SDIO_STATE_SECOND_INIT || > + sif_get_ate_config() == 1) { Drop the == 1. We mostly treat sif_get_ate_config() as bool, so let's do that consistently everywhere. > + ret = sip_poll_bootup_event(epub->sip); > + } else { > + ret = sip_poll_resetting_event(epub->sip); > + if (!ret) { > + sif_lock_bus(epub); > + sif_interrupt_target(epub, 7); > + sif_unlock_bus(epub); > + } > + } > + > + gl_bootup_cplx = NULL; > + > + if (sif_get_ate_config() == 1) > + ret = -EOPNOTSUPP; > + > + return ret; > +} > + > +void esp_dsr(struct esp_pub *epub) Can this function be removed? > +{ > + sip_rx(epub); > +} > + > +struct esp_fw_hdr { > + u8 magic; > + u8 blocks; > + u8 pad[2]; > + u32 entry_addr; > +} __packed; > + > +struct esp_fw_blk_hdr { > + u32 load_addr; > + u32 data_len; > +} __packed; > + > +#define ESP_FW_NAME1 "eagle_fw_ate_config_v19.bin" > +#define ESP_FW_NAME2 "eagle_fw_first_init_v19.bin" > +#define ESP_FW_NAME3 "eagle_fw_second_init_v19.bin" > + > +static int esp_download_fw(struct esp_pub *epub) > +{ > + const struct firmware *fw_entry; > + struct esp_fw_hdr *fhdr; > + struct esp_fw_blk_hdr *bhdr; > + struct sip_cmd_bootup bootcmd; > + u8 *fw_buf; > + u32 offset; > + int ret; > + u8 blocks; > + char *esp_fw_name; > + > + if (sif_get_ate_config() == 1) > + esp_fw_name = ESP_FW_NAME3; > + else > + esp_fw_name = epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT ? ESP_FW_NAME1 : ESP_FW_NAME2; > + > + ret = request_firmware(&fw_entry, esp_fw_name, epub->dev); > + if (ret) > + return ret; > + > + fw_buf = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL); > + > + release_firmware(fw_entry); > + > + if (!fw_buf) > + return -ENOMEM; > + > + fhdr = (struct esp_fw_hdr *)fw_buf; > + if (fhdr->magic != 0xE9) { > + printk("%s wrong firmware magic!\n", __func__); > + goto _err; Choose a label name based on what the goto does. For example, here if we hit the goto then we kfree(fw_buf); so "goto free_fw;" would be a descriptive name. Also don't forget to set the error code. > + } > + > + blocks = fhdr->blocks; > + offset = sizeof(struct esp_fw_hdr); > + > + while (blocks) { > + bhdr = (struct esp_fw_blk_hdr *)&fw_buf[offset]; > + offset += sizeof(struct esp_fw_blk_hdr); > + > + ret = sip_write_memory(epub->sip, bhdr->load_addr, > + &fw_buf[offset], bhdr->data_len); > + if (ret) { > + printk("failed to write firmware: %d\n", ret); > + goto _err; > + } > + > + blocks--; > + offset += bhdr->data_len; > + } > + > + /* TODO: last byte should be the checksum and skip checksum for now */ > + > + bootcmd.boot_addr = fhdr->entry_addr; > + ret = sip_send_cmd(epub->sip, SIP_CMD_BOOTUP, > + sizeof(struct sip_cmd_bootup), &bootcmd); > + if (ret) > + goto _err; > + > +_err: > + kfree(fw_buf); > + > + return ret; > +} > + > +MODULE_FIRMWARE(ESP_FW_NAME1); > +MODULE_FIRMWARE(ESP_FW_NAME2); > +MODULE_FIRMWARE(ESP_FW_NAME3); > diff --git a/drivers/staging/esp8089/esp_pub.h b/drivers/staging/esp8089/esp_pub.h > new file mode 100644 > index 000000000000..e329fb0999e9 > --- /dev/null > +++ b/drivers/staging/esp8089/esp_pub.h > @@ -0,0 +1,188 @@ > +/* > + * Copyright (c) 2011 - 2014 Espressif System. > + * Copyright (c) 2017 Free Electrons > + * Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it under > + * the terms of the GNU General Public License version 2 as published by the > + * Free Software Foundation. > + * > + */ > + > +#ifndef _ESP_PUB_H_ > +#define _ESP_PUB_H_ > + > +#include <linux/etherdevice.h> > +#include <linux/rtnetlink.h> > +#include <linux/firmware.h> > +#include <linux/sched.h> > +#include <net/mac80211.h> > +#include <net/cfg80211.h> > +#include "sip2_common.h" > + > +enum esp_sdio_state { > + ESP_SDIO_STATE_FIRST_INIT, > + ESP_SDIO_STATE_FIRST_NORMAL_EXIT, > + ESP_SDIO_STATE_FIRST_ERROR_EXIT, > + ESP_SDIO_STATE_SECOND_INIT, > + ESP_SDIO_STATE_SECOND_ERROR_EXIT, > +}; > + > +enum esp_tid_state { > + ESP_TID_STATE_INIT, > + ESP_TID_STATE_TRIGGER, > + ESP_TID_STATE_PROGRESS, > + ESP_TID_STATE_OPERATIONAL, > + ESP_TID_STATE_WAIT_STOP, > + ESP_TID_STATE_STOP, > +}; > + > +struct esp_tx_tid { > + u8 state; > + u8 cnt; > + u16 ssn; > +}; > + > +#define WME_NUM_TID 16 > +struct esp_node { > + struct esp_tx_tid tid[WME_NUM_TID]; > + struct ieee80211_sta *sta; > + u8 ifidx; > + u8 index; > +}; > + > +#define WME_AC_BE 2 > +#define WME_AC_BK 3 > +#define WME_AC_VI 1 > +#define WME_AC_VO 0 > + > +struct esp_vif { > + struct esp_pub *epub; > + u8 index; > + u32 beacon_interval; > + bool ap_up; > + struct timer_list beacon_timer; > +}; > + > +/* WLAN related, mostly... */ > +/*struct hw_scan_timeout { > + * struct delayed_work w; > + * struct ieee80211_hw *hw; > + * }; > + */ > + > +struct esp_wl { > + u8 bssid[ETH_ALEN]; > + u8 req_bssid[ETH_ALEN]; > + > + //struct hw_scan_timeout *hsd; > + struct cfg80211_scan_request *scan_req; > + atomic_t ptk_cnt; > + atomic_t gtk_cnt; > + atomic_t tkip_key_set; > + > + /* so far only 2G band */ > + struct ieee80211_supported_band sbands[NUM_NL80211_BANDS]; > + > + unsigned long flags; > + atomic_t off; > +}; > + > +struct esp_hw_idx_map { > + u8 mac[ETH_ALEN]; > + u8 flag; > +}; > + > +#define ESP_WL_FLAG_RFKILL BIT(0) > +#define ESP_WL_FLAG_HW_REGISTERED BIT(1) > +#define ESP_WL_FLAG_CONNECT BIT(2) > +#define ESP_WL_FLAG_STOP_TXQ BIT(3) > + > +#define ESP_PUB_MAX_VIF 2 > +#define ESP_PUB_MAX_STA 4 //for one interface > +#define ESP_PUB_MAX_RXAMPDU 8 //for all interfaces > + > +enum { > + ESP_PM_OFF = 0, > + ESP_PM_TURNING_ON, > + ESP_PM_ON, > + ESP_PM_TURNING_OFF, /* Do NOT change the order */ > +}; > + > +struct esp_ps { > + u32 dtim_period; > + u32 max_sleep_period; > + unsigned long last_config_time; > + atomic_t state; > + bool nulldata_pm_on; > +}; > + > +struct esp_mac_prefix { > + u8 mac_index; > + u8 mac_addr_prefix[3]; > +}; > + > +struct esp_pub { > + struct device *dev; > + struct ieee80211_hw *hw; > + struct ieee80211_vif *vif; > + u8 vif_slot; > + > + struct esp_sdio_ctrl *sif; /* serial interface control block, e.g. sdio */ > + enum esp_sdio_state sdio_state; > + struct esp_sip *sip; > + struct esp_wl wl; > + struct esp_hw_idx_map hi_map[19]; > + struct esp_hw_idx_map low_map[ESP_PUB_MAX_VIF][2]; > + //u32 flags; //flags to represent rfkill switch,start > + u8 roc_flags; //0: not in remain on channel state, 1: in roc state > + > + struct work_struct tx_work; /* attach to ieee80211 workqueue */ > + /* latest mac80211 has multiple tx queue, but we stick with single queue now */ > + spinlock_t rx_lock; > + spinlock_t tx_ampdu_lock; > + spinlock_t rx_ampdu_lock; > + spinlock_t tx_lock; > + struct mutex tx_mtx; > + struct sk_buff_head txq; > + atomic_t txq_stopped; > + > + struct work_struct sendup_work; /* attach to ieee80211 workqueue */ > + struct sk_buff_head txdoneq; > + struct sk_buff_head rxq; > + > + struct workqueue_struct *esp_wkq; > + > + //u8 bssid[ETH_ALEN]; > + u8 mac_addr[ETH_ALEN]; > + > + u32 rx_filter; > + unsigned long scan_permit; > + bool scan_permit_valid; > + struct delayed_work scan_timeout_work; > + u32 enodes_map; > + u8 rxampdu_map; > + u32 enodes_maps[ESP_PUB_MAX_VIF]; > + struct esp_node *enodes[ESP_PUB_MAX_STA + 1]; > + struct esp_node *rxampdu_node[ESP_PUB_MAX_RXAMPDU]; > + u8 rxampdu_tid[ESP_PUB_MAX_RXAMPDU]; > + struct esp_ps ps; > + int enable_int; > + int wait_reset; > +}; > + > +struct esp_pub *esp_pub_alloc_mac80211(struct device *dev); > +int esp_pub_dealloc_mac80211(struct esp_pub *epub); > +int esp_register_mac80211(struct esp_pub *epub); > + > +int esp_pub_init_all(struct esp_pub *epub); > + > +void esp_dsr(struct esp_pub *epub); > +void hw_scan_done(struct esp_pub *epub, bool aborted); > +void esp_rocdone_process(struct ieee80211_hw *hw, struct sip_evt_roc *report); > + > +struct esp_node *esp_get_node_by_addr(struct esp_pub *epub, const u8 *addr); > +int esp_get_empty_rxampdu(struct esp_pub *epub, const u8 *addr, u8 tid); > +int esp_get_exist_rxampdu(struct esp_pub *epub, const u8 *addr, u8 tid); > + > +#endif /* _ESP_PUB_H_ */ > diff --git a/drivers/staging/esp8089/esp_sif.h b/drivers/staging/esp8089/esp_sif.h > new file mode 100644 > index 000000000000..293640b11b0c > --- /dev/null > +++ b/drivers/staging/esp8089/esp_sif.h > @@ -0,0 +1,131 @@ > +/* > + * Copyright (c) 2011 - 2014 Espressif System. > + * Copyright (c) 2017 Free Electrons > + * Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it under > + * the terms of the GNU General Public License version 2 as published by the > + * Free Software Foundation. > + * > + */ > + > +#ifndef _ESP_SIF_H_ > +#define _ESP_SIF_H_ > + > +#include "esp_pub.h" > +#include <linux/mmc/host.h> > +#include <linux/spi/spi.h> > + > +/* H/W SLC module definitions */ > + > +#define SIF_SLC_BLOCK_SIZE 512 > + > +/* S/W struct mapping to slc registers */ > +struct slc_host_regs { > + /* do NOT read token_rdata > + * > + * u32 pf_data; > + * u32 token_rdata; > + */ > + u32 intr_raw; > + u32 state_w0; > + u32 state_w1; > + u32 config_w0; > + u32 config_w1; > + u32 intr_status; > + u32 config_w2; > + u32 config_w3; > + u32 config_w4; > + u32 token_wdata; > + u32 intr_clear; > + u32 intr_enable; > +}; > + > +enum io_sync_type { > + ESP_SIF_NOSYNC = 0, > + ESP_SIF_SYNC, > +}; > + > +struct esp_sdio_ctrl { > + struct sdio_func *func; > + struct esp_pub *epub; > + > + struct list_head free_req; > + > + u8 *dma_buffer; > + > + spinlock_t scat_lock; > + struct list_head scat_req; > + > + bool off; > + atomic_t irq_handling; > + const struct sdio_device_id *id; > + u32 slc_blk_sz; > + u32 target_id; > + u32 slc_window_end_addr; > + > + struct slc_host_regs slc_regs; > + atomic_t irq_installed; > + > +}; > + > +#define SIF_TO_DEVICE 0x1 > +#define SIF_FROM_DEVICE 0x2 > + > +#define SIF_SYNC 0x00000010 > +#define SIF_ASYNC 0x00000020 > + > +#define SIF_BYTE_BASIS 0x00000040 > +#define SIF_BLOCK_BASIS 0x00000080 > + > +#define SIF_FIXED_ADDR 0x00000100 > +#define SIF_INC_ADDR 0x00000200 > + > +void sdio_io_writeb(struct esp_pub *epub, u8 value, int addr, int *res); > +u8 sdio_io_readb(struct esp_pub *epub, int addr, int *res); > + > +void sif_enable_irq(struct esp_pub *epub); > +void sif_disable_irq(struct esp_pub *epub); > +void sif_disable_target_interrupt(struct esp_pub *epub); > + > +u32 sif_get_blksz(struct esp_pub *epub); > + > +void sif_dsr(struct sdio_func *func); > +int sif_io_raw(struct esp_pub *epub, u32 addr, u8 *buf, u32 len, u32 flag); > +int sif_io_sync(struct esp_pub *epub, u32 addr, u8 *buf, u32 len, u32 flag); > +int sif_lldesc_read_sync(struct esp_pub *epub, u8 *buf, u32 len); > +int sif_lldesc_write_sync(struct esp_pub *epub, u8 *buf, u32 len); > +int sif_lldesc_read_raw(struct esp_pub *epub, u8 *buf, u32 len, bool noround); > +int sif_lldesc_write_raw(struct esp_pub *epub, u8 *buf, u32 len); > + > +int esp_common_read(struct esp_pub *epub, u8 *buf, u32 len, int sync, > + bool noround); > +int esp_common_write(struct esp_pub *epub, u8 *buf, u32 len, int sync); > +int esp_common_read_with_addr(struct esp_pub *epub, u32 addr, u8 *buf, u32 len, > + int sync); > +int esp_common_write_with_addr(struct esp_pub *epub, u32 addr, u8 *buf, > + u32 len, int sync); > + > +int esp_common_readbyte_with_addr(struct esp_pub *epub, u32 addr, u8 *buf, > + int sync); > +int esp_common_writebyte_with_addr(struct esp_pub *epub, u32 addr, u8 buf, > + int sync); > + > +struct slc_host_regs *sif_get_regs(struct esp_pub *epub); > + > +void sif_lock_bus(struct esp_pub *epub); > +void sif_unlock_bus(struct esp_pub *epub); > + > +int sif_interrupt_target(struct esp_pub *epub, u8 index); > + > +void check_target_id(struct esp_pub *epub); > + > +void sif_record_bt_config(int value); > +int sif_get_bt_config(void); > +void sif_record_rst_config(int value); > +int sif_get_rst_config(void); > +void sif_record_ate_config(int value); > +int sif_get_ate_config(void); > +void sif_record_wakeup_gpio_config(int value); > +int sif_get_wakeup_gpio_config(void); > +#endif /* _ESP_SIF_H_ */ > diff --git a/drivers/staging/esp8089/esp_sip.c b/drivers/staging/esp8089/esp_sip.c > new file mode 100644 > index 000000000000..91fb64c9b794 > --- /dev/null > +++ b/drivers/staging/esp8089/esp_sip.c > @@ -0,0 +1,1718 @@ > +/* > + * Copyright (c) 2009 - 2014 Espressif System. > + * Copyright (c) 2017 Free Electrons > + * Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it under > + * the terms of the GNU General Public License version 2 as published by the > + * Free Software Foundation. > + * > + */ > + > +#include <linux/ieee80211.h> > +#include <net/mac80211.h> > +#include <net/cfg80211.h> > +#include <linux/skbuff.h> > +#include <linux/bitops.h> > +#include <linux/mmc/card.h> > +#include <linux/mmc/mmc.h> > +#include <linux/mmc/host.h> > +#include <linux/mmc/sdio_func.h> > +#include <linux/mmc/sdio_ids.h> > +#include <linux/mmc/sdio.h> > +#include <linux/mmc/sd.h> > +#include <linux/completion.h> > + > +#include "esp_mac80211.h" > +#include "esp_pub.h" > +#include "esp_sip.h" > +#include "esp_ctrl.h" > +#include "esp_sif.h" > +#include "esp_debug.h" > +#include "slc_host_register.h" > +#include "esp_wmac.h" > +#include "esp_utils.h" > + > +extern struct completion *gl_bootup_cplx; > + > +static int old_signal = -35; > +static int avg_signal; > +static int signal_loop; > + > +struct esp_mac_prefix esp_mac_prefix_table[] = { > + {0, {0x18, 0xfe, 0x34} }, > + {1, {0xac, 0xd0, 0x74} }, > + {255, {0x18, 0xfe, 0x34} }, > +}; > + > +#define SIGNAL_COUNT 300 > + > +/* FIXME: Incomplete ternary condition */ > +#define TID_TO_AC(_tid) (!(_tid) || (_tid) == 3 ? WME_AC_BE : ((_tid) < 3 ? WME_AC_BK : ((_tid) < 6 ? WME_AC_VI : WME_AC_VO))) > + > +#define esp_sip_dbg esp_dbg > +struct sip_trace { > + u32 tx_data; > + u32 tx_cmd; > + u32 rx_data; > + u32 rx_evt; > + u32 rx_tx_status; > + u32 tx_out_of_credit; > + u32 tx_one_shot_overflow; > +}; > + > +static struct sip_trace str; > + > +#define STRACE_TX_DATA_INC() (str.tx_data++) > +#define STRACE_TX_CMD_INC() (str.tx_cmd++) > +#define STRACE_RX_DATA_INC() (str.rx_data++) > +#define STRACE_RX_EVENT_INC() (str.rx_evt++) > +#define STRACE_RX_TXSTATUS_INC() (str.rx_tx_status++) > +#define STRACE_TX_OUT_OF_CREDIT_INC() (str.tx_out_of_credit++) > +#define STRACE_TX_ONE_SHOT_INC() (str.tx_one_shot_overflow++) > + > +#define SIP_STOP_QUEUE_THRESHOLD 48 > +#define SIP_RESUME_QUEUE_THRESHOLD 12 > + > +#define SIP_MIN_DATA_PKT_LEN (sizeof(struct esp_mac_rx_ctrl) + 24) //24 is min 80211hdr > + > +static void sip_recalc_credit_init(struct esp_sip *sip); > + > +static int sip_recalc_credit_claim(struct esp_sip *sip, int force); > + > +static void sip_recalc_credit_release(struct esp_sip *sip); > + > +static struct sip_pkt *sip_get_ctrl_buf(struct esp_sip *sip, > + enum sip_buf_type bftype); > + > +static void sip_reclaim_ctrl_buf(struct esp_sip *sip, struct sip_pkt *pkt, > + enum sip_buf_type bftype); > + > +static void sip_free_init_ctrl_buf(struct esp_sip *sip); > + > +static int sip_pack_pkt(struct esp_sip *sip, struct sk_buff *skb, > + int *pm_state); > + > +static struct esp_mac_rx_ctrl *sip_parse_normal_mac_ctrl(struct sk_buff *skb, > + int *pkt_len_enc, > + int *buf_len, > + int *pulled_len); > + > +static struct sk_buff *sip_parse_data_rx_info(struct esp_sip *sip, > + struct sk_buff *skb, > + int pkt_len_enc, int buf_len, > + struct esp_mac_rx_ctrl *mac_ctrl, > + int *pulled_len); > + > +static inline void sip_rx_pkt_enqueue(struct esp_sip *sip, struct sk_buff *skb); > + > +static void sip_update_tx_credits(struct esp_sip *sip, u16 recycled_credits); > + > +static bool sip_rx_pkt_process(struct esp_sip *sip, struct sk_buff *skb); > + > +static void sip_tx_status_report(struct esp_sip *sip, struct sk_buff *skb, > + struct ieee80211_tx_info *tx_info, > + bool success); > + > +static bool check_ac_tid(u8 *pkt, u8 ac, u8 tid) > +{ > + struct ieee80211_hdr *wh = (struct ieee80211_hdr *)pkt; > + > + if (!ieee80211_is_data_qos(wh->frame_control) && > + !ieee80211_is_mgmt(wh->frame_control) && > + !ieee80211_is_ctl(wh->frame_control)) { > + if (tid || ac != WME_AC_BE) { > + pr_info("444 ac:%u, tid:%u\n", ac, tid); > + > + if (tid == 7 && ac == WME_AC_VO) > + return false; > + } > + > + return true; //hack to modify non-qos null data. > + } > + > + return false; > +} > + > +static void sip_recalc_credit_timeout(unsigned long data) > +{ > + struct esp_sip *sip = (struct esp_sip *)data; > + > + sip_recalc_credit_claim(sip, 1); /* recalc again */ > +} > + > +static void sip_recalc_credit_init(struct esp_sip *sip) > +{ > + atomic_set(&sip->credit_status, RECALC_CREDIT_DISABLE); //set it disable > + > + setup_timer(&sip->credit_timer, sip_recalc_credit_timeout, > + (unsigned long)sip); > +} > + > +static int sip_recalc_credit_claim(struct esp_sip *sip, int force) > +{ > + int ret; > + > + if (atomic_read(&sip->credit_status) == RECALC_CREDIT_ENABLE && !force) > + return 1; Put a comment to describe what why we are returning a mix of 1, zero and negative error codes. It's impossible to tell from the context because none of the callers check. > + > + atomic_set(&sip->credit_status, RECALC_CREDIT_ENABLE); > + ret = sip_send_recalc_credit(sip->epub); > + if (ret) { > + dev_err(sip->epub->dev, "sending recalc credit failed: %d", > + ret); > + return ret; > + } > + > + /*setup a timer for handle the abs_credit not receive */ > + mod_timer(&sip->credit_timer, jiffies + msecs_to_jiffies(2000)); > + > + return ret; return 0; > +} > + > +static void sip_recalc_credit_release(struct esp_sip *sip) > +{ > + if (atomic_read(&sip->credit_status) == RECALC_CREDIT_ENABLE) { > + atomic_set(&sip->credit_status, RECALC_CREDIT_DISABLE); > + del_timer_sync(&sip->credit_timer); > + } else { > + dev_dbg(sip->epub->dev, "maybe bogus credit"); > + } > +} > + > +static void sip_update_tx_credits(struct esp_sip *sip, u16 recycled_credits) > +{ > + if (recycled_credits & 0x800) { > + atomic_set(&sip->tx_credits, (recycled_credits & 0x7ff)); > + sip_recalc_credit_release(sip); > + } else { > + atomic_add(recycled_credits, &sip->tx_credits); > + } > +} > + > +void sip_trigger_txq_process(struct esp_sip *sip) > +{ > + if (atomic_read(&sip->tx_credits) <= sip->credit_to_reserve + SIP_CTRL_CREDIT_RESERVE || > + atomic_read(&sip->credit_status) == RECALC_CREDIT_ENABLE) > + return; > + > + if (sip_queue_may_resume(sip)) { > + /* wakeup upper queue only if we have sufficient credits */ > + atomic_set(&sip->epub->txq_stopped, false); > + ieee80211_wake_queues(sip->epub->hw); > + } else if (atomic_read(&sip->epub->txq_stopped)) { > + dev_err(sip->epub->dev, "%s can't wake txq, credits: %d\n", > + __func__, atomic_read(&sip->tx_credits)); > + } > + > + if (!skb_queue_empty(&sip->epub->txq)) { > + /* try to send out pkt already in sip queue once we have credits */ > +#if !defined(FPGA_TXDATA) > + if (!sif_get_ate_config()) > + ieee80211_queue_work(sip->epub->hw, > + &sip->epub->tx_work); > + else > + queue_work(sip->epub->esp_wkq, &sip->epub->tx_work); > +#else > + queue_work(sip->epub->esp_wkq, &sip->epub->tx_work); > +#endif > + } > +} > + > +static bool sip_ampdu_occupy_buf(struct esp_sip *sip, > + struct esp_rx_ampdu_len *ampdu_len) > +{ > + return (!ampdu_len->substate || > + esp_wmac_rxsec_error(ampdu_len->substate) || > + (sip->dump_rpbm_err && ampdu_len->substate == RX_RPBM_ERR)); > +} > + > +/* TODO: HARDCORE CLEANUP */ > + > +static bool sip_rx_pkt_process(struct esp_sip *sip, struct sk_buff *skb) > +{ > + struct sip_hdr *hdr; > + struct sk_buff *rskb; > + struct esp_mac_rx_ctrl *mac_ctrl, new_mac_ctrl; > + int remains_len, first_pkt_len, ret = 0, pkt_len_enc = 0, buf_len = 0, > + pulled_len = 0, pkt_num; > + u8 *bufptr; > + bool trigger_rxq = false, have_rxabort = false, have_goodpkt = false; > + struct esp_rx_ampdu_len *ampdu_len; > + static int pkt_dropped, pkt_total; > + static u8 frame_head[16]; > + static u8 frame_buf_ttl; > + > + if (!skb) > + return trigger_rxq; > + > + hdr = (struct sip_hdr *)skb->data; > + bufptr = skb->data; > + > + if (hdr->h_credits & SIP_CREDITS_MASK) > + sip_update_tx_credits(sip, hdr->h_credits & SIP_CREDITS_MASK); > + > + hdr->h_credits &= ~SIP_CREDITS_MASK; /* clean credits in sip_hdr, prevent over-add */ > + > + /* first pkt's length is stored in recycled_credits first 20 bits > + * config w3 [31:12] > + * repair hdr->len of first pkt > + */ > + remains_len = hdr->len; > + first_pkt_len = hdr->h_credits >> 12; > + hdr->len = first_pkt_len; > + > + if (first_pkt_len > remains_len) { > + sip_recalc_credit_claim(sip, 0); > + show_buf((u8 *)hdr, first_pkt_len); This feels like an info leak. first_pkt_len comes from the network. > + ESSERT(0); > + goto _exit; > + } > + > + /* pkts handling, including the first pkt, should alloc new skb for each data pkt. > + * free the original whole skb after parsing is done. > + */ > + while (remains_len) { > + if (remains_len < sizeof(struct sip_hdr)) { > + sip_recalc_credit_claim(sip, 0); > + ESSERT(0); > + show_buf((u8 *)hdr, 512); > + goto _exit; > + } > + > + hdr = (struct sip_hdr *)bufptr; > + if (hdr->len <= 0 || hdr->len & 3) { > + sip_recalc_credit_claim(sip, 0); > + show_buf((u8 *)hdr, 512); > + ESSERT(0); > + goto _exit; > + } > + > + if (unlikely(hdr->seq != sip->rxseq++)) { > + sip_recalc_credit_claim(sip, 0); > + dev_dbg(sip->epub->dev, > + "%s seq mismatch! got %u, expect %u\n", > + __func__, hdr->seq, sip->rxseq - 1); > + sip->rxseq = hdr->seq + 1; > + show_buf(bufptr, 32); > + ESSERT(0); > + } > + > + if (SIP_HDR_IS_CTRL(hdr)) { > + STRACE_RX_EVENT_INC(); > + > + ret = sip_parse_events(sip, bufptr); > + skb_pull(skb, hdr->len); > + } else if (SIP_HDR_IS_DATA(hdr)) { > + STRACE_RX_DATA_INC(); > + mac_ctrl = sip_parse_normal_mac_ctrl(skb, &pkt_len_enc, > + &buf_len, > + &pulled_len); > + rskb = sip_parse_data_rx_info(sip, skb, pkt_len_enc, > + buf_len, mac_ctrl, > + &pulled_len); > + > + if (!rskb) > + goto _move_on; > + > + if (likely(atomic_read(&sip->epub->wl.off) == 0)) { > + local_bh_disable(); > + ieee80211_rx(sip->epub->hw, rskb); > + local_bh_enable(); > + } else { > + /* still need go thro parsing as skb_pull should invoke */ > + kfree_skb(rskb); > + } > + } else if (SIP_HDR_IS_AMPDU(hdr)) { > + ampdu_len = (struct esp_rx_ampdu_len *)(skb->data + hdr->len / sip->rx_blksz * sip->rx_blksz); > + dev_dbg(sip->epub->dev, "%s rx ampdu total len %u\n", > + __func__, hdr->len); > + if (skb->data != (u8 *)hdr) { > + printk("%p %p\n", skb->data, hdr); > + show_buf(skb->data, 512); > + show_buf((u8 *)hdr, 512); > + ESSERT(0); > + goto _exit; > + } > + mac_ctrl = sip_parse_normal_mac_ctrl(skb, NULL, NULL, > + &pulled_len); > + memcpy(&new_mac_ctrl, mac_ctrl, > + sizeof(struct esp_mac_rx_ctrl)); > + mac_ctrl = &new_mac_ctrl; > + pkt_num = mac_ctrl->ampdu_cnt; > + dev_dbg(sip->epub->dev, > + "%s %d rx ampdu %u pkts, %d pkts dumped, first len %u\n", > + __func__, __LINE__, > + (unsigned int)((hdr->len % sip->rx_blksz) / > + sizeof(struct esp_rx_ampdu_len)), > + pkt_num, (unsigned int)ampdu_len->sublen); > + > + pkt_total += mac_ctrl->ampdu_cnt; > + while (pkt_num > 0) { > + dev_dbg(sip->epub->dev, > + "%s %d ampdu sub state %02x,\n", > + __func__, __LINE__, > + ampdu_len->substate); > + > + if (sip_ampdu_occupy_buf(sip, ampdu_len)) { //pkt is dumped > + rskb = sip_parse_data_rx_info(sip, skb, > + ampdu_len->sublen - FCS_LEN, > + 0, > + mac_ctrl, > + &pulled_len); > + if (!rskb) { > + ESSERT(0); > + goto _exit; > + } > + > + if (likely(atomic_read(&sip->epub->wl.off) == 0) && > + (ampdu_len->substate == 0 || > + ampdu_len->substate == RX_TKIPMIC_ERR || > + (sip->sendup_rpbm_pkt && > + ampdu_len->substate == RX_RPBM_ERR)) && > + (sip->rxabort_fixed || > + !have_rxabort)) { > + if (!have_goodpkt) { > + have_goodpkt = true; > + memcpy(frame_head, > + rskb->data, > + 16); > + frame_head[1] &= ~0x80; > + frame_buf_ttl = 3; > + } > + local_bh_disable(); > + ieee80211_rx(sip->epub->hw, > + rskb); > + local_bh_enable(); > + } else { > + kfree_skb(rskb); > + } > + } else { > + if (ampdu_len->substate == RX_ABORT) { > + u8 *a; > + have_rxabort = true; > + dev_dbg(sip->epub->dev, > + "rx abort %d %d\n", > + frame_buf_ttl, pkt_num); > + if (frame_buf_ttl && > + !sip->rxabort_fixed) { > + struct esp_rx_ampdu_len *next_good_ampdu_len = ampdu_len + 1; > + a = frame_head; > + while (!sip_ampdu_occupy_buf(sip, next_good_ampdu_len)) { > + if (next_good_ampdu_len > ampdu_len + pkt_num - 1) > + break; > + next_good_ampdu_len++; > + } > + if (next_good_ampdu_len <= ampdu_len + pkt_num - 1) { > + bool b0, b10, b11; > + a = skb->data; > + b0 = memcmp(frame_head + 4, skb->data + 4, 12) == 0; > + b10 = memcmp(frame_head + 10, skb->data, 6) == 0; > + b11 = memcpy(frame_head + 11, skb->data, 5) == 0; > + if (b0 && !b10 && > + !b11) { > + have_rxabort = false; > + } else if (!b0 && > + b10 && > + !b11) { > + skb_push(skb, 10); > + memcpy(skb->data, > + frame_head, > + 10); > + have_rxabort = false; > + pulled_len -= 10; > + } else if (!b0 && !b10 && b11) { > + skb_push(skb, 11); > + memcpy(skb->data, > + frame_head, > + 11); > + have_rxabort = false; > + pulled_len -= 11; > + } > + } > + } > + } > + pkt_dropped++; > + dev_dbg(sip->epub->dev, > + "%s ampdu dropped %d/%d\n", > + __func__, pkt_dropped, > + pkt_total); > + } > + pkt_num--; > + ampdu_len++; > + } > + if (frame_buf_ttl) > + frame_buf_ttl--; > + skb_pull(skb, hdr->len - pulled_len); > + } else { > + dev_err(sip->epub->dev, "unknown SIP HDR type\n"); > + } > + > +_move_on: > + if (hdr->len < remains_len) > + remains_len -= hdr->len; > + else > + break; > + bufptr += hdr->len; > + } > + > +_exit: > + kfree_skb(skb); > + > + return trigger_rxq; > +} > + > +static void _sip_rxq_process(struct esp_sip *sip) > +{ > + struct sk_buff *skb; > + bool sendup = false; > + > + while ((skb = skb_dequeue(&sip->rxq))) > + if (sip_rx_pkt_process(sip, skb)) > + sendup = true; > + > + if (sendup) > + queue_work(sip->epub->esp_wkq, &sip->epub->sendup_work); > + > + /* probably tx_credit is updated, try txq */ > + sip_trigger_txq_process(sip); > +} > + > +void sip_rxq_process(struct work_struct *work) > +{ > + struct esp_sip *sip = container_of(work, struct esp_sip, > + rx_process_work); > + > + if (!sip) { > + ESSERT(0); > + return; > + } > + > + if (unlikely(atomic_read(&sip->state) == SIP_SEND_INIT)) { > + sip_send_chip_init(sip); > + atomic_set(&sip->state, SIP_WAIT_BOOTUP); > + return; > + } > + > + mutex_lock(&sip->rx_mtx); > + _sip_rxq_process(sip); > + mutex_unlock(&sip->rx_mtx); > +} > + > +static inline void sip_rx_pkt_enqueue(struct esp_sip *sip, struct sk_buff *skb) > +{ > + skb_queue_tail(&sip->rxq, skb); > +} > + > +static u32 sip_rx_count; > + > +int sip_rx(struct esp_pub *epub) > +{ > + struct sip_hdr *shdr; > + struct esp_sip *sip = epub->sip; > + struct sk_buff *first_skb, *rx_skb; > + u8 *rx_buf; > + u32 rx_blksz, first_sz; > + int err; > + u8 raw_seq; > + > + if (likely(sif_get_ate_config() != 1)) { > + raw_seq = sif_get_regs(epub)->intr_raw & 0xff; > + > + if (raw_seq != sip->to_host_seq) { > + if (raw_seq == sip->to_host_seq + 1) { /* when last read pkt crc err, this situation may occur, but raw_seq mustn't < to_host_Seq */ > + sip->to_host_seq = raw_seq; > + dev_dbg(epub->dev, > + "warn: to_host_seq reg 0x%02x, seq 0x%02x", > + raw_seq, sip->to_host_seq); > + } else { > + dev_err(epub->dev, > + "err: to_host_seq reg 0x%02x, seq 0x%02x", > + raw_seq, sip->to_host_seq); > + err = 0; > + goto _err; This function uses a mix of direct returns and goto _err. Just use direct returns unless there is a good reason in the present (not future) for the goto. It just hurts readability to have to scroll 2 pages down to see what the goto does. "return 0;" is much quicker. > + } > + } > + } > + > + /* first read one block out, if we luck enough, that's it > + * > + * To make design as simple as possible, we allocate skb(s) > + * separately for each sif read operation to avoid global > + * read_buf_pointe access. It coule be optimized late. > + */ > + > + first_sz = sif_get_regs(epub)->config_w0; > + rx_blksz = sif_get_blksz(epub); > + first_skb = __dev_alloc_skb(roundup(first_sz, rx_blksz), GFP_KERNEL); > + > + if (!first_skb) { > + sif_unlock_bus(epub); > + err = 0; > + goto _err; > + } > + > + rx_buf = skb_put(first_skb, first_sz); > + > + err = esp_common_read(epub, rx_buf, first_sz, ESP_SIF_NOSYNC, false); > + sip_rx_count++; > + if (unlikely(err)) { > + dev_err(epub->dev, " first read err %d %d\n", err, > + sif_get_regs(epub)->config_w0); > + kfree_skb(first_skb); > + sif_unlock_bus(epub); > + goto _err; I feel like we should create a error goto the unlocks and frees first_skb. > + } > + > + shdr = (struct sip_hdr *)rx_buf; > + if (SIP_HDR_IS_CTRL(shdr) && shdr->c_evtid == SIP_EVT_SLEEP) { > + atomic_set(&sip->epub->ps.state, ESP_PM_ON); > + } > + > + if (likely(sif_get_ate_config() != 1)) > + sip->to_host_seq++; > + > + if (shdr->len & 3) { > + dev_err(epub->dev, "shdr->len[%d] error\n", shdr->len); > + kfree_skb(first_skb); > + sif_unlock_bus(epub); > + err = -EIO; > + goto _err; > + } > + > + if (shdr->len != first_sz) { > + dev_err(epub->dev, "shdr->len[%d] first_size[%d] error\n", > + shdr->len, first_sz); > + kfree_skb(first_skb); > + sif_unlock_bus(epub); > + err = -EIO; > + goto _err; > + } else { > + sif_unlock_bus(epub); > + skb_trim(first_skb, shdr->len); > + dev_dbg(epub->dev, "first_skb only\n"); > + > + rx_skb = first_skb; > + } > + > + if (atomic_read(&sip->state) == SIP_STOP) { > + kfree_skb(rx_skb); > + dev_err(epub->dev, "rx packet while sip stopped\n"); We don't unlock on this path? > + return 0; > + } > + > + sip_rx_pkt_enqueue(sip, rx_skb); > + queue_work(sip->epub->esp_wkq, &sip->rx_process_work); > + > +_err: > + return err; > +} > + > +int sip_post_init(struct esp_sip *sip, struct sip_evt_bootup2 *bevt) > +{ > + struct esp_pub *epub; > + u8 mac_id = bevt->mac_addr[0]; > + int mac_index = 0, i; > + > + if (!sip) { > + ESSERT(0); > + return -EINVAL; > + } > + > + epub = sip->epub; > + > + sip->tx_aggr_write_ptr = sip->tx_aggr_buf; > + sip->tx_blksz = bevt->tx_blksz; > + sip->rx_blksz = bevt->rx_blksz; > + sip->credit_to_reserve = bevt->credit_to_reserve; > + sip->dump_rpbm_err = bevt->options & SIP_DUMP_RPBM_ERR; > + sip->rxabort_fixed = bevt->options & SIP_RXABORT_FIXED; > + sip->support_bgscan = bevt->options & SIP_SUPPORT_BGSCAN; > + sip->sendup_rpbm_pkt = 0; > + > + /* print out MAC addr... */ > + memcpy(epub->mac_addr, bevt->mac_addr, ETH_ALEN); > + for (i = 0; > + i < sizeof(esp_mac_prefix_table) / sizeof(struct esp_mac_prefix); > + i++) > + if (esp_mac_prefix_table[i].mac_index == mac_id) { > + mac_index = i; > + break; > + } > + > + epub->mac_addr[0] = esp_mac_prefix_table[mac_index].mac_addr_prefix[0]; > + epub->mac_addr[1] = esp_mac_prefix_table[mac_index].mac_addr_prefix[1]; > + epub->mac_addr[2] = esp_mac_prefix_table[mac_index].mac_addr_prefix[2]; > + > + atomic_set(&sip->noise_floor, bevt->noise_floor); > + > + sip_recalc_credit_init(sip); > + > + return 0; > +} > + > +/* write pkts in aggr buf to target memory */ > +static void sip_write_pkts(struct esp_sip *sip, int pm_state) > +{ > + int tx_aggr_len, err; > + struct sip_hdr *first_shdr; > + > + tx_aggr_len = sip->tx_aggr_write_ptr - sip->tx_aggr_buf; > + if (tx_aggr_len < sizeof(struct sip_hdr)) { > + dev_err(sip->epub->dev, "[tx_aggr_len] %d < sizeof(sip_hdr)\n", > + tx_aggr_len); > + ESSERT(0); > + return; > + } > + > + if (tx_aggr_len & 0x3) { > + ESSERT(0); > + return; > + } > + > + first_shdr = (struct sip_hdr *)sip->tx_aggr_buf; > + > + if (atomic_read(&sip->tx_credits) <= SIP_CREDITS_LOW_THRESHOLD) > + first_shdr->fc[1] |= SIP_HDR_F_NEED_CRDT_RPT; > + > + /* still use lock bus instead of sif_lldesc_write_sync since we want to protect several global varibles assignments */ > + sif_lock_bus(sip->epub); > + > + err = esp_common_write(sip->epub, sip->tx_aggr_buf, tx_aggr_len, > + ESP_SIF_NOSYNC); > + if (err) > + dev_err(sip->epub->dev, "error while writing pkts: %d\n", err); > + > + sip->tx_aggr_write_ptr = sip->tx_aggr_buf; > + sip->tx_tot_len = 0; > + > + sif_unlock_bus(sip->epub); > +} > + > +/* setup sip header and tx info, copy pkt into aggr buf */ > +static int sip_pack_pkt(struct esp_sip *sip, struct sk_buff *skb, int *pm_state) > +{ > + struct ieee80211_tx_info *itx_info; > + struct sip_hdr *shdr; > + struct ieee80211_hdr *wh; > + struct esp_vif *evif; > + struct esp_node *node; > + u32 tx_len, offset; > + bool is_data = true; > + u8 sta_index; > + int alg; > + > + itx_info = IEEE80211_SKB_CB(skb); > + if (itx_info->flags == 0xffffffff) { > + shdr = (struct sip_hdr *)skb->data; > + is_data = false; > + tx_len = skb->len; > + } else { > + wh = (struct ieee80211_hdr *)skb->data; > + evif = (struct esp_vif *)itx_info->control.vif->drv_priv; > + /* update sip header */ > + shdr = (struct sip_hdr *)sip->tx_aggr_write_ptr; > + > + shdr->fc[0] = 0; > + shdr->fc[1] = 0; > + > + if (itx_info->flags & IEEE80211_TX_CTL_AMPDU) > + SIP_HDR_SET_TYPE(shdr->fc[0], SIP_DATA_AMPDU); > + else > + SIP_HDR_SET_TYPE(shdr->fc[0], SIP_DATA); > + > + if (!evif->epub) { > + sip_tx_status_report(sip, skb, itx_info, false); > + atomic_dec(&sip->tx_data_pkt_queued); > + return -EINVAL; > + } > + > + /* make room for encrypted pkt */ > + if (itx_info->control.hw_key) { > + alg = esp_cipher2alg(itx_info->control.hw_key->cipher); > + if (unlikely(alg == -1)) { > + sip_tx_status_report(sip, skb, itx_info, false); > + atomic_dec(&sip->tx_data_pkt_queued); > + return -1; > + } > + > + shdr->d_enc_flag = alg + 1; > + shdr->d_hw_kid = itx_info->control.hw_key->hw_key_idx | > + (evif->index << 7); > + } else { > + shdr->d_enc_flag = 0; > + shdr->d_hw_kid = evif->index << 7 | evif->index; > + } > + > + /* update sip tx info */ > + node = esp_get_node_by_addr(sip->epub, wh->addr1); > + if (node) > + sta_index = node->index; > + else > + sta_index = ESP_PUB_MAX_STA + 1; > + > + SIP_HDR_SET_IFIDX(shdr->fc[0], evif->index << 3 | sta_index); > + shdr->d_p2p = itx_info->control.vif->p2p; > + > + if (evif->index == 1) > + shdr->d_p2p = 1; > + > + shdr->d_ac = skb_get_queue_mapping(skb); > + shdr->d_tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; > + > + wh = (struct ieee80211_hdr *)skb->data; > + > + if (ieee80211_is_mgmt(wh->frame_control)) { > + /* addba/delba/bar may use different tid/ac */ > + if (shdr->d_ac == WME_AC_VO) > + shdr->d_tid = 7; > + > + if (ieee80211_is_beacon(wh->frame_control)) { > + shdr->d_tid = 8; > + shdr->d_ac = 4; > + } > + } > + > + if (check_ac_tid(skb->data, shdr->d_ac, shdr->d_tid)) { > + shdr->d_ac = WME_AC_BE; > + shdr->d_tid = 0; > + } > + > + /* make sure data is start at 4 bytes aligned addr. */ > + offset = roundup(sizeof(struct sip_hdr), 4); > + > + if (SIP_HDR_IS_AMPDU(shdr)) { > + memset(sip->tx_aggr_write_ptr + offset, 0, > + sizeof(struct esp_tx_ampdu_entry)); > + offset += roundup(sizeof(struct esp_tx_ampdu_entry), 4); > + } > + > + tx_len = offset + skb->len; > + shdr->len = tx_len; /* actual len */ > + } > + > + shdr->seq = sip->txseq++; > + > + /* copy skb to aggr buf */ > + memcpy(sip->tx_aggr_write_ptr + offset, skb->data, skb->len); > + > + if (is_data) { > + spin_lock_bh(&sip->epub->tx_lock); > + sip->txdataseq = shdr->seq; > + spin_unlock_bh(&sip->epub->tx_lock); > + > + /* fake a tx_status and report to mac80211 stack to speed up tx, may affect > + * 1) rate control (now it's all in target, so should be OK) > + * 2) ps mode, mac80211 want to check ACK of ps/nulldata to see if AP is awake > + * 3) BAR, mac80211 do BAR by checking ACK > + * > + * XXX: need to adjust for 11n, e.g. report tx_status according to BA received in target > + */ > + sip_tx_status_report(sip, skb, itx_info, true); > + atomic_dec(&sip->tx_data_pkt_queued); > + > + STRACE_TX_DATA_INC(); > + } else { > + /* check pm state here */ > + > + /* no need to hold ctrl skb */ > + sip_free_ctrl_skbuff(sip, skb); > + STRACE_TX_CMD_INC(); > + } > + > + /* TBD: roundup here or whole aggr-buf */ > + tx_len = roundup(tx_len, sip->tx_blksz); > + > + sip->tx_aggr_write_ptr += tx_len; > + sip->tx_tot_len += tx_len; > + > + return 0; > +} > + > +static void sip_tx_status_report(struct esp_sip *sip, struct sk_buff *skb, > + struct ieee80211_tx_info *tx_info, > + bool success) > +{ > + struct ieee80211_hdr *wh; > + struct esp_node *node; > + struct esp_tx_tid *tid; > + struct ieee80211_sta *sta; > + u8 tidno; > + > + if (!(tx_info->flags & IEEE80211_TX_CTL_AMPDU)) { > + if (likely(success)) > + tx_info->flags |= IEEE80211_TX_STAT_ACK; > + else > + tx_info->flags &= ~IEEE80211_TX_STAT_ACK; > + > + /* manipulate rate status... */ > + tx_info->status.rates[0].idx = 11; > + tx_info->status.rates[0].count = 1; > + tx_info->status.rates[0].flags = 0; > + tx_info->status.rates[1].idx = -1; > + } else { > + tx_info->flags |= IEEE80211_TX_STAT_AMPDU | > + IEEE80211_TX_STAT_ACK; > + tx_info->status.ampdu_len = 1; > + tx_info->status.ampdu_ack_len = 1; > + > + /* manipulate rate status... */ > + tx_info->status.rates[0].idx = 7; > + tx_info->status.rates[0].count = 1; > + tx_info->status.rates[0].flags = IEEE80211_TX_RC_MCS | > + IEEE80211_TX_RC_SHORT_GI; > + tx_info->status.rates[1].idx = -1; > + } > + > + if (!mod_support_no_txampdu() && > + cfg80211_get_chandef_type(&sip->epub->hw->conf.chandef) != NL80211_CHAN_NO_HT) { > + wh = (struct ieee80211_hdr *)skb->data; > + > + if (ieee80211_is_data_qos(wh->frame_control) && > + !(IEEE80211_SKB_CB(skb)->flags & IEEE80211_TX_CTL_AMPDU)) { > + tidno = ieee80211_get_qos_ctl(wh)[0] & > + IEEE80211_QOS_CTL_TID_MASK; > + > + node = esp_get_node_by_addr(sip->epub, wh->addr1); > + if (!node || !node->sta) > + goto _exit; > + > + sta = node->sta; > + tid = &node->tid[tidno]; > + if (!tid) { > + ESSERT(0); > + goto _exit; > + } > + > + spin_lock_bh(&sip->epub->tx_ampdu_lock); > + > + if (tid->state == ESP_TID_STATE_INIT && > + TID_TO_AC(tidno) != WME_AC_VO && tid->cnt >= 10) { > + tid->state = ESP_TID_STATE_TRIGGER; > + dev_dbg(sip->epub->dev, > + "start tx ba session,addr:%pM,tid:%u\n", > + wh->addr1, tidno); > + spin_unlock_bh(&sip->epub->tx_ampdu_lock); > + ieee80211_start_tx_ba_session(sta, tidno, 0); > + } else { > + if (tid->state == ESP_TID_STATE_INIT) > + tid->cnt++; > + else > + tid->cnt = 0; > + > + spin_unlock_bh(&sip->epub->tx_ampdu_lock); > + } > + } > + } > + > +_exit: > + ieee80211_tx_status(sip->epub->hw, skb); > +} > + > +/* NB: this routine should be locked when calling > + */ > +void sip_txq_process(struct esp_pub *epub) > +{ > + struct sk_buff *skb; > + struct sip_hdr *hdr; > + struct esp_sip *sip = epub->sip; > + struct ieee80211_tx_info *itx_info; > + u32 pkt_len, tx_len = 0; > + int blknum = 0, pm_state = 0; > + bool queued_back = false, out_of_credits = false; > + > + while ((skb = skb_dequeue(&epub->txq))) { > + /* cmd skb->len does not include sip_hdr too */ > + pkt_len = skb->len; > + itx_info = IEEE80211_SKB_CB(skb); > + if (itx_info->flags != 0xffffffff) { > + pkt_len += roundup(sizeof(struct sip_hdr), 4); > + if (itx_info->flags & IEEE80211_TX_CTL_AMPDU) > + pkt_len += roundup(sizeof(struct esp_tx_ampdu_entry), > + 4); > + } > + > + /* current design simply requires every sip_hdr must be at the begin of mblk, that definitely > + * need to be optimized, e.g. calculate remain length in the previous mblk, if it larger than > + * certain threshold (e.g, whole pkt or > 50% of pkt or 2 x sizeof(struct sip_hdr), append pkt > + * to the previous mblk. This might be done in sip_pack_pkt() > + */ > + pkt_len = roundup(pkt_len, sip->tx_blksz); > + blknum = pkt_len / sip->tx_blksz; > + > + /* > + * FIXME: Empirical delay. Without this delay, the connection to > + * a WiFi network crashes the kernel (sometimes at the second > + * connection). > + */ > + udelay(2000); > + > + if (unlikely(atomic_read(&sip->credit_status) == RECALC_CREDIT_ENABLE)) { /* need recalc credit */ > + hdr = (struct sip_hdr *)skb->data; > + itx_info = IEEE80211_SKB_CB(skb); > + > + if (!(itx_info->flags == 0xffffffff && > + SIP_HDR_GET_TYPE(hdr->fc[0]) == SIP_CTRL && > + hdr->c_cmdid == SIP_CMD_RECALC_CREDIT && > + blknum <= atomic_read(&sip->tx_credits) - sip->credit_to_reserve)) { /* except cmd recalc credit */ > + dev_dbg(epub->dev, "recalc credits!\n"); > + STRACE_TX_OUT_OF_CREDIT_INC(); > + queued_back = true; > + out_of_credits = true; > + break; > + } > + } else { > + if (unlikely(blknum > (atomic_read(&sip->tx_credits) - sip->credit_to_reserve - SIP_CTRL_CREDIT_RESERVE))) { > + itx_info = IEEE80211_SKB_CB(skb); > + if (itx_info->flags == 0xffffffff) { /* priv ctrl pkt */ > + if (blknum > atomic_read(&sip->tx_credits) - sip->credit_to_reserve) { > + dev_dbg(epub->dev, > + "%s cmd pkt out of credits!\n", > + __func__); > + STRACE_TX_OUT_OF_CREDIT_INC(); > + queued_back = true; > + out_of_credits = true; > + break; > + } > + } else { > + dev_dbg(epub->dev, > + "%s out of credits!\n", > + __func__); > + STRACE_TX_OUT_OF_CREDIT_INC(); > + queued_back = true; > + out_of_credits = true; > + break; > + } > + } > + } > + > + tx_len += pkt_len; > + if (tx_len >= SIP_TX_AGGR_BUF_SIZE) { > + /* do we need to have limitation likemax 8 pkts in a row? */ > + dev_dbg(epub->dev, "%s too much pkts in one shot!\n", > + __func__); > + STRACE_TX_ONE_SHOT_INC(); > + tx_len -= pkt_len; > + queued_back = true; > + break; > + } > + > + if (sip_pack_pkt(sip, skb, &pm_state) != 0) { > + /* wrong pkt, won't send to target */ > + tx_len -= pkt_len; > + continue; > + } > + > + atomic_sub(blknum, &sip->tx_credits); > + /* > + * FIXME: Empirical delay. Without this delay, the connection to > + * a WiFi network crashes the kernel (sometimes at the second > + * connection). > + */ > + udelay(2000); > + > + } > + > + if (queued_back) > + skb_queue_head(&epub->txq, skb); > + > + if (atomic_read(&sip->state) == SIP_STOP > +#ifdef HOST_RESET_BUG > + || atomic_read(&epub->wl.off) == 1 > +#endif > + ) { > + queued_back = 1; > + tx_len = 0; > + } > + > + if (tx_len) > + sip_write_pkts(sip, pm_state); > + > + if (queued_back && !out_of_credits) > + /* skb pending, do async process again */ > + sip_trigger_txq_process(sip); > +} > + > +#ifndef NO_WMM_DUMMY > +static struct esp_80211_wmm_param_element esp_wmm_param = { > + .oui = {0x00, 0x50, 0xf2}, > + .oui_type = 0x02, > + .oui_subtype = 0x01, > + .version = 0x01, > + .qos_info = 0x00, > + .reserved = 0x00, > + .ac = { > + { > + .aci_aifsn = 0x03, > + .cw = 0xa4, > + .txop_limit = 0x0000, > + }, > + { > + .aci_aifsn = 0x27, > + .cw = 0xa4, > + .txop_limit = 0x0000, > + }, > + { > + .aci_aifsn = 0x42, > + .cw = 0x43, > + .txop_limit = 0x005e, > + }, > + { > + .aci_aifsn = 0x62, > + .cw = 0x32, > + .txop_limit = 0x002f, > + }, > + }, > +}; > + > +static int esp_add_wmm(struct sk_buff *skb) > +{ > + u8 *p; > + int flag = 0; > + int remain_len; > + int base_len; > + int len; > + struct ieee80211_mgmt *mgmt; > + struct ieee80211_hdr *wh; > + > + if (!skb) > + return -1; > + > + wh = (struct ieee80211_hdr *)skb->data; > + mgmt = (struct ieee80211_mgmt *)((u8 *)skb->data); > + > + if (ieee80211_is_assoc_resp(wh->frame_control)) { > + p = mgmt->u.assoc_resp.variable; > + base_len = (u8 *)mgmt->u.assoc_resp.variable - (u8 *)mgmt; > + } else if (ieee80211_is_reassoc_resp(wh->frame_control)) { > + p = mgmt->u.reassoc_resp.variable; > + base_len = (u8 *)mgmt->u.reassoc_resp.variable - (u8 *)mgmt; > + } else if (ieee80211_is_probe_resp(wh->frame_control)) { > + p = mgmt->u.probe_resp.variable; > + base_len = (u8 *)mgmt->u.probe_resp.variable - (u8 *)mgmt; > + } else if (ieee80211_is_beacon(wh->frame_control)) { > + p = mgmt->u.beacon.variable; > + base_len = (u8 *)mgmt->u.beacon.variable - (u8 *)mgmt; > + } else { > + return 1; > + } > + > + remain_len = skb->len - base_len; > + > + while (remain_len > 0) { > + if (*p == 0xdd && *(p + 5) == 0x02) //wmm type > + return 0; > + else if (*p == 0x2d) //has ht cap > + flag = 1; > + > + len = *(++p); > + p += (len + 1); > + remain_len -= (len + 2); > + } > + > + if (remain_len < 0) > + return -2; -2 is not a valid error code. > + > + if (flag == 1) { > + skb_put(skb, 2 + sizeof(esp_wmm_param)); > + > + memset(p, 0xdd, sizeof(u8)); > + memset(p + 1, sizeof(esp_wmm_param), sizeof(u8)); > + memcpy(p + 2, &esp_wmm_param, sizeof(esp_wmm_param)); > + } > + > + return 0; > +} > +#endif /* NO_WMM_DUMMY */ > + > +/* parse mac_rx_ctrl and return length */ > +static int sip_parse_mac_rx_info(struct esp_sip *sip, > + struct esp_mac_rx_ctrl *mac_ctrl, > + struct sk_buff *skb) > +{ > + struct ieee80211_rx_status *rx_status; > + struct ieee80211_hdr *hdr; > + struct ieee80211_hdr *wh; > + > + rx_status = IEEE80211_SKB_RXCB(skb); > + rx_status->freq = esp_ieee2mhz(mac_ctrl->channel); > + rx_status->signal = mac_ctrl->rssi + mac_ctrl->noise_floor; /* snr actually, need to offset noise floor e.g. -85 */ > + > + hdr = (struct ieee80211_hdr *)skb->data; > + if (mac_ctrl->damatch0 == 1 && mac_ctrl->bssidmatch0 == 1 && /*match bssid and da, but beacon package contain other bssid */ > + !strncmp(hdr->addr2, sip->epub->wl.bssid, ETH_ALEN)) { /* force match addr2 */ > + if (++signal_loop >= SIGNAL_COUNT) { > + avg_signal += rx_status->signal; > + avg_signal /= SIGNAL_COUNT; > + rx_status->signal = avg_signal + 5; > + old_signal = rx_status->signal; > + signal_loop = 0; > + avg_signal = 0; > + } else { > + avg_signal += rx_status->signal; > + rx_status->signal = old_signal; > + } > + } > + > + rx_status->antenna = 0; /* one antenna for now */ > + rx_status->band = NL80211_BAND_2GHZ; > + rx_status->flag = RX_FLAG_DECRYPTED | RX_FLAG_MMIC_STRIPPED; > + if (mac_ctrl->sig_mode) { > + rx_status->encoding |= RX_ENC_HT; > + rx_status->rate_idx = mac_ctrl->MCS; > + if (mac_ctrl->SGI) > + rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; > + } else { > + rx_status->rate_idx = esp_wmac_rate2idx(mac_ctrl->rate); > + } > + > + if (mac_ctrl->rxend_state == RX_FCS_ERR) > + rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; > + > + /* Mic error frame flag */ > + if (mac_ctrl->rxend_state == RX_TKIPMIC_ERR || > + mac_ctrl->rxend_state == RX_CCMPMIC_ERR) { > + if (atomic_read(&sip->epub->wl.tkip_key_set) == 1) { > + rx_status->flag |= RX_FLAG_MMIC_ERROR; > + atomic_set(&sip->epub->wl.tkip_key_set, 0); > + printk("mic err\n"); > + } else { > + printk("mic err discard\n"); > + } > + } > + > + wh = (struct ieee80211_hdr *)((u8 *)skb->data); > + > +#ifndef NO_WMM_DUMMY > + if (ieee80211_is_mgmt(wh->frame_control)) > + esp_add_wmm(skb); > +#endif > + > + /* some kernel e.g. 3.0.8 wrongly handles non-encrypted pkt like eapol */ > + if (ieee80211_is_data(wh->frame_control)) { > + if (!ieee80211_has_protected(wh->frame_control)) > + rx_status->flag |= RX_FLAG_IV_STRIPPED; > + else if (!atomic_read(&sip->epub->wl.ptk_cnt)) > + rx_status->flag |= RX_FLAG_IV_STRIPPED; > + } > + > + return 0; > +} > + > +static struct esp_mac_rx_ctrl *sip_parse_normal_mac_ctrl(struct sk_buff *skb, > + int *pkt_len_enc, > + int *buf_len, > + int *pulled_len) > +{ > + struct sip_hdr *hdr = (struct sip_hdr *)skb->data; > + struct esp_mac_rx_ctrl *mac_ctrl; > + int len_in_hdr = hdr->len; > + > + ESSERT(skb); > + ESSERT(skb->len > SIP_MIN_DATA_PKT_LEN); > + > + skb_pull(skb, sizeof(struct sip_hdr)); > + *pulled_len += sizeof(struct sip_hdr); > + mac_ctrl = (struct esp_mac_rx_ctrl *)skb->data; > + if (!mac_ctrl->aggregation) { > + ESSERT(pkt_len_enc); > + ESSERT(buf_len); > + *pkt_len_enc = (mac_ctrl->sig_mode ? mac_ctrl->HT_length : mac_ctrl->legacy_length) - FCS_LEN; > + *buf_len = len_in_hdr - sizeof(struct sip_hdr) - > + sizeof(struct esp_mac_rx_ctrl); > + } > + > + skb_pull(skb, sizeof(struct esp_mac_rx_ctrl)); > + *pulled_len += sizeof(struct esp_mac_rx_ctrl); > + > + return mac_ctrl; > +} > + > +/* for one MPDU (including subframe in AMPDU) */ > +static struct sk_buff *sip_parse_data_rx_info(struct esp_sip *sip, > + struct sk_buff *skb, > + int pkt_len_enc, int buf_len, > + struct esp_mac_rx_ctrl *mac_ctrl, > + int *pulled_len) > +{ > + /* | mac_rx_ctrl | real_data_payload | ampdu_entries | */ > + struct sk_buff *rskb; > + struct ieee80211_hdr *wh; > + int pkt_len, ret; > + > + if (mac_ctrl->aggregation) { > + wh = (struct ieee80211_hdr *)skb->data; > + pkt_len = pkt_len_enc; > + if (ieee80211_has_protected(wh->frame_control)) //ampdu, it is CCMP enc > + pkt_len -= 8; > + > + buf_len = roundup(pkt_len, 4); > + } else { > + pkt_len = buf_len - 3 + ((pkt_len_enc - 1) & 0x3); > + } > + > +#ifndef NO_WMM_DUMMY > + rskb = __dev_alloc_skb(pkt_len_enc + sizeof(esp_wmm_param) + 2, > + GFP_ATOMIC); > +#else > + rskb = __dev_alloc_skb(pkt_len_enc, GFP_ATOMIC); > +#endif /* NO_WMM_DUMMY */ > + if (unlikely(!rskb)) { Remove all likely/unlikely. > + dev_err(sip->epub->dev, "no mem for rskb\n"); No need for this printk. > + return NULL; > + } > + > + skb_put(rskb, pkt_len_enc); > + > + memcpy(rskb->data, skb->data, pkt_len); > + > + if (pkt_len_enc > pkt_len) > + memset(rskb->data + pkt_len, 0, pkt_len_enc - pkt_len); > + > + /* strip out current pkt, move to the next one */ > + skb_pull(skb, buf_len); > + *pulled_len += buf_len; > + > + ret = sip_parse_mac_rx_info(sip, mac_ctrl, rskb); > + if (ret == -1 && !mac_ctrl->aggregation) { sip_parse_mac_rx_info() never returns -1. Also -1 is not a the correct error code. > + kfree_skb(rskb); > + return NULL; > + } > + > + return rskb; > +} > + > +struct esp_sip *sip_attach(struct esp_pub *epub) > +{ > + struct esp_sip *sip; > + struct sip_pkt *pkt; > + int i, po; > + > + sip = kzalloc(sizeof(*sip), GFP_KERNEL); > + if (!sip) { > + dev_dbg(epub->dev, "no mem for sip!\n"); > + goto _err_sip; No need for the error message or the goto. > + } > + > + /* Finding the smalest available space to allocate this variavle */ > + po = get_order(SIP_TX_AGGR_BUF_SIZE); > + > + sip->tx_aggr_buf = (u8 *)__get_free_pages(GFP_ATOMIC, po); > + if (!sip->tx_aggr_buf) { > + dev_err(epub->dev, "no mem for tx_aggr_buf!\n"); > + goto _err_aggr; > + } > + > + spin_lock_init(&sip->lock); > + > + INIT_LIST_HEAD(&sip->free_ctrl_txbuf); > + INIT_LIST_HEAD(&sip->free_ctrl_rxbuf); > + > + for (i = 0; i < SIP_CTRL_BUF_N; i++) { > + pkt = kzalloc(sizeof(*pkt), GFP_KERNEL); > + if (!pkt) > + goto _err_pkt; > + > + pkt->buf_begin = kzalloc(SIP_CTRL_BUF_SZ, GFP_KERNEL); > + if (!pkt->buf_begin) { > + kfree(pkt); > + pkt = NULL; No need to set "pkt" to NULL > + goto _err_pkt; > + } > + > + pkt->buf_len = SIP_CTRL_BUF_SZ; > + pkt->buf = pkt->buf_begin; > + > + if (i < SIP_CTRL_TXBUF_N) > + list_add_tail(&pkt->list, &sip->free_ctrl_txbuf); > + else > + list_add_tail(&pkt->list, &sip->free_ctrl_rxbuf); > + } > + > + mutex_init(&sip->rx_mtx); > + skb_queue_head_init(&sip->rxq); > + INIT_WORK(&sip->rx_process_work, sip_rxq_process); > + > + sip->epub = epub; > + atomic_set(&sip->noise_floor, -96); > + > + atomic_set(&sip->state, SIP_INIT); > + atomic_set(&sip->tx_credits, 0); > + > + if (!sip->rawbuf) { > + sip->rawbuf = kzalloc(SIP_BOOT_BUF_SIZE, GFP_KERNEL); > + if (!sip->rawbuf) { > + dev_err(epub->dev, "no mem for rawbuf!\n"); > + goto _err_pkt; > + } > + } > + > + atomic_set(&sip->state, SIP_PREPARE_BOOT); > + > + return sip; > + > +_err_pkt: > + sip_free_init_ctrl_buf(sip); > + > + if (sip->tx_aggr_buf) { No need for this test. We know it's non-NULL. > + po = get_order(SIP_TX_AGGR_BUF_SIZE); po is already set. No need to calculate it again. > + free_pages((unsigned long)sip->tx_aggr_buf, po); > + sip->tx_aggr_buf = NULL; No need to set this to NULL. We're just going to free "sip" on the next line. > + } > + > +_err_aggr: > + kfree(sip); > + sip = NULL; No need to set this to NULL > + > +_err_sip: > + return NULL; > +} > + > +static void sip_free_init_ctrl_buf(struct esp_sip *sip) > +{ > + struct sip_pkt *pkt, *tpkt; > + > + list_for_each_entry_safe(pkt, tpkt, &sip->free_ctrl_txbuf, list) { > + list_del(&pkt->list); > + kfree(pkt->buf_begin); > + kfree(pkt); > + } > + > + list_for_each_entry_safe(pkt, tpkt, &sip->free_ctrl_rxbuf, list) { > + list_del(&pkt->list); > + kfree(pkt->buf_begin); > + kfree(pkt); > + } > +} > + > +void sip_detach(struct esp_sip *sip) > +{ > + if (!sip) > + return; > + > + sip_free_init_ctrl_buf(sip); > + > + if (atomic_read(&sip->state) == SIP_RUN) { > + sif_disable_target_interrupt(sip->epub); > + > + atomic_set(&sip->state, SIP_STOP); > + > + /* disable irq here */ > + sif_disable_irq(sip->epub); > + cancel_work_sync(&sip->rx_process_work); > + > + skb_queue_purge(&sip->rxq); > + mutex_destroy(&sip->rx_mtx); > + cancel_work_sync(&sip->epub->sendup_work); > + skb_queue_purge(&sip->epub->rxq); > + > + if (test_and_clear_bit(ESP_WL_FLAG_HW_REGISTERED, > + &sip->epub->wl.flags)) > + ieee80211_unregister_hw(sip->epub->hw); > + > + /* cancel all worker/timer */ > + cancel_work_sync(&sip->epub->tx_work); > + skb_queue_purge(&sip->epub->txq); > + skb_queue_purge(&sip->epub->txdoneq); > + > + free_pages((unsigned long)sip->tx_aggr_buf, > + get_order(SIP_TX_AGGR_BUF_SIZE)); > + sip->tx_aggr_buf = NULL; > + > + atomic_set(&sip->state, SIP_INIT); > + } else if (atomic_read(&sip->state) >= SIP_BOOT && > + atomic_read(&sip->state) <= SIP_WAIT_BOOTUP) { > + sif_disable_target_interrupt(sip->epub); > + atomic_set(&sip->state, SIP_STOP); > + sif_disable_irq(sip->epub); > + kfree(sip->rawbuf); > + > + if (atomic_read(&sip->state) == SIP_SEND_INIT) { > + cancel_work_sync(&sip->rx_process_work); > + skb_queue_purge(&sip->rxq); > + mutex_destroy(&sip->rx_mtx); > + cancel_work_sync(&sip->epub->sendup_work); > + skb_queue_purge(&sip->epub->rxq); > + } > + > + if (test_and_clear_bit(ESP_WL_FLAG_HW_REGISTERED, > + &sip->epub->wl.flags)) > + ieee80211_unregister_hw(sip->epub->hw); > + atomic_set(&sip->state, SIP_INIT); > + } else { > + dev_err(sip->epub->dev, "wrong state (%d) for detaching sip\n", > + atomic_read(&sip->state)); > + } > + > + kfree(sip); > +} > + > +int sip_write_memory(struct esp_sip *sip, u32 addr, u8 *buf, u16 len) > +{ > + struct sip_cmd_write_memory *cmd; > + struct sip_hdr *chdr; > + u16 remains, hdrs, bufsize; > + u32 loadaddr; > + u8 *src; > + int err = 0; No need to initialize "err". > + u32 *t; > + > + if (!sip || !sip->rawbuf) { > + ESSERT(sip); > + ESSERT(sip->rawbuf); > + return -EINVAL; > + } > + > + memset(sip->rawbuf, 0, SIP_BOOT_BUF_SIZE); > + > + chdr = (struct sip_hdr *)sip->rawbuf; > + SIP_HDR_SET_TYPE(chdr->fc[0], SIP_CTRL); > + chdr->c_cmdid = SIP_CMD_WRITE_MEMORY; > + > + remains = len; > + hdrs = sizeof(struct sip_hdr) + sizeof(struct sip_cmd_write_memory); > + > + while (remains) { > + src = &buf[len - remains]; > + loadaddr = addr + (len - remains); > + > + if (remains < (SIP_BOOT_BUF_SIZE - hdrs)) { > + /* aligned with 4 bytes */ > + bufsize = roundup(remains, 4); > + memset(sip->rawbuf + hdrs, 0, bufsize); > + remains = 0; > + } else { > + bufsize = SIP_BOOT_BUF_SIZE - hdrs; > + remains -= bufsize; > + } > + > + chdr->len = bufsize + hdrs; > + chdr->seq = sip->txseq++; > + cmd = (struct sip_cmd_write_memory *)(sip->rawbuf + SIP_CTRL_HDR_LEN); > + cmd->len = bufsize; > + cmd->addr = loadaddr; > + memcpy(sip->rawbuf + hdrs, src, bufsize); > + > + t = (u32 *)sip->rawbuf; > + err = esp_common_write(sip->epub, sip->rawbuf, chdr->len, > + ESP_SIF_SYNC); > + if (err) { > + dev_err(sip->epub->dev, "send buffer failed\n"); > + return err; > + } > + // 1ms is enough, in fact on dell-d430, need not delay at all. > + mdelay(1); > + } > + > + return err; return 0; > +} > + > +int sip_send_cmd(struct esp_sip *sip, int cid, u32 cmdlen, void *cmd) > +{ > + struct sip_hdr *chdr; > + struct sip_pkt *pkt; > + int ret; > + > + pkt = sip_get_ctrl_buf(sip, SIP_TX_CTRL_BUF); > + if (!pkt) > + return -ENOMEM; > + > + chdr = (struct sip_hdr *)pkt->buf_begin; > + chdr->len = SIP_CTRL_HDR_LEN + cmdlen; > + chdr->seq = sip->txseq++; > + chdr->c_cmdid = cid; > + > + if (cmd) { > + memset(pkt->buf, 0, cmdlen); No need for this memset() because we memcpy() over it on the next line. > + memcpy(pkt->buf, (u8 *)cmd, cmdlen); > + } > + > + ret = esp_common_write(sip->epub, pkt->buf_begin, chdr->len, > + ESP_SIF_SYNC); > + if (ret) > + dev_err(sip->epub->dev, "send cmd %d failed\n", cid); > + > + sip_reclaim_ctrl_buf(sip, pkt, SIP_TX_CTRL_BUF); > + > + /* Hack here: reset tx/rx seq before target ram code is up... */ > + if (cid == SIP_CMD_BOOTUP) { > + sip->rxseq = 0; > + sip->txseq = 0; > + sip->txdataseq = 0; > + } > + > + return ret; > +} > + > +struct sk_buff *sip_alloc_ctrl_skbuf(struct esp_sip *sip, u16 len, u32 cid) > +{ > + struct sip_hdr *si; > + struct ieee80211_tx_info *ti; > + struct sk_buff *skb; > + > + ESSERT(len <= sip->tx_blksz); > + > + /* no need to reserve space for net stack */ > + skb = __dev_alloc_skb(len, GFP_KERNEL); > + if (!skb) { > + dev_err(sip->epub->dev, "no skb for ctrl!\n"); > + return NULL; > + } > + > + skb->len = len; > + > + ti = IEEE80211_SKB_CB(skb); > + /* set tx_info flags to 0xffffffff to indicate sip_ctrl pkt */ > + ti->flags = 0xffffffff; > + > + si = (struct sip_hdr *)skb->data; > + memset(si, 0, sizeof(struct sip_hdr)); > + SIP_HDR_SET_TYPE(si->fc[0], SIP_CTRL); > + si->len = len; > + si->c_cmdid = cid; > + > + return skb; > +} > + > +void sip_free_ctrl_skbuff(struct esp_sip *sip, struct sk_buff *skb) > +{ > + memset(IEEE80211_SKB_CB(skb), 0, sizeof(struct ieee80211_tx_info)); > + kfree_skb(skb); > +} > + > +static struct sip_pkt *sip_get_ctrl_buf(struct esp_sip *sip, > + enum sip_buf_type bftype) > +{ > + struct sip_pkt *pkt; > + struct list_head *bflist; > + struct sip_hdr *chdr; > + > + /* FIXME: Why taking spinlock to check list_empty? */ > + bflist = (bftype == SIP_TX_CTRL_BUF) ? &sip->free_ctrl_txbuf : &sip->free_ctrl_rxbuf; > + > + spin_lock_bh(&sip->lock); > + > + if (list_empty(bflist)) { > + spin_unlock_bh(&sip->lock); > + return NULL; > + } > + > + pkt = list_first_entry(bflist, struct sip_pkt, list); > + list_del(&pkt->list); > + spin_unlock_bh(&sip->lock); > + > + if (bftype == SIP_TX_CTRL_BUF) { > + chdr = (struct sip_hdr *)pkt->buf_begin; > + SIP_HDR_SET_TYPE(chdr->fc[0], SIP_CTRL); > + pkt->buf = pkt->buf_begin + SIP_CTRL_HDR_LEN; > + } else { > + pkt->buf = pkt->buf_begin; > + } > + > + return pkt; > +} > + > +static void sip_reclaim_ctrl_buf(struct esp_sip *sip, struct sip_pkt *pkt, > + enum sip_buf_type bftype) > +{ > + struct list_head *bflist; > + > + if (bftype == SIP_TX_CTRL_BUF) > + bflist = &sip->free_ctrl_txbuf; > + else if (bftype == SIP_RX_CTRL_BUF) > + bflist = &sip->free_ctrl_rxbuf; > + else > + return; > + > + pkt->buf = pkt->buf_begin; > + > + spin_lock_bh(&sip->lock); > + list_add_tail(&pkt->list, bflist); > + spin_unlock_bh(&sip->lock); > +} > + > +int sip_poll_bootup_event(struct esp_sip *sip) > +{ > + int ret = 0; > + > + if (gl_bootup_cplx) > + ret = wait_for_completion_timeout(gl_bootup_cplx, 2 * HZ); > + > + if (!ret) { > + dev_err(sip->epub->dev, "bootup event timeout\n"); > + return -ETIMEDOUT; > + } > + > + if (!sif_get_ate_config()) > + ret = esp_register_mac80211(sip->epub); > + > + atomic_set(&sip->state, SIP_RUN); > + > + return ret; > +} > + > +/* FIXME: always returning 0 ? */ > +int sip_poll_resetting_event(struct esp_sip *sip) > +{ > + unsigned int ret; > + > + if (gl_bootup_cplx) > + ret = wait_for_completion_timeout(gl_bootup_cplx, 10 * HZ); > + > + if (!ret) { > + dev_err(sip->epub->dev, "resetting event timeout\n"); > + return -ETIMEDOUT; > + } > + > + return 0; > +} > + > +bool sip_queue_need_stop(struct esp_sip *sip) > +{ > + return atomic_read(&sip->tx_data_pkt_queued) >= SIP_STOP_QUEUE_THRESHOLD || > + (atomic_read(&sip->tx_credits) < 8 && > + atomic_read(&sip->tx_data_pkt_queued) >= SIP_STOP_QUEUE_THRESHOLD / 4 * 3); > +} > + > +bool sip_queue_may_resume(struct esp_sip *sip) > +{ > + return atomic_read(&sip->epub->txq_stopped) && > + !test_bit(ESP_WL_FLAG_STOP_TXQ, &sip->epub->wl.flags) && > + ((atomic_read(&sip->tx_credits) >= 16 && > + atomic_read(&sip->tx_data_pkt_queued) < SIP_RESUME_QUEUE_THRESHOLD * 2) || > + atomic_read(&sip->tx_data_pkt_queued) < SIP_RESUME_QUEUE_THRESHOLD); > +} > + > +int sip_cmd_enqueue(struct esp_sip *sip, struct sk_buff *skb, int prior) > +{ > + if (!sip || !sip->epub || !skb) { > + return -EINVAL; > + } > + > + if (prior == ENQUEUE_PRIOR_HEAD) > + skb_queue_head(&sip->epub->txq, skb); > + else > + skb_queue_tail(&sip->epub->txq, skb); > + > + if (!sif_get_ate_config()) > + ieee80211_queue_work(sip->epub->hw, &sip->epub->tx_work); > + else > + queue_work(sip->epub->esp_wkq, &sip->epub->tx_work); > + > + return 0; > +} > + > +void sip_tx_data_pkt_enqueue(struct esp_pub *epub, struct sk_buff *skb) > +{ > + if (!epub || !epub->sip || !skb) > + return; > + > + skb_queue_tail(&epub->txq, skb); > + atomic_inc(&epub->sip->tx_data_pkt_queued); > + > + if (sip_queue_need_stop(epub->sip)) > + if (epub->hw) { > + ieee80211_stop_queues(epub->hw); > + atomic_set(&epub->txq_stopped, true); > + } Multi-line indents get curly braces for readability. > +} > diff --git a/drivers/staging/esp8089/esp_sip.h b/drivers/staging/esp8089/esp_sip.h > new file mode 100644 > index 000000000000..fe58aae4ba54 > --- /dev/null > +++ b/drivers/staging/esp8089/esp_sip.h > @@ -0,0 +1,150 @@ > +/* > + * Copyright (c) 2009 - 2014 Espressif System. > + * Copyright (c) 2017 Free Electrons > + * Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it under > + * the terms of the GNU General Public License version 2 as published by the > + * Free Software Foundation. > + * > + */ > + > +#ifndef _ESP_SIP_H > +#define _ESP_SIP_H > + > +#include "sip2_common.h" > + > +#define SIP_CTRL_CREDIT_RESERVE 2 > + > +#define SIP_PKT_MAX_LEN (1024 * 16) > + > +/* 16KB on normal X86 system, should check before porting to orhters */ > + > +#define SIP_TX_AGGR_BUF_SIZE (4 * PAGE_SIZE) > +#define SIP_RX_AGGR_BUF_SIZE (4 * PAGE_SIZE) > + > +struct sk_buff; > + > +struct sip_pkt { > + struct list_head list; > + u8 *buf_begin; > + u32 buf_len; > + u8 *buf; > +}; > + > +enum RECALC_CREDIT_STATE { > + RECALC_CREDIT_DISABLE = 0, > + RECALC_CREDIT_ENABLE = 1, > +}; > + > +enum ENQUEUE_PRIOR { > + ENQUEUE_PRIOR_TAIL = 0, > + ENQUEUE_PRIOR_HEAD, > +}; > + > +enum SIP_STATE { > + SIP_INIT = 0, > + SIP_PREPARE_BOOT, > + SIP_BOOT, > + SIP_SEND_INIT, > + SIP_WAIT_BOOTUP, > + SIP_RUN, > + SIP_SUSPEND, > + SIP_STOP > +}; > + > +enum sip_notifier { > + SIP_TX_DONE = 1, > + SIP_RX_DONE = 2, > +}; > + > +#define SIP_CREDITS_LOW_THRESHOLD 64 //i.e. 4k > + > +struct esp_sip { > + struct list_head free_ctrl_txbuf; > + struct list_head free_ctrl_rxbuf; > + > + u32 rxseq; /* sip pkt seq, should match target side */ > + u32 txseq; > + u32 txdataseq; > + > + u8 to_host_seq; > + > + atomic_t state; > + spinlock_t lock; > + atomic_t tx_credits; > + > + atomic_t tx_ask_credit_update; > + > + u8 *rawbuf; /* used in boot stage, free once chip is fully up */ > + u8 *tx_aggr_buf; > + u8 *tx_aggr_write_ptr; /* update after insertion of each pkt */ > + u8 *tx_aggr_lastpkt_ptr; > + > + struct mutex rx_mtx; > + struct sk_buff_head rxq; > + struct work_struct rx_process_work; > + > + u16 tx_blksz; > + u16 rx_blksz; > + > + bool dump_rpbm_err; > + bool sendup_rpbm_pkt; > + bool rxabort_fixed; > + bool support_bgscan; > + u8 credit_to_reserve; > + > + atomic_t credit_status; > + struct timer_list credit_timer; > + > + atomic_t noise_floor; > + > + u32 tx_tot_len; /* total len for one transaction */ > + u32 rx_tot_len; > + > + atomic_t rx_handling; > + atomic_t tx_data_pkt_queued; > + > + atomic_t data_tx_stopped; > + atomic_t tx_stopped; > + > + struct esp_pub *epub; > +}; > + > +int sip_rx(struct esp_pub *epub); > + > +int sip_write_memory(struct esp_sip *sip, u32 addr, u8 *buf, u16 len); > + > +int sip_send_cmd(struct esp_sip *sip, int cid, u32 cmdlen, void *cmd); > + > +struct esp_sip *sip_attach(struct esp_pub *epub); > + > +int sip_post_init(struct esp_sip *sip, struct sip_evt_bootup2 *bevt); > + > +void sip_detach(struct esp_sip *sip); > + > +void sip_txq_process(struct esp_pub *epub); > + > +struct sk_buff *sip_alloc_ctrl_skbuf(struct esp_sip *sip, u16 len, u32 cid); > + > +void sip_free_ctrl_skbuff(struct esp_sip *sip, struct sk_buff *skb); > + > +bool sip_queue_need_stop(struct esp_sip *sip); > +bool sip_queue_may_resume(struct esp_sip *sip); > + > +void sip_tx_data_pkt_enqueue(struct esp_pub *epub, struct sk_buff *skb); > + > +int sip_cmd_enqueue(struct esp_sip *sip, struct sk_buff *skb, int prior); > + > +int sip_poll_bootup_event(struct esp_sip *sip); > + > +int sip_poll_resetting_event(struct esp_sip *sip); > + > +void sip_trigger_txq_process(struct esp_sip *sip); > + > +void sip_send_chip_init(struct esp_sip *sip); > + > +bool mod_support_no_txampdu(void); > + > +bool mod_support_no_rxampdu(void); > +#endif > diff --git a/drivers/staging/esp8089/esp_utils.c b/drivers/staging/esp8089/esp_utils.c > new file mode 100644 > index 000000000000..b35428d70c91 > --- /dev/null > +++ b/drivers/staging/esp8089/esp_utils.c > @@ -0,0 +1,133 @@ > +/* > + * Copyright (c) 2009 - 2014 Espressif System. > + * Copyright (c) 2017 Free Electrons > + * Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it under > + * the terms of the GNU General Public License version 2 as published by the > + * Free Software Foundation. > + * > + */ > + > +#include "linux/types.h" > +#include "linux/kernel.h" > +#include <linux/ieee80211.h> > +#include <net/mac80211.h> > +#include <linux/skbuff.h> > + > +#include <net/tcp.h> > +#include <linux/ip.h> > +#include <asm/checksum.h> > + > +#include "esp_pub.h" > +#include "esp_utils.h" > +#include "esp_wmac.h" > +#include "esp_debug.h" > + > +/* Convert IEEE channel number to MHz frequency. */ > +u32 esp_ieee2mhz(u8 chan) > +{ > + if (chan == 14) > + return 2484; > + > + if (chan < 14) > + return 2407 + chan * 5; > + > + return 2512 + ((chan - 15) * 20); > + //TODO, add 5GHz > +} > + > +enum { > + ESP_RATE_1_LONG = 0x0, > + ESP_RATE_2_LONG = 0x1, > + ESP_RATE_2_SHORT = 0x5, > + ESP_RATE_5_SHORT = 0x6, > + ESP_RATE_5_LONG = 0x2, > + ESP_RATE_11_SHORT = 0x7, > + ESP_RATE_11_LONG = 0x3, > + ESP_RATE_6 = 0xb, > + ESP_RATE_9 = 0xf, > + ESP_RATE_12 = 0xa, > + ESP_RATE_18 = 0xe, > + ESP_RATE_24 = 0x9, > + ESP_RATE_36 = 0xd, > + ESP_RATE_48 = 0x8, > + ESP_RATE_54 = 0xc, > + /* ESP_RATE_MCS0 =0x10, > + * ESP_RATE_MCS1 =0x11, > + * ESP_RATE_MCS2 =0x12, > + * ESP_RATE_MCS3 =0x13, > + * ESP_RATE_MCS4 =0x14, > + * ESP_RATE_MCS5 =0x15, > + * ESP_RATE_MCS6 =0x16, > + * ESP_RATE_MCS7 =0x17, > + */ > +}; > + > +static u8 esp_rate_table[20] = { > + ESP_RATE_1_LONG, > + ESP_RATE_2_SHORT, > + ESP_RATE_5_SHORT, > + ESP_RATE_11_SHORT, > + ESP_RATE_6, > + ESP_RATE_9, > + ESP_RATE_12, > + ESP_RATE_18, > + ESP_RATE_24, > + ESP_RATE_36, > + ESP_RATE_48, > + ESP_RATE_54, > + /* ESP_RATE_MCS0, > + * ESP_RATE_MCS1, > + * ESP_RATE_MCS2, > + * ESP_RATE_MCS3, > + * ESP_RATE_MCS4, > + * ESP_RATE_MCS5, > + * ESP_RATE_MCS6, > + * ESP_RATE_MCS7, > + */ > +}; > + > +s8 esp_wmac_rate2idx(u8 rate) Just make it return int or maybe u8. > +{ > + int i; > + > + if (rate == ESP_RATE_2_LONG) > + return 1; > + > + if (rate == ESP_RATE_5_LONG) > + return 2; > + > + if (rate == ESP_RATE_11_LONG) > + return 3; > + > + for (i = 0; i < 20; i++) > + if (rate == esp_rate_table[i]) > + return i; > + > + return 0; > +} > + > +bool esp_wmac_rxsec_error(u8 error) > +{ > + return (error >= RX_SECOV_ERR && error <= RX_SECFIFO_TIMEOUT) || > + (error >= RX_WEPICV_ERR && error <= RX_WAPIMIC_ERR); > +} > + > +int esp_cipher2alg(int cipher) > +{ > + if (cipher == WLAN_CIPHER_SUITE_TKIP) > + return ALG_TKIP; > + > + if (cipher == WLAN_CIPHER_SUITE_CCMP) > + return ALG_CCMP; > + > + if (cipher == WLAN_CIPHER_SUITE_WEP40 || > + cipher == WLAN_CIPHER_SUITE_WEP104) > + return ALG_WEP; > + > + if (cipher == WLAN_CIPHER_SUITE_AES_CMAC) > + return ALG_AES_CMAC; > + > + return -1; > +} > diff --git a/drivers/staging/esp8089/esp_utils.h b/drivers/staging/esp8089/esp_utils.h > new file mode 100644 > index 000000000000..3a4d461a4e70 > --- /dev/null > +++ b/drivers/staging/esp8089/esp_utils.h > @@ -0,0 +1,27 @@ > +/* > + * Copyright (c) 2011 - 2012 Espressif System. > + * Copyright (c) 2017 Free Electrons > + * Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it under > + * the terms of the GNU General Public License version 2 as published by the > + * Free Software Foundation. > + * > + */ > + > +#ifndef _ESP_UTILS_H_ > +#define _ESP_UTILS_H_ > + > +#include "linux/types.h" > + > +u32 esp_ieee2mhz(u8 chan); > + > +enum ieee80211_key_alg { > + ALG_WEP, > + ALG_TKIP, > + ALG_CCMP, > + ALG_AES_CMAC > +}; > + > +int esp_cipher2alg(int cipher); > +#endif > diff --git a/drivers/staging/esp8089/esp_wl.h b/drivers/staging/esp8089/esp_wl.h > new file mode 100644 > index 000000000000..9eb1f6421947 > --- /dev/null > +++ b/drivers/staging/esp8089/esp_wl.h > @@ -0,0 +1,35 @@ > +/* > + * Copyright (c) 2009 - 2014 Espressif System. > + * Copyright (c) 2017 Free Electrons > + * Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it under > + * the terms of the GNU General Public License version 2 as published by the > + * Free Software Foundation. > + * > + */ > + > +#ifndef _ESP_WL_H_ > +#define _ESP_WL_H_ > + > +//#define MAX_PROBED_SSID_INDEX 9 > + > +enum { > + CONF_HW_BIT_RATE_1MBPS = BIT(0), > + CONF_HW_BIT_RATE_2MBPS = BIT(1), > + CONF_HW_BIT_RATE_5_5MBPS = BIT(2), > + CONF_HW_BIT_RATE_11MBPS = BIT(3), > + CONF_HW_BIT_RATE_6MBPS = BIT(4), > + CONF_HW_BIT_RATE_9MBPS = BIT(5), > + CONF_HW_BIT_RATE_12MBPS = BIT(6), > + CONF_HW_BIT_RATE_18MBPS = BIT(7), > + CONF_HW_BIT_RATE_22MBPS = BIT(8), > + CONF_HW_BIT_RATE_24MBPS = BIT(9), > + CONF_HW_BIT_RATE_36MBPS = BIT(10), > + CONF_HW_BIT_RATE_48MBPS = BIT(11), > + CONF_HW_BIT_RATE_54MBPS = BIT(12), > +}; > + > +#define CONF_HW_BIT_RATE_11B_MASK (CONF_HW_BIT_RATE_1MBPS | CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | CONF_HW_BIT_RATE_11MBPS) > + > +#endif /* _ESP_WL_H_ */ > diff --git a/drivers/staging/esp8089/esp_wmac.h b/drivers/staging/esp8089/esp_wmac.h > new file mode 100644 > index 000000000000..67ea8e147c43 > --- /dev/null > +++ b/drivers/staging/esp8089/esp_wmac.h > @@ -0,0 +1,87 @@ > +/* > + * Copyright (c) 2011 - 2012 Espressif System. > + * Copyright (c) 2017 Free Electrons > + * Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it under > + * the terms of the GNU General Public License version 2 as published by the > + * Free Software Foundation. > + * > + */ > + > +#ifndef _ESP_WMAC_H_ > +#define _ESP_WMAC_H_ > + > +struct esp_mac_rx_ctrl { > + signed rssi:8; > + unsigned rate:4; > + unsigned is_group:1; > + unsigned:1; > + unsigned sig_mode:2; > + unsigned legacy_length:12; > + unsigned damatch0:1; > + unsigned damatch1:1; > + unsigned bssidmatch0:1; > + unsigned bssidmatch1:1; > + unsigned MCS:7; > + unsigned CWB:1; > + unsigned HT_length:16; > + unsigned smoothing:1; > + unsigned not_sounding:1; > + unsigned:1; > + unsigned aggregation:1; > + unsigned STBC:2; > + unsigned FEC_CODING:1; > + unsigned SGI:1; > + unsigned rxend_state:8; > + unsigned ampdu_cnt:8; > + unsigned channel:4; > + unsigned:4; > + signed noise_floor:8; > +}; > + > +struct esp_rx_ampdu_len { > + unsigned substate:8; > + unsigned sublen:12; > + unsigned:12; > +}; > + > +struct esp_tx_ampdu_entry { > + u32 sub_len:12, dili_num:7, :1, null_byte:2, data:1, enc:1, seq:8; > +}; > + > +//rxend_state flags > +#define RX_PYH_ERR_MIN 0x42 > +#define RX_AGC_ERR_MIN 0x42 > +#define RX_AGC_ERR_MAX 0x47 > +#define RX_OFDM_ERR_MIN 0x50 > +#define RX_OFDM_ERR_MAX 0x58 > +#define RX_CCK_ERR_MIN 0x59 > +#define RX_CCK_ERR_MAX 0x5F > +#define RX_ABORT 0x80 > +#define RX_SF_ERR 0x40 > +#define RX_FCS_ERR 0x41 > +#define RX_AHBOV_ERR 0xC0 > +#define RX_BUFOV_ERR 0xC1 > +#define RX_BUFINV_ERR 0xC2 > +#define RX_AMPDUSF_ERR 0xC3 > +#define RX_AMPDUBUFOV_ERR 0xC4 > +#define RX_MACBBFIFOOV_ERR 0xC5 > +#define RX_RPBM_ERR 0xC6 > +#define RX_BTFORCE_ERR 0xC7 > +#define RX_SECOV_ERR 0xE1 > +#define RX_SECPROT_ERR0 0xE2 > +#define RX_SECPROT_ERR1 0xE3 > +#define RX_SECKEY_ERR 0xE4 > +#define RX_SECCRLEN_ERR 0xE5 > +#define RX_SECFIFO_TIMEOUT 0xE6 > +#define RX_WEPICV_ERR 0xF0 > +#define RX_TKIPICV_ERR 0xF4 > +#define RX_TKIPMIC_ERR 0xF5 > +#define RX_CCMPMIC_ERR 0xF8 > +#define RX_WAPIMIC_ERR 0xFC > + > +s8 esp_wmac_rate2idx(u8 rate); > +bool esp_wmac_rxsec_error(u8 error); > + > +#endif /* _ESP_WMAC_H_ */ > diff --git a/drivers/staging/esp8089/sdio_sif_esp.c b/drivers/staging/esp8089/sdio_sif_esp.c > new file mode 100644 > index 000000000000..d8842f5085ce > --- /dev/null > +++ b/drivers/staging/esp8089/sdio_sif_esp.c > @@ -0,0 +1,552 @@ > +/* > + * Copyright (c) 2010 - 2013 Espressif System. > + * Copyright (c) 2017 Free Electrons > + * Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it under > + * the terms of the GNU General Public License version 2 as published by the > + * Free Software Foundation. > + * > + */ > + > +#include <linux/mmc/card.h> > +#include <linux/mmc/mmc.h> > +#include <linux/mmc/core.h> > +#include <linux/mmc/host.h> > +#include <linux/mmc/sdio_func.h> > +#include <linux/mmc/sdio_ids.h> > +#include <linux/mmc/sdio.h> > +#include <linux/mmc/sd.h> > +#include <linux/module.h> > +#include <net/mac80211.h> > +#include <linux/time.h> > +#include <linux/pm.h> > + > +#include "esp_pub.h" > +#include "esp_sif.h" > +#include "esp_sip.h" > +#include "esp_debug.h" > +#include "slc_host_register.h" > +#include "esp_version.h" > +#include "esp_ctrl.h" > +#include "esp_file.h" > + > +unsigned int esp_msg_level = ESP_DBG_ERROR | ESP_SHOW; > + > +/* HdG: Note: > + * 1) MMC_HAS_FORCE_DETECT_CHANGE is a hack which is set by my sunxi-wip > + * tree. FIXME replace with a version check once mmc_force_detect_change() > + * is added to the mainline kernel. > + * 2) This version does NOT implement keep_power, the dts must mark the > + * regulators as regulator-always-on and not use mmc-pwrseq for this stub > + * to work. > + */ > +#ifndef MMC_HAS_FORCE_DETECT_CHANGE > +void mmc_force_detect_change(struct mmc_host *host, unsigned long delay, > + bool keep_power) > +{ > + host->caps &= ~MMC_CAP_NONREMOVABLE; > + host->caps |= MMC_CAP_NEEDS_POLL; > + mmc_detect_change(host, delay); > +} > +#endif > + > +#define ESP_DMA_IBUFSZ 2048 > + > +struct esp_sdio_ctrl *sif_sctrl; > + > +static int esdio_power_off(struct esp_sdio_ctrl *sctrl); > +static int esdio_power_on(struct esp_sdio_ctrl *sctrl); > + > +void sif_set_clock(struct sdio_func *func, int clk); > + > +void sif_lock_bus(struct esp_pub *epub) > +{ > + if (!epub || !epub->sif || !epub->sif->func) > + return; > + sdio_claim_host(epub->sif->func); > +} > + > +void sif_unlock_bus(struct esp_pub *epub) > +{ > + if (!epub || !epub->sif || !epub->sif->func) > + return; > + sdio_release_host(epub->sif->func); > +} > + > +static inline bool bad_buf(u8 *buf) > +{ > + return (unsigned long)buf & 0x3 || !virt_addr_valid(buf); > +} > + > +u8 sdio_io_readb(struct esp_pub *epub, int addr, int *res) > +{ > + struct sdio_func *func = epub->sif->func; > + > + if (!func->num) > + return sdio_f0_readb(func, addr, res); > + > + return sdio_readb(func, addr, res); > +} > + > +void sdio_io_writeb(struct esp_pub *epub, u8 value, int addr, int *res) > +{ > + struct sdio_func *func = epub->sif->func; > + > + if (!func->num) > + sdio_f0_writeb(func, value, addr, res); > + else > + sdio_writeb(func, value, addr, res); > +} > + > +int sif_io(struct esp_pub *epub, u32 addr, u8 *buf, u32 len, u32 flag, > + bool sync) > +{ > + struct sdio_func *func; > + int err = 0; > + u8 *ibuf; > + bool need_ibuf = false; > + > + if (!epub || !buf || !epub->sif || !epub->sif->func) { > + ESSERT(0); > + return -EINVAL; > + } > + > + func = epub->sif->func; > + > + if (bad_buf(buf)) { > + need_ibuf = true; > + ibuf = epub->sif->dma_buffer; > + } else { > + ibuf = buf; > + } > + > + if (flag & SIF_TO_DEVICE) { > + if (need_ibuf) > + memcpy(ibuf, buf, len); > + > + if (sync) > + sdio_claim_host(func); > + > + if (flag & SIF_FIXED_ADDR) > + err = sdio_writesb(func, addr, ibuf, len); > + else if (flag & SIF_INC_ADDR) > + err = sdio_memcpy_toio(func, addr, ibuf, len); > + > + if (sync) > + sdio_release_host(func); > + > + } else if (flag & SIF_FROM_DEVICE) { > + if (sync) > + sdio_claim_host(func); > + > + if (flag & SIF_FIXED_ADDR) > + err = sdio_readsb(func, ibuf, addr, len); > + else if (flag & SIF_INC_ADDR) > + err = sdio_memcpy_fromio(func, ibuf, addr, len); > + > + if (sync) > + sdio_release_host(func); > + > + if (!err && need_ibuf) > + memcpy(buf, ibuf, len); > + } > + > + return err; > +} > + > +int sif_io_raw(struct esp_pub *epub, u32 addr, u8 *buf, u32 len, u32 flag) > +{ > + return sif_io(epub, addr, buf, len, flag, false); > +} > + > +int sif_io_sync(struct esp_pub *epub, u32 addr, u8 *buf, u32 len, u32 flag) > +{ > + return sif_io(epub, addr, buf, len, flag, true); > +} > + > +int sif_lldesc(struct esp_pub *epub, u8 *buf, u32 len, bool sync, bool read, > + bool noround) > +{ > + u32 read_len, addr, flags; > + > + if (!epub || !buf || !epub->sif || !epub->sif->target_id) { > + ESSERT(0); > + return -EINVAL; > + } > + > + if (epub->sif->target_id == 0x600 && !noround) > + read_len = roundup(len, epub->sif->slc_blk_sz); > + else > + read_len = len; > + > + if (read) { > + addr = epub->sif->slc_window_end_addr - 2 - len; > + flags = SIF_FROM_DEVICE | SIF_BYTE_BASIS | SIF_INC_ADDR; > + } else { > + addr = epub->sif->slc_window_end_addr - len; > + flags = SIF_TO_DEVICE | SIF_BYTE_BASIS | SIF_INC_ADDR; > + } > + > + if (sync) > + return sif_io_sync(epub, addr, buf, read_len, flags); > + return sif_io_raw(epub, addr, buf, read_len, flags); > +} > + > +int sif_lldesc_read_sync(struct esp_pub *epub, u8 *buf, u32 len) > +{ > + return sif_lldesc(epub, buf, len, true, true, false); > +} > + > +int sif_lldesc_write_sync(struct esp_pub *epub, u8 *buf, u32 len) > +{ > + return sif_lldesc(epub, buf, len, true, false, false); > +} > + > +int sif_lldesc_read_raw(struct esp_pub *epub, u8 *buf, u32 len, bool noround) > +{ > + return sif_lldesc(epub, buf, len, false, true, noround); > +} > + > +int sif_lldesc_write_raw(struct esp_pub *epub, u8 *buf, u32 len) > +{ > + return sif_lldesc(epub, buf, len, false, false, false); > +} > + > +#define MANUFACTURER_ID_EAGLE_BASE 0x1110 > +#define MANUFACTURER_CODE 0x6666 > + > +static const struct sdio_device_id esp_sdio_devices[] = { > + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_EAGLE_BASE | 0x1))}, > + {}, > +}; > + > +static int esdio_power(struct esp_sdio_ctrl *sctrl, bool power_on) > +{ > + int err; > + > + if (sctrl->off != power_on) > + return 0; > + > + sdio_claim_host(sctrl->func); > + > + if (power_on) > + err = sdio_enable_func(sctrl->func); > + else > + err = sdio_disable_func(sctrl->func); > + > + sdio_release_host(sctrl->func); > + > + if (err) { > + dev_err(sctrl->epub->dev, "unable to enable sdio func: %d\n", > + err); > + return err; > + } > + > + /* ensure device is up */ > + if (power_on) > + msleep(5); > + > + sctrl->off = !sctrl->off; > + > + return err; return 0; > +} > + > +static int esdio_power_on(struct esp_sdio_ctrl *sctrl) > +{ > + return esdio_power(sctrl, true); > +} > + > +static int esdio_power_off(struct esp_sdio_ctrl *sctrl) > +{ > + return esdio_power(sctrl, false); > +} > + > +void sif_enable_irq(struct esp_pub *epub) > +{ > + sdio_claim_host(epub->sif->func); > + > + if (sdio_claim_irq(epub->sif->func, sif_dsr)) > + dev_err(epub->dev, "sif %s failed\n", __func__); > + > + atomic_set(&epub->sip->state, SIP_BOOT); > + atomic_set(&epub->sif->irq_installed, 1); > + > + sdio_release_host(epub->sif->func); > +} > + > +void sif_disable_irq(struct esp_pub *epub) > +{ > + int i = 0; > + > + if (!atomic_read(&epub->sif->irq_installed)) > + return; > + > + sdio_claim_host(epub->sif->func); > + > + while (atomic_read(&epub->sif->irq_handling)) { > + sdio_release_host(epub->sif->func); > + schedule_timeout(HZ / 100); > + sdio_claim_host(epub->sif->func); > + if (i++ >= 400) > + break; > + } > + > + /* Ignore errors, we don't always use an irq. */ > + sdio_release_irq(epub->sif->func); > + > + atomic_set(&epub->sif->irq_installed, 0); > + sdio_release_host(epub->sif->func); > +} > + > +void sif_set_clock(struct sdio_func *func, int clk) > +{ > + struct mmc_host *host = func->card->host; > + > + sdio_claim_host(func); > + > + //currently only set clock > + host->ios.clock = clk * 1000000; > + > + if (host->ios.clock > host->f_max) > + host->ios.clock = host->f_max; > + > + host->ops->set_ios(host, &host->ios); > + > + mdelay(2); > + > + sdio_release_host(func); > +} > + > +static void esp_sdio_remove(struct sdio_func *func); > + > +static int esp_sdio_probe(struct sdio_func *func, > + const struct sdio_device_id *id) > +{ > + struct esp_pub *epub; > + struct esp_sdio_ctrl *sctrl; > + struct mmc_host *host = func->card->host; > + int err; > + > + if (!sif_sctrl) { > + request_init_conf(&func->dev); > + > + sctrl = kzalloc(sizeof(*sctrl), GFP_KERNEL); > + if (!sctrl) > + return -ENOMEM; > + > + /* temp buffer reserved for un-dma-able request */ > + sctrl->dma_buffer = kzalloc(ESP_DMA_IBUFSZ, GFP_KERNEL); > + if (!sctrl->dma_buffer) { > + err = -ENOMEM; > + goto _err_last; > + } > + > + sif_sctrl = sctrl; > + sctrl->slc_blk_sz = SIF_SLC_BLOCK_SIZE; > + > + epub = esp_pub_alloc_mac80211(&func->dev); > + if (!epub) { > + err = -ENOMEM; > + goto _err_dma; > + } > + > + epub->sif = sctrl; > + epub->sdio_state = ESP_SDIO_STATE_FIRST_INIT; > + sctrl->epub = epub; > + } else { > + /* FIXME: why duplicating sctrl and sif_sctrl? */ > + sctrl = sif_sctrl; > + sif_sctrl = NULL; > + epub = sctrl->epub; > + epub->sdio_state = ESP_SDIO_STATE_SECOND_INIT; > + /* FIXME: already done in esp_pub_alloc_mac80211 which is called on first probe */ > + SET_IEEE80211_DEV(epub->hw, &func->dev); > + epub->dev = &func->dev; > + } > + > + sctrl->func = func; > + sdio_set_drvdata(func, sctrl); > + sctrl->id = id; > + sctrl->off = true; > + > + /* give us some time to enable, in ms */ > + func->enable_timeout = 100; > + > + err = esdio_power_on(sctrl); > + if (err) { > + if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) > + goto _err_ext_gpio; > + > + goto _err_second_init; > + } > + > + check_target_id(epub); > + > + sdio_claim_host(func); > + > + err = sdio_set_block_size(func, sctrl->slc_blk_sz); > + if (err) { > + dev_err(epub->dev, "Set sdio block size %d failed: %d)\n", > + sctrl->slc_blk_sz, err); > + > + sdio_release_host(func); > + if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) > + goto _err_off; > + > + goto _err_second_init; > + } > + > + sdio_release_host(func); > + > +#ifdef LOWER_CLK > + /* fix clock for dongle */ > + sif_set_clock(func, 23); > +#endif //LOWER_CLK > + > + err = esp_pub_init_all(epub); > + if (err) { > + dev_err(epub->dev, "esp_init_all failed: %d\n", err); > + if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) { > + err = 0; Why is this success? > + goto _err_first_init; > + } > + > + if (epub->sdio_state == ESP_SDIO_STATE_SECOND_INIT) > + goto _err_second_init; > + } > + > + if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) { > + epub->sdio_state = ESP_SDIO_STATE_FIRST_NORMAL_EXIT; > + /* Rescan the esp8089 after loading the initial firmware */ > + mmc_force_detect_change(host, msecs_to_jiffies(100), true); > + } > + > + return err; return 0; > + > +_err_off: > + esdio_power_off(sctrl); > + > +_err_ext_gpio: > + esp_pub_dealloc_mac80211(epub); > + > +_err_dma: > + kfree(sctrl->dma_buffer); > + > +_err_last: > + kfree(sctrl); > + > +_err_first_init: > + if (epub && epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) > + epub->sdio_state = ESP_SDIO_STATE_FIRST_ERROR_EXIT; > + > + return err; > + > +_err_second_init: > + epub->sdio_state = ESP_SDIO_STATE_SECOND_ERROR_EXIT; > + esp_sdio_remove(func); > + > + return err; > +} > + > +static void esp_sdio_remove(struct sdio_func *func) > +{ > + struct esp_sdio_ctrl *sctrl = sdio_get_drvdata(func); > + struct esp_pub *epub; > + > + if (!sctrl) { > + return; > + } > + > + epub = sctrl->epub; > + if (!epub) { > + goto _out; > + } > + > + if (epub->sdio_state != ESP_SDIO_STATE_FIRST_NORMAL_EXIT) { > + if (epub->sip) { > + sip_detach(epub->sip); > + epub->sip = NULL; > + } > + } else { > + atomic_set(&epub->sip->state, SIP_STOP); > + sif_disable_irq(epub); > + } > + > + if (epub->sdio_state != ESP_SDIO_STATE_FIRST_NORMAL_EXIT) { > + esp_pub_dealloc_mac80211(epub); > + > + kfree(sctrl->dma_buffer); > + sctrl->dma_buffer = NULL; > + > + kfree(sctrl); > + } > + > +_out: > + sdio_set_drvdata(func, NULL); > +} > + > +MODULE_DEVICE_TABLE(sdio, esp_sdio_devices); > + > +static int esp_sdio_suspend(struct device *dev) > +{ > + struct sdio_func *func = dev_to_sdio_func(dev); > + struct esp_sdio_ctrl *sctrl = sdio_get_drvdata(func); > + struct esp_pub *epub = sctrl->epub; > + u32 sdio_flags; > + > + printk("%s", __func__); > + atomic_set(&epub->ps.state, ESP_PM_ON); > + > + sdio_flags = sdio_get_host_pm_caps(func); > + > + if (!(sdio_flags & MMC_PM_KEEP_POWER)) > + printk("%s can't keep power while host is suspended\n", > + __func__); > + > + /* keep power while host suspended */ > + if (sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER)) > + printk("%s error while trying to keep power\n", __func__); > + > + return 0; > +} > + > +static int esp_sdio_resume(struct device *dev) > +{ > + return 0; > +} > + > +static const struct dev_pm_ops esp_sdio_pm_ops = { > + .suspend = esp_sdio_suspend, > + .resume = esp_sdio_resume, > +}; > + > +static struct sdio_driver esp_sdio_driver = { > + .name = "eagle_sdio", > + .id_table = esp_sdio_devices, > + .probe = esp_sdio_probe, > + .remove = esp_sdio_remove, > + .drv = {.pm = &esp_sdio_pm_ops,}, > +}; > + > +static int /*__init*/ esp_sdio_init(void) > +{ > + esp_debugfs_init(); > + sdio_register_driver(&esp_sdio_driver); > + > + return 0; > +} > + > +static void /*__exit*/ esp_sdio_exit(void) > +{ > + sdio_unregister_driver(&esp_sdio_driver); > + esp_debugfs_exit(); > +} > + > +module_init(esp_sdio_init); > +module_exit(esp_sdio_exit); > + > +MODULE_AUTHOR("Espressif System"); > +MODULE_DESCRIPTION > +("Driver for SDIO interconnected eagle low-power WLAN devices"); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/staging/esp8089/sip2_common.h b/drivers/staging/esp8089/sip2_common.h > new file mode 100644 > index 000000000000..5dfb8c492223 > --- /dev/null > +++ b/drivers/staging/esp8089/sip2_common.h > @@ -0,0 +1,388 @@ > +/* > + * Copyright (c) 2010 - 2014 Espressif System. > + * Copyright (c) 2017 Free Electrons > + * Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it under > + * the terms of the GNU General Public License version 2 as published by the > + * Free Software Foundation. > + * > + */ > + > +#ifndef _SIP2_COMMON_H > +#define _SIP2_COMMON_H > + > +/* max 16 types */ > +enum sip_type { > + SIP_CTRL = 0, > + SIP_DATA, > + SIP_DATA_AMPDU > +}; > + > +enum sip_buf_type { > + SIP_TX_CTRL_BUF = 0, /* from host */ > + SIP_RX_CTRL_BUF, /* to host */ > + SIP_TX_DATA_BUF, /* from host */ > + SIP_RX_DATA_BUF /* to host */ > +}; > + > +enum sip_cmd_id { > + SIP_CMD_GET_VER = 0, > + SIP_CMD_WRITE_MEMORY, //1 ROM code > + SIP_CMD_READ_MEMORY, //2 > + SIP_CMD_WRITE_REG, //3 ROM code > + SIP_CMD_READ_REG, //4 > + SIP_CMD_BOOTUP, //5 ROM code > + SIP_CMD_COPYBACK, //6 > + SIP_CMD_INIT, //7 > + SIP_CMD_SCAN, //8 > + SIP_CMD_SETKEY, //9 > + SIP_CMD_CONFIG, //10 > + SIP_CMD_BSS_INFO_UPDATE, //11 > + SIP_CMD_LOOPBACK, //12 ROM code > + //do not add cmd before this line > + SIP_CMD_SET_WMM_PARAM, > + SIP_CMD_AMPDU_ACTION, > + SIP_CMD_HB_REQ, //15 > + SIP_CMD_RESET_MAC, //16 > + SIP_CMD_PRE_DOWN, //17 > + SIP_CMD_SLEEP, /* for sleep testing */ > + SIP_CMD_WAKEUP, /* for sleep testing */ > + SIP_CMD_DEBUG, /* for general testing */ > + SIP_CMD_GET_FW_VER, /* get fw rev. */ > + SIP_CMD_SETVIF, > + SIP_CMD_SETSTA, > + SIP_CMD_PS, > + SIP_CMD_ATE, > + SIP_CMD_SUSPEND, > + SIP_CMD_RECALC_CREDIT, > + SIP_CMD_MAX, > +}; > + > +enum { > + SIP_EVT_TARGET_ON = 0, // > + SIP_EVT_BOOTUP, //1 in ROM code > + SIP_EVT_COPYBACK, //2 > + SIP_EVT_SCAN_RESULT, //3 > + SIP_EVT_TX_STATUS, //4 > + SIP_EVT_CREDIT_RPT, //5, in ROM code > + SIP_EVT_ERROR, //6 > + SIP_EVT_LOOPBACK, //7, in ROM code > + SIP_EVT_SNPRINTF_TO_HOST, //8 in ROM code > + //do not add evt before this line > + SIP_EVT_HB_ACK, //9 > + SIP_EVT_RESET_MAC_ACK, //10 > + SIP_EVT_WAKEUP, //11 /* for sleep testing */ > + SIP_EVT_DEBUG, //12 /* for general testing */ > + SIP_EVT_PRINT_TO_HOST, //13 > + SIP_EVT_TRC_AMPDU, //14 > + SIP_EVT_ROC, //15 > + SIP_EVT_RESETTING, > + SIP_EVT_ATE, > + SIP_EVT_EP, > + SIP_EVT_INIT_EP, > + SIP_EVT_SLEEP, > + SIP_EVT_TXIDLE, > + SIP_EVT_NOISEFLOOR, > + SIP_EVT_MAX > +}; > + > +#define SIP_IFIDX_MASK 0xf0 > +#define SIP_IFIDX_S 4 > +#define SIP_TYPE_MASK 0x0f > +#define SIP_TYPE_S 0 > + > +#define SIP_HDR_GET_IFIDX(fc0) (((fc0) & SIP_IFIDX_MASK) >> SIP_IFIDX_S) > +#define SIP_HDR_SET_IFIDX(fc0, ifidx) ((fc0) = ((fc0) & ~SIP_IFIDX_MASK) | ((ifidx) << SIP_IFIDX_S & SIP_IFIDX_MASK)) > +#define SIP_HDR_GET_TYPE(fc0) ((fc0) & SIP_TYPE_MASK) > +/* assume type field is cleared */ > +#define SIP_HDR_SET_TYPE(fc0, type) ((fc0) = ((fc0) & ~SIP_TYPE_MASK) | ((type) & SIP_TYPE_MASK)) > + > +/* sip 2.0, not hybrid header so far */ > +#define SIP_HDR_IS_CTRL(hdr) (SIP_HDR_GET_TYPE((hdr)->fc[0]) == SIP_CTRL) > +#define SIP_HDR_IS_DATA(hdr) (SIP_HDR_GET_TYPE((hdr)->fc[0]) == SIP_DATA) > +#define SIP_HDR_IS_AMPDU(hdr) (SIP_HDR_GET_TYPE((hdr)->fc[0]) == SIP_DATA_AMPDU) > + > +/* fc[1] flags, only for data pkt. Ctrl pkts use fc[1] as eventID */ > +#define SIP_HDR_SET_FLAGS(hdr, flags) ((hdr)->fc[1] |= (flags)) > +#define SIP_HDR_F_MORE_PKT 0x1 > +#define SIP_HDR_F_NEED_CRDT_RPT 0x2 > +#define SIP_HDR_F_SYNC 0x4 > +#define SIP_HDR_F_SYNC_RESET 0x8 > +#define SIP_HDR_F_PM_TURNING_ON 0x10 > +#define SIP_HDR_F_PM_TURNING_OFF 0x20 > + > +#define SIP_HDR_NEED_CREDIT_UPDATE(hdr) ((hdr)->fc[1] & SIP_HDR_F_NEED_CRDT_RPT) > +#define SIP_HDR_IS_MORE_PKT(hdr) ((hdr)->fc[1] & SIP_HDR_F_MORE_PKT) > +#define SIP_HDR_IS_CRDT_RPT(hdr) ((hdr)->fc[1] & SIP_HDR_F_CRDT_RPT) > +#define SIP_HDR_IS_SYNC(hdr) ((hdr)->fc[1] & SIP_HDR_F_SYNC) > +#define SIP_HDR_IS_SYNC_RESET(hdr) ((hdr)->fc[1] & SIP_HDR_F_SYNC_RESET) > +#define SIP_HDR_IS_SYNC_PKT(hdr) (SIP_HDR_IS_SYNC(hdr) | SIP_HDR_IS_SYNC_RESET(hdr)) > +#define SIP_HDR_SET_SYNC(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_SYNC) > +#define SIP_HDR_SET_SYNC_RESET(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_SYNC_RESET) > +#define SIP_HDR_SET_MORE_PKT(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_MORE_PKT) > +#define SIP_HDR_SET_PM_TURNING_ON(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_PM_TURNING_ON) > +#define SIP_HDR_IS_PM_TURNING_ON(hdr) ((hdr)->fc[1] & SIP_HDR_F_PM_TURNING_ON) > +#define SIP_HDR_SET_PM_TURNING_OFF(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_PM_TURNING_OFF) > +#define SIP_HDR_IS_PM_TURNING_OFF(hdr) ((hdr)->fc[1] & SIP_HDR_F_PM_TURNING_OFF) > + > +/* fc[0]: first 4bit: ifidx; last 4bit: type > + * fc[1]: flags > + * > + * Don't touch the header definitons > + */ > +struct sip_hdr_min { > + u8 fc[2]; > + __le16 len; > +} __packed; > + > +/* not more than 4byte long */ > +struct sip_tx_data_info { > + u8 tid; > + u8 ac; > + u8 p2p:1, enc_flag:7; > + u8 hw_kid; > +} __packed; > + > +/* NB: this structure should be not more than 4byte !! */ > +struct sip_tx_info { > + union { > + u32 cmdid; > + struct sip_tx_data_info dinfo; > + } u; > +} __packed; > + > +struct sip_hdr { > + u8 fc[2]; //fc[0]: type and ifidx ; fc[1] is eventID if the first ctrl pkt in the chain. data pkt still can use fc[1] to set flag > + __le16 len; > + union { > + volatile u32 recycled_credits; /* last 12bits is credits, first 20 bits is actual length of the first pkt in the chain */ Remove the volatile. > + struct sip_tx_info tx_info; > + } u; If you just remove the "u" and make this an anonymous union then it makes everything a easier. > + u32 seq; > +} __packed; > + > +#define h_credits u.recycled_credits It means we can get rid of these ugly defines. > +#define c_evtid fc[1] > +#define c_cmdid u.tx_info.u.cmdid > +#define d_ac u.tx_info.u.dinfo.ac > +#define d_tid u.tx_info.u.dinfo.tid > +#define d_p2p u.tx_info.u.dinfo.p2p > +#define d_enc_flag u.tx_info.u.dinfo.enc_flag > +#define d_hw_kid u.tx_info.u.dinfo.hw_kid > + > +#define SIP_CREDITS_MASK 0xfff /* last 12 bits */ > + > +#define SIP_HDR_MIN_LEN 4 > +#define SIP_HDR_LEN sizeof(struct sip_hdr) > +#define SIP_CTRL_HDR_LEN SIP_HDR_LEN /* same as sip_hdr in sip2 design */ > +#define SIP_BOOT_BUF_SIZE 256 > +#define SIP_CTRL_BUF_SZ 256 /* too much?? */ > +#define SIP_CTRL_BUF_N 6 > +#define SIP_CTRL_TXBUF_N 2 > +#define SIP_CTRL_RXBUF_N 4 > + > +/* WAR for mblk */ > +#define SIP_RX_ADDR_PREFIX_MASK 0xfc000000 > +#define SIP_RX_ADDR_SHIFT 6 /* [31:5], shift 6 bits */ > + > +struct sip_cmd_write_memory { > + u32 addr; > + u32 len; > +} __packed; > + > +struct sip_cmd_read_memory { > + u32 addr; > + u32 len; > +} __packed; > + > +struct sip_cmd_write_reg { > + u32 addr; > + u32 val; > +} __packed; > + > +struct sip_cmd_bootup { > + u32 boot_addr; > +} __packed; > + > +struct sip_cmd_loopback { > + u32 txlen; //host to target packet len, 0 means no txpacket > + u32 rxlen; //target to host packet len, 0 means no rxpacket > + u32 pack_id; //sequence of packet > +} __packed; > + > +struct sip_evt_loopback { > + u32 txlen; //host to target packet len, 0 means no txpacket > + u32 rxlen; //target to host packet len, 0 means no rxpacket > + u32 pack_id; //sequence of packet > +} __packed; > + > +struct sip_cmd_copyback { > + u32 addr; > + u32 len; > +} __packed; > + > +struct sip_cmd_scan { > + // u8 ssid[32]; > + u8 ssid_len; > + // u8 hw_channel[14]; > + u8 n_channels; > + u8 ie_len; > + u8 aborted; > +} __packed; // ie[] append at the end > + > +struct sip_cmd_setkey { > + u8 bssid_no; > + u8 addr[ETH_ALEN]; > + u8 alg; > + u8 keyidx; > + u8 hw_key_idx; > + u8 flags; > + u8 keylen; > + u8 key[32]; > +} __packed; > + > +struct sip_cmd_config { > + u16 center_freq; > + u16 duration; > +} __packed; > + > +struct sip_cmd_bss_info_update { > + u8 bssid[ETH_ALEN]; > + u16 isassoc; > + u32 beacon_int; > + u8 bssid_no; > +} __packed; > + > +struct sip_evt_bootup { > + u16 tx_blksz; > + u8 mac_addr[ETH_ALEN]; > + /* anything else ? */ > +} __packed; > + > +struct sip_cmd_setvif { > + u8 index; > + u8 mac[ETH_ALEN]; > + u8 set; > + u8 op_mode; > + u8 is_p2p; > +} __packed; > + > +enum esp_ieee80211_phytype { > + ESP_IEEE80211_T_CCK = 0, > + ESP_IEEE80211_T_OFDM = 1, > + ESP_IEEE80211_T_HT20_L = 2, > + ESP_IEEE80211_T_HT20_S = 3, > +}; > + > +struct sip_cmd_setsta { > + u8 ifidx; > + u8 index; > + u8 set; > + u8 phymode; > + u8 mac[ETH_ALEN]; > + u16 aid; > + u8 ampdu_factor; > + u8 ampdu_density; > + u16 resv; > +} __packed; > + > +struct sip_cmd_ps { > + u8 dtim_period; > + u8 max_sleep_period; > + u8 on; > + u8 resv; > +} __packed; > + > +struct sip_cmd_suspend { > + u8 suspend; > + u8 resv[3]; > +} __packed; > + > +#define SIP_DUMP_RPBM_ERR BIT(0) > +#define SIP_RXABORT_FIXED BIT(1) > +#define SIP_SUPPORT_BGSCAN BIT(2) > +struct sip_evt_bootup2 { > + u16 tx_blksz; > + u8 mac_addr[ETH_ALEN]; > + u16 rx_blksz; > + u8 credit_to_reserve; > + u8 options; > + s16 noise_floor; > + u8 resv[2]; > + /* anything else ? */ > +} __packed; > + > +enum trc_ampdu_state { > + TRC_TX_AMPDU_STOPPED = 1, > + TRC_TX_AMPDU_OPERATIONAL, > + TRC_TX_AMPDU_WAIT_STOP, > + TRC_TX_AMPDU_WAIT_OPERATIONAL, > + TRC_TX_AMPDU_START, > +}; > + > +struct sip_evt_trc_ampdu { > + u8 state; > + u8 tid; > + u8 addr[ETH_ALEN]; > +} __packed; > + > +struct sip_cmd_set_wmm_params { > + u8 aci; > + u8 aifs; > + u8 ecw_min; > + u8 ecw_max; > + u16 txop_us; > +} __packed; > + > +#define SIP_AMPDU_RX_START 0 > +#define SIP_AMPDU_RX_STOP 1 > +#define SIP_AMPDU_TX_OPERATIONAL 2 > +#define SIP_AMPDU_TX_STOP 3 > + > +struct sip_cmd_ampdu_action { > + u8 action; > + u8 index; > + u8 tid; > + u8 win_size; > + u16 ssn; > + u8 addr[ETH_ALEN]; > +} __packed; > + > +#define SIP_TX_ST_OK 0 > +#define SIP_TX_ST_NOEB 1 > +#define SIP_TX_ST_ACKTO 2 > +#define SIP_TX_ST_ENCERR 3 > + > +//NB: sip_tx_status must be 4 bytes aligned > +struct sip_tx_status { > + u32 sip_seq; > + u8 errno; /* success or failure code */ > + u8 rate_index; > + char ack_signal; > + u8 pad; > +} __packed; > + > +struct sip_evt_scan_report { > + u16 scan_id; > + u16 aborted; > +} __packed; > + > +struct sip_evt_roc { > + u16 state; //start:1, end :0 > + u16 is_ok; > +} __packed; > + > +struct sip_evt_txidle { > + u32 last_seq; > +} __packed; > + > +struct sip_evt_noisefloor { > + s16 noise_floor; > + u16 pad; > +} __packed; > +/* for mblk direct memory access, no need for sip_hdr. tx: first 2k for contrl msg, > + * rest of 14k for data. rx, same. > + */ > + > +#endif /* _SIP_COMMON_H_ */ > diff --git a/drivers/staging/esp8089/slc_host_register.h b/drivers/staging/esp8089/slc_host_register.h > new file mode 100644 > index 000000000000..8cf139c0d7bc > --- /dev/null > +++ b/drivers/staging/esp8089/slc_host_register.h > @@ -0,0 +1,263 @@ > +/* > + * Copyright (c) 2011 Espressif System. > + * Copyright (c) 2017 Free Electrons > + * Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it under > + * the terms of the GNU General Public License version 2 as published by the > + * Free Software Foundation. > + * > + */ > + > +#ifndef SLC_HOST_REGISTER_H_INCLUDED > +#define SLC_HOST_REGISTER_H_INCLUDED > + > +/* #define REG_SLC_HOST_BASE 0x00000000 */ > +/* skip the token1, since reading it will clean the credit */ > +#define REG_SLC_HOST_BASE 0x00000000 > +#define REG_SLC_BASE 0x00000000 > + > +#define SLC_HOST_PF (REG_SLC_HOST_BASE + 0x0) > +#define SLC_HOST_TOKEN_RDATA (REG_SLC_HOST_BASE + 0x4) > +#define SLC_HOST_RX_PF_EOF 0x0000000F > +#define SLC_HOST_RX_PF_EOF_S 28 > +#define SLC_HOST_TOKEN1 0x00000FFF > +#define SLC_HOST_TOKEN1_S 16 > +#define SLC_HOST_RX_PF_VALID BIT(15) > +#define SLC_HOST_TOKEN0 0x00000FFF > +#define SLC_HOST_TOKEN0_S 0 > + > +#define SLC_HOST_TOKEN0_MASK SLC_HOST_TOKEN0 > +#define SLC_HOST_INT_RAW (REG_SLC_HOST_BASE + 0x8) > +#define SLC_HOST_EXT_BIT3_INT_RAW BIT(22) > +#define SLC_HOST_EXT_BIT2_INT_RAW BIT(21) > +#define SLC_HOST_EXT_BIT1_INT_RAW BIT(20) > +#define SLC_HOST_RXFIFO_NOT_EMPTY_INT_RAW BIT(19) > +#define SLC_HOST_RX_PF_VALID_INT_RAW BIT(18) > +#define SLC_HOST_TX_OVF_INT_RAW BIT(17) > +#define SLC_HOST_RX_UDF_INT_RAW BIT(16) > +#define SLC_HOST_TX_START_INT_RAW BIT(15) > +#define SLC_HOST_RX_START_INT_RAW BIT(14) > +#define SLC_HOST_RX_EOF_INT_RAW BIT(13) > +#define SLC_HOST_RX_SOF_INT_RAW BIT(12) > +#define SLC_HOST_TOKEN1_0TO1_INT_RAW BIT(11) > +#define SLC_HOST_TOKEN0_0TO1_INT_RAW BIT(10) > +#define SLC_HOST_TOKEN1_1TO0_INT_RAW BIT(9) > +#define SLC_HOST_TOKEN0_1TO0_INT_RAW BIT(8) > +#define SLC_HOST_TOHOST_BIT7_INT_RAW BIT(7) > +#define SLC_HOST_TOHOST_BIT6_INT_RAW BIT(6) > +#define SLC_HOST_TOHOST_BIT5_INT_RAW BIT(5) > +#define SLC_HOST_TOHOST_BIT4_INT_RAW BIT(4) > +#define SLC_HOST_TOHOST_BIT3_INT_RAW BIT(3) > +#define SLC_HOST_TOHOST_BIT2_INT_RAW BIT(2) > +#define SLC_HOST_TOHOST_BIT1_INT_RAW BIT(1) > +#define SLC_HOST_TOHOST_BIT0_INT_RAW BIT(0) > + > +#define SLC_HOST_STATE_W0 (REG_SLC_HOST_BASE + 0xC) > +#define SLC_HOST_STATE3 0x000000FF > +#define SLC_HOST_STATE3_S 24 > +#define SLC_HOST_STATE2 0x000000FF > +#define SLC_HOST_STATE2_S 16 > +#define SLC_HOST_STATE1 0x000000FF > +#define SLC_HOST_STATE1_S 8 > +#define SLC_HOST_STATE0 0x000000FF > +#define SLC_HOST_STATE0_S 0 > + > +#define SLC_HOST_STATE_W1 (REG_SLC_HOST_BASE + 0x10) > +#define SLC_HOST_STATE7 0x000000FF > +#define SLC_HOST_STATE7_S 24 > +#define SLC_HOST_STATE6 0x000000FF > +#define SLC_HOST_STATE6_S 16 > +#define SLC_HOST_STATE5 0x000000FF > +#define SLC_HOST_STATE5_S 8 > +#define SLC_HOST_STATE4 0x000000FF > +#define SLC_HOST_STATE4_S 0 > + > +#define SLC_HOST_CONF_W0 (REG_SLC_HOST_BASE + 0x14) > +#define SLC_HOST_CONF3 0x000000FF > +#define SLC_HOST_CONF3_S 24 > +#define SLC_HOST_CONF2 0x000000FF > +#define SLC_HOST_CONF2_S 16 > +#define SLC_HOST_CONF1 0x000000FF > +#define SLC_HOST_CONF1_S 8 > +#define SLC_HOST_CONF0 0x000000FF > +#define SLC_HOST_CONF0_S 0 > + > +#define SLC_HOST_CONF_W1 (REG_SLC_HOST_BASE + 0x18) > +#define SLC_HOST_CONF7 0x000000FF > +#define SLC_HOST_CONF7_S 24 > +#define SLC_HOST_CONF6 0x000000FF > +#define SLC_HOST_CONF6_S 16 > +#define SLC_HOST_CONF5 0x000000FF > +#define SLC_HOST_CONF5_S 8 > +#define SLC_HOST_CONF4 0x000000FF > +#define SLC_HOST_CONF4_S 0 > + > +#define SLC_HOST_INT_ST (REG_SLC_HOST_BASE + 0x1C) > +#define SLC_HOST_RX_ST BIT(23) > +#define SLC_HOST_EXT_BIT3_INT_ST BIT(22) > +#define SLC_HOST_EXT_BIT2_INT_ST BIT(21) > +#define SLC_HOST_EXT_BIT1_INT_ST BIT(20) > +#define SLC_HOST_RXFIFO_NOT_EMPTY_INT_ST BIT(19) > +#define SLC_HOST_RX_PF_VALID_INT_ST BIT(18) > +#define SLC_HOST_TX_OVF_INT_ST BIT(17) > +#define SLC_HOST_RX_UDF_INT_ST BIT(16) > +#define SLC_HOST_TX_START_INT_ST BIT(15) > +#define SLC_HOST_RX_START_INT_ST BIT(14) > +#define SLC_HOST_RX_EOF_INT_ST BIT(13) > +#define SLC_HOST_RX_SOF_INT_ST BIT(12) > +#define SLC_HOST_TOKEN1_0TO1_INT_ST BIT(11) > +#define SLC_HOST_TOKEN0_0TO1_INT_ST BIT(10) > +#define SLC_HOST_TOKEN1_1TO0_INT_ST BIT(9) > +#define SLC_HOST_TOKEN0_1TO0_INT_ST BIT(8) > +#define SLC_HOST_TOHOST_BIT7_INT_ST BIT(7) > +#define SLC_HOST_TOHOST_BIT6_INT_ST BIT(6) > +#define SLC_HOST_TOHOST_BIT5_INT_ST BIT(5) > +#define SLC_HOST_TOHOST_BIT4_INT_ST BIT(4) > +#define SLC_HOST_TOHOST_BIT3_INT_ST BIT(3) > +#define SLC_HOST_TOHOST_BIT2_INT_ST BIT(2) > +#define SLC_HOST_TOHOST_BIT1_INT_ST BIT(1) > +#define SLC_HOST_TOHOST_BIT0_INT_ST BIT(0) > + > +#define SLC_HOST_CONF_W2 (REG_SLC_HOST_BASE + 0x20) > +#define SLC_HOST_CONF11 0x000000FF > +#define SLC_HOST_CONF11_S 24 > +#define SLC_HOST_CONF10 0x000000FF > +#define SLC_HOST_CONF10_S 16 > +#define SLC_HOST_CONF9 0x000000FF > +#define SLC_HOST_CONF9_S 8 > +#define SLC_HOST_CONF8 0x000000FF > +#define SLC_HOST_CONF8_S 0 > + > +#define SLC_HOST_CONF_W3 (REG_SLC_HOST_BASE + 0x24) > +#define SLC_HOST_CONF15 0x000000FF > +#define SLC_HOST_CONF15_S 24 > +#define SLC_HOST_CONF14 0x000000FF > +#define SLC_HOST_CONF14_S 16 > +#define SLC_HOST_CONF13 0x000000FF > +#define SLC_HOST_CONF13_S 8 > +#define SLC_HOST_CONF12 0x000000FF > +#define SLC_HOST_CONF12_S 0 > + > +#define SLC_HOST_GEN_TXDONE_INT BIT(16) > +#define SLC_HOST_GEN_RXDONE_INT BIT(17) > + > +#define SLC_HOST_CONF_W4 (REG_SLC_HOST_BASE + 0x28) > +#define SLC_HOST_CONF19 0x000000FF > +#define SLC_HOST_CONF19_S 24 > +#define SLC_HOST_CONF18 0x000000FF > +#define SLC_HOST_CONF18_S 16 > +#define SLC_HOST_CONF17 0x000000FF > +#define SLC_HOST_CONF17_S 8 > +#define SLC_HOST_CONF16 0x000000FF > +#define SLC_HOST_CONF16_S 0 > + > +#define SLC_HOST_TOKEN_WDATA (REG_SLC_HOST_BASE + 0x2C) > +#define SLC_HOST_TOKEN1_WD 0x00000FFF > +#define SLC_HOST_TOKEN1_WD_S 16 > +#define SLC_HOST_TOKEN0_WD 0x00000FFF > +#define SLC_HOST_TOKEN0_WD_S 0 > + > +#define SLC_HOST_INT_CLR (REG_SLC_HOST_BASE + 0x30) > +#define SLC_HOST_TOKEN1_WR BIT(31) > +#define SLC_HOST_TOKEN0_WR BIT(30) > +#define SLC_HOST_TOKEN1_DEC BIT(29) > +#define SLC_HOST_TOKEN0_DEC BIT(28) > +#define SLC_HOST_EXT_BIT3_INT_CLR BIT(22) > +#define SLC_HOST_EXT_BIT2_INT_CLR BIT(21) > +#define SLC_HOST_EXT_BIT1_INT_CLR BIT(20) > +#define SLC_HOST_EXT_BIT0_INT_CLR BIT(19) > +#define SLC_HOST_RX_PF_VALID_INT_CLR BIT(18) > +#define SLC_HOST_TX_OVF_INT_CLR BIT(17) > +#define SLC_HOST_RX_UDF_INT_CLR BIT(16) > +#define SLC_HOST_TX_START_INT_CLR BIT(15) > +#define SLC_HOST_RX_START_INT_CLR BIT(14) > +#define SLC_HOST_RX_EOF_INT_CLR BIT(13) > +#define SLC_HOST_RX_SOF_INT_CLR BIT(12) > +#define SLC_HOST_TOKEN1_0TO1_INT_CLR BIT(11) > +#define SLC_HOST_TOKEN0_0TO1_INT_CLR BIT(10) > +#define SLC_HOST_TOKEN1_1TO0_INT_CLR BIT(9) > +#define SLC_HOST_TOKEN0_1TO0_INT_CLR BIT(8) > +#define SLC_HOST_TOHOST_BIT7_INT_CLR BIT(7) > +#define SLC_HOST_TOHOST_BIT6_INT_CLR BIT(6) > +#define SLC_HOST_TOHOST_BIT5_INT_CLR BIT(5) > +#define SLC_HOST_TOHOST_BIT4_INT_CLR BIT(4) > +#define SLC_HOST_TOHOST_BIT3_INT_CLR BIT(3) > +#define SLC_HOST_TOHOST_BIT2_INT_CLR BIT(2) > +#define SLC_HOST_TOHOST_BIT1_INT_CLR BIT(1) > +#define SLC_HOST_TOHOST_BIT0_INT_CLR BIT(0) > + > +#define SLC_HOST_INT_ENA (REG_SLC_HOST_BASE + 0x34) > +#define SLC_HOST_EXT_BIT3_INT_ENA BIT(22) > +#define SLC_HOST_EXT_BIT2_INT_ENA BIT(21) > +#define SLC_HOST_EXT_BIT1_INT_ENA BIT(20) > +#define SLC_HOST_EXT_BIT0_INT_ENA BIT(19) > +#define SLC_HOST_RX_PF_VALID_INT_ENA BIT(18) > +#define SLC_HOST_TX_OVF_INT_ENA BIT(17) > +#define SLC_HOST_RX_UDF_INT_ENA BIT(16) > +#define SLC_HOST_TX_START_INT_ENA BIT(15) > +#define SLC_HOST_RX_START_INT_ENA BIT(14) > +#define SLC_HOST_RX_EOF_INT_ENA BIT(13) > +#define SLC_HOST_RX_SOF_INT_ENA BIT(12) > +#define SLC_HOST_TOKEN1_0TO1_INT_ENA BIT(11) > +#define SLC_HOST_TOKEN0_0TO1_INT_ENA BIT(10) > +#define SLC_HOST_TOKEN1_1TO0_INT_ENA BIT(9) > +#define SLC_HOST_TOKEN0_1TO0_INT_ENA BIT(8) > +#define SLC_HOST_TOHOST_BIT7_INT_ENA BIT(7) > +#define SLC_HOST_TOHOST_BIT6_INT_ENA BIT(6) > +#define SLC_HOST_TOHOST_BIT5_INT_ENA BIT(5) > +#define SLC_HOST_TOHOST_BIT4_INT_ENA BIT(4) > +#define SLC_HOST_TOHOST_BIT3_INT_ENA BIT(3) > +#define SLC_HOST_TOHOST_BIT2_INT_ENA BIT(2) > +#define SLC_HOST_TOHOST_BIT1_INT_ENA BIT(1) > +#define SLC_HOST_TOHOST_BIT0_INT_ENA BIT(0) > + > +#define SLC_HOST_CONF_W5 (REG_SLC_HOST_BASE + 0x3C) > +#define SLC_HOST_CONF23 0x000000FF > +#define SLC_HOST_CONF23_S 24 > +#define SLC_HOST_CONF22 0x000000FF > +#define SLC_HOST_CONF22_S 16 > +#define SLC_HOST_CONF21 0x000000FF > +#define SLC_HOST_CONF21_S 8 > +#define SLC_HOST_CONF20 0x000000FF > +#define SLC_HOST_CONF20_S 0 > + > +#define SLC_HOST_WIN_CMD (REG_SLC_HOST_BASE + 0x40) > + > +#define SLC_HOST_DATE (REG_SLC_HOST_BASE + 0x78) > +#define SLC_HOST_ID (REG_SLC_HOST_BASE + 0x7C) > + > +#define SLC_ADDR_WINDOW_CLEAR_MASK (~(0xf << 12)) > +#define SLC_FROM_HOST_ADDR_WINDOW BIT(12) > +#define SLC_TO_HOST_ADDR_WINDOW (0x3 << 12) > + > +#define SLC_SET_FROM_HOST_ADDR_WINDOW(v) do { \ > + (v) &= 0xffff; \ > + (v) &= SLC_ADDR_WINDOW_CLEAR_MASK; \ > + (v) |= SLC_FROM_HOST_ADDR_WINDOW; \ > +} while (0) > + > +#define SLC_SET_TO_HOST_ADDR_WINDOW(v) do { \ > + (v) &= 0xffff; \ > + (v) &= SLC_ADDR_WINDOW_CLEAR_MASK; \ > + (v) |= SLC_TO_HOST_ADDR_WINDOW; \ > +} while (0) > + > +#define SLC_INT_ENA (REG_SLC_BASE + 0xC) > +#define SLC_RX_EOF_INT_ENA BIT(17) > +#define SLC_FRHOST_BIT2_INT_ENA BIT(2) > + > +#define SLC_RX_LINK (REG_SLC_BASE + 0x24) > +#define SLC_RXLINK_START BIT(29) > + > +#define SLC_BRIDGE_CONF (REG_SLC_BASE + 0x44) > +#define SLC_TX_PUSH_IDLE_NUM 0xFFFF > +#define SLC_TX_PUSH_IDLE_NUM_S 16 > +#define SLC_HDA_MAP_128K BIT(13) > +#define SLC_TX_DUMMY_MODE BIT(12) > +#define SLC_FIFO_MAP_ENA 0x0000000F > +#define SLC_FIFO_MAP_ENA_S 8 > +#define SLC_TXEOF_ENA 0x0000003F > +#define SLC_TXEOF_ENA_S > + > +#endif // SLC_HOST_REGISTER_H_INCLUDED > -- > 2.11.0 > > > _______________________________________________ > devel mailing list > devel@xxxxxxxxxxxxxxxxxxxxxx > http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel