18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2018 Redpine Signals Inc. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 58c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 68c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 98c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 108c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 118c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 128c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 138c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 148c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "rsi_main.h" 188c2ecf20Sopenharmony_ci#include "rsi_coex.h" 198c2ecf20Sopenharmony_ci#include "rsi_mgmt.h" 208c2ecf20Sopenharmony_ci#include "rsi_hal.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic enum rsi_coex_queues rsi_coex_determine_coex_q 238c2ecf20Sopenharmony_ci (struct rsi_coex_ctrl_block *coex_cb) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci enum rsi_coex_queues q_num = RSI_COEX_Q_INVALID; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci if (skb_queue_len(&coex_cb->coex_tx_qs[RSI_COEX_Q_COMMON]) > 0) 288c2ecf20Sopenharmony_ci q_num = RSI_COEX_Q_COMMON; 298c2ecf20Sopenharmony_ci if (skb_queue_len(&coex_cb->coex_tx_qs[RSI_COEX_Q_BT]) > 0) 308c2ecf20Sopenharmony_ci q_num = RSI_COEX_Q_BT; 318c2ecf20Sopenharmony_ci if (skb_queue_len(&coex_cb->coex_tx_qs[RSI_COEX_Q_WLAN]) > 0) 328c2ecf20Sopenharmony_ci q_num = RSI_COEX_Q_WLAN; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci return q_num; 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic void rsi_coex_sched_tx_pkts(struct rsi_coex_ctrl_block *coex_cb) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci enum rsi_coex_queues coex_q = RSI_COEX_Q_INVALID; 408c2ecf20Sopenharmony_ci struct sk_buff *skb; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci do { 438c2ecf20Sopenharmony_ci coex_q = rsi_coex_determine_coex_q(coex_cb); 448c2ecf20Sopenharmony_ci rsi_dbg(INFO_ZONE, "queue = %d\n", coex_q); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if (coex_q == RSI_COEX_Q_BT) { 478c2ecf20Sopenharmony_ci skb = skb_dequeue(&coex_cb->coex_tx_qs[RSI_COEX_Q_BT]); 488c2ecf20Sopenharmony_ci rsi_send_bt_pkt(coex_cb->priv, skb); 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci } while (coex_q != RSI_COEX_Q_INVALID); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic void rsi_coex_scheduler_thread(struct rsi_common *common) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci struct rsi_coex_ctrl_block *coex_cb = 568c2ecf20Sopenharmony_ci (struct rsi_coex_ctrl_block *)common->coex_cb; 578c2ecf20Sopenharmony_ci u32 timeout = EVENT_WAIT_FOREVER; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci do { 608c2ecf20Sopenharmony_ci rsi_wait_event(&coex_cb->coex_tx_thread.event, timeout); 618c2ecf20Sopenharmony_ci rsi_reset_event(&coex_cb->coex_tx_thread.event); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci rsi_coex_sched_tx_pkts(coex_cb); 648c2ecf20Sopenharmony_ci } while (atomic_read(&coex_cb->coex_tx_thread.thread_done) == 0); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci complete_and_exit(&coex_cb->coex_tx_thread.completion, 0); 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ciint rsi_coex_recv_pkt(struct rsi_common *common, u8 *msg) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci u8 msg_type = msg[RSI_RX_DESC_MSG_TYPE_OFFSET]; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci switch (msg_type) { 748c2ecf20Sopenharmony_ci case COMMON_CARD_READY_IND: 758c2ecf20Sopenharmony_ci rsi_dbg(INFO_ZONE, "common card ready received\n"); 768c2ecf20Sopenharmony_ci common->hibernate_resume = false; 778c2ecf20Sopenharmony_ci rsi_handle_card_ready(common, msg); 788c2ecf20Sopenharmony_ci break; 798c2ecf20Sopenharmony_ci case SLEEP_NOTIFY_IND: 808c2ecf20Sopenharmony_ci rsi_dbg(INFO_ZONE, "sleep notify received\n"); 818c2ecf20Sopenharmony_ci rsi_mgmt_pkt_recv(common, msg); 828c2ecf20Sopenharmony_ci break; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return 0; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic inline int rsi_map_coex_q(u8 hal_queue) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci switch (hal_queue) { 918c2ecf20Sopenharmony_ci case RSI_COEX_Q: 928c2ecf20Sopenharmony_ci return RSI_COEX_Q_COMMON; 938c2ecf20Sopenharmony_ci case RSI_WLAN_Q: 948c2ecf20Sopenharmony_ci return RSI_COEX_Q_WLAN; 958c2ecf20Sopenharmony_ci case RSI_BT_Q: 968c2ecf20Sopenharmony_ci return RSI_COEX_Q_BT; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci return RSI_COEX_Q_INVALID; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ciint rsi_coex_send_pkt(void *priv, struct sk_buff *skb, u8 hal_queue) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct rsi_common *common = (struct rsi_common *)priv; 1048c2ecf20Sopenharmony_ci struct rsi_coex_ctrl_block *coex_cb = 1058c2ecf20Sopenharmony_ci (struct rsi_coex_ctrl_block *)common->coex_cb; 1068c2ecf20Sopenharmony_ci struct skb_info *tx_params = NULL; 1078c2ecf20Sopenharmony_ci enum rsi_coex_queues coex_q; 1088c2ecf20Sopenharmony_ci int status; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci coex_q = rsi_map_coex_q(hal_queue); 1118c2ecf20Sopenharmony_ci if (coex_q == RSI_COEX_Q_INVALID) { 1128c2ecf20Sopenharmony_ci rsi_dbg(ERR_ZONE, "Invalid coex queue\n"); 1138c2ecf20Sopenharmony_ci return -EINVAL; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci if (coex_q != RSI_COEX_Q_COMMON && 1168c2ecf20Sopenharmony_ci coex_q != RSI_COEX_Q_WLAN) { 1178c2ecf20Sopenharmony_ci skb_queue_tail(&coex_cb->coex_tx_qs[coex_q], skb); 1188c2ecf20Sopenharmony_ci rsi_set_event(&coex_cb->coex_tx_thread.event); 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci if (common->iface_down) { 1228c2ecf20Sopenharmony_ci tx_params = 1238c2ecf20Sopenharmony_ci (struct skb_info *)&IEEE80211_SKB_CB(skb)->driver_data; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (!(tx_params->flags & INTERNAL_MGMT_PKT)) { 1268c2ecf20Sopenharmony_ci rsi_indicate_tx_status(common->priv, skb, -EINVAL); 1278c2ecf20Sopenharmony_ci return 0; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* Send packet to hal */ 1328c2ecf20Sopenharmony_ci if (skb->priority == MGMT_SOFT_Q) 1338c2ecf20Sopenharmony_ci status = rsi_send_mgmt_pkt(common, skb); 1348c2ecf20Sopenharmony_ci else 1358c2ecf20Sopenharmony_ci status = rsi_send_data_pkt(common, skb); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return status; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ciint rsi_coex_attach(struct rsi_common *common) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct rsi_coex_ctrl_block *coex_cb; 1438c2ecf20Sopenharmony_ci int cnt; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci coex_cb = kzalloc(sizeof(*coex_cb), GFP_KERNEL); 1468c2ecf20Sopenharmony_ci if (!coex_cb) 1478c2ecf20Sopenharmony_ci return -ENOMEM; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci common->coex_cb = (void *)coex_cb; 1508c2ecf20Sopenharmony_ci coex_cb->priv = common; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* Initialize co-ex queues */ 1538c2ecf20Sopenharmony_ci for (cnt = 0; cnt < NUM_COEX_TX_QUEUES; cnt++) 1548c2ecf20Sopenharmony_ci skb_queue_head_init(&coex_cb->coex_tx_qs[cnt]); 1558c2ecf20Sopenharmony_ci rsi_init_event(&coex_cb->coex_tx_thread.event); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* Initialize co-ex thread */ 1588c2ecf20Sopenharmony_ci if (rsi_create_kthread(common, 1598c2ecf20Sopenharmony_ci &coex_cb->coex_tx_thread, 1608c2ecf20Sopenharmony_ci rsi_coex_scheduler_thread, 1618c2ecf20Sopenharmony_ci "Coex-Tx-Thread")) { 1628c2ecf20Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: Unable to init tx thrd\n", __func__); 1638c2ecf20Sopenharmony_ci kfree(coex_cb); 1648c2ecf20Sopenharmony_ci return -EINVAL; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci return 0; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_civoid rsi_coex_detach(struct rsi_common *common) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct rsi_coex_ctrl_block *coex_cb = 1728c2ecf20Sopenharmony_ci (struct rsi_coex_ctrl_block *)common->coex_cb; 1738c2ecf20Sopenharmony_ci int cnt; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci rsi_kill_thread(&coex_cb->coex_tx_thread); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci for (cnt = 0; cnt < NUM_COEX_TX_QUEUES; cnt++) 1788c2ecf20Sopenharmony_ci skb_queue_purge(&coex_cb->coex_tx_qs[cnt]); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci kfree(coex_cb); 1818c2ecf20Sopenharmony_ci} 182