summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorFariya Fatima <fariyaf@gmail.com>2014-03-16 03:47:02 +0530
committerJohn W. Linville <linville@tuxdriver.com>2014-03-17 13:51:52 -0400
commitdad0d04fa7ba41ce603a01e8e64967650303e9a2 (patch)
treec33469edf6ad6cb54fa925b9eb5145b36d0912e0 /drivers
parent097638a08acde0320c44969a5dff3af105c341a0 (diff)
downloadlinux-stable-dad0d04fa7ba41ce603a01e8e64967650303e9a2.tar.gz
linux-stable-dad0d04fa7ba41ce603a01e8e64967650303e9a2.tar.bz2
linux-stable-dad0d04fa7ba41ce603a01e8e64967650303e9a2.zip
rsi: Add RS9113 wireless driver
This patch adds the Redpine Signals' 91x wireless driver. Signed-off-by: Fariya Fatima <fariyaf@gmail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/Kconfig1
-rw-r--r--drivers/net/wireless/Makefile1
-rw-r--r--drivers/net/wireless/rsi/Kconfig30
-rw-r--r--drivers/net/wireless/rsi/Makefile12
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_core.c342
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_debugfs.c339
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_mac80211.c1008
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_main.c270
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_mgmt.c1302
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_pkt.c196
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_sdio.c850
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_sdio_ops.c566
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_usb.c575
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_usb_ops.c177
-rw-r--r--drivers/net/wireless/rsi/rsi_boot_params.h126
-rw-r--r--drivers/net/wireless/rsi/rsi_common.h87
-rw-r--r--drivers/net/wireless/rsi/rsi_debugfs.h48
-rw-r--r--drivers/net/wireless/rsi/rsi_main.h232
-rw-r--r--drivers/net/wireless/rsi/rsi_mgmt.h285
-rw-r--r--drivers/net/wireless/rsi/rsi_sdio.h129
-rw-r--r--drivers/net/wireless/rsi/rsi_usb.h68
21 files changed, 6644 insertions, 0 deletions
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 9c2c285f35d5..b2137e8f7ca6 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -281,5 +281,6 @@ source "drivers/net/wireless/ti/Kconfig"
source "drivers/net/wireless/zd1211rw/Kconfig"
source "drivers/net/wireless/mwifiex/Kconfig"
source "drivers/net/wireless/cw1200/Kconfig"
+source "drivers/net/wireless/rsi/Kconfig"
endif # WLAN
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 0fab227025be..0c8891686718 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -59,3 +59,4 @@ obj-$(CONFIG_BRCMFMAC) += brcm80211/
obj-$(CONFIG_BRCMSMAC) += brcm80211/
obj-$(CONFIG_CW1200) += cw1200/
+obj-$(CONFIG_RSI_91X) += rsi/
diff --git a/drivers/net/wireless/rsi/Kconfig b/drivers/net/wireless/rsi/Kconfig
new file mode 100644
index 000000000000..35245f994c10
--- /dev/null
+++ b/drivers/net/wireless/rsi/Kconfig
@@ -0,0 +1,30 @@
+config RSI_91X
+ tristate "Redpine Signals Inc 91x WLAN driver support"
+ depends on MAC80211
+ ---help---
+ This option enabes support for RSI 1x1 devices.
+ Select M (recommended), if you have a RSI 1x1 wireless module.
+
+config RSI_DEBUGFS
+ bool "Redpine Signals Inc debug support"
+ depends on RSI_91X
+ default y
+ ---help---
+ Say Y, if you would like to enable debug support. This option
+ creates debugfs entries
+
+config RSI_SDIO
+ tristate "Redpine Signals SDIO bus support"
+ depends on MMC && RSI_91X
+ default m
+ ---help---
+ This option enables the SDIO bus support in rsi drivers.
+ Select M (recommended), if you have a RSI 1x1 wireless module.
+
+config RSI_USB
+ tristate "Redpine Signals USB bus support"
+ depends on USB && RSI_91X
+ default m
+ ---help---
+ This option enables the USB bus support in rsi drivers.
+ Select M (recommended), if you have a RSI 1x1 wireless module.
diff --git a/drivers/net/wireless/rsi/Makefile b/drivers/net/wireless/rsi/Makefile
new file mode 100644
index 000000000000..25828b692756
--- /dev/null
+++ b/drivers/net/wireless/rsi/Makefile
@@ -0,0 +1,12 @@
+rsi_91x-y += rsi_91x_main.o
+rsi_91x-y += rsi_91x_core.o
+rsi_91x-y += rsi_91x_mac80211.o
+rsi_91x-y += rsi_91x_mgmt.o
+rsi_91x-y += rsi_91x_pkt.o
+rsi_91x-$(CONFIG_RSI_DEBUGFS) += rsi_91x_debugfs.o
+
+rsi_usb-y += rsi_91x_usb.o rsi_91x_usb_ops.o
+rsi_sdio-y += rsi_91x_sdio.o rsi_91x_sdio_ops.o
+obj-$(CONFIG_RSI_91X) += rsi_91x.o
+obj-$(CONFIG_RSI_SDIO) += rsi_sdio.o
+obj-$(CONFIG_RSI_USB) += rsi_usb.o
diff --git a/drivers/net/wireless/rsi/rsi_91x_core.c b/drivers/net/wireless/rsi/rsi_91x_core.c
new file mode 100644
index 000000000000..e89535e86caf
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_91x_core.c
@@ -0,0 +1,342 @@
+/**
+ * Copyright (c) 2014 Redpine Signals Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "rsi_mgmt.h"
+#include "rsi_common.h"
+
+/**
+ * rsi_determine_min_weight_queue() - This function determines the queue with
+ * the min weight.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: q_num: Corresponding queue number.
+ */
+static u8 rsi_determine_min_weight_queue(struct rsi_common *common)
+{
+ struct wmm_qinfo *tx_qinfo = common->tx_qinfo;
+ u32 q_len = 0;
+ u8 ii = 0;
+
+ for (ii = 0; ii < NUM_EDCA_QUEUES; ii++) {
+ q_len = skb_queue_len(&common->tx_queue[ii]);
+ if ((tx_qinfo[ii].pkt_contended) && q_len) {
+ common->min_weight = tx_qinfo[ii].weight;
+ break;
+ }
+ }
+ return ii;
+}
+
+/**
+ * rsi_recalculate_weights() - This function recalculates the weights
+ * corresponding to each queue.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: recontend_queue bool variable
+ */
+static bool rsi_recalculate_weights(struct rsi_common *common)
+{
+ struct wmm_qinfo *tx_qinfo = common->tx_qinfo;
+ bool recontend_queue = false;
+ u8 ii = 0;
+ u32 q_len = 0;
+
+ for (ii = 0; ii < NUM_EDCA_QUEUES; ii++) {
+ q_len = skb_queue_len(&common->tx_queue[ii]);
+ /* Check for the need of contention */
+ if (q_len) {
+ if (tx_qinfo[ii].pkt_contended) {
+ tx_qinfo[ii].weight =
+ ((tx_qinfo[ii].weight > common->min_weight) ?
+ tx_qinfo[ii].weight - common->min_weight : 0);
+ } else {
+ tx_qinfo[ii].pkt_contended = 1;
+ tx_qinfo[ii].weight = tx_qinfo[ii].wme_params;
+ recontend_queue = true;
+ }
+ } else { /* No packets so no contention */
+ tx_qinfo[ii].weight = 0;
+ tx_qinfo[ii].pkt_contended = 0;
+ }
+ }
+
+ return recontend_queue;
+}
+
+/**
+ * rsi_core_determine_hal_queue() - This function determines the queue from
+ * which packet has to be dequeued.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: q_num: Corresponding queue number on success.
+ */
+static u8 rsi_core_determine_hal_queue(struct rsi_common *common)
+{
+ bool recontend_queue = false;
+ u32 q_len = 0;
+ u8 q_num = INVALID_QUEUE;
+ u8 ii, min = 0;
+
+ if (skb_queue_len(&common->tx_queue[MGMT_SOFT_Q])) {
+ if (!common->mgmt_q_block)
+ q_num = MGMT_SOFT_Q;
+ return q_num;
+ }
+
+ if (common->pkt_cnt != 0) {
+ --common->pkt_cnt;
+ return common->selected_qnum;
+ }
+
+get_queue_num:
+ q_num = 0;
+ recontend_queue = false;
+
+ q_num = rsi_determine_min_weight_queue(common);
+ q_len = skb_queue_len(&common->tx_queue[ii]);
+ ii = q_num;
+
+ /* Selecting the queue with least back off */
+ for (; ii < NUM_EDCA_QUEUES; ii++) {
+ if (((common->tx_qinfo[ii].pkt_contended) &&
+ (common->tx_qinfo[ii].weight < min)) && q_len) {
+ min = common->tx_qinfo[ii].weight;
+ q_num = ii;
+ }
+ }
+
+ common->tx_qinfo[q_num].pkt_contended = 0;
+ /* Adjust the back off values for all queues again */
+ recontend_queue = rsi_recalculate_weights(common);
+
+ q_len = skb_queue_len(&common->tx_queue[q_num]);
+ if (!q_len) {
+ /* If any queues are freshly contended and the selected queue
+ * doesn't have any packets
+ * then get the queue number again with fresh values
+ */
+ if (recontend_queue)
+ goto get_queue_num;
+
+ q_num = INVALID_QUEUE;
+ return q_num;
+ }
+
+ common->selected_qnum = q_num;
+ q_len = skb_queue_len(&common->tx_queue[q_num]);
+
+ switch (common->selected_qnum) {
+ case VO_Q:
+ if (q_len > MAX_CONTINUOUS_VO_PKTS)
+ common->pkt_cnt = (MAX_CONTINUOUS_VO_PKTS - 1);
+ else
+ common->pkt_cnt = --q_len;
+ break;
+
+ case VI_Q:
+ if (q_len > MAX_CONTINUOUS_VI_PKTS)
+ common->pkt_cnt = (MAX_CONTINUOUS_VI_PKTS - 1);
+ else
+ common->pkt_cnt = --q_len;
+
+ break;
+
+ default:
+ common->pkt_cnt = 0;
+ break;
+ }
+
+ return q_num;
+}
+
+/**
+ * rsi_core_queue_pkt() - This functions enqueues the packet to the queue
+ * specified by the queue number.
+ * @common: Pointer to the driver private structure.
+ * @skb: Pointer to the socket buffer structure.
+ *
+ * Return: None.
+ */
+static void rsi_core_queue_pkt(struct rsi_common *common,
+ struct sk_buff *skb)
+{
+ u8 q_num = skb->priority;
+ if (q_num >= NUM_SOFT_QUEUES) {
+ rsi_dbg(ERR_ZONE, "%s: Invalid Queue Number: q_num = %d\n",
+ __func__, q_num);
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ skb_queue_tail(&common->tx_queue[q_num], skb);
+}
+
+/**
+ * rsi_core_dequeue_pkt() - This functions dequeues the packet from the queue
+ * specified by the queue number.
+ * @common: Pointer to the driver private structure.
+ * @q_num: Queue number.
+ *
+ * Return: Pointer to sk_buff structure.
+ */
+static struct sk_buff *rsi_core_dequeue_pkt(struct rsi_common *common,
+ u8 q_num)
+{
+ if (q_num >= NUM_SOFT_QUEUES) {
+ rsi_dbg(ERR_ZONE, "%s: Invalid Queue Number: q_num = %d\n",
+ __func__, q_num);
+ return NULL;
+ }
+
+ return skb_dequeue(&common->tx_queue[q_num]);
+}
+
+/**
+ * rsi_core_qos_processor() - This function is used to determine the wmm queue
+ * based on the backoff procedure. Data packets are
+ * dequeued from the selected hal queue and sent to
+ * the below layers.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: None.
+ */
+void rsi_core_qos_processor(struct rsi_common *common)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct sk_buff *skb;
+ unsigned long tstamp_1, tstamp_2;
+ u8 q_num;
+ int status;
+
+ tstamp_1 = jiffies;
+ while (1) {
+ q_num = rsi_core_determine_hal_queue(common);
+ rsi_dbg(DATA_TX_ZONE,
+ "%s: Queue number = %d\n", __func__, q_num);
+
+ if (q_num == INVALID_QUEUE) {
+ rsi_dbg(DATA_TX_ZONE, "%s: No More Pkt\n", __func__);
+ break;
+ }
+
+ mutex_lock(&common->tx_rxlock);
+
+ status = adapter->check_hw_queue_status(adapter, q_num);
+ if ((status <= 0)) {
+ mutex_unlock(&common->tx_rxlock);
+ break;
+ }
+
+ if ((q_num < MGMT_SOFT_Q) &&
+ ((skb_queue_len(&common->tx_queue[q_num])) <=
+ MIN_DATA_QUEUE_WATER_MARK)) {
+ if (ieee80211_queue_stopped(adapter->hw, WME_AC(q_num)))
+ ieee80211_wake_queue(adapter->hw,
+ WME_AC(q_num));
+ }
+
+ skb = rsi_core_dequeue_pkt(common, q_num);
+ if (skb == NULL) {
+ mutex_unlock(&common->tx_rxlock);
+ break;
+ }
+
+ if (q_num == MGMT_SOFT_Q)
+ status = rsi_send_mgmt_pkt(common, skb);
+ else
+ status = rsi_send_data_pkt(common, skb);
+
+ if (status) {
+ mutex_unlock(&common->tx_rxlock);
+ break;
+ }
+
+ common->tx_stats.total_tx_pkt_send[q_num]++;
+
+ tstamp_2 = jiffies;
+ mutex_unlock(&common->tx_rxlock);
+
+ if (tstamp_2 > tstamp_1 + (300 * HZ / 1000))
+ schedule();
+ }
+}
+
+/**
+ * rsi_core_xmit() - This function transmits the packets received from mac80211
+ * @common: Pointer to the driver private structure.
+ * @skb: Pointer to the socket buffer structure.
+ *
+ * Return: None.
+ */
+void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct ieee80211_tx_info *info;
+ struct skb_info *tx_params;
+ struct ieee80211_hdr *tmp_hdr = NULL;
+ u8 q_num, tid = 0;
+
+ if ((!skb) || (!skb->len)) {
+ rsi_dbg(ERR_ZONE, "%s: Null skb/zero Length packet\n",
+ __func__);
+ goto xmit_fail;
+ }
+ info = IEEE80211_SKB_CB(skb);
+ tx_params = (struct skb_info *)info->driver_data;
+ tmp_hdr = (struct ieee80211_hdr *)&skb->data[0];
+
+ if (common->fsm_state != FSM_MAC_INIT_DONE) {
+ rsi_dbg(ERR_ZONE, "%s: FSM state not open\n", __func__);
+ goto xmit_fail;
+ }
+
+ if ((ieee80211_is_mgmt(tmp_hdr->frame_control)) ||
+ (ieee80211_is_ctl(tmp_hdr->frame_control))) {
+ q_num = MGMT_SOFT_Q;
+ skb->priority = q_num;
+ } else {
+ if (ieee80211_is_data_qos(tmp_hdr->frame_control)) {
+ tid = (skb->data[24] & IEEE80211_QOS_TID);
+ skb->priority = TID_TO_WME_AC(tid);
+ } else {
+ tid = IEEE80211_NONQOS_TID;
+ skb->priority = BE_Q;
+ }
+ q_num = skb->priority;
+ tx_params->tid = tid;
+ tx_params->sta_id = 0;
+ }
+
+ if ((q_num != MGMT_SOFT_Q) &&
+ ((skb_queue_len(&common->tx_queue[q_num]) + 1) >=
+ DATA_QUEUE_WATER_MARK)) {
+ if (!ieee80211_queue_stopped(adapter->hw, WME_AC(q_num)))
+ ieee80211_stop_queue(adapter->hw, WME_AC(q_num));
+ rsi_set_event(&common->tx_thread.event);
+ goto xmit_fail;
+ }
+
+ rsi_core_queue_pkt(common, skb);
+ rsi_dbg(DATA_TX_ZONE, "%s: ===> Scheduling TX thead <===\n", __func__);
+ rsi_set_event(&common->tx_thread.event);
+
+ return;
+
+xmit_fail:
+ rsi_dbg(ERR_ZONE, "%s: Failed to queue packet\n", __func__);
+ /* Dropping pkt here */
+ ieee80211_free_txskb(common->priv->hw, skb);
+}
diff --git a/drivers/net/wireless/rsi/rsi_91x_debugfs.c b/drivers/net/wireless/rsi/rsi_91x_debugfs.c
new file mode 100644
index 000000000000..7e4ef4554411
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_91x_debugfs.c
@@ -0,0 +1,339 @@
+/**
+ * Copyright (c) 2014 Redpine Signals Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "rsi_debugfs.h"
+#include "rsi_sdio.h"
+
+/**
+ * rsi_sdio_stats_read() - This function returns the sdio status of the driver.
+ * @seq: Pointer to the sequence file structure.
+ * @data: Pointer to the data.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_sdio_stats_read(struct seq_file *seq, void *data)
+{
+ struct rsi_common *common = seq->private;
+ struct rsi_hw *adapter = common->priv;
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+
+ seq_printf(seq, "total_sdio_interrupts: %d\n",
+ dev->rx_info.sdio_int_counter);
+ seq_printf(seq, "sdio_msdu_pending_intr_count: %d\n",
+ dev->rx_info.total_sdio_msdu_pending_intr);
+ seq_printf(seq, "sdio_buff_full_count : %d\n",
+ dev->rx_info.buf_full_counter);
+ seq_printf(seq, "sdio_buf_semi_full_count %d\n",
+ dev->rx_info.buf_semi_full_counter);
+ seq_printf(seq, "sdio_unknown_intr_count: %d\n",
+ dev->rx_info.total_sdio_unknown_intr);
+ /* RX Path Stats */
+ seq_printf(seq, "BUFFER FULL STATUS : %d\n",
+ dev->rx_info.buffer_full);
+ seq_printf(seq, "SEMI BUFFER FULL STATUS : %d\n",
+ dev->rx_info.semi_buffer_full);
+ seq_printf(seq, "MGMT BUFFER FULL STATUS : %d\n",
+ dev->rx_info.mgmt_buffer_full);
+ seq_printf(seq, "BUFFER FULL COUNTER : %d\n",
+ dev->rx_info.buf_full_counter);
+ seq_printf(seq, "BUFFER SEMI FULL COUNTER : %d\n",
+ dev->rx_info.buf_semi_full_counter);
+ seq_printf(seq, "MGMT BUFFER FULL COUNTER : %d\n",
+ dev->rx_info.mgmt_buf_full_counter);
+
+ return 0;
+}
+
+/**
+ * rsi_sdio_stats_open() - This funtion calls single open function of seq_file
+ * to open file and read contents from it.
+ * @inode: Pointer to the inode structure.
+ * @file: Pointer to the file structure.
+ *
+ * Return: Pointer to the opened file status: 0 on success, ENOMEM on failure.
+ */
+static int rsi_sdio_stats_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, rsi_sdio_stats_read, inode->i_private);
+}
+
+/**
+ * rsi_version_read() - This function gives driver and firmware version number.
+ * @seq: Pointer to the sequence file structure.
+ * @data: Pointer to the data.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_version_read(struct seq_file *seq, void *data)
+{
+ struct rsi_common *common = seq->private;
+
+ common->driver_ver.major = 0;
+ common->driver_ver.minor = 1;
+ common->driver_ver.release_num = 0;
+ common->driver_ver.patch_num = 0;
+ seq_printf(seq, "Driver : %x.%d.%d.%d\nLMAC : %d.%d.%d.%d\n",
+ common->driver_ver.major,
+ common->driver_ver.minor,
+ common->driver_ver.release_num,
+ common->driver_ver.patch_num,
+ common->fw_ver.major,
+ common->fw_ver.minor,
+ common->fw_ver.release_num,
+ common->fw_ver.patch_num);
+ return 0;
+}
+
+/**
+ * rsi_version_open() - This funtion calls single open function of seq_file to
+ * open file and read contents from it.
+ * @inode: Pointer to the inode structure.
+ * @file: Pointer to the file structure.
+ *
+ * Return: Pointer to the opened file status: 0 on success, ENOMEM on failure.
+ */
+static int rsi_version_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, rsi_version_read, inode->i_private);
+}
+
+/**
+ * rsi_stats_read() - This function return the status of the driver.
+ * @seq: Pointer to the sequence file structure.
+ * @data: Pointer to the data.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_stats_read(struct seq_file *seq, void *data)
+{
+ struct rsi_common *common = seq->private;
+
+ unsigned char fsm_state[][32] = {
+ "FSM_CARD_NOT_READY",
+ "FSM_BOOT_PARAMS_SENT",
+ "FSM_EEPROM_READ_MAC_ADDR",
+ "FSM_RESET_MAC_SENT",
+ "FSM_RADIO_CAPS_SENT",
+ "FSM_BB_RF_PROG_SENT",
+ "FSM_MAC_INIT_DONE"
+ };
+ seq_puts(seq, "==> RSI STA DRIVER STATUS <==\n");
+ seq_puts(seq, "DRIVER_FSM_STATE: ");
+
+ if (common->fsm_state <= FSM_MAC_INIT_DONE)
+ seq_printf(seq, "%s", fsm_state[common->fsm_state]);
+
+ seq_printf(seq, "(%d)\n\n", common->fsm_state);
+
+ /* Mgmt TX Path Stats */
+ seq_printf(seq, "total_mgmt_pkt_send : %d\n",
+ common->tx_stats.total_tx_pkt_send[MGMT_SOFT_Q]);
+ seq_printf(seq, "total_mgmt_pkt_queued : %d\n",
+ skb_queue_len(&common->tx_queue[4]));
+ seq_printf(seq, "total_mgmt_pkt_freed : %d\n",
+ common->tx_stats.total_tx_pkt_freed[MGMT_SOFT_Q]);
+
+ /* Data TX Path Stats */
+ seq_printf(seq, "total_data_vo_pkt_send: %8d\t",
+ common->tx_stats.total_tx_pkt_send[VO_Q]);
+ seq_printf(seq, "total_data_vo_pkt_queued: %8d\t",
+ skb_queue_len(&common->tx_queue[0]));
+ seq_printf(seq, "total_vo_pkt_freed: %8d\n",
+ common->tx_stats.total_tx_pkt_freed[VO_Q]);
+ seq_printf(seq, "total_data_vi_pkt_send: %8d\t",
+ common->tx_stats.total_tx_pkt_send[VI_Q]);
+ seq_printf(seq, "total_data_vi_pkt_queued: %8d\t",
+ skb_queue_len(&common->tx_queue[1]));
+ seq_printf(seq, "total_vi_pkt_freed: %8d\n",
+ common->tx_stats.total_tx_pkt_freed[VI_Q]);
+ seq_printf(seq, "total_data_be_pkt_send: %8d\t",
+ common->tx_stats.total_tx_pkt_send[BE_Q]);
+ seq_printf(seq, "total_data_be_pkt_queued: %8d\t",
+ skb_queue_len(&common->tx_queue[2]));
+ seq_printf(seq, "total_be_pkt_freed: %8d\n",
+ common->tx_stats.total_tx_pkt_freed[BE_Q]);
+ seq_printf(seq, "total_data_bk_pkt_send: %8d\t",
+ common->tx_stats.total_tx_pkt_send[BK_Q]);
+ seq_printf(seq, "total_data_bk_pkt_queued: %8d\t",
+ skb_queue_len(&common->tx_queue[3]));
+ seq_printf(seq, "total_bk_pkt_freed: %8d\n",
+ common->tx_stats.total_tx_pkt_freed[BK_Q]);
+
+ seq_puts(seq, "\n");
+ return 0;
+}
+
+/**
+ * rsi_stats_open() - This funtion calls single open function of seq_file to
+ * open file and read contents from it.
+ * @inode: Pointer to the inode structure.
+ * @file: Pointer to the file structure.
+ *
+ * Return: Pointer to the opened file status: 0 on success, ENOMEM on failure.
+ */
+static int rsi_stats_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, rsi_stats_read, inode->i_private);
+}
+
+/**
+ * rsi_debug_zone_read() - This function display the currently enabled debug zones.
+ * @seq: Pointer to the sequence file structure.
+ * @data: Pointer to the data.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_debug_zone_read(struct seq_file *seq, void *data)
+{
+ rsi_dbg(FSM_ZONE, "%x: rsi_enabled zone", rsi_zone_enabled);
+ seq_printf(seq, "The zones available are %#x\n",
+ rsi_zone_enabled);
+ return 0;
+}
+
+/**
+ * rsi_debug_read() - This funtion calls single open function of seq_file to
+ * open file and read contents from it.
+ * @inode: Pointer to the inode structure.
+ * @file: Pointer to the file structure.
+ *
+ * Return: Pointer to the opened file status: 0 on success, ENOMEM on failure.
+ */
+static int rsi_debug_read(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, rsi_debug_zone_read, inode->i_private);
+}
+
+/**
+ * rsi_debug_zone_write() - This function writes into hal queues as per user
+ * requirement.
+ * @filp: Pointer to the file structure.
+ * @buff: Pointer to the character buffer.
+ * @len: Length of the data to be written into buffer.
+ * @data: Pointer to the data.
+ *
+ * Return: len: Number of bytes read.
+ */
+static ssize_t rsi_debug_zone_write(struct file *filp,
+ const char __user *buff,
+ size_t len,
+ loff_t *data)
+{
+ unsigned long dbg_zone;
+ int ret;
+
+ if (!len)
+ return 0;
+
+ ret = kstrtoul_from_user(buff, len, 16, &dbg_zone);
+
+ if (ret)
+ return ret;
+
+ rsi_zone_enabled = dbg_zone;
+ return len;
+}
+
+#define FOPS(fopen) { \
+ .owner = THIS_MODULE, \
+ .open = (fopen), \
+ .read = seq_read, \
+ .llseek = seq_lseek, \
+}
+
+#define FOPS_RW(fopen, fwrite) { \
+ .owner = THIS_MODULE, \
+ .open = (fopen), \
+ .read = seq_read, \
+ .llseek = seq_lseek, \
+ .write = (fwrite), \
+}
+
+static const struct rsi_dbg_files dev_debugfs_files[] = {
+ {"version", 0644, FOPS(rsi_version_open),},
+ {"stats", 0644, FOPS(rsi_stats_open),},
+ {"debug_zone", 0666, FOPS_RW(rsi_debug_read, rsi_debug_zone_write),},
+ {"sdio_stats", 0644, FOPS(rsi_sdio_stats_open),},
+};
+
+/**
+ * rsi_init_dbgfs() - This function initializes the dbgfs entry.
+ * @adapter: Pointer to the adapter structure.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int rsi_init_dbgfs(struct rsi_hw *adapter)
+{
+ struct rsi_common *common = adapter->priv;
+ struct rsi_debugfs *dev_dbgfs;
+ char devdir[6];
+ int ii;
+ const struct rsi_dbg_files *files;
+
+ dev_dbgfs = kzalloc(sizeof(*dev_dbgfs), GFP_KERNEL);
+ adapter->dfsentry = dev_dbgfs;
+
+ snprintf(devdir, sizeof(devdir), "%s",
+ wiphy_name(adapter->hw->wiphy));
+ dev_dbgfs->subdir = debugfs_create_dir(devdir, NULL);
+
+ if (IS_ERR(dev_dbgfs->subdir)) {
+ if (dev_dbgfs->subdir == ERR_PTR(-ENODEV))
+ rsi_dbg(ERR_ZONE,
+ "%s:Debugfs has not been mounted\n", __func__);
+ else
+ rsi_dbg(ERR_ZONE, "debugfs:%s not created\n", devdir);
+
+ adapter->dfsentry = NULL;
+ kfree(dev_dbgfs);
+ return (int)PTR_ERR(dev_dbgfs->subdir);
+ } else {
+ for (ii = 0; ii < adapter->num_debugfs_entries; ii++) {
+ files = &dev_debugfs_files[ii];
+ dev_dbgfs->rsi_files[ii] =
+ debugfs_create_file(files->name,
+ files->perms,
+ dev_dbgfs->subdir,
+ common,
+ &files->fops);
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rsi_init_dbgfs);
+
+/**
+ * rsi_remove_dbgfs() - Removes the previously created dbgfs file entries
+ * in the reverse order of creation.
+ * @adapter: Pointer to the adapter structure.
+ *
+ * Return: None.
+ */
+void rsi_remove_dbgfs(struct rsi_hw *adapter)
+{
+ struct rsi_debugfs *dev_dbgfs = adapter->dfsentry;
+
+ if (!dev_dbgfs)
+ return;
+
+ debugfs_remove_recursive(dev_dbgfs->subdir);
+}
+EXPORT_SYMBOL_GPL(rsi_remove_dbgfs);
diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
new file mode 100644
index 000000000000..84164747ace0
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
@@ -0,0 +1,1008 @@
+/**
+ * Copyright (c) 2014 Redpine Signals Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/etherdevice.h>
+#include "rsi_debugfs.h"
+#include "rsi_mgmt.h"
+#include "rsi_common.h"
+
+static const struct ieee80211_channel rsi_2ghz_channels[] = {
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2412,
+ .hw_value = 1 }, /* Channel 1 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2417,
+ .hw_value = 2 }, /* Channel 2 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2422,
+ .hw_value = 3 }, /* Channel 3 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2427,
+ .hw_value = 4 }, /* Channel 4 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2432,
+ .hw_value = 5 }, /* Channel 5 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2437,
+ .hw_value = 6 }, /* Channel 6 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2442,
+ .hw_value = 7 }, /* Channel 7 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2447,
+ .hw_value = 8 }, /* Channel 8 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2452,
+ .hw_value = 9 }, /* Channel 9 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2457,
+ .hw_value = 10 }, /* Channel 10 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2462,
+ .hw_value = 11 }, /* Channel 11 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2467,
+ .hw_value = 12 }, /* Channel 12 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2472,
+ .hw_value = 13 }, /* Channel 13 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2484,
+ .hw_value = 14 }, /* Channel 14 */
+};
+
+static const struct ieee80211_channel rsi_5ghz_channels[] = {
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5180,
+ .hw_value = 36, }, /* Channel 36 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5200,
+ .hw_value = 40, }, /* Channel 40 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5220,
+ .hw_value = 44, }, /* Channel 44 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5240,
+ .hw_value = 48, }, /* Channel 48 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5260,
+ .hw_value = 52, }, /* Channel 52 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5280,
+ .hw_value = 56, }, /* Channel 56 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5300,
+ .hw_value = 60, }, /* Channel 60 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5320,
+ .hw_value = 64, }, /* Channel 64 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5500,
+ .hw_value = 100, }, /* Channel 100 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5520,
+ .hw_value = 104, }, /* Channel 104 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5540,
+ .hw_value = 108, }, /* Channel 108 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5560,
+ .hw_value = 112, }, /* Channel 112 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5580,
+ .hw_value = 116, }, /* Channel 116 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5600,
+ .hw_value = 120, }, /* Channel 120 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5620,
+ .hw_value = 124, }, /* Channel 124 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5640,
+ .hw_value = 128, }, /* Channel 128 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5660,
+ .hw_value = 132, }, /* Channel 132 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5680,
+ .hw_value = 136, }, /* Channel 136 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5700,
+ .hw_value = 140, }, /* Channel 140 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5745,
+ .hw_value = 149, }, /* Channel 149 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5765,
+ .hw_value = 153, }, /* Channel 153 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5785,
+ .hw_value = 157, }, /* Channel 157 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5805,
+ .hw_value = 161, }, /* Channel 161 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5825,
+ .hw_value = 165, }, /* Channel 165 */
+};
+
+struct ieee80211_rate rsi_rates[12] = {
+ { .bitrate = STD_RATE_01 * 5, .hw_value = RSI_RATE_1 },
+ { .bitrate = STD_RATE_02 * 5, .hw_value = RSI_RATE_2 },
+ { .bitrate = STD_RATE_5_5 * 5, .hw_value = RSI_RATE_5_5 },
+ { .bitrate = STD_RATE_11 * 5, .hw_value = RSI_RATE_11 },
+ { .bitrate = STD_RATE_06 * 5, .hw_value = RSI_RATE_6 },
+ { .bitrate = STD_RATE_09 * 5, .hw_value = RSI_RATE_9 },
+ { .bitrate = STD_RATE_12 * 5, .hw_value = RSI_RATE_12 },
+ { .bitrate = STD_RATE_18 * 5, .hw_value = RSI_RATE_18 },
+ { .bitrate = STD_RATE_24 * 5, .hw_value = RSI_RATE_24 },
+ { .bitrate = STD_RATE_36 * 5, .hw_value = RSI_RATE_36 },
+ { .bitrate = STD_RATE_48 * 5, .hw_value = RSI_RATE_48 },
+ { .bitrate = STD_RATE_54 * 5, .hw_value = RSI_RATE_54 },
+};
+
+const u16 rsi_mcsrates[8] = {
+ RSI_RATE_MCS0, RSI_RATE_MCS1, RSI_RATE_MCS2, RSI_RATE_MCS3,
+ RSI_RATE_MCS4, RSI_RATE_MCS5, RSI_RATE_MCS6, RSI_RATE_MCS7
+};
+
+/**
+ * rsi_is_cipher_wep() - This function determines if the cipher is WEP or not.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: If cipher type is WEP, a value of 1 is returned, else 0.
+ */
+
+bool rsi_is_cipher_wep(struct rsi_common *common)
+{
+ if (((common->secinfo.gtk_cipher == WLAN_CIPHER_SUITE_WEP104) ||
+ (common->secinfo.gtk_cipher == WLAN_CIPHER_SUITE_WEP40)) &&
+ (!common->secinfo.ptk_cipher))
+ return true;
+ else
+ return false;
+}
+
+/**
+ * rsi_register_rates_channels() - This function registers channels and rates.
+ * @adapter: Pointer to the adapter structure.
+ * @band: Operating band to be set.
+ *
+ * Return: None.
+ */
+static void rsi_register_rates_channels(struct rsi_hw *adapter, int band)
+{
+ struct ieee80211_supported_band *sbands = &adapter->sbands[band];
+ void *channels = NULL;
+
+ if (band == IEEE80211_BAND_2GHZ) {
+ channels = kmalloc(sizeof(rsi_2ghz_channels), GFP_KERNEL);
+ memcpy(channels,
+ rsi_2ghz_channels,
+ sizeof(rsi_2ghz_channels));
+ sbands->band = IEEE80211_BAND_2GHZ;
+ sbands->n_channels = ARRAY_SIZE(rsi_2ghz_channels);
+ sbands->bitrates = rsi_rates;
+ sbands->n_bitrates = ARRAY_SIZE(rsi_rates);
+ } else {
+ channels = kmalloc(sizeof(rsi_5ghz_channels), GFP_KERNEL);
+ memcpy(channels,
+ rsi_5ghz_channels,
+ sizeof(rsi_5ghz_channels));
+ sbands->band = IEEE80211_BAND_5GHZ;
+ sbands->n_channels = ARRAY_SIZE(rsi_5ghz_channels);
+ sbands->bitrates = &rsi_rates[4];
+ sbands->n_bitrates = ARRAY_SIZE(rsi_rates) - 4;
+ }
+
+ sbands->channels = channels;
+
+ memset(&sbands->ht_cap, 0, sizeof(struct ieee80211_sta_ht_cap));
+ sbands->ht_cap.ht_supported = true;
+ sbands->ht_cap.cap = (IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+ IEEE80211_HT_CAP_SGI_20 |
+ IEEE80211_HT_CAP_SGI_40);
+ sbands->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K;
+ sbands->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE;
+ sbands->ht_cap.mcs.rx_mask[0] = 0xff;
+ sbands->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+ /* sbands->ht_cap.mcs.rx_highest = 0x82; */
+}
+
+/**
+ * rsi_mac80211_attach() - This function is used to de-initialize the
+ * Mac80211 stack.
+ * @adapter: Pointer to the adapter structure.
+ *
+ * Return: None.
+ */
+void rsi_mac80211_detach(struct rsi_hw *adapter)
+{
+ struct ieee80211_hw *hw = adapter->hw;
+
+ if (hw) {
+ ieee80211_stop_queues(hw);
+ ieee80211_unregister_hw(hw);
+ ieee80211_free_hw(hw);
+ }
+
+ rsi_remove_dbgfs(adapter);
+}
+EXPORT_SYMBOL_GPL(rsi_mac80211_detach);
+
+/**
+ * rsi_indicate_tx_status() - This function indicates the transmit status.
+ * @adapter: Pointer to the adapter structure.
+ * @skb: Pointer to the socket buffer structure.
+ * @status: Status
+ *
+ * Return: None.
+ */
+void rsi_indicate_tx_status(struct rsi_hw *adapter,
+ struct sk_buff *skb,
+ int status)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ memset(info->driver_data, 0, IEEE80211_TX_INFO_DRIVER_DATA_SIZE);
+
+ if (!status)
+ info->flags |= IEEE80211_TX_STAT_ACK;
+
+ ieee80211_tx_status_irqsafe(adapter->hw, skb);
+}
+
+/**
+ * rsi_mac80211_tx() - This is the handler that 802.11 module calls for each
+ * transmitted frame.SKB contains the buffer starting
+ * from the IEEE 802.11 header.
+ * @hw: Pointer to the ieee80211_hw structure.
+ * @control: Pointer to the ieee80211_tx_control structure
+ * @skb: Pointer to the socket buffer structure.
+ *
+ * Return: None
+ */
+static void rsi_mac80211_tx(struct ieee80211_hw *hw,
+ struct ieee80211_tx_control *control,
+ struct sk_buff *skb)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+
+ rsi_core_xmit(common, skb);
+}
+
+/**
+ * rsi_mac80211_start() - This is first handler that 802.11 module calls, since
+ * the driver init is complete by then, just
+ * returns success.
+ * @hw: Pointer to the ieee80211_hw structure.
+ *
+ * Return: 0 as success.
+ */
+static int rsi_mac80211_start(struct ieee80211_hw *hw)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+
+ mutex_lock(&common->mutex);
+ common->iface_down = false;
+ mutex_unlock(&common->mutex);
+
+ return 0;
+}
+
+/**
+ * rsi_mac80211_stop() - This is the last handler that 802.11 module calls.
+ * @hw: Pointer to the ieee80211_hw structure.
+ *
+ * Return: None.
+ */
+static void rsi_mac80211_stop(struct ieee80211_hw *hw)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+
+ mutex_lock(&common->mutex);
+ common->iface_down = true;
+ mutex_unlock(&common->mutex);
+}
+
+/**
+ * rsi_mac80211_add_interface() - This function is called when a netdevice
+ * attached to the hardware is enabled.
+ * @hw: Pointer to the ieee80211_hw structure.
+ * @vif: Pointer to the ieee80211_vif structure.
+ *
+ * Return: ret: 0 on success, negative error code on failure.
+ */
+static int rsi_mac80211_add_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+ int ret = -EOPNOTSUPP;
+
+ mutex_lock(&common->mutex);
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ if (!adapter->sc_nvifs) {
+ ++adapter->sc_nvifs;
+ adapter->vifs[0] = vif;
+ ret = rsi_set_vap_capabilities(common, STA_OPMODE);
+ }
+ break;
+ default:
+ rsi_dbg(ERR_ZONE,
+ "%s: Interface type %d not supported\n", __func__,
+ vif->type);
+ }
+ mutex_unlock(&common->mutex);
+
+ return ret;
+}
+
+/**
+ * rsi_mac80211_remove_interface() - This function notifies driver that an
+ * interface is going down.
+ * @hw: Pointer to the ieee80211_hw structure.
+ * @vif: Pointer to the ieee80211_vif structure.
+ *
+ * Return: None.
+ */
+static void rsi_mac80211_remove_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+
+ mutex_lock(&common->mutex);
+ if (vif->type == NL80211_IFTYPE_STATION)
+ adapter->sc_nvifs--;
+
+ if (!memcmp(adapter->vifs[0], vif, sizeof(struct ieee80211_vif)))
+ adapter->vifs[0] = NULL;
+ mutex_unlock(&common->mutex);
+}
+
+/**
+ * rsi_mac80211_config() - This function is a handler for configuration
+ * requests. The stack calls this function to
+ * change hardware configuration, e.g., channel.
+ * @hw: Pointer to the ieee80211_hw structure.
+ * @changed: Changed flags set.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+static int rsi_mac80211_config(struct ieee80211_hw *hw,
+ u32 changed)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+ int status = -EOPNOTSUPP;
+
+ mutex_lock(&common->mutex);
+ if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+ struct ieee80211_channel *curchan = hw->conf.chandef.chan;
+ u16 channel = curchan->hw_value;
+
+ rsi_dbg(INFO_ZONE,
+ "%s: Set channel: %d MHz type: %d channel_no %d\n",
+ __func__, curchan->center_freq,
+ curchan->flags, channel);
+ common->band = curchan->band;
+ status = rsi_set_channel(adapter->priv, channel);
+ }
+ mutex_unlock(&common->mutex);
+
+ return status;
+}
+
+/**
+ * rsi_get_connected_channel() - This function is used to get the current
+ * connected channel number.
+ * @adapter: Pointer to the adapter structure.
+ *
+ * Return: Current connected AP's channel number is returned.
+ */
+u16 rsi_get_connected_channel(struct rsi_hw *adapter)
+{
+ struct ieee80211_vif *vif = adapter->vifs[0];
+ if (vif) {
+ struct ieee80211_bss_conf *bss = &vif->bss_conf;
+ struct ieee80211_channel *channel = bss->chandef.chan;
+ return channel->hw_value;
+ }
+
+ return 0;
+}
+
+/**
+ * rsi_mac80211_bss_info_changed() - This function is a handler for config
+ * requests related to BSS parameters that
+ * may vary during BSS's lifespan.
+ * @hw: Pointer to the ieee80211_hw structure.
+ * @vif: Pointer to the ieee80211_vif structure.
+ * @bss_conf: Pointer to the ieee80211_bss_conf structure.
+ * @changed: Changed flags set.
+ *
+ * Return: None.
+ */
+static void rsi_mac80211_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *bss_conf,
+ u32 changed)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+
+ mutex_lock(&common->mutex);
+ if (changed & BSS_CHANGED_ASSOC) {
+ rsi_dbg(INFO_ZONE, "%s: Changed Association status: %d\n",
+ __func__, bss_conf->assoc);
+ rsi_inform_bss_status(common,
+ bss_conf->assoc,
+ bss_conf->bssid,
+ bss_conf->qos,
+ bss_conf->aid);
+ }
+ mutex_unlock(&common->mutex);
+}
+
+/**
+ * rsi_mac80211_conf_filter() - This function configure the device's RX filter.
+ * @hw: Pointer to the ieee80211_hw structure.
+ * @changed: Changed flags set.
+ * @total_flags: Total initial flags set.
+ * @multicast: Multicast.
+ *
+ * Return: None.
+ */
+static void rsi_mac80211_conf_filter(struct ieee80211_hw *hw,
+ u32 changed_flags,
+ u32 *total_flags,
+ u64 multicast)
+{
+ /* Not doing much here as of now */
+ *total_flags &= RSI_SUPP_FILTERS;
+}
+
+/**
+ * rsi_mac80211_conf_tx() - This function configures TX queue parameters
+ * (EDCF (aifs, cw_min, cw_max), bursting)
+ * for a hardware TX queue.
+ * @hw: Pointer to the ieee80211_hw structure
+ * @vif: Pointer to the ieee80211_vif structure.
+ * @queue: Queue number.
+ * @params: Pointer to ieee80211_tx_queue_params structure.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+static int rsi_mac80211_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 queue,
+ const struct ieee80211_tx_queue_params *params)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+ u8 idx = 0;
+
+ if (queue >= IEEE80211_NUM_ACS)
+ return 0;
+
+ rsi_dbg(INFO_ZONE,
+ "%s: Conf queue %d, aifs: %d, cwmin: %d cwmax: %d, txop: %d\n",
+ __func__, queue, params->aifs,
+ params->cw_min, params->cw_max, params->txop);
+
+ mutex_lock(&common->mutex);
+ /* Map into the way the f/w expects */
+ switch (queue) {
+ case IEEE80211_AC_VO:
+ idx = VO_Q;
+ break;
+ case IEEE80211_AC_VI:
+ idx = VI_Q;
+ break;
+ case IEEE80211_AC_BE:
+ idx = BE_Q;
+ break;
+ case IEEE80211_AC_BK:
+ idx = BK_Q;
+ break;
+ default:
+ idx = BE_Q;
+ break;
+ }
+
+ memcpy(&common->edca_params[idx],
+ params,
+ sizeof(struct ieee80211_tx_queue_params));
+ mutex_unlock(&common->mutex);
+
+ return 0;
+}
+
+/**
+ * rsi_hal_key_config() - This function loads the keys into the firmware.
+ * @hw: Pointer to the ieee80211_hw structure.
+ * @vif: Pointer to the ieee80211_vif structure.
+ * @key: Pointer to the ieee80211_key_conf structure.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+static int rsi_hal_key_config(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_key_conf *key)
+{
+ struct rsi_hw *adapter = hw->priv;
+ int status;
+ u8 key_type;
+
+ if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
+ key_type = RSI_PAIRWISE_KEY;
+ else
+ key_type = RSI_GROUP_KEY;
+
+ rsi_dbg(ERR_ZONE, "%s: Cipher 0x%x key_type: %d key_len: %d\n",
+ __func__, key->cipher, key_type, key->keylen);
+
+ if ((key->cipher == WLAN_CIPHER_SUITE_WEP104) ||
+ (key->cipher == WLAN_CIPHER_SUITE_WEP40)) {
+ status = rsi_hal_load_key(adapter->priv,
+ key->key,
+ key->keylen,
+ RSI_PAIRWISE_KEY,
+ key->keyidx,
+ key->cipher);
+ if (status)
+ return status;
+ }
+ return rsi_hal_load_key(adapter->priv,
+ key->key,
+ key->keylen,
+ key_type,
+ key->keyidx,
+ key->cipher);
+}
+
+/**
+ * rsi_mac80211_set_key() - This function sets type of key to be loaded.
+ * @hw: Pointer to the ieee80211_hw structure.
+ * @cmd: enum set_key_cmd.
+ * @vif: Pointer to the ieee80211_vif structure.
+ * @sta: Pointer to the ieee80211_sta structure.
+ * @key: Pointer to the ieee80211_key_conf structure.
+ *
+ * Return: status: 0 on success, negative error code on failure.
+ */
+static int rsi_mac80211_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 rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+ struct security_info *secinfo = &common->secinfo;
+ int status;
+
+ mutex_lock(&common->mutex);
+ switch (cmd) {
+ case SET_KEY:
+ secinfo->security_enable = true;
+ status = rsi_hal_key_config(hw, vif, key);
+ if (status) {
+ mutex_unlock(&common->mutex);
+ return status;
+ }
+
+ if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
+ secinfo->ptk_cipher = key->cipher;
+ else
+ secinfo->gtk_cipher = key->cipher;
+
+ key->hw_key_idx = key->keyidx;
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+
+ rsi_dbg(ERR_ZONE, "%s: RSI set_key\n", __func__);
+ break;
+
+ case DISABLE_KEY:
+ secinfo->security_enable = false;
+ rsi_dbg(ERR_ZONE, "%s: RSI del key\n", __func__);
+ memset(key, 0, sizeof(struct ieee80211_key_conf));
+ status = rsi_hal_key_config(hw, vif, key);
+ break;
+
+ default:
+ status = -EOPNOTSUPP;
+ break;
+ }
+
+ mutex_unlock(&common->mutex);
+ return status;
+}
+
+/**
+ * rsi_mac80211_ampdu_action() - This function selects the AMPDU action for
+ * the corresponding mlme_action flag and
+ * informs the f/w regarding this.
+ * @hw: Pointer to the ieee80211_hw structure.
+ * @vif: Pointer to the ieee80211_vif structure.
+ * @action: ieee80211_ampdu_mlme_action enum.
+ * @sta: Pointer to the ieee80211_sta structure.
+ * @tid: Traffic identifier.
+ * @ssn: Pointer to ssn value.
+ * @buf_size: Buffer size (for kernel version > 2.6.38).
+ *
+ * Return: status: 0 on success, negative error code on failure.
+ */
+static int rsi_mac80211_ampdu_action(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum ieee80211_ampdu_mlme_action action,
+ struct ieee80211_sta *sta,
+ unsigned short tid,
+ unsigned short *ssn,
+ unsigned char buf_size)
+{
+ int status = -EOPNOTSUPP;
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+ u16 seq_no = 0;
+ u8 ii = 0;
+
+ for (ii = 0; ii < RSI_MAX_VIFS; ii++) {
+ if (vif == adapter->vifs[ii])
+ break;
+ }
+
+ mutex_lock(&common->mutex);
+ rsi_dbg(INFO_ZONE, "%s: AMPDU action %d called\n", __func__, action);
+ if (ssn != NULL)
+ seq_no = *ssn;
+
+ switch (action) {
+ case IEEE80211_AMPDU_RX_START:
+ status = rsi_send_aggregation_params_frame(common,
+ tid,
+ seq_no,
+ buf_size,
+ STA_RX_ADDBA_DONE);
+ break;
+
+ case IEEE80211_AMPDU_RX_STOP:
+ status = rsi_send_aggregation_params_frame(common,
+ tid,
+ 0,
+ buf_size,
+ STA_RX_DELBA);
+ break;
+
+ case IEEE80211_AMPDU_TX_START:
+ common->vif_info[ii].seq_start = seq_no;
+ ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+ break;
+
+ case IEEE80211_AMPDU_TX_STOP_CONT:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+ status = rsi_send_aggregation_params_frame(common,
+ tid,
+ seq_no,
+ buf_size,
+ STA_TX_DELBA);
+ if (!status)
+ ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+ break;
+
+ case IEEE80211_AMPDU_TX_OPERATIONAL:
+ status = rsi_send_aggregation_params_frame(common,
+ tid,
+ common->vif_info[ii]
+ .seq_start,
+ buf_size,
+ STA_TX_ADDBA_DONE);
+ break;
+
+ default:
+ rsi_dbg(ERR_ZONE, "%s: Uknown AMPDU action\n", __func__);
+ break;
+ }
+
+ mutex_unlock(&common->mutex);
+ return status;
+}
+
+/**
+ * rsi_mac80211_set_rts_threshold() - This function sets rts threshold value.
+ * @hw: Pointer to the ieee80211_hw structure.
+ * @value: Rts threshold value.
+ *
+ * Return: 0 on success.
+ */
+static int rsi_mac80211_set_rts_threshold(struct ieee80211_hw *hw,
+ u32 value)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+
+ mutex_lock(&common->mutex);
+ common->rts_threshold = value;
+ mutex_unlock(&common->mutex);
+
+ return 0;
+}
+
+/**
+ * rsi_mac80211_set_rate_mask() - This function sets bitrate_mask to be used.
+ * @hw: Pointer to the ieee80211_hw structure
+ * @vif: Pointer to the ieee80211_vif structure.
+ * @mask: Pointer to the cfg80211_bitrate_mask structure.
+ *
+ * Return: 0 on success.
+ */
+static int rsi_mac80211_set_rate_mask(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const struct cfg80211_bitrate_mask *mask)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+
+ mutex_lock(&common->mutex);
+
+ common->fixedrate_mask[IEEE80211_BAND_2GHZ] = 0;
+
+ if (mask->control[IEEE80211_BAND_2GHZ].legacy == 0xfff) {
+ common->fixedrate_mask[IEEE80211_BAND_2GHZ] =
+ (mask->control[IEEE80211_BAND_2GHZ].ht_mcs[0] << 12);
+ } else {
+ common->fixedrate_mask[IEEE80211_BAND_2GHZ] =
+ mask->control[IEEE80211_BAND_2GHZ].legacy;
+ }
+ mutex_unlock(&common->mutex);
+
+ return 0;
+}
+
+/**
+ * rsi_fill_rx_status() - This function fills rx status in
+ * ieee80211_rx_status structure.
+ * @hw: Pointer to the ieee80211_hw structure.
+ * @skb: Pointer to the socket buffer structure.
+ * @common: Pointer to the driver private structure.
+ * @rxs: Pointer to the ieee80211_rx_status structure.
+ *
+ * Return: None.
+ */
+static void rsi_fill_rx_status(struct ieee80211_hw *hw,
+ struct sk_buff *skb,
+ struct rsi_common *common,
+ struct ieee80211_rx_status *rxs)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct skb_info *rx_params = (struct skb_info *)info->driver_data;
+ struct ieee80211_hdr *hdr;
+ char rssi = rx_params->rssi;
+ u8 hdrlen = 0;
+ u8 channel = rx_params->channel;
+ s32 freq;
+
+ hdr = ((struct ieee80211_hdr *)(skb->data));
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
+
+ memset(info, 0, sizeof(struct ieee80211_tx_info));
+
+ rxs->signal = -(rssi);
+
+ if (channel <= 14)
+ rxs->band = IEEE80211_BAND_2GHZ;
+ else
+ rxs->band = IEEE80211_BAND_5GHZ;
+
+ freq = ieee80211_channel_to_frequency(channel, rxs->band);
+
+ if (freq)
+ rxs->freq = freq;
+
+ if (ieee80211_has_protected(hdr->frame_control)) {
+ if (rsi_is_cipher_wep(common)) {
+ memmove(skb->data + 4, skb->data, hdrlen);
+ skb_pull(skb, 4);
+ } else {
+ memmove(skb->data + 8, skb->data, hdrlen);
+ skb_pull(skb, 8);
+ rxs->flag |= RX_FLAG_MMIC_STRIPPED;
+ }
+ rxs->flag |= RX_FLAG_DECRYPTED;
+ rxs->flag |= RX_FLAG_IV_STRIPPED;
+ }
+}
+
+/**
+ * rsi_indicate_pkt_to_os() - This function sends recieved packet to mac80211.
+ * @common: Pointer to the driver private structure.
+ * @skb: Pointer to the socket buffer structure.
+ *
+ * Return: None.
+ */
+void rsi_indicate_pkt_to_os(struct rsi_common *common,
+ struct sk_buff *skb)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct ieee80211_hw *hw = adapter->hw;
+ struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
+
+ if ((common->iface_down) || (!adapter->sc_nvifs)) {
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ /* filling in the ieee80211_rx_status flags */
+ rsi_fill_rx_status(hw, skb, common, rx_status);
+
+ ieee80211_rx_irqsafe(hw, skb);
+}
+
+static void rsi_set_min_rate(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta,
+ struct rsi_common *common)
+{
+ u8 band = hw->conf.chandef.chan->band;
+ u8 ii;
+ u32 rate_bitmap;
+ bool matched = false;
+
+ common->bitrate_mask[band] = sta->supp_rates[band];
+
+ rate_bitmap = (common->fixedrate_mask[band] & sta->supp_rates[band]);
+
+ if (rate_bitmap & 0xfff) {
+ /* Find out the min rate */
+ for (ii = 0; ii < ARRAY_SIZE(rsi_rates); ii++) {
+ if (rate_bitmap & BIT(ii)) {
+ common->min_rate = rsi_rates[ii].hw_value;
+ matched = true;
+ break;
+ }
+ }
+ }
+
+ common->vif_info[0].is_ht = sta->ht_cap.ht_supported;
+
+ if ((common->vif_info[0].is_ht) && (rate_bitmap >> 12)) {
+ for (ii = 0; ii < ARRAY_SIZE(rsi_mcsrates); ii++) {
+ if ((rate_bitmap >> 12) & BIT(ii)) {
+ common->min_rate = rsi_mcsrates[ii];
+ matched = true;
+ break;
+ }
+ }
+ }
+
+ if (!matched)
+ common->min_rate = 0xffff;
+}
+
+/**
+ * rsi_mac80211_sta_add() - This function notifies driver about a peer getting
+ * connected.
+ * @hw: pointer to the ieee80211_hw structure.
+ * @vif: Pointer to the ieee80211_vif structure.
+ * @sta: Pointer to the ieee80211_sta structure.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_mac80211_sta_add(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+
+ mutex_lock(&common->mutex);
+
+ rsi_set_min_rate(hw, sta, common);
+
+ if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ||
+ (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40)) {
+ common->vif_info[0].sgi = true;
+ }
+
+ if (sta->ht_cap.ht_supported)
+ ieee80211_start_tx_ba_session(sta, 0, 0);
+
+ mutex_unlock(&common->mutex);
+
+ return 0;
+}
+
+/**
+ * rsi_mac80211_sta_remove() - This function notifies driver about a peer
+ * getting disconnected.
+ * @hw: Pointer to the ieee80211_hw structure.
+ * @vif: Pointer to the ieee80211_vif structure.
+ * @sta: Pointer to the ieee80211_sta structure.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_mac80211_sta_remove(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+
+ mutex_lock(&common->mutex);
+ /* Resetting all the fields to default values */
+ common->bitrate_mask[IEEE80211_BAND_2GHZ] = 0;
+ common->bitrate_mask[IEEE80211_BAND_5GHZ] = 0;
+ common->min_rate = 0xffff;
+ common->vif_info[0].is_ht = false;
+ common->vif_info[0].sgi = false;
+ common->vif_info[0].seq_start = 0;
+ common->secinfo.ptk_cipher = 0;
+ common->secinfo.gtk_cipher = 0;
+ mutex_unlock(&common->mutex);
+
+ return 0;
+}
+
+static struct ieee80211_ops mac80211_ops = {
+ .tx = rsi_mac80211_tx,
+ .start = rsi_mac80211_start,
+ .stop = rsi_mac80211_stop,
+ .add_interface = rsi_mac80211_add_interface,
+ .remove_interface = rsi_mac80211_remove_interface,
+ .config = rsi_mac80211_config,
+ .bss_info_changed = rsi_mac80211_bss_info_changed,
+ .conf_tx = rsi_mac80211_conf_tx,
+ .configure_filter = rsi_mac80211_conf_filter,
+ .set_key = rsi_mac80211_set_key,
+ .set_rts_threshold = rsi_mac80211_set_rts_threshold,
+ .set_bitrate_mask = rsi_mac80211_set_rate_mask,
+ .ampdu_action = rsi_mac80211_ampdu_action,
+ .sta_add = rsi_mac80211_sta_add,
+ .sta_remove = rsi_mac80211_sta_remove,
+};
+
+/**
+ * rsi_mac80211_attach() - This function is used to initialize Mac80211 stack.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int rsi_mac80211_attach(struct rsi_common *common)
+{
+ int status = 0;
+ struct ieee80211_hw *hw = NULL;
+ struct wiphy *wiphy = NULL;
+ struct rsi_hw *adapter = common->priv;
+ u8 addr_mask[ETH_ALEN] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x3};
+
+ rsi_dbg(INIT_ZONE, "%s: Performing mac80211 attach\n", __func__);
+
+ hw = ieee80211_alloc_hw(sizeof(struct rsi_hw), &mac80211_ops);
+ if (!hw) {
+ rsi_dbg(ERR_ZONE, "%s: ieee80211 hw alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ wiphy = hw->wiphy;
+
+ SET_IEEE80211_DEV(hw, adapter->device);
+
+ hw->priv = adapter;
+ adapter->hw = hw;
+
+ hw->flags = IEEE80211_HW_SIGNAL_DBM |
+ IEEE80211_HW_HAS_RATE_CONTROL |
+ IEEE80211_HW_AMPDU_AGGREGATION |
+ 0;
+
+ hw->queues = MAX_HW_QUEUES;
+ hw->extra_tx_headroom = RSI_NEEDED_HEADROOM;
+
+ hw->max_rates = 1;
+ hw->max_rate_tries = MAX_RETRIES;
+
+ hw->max_tx_aggregation_subframes = 6;
+ rsi_register_rates_channels(adapter, IEEE80211_BAND_2GHZ);
+ hw->rate_control_algorithm = "AARF";
+
+ SET_IEEE80211_PERM_ADDR(hw, common->mac_addr);
+ ether_addr_copy(hw->wiphy->addr_mask, addr_mask);
+
+ wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+ wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+ wiphy->retry_short = RETRY_SHORT;
+ wiphy->retry_long = RETRY_LONG;
+ wiphy->frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
+ wiphy->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
+ wiphy->flags = 0;
+
+ wiphy->available_antennas_rx = 1;
+ wiphy->available_antennas_tx = 1;
+ wiphy->bands[IEEE80211_BAND_2GHZ] =
+ &adapter->sbands[IEEE80211_BAND_2GHZ];
+
+ status = ieee80211_register_hw(hw);
+ if (status)
+ return status;
+
+ return rsi_init_dbgfs(adapter);
+}
diff --git a/drivers/net/wireless/rsi/rsi_91x_main.c b/drivers/net/wireless/rsi/rsi_91x_main.c
new file mode 100644
index 000000000000..410a4a423578
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_91x_main.c
@@ -0,0 +1,270 @@
+/**
+ * Copyright (c) 2014 Redpine Signals Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include "rsi_mgmt.h"
+#include "rsi_common.h"
+
+u32 rsi_zone_enabled = /* INFO_ZONE |
+ INIT_ZONE |
+ MGMT_TX_ZONE |
+ MGMT_RX_ZONE |
+ DATA_TX_ZONE |
+ DATA_RX_ZONE |
+ FSM_ZONE |
+ ISR_ZONE | */
+ ERR_ZONE |
+ 0;
+EXPORT_SYMBOL_GPL(rsi_zone_enabled);
+
+/**
+ * rsi_prepare_skb() - This function prepares the skb.
+ * @common: Pointer to the driver private structure.
+ * @buffer: Pointer to the packet data.
+ * @pkt_len: Length of the packet.
+ * @extended_desc: Extended descriptor.
+ *
+ * Return: Successfully skb.
+ */
+static struct sk_buff *rsi_prepare_skb(struct rsi_common *common,
+ u8 *buffer,
+ u32 pkt_len,
+ u8 extended_desc)
+{
+ struct ieee80211_tx_info *info;
+ struct skb_info *rx_params;
+ struct sk_buff *skb = NULL;
+ u8 payload_offset;
+
+ if (WARN(!pkt_len, "%s: Dummy pkt received", __func__))
+ return NULL;
+
+ if (pkt_len > (RSI_RCV_BUFFER_LEN * 4)) {
+ rsi_dbg(ERR_ZONE, "%s: Pkt size > max rx buf size %d\n",
+ __func__, pkt_len);
+ pkt_len = RSI_RCV_BUFFER_LEN * 4;
+ }
+
+ pkt_len -= extended_desc;
+ skb = dev_alloc_skb(pkt_len + FRAME_DESC_SZ);
+ if (skb == NULL)
+ return NULL;
+
+ payload_offset = (extended_desc + FRAME_DESC_SZ);
+ skb_put(skb, pkt_len);
+ memcpy((skb->data), (buffer + payload_offset), skb->len);
+
+ info = IEEE80211_SKB_CB(skb);
+ rx_params = (struct skb_info *)info->driver_data;
+ rx_params->rssi = rsi_get_rssi(buffer);
+ rx_params->channel = rsi_get_connected_channel(common->priv);
+
+ return skb;
+}
+
+/**
+ * rsi_read_pkt() - This function reads frames from the card.
+ * @common: Pointer to the driver private structure.
+ * @rcv_pkt_len: Received pkt length. In case of USB it is 0.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int rsi_read_pkt(struct rsi_common *common, s32 rcv_pkt_len)
+{
+ u8 *frame_desc = NULL, extended_desc = 0;
+ u32 index, length = 0, queueno = 0;
+ u16 actual_length = 0, offset;
+ struct sk_buff *skb = NULL;
+
+ index = 0;
+ do {
+ frame_desc = &common->rx_data_pkt[index];
+ actual_length = *(u16 *)&frame_desc[0];
+ offset = *(u16 *)&frame_desc[2];
+
+ queueno = rsi_get_queueno(frame_desc, offset);
+ length = rsi_get_length(frame_desc, offset);
+ extended_desc = rsi_get_extended_desc(frame_desc, offset);
+
+ switch (queueno) {
+ case RSI_WIFI_DATA_Q:
+ skb = rsi_prepare_skb(common,
+ (frame_desc + offset),
+ length,
+ extended_desc);
+ if (skb == NULL)
+ goto fail;
+
+ rsi_indicate_pkt_to_os(common, skb);
+ break;
+
+ case RSI_WIFI_MGMT_Q:
+ rsi_mgmt_pkt_recv(common, (frame_desc + offset));
+ break;
+
+ default:
+ rsi_dbg(ERR_ZONE, "%s: pkt from invalid queue: %d\n",
+ __func__, queueno);
+ goto fail;
+ }
+
+ index += actual_length;
+ rcv_pkt_len -= actual_length;
+ } while (rcv_pkt_len > 0);
+
+ return 0;
+fail:
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(rsi_read_pkt);
+
+/**
+ * rsi_tx_scheduler_thread() - This function is a kernel thread to send the
+ * packets to the device.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: None.
+ */
+static void rsi_tx_scheduler_thread(struct rsi_common *common)
+{
+ struct rsi_hw *adapter = common->priv;
+ u32 timeout = EVENT_WAIT_FOREVER;
+
+ do {
+ if (adapter->determine_event_timeout)
+ timeout = adapter->determine_event_timeout(adapter);
+ rsi_wait_event(&common->tx_thread.event, timeout);
+ rsi_reset_event(&common->tx_thread.event);
+
+ if (common->init_done)
+ rsi_core_qos_processor(common);
+ } while (atomic_read(&common->tx_thread.thread_done) == 0);
+ complete_and_exit(&common->tx_thread.completion, 0);
+}
+
+/**
+ * rsi_91x_init() - This function initializes os interface operations.
+ * @void: Void.
+ *
+ * Return: Pointer to the adapter structure on success, NULL on failure .
+ */
+struct rsi_hw *rsi_91x_init(void)
+{
+ struct rsi_hw *adapter = NULL;
+ struct rsi_common *common = NULL;
+ u8 ii = 0;
+
+ adapter = kzalloc(sizeof(*adapter), GFP_KERNEL);
+ if (!adapter)
+ return NULL;
+
+ adapter->priv = kzalloc(sizeof(*common), GFP_KERNEL);
+ if (adapter->priv == NULL) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of memory\n",
+ __func__);
+ kfree(adapter);
+ return NULL;
+ } else {
+ common = adapter->priv;
+ common->priv = adapter;
+ }
+
+ for (ii = 0; ii < NUM_SOFT_QUEUES; ii++)
+ skb_queue_head_init(&common->tx_queue[ii]);
+
+ rsi_init_event(&common->tx_thread.event);
+ mutex_init(&common->mutex);
+ mutex_init(&common->tx_rxlock);
+
+ if (rsi_create_kthread(common,
+ &common->tx_thread,
+ rsi_tx_scheduler_thread,
+ "Tx-Thread")) {
+ rsi_dbg(ERR_ZONE, "%s: Unable to init tx thrd\n", __func__);
+ goto err;
+ }
+
+ common->init_done = true;
+ return adapter;
+
+err:
+ kfree(common);
+ kfree(adapter);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(rsi_91x_init);
+
+/**
+ * rsi_91x_deinit() - This function de-intializes os intf operations.
+ * @adapter: Pointer to the adapter structure.
+ *
+ * Return: None.
+ */
+void rsi_91x_deinit(struct rsi_hw *adapter)
+{
+ struct rsi_common *common = adapter->priv;
+ u8 ii;
+
+ rsi_dbg(INFO_ZONE, "%s: Performing deinit os ops\n", __func__);
+
+ rsi_kill_thread(&common->tx_thread);
+
+ for (ii = 0; ii < NUM_SOFT_QUEUES; ii++)
+ skb_queue_purge(&common->tx_queue[ii]);
+
+ common->init_done = false;
+
+ kfree(common);
+ kfree(adapter->rsi_dev);
+ kfree(adapter);
+}
+EXPORT_SYMBOL_GPL(rsi_91x_deinit);
+
+/**
+ * rsi_91x_hal_module_init() - This function is invoked when the module is
+ * loaded into the kernel.
+ * It registers the client driver.
+ * @void: Void.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_91x_hal_module_init(void)
+{
+ rsi_dbg(INIT_ZONE, "%s: Module init called\n", __func__);
+ return 0;
+}
+
+/**
+ * rsi_91x_hal_module_exit() - This function is called at the time of
+ * removing/unloading the module.
+ * It unregisters the client driver.
+ * @void: Void.
+ *
+ * Return: None.
+ */
+static void rsi_91x_hal_module_exit(void)
+{
+ rsi_dbg(INIT_ZONE, "%s: Module exit called\n", __func__);
+}
+
+module_init(rsi_91x_hal_module_init);
+module_exit(rsi_91x_hal_module_exit);
+MODULE_AUTHOR("Redpine Signals Inc");
+MODULE_DESCRIPTION("Station driver for RSI 91x devices");
+MODULE_SUPPORTED_DEVICE("RSI-91x");
+MODULE_VERSION("0.1");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c
new file mode 100644
index 000000000000..f09c72ef55d5
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c
@@ -0,0 +1,1302 @@
+/**
+ * Copyright (c) 2014 Redpine Signals Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/etherdevice.h>
+#include "rsi_mgmt.h"
+#include "rsi_common.h"
+
+static struct bootup_params boot_params_20 = {
+ .magic_number = cpu_to_le16(0x5aa5),
+ .crystal_good_time = 0x0,
+ .valid = cpu_to_le32(VALID_20),
+ .reserved_for_valids = 0x0,
+ .bootup_mode_info = 0x0,
+ .digital_loop_back_params = 0x0,
+ .rtls_timestamp_en = 0x0,
+ .host_spi_intr_cfg = 0x0,
+ .device_clk_info = {{
+ .pll_config_g = {
+ .tapll_info_g = {
+ .pll_reg_1 = cpu_to_le16((TA_PLL_N_VAL_20 << 8)|
+ (TA_PLL_M_VAL_20)),
+ .pll_reg_2 = cpu_to_le16(TA_PLL_P_VAL_20),
+ },
+ .pll960_info_g = {
+ .pll_reg_1 = cpu_to_le16((PLL960_P_VAL_20 << 8)|
+ (PLL960_N_VAL_20)),
+ .pll_reg_2 = cpu_to_le16(PLL960_M_VAL_20),
+ .pll_reg_3 = 0x0,
+ },
+ .afepll_info_g = {
+ .pll_reg = cpu_to_le16(0x9f0),
+ }
+ },
+ .switch_clk_g = {
+ .switch_clk_info = cpu_to_le16(BIT(3)),
+ .bbp_lmac_clk_reg_val = cpu_to_le16(0x121),
+ .umac_clock_reg_config = 0x0,
+ .qspi_uart_clock_reg_config = 0x0
+ }
+ },
+ {
+ .pll_config_g = {
+ .tapll_info_g = {
+ .pll_reg_1 = cpu_to_le16((TA_PLL_N_VAL_20 << 8)|
+ (TA_PLL_M_VAL_20)),
+ .pll_reg_2 = cpu_to_le16(TA_PLL_P_VAL_20),
+ },
+ .pll960_info_g = {
+ .pll_reg_1 = cpu_to_le16((PLL960_P_VAL_20 << 8)|
+ (PLL960_N_VAL_20)),
+ .pll_reg_2 = cpu_to_le16(PLL960_M_VAL_20),
+ .pll_reg_3 = 0x0,
+ },
+ .afepll_info_g = {
+ .pll_reg = cpu_to_le16(0x9f0),
+ }
+ },
+ .switch_clk_g = {
+ .switch_clk_info = 0x0,
+ .bbp_lmac_clk_reg_val = 0x0,
+ .umac_clock_reg_config = 0x0,
+ .qspi_uart_clock_reg_config = 0x0
+ }
+ },
+ {
+ .pll_config_g = {
+ .tapll_info_g = {
+ .pll_reg_1 = cpu_to_le16((TA_PLL_N_VAL_20 << 8)|
+ (TA_PLL_M_VAL_20)),
+ .pll_reg_2 = cpu_to_le16(TA_PLL_P_VAL_20),
+ },
+ .pll960_info_g = {
+ .pll_reg_1 = cpu_to_le16((PLL960_P_VAL_20 << 8)|
+ (PLL960_N_VAL_20)),
+ .pll_reg_2 = cpu_to_le16(PLL960_M_VAL_20),
+ .pll_reg_3 = 0x0,
+ },
+ .afepll_info_g = {
+ .pll_reg = cpu_to_le16(0x9f0),
+ }
+ },
+ .switch_clk_g = {
+ .switch_clk_info = 0x0,
+ .bbp_lmac_clk_reg_val = 0x0,
+ .umac_clock_reg_config = 0x0,
+ .qspi_uart_clock_reg_config = 0x0
+ }
+ } },
+ .buckboost_wakeup_cnt = 0x0,
+ .pmu_wakeup_wait = 0x0,
+ .shutdown_wait_time = 0x0,
+ .pmu_slp_clkout_sel = 0x0,
+ .wdt_prog_value = 0x0,
+ .wdt_soc_rst_delay = 0x0,
+ .dcdc_operation_mode = 0x0,
+ .soc_reset_wait_cnt = 0x0
+};
+
+static struct bootup_params boot_params_40 = {
+ .magic_number = cpu_to_le16(0x5aa5),
+ .crystal_good_time = 0x0,
+ .valid = cpu_to_le32(VALID_40),
+ .reserved_for_valids = 0x0,
+ .bootup_mode_info = 0x0,
+ .digital_loop_back_params = 0x0,
+ .rtls_timestamp_en = 0x0,
+ .host_spi_intr_cfg = 0x0,
+ .device_clk_info = {{
+ .pll_config_g = {
+ .tapll_info_g = {
+ .pll_reg_1 = cpu_to_le16((TA_PLL_N_VAL_40 << 8)|
+ (TA_PLL_M_VAL_40)),
+ .pll_reg_2 = cpu_to_le16(TA_PLL_P_VAL_40),
+ },
+ .pll960_info_g = {
+ .pll_reg_1 = cpu_to_le16((PLL960_P_VAL_40 << 8)|
+ (PLL960_N_VAL_40)),
+ .pll_reg_2 = cpu_to_le16(PLL960_M_VAL_40),
+ .pll_reg_3 = 0x0,
+ },
+ .afepll_info_g = {
+ .pll_reg = cpu_to_le16(0x9f0),
+ }
+ },
+ .switch_clk_g = {
+ .switch_clk_info = cpu_to_le16(0x09),
+ .bbp_lmac_clk_reg_val = cpu_to_le16(0x1121),
+ .umac_clock_reg_config = cpu_to_le16(0x48),
+ .qspi_uart_clock_reg_config = 0x0
+ }
+ },
+ {
+ .pll_config_g = {
+ .tapll_info_g = {
+ .pll_reg_1 = cpu_to_le16((TA_PLL_N_VAL_40 << 8)|
+ (TA_PLL_M_VAL_40)),
+ .pll_reg_2 = cpu_to_le16(TA_PLL_P_VAL_40),
+ },
+ .pll960_info_g = {
+ .pll_reg_1 = cpu_to_le16((PLL960_P_VAL_40 << 8)|
+ (PLL960_N_VAL_40)),
+ .pll_reg_2 = cpu_to_le16(PLL960_M_VAL_40),
+ .pll_reg_3 = 0x0,
+ },
+ .afepll_info_g = {
+ .pll_reg = cpu_to_le16(0x9f0),
+ }
+ },
+ .switch_clk_g = {
+ .switch_clk_info = 0x0,
+ .bbp_lmac_clk_reg_val = 0x0,
+ .umac_clock_reg_config = 0x0,
+ .qspi_uart_clock_reg_config = 0x0
+ }
+ },
+ {
+ .pll_config_g = {
+ .tapll_info_g = {
+ .pll_reg_1 = cpu_to_le16((TA_PLL_N_VAL_40 << 8)|
+ (TA_PLL_M_VAL_40)),
+ .pll_reg_2 = cpu_to_le16(TA_PLL_P_VAL_40),
+ },
+ .pll960_info_g = {
+ .pll_reg_1 = cpu_to_le16((PLL960_P_VAL_40 << 8)|
+ (PLL960_N_VAL_40)),
+ .pll_reg_2 = cpu_to_le16(PLL960_M_VAL_40),
+ .pll_reg_3 = 0x0,
+ },
+ .afepll_info_g = {
+ .pll_reg = cpu_to_le16(0x9f0),
+ }
+ },
+ .switch_clk_g = {
+ .switch_clk_info = 0x0,
+ .bbp_lmac_clk_reg_val = 0x0,
+ .umac_clock_reg_config = 0x0,
+ .qspi_uart_clock_reg_config = 0x0
+ }
+ } },
+ .buckboost_wakeup_cnt = 0x0,
+ .pmu_wakeup_wait = 0x0,
+ .shutdown_wait_time = 0x0,
+ .pmu_slp_clkout_sel = 0x0,
+ .wdt_prog_value = 0x0,
+ .wdt_soc_rst_delay = 0x0,
+ .dcdc_operation_mode = 0x0,
+ .soc_reset_wait_cnt = 0x0
+};
+
+static u16 mcs[] = {13, 26, 39, 52, 78, 104, 117, 130};
+
+/**
+ * rsi_set_default_parameters() - This function sets default parameters.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: none
+ */
+static void rsi_set_default_parameters(struct rsi_common *common)
+{
+ common->band = IEEE80211_BAND_2GHZ;
+ common->channel_width = BW_20MHZ;
+ common->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
+ common->channel = 1;
+ common->min_rate = 0xffff;
+ common->fsm_state = FSM_CARD_NOT_READY;
+ common->iface_down = true;
+}
+
+/**
+ * rsi_set_contention_vals() - This function sets the contention values for the
+ * backoff procedure.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: None.
+ */
+static void rsi_set_contention_vals(struct rsi_common *common)
+{
+ u8 ii = 0;
+
+ for (; ii < NUM_EDCA_QUEUES; ii++) {
+ common->tx_qinfo[ii].wme_params =
+ (((common->edca_params[ii].cw_min / 2) +
+ (common->edca_params[ii].aifs)) *
+ WMM_SHORT_SLOT_TIME + SIFS_DURATION);
+ common->tx_qinfo[ii].weight = common->tx_qinfo[ii].wme_params;
+ common->tx_qinfo[ii].pkt_contended = 0;
+ }
+}
+
+/**
+ * rsi_send_internal_mgmt_frame() - This function sends management frames to
+ * firmware.Also schedules packet to queue
+ * for transmission.
+ * @common: Pointer to the driver private structure.
+ * @skb: Pointer to the socket buffer structure.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_send_internal_mgmt_frame(struct rsi_common *common,
+ struct sk_buff *skb)
+{
+ struct skb_info *tx_params;
+
+ if (skb == NULL) {
+ rsi_dbg(ERR_ZONE, "%s: Unable to allocate skb\n", __func__);
+ return -ENOMEM;
+ }
+ tx_params = (struct skb_info *)&IEEE80211_SKB_CB(skb)->driver_data;
+ tx_params->flags |= INTERNAL_MGMT_PKT;
+ skb_queue_tail(&common->tx_queue[MGMT_SOFT_Q], skb);
+ rsi_set_event(&common->tx_thread.event);
+ return 0;
+}
+
+/**
+ * rsi_load_radio_caps() - This function is used to send radio capabilities
+ * values to firmware.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: 0 on success, corresponding negative error code on failure.
+ */
+static int rsi_load_radio_caps(struct rsi_common *common)
+{
+ struct rsi_radio_caps *radio_caps;
+ struct rsi_hw *adapter = common->priv;
+ struct ieee80211_hw *hw = adapter->hw;
+ u16 inx = 0;
+ u8 ii;
+ u8 radio_id = 0;
+ u16 gc[20] = {0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0};
+ struct ieee80211_conf *conf = &hw->conf;
+ struct sk_buff *skb;
+
+ rsi_dbg(INFO_ZONE, "%s: Sending rate symbol req frame\n", __func__);
+
+ skb = dev_alloc_skb(sizeof(struct rsi_radio_caps));
+
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memset(skb->data, 0, sizeof(struct rsi_radio_caps));
+ radio_caps = (struct rsi_radio_caps *)skb->data;
+
+ radio_caps->desc_word[1] = cpu_to_le16(RADIO_CAPABILITIES);
+ radio_caps->desc_word[4] = cpu_to_le16(RSI_RF_TYPE << 8);
+
+ if (common->channel_width == BW_40MHZ) {
+ radio_caps->desc_word[7] |= cpu_to_le16(RSI_LMAC_CLOCK_80MHZ);
+ radio_caps->desc_word[7] |= cpu_to_le16(RSI_ENABLE_40MHZ);
+ if (common->channel_width) {
+ radio_caps->desc_word[5] =
+ cpu_to_le16(common->channel_width << 12);
+ radio_caps->desc_word[5] |= cpu_to_le16(FULL40M_ENABLE);
+ }
+
+ if (conf_is_ht40_minus(conf)) {
+ radio_caps->desc_word[5] = 0;
+ radio_caps->desc_word[5] |=
+ cpu_to_le16(LOWER_20_ENABLE);
+ radio_caps->desc_word[5] |=
+ cpu_to_le16(LOWER_20_ENABLE >> 12);
+ }
+
+ if (conf_is_ht40_plus(conf)) {
+ radio_caps->desc_word[5] = 0;
+ radio_caps->desc_word[5] |=
+ cpu_to_le16(UPPER_20_ENABLE);
+ radio_caps->desc_word[5] |=
+ cpu_to_le16(UPPER_20_ENABLE >> 12);
+ }
+ }
+
+ radio_caps->desc_word[7] |= cpu_to_le16(radio_id << 8);
+
+ for (ii = 0; ii < MAX_HW_QUEUES; ii++) {
+ radio_caps->qos_params[ii].cont_win_min_q = cpu_to_le16(3);
+ radio_caps->qos_params[ii].cont_win_max_q = cpu_to_le16(0x3f);
+ radio_caps->qos_params[ii].aifsn_val_q = cpu_to_le16(2);
+ radio_caps->qos_params[ii].txop_q = 0;
+ }
+
+ for (ii = 0; ii < MAX_HW_QUEUES - 4; ii++) {
+ radio_caps->qos_params[ii].cont_win_min_q =
+ cpu_to_le16(common->edca_params[ii].cw_min);
+ radio_caps->qos_params[ii].cont_win_max_q =
+ cpu_to_le16(common->edca_params[ii].cw_max);
+ radio_caps->qos_params[ii].aifsn_val_q =
+ cpu_to_le16((common->edca_params[ii].aifs) << 8);
+ radio_caps->qos_params[ii].txop_q =
+ cpu_to_le16(common->edca_params[ii].txop);
+ }
+
+ memcpy(&common->rate_pwr[0], &gc[0], 40);
+ for (ii = 0; ii < 20; ii++)
+ radio_caps->gcpd_per_rate[inx++] =
+ cpu_to_le16(common->rate_pwr[ii] & 0x00FF);
+
+ radio_caps->desc_word[0] = cpu_to_le16((sizeof(struct rsi_radio_caps) -
+ FRAME_DESC_SZ) |
+ (RSI_WIFI_MGMT_Q << 12));
+
+
+ skb_put(skb, (sizeof(struct rsi_radio_caps)));
+
+ return rsi_send_internal_mgmt_frame(common, skb);
+}
+
+/**
+ * rsi_mgmt_pkt_to_core() - This function is the entry point for Mgmt module.
+ * @common: Pointer to the driver private structure.
+ * @msg: Pointer to received packet.
+ * @msg_len: Length of the recieved packet.
+ * @type: Type of recieved packet.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_mgmt_pkt_to_core(struct rsi_common *common,
+ u8 *msg,
+ s32 msg_len,
+ u8 type)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct ieee80211_tx_info *info;
+ struct skb_info *rx_params;
+ u8 pad_bytes = msg[4];
+ u8 pkt_recv;
+ struct sk_buff *skb;
+ char *buffer;
+
+ if (type == RX_DOT11_MGMT) {
+ if (!adapter->sc_nvifs)
+ return -ENOLINK;
+
+ msg_len -= pad_bytes;
+ if ((msg_len <= 0) || (!msg)) {
+ rsi_dbg(MGMT_RX_ZONE, "Invalid rx msg of len = %d\n",
+ __func__, msg_len);
+ return -EINVAL;
+ }
+
+ skb = dev_alloc_skb(msg_len);
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed to allocate skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ buffer = skb_put(skb, msg_len);
+
+ memcpy(buffer,
+ (u8 *)(msg + FRAME_DESC_SZ + pad_bytes),
+ msg_len);
+
+ pkt_recv = buffer[0];
+
+ info = IEEE80211_SKB_CB(skb);
+ rx_params = (struct skb_info *)info->driver_data;
+ rx_params->rssi = rsi_get_rssi(msg);
+ rx_params->channel = rsi_get_channel(msg);
+ rsi_indicate_pkt_to_os(common, skb);
+ } else {
+ rsi_dbg(MGMT_TX_ZONE, "%s: Internal Packet\n", __func__);
+ }
+
+ return 0;
+}
+
+/**
+ * rsi_hal_send_sta_notify_frame() - This function sends the station notify
+ * frame to firmware.
+ * @common: Pointer to the driver private structure.
+ * @opmode: Operating mode of device.
+ * @notify_event: Notification about station connection.
+ * @bssid: bssid.
+ * @qos_enable: Qos is enabled.
+ * @aid: Aid (unique for all STA).
+ *
+ * Return: status: 0 on success, corresponding negative error code on failure.
+ */
+static int rsi_hal_send_sta_notify_frame(struct rsi_common *common,
+ u8 opmode,
+ u8 notify_event,
+ const unsigned char *bssid,
+ u8 qos_enable,
+ u16 aid)
+{
+ struct sk_buff *skb = NULL;
+ struct rsi_peer_notify *peer_notify;
+ u16 vap_id = 0;
+ int status;
+
+ rsi_dbg(MGMT_TX_ZONE, "%s: Sending sta notify frame\n", __func__);
+
+ skb = dev_alloc_skb(sizeof(struct rsi_peer_notify));
+
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memset(skb->data, 0, sizeof(struct rsi_peer_notify));
+ peer_notify = (struct rsi_peer_notify *)skb->data;
+
+ peer_notify->command = cpu_to_le16(opmode << 1);
+
+ switch (notify_event) {
+ case STA_CONNECTED:
+ peer_notify->command |= cpu_to_le16(RSI_ADD_PEER);
+ break;
+ case STA_DISCONNECTED:
+ peer_notify->command |= cpu_to_le16(RSI_DELETE_PEER);
+ break;
+ default:
+ break;
+ }
+
+ peer_notify->command |= cpu_to_le16((aid & 0xfff) << 4);
+ ether_addr_copy(peer_notify->mac_addr, bssid);
+
+ peer_notify->sta_flags = cpu_to_le32((qos_enable) ? 1 : 0);
+
+ peer_notify->desc_word[0] =
+ cpu_to_le16((sizeof(struct rsi_peer_notify) - FRAME_DESC_SZ) |
+ (RSI_WIFI_MGMT_Q << 12));
+ peer_notify->desc_word[1] = cpu_to_le16(PEER_NOTIFY);
+ peer_notify->desc_word[7] |= cpu_to_le16(vap_id << 8);
+
+ skb_put(skb, sizeof(struct rsi_peer_notify));
+
+ status = rsi_send_internal_mgmt_frame(common, skb);
+
+ if (!status && qos_enable) {
+ rsi_set_contention_vals(common);
+ status = rsi_load_radio_caps(common);
+ }
+ return status;
+}
+
+/**
+ * rsi_send_aggregation_params_frame() - This function sends the ampdu
+ * indication frame to firmware.
+ * @common: Pointer to the driver private structure.
+ * @tid: traffic identifier.
+ * @ssn: ssn.
+ * @buf_size: buffer size.
+ * @event: notification about station connection.
+ *
+ * Return: 0 on success, corresponding negative error code on failure.
+ */
+int rsi_send_aggregation_params_frame(struct rsi_common *common,
+ u16 tid,
+ u16 ssn,
+ u8 buf_size,
+ u8 event)
+{
+ struct sk_buff *skb = NULL;
+ struct rsi_mac_frame *mgmt_frame;
+ u8 peer_id = 0;
+
+ skb = dev_alloc_skb(FRAME_DESC_SZ);
+
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memset(skb->data, 0, FRAME_DESC_SZ);
+ mgmt_frame = (struct rsi_mac_frame *)skb->data;
+
+ rsi_dbg(MGMT_TX_ZONE, "%s: Sending AMPDU indication frame\n", __func__);
+
+ mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12);
+ mgmt_frame->desc_word[1] = cpu_to_le16(AMPDU_IND);
+
+ if (event == STA_TX_ADDBA_DONE) {
+ mgmt_frame->desc_word[4] = cpu_to_le16(ssn);
+ mgmt_frame->desc_word[5] = cpu_to_le16(buf_size);
+ mgmt_frame->desc_word[7] =
+ cpu_to_le16((tid | (START_AMPDU_AGGR << 4) | (peer_id << 8)));
+ } else if (event == STA_RX_ADDBA_DONE) {
+ mgmt_frame->desc_word[4] = cpu_to_le16(ssn);
+ mgmt_frame->desc_word[7] = cpu_to_le16(tid |
+ (START_AMPDU_AGGR << 4) |
+ (RX_BA_INDICATION << 5) |
+ (peer_id << 8));
+ } else if (event == STA_TX_DELBA) {
+ mgmt_frame->desc_word[7] = cpu_to_le16(tid |
+ (STOP_AMPDU_AGGR << 4) |
+ (peer_id << 8));
+ } else if (event == STA_RX_DELBA) {
+ mgmt_frame->desc_word[7] = cpu_to_le16(tid |
+ (STOP_AMPDU_AGGR << 4) |
+ (RX_BA_INDICATION << 5) |
+ (peer_id << 8));
+ }
+
+ skb_put(skb, FRAME_DESC_SZ);
+
+ return rsi_send_internal_mgmt_frame(common, skb);
+}
+
+/**
+ * rsi_program_bb_rf() - This function starts base band and RF programming.
+ * This is called after initial configurations are done.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: 0 on success, corresponding negative error code on failure.
+ */
+static int rsi_program_bb_rf(struct rsi_common *common)
+{
+ struct sk_buff *skb;
+ struct rsi_mac_frame *mgmt_frame;
+
+ rsi_dbg(MGMT_TX_ZONE, "%s: Sending program BB/RF frame\n", __func__);
+
+ skb = dev_alloc_skb(FRAME_DESC_SZ);
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memset(skb->data, 0, FRAME_DESC_SZ);
+ mgmt_frame = (struct rsi_mac_frame *)skb->data;
+
+ mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12);
+ mgmt_frame->desc_word[1] = cpu_to_le16(BBP_PROG_IN_TA);
+ mgmt_frame->desc_word[4] = cpu_to_le16(common->endpoint << 8);
+
+ if (common->rf_reset) {
+ mgmt_frame->desc_word[7] = cpu_to_le16(RF_RESET_ENABLE);
+ rsi_dbg(MGMT_TX_ZONE, "%s: ===> RF RESET REQUEST SENT <===\n",
+ __func__);
+ common->rf_reset = 0;
+ }
+ common->bb_rf_prog_count = 1;
+ mgmt_frame->desc_word[7] |= cpu_to_le16(PUT_BBP_RESET |
+ BBP_REG_WRITE | (RSI_RF_TYPE << 4));
+ skb_put(skb, FRAME_DESC_SZ);
+
+ return rsi_send_internal_mgmt_frame(common, skb);
+}
+
+/**
+ * rsi_set_vap_capabilities() - This function send vap capability to firmware.
+ * @common: Pointer to the driver private structure.
+ * @opmode: Operating mode of device.
+ *
+ * Return: 0 on success, corresponding negative error code on failure.
+ */
+int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode)
+{
+ struct sk_buff *skb = NULL;
+ struct rsi_vap_caps *vap_caps;
+ u16 vap_id = 0;
+
+ rsi_dbg(MGMT_TX_ZONE, "%s: Sending VAP capabilities frame\n", __func__);
+
+ skb = dev_alloc_skb(sizeof(struct rsi_vap_caps));
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memset(skb->data, 0, sizeof(struct rsi_vap_caps));
+ vap_caps = (struct rsi_vap_caps *)skb->data;
+
+ vap_caps->desc_word[0] = cpu_to_le16((sizeof(struct rsi_vap_caps) -
+ FRAME_DESC_SZ) |
+ (RSI_WIFI_MGMT_Q << 12));
+ vap_caps->desc_word[1] = cpu_to_le16(VAP_CAPABILITIES);
+ vap_caps->desc_word[4] = cpu_to_le16(mode |
+ (common->channel_width << 8));
+ vap_caps->desc_word[7] = cpu_to_le16((vap_id << 8) |
+ (common->mac_id << 4) |
+ common->radio_id);
+
+ memcpy(vap_caps->mac_addr, common->mac_addr, IEEE80211_ADDR_LEN);
+ vap_caps->keep_alive_period = cpu_to_le16(90);
+ vap_caps->frag_threshold = cpu_to_le16(IEEE80211_MAX_FRAG_THRESHOLD);
+
+ vap_caps->rts_threshold = cpu_to_le16(common->rts_threshold);
+ vap_caps->default_mgmt_rate = 0;
+ if (conf_is_ht40(&common->priv->hw->conf)) {
+ vap_caps->default_ctrl_rate =
+ cpu_to_le32(RSI_RATE_6 | FULL40M_ENABLE << 16);
+ } else {
+ vap_caps->default_ctrl_rate = cpu_to_le32(RSI_RATE_6);
+ }
+ vap_caps->default_data_rate = 0;
+ vap_caps->beacon_interval = cpu_to_le16(200);
+ vap_caps->dtim_period = cpu_to_le16(4);
+
+ skb_put(skb, sizeof(*vap_caps));
+
+ return rsi_send_internal_mgmt_frame(common, skb);
+}
+
+/**
+ * rsi_hal_load_key() - This function is used to load keys within the firmware.
+ * @common: Pointer to the driver private structure.
+ * @data: Pointer to the key data.
+ * @key_len: Key length to be loaded.
+ * @key_type: Type of key: GROUP/PAIRWISE.
+ * @key_id: Key index.
+ * @cipher: Type of cipher used.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int rsi_hal_load_key(struct rsi_common *common,
+ u8 *data,
+ u16 key_len,
+ u8 key_type,
+ u8 key_id,
+ u32 cipher)
+{
+ struct sk_buff *skb = NULL;
+ struct rsi_set_key *set_key;
+ u16 key_descriptor = 0;
+
+ rsi_dbg(MGMT_TX_ZONE, "%s: Sending load key frame\n", __func__);
+
+ skb = dev_alloc_skb(sizeof(struct rsi_set_key));
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memset(skb->data, 0, sizeof(struct rsi_set_key));
+ set_key = (struct rsi_set_key *)skb->data;
+
+ if ((cipher == WLAN_CIPHER_SUITE_WEP40) ||
+ (cipher == WLAN_CIPHER_SUITE_WEP104)) {
+ key_len += 1;
+ key_descriptor |= BIT(2);
+ if (key_len >= 13)
+ key_descriptor |= BIT(3);
+ } else if (cipher != KEY_TYPE_CLEAR) {
+ key_descriptor |= BIT(4);
+ if (key_type == RSI_PAIRWISE_KEY)
+ key_id = 0;
+ if (cipher == WLAN_CIPHER_SUITE_TKIP)
+ key_descriptor |= BIT(5);
+ }
+ key_descriptor |= (key_type | BIT(13) | (key_id << 14));
+
+ set_key->desc_word[0] = cpu_to_le16((sizeof(struct rsi_set_key) -
+ FRAME_DESC_SZ) |
+ (RSI_WIFI_MGMT_Q << 12));
+ set_key->desc_word[1] = cpu_to_le16(SET_KEY_REQ);
+ set_key->desc_word[4] = cpu_to_le16(key_descriptor);
+
+ if ((cipher == WLAN_CIPHER_SUITE_WEP40) ||
+ (cipher == WLAN_CIPHER_SUITE_WEP104)) {
+ memcpy(&set_key->key[key_id][1],
+ data,
+ key_len * 2);
+ } else {
+ memcpy(&set_key->key[0][0], data, key_len);
+ }
+
+ memcpy(set_key->tx_mic_key, &data[16], 8);
+ memcpy(set_key->rx_mic_key, &data[24], 8);
+
+ skb_put(skb, sizeof(struct rsi_set_key));
+
+ return rsi_send_internal_mgmt_frame(common, skb);
+}
+
+/*
+ * rsi_load_bootup_params() - This function send bootup params to the firmware.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: 0 on success, corresponding error code on failure.
+ */
+static u8 rsi_load_bootup_params(struct rsi_common *common)
+{
+ struct sk_buff *skb;
+ struct rsi_boot_params *boot_params;
+
+ rsi_dbg(MGMT_TX_ZONE, "%s: Sending boot params frame\n", __func__);
+ skb = dev_alloc_skb(sizeof(struct rsi_boot_params));
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memset(skb->data, 0, sizeof(struct rsi_boot_params));
+ boot_params = (struct rsi_boot_params *)skb->data;
+
+ rsi_dbg(MGMT_TX_ZONE, "%s:\n", __func__);
+
+ if (common->channel_width == BW_40MHZ) {
+ memcpy(&boot_params->bootup_params,
+ &boot_params_40,
+ sizeof(struct bootup_params));
+ rsi_dbg(MGMT_TX_ZONE, "%s: Packet 40MHZ <=== %d\n", __func__,
+ UMAC_CLK_40BW);
+ boot_params->desc_word[7] = cpu_to_le16(UMAC_CLK_40BW);
+ } else {
+ memcpy(&boot_params->bootup_params,
+ &boot_params_20,
+ sizeof(struct bootup_params));
+ if (boot_params_20.valid != cpu_to_le32(VALID_20)) {
+ boot_params->desc_word[7] = cpu_to_le16(UMAC_CLK_20BW);
+ rsi_dbg(MGMT_TX_ZONE,
+ "%s: Packet 20MHZ <=== %d\n", __func__,
+ UMAC_CLK_20BW);
+ } else {
+ boot_params->desc_word[7] = cpu_to_le16(UMAC_CLK_40MHZ);
+ rsi_dbg(MGMT_TX_ZONE,
+ "%s: Packet 20MHZ <=== %d\n", __func__,
+ UMAC_CLK_40MHZ);
+ }
+ }
+
+ /**
+ * Bit{0:11} indicates length of the Packet
+ * Bit{12:15} indicates host queue number
+ */
+ boot_params->desc_word[0] = cpu_to_le16(sizeof(struct bootup_params) |
+ (RSI_WIFI_MGMT_Q << 12));
+ boot_params->desc_word[1] = cpu_to_le16(BOOTUP_PARAMS_REQUEST);
+
+ skb_put(skb, sizeof(struct rsi_boot_params));
+
+ return rsi_send_internal_mgmt_frame(common, skb);
+}
+
+/**
+ * rsi_send_reset_mac() - This function prepares reset MAC request and sends an
+ * internal management frame to indicate it to firmware.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: 0 on success, corresponding error code on failure.
+ */
+static int rsi_send_reset_mac(struct rsi_common *common)
+{
+ struct sk_buff *skb;
+ struct rsi_mac_frame *mgmt_frame;
+
+ rsi_dbg(MGMT_TX_ZONE, "%s: Sending reset MAC frame\n", __func__);
+
+ skb = dev_alloc_skb(FRAME_DESC_SZ);
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memset(skb->data, 0, FRAME_DESC_SZ);
+ mgmt_frame = (struct rsi_mac_frame *)skb->data;
+
+ mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12);
+ mgmt_frame->desc_word[1] = cpu_to_le16(RESET_MAC_REQ);
+ mgmt_frame->desc_word[4] = cpu_to_le16(RETRY_COUNT << 8);
+
+ skb_put(skb, FRAME_DESC_SZ);
+
+ return rsi_send_internal_mgmt_frame(common, skb);
+}
+
+/**
+ * rsi_set_channel() - This function programs the channel.
+ * @common: Pointer to the driver private structure.
+ * @channel: Channel value to be set.
+ *
+ * Return: 0 on success, corresponding error code on failure.
+ */
+int rsi_set_channel(struct rsi_common *common, u16 channel)
+{
+ struct sk_buff *skb = NULL;
+ struct rsi_mac_frame *mgmt_frame;
+
+ rsi_dbg(MGMT_TX_ZONE,
+ "%s: Sending scan req frame\n", __func__);
+
+ skb = dev_alloc_skb(FRAME_DESC_SZ);
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memset(skb->data, 0, FRAME_DESC_SZ);
+ mgmt_frame = (struct rsi_mac_frame *)skb->data;
+
+ if (common->band == IEEE80211_BAND_5GHZ) {
+ if ((channel >= 36) && (channel <= 64))
+ channel = ((channel - 32) / 4);
+ else if ((channel > 64) && (channel <= 140))
+ channel = ((channel - 102) / 4) + 8;
+ else if (channel >= 149)
+ channel = ((channel - 151) / 4) + 18;
+ else
+ return -EINVAL;
+ } else {
+ if (channel > 14) {
+ rsi_dbg(ERR_ZONE, "%s: Invalid chno %d, band = %d\n",
+ __func__, channel, common->band);
+ return -EINVAL;
+ }
+ }
+
+ mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12);
+ mgmt_frame->desc_word[1] = cpu_to_le16(SCAN_REQUEST);
+ mgmt_frame->desc_word[4] = cpu_to_le16(channel);
+
+ mgmt_frame->desc_word[7] = cpu_to_le16(PUT_BBP_RESET |
+ BBP_REG_WRITE |
+ (RSI_RF_TYPE << 4));
+
+ mgmt_frame->desc_word[5] = cpu_to_le16(0x01);
+
+ if (common->channel_width == BW_40MHZ)
+ mgmt_frame->desc_word[5] |= cpu_to_le16(0x1 << 8);
+
+ common->channel = channel;
+
+ skb_put(skb, FRAME_DESC_SZ);
+
+ return rsi_send_internal_mgmt_frame(common, skb);
+}
+
+/**
+ * rsi_compare() - This function is used to compare two integers
+ * @a: pointer to the first integer
+ * @b: pointer to the second integer
+ *
+ * Return: 0 if both are equal, -1 if the first is smaller, else 1
+ */
+static int rsi_compare(const void *a, const void *b)
+{
+ u16 _a = *(const u16 *)(a);
+ u16 _b = *(const u16 *)(b);
+
+ if (_a > _b)
+ return -1;
+
+ if (_a < _b)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * rsi_map_rates() - This function is used to map selected rates to hw rates.
+ * @rate: The standard rate to be mapped.
+ * @offset: Offset that will be returned.
+ *
+ * Return: 0 if it is a mcs rate, else 1
+ */
+static bool rsi_map_rates(u16 rate, int *offset)
+{
+ int kk;
+ for (kk = 0; kk < ARRAY_SIZE(rsi_mcsrates); kk++) {
+ if (rate == mcs[kk]) {
+ *offset = kk;
+ return false;
+ }
+ }
+
+ for (kk = 0; kk < ARRAY_SIZE(rsi_rates); kk++) {
+ if (rate == rsi_rates[kk].bitrate / 5) {
+ *offset = kk;
+ break;
+ }
+ }
+ return true;
+}
+
+/**
+ * rsi_send_auto_rate_request() - This function is to set rates for connection
+ * and send autorate request to firmware.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: 0 on success, corresponding error code on failure.
+ */
+static int rsi_send_auto_rate_request(struct rsi_common *common)
+{
+ struct sk_buff *skb;
+ struct rsi_auto_rate *auto_rate;
+ int ii = 0, jj = 0, kk = 0;
+ struct ieee80211_hw *hw = common->priv->hw;
+ u8 band = hw->conf.chandef.chan->band;
+ u8 num_supported_rates = 0;
+ u8 rate_offset = 0;
+ u32 rate_bitmap = common->bitrate_mask[band];
+
+ u16 *selected_rates, min_rate;
+
+ skb = dev_alloc_skb(sizeof(struct rsi_auto_rate));
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ selected_rates = kmalloc(2 * RSI_TBL_SZ, GFP_KERNEL);
+ if (!selected_rates) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of mem\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memset(skb->data, 0, sizeof(struct rsi_auto_rate));
+ memset(selected_rates, 0, 2 * RSI_TBL_SZ);
+
+ auto_rate = (struct rsi_auto_rate *)skb->data;
+
+ auto_rate->aarf_rssi = cpu_to_le16(((u16)3 << 6) | (u16)(18 & 0x3f));
+ auto_rate->collision_tolerance = cpu_to_le16(3);
+ auto_rate->failure_limit = cpu_to_le16(3);
+ auto_rate->initial_boundary = cpu_to_le16(3);
+ auto_rate->max_threshold_limt = cpu_to_le16(27);
+
+ auto_rate->desc_word[1] = cpu_to_le16(AUTO_RATE_IND);
+
+ if (common->channel_width == BW_40MHZ)
+ auto_rate->desc_word[7] |= cpu_to_le16(1);
+
+ if (band == IEEE80211_BAND_2GHZ)
+ min_rate = STD_RATE_01;
+ else
+ min_rate = STD_RATE_06;
+
+ for (ii = 0, jj = 0; ii < ARRAY_SIZE(rsi_rates); ii++) {
+ if (rate_bitmap & BIT(ii)) {
+ selected_rates[jj++] = (rsi_rates[ii].bitrate / 5);
+ rate_offset++;
+ }
+ }
+ num_supported_rates = jj;
+
+ if (common->vif_info[0].is_ht) {
+ for (ii = 0; ii < ARRAY_SIZE(mcs); ii++)
+ selected_rates[jj++] = mcs[ii];
+ num_supported_rates += ARRAY_SIZE(mcs);
+ rate_offset += ARRAY_SIZE(mcs);
+ }
+
+ if (rate_offset < (RSI_TBL_SZ / 2) - 1) {
+ for (ii = jj; ii < (RSI_TBL_SZ / 2); ii++) {
+ selected_rates[jj++] = min_rate;
+ rate_offset++;
+ }
+ }
+
+ sort(selected_rates, jj, sizeof(u16), &rsi_compare, NULL);
+
+ /* mapping the rates to RSI rates */
+ for (ii = 0; ii < jj; ii++) {
+ if (rsi_map_rates(selected_rates[ii], &kk)) {
+ auto_rate->supported_rates[ii] =
+ cpu_to_le16(rsi_rates[kk].hw_value);
+ } else {
+ auto_rate->supported_rates[ii] =
+ cpu_to_le16(rsi_mcsrates[kk]);
+ }
+ }
+
+ /* loading HT rates in the bottom half of the auto rate table */
+ if (common->vif_info[0].is_ht) {
+ if (common->vif_info[0].sgi)
+ auto_rate->supported_rates[rate_offset++] =
+ cpu_to_le16(RSI_RATE_MCS7_SG);
+
+ for (ii = rate_offset, kk = ARRAY_SIZE(rsi_mcsrates) - 1;
+ ii < rate_offset + 2 * ARRAY_SIZE(rsi_mcsrates); ii++) {
+ if (common->vif_info[0].sgi)
+ auto_rate->supported_rates[ii++] =
+ cpu_to_le16(rsi_mcsrates[kk] | BIT(9));
+ auto_rate->supported_rates[ii] =
+ cpu_to_le16(rsi_mcsrates[kk--]);
+ }
+
+ for (; ii < RSI_TBL_SZ; ii++) {
+ auto_rate->supported_rates[ii] =
+ cpu_to_le16(rsi_mcsrates[0]);
+ }
+ }
+
+ auto_rate->num_supported_rates = cpu_to_le16(num_supported_rates * 2);
+ auto_rate->moderate_rate_inx = cpu_to_le16(num_supported_rates / 2);
+ auto_rate->desc_word[7] |= cpu_to_le16(0 << 8);
+ num_supported_rates *= 2;
+
+ auto_rate->desc_word[0] = cpu_to_le16((sizeof(*auto_rate) -
+ FRAME_DESC_SZ) |
+ (RSI_WIFI_MGMT_Q << 12));
+
+ skb_put(skb,
+ sizeof(struct rsi_auto_rate));
+ kfree(selected_rates);
+
+ return rsi_send_internal_mgmt_frame(common, skb);
+}
+
+/**
+ * rsi_inform_bss_status() - This function informs about bss status with the
+ * help of sta notify params by sending an internal
+ * management frame to firmware.
+ * @common: Pointer to the driver private structure.
+ * @status: Bss status type.
+ * @bssid: Bssid.
+ * @qos_enable: Qos is enabled.
+ * @aid: Aid (unique for all STAs).
+ *
+ * Return: None.
+ */
+void rsi_inform_bss_status(struct rsi_common *common,
+ u8 status,
+ const unsigned char *bssid,
+ u8 qos_enable,
+ u16 aid)
+{
+ if (status) {
+ rsi_hal_send_sta_notify_frame(common,
+ NL80211_IFTYPE_STATION,
+ STA_CONNECTED,
+ bssid,
+ qos_enable,
+ aid);
+ if (common->min_rate == 0xffff)
+ rsi_send_auto_rate_request(common);
+ } else {
+ rsi_hal_send_sta_notify_frame(common,
+ NL80211_IFTYPE_STATION,
+ STA_DISCONNECTED,
+ bssid,
+ qos_enable,
+ aid);
+ }
+}
+
+/**
+ * rsi_eeprom_read() - This function sends a frame to read the mac address
+ * from the eeprom.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_eeprom_read(struct rsi_common *common)
+{
+ struct rsi_mac_frame *mgmt_frame;
+ struct sk_buff *skb;
+
+ rsi_dbg(MGMT_TX_ZONE, "%s: Sending EEPROM read req frame\n", __func__);
+
+ skb = dev_alloc_skb(FRAME_DESC_SZ);
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memset(skb->data, 0, FRAME_DESC_SZ);
+ mgmt_frame = (struct rsi_mac_frame *)skb->data;
+
+ /* FrameType */
+ mgmt_frame->desc_word[1] = cpu_to_le16(EEPROM_READ_TYPE);
+ mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12);
+ /* Number of bytes to read */
+ mgmt_frame->desc_word[3] = cpu_to_le16(ETH_ALEN +
+ WLAN_MAC_MAGIC_WORD_LEN +
+ WLAN_HOST_MODE_LEN +
+ WLAN_FW_VERSION_LEN);
+ /* Address to read */
+ mgmt_frame->desc_word[4] = cpu_to_le16(WLAN_MAC_EEPROM_ADDR);
+
+ skb_put(skb, FRAME_DESC_SZ);
+
+ return rsi_send_internal_mgmt_frame(common, skb);
+}
+
+/**
+ * rsi_handle_ta_confirm_type() - This function handles the confirm frames.
+ * @common: Pointer to the driver private structure.
+ * @msg: Pointer to received packet.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_handle_ta_confirm_type(struct rsi_common *common,
+ u8 *msg)
+{
+ u8 sub_type = (msg[15] & 0xff);
+
+ switch (sub_type) {
+ case BOOTUP_PARAMS_REQUEST:
+ rsi_dbg(FSM_ZONE, "%s: Boot up params confirm received\n",
+ __func__);
+ if (common->fsm_state == FSM_BOOT_PARAMS_SENT) {
+ if (rsi_eeprom_read(common)) {
+ common->fsm_state = FSM_CARD_NOT_READY;
+ goto out;
+ } else {
+ common->fsm_state = FSM_EEPROM_READ_MAC_ADDR;
+ }
+ } else {
+ rsi_dbg(ERR_ZONE,
+ "%s: Received bootup params cfm in %d state\n",
+ __func__, common->fsm_state);
+ return 0;
+ }
+ break;
+
+ case EEPROM_READ_TYPE:
+ if (common->fsm_state == FSM_EEPROM_READ_MAC_ADDR) {
+ if (msg[16] == MAGIC_WORD) {
+ u8 offset = (FRAME_DESC_SZ + WLAN_HOST_MODE_LEN
+ + WLAN_MAC_MAGIC_WORD_LEN);
+ memcpy(common->mac_addr,
+ &msg[offset],
+ ETH_ALEN);
+ memcpy(&common->fw_ver,
+ &msg[offset + ETH_ALEN],
+ sizeof(struct version_info));
+
+ } else {
+ common->fsm_state = FSM_CARD_NOT_READY;
+ break;
+ }
+ if (rsi_send_reset_mac(common))
+ goto out;
+ else
+ common->fsm_state = FSM_RESET_MAC_SENT;
+ } else {
+ rsi_dbg(ERR_ZONE,
+ "%s: Received eeprom mac addr in %d state\n",
+ __func__, common->fsm_state);
+ return 0;
+ }
+ break;
+
+ case RESET_MAC_REQ:
+ if (common->fsm_state == FSM_RESET_MAC_SENT) {
+ rsi_dbg(FSM_ZONE, "%s: Reset MAC cfm received\n",
+ __func__);
+
+ if (rsi_load_radio_caps(common))
+ goto out;
+ else
+ common->fsm_state = FSM_RADIO_CAPS_SENT;
+ } else {
+ rsi_dbg(ERR_ZONE,
+ "%s: Received reset mac cfm in %d state\n",
+ __func__, common->fsm_state);
+ return 0;
+ }
+ break;
+
+ case RADIO_CAPABILITIES:
+ if (common->fsm_state == FSM_RADIO_CAPS_SENT) {
+ common->rf_reset = 1;
+ if (rsi_program_bb_rf(common)) {
+ goto out;
+ } else {
+ common->fsm_state = FSM_BB_RF_PROG_SENT;
+ rsi_dbg(FSM_ZONE, "%s: Radio cap cfm received\n",
+ __func__);
+ }
+ } else {
+ rsi_dbg(ERR_ZONE,
+ "%s: Received radio caps cfm in %d state\n",
+ __func__, common->fsm_state);
+ return 0;
+ }
+ break;
+
+ case BB_PROG_VALUES_REQUEST:
+ case RF_PROG_VALUES_REQUEST:
+ case BBP_PROG_IN_TA:
+ rsi_dbg(FSM_ZONE, "%s: BB/RF cfm received\n", __func__);
+ if (common->fsm_state == FSM_BB_RF_PROG_SENT) {
+ common->bb_rf_prog_count--;
+ if (!common->bb_rf_prog_count) {
+ common->fsm_state = FSM_MAC_INIT_DONE;
+ return rsi_mac80211_attach(common);
+ }
+ } else {
+ goto out;
+ }
+ break;
+
+ default:
+ rsi_dbg(INFO_ZONE, "%s: Invalid TA confirm pkt received\n",
+ __func__);
+ break;
+ }
+ return 0;
+out:
+ rsi_dbg(ERR_ZONE, "%s: Unable to send pkt/Invalid frame received\n",
+ __func__);
+ return -EINVAL;
+}
+
+/**
+ * rsi_mgmt_pkt_recv() - This function processes the management packets
+ * recieved from the hardware.
+ * @common: Pointer to the driver private structure.
+ * @msg: Pointer to the received packet.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int rsi_mgmt_pkt_recv(struct rsi_common *common, u8 *msg)
+{
+ s32 msg_len = (le16_to_cpu(*(__le16 *)&msg[0]) & 0x0fff);
+ u16 msg_type = (msg[2]);
+
+ rsi_dbg(FSM_ZONE, "%s: Msg Len: %d, Msg Type: %4x\n",
+ __func__, msg_len, msg_type);
+
+ if (msg_type == TA_CONFIRM_TYPE) {
+ return rsi_handle_ta_confirm_type(common, msg);
+ } else if (msg_type == CARD_READY_IND) {
+ rsi_dbg(FSM_ZONE, "%s: Card ready indication received\n",
+ __func__);
+ if (common->fsm_state == FSM_CARD_NOT_READY) {
+ rsi_set_default_parameters(common);
+
+ if (rsi_load_bootup_params(common))
+ return -ENOMEM;
+ else
+ common->fsm_state = FSM_BOOT_PARAMS_SENT;
+ } else {
+ return -EINVAL;
+ }
+ } else if (msg_type == TX_STATUS_IND) {
+ if (msg[15] == PROBEREQ_CONFIRM)
+ common->mgmt_q_block = false;
+ rsi_dbg(FSM_ZONE, "%s: Probe confirm received\n",
+ __func__);
+ } else {
+ return rsi_mgmt_pkt_to_core(common, msg, msg_len, msg_type);
+ }
+ return 0;
+}
diff --git a/drivers/net/wireless/rsi/rsi_91x_pkt.c b/drivers/net/wireless/rsi/rsi_91x_pkt.c
new file mode 100644
index 000000000000..8e48e72bae20
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_91x_pkt.c
@@ -0,0 +1,196 @@
+/**
+ * Copyright (c) 2014 Redpine Signals Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "rsi_mgmt.h"
+
+/**
+ * rsi_send_data_pkt() - This function sends the recieved data packet from
+ * driver to device.
+ * @common: Pointer to the driver private structure.
+ * @skb: Pointer to the socket buffer structure.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct ieee80211_hdr *tmp_hdr = NULL;
+ struct ieee80211_tx_info *info;
+ struct skb_info *tx_params;
+ struct ieee80211_bss_conf *bss = NULL;
+ int status = -EINVAL;
+ u8 ieee80211_size = MIN_802_11_HDR_LEN;
+ u8 extnd_size = 0;
+ __le16 *frame_desc;
+ u16 seq_num = 0;
+
+ info = IEEE80211_SKB_CB(skb);
+ bss = &info->control.vif->bss_conf;
+ tx_params = (struct skb_info *)info->driver_data;
+
+ if (!bss->assoc)
+ goto err;
+
+ tmp_hdr = (struct ieee80211_hdr *)&skb->data[0];
+ seq_num = (le16_to_cpu(tmp_hdr->seq_ctrl) >> 4);
+
+ extnd_size = ((uintptr_t)skb->data & 0x3);
+
+ if ((FRAME_DESC_SZ + extnd_size) > skb_headroom(skb)) {
+ rsi_dbg(ERR_ZONE, "%s: Unable to send pkt\n", __func__);
+ status = -ENOSPC;
+ goto err;
+ }
+
+ skb_push(skb, (FRAME_DESC_SZ + extnd_size));
+ frame_desc = (__le16 *)&skb->data[0];
+ memset((u8 *)frame_desc, 0, FRAME_DESC_SZ);
+
+ if (ieee80211_is_data_qos(tmp_hdr->frame_control)) {
+ ieee80211_size += 2;
+ frame_desc[6] |= cpu_to_le16(BIT(12));
+ }
+
+ if ((!(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT)) &&
+ (common->secinfo.security_enable)) {
+ if (rsi_is_cipher_wep(common))
+ ieee80211_size += 4;
+ else
+ ieee80211_size += 8;
+ frame_desc[6] |= cpu_to_le16(BIT(15));
+ }
+
+ frame_desc[0] = cpu_to_le16((skb->len - FRAME_DESC_SZ) |
+ (RSI_WIFI_DATA_Q << 12));
+ frame_desc[2] = cpu_to_le16((extnd_size) | (ieee80211_size) << 8);
+
+ if (common->min_rate != 0xffff) {
+ /* Send fixed rate */
+ frame_desc[3] = cpu_to_le16(RATE_INFO_ENABLE);
+ frame_desc[4] = cpu_to_le16(common->min_rate);
+ }
+
+ frame_desc[6] |= cpu_to_le16(seq_num & 0xfff);
+ frame_desc[7] = cpu_to_le16(((tx_params->tid & 0xf) << 4) |
+ (skb->priority & 0xf) |
+ (tx_params->sta_id << 8));
+
+ status = adapter->host_intf_write_pkt(common->priv,
+ skb->data,
+ skb->len);
+ if (status)
+ rsi_dbg(ERR_ZONE, "%s: Failed to write pkt\n",
+ __func__);
+
+err:
+ ++common->tx_stats.total_tx_pkt_freed[skb->priority];
+ rsi_indicate_tx_status(common->priv, skb, status);
+ return status;
+}
+
+/**
+ * rsi_send_mgmt_pkt() - This functions sends the received management packet
+ * from driver to device.
+ * @common: Pointer to the driver private structure.
+ * @skb: Pointer to the socket buffer structure.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+int rsi_send_mgmt_pkt(struct rsi_common *common,
+ struct sk_buff *skb)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct ieee80211_hdr *wh = NULL;
+ struct ieee80211_tx_info *info;
+ struct ieee80211_bss_conf *bss = NULL;
+ struct skb_info *tx_params;
+ int status = -E2BIG;
+ __le16 *msg = NULL;
+ u8 extnd_size = 0;
+ u8 vap_id = 0;
+
+ info = IEEE80211_SKB_CB(skb);
+ tx_params = (struct skb_info *)info->driver_data;
+ extnd_size = ((uintptr_t)skb->data & 0x3);
+
+ if (tx_params->flags & INTERNAL_MGMT_PKT) {
+ if ((extnd_size) > skb_headroom(skb)) {
+ rsi_dbg(ERR_ZONE, "%s: Unable to send pkt\n", __func__);
+ dev_kfree_skb(skb);
+ return -ENOSPC;
+ }
+ skb_push(skb, extnd_size);
+ skb->data[extnd_size + 4] = extnd_size;
+ status = adapter->host_intf_write_pkt(common->priv,
+ (u8 *)skb->data,
+ skb->len);
+ if (status) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to write the packet\n", __func__);
+ }
+ dev_kfree_skb(skb);
+ return status;
+ }
+
+ bss = &info->control.vif->bss_conf;
+ wh = (struct ieee80211_hdr *)&skb->data[0];
+
+ if (FRAME_DESC_SZ > skb_headroom(skb))
+ goto err;
+
+ skb_push(skb, FRAME_DESC_SZ);
+ memset(skb->data, 0, FRAME_DESC_SZ);
+ msg = (__le16 *)skb->data;
+
+ if (skb->len > MAX_MGMT_PKT_SIZE) {
+ rsi_dbg(INFO_ZONE, "%s: Dropping mgmt pkt > 512\n", __func__);
+ goto err;
+ }
+
+ msg[0] = cpu_to_le16((skb->len - FRAME_DESC_SZ) |
+ (RSI_WIFI_MGMT_Q << 12));
+ msg[1] = cpu_to_le16(TX_DOT11_MGMT);
+ msg[2] = cpu_to_le16(MIN_802_11_HDR_LEN << 8);
+ msg[3] = cpu_to_le16(RATE_INFO_ENABLE);
+ msg[6] = cpu_to_le16(le16_to_cpu(wh->seq_ctrl) >> 4);
+
+ if (wh->addr1[0] & BIT(0))
+ msg[3] |= cpu_to_le16(RSI_BROADCAST_PKT);
+
+ if (common->band == IEEE80211_BAND_2GHZ)
+ msg[4] = cpu_to_le16(RSI_11B_MODE);
+ else
+ msg[4] = cpu_to_le16((RSI_RATE_6 & 0x0f) | RSI_11G_MODE);
+
+ /* Indicate to firmware to give cfm */
+ if ((skb->data[16] == IEEE80211_STYPE_PROBE_REQ) && (!bss->assoc)) {
+ msg[1] |= cpu_to_le16(BIT(10));
+ msg[7] = cpu_to_le16(PROBEREQ_CONFIRM);
+ common->mgmt_q_block = true;
+ }
+
+ msg[7] |= cpu_to_le16(vap_id << 8);
+
+ status = adapter->host_intf_write_pkt(common->priv,
+ (u8 *)msg,
+ skb->len);
+ if (status)
+ rsi_dbg(ERR_ZONE, "%s: Failed to write the packet\n", __func__);
+
+err:
+ rsi_indicate_tx_status(common->priv, skb, status);
+ return status;
+}
diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio.c b/drivers/net/wireless/rsi/rsi_91x_sdio.c
new file mode 100644
index 000000000000..852453f386e2
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_91x_sdio.c
@@ -0,0 +1,850 @@
+/**
+ * Copyright (c) 2014 Redpine Signals Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include <linux/module.h>
+#include "rsi_sdio.h"
+#include "rsi_common.h"
+
+/**
+ * rsi_sdio_set_cmd52_arg() - This function prepares cmd 52 read/write arg.
+ * @rw: Read/write
+ * @func: function number
+ * @raw: indicates whether to perform read after write
+ * @address: address to which to read/write
+ * @writedata: data to write
+ *
+ * Return: argument
+ */
+static u32 rsi_sdio_set_cmd52_arg(bool rw,
+ u8 func,
+ u8 raw,
+ u32 address,
+ u8 writedata)
+{
+ return ((rw & 1) << 31) | ((func & 0x7) << 28) |
+ ((raw & 1) << 27) | (1 << 26) |
+ ((address & 0x1FFFF) << 9) | (1 << 8) |
+ (writedata & 0xFF);
+}
+
+/**
+ * rsi_cmd52writebyte() - This function issues cmd52 byte write onto the card.
+ * @card: Pointer to the mmc_card.
+ * @address: Address to write.
+ * @byte: Data to write.
+ *
+ * Return: Write status.
+ */
+static int rsi_cmd52writebyte(struct mmc_card *card,
+ u32 address,
+ u8 byte)
+{
+ struct mmc_command io_cmd;
+ u32 arg;
+
+ memset(&io_cmd, 0, sizeof(io_cmd));
+ arg = rsi_sdio_set_cmd52_arg(1, 0, 0, address, byte);
+ io_cmd.opcode = SD_IO_RW_DIRECT;
+ io_cmd.arg = arg;
+ io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
+
+ return mmc_wait_for_cmd(card->host, &io_cmd, 0);
+}
+
+/**
+ * rsi_cmd52readbyte() - This function issues cmd52 byte read onto the card.
+ * @card: Pointer to the mmc_card.
+ * @address: Address to read from.
+ * @byte: Variable to store read value.
+ *
+ * Return: Read status.
+ */
+static int rsi_cmd52readbyte(struct mmc_card *card,
+ u32 address,
+ u8 *byte)
+{
+ struct mmc_command io_cmd;
+ u32 arg;
+ int err;
+
+ memset(&io_cmd, 0, sizeof(io_cmd));
+ arg = rsi_sdio_set_cmd52_arg(0, 0, 0, address, 0);
+ io_cmd.opcode = SD_IO_RW_DIRECT;
+ io_cmd.arg = arg;
+ io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
+
+ err = mmc_wait_for_cmd(card->host, &io_cmd, 0);
+ if ((!err) && (byte))
+ *byte = io_cmd.resp[0] & 0xFF;
+ return err;
+}
+
+/**
+ * rsi_issue_sdiocommand() - This function issues sdio commands.
+ * @func: Pointer to the sdio_func structure.
+ * @opcode: Opcode value.
+ * @arg: Arguments to pass.
+ * @flags: Flags which are set.
+ * @resp: Pointer to store response.
+ *
+ * Return: err: command status as 0 or -1.
+ */
+static int rsi_issue_sdiocommand(struct sdio_func *func,
+ u32 opcode,
+ u32 arg,
+ u32 flags,
+ u32 *resp)
+{
+ struct mmc_command cmd;
+ struct mmc_host *host;
+ int err;
+
+ host = func->card->host;
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ cmd.opcode = opcode;
+ cmd.arg = arg;
+ cmd.flags = flags;
+ err = mmc_wait_for_cmd(host, &cmd, 3);
+
+ if ((!err) && (resp))
+ *resp = cmd.resp[0];
+
+ return err;
+}
+
+/**
+ * rsi_handle_interrupt() - This function is called upon the occurence
+ * of an interrupt.
+ * @function: Pointer to the sdio_func structure.
+ *
+ * Return: None.
+ */
+static void rsi_handle_interrupt(struct sdio_func *function)
+{
+ struct rsi_hw *adapter = sdio_get_drvdata(function);
+
+ sdio_release_host(function);
+ rsi_interrupt_handler(adapter);
+ sdio_claim_host(function);
+}
+
+/**
+ * rsi_reset_card() - This function resets and re-initializes the card.
+ * @pfunction: Pointer to the sdio_func structure.
+ *
+ * Return: None.
+ */
+static void rsi_reset_card(struct sdio_func *pfunction)
+{
+ int ret = 0;
+ int err;
+ struct mmc_card *card = pfunction->card;
+ struct mmc_host *host = card->host;
+ s32 bit = (fls(host->ocr_avail) - 1);
+ u8 cmd52_resp;
+ u32 clock, resp, i;
+ u16 rca;
+
+ /* Reset 9110 chip */
+ ret = rsi_cmd52writebyte(pfunction->card,
+ SDIO_CCCR_ABORT,
+ (1 << 3));
+
+ /* Card will not send any response as it is getting reset immediately
+ * Hence expect a timeout status from host controller
+ */
+ if (ret != -ETIMEDOUT)
+ rsi_dbg(ERR_ZONE, "%s: Reset failed : %d\n", __func__, ret);
+
+ /* Wait for few milli seconds to get rid of residue charges if any */
+ msleep(20);
+
+ /* Initialize the SDIO card */
+ host->ios.vdd = bit;
+ host->ios.chip_select = MMC_CS_DONTCARE;
+ host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
+ host->ios.power_mode = MMC_POWER_UP;
+ host->ios.bus_width = MMC_BUS_WIDTH_1;
+ host->ios.timing = MMC_TIMING_LEGACY;
+ host->ops->set_ios(host, &host->ios);
+
+ /*
+ * This delay should be sufficient to allow the power supply
+ * to reach the minimum voltage.
+ */
+ msleep(20);
+
+ host->ios.clock = host->f_min;
+ host->ios.power_mode = MMC_POWER_ON;
+ host->ops->set_ios(host, &host->ios);
+
+ /*
+ * This delay must be at least 74 clock sizes, or 1 ms, or the
+ * time required to reach a stable voltage.
+ */
+ msleep(20);
+
+ /* Issue CMD0. Goto idle state */
+ host->ios.chip_select = MMC_CS_HIGH;
+ host->ops->set_ios(host, &host->ios);
+ msleep(20);
+ err = rsi_issue_sdiocommand(pfunction,
+ MMC_GO_IDLE_STATE,
+ 0,
+ (MMC_RSP_NONE | MMC_CMD_BC),
+ NULL);
+ host->ios.chip_select = MMC_CS_DONTCARE;
+ host->ops->set_ios(host, &host->ios);
+ msleep(20);
+ host->use_spi_crc = 0;
+
+ if (err)
+ rsi_dbg(ERR_ZONE, "%s: CMD0 failed : %d\n", __func__, err);
+
+ if (!host->ocr_avail) {
+ /* Issue CMD5, arg = 0 */
+ err = rsi_issue_sdiocommand(pfunction,
+ SD_IO_SEND_OP_COND,
+ 0,
+ (MMC_RSP_R4 | MMC_CMD_BCR),
+ &resp);
+ if (err)
+ rsi_dbg(ERR_ZONE, "%s: CMD5 failed : %d\n",
+ __func__, err);
+ host->ocr_avail = resp;
+ }
+
+ /* Issue CMD5, arg = ocr. Wait till card is ready */
+ for (i = 0; i < 100; i++) {
+ err = rsi_issue_sdiocommand(pfunction,
+ SD_IO_SEND_OP_COND,
+ host->ocr_avail,
+ (MMC_RSP_R4 | MMC_CMD_BCR),
+ &resp);
+ if (err) {
+ rsi_dbg(ERR_ZONE, "%s: CMD5 failed : %d\n",
+ __func__, err);
+ break;
+ }
+
+ if (resp & MMC_CARD_BUSY)
+ break;
+ msleep(20);
+ }
+
+ if ((i == 100) || (err)) {
+ rsi_dbg(ERR_ZONE, "%s: card in not ready : %d %d\n",
+ __func__, i, err);
+ return;
+ }
+
+ /* Issue CMD3, get RCA */
+ err = rsi_issue_sdiocommand(pfunction,
+ SD_SEND_RELATIVE_ADDR,
+ 0,
+ (MMC_RSP_R6 | MMC_CMD_BCR),
+ &resp);
+ if (err) {
+ rsi_dbg(ERR_ZONE, "%s: CMD3 failed : %d\n", __func__, err);
+ return;
+ }
+ rca = resp >> 16;
+ host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
+ host->ops->set_ios(host, &host->ios);
+
+ /* Issue CMD7, select card */
+ err = rsi_issue_sdiocommand(pfunction,
+ MMC_SELECT_CARD,
+ (rca << 16),
+ (MMC_RSP_R1 | MMC_CMD_AC),
+ NULL);
+ if (err) {
+ rsi_dbg(ERR_ZONE, "%s: CMD7 failed : %d\n", __func__, err);
+ return;
+ }
+
+ /* Enable high speed */
+ if (card->host->caps & MMC_CAP_SD_HIGHSPEED) {
+ rsi_dbg(ERR_ZONE, "%s: Set high speed mode\n", __func__);
+ err = rsi_cmd52readbyte(card, SDIO_CCCR_SPEED, &cmd52_resp);
+ if (err) {
+ rsi_dbg(ERR_ZONE, "%s: CCCR speed reg read failed: %d\n",
+ __func__, err);
+ card->state &= ~MMC_STATE_HIGHSPEED;
+ } else {
+ err = rsi_cmd52writebyte(card,
+ SDIO_CCCR_SPEED,
+ (cmd52_resp | SDIO_SPEED_EHS));
+ if (err) {
+ rsi_dbg(ERR_ZONE,
+ "%s: CCR speed regwrite failed %d\n",
+ __func__, err);
+ return;
+ }
+ mmc_card_set_highspeed(card);
+ host->ios.timing = MMC_TIMING_SD_HS;
+ host->ops->set_ios(host, &host->ios);
+ }
+ }
+
+ /* Set clock */
+ if (mmc_card_highspeed(card))
+ clock = 50000000;
+ else
+ clock = card->cis.max_dtr;
+
+ if (clock > host->f_max)
+ clock = host->f_max;
+
+ host->ios.clock = clock;
+ host->ops->set_ios(host, &host->ios);
+
+ if (card->host->caps & MMC_CAP_4_BIT_DATA) {
+ /* CMD52: Set bus width & disable card detect resistor */
+ err = rsi_cmd52writebyte(card,
+ SDIO_CCCR_IF,
+ (SDIO_BUS_CD_DISABLE |
+ SDIO_BUS_WIDTH_4BIT));
+ if (err) {
+ rsi_dbg(ERR_ZONE, "%s: Set bus mode failed : %d\n",
+ __func__, err);
+ return;
+ }
+ host->ios.bus_width = MMC_BUS_WIDTH_4;
+ host->ops->set_ios(host, &host->ios);
+ }
+}
+
+/**
+ * rsi_setclock() - This function sets the clock frequency.
+ * @adapter: Pointer to the adapter structure.
+ * @freq: Clock frequency.
+ *
+ * Return: None.
+ */
+static void rsi_setclock(struct rsi_hw *adapter, u32 freq)
+{
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ struct mmc_host *host = dev->pfunction->card->host;
+ u32 clock;
+
+ clock = freq * 1000;
+ if (clock > host->f_max)
+ clock = host->f_max;
+ host->ios.clock = clock;
+ host->ops->set_ios(host, &host->ios);
+}
+
+/**
+ * rsi_setblocklength() - This function sets the host block length.
+ * @adapter: Pointer to the adapter structure.
+ * @length: Block length to be set.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+static int rsi_setblocklength(struct rsi_hw *adapter, u32 length)
+{
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ int status;
+ rsi_dbg(INIT_ZONE, "%s: Setting the block length\n", __func__);
+
+ status = sdio_set_block_size(dev->pfunction, length);
+ dev->pfunction->max_blksize = 256;
+
+ rsi_dbg(INFO_ZONE,
+ "%s: Operational blk length is %d\n", __func__, length);
+ return status;
+}
+
+/**
+ * rsi_setupcard() - This function queries and sets the card's features.
+ * @adapter: Pointer to the adapter structure.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+static int rsi_setupcard(struct rsi_hw *adapter)
+{
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ int status = 0;
+
+ rsi_setclock(adapter, 50000);
+
+ dev->tx_blk_size = 256;
+ status = rsi_setblocklength(adapter, dev->tx_blk_size);
+ if (status)
+ rsi_dbg(ERR_ZONE,
+ "%s: Unable to set block length\n", __func__);
+ return status;
+}
+
+/**
+ * rsi_sdio_read_register() - This function reads one byte of information
+ * from a register.
+ * @adapter: Pointer to the adapter structure.
+ * @addr: Address of the register.
+ * @data: Pointer to the data that stores the data read.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int rsi_sdio_read_register(struct rsi_hw *adapter,
+ u32 addr,
+ u8 *data)
+{
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ u8 fun_num = 0;
+ int status;
+
+ sdio_claim_host(dev->pfunction);
+
+ if (fun_num == 0)
+ *data = sdio_f0_readb(dev->pfunction, addr, &status);
+ else
+ *data = sdio_readb(dev->pfunction, addr, &status);
+
+ sdio_release_host(dev->pfunction);
+
+ return status;
+}
+
+/**
+ * rsi_sdio_write_register() - This function writes one byte of information
+ * into a register.
+ * @adapter: Pointer to the adapter structure.
+ * @function: Function Number.
+ * @addr: Address of the register.
+ * @data: Pointer to the data tha has to be written.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int rsi_sdio_write_register(struct rsi_hw *adapter,
+ u8 function,
+ u32 addr,
+ u8 *data)
+{
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ int status = 0;
+
+ sdio_claim_host(dev->pfunction);
+
+ if (function == 0)
+ sdio_f0_writeb(dev->pfunction, *data, addr, &status);
+ else
+ sdio_writeb(dev->pfunction, *data, addr, &status);
+
+ sdio_release_host(dev->pfunction);
+
+ return status;
+}
+
+/**
+ * rsi_sdio_ack_intr() - This function acks the interrupt received.
+ * @adapter: Pointer to the adapter structure.
+ * @int_bit: Interrupt bit to write into register.
+ *
+ * Return: None.
+ */
+void rsi_sdio_ack_intr(struct rsi_hw *adapter, u8 int_bit)
+{
+ int status;
+ status = rsi_sdio_write_register(adapter,
+ 1,
+ (SDIO_FUN1_INTR_CLR_REG |
+ RSI_SD_REQUEST_MASTER),
+ &int_bit);
+ if (status)
+ rsi_dbg(ERR_ZONE, "%s: unable to send ack\n", __func__);
+}
+
+
+
+/**
+ * rsi_sdio_read_register_multiple() - This function read multiple bytes of
+ * information from the SD card.
+ * @adapter: Pointer to the adapter structure.
+ * @addr: Address of the register.
+ * @count: Number of multiple bytes to be read.
+ * @data: Pointer to the read data.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_sdio_read_register_multiple(struct rsi_hw *adapter,
+ u32 addr,
+ u32 count,
+ u8 *data)
+{
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ u32 status;
+
+ sdio_claim_host(dev->pfunction);
+
+ status = sdio_readsb(dev->pfunction, data, addr, count);
+
+ sdio_release_host(dev->pfunction);
+
+ if (status != 0)
+ rsi_dbg(ERR_ZONE, "%s: Synch Cmd53 read failed\n", __func__);
+ return status;
+}
+
+/**
+ * rsi_sdio_write_register_multiple() - This function writes multiple bytes of
+ * information to the SD card.
+ * @adapter: Pointer to the adapter structure.
+ * @addr: Address of the register.
+ * @data: Pointer to the data that has to be written.
+ * @count: Number of multiple bytes to be written.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int rsi_sdio_write_register_multiple(struct rsi_hw *adapter,
+ u32 addr,
+ u8 *data,
+ u32 count)
+{
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ int status;
+
+ if (dev->write_fail > 1) {
+ rsi_dbg(ERR_ZONE, "%s: Stopping card writes\n", __func__);
+ return 0;
+ } else if (dev->write_fail == 1) {
+ /**
+ * Assuming it is a CRC failure, we want to allow another
+ * card write
+ */
+ rsi_dbg(ERR_ZONE, "%s: Continue card writes\n", __func__);
+ dev->write_fail++;
+ }
+
+ sdio_claim_host(dev->pfunction);
+
+ status = sdio_writesb(dev->pfunction, addr, data, count);
+
+ sdio_release_host(dev->pfunction);
+
+ if (status) {
+ rsi_dbg(ERR_ZONE, "%s: Synch Cmd53 write failed %d\n",
+ __func__, status);
+ dev->write_fail = 2;
+ } else {
+ memcpy(dev->prev_desc, data, FRAME_DESC_SZ);
+ }
+ return status;
+}
+
+/**
+ * rsi_sdio_host_intf_write_pkt() - This function writes the packet to device.
+ * @adapter: Pointer to the adapter structure.
+ * @pkt: Pointer to the data to be written on to the device.
+ * @len: length of the data to be written on to the device.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_sdio_host_intf_write_pkt(struct rsi_hw *adapter,
+ u8 *pkt,
+ u32 len)
+{
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ u32 block_size = dev->tx_blk_size;
+ u32 num_blocks, address, length;
+ u32 queueno;
+ int status;
+
+ queueno = ((pkt[1] >> 4) & 0xf);
+
+ num_blocks = len / block_size;
+
+ if (len % block_size)
+ num_blocks++;
+
+ address = (num_blocks * block_size | (queueno << 12));
+ length = num_blocks * block_size;
+
+ status = rsi_sdio_write_register_multiple(adapter,
+ address,
+ (u8 *)pkt,
+ length);
+ if (status)
+ rsi_dbg(ERR_ZONE, "%s: Unable to write onto the card: %d\n",
+ __func__, status);
+ rsi_dbg(DATA_TX_ZONE, "%s: Successfully written onto card\n", __func__);
+ return status;
+}
+
+/**
+ * rsi_sdio_host_intf_read_pkt() - This function reads the packet
+ from the device.
+ * @adapter: Pointer to the adapter data structure.
+ * @pkt: Pointer to the packet data to be read from the the device.
+ * @length: Length of the data to be read from the device.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int rsi_sdio_host_intf_read_pkt(struct rsi_hw *adapter,
+ u8 *pkt,
+ u32 length)
+{
+ int status = -EINVAL;
+
+ if (!length) {
+ rsi_dbg(ERR_ZONE, "%s: Pkt size is zero\n", __func__);
+ return status;
+ }
+
+ status = rsi_sdio_read_register_multiple(adapter,
+ length,
+ length, /*num of bytes*/
+ (u8 *)pkt);
+
+ if (status)
+ rsi_dbg(ERR_ZONE, "%s: Failed to read frame: %d\n", __func__,
+ status);
+ return status;
+}
+
+/**
+ * rsi_init_sdio_interface() - This function does init specific to SDIO.
+ *
+ * @adapter: Pointer to the adapter data structure.
+ * @pkt: Pointer to the packet data to be read from the the device.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+
+static int rsi_init_sdio_interface(struct rsi_hw *adapter,
+ struct sdio_func *pfunction)
+{
+ struct rsi_91x_sdiodev *rsi_91x_dev;
+ int status = -ENOMEM;
+
+ rsi_91x_dev = kzalloc(sizeof(*rsi_91x_dev), GFP_KERNEL);
+ if (!rsi_91x_dev)
+ return status;
+
+ adapter->rsi_dev = rsi_91x_dev;
+
+ sdio_claim_host(pfunction);
+
+ pfunction->enable_timeout = 100;
+ status = sdio_enable_func(pfunction);
+ if (status) {
+ rsi_dbg(ERR_ZONE, "%s: Failed to enable interface\n", __func__);
+ sdio_release_host(pfunction);
+ return status;
+ }
+
+ rsi_dbg(INIT_ZONE, "%s: Enabled the interface\n", __func__);
+
+ rsi_91x_dev->pfunction = pfunction;
+ adapter->device = &pfunction->dev;
+
+ sdio_set_drvdata(pfunction, adapter);
+
+ status = rsi_setupcard(adapter);
+ if (status) {
+ rsi_dbg(ERR_ZONE, "%s: Failed to setup card\n", __func__);
+ goto fail;
+ }
+
+ rsi_dbg(INIT_ZONE, "%s: Setup card succesfully\n", __func__);
+
+ status = rsi_init_sdio_slave_regs(adapter);
+ if (status) {
+ rsi_dbg(ERR_ZONE, "%s: Failed to init slave regs\n", __func__);
+ goto fail;
+ }
+ sdio_release_host(pfunction);
+
+ adapter->host_intf_write_pkt = rsi_sdio_host_intf_write_pkt;
+ adapter->host_intf_read_pkt = rsi_sdio_host_intf_read_pkt;
+ adapter->determine_event_timeout = rsi_sdio_determine_event_timeout;
+ adapter->check_hw_queue_status = rsi_sdio_read_buffer_status_register;
+
+#ifdef CONFIG_RSI_DEBUGFS
+ adapter->num_debugfs_entries = MAX_DEBUGFS_ENTRIES;
+#endif
+ return status;
+fail:
+ sdio_disable_func(pfunction);
+ sdio_release_host(pfunction);
+ return status;
+}
+
+/**
+ * rsi_probe() - This function is called by kernel when the driver provided
+ * Vendor and device IDs are matched. All the initialization
+ * work is done here.
+ * @pfunction: Pointer to the sdio_func structure.
+ * @id: Pointer to sdio_device_id structure.
+ *
+ * Return: 0 on success, 1 on failure.
+ */
+static int rsi_probe(struct sdio_func *pfunction,
+ const struct sdio_device_id *id)
+{
+ struct rsi_hw *adapter;
+
+ rsi_dbg(INIT_ZONE, "%s: Init function called\n", __func__);
+
+ adapter = rsi_91x_init();
+ if (!adapter) {
+ rsi_dbg(ERR_ZONE, "%s: Failed to init os intf ops\n",
+ __func__);
+ return 1;
+ }
+
+ if (rsi_init_sdio_interface(adapter, pfunction)) {
+ rsi_dbg(ERR_ZONE, "%s: Failed to init sdio interface\n",
+ __func__);
+ goto fail;
+ }
+
+ if (rsi_sdio_device_init(adapter->priv)) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in device init\n", __func__);
+ sdio_claim_host(pfunction);
+ sdio_disable_func(pfunction);
+ sdio_release_host(pfunction);
+ goto fail;
+ }
+
+ sdio_claim_host(pfunction);
+ if (sdio_claim_irq(pfunction, rsi_handle_interrupt)) {
+ rsi_dbg(ERR_ZONE, "%s: Failed to request IRQ\n", __func__);
+ sdio_release_host(pfunction);
+ goto fail;
+ }
+
+ sdio_release_host(pfunction);
+ rsi_dbg(INIT_ZONE, "%s: Registered Interrupt handler\n", __func__);
+
+ return 0;
+fail:
+ rsi_91x_deinit(adapter);
+ rsi_dbg(ERR_ZONE, "%s: Failed in probe...Exiting\n", __func__);
+ return 1;
+}
+
+/**
+ * rsi_disconnect() - This function performs the reverse of the probe function.
+ * @pfunction: Pointer to the sdio_func structure.
+ *
+ * Return: void.
+ */
+static void rsi_disconnect(struct sdio_func *pfunction)
+{
+ struct rsi_hw *adapter = sdio_get_drvdata(pfunction);
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+
+ if (!adapter)
+ return;
+
+ dev->write_fail = 2;
+ rsi_mac80211_detach(adapter);
+
+ sdio_claim_host(pfunction);
+ sdio_release_irq(pfunction);
+ sdio_disable_func(pfunction);
+ rsi_91x_deinit(adapter);
+ /* Resetting to take care of the case, where-in driver is re-loaded */
+ rsi_reset_card(pfunction);
+ sdio_release_host(pfunction);
+}
+
+#ifdef CONFIG_PM
+static int rsi_suspend(struct device *dev)
+{
+ /* Not yet implemented */
+ return -ENOSYS;
+}
+
+static int rsi_resume(struct device *dev)
+{
+ /* Not yet implemented */
+ return -ENOSYS;
+}
+
+static const struct dev_pm_ops rsi_pm_ops = {
+ .suspend = rsi_suspend,
+ .resume = rsi_resume,
+};
+#endif
+
+static const struct sdio_device_id rsi_dev_table[] = {
+ { SDIO_DEVICE(0x303, 0x100) },
+ { SDIO_DEVICE(0x041B, 0x0301) },
+ { SDIO_DEVICE(0x041B, 0x0201) },
+ { SDIO_DEVICE(0x041B, 0x9330) },
+ { /* Blank */},
+};
+
+static struct sdio_driver rsi_driver = {
+ .name = "RSI-SDIO WLAN",
+ .probe = rsi_probe,
+ .remove = rsi_disconnect,
+ .id_table = rsi_dev_table,
+#ifdef CONFIG_PM
+ .drv = {
+ .pm = &rsi_pm_ops,
+ }
+#endif
+};
+
+/**
+ * rsi_module_init() - This function registers the sdio module.
+ * @void: Void.
+ *
+ * Return: 0 on success.
+ */
+static int rsi_module_init(void)
+{
+ sdio_register_driver(&rsi_driver);
+ rsi_dbg(INIT_ZONE, "%s: Registering driver\n", __func__);
+ return 0;
+}
+
+/**
+ * rsi_module_exit() - This function unregisters the sdio module.
+ * @void: Void.
+ *
+ * Return: None.
+ */
+static void rsi_module_exit(void)
+{
+ sdio_unregister_driver(&rsi_driver);
+ rsi_dbg(INFO_ZONE, "%s: Unregistering driver\n", __func__);
+}
+
+module_init(rsi_module_init);
+module_exit(rsi_module_exit);
+
+MODULE_AUTHOR("Redpine Signals Inc");
+MODULE_DESCRIPTION("Common SDIO layer for RSI drivers");
+MODULE_SUPPORTED_DEVICE("RSI-91x");
+MODULE_DEVICE_TABLE(sdio, rsi_dev_table);
+MODULE_FIRMWARE(FIRMWARE_RSI9113);
+MODULE_VERSION("0.1");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c
new file mode 100644
index 000000000000..f1cb99cafed8
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c
@@ -0,0 +1,566 @@
+/**
+ * Copyright (c) 2014 Redpine Signals Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include <linux/firmware.h>
+#include "rsi_sdio.h"
+#include "rsi_common.h"
+
+/**
+ * rsi_sdio_master_access_msword() - This function sets the AHB master access
+ * MS word in the SDIO slave registers.
+ * @adapter: Pointer to the adapter structure.
+ * @ms_word: ms word need to be initialized.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+static int rsi_sdio_master_access_msword(struct rsi_hw *adapter,
+ u16 ms_word)
+{
+ u8 byte;
+ u8 function = 0;
+ int status = 0;
+
+ byte = (u8)(ms_word & 0x00FF);
+
+ rsi_dbg(INIT_ZONE,
+ "%s: MASTER_ACCESS_MSBYTE:0x%x\n", __func__, byte);
+
+ status = rsi_sdio_write_register(adapter,
+ function,
+ SDIO_MASTER_ACCESS_MSBYTE,
+ &byte);
+ if (status) {
+ rsi_dbg(ERR_ZONE,
+ "%s: fail to access MASTER_ACCESS_MSBYTE\n",
+ __func__);
+ return -1;
+ }
+
+ byte = (u8)(ms_word >> 8);
+
+ rsi_dbg(INIT_ZONE, "%s:MASTER_ACCESS_LSBYTE:0x%x\n", __func__, byte);
+ status = rsi_sdio_write_register(adapter,
+ function,
+ SDIO_MASTER_ACCESS_LSBYTE,
+ &byte);
+ return status;
+}
+
+/**
+ * rsi_copy_to_card() - This function includes the actual funtionality of
+ * copying the TA firmware to the card.Basically this
+ * function includes opening the TA file,reading the
+ * TA file and writing their values in blocks of data.
+ * @common: Pointer to the driver private structure.
+ * @fw: Pointer to the firmware value to be written.
+ * @len: length of firmware file.
+ * @num_blocks: Number of blocks to be written to the card.
+ *
+ * Return: 0 on success and -1 on failure.
+ */
+static int rsi_copy_to_card(struct rsi_common *common,
+ const u8 *fw,
+ u32 len,
+ u32 num_blocks)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ u32 indx, ii;
+ u32 block_size = dev->tx_blk_size;
+ u32 lsb_address;
+ __le32 data[] = { TA_HOLD_THREAD_VALUE, TA_SOFT_RST_CLR,
+ TA_PC_ZERO, TA_RELEASE_THREAD_VALUE };
+ u32 address[] = { TA_HOLD_THREAD_REG, TA_SOFT_RESET_REG,
+ TA_TH0_PC_REG, TA_RELEASE_THREAD_REG };
+ u32 base_address;
+ u16 msb_address;
+
+ base_address = TA_LOAD_ADDRESS;
+ msb_address = base_address >> 16;
+
+ for (indx = 0, ii = 0; ii < num_blocks; ii++, indx += block_size) {
+ lsb_address = ((u16) base_address | RSI_SD_REQUEST_MASTER);
+ if (rsi_sdio_write_register_multiple(adapter,
+ lsb_address,
+ (u8 *)(fw + indx),
+ block_size)) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Unable to load %s blk\n", __func__,
+ FIRMWARE_RSI9113);
+ return -1;
+ }
+ rsi_dbg(INIT_ZONE, "%s: loading block: %d\n", __func__, ii);
+ base_address += block_size;
+ if ((base_address >> 16) != msb_address) {
+ msb_address += 1;
+ if (rsi_sdio_master_access_msword(adapter,
+ msb_address)) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Unable to set ms word reg\n",
+ __func__);
+ return -1;
+ }
+ }
+ }
+
+ if (len % block_size) {
+ lsb_address = ((u16) base_address | RSI_SD_REQUEST_MASTER);
+ if (rsi_sdio_write_register_multiple(adapter,
+ lsb_address,
+ (u8 *)(fw + indx),
+ len % block_size)) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Unable to load f/w\n", __func__);
+ return -1;
+ }
+ }
+ rsi_dbg(INIT_ZONE,
+ "%s: Succesfully loaded TA instructions\n", __func__);
+
+ if (rsi_sdio_master_access_msword(adapter, TA_BASE_ADDR)) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Unable to set ms word to common reg\n",
+ __func__);
+ return -1;
+ }
+
+ for (ii = 0; ii < ARRAY_SIZE(data); ii++) {
+ /* Bringing TA out of reset */
+ if (rsi_sdio_write_register_multiple(adapter,
+ (address[ii] |
+ RSI_SD_REQUEST_MASTER),
+ (u8 *)&data[ii],
+ 4)) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Unable to hold TA threads\n", __func__);
+ return -1;
+ }
+ }
+
+ rsi_dbg(INIT_ZONE, "%s: loaded firmware\n", __func__);
+ return 0;
+}
+
+/**
+ * rsi_load_ta_instructions() - This function includes the actual funtionality
+ * of loading the TA firmware.This function also
+ * includes opening the TA file,reading the TA
+ * file and writing their value in blocks of data.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+static int rsi_load_ta_instructions(struct rsi_common *common)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ u32 len;
+ u32 num_blocks;
+ const u8 *fw;
+ const struct firmware *fw_entry = NULL;
+ u32 block_size = dev->tx_blk_size;
+ int status = 0;
+ u32 base_address;
+ u16 msb_address;
+
+ if (rsi_sdio_master_access_msword(adapter, TA_BASE_ADDR)) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Unable to set ms word to common reg\n",
+ __func__);
+ return -1;
+ }
+ base_address = TA_LOAD_ADDRESS;
+ msb_address = (base_address >> 16);
+
+ if (rsi_sdio_master_access_msword(adapter, msb_address)) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Unable to set ms word reg\n", __func__);
+ return -1;
+ }
+
+ status = request_firmware(&fw_entry, FIRMWARE_RSI9113, adapter->device);
+ if (status < 0) {
+ rsi_dbg(ERR_ZONE, "%s Firmware file %s not found\n",
+ __func__, FIRMWARE_RSI9113);
+ return status;
+ }
+
+ fw = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL);
+ len = fw_entry->size;
+
+ if (len % 4)
+ len += (4 - (len % 4));
+
+ num_blocks = (len / block_size);
+
+ rsi_dbg(INIT_ZONE, "%s: Instruction size:%d\n", __func__, len);
+ rsi_dbg(INIT_ZONE, "%s: num blocks: %d\n", __func__, num_blocks);
+
+ status = rsi_copy_to_card(common, fw, len, num_blocks);
+ release_firmware(fw_entry);
+ return status;
+}
+
+/**
+ * rsi_process_pkt() - This Function reads rx_blocks register and figures out
+ * the size of the rx pkt.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_process_pkt(struct rsi_common *common)
+{
+ struct rsi_hw *adapter = common->priv;
+ u8 num_blks = 0;
+ u32 rcv_pkt_len = 0;
+ int status = 0;
+
+ status = rsi_sdio_read_register(adapter,
+ SDIO_RX_NUM_BLOCKS_REG,
+ &num_blks);
+
+ if (status) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to read pkt length from the card:\n",
+ __func__);
+ return status;
+ }
+ rcv_pkt_len = (num_blks * 256);
+
+ common->rx_data_pkt = kmalloc(rcv_pkt_len, GFP_KERNEL);
+ if (!common->rx_data_pkt) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in memory allocation\n",
+ __func__);
+ return -1;
+ }
+
+ status = rsi_sdio_host_intf_read_pkt(adapter,
+ common->rx_data_pkt,
+ rcv_pkt_len);
+ if (status) {
+ rsi_dbg(ERR_ZONE, "%s: Failed to read packet from card\n",
+ __func__);
+ goto fail;
+ }
+
+ status = rsi_read_pkt(common, rcv_pkt_len);
+ kfree(common->rx_data_pkt);
+ return status;
+
+fail:
+ kfree(common->rx_data_pkt);
+ return -1;
+}
+
+/**
+ * rsi_init_sdio_slave_regs() - This function does the actual initialization
+ * of SDBUS slave registers.
+ * @adapter: Pointer to the adapter structure.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+int rsi_init_sdio_slave_regs(struct rsi_hw *adapter)
+{
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ u8 function = 0;
+ u8 byte;
+ int status = 0;
+
+ if (dev->next_read_delay) {
+ byte = dev->next_read_delay;
+ status = rsi_sdio_write_register(adapter,
+ function,
+ SDIO_NXT_RD_DELAY2,
+ &byte);
+ if (status) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to write SDIO_NXT_RD_DELAY2\n",
+ __func__);
+ return -1;
+ }
+ }
+
+ if (dev->sdio_high_speed_enable) {
+ rsi_dbg(INIT_ZONE, "%s: Enabling SDIO High speed\n", __func__);
+ byte = 0x3;
+
+ status = rsi_sdio_write_register(adapter,
+ function,
+ SDIO_REG_HIGH_SPEED,
+ &byte);
+ if (status) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to enable SDIO high speed\n",
+ __func__);
+ return -1;
+ }
+ }
+
+ /* This tells SDIO FIFO when to start read to host */
+ rsi_dbg(INIT_ZONE, "%s: Initialzing SDIO read start level\n", __func__);
+ byte = 0x24;
+
+ status = rsi_sdio_write_register(adapter,
+ function,
+ SDIO_READ_START_LVL,
+ &byte);
+ if (status) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to write SDIO_READ_START_LVL\n", __func__);
+ return -1;
+ }
+
+ rsi_dbg(INIT_ZONE, "%s: Initialzing FIFO ctrl registers\n", __func__);
+ byte = (128 - 32);
+
+ status = rsi_sdio_write_register(adapter,
+ function,
+ SDIO_READ_FIFO_CTL,
+ &byte);
+ if (status) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to write SDIO_READ_FIFO_CTL\n", __func__);
+ return -1;
+ }
+
+ byte = 32;
+ status = rsi_sdio_write_register(adapter,
+ function,
+ SDIO_WRITE_FIFO_CTL,
+ &byte);
+ if (status) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to write SDIO_WRITE_FIFO_CTL\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * rsi_interrupt_handler() - This function read and process SDIO interrupts.
+ * @adapter: Pointer to the adapter structure.
+ *
+ * Return: None.
+ */
+void rsi_interrupt_handler(struct rsi_hw *adapter)
+{
+ struct rsi_common *common = adapter->priv;
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ int status;
+ enum sdio_interrupt_type isr_type;
+ u8 isr_status = 0;
+ u8 fw_status = 0;
+
+ dev->rx_info.sdio_int_counter++;
+
+ do {
+ mutex_lock(&common->tx_rxlock);
+ status = rsi_sdio_read_register(common->priv,
+ RSI_FN1_INT_REGISTER,
+ &isr_status);
+ if (status) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to Read Intr Status Register\n",
+ __func__);
+ mutex_unlock(&common->tx_rxlock);
+ return;
+ }
+
+ if (isr_status == 0) {
+ rsi_set_event(&common->tx_thread.event);
+ dev->rx_info.sdio_intr_status_zero++;
+ mutex_unlock(&common->tx_rxlock);
+ return;
+ }
+
+ rsi_dbg(ISR_ZONE, "%s: Intr_status = %x %d %d\n",
+ __func__, isr_status, (1 << MSDU_PKT_PENDING),
+ (1 << FW_ASSERT_IND));
+
+ do {
+ RSI_GET_SDIO_INTERRUPT_TYPE(isr_status, isr_type);
+
+ switch (isr_type) {
+ case BUFFER_AVAILABLE:
+ dev->rx_info.watch_bufferfull_count = 0;
+ dev->rx_info.buffer_full = false;
+ dev->rx_info.mgmt_buffer_full = false;
+ rsi_sdio_ack_intr(common->priv,
+ (1 << PKT_BUFF_AVAILABLE));
+ rsi_set_event((&common->tx_thread.event));
+ rsi_dbg(ISR_ZONE,
+ "%s: ==> BUFFER_AVILABLE <==\n",
+ __func__);
+ dev->rx_info.buf_avilable_counter++;
+ break;
+
+ case FIRMWARE_ASSERT_IND:
+ rsi_dbg(ERR_ZONE,
+ "%s: ==> FIRMWARE Assert <==\n",
+ __func__);
+ status = rsi_sdio_read_register(common->priv,
+ SDIO_FW_STATUS_REG,
+ &fw_status);
+ if (status) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to read f/w reg\n",
+ __func__);
+ } else {
+ rsi_dbg(ERR_ZONE,
+ "%s: Firmware Status is 0x%x\n",
+ __func__ , fw_status);
+ rsi_sdio_ack_intr(common->priv,
+ (1 << FW_ASSERT_IND));
+ }
+
+ common->fsm_state = FSM_CARD_NOT_READY;
+ break;
+
+ case MSDU_PACKET_PENDING:
+ rsi_dbg(ISR_ZONE, "Pkt pending interrupt\n");
+ dev->rx_info.total_sdio_msdu_pending_intr++;
+
+ status = rsi_process_pkt(common);
+ if (status) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to read pkt\n",
+ __func__);
+ mutex_unlock(&common->tx_rxlock);
+ return;
+ }
+ break;
+ default:
+ rsi_sdio_ack_intr(common->priv, isr_status);
+ dev->rx_info.total_sdio_unknown_intr++;
+ isr_status = 0;
+ rsi_dbg(ISR_ZONE,
+ "Unknown Interrupt %x\n",
+ isr_status);
+ break;
+ }
+ isr_status ^= BIT(isr_type - 1);
+ } while (isr_status);
+ mutex_unlock(&common->tx_rxlock);
+ } while (1);
+}
+
+/**
+ * rsi_device_init() - This Function Initializes The HAL.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int rsi_sdio_device_init(struct rsi_common *common)
+{
+ if (rsi_load_ta_instructions(common))
+ return -1;
+
+ if (rsi_sdio_master_access_msword(common->priv, MISC_CFG_BASE_ADDR)) {
+ rsi_dbg(ERR_ZONE, "%s: Unable to set ms word reg\n",
+ __func__);
+ return -1;
+ }
+ rsi_dbg(INIT_ZONE,
+ "%s: Setting ms word to 0x41050000\n", __func__);
+
+ return 0;
+}
+
+/**
+ * rsi_sdio_read_buffer_status_register() - This function is used to the read
+ * buffer status register and set
+ * relevant fields in
+ * rsi_91x_sdiodev struct.
+ * @adapter: Pointer to the driver hw structure.
+ * @q_num: The Q number whose status is to be found.
+ *
+ * Return: status: -1 on failure or else queue full/stop is indicated.
+ */
+int rsi_sdio_read_buffer_status_register(struct rsi_hw *adapter, u8 q_num)
+{
+ struct rsi_common *common = adapter->priv;
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ u8 buf_status = 0;
+ int status = 0;
+
+ status = rsi_sdio_read_register(common->priv,
+ RSI_DEVICE_BUFFER_STATUS_REGISTER,
+ &buf_status);
+
+ if (status) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to read status register\n", __func__);
+ return -1;
+ }
+
+ if (buf_status & (BIT(PKT_MGMT_BUFF_FULL))) {
+ if (!dev->rx_info.mgmt_buffer_full)
+ dev->rx_info.mgmt_buf_full_counter++;
+ dev->rx_info.mgmt_buffer_full = true;
+ } else {
+ dev->rx_info.mgmt_buffer_full = false;
+ }
+
+ if (buf_status & (BIT(PKT_BUFF_FULL))) {
+ if (!dev->rx_info.buffer_full)
+ dev->rx_info.buf_full_counter++;
+ dev->rx_info.buffer_full = true;
+ } else {
+ dev->rx_info.buffer_full = false;
+ }
+
+ if (buf_status & (BIT(PKT_BUFF_SEMI_FULL))) {
+ if (!dev->rx_info.semi_buffer_full)
+ dev->rx_info.buf_semi_full_counter++;
+ dev->rx_info.semi_buffer_full = true;
+ } else {
+ dev->rx_info.semi_buffer_full = false;
+ }
+
+ if ((q_num == MGMT_SOFT_Q) && (dev->rx_info.mgmt_buffer_full))
+ return QUEUE_FULL;
+
+ if (dev->rx_info.buffer_full)
+ return QUEUE_FULL;
+
+ return QUEUE_NOT_FULL;
+}
+
+/**
+ * rsi_sdio_determine_event_timeout() - This Function determines the event
+ * timeout duration.
+ * @adapter: Pointer to the adapter structure.
+ *
+ * Return: timeout duration is returned.
+ */
+int rsi_sdio_determine_event_timeout(struct rsi_hw *adapter)
+{
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+
+ /* Once buffer full is seen, event timeout to occur every 2 msecs */
+ if (dev->rx_info.buffer_full)
+ return 2;
+
+ return EVENT_WAIT_FOREVER;
+}
diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c
new file mode 100644
index 000000000000..bb1bf96670eb
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_91x_usb.c
@@ -0,0 +1,575 @@
+/**
+ * Copyright (c) 2014 Redpine Signals Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include <linux/module.h>
+#include "rsi_usb.h"
+
+/**
+ * rsi_usb_card_write() - This function writes to the USB Card.
+ * @adapter: Pointer to the adapter structure.
+ * @buf: Pointer to the buffer from where the data has to be taken.
+ * @len: Length to be written.
+ * @endpoint: Type of endpoint.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+static int rsi_usb_card_write(struct rsi_hw *adapter,
+ void *buf,
+ u16 len,
+ u8 endpoint)
+{
+ struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
+ int status;
+ s32 transfer;
+
+ status = usb_bulk_msg(dev->usbdev,
+ usb_sndbulkpipe(dev->usbdev,
+ dev->bulkout_endpoint_addr[endpoint - 1]),
+ buf,
+ len,
+ &transfer,
+ HZ * 5);
+
+ if (status < 0) {
+ rsi_dbg(ERR_ZONE,
+ "Card write failed with error code :%10d\n", status);
+ dev->write_fail = 1;
+ }
+ return status;
+}
+
+/**
+ * rsi_write_multiple() - This function writes multiple bytes of information
+ * to the USB card.
+ * @adapter: Pointer to the adapter structure.
+ * @addr: Address of the register.
+ * @data: Pointer to the data that has to be written.
+ * @count: Number of multiple bytes to be written.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_write_multiple(struct rsi_hw *adapter,
+ u8 endpoint,
+ u8 *data,
+ u32 count)
+{
+ struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
+ u8 *seg = dev->tx_buffer;
+
+ if (dev->write_fail)
+ return 0;
+
+ if (endpoint == MGMT_EP) {
+ memset(seg, 0, RSI_USB_TX_HEAD_ROOM);
+ memcpy(seg + RSI_USB_TX_HEAD_ROOM, data, count);
+ } else {
+ seg = ((u8 *)data - RSI_USB_TX_HEAD_ROOM);
+ }
+
+ return rsi_usb_card_write(adapter,
+ seg,
+ count + RSI_USB_TX_HEAD_ROOM,
+ endpoint);
+}
+
+/**
+ * rsi_find_bulk_in_and_out_endpoints() - This function initializes the bulk
+ * endpoints to the device.
+ * @interface: Pointer to the USB interface structure.
+ * @adapter: Pointer to the adapter structure.
+ *
+ * Return: ret_val: 0 on success, -ENOMEM on failure.
+ */
+static int rsi_find_bulk_in_and_out_endpoints(struct usb_interface *interface,
+ struct rsi_hw *adapter)
+{
+ struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ __le16 buffer_size;
+ int ii, bep_found = 0;
+
+ iface_desc = &(interface->altsetting[0]);
+
+ for (ii = 0; ii < iface_desc->desc.bNumEndpoints; ++ii) {
+ endpoint = &(iface_desc->endpoint[ii].desc);
+
+ if ((!(dev->bulkin_endpoint_addr)) &&
+ (endpoint->bEndpointAddress & USB_DIR_IN) &&
+ ((endpoint->bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_BULK)) {
+ buffer_size = endpoint->wMaxPacketSize;
+ dev->bulkin_size = buffer_size;
+ dev->bulkin_endpoint_addr =
+ endpoint->bEndpointAddress;
+ }
+
+ if (!dev->bulkout_endpoint_addr[bep_found] &&
+ !(endpoint->bEndpointAddress & USB_DIR_IN) &&
+ ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_BULK)) {
+ dev->bulkout_endpoint_addr[bep_found] =
+ endpoint->bEndpointAddress;
+ buffer_size = endpoint->wMaxPacketSize;
+ dev->bulkout_size[bep_found] = buffer_size;
+ bep_found++;
+ }
+
+ if (bep_found >= MAX_BULK_EP)
+ break;
+ }
+
+ if (!(dev->bulkin_endpoint_addr) &&
+ (dev->bulkout_endpoint_addr[0]))
+ return -EINVAL;
+
+ return 0;
+}
+
+/* rsi_usb_reg_read() - This function reads data from given register address.
+ * @usbdev: Pointer to the usb_device structure.
+ * @reg: Address of the register to be read.
+ * @value: Value to be read.
+ * @len: length of data to be read.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+static int rsi_usb_reg_read(struct usb_device *usbdev,
+ u32 reg,
+ u16 *value,
+ u16 len)
+{
+ u8 temp_buf[4];
+ int status = 0;
+
+ status = usb_control_msg(usbdev,
+ usb_rcvctrlpipe(usbdev, 0),
+ USB_VENDOR_REGISTER_READ,
+ USB_TYPE_VENDOR,
+ ((reg & 0xffff0000) >> 16), (reg & 0xffff),
+ (void *)temp_buf,
+ len,
+ HZ * 5);
+
+ *value = (temp_buf[0] | (temp_buf[1] << 8));
+ if (status < 0) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Reg read failed with error code :%d\n",
+ __func__, status);
+ }
+ return status;
+}
+
+/**
+ * rsi_usb_reg_write() - This function writes the given data into the given
+ * register address.
+ * @usbdev: Pointer to the usb_device structure.
+ * @reg: Address of the register.
+ * @value: Value to write.
+ * @len: Length of data to be written.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+static int rsi_usb_reg_write(struct usb_device *usbdev,
+ u32 reg,
+ u16 value,
+ u16 len)
+{
+ u8 usb_reg_buf[4];
+ int status = 0;
+
+ usb_reg_buf[0] = (value & 0x00ff);
+ usb_reg_buf[1] = (value & 0xff00) >> 8;
+ usb_reg_buf[2] = 0x0;
+ usb_reg_buf[3] = 0x0;
+
+ status = usb_control_msg(usbdev,
+ usb_sndctrlpipe(usbdev, 0),
+ USB_VENDOR_REGISTER_WRITE,
+ USB_TYPE_VENDOR,
+ ((reg & 0xffff0000) >> 16),
+ (reg & 0xffff),
+ (void *)usb_reg_buf,
+ len,
+ HZ * 5);
+ if (status < 0) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Reg write failed with error code :%d\n",
+ __func__, status);
+ }
+ return status;
+}
+
+/**
+ * rsi_rx_done_handler() - This function is called when a packet is received
+ * from USB stack. This is callback to recieve done.
+ * @urb: Received URB.
+ *
+ * Return: None.
+ */
+static void rsi_rx_done_handler(struct urb *urb)
+{
+ struct rsi_hw *adapter = urb->context;
+ struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
+
+ if (urb->status)
+ return;
+
+ rsi_set_event(&dev->rx_thread.event);
+}
+
+/**
+ * rsi_rx_urb_submit() - This function submits the given URB to the USB stack.
+ * @adapter: Pointer to the adapter structure.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_rx_urb_submit(struct rsi_hw *adapter)
+{
+ struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
+ struct urb *urb = dev->rx_usb_urb[0];
+ int status;
+
+ usb_fill_bulk_urb(urb,
+ dev->usbdev,
+ usb_rcvbulkpipe(dev->usbdev,
+ dev->bulkin_endpoint_addr),
+ urb->transfer_buffer,
+ 3000,
+ rsi_rx_done_handler,
+ adapter);
+
+ status = usb_submit_urb(urb, GFP_KERNEL);
+ if (status)
+ rsi_dbg(ERR_ZONE, "%s: Failed in urb submission\n", __func__);
+
+ return status;
+}
+
+/**
+ * rsi_usb_write_register_multiple() - This function writes multiple bytes of
+ * information to multiple registers.
+ * @adapter: Pointer to the adapter structure.
+ * @addr: Address of the register.
+ * @data: Pointer to the data that has to be written.
+ * @count: Number of multiple bytes to be written on to the registers.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+int rsi_usb_write_register_multiple(struct rsi_hw *adapter,
+ u32 addr,
+ u8 *data,
+ u32 count)
+{
+ struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
+ u8 *buf;
+ u8 transfer;
+ int status = 0;
+
+ buf = kzalloc(4096, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ while (count) {
+ transfer = min_t(int, count, 4096);
+ memcpy(buf, data, transfer);
+ status = usb_control_msg(dev->usbdev,
+ usb_sndctrlpipe(dev->usbdev, 0),
+ USB_VENDOR_REGISTER_WRITE,
+ USB_TYPE_VENDOR,
+ ((addr & 0xffff0000) >> 16),
+ (addr & 0xffff),
+ (void *)buf,
+ transfer,
+ HZ * 5);
+ if (status < 0) {
+ rsi_dbg(ERR_ZONE,
+ "Reg write failed with error code :%d\n",
+ status);
+ } else {
+ count -= transfer;
+ data += transfer;
+ addr += transfer;
+ }
+ }
+
+ kfree(buf);
+ return 0;
+}
+
+/**
+ *rsi_usb_host_intf_write_pkt() - This function writes the packet to the
+ * USB card.
+ * @adapter: Pointer to the adapter structure.
+ * @pkt: Pointer to the data to be written on to the card.
+ * @len: Length of the data to be written on to the card.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_usb_host_intf_write_pkt(struct rsi_hw *adapter,
+ u8 *pkt,
+ u32 len)
+{
+ u32 queueno = ((pkt[1] >> 4) & 0xf);
+ u8 endpoint;
+
+ endpoint = ((queueno == RSI_WIFI_MGMT_Q) ? MGMT_EP : DATA_EP);
+
+ return rsi_write_multiple(adapter,
+ endpoint,
+ (u8 *)pkt,
+ len);
+}
+
+/**
+ * rsi_deinit_usb_interface() - This function deinitializes the usb interface.
+ * @adapter: Pointer to the adapter structure.
+ *
+ * Return: None.
+ */
+static void rsi_deinit_usb_interface(struct rsi_hw *adapter)
+{
+ struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
+
+ rsi_kill_thread(&dev->rx_thread);
+ kfree(adapter->priv->rx_data_pkt);
+ kfree(dev->tx_buffer);
+}
+
+/**
+ * rsi_init_usb_interface() - This function initializes the usb interface.
+ * @adapter: Pointer to the adapter structure.
+ * @pfunction: Pointer to USB interface structure.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_init_usb_interface(struct rsi_hw *adapter,
+ struct usb_interface *pfunction)
+{
+ struct rsi_91x_usbdev *rsi_dev;
+ struct rsi_common *common = adapter->priv;
+ int status;
+
+ rsi_dev = kzalloc(sizeof(*rsi_dev), GFP_KERNEL);
+ if (!rsi_dev)
+ return -ENOMEM;
+
+ adapter->rsi_dev = rsi_dev;
+ rsi_dev->usbdev = interface_to_usbdev(pfunction);
+
+ if (rsi_find_bulk_in_and_out_endpoints(pfunction, adapter))
+ return -EINVAL;
+
+ adapter->device = &pfunction->dev;
+ usb_set_intfdata(pfunction, adapter);
+
+ common->rx_data_pkt = kmalloc(2048, GFP_KERNEL);
+ if (!common->rx_data_pkt) {
+ rsi_dbg(ERR_ZONE, "%s: Failed to allocate memory\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ rsi_dev->tx_buffer = kmalloc(2048, GFP_ATOMIC);
+ rsi_dev->rx_usb_urb[0] = usb_alloc_urb(0, GFP_KERNEL);
+ rsi_dev->rx_usb_urb[0]->transfer_buffer = adapter->priv->rx_data_pkt;
+ rsi_dev->tx_blk_size = 252;
+
+ /* Initializing function callbacks */
+ adapter->rx_urb_submit = rsi_rx_urb_submit;
+ adapter->host_intf_write_pkt = rsi_usb_host_intf_write_pkt;
+ adapter->check_hw_queue_status = rsi_usb_check_queue_status;
+ adapter->determine_event_timeout = rsi_usb_event_timeout;
+
+ rsi_init_event(&rsi_dev->rx_thread.event);
+ status = rsi_create_kthread(common, &rsi_dev->rx_thread,
+ rsi_usb_rx_thread, "RX-Thread");
+ if (status) {
+ rsi_dbg(ERR_ZONE, "%s: Unable to init rx thrd\n", __func__);
+ goto fail;
+ }
+
+#ifdef CONFIG_RSI_DEBUGFS
+ /* In USB, one less than the MAX_DEBUGFS_ENTRIES entries is required */
+ adapter->num_debugfs_entries = (MAX_DEBUGFS_ENTRIES - 1);
+#endif
+
+ rsi_dbg(INIT_ZONE, "%s: Enabled the interface\n", __func__);
+ return 0;
+
+fail:
+ kfree(rsi_dev->tx_buffer);
+ kfree(common->rx_data_pkt);
+ return status;
+}
+
+/**
+ * rsi_probe() - This function is called by kernel when the driver provided
+ * Vendor and device IDs are matched. All the initialization
+ * work is done here.
+ * @pfunction: Pointer to the USB interface structure.
+ * @id: Pointer to the usb_device_id structure.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_probe(struct usb_interface *pfunction,
+ const struct usb_device_id *id)
+{
+ struct rsi_hw *adapter;
+ struct rsi_91x_usbdev *dev;
+ u16 fw_status;
+
+ rsi_dbg(INIT_ZONE, "%s: Init function called\n", __func__);
+
+ adapter = rsi_91x_init();
+ if (!adapter) {
+ rsi_dbg(ERR_ZONE, "%s: Failed to init os intf ops\n",
+ __func__);
+ return 1;
+ }
+
+ if (rsi_init_usb_interface(adapter, pfunction)) {
+ rsi_dbg(ERR_ZONE, "%s: Failed to init usb interface\n",
+ __func__);
+ goto err;
+ }
+
+ rsi_dbg(ERR_ZONE, "%s: Initialized os intf ops\n", __func__);
+
+ dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
+
+ if (rsi_usb_reg_read(dev->usbdev, FW_STATUS_REG, &fw_status, 2) < 0)
+ goto err1;
+ else
+ fw_status &= 1;
+
+ if (!fw_status) {
+ if (rsi_usb_device_init(adapter->priv)) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in device init\n",
+ __func__);
+ goto err1;
+ }
+
+ if (rsi_usb_reg_write(dev->usbdev,
+ USB_INTERNAL_REG_1,
+ RSI_USB_READY_MAGIC_NUM, 1) < 0)
+ goto err1;
+ rsi_dbg(INIT_ZONE, "%s: Performed device init\n", __func__);
+ }
+
+ if (rsi_rx_urb_submit(adapter))
+ goto err1;
+
+ return 0;
+err1:
+ rsi_deinit_usb_interface(adapter);
+err:
+ rsi_91x_deinit(adapter);
+ rsi_dbg(ERR_ZONE, "%s: Failed in probe...Exiting\n", __func__);
+ return 1;
+}
+
+/**
+ * rsi_disconnect() - This function performs the reverse of the probe function,
+ * it deintialize the driver structure.
+ * @pfunction: Pointer to the USB interface structure.
+ *
+ * Return: None.
+ */
+static void rsi_disconnect(struct usb_interface *pfunction)
+{
+ struct rsi_hw *adapter = usb_get_intfdata(pfunction);
+
+ if (!adapter)
+ return;
+
+ rsi_mac80211_detach(adapter);
+ rsi_deinit_usb_interface(adapter);
+ rsi_91x_deinit(adapter);
+
+ rsi_dbg(INFO_ZONE, "%s: Deinitialization completed\n", __func__);
+}
+
+#ifdef CONFIG_PM
+static int rsi_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ /* Not yet implemented */
+ return -ENOSYS;
+}
+
+static int rsi_resume(struct usb_interface *intf)
+{
+ /* Not yet implemented */
+ return -ENOSYS;
+}
+#endif
+
+static const struct usb_device_id rsi_dev_table[] = {
+ { USB_DEVICE(0x0303, 0x0100) },
+ { USB_DEVICE(0x041B, 0x0301) },
+ { USB_DEVICE(0x041B, 0x0201) },
+ { USB_DEVICE(0x041B, 0x9330) },
+ { /* Blank */},
+};
+
+static struct usb_driver rsi_driver = {
+ .name = "RSI-USB WLAN",
+ .probe = rsi_probe,
+ .disconnect = rsi_disconnect,
+ .id_table = rsi_dev_table,
+#ifdef CONFIG_PM
+ .suspend = rsi_suspend,
+ .resume = rsi_resume,
+#endif
+};
+
+/**
+ * rsi_module_init() - This function registers the client driver.
+ * @void: Void.
+ *
+ * Return: 0 on success.
+ */
+static int rsi_module_init(void)
+{
+ usb_register(&rsi_driver);
+ rsi_dbg(INIT_ZONE, "%s: Registering driver\n", __func__);
+ return 0;
+}
+
+/**
+ * rsi_module_exit() - This function unregisters the client driver.
+ * @void: Void.
+ *
+ * Return: None.
+ */
+static void rsi_module_exit(void)
+{
+ usb_deregister(&rsi_driver);
+ rsi_dbg(INFO_ZONE, "%s: Unregistering driver\n", __func__);
+}
+
+module_init(rsi_module_init);
+module_exit(rsi_module_exit);
+
+MODULE_AUTHOR("Redpine Signals Inc");
+MODULE_DESCRIPTION("Common USB layer for RSI drivers");
+MODULE_SUPPORTED_DEVICE("RSI-91x");
+MODULE_DEVICE_TABLE(usb, rsi_dev_table);
+MODULE_FIRMWARE(FIRMWARE_RSI9113);
+MODULE_VERSION("0.1");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/rsi/rsi_91x_usb_ops.c b/drivers/net/wireless/rsi/rsi_91x_usb_ops.c
new file mode 100644
index 000000000000..1106ce76707e
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_91x_usb_ops.c
@@ -0,0 +1,177 @@
+/**
+ * Copyright (c) 2014 Redpine Signals Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include <linux/firmware.h>
+#include "rsi_usb.h"
+
+/**
+ * rsi_copy_to_card() - This function includes the actual funtionality of
+ * copying the TA firmware to the card.Basically this
+ * function includes opening the TA file,reading the TA
+ * file and writing their values in blocks of data.
+ * @common: Pointer to the driver private structure.
+ * @fw: Pointer to the firmware value to be written.
+ * @len: length of firmware file.
+ * @num_blocks: Number of blocks to be written to the card.
+ *
+ * Return: 0 on success and -1 on failure.
+ */
+static int rsi_copy_to_card(struct rsi_common *common,
+ const u8 *fw,
+ u32 len,
+ u32 num_blocks)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
+ u32 indx, ii;
+ u32 block_size = dev->tx_blk_size;
+ u32 lsb_address;
+ u32 base_address;
+
+ base_address = TA_LOAD_ADDRESS;
+
+ for (indx = 0, ii = 0; ii < num_blocks; ii++, indx += block_size) {
+ lsb_address = base_address;
+ if (rsi_usb_write_register_multiple(adapter,
+ lsb_address,
+ (u8 *)(fw + indx),
+ block_size)) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Unable to load %s blk\n", __func__,
+ FIRMWARE_RSI9113);
+ return -EIO;
+ }
+ rsi_dbg(INIT_ZONE, "%s: loading block: %d\n", __func__, ii);
+ base_address += block_size;
+ }
+
+ if (len % block_size) {
+ lsb_address = base_address;
+ if (rsi_usb_write_register_multiple(adapter,
+ lsb_address,
+ (u8 *)(fw + indx),
+ len % block_size)) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Unable to load %s blk\n", __func__,
+ FIRMWARE_RSI9113);
+ return -EIO;
+ }
+ }
+ rsi_dbg(INIT_ZONE,
+ "%s: Succesfully loaded %s instructions\n", __func__,
+ FIRMWARE_RSI9113);
+
+ rsi_dbg(INIT_ZONE, "%s: loaded firmware\n", __func__);
+ return 0;
+}
+
+/**
+ * rsi_usb_rx_thread() - This is a kernel thread to receive the packets from
+ * the USB device.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: None.
+ */
+void rsi_usb_rx_thread(struct rsi_common *common)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
+ int status;
+
+ do {
+ rsi_wait_event(&dev->rx_thread.event, EVENT_WAIT_FOREVER);
+
+ if (atomic_read(&dev->rx_thread.thread_done))
+ goto out;
+
+ mutex_lock(&common->tx_rxlock);
+ status = rsi_read_pkt(common, 0);
+ if (status) {
+ rsi_dbg(ERR_ZONE, "%s: Failed To read data", __func__);
+ mutex_unlock(&common->tx_rxlock);
+ return;
+ }
+ mutex_unlock(&common->tx_rxlock);
+ rsi_reset_event(&dev->rx_thread.event);
+ if (adapter->rx_urb_submit(adapter)) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed in urb submission", __func__);
+ return;
+ }
+ } while (1);
+
+out:
+ rsi_dbg(INFO_ZONE, "%s: Terminated thread\n", __func__);
+ complete_and_exit(&dev->rx_thread.completion, 0);
+}
+
+
+/**
+ * rsi_load_ta_instructions() - This function includes the actual funtionality
+ * of loading the TA firmware.This function also
+ * includes opening the TA file,reading the TA
+ * file and writing their value in blocks of data.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+static int rsi_load_ta_instructions(struct rsi_common *common)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
+ const struct firmware *fw_entry = NULL;
+ u32 block_size = dev->tx_blk_size;
+ const u8 *fw;
+ u32 num_blocks, len;
+ int status = 0;
+
+ status = request_firmware(&fw_entry, FIRMWARE_RSI9113, adapter->device);
+ if (status < 0) {
+ rsi_dbg(ERR_ZONE, "%s Firmware file %s not found\n",
+ __func__, FIRMWARE_RSI9113);
+ return status;
+ }
+
+ fw = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL);
+ len = fw_entry->size;
+
+ if (len % 4)
+ len += (4 - (len % 4));
+
+ num_blocks = (len / block_size);
+
+ rsi_dbg(INIT_ZONE, "%s: Instruction size:%d\n", __func__, len);
+ rsi_dbg(INIT_ZONE, "%s: num blocks: %d\n", __func__, num_blocks);
+
+ status = rsi_copy_to_card(common, fw, len, num_blocks);
+ release_firmware(fw_entry);
+ return status;
+}
+
+/**
+ * rsi_device_init() - This Function Initializes The HAL.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int rsi_usb_device_init(struct rsi_common *common)
+{
+ if (rsi_load_ta_instructions(common))
+ return -EIO;
+
+ return 0;
+ }
diff --git a/drivers/net/wireless/rsi/rsi_boot_params.h b/drivers/net/wireless/rsi/rsi_boot_params.h
new file mode 100644
index 000000000000..5e2721f7909c
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_boot_params.h
@@ -0,0 +1,126 @@
+/**
+ * Copyright (c) 2014 Redpine Signals Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __RSI_BOOTPARAMS_HEADER_H__
+#define __RSI_BOOTPARAMS_HEADER_H__
+
+#define CRYSTAL_GOOD_TIME BIT(0)
+#define BOOTUP_MODE_INFO BIT(1)
+#define WIFI_TAPLL_CONFIGS BIT(5)
+#define WIFI_PLL960_CONFIGS BIT(6)
+#define WIFI_AFEPLL_CONFIGS BIT(7)
+#define WIFI_SWITCH_CLK_CONFIGS BIT(8)
+
+#define TA_PLL_M_VAL_20 8
+#define TA_PLL_N_VAL_20 1
+#define TA_PLL_P_VAL_20 4
+
+#define PLL960_M_VAL_20 0x14
+#define PLL960_N_VAL_20 0
+#define PLL960_P_VAL_20 5
+
+#define UMAC_CLK_40MHZ 40
+
+#define TA_PLL_M_VAL_40 46
+#define TA_PLL_N_VAL_40 3
+#define TA_PLL_P_VAL_40 3
+
+#define PLL960_M_VAL_40 0x14
+#define PLL960_N_VAL_40 0
+#define PLL960_P_VAL_40 5
+
+#define UMAC_CLK_20BW \
+ (((TA_PLL_M_VAL_20 + 1) * 40) / \
+ ((TA_PLL_N_VAL_20 + 1) * (TA_PLL_P_VAL_20 + 1)))
+#define VALID_20 \
+ (WIFI_PLL960_CONFIGS | WIFI_AFEPLL_CONFIGS | WIFI_SWITCH_CLK_CONFIGS)
+#define UMAC_CLK_40BW \
+ (((TA_PLL_M_VAL_40 + 1) * 40) / \
+ ((TA_PLL_N_VAL_40 + 1) * (TA_PLL_P_VAL_40 + 1)))
+#define VALID_40 \
+ (WIFI_PLL960_CONFIGS | WIFI_AFEPLL_CONFIGS | WIFI_SWITCH_CLK_CONFIGS | \
+ WIFI_TAPLL_CONFIGS | CRYSTAL_GOOD_TIME | BOOTUP_MODE_INFO)
+
+/* structure to store configs related to TAPLL programming */
+struct tapll_info {
+ __le16 pll_reg_1;
+ __le16 pll_reg_2;
+} __packed;
+
+/* structure to store configs related to PLL960 programming */
+struct pll960_info {
+ __le16 pll_reg_1;
+ __le16 pll_reg_2;
+ __le16 pll_reg_3;
+} __packed;
+
+/* structure to store configs related to AFEPLL programming */
+struct afepll_info {
+ __le16 pll_reg;
+} __packed;
+
+/* structure to store configs related to pll configs */
+struct pll_config {
+ struct tapll_info tapll_info_g;
+ struct pll960_info pll960_info_g;
+ struct afepll_info afepll_info_g;
+} __packed;
+
+/* structure to store configs related to UMAC clk programming */
+struct switch_clk {
+ __le16 switch_clk_info;
+ /* If switch_bbp_lmac_clk_reg is set then this value will be programmed
+ * into reg
+ */
+ __le16 bbp_lmac_clk_reg_val;
+ /* if switch_umac_clk is set then this value will be programmed */
+ __le16 umac_clock_reg_config;
+ /* if switch_qspi_clk is set then this value will be programmed */
+ __le16 qspi_uart_clock_reg_config;
+} __packed;
+
+struct device_clk_info {
+ struct pll_config pll_config_g;
+ struct switch_clk switch_clk_g;
+} __packed;
+
+struct bootup_params {
+ __le16 magic_number;
+ __le16 crystal_good_time;
+ __le32 valid;
+ __le32 reserved_for_valids;
+ __le16 bootup_mode_info;
+ /* configuration used for digital loop back */
+ __le16 digital_loop_back_params;
+ __le16 rtls_timestamp_en;
+ __le16 host_spi_intr_cfg;
+ struct device_clk_info device_clk_info[3];
+ /* ulp buckboost wait time */
+ __le32 buckboost_wakeup_cnt;
+ /* pmu wakeup wait time & WDT EN info */
+ __le16 pmu_wakeup_wait;
+ u8 shutdown_wait_time;
+ /* Sleep clock source selection */
+ u8 pmu_slp_clkout_sel;
+ /* WDT programming values */
+ __le32 wdt_prog_value;
+ /* WDT soc reset delay */
+ __le32 wdt_soc_rst_delay;
+ /* dcdc modes configs */
+ __le32 dcdc_operation_mode;
+ __le32 soc_reset_wait_cnt;
+} __packed;
+#endif
diff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h
new file mode 100644
index 000000000000..f2f70784d4ad
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_common.h
@@ -0,0 +1,87 @@
+/**
+ * Copyright (c) 2014 Redpine Signals Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __RSI_COMMON_H__
+#define __RSI_COMMON_H__
+
+#include <linux/kthread.h>
+
+#define EVENT_WAIT_FOREVER 0
+#define TA_LOAD_ADDRESS 0x00
+#define FIRMWARE_RSI9113 "rsi_91x.fw"
+#define QUEUE_NOT_FULL 1
+#define QUEUE_FULL 0
+
+static inline int rsi_init_event(struct rsi_event *pevent)
+{
+ atomic_set(&pevent->event_condition, 1);
+ init_waitqueue_head(&pevent->event_queue);
+ return 0;
+}
+
+static inline int rsi_wait_event(struct rsi_event *event, u32 timeout)
+{
+ int status = 0;
+
+ if (!timeout)
+ status = wait_event_interruptible(event->event_queue,
+ (atomic_read(&event->event_condition) == 0));
+ else
+ status = wait_event_interruptible_timeout(event->event_queue,
+ (atomic_read(&event->event_condition) == 0),
+ timeout);
+ return status;
+}
+
+static inline void rsi_set_event(struct rsi_event *event)
+{
+ atomic_set(&event->event_condition, 0);
+ wake_up_interruptible(&event->event_queue);
+}
+
+static inline void rsi_reset_event(struct rsi_event *event)
+{
+ atomic_set(&event->event_condition, 1);
+}
+
+static inline int rsi_create_kthread(struct rsi_common *common,
+ struct rsi_thread *thread,
+ void *func_ptr,
+ u8 *name)
+{
+ init_completion(&thread->completion);
+ thread->task = kthread_run(func_ptr, common, name);
+ if (IS_ERR(thread->task))
+ return (int)PTR_ERR(thread->task);
+
+ return 0;
+}
+
+static inline int rsi_kill_thread(struct rsi_thread *handle)
+{
+ atomic_inc(&handle->thread_done);
+ rsi_set_event(&handle->event);
+
+ wait_for_completion(&handle->completion);
+ return kthread_stop(handle->task);
+}
+
+void rsi_mac80211_detach(struct rsi_hw *hw);
+u16 rsi_get_connected_channel(struct rsi_hw *adapter);
+struct rsi_hw *rsi_91x_init(void);
+void rsi_91x_deinit(struct rsi_hw *adapter);
+int rsi_read_pkt(struct rsi_common *common, s32 rcv_pkt_len);
+#endif
diff --git a/drivers/net/wireless/rsi/rsi_debugfs.h b/drivers/net/wireless/rsi/rsi_debugfs.h
new file mode 100644
index 000000000000..580ad3b3f710
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_debugfs.h
@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2014 Redpine Signals Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __RSI_DEBUGFS_H__
+#define __RSI_DEBUGFS_H__
+
+#include "rsi_main.h"
+#include <linux/debugfs.h>
+
+#ifndef CONFIG_RSI_DEBUGFS
+static inline int rsi_init_dbgfs(struct rsi_hw *adapter)
+{
+ return 0;
+}
+
+static inline void rsi_remove_dbgfs(struct rsi_hw *adapter)
+{
+ return;
+}
+#else
+struct rsi_dbg_files {
+ const char *name;
+ umode_t perms;
+ const struct file_operations fops;
+};
+
+struct rsi_debugfs {
+ struct dentry *subdir;
+ struct rsi_dbg_ops *dfs_get_ops;
+ struct dentry *rsi_files[MAX_DEBUGFS_ENTRIES];
+};
+int rsi_init_dbgfs(struct rsi_hw *adapter);
+void rsi_remove_dbgfs(struct rsi_hw *adapter);
+#endif
+#endif
diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h
new file mode 100644
index 000000000000..bebdc2ae6c5b
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_main.h
@@ -0,0 +1,232 @@
+/**
+ * Copyright (c) 2014 Redpine Signals Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __RSI_MAIN_H__
+#define __RSI_MAIN_H__
+
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <net/mac80211.h>
+
+#define ERR_ZONE BIT(0) /* For Error Msgs */
+#define INFO_ZONE BIT(1) /* For General Status Msgs */
+#define INIT_ZONE BIT(2) /* For Driver Init Seq Msgs */
+#define MGMT_TX_ZONE BIT(3) /* For TX Mgmt Path Msgs */
+#define MGMT_RX_ZONE BIT(4) /* For RX Mgmt Path Msgs */
+#define DATA_TX_ZONE BIT(5) /* For TX Data Path Msgs */
+#define DATA_RX_ZONE BIT(6) /* For RX Data Path Msgs */
+#define FSM_ZONE BIT(7) /* For State Machine Msgs */
+#define ISR_ZONE BIT(8) /* For Interrupt Msgs */
+
+#define FSM_CARD_NOT_READY 0
+#define FSM_BOOT_PARAMS_SENT 1
+#define FSM_EEPROM_READ_MAC_ADDR 2
+#define FSM_RESET_MAC_SENT 3
+#define FSM_RADIO_CAPS_SENT 4
+#define FSM_BB_RF_PROG_SENT 5
+#define FSM_MAC_INIT_DONE 6
+
+extern u32 rsi_zone_enabled;
+
+static inline void rsi_dbg(u32 zone, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ if (zone & rsi_zone_enabled)
+ pr_info("%pV", &vaf);
+ va_end(args);
+}
+
+#define RSI_MAX_VIFS 1
+#define NUM_EDCA_QUEUES 4
+#define IEEE80211_ADDR_LEN 6
+#define FRAME_DESC_SZ 16
+#define MIN_802_11_HDR_LEN 24
+
+#define DATA_QUEUE_WATER_MARK 400
+#define MIN_DATA_QUEUE_WATER_MARK 300
+#define MULTICAST_WATER_MARK 200
+#define MAC_80211_HDR_FRAME_CONTROL 0
+#define WME_NUM_AC 4
+#define NUM_SOFT_QUEUES 5
+#define MAX_HW_QUEUES 8
+#define INVALID_QUEUE 0xff
+#define MAX_CONTINUOUS_VO_PKTS 8
+#define MAX_CONTINUOUS_VI_PKTS 4
+
+/* Queue information */
+#define RSI_WIFI_MGMT_Q 0x4
+#define RSI_WIFI_DATA_Q 0x5
+#define IEEE80211_MGMT_FRAME 0x00
+#define IEEE80211_CTL_FRAME 0x04
+
+#define IEEE80211_QOS_TID 0x0f
+#define IEEE80211_NONQOS_TID 16
+
+#define MAX_DEBUGFS_ENTRIES 4
+
+#define TID_TO_WME_AC(_tid) ( \
+ ((_tid) == 0 || (_tid) == 3) ? BE_Q : \
+ ((_tid) < 3) ? BK_Q : \
+ ((_tid) < 6) ? VI_Q : \
+ VO_Q)
+
+#define WME_AC(_q) ( \
+ ((_q) == BK_Q) ? IEEE80211_AC_BK : \
+ ((_q) == BE_Q) ? IEEE80211_AC_BE : \
+ ((_q) == VI_Q) ? IEEE80211_AC_VI : \
+ IEEE80211_AC_VO)
+
+struct version_info {
+ u16 major;
+ u16 minor;
+ u16 release_num;
+ u16 patch_num;
+} __packed;
+
+struct skb_info {
+ s8 rssi;
+ u32 flags;
+ u16 channel;
+ s8 tid;
+ s8 sta_id;
+};
+
+enum edca_queue {
+ BK_Q,
+ BE_Q,
+ VI_Q,
+ VO_Q,
+ MGMT_SOFT_Q
+};
+
+struct security_info {
+ bool security_enable;
+ u32 ptk_cipher;
+ u32 gtk_cipher;
+};
+
+struct wmm_qinfo {
+ s32 weight;
+ s32 wme_params;
+ s32 pkt_contended;
+};
+
+struct transmit_q_stats {
+ u32 total_tx_pkt_send[NUM_EDCA_QUEUES + 1];
+ u32 total_tx_pkt_freed[NUM_EDCA_QUEUES + 1];
+};
+
+struct vif_priv {
+ bool is_ht;
+ bool sgi;
+ u16 seq_start;
+};
+
+struct rsi_event {
+ atomic_t event_condition;
+ wait_queue_head_t event_queue;
+};
+
+struct rsi_thread {
+ void (*thread_function)(void *);
+ struct completion completion;
+ struct task_struct *task;
+ struct rsi_event event;
+ atomic_t thread_done;
+};
+
+struct rsi_hw;
+
+struct rsi_common {
+ struct rsi_hw *priv;
+ struct vif_priv vif_info[RSI_MAX_VIFS];
+
+ bool mgmt_q_block;
+ struct version_info driver_ver;
+ struct version_info fw_ver;
+
+ struct rsi_thread tx_thread;
+ struct sk_buff_head tx_queue[NUM_EDCA_QUEUES + 1];
+ /* Mutex declaration */
+ struct mutex mutex;
+ /* Mutex used between tx/rx threads */
+ struct mutex tx_rxlock;
+ u8 endpoint;
+
+ /* Channel/band related */
+ u8 band;
+ u8 channel_width;
+
+ u16 rts_threshold;
+ u16 bitrate_mask[2];
+ u32 fixedrate_mask[2];
+
+ u8 rf_reset;
+ struct transmit_q_stats tx_stats;
+ struct security_info secinfo;
+ struct wmm_qinfo tx_qinfo[NUM_EDCA_QUEUES];
+ struct ieee80211_tx_queue_params edca_params[NUM_EDCA_QUEUES];
+ u8 mac_addr[IEEE80211_ADDR_LEN];
+
+ /* state related */
+ u32 fsm_state;
+ bool init_done;
+ u8 bb_rf_prog_count;
+ bool iface_down;
+
+ /* Generic */
+ u8 channel;
+ u8 *rx_data_pkt;
+ u8 mac_id;
+ u8 radio_id;
+ u16 rate_pwr[20];
+ u16 min_rate;
+
+ /* WMM algo related */
+ u8 selected_qnum;
+ u32 pkt_cnt;
+ u8 min_weight;
+};
+
+struct rsi_hw {
+ struct rsi_common *priv;
+ struct ieee80211_hw *hw;
+ struct ieee80211_vif *vifs[RSI_MAX_VIFS];
+ struct ieee80211_tx_queue_params edca_params[NUM_EDCA_QUEUES];
+ struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
+
+ struct device *device;
+ u8 sc_nvifs;
+
+#ifdef CONFIG_RSI_DEBUGFS
+ struct rsi_debugfs *dfsentry;
+ u8 num_debugfs_entries;
+#endif
+ void *rsi_dev;
+ int (*host_intf_read_pkt)(struct rsi_hw *adapter, u8 *pkt, u32 len);
+ int (*host_intf_write_pkt)(struct rsi_hw *adapter, u8 *pkt, u32 len);
+ int (*check_hw_queue_status)(struct rsi_hw *adapter, u8 q_num);
+ int (*rx_urb_submit)(struct rsi_hw *adapter);
+ int (*determine_event_timeout)(struct rsi_hw *adapter);
+};
+#endif
diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h
new file mode 100644
index 000000000000..ac67c4ad63c2
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_mgmt.h
@@ -0,0 +1,285 @@
+/**
+ * Copyright (c) 2014 Redpine Signals Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __RSI_MGMT_H__
+#define __RSI_MGMT_H__
+
+#include <linux/sort.h>
+#include "rsi_boot_params.h"
+#include "rsi_main.h"
+
+#define MAX_MGMT_PKT_SIZE 512
+#define RSI_NEEDED_HEADROOM 80
+#define RSI_RCV_BUFFER_LEN 2000
+
+#define RSI_11B_MODE 0
+#define RSI_11G_MODE BIT(7)
+#define RETRY_COUNT 8
+#define RETRY_LONG 4
+#define RETRY_SHORT 7
+#define WMM_SHORT_SLOT_TIME 9
+#define SIFS_DURATION 16
+
+#define KEY_TYPE_CLEAR 0
+#define RSI_PAIRWISE_KEY 1
+#define RSI_GROUP_KEY 2
+
+/* EPPROM_READ_ADDRESS */
+#define WLAN_MAC_EEPROM_ADDR 40
+#define WLAN_MAC_MAGIC_WORD_LEN 0x01
+#define WLAN_HOST_MODE_LEN 0x04
+#define WLAN_FW_VERSION_LEN 0x08
+#define MAGIC_WORD 0x5A
+
+/* Receive Frame Types */
+#define TA_CONFIRM_TYPE 0x01
+#define RX_DOT11_MGMT 0x02
+#define TX_STATUS_IND 0x04
+#define PROBEREQ_CONFIRM 2
+#define CARD_READY_IND 0x00
+
+#define RSI_DELETE_PEER 0x0
+#define RSI_ADD_PEER 0x1
+#define START_AMPDU_AGGR 0x1
+#define STOP_AMPDU_AGGR 0x0
+#define INTERNAL_MGMT_PKT 0x99
+
+#define PUT_BBP_RESET 0
+#define BBP_REG_WRITE 0
+#define RF_RESET_ENABLE BIT(3)
+#define RATE_INFO_ENABLE BIT(0)
+#define RSI_BROADCAST_PKT BIT(9)
+
+#define UPPER_20_ENABLE (0x2 << 12)
+#define LOWER_20_ENABLE (0x4 << 12)
+#define FULL40M_ENABLE 0x6
+
+#define RSI_LMAC_CLOCK_80MHZ 0x1
+#define RSI_ENABLE_40MHZ (0x1 << 3)
+
+#define RX_BA_INDICATION 1
+#define RSI_TBL_SZ 40
+#define MAX_RETRIES 8
+
+#define STD_RATE_MCS7 0x07
+#define STD_RATE_MCS6 0x06
+#define STD_RATE_MCS5 0x05
+#define STD_RATE_MCS4 0x04
+#define STD_RATE_MCS3 0x03
+#define STD_RATE_MCS2 0x02
+#define STD_RATE_MCS1 0x01
+#define STD_RATE_MCS0 0x00
+#define STD_RATE_54 0x6c
+#define STD_RATE_48 0x60
+#define STD_RATE_36 0x48
+#define STD_RATE_24 0x30
+#define STD_RATE_18 0x24
+#define STD_RATE_12 0x18
+#define STD_RATE_11 0x16
+#define STD_RATE_09 0x12
+#define STD_RATE_06 0x0C
+#define STD_RATE_5_5 0x0B
+#define STD_RATE_02 0x04
+#define STD_RATE_01 0x02
+
+#define RSI_RF_TYPE 1
+#define RSI_RATE_00 0x00
+#define RSI_RATE_1 0x0
+#define RSI_RATE_2 0x2
+#define RSI_RATE_5_5 0x4
+#define RSI_RATE_11 0x6
+#define RSI_RATE_6 0x8b
+#define RSI_RATE_9 0x8f
+#define RSI_RATE_12 0x8a
+#define RSI_RATE_18 0x8e
+#define RSI_RATE_24 0x89
+#define RSI_RATE_36 0x8d
+#define RSI_RATE_48 0x88
+#define RSI_RATE_54 0x8c
+#define RSI_RATE_MCS0 0x100
+#define RSI_RATE_MCS1 0x101
+#define RSI_RATE_MCS2 0x102
+#define RSI_RATE_MCS3 0x103
+#define RSI_RATE_MCS4 0x104
+#define RSI_RATE_MCS5 0x105
+#define RSI_RATE_MCS6 0x106
+#define RSI_RATE_MCS7 0x107
+#define RSI_RATE_MCS7_SG 0x307
+
+#define BW_20MHZ 0
+#define BW_40MHZ 1
+
+#define RSI_SUPP_FILTERS (FIF_ALLMULTI | FIF_PROBE_REQ |\
+ FIF_BCN_PRBRESP_PROMISC)
+enum opmode {
+ STA_OPMODE = 1,
+ AP_OPMODE = 2
+};
+
+extern struct ieee80211_rate rsi_rates[12];
+extern const u16 rsi_mcsrates[8];
+
+enum sta_notify_events {
+ STA_CONNECTED = 0,
+ STA_DISCONNECTED,
+ STA_TX_ADDBA_DONE,
+ STA_TX_DELBA,
+ STA_RX_ADDBA_DONE,
+ STA_RX_DELBA
+};
+
+/* Send Frames Types */
+enum cmd_frame_type {
+ TX_DOT11_MGMT,
+ RESET_MAC_REQ,
+ RADIO_CAPABILITIES,
+ BB_PROG_VALUES_REQUEST,
+ RF_PROG_VALUES_REQUEST,
+ WAKEUP_SLEEP_REQUEST,
+ SCAN_REQUEST,
+ TSF_UPDATE,
+ PEER_NOTIFY,
+ BLOCK_UNBLOCK,
+ SET_KEY_REQ,
+ AUTO_RATE_IND,
+ BOOTUP_PARAMS_REQUEST,
+ VAP_CAPABILITIES,
+ EEPROM_READ_TYPE ,
+ EEPROM_WRITE,
+ GPIO_PIN_CONFIG ,
+ SET_RX_FILTER,
+ AMPDU_IND,
+ STATS_REQUEST_FRAME,
+ BB_BUF_PROG_VALUES_REQ,
+ BBP_PROG_IN_TA,
+ BG_SCAN_PARAMS,
+ BG_SCAN_PROBE_REQ,
+ CW_MODE_REQ,
+ PER_CMD_PKT
+};
+
+struct rsi_mac_frame {
+ __le16 desc_word[8];
+} __packed;
+
+struct rsi_boot_params {
+ __le16 desc_word[8];
+ struct bootup_params bootup_params;
+} __packed;
+
+struct rsi_peer_notify {
+ __le16 desc_word[8];
+ u8 mac_addr[6];
+ __le16 command;
+ __le16 mpdu_density;
+ __le16 reserved;
+ __le32 sta_flags;
+} __packed;
+
+struct rsi_vap_caps {
+ __le16 desc_word[8];
+ u8 mac_addr[6];
+ __le16 keep_alive_period;
+ u8 bssid[6];
+ __le16 reserved;
+ __le32 flags;
+ __le16 frag_threshold;
+ __le16 rts_threshold;
+ __le32 default_mgmt_rate;
+ __le32 default_ctrl_rate;
+ __le32 default_data_rate;
+ __le16 beacon_interval;
+ __le16 dtim_period;
+} __packed;
+
+struct rsi_set_key {
+ __le16 desc_word[8];
+ u8 key[4][32];
+ u8 tx_mic_key[8];
+ u8 rx_mic_key[8];
+} __packed;
+
+struct rsi_auto_rate {
+ __le16 desc_word[8];
+ __le16 failure_limit;
+ __le16 initial_boundary;
+ __le16 max_threshold_limt;
+ __le16 num_supported_rates;
+ __le16 aarf_rssi;
+ __le16 moderate_rate_inx;
+ __le16 collision_tolerance;
+ __le16 supported_rates[40];
+} __packed;
+
+struct qos_params {
+ __le16 cont_win_min_q;
+ __le16 cont_win_max_q;
+ __le16 aifsn_val_q;
+ __le16 txop_q;
+} __packed;
+
+struct rsi_radio_caps {
+ __le16 desc_word[8];
+ struct qos_params qos_params[MAX_HW_QUEUES];
+ u8 num_11n_rates;
+ u8 num_11ac_rates;
+ __le16 gcpd_per_rate[20];
+} __packed;
+
+static inline u32 rsi_get_queueno(u8 *addr, u16 offset)
+{
+ return (le16_to_cpu(*(__le16 *)&addr[offset]) & 0x7000) >> 12;
+}
+
+static inline u32 rsi_get_length(u8 *addr, u16 offset)
+{
+ return (le16_to_cpu(*(__le16 *)&addr[offset])) & 0x0fff;
+}
+
+static inline u8 rsi_get_extended_desc(u8 *addr, u16 offset)
+{
+ return le16_to_cpu(*((__le16 *)&addr[offset + 4])) & 0x00ff;
+}
+
+static inline u8 rsi_get_rssi(u8 *addr)
+{
+ return *(u8 *)(addr + FRAME_DESC_SZ);
+}
+
+static inline u8 rsi_get_channel(u8 *addr)
+{
+ return *(char *)(addr + 15);
+}
+
+int rsi_mgmt_pkt_recv(struct rsi_common *common, u8 *msg);
+int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode);
+int rsi_send_aggregation_params_frame(struct rsi_common *common, u16 tid,
+ u16 ssn, u8 buf_size, u8 event);
+int rsi_hal_load_key(struct rsi_common *common, u8 *data, u16 key_len,
+ u8 key_type, u8 key_id, u32 cipher);
+int rsi_set_channel(struct rsi_common *common, u16 chno);
+void rsi_inform_bss_status(struct rsi_common *common, u8 status,
+ const u8 *bssid, u8 qos_enable, u16 aid);
+void rsi_indicate_pkt_to_os(struct rsi_common *common, struct sk_buff *skb);
+int rsi_mac80211_attach(struct rsi_common *common);
+void rsi_indicate_tx_status(struct rsi_hw *common, struct sk_buff *skb,
+ int status);
+bool rsi_is_cipher_wep(struct rsi_common *common);
+void rsi_core_qos_processor(struct rsi_common *common);
+void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb);
+int rsi_send_mgmt_pkt(struct rsi_common *common, struct sk_buff *skb);
+int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb);
+#endif
diff --git a/drivers/net/wireless/rsi/rsi_sdio.h b/drivers/net/wireless/rsi/rsi_sdio.h
new file mode 100644
index 000000000000..df4b5e20e05f
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_sdio.h
@@ -0,0 +1,129 @@
+/**
+ * @section LICENSE
+ * Copyright (c) 2014 Redpine Signals Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef __RSI_SDIO_INTF__
+#define __RSI_SDIO_INTF__
+
+#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.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/sdio_ids.h>
+#include "rsi_main.h"
+
+enum sdio_interrupt_type {
+ BUFFER_FULL = 0x0,
+ BUFFER_AVAILABLE = 0x1,
+ FIRMWARE_ASSERT_IND = 0x3,
+ MSDU_PACKET_PENDING = 0x4,
+ UNKNOWN_INT = 0XE
+};
+
+/* Buffer status register related info */
+#define PKT_BUFF_SEMI_FULL 0
+#define PKT_BUFF_FULL 1
+#define PKT_MGMT_BUFF_FULL 2
+#define MSDU_PKT_PENDING 3
+/* Interrupt Bit Related Macros */
+#define PKT_BUFF_AVAILABLE 0
+#define FW_ASSERT_IND 2
+
+#define RSI_DEVICE_BUFFER_STATUS_REGISTER 0xf3
+#define RSI_FN1_INT_REGISTER 0xf9
+#define RSI_SD_REQUEST_MASTER 0x10000
+
+/* FOR SD CARD ONLY */
+#define SDIO_RX_NUM_BLOCKS_REG 0x000F1
+#define SDIO_FW_STATUS_REG 0x000F2
+#define SDIO_NXT_RD_DELAY2 0x000F5
+#define SDIO_MASTER_ACCESS_MSBYTE 0x000FA
+#define SDIO_MASTER_ACCESS_LSBYTE 0x000FB
+#define SDIO_READ_START_LVL 0x000FC
+#define SDIO_READ_FIFO_CTL 0x000FD
+#define SDIO_WRITE_FIFO_CTL 0x000FE
+#define SDIO_FUN1_INTR_CLR_REG 0x0008
+#define SDIO_REG_HIGH_SPEED 0x0013
+
+#define RSI_GET_SDIO_INTERRUPT_TYPE(_I, TYPE) \
+ { \
+ TYPE = \
+ (_I & (1 << PKT_BUFF_AVAILABLE)) ? \
+ BUFFER_AVAILABLE : \
+ (_I & (1 << MSDU_PKT_PENDING)) ? \
+ MSDU_PACKET_PENDING : \
+ (_I & (1 << FW_ASSERT_IND)) ? \
+ FIRMWARE_ASSERT_IND : UNKNOWN_INT; \
+ }
+
+/* common registers in SDIO function1 */
+#define TA_SOFT_RESET_REG 0x0004
+#define TA_TH0_PC_REG 0x0400
+#define TA_HOLD_THREAD_REG 0x0844
+#define TA_RELEASE_THREAD_REG 0x0848
+
+#define TA_SOFT_RST_CLR 0
+#define TA_SOFT_RST_SET BIT(0)
+#define TA_PC_ZERO 0
+#define TA_HOLD_THREAD_VALUE cpu_to_le32(0xF)
+#define TA_RELEASE_THREAD_VALUE cpu_to_le32(0xF)
+#define TA_BASE_ADDR 0x2200
+#define MISC_CFG_BASE_ADDR 0x4150
+
+struct receive_info {
+ bool buffer_full;
+ bool semi_buffer_full;
+ bool mgmt_buffer_full;
+ u32 mgmt_buf_full_counter;
+ u32 buf_semi_full_counter;
+ u8 watch_bufferfull_count;
+ u32 sdio_intr_status_zero;
+ u32 sdio_int_counter;
+ u32 total_sdio_msdu_pending_intr;
+ u32 total_sdio_unknown_intr;
+ u32 buf_full_counter;
+ u32 buf_avilable_counter;
+};
+
+struct rsi_91x_sdiodev {
+ struct sdio_func *pfunction;
+ struct task_struct *in_sdio_litefi_irq;
+ struct receive_info rx_info;
+ u32 next_read_delay;
+ u32 sdio_high_speed_enable;
+ u8 sdio_clock_speed;
+ u32 cardcapability;
+ u8 prev_desc[16];
+ u32 tx_blk_size;
+ u8 write_fail;
+};
+
+void rsi_interrupt_handler(struct rsi_hw *adapter);
+int rsi_init_sdio_slave_regs(struct rsi_hw *adapter);
+int rsi_sdio_device_init(struct rsi_common *common);
+int rsi_sdio_read_register(struct rsi_hw *adapter, u32 addr, u8 *data);
+int rsi_sdio_host_intf_read_pkt(struct rsi_hw *adapter, u8 *pkt, u32 length);
+int rsi_sdio_write_register(struct rsi_hw *adapter, u8 function,
+ u32 addr, u8 *data);
+int rsi_sdio_write_register_multiple(struct rsi_hw *adapter, u32 addr,
+ u8 *data, u32 count);
+void rsi_sdio_ack_intr(struct rsi_hw *adapter, u8 int_bit);
+int rsi_sdio_determine_event_timeout(struct rsi_hw *adapter);
+int rsi_sdio_read_buffer_status_register(struct rsi_hw *adapter, u8 q_num);
+#endif
diff --git a/drivers/net/wireless/rsi/rsi_usb.h b/drivers/net/wireless/rsi/rsi_usb.h
new file mode 100644
index 000000000000..ebea0c411ead
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_usb.h
@@ -0,0 +1,68 @@
+/**
+ * @section LICENSE
+ * Copyright (c) 2014 Redpine Signals Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __RSI_USB_INTF__
+#define __RSI_USB_INTF__
+
+#include <linux/usb.h>
+#include "rsi_main.h"
+#include "rsi_common.h"
+
+#define USB_INTERNAL_REG_1 0x25000
+#define RSI_USB_READY_MAGIC_NUM 0xab
+#define FW_STATUS_REG 0x41050012
+
+#define USB_VENDOR_REGISTER_READ 0x15
+#define USB_VENDOR_REGISTER_WRITE 0x16
+#define RSI_USB_TX_HEAD_ROOM 128
+
+#define MAX_RX_URBS 1
+#define MAX_BULK_EP 8
+#define MGMT_EP 1
+#define DATA_EP 2
+
+struct rsi_91x_usbdev {
+ struct rsi_thread rx_thread;
+ u8 endpoint;
+ struct usb_device *usbdev;
+ struct usb_interface *pfunction;
+ struct urb *rx_usb_urb[MAX_RX_URBS];
+ u8 *tx_buffer;
+ __le16 bulkin_size;
+ u8 bulkin_endpoint_addr;
+ __le16 bulkout_size[MAX_BULK_EP];
+ u8 bulkout_endpoint_addr[MAX_BULK_EP];
+ u32 tx_blk_size;
+ u8 write_fail;
+};
+
+static inline int rsi_usb_check_queue_status(struct rsi_hw *adapter, u8 q_num)
+{
+ /* In USB, there isn't any need to check the queue status */
+ return QUEUE_NOT_FULL;
+}
+
+static inline int rsi_usb_event_timeout(struct rsi_hw *adapter)
+{
+ return EVENT_WAIT_FOREVER;
+}
+
+int rsi_usb_device_init(struct rsi_common *common);
+int rsi_usb_write_register_multiple(struct rsi_hw *adapter, u32 addr,
+ u8 *data, u32 count);
+void rsi_usb_rx_thread(struct rsi_common *common);
+#endif