Skip to content

LTE-3100: DFS channel can't be reuse for BH on nop_started - #1542

Open
poornakumar-pk wants to merge 1 commit into
rdkcentral:mainfrom
poornakumar-pk:XLE-3100-stable2
Open

poornakumar-pk wants to merge 1 commit into
rdkcentral:mainfrom
poornakumar-pk:XLE-3100-stable2

Conversation

@poornakumar-pk

@poornakumar-pk poornakumar-pk commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Reason for change: It prevents the STA connection attempt when the candidate is invalid
Test Procedure: check XLE not use same channel while it is in the nop_started state
Risks:
Priority: P1
Signed-off-by: poornakumar_mezhiselvam@comcast.com

Copilot AI balanced review requested due to automatic review settings October 22, 2026 09:33
@poornakumar-pk poornakumar-pk changed the title LTE-0: Backport DFS NOP candidate validation to stable2 LTE-3100: DFS channel can't be reuse for BH on nop_started Oct 22, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new validation relies on incorrect frequency conversion that rejects valid backhaul candidates.

1 open issue
What changed in this PR

Backports #1490’s DFS non-occupancy protection to stable2, validating backhaul candidates before attempting a connection.

Changes:

  • Validates cached and scanned candidates against radio capabilities and runtime channel availability before calling wifi_hal_connect().
  • Updates candidate state and reschedules connection selection after rejection.
  • Extends radar checks to valid 160 MHz operating blocks, including those with non-DFS primary channels.
File Description
source/​utils/​wifi_util.c Expands DFS runtime checks to cover 160 MHz operating blocks.
source/​core/​services/​vap_svc_mesh_ext.c Adds candidate validation and rejection handling before backhaul connection attempts.

Review effort: Balanced

Comment thread source/core/services/vap_svc_mesh_ext.c Outdated
Copilot AI balanced review requested due to automatic review settings October 22, 2026 10:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Candidate validation uses the cached radio width, which can reject valid backhaul targets or miss unavailable secondary DFS channels.

1 open issue
1 resolved since last review

Review effort: Balanced

Comment thread source/core/services/vap_svc_mesh_ext.c
@mateuszCieslak-GL mateuszCieslak-GL added bug Something isn't working pending main labels Oct 22, 2026
Reason for change: It prevents the STA connection attempt when the candidate is invalid
Test Procedure: check XLE not use same channel while it is in the nop_started state
Risks:
Priority: P1
Signed-off-by: poornakumar_mezhiselvam@comcast.com
Copilot AI balanced review requested due to automatic review settings October 22, 2026 11:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The shared validator now rejects HAL-supported 80+80 MHz radio configurations outside the backhaul connection path.

1 open issue
1 resolved since last review

Review effort: Balanced

Comment thread source/utils/wifi_util.c
@mateuszCieslak-GL

Copy link
Copy Markdown
Contributor

@poornakumar-pk thanks for the band-aware converter, the retry on allocation failure and the width update in the latest push. Taking the smaller of the parent's width and the radio's width fixes the false rejections (149/80 with a radio last at 160 MHz, 165/20).

Three gaps remain:

  • The radio width isn't a cap on the next link. After connect, vap_svc_mesh_ext.c overwrites radio_params->channelWidth with the negotiated STA width, so it's just the previous parent's width. Example: radio last at 80 MHz, new parent on 36/160 with 52–64 in NOP. The check runs on 36/80 and passes, and if the STA negotiates 160 the link covers the NOP channels.
  • With DFS disabled on the extender, every parent on 52–144 is still rejected, not only channels in NOP (see LTE-3100: DFS channel can't be reuse for BH on nop_started #1540).
  • A new_bss set over webconfig carries no width, so it's still checked with the cached width.

Proposal: check only what this PR is about, the NOP state of the parent's whole operating block, and keep the radio-config validator out of the connect path. The parent's block always contains the block the extender will use, so no NOP channel is missed. The cost is a conservative reject when the extender can't use the full width. It also removes the 100 KB copy per attempt and the width special cases. Patch on top of the current head:

diff --git a/source/core/services/vap_svc_mesh_ext.c b/source/core/services/vap_svc_mesh_ext.c
--- a/source/core/services/vap_svc_mesh_ext.c
+++ b/source/core/services/vap_svc_mesh_ext.c
@@ -1054,6 +1054,41 @@ static void reset_sta_state(vap_svc_t *svc, unsigned int vap_index)
     }
 }
 
+/* True if any 20 MHz channel of the 5 GHz block (channel, width) is in DFS non-occupancy */
+static bool ext_channel_block_in_nop(wifi_radio_operationParam_t *radio_params,
+    unsigned int channel, wifi_channelBandwidth_t width)
+{
+    unsigned int i, span, first;
+    int ch;
+
+    if (radio_params->band != WIFI_FREQUENCY_5_BAND &&
+        radio_params->band != WIFI_FREQUENCY_5L_BAND &&
+        radio_params->band != WIFI_FREQUENCY_5H_BAND) {
+        return false;
+    }
+
+    // block size in channel numbers, one 20 MHz channel is 4
+    if (width == WIFI_CHANNELBANDWIDTH_160MHZ || width == WIFI_CHANNELBANDWIDTH_320MHZ) {
+        span = 32;
+    } else if (width == WIFI_CHANNELBANDWIDTH_80MHZ || width == WIFI_CHANNELBANDWIDTH_80_80MHZ) {
+        span = 16;
+    } else if (width == WIFI_CHANNELBANDWIDTH_40MHZ) {
+        span = 8;
+    } else {
+        span = 4;
+    }
+    first = 36 + (channel - 36) / span * span;
+
+    for (i = 0; i < sizeof(radio_params->channel_map) / sizeof(radio_params->channel_map[0]); i++) {
+        ch = radio_params->channel_map[i].ch_number;
+        if (ch >= (int)first && ch < (int)(first + span) &&
+            radio_params->channel_map[i].ch_state == CHAN_STATE_DFS_NOP_START) {
+            return true;
+        }
+    }
+    return false;
+}
+
 void ext_try_connecting(vap_svc_t *svc)
 {
     vap_svc_ext_t   *ext;
@@ -1068,7 +1103,7 @@ void ext_try_connecting(vap_svc_t *svc)
     wifi_ctrl_t *ctrl;
     wifi_mgr_t *mgr;
     wifi_radio_operationParam_t *radio_params;
-    wifi_radio_operationParam_t *candidate_params;
+    wifi_channelBandwidth_t candidate_bw;
     unsigned char candidate_channel;
 
     ctrl = svc->ctrl;
@@ -1183,51 +1218,15 @@ void ext_try_connecting(vap_svc_t *svc)
         candidate_channel = 0;
         convert_freq_to_channel(candidate->radio_freq_band, candidate->external_ap.freq,
             &candidate_channel);
-        candidate_params = (wifi_radio_operationParam_t *)malloc(sizeof(wifi_radio_operationParam_t));
-        if (candidate_params == NULL) {
-            wifi_util_error_print(WIFI_CTRL, "%s:%d: failed to allocate candidate radio parameters\n",
-                __func__, __LINE__);
-            schedule_connect_sm(svc);
-            return;
-        }
-
+        // check the parent's whole block; fall back to the radio width if the parent's is unknown
+        candidate_bw = candidate->external_ap.oper_chan_bw;
         pthread_mutex_lock(&mgr->data_cache_lock);
-        memcpy(candidate_params, radio_params, sizeof(wifi_radio_operationParam_t));
-        pthread_mutex_unlock(&mgr->data_cache_lock);
-
-        candidate_params->channel = candidate_channel;
-        /*
-         * When AP width is unknown, retain the configured STA width so DFS
-         * validation does not assume a narrower channel block.
-         */
-        if (candidate->external_ap.oper_chan_bw != 0) {
-            if ((candidate->radio_freq_band == WIFI_FREQUENCY_5_BAND ||
-                    candidate->radio_freq_band == WIFI_FREQUENCY_5L_BAND ||
-                    candidate->radio_freq_band == WIFI_FREQUENCY_5H_BAND) &&
-                candidate->external_ap.oper_chan_bw == WIFI_CHANNELBANDWIDTH_80_80MHZ) {
-                if (radio_params->channelWidth <= WIFI_CHANNELBANDWIDTH_80MHZ) {
-                    candidate_params->channelWidth = radio_params->channelWidth;
-                } else {
-                    /*
-                     * BSS information does not provide the second 80 MHz
-                     * segment, so its DFS/NOP state cannot be validated.
-                     */
-                    candidate_params->channelWidth = WIFI_CHANNELBANDWIDTH_80_80MHZ;
-                }
-            } else {
-                if (radio_params->channelWidth == WIFI_CHANNELBANDWIDTH_80_80MHZ) {
-                    candidate_params->channelWidth = WIFI_CHANNELBANDWIDTH_80MHZ;
-                }
-                if (candidate->external_ap.oper_chan_bw < candidate_params->channelWidth) {
-                    candidate_params->channelWidth = candidate->external_ap.oper_chan_bw;
-                }
-            }
+        if (candidate_bw == 0) {
+            candidate_bw = radio_params->channelWidth;
         }
-
         candidate_valid = (candidate_channel != 0) &&
-            (wifi_radio_operationParam_validation(&mgr->hal_cap, candidate_params) == RETURN_OK);
-        free(candidate_params);
-        candidate_params = NULL;
+            !ext_channel_block_in_nop(radio_params, candidate_channel, candidate_bw);
+        pthread_mutex_unlock(&mgr->data_cache_lock);
 
         if (!candidate_valid) {
             wifi_util_error_print(WIFI_CTRL,

When the parent's width is unknown (new_bss), the check falls back to the radio's current width, as now. This drops the validator's DfsEnabled and channel-list checks from this path. If rejecting DFS parents with local DFS off is intended, I'd add it as an explicit check with its own log line.

I tested it with an RPi build and link, plus a table test that runs the real helper, validator and converter code over 25 scenarios, with your current push as a column. Your push misses only the 36/160 NOP case, the DFS-off case and the unknown-width cases. The same change applies on develop on top of this PR, so could you open the develop follow-up as well?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working pending main

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants