xref: /aosp_15_r20/external/autotest/client/deps/iwcap/src/iwcap.c (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li /*
2*9c5db199SXin Li  * This is a stripped down version of the iw tool designed for
3*9c5db199SXin Li  * programmatically checking driver/hw capabilities.
4*9c5db199SXin Li  *
5*9c5db199SXin Li  * Copyright 2007, 2008	Johannes Berg <[email protected]>
6*9c5db199SXin Li  */
7*9c5db199SXin Li 
8*9c5db199SXin Li #include <errno.h>
9*9c5db199SXin Li #include <stdio.h>
10*9c5db199SXin Li #include <string.h>
11*9c5db199SXin Li #include <net/if.h>
12*9c5db199SXin Li #include <sys/types.h>
13*9c5db199SXin Li #include <sys/stat.h>
14*9c5db199SXin Li #include <fcntl.h>
15*9c5db199SXin Li #include <unistd.h>
16*9c5db199SXin Li #include <stdbool.h>
17*9c5db199SXin Li 
18*9c5db199SXin Li #include <netlink/netlink.h>
19*9c5db199SXin Li #include <netlink/genl/genl.h>
20*9c5db199SXin Li #include <netlink/genl/family.h>
21*9c5db199SXin Li #include <netlink/genl/ctrl.h>
22*9c5db199SXin Li #include <netlink/msg.h>
23*9c5db199SXin Li #include <netlink/attr.h>
24*9c5db199SXin Li 
25*9c5db199SXin Li #include "nl80211.h"
26*9c5db199SXin Li 
27*9c5db199SXin Li #ifndef CONFIG_LIBNL20
28*9c5db199SXin Li /* libnl 2.0 compatibility code */
29*9c5db199SXin Li 
30*9c5db199SXin Li #  define nl_sock nl_handle
31*9c5db199SXin Li 
nl_socket_alloc(void)32*9c5db199SXin Li static inline struct nl_handle *nl_socket_alloc(void)
33*9c5db199SXin Li {
34*9c5db199SXin Li 	return nl_handle_alloc();
35*9c5db199SXin Li }
36*9c5db199SXin Li 
nl_socket_free(struct nl_sock * h)37*9c5db199SXin Li static inline void nl_socket_free(struct nl_sock *h)
38*9c5db199SXin Li {
39*9c5db199SXin Li 	nl_handle_destroy(h);
40*9c5db199SXin Li }
41*9c5db199SXin Li 
__genl_ctrl_alloc_cache(struct nl_sock * h,struct nl_cache ** cache)42*9c5db199SXin Li static inline int __genl_ctrl_alloc_cache(struct nl_sock *h, struct nl_cache **cache)
43*9c5db199SXin Li {
44*9c5db199SXin Li 	struct nl_cache *tmp = genl_ctrl_alloc_cache(h);
45*9c5db199SXin Li 	if (!tmp)
46*9c5db199SXin Li 		return -ENOMEM;
47*9c5db199SXin Li 	*cache = tmp;
48*9c5db199SXin Li 	return 0;
49*9c5db199SXin Li }
50*9c5db199SXin Li #define genl_ctrl_alloc_cache __genl_ctrl_alloc_cache
51*9c5db199SXin Li #endif /* CONFIG_LIBNL20 */
52*9c5db199SXin Li 
53*9c5db199SXin Li struct nl80211_state {
54*9c5db199SXin Li 	struct nl_sock *nl_sock;
55*9c5db199SXin Li 	struct nl_cache *nl_cache;
56*9c5db199SXin Li 	struct genl_family *nl80211;
57*9c5db199SXin Li };
58*9c5db199SXin Li 
nl80211_init(struct nl80211_state * state)59*9c5db199SXin Li static int nl80211_init(struct nl80211_state *state)
60*9c5db199SXin Li {
61*9c5db199SXin Li 	int err;
62*9c5db199SXin Li 
63*9c5db199SXin Li 	state->nl_sock = nl_socket_alloc();
64*9c5db199SXin Li 	if (!state->nl_sock) {
65*9c5db199SXin Li 		fprintf(stderr, "Failed to allocate netlink socket.\n");
66*9c5db199SXin Li 		return -ENOMEM;
67*9c5db199SXin Li 	}
68*9c5db199SXin Li 
69*9c5db199SXin Li 	if (genl_connect(state->nl_sock)) {
70*9c5db199SXin Li 		fprintf(stderr, "Failed to connect to generic netlink.\n");
71*9c5db199SXin Li 		err = -ENOLINK;
72*9c5db199SXin Li 		goto out_handle_destroy;
73*9c5db199SXin Li 	}
74*9c5db199SXin Li 
75*9c5db199SXin Li 	if (genl_ctrl_alloc_cache(state->nl_sock, &state->nl_cache)) {
76*9c5db199SXin Li 		fprintf(stderr, "Failed to allocate generic netlink cache.\n");
77*9c5db199SXin Li 		err = -ENOMEM;
78*9c5db199SXin Li 		goto out_handle_destroy;
79*9c5db199SXin Li 	}
80*9c5db199SXin Li 
81*9c5db199SXin Li 	state->nl80211 = genl_ctrl_search_by_name(state->nl_cache, "nl80211");
82*9c5db199SXin Li 	if (!state->nl80211) {
83*9c5db199SXin Li 		fprintf(stderr, "nl80211 not found.\n");
84*9c5db199SXin Li 		err = -ENOENT;
85*9c5db199SXin Li 		goto out_cache_free;
86*9c5db199SXin Li 	}
87*9c5db199SXin Li 
88*9c5db199SXin Li 	return 0;
89*9c5db199SXin Li 
90*9c5db199SXin Li  out_cache_free:
91*9c5db199SXin Li 	nl_cache_free(state->nl_cache);
92*9c5db199SXin Li  out_handle_destroy:
93*9c5db199SXin Li 	nl_socket_free(state->nl_sock);
94*9c5db199SXin Li 	return err;
95*9c5db199SXin Li }
96*9c5db199SXin Li 
nl80211_cleanup(struct nl80211_state * state)97*9c5db199SXin Li static void nl80211_cleanup(struct nl80211_state *state)
98*9c5db199SXin Li {
99*9c5db199SXin Li 	genl_family_put(state->nl80211);
100*9c5db199SXin Li 	nl_cache_free(state->nl_cache);
101*9c5db199SXin Li 	nl_socket_free(state->nl_sock);
102*9c5db199SXin Li }
103*9c5db199SXin Li 
104*9c5db199SXin Li static const char *argv0;
105*9c5db199SXin Li 
phy_lookup(char * name)106*9c5db199SXin Li static int phy_lookup(char *name)
107*9c5db199SXin Li {
108*9c5db199SXin Li 	char buf[200];
109*9c5db199SXin Li 	int fd, pos;
110*9c5db199SXin Li 
111*9c5db199SXin Li 	snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", name);
112*9c5db199SXin Li 
113*9c5db199SXin Li 	fd = open(buf, O_RDONLY);
114*9c5db199SXin Li 	if (fd < 0)
115*9c5db199SXin Li 		return -1;
116*9c5db199SXin Li 	pos = read(fd, buf, sizeof(buf) - 1);
117*9c5db199SXin Li 	if (pos < 0)
118*9c5db199SXin Li 		return -1;
119*9c5db199SXin Li 	buf[pos] = '\0';
120*9c5db199SXin Li 	return atoi(buf);
121*9c5db199SXin Li }
122*9c5db199SXin Li 
123*9c5db199SXin Li enum {
124*9c5db199SXin Li 	CHECK_IS_HT20		= 0x00000001,
125*9c5db199SXin Li 	CHECK_IS_HT40		= 0x00000002,
126*9c5db199SXin Li 	CHECK_IS_PSMP		= 0x00000004,
127*9c5db199SXin Li 	CHECK_IS_AMPDU		= 0x00000008,
128*9c5db199SXin Li 	CHECK_IS_AMSDU		= 0x00000010,
129*9c5db199SXin Li 	CHECK_IS_SMPS		= 0x00000020,
130*9c5db199SXin Li 	CHECK_IS_STA		= 0x00000040,
131*9c5db199SXin Li 	CHECK_IS_AP		= 0x00000080,
132*9c5db199SXin Li 	CHECK_IS_IBSS		= 0x00000100,
133*9c5db199SXin Li 	CHECK_IS_MBSS		= 0x00000200,
134*9c5db199SXin Li 	CHECK_IS_MONITOR	= 0x00000400,
135*9c5db199SXin Li 	CHECK_BANDS		= 0x00000800,
136*9c5db199SXin Li 	CHECK_FREQS		= 0x00001000,
137*9c5db199SXin Li 	CHECK_RATES		= 0x00002000,
138*9c5db199SXin Li 	CHECK_MCS		= 0x00004000,
139*9c5db199SXin Li 	CHECK_AMPDU_DENS	= 0x00008000,
140*9c5db199SXin Li 	CHECK_AMPDU_FACT	= 0x00010000,
141*9c5db199SXin Li 	CHECK_AMSDU_LEN		= 0x00020000,
142*9c5db199SXin Li 	CHECK_IS_LPDC		= 0x00040000,
143*9c5db199SXin Li 	CHECK_IS_GREENFIELD	= 0x00080000,
144*9c5db199SXin Li 	CHECK_IS_SGI20		= 0x00100000,
145*9c5db199SXin Li 	CHECK_IS_SGI40		= 0x00200000,
146*9c5db199SXin Li 	CHECK_IS_TXSTBC		= 0x00400000,
147*9c5db199SXin Li 	CHECK_RXSTBC		= 0x00800000,
148*9c5db199SXin Li 	CHECK_IS_DELBA		= 0x01000000,
149*9c5db199SXin Li 	/* NB: must be in upper 16-bits to avoid HT caps */
150*9c5db199SXin Li 	CHECK_IS_24GHZ		= 0x02000000,
151*9c5db199SXin Li 	CHECK_IS_5GHZ		= 0x04000000,
152*9c5db199SXin Li 	CHECK_IS_11B		= 0x08000000,
153*9c5db199SXin Li 	CHECK_IS_11G		= 0x10000000,
154*9c5db199SXin Li 	CHECK_IS_11A		= 0x20000000,
155*9c5db199SXin Li 	CHECK_IS_11N		= 0x40000000,
156*9c5db199SXin Li };
157*9c5db199SXin Li 
158*9c5db199SXin Li struct check {
159*9c5db199SXin Li 	const char *name;
160*9c5db199SXin Li 	int namelen;
161*9c5db199SXin Li 	int bits;
162*9c5db199SXin Li };
163*9c5db199SXin Li static const struct check checks[] = {
164*9c5db199SXin Li 	{ "24ghz", 5, CHECK_IS_24GHZ },
165*9c5db199SXin Li 	{ "5ghz", 4, CHECK_IS_5GHZ },
166*9c5db199SXin Li 	{ "11b", 3, CHECK_IS_11B },
167*9c5db199SXin Li 	{ "11g", 3, CHECK_IS_11G },
168*9c5db199SXin Li 	{ "11a", 3, CHECK_IS_11A },
169*9c5db199SXin Li 	{ "11n", 3, CHECK_IS_11N },
170*9c5db199SXin Li 	{ "ht20", 4, CHECK_IS_HT20 },
171*9c5db199SXin Li 	{ "ht40", 4, CHECK_IS_HT40 },
172*9c5db199SXin Li 	{ "psmp", 5, CHECK_IS_PSMP },
173*9c5db199SXin Li 	{ "ampdu", 5, CHECK_IS_AMPDU },
174*9c5db199SXin Li 	{ "amsdu", 5, CHECK_IS_AMSDU },
175*9c5db199SXin Li 	{ "smps", 4, CHECK_IS_SMPS },
176*9c5db199SXin Li 	{ "sta", 3, CHECK_IS_STA },
177*9c5db199SXin Li 	{ "ap", 2, CHECK_IS_AP },
178*9c5db199SXin Li 	{ "ibss", 4, CHECK_IS_IBSS },
179*9c5db199SXin Li 	{ "mbss", 4, CHECK_IS_MBSS },
180*9c5db199SXin Li 	{ "mon", 3, CHECK_IS_MONITOR },
181*9c5db199SXin Li 	{ "bands", 4, CHECK_BANDS },
182*9c5db199SXin Li 	{ "freqs", 4, CHECK_FREQS },
183*9c5db199SXin Li 	{ "rates", 4, CHECK_RATES },
184*9c5db199SXin Li 	{ "mcs", 3, CHECK_MCS },
185*9c5db199SXin Li 	{ "ampdu_dens", 10, CHECK_AMPDU_DENS },
186*9c5db199SXin Li 	{ "ampdu_fact", 10, CHECK_AMPDU_FACT },
187*9c5db199SXin Li 	{ "amsdu_len", 9, CHECK_AMSDU_LEN },
188*9c5db199SXin Li 	{ "lpdc", 4, CHECK_IS_LPDC },
189*9c5db199SXin Li 	{ "green", 5, CHECK_IS_GREENFIELD },
190*9c5db199SXin Li 	{ "sgi20", 5, CHECK_IS_SGI20 },
191*9c5db199SXin Li 	{ "sgi40", 5, CHECK_IS_SGI40 },
192*9c5db199SXin Li 	{ "txstbc", 6, CHECK_IS_TXSTBC },
193*9c5db199SXin Li 	{ "rxstbc", 6, CHECK_RXSTBC },
194*9c5db199SXin Li 	{ "delba", 5, CHECK_IS_DELBA },
195*9c5db199SXin Li 	{ "all", 3, -1 },
196*9c5db199SXin Li 	{ NULL }
197*9c5db199SXin Li };
198*9c5db199SXin Li 
find_check_byname(const char * name)199*9c5db199SXin Li static const struct check *find_check_byname(const char *name)
200*9c5db199SXin Li {
201*9c5db199SXin Li 	const struct check *p;
202*9c5db199SXin Li 
203*9c5db199SXin Li 	for (p = checks; p->name != NULL; p++)
204*9c5db199SXin Li 		if (strncasecmp(p->name, name, p->namelen) == 0)
205*9c5db199SXin Li 			return p;
206*9c5db199SXin Li 	return NULL;
207*9c5db199SXin Li }
208*9c5db199SXin Li 
209*9c5db199SXin Li #if 0
210*9c5db199SXin Li static const struct check *find_check_bybits(int bits)
211*9c5db199SXin Li {
212*9c5db199SXin Li 	const struct check *p;
213*9c5db199SXin Li 
214*9c5db199SXin Li 	for (p = checks; p->name != NULL; p++)
215*9c5db199SXin Li 		if (p->bits == bits)
216*9c5db199SXin Li 			return p;
217*9c5db199SXin Li 	return NULL;
218*9c5db199SXin Li }
219*9c5db199SXin Li #endif
220*9c5db199SXin Li 
check_iftype(struct nlattr * tb_msg[],int nl_type)221*9c5db199SXin Li static int check_iftype(struct nlattr *tb_msg[], int nl_type)
222*9c5db199SXin Li {
223*9c5db199SXin Li 	struct nlattr *nl_mode;
224*9c5db199SXin Li 	int rem_mode;
225*9c5db199SXin Li 
226*9c5db199SXin Li 	if (!tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES])
227*9c5db199SXin Li 		return 0;
228*9c5db199SXin Li 
229*9c5db199SXin Li 	nla_for_each_nested(nl_mode, tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES], rem_mode)
230*9c5db199SXin Li 		if (nl_mode->nla_type == nl_type)
231*9c5db199SXin Li 			return 1;
232*9c5db199SXin Li 	return 0;
233*9c5db199SXin Li }
234*9c5db199SXin Li 
get_max_mcs(unsigned char * mcs)235*9c5db199SXin Li static unsigned int get_max_mcs(unsigned char *mcs)
236*9c5db199SXin Li {
237*9c5db199SXin Li 	unsigned int mcs_bit, max;
238*9c5db199SXin Li 
239*9c5db199SXin Li 	max = 0;
240*9c5db199SXin Li 	for (mcs_bit = 0; mcs_bit <= 76; mcs_bit++) {
241*9c5db199SXin Li 		unsigned int mcs_octet = mcs_bit/8;
242*9c5db199SXin Li 		unsigned int MCS_RATE_BIT = 1 << mcs_bit % 8;
243*9c5db199SXin Li 		bool mcs_rate_idx_set;
244*9c5db199SXin Li 
245*9c5db199SXin Li 		mcs_rate_idx_set = !!(mcs[mcs_octet] & MCS_RATE_BIT);
246*9c5db199SXin Li 
247*9c5db199SXin Li 		if (!mcs_rate_idx_set)
248*9c5db199SXin Li 			continue;
249*9c5db199SXin Li 
250*9c5db199SXin Li 		if (mcs_bit > max)
251*9c5db199SXin Li 			max = mcs_bit;
252*9c5db199SXin Li 	}
253*9c5db199SXin Li 	return max;
254*9c5db199SXin Li }
255*9c5db199SXin Li 
pbool(const char * tag,int v)256*9c5db199SXin Li static void pbool(const char *tag, int v)
257*9c5db199SXin Li {
258*9c5db199SXin Li 	printf("%s: %s\n", tag, v ? "true" : "false");
259*9c5db199SXin Li }
260*9c5db199SXin Li 
pint(const char * tag,int v)261*9c5db199SXin Li static void pint(const char *tag, int v)
262*9c5db199SXin Li {
263*9c5db199SXin Li 	printf("%s: %d\n", tag, v);
264*9c5db199SXin Li }
265*9c5db199SXin Li 
prate(const char * tag,int v)266*9c5db199SXin Li static void prate(const char *tag, int v)
267*9c5db199SXin Li {
268*9c5db199SXin Li 	printf("%s: %2.1f\n", tag, 0.1*v);
269*9c5db199SXin Li }
270*9c5db199SXin Li 
check_phy_handler(struct nl_msg * msg,void * arg)271*9c5db199SXin Li static int check_phy_handler(struct nl_msg *msg, void *arg)
272*9c5db199SXin Li {
273*9c5db199SXin Li 	struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
274*9c5db199SXin Li 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
275*9c5db199SXin Li 	uintptr_t checks = (uintptr_t) arg;
276*9c5db199SXin Li 
277*9c5db199SXin Li 	struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
278*9c5db199SXin Li 
279*9c5db199SXin Li 	struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
280*9c5db199SXin Li 	static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
281*9c5db199SXin Li 		[NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
282*9c5db199SXin Li 		[NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
283*9c5db199SXin Li 		[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG },
284*9c5db199SXin Li 		[NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG },
285*9c5db199SXin Li 		[NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
286*9c5db199SXin Li 		[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
287*9c5db199SXin Li 	};
288*9c5db199SXin Li 
289*9c5db199SXin Li 	struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1];
290*9c5db199SXin Li 	static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = {
291*9c5db199SXin Li 		[NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 },
292*9c5db199SXin Li 		[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = { .type = NLA_FLAG },
293*9c5db199SXin Li 	};
294*9c5db199SXin Li 
295*9c5db199SXin Li 	struct nlattr *nl_band;
296*9c5db199SXin Li 	struct nlattr *nl_freq;
297*9c5db199SXin Li 	struct nlattr *nl_rate;
298*9c5db199SXin Li 	int rem_band, rem_freq, rem_rate, phy_caps;
299*9c5db199SXin Li 	int amsdu_len, ampdu_fact, ampdu_dens, max_mcs, max_rate;
300*9c5db199SXin Li 
301*9c5db199SXin Li 	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
302*9c5db199SXin Li 		  genlmsg_attrlen(gnlh, 0), NULL);
303*9c5db199SXin Li 
304*9c5db199SXin Li 	if (!tb_msg[NL80211_ATTR_WIPHY_BANDS])
305*9c5db199SXin Li 		return NL_SKIP;
306*9c5db199SXin Li 
307*9c5db199SXin Li 	phy_caps = 0;
308*9c5db199SXin Li 	amsdu_len = 0;
309*9c5db199SXin Li 	ampdu_fact = 0;
310*9c5db199SXin Li 	ampdu_dens = 0;
311*9c5db199SXin Li 	max_mcs = 0;
312*9c5db199SXin Li 	max_rate = 0;
313*9c5db199SXin Li 	/* NB: merge each band's findings; this stuff is silly */
314*9c5db199SXin Li 	nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) {
315*9c5db199SXin Li 		nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
316*9c5db199SXin Li 			  nla_len(nl_band), NULL);
317*9c5db199SXin Li 
318*9c5db199SXin Li 		if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) {
319*9c5db199SXin Li 			unsigned short caps = nla_get_u16(tb_band[NL80211_BAND_ATTR_HT_CAPA]);
320*9c5db199SXin Li 			int len;
321*9c5db199SXin Li 
322*9c5db199SXin Li 			/* XXX not quite right but close enough */
323*9c5db199SXin Li 			phy_caps |= CHECK_IS_11N | caps;
324*9c5db199SXin Li 			len = 0xeff + ((caps & 0x0800) << 1);
325*9c5db199SXin Li 			if (len > amsdu_len)
326*9c5db199SXin Li 				amsdu_len = len;
327*9c5db199SXin Li 		}
328*9c5db199SXin Li 		if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]) {
329*9c5db199SXin Li 			unsigned char factor = nla_get_u8(tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]);
330*9c5db199SXin Li 			int fact = (1<<(13+factor))-1;
331*9c5db199SXin Li 			if (fact > ampdu_fact)
332*9c5db199SXin Li 				ampdu_fact = fact;
333*9c5db199SXin Li 		}
334*9c5db199SXin Li 		if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]) {
335*9c5db199SXin Li 			unsigned char dens = nla_get_u8(tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]);
336*9c5db199SXin Li 			if (dens > ampdu_dens)
337*9c5db199SXin Li 				ampdu_dens = dens;
338*9c5db199SXin Li 		}
339*9c5db199SXin Li 		if (tb_band[NL80211_BAND_ATTR_HT_MCS_SET] &&
340*9c5db199SXin Li 		    nla_len(tb_band[NL80211_BAND_ATTR_HT_MCS_SET]) == 16) {
341*9c5db199SXin Li 			/* As defined in 7.3.2.57.4 Supported MCS Set field */
342*9c5db199SXin Li 			unsigned char *mcs = nla_data(tb_band[NL80211_BAND_ATTR_HT_MCS_SET]);
343*9c5db199SXin Li 			int max = get_max_mcs(&mcs[0]);
344*9c5db199SXin Li 			if (max > max_mcs)
345*9c5db199SXin Li 				max_mcs = max;
346*9c5db199SXin Li 		}
347*9c5db199SXin Li 
348*9c5db199SXin Li 		nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
349*9c5db199SXin Li 			uint32_t freq;
350*9c5db199SXin Li 
351*9c5db199SXin Li 			nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX,
352*9c5db199SXin Li 			    nla_data(nl_freq), nla_len(nl_freq),
353*9c5db199SXin Li 			    freq_policy);
354*9c5db199SXin Li 			if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
355*9c5db199SXin Li 				continue;
356*9c5db199SXin Li #if 0
357*9c5db199SXin Li 			/* NB: we care about device caps, not regulatory */
358*9c5db199SXin Li 			if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
359*9c5db199SXin Li 				continue;
360*9c5db199SXin Li #endif
361*9c5db199SXin Li 			freq = nla_get_u32(
362*9c5db199SXin Li 			    tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
363*9c5db199SXin Li 			if (checks & CHECK_FREQS)
364*9c5db199SXin Li 				pint("freq", freq);
365*9c5db199SXin Li 
366*9c5db199SXin Li 			/* NB: approximate band boundaries, we get no help */
367*9c5db199SXin Li 			if (2000 <= freq && freq <= 3000)
368*9c5db199SXin Li 				phy_caps |= CHECK_IS_24GHZ;
369*9c5db199SXin Li 			else if (4000 <= freq && freq <= 6000)
370*9c5db199SXin Li 				phy_caps |= CHECK_IS_5GHZ;
371*9c5db199SXin Li 		}
372*9c5db199SXin Li 
373*9c5db199SXin Li 		nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) {
374*9c5db199SXin Li 			int rate;
375*9c5db199SXin Li 
376*9c5db199SXin Li 			nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate),
377*9c5db199SXin Li 				  nla_len(nl_rate), rate_policy);
378*9c5db199SXin Li 			if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
379*9c5db199SXin Li 				continue;
380*9c5db199SXin Li 			rate = nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE]);
381*9c5db199SXin Li 			if (rate > max_rate)
382*9c5db199SXin Li 				max_rate = rate;
383*9c5db199SXin Li 		}
384*9c5db199SXin Li 	}
385*9c5db199SXin Li #if 0
386*9c5db199SXin Li 	/* NB: 11n =>'s legacy support */
387*9c5db199SXin Li 	if (phy_caps & CHECK_IS_11N) {
388*9c5db199SXin Li 		if (phy_caps & CHECK_IS_24GHZ)
389*9c5db199SXin Li 			phy_caps |= CHECK_IS_11B | CHECK_IS_11G;
390*9c5db199SXin Li 		if (phy_caps & CHECK_IS_5GHZ)
391*9c5db199SXin Li 			phy_caps |= CHECK_IS_11A;
392*9c5db199SXin Li 	}
393*9c5db199SXin Li #else
394*9c5db199SXin Li 	/* XXX no way to figure this out; just force 'em */
395*9c5db199SXin Li 	if (phy_caps & CHECK_IS_24GHZ)
396*9c5db199SXin Li 		phy_caps |= CHECK_IS_11B | CHECK_IS_11G;
397*9c5db199SXin Li 	if (phy_caps & CHECK_IS_5GHZ)
398*9c5db199SXin Li 		phy_caps |= CHECK_IS_11A;
399*9c5db199SXin Li #endif
400*9c5db199SXin Li 
401*9c5db199SXin Li #define	PBOOL(c, b, name) if (checks & (c)) pbool(name, phy_caps & (b))
402*9c5db199SXin Li 	PBOOL(CHECK_IS_24GHZ, CHECK_IS_24GHZ, "24ghz");
403*9c5db199SXin Li 	PBOOL(CHECK_IS_5GHZ, CHECK_IS_5GHZ, "5ghz");
404*9c5db199SXin Li 	PBOOL(CHECK_IS_11B, CHECK_IS_11B, "11b");
405*9c5db199SXin Li 	PBOOL(CHECK_IS_11G, CHECK_IS_11G, "11g");
406*9c5db199SXin Li 	PBOOL(CHECK_IS_11A, CHECK_IS_11A, "11a");
407*9c5db199SXin Li 	PBOOL(CHECK_IS_11N, CHECK_IS_11N, "11n");
408*9c5db199SXin Li 	PBOOL(CHECK_IS_LPDC, 0x1, "lpdc");
409*9c5db199SXin Li 	PBOOL(CHECK_IS_HT20, CHECK_IS_11N, "ht20");
410*9c5db199SXin Li 	PBOOL(CHECK_IS_HT40, 0x2, "ht40");
411*9c5db199SXin Li 	if (checks & CHECK_IS_SMPS)
412*9c5db199SXin Li 		pbool("smps", ((phy_caps & 0x000c) >> 2) < 2);
413*9c5db199SXin Li 	PBOOL(CHECK_IS_GREENFIELD, 0x10, "green");
414*9c5db199SXin Li 	PBOOL(CHECK_IS_SGI20, 0x20, "sgi20");
415*9c5db199SXin Li 	PBOOL(CHECK_IS_SGI40, 0x40, "sgi40");
416*9c5db199SXin Li 	PBOOL(CHECK_IS_TXSTBC, 0x40, "txstbc");
417*9c5db199SXin Li 	PBOOL(CHECK_RXSTBC, 0x300, "rxstbc");
418*9c5db199SXin Li 	PBOOL(CHECK_IS_DELBA, 0x400, "delba");
419*9c5db199SXin Li #if 0
420*9c5db199SXin Li 	PBOOL(CHECK_IS_DSSCCK, 0x1000, "dsscck");
421*9c5db199SXin Li #endif
422*9c5db199SXin Li 	PBOOL(CHECK_IS_PSMP, 0x2000, "psmp");
423*9c5db199SXin Li #if 0
424*9c5db199SXin Li 	PBOOL(CHECK_IS_INTOL, 0x4000, "intol");
425*9c5db199SXin Li #endif
426*9c5db199SXin Li #if 0
427*9c5db199SXin Li 	PBOOL(CHECK_IS_LSIGTXOP, 0x8000, "lsigtxop");
428*9c5db199SXin Li #endif
429*9c5db199SXin Li #undef PBOOL
430*9c5db199SXin Li 	if (checks & CHECK_AMSDU_LEN)
431*9c5db199SXin Li 		pint("amsdu_len", amsdu_len);
432*9c5db199SXin Li 	if (checks & CHECK_AMPDU_FACT)
433*9c5db199SXin Li 		pint("ampdu_fact", ampdu_fact);
434*9c5db199SXin Li 	if (checks & CHECK_AMPDU_DENS)
435*9c5db199SXin Li 		pint("ampdu_dens", ampdu_dens);
436*9c5db199SXin Li 	if (checks & CHECK_RATES)
437*9c5db199SXin Li 		prate("rate", max_rate);
438*9c5db199SXin Li 	if (checks & CHECK_MCS)
439*9c5db199SXin Li 		pint("mcs", max_mcs);
440*9c5db199SXin Li 
441*9c5db199SXin Li 	if (checks & CHECK_IS_STA)
442*9c5db199SXin Li 		pbool("sta", check_iftype(tb_msg, NL80211_IFTYPE_STATION));
443*9c5db199SXin Li 	if (checks & CHECK_IS_IBSS)
444*9c5db199SXin Li 		pbool("ibss", check_iftype(tb_msg, NL80211_IFTYPE_ADHOC));
445*9c5db199SXin Li 	if (checks & CHECK_IS_AP)
446*9c5db199SXin Li 		pbool("ap", check_iftype(tb_msg, NL80211_IFTYPE_AP));
447*9c5db199SXin Li 	if (checks & CHECK_IS_MBSS)
448*9c5db199SXin Li 		pbool("mbss", check_iftype(tb_msg, NL80211_IFTYPE_MESH_POINT));
449*9c5db199SXin Li 	if (checks & CHECK_IS_MONITOR)
450*9c5db199SXin Li 		pbool("mon", check_iftype(tb_msg, NL80211_IFTYPE_MONITOR));
451*9c5db199SXin Li 
452*9c5db199SXin Li 	return NL_SKIP;
453*9c5db199SXin Li }
454*9c5db199SXin Li 
check_phy_caps(struct nl80211_state * state,struct nl_cb * cb,struct nl_msg * msg,int argc,char ** argv)455*9c5db199SXin Li static int check_phy_caps(struct nl80211_state *state,
456*9c5db199SXin Li 		       struct nl_cb *cb,
457*9c5db199SXin Li 		       struct nl_msg *msg,
458*9c5db199SXin Li 		       int argc, char **argv)
459*9c5db199SXin Li {
460*9c5db199SXin Li 	int checks = 0;
461*9c5db199SXin Li 	for (; argc > 0; argc--, argv++) {
462*9c5db199SXin Li 		const struct check *p = find_check_byname(argv[0]);
463*9c5db199SXin Li 		if (p == NULL) {
464*9c5db199SXin Li 			fprintf(stderr, "invalid check %s\n", argv[0]);
465*9c5db199SXin Li 			return 3;		/* XXX whatever? */
466*9c5db199SXin Li 		}
467*9c5db199SXin Li 		checks |= p->bits;
468*9c5db199SXin Li 	}
469*9c5db199SXin Li 	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, check_phy_handler,
470*9c5db199SXin Li 	    (void *)(uintptr_t) checks);
471*9c5db199SXin Li 	return 0;
472*9c5db199SXin Li }
473*9c5db199SXin Li 
error_handler(struct sockaddr_nl * nla,struct nlmsgerr * err,void * arg)474*9c5db199SXin Li static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
475*9c5db199SXin Li 			 void *arg)
476*9c5db199SXin Li {
477*9c5db199SXin Li 	int *ret = arg;
478*9c5db199SXin Li 	*ret = err->error;
479*9c5db199SXin Li 	return NL_STOP;
480*9c5db199SXin Li }
481*9c5db199SXin Li 
finish_handler(struct nl_msg * msg,void * arg)482*9c5db199SXin Li static int finish_handler(struct nl_msg *msg, void *arg)
483*9c5db199SXin Li {
484*9c5db199SXin Li 	int *ret = arg;
485*9c5db199SXin Li 	*ret = 0;
486*9c5db199SXin Li 	return NL_SKIP;
487*9c5db199SXin Li }
488*9c5db199SXin Li 
ack_handler(struct nl_msg * msg,void * arg)489*9c5db199SXin Li static int ack_handler(struct nl_msg *msg, void *arg)
490*9c5db199SXin Li {
491*9c5db199SXin Li 	int *ret = arg;
492*9c5db199SXin Li 	*ret = 0;
493*9c5db199SXin Li 	return NL_STOP;
494*9c5db199SXin Li }
495*9c5db199SXin Li 
__handle_cmd(struct nl80211_state * state,int argc,char ** argv)496*9c5db199SXin Li static int __handle_cmd(struct nl80211_state *state, int argc, char **argv)
497*9c5db199SXin Li {
498*9c5db199SXin Li 	struct nl_cb *cb;
499*9c5db199SXin Li 	struct nl_msg *msg;
500*9c5db199SXin Li 	int devidx, err;
501*9c5db199SXin Li 
502*9c5db199SXin Li 	if (argc <= 1)
503*9c5db199SXin Li 		return 1;
504*9c5db199SXin Li 
505*9c5db199SXin Li 	devidx = phy_lookup(*argv);
506*9c5db199SXin Li 	if (devidx < 0)
507*9c5db199SXin Li 		return -errno;
508*9c5db199SXin Li 	argc--, argv++;
509*9c5db199SXin Li 
510*9c5db199SXin Li 	msg = nlmsg_alloc();
511*9c5db199SXin Li 	if (!msg) {
512*9c5db199SXin Li 		fprintf(stderr, "failed to allocate netlink message\n");
513*9c5db199SXin Li 		return 2;
514*9c5db199SXin Li 	}
515*9c5db199SXin Li 
516*9c5db199SXin Li 	cb = nl_cb_alloc(NL_CB_DEFAULT);
517*9c5db199SXin Li 	if (!cb) {
518*9c5db199SXin Li 		fprintf(stderr, "failed to allocate netlink callbacks\n");
519*9c5db199SXin Li 		err = 2;
520*9c5db199SXin Li 		goto out_free_msg;
521*9c5db199SXin Li 	}
522*9c5db199SXin Li 
523*9c5db199SXin Li 	genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0,
524*9c5db199SXin Li 		    0, NL80211_CMD_GET_WIPHY, 0);
525*9c5db199SXin Li 	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, devidx);
526*9c5db199SXin Li 
527*9c5db199SXin Li 	err = check_phy_caps(state, cb, msg, argc, argv);
528*9c5db199SXin Li 	if (err)
529*9c5db199SXin Li 		goto out;
530*9c5db199SXin Li 
531*9c5db199SXin Li 	err = nl_send_auto_complete(state->nl_sock, msg);
532*9c5db199SXin Li 	if (err < 0)
533*9c5db199SXin Li 		goto out;
534*9c5db199SXin Li 
535*9c5db199SXin Li 	err = 1;
536*9c5db199SXin Li 
537*9c5db199SXin Li 	nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
538*9c5db199SXin Li 	nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
539*9c5db199SXin Li 	nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
540*9c5db199SXin Li 
541*9c5db199SXin Li 	while (err > 0)
542*9c5db199SXin Li 		nl_recvmsgs(state->nl_sock, cb);
543*9c5db199SXin Li  out:
544*9c5db199SXin Li 	nl_cb_put(cb);
545*9c5db199SXin Li  out_free_msg:
546*9c5db199SXin Li 	nlmsg_free(msg);
547*9c5db199SXin Li 	return err;
548*9c5db199SXin Li  nla_put_failure:
549*9c5db199SXin Li 	fprintf(stderr, "building message failed\n");
550*9c5db199SXin Li 	return 2;
551*9c5db199SXin Li }
552*9c5db199SXin Li 
main(int argc,char ** argv)553*9c5db199SXin Li int main(int argc, char **argv)
554*9c5db199SXin Li {
555*9c5db199SXin Li 	struct nl80211_state nlstate;
556*9c5db199SXin Li 	int err;
557*9c5db199SXin Li 
558*9c5db199SXin Li 	argc--;
559*9c5db199SXin Li 	argv0 = *argv++;
560*9c5db199SXin Li 
561*9c5db199SXin Li 	err = nl80211_init(&nlstate);
562*9c5db199SXin Li 	if (err == 0) {
563*9c5db199SXin Li 		if (argc > 1 && strncmp(*argv, "phy", 3) == 0) {
564*9c5db199SXin Li 			err = __handle_cmd(&nlstate, argc, argv);
565*9c5db199SXin Li 			if (err < 0)
566*9c5db199SXin Li 				fprintf(stderr, "command failed: %s (%d)\n",
567*9c5db199SXin Li 				    strerror(-err), err);
568*9c5db199SXin Li 			else if (err)
569*9c5db199SXin Li 				fprintf(stderr, "command failed: err %d\n", err);
570*9c5db199SXin Li 		} else {
571*9c5db199SXin Li 			fprintf(stderr, "usage: %s phyX [args]\n", argv0);
572*9c5db199SXin Li 			err = 1;
573*9c5db199SXin Li 		}
574*9c5db199SXin Li 		nl80211_cleanup(&nlstate);
575*9c5db199SXin Li 	}
576*9c5db199SXin Li 	return err;
577*9c5db199SXin Li }
578