1/*
2 * Copyright (c) 2013 Adrian Chadd <adrian@FreeBSD.org>
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 *
25 * $FreeBSD$
26 */
27
28#include "diag.h"
29
30#include "ah.h"
31#include "ah_internal.h"
32
33#include <getopt.h>
34#include <errno.h>
35#include <err.h>
36#include <stdlib.h>
37#include <string.h>
38#include <ctype.h>
39#include <unistd.h>
40
41struct spectralhandler {
42	struct		ath_diag atd;
43	int		s;
44	struct ifreq	ifr;
45	int		ah_devid;
46};
47
48int
49spectral_opendev(struct spectralhandler *spectral, const char *devid)
50{
51	HAL_REVS revs;
52
53	spectral->s = socket(AF_INET, SOCK_DGRAM, 0);
54	if (spectral->s < 0) {
55		warn("socket");
56		return 0;
57	}
58
59	strncpy(spectral->atd.ad_name, devid, sizeof (spectral->atd.ad_name));
60
61	/* Get the hardware revision, just to verify things are working */
62	spectral->atd.ad_id = HAL_DIAG_REVS;
63	spectral->atd.ad_out_data = (caddr_t) &revs;
64	spectral->atd.ad_out_size = sizeof(revs);
65	if (ioctl(spectral->s, SIOCGATHDIAG, &spectral->atd) < 0) {
66		warn("%s", spectral->atd.ad_name);
67		return 0;
68	}
69	spectral->ah_devid = revs.ah_devid;
70	return 1;
71}
72
73void
74spectral_closedev(struct spectralhandler *spectral)
75{
76	close(spectral->s);
77	spectral->s = -1;
78}
79
80void
81spectralset(struct spectralhandler *spectral, int op, u_int32_t param)
82{
83	HAL_SPECTRAL_PARAM pe;
84
85	pe.ss_fft_period = HAL_SPECTRAL_PARAM_NOVAL;
86	pe.ss_period = HAL_SPECTRAL_PARAM_NOVAL;
87	pe.ss_count = HAL_SPECTRAL_PARAM_NOVAL;
88	pe.ss_short_report = HAL_SPECTRAL_PARAM_NOVAL;
89	pe.ss_spectral_pri = HAL_SPECTRAL_PARAM_NOVAL;
90	pe.ss_fft_period = HAL_SPECTRAL_PARAM_NOVAL;
91	pe.ss_enabled = HAL_SPECTRAL_PARAM_NOVAL;
92	pe.ss_active = HAL_SPECTRAL_PARAM_NOVAL;
93
94	switch (op) {
95	case SPECTRAL_PARAM_FFT_PERIOD:
96		pe.ss_fft_period = param;
97		break;
98	case SPECTRAL_PARAM_SS_PERIOD:
99		pe.ss_period = param;
100		break;
101	case SPECTRAL_PARAM_SS_COUNT:
102		pe.ss_count = param;
103		break;
104	case SPECTRAL_PARAM_SS_SHORT_RPT:
105		pe.ss_short_report = param;
106		break;
107	}
108
109	spectral->atd.ad_id = SPECTRAL_CONTROL_SET_PARAMS | ATH_DIAG_IN;
110	spectral->atd.ad_out_data = NULL;
111	spectral->atd.ad_out_size = 0;
112	spectral->atd.ad_in_data = (caddr_t) &pe;
113	spectral->atd.ad_in_size = sizeof(HAL_SPECTRAL_PARAM);
114	if (ioctl(spectral->s, SIOCGATHSPECTRAL, &spectral->atd) < 0)
115		err(1, "%s", spectral->atd.ad_name);
116}
117
118static void
119spectral_get(struct spectralhandler *spectral)
120{
121	HAL_SPECTRAL_PARAM pe;
122
123	spectral->atd.ad_id = SPECTRAL_CONTROL_GET_PARAMS | ATH_DIAG_DYN;
124	memset(&pe, 0, sizeof(pe));
125
126	spectral->atd.ad_in_data = NULL;
127	spectral->atd.ad_in_size = 0;
128	spectral->atd.ad_out_data = (caddr_t) &pe;
129	spectral->atd.ad_out_size = sizeof(pe);
130
131	if (ioctl(spectral->s, SIOCGATHSPECTRAL, &spectral->atd) < 0)
132		err(1, "%s", spectral->atd.ad_name);
133
134	printf("Spectral parameters (raw):\n");
135	printf("   ss_enabled: %d\n", pe.ss_enabled);
136	printf("   ss_active: %d\n", pe.ss_active);
137	printf("   ss_count: %d\n", pe.ss_count);
138	printf("   ss_fft_period: %d\n", pe.ss_fft_period);
139	printf("   ss_period: %d\n", pe.ss_period);
140	printf("   ss_short_report: %d\n", pe.ss_short_report);
141	printf("   radar_bin_thresh_sel: %d\n", pe.radar_bin_thresh_sel);
142}
143
144static void
145spectral_start(struct spectralhandler *spectral)
146{
147	HAL_SPECTRAL_PARAM pe;
148
149	spectral->atd.ad_id = SPECTRAL_CONTROL_START | ATH_DIAG_DYN;
150	memset(&pe, 0, sizeof(pe));
151
152	/*
153	 * XXX don't need these, but need to eliminate the ATH_DIAG_DYN flag
154	 * and debug
155	 */
156	spectral->atd.ad_in_data = NULL;
157	spectral->atd.ad_in_size = 0;
158	spectral->atd.ad_out_data = (caddr_t) &pe;
159	spectral->atd.ad_out_size = sizeof(pe);
160
161	if (ioctl(spectral->s, SIOCGATHSPECTRAL, &spectral->atd) < 0)
162		err(1, "%s", spectral->atd.ad_name);
163}
164
165static void
166spectral_stop(struct spectralhandler *spectral)
167{
168	HAL_SPECTRAL_PARAM pe;
169
170	spectral->atd.ad_id = SPECTRAL_CONTROL_STOP | ATH_DIAG_DYN;
171	memset(&pe, 0, sizeof(pe));
172
173	/*
174	 * XXX don't need these, but need to eliminate the ATH_DIAG_DYN flag
175	 * and debug
176	 */
177	spectral->atd.ad_in_data = NULL;
178	spectral->atd.ad_in_size = 0;
179	spectral->atd.ad_out_data = (caddr_t) &pe;
180	spectral->atd.ad_out_size = sizeof(pe);
181
182	if (ioctl(spectral->s, SIOCGATHSPECTRAL, &spectral->atd) < 0)
183		err(1, "%s", spectral->atd.ad_name);
184}
185
186static void
187spectral_enable_at_reset(struct spectralhandler *spectral, int val)
188{
189	int v = val;
190
191	spectral->atd.ad_id = SPECTRAL_CONTROL_ENABLE_AT_RESET
192	    | ATH_DIAG_IN;
193
194	/*
195	 * XXX don't need these, but need to eliminate the ATH_DIAG_DYN flag
196	 * and debug
197	 */
198	spectral->atd.ad_out_data = NULL;
199	spectral->atd.ad_out_size = 0;
200	spectral->atd.ad_in_data = (caddr_t) &v;
201	spectral->atd.ad_in_size = sizeof(v);
202
203	printf("%s: val=%d\n", __func__, v);
204
205	if (ioctl(spectral->s, SIOCGATHSPECTRAL, &spectral->atd) < 0)
206		err(1, "%s", spectral->atd.ad_name);
207}
208
209static int
210spectral_set_param(struct spectralhandler *spectral, const char *param,
211    const char *val)
212{
213	int v;
214
215	v = atoi(val);
216
217	if (strcmp(param, "ss_short_report") == 0) {
218		spectralset(spectral, SPECTRAL_PARAM_SS_SHORT_RPT, v);
219	} else if (strcmp(param, "ss_fft_period") == 0) {
220		spectralset(spectral, SPECTRAL_PARAM_FFT_PERIOD, v);
221	} else if (strcmp(param, "ss_period") == 0) {
222		spectralset(spectral, SPECTRAL_PARAM_SS_PERIOD, v);
223	} else if (strcmp(param, "ss_count") == 0) {
224		spectralset(spectral, SPECTRAL_PARAM_SS_COUNT, v);
225	} else {
226		return (0);
227	}
228
229#if 0
230	if (strcmp(param, "enabled") == 0) {
231		spectralset(spectral, DFS_PARAM_ENABLE, v);
232	} else if (strcmp(param, "firpwr") == 0) {
233		spectralset(spectral, DFS_PARAM_FIRPWR, v);
234	} else if (strcmp(param, "rrssi") == 0) {
235		spectralset(spectral, DFS_PARAM_RRSSI, v);
236	} else if (strcmp(param, "height") == 0) {
237		spectralset(spectral, DFS_PARAM_HEIGHT, v);
238	} else if (strcmp(param, "prssi") == 0) {
239		spectralset(spectral, DFS_PARAM_PRSSI, v);
240	} else if (strcmp(param, "inband") == 0) {
241		spectralset(spectral, DFS_PARAM_INBAND, v);
242	} else if (strcmp(param, "relpwr") == 0) {
243		spectralset(spectral, DFS_PARAM_RELPWR, v);
244	} else if (strcmp(param, "relstep") == 0) {
245		spectralset(spectral, DFS_PARAM_RELSTEP, v);
246	} else if (strcmp(param, "maxlen") == 0) {
247		spectralset(spectral, DFS_PARAM_MAXLEN, v);
248	} else if (strcmp(param, "usefir128") == 0) {
249		spectralset(spectral, DFS_PARAM_USEFIR128, v);
250	} else if (strcmp(param, "blockspectral") == 0) {
251		spectralset(spectral, DFS_PARAM_BLOCKRADAR, v);
252	} else if (strcmp(param, "enmaxrssi") == 0) {
253		spectralset(spectral, DFS_PARAM_MAXRSSI_EN, v);
254	} else if (strcmp(param, "extchannel") == 0) {
255		spectralset(spectral, DFS_PARAM_EN_EXTCH, v);
256	} else if (strcmp(param, "enrelpwr") == 0) {
257		spectralset(spectral, DFS_PARAM_RELPWR_EN, v);
258	} else if (strcmp(param, "en_relstep_check") == 0) {
259		spectralset(spectral, DFS_PARAM_RELSTEP_EN, v);
260	} else {
261		return 0;
262	}
263#endif
264
265	return 1;
266}
267
268void
269usage(const char *progname)
270{
271	printf("Usage:\n");
272	printf("\t%s: [-i <interface>] <cmd> (<arg>)\n", progname);
273	printf("\t%s: [-h]\n", progname);
274	printf("\n");
275	printf("Valid commands:\n");
276	printf("\tget:\t\tGet current spectral parameters\n");
277	printf("\tset <param> <value>:\t\tSet spectral parameter\n");
278	printf("\tstart: Start spectral scan\n");
279	printf("\tstop: Stop spectral scan\n");
280	printf("\tenable_at_reset <0|1>: enable reporting upon channel reset\n");
281}
282
283int
284main(int argc, char *argv[])
285{
286	struct spectralhandler spectral;
287	const char *devname = ATH_DEFAULT;
288	const char *progname = argv[0];
289
290	memset(&spectral, 0, sizeof(spectral));
291
292	/* Parse command line options */
293	if (argc >= 2 && strcmp(argv[1], "-h") == 0) {
294		usage(progname);
295		exit(0);
296	}
297	if (argc >= 2 && strcmp(argv[1], "-?") == 0) {
298		usage(progname);
299		exit(0);
300	}
301
302	if (argc >= 2 && strcmp(argv[1], "-i") == 0) {
303		if (argc == 2) {
304			usage(progname);
305			exit(127);
306		}
307		devname = argv[2];
308		argc -= 2; argv += 2;
309	}
310
311	/* At this point we require at least one command */
312	if (argc == 1) {
313		usage(progname);
314		exit(127);
315	}
316
317	if (spectral_opendev(&spectral, devname) == 0)
318		exit(127);
319
320	if (strcasecmp(argv[1], "get") == 0) {
321		spectral_get(&spectral);
322	} else if (strcasecmp(argv[1], "set") == 0) {
323		if (argc < 4) {
324			usage(progname);
325			exit(127);
326		}
327		if (spectral_set_param(&spectral, argv[2], argv[3]) == 0) {
328			usage(progname);
329			exit(127);
330		}
331	} else if (strcasecmp(argv[1], "start") == 0) {
332		spectral_start(&spectral);
333	} else if (strcasecmp(argv[1], "stop") == 0) {
334		spectral_stop(&spectral);
335	} else if (strcasecmp(argv[1], "enable_at_reset") == 0) {
336		if (argc < 3) {
337			usage(progname);
338			exit(127);
339		}
340		spectral_enable_at_reset(&spectral, atoi(argv[2]));
341	} else {
342		usage(progname);
343		exit(127);
344	}
345
346	/* wrap up */
347	spectral_closedev(&spectral);
348	exit(0);
349}
350