From 2e91ed6ee44df6f7481a14eeaea6d7e4c311c434 Mon Sep 17 00:00:00 2001
From: Janusz Dziedzic <janusz.dziedzic@iopsys.eu>
Date: Thu, 20 Mar 2025 15:39:55 +0100
Subject: [PATCH] acs: introduce improvements
UBUS:
- add trigger_channel_acs
- add trigger_channel_clearing
- report clearing/acs status in ubus status
- report status request during ubus call
CMDUS:
- handle and use CAC completion report
- handle and use CAC status
- handle and use oper channel report
- fix generation of channel selection request
ACS:
- add CMDUs ACS callbacks - use them for recalc
- remove dfs_cleanup timer - base on events when recalc
- fix bsta connected check
- run clearing when background CAC supported by node
- run acs recalc when DFS chan available
- introduce backoff when platform don't report CAC ongoing correctly
---
src/acs.c | 590 ++++++++++++++++++++++++++++++++------
src/acs.h | 35 ++-
src/cntlr.c | 23 +-
src/cntlr.h | 9 -
src/cntlr_cmdu.c | 43 ++-
src/cntlr_cmdu.h | 15 +-
src/cntlr_commands.c | 54 +++-
src/cntlr_commands.h | 23 ++
src/cntlr_commands_impl.c | 284 ++++++++++++++++--
src/cntlr_commands_impl.h | 1 +
src/cntlr_map.c | 134 ++++++++-
src/cntlr_tlv.c | 148 +++-------
src/cntlr_tlv.h | 7 +-
src/cntlr_ubus.c | 29 ++
src/cntlr_ubus_dbg.c | 2 +-
src/config.c | 15 +-
src/config.h | 2 +-
src/utils/utils.c | 16 ++
src/utils/utils.h | 1 +
src/wifi_dataelements.h | 74 +++++
src/wifi_opclass.c | 99 ++++---
src/wifi_opclass.h | 8 +-
22 files changed, 1267 insertions(+), 345 deletions(-)
diff --git a/src/acs.c b/src/acs.c
index 6c014f08..ceeee8da 100644
--- a/src/acs.c
+++ b/src/acs.c
@@ -18,10 +18,13 @@
#include "cntlr.h"
#include "cntlr_cmdu.h"
#include "utils/debug.h"
+#include "utils/utils.h"
#include "wifi_dataelements.h"
#include "wifi_opclass.h"
+#include "sta.h"
+/* Opclass helpers */
int cntlr_radio_pref_opclass_reset(struct wifi_radio_element *radio)
{
/*
@@ -34,6 +37,24 @@ int cntlr_radio_pref_opclass_reset(struct wifi_radio_element *radio)
return radio->pref_opclass.num_opclass;
}
+enum wifi_radio_opclass_dfs dfs_state_from_preference(uint8_t preference)
+{
+ uint8_t reas = preference & CHANNEL_PREF_REASON;
+
+ switch(reas) {
+ case CHANNEL_PREF_REASON_DFS_USABLE:
+ return WIFI_RADIO_OPCLASS_CHANNEL_DFS_USABLE;
+ case CHANNEL_PREF_REASON_DFS_AVAILABLE:
+ return WIFI_RADIO_OPCLASS_CHANNEL_DFS_AVAILABLE;
+ case CHANNEL_PREF_REASON_DFS_NOP:
+ return WIFI_RADIO_OPCLASS_CHANNEL_DFS_NOP;
+ default:
+ break;
+ }
+
+ return WIFI_RADIO_OPCLASS_CHANNEL_DFS_NONE;
+}
+
int cntlr_radio_pref_opclass_add(struct wifi_radio_element *radio, uint8_t classid,
uint8_t channel, uint8_t preference)
{
@@ -41,6 +62,7 @@ int cntlr_radio_pref_opclass_add(struct wifi_radio_element *radio, uint8_t class
struct wifi_radio_opclass_channel *chan;
struct wifi_radio_opclass_channel new_chan = {};
struct wifi_radio_opclass *opclass;
+ enum wifi_radio_opclass_dfs old;
opclass = &radio->pref_opclass;
@@ -53,6 +75,7 @@ int cntlr_radio_pref_opclass_add(struct wifi_radio_element *radio, uint8_t class
new_chan.channel = channel;
new_chan.preference = preference;
+ new_chan.dfs = dfs_state_from_preference(preference);
timestamp_update(&opclass->entry_time);
@@ -61,12 +84,37 @@ int cntlr_radio_pref_opclass_add(struct wifi_radio_element *radio, uint8_t class
if (chan) {
chan->channel = channel;
chan->preference = preference;
+
+ old = chan->dfs;
+ chan->dfs = dfs_state_from_preference(preference);
+
+ /* save backoff */
+ if (old == WIFI_RADIO_OPCLASS_CHANNEL_DFS_BACKOFF &&
+ chan->dfs == WIFI_RADIO_OPCLASS_CHANNEL_DFS_USABLE)
+ chan->dfs = WIFI_RADIO_OPCLASS_CHANNEL_DFS_BACKOFF;
+
return 0;
}
return wifi_opclass_add_channel(entry, &new_chan);
}
+int cntlr_radio_pref_opclass_set_dfs_status(struct wifi_radio_element *radio, uint8_t classid,
+ uint8_t channel, enum wifi_radio_opclass_dfs state)
+{
+ struct wifi_radio_opclass_channel *chan;
+ struct wifi_radio_opclass *opclass;
+
+ opclass = &radio->pref_opclass;
+
+ chan = wifi_opclass_get_channel(opclass, classid, channel);
+ if (!chan)
+ return -1;
+
+ chan->dfs = state;
+ return 0;
+}
+
void cntlr_radio_pref_opclass_dump(struct wifi_radio_element *radio)
{
wifi_opclass_dump(&radio->pref_opclass, "dev_pref_opclass", radio->macaddr);
@@ -105,7 +153,7 @@ uint8_t ctrl_radio_cur_opclass_id(struct wifi_radio_element *radio)
uint8_t id = 0;
int ret;
- ret = wifi_opclass_get_max_bw(&radio->cur_opclass, NULL, NULL, &id);
+ ret = wifi_opclass_get_max_bw(&radio->cur_opclass, NULL, NULL, &id, NULL);
if (ret)
return 0;
return id;
@@ -116,18 +164,29 @@ uint8_t ctrl_radio_cur_opclass_ctrl_chan(struct wifi_radio_element *radio)
uint8_t ctrl_chan = 0;
int ret;
- ret = wifi_opclass_get_max_bw(&radio->cur_opclass, &ctrl_chan, NULL, NULL);
+ ret = wifi_opclass_get_max_bw(&radio->cur_opclass, &ctrl_chan, NULL, NULL, NULL);
if (ret)
return 0;
return ctrl_chan;
}
+uint8_t ctrl_radio_cur_opclass_chan(struct wifi_radio_element *radio)
+{
+ uint8_t chan = 0;
+ int ret;
+
+ ret = wifi_opclass_get_max_bw(&radio->cur_opclass, NULL, NULL, NULL, &chan);
+ if (ret)
+ return 0;
+ return chan;
+}
+
uint8_t ctrl_radio_cur_opclass_max_bw(struct wifi_radio_element *radio)
{
uint32_t bw = 0;
int ret;
- ret = wifi_opclass_get_max_bw(&radio->cur_opclass, NULL, &bw, NULL);
+ ret = wifi_opclass_get_max_bw(&radio->cur_opclass, NULL, &bw, NULL, NULL);
if (ret)
return 0;
return bw;
@@ -160,25 +219,100 @@ bool cntlr_node_pref_opclass_expired(struct node *node)
return expired;
}
+/* ACS recalc code */
+uint8_t cntrl_acs_radio_ctrl_channel(struct wifi_radio_element *radio, uint8_t opclass, uint8_t channel)
+{
+ struct wifi_radio_opclass_channel *chan;
+
+ chan = wifi_opclass_get_channel(&radio->pref_opclass, opclass, channel);
+ if (!chan)
+ return 0;
-int cntlr_acs_radio_channel_recalc(struct wifi_radio_element *radio, struct acs_params *params)
+ return wifi_get_best_ctrl_channel(&radio->pref_opclass,
+ chan->ctrl_channels,
+ ARRAY_SIZE(chan->ctrl_channels));
+}
+
+static bool cntlr_acs_radio_is_bsta_connected(struct controller *cntlr, struct netif_radio *radio)
{
- struct acs_params acs_params[64] = {};
- int acs_params_num = 0;
+ struct netif_iface *iface = NULL;
+
+ list_for_each_entry(iface, &radio->iflist, list) {
+ struct node *n = NULL;
+
+ /* Check if sta iface */
+ if (iface->bss->is_bbss || iface->bss->is_fbss)
+ continue;
+
+ /* Check all nodes */
+ list_for_each_entry(n, &cntlr->nodelist, list) {
+ struct sta *s = NULL;
+
+ list_for_each_entry(s, &n->stalist, list) {
+ /* skip other band */
+ if (memcmp(iface->bss->bssid, s->de_sta->macaddr, sizeof(iface->bss->bssid)))
+ continue;
+
+ if (s->is_bsta && s->state == STA_ASSOCIATED)
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+static void cntrl_acs_radio_common_opclass(struct controller *cntlr, struct netif_radio *rd,
+ struct wifi_radio_opclass *opclass)
+{
+ memcpy(opclass, &rd->radio_el->pref_opclass, sizeof(*opclass));
+
+ /*
+ * TODO Check leafs and build common opclass. Prevent different
+ * channels set. Skip when ethernet backhaul.
+ */
+}
+
+int cntlr_acs_radio_channel_recalc(struct node *node, struct netif_radio *rd, struct wifi_acs_params *params)
+{
+ struct wifi_radio_element *radio = rd->radio_el;
+ struct wifi_radio_opclass common_opclass = {};
+ struct wifi_acs_params acs_params[64] = {};
struct wifi_radio_opclass_entry *entry;
struct wifi_radio_opclass *opclass;
+ int acs_params_num = 0;
int chan, pref, reas;
int pref_best = 0;
int pref_cur = 0;
+ uint32_t cac_time = 0;
int prefered[64] = {0};
int i, j, r;
- opclass = &radio->pref_opclass;
-
- cntlr_dbg(LOG_CHANNEL, "acs radio channel recalc " MACFMT " opclass %d bw %d skip_dfs %d\n",
+ cntlr_trace(LOG_CHANNEL, "acs radio channel recalc " MACFMT " opclass %d bw %d skip_dfs %d\n",
MAC2STR(radio->macaddr),
params->opclass, params->bw, params->skip_dfs);
+ memset(&radio->last_acs, 0, sizeof(radio->last_acs));
+
+ if (!radio->pref_opclass.num_opclass) {
+ cntlr_dbg(LOG_CHANNEL, "acs radio channel recalc " MACFMT " - no pref opclass - skip recalc\n",
+ MAC2STR(radio->macaddr));
+
+ params->status = WIFI_ACS_RECALC_STATUS_INPUT_DATA_INSUFFICIENT;
+ goto out;
+ }
+
+ if (cntlr_acs_radio_is_bsta_connected(node->cntlr, rd)) {
+ cntlr_dbg(LOG_CHANNEL, "acs radio " MACFMT " - skip switch - bsta connected\n",
+ MAC2STR(radio->macaddr));
+
+ params->status = WIFI_ACS_RECALC_STATUS_BSTA_CONNECTED;
+ goto out;
+ }
+
+ cntrl_acs_radio_common_opclass(node->cntlr, rd, &common_opclass);
+ opclass = &common_opclass;
+
for (i = 0; i < opclass->num_opclass; i++) {
entry = &opclass->opclass[i];
@@ -194,8 +328,9 @@ int cntlr_acs_radio_channel_recalc(struct wifi_radio_element *radio, struct acs_
chan = entry->channel[j].channel;
pref = (entry->channel[j].preference & CHANNEL_PREF_MASK) >> 4;
reas = entry->channel[j].preference & CHANNEL_PREF_REASON;
+ cac_time = entry->channel[j].cac_time;
- trace("\tacs check/cmp chan %d pref %d reas %d\n", chan, pref, reas);
+ cntlr_trace(LOG_CHANNEL, "\tacs check/cmp chan %d pref %d reas %d\n", chan, pref, reas);
/* Always skip disabled channels */
if (reas == CHANNEL_PREF_REASON_DFS_NOP)
@@ -203,6 +338,10 @@ int cntlr_acs_radio_channel_recalc(struct wifi_radio_element *radio, struct acs_
if (reas == CHANNEL_PREF_REASON_REG_DISALLOWED)
continue;
+ /* Current channel preference */
+ if (chan == params->best_channel)
+ pref_cur = pref;
+
/* Skip DFS channels if requested */
if (params->skip_dfs) {
if (reas == CHANNEL_PREF_REASON_DFS_AVAILABLE ||
@@ -211,16 +350,12 @@ int cntlr_acs_radio_channel_recalc(struct wifi_radio_element *radio, struct acs_
}
/* Skip non available DFS channels if requested */
- if (params->skip_dfs_not_available && reas != CHANNEL_PREF_REASON_DFS_AVAILABLE)
+ if (params->skip_dfs_not_available && reas == CHANNEL_PREF_REASON_DFS_USABLE)
continue;
if (WARN_ON(acs_params_num >= ARRAY_SIZE(acs_params)))
break;
- /* Current channel preference */
- if (chan == params->best_channel)
- pref_cur = pref;
-
/* Kick best value */
if (pref > pref_best)
pref_best = pref;
@@ -229,21 +364,30 @@ int cntlr_acs_radio_channel_recalc(struct wifi_radio_element *radio, struct acs_
acs_params[acs_params_num].best_opclass = entry->id;
acs_params[acs_params_num].best_bw = entry->bandwidth;
acs_params[acs_params_num].best_pref = pref;
+ acs_params[acs_params_num].best_cac_time = cac_time;
acs_params_num++;
}
}
- if (!pref_best)
- return -1;
+ if (!pref_best) {
+ cntlr_dbg(LOG_CHANNEL, "acs radio channel recalc " MACFMT " - no pref best - skip recalc\n",
+ MAC2STR(radio->macaddr));
+
+ params->status = WIFI_ACS_RECALC_STATUS_INPUT_DATA_INSUFFICIENT;
+ goto out;
+ }
- cntlr_dbg(LOG_CHANNEL, "acs radio " MACFMT " best pref %d vs current pref %d\n",
+ cntlr_trace(LOG_CHANNEL, "acs radio " MACFMT " best pref %d vs current pref %d\n",
MAC2STR(radio->macaddr), pref_best, pref_cur);
/* If current channel equal to best don't switch */
if (pref_cur == pref_best) {
- cntlr_dbg(LOG_CHANNEL, "acs skip - current channel %d is the best\n", params->best_channel);
- return -1;
+ cntlr_warn(LOG_CHANNEL, "acs node " MACFMT " " MACFMT " current channel %d is the best\n",
+ MAC2STR(node->almacaddr), MAC2STR(radio->macaddr), params->best_channel);
+
+ params->status = WIFI_ACS_RECALC_STATUS_BEST_ALREADY_SET;
+ goto out;
}
/* Get random channel from best performance */
@@ -259,48 +403,42 @@ int cntlr_acs_radio_channel_recalc(struct wifi_radio_element *radio, struct acs_
j++;
}
- if (WARN_ON(!j))
- return -1;
+ if (WARN_ON(!j)) {
+ params->status = WIFI_ACS_RECALC_STATUS_INPUT_DATA_INSUFFICIENT;
+ goto out;
+ }
srandom(time(NULL));
r = random() % j;
- cntlr_dbg(LOG_CHANNEL, "acs radio " MACFMT " table size %d - rand %d, index %d\n",
+ cntlr_trace(LOG_CHANNEL, "acs radio " MACFMT " table size %d - rand %d, index %d\n",
MAC2STR(radio->macaddr), j, r, prefered[r]);
- if (prefered[r] >= acs_params_num)
- return -1;
+ if (prefered[r] >= acs_params_num) {
+ params->status = WIFI_ACS_RECALC_STATUS_INPUT_DATA_INSUFFICIENT;
+ goto out;
+ }
params->best_channel = acs_params[prefered[r]].best_channel;
params->best_bw = acs_params[prefered[r]].best_bw;
params->best_opclass = acs_params[prefered[r]].best_opclass;
+ params->best_cac_time = acs_params[prefered[r]].best_cac_time;
+ params->best_pref = acs_params[prefered[r]].best_pref;
+ params->status = WIFI_ACS_RECALC_STATUS_BEST_SELECTED;
+
cntlr_dbg(LOG_CHANNEL, "acs radio " MACFMT " best chan %d/%d opclass %d\n",
MAC2STR(radio->macaddr),
params->best_channel, params->best_bw, params->best_opclass);
- return 0;
-}
-
-static bool cntlr_acs_radio_is_bsta_connected(struct netif_radio *radio)
-{
- struct netif_iface *iface = NULL;
-
- list_for_each_entry(iface, &radio->iflist, list) {
- /* Check if sta iface connected */
- if (iface->bss->is_bbss || iface->bss->is_fbss)
- continue;
+out:
+ memcpy(&radio->last_acs, params, sizeof(radio->last_acs));
+ timestamp_update(&radio->last_acs.entry_time);
- if (hwaddr_is_zero(iface->upstream_bssid))
- continue;
-
- return true;
- }
-
- return false;
+ return params->status != WIFI_ACS_RECALC_STATUS_BEST_SELECTED;
}
-static int cntlr_get_current_acs_params(struct wifi_radio_element *radio, struct acs_params *params)
+static int cntlr_get_current_acs_params(struct wifi_radio_element *radio, struct wifi_acs_params *params)
{
memset(params, 0, sizeof(*params));
@@ -310,65 +448,123 @@ static int cntlr_get_current_acs_params(struct wifi_radio_element *radio, struct
params->opclass = ctrl_radio_cur_opclass_id(radio);
params->bw = ctrl_radio_cur_opclass_max_bw(radio);
- params->best_channel = ctrl_radio_cur_opclass_ctrl_chan(radio);
+ params->best_channel = ctrl_radio_cur_opclass_chan(radio);
params->best_bw = params->bw;
params->best_opclass = params->opclass;
return 0;
}
-void cntlr_acs_node_channel_recalc(struct node *node, bool skip_dfs)
+void cntlr_acs_node_channel_recalc(struct node *node, enum wifi_band band, uint8_t opclass,
+ uint32_t bandwidth, bool skip_dfs, bool prevent_cac)
{
- struct acs_params cur_acs_params = {};
- struct acs_params acs_params = {};
+ struct wifi_acs_params cur_acs_params = {};
+ struct wifi_acs_params acs_params = {};
struct netif_radio *r = NULL;
int ret;
acs_params.skip_dfs = skip_dfs;
+ acs_params.skip_dfs_not_available = prevent_cac;
- cntlr_dbg(LOG_CHANNEL, "acs node channel recalc " MACFMT " skip_dfs %d\n",
- MAC2STR(node->almacaddr), acs_params.skip_dfs);
+ cntlr_dbg(LOG_CHANNEL, "acs node channel recalc " MACFMT " skip_dfs %d prevent_cac %d\n",
+ MAC2STR(node->almacaddr), acs_params.skip_dfs, acs_params.skip_dfs_not_available);
list_for_each_entry(r, &node->radiolist, list) {
- WARN_ON(cntlr_get_current_acs_params(r->radio_el, &cur_acs_params));
+ struct wifi_radio_opclass req_opclass = {};
+ uint8_t ctrl_channel;
- /* Use current opclass - TODO: if no opclass check 80/40/20 */
- acs_params.opclass = cur_acs_params.opclass;
- acs_params.best_channel = cur_acs_params.best_channel;
+ if (band && band != BAND_ANY && band != r->radio_el->band)
+ continue;
+
+ if (opclass) {
+ cntlr_get_current_acs_params(r->radio_el, &cur_acs_params);
+ acs_params.opclass = opclass;
- ret = cntlr_acs_radio_channel_recalc(r->radio_el, &acs_params);
+ if (cur_acs_params.opclass == opclass) {
+ acs_params.best_channel = cur_acs_params.best_channel;
+ acs_params.best_bw = cur_acs_params.best_bw;
+ }
+ } else if (bandwidth) {
+ cntlr_get_current_acs_params(r->radio_el, &cur_acs_params);
+ acs_params.bw = bandwidth;
+
+ if (cur_acs_params.bw == bandwidth) {
+ acs_params.best_channel = cur_acs_params.best_channel;
+ acs_params.best_bw = cur_acs_params.best_bw;
+ }
+ } else {
+ if (cntlr_get_current_acs_params(r->radio_el, &cur_acs_params)) {
+ cntlr_dbg(LOG_CHANNEL, "acs node " MACFMT " " MACFMT " - current channel not known\n",
+ MAC2STR(node->almacaddr), MAC2STR(r->radio_el->macaddr));
+ continue;
+ }
+
+ acs_params.opclass = cur_acs_params.opclass;
+ acs_params.best_channel = cur_acs_params.best_channel;
+ acs_params.best_bw = cur_acs_params.best_bw;
+ }
+
+ ret = cntlr_acs_radio_channel_recalc(node, r, &acs_params);
if (WARN_ON(ret))
continue;
- cntlr_dbg(LOG_CHANNEL, "acs node " MACFMT " radio " MACFMT " new %d/%d opclass %d vs old %d/%d opclass %d\n",
+ cntlr_trace(LOG_CHANNEL, "acs node " MACFMT " radio " MACFMT " new %d/%d opclass %d vs old %d/%d opclass %d cac_time %u\n",
MAC2STR(node->almacaddr), MAC2STR(r->radio_el->macaddr), acs_params.best_channel,
acs_params.best_bw, acs_params.best_opclass, cur_acs_params.best_channel,
- cur_acs_params.bw, cur_acs_params.opclass);
+ cur_acs_params.bw, cur_acs_params.opclass, acs_params.best_cac_time);
+
+ /* Pick up control channel */
+ if (acs_params.best_bw > 40) {
+ ctrl_channel = cntrl_acs_radio_ctrl_channel(r->radio_el,
+ acs_params.best_opclass,
+ acs_params.best_channel);
+ } else {
+ ctrl_channel = acs_params.best_channel;
+ }
+
+ cntlr_warn(LOG_CHANNEL, "acs node " MACFMT " " MACFMT "switch to best channel %d/%d ctrl %d\n",
+ MAC2STR(node->almacaddr), MAC2STR(r->radio_el->macaddr), acs_params.best_channel,
+ acs_params.best_bw, ctrl_channel);
+
+ /* Build opclass we would like to send */
+ memcpy(&req_opclass, &r->radio_el->supp_opclass, sizeof(req_opclass));
+ wifi_opclass_set_preferences(&req_opclass, 0x0);
- if (cntlr_acs_radio_is_bsta_connected(r))
+ if (wifi_radio_opclass_update_channel(&req_opclass,
+ wifi_band_to_band(r->radio_el->band),
+ ctrl_channel, acs_params.best_bw, 15)) {
+
+ cntlr_dbg(LOG_CHANNEL, "acs node " MACFMT " " MACFMT " - update opclass failed\n",
+ MAC2STR(node->almacaddr), MAC2STR(r->radio_el->macaddr));
continue;
+ }
+
+ wifi_opclass_dump_ex(&req_opclass, "acs_selection", r->radio_el->macaddr, false);
- warn("acs switch to best channel %d/%d\n", acs_params.best_channel, acs_params.best_bw);
ret = cntlr_send_channel_selection(node->cntlr, node->almacaddr,
- r->radio_el->macaddr,
- acs_params.best_channel,
- acs_params.best_opclass,
- 14);
+ r->radio_el->macaddr, &req_opclass);
+ if (ret == 0xffff) {
+ cntlr_warn(LOG_CHANNEL, "acs node " MACFMT " " MACFMT " send channel switch request failed\n",
+ MAC2STR(node->almacaddr), MAC2STR(r->radio_el->macaddr));
+ continue;
+ }
- if (ret)
- warn("acs switch failed\n");
+ r->radio_el->last_acs.mid = ret;
+ r->radio_el->last_acs.status = WIFI_ACS_RECALC_STATUS_BEST_REQUESTED;
}
}
-void cntlr_acs_recalc(struct controller *c, bool skip_dfs)
+void cntlr_acs_recalc(struct controller *c, bool skip_dfs, bool skip_cac)
{
struct node *n = NULL;
list_for_each_entry(n, &c->nodelist, list) {
- cntlr_acs_node_channel_recalc(n, skip_dfs);
+ cntlr_acs_node_channel_recalc(n, BAND_ANY, 0, 0, skip_dfs, skip_cac);
}
}
+
+/* DFS background CAC cleanup */
static uint8_t cntlr_dfs_cac_method(uint32_t methods)
{
uint8_t cac_method = 0;
@@ -384,7 +580,7 @@ static uint8_t cntlr_dfs_cac_method(uint32_t methods)
return cac_method;
}
-static bool cntlr_dfs_get_usable(struct wifi_radio_opclass_entry *entry, struct cac_data *cac_data)
+static bool cntlr_dfs_get_usable(struct wifi_radio_opclass_entry *entry, struct wifi_cac_data *cac_data)
{
uint8_t reas;
int i;
@@ -395,12 +591,17 @@ static bool cntlr_dfs_get_usable(struct wifi_radio_opclass_entry *entry, struct
if (reas != CHANNEL_PREF_REASON_DFS_USABLE)
continue;
+ if (entry->channel[i].dfs == WIFI_RADIO_OPCLASS_CHANNEL_DFS_BACKOFF &&
+ timestamp_elapsed_sec(&entry->channel[i].backoff_time) < 120)
+ continue;
+
cac_data->cac_method = cntlr_dfs_cac_method(entry->channel[i].cac_methods);
if (!cac_data->cac_method)
continue;
cac_data->channel = entry->channel[i].channel;
cac_data->opclass = entry->id;
+ cac_data->cac_time = entry->channel[i].cac_time;
return true;
}
@@ -422,23 +623,29 @@ static bool cntlr_radio_is_ap_iface(struct netif_radio *radio)
return false;
}
-static bool cntlr_dfs_get_cac_data(struct wifi_radio_element *radio, struct cac_data *cac_data)
+static bool cntlr_dfs_get_cac_data(struct wifi_radio_element *radio, struct wifi_cac_data *cac_data, uint16_t bw)
{
struct wifi_radio_opclass_entry *entry;
struct wifi_radio_opclass *opclass;
+ uint32_t cur_bw = 0;
+ uint32_t cur_chan = 0;
int i;
- /* TODO check dfs_region early - skip non EU */
+ if (!bw) {
+ cur_bw = ctrl_radio_cur_opclass_max_bw(radio);
+ cur_chan = ctrl_radio_cur_opclass_chan(radio);
+ } else {
+ cur_bw = bw;
+ cur_chan = 0;
+ }
+
+ if (!cur_bw)
+ return false;
opclass = &radio->pref_opclass;
for (i = 0; i < opclass->num_opclass; i++) {
entry = &opclass->opclass[i];
- uint32_t cur_bw = 0;
- uint32_t cur_ctrl_chan = 0;
-
- cur_bw = ctrl_radio_cur_opclass_max_bw(radio);
- cur_ctrl_chan = ctrl_radio_cur_opclass_ctrl_chan(radio);
if (entry->bandwidth != cur_bw)
continue;
@@ -446,8 +653,7 @@ static bool cntlr_dfs_get_cac_data(struct wifi_radio_element *radio, struct cac_
if (!cntlr_dfs_get_usable(entry, cac_data))
continue;
- /* TODO check chan/bw - not only control channel */
- if (cac_data->channel == cur_ctrl_chan)
+ if (cac_data->channel == cur_chan)
continue;
memcpy(cac_data->radio, radio->macaddr, sizeof(cac_data->radio));
@@ -460,30 +666,60 @@ static bool cntlr_dfs_get_cac_data(struct wifi_radio_element *radio, struct cac_
void cntlr_dfs_radio_cleanup(struct node *node, struct netif_radio *radio)
{
- struct cac_data cac_data = {};
+ struct wifi_cac_data cac_data = {};
+
+ if (!radio->radio_el->pref_opclass.num_opclass) {
+ cntlr_dbg(LOG_CHANNEL, "dfs radio preCAC not yet " MACFMT "\n", MAC2STR(radio->radio_el->macaddr));
+ radio->radio_el->last_cac_data.status = WIFI_ACS_CLEANUP_STATUS_SKIPPED;
+ return;
+ }
+
+ if (!radio->radio_el->bgcac_supported) {
+ cntlr_dbg(LOG_CHANNEL, "dfs radio preCAC not supported " MACFMT "\n", MAC2STR(radio->radio_el->macaddr));
+ radio->radio_el->last_cac_data.status = WIFI_ACS_CLEANUP_STATUS_NOT_SUPPORTED;
+ return;
+ }
cntlr_dbg(LOG_CHANNEL, "dfs radio preCAC cleanup " MACFMT "\n", MAC2STR(radio->radio_el->macaddr));
if (!cntlr_radio_is_ap_iface(radio)) {
cntlr_dbg(LOG_CHANNEL, "dfs radio preCAC no AP ifaces, skip radio\n");
+ radio->radio_el->last_cac_data.status = WIFI_ACS_CLEANUP_STATUS_NO_APS;
return;
}
- if (!cntlr_dfs_get_cac_data(radio->radio_el, &cac_data)) {
- cntlr_dbg(LOG_CHANNEL, "dfs radio preCAC cleanup no channels left\n");
- return;
+ if (!cntlr_dfs_get_cac_data(radio->radio_el, &cac_data, 0)) {
+ cntlr_dbg(LOG_CHANNEL, "dfs radio preCAC cleanup no channels left check 20MHz\n");
+ if (!cntlr_dfs_get_cac_data(radio->radio_el, &cac_data, 20)) {
+ cntlr_dbg(LOG_CHANNEL, "dfs radio preCAC cleanup no channels left\n");
+ radio->radio_el->last_cac_data.status = WIFI_ACS_CLEANUP_STATUS_ALL_CLEAN;
+ radio->radio_el->last_acs.recalc = true;
+ return;
+ }
}
- cntlr_dbg(LOG_CHANNEL, "dfs radio preCAC run chan %d opclass %d\n", cac_data.channel, cac_data.opclass);
+ cntlr_warn(LOG_CHANNEL, "dfs node " MACFMT " radio " MACFMT " preCAC run chan %d opclass %d\n",
+ MAC2STR(node->almacaddr), MAC2STR(radio->radio_el->macaddr),
+ cac_data.channel, cac_data.opclass);
+ memcpy(&radio->radio_el->last_cac_data, &cac_data, sizeof(cac_data));
+ timestamp_update(&radio->radio_el->last_cac_data.entry_time);
+ radio->radio_el->last_cac_data.status = WIFI_ACS_CLEANUP_STATUS_SELECTED;
+
WARN_ON(cntlr_send_cac_req(node->cntlr, node->almacaddr, 1, &cac_data));
+
+ radio->radio_el->last_cac_data.mid = cac_data.mid;
+ if (cac_data.mid != 0xffff)
+ radio->radio_el->last_cac_data.status = WIFI_ACS_CLEANUP_STATUS_REQUESTED;
}
void cntlr_dfs_node_cleanup(struct node *node)
{
struct netif_radio *radio = NULL;
- cntlr_dbg(LOG_CHANNEL, "dfs node preCAC cleanup " MACFMT "\n", MAC2STR(node->almacaddr));
+ cntlr_trace(LOG_CHANNEL, "dfs node preCAC cleanup " MACFMT "\n", MAC2STR(node->almacaddr));
list_for_each_entry(radio, &node->radiolist, list) {
+ if (radio->radio_el->band != BAND_5)
+ continue;
cntlr_dfs_radio_cleanup(node, radio);
}
}
@@ -496,3 +732,191 @@ void cntlr_dfs_cleanup(struct controller *c)
cntlr_dfs_node_cleanup(n);
}
}
+
+/* Callbacks */
+void cntlr_acs_channel_sel_response(struct netif_radio *r, uint16_t mid, uint8_t status)
+{
+ struct wifi_acs_params *acs = &r->radio_el->last_acs;
+
+ if (acs->status != WIFI_ACS_RECALC_STATUS_BEST_REQUESTED)
+ return;
+
+ if (acs->mid != mid)
+ return;
+
+ if (status == 0)
+ acs->status = WIFI_ACS_RECALC_STATUS_BEST_ACCEPTED;
+ else
+ acs->status = WIFI_ACS_RECALC_STATUS_BEST_REJECTED;
+}
+
+void cntlr_acs_oper_channel_report(struct netif_radio *r)
+{
+ struct wifi_acs_params *acs = &r->radio_el->last_acs;
+ struct wifi_radio_opclass_entry *entry;
+ struct wifi_radio_opclass *cur;
+ int i, j;
+
+ if (acs->status != WIFI_ACS_RECALC_STATUS_BEST_REQUESTED &&
+ acs->status != WIFI_ACS_RECALC_STATUS_BEST_ACCEPTED)
+ return;
+
+ cur = &r->radio_el->cur_opclass;
+ for (i = 0; i < cur->num_opclass; i++) {
+ entry = &cur->opclass[i];
+
+ if (entry->bandwidth != acs->best_bw)
+ continue;
+
+ for (j = 0; j < entry->num_channel; j++) {
+ if (entry->channel[j].channel == acs->best_channel) {
+ acs->status = WIFI_ACS_RECALC_STATUS_BEST_SET;
+ return;
+ }
+ }
+ }
+
+ /* timeout check */
+ if (timestamp_elapsed_sec(&acs->entry_time) > 30 + acs->best_cac_time)
+ acs->status = WIFI_ACS_RECALC_STATUS_BEST_SET_TIMEOUT;
+}
+
+static void cntlr_dfs_cleanup_set_backoff(struct netif_radio *r, uint8_t classid, uint8_t channel)
+{
+ struct wifi_radio_opclass_channel *chan;
+
+ chan = wifi_opclass_get_channel(&r->radio_el->pref_opclass,
+ classid, channel);
+ if (chan) {
+ chan->dfs = WIFI_RADIO_OPCLASS_CHANNEL_DFS_BACKOFF;
+ timestamp_update(&chan->backoff_time);
+ }
+
+ chan = wifi_opclass_get_channel(&r->radio_el->supp_opclass,
+ classid, channel);
+ if (chan) {
+ chan->dfs = WIFI_RADIO_OPCLASS_CHANNEL_DFS_BACKOFF;
+ timestamp_update(&chan->backoff_time);
+ }
+}
+
+void cntlr_acs_cac_completion(struct netif_radio *r, uint8_t classid,
+ uint8_t channel, uint8_t status)
+{
+ struct wifi_cac_data *cac = &r->radio_el->last_cac_data;
+
+ cntlr_dbg(LOG_CHANNEL, "new cac completion radio " MACFMT " opclass %u channel %u status %u\n",
+ MAC2STR(r->radio_el->macaddr), classid, channel, status);
+
+ if (cac->status != WIFI_ACS_CLEANUP_STATUS_REQUESTED)
+ return;
+
+ if (cac->channel != channel)
+ return;
+ if (cac->opclass != classid)
+ return;
+
+
+ switch (status) {
+ case CAC_COMP_REPORT_STATUS_SUCCESSFUL:
+ cac->status = WIFI_ACS_CLEANUP_STATUS_DONE;
+ break;
+ case CAC_COMP_REPORT_STATUS_CAC_NOT_SUPPORTED:
+ case CAC_COMP_REPORT_STATUS_TOO_BUSY:
+ case CAC_COMP_REPORT_STATUS_NON_CONFORMANT:
+ case CAC_COMP_REPORT_STATUS_OTHER:
+ cac->status = WIFI_ACS_CLEANUP_STATUS_REJECTED;
+
+ cntlr_dfs_cleanup_set_backoff(r, cac->opclass, cac->channel);
+ break;
+ case CAC_COMP_REPORT_STATUS_RADAR_DETECTED:
+ cac->status = WIFI_ACS_CLEANUP_STATUS_RADAR;
+ break;
+ default:
+ break;
+ }
+}
+
+void cntlr_acs_channel_pref_report(struct node *n, struct netif_radio *r)
+{
+ struct wifi_acs_params *acs = &r->radio_el->last_acs;
+ struct wifi_cac_data *cac = &r->radio_el->last_cac_data;
+
+ cntlr_dbg(LOG_CHANNEL, "new pref report node " MACFMT " radio " MACFMT "\n",
+ MAC2STR(n->almacaddr), MAC2STR(r->radio_el->macaddr));
+
+ /* ACS status */
+ if (acs->status == WIFI_ACS_RECALC_STATUS_BEST_REQUESTED ||
+ acs->status == WIFI_ACS_RECALC_STATUS_BEST_ACCEPTED) {
+ struct wifi_radio_opclass_channel *chan;
+
+ chan = wifi_opclass_get_channel(&r->radio_el->pref_opclass,
+ acs->best_opclass,
+ acs->best_channel);
+ if (chan) {
+ switch (chan->dfs) {
+ case WIFI_RADIO_OPCLASS_CHANNEL_DFS_CAC:
+ acs->status = WIFI_ACS_RECALC_STATUS_BEST_CAC;
+ break;
+ case WIFI_RADIO_OPCLASS_CHANNEL_DFS_NOP:
+ acs->status = WIFI_ACS_RECALC_STATUS_BEST_CAC_RADAR;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ /* BG CAC status */
+ if (cac->status == WIFI_ACS_CLEANUP_STATUS_REQUESTED ||
+ cac->status == WIFI_ACS_CLEANUP_STATUS_ACCEPTED ||
+ cac->status == WIFI_ACS_CLEANUP_STATUS_TIMEOUT) {
+ struct wifi_radio_opclass_channel *chan;
+
+ chan = wifi_opclass_get_channel(&r->radio_el->pref_opclass,
+ cac->opclass,
+ cac->channel);
+ if (chan) {
+ switch (chan->dfs) {
+ case WIFI_RADIO_OPCLASS_CHANNEL_DFS_AVAILABLE:
+ cac->status = WIFI_ACS_CLEANUP_STATUS_DONE;
+ break;
+ case WIFI_RADIO_OPCLASS_CHANNEL_DFS_CAC:
+ cac->status = WIFI_ACS_CLEANUP_STATUS_ACCEPTED;
+ break;
+ case WIFI_RADIO_OPCLASS_CHANNEL_DFS_NOP:
+ cac->status = WIFI_ACS_CLEANUP_STATUS_RADAR;
+ break;
+ case WIFI_RADIO_OPCLASS_CHANNEL_DFS_USABLE:
+ if (timestamp_elapsed_sec(&cac->entry_time) > 30 + cac->cac_time)
+ cac->status = WIFI_ACS_CLEANUP_STATUS_TIMEOUT;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ /* Kick CAC background clearing */
+ if (n->cntlr->cfg.dfs_cleanup &&
+ r->radio_el->band == BAND_5 &&
+ r->radio_el->bgcac_supported &&
+ cac->status != WIFI_ACS_CLEANUP_STATUS_REQUESTED &&
+ cac->status != WIFI_ACS_CLEANUP_STATUS_ACCEPTED) {
+ cntlr_dbg(LOG_CHANNEL, "kick dfs cleanup " MACFMT " radio " MACFMT "\n",
+ MAC2STR(n->almacaddr), MAC2STR(r->radio_el->macaddr));
+ cntlr_dfs_radio_cleanup(n, r);
+ }
+
+ /* Kick ACS recalc code */
+ if (acs->recalc && n->cntlr->cfg.acs_timeout) {
+ bool skip_dfs = false;
+ bool prevent_cac = true;
+
+ cntlr_dbg(LOG_CHANNEL, "kick acs reclac " MACFMT " radio " MACFMT "\n",
+ MAC2STR(n->almacaddr), MAC2STR(r->radio_el->macaddr));
+
+ acs->recalc = false;
+ cntlr_acs_node_channel_recalc(n, BAND_ANY, 0, 0, skip_dfs, prevent_cac);
+ }
+}
diff --git a/src/acs.h b/src/acs.h
index 8dc7a024..5c03f80c 100644
--- a/src/acs.h
+++ b/src/acs.h
@@ -13,29 +13,23 @@
struct controller;
struct node;
struct wifi_radio_element;
-
-
-struct acs_params {
- int opclass;
- int bw;
-
- bool skip_dfs;
- bool skip_dfs_not_available;
-
- int best_channel;
- int best_opclass;
- int best_bw;
- int best_pref;
-};
-
-int cntlr_acs_radio_channel_recalc(struct wifi_radio_element *radio, struct acs_params *params);
-void cntlr_acs_node_channel_recalc(struct node *node, bool skip_dfs);
+struct netif_radio;
+enum wifi_band;
+struct wifi_acs_params;
+enum wifi_radio_opclass_dfs;
+
+int cntlr_acs_radio_channel_recalc(struct node *node, struct netif_radio *rd, struct wifi_acs_params *params);
+void cntlr_acs_node_channel_recalc(struct node *node, enum wifi_band band, uint8_t opclass,
+ uint32_t bandwidth, bool skip_dfs, bool prevent_cac);
void cntlr_dfs_node_cleanup(struct node *node);
-void cntlr_acs_recalc(struct controller *c, bool skip_dfs);
+void cntlr_dfs_radio_cleanup(struct node *node, struct netif_radio *radio);
+void cntlr_acs_recalc(struct controller *c, bool skip_dfs, bool skip_cac);
void cntlr_dfs_cleanup(struct controller *c);
int cntlr_radio_pref_opclass_add(struct wifi_radio_element *radio, uint8_t classid,
uint8_t channel, uint8_t preference);
+int cntlr_radio_pref_opclass_set_dfs_status(struct wifi_radio_element *radio, uint8_t classid,
+ uint8_t channel, enum wifi_radio_opclass_dfs state);
int cntlr_radio_pref_opclass_reset(struct wifi_radio_element *radio);
void cntlr_radio_pref_opclass_dump(struct wifi_radio_element *radio);
@@ -49,4 +43,9 @@ uint8_t ctrl_radio_cur_opclass_id(struct wifi_radio_element *radio);
uint8_t ctrl_radio_cur_opclass_ctrl_chan(struct wifi_radio_element *radio);
uint8_t ctrl_radio_cur_opclass_max_bw(struct wifi_radio_element *radio);
+void cntlr_acs_channel_sel_response(struct netif_radio *r, uint16_t mid, uint8_t status);
+void cntlr_acs_oper_channel_report(struct netif_radio *r);
+void cntlr_acs_cac_completion(struct netif_radio *r, uint8_t classid,
+ uint8_t channel, uint8_t status);
+void cntlr_acs_channel_pref_report(struct node *n, struct netif_radio *r);
#endif
diff --git a/src/cntlr.c b/src/cntlr.c
index 9d290cd4..5a1c1eb9 100644
--- a/src/cntlr.c
+++ b/src/cntlr.c
@@ -639,8 +639,7 @@ struct netif_iface *cntlr_radio_add_iface(struct controller *c,
#if (EASYMESH_VERSION >= 6)
/* send channel selection request to propagate puncture bitmap */
- cntlr_send_channel_selection(c, r->agent->almacaddr, r->radio_el->macaddr,
- 0, 0, 0);
+ cntlr_send_channel_selection(c, r->agent->almacaddr, r->radio_el->macaddr, NULL);
#endif
mactable_add_entry(c->mac_table, macaddr, MAC_ENTRY_FBSS, (void *)n);
@@ -688,7 +687,7 @@ struct netif_radio *cntlr_node_add_radio(struct controller *c, struct node *n,
mactable_add_entry(c->mac_table, macaddr, MAC_ENTRY_RADIO, (void *)r);
- cntlr_send_channel_selection(c, n->almacaddr, macaddr, 0, 0, 0);
+ cntlr_send_channel_selection(c, n->almacaddr, macaddr, NULL);
p = cntlr_get_radio_policy(&c->cfg, macaddr);
if (p)
@@ -1451,27 +1450,16 @@ static void cntlr_acs_run(atimer_t *t)
{
struct controller *c = container_of(t, struct controller, acs);
bool skip_dfs = false;
+ bool prevent_cac = true;
/* Run ACS recalc here */
dbg("acs timeout - run recalc\n");
- cntlr_acs_recalc(c, skip_dfs);
+ cntlr_acs_recalc(c, skip_dfs, prevent_cac);
if (c->cfg.acs_timeout)
timer_set(&c->acs, c->cfg.acs_timeout * 1000);
}
-static void cntlr_dfs_cleanup_run(atimer_t *t)
-{
- struct controller *c = container_of(t, struct controller, dfs_cleanup);
-
- /* Run background CAC here */
- dbg("dfs bgcac timeout - run cleanup\n");
- cntlr_dfs_cleanup(c);
-
- if (c->cfg.dfs_cleanup_timeout)
- timer_set(&c->dfs_cleanup, c->cfg.dfs_cleanup_timeout * 1000);
-}
-
static void cntlr_steer_sched_run(atimer_t *t)
{
struct controller *c = container_of(t, struct controller, steer_sched_timer);
@@ -1879,7 +1867,6 @@ void run_controller(void *opts)
timer_init(&c->signal_handler, cntlr_signal_periodic_run);
timer_init(&c->query_nodes, cntlr_query_nodes);
timer_init(&c->acs, cntlr_acs_run);
- timer_init(&c->dfs_cleanup, cntlr_dfs_cleanup_run);
timer_init(&c->steer_sched_timer, cntlr_steer_sched_run);
timer_set(&c->heartbeat, 1 * 1000);
@@ -1890,8 +1877,6 @@ void run_controller(void *opts)
if (c->cfg.acs_timeout)
timer_set(&c->acs, c->cfg.acs_timeout * 1000);
- if (c->cfg.dfs_cleanup_timeout)
- timer_set(&c->dfs_cleanup, c->cfg.dfs_cleanup_timeout * 1000);
c->evh.cb = cntlr_event_handler;
ubus_register_event_handler(ctx, &c->evh, "ubus.object.*");
diff --git a/src/cntlr.h b/src/cntlr.h
index 58139fdc..6489436d 100644
--- a/src/cntlr.h
+++ b/src/cntlr.h
@@ -55,14 +55,6 @@ enum device_type {
IEEE1905
};
-struct cac_data {
- uint8_t radio[6];
- uint8_t opclass;
- uint8_t channel;
- uint8_t cac_method;
- uint8_t cac_action;
-};
-
#define SCAN_REQ_MAX_NUM_RADIO 4
#define SCAN_REQ_MAX_NUM_OPCLASS 8
#define SCAN_REQ_MAX_NUM_CHAN 16
@@ -295,7 +287,6 @@ struct controller {
struct ubus_event_handler evh;
atimer_t heartbeat;
atimer_t acs;
- atimer_t dfs_cleanup;
int num_nodes;
int num_tx_links;
int num_rx_links;
diff --git a/src/cntlr_cmdu.c b/src/cntlr_cmdu.c
index 083cb0b8..87c3a950 100644
--- a/src/cntlr_cmdu.c
+++ b/src/cntlr_cmdu.c
@@ -901,50 +901,40 @@ int cntlr_send_channel_preference_query(struct controller *c, uint8_t *agent)
return 0;
}
-int cntlr_send_channel_selection(struct controller *c, uint8_t *agent, uint8_t *radio,
- uint8_t channel, uint8_t opclass, uint8_t pref)
+uint16_t cntlr_send_channel_selection(struct controller *c, uint8_t *agent, uint8_t *radio,
+ struct wifi_radio_opclass *opclass)
{
- uint8_t chanlist[1] = {};
+ struct cmdu_buff *cmdu;
+ uint16_t mid;
#if (EASYMESH_VERSION >= 6)
struct node *n;
+ int ret;
#endif
- struct cmdu_buff *cmdu;
- uint16_t mid = 0;
- int ret = 0;
-
- chanlist[0] = channel;
- cmdu = cmdu_alloc_simple(CMDU_CHANNEL_SELECTION_REQ, &mid);
+ cmdu = cntlr_gen_channel_sel_request(c, agent, radio, opclass);
if (!cmdu)
return -1;
- memcpy(cmdu->origin, agent, 6);
- ret = cntlr_gen_channel_pref(c, cmdu, radio, opclass, channel ? ARRAY_SIZE(chanlist) : 0, chanlist, pref);
- if (ret) {
- cmdu_free(cmdu);
- return -1;
- }
-
#if (EASYMESH_VERSION >= 6)
n = cntlr_find_node(c, agent);
if (n) {
ret = cntlr_gen_eht_operations(c, cmdu, n);
if (ret) {
cmdu_free(cmdu);
- return -1;
+ return 0xffff;
}
}
#endif
cmdu_put_eom(cmdu);
- send_cmdu(c, cmdu);
+ mid = send_cmdu(c, cmdu);
cmdu_free(cmdu);
- return 0;
+ return mid;
}
struct cmdu_buff* cntlr_gen_cac_req(struct controller *c, uint8_t *agent,
- int num_data, struct cac_data *data)
+ int num_data, struct wifi_cac_data *data)
{
uint16_t mid = 0;
int ret;
@@ -970,7 +960,7 @@ struct cmdu_buff* cntlr_gen_cac_req(struct controller *c, uint8_t *agent,
}
struct cmdu_buff* cntlr_gen_cac_term(struct controller *c, uint8_t *agent,
- int num_data, struct cac_data *data)
+ int num_data, struct wifi_cac_data *data)
{
uint16_t mid = 0;
int ret;
@@ -1013,22 +1003,27 @@ int cntlr_send_channel_scan_request(struct controller *c, uint8_t *agent_mac,
}
int cntlr_send_cac_req(struct controller *c, uint8_t *agent,
- int num_data, struct cac_data *data)
+ int num_data, struct wifi_cac_data *data)
{
struct cmdu_buff *cmdu;
+ uint16_t mid;
+ int i;
cmdu = cntlr_gen_cac_req(c, agent, num_data, data);
if (!cmdu)
return -1;
- send_cmdu(c, cmdu);
+ mid = send_cmdu(c, cmdu);
cmdu_free(cmdu);
+ for(i = 0; i < num_data; i++)
+ data[i].mid = mid;
+
return 0;
}
int cntlr_send_cac_term(struct controller *c, uint8_t *agent,
- int num_data, struct cac_data *data)
+ int num_data, struct wifi_cac_data *data)
{
struct cmdu_buff *cmdu;
diff --git a/src/cntlr_cmdu.h b/src/cntlr_cmdu.h
index 8ea5b7ea..560a5182 100644
--- a/src/cntlr_cmdu.h
+++ b/src/cntlr_cmdu.h
@@ -13,7 +13,7 @@
#include <stdint.h>
#include <stdbool.h>
-struct cac_data;
+struct wifi_cac_data;
struct cmdu_buff;
struct controller;
struct node;
@@ -24,6 +24,7 @@ struct sta_error_response;
struct tlv;
struct unassoc_sta_metric;
struct wifi7_radio_capabilities;
+struct wifi_radio_opclass;
struct cmdu_buff *cntlr_gen_ap_autoconfig_renew(struct controller *c,
uint8_t *dst);
@@ -84,9 +85,9 @@ struct cmdu_buff *cntlr_gen_channel_scan_request(struct controller *c,
struct cmdu_buff *cntlr_gen_channel_preference_query(struct controller *c,
uint8_t *agent);
struct cmdu_buff* cntlr_gen_cac_req(struct controller *c, uint8_t *agent,
- int num_data, struct cac_data *data);
+ int num_data, struct wifi_cac_data *data);
struct cmdu_buff* cntlr_gen_cac_term(struct controller *c, uint8_t *agent,
- int num_data, struct cac_data *data);
+ int num_data, struct wifi_cac_data *data);
struct cmdu_buff *cntlr_gen_bk_caps_query(struct controller *c,
uint8_t *origin);
struct cmdu_buff *cntlr_gen_client_assoc_ctrl_request(struct controller *c,
@@ -104,8 +105,8 @@ struct cmdu_buff *cntlr_gen_client_steer_request(struct controller *c,
struct cmdu_buff *cntlr_gen_comb_infra_metrics_query(struct controller *c,
uint8_t *origin, uint8_t *bssid_mac);
int cntlr_send_channel_preference_query(struct controller *c, uint8_t *agent);
-int cntlr_send_channel_selection(struct controller *c, uint8_t *agent, uint8_t *radio,
- uint8_t channel, uint8_t opclass, uint8_t pref);
+uint16_t cntlr_send_channel_selection(struct controller *c, uint8_t *agent, uint8_t *radio,
+ struct wifi_radio_opclass *opclass);
int cntlr_send_channel_scan_request(struct controller *c, uint8_t *agent_mac,
struct scan_req_data *data);
int cntlr_send_client_assoc_ctrl_request(struct controller *c,
@@ -113,9 +114,9 @@ int cntlr_send_client_assoc_ctrl_request(struct controller *c,
uint8_t assoc_cntl_mode, uint16_t assoc_timeout,
uint8_t sta_nr, uint8_t *stalist, uint16_t *mid);
int cntlr_send_cac_req(struct controller *c, uint8_t *agent,
- int num_data, struct cac_data *data);
+ int num_data, struct wifi_cac_data *data);
int cntlr_send_cac_term(struct controller *c, uint8_t *agent,
- int num_data, struct cac_data *data);
+ int num_data, struct wifi_cac_data *data);
int cntlr_send_client_steer_request(struct controller *c, uint8_t *agent,
uint8_t *bssid, uint32_t steer_timeout,
uint32_t num_sta, uint8_t stas[][6],
diff --git a/src/cntlr_commands.c b/src/cntlr_commands.c
index 97596f80..1dd6a420 100644
--- a/src/cntlr_commands.c
+++ b/src/cntlr_commands.c
@@ -243,7 +243,7 @@ DEFINE_ATTR(send_channel_sel) = {
DEFINE_ATTR(trigger_channel_clearing) = {
[CHANNEL_CLEARING_ATTR_AGENT] = {
- .optional = 0,
+ .optional = 1,
.pol = {
.name = "agent",
.type = BLOBMSG_TYPE_STRING,
@@ -252,6 +252,57 @@ DEFINE_ATTR(trigger_channel_clearing) = {
},
};
+DEFINE_ATTR(trigger_channel_acs) = {
+ [CHANNEL_ACS_ATTR_AGENT] = {
+ .optional = 1,
+ .pol = {
+ .name = "agent",
+ .type = BLOBMSG_TYPE_STRING,
+ },
+ .help = "AL-address of Agent for channel recalc request",
+ },
+ [CHANNEL_ACS_ATTR_BAND] = {
+ .optional = 1,
+ .pol = {
+ .name = "band",
+ .type = BLOBMSG_TYPE_INT32,
+ },
+ .help = "Band for channel recalc request",
+ },
+ [CHANNEL_ACS_ATTR_SKIP_DFS] = {
+ .optional = 1,
+ .pol = {
+ .name = "skip_dfs",
+ .type = BLOBMSG_TYPE_INT32,
+ },
+ .help = "skip DFS channels when recalc",
+ },
+ [CHANNEL_ACS_ATTR_OPCLASS] = {
+ .optional = 1,
+ .pol = {
+ .name = "opclass",
+ .type = BLOBMSG_TYPE_INT32,
+ },
+ .help = "opclass for channel recalc request",
+ },
+ [CHANNEL_ACS_ATTR_BANDWIDTH] = {
+ .optional = 1,
+ .pol = {
+ .name = "bandwidth",
+ .type = BLOBMSG_TYPE_INT32,
+ },
+ .help = "bandwidth for channel recalc request",
+ },
+ [CHANNEL_ACS_ATTR_PREVENT_CAC] = {
+ .optional = 1,
+ .pol = {
+ .name = "prevent_cac",
+ .type = BLOBMSG_TYPE_INT32,
+ },
+ .help = "prevent DFS CAC when recalc",
+ },
+};
+
DEFINE_ATTR(query_beacon_metrics) = {
[BCN_METRICS_ATTR_AGENT] = {
.optional = 0,
@@ -974,6 +1025,7 @@ static struct controller_command cntlr_commandlist[] = {
CNTLR_CMD(query_beacon_metrics, "Send Beacon Metrics Query to Agent"),
CNTLR_CMD(send_channel_sel, "Send Channel Selection Request to Agent"),
CNTLR_CMD(trigger_channel_clearing, "Trigger CAC Request in Agent for clearing DFS channels"),
+ CNTLR_CMD(trigger_channel_acs, "Trigger ACS"),
CNTLR_CMD(steer, "Send STA steering request to Agent"),
CNTLR_CMD(steer_op, "Send STA steering opportunity request to Agent"),
CNTLR_CMD(assoc_control, "Send STA/bSTA association control request to Agent"),
diff --git a/src/cntlr_commands.h b/src/cntlr_commands.h
index 297e7fdb..18533eaf 100644
--- a/src/cntlr_commands.h
+++ b/src/cntlr_commands.h
@@ -214,6 +214,29 @@ enum channel_clearing_attrs {
NUM_ATTRS_CHANNEL_CLEARING,
};
+/**
+ * @enum channel_acs_attrs
+ * @brief ACS Channel attributes
+ *
+ * @ingroup cmdattrs
+ * @CHANNEL_ACS_ATTR_AGENT: Agent's AL-address.
+ * @CHANNEL_ACS_ATTR_BAND: band in Agent.
+ * @CHANNEL_ACS_ATTR_SKIP_DFS: skip DFS channels.
+ * @CHANNEL_ACS_ATTR_OPCLASS: requested opclass.
+ * @CHANNEL_ACS_ATTR_BANDWIDTH: requested bandwidth.
+ * @CHANNEL_ACS_ATTR_PREVENT_CAC: prevent DFS CAC.
+ * @NUM_ATTRS_CHANNEL_ACS: Number of Channel ACS attributes.
+ */
+enum channel_acs_attrs {
+ CHANNEL_ACS_ATTR_AGENT,
+ CHANNEL_ACS_ATTR_BAND,
+ CHANNEL_ACS_ATTR_SKIP_DFS,
+ CHANNEL_ACS_ATTR_OPCLASS,
+ CHANNEL_ACS_ATTR_BANDWIDTH,
+ CHANNEL_ACS_ATTR_PREVENT_CAC,
+ NUM_ATTRS_CHANNEL_ACS,
+};
+
/**
* @enum steer_attrs
* @brief Steer Request attributes
diff --git a/src/cntlr_commands_impl.c b/src/cntlr_commands_impl.c
index fed5360d..526f9006 100644
--- a/src/cntlr_commands_impl.c
+++ b/src/cntlr_commands_impl.c
@@ -717,7 +717,7 @@ int COMMAND(cac_start)(void *priv, void *args, void *out)
struct controller *c = (struct controller *)priv;
struct blob_buf *bb = (struct blob_buf *)out;
struct blob_attr *tb[NUM_ATTRS_CAC_REQUEST];
- struct cac_data *cac_data = NULL;
+ struct wifi_cac_data *cac_data = NULL;
char agent_macstr[18] = {0};
char radiostr[18] = {0};
uint8_t agent[6] = {0};
@@ -777,7 +777,7 @@ int COMMAND(cac_start)(void *priv, void *args, void *out)
* ctrl-channels within the opclass.
*/
do {
- tmp = realloc((void *)cac_data, (num_data + 1) * sizeof(struct cac_data));
+ tmp = realloc((void *)cac_data, (num_data + 1) * sizeof(struct wifi_cac_data));
if (!tmp) {
dbg("%s:%d -ENOMEM\n", __func__, __LINE__);
if (cac_data)
@@ -819,7 +819,7 @@ int COMMAND(cac_stop)(void *priv, void *args, void *out)
struct controller *c = (struct controller *)priv;
struct blob_buf *bb = (struct blob_buf *)out;
struct blob_attr *tb[NUM_ATTRS_CAC_TERMINATE];
- struct cac_data *cac_data = NULL;
+ struct wifi_cac_data *cac_data = NULL;
char agent_macstr[18] = {0};
char radiostr[18] = {0};
uint8_t agent[6] = {0};
@@ -1029,22 +1029,6 @@ int COMMAND(send_hld)(void *priv, void *args, void *out)
return mid != 0xffff ? 0 : -1;
}
-static uint8_t wifi_band_to_band(enum wifi_band band)
-{
- switch (band) {
- case BAND_2:
- return 2;
- case BAND_5:
- return 5;
- case BAND_6:
- return 6;
- default:
- break;
- }
-
- return 0;
-}
-
int COMMAND(send_channel_sel)(void *priv, void *args, void *out)
{
struct controller *c = (struct controller *)priv;
@@ -1105,12 +1089,12 @@ int COMMAND(send_channel_sel)(void *priv, void *args, void *out)
return -EINVAL;
}
- if (wifi_radio_opclass_update_channel(&opclass, band, channel, bandwidth, 15 << 4)) {
+ if (wifi_radio_opclass_update_channel(&opclass, band, channel, bandwidth, 15)) {
err("%s: opclass_update_channel(%u, %u, %u) error\n", __func__, band, channel, bandwidth);
return -EINVAL;
}
- wifi_opclass_dump(&opclass, "chan_selection", radio_mac);
+ wifi_opclass_dump_ex(&opclass, "chan_selection", radio_mac, false);
cmdu = cntlr_gen_channel_sel_request(c, agent_mac, radio_mac, &opclass);
if (!cmdu) {
@@ -1118,6 +1102,7 @@ int COMMAND(send_channel_sel)(void *priv, void *args, void *out)
return -1;
}
+ cmdu_put_eom(cmdu);
mid = send_cmdu(c, cmdu);
cmdu_free(cmdu);
@@ -1129,6 +1114,73 @@ int COMMAND(send_channel_sel)(void *priv, void *args, void *out)
return mid != 0xffff ? 0 : -1;
}
+static char *cntlr_acs_radio_cleanup_status(enum wifi_acs_cleanup_status status)
+{
+ switch (status) {
+ case WIFI_ACS_CLEANUP_STATUS_SKIPPED:
+ return "skipped";
+ case WIFI_ACS_CLEANUP_STATUS_SELECTED:
+ return "selected";
+ case WIFI_ACS_CLEANUP_STATUS_REQUESTED:
+ return "requested";
+ case WIFI_ACS_CLEANUP_STATUS_REJECTED:
+ return "rejected";
+ case WIFI_ACS_CLEANUP_STATUS_RADAR:
+ return "radar";
+ case WIFI_ACS_CLEANUP_STATUS_ACCEPTED:
+ return "accepted";
+ case WIFI_ACS_CLEANUP_STATUS_DONE:
+ return "done";
+ case WIFI_ACS_CLEANUP_STATUS_ALL_CLEAN:
+ return "all clean";
+ case WIFI_ACS_CLEANUP_STATUS_NOT_SUPPORTED:
+ return "not supported";
+ case WIFI_ACS_CLEANUP_STATUS_NO_APS:
+ return "no APs iface";
+ case WIFI_ACS_CLEANUP_STATUS_TIMEOUT:
+ return "timeout";
+ default:
+ break;
+ }
+ return "unknown";
+}
+
+static void cntlr_dfs_radio_cleanup_info(struct node *n, struct netif_radio *r, struct blob_buf *bb)
+{
+ struct wifi_cac_data *cac;
+ void *t;
+
+ cac = &r->radio_el->last_cac_data;
+
+ t = blobmsg_open_table(bb, "");
+ blobmsg_add_macaddr(bb, "agent", n->almacaddr);
+ blobmsg_add_macaddr(bb, "radio", r->radio_el->macaddr);
+ blobmsg_add_string(bb, "status", cntlr_acs_radio_cleanup_status(cac->status));
+ blobmsg_add_u32(bb, "channel", cac->channel);
+ blobmsg_add_u32(bb, "opclass", cac->opclass);
+
+ if (cac->status == WIFI_ACS_CLEANUP_STATUS_REQUESTED)
+ blobmsg_add_u32(bb, "mid", cac->mid);
+ blobmsg_close_table(bb, t);
+}
+
+static void cntlr_acs_radio_cleanup_info(struct blob_buf *bb, struct wifi_cac_data *cac)
+{
+ void *t, *a;
+
+ blobmsg_add_u32(bb, "cleanup_request_age", (uint32_t) timestamp_elapsed_sec(&cac->entry_time));
+ a = blobmsg_open_array(bb, "cleanup_request");
+ t = blobmsg_open_table(bb, "");
+ blobmsg_add_string(bb, "status", cntlr_acs_radio_cleanup_status(cac->status));
+ blobmsg_add_u32(bb, "channel", cac->channel);
+ blobmsg_add_u32(bb, "opclass", cac->opclass);
+
+ if (cac->status == WIFI_ACS_CLEANUP_STATUS_REQUESTED)
+ blobmsg_add_u32(bb, "mid", cac->mid);
+ blobmsg_close_table(bb, t);
+ blobmsg_close_array(bb, a);
+}
+
int COMMAND(trigger_channel_clearing)(void *priv, void *args, void *out)
{
struct controller *c = (struct controller *)priv;
@@ -1145,20 +1197,180 @@ int COMMAND(trigger_channel_clearing)(void *priv, void *args, void *out)
return -EINVAL;
}
- strncpy(agent, blobmsg_data(tb[CHANNEL_CLEARING_ATTR_AGENT]), sizeof(agent) - 1);
- if (!hwaddr_aton(agent, agent_mac))
+ if (tb[CHANNEL_CLEARING_ATTR_AGENT]) {
+ strncpy(agent, blobmsg_data(tb[CHANNEL_CLEARING_ATTR_AGENT]), sizeof(agent) - 1);
+ if (!hwaddr_aton(agent, agent_mac))
+ return -EINVAL;
+ }
+
+ list_for_each_entry(node, &c->nodelist, list) {
+ struct netif_radio *radio = NULL;
+
+ if (!hwaddr_is_zero(agent_mac) && memcmp(agent_mac, node->almacaddr, 6))
+ continue;
+
+ list_for_each_entry(radio, &node->radiolist, list) {
+ if (radio->radio_el->band != BAND_5)
+ continue;
+
+ /* Action here */
+ cntlr_dfs_radio_cleanup(node, radio);
+ cntlr_dfs_radio_cleanup_info(node, radio, bb);
+ }
+ }
+
+ return 0;
+}
+
+static char *cntlr_acs_radio_status(enum wifi_acs_recalc_status status)
+{
+ switch (status) {
+ case WIFI_ACS_RECALC_STATUS_SKIPPED:
+ return "skipped";
+ case WIFI_ACS_RECALC_STATUS_BEST_SELECTED:
+ return "best_selected";
+ case WIFI_ACS_RECALC_STATUS_BEST_REQUESTED:
+ return "best_requested";
+ case WIFI_ACS_RECALC_STATUS_BEST_ACCEPTED:
+ return "best_accepted";
+ case WIFI_ACS_RECALC_STATUS_BEST_REJECTED:
+ return "best_rejected";
+ case WIFI_ACS_RECALC_STATUS_BEST_SET:
+ return "best_set";
+ case WIFI_ACS_RECALC_STATUS_INPUT_DATA_INSUFFICIENT:
+ return "insufficient_data";
+ case WIFI_ACS_RECALC_STATUS_BEST_ALREADY_SET:
+ return "current_best";
+ case WIFI_ACS_RECALC_STATUS_BSTA_CONNECTED:
+ return "skipped - bsta connected";
+ case WIFI_ACS_RECALC_STATUS_INVALID_DATA:
+ return "skipped - invalid data";
+ case WIFI_ACS_RECALC_STATUS_BEST_SET_TIMEOUT:
+ return "best_set timeout";
+ case WIFI_ACS_RECALC_STATUS_BEST_CAC:
+ return "best_set CAC ongoing";
+ case WIFI_ACS_RECALC_STATUS_BEST_CAC_RADAR:
+ return "best_set CAC RADAR hit";
+ default:
+ break;
+ }
+
+ return "recalc_unknown";
+}
+
+static void cntlr_acs_radio_info(struct blob_buf *bb, struct wifi_acs_params *acs)
+{
+ void *t, *a;
+
+ blobmsg_add_u32(bb, "acs_request_age", (uint32_t) timestamp_elapsed_sec(&acs->entry_time));
+ a = blobmsg_open_array(bb, "acs_request");
+ t = blobmsg_open_table(bb, "");
+ blobmsg_add_string(bb, "status", cntlr_acs_radio_status(acs->status));
+ blobmsg_add_u32(bb, "channel", acs->best_channel);
+ blobmsg_add_u32(bb, "bandwidth", acs->best_bw);
+
+ if (acs->status == WIFI_ACS_RECALC_STATUS_BEST_REQUESTED)
+ blobmsg_add_u32(bb, "mid", acs->mid);
+ blobmsg_close_table(bb, t);
+ blobmsg_close_array(bb, a);
+}
+
+static void cntlr_acs_node_info(struct node *node, enum wifi_band band, struct blob_buf *bb)
+{
+ struct netif_radio *r = NULL;
+ void *t;
+
+ list_for_each_entry(r, &node->radiolist, list) {
+ struct wifi_acs_params *acs;
+
+ acs = &r->radio_el->last_acs;
+
+ if (band && band != BAND_ANY && band != r->radio_el->band)
+ continue;
+
+ if (acs->status == WIFI_ACS_RECALC_STATUS_SKIPPED)
+ continue;
+
+ t = blobmsg_open_table(bb, "");
+ blobmsg_add_macaddr(bb, "agent", node->almacaddr);
+ blobmsg_add_macaddr(bb, "radio", r->radio_el->macaddr);
+ blobmsg_add_string(bb, "status", cntlr_acs_radio_status(acs->status));
+ blobmsg_add_u32(bb, "channel", acs->best_channel);
+ blobmsg_add_u32(bb, "bandwidth", acs->best_bw);
+
+ if (acs->status == WIFI_ACS_RECALC_STATUS_BEST_REQUESTED)
+ blobmsg_add_u32(bb, "mid", acs->mid);
+
+ blobmsg_close_table(bb, t);
+ }
+}
+
+int COMMAND(trigger_channel_acs)(void *priv, void *args, void *out)
+{
+ struct controller *c = (struct controller *)priv;
+ struct blob_buf *bb = (struct blob_buf *)out;
+ struct blob_attr *tb[NUM_ATTRS_CHANNEL_ACS];
+ uint8_t agent_mac[6] = { 0 };
+ enum wifi_band band = BAND_ANY;
+ struct node *node = NULL;
+ char agent[18] = { 0 };
+ bool skip_dfs = false;
+ bool prevent_cac = true;
+ uint32_t bandwidth = 0;
+ uint8_t opclass = 0;
+ int ret;
+
+ ret = controller_command_parse("trigger_channel_acs", args, tb);
+ if (ret) {
+ err("%s: Error (ret = %d)\n", __func__, ret);
return -EINVAL;
+ }
+
+ if (tb[CHANNEL_ACS_ATTR_SKIP_DFS])
+ skip_dfs = blobmsg_get_u32(tb[CHANNEL_ACS_ATTR_SKIP_DFS]);
+
+ if (tb[CHANNEL_ACS_ATTR_PREVENT_CAC])
+ prevent_cac = blobmsg_get_u32(tb[CHANNEL_ACS_ATTR_PREVENT_CAC]);
+
+ if (tb[CHANNEL_ACS_ATTR_AGENT]) {
+ strncpy(agent, blobmsg_data(tb[CHANNEL_ACS_ATTR_AGENT]), sizeof(agent) - 1);
+ if (!hwaddr_aton(agent, agent_mac))
+ return -EINVAL;
+ }
+
+ if (tb[CHANNEL_ACS_ATTR_BAND]) {
+ switch (blobmsg_get_u32(tb[CHANNEL_ACS_ATTR_BAND])) {
+ case 2:
+ band = BAND_2;
+ break;
+ case 5:
+ band = BAND_5;
+ break;
+ case 6:
+ band = BAND_6;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (tb[CHANNEL_ACS_ATTR_OPCLASS])
+ opclass = blobmsg_get_u32(tb[CHANNEL_ACS_ATTR_OPCLASS]);
+
+ if (tb[CHANNEL_ACS_ATTR_BANDWIDTH])
+ bandwidth = blobmsg_get_u32(tb[CHANNEL_ACS_ATTR_BANDWIDTH]);
list_for_each_entry(node, &c->nodelist, list) {
if (!hwaddr_is_zero(agent_mac) && memcmp(agent_mac, node->almacaddr, 6))
continue;
/* Action here */
- cntlr_dfs_node_cleanup(node);
+ cntlr_acs_node_channel_recalc(node, band, opclass, bandwidth, skip_dfs, prevent_cac);
+
+ /* Show information about recalc */
+ cntlr_acs_node_info(node, band, bb);
}
- /* reply with status ok */
- blobmsg_add_string(bb, "status", "ok");
return 0;
}
@@ -2848,7 +3060,8 @@ out:
return mid != 0xffff ? 0 : -1;
}
-static char *cntlr_status_channel_pref_reason(uint8_t reason)
+static char *cntlr_status_channel_pref_reason(uint8_t reason,
+ enum wifi_radio_opclass_dfs status)
{
switch (reason) {
case CHANNEL_PREF_REASON_UNSPEC:
@@ -2870,7 +3083,13 @@ static char *cntlr_status_channel_pref_reason(uint8_t reason)
case CHANNEL_PREF_REASON_SHARED_BHAUL_PREVENT:
return "shared-bhaul-prevent";
case CHANNEL_PREF_REASON_DFS_USABLE:
- return "dfs-usable";
+ /* Three options here, CAC required or CAC ongoing CAC backoff */
+ if (status == WIFI_RADIO_OPCLASS_CHANNEL_DFS_CAC)
+ return "dfs-cac";
+ else if (status == WIFI_RADIO_OPCLASS_CHANNEL_DFS_BACKOFF)
+ return "dfs-usable-backoff";
+ else
+ return "dfs-usable";
case CHANNEL_PREF_REASON_DFS_AVAILABLE:
return "dfs-available";
case CHANNEL_PREF_REASON_REG_DISALLOWED:
@@ -2885,6 +3104,7 @@ static char *cntlr_status_channel_pref_reason(uint8_t reason)
void cntlr_status_add_opclass(struct blob_buf *bb, struct wifi_radio_opclass *opclass,
const char *name, int opclass_id)
{
+ enum wifi_radio_opclass_dfs dfs_status;
uint32_t cac_methods, cac_time;
void *a, *aa, *t, *tt;
uint8_t reas, pref;
@@ -2911,9 +3131,10 @@ void cntlr_status_add_opclass(struct blob_buf *bb, struct wifi_radio_opclass *op
if (!strstr(name, "cur")) {
pref = (opclass->opclass[j].channel[k].preference & CHANNEL_PREF_MASK) >> 4;
reas = opclass->opclass[j].channel[k].preference & CHANNEL_PREF_REASON;
+ dfs_status = opclass->opclass[j].channel[k].dfs;
blobmsg_add_u32(bb, "preference", pref);
- blobmsg_add_string(bb, "reason", cntlr_status_channel_pref_reason(reas));
+ blobmsg_add_string(bb, "reason", cntlr_status_channel_pref_reason(reas, dfs_status));
if (opclass->opclass[j].channel[k].cac_methods) {
cac_methods = opclass->opclass[j].channel[k].cac_methods;
@@ -2965,6 +3186,10 @@ static int _cntlr_status(struct controller *c, void *args, void *out, bool full)
/* Show current/prefered opclasses */
cntlr_status_add_opclass(bb, &p->radio_el->cur_opclass, "cur_opclass", 0);
+ cntlr_acs_radio_info(bb, &p->radio_el->last_acs);
+
+ if (p->radio_el->bgcac_supported)
+ cntlr_acs_radio_cleanup_info(bb, &p->radio_el->last_cac_data);
/* Limit opclass output if possible */
cur_opclass_id = ctrl_radio_cur_opclass_id(p->radio_el);
@@ -3098,7 +3323,6 @@ int COMMAND(timers)(void *priv, void *args, void *out)
t = blobmsg_open_table(bb, "channel_planning");
blobmsg_add_u32(bb, "channel_plan", timer_remaining_ms(&c->acs));
- blobmsg_add_u32(bb, "allow_bgdfs", timer_remaining_ms(&c->dfs_cleanup));
blobmsg_close_table(bb, t);
return 0;
diff --git a/src/cntlr_commands_impl.h b/src/cntlr_commands_impl.h
index 84f4dd25..069122b5 100644
--- a/src/cntlr_commands_impl.h
+++ b/src/cntlr_commands_impl.h
@@ -38,6 +38,7 @@ DECLARE_COMMAND(query_channel_pref);
DECLARE_COMMAND(query_beacon_metrics);
DECLARE_COMMAND(send_channel_sel);
DECLARE_COMMAND(trigger_channel_clearing);
+DECLARE_COMMAND(trigger_channel_acs);
DECLARE_COMMAND(steer);
DECLARE_COMMAND(steer_op);
DECLARE_COMMAND(assoc_control);
diff --git a/src/cntlr_map.c b/src/cntlr_map.c
index b26ba55c..24cbbac3 100644
--- a/src/cntlr_map.c
+++ b/src/cntlr_map.c
@@ -1601,6 +1601,9 @@ static int cntlr_parse_radio_cac_caps(struct controller *c, struct node *n, stru
chan->cac_methods |= get_supp_methods(cac->supp_method);
chan->cac_time = get_duration_data(cac->duration);
+
+ if (chan->cac_methods && chan->cac_methods != (1 << WIFI_CAC_CONTINUOUS))
+ radio_el->bgcac_supported = true;
}
}
}
@@ -1852,13 +1855,137 @@ int handle_channel_pref_report(void *cntlr, struct cmdu_buff *cmdu, struct node
cntlr_radio_pref_opclass_dump(r->radio_el);
}
+ idx = 0;
+ while (tv[CHANNEL_PREF_REPORT_CAC_COMPLETION_REPORT_IDX][idx]) {
+ struct tlv *t = (struct tlv *)tv[CHANNEL_PREF_REPORT_CAC_COMPLETION_REPORT_IDX][idx++];
+ uint8_t num_radio;
+ uint8_t num_pairs;
+
+ offset = 0;
+ num_radio = t->data[offset++];
+
+ for (i = 0; i < num_radio; i++) {
+ uint8_t mac[6] = { 0 };
+ uint8_t opclass, channel, status;
+
+ memcpy(mac, &t->data[offset], 6);
+ offset += 6;
+
+ opclass = t->data[offset++];
+ channel = t->data[offset++];
+ status = t->data[offset++];
+
+ num_pairs = t->data[offset++];
+ for (j = 0; j < num_pairs; j++)
+ offset += 2;
+
+ r = cntlr_find_radio(cntlr, mac);
+ if (!r)
+ continue;
+
+ /* Kick ACS code */
+ cntlr_acs_cac_completion(r, opclass, channel, status);
+ }
+
+ break;
+ }
+
+ idx = 0;
+ while (tv[CHANNEL_PREF_REPORT_CAC_STATUS_REPORT_IDX][idx]) {
+ struct tlv *t = (struct tlv *)tv[CHANNEL_PREF_REPORT_CAC_STATUS_REPORT_IDX][idx++];
+ uint8_t channel, opclassid;
+ uint16_t time;
+ uint8_t num;
+
+ r = cntlr_find_radio_in_node_by_band(n->cntlr, n, BAND_5);
+ if (!r)
+ break;
+
+ offset = 0;
+
+ /* CAC completed */
+ num = t->data[offset++];
+ for (i = 0; i < num; i++) {
+ opclassid = t->data[offset++];
+ channel = t->data[offset++];
+ time = BUF_GET_BE16(t->data[offset]);
+ offset += 2;
+
+ cntlr_radio_pref_opclass_set_dfs_status(r->radio_el, opclassid, channel,
+ time ? WIFI_RADIO_OPCLASS_CHANNEL_DFS_AVAILABLE :
+ WIFI_RADIO_OPCLASS_CHANNEL_DFS_NONE);
+ }
+
+ /* NOP */
+ num = t->data[offset++];
+ for (i = 0; i < num; i++) {
+ opclassid = t->data[offset++];
+ channel = t->data[offset++];
+ time = BUF_GET_BE16(t->data[offset]);
+ offset += 2;
+
+ cntlr_radio_pref_opclass_set_dfs_status(r->radio_el, opclassid, channel,
+ WIFI_RADIO_OPCLASS_CHANNEL_DFS_NOP);
+ }
+
+ /* CAC ongoing */
+ num = t->data[offset++];
+ for (i = 0; i < num; i++) {
+ opclassid = t->data[offset++];
+ channel = t->data[offset++];
+ time = BUF_GET_BE24(t->data[offset]);
+ offset += 3;
+
+ cntlr_radio_pref_opclass_set_dfs_status(r->radio_el, opclassid, channel,
+ WIFI_RADIO_OPCLASS_CHANNEL_DFS_CAC);
+ }
+
+ /* Kick ACS code */
+ cntlr_acs_channel_pref_report(n, r);
+
+ /* Only one allowed */
+ break;
+ }
+
return 0;
}
int handle_channel_sel_response(void *cntlr, struct cmdu_buff *cmdu, struct node *n)
{
- trace("%s: --->\n", __func__);
- return 0;
+ int idx = 0;
+ struct controller *c = (struct controller *) cntlr;
+ struct tlv *tv[CHANNEL_SELECTION_RESP_NUM_OF_TLV_TYPES][TLV_MAXNUM] = {0};
+ uint16_t mid;
+
+ cntlr_dbg(LOG_CHANNEL, "%s called\n", __func__);
+
+ if (!map_cmdu_validate_parse(cmdu, tv, ARRAY_SIZE(tv), n->map_profile)) {
+ dbg("%s: map_cmdu_validate_parse ( ..,EMP=%d) failed, err = (%d) '%s'\n", __func__,
+ n->map_profile, map_error, map_strerror(map_error));
+ return -1;
+ }
+
+ mid = cmdu_get_mid(cmdu);
+ while (idx < TLV_MAXNUM && tv[CHANNEL_SELECTION_RESP_CHANNEL_SELECTION_IDX][idx]) {
+ struct netif_radio *r;
+ struct tlv_channel_selection_resp *p;
+
+ p = (struct tlv_channel_selection_resp *)
+ tv[CHANNEL_SELECTION_RESP_CHANNEL_SELECTION_IDX][idx++]->data;
+
+ cntlr_dbg(LOG_CHANNEL, "\tmid: %d\n", mid);
+ cntlr_dbg(LOG_CHANNEL, "\tradio_id: " MACFMT "\n", MAC2STR(p->radio));
+ cntlr_dbg(LOG_CHANNEL, "\tresponse_code: %d\n", p->response);
+
+ r = cntlr_find_radio(c, p->radio);
+ if (!r)
+ continue;
+
+ /* kick acs code */
+ cntlr_acs_channel_sel_response(r, mid, p->response);
+ }
+
+ return 0;
}
int handle_oper_channel_report(void *cntlr, struct cmdu_buff *cmdu, struct node *n)
@@ -1929,6 +2056,9 @@ int handle_oper_channel_report(void *cntlr, struct cmdu_buff *cmdu, struct node
}
cntlr_radio_cur_opclass_dump(r->radio_el);
+
+ /* kick acs code */
+ cntlr_acs_oper_channel_report(r);
}
return 0;
diff --git a/src/cntlr_tlv.c b/src/cntlr_tlv.c
index 1f0ec6c4..7ce1f12f 100644
--- a/src/cntlr_tlv.c
+++ b/src/cntlr_tlv.c
@@ -1357,6 +1357,7 @@ struct cmdu_buff *cntlr_gen_channel_sel_request(struct controller *c,
{
struct wifi_radio_opclass_entry *entry;
struct wifi_radio_opclass_channel *channel;
+ struct wifi_radio_opclass empty = {};
int ret, offset = 0;
struct cmdu_buff *cmdu;
struct netif_radio *radio;
@@ -1391,6 +1392,12 @@ struct cmdu_buff *cntlr_gen_channel_sel_request(struct controller *c,
memcpy(&t->data[offset], radio_id, 6); /* radio id */
offset += 6;
+ if (!opclass) {
+ memcpy(&empty, &radio->radio_el->supp_opclass, sizeof(empty));
+ wifi_opclass_set_preferences(&empty, 0x0);
+ opclass = ∅
+ }
+
/* Now update prefered */
opclass_num_offset = offset;
t->data[offset++] = 0; /* m */
@@ -1410,125 +1417,60 @@ struct cmdu_buff *cntlr_gen_channel_sel_request(struct controller *c,
continue;
}
- /* Group disabled channels - assume other with pref=15 */
- t->data[offset++] = entry->id;
- channel_offset = offset;
- t->data[offset++] = 0; /* k */
-
- for (j = 0; j < entry->num_channel; j++) {
- channel = &entry->channel[j];
-
- if (((channel->preference & CHANNEL_PREF_MASK) >> 4) != 0)
- continue;
-
- t->data[offset++] = channel->channel;
- channel_num++;
- }
-
- t->data[offset++] = 0;
- t->data[channel_offset] = channel_num;
- opclass_num++;
- }
-
- t->data[opclass_num_offset] = opclass_num; /* m */
- t->len = offset;
- ret = cmdu_put_tlv(cmdu, t);
- if (ret) {
- dbg("%s: error: cmdu_put_tlv()\n", __func__);
- cmdu_free(cmdu);
- return NULL;
- }
+ if (wifi_opclass_same_preference(opclass, &preference)) {
+ /* Group disabled channels - assume other with pref=15 */
+ t->data[offset++] = entry->id;
+ channel_offset = offset;
+ t->data[offset++] = 0; /* k */
- cmdu_put_eom(cmdu);
- return cmdu;
-}
+ for (j = 0; j < entry->num_channel; j++) {
+ channel = &entry->channel[j];
-int cntlr_gen_channel_pref(struct controller *c, struct cmdu_buff *frm,
- uint8_t *radio_id, uint8_t class_id, uint8_t channel_nr,
- const uint8_t *chanlist, uint8_t pref)
-{
- int ret, offset = 0;
- struct wifi_radio_opclass opclass = {};
- struct wifi_radio_opclass_entry *entry;
- struct wifi_radio_opclass_channel *channel;
- struct netif_radio *radio;
- struct tlv *t;
- int opclass_num_offset;
- int opclass_num;
- int i, j;
-
- /* Find radio and supported opclasses */
- radio = cntlr_find_radio(c, radio_id);
- if (!radio)
- return -1;
-
- if (!radio->radio_el)
- return -1;
-
- if (!radio->radio_el->supp_opclass.num_opclass && channel_nr)
- return -1;
+ if (((channel->preference & CHANNEL_PREF_MASK) >> 4) != 0)
+ continue;
- /* Build opclass we would like to send */
- memcpy(&opclass, &radio->radio_el->supp_opclass, sizeof(opclass));
- wifi_opclass_set_preferences(&opclass, 0x0);
-
- /* Update requested preference */
- for (i = 0; i < channel_nr; i++)
- wifi_opclass_id_set_channel_preferences(&opclass, class_id, chanlist[i], pref << 4);
-
- wifi_opclass_dump(&opclass, "send_chan_pref", radio->radio_el->macaddr);
-
- t = cmdu_reserve_tlv(frm, 1024);
- if (!t)
- return -1;
-
- /* Prefer all supported - don't include any opclass */
- if (!channel_nr)
- opclass.num_opclass = 0;
-
- t->type = MAP_TLV_CHANNEL_PREFERENCE;
-
- memcpy(&t->data[offset], radio_id, 6); /* radio id */
- offset += 6;
+ t->data[offset++] = channel->channel;
+ channel_num++;
+ }
- /* Now update prefered */
- opclass_num_offset = offset;
- t->data[offset++] = 0; /* m */
+ t->data[offset++] = 0;
+ t->data[channel_offset] = channel_num;
+ opclass_num++;
+ continue;
+ }
- opclass_num = 0;
- for (i = 0; i < opclass.num_opclass; i++) {
- entry = &opclass.opclass[i];
- uint8_t preference;
+ /* Setup whole opclass disabled */
+ t->data[offset++] = entry->id;
+ t->data[offset++] = 0; /* k */
+ t->data[offset++] = 0;
+ opclass_num++;
- if (wifi_opclass_id_same_preference(&opclass, entry->id, &preference)) {
- t->data[offset++] = entry->id;
- t->data[offset++] = 0; /* k */
- t->data[offset++] = preference;
- opclass_num++;
- continue;
- }
+ /* Next unlock required channels */
+ for (j = 0; j < entry->num_channel; j++) {
+ channel = &entry->channel[j];
- for (j = 0; j < entry->num_channel; j++) {
- channel = &entry->channel[j];
+ if (((channel->preference & CHANNEL_PREF_MASK) >> 4) == 0)
+ continue;
- t->data[offset++] = entry->id;
- t->data[offset++] = 1; /* k */
- t->data[offset++] = channel->channel;
- t->data[offset++] = channel->preference;
+ t->data[offset++] = entry->id;
+ t->data[offset++] = 1; /* k */
+ t->data[offset++] = channel->channel;
+ t->data[offset++] = channel->preference;
- opclass_num++;
- }
+ opclass_num++;
+ }
}
t->data[opclass_num_offset] = opclass_num; /* m */
t->len = offset;
- ret = cmdu_put_tlv(frm, t);
+ ret = cmdu_put_tlv(cmdu, t);
if (ret) {
dbg("%s: error: cmdu_put_tlv()\n", __func__);
- return -1;
+ cmdu_free(cmdu);
+ return NULL;
}
- return 0;
+ return cmdu;
}
int cntlr_gen_txpower_limit(struct controller *c, struct cmdu_buff *frm,
@@ -1559,7 +1501,7 @@ int cntlr_gen_txpower_limit(struct controller *c, struct cmdu_buff *frm,
}
int cntlr_gen_cac_tlv(struct controller *c, struct cmdu_buff *frm,
- uint8_t tlv_type, int num_data, struct cac_data *cac_data)
+ uint8_t tlv_type, int num_data, struct wifi_cac_data *cac_data)
{
int i, ret, offset = 0;
struct tlv *t;
diff --git a/src/cntlr_tlv.h b/src/cntlr_tlv.h
index bb64f3dd..6e1d659e 100644
--- a/src/cntlr_tlv.h
+++ b/src/cntlr_tlv.h
@@ -14,7 +14,7 @@
#include <1905_tlvs.h>
#include <stdbool.h>
-struct cac_data;
+struct wifi_cac_data;
struct cmdu_buff;
struct controller;
struct iface_credential;
@@ -144,13 +144,10 @@ int cnltr_gen_searched_service(struct controller *c, struct cmdu_buff *frm,
uint8_t service);
int agent_gen_tlv_error_code(struct controller *c,
struct cmdu_buff *cmdu, uint8_t *macaddr, uint8_t reason_code);
-int cntlr_gen_channel_pref(struct controller *c, struct cmdu_buff *frm,
- uint8_t *radio_id, uint8_t class_id, uint8_t channel_nr,
- const uint8_t *chanlist, uint8_t pref);
int cntlr_gen_txpower_limit(struct controller *c, struct cmdu_buff *frm,
uint8_t *radio_id, uint8_t txpower_limit);
int cntlr_gen_cac_tlv(struct controller *c, struct cmdu_buff *frm,
- uint8_t tlv_type, int num_data, struct cac_data *data);
+ uint8_t tlv_type, int num_data, struct wifi_cac_data *data);
int cntlr_gen_tlv_error_code(struct controller *c,
struct cmdu_buff *frm, uint8_t *macaddr, uint8_t reason_code);
int cntlr_gen_tlv_higher_layer_data(struct controller *c, struct cmdu_buff *frm,
diff --git a/src/cntlr_ubus.c b/src/cntlr_ubus.c
index d26eeb77..dd5378f8 100644
--- a/src/cntlr_ubus.c
+++ b/src/cntlr_ubus.c
@@ -409,6 +409,34 @@ out:
return ret;
}
+int cntlr_ubus_trigger_channel_acs(struct ubus_context *ctx,
+ struct ubus_object *obj,
+ struct ubus_request_data *req,
+ const char *method,
+ struct blob_attr *msg)
+{
+ //TRACE_ENTER();
+ struct controller *c = container_of(obj, struct controller, obj);
+ struct blob_buf bb;
+ int ret;
+
+ memset(&bb, 0, sizeof(bb));
+ blob_buf_init(&bb, 0);
+
+ ret = COMMAND(trigger_channel_acs)(c, msg, &bb);
+ if (ret) {
+ if (ret == -EINVAL)
+ ret = UBUS_STATUS_INVALID_ARGUMENT;
+
+ goto out;
+ }
+
+ ubus_send_reply(ctx, req, bb.head);
+out:
+ blob_buf_free(&bb);
+ return ret;
+}
+
int cntlr_ubus_steer(struct ubus_context *ctx,
struct ubus_object *obj,
struct ubus_request_data *req,
@@ -1102,6 +1130,7 @@ int cntlr_publish_object(struct controller *c, const char *objname)
{ "query_beacon_metrics", cntlr_ubus_query_beacon_metrics },
{ "send_channel_sel", cntlr_ubus_send_channel_sel },
{ "trigger_channel_clearing", cntlr_ubus_trigger_channel_clearing },
+ { "trigger_channel_acs", cntlr_ubus_trigger_channel_acs },
{ "steer", cntlr_ubus_steer },
{ "steer_op", cntlr_ubus_steer_op },
{ "assoc_control", cntlr_ubus_assoc_control },
diff --git a/src/cntlr_ubus_dbg.c b/src/cntlr_ubus_dbg.c
index e66a91b0..236446d0 100644
--- a/src/cntlr_ubus_dbg.c
+++ b/src/cntlr_ubus_dbg.c
@@ -214,7 +214,7 @@ static int cntlr_dbg_csr(struct ubus_context *ctx,
struct netif_radio *r = NULL;
list_for_each_entry(r, &node->radiolist, list) {
- cntlr_send_channel_selection(c, node->almacaddr, r->radio_el->macaddr, 0, 0, 0);
+ cntlr_send_channel_selection(c, node->almacaddr, r->radio_el->macaddr, NULL);
}
}
diff --git a/src/config.c b/src/config.c
index 45004728..6b11a0a1 100644
--- a/src/config.c
+++ b/src/config.c
@@ -721,7 +721,7 @@ static int cntlr_config_get_base(struct controller_config *c,
CNTLR_BCN_METRICS_MAX_NUM,
CNTLR_INITIAL_CHANNEL_SCAN,
CNTLR_CHANNEL_PLAN_TIMEOUT,
- CNTLR_BGDFS_TIMEOUT,
+ CNTLR_BGDFS,
CNTLR_STALE_STA_TIMEOUT,
CNTLR_PRIMARY_VID,
CNTLR_DEFAULT_PCP,
@@ -741,7 +741,7 @@ static int cntlr_config_get_base(struct controller_config *c,
[CNTLR_BCN_METRICS_MAX_NUM] = { .name = "bcn_metrics_max_num", .type = UCI_TYPE_STRING },
[CNTLR_INITIAL_CHANNEL_SCAN] = { .name = "initial_channel_scan", .type = UCI_TYPE_STRING },
[CNTLR_CHANNEL_PLAN_TIMEOUT] = { .name = "channel_plan", .type = UCI_TYPE_STRING },
- [CNTLR_BGDFS_TIMEOUT] = { .name = "allow_bgdfs", .type = UCI_TYPE_STRING },
+ [CNTLR_BGDFS] = { .name = "allow_bgdfs", .type = UCI_TYPE_STRING },
[CNTLR_STALE_STA_TIMEOUT] = { .name = "stale_sta_timeout", .type = UCI_TYPE_STRING },
[CNTLR_PRIMARY_VID] = { .name = "primary_vid", .type = UCI_TYPE_STRING },
[CNTLR_DEFAULT_PCP] = { .name = "default_pcp", .type = UCI_TYPE_STRING },
@@ -869,18 +869,19 @@ static int cntlr_config_get_base(struct controller_config *c,
c->acs_timeout = 3600 * 3;
}
- if (tb[CNTLR_BGDFS_TIMEOUT]) {
- const char *val = tb[CNTLR_BGDFS_TIMEOUT]->v.string;
+ if (tb[CNTLR_BGDFS]) {
+ const char *val = tb[CNTLR_BGDFS]->v.string;
errno = 0;
- c->dfs_cleanup_timeout = strtol(val, &endptr, 10);
+ c->dfs_cleanup = !!strtol(val, &endptr, 10);
if (errno || *endptr != '\0') {
cntlr_dbg(LOG_MISC, "|%s:%d| Error parsing allow_bgdfs value: %s\n",
__func__, __LINE__, val);
return -1;
}
- if (c->dfs_cleanup_timeout > 0 && c->dfs_cleanup_timeout < 120)
- c->dfs_cleanup_timeout = 120;
+ } else {
+ cntlr_dbg(LOG_MISC, "|%s:%d| Enable background DFS clearing\n", __func__, __LINE__);
+ c->dfs_cleanup = true;
}
#define DEFAULT_STALE_STA_TIMEOUT (30 * 86400) /* Default 30days */
diff --git a/src/config.h b/src/config.h
index cb45fb50..bd728acf 100644
--- a/src/config.h
+++ b/src/config.h
@@ -259,7 +259,7 @@ struct controller_config {
int num_bss;
int num_apolicy;
int acs_timeout;
- int dfs_cleanup_timeout;
+ bool dfs_cleanup;
int stale_sta_timeout;
unsigned int primary_vid;
unsigned int default_pcp;
diff --git a/src/utils/utils.c b/src/utils/utils.c
index 93fc6522..13e9e0e1 100644
--- a/src/utils/utils.c
+++ b/src/utils/utils.c
@@ -590,6 +590,22 @@ const char *wifi_band_to_str(uint32_t band)
return NULL;
}
+uint8_t wifi_band_to_band(enum wifi_band band)
+{
+ switch (band) {
+ case BAND_2:
+ return 2;
+ case BAND_5:
+ return 5;
+ case BAND_6:
+ return 6;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
int time_str_to_sec(const char *val)
{
if (!val || *val == '\0') {
diff --git a/src/utils/utils.h b/src/utils/utils.h
index afb812ac..c26b9dff 100644
--- a/src/utils/utils.h
+++ b/src/utils/utils.h
@@ -160,6 +160,7 @@ int rcpi_to_rssi(uint8_t rcpi);
bool is_vid_valid(uint16_t vid);
uint16_t wifi_sec_to_auth_types(enum wifi_security sec);
const char *wifi_band_to_str(uint32_t band);
+uint8_t wifi_band_to_band(enum wifi_band band);
int time_str_to_sec(const char *val);
#define blobmsg_add_macaddr(b, f, v) \
diff --git a/src/wifi_dataelements.h b/src/wifi_dataelements.h
index 5559a479..9e672654 100644
--- a/src/wifi_dataelements.h
+++ b/src/wifi_dataelements.h
@@ -41,6 +41,7 @@ enum wifi_radio_opclass_dfs {
WIFI_RADIO_OPCLASS_CHANNEL_DFS_AVAILABLE, /**< CAC required and done; channel is available */
WIFI_RADIO_OPCLASS_CHANNEL_DFS_NOP, /**< channel unavailable; in NOP state after radar hit */
WIFI_RADIO_OPCLASS_CHANNEL_DFS_CAC, /**< Pre-ISM CAC ongoing */
+ WIFI_RADIO_OPCLASS_CHANNEL_DFS_BACKOFF, /**< Don't run CAC */
};
struct wifi_radio_opclass_channel {
@@ -50,6 +51,7 @@ struct wifi_radio_opclass_channel {
uint32_t cac_time; /**< CAC time needed */
uint32_t nop_time; /**< remaining nop time */
uint32_t cac_methods; /**< bitmap of wifi_cac_method */
+ struct timespec backoff_time; /**< backoff time when CAC request fail */
uint8_t ctrl_channels[32];
};
@@ -68,6 +70,73 @@ struct wifi_radio_opclass {
struct wifi_radio_opclass_entry opclass[64];
};
+enum wifi_acs_recalc_status {
+ WIFI_ACS_RECALC_STATUS_SKIPPED,
+ WIFI_ACS_RECALC_STATUS_BEST_SELECTED,
+ WIFI_ACS_RECALC_STATUS_BEST_REQUESTED,
+ WIFI_ACS_RECALC_STATUS_BEST_REJECTED,
+ WIFI_ACS_RECALC_STATUS_BEST_ACCEPTED,
+ WIFI_ACS_RECALC_STATUS_BEST_SET,
+ WIFI_ACS_RECALC_STATUS_BEST_ALREADY_SET,
+ WIFI_ACS_RECALC_STATUS_INPUT_DATA_INSUFFICIENT,
+ WIFI_ACS_RECALC_STATUS_BSTA_CONNECTED,
+ WIFI_ACS_RECALC_STATUS_INVALID_DATA,
+ WIFI_ACS_RECALC_STATUS_BEST_SET_TIMEOUT,
+ WIFI_ACS_RECALC_STATUS_BEST_CAC,
+ WIFI_ACS_RECALC_STATUS_BEST_CAC_RADAR,
+};
+
+struct wifi_acs_params {
+ struct timespec entry_time;
+
+ /* Input params */
+ int opclass; /* recalc for this opclass */
+ int bw; /* recalc for this bandwidth */
+
+ bool skip_dfs; /* Skip all DFS channels */
+ bool skip_dfs_not_available; /* Prevent CAC */
+
+ /* Output params */
+ int best_channel;
+ int best_opclass;
+ int best_bw;
+ int best_pref;
+ uint32_t best_cac_time;
+
+ enum wifi_acs_recalc_status status;
+ uint16_t mid;
+
+ bool recalc;
+};
+
+enum wifi_acs_cleanup_status {
+ WIFI_ACS_CLEANUP_STATUS_SKIPPED,
+ WIFI_ACS_CLEANUP_STATUS_SELECTED,
+ WIFI_ACS_CLEANUP_STATUS_REQUESTED,
+ WIFI_ACS_CLEANUP_STATUS_REJECTED,
+ WIFI_ACS_CLEANUP_STATUS_RADAR,
+ WIFI_ACS_CLEANUP_STATUS_ACCEPTED,
+ WIFI_ACS_CLEANUP_STATUS_DONE,
+ WIFI_ACS_CLEANUP_STATUS_ALL_CLEAN,
+ WIFI_ACS_CLEANUP_STATUS_NOT_SUPPORTED,
+ WIFI_ACS_CLEANUP_STATUS_NO_APS,
+ WIFI_ACS_CLEANUP_STATUS_TIMEOUT,
+};
+
+struct wifi_cac_data {
+ struct timespec entry_time;
+
+ uint8_t radio[6];
+ uint8_t opclass;
+ uint8_t channel;
+ uint8_t cac_method;
+ uint8_t cac_action;
+
+ enum wifi_acs_cleanup_status status;
+ uint16_t mid;
+ uint32_t cac_time;
+};
+
struct wifi_sta_meas_report {
struct list_head list;
@@ -665,6 +734,11 @@ struct wifi_radio_element {
struct list_head fbss_akmlist; /* fBSS AKM list */
struct list_head bbss_akmlist; /* bBSS AKM list */
+
+ struct wifi_acs_params last_acs;
+
+ bool bgcac_supported;
+ struct wifi_cac_data last_cac_data;
};
struct wifi_default_8021q {
diff --git a/src/wifi_opclass.c b/src/wifi_opclass.c
index af46639f..4c9bcb57 100644
--- a/src/wifi_opclass.c
+++ b/src/wifi_opclass.c
@@ -214,7 +214,7 @@ static const struct wifi_radio_opclass e4 = {
{ .channel = 153, .ctrl_channels = {153}},
{ .channel = 157, .ctrl_channels = {157}},
{ .channel = 161, .ctrl_channels = {161}},
- { .channel = 164, .ctrl_channels = {164}},
+ { .channel = 165, .ctrl_channels = {165}},
{ .channel = 169, .ctrl_channels = {169}},
{ .channel = 173, .ctrl_channels = {173}},
{ .channel = 173, .ctrl_channels = {173}},
@@ -616,7 +616,7 @@ void wifi_opclass_reset(struct wifi_radio_opclass *opclass)
memset(opclass->opclass, 0, sizeof(opclass->opclass));
}
-void wifi_opclass_dump(struct wifi_radio_opclass *opclass, const char *name, uint8_t *radio)
+void wifi_opclass_dump_ex(struct wifi_radio_opclass *opclass, const char *name, uint8_t *radio, bool full)
{
struct wifi_radio_opclass_entry *entry;
char radio_str[18] = {};
@@ -628,15 +628,26 @@ void wifi_opclass_dump(struct wifi_radio_opclass *opclass, const char *name, uin
cntlr_dbg(LOG_CHANNEL, ">>> %s %s opclass num: %d\n", radio_str, name ? name : "", opclass->num_opclass);
for (i = 0; i < opclass->num_opclass; i++) {
entry = &opclass->opclass[i];
- cntlr_dbg(LOG_CHANNEL, "opclass: %u\n", entry->id);
+
+ if (full)
+ cntlr_trace(LOG_CHANNEL, "opclass: %u\n", entry->id);
for (j = 0; j < entry->num_channel; j++) {
- cntlr_dbg(LOG_CHANNEL, "\tchan %u pref %u reason %u\n",
+ if (!full && !((entry->channel[j].preference & CHANNEL_PREF_MASK) >> 4))
+ continue;
+ if (!full)
+ cntlr_trace(LOG_CHANNEL, "opclass: %u\n", entry->id);
+ cntlr_trace(LOG_CHANNEL, "\tchan %u pref %u reason %u\n",
entry->channel[j].channel,
(entry->channel[j].preference & CHANNEL_PREF_MASK) >> 4,
entry->channel[j].preference & CHANNEL_PREF_REASON);
}
}
- cntlr_dbg(LOG_CHANNEL, "<<<\n");
+ cntlr_trace(LOG_CHANNEL, "<<<\n");
+}
+
+void wifi_opclass_dump(struct wifi_radio_opclass *opclass, const char *name, uint8_t *radio)
+{
+ return wifi_opclass_dump_ex(opclass, name, radio, true);
}
uint8_t wifi_opclass_get_id(struct wifi_radio_opclass *opclass, uint8_t channel, int bandwidth)
@@ -680,7 +691,7 @@ _wifi_opclass_get_higest_preference(struct wifi_radio_opclass *opclass,
int req_bandwidth,
uint8_t *opclass_id,
uint8_t *channel,
- uint8_t *bandwidth)
+ uint16_t *bandwidth)
{
struct wifi_radio_opclass_entry *entry;
struct wifi_radio_opclass_channel *chan;
@@ -726,7 +737,7 @@ _wifi_opclass_get_higest_preference(struct wifi_radio_opclass *opclass,
return best;
}
-static uint8_t wifi_get_best_ctrl_channel(struct wifi_radio_opclass *opclass, const uint8_t *channels, int channels_num)
+uint8_t wifi_get_best_ctrl_channel(struct wifi_radio_opclass *opclass, const uint8_t *channels, int channels_num)
{
struct wifi_radio_opclass_entry *entry;
struct wifi_radio_opclass_channel *chan;
@@ -767,13 +778,14 @@ int wifi_opclass_get_higest_preference(struct wifi_radio_opclass *opclass, int b
uint8_t *opclass_id, uint8_t *channel)
{
struct wifi_radio_opclass_channel *best;
- uint8_t bw;
+ uint16_t bw;
best = _wifi_opclass_get_higest_preference(opclass, bandwidth, opclass_id, channel, &bw);
if (!best)
return -1;
switch (bw) {
+ case 320:
case 160:
case 80:
*channel = wifi_get_best_ctrl_channel(opclass, best->ctrl_channels, ARRAY_SIZE(best->ctrl_channels));
@@ -836,6 +848,33 @@ bool wifi_opclass_id_same_preference(struct wifi_radio_opclass *opclass, uint8_t
return true;
}
+bool wifi_opclass_same_preference(struct wifi_radio_opclass *opclass, uint8_t *preferences)
+{
+ struct wifi_radio_opclass_entry *entry;
+ struct wifi_radio_opclass_channel *chan;
+ uint8_t pref = 0;
+ int i, j;
+
+ for (i = 0; i < opclass->num_opclass; i++) {
+ entry = &opclass->opclass[i];
+
+ for (j = 0; j < entry->num_channel; j++) {
+ chan = &entry->channel[j];
+
+ if (((chan->preference & CHANNEL_PREF_MASK) >> 4) == 0)
+ continue;
+
+ if (pref && pref != ((chan->preference & CHANNEL_PREF_MASK) >> 4))
+ return false;
+
+ pref = (chan->preference & CHANNEL_PREF_MASK) >> 4;
+ }
+ }
+
+ *preferences = pref;
+ return true;
+}
+
bool wifi_opclass_max_preference(uint8_t preference)
{
uint8_t pref;
@@ -1388,36 +1427,24 @@ bool wifi_opclass_has_channel(uint8_t id, uint8_t channel)
}
int wifi_radio_opclass_update_channel(struct wifi_radio_opclass *opclass, uint8_t band,
- uint32_t channel, uint32_t bw, uint32_t pref)
+ uint32_t channel, uint32_t bw, uint8_t pref)
{
struct wifi_radio_opclass_channel *channel_entry;
- int bws[] = {20, 40, 80, 160, 320};
+ int bws[] = {320, 160, 80, 40, 20};
int i;
- if (!bw) {
- for (i = 0; i < ARRAY_SIZE(bws); i++) {
- channel_entry = wifi_opclass_find_opclass_channel(opclass, band, channel, bws[i]);
- if (!channel_entry)
- continue;
-
- channel_entry->preference = pref;
- }
-
- return 0;
- }
-
- channel_entry = wifi_opclass_find_opclass_channel(opclass, band, channel, bw);
- if (!channel_entry)
- return -1;
-
- channel_entry->preference = pref;
+ for (i = 0; i < ARRAY_SIZE(bws); i++) {
+ if (bw && bw < bws[i])
+ continue;
- /* Setup also 20MHz channel - so agent will know ctrl channel */
- if (bw >= 80) {
- channel_entry = wifi_opclass_find_opclass_channel(opclass, band, channel, 20);
+ channel_entry = wifi_opclass_find_opclass_channel(opclass, band, channel, bws[i]);
if (!channel_entry)
- return -1;
- channel_entry->preference = pref;
+ continue;
+
+ if (pref > 1)
+ channel_entry->preference = (pref--) << 4;
+ else
+ channel_entry->preference = pref << 4;
}
return 0;
@@ -1426,11 +1453,13 @@ int wifi_radio_opclass_update_channel(struct wifi_radio_opclass *opclass, uint8_
int wifi_opclass_get_max_bw(struct wifi_radio_opclass *opclass,
uint8_t *ctrl_channel,
uint32_t *bw,
- uint8_t *id)
+ uint8_t *id,
+ uint8_t *channel)
{
struct wifi_radio_opclass_entry *entry, *max = NULL;
uint32_t max_bw = 0;
uint8_t ctrl_chan = 0;
+ uint8_t chan = 0;
int i;
for (i = 0; i < opclass->num_opclass; i++) {
@@ -1443,6 +1472,7 @@ int wifi_opclass_get_max_bw(struct wifi_radio_opclass *opclass,
if (entry->bandwidth > max_bw) {
max_bw = entry->bandwidth;
max = entry;
+ chan = entry->channel[0].channel;
}
}
@@ -1457,5 +1487,8 @@ int wifi_opclass_get_max_bw(struct wifi_radio_opclass *opclass,
if (ctrl_channel)
*ctrl_channel = ctrl_chan;
+ if (channel)
+ *channel = chan;
+
return 0;
}
diff --git a/src/wifi_opclass.h b/src/wifi_opclass.h
index 29220902..ff8216db 100644
--- a/src/wifi_opclass.h
+++ b/src/wifi_opclass.h
@@ -23,11 +23,13 @@ int wifi_opclass_add_entry(struct wifi_radio_opclass *opclass, struct wifi_radio
bool wifi_opclass_expired(struct wifi_radio_opclass *opclass, uint32_t seconds);
void wifi_opclass_reset(struct wifi_radio_opclass *opclass);
void wifi_opclass_dump(struct wifi_radio_opclass *opclass, const char *name, uint8_t *radio);
+void wifi_opclass_dump_ex(struct wifi_radio_opclass *opclass, const char *name, uint8_t *radio, bool full);
uint8_t wifi_opclass_get_id(struct wifi_radio_opclass *opclass, uint8_t channel, int bandwidth);
void wifi_opclass_set_preferences(struct wifi_radio_opclass *opclass, uint8_t preference);
int wifi_opclass_get_higest_preference(struct wifi_radio_opclass *opclass, int bandwith,
uint8_t *opclass_id, uint8_t *channel);
+uint8_t wifi_get_best_ctrl_channel(struct wifi_radio_opclass *opclass, const uint8_t *channels, int channels_num);
bool wifi_opclass_id_supported(struct wifi_radio_opclass *opclass, uint8_t id);
uint8_t wifi_opclass_num_supported(struct wifi_radio_opclass *opclass);
bool wifi_opclass_cac_required(struct wifi_radio_opclass *opclass,
@@ -54,6 +56,7 @@ uint8_t wifi_opclass_find_id_from_channel(struct wifi_radio_opclass *opclass,
int ctrl_channel,
int bandwidth);
bool wifi_opclass_id_same_preference(struct wifi_radio_opclass *opclass, uint8_t id, uint8_t *pref);
+bool wifi_opclass_same_preference(struct wifi_radio_opclass *opclass, uint8_t *preferences);
bool wifi_opclass_max_preference(uint8_t preference);
bool wifi_opclass_is_channel_dfs_available(struct wifi_radio_opclass_channel *chan);
bool wifi_opclass_is_channel_dfs_nop(struct wifi_radio_opclass_channel *chan);
@@ -67,9 +70,10 @@ struct wifi_radio_opclass_channel *wifi_opclass_get_channel(struct wifi_radio_op
struct wifi_radio_opclass_channel *wifi_opclass_get_ctrl_channel(struct wifi_radio_opclass *opclass, uint8_t id, uint8_t channel);
bool wifi_opclass_has_channel(uint8_t id, uint8_t channel);
int wifi_radio_opclass_update_channel(struct wifi_radio_opclass *opclass, uint8_t band,
- uint32_t channel, uint32_t bw, uint32_t pref);
+ uint32_t channel, uint32_t bw, uint8_t pref);
int wifi_opclass_get_max_bw(struct wifi_radio_opclass *opclass,
uint8_t *ctrl_channel,
uint32_t *bw,
- uint8_t *id);
+ uint8_t *id,
+ uint8_t *channel);
#endif /* _WIFI_RADIO_OPCLASS_H_ */
--
GitLab