enable wifi version steering (#28)
Reviewed-on: #28
This commit was merged in pull request #28.
This commit is contained in:
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#define OPENUF_CONFIG_SCHEMA 6
|
#define OPENUF_CONFIG_SCHEMA 7
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool adopted;
|
bool adopted;
|
||||||
|
|||||||
+136
-17
@@ -737,6 +737,84 @@ void wlan_clear(void)
|
|||||||
uci_free_context(ctx);
|
uci_free_context(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum wifi_standard {
|
||||||
|
WIFI_STANDARD_UNKNOWN = 0,
|
||||||
|
WIFI_STANDARD_4 = 4,
|
||||||
|
WIFI_STANDARD_5 = 5,
|
||||||
|
WIFI_STANDARD_6 = 6,
|
||||||
|
WIFI_STANDARD_7 = 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Return the newest 802.11 generation advertised by the local PHY. */
|
||||||
|
static enum wifi_standard radio_max_standard(const char *device_name)
|
||||||
|
{
|
||||||
|
int phy_index = -1;
|
||||||
|
char command[96];
|
||||||
|
char line[256];
|
||||||
|
enum wifi_standard standard = WIFI_STANDARD_UNKNOWN;
|
||||||
|
|
||||||
|
if (!device_name || sscanf(device_name, "radio%d", &phy_index) != 1 ||
|
||||||
|
phy_index < 0)
|
||||||
|
return WIFI_STANDARD_UNKNOWN;
|
||||||
|
|
||||||
|
snprintf(command, sizeof(command), "iw phy phy%d info 2>/dev/null",
|
||||||
|
phy_index);
|
||||||
|
FILE *pipe = popen(command, "r");
|
||||||
|
if (!pipe)
|
||||||
|
return WIFI_STANDARD_UNKNOWN;
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), pipe)) {
|
||||||
|
if (strstr(line, "EHT Iftypes"))
|
||||||
|
standard = WIFI_STANDARD_7;
|
||||||
|
else if (standard < WIFI_STANDARD_6 && strstr(line, "HE Iftypes"))
|
||||||
|
standard = WIFI_STANDARD_6;
|
||||||
|
else if (standard < WIFI_STANDARD_5 &&
|
||||||
|
strstr(line, "VHT Capabilities"))
|
||||||
|
standard = WIFI_STANDARD_5;
|
||||||
|
else if (standard < WIFI_STANDARD_4 && strstr(line, "Capabilities:"))
|
||||||
|
standard = WIFI_STANDARD_4;
|
||||||
|
}
|
||||||
|
pclose(pipe);
|
||||||
|
return standard;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keep the controller-selected width but use the newest PHY generation. */
|
||||||
|
static void select_htmode(const char *device_name, const char *radio_band,
|
||||||
|
const char *requested, int force_wifi4,
|
||||||
|
char *result, size_t result_size)
|
||||||
|
{
|
||||||
|
enum wifi_standard standard = radio_max_standard(device_name);
|
||||||
|
int width = 20;
|
||||||
|
const char *number = requested;
|
||||||
|
|
||||||
|
while (number && *number && (*number < '0' || *number > '9'))
|
||||||
|
number++;
|
||||||
|
if (number && *number)
|
||||||
|
width = atoi(number);
|
||||||
|
|
||||||
|
if (force_wifi4) {
|
||||||
|
/* 802.11n cannot use 80 MHz or wider channels. */
|
||||||
|
standard = WIFI_STANDARD_4;
|
||||||
|
if (width > 40)
|
||||||
|
width = 40;
|
||||||
|
} else if (standard == WIFI_STANDARD_5 && radio_band &&
|
||||||
|
(!strcmp(radio_band, "ng") || !strcmp(radio_band, "2g"))) {
|
||||||
|
/* OpenWrt does not use VHT modes on the 2.4 GHz band. */
|
||||||
|
standard = WIFI_STANDARD_4;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *prefix = standard == WIFI_STANDARD_7 ? "EHT" :
|
||||||
|
standard == WIFI_STANDARD_6 ? "HE" :
|
||||||
|
standard == WIFI_STANDARD_5 ? "VHT" :
|
||||||
|
standard == WIFI_STANDARD_4 ? "HT" : NULL;
|
||||||
|
if (!prefix) {
|
||||||
|
snprintf(result, result_size, "%s", requested ? requested : "HT20");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(result, result_size, "%s%d", prefix, width);
|
||||||
|
}
|
||||||
|
|
||||||
/* ═══════════════════════════════════════════════════════════════════
|
/* ═══════════════════════════════════════════════════════════════════
|
||||||
wlan_apply_radio — apply radio config (channel, HT, power)
|
wlan_apply_radio — apply radio config (channel, HT, power)
|
||||||
═══════════════════════════════════════════════════════════════════
|
═══════════════════════════════════════════════════════════════════
|
||||||
@@ -748,7 +826,8 @@ void wlan_clear(void)
|
|||||||
min_rssi → not mapped to UCI (requires an external daemon)
|
min_rssi → not mapped to UCI (requires an external daemon)
|
||||||
*/
|
*/
|
||||||
void wlan_apply_radio(struct json_object *radio_json,
|
void wlan_apply_radio(struct json_object *radio_json,
|
||||||
const char *device_name)
|
const char *device_name,
|
||||||
|
int force_wifi4)
|
||||||
{
|
{
|
||||||
struct uci_context *ctx = uci_alloc_context();
|
struct uci_context *ctx = uci_alloc_context();
|
||||||
if (!ctx) return;
|
if (!ctx) return;
|
||||||
@@ -762,11 +841,12 @@ void wlan_apply_radio(struct json_object *radio_json,
|
|||||||
char path[256];
|
char path[256];
|
||||||
|
|
||||||
/* Map UniFi band names to OpenWrt mac80211 band names. */
|
/* Map UniFi band names to OpenWrt mac80211 band names. */
|
||||||
|
const char *radio_band = NULL;
|
||||||
if (json_object_object_get_ex(radio_json, "radio", &v)) {
|
if (json_object_object_get_ex(radio_json, "radio", &v)) {
|
||||||
const char *radio = json_object_get_string(v);
|
radio_band = json_object_get_string(v);
|
||||||
const char *band = !strcmp(radio, "ng") ? "2g" :
|
const char *band = !strcmp(radio_band, "ng") ? "2g" :
|
||||||
!strcmp(radio, "na") ? "5g" :
|
!strcmp(radio_band, "na") ? "5g" :
|
||||||
!strcmp(radio, "6g") ? "6g" : NULL;
|
!strcmp(radio_band, "6g") ? "6g" : NULL;
|
||||||
if (band) {
|
if (band) {
|
||||||
snprintf(path, sizeof(path), "wireless.%s.band=%s",
|
snprintf(path, sizeof(path), "wireless.%s.band=%s",
|
||||||
device_name, band);
|
device_name, band);
|
||||||
@@ -776,17 +856,20 @@ void wlan_apply_radio(struct json_object *radio_json,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define RP(key, uci_opt) \
|
if (json_object_object_get_ex(radio_json, "ht", &v)) {
|
||||||
if (json_object_object_get_ex(radio_json, key, &v)) { \
|
const char *requested = json_object_get_string(v);
|
||||||
snprintf(path, sizeof(path), "wireless.%s.%s=%s", \
|
char htmode[16];
|
||||||
device_name, uci_opt, json_object_get_string(v)); \
|
select_htmode(device_name, radio_band, requested, force_wifi4,
|
||||||
struct uci_ptr ptr; \
|
htmode, sizeof(htmode));
|
||||||
if (uci_lookup_ptr(ctx, &ptr, path, true) == UCI_OK) \
|
snprintf(path, sizeof(path), "wireless.%s.htmode=%s",
|
||||||
uci_set(ctx, &ptr); \
|
device_name, htmode);
|
||||||
|
struct uci_ptr ptr;
|
||||||
|
if (uci_lookup_ptr(ctx, &ptr, path, true) == UCI_OK)
|
||||||
|
uci_set(ctx, &ptr);
|
||||||
|
printf("[openuf] Radio %s standard: %s%s\n", device_name, htmode,
|
||||||
|
force_wifi4 ? " (Force WiFi 4)" : " (newest supported)");
|
||||||
}
|
}
|
||||||
|
|
||||||
RP("ht", "htmode");
|
|
||||||
|
|
||||||
/* Channel: 0 = auto in UniFi */
|
/* Channel: 0 = auto in UniFi */
|
||||||
if (json_object_object_get_ex(radio_json, "channel", &v)) {
|
if (json_object_object_get_ex(radio_json, "channel", &v)) {
|
||||||
int ch = json_object_get_int(v);
|
int ch = json_object_get_int(v);
|
||||||
@@ -815,8 +898,6 @@ void wlan_apply_radio(struct json_object *radio_json,
|
|||||||
if (uci_lookup_ptr(ctx, &ptr, path, true) == UCI_OK)
|
if (uci_lookup_ptr(ctx, &ptr, path, true) == UCI_OK)
|
||||||
uci_set(ctx, &ptr);
|
uci_set(ctx, &ptr);
|
||||||
|
|
||||||
#undef RP
|
|
||||||
|
|
||||||
uci_commit(ctx, &pkg, false);
|
uci_commit(ctx, &pkg, false);
|
||||||
uci_unload(ctx, pkg);
|
uci_unload(ctx, pkg);
|
||||||
uci_free_context(ctx);
|
uci_free_context(ctx);
|
||||||
@@ -1110,6 +1191,37 @@ static int apply_vap(struct uci_context *ctx,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* True when a UniFi WLAN on this band explicitly requests WiFi 4 mode. */
|
||||||
|
static int force_wifi4_for_band(struct json_object *vaps, const char *band)
|
||||||
|
{
|
||||||
|
if (!vaps || !json_object_is_type(vaps, json_type_array))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const char *force_keys[] = {
|
||||||
|
"iot", "force_wifi4", "force_wifi4_mode"
|
||||||
|
};
|
||||||
|
int count = json_object_array_length(vaps);
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
struct json_object *vap = json_object_array_get_idx(vaps, i);
|
||||||
|
struct json_object *value;
|
||||||
|
if (!vap || !json_boolean_any(vap, force_keys,
|
||||||
|
sizeof(force_keys) /
|
||||||
|
sizeof(force_keys[0])))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const char *vap_band = NULL;
|
||||||
|
if (json_object_object_get_ex(vap, "radio", &value))
|
||||||
|
vap_band = json_object_get_string(value);
|
||||||
|
if (!vap_band || !vap_band[0] || !strcmp(vap_band, "both") ||
|
||||||
|
!strcmp(vap_band, "all") || !strcmp(vap_band, band) ||
|
||||||
|
(!strcmp(vap_band, "2g") && !strcmp(band, "ng")) ||
|
||||||
|
(!strcmp(vap_band, "5g") && !strcmp(band, "na")) ||
|
||||||
|
(!strcmp(vap_band, "6GHz") && !strcmp(band, "6g")))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* ═══════════════════════════════════════════════════════════════════
|
/* ═══════════════════════════════════════════════════════════════════
|
||||||
wlan_apply_config — apply the controller's full configuration
|
wlan_apply_config — apply the controller's full configuration
|
||||||
═══════════════════════════════════════════════════════════════════
|
═══════════════════════════════════════════════════════════════════
|
||||||
@@ -1167,7 +1279,8 @@ int wlan_apply_config(struct json_object *config_json,
|
|||||||
radio_band);
|
radio_band);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
wlan_apply_radio(r, device_name);
|
wlan_apply_radio(r, device_name,
|
||||||
|
force_wifi4_for_band(vt_arr, radio_band));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1476,6 +1589,12 @@ int wlan_apply_system_cfg(const char *system_cfg,
|
|||||||
band = "na";
|
band = "na";
|
||||||
json_object_object_add(vap, "radio", json_object_new_string(band));
|
json_object_object_add(vap, "radio", json_object_new_string(band));
|
||||||
|
|
||||||
|
/* UniFi's legacy Force WiFi 4 mode is carried as wireless.N.iot. */
|
||||||
|
snprintf(key, sizeof(key), "wireless.%d.iot", i);
|
||||||
|
if (system_cfg_get(system_cfg, key, value, sizeof(value)))
|
||||||
|
json_object_object_add(vap, "iot", json_object_new_boolean(
|
||||||
|
feature_text_enabled(value)));
|
||||||
|
|
||||||
char passphrase[256] = {0};
|
char passphrase[256] = {0};
|
||||||
snprintf(key, sizeof(key), "aaa.%d.wpa.psk", i);
|
snprintf(key, sizeof(key), "aaa.%d.wpa.psk", i);
|
||||||
int has_passphrase = system_cfg_get(system_cfg, key, passphrase,
|
int has_passphrase = system_cfg_get(system_cfg, key, passphrase,
|
||||||
|
|||||||
+4
-2
@@ -15,9 +15,11 @@ void wlan_clear(void);
|
|||||||
|
|
||||||
/* Apply radio-level settings from a UniFi radio_table entry.
|
/* Apply radio-level settings from a UniFi radio_table entry.
|
||||||
* radio_json : JSON object with fields: channel, ht, tx_power
|
* radio_json : JSON object with fields: channel, ht, tx_power
|
||||||
* device_name: OpenWrt radio device ("radio0", "radio1") */
|
* device_name: OpenWrt radio device ("radio0", "radio1")
|
||||||
|
* force_wifi4: cap the radio at 802.11n when requested by UniFi */
|
||||||
void wlan_apply_radio(struct json_object *radio_json,
|
void wlan_apply_radio(struct json_object *radio_json,
|
||||||
const char *device_name);
|
const char *device_name,
|
||||||
|
int force_wifi4);
|
||||||
|
|
||||||
/* Resolve the model's logical UniFi band against the bands advertised by
|
/* Resolve the model's logical UniFi band against the bands advertised by
|
||||||
* the local PHYs. Falls back to the model mapping when discovery fails. */
|
* the local PHYs. Falls back to the model mapping when discovery fails. */
|
||||||
|
|||||||
Reference in New Issue
Block a user