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