rsi: Add RS9113 wireless driver

This patch adds the Redpine Signals' 91x wireless driver.

Signed-off-by: Fariya Fatima <fariyaf@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Fariya Fatima 2014-03-16 03:47:02 +05:30 committed by John W. Linville
parent 097638a08a
commit dad0d04fa7
21 changed files with 6644 additions and 0 deletions

View 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

View File

@ -59,3 +59,4 @@ obj-$(CONFIG_BRCMFMAC) += brcm80211/
obj-$(CONFIG_BRCMSMAC) += brcm80211/
obj-$(CONFIG_CW1200) += cw1200/
obj-$(CONFIG_RSI_91X) += rsi/

View File

@ -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.

View File

@ -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

View File

@ -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);
}

View File

@ -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);

File diff suppressed because it is too large Load Diff

View File

@ -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");

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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");

View File

@ -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;
}

View File

@ -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");

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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