Implement bulk transfer for OHCI.

This commit is contained in:
augustss 1998-07-23 13:41:04 +00:00
parent 217addc8de
commit 8cdf268a45
1 changed files with 188 additions and 21 deletions

View File

@ -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;