wifi: wfx: allow to send frames during ROC
authorJérôme Pouiller <jerome.pouiller@silabs.com>
Wed, 4 Oct 2023 17:28:42 +0000 (19:28 +0200)
committerKalle Valo <kvalo@kernel.org>
Mon, 9 Oct 2023 06:53:07 +0000 (09:53 +0300)
Until now, all the traffic was blocked during scan operation. However,
scan operation is going to be used to implement Remain On Channel (ROC).
In this case, special frames (marked with IEEE80211_TX_CTL_TX_OFFCHAN)
must be sent during the operation.

These frames need to be sent on the virtual interface #2. Until now,
this interface was only used by the device for internal purpose. But
since API 3.9, it can be used to send data during scan operation (we
hijack the scan process to implement ROC).

Thus, we need to change a bit the way we match the frames with the
interface.

Fortunately, the frames received during the scan are marked with the
correct interface number. So there is no change to do on this part.

Signed-off-by: Jérôme Pouiller <jerome.pouiller@silabs.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20231004172843.195332-8-jerome.pouiller@silabs.com
drivers/net/wireless/silabs/wfx/data_tx.c
drivers/net/wireless/silabs/wfx/data_tx.h
drivers/net/wireless/silabs/wfx/queue.c
drivers/net/wireless/silabs/wfx/queue.h

index ce2b5dcfd8d89e1a3f4ee915615c3e0c1d5b4ba2..e8b6d41f5519637bddca8ea30faf5d5d67476c16 100644 (file)
@@ -226,6 +226,18 @@ struct wfx_hif_req_tx *wfx_skb_txreq(struct sk_buff *skb)
        return req;
 }
 
+struct wfx_vif *wfx_skb_wvif(struct wfx_dev *wdev, struct sk_buff *skb)
+{
+       struct wfx_tx_priv *tx_priv = wfx_skb_tx_priv(skb);
+       struct wfx_hif_msg *hif = (struct wfx_hif_msg *)skb->data;
+
+       if (tx_priv->vif_id != hif->interface && hif->interface != 2) {
+               dev_err(wdev->dev, "corrupted skb");
+               return wdev_to_wvif(wdev, hif->interface);
+       }
+       return wdev_to_wvif(wdev, tx_priv->vif_id);
+}
+
 static u8 wfx_tx_get_link_id(struct wfx_vif *wvif, struct ieee80211_sta *sta,
                             struct ieee80211_hdr *hdr)
 {
@@ -352,6 +364,7 @@ static int wfx_tx_inner(struct wfx_vif *wvif, struct ieee80211_sta *sta, struct
        /* Fill tx_priv */
        tx_priv = (struct wfx_tx_priv *)tx_info->rate_driver_data;
        tx_priv->icv_size = wfx_tx_get_icv_len(hw_key);
+       tx_priv->vif_id = wvif->id;
 
        /* Fill hif_msg */
        WARN(skb_headroom(skb) < wmsg_len, "not enough space in skb");
@@ -362,7 +375,10 @@ static int wfx_tx_inner(struct wfx_vif *wvif, struct ieee80211_sta *sta, struct
        hif_msg = (struct wfx_hif_msg *)skb->data;
        hif_msg->len = cpu_to_le16(skb->len);
        hif_msg->id = HIF_REQ_ID_TX;
-       hif_msg->interface = wvif->id;
+       if (tx_info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
+               hif_msg->interface = 2;
+       else
+               hif_msg->interface = wvif->id;
        if (skb->len > le16_to_cpu(wvif->wdev->hw_caps.size_inp_ch_buf)) {
                dev_warn(wvif->wdev->dev,
                         "requested frame size (%d) is larger than maximum supported (%d)\n",
@@ -383,9 +399,15 @@ static int wfx_tx_inner(struct wfx_vif *wvif, struct ieee80211_sta *sta, struct
        req->fc_offset = offset;
        /* Queue index are inverted between firmware and Linux */
        req->queue_id = 3 - queue_id;
-       req->peer_sta_id = wfx_tx_get_link_id(wvif, sta, hdr);
-       req->retry_policy_index = wfx_tx_get_retry_policy_id(wvif, tx_info);
-       req->frame_format = wfx_tx_get_frame_format(tx_info);
+       if (tx_info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
+               req->peer_sta_id = HIF_LINK_ID_NOT_ASSOCIATED;
+               req->retry_policy_index = HIF_TX_RETRY_POLICY_INVALID;
+               req->frame_format = HIF_FRAME_FORMAT_NON_HT;
+       } else {
+               req->peer_sta_id = wfx_tx_get_link_id(wvif, sta, hdr);
+               req->retry_policy_index = wfx_tx_get_retry_policy_id(wvif, tx_info);
+               req->frame_format = wfx_tx_get_frame_format(tx_info);
+       }
        if (tx_info->driver_rates[0].flags & IEEE80211_TX_RC_SHORT_GI)
                req->short_gi = 1;
        if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM)
@@ -501,7 +523,7 @@ void wfx_tx_confirm_cb(struct wfx_dev *wdev, const struct wfx_hif_cnf_tx *arg)
        }
        tx_info = IEEE80211_SKB_CB(skb);
        tx_priv = wfx_skb_tx_priv(skb);
-       wvif = wdev_to_wvif(wdev, ((struct wfx_hif_msg *)skb->data)->interface);
+       wvif = wfx_skb_wvif(wdev, skb);
        WARN_ON(!wvif);
        if (!wvif)
                return;
@@ -563,7 +585,6 @@ void wfx_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 queues, b
        struct wfx_dev *wdev = hw->priv;
        struct sk_buff_head dropped;
        struct wfx_vif *wvif;
-       struct wfx_hif_msg *hif;
        struct sk_buff *skb;
 
        skb_queue_head_init(&dropped);
@@ -579,8 +600,7 @@ void wfx_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 queues, b
        if (wdev->chip_frozen)
                wfx_pending_drop(wdev, &dropped);
        while ((skb = skb_dequeue(&dropped)) != NULL) {
-               hif = (struct wfx_hif_msg *)skb->data;
-               wvif = wdev_to_wvif(wdev, hif->interface);
+               wvif = wfx_skb_wvif(wdev, skb);
                ieee80211_tx_info_clear_status(IEEE80211_SKB_CB(skb));
                wfx_skb_dtor(wvif, skb);
        }
index a5b80eacce39ace6a1a54442a284f4d18f7354f0..0621b82103bef898ddb7411c96ce9408192332be 100644 (file)
@@ -36,6 +36,7 @@ struct wfx_tx_policy_cache {
 struct wfx_tx_priv {
        ktime_t xmit_timestamp;
        unsigned char icv_size;
+       unsigned char vif_id;
 };
 
 void wfx_tx_policy_init(struct wfx_vif *wvif);
@@ -47,5 +48,6 @@ void wfx_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 queues, b
 
 struct wfx_tx_priv *wfx_skb_tx_priv(struct sk_buff *skb);
 struct wfx_hif_req_tx *wfx_skb_txreq(struct sk_buff *skb);
+struct wfx_vif *wfx_skb_wvif(struct wfx_dev *wdev, struct sk_buff *skb);
 
 #endif
index 37f492e5d3bea5dcf16f375e69a296fcf12d3734..e61b86f211e53872dc60883b67759e03f5958c86 100644 (file)
@@ -68,13 +68,16 @@ void wfx_tx_queues_init(struct wfx_vif *wvif)
        for (i = 0; i < IEEE80211_NUM_ACS; ++i) {
                skb_queue_head_init(&wvif->tx_queue[i].normal);
                skb_queue_head_init(&wvif->tx_queue[i].cab);
+               skb_queue_head_init(&wvif->tx_queue[i].offchan);
                wvif->tx_queue[i].priority = priorities[i];
        }
 }
 
 bool wfx_tx_queue_empty(struct wfx_vif *wvif, struct wfx_queue *queue)
 {
-       return skb_queue_empty_lockless(&queue->normal) && skb_queue_empty_lockless(&queue->cab);
+       return skb_queue_empty_lockless(&queue->normal) &&
+              skb_queue_empty_lockless(&queue->cab) &&
+              skb_queue_empty_lockless(&queue->offchan);
 }
 
 void wfx_tx_queues_check_empty(struct wfx_vif *wvif)
@@ -103,8 +106,9 @@ static void __wfx_tx_queue_drop(struct wfx_vif *wvif,
 void wfx_tx_queue_drop(struct wfx_vif *wvif, struct wfx_queue *queue,
                       struct sk_buff_head *dropped)
 {
-       __wfx_tx_queue_drop(wvif, &queue->cab, dropped);
        __wfx_tx_queue_drop(wvif, &queue->normal, dropped);
+       __wfx_tx_queue_drop(wvif, &queue->cab, dropped);
+       __wfx_tx_queue_drop(wvif, &queue->offchan, dropped);
        wake_up(&wvif->wdev->tx_dequeue);
 }
 
@@ -113,7 +117,9 @@ void wfx_tx_queues_put(struct wfx_vif *wvif, struct sk_buff *skb)
        struct wfx_queue *queue = &wvif->tx_queue[skb_get_queue_mapping(skb)];
        struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
 
-       if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM)
+       if (tx_info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
+               skb_queue_tail(&queue->offchan, skb);
+       else if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM)
                skb_queue_tail(&queue->cab, skb);
        else
                skb_queue_tail(&queue->normal, skb);
@@ -123,13 +129,11 @@ void wfx_pending_drop(struct wfx_dev *wdev, struct sk_buff_head *dropped)
 {
        struct wfx_queue *queue;
        struct wfx_vif *wvif;
-       struct wfx_hif_msg *hif;
        struct sk_buff *skb;
 
        WARN(!wdev->chip_frozen, "%s should only be used to recover a frozen device", __func__);
        while ((skb = skb_dequeue(&wdev->tx_pending)) != NULL) {
-               hif = (struct wfx_hif_msg *)skb->data;
-               wvif = wdev_to_wvif(wdev, hif->interface);
+               wvif = wfx_skb_wvif(wdev, skb);
                if (wvif) {
                        queue = &wvif->tx_queue[skb_get_queue_mapping(skb)];
                        WARN_ON(skb_get_queue_mapping(skb) > 3);
@@ -155,7 +159,7 @@ struct sk_buff *wfx_pending_get(struct wfx_dev *wdev, u32 packet_id)
                if (req->packet_id != packet_id)
                        continue;
                spin_unlock_bh(&wdev->tx_pending.lock);
-               wvif = wdev_to_wvif(wdev, hif->interface);
+               wvif = wfx_skb_wvif(wdev, skb);
                if (wvif) {
                        queue = &wvif->tx_queue[skb_get_queue_mapping(skb)];
                        WARN_ON(skb_get_queue_mapping(skb) > 3);
@@ -246,6 +250,26 @@ static struct sk_buff *wfx_tx_queues_get_skb(struct wfx_dev *wdev)
                }
        }
 
+       wvif = NULL;
+       while ((wvif = wvif_iterate(wdev, wvif)) != NULL) {
+               for (i = 0; i < num_queues; i++) {
+                       skb = skb_dequeue(&queues[i]->offchan);
+                       if (!skb)
+                               continue;
+                       hif = (struct wfx_hif_msg *)skb->data;
+                       /* Offchan frames are assigned to a special interface.
+                        * The only interface allowed to send data during scan.
+                        */
+                       WARN_ON(hif->interface != 2);
+                       atomic_inc(&queues[i]->pending_frames);
+                       trace_queues_stats(wdev, queues[i]);
+                       return skb;
+               }
+       }
+
+       if (mutex_is_locked(&wdev->scan_lock))
+               return NULL;
+
        wvif = NULL;
        while ((wvif = wvif_iterate(wdev, wvif)) != NULL) {
                if (!wvif->after_dtim_tx_allowed)
index 4731debca93d25ab77bdd42f3d5871b1b0d459ba..6857fbd60fbad58eeae1121b37f20154c54302ff 100644 (file)
@@ -17,6 +17,7 @@ struct wfx_vif;
 struct wfx_queue {
        struct sk_buff_head normal;
        struct sk_buff_head cab; /* Content After (DTIM) Beacon */
+       struct sk_buff_head offchan;
        atomic_t            pending_frames;
        int                 priority;
 };