17a03124cSLee Jones /*
22108df3cSPrameela Rani Garnepudi * Copyright (c) 2018 Redpine Signals Inc.
32108df3cSPrameela Rani Garnepudi *
42108df3cSPrameela Rani Garnepudi * Permission to use, copy, modify, and/or distribute this software for any
52108df3cSPrameela Rani Garnepudi * purpose with or without fee is hereby granted, provided that the above
62108df3cSPrameela Rani Garnepudi * copyright notice and this permission notice appear in all copies.
72108df3cSPrameela Rani Garnepudi *
82108df3cSPrameela Rani Garnepudi * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
92108df3cSPrameela Rani Garnepudi * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
102108df3cSPrameela Rani Garnepudi * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
112108df3cSPrameela Rani Garnepudi * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
122108df3cSPrameela Rani Garnepudi * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
132108df3cSPrameela Rani Garnepudi * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
142108df3cSPrameela Rani Garnepudi * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
152108df3cSPrameela Rani Garnepudi */
162108df3cSPrameela Rani Garnepudi
172108df3cSPrameela Rani Garnepudi #include "rsi_main.h"
182108df3cSPrameela Rani Garnepudi #include "rsi_coex.h"
192108df3cSPrameela Rani Garnepudi #include "rsi_mgmt.h"
202108df3cSPrameela Rani Garnepudi #include "rsi_hal.h"
212108df3cSPrameela Rani Garnepudi
rsi_coex_determine_coex_q(struct rsi_coex_ctrl_block * coex_cb)222108df3cSPrameela Rani Garnepudi static enum rsi_coex_queues rsi_coex_determine_coex_q
232108df3cSPrameela Rani Garnepudi (struct rsi_coex_ctrl_block *coex_cb)
242108df3cSPrameela Rani Garnepudi {
252108df3cSPrameela Rani Garnepudi enum rsi_coex_queues q_num = RSI_COEX_Q_INVALID;
262108df3cSPrameela Rani Garnepudi
272108df3cSPrameela Rani Garnepudi if (skb_queue_len(&coex_cb->coex_tx_qs[RSI_COEX_Q_COMMON]) > 0)
282108df3cSPrameela Rani Garnepudi q_num = RSI_COEX_Q_COMMON;
292108df3cSPrameela Rani Garnepudi if (skb_queue_len(&coex_cb->coex_tx_qs[RSI_COEX_Q_BT]) > 0)
302108df3cSPrameela Rani Garnepudi q_num = RSI_COEX_Q_BT;
312108df3cSPrameela Rani Garnepudi if (skb_queue_len(&coex_cb->coex_tx_qs[RSI_COEX_Q_WLAN]) > 0)
322108df3cSPrameela Rani Garnepudi q_num = RSI_COEX_Q_WLAN;
332108df3cSPrameela Rani Garnepudi
342108df3cSPrameela Rani Garnepudi return q_num;
352108df3cSPrameela Rani Garnepudi }
362108df3cSPrameela Rani Garnepudi
rsi_coex_sched_tx_pkts(struct rsi_coex_ctrl_block * coex_cb)372108df3cSPrameela Rani Garnepudi static void rsi_coex_sched_tx_pkts(struct rsi_coex_ctrl_block *coex_cb)
382108df3cSPrameela Rani Garnepudi {
392108df3cSPrameela Rani Garnepudi enum rsi_coex_queues coex_q = RSI_COEX_Q_INVALID;
402108df3cSPrameela Rani Garnepudi struct sk_buff *skb;
412108df3cSPrameela Rani Garnepudi
422108df3cSPrameela Rani Garnepudi do {
432108df3cSPrameela Rani Garnepudi coex_q = rsi_coex_determine_coex_q(coex_cb);
442108df3cSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "queue = %d\n", coex_q);
452108df3cSPrameela Rani Garnepudi
46716b840cSSiva Rebbagondla if (coex_q == RSI_COEX_Q_BT) {
472108df3cSPrameela Rani Garnepudi skb = skb_dequeue(&coex_cb->coex_tx_qs[RSI_COEX_Q_BT]);
48716b840cSSiva Rebbagondla rsi_send_bt_pkt(coex_cb->priv, skb);
49716b840cSSiva Rebbagondla }
502108df3cSPrameela Rani Garnepudi } while (coex_q != RSI_COEX_Q_INVALID);
512108df3cSPrameela Rani Garnepudi }
522108df3cSPrameela Rani Garnepudi
rsi_coex_scheduler_thread(struct rsi_common * common)532108df3cSPrameela Rani Garnepudi static void rsi_coex_scheduler_thread(struct rsi_common *common)
542108df3cSPrameela Rani Garnepudi {
55*5f48e916SWu Yunchuan struct rsi_coex_ctrl_block *coex_cb = common->coex_cb;
562108df3cSPrameela Rani Garnepudi u32 timeout = EVENT_WAIT_FOREVER;
572108df3cSPrameela Rani Garnepudi
582108df3cSPrameela Rani Garnepudi do {
592108df3cSPrameela Rani Garnepudi rsi_wait_event(&coex_cb->coex_tx_thread.event, timeout);
602108df3cSPrameela Rani Garnepudi rsi_reset_event(&coex_cb->coex_tx_thread.event);
612108df3cSPrameela Rani Garnepudi
622108df3cSPrameela Rani Garnepudi rsi_coex_sched_tx_pkts(coex_cb);
632108df3cSPrameela Rani Garnepudi } while (atomic_read(&coex_cb->coex_tx_thread.thread_done) == 0);
642108df3cSPrameela Rani Garnepudi
65cead1855SEric W. Biederman kthread_complete_and_exit(&coex_cb->coex_tx_thread.completion, 0);
662108df3cSPrameela Rani Garnepudi }
672108df3cSPrameela Rani Garnepudi
rsi_coex_recv_pkt(struct rsi_common * common,u8 * msg)682108df3cSPrameela Rani Garnepudi int rsi_coex_recv_pkt(struct rsi_common *common, u8 *msg)
692108df3cSPrameela Rani Garnepudi {
702108df3cSPrameela Rani Garnepudi u8 msg_type = msg[RSI_RX_DESC_MSG_TYPE_OFFSET];
712108df3cSPrameela Rani Garnepudi
722108df3cSPrameela Rani Garnepudi switch (msg_type) {
732108df3cSPrameela Rani Garnepudi case COMMON_CARD_READY_IND:
742108df3cSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "common card ready received\n");
75d76f8513SSiva Rebbagondla common->hibernate_resume = false;
762108df3cSPrameela Rani Garnepudi rsi_handle_card_ready(common, msg);
772108df3cSPrameela Rani Garnepudi break;
782108df3cSPrameela Rani Garnepudi case SLEEP_NOTIFY_IND:
792108df3cSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "sleep notify received\n");
802108df3cSPrameela Rani Garnepudi rsi_mgmt_pkt_recv(common, msg);
812108df3cSPrameela Rani Garnepudi break;
822108df3cSPrameela Rani Garnepudi }
832108df3cSPrameela Rani Garnepudi
842108df3cSPrameela Rani Garnepudi return 0;
852108df3cSPrameela Rani Garnepudi }
862108df3cSPrameela Rani Garnepudi
rsi_map_coex_q(u8 hal_queue)872108df3cSPrameela Rani Garnepudi static inline int rsi_map_coex_q(u8 hal_queue)
882108df3cSPrameela Rani Garnepudi {
892108df3cSPrameela Rani Garnepudi switch (hal_queue) {
902108df3cSPrameela Rani Garnepudi case RSI_COEX_Q:
912108df3cSPrameela Rani Garnepudi return RSI_COEX_Q_COMMON;
922108df3cSPrameela Rani Garnepudi case RSI_WLAN_Q:
932108df3cSPrameela Rani Garnepudi return RSI_COEX_Q_WLAN;
942108df3cSPrameela Rani Garnepudi case RSI_BT_Q:
952108df3cSPrameela Rani Garnepudi return RSI_COEX_Q_BT;
962108df3cSPrameela Rani Garnepudi }
972108df3cSPrameela Rani Garnepudi return RSI_COEX_Q_INVALID;
982108df3cSPrameela Rani Garnepudi }
992108df3cSPrameela Rani Garnepudi
rsi_coex_send_pkt(void * priv,struct sk_buff * skb,u8 hal_queue)1002108df3cSPrameela Rani Garnepudi int rsi_coex_send_pkt(void *priv, struct sk_buff *skb, u8 hal_queue)
1012108df3cSPrameela Rani Garnepudi {
102*5f48e916SWu Yunchuan struct rsi_common *common = priv;
103*5f48e916SWu Yunchuan struct rsi_coex_ctrl_block *coex_cb = common->coex_cb;
1042108df3cSPrameela Rani Garnepudi struct skb_info *tx_params = NULL;
1052108df3cSPrameela Rani Garnepudi enum rsi_coex_queues coex_q;
1062108df3cSPrameela Rani Garnepudi int status;
1072108df3cSPrameela Rani Garnepudi
1082108df3cSPrameela Rani Garnepudi coex_q = rsi_map_coex_q(hal_queue);
1092108df3cSPrameela Rani Garnepudi if (coex_q == RSI_COEX_Q_INVALID) {
1102108df3cSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "Invalid coex queue\n");
1112108df3cSPrameela Rani Garnepudi return -EINVAL;
1122108df3cSPrameela Rani Garnepudi }
1132108df3cSPrameela Rani Garnepudi if (coex_q != RSI_COEX_Q_COMMON &&
1142108df3cSPrameela Rani Garnepudi coex_q != RSI_COEX_Q_WLAN) {
1152108df3cSPrameela Rani Garnepudi skb_queue_tail(&coex_cb->coex_tx_qs[coex_q], skb);
1162108df3cSPrameela Rani Garnepudi rsi_set_event(&coex_cb->coex_tx_thread.event);
1172108df3cSPrameela Rani Garnepudi return 0;
1182108df3cSPrameela Rani Garnepudi }
1192108df3cSPrameela Rani Garnepudi if (common->iface_down) {
1202108df3cSPrameela Rani Garnepudi tx_params =
1212108df3cSPrameela Rani Garnepudi (struct skb_info *)&IEEE80211_SKB_CB(skb)->driver_data;
1222108df3cSPrameela Rani Garnepudi
1232108df3cSPrameela Rani Garnepudi if (!(tx_params->flags & INTERNAL_MGMT_PKT)) {
1242108df3cSPrameela Rani Garnepudi rsi_indicate_tx_status(common->priv, skb, -EINVAL);
1252108df3cSPrameela Rani Garnepudi return 0;
1262108df3cSPrameela Rani Garnepudi }
1272108df3cSPrameela Rani Garnepudi }
1282108df3cSPrameela Rani Garnepudi
1292108df3cSPrameela Rani Garnepudi /* Send packet to hal */
1302108df3cSPrameela Rani Garnepudi if (skb->priority == MGMT_SOFT_Q)
1312108df3cSPrameela Rani Garnepudi status = rsi_send_mgmt_pkt(common, skb);
1322108df3cSPrameela Rani Garnepudi else
1332108df3cSPrameela Rani Garnepudi status = rsi_send_data_pkt(common, skb);
1342108df3cSPrameela Rani Garnepudi
1352108df3cSPrameela Rani Garnepudi return status;
1362108df3cSPrameela Rani Garnepudi }
1372108df3cSPrameela Rani Garnepudi
rsi_coex_attach(struct rsi_common * common)1382108df3cSPrameela Rani Garnepudi int rsi_coex_attach(struct rsi_common *common)
1392108df3cSPrameela Rani Garnepudi {
1402108df3cSPrameela Rani Garnepudi struct rsi_coex_ctrl_block *coex_cb;
1412108df3cSPrameela Rani Garnepudi int cnt;
1422108df3cSPrameela Rani Garnepudi
1432108df3cSPrameela Rani Garnepudi coex_cb = kzalloc(sizeof(*coex_cb), GFP_KERNEL);
1442108df3cSPrameela Rani Garnepudi if (!coex_cb)
1452108df3cSPrameela Rani Garnepudi return -ENOMEM;
1462108df3cSPrameela Rani Garnepudi
1472108df3cSPrameela Rani Garnepudi common->coex_cb = (void *)coex_cb;
1482108df3cSPrameela Rani Garnepudi coex_cb->priv = common;
1492108df3cSPrameela Rani Garnepudi
1502108df3cSPrameela Rani Garnepudi /* Initialize co-ex queues */
1512108df3cSPrameela Rani Garnepudi for (cnt = 0; cnt < NUM_COEX_TX_QUEUES; cnt++)
1522108df3cSPrameela Rani Garnepudi skb_queue_head_init(&coex_cb->coex_tx_qs[cnt]);
1532108df3cSPrameela Rani Garnepudi rsi_init_event(&coex_cb->coex_tx_thread.event);
1542108df3cSPrameela Rani Garnepudi
1552108df3cSPrameela Rani Garnepudi /* Initialize co-ex thread */
1562108df3cSPrameela Rani Garnepudi if (rsi_create_kthread(common,
1572108df3cSPrameela Rani Garnepudi &coex_cb->coex_tx_thread,
1582108df3cSPrameela Rani Garnepudi rsi_coex_scheduler_thread,
1592108df3cSPrameela Rani Garnepudi "Coex-Tx-Thread")) {
1602108df3cSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "%s: Unable to init tx thrd\n", __func__);
161956fb851SYuan Can kfree(coex_cb);
1622108df3cSPrameela Rani Garnepudi return -EINVAL;
1632108df3cSPrameela Rani Garnepudi }
1642108df3cSPrameela Rani Garnepudi return 0;
1652108df3cSPrameela Rani Garnepudi }
1662108df3cSPrameela Rani Garnepudi
rsi_coex_detach(struct rsi_common * common)1672108df3cSPrameela Rani Garnepudi void rsi_coex_detach(struct rsi_common *common)
1682108df3cSPrameela Rani Garnepudi {
169*5f48e916SWu Yunchuan struct rsi_coex_ctrl_block *coex_cb = common->coex_cb;
1702108df3cSPrameela Rani Garnepudi int cnt;
1712108df3cSPrameela Rani Garnepudi
1722108df3cSPrameela Rani Garnepudi rsi_kill_thread(&coex_cb->coex_tx_thread);
1732108df3cSPrameela Rani Garnepudi
1742108df3cSPrameela Rani Garnepudi for (cnt = 0; cnt < NUM_COEX_TX_QUEUES; cnt++)
1752108df3cSPrameela Rani Garnepudi skb_queue_purge(&coex_cb->coex_tx_qs[cnt]);
1762108df3cSPrameela Rani Garnepudi
1772108df3cSPrameela Rani Garnepudi kfree(coex_cb);
1782108df3cSPrameela Rani Garnepudi }
179