• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
nl_socket_alloc(void)32 static inline struct nl_handle *nl_socket_alloc(void)
33 {
34 	return nl_handle_alloc();
35 }
36 
nl_socket_free(struct nl_sock * h)37 static inline void nl_socket_free(struct nl_sock *h)
38 {
39 	nl_handle_destroy(h);
40 }
41 
__genl_ctrl_alloc_cache(struct nl_sock * h,struct nl_cache ** cache)42 static 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 
53 struct nl80211_state {
54 	struct nl_sock *nl_sock;
55 	struct nl_cache *nl_cache;
56 	struct genl_family *nl80211;
57 };
58 
nl80211_init(struct nl80211_state * state)59 static 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 
nl80211_cleanup(struct nl80211_state * state)97 static 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 
104 static const char *argv0;
105 
phy_lookup(char * name)106 static 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 
123 enum {
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 
158 struct check {
159 	const char *name;
160 	int namelen;
161 	int bits;
162 };
163 static 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 
find_check_byname(const char * name)199 static 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
210 static 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 
check_iftype(struct nlattr * tb_msg[],int nl_type)221 static 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 
get_max_mcs(unsigned char * mcs)235 static 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 
pbool(const char * tag,int v)256 static void pbool(const char *tag, int v)
257 {
258 	printf("%s: %s\n", tag, v ? "true" : "false");
259 }
260 
pint(const char * tag,int v)261 static void pint(const char *tag, int v)
262 {
263 	printf("%s: %d\n", tag, v);
264 }
265 
prate(const char * tag,int v)266 static void prate(const char *tag, int v)
267 {
268 	printf("%s: %2.1f\n", tag, 0.1*v);
269 }
270 
check_phy_handler(struct nl_msg * msg,void * arg)271 static 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 
check_phy_caps(struct nl80211_state * state,struct nl_cb * cb,struct nl_msg * msg,int argc,char ** argv)455 static 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 
error_handler(struct sockaddr_nl * nla,struct nlmsgerr * err,void * arg)474 static 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 
finish_handler(struct nl_msg * msg,void * arg)482 static int finish_handler(struct nl_msg *msg, void *arg)
483 {
484 	int *ret = arg;
485 	*ret = 0;
486 	return NL_SKIP;
487 }
488 
ack_handler(struct nl_msg * msg,void * arg)489 static int ack_handler(struct nl_msg *msg, void *arg)
490 {
491 	int *ret = arg;
492 	*ret = 0;
493 	return NL_STOP;
494 }
495 
__handle_cmd(struct nl80211_state * state,int argc,char ** argv)496 static 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 
main(int argc,char ** argv)553 int 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