1/*	$OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $	*/
2
3/*-
4 * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
5 * Copyright (c) 2014 Kevin Lo <kevlo@FreeBSD.org>
6 * Copyright (c) 2015-2016 Andriy Voskoboinyk <avos@FreeBSD.org>
7 *
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 */
20
21#include <sys/cdefs.h>
22__FBSDID("$FreeBSD$");
23
24#include <sys/param.h>
25#include <sys/sysctl.h>
26#include <sys/lock.h>
27#include <sys/mutex.h>
28#include <sys/mbuf.h>
29#include <sys/kernel.h>
30#include <sys/socket.h>
31#include <sys/systm.h>
32#include <sys/malloc.h>
33#include <sys/module.h>
34#include <sys/bus.h>
35#include <sys/endian.h>
36#include <sys/linker.h>
37#include <sys/kdb.h>
38
39#include <net/if.h>
40#include <net/if_var.h>
41#include <net/ethernet.h>
42#include <net/if_media.h>
43
44#include <net80211/ieee80211_var.h>
45
46#include <dev/usb/usb.h>
47#include <dev/usb/usbdi.h>
48#include "usbdevs.h"
49
50#include <dev/rtwn/if_rtwnvar.h>
51#include <dev/rtwn/if_rtwn_nop.h>
52
53#include <dev/rtwn/usb/rtwn_usb_var.h>
54
55#include <dev/rtwn/usb/rtwn_usb_attach.h>
56#include <dev/rtwn/usb/rtwn_usb_ep.h>
57#include <dev/rtwn/usb/rtwn_usb_reg.h>
58#include <dev/rtwn/usb/rtwn_usb_tx.h>
59
60#include <dev/rtwn/rtl8192c/r92c_reg.h>
61
62static device_probe_t	rtwn_usb_match;
63static device_attach_t	rtwn_usb_attach;
64static device_detach_t	rtwn_usb_detach;
65static device_suspend_t	rtwn_usb_suspend;
66static device_resume_t	rtwn_usb_resume;
67
68static int	rtwn_usb_alloc_list(struct rtwn_softc *,
69		    struct rtwn_data[], int, int);
70static int	rtwn_usb_alloc_rx_list(struct rtwn_softc *);
71static int	rtwn_usb_alloc_tx_list(struct rtwn_softc *);
72static void	rtwn_usb_free_list(struct rtwn_softc *,
73		    struct rtwn_data data[], int);
74static void	rtwn_usb_free_rx_list(struct rtwn_softc *);
75static void	rtwn_usb_free_tx_list(struct rtwn_softc *);
76static void	rtwn_usb_reset_lists(struct rtwn_softc *,
77		    struct ieee80211vap *);
78static void	rtwn_usb_reset_tx_list(struct rtwn_usb_softc *,
79		    rtwn_datahead *, struct ieee80211vap *);
80static void	rtwn_usb_reset_rx_list(struct rtwn_usb_softc *);
81static void	rtwn_usb_start_xfers(struct rtwn_softc *);
82static void	rtwn_usb_abort_xfers(struct rtwn_softc *);
83static int	rtwn_usb_fw_write_block(struct rtwn_softc *,
84		    const uint8_t *, uint16_t, int);
85static void	rtwn_usb_drop_incorrect_tx(struct rtwn_softc *);
86static void	rtwn_usb_attach_methods(struct rtwn_softc *);
87static void	rtwn_usb_sysctlattach(struct rtwn_softc *);
88
89#define RTWN_CONFIG_INDEX	0
90
91static int
92rtwn_usb_match(device_t self)
93{
94	struct usb_attach_arg *uaa = device_get_ivars(self);
95
96	if (uaa->usb_mode != USB_MODE_HOST)
97		return (ENXIO);
98	if (uaa->info.bConfigIndex != RTWN_CONFIG_INDEX)
99		return (ENXIO);
100	if (uaa->info.bIfaceIndex != RTWN_IFACE_INDEX)
101		return (ENXIO);
102
103	return (usbd_lookup_id_by_uaa(rtwn_devs, sizeof(rtwn_devs), uaa));
104}
105
106static int
107rtwn_usb_alloc_list(struct rtwn_softc *sc, struct rtwn_data data[],
108    int ndata, int maxsz)
109{
110	int i, error;
111
112	for (i = 0; i < ndata; i++) {
113		struct rtwn_data *dp = &data[i];
114		dp->m = NULL;
115		dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT);
116		if (dp->buf == NULL) {
117			device_printf(sc->sc_dev,
118			    "could not allocate buffer\n");
119			error = ENOMEM;
120			goto fail;
121		}
122		dp->ni = NULL;
123	}
124
125	return (0);
126fail:
127	rtwn_usb_free_list(sc, data, ndata);
128	return (error);
129}
130
131static int
132rtwn_usb_alloc_rx_list(struct rtwn_softc *sc)
133{
134	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
135	int error, i;
136
137	error = rtwn_usb_alloc_list(sc, uc->uc_rx, RTWN_USB_RX_LIST_COUNT,
138	    uc->uc_rx_buf_size * RTWN_USB_RXBUFSZ_UNIT);
139	if (error != 0)
140		return (error);
141
142	STAILQ_INIT(&uc->uc_rx_active);
143	STAILQ_INIT(&uc->uc_rx_inactive);
144
145	for (i = 0; i < RTWN_USB_RX_LIST_COUNT; i++)
146		STAILQ_INSERT_HEAD(&uc->uc_rx_inactive, &uc->uc_rx[i], next);
147
148	return (0);
149}
150
151static int
152rtwn_usb_alloc_tx_list(struct rtwn_softc *sc)
153{
154	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
155	int error, i;
156
157	error = rtwn_usb_alloc_list(sc, uc->uc_tx, RTWN_USB_TX_LIST_COUNT,
158	    RTWN_USB_TXBUFSZ);
159	if (error != 0)
160		return (error);
161
162	STAILQ_INIT(&uc->uc_tx_active);
163	STAILQ_INIT(&uc->uc_tx_inactive);
164	STAILQ_INIT(&uc->uc_tx_pending);
165
166	for (i = 0; i < RTWN_USB_TX_LIST_COUNT; i++)
167		STAILQ_INSERT_HEAD(&uc->uc_tx_inactive, &uc->uc_tx[i], next);
168
169	return (0);
170}
171
172static void
173rtwn_usb_free_list(struct rtwn_softc *sc, struct rtwn_data data[], int ndata)
174{
175	int i;
176
177	for (i = 0; i < ndata; i++) {
178		struct rtwn_data *dp = &data[i];
179
180		if (dp->buf != NULL) {
181			free(dp->buf, M_USBDEV);
182			dp->buf = NULL;
183		}
184		if (dp->ni != NULL) {
185			ieee80211_free_node(dp->ni);
186			dp->ni = NULL;
187		}
188		if (dp->m != NULL) {
189			m_freem(dp->m);
190			dp->m = NULL;
191		}
192	}
193}
194
195static void
196rtwn_usb_free_rx_list(struct rtwn_softc *sc)
197{
198	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
199
200	rtwn_usb_free_list(sc, uc->uc_rx, RTWN_USB_RX_LIST_COUNT);
201
202	uc->uc_rx_stat_len = 0;
203	uc->uc_rx_off = 0;
204
205	STAILQ_INIT(&uc->uc_rx_active);
206	STAILQ_INIT(&uc->uc_rx_inactive);
207}
208
209static void
210rtwn_usb_free_tx_list(struct rtwn_softc *sc)
211{
212	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
213
214	rtwn_usb_free_list(sc, uc->uc_tx, RTWN_USB_TX_LIST_COUNT);
215
216	STAILQ_INIT(&uc->uc_tx_active);
217	STAILQ_INIT(&uc->uc_tx_inactive);
218	STAILQ_INIT(&uc->uc_tx_pending);
219}
220
221static void
222rtwn_usb_reset_lists(struct rtwn_softc *sc, struct ieee80211vap *vap)
223{
224	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
225
226	RTWN_ASSERT_LOCKED(sc);
227
228	rtwn_usb_reset_tx_list(uc, &uc->uc_tx_active, vap);
229	rtwn_usb_reset_tx_list(uc, &uc->uc_tx_pending, vap);
230	if (vap == NULL) {
231		rtwn_usb_reset_rx_list(uc);
232		sc->qfullmsk = 0;
233	}
234}
235
236static void
237rtwn_usb_reset_tx_list(struct rtwn_usb_softc *uc,
238    rtwn_datahead *head, struct ieee80211vap *vap)
239{
240	struct rtwn_vap *uvp = RTWN_VAP(vap);
241	struct rtwn_data *dp, *tmp;
242	int id;
243
244	id = (uvp != NULL ? uvp->id : RTWN_VAP_ID_INVALID);
245
246	STAILQ_FOREACH_SAFE(dp, head, next, tmp) {
247		if (vap == NULL || (dp->ni == NULL &&
248		    (dp->id == id || id == RTWN_VAP_ID_INVALID)) ||
249		    (dp->ni != NULL && dp->ni->ni_vap == vap)) {
250			if (dp->ni != NULL) {
251				ieee80211_free_node(dp->ni);
252				dp->ni = NULL;
253			}
254
255			if (dp->m != NULL) {
256				m_freem(dp->m);
257				dp->m = NULL;
258			}
259
260			STAILQ_REMOVE(head, dp, rtwn_data, next);
261			STAILQ_INSERT_TAIL(&uc->uc_tx_inactive, dp, next);
262		}
263	}
264}
265
266static void
267rtwn_usb_reset_rx_list(struct rtwn_usb_softc *uc)
268{
269	int i;
270
271	for (i = 0; i < RTWN_USB_RX_LIST_COUNT; i++) {
272		struct rtwn_data *dp = &uc->uc_rx[i];
273
274		if (dp->m != NULL) {
275			m_freem(dp->m);
276			dp->m = NULL;
277		}
278	}
279	uc->uc_rx_stat_len = 0;
280	uc->uc_rx_off = 0;
281}
282
283static void
284rtwn_usb_start_xfers(struct rtwn_softc *sc)
285{
286	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
287
288	usbd_transfer_start(uc->uc_xfer[RTWN_BULK_RX]);
289}
290
291static void
292rtwn_usb_abort_xfers(struct rtwn_softc *sc)
293{
294	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
295	int i;
296
297	RTWN_ASSERT_LOCKED(sc);
298
299	/* abort any pending transfers */
300	RTWN_UNLOCK(sc);
301	for (i = 0; i < RTWN_N_TRANSFER; i++)
302		usbd_transfer_drain(uc->uc_xfer[i]);
303	RTWN_LOCK(sc);
304}
305
306static int
307rtwn_usb_fw_write_block(struct rtwn_softc *sc, const uint8_t *buf,
308    uint16_t reg, int mlen)
309{
310	int error;
311
312	/* XXX fix this deconst */
313	error = rtwn_usb_write_region_1(sc, reg, __DECONST(uint8_t *, buf),
314	    mlen);
315
316	return (error);
317}
318
319static void
320rtwn_usb_drop_incorrect_tx(struct rtwn_softc *sc)
321{
322
323	rtwn_setbits_1_shift(sc, R92C_TXDMA_OFFSET_CHK, 0,
324	    R92C_TXDMA_OFFSET_DROP_DATA_EN, 1);
325}
326
327static void
328rtwn_usb_attach_methods(struct rtwn_softc *sc)
329{
330	sc->sc_write_1		= rtwn_usb_write_1;
331	sc->sc_write_2		= rtwn_usb_write_2;
332	sc->sc_write_4		= rtwn_usb_write_4;
333	sc->sc_read_1		= rtwn_usb_read_1;
334	sc->sc_read_2		= rtwn_usb_read_2;
335	sc->sc_read_4		= rtwn_usb_read_4;
336	sc->sc_delay		= rtwn_usb_delay;
337	sc->sc_tx_start		= rtwn_usb_tx_start;
338	sc->sc_start_xfers	= rtwn_usb_start_xfers;
339	sc->sc_reset_lists	= rtwn_usb_reset_lists;
340	sc->sc_abort_xfers	= rtwn_usb_abort_xfers;
341	sc->sc_fw_write_block	= rtwn_usb_fw_write_block;
342	sc->sc_get_qmap		= rtwn_usb_get_qmap;
343	sc->sc_set_desc_addr	= rtwn_nop_softc;
344	sc->sc_drop_incorrect_tx = rtwn_usb_drop_incorrect_tx;
345	sc->sc_beacon_update_begin = rtwn_nop_softc_vap;
346	sc->sc_beacon_update_end = rtwn_nop_softc_vap;
347	sc->sc_beacon_unload	= rtwn_nop_softc_int;
348
349	sc->bcn_check_interval	= 100;
350}
351
352static void
353rtwn_usb_sysctlattach(struct rtwn_softc *sc)
354{
355	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
356	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
357	struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
358	char str[64];
359	int ret;
360
361	ret = snprintf(str, sizeof(str),
362	    "Rx buffer size, 512-byte units [%d...%d]",
363	    RTWN_USB_RXBUFSZ_MIN, RTWN_USB_RXBUFSZ_MAX);
364	KASSERT(ret > 0, ("ret (%d) <= 0!\n", ret));
365	(void) ret;
366
367	uc->uc_rx_buf_size = RTWN_USB_RXBUFSZ_DEF;
368	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
369	    "rx_buf_size", CTLFLAG_RDTUN, &uc->uc_rx_buf_size,
370	    uc->uc_rx_buf_size, str);
371	if (uc->uc_rx_buf_size < RTWN_USB_RXBUFSZ_MIN)
372		uc->uc_rx_buf_size = RTWN_USB_RXBUFSZ_MIN;
373	if (uc->uc_rx_buf_size > RTWN_USB_RXBUFSZ_MAX)
374		uc->uc_rx_buf_size = RTWN_USB_RXBUFSZ_MAX;
375}
376
377static int
378rtwn_usb_attach(device_t self)
379{
380	struct usb_attach_arg *uaa = device_get_ivars(self);
381	struct rtwn_usb_softc *uc = device_get_softc(self);
382	struct rtwn_softc *sc = &uc->uc_sc;
383	struct ieee80211com *ic = &sc->sc_ic;
384	int error;
385
386	device_set_usb_desc(self);
387	uc->uc_udev = uaa->device;
388	sc->sc_dev = self;
389	ic->ic_name = device_get_nameunit(self);
390
391	/* Need to be initialized early. */
392	rtwn_sysctlattach(sc);
393	rtwn_usb_sysctlattach(sc);
394	mtx_init(&sc->sc_mtx, ic->ic_name, MTX_NETWORK_LOCK, MTX_DEF);
395
396	rtwn_usb_attach_methods(sc);
397	rtwn_usb_attach_private(uc, USB_GET_DRIVER_INFO(uaa));
398
399	error = rtwn_usb_setup_endpoints(uc);
400	if (error != 0)
401		goto detach;
402
403	/* Allocate Tx/Rx buffers. */
404	error = rtwn_usb_alloc_rx_list(sc);
405	if (error != 0)
406		goto detach;
407
408	error = rtwn_usb_alloc_tx_list(sc);
409	if (error != 0)
410		goto detach;
411
412	/* Generic attach. */
413	error = rtwn_attach(sc);
414	if (error != 0)
415		goto detach;
416
417	return (0);
418
419detach:
420	rtwn_usb_detach(self);		/* failure */
421	return (ENXIO);
422}
423
424static int
425rtwn_usb_detach(device_t self)
426{
427	struct rtwn_usb_softc *uc = device_get_softc(self);
428	struct rtwn_softc *sc = &uc->uc_sc;
429
430	/* Generic detach. */
431	rtwn_detach(sc);
432
433	/* Free Tx/Rx buffers. */
434	rtwn_usb_free_tx_list(sc);
435	rtwn_usb_free_rx_list(sc);
436
437	/* Detach all USB transfers. */
438	usbd_transfer_unsetup(uc->uc_xfer, RTWN_N_TRANSFER);
439
440	rtwn_detach_private(sc);
441	mtx_destroy(&sc->sc_mtx);
442
443	return (0);
444}
445
446static int
447rtwn_usb_suspend(device_t self)
448{
449	struct rtwn_usb_softc *uc = device_get_softc(self);
450
451	rtwn_suspend(&uc->uc_sc);
452
453	return (0);
454}
455
456static int
457rtwn_usb_resume(device_t self)
458{
459	struct rtwn_usb_softc *uc = device_get_softc(self);
460
461	rtwn_resume(&uc->uc_sc);
462
463	return (0);
464}
465
466static device_method_t rtwn_usb_methods[] = {
467	/* Device interface */
468	DEVMETHOD(device_probe,		rtwn_usb_match),
469	DEVMETHOD(device_attach,	rtwn_usb_attach),
470	DEVMETHOD(device_detach,	rtwn_usb_detach),
471	DEVMETHOD(device_suspend,	rtwn_usb_suspend),
472	DEVMETHOD(device_resume,	rtwn_usb_resume),
473
474	DEVMETHOD_END
475};
476
477static driver_t rtwn_usb_driver = {
478	"rtwn",
479	rtwn_usb_methods,
480	sizeof(struct rtwn_usb_softc)
481};
482
483static devclass_t rtwn_usb_devclass;
484
485DRIVER_MODULE(rtwn_usb, uhub, rtwn_usb_driver, rtwn_usb_devclass, NULL, NULL);
486MODULE_VERSION(rtwn_usb, 1);
487MODULE_DEPEND(rtwn_usb, usb, 1, 1, 1);
488MODULE_DEPEND(rtwn_usb, wlan, 1, 1, 1);
489MODULE_DEPEND(rtwn_usb, rtwn, 2, 2, 2);
490USB_PNP_HOST_INFO(rtwn_devs);
491