From 8cdf268a454e8ac4bb79a6f31fd62a5de0b5a1c7 Mon Sep 17 00:00:00 2001 From: augustss Date: Thu, 23 Jul 1998 13:41:04 +0000 Subject: [PATCH] Implement bulk transfer for OHCI. --- sys/dev/usb/ohci.c | 209 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 188 insertions(+), 21 deletions(-) diff --git a/sys/dev/usb/ohci.c b/sys/dev/usb/ohci.c index 4cddc37d20de..1b029d6ec30c 100644 --- a/sys/dev/usb/ohci.c +++ b/sys/dev/usb/ohci.c @@ -1,4 +1,4 @@ -/* $NetBSD: ohci.c,v 1.2 1998/07/23 01:46:27 augustss Exp $ */ +/* $NetBSD: ohci.c,v 1.3 1998/07/23 13:41:04 augustss Exp $ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -80,13 +80,14 @@ void ohci_rhsc __P((ohci_softc_t *, usbd_request_handle)); void ohci_process_done __P((ohci_softc_t *, ohci_physaddr_t)); void ohci_ctrl_done __P((ohci_softc_t *, usbd_request_handle)); void ohci_intr_done __P((ohci_softc_t *, usbd_request_handle)); +void ohci_bulk_done __P((ohci_softc_t *, usbd_request_handle)); usbd_status ohci_allocmem __P((ohci_softc_t *,size_t,size_t, ohci_dma_t*)); void ohci_freemem __P((ohci_softc_t *, ohci_dma_t *)); usbd_status ohci_device_request __P((usbd_request_handle reqh)); -void ohci_add_ed __P((ohci_softc_t *, ohci_soft_ed_t *, - ohci_soft_ed_t *)); +void ohci_add_ed __P((ohci_soft_ed_t *, ohci_soft_ed_t *)); +void ohci_rem_ed __P((ohci_soft_ed_t *, ohci_soft_ed_t *)); void ohci_hash_add_td __P((ohci_softc_t *, ohci_soft_td_t *)); void ohci_hash_rem_td __P((ohci_softc_t *, ohci_soft_td_t *)); ohci_soft_td_t *ohci_hash_find_td __P((ohci_softc_t *, ohci_physaddr_t)); @@ -103,6 +104,10 @@ usbd_status ohci_device_ctrl_transfer __P((usbd_request_handle)); void ohci_device_ctrl_abort __P((usbd_request_handle)); void ohci_device_ctrl_close __P((usbd_pipe_handle)); +usbd_status ohci_device_bulk_transfer __P((usbd_request_handle)); +void ohci_device_bulk_abort __P((usbd_request_handle)); +void ohci_device_bulk_close __P((usbd_pipe_handle)); + usbd_status ohci_device_intr_transfer __P((usbd_request_handle)); void ohci_device_intr_abort __P((usbd_request_handle)); void ohci_device_intr_close __P((usbd_pipe_handle)); @@ -152,6 +157,11 @@ struct ohci_pipe { int nslots; int pos; } intr; + /* Bulk pipe */ + struct { + ohci_dma_t datadma; + u_int length; + } bulk; } u; }; @@ -181,6 +191,12 @@ struct usbd_methods ohci_device_intr_methods = { ohci_device_intr_close, }; +struct usbd_methods ohci_device_bulk_methods = { + ohci_device_bulk_transfer, + ohci_device_bulk_abort, + ohci_device_bulk_close, +}; + usbd_status ohci_allocmem(sc, size, align, p) ohci_softc_t *sc; @@ -673,7 +689,7 @@ ohci_process_done(sc, done) ohci_intr_done(sc, reqh); break; case UE_BULK: - printf("ohci_process_done: BULK done?\n"); + ohci_bulk_done(sc, reqh); break; case UE_ISOCHRONOUS: printf("ohci_process_done: ISO done?\n"); @@ -783,6 +799,24 @@ ohci_intr_done(sc, reqh) } } +void +ohci_bulk_done(sc, reqh) + ohci_softc_t *sc; + usbd_request_handle reqh; +{ + struct ohci_pipe *opipe = (struct ohci_pipe *)reqh->pipe; + ohci_dma_t *dma; + + + DPRINTFN(10,("ohci_bulk_done: reqh=%p, actlen=%d\n", + reqh, reqh->actlen)); + + dma = &opipe->u.bulk.datadma; + if (reqh->request.bmRequestType & UT_READ) + memcpy(reqh->buffer, KERNADDR(dma), reqh->actlen); + ohci_freemem(sc, dma); +} + void ohci_rhsc(sc, reqh) ohci_softc_t *sc; @@ -1002,8 +1036,7 @@ ohci_device_request(reqh) * Add an ED to the schedule. Called at splusb(). */ void -ohci_add_ed(sc, sed, head) - ohci_softc_t *sc; +ohci_add_ed(sed, head) ohci_soft_ed_t *sed; ohci_soft_ed_t *head; { @@ -1013,6 +1046,25 @@ ohci_add_ed(sc, sed, head) head->ed->ed_nexted = sed->physaddr; } +/* + * Remove an ED from the schedule. Called at splusb(). + */ +void +ohci_rem_ed(sed, head) + ohci_soft_ed_t *sed; + ohci_soft_ed_t *head; +{ + ohci_soft_ed_t *p; + + /* XXX */ + for (p = head; p && p->next != sed; p = p->next) + ; + if (!p) + panic("ohci_rem_ed: ED not found\n"); + p->next = sed->next; + p->ed->ed_nexted = sed->ed->ed_nexted; +} + /* * When a transfer is completed the TD is added to the done queue by * the host controller. This queue is the processed by software. @@ -1171,7 +1223,7 @@ ohci_open(pipe) if (r != USBD_NORMAL_COMPLETION) goto bad; s = splusb(); - ohci_add_ed(sc, sed, sc->sc_ctrl_head); + ohci_add_ed(sed, sc->sc_ctrl_head); splx(s); break; case UE_INTERRUPT: @@ -1181,8 +1233,11 @@ ohci_open(pipe) printf("ohci_open: open iso unimplemented\n"); return (USBD_XXX); case UE_BULK: - printf("ohci_open: open bulk unimplemented\n"); - return (USBD_XXX); + pipe->methods = &ohci_device_bulk_methods; + s = splusb(); + ohci_add_ed(sed, sc->sc_bulk_head); + splx(s); + break; } } return (USBD_NORMAL_COMPLETION); @@ -1581,6 +1636,7 @@ void ohci_root_ctrl_abort(reqh) usbd_request_handle reqh; { + /* Nothing to do, all transfers are syncronous. */ } /* Close the root pipe. */ @@ -1615,11 +1671,12 @@ ohci_root_intr_transfer(reqh) return (USBD_IN_PROGRESS); } -/* Abort a root control request. */ +/* Abort a root interrupt request. */ void ohci_root_intr_abort(reqh) usbd_request_handle reqh; { + /* No need to abort. */ } /* Close the root pipe. */ @@ -1661,8 +1718,9 @@ void ohci_device_ctrl_abort(reqh) usbd_request_handle reqh; { - /* XXX */ - usbd_delay_ms(2); /* make sure it is finished */ + /* XXX inactivate */ + usbd_delay_ms(1); /* make sure it is finished */ + /* XXX call done */ } /* Close a device control pipe. */ @@ -1672,19 +1730,128 @@ ohci_device_ctrl_close(pipe) { struct ohci_pipe *opipe = (struct ohci_pipe *)pipe; ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus; - ohci_soft_ed_t *p, *sed = opipe->sed; + ohci_soft_ed_t *sed = opipe->sed; int s; s = splusb(); sed->ed->ed_flags |= OHCI_ED_SKIP; if ((sed->ed->ed_tailp & OHCI_TAILMASK) != sed->ed->ed_headp) usbd_delay_ms(2); - /* XXX */ - for (p = sc->sc_ctrl_head; p && p->next != opipe->sed; p = p->next) - ; - if (!p) - panic("ohci_device_ctrl_close: ED not found\n"); - p->next = p->next->next; + ohci_rem_ed(sed, sc->sc_ctrl_head); + splx(s); + ohci_free_std(sc, opipe->tail); + ohci_free_sed(sc, opipe->sed); + /* XXX free other resources */ +} + +/************************/ + +usbd_status +ohci_device_bulk_transfer(reqh) + usbd_request_handle reqh; +{ + struct ohci_pipe *opipe = (struct ohci_pipe *)reqh->pipe; + usbd_device_handle dev = opipe->pipe.device; + ohci_softc_t *sc = (ohci_softc_t *)dev->bus; + int addr = dev->address; + ohci_soft_td_t *xfer, *tail; + ohci_soft_ed_t *sed; + ohci_dma_t *dmap; + usbd_status r; + int s, len, isread; + + if (reqh->isreq) { + /* XXX panic */ + printf("ohci_device_bulk_transfer: a request\n"); + return (USBD_INVAL); + } + + len = reqh->length; + dmap = &opipe->u.bulk.datadma; + isread = reqh->pipe->endpoint->edesc->bEndpointAddress & UE_IN; + sed = opipe->sed; + + opipe->u.bulk.length = len; + + r = ohci_allocmem(sc, len, 0, dmap); + if (r != USBD_NORMAL_COMPLETION) + goto ret1; + + tail = ohci_alloc_std(sc); + if (!tail) { + r = USBD_NOMEM; + goto ret2; + } + tail->reqh = 0; + + /* Update device address */ + sed->ed->ed_flags = + (sed->ed->ed_flags & ~OHCI_ED_ADDRMASK) | + OHCI_ED_SET_FA(addr); + + /* Set up data transaction */ + xfer = opipe->tail; + xfer->td->td_flags = + (isread ? OHCI_TD_IN : OHCI_TD_OUT) | OHCI_TD_NOCC | + OHCI_TD_SET_DI(1) | OHCI_TD_TOGGLE_CARRY; + xfer->td->td_cbp = DMAADDR(dmap); + xfer->nexttd = tail; + xfer->td->td_nexttd = tail->physaddr; + xfer->td->td_be = xfer->td->td_cbp + len - 1; + xfer->len = len; + xfer->reqh = reqh; + + reqh->actlen = 0; + reqh->hcpriv = xfer; + + if (!isread) + memcpy(KERNADDR(dmap), reqh->buffer, len); + + /* Insert ED in schedule */ + s = splusb(); + ohci_hash_add_td(sc, xfer); + sed->ed->ed_tailp = tail->physaddr; + opipe->tail = tail; + OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF); + if (reqh->timeout && !usbd_use_polling) + timeout(ohci_timeout, reqh, MS_TO_TICKS(reqh->timeout)); + splx(s); + + return (USBD_IN_PROGRESS); + + ret2: + ohci_freemem(sc, dmap); + ret1: + return (r); +} + +/* Abort a device bulk request. */ +void +ohci_device_bulk_abort(reqh) + usbd_request_handle reqh; +{ +#if 0 + sed->ed->ed_flags |= OHCI_ED_SKIP; + if ((sed->ed->ed_tailp & OHCI_TAILMASK) != sed->ed->ed_headp) + usbd_delay_ms(2); +#endif + /* XXX inactivate */ + usbd_delay_ms(1); /* make sure it is finished */ + /* XXX call done */ +} + +/* Close a device bulk pipe. */ +void +ohci_device_bulk_close(pipe) + usbd_pipe_handle pipe; +{ + struct ohci_pipe *opipe = (struct ohci_pipe *)pipe; + usbd_device_handle dev = opipe->pipe.device; + ohci_softc_t *sc = (ohci_softc_t *)dev->bus; + int s; + + s = splusb(); + ohci_rem_ed(opipe->sed, sc->sc_bulk_head); splx(s); ohci_free_std(sc, opipe->tail); ohci_free_sed(sc, opipe->sed); @@ -1787,8 +1954,8 @@ ohci_device_intr_abort(reqh) { struct uhci_pipe *opipe; - /* XXX */ - usbd_delay_ms(2); /* make sure it is finished */ + /* XXX inactivate */ + usbd_delay_ms(1); /* make sure it is finished */ if (reqh->pipe->intrreqh == reqh) { DPRINTF(("ohci_device_intr_abort: remove\n")); reqh->pipe->intrreqh = 0;