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: releng/11.0/sys/dev/ath/if_ath_spectral.c 257176 2013-10-26 17:58:36Z glebius $
30244951Sadrian */
31244951Sadrian#include <sys/cdefs.h>
32244951Sadrian__FBSDID("$FreeBSD: releng/11.0/sys/dev/ath/if_ath_spectral.c 257176 2013-10-26 17:58:36Z glebius $");
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>
46257176Sglebius#include <sys/malloc.h>
47244951Sadrian#include <sys/mutex.h>
48244951Sadrian#include <sys/errno.h>
49244951Sadrian
50244951Sadrian#include <machine/bus.h>
51244951Sadrian#include <machine/resource.h>
52244951Sadrian#include <sys/bus.h>
53244951Sadrian
54244951Sadrian#include <sys/socket.h>
55244951Sadrian
56244951Sadrian#include <net/if.h>
57257176Sglebius#include <net/if_var.h>
58244951Sadrian#include <net/if_media.h>
59244951Sadrian#include <net/if_arp.h>
60244951Sadrian#include <net/ethernet.h>		/* XXX for ether_sprintf */
61244951Sadrian
62244951Sadrian#include <net80211/ieee80211_var.h>
63244951Sadrian
64244951Sadrian#include <net/bpf.h>
65244951Sadrian
66244951Sadrian#ifdef INET
67244951Sadrian#include <netinet/in.h>
68244951Sadrian#include <netinet/if_ether.h>
69244951Sadrian#endif
70244951Sadrian
71244951Sadrian#include <dev/ath/if_athvar.h>
72244951Sadrian#include <dev/ath/if_ath_spectral.h>
73244951Sadrian
74244951Sadrian#include <dev/ath/ath_hal/ah_desc.h>
75244951Sadrian
76244951Sadrianstruct ath_spectral_state {
77244951Sadrian	HAL_SPECTRAL_PARAM	spectral_state;
78245185Sadrian
79245185Sadrian	/*
80245185Sadrian	 * Should we enable spectral scan upon
81245185Sadrian	 * each network interface reset/change?
82245185Sadrian	 *
83245185Sadrian	 * This is intended to allow spectral scan
84245185Sadrian	 * frame reporting during channel scans.
85245185Sadrian	 *
86245185Sadrian	 * Later on it can morph into a larger
87245185Sadrian	 * scale config method where it pushes
88245185Sadrian	 * a "channel scan" config into the hardware
89245185Sadrian	 * rather than just the spectral_state
90245185Sadrian	 * config.
91245185Sadrian	 */
92245185Sadrian	int spectral_enable_after_reset;
93244951Sadrian};
94244951Sadrian
95244951Sadrian/*
96244951Sadrian * Methods which are required
97244951Sadrian */
98244951Sadrian
99244951Sadrian/*
100245002Sadrian * Attach spectral to the given interface
101244951Sadrian */
102244951Sadrianint
103244951Sadrianath_spectral_attach(struct ath_softc *sc)
104244951Sadrian{
105244951Sadrian	struct ath_spectral_state *ss;
106244951Sadrian
107245002Sadrian	/*
108245002Sadrian	 * If spectral isn't supported, don't error - just
109245002Sadrian	 * quietly complete.
110245002Sadrian	 */
111245002Sadrian	if (! ath_hal_spectral_supported(sc->sc_ah))
112245002Sadrian		return (0);
113245002Sadrian
114244951Sadrian	ss = malloc(sizeof(struct ath_spectral_state),
115244951Sadrian	    M_TEMP, M_WAITOK | M_ZERO);
116244951Sadrian
117244951Sadrian	if (ss == NULL) {
118244951Sadrian		device_printf(sc->sc_dev, "%s: failed to alloc memory\n",
119244951Sadrian		    __func__);
120244951Sadrian		return (-ENOMEM);
121244951Sadrian	}
122244951Sadrian
123244951Sadrian	sc->sc_spectral = ss;
124244951Sadrian
125244951Sadrian	(void) ath_hal_spectral_get_config(sc->sc_ah, &ss->spectral_state);
126244951Sadrian
127244951Sadrian	return (0);
128244951Sadrian}
129244951Sadrian
130244951Sadrian/*
131245002Sadrian * Detach spectral from the given interface
132244951Sadrian */
133244951Sadrianint
134244951Sadrianath_spectral_detach(struct ath_softc *sc)
135244951Sadrian{
136245002Sadrian
137245002Sadrian	if (! ath_hal_spectral_supported(sc->sc_ah))
138245002Sadrian		return (0);
139245002Sadrian
140244951Sadrian	if (sc->sc_spectral != NULL) {
141244951Sadrian		free(sc->sc_spectral, M_TEMP);
142244951Sadrian	}
143244951Sadrian	return (0);
144244951Sadrian}
145244951Sadrian
146244951Sadrian/*
147244951Sadrian * Check whether spectral needs enabling and if so,
148244951Sadrian * flip it on.
149244951Sadrian */
150244951Sadrianint
151244951Sadrianath_spectral_enable(struct ath_softc *sc, struct ieee80211_channel *ch)
152244951Sadrian{
153245185Sadrian	struct ath_spectral_state *ss = sc->sc_spectral;
154244951Sadrian
155245185Sadrian	/* Default to disable spectral PHY reporting */
156245185Sadrian	sc->sc_dospectral = 0;
157245185Sadrian
158245185Sadrian	if (ss == NULL)
159245185Sadrian		return (0);
160245185Sadrian
161245185Sadrian	if (ss->spectral_enable_after_reset) {
162245185Sadrian		ath_hal_spectral_configure(sc->sc_ah,
163245185Sadrian		    &ss->spectral_state);
164245185Sadrian		(void) ath_hal_spectral_start(sc->sc_ah);
165245185Sadrian		sc->sc_dospectral = 1;
166245185Sadrian	}
167244951Sadrian	return (0);
168244951Sadrian}
169244951Sadrian
170244951Sadrian/*
171244951Sadrian * Handle ioctl requests from the diagnostic interface.
172244951Sadrian *
173244951Sadrian * The initial part of this code resembles ath_ioctl_diag();
174244951Sadrian * it's likely a good idea to reduce duplication between
175244951Sadrian * these two routines.
176244951Sadrian */
177244951Sadrianint
178244951Sadrianath_ioctl_spectral(struct ath_softc *sc, struct ath_diag *ad)
179244951Sadrian{
180244951Sadrian	unsigned int id = ad->ad_id & ATH_DIAG_ID;
181244951Sadrian	void *indata = NULL;
182244951Sadrian	void *outdata = NULL;
183244951Sadrian	u_int32_t insize = ad->ad_in_size;
184244951Sadrian	u_int32_t outsize = ad->ad_out_size;
185244951Sadrian	int error = 0;
186244951Sadrian	HAL_SPECTRAL_PARAM peout;
187244951Sadrian	HAL_SPECTRAL_PARAM *pe;
188244951Sadrian	struct ath_spectral_state *ss = sc->sc_spectral;
189245185Sadrian	int val;
190244951Sadrian
191245002Sadrian	if (! ath_hal_spectral_supported(sc->sc_ah))
192245002Sadrian		return (EINVAL);
193245002Sadrian
194244951Sadrian	if (ad->ad_id & ATH_DIAG_IN) {
195244951Sadrian		/*
196244951Sadrian		 * Copy in data.
197244951Sadrian		 */
198244951Sadrian		indata = malloc(insize, M_TEMP, M_NOWAIT);
199244951Sadrian		if (indata == NULL) {
200244951Sadrian			error = ENOMEM;
201244951Sadrian			goto bad;
202244951Sadrian		}
203244951Sadrian		error = copyin(ad->ad_in_data, indata, insize);
204244951Sadrian		if (error)
205244951Sadrian			goto bad;
206244951Sadrian	}
207244951Sadrian	if (ad->ad_id & ATH_DIAG_DYN) {
208244951Sadrian		/*
209244951Sadrian		 * Allocate a buffer for the results (otherwise the HAL
210244951Sadrian		 * returns a pointer to a buffer where we can read the
211244951Sadrian		 * results).  Note that we depend on the HAL leaving this
212244951Sadrian		 * pointer for us to use below in reclaiming the buffer;
213244951Sadrian		 * may want to be more defensive.
214244951Sadrian		 */
215244951Sadrian		outdata = malloc(outsize, M_TEMP, M_NOWAIT);
216244951Sadrian		if (outdata == NULL) {
217244951Sadrian			error = ENOMEM;
218244951Sadrian			goto bad;
219244951Sadrian		}
220244951Sadrian	}
221244951Sadrian	switch (id) {
222244951Sadrian		case SPECTRAL_CONTROL_GET_PARAMS:
223244951Sadrian			memset(&peout, 0, sizeof(peout));
224244951Sadrian			outsize = sizeof(HAL_SPECTRAL_PARAM);
225244951Sadrian			ath_hal_spectral_get_config(sc->sc_ah, &peout);
226244951Sadrian			pe = (HAL_SPECTRAL_PARAM *) outdata;
227244951Sadrian			memcpy(pe, &peout, sizeof(*pe));
228244951Sadrian			break;
229244951Sadrian		case SPECTRAL_CONTROL_SET_PARAMS:
230244951Sadrian			if (insize < sizeof(HAL_SPECTRAL_PARAM)) {
231244951Sadrian				error = EINVAL;
232244951Sadrian				break;
233244951Sadrian			}
234244951Sadrian			pe = (HAL_SPECTRAL_PARAM *) indata;
235244951Sadrian			ath_hal_spectral_configure(sc->sc_ah, pe);
236244951Sadrian			/* Save a local copy of the updated parameters */
237244951Sadrian			ath_hal_spectral_get_config(sc->sc_ah,
238244951Sadrian			    &ss->spectral_state);
239244951Sadrian			break;
240244951Sadrian		case SPECTRAL_CONTROL_START:
241244951Sadrian			ath_hal_spectral_configure(sc->sc_ah,
242244951Sadrian			    &ss->spectral_state);
243244951Sadrian			(void) ath_hal_spectral_start(sc->sc_ah);
244245185Sadrian			sc->sc_dospectral = 1;
245245185Sadrian			/* XXX need to update the PHY mask in the driver */
246244951Sadrian			break;
247244951Sadrian		case SPECTRAL_CONTROL_STOP:
248244951Sadrian			(void) ath_hal_spectral_stop(sc->sc_ah);
249245185Sadrian			sc->sc_dospectral = 0;
250245185Sadrian			/* XXX need to update the PHY mask in the driver */
251244951Sadrian			break;
252245185Sadrian		case SPECTRAL_CONTROL_ENABLE_AT_RESET:
253245185Sadrian			if (insize < sizeof(int)) {
254245185Sadrian				device_printf(sc->sc_dev, "%d != %d\n",
255245185Sadrian				    insize,
256245190Sadrian				    (int) sizeof(int));
257245185Sadrian				error = EINVAL;
258245185Sadrian				break;
259245185Sadrian			}
260245185Sadrian			if (indata == NULL) {
261245185Sadrian				device_printf(sc->sc_dev, "indata=NULL\n");
262245185Sadrian				error = EINVAL;
263245185Sadrian				break;
264245185Sadrian			}
265245185Sadrian			val = * ((int *) indata);
266245185Sadrian			if (val == 0)
267245185Sadrian				ss->spectral_enable_after_reset = 0;
268245185Sadrian			else
269245185Sadrian				ss->spectral_enable_after_reset = 1;
270245185Sadrian			break;
271244951Sadrian		case SPECTRAL_CONTROL_ENABLE:
272244951Sadrian			/* XXX TODO */
273244951Sadrian		case SPECTRAL_CONTROL_DISABLE:
274244951Sadrian			/* XXX TODO */
275244951Sadrian		break;
276244951Sadrian		default:
277244951Sadrian			error = EINVAL;
278244951Sadrian	}
279244951Sadrian	if (outsize < ad->ad_out_size)
280244951Sadrian		ad->ad_out_size = outsize;
281244951Sadrian	if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size))
282244951Sadrian		error = EFAULT;
283244951Sadrianbad:
284244951Sadrian	if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
285244951Sadrian		free(indata, M_TEMP);
286244951Sadrian	if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
287244951Sadrian		free(outdata, M_TEMP);
288244951Sadrian	return (error);
289244951Sadrian}
290244951Sadrian
291