1*7a03124cSLee 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 { 552108df3cSPrameela Rani Garnepudi struct rsi_coex_ctrl_block *coex_cb = 562108df3cSPrameela Rani Garnepudi (struct rsi_coex_ctrl_block *)common->coex_cb; 572108df3cSPrameela Rani Garnepudi u32 timeout = EVENT_WAIT_FOREVER; 582108df3cSPrameela Rani Garnepudi 592108df3cSPrameela Rani Garnepudi do { 602108df3cSPrameela Rani Garnepudi rsi_wait_event(&coex_cb->coex_tx_thread.event, timeout); 612108df3cSPrameela Rani Garnepudi rsi_reset_event(&coex_cb->coex_tx_thread.event); 622108df3cSPrameela Rani Garnepudi 632108df3cSPrameela Rani Garnepudi rsi_coex_sched_tx_pkts(coex_cb); 642108df3cSPrameela Rani Garnepudi } while (atomic_read(&coex_cb->coex_tx_thread.thread_done) == 0); 652108df3cSPrameela Rani Garnepudi 662108df3cSPrameela Rani Garnepudi complete_and_exit(&coex_cb->coex_tx_thread.completion, 0); 672108df3cSPrameela Rani Garnepudi } 682108df3cSPrameela Rani Garnepudi 692108df3cSPrameela Rani Garnepudi int rsi_coex_recv_pkt(struct rsi_common *common, u8 *msg) 702108df3cSPrameela Rani Garnepudi { 712108df3cSPrameela Rani Garnepudi u8 msg_type = msg[RSI_RX_DESC_MSG_TYPE_OFFSET]; 722108df3cSPrameela Rani Garnepudi 732108df3cSPrameela Rani Garnepudi switch (msg_type) { 742108df3cSPrameela Rani Garnepudi case COMMON_CARD_READY_IND: 752108df3cSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "common card ready received\n"); 76d76f8513SSiva Rebbagondla common->hibernate_resume = false; 772108df3cSPrameela Rani Garnepudi rsi_handle_card_ready(common, msg); 782108df3cSPrameela Rani Garnepudi break; 792108df3cSPrameela Rani Garnepudi case SLEEP_NOTIFY_IND: 802108df3cSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "sleep notify received\n"); 812108df3cSPrameela Rani Garnepudi rsi_mgmt_pkt_recv(common, msg); 822108df3cSPrameela Rani Garnepudi break; 832108df3cSPrameela Rani Garnepudi } 842108df3cSPrameela Rani Garnepudi 852108df3cSPrameela Rani Garnepudi return 0; 862108df3cSPrameela Rani Garnepudi } 872108df3cSPrameela Rani Garnepudi 882108df3cSPrameela Rani Garnepudi static inline int rsi_map_coex_q(u8 hal_queue) 892108df3cSPrameela Rani Garnepudi { 902108df3cSPrameela Rani Garnepudi switch (hal_queue) { 912108df3cSPrameela Rani Garnepudi case RSI_COEX_Q: 922108df3cSPrameela Rani Garnepudi return RSI_COEX_Q_COMMON; 932108df3cSPrameela Rani Garnepudi case RSI_WLAN_Q: 942108df3cSPrameela Rani Garnepudi return RSI_COEX_Q_WLAN; 952108df3cSPrameela Rani Garnepudi case RSI_BT_Q: 962108df3cSPrameela Rani Garnepudi return RSI_COEX_Q_BT; 972108df3cSPrameela Rani Garnepudi } 982108df3cSPrameela Rani Garnepudi return RSI_COEX_Q_INVALID; 992108df3cSPrameela Rani Garnepudi } 1002108df3cSPrameela Rani Garnepudi 1012108df3cSPrameela Rani Garnepudi int rsi_coex_send_pkt(void *priv, struct sk_buff *skb, u8 hal_queue) 1022108df3cSPrameela Rani Garnepudi { 1032108df3cSPrameela Rani Garnepudi struct rsi_common *common = (struct rsi_common *)priv; 1042108df3cSPrameela Rani Garnepudi struct rsi_coex_ctrl_block *coex_cb = 1052108df3cSPrameela Rani Garnepudi (struct rsi_coex_ctrl_block *)common->coex_cb; 1062108df3cSPrameela Rani Garnepudi struct skb_info *tx_params = NULL; 1072108df3cSPrameela Rani Garnepudi enum rsi_coex_queues coex_q; 1082108df3cSPrameela Rani Garnepudi int status; 1092108df3cSPrameela Rani Garnepudi 1102108df3cSPrameela Rani Garnepudi coex_q = rsi_map_coex_q(hal_queue); 1112108df3cSPrameela Rani Garnepudi if (coex_q == RSI_COEX_Q_INVALID) { 1122108df3cSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "Invalid coex queue\n"); 1132108df3cSPrameela Rani Garnepudi return -EINVAL; 1142108df3cSPrameela Rani Garnepudi } 1152108df3cSPrameela Rani Garnepudi if (coex_q != RSI_COEX_Q_COMMON && 1162108df3cSPrameela Rani Garnepudi coex_q != RSI_COEX_Q_WLAN) { 1172108df3cSPrameela Rani Garnepudi skb_queue_tail(&coex_cb->coex_tx_qs[coex_q], skb); 1182108df3cSPrameela Rani Garnepudi rsi_set_event(&coex_cb->coex_tx_thread.event); 1192108df3cSPrameela Rani Garnepudi return 0; 1202108df3cSPrameela Rani Garnepudi } 1212108df3cSPrameela Rani Garnepudi if (common->iface_down) { 1222108df3cSPrameela Rani Garnepudi tx_params = 1232108df3cSPrameela Rani Garnepudi (struct skb_info *)&IEEE80211_SKB_CB(skb)->driver_data; 1242108df3cSPrameela Rani Garnepudi 1252108df3cSPrameela Rani Garnepudi if (!(tx_params->flags & INTERNAL_MGMT_PKT)) { 1262108df3cSPrameela Rani Garnepudi rsi_indicate_tx_status(common->priv, skb, -EINVAL); 1272108df3cSPrameela Rani Garnepudi return 0; 1282108df3cSPrameela Rani Garnepudi } 1292108df3cSPrameela Rani Garnepudi } 1302108df3cSPrameela Rani Garnepudi 1312108df3cSPrameela Rani Garnepudi /* Send packet to hal */ 1322108df3cSPrameela Rani Garnepudi if (skb->priority == MGMT_SOFT_Q) 1332108df3cSPrameela Rani Garnepudi status = rsi_send_mgmt_pkt(common, skb); 1342108df3cSPrameela Rani Garnepudi else 1352108df3cSPrameela Rani Garnepudi status = rsi_send_data_pkt(common, skb); 1362108df3cSPrameela Rani Garnepudi 1372108df3cSPrameela Rani Garnepudi return status; 1382108df3cSPrameela Rani Garnepudi } 1392108df3cSPrameela Rani Garnepudi 1402108df3cSPrameela Rani Garnepudi int rsi_coex_attach(struct rsi_common *common) 1412108df3cSPrameela Rani Garnepudi { 1422108df3cSPrameela Rani Garnepudi struct rsi_coex_ctrl_block *coex_cb; 1432108df3cSPrameela Rani Garnepudi int cnt; 1442108df3cSPrameela Rani Garnepudi 1452108df3cSPrameela Rani Garnepudi coex_cb = kzalloc(sizeof(*coex_cb), GFP_KERNEL); 1462108df3cSPrameela Rani Garnepudi if (!coex_cb) 1472108df3cSPrameela Rani Garnepudi return -ENOMEM; 1482108df3cSPrameela Rani Garnepudi 1492108df3cSPrameela Rani Garnepudi common->coex_cb = (void *)coex_cb; 1502108df3cSPrameela Rani Garnepudi coex_cb->priv = common; 1512108df3cSPrameela Rani Garnepudi 1522108df3cSPrameela Rani Garnepudi /* Initialize co-ex queues */ 1532108df3cSPrameela Rani Garnepudi for (cnt = 0; cnt < NUM_COEX_TX_QUEUES; cnt++) 1542108df3cSPrameela Rani Garnepudi skb_queue_head_init(&coex_cb->coex_tx_qs[cnt]); 1552108df3cSPrameela Rani Garnepudi rsi_init_event(&coex_cb->coex_tx_thread.event); 1562108df3cSPrameela Rani Garnepudi 1572108df3cSPrameela Rani Garnepudi /* Initialize co-ex thread */ 1582108df3cSPrameela Rani Garnepudi if (rsi_create_kthread(common, 1592108df3cSPrameela Rani Garnepudi &coex_cb->coex_tx_thread, 1602108df3cSPrameela Rani Garnepudi rsi_coex_scheduler_thread, 1612108df3cSPrameela Rani Garnepudi "Coex-Tx-Thread")) { 1622108df3cSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "%s: Unable to init tx thrd\n", __func__); 1632108df3cSPrameela Rani Garnepudi return -EINVAL; 1642108df3cSPrameela Rani Garnepudi } 1652108df3cSPrameela Rani Garnepudi return 0; 1662108df3cSPrameela Rani Garnepudi } 1672108df3cSPrameela Rani Garnepudi 1682108df3cSPrameela Rani Garnepudi void rsi_coex_detach(struct rsi_common *common) 1692108df3cSPrameela Rani Garnepudi { 1702108df3cSPrameela Rani Garnepudi struct rsi_coex_ctrl_block *coex_cb = 1712108df3cSPrameela Rani Garnepudi (struct rsi_coex_ctrl_block *)common->coex_cb; 1722108df3cSPrameela Rani Garnepudi int cnt; 1732108df3cSPrameela Rani Garnepudi 1742108df3cSPrameela Rani Garnepudi rsi_kill_thread(&coex_cb->coex_tx_thread); 1752108df3cSPrameela Rani Garnepudi 1762108df3cSPrameela Rani Garnepudi for (cnt = 0; cnt < NUM_COEX_TX_QUEUES; cnt++) 1772108df3cSPrameela Rani Garnepudi skb_queue_purge(&coex_cb->coex_tx_qs[cnt]); 1782108df3cSPrameela Rani Garnepudi 1792108df3cSPrameela Rani Garnepudi kfree(coex_cb); 1802108df3cSPrameela Rani Garnepudi } 181