Add cac command to allow clearing channels

Allow the user to start a CAC for clearing DFS channels.

Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
Signed-off-by: Benjamin Berg <benjamin.berg@open-mesh.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
diff --git a/phy.c b/phy.c
index 266de4d..be31820 100644
--- a/phy.c
+++ b/phy.c
@@ -226,6 +226,151 @@
 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 {
+		return 1;
+	}
+
+	cac_trigger_argv = calloc(argc + 1, sizeof(char*));
+	if (!cac_trigger_argv)
+		return -ENOMEM;
+
+	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);
+	free(cac_trigger_argv);
+	if (err)
+		return err;
+
+	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);
+
+	return 0;
+}
+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,