ipq807x_v5.4: backport AFC support

Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
Felix Fietkau
2024-04-19 13:09:26 +02:00
committed by John Crispin
parent 3ca02ca424
commit 49560ef3d3
13 changed files with 2578 additions and 3 deletions

View File

@@ -92,6 +92,7 @@ DRIVER_MAKEOPTS= \
CONFIG_IEEE80211N=$(HOSTAPD_IEEE80211N) \
CONFIG_IEEE80211AC=$(HOSTAPD_IEEE80211AC) \
CONFIG_IEEE80211AX=$(HOSTAPD_IEEE80211AX) \
CONFIG_AFC=$(HOSTAPD_IEEE80211AX) \
CONFIG_DRIVER_WEXT=$(CONFIG_DRIVER_WEXT_SUPPORT) \
CONFIG_UCODE=y
@@ -138,6 +139,13 @@ endif
DRV_DEPENDS:=+PACKAGE_kmod-cfg80211:libnl-tiny
define Package/afcd
SECTION:=net
CATEGORY:=Network
SUBMENU:=WirelessAPD
TITLE:=AFC communication daemon
DEPENDS:=+ucode +ucode-mod-uclient +ucode-mod-uloop
endef
define Package/hostapd/Default
SECTION:=net
@@ -506,7 +514,7 @@ TARGET_CPPFLAGS := \
$(if $(CONFIG_WPA_MSG_MIN_PRIORITY),-DCONFIG_MSG_MIN_PRIORITY=$(CONFIG_WPA_MSG_MIN_PRIORITY))
TARGET_CFLAGS += -ffunction-sections -fdata-sections -flto
TARGET_LDFLAGS += -Wl,--gc-sections -flto=jobserver -fuse-linker-plugin -lubox -lubus -lucode -lblobmsg_json
TARGET_LDFLAGS += -Wl,--gc-sections -flto=jobserver -fuse-linker-plugin -lubox -lubus -lucode -lblobmsg_json -ljson-c
ifdef CONFIG_PACKAGE_kmod-cfg80211
TARGET_LDFLAGS += -lm -lnl-tiny
@@ -589,6 +597,12 @@ define Build/Compile
$(Build/Compile/$(BUILD_VARIANT))
endef
define Package/afcd/install
$(INSTALL_DIR) $(1)/usr/share/hostap $(1)/etc/init.d
$(INSTALL_BIN) ./files/afcd.init $(1)/etc/init.d/afcd
$(INSTALL_DATA) ./files/afcd.uc $(1)/usr/share/hostap/
endef
define Install/hostapd
$(INSTALL_DIR) $(1)/usr/sbin $(1)/usr/share/hostap
$(INSTALL_DATA) ./files/hostapd.uc $(1)/usr/share/hostap/
@@ -692,6 +706,7 @@ ifeq ($(BUILD_VARIANT),supplicant-full-wolfssl)
endef
endif
$(eval $(call BuildPackage,afcd))
$(eval $(call BuildPackage,hostapd))
$(eval $(call BuildPackage,hostapd-basic))
$(eval $(call BuildPackage,hostapd-basic-openssl))

View File

@@ -0,0 +1,30 @@
#!/bin/sh /etc/rc.common
START=19
USE_PROCD=1
NAME=afcd
add_afc() {
config_get_bool disabled "$1" disabled 0
[ "$disabled" -gt 0 ] && return
config_get url "$1" url
config_get cert "$1" cert
[ -n "$cert" -a -n "$url" ] || return
procd_open_instance afcd
procd_set_param command /usr/bin/ucode /usr/share/hostap/afcd.uc -u "$url" -c "$cert"
procd_set_param respawn
procd_close_instance
}
start_service() {
config_load wireless
config_foreach add_afc afc-server
}
service_triggers()
{
procd_add_reload_trigger wireless
}

View File

@@ -0,0 +1,132 @@
#!/usr/bin/env ucode
'use strict';
import { basename } from "fs";
let uclient = require("uclient");
let uloop = require("uloop");
let libubus = require("ubus");
let opts = {};
let reqs = [];
const usage_message = `Usage: ${basename(sourcepath())} <options>
Options:
-u <url>: AFC server URL (required)
-c <path>: AFC server CA certificate
`;
function usage() {
warn(usage_message);
exit(1);
}
while (substr(ARGV[0], 0, 1) == "-") {
let opt = substr(shift(ARGV), 1);
switch (opt) {
case 'u':
opts.url = shift(ARGV);
break;
case 'c':
opts.cert = shift(ARGV);
if (!opts.cert)
usage();
break;
default:
usage();
}
}
if (!opts.url)
usage();
function request_done(cb, error)
{
if (!cb.req)
return;
if (error)
delete cb.data;
cb.req.reply({ data: cb.data }, error);
delete cb.req;
delete cb.client;
}
const cb_proto = {
data_read: function(cb) {
let cur;
while (length(cur = this.read()) > 0)
cb.data += cur;
},
data_eof: function(cb) {
request_done(cb, 0);
},
error: function(cb, code) {
request_done(cb, libubus.STATUS_UNKNOWN_ERROR);
},
};
function handle_request(req)
{
let cb = proto({ data: "" }, cb_proto);
let cl = uclient.new(opts.url, null, cb);
if (!cl.ssl_init({ verify: true, ca_files: [ opts.cert ] })) {
warn(`Failed to initialize SSL\n`);
return false;
}
if (!cl.connect()) {
warn(`Failed to connect\n`);
return false;
}
let meta = {
headers: {
"Content-Type": "application/json",
},
post_data: req.args.data
};
if (!cl.request("POST", meta)) {
warn(`Failed to send request\n`);
return false;
}
cb.client = cl;
cb.req = req;
return true;
}
function add_ubus(ubus) {
return ubus.publish("afc", {
request: {
call: function(req) {
if (!req.args.data)
return libubus.STATUS_INVALID_ARGUMENT;
let ret = handle_request(req);
if (!ret)
return libubus.STATUS_UNKNOWN_ERROR;
req.defer();
},
args: {
data: "",
},
},
});
}
uloop.init();
let ubus = libubus.connect();
if (!add_ubus(ubus)) {
warn("Failed to publish ubus object\n");
exit(1);
}
uloop.run();
uloop.done();

View File

@@ -126,9 +126,30 @@ hostapd_common_add_device_config() {
config_add_boolean multiple_bssid rnr_beacon he_co_locate ema
config_add_boolean afc
config_add_string \
afc_request_version afc_request_id afc_serial_number \
afc_location_type afc_location afc_height afc_height_type
config_add_array afc_cert_ids afc_freq_range afc_op_class
config_add_int \
afc_min_power afc_major_axis afc_minor_axis afc_orientation \
afc_vertical_tolerance
hostapd_add_log_config
}
hostapd_get_list() {
local var="$1"
local field="$2"
local cur __val_list
json_get_values __val_list "$field"
for cur in $__val_list; do
append "$var" "$cur" ","
done
}
hostapd_prepare_device_config() {
local config="$1"
local driver="$2"
@@ -139,7 +160,7 @@ hostapd_prepare_device_config() {
acs_chan_bias local_pwr_constraint spectrum_mgmt_required airtime_mode cell_density \
rts_threshold beacon_rate rssi_reject_assoc_rssi rssi_ignore_probe_request maxassoc \
multiple_bssid he_co_locate rnr_beacon ema acs_exclude_dfs \
maxassoc_ignore_probe
maxassoc_ignore_probe band
hostapd_set_log_options base_cfg
@@ -252,6 +273,45 @@ hostapd_prepare_device_config() {
[ "$multiple_bssid" -gt 0 ] && append base_cfg "multiple_bssid=$multiple_bssid" "$N"
[ "$ema" -gt 0 ] && append base_cfg "ema=$ema" "$N"
[ "$acs_exclude_dfs" -gt 0 ] && append base_cfg "acs_exclude_dfs=$acs_exclude_dfs" "$N"
if [ "$band" = "6g" ]; then
json_get_vars afc he_6ghz_reg_pwr_type
else
afc=0
he_6ghz_reg_pwr_type=
fi
set_default afc 0
[ "$afc" -gt 0 ] && {
for v in afc_request_version afc_request_id afc_serial_number afc_min_power afc_height afc_height_type afc_vertical_tolerance \
afc_major_axis afc_minor_axis afc_orientation; do
json_get_var val $v
append base_cfg "$v=$val" "$N"
done
for v in afc_cert_ids afc_op_class afc_freq_range; do
val=
hostapd_get_list val $v
append base_cfg "$v=$val" "$N"
done
json_get_vars afc_location_type afc_location
case "$afc_location_type" in
ellipse)
append base_cfg "afc_location_type=0" "$N"
append base_cfg "afc_linear_polygon=$afc_location" "$N"
;;
linear_polygon)
append base_cfg "afc_location_type=1" "$N"
append base_cfg "afc_linear_polygon=$afc_location" "$N"
;;
radial_polygon)
append base_cfg "afc_location_type=2" "$N"
append base_cfg "afc_radial_polygon=$afc_location" "$N"
;;
esac
he_6ghz_reg_pwr_type=1
}
[ -n "$he_6ghz_reg_pwr_type" ] && append base_cfg "he_6ghz_reg_pwr_type=$he_6ghz_reg_pwr_type" "$N"
json_get_values opts hostapd_options
for val in $opts; do

View File

@@ -800,6 +800,12 @@ return {
iface_set_config(phy, null);
hostapd.ubus.disconnect();
},
afc_request: function(iface, data) {
let ret = ubus.call("afc", "request", { data });
if (type(ret) != "object")
return;
return ret.data;
},
bss_add: function(name, obj) {
bss_event("add", name);
},

View File

@@ -3,6 +3,15 @@
"access": {
"service": {
"methods": [ "event" ]
},
"wpa_supplicant": {
"methods": [ "phy_set_state", "phy_set_macaddr_list", "phy_status" ]
},
"hostapd": {
"methods": [ "apsta_state" ]
},
"afc": {
"methods": [ "request" ]
}
},
"publish": [ "hostapd", "hostapd.*", "wpa_supplicant", "wpa_supplicant.*" ],

View File

@@ -0,0 +1,575 @@
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Sat, 17 Feb 2024 11:24:44 +0100
Subject: [PATCH] hostapd: afcd: add AFC daemon support
Introduce Automated Frequency Coordination Daemon (AFCD) support
for UNII-5 and UNII-7 6GHz bands.
AFCD will be used by hostapd AFC client in order to forward the AFC
request to the AFC coordinator and decouple AFC connection management
from hostapd.
AFC is required for Standard Power Devices (SPDs) to determine a lists
of channels and EIRP/PSD powers that are available in the 6GHz spectrum.
AFCD is tested with AFC DUT Test Harness [0].
Add afc-reply.json as reference for replies from the AFC coordinator.
[0] https://github.com/Wi-FiTestSuite/AFC-DUT/tree/main
Tested-by: Allen Ye <allen.ye@mediatek.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
create mode 100644 afc/.gitignore
create mode 100644 afc/Makefile
create mode 100644 afc/afc-reply.json
create mode 100644 afc/afcd.c
--- /dev/null
+++ b/afc/.gitignore
@@ -0,0 +1 @@
+afcd
--- /dev/null
+++ b/afc/Makefile
@@ -0,0 +1,31 @@
+ALL=afcd
+
+include ../src/build.rules
+
+CFLAGS += -I../src/utils
+CFLAGS += -I../src
+
+OBJS=afcd.o
+OBJS += ../src/utils/common.o
+OBJS += ../src/utils/wpa_debug.o
+OBJS += ../src/utils/wpabuf.o
+
+ifndef CONFIG_OS
+ifdef CONFIG_NATIVE_WINDOWS
+CONFIG_OS=win32
+else
+CONFIG_OS=unix
+endif
+endif
+OBJS += ../src/utils/os_$(CONFIG_OS).o
+
+LIBS += -lcurl
+
+_OBJS_VAR := OBJS
+include ../src/objs.mk
+afcd: $(OBJS)
+ $(Q)$(LDO) $(LDFLAGS) -o afcd $(OBJS) $(LIBS)
+ @$(E) " LD " $@
+
+clean: common-clean
+ rm -f core *~
--- /dev/null
+++ b/afc/afc-reply.json
@@ -0,0 +1,215 @@
+{
+ "availableSpectrumInquiryResponses":[
+ {
+ "availabilityExpireTime":"2023-02-23T12:53:18Z",
+ "availableChannelInfo":[
+ {
+ "channelCfi":[
+ 1,
+ 5,
+ 9,
+ 13,
+ 17,
+ 21,
+ 25,
+ 29,
+ 33,
+ 37,
+ 41,
+ 45,
+ 49,
+ 53,
+ 57,
+ 61,
+ 65,
+ 69,
+ 73,
+ 77,
+ 81,
+ 85,
+ 89,
+ 93,
+ 117,
+ 121,
+ 125,
+ 129,
+ 133,
+ 137,
+ 141,
+ 145,
+ 149,
+ 153,
+ 157,
+ 161,
+ 165,
+ 169,
+ 173,
+ 177,
+ 181
+ ],
+ "globalOperatingClass":131,
+ "maxEirp":[
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5
+ ]
+ },
+ {
+ "channelCfi":[
+ 3,
+ 11,
+ 19,
+ 27,
+ 35,
+ 43,
+ 51,
+ 59,
+ 67,
+ 75,
+ 83,
+ 91,
+ 123,
+ 131,
+ 139,
+ 147,
+ 155,
+ 163,
+ 171,
+ 179
+ ],
+ "globalOperatingClass":132,
+ "maxEirp":[
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5
+ ]
+ },
+ {
+ "channelCfi":[
+ 7,
+ 23,
+ 39,
+ 55,
+ 71,
+ 87,
+ 135,
+ 151,
+ 167
+ ],
+ "globalOperatingClass":133,
+ "maxEirp":[
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5
+ ]
+ },
+ {
+ "channelCfi":[
+ 15,
+ 47,
+ 79,
+ 143
+ ],
+ "globalOperatingClass":134,
+ "maxEirp":[
+ 5,
+ 5,
+ 5,
+ 5
+ ]
+ },
+ {
+ "channelCfi":[
+ ],
+ "globalOperatingClass":135,
+ "maxEirp":[
+ ]
+ }
+ ],
+ "availableFrequencyInfo":[
+ {
+ "frequencyRange":{
+ "highFrequency":6425,
+ "lowFrequency":5925
+ },
+ "maxPSD":3.98970004336019
+ },
+ {
+ "frequencyRange":{
+ "highFrequency":6865,
+ "lowFrequency":6525
+ },
+ "maxPSD":3.98970004336019
+ }
+ ],
+ "requestId":"11235814",
+ "response":{
+ "responseCode":0,
+ "shortDescription":"Success"
+ },
+ "rulesetId":"US_47_CFR_PART_15_SUBPART_E"
+ }
+ ],
+ "version":"1.1"
+}
--- /dev/null
+++ b/afc/afcd.c
@@ -0,0 +1,292 @@
+/*
+ * Automated Frequency Coordination Daemon
+ * Copyright (c) 2024, Lorenzo Bianconi <lorenzo@kernel.org>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include <curl/curl.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+
+#include "utils/includes.h"
+#include "utils/common.h"
+
+#define CURL_TIMEOUT 60
+#define AFCD_SOCK "afcd.sock"
+
+struct curl_ctx {
+ char *buf;
+ size_t buf_len;
+};
+
+static volatile bool exiting;
+
+static char *path = "/var/run";
+static char *bearer_token;
+static char *url;
+static int port = 443;
+
+
+static size_t afcd_curl_cb_write(void *ptr, size_t size, size_t nmemb,
+ void *userdata)
+{
+ struct curl_ctx *ctx = userdata;
+ char *buf;
+
+ buf = os_realloc(ctx->buf, ctx->buf_len + size * nmemb + 1);
+ if (!buf)
+ return 0;
+
+ ctx->buf = buf;
+ os_memcpy(buf + ctx->buf_len, ptr, size * nmemb);
+ buf[ctx->buf_len + size * nmemb] = '\0';
+ ctx->buf_len += size * nmemb;
+
+ return size * nmemb;
+}
+
+
+static int afcd_send_request(struct curl_ctx *ctx, unsigned char *request)
+{
+ struct curl_slist *headers = NULL;
+ CURL *curl;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "Sending AFC request to %s", url);
+
+ curl_global_init(CURL_GLOBAL_ALL);
+ curl = curl_easy_init();
+ if (!curl)
+ return -ENOMEM;
+
+ headers = curl_slist_append(headers, "Accept: application/json");
+ headers = curl_slist_append(headers,
+ "Content-Type: application/json");
+ headers = curl_slist_append(headers, "charset: utf-8");
+
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+ curl_easy_setopt(curl, CURLOPT_PORT, port);
+ curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
+ afcd_curl_cb_write);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctx);
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcrp/0.1");
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, CURL_TIMEOUT);
+ curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
+ if (bearer_token)
+ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, bearer_token);
+ curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BEARER);
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request);
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1L);
+
+ ret = curl_easy_perform(curl);
+ if (ret != CURLE_OK)
+ wpa_printf(MSG_ERROR, "curl_easy_perform failed: %s",
+ curl_easy_strerror(ret));
+
+ curl_easy_cleanup(curl);
+ curl_global_cleanup();
+
+ return ret == CURLE_OK ? 0 : -EINVAL;
+}
+
+
+static void handle_term(int sig)
+{
+ wpa_printf(MSG_ERROR, "Received signal %d", sig);
+ exiting = true;
+}
+
+
+static void usage(void)
+{
+ wpa_printf(MSG_ERROR,
+ "%s:\n"
+ "afcd -u<url> [-p<port>][-t<token>][-D<unix-sock dir>][-P<PID file>][-dB]",
+ __func__);
+}
+
+
+#define BUFSIZE 8192
+static int afcd_server_run(void)
+{
+ size_t len = os_strlen(path) + 1 + os_strlen(AFCD_SOCK);
+ struct sockaddr_un addr = {
+ .sun_family = AF_UNIX,
+#ifdef __FreeBSD__
+ .sun_len = sizeof(addr),
+#endif /* __FreeBSD__ */
+ };
+ int sockfd, ret = 0;
+ char *fname = NULL;
+ unsigned char *buf;
+ fd_set read_set;
+
+ if (len >= sizeof(addr.sun_path))
+ return -EINVAL;
+
+ if (mkdir(path, S_IRWXU | S_IRWXG) < 0 && errno != EEXIST)
+ return -EINVAL;
+
+ buf = os_malloc(BUFSIZE);
+ if (!buf)
+ return -ENOMEM;
+
+ fname = os_malloc(len + 1);
+ if (!fname) {
+ ret = -ENOMEM;
+ goto free_buf;
+ }
+
+ os_snprintf(fname, len + 1, "%s/%s", path, AFCD_SOCK);
+ fname[len] = '\0';
+ os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
+
+ sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sockfd < 0) {
+ wpa_printf(MSG_ERROR, "Failed creating socket");
+ ret = -errno;
+ goto unlink;
+ }
+
+ if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ wpa_printf(MSG_ERROR, "Failed to bind socket");
+ ret = -errno;
+ goto close;
+ }
+
+ if (listen(sockfd, 10) < 0) {
+ wpa_printf(MSG_ERROR, "Failed to listen on socket");
+ ret = -errno;
+ goto close;
+ }
+
+ FD_ZERO(&read_set);
+ while (!exiting) {
+ socklen_t addr_len = sizeof(addr);
+ struct sockaddr_in6 c_addr;
+ struct timeval timeout = {
+ .tv_sec = 1,
+ };
+ struct curl_ctx ctx = {};
+ int fd;
+
+ FD_SET(sockfd, &read_set);
+ if (select(sockfd + 1, &read_set, NULL, NULL, &timeout) < 0) {
+ if (errno != EINTR) {
+ wpa_printf(MSG_ERROR,
+ "Select failed on socket");
+ ret = -errno;
+ break;
+ }
+ continue;
+ }
+
+ if (!FD_ISSET(sockfd, &read_set))
+ continue;
+
+ fd = accept(sockfd, (struct sockaddr *)&c_addr,
+ &addr_len);
+ if (fd < 0) {
+ if (errno != EINTR) {
+ wpa_printf(MSG_ERROR,
+ "Failed accepting connections");
+ ret = -errno;
+ break;
+ }
+ continue;
+ }
+
+ os_memset(buf, 0, BUFSIZE);
+ if (recv(fd, buf, BUFSIZE - 1, 0) <= 0) {
+ close(fd);
+ continue;
+ }
+
+ wpa_printf(MSG_DEBUG, "Received request: %s", buf);
+ if (!afcd_send_request(&ctx, buf)) {
+ wpa_printf(MSG_DEBUG, "Received reply: %s", ctx.buf);
+ send(fd, ctx.buf, ctx.buf_len, MSG_NOSIGNAL);
+ free(ctx.buf);
+ }
+ close(fd);
+ }
+close:
+ close(sockfd);
+unlink:
+ unlink(fname);
+ os_free(fname);
+free_buf:
+ os_free(buf);
+
+ return ret;
+}
+
+
+int main(int argc, char **argv)
+{
+ bool daemonize = false;
+ char *pid_file = NULL;
+
+ if (os_program_init())
+ return -1;
+
+ for (;;) {
+ int c = getopt(argc, argv, "u:p:t:D:P:hdB");
+
+ if (c < 0)
+ break;
+
+ switch (c) {
+ case 'h':
+ usage();
+ return 0;
+ case 'B':
+ daemonize = true;
+ break;
+ case 'D':
+ path = optarg;
+ break;
+ case 'P':
+ os_free(pid_file);
+ pid_file = os_rel2abs_path(optarg);
+ break;
+ case 'u':
+ url = optarg;
+ break;
+ case 'p':
+ port = atoi(optarg);
+ break;
+ case 'd':
+ if (wpa_debug_level > 0)
+ wpa_debug_level--;
+ break;
+ case 't':
+ bearer_token = optarg;
+ break;
+ default:
+ usage();
+ return -EINVAL;
+ }
+ }
+
+ if (!url) {
+ usage();
+ return -EINVAL;
+ }
+
+ if (daemonize && os_daemonize(pid_file)) {
+ wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
+ return -EINVAL;
+ }
+
+ signal(SIGTERM, handle_term);
+ signal(SIGINT, handle_term);
+
+ return afcd_server_run();
+}

View File

@@ -0,0 +1,44 @@
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Sat, 16 Mar 2024 12:35:24 +0100
Subject: [PATCH] hostapd: export hostapd_is_usable_chans utility routine
This is a preliminary patch to introduce AFC support.
Tested-by: Allen Ye <allen.ye@mediatek.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -953,7 +953,7 @@ static int hostapd_is_usable_edmg(struct
}
-static int hostapd_is_usable_chans(struct hostapd_iface *iface)
+int hostapd_is_usable_chans(struct hostapd_iface *iface)
{
int secondary_freq;
struct hostapd_channel_data *pri_chan;
--- a/src/ap/hw_features.h
+++ b/src/ap/hw_features.h
@@ -28,6 +28,8 @@ int hostapd_prepare_rates(struct hostapd
void hostapd_stop_setup_timers(struct hostapd_iface *iface);
int hostapd_hw_skip_mode(struct hostapd_iface *iface,
struct hostapd_hw_modes *mode);
+int hostapd_is_usable_chans(struct hostapd_iface *iface);
+
#else /* NEED_AP_MLME */
static inline void
hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
@@ -91,6 +93,11 @@ static inline int hostapd_check_he_6ghz_
return 0;
}
+static inline int hostapd_is_usable_chans(struct hostapd_iface *iface)
+{
+ return 1;
+}
+
#endif /* NEED_AP_MLME */
#endif /* HW_FEATURES_H */

View File

@@ -0,0 +1,127 @@
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Wed, 7 Feb 2024 00:08:18 +0100
Subject: [PATCH] hostapd: update TPE IE according to AFC
Update Transmit Power Envelope (TPE) IE according to the reply from AFC
coordinator on UNII-5 or UNII-7 6GHz bands.
Tested-by: Allen Ye <allen.ye@mediatek.com>
---
--- a/src/ap/afc.c
+++ b/src/ap/afc.c
@@ -977,3 +977,40 @@ void hostap_afc_disable_channels(struct
chan->flag |= HOSTAPD_CHAN_DISABLED;
}
}
+
+
+int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
+ int *power)
+{
+ int i;
+
+ if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type))
+ return -EINVAL;
+
+ if (!iface->afc.data_valid)
+ return -EINVAL;
+
+ if (psd) {
+ for (i = 0; i < iface->afc.num_freq_range; i++) {
+ struct afc_freq_range_elem *f;
+
+ f = &iface->afc.freq_range[i];
+ if (iface->freq >= f->low_freq &&
+ iface->freq <= f->high_freq) {
+ *power = 2 * f->max_psd;
+ return 0;
+ }
+ }
+ } else {
+ for (i = 0; i < iface->afc.num_chan_info; i++) {
+ struct afc_chan_info_elem *c;
+
+ c = &iface->afc.chan_info_list[i];
+ if (c->chan == iface->conf->channel) {
+ *power = 2 * c->power;
+ return 0;
+ }
+ }
+ }
+ return -EINVAL;
+}
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -666,10 +666,19 @@ struct hostapd_iface {
/* hostapd.c */
#ifdef CONFIG_AFC
+int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
+ int *power);
int hostapd_afc_handle_request(struct hostapd_iface *iface);
void hostapd_afc_stop(struct hostapd_iface *iface);
void hostap_afc_disable_channels(struct hostapd_iface *iface);
#else
+static inline int
+hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
+ int *power)
+{
+ return -EINVAL;
+}
+
static inline int hostapd_afc_handle_request(struct hostapd_iface *iface)
{
return 1;
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -6970,6 +6970,8 @@ u8 * hostapd_eid_txpower_envelope(struct
* Unit interpretation = Regulatory client EIRP PSD
*/
if (is_6ghz_op_class(iconf->op_class)) {
+ int err, max_eirp_psd;
+
ap_type = hostapd_get_he_6ghz_reg_pwr_type(iconf);
tx_pwr_count = DEFAULT_MAX_TX_POWER_COUNT_6G;
@@ -6978,14 +6980,20 @@ u8 * hostapd_eid_txpower_envelope(struct
/* Indoor access point must include additional
* TPE for subordinate device
*/
+ err = hostap_afc_get_chan_max_eirp_power(iface, true,
+ &max_eirp_psd);
if (ap_type == AP_TYPE_6GHZ_INDOOR_AP) {
tx_pwr_cat = REG_SUBORDINATE_CLIENT;
psd = mode->psd_values[NL80211_REG_SUBORDINATE_CLIENT_LPI + ap_type];
+ if (err < 0) {
#ifdef REG_DOM_SUPPORT_TX_POWER
- tx_pwr = psd * 2;
+ tx_pwr = psd * 2;
#else
- tx_pwr = (hostapd_get_6g_tx_power(hapd, ap_type, tx_pwr_cat) * 2);
+ tx_pwr = (hostapd_get_6g_tx_power(hapd, ap_type, tx_pwr_cat) * 2);
#endif /* REG_DOM_SUPPORT_TX_POWER */
+ } else {
+ tx_pwr = max_eirp_psd;
+ }
eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn,
tx_pwr_cat, tx_pwr);
}
@@ -6993,11 +7001,15 @@ u8 * hostapd_eid_txpower_envelope(struct
/* Default Tx Power envelope for Global Operating class */
tx_pwr_cat = REG_DEFAULT_CLIENT;
psd = mode->psd_values[NL80211_REG_REGULAR_CLIENT_LPI + ap_type];
+ if (err < 0) {
#ifdef REG_DOM_SUPPORT_TX_POWER
- tx_pwr = psd * 2;
+ tx_pwr = psd * 2;
#else
- tx_pwr = (hostapd_get_6g_tx_power(hapd, ap_type, tx_pwr_cat) * 2);
+ tx_pwr = (hostapd_get_6g_tx_power(hapd, ap_type, tx_pwr_cat) * 2);
#endif /* REG_DOM_SUPPORT_TX_POWER */
+ } else {
+ tx_pwr = max_eirp_psd;
+ }
eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn, tx_pwr_cat, tx_pwr);
return eid;

View File

@@ -0,0 +1,23 @@
--- a/src/ap/afc.c
+++ b/src/ap/afc.c
@@ -737,6 +737,20 @@ static int hostapd_afc_send_receive(stru
return 0;
}
+#ifdef UCODE_SUPPORT
+ request_obj = hostapd_afc_build_request(iface);
+ if (!request_obj)
+ return -ENOMEM;
+
+ request = json_object_to_json_string(request_obj);
+ ret = hostapd_ucode_afc_request(iface, request, buf, sizeof(buf));
+ json_object_put(request_obj);
+ if (ret < 0)
+ return ret;
+
+ return hostapd_afc_parse_reply(iface, buf);
+#endif
+
if (!iconf->afc.socket) {
wpa_printf(MSG_ERROR, "Missing AFC socket string");
return -EINVAL;

View File

@@ -51,7 +51,7 @@ hostapd_ucode_update_bss_list(struct hostapd_iface *iface, uc_value_t *if_bss, u
int i;
list = ucv_array_new(vm);
for (i = 0; i < iface->num_bss; i++) {
for (i = 0; iface->bss && i < iface->num_bss; i++) {
struct hostapd_data *hapd = iface->bss[i];
uc_value_t *val = hostapd_ucode_bss_get_uval(hapd);
@@ -764,6 +764,34 @@ void hostapd_ucode_free_iface(struct hostapd_iface *iface)
wpa_ucode_registry_remove(iface_registry, iface->ucode.idx);
}
int hostapd_ucode_afc_request(struct hostapd_iface *iface, const char *request,
char *buf, size_t len)
{
uc_value_t *val;
size_t ret_len;
int ret = -1;
if (wpa_ucode_call_prepare("afc_request"))
return -1;
uc_value_push(ucv_get(ucv_string_new(iface->phy)));
uc_value_push(ucv_get(ucv_string_new(request)));
val = wpa_ucode_call(2);
if (ucv_type(val) != UC_STRING)
goto out;
ret_len = ucv_string_length(val);
if (ret_len >= len)
goto out;
memcpy(buf, ucv_string_get(val), ret_len + 1);
ret = (int)ret_len;
out:
ucv_put(val);
return ret;
}
void hostapd_ucode_add_bss(struct hostapd_data *hapd)
{
uc_value_t *val;

View File

@@ -26,6 +26,8 @@ void hostapd_ucode_free_iface(struct hostapd_iface *iface);
void hostapd_ucode_add_bss(struct hostapd_data *hapd);
void hostapd_ucode_free_bss(struct hostapd_data *hapd);
void hostapd_ucode_reload_bss(struct hostapd_data *hapd);
int hostapd_ucode_afc_request(struct hostapd_iface *iface, const char *request,
char *buf, size_t len);
#else