diff options
Diffstat (limited to 'etf_itp_patches/backports/000-add_itp_etf.patch')
-rw-r--r-- | etf_itp_patches/backports/000-add_itp_etf.patch | 1500 |
1 files changed, 1500 insertions, 0 deletions
diff --git a/etf_itp_patches/backports/000-add_itp_etf.patch b/etf_itp_patches/backports/000-add_itp_etf.patch new file mode 100644 index 0000000..46e0d65 --- /dev/null +++ b/etf_itp_patches/backports/000-add_itp_etf.patch @@ -0,0 +1,1500 @@ +diff --git a/packages/foss/backports/.local-symbols b/packages/foss/backports/.local-symbols +index 5e2f85a..b879325 100644 +--- a/packages/foss/backports/.local-symbols ++++ b/packages/foss/backports/.local-symbols +@@ -301,6 +301,8 @@ MWIFIEX_USB= + CW1200= + CW1200_WLAN_SDIO= + CW1200_WLAN_SPI= ++CW1200_ETF= ++CW1200_ITP= + ETHERNET= + MDIO= + SUNGEM_PHY= +diff --git a/packages/foss/backports/drivers/net/wireless/cw1200/Kconfig b/packages/foss/backports/drivers/net/wireless/cw1200/Kconfig +index 0fbf25e..744a7a4 100644 +--- a/packages/foss/backports/drivers/net/wireless/cw1200/Kconfig ++++ b/packages/foss/backports/drivers/net/wireless/cw1200/Kconfig +@@ -32,4 +32,19 @@ config CW1200_WLAN_SPI + need to add appropriate platform data glue in your board setup + file. + ++menu "Driver debug features" ++ depends on CW1200 && DEBUG_FS ++ ++config CW1200_ETF ++ bool "Enable CW1200 Engineering Test Framework hooks" ++ help ++ If you don't know what this is, just say N. ++ ++config CW1200_ITP ++ bool "Enable ITP access" ++ help ++ If you don't know what this is, just say N. ++ ++endmenu ++ + endif +diff --git a/packages/foss/backports/drivers/net/wireless/cw1200/Makefile b/packages/foss/backports/drivers/net/wireless/cw1200/Makefile +index 827a5f4..181eeb5 100644 +--- a/packages/foss/backports/drivers/net/wireless/cw1200/Makefile ++++ b/packages/foss/backports/drivers/net/wireless/cw1200/Makefile +@@ -9,6 +9,7 @@ cw1200_core-y := \ + sta.o \ + scan.o \ + debug.o ++cw1200_core-$(CPTCFG_CW1200_ITP) += itp.o + cw1200_core-$(CONFIG_PM) += pm.o + + # CFLAGS_sta.o += -DDEBUG +diff --git a/packages/foss/backports/drivers/net/wireless/cw1200/cw1200.h b/packages/foss/backports/drivers/net/wireless/cw1200/cw1200.h +index 300dbbb..db5a0cf 100644 +--- a/packages/foss/backports/drivers/net/wireless/cw1200/cw1200.h ++++ b/packages/foss/backports/drivers/net/wireless/cw1200/cw1200.h +@@ -41,6 +41,11 @@ struct task_struct; + struct cw1200_debug_priv; + struct firmware; + ++#ifdef CPTCFG_CW1200_ETF ++extern int etf_mode; ++extern char *etf_firmware; ++#endif ++ + #define CW1200_MAX_CTRL_FRAME_LEN (0x1000) + + #define CW1200_MAX_STA_IN_AP_MODE (5) +@@ -293,6 +298,10 @@ struct cw1200_common { + struct work_struct linkid_reset_work; + u8 action_frame_sa[ETH_ALEN]; + u8 action_linkid; ++ ++#ifdef CPTCFG_CW1200_ETF ++ struct sk_buff_head etf_q; ++#endif + }; + + struct cw1200_sta_priv { +diff --git a/packages/foss/backports/drivers/net/wireless/cw1200/debug.c b/packages/foss/backports/drivers/net/wireless/cw1200/debug.c +index e323b4d..689cd61 100644 +--- a/packages/foss/backports/drivers/net/wireless/cw1200/debug.c ++++ b/packages/foss/backports/drivers/net/wireless/cw1200/debug.c +@@ -357,6 +357,144 @@ static const struct file_operations fops_counters = { + .owner = THIS_MODULE, + }; + ++#ifdef CPTCFG_CW1200_ETF ++static int cw1200_etf_out_show(struct seq_file *seq, void *v) ++{ ++ struct cw1200_common *priv = seq->private; ++ struct sk_buff *skb; ++ u32 len = 0; ++ ++ skb = skb_dequeue(&priv->etf_q); ++ ++ if (skb) ++ len = skb->len; ++ ++ seq_write(seq, &len, sizeof(len)); ++ ++ if (skb) { ++ seq_write(seq, skb->data, len); ++ kfree_skb(skb); ++ } ++ ++ return 0; ++} ++ ++static int cw1200_etf_out_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, &cw1200_etf_out_show, ++ inode->i_private); ++} ++ ++static const struct file_operations fops_etf_out = { ++ .open = cw1200_etf_out_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++ ++struct etf_req_msg; ++static int etf_request(struct cw1200_common *priv, ++ struct etf_req_msg *msg, u32 len); ++ ++#define MAX_RX_SIZE 2600 ++ ++struct etf_in_state { ++ struct cw1200_common *priv; ++ u16 total_len; ++ u16 written; ++ u8 buf[MAX_RX_SIZE]; ++}; ++ ++static int cw1200_etf_in_open(struct inode *inode, struct file *file) ++{ ++ struct etf_in_state *etf = kmalloc(sizeof(struct etf_in_state), ++ GFP_KERNEL); ++ ++ if (!etf) ++ return -ENOMEM; ++ ++ etf->written = 0; ++ etf->total_len = 0; ++ etf->priv = inode->i_private; ++ ++ file->private_data = etf; ++ ++ return 0; ++} ++ ++static int cw1200_etf_in_release(struct inode *inode, struct file *file) ++{ ++ kfree(file->private_data); ++ return 0; ++} ++ ++static ssize_t cw1200_etf_in_write(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct etf_in_state *etf = file->private_data; ++ ++ ssize_t written = 0; ++ ++ if (!etf->total_len) { ++ if (count < sizeof(etf->total_len)) { ++ pr_err("count < sizeof(total_len)\n"); ++ return -EINVAL; ++ } ++ ++ if (copy_from_user(&etf->total_len, user_buf, ++ sizeof(etf->total_len))) { ++ pr_err("copy_from_user (len) failed\n"); ++ return -EFAULT; ++ } ++ ++ if (etf->total_len > MAX_RX_SIZE) { ++ pr_err("requested length > MAX_RX_SIZE\n"); ++ return -EINVAL; ++ } ++ ++ written += sizeof(etf->total_len); ++ count -= sizeof(etf->total_len); ++ } ++ ++ if (!count) ++ goto done; ++ ++ if (count > (etf->total_len - written)) { ++ pr_err("Tried to write > MAX_RX_SIZE\n"); ++ return -EINVAL; ++ } ++ ++ if (copy_from_user(etf->buf + etf->written, user_buf + written, ++ count)) { ++ pr_err("copy_from_user (payload %zu) failed\n", count); ++ return -EFAULT; ++ } ++ ++ written += count; ++ etf->written += count; ++ ++ if (etf->written >= etf->total_len) { ++ if (etf_request(etf->priv, (struct etf_req_msg *)etf->buf, ++ etf->total_len)) { ++ pr_err("etf_request failed\n"); ++ return -EIO; ++ } ++ } ++ ++done: ++ return written; ++} ++ ++static const struct file_operations fops_etf_in = { ++ .open = cw1200_etf_in_open, ++ .release = cw1200_etf_in_release, ++ .write = cw1200_etf_in_write, ++ .llseek = default_llseek, ++ .owner = THIS_MODULE, ++}; ++#endif /* CPTCFG_CW1200_ETF */ ++ + static ssize_t cw1200_wsm_dumps(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) + { +@@ -404,10 +542,27 @@ int cw1200_debug_init(struct cw1200_common *priv) + priv, &fops_counters)) + goto err; + ++#ifdef CPTCFG_CW1200_ETF ++ if (etf_mode) { ++ skb_queue_head_init(&priv->etf_q); ++ ++ if (!debugfs_create_file("etf_out", S_IRUSR, d->debugfs_phy, ++ priv, &fops_etf_out)) ++ goto err; ++ if (!debugfs_create_file("etf_in", S_IWUSR, d->debugfs_phy, ++ priv, &fops_etf_in)) ++ goto err; ++ } ++#endif /* CPTCFG_CW1200_ETF */ ++ + if (!debugfs_create_file("wsm_dumps", S_IWUSR, d->debugfs_phy, + priv, &fops_wsm_dumps)) + goto err; + ++ ret = cw1200_itp_init(priv); ++ if (ret) ++ goto err; ++ + return 0; + + err: +@@ -421,8 +576,93 @@ void cw1200_debug_release(struct cw1200_common *priv) + { + struct cw1200_debug_priv *d = priv->debug; + if (d) { +- debugfs_remove_recursive(d->debugfs_phy); ++ cw1200_itp_release(priv); + priv->debug = NULL; + kfree(d); + } + } ++ ++#ifdef CPTCFG_CW1200_ETF ++struct cw1200_sdd { ++ u8 id; ++ u8 len; ++ u8 data[]; ++}; ++ ++struct etf_req_msg { ++ u32 id; ++ u32 len; ++ u8 data[]; ++}; ++ ++static int parse_sdd_file(struct cw1200_common *priv, u8 *data, u32 length) ++{ ++ struct cw1200_sdd *ie; ++ ++ while (length > 0) { ++ ie = (struct cw1200_sdd *)data; ++ if (ie->id == SDD_REFERENCE_FREQUENCY_ELT_ID) { ++ priv->hw_refclk = cpu_to_le16(*((u16 *)ie->data)); ++ pr_info("Using Reference clock frequency %d KHz\n", ++ priv->hw_refclk); ++ break; ++ } ++ ++ length -= ie->len + sizeof(*ie); ++ data += ie->len + sizeof(*ie); ++ } ++ return 0; ++} ++ ++char *etf_firmware; ++ ++#define ST90TDS_START_ADAPTER 0x09 /* Loads firmware too */ ++#define ST90TDS_STOP_ADAPTER 0x0A ++#define ST90TDS_CONFIG_ADAPTER 0x0E /* Send configuration params */ ++#define ST90TDS_SBUS_READ 0x13 ++#define ST90TDS_SBUS_WRITE 0x14 ++#define ST90TDS_GET_DEVICE_OPTION 0x19 ++#define ST90TDS_SET_DEVICE_OPTION 0x1A ++#define ST90TDS_SEND_SDD 0x1D /* SDD File used to find DPLL */ ++ ++#include "fwio.h" ++ ++static int etf_request(struct cw1200_common *priv, ++ struct etf_req_msg *msg, ++ u32 len) ++{ ++ int rval = -1; ++ switch (msg->id) { ++ case ST90TDS_START_ADAPTER: ++ etf_firmware = "cw1200_etf.bin"; ++ pr_info("ETF_START (len %d, '%s')\n", len, etf_firmware); ++ rval = cw1200_load_firmware(priv); ++ break; ++ case ST90TDS_STOP_ADAPTER: ++ pr_info("ETF_STOP (unhandled)\n"); ++ break; ++ case ST90TDS_SEND_SDD: ++ pr_info("ETF_SDD\n"); ++ rval = parse_sdd_file(priv, msg->data, msg->len); ++ break; ++ case ST90TDS_CONFIG_ADAPTER: ++ pr_info("ETF_CONFIG_ADAP (unhandled)\n"); ++ break; ++ case ST90TDS_SBUS_READ: ++ pr_info("ETF_SBUS_READ (unhandled)\n"); ++ break; ++ case ST90TDS_SBUS_WRITE: ++ pr_info("ETF_SBUS_WRITE (unhandled)\n"); ++ break; ++ case ST90TDS_SET_DEVICE_OPTION: ++ pr_info("ETF_SET_DEV_OPT (unhandled)\n"); ++ break; ++ default: ++ pr_info("ETF_PASSTHRU (0x%08x)\n", msg->id); ++ rval = wsm_raw_cmd(priv, (u8 *)msg, len); ++ break; ++ } ++ ++ return rval; ++} ++#endif /* CPTCFG_CW1200_ETF */ +diff --git a/packages/foss/backports/drivers/net/wireless/cw1200/debug.h b/packages/foss/backports/drivers/net/wireless/cw1200/debug.h +index b525aba..36c527f 100644 +--- a/packages/foss/backports/drivers/net/wireless/cw1200/debug.h ++++ b/packages/foss/backports/drivers/net/wireless/cw1200/debug.h +@@ -12,6 +12,8 @@ + #ifndef CW1200_DEBUG_H_INCLUDED + #define CW1200_DEBUG_H_INCLUDED + ++#include "itp.h" ++ + struct cw1200_debug_priv { + struct dentry *debugfs_phy; + int tx; +@@ -28,6 +30,9 @@ struct cw1200_debug_priv { + int ba_acc; + int ba_cnt_rx; + int ba_acc_rx; ++#ifdef CPTCFG_CW1200_ITP ++ struct cw1200_itp itp; ++#endif /* CPTCFG_CW1200_ITP */ + }; + + int cw1200_debug_init(struct cw1200_common *priv); +diff --git a/packages/foss/backports/drivers/net/wireless/cw1200/fwio.c b/packages/foss/backports/drivers/net/wireless/cw1200/fwio.c +index acdff0f..98612ad 100644 +--- a/packages/foss/backports/drivers/net/wireless/cw1200/fwio.c ++++ b/packages/foss/backports/drivers/net/wireless/cw1200/fwio.c +@@ -139,6 +139,11 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) + val32 &= ~ST90TDS_CONFIG_CPU_CLK_DIS_BIT; + REG_WRITE(ST90TDS_CONFIG_REG_ID, val32); + ++#ifdef CPTCFG_CW1200_ETF ++ if (etf_mode) ++ fw_path = etf_firmware; ++#endif ++ + /* Load a firmware file */ + ret = request_firmware(&firmware, fw_path, priv->pdev); + if (ret) { +diff --git a/packages/foss/backports/drivers/net/wireless/cw1200/itp.c b/packages/foss/backports/drivers/net/wireless/cw1200/itp.c +new file mode 100644 +index 0000000..c0730bb +--- /dev/null ++++ b/packages/foss/backports/drivers/net/wireless/cw1200/itp.c +@@ -0,0 +1,730 @@ ++/* ++ * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers ++ * ITP code ++ * ++ * Copyright (c) 2010, ST-Ericsson ++ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> ++ * ++ * 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/module.h> ++#include <linux/debugfs.h> ++#include <linux/poll.h> ++#include <linux/time.h> ++#include <linux/random.h> ++#include <linux/kallsyms.h> ++#include <net/mac80211.h> ++#include "cw1200.h" ++#include "debug.h" ++#include "itp.h" ++#include "sta.h" ++ ++static int __cw1200_itp_open(struct cw1200_common *priv); ++static int __cw1200_itp_close(struct cw1200_common *priv); ++static void cw1200_itp_rx_start(struct cw1200_common *priv); ++static void cw1200_itp_rx_stop(struct cw1200_common *priv); ++static void cw1200_itp_rx_stats(struct cw1200_common *priv); ++static void cw1200_itp_rx_reset(struct cw1200_common *priv); ++static void cw1200_itp_tx_stop(struct cw1200_common *priv); ++static void cw1200_itp_handle(struct cw1200_common *priv, ++ struct sk_buff *skb); ++static void cw1200_itp_err(struct cw1200_common *priv, ++ int err, ++ int arg); ++static void __cw1200_itp_tx_stop(struct cw1200_common *priv); ++ ++static ssize_t cw1200_itp_read(struct file *file, ++ char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct cw1200_common *priv = file->private_data; ++ struct cw1200_itp *itp = &priv->debug->itp; ++ struct sk_buff *skb; ++ int ret; ++ ++ if (skb_queue_empty(&itp->log_queue)) ++ return 0; ++ ++ skb = skb_dequeue(&itp->log_queue); ++ ret = copy_to_user(user_buf, skb->data, skb->len); ++ *ppos += skb->len; ++ skb->data[skb->len] = 0; ++ pr_debug("[ITP] >>> %s", skb->data); ++ consume_skb(skb); ++ ++ return skb->len - ret; ++} ++ ++static ssize_t cw1200_itp_write(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct cw1200_common *priv = file->private_data; ++ struct sk_buff *skb; ++ ++ if (!count || count > 1024) ++ return -EINVAL; ++ skb = dev_alloc_skb(count + 1); ++ if (!skb) ++ return -ENOMEM; ++ skb_trim(skb, 0); ++ skb_put(skb, count + 1); ++ if (copy_from_user(skb->data, user_buf, count)) { ++ kfree_skb(skb); ++ return -EFAULT; ++ } ++ skb->data[count] = 0; ++ ++ cw1200_itp_handle(priv, skb); ++ consume_skb(skb); ++ return count; ++} ++ ++static unsigned int cw1200_itp_poll(struct file *file, poll_table *wait) ++{ ++ struct cw1200_common *priv = file->private_data; ++ struct cw1200_itp *itp = &priv->debug->itp; ++ unsigned int mask = 0; ++ ++ poll_wait(file, &itp->read_wait, wait); ++ ++ if (!skb_queue_empty(&itp->log_queue)) ++ mask |= POLLIN | POLLRDNORM; ++ ++ mask |= POLLOUT | POLLWRNORM; ++ ++ return mask; ++} ++ ++static int cw1200_itp_open(struct inode *inode, struct file *file) ++{ ++ struct cw1200_common *priv = inode->i_private; ++ struct cw1200_itp *itp = &priv->debug->itp; ++ int ret = 0; ++ ++ file->private_data = priv; ++ if (atomic_inc_return(&itp->open_count) == 1) { ++ ret = __cw1200_itp_open(priv); ++ if (ret && !atomic_dec_return(&itp->open_count)) ++ __cw1200_itp_close(priv); ++ } else { ++ atomic_dec(&itp->open_count); ++ ret = -EBUSY; ++ } ++ ++ return ret; ++} ++ ++static int cw1200_itp_close(struct inode *inode, struct file *file) ++{ ++ struct cw1200_common *priv = file->private_data; ++ struct cw1200_itp *itp = &priv->debug->itp; ++ if (!atomic_dec_return(&itp->open_count)) { ++ __cw1200_itp_close(priv); ++ wake_up(&itp->close_wait); ++ } ++ return 0; ++} ++ ++static const struct file_operations fops_itp = { ++ .open = cw1200_itp_open, ++ .read = cw1200_itp_read, ++ .write = cw1200_itp_write, ++ .poll = cw1200_itp_poll, ++ .release = cw1200_itp_close, ++ .llseek = default_llseek, ++ .owner = THIS_MODULE, ++}; ++ ++static void cw1200_itp_fill_pattern(u8 *data, int size, ++ enum cw1200_itp_data_modes mode) ++{ ++ if (size <= 0) ++ return; ++ ++ switch (mode) { ++ default: ++ case ITP_DATA_ZEROS: ++ memset(data, 0x0, size); ++ break; ++ case ITP_DATA_ONES: ++ memset(data, 0xff, size); ++ break; ++ case ITP_DATA_ZERONES: ++ memset(data, 0x55, size); ++ break; ++ case ITP_DATA_RANDOM: ++ get_random_bytes(data, size); ++ break; ++ } ++ return; ++} ++ ++static void cw1200_itp_tx_work(struct work_struct *work) ++{ ++ struct cw1200_itp *itp = container_of(work, struct cw1200_itp, ++ tx_work.work); ++ struct cw1200_common *priv = itp->priv; ++ atomic_set(&priv->bh_tx, 1); ++ wake_up(&priv->bh_wq); ++} ++ ++static void cw1200_itp_tx_finish(struct work_struct *work) ++{ ++ struct cw1200_itp *itp = container_of(work, struct cw1200_itp, ++ tx_finish.work); ++ __cw1200_itp_tx_stop(itp->priv); ++} ++ ++int cw1200_itp_init(struct cw1200_common *priv) ++{ ++ struct cw1200_itp *itp = &priv->debug->itp; ++ ++ itp->priv = priv; ++ atomic_set(&itp->open_count, 0); ++ atomic_set(&itp->stop_tx, 0); ++ atomic_set(&itp->awaiting_confirm, 0); ++ skb_queue_head_init(&itp->log_queue); ++ spin_lock_init(&itp->tx_lock); ++ init_waitqueue_head(&itp->read_wait); ++ init_waitqueue_head(&itp->write_wait); ++ init_waitqueue_head(&itp->close_wait); ++ INIT_DELAYED_WORK(&itp->tx_work, cw1200_itp_tx_work); ++ INIT_DELAYED_WORK(&itp->tx_finish, cw1200_itp_tx_finish); ++ itp->data = NULL; ++ itp->hdr_len = WSM_TX_EXTRA_HEADROOM + ++ sizeof(struct ieee80211_hdr_3addr); ++ ++ if (!debugfs_create_file("itp", S_IRUSR | S_IWUSR, ++ priv->debug->debugfs_phy, priv, &fops_itp)) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++void cw1200_itp_release(struct cw1200_common *priv) ++{ ++ struct cw1200_itp *itp = &priv->debug->itp; ++ ++ wait_event_interruptible(itp->close_wait, ++ !atomic_read(&itp->open_count)); ++ ++ WARN_ON(atomic_read(&itp->open_count)); ++ ++ skb_queue_purge(&itp->log_queue); ++ cw1200_itp_tx_stop(priv); ++} ++ ++static int __cw1200_itp_open(struct cw1200_common *priv) ++{ ++ struct cw1200_itp *itp = &priv->debug->itp; ++ ++ if (!priv->vif) ++ return -EINVAL; ++ if (priv->join_status) ++ return -EINVAL; ++ itp->saved_channel = priv->channel; ++ if (!priv->channel) ++ priv->channel = &priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0]; ++ wsm_set_bssid_filtering(priv, false); ++ cw1200_itp_rx_reset(priv); ++ return 0; ++} ++ ++static int __cw1200_itp_close(struct cw1200_common *priv) ++{ ++ struct cw1200_itp *itp = &priv->debug->itp; ++ if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST) ++ cw1200_itp_rx_stop(priv); ++ cw1200_itp_tx_stop(priv); ++ cw1200_disable_listening(priv); ++ cw1200_update_filtering(priv); ++ priv->channel = itp->saved_channel; ++ return 0; ++} ++ ++bool cw1200_is_itp(struct cw1200_common *priv) ++{ ++ struct cw1200_itp *itp = &priv->debug->itp; ++ return atomic_read(&itp->open_count) != 0; ++} ++ ++static void cw1200_itp_rx_reset(struct cw1200_common *priv) ++{ ++ struct cw1200_itp *itp = &priv->debug->itp; ++ itp->rx_cnt = 0; ++ itp->rx_rssi = 0; ++ itp->rx_rssi_max = -1000; ++ itp->rx_rssi_min = 1000; ++} ++ ++static void cw1200_itp_rx_start(struct cw1200_common *priv) ++{ ++ struct cw1200_itp *itp = &priv->debug->itp; ++ ++ pr_debug("[ITP] RX start, band = %d, ch = %d\n", ++ itp->band, itp->ch); ++ atomic_set(&itp->test_mode, TEST_MODE_RX_TEST); ++ cw1200_update_listening(priv, false); ++ priv->channel = &priv->hw-> ++ wiphy->bands[itp->band]->channels[itp->ch]; ++ cw1200_update_listening(priv, true); ++ wsm_set_bssid_filtering(priv, false); ++} ++ ++static void cw1200_itp_rx_stop(struct cw1200_common *priv) ++{ ++ struct cw1200_itp *itp = &priv->debug->itp; ++ pr_debug("[ITP] RX stop\n"); ++ atomic_set(&itp->test_mode, TEST_MODE_NO_TEST); ++ cw1200_itp_rx_reset(priv); ++} ++ ++static void cw1200_itp_rx_stats(struct cw1200_common *priv) ++{ ++ struct cw1200_itp *itp = &priv->debug->itp; ++ struct sk_buff *skb; ++ char buf[128]; ++ int len, ret; ++ struct wsm_mib_counters_table counters; ++ ++ ret = wsm_get_counters_table(priv, &counters); ++ ++ if (ret) ++ cw1200_itp_err(priv, -EBUSY, 20); ++ ++ if (!itp->rx_cnt) ++ len = snprintf(buf, sizeof(buf), "1,0,0,0,0,%d\n", ++ counters.rx_packet_errors); ++ else ++ len = snprintf(buf, sizeof(buf), "1,%d,%ld,%d,%d,%d\n", ++ itp->rx_cnt, ++ itp->rx_cnt ? itp->rx_rssi / itp->rx_cnt : 0, ++ itp->rx_rssi_min, itp->rx_rssi_max, ++ counters.rx_packet_errors); ++ ++ if (len <= 0) { ++ cw1200_itp_err(priv, -EBUSY, 21); ++ return; ++ } ++ ++ skb = dev_alloc_skb(len); ++ if (!skb) { ++ cw1200_itp_err(priv, -ENOMEM, 22); ++ return; ++ } ++ ++ itp->rx_cnt = 0; ++ itp->rx_rssi = 0; ++ itp->rx_rssi_max = -1000; ++ itp->rx_rssi_min = 1000; ++ ++ skb_trim(skb, 0); ++ skb_put(skb, len); ++ ++ memcpy(skb->data, buf, len); ++ skb_queue_tail(&itp->log_queue, skb); ++ wake_up(&itp->read_wait); ++} ++ ++static void cw1200_itp_tx_start(struct cw1200_common *priv) ++{ ++ struct wsm_tx *tx; ++ struct ieee80211_hdr_3addr *hdr; ++ struct cw1200_itp *itp = &priv->debug->itp; ++ struct wsm_mib_association_mode assoc_mode = { ++ .flags = WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE, ++ .preamble = itp->preamble, ++ }; ++ int len; ++ u8 da_addr[6] = ITP_DEFAULT_DA_ADDR; ++ ++ /* Rates index 4 and 5 are not supported */ ++ if (itp->rate > 3) ++ itp->rate += 2; ++ ++ pr_debug("[ITP] TX start: band = %d, ch = %d, rate = %d, preamble = %d, number = %d, data_mode = %d, interval = %d, power = %d, data_len = %d\n", ++ itp->band, itp->ch, itp->rate, itp->preamble, ++ itp->number, itp->data_mode, itp->interval_us, ++ itp->power, itp->data_len); ++ ++ len = itp->hdr_len + itp->data_len; ++ ++ itp->data = kmalloc(len, GFP_KERNEL); ++ tx = (struct wsm_tx *)itp->data; ++ tx->hdr.len = itp->data_len + itp->hdr_len; ++ tx->hdr.id = __cpu_to_le16(0x0004 | 1 << 6); ++ tx->max_tx_rate = itp->rate; ++ tx->queue_id = 3; ++ tx->more = 0; ++ tx->flags = 0xc; ++ tx->packet_id = 0x55ff55; ++ tx->reserved = 0; ++ tx->expire_time = 1; ++ ++ if (itp->preamble == ITP_PREAMBLE_GREENFIELD) ++ tx->ht_tx_parameters = WSM_HT_TX_GREENFIELD; ++ else if (itp->preamble == ITP_PREAMBLE_MIXED) ++ tx->ht_tx_parameters = WSM_HT_TX_MIXED; ++ ++ hdr = (struct ieee80211_hdr_3addr *)&itp->data[sizeof(struct wsm_tx)]; ++ memset(hdr, 0, sizeof(*hdr)); ++ hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_FCTL_TODS); ++ memcpy(hdr->addr1, da_addr, ETH_ALEN); ++ memcpy(hdr->addr2, priv->vif->addr, ETH_ALEN); ++ memcpy(hdr->addr3, da_addr, ETH_ALEN); ++ ++ cw1200_itp_fill_pattern(&itp->data[itp->hdr_len], ++ itp->data_len, itp->data_mode); ++ ++ cw1200_update_listening(priv, false); ++ priv->channel = &priv->hw->wiphy->bands[itp->band]->channels[itp->ch]; ++ WARN_ON(wsm_set_output_power(priv, itp->power)); ++ if (itp->preamble == ITP_PREAMBLE_SHORT || ++ itp->preamble == ITP_PREAMBLE_LONG) ++ WARN_ON(wsm_set_association_mode(priv, ++ &assoc_mode)); ++ wsm_set_bssid_filtering(priv, false); ++ cw1200_update_listening(priv, true); ++ ++ spin_lock_bh(&itp->tx_lock); ++ atomic_set(&itp->test_mode, TEST_MODE_TX_TEST); ++ atomic_set(&itp->awaiting_confirm, 0); ++ atomic_set(&itp->stop_tx, 0); ++ atomic_set(&priv->bh_tx, 1); ++ ktime_get_ts(&itp->last_sent); ++ wake_up(&priv->bh_wq); ++ spin_unlock_bh(&itp->tx_lock); ++} ++ ++void __cw1200_itp_tx_stop(struct cw1200_common *priv) ++{ ++ struct cw1200_itp *itp = &priv->debug->itp; ++ spin_lock_bh(&itp->tx_lock); ++ kfree(itp->data); ++ itp->data = NULL; ++ atomic_set(&itp->test_mode, TEST_MODE_NO_TEST); ++ spin_unlock_bh(&itp->tx_lock); ++} ++ ++static void cw1200_itp_tx_stop(struct cw1200_common *priv) ++{ ++ struct cw1200_itp *itp = &priv->debug->itp; ++ pr_debug("[ITP] TX stop\n"); ++ atomic_set(&itp->stop_tx, 1); ++ flush_workqueue(priv->workqueue); ++ ++ /* time for FW to confirm all tx requests */ ++ msleep(500); ++ ++ __cw1200_itp_tx_stop(priv); ++} ++ ++static int cw1200_print_fw_version(struct cw1200_common *priv, ++ u8 *buf, size_t len) ++{ ++ return snprintf(buf, len, "%s %d.%d", ++ cw1200_fw_types[priv->wsm_caps.fw_type], ++ priv->wsm_caps.fw_ver, ++ priv->wsm_caps.fw_build); ++} ++ ++static void cw1200_itp_get_version(struct cw1200_common *priv, ++ enum cw1200_itp_version_type type) ++{ ++ struct cw1200_itp *itp = &priv->debug->itp; ++ struct sk_buff *skb; ++ char buf[ITP_BUF_SIZE]; ++ size_t size = 0; ++ int len; ++ pr_debug("[ITP] print %s version\n", ++ type == ITP_CHIP_ID ? "chip" : "firmware"); ++ ++ len = snprintf(buf, ITP_BUF_SIZE, "2,"); ++ if (len <= 0) { ++ cw1200_itp_err(priv, -EINVAL, 40); ++ return; ++ } ++ size += len; ++ ++ switch (type) { ++ case ITP_CHIP_ID: ++ len = cw1200_print_fw_version(priv, buf+size, ++ ITP_BUF_SIZE - size); ++ ++ if (len <= 0) { ++ cw1200_itp_err(priv, -EINVAL, 41); ++ return; ++ } ++ size += len; ++ break; ++ case ITP_FW_VER: ++ len = snprintf(buf+size, ITP_BUF_SIZE - size, ++ "%d.%d", priv->wsm_caps.hw_id, ++ priv->wsm_caps.hw_subid); ++ if (len <= 0) { ++ cw1200_itp_err(priv, -EINVAL, 42); ++ return; ++ } ++ size += len; ++ break; ++ default: ++ cw1200_itp_err(priv, -EINVAL, 43); ++ break; ++ } ++ ++ len = snprintf(buf+size, ITP_BUF_SIZE-size, "\n"); ++ if (len <= 0) { ++ cw1200_itp_err(priv, -EINVAL, 44); ++ return; ++ } ++ size += len; ++ ++ skb = dev_alloc_skb(size); ++ if (!skb) { ++ cw1200_itp_err(priv, -ENOMEM, 45); ++ return; ++ } ++ ++ skb_trim(skb, 0); ++ skb_put(skb, size); ++ ++ memcpy(skb->data, buf, size); ++ skb_queue_tail(&itp->log_queue, skb); ++ wake_up(&itp->read_wait); ++} ++ ++int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data, ++ size_t *tx_len, int *burst) ++{ ++ struct cw1200_itp *itp; ++ struct timespec now; ++ int time_left_us; ++ ++ if (!priv->debug) ++ return 0; ++ ++ itp = &priv->debug->itp; ++ ++ if (!itp) ++ return 0; ++ ++ spin_lock_bh(&itp->tx_lock); ++ if (atomic_read(&itp->test_mode) != TEST_MODE_TX_TEST) ++ goto out; ++ ++ if (atomic_read(&itp->stop_tx)) ++ goto out; ++ ++ if (itp->number == 0) { ++ atomic_set(&itp->stop_tx, 1); ++ queue_delayed_work(priv->workqueue, &itp->tx_finish, HZ/10); ++ goto out; ++ } ++ ++ if (!itp->data) ++ goto out; ++ ++ if (priv->hw_bufs_used >= 2) { ++ if (!atomic_read(&priv->bh_rx)) ++ atomic_set(&priv->bh_rx, 1); ++ atomic_set(&priv->bh_tx, 1); ++ goto out; ++ } ++ ++ ktime_get_ts(&now); ++ time_left_us = (itp->last_sent.tv_sec - now.tv_sec)*1000000 + ++ (itp->last_sent.tv_nsec - now.tv_nsec)/1000 + ++ itp->interval_us; ++ ++ if (time_left_us > ITP_TIME_THRES_US) { ++ queue_delayed_work(priv->workqueue, &itp->tx_work, ++ ITP_US_TO_MS(time_left_us)*HZ/1000); ++ goto out; ++ } ++ ++ if (time_left_us > 50) ++ udelay(time_left_us); ++ ++ if (itp->number > 0) ++ itp->number--; ++ ++ *data = itp->data; ++ *tx_len = itp->data_len + itp->hdr_len; ++ ++ if (itp->data_mode == ITP_DATA_RANDOM) ++ cw1200_itp_fill_pattern(&itp->data[itp->hdr_len], ++ itp->data_len, itp->data_mode); ++ *burst = 2; ++ atomic_set(&priv->bh_tx, 1); ++ ktime_get_ts(&itp->last_sent); ++ atomic_add(1, &itp->awaiting_confirm); ++ spin_unlock_bh(&itp->tx_lock); ++ return 1; ++ ++out: ++ spin_unlock_bh(&itp->tx_lock); ++ return 0; ++} ++ ++bool cw1200_itp_rxed(struct cw1200_common *priv, struct sk_buff *skb) ++{ ++ struct cw1200_itp *itp = &priv->debug->itp; ++ struct ieee80211_rx_status *rx = IEEE80211_SKB_RXCB(skb); ++ int signal; ++ ++ if (atomic_read(&itp->test_mode) != TEST_MODE_RX_TEST) ++ return cw1200_is_itp(priv); ++ if (rx->freq != priv->channel->center_freq) ++ return true; ++ ++ signal = rx->signal; ++ itp->rx_cnt++; ++ itp->rx_rssi += signal; ++ if (itp->rx_rssi_min > rx->signal) ++ itp->rx_rssi_min = rx->signal; ++ if (itp->rx_rssi_max < rx->signal) ++ itp->rx_rssi_max = rx->signal; ++ ++ return true; ++} ++ ++void cw1200_itp_wake_up_tx(struct cw1200_common *priv) ++{ ++ wake_up(&priv->debug->itp.write_wait); ++} ++ ++bool cw1200_itp_tx_running(struct cw1200_common *priv) ++{ ++ if (atomic_read(&priv->debug->itp.awaiting_confirm) || ++ atomic_read(&priv->debug->itp.test_mode) == ++ TEST_MODE_TX_TEST) { ++ atomic_sub(1, &priv->debug->itp.awaiting_confirm); ++ return true; ++ } ++ return false; ++} ++ ++static void cw1200_itp_handle(struct cw1200_common *priv, ++ struct sk_buff *skb) ++{ ++ struct cw1200_itp *itp = &priv->debug->itp; ++ const struct wiphy *wiphy = priv->hw->wiphy; ++ int cmd; ++ int ret; ++ ++ pr_debug("[ITP] <<< %s", skb->data); ++ if (sscanf(skb->data, "%d", &cmd) != 1) { ++ cw1200_itp_err(priv, -EINVAL, 1); ++ return; ++ } ++ ++ switch (cmd) { ++ case 1: /* RX test */ ++ if (atomic_read(&itp->test_mode)) { ++ cw1200_itp_err(priv, -EBUSY, 0); ++ return; ++ } ++ ret = sscanf(skb->data, "%d,%d,%d", ++ &cmd, &itp->band, &itp->ch); ++ if (ret != 3) { ++ cw1200_itp_err(priv, -EINVAL, ret + 1); ++ return; ++ } ++ if (itp->band >= 2) { ++ cw1200_itp_err(priv, -EINVAL, 2); ++ } else if (!wiphy->bands[itp->band]) { ++ cw1200_itp_err(priv, -EINVAL, 2); ++ } else if (itp->ch >= wiphy->bands[itp->band]->n_channels) { ++ cw1200_itp_err(priv, -EINVAL, 3); ++ } else { ++ cw1200_itp_rx_stats(priv); ++ cw1200_itp_rx_start(priv); ++ } ++ break; ++ case 2: /* RX stat */ ++ cw1200_itp_rx_stats(priv); ++ break; ++ case 3: /* RX/TX stop */ ++ if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST) { ++ cw1200_itp_rx_stats(priv); ++ cw1200_itp_rx_stop(priv); ++ } else if (atomic_read(&itp->test_mode) == TEST_MODE_TX_TEST) { ++ cw1200_itp_tx_stop(priv); ++ } else { ++ cw1200_itp_err(priv, -EBUSY, 0); ++ } ++ break; ++ case 4: /* TX start */ ++ if (atomic_read(&itp->test_mode) != TEST_MODE_NO_TEST) { ++ cw1200_itp_err(priv, -EBUSY, 0); ++ return; ++ } ++ ret = sscanf(skb->data, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", ++ &cmd, &itp->band, &itp->ch, &itp->rate, ++ &itp->preamble, &itp->number, &itp->data_mode, ++ &itp->interval_us, &itp->power, &itp->data_len); ++ if (ret != 10) { ++ cw1200_itp_err(priv, -EINVAL, ret + 1); ++ return; ++ } ++ if (itp->band >= 2) { ++ cw1200_itp_err(priv, -EINVAL, 2); ++ } else if (!wiphy->bands[itp->band]) { ++ cw1200_itp_err(priv, -EINVAL, 2); ++ } else if (itp->ch >= wiphy->bands[itp->band]->n_channels) { ++ cw1200_itp_err(priv, -EINVAL, 3); ++ } else if (itp->rate >= 20) { ++ cw1200_itp_err(priv, -EINVAL, 4); ++ } else if (itp->preamble >= ITP_PREAMBLE_MAX) { ++ cw1200_itp_err(priv, -EINVAL, 5); ++ } else if (itp->data_mode >= ITP_DATA_MAX_MODE) { ++ cw1200_itp_err(priv, -EINVAL, 7); ++ } else if (itp->data_len < ITP_MIN_DATA_SIZE || ++ itp->data_len > (priv->wsm_caps.input_buffer_size - itp->hdr_len)) { ++ cw1200_itp_err(priv, -EINVAL, 8); ++ } else { ++ cw1200_itp_tx_start(priv); ++ } ++ break; ++ case 5: ++ cw1200_itp_get_version(priv, ITP_CHIP_ID); ++ break; ++ case 6: ++ cw1200_itp_get_version(priv, ITP_FW_VER); ++ break; ++ } ++} ++ ++static void cw1200_itp_err(struct cw1200_common *priv, ++ int err, int arg) ++{ ++ struct cw1200_itp *itp = &priv->debug->itp; ++ struct sk_buff *skb; ++ static char buf[255]; ++ int len; ++ ++ len = snprintf(buf, sizeof(buf), "%d,%d\n", ++ err, arg); ++ if (len <= 0) ++ return; ++ ++ skb = dev_alloc_skb(len); ++ if (!skb) ++ return; ++ ++ skb_trim(skb, 0); ++ skb_put(skb, len); ++ ++ memcpy(skb->data, buf, len); ++ skb_queue_tail(&itp->log_queue, skb); ++ wake_up(&itp->read_wait); ++ ++ len = sprint_symbol(buf, ++ (unsigned long)__builtin_return_address(0)); ++ if (len <= 0) ++ return; ++ pr_debug("[ITP] error %d,%d from %s\n", ++ err, arg, buf); ++} +diff --git a/packages/foss/backports/drivers/net/wireless/cw1200/itp.h b/packages/foss/backports/drivers/net/wireless/cw1200/itp.h +new file mode 100644 +index 0000000..350f498 +--- /dev/null ++++ b/packages/foss/backports/drivers/net/wireless/cw1200/itp.h +@@ -0,0 +1,144 @@ ++/* ++ * ITP code for ST-Ericsson CW1200 mac80211 driver ++ * ++ * Copyright (c) 2011, ST-Ericsson ++ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> ++ * ++ * 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 CW1200_ITP_H_INCLUDED ++#define CW1200_ITP_H_INCLUDED ++ ++struct cw200_common; ++struct wsm_tx_confirm; ++struct dentry; ++ ++#ifdef CPTCFG_CW1200_ITP ++ ++/*extern*/ struct ieee80211_channel; ++ ++#define TEST_MODE_NO_TEST (0) ++#define TEST_MODE_RX_TEST (1) ++#define TEST_MODE_TX_TEST (2) ++#define ITP_DEFAULT_DA_ADDR {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} ++#define ITP_MIN_DATA_SIZE 6 ++#define ITP_MAX_DATA_SIZE 1600 ++#define ITP_TIME_THRES_US 10000 ++#define ITP_US_TO_MS(x) ((x)/1000) ++#define ITP_MS_TO_US(x) ((x)*1000) ++#define ITP_BUF_SIZE 255 ++ ++ ++enum cw1200_itp_data_modes { ++ ITP_DATA_ZEROS, ++ ITP_DATA_ONES, ++ ITP_DATA_ZERONES, ++ ITP_DATA_RANDOM, ++ ITP_DATA_MAX_MODE, ++}; ++ ++enum cw1200_itp_version_type { ++ ITP_CHIP_ID, ++ ITP_FW_VER, ++}; ++ ++enum cw1200_itp_preamble_type { ++ ITP_PREAMBLE_LONG, ++ ITP_PREAMBLE_SHORT, ++ ITP_PREAMBLE_OFDM, ++ ITP_PREAMBLE_MIXED, ++ ITP_PREAMBLE_GREENFIELD, ++ ITP_PREAMBLE_MAX, ++}; ++ ++ ++struct cw1200_itp { ++ struct cw1200_common *priv; ++ atomic_t open_count; ++ atomic_t awaiting_confirm; ++ struct sk_buff_head log_queue; ++ wait_queue_head_t read_wait; ++ wait_queue_head_t write_wait; ++ wait_queue_head_t close_wait; ++ struct ieee80211_channel *saved_channel; ++ atomic_t stop_tx; ++ struct delayed_work tx_work; ++ struct delayed_work tx_finish; ++ spinlock_t tx_lock; ++ struct timespec last_sent; ++ atomic_t test_mode; ++ int rx_cnt; ++ long rx_rssi; ++ int rx_rssi_max; ++ int rx_rssi_min; ++ unsigned band; ++ unsigned ch; ++ unsigned rate; ++ unsigned preamble; ++ unsigned int number; ++ unsigned data_mode; ++ int interval_us; ++ int power; ++ u8 *data; ++ int hdr_len; ++ int data_len; ++}; ++ ++int cw1200_itp_init(struct cw1200_common *priv); ++void cw1200_itp_release(struct cw1200_common *priv); ++ ++bool cw1200_is_itp(struct cw1200_common *priv); ++bool cw1200_itp_rxed(struct cw1200_common *priv, struct sk_buff *skb); ++void cw1200_itp_wake_up_tx(struct cw1200_common *priv); ++int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data, ++ size_t *tx_len, int *burst); ++bool cw1200_itp_tx_running(struct cw1200_common *priv); ++ ++#else /* CPTCFG_CW1200_ITP */ ++ ++static inline int cw1200_itp_init(struct cw1200_common *priv) ++{ ++ return 0; ++} ++ ++static inline void cw1200_itp_release(struct cw1200_common *priv) ++{ ++} ++ ++static inline bool cw1200_is_itp(struct cw1200_common *priv) ++{ ++ return false; ++} ++ ++static inline bool cw1200_itp_rxed(struct cw1200_common *priv, ++ struct sk_buff *skb) ++{ ++ return false; ++} ++ ++ ++static inline void cw1200_itp_consume_txed(struct cw1200_common *priv) ++{ ++} ++ ++static inline void cw1200_itp_wake_up_tx(struct cw1200_common *priv) ++{ ++} ++ ++static inline int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data, ++ size_t *tx_len, int *burst) ++{ ++ return 0; ++} ++ ++static inline bool cw1200_itp_tx_running(struct cw1200_common *priv) ++{ ++ return false; ++} ++ ++#endif /* CPTCFG_CW1200_ITP */ ++ ++#endif /* CW1200_ITP_H_INCLUDED */ +diff --git a/packages/foss/backports/drivers/net/wireless/cw1200/main.c b/packages/foss/backports/drivers/net/wireless/cw1200/main.c +index 090f015..16c84a7 100644 +--- a/packages/foss/backports/drivers/net/wireless/cw1200/main.c ++++ b/packages/foss/backports/drivers/net/wireless/cw1200/main.c +@@ -61,6 +61,12 @@ int cw1200_power_mode = wsm_power_mode_quiescent; + module_param(cw1200_power_mode, int, 0644); + MODULE_PARM_DESC(cw1200_power_mode, "WSM power mode. 0 == active, 1 == doze, 2 == quiescent (default)"); + ++#ifdef CPTCFG_CW1200_ETF ++int etf_mode; ++module_param(etf_mode, int, 0644); ++MODULE_PARM_DESC(etf_mode, "Enable EngineeringTestingFramework operation"); ++#endif ++ + #define RATETAB_ENT(_rate, _rateid, _flags) \ + { \ + .bitrate = (_rate), \ +@@ -417,6 +423,11 @@ static int cw1200_register_common(struct ieee80211_hw *dev) + struct cw1200_common *priv = dev->priv; + int err; + ++#ifdef CPTCFG_CW1200_ETF ++ if (etf_mode) ++ goto done; ++#endif ++ + #ifdef CONFIG_PM + err = cw1200_pm_init(&priv->pm_state, priv); + if (err) { +@@ -436,6 +447,9 @@ static int cw1200_register_common(struct ieee80211_hw *dev) + return err; + } + ++#ifdef CPTCFG_CW1200_ETF ++done: ++#endif + cw1200_debug_init(priv); + + pr_info("Registered as '%s'\n", wiphy_name(dev->wiphy)); +@@ -452,7 +466,13 @@ static void cw1200_unregister_common(struct ieee80211_hw *dev) + struct cw1200_common *priv = dev->priv; + int i; + +- ieee80211_unregister_hw(dev); ++#ifdef CPTCFG_CW1200_ETF ++ if (!etf_mode) { ++#endif ++ ieee80211_unregister_hw(dev); ++#ifdef CPTCFG_CW1200_ETF ++ } ++#endif + + del_timer_sync(&priv->mcast_timeout); + cw1200_unregister_bh(priv); +@@ -553,6 +573,11 @@ int cw1200_core_probe(const struct hwbus_ops *hwbus_ops, + if (err) + goto err1; + ++#ifdef CPTCFG_CW1200_ETF ++ if (etf_mode) ++ goto skip_fw; ++#endif ++ + err = cw1200_load_firmware(priv); + if (err) + goto err2; +@@ -574,6 +599,9 @@ int cw1200_core_probe(const struct hwbus_ops *hwbus_ops, + /* Enable multi-TX confirmation */ + wsm_use_multi_tx_conf(priv, true); + ++#ifdef CPTCFG_CW1200_ETF ++skip_fw: ++#endif + err = cw1200_register_common(dev); + if (err) + goto err2; +diff --git a/packages/foss/backports/drivers/net/wireless/cw1200/txrx.c b/packages/foss/backports/drivers/net/wireless/cw1200/txrx.c +index e824d4d..951deb2 100644 +--- a/packages/foss/backports/drivers/net/wireless/cw1200/txrx.c ++++ b/packages/foss/backports/drivers/net/wireless/cw1200/txrx.c +@@ -862,6 +862,9 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, + pr_debug("[TX] TX confirm: %d, %d.\n", + arg->status, arg->ack_failures); + ++ if (cw1200_itp_tx_running(priv)) ++ return; ++ + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) { + /* STA is stopped. */ + return; +@@ -1000,7 +1003,8 @@ void cw1200_skb_dtor(struct cw1200_common *priv, + txpriv->raw_link_id, txpriv->tid); + tx_policy_put(priv, txpriv->rate_id); + } +- ieee80211_tx_status(priv->hw, skb); ++ if (!cw1200_is_itp(priv)) ++ ieee80211_tx_status(priv->hw, skb); + } + + void cw1200_rx_cb(struct cw1200_common *priv, +@@ -1207,7 +1211,9 @@ void cw1200_rx_cb(struct cw1200_common *priv, + grace_period = 1 * HZ; + cw1200_pm_stay_awake(&priv->pm_state, grace_period); + +- if (early_data) { ++ if (cw1200_itp_rxed(priv, skb)) { ++ consume_skb(skb); ++ } else if (early_data) { + spin_lock_bh(&priv->ps_state_lock); + /* Double-check status with lock held */ + if (entry->status == CW1200_LINK_SOFT) +diff --git a/packages/foss/backports/drivers/net/wireless/cw1200/wsm.c b/packages/foss/backports/drivers/net/wireless/cw1200/wsm.c +index 9e0ca30..2db8830 100644 +--- a/packages/foss/backports/drivers/net/wireless/cw1200/wsm.c ++++ b/packages/foss/backports/drivers/net/wireless/cw1200/wsm.c +@@ -21,6 +21,7 @@ + #include "bh.h" + #include "sta.h" + #include "debug.h" ++#include "itp.h" + + #define WSM_CMD_TIMEOUT (2 * HZ) /* With respect to interrupt loss */ + #define WSM_CMD_START_TIMEOUT (7 * HZ) +@@ -1110,7 +1111,10 @@ static int wsm_cmd_send(struct cw1200_common *priv, + * pad the message by a few bytes to ensure + * that it's completely received. + */ +- buf_len += 4; ++#ifdef CPTCFG_CW1200_ETF ++ if (!etf_mode) ++#endif ++ buf_len += 4; + + /* Fill HI message header */ + /* BH will add sequence number */ +@@ -1161,6 +1165,29 @@ done: + return ret; + } + ++#ifdef CPTCFG_CW1200_ETF ++int wsm_raw_cmd(struct cw1200_common *priv, u8 *data, size_t len) ++{ ++ struct wsm_buf *buf = &priv->wsm_cmd_buf; ++ int ret; ++ ++ u16 *cmd = (u16 *)(data + 2); ++ ++ wsm_cmd_lock(priv); ++ ++ WSM_PUT(buf, data + 4, len - 4); /* Skip over header (u16+u16) */ ++ ++ ret = wsm_cmd_send(priv, buf, NULL, __le16_to_cpu(*cmd), WSM_CMD_TIMEOUT); ++ ++ wsm_cmd_unlock(priv); ++ return ret; ++ ++nomem: ++ wsm_cmd_unlock(priv); ++ return -ENOMEM; ++} ++#endif /* CPTCFG_CW1200_ETF */ ++ + /* ******************************************************************** */ + /* WSM TX port control */ + +@@ -1316,6 +1343,34 @@ int wsm_handle_rx(struct cw1200_common *priv, u16 id, + pr_debug("[WSM] <<< 0x%.4X (%td)\n", id, + wsm_buf.end - wsm_buf.begin); + ++#ifdef CPTCFG_CW1200_ETF ++ if (etf_mode) { ++ struct sk_buff *skb = alloc_skb(wsm_buf.end - wsm_buf.begin, GFP_KERNEL); ++ ++ /* Strip out Sequence num before passing up */ ++ wsm->id = __le16_to_cpu(wsm->id); ++ wsm->id &= 0x0FFF; ++ wsm->id = __cpu_to_le16(wsm->id); ++ ++ memcpy(skb_put(skb, wsm_buf.end - wsm_buf.begin), ++ wsm_buf.begin, ++ wsm_buf.end - wsm_buf.begin); ++ skb_queue_tail(&priv->etf_q, skb); ++ ++ /* Special case for startup */ ++ if (id == WSM_STARTUP_IND_ID) { ++ wsm_startup_indication(priv, &wsm_buf); ++ } else if (id & 0x0400) { ++ spin_lock(&priv->wsm_cmd.lock); ++ priv->wsm_cmd.done = 1; ++ spin_unlock(&priv->wsm_cmd.lock); ++ wake_up(&priv->wsm_cmd_wq); ++ } ++ ++ goto out; ++ } ++#endif ++ + if (id == WSM_TX_CONFIRM_IND_ID) { + ret = wsm_tx_confirm(priv, &wsm_buf, link_id); + } else if (id == WSM_MULTI_TX_CONFIRM_ID) { +@@ -1676,6 +1731,12 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, + /* More is used only for broadcasts. */ + bool more = false; + ++#ifdef CPTCFG_CW1200_ITP ++ count = cw1200_itp_get_tx(priv, data, tx_len, burst); ++ if (count) ++ return count; ++#endif ++ + if (priv->wsm_cmd.ptr) { /* CMD request */ + ++count; + spin_lock(&priv->wsm_cmd.lock); +diff --git a/packages/foss/backports/drivers/net/wireless/cw1200/wsm.h b/packages/foss/backports/drivers/net/wireless/cw1200/wsm.h +index 48086e8..408853b 100644 +--- a/packages/foss/backports/drivers/net/wireless/cw1200/wsm.h ++++ b/packages/foss/backports/drivers/net/wireless/cw1200/wsm.h +@@ -1867,4 +1867,9 @@ static inline u8 wsm_queue_id_to_wsm(u8 queue_id) + return queue_mapping[queue_id]; + } + ++ ++#ifdef CPTCFG_CW1200_ETF ++int wsm_raw_cmd(struct cw1200_common *priv, u8 *data, size_t len); ++#endif ++ + #endif /* CW1200_HWIO_H_INCLUDED */ |