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:
parent
097638a08a
commit
dad0d04fa7
|
@ -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
|
||||
|
|
|
@ -59,3 +59,4 @@ obj-$(CONFIG_BRCMFMAC) += brcm80211/
|
|||
obj-$(CONFIG_BRCMSMAC) += brcm80211/
|
||||
|
||||
obj-$(CONFIG_CW1200) += cw1200/
|
||||
obj-$(CONFIG_RSI_91X) += rsi/
|
||||
|
|
|
@ -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.
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
@ -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
|
@ -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;
|
||||
}
|
|
@ -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");
|
|
@ -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;
|
||||
}
|
|
@ -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");
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue