From 1d1e43ca14cb5ca722a8343b92d4428ae2a1db4f Mon Sep 17 00:00:00 2001 From: Jakob Olsson <jakob.olsson@iopsys.eu> Date: Tue, 6 May 2025 11:49:14 +0200 Subject: [PATCH 01/10] scan: don't split report into multiple CMDUs --- src/agent.c | 105 +----------------------------------------------- src/agent_tlv.c | 2 +- 2 files changed, 2 insertions(+), 105 deletions(-) diff --git a/src/agent.c b/src/agent.c index 187711c53..0d9395ca6 100644 --- a/src/agent.c +++ b/src/agent.c @@ -8725,108 +8725,6 @@ struct cmdu_buff *agent_prepare_scan_cmdu(struct agent *a, struct tlv *tsp) return cmdu_data; } -/* Splits Channel Scan Report TLVs into fragment CMDUs. - * Sends fragmet CMDUs to controller. - */ -static int agent_send_ch_scan_rsp_frag(struct agent *a, struct cmdu_buff *cmdu) -{ - trace("agent: %s: --->\n", __func__); - - struct tlv_policy d_policy_scan[] = { - [0] = { - .type = MAP_TLV_CHANNEL_SCAN_RES, - .present = TLV_PRESENT_MORE, - .minlen = 9 - } - }; - /* Timestamp TLV */ - struct tlv *tv[CHAN_SCAN_REPORT_NUM_OF_TLV_TYPES][TLV_MAXNUM] = {0}; - struct tlv *tv_scan[256] = {0}; /* Channel Scan Results TLVs */ - int i; - int num_tlv = 256, num_tlv_copied = 0; - int ret = 0; - struct cmdu_buff *frag_cmdu = NULL; - - if (cmdu->datalen < CH_SCAN_RESP_CMDU_MAX_LEN) - /* No fragmentation required, just send the CMDU as is */ - return agent_send_cmdu(a, cmdu); - - /* If the number of neighbors detected during a channel scan would - * mean that the channel scan report message would not fit within - * one 1905 CMDU, the Multi-AP Agent shall split the channel scan - * report across multiple Channel Scan Result TLVs by splitting - * the information related to sets of neighbor BSSs into separate - * Channel Scan Result TLVs and setting the NumberofNeighbors field - * to the number of neighbors contained in the corresponding TLV. - */ - - /* Note: assuming neighbors are already evenly split between TLVs */ - - ret = map_cmdu_validate_parse(cmdu, tv, ARRAY_SIZE(tv), a->cfg.map_profile); - if (!ret) { - dbg("%s: map_cmdu_validate_parse failed, err = (%d) '%s'\n", __func__, - map_error, map_strerror(map_error)); - return -1; - } - - if (!tv[CHAN_SCAN_REPORT_TIMESTAMP_IDX][0]) { - dbg("%s: Missing TIMESTAMP_TLV!\n", __func__); - return -1; - } - - ret = cmdu_parse_tlv_single(cmdu, tv_scan, d_policy_scan, &num_tlv); - if (ret) { - dbg("%s: cmdu_parse_tlv_single failed, err = (%d) '%s'\n", - __func__, map_error, map_strerror(map_error)); - return -1; - } - - if (!tv_scan[0]) { - dbg("%s: Missing CHANNEL SCAN RESULT_TLV!\n", __func__); - return -1; - } - - tv[CHAN_SCAN_REPORT_TIMESTAMP_IDX][0]->len = - tlv_length(tv[CHAN_SCAN_REPORT_TIMESTAMP_IDX][0]); // FIXME - frag_cmdu = agent_prepare_scan_cmdu(a, tv[CHAN_SCAN_REPORT_TIMESTAMP_IDX][0]); - if (WARN_ON(!frag_cmdu)) - return -1; - - for (i = 0; i < num_tlv; i++) { - uint16_t tlv_len = tlv_length(tv_scan[i]); - - if (tlv_total_length(tv_scan[i]) > frag_cmdu->end - frag_cmdu->tail) { - /* No space left for current TLV in CMDU buffer */ - cmdu_put_eom(frag_cmdu); - agent_send_cmdu(a, frag_cmdu); - cmdu_free(frag_cmdu); - - num_tlv_copied = 0; - - /* Create next fragment and put next TLVs into it */ - frag_cmdu = agent_prepare_scan_cmdu(a, tv[CHAN_SCAN_REPORT_TIMESTAMP_IDX][0]); - } - - /* Add TLV to CMDU & continue */ - tv_scan[i]->len = tlv_len; // FIXME: assign len smwhr else - if (cmdu_copy_tlvs(frag_cmdu, &tv_scan[i], 1)) { - dbg("%s:%d copy TLVs failed, end = %p, tail = %p, len = %d\n", - __func__, __LINE__, - frag_cmdu->end, frag_cmdu->tail, tv_scan[i]->len); - return -1; - } - num_tlv_copied++; - } - - if (num_tlv_copied) { /* avoid sending timestamp alone */ - cmdu_put_eom(frag_cmdu); - agent_send_cmdu(a, frag_cmdu); - } - - cmdu_free(frag_cmdu); - - return 0; -} int agent_send_ch_scan_response(struct agent *a, struct wifi_radio_element *re, struct wifi_scan_request_radio *req) @@ -8859,8 +8757,7 @@ int agent_send_ch_scan_response(struct agent *a, struct wifi_radio_element *re, } /* Send the response cmdu */ - ret = agent_send_ch_scan_rsp_frag(a, cmdu_data); - + agent_send_cmdu(a, cmdu_data); cmdu_free(cmdu_data); return ret; diff --git a/src/agent_tlv.c b/src/agent_tlv.c index a45236950..39481b9dc 100644 --- a/src/agent_tlv.c +++ b/src/agent_tlv.c @@ -3231,7 +3231,7 @@ int agent_gen_ch_scan_response_tlv(struct agent *a, struct cmdu_buff *cmdu, uint32_t scan_duration = 0; uint32_t num_nbr = 0; - trace("\t INFO: radio " MACFMT ", opclass %d, channel %d\n", + trace("\t %s:INFO: radio " MACFMT ", opclass %d, channel %d\n", __func__, MAC2STR(radio_mac), opclass_id, ch->channel); get_timestamp(NULL, tsp); -- GitLab From f9c3613689831b61752dd283fab656a663df97dc Mon Sep 17 00:00:00 2001 From: Jakob Olsson <jakob.olsson@iopsys.eu> Date: Tue, 6 May 2025 11:52:56 +0200 Subject: [PATCH 02/10] scan: always use new mid for scan report --- src/agent_cmdu.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/agent_cmdu.c b/src/agent_cmdu.c index de97f9ba2..eafac4ca5 100644 --- a/src/agent_cmdu.c +++ b/src/agent_cmdu.c @@ -1047,9 +1047,6 @@ struct cmdu_buff *agent_gen_ch_scan_response_radio(struct agent *a, /* Define the cmdu */ cmdu_set_type(cmdu_data, CMDU_CHANNEL_SCAN_REPORT); - - cmdu_set_mid(cmdu_data, req->mid); - memcpy(cmdu_data->origin, a->cntlr_almac, 6); /* Define the TLVs */ -- GitLab From 05b4ced018781de16217574d311bd3ebf4399c90 Mon Sep 17 00:00:00 2001 From: Jakob Olsson <jakob.olsson@iopsys.eu> Date: Tue, 6 May 2025 16:49:09 +0200 Subject: [PATCH 03/10] scan: set scanning state based on UBUS API return code --- src/agent.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/agent.c b/src/agent.c index 0d9395ca6..7849523cb 100644 --- a/src/agent.c +++ b/src/agent.c @@ -837,13 +837,11 @@ int wifi_radio_scan_req(struct agent *a, struct wifi_radio_element *re, } if (re) { - re->scan_state = SCAN_REQUESTED; return wifi_scan(re->name, ¶m, num_opclass, opclass, - num_channel, channel); + num_channel, channel); } list_for_each_entry(re, &a->radiolist, list) { - re->scan_state = SCAN_REQUESTED; res |= wifi_scan(re->name, ¶m, num_opclass, opclass, num_channel, channel); } @@ -7015,13 +7013,19 @@ static void agent_boot_scan_cb(atimer_t *t) return; list_for_each_entry(re, &a->radiolist, list) { + int ret; + if (!re->enabled) { radio_missing = true; continue; } - if (re->scan_state != SCAN_DONE) - wifi_radio_scan_req_all(a); + if (re->scan_state == SCAN_DONE) + continue; + + ret = wifi_radio_scan_req_all(a); + if (!ret) + re->scan_state = SCAN_REQUESTED; } if (a->boot_scan_tries++ < BOOT_UP_SCAN_MAX_TRY -- GitLab From 90301e244db6bf13853c718231b3bda8fc898d18 Mon Sep 17 00:00:00 2001 From: Jakob Olsson <jakob.olsson@iopsys.eu> Date: Tue, 6 May 2025 16:50:19 +0200 Subject: [PATCH 04/10] scan: don't reset scan timeout upon new requests while scan ongoing --- src/agent.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/agent.c b/src/agent.c index 7849523cb..b384845af 100644 --- a/src/agent.c +++ b/src/agent.c @@ -8752,18 +8752,9 @@ int agent_send_ch_scan_response(struct agent *a, struct wifi_radio_element *re, if (!cmdu_data) return -1; - /* Status will be SUCCESS once results available */ - if (req && req->status == CH_SCAN_STATUS_SCAN_NOT_COMPLETED) { - /* Replace old request data with the new one */ - re->scan_req = *req; - /* Reset the timer of available time (5min) */ - timer_set(&re->available_scan_timer, 300000); - } - /* Send the response cmdu */ agent_send_cmdu(a, cmdu_data); cmdu_free(cmdu_data); - return ret; } -- GitLab From 6bb0d8684c7c2c5d388f9abb3dde0d691261d2dc Mon Sep 17 00:00:00 2001 From: Jakob Olsson <jakob.olsson@iopsys.eu> Date: Wed, 7 May 2025 12:46:47 +0200 Subject: [PATCH 05/10] scan: fix handling of opclass based requests --- src/agent.c | 17 +++++++++++------ src/agent_cmdu.c | 13 +++++++++++-- src/utils/utils.c | 24 ++++++++++++++++++++++++ src/utils/utils.h | 2 ++ src/wifi_opclass.c | 28 ++++++++++++++++++++++++++++ src/wifi_opclass.h | 4 ++++ 6 files changed, 80 insertions(+), 8 deletions(-) diff --git a/src/agent.c b/src/agent.c index b384845af..93bf73bce 100644 --- a/src/agent.c +++ b/src/agent.c @@ -899,16 +899,21 @@ int issue_channel_scan(struct agent *a, struct wifi_radio_element *re, /* num_channel == 0 indicates that the Multi-AP Agent is * requested to scan on all channels in the Operating Class. */ - if (req->opclass[i].num_channel == 0) { - ch_idx = ARRAY_SIZE(channels); + if (req->opclass[i].num_channel == 0 && ch_idx < ARRAY_SIZE(channels)) { + int idx = ARRAY_SIZE(channels) - ch_idx; + wifi_opclass_get_supported_ctrl_channels( - &re->opclass, - req->opclass[i].classid, - channels, - &ch_idx); + &re->opclass, + req->opclass[i].classid, + &channels[ch_idx], + &idx); + + ch_idx += idx; } } + ch_idx = remove_uint8_duplicates(channels, ch_idx); + /* Pass all the opclasses & channels from the request */ if (op_idx > 0 || ch_idx > 0) ret = wifi_radio_scan_req(a, re, NULL, op_idx, opclasses, diff --git a/src/agent_cmdu.c b/src/agent_cmdu.c index eafac4ca5..c7b2e69ef 100644 --- a/src/agent_cmdu.c +++ b/src/agent_cmdu.c @@ -893,7 +893,7 @@ static int agent_gen_ch_scan_response_opc(struct agent *a, if (req_opc->num_channel == 0) { /* Report all supported channels in Operating Class */ num_chan = ARRAY_SIZE(channels); - ret = wifi_opclass_get_supported_ctrl_channels(&re->opclass, + ret = wifi_opclass_get_supported_channels(&re->opclass, req_opc->classid, channels, &num_chan); @@ -957,14 +957,23 @@ static int agent_gen_ch_scan_response_opc(struct agent *a, } for (j = 0; j < op->num_channels; j++) { + uint8_t st = status; + ch = op->channel_scanlist + j; if (ch->channel != channels[ci]) continue; + if (!wifi_opclass_id_channel_supported(&re->opclass, + op->opclass, + ch->channel)) { + /* override status for not supported channels */ + st = CH_SCAN_STATUS_SCAN_NOT_SUPPORTED; + } + ret = agent_gen_ch_scan_response_tlv(a, cmdu, re->macaddr, - op->opclass, ch, status); + op->opclass, ch, st); if (ret) return ret; diff --git a/src/utils/utils.c b/src/utils/utils.c index 1f611cd67..f6487c56e 100644 --- a/src/utils/utils.c +++ b/src/utils/utils.c @@ -732,3 +732,27 @@ out: blob_buf_free(&bb); return -1; } + + +/* remove duplicates from a uint8_t array and return the new length */ +int remove_uint8_duplicates(uint8_t *arr, int length) +{ + int i; + + for (i = 0; i < length; i++) { + int j; + + for (j = i + 1; j < length; ) { + if (arr[i] == arr[j]) { + int k; + + for (k = j; k < length - 1; k++) + arr[k] = arr[k + 1]; + length--; + } else + j++; + } + } + + return length; +} diff --git a/src/utils/utils.h b/src/utils/utils.h index e21deeac7..c81f77295 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -180,6 +180,8 @@ uint8_t *blobattrtob(struct blob_attr *attr, uint8_t *out, int size); int get_ethportslist(int *num, char ifs[][16]); +int remove_uint8_duplicates(uint8_t *arr, int length); + #define TS_VID_INVALID 0x0FFF bool is_vid_valid(unsigned int vid); diff --git a/src/wifi_opclass.c b/src/wifi_opclass.c index b8dba1e00..7f9d983b4 100644 --- a/src/wifi_opclass.c +++ b/src/wifi_opclass.c @@ -1181,6 +1181,34 @@ bool wifi_opclass_is_channel_supported(struct wifi_radio_opclass_channel *chan) return true; } +int wifi_opclass_get_supported_channels(struct wifi_radio_opclass *opclass, + uint8_t id, + uint8_t channels[], + int *num_channels) +{ + struct wifi_radio_opclass_entry *entry; + int i; + + if (WARN_ON(!opclass)) + return -1; + + entry = wifi_opclass_find_entry(opclass, id); + if (WARN_ON(!entry)) + return -1; + + if (WARN_ON(entry->channel_num > *num_channels)) + return -1; + + *num_channels = 0; + + for (i = 0; i < entry->channel_num; i++) { + channels[*num_channels] = entry->channel[i].channel; + (*num_channels)++; + } + + return 0; +} + int wifi_opclass_get_supported_ctrl_channels(struct wifi_radio_opclass *opclass, uint8_t id, uint8_t ctrl_channels[], diff --git a/src/wifi_opclass.h b/src/wifi_opclass.h index a5938ba49..ad6576c37 100644 --- a/src/wifi_opclass.h +++ b/src/wifi_opclass.h @@ -86,6 +86,10 @@ int wifi_opclass_id_num_channels_unsupported(struct wifi_radio_opclass *opclass, bool wifi_opclass_id_channel_supported(struct wifi_radio_opclass *opclass, uint8_t id, uint8_t channel); void wifi_opclass_id_set_preferences(struct wifi_radio_opclass *opclass, uint8_t id, uint8_t preference); bool wifi_opclass_is_channel_supported(struct wifi_radio_opclass_channel *chan); +int wifi_opclass_get_supported_channels(struct wifi_radio_opclass *opclass, + uint8_t id, + uint8_t channels[], + int *num_channels); int wifi_opclass_get_supported_ctrl_channels(struct wifi_radio_opclass *opclass, uint8_t id, uint8_t ctrl_channels[], -- GitLab From 8e7f8106c96a2e7a987d0fcc57981fc62fbe5e80 Mon Sep 17 00:00:00 2001 From: Jakob Olsson <jakob.olsson@iopsys.eu> Date: Wed, 7 May 2025 16:29:07 +0200 Subject: [PATCH 06/10] scan: fix rssi handling --- src/agent_tlv.c | 2 +- src/wifi_ubus.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/agent_tlv.c b/src/agent_tlv.c index 39481b9dc..c5300c7e2 100644 --- a/src/agent_tlv.c +++ b/src/agent_tlv.c @@ -3290,7 +3290,7 @@ int agent_gen_ch_scan_response_tlv(struct agent *a, struct cmdu_buff *cmdu, /* ssid */ memcpy(&t->data[offset], nbr->ssid, strlen(nbr->ssid)); offset += strlen(nbr->ssid); - t->data[offset++] = rssi_to_rcpi(nbr->rssi); /* rcpi */ + t->data[offset++] = nbr->rssi; /* rcpi */ t->data[offset++] = strlen(bw_str); //+ 1; /* BW length */ memcpy(&t->data[offset], bw_str, strlen(bw_str)); diff --git a/src/wifi_ubus.c b/src/wifi_ubus.c index 100d0b4e4..751f2a6ff 100644 --- a/src/wifi_ubus.c +++ b/src/wifi_ubus.c @@ -1378,7 +1378,7 @@ static void wifi_ubus_radio_scanresults_cb(struct ubus_request *req, bss->curr_bw = BW20; break; } - bss->rssi = blobmsg_get_u32(bss_tb[4]); + bss->rssi = (int32_t)blobmsg_get_u32(bss_tb[4]); bss->load.sta_count = blobmsg_get_u32(bss_tb[5]); bss->load.utilization = blobmsg_get_u32(bss_tb[6]); -- GitLab From b6dc15d4afd220d6aba40b3dcabed2b65250605c Mon Sep 17 00:00:00 2001 From: Jakob Olsson <jakob.olsson@iopsys.eu> Date: Thu, 8 May 2025 17:23:52 +0200 Subject: [PATCH 07/10] scan: don't trigger scan if no channels or opclasses provided --- src/agent.c | 8 +++++--- src/agent_map.c | 11 ++++++++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/agent.c b/src/agent.c index 93bf73bce..74adcb57b 100644 --- a/src/agent.c +++ b/src/agent.c @@ -877,19 +877,21 @@ int issue_channel_scan(struct agent *a, struct wifi_radio_element *re, if (ch_idx >= ARRAY_SIZE(channels)) break; - if (req->opclass[i].channel[j] > 0) + if (req->opclass[i].channel[j] > 0) { channels[ch_idx++] = req->opclass[i].channel[j]; + } } /* Channels listed explicitly */ if (req->opclass[i].num_channel) continue; - if (req->opclass[i].classid == 0) + if (req->opclass[i].classid == 0) { /* only channels provided, skip opclass */ // TODO: revisit continue; + } if (op_idx >= ARRAY_SIZE(opclasses)) break; @@ -919,7 +921,7 @@ int issue_channel_scan(struct agent *a, struct wifi_radio_element *re, ret = wifi_radio_scan_req(a, re, NULL, op_idx, opclasses, ch_idx, channels); else - ret = wifi_radio_scan_req_all(a); + dbg("%s: no opclass/channel to scan\n", __func__); return ret; } diff --git a/src/agent_map.c b/src/agent_map.c index 6465cefd8..25a2c6f0e 100644 --- a/src/agent_map.c +++ b/src/agent_map.c @@ -5621,8 +5621,17 @@ int handle_channel_scan_request(void *agent, struct cmdu_buff *rx_cmdu, scan_req = &ch_scan_req.radio[i]; re = agent_get_radio(a, scan_req->radio); - if (!re) + if (!re) { + dbg("%s: radio not found:"MACFMT"\n", + __func__, MAC2STR(scan_req->radio)); + continue; + } + + if (scan_req->num_opclass <= 0) { + dbg("%s: no opclass provided, dropping scan req for radio:"MACFMT"\n", + __func__, MAC2STR(scan_req->radio)); continue; + } /* 'Pefrorm Fresh Scan' while 'On boot only' set in Caps */ if (a->cfg.scan_on_boot_only -- GitLab From cbdf8737bd807b6c6bc12376bcd8612ce47bfc92 Mon Sep 17 00:00:00 2001 From: Jakob Olsson <jakob.olsson@iopsys.eu> Date: Fri, 9 May 2025 15:28:12 +0200 Subject: [PATCH 08/10] scan: fix reporting stored scan results --- src/agent_cmdu.c | 61 +++++++++++++++++++----------------------------- src/agent_map.c | 19 ++++++++++----- 2 files changed, 37 insertions(+), 43 deletions(-) diff --git a/src/agent_cmdu.c b/src/agent_cmdu.c index c7b2e69ef..49ea9be17 100644 --- a/src/agent_cmdu.c +++ b/src/agent_cmdu.c @@ -806,13 +806,8 @@ static int agent_gen_ch_scan_response_all(struct agent *a, struct cmdu_buff *cmd { trace("%s --->\n", __func__); - int i, j, ret; struct wifi_scanres_element *sl; - struct wifi_scanres_opclass_element *op; - struct wifi_scanres_channel_element *ch; - int num_tlv = 0; - int num_bw_20 = 0; - bool results_missing = false; + int i, ret; if (!a || !cmdu || !re) return -1; @@ -828,45 +823,37 @@ static int agent_gen_ch_scan_response_all(struct agent *a, struct cmdu_buff *cmd if (WARN_ON(!sl)) return -1; - do { - /* do: second iteration to send empty TLVs for all supported opc/ch - * pairs in case there were no nbrs cached on any opc/ch pair. - */ - for (i = 0; i < sl->num_opclass; i++) { - op = sl->opclass_scanlist + i; + for (i = 0; i < sl->num_opclass; i++) { + struct wifi_scanres_opclass_element *op; + int j; - if (op->bandwidth != 20) /* caps */ - continue; - else - num_bw_20++; + op = sl->opclass_scanlist + i; - for (j = 0; j < op->num_channels; j++) { - ch = op->channel_scanlist + j; + if (op->bandwidth != 20) /* caps */ + continue; - if (!ch->num_neighbors && !results_missing) - /* report only non-empty results */ - continue; + for (j = 0; j < op->num_channels; j++) { + struct wifi_scanres_channel_element *ch; - ret = agent_gen_ch_scan_response_tlv(a, cmdu, - re->macaddr, op->opclass, - ch, status); + ch = op->channel_scanlist + j; - if (ret) - return ret; - - dbg("|%s:%d| Added Channel Scan Result TLV.\n", - __func__, __LINE__); - num_tlv++; + if (!wifi_opclass_id_channel_supported(&re->opclass, + op->opclass, + ch->channel)) { + /* skip channel(s) not supported */ + continue; } - } - if (!num_tlv) { - dbg("|%s:%d| No Scan Results found.\n", - __func__, __LINE__); - /* do send empty TLVs for all supported opc/ch pairs */ - results_missing = true; + ret = agent_gen_ch_scan_response_tlv(a, cmdu, + re->macaddr, op->opclass, + ch, status); + if (ret) + return ret; + + dbg("|%s:%d| Added Channel Scan Result TLV.\n", + __func__, __LINE__); } - } while (!num_tlv && num_bw_20); + } return 0; } diff --git a/src/agent_map.c b/src/agent_map.c index 25a2c6f0e..cedc3f5d5 100644 --- a/src/agent_map.c +++ b/src/agent_map.c @@ -5627,6 +5627,18 @@ int handle_channel_scan_request(void *agent, struct cmdu_buff *rx_cmdu, continue; } + if (!(ch_scan_req.mode & SCAN_REQUEST_FRESH_SCAN)) { + dbg("%s: return scan cache for radio:%s\n", __func__, re->name); + if (a->cfg.scan_on_boot_only && + re->scan_state != SCAN_DONE) { + /* Some boot scan results missing yet */ + scan_req->status = CH_SCAN_STATUS_SCAN_NOT_COMPLETED; + } else + scan_req->status = CH_SCAN_STATUS_SUCCESS; + ret = agent_send_ch_scan_response(a, re, scan_req); + continue; + } + if (scan_req->num_opclass <= 0) { dbg("%s: no opclass provided, dropping scan req for radio:"MACFMT"\n", __func__, MAC2STR(scan_req->radio)); @@ -5644,12 +5656,7 @@ int handle_channel_scan_request(void *agent, struct cmdu_buff *rx_cmdu, } /* Do not 'Perform Fresh Scan' */ else if (!(ch_scan_req.mode & SCAN_REQUEST_FRESH_SCAN)) { - if (a->cfg.scan_on_boot_only - && re->scan_state != SCAN_DONE) - /* Some boot scan results missing yet */ - scan_req->status = CH_SCAN_STATUS_SCAN_NOT_COMPLETED; - else - scan_req->status = CH_SCAN_STATUS_SUCCESS; + } /* Check all requested opc/chan pairs supported by radio */ else if (!scan_supported(a, scan_req, re)) { -- GitLab From b3a2c11576ad7f044c5fe59362c67dca24da55b0 Mon Sep 17 00:00:00 2001 From: Jakob Olsson <jakob.olsson@iopsys.eu> Date: Wed, 7 May 2025 12:47:11 +0200 Subject: [PATCH 09/10] scan: add debug output over UBUS and prefix prints --- src/agent_cmdu.c | 9 +++++---- src/agent_map.c | 16 ++++++++-------- src/agent_tlv.c | 2 +- src/agent_ubus.c | 16 ++++++++++++++++ 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/agent_cmdu.c b/src/agent_cmdu.c index 49ea9be17..607bbc1fa 100644 --- a/src/agent_cmdu.c +++ b/src/agent_cmdu.c @@ -929,17 +929,18 @@ static int agent_gen_ch_scan_response_opc(struct agent *a, for (j = 0; j < op->num_channels; j++) { int k; + ch = op->channel_scanlist + j; - trace("scan opclass %d channel %d num_neighbors %d\n", - op->opclass, ch->channel, ch->num_neighbors); + trace("%s: scan opclass %d channel %d num_neighbors %d\n", + __func__, op->opclass, ch->channel, ch->num_neighbors); for (k = 0; k < ch->num_neighbors; k++) { struct wifi_scanres_neighbor_element *nbr = ch->nbrlist + k; - trace("trace \tneigh " MACFMT " ssid %s\n", MAC2STR(nbr->bssid), nbr->ssid); + trace("\t %s: neigh " MACFMT " ssid %s\n", __func__, MAC2STR(nbr->bssid), nbr->ssid); } } if (req_opc->classid != op->opclass) { - trace("skip opclass %d\n", op->opclass); + trace("%s: skip opclass %d\n", __func__, op->opclass); continue; } diff --git a/src/agent_map.c b/src/agent_map.c index cedc3f5d5..b3c5a2c16 100644 --- a/src/agent_map.c +++ b/src/agent_map.c @@ -5648,7 +5648,7 @@ int handle_channel_scan_request(void *agent, struct cmdu_buff *rx_cmdu, /* 'Pefrorm Fresh Scan' while 'On boot only' set in Caps */ if (a->cfg.scan_on_boot_only && ch_scan_req.mode & SCAN_REQUEST_FRESH_SCAN) { - dbg("[Scan Status] radio %s: BOOT SCAN ONLY\n\n", + dbg("%s: [Scan Status] radio %s: BOOT SCAN ONLY\n\n", __func__, re->name); /* Special status in 'boot only' mode for 'fresh scan' */ @@ -5661,7 +5661,7 @@ int handle_channel_scan_request(void *agent, struct cmdu_buff *rx_cmdu, /* Check all requested opc/chan pairs supported by radio */ else if (!scan_supported(a, scan_req, re)) { /* Scan not supported for some opc/channel pairs */ - dbg("[Status code] SCAN NOT SUPPORTED\n\n"); + dbg("%s: [Status code] SCAN NOT SUPPORTED\n\n", __func__); //TODO: separate status for individual opc/ch pairs @@ -5670,13 +5670,13 @@ int handle_channel_scan_request(void *agent, struct cmdu_buff *rx_cmdu, /* Scan too soon */ else if (!timestamp_expired(&re->last_scan_tsp, MIN_SCAN_ITV_SEC * 1000)) { - dbg("[Status code] SCAN TOO SOON\n\n"); + dbg("%s: [Status code] SCAN TOO SOON\n\n", __func__); scan_req->status = CH_SCAN_STATUS_TOO_SOON; } /* Ongoing scan in progress */ else if (re->scan_state == SCAN_SCANNING) { - dbg("[Status code] ONGOING SCAN NOT COMPLETED\n\n"); + dbg("%s: [Status code] ONGOING SCAN NOT COMPLETED\n\n", __func__); scan_req->status = CH_SCAN_STATUS_SCAN_NOT_COMPLETED; } else @@ -5699,13 +5699,13 @@ int handle_channel_scan_request(void *agent, struct cmdu_buff *rx_cmdu, /* Mark radio unscanned prior to a new scan (only) */ re->scan_state = SCAN_NONE; - trace("Trying to issue channel scan on the request of mid: %d\n", + trace("%s: Trying to issue channel scan on the request of mid: %d\n", __func__, scan_req->mid); /* Issue channel scan & check return code */ ret = issue_channel_scan(a, re, scan_req); if (ret) { - dbg("[Status code] RADIO BUSY\n\n"); + dbg("%s: [Status code] RADIO BUSY\n\n", __func__); /* Send the 'busy' response */ scan_req->status = CH_SCAN_STATUS_TOO_BUSY; @@ -5716,11 +5716,11 @@ int handle_channel_scan_request(void *agent, struct cmdu_buff *rx_cmdu, continue; } - trace("Scan started successfully.\n"); + trace("%s: Scan started successfully.\n", __func__); re->scan_state = SCAN_REQUESTED; /* Wait (up to 5min) for the results */ - timer_set(&re->available_scan_timer, 300000); + timer_set(&re->available_scan_timer, 300 * 1000); /* Store the request data */ re->scan_req = *scan_req; diff --git a/src/agent_tlv.c b/src/agent_tlv.c index c5300c7e2..d816f4647 100644 --- a/src/agent_tlv.c +++ b/src/agent_tlv.c @@ -3290,7 +3290,7 @@ int agent_gen_ch_scan_response_tlv(struct agent *a, struct cmdu_buff *cmdu, /* ssid */ memcpy(&t->data[offset], nbr->ssid, strlen(nbr->ssid)); offset += strlen(nbr->ssid); - t->data[offset++] = nbr->rssi; /* rcpi */ + t->data[offset++] = nbr->rssi; /* rssi */ t->data[offset++] = strlen(bw_str); //+ 1; /* BW length */ memcpy(&t->data[offset], bw_str, strlen(bw_str)); diff --git a/src/agent_ubus.c b/src/agent_ubus.c index 99fdade97..14bcfca93 100644 --- a/src/agent_ubus.c +++ b/src/agent_ubus.c @@ -954,8 +954,10 @@ static int agent_timers(struct ubus_context *ctx, struct ubus_object *obj, { struct blob_buf bb; struct agent *a = container_of(obj, struct agent, obj); + struct wifi_radio_element *re = NULL; void *t; struct timespec now = {0}; + void *b; timestamp_update(&now); @@ -977,6 +979,20 @@ static int agent_timers(struct ubus_context *ctx, struct ubus_object *obj, #endif /* AGENT_ISLAND_PREVENTION */ blobmsg_add_u32(&bb, "disable_unconnected_bstas_scheduler", timer_remaining_ms(&a->disable_unconnected_bstas_scheduler)); blobmsg_add_u32(&bb, "onboarding_scheduler", timer_remaining_ms(&a->onboarding_scheduler)); + b = blobmsg_open_array(&bb, "scan"); + + list_for_each_entry(re, &a->radiolist, list) { + void *tt; + + tt = blobmsg_open_table(&bb, ""); + blobmsg_add_string(&bb, "name", re->name); + blobmsg_add_u32(&bb, "timeout", timer_remaining_ms(&re->available_scan_timer)); + blobmsg_add_u32(&bb, "state", re->scan_state); + blobmsg_close_table(&bb, tt); + } + + blobmsg_close_array(&bb, b); + blobmsg_close_table(&bb, t); ubus_send_reply(ctx, req, bb.head); blob_buf_free(&bb); -- GitLab From 77aca9e9c479547820b8531e61d3f48dff30c288 Mon Sep 17 00:00:00 2001 From: Jakob Olsson <jakob.olsson@iopsys.eu> Date: Mon, 12 May 2025 19:01:00 +0200 Subject: [PATCH 10/10] scan: add scan duration handling --- src/agent.c | 65 +++++++++++++++++++++--- src/agent.h | 1 + src/agent_map.c | 4 -- src/agent_tlv.c | 8 +-- src/wifi.c | 15 ++++++ src/wifi.h | 1 + src/wifi_ubus.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++ src/wifi_ubus.h | 3 ++ 8 files changed, 208 insertions(+), 18 deletions(-) diff --git a/src/agent.c b/src/agent.c index 74adcb57b..bb29a3f4d 100644 --- a/src/agent.c +++ b/src/agent.c @@ -6481,8 +6481,9 @@ static void parse_ap(struct ubus_request *req, int type, } -static int agent_radio_update_scanresults_element(struct wifi_radio_element *re, struct wifi_bss *bsss, - uint32_t classid) +static int agent_radio_update_scanresults_element(struct wifi_radio_element *re, + struct wifi_bss *bsss, + uint32_t classid) { struct wifi_scanres_channel_element *scanres_el; int idx; @@ -6532,13 +6533,32 @@ static int agent_radio_update_scanresults_element(struct wifi_radio_element *re, return 0; } +/* scan_duration expected as ms */ +static int agent_scanres_update_scan_duration(struct wifi_radio_element *re, + uint8_t classid, uint8_t channel, + uint64_t scan_duration) +{ + struct wifi_scanres_channel_element *scanres_el; + + scanres_el = wifi_get_scanres_ch_element(re, classid, channel); + if (!scanres_el) + return -1; + + scanres_el->scan_duration = scan_duration; + return 0; +} + static int agent_radio_update_scanlist(struct agent *a, - struct wifi_radio_element *re, struct wifi_bss *bsss, int bss_num) + struct wifi_radio_element *re, + struct wifi_bss *bsss, int bss_num, + struct chan_entry *ch_entry, + int ch_entry_num) { int i; trace("%s: update results for [%s]\n", __func__, re->name); + /* add neighbors */ for (i = 0; i < bss_num; i++) { uint8_t classid = wifi_opclass_find_id_from_channel( &re->opclass, @@ -6556,40 +6576,69 @@ static int agent_radio_update_scanlist(struct agent *a, } } + /* add scan duration */ + for (i = 0; i < ch_entry_num; i++) { + uint8_t classid = wifi_opclass_find_id_from_channel( + &re->opclass, + ch_entry[i].channel, + 20); /* caps */ + + /* TODO: current method is not an accurate measurement of + * scan duration for current channel */ + if (ch_entry[i].channel == re->current_channel) + continue; + + agent_scanres_update_scan_duration(re, classid, + ch_entry[i].channel, + ch_entry[i].survey.cca_time / 1000); + /* duplicate scan duration for opclass 125 channel overlap */ + if (classid == OPCLASS_124) { + agent_scanres_update_scan_duration(re, OPCLASS_125, + ch_entry[i].channel, + ch_entry[i].survey.cca_time / 1000); + } + } + return 0; } int agent_radio_scanresults(struct agent *a, struct wifi_radio_element *re) { struct wifi_bss bss[128]; + struct chan_entry ch_entry[128]; int bss_num = ARRAY_SIZE(bss); + int ch_entry_num = ARRAY_SIZE(ch_entry); int ret; if (!re || !strlen(re->name)) return -1; - trace("[%s] radio scanresults\n", re->name); + trace("%s: radio %s scanresults\n", __func__, re->name); /* Get scan results from the driver */ ret = wifi_get_scan_results(re->name, bss, &bss_num); if (ret) { - dbg("[%s] failed to get scanresults\n", re->name); + dbg("%s: failed to get radio %s scanresults\n", __func__, re->name); return -1; } /* Update scan cache */ ret = wifi_scanresults_add(&re->scanresults, &re->opclass, bss, bss_num); if (ret) { - dbg("[%s] failed to update scan cache\n", re->name); + dbg("%s: failed to update scan cache for radio %s\n", __func__, re->name); return -1; } + ret = wifi_get_channels_info(re->name, ch_entry, &ch_entry_num); + if (ret) { + ch_entry_num = 0; + dbg("%s: failed to fetch channels info for radio %s\n", __func__, re->name); + } /* TODO: update scanlist using fresh results from the cache */ - /* Empty scanlist & add most recent results for reporting */ free_scanlist_neighbors(re); - ret = agent_radio_update_scanlist(a, re, bss, bss_num); + ret = agent_radio_update_scanlist(a, re, bss, bss_num, ch_entry, ch_entry_num); return ret; } diff --git a/src/agent.h b/src/agent.h index 1a9eac6b6..863f5ad3c 100644 --- a/src/agent.h +++ b/src/agent.h @@ -576,6 +576,7 @@ struct wifi_scanres_channel_element { uint8_t channel; uint8_t utilization; uint8_t anpi; + uint32_t scan_duration; uint32_t num_neighbors; struct wifi_scanres_neighbor_element *nbrlist; /* scanned AP list */ }; diff --git a/src/agent_map.c b/src/agent_map.c index b3c5a2c16..499e5f81a 100644 --- a/src/agent_map.c +++ b/src/agent_map.c @@ -5653,10 +5653,6 @@ int handle_channel_scan_request(void *agent, struct cmdu_buff *rx_cmdu, /* Special status in 'boot only' mode for 'fresh scan' */ scan_req->status = CH_SCAN_STATUS_BOOT_SCAN_ONLY; - } - /* Do not 'Perform Fresh Scan' */ - else if (!(ch_scan_req.mode & SCAN_REQUEST_FRESH_SCAN)) { - } /* Check all requested opc/chan pairs supported by radio */ else if (!scan_supported(a, scan_req, re)) { diff --git a/src/agent_tlv.c b/src/agent_tlv.c index d816f4647..baab36609 100644 --- a/src/agent_tlv.c +++ b/src/agent_tlv.c @@ -3225,10 +3225,6 @@ int agent_gen_ch_scan_response_tlv(struct agent *a, struct cmdu_buff *cmdu, uint8_t bssload_elem_pres = CH_SCAN_RESULT_BSSLOAD_PRESENT; int i, ret, offset = 0; int reserve_len = CH_SCAN_RESP_TLV_MAX_LEN; - /* TODO/FIXME - * add the total scan duration for active scan - */ - uint32_t scan_duration = 0; uint32_t num_nbr = 0; trace("\t %s:INFO: radio " MACFMT ", opclass %d, channel %d\n", __func__, @@ -3254,7 +3250,7 @@ int agent_gen_ch_scan_response_tlv(struct agent *a, struct cmdu_buff *cmdu, /* Check if nbr data will fit within TLV limits */ if ((offset + 18 + strlen(nbr->ssid) + strlen(bw_str)) >= reserve_len) { /* Always add scan duration and scan type to TLV */ - BUF_PUT_BE32(t->data[offset], scan_duration); + BUF_PUT_BE32(t->data[offset], ch->scan_duration); offset += 4; t->data[offset++] = SCAN_RESULT_SCAN_TYPE; @@ -3307,7 +3303,7 @@ int agent_gen_ch_scan_response_tlv(struct agent *a, struct cmdu_buff *cmdu, num_nbr++; } - BUF_PUT_BE32(t->data[offset], scan_duration); /* scan duration */ + BUF_PUT_BE32(t->data[offset], ch->scan_duration); /* scan duration */ offset += 4; t->data[offset++] = SCAN_RESULT_SCAN_TYPE; /* scan type */ diff --git a/src/wifi.c b/src/wifi.c index f1ca07640..89da0315e 100644 --- a/src/wifi.c +++ b/src/wifi.c @@ -111,6 +111,21 @@ int wifi_get_scan_results(const char *name, struct wifi_bss *bsss, int *num) return ret; } +int wifi_get_channels_info(const char *name, struct chan_entry *ch_entry, int *num) +{ + struct ubus_context *ctx = ubus_connect(NULL); + int ret; + + if (!ctx) + return -1; + + ret = wifi_ubus_channels_info(ctx, name, ch_entry, num); + + ubus_free(ctx); + return ret; +} + + int wifi_set_4addr(const char *ifname, bool enable) { struct ubus_context *ctx = ubus_connect(NULL); diff --git a/src/wifi.h b/src/wifi.h index bef96d283..66575bf94 100644 --- a/src/wifi.h +++ b/src/wifi.h @@ -83,6 +83,7 @@ int wifi_scan(const char *name, struct scan_param_ex *p, int num_opclass, uint8_t *opclass, int num_channel, uint8_t *channel); int wifi_get_scan_results(const char *name, struct wifi_bss *bsss, int *num); +int wifi_get_channels_info(const char *name, struct chan_entry *ch_entry, int *num); int wifi_start_cac(const char *name, int channel, enum wifi_bw bw, enum wifi_cac_method method); int wifi_stop_cac(const char *name, int channel, enum wifi_bw bw); diff --git a/src/wifi_ubus.c b/src/wifi_ubus.c index 751f2a6ff..74130cbb9 100644 --- a/src/wifi_ubus.c +++ b/src/wifi_ubus.c @@ -1428,6 +1428,135 @@ out: return ret; } + +struct radio_chinfo_ctx { + const char *radio; + struct chan_entry *ch; + int num; + int max; + int status; +}; + +static void wifi_ubus_channels_info_cb(struct ubus_request *req, + int type, + struct blob_attr *msg) +{ + struct radio_chinfo_ctx *ctx = req->priv; + static const struct blobmsg_policy radio_chinfo_policy[] = { + [0] = { .name = "channels", .type = BLOBMSG_TYPE_ARRAY }, + }; + struct blob_attr *tb[ARRAY_SIZE(radio_chinfo_policy)]; + struct chan_entry *ch; + struct blob_attr *cur; + int rem, num; + + blobmsg_parse(radio_chinfo_policy, ARRAY_SIZE(radio_chinfo_policy), + tb, blob_data(msg), blob_len(msg)); + + num = 0; + ctx->num = 0; + + if (!tb[0]) { + ctx->status = -1; + return; + } + + blobmsg_for_each_attr(cur, tb[0], rem) { + static const struct blobmsg_policy ch_policy[] = { + [0] = { .name = "channel", .type = BLOBMSG_TYPE_INT32 }, + [1] = { .name = "freq", .type = BLOBMSG_TYPE_INT32 }, + [2] = { .name = "noise", .type = BLOBMSG_TYPE_INT32 }, + [3] = { .name = "diagnostics", .type = BLOBMSG_TYPE_TABLE } + }; + struct blob_attr *ch_tb[ARRAY_SIZE(ch_policy)]; + + if (WARN_ON(num >= ctx->max)) + break; + + blobmsg_parse(ch_policy, ARRAY_SIZE(ch_policy), ch_tb, + blobmsg_data(cur), blobmsg_data_len(cur)); + + if (!ch_tb[0] || !ch_tb[1] || !ch_tb[2] || !ch_tb[3]) + continue; + + ch = &ctx->ch[num]; + + ch->channel = blobmsg_get_u32(ch_tb[0]); + ch->freq = blobmsg_get_u32(ch_tb[1]); + ch->noise = (int32_t)blobmsg_get_u32(ch_tb[2]); + + { + static const struct blobmsg_policy diag_policy[] = { + [0] = { .name = "channel_busy", .type = BLOBMSG_TYPE_INT64 }, + [1] = { .name = "tx_airtime", .type = BLOBMSG_TYPE_INT64 }, + [2] = { .name = "rx_airtime", .type = BLOBMSG_TYPE_INT64 }, + [3] = { .name = "obss_airtime", .type = BLOBMSG_TYPE_INT64 }, + [4] = { .name = "cca_time", .type = BLOBMSG_TYPE_INT64 }, + [5] = { .name = "false_cca_count", .type = BLOBMSG_TYPE_INT64 } + }; + struct blob_attr *diag_tb[ARRAY_SIZE(diag_policy)]; + + blobmsg_parse(diag_policy, ARRAY_SIZE(diag_policy), diag_tb, + blobmsg_data(ch_tb[3]), blobmsg_data_len(ch_tb[3])); + + if (!diag_tb[0] || !diag_tb[1] || !diag_tb[2] || !diag_tb[3] || !diag_tb[4] || !diag_tb[5]) + continue; + + ch->survey.channel_busy = blobmsg_get_u64(diag_tb[0]); + ch->survey.tx_airtime = blobmsg_get_u64(diag_tb[1]); + ch->survey.rx_airtime = blobmsg_get_u64(diag_tb[2]); + ch->survey.obss_airtime = blobmsg_get_u64(diag_tb[3]); + ch->survey.cca_time = blobmsg_get_u64(diag_tb[4]); + ch->survey.false_cca_count = blobmsg_get_u64(diag_tb[5]); + } + num++; + } + + ctx->num = num; + ctx->status = 0; +} + + + +int wifi_ubus_channels_info(struct ubus_context *ubus_ctx, const char *radio, + struct chan_entry *ch, int *num) +{ + struct radio_chinfo_ctx ctx = { + .radio = radio, + .ch = ch, + .num = 0, + .max = *num, + .status = -1, + }; + struct blob_buf bb = {}; + char name[256] = {}; + uint32_t id; + int ret; + + trace("[%s] %s\n", radio, __func__); + + /* Get id from radio name */ + snprintf(name, sizeof(name), "wifi.radio.%s", radio); + ret = ubus_lookup_id(ubus_ctx, name, &id); + if (ret != UBUS_STATUS_OK) + goto out; + + blob_buf_init(&bb, 0); + ret = ubus_invoke(ubus_ctx, id, "channels_info", bb.head, + wifi_ubus_channels_info_cb, &ctx, + 2 * 1000); + blob_buf_free(&bb); + + if (ctx.status) + ret = ctx.status; + + *num = ctx.num; + +out: + trace("[%s] %s ret %d num %d max %d\n", radio, __func__, ret, *num, ctx.max); + return ret; +} + int wifi_ubus_chan_switch(struct ubus_context *ubus_ctx, const char *ifname, struct chan_switch_param *param) { diff --git a/src/wifi_ubus.h b/src/wifi_ubus.h index e7868d313..dffd2c22c 100644 --- a/src/wifi_ubus.h +++ b/src/wifi_ubus.h @@ -62,6 +62,9 @@ int wifi_ubus_ap_status(struct ubus_context *ubus_ctx, const char *ifname, struc int wifi_ubus_radio_scanresults(struct ubus_context *ubus_ctx, const char *radio, struct wifi_bss *bss, int *num); +int wifi_ubus_channels_info(struct ubus_context *ubus_ctx, const char *radio, + struct chan_entry *ch_entry, int *num); + int wifi_ubus_monitor_add_del(struct ubus_context *ubus_ctx, const char *ifname, uint8_t *macaddr, bool add); int wifi_ubus_monitor_add(struct ubus_context *ubus_ctx, const char *ifname, uint8_t *macaddr); -- GitLab