1*54d48227Smglocker /* $OpenBSD: dwc2.c,v 1.68 2022/09/18 21:12:19 mglocker Exp $ */
225e434cbSuebayasi /* $NetBSD: dwc2.c,v 1.32 2014/09/02 23:26:20 macallan Exp $ */
3578f812dSuebayasi
4578f812dSuebayasi /*-
5578f812dSuebayasi * Copyright (c) 2013 The NetBSD Foundation, Inc.
6578f812dSuebayasi * All rights reserved.
7578f812dSuebayasi *
8578f812dSuebayasi * This code is derived from software contributed to The NetBSD Foundation
9578f812dSuebayasi * by Nick Hudson
10578f812dSuebayasi *
11578f812dSuebayasi * Redistribution and use in source and binary forms, with or without
12578f812dSuebayasi * modification, are permitted provided that the following conditions
13578f812dSuebayasi * are met:
14578f812dSuebayasi * 1. Redistributions of source code must retain the above copyright
15578f812dSuebayasi * notice, this list of conditions and the following disclaimer.
16578f812dSuebayasi * 2. Redistributions in binary form must reproduce the above copyright
17578f812dSuebayasi * notice, this list of conditions and the following disclaimer in the
18578f812dSuebayasi * documentation and/or other materials provided with the distribution.
19578f812dSuebayasi *
20578f812dSuebayasi * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21578f812dSuebayasi * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22578f812dSuebayasi * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23578f812dSuebayasi * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24578f812dSuebayasi * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25578f812dSuebayasi * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26578f812dSuebayasi * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27578f812dSuebayasi * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28578f812dSuebayasi * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29578f812dSuebayasi * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30578f812dSuebayasi * POSSIBILITY OF SUCH DAMAGE.
31578f812dSuebayasi */
32578f812dSuebayasi
33578f812dSuebayasi #include <sys/param.h>
34578f812dSuebayasi #include <sys/systm.h>
35309465dbSuebayasi #include <sys/malloc.h>
36578f812dSuebayasi #include <sys/kernel.h>
37578f812dSuebayasi #include <sys/device.h>
38578f812dSuebayasi #include <sys/proc.h>
39578f812dSuebayasi #include <sys/queue.h>
405442788eSguenther #include <sys/endian.h>
41578f812dSuebayasi
42309465dbSuebayasi #include <machine/bus.h>
43578f812dSuebayasi
44578f812dSuebayasi #include <dev/usb/usb.h>
45578f812dSuebayasi #include <dev/usb/usbdi.h>
46578f812dSuebayasi #include <dev/usb/usbdivar.h>
47578f812dSuebayasi #include <dev/usb/usb_mem.h>
48578f812dSuebayasi
49309465dbSuebayasi #include <dev/usb/dwc2/dwc2.h>
50309465dbSuebayasi #include <dev/usb/dwc2/dwc2var.h>
51578f812dSuebayasi
52309465dbSuebayasi #include <dev/usb/dwc2/dwc2_core.h>
53309465dbSuebayasi #include <dev/usb/dwc2/dwc2_hcd.h>
54578f812dSuebayasi
55578f812dSuebayasi #ifdef DWC2_COUNTERS
56578f812dSuebayasi #define DWC2_EVCNT_ADD(a,b) ((void)((a).ev_count += (b)))
57578f812dSuebayasi #else
58578f812dSuebayasi #define DWC2_EVCNT_ADD(a,b) do { } while (/*CONSTCOND*/0)
59578f812dSuebayasi #endif
60578f812dSuebayasi #define DWC2_EVCNT_INCR(a) DWC2_EVCNT_ADD((a), 1)
61578f812dSuebayasi
62578f812dSuebayasi #ifdef DWC2_DEBUG
63578f812dSuebayasi #define DPRINTFN(n,fmt,...) do { \
64578f812dSuebayasi if (dwc2debug >= (n)) { \
65578f812dSuebayasi printf("%s: " fmt, \
66578f812dSuebayasi __FUNCTION__,## __VA_ARGS__); \
67578f812dSuebayasi } \
68578f812dSuebayasi } while (0)
69578f812dSuebayasi #define DPRINTF(...) DPRINTFN(1, __VA_ARGS__)
70578f812dSuebayasi int dwc2debug = 0;
71578f812dSuebayasi #else
72578f812dSuebayasi #define DPRINTF(...) do { } while (0)
73578f812dSuebayasi #define DPRINTFN(...) do { } while (0)
74578f812dSuebayasi #endif
75578f812dSuebayasi
7605c50565Suebayasi STATIC usbd_status dwc2_open(struct usbd_pipe *);
77774acca3Smpi STATIC int dwc2_setaddr(struct usbd_device *, int);
7805c50565Suebayasi STATIC void dwc2_poll(struct usbd_bus *);
7905c50565Suebayasi STATIC void dwc2_softintr(void *);
80578f812dSuebayasi
8105c50565Suebayasi STATIC struct usbd_xfer *dwc2_allocx(struct usbd_bus *);
8205c50565Suebayasi STATIC void dwc2_freex(struct usbd_bus *, struct usbd_xfer *);
83578f812dSuebayasi
8405c50565Suebayasi STATIC usbd_status dwc2_root_ctrl_transfer(struct usbd_xfer *);
8505c50565Suebayasi STATIC usbd_status dwc2_root_ctrl_start(struct usbd_xfer *);
8605c50565Suebayasi STATIC void dwc2_root_ctrl_abort(struct usbd_xfer *);
8705c50565Suebayasi STATIC void dwc2_root_ctrl_close(struct usbd_pipe *);
8805c50565Suebayasi STATIC void dwc2_root_ctrl_done(struct usbd_xfer *);
89578f812dSuebayasi
9005c50565Suebayasi STATIC usbd_status dwc2_root_intr_transfer(struct usbd_xfer *);
9105c50565Suebayasi STATIC usbd_status dwc2_root_intr_start(struct usbd_xfer *);
9205c50565Suebayasi STATIC void dwc2_root_intr_abort(struct usbd_xfer *);
9305c50565Suebayasi STATIC void dwc2_root_intr_close(struct usbd_pipe *);
9405c50565Suebayasi STATIC void dwc2_root_intr_done(struct usbd_xfer *);
95578f812dSuebayasi
9605c50565Suebayasi STATIC usbd_status dwc2_device_ctrl_transfer(struct usbd_xfer *);
9705c50565Suebayasi STATIC usbd_status dwc2_device_ctrl_start(struct usbd_xfer *);
9805c50565Suebayasi STATIC void dwc2_device_ctrl_abort(struct usbd_xfer *);
9905c50565Suebayasi STATIC void dwc2_device_ctrl_close(struct usbd_pipe *);
10005c50565Suebayasi STATIC void dwc2_device_ctrl_done(struct usbd_xfer *);
101578f812dSuebayasi
10205c50565Suebayasi STATIC usbd_status dwc2_device_bulk_transfer(struct usbd_xfer *);
10305c50565Suebayasi STATIC usbd_status dwc2_device_bulk_start(struct usbd_xfer *);
10405c50565Suebayasi STATIC void dwc2_device_bulk_abort(struct usbd_xfer *);
10505c50565Suebayasi STATIC void dwc2_device_bulk_close(struct usbd_pipe *);
10605c50565Suebayasi STATIC void dwc2_device_bulk_done(struct usbd_xfer *);
107578f812dSuebayasi
10805c50565Suebayasi STATIC usbd_status dwc2_device_intr_transfer(struct usbd_xfer *);
10905c50565Suebayasi STATIC usbd_status dwc2_device_intr_start(struct usbd_xfer *);
11005c50565Suebayasi STATIC void dwc2_device_intr_abort(struct usbd_xfer *);
11105c50565Suebayasi STATIC void dwc2_device_intr_close(struct usbd_pipe *);
11205c50565Suebayasi STATIC void dwc2_device_intr_done(struct usbd_xfer *);
113578f812dSuebayasi
11405c50565Suebayasi STATIC usbd_status dwc2_device_isoc_transfer(struct usbd_xfer *);
11505c50565Suebayasi STATIC usbd_status dwc2_device_isoc_start(struct usbd_xfer *);
11605c50565Suebayasi STATIC void dwc2_device_isoc_abort(struct usbd_xfer *);
11705c50565Suebayasi STATIC void dwc2_device_isoc_close(struct usbd_pipe *);
11805c50565Suebayasi STATIC void dwc2_device_isoc_done(struct usbd_xfer *);
119578f812dSuebayasi
12005c50565Suebayasi STATIC usbd_status dwc2_device_start(struct usbd_xfer *);
121578f812dSuebayasi
12205c50565Suebayasi STATIC void dwc2_close_pipe(struct usbd_pipe *);
12305c50565Suebayasi STATIC void dwc2_abort_xfer(struct usbd_xfer *, usbd_status);
124578f812dSuebayasi
12505c50565Suebayasi STATIC void dwc2_device_clear_toggle(struct usbd_pipe *);
12605c50565Suebayasi STATIC void dwc2_noop(struct usbd_pipe *pipe);
127578f812dSuebayasi
12805c50565Suebayasi STATIC int dwc2_interrupt(struct dwc2_softc *);
12905c50565Suebayasi STATIC void dwc2_rhc(void *);
130578f812dSuebayasi
13105c50565Suebayasi STATIC void dwc2_timeout(void *);
13205c50565Suebayasi STATIC void dwc2_timeout_task(void *);
133578f812dSuebayasi
134a9beb1edSmglocker int dwc2_check_core_version(struct dwc2_hsotg *);
135578f812dSuebayasi
136578f812dSuebayasi #define DWC2_INTR_ENDPT 1
137578f812dSuebayasi
1388f1d17e8Snaddy STATIC const struct usbd_bus_methods dwc2_bus_methods = {
139578f812dSuebayasi .open_pipe = dwc2_open,
140774acca3Smpi .dev_setaddr = dwc2_setaddr,
141578f812dSuebayasi .soft_intr = dwc2_softintr,
142578f812dSuebayasi .do_poll = dwc2_poll,
143578f812dSuebayasi .allocx = dwc2_allocx,
144578f812dSuebayasi .freex = dwc2_freex,
145578f812dSuebayasi };
146578f812dSuebayasi
1478f1d17e8Snaddy STATIC const struct usbd_pipe_methods dwc2_root_ctrl_methods = {
148578f812dSuebayasi .transfer = dwc2_root_ctrl_transfer,
149578f812dSuebayasi .start = dwc2_root_ctrl_start,
150578f812dSuebayasi .abort = dwc2_root_ctrl_abort,
151578f812dSuebayasi .close = dwc2_root_ctrl_close,
152578f812dSuebayasi .cleartoggle = dwc2_noop,
153578f812dSuebayasi .done = dwc2_root_ctrl_done,
154578f812dSuebayasi };
155578f812dSuebayasi
1568f1d17e8Snaddy STATIC const struct usbd_pipe_methods dwc2_root_intr_methods = {
157578f812dSuebayasi .transfer = dwc2_root_intr_transfer,
158578f812dSuebayasi .start = dwc2_root_intr_start,
159578f812dSuebayasi .abort = dwc2_root_intr_abort,
160578f812dSuebayasi .close = dwc2_root_intr_close,
161578f812dSuebayasi .cleartoggle = dwc2_noop,
162578f812dSuebayasi .done = dwc2_root_intr_done,
163578f812dSuebayasi };
164578f812dSuebayasi
1658f1d17e8Snaddy STATIC const struct usbd_pipe_methods dwc2_device_ctrl_methods = {
166578f812dSuebayasi .transfer = dwc2_device_ctrl_transfer,
167578f812dSuebayasi .start = dwc2_device_ctrl_start,
168578f812dSuebayasi .abort = dwc2_device_ctrl_abort,
169578f812dSuebayasi .close = dwc2_device_ctrl_close,
170578f812dSuebayasi .cleartoggle = dwc2_noop,
171578f812dSuebayasi .done = dwc2_device_ctrl_done,
172578f812dSuebayasi };
173578f812dSuebayasi
1748f1d17e8Snaddy STATIC const struct usbd_pipe_methods dwc2_device_intr_methods = {
175578f812dSuebayasi .transfer = dwc2_device_intr_transfer,
176578f812dSuebayasi .start = dwc2_device_intr_start,
177578f812dSuebayasi .abort = dwc2_device_intr_abort,
178578f812dSuebayasi .close = dwc2_device_intr_close,
179578f812dSuebayasi .cleartoggle = dwc2_device_clear_toggle,
180578f812dSuebayasi .done = dwc2_device_intr_done,
181578f812dSuebayasi };
182578f812dSuebayasi
1838f1d17e8Snaddy STATIC const struct usbd_pipe_methods dwc2_device_bulk_methods = {
184578f812dSuebayasi .transfer = dwc2_device_bulk_transfer,
185578f812dSuebayasi .start = dwc2_device_bulk_start,
186578f812dSuebayasi .abort = dwc2_device_bulk_abort,
187578f812dSuebayasi .close = dwc2_device_bulk_close,
188578f812dSuebayasi .cleartoggle = dwc2_device_clear_toggle,
189578f812dSuebayasi .done = dwc2_device_bulk_done,
190578f812dSuebayasi };
191578f812dSuebayasi
1928f1d17e8Snaddy STATIC const struct usbd_pipe_methods dwc2_device_isoc_methods = {
193578f812dSuebayasi .transfer = dwc2_device_isoc_transfer,
194578f812dSuebayasi .start = dwc2_device_isoc_start,
195578f812dSuebayasi .abort = dwc2_device_isoc_abort,
196578f812dSuebayasi .close = dwc2_device_isoc_close,
197578f812dSuebayasi .cleartoggle = dwc2_noop,
198578f812dSuebayasi .done = dwc2_device_isoc_done,
199578f812dSuebayasi };
200578f812dSuebayasi
201774acca3Smpi /*
202774acca3Smpi * Work around the half configured control (default) pipe when setting
203774acca3Smpi * the address of a device.
204774acca3Smpi */
205774acca3Smpi STATIC int
dwc2_setaddr(struct usbd_device * dev,int addr)206774acca3Smpi dwc2_setaddr(struct usbd_device *dev, int addr)
207774acca3Smpi {
208774acca3Smpi if (usbd_set_address(dev, addr))
209774acca3Smpi return (1);
210774acca3Smpi
211774acca3Smpi dev->address = addr;
212774acca3Smpi
213774acca3Smpi /*
214774acca3Smpi * Re-establish the default pipe with the new address and the
215774acca3Smpi * new max packet size.
216774acca3Smpi */
217774acca3Smpi dwc2_close_pipe(dev->default_pipe);
218774acca3Smpi if (dwc2_open(dev->default_pipe))
219774acca3Smpi return (EINVAL);
220774acca3Smpi
221774acca3Smpi return (0);
222774acca3Smpi }
223774acca3Smpi
22440442a57Suebayasi struct usbd_xfer *
dwc2_allocx(struct usbd_bus * bus)225578f812dSuebayasi dwc2_allocx(struct usbd_bus *bus)
226578f812dSuebayasi {
227578f812dSuebayasi struct dwc2_softc *sc = DWC2_BUS2SC(bus);
228578f812dSuebayasi struct dwc2_xfer *dxfer;
229578f812dSuebayasi
230578f812dSuebayasi DPRINTFN(10, "\n");
231578f812dSuebayasi
232578f812dSuebayasi DWC2_EVCNT_INCR(sc->sc_ev_xferpoolget);
233a0d2b8daSmglocker dxfer = pool_get(&sc->sc_xferpool, PR_NOWAIT | PR_ZERO);
234578f812dSuebayasi if (dxfer != NULL) {
235d05ae140Smglocker #ifdef DIAGNOSTIC
2363128e5d1Suebayasi dxfer->xfer.busy_free = XFER_ONQU;
237578f812dSuebayasi #endif
238578f812dSuebayasi }
23940442a57Suebayasi return (struct usbd_xfer *)dxfer;
240578f812dSuebayasi }
241578f812dSuebayasi
242578f812dSuebayasi void
dwc2_freex(struct usbd_bus * bus,struct usbd_xfer * xfer)24340442a57Suebayasi dwc2_freex(struct usbd_bus *bus, struct usbd_xfer *xfer)
244578f812dSuebayasi {
245578f812dSuebayasi struct dwc2_softc *sc = DWC2_BUS2SC(bus);
246578f812dSuebayasi
247578f812dSuebayasi DPRINTFN(10, "\n");
248578f812dSuebayasi
249d05ae140Smglocker #ifdef DIAGNOSTIC
250d05ae140Smglocker if (xfer->busy_free != XFER_ONQU &&
251d05ae140Smglocker xfer->status != USBD_NOT_STARTED) {
252578f812dSuebayasi DPRINTF("xfer=%p not busy, 0x%08x\n", xfer, xfer->busy_free);
253578f812dSuebayasi }
254578f812dSuebayasi xfer->busy_free = XFER_FREE;
255578f812dSuebayasi #endif
256578f812dSuebayasi DWC2_EVCNT_INCR(sc->sc_ev_xferpoolput);
2575e13bfdfSuebayasi pool_put(&sc->sc_xferpool, xfer);
258578f812dSuebayasi }
259578f812dSuebayasi
26005c50565Suebayasi STATIC void
dwc2_rhc(void * addr)261578f812dSuebayasi dwc2_rhc(void *addr)
262578f812dSuebayasi {
263578f812dSuebayasi struct dwc2_softc *sc = addr;
26440442a57Suebayasi struct usbd_xfer *xfer;
265578f812dSuebayasi u_char *p;
266578f812dSuebayasi
267578f812dSuebayasi DPRINTF("\n");
268d05ae140Smglocker mtx_enter(&sc->sc_lock);
269578f812dSuebayasi xfer = sc->sc_intrxfer;
270578f812dSuebayasi
271578f812dSuebayasi if (xfer == NULL) {
272578f812dSuebayasi /* Just ignore the change. */
273d05ae140Smglocker mtx_leave(&sc->sc_lock);
274578f812dSuebayasi return;
275578f812dSuebayasi
276578f812dSuebayasi }
277d05ae140Smglocker
278578f812dSuebayasi /* set port bit */
279578f812dSuebayasi p = KERNADDR(&xfer->dmabuf, 0);
280578f812dSuebayasi
281578f812dSuebayasi p[0] = 0x02; /* we only have one port (1 << 1) */
282578f812dSuebayasi
283578f812dSuebayasi xfer->actlen = xfer->length;
284578f812dSuebayasi xfer->status = USBD_NORMAL_COMPLETION;
285578f812dSuebayasi
286578f812dSuebayasi usb_transfer_complete(xfer);
287d05ae140Smglocker mtx_leave(&sc->sc_lock);
288578f812dSuebayasi }
289578f812dSuebayasi
29005c50565Suebayasi STATIC void
dwc2_softintr(void * v)291578f812dSuebayasi dwc2_softintr(void *v)
292578f812dSuebayasi {
293578f812dSuebayasi struct usbd_bus *bus = v;
294578f812dSuebayasi struct dwc2_softc *sc = DWC2_BUS2SC(bus);
295578f812dSuebayasi struct dwc2_hsotg *hsotg = sc->sc_hsotg;
296d05ae140Smglocker struct dwc2_xfer *dxfer, *next;
297d05ae140Smglocker TAILQ_HEAD(, dwc2_xfer) claimed = TAILQ_HEAD_INITIALIZER(claimed);
298578f812dSuebayasi
299578f812dSuebayasi /*
300d05ae140Smglocker * Grab all the xfers that have not been aborted or timed out.
301d05ae140Smglocker * Do so under a single lock -- without dropping it to run
302d05ae140Smglocker * usb_transfer_complete as we go -- so that dwc2_abortx won't
303d05ae140Smglocker * remove next out from under us during iteration when we've
304d05ae140Smglocker * dropped the lock.
305578f812dSuebayasi */
306b6c5b329Suebayasi mtx_enter(&hsotg->lock);
307d05ae140Smglocker TAILQ_FOREACH_SAFE(dxfer, &sc->sc_complete, xnext, next) {
308d05ae140Smglocker KASSERT(dxfer->xfer.status == USBD_IN_PROGRESS);
309d05ae140Smglocker KASSERT(dxfer->intr_status != USBD_CANCELLED);
310d05ae140Smglocker KASSERT(dxfer->intr_status != USBD_TIMEOUT);
311d05ae140Smglocker TAILQ_REMOVE(&sc->sc_complete, dxfer, xnext);
312d05ae140Smglocker TAILQ_INSERT_TAIL(&claimed, dxfer, xnext);
313578f812dSuebayasi }
314b6c5b329Suebayasi mtx_leave(&hsotg->lock);
315d05ae140Smglocker
316d05ae140Smglocker /* Now complete them. */
317d05ae140Smglocker while (!TAILQ_EMPTY(&claimed)) {
318d05ae140Smglocker dxfer = TAILQ_FIRST(&claimed);
319d05ae140Smglocker KASSERT(dxfer->xfer.status == USBD_IN_PROGRESS);
320d05ae140Smglocker KASSERT(dxfer->intr_status != USBD_CANCELLED);
321d05ae140Smglocker KASSERT(dxfer->intr_status != USBD_TIMEOUT);
322d05ae140Smglocker TAILQ_REMOVE(&claimed, dxfer, xnext);
323d05ae140Smglocker
324d05ae140Smglocker dxfer->xfer.status = dxfer->intr_status;
325d05ae140Smglocker usb_transfer_complete(&dxfer->xfer);
326d05ae140Smglocker }
327578f812dSuebayasi }
328578f812dSuebayasi
32905c50565Suebayasi STATIC void
dwc2_timeout(void * addr)330578f812dSuebayasi dwc2_timeout(void *addr)
331578f812dSuebayasi {
33240442a57Suebayasi struct usbd_xfer *xfer = addr;
333578f812dSuebayasi struct dwc2_softc *sc = DWC2_XFER2SC(xfer);
334578f812dSuebayasi
335d05ae140Smglocker if (sc->sc_bus.dying) {
33670e5eba0Svisa dwc2_timeout_task(addr);
337578f812dSuebayasi return;
338578f812dSuebayasi }
339578f812dSuebayasi
340578f812dSuebayasi /* Execute the abort in a process context. */
341921c091fSvisa usb_init_task(&xfer->abort_task, dwc2_timeout_task, addr,
342ba7e0caeSmpi USB_TASK_TYPE_ABORT);
343921c091fSvisa usb_add_task(xfer->device, &xfer->abort_task);
344578f812dSuebayasi }
345578f812dSuebayasi
34605c50565Suebayasi STATIC void
dwc2_timeout_task(void * addr)347578f812dSuebayasi dwc2_timeout_task(void *addr)
348578f812dSuebayasi {
34940442a57Suebayasi struct usbd_xfer *xfer = addr;
35070e5eba0Svisa int s;
351578f812dSuebayasi
35270e5eba0Svisa s = splusb();
353578f812dSuebayasi dwc2_abort_xfer(xfer, USBD_TIMEOUT);
35470e5eba0Svisa splx(s);
355578f812dSuebayasi }
356578f812dSuebayasi
357578f812dSuebayasi usbd_status
dwc2_open(struct usbd_pipe * pipe)35840442a57Suebayasi dwc2_open(struct usbd_pipe *pipe)
359578f812dSuebayasi {
36040442a57Suebayasi struct usbd_device *dev = pipe->device;
361578f812dSuebayasi struct dwc2_softc *sc = DWC2_PIPE2SC(pipe);
362578f812dSuebayasi struct dwc2_pipe *dpipe = DWC2_PIPE2DPIPE(pipe);
363578f812dSuebayasi usb_endpoint_descriptor_t *ed = pipe->endpoint->edesc;
364578f812dSuebayasi uint8_t addr = dev->address;
365578f812dSuebayasi uint8_t xfertype = UE_GET_XFERTYPE(ed->bmAttributes);
366578f812dSuebayasi usbd_status err;
367578f812dSuebayasi
368578f812dSuebayasi DPRINTF("pipe %p addr %d xfertype %d dir %s\n", pipe, addr, xfertype,
369578f812dSuebayasi UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN ? "in" : "out");
370578f812dSuebayasi
371d05ae140Smglocker if (sc->sc_bus.dying) {
372578f812dSuebayasi return USBD_IOERROR;
373578f812dSuebayasi }
374578f812dSuebayasi
375578f812dSuebayasi if (addr == sc->sc_addr) {
376578f812dSuebayasi switch (ed->bEndpointAddress) {
377578f812dSuebayasi case USB_CONTROL_ENDPOINT:
378578f812dSuebayasi pipe->methods = &dwc2_root_ctrl_methods;
379578f812dSuebayasi break;
380578f812dSuebayasi case UE_DIR_IN | DWC2_INTR_ENDPT:
381578f812dSuebayasi pipe->methods = &dwc2_root_intr_methods;
382578f812dSuebayasi break;
383578f812dSuebayasi default:
384578f812dSuebayasi DPRINTF("bad bEndpointAddress 0x%02x\n",
385578f812dSuebayasi ed->bEndpointAddress);
386578f812dSuebayasi return USBD_INVAL;
387578f812dSuebayasi }
388578f812dSuebayasi DPRINTF("root hub pipe open\n");
389578f812dSuebayasi return USBD_NORMAL_COMPLETION;
390578f812dSuebayasi }
391578f812dSuebayasi
392578f812dSuebayasi switch (xfertype) {
393578f812dSuebayasi case UE_CONTROL:
394578f812dSuebayasi pipe->methods = &dwc2_device_ctrl_methods;
395578f812dSuebayasi err = usb_allocmem(&sc->sc_bus, sizeof(usb_device_request_t),
396c28d999fSpatrick 0, USB_DMA_COHERENT, &dpipe->req_dma);
397578f812dSuebayasi if (err)
398d05ae140Smglocker return USBD_NOMEM;
399578f812dSuebayasi break;
400578f812dSuebayasi case UE_INTERRUPT:
401578f812dSuebayasi pipe->methods = &dwc2_device_intr_methods;
402578f812dSuebayasi break;
403578f812dSuebayasi case UE_ISOCHRONOUS:
404578f812dSuebayasi pipe->methods = &dwc2_device_isoc_methods;
405578f812dSuebayasi break;
406578f812dSuebayasi case UE_BULK:
407578f812dSuebayasi pipe->methods = &dwc2_device_bulk_methods;
408578f812dSuebayasi break;
409578f812dSuebayasi default:
410578f812dSuebayasi DPRINTF("bad xfer type %d\n", xfertype);
411578f812dSuebayasi return USBD_INVAL;
412578f812dSuebayasi }
413578f812dSuebayasi
414d05ae140Smglocker /* QH */
415d05ae140Smglocker dpipe->priv = NULL;
416578f812dSuebayasi
417578f812dSuebayasi return USBD_NORMAL_COMPLETION;
418578f812dSuebayasi }
419578f812dSuebayasi
42005c50565Suebayasi STATIC void
dwc2_poll(struct usbd_bus * bus)421578f812dSuebayasi dwc2_poll(struct usbd_bus *bus)
422578f812dSuebayasi {
423578f812dSuebayasi struct dwc2_softc *sc = DWC2_BUS2SC(bus);
424578f812dSuebayasi
425578f812dSuebayasi dwc2_interrupt(sc);
426578f812dSuebayasi }
427578f812dSuebayasi
428578f812dSuebayasi /*
429578f812dSuebayasi * Close a reqular pipe.
430578f812dSuebayasi * Assumes that there are no pending transactions.
431578f812dSuebayasi */
43205c50565Suebayasi STATIC void
dwc2_close_pipe(struct usbd_pipe * pipe)43340442a57Suebayasi dwc2_close_pipe(struct usbd_pipe *pipe)
434578f812dSuebayasi {
43570e5eba0Svisa /* nothing */
436578f812dSuebayasi }
437578f812dSuebayasi
438578f812dSuebayasi /*
439578f812dSuebayasi * Abort a device request.
440578f812dSuebayasi */
44105c50565Suebayasi STATIC void
dwc2_abort_xfer(struct usbd_xfer * xfer,usbd_status status)44240442a57Suebayasi dwc2_abort_xfer(struct usbd_xfer *xfer, usbd_status status)
443578f812dSuebayasi {
444578f812dSuebayasi struct dwc2_xfer *dxfer = DWC2_XFER2DXFER(xfer);
445578f812dSuebayasi struct dwc2_softc *sc = DWC2_XFER2SC(xfer);
446578f812dSuebayasi struct dwc2_hsotg *hsotg = sc->sc_hsotg;
44726b0707eSmglocker struct dwc2_xfer *d;
448578f812dSuebayasi int err;
449578f812dSuebayasi
4504ac8836eSmpi splsoftassert(IPL_SOFTUSB);
451578f812dSuebayasi
452bc00d75aSmglocker DPRINTF("xfer %p pipe %p status 0x%08x\n", xfer, xfer->pipe,
45326b0707eSmglocker xfer->status);
454578f812dSuebayasi
45526b0707eSmglocker /* XXX The stack should not call abort() in this case. */
45626b0707eSmglocker if (sc->sc_bus.dying || xfer->status == USBD_NOT_STARTED) {
457578f812dSuebayasi xfer->status = status;
458016aa740Suebayasi timeout_del(&xfer->timeout_handle);
459c733efffSvisa usb_rem_task(xfer->device, &xfer->abort_task);
460578f812dSuebayasi usb_transfer_complete(xfer);
461578f812dSuebayasi return;
462578f812dSuebayasi }
463578f812dSuebayasi
46426b0707eSmglocker KASSERT(xfer->status != USBD_CANCELLED);
46526b0707eSmglocker /* Transfer is already done. */
466c733efffSvisa if (xfer->status != USBD_IN_PROGRESS) {
46726b0707eSmglocker DPRINTF("%s: already done \n", __func__);
468c733efffSvisa return;
469c733efffSvisa }
470c733efffSvisa
47126b0707eSmglocker /* Prevent any timeout to kick in. */
472016aa740Suebayasi timeout_del(&xfer->timeout_handle);
473c733efffSvisa usb_rem_task(xfer->device, &xfer->abort_task);
474578f812dSuebayasi
47526b0707eSmglocker /* Claim the transfer status as cancelled. */
47626b0707eSmglocker xfer->status = USBD_CANCELLED;
47726b0707eSmglocker
47826b0707eSmglocker KASSERTMSG((xfer->status == USBD_CANCELLED ||
47926b0707eSmglocker xfer->status == USBD_TIMEOUT),
48026b0707eSmglocker "bad abort status: %d", xfer->status);
48126b0707eSmglocker
48226b0707eSmglocker mtx_enter(&hsotg->lock);
48326b0707eSmglocker
48426b0707eSmglocker /*
48526b0707eSmglocker * Check whether we aborted or timed out after the hardware
48626b0707eSmglocker * completion interrupt determined that it's done but before
48726b0707eSmglocker * the soft interrupt could actually complete it. If so, it's
48826b0707eSmglocker * too late for the soft interrupt -- at this point we've
48926b0707eSmglocker * already committed to abort it or time it out, so we need to
49026b0707eSmglocker * take it off the softint's list of work in case the caller,
49126b0707eSmglocker * say, frees the xfer before the softint runs.
49226b0707eSmglocker *
49326b0707eSmglocker * This logic is unusual among host controller drivers, and
49426b0707eSmglocker * happens because dwc2 decides to complete xfers in the hard
49526b0707eSmglocker * interrupt handler rather than in the soft interrupt handler,
49626b0707eSmglocker * but usb_transfer_complete must be deferred to softint -- and
49726b0707eSmglocker * we happened to swoop in between the hard interrupt and the
49826b0707eSmglocker * soft interrupt. Other host controller drivers do almost all
49926b0707eSmglocker * processing in the softint so there's no intermediate stage.
50026b0707eSmglocker *
50126b0707eSmglocker * Fortunately, this linear search to discern the intermediate
50226b0707eSmglocker * stage is not likely to be a serious performance impact
50326b0707eSmglocker * because it happens only on abort or timeout.
50426b0707eSmglocker */
50526b0707eSmglocker TAILQ_FOREACH(d, &sc->sc_complete, xnext) {
506578f812dSuebayasi if (d == dxfer) {
507578f812dSuebayasi TAILQ_REMOVE(&sc->sc_complete, dxfer, xnext);
50826b0707eSmglocker break;
509578f812dSuebayasi }
510578f812dSuebayasi }
511578f812dSuebayasi
51226b0707eSmglocker /*
51326b0707eSmglocker * HC Step 1: Handle the hardware.
51426b0707eSmglocker */
515578f812dSuebayasi err = dwc2_hcd_urb_dequeue(hsotg, dxfer->urb);
516578f812dSuebayasi if (err) {
517578f812dSuebayasi DPRINTF("dwc2_hcd_urb_dequeue failed\n");
518578f812dSuebayasi }
519578f812dSuebayasi
520b6c5b329Suebayasi mtx_leave(&hsotg->lock);
521578f812dSuebayasi
522578f812dSuebayasi /*
52326b0707eSmglocker * Final Step: Notify completion to waiting xfers.
524578f812dSuebayasi */
525578f812dSuebayasi usb_transfer_complete(xfer);
526578f812dSuebayasi }
527578f812dSuebayasi
52805c50565Suebayasi STATIC void
dwc2_noop(struct usbd_pipe * pipe)52940442a57Suebayasi dwc2_noop(struct usbd_pipe *pipe)
530578f812dSuebayasi {
531578f812dSuebayasi
532578f812dSuebayasi }
533578f812dSuebayasi
53405c50565Suebayasi STATIC void
dwc2_device_clear_toggle(struct usbd_pipe * pipe)53540442a57Suebayasi dwc2_device_clear_toggle(struct usbd_pipe *pipe)
536578f812dSuebayasi {
537ef05a7e5Suebayasi DPRINTF("toggle %d -> 0", pipe->endpoint->savedtoggle);
538578f812dSuebayasi }
539578f812dSuebayasi
540578f812dSuebayasi /*
541578f812dSuebayasi * Data structures and routines to emulate the root hub.
542578f812dSuebayasi */
543578f812dSuebayasi
54405c50565Suebayasi STATIC const usb_device_descriptor_t dwc2_devd = {
545578f812dSuebayasi .bLength = sizeof(usb_device_descriptor_t),
546578f812dSuebayasi .bDescriptorType = UDESC_DEVICE,
547578f812dSuebayasi .bcdUSB = {0x00, 0x02},
548578f812dSuebayasi .bDeviceClass = UDCLASS_HUB,
549578f812dSuebayasi .bDeviceSubClass = UDSUBCLASS_HUB,
550578f812dSuebayasi .bDeviceProtocol = UDPROTO_HSHUBSTT,
551578f812dSuebayasi .bMaxPacketSize = 64,
552578f812dSuebayasi .bcdDevice = {0x00, 0x01},
553578f812dSuebayasi .iManufacturer = 1,
554578f812dSuebayasi .iProduct = 2,
555578f812dSuebayasi .bNumConfigurations = 1,
556578f812dSuebayasi };
557578f812dSuebayasi
558578f812dSuebayasi struct dwc2_config_desc {
559578f812dSuebayasi usb_config_descriptor_t confd;
560578f812dSuebayasi usb_interface_descriptor_t ifcd;
561578f812dSuebayasi usb_endpoint_descriptor_t endpd;
562578f812dSuebayasi } __packed;
563578f812dSuebayasi
56405c50565Suebayasi STATIC const struct dwc2_config_desc dwc2_confd = {
565578f812dSuebayasi .confd = {
566578f812dSuebayasi .bLength = USB_CONFIG_DESCRIPTOR_SIZE,
567578f812dSuebayasi .bDescriptorType = UDESC_CONFIG,
568578f812dSuebayasi .wTotalLength[0] = sizeof(dwc2_confd),
56911e74109Skurt .bNumInterfaces = 1,
570578f812dSuebayasi .bConfigurationValue = 1,
571578f812dSuebayasi .iConfiguration = 0,
5723a3b7daeSmpi .bmAttributes = UC_BUS_POWERED | UC_SELF_POWERED,
573578f812dSuebayasi .bMaxPower = 0,
574578f812dSuebayasi },
575578f812dSuebayasi .ifcd = {
576578f812dSuebayasi .bLength = USB_INTERFACE_DESCRIPTOR_SIZE,
577578f812dSuebayasi .bDescriptorType = UDESC_INTERFACE,
578578f812dSuebayasi .bInterfaceNumber = 0,
579578f812dSuebayasi .bAlternateSetting = 0,
580578f812dSuebayasi .bNumEndpoints = 1,
581578f812dSuebayasi .bInterfaceClass = UICLASS_HUB,
582578f812dSuebayasi .bInterfaceSubClass = UISUBCLASS_HUB,
583578f812dSuebayasi .bInterfaceProtocol = UIPROTO_HSHUBSTT,
584578f812dSuebayasi .iInterface = 0
585578f812dSuebayasi },
586578f812dSuebayasi .endpd = {
587578f812dSuebayasi .bLength = USB_ENDPOINT_DESCRIPTOR_SIZE,
588578f812dSuebayasi .bDescriptorType = UDESC_ENDPOINT,
589578f812dSuebayasi .bEndpointAddress = UE_DIR_IN | DWC2_INTR_ENDPT,
590578f812dSuebayasi .bmAttributes = UE_INTERRUPT,
591578f812dSuebayasi .wMaxPacketSize = {8, 0}, /* max packet */
592578f812dSuebayasi .bInterval = 255,
593578f812dSuebayasi },
594578f812dSuebayasi };
595578f812dSuebayasi
59605c50565Suebayasi STATIC usbd_status
dwc2_root_ctrl_transfer(struct usbd_xfer * xfer)59740442a57Suebayasi dwc2_root_ctrl_transfer(struct usbd_xfer *xfer)
598578f812dSuebayasi {
599578f812dSuebayasi usbd_status err;
600578f812dSuebayasi
601578f812dSuebayasi err = usb_insert_transfer(xfer);
602578f812dSuebayasi if (err)
603578f812dSuebayasi return err;
604578f812dSuebayasi
605578f812dSuebayasi return dwc2_root_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue));
606578f812dSuebayasi }
607578f812dSuebayasi
60805c50565Suebayasi STATIC usbd_status
dwc2_root_ctrl_start(struct usbd_xfer * xfer)60940442a57Suebayasi dwc2_root_ctrl_start(struct usbd_xfer *xfer)
610578f812dSuebayasi {
611578f812dSuebayasi struct dwc2_softc *sc = DWC2_XFER2SC(xfer);
612578f812dSuebayasi usb_device_request_t *req;
613578f812dSuebayasi uint8_t *buf;
614578f812dSuebayasi uint16_t len;
61570e5eba0Svisa int value, index, l, s, totlen;
616578f812dSuebayasi usbd_status err = USBD_IOERROR;
617578f812dSuebayasi
618deae3185Smglocker KASSERT(xfer->rqflags & URQ_REQUEST);
619deae3185Smglocker
620d05ae140Smglocker if (sc->sc_bus.dying)
621578f812dSuebayasi return USBD_IOERROR;
622578f812dSuebayasi
623578f812dSuebayasi req = &xfer->request;
624578f812dSuebayasi
625578f812dSuebayasi DPRINTFN(4, "type=0x%02x request=%02x\n",
626578f812dSuebayasi req->bmRequestType, req->bRequest);
627578f812dSuebayasi
628578f812dSuebayasi len = UGETW(req->wLength);
629578f812dSuebayasi value = UGETW(req->wValue);
630578f812dSuebayasi index = UGETW(req->wIndex);
631578f812dSuebayasi
632578f812dSuebayasi buf = len ? KERNADDR(&xfer->dmabuf, 0) : NULL;
633578f812dSuebayasi
634578f812dSuebayasi totlen = 0;
635578f812dSuebayasi
636578f812dSuebayasi #define C(x,y) ((x) | ((y) << 8))
637578f812dSuebayasi switch (C(req->bRequest, req->bmRequestType)) {
638578f812dSuebayasi case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE):
639578f812dSuebayasi case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE):
640578f812dSuebayasi case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
641578f812dSuebayasi /*
642578f812dSuebayasi * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops
643578f812dSuebayasi * for the integrated root hub.
644578f812dSuebayasi */
645578f812dSuebayasi break;
646578f812dSuebayasi case C(UR_GET_CONFIG, UT_READ_DEVICE):
647578f812dSuebayasi if (len > 0) {
648578f812dSuebayasi *buf = sc->sc_conf;
649578f812dSuebayasi totlen = 1;
650578f812dSuebayasi }
651578f812dSuebayasi break;
652578f812dSuebayasi case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
653578f812dSuebayasi DPRINTFN(8, "wValue=0x%04x\n", value);
654578f812dSuebayasi
655578f812dSuebayasi if (len == 0)
656578f812dSuebayasi break;
657578f812dSuebayasi switch (value) {
658578f812dSuebayasi case C(0, UDESC_DEVICE):
659578f812dSuebayasi l = min(len, USB_DEVICE_DESCRIPTOR_SIZE);
660578f812dSuebayasi memcpy(buf, &dwc2_devd, l);
661578f812dSuebayasi buf += l;
662578f812dSuebayasi len -= l;
663578f812dSuebayasi totlen += l;
664578f812dSuebayasi
665578f812dSuebayasi break;
666578f812dSuebayasi case C(0, UDESC_CONFIG):
667578f812dSuebayasi l = min(len, sizeof(dwc2_confd));
668578f812dSuebayasi memcpy(buf, &dwc2_confd, l);
669578f812dSuebayasi buf += l;
670578f812dSuebayasi len -= l;
671578f812dSuebayasi totlen += l;
672578f812dSuebayasi
673578f812dSuebayasi break;
674578f812dSuebayasi #define sd ((usb_string_descriptor_t *)buf)
675578f812dSuebayasi case C(0, UDESC_STRING):
6763215ab50Suebayasi totlen = usbd_str(sd, len, "\001");
677578f812dSuebayasi break;
678578f812dSuebayasi case C(1, UDESC_STRING):
6793215ab50Suebayasi totlen = usbd_str(sd, len, sc->sc_vendor);
680578f812dSuebayasi break;
681578f812dSuebayasi case C(2, UDESC_STRING):
6823215ab50Suebayasi totlen = usbd_str(sd, len, "DWC2 root hub");
683578f812dSuebayasi break;
684578f812dSuebayasi #undef sd
685578f812dSuebayasi default:
686578f812dSuebayasi goto fail;
687578f812dSuebayasi }
688578f812dSuebayasi break;
689578f812dSuebayasi case C(UR_GET_INTERFACE, UT_READ_INTERFACE):
690578f812dSuebayasi if (len > 0) {
691578f812dSuebayasi *buf = 0;
692578f812dSuebayasi totlen = 1;
693578f812dSuebayasi }
694578f812dSuebayasi break;
695578f812dSuebayasi case C(UR_GET_STATUS, UT_READ_DEVICE):
696578f812dSuebayasi if (len > 1) {
697578f812dSuebayasi USETW(((usb_status_t *)buf)->wStatus,UDS_SELF_POWERED);
698578f812dSuebayasi totlen = 2;
699578f812dSuebayasi }
700578f812dSuebayasi break;
701578f812dSuebayasi case C(UR_GET_STATUS, UT_READ_INTERFACE):
702578f812dSuebayasi case C(UR_GET_STATUS, UT_READ_ENDPOINT):
703578f812dSuebayasi if (len > 1) {
704578f812dSuebayasi USETW(((usb_status_t *)buf)->wStatus, 0);
705578f812dSuebayasi totlen = 2;
706578f812dSuebayasi }
707578f812dSuebayasi break;
708578f812dSuebayasi case C(UR_SET_ADDRESS, UT_WRITE_DEVICE):
709578f812dSuebayasi DPRINTF("UR_SET_ADDRESS, UT_WRITE_DEVICE: addr %d\n",
710578f812dSuebayasi value);
711578f812dSuebayasi if (value >= USB_MAX_DEVICES)
712578f812dSuebayasi goto fail;
713578f812dSuebayasi
714578f812dSuebayasi sc->sc_addr = value;
715578f812dSuebayasi break;
716578f812dSuebayasi case C(UR_SET_CONFIG, UT_WRITE_DEVICE):
717578f812dSuebayasi if (value != 0 && value != 1)
718578f812dSuebayasi goto fail;
719578f812dSuebayasi
720578f812dSuebayasi sc->sc_conf = value;
721578f812dSuebayasi break;
722578f812dSuebayasi case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE):
723578f812dSuebayasi break;
724578f812dSuebayasi case C(UR_SET_FEATURE, UT_WRITE_DEVICE):
725578f812dSuebayasi case C(UR_SET_FEATURE, UT_WRITE_INTERFACE):
726578f812dSuebayasi case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT):
727578f812dSuebayasi err = USBD_IOERROR;
728578f812dSuebayasi goto fail;
729578f812dSuebayasi case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
730578f812dSuebayasi break;
731578f812dSuebayasi case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT):
732578f812dSuebayasi break;
733578f812dSuebayasi default:
734578f812dSuebayasi /* Hub requests - XXXNH len check? */
735578f812dSuebayasi err = dwc2_hcd_hub_control(sc->sc_hsotg,
736578f812dSuebayasi C(req->bRequest, req->bmRequestType), value, index,
737578f812dSuebayasi buf, len);
738578f812dSuebayasi if (err) {
739578f812dSuebayasi err = USBD_IOERROR;
740578f812dSuebayasi goto fail;
741578f812dSuebayasi }
742578f812dSuebayasi totlen = len;
743578f812dSuebayasi }
744578f812dSuebayasi xfer->actlen = totlen;
745578f812dSuebayasi err = USBD_NORMAL_COMPLETION;
746578f812dSuebayasi
747578f812dSuebayasi fail:
74870e5eba0Svisa s = splusb();
749578f812dSuebayasi xfer->status = err;
750578f812dSuebayasi usb_transfer_complete(xfer);
75170e5eba0Svisa splx(s);
752578f812dSuebayasi
753628113a4Smpi return err;
754578f812dSuebayasi }
755578f812dSuebayasi
75605c50565Suebayasi STATIC void
dwc2_root_ctrl_abort(struct usbd_xfer * xfer)75740442a57Suebayasi dwc2_root_ctrl_abort(struct usbd_xfer *xfer)
758578f812dSuebayasi {
759578f812dSuebayasi }
760578f812dSuebayasi
76105c50565Suebayasi STATIC void
dwc2_root_ctrl_close(struct usbd_pipe * pipe)76240442a57Suebayasi dwc2_root_ctrl_close(struct usbd_pipe *pipe)
763578f812dSuebayasi {
764578f812dSuebayasi }
765578f812dSuebayasi
76605c50565Suebayasi STATIC void
dwc2_root_ctrl_done(struct usbd_xfer * xfer)76740442a57Suebayasi dwc2_root_ctrl_done(struct usbd_xfer *xfer)
768578f812dSuebayasi {
769578f812dSuebayasi }
770578f812dSuebayasi
77105c50565Suebayasi STATIC usbd_status
dwc2_root_intr_transfer(struct usbd_xfer * xfer)77240442a57Suebayasi dwc2_root_intr_transfer(struct usbd_xfer *xfer)
773578f812dSuebayasi {
774578f812dSuebayasi usbd_status err;
775578f812dSuebayasi
776578f812dSuebayasi err = usb_insert_transfer(xfer);
777578f812dSuebayasi if (err)
778578f812dSuebayasi return err;
779578f812dSuebayasi
780578f812dSuebayasi return dwc2_root_intr_start(SIMPLEQ_FIRST(&xfer->pipe->queue));
781578f812dSuebayasi }
782578f812dSuebayasi
78305c50565Suebayasi STATIC usbd_status
dwc2_root_intr_start(struct usbd_xfer * xfer)78440442a57Suebayasi dwc2_root_intr_start(struct usbd_xfer *xfer)
785578f812dSuebayasi {
786578f812dSuebayasi struct dwc2_softc *sc = DWC2_XFER2SC(xfer);
787578f812dSuebayasi
788d05ae140Smglocker if (sc->sc_bus.dying)
789578f812dSuebayasi return USBD_IOERROR;
790578f812dSuebayasi
791578f812dSuebayasi sc->sc_intrxfer = xfer;
792578f812dSuebayasi
793578f812dSuebayasi return USBD_IN_PROGRESS;
794578f812dSuebayasi }
795578f812dSuebayasi
79605c50565Suebayasi STATIC void
dwc2_root_intr_abort(struct usbd_xfer * xfer)79740442a57Suebayasi dwc2_root_intr_abort(struct usbd_xfer *xfer)
798578f812dSuebayasi {
799578f812dSuebayasi struct dwc2_softc *sc = DWC2_XFER2SC(xfer);
800deae3185Smglocker int s;
801578f812dSuebayasi
802deae3185Smglocker sc->sc_intrxfer = NULL;
803578f812dSuebayasi
804578f812dSuebayasi xfer->status = USBD_CANCELLED;
805deae3185Smglocker s = splusb();
806578f812dSuebayasi usb_transfer_complete(xfer);
807deae3185Smglocker splx(s);
808578f812dSuebayasi }
809578f812dSuebayasi
81005c50565Suebayasi STATIC void
dwc2_root_intr_close(struct usbd_pipe * pipe)81140442a57Suebayasi dwc2_root_intr_close(struct usbd_pipe *pipe)
812578f812dSuebayasi {
813578f812dSuebayasi }
814578f812dSuebayasi
81505c50565Suebayasi STATIC void
dwc2_root_intr_done(struct usbd_xfer * xfer)81640442a57Suebayasi dwc2_root_intr_done(struct usbd_xfer *xfer)
817578f812dSuebayasi {
818578f812dSuebayasi }
819578f812dSuebayasi
82005c50565Suebayasi STATIC usbd_status
dwc2_device_ctrl_transfer(struct usbd_xfer * xfer)82140442a57Suebayasi dwc2_device_ctrl_transfer(struct usbd_xfer *xfer)
822578f812dSuebayasi {
823578f812dSuebayasi usbd_status err;
824578f812dSuebayasi
825578f812dSuebayasi err = usb_insert_transfer(xfer);
826578f812dSuebayasi if (err)
827578f812dSuebayasi return err;
828578f812dSuebayasi
829578f812dSuebayasi return dwc2_device_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue));
830578f812dSuebayasi }
831578f812dSuebayasi
83205c50565Suebayasi STATIC usbd_status
dwc2_device_ctrl_start(struct usbd_xfer * xfer)83340442a57Suebayasi dwc2_device_ctrl_start(struct usbd_xfer *xfer)
834578f812dSuebayasi {
835d05ae140Smglocker struct dwc2_softc *sc = DWC2_XFER2SC(xfer);
836578f812dSuebayasi usbd_status err;
837578f812dSuebayasi
838deae3185Smglocker KASSERT(xfer->rqflags & URQ_REQUEST);
839578f812dSuebayasi
840deae3185Smglocker if (sc->sc_bus.dying)
841deae3185Smglocker return USBD_IOERROR;
842deae3185Smglocker
843578f812dSuebayasi err = dwc2_device_start(xfer);
844d05ae140Smglocker if (err)
8459eaa3e95Sjmatthew return err;
846d05ae140Smglocker
847d05ae140Smglocker return USBD_IN_PROGRESS;
848578f812dSuebayasi }
849578f812dSuebayasi
85005c50565Suebayasi STATIC void
dwc2_device_ctrl_abort(struct usbd_xfer * xfer)85140442a57Suebayasi dwc2_device_ctrl_abort(struct usbd_xfer *xfer)
852578f812dSuebayasi {
853578f812dSuebayasi dwc2_abort_xfer(xfer, USBD_CANCELLED);
854578f812dSuebayasi }
855578f812dSuebayasi
85605c50565Suebayasi STATIC void
dwc2_device_ctrl_close(struct usbd_pipe * pipe)85740442a57Suebayasi dwc2_device_ctrl_close(struct usbd_pipe *pipe)
858578f812dSuebayasi {
859d05ae140Smglocker struct dwc2_softc * const sc = DWC2_PIPE2SC(pipe);
860d05ae140Smglocker struct dwc2_pipe * const dpipe = DWC2_PIPE2DPIPE(pipe);
861578f812dSuebayasi
862578f812dSuebayasi dwc2_close_pipe(pipe);
863d05ae140Smglocker usb_freemem(&sc->sc_bus, &dpipe->req_dma);
864578f812dSuebayasi }
865578f812dSuebayasi
86605c50565Suebayasi STATIC void
dwc2_device_ctrl_done(struct usbd_xfer * xfer)86740442a57Suebayasi dwc2_device_ctrl_done(struct usbd_xfer *xfer)
868578f812dSuebayasi {
869deae3185Smglocker KASSERT(xfer->rqflags & URQ_REQUEST);
870578f812dSuebayasi }
871578f812dSuebayasi
87205c50565Suebayasi STATIC usbd_status
dwc2_device_bulk_transfer(struct usbd_xfer * xfer)87340442a57Suebayasi dwc2_device_bulk_transfer(struct usbd_xfer *xfer)
874578f812dSuebayasi {
875578f812dSuebayasi usbd_status err;
876578f812dSuebayasi
877578f812dSuebayasi err = usb_insert_transfer(xfer);
878578f812dSuebayasi if (err)
879578f812dSuebayasi return err;
880578f812dSuebayasi
881578f812dSuebayasi return dwc2_device_bulk_start(SIMPLEQ_FIRST(&xfer->pipe->queue));
882578f812dSuebayasi }
883578f812dSuebayasi
88405c50565Suebayasi STATIC usbd_status
dwc2_device_bulk_start(struct usbd_xfer * xfer)88540442a57Suebayasi dwc2_device_bulk_start(struct usbd_xfer *xfer)
886578f812dSuebayasi {
887deae3185Smglocker struct dwc2_softc *sc = DWC2_XFER2SC(xfer);
888578f812dSuebayasi usbd_status err;
889578f812dSuebayasi
890deae3185Smglocker KASSERT(!(xfer->rqflags & URQ_REQUEST));
89170e5eba0Svisa
892deae3185Smglocker if (sc->sc_bus.dying)
893deae3185Smglocker return (USBD_IOERROR);
894deae3185Smglocker
895578f812dSuebayasi err = dwc2_device_start(xfer);
896deae3185Smglocker if (err)
897578f812dSuebayasi return err;
898deae3185Smglocker
899deae3185Smglocker return USBD_IN_PROGRESS;
900578f812dSuebayasi }
901578f812dSuebayasi
90205c50565Suebayasi STATIC void
dwc2_device_bulk_abort(struct usbd_xfer * xfer)90340442a57Suebayasi dwc2_device_bulk_abort(struct usbd_xfer *xfer)
904578f812dSuebayasi {
905578f812dSuebayasi dwc2_abort_xfer(xfer, USBD_CANCELLED);
906578f812dSuebayasi }
907578f812dSuebayasi
90805c50565Suebayasi STATIC void
dwc2_device_bulk_close(struct usbd_pipe * pipe)90940442a57Suebayasi dwc2_device_bulk_close(struct usbd_pipe *pipe)
910578f812dSuebayasi {
911578f812dSuebayasi dwc2_close_pipe(pipe);
912578f812dSuebayasi }
913578f812dSuebayasi
91405c50565Suebayasi STATIC void
dwc2_device_bulk_done(struct usbd_xfer * xfer)91540442a57Suebayasi dwc2_device_bulk_done(struct usbd_xfer *xfer)
916578f812dSuebayasi {
917578f812dSuebayasi }
918578f812dSuebayasi
91905c50565Suebayasi STATIC usbd_status
dwc2_device_intr_transfer(struct usbd_xfer * xfer)92040442a57Suebayasi dwc2_device_intr_transfer(struct usbd_xfer *xfer)
921578f812dSuebayasi {
922578f812dSuebayasi usbd_status err;
923578f812dSuebayasi
924578f812dSuebayasi err = usb_insert_transfer(xfer);
925578f812dSuebayasi if (err)
926578f812dSuebayasi return err;
927578f812dSuebayasi
928578f812dSuebayasi return dwc2_device_intr_start(SIMPLEQ_FIRST(&xfer->pipe->queue));
929578f812dSuebayasi }
930578f812dSuebayasi
93105c50565Suebayasi STATIC usbd_status
dwc2_device_intr_start(struct usbd_xfer * xfer)93240442a57Suebayasi dwc2_device_intr_start(struct usbd_xfer *xfer)
933578f812dSuebayasi {
934d05ae140Smglocker struct dwc2_softc *sc = DWC2_XFER2SC(xfer);
935578f812dSuebayasi usbd_status err;
936578f812dSuebayasi
937deae3185Smglocker KASSERT(!(xfer->rqflags & URQ_REQUEST));
938deae3185Smglocker
939deae3185Smglocker if (sc->sc_bus.dying)
940deae3185Smglocker return (USBD_IOERROR);
941deae3185Smglocker
942578f812dSuebayasi err = dwc2_device_start(xfer);
943d05ae140Smglocker if (err)
9449eaa3e95Sjmatthew return err;
945d05ae140Smglocker
946d05ae140Smglocker return USBD_IN_PROGRESS;
947578f812dSuebayasi }
948578f812dSuebayasi
94905c50565Suebayasi STATIC void
dwc2_device_intr_abort(struct usbd_xfer * xfer)95040442a57Suebayasi dwc2_device_intr_abort(struct usbd_xfer *xfer)
951578f812dSuebayasi {
952deae3185Smglocker KASSERT(!xfer->pipe->repeat || xfer->pipe->intrxfer == xfer);
953578f812dSuebayasi
954578f812dSuebayasi dwc2_abort_xfer(xfer, USBD_CANCELLED);
955578f812dSuebayasi }
956578f812dSuebayasi
95705c50565Suebayasi STATIC void
dwc2_device_intr_close(struct usbd_pipe * pipe)95840442a57Suebayasi dwc2_device_intr_close(struct usbd_pipe *pipe)
959578f812dSuebayasi {
960578f812dSuebayasi dwc2_close_pipe(pipe);
961578f812dSuebayasi }
962578f812dSuebayasi
96305c50565Suebayasi STATIC void
dwc2_device_intr_done(struct usbd_xfer * xfer)96440442a57Suebayasi dwc2_device_intr_done(struct usbd_xfer *xfer)
965578f812dSuebayasi {
966deae3185Smglocker if (xfer->pipe->repeat)
967578f812dSuebayasi dwc2_device_start(xfer);
968578f812dSuebayasi }
969578f812dSuebayasi
970578f812dSuebayasi usbd_status
dwc2_device_isoc_transfer(struct usbd_xfer * xfer)97140442a57Suebayasi dwc2_device_isoc_transfer(struct usbd_xfer *xfer)
972578f812dSuebayasi {
973578f812dSuebayasi usbd_status err;
974578f812dSuebayasi
975578f812dSuebayasi err = usb_insert_transfer(xfer);
976578f812dSuebayasi if (err)
977578f812dSuebayasi return err;
978578f812dSuebayasi
979578f812dSuebayasi return dwc2_device_isoc_start(SIMPLEQ_FIRST(&xfer->pipe->queue));
980578f812dSuebayasi }
981578f812dSuebayasi
982578f812dSuebayasi usbd_status
dwc2_device_isoc_start(struct usbd_xfer * xfer)98340442a57Suebayasi dwc2_device_isoc_start(struct usbd_xfer *xfer)
984578f812dSuebayasi {
985578f812dSuebayasi struct dwc2_pipe *dpipe = DWC2_XFER2DPIPE(xfer);
9863215ab50Suebayasi struct dwc2_softc *sc = DWC2_DPIPE2SC(dpipe);
987578f812dSuebayasi usbd_status err;
988578f812dSuebayasi
9899deba327Sjmatthew /* Why would you do that anyway? */
9909deba327Sjmatthew if (sc->sc_bus.use_polling)
9919deba327Sjmatthew return (USBD_INVAL);
9929deba327Sjmatthew
993578f812dSuebayasi err = dwc2_device_start(xfer);
994deae3185Smglocker if (err)
995578f812dSuebayasi return err;
996deae3185Smglocker
997deae3185Smglocker return USBD_IN_PROGRESS;
998578f812dSuebayasi }
999578f812dSuebayasi
1000578f812dSuebayasi void
dwc2_device_isoc_abort(struct usbd_xfer * xfer)100140442a57Suebayasi dwc2_device_isoc_abort(struct usbd_xfer *xfer)
1002578f812dSuebayasi {
1003578f812dSuebayasi dwc2_abort_xfer(xfer, USBD_CANCELLED);
1004578f812dSuebayasi }
1005578f812dSuebayasi
1006578f812dSuebayasi void
dwc2_device_isoc_close(struct usbd_pipe * pipe)100740442a57Suebayasi dwc2_device_isoc_close(struct usbd_pipe *pipe)
1008578f812dSuebayasi {
1009578f812dSuebayasi dwc2_close_pipe(pipe);
1010578f812dSuebayasi }
1011578f812dSuebayasi
1012578f812dSuebayasi void
dwc2_device_isoc_done(struct usbd_xfer * xfer)101340442a57Suebayasi dwc2_device_isoc_done(struct usbd_xfer *xfer)
1014578f812dSuebayasi {
1015578f812dSuebayasi }
1016578f812dSuebayasi
1017578f812dSuebayasi usbd_status
dwc2_device_start(struct usbd_xfer * xfer)101840442a57Suebayasi dwc2_device_start(struct usbd_xfer *xfer)
1019578f812dSuebayasi {
1020578f812dSuebayasi struct dwc2_xfer *dxfer = DWC2_XFER2DXFER(xfer);
1021578f812dSuebayasi struct dwc2_pipe *dpipe = DWC2_XFER2DPIPE(xfer);
1022578f812dSuebayasi struct dwc2_softc *sc = DWC2_XFER2SC(xfer);
1023578f812dSuebayasi struct dwc2_hsotg *hsotg = sc->sc_hsotg;
1024578f812dSuebayasi struct dwc2_hcd_urb *dwc2_urb;
1025578f812dSuebayasi
102640442a57Suebayasi struct usbd_device *dev = xfer->pipe->device;
1027578f812dSuebayasi usb_endpoint_descriptor_t *ed = xfer->pipe->endpoint->edesc;
1028578f812dSuebayasi uint8_t addr = dev->address;
1029578f812dSuebayasi uint8_t xfertype = UE_GET_XFERTYPE(ed->bmAttributes);
1030578f812dSuebayasi uint8_t epnum = UE_GET_ADDR(ed->bEndpointAddress);
1031578f812dSuebayasi uint8_t dir = UE_GET_DIR(ed->bEndpointAddress);
1032a9beb1edSmglocker uint32_t mps = UGETW(ed->wMaxPacketSize);
1033578f812dSuebayasi uint32_t len;
1034578f812dSuebayasi
1035578f812dSuebayasi uint32_t flags = 0;
1036578f812dSuebayasi uint32_t off = 0;
1037d05ae140Smglocker int retval, err;
1038578f812dSuebayasi int alloc_bandwidth = 0;
1039578f812dSuebayasi
1040578f812dSuebayasi DPRINTFN(1, "xfer=%p pipe=%p\n", xfer, xfer->pipe);
1041578f812dSuebayasi
1042578f812dSuebayasi if (xfertype == UE_ISOCHRONOUS ||
1043578f812dSuebayasi xfertype == UE_INTERRUPT) {
1044b6c5b329Suebayasi mtx_enter(&hsotg->lock);
1045578f812dSuebayasi if (!dwc2_hcd_is_bandwidth_allocated(hsotg, xfer))
1046578f812dSuebayasi alloc_bandwidth = 1;
1047b6c5b329Suebayasi mtx_leave(&hsotg->lock);
1048578f812dSuebayasi }
1049578f812dSuebayasi
1050578f812dSuebayasi /*
1051578f812dSuebayasi * For Control pipe the direction is from the request, all other
1052578f812dSuebayasi * transfers have been set correctly at pipe open time.
1053578f812dSuebayasi */
1054578f812dSuebayasi if (xfertype == UE_CONTROL) {
1055578f812dSuebayasi usb_device_request_t *req = &xfer->request;
1056578f812dSuebayasi
1057578f812dSuebayasi DPRINTFN(3, "xfer=%p type=0x%02x request=0x%02x wValue=0x%04x "
1058578f812dSuebayasi "wIndex=0x%04x len=%d addr=%d endpt=%d dir=%s speed=%d "
1059578f812dSuebayasi "mps=%d\n",
1060578f812dSuebayasi xfer, req->bmRequestType, req->bRequest, UGETW(req->wValue),
1061578f812dSuebayasi UGETW(req->wIndex), UGETW(req->wLength), dev->address,
1062a9beb1edSmglocker epnum, dir == UT_READ ? "in" :"out", dev->speed,
1063a9beb1edSmglocker UE_GET_SIZE(mps));
1064578f812dSuebayasi
1065578f812dSuebayasi /* Copy request packet to our DMA buffer */
1066578f812dSuebayasi memcpy(KERNADDR(&dpipe->req_dma, 0), req, sizeof(*req));
1067578f812dSuebayasi usb_syncmem(&dpipe->req_dma, 0, sizeof(*req),
1068578f812dSuebayasi BUS_DMASYNC_PREWRITE);
1069578f812dSuebayasi len = UGETW(req->wLength);
1070578f812dSuebayasi if ((req->bmRequestType & UT_READ) == UT_READ) {
1071578f812dSuebayasi dir = UE_DIR_IN;
1072578f812dSuebayasi } else {
1073578f812dSuebayasi dir = UE_DIR_OUT;
1074578f812dSuebayasi }
1075578f812dSuebayasi
1076ef05a7e5Suebayasi DPRINTFN(3, "req = %p dma = %llx len %d dir %s\n",
1077ef05a7e5Suebayasi KERNADDR(&dpipe->req_dma, 0),
1078ef05a7e5Suebayasi (long long)DMAADDR(&dpipe->req_dma, 0),
1079578f812dSuebayasi len, dir == UE_DIR_IN ? "in" : "out");
1080d05ae140Smglocker } else if (xfertype == UE_ISOCHRONOUS) {
1081d05ae140Smglocker DPRINTFN(3, "xfer=%p nframes=%d flags=%d addr=%d endpt=%d,"
1082d05ae140Smglocker " mps=%d dir %s\n", xfer, xfer->nframes, xfer->flags, addr,
1083a9beb1edSmglocker epnum, UE_GET_SIZE(mps), dir == UT_READ ? "in" :"out");
1084d05ae140Smglocker
1085d05ae140Smglocker #ifdef DIAGNOSTIC
1086d05ae140Smglocker len = 0;
1087d05ae140Smglocker for (size_t i = 0; i < xfer->nframes; i++)
1088d05ae140Smglocker len += xfer->frlengths[i];
1089d05ae140Smglocker if (len != xfer->length)
1090d05ae140Smglocker panic("len (%d) != xfer->length (%d)", len,
1091d05ae140Smglocker xfer->length);
1092d05ae140Smglocker #endif
1093d05ae140Smglocker len = xfer->length;
1094578f812dSuebayasi } else {
1095578f812dSuebayasi DPRINTFN(3, "xfer=%p len=%d flags=%d addr=%d endpt=%d,"
1096578f812dSuebayasi " mps=%d dir %s\n", xfer, xfer->length, xfer->flags, addr,
1097a9beb1edSmglocker epnum, UE_GET_SIZE(mps), dir == UT_READ ? "in" :"out");
1098578f812dSuebayasi
1099578f812dSuebayasi len = xfer->length;
1100578f812dSuebayasi }
1101578f812dSuebayasi
1102f6fab45dSmglocker dxfer->urb = dwc2_hcd_urb_alloc(sc->sc_hsotg, xfer->nframes, M_NOWAIT);
1103578f812dSuebayasi dwc2_urb = dxfer->urb;
1104578f812dSuebayasi if (!dwc2_urb)
1105578f812dSuebayasi return USBD_NOMEM;
1106578f812dSuebayasi
1107578f812dSuebayasi memset(dwc2_urb, 0, sizeof(*dwc2_urb) +
1108f6fab45dSmglocker sizeof(dwc2_urb->iso_descs[0]) * xfer->nframes);
1109578f812dSuebayasi
1110d05ae140Smglocker dwc2_urb->priv = xfer;
1111d05ae140Smglocker dwc2_urb->packet_count = xfer->nframes;
1112d05ae140Smglocker
1113578f812dSuebayasi dwc2_hcd_urb_set_pipeinfo(hsotg, dwc2_urb, addr, epnum, xfertype, dir,
1114a9beb1edSmglocker UE_GET_SIZE(mps), UE_GET_TRANS(mps) + 1);
1115578f812dSuebayasi
1116578f812dSuebayasi if (xfertype == UE_CONTROL) {
1117578f812dSuebayasi dwc2_urb->setup_usbdma = &dpipe->req_dma;
1118578f812dSuebayasi dwc2_urb->setup_packet = KERNADDR(&dpipe->req_dma, 0);
1119578f812dSuebayasi dwc2_urb->setup_dma = DMAADDR(&dpipe->req_dma, 0);
1120578f812dSuebayasi } else {
1121578f812dSuebayasi /* XXXNH - % mps required? */
1122a9beb1edSmglocker if ((xfer->flags & USBD_FORCE_SHORT_XFER) && (len %
1123a9beb1edSmglocker UE_GET_SIZE(mps)) == 0)
1124578f812dSuebayasi flags |= URB_SEND_ZERO_PACKET;
1125578f812dSuebayasi }
1126578f812dSuebayasi flags |= URB_GIVEBACK_ASAP;
1127578f812dSuebayasi
1128578f812dSuebayasi /*
1129578f812dSuebayasi * control transfers with no data phase don't touch usbdma, but
1130578f812dSuebayasi * everything else does.
1131578f812dSuebayasi */
1132578f812dSuebayasi if (!(xfertype == UE_CONTROL && len == 0)) {
1133578f812dSuebayasi dwc2_urb->usbdma = &xfer->dmabuf;
1134578f812dSuebayasi dwc2_urb->buf = KERNADDR(dwc2_urb->usbdma, 0);
1135578f812dSuebayasi dwc2_urb->dma = DMAADDR(dwc2_urb->usbdma, 0);
1136d05ae140Smglocker
1137d05ae140Smglocker usb_syncmem(&xfer->dmabuf, 0, len,
1138d05ae140Smglocker dir == UE_DIR_IN ?
11395343ff5aSpatrick BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
1140578f812dSuebayasi }
1141578f812dSuebayasi dwc2_urb->length = len;
1142578f812dSuebayasi dwc2_urb->flags = flags;
1143578f812dSuebayasi dwc2_urb->status = -EINPROGRESS;
1144578f812dSuebayasi
1145578f812dSuebayasi if (xfertype == UE_INTERRUPT ||
1146578f812dSuebayasi xfertype == UE_ISOCHRONOUS) {
1147578f812dSuebayasi uint16_t ival;
1148578f812dSuebayasi
1149578f812dSuebayasi if (xfertype == UE_INTERRUPT &&
1150578f812dSuebayasi dpipe->pipe.interval != USBD_DEFAULT_INTERVAL) {
1151578f812dSuebayasi ival = dpipe->pipe.interval;
1152578f812dSuebayasi } else {
1153578f812dSuebayasi ival = ed->bInterval;
1154578f812dSuebayasi }
1155578f812dSuebayasi
1156578f812dSuebayasi if (ival < 1) {
1157578f812dSuebayasi retval = -ENODEV;
1158578f812dSuebayasi goto fail;
1159578f812dSuebayasi }
1160578f812dSuebayasi if (dev->speed == USB_SPEED_HIGH ||
1161578f812dSuebayasi (dev->speed == USB_SPEED_FULL && xfertype == UE_ISOCHRONOUS)) {
1162578f812dSuebayasi if (ival > 16) {
1163578f812dSuebayasi /*
1164578f812dSuebayasi * illegal with HS/FS, but there were
1165578f812dSuebayasi * documentation bugs in the spec
1166578f812dSuebayasi */
1167578f812dSuebayasi ival = 256;
1168578f812dSuebayasi } else {
1169578f812dSuebayasi ival = (1 << (ival - 1));
1170578f812dSuebayasi }
1171578f812dSuebayasi } else {
1172578f812dSuebayasi if (xfertype == UE_INTERRUPT && ival < 10)
1173578f812dSuebayasi ival = 10;
1174578f812dSuebayasi }
1175578f812dSuebayasi dwc2_urb->interval = ival;
1176578f812dSuebayasi }
1177578f812dSuebayasi
1178578f812dSuebayasi xfer->actlen = 0;
1179578f812dSuebayasi
1180578f812dSuebayasi KASSERTMSG(xfer->nframes == 0 || xfertype == UE_ISOCHRONOUS,
1181578f812dSuebayasi "nframes %d xfertype %d\n", xfer->nframes, xfertype);
1182578f812dSuebayasi
1183d05ae140Smglocker off = 0;
1184d05ae140Smglocker for (size_t i = 0; i < xfer->nframes; ++i) {
1185d05ae140Smglocker DPRINTFN(3, "xfer=%p frame=%zu offset=%d length=%d\n", xfer, i,
1186578f812dSuebayasi off, xfer->frlengths[i]);
1187578f812dSuebayasi
1188578f812dSuebayasi dwc2_hcd_urb_set_iso_desc_params(dwc2_urb, i, off,
1189578f812dSuebayasi xfer->frlengths[i]);
1190578f812dSuebayasi off += xfer->frlengths[i];
1191578f812dSuebayasi }
1192578f812dSuebayasi
1193d05ae140Smglocker struct dwc2_qh *qh = dpipe->priv;
1194d05ae140Smglocker struct dwc2_qtd *qtd;
1195d05ae140Smglocker bool qh_allocated = false;
1196578f812dSuebayasi
1197d05ae140Smglocker /* Create QH for the endpoint if it doesn't exist */
1198d05ae140Smglocker if (!qh) {
1199a9beb1edSmglocker qh = dwc2_hcd_qh_create(hsotg, dwc2_urb, M_ZERO | M_NOWAIT);
1200d05ae140Smglocker if (!qh) {
1201d05ae140Smglocker retval = -ENOMEM;
1202d05ae140Smglocker goto fail;
1203d05ae140Smglocker }
1204d05ae140Smglocker dpipe->priv = qh;
1205d05ae140Smglocker qh_allocated = true;
1206578f812dSuebayasi }
1207578f812dSuebayasi
1208a0d2b8daSmglocker qtd = pool_get(&sc->sc_qtdpool, PR_NOWAIT | PR_ZERO);
1209d05ae140Smglocker if (!qtd) {
1210d05ae140Smglocker retval = -ENOMEM;
1211d05ae140Smglocker goto fail1;
1212d05ae140Smglocker }
1213d05ae140Smglocker
1214d05ae140Smglocker /* might need to check cpu_intr_p */
1215d05ae140Smglocker mtx_enter(&hsotg->lock);
1216d05ae140Smglocker retval = dwc2_hcd_urb_enqueue(hsotg, dwc2_urb, qh, qtd);
1217578f812dSuebayasi if (retval)
1218d05ae140Smglocker goto fail2;
1219d05ae140Smglocker if (xfer->timeout && !sc->sc_bus.use_polling) {
1220d05ae140Smglocker timeout_set(&xfer->timeout_handle, dwc2_timeout, xfer);
1221d05ae140Smglocker timeout_add_msec(&xfer->timeout_handle, xfer->timeout);
1222d05ae140Smglocker }
1223d05ae140Smglocker xfer->status = USBD_IN_PROGRESS;
1224578f812dSuebayasi
1225578f812dSuebayasi if (alloc_bandwidth) {
1226578f812dSuebayasi dwc2_allocate_bus_bandwidth(hsotg,
1227578f812dSuebayasi dwc2_hcd_get_ep_bandwidth(hsotg, dpipe),
1228578f812dSuebayasi xfer);
1229578f812dSuebayasi }
1230b6c5b329Suebayasi mtx_leave(&hsotg->lock);
1231d05ae140Smglocker
1232d05ae140Smglocker return USBD_IN_PROGRESS;
1233d05ae140Smglocker
1234d05ae140Smglocker fail2:
1235d05ae140Smglocker dwc2_urb->priv = NULL;
1236d05ae140Smglocker mtx_leave(&hsotg->lock);
1237d05ae140Smglocker pool_put(&sc->sc_qtdpool, qtd);
1238d05ae140Smglocker
1239d05ae140Smglocker fail1:
1240d05ae140Smglocker if (qh_allocated) {
1241d05ae140Smglocker dpipe->priv = NULL;
1242d05ae140Smglocker dwc2_hcd_qh_free(hsotg, qh);
1243d05ae140Smglocker }
1244d05ae140Smglocker fail:
1245578f812dSuebayasi
1246578f812dSuebayasi switch (retval) {
1247d05ae140Smglocker case -EINVAL:
1248578f812dSuebayasi case -ENODEV:
1249578f812dSuebayasi err = USBD_INVAL;
1250578f812dSuebayasi break;
1251578f812dSuebayasi case -ENOMEM:
1252578f812dSuebayasi err = USBD_NOMEM;
1253578f812dSuebayasi break;
1254578f812dSuebayasi default:
1255578f812dSuebayasi err = USBD_IOERROR;
1256578f812dSuebayasi }
1257578f812dSuebayasi
1258578f812dSuebayasi return err;
1259578f812dSuebayasi
1260578f812dSuebayasi }
1261578f812dSuebayasi
dwc2_intr(void * p)1262d05ae140Smglocker int dwc2_intr(void *p)
1263578f812dSuebayasi {
1264578f812dSuebayasi struct dwc2_softc *sc = p;
1265d05ae140Smglocker struct dwc2_hsotg *hsotg;
1266578f812dSuebayasi int ret = 0;
1267578f812dSuebayasi
1268d05ae140Smglocker if (sc == NULL)
1269d05ae140Smglocker return 0;
1270d05ae140Smglocker
1271d05ae140Smglocker hsotg = sc->sc_hsotg;
1272a9beb1edSmglocker // mtx_enter(&hsotg->lock);
1273578f812dSuebayasi
1274d05ae140Smglocker if (sc->sc_bus.dying)
1275578f812dSuebayasi goto done;
1276578f812dSuebayasi
1277578f812dSuebayasi if (sc->sc_bus.use_polling) {
1278578f812dSuebayasi uint32_t intrs;
1279578f812dSuebayasi
1280578f812dSuebayasi intrs = dwc2_read_core_intr(hsotg);
1281a9beb1edSmglocker dwc2_writel(hsotg, intrs, GINTSTS);
1282578f812dSuebayasi } else {
1283578f812dSuebayasi ret = dwc2_interrupt(sc);
1284578f812dSuebayasi }
1285578f812dSuebayasi
1286578f812dSuebayasi done:
1287a9beb1edSmglocker // mtx_leave(&hsotg->lock);
1288578f812dSuebayasi
1289578f812dSuebayasi return ret;
1290578f812dSuebayasi }
1291578f812dSuebayasi
1292578f812dSuebayasi int
dwc2_interrupt(struct dwc2_softc * sc)1293578f812dSuebayasi dwc2_interrupt(struct dwc2_softc *sc)
1294578f812dSuebayasi {
1295578f812dSuebayasi int ret = 0;
1296578f812dSuebayasi
1297c8466c48Smglocker if (sc->sc_hcdenabled)
1298578f812dSuebayasi ret |= dwc2_handle_hcd_intr(sc->sc_hsotg);
1299578f812dSuebayasi
1300578f812dSuebayasi ret |= dwc2_handle_common_intr(sc->sc_hsotg);
1301578f812dSuebayasi
1302578f812dSuebayasi return ret;
1303578f812dSuebayasi }
1304578f812dSuebayasi
1305578f812dSuebayasi int
dwc2_detach(struct dwc2_softc * sc,int flags)1306578f812dSuebayasi dwc2_detach(struct dwc2_softc *sc, int flags)
1307578f812dSuebayasi {
1308578f812dSuebayasi int rv = 0;
1309578f812dSuebayasi
1310578f812dSuebayasi if (sc->sc_child != NULL)
1311578f812dSuebayasi rv = config_detach(sc->sc_child, flags);
1312578f812dSuebayasi
1313578f812dSuebayasi return rv;
1314578f812dSuebayasi }
1315578f812dSuebayasi
1316578f812dSuebayasi int
dwc2_init(struct dwc2_softc * sc)1317578f812dSuebayasi dwc2_init(struct dwc2_softc *sc)
1318578f812dSuebayasi {
1319c8466c48Smglocker int retval, err = 0;
1320c8466c48Smglocker struct dwc2_hsotg *hsotg;
1321578f812dSuebayasi
1322578f812dSuebayasi sc->sc_bus.usbrev = USBREV_2_0;
1323578f812dSuebayasi sc->sc_bus.methods = &dwc2_bus_methods;
1324578f812dSuebayasi sc->sc_bus.pipe_size = sizeof(struct dwc2_pipe);
1325578f812dSuebayasi sc->sc_hcdenabled = false;
1326578f812dSuebayasi
1327d05ae140Smglocker mtx_init(&sc->sc_lock, IPL_SOFTUSB);
1328d05ae140Smglocker
1329578f812dSuebayasi TAILQ_INIT(&sc->sc_complete);
1330578f812dSuebayasi
1331ef05a7e5Suebayasi sc->sc_rhc_si = softintr_establish(IPL_SOFTUSB, dwc2_rhc, sc);
1332578f812dSuebayasi
1333a9beb1edSmglocker pool_init(&sc->sc_xferpool, sizeof(struct dwc2_xfer), 0, IPL_VM, 0,
13345e13bfdfSuebayasi "dwc2xfer", NULL);
1335a9beb1edSmglocker pool_init(&sc->sc_qhpool, sizeof(struct dwc2_qh), 0, IPL_VM, 0,
13365e13bfdfSuebayasi "dwc2qh", NULL);
1337a9beb1edSmglocker pool_init(&sc->sc_qtdpool, sizeof(struct dwc2_qtd), 0, IPL_VM, 0,
13385e13bfdfSuebayasi "dwc2qtd", NULL);
1339578f812dSuebayasi
13406303db42Smglocker sc->sc_hsotg = malloc(sizeof(struct dwc2_hsotg), M_USBHC,
1341aaade355Suebayasi M_ZERO | M_WAITOK);
1342578f812dSuebayasi sc->sc_hsotg->hsotg_sc = sc;
13433215ab50Suebayasi sc->sc_hsotg->dev = &sc->sc_bus.bdev;
1344578f812dSuebayasi sc->sc_hcdenabled = true;
1345c8466c48Smglocker hsotg = sc->sc_hsotg;
1346578f812dSuebayasi
1347d05ae140Smglocker hsotg->dr_mode = USB_DR_MODE_HOST;
1348d05ae140Smglocker
1349d05ae140Smglocker /*
1350a9beb1edSmglocker * Before performing any core related operations
1351a9beb1edSmglocker * check core version.
1352a9beb1edSmglocker */
1353a9beb1edSmglocker retval = dwc2_check_core_version(hsotg);
1354a9beb1edSmglocker if (retval)
1355a9beb1edSmglocker goto fail2;
1356a9beb1edSmglocker
1357a9beb1edSmglocker /*
1358d05ae140Smglocker * Reset before dwc2_get_hwparams() then it could get power-on real
1359d05ae140Smglocker * reset value form registers.
1360d05ae140Smglocker */
1361a9beb1edSmglocker retval = dwc2_core_reset(hsotg, false);
1362a9beb1edSmglocker if (retval)
1363a9beb1edSmglocker goto fail2;
1364d05ae140Smglocker
1365d05ae140Smglocker /* Detect config values from hardware */
1366d05ae140Smglocker retval = dwc2_get_hwparams(hsotg);
1367a9beb1edSmglocker if (retval)
1368d05ae140Smglocker goto fail2;
1369d05ae140Smglocker
1370a9beb1edSmglocker /*
1371a9beb1edSmglocker * For OTG cores, set the force mode bits to reflect the value
1372a9beb1edSmglocker * of dr_mode. Force mode bits should not be touched at any
1373a9beb1edSmglocker * other time after this.
1374a9beb1edSmglocker */
1375a9beb1edSmglocker dwc2_force_dr_mode(hsotg);
1376d05ae140Smglocker
1377a9beb1edSmglocker retval = dwc2_init_params(hsotg);
1378a9beb1edSmglocker if (retval)
1379a9beb1edSmglocker goto fail2;
1380a9beb1edSmglocker #if 0
1381d05ae140Smglocker if (hsotg->dr_mode != USB_DR_MODE_HOST) {
1382d05ae140Smglocker retval = dwc2_gadget_init(hsotg);
1383d05ae140Smglocker if (retval)
1384d05ae140Smglocker goto fail2;
1385d05ae140Smglocker hsotg->gadget_enabled = 1;
1386d05ae140Smglocker }
1387d05ae140Smglocker #endif
1388d05ae140Smglocker if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
1389d05ae140Smglocker retval = dwc2_hcd_init(hsotg);
1390d05ae140Smglocker if (retval) {
1391d05ae140Smglocker if (hsotg->gadget_enabled)
1392d05ae140Smglocker dwc2_hsotg_remove(hsotg);
1393d05ae140Smglocker goto fail2;
1394d05ae140Smglocker }
1395d05ae140Smglocker hsotg->hcd_enabled = 1;
1396d05ae140Smglocker }
1397d05ae140Smglocker
1398a9beb1edSmglocker hsotg->hibernated = 0;
1399578f812dSuebayasi
1400578f812dSuebayasi return 0;
1401578f812dSuebayasi
1402d05ae140Smglocker fail2:
1403d05ae140Smglocker err = -retval;
14046303db42Smglocker free(sc->sc_hsotg, M_USBHC, sizeof(struct dwc2_hsotg));
1405b6c5b329Suebayasi softintr_disestablish(sc->sc_rhc_si);
1406578f812dSuebayasi
1407578f812dSuebayasi return err;
1408578f812dSuebayasi }
1409578f812dSuebayasi
1410578f812dSuebayasi void
dw_timeout(void * arg)14110395aa1eSuebayasi dw_timeout(void *arg)
1412578f812dSuebayasi {
1413578f812dSuebayasi struct delayed_work *dw = arg;
1414578f812dSuebayasi
1415ea6872ecSjmatthew task_set(&dw->work, dw->dw_fn, dw->dw_arg);
1416016aa740Suebayasi task_add(dw->dw_wq, &dw->work);
1417578f812dSuebayasi }
1418578f812dSuebayasi
1419a9beb1edSmglocker /*** platform.c ***************************************************************/
1420a9beb1edSmglocker
dwc2_check_core_version(struct dwc2_hsotg * hsotg)1421a9beb1edSmglocker int dwc2_check_core_version(struct dwc2_hsotg *hsotg)
1422578f812dSuebayasi {
1423a9beb1edSmglocker struct dwc2_hw_params *hw = &hsotg->hw_params;
1424578f812dSuebayasi
1425578f812dSuebayasi /*
1426a9beb1edSmglocker * Attempt to ensure this device is really a DWC_otg Controller.
1427a9beb1edSmglocker * Read and verify the GSNPSID register contents. The value should be
1428a9beb1edSmglocker * 0x45f4xxxx, 0x5531xxxx or 0x5532xxxx
1429578f812dSuebayasi */
1430578f812dSuebayasi
1431a9beb1edSmglocker hw->snpsid = dwc2_readl(hsotg, GSNPSID);
1432a9beb1edSmglocker if ((hw->snpsid & GSNPSID_ID_MASK) != DWC2_OTG_ID &&
1433a9beb1edSmglocker (hw->snpsid & GSNPSID_ID_MASK) != DWC2_FS_IOT_ID &&
1434a9beb1edSmglocker (hw->snpsid & GSNPSID_ID_MASK) != DWC2_HS_IOT_ID) {
1435a9beb1edSmglocker dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n",
1436a9beb1edSmglocker hw->snpsid);
1437a9beb1edSmglocker return -ENODEV;
1438578f812dSuebayasi }
1439578f812dSuebayasi
1440a9beb1edSmglocker dev_dbg(hsotg->dev, "Core Release: %1x.%1x%1x%1x (snpsid=%x)\n",
1441a9beb1edSmglocker hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf,
1442a9beb1edSmglocker hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid);
1443578f812dSuebayasi return 0;
1444578f812dSuebayasi }
1445