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