#include <stdbool.h>
#include <errno.h>
#include <strings.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>

#include "nl80211.h"
#include "iw.h"

struct channels_ctx {
	int last_band;
	bool width_40;
	bool width_80;
	bool width_160;
};

static char *dfs_state_name(enum nl80211_dfs_state state)
{
	switch (state) {
	case NL80211_DFS_USABLE:
		return "usable";
	case NL80211_DFS_AVAILABLE:
		return "available";
	case NL80211_DFS_UNAVAILABLE:
		return "unavailable";
	default:
		return "unknown";
	}
}

static int print_channels_handler(struct nl_msg *msg, void *arg)
{
	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
	struct channels_ctx *ctx = arg;
	struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
	struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
	struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
	struct nlattr *nl_band;
	struct nlattr *nl_freq;
	int rem_band, rem_freq;

	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL);

	if (tb_msg[NL80211_ATTR_WIPHY_BANDS]) {
		nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) {
			if (ctx->last_band != nl_band->nla_type) {
				printf("Band %d:\n", nl_band->nla_type + 1);
				ctx->width_40 = false;
				ctx->width_80 = false;
				ctx->width_160 = false;
				ctx->last_band = nl_band->nla_type;
			}

			nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), nla_len(nl_band), NULL);

			if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) {
				__u16 cap = nla_get_u16(tb_band[NL80211_BAND_ATTR_HT_CAPA]);

				if (cap & BIT(1))
					ctx->width_40 = true;
			}

			if (tb_band[NL80211_BAND_ATTR_VHT_CAPA]) {
				__u32 capa;

				ctx->width_80 = true;

				capa = nla_get_u32(tb_band[NL80211_BAND_ATTR_VHT_CAPA]);
				switch ((capa >> 2) & 3) {
				case 2:
					/* width_80p80 = true; */
					/* fall through */
				case 1:
					ctx->width_160 = true;
				break;
				}
			}

			if (tb_band[NL80211_BAND_ATTR_FREQS]) {
				nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
					uint32_t freq;

					nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), nla_len(nl_freq), NULL);

					if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
						continue;
					freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
					printf("\t* %d MHz [%d] ", freq, ieee80211_frequency_to_channel(freq));

					if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) {
						printf("(disabled)\n");
						continue;
					}
					printf("\n");

					if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER])
						printf("\t  Maximum TX power: %.1f dBm\n", 0.01 * nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]));

					/* If both flags are set assume an new kernel */
					if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR] && tb_freq[__NL80211_FREQUENCY_ATTR_NO_IBSS]) {
						printf("\t  No IR\n");
					} else if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN]) {
						printf("\t  Passive scan\n");
					} else if (tb_freq[__NL80211_FREQUENCY_ATTR_NO_IBSS]){
						printf("\t  No IBSS\n");
					}

					if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
						printf("\t  Radar detection\n");

					printf("\t  Channel widths:");
					if (!tb_freq[NL80211_FREQUENCY_ATTR_NO_20MHZ])
						printf(" 20MHz");
					if (ctx->width_40 && !tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS])
						printf(" HT40-");
					if (ctx->width_40 && !tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_PLUS])
						printf(" HT40+");
					if (ctx->width_80 && !tb_freq[NL80211_FREQUENCY_ATTR_NO_80MHZ])
						printf(" VHT80");
					if (ctx->width_160 && !tb_freq[NL80211_FREQUENCY_ATTR_NO_160MHZ])
						printf(" VHT160");
					printf("\n");

					if (!tb_freq[NL80211_FREQUENCY_ATTR_DISABLED] && tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) {
						enum nl80211_dfs_state state = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]);
						unsigned long time;

						printf("\t  DFS state: %s", dfs_state_name(state));
						if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_TIME]) {
							time = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_TIME]);
							printf(" (for %lu sec)", time / 1000);
						}
						printf("\n");
						if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME])
							printf("\t  DFS CAC time: %u ms\n",
							       nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]));
					}
				}
			}
		}
	}

	return NL_SKIP;
}

static int handle_channels(struct nl80211_state *state, struct nl_msg *msg,
			   int argc, char **argv, enum id_input id)
{
	static struct channels_ctx ctx = {
		.last_band = -1,
	};

	nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP);
	nlmsg_hdr(msg)->nlmsg_flags |= NLM_F_DUMP;

	register_handler(print_channels_handler, &ctx);

	return 0;
}
TOPLEVEL(channels, NULL, NL80211_CMD_GET_WIPHY, 0, CIB_PHY, handle_channels, "Show available channels.");

static int handle_name(struct nl80211_state *state,
		       struct nl_msg *msg,
		       int argc, char **argv,
		       enum id_input id)
{
	if (argc != 1)
		return 1;

	NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, *argv);

	return 0;
 nla_put_failure:
	return -ENOBUFS;
}
COMMAND(set, name, "<new name>", NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_name,
	"Rename this wireless device.");

static int handle_freq(struct nl80211_state *state, struct nl_msg *msg,
		       int argc, char **argv,
		       enum id_input id)
{
	struct chandef chandef;
	int res;

	res = parse_freqchan(&chandef, false, argc, argv, NULL);
	if (res)
		return res;

	return put_chandef(msg, &chandef);
}

COMMAND(set, freq,
	"<freq> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]\n"
	"<control freq> [5|10|20|40|80|80+80|160] [<center1_freq> [<center2_freq>]]",
	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_freq,
	"Set frequency/channel the hardware is using, including HT\n"
	"configuration.");
COMMAND(set, freq,
	"<freq> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]\n"
	"<control freq> [5|10|20|40|80|80+80|160] [<center1_freq> [<center2_freq>]]",
	NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_freq, NULL);

static int handle_chan(struct nl80211_state *state, struct nl_msg *msg,
		       int argc, char **argv,
		       enum id_input id)
{
	struct chandef chandef;
	int res;

	res = parse_freqchan(&chandef, true, argc, argv, NULL);
	if (res)
		return res;

	return put_chandef(msg, &chandef);
}
COMMAND(set, channel, "<channel> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]",
	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_chan, NULL);
COMMAND(set, channel, "<channel> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]",
	NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_chan, NULL);


struct cac_event {
	int ret;
	uint32_t freq;
};

static int print_cac_event(struct nl_msg *msg, void *arg)
{
	struct nlattr *tb[NL80211_ATTR_MAX + 1];
	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
	enum nl80211_radar_event event_type;
	struct cac_event *cac_event = arg;
	uint32_t freq;

	if (gnlh->cmd != NL80211_CMD_RADAR_DETECT)
		return NL_SKIP;

	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
		  genlmsg_attrlen(gnlh, 0), NULL);

	if (!tb[NL80211_ATTR_RADAR_EVENT] || !tb[NL80211_ATTR_WIPHY_FREQ])
		return NL_SKIP;

	freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
	event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]);
	if (freq != cac_event->freq)
		return NL_SKIP;

	switch (event_type) {
	case NL80211_RADAR_DETECTED:
		printf("%d MHz: radar detected\n", freq);
		break;
	case NL80211_RADAR_CAC_FINISHED:
		printf("%d MHz: CAC finished\n", freq);
		break;
	case NL80211_RADAR_CAC_ABORTED:
		printf("%d MHz: CAC was aborted\n", freq);
		break;
	case NL80211_RADAR_NOP_FINISHED:
		printf("%d MHz: NOP finished\n", freq);
		break;
	default:
		printf("%d MHz: unknown radar event\n", freq);
	}
	cac_event->ret = 0;

	return NL_SKIP;
}

static int handle_cac_trigger(struct nl80211_state *state,
			    struct nl_msg *msg,
			    int argc, char **argv,
			    enum id_input id)
{
	struct chandef chandef;
	int res;

	if (argc < 2)
		return 1;

	if (strcmp(argv[0], "channel") == 0) {
		res = parse_freqchan(&chandef, true, argc - 1, argv + 1, NULL);
	} else if (strcmp(argv[0], "freq") == 0) {
		res = parse_freqchan(&chandef, false, argc - 1, argv + 1, NULL);
	} else {
		return 1;
	}

	if (res)
		return res;

	return put_chandef(msg, &chandef);
}

static int no_seq_check(struct nl_msg *msg, void *arg)
{
	return NL_OK;
}

static int handle_cac(struct nl80211_state *state,
		      struct nl_msg *msg,
		      int argc, char **argv,
		      enum id_input id)
{
	int err;
	struct nl_cb *radar_cb;
	struct chandef chandef;
	struct cac_event cac_event;
	char **cac_trigger_argv = NULL;

	radar_cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT);
	if (!radar_cb)
		return 1;

	if (argc < 3)
		return 1;

	if (strcmp(argv[2], "channel") == 0) {
		err = parse_freqchan(&chandef, true, argc - 3, argv + 3, NULL);
	} else if (strcmp(argv[2], "freq") == 0) {
		err = parse_freqchan(&chandef, false, argc - 3, argv + 3, NULL);
	} else {
		err = 1;
	}
	if (err)
		goto err_out;

	cac_trigger_argv = calloc(argc + 1, sizeof(char*));
	if (!cac_trigger_argv) {
		err = -ENOMEM;
		goto err_out;
	}

	cac_trigger_argv[0] = argv[0];
	cac_trigger_argv[1] = "cac";
	cac_trigger_argv[2] = "trigger";
	memcpy(&cac_trigger_argv[3], &argv[2], (argc - 2) * sizeof(char*));

	err = handle_cmd(state, id, argc + 1, cac_trigger_argv);
	if (err)
		goto err_out;

	cac_event.ret = 1;
	cac_event.freq = chandef.control_freq;

	__prepare_listen_events(state);
	nl_socket_set_cb(state->nl_sock, radar_cb);

	/* need to turn off sequence number checking */
	nl_cb_set(radar_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
	nl_cb_set(radar_cb, NL_CB_VALID, NL_CB_CUSTOM, print_cac_event, &cac_event);
	while (cac_event.ret > 0)
		nl_recvmsgs(state->nl_sock, radar_cb);

	err = 0;
err_out:
	if (radar_cb)
		nl_cb_put(radar_cb);
	if (cac_trigger_argv)
		free(cac_trigger_argv);
	return err;
}
TOPLEVEL(cac, "channel <channel> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]\n"
	      "freq <freq> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]\n"
	      "freq <control freq> [5|10|20|40|80|80+80|160] [<center1_freq> [<center2_freq>]]",
	 0, 0, CIB_NETDEV, handle_cac, NULL);
COMMAND(cac, trigger,
	"channel <channel> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]\n"
	"freq <frequency> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]\n"
	"freq <frequency> [5|10|20|40|80|80+80|160] [<center1_freq> [<center2_freq>]]",
	NL80211_CMD_RADAR_DETECT, 0, CIB_NETDEV, handle_cac_trigger,
	"Start or trigger a channel availability check (CAC) looking to look for\n"
	"radars on the given channel.");

static int handle_fragmentation(struct nl80211_state *state,
				struct nl_msg *msg,
				int argc, char **argv,
				enum id_input id)
{
	unsigned int frag;

	if (argc != 1)
		return 1;

	if (strcmp("off", argv[0]) == 0)
		frag = -1;
	else {
		char *end;

		if (!*argv[0])
			return 1;
		frag = strtoul(argv[0], &end, 10);
		if (*end != '\0')
			return 1;
	}

	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, frag);

	return 0;
 nla_put_failure:
	return -ENOBUFS;
}
COMMAND(set, frag, "<fragmentation threshold|off>",
	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_fragmentation,
	"Set fragmentation threshold.");

static int handle_rts(struct nl80211_state *state,
		      struct nl_msg *msg,
		      int argc, char **argv,
		      enum id_input id)
{
	unsigned int rts;

	if (argc != 1)
		return 1;

	if (strcmp("off", argv[0]) == 0)
		rts = -1;
	else {
		char *end;

		if (!*argv[0])
			return 1;
		rts = strtoul(argv[0], &end, 10);
		if (*end != '\0')
			return 1;
	}

	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, rts);

	return 0;
 nla_put_failure:
	return -ENOBUFS;
}
COMMAND(set, rts, "<rts threshold|off>",
	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_rts,
	"Set rts threshold.");

static int handle_retry(struct nl80211_state *state,
			struct nl_msg *msg,
			int argc, char **argv, enum id_input id)
{
	unsigned int retry_short = 0, retry_long = 0;
	bool have_retry_s = false, have_retry_l = false;
	int i;
	enum {
		S_NONE,
		S_SHORT,
		S_LONG,
	} parser_state = S_NONE;

	if (!argc || (argc != 2 && argc != 4))
		return 1;

	for (i = 0; i < argc; i++) {
		char *end;
		unsigned int tmpul;

		if (strcmp(argv[i], "short") == 0) {
			if (have_retry_s)
				return 1;
			parser_state = S_SHORT;
			have_retry_s = true;
		} else if (strcmp(argv[i], "long") == 0) {
			if (have_retry_l)
				return 1;
			parser_state = S_LONG;
			have_retry_l = true;
		} else {
			tmpul = strtoul(argv[i], &end, 10);
			if (*end != '\0')
				return 1;
			if (!tmpul || tmpul > 255)
				return -EINVAL;
			switch (parser_state) {
			case S_SHORT:
				retry_short = tmpul;
				break;
			case S_LONG:
				retry_long = tmpul;
				break;
			default:
				return 1;
			}
		}
	}

	if (!have_retry_s && !have_retry_l)
		return 1;
	if (have_retry_s)
		NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT, retry_short);
	if (have_retry_l)
		NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG, retry_long);

	return 0;
 nla_put_failure:
	return -ENOBUFS;
}
COMMAND(set, retry, "[short <limit>] [long <limit>]",
	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_retry,
	"Set retry limit.");

#ifndef NETNS_RUN_DIR
#define NETNS_RUN_DIR "/var/run/netns"
#endif
static int netns_get_fd(const char *name)
{
	char pathbuf[MAXPATHLEN];
	const char *path, *ptr;

	path = name;
	ptr = strchr(name, '/');
	if (!ptr) {
		snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
			NETNS_RUN_DIR, name );
		path = pathbuf;
	}
	return open(path, O_RDONLY);
}

static int handle_netns(struct nl80211_state *state,
			struct nl_msg *msg,
			int argc, char **argv,
			enum id_input id)
{
	char *end;
	int fd = -1;

	if (argc < 1 || !*argv[0])
		return 1;

	if (argc == 1) {
		NLA_PUT_U32(msg, NL80211_ATTR_PID,
				strtoul(argv[0], &end, 10));
		if (*end != '\0') {
			printf("Invalid parameter: pid(%s)\n", argv[0]);
			return 1;
		}
		return 0;
	}

	if (argc != 2 || strcmp(argv[0], "name"))
		return 1;

	if ((fd = netns_get_fd(argv[1])) >= 0) {
		NLA_PUT_U32(msg, NL80211_ATTR_NETNS_FD, fd);
		return 0;
	} else {
		printf("Invalid parameter: nsname(%s)\n", argv[0]);
	}

	return 1;

 nla_put_failure:
	if (fd >= 0)
		close(fd);
	return -ENOBUFS;
}
COMMAND(set, netns, "{ <pid> | name <nsname> }",
	NL80211_CMD_SET_WIPHY_NETNS, 0, CIB_PHY, handle_netns,
	"Put this wireless device into a different network namespace:\n"
	"    <pid>    - change network namespace by process id\n"
	"    <nsname> - change network namespace by name from "NETNS_RUN_DIR"\n"
	"               or by absolute path (man ip-netns)\n");

static int handle_coverage(struct nl80211_state *state,
			struct nl_msg *msg,
			int argc, char **argv,
			enum id_input id)
{
	char *end;
	unsigned int coverage;

	if (argc != 1)
		return 1;

	if (!*argv[0])
		return 1;
	coverage = strtoul(argv[0], &end, 10);
	if (coverage > 255)
		return 1;

	if (*end)
		return 1;

	NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, coverage);

	return 0;
 nla_put_failure:
	return -ENOBUFS;
}
COMMAND(set, coverage, "<coverage class>",
	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_coverage,
	"Set coverage class (1 for every 3 usec of air propagation time).\n"
	"Valid values: 0 - 255.");

static int handle_distance(struct nl80211_state *state,
			struct nl_msg *msg,
			int argc, char **argv,
			enum id_input id)
{
	if (argc != 1)
		return 1;

	if (!*argv[0])
		return 1;

	if (strcmp("auto", argv[0]) == 0) {
		NLA_PUT_FLAG(msg, NL80211_ATTR_WIPHY_DYN_ACK);
	} else {
		char *end;
		unsigned int distance, coverage;

		distance = strtoul(argv[0], &end, 10);

		if (*end)
			return 1;

		/*
		 * Divide double the distance by the speed of light
		 * in m/usec (300) to get round-trip time in microseconds
		 * and then divide the result by three to get coverage class
		 * as specified in IEEE 802.11-2007 table 7-27.
		 * Values are rounded upwards.
		 */
		coverage = (distance + 449) / 450;
		if (coverage > 255)
			return 1;

		NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, coverage);
	}

	return 0;
 nla_put_failure:
	return -ENOBUFS;
}
COMMAND(set, distance, "<auto|distance>",
	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_distance,
	"Enable ACK timeout estimation algorithm (dynack) or set appropriate\n"
	"coverage class for given link distance in meters.\n"
	"To disable dynack set valid value for coverage class.\n"
	"Valid values: 0 - 114750");

static int handle_txpower(struct nl80211_state *state,
			  struct nl_msg *msg,
			  int argc, char **argv,
			  enum id_input id)
{
	enum nl80211_tx_power_setting type;
	int mbm;

	/* get the required args */
	if (argc != 1 && argc != 2)
		return 1;

	if (!strcmp(argv[0], "auto"))
		type = NL80211_TX_POWER_AUTOMATIC;
	else if (!strcmp(argv[0], "fixed"))
		type = NL80211_TX_POWER_FIXED;
	else if (!strcmp(argv[0], "limit"))
		type = NL80211_TX_POWER_LIMITED;
	else {
		printf("Invalid parameter: %s\n", argv[0]);
		return 2;
	}

	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_SETTING, type);

	if (type != NL80211_TX_POWER_AUTOMATIC) {
		char *endptr;
		if (argc != 2) {
			printf("Missing TX power level argument.\n");
			return 2;
		}

		mbm = strtol(argv[1], &endptr, 10);
		if (*endptr)
			return 2;
		NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_LEVEL, mbm);
	} else if (argc != 1)
		return 1;

	return 0;

 nla_put_failure:
	return -ENOBUFS;
}
COMMAND(set, txpower, "<auto|fixed|limit> [<tx power in mBm>]",
	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_txpower,
	"Specify transmit power level and setting type.");
COMMAND(set, txpower, "<auto|fixed|limit> [<tx power in mBm>]",
	NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_txpower,
	"Specify transmit power level and setting type.");

static int handle_antenna(struct nl80211_state *state,
			  struct nl_msg *msg,
			  int argc, char **argv,
			  enum id_input id)
{
	char *end;
	uint32_t tx_ant = 0, rx_ant = 0;

	if (argc == 1 && strcmp(argv[0], "all") == 0) {
		tx_ant = 0xffffffff;
		rx_ant = 0xffffffff;
	} else if (argc == 1) {
		tx_ant = rx_ant = strtoul(argv[0], &end, 0);
		if (*end)
			return 1;
	}
	else if (argc == 2) {
		tx_ant = strtoul(argv[0], &end, 0);
		if (*end)
			return 1;
		rx_ant = strtoul(argv[1], &end, 0);
		if (*end)
			return 1;
	} else
		return 1;

	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, tx_ant);
	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, rx_ant);

	return 0;

 nla_put_failure:
	return -ENOBUFS;
}
COMMAND(set, antenna, "<bitmap> | all | <tx bitmap> <rx bitmap>",
	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_antenna,
	"Set a bitmap of allowed antennas to use for TX and RX.\n"
	"The driver may reject antenna configurations it cannot support.");

static int handle_set_txq(struct nl80211_state *state,
			  struct nl_msg *msg,
			  int argc, char **argv,
			  enum id_input id)
{
	unsigned int argval;
	char *end;

	if (argc != 2)
		return 1;

	if (!*argv[0] || !*argv[1])
		return 1;

	argval = strtoul(argv[1], &end, 10);

	if (*end)
		return 1;

	if (!argval)
		return 1;

	if (strcmp("limit", argv[0]) == 0)
		NLA_PUT_U32(msg, NL80211_ATTR_TXQ_LIMIT, argval);
	else if (strcmp("memory_limit", argv[0]) == 0)
		NLA_PUT_U32(msg, NL80211_ATTR_TXQ_MEMORY_LIMIT, argval);
	else if (strcmp("quantum", argv[0]) == 0)
		NLA_PUT_U32(msg, NL80211_ATTR_TXQ_QUANTUM, argval);
	else
		return -1;

	return 0;
 nla_put_failure:
	return -ENOBUFS;
}
COMMAND(set, txq, "limit <packets> | memory_limit <bytes> | quantum <bytes>",
	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_set_txq,
	"Set TXQ parameters. The limit and memory_limit are global queue limits\n"
	"for the whole phy. The quantum is the DRR scheduler quantum setting.\n"
	"Valid values: 1 - 2**32");

static int print_txq_handler(struct nl_msg *msg, void *arg)
{
	struct nlattr *attrs[NL80211_ATTR_MAX + 1];
	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
	struct nlattr *txqstats_info[NL80211_TXQ_STATS_MAX + 1], *txqinfo;
	static struct nla_policy txqstats_policy[NL80211_TXQ_STATS_MAX + 1] = {
		[NL80211_TXQ_STATS_BACKLOG_PACKETS] = { .type = NLA_U32 },
		[NL80211_TXQ_STATS_BACKLOG_BYTES] = { .type = NLA_U32 },
		[NL80211_TXQ_STATS_OVERLIMIT] = { .type = NLA_U32 },
		[NL80211_TXQ_STATS_OVERMEMORY] = { .type = NLA_U32 },
		[NL80211_TXQ_STATS_COLLISIONS] = { .type = NLA_U32 },
		[NL80211_TXQ_STATS_MAX_FLOWS] = { .type = NLA_U32 },
	};

	nla_parse(attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
		  genlmsg_attrlen(gnlh, 0), NULL);


	if (attrs[NL80211_ATTR_TXQ_LIMIT])
		printf("Packet limit:\t\t%u pkts\n",
			nla_get_u32(attrs[NL80211_ATTR_TXQ_LIMIT]));
	if (attrs[NL80211_ATTR_TXQ_MEMORY_LIMIT])
		printf("Memory limit:\t\t%u bytes\n",
			nla_get_u32(attrs[NL80211_ATTR_TXQ_MEMORY_LIMIT]));
	if (attrs[NL80211_ATTR_TXQ_QUANTUM])
		printf("Quantum:\t\t%u bytes\n",
			nla_get_u32(attrs[NL80211_ATTR_TXQ_QUANTUM]));

	if (attrs[NL80211_ATTR_TXQ_STATS]) {
		if (nla_parse_nested(txqstats_info, NL80211_TXQ_STATS_MAX,
				     attrs[NL80211_ATTR_TXQ_STATS],
				     txqstats_policy)) {
			printf("failed to parse nested TXQ stats attributes!");
			return 0;
		}
		txqinfo = txqstats_info[NL80211_TXQ_STATS_MAX_FLOWS];
		if (txqinfo)
			printf("Number of queues:\t%u\n", nla_get_u32(txqinfo));

		txqinfo = txqstats_info[NL80211_TXQ_STATS_BACKLOG_PACKETS];
		if (txqinfo)
			printf("Backlog:\t\t%u pkts\n", nla_get_u32(txqinfo));

		txqinfo = txqstats_info[NL80211_TXQ_STATS_BACKLOG_BYTES];
		if (txqinfo)
			printf("Memory usage:\t\t%u bytes\n", nla_get_u32(txqinfo));

		txqinfo = txqstats_info[NL80211_TXQ_STATS_OVERLIMIT];
		if (txqinfo)
			printf("Packet limit overflows:\t%u\n", nla_get_u32(txqinfo));

		txqinfo = txqstats_info[NL80211_TXQ_STATS_OVERMEMORY];
		if (txqinfo)
			printf("Memory limit overflows:\t%u\n", nla_get_u32(txqinfo));
		txqinfo = txqstats_info[NL80211_TXQ_STATS_COLLISIONS];
		if (txqinfo)
			printf("Hash collisions:\t%u\n", nla_get_u32(txqinfo));
	}
	return NL_SKIP;
}

static int handle_get_txq(struct nl80211_state *state,
			  struct nl_msg *msg,
			  int argc, char **argv,
			  enum id_input id)
{
	nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP);
	nlmsg_hdr(msg)->nlmsg_flags |= NLM_F_DUMP;
	register_handler(print_txq_handler, NULL);
	return 0;
}
COMMAND(get, txq, "",
	NL80211_CMD_GET_WIPHY, 0, CIB_PHY, handle_get_txq,
	"Get TXQ parameters.");
