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