Home | History | Annotate | Download | only in src
      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 (at) 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 
     32 static inline struct nl_handle *nl_socket_alloc(void)
     33 {
     34 	return nl_handle_alloc();
     35 }
     36 
     37 static inline void nl_socket_free(struct nl_sock *h)
     38 {
     39 	nl_handle_destroy(h);
     40 }
     41 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    256 static void pbool(const char *tag, int v)
    257 {
    258 	printf("%s: %s\n", tag, v ? "true" : "false");
    259 }
    260 
    261 static void pint(const char *tag, int v)
    262 {
    263 	printf("%s: %d\n", tag, v);
    264 }
    265 
    266 static void prate(const char *tag, int v)
    267 {
    268 	printf("%s: %2.1f\n", tag, 0.1*v);
    269 }
    270 
    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 
    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 
    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 
    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 
    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 
    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 
    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