14a5fb1bbSJérôme Pouiller // SPDX-License-Identifier: GPL-2.0-only
24a5fb1bbSJérôme Pouiller /*
34a5fb1bbSJérôme Pouiller * Queue between the tx operation and the bh workqueue.
44a5fb1bbSJérôme Pouiller *
54a5fb1bbSJérôme Pouiller * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
64a5fb1bbSJérôme Pouiller * Copyright (c) 2010, ST-Ericsson
74a5fb1bbSJérôme Pouiller */
84a5fb1bbSJérôme Pouiller #include <linux/sched.h>
94a5fb1bbSJérôme Pouiller #include <net/mac80211.h>
104a5fb1bbSJérôme Pouiller
114a5fb1bbSJérôme Pouiller #include "queue.h"
124a5fb1bbSJérôme Pouiller #include "wfx.h"
134a5fb1bbSJérôme Pouiller #include "sta.h"
144a5fb1bbSJérôme Pouiller #include "data_tx.h"
154a5fb1bbSJérôme Pouiller #include "traces.h"
164a5fb1bbSJérôme Pouiller
wfx_tx_lock(struct wfx_dev * wdev)174a5fb1bbSJérôme Pouiller void wfx_tx_lock(struct wfx_dev *wdev)
184a5fb1bbSJérôme Pouiller {
194a5fb1bbSJérôme Pouiller atomic_inc(&wdev->tx_lock);
204a5fb1bbSJérôme Pouiller }
214a5fb1bbSJérôme Pouiller
wfx_tx_unlock(struct wfx_dev * wdev)224a5fb1bbSJérôme Pouiller void wfx_tx_unlock(struct wfx_dev *wdev)
234a5fb1bbSJérôme Pouiller {
244a5fb1bbSJérôme Pouiller int tx_lock = atomic_dec_return(&wdev->tx_lock);
254a5fb1bbSJérôme Pouiller
264a5fb1bbSJérôme Pouiller WARN(tx_lock < 0, "inconsistent tx_lock value");
274a5fb1bbSJérôme Pouiller if (!tx_lock)
284a5fb1bbSJérôme Pouiller wfx_bh_request_tx(wdev);
294a5fb1bbSJérôme Pouiller }
304a5fb1bbSJérôme Pouiller
wfx_tx_flush(struct wfx_dev * wdev)314a5fb1bbSJérôme Pouiller void wfx_tx_flush(struct wfx_dev *wdev)
324a5fb1bbSJérôme Pouiller {
334a5fb1bbSJérôme Pouiller int ret;
344a5fb1bbSJérôme Pouiller
354a5fb1bbSJérôme Pouiller /* Do not wait for any reply if chip is frozen */
364a5fb1bbSJérôme Pouiller if (wdev->chip_frozen)
374a5fb1bbSJérôme Pouiller return;
384a5fb1bbSJérôme Pouiller
394a5fb1bbSJérôme Pouiller wfx_tx_lock(wdev);
404a5fb1bbSJérôme Pouiller mutex_lock(&wdev->hif_cmd.lock);
414a5fb1bbSJérôme Pouiller ret = wait_event_timeout(wdev->hif.tx_buffers_empty, !wdev->hif.tx_buffers_used,
424a5fb1bbSJérôme Pouiller msecs_to_jiffies(3000));
434a5fb1bbSJérôme Pouiller if (!ret) {
444a5fb1bbSJérôme Pouiller dev_warn(wdev->dev, "cannot flush tx buffers (%d still busy)\n",
454a5fb1bbSJérôme Pouiller wdev->hif.tx_buffers_used);
464a5fb1bbSJérôme Pouiller wfx_pending_dump_old_frames(wdev, 3000);
474a5fb1bbSJérôme Pouiller /* FIXME: drop pending frames here */
484a5fb1bbSJérôme Pouiller wdev->chip_frozen = true;
494a5fb1bbSJérôme Pouiller }
504a5fb1bbSJérôme Pouiller mutex_unlock(&wdev->hif_cmd.lock);
514a5fb1bbSJérôme Pouiller wfx_tx_unlock(wdev);
524a5fb1bbSJérôme Pouiller }
534a5fb1bbSJérôme Pouiller
wfx_tx_lock_flush(struct wfx_dev * wdev)544a5fb1bbSJérôme Pouiller void wfx_tx_lock_flush(struct wfx_dev *wdev)
554a5fb1bbSJérôme Pouiller {
564a5fb1bbSJérôme Pouiller wfx_tx_lock(wdev);
574a5fb1bbSJérôme Pouiller wfx_tx_flush(wdev);
584a5fb1bbSJérôme Pouiller }
594a5fb1bbSJérôme Pouiller
wfx_tx_queues_init(struct wfx_vif * wvif)604a5fb1bbSJérôme Pouiller void wfx_tx_queues_init(struct wfx_vif *wvif)
614a5fb1bbSJérôme Pouiller {
624a5fb1bbSJérôme Pouiller /* The device is in charge to respect the details of the QoS parameters. The driver just
634a5fb1bbSJérôme Pouiller * ensure that it roughtly respect the priorities to avoid any shortage.
644a5fb1bbSJérôme Pouiller */
654a5fb1bbSJérôme Pouiller const int priorities[IEEE80211_NUM_ACS] = { 1, 2, 64, 128 };
664a5fb1bbSJérôme Pouiller int i;
674a5fb1bbSJérôme Pouiller
684a5fb1bbSJérôme Pouiller for (i = 0; i < IEEE80211_NUM_ACS; ++i) {
694a5fb1bbSJérôme Pouiller skb_queue_head_init(&wvif->tx_queue[i].normal);
704a5fb1bbSJérôme Pouiller skb_queue_head_init(&wvif->tx_queue[i].cab);
714a5fb1bbSJérôme Pouiller wvif->tx_queue[i].priority = priorities[i];
724a5fb1bbSJérôme Pouiller }
734a5fb1bbSJérôme Pouiller }
744a5fb1bbSJérôme Pouiller
wfx_tx_queue_empty(struct wfx_vif * wvif,struct wfx_queue * queue)754a5fb1bbSJérôme Pouiller bool wfx_tx_queue_empty(struct wfx_vif *wvif, struct wfx_queue *queue)
764a5fb1bbSJérôme Pouiller {
774a5fb1bbSJérôme Pouiller return skb_queue_empty_lockless(&queue->normal) && skb_queue_empty_lockless(&queue->cab);
784a5fb1bbSJérôme Pouiller }
794a5fb1bbSJérôme Pouiller
wfx_tx_queues_check_empty(struct wfx_vif * wvif)804a5fb1bbSJérôme Pouiller void wfx_tx_queues_check_empty(struct wfx_vif *wvif)
814a5fb1bbSJérôme Pouiller {
824a5fb1bbSJérôme Pouiller int i;
834a5fb1bbSJérôme Pouiller
844a5fb1bbSJérôme Pouiller for (i = 0; i < IEEE80211_NUM_ACS; ++i) {
854a5fb1bbSJérôme Pouiller WARN_ON(atomic_read(&wvif->tx_queue[i].pending_frames));
864a5fb1bbSJérôme Pouiller WARN_ON(!wfx_tx_queue_empty(wvif, &wvif->tx_queue[i]));
874a5fb1bbSJérôme Pouiller }
884a5fb1bbSJérôme Pouiller }
894a5fb1bbSJérôme Pouiller
__wfx_tx_queue_drop(struct wfx_vif * wvif,struct sk_buff_head * skb_queue,struct sk_buff_head * dropped)904a5fb1bbSJérôme Pouiller static void __wfx_tx_queue_drop(struct wfx_vif *wvif,
914a5fb1bbSJérôme Pouiller struct sk_buff_head *skb_queue, struct sk_buff_head *dropped)
924a5fb1bbSJérôme Pouiller {
934a5fb1bbSJérôme Pouiller struct sk_buff *skb, *tmp;
944a5fb1bbSJérôme Pouiller
954a5fb1bbSJérôme Pouiller spin_lock_bh(&skb_queue->lock);
964a5fb1bbSJérôme Pouiller skb_queue_walk_safe(skb_queue, skb, tmp) {
974a5fb1bbSJérôme Pouiller __skb_unlink(skb, skb_queue);
984a5fb1bbSJérôme Pouiller skb_queue_head(dropped, skb);
994a5fb1bbSJérôme Pouiller }
1004a5fb1bbSJérôme Pouiller spin_unlock_bh(&skb_queue->lock);
1014a5fb1bbSJérôme Pouiller }
1024a5fb1bbSJérôme Pouiller
wfx_tx_queue_drop(struct wfx_vif * wvif,struct wfx_queue * queue,struct sk_buff_head * dropped)1034a5fb1bbSJérôme Pouiller void wfx_tx_queue_drop(struct wfx_vif *wvif, struct wfx_queue *queue,
1044a5fb1bbSJérôme Pouiller struct sk_buff_head *dropped)
1054a5fb1bbSJérôme Pouiller {
1064a5fb1bbSJérôme Pouiller __wfx_tx_queue_drop(wvif, &queue->cab, dropped);
1074a5fb1bbSJérôme Pouiller __wfx_tx_queue_drop(wvif, &queue->normal, dropped);
1084a5fb1bbSJérôme Pouiller wake_up(&wvif->wdev->tx_dequeue);
1094a5fb1bbSJérôme Pouiller }
1104a5fb1bbSJérôme Pouiller
wfx_tx_queues_put(struct wfx_vif * wvif,struct sk_buff * skb)1114a5fb1bbSJérôme Pouiller void wfx_tx_queues_put(struct wfx_vif *wvif, struct sk_buff *skb)
1124a5fb1bbSJérôme Pouiller {
1134a5fb1bbSJérôme Pouiller struct wfx_queue *queue = &wvif->tx_queue[skb_get_queue_mapping(skb)];
1144a5fb1bbSJérôme Pouiller struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
1154a5fb1bbSJérôme Pouiller
1164a5fb1bbSJérôme Pouiller if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM)
1174a5fb1bbSJérôme Pouiller skb_queue_tail(&queue->cab, skb);
1184a5fb1bbSJérôme Pouiller else
1194a5fb1bbSJérôme Pouiller skb_queue_tail(&queue->normal, skb);
1204a5fb1bbSJérôme Pouiller }
1214a5fb1bbSJérôme Pouiller
wfx_pending_drop(struct wfx_dev * wdev,struct sk_buff_head * dropped)1224a5fb1bbSJérôme Pouiller void wfx_pending_drop(struct wfx_dev *wdev, struct sk_buff_head *dropped)
1234a5fb1bbSJérôme Pouiller {
1244a5fb1bbSJérôme Pouiller struct wfx_queue *queue;
1254a5fb1bbSJérôme Pouiller struct wfx_vif *wvif;
1264a5fb1bbSJérôme Pouiller struct wfx_hif_msg *hif;
1274a5fb1bbSJérôme Pouiller struct sk_buff *skb;
1284a5fb1bbSJérôme Pouiller
1294a5fb1bbSJérôme Pouiller WARN(!wdev->chip_frozen, "%s should only be used to recover a frozen device", __func__);
1304a5fb1bbSJérôme Pouiller while ((skb = skb_dequeue(&wdev->tx_pending)) != NULL) {
1314a5fb1bbSJérôme Pouiller hif = (struct wfx_hif_msg *)skb->data;
1324a5fb1bbSJérôme Pouiller wvif = wdev_to_wvif(wdev, hif->interface);
1334a5fb1bbSJérôme Pouiller if (wvif) {
1344a5fb1bbSJérôme Pouiller queue = &wvif->tx_queue[skb_get_queue_mapping(skb)];
1354a5fb1bbSJérôme Pouiller WARN_ON(skb_get_queue_mapping(skb) > 3);
1364a5fb1bbSJérôme Pouiller WARN_ON(!atomic_read(&queue->pending_frames));
1374a5fb1bbSJérôme Pouiller atomic_dec(&queue->pending_frames);
1384a5fb1bbSJérôme Pouiller }
1394a5fb1bbSJérôme Pouiller skb_queue_head(dropped, skb);
1404a5fb1bbSJérôme Pouiller }
1414a5fb1bbSJérôme Pouiller }
1424a5fb1bbSJérôme Pouiller
wfx_pending_get(struct wfx_dev * wdev,u32 packet_id)1434a5fb1bbSJérôme Pouiller struct sk_buff *wfx_pending_get(struct wfx_dev *wdev, u32 packet_id)
1444a5fb1bbSJérôme Pouiller {
1454a5fb1bbSJérôme Pouiller struct wfx_queue *queue;
1464a5fb1bbSJérôme Pouiller struct wfx_hif_req_tx *req;
1474a5fb1bbSJérôme Pouiller struct wfx_vif *wvif;
1484a5fb1bbSJérôme Pouiller struct wfx_hif_msg *hif;
1494a5fb1bbSJérôme Pouiller struct sk_buff *skb;
1504a5fb1bbSJérôme Pouiller
1514a5fb1bbSJérôme Pouiller spin_lock_bh(&wdev->tx_pending.lock);
1524a5fb1bbSJérôme Pouiller skb_queue_walk(&wdev->tx_pending, skb) {
1534a5fb1bbSJérôme Pouiller hif = (struct wfx_hif_msg *)skb->data;
1544a5fb1bbSJérôme Pouiller req = (struct wfx_hif_req_tx *)hif->body;
1554a5fb1bbSJérôme Pouiller if (req->packet_id != packet_id)
1564a5fb1bbSJérôme Pouiller continue;
1574a5fb1bbSJérôme Pouiller spin_unlock_bh(&wdev->tx_pending.lock);
1584a5fb1bbSJérôme Pouiller wvif = wdev_to_wvif(wdev, hif->interface);
1594a5fb1bbSJérôme Pouiller if (wvif) {
1604a5fb1bbSJérôme Pouiller queue = &wvif->tx_queue[skb_get_queue_mapping(skb)];
1614a5fb1bbSJérôme Pouiller WARN_ON(skb_get_queue_mapping(skb) > 3);
1624a5fb1bbSJérôme Pouiller WARN_ON(!atomic_read(&queue->pending_frames));
1634a5fb1bbSJérôme Pouiller atomic_dec(&queue->pending_frames);
1644a5fb1bbSJérôme Pouiller }
1654a5fb1bbSJérôme Pouiller skb_unlink(skb, &wdev->tx_pending);
1664a5fb1bbSJérôme Pouiller return skb;
1674a5fb1bbSJérôme Pouiller }
1684a5fb1bbSJérôme Pouiller spin_unlock_bh(&wdev->tx_pending.lock);
1694a5fb1bbSJérôme Pouiller WARN(1, "cannot find packet in pending queue");
1704a5fb1bbSJérôme Pouiller return NULL;
1714a5fb1bbSJérôme Pouiller }
1724a5fb1bbSJérôme Pouiller
wfx_pending_dump_old_frames(struct wfx_dev * wdev,unsigned int limit_ms)1734a5fb1bbSJérôme Pouiller void wfx_pending_dump_old_frames(struct wfx_dev *wdev, unsigned int limit_ms)
1744a5fb1bbSJérôme Pouiller {
1754a5fb1bbSJérôme Pouiller ktime_t now = ktime_get();
1764a5fb1bbSJérôme Pouiller struct wfx_tx_priv *tx_priv;
1774a5fb1bbSJérôme Pouiller struct wfx_hif_req_tx *req;
1784a5fb1bbSJérôme Pouiller struct sk_buff *skb;
1794a5fb1bbSJérôme Pouiller bool first = true;
1804a5fb1bbSJérôme Pouiller
1814a5fb1bbSJérôme Pouiller spin_lock_bh(&wdev->tx_pending.lock);
1824a5fb1bbSJérôme Pouiller skb_queue_walk(&wdev->tx_pending, skb) {
1834a5fb1bbSJérôme Pouiller tx_priv = wfx_skb_tx_priv(skb);
1844a5fb1bbSJérôme Pouiller req = wfx_skb_txreq(skb);
1854a5fb1bbSJérôme Pouiller if (ktime_after(now, ktime_add_ms(tx_priv->xmit_timestamp, limit_ms))) {
1864a5fb1bbSJérôme Pouiller if (first) {
1874a5fb1bbSJérôme Pouiller dev_info(wdev->dev, "frames stuck in firmware since %dms or more:\n",
1884a5fb1bbSJérôme Pouiller limit_ms);
1894a5fb1bbSJérôme Pouiller first = false;
1904a5fb1bbSJérôme Pouiller }
1914a5fb1bbSJérôme Pouiller dev_info(wdev->dev, " id %08x sent %lldms ago\n",
1924a5fb1bbSJérôme Pouiller req->packet_id, ktime_ms_delta(now, tx_priv->xmit_timestamp));
1934a5fb1bbSJérôme Pouiller }
1944a5fb1bbSJérôme Pouiller }
1954a5fb1bbSJérôme Pouiller spin_unlock_bh(&wdev->tx_pending.lock);
1964a5fb1bbSJérôme Pouiller }
1974a5fb1bbSJérôme Pouiller
wfx_pending_get_pkt_us_delay(struct wfx_dev * wdev,struct sk_buff * skb)1984a5fb1bbSJérôme Pouiller unsigned int wfx_pending_get_pkt_us_delay(struct wfx_dev *wdev, struct sk_buff *skb)
1994a5fb1bbSJérôme Pouiller {
2004a5fb1bbSJérôme Pouiller ktime_t now = ktime_get();
2014a5fb1bbSJérôme Pouiller struct wfx_tx_priv *tx_priv = wfx_skb_tx_priv(skb);
2024a5fb1bbSJérôme Pouiller
2034a5fb1bbSJérôme Pouiller return ktime_us_delta(now, tx_priv->xmit_timestamp);
2044a5fb1bbSJérôme Pouiller }
2054a5fb1bbSJérôme Pouiller
wfx_tx_queues_has_cab(struct wfx_vif * wvif)2064a5fb1bbSJérôme Pouiller bool wfx_tx_queues_has_cab(struct wfx_vif *wvif)
2074a5fb1bbSJérôme Pouiller {
208*2c33360bSJaehee Park struct ieee80211_vif *vif = wvif_to_vif(wvif);
2094a5fb1bbSJérôme Pouiller int i;
2104a5fb1bbSJérôme Pouiller
211*2c33360bSJaehee Park if (vif->type != NL80211_IFTYPE_AP)
2124a5fb1bbSJérôme Pouiller return false;
2134a5fb1bbSJérôme Pouiller for (i = 0; i < IEEE80211_NUM_ACS; ++i)
2144a5fb1bbSJérôme Pouiller /* Note: since only AP can have mcast frames in queue and only one vif can be AP,
2154a5fb1bbSJérôme Pouiller * all queued frames has same interface id
2164a5fb1bbSJérôme Pouiller */
2174a5fb1bbSJérôme Pouiller if (!skb_queue_empty_lockless(&wvif->tx_queue[i].cab))
2184a5fb1bbSJérôme Pouiller return true;
2194a5fb1bbSJérôme Pouiller return false;
2204a5fb1bbSJérôme Pouiller }
2214a5fb1bbSJérôme Pouiller
wfx_tx_queue_get_weight(struct wfx_queue * queue)2224a5fb1bbSJérôme Pouiller static int wfx_tx_queue_get_weight(struct wfx_queue *queue)
2234a5fb1bbSJérôme Pouiller {
2244a5fb1bbSJérôme Pouiller return atomic_read(&queue->pending_frames) * queue->priority;
2254a5fb1bbSJérôme Pouiller }
2264a5fb1bbSJérôme Pouiller
wfx_tx_queues_get_skb(struct wfx_dev * wdev)2274a5fb1bbSJérôme Pouiller static struct sk_buff *wfx_tx_queues_get_skb(struct wfx_dev *wdev)
2284a5fb1bbSJérôme Pouiller {
2294a5fb1bbSJérôme Pouiller struct wfx_queue *queues[IEEE80211_NUM_ACS * ARRAY_SIZE(wdev->vif)];
2304a5fb1bbSJérôme Pouiller int i, j, num_queues = 0;
2314a5fb1bbSJérôme Pouiller struct wfx_vif *wvif;
2324a5fb1bbSJérôme Pouiller struct wfx_hif_msg *hif;
2334a5fb1bbSJérôme Pouiller struct sk_buff *skb;
2344a5fb1bbSJérôme Pouiller
2354a5fb1bbSJérôme Pouiller /* sort the queues */
2364a5fb1bbSJérôme Pouiller wvif = NULL;
2374a5fb1bbSJérôme Pouiller while ((wvif = wvif_iterate(wdev, wvif)) != NULL) {
2384a5fb1bbSJérôme Pouiller for (i = 0; i < IEEE80211_NUM_ACS; i++) {
2394a5fb1bbSJérôme Pouiller WARN_ON(num_queues >= ARRAY_SIZE(queues));
2404a5fb1bbSJérôme Pouiller queues[num_queues] = &wvif->tx_queue[i];
2414a5fb1bbSJérôme Pouiller for (j = num_queues; j > 0; j--)
2424a5fb1bbSJérôme Pouiller if (wfx_tx_queue_get_weight(queues[j]) <
2434a5fb1bbSJérôme Pouiller wfx_tx_queue_get_weight(queues[j - 1]))
2444a5fb1bbSJérôme Pouiller swap(queues[j - 1], queues[j]);
2454a5fb1bbSJérôme Pouiller num_queues++;
2464a5fb1bbSJérôme Pouiller }
2474a5fb1bbSJérôme Pouiller }
2484a5fb1bbSJérôme Pouiller
2494a5fb1bbSJérôme Pouiller wvif = NULL;
2504a5fb1bbSJérôme Pouiller while ((wvif = wvif_iterate(wdev, wvif)) != NULL) {
2514a5fb1bbSJérôme Pouiller if (!wvif->after_dtim_tx_allowed)
2524a5fb1bbSJérôme Pouiller continue;
2534a5fb1bbSJérôme Pouiller for (i = 0; i < num_queues; i++) {
2544a5fb1bbSJérôme Pouiller skb = skb_dequeue(&queues[i]->cab);
2554a5fb1bbSJérôme Pouiller if (!skb)
2564a5fb1bbSJérôme Pouiller continue;
2574a5fb1bbSJérôme Pouiller /* Note: since only AP can have mcast frames in queue and only one vif can
2584a5fb1bbSJérôme Pouiller * be AP, all queued frames has same interface id
2594a5fb1bbSJérôme Pouiller */
2604a5fb1bbSJérôme Pouiller hif = (struct wfx_hif_msg *)skb->data;
2614a5fb1bbSJérôme Pouiller WARN_ON(hif->interface != wvif->id);
2624a5fb1bbSJérôme Pouiller WARN_ON(queues[i] != &wvif->tx_queue[skb_get_queue_mapping(skb)]);
2634a5fb1bbSJérôme Pouiller atomic_inc(&queues[i]->pending_frames);
2644a5fb1bbSJérôme Pouiller trace_queues_stats(wdev, queues[i]);
2654a5fb1bbSJérôme Pouiller return skb;
2664a5fb1bbSJérôme Pouiller }
2674a5fb1bbSJérôme Pouiller /* No more multicast to sent */
2684a5fb1bbSJérôme Pouiller wvif->after_dtim_tx_allowed = false;
2694a5fb1bbSJérôme Pouiller schedule_work(&wvif->update_tim_work);
2704a5fb1bbSJérôme Pouiller }
2714a5fb1bbSJérôme Pouiller
2724a5fb1bbSJérôme Pouiller for (i = 0; i < num_queues; i++) {
2734a5fb1bbSJérôme Pouiller skb = skb_dequeue(&queues[i]->normal);
2744a5fb1bbSJérôme Pouiller if (skb) {
2754a5fb1bbSJérôme Pouiller atomic_inc(&queues[i]->pending_frames);
2764a5fb1bbSJérôme Pouiller trace_queues_stats(wdev, queues[i]);
2774a5fb1bbSJérôme Pouiller return skb;
2784a5fb1bbSJérôme Pouiller }
2794a5fb1bbSJérôme Pouiller }
2804a5fb1bbSJérôme Pouiller return NULL;
2814a5fb1bbSJérôme Pouiller }
2824a5fb1bbSJérôme Pouiller
wfx_tx_queues_get(struct wfx_dev * wdev)2834a5fb1bbSJérôme Pouiller struct wfx_hif_msg *wfx_tx_queues_get(struct wfx_dev *wdev)
2844a5fb1bbSJérôme Pouiller {
2854a5fb1bbSJérôme Pouiller struct wfx_tx_priv *tx_priv;
2864a5fb1bbSJérôme Pouiller struct sk_buff *skb;
2874a5fb1bbSJérôme Pouiller
2884a5fb1bbSJérôme Pouiller if (atomic_read(&wdev->tx_lock))
2894a5fb1bbSJérôme Pouiller return NULL;
2904a5fb1bbSJérôme Pouiller skb = wfx_tx_queues_get_skb(wdev);
2914a5fb1bbSJérôme Pouiller if (!skb)
2924a5fb1bbSJérôme Pouiller return NULL;
2934a5fb1bbSJérôme Pouiller skb_queue_tail(&wdev->tx_pending, skb);
2944a5fb1bbSJérôme Pouiller wake_up(&wdev->tx_dequeue);
2954a5fb1bbSJérôme Pouiller tx_priv = wfx_skb_tx_priv(skb);
2964a5fb1bbSJérôme Pouiller tx_priv->xmit_timestamp = ktime_get();
2974a5fb1bbSJérôme Pouiller return (struct wfx_hif_msg *)skb->data;
2984a5fb1bbSJérôme Pouiller }
299