xref: /freebsd/sys/dev/rtwn/rtl8821a/usb/r21au_dfs.c (revision 22cf89c938886d14f5796fc49f9f020c23ea8eaf)
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 #include "opt_wlan.h"
29 
30 #include <sys/param.h>
31 #include <sys/lock.h>
32 #include <sys/mutex.h>
33 #include <sys/mbuf.h>
34 #include <sys/kernel.h>
35 #include <sys/socket.h>
36 #include <sys/systm.h>
37 #include <sys/malloc.h>
38 #include <sys/queue.h>
39 #include <sys/taskqueue.h>
40 #include <sys/bus.h>
41 #include <sys/endian.h>
42 #include <sys/linker.h>
43 
44 #include <net/if.h>
45 #include <net/ethernet.h>
46 #include <net/if_media.h>
47 
48 #include <net80211/ieee80211_var.h>
49 #include <net80211/ieee80211_radiotap.h>
50 
51 #include <dev/rtwn/if_rtwnvar.h>
52 #include <dev/rtwn/if_rtwn_debug.h>
53 
54 #include <dev/rtwn/usb/rtwn_usb_var.h>
55 
56 #include <dev/rtwn/rtl8812a/r12a_var.h>
57 
58 #include <dev/rtwn/rtl8821a/usb/r21au.h>
59 #include <dev/rtwn/rtl8821a/usb/r21au_reg.h>
60 
61 #define R21AU_RADAR_CHECK_PERIOD	(2 * hz)
62 
63 static void
64 r21au_dfs_radar_disable(struct rtwn_softc *sc)
65 {
66 	rtwn_bb_setbits(sc, 0x924, 0x00008000, 0);
67 }
68 
69 static int
70 r21au_dfs_radar_is_enabled(struct rtwn_softc *sc)
71 {
72 	return !!(rtwn_bb_read(sc, 0x924) & 0x00008000);
73 }
74 
75 static int
76 r21au_dfs_radar_reset(struct rtwn_softc *sc)
77 {
78 	int error;
79 
80 	error = rtwn_bb_setbits(sc, 0x924, 0x00008000, 0);
81 	if (error != 0)
82 		return (error);
83 
84 	return (rtwn_bb_setbits(sc, 0x924, 0, 0x00008000));
85 }
86 
87 static int
88 r21au_dfs_radar_enable(struct rtwn_softc *sc)
89 {
90 #define RTWN_CHK(res) do {	\
91 	if (res != 0)		\
92 		return (EIO);	\
93 } while(0)
94 
95 	RTWN_ASSERT_LOCKED(sc);
96 
97 	RTWN_CHK(rtwn_bb_setbits(sc, 0x814, 0x3fffffff, 0x04cc4d10));
98 	RTWN_CHK(rtwn_bb_setbits(sc, R12A_BW_INDICATION, 0xff, 0x06));
99 	RTWN_CHK(rtwn_bb_write(sc, 0x918, 0x1c16ecdf));
100 	RTWN_CHK(rtwn_bb_write(sc, 0x924, 0x0152a400));
101 	RTWN_CHK(rtwn_bb_write(sc, 0x91c, 0x0fa21a20));
102 	RTWN_CHK(rtwn_bb_write(sc, 0x920, 0xe0f57204));
103 
104 	return (r21au_dfs_radar_reset(sc));
105 
106 #undef RTWN_CHK
107 }
108 
109 static int
110 r21au_dfs_radar_is_detected(struct rtwn_softc *sc)
111 {
112 	return !!(rtwn_bb_read(sc, 0xf98) & 0x00020000);
113 }
114 
115 void
116 r21au_chan_check(void *arg, int npending __unused)
117 {
118 	struct rtwn_softc *sc = arg;
119 	struct r12a_softc *rs = sc->sc_priv;
120 	struct ieee80211com *ic = &sc->sc_ic;
121 
122 	RTWN_LOCK(sc);
123 #ifdef DIAGNOSTIC
124 	RTWN_DPRINTF(sc, RTWN_DEBUG_STATE,
125 	    "%s: periodical radar detection task\n", __func__);
126 #endif
127 
128 	if (!r21au_dfs_radar_is_enabled(sc)) {
129 		if (rs->rs_flags & R12A_RADAR_ENABLED) {
130 			/* should not happen */
131 			device_printf(sc->sc_dev,
132 			    "%s: radar detection was turned off "
133 			    "unexpectedly, resetting...\n", __func__);
134 
135 			/* XXX something more appropriate? */
136 			ieee80211_restart_all(ic);
137 		}
138 		RTWN_UNLOCK(sc);
139 		return;
140 	}
141 
142 	if (r21au_dfs_radar_is_detected(sc)) {
143 		r21au_dfs_radar_reset(sc);
144 
145 		RTWN_DPRINTF(sc, RTWN_DEBUG_RADAR, "%s: got radar event\n",
146 		    __func__);
147 
148 		RTWN_UNLOCK(sc);
149 		IEEE80211_LOCK(ic);
150 
151 		ieee80211_dfs_notify_radar(ic, ic->ic_curchan);
152 
153 		IEEE80211_UNLOCK(ic);
154 		RTWN_LOCK(sc);
155 	}
156 
157 	if (rs->rs_flags & R12A_RADAR_ENABLED) {
158 		taskqueue_enqueue_timeout(taskqueue_thread,
159 		    &rs->rs_chan_check, R21AU_RADAR_CHECK_PERIOD);
160 	}
161 	RTWN_UNLOCK(sc);
162 }
163 
164 int
165 r21au_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
166 {
167 	struct ieee80211com *ic = vap->iv_ic;
168 	struct rtwn_softc *sc = ic->ic_softc;
169 	struct rtwn_vap *rvp = RTWN_VAP(vap);
170 	struct r12a_softc *rs = sc->sc_priv;
171 	int error;
172 
173 	KASSERT(rvp->id == 0 || rvp->id == 1,
174 	    ("%s: unexpected vap id %d\n", __func__, rvp->id));
175 
176 	IEEE80211_UNLOCK(ic);
177 	RTWN_LOCK(sc);
178 
179 	error = 0;
180 	if (nstate == IEEE80211_S_CAC &&
181 	    !(rs->rs_flags & R12A_RADAR_ENABLED)) {
182 		error = r21au_dfs_radar_enable(sc);
183 		if (error != 0) {
184 			device_printf(sc->sc_dev,
185 			    "%s: cannot enable radar detection\n", __func__);
186 			goto fail;
187 		}
188 		rs->rs_flags |= R12A_RADAR_ENABLED;
189 
190 		RTWN_DPRINTF(sc, RTWN_DEBUG_RADAR,
191 		    "%s: radar detection was enabled\n", __func__);
192 
193 		taskqueue_enqueue_timeout(taskqueue_thread,
194 		    &rs->rs_chan_check, R21AU_RADAR_CHECK_PERIOD);
195 	}
196 
197 	if ((nstate < IEEE80211_S_CAC || nstate == IEEE80211_S_CSA) &&
198 	    (rs->rs_flags & R12A_RADAR_ENABLED) &&
199 	    (sc->vaps[!rvp->id] == NULL ||
200 	    sc->vaps[!rvp->id]->vap.iv_state < IEEE80211_S_CAC ||
201 	    sc->vaps[!rvp->id]->vap.iv_state == IEEE80211_S_CSA)) {
202 		taskqueue_cancel_timeout(taskqueue_thread, &rs->rs_chan_check,
203 		    NULL);
204 
205 		rs->rs_flags &= ~R12A_RADAR_ENABLED;
206 		r21au_dfs_radar_disable(sc);
207 
208 		RTWN_DPRINTF(sc, RTWN_DEBUG_RADAR,
209 		    "%s: radar detection was disabled\n", __func__);
210 	}
211 
212 fail:
213 	RTWN_UNLOCK(sc);
214 	IEEE80211_LOCK(ic);
215 
216 	if (error != 0)
217 		return (error);
218 
219 	return (rs->rs_newstate[rvp->id](vap, nstate, arg));
220 }
221 
222 void
223 r21au_scan_start(struct ieee80211com *ic)
224 {
225 	struct rtwn_softc *sc = ic->ic_softc;
226 	struct r12a_softc *rs = sc->sc_priv;
227 
228 	RTWN_LOCK(sc);
229 	if (rs->rs_flags & R12A_RADAR_ENABLED) {
230 		RTWN_UNLOCK(sc);
231 		while (taskqueue_cancel_timeout(taskqueue_thread,
232 		    &rs->rs_chan_check, NULL) != 0) {
233 			taskqueue_drain_timeout(taskqueue_thread,
234 			    &rs->rs_chan_check);
235 		}
236 		RTWN_LOCK(sc);
237 
238 		r21au_dfs_radar_disable(sc);
239 		RTWN_DPRINTF(sc, RTWN_DEBUG_RADAR,
240 		    "%s: radar detection was (temporarily) disabled\n",
241 		    __func__);
242 	}
243 	RTWN_UNLOCK(sc);
244 
245 	rs->rs_scan_start(ic);
246 }
247 
248 void
249 r21au_scan_end(struct ieee80211com *ic)
250 {
251 	struct rtwn_softc *sc = ic->ic_softc;
252 	struct r12a_softc *rs = sc->sc_priv;
253 	int error;
254 
255 	RTWN_LOCK(sc);
256 	if (rs->rs_flags & R12A_RADAR_ENABLED) {
257 		error = r21au_dfs_radar_enable(sc);
258 		if (error != 0) {
259 			device_printf(sc->sc_dev,
260 			    "%s: cannot re-enable radar detection\n",
261 			    __func__);
262 
263 			/* XXX */
264 			ieee80211_restart_all(ic);
265 			RTWN_UNLOCK(sc);
266 			return;
267 		}
268 		RTWN_DPRINTF(sc, RTWN_DEBUG_RADAR,
269 		    "%s: radar detection was re-enabled\n", __func__);
270 
271 		taskqueue_enqueue_timeout(taskqueue_thread,
272 		    &rs->rs_chan_check, R21AU_RADAR_CHECK_PERIOD);
273 	}
274 	RTWN_UNLOCK(sc);
275 
276 	rs->rs_scan_end(ic);
277 }
278