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