summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb/ohci.c
diff options
context:
space:
mode:
authorwpaul <wpaul@FreeBSD.org>2000-01-20 07:38:33 +0000
committerwpaul <wpaul@FreeBSD.org>2000-01-20 07:38:33 +0000
commitd5c9bbfbf2027c7efc9f44aee8f5cc459198b3f0 (patch)
tree605d3c9f2f783654045386042cd04f312155f047 /sys/dev/usb/ohci.c
parent6dea110be6b504383e7271cd0173acd492dea9e8 (diff)
downloadFreeBSD-src-d5c9bbfbf2027c7efc9f44aee8f5cc459198b3f0.zip
FreeBSD-src-d5c9bbfbf2027c7efc9f44aee8f5cc459198b3f0.tar.gz
More USB ethernet tweaks:
- Sync ohci, uhci and usbdi modules with NetBSD in order to obtain the following improvements: o New USBD_NO_TSLEEP flag can be used in place of UQ_NO_TSLEEP quirk. This allows drivers to specify busy waiting only for certain transfers (namely control transfers for reading/writing registers and stuff). o New USBD_FORCE_SHORT_XFER flag can be used to deal with devices like the ADMtek Pegasus that sense the end of bulk OUT transfers in a special way (if a transfer is exactly a multiple of 64 bytes in size, you need to send an extra empty packet to terminate the transfer). o usbd_open_pipe_intr() now accepts an interval argument which can be used to change the rate at which the interrupt callback routine is invoked. Specifying USBD_DEFAULT_INTERVAL uses the value specified in the device's config data, but drivers can override it if needed. - Change if_aue to use USBD_FORCE_SHORT_XFER for packet transmissions. - Change if_aue, if_kue and if_cue to use USBD_NO_TSLEEP for all control transfers. We no longer force the non-tsleep hack for bulk transfers since these are done asynchronously anyway. - Removed quirk entry fiddling from if_aue and if_kue since we don't need it anymore now that we have the USBD_NO_TSLEEP flag. - Tweak ulpt, uhid, ums and ukbd drivers to use the new arg to usbd_open_pipe_intr(). - Add a flag to the softc struct in the ethernet drivers to indicate when a device has been detached, and use this flag to perform tests to prevent the drivers from trying to do control transfers if this is the case. This is necessary because calling if_detach() with INET6 enabled will eventually result in a call to the driver's ioctl() routine to delete the multicast groups on the interface, which will result in attempts to perform control transfers. (It's possible this also happens even without INET6 support enabled.) This is pointless since we know that if the detach method has been called, the hardware has been unplugged. - Changed watchdog timeout routines to just call the driver init routines to initialize the device states without trying to close and re-open the pipes. This is partly because we don't want to frob things at interrupt context, but also because this doesn't seem to work right and I don't want to panic the system just because a USB device may have stopped responding. - Fix aue_rxeof() to be a little smarter about detecting when a double transfer is needed. Unfortunately, the design of the chip makes it hard to get this exactly right. Hopefully, this will go away once either Nick or Lennart finds the bug in the uhci driver that makes this ugly hack necessary. - Also sync usbdevs with NetBSD.
Diffstat (limited to 'sys/dev/usb/ohci.c')
-rw-r--r--sys/dev/usb/ohci.c443
1 files changed, 381 insertions, 62 deletions
diff --git a/sys/dev/usb/ohci.c b/sys/dev/usb/ohci.c
index 3bbc809..c5a2d81 100644
--- a/sys/dev/usb/ohci.c
+++ b/sys/dev/usb/ohci.c
@@ -1,4 +1,4 @@
-/* $NetBSD: ohci.c,v 1.55 1999/11/18 23:32:26 augustss Exp $ */
+/* $NetBSD: ohci.c,v 1.64 2000/01/19 00:23:58 augustss Exp $ */
/* $FreeBSD$ */
/*
@@ -115,15 +115,21 @@ static void ohci_free_sed __P((ohci_softc_t *, ohci_soft_ed_t *));
static ohci_soft_td_t *ohci_alloc_std __P((ohci_softc_t *));
static void ohci_free_std __P((ohci_softc_t *, ohci_soft_td_t *));
+static ohci_soft_itd_t *ohci_alloc_sitd __P((ohci_softc_t *));
+static void ohci_free_sitd __P((ohci_softc_t *,ohci_soft_itd_t *));
+
#if 0
static void ohci_free_std_chain __P((ohci_softc_t *,
ohci_soft_td_t *, ohci_soft_td_t *));
#endif
static usbd_status ohci_alloc_std_chain __P((struct ohci_pipe *,
- ohci_softc_t *, int, int, int, usb_dma_t *,
+ ohci_softc_t *, int, int, u_int16_t, usb_dma_t *,
ohci_soft_td_t *, ohci_soft_td_t **));
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+static void ohci_shutdown __P((void *v));
static void ohci_power __P((int, void *));
+#endif
static usbd_status ohci_open __P((usbd_pipe_handle));
static void ohci_poll __P((struct usbd_bus *));
static void ohci_waitintr __P((ohci_softc_t *,
@@ -142,10 +148,16 @@ static void ohci_hash_rem_td __P((ohci_softc_t *,
static ohci_soft_td_t *ohci_hash_find_td __P((ohci_softc_t *,
ohci_physaddr_t));
+static usbd_status ohci_setup_isoc __P((usbd_pipe_handle pipe));
+static void ohci_device_isoc_enter __P((usbd_xfer_handle));
+
static usbd_status ohci_allocm __P((struct usbd_bus *, usb_dma_t *,
u_int32_t));
static void ohci_freem __P((struct usbd_bus *, usb_dma_t *));
+static usbd_xfer_handle ohci_allocx __P((struct usbd_bus *));
+static void ohci_freex __P((struct usbd_bus *, usbd_xfer_handle));
+
static usbd_status ohci_root_ctrl_transfer __P((usbd_xfer_handle));
static usbd_status ohci_root_ctrl_start __P((usbd_xfer_handle));
static void ohci_root_ctrl_abort __P((usbd_xfer_handle));
@@ -175,13 +187,11 @@ static void ohci_device_intr_abort __P((usbd_xfer_handle));
static void ohci_device_intr_close __P((usbd_pipe_handle));
static void ohci_device_intr_done __P((usbd_xfer_handle));
-#if 0
static usbd_status ohci_device_isoc_transfer __P((usbd_xfer_handle));
static usbd_status ohci_device_isoc_start __P((usbd_xfer_handle));
static void ohci_device_isoc_abort __P((usbd_xfer_handle));
static void ohci_device_isoc_close __P((usbd_pipe_handle));
static void ohci_device_isoc_done __P((usbd_xfer_handle));
-#endif
static usbd_status ohci_device_setintr __P((ohci_softc_t *sc,
struct ohci_pipe *pipe, int ival));
@@ -221,7 +231,10 @@ static u_int8_t revbits[OHCI_NO_INTRS] =
struct ohci_pipe {
struct usbd_pipe pipe;
ohci_soft_ed_t *sed;
- ohci_soft_td_t *tail;
+ union {
+ ohci_soft_td_t *td;
+ ohci_soft_itd_t *itd;
+ } tail;
/* Info needed for different pipe kinds. */
union {
/* Control pipe */
@@ -242,7 +255,7 @@ struct ohci_pipe {
} bulk;
/* Iso pipe */
struct iso {
- int xxxxx;
+ int next, inuse;
} iso;
} u;
};
@@ -254,6 +267,8 @@ static struct usbd_bus_methods ohci_bus_methods = {
ohci_poll,
ohci_allocm,
ohci_freem,
+ ohci_allocx,
+ ohci_freex,
};
static struct usbd_pipe_methods ohci_root_ctrl_methods = {
@@ -301,7 +316,6 @@ static struct usbd_pipe_methods ohci_device_bulk_methods = {
ohci_device_bulk_done,
};
-#if 0
static struct usbd_pipe_methods ohci_device_isoc_methods = {
ohci_device_isoc_transfer,
ohci_device_isoc_start,
@@ -310,7 +324,6 @@ static struct usbd_pipe_methods ohci_device_isoc_methods = {
ohci_noop,
ohci_device_isoc_done,
};
-#endif
#if defined(__NetBSD__) || defined(__OpenBSD__)
int
@@ -348,6 +361,9 @@ ohci_detach(sc, flags)
return (rv);
powerhook_disestablish(sc->sc_powerhook);
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ shutdownhook_disestablish(sc->sc_shutdownhook);
+#endif
/* free data structures XXX */
return (rv);
@@ -433,28 +449,32 @@ ohci_free_std(sc, std)
}
usbd_status
-ohci_alloc_std_chain(upipe, sc, len, rd, shortok, dma, sp, ep)
- struct ohci_pipe *upipe;
+ohci_alloc_std_chain(opipe, sc, len, rd, flags, dma, sp, ep)
+ struct ohci_pipe *opipe;
ohci_softc_t *sc;
- int len, rd, shortok;
+ int len, rd;
+ u_int16_t flags;
usb_dma_t *dma;
ohci_soft_td_t *sp, **ep;
{
ohci_soft_td_t *next, *cur;
ohci_physaddr_t dataphys, dataphysend;
- u_int32_t intr;
+ u_int32_t intr, tdflags;
int curlen;
DPRINTFN(len < 4096,("ohci_alloc_std_chain: start len=%d\n", len));
cur = sp;
dataphys = DMAADDR(dma);
dataphysend = OHCI_PAGE(dataphys + len - 1);
+ tdflags =
+ (rd ? OHCI_TD_IN : OHCI_TD_OUT) |
+ OHCI_TD_NOCC | OHCI_TD_TOGGLE_CARRY |
+ (flags & USBD_SHORT_XFER_OK ? OHCI_TD_R : 0);
+
for (;;) {
next = ohci_alloc_std(sc);
- if (next == 0) {
- /* XXX free chain */
- return (USBD_NOMEM);
- }
+ if (next == 0)
+ goto nomem;
/* The OHCI hardware can handle at most one page crossing. */
if (OHCI_PAGE(dataphys) == dataphysend ||
@@ -473,10 +493,7 @@ ohci_alloc_std_chain(upipe, sc, len, rd, shortok, dma, sp, ep)
len -= curlen;
intr = len == 0 ? OHCI_TD_SET_DI(1) : OHCI_TD_NOINTR;
- cur->td.td_flags = LE(
- (rd ? OHCI_TD_IN : OHCI_TD_OUT) | OHCI_TD_NOCC |
- intr | OHCI_TD_TOGGLE_CARRY |
- (shortok ? OHCI_TD_R : 0));
+ cur->td.td_flags = LE(tdflags | intr);
cur->td.td_cbp = LE(dataphys);
cur->nexttd = next;
cur->td.td_nexttd = LE(next->physaddr);
@@ -491,10 +508,31 @@ ohci_alloc_std_chain(upipe, sc, len, rd, shortok, dma, sp, ep)
dataphys += curlen;
cur = next;
}
+ if ((flags & USBD_FORCE_SHORT_XFER) &&
+ len % UGETW(opipe->pipe.endpoint->edesc->wMaxPacketSize) == 0) {
+ /* Force a 0 length transfer at the end. */
+ next = ohci_alloc_std(sc);
+ if (next == 0)
+ goto nomem;
+
+ cur->td.td_flags = LE(tdflags | OHCI_TD_SET_DI(1));
+ cur->td.td_cbp = 0; /* indicate 0 length packet */
+ cur->nexttd = next;
+ cur->td.td_nexttd = LE(next->physaddr);
+ cur->td.td_be = LE(dataphys - 1);
+ cur->len = 0;
+ cur->flags = 0;
+ cur = next;
+ DPRINTFN(2,("ohci_alloc_std_chain: add 0 xfer\n"));
+ }
cur->flags = OHCI_CALL_DONE | OHCI_ADD_LEN;
*ep = next;
return (USBD_NORMAL_COMPLETION);
+
+ nomem:
+ /* XXX free chain */
+ return (USBD_NOMEM);
}
#if 0
@@ -513,15 +551,53 @@ ohci_free_std_chain(sc, std, stdend)
}
#endif
+ohci_soft_itd_t *
+ohci_alloc_sitd(sc)
+ ohci_softc_t *sc;
+{
+ ohci_soft_itd_t *sitd;
+ usbd_status err;
+ int i, offs;
+ usb_dma_t dma;
+
+ if (sc->sc_freeitds == NULL) {
+ DPRINTFN(2, ("ohci_alloc_sitd: allocating chunk\n"));
+ err = usb_allocmem(&sc->sc_bus, OHCI_STD_SIZE * OHCI_STD_CHUNK,
+ OHCI_TD_ALIGN, &dma);
+ if (err)
+ return (0);
+ for(i = 0; i < OHCI_STD_CHUNK; i++) {
+ offs = i * OHCI_STD_SIZE;
+ sitd = (ohci_soft_itd_t *)((char*)KERNADDR(&dma)+offs);
+ sitd->physaddr = DMAADDR(&dma) + offs;
+ sitd->nextitd = sc->sc_freeitds;
+ sc->sc_freeitds = sitd;
+ }
+ }
+ sitd = sc->sc_freeitds;
+ sc->sc_freeitds = sitd->nextitd;
+ memset(&sitd->itd, 0, sizeof(ohci_itd_t));
+ sitd->nextitd = 0;
+ return (sitd);
+}
+
+void
+ohci_free_sitd(sc, sitd)
+ ohci_softc_t *sc;
+ ohci_soft_itd_t *sitd;
+{
+ sitd->nextitd = sc->sc_freeitds;
+ sc->sc_freeitds = sitd;
+}
+
usbd_status
ohci_init(sc)
ohci_softc_t *sc;
{
ohci_soft_ed_t *sed, *psed;
usbd_status err;
- int rev;
int i;
- u_int32_t s, ctl, ival, hcr, fm, per;
+ u_int32_t s, ctl, ival, hcr, fm, per, rev;
DPRINTF(("ohci_init: start\n"));
#if defined(__OpenBSD__)
@@ -544,6 +620,8 @@ ohci_init(sc)
for (i = 0; i < OHCI_HASH_SIZE; i++)
LIST_INIT(&sc->sc_hash_tds[i]);
+ SIMPLEQ_INIT(&sc->sc_free_xfers);
+
/* Allocate the HCCA area. */
err = usb_allocmem(&sc->sc_bus, OHCI_HCCA_SIZE,
OHCI_HCCA_ALIGN, &sc->sc_hccadma);
@@ -554,6 +632,7 @@ ohci_init(sc)
sc->sc_eintrs = OHCI_NORMAL_INTRS;
+ /* Allocate dummy ED that starts the control list. */
sc->sc_ctrl_head = ohci_alloc_sed(sc);
if (sc->sc_ctrl_head == NULL) {
err = USBD_NOMEM;
@@ -561,6 +640,7 @@ ohci_init(sc)
}
sc->sc_ctrl_head->ed.ed_flags |= LE(OHCI_ED_SKIP);
+ /* Allocate dummy ED that starts the bulk list. */
sc->sc_bulk_head = ohci_alloc_sed(sc);
if (sc->sc_bulk_head == NULL) {
err = USBD_NOMEM;
@@ -568,6 +648,14 @@ ohci_init(sc)
}
sc->sc_bulk_head->ed.ed_flags |= LE(OHCI_ED_SKIP);
+ /* Allocate dummy ED that starts the isochronous list. */
+ sc->sc_isoc_head = ohci_alloc_sed(sc);
+ if (sc->sc_isoc_head == NULL) {
+ err = USBD_NOMEM;
+ goto bad3;
+ }
+ sc->sc_isoc_head->ed.ed_flags |= LE(OHCI_ED_SKIP);
+
/* Allocate all the dummy EDs that make up the interrupt tree. */
for (i = 0; i < OHCI_NO_EDS; i++) {
sed = ohci_alloc_sed(sc);
@@ -575,16 +663,17 @@ ohci_init(sc)
while (--i >= 0)
ohci_free_sed(sc, sc->sc_eds[i]);
err = USBD_NOMEM;
- goto bad3;
+ goto bad4;
}
/* All ED fields are set to 0. */
sc->sc_eds[i] = sed;
sed->ed.ed_flags |= LE(OHCI_ED_SKIP);
- if (i != 0) {
+ if (i != 0)
psed = sc->sc_eds[(i-1) / 2];
- sed->next = psed;
- sed->ed.ed_nexted = LE(psed->physaddr);
- }
+ else
+ psed= sc->sc_isoc_head;
+ sed->next = psed;
+ sed->ed.ed_nexted = LE(psed->physaddr);
}
/*
* Fill HCCA interrupt table. The bit reversal is to get
@@ -647,14 +736,14 @@ ohci_init(sc)
if (hcr) {
printf("%s: reset timeout\n", USBDEVNAME(sc->sc_bus.bdev));
err = USBD_IOERROR;
- goto bad3;
+ goto bad5;
}
#ifdef OHCI_DEBUG
if (ohcidebug > 15)
ohci_dumpregs(sc);
#endif
- /* The controller is now in suspend state, we have 2ms to finish. */
+ /* The controller is now in SUSPEND state, we have 2ms to finish. */
/* Set up HC registers. */
OWRITE4(sc, OHCI_HCCA, DMAADDR(&sc->sc_hccadma));
@@ -696,9 +785,16 @@ ohci_init(sc)
sc->sc_bus.pipe_size = sizeof(struct ohci_pipe);
sc->sc_powerhook = powerhook_establish(ohci_power, sc);
-
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ sc->sc_shutdownhook = shutdownhook_establish(ohci_shutdown, sc);
+#endif
return (USBD_NORMAL_COMPLETION);
+ bad5:
+ for (i = 0; i < OHCI_NO_EDS; i++)
+ ohci_free_sed(sc, sc->sc_eds[i]);
+ bad4:
+ ohci_free_sed(sc, sc->sc_isoc_head);
bad3:
ohci_free_sed(sc, sc->sc_ctrl_head);
bad2:
@@ -733,7 +829,54 @@ ohci_freem(bus, dma)
usb_freemem(&sc->sc_bus, dma);
}
-#if defined(__NetBSD__)
+usbd_xfer_handle
+ohci_allocx(bus)
+ struct usbd_bus *bus;
+{
+ struct ohci_softc *sc = (struct ohci_softc *)bus;
+ usbd_xfer_handle xfer;
+
+ xfer = SIMPLEQ_FIRST(&sc->sc_free_xfers);
+ if (xfer != NULL)
+ SIMPLEQ_REMOVE_HEAD(&sc->sc_free_xfers, xfer, next);
+ else
+ xfer = malloc(sizeof(*xfer), M_USB, M_NOWAIT);
+ if (xfer != NULL)
+ memset(xfer, 0, sizeof *xfer);
+ return (xfer);
+}
+
+void
+ohci_freex(bus, xfer)
+ struct usbd_bus *bus;
+ usbd_xfer_handle xfer;
+{
+ struct ohci_softc *sc = (struct ohci_softc *)bus;
+
+ SIMPLEQ_INSERT_HEAD(&sc->sc_free_xfers, xfer, next);
+}
+
+/*
+ * Shut down the controller when the system is going down.
+ */
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+void
+ohci_shutdown(v)
+ void *v;
+{
+ ohci_softc_t *sc = v;
+
+ DPRINTF(("ohci_shutdown: stopping the HC\n"));
+ OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET);
+}
+
+/*
+ * Handle suspend/resume.
+ *
+ * We need to switch to polling mode here, because this routine is
+ * called from an intterupt context. This is all right since we
+ * are almost suspended anyway.
+ */
void
ohci_power(why, v)
int why;
@@ -747,7 +890,7 @@ ohci_power(why, v)
ohci_dumpregs(sc);
#endif
}
-#endif /* defined(__NetBSD__) */
+#endif
#ifdef OHCI_DEBUG
void
@@ -801,8 +944,9 @@ ohci_intr(p)
/* If we get an interrupt while polling, then just ignore it. */
if (sc->sc_bus.use_polling) {
- DPRINTF(("%s: Ignored interrupt while polling",
- USBDEVNAME(sc->sc_bus.bdev)));
+#ifdef DIAGNOSTIC
+ printf("ohci_intr: ignored interrupt while polling\n");
+#endif
return (0);
}
@@ -948,12 +1092,12 @@ ohci_process_done(sc, done)
#endif
for (std = sdone; std; std = stdnext) {
- stdnext = std->dnext;
xfer = std->xfer;
- usb_untimeout(ohci_timeout, xfer, xfer->timo_handle);
+ stdnext = std->dnext;
DPRINTFN(10, ("ohci_process_done: std=%p xfer=%p hcpriv=%p\n",
std, xfer, xfer->hcpriv));
cc = OHCI_TD_GET_CC(LE(std->td.td_flags));
+ usb_untimeout(ohci_timeout, xfer, xfer->timo_handle);
if (xfer->status == USBD_CANCELLED ||
xfer->status == USBD_TIMEOUT) {
DPRINTF(("ohci_process_done: cancel/timeout %p\n",
@@ -1036,7 +1180,7 @@ ohci_device_intr_done(xfer)
xfer->hcpriv = NULL;
if (xfer->pipe->repeat) {
- data = opipe->tail;
+ data = opipe->tail.td;
tail = ohci_alloc_std(sc); /* XXX should reuse TD */
if (tail == NULL) {
xfer->status = USBD_NOMEM;
@@ -1061,7 +1205,7 @@ ohci_device_intr_done(xfer)
ohci_hash_add_td(sc, data);
sed->ed.ed_tailp = LE(tail->physaddr);
- opipe->tail = tail;
+ opipe->tail.td = tail;
}
}
@@ -1191,7 +1335,7 @@ ohci_device_request(xfer)
UGETW(req->wIndex), len, addr,
opipe->pipe.endpoint->edesc->bEndpointAddress));
- setup = opipe->tail;
+ setup = opipe->tail.td;
stat = ohci_alloc_std(sc);
if (stat == NULL) {
err = USBD_NOMEM;
@@ -1244,7 +1388,7 @@ ohci_device_request(xfer)
memcpy(KERNADDR(&opipe->u.ctl.reqdma), req, sizeof *req);
setup->td.td_flags = LE(OHCI_TD_SETUP | OHCI_TD_NOCC |
- OHCI_TD_TOGGLE_0 | OHCI_TD_NOINTR);
+ OHCI_TD_TOGGLE_0 | OHCI_TD_NOINTR);
setup->td.td_cbp = LE(DMAADDR(&opipe->u.ctl.reqdma));
setup->nexttd = next;
setup->td.td_nexttd = LE(next->physaddr);
@@ -1279,7 +1423,7 @@ ohci_device_request(xfer)
ohci_hash_add_td(sc, data);
ohci_hash_add_td(sc, stat);
sed->ed.ed_tailp = LE(tail->physaddr);
- opipe->tail = tail;
+ opipe->tail.td = tail;
OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF);
if (xfer->timeout && !sc->sc_bus.use_polling) {
usb_timeout(ohci_timeout, xfer,
@@ -1289,6 +1433,7 @@ ohci_device_request(xfer)
#ifdef OHCI_DEBUG
if (ohcidebug > 5) {
+ usb_delay_ms(&sc->sc_bus, 5);
DPRINTF(("ohci_device_request: status=%x\n",
OREAD4(sc, OHCI_COMMAND_STATUS)));
ohci_dump_ed(sed);
@@ -1462,10 +1607,15 @@ ohci_open(pipe)
usb_endpoint_descriptor_t *ed = pipe->endpoint->edesc;
struct ohci_pipe *opipe = (struct ohci_pipe *)pipe;
u_int8_t addr = dev->address;
+ u_int8_t xfertype = ed->bmAttributes & UE_XFERTYPE;
ohci_soft_ed_t *sed;
- ohci_soft_td_t *std;
+ ohci_soft_td_t *std = NULL;
+ ohci_soft_itd_t *sitd;
+ ohci_physaddr_t tdphys;
+ u_int32_t fmt;
usbd_status err;
int s;
+ int ival;
DPRINTFN(1, ("ohci_open: pipe=%p, addr=%d, endpt=%d (%d)\n",
pipe, addr, ed->bEndpointAddress, sc->sc_addr));
@@ -1484,22 +1634,35 @@ ohci_open(pipe)
sed = ohci_alloc_sed(sc);
if (sed == NULL)
goto bad0;
- std = ohci_alloc_std(sc);
- if (std == NULL)
- goto bad1;
opipe->sed = sed;
- opipe->tail = std;
+ if (xfertype == UE_ISOCHRONOUS) {
+ sitd = ohci_alloc_sitd(sc);
+ if (sitd == NULL) {
+ ohci_free_sitd(sc, sitd);
+ goto bad1;
+ }
+ opipe->tail.itd = sitd;
+ tdphys = LE(sitd->physaddr);
+ fmt = OHCI_ED_FORMAT_ISO;
+ } else {
+ std = ohci_alloc_std(sc);
+ if (std == NULL) {
+ ohci_free_std(sc, std);
+ goto bad1;
+ }
+ opipe->tail.td = std;
+ tdphys = LE(std->physaddr);
+ fmt = OHCI_ED_FORMAT_GEN;
+ }
sed->ed.ed_flags = LE(
OHCI_ED_SET_FA(addr) |
OHCI_ED_SET_EN(ed->bEndpointAddress) |
OHCI_ED_DIR_TD |
- (dev->lowspeed ? OHCI_ED_SPEED : 0) |
- ((ed->bmAttributes & UE_XFERTYPE) == UE_ISOCHRONOUS ?
- OHCI_ED_FORMAT_ISO : OHCI_ED_FORMAT_GEN) |
+ (dev->lowspeed ? OHCI_ED_SPEED : 0) | fmt |
OHCI_ED_SET_MAXP(UGETW(ed->wMaxPacketSize)));
- sed->ed.ed_headp = sed->ed.ed_tailp = LE(std->physaddr);
+ sed->ed.ed_headp = sed->ed.ed_tailp = tdphys;
- switch (ed->bmAttributes & UE_XFERTYPE) {
+ switch (xfertype) {
case UE_CONTROL:
pipe->methods = &ohci_device_ctrl_methods;
err = usb_allocmem(&sc->sc_bus,
@@ -1513,10 +1676,13 @@ ohci_open(pipe)
break;
case UE_INTERRUPT:
pipe->methods = &ohci_device_intr_methods;
- return (ohci_device_setintr(sc, opipe, ed->bInterval));
+ ival = pipe->interval;
+ if (ival == USBD_DEFAULT_INTERVAL)
+ ival = ed->bInterval;
+ return (ohci_device_setintr(sc, opipe, ival));
case UE_ISOCHRONOUS:
- printf("ohci_open: open iso unimplemented\n");
- return (USBD_INVAL);
+ pipe->methods = &ohci_device_isoc_methods;
+ return (ohci_setup_isoc(pipe));
case UE_BULK:
pipe->methods = &ohci_device_bulk_methods;
s = splusb();
@@ -1574,7 +1740,6 @@ ohci_close_pipe(pipe, head)
#endif
ohci_rem_ed(sed, head);
splx(s);
- ohci_free_std(sc, opipe->tail);
ohci_free_sed(sc, opipe->sed);
}
@@ -1606,6 +1771,7 @@ ohci_abort_xfer(xfer, status)
DPRINTFN(1,("ohci_abort_xfer: stop ed=%p\n", sed));
sed->ed.ed_flags |= LE(OHCI_ED_SKIP); /* force hardware skip */
+#if 1
if (xfer->device->bus->intr_context) {
/* We have no process context, so we can't use tsleep(). */
timeout(ohci_abort_xfer_end, xfer, hz / USB_FRAMES_PER_SECOND);
@@ -1617,6 +1783,10 @@ ohci_abort_xfer(xfer, status)
usb_delay_ms(opipe->pipe.device->bus, 1);
ohci_abort_xfer_end(xfer);
}
+#else
+ delay(1000);
+ ohci_abort_xfer_end(xfer);
+#endif
}
void
@@ -2181,10 +2351,12 @@ static void
ohci_device_ctrl_close(pipe)
usbd_pipe_handle pipe;
{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)pipe;
ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus;
DPRINTF(("ohci_device_ctrl_close: pipe=%p\n", pipe));
ohci_close_pipe(pipe, sc->sc_ctrl_head);
+ ohci_free_std(sc, opipe->tail.td);
}
/************************/
@@ -2258,9 +2430,8 @@ ohci_device_bulk_start(xfer)
OHCI_ED_SET_FA(addr));
/* Allocate a chain of new TDs (including a new tail). */
- data = opipe->tail;
- err = ohci_alloc_std_chain(opipe, sc, len, isread,
- xfer->flags & USBD_SHORT_XFER_OK,
+ data = opipe->tail.td;
+ err = ohci_alloc_std_chain(opipe, sc, len, isread, xfer->flags,
&xfer->dmabuf, data, &tail);
if (err)
return (err);
@@ -2287,7 +2458,7 @@ ohci_device_bulk_start(xfer)
ohci_hash_add_td(sc, tdp);
}
sed->ed.ed_tailp = LE(tail->physaddr);
- opipe->tail = tail;
+ opipe->tail.td = tail;
sed->ed.ed_flags &= LE(~OHCI_ED_SKIP);
OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF);
if (xfer->timeout && !sc->sc_bus.use_polling) {
@@ -2326,10 +2497,12 @@ static void
ohci_device_bulk_close(pipe)
usbd_pipe_handle pipe;
{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)pipe;
ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus;
DPRINTF(("ohci_device_bulk_close: pipe=%p\n", pipe));
ohci_close_pipe(pipe, sc->sc_bulk_head);
+ ohci_free_std(sc, opipe->tail.td);
}
/************************/
@@ -2372,7 +2545,7 @@ ohci_device_intr_start(xfer)
len = xfer->length;
- data = opipe->tail;
+ data = opipe->tail.td;
tail = ohci_alloc_std(sc);
if (tail == NULL)
return (USBD_NOMEM);
@@ -2404,7 +2577,7 @@ ohci_device_intr_start(xfer)
s = splusb();
ohci_hash_add_td(sc, data);
sed->ed.ed_tailp = LE(tail->physaddr);
- opipe->tail = tail;
+ opipe->tail.td = tail;
sed->ed.ed_flags &= LE(~OHCI_ED_SKIP);
#if 0
@@ -2472,7 +2645,7 @@ ohci_device_intr_close(pipe)
for (j = 0; j < nslots; j++)
--sc->sc_bws[(pos * nslots + j) % OHCI_NO_INTRS];
- ohci_free_std(sc, opipe->tail);
+ ohci_free_std(sc, opipe->tail.td);
ohci_free_sed(sc, opipe->sed);
}
@@ -2540,3 +2713,149 @@ ohci_device_setintr(sc, opipe, ival)
DPRINTFN(5, ("ohci_setintr: returns %p\n", opipe));
return (USBD_NORMAL_COMPLETION);
}
+
+/***********************/
+
+usbd_status
+ohci_device_isoc_transfer(xfer)
+ usbd_xfer_handle xfer;
+{
+ usbd_status err;
+
+ DPRINTFN(5,("ohci_device_isoc_transfer: xfer=%p\n", xfer));
+
+ /* Put it on our queue, */
+ err = usb_insert_transfer(xfer);
+
+ /* bail out on error, */
+ if (err && err != USBD_IN_PROGRESS)
+ return (err);
+
+ /* XXX should check inuse here */
+
+ /* insert into schedule, */
+ ohci_device_isoc_enter(xfer);
+
+ /* and put on interrupt list if the pipe wasn't running */
+ if (!err)
+ ohci_device_isoc_start(SIMPLEQ_FIRST(&xfer->pipe->queue));
+
+ return (err);
+}
+
+void
+ohci_device_isoc_enter(xfer)
+ usbd_xfer_handle xfer;
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
+ usbd_device_handle dev = opipe->pipe.device;
+ ohci_softc_t *sc = (ohci_softc_t *)dev->bus;
+ ohci_soft_ed_t *sed = opipe->sed;
+ struct iso *iso = &opipe->u.iso;
+ ohci_soft_itd_t *sitd, *nsitd;
+ ohci_physaddr_t buf, offs;
+ int i, ncur, nframes;
+ int ncross = 0;
+ int s;
+
+ s = splusb();
+ sitd = opipe->tail.itd;
+ buf = DMAADDR(&xfer->dmabuf);
+ sitd->itd.itd_bp0 = LE(buf & OHCI_ITD_PAGE_MASK);
+ nframes = xfer->nframes;
+ offs = buf & OHCI_ITD_OFFSET_MASK;
+ for (i = ncur = 0; i < nframes; i++, ncur++) {
+ if (ncur == OHCI_ITD_NOFFSET || /* all offsets used */
+ ncross > 1) { /* too many page crossings */
+
+ nsitd = ohci_alloc_sitd(sc);
+ if (nsitd == NULL) {
+ /* XXX what now? */
+ return;
+ }
+ sitd->nextitd = nsitd;
+ sitd->itd.itd_nextitd = LE(nsitd->physaddr);
+ sitd->itd.itd_flags = LE(
+ OHCI_ITD_NOCC |
+ OHCI_ITD_SET_SF(iso->next) |
+ OHCI_ITD_NOINTR |
+ OHCI_ITD_SET_FC(OHCI_ITD_NOFFSET));
+ sitd->itd.itd_be = LE(LE(sitd->itd.itd_bp0) + offs - 1);
+ nsitd->itd.itd_bp0 = LE((buf + offs) & OHCI_ITD_PAGE_MASK);
+ sitd = nsitd;
+ iso->next = iso->next + ncur;
+ ncur = 0;
+ ncross = 0;
+ }
+ /* XXX byte order */
+ sitd->itd.itd_offset[i] =
+ offs | (ncross == 1 ? OHCI_ITD_PAGE_SELECT : 0);
+ offs += xfer->frlengths[i];
+ /* XXX update ncross */
+ }
+ nsitd = ohci_alloc_sitd(sc);
+ if (nsitd == NULL) {
+ /* XXX what now? */
+ return;
+ }
+ sitd->nextitd = nsitd;
+ sitd->itd.itd_nextitd = LE(nsitd->physaddr);
+ sitd->itd.itd_flags = LE(
+ OHCI_ITD_NOCC |
+ OHCI_ITD_SET_SF(iso->next) |
+ OHCI_ITD_SET_DI(0) |
+ OHCI_ITD_SET_FC(ncur));
+ sitd->itd.itd_be = LE(LE(sitd->itd.itd_bp0) + offs - 1);
+ iso->next = iso->next + ncur;
+
+ opipe->tail.itd = nsitd;
+ sed->ed.ed_tailp = LE(nsitd->physaddr);
+ /* XXX update ED */
+ splx(s);
+}
+
+usbd_status
+ohci_device_isoc_start(xfer)
+ usbd_xfer_handle xfer;
+{
+ printf("ohci_device_isoc_start: not implemented\n");
+ return (USBD_INVAL);
+}
+
+void
+ohci_device_isoc_abort(xfer)
+ usbd_xfer_handle xfer;
+{
+}
+
+void
+ohci_device_isoc_done(xfer)
+ usbd_xfer_handle xfer;
+{
+ printf("ohci_device_isoc_done: not implemented\n");
+}
+
+usbd_status
+ohci_setup_isoc(pipe)
+ usbd_pipe_handle pipe;
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)pipe;
+ struct iso *iso = &opipe->u.iso;
+
+ iso->next = -1;
+ iso->inuse = 0;
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+void
+ohci_device_isoc_close(pipe)
+ usbd_pipe_handle pipe;
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus;
+
+ DPRINTF(("ohci_device_isoc_close: pipe=%p\n", pipe));
+ ohci_close_pipe(pipe, sc->sc_isoc_head);
+ ohci_free_sitd(sc, opipe->tail.itd);
+}
OpenPOWER on IntegriCloud