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