|
|
@@ -43,7 +43,7 @@
|
|
|
|
|
|
|
|
|
static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb);
|
|
|
-
|
|
|
+static void ath10k_htt_txrx_compl_task(unsigned long ptr);
|
|
|
|
|
|
static int ath10k_htt_rx_ring_size(struct ath10k_htt *htt)
|
|
|
{
|
|
|
@@ -237,6 +237,10 @@ void ath10k_htt_rx_detach(struct ath10k_htt *htt)
|
|
|
|
|
|
del_timer_sync(&htt->rx_ring.refill_retry_timer);
|
|
|
tasklet_kill(&htt->rx_replenish_task);
|
|
|
+ tasklet_kill(&htt->txrx_compl_task);
|
|
|
+
|
|
|
+ skb_queue_purge(&htt->tx_compl_q);
|
|
|
+ skb_queue_purge(&htt->rx_compl_q);
|
|
|
|
|
|
while (sw_rd_idx != __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr))) {
|
|
|
struct sk_buff *skb =
|
|
|
@@ -529,6 +533,12 @@ int ath10k_htt_rx_attach(struct ath10k_htt *htt)
|
|
|
tasklet_init(&htt->rx_replenish_task, ath10k_htt_rx_replenish_task,
|
|
|
(unsigned long)htt);
|
|
|
|
|
|
+ skb_queue_head_init(&htt->tx_compl_q);
|
|
|
+ skb_queue_head_init(&htt->rx_compl_q);
|
|
|
+
|
|
|
+ tasklet_init(&htt->txrx_compl_task, ath10k_htt_txrx_compl_task,
|
|
|
+ (unsigned long)htt);
|
|
|
+
|
|
|
ath10k_dbg(ATH10K_DBG_BOOT, "htt rx ring size %d fill_level %d\n",
|
|
|
htt->rx_ring.size, htt->rx_ring.fill_level);
|
|
|
return 0;
|
|
|
@@ -1138,6 +1148,43 @@ end:
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static void ath10k_htt_rx_frm_tx_compl(struct ath10k *ar,
|
|
|
+ struct sk_buff *skb)
|
|
|
+{
|
|
|
+ struct ath10k_htt *htt = &ar->htt;
|
|
|
+ struct htt_resp *resp = (struct htt_resp *)skb->data;
|
|
|
+ struct htt_tx_done tx_done = {};
|
|
|
+ int status = MS(resp->data_tx_completion.flags, HTT_DATA_TX_STATUS);
|
|
|
+ __le16 msdu_id;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ switch (status) {
|
|
|
+ case HTT_DATA_TX_STATUS_NO_ACK:
|
|
|
+ tx_done.no_ack = true;
|
|
|
+ break;
|
|
|
+ case HTT_DATA_TX_STATUS_OK:
|
|
|
+ break;
|
|
|
+ case HTT_DATA_TX_STATUS_DISCARD:
|
|
|
+ case HTT_DATA_TX_STATUS_POSTPONE:
|
|
|
+ case HTT_DATA_TX_STATUS_DOWNLOAD_FAIL:
|
|
|
+ tx_done.discard = true;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ ath10k_warn("unhandled tx completion status %d\n", status);
|
|
|
+ tx_done.discard = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion num_msdus %d\n",
|
|
|
+ resp->data_tx_completion.num_msdus);
|
|
|
+
|
|
|
+ for (i = 0; i < resp->data_tx_completion.num_msdus; i++) {
|
|
|
+ msdu_id = resp->data_tx_completion.msdus[i];
|
|
|
+ tx_done.msdu_id = __le16_to_cpu(msdu_id);
|
|
|
+ ath10k_txrx_tx_unref(htt, &tx_done);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
|
|
|
{
|
|
|
struct ath10k_htt *htt = &ar->htt;
|
|
|
@@ -1156,10 +1203,10 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
|
|
|
complete(&htt->target_version_received);
|
|
|
break;
|
|
|
}
|
|
|
- case HTT_T2H_MSG_TYPE_RX_IND: {
|
|
|
- ath10k_htt_rx_handler(htt, &resp->rx_ind);
|
|
|
- break;
|
|
|
- }
|
|
|
+ case HTT_T2H_MSG_TYPE_RX_IND:
|
|
|
+ skb_queue_tail(&htt->rx_compl_q, skb);
|
|
|
+ tasklet_schedule(&htt->txrx_compl_task);
|
|
|
+ return;
|
|
|
case HTT_T2H_MSG_TYPE_PEER_MAP: {
|
|
|
struct htt_peer_map_event ev = {
|
|
|
.vdev_id = resp->peer_map.vdev_id,
|
|
|
@@ -1194,44 +1241,17 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
+ spin_lock_bh(&htt->tx_lock);
|
|
|
ath10k_txrx_tx_unref(htt, &tx_done);
|
|
|
+ spin_unlock_bh(&htt->tx_lock);
|
|
|
break;
|
|
|
}
|
|
|
- case HTT_T2H_MSG_TYPE_TX_COMPL_IND: {
|
|
|
- struct htt_tx_done tx_done = {};
|
|
|
- int status = MS(resp->data_tx_completion.flags,
|
|
|
- HTT_DATA_TX_STATUS);
|
|
|
- __le16 msdu_id;
|
|
|
- int i;
|
|
|
-
|
|
|
- switch (status) {
|
|
|
- case HTT_DATA_TX_STATUS_NO_ACK:
|
|
|
- tx_done.no_ack = true;
|
|
|
- break;
|
|
|
- case HTT_DATA_TX_STATUS_OK:
|
|
|
- break;
|
|
|
- case HTT_DATA_TX_STATUS_DISCARD:
|
|
|
- case HTT_DATA_TX_STATUS_POSTPONE:
|
|
|
- case HTT_DATA_TX_STATUS_DOWNLOAD_FAIL:
|
|
|
- tx_done.discard = true;
|
|
|
- break;
|
|
|
- default:
|
|
|
- ath10k_warn("unhandled tx completion status %d\n",
|
|
|
- status);
|
|
|
- tx_done.discard = true;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion num_msdus %d\n",
|
|
|
- resp->data_tx_completion.num_msdus);
|
|
|
-
|
|
|
- for (i = 0; i < resp->data_tx_completion.num_msdus; i++) {
|
|
|
- msdu_id = resp->data_tx_completion.msdus[i];
|
|
|
- tx_done.msdu_id = __le16_to_cpu(msdu_id);
|
|
|
- ath10k_txrx_tx_unref(htt, &tx_done);
|
|
|
- }
|
|
|
- break;
|
|
|
- }
|
|
|
+ case HTT_T2H_MSG_TYPE_TX_COMPL_IND:
|
|
|
+ spin_lock_bh(&htt->tx_lock);
|
|
|
+ __skb_queue_tail(&htt->tx_compl_q, skb);
|
|
|
+ spin_unlock_bh(&htt->tx_lock);
|
|
|
+ tasklet_schedule(&htt->txrx_compl_task);
|
|
|
+ return;
|
|
|
case HTT_T2H_MSG_TYPE_SEC_IND: {
|
|
|
struct ath10k *ar = htt->ar;
|
|
|
struct htt_security_indication *ev = &resp->security_indication;
|
|
|
@@ -1271,3 +1291,21 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
|
|
|
/* Free the indication buffer */
|
|
|
dev_kfree_skb_any(skb);
|
|
|
}
|
|
|
+
|
|
|
+static void ath10k_htt_txrx_compl_task(unsigned long ptr)
|
|
|
+{
|
|
|
+ struct ath10k_htt *htt = (struct ath10k_htt *)ptr;
|
|
|
+ struct htt_resp *resp;
|
|
|
+ struct sk_buff *skb;
|
|
|
+
|
|
|
+ while ((skb = skb_dequeue(&htt->tx_compl_q))) {
|
|
|
+ ath10k_htt_rx_frm_tx_compl(htt->ar, skb);
|
|
|
+ dev_kfree_skb_any(skb);
|
|
|
+ }
|
|
|
+
|
|
|
+ while ((skb = skb_dequeue(&htt->rx_compl_q))) {
|
|
|
+ resp = (struct htt_resp *)skb->data;
|
|
|
+ ath10k_htt_rx_handler(htt, &resp->rx_ind);
|
|
|
+ dev_kfree_skb_any(skb);
|
|
|
+ }
|
|
|
+}
|