rsi: Add RS9113 wireless driver
authorFariya Fatima <fariyaf@gmail.com>
Sat, 15 Mar 2014 22:17:02 +0000 (03:47 +0530)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 17 Mar 2014 17:51:52 +0000 (13:51 -0400)
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>
21 files changed:
drivers/net/wireless/Kconfig
drivers/net/wireless/Makefile
drivers/net/wireless/rsi/Kconfig [new file with mode: 0644]
drivers/net/wireless/rsi/Makefile [new file with mode: 0644]
drivers/net/wireless/rsi/rsi_91x_core.c [new file with mode: 0644]
drivers/net/wireless/rsi/rsi_91x_debugfs.c [new file with mode: 0644]
drivers/net/wireless/rsi/rsi_91x_mac80211.c [new file with mode: 0644]
drivers/net/wireless/rsi/rsi_91x_main.c [new file with mode: 0644]
drivers/net/wireless/rsi/rsi_91x_mgmt.c [new file with mode: 0644]
drivers/net/wireless/rsi/rsi_91x_pkt.c [new file with mode: 0644]
drivers/net/wireless/rsi/rsi_91x_sdio.c [new file with mode: 0644]
drivers/net/wireless/rsi/rsi_91x_sdio_ops.c [new file with mode: 0644]
drivers/net/wireless/rsi/rsi_91x_usb.c [new file with mode: 0644]
drivers/net/wireless/rsi/rsi_91x_usb_ops.c [new file with mode: 0644]
drivers/net/wireless/rsi/rsi_boot_params.h [new file with mode: 0644]
drivers/net/wireless/rsi/rsi_common.h [new file with mode: 0644]
drivers/net/wireless/rsi/rsi_debugfs.h [new file with mode: 0644]
drivers/net/wireless/rsi/rsi_main.h [new file with mode: 0644]
drivers/net/wireless/rsi/rsi_mgmt.h [new file with mode: 0644]
drivers/net/wireless/rsi/rsi_sdio.h [new file with mode: 0644]
drivers/net/wireless/rsi/rsi_usb.h [new file with mode: 0644]

index 9c2c285f35d5e4f2374e3dfea1396fa942f6eae2..b2137e8f7ca63dbd4b2d7b88e3c19ea5153d327f 100644 (file)
@@ -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
index 0fab227025be33bbd399fd29b3ea2693a6d21000..0c88916867187817ea08f43c4d69cbaf86d28246 100644 (file)
@@ -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 (file)
index 0000000..35245f9
--- /dev/null
@@ -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 (file)
index 0000000..25828b6
--- /dev/null
@@ -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 (file)
index 0000000..e89535e
--- /dev/null
@@ -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 (file)
index 0000000..7e4ef45
--- /dev/null
@@ -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 (file)
index 0000000..8416474
--- /dev/null
@@ -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 (file)
index 0000000..410a4a4
--- /dev/null
@@ -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 (file)
index 0000000..f09c72e
--- /dev/null
@@ -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 (file)
index 0000000..8e48e72
--- /dev/null
@@ -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 (file)
index 0000000..852453f
--- /dev/null
@@ -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 (file)
index 0000000..f1cb99c
--- /dev/null
@@ -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 (file)
index 0000000..bb1bf96
--- /dev/null
@@ -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 (file)
index 0000000..1106ce7
--- /dev/null
@@ -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 (file)
index 0000000..5e2721f
--- /dev/null
@@ -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 (file)
index 0000000..f2f7078
--- /dev/null
@@ -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 (file)
index 0000000..580ad3b
--- /dev/null
@@ -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 (file)
index 0000000..bebdc2a
--- /dev/null
@@ -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 (file)
index 0000000..ac67c4a
--- /dev/null
@@ -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 (file)
index 0000000..df4b5e2
--- /dev/null
@@ -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 (file)
index 0000000..ebea0c4
--- /dev/null
@@ -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