162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2018 Redpine Signals Inc. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 562306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 662306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 962306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1062306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1162306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1262306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1362306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1462306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "rsi_main.h" 1862306a36Sopenharmony_ci#include "rsi_coex.h" 1962306a36Sopenharmony_ci#include "rsi_mgmt.h" 2062306a36Sopenharmony_ci#include "rsi_hal.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic enum rsi_coex_queues rsi_coex_determine_coex_q 2362306a36Sopenharmony_ci (struct rsi_coex_ctrl_block *coex_cb) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci enum rsi_coex_queues q_num = RSI_COEX_Q_INVALID; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci if (skb_queue_len(&coex_cb->coex_tx_qs[RSI_COEX_Q_COMMON]) > 0) 2862306a36Sopenharmony_ci q_num = RSI_COEX_Q_COMMON; 2962306a36Sopenharmony_ci if (skb_queue_len(&coex_cb->coex_tx_qs[RSI_COEX_Q_BT]) > 0) 3062306a36Sopenharmony_ci q_num = RSI_COEX_Q_BT; 3162306a36Sopenharmony_ci if (skb_queue_len(&coex_cb->coex_tx_qs[RSI_COEX_Q_WLAN]) > 0) 3262306a36Sopenharmony_ci q_num = RSI_COEX_Q_WLAN; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci return q_num; 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic void rsi_coex_sched_tx_pkts(struct rsi_coex_ctrl_block *coex_cb) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci enum rsi_coex_queues coex_q = RSI_COEX_Q_INVALID; 4062306a36Sopenharmony_ci struct sk_buff *skb; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci do { 4362306a36Sopenharmony_ci coex_q = rsi_coex_determine_coex_q(coex_cb); 4462306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, "queue = %d\n", coex_q); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (coex_q == RSI_COEX_Q_BT) { 4762306a36Sopenharmony_ci skb = skb_dequeue(&coex_cb->coex_tx_qs[RSI_COEX_Q_BT]); 4862306a36Sopenharmony_ci rsi_send_bt_pkt(coex_cb->priv, skb); 4962306a36Sopenharmony_ci } 5062306a36Sopenharmony_ci } while (coex_q != RSI_COEX_Q_INVALID); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void rsi_coex_scheduler_thread(struct rsi_common *common) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct rsi_coex_ctrl_block *coex_cb = common->coex_cb; 5662306a36Sopenharmony_ci u32 timeout = EVENT_WAIT_FOREVER; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci do { 5962306a36Sopenharmony_ci rsi_wait_event(&coex_cb->coex_tx_thread.event, timeout); 6062306a36Sopenharmony_ci rsi_reset_event(&coex_cb->coex_tx_thread.event); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci rsi_coex_sched_tx_pkts(coex_cb); 6362306a36Sopenharmony_ci } while (atomic_read(&coex_cb->coex_tx_thread.thread_done) == 0); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci kthread_complete_and_exit(&coex_cb->coex_tx_thread.completion, 0); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ciint rsi_coex_recv_pkt(struct rsi_common *common, u8 *msg) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci u8 msg_type = msg[RSI_RX_DESC_MSG_TYPE_OFFSET]; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci switch (msg_type) { 7362306a36Sopenharmony_ci case COMMON_CARD_READY_IND: 7462306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, "common card ready received\n"); 7562306a36Sopenharmony_ci common->hibernate_resume = false; 7662306a36Sopenharmony_ci rsi_handle_card_ready(common, msg); 7762306a36Sopenharmony_ci break; 7862306a36Sopenharmony_ci case SLEEP_NOTIFY_IND: 7962306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, "sleep notify received\n"); 8062306a36Sopenharmony_ci rsi_mgmt_pkt_recv(common, msg); 8162306a36Sopenharmony_ci break; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic inline int rsi_map_coex_q(u8 hal_queue) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci switch (hal_queue) { 9062306a36Sopenharmony_ci case RSI_COEX_Q: 9162306a36Sopenharmony_ci return RSI_COEX_Q_COMMON; 9262306a36Sopenharmony_ci case RSI_WLAN_Q: 9362306a36Sopenharmony_ci return RSI_COEX_Q_WLAN; 9462306a36Sopenharmony_ci case RSI_BT_Q: 9562306a36Sopenharmony_ci return RSI_COEX_Q_BT; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci return RSI_COEX_Q_INVALID; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ciint rsi_coex_send_pkt(void *priv, struct sk_buff *skb, u8 hal_queue) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct rsi_common *common = priv; 10362306a36Sopenharmony_ci struct rsi_coex_ctrl_block *coex_cb = common->coex_cb; 10462306a36Sopenharmony_ci struct skb_info *tx_params = NULL; 10562306a36Sopenharmony_ci enum rsi_coex_queues coex_q; 10662306a36Sopenharmony_ci int status; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci coex_q = rsi_map_coex_q(hal_queue); 10962306a36Sopenharmony_ci if (coex_q == RSI_COEX_Q_INVALID) { 11062306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, "Invalid coex queue\n"); 11162306a36Sopenharmony_ci return -EINVAL; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci if (coex_q != RSI_COEX_Q_COMMON && 11462306a36Sopenharmony_ci coex_q != RSI_COEX_Q_WLAN) { 11562306a36Sopenharmony_ci skb_queue_tail(&coex_cb->coex_tx_qs[coex_q], skb); 11662306a36Sopenharmony_ci rsi_set_event(&coex_cb->coex_tx_thread.event); 11762306a36Sopenharmony_ci return 0; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci if (common->iface_down) { 12062306a36Sopenharmony_ci tx_params = 12162306a36Sopenharmony_ci (struct skb_info *)&IEEE80211_SKB_CB(skb)->driver_data; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (!(tx_params->flags & INTERNAL_MGMT_PKT)) { 12462306a36Sopenharmony_ci rsi_indicate_tx_status(common->priv, skb, -EINVAL); 12562306a36Sopenharmony_ci return 0; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* Send packet to hal */ 13062306a36Sopenharmony_ci if (skb->priority == MGMT_SOFT_Q) 13162306a36Sopenharmony_ci status = rsi_send_mgmt_pkt(common, skb); 13262306a36Sopenharmony_ci else 13362306a36Sopenharmony_ci status = rsi_send_data_pkt(common, skb); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return status; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ciint rsi_coex_attach(struct rsi_common *common) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct rsi_coex_ctrl_block *coex_cb; 14162306a36Sopenharmony_ci int cnt; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci coex_cb = kzalloc(sizeof(*coex_cb), GFP_KERNEL); 14462306a36Sopenharmony_ci if (!coex_cb) 14562306a36Sopenharmony_ci return -ENOMEM; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci common->coex_cb = (void *)coex_cb; 14862306a36Sopenharmony_ci coex_cb->priv = common; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* Initialize co-ex queues */ 15162306a36Sopenharmony_ci for (cnt = 0; cnt < NUM_COEX_TX_QUEUES; cnt++) 15262306a36Sopenharmony_ci skb_queue_head_init(&coex_cb->coex_tx_qs[cnt]); 15362306a36Sopenharmony_ci rsi_init_event(&coex_cb->coex_tx_thread.event); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* Initialize co-ex thread */ 15662306a36Sopenharmony_ci if (rsi_create_kthread(common, 15762306a36Sopenharmony_ci &coex_cb->coex_tx_thread, 15862306a36Sopenharmony_ci rsi_coex_scheduler_thread, 15962306a36Sopenharmony_ci "Coex-Tx-Thread")) { 16062306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: Unable to init tx thrd\n", __func__); 16162306a36Sopenharmony_ci kfree(coex_cb); 16262306a36Sopenharmony_ci return -EINVAL; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci return 0; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_civoid rsi_coex_detach(struct rsi_common *common) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct rsi_coex_ctrl_block *coex_cb = common->coex_cb; 17062306a36Sopenharmony_ci int cnt; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci rsi_kill_thread(&coex_cb->coex_tx_thread); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci for (cnt = 0; cnt < NUM_COEX_TX_QUEUES; cnt++) 17562306a36Sopenharmony_ci skb_queue_purge(&coex_cb->coex_tx_qs[cnt]); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci kfree(coex_cb); 17862306a36Sopenharmony_ci} 179