xref: /freebsd/contrib/wpa/src/ap/afc.c (revision 71e72c9e91c4b8007a4292e09669e8b549c29e97)
1*71e72c9eSCy Schubert /*
2*71e72c9eSCy Schubert  * Automated Frequency Coordination
3*71e72c9eSCy Schubert  * Copyright (c) 2024, Lorenzo Bianconi <lorenzo@kernel.org>
4*71e72c9eSCy Schubert  *
5*71e72c9eSCy Schubert  * This software may be distributed under the terms of the BSD license.
6*71e72c9eSCy Schubert  * See README for more details.
7*71e72c9eSCy Schubert  */
8*71e72c9eSCy Schubert 
9*71e72c9eSCy Schubert #include "utils/includes.h"
10*71e72c9eSCy Schubert #include <sys/un.h>
11*71e72c9eSCy Schubert #include <time.h>
12*71e72c9eSCy Schubert 
13*71e72c9eSCy Schubert #include "utils/common.h"
14*71e72c9eSCy Schubert #include "utils/eloop.h"
15*71e72c9eSCy Schubert #include "utils/json.h"
16*71e72c9eSCy Schubert #include "common/hw_features_common.h"
17*71e72c9eSCy Schubert #include "common/wpa_ctrl.h"
18*71e72c9eSCy Schubert #include "hostapd.h"
19*71e72c9eSCy Schubert #include "acs.h"
20*71e72c9eSCy Schubert #include "hw_features.h"
21*71e72c9eSCy Schubert 
22*71e72c9eSCy Schubert #define HOSTAPD_AFC_RETRY_TIMEOUT	180
23*71e72c9eSCy Schubert #define HOSTAPD_AFC_TIMEOUT		86400 /* 24h */
24*71e72c9eSCy Schubert #define HOSTAPD_AFC_BUFSIZE		8192
25*71e72c9eSCy Schubert 
26*71e72c9eSCy Schubert static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx);
27*71e72c9eSCy Schubert 
28*71e72c9eSCy Schubert 
hostapd_afc_reset_channels(struct hostapd_iface * iface)29*71e72c9eSCy Schubert static int hostapd_afc_reset_channels(struct hostapd_iface *iface)
30*71e72c9eSCy Schubert {
31*71e72c9eSCy Schubert 	int ret;
32*71e72c9eSCy Schubert 
33*71e72c9eSCy Schubert 	ret = hostapd_get_hw_features(iface);
34*71e72c9eSCy Schubert 	if (ret) {
35*71e72c9eSCy Schubert 		wpa_printf(MSG_ERROR, "AFC: Failed to reset channel flags");
36*71e72c9eSCy Schubert 		return ret;
37*71e72c9eSCy Schubert 	}
38*71e72c9eSCy Schubert 
39*71e72c9eSCy Schubert 	ret = hostapd_set_current_hw_info(iface, iface->freq);
40*71e72c9eSCy Schubert 	if (ret)
41*71e72c9eSCy Schubert 		wpa_printf(MSG_ERROR,
42*71e72c9eSCy Schubert 			   "AFC: Failed to set current hardware info");
43*71e72c9eSCy Schubert 
44*71e72c9eSCy Schubert 	return ret;
45*71e72c9eSCy Schubert }
46*71e72c9eSCy Schubert 
47*71e72c9eSCy Schubert 
48*71e72c9eSCy Schubert static struct wpabuf *
hostapd_afc_build_location_request(struct hostapd_iface * iface)49*71e72c9eSCy Schubert hostapd_afc_build_location_request(struct hostapd_iface *iface)
50*71e72c9eSCy Schubert {
51*71e72c9eSCy Schubert 	struct wpabuf *location_obj, *ellipse_obj = NULL;
52*71e72c9eSCy Schubert 	struct hostapd_config *iconf = iface->conf;
53*71e72c9eSCy Schubert 	bool is_ap_indoor = he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type);
54*71e72c9eSCy Schubert 	size_t location_len = 1024, ellipse_len = 128;
55*71e72c9eSCy Schubert 	unsigned int i;
56*71e72c9eSCy Schubert 
57*71e72c9eSCy Schubert 	location_obj = wpabuf_alloc(location_len);
58*71e72c9eSCy Schubert 	if (!location_obj)
59*71e72c9eSCy Schubert 		return NULL;
60*71e72c9eSCy Schubert 
61*71e72c9eSCy Schubert 	json_start_object(location_obj, "location");
62*71e72c9eSCy Schubert 	if (iconf->afc.location.type != AFC_LINEAR_POLYGON) {
63*71e72c9eSCy Schubert 		struct afc_linear_polygon *lp =
64*71e72c9eSCy Schubert 			iconf->afc.location.linear_polygon_data;
65*71e72c9eSCy Schubert 
66*71e72c9eSCy Schubert 		if (!lp)
67*71e72c9eSCy Schubert 			goto error;
68*71e72c9eSCy Schubert 
69*71e72c9eSCy Schubert 		ellipse_obj = wpabuf_alloc(ellipse_len);
70*71e72c9eSCy Schubert 		if (!ellipse_obj)
71*71e72c9eSCy Schubert 			goto error;
72*71e72c9eSCy Schubert 
73*71e72c9eSCy Schubert 		json_start_object(ellipse_obj, "center");
74*71e72c9eSCy Schubert 
75*71e72c9eSCy Schubert 		json_add_double(ellipse_obj, "longitude", lp->longitude);
76*71e72c9eSCy Schubert 		json_value_sep(ellipse_obj);
77*71e72c9eSCy Schubert 
78*71e72c9eSCy Schubert 		json_add_double(ellipse_obj, "latitude", lp->latitude);
79*71e72c9eSCy Schubert 		json_end_object(ellipse_obj);
80*71e72c9eSCy Schubert 		json_value_sep(ellipse_obj);
81*71e72c9eSCy Schubert 	}
82*71e72c9eSCy Schubert 
83*71e72c9eSCy Schubert 	switch (iconf->afc.location.type) {
84*71e72c9eSCy Schubert 	case AFC_LINEAR_POLYGON:
85*71e72c9eSCy Schubert 		json_start_object(location_obj, "linearPolygon");
86*71e72c9eSCy Schubert 		json_start_array(location_obj, "outerBoundary");
87*71e72c9eSCy Schubert 		for (i = 0; i < iconf->afc.location.n_linear_polygon_data; i++)
88*71e72c9eSCy Schubert 		{
89*71e72c9eSCy Schubert 			struct afc_linear_polygon *lp =
90*71e72c9eSCy Schubert 				&iconf->afc.location.linear_polygon_data[i];
91*71e72c9eSCy Schubert 
92*71e72c9eSCy Schubert 			json_start_object(location_obj, NULL);
93*71e72c9eSCy Schubert 
94*71e72c9eSCy Schubert 			json_add_double(location_obj, "longitude",
95*71e72c9eSCy Schubert 					lp->longitude);
96*71e72c9eSCy Schubert 			json_value_sep(location_obj);
97*71e72c9eSCy Schubert 
98*71e72c9eSCy Schubert 			json_add_double(location_obj, "latitude",
99*71e72c9eSCy Schubert 					lp->latitude);
100*71e72c9eSCy Schubert 
101*71e72c9eSCy Schubert 			json_end_object(location_obj);
102*71e72c9eSCy Schubert 			if (i < iconf->afc.location.n_linear_polygon_data - 1)
103*71e72c9eSCy Schubert 				json_value_sep(location_obj);
104*71e72c9eSCy Schubert 		}
105*71e72c9eSCy Schubert 		json_end_array(location_obj);
106*71e72c9eSCy Schubert 
107*71e72c9eSCy Schubert 		/* linearPolygon */
108*71e72c9eSCy Schubert 		json_end_object(location_obj);
109*71e72c9eSCy Schubert 		json_value_sep(location_obj);
110*71e72c9eSCy Schubert 		break;
111*71e72c9eSCy Schubert 	case AFC_RADIAL_POLYGON:
112*71e72c9eSCy Schubert 		json_start_object(location_obj, "radialPolygon");
113*71e72c9eSCy Schubert 		wpabuf_put_buf(location_obj, ellipse_obj);
114*71e72c9eSCy Schubert 
115*71e72c9eSCy Schubert 		json_start_array(location_obj, "outerBoundary");
116*71e72c9eSCy Schubert 		for (i = 0; i < iconf->afc.location.n_radial_polygon_data; i++)
117*71e72c9eSCy Schubert 		{
118*71e72c9eSCy Schubert 			struct afc_radial_polygon *rp =
119*71e72c9eSCy Schubert 				&iconf->afc.location.radial_polygon_data[i];
120*71e72c9eSCy Schubert 
121*71e72c9eSCy Schubert 			json_start_object(location_obj, NULL);
122*71e72c9eSCy Schubert 			json_add_double(location_obj, "length", rp->length);
123*71e72c9eSCy Schubert 			json_value_sep(location_obj);
124*71e72c9eSCy Schubert 
125*71e72c9eSCy Schubert 			json_add_double(location_obj, "angle", rp->angle);
126*71e72c9eSCy Schubert 			json_end_object(location_obj);
127*71e72c9eSCy Schubert 			if (i < iconf->afc.location.n_radial_polygon_data - 1)
128*71e72c9eSCy Schubert 				json_value_sep(location_obj);
129*71e72c9eSCy Schubert 		}
130*71e72c9eSCy Schubert 		json_end_array(location_obj);
131*71e72c9eSCy Schubert 
132*71e72c9eSCy Schubert 		/* radialPolygon */
133*71e72c9eSCy Schubert 		json_end_object(location_obj);
134*71e72c9eSCy Schubert 		json_value_sep(location_obj);
135*71e72c9eSCy Schubert 		break;
136*71e72c9eSCy Schubert 	case AFC_ELLIPSE:
137*71e72c9eSCy Schubert 	default:
138*71e72c9eSCy Schubert 		json_start_object(location_obj, "ellipse");
139*71e72c9eSCy Schubert 		wpabuf_put_buf(location_obj, ellipse_obj);
140*71e72c9eSCy Schubert 
141*71e72c9eSCy Schubert 		json_add_int(location_obj, "majorAxis",
142*71e72c9eSCy Schubert 			     iconf->afc.location.major_axis);
143*71e72c9eSCy Schubert 		json_value_sep(location_obj);
144*71e72c9eSCy Schubert 		json_add_int(location_obj, "minorAxis",
145*71e72c9eSCy Schubert 			     iconf->afc.location.minor_axis);
146*71e72c9eSCy Schubert 		json_value_sep(location_obj);
147*71e72c9eSCy Schubert 		json_add_int(location_obj, "orientation",
148*71e72c9eSCy Schubert 			     iconf->afc.location.orientation);
149*71e72c9eSCy Schubert 		/* ellipse */
150*71e72c9eSCy Schubert 		json_end_object(location_obj);
151*71e72c9eSCy Schubert 		json_value_sep(location_obj);
152*71e72c9eSCy Schubert 		break;
153*71e72c9eSCy Schubert 	}
154*71e72c9eSCy Schubert 
155*71e72c9eSCy Schubert 	json_start_object(location_obj, "elevation");
156*71e72c9eSCy Schubert 
157*71e72c9eSCy Schubert 	json_add_double(location_obj, "height", iconf->afc.location.height);
158*71e72c9eSCy Schubert 	json_value_sep(location_obj);
159*71e72c9eSCy Schubert 	if (iconf->afc.location.height_type) {
160*71e72c9eSCy Schubert 		json_add_string(location_obj, "heightType",
161*71e72c9eSCy Schubert 				iconf->afc.location.height_type);
162*71e72c9eSCy Schubert 		json_value_sep(location_obj);
163*71e72c9eSCy Schubert 	}
164*71e72c9eSCy Schubert 	json_add_int(location_obj, "verticalUncertainty",
165*71e72c9eSCy Schubert 		     iconf->afc.location.vertical_tolerance);
166*71e72c9eSCy Schubert 
167*71e72c9eSCy Schubert 	/* elevation */
168*71e72c9eSCy Schubert 	json_end_object(location_obj);
169*71e72c9eSCy Schubert 	json_value_sep(location_obj);
170*71e72c9eSCy Schubert 
171*71e72c9eSCy Schubert 	json_add_int(location_obj, "indoorDeployment", is_ap_indoor);
172*71e72c9eSCy Schubert 
173*71e72c9eSCy Schubert 	/* location */
174*71e72c9eSCy Schubert 	json_end_object(location_obj);
175*71e72c9eSCy Schubert 	json_value_sep(location_obj);
176*71e72c9eSCy Schubert 
177*71e72c9eSCy Schubert 	wpabuf_free(ellipse_obj);
178*71e72c9eSCy Schubert 	return location_obj;
179*71e72c9eSCy Schubert 
180*71e72c9eSCy Schubert error:
181*71e72c9eSCy Schubert 	wpabuf_free(location_obj);
182*71e72c9eSCy Schubert 	wpabuf_free(ellipse_obj);
183*71e72c9eSCy Schubert 
184*71e72c9eSCy Schubert 	return NULL;
185*71e72c9eSCy Schubert }
186*71e72c9eSCy Schubert 
187*71e72c9eSCy Schubert 
hostapd_afc_get_opclass_chan_list(u8 op_class)188*71e72c9eSCy Schubert static struct wpabuf * hostapd_afc_get_opclass_chan_list(u8 op_class)
189*71e72c9eSCy Schubert {
190*71e72c9eSCy Schubert 	struct wpabuf *chan_list_obj;
191*71e72c9eSCy Schubert 	const struct oper_class_map *oper_class;
192*71e72c9eSCy Schubert 	int chan_offset, chan;
193*71e72c9eSCy Schubert 	size_t ch_len = 512;
194*71e72c9eSCy Schubert 
195*71e72c9eSCy Schubert 	oper_class = get_oper_class(NULL, op_class);
196*71e72c9eSCy Schubert 	if (!oper_class)
197*71e72c9eSCy Schubert 		return NULL;
198*71e72c9eSCy Schubert 
199*71e72c9eSCy Schubert 	chan_list_obj = wpabuf_alloc(ch_len);
200*71e72c9eSCy Schubert 	if (!chan_list_obj)
201*71e72c9eSCy Schubert 		return NULL;
202*71e72c9eSCy Schubert 	json_start_array(chan_list_obj, "channelCfi");
203*71e72c9eSCy Schubert 
204*71e72c9eSCy Schubert 	switch (op_class) {
205*71e72c9eSCy Schubert 	case 132: /* 40 MHz */
206*71e72c9eSCy Schubert 		chan_offset = 2;
207*71e72c9eSCy Schubert 		break;
208*71e72c9eSCy Schubert 	case 133: /* 80 MHz */
209*71e72c9eSCy Schubert 		chan_offset = 6;
210*71e72c9eSCy Schubert 		break;
211*71e72c9eSCy Schubert 	case 134: /* 160 MHz */
212*71e72c9eSCy Schubert 		chan_offset = 14;
213*71e72c9eSCy Schubert 		break;
214*71e72c9eSCy Schubert 	case 137: /* 320 MHz */
215*71e72c9eSCy Schubert 		/*
216*71e72c9eSCy Schubert 		 * global_op_class already use the central channels for
217*71e72c9eSCy Schubert 		 * 320 MHz, so fallthrough and use 0 for chan_offset.
218*71e72c9eSCy Schubert 		 */
219*71e72c9eSCy Schubert 	default:
220*71e72c9eSCy Schubert 		chan_offset = 0;
221*71e72c9eSCy Schubert 		break;
222*71e72c9eSCy Schubert 	}
223*71e72c9eSCy Schubert 
224*71e72c9eSCy Schubert 	for (chan = oper_class->min_chan; chan <= oper_class->max_chan;
225*71e72c9eSCy Schubert 	     chan += oper_class->inc) {
226*71e72c9eSCy Schubert 		char ch_str[32];
227*71e72c9eSCy Schubert 
228*71e72c9eSCy Schubert 		if (chan + chan_offset > oper_class->max_chan)
229*71e72c9eSCy Schubert 			break;
230*71e72c9eSCy Schubert 
231*71e72c9eSCy Schubert 		os_snprintf(ch_str, sizeof(ch_str), "%s%d",
232*71e72c9eSCy Schubert 			    chan != oper_class->min_chan ? ", " : "",
233*71e72c9eSCy Schubert 			    chan + chan_offset);
234*71e72c9eSCy Schubert 		wpabuf_put_str(chan_list_obj, ch_str);
235*71e72c9eSCy Schubert 	}
236*71e72c9eSCy Schubert 	json_end_array(chan_list_obj);
237*71e72c9eSCy Schubert 
238*71e72c9eSCy Schubert 	return chan_list_obj;
239*71e72c9eSCy Schubert }
240*71e72c9eSCy Schubert 
241*71e72c9eSCy Schubert 
242*71e72c9eSCy Schubert static struct wpabuf *
hostapd_afc_build_req_chan_list(struct hostapd_iface * iface)243*71e72c9eSCy Schubert hostapd_afc_build_req_chan_list(struct hostapd_iface *iface)
244*71e72c9eSCy Schubert {
245*71e72c9eSCy Schubert 	struct wpabuf *op_class_list_obj;
246*71e72c9eSCy Schubert 	struct hostapd_config *iconf = iface->conf;
247*71e72c9eSCy Schubert 	unsigned int i, num;
248*71e72c9eSCy Schubert 
249*71e72c9eSCy Schubert 	op_class_list_obj = wpabuf_alloc(HOSTAPD_AFC_BUFSIZE);
250*71e72c9eSCy Schubert 	if (!op_class_list_obj)
251*71e72c9eSCy Schubert 		return NULL;
252*71e72c9eSCy Schubert 
253*71e72c9eSCy Schubert 	json_start_array(op_class_list_obj, "inquiredChannels");
254*71e72c9eSCy Schubert 	num = int_array_len(iconf->afc.op_class);
255*71e72c9eSCy Schubert 	for (i = 0; i < num; i++) {
256*71e72c9eSCy Schubert 		struct wpabuf *chan_list_obj = NULL;
257*71e72c9eSCy Schubert 		u8 op_class = iconf->afc.op_class[i];
258*71e72c9eSCy Schubert 
259*71e72c9eSCy Schubert 		if (!is_6ghz_op_class(op_class))
260*71e72c9eSCy Schubert 			continue;
261*71e72c9eSCy Schubert 
262*71e72c9eSCy Schubert 		json_start_object(op_class_list_obj, NULL);
263*71e72c9eSCy Schubert 
264*71e72c9eSCy Schubert 		json_add_int(op_class_list_obj, "globalOperatingClass",
265*71e72c9eSCy Schubert 			     op_class);
266*71e72c9eSCy Schubert 		json_value_sep(op_class_list_obj);
267*71e72c9eSCy Schubert 
268*71e72c9eSCy Schubert 		chan_list_obj = hostapd_afc_get_opclass_chan_list(op_class);
269*71e72c9eSCy Schubert 		if (!chan_list_obj)
270*71e72c9eSCy Schubert 			goto error;
271*71e72c9eSCy Schubert 
272*71e72c9eSCy Schubert 		wpabuf_put_buf(op_class_list_obj, chan_list_obj);
273*71e72c9eSCy Schubert 		json_end_object(op_class_list_obj);
274*71e72c9eSCy Schubert 
275*71e72c9eSCy Schubert 		if (i < num - 1)
276*71e72c9eSCy Schubert 			json_value_sep(op_class_list_obj);
277*71e72c9eSCy Schubert 
278*71e72c9eSCy Schubert 		wpabuf_free(chan_list_obj);
279*71e72c9eSCy Schubert 		chan_list_obj = NULL;
280*71e72c9eSCy Schubert 	}
281*71e72c9eSCy Schubert 	json_end_array(op_class_list_obj);
282*71e72c9eSCy Schubert 
283*71e72c9eSCy Schubert 	return op_class_list_obj;
284*71e72c9eSCy Schubert 
285*71e72c9eSCy Schubert error:
286*71e72c9eSCy Schubert 	wpabuf_free(op_class_list_obj);
287*71e72c9eSCy Schubert 	return NULL;
288*71e72c9eSCy Schubert }
289*71e72c9eSCy Schubert 
290*71e72c9eSCy Schubert 
291*71e72c9eSCy Schubert static struct wpabuf *
hostapd_afc_build_request(struct hostapd_iface * iface)292*71e72c9eSCy Schubert hostapd_afc_build_request(struct hostapd_iface *iface)
293*71e72c9eSCy Schubert {
294*71e72c9eSCy Schubert 	struct wpabuf *req_obj, *location_obj, *op_class_list_obj = NULL;
295*71e72c9eSCy Schubert 	struct hostapd_config *iconf = iface->conf;
296*71e72c9eSCy Schubert 	unsigned int i;
297*71e72c9eSCy Schubert 	char request_id_str[16];
298*71e72c9eSCy Schubert 
299*71e72c9eSCy Schubert 	req_obj = wpabuf_alloc(HOSTAPD_AFC_BUFSIZE);
300*71e72c9eSCy Schubert 	if (!req_obj)
301*71e72c9eSCy Schubert 		return NULL;
302*71e72c9eSCy Schubert 
303*71e72c9eSCy Schubert 	json_start_object(req_obj, NULL);
304*71e72c9eSCy Schubert 
305*71e72c9eSCy Schubert 	if (iconf->afc.request.version) {
306*71e72c9eSCy Schubert 		json_add_string(req_obj, "version", iconf->afc.request.version);
307*71e72c9eSCy Schubert 		json_value_sep(req_obj);
308*71e72c9eSCy Schubert 	}
309*71e72c9eSCy Schubert 
310*71e72c9eSCy Schubert 	json_start_array(req_obj, "availableSpectrumInquiryRequests");
311*71e72c9eSCy Schubert 	json_start_object(req_obj, NULL);
312*71e72c9eSCy Schubert 
313*71e72c9eSCy Schubert 	iface->afc.request_id++;
314*71e72c9eSCy Schubert 	os_snprintf(request_id_str, sizeof(request_id_str), "%u",
315*71e72c9eSCy Schubert 		    iface->afc.request_id);
316*71e72c9eSCy Schubert 	json_add_string(req_obj, "requestId", request_id_str);
317*71e72c9eSCy Schubert 	json_value_sep(req_obj);
318*71e72c9eSCy Schubert 
319*71e72c9eSCy Schubert 	json_start_object(req_obj, "deviceDescriptor");
320*71e72c9eSCy Schubert 	if (iconf->afc.request.sn) {
321*71e72c9eSCy Schubert 		json_add_string(req_obj, "serialNumber", iconf->afc.request.sn);
322*71e72c9eSCy Schubert 		json_value_sep(req_obj);
323*71e72c9eSCy Schubert 	}
324*71e72c9eSCy Schubert 
325*71e72c9eSCy Schubert 	json_start_array(req_obj, "certificationId");
326*71e72c9eSCy Schubert 
327*71e72c9eSCy Schubert 	for (i = 0; i < iconf->afc.n_cert_ids; i++) {
328*71e72c9eSCy Schubert 		json_start_object(req_obj, NULL);
329*71e72c9eSCy Schubert 		json_add_string(req_obj, "rulesetId",
330*71e72c9eSCy Schubert 				iconf->afc.cert_ids[i].ruleset);
331*71e72c9eSCy Schubert 		json_value_sep(req_obj);
332*71e72c9eSCy Schubert 
333*71e72c9eSCy Schubert 		json_add_string(req_obj, "id", iconf->afc.cert_ids[i].id);
334*71e72c9eSCy Schubert 		json_end_object(req_obj);
335*71e72c9eSCy Schubert 
336*71e72c9eSCy Schubert 		if (i < iconf->afc.n_cert_ids - 1)
337*71e72c9eSCy Schubert 			json_value_sep(req_obj);
338*71e72c9eSCy Schubert 	}
339*71e72c9eSCy Schubert 	/* certificationId */
340*71e72c9eSCy Schubert 	json_end_array(req_obj);
341*71e72c9eSCy Schubert 
342*71e72c9eSCy Schubert 	/* deviceDescriptor */
343*71e72c9eSCy Schubert 	json_end_object(req_obj);
344*71e72c9eSCy Schubert 	json_value_sep(req_obj);
345*71e72c9eSCy Schubert 
346*71e72c9eSCy Schubert 	location_obj = hostapd_afc_build_location_request(iface);
347*71e72c9eSCy Schubert 	if (!location_obj)
348*71e72c9eSCy Schubert 		goto error;
349*71e72c9eSCy Schubert 
350*71e72c9eSCy Schubert 	wpabuf_put_buf(req_obj, location_obj);
351*71e72c9eSCy Schubert 
352*71e72c9eSCy Schubert 	if (iconf->afc.freqs.num) {
353*71e72c9eSCy Schubert 		json_start_array(req_obj, "inquiredFrequencyRange");
354*71e72c9eSCy Schubert 		for (i = 0; i < iconf->afc.freqs.num; i++) {
355*71e72c9eSCy Schubert 			struct wpa_freq_range *fr = &iconf->afc.freqs.range[i];
356*71e72c9eSCy Schubert 
357*71e72c9eSCy Schubert 			json_start_object(req_obj, NULL);
358*71e72c9eSCy Schubert 
359*71e72c9eSCy Schubert 			json_add_int(req_obj, "lowFrequency", fr->min);
360*71e72c9eSCy Schubert 			json_value_sep(req_obj);
361*71e72c9eSCy Schubert 			json_add_int(req_obj, "highFrequency", fr->max);
362*71e72c9eSCy Schubert 
363*71e72c9eSCy Schubert 			json_end_object(req_obj);
364*71e72c9eSCy Schubert 			if (i < iconf->afc.freqs.num - 1)
365*71e72c9eSCy Schubert 				json_value_sep(req_obj);
366*71e72c9eSCy Schubert 		}
367*71e72c9eSCy Schubert 		json_end_array(req_obj);
368*71e72c9eSCy Schubert 		json_value_sep(req_obj);
369*71e72c9eSCy Schubert 	}
370*71e72c9eSCy Schubert 
371*71e72c9eSCy Schubert 	op_class_list_obj = hostapd_afc_build_req_chan_list(iface);
372*71e72c9eSCy Schubert 	if (!op_class_list_obj)
373*71e72c9eSCy Schubert 		goto error;
374*71e72c9eSCy Schubert 
375*71e72c9eSCy Schubert 	wpabuf_put_buf(req_obj, op_class_list_obj);
376*71e72c9eSCy Schubert 
377*71e72c9eSCy Schubert 	if (iconf->afc.min_power) {
378*71e72c9eSCy Schubert 		json_value_sep(req_obj);
379*71e72c9eSCy Schubert 		json_add_int(req_obj, "minDesiredPower", iconf->afc.min_power);
380*71e72c9eSCy Schubert 	}
381*71e72c9eSCy Schubert 
382*71e72c9eSCy Schubert 	/* availableSpectrumInquiryRequests */
383*71e72c9eSCy Schubert 	json_end_object(req_obj);
384*71e72c9eSCy Schubert 	json_end_array(req_obj);
385*71e72c9eSCy Schubert 
386*71e72c9eSCy Schubert 	json_end_object(req_obj);
387*71e72c9eSCy Schubert 	wpa_hexdump_ascii_buf(MSG_MSGDUMP, "AFC: Pending request", req_obj);
388*71e72c9eSCy Schubert 
389*71e72c9eSCy Schubert 	wpabuf_free(location_obj);
390*71e72c9eSCy Schubert 	wpabuf_free(op_class_list_obj);
391*71e72c9eSCy Schubert 
392*71e72c9eSCy Schubert 	return req_obj;
393*71e72c9eSCy Schubert 
394*71e72c9eSCy Schubert error:
395*71e72c9eSCy Schubert 	wpabuf_free(req_obj);
396*71e72c9eSCy Schubert 	wpabuf_free(location_obj);
397*71e72c9eSCy Schubert 	wpabuf_free(op_class_list_obj);
398*71e72c9eSCy Schubert 
399*71e72c9eSCy Schubert 	return NULL;
400*71e72c9eSCy Schubert }
401*71e72c9eSCy Schubert 
402*71e72c9eSCy Schubert 
403*71e72c9eSCy Schubert static int
hostad_afc_parse_available_freq_info(struct hostapd_iface * iface,struct json_token * freq_info)404*71e72c9eSCy Schubert hostad_afc_parse_available_freq_info(struct hostapd_iface *iface,
405*71e72c9eSCy Schubert 				     struct json_token *freq_info)
406*71e72c9eSCy Schubert {
407*71e72c9eSCy Schubert 	struct afc_freq_range_elem *f = NULL, *tmp;
408*71e72c9eSCy Schubert 	struct json_token *freq_obj;
409*71e72c9eSCy Schubert 	int count = 0;
410*71e72c9eSCy Schubert 
411*71e72c9eSCy Schubert 	for (freq_obj = freq_info->child; freq_obj;
412*71e72c9eSCy Schubert 	     freq_obj = freq_obj->sibling) {
413*71e72c9eSCy Schubert 		struct json_token *freq_range_obj, *token;
414*71e72c9eSCy Schubert 		int low_freq, high_freq;
415*71e72c9eSCy Schubert 		double max_psd;
416*71e72c9eSCy Schubert 
417*71e72c9eSCy Schubert 		freq_range_obj = json_get_member(freq_obj, "frequencyRange");
418*71e72c9eSCy Schubert 		if (!freq_range_obj || freq_range_obj->type != JSON_OBJECT)
419*71e72c9eSCy Schubert 			continue;
420*71e72c9eSCy Schubert 
421*71e72c9eSCy Schubert 		token = json_get_member(freq_range_obj, "lowFrequency");
422*71e72c9eSCy Schubert 		if (!token || token->type != JSON_NUMBER)
423*71e72c9eSCy Schubert 			continue;
424*71e72c9eSCy Schubert 		low_freq = token->number;
425*71e72c9eSCy Schubert 
426*71e72c9eSCy Schubert 		token = json_get_member(freq_range_obj, "highFrequency");
427*71e72c9eSCy Schubert 		if (!token || token->type != JSON_NUMBER)
428*71e72c9eSCy Schubert 			continue;
429*71e72c9eSCy Schubert 		high_freq = token->number;
430*71e72c9eSCy Schubert 
431*71e72c9eSCy Schubert 		token = json_get_member(freq_obj, "maxPsd");
432*71e72c9eSCy Schubert 		if (!token)
433*71e72c9eSCy Schubert 			token = json_get_member(freq_obj, "maxPSD");
434*71e72c9eSCy Schubert 		if (!token || (token->type != JSON_DOUBLE &&
435*71e72c9eSCy Schubert 			       token->type != JSON_NUMBER))
436*71e72c9eSCy Schubert 			continue;
437*71e72c9eSCy Schubert 		max_psd = token->type == JSON_DOUBLE ?
438*71e72c9eSCy Schubert 			token->dnumber : (double) token->number;
439*71e72c9eSCy Schubert 
440*71e72c9eSCy Schubert 		tmp = os_realloc_array(f, count + 1, sizeof(*f));
441*71e72c9eSCy Schubert 		if (!tmp) {
442*71e72c9eSCy Schubert 			os_free(f);
443*71e72c9eSCy Schubert 			return -ENOMEM;
444*71e72c9eSCy Schubert 		}
445*71e72c9eSCy Schubert 		f = tmp;
446*71e72c9eSCy Schubert 
447*71e72c9eSCy Schubert 		f[count].low_freq = low_freq;
448*71e72c9eSCy Schubert 		f[count].high_freq = high_freq;
449*71e72c9eSCy Schubert 		f[count++].max_psd = max_psd;
450*71e72c9eSCy Schubert 	}
451*71e72c9eSCy Schubert 	os_free(iface->afc.freq_range);
452*71e72c9eSCy Schubert 	iface->afc.freq_range = f;
453*71e72c9eSCy Schubert 	iface->afc.num_freq_range = count;
454*71e72c9eSCy Schubert 
455*71e72c9eSCy Schubert 	return 0;
456*71e72c9eSCy Schubert }
457*71e72c9eSCy Schubert 
458*71e72c9eSCy Schubert 
hostad_afc_update_chan_info(struct afc_chan_info_elem ** chan_list,int * chan_list_size,u8 op_class,int center_chan,double power)459*71e72c9eSCy Schubert static int hostad_afc_update_chan_info(struct afc_chan_info_elem **chan_list,
460*71e72c9eSCy Schubert 				       int *chan_list_size, u8 op_class,
461*71e72c9eSCy Schubert 				       int center_chan, double power)
462*71e72c9eSCy Schubert {
463*71e72c9eSCy Schubert 	int op_class_pwr_index, num_low_subchan, channel;
464*71e72c9eSCy Schubert 	int count = *chan_list_size;
465*71e72c9eSCy Schubert 	struct afc_chan_info_elem *c = *chan_list, *tmp;
466*71e72c9eSCy Schubert 
467*71e72c9eSCy Schubert 	switch (op_class) {
468*71e72c9eSCy Schubert 	case 132: /* 40 MHz */
469*71e72c9eSCy Schubert 		op_class_pwr_index = 1;
470*71e72c9eSCy Schubert 		num_low_subchan = 2;
471*71e72c9eSCy Schubert 		break;
472*71e72c9eSCy Schubert 	case 133: /* 80 MHz */
473*71e72c9eSCy Schubert 		op_class_pwr_index = 2;
474*71e72c9eSCy Schubert 		num_low_subchan = 6;
475*71e72c9eSCy Schubert 		break;
476*71e72c9eSCy Schubert 	case 134: /* 160 MHz */
477*71e72c9eSCy Schubert 		op_class_pwr_index = 3;
478*71e72c9eSCy Schubert 		num_low_subchan = 14;
479*71e72c9eSCy Schubert 		break;
480*71e72c9eSCy Schubert 	case 137: /* 320 MHz */
481*71e72c9eSCy Schubert 		op_class_pwr_index = 4;
482*71e72c9eSCy Schubert 		num_low_subchan = 30;
483*71e72c9eSCy Schubert 		break;
484*71e72c9eSCy Schubert 	default:
485*71e72c9eSCy Schubert 		op_class_pwr_index = 0;
486*71e72c9eSCy Schubert 		num_low_subchan = 0;
487*71e72c9eSCy Schubert 		break;
488*71e72c9eSCy Schubert 	}
489*71e72c9eSCy Schubert 
490*71e72c9eSCy Schubert 	for (channel = center_chan - num_low_subchan;
491*71e72c9eSCy Schubert 	     channel <= center_chan + num_low_subchan; channel += 4) {
492*71e72c9eSCy Schubert 		int i;
493*71e72c9eSCy Schubert 
494*71e72c9eSCy Schubert 		for (i = 0; i < count; i++) {
495*71e72c9eSCy Schubert 			if (c[i].chan == channel)
496*71e72c9eSCy Schubert 				break;
497*71e72c9eSCy Schubert 		}
498*71e72c9eSCy Schubert 
499*71e72c9eSCy Schubert 		if (i == count) {
500*71e72c9eSCy Schubert 			tmp = os_realloc_array(c, count + 1, sizeof(*c));
501*71e72c9eSCy Schubert 			if (!tmp) {
502*71e72c9eSCy Schubert 				os_free(c);
503*71e72c9eSCy Schubert 				*chan_list = NULL;
504*71e72c9eSCy Schubert 				*chan_list_size = 0;
505*71e72c9eSCy Schubert 				return -ENOMEM;
506*71e72c9eSCy Schubert 			}
507*71e72c9eSCy Schubert 			c = tmp;
508*71e72c9eSCy Schubert 
509*71e72c9eSCy Schubert 			c[count].chan = channel;
510*71e72c9eSCy Schubert 			os_memset(c[count].power, 0, sizeof(c[count].power));
511*71e72c9eSCy Schubert 			count++;
512*71e72c9eSCy Schubert 		}
513*71e72c9eSCy Schubert 		c[i].power[op_class_pwr_index] = power;
514*71e72c9eSCy Schubert 	}
515*71e72c9eSCy Schubert 
516*71e72c9eSCy Schubert 	*chan_list_size = count;
517*71e72c9eSCy Schubert 	*chan_list = c;
518*71e72c9eSCy Schubert 
519*71e72c9eSCy Schubert 	return 0;
520*71e72c9eSCy Schubert }
521*71e72c9eSCy Schubert 
522*71e72c9eSCy Schubert 
523*71e72c9eSCy Schubert static int
hostad_afc_parse_available_chan_info(struct hostapd_iface * iface,struct json_token * chan_info)524*71e72c9eSCy Schubert hostad_afc_parse_available_chan_info(struct hostapd_iface *iface,
525*71e72c9eSCy Schubert 				     struct json_token *chan_info)
526*71e72c9eSCy Schubert {
527*71e72c9eSCy Schubert 	struct afc_chan_info_elem *c = NULL;
528*71e72c9eSCy Schubert 	struct json_token *chan_obj;
529*71e72c9eSCy Schubert 	int count = 0;
530*71e72c9eSCy Schubert 
531*71e72c9eSCy Schubert 	for (chan_obj = chan_info->child; chan_obj;
532*71e72c9eSCy Schubert 	     chan_obj = chan_obj->sibling) {
533*71e72c9eSCy Schubert 		struct json_token *chan_cfi, *max_eirp;
534*71e72c9eSCy Schubert 		struct json_token *token, *chan_arr, *power_arr;
535*71e72c9eSCy Schubert 		int op_class;
536*71e72c9eSCy Schubert 
537*71e72c9eSCy Schubert 		token = json_get_member(chan_obj, "globalOperatingClass");
538*71e72c9eSCy Schubert 		if (!token || token->type != JSON_NUMBER)
539*71e72c9eSCy Schubert 			continue;
540*71e72c9eSCy Schubert 		op_class = token->number;
541*71e72c9eSCy Schubert 
542*71e72c9eSCy Schubert 		chan_cfi = json_get_member(chan_obj, "channelCfi");
543*71e72c9eSCy Schubert 		if (!chan_cfi || chan_cfi->type != JSON_ARRAY)
544*71e72c9eSCy Schubert 			continue;
545*71e72c9eSCy Schubert 
546*71e72c9eSCy Schubert 		max_eirp = json_get_member(chan_obj, "maxEirp");
547*71e72c9eSCy Schubert 		if (!max_eirp || max_eirp->type != JSON_ARRAY)
548*71e72c9eSCy Schubert 			continue;
549*71e72c9eSCy Schubert 
550*71e72c9eSCy Schubert 		chan_arr = chan_cfi->child;
551*71e72c9eSCy Schubert 		power_arr = max_eirp->child;
552*71e72c9eSCy Schubert 		while (chan_arr && power_arr) {
553*71e72c9eSCy Schubert 			int channel, ret;
554*71e72c9eSCy Schubert 			double power;
555*71e72c9eSCy Schubert 
556*71e72c9eSCy Schubert 			if (chan_arr->type != JSON_NUMBER ||
557*71e72c9eSCy Schubert 			    (power_arr->type != JSON_DOUBLE &&
558*71e72c9eSCy Schubert 			     power_arr->type != JSON_NUMBER)) {
559*71e72c9eSCy Schubert 				wpa_printf(MSG_DEBUG,
560*71e72c9eSCy Schubert 					   "AFC: Failed to parse array at opclass %d",
561*71e72c9eSCy Schubert 					   op_class);
562*71e72c9eSCy Schubert 				break;
563*71e72c9eSCy Schubert 			}
564*71e72c9eSCy Schubert 
565*71e72c9eSCy Schubert 			channel = chan_arr->number;
566*71e72c9eSCy Schubert 			power = power_arr->type == JSON_DOUBLE ?
567*71e72c9eSCy Schubert 				power_arr->dnumber : (double) power_arr->number;
568*71e72c9eSCy Schubert 
569*71e72c9eSCy Schubert 			ret = hostad_afc_update_chan_info(&c, &count, op_class,
570*71e72c9eSCy Schubert 							  channel, power);
571*71e72c9eSCy Schubert 			if (ret) {
572*71e72c9eSCy Schubert 				iface->afc.chan_info_list = NULL;
573*71e72c9eSCy Schubert 				iface->afc.num_chan_info = 0;
574*71e72c9eSCy Schubert 				return ret;
575*71e72c9eSCy Schubert 			}
576*71e72c9eSCy Schubert 			chan_arr = chan_arr->sibling;
577*71e72c9eSCy Schubert 			power_arr = power_arr->sibling;
578*71e72c9eSCy Schubert 		}
579*71e72c9eSCy Schubert 	}
580*71e72c9eSCy Schubert 	os_free(iface->afc.chan_info_list);
581*71e72c9eSCy Schubert 	iface->afc.chan_info_list = c;
582*71e72c9eSCy Schubert 	iface->afc.num_chan_info = count;
583*71e72c9eSCy Schubert 
584*71e72c9eSCy Schubert 	return 0;
585*71e72c9eSCy Schubert }
586*71e72c9eSCy Schubert 
587*71e72c9eSCy Schubert 
hostad_afc_get_timeout(struct json_token * obj)588*71e72c9eSCy Schubert static int hostad_afc_get_timeout(struct json_token *obj)
589*71e72c9eSCy Schubert {
590*71e72c9eSCy Schubert 	int year, month, day, hour, min, sec;
591*71e72c9eSCy Schubert 	os_time_t t;
592*71e72c9eSCy Schubert 	struct os_time now;
593*71e72c9eSCy Schubert 
594*71e72c9eSCy Schubert 	if (sscanf(obj->string, "%d-%d-%dT%d:%d:%dZ",
595*71e72c9eSCy Schubert 		   &year, &month, &day, &hour, &min, &sec) != 6)
596*71e72c9eSCy Schubert 		return HOSTAPD_AFC_TIMEOUT;
597*71e72c9eSCy Schubert 
598*71e72c9eSCy Schubert 	if (os_mktime(year, month, day, hour, min, sec, &t) < 0)
599*71e72c9eSCy Schubert 		return HOSTAPD_AFC_TIMEOUT;
600*71e72c9eSCy Schubert 
601*71e72c9eSCy Schubert 	os_get_time(&now);
602*71e72c9eSCy Schubert 
603*71e72c9eSCy Schubert 	return t < now.sec ?
604*71e72c9eSCy Schubert 		HOSTAPD_AFC_RETRY_TIMEOUT : (t - now.sec) * 80 / 100;
605*71e72c9eSCy Schubert }
606*71e72c9eSCy Schubert 
607*71e72c9eSCy Schubert 
608*71e72c9eSCy Schubert static int
hostapd_afc_parse_reply(struct hostapd_iface * iface,char * reply,size_t len)609*71e72c9eSCy Schubert hostapd_afc_parse_reply(struct hostapd_iface *iface, char *reply, size_t len)
610*71e72c9eSCy Schubert {
611*71e72c9eSCy Schubert 	struct hostapd_config *iconf = iface->conf;
612*71e72c9eSCy Schubert 	int request_timeout = -1, ret = -EINVAL;
613*71e72c9eSCy Schubert 	struct json_token *root = NULL, *reply_obj, *token;
614*71e72c9eSCy Schubert 
615*71e72c9eSCy Schubert 	wpa_hexdump_ascii(MSG_MSGDUMP, "AFC: Received reply", reply, len);
616*71e72c9eSCy Schubert 
617*71e72c9eSCy Schubert 	iface->afc_response = os_malloc(len + 1);
618*71e72c9eSCy Schubert 	if (!iface->afc_response) {
619*71e72c9eSCy Schubert 		wpa_printf(MSG_ERROR,
620*71e72c9eSCy Schubert 			   "AFC: Failed to alloc memory for storing reply");
621*71e72c9eSCy Schubert 		return -ENOMEM;
622*71e72c9eSCy Schubert 	}
623*71e72c9eSCy Schubert 	os_memcpy(iface->afc_response, reply, len);
624*71e72c9eSCy Schubert 	iface->afc_response[len] = '\0';
625*71e72c9eSCy Schubert 
626*71e72c9eSCy Schubert 	root = json_parse(reply, len);
627*71e72c9eSCy Schubert 	if (!root) {
628*71e72c9eSCy Schubert 		wpa_printf(MSG_ERROR, "AFC: Failed to parse reply payload");
629*71e72c9eSCy Schubert 		goto fail;
630*71e72c9eSCy Schubert 	}
631*71e72c9eSCy Schubert 
632*71e72c9eSCy Schubert 	token = json_get_member(root, "version");
633*71e72c9eSCy Schubert 	if (!token || token->type != JSON_STRING) {
634*71e72c9eSCy Schubert 		wpa_printf(MSG_ERROR, "AFC: Missing version in reply");
635*71e72c9eSCy Schubert 		goto fail;
636*71e72c9eSCy Schubert 	}
637*71e72c9eSCy Schubert 	if (iconf->afc.request.version &&
638*71e72c9eSCy Schubert 	    os_strcmp(iconf->afc.request.version, token->string) != 0) {
639*71e72c9eSCy Schubert 		wpa_printf(MSG_ERROR, "AFC: Mismatch in reply version");
640*71e72c9eSCy Schubert 		goto fail;
641*71e72c9eSCy Schubert 	}
642*71e72c9eSCy Schubert 
643*71e72c9eSCy Schubert 	reply_obj = json_get_member(root, "availableSpectrumInquiryResponses");
644*71e72c9eSCy Schubert 	if (!reply_obj || reply_obj->type != JSON_ARRAY) {
645*71e72c9eSCy Schubert 		wpa_printf(MSG_ERROR,
646*71e72c9eSCy Schubert 			   "AFC: Missing availableSpectrumInquiry in reply");
647*71e72c9eSCy Schubert 		goto fail;
648*71e72c9eSCy Schubert 	}
649*71e72c9eSCy Schubert 
650*71e72c9eSCy Schubert 	for (token = reply_obj->child; token; token = token->sibling) {
651*71e72c9eSCy Schubert 		struct json_token *reply_elem, *response;
652*71e72c9eSCy Schubert 		int status = -EINVAL, timeout;
653*71e72c9eSCy Schubert 		unsigned int j;
654*71e72c9eSCy Schubert 		char request_id_str[16];
655*71e72c9eSCy Schubert 
656*71e72c9eSCy Schubert 		reply_elem = json_get_member(token, "requestId");
657*71e72c9eSCy Schubert 		if (!reply_elem || reply_elem->type != JSON_STRING) {
658*71e72c9eSCy Schubert 			wpa_printf(MSG_DEBUG,
659*71e72c9eSCy Schubert 				   "AFC: Missing requestId in reply element");
660*71e72c9eSCy Schubert 			continue;
661*71e72c9eSCy Schubert 		}
662*71e72c9eSCy Schubert 
663*71e72c9eSCy Schubert 		snprintf(request_id_str, sizeof(request_id_str), "%u",
664*71e72c9eSCy Schubert 			 iface->afc.request_id);
665*71e72c9eSCy Schubert 		if (os_strcmp(request_id_str, reply_elem->string) != 0) {
666*71e72c9eSCy Schubert 			wpa_printf(MSG_DEBUG,
667*71e72c9eSCy Schubert 				   "AFC: RequestId mismatch in reply element");
668*71e72c9eSCy Schubert 			continue;
669*71e72c9eSCy Schubert 		}
670*71e72c9eSCy Schubert 
671*71e72c9eSCy Schubert 		reply_elem = json_get_member(token, "rulesetId");
672*71e72c9eSCy Schubert 		if (!reply_elem || reply_elem->type != JSON_STRING) {
673*71e72c9eSCy Schubert 			wpa_printf(MSG_DEBUG,
674*71e72c9eSCy Schubert 				   "AFC: Missing rulesetId in reply element");
675*71e72c9eSCy Schubert 			continue;
676*71e72c9eSCy Schubert 		}
677*71e72c9eSCy Schubert 
678*71e72c9eSCy Schubert 		for (j = 0; j < iconf->afc.n_cert_ids; j++) {
679*71e72c9eSCy Schubert 			if (os_strcmp(iconf->afc.cert_ids[j].ruleset,
680*71e72c9eSCy Schubert 				      reply_elem->string) == 0)
681*71e72c9eSCy Schubert 				break;
682*71e72c9eSCy Schubert 		}
683*71e72c9eSCy Schubert 
684*71e72c9eSCy Schubert 		if (j == iconf->afc.n_cert_ids) {
685*71e72c9eSCy Schubert 			wpa_printf(MSG_DEBUG,
686*71e72c9eSCy Schubert 				   "AFC: RulesetId mismatch in reply element");
687*71e72c9eSCy Schubert 			continue;
688*71e72c9eSCy Schubert 		}
689*71e72c9eSCy Schubert 
690*71e72c9eSCy Schubert 		response = json_get_member(token, "response");
691*71e72c9eSCy Schubert 		if (!response || response->type != JSON_OBJECT) {
692*71e72c9eSCy Schubert 			wpa_printf(MSG_DEBUG,
693*71e72c9eSCy Schubert 				   "AFC: Missing response field in reply element");
694*71e72c9eSCy Schubert 			continue;
695*71e72c9eSCy Schubert 		}
696*71e72c9eSCy Schubert 
697*71e72c9eSCy Schubert 		reply_elem = json_get_member(response, "shortDescription");
698*71e72c9eSCy Schubert 		if (reply_elem && reply_elem->type == JSON_STRING)
699*71e72c9eSCy Schubert 			wpa_printf(MSG_DEBUG, "AFC: Reply element: %s",
700*71e72c9eSCy Schubert 				   reply_elem->string);
701*71e72c9eSCy Schubert 
702*71e72c9eSCy Schubert 		reply_elem = json_get_member(response, "responseCode");
703*71e72c9eSCy Schubert 		if (reply_elem && reply_elem->type == JSON_NUMBER)
704*71e72c9eSCy Schubert 			status = reply_elem->number;
705*71e72c9eSCy Schubert 
706*71e72c9eSCy Schubert 		if (status < 0) {
707*71e72c9eSCy Schubert 			wpa_printf(MSG_DEBUG,
708*71e72c9eSCy Schubert 				   "AFC: Reply invalid responseCode: %d",
709*71e72c9eSCy Schubert 				   status);
710*71e72c9eSCy Schubert 			continue;
711*71e72c9eSCy Schubert 		}
712*71e72c9eSCy Schubert 		reply_elem = json_get_member(token, "availableFrequencyInfo");
713*71e72c9eSCy Schubert 		if (reply_elem && reply_elem->type == JSON_ARRAY &&
714*71e72c9eSCy Schubert 		    hostad_afc_parse_available_freq_info(iface, reply_elem)) {
715*71e72c9eSCy Schubert 			wpa_printf(MSG_DEBUG,
716*71e72c9eSCy Schubert 				   "AFC: Failed to parse availableFrequencyInfo");
717*71e72c9eSCy Schubert 			continue;
718*71e72c9eSCy Schubert 		}
719*71e72c9eSCy Schubert 
720*71e72c9eSCy Schubert 		reply_elem = json_get_member(token, "availableChannelInfo");
721*71e72c9eSCy Schubert 		if (reply_elem && reply_elem->type == JSON_ARRAY &&
722*71e72c9eSCy Schubert 		    hostad_afc_parse_available_chan_info(iface, reply_elem)) {
723*71e72c9eSCy Schubert 			wpa_printf(MSG_DEBUG,
724*71e72c9eSCy Schubert 				   "AFC: Failed to parse availableChannelInfo");
725*71e72c9eSCy Schubert 			continue;
726*71e72c9eSCy Schubert 		}
727*71e72c9eSCy Schubert 
728*71e72c9eSCy Schubert 		reply_elem = json_get_member(token, "availabilityExpireTime");
729*71e72c9eSCy Schubert 		if (!reply_elem || reply_elem->type != JSON_STRING) {
730*71e72c9eSCy Schubert 			wpa_printf(MSG_DEBUG,
731*71e72c9eSCy Schubert 				   "AFC: Missing expire time, use default timeout");
732*71e72c9eSCy Schubert 			continue;
733*71e72c9eSCy Schubert 		}
734*71e72c9eSCy Schubert 
735*71e72c9eSCy Schubert 		timeout = hostad_afc_get_timeout(reply_elem);
736*71e72c9eSCy Schubert 		if (request_timeout < 0 || timeout < request_timeout)
737*71e72c9eSCy Schubert 			request_timeout = timeout;
738*71e72c9eSCy Schubert 
739*71e72c9eSCy Schubert 		ret = status;
740*71e72c9eSCy Schubert 	}
741*71e72c9eSCy Schubert 
742*71e72c9eSCy Schubert 	iface->afc.data_valid = true;
743*71e72c9eSCy Schubert 	iface->afc.timeout = request_timeout;
744*71e72c9eSCy Schubert 	if (iface->afc.timeout < 0)
745*71e72c9eSCy Schubert 		iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
746*71e72c9eSCy Schubert 
747*71e72c9eSCy Schubert fail:
748*71e72c9eSCy Schubert 	json_free(root);
749*71e72c9eSCy Schubert 	return ret;
750*71e72c9eSCy Schubert }
751*71e72c9eSCy Schubert 
752*71e72c9eSCy Schubert 
hostapd_afc_send_receive(struct hostapd_iface * iface)753*71e72c9eSCy Schubert static int hostapd_afc_send_receive(struct hostapd_iface *iface)
754*71e72c9eSCy Schubert {
755*71e72c9eSCy Schubert 	struct hostapd_config *iconf = iface->conf;
756*71e72c9eSCy Schubert 	struct wpabuf *request_obj = NULL;
757*71e72c9eSCy Schubert 	struct timeval sock_timeout = {
758*71e72c9eSCy Schubert 		.tv_sec = 10,
759*71e72c9eSCy Schubert 	};
760*71e72c9eSCy Schubert 	struct sockaddr_un addr = {
761*71e72c9eSCy Schubert 		.sun_family = AF_UNIX,
762*71e72c9eSCy Schubert #ifdef __FreeBSD__
763*71e72c9eSCy Schubert 		.sun_len = sizeof(addr),
764*71e72c9eSCy Schubert #endif /* __FreeBSD__ */
765*71e72c9eSCy Schubert 	};
766*71e72c9eSCy Schubert 	char *buf = NULL;
767*71e72c9eSCy Schubert 	int sockfd, ret;
768*71e72c9eSCy Schubert 	fd_set read_set;
769*71e72c9eSCy Schubert 
770*71e72c9eSCy Schubert 	if (iface->afc.data_valid) {
771*71e72c9eSCy Schubert 		/* AFC data already downloaded from the server */
772*71e72c9eSCy Schubert 		return 0;
773*71e72c9eSCy Schubert 	}
774*71e72c9eSCy Schubert 
775*71e72c9eSCy Schubert 	iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
776*71e72c9eSCy Schubert 	if (!iconf->afc.socket) {
777*71e72c9eSCy Schubert 		wpa_printf(MSG_ERROR, "AFC: Missing socket string");
778*71e72c9eSCy Schubert 		return -EINVAL;
779*71e72c9eSCy Schubert 	}
780*71e72c9eSCy Schubert 
781*71e72c9eSCy Schubert 	if (os_strlen(iconf->afc.socket) >= sizeof(addr.sun_path)) {
782*71e72c9eSCy Schubert 		wpa_printf(MSG_ERROR, "AFC: Malformed socket string %s",
783*71e72c9eSCy Schubert 			   iconf->afc.socket);
784*71e72c9eSCy Schubert 		return -EINVAL;
785*71e72c9eSCy Schubert 	}
786*71e72c9eSCy Schubert 
787*71e72c9eSCy Schubert 	os_strlcpy(addr.sun_path, iconf->afc.socket, sizeof(addr.sun_path));
788*71e72c9eSCy Schubert 	sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
789*71e72c9eSCy Schubert 	if (sockfd < 0) {
790*71e72c9eSCy Schubert 		wpa_printf(MSG_ERROR, "AFC: Failed creating socket");
791*71e72c9eSCy Schubert 		return sockfd;
792*71e72c9eSCy Schubert 	}
793*71e72c9eSCy Schubert 
794*71e72c9eSCy Schubert 	if (connect(sockfd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
795*71e72c9eSCy Schubert 		wpa_printf(MSG_ERROR, "AFC: Failed connecting socket");
796*71e72c9eSCy Schubert 		ret = -EIO;
797*71e72c9eSCy Schubert 		goto close_sock;
798*71e72c9eSCy Schubert 	}
799*71e72c9eSCy Schubert 
800*71e72c9eSCy Schubert 	request_obj = hostapd_afc_build_request(iface);
801*71e72c9eSCy Schubert 	if (!request_obj) {
802*71e72c9eSCy Schubert 		ret = -EINVAL;
803*71e72c9eSCy Schubert 		goto close_sock;
804*71e72c9eSCy Schubert 	}
805*71e72c9eSCy Schubert 
806*71e72c9eSCy Schubert 	ret = send(sockfd, wpabuf_head(request_obj), wpabuf_len(request_obj),
807*71e72c9eSCy Schubert 		   0);
808*71e72c9eSCy Schubert 	if (ret < 0 || (size_t) ret != wpabuf_len(request_obj)) {
809*71e72c9eSCy Schubert 		wpa_printf(MSG_ERROR, "AFC: Failed sending request");
810*71e72c9eSCy Schubert 		ret = -EIO;
811*71e72c9eSCy Schubert 		goto close_sock;
812*71e72c9eSCy Schubert 	}
813*71e72c9eSCy Schubert 
814*71e72c9eSCy Schubert 	FD_ZERO(&read_set);
815*71e72c9eSCy Schubert 	FD_SET(sockfd, &read_set);
816*71e72c9eSCy Schubert 	if (select(sockfd + 1, &read_set, NULL, NULL, &sock_timeout) < 0) {
817*71e72c9eSCy Schubert 		wpa_printf(MSG_ERROR, "AFC: select() failed on socket");
818*71e72c9eSCy Schubert 		ret = -errno;
819*71e72c9eSCy Schubert 		goto close_sock;
820*71e72c9eSCy Schubert 	}
821*71e72c9eSCy Schubert 
822*71e72c9eSCy Schubert 	if (!FD_ISSET(sockfd, &read_set)) {
823*71e72c9eSCy Schubert 		ret = -EIO;
824*71e72c9eSCy Schubert 		goto close_sock;
825*71e72c9eSCy Schubert 	}
826*71e72c9eSCy Schubert 
827*71e72c9eSCy Schubert 	buf = os_zalloc(HOSTAPD_AFC_BUFSIZE);
828*71e72c9eSCy Schubert 	if (!buf) {
829*71e72c9eSCy Schubert 		ret = -ENOMEM;
830*71e72c9eSCy Schubert 		goto close_sock;
831*71e72c9eSCy Schubert 	}
832*71e72c9eSCy Schubert 
833*71e72c9eSCy Schubert 	ret = recv(sockfd, buf, HOSTAPD_AFC_BUFSIZE - 1, 0);
834*71e72c9eSCy Schubert 	if (!ret)
835*71e72c9eSCy Schubert 		ret = -EIO;
836*71e72c9eSCy Schubert 	if (ret < 0)
837*71e72c9eSCy Schubert 		goto close_sock;
838*71e72c9eSCy Schubert 
839*71e72c9eSCy Schubert 	ret = hostapd_afc_parse_reply(iface, buf, ret);
840*71e72c9eSCy Schubert 	if (ret) {
841*71e72c9eSCy Schubert 		wpa_printf(MSG_ERROR, "AFC: Failed parsing reply: %d", ret);
842*71e72c9eSCy Schubert 		goto close_sock;
843*71e72c9eSCy Schubert 	}
844*71e72c9eSCy Schubert 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AFC_EVENT_RECEIVED);
845*71e72c9eSCy Schubert close_sock:
846*71e72c9eSCy Schubert 	os_free(buf);
847*71e72c9eSCy Schubert 	wpabuf_free(iface->afc_request);
848*71e72c9eSCy Schubert 	iface->afc_request = request_obj;
849*71e72c9eSCy Schubert 	close(sockfd);
850*71e72c9eSCy Schubert 
851*71e72c9eSCy Schubert 	return ret;
852*71e72c9eSCy Schubert }
853*71e72c9eSCy Schubert 
854*71e72c9eSCy Schubert 
hostapd_afc_has_usable_chans(struct hostapd_iface * iface)855*71e72c9eSCy Schubert static bool hostapd_afc_has_usable_chans(struct hostapd_iface *iface)
856*71e72c9eSCy Schubert {
857*71e72c9eSCy Schubert 	const struct oper_class_map *oper_class;
858*71e72c9eSCy Schubert 	int channel, chan_num;
859*71e72c9eSCy Schubert 
860*71e72c9eSCy Schubert 	oper_class = get_oper_class(NULL, iface->conf->op_class);
861*71e72c9eSCy Schubert 	if (!oper_class)
862*71e72c9eSCy Schubert 		return false;
863*71e72c9eSCy Schubert 
864*71e72c9eSCy Schubert 	switch (iface->conf->op_class) {
865*71e72c9eSCy Schubert 	case 132: /* 40 MHz */
866*71e72c9eSCy Schubert 		chan_num = 2;
867*71e72c9eSCy Schubert 		break;
868*71e72c9eSCy Schubert 	case 133: /* 80 MHz */
869*71e72c9eSCy Schubert 		chan_num = 4;
870*71e72c9eSCy Schubert 		break;
871*71e72c9eSCy Schubert 	case 134: /* 160 MHz */
872*71e72c9eSCy Schubert 		chan_num = 8;
873*71e72c9eSCy Schubert 		break;
874*71e72c9eSCy Schubert 	case 137: /* 320 MHz */
875*71e72c9eSCy Schubert 		chan_num = 16;
876*71e72c9eSCy Schubert 		break;
877*71e72c9eSCy Schubert 	default:
878*71e72c9eSCy Schubert 		chan_num = 1;
879*71e72c9eSCy Schubert 		break;
880*71e72c9eSCy Schubert 	}
881*71e72c9eSCy Schubert 
882*71e72c9eSCy Schubert 	for (channel = oper_class->min_chan; channel <= oper_class->max_chan;
883*71e72c9eSCy Schubert 	     channel += oper_class->inc) {
884*71e72c9eSCy Schubert 		struct hostapd_hw_modes *mode = iface->current_mode;
885*71e72c9eSCy Schubert 		struct hostapd_channel_data *chan = NULL;
886*71e72c9eSCy Schubert 		int i, start_ch = channel;
887*71e72c9eSCy Schubert 
888*71e72c9eSCy Schubert 		if (iface->conf->op_class == 137)
889*71e72c9eSCy Schubert 			start_ch = channel - 30;
890*71e72c9eSCy Schubert 
891*71e72c9eSCy Schubert 		for (i = 0; i < chan_num; i++) {
892*71e72c9eSCy Schubert 			chan = hw_get_channel_chan(mode, start_ch + i * 4,
893*71e72c9eSCy Schubert 						   NULL);
894*71e72c9eSCy Schubert 
895*71e72c9eSCy Schubert 			if (!chan || chan->flag & HOSTAPD_CHAN_DISABLED)
896*71e72c9eSCy Schubert 				break;
897*71e72c9eSCy Schubert 		}
898*71e72c9eSCy Schubert 
899*71e72c9eSCy Schubert 		if (i == chan_num)
900*71e72c9eSCy Schubert 			return true;
901*71e72c9eSCy Schubert 	}
902*71e72c9eSCy Schubert 
903*71e72c9eSCy Schubert 	return false;
904*71e72c9eSCy Schubert }
905*71e72c9eSCy Schubert 
906*71e72c9eSCy Schubert 
hostapd_afc_handle_request(struct hostapd_iface * iface)907*71e72c9eSCy Schubert int hostapd_afc_handle_request(struct hostapd_iface *iface)
908*71e72c9eSCy Schubert {
909*71e72c9eSCy Schubert 	struct hostapd_config *iconf = iface->conf;
910*71e72c9eSCy Schubert 	int ret;
911*71e72c9eSCy Schubert 
912*71e72c9eSCy Schubert 	/* AFC is required just for standard power AP */
913*71e72c9eSCy Schubert 	if (!he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
914*71e72c9eSCy Schubert 		return 1;
915*71e72c9eSCy Schubert 
916*71e72c9eSCy Schubert 	if (!is_6ghz_op_class(iconf->op_class) || !is_6ghz_freq(iface->freq))
917*71e72c9eSCy Schubert 		return 1;
918*71e72c9eSCy Schubert 
919*71e72c9eSCy Schubert 	if (iface->state == HAPD_IFACE_ACS)
920*71e72c9eSCy Schubert 		return 1;
921*71e72c9eSCy Schubert 
922*71e72c9eSCy Schubert 	iface->afc.request_id = os_random();
923*71e72c9eSCy Schubert 	ret = hostapd_afc_send_receive(iface);
924*71e72c9eSCy Schubert 	if (ret < 0) {
925*71e72c9eSCy Schubert 		/*
926*71e72c9eSCy Schubert 		 * If the connection to the AFCD failed, reschedule for a
927*71e72c9eSCy Schubert 		 * future attempt.
928*71e72c9eSCy Schubert 		 */
929*71e72c9eSCy Schubert 		wpa_printf(MSG_ERROR, "AFC: Connection failed: %d", ret);
930*71e72c9eSCy Schubert 		if (ret == -EIO)
931*71e72c9eSCy Schubert 			ret = 0;
932*71e72c9eSCy Schubert 		goto resched;
933*71e72c9eSCy Schubert 	}
934*71e72c9eSCy Schubert 
935*71e72c9eSCy Schubert 	hostap_afc_disable_channels(iface);
936*71e72c9eSCy Schubert 	if (!hostapd_afc_has_usable_chans(iface))
937*71e72c9eSCy Schubert 		goto resched;
938*71e72c9eSCy Schubert 
939*71e72c9eSCy Schubert 	ret = hostapd_is_usable_chans(iface);
940*71e72c9eSCy Schubert 	if (ret != 1) {
941*71e72c9eSCy Schubert 		/* Trigger an ACS freq scan */
942*71e72c9eSCy Schubert 		iconf->channel = 0;
943*71e72c9eSCy Schubert 		iface->freq = 0;
944*71e72c9eSCy Schubert 		hostapd_set_and_check_bw320_offset(iface->conf, 0);
945*71e72c9eSCy Schubert 
946*71e72c9eSCy Schubert 		if (!ret && acs_init(iface) != HOSTAPD_CHAN_ACS) {
947*71e72c9eSCy Schubert 			wpa_printf(MSG_ERROR, "AFC: Could not start ACS");
948*71e72c9eSCy Schubert 			ret = -EINVAL;
949*71e72c9eSCy Schubert 		}
950*71e72c9eSCy Schubert 	}
951*71e72c9eSCy Schubert 
952*71e72c9eSCy Schubert resched:
953*71e72c9eSCy Schubert 	eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL);
954*71e72c9eSCy Schubert 	eloop_register_timeout(iface->afc.timeout, 0,
955*71e72c9eSCy Schubert 			       hostapd_afc_timeout_handler, iface, NULL);
956*71e72c9eSCy Schubert 
957*71e72c9eSCy Schubert 	return ret;
958*71e72c9eSCy Schubert }
959*71e72c9eSCy Schubert 
960*71e72c9eSCy Schubert 
hostapd_afc_delete_data_from_server(struct hostapd_iface * iface)961*71e72c9eSCy Schubert static void hostapd_afc_delete_data_from_server(struct hostapd_iface *iface)
962*71e72c9eSCy Schubert {
963*71e72c9eSCy Schubert 	wpabuf_free(iface->afc_request);
964*71e72c9eSCy Schubert 	os_free(iface->afc_response);
965*71e72c9eSCy Schubert 	os_free(iface->afc.chan_info_list);
966*71e72c9eSCy Schubert 	os_free(iface->afc.freq_range);
967*71e72c9eSCy Schubert 
968*71e72c9eSCy Schubert 	iface->afc_request = NULL;
969*71e72c9eSCy Schubert 	iface->afc_response = NULL;
970*71e72c9eSCy Schubert 
971*71e72c9eSCy Schubert 	iface->afc.num_freq_range = 0;
972*71e72c9eSCy Schubert 	iface->afc.num_chan_info = 0;
973*71e72c9eSCy Schubert 
974*71e72c9eSCy Schubert 	iface->afc.chan_info_list = NULL;
975*71e72c9eSCy Schubert 	iface->afc.freq_range = NULL;
976*71e72c9eSCy Schubert 
977*71e72c9eSCy Schubert 	iface->afc.data_valid = false;
978*71e72c9eSCy Schubert }
979*71e72c9eSCy Schubert 
980*71e72c9eSCy Schubert 
hostapd_afc_timeout_handler(void * eloop_ctx,void * timeout_ctx)981*71e72c9eSCy Schubert static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx)
982*71e72c9eSCy Schubert {
983*71e72c9eSCy Schubert 	struct hostapd_iface *iface = eloop_ctx;
984*71e72c9eSCy Schubert 	bool restart_iface = true;
985*71e72c9eSCy Schubert 
986*71e72c9eSCy Schubert 	hostapd_afc_delete_data_from_server(iface);
987*71e72c9eSCy Schubert 	if (iface->state != HAPD_IFACE_ENABLED) {
988*71e72c9eSCy Schubert 		/* hostapd is not fully enabled yet, toggle the interface */
989*71e72c9eSCy Schubert 		goto restart_interface;
990*71e72c9eSCy Schubert 	}
991*71e72c9eSCy Schubert 
992*71e72c9eSCy Schubert 	if (hostapd_afc_send_receive(iface) < 0 ||
993*71e72c9eSCy Schubert 	    hostapd_afc_reset_channels(iface)) {
994*71e72c9eSCy Schubert 		restart_iface = false;
995*71e72c9eSCy Schubert 		goto restart_interface;
996*71e72c9eSCy Schubert 	}
997*71e72c9eSCy Schubert 
998*71e72c9eSCy Schubert 	if (hostapd_is_usable_chans(iface) > 0) {
999*71e72c9eSCy Schubert 		wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AFC_EVENT_COMPLETE);
1000*71e72c9eSCy Schubert 		goto resched;
1001*71e72c9eSCy Schubert 	}
1002*71e72c9eSCy Schubert 
1003*71e72c9eSCy Schubert 	restart_iface = hostapd_afc_has_usable_chans(iface);
1004*71e72c9eSCy Schubert 	if (restart_iface) {
1005*71e72c9eSCy Schubert 		/* Trigger an ACS freq scan */
1006*71e72c9eSCy Schubert 		iface->conf->channel = 0;
1007*71e72c9eSCy Schubert 		iface->freq = 0;
1008*71e72c9eSCy Schubert 		hostapd_set_and_check_bw320_offset(iface->conf, 0);
1009*71e72c9eSCy Schubert 	} else {
1010*71e72c9eSCy Schubert 		wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AFC_EVENT_COMPLETE);
1011*71e72c9eSCy Schubert 	}
1012*71e72c9eSCy Schubert 
1013*71e72c9eSCy Schubert restart_interface:
1014*71e72c9eSCy Schubert 	hostapd_disable_iface(iface);
1015*71e72c9eSCy Schubert 	if (restart_iface)
1016*71e72c9eSCy Schubert 		hostapd_enable_iface(iface);
1017*71e72c9eSCy Schubert resched:
1018*71e72c9eSCy Schubert 	eloop_register_timeout(iface->afc.timeout, 0,
1019*71e72c9eSCy Schubert 			       hostapd_afc_timeout_handler, iface, NULL);
1020*71e72c9eSCy Schubert }
1021*71e72c9eSCy Schubert 
1022*71e72c9eSCy Schubert 
hostapd_afc_send_request(struct hostapd_iface * iface)1023*71e72c9eSCy Schubert void hostapd_afc_send_request(struct hostapd_iface *iface)
1024*71e72c9eSCy Schubert {
1025*71e72c9eSCy Schubert 	eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL);
1026*71e72c9eSCy Schubert 	eloop_register_timeout(0, 0, hostapd_afc_timeout_handler, iface, NULL);
1027*71e72c9eSCy Schubert }
1028*71e72c9eSCy Schubert 
1029*71e72c9eSCy Schubert 
hostapd_afc_stop(struct hostapd_iface * iface)1030*71e72c9eSCy Schubert void hostapd_afc_stop(struct hostapd_iface *iface)
1031*71e72c9eSCy Schubert {
1032*71e72c9eSCy Schubert 	eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL);
1033*71e72c9eSCy Schubert }
1034*71e72c9eSCy Schubert 
1035*71e72c9eSCy Schubert 
hostap_afc_disable_channels(struct hostapd_iface * iface)1036*71e72c9eSCy Schubert void hostap_afc_disable_channels(struct hostapd_iface *iface)
1037*71e72c9eSCy Schubert {
1038*71e72c9eSCy Schubert 	struct hostapd_hw_modes *mode = NULL;
1039*71e72c9eSCy Schubert 	int i;
1040*71e72c9eSCy Schubert 
1041*71e72c9eSCy Schubert 	for (i = 0; i < iface->num_hw_features; i++) {
1042*71e72c9eSCy Schubert 		mode = &iface->hw_features[i];
1043*71e72c9eSCy Schubert 		if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
1044*71e72c9eSCy Schubert 		    mode->is_6ghz)
1045*71e72c9eSCy Schubert 			break;
1046*71e72c9eSCy Schubert 	}
1047*71e72c9eSCy Schubert 
1048*71e72c9eSCy Schubert 	if (i == iface->num_hw_features ||
1049*71e72c9eSCy Schubert 	    !he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type) ||
1050*71e72c9eSCy Schubert 	    !iface->afc.data_valid)
1051*71e72c9eSCy Schubert 		return;
1052*71e72c9eSCy Schubert 
1053*71e72c9eSCy Schubert 	for (i = 0; i < mode->num_channels; i++) {
1054*71e72c9eSCy Schubert 		struct hostapd_channel_data *chan = &mode->channels[i];
1055*71e72c9eSCy Schubert 		unsigned int j;
1056*71e72c9eSCy Schubert 
1057*71e72c9eSCy Schubert 		if (!is_6ghz_freq(chan->freq))
1058*71e72c9eSCy Schubert 			continue;
1059*71e72c9eSCy Schubert 
1060*71e72c9eSCy Schubert 		for (j = 0; j < iface->afc.num_freq_range; j++) {
1061*71e72c9eSCy Schubert 			if (chan->freq >= iface->afc.freq_range[j].low_freq &&
1062*71e72c9eSCy Schubert 			    chan->freq <= iface->afc.freq_range[j].high_freq)
1063*71e72c9eSCy Schubert 				break;
1064*71e72c9eSCy Schubert 		}
1065*71e72c9eSCy Schubert 
1066*71e72c9eSCy Schubert 		if (j != iface->afc.num_freq_range)
1067*71e72c9eSCy Schubert 			continue;
1068*71e72c9eSCy Schubert 
1069*71e72c9eSCy Schubert 		for (j = 0; j < iface->afc.num_chan_info; j++) {
1070*71e72c9eSCy Schubert 			if (chan->chan == iface->afc.chan_info_list[j].chan)
1071*71e72c9eSCy Schubert 				break;
1072*71e72c9eSCy Schubert 		}
1073*71e72c9eSCy Schubert 
1074*71e72c9eSCy Schubert 		if (j != iface->afc.num_chan_info)
1075*71e72c9eSCy Schubert 			continue;
1076*71e72c9eSCy Schubert 
1077*71e72c9eSCy Schubert 		chan->flag |= HOSTAPD_CHAN_DISABLED;
1078*71e72c9eSCy Schubert 		wpa_printf(MSG_MSGDUMP,
1079*71e72c9eSCy Schubert 			   "AFC: Disabling freq=%d MHz (not allowed by AFC)",
1080*71e72c9eSCy Schubert 			   chan->freq);
1081*71e72c9eSCy Schubert 	}
1082*71e72c9eSCy Schubert }
1083*71e72c9eSCy Schubert 
1084*71e72c9eSCy Schubert 
hostap_afc_get_chan_max_eirp_power(struct hostapd_iface * iface,bool psd,int * power)1085*71e72c9eSCy Schubert int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
1086*71e72c9eSCy Schubert 				       int *power)
1087*71e72c9eSCy Schubert {
1088*71e72c9eSCy Schubert 	unsigned int i;
1089*71e72c9eSCy Schubert 
1090*71e72c9eSCy Schubert 	if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type) ||
1091*71e72c9eSCy Schubert 	    !iface->afc.data_valid)
1092*71e72c9eSCy Schubert 		return -EINVAL;
1093*71e72c9eSCy Schubert 
1094*71e72c9eSCy Schubert 	if (psd) {
1095*71e72c9eSCy Schubert 		for (i = 0; i < iface->afc.num_freq_range; i++) {
1096*71e72c9eSCy Schubert 			struct afc_freq_range_elem *f;
1097*71e72c9eSCy Schubert 
1098*71e72c9eSCy Schubert 			f = &iface->afc.freq_range[i];
1099*71e72c9eSCy Schubert 			if (iface->freq >= f->low_freq &&
1100*71e72c9eSCy Schubert 			    iface->freq <= f->high_freq) {
1101*71e72c9eSCy Schubert 				*power = (int) (2 * f->max_psd);
1102*71e72c9eSCy Schubert 				return 0;
1103*71e72c9eSCy Schubert 			}
1104*71e72c9eSCy Schubert 		}
1105*71e72c9eSCy Schubert 	} else {
1106*71e72c9eSCy Schubert 		for (i = 0; i < iface->afc.num_chan_info; i++) {
1107*71e72c9eSCy Schubert 			struct afc_chan_info_elem *c;
1108*71e72c9eSCy Schubert 
1109*71e72c9eSCy Schubert 			c = &iface->afc.chan_info_list[i];
1110*71e72c9eSCy Schubert 			if (c->chan == iface->conf->channel) {
1111*71e72c9eSCy Schubert 				unsigned int j;
1112*71e72c9eSCy Schubert 
1113*71e72c9eSCy Schubert 				*power = 0;
1114*71e72c9eSCy Schubert 				for (j = 0; j < ARRAY_SIZE(c->power); j++)
1115*71e72c9eSCy Schubert 					*power = MAX(*power,
1116*71e72c9eSCy Schubert 						     (int) (2 * c->power[j]));
1117*71e72c9eSCy Schubert 				return 0;
1118*71e72c9eSCy Schubert 			}
1119*71e72c9eSCy Schubert 		}
1120*71e72c9eSCy Schubert 	}
1121*71e72c9eSCy Schubert 
1122*71e72c9eSCy Schubert 	return -EINVAL;
1123*71e72c9eSCy Schubert }
1124