xref: /freebsd/sys/dev/rtwn/rtl8821a/usb/r21au_dfs.c (revision 42249ef2)
1 /*-
2  * Copyright (c) 2016 Andriy Voskoboinyk <avos@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  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include "opt_wlan.h"
31 
32 #include <sys/param.h>
33 #include <sys/lock.h>
34 #include <sys/mutex.h>
35 #include <sys/mbuf.h>
36 #include <sys/kernel.h>
37 #include <sys/socket.h>
38 #include <sys/systm.h>
39 #include <sys/malloc.h>
40 #include <sys/queue.h>
41 #include <sys/taskqueue.h>
42 #include <sys/bus.h>
43 #include <sys/endian.h>
44 #include <sys/linker.h>
45 
46 #include <net/if.h>
47 #include <net/ethernet.h>
48 #include <net/if_media.h>
49 
50 #include <net80211/ieee80211_var.h>
51 #include <net80211/ieee80211_radiotap.h>
52 
53 #include <dev/rtwn/if_rtwnvar.h>
54 #include <dev/rtwn/if_rtwn_debug.h>
55 
56 #include <dev/rtwn/usb/rtwn_usb_var.h>
57 
58 #include <dev/rtwn/rtl8812a/r12a_var.h>
59 
60 #include <dev/rtwn/rtl8821a/usb/r21au.h>
61 #include <dev/rtwn/rtl8821a/usb/r21au_reg.h>
62 
63 
64 #define R21AU_RADAR_CHECK_PERIOD	(2 * hz)
65 
66 static void
67 r21au_dfs_radar_disable(struct rtwn_softc *sc)
68 {
69 	rtwn_bb_setbits(sc, 0x924, 0x00008000, 0);
70 }
71 
72 static int
73 r21au_dfs_radar_is_enabled(struct rtwn_softc *sc)
74 {
75 	return !!(rtwn_bb_read(sc, 0x924) & 0x00008000);
76 }
77 
78 static int
79 r21au_dfs_radar_reset(struct rtwn_softc *sc)
80 {
81 	int error;
82 
83 	error = rtwn_bb_setbits(sc, 0x924, 0x00008000, 0);
84 	if (error != 0)
85 		return (error);
86 
87 	return (rtwn_bb_setbits(sc, 0x924, 0, 0x00008000));
88 }
89 
90 static int
91 r21au_dfs_radar_enable(struct rtwn_softc *sc)
92 {
93 #define RTWN_CHK(res) do {	\
94 	if (res != 0)		\
95 		return (EIO);	\
96 } while(0)
97 
98 	RTWN_ASSERT_LOCKED(sc);
99 
100 	RTWN_CHK(rtwn_bb_setbits(sc, 0x814, 0x3fffffff, 0x04cc4d10));
101 	RTWN_CHK(rtwn_bb_setbits(sc, R12A_BW_INDICATION, 0xff, 0x06));
102 	RTWN_CHK(rtwn_bb_write(sc, 0x918, 0x1c16ecdf));
103 	RTWN_CHK(rtwn_bb_write(sc, 0x924, 0x0152a400));
104 	RTWN_CHK(rtwn_bb_write(sc, 0x91c, 0x0fa21a20));
105 	RTWN_CHK(rtwn_bb_write(sc, 0x920, 0xe0f57204));
106 
107 	return (r21au_dfs_radar_reset(sc));
108 
109 #undef RTWN_CHK
110 }
111 
112 static int
113 r21au_dfs_radar_is_detected(struct rtwn_softc *sc)
114 {
115 	return !!(rtwn_bb_read(sc, 0xf98) & 0x00020000);
116 }
117 
118 void
119 r21au_chan_check(void *arg, int npending __unused)
120 {
121 	struct rtwn_softc *sc = arg;
122 	struct r12a_softc *rs = sc->sc_priv;
123 	struct ieee80211com *ic = &sc->sc_ic;
124 
125 	RTWN_LOCK(sc);
126 #ifdef DIAGNOSTIC
127 	RTWN_DPRINTF(sc, RTWN_DEBUG_STATE,
128 	    "%s: periodical radar detection task\n", __func__);
129 #endif
130 
131 	if (!r21au_dfs_radar_is_enabled(sc)) {
132 		if (rs->rs_flags & R12A_RADAR_ENABLED) {
133 			/* should not happen */
134 			device_printf(sc->sc_dev,
135 			    "%s: radar detection was turned off "
136 			    "unexpectedly, resetting...\n", __func__);
137 
138 			/* XXX something more appropriate? */
139 			ieee80211_restart_all(ic);
140 		}
141 		RTWN_UNLOCK(sc);
142 		return;
143 	}
144 
145 	if (r21au_dfs_radar_is_detected(sc)) {
146 		r21au_dfs_radar_reset(sc);
147 
148 		RTWN_DPRINTF(sc, RTWN_DEBUG_RADAR, "%s: got radar event\n",
149 		    __func__);
150 
151 		RTWN_UNLOCK(sc);
152 		IEEE80211_LOCK(ic);
153 
154 		ieee80211_dfs_notify_radar(ic, ic->ic_curchan);
155 
156 		IEEE80211_UNLOCK(ic);
157 		RTWN_LOCK(sc);
158 	}
159 
160 	if (rs->rs_flags & R12A_RADAR_ENABLED) {
161 		taskqueue_enqueue_timeout(taskqueue_thread,
162 		    &rs->rs_chan_check, R21AU_RADAR_CHECK_PERIOD);
163 	}
164 	RTWN_UNLOCK(sc);
165 }
166 
167 int
168 r21au_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
169 {
170 	struct ieee80211com *ic = vap->iv_ic;
171 	struct rtwn_softc *sc = ic->ic_softc;
172 	struct rtwn_vap *rvp = RTWN_VAP(vap);
173 	struct r12a_softc *rs = sc->sc_priv;
174 	int error;
175 
176 	KASSERT(rvp->id == 0 || rvp->id == 1,
177 	    ("%s: unexpected vap id %d\n", __func__, rvp->id));
178 
179 	IEEE80211_UNLOCK(ic);
180 	RTWN_LOCK(sc);
181 
182 	error = 0;
183 	if (nstate == IEEE80211_S_CAC &&
184 	    !(rs->rs_flags & R12A_RADAR_ENABLED)) {
185 		error = r21au_dfs_radar_enable(sc);
186 		if (error != 0) {
187 			device_printf(sc->sc_dev,
188 			    "%s: cannot enable radar detection\n", __func__);
189 			goto fail;
190 		}
191 		rs->rs_flags |= R12A_RADAR_ENABLED;
192 
193 		RTWN_DPRINTF(sc, RTWN_DEBUG_RADAR,
194 		    "%s: radar detection was enabled\n", __func__);
195 
196 		taskqueue_enqueue_timeout(taskqueue_thread,
197 		    &rs->rs_chan_check, R21AU_RADAR_CHECK_PERIOD);
198 	}
199 
200 	if ((nstate < IEEE80211_S_CAC || nstate == IEEE80211_S_CSA) &&
201 	    (rs->rs_flags & R12A_RADAR_ENABLED) &&
202 	    (sc->vaps[!rvp->id] == NULL ||
203 	    sc->vaps[!rvp->id]->vap.iv_state < IEEE80211_S_CAC ||
204 	    sc->vaps[!rvp->id]->vap.iv_state == IEEE80211_S_CSA)) {
205 		taskqueue_cancel_timeout(taskqueue_thread, &rs->rs_chan_check,
206 		    NULL);
207 
208 		rs->rs_flags &= ~R12A_RADAR_ENABLED;
209 		r21au_dfs_radar_disable(sc);
210 
211 		RTWN_DPRINTF(sc, RTWN_DEBUG_RADAR,
212 		    "%s: radar detection was disabled\n", __func__);
213 	}
214 
215 fail:
216 	RTWN_UNLOCK(sc);
217 	IEEE80211_LOCK(ic);
218 
219 	if (error != 0)
220 		return (error);
221 
222 	return (rs->rs_newstate[rvp->id](vap, nstate, arg));
223 }
224 
225 void
226 r21au_scan_start(struct ieee80211com *ic)
227 {
228 	struct rtwn_softc *sc = ic->ic_softc;
229 	struct r12a_softc *rs = sc->sc_priv;
230 
231 	RTWN_LOCK(sc);
232 	if (rs->rs_flags & R12A_RADAR_ENABLED) {
233 		RTWN_UNLOCK(sc);
234 		while (taskqueue_cancel_timeout(taskqueue_thread,
235 		    &rs->rs_chan_check, NULL) != 0) {
236 			taskqueue_drain_timeout(taskqueue_thread,
237 			    &rs->rs_chan_check);
238 		}
239 		RTWN_LOCK(sc);
240 
241 		r21au_dfs_radar_disable(sc);
242 		RTWN_DPRINTF(sc, RTWN_DEBUG_RADAR,
243 		    "%s: radar detection was (temporarily) disabled\n",
244 		    __func__);
245 	}
246 	RTWN_UNLOCK(sc);
247 
248 	rs->rs_scan_start(ic);
249 }
250 
251 void
252 r21au_scan_end(struct ieee80211com *ic)
253 {
254 	struct rtwn_softc *sc = ic->ic_softc;
255 	struct r12a_softc *rs = sc->sc_priv;
256 	int error;
257 
258 	RTWN_LOCK(sc);
259 	if (rs->rs_flags & R12A_RADAR_ENABLED) {
260 		error = r21au_dfs_radar_enable(sc);
261 		if (error != 0) {
262 			device_printf(sc->sc_dev,
263 			    "%s: cannot re-enable radar detection\n",
264 			    __func__);
265 
266 			/* XXX */
267 			ieee80211_restart_all(ic);
268 			RTWN_UNLOCK(sc);
269 			return;
270 		}
271 		RTWN_DPRINTF(sc, RTWN_DEBUG_RADAR,
272 		    "%s: radar detection was re-enabled\n", __func__);
273 
274 		taskqueue_enqueue_timeout(taskqueue_thread,
275 		    &rs->rs_chan_check, R21AU_RADAR_CHECK_PERIOD);
276 	}
277 	RTWN_UNLOCK(sc);
278 
279 	rs->rs_scan_end(ic);
280 }
281