1/*-
2 * Copyright (c) 2019 Adrian Chadd <adrian@FreeBSD.org>.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer,
10 *    without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13 *    redistribution must be conditioned upon including a substantially
14 *    similar Disclaimer requirement for further binary redistribution.
15 *
16 * NO WARRANTY
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 * THE POSSIBILITY OF SUCH DAMAGES.
28 */
29#include "diag.h"
30
31#include "ah.h"
32#include "ah_internal.h"
33
34#include <getopt.h>
35#include <stdlib.h>
36#include <string.h>
37#include <ctype.h>
38#include <err.h>
39
40#include "../common/ctrl.h"
41
42/*
43 * This is a simple wrapper program around the ANI diagnostic interface.
44 * It is for fetching and setting the live ANI configuration when trying
45 * to diagnose a noisy environment.
46 */
47
48/*
49 * HAL_DIAG_ANI_CMD is used to set the ANI configuration.
50 * HAL_DIAG_ANI_CURRENT is used to fetch the current ANI configuration.
51 */
52
53struct ani_var {
54  const char *name;
55  int id;
56};
57
58static struct ani_var ani_labels[] = {
59  { "ofdm_noise_immunity_level", 1, },
60  { "noise_immunity_level", 1, },
61  { "ofdm_weak_signal_detect", 2, },
62  { "cck_weak_signal_threshold", 3, },
63  { "firstep_level", 4, },
64  { "spur_immunity_level", 5, },
65  { "mrc_cck", 8, },
66  { "cck_noise_immunity_level", 9, },
67  { NULL, -1, },
68};
69
70static void
71usage(void)
72{
73	fprintf(stderr, "usage: athani [-i interface] [-l]\n");
74	fprintf(stderr, "    -i: interface\n");
75	fprintf(stderr, "    -l: list ANI labels\n");
76	fprintf(stderr, "  If no args are given after flags, the ANI state will be listed.\n");
77	fprintf(stderr, "  To set, use '<label> <value>' to set the state\n");
78	exit(-1);
79}
80
81static void
82list_labels(void)
83{
84	int i;
85
86	for (i = 0; ani_labels[i].name != NULL; i++) {
87		printf("%s (%d)\n", ani_labels[i].name, ani_labels[i].id);
88	}
89}
90
91static int
92ani_write_state(struct ath_driver_req *req, const char *ifname,
93  const char *label, const char *value)
94{
95	struct ath_diag atd;
96	uint32_t args[2];
97	uint32_t cmd, val;
98	size_t sl;
99	int i;
100
101	/* Find the label */
102	sl = strlen(label);
103	for (i = 0; ani_labels[i].name != NULL; i++) {
104		if ((strlen(ani_labels[i].name) == sl) &&
105		    (strcmp(label, ani_labels[i].name) == 0)) {
106			cmd = ani_labels[i].id;
107			break;
108		}
109	}
110	if (ani_labels[i].name == NULL) {
111		fprintf(stderr, "%s: couldn't find ANI label (%s)\n",
112		    __func__, label);
113		return (-1);
114	}
115
116	val = strtoul(value, NULL, 0);
117
118	/*
119	 * Whilst we're doing the ath_diag pieces, we have to set this
120	 * ourselves.
121	 */
122	strncpy(atd.ad_name, ifname, sizeof (atd.ad_name));
123
124	/*
125	 * Populate HAL_DIAG_ANI_CMD fields.
126	 */
127	args[0] = cmd;
128	args[1] = val;
129
130	atd.ad_id = HAL_DIAG_ANI_CMD | ATH_DIAG_IN;
131	atd.ad_out_data = NULL;
132	atd.ad_out_size = 0;
133	atd.ad_in_data = (void *) &args;
134	atd.ad_in_size = sizeof(args);
135
136	if (ath_driver_req_fetch_diag(req, SIOCGATHDIAG, &atd) < 0) {
137		warn("SIOCGATHDIAG HAL_DIAG_ANI_CMD (%s)", atd.ad_name);
138		return (-1);
139	}
140
141	return (0);
142}
143
144static void
145ani_read_state(struct ath_driver_req *req, const char *ifname)
146{
147	struct ath_diag atd;
148	HAL_ANI_STATE state;
149
150	/*
151	 * Whilst we're doing the ath_diag pieces, we have to set this
152	 * ourselves.
153	 */
154	strncpy(atd.ad_name, ifname, sizeof (atd.ad_name));
155
156	atd.ad_id = HAL_DIAG_ANI_CURRENT; /* XXX | DIAG_DYN? */
157	atd.ad_out_data = (caddr_t) &state;
158	atd.ad_out_size = sizeof(state);
159
160	if (ath_driver_req_fetch_diag(req, SIOCGATHDIAG, &atd) < 0)
161		err(1, "%s", atd.ad_name);
162
163
164	printf("  ofdm_noise_immunity_level=%d\n", state.noiseImmunityLevel);
165	printf("  cck_noise_immunity_level=%d\n", state.cckNoiseImmunityLevel);
166	printf("  spur_immunity_level=%d\n", state.spurImmunityLevel);
167	printf("  firstep_level=%d\n", state.firstepLevel);
168	printf("  ofdm_weak_signal_detect=%d\n", state.ofdmWeakSigDetectOff);
169	printf("  cck_weak_signal_threshold=%d\n", state.cckWeakSigThreshold);
170	printf("  mrc_cck=%d\n", state.mrcCck);
171	/* XXX TODO: cycle counts? */
172}
173
174int
175main(int argc, char *argv[])
176{
177	struct ath_diag atd;
178	const char *ifname;
179	struct ath_driver_req req;
180	int what, c;
181
182	ath_driver_req_init(&req);
183
184	ifname = getenv("ATH");
185	if (!ifname)
186		ifname = ATH_DEFAULT;
187
188	what = 0;
189	while ((c = getopt(argc, argv, "i:l")) != -1)
190		switch (c) {
191		case 'i':
192			ifname = optarg;
193			break;
194		case 'l':
195			list_labels();
196			exit(0);
197		default:
198			usage();
199			/*NOTREACHED*/
200		}
201
202	/* Initialise the driver interface */
203	if (ath_driver_req_open(&req, ifname) < 0) {
204		exit(127);
205	}
206
207	argc -= optind;
208	argv += optind;
209
210	if (argc == 0) {
211		ani_read_state(&req, ifname);
212		exit(0);
213	}
214
215	if (argc < 2) {
216		usage();
217		/*NOTREACHED*/
218	}
219
220	if (ani_write_state(&req, ifname, argv[0], argv[1]) != 0)
221		exit(1);
222
223	exit(0);
224}
225