version 1.47.2.9, 2005/12/11 10:29:05 |
version 1.48, 2003/10/12 18:04:28 |
|
|
/* $NetBSD$ */ |
/* $NetBSD$ */ |
|
|
/* |
/* |
* Copyright (c) 2004,2005 The NetBSD Foundation, Inc. |
* TODO |
|
* hold off explorations by companion controllers until ehci has started. |
|
*/ |
|
|
|
/* |
|
* Copyright (c) 2001 The NetBSD Foundation, Inc. |
* All rights reserved. |
* All rights reserved. |
* |
* |
* This code is derived from software contributed to The NetBSD Foundation |
* This code is derived from software contributed to The NetBSD Foundation |
* by Lennart Augustsson (lennart@augustsson.net) and by Charles M. Hannum. |
* by Lennart Augustsson (lennart@augustsson.net). |
* |
* |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* modification, are permitted provided that the following conditions |
|
|
* |
* |
*/ |
*/ |
|
|
/* |
|
* TODO: |
|
* 1) hold off explorations by companion controllers until ehci has started. |
|
* |
|
* 2) The EHCI driver lacks support for isochronous transfers, so |
|
* devices using them don't work. |
|
* |
|
* 3) The hub driver needs to handle and schedule the transaction translator, |
|
* to assign place in frame where different devices get to go. See chapter |
|
* on hubs in USB 2.0 for details. |
|
* |
|
* 4) command failures are not recovered correctly |
|
*/ |
|
|
|
#include <sys/cdefs.h> |
#include <sys/cdefs.h> |
__KERNEL_RCSID(0, "$NetBSD$"); |
__KERNEL_RCSID(0, "$NetBSD$"); |
|
|
Line 88 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 79 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include <dev/usb/ehcivar.h> |
#include <dev/usb/ehcivar.h> |
|
|
#ifdef EHCI_DEBUG |
#ifdef EHCI_DEBUG |
#define DPRINTF(x) do { if (ehcidebug) printf x; } while(0) |
#define DPRINTF(x) if (ehcidebug) printf x |
#define DPRINTFN(n,x) do { if (ehcidebug>(n)) printf x; } while (0) |
#define DPRINTFN(n,x) if (ehcidebug>(n)) printf x |
int ehcidebug = 0; |
int ehcidebug = 0; |
#ifndef __NetBSD__ |
#ifndef __NetBSD__ |
#define bitmask_snprintf(q,f,b,l) snprintf((b), (l), "%b", (q), (f)) |
#define bitmask_snprintf(q,f,b,l) snprintf((b), (l), "%b", (q), (f)) |
Line 101 int ehcidebug = 0; |
|
Line 92 int ehcidebug = 0; |
|
|
|
struct ehci_pipe { |
struct ehci_pipe { |
struct usbd_pipe pipe; |
struct usbd_pipe pipe; |
int nexttoggle; |
|
|
|
ehci_soft_qh_t *sqh; |
ehci_soft_qh_t *sqh; |
union { |
union { |
ehci_soft_qtd_t *qtd; |
ehci_soft_qtd_t *qtd; |
Line 113 struct ehci_pipe { |
|
Line 102 struct ehci_pipe { |
|
struct { |
struct { |
usb_dma_t reqdma; |
usb_dma_t reqdma; |
u_int length; |
u_int length; |
|
/*ehci_soft_qtd_t *setup, *data, *stat;*/ |
} ctl; |
} ctl; |
/* Interrupt pipe */ |
/* Interrupt pipe */ |
struct { |
/* XXX */ |
u_int length; |
|
} intr; |
|
/* Bulk pipe */ |
/* Bulk pipe */ |
struct { |
struct { |
u_int length; |
u_int length; |
Line 185 Static void ehci_device_isoc_done(usbd_ |
|
Line 173 Static void ehci_device_isoc_done(usbd_ |
|
Static void ehci_device_clear_toggle(usbd_pipe_handle pipe); |
Static void ehci_device_clear_toggle(usbd_pipe_handle pipe); |
Static void ehci_noop(usbd_pipe_handle pipe); |
Static void ehci_noop(usbd_pipe_handle pipe); |
|
|
Static int ehci_str(usb_string_descriptor_t *, int, const char *); |
Static int ehci_str(usb_string_descriptor_t *, int, char *); |
Static void ehci_pcd(ehci_softc_t *, usbd_xfer_handle); |
Static void ehci_pcd(ehci_softc_t *, usbd_xfer_handle); |
Static void ehci_pcd_able(ehci_softc_t *, int); |
Static void ehci_pcd_able(ehci_softc_t *, int); |
Static void ehci_pcd_enable(void *); |
Static void ehci_pcd_enable(void *); |
Line 204 Static void ehci_free_sqtd_chain(ehci_s |
|
Line 192 Static void ehci_free_sqtd_chain(ehci_s |
|
|
|
Static usbd_status ehci_device_request(usbd_xfer_handle xfer); |
Static usbd_status ehci_device_request(usbd_xfer_handle xfer); |
|
|
Static usbd_status ehci_device_setintr(ehci_softc_t *, ehci_soft_qh_t *, |
|
int ival); |
|
|
|
Static void ehci_add_qh(ehci_soft_qh_t *, ehci_soft_qh_t *); |
Static void ehci_add_qh(ehci_soft_qh_t *, ehci_soft_qh_t *); |
Static void ehci_rem_qh(ehci_softc_t *, ehci_soft_qh_t *, |
Static void ehci_rem_qh(ehci_softc_t *, ehci_soft_qh_t *, |
ehci_soft_qh_t *); |
ehci_soft_qh_t *); |
Line 218 Static void ehci_abort_xfer(usbd_xfer_h |
|
Line 203 Static void ehci_abort_xfer(usbd_xfer_h |
|
|
|
#ifdef EHCI_DEBUG |
#ifdef EHCI_DEBUG |
Static void ehci_dump_regs(ehci_softc_t *); |
Static void ehci_dump_regs(ehci_softc_t *); |
void ehci_dump(void); |
Static void ehci_dump(void); |
Static ehci_softc_t *theehci; |
Static ehci_softc_t *theehci; |
Static void ehci_dump_link(ehci_link_t, int); |
Static void ehci_dump_link(ehci_link_t, int); |
Static void ehci_dump_sqtds(ehci_soft_qtd_t *); |
Static void ehci_dump_sqtds(ehci_soft_qtd_t *); |
Line 307 Static struct usbd_pipe_methods ehci_dev |
|
Line 292 Static struct usbd_pipe_methods ehci_dev |
|
ehci_device_isoc_done, |
ehci_device_isoc_done, |
}; |
}; |
|
|
static uint8_t revbits[EHCI_MAX_POLLRATE] = { |
|
0x00,0x40,0x20,0x60,0x10,0x50,0x30,0x70,0x08,0x48,0x28,0x68,0x18,0x58,0x38,0x78, |
|
0x04,0x44,0x24,0x64,0x14,0x54,0x34,0x74,0x0c,0x4c,0x2c,0x6c,0x1c,0x5c,0x3c,0x7c, |
|
0x02,0x42,0x22,0x62,0x12,0x52,0x32,0x72,0x0a,0x4a,0x2a,0x6a,0x1a,0x5a,0x3a,0x7a, |
|
0x06,0x46,0x26,0x66,0x16,0x56,0x36,0x76,0x0e,0x4e,0x2e,0x6e,0x1e,0x5e,0x3e,0x7e, |
|
0x01,0x41,0x21,0x61,0x11,0x51,0x31,0x71,0x09,0x49,0x29,0x69,0x19,0x59,0x39,0x79, |
|
0x05,0x45,0x25,0x65,0x15,0x55,0x35,0x75,0x0d,0x4d,0x2d,0x6d,0x1d,0x5d,0x3d,0x7d, |
|
0x03,0x43,0x23,0x63,0x13,0x53,0x33,0x73,0x0b,0x4b,0x2b,0x6b,0x1b,0x5b,0x3b,0x7b, |
|
0x07,0x47,0x27,0x67,0x17,0x57,0x37,0x77,0x0f,0x4f,0x2f,0x6f,0x1f,0x5f,0x3f,0x7f, |
|
}; |
|
|
|
usbd_status |
usbd_status |
ehci_init(ehci_softc_t *sc) |
ehci_init(ehci_softc_t *sc) |
{ |
{ |
u_int32_t vers, sparams, cparams, hcr; |
u_int32_t version, sparams, cparams, hcr; |
u_int i; |
u_int i; |
usbd_status err; |
usbd_status err; |
ehci_soft_qh_t *sqh; |
ehci_soft_qh_t *sqh; |
u_int ncomp; |
|
|
|
DPRINTF(("ehci_init: start\n")); |
DPRINTF(("ehci_init: start\n")); |
#ifdef EHCI_DEBUG |
#ifdef EHCI_DEBUG |
Line 334 ehci_init(ehci_softc_t *sc) |
|
Line 307 ehci_init(ehci_softc_t *sc) |
|
|
|
sc->sc_offs = EREAD1(sc, EHCI_CAPLENGTH); |
sc->sc_offs = EREAD1(sc, EHCI_CAPLENGTH); |
|
|
vers = EREAD2(sc, EHCI_HCIVERSION); |
version = EREAD2(sc, EHCI_HCIVERSION); |
aprint_normal("%s: EHCI version %x.%x\n", USBDEVNAME(sc->sc_bus.bdev), |
aprint_normal("%s: EHCI version %x.%x\n", USBDEVNAME(sc->sc_bus.bdev), |
vers >> 8, vers & 0xff); |
version >> 8, version & 0xff); |
|
|
sparams = EREAD4(sc, EHCI_HCSPARAMS); |
sparams = EREAD4(sc, EHCI_HCSPARAMS); |
DPRINTF(("ehci_init: sparams=0x%x\n", sparams)); |
DPRINTF(("ehci_init: sparams=0x%x\n", sparams)); |
sc->sc_npcomp = EHCI_HCS_N_PCC(sparams); |
sc->sc_npcomp = EHCI_HCS_N_PCC(sparams); |
ncomp = EHCI_HCS_N_CC(sparams); |
if (EHCI_HCS_N_CC(sparams) != sc->sc_ncomp) { |
if (ncomp != sc->sc_ncomp) { |
|
aprint_error("%s: wrong number of companions (%d != %d)\n", |
aprint_error("%s: wrong number of companions (%d != %d)\n", |
USBDEVNAME(sc->sc_bus.bdev), |
USBDEVNAME(sc->sc_bus.bdev), |
ncomp, sc->sc_ncomp); |
EHCI_HCS_N_CC(sparams), sc->sc_ncomp); |
#if NOHCI == 0 || NUHCI == 0 |
#if NOHCI == 0 || NUHCI == 0 |
aprint_error("%s: ohci or uhci probably not configured\n", |
aprint_error("%s: ohci or uhci probably not configured\n", |
USBDEVNAME(sc->sc_bus.bdev)); |
USBDEVNAME(sc->sc_bus.bdev)); |
#endif |
#endif |
if (ncomp < sc->sc_ncomp) |
return (USBD_IOERROR); |
sc->sc_ncomp = ncomp; |
|
} |
} |
if (sc->sc_ncomp > 0) { |
if (sc->sc_ncomp > 0) { |
aprint_normal("%s: companion controller%s, %d port%s each:", |
aprint_normal("%s: companion controller%s, %d port%s each:", |
Line 365 ehci_init(ehci_softc_t *sc) |
|
Line 336 ehci_init(ehci_softc_t *sc) |
|
sc->sc_noport = EHCI_HCS_N_PORTS(sparams); |
sc->sc_noport = EHCI_HCS_N_PORTS(sparams); |
cparams = EREAD4(sc, EHCI_HCCPARAMS); |
cparams = EREAD4(sc, EHCI_HCCPARAMS); |
DPRINTF(("ehci_init: cparams=0x%x\n", cparams)); |
DPRINTF(("ehci_init: cparams=0x%x\n", cparams)); |
sc->sc_hasppc = EHCI_HCS_PPC(sparams); |
|
|
|
if (EHCI_HCC_64BIT(cparams)) { |
if (EHCI_HCC_64BIT(cparams)) { |
/* MUST clear segment register if 64 bit capable. */ |
/* MUST clear segment register if 64 bit capable. */ |
Line 374 ehci_init(ehci_softc_t *sc) |
|
Line 344 ehci_init(ehci_softc_t *sc) |
|
|
|
sc->sc_bus.usbrev = USBREV_2_0; |
sc->sc_bus.usbrev = USBREV_2_0; |
|
|
usb_setup_reserve(sc, &sc->sc_dma_reserve, sc->sc_bus.dmatag, |
|
USB_MEM_RESERVE); |
|
|
|
/* Reset the controller */ |
/* Reset the controller */ |
DPRINTF(("%s: resetting\n", USBDEVNAME(sc->sc_bus.bdev))); |
DPRINTF(("%s: resetting\n", USBDEVNAME(sc->sc_bus.bdev))); |
EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */ |
EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */ |
Line 394 ehci_init(ehci_softc_t *sc) |
|
Line 361 ehci_init(ehci_softc_t *sc) |
|
return (USBD_IOERROR); |
return (USBD_IOERROR); |
} |
} |
|
|
/* XXX need proper intr scheduling */ |
|
sc->sc_rand = 96; |
|
|
|
/* frame list size at default, read back what we got and use that */ |
/* frame list size at default, read back what we got and use that */ |
switch (EHCI_CMD_FLS(EOREAD4(sc, EHCI_USBCMD))) { |
switch (EHCI_CMD_FLS(EOREAD4(sc, EHCI_USBCMD))) { |
case 0: sc->sc_flsize = 1024; break; |
case 0: sc->sc_flsize = 1024*4; break; |
case 1: sc->sc_flsize = 512; break; |
case 1: sc->sc_flsize = 512*4; break; |
case 2: sc->sc_flsize = 256; break; |
case 2: sc->sc_flsize = 256*4; break; |
case 3: return (USBD_IOERROR); |
case 3: return (USBD_IOERROR); |
} |
} |
err = usb_allocmem(&sc->sc_bus, sc->sc_flsize * sizeof(ehci_link_t), |
err = usb_allocmem(&sc->sc_bus, sc->sc_flsize, |
EHCI_FLALIGN_ALIGN, &sc->sc_fldma); |
EHCI_FLALIGN_ALIGN, &sc->sc_fldma); |
if (err) |
if (err) |
return (err); |
return (err); |
DPRINTF(("%s: flsize=%d\n", USBDEVNAME(sc->sc_bus.bdev),sc->sc_flsize)); |
DPRINTF(("%s: flsize=%d\n", USBDEVNAME(sc->sc_bus.bdev),sc->sc_flsize)); |
sc->sc_flist = KERNADDR(&sc->sc_fldma, 0); |
|
EOWRITE4(sc, EHCI_PERIODICLISTBASE, DMAADDR(&sc->sc_fldma, 0)); |
|
|
|
/* Set up the bus struct. */ |
/* Set up the bus struct. */ |
sc->sc_bus.methods = &ehci_bus_methods; |
sc->sc_bus.methods = &ehci_bus_methods; |
Line 421 ehci_init(ehci_softc_t *sc) |
|
Line 383 ehci_init(ehci_softc_t *sc) |
|
|
|
sc->sc_eintrs = EHCI_NORMAL_INTRS; |
sc->sc_eintrs = EHCI_NORMAL_INTRS; |
|
|
/* |
|
* Allocate the interrupt dummy QHs. These are arranged to give poll |
|
* intervals that are powers of 2 times 1ms. |
|
*/ |
|
for (i = 0; i < EHCI_INTRQHS; i++) { |
|
sqh = ehci_alloc_sqh(sc); |
|
if (sqh == NULL) { |
|
err = USBD_NOMEM; |
|
goto bad1; |
|
} |
|
sc->sc_islots[i].sqh = sqh; |
|
} |
|
for (i = 0; i < EHCI_INTRQHS; i++) { |
|
sqh = sc->sc_islots[i].sqh; |
|
if (i == 0) { |
|
/* The last (1ms) QH terminates. */ |
|
sqh->qh.qh_link = EHCI_NULL; |
|
sqh->next = NULL; |
|
} else { |
|
/* Otherwise the next QH has half the poll interval */ |
|
sqh->next = sc->sc_islots[(i + 1) / 2 - 1].sqh; |
|
sqh->qh.qh_link = htole32(sqh->next->physaddr | |
|
EHCI_LINK_QH); |
|
} |
|
sqh->qh.qh_endp = htole32(EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH)); |
|
sqh->qh.qh_curqtd = EHCI_NULL; |
|
sqh->next = NULL; |
|
sqh->qh.qh_qtd.qtd_next = EHCI_NULL; |
|
sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL; |
|
sqh->qh.qh_qtd.qtd_status = htole32(EHCI_QTD_HALTED); |
|
sqh->sqtd = NULL; |
|
} |
|
/* Point the frame list at the last level (128ms). */ |
|
for (i = 0; i < sc->sc_flsize; i++) { |
|
int j; |
|
|
|
j = (i & ~(EHCI_MAX_POLLRATE-1)) | |
|
revbits[i & (EHCI_MAX_POLLRATE-1)]; |
|
sc->sc_flist[j] = htole32(EHCI_LINK_QH | |
|
sc->sc_islots[EHCI_IQHIDX(EHCI_IPOLLRATES - 1, |
|
i)].sqh->physaddr); |
|
} |
|
|
|
/* Allocate dummy QH that starts the async list. */ |
/* Allocate dummy QH that starts the async list. */ |
sqh = ehci_alloc_sqh(sc); |
sqh = ehci_alloc_sqh(sc); |
if (sqh == NULL) { |
if (sqh == NULL) { |
Line 496 ehci_init(ehci_softc_t *sc) |
|
Line 415 ehci_init(ehci_softc_t *sc) |
|
|
|
lockinit(&sc->sc_doorbell_lock, PZERO, "ehcidb", 0, 0); |
lockinit(&sc->sc_doorbell_lock, PZERO, "ehcidb", 0, 0); |
|
|
|
/* Enable interrupts */ |
|
EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); |
|
|
/* Turn on controller */ |
/* Turn on controller */ |
EOWRITE4(sc, EHCI_USBCMD, |
EOWRITE4(sc, EHCI_USBCMD, |
EHCI_CMD_ITC_2 | /* 2 microframes interrupt delay */ |
EHCI_CMD_ITC_8 | /* 8 microframes */ |
(EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_FLS_M) | |
(EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_FLS_M) | |
EHCI_CMD_ASE | |
EHCI_CMD_ASE | |
EHCI_CMD_PSE | |
/* EHCI_CMD_PSE | */ |
EHCI_CMD_RS); |
EHCI_CMD_RS); |
|
|
/* Take over port ownership */ |
/* Take over port ownership */ |
Line 518 ehci_init(ehci_softc_t *sc) |
|
Line 440 ehci_init(ehci_softc_t *sc) |
|
return (USBD_IOERROR); |
return (USBD_IOERROR); |
} |
} |
|
|
/* Enable interrupts */ |
|
DPRINTFN(1,("ehci_init: enabling\n")); |
|
EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); |
|
|
|
return (USBD_NORMAL_COMPLETION); |
return (USBD_NORMAL_COMPLETION); |
|
|
#if 0 |
#if 0 |
Line 543 ehci_intr(void *v) |
|
Line 461 ehci_intr(void *v) |
|
|
|
/* If we get an interrupt while polling, then just ignore it. */ |
/* If we get an interrupt while polling, then just ignore it. */ |
if (sc->sc_bus.use_polling) { |
if (sc->sc_bus.use_polling) { |
u_int32_t intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); |
|
|
|
if (intrs) |
|
EOWRITE4(sc, EHCI_USBSTS, intrs); /* Acknowledge */ |
|
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
DPRINTFN(16, ("ehci_intr: ignored interrupt while polling\n")); |
printf("ehci_intr: ignored interrupt while polling\n"); |
#endif |
#endif |
return (0); |
return (0); |
} |
} |
Line 566 ehci_intr1(ehci_softc_t *sc) |
|
Line 480 ehci_intr1(ehci_softc_t *sc) |
|
/* In case the interrupt occurs before initialization has completed. */ |
/* In case the interrupt occurs before initialization has completed. */ |
if (sc == NULL) { |
if (sc == NULL) { |
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
printf("ehci_intr1: sc == NULL\n"); |
printf("ehci_intr: sc == NULL\n"); |
#endif |
#endif |
return (0); |
return (0); |
} |
} |
|
|
intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); |
intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); |
|
|
if (!intrs) |
if (!intrs) |
return (0); |
return (0); |
|
|
|
EOWRITE4(sc, EHCI_USBSTS, intrs); /* Acknowledge */ |
eintrs = intrs & sc->sc_eintrs; |
eintrs = intrs & sc->sc_eintrs; |
DPRINTFN(7, ("ehci_intr1: sc=%p intrs=0x%x(0x%x) eintrs=0x%x\n", |
DPRINTFN(7, ("ehci_intr: sc=%p intrs=0x%x(0x%x) eintrs=0x%x\n", |
sc, (u_int)intrs, EOREAD4(sc, EHCI_USBSTS), |
sc, (u_int)intrs, EOREAD4(sc, EHCI_USBSTS), |
(u_int)eintrs)); |
(u_int)eintrs)); |
if (!eintrs) |
if (!eintrs) |
return (0); |
return (0); |
|
|
EOWRITE4(sc, EHCI_USBSTS, intrs); /* Acknowledge */ |
|
sc->sc_bus.intr_context++; |
sc->sc_bus.intr_context++; |
sc->sc_bus.no_intrs++; |
sc->sc_bus.no_intrs++; |
if (eintrs & EHCI_STS_IAA) { |
if (eintrs & EHCI_STS_IAA) { |
|
|
ehci_pcd(ehci_softc_t *sc, usbd_xfer_handle xfer) |
ehci_pcd(ehci_softc_t *sc, usbd_xfer_handle xfer) |
{ |
{ |
usbd_pipe_handle pipe; |
usbd_pipe_handle pipe; |
|
struct ehci_pipe *epipe; |
u_char *p; |
u_char *p; |
int i, m; |
int i, m; |
|
|
Line 659 ehci_pcd(ehci_softc_t *sc, usbd_xfer_han |
|
Line 575 ehci_pcd(ehci_softc_t *sc, usbd_xfer_han |
|
} |
} |
|
|
pipe = xfer->pipe; |
pipe = xfer->pipe; |
|
epipe = (struct ehci_pipe *)pipe; |
|
|
p = KERNADDR(&xfer->dmabuf, 0); |
p = KERNADDR(&xfer->dmabuf, 0); |
m = min(sc->sc_noport, xfer->length * 8 - 1); |
m = min(sc->sc_noport, xfer->length * 8 - 1); |
|
|
ehci_softintr(void *v) |
ehci_softintr(void *v) |
{ |
{ |
ehci_softc_t *sc = v; |
ehci_softc_t *sc = v; |
struct ehci_xfer *ex, *nextex; |
struct ehci_xfer *ex; |
|
|
DPRINTFN(10,("%s: ehci_softintr (%d)\n", USBDEVNAME(sc->sc_bus.bdev), |
DPRINTFN(10,("%s: ehci_softintr (%d)\n", USBDEVNAME(sc->sc_bus.bdev), |
sc->sc_bus.intr_context)); |
sc->sc_bus.intr_context)); |
Line 692 ehci_softintr(void *v) |
|
Line 609 ehci_softintr(void *v) |
|
* An interrupt just tells us that something is done, we have no |
* An interrupt just tells us that something is done, we have no |
* clue what, so we need to scan through all active transfers. :-( |
* clue what, so we need to scan through all active transfers. :-( |
*/ |
*/ |
for (ex = LIST_FIRST(&sc->sc_intrhead); ex; ex = nextex) { |
for (ex = LIST_FIRST(&sc->sc_intrhead); ex; ex = LIST_NEXT(ex, inext)) |
nextex = LIST_NEXT(ex, inext); |
|
ehci_check_intr(sc, ex); |
ehci_check_intr(sc, ex); |
} |
|
|
|
#ifdef USB_USE_SOFTINTR |
|
if (sc->sc_softwake) { |
if (sc->sc_softwake) { |
sc->sc_softwake = 0; |
sc->sc_softwake = 0; |
wakeup(&sc->sc_softwake); |
wakeup(&sc->sc_softwake); |
} |
} |
#endif /* USB_USE_SOFTINTR */ |
|
|
|
sc->sc_bus.intr_context--; |
sc->sc_bus.intr_context--; |
} |
} |
Line 723 ehci_check_intr(ehci_softc_t *sc, struct |
|
Line 636 ehci_check_intr(ehci_softc_t *sc, struct |
|
lsqtd = ex->sqtdend; |
lsqtd = ex->sqtdend; |
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
if (lsqtd == NULL) { |
if (lsqtd == NULL) { |
printf("ehci_check_intr: lsqtd==0\n"); |
printf("ehci_check_intr: sqtd==0\n"); |
return; |
return; |
} |
} |
#endif |
#endif |
Line 743 ehci_check_intr(ehci_softc_t *sc, struct |
|
Line 656 ehci_check_intr(ehci_softc_t *sc, struct |
|
if (status & EHCI_QTD_HALTED) |
if (status & EHCI_QTD_HALTED) |
goto done; |
goto done; |
/* We want short packets, and it is short: it's done */ |
/* We want short packets, and it is short: it's done */ |
if (EHCI_QTD_GET_BYTES(status) != 0) |
if (EHCI_QTD_SET_BYTES(status) != 0) |
goto done; |
goto done; |
} |
} |
DPRINTFN(12, ("ehci_check_intr: ex=%p std=%p still active\n", |
DPRINTFN(12, ("ehci_check_intr: ex=%p std=%p still active\n", |
|
|
ehci_idone(struct ehci_xfer *ex) |
ehci_idone(struct ehci_xfer *ex) |
{ |
{ |
usbd_xfer_handle xfer = &ex->xfer; |
usbd_xfer_handle xfer = &ex->xfer; |
|
#ifdef EHCI_DEBUG |
struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; |
struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; |
ehci_soft_qtd_t *sqtd, *lsqtd; |
#endif |
u_int32_t status = 0, nstatus = 0; |
ehci_soft_qtd_t *sqtd; |
|
u_int32_t status = 0, nstatus; |
int actlen; |
int actlen; |
uint pkts_left; |
|
|
|
DPRINTFN(/*12*/2, ("ehci_idone: ex=%p\n", ex)); |
DPRINTFN(/*12*/2, ("ehci_idone: ex=%p\n", ex)); |
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
Line 798 ehci_idone(struct ehci_xfer *ex) |
|
Line 712 ehci_idone(struct ehci_xfer *ex) |
|
#endif |
#endif |
|
|
/* The transfer is done, compute actual length and status. */ |
/* The transfer is done, compute actual length and status. */ |
lsqtd = ex->sqtdend; |
|
actlen = 0; |
actlen = 0; |
for (sqtd = ex->sqtdstart; sqtd != lsqtd->nextqtd; sqtd=sqtd->nextqtd) { |
for (sqtd = ex->sqtdstart; sqtd != NULL; sqtd = sqtd->nextqtd) { |
nstatus = le32toh(sqtd->qtd.qtd_status); |
nstatus = le32toh(sqtd->qtd.qtd_status); |
if (nstatus & EHCI_QTD_ACTIVE) |
if (nstatus & EHCI_QTD_ACTIVE) |
break; |
break; |
|
|
status = nstatus; |
status = nstatus; |
|
/* halt is ok if descriptor is last, and complete */ |
|
if(sqtd->qtd.qtd_next == EHCI_NULL |
|
&& EHCI_QTD_GET_BYTES(status) == 0) |
|
status &= ~EHCI_QTD_HALTED; |
if (EHCI_QTD_GET_PID(status) != EHCI_QTD_PID_SETUP) |
if (EHCI_QTD_GET_PID(status) != EHCI_QTD_PID_SETUP) |
actlen += sqtd->len - EHCI_QTD_GET_BYTES(status); |
actlen += sqtd->len - EHCI_QTD_GET_BYTES(status); |
} |
} |
|
|
/* |
/* If there are left over TDs we need to update the toggle. */ |
* If there are left over TDs we need to update the toggle. |
if (sqtd != NULL) { |
* The default pipe doesn't need it since control transfers |
if (!(xfer->rqflags & URQ_REQUEST)) |
* start the toggle at 0 every time. |
printf("ehci_idone: need toggle update\n"); |
*/ |
|
if (sqtd != lsqtd->nextqtd && |
|
xfer->pipe->device->default_pipe != xfer->pipe) { |
|
printf("ehci_idone: need toggle update status=%08x nstatus=%08x\n", status, nstatus); |
|
#if 0 |
#if 0 |
ehci_dump_sqh(epipe->sqh); |
epipe->nexttoggle = EHCI_TD_GET_DT(le32toh(std->td.td_token)); |
ehci_dump_sqtds(ex->sqtdstart); |
|
#endif |
#endif |
epipe->nexttoggle = EHCI_QTD_GET_TOGGLE(nstatus); |
|
} |
} |
|
|
/* |
status &= EHCI_QTD_STATERRS; |
* For a short transfer we need to update the toggle for the missing |
|
* packets within the qTD. |
|
*/ |
|
pkts_left = EHCI_QTD_GET_BYTES(status) / |
|
UGETW(xfer->pipe->endpoint->edesc->wMaxPacketSize); |
|
epipe->nexttoggle ^= pkts_left % 2; |
|
|
|
DPRINTFN(/*10*/2, ("ehci_idone: len=%d, actlen=%d, status=0x%x\n", |
DPRINTFN(/*10*/2, ("ehci_idone: len=%d, actlen=%d, status=0x%x\n", |
xfer->length, actlen, status)); |
xfer->length, actlen, status)); |
xfer->actlen = actlen; |
xfer->actlen = actlen; |
if (status & EHCI_QTD_HALTED) { |
if (status != 0) { |
#ifdef EHCI_DEBUG |
#ifdef EHCI_DEBUG |
char sbuf[128]; |
char sbuf[128]; |
|
|
bitmask_snprintf((u_int32_t)status, |
bitmask_snprintf((u_int32_t)status, |
"\20\7HALTED\6BUFERR\5BABBLE\4XACTERR" |
"\20\3MISSEDMICRO\4XACT\5BABBLE\6BABBLE" |
"\3MISSED\1PINGSTATE", sbuf, sizeof(sbuf)); |
"\7HALTED", |
|
sbuf, sizeof(sbuf)); |
|
|
DPRINTFN(2, ("ehci_idone: error, addr=%d, endpt=0x%02x, " |
DPRINTFN((status == EHCI_QTD_HALTED)*/*10*/2, |
|
("ehci_idone: error, addr=%d, endpt=0x%02x, " |
"status 0x%s\n", |
"status 0x%s\n", |
xfer->pipe->device->address, |
xfer->pipe->device->address, |
xfer->pipe->endpoint->edesc->bEndpointAddress, |
xfer->pipe->endpoint->edesc->bEndpointAddress, |
Line 854 ehci_idone(struct ehci_xfer *ex) |
|
Line 760 ehci_idone(struct ehci_xfer *ex) |
|
ehci_dump_sqtds(ex->sqtdstart); |
ehci_dump_sqtds(ex->sqtdstart); |
} |
} |
#endif |
#endif |
/* low&full speed has an extra error flag */ |
if (status == EHCI_QTD_HALTED) |
if (EHCI_QH_GET_EPS(epipe->sqh->qh.qh_endp) != |
|
EHCI_QH_SPEED_HIGH) |
|
status &= EHCI_QTD_STATERRS | EHCI_QTD_PINGSTATE; |
|
else |
|
status &= EHCI_QTD_STATERRS; |
|
if (status == 0) /* no other errors means a stall */ |
|
xfer->status = USBD_STALLED; |
xfer->status = USBD_STALLED; |
else |
else |
xfer->status = USBD_IOERROR; /* more info XXX */ |
xfer->status = USBD_IOERROR; /* more info XXX */ |
/* XXX need to reset TT on missed microframe */ |
|
if (status & EHCI_QTD_MISSEDMICRO) { |
|
ehci_softc_t *sc = (ehci_softc_t *) |
|
xfer->pipe->device->bus; |
|
|
|
printf("%s: missed microframe, TT reset not " |
|
"implemented, hub might be inoperational\n", |
|
USBDEVNAME(sc->sc_bus.bdev)); |
|
} |
|
} else { |
} else { |
xfer->status = USBD_NORMAL_COMPLETION; |
xfer->status = USBD_NORMAL_COMPLETION; |
} |
} |
Line 889 ehci_idone(struct ehci_xfer *ex) |
|
Line 780 ehci_idone(struct ehci_xfer *ex) |
|
void |
void |
ehci_waitintr(ehci_softc_t *sc, usbd_xfer_handle xfer) |
ehci_waitintr(ehci_softc_t *sc, usbd_xfer_handle xfer) |
{ |
{ |
int timo; |
int timo = xfer->timeout; |
|
int usecs; |
u_int32_t intrs; |
u_int32_t intrs; |
|
|
xfer->status = USBD_IN_PROGRESS; |
xfer->status = USBD_IN_PROGRESS; |
for (timo = xfer->timeout; timo >= 0; timo--) { |
for (usecs = timo * 1000000 / hz; usecs > 0; usecs -= 1000) { |
usb_delay_ms(&sc->sc_bus, 1); |
usb_delay_ms(&sc->sc_bus, 1); |
if (sc->sc_dying) |
if (sc->sc_dying) |
break; |
break; |
intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)) & |
intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)) & |
sc->sc_eintrs; |
sc->sc_eintrs; |
DPRINTFN(15,("ehci_waitintr: 0x%04x\n", intrs)); |
DPRINTFN(15,("ehci_waitintr: 0x%04x\n", intrs)); |
#ifdef EHCI_DEBUG |
#ifdef OHCI_DEBUG |
if (ehcidebug > 15) |
if (ehcidebug > 15) |
ehci_dump_regs(sc); |
ehci_dump_regs(sc); |
#endif |
#endif |
Line 971 ehci_activate(device_ptr_t self, enum de |
|
Line 863 ehci_activate(device_ptr_t self, enum de |
|
switch (act) { |
switch (act) { |
case DVACT_ACTIVATE: |
case DVACT_ACTIVATE: |
return (EOPNOTSUPP); |
return (EOPNOTSUPP); |
|
break; |
|
|
case DVACT_DEACTIVATE: |
case DVACT_DEACTIVATE: |
if (sc->sc_child != NULL) |
if (sc->sc_child != NULL) |
Line 985 ehci_activate(device_ptr_t self, enum de |
|
Line 878 ehci_activate(device_ptr_t self, enum de |
|
* Handle suspend/resume. |
* Handle suspend/resume. |
* |
* |
* We need to switch to polling mode here, because this routine is |
* We need to switch to polling mode here, because this routine is |
* called from an interrupt context. This is all right since we |
* called from an intterupt context. This is all right since we |
* are almost suspended anyway. |
* are almost suspended anyway. |
*/ |
*/ |
void |
void |
ehci_power(int why, void *v) |
ehci_power(int why, void *v) |
{ |
{ |
ehci_softc_t *sc = v; |
ehci_softc_t *sc = v; |
u_int32_t cmd, hcr; |
//u_int32_t ctl; |
int s, i; |
int s; |
|
|
#ifdef EHCI_DEBUG |
#ifdef EHCI_DEBUG |
DPRINTF(("ehci_power: sc=%p, why=%d\n", sc, why)); |
DPRINTF(("ehci_power: sc=%p, why=%d\n", sc, why)); |
if (ehcidebug > 0) |
ehci_dump_regs(sc); |
ehci_dump_regs(sc); |
|
#endif |
#endif |
|
|
s = splhardusb(); |
s = splhardusb(); |
Line 1006 ehci_power(int why, void *v) |
|
Line 898 ehci_power(int why, void *v) |
|
case PWR_SUSPEND: |
case PWR_SUSPEND: |
case PWR_STANDBY: |
case PWR_STANDBY: |
sc->sc_bus.use_polling++; |
sc->sc_bus.use_polling++; |
|
#if 0 |
sc->sc_cmd = EOREAD4(sc, EHCI_USBCMD); |
OOO |
|
ctl = OREAD4(sc, EHCI_CONTROL) & ~EHCI_HCFS_MASK; |
cmd = sc->sc_cmd & ~(EHCI_CMD_ASE | EHCI_CMD_PSE); |
if (sc->sc_control == 0) { |
EOWRITE4(sc, EHCI_USBCMD, cmd); |
/* |
|
* Preserve register values, in case that APM BIOS |
for (i = 0; i < 100; i++) { |
* does not recover them. |
hcr = EOREAD4(sc, EHCI_USBSTS) & |
*/ |
(EHCI_STS_ASS | EHCI_STS_PSS); |
sc->sc_control = ctl; |
if (hcr == 0) |
sc->sc_intre = OREAD4(sc, EHCI_INTERRUPT_ENABLE); |
break; |
|
|
|
usb_delay_ms(&sc->sc_bus, 1); |
|
} |
|
if (hcr != 0) { |
|
printf("%s: reset timeout\n", |
|
USBDEVNAME(sc->sc_bus.bdev)); |
|
} |
|
|
|
cmd &= ~EHCI_CMD_RS; |
|
EOWRITE4(sc, EHCI_USBCMD, cmd); |
|
|
|
for (i = 0; i < 100; i++) { |
|
hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH; |
|
if (hcr == EHCI_STS_HCH) |
|
break; |
|
|
|
usb_delay_ms(&sc->sc_bus, 1); |
|
} |
|
if (hcr != EHCI_STS_HCH) { |
|
printf("%s: config timeout\n", |
|
USBDEVNAME(sc->sc_bus.bdev)); |
|
} |
} |
|
ctl |= EHCI_HCFS_SUSPEND; |
|
OWRITE4(sc, EHCI_CONTROL, ctl); |
|
#endif |
|
usb_delay_ms(&sc->sc_bus, USB_RESUME_WAIT); |
sc->sc_bus.use_polling--; |
sc->sc_bus.use_polling--; |
break; |
break; |
|
|
case PWR_RESUME: |
case PWR_RESUME: |
sc->sc_bus.use_polling++; |
sc->sc_bus.use_polling++; |
|
#if 0 |
/* restore things in case the bios sucks */ |
OOO |
EOWRITE4(sc, EHCI_CTRLDSSEGMENT, 0); |
/* Some broken BIOSes do not recover these values */ |
EOWRITE4(sc, EHCI_PERIODICLISTBASE, DMAADDR(&sc->sc_fldma, 0)); |
OWRITE4(sc, EHCI_HCCA, DMAADDR(&sc->sc_hccadma, 0)); |
EOWRITE4(sc, EHCI_ASYNCLISTADDR, |
OWRITE4(sc, EHCI_CONTROL_HEAD_ED, sc->sc_ctrl_head->physaddr); |
sc->sc_async_head->physaddr | EHCI_LINK_QH); |
OWRITE4(sc, EHCI_BULK_HEAD_ED, sc->sc_bulk_head->physaddr); |
EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); |
if (sc->sc_intre) |
|
OWRITE4(sc, EHCI_INTERRUPT_ENABLE, |
EOWRITE4(sc, EHCI_USBCMD, sc->sc_cmd); |
sc->sc_intre & (EHCI_ALL_INTRS | EHCI_MIE)); |
|
if (sc->sc_control) |
for (i = 0; i < 100; i++) { |
ctl = sc->sc_control; |
hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH; |
else |
if (hcr != EHCI_STS_HCH) |
ctl = OREAD4(sc, EHCI_CONTROL); |
break; |
ctl |= EHCI_HCFS_RESUME; |
|
OWRITE4(sc, EHCI_CONTROL, ctl); |
usb_delay_ms(&sc->sc_bus, 1); |
usb_delay_ms(&sc->sc_bus, USB_RESUME_DELAY); |
} |
ctl = (ctl & ~EHCI_HCFS_MASK) | EHCI_HCFS_OPERATIONAL; |
if (hcr == EHCI_STS_HCH) { |
OWRITE4(sc, EHCI_CONTROL, ctl); |
printf("%s: config timeout\n", |
usb_delay_ms(&sc->sc_bus, USB_RESUME_RECOVERY); |
USBDEVNAME(sc->sc_bus.bdev)); |
sc->sc_control = sc->sc_intre = 0; |
} |
#endif |
|
|
usb_delay_ms(&sc->sc_bus, USB_RESUME_WAIT); |
|
|
|
sc->sc_bus.use_polling--; |
sc->sc_bus.use_polling--; |
break; |
break; |
case PWR_SOFTSUSPEND: |
case PWR_SOFTSUSPEND: |
Line 1077 ehci_power(int why, void *v) |
|
Line 946 ehci_power(int why, void *v) |
|
break; |
break; |
} |
} |
splx(s); |
splx(s); |
|
|
#ifdef EHCI_DEBUG |
|
DPRINTF(("ehci_power: sc=%p\n", sc)); |
|
if (ehcidebug > 0) |
|
ehci_dump_regs(sc); |
|
#endif |
|
} |
} |
|
|
/* |
/* |
Line 1105 ehci_allocm(struct usbd_bus *bus, usb_dm |
|
Line 968 ehci_allocm(struct usbd_bus *bus, usb_dm |
|
usbd_status err; |
usbd_status err; |
|
|
err = usb_allocmem(&sc->sc_bus, size, 0, dma); |
err = usb_allocmem(&sc->sc_bus, size, 0, dma); |
if (err == USBD_NOMEM) |
|
err = usb_reserve_allocm(&sc->sc_dma_reserve, dma, size); |
|
#ifdef EHCI_DEBUG |
#ifdef EHCI_DEBUG |
if (err) |
if (err) |
printf("ehci_allocm: usb_allocmem()=%d\n", err); |
printf("ehci_allocm: usb_allocmem()=%d\n", err); |
Line 1119 ehci_freem(struct usbd_bus *bus, usb_dma |
|
Line 980 ehci_freem(struct usbd_bus *bus, usb_dma |
|
{ |
{ |
struct ehci_softc *sc = (struct ehci_softc *)bus; |
struct ehci_softc *sc = (struct ehci_softc *)bus; |
|
|
if (dma->block->flags & USB_DMA_RESERVE) { |
|
usb_reserve_freem(&((struct ehci_softc *)bus)->sc_dma_reserve, |
|
dma); |
|
return; |
|
} |
|
usb_freemem(&sc->sc_bus, dma); |
usb_freemem(&sc->sc_bus, dma); |
} |
} |
|
|
Line 1138 ehci_allocx(struct usbd_bus *bus) |
|
Line 994 ehci_allocx(struct usbd_bus *bus) |
|
SIMPLEQ_REMOVE_HEAD(&sc->sc_free_xfers, next); |
SIMPLEQ_REMOVE_HEAD(&sc->sc_free_xfers, next); |
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
if (xfer->busy_free != XFER_FREE) { |
if (xfer->busy_free != XFER_FREE) { |
printf("ehci_allocx: xfer=%p not free, 0x%08x\n", xfer, |
printf("uhci_allocx: xfer=%p not free, 0x%08x\n", xfer, |
xfer->busy_free); |
xfer->busy_free); |
} |
} |
#endif |
#endif |
Line 1146 ehci_allocx(struct usbd_bus *bus) |
|
Line 1002 ehci_allocx(struct usbd_bus *bus) |
|
xfer = malloc(sizeof(struct ehci_xfer), M_USB, M_NOWAIT); |
xfer = malloc(sizeof(struct ehci_xfer), M_USB, M_NOWAIT); |
} |
} |
if (xfer != NULL) { |
if (xfer != NULL) { |
memset(xfer, 0, sizeof(struct ehci_xfer)); |
memset(xfer, 0, sizeof (struct ehci_xfer)); |
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
EXFER(xfer)->isdone = 1; |
EXFER(xfer)->isdone = 1; |
xfer->busy_free = XFER_BUSY; |
xfer->busy_free = XFER_BUSY; |
Line 1186 ehci_device_clear_toggle(usbd_pipe_handl |
|
Line 1042 ehci_device_clear_toggle(usbd_pipe_handl |
|
if (ehcidebug) |
if (ehcidebug) |
usbd_dump_pipe(pipe); |
usbd_dump_pipe(pipe); |
#endif |
#endif |
epipe->nexttoggle = 0; |
epipe->sqh->qh.qh_qtd.qtd_status &= htole32(~EHCI_QTD_TOGGLE); |
} |
} |
|
|
Static void |
Static void |
Line 1253 ehci_dump_sqtds(ehci_soft_qtd_t *sqtd) |
|
Line 1109 ehci_dump_sqtds(ehci_soft_qtd_t *sqtd) |
|
stop = 0; |
stop = 0; |
for (i = 0; sqtd && i < 20 && !stop; sqtd = sqtd->nextqtd, i++) { |
for (i = 0; sqtd && i < 20 && !stop; sqtd = sqtd->nextqtd, i++) { |
ehci_dump_sqtd(sqtd); |
ehci_dump_sqtd(sqtd); |
stop = sqtd->qtd.qtd_next & htole32(EHCI_LINK_TERMINATE); |
stop = sqtd->qtd.qtd_next & EHCI_LINK_TERMINATE; |
} |
} |
if (sqtd) |
if (sqtd) |
printf("dump aborted, too many TDs\n"); |
printf("dump aborted, too many TDs\n"); |
Line 1337 ehci_open(usbd_pipe_handle pipe) |
|
Line 1193 ehci_open(usbd_pipe_handle pipe) |
|
ehci_soft_qh_t *sqh; |
ehci_soft_qh_t *sqh; |
usbd_status err; |
usbd_status err; |
int s; |
int s; |
int ival, speed, naks; |
int speed, naks; |
int hshubaddr, hshubport; |
|
|
|
DPRINTFN(1, ("ehci_open: pipe=%p, addr=%d, endpt=%d (%d)\n", |
DPRINTFN(1, ("ehci_open: pipe=%p, addr=%d, endpt=%d (%d)\n", |
pipe, addr, ed->bEndpointAddress, sc->sc_addr)); |
pipe, addr, ed->bEndpointAddress, sc->sc_addr)); |
|
|
if (dev->myhsport) { |
|
hshubaddr = dev->myhsport->parent->address; |
|
hshubport = dev->myhsport->portno; |
|
} else { |
|
hshubaddr = 0; |
|
hshubport = 0; |
|
} |
|
|
|
if (sc->sc_dying) |
if (sc->sc_dying) |
return (USBD_IOERROR); |
return (USBD_IOERROR); |
|
|
epipe->nexttoggle = 0; |
|
|
|
if (addr == sc->sc_addr) { |
if (addr == sc->sc_addr) { |
switch (ed->bEndpointAddress) { |
switch (ed->bEndpointAddress) { |
case USB_CONTROL_ENDPOINT: |
case USB_CONTROL_ENDPOINT: |
Line 1377 ehci_open(usbd_pipe_handle pipe) |
|
Line 1222 ehci_open(usbd_pipe_handle pipe) |
|
case USB_SPEED_HIGH: speed = EHCI_QH_SPEED_HIGH; break; |
case USB_SPEED_HIGH: speed = EHCI_QH_SPEED_HIGH; break; |
default: panic("ehci_open: bad device speed %d", dev->speed); |
default: panic("ehci_open: bad device speed %d", dev->speed); |
} |
} |
if (speed != EHCI_QH_SPEED_HIGH && xfertype == UE_ISOCHRONOUS) { |
|
printf("%s: *** WARNING: opening low/full speed isoc device, " |
|
"this does not work yet.\n", |
|
USBDEVNAME(sc->sc_bus.bdev)); |
|
DPRINTFN(1,("ehci_open: hshubaddr=%d hshubport=%d\n", |
|
hshubaddr, hshubport)); |
|
return USBD_INVAL; |
|
} |
|
|
|
naks = 8; /* XXX */ |
naks = 8; /* XXX */ |
sqh = ehci_alloc_sqh(sc); |
sqh = ehci_alloc_sqh(sc); |
if (sqh == NULL) |
if (sqh == NULL) |
Line 1393 ehci_open(usbd_pipe_handle pipe) |
|
Line 1229 ehci_open(usbd_pipe_handle pipe) |
|
/* qh_link filled when the QH is added */ |
/* qh_link filled when the QH is added */ |
sqh->qh.qh_endp = htole32( |
sqh->qh.qh_endp = htole32( |
EHCI_QH_SET_ADDR(addr) | |
EHCI_QH_SET_ADDR(addr) | |
EHCI_QH_SET_ENDPT(UE_GET_ADDR(ed->bEndpointAddress)) | |
EHCI_QH_SET_ENDPT(ed->bEndpointAddress) | |
EHCI_QH_SET_EPS(speed) | |
EHCI_QH_SET_EPS(speed) | /* XXX */ |
EHCI_QH_DTC | |
/* XXX EHCI_QH_DTC ? */ |
EHCI_QH_SET_MPL(UGETW(ed->wMaxPacketSize)) | |
EHCI_QH_SET_MPL(UGETW(ed->wMaxPacketSize)) | |
(speed != EHCI_QH_SPEED_HIGH && xfertype == UE_CONTROL ? |
(speed != EHCI_QH_SPEED_HIGH && xfertype == UE_CONTROL ? |
EHCI_QH_CTL : 0) | |
EHCI_QH_CTL : 0) | |
EHCI_QH_SET_NRL(naks) |
EHCI_QH_SET_NRL(naks) |
); |
); |
sqh->qh.qh_endphub = htole32( |
sqh->qh.qh_endphub = htole32( |
EHCI_QH_SET_MULT(1) | |
EHCI_QH_SET_MULT(1) |
EHCI_QH_SET_HUBA(hshubaddr) | |
/* XXX TT stuff */ |
EHCI_QH_SET_PORT(hshubport) | |
/* XXX interrupt mask */ |
EHCI_QH_SET_CMASK(0x08) | /* XXX */ |
|
EHCI_QH_SET_SMASK(xfertype == UE_INTERRUPT ? 0x02 : 0) |
|
); |
); |
sqh->qh.qh_curqtd = EHCI_NULL; |
sqh->qh.qh_curqtd = EHCI_NULL; |
/* Fill the overlay qTD */ |
/* Fill the overlay qTD */ |
Line 1439 ehci_open(usbd_pipe_handle pipe) |
|
Line 1273 ehci_open(usbd_pipe_handle pipe) |
|
break; |
break; |
case UE_INTERRUPT: |
case UE_INTERRUPT: |
pipe->methods = &ehci_device_intr_methods; |
pipe->methods = &ehci_device_intr_methods; |
ival = pipe->interval; |
return (USBD_INVAL); |
if (ival == USBD_DEFAULT_INTERVAL) |
|
ival = ed->bInterval; |
|
return (ehci_device_setintr(sc, sqh, ival)); |
|
case UE_ISOCHRONOUS: |
case UE_ISOCHRONOUS: |
pipe->methods = &ehci_device_isoc_methods; |
pipe->methods = &ehci_device_isoc_methods; |
return (USBD_INVAL); |
return (USBD_INVAL); |
Line 1501 ehci_rem_qh(ehci_softc_t *sc, ehci_soft_ |
|
Line 1332 ehci_rem_qh(ehci_softc_t *sc, ehci_soft_ |
|
void |
void |
ehci_set_qh_qtd(ehci_soft_qh_t *sqh, ehci_soft_qtd_t *sqtd) |
ehci_set_qh_qtd(ehci_soft_qh_t *sqh, ehci_soft_qtd_t *sqtd) |
{ |
{ |
int i; |
/* Halt while we are messing. */ |
u_int32_t status; |
sqh->qh.qh_qtd.qtd_status |= htole32(EHCI_QTD_HALTED); |
|
|
/* Save toggle bit and ping status. */ |
|
status = sqh->qh.qh_qtd.qtd_status & |
|
htole32(EHCI_QTD_TOGGLE_MASK | |
|
EHCI_QTD_SET_STATUS(EHCI_QTD_PINGSTATE)); |
|
/* Set HALTED to make hw leave it alone. */ |
|
sqh->qh.qh_qtd.qtd_status = |
|
htole32(EHCI_QTD_SET_STATUS(EHCI_QTD_HALTED)); |
|
sqh->qh.qh_curqtd = 0; |
sqh->qh.qh_curqtd = 0; |
sqh->qh.qh_qtd.qtd_next = htole32(sqtd->physaddr); |
sqh->qh.qh_qtd.qtd_next = htole32(sqtd->physaddr); |
sqh->qh.qh_qtd.qtd_altnext = 0; |
|
for (i = 0; i < EHCI_QTD_NBUFFERS; i++) |
|
sqh->qh.qh_qtd.qtd_buffer[i] = 0; |
|
sqh->sqtd = sqtd; |
sqh->sqtd = sqtd; |
/* Set !HALTED && !ACTIVE to start execution, preserve some fields */ |
/* Keep toggle, clear the rest, including length. */ |
sqh->qh.qh_qtd.qtd_status = status; |
sqh->qh.qh_qtd.qtd_status &= htole32(EHCI_QTD_TOGGLE); |
} |
} |
|
|
/* |
/* |
Line 1537 ehci_sync_hc(ehci_softc_t *sc) |
|
Line 1357 ehci_sync_hc(ehci_softc_t *sc) |
|
return; |
return; |
} |
} |
DPRINTFN(2,("ehci_sync_hc: enter\n")); |
DPRINTFN(2,("ehci_sync_hc: enter\n")); |
usb_lockmgr(&sc->sc_doorbell_lock, LK_EXCLUSIVE, NULL); /* get doorbell */ |
lockmgr(&sc->sc_doorbell_lock, LK_EXCLUSIVE, NULL); /* get doorbell */ |
s = splhardusb(); |
s = splhardusb(); |
/* ask for doorbell */ |
/* ask for doorbell */ |
EOWRITE4(sc, EHCI_USBCMD, EOREAD4(sc, EHCI_USBCMD) | EHCI_CMD_IAAD); |
EOWRITE4(sc, EHCI_USBCMD, EOREAD4(sc, EHCI_USBCMD) | EHCI_CMD_IAAD); |
Line 1547 ehci_sync_hc(ehci_softc_t *sc) |
|
Line 1367 ehci_sync_hc(ehci_softc_t *sc) |
|
DPRINTFN(1,("ehci_sync_hc: cmd=0x%08x sts=0x%08x\n", |
DPRINTFN(1,("ehci_sync_hc: cmd=0x%08x sts=0x%08x\n", |
EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS))); |
EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS))); |
splx(s); |
splx(s); |
usb_lockmgr(&sc->sc_doorbell_lock, LK_RELEASE, NULL); /* release doorbell */ |
lockmgr(&sc->sc_doorbell_lock, LK_RELEASE, NULL); /* release doorbell */ |
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
if (error) |
if (error) |
printf("ehci_sync_hc: tsleep() = %d\n", error); |
printf("ehci_sync_hc: tsleep() = %d\n", error); |
Line 1630 Static usb_hub_descriptor_t ehci_hubd = |
|
Line 1450 Static usb_hub_descriptor_t ehci_hubd = |
|
}; |
}; |
|
|
Static int |
Static int |
ehci_str(usb_string_descriptor_t *p, int l, const char *s) |
ehci_str(p, l, s) |
|
usb_string_descriptor_t *p; |
|
int l; |
|
char *s; |
{ |
{ |
int i; |
int i; |
|
|
Line 1686 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 1509 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
#endif |
#endif |
req = &xfer->request; |
req = &xfer->request; |
|
|
DPRINTFN(4,("ehci_root_ctrl_start: type=0x%02x request=%02x\n", |
DPRINTFN(4,("ehci_root_ctrl_control type=0x%02x request=%02x\n", |
req->bmRequestType, req->bRequest)); |
req->bmRequestType, req->bRequest)); |
|
|
len = UGETW(req->wLength); |
len = UGETW(req->wLength); |
Line 1713 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 1536 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
} |
} |
break; |
break; |
case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): |
case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): |
DPRINTFN(8,("ehci_root_ctrl_start: wValue=0x%04x\n", value)); |
DPRINTFN(8,("ehci_root_ctrl_control wValue=0x%04x\n", value)); |
switch(value >> 8) { |
switch(value >> 8) { |
case UDESC_DEVICE: |
case UDESC_DEVICE: |
if ((value & 0xff) != 0) { |
if ((value & 0xff) != 0) { |
Line 1767 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 1590 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
*(u_int8_t *)buf = 0; |
*(u_int8_t *)buf = 0; |
totlen = 1; |
totlen = 1; |
switch (value & 0xff) { |
switch (value & 0xff) { |
case 0: /* Language table */ |
|
totlen = ehci_str(buf, len, "\001"); |
|
break; |
|
case 1: /* Vendor */ |
case 1: /* Vendor */ |
totlen = ehci_str(buf, len, sc->sc_vendor); |
totlen = ehci_str(buf, len, sc->sc_vendor); |
break; |
break; |
Line 1831 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 1651 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE): |
case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE): |
break; |
break; |
case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): |
case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): |
DPRINTFN(4, ("ehci_root_ctrl_start: UR_CLEAR_PORT_FEATURE " |
DPRINTFN(8, ("ehci_root_ctrl_control: UR_CLEAR_PORT_FEATURE " |
"port=%d feature=%d\n", |
"port=%d feature=%d\n", |
index, value)); |
index, value)); |
if (index < 1 || index > sc->sc_noport) { |
if (index < 1 || index > sc->sc_noport) { |
Line 1839 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 1659 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
goto ret; |
goto ret; |
} |
} |
port = EHCI_PORTSC(index); |
port = EHCI_PORTSC(index); |
v = EOREAD4(sc, port); |
v = EOREAD4(sc, port) &~ EHCI_PS_CLEAR; |
DPRINTFN(4, ("ehci_root_ctrl_start: portsc=0x%08x\n", v)); |
|
v &= ~EHCI_PS_CLEAR; |
|
switch(value) { |
switch(value) { |
case UHF_PORT_ENABLE: |
case UHF_PORT_ENABLE: |
EOWRITE4(sc, port, v &~ EHCI_PS_PE); |
EOWRITE4(sc, port, v &~ EHCI_PS_PE); |
Line 1850 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 1668 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
EOWRITE4(sc, port, v &~ EHCI_PS_SUSP); |
EOWRITE4(sc, port, v &~ EHCI_PS_SUSP); |
break; |
break; |
case UHF_PORT_POWER: |
case UHF_PORT_POWER: |
if (sc->sc_hasppc) |
EOWRITE4(sc, port, v &~ EHCI_PS_PP); |
EOWRITE4(sc, port, v &~ EHCI_PS_PP); |
|
break; |
break; |
case UHF_PORT_TEST: |
case UHF_PORT_TEST: |
DPRINTFN(2,("ehci_root_ctrl_start: clear port test " |
DPRINTFN(2,("ehci_root_ctrl_transfer: clear port test " |
"%d\n", index)); |
"%d\n", index)); |
break; |
break; |
case UHF_PORT_INDICATOR: |
case UHF_PORT_INDICATOR: |
DPRINTFN(2,("ehci_root_ctrl_start: clear port ind " |
DPRINTFN(2,("ehci_root_ctrl_transfer: clear port ind " |
"%d\n", index)); |
"%d\n", index)); |
EOWRITE4(sc, port, v &~ EHCI_PS_PIC); |
EOWRITE4(sc, port, v &~ EHCI_PS_PIC); |
break; |
break; |
Line 1875 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 1692 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
EOWRITE4(sc, port, v | EHCI_PS_OCC); |
EOWRITE4(sc, port, v | EHCI_PS_OCC); |
break; |
break; |
case UHF_C_PORT_RESET: |
case UHF_C_PORT_RESET: |
sc->sc_isreset[index] = 0; |
sc->sc_isreset = 0; |
break; |
break; |
default: |
default: |
err = USBD_IOERROR; |
err = USBD_IOERROR; |
Line 1898 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 1715 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
#endif |
#endif |
break; |
break; |
case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): |
case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): |
if ((value & 0xff) != 0) { |
if (value != 0) { |
err = USBD_IOERROR; |
err = USBD_IOERROR; |
goto ret; |
goto ret; |
} |
} |
Line 1907 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 1724 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
v = EOREAD4(sc, EHCI_HCSPARAMS); |
v = EOREAD4(sc, EHCI_HCSPARAMS); |
USETW(hubd.wHubCharacteristics, |
USETW(hubd.wHubCharacteristics, |
EHCI_HCS_PPC(v) ? UHD_PWR_INDIVIDUAL : UHD_PWR_NO_SWITCH | |
EHCI_HCS_PPC(v) ? UHD_PWR_INDIVIDUAL : UHD_PWR_NO_SWITCH | |
EHCI_HCS_P_INDICATOR(EREAD4(sc, EHCI_HCSPARAMS)) |
EHCI_HCS_P_INCICATOR(EREAD4(sc, EHCI_HCSPARAMS)) |
? UHD_PORT_IND : 0); |
? UHD_PORT_IND : 0); |
hubd.bPwrOn2PwrGood = 200; /* XXX can't find out? */ |
hubd.bPwrOn2PwrGood = 200; /* XXX can't find out? */ |
for (i = 0, l = sc->sc_noport; l > 0; i++, l -= 8, v >>= 8) |
for (i = 0, l = sc->sc_noport; l > 0; i++, l -= 8, v >>= 8) |
Line 1926 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 1743 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
totlen = len; |
totlen = len; |
break; |
break; |
case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): |
case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): |
DPRINTFN(8,("ehci_root_ctrl_start: get port status i=%d\n", |
DPRINTFN(8,("ehci_root_ctrl_transfer: get port status i=%d\n", |
index)); |
index)); |
if (index < 1 || index > sc->sc_noport) { |
if (index < 1 || index > sc->sc_noport) { |
err = USBD_IOERROR; |
err = USBD_IOERROR; |
Line 1937 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 1754 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
goto ret; |
goto ret; |
} |
} |
v = EOREAD4(sc, EHCI_PORTSC(index)); |
v = EOREAD4(sc, EHCI_PORTSC(index)); |
DPRINTFN(8,("ehci_root_ctrl_start: port status=0x%04x\n", |
DPRINTFN(8,("ehci_root_ctrl_transfer: port status=0x%04x\n", |
v)); |
v)); |
i = UPS_HIGH_SPEED; |
i = UPS_HIGH_SPEED; |
if (v & EHCI_PS_CS) i |= UPS_CURRENT_CONNECT_STATUS; |
if (v & EHCI_PS_CS) i |= UPS_CURRENT_CONNECT_STATUS; |
Line 1951 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 1768 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
if (v & EHCI_PS_CSC) i |= UPS_C_CONNECT_STATUS; |
if (v & EHCI_PS_CSC) i |= UPS_C_CONNECT_STATUS; |
if (v & EHCI_PS_PEC) i |= UPS_C_PORT_ENABLED; |
if (v & EHCI_PS_PEC) i |= UPS_C_PORT_ENABLED; |
if (v & EHCI_PS_OCC) i |= UPS_C_OVERCURRENT_INDICATOR; |
if (v & EHCI_PS_OCC) i |= UPS_C_OVERCURRENT_INDICATOR; |
if (sc->sc_isreset[index]) i |= UPS_C_PORT_RESET; |
if (sc->sc_isreset) i |= UPS_C_PORT_RESET; |
USETW(ps.wPortChange, i); |
USETW(ps.wPortChange, i); |
l = min(len, sizeof ps); |
l = min(len, sizeof ps); |
memcpy(buf, &ps, l); |
memcpy(buf, &ps, l); |
Line 1968 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 1785 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
goto ret; |
goto ret; |
} |
} |
port = EHCI_PORTSC(index); |
port = EHCI_PORTSC(index); |
v = EOREAD4(sc, port); |
v = EOREAD4(sc, port) &~ EHCI_PS_CLEAR; |
DPRINTFN(4, ("ehci_root_ctrl_start: portsc=0x%08x\n", v)); |
|
v &= ~EHCI_PS_CLEAR; |
|
switch(value) { |
switch(value) { |
case UHF_PORT_ENABLE: |
case UHF_PORT_ENABLE: |
EOWRITE4(sc, port, v | EHCI_PS_PE); |
EOWRITE4(sc, port, v | EHCI_PS_PE); |
Line 1979 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 1794 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
EOWRITE4(sc, port, v | EHCI_PS_SUSP); |
EOWRITE4(sc, port, v | EHCI_PS_SUSP); |
break; |
break; |
case UHF_PORT_RESET: |
case UHF_PORT_RESET: |
DPRINTFN(5,("ehci_root_ctrl_start: reset port %d\n", |
DPRINTFN(5,("ehci_root_ctrl_transfer: reset port %d\n", |
index)); |
index)); |
if (EHCI_PS_IS_LOWSPEED(v)) { |
if (EHCI_PS_IS_LOWSPEED(v)) { |
/* Low speed device, give up ownership. */ |
/* Low speed device, give up ownership. */ |
Line 2015 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 1830 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
ehci_disown(sc, index, 0); |
ehci_disown(sc, index, 0); |
break; |
break; |
} |
} |
sc->sc_isreset[index] = 1; |
sc->sc_isreset = 1; |
DPRINTF(("ehci port %d reset, status = 0x%08x\n", |
DPRINTF(("ehci port %d reset, status = 0x%08x\n", |
index, v)); |
index, v)); |
break; |
break; |
case UHF_PORT_POWER: |
case UHF_PORT_POWER: |
DPRINTFN(2,("ehci_root_ctrl_start: set port power " |
DPRINTFN(2,("ehci_root_ctrl_transfer: set port power " |
"%d (has PPC = %d)\n", index, |
"%d\n", index)); |
sc->sc_hasppc)); |
EOWRITE4(sc, port, v | EHCI_PS_PP); |
if (sc->sc_hasppc) |
|
EOWRITE4(sc, port, v | EHCI_PS_PP); |
|
break; |
break; |
case UHF_PORT_TEST: |
case UHF_PORT_TEST: |
DPRINTFN(2,("ehci_root_ctrl_start: set port test " |
DPRINTFN(2,("ehci_root_ctrl_transfer: set port test " |
"%d\n", index)); |
"%d\n", index)); |
break; |
break; |
case UHF_PORT_INDICATOR: |
case UHF_PORT_INDICATOR: |
DPRINTFN(2,("ehci_root_ctrl_start: set port ind " |
DPRINTFN(2,("ehci_root_ctrl_transfer: set port ind " |
"%d\n", index)); |
"%d\n", index)); |
EOWRITE4(sc, port, v | EHCI_PS_PIC); |
EOWRITE4(sc, port, v | EHCI_PS_PIC); |
break; |
break; |
Line 2271 ehci_alloc_sqtd_chain(struct ehci_pipe * |
|
Line 2084 ehci_alloc_sqtd_chain(struct ehci_pipe * |
|
ehci_soft_qtd_t *next, *cur; |
ehci_soft_qtd_t *next, *cur; |
ehci_physaddr_t dataphys, dataphyspage, dataphyslastpage, nextphys; |
ehci_physaddr_t dataphys, dataphyspage, dataphyslastpage, nextphys; |
u_int32_t qtdstatus; |
u_int32_t qtdstatus; |
int len, curlen, mps; |
int len, curlen; |
int i, tog; |
int i; |
usb_dma_t *dma = &xfer->dmabuf; |
usb_dma_t *dma = &xfer->dmabuf; |
u_int16_t flags = xfer->flags; |
|
|
|
DPRINTFN(alen<4*4096,("ehci_alloc_sqtd_chain: start len=%d\n", alen)); |
DPRINTFN(alen<4*4096,("ehci_alloc_sqtd_chain: start len=%d\n", alen)); |
|
|
len = alen; |
len = alen; |
dataphys = DMAADDR(dma, 0); |
dataphys = DMAADDR(dma, 0); |
dataphyslastpage = EHCI_PAGE(dataphys + len - 1); |
dataphyslastpage = EHCI_PAGE(dataphys + len - 1); |
qtdstatus = EHCI_QTD_ACTIVE | |
qtdstatus = htole32( |
|
EHCI_QTD_ACTIVE | |
EHCI_QTD_SET_PID(rd ? EHCI_QTD_PID_IN : EHCI_QTD_PID_OUT) | |
EHCI_QTD_SET_PID(rd ? EHCI_QTD_PID_IN : EHCI_QTD_PID_OUT) | |
EHCI_QTD_SET_CERR(3) |
EHCI_QTD_SET_CERR(3) |
/* IOC set below */ |
/* IOC set below */ |
/* BYTES set below */ |
/* BYTES set below */ |
; |
/* XXX Data toggle */ |
mps = UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize); |
); |
tog = epipe->nexttoggle; |
|
qtdstatus |= EHCI_QTD_SET_TOGGLE(tog); |
|
|
|
cur = ehci_alloc_sqtd(sc); |
cur = ehci_alloc_sqtd(sc); |
*sp = cur; |
*sp = cur; |
Line 2317 ehci_alloc_sqtd_chain(struct ehci_pipe * |
|
Line 2128 ehci_alloc_sqtd_chain(struct ehci_pipe * |
|
curlen = len; |
curlen = len; |
} |
} |
#endif |
#endif |
|
|
|
/* XXX true for EHCI? */ |
/* the length must be a multiple of the max size */ |
/* the length must be a multiple of the max size */ |
curlen -= curlen % mps; |
curlen -= curlen % UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize); |
DPRINTFN(1,("ehci_alloc_sqtd_chain: multiple QTDs, " |
DPRINTFN(1,("ehci_alloc_sqtd_chain: multiple QTDs, " |
"curlen=%d\n", curlen)); |
"curlen=%d\n", curlen)); |
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
if (curlen == 0) |
if (curlen == 0) |
panic("ehci_alloc_sqtd_chain: curlen == 0"); |
panic("ehci_alloc_std: curlen == 0"); |
#endif |
#endif |
} |
} |
DPRINTFN(4,("ehci_alloc_sqtd_chain: dataphys=0x%08x " |
DPRINTFN(4,("ehci_alloc_sqtd_chain: dataphys=0x%08x " |
Line 2332 ehci_alloc_sqtd_chain(struct ehci_pipe * |
|
Line 2145 ehci_alloc_sqtd_chain(struct ehci_pipe * |
|
len, curlen)); |
len, curlen)); |
len -= curlen; |
len -= curlen; |
|
|
/* |
if (len != 0) { |
* Allocate another transfer if there's more data left, |
|
* or if force last short transfer flag is set and we're |
|
* allocating a multiple of the max packet size. |
|
*/ |
|
if (len != 0 || |
|
((curlen % mps) == 0 && !rd && curlen != 0 && |
|
(flags & USBD_FORCE_SHORT_XFER))) { |
|
next = ehci_alloc_sqtd(sc); |
next = ehci_alloc_sqtd(sc); |
if (next == NULL) |
if (next == NULL) |
goto nomem; |
goto nomem; |
nextphys = htole32(next->physaddr); |
nextphys = next->physaddr; |
} else { |
} else { |
next = NULL; |
next = NULL; |
nextphys = EHCI_NULL; |
nextphys = EHCI_NULL; |
} |
} |
|
|
for (i = 0; i * EHCI_PAGE_SIZE < |
for (i = 0; i * EHCI_PAGE_SIZE < curlen; i++) { |
curlen + EHCI_PAGE_OFFSET(dataphys); i++) { |
|
ehci_physaddr_t a = dataphys + i * EHCI_PAGE_SIZE; |
ehci_physaddr_t a = dataphys + i * EHCI_PAGE_SIZE; |
if (i != 0) /* use offset only in first buffer */ |
if (i != 0) /* use offset only in first buffer */ |
a = EHCI_PAGE(a); |
a = EHCI_PAGE(a); |
Line 2364 ehci_alloc_sqtd_chain(struct ehci_pipe * |
|
Line 2169 ehci_alloc_sqtd_chain(struct ehci_pipe * |
|
#endif |
#endif |
} |
} |
cur->nextqtd = next; |
cur->nextqtd = next; |
cur->qtd.qtd_next = cur->qtd.qtd_altnext = nextphys; |
cur->qtd.qtd_next = cur->qtd.qtd_altnext = htole32(nextphys); |
cur->qtd.qtd_status = |
cur->qtd.qtd_status = |
htole32(qtdstatus | EHCI_QTD_SET_BYTES(curlen)); |
qtdstatus | htole32(EHCI_QTD_SET_BYTES(curlen)); |
cur->xfer = xfer; |
cur->xfer = xfer; |
cur->len = curlen; |
cur->len = curlen; |
DPRINTFN(10,("ehci_alloc_sqtd_chain: cbp=0x%08x end=0x%08x\n", |
DPRINTFN(10,("ehci_alloc_sqtd_chain: cbp=0x%08x end=0x%08x\n", |
dataphys, dataphys + curlen)); |
dataphys, dataphys + curlen)); |
/* adjust the toggle based on the number of packets in this |
if (len == 0) |
qtd */ |
|
if (((curlen + mps - 1) / mps) & 1) { |
|
tog ^= 1; |
|
qtdstatus ^= EHCI_QTD_TOGGLE_MASK; |
|
} |
|
if (next == NULL) |
|
break; |
break; |
DPRINTFN(10,("ehci_alloc_sqtd_chain: extend chain\n")); |
DPRINTFN(10,("ehci_alloc_sqtd_chain: extend chain\n")); |
dataphys += curlen; |
dataphys += curlen; |
Line 2385 ehci_alloc_sqtd_chain(struct ehci_pipe * |
|
Line 2184 ehci_alloc_sqtd_chain(struct ehci_pipe * |
|
} |
} |
cur->qtd.qtd_status |= htole32(EHCI_QTD_IOC); |
cur->qtd.qtd_status |= htole32(EHCI_QTD_IOC); |
*ep = cur; |
*ep = cur; |
epipe->nexttoggle = tog; |
|
|
|
DPRINTFN(10,("ehci_alloc_sqtd_chain: return sqtd=%p sqtdend=%p\n", |
DPRINTFN(10,("ehci_alloc_sqtd_chain: return sqtd=%p sqtdend=%p\n", |
*sp, *ep)); |
*sp, *ep)); |
Line 2457 ehci_abort_xfer(usbd_xfer_handle xfer, u |
|
Line 2255 ehci_abort_xfer(usbd_xfer_handle xfer, u |
|
u_int32_t qhstatus; |
u_int32_t qhstatus; |
int s; |
int s; |
int hit; |
int hit; |
int wake; |
|
|
|
DPRINTF(("ehci_abort_xfer: xfer=%p pipe=%p\n", xfer, epipe)); |
DPRINTF(("ehci_abort_xfer: xfer=%p pipe=%p\n", xfer, epipe)); |
|
|
Line 2475 ehci_abort_xfer(usbd_xfer_handle xfer, u |
|
Line 2272 ehci_abort_xfer(usbd_xfer_handle xfer, u |
|
panic("ehci_abort_xfer: not in process context"); |
panic("ehci_abort_xfer: not in process context"); |
|
|
/* |
/* |
* If an abort is already in progress then just wait for it to |
|
* complete and return. |
|
*/ |
|
if (xfer->hcflags & UXFER_ABORTING) { |
|
DPRINTFN(2, ("ehci_abort_xfer: already aborting\n")); |
|
#ifdef DIAGNOSTIC |
|
if (status == USBD_TIMEOUT) |
|
printf("ehci_abort_xfer: TIMEOUT while aborting\n"); |
|
#endif |
|
/* Override the status which might be USBD_TIMEOUT. */ |
|
xfer->status = status; |
|
DPRINTFN(2, ("ehci_abort_xfer: waiting for abort to finish\n")); |
|
xfer->hcflags |= UXFER_ABORTWAIT; |
|
while (xfer->hcflags & UXFER_ABORTING) |
|
tsleep(&xfer->hcflags, PZERO, "ehciaw", 0); |
|
return; |
|
} |
|
xfer->hcflags |= UXFER_ABORTING; |
|
|
|
/* |
|
* Step 1: Make interrupt routine and hardware ignore xfer. |
* Step 1: Make interrupt routine and hardware ignore xfer. |
*/ |
*/ |
s = splusb(); |
s = splusb(); |
Line 2516 ehci_abort_xfer(usbd_xfer_handle xfer, u |
|
Line 2293 ehci_abort_xfer(usbd_xfer_handle xfer, u |
|
*/ |
*/ |
ehci_sync_hc(sc); |
ehci_sync_hc(sc); |
s = splusb(); |
s = splusb(); |
#ifdef USB_USE_SOFTINTR |
|
sc->sc_softwake = 1; |
sc->sc_softwake = 1; |
#endif /* USB_USE_SOFTINTR */ |
|
usb_schedsoftintr(&sc->sc_bus); |
usb_schedsoftintr(&sc->sc_bus); |
#ifdef USB_USE_SOFTINTR |
|
tsleep(&sc->sc_softwake, PZERO, "ehciab", 0); |
tsleep(&sc->sc_softwake, PZERO, "ehciab", 0); |
#endif /* USB_USE_SOFTINTR */ |
|
splx(s); |
splx(s); |
|
|
/* |
/* |
Line 2556 ehci_abort_xfer(usbd_xfer_handle xfer, u |
|
Line 2329 ehci_abort_xfer(usbd_xfer_handle xfer, u |
|
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
exfer->isdone = 1; |
exfer->isdone = 1; |
#endif |
#endif |
wake = xfer->hcflags & UXFER_ABORTWAIT; |
|
xfer->hcflags &= ~(UXFER_ABORTING | UXFER_ABORTWAIT); |
|
usb_transfer_complete(xfer); |
usb_transfer_complete(xfer); |
if (wake) |
|
wakeup(&xfer->hcflags); |
|
|
|
splx(s); |
splx(s); |
#undef exfer |
#undef exfer |
Line 2705 ehci_device_request(usbd_xfer_handle xfe |
|
Line 2474 ehci_device_request(usbd_xfer_handle xfe |
|
isread = req->bmRequestType & UT_READ; |
isread = req->bmRequestType & UT_READ; |
len = UGETW(req->wLength); |
len = UGETW(req->wLength); |
|
|
DPRINTFN(3,("ehci_device_request: type=0x%02x, request=0x%02x, " |
DPRINTFN(3,("ehci_device_control type=0x%02x, request=0x%02x, " |
"wValue=0x%04x, wIndex=0x%04x len=%d, addr=%d, endpt=%d\n", |
"wValue=0x%04x, wIndex=0x%04x len=%d, addr=%d, endpt=%d\n", |
req->bmRequestType, req->bRequest, UGETW(req->wValue), |
req->bmRequestType, req->bRequest, UGETW(req->wValue), |
UGETW(req->wIndex), len, addr, |
UGETW(req->wIndex), len, addr, |
Line 2725 ehci_device_request(usbd_xfer_handle xfe |
|
Line 2494 ehci_device_request(usbd_xfer_handle xfe |
|
sqh = epipe->sqh; |
sqh = epipe->sqh; |
epipe->u.ctl.length = len; |
epipe->u.ctl.length = len; |
|
|
/* Update device address and length since they may have changed |
/* XXX |
during the setup of the control pipe in usbd_new_device(). */ |
* Since we're messing with the QH we must know the HC is in sync. |
|
* This needs to go away since it slows down control transfers. |
|
* Removing it entails: |
|
* - fill the QH only once with addr & wMaxPacketSize |
|
* - put the correct data toggles in the qtds and set DTC |
|
*/ |
|
/* ehci_sync_hc(sc); */ |
|
/* Update device address and length since they may have changed. */ |
/* XXX This only needs to be done once, but it's too early in open. */ |
/* XXX This only needs to be done once, but it's too early in open. */ |
/* XXXX Should not touch ED here! */ |
/* XXXX Should not touch ED here! */ |
sqh->qh.qh_endp = |
sqh->qh.qh_endp = |
(sqh->qh.qh_endp & htole32(~(EHCI_QH_ADDRMASK | EHCI_QH_MPLMASK))) | |
(sqh->qh.qh_endp & htole32(~(EHCI_QH_ADDRMASK | EHCI_QG_MPLMASK))) | |
htole32( |
htole32( |
EHCI_QH_SET_ADDR(addr) | |
EHCI_QH_SET_ADDR(addr) | |
|
/* EHCI_QH_DTC | */ |
EHCI_QH_SET_MPL(UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize)) |
EHCI_QH_SET_MPL(UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize)) |
); |
); |
|
/* Clear toggle */ |
|
sqh->qh.qh_qtd.qtd_status &= htole32(~EHCI_QTD_TOGGLE); |
|
|
/* Set up data transaction */ |
/* Set up data transaction */ |
if (len != 0) { |
if (len != 0) { |
ehci_soft_qtd_t *end; |
ehci_soft_qtd_t *end; |
|
|
/* Start toggle at 1. */ |
|
epipe->nexttoggle = 1; |
|
err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, |
err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, |
&next, &end); |
&next, &end); |
if (err) |
if (err) |
goto bad3; |
goto bad3; |
end->qtd.qtd_status &= htole32(~EHCI_QTD_IOC); |
|
end->nextqtd = stat; |
end->nextqtd = stat; |
end->qtd.qtd_next = |
end->qtd.qtd_next = |
end->qtd.qtd_altnext = htole32(stat->physaddr); |
end->qtd.qtd_altnext = htole32(stat->physaddr); |
|
/* Start toggle at 1. */ |
|
/*next->qtd.td_flags |= htole32(EHCI_QTD_TOGGLE);*/ |
} else { |
} else { |
next = stat; |
next = stat; |
} |
} |
|
|
memcpy(KERNADDR(&epipe->u.ctl.reqdma, 0), req, sizeof *req); |
memcpy(KERNADDR(&epipe->u.ctl.reqdma, 0), req, sizeof *req); |
|
|
/* Clear toggle */ |
|
setup->qtd.qtd_status = htole32( |
setup->qtd.qtd_status = htole32( |
EHCI_QTD_ACTIVE | |
EHCI_QTD_ACTIVE | |
EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) | |
EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) | |
EHCI_QTD_SET_CERR(3) | |
EHCI_QTD_SET_CERR(3) | |
EHCI_QTD_SET_TOGGLE(0) | |
|
EHCI_QTD_SET_BYTES(sizeof *req) |
EHCI_QTD_SET_BYTES(sizeof *req) |
); |
); |
setup->qtd.qtd_buffer[0] = htole32(DMAADDR(&epipe->u.ctl.reqdma, 0)); |
setup->qtd.qtd_buffer[0] = htole32(DMAADDR(&epipe->u.ctl.reqdma, 0)); |
Line 2775 ehci_device_request(usbd_xfer_handle xfe |
|
Line 2551 ehci_device_request(usbd_xfer_handle xfe |
|
EHCI_QTD_ACTIVE | |
EHCI_QTD_ACTIVE | |
EHCI_QTD_SET_PID(isread ? EHCI_QTD_PID_OUT : EHCI_QTD_PID_IN) | |
EHCI_QTD_SET_PID(isread ? EHCI_QTD_PID_OUT : EHCI_QTD_PID_IN) | |
EHCI_QTD_SET_CERR(3) | |
EHCI_QTD_SET_CERR(3) | |
EHCI_QTD_SET_TOGGLE(1) | |
|
EHCI_QTD_IOC |
EHCI_QTD_IOC |
); |
); |
stat->qtd.qtd_buffer[0] = 0; /* XXX not needed? */ |
stat->qtd.qtd_buffer[0] = 0; /* XXX not needed? */ |
Line 2868 ehci_device_bulk_start(usbd_xfer_handle |
|
Line 2643 ehci_device_bulk_start(usbd_xfer_handle |
|
int len, isread, endpt; |
int len, isread, endpt; |
int s; |
int s; |
|
|
DPRINTFN(2, ("ehci_device_bulk_start: xfer=%p len=%d flags=%d\n", |
DPRINTFN(2, ("ehci_device_bulk_transfer: xfer=%p len=%d flags=%d\n", |
xfer, xfer->length, xfer->flags)); |
xfer, xfer->length, xfer->flags)); |
|
|
if (sc->sc_dying) |
if (sc->sc_dying) |
Line 2876 ehci_device_bulk_start(usbd_xfer_handle |
|
Line 2651 ehci_device_bulk_start(usbd_xfer_handle |
|
|
|
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
if (xfer->rqflags & URQ_REQUEST) |
if (xfer->rqflags & URQ_REQUEST) |
panic("ehci_device_bulk_start: a request"); |
panic("ehci_device_bulk_transfer: a request"); |
#endif |
#endif |
|
|
len = xfer->length; |
len = xfer->length; |
Line 2897 ehci_device_bulk_start(usbd_xfer_handle |
|
Line 2672 ehci_device_bulk_start(usbd_xfer_handle |
|
|
|
#ifdef EHCI_DEBUG |
#ifdef EHCI_DEBUG |
if (ehcidebug > 5) { |
if (ehcidebug > 5) { |
DPRINTF(("ehci_device_bulk_start: data(1)\n")); |
DPRINTF(("ehci_device_bulk_transfer: data(1)\n")); |
ehci_dump_sqh(sqh); |
ehci_dump_sqh(sqh); |
ehci_dump_sqtds(data); |
ehci_dump_sqtds(data); |
} |
} |
Line 2908 ehci_device_bulk_start(usbd_xfer_handle |
|
Line 2683 ehci_device_bulk_start(usbd_xfer_handle |
|
exfer->sqtdend = dataend; |
exfer->sqtdend = dataend; |
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
if (!exfer->isdone) { |
if (!exfer->isdone) { |
printf("ehci_device_bulk_start: not done, ex=%p\n", exfer); |
printf("ehci_device_bulk_transfer: not done, ex=%p\n", exfer); |
} |
} |
exfer->isdone = 0; |
exfer->isdone = 0; |
#endif |
#endif |
Line 2925 ehci_device_bulk_start(usbd_xfer_handle |
|
Line 2700 ehci_device_bulk_start(usbd_xfer_handle |
|
|
|
#ifdef EHCI_DEBUG |
#ifdef EHCI_DEBUG |
if (ehcidebug > 10) { |
if (ehcidebug > 10) { |
DPRINTF(("ehci_device_bulk_start: data(2)\n")); |
DPRINTF(("ehci_device_bulk_transfer: data(2)\n")); |
delay(10000); |
delay(10000); |
DPRINTF(("ehci_device_bulk_start: data(3)\n")); |
DPRINTF(("ehci_device_bulk_transfer: data(3)\n")); |
ehci_dump_regs(sc); |
ehci_dump_regs(sc); |
#if 0 |
#if 0 |
printf("async_head:\n"); |
printf("async_head:\n"); |
Line 2985 ehci_device_bulk_done(usbd_xfer_handle x |
|
Line 2760 ehci_device_bulk_done(usbd_xfer_handle x |
|
|
|
/************************/ |
/************************/ |
|
|
Static usbd_status |
Static usbd_status ehci_device_intr_transfer(usbd_xfer_handle xfer) { return USBD_IOERROR; } |
ehci_device_setintr(ehci_softc_t *sc, ehci_soft_qh_t *sqh, int ival) |
Static usbd_status ehci_device_intr_start(usbd_xfer_handle xfer) { return USBD_IOERROR; } |
{ |
Static void ehci_device_intr_abort(usbd_xfer_handle xfer) { } |
struct ehci_soft_islot *isp; |
Static void ehci_device_intr_close(usbd_pipe_handle pipe) { } |
int islot, lev; |
Static void ehci_device_intr_done(usbd_xfer_handle xfer) { } |
|
|
/* Find a poll rate that is large enough. */ |
|
for (lev = EHCI_IPOLLRATES - 1; lev > 0; lev--) |
|
if (EHCI_ILEV_IVAL(lev) <= ival) |
|
break; |
|
|
|
/* Pick an interrupt slot at the right level. */ |
|
/* XXX could do better than picking at random */ |
|
sc->sc_rand = (sc->sc_rand + 191) % sc->sc_flsize; |
|
islot = EHCI_IQHIDX(lev, sc->sc_rand); |
|
|
|
sqh->islot = islot; |
|
isp = &sc->sc_islots[islot]; |
|
ehci_add_qh(sqh, isp->sqh); |
|
|
|
return (USBD_NORMAL_COMPLETION); |
|
} |
|
|
|
Static usbd_status |
|
ehci_device_intr_transfer(usbd_xfer_handle xfer) |
|
{ |
|
usbd_status err; |
|
|
|
/* Insert last in queue. */ |
|
err = usb_insert_transfer(xfer); |
|
if (err) |
|
return (err); |
|
|
|
/* |
|
* Pipe isn't running (otherwise err would be USBD_INPROG), |
|
* so start it first. |
|
*/ |
|
return (ehci_device_intr_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); |
|
} |
|
|
|
Static usbd_status |
|
ehci_device_intr_start(usbd_xfer_handle xfer) |
|
{ |
|
#define exfer EXFER(xfer) |
|
struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; |
|
usbd_device_handle dev = xfer->pipe->device; |
|
ehci_softc_t *sc = (ehci_softc_t *)dev->bus; |
|
ehci_soft_qtd_t *data, *dataend; |
|
ehci_soft_qh_t *sqh; |
|
usbd_status err; |
|
int len, isread, endpt; |
|
int s; |
|
|
|
DPRINTFN(2, ("ehci_device_intr_start: xfer=%p len=%d flags=%d\n", |
|
xfer, xfer->length, xfer->flags)); |
|
|
|
if (sc->sc_dying) |
|
return (USBD_IOERROR); |
|
|
|
#ifdef DIAGNOSTIC |
|
if (xfer->rqflags & URQ_REQUEST) |
|
panic("ehci_device_intr_start: a request"); |
|
#endif |
|
|
|
len = xfer->length; |
|
endpt = epipe->pipe.endpoint->edesc->bEndpointAddress; |
|
isread = UE_GET_DIR(endpt) == UE_DIR_IN; |
|
sqh = epipe->sqh; |
|
|
|
epipe->u.intr.length = len; |
|
|
|
err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, &data, |
|
&dataend); |
|
if (err) { |
|
DPRINTFN(-1, ("ehci_device_intr_start: no memory\n")); |
|
xfer->status = err; |
|
usb_transfer_complete(xfer); |
|
return (err); |
|
} |
|
|
|
#ifdef EHCI_DEBUG |
|
if (ehcidebug > 5) { |
|
DPRINTF(("ehci_device_intr_start: data(1)\n")); |
|
ehci_dump_sqh(sqh); |
|
ehci_dump_sqtds(data); |
|
} |
|
#endif |
|
|
|
/* Set up interrupt info. */ |
|
exfer->sqtdstart = data; |
|
exfer->sqtdend = dataend; |
|
#ifdef DIAGNOSTIC |
|
if (!exfer->isdone) { |
|
printf("ehci_device_intr_start: not done, ex=%p\n", exfer); |
|
} |
|
exfer->isdone = 0; |
|
#endif |
|
|
|
s = splusb(); |
|
ehci_set_qh_qtd(sqh, data); |
|
if (xfer->timeout && !sc->sc_bus.use_polling) { |
|
usb_callout(xfer->timeout_handle, mstohz(xfer->timeout), |
|
ehci_timeout, xfer); |
|
} |
|
ehci_add_intr_list(sc, exfer); |
|
xfer->status = USBD_IN_PROGRESS; |
|
splx(s); |
|
|
|
#ifdef EHCI_DEBUG |
|
if (ehcidebug > 10) { |
|
DPRINTF(("ehci_device_intr_start: data(2)\n")); |
|
delay(10000); |
|
DPRINTF(("ehci_device_intr_start: data(3)\n")); |
|
ehci_dump_regs(sc); |
|
printf("sqh:\n"); |
|
ehci_dump_sqh(sqh); |
|
ehci_dump_sqtds(data); |
|
} |
|
#endif |
|
|
|
if (sc->sc_bus.use_polling) |
|
ehci_waitintr(sc, xfer); |
|
|
|
return (USBD_IN_PROGRESS); |
|
#undef exfer |
|
} |
|
|
|
Static void |
|
ehci_device_intr_abort(usbd_xfer_handle xfer) |
|
{ |
|
DPRINTFN(1, ("ehci_device_intr_abort: xfer=%p\n", xfer)); |
|
if (xfer->pipe->intrxfer == xfer) { |
|
DPRINTFN(1, ("echi_device_intr_abort: remove\n")); |
|
xfer->pipe->intrxfer = NULL; |
|
} |
|
ehci_abort_xfer(xfer, USBD_CANCELLED); |
|
} |
|
|
|
Static void |
|
ehci_device_intr_close(usbd_pipe_handle pipe) |
|
{ |
|
ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus; |
|
struct ehci_pipe *epipe = (struct ehci_pipe *)pipe; |
|
struct ehci_soft_islot *isp; |
|
|
|
isp = &sc->sc_islots[epipe->sqh->islot]; |
|
ehci_close_pipe(pipe, isp->sqh); |
|
} |
|
|
|
Static void |
|
ehci_device_intr_done(usbd_xfer_handle xfer) |
|
{ |
|
#define exfer EXFER(xfer) |
|
struct ehci_xfer *ex = EXFER(xfer); |
|
ehci_softc_t *sc = (ehci_softc_t *)xfer->pipe->device->bus; |
|
struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; |
|
ehci_soft_qtd_t *data, *dataend; |
|
ehci_soft_qh_t *sqh; |
|
usbd_status err; |
|
int len, isread, endpt, s; |
|
|
|
DPRINTFN(10, ("ehci_device_intr_done: xfer=%p, actlen=%d\n", |
|
xfer, xfer->actlen)); |
|
|
|
if (xfer->pipe->repeat) { |
|
ehci_free_sqtd_chain(sc, ex->sqtdstart, NULL); |
|
|
|
len = epipe->u.intr.length; |
|
xfer->length = len; |
|
endpt = epipe->pipe.endpoint->edesc->bEndpointAddress; |
|
isread = UE_GET_DIR(endpt) == UE_DIR_IN; |
|
sqh = epipe->sqh; |
|
|
|
err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, |
|
&data, &dataend); |
|
if (err) { |
|
DPRINTFN(-1, ("ehci_device_intr_done: no memory\n")); |
|
xfer->status = err; |
|
return; |
|
} |
|
|
|
/* Set up interrupt info. */ |
|
exfer->sqtdstart = data; |
|
exfer->sqtdend = dataend; |
|
#ifdef DIAGNOSTIC |
|
if (!exfer->isdone) { |
|
printf("ehci_device_intr_done: not done, ex=%p\n", |
|
exfer); |
|
} |
|
exfer->isdone = 0; |
|
#endif |
|
|
|
s = splusb(); |
|
ehci_set_qh_qtd(sqh, data); |
|
if (xfer->timeout && !sc->sc_bus.use_polling) { |
|
usb_callout(xfer->timeout_handle, |
|
mstohz(xfer->timeout), ehci_timeout, xfer); |
|
} |
|
splx(s); |
|
|
|
xfer->status = USBD_IN_PROGRESS; |
|
} else if (xfer->status != USBD_NOMEM && ehci_active_intr_list(ex)) { |
|
ehci_del_intr_list(ex); /* remove from active list */ |
|
ehci_free_sqtd_chain(sc, ex->sqtdstart, NULL); |
|
} |
|
#undef exfer |
|
} |
|
|
|
/************************/ |
/************************/ |
|
|