diff --git a/src/acs.c b/src/acs.c index 6c014f083ec8dfc56171c2db1cf9f28cb4cc1c37..ceeee8da63198c395bcd4487fed54f861de9d6d7 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 8dc7a024f5226a52fe850afae22e0b30f07b886b..5c03f80c09b669274aef63cfde1b664a73afe8e4 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 9d290cd4bcacf475b2954ede6de9eab2cd8abd65..5a1c1eb91c33883786d9aedfa6c499139595b2ea 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 58139fdc2176b41903c19196d4f05bbfafaa2a7b..6489436d7117c37b3f6cc52a98dd064d969c9c5a 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 083cb0b84f58d2970afc8e406db5df4b8621e2a4..87c3a95063dcd4f4a40e9dd43e01cdaa012b1e92 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 8ea5b7ea60ca480975f83dfa6742fe5dcb03a12b..560a51829398e9eea9e2c7511dc2887128d580c8 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 97596f805d72aaa4857fdccbd12b989fc109a23a..1dd6a420417a79fb5986654d23cce1361b903763 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 297e7fdba6b2f07c578c04c4a40dd6302b796341..18533eaf2980ec9e10f86ebd6dce0c9e1b5b0c3c 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 fed5360d6ad58ce5979ef2acd5ad75231e62365e..526f9006f877465f66ac98bfd983818451bd4964 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 84f4dd2589d7c736f0feb3538899e9c4c8bf5cd0..069122b5493e5cf93e4ee782afdbc50596d64a69 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 b26ba55cc82d9b3365a6f1ba0667ddce3a639b5f..24cbbac3fe6c8700c688068a76c8118d7bbc5e6e 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 1f0ec6c4ee4384059cdfdfe0ede09ef4b9507583..7ce1f12ffb781a1611785e195f760801d46951cd 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 bb64f3dd65a78a275111814ceb8a4bdecb19eb8d..6e1d659e8c38f25192365cf941605e4f2f09b419 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 d26eeb7750ac2a63356445d422f8759b1bd62f36..dd5378f83da515536c9622f8b6f8b1dbefe3c5c2 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 e66a91b048114783dc77edb78a90b65eb1d5233c..236446d00ccb41b61bbb403592f7e15579e900b1 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 45004728108c08f1f41699a853c0159cab51efcc..6b11a0a1a90e36d89c4378298d78b0735b05d85e 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 cb45fb50b099b365eb6ce90f6c949941e169ace2..bd728acf1a5495ced9884ca7384126897fb19fb1 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 93fc6522be296d51566a7ea3d0aa0f8ba294a7b8..13e9e0e1df8be8b9d510eddca0bc5b1251645116 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 afb812acade4a34e6a520d222c6ed43f51259abe..c26b9dff8d61bdd986a0bdbe975514e7f7290e57 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 5559a4793d197ec7d6d725f28f55a8b0a745ea24..9e6726547d4e053fbfa5b2e1c688e58601199c51 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 af46639f6867d44f635e4d0c6b570ad676f32c9c..4c9bcb5705a837efd60168c46e1668d8b9875188 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 292209026a6264f73eee4081673590ca420fc656..ff8216dbd4aeb1aa48bb31476f2d1f5ed8556b4c 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_ */