diff options
| author | augustss <augustss@NetBSD.org> | 2001-11-15 23:25:09 +0000 |
|---|---|---|
| committer | augustss <augustss@NetBSD.org> | 2001-11-15 23:25:09 +0000 |
| commit | 122d2863d8b0e321bdbac090116fbe312c4a740b (patch) | |
| tree | bcc27949dafde9cff59e1d6a83b24b00a06902ac /sys/dev | |
| parent | 6424a6316a432b29c636e0202c50e243a511896c (diff) | |
Add root hub emulation.
Diffstat (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/usb/ehci.c | 967 | ||||
| -rw-r--r-- | sys/dev/usb/ehcireg.h | 8 | ||||
| -rw-r--r-- | sys/dev/usb/ehcivar.h | 13 |
3 files changed, 974 insertions, 14 deletions
diff --git a/sys/dev/usb/ehci.c b/sys/dev/usb/ehci.c index a5124af9ecd..1f2c324bc74 100644 --- a/sys/dev/usb/ehci.c +++ b/sys/dev/usb/ehci.c @@ -1,7 +1,7 @@ -/* $NetBSD: ehci.c,v 1.4 2001/11/13 06:24:53 lukem Exp $ */ +/* $NetBSD: ehci.c,v 1.5 2001/11/15 23:25:09 augustss Exp $ */ /* - * Copyright (c) 2000 The NetBSD Foundation, Inc. + * Copyright (c) 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -39,13 +39,13 @@ /* * USB Enhanced Host Controller Driver, a.k.a. USB 2.0 controller. * - * EHCI 0.96 spec can be found at + * The EHCI 0.96 spec can be found at * http://developer.intel.com/technology/usb/download/ehci-r096.pdf * */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.4 2001/11/13 06:24:53 lukem Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.5 2001/11/15 23:25:09 augustss Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -71,13 +71,141 @@ __KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.4 2001/11/13 06:24:53 lukem Exp $"); #ifdef EHCI_DEBUG #define DPRINTF(x) if (ehcidebug) printf x #define DPRINTFN(n,x) if (ehcidebug>(n)) printf x -int ehcidebug = 1; +int ehcidebug = 5; #define bitmask_snprintf(q,f,b,l) snprintf((b), (l), "%b", (q), (f)) #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif +struct ehci_pipe { + struct usbd_pipe pipe; +}; + +Static void ehci_shutdown(void *); +Static void ehci_power(int, void *); + +Static usbd_status ehci_open(usbd_pipe_handle); +Static void ehci_poll(struct usbd_bus *); +Static void ehci_softintr(void *); + +Static usbd_status ehci_allocm(struct usbd_bus *, usb_dma_t *, u_int32_t); +Static void ehci_freem(struct usbd_bus *, usb_dma_t *); + +Static usbd_xfer_handle ehci_allocx(struct usbd_bus *); +Static void ehci_freex(struct usbd_bus *, usbd_xfer_handle); + +Static usbd_status ehci_root_ctrl_transfer(usbd_xfer_handle); +Static usbd_status ehci_root_ctrl_start(usbd_xfer_handle); +Static void ehci_root_ctrl_abort(usbd_xfer_handle); +Static void ehci_root_ctrl_close(usbd_pipe_handle); +Static void ehci_root_ctrl_done(usbd_xfer_handle); + +Static usbd_status ehci_root_intr_transfer(usbd_xfer_handle); +Static usbd_status ehci_root_intr_start(usbd_xfer_handle); +Static void ehci_root_intr_abort(usbd_xfer_handle); +Static void ehci_root_intr_close(usbd_pipe_handle); +Static void ehci_root_intr_done(usbd_xfer_handle); + +Static usbd_status ehci_device_ctrl_transfer(usbd_xfer_handle); +Static usbd_status ehci_device_ctrl_start(usbd_xfer_handle); +Static void ehci_device_ctrl_abort(usbd_xfer_handle); +Static void ehci_device_ctrl_close(usbd_pipe_handle); +Static void ehci_device_ctrl_done(usbd_xfer_handle); + +Static usbd_status ehci_device_bulk_transfer(usbd_xfer_handle); +Static usbd_status ehci_device_bulk_start(usbd_xfer_handle); +Static void ehci_device_bulk_abort(usbd_xfer_handle); +Static void ehci_device_bulk_close(usbd_pipe_handle); +Static void ehci_device_bulk_done(usbd_xfer_handle); + +Static usbd_status ehci_device_intr_transfer(usbd_xfer_handle); +Static usbd_status ehci_device_intr_start(usbd_xfer_handle); +Static void ehci_device_intr_abort(usbd_xfer_handle); +Static void ehci_device_intr_close(usbd_pipe_handle); +Static void ehci_device_intr_done(usbd_xfer_handle); + +Static usbd_status ehci_device_isoc_transfer(usbd_xfer_handle); +Static usbd_status ehci_device_isoc_start(usbd_xfer_handle); +Static void ehci_device_isoc_abort(usbd_xfer_handle); +Static void ehci_device_isoc_close(usbd_pipe_handle); +Static void ehci_device_isoc_done(usbd_xfer_handle); + +Static void ehci_device_clear_toggle(usbd_pipe_handle pipe); +Static void ehci_noop(usbd_pipe_handle pipe); + +Static int ehci_str(usb_string_descriptor_t *, int, char *); + +#ifdef EHCI_DEBUG +Static void ehci_dumpregs(ehci_softc_t *); +#endif + +#define EHCI_INTR_ENDPT 1 + +Static struct usbd_bus_methods ehci_bus_methods = { + ehci_open, + ehci_softintr, + ehci_poll, + ehci_allocm, + ehci_freem, + ehci_allocx, + ehci_freex, +}; + +Static struct usbd_pipe_methods ehci_root_ctrl_methods = { + ehci_root_ctrl_transfer, + ehci_root_ctrl_start, + ehci_root_ctrl_abort, + ehci_root_ctrl_close, + ehci_noop, + ehci_root_ctrl_done, +}; + +Static struct usbd_pipe_methods ehci_root_intr_methods = { + ehci_root_intr_transfer, + ehci_root_intr_start, + ehci_root_intr_abort, + ehci_root_intr_close, + ehci_noop, + ehci_root_intr_done, +}; + +Static struct usbd_pipe_methods ehci_device_ctrl_methods = { + ehci_device_ctrl_transfer, + ehci_device_ctrl_start, + ehci_device_ctrl_abort, + ehci_device_ctrl_close, + ehci_noop, + ehci_device_ctrl_done, +}; + +Static struct usbd_pipe_methods ehci_device_intr_methods = { + ehci_device_intr_transfer, + ehci_device_intr_start, + ehci_device_intr_abort, + ehci_device_intr_close, + ehci_device_clear_toggle, + ehci_device_intr_done, +}; + +Static struct usbd_pipe_methods ehci_device_bulk_methods = { + ehci_device_bulk_transfer, + ehci_device_bulk_start, + ehci_device_bulk_abort, + ehci_device_bulk_close, + ehci_device_clear_toggle, + ehci_device_bulk_done, +}; + +Static struct usbd_pipe_methods ehci_device_isoc_methods = { + ehci_device_isoc_transfer, + ehci_device_isoc_start, + ehci_device_isoc_abort, + ehci_device_isoc_close, + ehci_noop, + ehci_device_isoc_done, +}; + usbd_status ehci_init(ehci_softc_t *sc) { @@ -110,6 +238,7 @@ ehci_init(ehci_softc_t *sc) printf(" %s", USBDEVNAME(sc->sc_comps[i]->bdev)); printf("\n"); } + sc->sc_noport = EHCI_HCS_N_PORTS(sparams); cparams = EREAD4(sc, EHCI_HCCPARAMS); DPRINTF(("ehci_init: cparams=0x%x\n", cparams)); @@ -142,11 +271,16 @@ ehci_init(ehci_softc_t *sc) EHCI_FLALIGN_ALIGN, &sc->sc_fldma); if (err) return (err); - //sc->sc_fl = (struct ohci_hcca *)KERNADDR(&sc->sc_hccadma); DPRINTF(("%s: flsize=%d\n", USBDEVNAME(sc->sc_bus.bdev),sc->sc_flsize)); - printf("%s: EHCI not supported yet.\n", USBDEVNAME(sc->sc_bus.bdev)); - return (USBD_IOERROR); + /* Set up the bus struct. */ + sc->sc_bus.methods = &ehci_bus_methods; + sc->sc_bus.pipe_size = sizeof(struct ehci_pipe); + + sc->sc_powerhook = powerhook_establish(ehci_power, sc); + sc->sc_shutdownhook = shutdownhook_establish(ehci_shutdown, sc); + + return (USBD_NORMAL_COMPLETION); } int @@ -155,6 +289,32 @@ ehci_intr(void *v) return (0); } +void +ehci_softintr(void *v) +{ + //ehci_softc_t *sc = v; +} + +void +ehci_poll(struct usbd_bus *bus) +{ +#if 0 + ehci_softc_t *sc = (ehci_softc_t *)bus; +#ifdef EHCI_DEBUG + static int last; + int new; + new = OREAD4(sc, EHCI_INTERRUPT_STATUS); + if (new != last) { + DPRINTFN(10,("ehci_poll: intrs=0x%04x\n", new)); + last = new; + } +#endif + + if (OREAD4(sc, EHCI_INTERRUPT_STATUS) & sc->sc_eintrs) + ehci_intr1(sc); +#endif +} + int ehci_detach(struct ehci_softc *sc, int flags) { @@ -191,8 +351,799 @@ ehci_activate(device_ptr_t self, enum devact act) case DVACT_DEACTIVATE: if (sc->sc_child != NULL) rv = config_deactivate(sc->sc_child); + sc->sc_dying = 1; break; } return (rv); } +/* + * Handle suspend/resume. + * + * We need to switch to polling mode here, because this routine is + * called from an intterupt context. This is all right since we + * are almost suspended anyway. + */ +void +ehci_power(int why, void *v) +{ + ehci_softc_t *sc = v; + //u_int32_t ctl; + int s; + +#ifdef EHCI_DEBUG + DPRINTF(("ehci_power: sc=%p, why=%d\n", sc, why)); + ehci_dumpregs(sc); +#endif + + s = splhardusb(); + switch (why) { + case PWR_SUSPEND: + case PWR_STANDBY: + sc->sc_bus.use_polling++; +#if 0 +OOO + ctl = OREAD4(sc, EHCI_CONTROL) & ~EHCI_HCFS_MASK; + if (sc->sc_control == 0) { + /* + * Preserve register values, in case that APM BIOS + * does not recover them. + */ + sc->sc_control = ctl; + sc->sc_intre = OREAD4(sc, EHCI_INTERRUPT_ENABLE); + } + ctl |= EHCI_HCFS_SUSPEND; + OWRITE4(sc, EHCI_CONTROL, ctl); +#endif + usb_delay_ms(&sc->sc_bus, USB_RESUME_WAIT); + sc->sc_bus.use_polling--; + break; + case PWR_RESUME: + sc->sc_bus.use_polling++; +#if 0 +OOO + /* Some broken BIOSes do not recover these values */ + OWRITE4(sc, EHCI_HCCA, DMAADDR(&sc->sc_hccadma)); + OWRITE4(sc, EHCI_CONTROL_HEAD_ED, sc->sc_ctrl_head->physaddr); + OWRITE4(sc, EHCI_BULK_HEAD_ED, sc->sc_bulk_head->physaddr); + if (sc->sc_intre) + OWRITE4(sc, EHCI_INTERRUPT_ENABLE, + sc->sc_intre & (EHCI_ALL_INTRS | EHCI_MIE)); + if (sc->sc_control) + ctl = sc->sc_control; + else + ctl = OREAD4(sc, EHCI_CONTROL); + ctl |= EHCI_HCFS_RESUME; + OWRITE4(sc, EHCI_CONTROL, ctl); + usb_delay_ms(&sc->sc_bus, USB_RESUME_DELAY); + ctl = (ctl & ~EHCI_HCFS_MASK) | EHCI_HCFS_OPERATIONAL; + OWRITE4(sc, EHCI_CONTROL, ctl); + usb_delay_ms(&sc->sc_bus, USB_RESUME_RECOVERY); + sc->sc_control = sc->sc_intre = 0; +#endif + sc->sc_bus.use_polling--; + break; + case PWR_SOFTSUSPEND: + case PWR_SOFTSTANDBY: + case PWR_SOFTRESUME: + break; + } + splx(s); +} + +/* + * Shut down the controller when the system is going down. + */ +void +ehci_shutdown(void *v) +{ + //ehci_softc_t *sc = v; + + DPRINTF(("ehci_shutdown: stopping the HC\n")); +#if 0 +OOO + OWRITE4(sc, EHCI_CONTROL, EHCI_HCFS_RESET); +#endif +} + +usbd_status +ehci_allocm(struct usbd_bus *bus, usb_dma_t *dma, u_int32_t size) +{ + struct ehci_softc *sc = (struct ehci_softc *)bus; + + return (usb_allocmem(&sc->sc_bus, size, 0, dma)); +} + +void +ehci_freem(struct usbd_bus *bus, usb_dma_t *dma) +{ + struct ehci_softc *sc = (struct ehci_softc *)bus; + + usb_freemem(&sc->sc_bus, dma); +} + +usbd_xfer_handle +ehci_allocx(struct usbd_bus *bus) +{ + struct ehci_softc *sc = (struct ehci_softc *)bus; + usbd_xfer_handle xfer; + + xfer = SIMPLEQ_FIRST(&sc->sc_free_xfers); + if (xfer != NULL) + SIMPLEQ_REMOVE_HEAD(&sc->sc_free_xfers, xfer, next); + else + xfer = malloc(sizeof(*xfer), M_USB, M_NOWAIT); + if (xfer != NULL) + memset(xfer, 0, sizeof *xfer); + return (xfer); +} + +void +ehci_freex(struct usbd_bus *bus, usbd_xfer_handle xfer) +{ + struct ehci_softc *sc = (struct ehci_softc *)bus; + + SIMPLEQ_INSERT_HEAD(&sc->sc_free_xfers, xfer, next); +} + +Static void +ehci_device_clear_toggle(usbd_pipe_handle pipe) +{ +#if 0 +OOO + struct ehci_pipe *epipe = (struct ehci_pipe *)pipe; + + epipe->sed->ed.ed_headp &= htole32(~EHCI_TOGGLECARRY); +#endif +} + +Static void +ehci_noop(usbd_pipe_handle pipe) +{ +} + +#ifdef EHCI_DEBUG +void +ehci_dumpregs(ehci_softc_t *sc) +{ + // OOO +} +#endif + +usbd_status +ehci_open(usbd_pipe_handle pipe) +{ + usbd_device_handle dev = pipe->device; + ehci_softc_t *sc = (ehci_softc_t *)dev->bus; + usb_endpoint_descriptor_t *ed = pipe->endpoint->edesc; + u_int8_t addr = dev->address; +#if 0 + u_int8_t xfertype = ed->bmAttributes & UE_XFERTYPE; + struct ehci_pipe *epipe = (struct ehci_pipe *)pipe; + ehci_soft_ed_t *sed; + ehci_soft_td_t *std; + ehci_soft_itd_t *sitd; + ehci_physaddr_t tdphys; + u_int32_t fmt; + usbd_status err; + int s; + int ival; +#endif + + DPRINTFN(1, ("ehci_open: pipe=%p, addr=%d, endpt=%d (%d)\n", + pipe, addr, ed->bEndpointAddress, sc->sc_addr)); + + if (addr == sc->sc_addr) { + switch (ed->bEndpointAddress) { + case USB_CONTROL_ENDPOINT: + pipe->methods = &ehci_root_ctrl_methods; + break; + case UE_DIR_IN | EHCI_INTR_ENDPT: + pipe->methods = &ehci_root_intr_methods; + break; + default: + return (USBD_INVAL); + } + } else { +#if 0 + std = NULL; + sed = NULL; + + sed = ehci_alloc_sed(sc); + if (sed == NULL) + goto bad0; + epipe->sed = sed; + if (xfertype == UE_ISOCHRONOUS) { + sitd = ehci_alloc_sitd(sc); + if (sitd == NULL) { + ehci_free_sitd(sc, sitd); + goto bad1; + } + epipe->tail.itd = sitd; + tdphys = sitd->physaddr; + fmt = EHCI_ED_FORMAT_ISO; + if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN) + fmt |= EHCI_ED_DIR_IN; + else + fmt |= EHCI_ED_DIR_OUT; + } else { + std = ehci_alloc_std(sc); + if (std == NULL) { + ehci_free_std(sc, std); + goto bad1; + } + epipe->tail.td = std; + tdphys = std->physaddr; + fmt = EHCI_ED_FORMAT_GEN | EHCI_ED_DIR_TD; + } + sed->ed.ed_flags = htole32( + EHCI_ED_SET_FA(addr) | + EHCI_ED_SET_EN(ed->bEndpointAddress) | + (dev->lowspeed ? EHCI_ED_SPEED : 0) | fmt | + EHCI_ED_SET_MAXP(UGETW(ed->wMaxPacketSize))); + sed->ed.ed_headp = sed->ed.ed_tailp = htole32(tdphys); + + switch (xfertype) { + case UE_CONTROL: + pipe->methods = &ehci_device_ctrl_methods; + err = usb_allocmem(&sc->sc_bus, + sizeof(usb_device_request_t), + 0, &epipe->u.ctl.reqdma); + if (err) + goto bad; + s = splusb(); + ehci_add_ed(sed, sc->sc_ctrl_head); + splx(s); + break; + case UE_INTERRUPT: + pipe->methods = &ehci_device_intr_methods; + ival = pipe->interval; + if (ival == USBD_DEFAULT_INTERVAL) + ival = ed->bInterval; + return (ehci_device_setintr(sc, epipe, ival)); + case UE_ISOCHRONOUS: + pipe->methods = &ehci_device_isoc_methods; + return (ehci_setup_isoc(pipe)); + case UE_BULK: + pipe->methods = &ehci_device_bulk_methods; + s = splusb(); + ehci_add_ed(sed, sc->sc_bulk_head); + splx(s); + break; + } +#else + return (USBD_IOERROR); +#endif + } + return (USBD_NORMAL_COMPLETION); + +#if 0 + bad: + if (std != NULL) + ehci_free_std(sc, std); + bad1: + if (sed != NULL) + ehci_free_sed(sc, sed); + bad0: + return (USBD_NOMEM); +#endif +} + +/***********/ + +/* + * Data structures and routines to emulate the root hub. + */ +Static usb_device_descriptor_t ehci_devd = { + USB_DEVICE_DESCRIPTOR_SIZE, + UDESC_DEVICE, /* type */ + {0x00, 0x02}, /* USB version */ + UDCLASS_HUB, /* class */ + UDSUBCLASS_HUB, /* subclass */ + 0, /* protocol */ + 64, /* max packet */ + {0},{0},{0x00,0x01}, /* device id */ + 1,2,0, /* string indicies */ + 1 /* # of configurations */ +}; + +Static usb_config_descriptor_t ehci_confd = { + USB_CONFIG_DESCRIPTOR_SIZE, + UDESC_CONFIG, + {USB_CONFIG_DESCRIPTOR_SIZE + + USB_INTERFACE_DESCRIPTOR_SIZE + + USB_ENDPOINT_DESCRIPTOR_SIZE}, + 1, + 1, + 0, + UC_SELF_POWERED, + 0 /* max power */ +}; + +Static usb_interface_descriptor_t ehci_ifcd = { + USB_INTERFACE_DESCRIPTOR_SIZE, + UDESC_INTERFACE, + 0, + 0, + 1, + UICLASS_HUB, + UISUBCLASS_HUB, + 0, + 0 +}; + +Static usb_endpoint_descriptor_t ehci_endpd = { + USB_ENDPOINT_DESCRIPTOR_SIZE, + UDESC_ENDPOINT, + UE_DIR_IN | EHCI_INTR_ENDPT, + UE_INTERRUPT, + {8, 0}, /* max packet */ + 255 +}; + +Static usb_hub_descriptor_t ehci_hubd = { + USB_HUB_DESCRIPTOR_SIZE, + UDESC_HUB, + 0, + {0,0}, + 0, + 0, + {0}, +}; + +Static int +ehci_str(p, l, s) + usb_string_descriptor_t *p; + int l; + char *s; +{ + int i; + + if (l == 0) + return (0); + p->bLength = 2 * strlen(s) + 2; + if (l == 1) + return (1); + p->bDescriptorType = UDESC_STRING; + l -= 2; + for (i = 0; s[i] && l > 1; i++, l -= 2) + USETW2(p->bString[i], 0, s[i]); + return (2*i+2); +} + +/* + * Simulate a hardware hub by handling all the necessary requests. + */ +Static usbd_status +ehci_root_ctrl_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, start first */ + return (ehci_root_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); +} + +Static usbd_status +ehci_root_ctrl_start(usbd_xfer_handle xfer) +{ + ehci_softc_t *sc = (ehci_softc_t *)xfer->pipe->device->bus; + usb_device_request_t *req; + void *buf = NULL; + int port, i; + int s, len, value, index, l, totlen = 0; + usb_port_status_t ps; + usb_hub_descriptor_t hubd; + usbd_status err; + u_int32_t v; + + if (sc->sc_dying) + return (USBD_IOERROR); + +#ifdef DIAGNOSTIC + if (!(xfer->rqflags & URQ_REQUEST)) + /* XXX panic */ + return (USBD_INVAL); +#endif + req = &xfer->request; + + DPRINTFN(4,("ehci_root_ctrl_control type=0x%02x request=%02x\n", + req->bmRequestType, req->bRequest)); + + len = UGETW(req->wLength); + value = UGETW(req->wValue); + index = UGETW(req->wIndex); + + if (len != 0) + buf = KERNADDR(&xfer->dmabuf); + +#define C(x,y) ((x) | ((y) << 8)) + switch(C(req->bRequest, req->bmRequestType)) { + case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE): + case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE): + case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT): + /* + * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops + * for the integrated root hub. + */ + break; + case C(UR_GET_CONFIG, UT_READ_DEVICE): + if (len > 0) { + *(u_int8_t *)buf = sc->sc_conf; + totlen = 1; + } + break; + case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): + DPRINTFN(8,("ehci_root_ctrl_control wValue=0x%04x\n", value)); + switch(value >> 8) { + case UDESC_DEVICE: + if ((value & 0xff) != 0) { + err = USBD_IOERROR; + goto ret; + } + totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE); + USETW(ehci_devd.idVendor, sc->sc_id_vendor); + memcpy(buf, &ehci_devd, l); + break; + case UDESC_CONFIG: + if ((value & 0xff) != 0) { + err = USBD_IOERROR; + goto ret; + } + totlen = l = min(len, USB_CONFIG_DESCRIPTOR_SIZE); + memcpy(buf, &ehci_confd, l); + buf = (char *)buf + l; + len -= l; + l = min(len, USB_INTERFACE_DESCRIPTOR_SIZE); + totlen += l; + memcpy(buf, &ehci_ifcd, l); + buf = (char *)buf + l; + len -= l; + l = min(len, USB_ENDPOINT_DESCRIPTOR_SIZE); + totlen += l; + memcpy(buf, &ehci_endpd, l); + break; + case UDESC_STRING: + if (len == 0) + break; + *(u_int8_t *)buf = 0; + totlen = 1; + switch (value & 0xff) { + case 1: /* Vendor */ + totlen = ehci_str(buf, len, sc->sc_vendor); + break; + case 2: /* Product */ + totlen = ehci_str(buf, len, "EHCI root hub"); + break; + } + break; + default: + err = USBD_IOERROR; + goto ret; + } + break; + case C(UR_GET_INTERFACE, UT_READ_INTERFACE): + if (len > 0) { + *(u_int8_t *)buf = 0; + totlen = 1; + } + break; + case C(UR_GET_STATUS, UT_READ_DEVICE): + if (len > 1) { + USETW(((usb_status_t *)buf)->wStatus,UDS_SELF_POWERED); + totlen = 2; + } + break; + case C(UR_GET_STATUS, UT_READ_INTERFACE): + case C(UR_GET_STATUS, UT_READ_ENDPOINT): + if (len > 1) { + USETW(((usb_status_t *)buf)->wStatus, 0); + totlen = 2; + } + break; + case C(UR_SET_ADDRESS, UT_WRITE_DEVICE): + if (value >= USB_MAX_DEVICES) { + err = USBD_IOERROR; + goto ret; + } + sc->sc_addr = value; + break; + case C(UR_SET_CONFIG, UT_WRITE_DEVICE): + if (value != 0 && value != 1) { + err = USBD_IOERROR; + goto ret; + } + sc->sc_conf = value; + break; + case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE): + break; + case C(UR_SET_FEATURE, UT_WRITE_DEVICE): + case C(UR_SET_FEATURE, UT_WRITE_INTERFACE): + case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT): + err = USBD_IOERROR; + goto ret; + case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE): + break; + case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT): + break; + /* Hub requests */ + case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE): + break; + case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): + DPRINTFN(8, ("ehci_root_ctrl_control: UR_CLEAR_PORT_FEATURE " + "port=%d feature=%d\n", + index, value)); + if (index < 1 || index > sc->sc_noport) { + err = USBD_IOERROR; + goto ret; + } + port = EHCI_PORTSC(index); + v = EOREAD4(sc, port) &~ EHCI_PS_CLEAR; + switch(value) { + case UHF_PORT_ENABLE: + EOWRITE4(sc, port, v &~ EHCI_PS_PE); + break; + case UHF_PORT_SUSPEND: + EOWRITE4(sc, port, v &~ EHCI_PS_SUSP); + break; + case UHF_PORT_POWER: + EOWRITE4(sc, port, v &~ EHCI_PS_PP); + break; + case UHF_C_PORT_CONNECTION: + EOWRITE4(sc, port, v | EHCI_PS_CSC); + break; + case UHF_C_PORT_ENABLE: + EOWRITE4(sc, port, v | EHCI_PS_PEC); + break; + case UHF_C_PORT_SUSPEND: + /* how? */ + break; + case UHF_C_PORT_OVER_CURRENT: + EOWRITE4(sc, port, v | EHCI_PS_OCC); + break; + case UHF_C_PORT_RESET: + /* how? */ + break; + default: + err = USBD_IOERROR; + goto ret; + } +#if 0 + switch(value) { + case UHF_C_PORT_CONNECTION: + case UHF_C_PORT_ENABLE: + case UHF_C_PORT_SUSPEND: + case UHF_C_PORT_OVER_CURRENT: + case UHF_C_PORT_RESET: + /* Enable RHSC interrupt if condition is cleared. */ + if ((OREAD4(sc, port) >> 16) == 0) + ehci_rhsc_able(sc, 1); + break; + default: + break; + } +#endif + break; + case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): + if (value != 0) { + err = USBD_IOERROR; + goto ret; + } + hubd = ehci_hubd; + hubd.bNbrPorts = sc->sc_noport; + v = EOREAD4(sc, EHCI_HCSPARAMS); + USETW(hubd.wHubCharacteristics, + EHCI_HCS_PPC(v) ? UHD_PWR_INDIVIDUAL : UHD_PWR_NO_SWITCH); + hubd.bPwrOn2PwrGood = 200; /* XXX can't find out? */ + for (i = 0, l = sc->sc_noport; l > 0; i++, l -= 8, v >>= 8) + hubd.DeviceRemovable[i++] = 0; /* XXX can't find out? */ + hubd.bDescLength = USB_HUB_DESCRIPTOR_SIZE + i; + l = min(len, hubd.bDescLength); + totlen = l; + memcpy(buf, &hubd, l); + break; + case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): + if (len != 4) { + err = USBD_IOERROR; + goto ret; + } + memset(buf, 0, len); /* ? XXX */ + totlen = len; + break; + case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): + DPRINTFN(8,("ehci_root_ctrl_transfer: get port status i=%d\n", + index)); + if (index < 1 || index > sc->sc_noport) { + err = USBD_IOERROR; + goto ret; + } + if (len != 4) { + err = USBD_IOERROR; + goto ret; + } + v = EOREAD4(sc, EHCI_PORTSC(index)); + DPRINTFN(8,("ehci_root_ctrl_transfer: port status=0x%04x\n", + v)); + i = 0; + if (v & EHCI_PS_CS) i |= UPS_CURRENT_CONNECT_STATUS; + if (v & EHCI_PS_PE) i |= UPS_PORT_ENABLED; + if (v & EHCI_PS_SUSP) i |= UPS_SUSPEND; + if (v & EHCI_PS_OCA) i |= UPS_OVERCURRENT_INDICATOR; + if (v & EHCI_PS_PR) i |= UPS_RESET; + if (v & EHCI_PS_PP) i |= UPS_PORT_POWER; + USETW(ps.wPortStatus, i); + i = 0; + if (v & EHCI_PS_CSC) i |= UPS_C_CONNECT_STATUS; + if (v & EHCI_PS_PEC) i |= UPS_C_PORT_ENABLED; + if (v & EHCI_PS_OCC) i |= UPS_C_OVERCURRENT_INDICATOR; + USETW(ps.wPortChange, i); + l = min(len, sizeof ps); + memcpy(buf, &ps, l); + totlen = l; + break; + case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): + err = USBD_IOERROR; + goto ret; + case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE): + break; + case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER): + if (index < 1 || index > sc->sc_noport) { + err = USBD_IOERROR; + goto ret; + } + port = EHCI_PORTSC(index); + v = EOREAD4(sc, port) &~ EHCI_PS_CLEAR; + switch(value) { + case UHF_PORT_ENABLE: + EOWRITE4(sc, port, v | EHCI_PS_PE); + break; + case UHF_PORT_SUSPEND: + EOWRITE4(sc, port, v | EHCI_PS_SUSP); + break; + case UHF_PORT_RESET: + DPRINTFN(5,("ehci_root_ctrl_transfer: reset port %d\n", + index)); + EOWRITE4(sc, port, v | EHCI_PS_PR); + for (i = 0; i < 10; i++) { + usb_delay_ms(&sc->sc_bus, 10); /* XXX */ + if ((EOREAD4(sc, port) & EHCI_PS_PR) == 0) + break; + } + DPRINTFN(8,("ehci port %d reset, status = 0x%04x\n", + index, EOREAD4(sc, port))); + break; + case UHF_PORT_POWER: + DPRINTFN(2,("ehci_root_ctrl_transfer: set port power " + "%d\n", index)); + EOWRITE4(sc, port, v | EHCI_PS_PP); + break; + default: + err = USBD_IOERROR; + goto ret; + } + break; + default: + err = USBD_IOERROR; + goto ret; + } + xfer->actlen = totlen; + err = USBD_NORMAL_COMPLETION; + ret: + xfer->status = err; + s = splusb(); + usb_transfer_complete(xfer); + splx(s); + return (USBD_IN_PROGRESS); +} + +/* Abort a root control request. */ +Static void +ehci_root_ctrl_abort(usbd_xfer_handle xfer) +{ + /* Nothing to do, all transfers are synchronous. */ +} + +/* Close the root pipe. */ +Static void +ehci_root_ctrl_close(usbd_pipe_handle pipe) +{ + DPRINTF(("ehci_root_ctrl_close\n")); + /* Nothing to do. */ +} + +void +ehci_root_intr_done(usbd_xfer_handle xfer) +{ + xfer->hcpriv = NULL; +} + +Static usbd_status +ehci_root_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, start first */ + return (ehci_root_intr_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); +} + +Static usbd_status +ehci_root_intr_start(usbd_xfer_handle xfer) +{ + usbd_pipe_handle pipe = xfer->pipe; + ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus; + + if (sc->sc_dying) + return (USBD_IOERROR); + + sc->sc_intrxfer = xfer; + + return (USBD_IN_PROGRESS); +} + +/* Abort a root interrupt request. */ +Static void +ehci_root_intr_abort(usbd_xfer_handle xfer) +{ + int s; + + if (xfer->pipe->intrxfer == xfer) { + DPRINTF(("ehci_root_intr_abort: remove\n")); + xfer->pipe->intrxfer = NULL; + } + xfer->status = USBD_CANCELLED; + s = splusb(); + usb_transfer_complete(xfer); + splx(s); +} + +/* Close the root pipe. */ +Static void +ehci_root_intr_close(usbd_pipe_handle pipe) +{ + ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus; + + DPRINTF(("ehci_root_intr_close\n")); + + sc->sc_intrxfer = NULL; +} + +void +ehci_root_ctrl_done(usbd_xfer_handle xfer) +{ + xfer->hcpriv = NULL; +} + +/************************/ + +Static usbd_status ehci_device_ctrl_transfer(usbd_xfer_handle xfer) { return USBD_IOERROR; } +Static usbd_status ehci_device_ctrl_start(usbd_xfer_handle xfer) { return USBD_IOERROR; } +Static void ehci_device_ctrl_abort(usbd_xfer_handle xfer) { } +Static void ehci_device_ctrl_close(usbd_pipe_handle pipe) { } +Static void ehci_device_ctrl_done(usbd_xfer_handle xfer) { } + +Static usbd_status ehci_device_bulk_transfer(usbd_xfer_handle xfer) { return USBD_IOERROR; } +Static usbd_status ehci_device_bulk_start(usbd_xfer_handle xfer) { return USBD_IOERROR; } +Static void ehci_device_bulk_abort(usbd_xfer_handle xfer) { } +Static void ehci_device_bulk_close(usbd_pipe_handle pipe) { } +Static void ehci_device_bulk_done(usbd_xfer_handle xfer) { } + +Static usbd_status ehci_device_intr_transfer(usbd_xfer_handle xfer) { return USBD_IOERROR; } +Static usbd_status ehci_device_intr_start(usbd_xfer_handle xfer) { return USBD_IOERROR; } +Static void ehci_device_intr_abort(usbd_xfer_handle xfer) { } +Static void ehci_device_intr_close(usbd_pipe_handle pipe) { } +Static void ehci_device_intr_done(usbd_xfer_handle xfer) { } + +Static usbd_status ehci_device_isoc_transfer(usbd_xfer_handle xfer) { return USBD_IOERROR; } +Static usbd_status ehci_device_isoc_start(usbd_xfer_handle xfer) { return USBD_IOERROR; } +Static void ehci_device_isoc_abort(usbd_xfer_handle xfer) { } +Static void ehci_device_isoc_close(usbd_pipe_handle pipe) { } +Static void ehci_device_isoc_done(usbd_xfer_handle xfer) { } diff --git a/sys/dev/usb/ehcireg.h b/sys/dev/usb/ehcireg.h index be4383d9497..7d638bd3537 100644 --- a/sys/dev/usb/ehcireg.h +++ b/sys/dev/usb/ehcireg.h @@ -1,7 +1,7 @@ -/* $NetBSD: ehcireg.h,v 1.4 2001/11/10 17:06:11 augustss Exp $ */ +/* $NetBSD: ehcireg.h,v 1.5 2001/11/15 23:25:09 augustss Exp $ */ /* - * Copyright (c) 2000 The NetBSD Foundation, Inc. + * Copyright (c) 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -146,10 +146,10 @@ #define EHCI_PS_OCC 0x00000020 /* RWC over current change */ #define EHCI_PS_OCA 0x00000010 /* RO over current active */ #define EHCI_PS_PEC 0x00000008 /* RWC port enable change */ -#define EHCI_PS_PE 0x00000004 /* RO port enable */ +#define EHCI_PS_PE 0x00000004 /* RW port enable */ #define EHCI_PS_CSC 0x00000002 /* RWC connect status change */ #define EHCI_PS_CS 0x00000001 /* RO connect status */ - +#define EHCI_PS_CLEAR (EHCI_PS_OCC|EHCI_PS_PEC|EHCI_PS_CSC) #define EHCI_FLALIGN_ALIGN 0x1000 diff --git a/sys/dev/usb/ehcivar.h b/sys/dev/usb/ehcivar.h index 60d9ae0b3e9..29d853fb5a5 100644 --- a/sys/dev/usb/ehcivar.h +++ b/sys/dev/usb/ehcivar.h @@ -1,7 +1,7 @@ -/* $NetBSD: ehcivar.h,v 1.3 2001/11/10 17:06:11 augustss Exp $ */ +/* $NetBSD: ehcivar.h,v 1.4 2001/11/15 23:25:09 augustss Exp $ */ /* - * Copyright (c) 2000 The NetBSD Foundation, Inc. + * Copyright (c) 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -56,7 +56,16 @@ typedef struct ehci_softc { usb_dma_t sc_fldma; u_int sc_flsize; + int sc_noport; + u_int8_t sc_addr; /* device address */ + u_int8_t sc_conf; /* device configuration */ + usbd_xfer_handle sc_intrxfer; + + SIMPLEQ_HEAD(, usbd_xfer) sc_free_xfers; /* free xfers */ + device_ptr_t sc_child; /* /dev/usb# device */ + + char sc_dying; } ehci_softc_t; #define EREAD1(sc, a) bus_space_read_1((sc)->iot, (sc)->ioh, (a)) |
