1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2013 Adrian Chadd <adrian@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer,
12 *    without modification.
13 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
14 *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
15 *    redistribution must be conditioned upon including a substantially
16 *    similar Disclaimer requirement for further binary redistribution.
17 *
18 * NO WARRANTY
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
22 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
24 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
27 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
29 * THE POSSIBILITY OF SUCH DAMAGES.
30 *
31 * $FreeBSD: releng/12.0/sys/dev/ath/if_ath_spectral.c 327500 2018-01-02 19:34:23Z emaste $
32 */
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD: releng/12.0/sys/dev/ath/if_ath_spectral.c 327500 2018-01-02 19:34:23Z emaste $");
35
36/*
37 * Implement some basic spectral scan control logic.
38 */
39#include "opt_ath.h"
40#include "opt_inet.h"
41#include "opt_wlan.h"
42
43#include <sys/param.h>
44#include <sys/systm.h>
45#include <sys/sysctl.h>
46#include <sys/kernel.h>
47#include <sys/lock.h>
48#include <sys/malloc.h>
49#include <sys/mutex.h>
50#include <sys/errno.h>
51
52#include <machine/bus.h>
53#include <machine/resource.h>
54#include <sys/bus.h>
55
56#include <sys/socket.h>
57
58#include <net/if.h>
59#include <net/if_var.h>
60#include <net/if_media.h>
61#include <net/if_arp.h>
62#include <net/ethernet.h>		/* XXX for ether_sprintf */
63
64#include <net80211/ieee80211_var.h>
65
66#include <net/bpf.h>
67
68#ifdef INET
69#include <netinet/in.h>
70#include <netinet/if_ether.h>
71#endif
72
73#include <dev/ath/if_athvar.h>
74#include <dev/ath/if_ath_spectral.h>
75#include <dev/ath/if_ath_misc.h>
76
77#include <dev/ath/ath_hal/ah_desc.h>
78
79struct ath_spectral_state {
80	HAL_SPECTRAL_PARAM	spectral_state;
81
82	/*
83	 * Should we enable spectral scan upon
84	 * each network interface reset/change?
85	 *
86	 * This is intended to allow spectral scan
87	 * frame reporting during channel scans.
88	 *
89	 * Later on it can morph into a larger
90	 * scale config method where it pushes
91	 * a "channel scan" config into the hardware
92	 * rather than just the spectral_state
93	 * config.
94	 */
95	int spectral_enable_after_reset;
96};
97
98/*
99 * Methods which are required
100 */
101
102/*
103 * Attach spectral to the given interface
104 */
105int
106ath_spectral_attach(struct ath_softc *sc)
107{
108	struct ath_spectral_state *ss;
109
110	/*
111	 * If spectral isn't supported, don't error - just
112	 * quietly complete.
113	 */
114	if (! ath_hal_spectral_supported(sc->sc_ah))
115		return (0);
116
117	ss = malloc(sizeof(struct ath_spectral_state),
118	    M_TEMP, M_WAITOK | M_ZERO);
119
120	if (ss == NULL) {
121		device_printf(sc->sc_dev, "%s: failed to alloc memory\n",
122		    __func__);
123		return (-ENOMEM);
124	}
125
126	sc->sc_spectral = ss;
127
128	(void) ath_hal_spectral_get_config(sc->sc_ah, &ss->spectral_state);
129
130	return (0);
131}
132
133/*
134 * Detach spectral from the given interface
135 */
136int
137ath_spectral_detach(struct ath_softc *sc)
138{
139
140	if (! ath_hal_spectral_supported(sc->sc_ah))
141		return (0);
142
143	if (sc->sc_spectral != NULL) {
144		free(sc->sc_spectral, M_TEMP);
145	}
146	return (0);
147}
148
149/*
150 * Check whether spectral needs enabling and if so,
151 * flip it on.
152 */
153int
154ath_spectral_enable(struct ath_softc *sc, struct ieee80211_channel *ch)
155{
156	struct ath_spectral_state *ss = sc->sc_spectral;
157
158	/* Default to disable spectral PHY reporting */
159	sc->sc_dospectral = 0;
160
161	if (ss == NULL)
162		return (0);
163
164	if (ss->spectral_enable_after_reset) {
165		ath_hal_spectral_configure(sc->sc_ah,
166		    &ss->spectral_state);
167		(void) ath_hal_spectral_start(sc->sc_ah);
168		sc->sc_dospectral = 1;
169	}
170	return (0);
171}
172
173/*
174 * Handle ioctl requests from the diagnostic interface.
175 *
176 * The initial part of this code resembles ath_ioctl_diag();
177 * it's likely a good idea to reduce duplication between
178 * these two routines.
179 */
180int
181ath_ioctl_spectral(struct ath_softc *sc, struct ath_diag *ad)
182{
183	unsigned int id = ad->ad_id & ATH_DIAG_ID;
184	void *indata = NULL;
185	void *outdata = NULL;
186	u_int32_t insize = ad->ad_in_size;
187	u_int32_t outsize = ad->ad_out_size;
188	int error = 0;
189	HAL_SPECTRAL_PARAM peout;
190	HAL_SPECTRAL_PARAM *pe;
191	struct ath_spectral_state *ss = sc->sc_spectral;
192	int val;
193
194	if (! ath_hal_spectral_supported(sc->sc_ah))
195		return (EINVAL);
196
197	ATH_LOCK(sc);
198	ath_power_set_power_state(sc, HAL_PM_AWAKE);
199	ATH_UNLOCK(sc);
200
201	if (ad->ad_id & ATH_DIAG_IN) {
202		/*
203		 * Copy in data.
204		 */
205		indata = malloc(insize, M_TEMP, M_NOWAIT);
206		if (indata == NULL) {
207			error = ENOMEM;
208			goto bad;
209		}
210		error = copyin(ad->ad_in_data, indata, insize);
211		if (error)
212			goto bad;
213	}
214	if (ad->ad_id & ATH_DIAG_DYN) {
215		/*
216		 * Allocate a buffer for the results (otherwise the HAL
217		 * returns a pointer to a buffer where we can read the
218		 * results).  Note that we depend on the HAL leaving this
219		 * pointer for us to use below in reclaiming the buffer;
220		 * may want to be more defensive.
221		 */
222		outdata = malloc(outsize, M_TEMP, M_NOWAIT | M_ZERO);
223		if (outdata == NULL) {
224			error = ENOMEM;
225			goto bad;
226		}
227	}
228	switch (id) {
229		case SPECTRAL_CONTROL_GET_PARAMS:
230			memset(&peout, 0, sizeof(peout));
231			outsize = sizeof(HAL_SPECTRAL_PARAM);
232			ath_hal_spectral_get_config(sc->sc_ah, &peout);
233			pe = (HAL_SPECTRAL_PARAM *) outdata;
234			memcpy(pe, &peout, sizeof(*pe));
235			break;
236		case SPECTRAL_CONTROL_SET_PARAMS:
237			if (insize < sizeof(HAL_SPECTRAL_PARAM)) {
238				error = EINVAL;
239				break;
240			}
241			pe = (HAL_SPECTRAL_PARAM *) indata;
242			ath_hal_spectral_configure(sc->sc_ah, pe);
243			/* Save a local copy of the updated parameters */
244			ath_hal_spectral_get_config(sc->sc_ah,
245			    &ss->spectral_state);
246			break;
247		case SPECTRAL_CONTROL_START:
248			ath_hal_spectral_configure(sc->sc_ah,
249			    &ss->spectral_state);
250			(void) ath_hal_spectral_start(sc->sc_ah);
251			sc->sc_dospectral = 1;
252			/* XXX need to update the PHY mask in the driver */
253			break;
254		case SPECTRAL_CONTROL_STOP:
255			(void) ath_hal_spectral_stop(sc->sc_ah);
256			sc->sc_dospectral = 0;
257			/* XXX need to update the PHY mask in the driver */
258			break;
259		case SPECTRAL_CONTROL_ENABLE_AT_RESET:
260			if (insize < sizeof(int)) {
261				device_printf(sc->sc_dev, "%d != %d\n",
262				    insize,
263				    (int) sizeof(int));
264				error = EINVAL;
265				break;
266			}
267			if (indata == NULL) {
268				device_printf(sc->sc_dev, "indata=NULL\n");
269				error = EINVAL;
270				break;
271			}
272			val = * ((int *) indata);
273			if (val == 0)
274				ss->spectral_enable_after_reset = 0;
275			else
276				ss->spectral_enable_after_reset = 1;
277			break;
278		case SPECTRAL_CONTROL_ENABLE:
279			/* XXX TODO */
280		case SPECTRAL_CONTROL_DISABLE:
281			/* XXX TODO */
282		break;
283		default:
284			error = EINVAL;
285			goto bad;
286	}
287	if (outsize < ad->ad_out_size)
288		ad->ad_out_size = outsize;
289	if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size))
290		error = EFAULT;
291bad:
292	if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
293		free(indata, M_TEMP);
294	if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
295		free(outdata, M_TEMP);
296	ATH_LOCK(sc);
297	ath_power_restore_power_state(sc);
298	ATH_UNLOCK(sc);
299
300	return (error);
301}
302