mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-17 10:26:15 +00:00
fix support for umass and related devices on ohci. This is a partial
sync of the NetBSD code. fix isochornous support for ohci. This gets webcams like my OV511 working on sparc64. PR: kern/52589 Submitted by: Bruce R. Montague (isochonous support) Reviewed by: joe among others
This commit is contained in:
parent
3004afca6e
commit
86231a1846
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=117656
@ -267,6 +267,7 @@ Static u_int8_t revbits[OHCI_NO_INTRS] =
|
||||
struct ohci_pipe {
|
||||
struct usbd_pipe pipe;
|
||||
ohci_soft_ed_t *sed;
|
||||
u_int32_t aborting;
|
||||
union {
|
||||
ohci_soft_td_t *td;
|
||||
ohci_soft_itd_t *itd;
|
||||
@ -985,6 +986,14 @@ void
|
||||
ohci_freex(struct usbd_bus *bus, usbd_xfer_handle xfer)
|
||||
{
|
||||
struct ohci_softc *sc = (struct ohci_softc *)bus;
|
||||
struct ohci_xfer *oxfer = (struct ohci_xfer *)xfer;
|
||||
ohci_soft_itd_t *sitd;
|
||||
|
||||
if (oxfer->ohci_xfer_flags & OHCI_ISOC_DIRTY) {
|
||||
for (sitd = xfer->hcpriv; sitd != NULL && sitd->xfer == xfer;
|
||||
sitd = sitd->nextitd)
|
||||
ohci_free_sitd(sc, sitd);
|
||||
}
|
||||
|
||||
#ifdef DIAGNOSTIC
|
||||
if (xfer->busy_free != XFER_BUSY) {
|
||||
@ -1338,7 +1347,6 @@ ohci_softintr(void *v)
|
||||
usbd_xfer_handle xfer;
|
||||
struct ohci_pipe *opipe;
|
||||
int len, cc, s;
|
||||
int i, j, actlen, iframes, uedir;
|
||||
|
||||
DPRINTFN(10,("ohci_softintr: enter\n"));
|
||||
|
||||
@ -1453,6 +1461,7 @@ ohci_softintr(void *v)
|
||||
for (sitd = sidone; sitd != NULL; sitd = sitdnext) {
|
||||
xfer = sitd->xfer;
|
||||
sitdnext = sitd->dnext;
|
||||
sitd->flags |= OHCI_ITD_INTFIN;
|
||||
DPRINTFN(1, ("ohci_process_done: sitd=%p xfer=%p hcpriv=%p\n",
|
||||
sitd, xfer, xfer ? xfer->hcpriv : 0));
|
||||
if (xfer == NULL)
|
||||
@ -1464,52 +1473,33 @@ ohci_softintr(void *v)
|
||||
/* Handled by abort routine. */
|
||||
continue;
|
||||
}
|
||||
if (xfer->pipe)
|
||||
if (xfer->pipe->aborting)
|
||||
continue; /*Ignore.*/
|
||||
#ifdef DIAGNOSTIC
|
||||
if (sitd->isdone)
|
||||
printf("ohci_softintr: sitd=%p is done\n", sitd);
|
||||
sitd->isdone = 1;
|
||||
#endif
|
||||
struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
|
||||
if (opipe->aborting)
|
||||
continue;
|
||||
|
||||
cc = OHCI_ITD_GET_CC(le32toh(sitd->itd.itd_flags));
|
||||
if (cc == OHCI_CC_NO_ERROR) {
|
||||
/* XXX compute length for input */
|
||||
if (sitd->flags & OHCI_CALL_DONE) {
|
||||
ohci_soft_itd_t *next;
|
||||
|
||||
opipe = (struct ohci_pipe *)xfer->pipe;
|
||||
opipe->u.iso.inuse -= xfer->nframes;
|
||||
uedir = UE_GET_DIR(xfer->pipe->endpoint->edesc->
|
||||
bEndpointAddress);
|
||||
/* XXX update frlengths with actual length */
|
||||
/* XXX xfer->actlen = actlen; */
|
||||
xfer->status = USBD_NORMAL_COMPLETION;
|
||||
actlen = 0;
|
||||
for (i = 0, sitd = xfer->hcpriv;;
|
||||
sitd = next) {
|
||||
next = sitd->nextitd;
|
||||
if (OHCI_ITD_GET_CC(le32toh(sitd->
|
||||
itd.itd_flags)) != OHCI_CC_NO_ERROR)
|
||||
s = splusb();
|
||||
usb_transfer_complete(xfer);
|
||||
splx(s);
|
||||
}
|
||||
} else {
|
||||
/* XXX Do more */
|
||||
xfer->status = USBD_IOERROR;
|
||||
/* For input, update frlengths with actual */
|
||||
/* XXX anything necessary for output? */
|
||||
if (uedir == UE_DIR_IN &&
|
||||
xfer->status == USBD_NORMAL_COMPLETION) {
|
||||
iframes = OHCI_ITD_GET_FC(le32toh(
|
||||
sitd->itd.itd_flags));
|
||||
for (j = 0; j < iframes; i++, j++) {
|
||||
len = le16toh(sitd->
|
||||
itd.itd_offset[j]);
|
||||
len =
|
||||
(OHCI_ITD_PSW_GET_CC(len) ==
|
||||
OHCI_CC_NOT_ACCESSED) ? 0 :
|
||||
OHCI_ITD_PSW_LENGTH(len);
|
||||
xfer->frlengths[i] = len;
|
||||
actlen += len;
|
||||
}
|
||||
}
|
||||
if (sitd->flags & OHCI_CALL_DONE)
|
||||
break;
|
||||
ohci_free_sitd(sc, sitd);
|
||||
}
|
||||
ohci_free_sitd(sc, sitd);
|
||||
if (uedir == UE_DIR_IN &&
|
||||
xfer->status == USBD_NORMAL_COMPLETION)
|
||||
xfer->actlen = actlen;
|
||||
|
||||
s = splusb();
|
||||
usb_transfer_complete(xfer);
|
||||
splx(s);
|
||||
@ -1704,7 +1694,6 @@ ohci_device_request(usbd_xfer_handle xfer)
|
||||
usbd_device_handle dev = opipe->pipe.device;
|
||||
ohci_softc_t *sc = (ohci_softc_t *)dev->bus;
|
||||
int addr = dev->address;
|
||||
ohci_soft_td_t *data = 0;
|
||||
ohci_soft_td_t *setup, *stat, *next, *tail;
|
||||
ohci_soft_ed_t *sed;
|
||||
int isread;
|
||||
@ -1745,31 +1734,20 @@ ohci_device_request(usbd_xfer_handle xfer)
|
||||
OHCI_ED_SET_FA(addr) |
|
||||
OHCI_ED_SET_MAXP(UGETW(opipe->pipe.endpoint->edesc->wMaxPacketSize)));
|
||||
|
||||
next = stat;
|
||||
|
||||
/* Set up data transaction */
|
||||
if (len != 0) {
|
||||
data = ohci_alloc_std(sc);
|
||||
if (data == NULL) {
|
||||
err = USBD_NOMEM;
|
||||
goto bad3;
|
||||
}
|
||||
data->td.td_flags = htole32(
|
||||
(isread ? OHCI_TD_IN : OHCI_TD_OUT) | OHCI_TD_NOCC |
|
||||
OHCI_TD_TOGGLE_1 | OHCI_TD_NOINTR |
|
||||
(xfer->flags & USBD_SHORT_XFER_OK ? OHCI_TD_R : 0));
|
||||
data->td.td_cbp = htole32(DMAADDR(&xfer->dmabuf, 0));
|
||||
data->nexttd = stat;
|
||||
data->td.td_nexttd = htole32(stat->physaddr);
|
||||
data->td.td_be = htole32(le32toh(data->td.td_cbp) + len - 1);
|
||||
data->len = len;
|
||||
data->xfer = xfer;
|
||||
data->flags = OHCI_ADD_LEN;
|
||||
ohci_soft_td_t *std = stat;
|
||||
|
||||
next = data;
|
||||
stat->flags = OHCI_CALL_DONE;
|
||||
} else {
|
||||
next = stat;
|
||||
/* XXX ADD_LEN? */
|
||||
stat->flags = OHCI_CALL_DONE | OHCI_ADD_LEN;
|
||||
err = ohci_alloc_std_chain(opipe, sc, len, isread, xfer,
|
||||
std, &stat);
|
||||
stat = stat->nexttd; /* point at free TD */
|
||||
if (err)
|
||||
goto bad3;
|
||||
/* Start toggle at 1 and then use the carried toggle. */
|
||||
std->td.td_flags &= htole32(~OHCI_TD_TOGGLE_MASK);
|
||||
std->td.td_flags |= htole32(OHCI_TD_TOGGLE_1);
|
||||
}
|
||||
|
||||
memcpy(KERNADDR(&opipe->u.ctl.reqdma, 0), req, sizeof *req);
|
||||
@ -1792,6 +1770,7 @@ ohci_device_request(usbd_xfer_handle xfer)
|
||||
stat->nexttd = tail;
|
||||
stat->td.td_nexttd = htole32(tail->physaddr);
|
||||
stat->td.td_be = 0;
|
||||
stat->flags = OHCI_CALL_DONE;
|
||||
stat->len = 0;
|
||||
stat->xfer = xfer;
|
||||
|
||||
@ -2126,6 +2105,7 @@ ohci_open(usbd_pipe_handle pipe)
|
||||
if (sitd == NULL)
|
||||
goto bad1;
|
||||
opipe->tail.itd = sitd;
|
||||
opipe->aborting = 0;
|
||||
tdphys = sitd->physaddr;
|
||||
fmt = OHCI_ED_FORMAT_ISO;
|
||||
if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN)
|
||||
@ -2953,6 +2933,11 @@ ohci_device_bulk_start(usbd_xfer_handle xfer)
|
||||
data = opipe->tail.td;
|
||||
err = ohci_alloc_std_chain(opipe, sc, len, isread, xfer,
|
||||
data, &tail);
|
||||
/* We want interrupt at the end of the transfer. */
|
||||
tail->td.td_flags &= htole32(~OHCI_TD_INTR_MASK);
|
||||
tail->td.td_flags |= htole32(OHCI_TD_SET_DI(1));
|
||||
tail->flags |= OHCI_CALL_DONE;
|
||||
tail = tail->nexttd; /* point at sentinel */
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
@ -3270,8 +3255,9 @@ ohci_device_isoc_enter(usbd_xfer_handle xfer)
|
||||
ohci_softc_t *sc = (ohci_softc_t *)dev->bus;
|
||||
ohci_soft_ed_t *sed = opipe->sed;
|
||||
struct iso *iso = &opipe->u.iso;
|
||||
struct ohci_xfer *oxfer = (struct ohci_xfer *)xfer;
|
||||
ohci_soft_itd_t *sitd, *nsitd;
|
||||
ohci_physaddr_t buf, offs, noffs, bp0;
|
||||
ohci_physaddr_t buf, offs, noffs, bp0, tdphys;
|
||||
int i, ncur, nframes;
|
||||
int s;
|
||||
|
||||
@ -3289,6 +3275,24 @@ ohci_device_isoc_enter(usbd_xfer_handle xfer)
|
||||
iso->next));
|
||||
}
|
||||
|
||||
if (xfer->hcpriv) {
|
||||
for (sitd = xfer->hcpriv; sitd != NULL && sitd->xfer == xfer;
|
||||
sitd = sitd->nextitd)
|
||||
ohci_free_sitd(sc, sitd); /* Free ITDs in prev xfer*/
|
||||
|
||||
if (sitd == NULL) {
|
||||
sitd = ohci_alloc_sitd(sc);
|
||||
if (sitd == NULL)
|
||||
panic("cant alloc isoc");
|
||||
opipe->tail.itd = sitd;
|
||||
tdphys = sitd->physaddr;
|
||||
sed->ed.ed_flags |= htole32(OHCI_ED_SKIP); /* Stop*/
|
||||
sed->ed.ed_headp =
|
||||
sed->ed.ed_tailp = htole32(tdphys);
|
||||
sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP); /* Start.*/
|
||||
}
|
||||
}
|
||||
|
||||
sitd = opipe->tail.itd;
|
||||
buf = DMAADDR(&xfer->dmabuf, 0);
|
||||
bp0 = OHCI_PAGE(buf);
|
||||
@ -3320,7 +3324,7 @@ ohci_device_isoc_enter(usbd_xfer_handle xfer)
|
||||
sitd->itd.itd_nextitd = htole32(nsitd->physaddr);
|
||||
sitd->itd.itd_be = htole32(bp0 + offs - 1);
|
||||
sitd->xfer = xfer;
|
||||
sitd->flags = 0;
|
||||
sitd->flags = OHCI_ITD_ACTIVE;
|
||||
|
||||
sitd = nsitd;
|
||||
iso->next = iso->next + ncur;
|
||||
@ -3348,7 +3352,7 @@ ohci_device_isoc_enter(usbd_xfer_handle xfer)
|
||||
sitd->itd.itd_nextitd = htole32(nsitd->physaddr);
|
||||
sitd->itd.itd_be = htole32(bp0 + offs - 1);
|
||||
sitd->xfer = xfer;
|
||||
sitd->flags = OHCI_CALL_DONE;
|
||||
sitd->flags = OHCI_CALL_DONE | OHCI_ITD_ACTIVE;
|
||||
|
||||
iso->next = iso->next + ncur;
|
||||
iso->inuse += nframes;
|
||||
@ -3357,6 +3361,8 @@ ohci_device_isoc_enter(usbd_xfer_handle xfer)
|
||||
|
||||
xfer->status = USBD_IN_PROGRESS;
|
||||
|
||||
oxfer->ohci_xfer_flags |= OHCI_ISOC_DIRTY;
|
||||
|
||||
#ifdef USB_DEBUG
|
||||
if (ohcidebug > 5) {
|
||||
DPRINTF(("ohci_device_isoc_enter: frame=%d\n",
|
||||
@ -3388,6 +3394,8 @@ ohci_device_isoc_start(usbd_xfer_handle xfer)
|
||||
{
|
||||
struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
|
||||
ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus;
|
||||
ohci_soft_ed_t *sed;
|
||||
int s;
|
||||
|
||||
DPRINTFN(5,("ohci_device_isoc_start: xfer=%p\n", xfer));
|
||||
|
||||
@ -3401,6 +3409,11 @@ ohci_device_isoc_start(usbd_xfer_handle xfer)
|
||||
|
||||
/* XXX anything to do? */
|
||||
|
||||
s = splusb();
|
||||
sed = opipe->sed; /* Turn off ED skip-bit to start processing */
|
||||
sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP); /* ED's ITD list.*/
|
||||
splx(s);
|
||||
|
||||
return (USBD_IN_PROGRESS);
|
||||
}
|
||||
|
||||
@ -3410,10 +3423,11 @@ ohci_device_isoc_abort(usbd_xfer_handle xfer)
|
||||
struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
|
||||
ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus;
|
||||
ohci_soft_ed_t *sed;
|
||||
ohci_soft_itd_t *sitd;
|
||||
int s;
|
||||
ohci_soft_itd_t *sitd, *tmp_sitd;
|
||||
int s,undone,num_sitds;
|
||||
|
||||
s = splusb();
|
||||
opipe->aborting = 1;
|
||||
|
||||
DPRINTFN(1,("ohci_device_isoc_abort: xfer=%p\n", xfer));
|
||||
|
||||
@ -3431,6 +3445,7 @@ ohci_device_isoc_abort(usbd_xfer_handle xfer)
|
||||
sed = opipe->sed;
|
||||
sed->ed.ed_flags |= htole32(OHCI_ED_SKIP); /* force hardware skip */
|
||||
|
||||
num_sitds = 0;
|
||||
sitd = xfer->hcpriv;
|
||||
#ifdef DIAGNOSTIC
|
||||
if (sitd == NULL) {
|
||||
@ -3439,7 +3454,8 @@ ohci_device_isoc_abort(usbd_xfer_handle xfer)
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
for (; sitd->xfer == xfer; sitd = sitd->nextitd) {
|
||||
for (; sitd != NULL && sitd->xfer == xfer; sitd = sitd->nextitd) {
|
||||
num_sitds++;
|
||||
#ifdef DIAGNOSTIC
|
||||
DPRINTFN(1,("abort sets done sitd=%p\n", sitd));
|
||||
sitd->isdone = 1;
|
||||
@ -3448,14 +3464,43 @@ ohci_device_isoc_abort(usbd_xfer_handle xfer)
|
||||
|
||||
splx(s);
|
||||
|
||||
usb_delay_ms(&sc->sc_bus, OHCI_ITD_NOFFSET);
|
||||
/*
|
||||
* Each sitd has up to OHCI_ITD_NOFFSET transfers, each can
|
||||
* take a usb 1ms cycle. Conservatively wait for it to drain.
|
||||
* Even with DMA done, it can take awhile for the "batch"
|
||||
* delivery of completion interrupts to occur thru the controller.
|
||||
*/
|
||||
|
||||
do {
|
||||
usb_delay_ms(&sc->sc_bus, 2*(num_sitds*OHCI_ITD_NOFFSET));
|
||||
|
||||
undone = 0;
|
||||
tmp_sitd = xfer->hcpriv;
|
||||
for (; tmp_sitd != NULL && tmp_sitd->xfer == xfer;
|
||||
tmp_sitd = tmp_sitd->nextitd) {
|
||||
if (OHCI_CC_NO_ERROR ==
|
||||
OHCI_ITD_GET_CC(le32toh(tmp_sitd->itd.itd_flags)) &&
|
||||
tmp_sitd->flags & OHCI_ITD_ACTIVE &&
|
||||
(tmp_sitd->flags & OHCI_ITD_INTFIN) == 0)
|
||||
undone++;
|
||||
}
|
||||
} while( undone != 0 );
|
||||
|
||||
|
||||
s = splusb();
|
||||
|
||||
/* Run callback. */
|
||||
usb_transfer_complete(xfer);
|
||||
|
||||
sed->ed.ed_headp = htole32(sitd->physaddr); /* unlink TDs */
|
||||
if (sitd != NULL)
|
||||
/*
|
||||
* Only if there is a `next' sitd in next xfer...
|
||||
* unlink this xfer's sitds.
|
||||
*/
|
||||
sed->ed.ed_headp = htole32(sitd->physaddr);
|
||||
else
|
||||
sed->ed.ed_headp = 0;
|
||||
|
||||
sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP); /* remove hardware skip */
|
||||
|
||||
splx(s);
|
||||
@ -3464,10 +3509,23 @@ ohci_device_isoc_abort(usbd_xfer_handle xfer)
|
||||
void
|
||||
ohci_device_isoc_done(usbd_xfer_handle xfer)
|
||||
{
|
||||
|
||||
DPRINTFN(1,("ohci_device_isoc_done: xfer=%p\n", xfer));
|
||||
|
||||
xfer->hcpriv = NULL;
|
||||
/* This null routine corresponds to non-isoc "done()" routines
|
||||
* that free the stds associated with an xfer after a completed
|
||||
* xfer interrupt. However, in the case of isoc transfers, the
|
||||
* sitds associated with the transfer have already been processed
|
||||
* and reallocated for the next iteration by
|
||||
* "ohci_device_isoc_transfer()".
|
||||
*
|
||||
* Routine "usb_transfer_complete()" is called at the end of every
|
||||
* relevant usb interrupt. "usb_transfer_complete()" indirectly
|
||||
* calls 1) "ohci_device_isoc_transfer()" (which keeps pumping the
|
||||
* pipeline by setting up the next transfer iteration) and 2) then
|
||||
* calls "ohci_device_isoc_done()". Isoc transfers have not been
|
||||
* working for the ohci usb because this routine was trashing the
|
||||
* xfer set up for the next iteration (thus, only the first
|
||||
* UGEN_NISOREQS xfers outstanding on an open would work). Perhaps
|
||||
* this could all be re-factored, but that's another pass...
|
||||
*/
|
||||
}
|
||||
|
||||
usbd_status
|
||||
@ -3493,11 +3551,19 @@ ohci_device_isoc_close(usbd_pipe_handle pipe)
|
||||
{
|
||||
struct ohci_pipe *opipe = (struct ohci_pipe *)pipe;
|
||||
ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus;
|
||||
ohci_soft_ed_t *sed;
|
||||
|
||||
DPRINTF(("ohci_device_isoc_close: pipe=%p\n", pipe));
|
||||
ohci_close_pipe(pipe, sc->sc_isoc_head);
|
||||
|
||||
sed = opipe->sed;
|
||||
sed->ed.ed_flags |= htole32(OHCI_ED_SKIP); /* Stop device. */
|
||||
|
||||
ohci_close_pipe(pipe, sc->sc_isoc_head); /* Stop isoc list, free ED.*/
|
||||
|
||||
/* up to NISOREQs xfers still outstanding. */
|
||||
|
||||
#ifdef DIAGNOSTIC
|
||||
opipe->tail.itd->isdone = 1;
|
||||
#endif
|
||||
ohci_free_sitd(sc, opipe->tail.itd);
|
||||
ohci_free_sitd(sc, opipe->tail.itd); /* Next `avail free' sitd.*/
|
||||
}
|
||||
|
@ -187,9 +187,11 @@ typedef struct {
|
||||
#define OHCI_TD_GET_DI(x) (((x) >> 21) & 7) /* Delay Interrupt */
|
||||
#define OHCI_TD_SET_DI(x) ((x) << 21)
|
||||
#define OHCI_TD_NOINTR 0x00e00000
|
||||
#define OHCI_TD_INTR_MASK 0x00e00000
|
||||
#define OHCI_TD_TOGGLE_CARRY 0x00000000
|
||||
#define OHCI_TD_TOGGLE_0 0x02000000
|
||||
#define OHCI_TD_TOGGLE_1 0x03000000
|
||||
#define OHCI_TD_TOGGLE_MASK 0x03000000
|
||||
#define OHCI_TD_GET_EC(x) (((x) >> 26) & 3) /* Error Count */
|
||||
#define OHCI_TD_GET_CC(x) ((x) >> 28) /* Condition Code */
|
||||
#define OHCI_TD_NOCC 0xf0000000
|
||||
|
@ -70,6 +70,8 @@ typedef struct ohci_soft_itd {
|
||||
LIST_ENTRY(ohci_soft_itd) hnext;
|
||||
usbd_xfer_handle xfer;
|
||||
u_int16_t flags;
|
||||
#define OHCI_ITD_ACTIVE 0x0010 /* Hardware op in progress */
|
||||
#define OHCI_ITD_INTFIN 0x0020 /* Hw completion interrupt seen.*/
|
||||
#ifdef DIAGNOSTIC
|
||||
char isdone;
|
||||
#endif
|
||||
@ -149,9 +151,11 @@ typedef struct ohci_softc {
|
||||
struct ohci_xfer {
|
||||
struct usbd_xfer xfer;
|
||||
struct usb_task abort_task;
|
||||
u_int32_t ohci_xfer_flags;
|
||||
};
|
||||
#define OHCI_ISOC_DIRTY 0x01
|
||||
|
||||
#define OXFER(xfer) ((struct ehci_xfer *)(xfer))
|
||||
#define OXFER(xfer) ((struct ohci_xfer *)(xfer))
|
||||
|
||||
usbd_status ohci_init(ohci_softc_t *);
|
||||
int ohci_intr(void *);
|
||||
|
Loading…
Reference in New Issue
Block a user