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