rsi_91x_coex.c 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. /**
  2. * Copyright (c) 2018 Redpine Signals Inc.
  3. *
  4. * Permission to use, copy, modify, and/or distribute this software for any
  5. * purpose with or without fee is hereby granted, provided that the above
  6. * copyright notice and this permission notice appear in all copies.
  7. *
  8. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #include "rsi_main.h"
  17. #include "rsi_coex.h"
  18. #include "rsi_mgmt.h"
  19. #include "rsi_hal.h"
  20. static enum rsi_coex_queues rsi_coex_determine_coex_q
  21. (struct rsi_coex_ctrl_block *coex_cb)
  22. {
  23. enum rsi_coex_queues q_num = RSI_COEX_Q_INVALID;
  24. if (skb_queue_len(&coex_cb->coex_tx_qs[RSI_COEX_Q_COMMON]) > 0)
  25. q_num = RSI_COEX_Q_COMMON;
  26. if (skb_queue_len(&coex_cb->coex_tx_qs[RSI_COEX_Q_BT]) > 0)
  27. q_num = RSI_COEX_Q_BT;
  28. if (skb_queue_len(&coex_cb->coex_tx_qs[RSI_COEX_Q_WLAN]) > 0)
  29. q_num = RSI_COEX_Q_WLAN;
  30. return q_num;
  31. }
  32. static void rsi_coex_sched_tx_pkts(struct rsi_coex_ctrl_block *coex_cb)
  33. {
  34. enum rsi_coex_queues coex_q = RSI_COEX_Q_INVALID;
  35. struct sk_buff *skb;
  36. do {
  37. coex_q = rsi_coex_determine_coex_q(coex_cb);
  38. rsi_dbg(INFO_ZONE, "queue = %d\n", coex_q);
  39. if (coex_q == RSI_COEX_Q_BT) {
  40. skb = skb_dequeue(&coex_cb->coex_tx_qs[RSI_COEX_Q_BT]);
  41. rsi_send_bt_pkt(coex_cb->priv, skb);
  42. }
  43. } while (coex_q != RSI_COEX_Q_INVALID);
  44. }
  45. static void rsi_coex_scheduler_thread(struct rsi_common *common)
  46. {
  47. struct rsi_coex_ctrl_block *coex_cb =
  48. (struct rsi_coex_ctrl_block *)common->coex_cb;
  49. u32 timeout = EVENT_WAIT_FOREVER;
  50. do {
  51. rsi_wait_event(&coex_cb->coex_tx_thread.event, timeout);
  52. rsi_reset_event(&coex_cb->coex_tx_thread.event);
  53. rsi_coex_sched_tx_pkts(coex_cb);
  54. } while (atomic_read(&coex_cb->coex_tx_thread.thread_done) == 0);
  55. complete_and_exit(&coex_cb->coex_tx_thread.completion, 0);
  56. }
  57. int rsi_coex_recv_pkt(struct rsi_common *common, u8 *msg)
  58. {
  59. u8 msg_type = msg[RSI_RX_DESC_MSG_TYPE_OFFSET];
  60. switch (msg_type) {
  61. case COMMON_CARD_READY_IND:
  62. rsi_dbg(INFO_ZONE, "common card ready received\n");
  63. common->hibernate_resume = false;
  64. rsi_handle_card_ready(common, msg);
  65. break;
  66. case SLEEP_NOTIFY_IND:
  67. rsi_dbg(INFO_ZONE, "sleep notify received\n");
  68. rsi_mgmt_pkt_recv(common, msg);
  69. break;
  70. }
  71. return 0;
  72. }
  73. static inline int rsi_map_coex_q(u8 hal_queue)
  74. {
  75. switch (hal_queue) {
  76. case RSI_COEX_Q:
  77. return RSI_COEX_Q_COMMON;
  78. case RSI_WLAN_Q:
  79. return RSI_COEX_Q_WLAN;
  80. case RSI_BT_Q:
  81. return RSI_COEX_Q_BT;
  82. }
  83. return RSI_COEX_Q_INVALID;
  84. }
  85. int rsi_coex_send_pkt(void *priv, struct sk_buff *skb, u8 hal_queue)
  86. {
  87. struct rsi_common *common = (struct rsi_common *)priv;
  88. struct rsi_coex_ctrl_block *coex_cb =
  89. (struct rsi_coex_ctrl_block *)common->coex_cb;
  90. struct skb_info *tx_params = NULL;
  91. enum rsi_coex_queues coex_q;
  92. int status;
  93. coex_q = rsi_map_coex_q(hal_queue);
  94. if (coex_q == RSI_COEX_Q_INVALID) {
  95. rsi_dbg(ERR_ZONE, "Invalid coex queue\n");
  96. return -EINVAL;
  97. }
  98. if (coex_q != RSI_COEX_Q_COMMON &&
  99. coex_q != RSI_COEX_Q_WLAN) {
  100. skb_queue_tail(&coex_cb->coex_tx_qs[coex_q], skb);
  101. rsi_set_event(&coex_cb->coex_tx_thread.event);
  102. return 0;
  103. }
  104. if (common->iface_down) {
  105. tx_params =
  106. (struct skb_info *)&IEEE80211_SKB_CB(skb)->driver_data;
  107. if (!(tx_params->flags & INTERNAL_MGMT_PKT)) {
  108. rsi_indicate_tx_status(common->priv, skb, -EINVAL);
  109. return 0;
  110. }
  111. }
  112. /* Send packet to hal */
  113. if (skb->priority == MGMT_SOFT_Q)
  114. status = rsi_send_mgmt_pkt(common, skb);
  115. else
  116. status = rsi_send_data_pkt(common, skb);
  117. return status;
  118. }
  119. int rsi_coex_attach(struct rsi_common *common)
  120. {
  121. struct rsi_coex_ctrl_block *coex_cb;
  122. int cnt;
  123. coex_cb = kzalloc(sizeof(*coex_cb), GFP_KERNEL);
  124. if (!coex_cb)
  125. return -ENOMEM;
  126. common->coex_cb = (void *)coex_cb;
  127. coex_cb->priv = common;
  128. /* Initialize co-ex queues */
  129. for (cnt = 0; cnt < NUM_COEX_TX_QUEUES; cnt++)
  130. skb_queue_head_init(&coex_cb->coex_tx_qs[cnt]);
  131. rsi_init_event(&coex_cb->coex_tx_thread.event);
  132. /* Initialize co-ex thread */
  133. if (rsi_create_kthread(common,
  134. &coex_cb->coex_tx_thread,
  135. rsi_coex_scheduler_thread,
  136. "Coex-Tx-Thread")) {
  137. rsi_dbg(ERR_ZONE, "%s: Unable to init tx thrd\n", __func__);
  138. return -EINVAL;
  139. }
  140. return 0;
  141. }
  142. void rsi_coex_detach(struct rsi_common *common)
  143. {
  144. struct rsi_coex_ctrl_block *coex_cb =
  145. (struct rsi_coex_ctrl_block *)common->coex_cb;
  146. int cnt;
  147. rsi_kill_thread(&coex_cb->coex_tx_thread);
  148. for (cnt = 0; cnt < NUM_COEX_TX_QUEUES; cnt++)
  149. skb_queue_purge(&coex_cb->coex_tx_qs[cnt]);
  150. kfree(coex_cb);
  151. }