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
26#include "diag.h"
27
28#include "ah.h"
29#include "ah_internal.h"
30
31#include <getopt.h>
32#include <errno.h>
33#include <err.h>
34#include <stdlib.h>
35#include <string.h>
36#include <ctype.h>
37#include <unistd.h>
38
39struct spectralhandler {
40	struct		ath_diag atd;
41	int		s;
42	struct ifreq	ifr;
43	int		ah_devid;
44};
45
46int
47spectral_opendev(struct spectralhandler *spectral, const char *devid)
48{
49	HAL_REVS revs;
50
51	spectral->s = socket(AF_INET, SOCK_DGRAM, 0);
52	if (spectral->s < 0) {
53		warn("socket");
54		return 0;
55	}
56
57	strncpy(spectral->atd.ad_name, devid, sizeof (spectral->atd.ad_name));
58
59	/* Get the hardware revision, just to verify things are working */
60	spectral->atd.ad_id = HAL_DIAG_REVS;
61	spectral->atd.ad_out_data = (caddr_t) &revs;
62	spectral->atd.ad_out_size = sizeof(revs);
63	if (ioctl(spectral->s, SIOCGATHDIAG, &spectral->atd) < 0) {
64		warn("%s", spectral->atd.ad_name);
65		return 0;
66	}
67	spectral->ah_devid = revs.ah_devid;
68	return 1;
69}
70
71void
72spectral_closedev(struct spectralhandler *spectral)
73{
74	close(spectral->s);
75	spectral->s = -1;
76}
77
78void
79spectralset(struct spectralhandler *spectral, int op, u_int32_t param)
80{
81	HAL_SPECTRAL_PARAM pe;
82
83	pe.ss_fft_period = HAL_SPECTRAL_PARAM_NOVAL;
84	pe.ss_period = HAL_SPECTRAL_PARAM_NOVAL;
85	pe.ss_count = HAL_SPECTRAL_PARAM_NOVAL;
86	pe.ss_short_report = HAL_SPECTRAL_PARAM_NOVAL;
87	pe.ss_spectral_pri = HAL_SPECTRAL_PARAM_NOVAL;
88	pe.ss_fft_period = HAL_SPECTRAL_PARAM_NOVAL;
89	pe.ss_enabled = HAL_SPECTRAL_PARAM_NOVAL;
90	pe.ss_active = HAL_SPECTRAL_PARAM_NOVAL;
91
92	switch (op) {
93	case SPECTRAL_PARAM_FFT_PERIOD:
94		pe.ss_fft_period = param;
95		break;
96	case SPECTRAL_PARAM_SS_PERIOD:
97		pe.ss_period = param;
98		break;
99	case SPECTRAL_PARAM_SS_COUNT:
100		pe.ss_count = param;
101		break;
102	case SPECTRAL_PARAM_SS_SHORT_RPT:
103		pe.ss_short_report = param;
104		break;
105	case SPECTRAL_PARAM_SS_SPECTRAL_PRI:
106		pe.ss_spectral_pri = param;
107		break;
108	}
109
110	spectral->atd.ad_id = SPECTRAL_CONTROL_SET_PARAMS | ATH_DIAG_IN;
111	spectral->atd.ad_out_data = NULL;
112	spectral->atd.ad_out_size = 0;
113	spectral->atd.ad_in_data = (caddr_t) &pe;
114	spectral->atd.ad_in_size = sizeof(HAL_SPECTRAL_PARAM);
115	if (ioctl(spectral->s, SIOCGATHSPECTRAL, &spectral->atd) < 0)
116		err(1, "%s", spectral->atd.ad_name);
117}
118
119static void
120spectral_get(struct spectralhandler *spectral)
121{
122	HAL_SPECTRAL_PARAM pe;
123
124	spectral->atd.ad_id = SPECTRAL_CONTROL_GET_PARAMS | ATH_DIAG_DYN;
125	memset(&pe, 0, sizeof(pe));
126
127	spectral->atd.ad_in_data = NULL;
128	spectral->atd.ad_in_size = 0;
129	spectral->atd.ad_out_data = (caddr_t) &pe;
130	spectral->atd.ad_out_size = sizeof(pe);
131
132	if (ioctl(spectral->s, SIOCGATHSPECTRAL, &spectral->atd) < 0)
133		err(1, "%s", spectral->atd.ad_name);
134
135	printf("Spectral parameters (raw):\n");
136	printf("   ss_enabled: %d\n", pe.ss_enabled);
137	printf("   ss_active: %d\n", pe.ss_active);
138	printf("   ss_count: %d\n", pe.ss_count);
139	printf("   ss_fft_period: %d\n", pe.ss_fft_period);
140	printf("   ss_period: %d\n", pe.ss_period);
141	printf("   ss_short_report: %d\n", pe.ss_short_report);
142	printf("   ss_spectral_pri: %d\n", pe.ss_spectral_pri);
143	printf("   radar_bin_thresh_sel: %d\n", pe.radar_bin_thresh_sel);
144}
145
146static void
147spectral_start(struct spectralhandler *spectral)
148{
149	HAL_SPECTRAL_PARAM pe;
150
151	spectral->atd.ad_id = SPECTRAL_CONTROL_START | ATH_DIAG_DYN;
152	memset(&pe, 0, sizeof(pe));
153
154	/*
155	 * XXX don't need these, but need to eliminate the ATH_DIAG_DYN flag
156	 * and debug
157	 */
158	spectral->atd.ad_in_data = NULL;
159	spectral->atd.ad_in_size = 0;
160	spectral->atd.ad_out_data = (caddr_t) &pe;
161	spectral->atd.ad_out_size = sizeof(pe);
162
163	if (ioctl(spectral->s, SIOCGATHSPECTRAL, &spectral->atd) < 0)
164		err(1, "%s", spectral->atd.ad_name);
165}
166
167static void
168spectral_stop(struct spectralhandler *spectral)
169{
170	HAL_SPECTRAL_PARAM pe;
171
172	spectral->atd.ad_id = SPECTRAL_CONTROL_STOP | ATH_DIAG_DYN;
173	memset(&pe, 0, sizeof(pe));
174
175	/*
176	 * XXX don't need these, but need to eliminate the ATH_DIAG_DYN flag
177	 * and debug
178	 */
179	spectral->atd.ad_in_data = NULL;
180	spectral->atd.ad_in_size = 0;
181	spectral->atd.ad_out_data = (caddr_t) &pe;
182	spectral->atd.ad_out_size = sizeof(pe);
183
184	if (ioctl(spectral->s, SIOCGATHSPECTRAL, &spectral->atd) < 0)
185		err(1, "%s", spectral->atd.ad_name);
186}
187
188static void
189spectral_enable_at_reset(struct spectralhandler *spectral, int val)
190{
191	int v = val;
192
193	spectral->atd.ad_id = SPECTRAL_CONTROL_ENABLE_AT_RESET
194	    | ATH_DIAG_IN;
195
196	/*
197	 * XXX don't need these, but need to eliminate the ATH_DIAG_DYN flag
198	 * and debug
199	 */
200	spectral->atd.ad_out_data = NULL;
201	spectral->atd.ad_out_size = 0;
202	spectral->atd.ad_in_data = (caddr_t) &v;
203	spectral->atd.ad_in_size = sizeof(v);
204
205	printf("%s: val=%d\n", __func__, v);
206
207	if (ioctl(spectral->s, SIOCGATHSPECTRAL, &spectral->atd) < 0)
208		err(1, "%s", spectral->atd.ad_name);
209}
210
211static int
212spectral_set_param(struct spectralhandler *spectral, const char *param,
213    const char *val)
214{
215	int v;
216
217	v = atoi(val);
218
219	if (strcmp(param, "ss_short_report") == 0) {
220		spectralset(spectral, SPECTRAL_PARAM_SS_SHORT_RPT, v);
221	} else if (strcmp(param, "ss_fft_period") == 0) {
222		spectralset(spectral, SPECTRAL_PARAM_FFT_PERIOD, v);
223	} else if (strcmp(param, "ss_period") == 0) {
224		spectralset(spectral, SPECTRAL_PARAM_SS_PERIOD, v);
225	} else if (strcmp(param, "ss_count") == 0) {
226		spectralset(spectral, SPECTRAL_PARAM_SS_COUNT, v);
227	} else if (strcmp(param, "ss_spectral_pri") == 0) {
228		spectralset(spectral, SPECTRAL_PARAM_SS_SPECTRAL_PRI, v);
229	} else {
230		return (0);
231	}
232
233#if 0
234	if (strcmp(param, "enabled") == 0) {
235		spectralset(spectral, DFS_PARAM_ENABLE, v);
236	} else if (strcmp(param, "firpwr") == 0) {
237		spectralset(spectral, DFS_PARAM_FIRPWR, v);
238	} else if (strcmp(param, "rrssi") == 0) {
239		spectralset(spectral, DFS_PARAM_RRSSI, v);
240	} else if (strcmp(param, "height") == 0) {
241		spectralset(spectral, DFS_PARAM_HEIGHT, v);
242	} else if (strcmp(param, "prssi") == 0) {
243		spectralset(spectral, DFS_PARAM_PRSSI, v);
244	} else if (strcmp(param, "inband") == 0) {
245		spectralset(spectral, DFS_PARAM_INBAND, v);
246	} else if (strcmp(param, "relpwr") == 0) {
247		spectralset(spectral, DFS_PARAM_RELPWR, v);
248	} else if (strcmp(param, "relstep") == 0) {
249		spectralset(spectral, DFS_PARAM_RELSTEP, v);
250	} else if (strcmp(param, "maxlen") == 0) {
251		spectralset(spectral, DFS_PARAM_MAXLEN, v);
252	} else if (strcmp(param, "usefir128") == 0) {
253		spectralset(spectral, DFS_PARAM_USEFIR128, v);
254	} else if (strcmp(param, "blockspectral") == 0) {
255		spectralset(spectral, DFS_PARAM_BLOCKRADAR, v);
256	} else if (strcmp(param, "enmaxrssi") == 0) {
257		spectralset(spectral, DFS_PARAM_MAXRSSI_EN, v);
258	} else if (strcmp(param, "extchannel") == 0) {
259		spectralset(spectral, DFS_PARAM_EN_EXTCH, v);
260	} else if (strcmp(param, "enrelpwr") == 0) {
261		spectralset(spectral, DFS_PARAM_RELPWR_EN, v);
262	} else if (strcmp(param, "en_relstep_check") == 0) {
263		spectralset(spectral, DFS_PARAM_RELSTEP_EN, v);
264	} else {
265		return 0;
266	}
267#endif
268
269	return 1;
270}
271
272void
273usage(const char *progname)
274{
275	printf("Usage:\n");
276	printf("\t%s: [-i <interface>] <cmd> (<arg>)\n", progname);
277	printf("\t%s: [-h]\n", progname);
278	printf("\n");
279	printf("Valid commands:\n");
280	printf("\tget:\t\tGet current spectral parameters\n");
281	printf("\tset <param> <value>:\t\tSet spectral parameter\n");
282	printf("\tstart: Start spectral scan\n");
283	printf("\tstop: Stop spectral scan\n");
284	printf("\tenable_at_reset <0|1>: enable reporting upon channel reset\n");
285}
286
287int
288main(int argc, char *argv[])
289{
290	struct spectralhandler spectral;
291	const char *devname = ATH_DEFAULT;
292	const char *progname = argv[0];
293
294	memset(&spectral, 0, sizeof(spectral));
295
296	/* Parse command line options */
297	if (argc >= 2 && strcmp(argv[1], "-h") == 0) {
298		usage(progname);
299		exit(0);
300	}
301	if (argc >= 2 && strcmp(argv[1], "-?") == 0) {
302		usage(progname);
303		exit(0);
304	}
305
306	if (argc >= 2 && strcmp(argv[1], "-i") == 0) {
307		if (argc == 2) {
308			usage(progname);
309			exit(127);
310		}
311		devname = argv[2];
312		argc -= 2; argv += 2;
313	}
314
315	/* At this point we require at least one command */
316	if (argc == 1) {
317		usage(progname);
318		exit(127);
319	}
320
321	if (spectral_opendev(&spectral, devname) == 0)
322		exit(127);
323
324	if (strcasecmp(argv[1], "get") == 0) {
325		spectral_get(&spectral);
326	} else if (strcasecmp(argv[1], "set") == 0) {
327		if (argc < 4) {
328			usage(progname);
329			exit(127);
330		}
331		if (spectral_set_param(&spectral, argv[2], argv[3]) == 0) {
332			usage(progname);
333			exit(127);
334		}
335	} else if (strcasecmp(argv[1], "start") == 0) {
336		spectral_start(&spectral);
337	} else if (strcasecmp(argv[1], "stop") == 0) {
338		spectral_stop(&spectral);
339	} else if (strcasecmp(argv[1], "enable_at_reset") == 0) {
340		if (argc < 3) {
341			usage(progname);
342			exit(127);
343		}
344		spectral_enable_at_reset(&spectral, atoi(argv[2]));
345	} else {
346		usage(progname);
347		exit(127);
348	}
349
350	/* wrap up */
351	spectral_closedev(&spectral);
352	exit(0);
353}
354