25a19511ea4e0e2ed10f6c8ddf03f33a66c4f64c
[dragonfly.git] / sys / dev / netif / ath / ath / if_ath_spectral.c
1 /*-
2  * Copyright (c) 2013 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  * $FreeBSD$
30  */
31 #include <sys/cdefs.h>
32
33 /*
34  * Implement some basic spectral scan control logic.
35  */
36 #include "opt_ath.h"
37 #include "opt_inet.h"
38 #include "opt_wlan.h"
39
40 #include <sys/param.h>
41 #include <sys/systm.h> 
42 #include <sys/sysctl.h>
43 #include <sys/kernel.h>
44 #include <sys/lock.h>
45 #include <sys/malloc.h>
46 #include <sys/mutex.h>
47 #include <sys/errno.h>
48 #include <sys/bus.h>
49 #include <sys/socket.h>
50  
51 #include <net/if.h>
52 #include <net/if_var.h>
53 #include <net/if_media.h>
54 #include <net/if_arp.h>
55 #include <net/ethernet.h>               /* XXX for ether_sprintf */
56
57 #include <netproto/802_11/ieee80211_var.h>
58
59 #include <net/bpf.h>
60
61 #ifdef INET
62 #include <netinet/in.h>
63 #include <netinet/if_ether.h>
64 #endif
65
66 #include <machine/resource.h>
67
68 #include <dev/netif/ath/ath/if_athvar.h>
69 #include <dev/netif/ath/ath/if_ath_spectral.h>
70
71 #include <dev/netif/ath/ath_hal/ah_desc.h>
72
73 struct ath_spectral_state {
74         HAL_SPECTRAL_PARAM      spectral_state;
75
76         /*
77          * Should we enable spectral scan upon
78          * each network interface reset/change?
79          *
80          * This is intended to allow spectral scan
81          * frame reporting during channel scans.
82          *
83          * Later on it can morph into a larger
84          * scale config method where it pushes
85          * a "channel scan" config into the hardware
86          * rather than just the spectral_state
87          * config.
88          */
89         int spectral_enable_after_reset;
90 };
91
92 /*
93  * Methods which are required
94  */
95
96 /*
97  * Attach spectral to the given interface
98  */
99 int
100 ath_spectral_attach(struct ath_softc *sc)
101 {
102         struct ath_spectral_state *ss;
103
104         /*
105          * If spectral isn't supported, don't error - just
106          * quietly complete.
107          */
108         if (! ath_hal_spectral_supported(sc->sc_ah))
109                 return (0);
110
111         ss = malloc(sizeof(struct ath_spectral_state),
112             M_TEMP, M_WAITOK | M_ZERO);
113
114         if (ss == NULL) {
115                 device_printf(sc->sc_dev, "%s: failed to alloc memory\n",
116                     __func__);
117                 return (-ENOMEM);
118         }
119
120         sc->sc_spectral = ss;
121
122         (void) ath_hal_spectral_get_config(sc->sc_ah, &ss->spectral_state);
123
124         return (0);
125 }
126
127 /*
128  * Detach spectral from the given interface
129  */
130 int
131 ath_spectral_detach(struct ath_softc *sc)
132 {
133
134         if (! ath_hal_spectral_supported(sc->sc_ah))
135                 return (0);
136
137         if (sc->sc_spectral != NULL) {
138                 free(sc->sc_spectral, M_TEMP);
139         }
140         return (0);
141 }
142
143 /*
144  * Check whether spectral needs enabling and if so,
145  * flip it on.
146  */
147 int
148 ath_spectral_enable(struct ath_softc *sc, struct ieee80211_channel *ch)
149 {
150         struct ath_spectral_state *ss = sc->sc_spectral;
151
152         /* Default to disable spectral PHY reporting */
153         sc->sc_dospectral = 0;
154
155         if (ss == NULL)
156                 return (0);
157
158         if (ss->spectral_enable_after_reset) {
159                 ath_hal_spectral_configure(sc->sc_ah,
160                     &ss->spectral_state);
161                 (void) ath_hal_spectral_start(sc->sc_ah);
162                 sc->sc_dospectral = 1;
163         }
164         return (0);
165 }
166
167 /*
168  * Handle ioctl requests from the diagnostic interface.
169  *
170  * The initial part of this code resembles ath_ioctl_diag();
171  * it's likely a good idea to reduce duplication between
172  * these two routines.
173  */
174 int
175 ath_ioctl_spectral(struct ath_softc *sc, struct ath_diag *ad)
176 {
177         unsigned int id = ad->ad_id & ATH_DIAG_ID;
178         void *indata = NULL;
179         void *outdata = NULL;
180         u_int32_t insize = ad->ad_in_size;
181         u_int32_t outsize = ad->ad_out_size;
182         int error = 0;
183         HAL_SPECTRAL_PARAM peout;
184         HAL_SPECTRAL_PARAM *pe;
185         struct ath_spectral_state *ss = sc->sc_spectral;
186         int val;
187
188         if (! ath_hal_spectral_supported(sc->sc_ah))
189                 return (EINVAL);
190
191         if (ad->ad_id & ATH_DIAG_IN) {
192                 /*
193                  * Copy in data.
194                  */
195                 indata = malloc(insize, M_TEMP, M_NOWAIT);
196                 if (indata == NULL) {
197                         error = ENOMEM;
198                         goto bad;
199                 }
200                 error = copyin(ad->ad_in_data, indata, insize);
201                 if (error)
202                         goto bad;
203         }
204         if (ad->ad_id & ATH_DIAG_DYN) {
205                 /*
206                  * Allocate a buffer for the results (otherwise the HAL
207                  * returns a pointer to a buffer where we can read the
208                  * results).  Note that we depend on the HAL leaving this
209                  * pointer for us to use below in reclaiming the buffer;
210                  * may want to be more defensive.
211                  */
212                 outdata = malloc(outsize, M_TEMP, M_NOWAIT);
213                 if (outdata == NULL) {
214                         error = ENOMEM;
215                         goto bad;
216                 }
217         }
218         switch (id) {
219                 case SPECTRAL_CONTROL_GET_PARAMS:
220                         memset(&peout, 0, sizeof(peout));
221                         outsize = sizeof(HAL_SPECTRAL_PARAM);
222                         ath_hal_spectral_get_config(sc->sc_ah, &peout);
223                         pe = (HAL_SPECTRAL_PARAM *) outdata;
224                         memcpy(pe, &peout, sizeof(*pe));
225                         break;
226                 case SPECTRAL_CONTROL_SET_PARAMS:
227                         if (insize < sizeof(HAL_SPECTRAL_PARAM)) {
228                                 error = EINVAL;
229                                 break;
230                         }
231                         pe = (HAL_SPECTRAL_PARAM *) indata;
232                         ath_hal_spectral_configure(sc->sc_ah, pe);
233                         /* Save a local copy of the updated parameters */
234                         ath_hal_spectral_get_config(sc->sc_ah,
235                             &ss->spectral_state);
236                         break;
237                 case SPECTRAL_CONTROL_START:
238                         ath_hal_spectral_configure(sc->sc_ah,
239                             &ss->spectral_state);
240                         (void) ath_hal_spectral_start(sc->sc_ah);
241                         sc->sc_dospectral = 1;
242                         /* XXX need to update the PHY mask in the driver */
243                         break;
244                 case SPECTRAL_CONTROL_STOP:
245                         (void) ath_hal_spectral_stop(sc->sc_ah);
246                         sc->sc_dospectral = 0;
247                         /* XXX need to update the PHY mask in the driver */
248                         break;
249                 case SPECTRAL_CONTROL_ENABLE_AT_RESET:
250                         if (insize < sizeof(int)) {
251                                 device_printf(sc->sc_dev, "%d != %d\n",
252                                     insize,
253                                     (int) sizeof(int));
254                                 error = EINVAL;
255                                 break;
256                         }
257                         if (indata == NULL) {
258                                 device_printf(sc->sc_dev, "indata=NULL\n");
259                                 error = EINVAL;
260                                 break;
261                         }
262                         val = * ((int *) indata);
263                         if (val == 0)
264                                 ss->spectral_enable_after_reset = 0;
265                         else
266                                 ss->spectral_enable_after_reset = 1;
267                         break;
268                 case SPECTRAL_CONTROL_ENABLE:
269                         /* XXX TODO */
270                 case SPECTRAL_CONTROL_DISABLE:
271                         /* XXX TODO */
272                 break;
273                 default:
274                         error = EINVAL;
275         }
276         if (outsize < ad->ad_out_size)
277                 ad->ad_out_size = outsize;
278         if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size))
279                 error = EFAULT;
280 bad:
281         if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
282                 free(indata, M_TEMP);
283         if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
284                 free(outdata, M_TEMP);
285         return (error);
286 }
287