Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,57 @@
# SPDX-License-Identifier: GPL-2.0-only
config WLAN_VENDOR_RSI
bool "Redpine Signals Inc devices"
default y
help
If you have a wireless card belonging to this class, say Y.
Note that the answer to this question doesn't directly affect the
kernel: saying N will just cause the configurator to skip all the
questions about these cards. If you say Y, you will be asked for
your specific card in the following questions.
if WLAN_VENDOR_RSI
config RSI_91X
tristate "Redpine Signals Inc 91x WLAN driver support"
select BT_HCIRSI if RSI_COEX
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.
config RSI_COEX
bool "Redpine Signals WLAN BT Coexistence support"
depends on BT && RSI_91X
depends on !(BT=m && RSI_91X=y)
default y
help
This option enables the WLAN BT coex support in rsi drivers.
Select M (recommended), if you have want to use this feature
and you have RS9113 module.
endif # WLAN_VENDOR_RSI
@@ -0,0 +1,15 @@
# SPDX-License-Identifier: GPL-2.0
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_hal.o
rsi_91x-y += rsi_91x_ps.o
rsi_91x-$(CONFIG_RSI_COEX) += rsi_91x_coex.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,178 @@
/*
* Copyright (c) 2018 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_main.h"
#include "rsi_coex.h"
#include "rsi_mgmt.h"
#include "rsi_hal.h"
static enum rsi_coex_queues rsi_coex_determine_coex_q
(struct rsi_coex_ctrl_block *coex_cb)
{
enum rsi_coex_queues q_num = RSI_COEX_Q_INVALID;
if (skb_queue_len(&coex_cb->coex_tx_qs[RSI_COEX_Q_COMMON]) > 0)
q_num = RSI_COEX_Q_COMMON;
if (skb_queue_len(&coex_cb->coex_tx_qs[RSI_COEX_Q_BT]) > 0)
q_num = RSI_COEX_Q_BT;
if (skb_queue_len(&coex_cb->coex_tx_qs[RSI_COEX_Q_WLAN]) > 0)
q_num = RSI_COEX_Q_WLAN;
return q_num;
}
static void rsi_coex_sched_tx_pkts(struct rsi_coex_ctrl_block *coex_cb)
{
enum rsi_coex_queues coex_q = RSI_COEX_Q_INVALID;
struct sk_buff *skb;
do {
coex_q = rsi_coex_determine_coex_q(coex_cb);
rsi_dbg(INFO_ZONE, "queue = %d\n", coex_q);
if (coex_q == RSI_COEX_Q_BT) {
skb = skb_dequeue(&coex_cb->coex_tx_qs[RSI_COEX_Q_BT]);
rsi_send_bt_pkt(coex_cb->priv, skb);
}
} while (coex_q != RSI_COEX_Q_INVALID);
}
static void rsi_coex_scheduler_thread(struct rsi_common *common)
{
struct rsi_coex_ctrl_block *coex_cb = common->coex_cb;
u32 timeout = EVENT_WAIT_FOREVER;
do {
rsi_wait_event(&coex_cb->coex_tx_thread.event, timeout);
rsi_reset_event(&coex_cb->coex_tx_thread.event);
rsi_coex_sched_tx_pkts(coex_cb);
} while (atomic_read(&coex_cb->coex_tx_thread.thread_done) == 0);
kthread_complete_and_exit(&coex_cb->coex_tx_thread.completion, 0);
}
int rsi_coex_recv_pkt(struct rsi_common *common, u8 *msg)
{
u8 msg_type = msg[RSI_RX_DESC_MSG_TYPE_OFFSET];
switch (msg_type) {
case COMMON_CARD_READY_IND:
rsi_dbg(INFO_ZONE, "common card ready received\n");
common->hibernate_resume = false;
rsi_handle_card_ready(common, msg);
break;
case SLEEP_NOTIFY_IND:
rsi_dbg(INFO_ZONE, "sleep notify received\n");
rsi_mgmt_pkt_recv(common, msg);
break;
}
return 0;
}
static inline int rsi_map_coex_q(u8 hal_queue)
{
switch (hal_queue) {
case RSI_COEX_Q:
return RSI_COEX_Q_COMMON;
case RSI_WLAN_Q:
return RSI_COEX_Q_WLAN;
case RSI_BT_Q:
return RSI_COEX_Q_BT;
}
return RSI_COEX_Q_INVALID;
}
int rsi_coex_send_pkt(void *priv, struct sk_buff *skb, u8 hal_queue)
{
struct rsi_common *common = priv;
struct rsi_coex_ctrl_block *coex_cb = common->coex_cb;
struct skb_info *tx_params = NULL;
enum rsi_coex_queues coex_q;
int status;
coex_q = rsi_map_coex_q(hal_queue);
if (coex_q == RSI_COEX_Q_INVALID) {
rsi_dbg(ERR_ZONE, "Invalid coex queue\n");
return -EINVAL;
}
if (coex_q != RSI_COEX_Q_COMMON &&
coex_q != RSI_COEX_Q_WLAN) {
skb_queue_tail(&coex_cb->coex_tx_qs[coex_q], skb);
rsi_set_event(&coex_cb->coex_tx_thread.event);
return 0;
}
if (common->iface_down) {
tx_params =
(struct skb_info *)&IEEE80211_SKB_CB(skb)->driver_data;
if (!(tx_params->flags & INTERNAL_MGMT_PKT)) {
rsi_indicate_tx_status(common->priv, skb, -EINVAL);
return 0;
}
}
/* Send packet to hal */
if (skb->priority == MGMT_SOFT_Q)
status = rsi_send_mgmt_pkt(common, skb);
else
status = rsi_send_data_pkt(common, skb);
return status;
}
int rsi_coex_attach(struct rsi_common *common)
{
struct rsi_coex_ctrl_block *coex_cb;
int cnt;
coex_cb = kzalloc_obj(*coex_cb);
if (!coex_cb)
return -ENOMEM;
common->coex_cb = (void *)coex_cb;
coex_cb->priv = common;
/* Initialize co-ex queues */
for (cnt = 0; cnt < NUM_COEX_TX_QUEUES; cnt++)
skb_queue_head_init(&coex_cb->coex_tx_qs[cnt]);
rsi_init_event(&coex_cb->coex_tx_thread.event);
/* Initialize co-ex thread */
if (rsi_create_kthread(common,
&coex_cb->coex_tx_thread,
rsi_coex_scheduler_thread,
"Coex-Tx-Thread")) {
rsi_dbg(ERR_ZONE, "%s: Unable to init tx thrd\n", __func__);
kfree(coex_cb);
return -EINVAL;
}
return 0;
}
void rsi_coex_detach(struct rsi_common *common)
{
struct rsi_coex_ctrl_block *coex_cb = common->coex_cb;
int cnt;
rsi_kill_thread(&coex_cb->coex_tx_thread);
for (cnt = 0; cnt < NUM_COEX_TX_QUEUES; cnt++)
skb_queue_purge(&coex_cb->coex_tx_qs[cnt]);
kfree(coex_cb);
}
@@ -0,0 +1,501 @@
/*
* 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"
#include "rsi_hal.h"
#include "rsi_coex.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_get_num_pkts_dequeue() - This function determines the number of
* packets to be dequeued based on the number
* of bytes calculated using txop.
*
* @common: Pointer to the driver private structure.
* @q_num: the queue from which pkts have to be dequeued
*
* Return: pkt_num: Number of pkts to be dequeued.
*/
static u32 rsi_get_num_pkts_dequeue(struct rsi_common *common, u8 q_num)
{
struct rsi_hw *adapter = common->priv;
struct sk_buff *skb;
u32 pkt_cnt = 0;
s16 txop = common->tx_qinfo[q_num].txop * 32;
__le16 r_txop;
struct ieee80211_rate rate;
struct ieee80211_hdr *wh;
struct ieee80211_vif *vif;
rate.bitrate = RSI_RATE_MCS0 * 5 * 10; /* Convert to Kbps */
if (q_num == VI_Q)
txop = ((txop << 5) / 80);
if (skb_queue_len(&common->tx_queue[q_num]))
skb = skb_peek(&common->tx_queue[q_num]);
else
return 0;
do {
wh = (struct ieee80211_hdr *)skb->data;
vif = rsi_get_vif(adapter, wh->addr2);
r_txop = ieee80211_generic_frame_duration(adapter->hw,
vif,
common->band,
skb->len, &rate);
txop -= le16_to_cpu(r_txop);
pkt_cnt += 1;
/*checking if pkts are still there*/
if (skb_queue_len(&common->tx_queue[q_num]) - pkt_cnt)
skb = skb->next;
else
break;
} while (txop > 0);
return pkt_cnt;
}
/**
* 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 = 0;
if (skb_queue_len(&common->tx_queue[MGMT_BEACON_Q])) {
q_num = MGMT_BEACON_Q;
return q_num;
}
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->hw_data_qs_blocked)
return q_num;
if (common->pkt_cnt != 0) {
--common->pkt_cnt;
return common->selected_qnum;
}
get_queue_num:
recontend_queue = false;
q_num = rsi_determine_min_weight_queue(common);
ii = q_num;
/* Selecting the queue with least back off */
for (; ii < NUM_EDCA_QUEUES; ii++) {
q_len = skb_queue_len(&common->tx_queue[ii]);
if (((common->tx_qinfo[ii].pkt_contended) &&
(common->tx_qinfo[ii].weight < common->min_weight)) &&
q_len) {
common->min_weight = common->tx_qinfo[ii].weight;
q_num = ii;
}
}
if (q_num < NUM_EDCA_QUEUES)
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;
return INVALID_QUEUE;
}
common->selected_qnum = q_num;
q_len = skb_queue_len(&common->tx_queue[q_num]);
if (q_num == VO_Q || q_num == VI_Q) {
common->pkt_cnt = rsi_get_num_pkts_dequeue(common, q_num);
common->pkt_cnt -= 1;
}
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;
}
if (common->hibernate_resume)
break;
mutex_lock(&common->tx_lock);
status = adapter->check_hw_queue_status(adapter, q_num);
if ((status <= 0)) {
mutex_unlock(&common->tx_lock);
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) {
rsi_dbg(ERR_ZONE, "skb null\n");
mutex_unlock(&common->tx_lock);
break;
}
if (q_num == MGMT_BEACON_Q) {
status = rsi_send_pkt_to_bus(common, skb);
dev_kfree_skb(skb);
} else {
#ifdef CONFIG_RSI_COEX
if (common->coex_mode > 1) {
status = rsi_coex_send_pkt(common, skb,
RSI_WLAN_Q);
} else {
#endif
if (q_num == MGMT_SOFT_Q)
status = rsi_send_mgmt_pkt(common, skb);
else
status = rsi_send_data_pkt(common, skb);
#ifdef CONFIG_RSI_COEX
}
#endif
}
if (status) {
mutex_unlock(&common->tx_lock);
break;
}
common->tx_stats.total_tx_pkt_send[q_num]++;
tstamp_2 = jiffies;
mutex_unlock(&common->tx_lock);
if (time_after(tstamp_2, tstamp_1 + (300 * HZ) / 1000))
schedule();
}
}
struct rsi_sta *rsi_find_sta(struct rsi_common *common, u8 *mac_addr)
{
int i;
for (i = 0; i < common->max_stations; i++) {
if (!common->stations[i].sta)
continue;
if (!(memcmp(common->stations[i].sta->addr,
mac_addr, ETH_ALEN)))
return &common->stations[i];
}
return NULL;
}
struct ieee80211_vif *rsi_get_vif(struct rsi_hw *adapter, u8 *mac)
{
struct ieee80211_vif *vif;
int i;
for (i = 0; i < RSI_MAX_VIFS; i++) {
vif = adapter->vifs[i];
if (!vif)
continue;
if (!memcmp(vif->addr, mac, ETH_ALEN))
return vif;
}
return NULL;
}
/**
* 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 *wh = NULL;
struct ieee80211_vif *vif;
u8 q_num, tid = 0;
struct rsi_sta *rsta = NULL;
if ((!skb) || (!skb->len)) {
rsi_dbg(ERR_ZONE, "%s: Null skb/zero Length packet\n",
__func__);
goto xmit_fail;
}
if (common->fsm_state != FSM_MAC_INIT_DONE) {
rsi_dbg(ERR_ZONE, "%s: FSM state not open\n", __func__);
goto xmit_fail;
}
if (common->wow_flags & RSI_WOW_ENABLED) {
rsi_dbg(ERR_ZONE,
"%s: Blocking Tx_packets when WOWLAN is enabled\n",
__func__);
goto xmit_fail;
}
info = IEEE80211_SKB_CB(skb);
tx_params = (struct skb_info *)info->driver_data;
/* info->driver_data and info->control part of union so make copy */
tx_params->have_key = !!info->control.hw_key;
wh = (struct ieee80211_hdr *)&skb->data[0];
tx_params->sta_id = 0;
vif = rsi_get_vif(adapter, wh->addr2);
if (!vif)
goto xmit_fail;
tx_params->vif = vif;
tx_params->vap_id = ((struct vif_priv *)vif->drv_priv)->vap_id;
if ((ieee80211_is_mgmt(wh->frame_control)) ||
(ieee80211_is_ctl(wh->frame_control)) ||
(ieee80211_is_qos_nullfunc(wh->frame_control))) {
if (ieee80211_is_assoc_req(wh->frame_control) ||
ieee80211_is_reassoc_req(wh->frame_control)) {
struct ieee80211_bss_conf *bss = &vif->bss_conf;
common->eapol4_confirm = false;
rsi_hal_send_sta_notify_frame(common,
RSI_IFTYPE_STATION,
STA_CONNECTED, bss->bssid,
bss->qos, vif->cfg.aid,
0,
vif);
}
q_num = MGMT_SOFT_Q;
skb->priority = q_num;
if (rsi_prepare_mgmt_desc(common, skb)) {
rsi_dbg(ERR_ZONE, "Failed to prepare desc\n");
goto xmit_fail;
}
} else {
if (ieee80211_is_data_qos(wh->frame_control)) {
u8 *qos = ieee80211_get_qos_ctl(wh);
tid = *qos & IEEE80211_QOS_CTL_TID_MASK;
skb->priority = TID_TO_WME_AC(tid);
} else {
tid = IEEE80211_NONQOS_TID;
skb->priority = BE_Q;
}
q_num = skb->priority;
tx_params->tid = tid;
if (((vif->type == NL80211_IFTYPE_AP) ||
(vif->type == NL80211_IFTYPE_P2P_GO)) &&
(!is_broadcast_ether_addr(wh->addr1)) &&
(!is_multicast_ether_addr(wh->addr1))) {
rsta = rsi_find_sta(common, wh->addr1);
if (!rsta)
goto xmit_fail;
tx_params->sta_id = rsta->sta_id;
} else {
tx_params->sta_id = 0;
}
if (rsta) {
/* Start aggregation if not done for this tid */
if (!rsta->start_tx_aggr[tid]) {
rsta->start_tx_aggr[tid] = true;
ieee80211_start_tx_ba_session(rsta->sta,
tid, 0);
}
}
if (IEEE80211_SKB_CB(skb)->control.flags &
IEEE80211_TX_CTRL_PORT_CTRL_PROTO) {
q_num = MGMT_SOFT_Q;
skb->priority = q_num;
}
if (rsi_prepare_data_desc(common, skb)) {
rsi_dbg(ERR_ZONE, "Failed to prepare data desc\n");
goto xmit_fail;
}
}
if ((q_num < MGMT_SOFT_Q) &&
((skb_queue_len(&common->tx_queue[q_num]) + 1) >=
DATA_QUEUE_WATER_MARK)) {
rsi_dbg(ERR_ZONE, "%s: sw queue full\n", __func__);
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 thread <===\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,328 @@
/*
* 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 = 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 function 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;
seq_printf(seq, "LMAC : %d.%d.%d.%d\n",
common->lmac_ver.major,
common->lmac_ver.minor,
common->lmac_ver.release_num,
common->lmac_ver.patch_num);
return 0;
}
/**
* rsi_version_open() - This function 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;
static const unsigned char fsm_state[][32] = {
"FSM_FW_NOT_LOADED",
"FSM_CARD_NOT_READY",
"FSM_COMMON_DEV_PARAMS_SENT",
"FSM_BOOT_PARAMS_SENT",
"FSM_EEPROM_READ_MAC_ADDR",
"FSM_EEPROM_READ_RF_TYPE",
"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: ");
BUILD_BUG_ON(ARRAY_SIZE(fsm_state) != NUM_FSM_STATES);
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[MGMT_SOFT_Q]));
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[VO_Q]));
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[VI_Q]));
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[BE_Q]));
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[BK_Q]));
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 function 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 function 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_obj(*dev_dbgfs);
if (!dev_dbgfs)
return -ENOMEM;
adapter->dfsentry = dev_dbgfs;
snprintf(devdir, sizeof(devdir), "%s",
wiphy_name(adapter->hw->wiphy));
dev_dbgfs->subdir = debugfs_create_dir(devdir, NULL);
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
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,431 @@
/*
* 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.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/firmware.h>
#include <net/rsi_91x.h>
#include "rsi_mgmt.h"
#include "rsi_common.h"
#include "rsi_coex.h"
#include "rsi_hal.h"
#include "rsi_usb.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);
#ifdef CONFIG_RSI_COEX
static struct rsi_proto_ops g_proto_ops = {
.coex_send_pkt = rsi_coex_send_pkt,
.get_host_intf = rsi_get_host_intf,
.set_bt_context = rsi_set_bt_context,
};
#endif
/**
* rsi_dbg() - This function outputs informational messages.
* @zone: Zone of interest for output message.
* @fmt: printf-style format for output message.
*
* Return: none
*/
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);
}
EXPORT_SYMBOL_GPL(rsi_dbg);
static char *opmode_str(int oper_mode)
{
switch (oper_mode) {
case DEV_OPMODE_WIFI_ALONE:
return "Wi-Fi alone";
case DEV_OPMODE_BT_ALONE:
return "BT EDR alone";
case DEV_OPMODE_BT_LE_ALONE:
return "BT LE alone";
case DEV_OPMODE_BT_DUAL:
return "BT Dual";
case DEV_OPMODE_STA_BT:
return "Wi-Fi STA + BT EDR";
case DEV_OPMODE_STA_BT_LE:
return "Wi-Fi STA + BT LE";
case DEV_OPMODE_STA_BT_DUAL:
return "Wi-Fi STA + BT DUAL";
case DEV_OPMODE_AP_BT:
return "Wi-Fi AP + BT EDR";
case DEV_OPMODE_AP_BT_DUAL:
return "Wi-Fi AP + BT DUAL";
}
return "Unknown";
}
void rsi_print_version(struct rsi_common *common)
{
rsi_dbg(ERR_ZONE, "================================================\n");
rsi_dbg(ERR_ZONE, "================ RSI Version Info ==============\n");
rsi_dbg(ERR_ZONE, "================================================\n");
rsi_dbg(ERR_ZONE, "FW Version\t: %d.%d.%d\n",
common->lmac_ver.major, common->lmac_ver.minor,
common->lmac_ver.release_num);
rsi_dbg(ERR_ZONE, "Operating mode\t: %d [%s]",
common->oper_mode, opmode_str(common->oper_mode));
rsi_dbg(ERR_ZONE, "Firmware file\t: %s", common->priv->fw_file_name);
rsi_dbg(ERR_ZONE, "================================================\n");
}
/**
* 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 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);
return skb;
}
/**
* rsi_read_pkt() - This function reads frames from the card.
* @common: Pointer to the driver private structure.
* @rx_pkt: Received pkt.
* @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, u8 *rx_pkt, 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;
#ifdef CONFIG_RSI_COEX
u8 bt_pkt_type;
#endif
index = 0;
do {
frame_desc = &rx_pkt[index];
actual_length = *(u16 *)&frame_desc[0];
offset = *(u16 *)&frame_desc[2];
if (!rcv_pkt_len && offset >
RSI_MAX_RX_USB_PKT_SIZE - FRAME_DESC_SZ)
goto fail;
queueno = rsi_get_queueno(frame_desc, offset);
length = rsi_get_length(frame_desc, offset);
/* Extended descriptor is valid for WLAN queues only */
if (queueno == RSI_WIFI_DATA_Q || queueno == RSI_WIFI_MGMT_Q)
extended_desc = rsi_get_extended_desc(frame_desc,
offset);
switch (queueno) {
case RSI_COEX_Q:
#ifdef CONFIG_RSI_COEX
if (common->coex_mode > 1)
rsi_coex_recv_pkt(common, frame_desc + offset);
else
#endif
rsi_mgmt_pkt_recv(common,
(frame_desc + offset));
break;
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;
#ifdef CONFIG_RSI_COEX
case RSI_BT_MGMT_Q:
case RSI_BT_DATA_Q:
#define BT_RX_PKT_TYPE_OFST 14
#define BT_CARD_READY_IND 0x89
bt_pkt_type = frame_desc[offset + BT_RX_PKT_TYPE_OFST];
if (bt_pkt_type == BT_CARD_READY_IND) {
rsi_dbg(INFO_ZONE, "BT Card ready recvd\n");
if (common->fsm_state == FSM_MAC_INIT_DONE)
rsi_attach_bt(common);
else
common->bt_defer_attach = true;
} else {
if (common->bt_adapter)
rsi_bt_ops.recv_pkt(common->bt_adapter,
frame_desc + offset);
}
break;
#endif
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);
kthread_complete_and_exit(&common->tx_thread.completion, 0);
}
#ifdef CONFIG_RSI_COEX
enum rsi_host_intf rsi_get_host_intf(void *priv)
{
struct rsi_common *common = priv;
return common->priv->rsi_host_intf;
}
void rsi_set_bt_context(void *priv, void *bt_context)
{
struct rsi_common *common = priv;
common->bt_adapter = bt_context;
}
#endif
void rsi_attach_bt(struct rsi_common *common)
{
#ifdef CONFIG_RSI_COEX
if (rsi_bt_ops.attach(common, &g_proto_ops))
rsi_dbg(ERR_ZONE,
"Failed to attach BT module\n");
#endif
}
/**
* rsi_91x_init() - This function initializes os interface operations.
* @oper_mode: One of DEV_OPMODE_*.
*
* Return: Pointer to the adapter structure on success, NULL on failure .
*/
struct rsi_hw *rsi_91x_init(u16 oper_mode)
{
struct rsi_hw *adapter = NULL;
struct rsi_common *common = NULL;
u8 ii = 0;
adapter = kzalloc_obj(*adapter);
if (!adapter)
return NULL;
adapter->priv = kzalloc_obj(*common);
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_lock);
mutex_init(&common->rx_lock);
mutex_init(&common->tx_bus_mutex);
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;
}
rsi_default_ps_params(adapter);
init_bgscan_params(common);
spin_lock_init(&adapter->ps_lock);
timer_setup(&common->roc_timer, rsi_roc_timeout, 0);
init_completion(&common->wlan_init_completion);
adapter->device_model = RSI_DEV_9113;
common->oper_mode = oper_mode;
/* Determine coex mode */
switch (common->oper_mode) {
case DEV_OPMODE_STA_BT_DUAL:
case DEV_OPMODE_STA_BT:
case DEV_OPMODE_STA_BT_LE:
case DEV_OPMODE_BT_ALONE:
case DEV_OPMODE_BT_LE_ALONE:
case DEV_OPMODE_BT_DUAL:
common->coex_mode = 2;
break;
case DEV_OPMODE_AP_BT_DUAL:
case DEV_OPMODE_AP_BT:
common->coex_mode = 4;
break;
case DEV_OPMODE_WIFI_ALONE:
common->coex_mode = 1;
break;
default:
common->oper_mode = 1;
common->coex_mode = 1;
}
rsi_dbg(INFO_ZONE, "%s: oper_mode = %d, coex_mode = %d\n",
__func__, common->oper_mode, common->coex_mode);
adapter->device_model = RSI_DEV_9113;
#ifdef CONFIG_RSI_COEX
if (common->coex_mode > 1) {
if (rsi_coex_attach(common)) {
rsi_dbg(ERR_ZONE, "Failed to init coex module\n");
rsi_kill_thread(&common->tx_thread);
goto err;
}
}
#endif
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]);
#ifdef CONFIG_RSI_COEX
if (common->coex_mode > 1) {
if (common->bt_adapter) {
rsi_bt_ops.detach(common->bt_adapter);
common->bt_adapter = NULL;
}
rsi_coex_detach(common);
}
#endif
common->init_done = false;
kfree(common);
kfree(adapter->rsi_dev);
kfree(adapter);
}
EXPORT_SYMBOL_GPL(rsi_91x_deinit);
MODULE_AUTHOR("Redpine Signals Inc");
MODULE_DESCRIPTION("Station driver for RSI 91x devices");
MODULE_VERSION("0.1");
MODULE_LICENSE("Dual BSD/GPL");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,145 @@
/*
* Copyright (c) 2014 Redpine Signals Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/etherdevice.h>
#include <linux/if.h>
#include "rsi_debugfs.h"
#include "rsi_mgmt.h"
#include "rsi_common.h"
#include "rsi_ps.h"
char *str_psstate(enum ps_state state)
{
switch (state) {
case PS_NONE:
return "PS_NONE";
case PS_DISABLE_REQ_SENT:
return "PS_DISABLE_REQ_SENT";
case PS_ENABLE_REQ_SENT:
return "PS_ENABLE_REQ_SENT";
case PS_ENABLED:
return "PS_ENABLED";
default:
return "INVALID_STATE";
}
}
static inline void rsi_modify_ps_state(struct rsi_hw *adapter,
enum ps_state nstate)
{
rsi_dbg(INFO_ZONE, "PS state changed %s => %s\n",
str_psstate(adapter->ps_state),
str_psstate(nstate));
adapter->ps_state = nstate;
}
void rsi_default_ps_params(struct rsi_hw *adapter)
{
struct rsi_ps_info *ps_info = &adapter->ps_info;
ps_info->enabled = true;
ps_info->sleep_type = RSI_SLEEP_TYPE_LP;
ps_info->tx_threshold = 0;
ps_info->rx_threshold = 0;
ps_info->tx_hysterisis = 0;
ps_info->rx_hysterisis = 0;
ps_info->monitor_interval = 0;
ps_info->listen_interval = RSI_DEF_LISTEN_INTERVAL;
ps_info->num_bcns_per_lis_int = 0;
ps_info->dtim_interval_duration = 0;
ps_info->num_dtims_per_sleep = 0;
ps_info->deep_sleep_wakeup_period = RSI_DEF_DS_WAKEUP_PERIOD;
}
void rsi_enable_ps(struct rsi_hw *adapter, struct ieee80211_vif *vif)
{
if (adapter->ps_state != PS_NONE) {
rsi_dbg(ERR_ZONE,
"%s: Cannot accept enable PS in %s state\n",
__func__, str_psstate(adapter->ps_state));
return;
}
if (rsi_send_ps_request(adapter, true, vif)) {
rsi_dbg(ERR_ZONE,
"%s: Failed to send PS request to device\n",
__func__);
return;
}
rsi_modify_ps_state(adapter, PS_ENABLE_REQ_SENT);
}
/* This function is used to disable power save */
void rsi_disable_ps(struct rsi_hw *adapter, struct ieee80211_vif *vif)
{
if (adapter->ps_state != PS_ENABLED) {
rsi_dbg(ERR_ZONE,
"%s: Cannot accept disable PS in %s state\n",
__func__, str_psstate(adapter->ps_state));
return;
}
if (rsi_send_ps_request(adapter, false, vif)) {
rsi_dbg(ERR_ZONE,
"%s: Failed to send PS request to device\n",
__func__);
return;
}
rsi_modify_ps_state(adapter, PS_DISABLE_REQ_SENT);
}
void rsi_conf_uapsd(struct rsi_hw *adapter, struct ieee80211_vif *vif)
{
int ret;
if (adapter->ps_state != PS_ENABLED)
return;
ret = rsi_send_ps_request(adapter, false, vif);
if (!ret)
ret = rsi_send_ps_request(adapter, true, vif);
if (ret)
rsi_dbg(ERR_ZONE,
"%s: Failed to send PS request to device\n",
__func__);
}
int rsi_handle_ps_confirm(struct rsi_hw *adapter, u8 *msg)
{
u16 cfm_type = get_unaligned_le16(msg + PS_CONFIRM_INDEX);
switch (cfm_type) {
case RSI_SLEEP_REQUEST:
if (adapter->ps_state == PS_ENABLE_REQ_SENT)
rsi_modify_ps_state(adapter, PS_ENABLED);
break;
case RSI_WAKEUP_REQUEST:
if (adapter->ps_state == PS_DISABLE_REQ_SENT)
rsi_modify_ps_state(adapter, PS_NONE);
break;
default:
rsi_dbg(ERR_ZONE,
"Invalid PS confirm type %x in state %s\n",
cfm_type, str_psstate(adapter->ps_state));
return -1;
}
return 0;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,415 @@
/*
* 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 <net/rsi_91x.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.
*/
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;
}
static void rsi_rx_handler(struct rsi_hw *adapter);
void rsi_sdio_rx_thread(struct rsi_common *common)
{
struct rsi_hw *adapter = common->priv;
struct rsi_91x_sdiodev *sdev = adapter->rsi_dev;
do {
rsi_wait_event(&sdev->rx_thread.event, EVENT_WAIT_FOREVER);
rsi_reset_event(&sdev->rx_thread.event);
rsi_rx_handler(adapter);
} while (!atomic_read(&sdev->rx_thread.thread_done));
rsi_dbg(INFO_ZONE, "%s: Terminated SDIO RX thread\n", __func__);
atomic_inc(&sdev->rx_thread.thread_done);
kthread_complete_and_exit(&sdev->rx_thread.completion, 0);
}
/**
* 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;
struct rsi_91x_sdiodev *dev = adapter->rsi_dev;
u8 num_blks = 0;
u32 rcv_pkt_len = 0;
int status = 0;
u8 value = 0;
num_blks = ((adapter->interrupt_status & 1) |
((adapter->interrupt_status >> RECV_NUM_BLOCKS) << 1));
if (!num_blks) {
status = rsi_sdio_read_register(adapter,
SDIO_RX_NUM_BLOCKS_REG,
&value);
if (status) {
rsi_dbg(ERR_ZONE,
"%s: Failed to read pkt length from the card:\n",
__func__);
return status;
}
num_blks = value & 0x1f;
}
if (dev->write_fail == 2)
rsi_sdio_ack_intr(common->priv, (1 << MSDU_PKT_PENDING));
if (unlikely(!num_blks)) {
dev->write_fail = 2;
return -1;
}
rcv_pkt_len = (num_blks * 256);
status = rsi_sdio_host_intf_read_pkt(adapter, dev->pktbuffer,
rcv_pkt_len);
if (status) {
rsi_dbg(ERR_ZONE, "%s: Failed to read packet from card\n",
__func__);
return status;
}
status = rsi_read_pkt(common, dev->pktbuffer, rcv_pkt_len);
if (status) {
rsi_dbg(ERR_ZONE, "Failed to read the packet\n");
return status;
}
return 0;
}
/**
* 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 = 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: Initializing 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: Initializing 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_rx_handler() - Read and process SDIO interrupts.
* @adapter: Pointer to the adapter structure.
*
* Return: None.
*/
static void rsi_rx_handler(struct rsi_hw *adapter)
{
struct rsi_common *common = adapter->priv;
struct rsi_91x_sdiodev *dev = adapter->rsi_dev;
int status;
u8 isr_status = 0;
u8 fw_status = 0;
dev->rx_info.sdio_int_counter++;
do {
mutex_lock(&common->rx_lock);
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->rx_lock);
return;
}
adapter->interrupt_status = isr_status;
if (isr_status == 0) {
rsi_set_event(&common->tx_thread.event);
dev->rx_info.sdio_intr_status_zero++;
mutex_unlock(&common->rx_lock);
return;
}
rsi_dbg(ISR_ZONE, "%s: Intr_status = %x %d %d\n",
__func__, isr_status, (1 << MSDU_PKT_PENDING),
(1 << FW_ASSERT_IND));
if (isr_status & BIT(PKT_BUFF_AVAILABLE)) {
status = rsi_sdio_check_buffer_status(adapter, 0);
if (status < 0)
rsi_dbg(ERR_ZONE,
"%s: Failed to check buffer status\n",
__func__);
rsi_sdio_ack_intr(common->priv,
BIT(PKT_BUFF_AVAILABLE));
rsi_set_event(&common->tx_thread.event);
rsi_dbg(ISR_ZONE, "%s: ==> BUFFER_AVAILABLE <==\n",
__func__);
dev->buff_status_updated = true;
isr_status &= ~BIT(PKT_BUFF_AVAILABLE);
}
if (isr_status & BIT(FW_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,
BIT(FW_ASSERT_IND));
}
common->fsm_state = FSM_CARD_NOT_READY;
isr_status &= ~BIT(FW_ASSERT_IND);
}
if (isr_status & BIT(MSDU_PKT_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->rx_lock);
return;
}
isr_status &= ~BIT(MSDU_PKT_PENDING);
}
if (isr_status) {
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);
}
mutex_unlock(&common->rx_lock);
} while (1);
}
/* This function is used to read buffer status register and
* set relevant fields in rsi_91x_sdiodev struct.
*/
int rsi_sdio_check_buffer_status(struct rsi_hw *adapter, u8 q_num)
{
struct rsi_common *common = adapter->priv;
struct rsi_91x_sdiodev *dev = adapter->rsi_dev;
u8 buf_status = 0;
int status = 0;
static int counter = 4;
if (!dev->buff_status_updated && counter) {
counter--;
goto out;
}
dev->buff_status_updated = false;
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 (dev->rx_info.mgmt_buffer_full || dev->rx_info.buf_full_counter)
counter = 1;
else
counter = 4;
out:
if ((q_num == MGMT_SOFT_Q) && (dev->rx_info.mgmt_buffer_full))
return QUEUE_FULL;
if ((q_num < MGMT_SOFT_Q) && (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 = 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,939 @@
/*
* 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/types.h>
#include <net/rsi_91x.h>
#include "rsi_usb.h"
#include "rsi_hal.h"
#include "rsi_coex.h"
/* Default operating mode is wlan STA + BT */
static u16 dev_oper_mode = DEV_OPMODE_STA_BT_DUAL;
module_param(dev_oper_mode, ushort, 0444);
MODULE_PARM_DESC(dev_oper_mode, DEV_OPMODE_PARAM_DESC);
static int rsi_rx_urb_submit(struct rsi_hw *adapter, u8 ep_num, gfp_t flags);
/**
* 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, a negative error code on failure.
*/
static int rsi_usb_card_write(struct rsi_hw *adapter,
u8 *buf,
u16 len,
u8 endpoint)
{
struct rsi_91x_usbdev *dev = adapter->rsi_dev;
int status;
u8 *seg = dev->tx_buffer;
int transfer;
int ep = dev->bulkout_endpoint_addr[endpoint - 1];
memset(seg, 0, len + RSI_USB_TX_HEAD_ROOM);
memcpy(seg + RSI_USB_TX_HEAD_ROOM, buf, len);
len += RSI_USB_TX_HEAD_ROOM;
transfer = len;
status = usb_bulk_msg(dev->usbdev,
usb_sndbulkpipe(dev->usbdev, ep),
(void *)seg,
(int)len,
&transfer,
USB_CTRL_SET_TIMEOUT);
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.
* @endpoint: Type of endpoint.
* @data: Pointer to the data that has to be written.
* @count: Number of multiple bytes to be written.
*
* Return: 0 on success, a negative error code on failure.
*/
static int rsi_write_multiple(struct rsi_hw *adapter,
u8 endpoint,
u8 *data,
u32 count)
{
struct rsi_91x_usbdev *dev;
if (!adapter)
return -ENODEV;
if (endpoint == 0)
return -EINVAL;
dev = adapter->rsi_dev;
if (dev->write_fail)
return -ENETDOWN;
return rsi_usb_card_write(adapter, data, count, 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 = adapter->rsi_dev;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
__le16 buffer_size;
int ii, bin_found = 0, bout_found = 0;
iface_desc = interface->cur_altsetting;
for (ii = 0; ii < iface_desc->desc.bNumEndpoints; ++ii) {
endpoint = &(iface_desc->endpoint[ii].desc);
if (!dev->bulkin_endpoint_addr[bin_found] &&
(endpoint->bEndpointAddress & USB_DIR_IN) &&
((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
USB_ENDPOINT_XFER_BULK)) {
buffer_size = endpoint->wMaxPacketSize;
dev->bulkin_size[bin_found] = buffer_size;
dev->bulkin_endpoint_addr[bin_found] =
endpoint->bEndpointAddress;
bin_found++;
}
if (!dev->bulkout_endpoint_addr[bout_found] &&
!(endpoint->bEndpointAddress & USB_DIR_IN) &&
((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
USB_ENDPOINT_XFER_BULK)) {
buffer_size = endpoint->wMaxPacketSize;
dev->bulkout_endpoint_addr[bout_found] =
endpoint->bEndpointAddress;
dev->bulkout_size[bout_found] = buffer_size;
bout_found++;
}
if (bin_found >= MAX_BULK_EP || bout_found >= MAX_BULK_EP)
break;
}
if (!(dev->bulkin_endpoint_addr[0] && dev->bulkout_endpoint_addr[0])) {
dev_err(&interface->dev, "missing wlan bulk endpoints\n");
return -EINVAL;
}
if (adapter->priv->coex_mode > 1) {
if (!dev->bulkin_endpoint_addr[1]) {
dev_err(&interface->dev, "missing bt bulk-in endpoint\n");
return -EINVAL;
}
}
return 0;
}
#define RSI_USB_REQ_OUT (USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE)
#define RSI_USB_REQ_IN (USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE)
/* 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, a negative error code on failure.
*/
static int rsi_usb_reg_read(struct usb_device *usbdev,
u32 reg,
u16 *value,
u16 len)
{
u8 *buf;
int status = -ENOMEM;
if (len > RSI_USB_CTRL_BUF_SIZE)
return -EINVAL;
buf = kmalloc(RSI_USB_CTRL_BUF_SIZE, GFP_KERNEL);
if (!buf)
return status;
status = usb_control_msg(usbdev,
usb_rcvctrlpipe(usbdev, 0),
USB_VENDOR_REGISTER_READ,
RSI_USB_REQ_IN,
((reg & 0xffff0000) >> 16), (reg & 0xffff),
(void *)buf,
len,
USB_CTRL_GET_TIMEOUT);
*value = (buf[0] | (buf[1] << 8));
if (status < 0) {
rsi_dbg(ERR_ZONE,
"%s: Reg read failed with error code :%d\n",
__func__, status);
}
kfree(buf);
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, a negative error code on failure.
*/
static int rsi_usb_reg_write(struct usb_device *usbdev,
u32 reg,
u32 value,
u16 len)
{
u8 *usb_reg_buf;
int status = -ENOMEM;
if (len > RSI_USB_CTRL_BUF_SIZE)
return -EINVAL;
usb_reg_buf = kmalloc(RSI_USB_CTRL_BUF_SIZE, GFP_KERNEL);
if (!usb_reg_buf)
return status;
usb_reg_buf[0] = value & 0x00ff;
usb_reg_buf[1] = (value & 0xff00) >> 8;
usb_reg_buf[2] = (value & 0x00ff0000) >> 16;
usb_reg_buf[3] = (value & 0xff000000) >> 24;
status = usb_control_msg(usbdev,
usb_sndctrlpipe(usbdev, 0),
USB_VENDOR_REGISTER_WRITE,
RSI_USB_REQ_OUT,
(reg & 0xffff0000) >> 16,
reg & 0xffff,
(void *)usb_reg_buf,
len,
USB_CTRL_SET_TIMEOUT);
if (status < 0) {
rsi_dbg(ERR_ZONE,
"%s: Reg write failed with error code :%d\n",
__func__, status);
}
kfree(usb_reg_buf);
return status;
}
/**
* rsi_rx_done_handler() - This function is called when a packet is received
* from USB stack. This is callback to receive done.
* @urb: Received URB.
*
* Return: None.
*/
static void rsi_rx_done_handler(struct urb *urb)
{
struct rx_usb_ctrl_block *rx_cb = urb->context;
struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)rx_cb->data;
int status = -EINVAL;
if (!rx_cb->rx_skb)
return;
if (urb->status) {
dev_kfree_skb(rx_cb->rx_skb);
rx_cb->rx_skb = NULL;
return;
}
if (urb->actual_length <= 0 ||
urb->actual_length > rx_cb->rx_skb->len) {
rsi_dbg(INFO_ZONE, "%s: Invalid packet length = %d\n",
__func__, urb->actual_length);
goto out;
}
if (skb_queue_len(&dev->rx_q) >= RSI_MAX_RX_PKTS) {
rsi_dbg(INFO_ZONE, "Max RX packets reached\n");
goto out;
}
skb_trim(rx_cb->rx_skb, urb->actual_length);
skb_queue_tail(&dev->rx_q, rx_cb->rx_skb);
rsi_set_event(&dev->rx_thread.event);
status = 0;
out:
if (rsi_rx_urb_submit(dev->priv, rx_cb->ep_num, GFP_ATOMIC))
rsi_dbg(ERR_ZONE, "%s: Failed in urb submission", __func__);
if (status) {
dev_kfree_skb(rx_cb->rx_skb);
rx_cb->rx_skb = NULL;
}
}
static void rsi_rx_urb_kill(struct rsi_hw *adapter, u8 ep_num)
{
struct rsi_91x_usbdev *dev = adapter->rsi_dev;
struct rx_usb_ctrl_block *rx_cb = &dev->rx_cb[ep_num - 1];
struct urb *urb = rx_cb->rx_urb;
usb_kill_urb(urb);
}
/**
* rsi_rx_urb_submit() - This function submits the given URB to the USB stack.
* @adapter: Pointer to the adapter structure.
* @ep_num: Endpoint number.
* @mem_flags: The type of memory to allocate.
*
* Return: 0 on success, a negative error code on failure.
*/
static int rsi_rx_urb_submit(struct rsi_hw *adapter, u8 ep_num, gfp_t mem_flags)
{
struct rsi_91x_usbdev *dev = adapter->rsi_dev;
struct rx_usb_ctrl_block *rx_cb = &dev->rx_cb[ep_num - 1];
struct urb *urb = rx_cb->rx_urb;
int status;
struct sk_buff *skb;
u8 dword_align_bytes = 0;
skb = dev_alloc_skb(RSI_MAX_RX_USB_PKT_SIZE);
if (!skb)
return -ENOMEM;
skb_reserve(skb, MAX_DWORD_ALIGN_BYTES);
skb_put(skb, RSI_MAX_RX_USB_PKT_SIZE - MAX_DWORD_ALIGN_BYTES);
dword_align_bytes = (unsigned long)skb->data & 0x3f;
if (dword_align_bytes > 0)
skb_push(skb, dword_align_bytes);
urb->transfer_buffer = skb->data;
rx_cb->rx_skb = skb;
usb_fill_bulk_urb(urb,
dev->usbdev,
usb_rcvbulkpipe(dev->usbdev,
dev->bulkin_endpoint_addr[ep_num - 1]),
urb->transfer_buffer,
skb->len,
rsi_rx_done_handler,
rx_cb);
status = usb_submit_urb(urb, mem_flags);
if (status) {
rsi_dbg(ERR_ZONE, "%s: Failed in urb submission\n", __func__);
dev_kfree_skb(skb);
}
return status;
}
static int rsi_usb_read_register_multiple(struct rsi_hw *adapter, u32 addr,
u8 *data, u16 count)
{
struct rsi_91x_usbdev *dev = adapter->rsi_dev;
u8 *buf;
u16 transfer;
int status;
if (!addr)
return -EINVAL;
buf = kzalloc(RSI_USB_BUF_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
while (count) {
transfer = min_t(u16, count, RSI_USB_BUF_SIZE);
status = usb_control_msg(dev->usbdev,
usb_rcvctrlpipe(dev->usbdev, 0),
USB_VENDOR_REGISTER_READ,
RSI_USB_REQ_IN,
((addr & 0xffff0000) >> 16),
(addr & 0xffff), (void *)buf,
transfer, USB_CTRL_GET_TIMEOUT);
if (status < 0) {
rsi_dbg(ERR_ZONE,
"Reg read failed with error code :%d\n",
status);
kfree(buf);
return status;
}
memcpy(data, buf, transfer);
count -= transfer;
data += transfer;
addr += transfer;
}
kfree(buf);
return 0;
}
/**
* 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, a negative error code on failure.
*/
static int rsi_usb_write_register_multiple(struct rsi_hw *adapter, u32 addr,
u8 *data, u16 count)
{
struct rsi_91x_usbdev *dev = adapter->rsi_dev;
u8 *buf;
u16 transfer;
int status = 0;
buf = kzalloc(RSI_USB_BUF_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
while (count) {
transfer = min_t(u16, count, RSI_USB_BUF_SIZE);
memcpy(buf, data, transfer);
status = usb_control_msg(dev->usbdev,
usb_sndctrlpipe(dev->usbdev, 0),
USB_VENDOR_REGISTER_WRITE,
RSI_USB_REQ_OUT,
((addr & 0xffff0000) >> 16),
(addr & 0xffff),
(void *)buf,
transfer,
USB_CTRL_SET_TIMEOUT);
if (status < 0) {
rsi_dbg(ERR_ZONE,
"Reg write failed with error code :%d\n",
status);
kfree(buf);
return status;
}
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, a negative error code on failure.
*/
static int rsi_usb_host_intf_write_pkt(struct rsi_hw *adapter,
u8 *pkt,
u32 len)
{
u32 queueno = ((pkt[1] >> 4) & 0x7);
u8 endpoint;
endpoint = ((queueno == RSI_WIFI_MGMT_Q || queueno == RSI_WIFI_DATA_Q ||
queueno == RSI_COEX_Q) ? WLAN_EP : BT_EP);
return rsi_write_multiple(adapter,
endpoint,
(u8 *)pkt,
len);
}
static int rsi_usb_master_reg_read(struct rsi_hw *adapter, u32 reg,
u32 *value, u16 len)
{
struct usb_device *usbdev =
((struct rsi_91x_usbdev *)adapter->rsi_dev)->usbdev;
u16 temp;
int ret;
ret = rsi_usb_reg_read(usbdev, reg, &temp, len);
if (ret < 0)
return ret;
*value = temp;
return 0;
}
static int rsi_usb_master_reg_write(struct rsi_hw *adapter,
unsigned long reg,
unsigned long value, u16 len)
{
struct usb_device *usbdev =
((struct rsi_91x_usbdev *)adapter->rsi_dev)->usbdev;
return rsi_usb_reg_write(usbdev, reg, value, len);
}
static int rsi_usb_load_data_master_write(struct rsi_hw *adapter,
u32 base_address,
u32 instructions_sz, u16 block_size,
u8 *ta_firmware)
{
u16 num_blocks;
u32 cur_indx, i;
u8 temp_buf[256];
int status;
num_blocks = instructions_sz / block_size;
rsi_dbg(INFO_ZONE, "num_blocks: %d\n", num_blocks);
for (cur_indx = 0, i = 0; i < num_blocks; i++, cur_indx += block_size) {
memcpy(temp_buf, ta_firmware + cur_indx, block_size);
status = rsi_usb_write_register_multiple(adapter, base_address,
(u8 *)(temp_buf),
block_size);
if (status < 0)
return status;
rsi_dbg(INFO_ZONE, "%s: loading block: %d\n", __func__, i);
base_address += block_size;
}
if (instructions_sz % block_size) {
memset(temp_buf, 0, block_size);
memcpy(temp_buf, ta_firmware + cur_indx,
instructions_sz % block_size);
status = rsi_usb_write_register_multiple
(adapter, base_address,
(u8 *)temp_buf,
instructions_sz % block_size);
if (status < 0)
return status;
rsi_dbg(INFO_ZONE,
"Written Last Block in Address 0x%x Successfully\n",
cur_indx);
}
return 0;
}
static struct rsi_host_intf_ops usb_host_intf_ops = {
.write_pkt = rsi_usb_host_intf_write_pkt,
.read_reg_multiple = rsi_usb_read_register_multiple,
.write_reg_multiple = rsi_usb_write_register_multiple,
.master_reg_read = rsi_usb_master_reg_read,
.master_reg_write = rsi_usb_master_reg_write,
.load_data_master_write = rsi_usb_load_data_master_write,
};
/**
* 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 = adapter->rsi_dev;
rsi_kill_thread(&dev->rx_thread);
usb_free_urb(dev->rx_cb[0].rx_urb);
if (adapter->priv->coex_mode > 1)
usb_free_urb(dev->rx_cb[1].rx_urb);
kfree(dev->tx_buffer);
}
static int rsi_usb_init_rx(struct rsi_hw *adapter)
{
struct rsi_91x_usbdev *dev = adapter->rsi_dev;
struct rx_usb_ctrl_block *rx_cb;
u8 idx, num_rx_cb;
num_rx_cb = (adapter->priv->coex_mode > 1 ? 2 : 1);
for (idx = 0; idx < num_rx_cb; idx++) {
rx_cb = &dev->rx_cb[idx];
rx_cb->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!rx_cb->rx_urb) {
rsi_dbg(ERR_ZONE, "Failed alloc rx urb[%d]\n", idx);
goto err;
}
rx_cb->ep_num = idx + 1;
rx_cb->data = (void *)dev;
}
skb_queue_head_init(&dev->rx_q);
rsi_init_event(&dev->rx_thread.event);
if (rsi_create_kthread(adapter->priv, &dev->rx_thread,
rsi_usb_rx_thread, "RX-Thread")) {
rsi_dbg(ERR_ZONE, "%s: Unable to init rx thrd\n", __func__);
goto err;
}
return 0;
err:
usb_free_urb(dev->rx_cb[0].rx_urb);
if (adapter->priv->coex_mode > 1)
usb_free_urb(dev->rx_cb[1].rx_urb);
return -1;
}
/**
* 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, a negative error code on failure.
*/
static int rsi_init_usb_interface(struct rsi_hw *adapter,
struct usb_interface *pfunction)
{
struct rsi_91x_usbdev *rsi_dev;
int status;
rsi_dev = kzalloc_obj(*rsi_dev);
if (!rsi_dev)
return -ENOMEM;
adapter->rsi_dev = rsi_dev;
rsi_dev->usbdev = interface_to_usbdev(pfunction);
rsi_dev->priv = (void *)adapter;
if (rsi_find_bulk_in_and_out_endpoints(pfunction, adapter)) {
status = -EINVAL;
goto fail_eps;
}
adapter->device = &pfunction->dev;
usb_set_intfdata(pfunction, adapter);
rsi_dev->tx_buffer = kmalloc(2048, GFP_KERNEL);
if (!rsi_dev->tx_buffer) {
status = -ENOMEM;
goto fail_eps;
}
if (rsi_usb_init_rx(adapter)) {
rsi_dbg(ERR_ZONE, "Failed to init RX handle\n");
status = -ENOMEM;
goto fail_rx;
}
rsi_dev->tx_blk_size = 252;
adapter->block_size = rsi_dev->tx_blk_size;
/* Initializing function callbacks */
adapter->check_hw_queue_status = rsi_usb_check_queue_status;
adapter->determine_event_timeout = rsi_usb_event_timeout;
adapter->rsi_host_intf = RSI_HOST_INTF_USB;
adapter->host_intf_ops = &usb_host_intf_ops;
#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_rx:
kfree(rsi_dev->tx_buffer);
fail_eps:
return status;
}
static int usb_ulp_read_write(struct rsi_hw *adapter, u16 addr, u32 data,
u16 len_in_bits)
{
int ret;
ret = rsi_usb_master_reg_write
(adapter, RSI_GSPI_DATA_REG1,
((addr << 6) | ((data >> 16) & 0xffff)), 2);
if (ret < 0)
return ret;
ret = rsi_usb_master_reg_write(adapter, RSI_GSPI_DATA_REG0,
(data & 0xffff), 2);
if (ret < 0)
return ret;
/* Initializing GSPI for ULP read/writes */
rsi_usb_master_reg_write(adapter, RSI_GSPI_CTRL_REG0,
RSI_GSPI_CTRL_REG0_VALUE, 2);
ret = rsi_usb_master_reg_write(adapter, RSI_GSPI_CTRL_REG1,
((len_in_bits - 1) | RSI_GSPI_TRIG), 2);
if (ret < 0)
return ret;
msleep(20);
return 0;
}
static int rsi_reset_card(struct rsi_hw *adapter)
{
int ret;
rsi_dbg(INFO_ZONE, "Resetting Card...\n");
rsi_usb_master_reg_write(adapter, RSI_TA_HOLD_REG, 0xE, 4);
/* This msleep will ensure Thread-Arch processor to go to hold
* and any pending dma transfers to rf in device to finish.
*/
msleep(100);
ret = rsi_usb_master_reg_write(adapter, SWBL_REGOUT,
RSI_FW_WDT_DISABLE_REQ,
RSI_COMMON_REG_SIZE);
if (ret < 0) {
rsi_dbg(ERR_ZONE, "Disabling firmware watchdog timer failed\n");
goto fail;
}
if (adapter->device_model != RSI_DEV_9116) {
ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_TIMER_1,
RSI_ULP_WRITE_2, 32);
if (ret < 0)
goto fail;
ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_TIMER_2,
RSI_ULP_WRITE_0, 32);
if (ret < 0)
goto fail;
ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_DELAY_TIMER_1,
RSI_ULP_WRITE_50, 32);
if (ret < 0)
goto fail;
ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_DELAY_TIMER_2,
RSI_ULP_WRITE_0, 32);
if (ret < 0)
goto fail;
ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_TIMER_ENABLE,
RSI_ULP_TIMER_ENABLE, 32);
if (ret < 0)
goto fail;
} else {
ret = rsi_usb_master_reg_write(adapter,
NWP_WWD_INTERRUPT_TIMER,
NWP_WWD_INT_TIMER_CLKS,
RSI_9116_REG_SIZE);
if (ret < 0)
goto fail;
ret = rsi_usb_master_reg_write(adapter,
NWP_WWD_SYSTEM_RESET_TIMER,
NWP_WWD_SYS_RESET_TIMER_CLKS,
RSI_9116_REG_SIZE);
if (ret < 0)
goto fail;
ret = rsi_usb_master_reg_write(adapter,
NWP_WWD_MODE_AND_RSTART,
NWP_WWD_TIMER_DISABLE,
RSI_9116_REG_SIZE);
if (ret < 0)
goto fail;
}
rsi_dbg(INFO_ZONE, "Reset card done\n");
return ret;
fail:
rsi_dbg(ERR_ZONE, "Reset card failed\n");
return ret;
}
/**
* 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, a negative error code 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;
int status;
rsi_dbg(INIT_ZONE, "%s: Init function called\n", __func__);
adapter = rsi_91x_init(dev_oper_mode);
if (!adapter) {
rsi_dbg(ERR_ZONE, "%s: Failed to init os intf ops\n",
__func__);
return -ENOMEM;
}
adapter->rsi_host_intf = RSI_HOST_INTF_USB;
status = rsi_init_usb_interface(adapter, pfunction);
if (status) {
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__);
if (id->idProduct == RSI_USB_PID_9113) {
rsi_dbg(INIT_ZONE, "%s: 9113 module detected\n", __func__);
adapter->device_model = RSI_DEV_9113;
} else if (id->idProduct == RSI_USB_PID_9116) {
rsi_dbg(INIT_ZONE, "%s: 9116 module detected\n", __func__);
adapter->device_model = RSI_DEV_9116;
} else {
rsi_dbg(ERR_ZONE, "%s: Unsupported RSI device id 0x%x\n",
__func__, id->idProduct);
status = -ENODEV;
goto err1;
}
dev = adapter->rsi_dev;
status = rsi_usb_reg_read(dev->usbdev, FW_STATUS_REG, &fw_status, 2);
if (status < 0)
goto err1;
else
fw_status &= 1;
if (!fw_status) {
rsi_dbg(INIT_ZONE, "Loading firmware...\n");
status = rsi_hal_device_init(adapter);
if (status) {
rsi_dbg(ERR_ZONE, "%s: Failed in device init\n",
__func__);
goto err1;
}
rsi_dbg(INIT_ZONE, "%s: Device Init Done\n", __func__);
}
status = rsi_rx_urb_submit(adapter, WLAN_EP, GFP_KERNEL);
if (status)
goto err1;
if (adapter->priv->coex_mode > 1) {
status = rsi_rx_urb_submit(adapter, BT_EP, GFP_KERNEL);
if (status)
goto err_kill_wlan_urb;
}
return 0;
err_kill_wlan_urb:
rsi_rx_urb_kill(adapter, WLAN_EP);
err1:
rsi_deinit_usb_interface(adapter);
err:
rsi_91x_deinit(adapter);
rsi_dbg(ERR_ZONE, "%s: Failed in probe...Exiting\n", __func__);
return status;
}
/**
* rsi_disconnect() - This function performs the reverse of the probe function,
* it deinitialize 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_rfkill_exit(adapter);
rsi_mac80211_detach(adapter);
if (IS_ENABLED(CONFIG_RSI_COEX) && adapter->priv->coex_mode > 1 &&
adapter->priv->bt_adapter) {
rsi_bt_ops.detach(adapter->priv->bt_adapter);
adapter->priv->bt_adapter = NULL;
}
if (adapter->priv->coex_mode > 1)
rsi_rx_urb_kill(adapter, BT_EP);
rsi_rx_urb_kill(adapter, WLAN_EP);
rsi_reset_card(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(RSI_USB_VENDOR_ID, RSI_USB_PID_9113) },
{ USB_DEVICE(RSI_USB_VENDOR_ID, RSI_USB_PID_9116) },
{ /* 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
};
module_usb_driver(rsi_driver);
MODULE_AUTHOR("Redpine Signals Inc");
MODULE_DESCRIPTION("Common USB layer for RSI drivers");
MODULE_DEVICE_TABLE(usb, rsi_dev_table);
MODULE_FIRMWARE(FIRMWARE_RSI9113);
MODULE_VERSION("0.1");
MODULE_LICENSE("Dual BSD/GPL");
@@ -0,0 +1,61 @@
/*
* 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_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 = adapter->rsi_dev;
int status;
struct sk_buff *skb;
do {
rsi_wait_event(&dev->rx_thread.event, EVENT_WAIT_FOREVER);
rsi_reset_event(&dev->rx_thread.event);
while (true) {
if (atomic_read(&dev->rx_thread.thread_done))
goto out;
skb = skb_dequeue(&dev->rx_q);
if (!skb)
break;
status = rsi_read_pkt(common, skb->data, 0);
if (status) {
rsi_dbg(ERR_ZONE, "%s: Failed To read data",
__func__);
break;
}
dev_kfree_skb(skb);
}
} while (1);
out:
rsi_dbg(INFO_ZONE, "%s: Terminated thread\n", __func__);
skb_queue_purge(&dev->rx_q);
kthread_complete_and_exit(&dev->rx_thread.completion, 0);
}
@@ -0,0 +1,193 @@
/*
* 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 9
#define TA_PLL_N_VAL_20 0
#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 80
#define TA_PLL_M_VAL_40 9
#define TA_PLL_N_VAL_40 0
#define TA_PLL_P_VAL_40 4
#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_TAPLL_CONFIGS | WIFI_PLL960_CONFIGS | WIFI_AFEPLL_CONFIGS | \
WIFI_SWITCH_CLK_CONFIGS | BOOTUP_MODE_INFO | CRYSTAL_GOOD_TIME)
#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;
struct pll_config_9116 {
__le16 pll_ctrl_set_reg;
__le16 pll_ctrl_clr_reg;
__le16 pll_modem_conig_reg;
__le16 soc_clk_config_reg;
__le16 adc_dac_strm1_config_reg;
__le16 adc_dac_strm2_config_reg;
} __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;
#define RSI_SWITCH_TASS_CLK BIT(0)
#define RSI_SWITCH_QSPI_CLK BIT(1)
#define RSI_SWITCH_SLP_CLK_2_32 BIT(2)
#define RSI_SWITCH_WLAN_BBP_LMAC_CLK_REG BIT(3)
#define RSI_SWITCH_ZBBT_BBP_LMAC_CLK_REG BIT(4)
#define RSI_SWITCH_BBP_LMAC_CLK_REG BIT(5)
#define RSI_MODEM_CLK_160MHZ BIT(6)
struct switch_clk_9116 {
__le32 switch_clk_info;
__le32 tass_clock_reg;
__le32 wlan_bbp_lmac_clk_reg_val;
__le32 zbbt_bbp_lmac_clk_reg_val;
__le32 bbp_lmac_clk_en_val;
} __packed;
struct device_clk_info {
struct pll_config pll_config_g;
struct switch_clk switch_clk_g;
} __packed;
struct device_clk_info_9116 {
struct pll_config_9116 pll_config_9116_g;
struct switch_clk_9116 switch_clk_9116_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;
__le32 waiting_time_at_fresh_sleep;
__le32 max_threshold_to_avoid_sleep;
u8 beacon_resedue_alg_en;
} __packed;
struct bootup_params_9116 {
__le16 magic_number;
#define LOADED_TOKEN 0x5AA5 /* Bootup params are installed by host
* or OTP/FLASH (Bootloader)
*/
#define ROM_TOKEN 0x55AA /* Bootup params are taken from ROM
* itself in MCU mode.
*/
__le16 crystal_good_time;
__le32 valid;
__le32 reserved_for_valids;
__le16 bootup_mode_info;
#define BT_COEXIST BIT(0)
#define BOOTUP_MODE (BIT(2) | BIT(1))
#define CUR_DEV_MODE_9116 (bootup_params_9116.bootup_mode_info >> 1)
__le16 digital_loop_back_params;
__le16 rtls_timestamp_en;
__le16 host_spi_intr_cfg;
struct device_clk_info_9116 device_clk_info_9116[1];
__le32 buckboost_wakeup_cnt;
__le16 pmu_wakeup_wait;
u8 shutdown_wait_time;
u8 pmu_slp_clkout_sel;
__le32 wdt_prog_value;
__le32 wdt_soc_rst_delay;
__le32 dcdc_operation_mode;
__le32 soc_reset_wait_cnt;
__le32 waiting_time_at_fresh_sleep;
__le32 max_threshold_to_avoid_sleep;
u8 beacon_resedue_alg_en;
} __packed;
#endif
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2018 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_COEX_H__
#define __RSI_COEX_H__
#include "rsi_common.h"
#ifdef CONFIG_RSI_COEX
#define COMMON_CARD_READY_IND 0
#define NUM_COEX_TX_QUEUES 5
struct rsi_coex_ctrl_block {
struct rsi_common *priv;
struct sk_buff_head coex_tx_qs[NUM_COEX_TX_QUEUES];
struct rsi_thread coex_tx_thread;
};
int rsi_coex_attach(struct rsi_common *common);
void rsi_coex_detach(struct rsi_common *common);
int rsi_coex_send_pkt(void *priv, struct sk_buff *skb, u8 proto_type);
int rsi_coex_recv_pkt(struct rsi_common *common, u8 *msg);
#endif
#endif
@@ -0,0 +1,92 @@
/*
* 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 FIRMWARE_RSI9113 "rs9113_wlan_qspi.rps"
#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);
atomic_set(&thread->thread_done, 0);
thread->task = kthread_run(func_ptr, common, "%s", name);
if (IS_ERR(thread->task))
return (int)PTR_ERR(thread->task);
return 0;
}
static inline void rsi_kill_thread(struct rsi_thread *handle)
{
atomic_inc(&handle->thread_done);
rsi_set_event(&handle->event);
wait_for_completion(&handle->completion);
}
void rsi_mac80211_detach(struct rsi_hw *hw);
void rsi_mac80211_rfkill_exit(struct rsi_hw *hw);
u16 rsi_get_connected_channel(struct ieee80211_vif *vif);
struct rsi_hw *rsi_91x_init(u16 oper_mode);
void rsi_91x_deinit(struct rsi_hw *adapter);
int rsi_read_pkt(struct rsi_common *common, u8 *rx_pkt, s32 rcv_pkt_len);
#ifdef CONFIG_PM
int rsi_config_wowlan(struct rsi_hw *adapter, struct cfg80211_wowlan *wowlan);
#endif
struct rsi_sta *rsi_find_sta(struct rsi_common *common, u8 *mac_addr);
struct ieee80211_vif *rsi_get_vif(struct rsi_hw *adapter, u8 *mac);
void rsi_roc_timeout(struct timer_list *t);
#endif
@@ -0,0 +1,47 @@
/*
* 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 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,230 @@
/*
* Copyright (c) 2017 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_HAL_H__
#define __RSI_HAL_H__
/* Device Operating modes */
#define DEV_OPMODE_WIFI_ALONE 1
#define DEV_OPMODE_BT_ALONE 4
#define DEV_OPMODE_BT_LE_ALONE 8
#define DEV_OPMODE_BT_DUAL 12
#define DEV_OPMODE_STA_BT 5
#define DEV_OPMODE_STA_BT_LE 9
#define DEV_OPMODE_STA_BT_DUAL 13
#define DEV_OPMODE_AP_BT 6
#define DEV_OPMODE_AP_BT_DUAL 14
#define DEV_OPMODE_PARAM_DESC \
__stringify(DEV_OPMODE_WIFI_ALONE) "[Wi-Fi alone], " \
__stringify(DEV_OPMODE_BT_ALONE) "[BT classic alone], " \
__stringify(DEV_OPMODE_BT_LE_ALONE) "[BT LE alone], " \
__stringify(DEV_OPMODE_BT_DUAL) "[BT classic + BT LE alone], " \
__stringify(DEV_OPMODE_STA_BT) "[Wi-Fi STA + BT classic], " \
__stringify(DEV_OPMODE_STA_BT_LE) "[Wi-Fi STA + BT LE], " \
__stringify(DEV_OPMODE_STA_BT_DUAL) "[Wi-Fi STA + BT classic + BT LE], " \
__stringify(DEV_OPMODE_AP_BT) "[Wi-Fi AP + BT classic], " \
__stringify(DEV_OPMODE_AP_BT_DUAL) "[Wi-Fi AP + BT classic + BT LE]"
#define FLASH_WRITE_CHUNK_SIZE (4 * 1024)
#define FLASH_SECTOR_SIZE (4 * 1024)
#define FLASH_SIZE_ADDR 0x04000016
#define PING_BUFFER_ADDRESS 0x19000
#define PONG_BUFFER_ADDRESS 0x1a000
#define SWBL_REGIN 0x41050034
#define SWBL_REGOUT 0x4105003c
#define PING_WRITE 0x1
#define PONG_WRITE 0x2
#define BL_CMD_TIMEOUT 2000
#define BL_BURN_TIMEOUT (50 * 1000)
#define REGIN_VALID 0xA
#define REGIN_INPUT 0xA0
#define REGOUT_VALID 0xAB
#define REGOUT_INVALID (~0xAB)
#define CMD_PASS 0xAA
#define CMD_FAIL 0xCC
#define LOAD_HOSTED_FW 'A'
#define BURN_HOSTED_FW 'B'
#define PING_VALID 'I'
#define PONG_VALID 'O'
#define PING_AVAIL 'I'
#define PONG_AVAIL 'O'
#define EOF_REACHED 'E'
#define CHECK_CRC 'K'
#define POLLING_MODE 'P'
#define AUTO_READ_MODE 'R'
#define JUMP_TO_ZERO_PC 'J'
#define FW_LOADING_SUCCESSFUL 'S'
#define LOADING_INITIATED '1'
#define RSI_ULP_RESET_REG 0x161
#define RSI_WATCH_DOG_TIMER_1 0x16c
#define RSI_WATCH_DOG_TIMER_2 0x16d
#define RSI_WATCH_DOG_DELAY_TIMER_1 0x16e
#define RSI_WATCH_DOG_DELAY_TIMER_2 0x16f
#define RSI_WATCH_DOG_TIMER_ENABLE 0x170
/* Watchdog timer addresses for 9116 */
#define NWP_AHB_BASE_ADDR 0x41300000
#define NWP_WWD_INTERRUPT_TIMER (NWP_AHB_BASE_ADDR + 0x300)
#define NWP_WWD_SYSTEM_RESET_TIMER (NWP_AHB_BASE_ADDR + 0x304)
#define NWP_WWD_WINDOW_TIMER (NWP_AHB_BASE_ADDR + 0x308)
#define NWP_WWD_TIMER_SETTINGS (NWP_AHB_BASE_ADDR + 0x30C)
#define NWP_WWD_MODE_AND_RSTART (NWP_AHB_BASE_ADDR + 0x310)
#define NWP_WWD_RESET_BYPASS (NWP_AHB_BASE_ADDR + 0x314)
#define NWP_FSM_INTR_MASK_REG (NWP_AHB_BASE_ADDR + 0x104)
/* Watchdog timer values */
#define NWP_WWD_INT_TIMER_CLKS 5
#define NWP_WWD_SYS_RESET_TIMER_CLKS 4
#define NWP_WWD_TIMER_DISABLE 0xAA0001
#define RSI_ULP_WRITE_0 00
#define RSI_ULP_WRITE_2 02
#define RSI_ULP_WRITE_50 50
#define RSI_RESTART_WDT BIT(11)
#define RSI_BYPASS_ULP_ON_WDT BIT(1)
#define RSI_ULP_TIMER_ENABLE ((0xaa000) | RSI_RESTART_WDT | \
RSI_BYPASS_ULP_ON_WDT)
#define RSI_RF_SPI_PROG_REG_BASE_ADDR 0x40080000
#define RSI_GSPI_CTRL_REG0 (RSI_RF_SPI_PROG_REG_BASE_ADDR)
#define RSI_GSPI_CTRL_REG1 (RSI_RF_SPI_PROG_REG_BASE_ADDR + 0x2)
#define RSI_GSPI_DATA_REG0 (RSI_RF_SPI_PROG_REG_BASE_ADDR + 0x4)
#define RSI_GSPI_DATA_REG1 (RSI_RF_SPI_PROG_REG_BASE_ADDR + 0x6)
#define RSI_GSPI_DATA_REG2 (RSI_RF_SPI_PROG_REG_BASE_ADDR + 0x8)
#define RSI_GSPI_CTRL_REG0_VALUE 0x340
#define RSI_GSPI_DMA_MODE BIT(13)
#define RSI_GSPI_2_ULP BIT(12)
#define RSI_GSPI_TRIG BIT(7)
#define RSI_GSPI_READ BIT(6)
#define RSI_GSPI_RF_SPI_ACTIVE BIT(8)
/* Boot loader commands */
#define SEND_RPS_FILE '2'
#define FW_IMAGE_MIN_ADDRESS (68 * 1024)
#define MAX_FLASH_FILE_SIZE (400 * 1024) //400K
#define FLASH_START_ADDRESS 16
#define COMMON_HAL_CARD_READY_IND 0x0
#define COMMAN_HAL_WAIT_FOR_CARD_READY 1
#define RSI_DEV_OPMODE_WIFI_ALONE 1
#define RSI_DEV_COEX_MODE_WIFI_ALONE 1
#define BBP_INFO_40MHZ 0x6
#define FW_FLASH_OFFSET 0x820
#define LMAC_VER_OFFSET_9113 (FW_FLASH_OFFSET + 0x200)
#define LMAC_VER_OFFSET_9116 0x22C2
#define MAX_DWORD_ALIGN_BYTES 64
#define RSI_COMMON_REG_SIZE 2
#define RSI_9116_REG_SIZE 4
#define FW_ALIGN_SIZE 4
#define RSI_9116_FW_MAGIC_WORD 0x5aa5
#define MEM_ACCESS_CTRL_FROM_HOST 0x41300000
#define RAM_384K_ACCESS_FROM_TA (BIT(2) | BIT(3) | BIT(4) | BIT(5) | \
BIT(20) | BIT(21) | BIT(22) | \
BIT(23) | BIT(24) | BIT(25))
struct bl_header {
__le32 flags;
__le32 image_no;
__le32 check_sum;
__le32 flash_start_address;
__le32 flash_len;
} __packed;
struct ta_metadata {
char *name;
unsigned int address;
};
#define RSI_BL_CTRL_LEN_MASK 0xFFFFFF
#define RSI_BL_CTRL_SPI_32BIT_MODE BIT(27)
#define RSI_BL_CTRL_REL_TA_SOFTRESET BIT(28)
#define RSI_BL_CTRL_START_FROM_ROM_PC BIT(29)
#define RSI_BL_CTRL_SPI_8BIT_MODE BIT(30)
#define RSI_BL_CTRL_LAST_ENTRY BIT(31)
struct bootload_entry {
__le32 control;
__le32 dst_addr;
} __packed;
struct bootload_ds {
__le16 fixed_pattern;
__le16 offset;
__le32 reserved;
struct bootload_entry bl_entry[7];
} __packed;
struct rsi_mgmt_desc {
__le16 len_qno;
u8 frame_type;
u8 misc_flags;
u8 xtend_desc_size;
u8 header_len;
__le16 frame_info;
__le16 rate_info;
__le16 bbp_info;
__le16 seq_ctrl;
u8 reserved2;
u8 sta_id;
} __packed;
struct rsi_data_desc {
__le16 len_qno;
u8 cfm_frame_type;
u8 misc_flags;
u8 xtend_desc_size;
u8 header_len;
__le16 frame_info;
__le16 rate_info;
__le16 bbp_info;
__le16 mac_flags;
u8 qid_tid;
u8 sta_id;
} __packed;
struct rsi_bt_desc {
__le16 len_qno;
__le16 reserved1;
__le32 reserved2;
__le32 reserved3;
__le16 reserved4;
__le16 bt_pkt_type;
} __packed;
int rsi_hal_device_init(struct rsi_hw *adapter);
int rsi_prepare_mgmt_desc(struct rsi_common *common, struct sk_buff *skb);
int rsi_prepare_data_desc(struct rsi_common *common, struct sk_buff *skb);
int rsi_prepare_beacon(struct rsi_common *common, struct sk_buff *skb);
int rsi_send_pkt_to_bus(struct rsi_common *common, struct sk_buff *skb);
int rsi_send_bt_pkt(struct rsi_common *common, struct sk_buff *skb);
#endif
@@ -0,0 +1,414 @@
/*
* 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>
#include <net/rsi_91x.h>
struct rsi_sta {
struct ieee80211_sta *sta;
s16 sta_id;
u16 seq_start[IEEE80211_NUM_TIDS];
bool start_tx_aggr[IEEE80211_NUM_TIDS];
};
struct rsi_hw;
#include "rsi_ps.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 */
enum RSI_FSM_STATES {
FSM_FW_NOT_LOADED,
FSM_CARD_NOT_READY,
FSM_COMMON_DEV_PARAMS_SENT,
FSM_BOOT_PARAMS_SENT,
FSM_EEPROM_READ_MAC_ADDR,
FSM_EEPROM_READ_RF_TYPE,
FSM_RESET_MAC_SENT,
FSM_RADIO_CAPS_SENT,
FSM_BB_RF_PROG_SENT,
FSM_MAC_INIT_DONE,
NUM_FSM_STATES
};
extern u32 rsi_zone_enabled;
extern __printf(2, 3) void rsi_dbg(u32 zone, const char *fmt, ...);
#define RSI_MAX_BANDS 2
#define RSI_MAX_VIFS 3
#define NUM_EDCA_QUEUES 4
#define IEEE80211_ADDR_LEN 6
#define FRAME_DESC_SZ 16
#define MIN_802_11_HDR_LEN 24
#define RSI_DEF_KEEPALIVE 90
#define RSI_WOW_KEEPALIVE 5
#define RSI_BCN_MISS_THRESHOLD 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 6
#define MAX_HW_QUEUES 12
#define INVALID_QUEUE 0xff
#define MAX_CONTINUOUS_VO_PKTS 8
#define MAX_CONTINUOUS_VI_PKTS 4
/* Hardware queue info */
#define BROADCAST_HW_Q 9
#define MGMT_HW_Q 10
#define BEACON_HW_Q 11
#define IEEE80211_MGMT_FRAME 0x00
#define IEEE80211_CTL_FRAME 0x04
#define RSI_MAX_ASSOC_STAS 32
#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)
/* WoWLAN flags */
#define RSI_WOW_ENABLED BIT(0)
#define RSI_WOW_NO_CONNECTION BIT(1)
#define RSI_MAX_RX_PKTS 64
enum rsi_dev_model {
RSI_DEV_9113 = 0,
RSI_DEV_9116
};
struct version_info {
u16 major;
u16 minor;
u8 release_num;
u8 patch_num;
union {
struct {
u8 fw_ver[8];
} info;
} ver;
} __packed;
struct skb_info {
s8 rssi;
u32 flags;
u16 channel;
s8 tid;
s8 sta_id;
u8 internal_hdr_size;
struct ieee80211_vif *vif;
u8 vap_id;
bool have_key;
};
enum edca_queue {
BK_Q,
BE_Q,
VI_Q,
VO_Q,
MGMT_SOFT_Q,
MGMT_BEACON_Q
};
struct security_info {
u32 ptk_cipher;
u32 gtk_cipher;
};
struct wmm_qinfo {
s32 weight;
s32 wme_params;
s32 pkt_contended;
s32 txop;
};
struct transmit_q_stats {
u32 total_tx_pkt_send[NUM_EDCA_QUEUES + 2];
u32 total_tx_pkt_freed[NUM_EDCA_QUEUES + 2];
};
#define MAX_BGSCAN_CHANNELS_DUAL_BAND 38
#define MAX_BGSCAN_PROBE_REQ_LEN 0x64
#define RSI_DEF_BGSCAN_THRLD 0x0
#define RSI_DEF_ROAM_THRLD 0xa
#define RSI_BGSCAN_PERIODICITY 0x1e
#define RSI_ACTIVE_SCAN_TIME 0x14
#define RSI_PASSIVE_SCAN_TIME 0x46
#define RSI_CHANNEL_SCAN_TIME 20
struct rsi_bgscan_params {
u16 bgscan_threshold;
u16 roam_threshold;
u16 bgscan_periodicity;
u8 num_bgscan_channels;
u8 two_probe;
u16 active_scan_duration;
u16 passive_scan_duration;
};
struct vif_priv {
bool is_ht;
bool sgi;
u16 seq_start;
int vap_id;
};
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 cqm_info {
s8 last_cqm_event_rssi;
int rssi_thold;
u32 rssi_hyst;
};
enum rsi_dfs_regions {
RSI_REGION_FCC = 0,
RSI_REGION_ETSI,
RSI_REGION_TELEC,
RSI_REGION_WORLD
};
struct rsi_9116_features {
u8 pll_mode;
u8 rf_type;
u8 wireless_mode;
u8 afe_type;
u8 enable_ppe;
u8 dpd;
u32 sifs_tx_enable;
u32 ps_options;
};
struct rsi_rate_config {
u32 configured_mask; /* configured by mac80211 bits 0-11=legacy 12+ mcs */
u16 fixed_hw_rate;
bool fixed_enabled;
};
struct rsi_common {
struct rsi_hw *priv;
struct vif_priv vif_info[RSI_MAX_VIFS];
void *coex_cb;
bool mgmt_q_block;
struct version_info lmac_ver;
struct rsi_thread tx_thread;
struct sk_buff_head tx_queue[NUM_EDCA_QUEUES + 2];
struct completion wlan_init_completion;
/* Mutex declaration */
struct mutex mutex;
/* Mutex used for tx thread */
struct mutex tx_lock;
/* Mutex used for rx thread */
struct mutex rx_lock;
u8 endpoint;
/* Channel/band related */
u8 band;
u8 num_supp_bands;
u8 channel_width;
u16 rts_threshold;
u32 bitrate_mask[RSI_MAX_BANDS];
struct rsi_rate_config rate_config[RSI_MAX_BANDS];
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];
/* WMM algo related */
u8 selected_qnum;
u32 pkt_cnt;
u8 min_weight;
/* bgscan related */
struct cqm_info cqm_info;
bool hw_data_qs_blocked;
u8 driver_mode;
u8 coex_mode;
u16 oper_mode;
u8 lp_ps_handshake_mode;
u8 ulp_ps_handshake_mode;
u8 uapsd_bitmap;
u8 rf_power_val;
u8 wlan_rf_power_mode;
u8 obm_ant_sel_val;
int tx_power;
u8 ant_in_use;
/* Mutex used for writing packet to bus */
struct mutex tx_bus_mutex;
bool hibernate_resume;
bool reinit_hw;
u8 wow_flags;
u16 beacon_interval;
u8 dtim_cnt;
/* AP mode parameters */
u8 beacon_enabled;
u16 beacon_cnt;
struct rsi_sta stations[RSI_MAX_ASSOC_STAS + 1];
int num_stations;
int max_stations;
struct ieee80211_key_conf *key;
/* Wi-Fi direct mode related */
bool p2p_enabled;
struct timer_list roc_timer;
struct ieee80211_vif *roc_vif;
bool eapol4_confirm;
bool bt_defer_attach;
void *bt_adapter;
struct cfg80211_scan_request *hwscan;
struct rsi_bgscan_params bgscan;
struct rsi_9116_features w9116_features;
u8 bgscan_en;
u8 mac_ops_resumed;
};
struct eepromrw_info {
u32 offset;
u32 length;
u8 write;
u16 eeprom_erase;
u8 data[480];
};
struct eeprom_read {
u16 length;
u16 off_set;
};
struct rsi_hw {
struct rsi_common *priv;
enum rsi_dev_model device_model;
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[NUM_NL80211_BANDS];
struct device *device;
u8 sc_nvifs;
enum rsi_host_intf rsi_host_intf;
u16 block_size;
enum ps_state ps_state;
struct rsi_ps_info ps_info;
spinlock_t ps_lock; /*To protect power save config*/
u32 usb_buffer_status_reg;
#ifdef CONFIG_RSI_DEBUGFS
struct rsi_debugfs *dfsentry;
u8 num_debugfs_entries;
#endif
char *fw_file_name;
struct timer_list bl_cmd_timer;
bool blcmd_timer_expired;
u32 flash_capacity;
struct eepromrw_info eeprom;
u32 interrupt_status;
u8 dfs_region;
char country[2];
void *rsi_dev;
struct rsi_host_intf_ops *host_intf_ops;
int (*check_hw_queue_status)(struct rsi_hw *adapter, u8 q_num);
int (*determine_event_timeout)(struct rsi_hw *adapter);
};
void rsi_print_version(struct rsi_common *common);
struct rsi_host_intf_ops {
int (*read_pkt)(struct rsi_hw *adapter, u8 *pkt, u32 len);
int (*write_pkt)(struct rsi_hw *adapter, u8 *pkt, u32 len);
int (*master_access_msword)(struct rsi_hw *adapter, u16 ms_word);
int (*read_reg_multiple)(struct rsi_hw *adapter, u32 addr,
u8 *data, u16 count);
int (*write_reg_multiple)(struct rsi_hw *adapter, u32 addr,
u8 *data, u16 count);
int (*master_reg_read)(struct rsi_hw *adapter, u32 addr,
u32 *read_buf, u16 size);
int (*master_reg_write)(struct rsi_hw *adapter,
unsigned long addr, unsigned long data,
u16 size);
int (*load_data_master_write)(struct rsi_hw *adapter, u32 addr,
u32 instructions_size, u16 block_size,
u8 *fw);
int (*reinit_device)(struct rsi_hw *adapter);
int (*ta_reset)(struct rsi_hw *adapter);
};
enum rsi_host_intf rsi_get_host_intf(void *priv);
void rsi_set_bt_context(void *priv, void *bt_context);
void rsi_attach_bt(struct rsi_common *common);
#endif
@@ -0,0 +1,758 @@
/*
* 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 84
#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 EAPOL4_PACKET_LEN 0x85
#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
#define WLAN_EEPROM_RFTYPE_ADDR 424
/*WOWLAN RESUME WAKEUP TYPES*/
#define RSI_UNICAST_MAGIC_PKT BIT(0)
#define RSI_BROADCAST_MAGICPKT BIT(1)
#define RSI_EAPOL_PKT BIT(2)
#define RSI_DISCONNECT_PKT BIT(3)
#define RSI_HW_BMISS_PKT BIT(4)
#define RSI_INSERT_SEQ_IN_FW BIT(2)
#define WOW_MAX_FILTERS_PER_LIST 16
#define WOW_PATTERN_SIZE 256
/* Receive Frame Types */
#define RSI_RX_DESC_MSG_TYPE_OFFSET 2
#define TA_CONFIRM_TYPE 0x01
#define RX_DOT11_MGMT 0x02
#define TX_STATUS_IND 0x04
#define BEACON_EVENT_IND 0x08
#define EAPOL4_CONFIRM 1
#define PROBEREQ_CONFIRM 2
#define CARD_READY_IND 0x00
#define SLEEP_NOTIFY_IND 0x06
#define RSI_TX_STATUS_TYPE 15
#define RSI_TX_STATUS 12
#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 MORE_DATA_PRESENT BIT(1)
#define RSI_BROADCAST_PKT BIT(9)
#define RSI_DESC_REQUIRE_CFM_TO_HOST BIT(2)
#define RSI_ADD_DELTA_TSF_VAP_ID BIT(3)
#define RSI_FETCH_RETRY_CNT_FRM_HST BIT(4)
#define RSI_QOS_ENABLE BIT(12)
#define RSI_REKEY_PURPOSE BIT(13)
#define RSI_ENCRYPT_PKT BIT(15)
#define RSI_SET_PS_ENABLE BIT(12)
#define RSI_CMDDESC_40MHZ BIT(4)
#define RSI_CMDDESC_UPPER_20_ENABLE BIT(5)
#define RSI_CMDDESC_LOWER_20_ENABLE BIT(6)
#define RSI_CMDDESC_FULL_40_ENABLE (BIT(5) | BIT(6))
#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 ENABLE_SHORTGI_RATE BIT(9)
#define RX_BA_INDICATION 1
#define RSI_TBL_SZ 40
#define MAX_RETRIES 8
#define RSI_IFTYPE_STATION 0
#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 RSI_RATE_AUTO 0xffff
#define BW_20MHZ 0
#define BW_40MHZ 1
#define EP_2GHZ_20MHZ 0
#define EP_2GHZ_40MHZ 1
#define EP_5GHZ_20MHZ 2
#define EP_5GHZ_40MHZ 3
#define SIFS_TX_11N_VALUE 580
#define SIFS_TX_11B_VALUE 346
#define SHORT_SLOT_VALUE 360
#define LONG_SLOT_VALUE 640
#define OFDM_ACK_TOUT_VALUE 2720
#define CCK_ACK_TOUT_VALUE 9440
#define LONG_PREAMBLE 0x0000
#define SHORT_PREAMBLE 0x0001
#define RSI_SUPP_FILTERS (FIF_ALLMULTI | FIF_PROBE_REQ |\
FIF_BCN_PRBRESP_PROMISC)
#define ANTENNA_SEL_INT 0x02 /* RF_OUT_2 / Integerated */
#define ANTENNA_SEL_UFL 0x03 /* RF_OUT_1 / U.FL */
#define ANTENNA_MASK_VALUE 0x00ff
#define ANTENNA_SEL_TYPE 1
/* Rx filter word definitions */
#define PROMISCOUS_MODE BIT(0)
#define ALLOW_DATA_ASSOC_PEER BIT(1)
#define ALLOW_MGMT_ASSOC_PEER BIT(2)
#define ALLOW_CTRL_ASSOC_PEER BIT(3)
#define DISALLOW_BEACONS BIT(4)
#define ALLOW_CONN_PEER_MGMT_WHILE_BUF_FULL BIT(5)
#define DISALLOW_BROADCAST_DATA BIT(6)
#define RSI_MPDU_DENSITY 0x8
#define RSI_CHAN_RADAR BIT(7)
#define RSI_BEACON_INTERVAL 200
#define RSI_DTIM_COUNT 2
#define RSI_PS_DISABLE_IND BIT(15)
#define RSI_PS_ENABLE 1
#define RSI_PS_DISABLE 0
#define RSI_DEEP_SLEEP 1
#define RSI_CONNECTED_SLEEP 2
#define RSI_SLEEP_REQUEST 1
#define RSI_WAKEUP_REQUEST 2
#define RSI_IEEE80211_UAPSD_QUEUES \
(IEEE80211_WMM_IE_STA_QOSINFO_AC_VO | \
IEEE80211_WMM_IE_STA_QOSINFO_AC_VI | \
IEEE80211_WMM_IE_STA_QOSINFO_AC_BE | \
IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)
#define RSI_DESC_VAP_ID_MASK 0xC000u
#define RSI_DESC_VAP_ID_OFST 14
#define RSI_DATA_DESC_MAC_BBP_INFO BIT(0)
#define RSI_DATA_DESC_NO_ACK_IND BIT(9)
#define RSI_DATA_DESC_QOS_EN BIT(12)
#define RSI_DATA_DESC_NORMAL_FRAME 0x00
#define RSI_DATA_DESC_DTIM_BEACON_GATED_FRAME BIT(10)
#define RSI_DATA_DESC_BEACON_FRAME BIT(11)
#define RSI_DATA_DESC_DTIM_BEACON (BIT(10) | BIT(11))
#define RSI_DATA_DESC_INSERT_TSF BIT(15)
#define RSI_DATA_DESC_INSERT_SEQ_NO BIT(2)
#ifdef CONFIG_PM
#define RSI_WOW_ANY BIT(1)
#define RSI_WOW_GTK_REKEY BIT(3)
#define RSI_WOW_MAGIC_PKT BIT(4)
#define RSI_WOW_DISCONNECT BIT(5)
#endif
#define RSI_MAX_TX_AGGR_FRMS 8
#define RSI_MAX_RX_AGGR_FRMS 8
#define RSI_MAX_SCAN_SSIDS 16
#define RSI_MAX_SCAN_IE_LEN 256
enum opmode {
RSI_OPMODE_UNSUPPORTED = -1,
RSI_OPMODE_AP = 0,
RSI_OPMODE_STA,
RSI_OPMODE_P2P_GO,
RSI_OPMODE_P2P_CLIENT
};
enum vap_status {
VAP_ADD = 1,
VAP_DELETE = 2,
VAP_UPDATE = 3
};
enum peer_type {
PEER_TYPE_AP,
PEER_TYPE_STA,
};
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_HW_QUEUE,
SET_KEY_REQ,
AUTO_RATE_IND,
BOOTUP_PARAMS_REQUEST,
VAP_CAPABILITIES,
EEPROM_READ,
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,
ANT_SEL_FRAME = 0x20,
VAP_DYNAMIC_UPDATE = 0x27,
COMMON_DEV_CONFIG = 0x28,
RADIO_PARAMS_UPDATE = 0x29,
WOWLAN_CONFIG_PARAMS = 0x2B,
FEATURES_ENABLE = 0x33,
WOWLAN_WAKEUP_REASON = 0xc5
};
struct rsi_mac_frame {
__le16 desc_word[8];
} __packed;
#define PWR_SAVE_WAKEUP_IND BIT(0)
#define TCP_CHECK_SUM_OFFLOAD BIT(1)
#define CONFIRM_REQUIRED_TO_HOST BIT(2)
#define ADD_DELTA_TSF BIT(3)
#define FETCH_RETRY_CNT_FROM_HOST_DESC BIT(4)
#define EOSP_INDICATION BIT(5)
#define REQUIRE_TSF_SYNC_CONFIRM BIT(6)
#define ENCAP_MGMT_PKT BIT(7)
#define DESC_IMMEDIATE_WAKEUP BIT(15)
struct rsi_xtended_desc {
u8 confirm_frame_type;
u8 retry_cnt;
u16 reserved;
};
struct rsi_cmd_desc_dword0 {
__le16 len_qno;
u8 frame_type;
u8 misc_flags;
};
struct rsi_cmd_desc_dword1 {
u8 xtend_desc_size;
u8 reserved1;
__le16 reserved2;
};
struct rsi_cmd_desc_dword2 {
__le32 pkt_info; /* Packet specific data */
};
struct rsi_cmd_desc_dword3 {
__le16 token;
u8 qid_tid;
u8 sta_id;
};
struct rsi_cmd_desc {
struct rsi_cmd_desc_dword0 desc_dword0;
struct rsi_cmd_desc_dword1 desc_dword1;
struct rsi_cmd_desc_dword2 desc_dword2;
struct rsi_cmd_desc_dword3 desc_dword3;
};
struct rsi_boot_params {
__le16 desc_word[8];
struct bootup_params bootup_params;
} __packed;
struct rsi_boot_params_9116 {
struct rsi_cmd_desc_dword0 desc_dword0;
struct rsi_cmd_desc_dword1 desc_dword1;
struct rsi_cmd_desc_dword2 desc_dword2;
__le16 reserved;
__le16 umac_clk;
struct bootup_params_9116 bootup_params;
} __packed;
struct rsi_peer_notify {
struct rsi_cmd_desc desc;
u8 mac_addr[6];
__le16 command;
__le16 mpdu_density;
__le16 reserved;
__le32 sta_flags;
} __packed;
/* Aggregation params flags */
#define RSI_AGGR_PARAMS_TID_MASK 0xf
#define RSI_AGGR_PARAMS_START BIT(4)
#define RSI_AGGR_PARAMS_RX_AGGR BIT(5)
struct rsi_aggr_params {
struct rsi_cmd_desc_dword0 desc_dword0;
struct rsi_cmd_desc_dword0 desc_dword1;
__le16 seq_start;
__le16 baw_size;
__le16 token;
u8 aggr_params;
u8 peer_id;
} __packed;
struct rsi_bb_rf_prog {
struct rsi_cmd_desc_dword0 desc_dword0;
__le16 reserved1;
u8 rf_power_mode;
u8 reserved2;
u8 endpoint;
u8 reserved3;
__le16 reserved4;
__le16 reserved5;
__le16 flags;
} __packed;
struct rsi_chan_config {
struct rsi_cmd_desc_dword0 desc_dword0;
struct rsi_cmd_desc_dword1 desc_dword1;
u8 channel_number;
u8 antenna_gain_offset_2g;
u8 antenna_gain_offset_5g;
u8 channel_width;
__le16 tx_power;
u8 region_rftype;
u8 flags;
} __packed;
struct rsi_vap_caps {
struct rsi_cmd_desc_dword0 desc_dword0;
u8 reserved1;
u8 status;
__le16 reserved2;
u8 vif_type;
u8 channel_bw;
__le16 antenna_info;
__le16 token;
u8 radioid_macid;
u8 vap_id;
u8 mac_addr[6];
__le16 keep_alive_period;
u8 bssid[6];
__le16 reserved4;
__le32 flags;
__le16 frag_threshold;
__le16 rts_threshold;
__le32 default_mgmt_rate;
__le16 default_ctrl_rate;
__le16 ctrl_rate_flags;
__le32 default_data_rate;
__le16 beacon_interval;
__le16 dtim_period;
__le16 beacon_miss_threshold;
} __packed;
struct rsi_ant_sel_frame {
struct rsi_cmd_desc_dword0 desc_dword0;
u8 reserved;
u8 sub_frame_type;
__le16 ant_value;
__le32 reserved1;
__le32 reserved2;
} __packed;
struct rsi_dynamic_s {
struct rsi_cmd_desc_dword0 desc_dword0;
struct rsi_cmd_desc_dword1 desc_dword1;
struct rsi_cmd_desc_dword2 desc_dword2;
struct rsi_cmd_desc_dword3 desc_dword3;
struct framebody {
__le16 data_rate;
__le16 mgmt_rate;
__le16 keep_alive_period;
} frame_body;
} __packed;
/* Key descriptor flags */
#define RSI_KEY_TYPE_BROADCAST BIT(1)
#define RSI_WEP_KEY BIT(2)
#define RSI_WEP_KEY_104 BIT(3)
#define RSI_CIPHER_WPA BIT(4)
#define RSI_CIPHER_TKIP BIT(5)
#define RSI_KEY_MODE_AP BIT(7)
#define RSI_PROTECT_DATA_FRAMES BIT(13)
#define RSI_KEY_ID_MASK 0xC0
#define RSI_KEY_ID_OFFSET 14
struct rsi_set_key {
struct rsi_cmd_desc_dword0 desc_dword0;
struct rsi_cmd_desc_dword1 desc_dword1;
__le16 key_desc;
__le32 bpn;
u8 sta_id;
u8 vap_id;
u8 key[4][32];
u8 tx_mic_key[8];
u8 rx_mic_key[8];
} __packed;
struct rsi_auto_rate {
struct rsi_cmd_desc desc;
__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;
#define QUIET_INFO_VALID BIT(0)
#define QUIET_ENABLE BIT(1)
struct rsi_block_unblock_data {
struct rsi_cmd_desc_dword0 desc_dword0;
u8 xtend_desc_size;
u8 host_quiet_info;
__le16 reserved;
__le16 block_q_bitmap;
__le16 unblock_q_bitmap;
__le16 token;
__le16 flush_q_bitmap;
} __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 {
struct rsi_cmd_desc_dword0 desc_dword0;
struct rsi_cmd_desc_dword0 desc_dword1;
u8 channel_num;
u8 rf_model;
__le16 ppe_ack_rate;
__le16 mode_11j;
u8 radio_cfg_info;
u8 radio_info;
struct qos_params qos_params[MAX_HW_QUEUES];
u8 num_11n_rates;
u8 num_11ac_rates;
__le16 gcpd_per_rate[20];
__le16 sifs_tx_11n;
__le16 sifs_tx_11b;
__le16 slot_rx_11n;
__le16 ofdm_ack_tout;
__le16 cck_ack_tout;
__le16 preamble_type;
} __packed;
/* ULP GPIO flags */
#define RSI_GPIO_MOTION_SENSOR_ULP_WAKEUP BIT(0)
#define RSI_GPIO_SLEEP_IND_FROM_DEVICE BIT(1)
#define RSI_GPIO_2_ULP BIT(2)
#define RSI_GPIO_PUSH_BUTTON_ULP_WAKEUP BIT(3)
/* SOC GPIO flags */
#define RSI_GPIO_0_PSPI_CSN_0 BIT(0)
#define RSI_GPIO_1_PSPI_CSN_1 BIT(1)
#define RSI_GPIO_2_HOST_WAKEUP_INTR BIT(2)
#define RSI_GPIO_3_PSPI_DATA_0 BIT(3)
#define RSI_GPIO_4_PSPI_DATA_1 BIT(4)
#define RSI_GPIO_5_PSPI_DATA_2 BIT(5)
#define RSI_GPIO_6_PSPI_DATA_3 BIT(6)
#define RSI_GPIO_7_I2C_SCL BIT(7)
#define RSI_GPIO_8_I2C_SDA BIT(8)
#define RSI_GPIO_9_UART1_RX BIT(9)
#define RSI_GPIO_10_UART1_TX BIT(10)
#define RSI_GPIO_11_UART1_RTS_I2S_CLK BIT(11)
#define RSI_GPIO_12_UART1_CTS_I2S_WS BIT(12)
#define RSI_GPIO_13_DBG_UART_RX_I2S_DIN BIT(13)
#define RSI_GPIO_14_DBG_UART_RX_I2S_DOUT BIT(14)
#define RSI_GPIO_15_LP_WAKEUP_BOOT_BYPASS BIT(15)
#define RSI_GPIO_16_LED_0 BIT(16)
#define RSI_GPIO_17_BTCOEX_WLAN_ACT_EXT_ANT_SEL BIT(17)
#define RSI_GPIO_18_BTCOEX_BT_PRIO_EXT_ANT_SEL BIT(18)
#define RSI_GPIO_19_BTCOEX_BT_ACT_EXT_ON_OFF BIT(19)
#define RSI_GPIO_20_RF_RESET BIT(20)
#define RSI_GPIO_21_SLEEP_IND_FROM_DEVICE BIT(21)
#define RSI_UNUSED_SOC_GPIO_BITMAP (RSI_GPIO_9_UART1_RX | \
RSI_GPIO_10_UART1_TX | \
RSI_GPIO_11_UART1_RTS_I2S_CLK | \
RSI_GPIO_12_UART1_CTS_I2S_WS | \
RSI_GPIO_13_DBG_UART_RX_I2S_DIN | \
RSI_GPIO_14_DBG_UART_RX_I2S_DOUT | \
RSI_GPIO_15_LP_WAKEUP_BOOT_BYPASS | \
RSI_GPIO_17_BTCOEX_WLAN_ACT_EXT_ANT_SEL | \
RSI_GPIO_18_BTCOEX_BT_PRIO_EXT_ANT_SEL | \
RSI_GPIO_19_BTCOEX_BT_ACT_EXT_ON_OFF | \
RSI_GPIO_21_SLEEP_IND_FROM_DEVICE)
#define RSI_UNUSED_ULP_GPIO_BITMAP (RSI_GPIO_MOTION_SENSOR_ULP_WAKEUP | \
RSI_GPIO_SLEEP_IND_FROM_DEVICE | \
RSI_GPIO_2_ULP | \
RSI_GPIO_PUSH_BUTTON_ULP_WAKEUP);
struct rsi_config_vals {
__le16 len_qno;
u8 pkt_type;
u8 misc_flags;
__le16 reserved1[6];
u8 lp_ps_handshake;
u8 ulp_ps_handshake;
u8 sleep_config_params; /* 0 for no handshake,
* 1 for GPIO based handshake,
* 2 packet handshake
*/
u8 unused_ulp_gpio;
__le32 unused_soc_gpio_bitmap;
u8 ext_pa_or_bt_coex_en;
u8 opermode;
u8 wlan_rf_pwr_mode;
u8 bt_rf_pwr_mode;
u8 zigbee_rf_pwr_mode;
u8 driver_mode;
u8 region_code;
u8 antenna_sel_val;
u8 reserved2[16];
} __packed;
/* Packet info flags */
#define RSI_EEPROM_HDR_SIZE_OFFSET 8
#define RSI_EEPROM_HDR_SIZE_MASK 0x300
#define RSI_EEPROM_LEN_OFFSET 20
#define RSI_EEPROM_LEN_MASK 0xFFF00000
struct rsi_eeprom_read_frame {
__le16 len_qno;
u8 pkt_type;
u8 misc_flags;
__le32 pkt_info;
__le32 eeprom_offset;
__le16 delay_ms;
__le16 reserved3;
} __packed;
struct rsi_request_ps {
struct rsi_cmd_desc desc;
struct ps_sleep_params ps_sleep;
u8 ps_mimic_support;
u8 ps_uapsd_acs;
u8 ps_uapsd_wakeup_period;
u8 reserved;
__le32 ps_listen_interval;
__le32 ps_dtim_interval_duration;
__le16 ps_num_dtim_intervals;
} __packed;
struct rsi_wowlan_req {
struct rsi_cmd_desc desc;
u8 sourceid[ETH_ALEN];
u16 wow_flags;
u16 host_sleep_status;
} __packed;
#define RSI_START_BGSCAN 1
#define RSI_STOP_BGSCAN 0
#define HOST_BG_SCAN_TRIG BIT(4)
struct rsi_bgscan_config {
struct rsi_cmd_desc_dword0 desc_dword0;
__le64 reserved;
__le32 reserved1;
__le16 bgscan_threshold;
__le16 roam_threshold;
__le16 bgscan_periodicity;
u8 num_bgscan_channels;
u8 two_probe;
__le16 active_scan_duration;
__le16 passive_scan_duration;
__le16 channels2scan[MAX_BGSCAN_CHANNELS_DUAL_BAND];
} __packed;
struct rsi_bgscan_probe {
struct rsi_cmd_desc_dword0 desc_dword0;
__le64 reserved;
__le32 reserved1;
__le16 mgmt_rate;
__le16 flags;
__le16 def_chan;
__le16 channel_scan_time;
__le16 probe_req_length;
} __packed;
#define RSI_DUTY_CYCLING BIT(0)
#define RSI_END_OF_FRAME BIT(1)
#define RSI_SIFS_TX_ENABLE BIT(2)
#define RSI_DPD BIT(3)
struct rsi_wlan_9116_features {
struct rsi_cmd_desc desc;
u8 pll_mode;
u8 rf_type;
u8 wireless_mode;
u8 enable_ppe;
u8 afe_type;
u8 reserved1;
__le16 reserved2;
__le32 feature_enable;
};
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);
}
static inline void rsi_set_len_qno(__le16 *addr, u16 len, u8 qno)
{
*addr = cpu_to_le16(len | ((qno & 7) << 12));
}
int rsi_handle_card_ready(struct rsi_common *common, u8 *msg);
int rsi_mgmt_pkt_recv(struct rsi_common *common, u8 *msg);
int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode,
u8 *mac_addr, u8 vap_id, u8 vap_status);
int rsi_send_aggregation_params_frame(struct rsi_common *common, u16 tid,
u16 ssn, u8 buf_size, u8 event,
u8 sta_id);
int rsi_hal_load_key(struct rsi_common *common, u8 *data, u16 key_len,
u8 key_type, u8 key_id, u32 cipher, s16 sta_id,
struct ieee80211_vif *vif);
int rsi_set_channel(struct rsi_common *common,
struct ieee80211_channel *channel);
int rsi_send_vap_dynamic_update(struct rsi_common *common);
int rsi_send_block_unblock_frame(struct rsi_common *common, bool event);
int rsi_hal_send_sta_notify_frame(struct rsi_common *common, enum opmode opmode,
u8 notify_event, const unsigned char *bssid,
u8 qos_enable, u16 aid, u16 sta_id,
struct ieee80211_vif *vif);
void rsi_inform_bss_status(struct rsi_common *common, enum opmode opmode,
u8 status, const u8 *addr, u8 qos_enable, u16 aid,
struct ieee80211_sta *sta, u16 sta_id,
u16 assoc_cap, struct ieee80211_vif *vif);
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);
int rsi_band_check(struct rsi_common *common, struct ieee80211_channel *chan);
int rsi_send_rx_filter_frame(struct rsi_common *common, u16 rx_filter_word);
int rsi_send_radio_params_update(struct rsi_common *common);
int rsi_set_antenna(struct rsi_common *common, u8 antenna);
#ifdef CONFIG_PM
int rsi_send_wowlan_request(struct rsi_common *common, u16 flags,
u16 sleep_status);
#endif
int rsi_send_ps_request(struct rsi_hw *adapter, bool enable,
struct ieee80211_vif *vif);
void init_bgscan_params(struct rsi_common *common);
int rsi_send_bgscan_params(struct rsi_common *common, int enable);
int rsi_send_bgscan_probe_req(struct rsi_common *common,
struct ieee80211_vif *vif);
#endif
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2017 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_PS_H__
#define __RSI_PS_H__
#define PS_CONFIRM_INDEX 12
#define RSI_DEF_DS_WAKEUP_PERIOD 200
#define RSI_DEF_LISTEN_INTERVAL 200
#define RSI_SLEEP_TYPE_LP 1
enum ps_state {
PS_NONE = 0,
PS_ENABLE_REQ_SENT = 1,
PS_DISABLE_REQ_SENT = 2,
PS_ENABLED = 3
};
struct ps_sleep_params {
u8 enable;
u8 sleep_type;
u8 connected_sleep;
u8 reserved1;
__le16 num_bcns_per_lis_int;
__le16 wakeup_type;
__le32 sleep_duration;
} __packed;
struct rsi_ps_info {
u8 enabled;
u8 sleep_type;
u8 tx_threshold;
u8 rx_threshold;
u8 tx_hysterisis;
u8 rx_hysterisis;
u16 monitor_interval;
u32 listen_interval;
u16 num_bcns_per_lis_int;
u32 dtim_interval_duration;
u16 num_dtims_per_sleep;
u32 deep_sleep_wakeup_period;
} __packed;
char *str_psstate(enum ps_state state);
void rsi_enable_ps(struct rsi_hw *adapter, struct ieee80211_vif *vif);
void rsi_disable_ps(struct rsi_hw *adapter, struct ieee80211_vif *vif);
int rsi_handle_ps_confirm(struct rsi_hw *adapter, u8 *msg);
void rsi_default_ps_params(struct rsi_hw *hw);
void rsi_conf_uapsd(struct rsi_hw *adapter, struct ieee80211_vif *vif);
#endif
@@ -0,0 +1,138 @@
/*
* @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 = 0x2,
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
#define RECV_NUM_BLOCKS 4
/* Interrupt Bit Related Macros */
#define PKT_BUFF_AVAILABLE 1
#define FW_ASSERT_IND 2
#define RSI_MASTER_REG_BUF_SIZE 12
#define RSI_DEVICE_BUFFER_STATUS_REGISTER 0xf3
#define RSI_FN1_INT_REGISTER 0xf9
#define RSI_INT_ENABLE_REGISTER 0x04
#define RSI_INT_ENABLE_MASK 0xfc
#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_WAKEUP_REG 0x000FF
#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 0xF
#define TA_RELEASE_THREAD_VALUE 0xF
#define TA_BASE_ADDR 0x2200
#define MISC_CFG_BASE_ADDR 0x4105
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_available_counter;
};
struct rsi_91x_sdiodev {
struct sdio_func *pfunction;
struct task_struct *sdio_irq_task;
struct receive_info rx_info;
u32 next_read_delay;
u32 sdio_high_speed_enable;
u8 sdio_clock_speed;
u32 cardcapability;
u8 prev_desc[16];
u16 tx_blk_size;
u8 write_fail;
bool buff_status_updated;
struct rsi_thread rx_thread;
u8 pktbuffer[8192] __aligned(4);
};
int rsi_init_sdio_slave_regs(struct rsi_hw *adapter);
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, u16 count);
int rsi_sdio_master_access_msword(struct rsi_hw *adapter, u16 ms_word);
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_check_buffer_status(struct rsi_hw *adapter, u8 q_num);
void rsi_sdio_rx_thread(struct rsi_common *common);
#endif
@@ -0,0 +1,85 @@
/*
* @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 RSI_USB_VENDOR_ID 0x1618
#define RSI_USB_PID_9113 0x9113
#define RSI_USB_PID_9116 0x9116
#define USB_INTERNAL_REG_1 0x25000
#define RSI_USB_READY_MAGIC_NUM 0xab
#define FW_STATUS_REG 0x41050012
#define RSI_TA_HOLD_REG 0x22000844
#define RSI_FW_WDT_DISABLE_REQ 0x69
#define USB_VENDOR_REGISTER_READ 0x15
#define USB_VENDOR_REGISTER_WRITE 0x16
#define RSI_USB_TX_HEAD_ROOM 128
#define MAX_RX_URBS 2
#define MAX_BULK_EP 8
#define WLAN_EP 1
#define BT_EP 2
#define RSI_USB_BUF_SIZE 4096
#define RSI_USB_CTRL_BUF_SIZE 0x04
#define RSI_MAX_RX_USB_PKT_SIZE 3000
struct rx_usb_ctrl_block {
u8 *data;
struct urb *rx_urb;
struct sk_buff *rx_skb;
u8 ep_num;
};
struct rsi_91x_usbdev {
void *priv;
struct rsi_thread rx_thread;
u8 endpoint;
struct usb_device *usbdev;
struct usb_interface *pfunction;
struct rx_usb_ctrl_block rx_cb[MAX_RX_URBS];
u8 *tx_buffer;
__le16 bulkin_size[MAX_BULK_EP];
u8 bulkin_endpoint_addr[MAX_BULK_EP];
__le16 bulkout_size[MAX_BULK_EP];
u8 bulkout_endpoint_addr[MAX_BULK_EP];
u32 tx_blk_size;
u8 write_fail;
struct sk_buff_head rx_q;
};
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;
}
void rsi_usb_rx_thread(struct rsi_common *common);
#endif