1244951Sadrian/*-
2244951Sadrian * Copyright (c) 2013 Adrian Chadd <adrian@FreeBSD.org>
3244951Sadrian * All rights reserved.
4244951Sadrian *
5244951Sadrian * Redistribution and use in source and binary forms, with or without
6244951Sadrian * modification, are permitted provided that the following conditions
7244951Sadrian * are met:
8244951Sadrian * 1. Redistributions of source code must retain the above copyright
9244951Sadrian *    notice, this list of conditions and the following disclaimer,
10244951Sadrian *    without modification.
11244951Sadrian * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12244951Sadrian *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13244951Sadrian *    redistribution must be conditioned upon including a substantially
14244951Sadrian *    similar Disclaimer requirement for further binary redistribution.
15244951Sadrian *
16244951Sadrian * NO WARRANTY
17244951Sadrian * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18244951Sadrian * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19244951Sadrian * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20244951Sadrian * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21244951Sadrian * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22244951Sadrian * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23244951Sadrian * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24244951Sadrian * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25244951Sadrian * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26244951Sadrian * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27244951Sadrian * THE POSSIBILITY OF SUCH DAMAGES.
28244951Sadrian *
29244951Sadrian * $FreeBSD: stable/10/sys/dev/ath/if_ath_spectral.c 332320 2018-04-09 12:53:15Z emaste $
30244951Sadrian */
31244951Sadrian#include <sys/cdefs.h>
32244951Sadrian__FBSDID("$FreeBSD: stable/10/sys/dev/ath/if_ath_spectral.c 332320 2018-04-09 12:53:15Z emaste $");
33244951Sadrian
34244951Sadrian/*
35244951Sadrian * Implement some basic spectral scan control logic.
36244951Sadrian */
37244951Sadrian#include "opt_ath.h"
38244951Sadrian#include "opt_inet.h"
39244951Sadrian#include "opt_wlan.h"
40244951Sadrian
41244951Sadrian#include <sys/param.h>
42244951Sadrian#include <sys/systm.h>
43244951Sadrian#include <sys/sysctl.h>
44244951Sadrian#include <sys/kernel.h>
45244951Sadrian#include <sys/lock.h>
46244951Sadrian#include <sys/mutex.h>
47244951Sadrian#include <sys/errno.h>
48244951Sadrian
49244951Sadrian#include <machine/bus.h>
50244951Sadrian#include <machine/resource.h>
51244951Sadrian#include <sys/bus.h>
52244951Sadrian
53244951Sadrian#include <sys/socket.h>
54244951Sadrian
55244951Sadrian#include <net/if.h>
56244951Sadrian#include <net/if_media.h>
57244951Sadrian#include <net/if_arp.h>
58244951Sadrian#include <net/ethernet.h>		/* XXX for ether_sprintf */
59244951Sadrian
60244951Sadrian#include <net80211/ieee80211_var.h>
61244951Sadrian
62244951Sadrian#include <net/bpf.h>
63244951Sadrian
64244951Sadrian#ifdef INET
65244951Sadrian#include <netinet/in.h>
66244951Sadrian#include <netinet/if_ether.h>
67244951Sadrian#endif
68244951Sadrian
69244951Sadrian#include <dev/ath/if_athvar.h>
70244951Sadrian#include <dev/ath/if_ath_spectral.h>
71244951Sadrian
72244951Sadrian#include <dev/ath/ath_hal/ah_desc.h>
73244951Sadrian
74244951Sadrianstruct ath_spectral_state {
75244951Sadrian	HAL_SPECTRAL_PARAM	spectral_state;
76245185Sadrian
77245185Sadrian	/*
78245185Sadrian	 * Should we enable spectral scan upon
79245185Sadrian	 * each network interface reset/change?
80245185Sadrian	 *
81245185Sadrian	 * This is intended to allow spectral scan
82245185Sadrian	 * frame reporting during channel scans.
83245185Sadrian	 *
84245185Sadrian	 * Later on it can morph into a larger
85245185Sadrian	 * scale config method where it pushes
86245185Sadrian	 * a "channel scan" config into the hardware
87245185Sadrian	 * rather than just the spectral_state
88245185Sadrian	 * config.
89245185Sadrian	 */
90245185Sadrian	int spectral_enable_after_reset;
91244951Sadrian};
92244951Sadrian
93244951Sadrian/*
94244951Sadrian * Methods which are required
95244951Sadrian */
96244951Sadrian
97244951Sadrian/*
98245002Sadrian * Attach spectral to the given interface
99244951Sadrian */
100244951Sadrianint
101244951Sadrianath_spectral_attach(struct ath_softc *sc)
102244951Sadrian{
103244951Sadrian	struct ath_spectral_state *ss;
104244951Sadrian
105245002Sadrian	/*
106245002Sadrian	 * If spectral isn't supported, don't error - just
107245002Sadrian	 * quietly complete.
108245002Sadrian	 */
109245002Sadrian	if (! ath_hal_spectral_supported(sc->sc_ah))
110245002Sadrian		return (0);
111245002Sadrian
112244951Sadrian	ss = malloc(sizeof(struct ath_spectral_state),
113244951Sadrian	    M_TEMP, M_WAITOK | M_ZERO);
114244951Sadrian
115244951Sadrian	if (ss == NULL) {
116244951Sadrian		device_printf(sc->sc_dev, "%s: failed to alloc memory\n",
117244951Sadrian		    __func__);
118244951Sadrian		return (-ENOMEM);
119244951Sadrian	}
120244951Sadrian
121244951Sadrian	sc->sc_spectral = ss;
122244951Sadrian
123244951Sadrian	(void) ath_hal_spectral_get_config(sc->sc_ah, &ss->spectral_state);
124244951Sadrian
125244951Sadrian	return (0);
126244951Sadrian}
127244951Sadrian
128244951Sadrian/*
129245002Sadrian * Detach spectral from the given interface
130244951Sadrian */
131244951Sadrianint
132244951Sadrianath_spectral_detach(struct ath_softc *sc)
133244951Sadrian{
134245002Sadrian
135245002Sadrian	if (! ath_hal_spectral_supported(sc->sc_ah))
136245002Sadrian		return (0);
137245002Sadrian
138244951Sadrian	if (sc->sc_spectral != NULL) {
139244951Sadrian		free(sc->sc_spectral, M_TEMP);
140244951Sadrian	}
141244951Sadrian	return (0);
142244951Sadrian}
143244951Sadrian
144244951Sadrian/*
145244951Sadrian * Check whether spectral needs enabling and if so,
146244951Sadrian * flip it on.
147244951Sadrian */
148244951Sadrianint
149244951Sadrianath_spectral_enable(struct ath_softc *sc, struct ieee80211_channel *ch)
150244951Sadrian{
151245185Sadrian	struct ath_spectral_state *ss = sc->sc_spectral;
152244951Sadrian
153245185Sadrian	/* Default to disable spectral PHY reporting */
154245185Sadrian	sc->sc_dospectral = 0;
155245185Sadrian
156245185Sadrian	if (ss == NULL)
157245185Sadrian		return (0);
158245185Sadrian
159245185Sadrian	if (ss->spectral_enable_after_reset) {
160245185Sadrian		ath_hal_spectral_configure(sc->sc_ah,
161245185Sadrian		    &ss->spectral_state);
162245185Sadrian		(void) ath_hal_spectral_start(sc->sc_ah);
163245185Sadrian		sc->sc_dospectral = 1;
164245185Sadrian	}
165244951Sadrian	return (0);
166244951Sadrian}
167244951Sadrian
168244951Sadrian/*
169244951Sadrian * Handle ioctl requests from the diagnostic interface.
170244951Sadrian *
171244951Sadrian * The initial part of this code resembles ath_ioctl_diag();
172244951Sadrian * it's likely a good idea to reduce duplication between
173244951Sadrian * these two routines.
174244951Sadrian */
175244951Sadrianint
176244951Sadrianath_ioctl_spectral(struct ath_softc *sc, struct ath_diag *ad)
177244951Sadrian{
178244951Sadrian	unsigned int id = ad->ad_id & ATH_DIAG_ID;
179244951Sadrian	void *indata = NULL;
180244951Sadrian	void *outdata = NULL;
181244951Sadrian	u_int32_t insize = ad->ad_in_size;
182244951Sadrian	u_int32_t outsize = ad->ad_out_size;
183244951Sadrian	int error = 0;
184244951Sadrian	HAL_SPECTRAL_PARAM peout;
185244951Sadrian	HAL_SPECTRAL_PARAM *pe;
186244951Sadrian	struct ath_spectral_state *ss = sc->sc_spectral;
187245185Sadrian	int val;
188244951Sadrian
189245002Sadrian	if (! ath_hal_spectral_supported(sc->sc_ah))
190245002Sadrian		return (EINVAL);
191245002Sadrian
192244951Sadrian	if (ad->ad_id & ATH_DIAG_IN) {
193244951Sadrian		/*
194244951Sadrian		 * Copy in data.
195244951Sadrian		 */
196244951Sadrian		indata = malloc(insize, M_TEMP, M_NOWAIT);
197244951Sadrian		if (indata == NULL) {
198244951Sadrian			error = ENOMEM;
199244951Sadrian			goto bad;
200244951Sadrian		}
201244951Sadrian		error = copyin(ad->ad_in_data, indata, insize);
202244951Sadrian		if (error)
203244951Sadrian			goto bad;
204244951Sadrian	}
205244951Sadrian	if (ad->ad_id & ATH_DIAG_DYN) {
206244951Sadrian		/*
207244951Sadrian		 * Allocate a buffer for the results (otherwise the HAL
208244951Sadrian		 * returns a pointer to a buffer where we can read the
209244951Sadrian		 * results).  Note that we depend on the HAL leaving this
210244951Sadrian		 * pointer for us to use below in reclaiming the buffer;
211244951Sadrian		 * may want to be more defensive.
212244951Sadrian		 */
213332320Semaste		outdata = malloc(outsize, M_TEMP, M_NOWAIT | M_ZERO);
214244951Sadrian		if (outdata == NULL) {
215244951Sadrian			error = ENOMEM;
216244951Sadrian			goto bad;
217244951Sadrian		}
218244951Sadrian	}
219244951Sadrian	switch (id) {
220244951Sadrian		case SPECTRAL_CONTROL_GET_PARAMS:
221244951Sadrian			memset(&peout, 0, sizeof(peout));
222244951Sadrian			outsize = sizeof(HAL_SPECTRAL_PARAM);
223244951Sadrian			ath_hal_spectral_get_config(sc->sc_ah, &peout);
224244951Sadrian			pe = (HAL_SPECTRAL_PARAM *) outdata;
225244951Sadrian			memcpy(pe, &peout, sizeof(*pe));
226244951Sadrian			break;
227244951Sadrian		case SPECTRAL_CONTROL_SET_PARAMS:
228244951Sadrian			if (insize < sizeof(HAL_SPECTRAL_PARAM)) {
229244951Sadrian				error = EINVAL;
230244951Sadrian				break;
231244951Sadrian			}
232244951Sadrian			pe = (HAL_SPECTRAL_PARAM *) indata;
233244951Sadrian			ath_hal_spectral_configure(sc->sc_ah, pe);
234244951Sadrian			/* Save a local copy of the updated parameters */
235244951Sadrian			ath_hal_spectral_get_config(sc->sc_ah,
236244951Sadrian			    &ss->spectral_state);
237244951Sadrian			break;
238244951Sadrian		case SPECTRAL_CONTROL_START:
239244951Sadrian			ath_hal_spectral_configure(sc->sc_ah,
240244951Sadrian			    &ss->spectral_state);
241244951Sadrian			(void) ath_hal_spectral_start(sc->sc_ah);
242245185Sadrian			sc->sc_dospectral = 1;
243245185Sadrian			/* XXX need to update the PHY mask in the driver */
244244951Sadrian			break;
245244951Sadrian		case SPECTRAL_CONTROL_STOP:
246244951Sadrian			(void) ath_hal_spectral_stop(sc->sc_ah);
247245185Sadrian			sc->sc_dospectral = 0;
248245185Sadrian			/* XXX need to update the PHY mask in the driver */
249244951Sadrian			break;
250245185Sadrian		case SPECTRAL_CONTROL_ENABLE_AT_RESET:
251245185Sadrian			if (insize < sizeof(int)) {
252245185Sadrian				device_printf(sc->sc_dev, "%d != %d\n",
253245185Sadrian				    insize,
254245190Sadrian				    (int) sizeof(int));
255245185Sadrian				error = EINVAL;
256245185Sadrian				break;
257245185Sadrian			}
258245185Sadrian			if (indata == NULL) {
259245185Sadrian				device_printf(sc->sc_dev, "indata=NULL\n");
260245185Sadrian				error = EINVAL;
261245185Sadrian				break;
262245185Sadrian			}
263245185Sadrian			val = * ((int *) indata);
264245185Sadrian			if (val == 0)
265245185Sadrian				ss->spectral_enable_after_reset = 0;
266245185Sadrian			else
267245185Sadrian				ss->spectral_enable_after_reset = 1;
268245185Sadrian			break;
269244951Sadrian		case SPECTRAL_CONTROL_ENABLE:
270244951Sadrian			/* XXX TODO */
271244951Sadrian		case SPECTRAL_CONTROL_DISABLE:
272244951Sadrian			/* XXX TODO */
273244951Sadrian		break;
274244951Sadrian		default:
275244951Sadrian			error = EINVAL;
276332320Semaste			goto bad;
277244951Sadrian	}
278244951Sadrian	if (outsize < ad->ad_out_size)
279244951Sadrian		ad->ad_out_size = outsize;
280244951Sadrian	if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size))
281244951Sadrian		error = EFAULT;
282244951Sadrianbad:
283244951Sadrian	if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
284244951Sadrian		free(indata, M_TEMP);
285244951Sadrian	if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
286244951Sadrian		free(outdata, M_TEMP);
287244951Sadrian	return (error);
288244951Sadrian}
289244951Sadrian
290