xref: /freebsd/contrib/wpa/src/drivers/driver_nl80211_capa.c (revision 5b9c547c072b84410b50897cc53710c75b2f6b74)
1*5b9c547cSRui Paulo /*
2*5b9c547cSRui Paulo  * Driver interaction with Linux nl80211/cfg80211 - Capabilities
3*5b9c547cSRui Paulo  * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
4*5b9c547cSRui Paulo  * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
5*5b9c547cSRui Paulo  * Copyright (c) 2009-2010, Atheros Communications
6*5b9c547cSRui Paulo  *
7*5b9c547cSRui Paulo  * This software may be distributed under the terms of the BSD license.
8*5b9c547cSRui Paulo  * See README for more details.
9*5b9c547cSRui Paulo  */
10*5b9c547cSRui Paulo 
11*5b9c547cSRui Paulo #include "includes.h"
12*5b9c547cSRui Paulo #include <netlink/genl/genl.h>
13*5b9c547cSRui Paulo 
14*5b9c547cSRui Paulo #include "utils/common.h"
15*5b9c547cSRui Paulo #include "common/ieee802_11_defs.h"
16*5b9c547cSRui Paulo #include "common/ieee802_11_common.h"
17*5b9c547cSRui Paulo #include "common/qca-vendor.h"
18*5b9c547cSRui Paulo #include "common/qca-vendor-attr.h"
19*5b9c547cSRui Paulo #include "driver_nl80211.h"
20*5b9c547cSRui Paulo 
21*5b9c547cSRui Paulo 
22*5b9c547cSRui Paulo static int protocol_feature_handler(struct nl_msg *msg, void *arg)
23*5b9c547cSRui Paulo {
24*5b9c547cSRui Paulo 	u32 *feat = arg;
25*5b9c547cSRui Paulo 	struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
26*5b9c547cSRui Paulo 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
27*5b9c547cSRui Paulo 
28*5b9c547cSRui Paulo 	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
29*5b9c547cSRui Paulo 		  genlmsg_attrlen(gnlh, 0), NULL);
30*5b9c547cSRui Paulo 
31*5b9c547cSRui Paulo 	if (tb_msg[NL80211_ATTR_PROTOCOL_FEATURES])
32*5b9c547cSRui Paulo 		*feat = nla_get_u32(tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]);
33*5b9c547cSRui Paulo 
34*5b9c547cSRui Paulo 	return NL_SKIP;
35*5b9c547cSRui Paulo }
36*5b9c547cSRui Paulo 
37*5b9c547cSRui Paulo 
38*5b9c547cSRui Paulo static u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv)
39*5b9c547cSRui Paulo {
40*5b9c547cSRui Paulo 	u32 feat = 0;
41*5b9c547cSRui Paulo 	struct nl_msg *msg;
42*5b9c547cSRui Paulo 
43*5b9c547cSRui Paulo 	msg = nlmsg_alloc();
44*5b9c547cSRui Paulo 	if (!msg)
45*5b9c547cSRui Paulo 		return 0;
46*5b9c547cSRui Paulo 
47*5b9c547cSRui Paulo 	if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_PROTOCOL_FEATURES)) {
48*5b9c547cSRui Paulo 		nlmsg_free(msg);
49*5b9c547cSRui Paulo 		return 0;
50*5b9c547cSRui Paulo 	}
51*5b9c547cSRui Paulo 
52*5b9c547cSRui Paulo 	if (send_and_recv_msgs(drv, msg, protocol_feature_handler, &feat) == 0)
53*5b9c547cSRui Paulo 		return feat;
54*5b9c547cSRui Paulo 
55*5b9c547cSRui Paulo 	return 0;
56*5b9c547cSRui Paulo }
57*5b9c547cSRui Paulo 
58*5b9c547cSRui Paulo 
59*5b9c547cSRui Paulo struct wiphy_info_data {
60*5b9c547cSRui Paulo 	struct wpa_driver_nl80211_data *drv;
61*5b9c547cSRui Paulo 	struct wpa_driver_capa *capa;
62*5b9c547cSRui Paulo 
63*5b9c547cSRui Paulo 	unsigned int num_multichan_concurrent;
64*5b9c547cSRui Paulo 
65*5b9c547cSRui Paulo 	unsigned int error:1;
66*5b9c547cSRui Paulo 	unsigned int device_ap_sme:1;
67*5b9c547cSRui Paulo 	unsigned int poll_command_supported:1;
68*5b9c547cSRui Paulo 	unsigned int data_tx_status:1;
69*5b9c547cSRui Paulo 	unsigned int monitor_supported:1;
70*5b9c547cSRui Paulo 	unsigned int auth_supported:1;
71*5b9c547cSRui Paulo 	unsigned int connect_supported:1;
72*5b9c547cSRui Paulo 	unsigned int p2p_go_supported:1;
73*5b9c547cSRui Paulo 	unsigned int p2p_client_supported:1;
74*5b9c547cSRui Paulo 	unsigned int p2p_go_ctwindow_supported:1;
75*5b9c547cSRui Paulo 	unsigned int p2p_concurrent:1;
76*5b9c547cSRui Paulo 	unsigned int channel_switch_supported:1;
77*5b9c547cSRui Paulo 	unsigned int set_qos_map_supported:1;
78*5b9c547cSRui Paulo 	unsigned int have_low_prio_scan:1;
79*5b9c547cSRui Paulo 	unsigned int wmm_ac_supported:1;
80*5b9c547cSRui Paulo 	unsigned int mac_addr_rand_scan_supported:1;
81*5b9c547cSRui Paulo 	unsigned int mac_addr_rand_sched_scan_supported:1;
82*5b9c547cSRui Paulo };
83*5b9c547cSRui Paulo 
84*5b9c547cSRui Paulo 
85*5b9c547cSRui Paulo static unsigned int probe_resp_offload_support(int supp_protocols)
86*5b9c547cSRui Paulo {
87*5b9c547cSRui Paulo 	unsigned int prot = 0;
88*5b9c547cSRui Paulo 
89*5b9c547cSRui Paulo 	if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS)
90*5b9c547cSRui Paulo 		prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS;
91*5b9c547cSRui Paulo 	if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2)
92*5b9c547cSRui Paulo 		prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2;
93*5b9c547cSRui Paulo 	if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P)
94*5b9c547cSRui Paulo 		prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P;
95*5b9c547cSRui Paulo 	if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U)
96*5b9c547cSRui Paulo 		prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING;
97*5b9c547cSRui Paulo 
98*5b9c547cSRui Paulo 	return prot;
99*5b9c547cSRui Paulo }
100*5b9c547cSRui Paulo 
101*5b9c547cSRui Paulo 
102*5b9c547cSRui Paulo static void wiphy_info_supported_iftypes(struct wiphy_info_data *info,
103*5b9c547cSRui Paulo 					 struct nlattr *tb)
104*5b9c547cSRui Paulo {
105*5b9c547cSRui Paulo 	struct nlattr *nl_mode;
106*5b9c547cSRui Paulo 	int i;
107*5b9c547cSRui Paulo 
108*5b9c547cSRui Paulo 	if (tb == NULL)
109*5b9c547cSRui Paulo 		return;
110*5b9c547cSRui Paulo 
111*5b9c547cSRui Paulo 	nla_for_each_nested(nl_mode, tb, i) {
112*5b9c547cSRui Paulo 		switch (nla_type(nl_mode)) {
113*5b9c547cSRui Paulo 		case NL80211_IFTYPE_AP:
114*5b9c547cSRui Paulo 			info->capa->flags |= WPA_DRIVER_FLAGS_AP;
115*5b9c547cSRui Paulo 			break;
116*5b9c547cSRui Paulo 		case NL80211_IFTYPE_MESH_POINT:
117*5b9c547cSRui Paulo 			info->capa->flags |= WPA_DRIVER_FLAGS_MESH;
118*5b9c547cSRui Paulo 			break;
119*5b9c547cSRui Paulo 		case NL80211_IFTYPE_ADHOC:
120*5b9c547cSRui Paulo 			info->capa->flags |= WPA_DRIVER_FLAGS_IBSS;
121*5b9c547cSRui Paulo 			break;
122*5b9c547cSRui Paulo 		case NL80211_IFTYPE_P2P_DEVICE:
123*5b9c547cSRui Paulo 			info->capa->flags |=
124*5b9c547cSRui Paulo 				WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE;
125*5b9c547cSRui Paulo 			break;
126*5b9c547cSRui Paulo 		case NL80211_IFTYPE_P2P_GO:
127*5b9c547cSRui Paulo 			info->p2p_go_supported = 1;
128*5b9c547cSRui Paulo 			break;
129*5b9c547cSRui Paulo 		case NL80211_IFTYPE_P2P_CLIENT:
130*5b9c547cSRui Paulo 			info->p2p_client_supported = 1;
131*5b9c547cSRui Paulo 			break;
132*5b9c547cSRui Paulo 		case NL80211_IFTYPE_MONITOR:
133*5b9c547cSRui Paulo 			info->monitor_supported = 1;
134*5b9c547cSRui Paulo 			break;
135*5b9c547cSRui Paulo 		}
136*5b9c547cSRui Paulo 	}
137*5b9c547cSRui Paulo }
138*5b9c547cSRui Paulo 
139*5b9c547cSRui Paulo 
140*5b9c547cSRui Paulo static int wiphy_info_iface_comb_process(struct wiphy_info_data *info,
141*5b9c547cSRui Paulo 					 struct nlattr *nl_combi)
142*5b9c547cSRui Paulo {
143*5b9c547cSRui Paulo 	struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB];
144*5b9c547cSRui Paulo 	struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT];
145*5b9c547cSRui Paulo 	struct nlattr *nl_limit, *nl_mode;
146*5b9c547cSRui Paulo 	int err, rem_limit, rem_mode;
147*5b9c547cSRui Paulo 	int combination_has_p2p = 0, combination_has_mgd = 0;
148*5b9c547cSRui Paulo 	static struct nla_policy
149*5b9c547cSRui Paulo 	iface_combination_policy[NUM_NL80211_IFACE_COMB] = {
150*5b9c547cSRui Paulo 		[NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED },
151*5b9c547cSRui Paulo 		[NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 },
152*5b9c547cSRui Paulo 		[NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG },
153*5b9c547cSRui Paulo 		[NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 },
154*5b9c547cSRui Paulo 		[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS] = { .type = NLA_U32 },
155*5b9c547cSRui Paulo 	},
156*5b9c547cSRui Paulo 	iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = {
157*5b9c547cSRui Paulo 		[NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED },
158*5b9c547cSRui Paulo 		[NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 },
159*5b9c547cSRui Paulo 	};
160*5b9c547cSRui Paulo 
161*5b9c547cSRui Paulo 	err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB,
162*5b9c547cSRui Paulo 			       nl_combi, iface_combination_policy);
163*5b9c547cSRui Paulo 	if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] ||
164*5b9c547cSRui Paulo 	    !tb_comb[NL80211_IFACE_COMB_MAXNUM] ||
165*5b9c547cSRui Paulo 	    !tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS])
166*5b9c547cSRui Paulo 		return 0; /* broken combination */
167*5b9c547cSRui Paulo 
168*5b9c547cSRui Paulo 	if (tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS])
169*5b9c547cSRui Paulo 		info->capa->flags |= WPA_DRIVER_FLAGS_RADAR;
170*5b9c547cSRui Paulo 
171*5b9c547cSRui Paulo 	nla_for_each_nested(nl_limit, tb_comb[NL80211_IFACE_COMB_LIMITS],
172*5b9c547cSRui Paulo 			    rem_limit) {
173*5b9c547cSRui Paulo 		err = nla_parse_nested(tb_limit, MAX_NL80211_IFACE_LIMIT,
174*5b9c547cSRui Paulo 				       nl_limit, iface_limit_policy);
175*5b9c547cSRui Paulo 		if (err || !tb_limit[NL80211_IFACE_LIMIT_TYPES])
176*5b9c547cSRui Paulo 			return 0; /* broken combination */
177*5b9c547cSRui Paulo 
178*5b9c547cSRui Paulo 		nla_for_each_nested(nl_mode,
179*5b9c547cSRui Paulo 				    tb_limit[NL80211_IFACE_LIMIT_TYPES],
180*5b9c547cSRui Paulo 				    rem_mode) {
181*5b9c547cSRui Paulo 			int ift = nla_type(nl_mode);
182*5b9c547cSRui Paulo 			if (ift == NL80211_IFTYPE_P2P_GO ||
183*5b9c547cSRui Paulo 			    ift == NL80211_IFTYPE_P2P_CLIENT)
184*5b9c547cSRui Paulo 				combination_has_p2p = 1;
185*5b9c547cSRui Paulo 			if (ift == NL80211_IFTYPE_STATION)
186*5b9c547cSRui Paulo 				combination_has_mgd = 1;
187*5b9c547cSRui Paulo 		}
188*5b9c547cSRui Paulo 		if (combination_has_p2p && combination_has_mgd)
189*5b9c547cSRui Paulo 			break;
190*5b9c547cSRui Paulo 	}
191*5b9c547cSRui Paulo 
192*5b9c547cSRui Paulo 	if (combination_has_p2p && combination_has_mgd) {
193*5b9c547cSRui Paulo 		unsigned int num_channels =
194*5b9c547cSRui Paulo 			nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]);
195*5b9c547cSRui Paulo 
196*5b9c547cSRui Paulo 		info->p2p_concurrent = 1;
197*5b9c547cSRui Paulo 		if (info->num_multichan_concurrent < num_channels)
198*5b9c547cSRui Paulo 			info->num_multichan_concurrent = num_channels;
199*5b9c547cSRui Paulo 	}
200*5b9c547cSRui Paulo 
201*5b9c547cSRui Paulo 	return 0;
202*5b9c547cSRui Paulo }
203*5b9c547cSRui Paulo 
204*5b9c547cSRui Paulo 
205*5b9c547cSRui Paulo static void wiphy_info_iface_comb(struct wiphy_info_data *info,
206*5b9c547cSRui Paulo 				  struct nlattr *tb)
207*5b9c547cSRui Paulo {
208*5b9c547cSRui Paulo 	struct nlattr *nl_combi;
209*5b9c547cSRui Paulo 	int rem_combi;
210*5b9c547cSRui Paulo 
211*5b9c547cSRui Paulo 	if (tb == NULL)
212*5b9c547cSRui Paulo 		return;
213*5b9c547cSRui Paulo 
214*5b9c547cSRui Paulo 	nla_for_each_nested(nl_combi, tb, rem_combi) {
215*5b9c547cSRui Paulo 		if (wiphy_info_iface_comb_process(info, nl_combi) > 0)
216*5b9c547cSRui Paulo 			break;
217*5b9c547cSRui Paulo 	}
218*5b9c547cSRui Paulo }
219*5b9c547cSRui Paulo 
220*5b9c547cSRui Paulo 
221*5b9c547cSRui Paulo static void wiphy_info_supp_cmds(struct wiphy_info_data *info,
222*5b9c547cSRui Paulo 				 struct nlattr *tb)
223*5b9c547cSRui Paulo {
224*5b9c547cSRui Paulo 	struct nlattr *nl_cmd;
225*5b9c547cSRui Paulo 	int i;
226*5b9c547cSRui Paulo 
227*5b9c547cSRui Paulo 	if (tb == NULL)
228*5b9c547cSRui Paulo 		return;
229*5b9c547cSRui Paulo 
230*5b9c547cSRui Paulo 	nla_for_each_nested(nl_cmd, tb, i) {
231*5b9c547cSRui Paulo 		switch (nla_get_u32(nl_cmd)) {
232*5b9c547cSRui Paulo 		case NL80211_CMD_AUTHENTICATE:
233*5b9c547cSRui Paulo 			info->auth_supported = 1;
234*5b9c547cSRui Paulo 			break;
235*5b9c547cSRui Paulo 		case NL80211_CMD_CONNECT:
236*5b9c547cSRui Paulo 			info->connect_supported = 1;
237*5b9c547cSRui Paulo 			break;
238*5b9c547cSRui Paulo 		case NL80211_CMD_START_SCHED_SCAN:
239*5b9c547cSRui Paulo 			info->capa->sched_scan_supported = 1;
240*5b9c547cSRui Paulo 			break;
241*5b9c547cSRui Paulo 		case NL80211_CMD_PROBE_CLIENT:
242*5b9c547cSRui Paulo 			info->poll_command_supported = 1;
243*5b9c547cSRui Paulo 			break;
244*5b9c547cSRui Paulo 		case NL80211_CMD_CHANNEL_SWITCH:
245*5b9c547cSRui Paulo 			info->channel_switch_supported = 1;
246*5b9c547cSRui Paulo 			break;
247*5b9c547cSRui Paulo 		case NL80211_CMD_SET_QOS_MAP:
248*5b9c547cSRui Paulo 			info->set_qos_map_supported = 1;
249*5b9c547cSRui Paulo 			break;
250*5b9c547cSRui Paulo 		}
251*5b9c547cSRui Paulo 	}
252*5b9c547cSRui Paulo }
253*5b9c547cSRui Paulo 
254*5b9c547cSRui Paulo 
255*5b9c547cSRui Paulo static void wiphy_info_cipher_suites(struct wiphy_info_data *info,
256*5b9c547cSRui Paulo 				     struct nlattr *tb)
257*5b9c547cSRui Paulo {
258*5b9c547cSRui Paulo 	int i, num;
259*5b9c547cSRui Paulo 	u32 *ciphers;
260*5b9c547cSRui Paulo 
261*5b9c547cSRui Paulo 	if (tb == NULL)
262*5b9c547cSRui Paulo 		return;
263*5b9c547cSRui Paulo 
264*5b9c547cSRui Paulo 	num = nla_len(tb) / sizeof(u32);
265*5b9c547cSRui Paulo 	ciphers = nla_data(tb);
266*5b9c547cSRui Paulo 	for (i = 0; i < num; i++) {
267*5b9c547cSRui Paulo 		u32 c = ciphers[i];
268*5b9c547cSRui Paulo 
269*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "nl80211: Supported cipher %02x-%02x-%02x:%d",
270*5b9c547cSRui Paulo 			   c >> 24, (c >> 16) & 0xff,
271*5b9c547cSRui Paulo 			   (c >> 8) & 0xff, c & 0xff);
272*5b9c547cSRui Paulo 		switch (c) {
273*5b9c547cSRui Paulo 		case WLAN_CIPHER_SUITE_CCMP_256:
274*5b9c547cSRui Paulo 			info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP_256;
275*5b9c547cSRui Paulo 			break;
276*5b9c547cSRui Paulo 		case WLAN_CIPHER_SUITE_GCMP_256:
277*5b9c547cSRui Paulo 			info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP_256;
278*5b9c547cSRui Paulo 			break;
279*5b9c547cSRui Paulo 		case WLAN_CIPHER_SUITE_CCMP:
280*5b9c547cSRui Paulo 			info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP;
281*5b9c547cSRui Paulo 			break;
282*5b9c547cSRui Paulo 		case WLAN_CIPHER_SUITE_GCMP:
283*5b9c547cSRui Paulo 			info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP;
284*5b9c547cSRui Paulo 			break;
285*5b9c547cSRui Paulo 		case WLAN_CIPHER_SUITE_TKIP:
286*5b9c547cSRui Paulo 			info->capa->enc |= WPA_DRIVER_CAPA_ENC_TKIP;
287*5b9c547cSRui Paulo 			break;
288*5b9c547cSRui Paulo 		case WLAN_CIPHER_SUITE_WEP104:
289*5b9c547cSRui Paulo 			info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP104;
290*5b9c547cSRui Paulo 			break;
291*5b9c547cSRui Paulo 		case WLAN_CIPHER_SUITE_WEP40:
292*5b9c547cSRui Paulo 			info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP40;
293*5b9c547cSRui Paulo 			break;
294*5b9c547cSRui Paulo 		case WLAN_CIPHER_SUITE_AES_CMAC:
295*5b9c547cSRui Paulo 			info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP;
296*5b9c547cSRui Paulo 			break;
297*5b9c547cSRui Paulo 		case WLAN_CIPHER_SUITE_BIP_GMAC_128:
298*5b9c547cSRui Paulo 			info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_128;
299*5b9c547cSRui Paulo 			break;
300*5b9c547cSRui Paulo 		case WLAN_CIPHER_SUITE_BIP_GMAC_256:
301*5b9c547cSRui Paulo 			info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_256;
302*5b9c547cSRui Paulo 			break;
303*5b9c547cSRui Paulo 		case WLAN_CIPHER_SUITE_BIP_CMAC_256:
304*5b9c547cSRui Paulo 			info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_CMAC_256;
305*5b9c547cSRui Paulo 			break;
306*5b9c547cSRui Paulo 		case WLAN_CIPHER_SUITE_NO_GROUP_ADDR:
307*5b9c547cSRui Paulo 			info->capa->enc |= WPA_DRIVER_CAPA_ENC_GTK_NOT_USED;
308*5b9c547cSRui Paulo 			break;
309*5b9c547cSRui Paulo 		}
310*5b9c547cSRui Paulo 	}
311*5b9c547cSRui Paulo }
312*5b9c547cSRui Paulo 
313*5b9c547cSRui Paulo 
314*5b9c547cSRui Paulo static void wiphy_info_max_roc(struct wpa_driver_capa *capa,
315*5b9c547cSRui Paulo 			       struct nlattr *tb)
316*5b9c547cSRui Paulo {
317*5b9c547cSRui Paulo 	if (tb)
318*5b9c547cSRui Paulo 		capa->max_remain_on_chan = nla_get_u32(tb);
319*5b9c547cSRui Paulo }
320*5b9c547cSRui Paulo 
321*5b9c547cSRui Paulo 
322*5b9c547cSRui Paulo static void wiphy_info_tdls(struct wpa_driver_capa *capa, struct nlattr *tdls,
323*5b9c547cSRui Paulo 			    struct nlattr *ext_setup)
324*5b9c547cSRui Paulo {
325*5b9c547cSRui Paulo 	if (tdls == NULL)
326*5b9c547cSRui Paulo 		return;
327*5b9c547cSRui Paulo 
328*5b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "nl80211: TDLS supported");
329*5b9c547cSRui Paulo 	capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT;
330*5b9c547cSRui Paulo 
331*5b9c547cSRui Paulo 	if (ext_setup) {
332*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup");
333*5b9c547cSRui Paulo 		capa->flags |= WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP;
334*5b9c547cSRui Paulo 	}
335*5b9c547cSRui Paulo }
336*5b9c547cSRui Paulo 
337*5b9c547cSRui Paulo 
338*5b9c547cSRui Paulo static void wiphy_info_feature_flags(struct wiphy_info_data *info,
339*5b9c547cSRui Paulo 				     struct nlattr *tb)
340*5b9c547cSRui Paulo {
341*5b9c547cSRui Paulo 	u32 flags;
342*5b9c547cSRui Paulo 	struct wpa_driver_capa *capa = info->capa;
343*5b9c547cSRui Paulo 
344*5b9c547cSRui Paulo 	if (tb == NULL)
345*5b9c547cSRui Paulo 		return;
346*5b9c547cSRui Paulo 
347*5b9c547cSRui Paulo 	flags = nla_get_u32(tb);
348*5b9c547cSRui Paulo 
349*5b9c547cSRui Paulo 	if (flags & NL80211_FEATURE_SK_TX_STATUS)
350*5b9c547cSRui Paulo 		info->data_tx_status = 1;
351*5b9c547cSRui Paulo 
352*5b9c547cSRui Paulo 	if (flags & NL80211_FEATURE_INACTIVITY_TIMER)
353*5b9c547cSRui Paulo 		capa->flags |= WPA_DRIVER_FLAGS_INACTIVITY_TIMER;
354*5b9c547cSRui Paulo 
355*5b9c547cSRui Paulo 	if (flags & NL80211_FEATURE_SAE)
356*5b9c547cSRui Paulo 		capa->flags |= WPA_DRIVER_FLAGS_SAE;
357*5b9c547cSRui Paulo 
358*5b9c547cSRui Paulo 	if (flags & NL80211_FEATURE_NEED_OBSS_SCAN)
359*5b9c547cSRui Paulo 		capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN;
360*5b9c547cSRui Paulo 
361*5b9c547cSRui Paulo 	if (flags & NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)
362*5b9c547cSRui Paulo 		capa->flags |= WPA_DRIVER_FLAGS_HT_2040_COEX;
363*5b9c547cSRui Paulo 
364*5b9c547cSRui Paulo 	if (flags & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) {
365*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "nl80211: TDLS channel switch");
366*5b9c547cSRui Paulo 		capa->flags |= WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH;
367*5b9c547cSRui Paulo 	}
368*5b9c547cSRui Paulo 
369*5b9c547cSRui Paulo 	if (flags & NL80211_FEATURE_P2P_GO_CTWIN)
370*5b9c547cSRui Paulo 		info->p2p_go_ctwindow_supported = 1;
371*5b9c547cSRui Paulo 
372*5b9c547cSRui Paulo 	if (flags & NL80211_FEATURE_LOW_PRIORITY_SCAN)
373*5b9c547cSRui Paulo 		info->have_low_prio_scan = 1;
374*5b9c547cSRui Paulo 
375*5b9c547cSRui Paulo 	if (flags & NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR)
376*5b9c547cSRui Paulo 		info->mac_addr_rand_scan_supported = 1;
377*5b9c547cSRui Paulo 
378*5b9c547cSRui Paulo 	if (flags & NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR)
379*5b9c547cSRui Paulo 		info->mac_addr_rand_sched_scan_supported = 1;
380*5b9c547cSRui Paulo 
381*5b9c547cSRui Paulo 	if (flags & NL80211_FEATURE_STATIC_SMPS)
382*5b9c547cSRui Paulo 		capa->smps_modes |= WPA_DRIVER_SMPS_MODE_STATIC;
383*5b9c547cSRui Paulo 
384*5b9c547cSRui Paulo 	if (flags & NL80211_FEATURE_DYNAMIC_SMPS)
385*5b9c547cSRui Paulo 		capa->smps_modes |= WPA_DRIVER_SMPS_MODE_DYNAMIC;
386*5b9c547cSRui Paulo 
387*5b9c547cSRui Paulo 	if (flags & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION)
388*5b9c547cSRui Paulo 		info->wmm_ac_supported = 1;
389*5b9c547cSRui Paulo 
390*5b9c547cSRui Paulo 	if (flags & NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES)
391*5b9c547cSRui Paulo 		capa->rrm_flags |= WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES;
392*5b9c547cSRui Paulo 
393*5b9c547cSRui Paulo 	if (flags & NL80211_FEATURE_WFA_TPC_IE_IN_PROBES)
394*5b9c547cSRui Paulo 		capa->rrm_flags |= WPA_DRIVER_FLAGS_WFA_TPC_IE_IN_PROBES;
395*5b9c547cSRui Paulo 
396*5b9c547cSRui Paulo 	if (flags & NL80211_FEATURE_QUIET)
397*5b9c547cSRui Paulo 		capa->rrm_flags |= WPA_DRIVER_FLAGS_QUIET;
398*5b9c547cSRui Paulo 
399*5b9c547cSRui Paulo 	if (flags & NL80211_FEATURE_TX_POWER_INSERTION)
400*5b9c547cSRui Paulo 		capa->rrm_flags |= WPA_DRIVER_FLAGS_TX_POWER_INSERTION;
401*5b9c547cSRui Paulo 
402*5b9c547cSRui Paulo 	if (flags & NL80211_FEATURE_HT_IBSS)
403*5b9c547cSRui Paulo 		capa->flags |= WPA_DRIVER_FLAGS_HT_IBSS;
404*5b9c547cSRui Paulo }
405*5b9c547cSRui Paulo 
406*5b9c547cSRui Paulo 
407*5b9c547cSRui Paulo static void wiphy_info_probe_resp_offload(struct wpa_driver_capa *capa,
408*5b9c547cSRui Paulo 					  struct nlattr *tb)
409*5b9c547cSRui Paulo {
410*5b9c547cSRui Paulo 	u32 protocols;
411*5b9c547cSRui Paulo 
412*5b9c547cSRui Paulo 	if (tb == NULL)
413*5b9c547cSRui Paulo 		return;
414*5b9c547cSRui Paulo 
415*5b9c547cSRui Paulo 	protocols = nla_get_u32(tb);
416*5b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "nl80211: Supports Probe Response offload in AP "
417*5b9c547cSRui Paulo 		   "mode");
418*5b9c547cSRui Paulo 	capa->flags |= WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD;
419*5b9c547cSRui Paulo 	capa->probe_resp_offloads = probe_resp_offload_support(protocols);
420*5b9c547cSRui Paulo }
421*5b9c547cSRui Paulo 
422*5b9c547cSRui Paulo 
423*5b9c547cSRui Paulo static void wiphy_info_wowlan_triggers(struct wpa_driver_capa *capa,
424*5b9c547cSRui Paulo 				       struct nlattr *tb)
425*5b9c547cSRui Paulo {
426*5b9c547cSRui Paulo 	struct nlattr *triggers[MAX_NL80211_WOWLAN_TRIG + 1];
427*5b9c547cSRui Paulo 
428*5b9c547cSRui Paulo 	if (tb == NULL)
429*5b9c547cSRui Paulo 		return;
430*5b9c547cSRui Paulo 
431*5b9c547cSRui Paulo 	if (nla_parse_nested(triggers, MAX_NL80211_WOWLAN_TRIG,
432*5b9c547cSRui Paulo 			     tb, NULL))
433*5b9c547cSRui Paulo 		return;
434*5b9c547cSRui Paulo 
435*5b9c547cSRui Paulo 	if (triggers[NL80211_WOWLAN_TRIG_ANY])
436*5b9c547cSRui Paulo 		capa->wowlan_triggers.any = 1;
437*5b9c547cSRui Paulo 	if (triggers[NL80211_WOWLAN_TRIG_DISCONNECT])
438*5b9c547cSRui Paulo 		capa->wowlan_triggers.disconnect = 1;
439*5b9c547cSRui Paulo 	if (triggers[NL80211_WOWLAN_TRIG_MAGIC_PKT])
440*5b9c547cSRui Paulo 		capa->wowlan_triggers.magic_pkt = 1;
441*5b9c547cSRui Paulo 	if (triggers[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE])
442*5b9c547cSRui Paulo 		capa->wowlan_triggers.gtk_rekey_failure = 1;
443*5b9c547cSRui Paulo 	if (triggers[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST])
444*5b9c547cSRui Paulo 		capa->wowlan_triggers.eap_identity_req = 1;
445*5b9c547cSRui Paulo 	if (triggers[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE])
446*5b9c547cSRui Paulo 		capa->wowlan_triggers.four_way_handshake = 1;
447*5b9c547cSRui Paulo 	if (triggers[NL80211_WOWLAN_TRIG_RFKILL_RELEASE])
448*5b9c547cSRui Paulo 		capa->wowlan_triggers.rfkill_release = 1;
449*5b9c547cSRui Paulo }
450*5b9c547cSRui Paulo 
451*5b9c547cSRui Paulo 
452*5b9c547cSRui Paulo static int wiphy_info_handler(struct nl_msg *msg, void *arg)
453*5b9c547cSRui Paulo {
454*5b9c547cSRui Paulo 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
455*5b9c547cSRui Paulo 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
456*5b9c547cSRui Paulo 	struct wiphy_info_data *info = arg;
457*5b9c547cSRui Paulo 	struct wpa_driver_capa *capa = info->capa;
458*5b9c547cSRui Paulo 	struct wpa_driver_nl80211_data *drv = info->drv;
459*5b9c547cSRui Paulo 
460*5b9c547cSRui Paulo 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
461*5b9c547cSRui Paulo 		  genlmsg_attrlen(gnlh, 0), NULL);
462*5b9c547cSRui Paulo 
463*5b9c547cSRui Paulo 	if (tb[NL80211_ATTR_WIPHY_NAME])
464*5b9c547cSRui Paulo 		os_strlcpy(drv->phyname,
465*5b9c547cSRui Paulo 			   nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]),
466*5b9c547cSRui Paulo 			   sizeof(drv->phyname));
467*5b9c547cSRui Paulo 	if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS])
468*5b9c547cSRui Paulo 		capa->max_scan_ssids =
469*5b9c547cSRui Paulo 			nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]);
470*5b9c547cSRui Paulo 
471*5b9c547cSRui Paulo 	if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS])
472*5b9c547cSRui Paulo 		capa->max_sched_scan_ssids =
473*5b9c547cSRui Paulo 			nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]);
474*5b9c547cSRui Paulo 
475*5b9c547cSRui Paulo 	if (tb[NL80211_ATTR_MAX_MATCH_SETS])
476*5b9c547cSRui Paulo 		capa->max_match_sets =
477*5b9c547cSRui Paulo 			nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
478*5b9c547cSRui Paulo 
479*5b9c547cSRui Paulo 	if (tb[NL80211_ATTR_MAC_ACL_MAX])
480*5b9c547cSRui Paulo 		capa->max_acl_mac_addrs =
481*5b9c547cSRui Paulo 			nla_get_u8(tb[NL80211_ATTR_MAC_ACL_MAX]);
482*5b9c547cSRui Paulo 
483*5b9c547cSRui Paulo 	wiphy_info_supported_iftypes(info, tb[NL80211_ATTR_SUPPORTED_IFTYPES]);
484*5b9c547cSRui Paulo 	wiphy_info_iface_comb(info, tb[NL80211_ATTR_INTERFACE_COMBINATIONS]);
485*5b9c547cSRui Paulo 	wiphy_info_supp_cmds(info, tb[NL80211_ATTR_SUPPORTED_COMMANDS]);
486*5b9c547cSRui Paulo 	wiphy_info_cipher_suites(info, tb[NL80211_ATTR_CIPHER_SUITES]);
487*5b9c547cSRui Paulo 
488*5b9c547cSRui Paulo 	if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) {
489*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "nl80211: Using driver-based "
490*5b9c547cSRui Paulo 			   "off-channel TX");
491*5b9c547cSRui Paulo 		capa->flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX;
492*5b9c547cSRui Paulo 	}
493*5b9c547cSRui Paulo 
494*5b9c547cSRui Paulo 	if (tb[NL80211_ATTR_ROAM_SUPPORT]) {
495*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "nl80211: Using driver-based roaming");
496*5b9c547cSRui Paulo 		capa->flags |= WPA_DRIVER_FLAGS_BSS_SELECTION;
497*5b9c547cSRui Paulo 	}
498*5b9c547cSRui Paulo 
499*5b9c547cSRui Paulo 	wiphy_info_max_roc(capa,
500*5b9c547cSRui Paulo 			   tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]);
501*5b9c547cSRui Paulo 
502*5b9c547cSRui Paulo 	if (tb[NL80211_ATTR_SUPPORT_AP_UAPSD])
503*5b9c547cSRui Paulo 		capa->flags |= WPA_DRIVER_FLAGS_AP_UAPSD;
504*5b9c547cSRui Paulo 
505*5b9c547cSRui Paulo 	wiphy_info_tdls(capa, tb[NL80211_ATTR_TDLS_SUPPORT],
506*5b9c547cSRui Paulo 			tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]);
507*5b9c547cSRui Paulo 
508*5b9c547cSRui Paulo 	if (tb[NL80211_ATTR_DEVICE_AP_SME])
509*5b9c547cSRui Paulo 		info->device_ap_sme = 1;
510*5b9c547cSRui Paulo 
511*5b9c547cSRui Paulo 	wiphy_info_feature_flags(info, tb[NL80211_ATTR_FEATURE_FLAGS]);
512*5b9c547cSRui Paulo 	wiphy_info_probe_resp_offload(capa,
513*5b9c547cSRui Paulo 				      tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]);
514*5b9c547cSRui Paulo 
515*5b9c547cSRui Paulo 	if (tb[NL80211_ATTR_EXT_CAPA] && tb[NL80211_ATTR_EXT_CAPA_MASK] &&
516*5b9c547cSRui Paulo 	    drv->extended_capa == NULL) {
517*5b9c547cSRui Paulo 		drv->extended_capa =
518*5b9c547cSRui Paulo 			os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA]));
519*5b9c547cSRui Paulo 		if (drv->extended_capa) {
520*5b9c547cSRui Paulo 			os_memcpy(drv->extended_capa,
521*5b9c547cSRui Paulo 				  nla_data(tb[NL80211_ATTR_EXT_CAPA]),
522*5b9c547cSRui Paulo 				  nla_len(tb[NL80211_ATTR_EXT_CAPA]));
523*5b9c547cSRui Paulo 			drv->extended_capa_len =
524*5b9c547cSRui Paulo 				nla_len(tb[NL80211_ATTR_EXT_CAPA]);
525*5b9c547cSRui Paulo 		}
526*5b9c547cSRui Paulo 		drv->extended_capa_mask =
527*5b9c547cSRui Paulo 			os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK]));
528*5b9c547cSRui Paulo 		if (drv->extended_capa_mask) {
529*5b9c547cSRui Paulo 			os_memcpy(drv->extended_capa_mask,
530*5b9c547cSRui Paulo 				  nla_data(tb[NL80211_ATTR_EXT_CAPA_MASK]),
531*5b9c547cSRui Paulo 				  nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK]));
532*5b9c547cSRui Paulo 		} else {
533*5b9c547cSRui Paulo 			os_free(drv->extended_capa);
534*5b9c547cSRui Paulo 			drv->extended_capa = NULL;
535*5b9c547cSRui Paulo 			drv->extended_capa_len = 0;
536*5b9c547cSRui Paulo 		}
537*5b9c547cSRui Paulo 	}
538*5b9c547cSRui Paulo 
539*5b9c547cSRui Paulo 	if (tb[NL80211_ATTR_VENDOR_DATA]) {
540*5b9c547cSRui Paulo 		struct nlattr *nl;
541*5b9c547cSRui Paulo 		int rem;
542*5b9c547cSRui Paulo 
543*5b9c547cSRui Paulo 		nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_DATA], rem) {
544*5b9c547cSRui Paulo 			struct nl80211_vendor_cmd_info *vinfo;
545*5b9c547cSRui Paulo 			if (nla_len(nl) != sizeof(*vinfo)) {
546*5b9c547cSRui Paulo 				wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info");
547*5b9c547cSRui Paulo 				continue;
548*5b9c547cSRui Paulo 			}
549*5b9c547cSRui Paulo 			vinfo = nla_data(nl);
550*5b9c547cSRui Paulo 			switch (vinfo->subcmd) {
551*5b9c547cSRui Paulo 			case QCA_NL80211_VENDOR_SUBCMD_TEST:
552*5b9c547cSRui Paulo 				drv->vendor_cmd_test_avail = 1;
553*5b9c547cSRui Paulo 				break;
554*5b9c547cSRui Paulo 			case QCA_NL80211_VENDOR_SUBCMD_ROAMING:
555*5b9c547cSRui Paulo 				drv->roaming_vendor_cmd_avail = 1;
556*5b9c547cSRui Paulo 				break;
557*5b9c547cSRui Paulo 			case QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY:
558*5b9c547cSRui Paulo 				drv->dfs_vendor_cmd_avail = 1;
559*5b9c547cSRui Paulo 				break;
560*5b9c547cSRui Paulo 			case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES:
561*5b9c547cSRui Paulo 				drv->get_features_vendor_cmd_avail = 1;
562*5b9c547cSRui Paulo 				break;
563*5b9c547cSRui Paulo 			case QCA_NL80211_VENDOR_SUBCMD_DO_ACS:
564*5b9c547cSRui Paulo 				drv->capa.flags |= WPA_DRIVER_FLAGS_ACS_OFFLOAD;
565*5b9c547cSRui Paulo 				break;
566*5b9c547cSRui Paulo 			}
567*5b9c547cSRui Paulo 
568*5b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
569*5b9c547cSRui Paulo 				   vinfo->vendor_id, vinfo->subcmd);
570*5b9c547cSRui Paulo 		}
571*5b9c547cSRui Paulo 	}
572*5b9c547cSRui Paulo 
573*5b9c547cSRui Paulo 	if (tb[NL80211_ATTR_VENDOR_EVENTS]) {
574*5b9c547cSRui Paulo 		struct nlattr *nl;
575*5b9c547cSRui Paulo 		int rem;
576*5b9c547cSRui Paulo 
577*5b9c547cSRui Paulo 		nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_EVENTS], rem) {
578*5b9c547cSRui Paulo 			struct nl80211_vendor_cmd_info *vinfo;
579*5b9c547cSRui Paulo 			if (nla_len(nl) != sizeof(*vinfo)) {
580*5b9c547cSRui Paulo 				wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info");
581*5b9c547cSRui Paulo 				continue;
582*5b9c547cSRui Paulo 			}
583*5b9c547cSRui Paulo 			vinfo = nla_data(nl);
584*5b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "nl80211: Supported vendor event: vendor_id=0x%x subcmd=%u",
585*5b9c547cSRui Paulo 				   vinfo->vendor_id, vinfo->subcmd);
586*5b9c547cSRui Paulo 		}
587*5b9c547cSRui Paulo 	}
588*5b9c547cSRui Paulo 
589*5b9c547cSRui Paulo 	wiphy_info_wowlan_triggers(capa,
590*5b9c547cSRui Paulo 				   tb[NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED]);
591*5b9c547cSRui Paulo 
592*5b9c547cSRui Paulo 	if (tb[NL80211_ATTR_MAX_AP_ASSOC_STA])
593*5b9c547cSRui Paulo 		capa->max_stations =
594*5b9c547cSRui Paulo 			nla_get_u32(tb[NL80211_ATTR_MAX_AP_ASSOC_STA]);
595*5b9c547cSRui Paulo 
596*5b9c547cSRui Paulo 	return NL_SKIP;
597*5b9c547cSRui Paulo }
598*5b9c547cSRui Paulo 
599*5b9c547cSRui Paulo 
600*5b9c547cSRui Paulo static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
601*5b9c547cSRui Paulo 				       struct wiphy_info_data *info)
602*5b9c547cSRui Paulo {
603*5b9c547cSRui Paulo 	u32 feat;
604*5b9c547cSRui Paulo 	struct nl_msg *msg;
605*5b9c547cSRui Paulo 	int flags = 0;
606*5b9c547cSRui Paulo 
607*5b9c547cSRui Paulo 	os_memset(info, 0, sizeof(*info));
608*5b9c547cSRui Paulo 	info->capa = &drv->capa;
609*5b9c547cSRui Paulo 	info->drv = drv;
610*5b9c547cSRui Paulo 
611*5b9c547cSRui Paulo 	feat = get_nl80211_protocol_features(drv);
612*5b9c547cSRui Paulo 	if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
613*5b9c547cSRui Paulo 		flags = NLM_F_DUMP;
614*5b9c547cSRui Paulo 	msg = nl80211_cmd_msg(drv->first_bss, flags, NL80211_CMD_GET_WIPHY);
615*5b9c547cSRui Paulo 	if (!msg || nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) {
616*5b9c547cSRui Paulo 		nlmsg_free(msg);
617*5b9c547cSRui Paulo 		return -1;
618*5b9c547cSRui Paulo 	}
619*5b9c547cSRui Paulo 
620*5b9c547cSRui Paulo 	if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info))
621*5b9c547cSRui Paulo 		return -1;
622*5b9c547cSRui Paulo 
623*5b9c547cSRui Paulo 	if (info->auth_supported)
624*5b9c547cSRui Paulo 		drv->capa.flags |= WPA_DRIVER_FLAGS_SME;
625*5b9c547cSRui Paulo 	else if (!info->connect_supported) {
626*5b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "nl80211: Driver does not support "
627*5b9c547cSRui Paulo 			   "authentication/association or connect commands");
628*5b9c547cSRui Paulo 		info->error = 1;
629*5b9c547cSRui Paulo 	}
630*5b9c547cSRui Paulo 
631*5b9c547cSRui Paulo 	if (info->p2p_go_supported && info->p2p_client_supported)
632*5b9c547cSRui Paulo 		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
633*5b9c547cSRui Paulo 	if (info->p2p_concurrent) {
634*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
635*5b9c547cSRui Paulo 			   "interface (driver advertised support)");
636*5b9c547cSRui Paulo 		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
637*5b9c547cSRui Paulo 		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
638*5b9c547cSRui Paulo 	}
639*5b9c547cSRui Paulo 	if (info->num_multichan_concurrent > 1) {
640*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "nl80211: Enable multi-channel "
641*5b9c547cSRui Paulo 			   "concurrent (driver advertised support)");
642*5b9c547cSRui Paulo 		drv->capa.num_multichan_concurrent =
643*5b9c547cSRui Paulo 			info->num_multichan_concurrent;
644*5b9c547cSRui Paulo 	}
645*5b9c547cSRui Paulo 	if (drv->capa.flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)
646*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "nl80211: use P2P_DEVICE support");
647*5b9c547cSRui Paulo 
648*5b9c547cSRui Paulo 	/* default to 5000 since early versions of mac80211 don't set it */
649*5b9c547cSRui Paulo 	if (!drv->capa.max_remain_on_chan)
650*5b9c547cSRui Paulo 		drv->capa.max_remain_on_chan = 5000;
651*5b9c547cSRui Paulo 
652*5b9c547cSRui Paulo 	if (info->channel_switch_supported)
653*5b9c547cSRui Paulo 		drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA;
654*5b9c547cSRui Paulo 	drv->capa.wmm_ac_supported = info->wmm_ac_supported;
655*5b9c547cSRui Paulo 
656*5b9c547cSRui Paulo 	drv->capa.mac_addr_rand_sched_scan_supported =
657*5b9c547cSRui Paulo 		info->mac_addr_rand_sched_scan_supported;
658*5b9c547cSRui Paulo 	drv->capa.mac_addr_rand_scan_supported =
659*5b9c547cSRui Paulo 		info->mac_addr_rand_scan_supported;
660*5b9c547cSRui Paulo 
661*5b9c547cSRui Paulo 	return 0;
662*5b9c547cSRui Paulo }
663*5b9c547cSRui Paulo 
664*5b9c547cSRui Paulo 
665*5b9c547cSRui Paulo static int dfs_info_handler(struct nl_msg *msg, void *arg)
666*5b9c547cSRui Paulo {
667*5b9c547cSRui Paulo 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
668*5b9c547cSRui Paulo 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
669*5b9c547cSRui Paulo 	int *dfs_capability_ptr = arg;
670*5b9c547cSRui Paulo 
671*5b9c547cSRui Paulo 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
672*5b9c547cSRui Paulo 		  genlmsg_attrlen(gnlh, 0), NULL);
673*5b9c547cSRui Paulo 
674*5b9c547cSRui Paulo 	if (tb[NL80211_ATTR_VENDOR_DATA]) {
675*5b9c547cSRui Paulo 		struct nlattr *nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
676*5b9c547cSRui Paulo 		struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
677*5b9c547cSRui Paulo 
678*5b9c547cSRui Paulo 		nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
679*5b9c547cSRui Paulo 			  nla_data(nl_vend), nla_len(nl_vend), NULL);
680*5b9c547cSRui Paulo 
681*5b9c547cSRui Paulo 		if (tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]) {
682*5b9c547cSRui Paulo 			u32 val;
683*5b9c547cSRui Paulo 			val = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]);
684*5b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "nl80211: DFS offload capability: %u",
685*5b9c547cSRui Paulo 				   val);
686*5b9c547cSRui Paulo 			*dfs_capability_ptr = val;
687*5b9c547cSRui Paulo 		}
688*5b9c547cSRui Paulo 	}
689*5b9c547cSRui Paulo 
690*5b9c547cSRui Paulo 	return NL_SKIP;
691*5b9c547cSRui Paulo }
692*5b9c547cSRui Paulo 
693*5b9c547cSRui Paulo 
694*5b9c547cSRui Paulo static void qca_nl80211_check_dfs_capa(struct wpa_driver_nl80211_data *drv)
695*5b9c547cSRui Paulo {
696*5b9c547cSRui Paulo 	struct nl_msg *msg;
697*5b9c547cSRui Paulo 	int dfs_capability = 0;
698*5b9c547cSRui Paulo 	int ret;
699*5b9c547cSRui Paulo 
700*5b9c547cSRui Paulo 	if (!drv->dfs_vendor_cmd_avail)
701*5b9c547cSRui Paulo 		return;
702*5b9c547cSRui Paulo 
703*5b9c547cSRui Paulo 	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
704*5b9c547cSRui Paulo 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
705*5b9c547cSRui Paulo 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
706*5b9c547cSRui Paulo 			QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY)) {
707*5b9c547cSRui Paulo 		nlmsg_free(msg);
708*5b9c547cSRui Paulo 		return;
709*5b9c547cSRui Paulo 	}
710*5b9c547cSRui Paulo 
711*5b9c547cSRui Paulo 	ret = send_and_recv_msgs(drv, msg, dfs_info_handler, &dfs_capability);
712*5b9c547cSRui Paulo 	if (!ret && dfs_capability)
713*5b9c547cSRui Paulo 		drv->capa.flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD;
714*5b9c547cSRui Paulo }
715*5b9c547cSRui Paulo 
716*5b9c547cSRui Paulo 
717*5b9c547cSRui Paulo struct features_info {
718*5b9c547cSRui Paulo 	u8 *flags;
719*5b9c547cSRui Paulo 	size_t flags_len;
720*5b9c547cSRui Paulo };
721*5b9c547cSRui Paulo 
722*5b9c547cSRui Paulo 
723*5b9c547cSRui Paulo static int features_info_handler(struct nl_msg *msg, void *arg)
724*5b9c547cSRui Paulo {
725*5b9c547cSRui Paulo 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
726*5b9c547cSRui Paulo 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
727*5b9c547cSRui Paulo 	struct features_info *info = arg;
728*5b9c547cSRui Paulo 	struct nlattr *nl_vend, *attr;
729*5b9c547cSRui Paulo 
730*5b9c547cSRui Paulo 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
731*5b9c547cSRui Paulo 		  genlmsg_attrlen(gnlh, 0), NULL);
732*5b9c547cSRui Paulo 
733*5b9c547cSRui Paulo 	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
734*5b9c547cSRui Paulo 	if (nl_vend) {
735*5b9c547cSRui Paulo 		struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
736*5b9c547cSRui Paulo 
737*5b9c547cSRui Paulo 		nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
738*5b9c547cSRui Paulo 			  nla_data(nl_vend), nla_len(nl_vend), NULL);
739*5b9c547cSRui Paulo 
740*5b9c547cSRui Paulo 		attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS];
741*5b9c547cSRui Paulo 		if (attr) {
742*5b9c547cSRui Paulo 			info->flags = nla_data(attr);
743*5b9c547cSRui Paulo 			info->flags_len = nla_len(attr);
744*5b9c547cSRui Paulo 		}
745*5b9c547cSRui Paulo 	}
746*5b9c547cSRui Paulo 
747*5b9c547cSRui Paulo 	return NL_SKIP;
748*5b9c547cSRui Paulo }
749*5b9c547cSRui Paulo 
750*5b9c547cSRui Paulo 
751*5b9c547cSRui Paulo static int check_feature(enum qca_wlan_vendor_features feature,
752*5b9c547cSRui Paulo 			 struct features_info *info)
753*5b9c547cSRui Paulo {
754*5b9c547cSRui Paulo 	size_t idx = feature / 8;
755*5b9c547cSRui Paulo 
756*5b9c547cSRui Paulo 	return (idx < info->flags_len) &&
757*5b9c547cSRui Paulo 		(info->flags[idx] & BIT(feature % 8));
758*5b9c547cSRui Paulo }
759*5b9c547cSRui Paulo 
760*5b9c547cSRui Paulo 
761*5b9c547cSRui Paulo static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv)
762*5b9c547cSRui Paulo {
763*5b9c547cSRui Paulo 	struct nl_msg *msg;
764*5b9c547cSRui Paulo 	struct features_info info;
765*5b9c547cSRui Paulo 	int ret;
766*5b9c547cSRui Paulo 
767*5b9c547cSRui Paulo 	if (!drv->get_features_vendor_cmd_avail)
768*5b9c547cSRui Paulo 		return;
769*5b9c547cSRui Paulo 
770*5b9c547cSRui Paulo 	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
771*5b9c547cSRui Paulo 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
772*5b9c547cSRui Paulo 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
773*5b9c547cSRui Paulo 			QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES)) {
774*5b9c547cSRui Paulo 		nlmsg_free(msg);
775*5b9c547cSRui Paulo 		return;
776*5b9c547cSRui Paulo 	}
777*5b9c547cSRui Paulo 
778*5b9c547cSRui Paulo 	os_memset(&info, 0, sizeof(info));
779*5b9c547cSRui Paulo 	ret = send_and_recv_msgs(drv, msg, features_info_handler, &info);
780*5b9c547cSRui Paulo 	if (ret || !info.flags)
781*5b9c547cSRui Paulo 		return;
782*5b9c547cSRui Paulo 
783*5b9c547cSRui Paulo 	if (check_feature(QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD, &info))
784*5b9c547cSRui Paulo 		drv->capa.flags |= WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD;
785*5b9c547cSRui Paulo }
786*5b9c547cSRui Paulo 
787*5b9c547cSRui Paulo 
788*5b9c547cSRui Paulo int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
789*5b9c547cSRui Paulo {
790*5b9c547cSRui Paulo 	struct wiphy_info_data info;
791*5b9c547cSRui Paulo 	if (wpa_driver_nl80211_get_info(drv, &info))
792*5b9c547cSRui Paulo 		return -1;
793*5b9c547cSRui Paulo 
794*5b9c547cSRui Paulo 	if (info.error)
795*5b9c547cSRui Paulo 		return -1;
796*5b9c547cSRui Paulo 
797*5b9c547cSRui Paulo 	drv->has_capability = 1;
798*5b9c547cSRui Paulo 	drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
799*5b9c547cSRui Paulo 		WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
800*5b9c547cSRui Paulo 		WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
801*5b9c547cSRui Paulo 		WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK |
802*5b9c547cSRui Paulo 		WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B |
803*5b9c547cSRui Paulo 		WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
804*5b9c547cSRui Paulo 	drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
805*5b9c547cSRui Paulo 		WPA_DRIVER_AUTH_SHARED |
806*5b9c547cSRui Paulo 		WPA_DRIVER_AUTH_LEAP;
807*5b9c547cSRui Paulo 
808*5b9c547cSRui Paulo 	drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES;
809*5b9c547cSRui Paulo 	drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE;
810*5b9c547cSRui Paulo 	drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
811*5b9c547cSRui Paulo 
812*5b9c547cSRui Paulo 	/*
813*5b9c547cSRui Paulo 	 * As all cfg80211 drivers must support cases where the AP interface is
814*5b9c547cSRui Paulo 	 * removed without the knowledge of wpa_supplicant/hostapd, e.g., in
815*5b9c547cSRui Paulo 	 * case that the user space daemon has crashed, they must be able to
816*5b9c547cSRui Paulo 	 * cleanup all stations and key entries in the AP tear down flow. Thus,
817*5b9c547cSRui Paulo 	 * this flag can/should always be set for cfg80211 drivers.
818*5b9c547cSRui Paulo 	 */
819*5b9c547cSRui Paulo 	drv->capa.flags |= WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT;
820*5b9c547cSRui Paulo 
821*5b9c547cSRui Paulo 	if (!info.device_ap_sme) {
822*5b9c547cSRui Paulo 		drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS;
823*5b9c547cSRui Paulo 
824*5b9c547cSRui Paulo 		/*
825*5b9c547cSRui Paulo 		 * No AP SME is currently assumed to also indicate no AP MLME
826*5b9c547cSRui Paulo 		 * in the driver/firmware.
827*5b9c547cSRui Paulo 		 */
828*5b9c547cSRui Paulo 		drv->capa.flags |= WPA_DRIVER_FLAGS_AP_MLME;
829*5b9c547cSRui Paulo 	}
830*5b9c547cSRui Paulo 
831*5b9c547cSRui Paulo 	drv->device_ap_sme = info.device_ap_sme;
832*5b9c547cSRui Paulo 	drv->poll_command_supported = info.poll_command_supported;
833*5b9c547cSRui Paulo 	drv->data_tx_status = info.data_tx_status;
834*5b9c547cSRui Paulo 	drv->p2p_go_ctwindow_supported = info.p2p_go_ctwindow_supported;
835*5b9c547cSRui Paulo 	if (info.set_qos_map_supported)
836*5b9c547cSRui Paulo 		drv->capa.flags |= WPA_DRIVER_FLAGS_QOS_MAPPING;
837*5b9c547cSRui Paulo 	drv->have_low_prio_scan = info.have_low_prio_scan;
838*5b9c547cSRui Paulo 
839*5b9c547cSRui Paulo 	/*
840*5b9c547cSRui Paulo 	 * If poll command and tx status are supported, mac80211 is new enough
841*5b9c547cSRui Paulo 	 * to have everything we need to not need monitor interfaces.
842*5b9c547cSRui Paulo 	 */
843*5b9c547cSRui Paulo 	drv->use_monitor = !info.poll_command_supported || !info.data_tx_status;
844*5b9c547cSRui Paulo 
845*5b9c547cSRui Paulo 	if (drv->device_ap_sme && drv->use_monitor) {
846*5b9c547cSRui Paulo 		/*
847*5b9c547cSRui Paulo 		 * Non-mac80211 drivers may not support monitor interface.
848*5b9c547cSRui Paulo 		 * Make sure we do not get stuck with incorrect capability here
849*5b9c547cSRui Paulo 		 * by explicitly testing this.
850*5b9c547cSRui Paulo 		 */
851*5b9c547cSRui Paulo 		if (!info.monitor_supported) {
852*5b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "nl80211: Disable use_monitor "
853*5b9c547cSRui Paulo 				   "with device_ap_sme since no monitor mode "
854*5b9c547cSRui Paulo 				   "support detected");
855*5b9c547cSRui Paulo 			drv->use_monitor = 0;
856*5b9c547cSRui Paulo 		}
857*5b9c547cSRui Paulo 	}
858*5b9c547cSRui Paulo 
859*5b9c547cSRui Paulo 	/*
860*5b9c547cSRui Paulo 	 * If we aren't going to use monitor interfaces, but the
861*5b9c547cSRui Paulo 	 * driver doesn't support data TX status, we won't get TX
862*5b9c547cSRui Paulo 	 * status for EAPOL frames.
863*5b9c547cSRui Paulo 	 */
864*5b9c547cSRui Paulo 	if (!drv->use_monitor && !info.data_tx_status)
865*5b9c547cSRui Paulo 		drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
866*5b9c547cSRui Paulo 
867*5b9c547cSRui Paulo 	qca_nl80211_check_dfs_capa(drv);
868*5b9c547cSRui Paulo 	qca_nl80211_get_features(drv);
869*5b9c547cSRui Paulo 
870*5b9c547cSRui Paulo 	return 0;
871*5b9c547cSRui Paulo }
872*5b9c547cSRui Paulo 
873*5b9c547cSRui Paulo 
874*5b9c547cSRui Paulo struct phy_info_arg {
875*5b9c547cSRui Paulo 	u16 *num_modes;
876*5b9c547cSRui Paulo 	struct hostapd_hw_modes *modes;
877*5b9c547cSRui Paulo 	int last_mode, last_chan_idx;
878*5b9c547cSRui Paulo };
879*5b9c547cSRui Paulo 
880*5b9c547cSRui Paulo static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa,
881*5b9c547cSRui Paulo 			     struct nlattr *ampdu_factor,
882*5b9c547cSRui Paulo 			     struct nlattr *ampdu_density,
883*5b9c547cSRui Paulo 			     struct nlattr *mcs_set)
884*5b9c547cSRui Paulo {
885*5b9c547cSRui Paulo 	if (capa)
886*5b9c547cSRui Paulo 		mode->ht_capab = nla_get_u16(capa);
887*5b9c547cSRui Paulo 
888*5b9c547cSRui Paulo 	if (ampdu_factor)
889*5b9c547cSRui Paulo 		mode->a_mpdu_params |= nla_get_u8(ampdu_factor) & 0x03;
890*5b9c547cSRui Paulo 
891*5b9c547cSRui Paulo 	if (ampdu_density)
892*5b9c547cSRui Paulo 		mode->a_mpdu_params |= nla_get_u8(ampdu_density) << 2;
893*5b9c547cSRui Paulo 
894*5b9c547cSRui Paulo 	if (mcs_set && nla_len(mcs_set) >= 16) {
895*5b9c547cSRui Paulo 		u8 *mcs;
896*5b9c547cSRui Paulo 		mcs = nla_data(mcs_set);
897*5b9c547cSRui Paulo 		os_memcpy(mode->mcs_set, mcs, 16);
898*5b9c547cSRui Paulo 	}
899*5b9c547cSRui Paulo }
900*5b9c547cSRui Paulo 
901*5b9c547cSRui Paulo 
902*5b9c547cSRui Paulo static void phy_info_vht_capa(struct hostapd_hw_modes *mode,
903*5b9c547cSRui Paulo 			      struct nlattr *capa,
904*5b9c547cSRui Paulo 			      struct nlattr *mcs_set)
905*5b9c547cSRui Paulo {
906*5b9c547cSRui Paulo 	if (capa)
907*5b9c547cSRui Paulo 		mode->vht_capab = nla_get_u32(capa);
908*5b9c547cSRui Paulo 
909*5b9c547cSRui Paulo 	if (mcs_set && nla_len(mcs_set) >= 8) {
910*5b9c547cSRui Paulo 		u8 *mcs;
911*5b9c547cSRui Paulo 		mcs = nla_data(mcs_set);
912*5b9c547cSRui Paulo 		os_memcpy(mode->vht_mcs_set, mcs, 8);
913*5b9c547cSRui Paulo 	}
914*5b9c547cSRui Paulo }
915*5b9c547cSRui Paulo 
916*5b9c547cSRui Paulo 
917*5b9c547cSRui Paulo static void phy_info_freq(struct hostapd_hw_modes *mode,
918*5b9c547cSRui Paulo 			  struct hostapd_channel_data *chan,
919*5b9c547cSRui Paulo 			  struct nlattr *tb_freq[])
920*5b9c547cSRui Paulo {
921*5b9c547cSRui Paulo 	u8 channel;
922*5b9c547cSRui Paulo 	chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
923*5b9c547cSRui Paulo 	chan->flag = 0;
924*5b9c547cSRui Paulo 	chan->dfs_cac_ms = 0;
925*5b9c547cSRui Paulo 	if (ieee80211_freq_to_chan(chan->freq, &channel) != NUM_HOSTAPD_MODES)
926*5b9c547cSRui Paulo 		chan->chan = channel;
927*5b9c547cSRui Paulo 
928*5b9c547cSRui Paulo 	if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
929*5b9c547cSRui Paulo 		chan->flag |= HOSTAPD_CHAN_DISABLED;
930*5b9c547cSRui Paulo 	if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR])
931*5b9c547cSRui Paulo 		chan->flag |= HOSTAPD_CHAN_NO_IR;
932*5b9c547cSRui Paulo 	if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
933*5b9c547cSRui Paulo 		chan->flag |= HOSTAPD_CHAN_RADAR;
934*5b9c547cSRui Paulo 	if (tb_freq[NL80211_FREQUENCY_ATTR_INDOOR_ONLY])
935*5b9c547cSRui Paulo 		chan->flag |= HOSTAPD_CHAN_INDOOR_ONLY;
936*5b9c547cSRui Paulo 	if (tb_freq[NL80211_FREQUENCY_ATTR_GO_CONCURRENT])
937*5b9c547cSRui Paulo 		chan->flag |= HOSTAPD_CHAN_GO_CONCURRENT;
938*5b9c547cSRui Paulo 
939*5b9c547cSRui Paulo 	if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) {
940*5b9c547cSRui Paulo 		enum nl80211_dfs_state state =
941*5b9c547cSRui Paulo 			nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]);
942*5b9c547cSRui Paulo 
943*5b9c547cSRui Paulo 		switch (state) {
944*5b9c547cSRui Paulo 		case NL80211_DFS_USABLE:
945*5b9c547cSRui Paulo 			chan->flag |= HOSTAPD_CHAN_DFS_USABLE;
946*5b9c547cSRui Paulo 			break;
947*5b9c547cSRui Paulo 		case NL80211_DFS_AVAILABLE:
948*5b9c547cSRui Paulo 			chan->flag |= HOSTAPD_CHAN_DFS_AVAILABLE;
949*5b9c547cSRui Paulo 			break;
950*5b9c547cSRui Paulo 		case NL80211_DFS_UNAVAILABLE:
951*5b9c547cSRui Paulo 			chan->flag |= HOSTAPD_CHAN_DFS_UNAVAILABLE;
952*5b9c547cSRui Paulo 			break;
953*5b9c547cSRui Paulo 		}
954*5b9c547cSRui Paulo 	}
955*5b9c547cSRui Paulo 
956*5b9c547cSRui Paulo 	if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]) {
957*5b9c547cSRui Paulo 		chan->dfs_cac_ms = nla_get_u32(
958*5b9c547cSRui Paulo 			tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]);
959*5b9c547cSRui Paulo 	}
960*5b9c547cSRui Paulo }
961*5b9c547cSRui Paulo 
962*5b9c547cSRui Paulo 
963*5b9c547cSRui Paulo static int phy_info_freqs(struct phy_info_arg *phy_info,
964*5b9c547cSRui Paulo 			  struct hostapd_hw_modes *mode, struct nlattr *tb)
965*5b9c547cSRui Paulo {
966*5b9c547cSRui Paulo 	static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
967*5b9c547cSRui Paulo 		[NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
968*5b9c547cSRui Paulo 		[NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
969*5b9c547cSRui Paulo 		[NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG },
970*5b9c547cSRui Paulo 		[NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
971*5b9c547cSRui Paulo 		[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
972*5b9c547cSRui Paulo 		[NL80211_FREQUENCY_ATTR_DFS_STATE] = { .type = NLA_U32 },
973*5b9c547cSRui Paulo 	};
974*5b9c547cSRui Paulo 	int new_channels = 0;
975*5b9c547cSRui Paulo 	struct hostapd_channel_data *channel;
976*5b9c547cSRui Paulo 	struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
977*5b9c547cSRui Paulo 	struct nlattr *nl_freq;
978*5b9c547cSRui Paulo 	int rem_freq, idx;
979*5b9c547cSRui Paulo 
980*5b9c547cSRui Paulo 	if (tb == NULL)
981*5b9c547cSRui Paulo 		return NL_OK;
982*5b9c547cSRui Paulo 
983*5b9c547cSRui Paulo 	nla_for_each_nested(nl_freq, tb, rem_freq) {
984*5b9c547cSRui Paulo 		nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX,
985*5b9c547cSRui Paulo 			  nla_data(nl_freq), nla_len(nl_freq), freq_policy);
986*5b9c547cSRui Paulo 		if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
987*5b9c547cSRui Paulo 			continue;
988*5b9c547cSRui Paulo 		new_channels++;
989*5b9c547cSRui Paulo 	}
990*5b9c547cSRui Paulo 
991*5b9c547cSRui Paulo 	channel = os_realloc_array(mode->channels,
992*5b9c547cSRui Paulo 				   mode->num_channels + new_channels,
993*5b9c547cSRui Paulo 				   sizeof(struct hostapd_channel_data));
994*5b9c547cSRui Paulo 	if (!channel)
995*5b9c547cSRui Paulo 		return NL_SKIP;
996*5b9c547cSRui Paulo 
997*5b9c547cSRui Paulo 	mode->channels = channel;
998*5b9c547cSRui Paulo 	mode->num_channels += new_channels;
999*5b9c547cSRui Paulo 
1000*5b9c547cSRui Paulo 	idx = phy_info->last_chan_idx;
1001*5b9c547cSRui Paulo 
1002*5b9c547cSRui Paulo 	nla_for_each_nested(nl_freq, tb, rem_freq) {
1003*5b9c547cSRui Paulo 		nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX,
1004*5b9c547cSRui Paulo 			  nla_data(nl_freq), nla_len(nl_freq), freq_policy);
1005*5b9c547cSRui Paulo 		if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
1006*5b9c547cSRui Paulo 			continue;
1007*5b9c547cSRui Paulo 		phy_info_freq(mode, &mode->channels[idx], tb_freq);
1008*5b9c547cSRui Paulo 		idx++;
1009*5b9c547cSRui Paulo 	}
1010*5b9c547cSRui Paulo 	phy_info->last_chan_idx = idx;
1011*5b9c547cSRui Paulo 
1012*5b9c547cSRui Paulo 	return NL_OK;
1013*5b9c547cSRui Paulo }
1014*5b9c547cSRui Paulo 
1015*5b9c547cSRui Paulo 
1016*5b9c547cSRui Paulo static int phy_info_rates(struct hostapd_hw_modes *mode, struct nlattr *tb)
1017*5b9c547cSRui Paulo {
1018*5b9c547cSRui Paulo 	static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = {
1019*5b9c547cSRui Paulo 		[NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 },
1020*5b9c547cSRui Paulo 		[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] =
1021*5b9c547cSRui Paulo 		{ .type = NLA_FLAG },
1022*5b9c547cSRui Paulo 	};
1023*5b9c547cSRui Paulo 	struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1];
1024*5b9c547cSRui Paulo 	struct nlattr *nl_rate;
1025*5b9c547cSRui Paulo 	int rem_rate, idx;
1026*5b9c547cSRui Paulo 
1027*5b9c547cSRui Paulo 	if (tb == NULL)
1028*5b9c547cSRui Paulo 		return NL_OK;
1029*5b9c547cSRui Paulo 
1030*5b9c547cSRui Paulo 	nla_for_each_nested(nl_rate, tb, rem_rate) {
1031*5b9c547cSRui Paulo 		nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX,
1032*5b9c547cSRui Paulo 			  nla_data(nl_rate), nla_len(nl_rate),
1033*5b9c547cSRui Paulo 			  rate_policy);
1034*5b9c547cSRui Paulo 		if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
1035*5b9c547cSRui Paulo 			continue;
1036*5b9c547cSRui Paulo 		mode->num_rates++;
1037*5b9c547cSRui Paulo 	}
1038*5b9c547cSRui Paulo 
1039*5b9c547cSRui Paulo 	mode->rates = os_calloc(mode->num_rates, sizeof(int));
1040*5b9c547cSRui Paulo 	if (!mode->rates)
1041*5b9c547cSRui Paulo 		return NL_SKIP;
1042*5b9c547cSRui Paulo 
1043*5b9c547cSRui Paulo 	idx = 0;
1044*5b9c547cSRui Paulo 
1045*5b9c547cSRui Paulo 	nla_for_each_nested(nl_rate, tb, rem_rate) {
1046*5b9c547cSRui Paulo 		nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX,
1047*5b9c547cSRui Paulo 			  nla_data(nl_rate), nla_len(nl_rate),
1048*5b9c547cSRui Paulo 			  rate_policy);
1049*5b9c547cSRui Paulo 		if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
1050*5b9c547cSRui Paulo 			continue;
1051*5b9c547cSRui Paulo 		mode->rates[idx] = nla_get_u32(
1052*5b9c547cSRui Paulo 			tb_rate[NL80211_BITRATE_ATTR_RATE]);
1053*5b9c547cSRui Paulo 		idx++;
1054*5b9c547cSRui Paulo 	}
1055*5b9c547cSRui Paulo 
1056*5b9c547cSRui Paulo 	return NL_OK;
1057*5b9c547cSRui Paulo }
1058*5b9c547cSRui Paulo 
1059*5b9c547cSRui Paulo 
1060*5b9c547cSRui Paulo static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band)
1061*5b9c547cSRui Paulo {
1062*5b9c547cSRui Paulo 	struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
1063*5b9c547cSRui Paulo 	struct hostapd_hw_modes *mode;
1064*5b9c547cSRui Paulo 	int ret;
1065*5b9c547cSRui Paulo 
1066*5b9c547cSRui Paulo 	if (phy_info->last_mode != nl_band->nla_type) {
1067*5b9c547cSRui Paulo 		mode = os_realloc_array(phy_info->modes,
1068*5b9c547cSRui Paulo 					*phy_info->num_modes + 1,
1069*5b9c547cSRui Paulo 					sizeof(*mode));
1070*5b9c547cSRui Paulo 		if (!mode)
1071*5b9c547cSRui Paulo 			return NL_SKIP;
1072*5b9c547cSRui Paulo 		phy_info->modes = mode;
1073*5b9c547cSRui Paulo 
1074*5b9c547cSRui Paulo 		mode = &phy_info->modes[*(phy_info->num_modes)];
1075*5b9c547cSRui Paulo 		os_memset(mode, 0, sizeof(*mode));
1076*5b9c547cSRui Paulo 		mode->mode = NUM_HOSTAPD_MODES;
1077*5b9c547cSRui Paulo 		mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN |
1078*5b9c547cSRui Paulo 			HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN;
1079*5b9c547cSRui Paulo 
1080*5b9c547cSRui Paulo 		/*
1081*5b9c547cSRui Paulo 		 * Unsupported VHT MCS stream is defined as value 3, so the VHT
1082*5b9c547cSRui Paulo 		 * MCS RX/TX map must be initialized with 0xffff to mark all 8
1083*5b9c547cSRui Paulo 		 * possible streams as unsupported. This will be overridden if
1084*5b9c547cSRui Paulo 		 * driver advertises VHT support.
1085*5b9c547cSRui Paulo 		 */
1086*5b9c547cSRui Paulo 		mode->vht_mcs_set[0] = 0xff;
1087*5b9c547cSRui Paulo 		mode->vht_mcs_set[1] = 0xff;
1088*5b9c547cSRui Paulo 		mode->vht_mcs_set[4] = 0xff;
1089*5b9c547cSRui Paulo 		mode->vht_mcs_set[5] = 0xff;
1090*5b9c547cSRui Paulo 
1091*5b9c547cSRui Paulo 		*(phy_info->num_modes) += 1;
1092*5b9c547cSRui Paulo 		phy_info->last_mode = nl_band->nla_type;
1093*5b9c547cSRui Paulo 		phy_info->last_chan_idx = 0;
1094*5b9c547cSRui Paulo 	} else
1095*5b9c547cSRui Paulo 		mode = &phy_info->modes[*(phy_info->num_modes) - 1];
1096*5b9c547cSRui Paulo 
1097*5b9c547cSRui Paulo 	nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
1098*5b9c547cSRui Paulo 		  nla_len(nl_band), NULL);
1099*5b9c547cSRui Paulo 
1100*5b9c547cSRui Paulo 	phy_info_ht_capa(mode, tb_band[NL80211_BAND_ATTR_HT_CAPA],
1101*5b9c547cSRui Paulo 			 tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR],
1102*5b9c547cSRui Paulo 			 tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY],
1103*5b9c547cSRui Paulo 			 tb_band[NL80211_BAND_ATTR_HT_MCS_SET]);
1104*5b9c547cSRui Paulo 	phy_info_vht_capa(mode, tb_band[NL80211_BAND_ATTR_VHT_CAPA],
1105*5b9c547cSRui Paulo 			  tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]);
1106*5b9c547cSRui Paulo 	ret = phy_info_freqs(phy_info, mode, tb_band[NL80211_BAND_ATTR_FREQS]);
1107*5b9c547cSRui Paulo 	if (ret != NL_OK)
1108*5b9c547cSRui Paulo 		return ret;
1109*5b9c547cSRui Paulo 	ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]);
1110*5b9c547cSRui Paulo 	if (ret != NL_OK)
1111*5b9c547cSRui Paulo 		return ret;
1112*5b9c547cSRui Paulo 
1113*5b9c547cSRui Paulo 	return NL_OK;
1114*5b9c547cSRui Paulo }
1115*5b9c547cSRui Paulo 
1116*5b9c547cSRui Paulo 
1117*5b9c547cSRui Paulo static int phy_info_handler(struct nl_msg *msg, void *arg)
1118*5b9c547cSRui Paulo {
1119*5b9c547cSRui Paulo 	struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
1120*5b9c547cSRui Paulo 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
1121*5b9c547cSRui Paulo 	struct phy_info_arg *phy_info = arg;
1122*5b9c547cSRui Paulo 	struct nlattr *nl_band;
1123*5b9c547cSRui Paulo 	int rem_band;
1124*5b9c547cSRui Paulo 
1125*5b9c547cSRui Paulo 	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
1126*5b9c547cSRui Paulo 		  genlmsg_attrlen(gnlh, 0), NULL);
1127*5b9c547cSRui Paulo 
1128*5b9c547cSRui Paulo 	if (!tb_msg[NL80211_ATTR_WIPHY_BANDS])
1129*5b9c547cSRui Paulo 		return NL_SKIP;
1130*5b9c547cSRui Paulo 
1131*5b9c547cSRui Paulo 	nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band)
1132*5b9c547cSRui Paulo 	{
1133*5b9c547cSRui Paulo 		int res = phy_info_band(phy_info, nl_band);
1134*5b9c547cSRui Paulo 		if (res != NL_OK)
1135*5b9c547cSRui Paulo 			return res;
1136*5b9c547cSRui Paulo 	}
1137*5b9c547cSRui Paulo 
1138*5b9c547cSRui Paulo 	return NL_SKIP;
1139*5b9c547cSRui Paulo }
1140*5b9c547cSRui Paulo 
1141*5b9c547cSRui Paulo 
1142*5b9c547cSRui Paulo static struct hostapd_hw_modes *
1143*5b9c547cSRui Paulo wpa_driver_nl80211_postprocess_modes(struct hostapd_hw_modes *modes,
1144*5b9c547cSRui Paulo 				     u16 *num_modes)
1145*5b9c547cSRui Paulo {
1146*5b9c547cSRui Paulo 	u16 m;
1147*5b9c547cSRui Paulo 	struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode;
1148*5b9c547cSRui Paulo 	int i, mode11g_idx = -1;
1149*5b9c547cSRui Paulo 
1150*5b9c547cSRui Paulo 	/* heuristic to set up modes */
1151*5b9c547cSRui Paulo 	for (m = 0; m < *num_modes; m++) {
1152*5b9c547cSRui Paulo 		if (!modes[m].num_channels)
1153*5b9c547cSRui Paulo 			continue;
1154*5b9c547cSRui Paulo 		if (modes[m].channels[0].freq < 4000) {
1155*5b9c547cSRui Paulo 			modes[m].mode = HOSTAPD_MODE_IEEE80211B;
1156*5b9c547cSRui Paulo 			for (i = 0; i < modes[m].num_rates; i++) {
1157*5b9c547cSRui Paulo 				if (modes[m].rates[i] > 200) {
1158*5b9c547cSRui Paulo 					modes[m].mode = HOSTAPD_MODE_IEEE80211G;
1159*5b9c547cSRui Paulo 					break;
1160*5b9c547cSRui Paulo 				}
1161*5b9c547cSRui Paulo 			}
1162*5b9c547cSRui Paulo 		} else if (modes[m].channels[0].freq > 50000)
1163*5b9c547cSRui Paulo 			modes[m].mode = HOSTAPD_MODE_IEEE80211AD;
1164*5b9c547cSRui Paulo 		else
1165*5b9c547cSRui Paulo 			modes[m].mode = HOSTAPD_MODE_IEEE80211A;
1166*5b9c547cSRui Paulo 	}
1167*5b9c547cSRui Paulo 
1168*5b9c547cSRui Paulo 	/* If only 802.11g mode is included, use it to construct matching
1169*5b9c547cSRui Paulo 	 * 802.11b mode data. */
1170*5b9c547cSRui Paulo 
1171*5b9c547cSRui Paulo 	for (m = 0; m < *num_modes; m++) {
1172*5b9c547cSRui Paulo 		if (modes[m].mode == HOSTAPD_MODE_IEEE80211B)
1173*5b9c547cSRui Paulo 			return modes; /* 802.11b already included */
1174*5b9c547cSRui Paulo 		if (modes[m].mode == HOSTAPD_MODE_IEEE80211G)
1175*5b9c547cSRui Paulo 			mode11g_idx = m;
1176*5b9c547cSRui Paulo 	}
1177*5b9c547cSRui Paulo 
1178*5b9c547cSRui Paulo 	if (mode11g_idx < 0)
1179*5b9c547cSRui Paulo 		return modes; /* 2.4 GHz band not supported at all */
1180*5b9c547cSRui Paulo 
1181*5b9c547cSRui Paulo 	nmodes = os_realloc_array(modes, *num_modes + 1, sizeof(*nmodes));
1182*5b9c547cSRui Paulo 	if (nmodes == NULL)
1183*5b9c547cSRui Paulo 		return modes; /* Could not add 802.11b mode */
1184*5b9c547cSRui Paulo 
1185*5b9c547cSRui Paulo 	mode = &nmodes[*num_modes];
1186*5b9c547cSRui Paulo 	os_memset(mode, 0, sizeof(*mode));
1187*5b9c547cSRui Paulo 	(*num_modes)++;
1188*5b9c547cSRui Paulo 	modes = nmodes;
1189*5b9c547cSRui Paulo 
1190*5b9c547cSRui Paulo 	mode->mode = HOSTAPD_MODE_IEEE80211B;
1191*5b9c547cSRui Paulo 
1192*5b9c547cSRui Paulo 	mode11g = &modes[mode11g_idx];
1193*5b9c547cSRui Paulo 	mode->num_channels = mode11g->num_channels;
1194*5b9c547cSRui Paulo 	mode->channels = os_malloc(mode11g->num_channels *
1195*5b9c547cSRui Paulo 				   sizeof(struct hostapd_channel_data));
1196*5b9c547cSRui Paulo 	if (mode->channels == NULL) {
1197*5b9c547cSRui Paulo 		(*num_modes)--;
1198*5b9c547cSRui Paulo 		return modes; /* Could not add 802.11b mode */
1199*5b9c547cSRui Paulo 	}
1200*5b9c547cSRui Paulo 	os_memcpy(mode->channels, mode11g->channels,
1201*5b9c547cSRui Paulo 		  mode11g->num_channels * sizeof(struct hostapd_channel_data));
1202*5b9c547cSRui Paulo 
1203*5b9c547cSRui Paulo 	mode->num_rates = 0;
1204*5b9c547cSRui Paulo 	mode->rates = os_malloc(4 * sizeof(int));
1205*5b9c547cSRui Paulo 	if (mode->rates == NULL) {
1206*5b9c547cSRui Paulo 		os_free(mode->channels);
1207*5b9c547cSRui Paulo 		(*num_modes)--;
1208*5b9c547cSRui Paulo 		return modes; /* Could not add 802.11b mode */
1209*5b9c547cSRui Paulo 	}
1210*5b9c547cSRui Paulo 
1211*5b9c547cSRui Paulo 	for (i = 0; i < mode11g->num_rates; i++) {
1212*5b9c547cSRui Paulo 		if (mode11g->rates[i] != 10 && mode11g->rates[i] != 20 &&
1213*5b9c547cSRui Paulo 		    mode11g->rates[i] != 55 && mode11g->rates[i] != 110)
1214*5b9c547cSRui Paulo 			continue;
1215*5b9c547cSRui Paulo 		mode->rates[mode->num_rates] = mode11g->rates[i];
1216*5b9c547cSRui Paulo 		mode->num_rates++;
1217*5b9c547cSRui Paulo 		if (mode->num_rates == 4)
1218*5b9c547cSRui Paulo 			break;
1219*5b9c547cSRui Paulo 	}
1220*5b9c547cSRui Paulo 
1221*5b9c547cSRui Paulo 	if (mode->num_rates == 0) {
1222*5b9c547cSRui Paulo 		os_free(mode->channels);
1223*5b9c547cSRui Paulo 		os_free(mode->rates);
1224*5b9c547cSRui Paulo 		(*num_modes)--;
1225*5b9c547cSRui Paulo 		return modes; /* No 802.11b rates */
1226*5b9c547cSRui Paulo 	}
1227*5b9c547cSRui Paulo 
1228*5b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "nl80211: Added 802.11b mode based on 802.11g "
1229*5b9c547cSRui Paulo 		   "information");
1230*5b9c547cSRui Paulo 
1231*5b9c547cSRui Paulo 	return modes;
1232*5b9c547cSRui Paulo }
1233*5b9c547cSRui Paulo 
1234*5b9c547cSRui Paulo 
1235*5b9c547cSRui Paulo static void nl80211_set_ht40_mode(struct hostapd_hw_modes *mode, int start,
1236*5b9c547cSRui Paulo 				  int end)
1237*5b9c547cSRui Paulo {
1238*5b9c547cSRui Paulo 	int c;
1239*5b9c547cSRui Paulo 
1240*5b9c547cSRui Paulo 	for (c = 0; c < mode->num_channels; c++) {
1241*5b9c547cSRui Paulo 		struct hostapd_channel_data *chan = &mode->channels[c];
1242*5b9c547cSRui Paulo 		if (chan->freq - 10 >= start && chan->freq + 10 <= end)
1243*5b9c547cSRui Paulo 			chan->flag |= HOSTAPD_CHAN_HT40;
1244*5b9c547cSRui Paulo 	}
1245*5b9c547cSRui Paulo }
1246*5b9c547cSRui Paulo 
1247*5b9c547cSRui Paulo 
1248*5b9c547cSRui Paulo static void nl80211_set_ht40_mode_sec(struct hostapd_hw_modes *mode, int start,
1249*5b9c547cSRui Paulo 				      int end)
1250*5b9c547cSRui Paulo {
1251*5b9c547cSRui Paulo 	int c;
1252*5b9c547cSRui Paulo 
1253*5b9c547cSRui Paulo 	for (c = 0; c < mode->num_channels; c++) {
1254*5b9c547cSRui Paulo 		struct hostapd_channel_data *chan = &mode->channels[c];
1255*5b9c547cSRui Paulo 		if (!(chan->flag & HOSTAPD_CHAN_HT40))
1256*5b9c547cSRui Paulo 			continue;
1257*5b9c547cSRui Paulo 		if (chan->freq - 30 >= start && chan->freq - 10 <= end)
1258*5b9c547cSRui Paulo 			chan->flag |= HOSTAPD_CHAN_HT40MINUS;
1259*5b9c547cSRui Paulo 		if (chan->freq + 10 >= start && chan->freq + 30 <= end)
1260*5b9c547cSRui Paulo 			chan->flag |= HOSTAPD_CHAN_HT40PLUS;
1261*5b9c547cSRui Paulo 	}
1262*5b9c547cSRui Paulo }
1263*5b9c547cSRui Paulo 
1264*5b9c547cSRui Paulo 
1265*5b9c547cSRui Paulo static void nl80211_reg_rule_max_eirp(u32 start, u32 end, u32 max_eirp,
1266*5b9c547cSRui Paulo 				      struct phy_info_arg *results)
1267*5b9c547cSRui Paulo {
1268*5b9c547cSRui Paulo 	u16 m;
1269*5b9c547cSRui Paulo 
1270*5b9c547cSRui Paulo 	for (m = 0; m < *results->num_modes; m++) {
1271*5b9c547cSRui Paulo 		int c;
1272*5b9c547cSRui Paulo 		struct hostapd_hw_modes *mode = &results->modes[m];
1273*5b9c547cSRui Paulo 
1274*5b9c547cSRui Paulo 		for (c = 0; c < mode->num_channels; c++) {
1275*5b9c547cSRui Paulo 			struct hostapd_channel_data *chan = &mode->channels[c];
1276*5b9c547cSRui Paulo 			if ((u32) chan->freq - 10 >= start &&
1277*5b9c547cSRui Paulo 			    (u32) chan->freq + 10 <= end)
1278*5b9c547cSRui Paulo 				chan->max_tx_power = max_eirp;
1279*5b9c547cSRui Paulo 		}
1280*5b9c547cSRui Paulo 	}
1281*5b9c547cSRui Paulo }
1282*5b9c547cSRui Paulo 
1283*5b9c547cSRui Paulo 
1284*5b9c547cSRui Paulo static void nl80211_reg_rule_ht40(u32 start, u32 end,
1285*5b9c547cSRui Paulo 				  struct phy_info_arg *results)
1286*5b9c547cSRui Paulo {
1287*5b9c547cSRui Paulo 	u16 m;
1288*5b9c547cSRui Paulo 
1289*5b9c547cSRui Paulo 	for (m = 0; m < *results->num_modes; m++) {
1290*5b9c547cSRui Paulo 		if (!(results->modes[m].ht_capab &
1291*5b9c547cSRui Paulo 		      HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
1292*5b9c547cSRui Paulo 			continue;
1293*5b9c547cSRui Paulo 		nl80211_set_ht40_mode(&results->modes[m], start, end);
1294*5b9c547cSRui Paulo 	}
1295*5b9c547cSRui Paulo }
1296*5b9c547cSRui Paulo 
1297*5b9c547cSRui Paulo 
1298*5b9c547cSRui Paulo static void nl80211_reg_rule_sec(struct nlattr *tb[],
1299*5b9c547cSRui Paulo 				 struct phy_info_arg *results)
1300*5b9c547cSRui Paulo {
1301*5b9c547cSRui Paulo 	u32 start, end, max_bw;
1302*5b9c547cSRui Paulo 	u16 m;
1303*5b9c547cSRui Paulo 
1304*5b9c547cSRui Paulo 	if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
1305*5b9c547cSRui Paulo 	    tb[NL80211_ATTR_FREQ_RANGE_END] == NULL ||
1306*5b9c547cSRui Paulo 	    tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL)
1307*5b9c547cSRui Paulo 		return;
1308*5b9c547cSRui Paulo 
1309*5b9c547cSRui Paulo 	start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
1310*5b9c547cSRui Paulo 	end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
1311*5b9c547cSRui Paulo 	max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
1312*5b9c547cSRui Paulo 
1313*5b9c547cSRui Paulo 	if (max_bw < 20)
1314*5b9c547cSRui Paulo 		return;
1315*5b9c547cSRui Paulo 
1316*5b9c547cSRui Paulo 	for (m = 0; m < *results->num_modes; m++) {
1317*5b9c547cSRui Paulo 		if (!(results->modes[m].ht_capab &
1318*5b9c547cSRui Paulo 		      HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
1319*5b9c547cSRui Paulo 			continue;
1320*5b9c547cSRui Paulo 		nl80211_set_ht40_mode_sec(&results->modes[m], start, end);
1321*5b9c547cSRui Paulo 	}
1322*5b9c547cSRui Paulo }
1323*5b9c547cSRui Paulo 
1324*5b9c547cSRui Paulo 
1325*5b9c547cSRui Paulo static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start,
1326*5b9c547cSRui Paulo 				 int end)
1327*5b9c547cSRui Paulo {
1328*5b9c547cSRui Paulo 	int c;
1329*5b9c547cSRui Paulo 
1330*5b9c547cSRui Paulo 	for (c = 0; c < mode->num_channels; c++) {
1331*5b9c547cSRui Paulo 		struct hostapd_channel_data *chan = &mode->channels[c];
1332*5b9c547cSRui Paulo 		if (chan->freq - 10 >= start && chan->freq + 70 <= end)
1333*5b9c547cSRui Paulo 			chan->flag |= HOSTAPD_CHAN_VHT_10_70;
1334*5b9c547cSRui Paulo 
1335*5b9c547cSRui Paulo 		if (chan->freq - 30 >= start && chan->freq + 50 <= end)
1336*5b9c547cSRui Paulo 			chan->flag |= HOSTAPD_CHAN_VHT_30_50;
1337*5b9c547cSRui Paulo 
1338*5b9c547cSRui Paulo 		if (chan->freq - 50 >= start && chan->freq + 30 <= end)
1339*5b9c547cSRui Paulo 			chan->flag |= HOSTAPD_CHAN_VHT_50_30;
1340*5b9c547cSRui Paulo 
1341*5b9c547cSRui Paulo 		if (chan->freq - 70 >= start && chan->freq + 10 <= end)
1342*5b9c547cSRui Paulo 			chan->flag |= HOSTAPD_CHAN_VHT_70_10;
1343*5b9c547cSRui Paulo 	}
1344*5b9c547cSRui Paulo }
1345*5b9c547cSRui Paulo 
1346*5b9c547cSRui Paulo 
1347*5b9c547cSRui Paulo static void nl80211_reg_rule_vht(struct nlattr *tb[],
1348*5b9c547cSRui Paulo 				 struct phy_info_arg *results)
1349*5b9c547cSRui Paulo {
1350*5b9c547cSRui Paulo 	u32 start, end, max_bw;
1351*5b9c547cSRui Paulo 	u16 m;
1352*5b9c547cSRui Paulo 
1353*5b9c547cSRui Paulo 	if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
1354*5b9c547cSRui Paulo 	    tb[NL80211_ATTR_FREQ_RANGE_END] == NULL ||
1355*5b9c547cSRui Paulo 	    tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL)
1356*5b9c547cSRui Paulo 		return;
1357*5b9c547cSRui Paulo 
1358*5b9c547cSRui Paulo 	start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
1359*5b9c547cSRui Paulo 	end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
1360*5b9c547cSRui Paulo 	max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
1361*5b9c547cSRui Paulo 
1362*5b9c547cSRui Paulo 	if (max_bw < 80)
1363*5b9c547cSRui Paulo 		return;
1364*5b9c547cSRui Paulo 
1365*5b9c547cSRui Paulo 	for (m = 0; m < *results->num_modes; m++) {
1366*5b9c547cSRui Paulo 		if (!(results->modes[m].ht_capab &
1367*5b9c547cSRui Paulo 		      HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
1368*5b9c547cSRui Paulo 			continue;
1369*5b9c547cSRui Paulo 		/* TODO: use a real VHT support indication */
1370*5b9c547cSRui Paulo 		if (!results->modes[m].vht_capab)
1371*5b9c547cSRui Paulo 			continue;
1372*5b9c547cSRui Paulo 
1373*5b9c547cSRui Paulo 		nl80211_set_vht_mode(&results->modes[m], start, end);
1374*5b9c547cSRui Paulo 	}
1375*5b9c547cSRui Paulo }
1376*5b9c547cSRui Paulo 
1377*5b9c547cSRui Paulo 
1378*5b9c547cSRui Paulo static const char * dfs_domain_name(enum nl80211_dfs_regions region)
1379*5b9c547cSRui Paulo {
1380*5b9c547cSRui Paulo 	switch (region) {
1381*5b9c547cSRui Paulo 	case NL80211_DFS_UNSET:
1382*5b9c547cSRui Paulo 		return "DFS-UNSET";
1383*5b9c547cSRui Paulo 	case NL80211_DFS_FCC:
1384*5b9c547cSRui Paulo 		return "DFS-FCC";
1385*5b9c547cSRui Paulo 	case NL80211_DFS_ETSI:
1386*5b9c547cSRui Paulo 		return "DFS-ETSI";
1387*5b9c547cSRui Paulo 	case NL80211_DFS_JP:
1388*5b9c547cSRui Paulo 		return "DFS-JP";
1389*5b9c547cSRui Paulo 	default:
1390*5b9c547cSRui Paulo 		return "DFS-invalid";
1391*5b9c547cSRui Paulo 	}
1392*5b9c547cSRui Paulo }
1393*5b9c547cSRui Paulo 
1394*5b9c547cSRui Paulo 
1395*5b9c547cSRui Paulo static int nl80211_get_reg(struct nl_msg *msg, void *arg)
1396*5b9c547cSRui Paulo {
1397*5b9c547cSRui Paulo 	struct phy_info_arg *results = arg;
1398*5b9c547cSRui Paulo 	struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
1399*5b9c547cSRui Paulo 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
1400*5b9c547cSRui Paulo 	struct nlattr *nl_rule;
1401*5b9c547cSRui Paulo 	struct nlattr *tb_rule[NL80211_FREQUENCY_ATTR_MAX + 1];
1402*5b9c547cSRui Paulo 	int rem_rule;
1403*5b9c547cSRui Paulo 	static struct nla_policy reg_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
1404*5b9c547cSRui Paulo 		[NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
1405*5b9c547cSRui Paulo 		[NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
1406*5b9c547cSRui Paulo 		[NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
1407*5b9c547cSRui Paulo 		[NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
1408*5b9c547cSRui Paulo 		[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
1409*5b9c547cSRui Paulo 		[NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
1410*5b9c547cSRui Paulo 	};
1411*5b9c547cSRui Paulo 
1412*5b9c547cSRui Paulo 	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
1413*5b9c547cSRui Paulo 		  genlmsg_attrlen(gnlh, 0), NULL);
1414*5b9c547cSRui Paulo 	if (!tb_msg[NL80211_ATTR_REG_ALPHA2] ||
1415*5b9c547cSRui Paulo 	    !tb_msg[NL80211_ATTR_REG_RULES]) {
1416*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "nl80211: No regulatory information "
1417*5b9c547cSRui Paulo 			   "available");
1418*5b9c547cSRui Paulo 		return NL_SKIP;
1419*5b9c547cSRui Paulo 	}
1420*5b9c547cSRui Paulo 
1421*5b9c547cSRui Paulo 	if (tb_msg[NL80211_ATTR_DFS_REGION]) {
1422*5b9c547cSRui Paulo 		enum nl80211_dfs_regions dfs_domain;
1423*5b9c547cSRui Paulo 		dfs_domain = nla_get_u8(tb_msg[NL80211_ATTR_DFS_REGION]);
1424*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s (%s)",
1425*5b9c547cSRui Paulo 			   (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]),
1426*5b9c547cSRui Paulo 			   dfs_domain_name(dfs_domain));
1427*5b9c547cSRui Paulo 	} else {
1428*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s",
1429*5b9c547cSRui Paulo 			   (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]));
1430*5b9c547cSRui Paulo 	}
1431*5b9c547cSRui Paulo 
1432*5b9c547cSRui Paulo 	nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
1433*5b9c547cSRui Paulo 	{
1434*5b9c547cSRui Paulo 		u32 start, end, max_eirp = 0, max_bw = 0, flags = 0;
1435*5b9c547cSRui Paulo 		nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
1436*5b9c547cSRui Paulo 			  nla_data(nl_rule), nla_len(nl_rule), reg_policy);
1437*5b9c547cSRui Paulo 		if (tb_rule[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
1438*5b9c547cSRui Paulo 		    tb_rule[NL80211_ATTR_FREQ_RANGE_END] == NULL)
1439*5b9c547cSRui Paulo 			continue;
1440*5b9c547cSRui Paulo 		start = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
1441*5b9c547cSRui Paulo 		end = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
1442*5b9c547cSRui Paulo 		if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP])
1443*5b9c547cSRui Paulo 			max_eirp = nla_get_u32(tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) / 100;
1444*5b9c547cSRui Paulo 		if (tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW])
1445*5b9c547cSRui Paulo 			max_bw = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
1446*5b9c547cSRui Paulo 		if (tb_rule[NL80211_ATTR_REG_RULE_FLAGS])
1447*5b9c547cSRui Paulo 			flags = nla_get_u32(tb_rule[NL80211_ATTR_REG_RULE_FLAGS]);
1448*5b9c547cSRui Paulo 
1449*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz %u mBm%s%s%s%s%s%s%s%s",
1450*5b9c547cSRui Paulo 			   start, end, max_bw, max_eirp,
1451*5b9c547cSRui Paulo 			   flags & NL80211_RRF_NO_OFDM ? " (no OFDM)" : "",
1452*5b9c547cSRui Paulo 			   flags & NL80211_RRF_NO_CCK ? " (no CCK)" : "",
1453*5b9c547cSRui Paulo 			   flags & NL80211_RRF_NO_INDOOR ? " (no indoor)" : "",
1454*5b9c547cSRui Paulo 			   flags & NL80211_RRF_NO_OUTDOOR ? " (no outdoor)" :
1455*5b9c547cSRui Paulo 			   "",
1456*5b9c547cSRui Paulo 			   flags & NL80211_RRF_DFS ? " (DFS)" : "",
1457*5b9c547cSRui Paulo 			   flags & NL80211_RRF_PTP_ONLY ? " (PTP only)" : "",
1458*5b9c547cSRui Paulo 			   flags & NL80211_RRF_PTMP_ONLY ? " (PTMP only)" : "",
1459*5b9c547cSRui Paulo 			   flags & NL80211_RRF_NO_IR ? " (no IR)" : "");
1460*5b9c547cSRui Paulo 		if (max_bw >= 40)
1461*5b9c547cSRui Paulo 			nl80211_reg_rule_ht40(start, end, results);
1462*5b9c547cSRui Paulo 		if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP])
1463*5b9c547cSRui Paulo 			nl80211_reg_rule_max_eirp(start, end, max_eirp,
1464*5b9c547cSRui Paulo 						  results);
1465*5b9c547cSRui Paulo 	}
1466*5b9c547cSRui Paulo 
1467*5b9c547cSRui Paulo 	nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
1468*5b9c547cSRui Paulo 	{
1469*5b9c547cSRui Paulo 		nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
1470*5b9c547cSRui Paulo 			  nla_data(nl_rule), nla_len(nl_rule), reg_policy);
1471*5b9c547cSRui Paulo 		nl80211_reg_rule_sec(tb_rule, results);
1472*5b9c547cSRui Paulo 	}
1473*5b9c547cSRui Paulo 
1474*5b9c547cSRui Paulo 	nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
1475*5b9c547cSRui Paulo 	{
1476*5b9c547cSRui Paulo 		nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
1477*5b9c547cSRui Paulo 			  nla_data(nl_rule), nla_len(nl_rule), reg_policy);
1478*5b9c547cSRui Paulo 		nl80211_reg_rule_vht(tb_rule, results);
1479*5b9c547cSRui Paulo 	}
1480*5b9c547cSRui Paulo 
1481*5b9c547cSRui Paulo 	return NL_SKIP;
1482*5b9c547cSRui Paulo }
1483*5b9c547cSRui Paulo 
1484*5b9c547cSRui Paulo 
1485*5b9c547cSRui Paulo static int nl80211_set_regulatory_flags(struct wpa_driver_nl80211_data *drv,
1486*5b9c547cSRui Paulo 					struct phy_info_arg *results)
1487*5b9c547cSRui Paulo {
1488*5b9c547cSRui Paulo 	struct nl_msg *msg;
1489*5b9c547cSRui Paulo 
1490*5b9c547cSRui Paulo 	msg = nlmsg_alloc();
1491*5b9c547cSRui Paulo 	if (!msg)
1492*5b9c547cSRui Paulo 		return -ENOMEM;
1493*5b9c547cSRui Paulo 
1494*5b9c547cSRui Paulo 	nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG);
1495*5b9c547cSRui Paulo 	return send_and_recv_msgs(drv, msg, nl80211_get_reg, results);
1496*5b9c547cSRui Paulo }
1497*5b9c547cSRui Paulo 
1498*5b9c547cSRui Paulo 
1499*5b9c547cSRui Paulo struct hostapd_hw_modes *
1500*5b9c547cSRui Paulo nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
1501*5b9c547cSRui Paulo {
1502*5b9c547cSRui Paulo 	u32 feat;
1503*5b9c547cSRui Paulo 	struct i802_bss *bss = priv;
1504*5b9c547cSRui Paulo 	struct wpa_driver_nl80211_data *drv = bss->drv;
1505*5b9c547cSRui Paulo 	int nl_flags = 0;
1506*5b9c547cSRui Paulo 	struct nl_msg *msg;
1507*5b9c547cSRui Paulo 	struct phy_info_arg result = {
1508*5b9c547cSRui Paulo 		.num_modes = num_modes,
1509*5b9c547cSRui Paulo 		.modes = NULL,
1510*5b9c547cSRui Paulo 		.last_mode = -1,
1511*5b9c547cSRui Paulo 	};
1512*5b9c547cSRui Paulo 
1513*5b9c547cSRui Paulo 	*num_modes = 0;
1514*5b9c547cSRui Paulo 	*flags = 0;
1515*5b9c547cSRui Paulo 
1516*5b9c547cSRui Paulo 	feat = get_nl80211_protocol_features(drv);
1517*5b9c547cSRui Paulo 	if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
1518*5b9c547cSRui Paulo 		nl_flags = NLM_F_DUMP;
1519*5b9c547cSRui Paulo 	if (!(msg = nl80211_cmd_msg(bss, nl_flags, NL80211_CMD_GET_WIPHY)) ||
1520*5b9c547cSRui Paulo 	    nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) {
1521*5b9c547cSRui Paulo 		nlmsg_free(msg);
1522*5b9c547cSRui Paulo 		return NULL;
1523*5b9c547cSRui Paulo 	}
1524*5b9c547cSRui Paulo 
1525*5b9c547cSRui Paulo 	if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) {
1526*5b9c547cSRui Paulo 		nl80211_set_regulatory_flags(drv, &result);
1527*5b9c547cSRui Paulo 		return wpa_driver_nl80211_postprocess_modes(result.modes,
1528*5b9c547cSRui Paulo 							    num_modes);
1529*5b9c547cSRui Paulo 	}
1530*5b9c547cSRui Paulo 
1531*5b9c547cSRui Paulo 	return NULL;
1532*5b9c547cSRui Paulo }
1533