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