diff options
Diffstat (limited to 'sys')
66 files changed, 13599 insertions, 3915 deletions
diff --git a/sys/dev/usb/FILES b/sys/dev/usb/FILES index ed7b4094b13..166fecb2dcd 100644 --- a/sys/dev/usb/FILES +++ b/sys/dev/usb/FILES @@ -5,6 +5,9 @@ Makefile to install .h files Makefile.usbdevs to run devlist2h.awk TODO just a list of things to do devlist2h.awk script to generate usbdevs*.h +ehci.c Host controller driver for EHCI (just a stub now) +ehcireg.h Hardware definitions for EHCI (just a stub now) +ehcivar.h API for ehci.c ezload.c EZ-USB firmware download subroutines ezload.h API for ezload.c files.usb config include file @@ -31,13 +34,27 @@ ugen.c generic driver that can handle access to any USB device uhci.c Host controller driver for UHCI uhcireg.h Hardware definitions for UHCI uhcivar.h API for uhci.c -uhid.c USB HID class driver +uhid.c USB generic HID driver +uhidev.c USB HID class driver +uhidev.h and definitions for it uhub.c USB hub driver ukbd.c USB keyboard driver ukbdmap.c wscons key mapping for ukbd ukbdvar.h API for ukbd.c ulpt.c USB printer class driver -umass.c USB mass storage driver (bulk only for now) +umass.c USB mass storage wire protocol driver +umass_isdata.c In-System Design ATA over bulk-only driver +umass_isdata.h and definitions for it +umass_quirks.c Table of strange umass devices +umass_quirks.h and definitions for it +umass_scsipi.c umass command protocol driver +umass_scsipi.h and definitions for it +umassvar.h definitions for umass.c +umidi.c USB MIDI driver +umidi_quirks.c Strange MIDI devices +umidi_quirks.h and definitions for it +umidireg.h Protocol definitions for umidi.c +umidivar.h definitions for umidi.c umodem.c USB modem (CDC ACM) driver ums.c USB mouse driver urio.c USB Diamond Rio500 driver diff --git a/sys/dev/usb/TODO b/sys/dev/usb/TODO index 28088a69fb3..2bc3497628a 100644 --- a/sys/dev/usb/TODO +++ b/sys/dev/usb/TODO @@ -85,6 +85,8 @@ Get rid of hcpriv. Keyspan serial driver +Clean up umass driver + Documentation: -------------- diff --git a/sys/dev/usb/ehci.c b/sys/dev/usb/ehci.c new file mode 100644 index 00000000000..ea68e25be57 --- /dev/null +++ b/sys/dev/usb/ehci.c @@ -0,0 +1,2757 @@ +/* $OpenBSD: ehci.c,v 1.1 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: ehci.c,v 1.29 2001/12/31 12:16:57 augustss Exp $ */ + +/* + * TODO + * hold off explorations by companion controllers until ehci has started. + */ + +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * USB Enhanced Host Controller Driver, a.k.a. USB 2.0 controller. + * + * The EHCI 0.96 spec can be found at + * http://developer.intel.com/technology/usb/download/ehci-r096.pdf + * and the USB 2.0 spec at + * http://www.usb.org/developers/data/usb_20.zip + * + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/select.h> +#include <sys/proc.h> +#include <sys/queue.h> + +#include <machine/bus.h> +#include <machine/endian.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdivar.h> +#include <dev/usb/usb_mem.h> +#include <dev/usb/usb_quirks.h> + +#include <dev/usb/ehcireg.h> +#include <dev/usb/ehcivar.h> + +#if defined(__OpenBSD__) +struct cfdriver ehci_cd = { + NULL, "ehci", DV_DULL +}; +#endif + +#ifdef EHCI_DEBUG +#define DPRINTF(x) if (ehcidebug) printf x +#define DPRINTFN(n,x) if (ehcidebug>(n)) printf x +int ehcidebug = 0; +#ifndef __NetBSD__ +#define bitmask_snprintf(q,f,b,l) snprintf((b), (l), "%b", (q), (f)) +#endif +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +struct ehci_pipe { + struct usbd_pipe pipe; + ehci_soft_qh_t *sqh; + union { + ehci_soft_qtd_t *qtd; + /* ehci_soft_itd_t *itd; */ + } tail; + union { + /* Control pipe */ + struct { + usb_dma_t reqdma; + u_int length; + /*ehci_soft_qtd_t *setup, *data, *stat;*/ + } ctl; + /* Interrupt pipe */ + /* XXX */ + /* Bulk pipe */ + struct { + u_int length; + } bulk; + /* Iso pipe */ + /* XXX */ + } u; +}; + +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 int ehci_intr1(ehci_softc_t *); +Static void ehci_waitintr(ehci_softc_t *, usbd_xfer_handle); +Static void ehci_check_intr(ehci_softc_t *, struct ehci_xfer *); +Static void ehci_idone(struct ehci_xfer *); +Static void ehci_timeout(void *); +Static void ehci_timeout_task(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 *); +Static void ehci_pcd(ehci_softc_t *, usbd_xfer_handle); +Static void ehci_pcd_able(ehci_softc_t *, int); +Static void ehci_pcd_enable(void *); +Static void ehci_disown(ehci_softc_t *, int, int); + +Static ehci_soft_qh_t *ehci_alloc_sqh(ehci_softc_t *); +Static void ehci_free_sqh(ehci_softc_t *, ehci_soft_qh_t *); + +Static ehci_soft_qtd_t *ehci_alloc_sqtd(ehci_softc_t *); +Static void ehci_free_sqtd(ehci_softc_t *, ehci_soft_qtd_t *); +Static usbd_status ehci_alloc_sqtd_chain(struct ehci_pipe *, + ehci_softc_t *, int, int, usbd_xfer_handle, + ehci_soft_qtd_t **, ehci_soft_qtd_t **); +Static void ehci_free_sqtd_chain(ehci_softc_t *, ehci_soft_qtd_t *, + ehci_soft_qtd_t *); + +Static usbd_status ehci_device_request(usbd_xfer_handle xfer); + +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 *, + ehci_soft_qh_t *); +Static void ehci_set_qh_qtd(ehci_soft_qh_t *, ehci_soft_qtd_t *); +Static void ehci_sync_hc(ehci_softc_t *); + +Static void ehci_close_pipe(usbd_pipe_handle, ehci_soft_qh_t *); +Static void ehci_abort_xfer(usbd_xfer_handle, usbd_status); + +#ifdef EHCI_DEBUG +Static void ehci_dump_regs(ehci_softc_t *); +Static void ehci_dump(void); +Static ehci_softc_t *theehci; +Static void ehci_dump_link(ehci_link_t, int); +Static void ehci_dump_sqtds(ehci_soft_qtd_t *); +Static void ehci_dump_sqtd(ehci_soft_qtd_t *); +Static void ehci_dump_qtd(ehci_qtd_t *); +Static void ehci_dump_sqh(ehci_soft_qh_t *); +Static void ehci_dump_exfer(struct ehci_xfer *); +#endif + +#define MS_TO_TICKS(ms) ((ms) * hz / 1000) + +#define EHCI_NULL htole32(EHCI_LINK_TERMINATE) + +#define EHCI_INTR_ENDPT 1 + +#define ehci_add_intr_list(sc, ex) \ + LIST_INSERT_HEAD(&(sc)->sc_intrhead, (ex), inext); +#define ehci_del_intr_list(ex) \ + LIST_REMOVE((ex), inext) + +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) +{ + u_int32_t version, sparams, cparams, hcr; + u_int i; + usbd_status err; + ehci_soft_qh_t *sqh; + + DPRINTF(("ehci_init: start\n")); +#ifdef EHCI_DEBUG + theehci = sc; +#endif + + sc->sc_offs = EREAD1(sc, EHCI_CAPLENGTH); + + version = EREAD2(sc, EHCI_HCIVERSION); + printf("%s: EHCI version %x.%x\n", USBDEVNAME(sc->sc_bus.bdev), + version >> 8, version & 0xff); + + sparams = EREAD4(sc, EHCI_HCSPARAMS); + DPRINTF(("ehci_init: sparams=0x%x\n", sparams)); + sc->sc_npcomp = EHCI_HCS_N_PCC(sparams); + if (EHCI_HCS_N_CC(sparams) != sc->sc_ncomp) { + printf("%s: wrong number of companions (%d != %d)\n", + USBDEVNAME(sc->sc_bus.bdev), + EHCI_HCS_N_CC(sparams), sc->sc_ncomp); + return (USBD_IOERROR); + } + if (sc->sc_ncomp > 0) { + printf("%s: companion controller%s, %d port%s each:", + USBDEVNAME(sc->sc_bus.bdev), sc->sc_ncomp!=1 ? "s" : "", + EHCI_HCS_N_PCC(sparams), + EHCI_HCS_N_PCC(sparams)!=1 ? "s" : ""); + for (i = 0; i < sc->sc_ncomp; i++) + 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)); + + sc->sc_bus.usbrev = USBREV_2_0; + + /* Reset the controller */ + DPRINTF(("%s: resetting\n", USBDEVNAME(sc->sc_bus.bdev))); + EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */ + usb_delay_ms(&sc->sc_bus, 1); + EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET); + for (i = 0; i < 100; i++) { + delay(10); + hcr = EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_HCRESET; + if (!hcr) + break; + } + if (hcr) { + printf("%s: reset timeout\n", USBDEVNAME(sc->sc_bus.bdev)); + return (USBD_IOERROR); + } + + /* frame list size at default, read back what we got and use that */ + switch (EHCI_CMD_FLS(EOREAD4(sc, EHCI_USBCMD))) { + case 0: sc->sc_flsize = 1024*4; break; + case 1: sc->sc_flsize = 512*4; break; + case 2: sc->sc_flsize = 256*4; break; + case 3: return (USBD_IOERROR); + } + err = usb_allocmem(&sc->sc_bus, sc->sc_flsize, + EHCI_FLALIGN_ALIGN, &sc->sc_fldma); + if (err) + return (err); + DPRINTF(("%s: flsize=%d\n", USBDEVNAME(sc->sc_bus.bdev),sc->sc_flsize)); + + /* 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); + + sc->sc_eintrs = EHCI_NORMAL_INTRS; + + /* Allocate dummy QH that starts the async list. */ + sqh = ehci_alloc_sqh(sc); + if (sqh == NULL) { + err = USBD_NOMEM; + goto bad1; + } + /* Fill the QH */ + sqh->qh.qh_endp = + htole32(EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH) | EHCI_QH_HRECL); + sqh->qh.qh_link = + htole32(sqh->physaddr | EHCI_LINK_QH); + sqh->qh.qh_curqtd = EHCI_NULL; + sqh->next = NULL; + /* Fill the overlay qTD */ + 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; +#ifdef EHCI_DEBUG + if (ehcidebug) { + ehci_dump_sqh(sqh); + } +#endif + + /* Point to async list */ + sc->sc_async_head = sqh; + EOWRITE4(sc, EHCI_ASYNCLISTADDR, sqh->physaddr | EHCI_LINK_QH); + + usb_callout_init(sc->sc_tmo_pcd); + + lockinit(&sc->sc_doorbell_lock, PZERO, "ehcidb", 0, 0); + + /* Enable interrupts */ + EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); + + /* Turn on controller */ + EOWRITE4(sc, EHCI_USBCMD, + EHCI_CMD_ITC_8 | /* 8 microframes */ + (EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_FLS_M) | + EHCI_CMD_ASE | + /* EHCI_CMD_PSE | */ + EHCI_CMD_RS); + + /* Take over port ownership */ + EOWRITE4(sc, EHCI_CONFIGFLAG, EHCI_CONF_CF); + + for (i = 0; i < 100; i++) { + delay(10); + hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH; + if (!hcr) + break; + } + if (hcr) { + printf("%s: run timeout\n", USBDEVNAME(sc->sc_bus.bdev)); + return (USBD_IOERROR); + } + + return (USBD_NORMAL_COMPLETION); + +#if 0 + bad2: + ehci_free_sqh(sc, sc->sc_async_head); +#endif + bad1: + usb_freemem(&sc->sc_bus, &sc->sc_fldma); + return (err); +} + +int +ehci_intr(void *v) +{ + ehci_softc_t *sc = v; + + if (sc == NULL || sc->sc_dying) + return (0); + + /* If we get an interrupt while polling, then just ignore it. */ + if (sc->sc_bus.use_polling) { +#ifdef DIAGNOSTIC + printf("ehci_intr: ignored interrupt while polling\n"); +#endif + return (0); + } + + return (ehci_intr1(sc)); +} + +Static int +ehci_intr1(ehci_softc_t *sc) +{ + u_int32_t intrs, eintrs; + + DPRINTFN(20,("ehci_intr1: enter\n")); + + /* In case the interrupt occurs before initialization has completed. */ + if (sc == NULL) { +#ifdef DIAGNOSTIC + printf("ehci_intr: sc == NULL\n"); +#endif + return (0); + } + + intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); + + if (!intrs) + return (0); + + EOWRITE4(sc, EHCI_USBSTS, intrs); /* Acknowledge */ + eintrs = intrs & sc->sc_eintrs; + DPRINTFN(7, ("ehci_intr: sc=%p intrs=0x%x(0x%x) eintrs=0x%x\n", + sc, (u_int)intrs, EOREAD4(sc, EHCI_USBSTS), + (u_int)eintrs)); + if (!eintrs) + return (0); + + sc->sc_bus.intr_context++; + sc->sc_bus.no_intrs++; + if (eintrs & EHCI_STS_IAA) { + DPRINTF(("ehci_intr1: door bell\n")); + wakeup(&sc->sc_async_head); + eintrs &= ~EHCI_STS_IAA; + } + if (eintrs & (EHCI_STS_INT | EHCI_STS_ERRINT)) { + DPRINTF(("ehci_intr1: %s %s\n", + eintrs & EHCI_STS_INT ? "INT" : "", + eintrs & EHCI_STS_ERRINT ? "ERRINT" : "")); + usb_schedsoftintr(&sc->sc_bus); + eintrs &= ~(EHCI_STS_INT | EHCI_STS_ERRINT); + } + if (eintrs & EHCI_STS_HSE) { + printf("%s: unrecoverable error, controller halted\n", + USBDEVNAME(sc->sc_bus.bdev)); + /* XXX what else */ + } + if (eintrs & EHCI_STS_PCD) { + ehci_pcd(sc, sc->sc_intrxfer); + /* + * Disable PCD interrupt for now, because it will be + * on until the port has been reset. + */ + ehci_pcd_able(sc, 0); + /* Do not allow RHSC interrupts > 1 per second */ + usb_callout(sc->sc_tmo_pcd, hz, ehci_pcd_enable, sc); + eintrs &= ~EHCI_STS_PCD; + } + + sc->sc_bus.intr_context--; + + if (eintrs != 0) { + /* Block unprocessed interrupts. */ + sc->sc_eintrs &= ~eintrs; + EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); + printf("%s: blocking intrs 0x%x\n", + USBDEVNAME(sc->sc_bus.bdev), eintrs); + } + + return (1); +} + +void +ehci_pcd_able(ehci_softc_t *sc, int on) +{ + DPRINTFN(4, ("ehci_pcd_able: on=%d\n", on)); + if (on) + sc->sc_eintrs |= EHCI_STS_PCD; + else + sc->sc_eintrs &= ~EHCI_STS_PCD; + EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); +} + +void +ehci_pcd_enable(void *v_sc) +{ + ehci_softc_t *sc = v_sc; + + ehci_pcd_able(sc, 1); +} + +void +ehci_pcd(ehci_softc_t *sc, usbd_xfer_handle xfer) +{ + usbd_pipe_handle pipe; + struct ehci_pipe *epipe; + u_char *p; + int i, m; + + if (xfer == NULL) { + /* Just ignore the change. */ + return; + } + + pipe = xfer->pipe; + epipe = (struct ehci_pipe *)pipe; + + p = KERNADDR(&xfer->dmabuf); + m = min(sc->sc_noport, xfer->length * 8 - 1); + memset(p, 0, xfer->length); + for (i = 1; i <= m; i++) { + /* Pick out CHANGE bits from the status reg. */ + if (EOREAD4(sc, EHCI_PORTSC(i)) & EHCI_PS_CLEAR) + p[i/8] |= 1 << (i%8); + } + DPRINTF(("ehci_pcd: change=0x%02x\n", *p)); + xfer->actlen = xfer->length; + xfer->status = USBD_NORMAL_COMPLETION; + + usb_transfer_complete(xfer); +} + +void +ehci_softintr(void *v) +{ + ehci_softc_t *sc = v; + struct ehci_xfer *ex; + + DPRINTFN(10,("%s: ehci_softintr (%d)\n", USBDEVNAME(sc->sc_bus.bdev), + sc->sc_bus.intr_context)); + + sc->sc_bus.intr_context++; + + /* + * The only explanation I can think of for why EHCI is as brain dead + * as UHCI interrupt-wise is that Intel was involved in both. + * An interrupt just tells us that something is done, we have no + * clue what, so we need to scan through all active transfers. :-( + */ + for (ex = LIST_FIRST(&sc->sc_intrhead); ex; ex = LIST_NEXT(ex, inext)) + ehci_check_intr(sc, ex); + + if (sc->sc_softwake) { + sc->sc_softwake = 0; + wakeup(&sc->sc_softwake); + } + + sc->sc_bus.intr_context--; +} + +/* Check for an interrupt. */ +void +ehci_check_intr(ehci_softc_t *sc, struct ehci_xfer *ex) +{ + ehci_soft_qtd_t *sqtd, *lsqtd; + u_int32_t status; + + DPRINTFN(/*15*/2, ("ehci_check_intr: ex=%p\n", ex)); + + if (ex->sqtdstart == NULL) { + printf("ehci_check_intr: sqtdstart=NULL\n"); + return; + } + lsqtd = ex->sqtdend; +#ifdef DIAGNOSTIC + if (lsqtd == NULL) { + printf("ehci_check_intr: sqtd==0\n"); + return; + } +#endif + /* + * If the last TD is still active we need to check whether there + * is a an error somewhere in the middle, or whether there was a + * short packet (SPD and not ACTIVE). + */ + if (le32toh(lsqtd->qtd.qtd_status) & EHCI_QTD_ACTIVE) { + DPRINTFN(12, ("ehci_check_intr: active ex=%p\n", ex)); + for (sqtd = ex->sqtdstart; sqtd != lsqtd; sqtd=sqtd->nextqtd) { + status = le32toh(sqtd->qtd.qtd_status); + /* If there's an active QTD the xfer isn't done. */ + if (status & EHCI_QTD_ACTIVE) + break; + /* Any kind of error makes the xfer done. */ + if (status & EHCI_QTD_HALTED) + goto done; + /* We want short packets, and it is short: it's done */ + if (EHCI_QTD_SET_BYTES(status) != 0) + goto done; + } + DPRINTFN(12, ("ehci_check_intr: ex=%p std=%p still active\n", + ex, ex->sqtdstart)); + return; + } + done: + DPRINTFN(12, ("ehci_check_intr: ex=%p done\n", ex)); + usb_uncallout(ex->xfer.timeout_handle, ehci_timeout, ex); + ehci_idone(ex); +} + +void +ehci_idone(struct ehci_xfer *ex) +{ + usbd_xfer_handle xfer = &ex->xfer; +#ifdef EHCI_DEBUG + struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; +#endif + ehci_soft_qtd_t *sqtd; + u_int32_t status = 0, nstatus; + int actlen; + + DPRINTFN(/*12*/2, ("ehci_idone: ex=%p\n", ex)); +#ifdef DIAGNOSTIC + { + int s = splhigh(); + if (ex->isdone) { + splx(s); +#ifdef EHCI_DEBUG + printf("ehci_idone: ex is done!\n "); + ehci_dump_exfer(ex); +#else + printf("ehci_idone: ex=%p is done!\n", ex); +#endif + return; + } + ex->isdone = 1; + splx(s); + } +#endif + + if (xfer->status == USBD_CANCELLED || + xfer->status == USBD_TIMEOUT) { + DPRINTF(("ehci_idone: aborted xfer=%p\n", xfer)); + return; + } + +#ifdef EHCI_DEBUG + DPRINTFN(/*10*/2, ("ehci_idone: xfer=%p, pipe=%p ready\n", xfer, epipe)); + if (ehcidebug > 10) + ehci_dump_sqtds(ex->sqtdstart); +#endif + + /* The transfer is done, compute actual length and status. */ + actlen = 0; + for (sqtd = ex->sqtdstart; sqtd != NULL; sqtd = sqtd->nextqtd) { + nstatus = le32toh(sqtd->qtd.qtd_status); + if (nstatus & EHCI_QTD_ACTIVE) + break; + + status = nstatus; + if (EHCI_QTD_GET_PID(status) != EHCI_QTD_PID_SETUP) + actlen += sqtd->len - EHCI_QTD_GET_BYTES(status); + } + + /* If there are left over TDs we need to update the toggle. */ + if (sqtd != NULL) { + if (!(xfer->rqflags & URQ_REQUEST)) + printf("ehci_idone: need toggle update\n"); +#if 0 + epipe->nexttoggle = EHCI_TD_GET_DT(le32toh(std->td.td_token)); +#endif + } + + status &= EHCI_QTD_STATERRS; + DPRINTFN(/*10*/2, ("ehci_idone: len=%d, actlen=%d, status=0x%x\n", + xfer->length, actlen, status)); + xfer->actlen = actlen; + if (status != 0) { +#ifdef EHCI_DEBUG + char sbuf[128]; + + bitmask_snprintf((u_int32_t)status, + "\20\3MISSEDMICRO\4XACT\5BABBLE\6BABBLE" + "\7HALTED", + sbuf, sizeof(sbuf)); + + DPRINTFN((status == EHCI_QTD_HALTED)*/*10*/2, + ("ehci_idone: error, addr=%d, endpt=0x%02x, " + "status 0x%s\n", + xfer->pipe->device->address, + xfer->pipe->endpoint->edesc->bEndpointAddress, + sbuf)); + if (ehcidebug > 2) { + ehci_dump_sqh(epipe->sqh); + ehci_dump_sqtds(ex->sqtdstart); + } +#endif + if (status == EHCI_QTD_HALTED) + xfer->status = USBD_STALLED; + else + xfer->status = USBD_IOERROR; /* more info XXX */ + } else { + xfer->status = USBD_NORMAL_COMPLETION; + } + + usb_transfer_complete(xfer); + DPRINTFN(/*12*/2, ("ehci_idone: ex=%p done\n", ex)); +} + +/* + * Wait here until controller claims to have an interrupt. + * Then call ehci_intr and return. Use timeout to avoid waiting + * too long. + */ +void +ehci_waitintr(ehci_softc_t *sc, usbd_xfer_handle xfer) +{ + int timo = xfer->timeout; + int usecs; + u_int32_t intrs; + + xfer->status = USBD_IN_PROGRESS; + for (usecs = timo * 1000000 / hz; usecs > 0; usecs -= 1000) { + usb_delay_ms(&sc->sc_bus, 1); + if (sc->sc_dying) + break; + intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)) & + sc->sc_eintrs; + DPRINTFN(15,("ehci_waitintr: 0x%04x\n", intrs)); +#ifdef EHCI_DEBUG + if (ehcidebug > 15) + ehci_dump_regs(sc); +#endif + if (intrs) { + ehci_intr1(sc); + if (xfer->status != USBD_IN_PROGRESS) + return; + } + } + + /* Timeout */ + DPRINTF(("ehci_waitintr: timeout\n")); + xfer->status = USBD_TIMEOUT; + usb_transfer_complete(xfer); + /* XXX should free TD */ +} + +void +ehci_poll(struct usbd_bus *bus) +{ + ehci_softc_t *sc = (ehci_softc_t *)bus; +#ifdef EHCI_DEBUG + static int last; + int new; + new = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); + if (new != last) { + DPRINTFN(10,("ehci_poll: intrs=0x%04x\n", new)); + last = new; + } +#endif + + if (EOREAD4(sc, EHCI_USBSTS) & sc->sc_eintrs) + ehci_intr1(sc); +} + +int +ehci_detach(struct ehci_softc *sc, int flags) +{ + int rv = 0; + + if (sc->sc_child != NULL) + rv = config_detach(sc->sc_child, flags); + + if (rv != 0) + return (rv); + + usb_uncallout(sc->sc_tmo_pcd, ehci_pcd_enable, sc); + + if (sc->sc_powerhook != NULL) + powerhook_disestablish(sc->sc_powerhook); + if (sc->sc_shutdownhook != NULL) + shutdownhook_disestablish(sc->sc_shutdownhook); + + usb_delay_ms(&sc->sc_bus, 300); /* XXX let stray task complete */ + + /* XXX free other data structures XXX */ + + return (rv); +} + + +int +ehci_activate(device_ptr_t self, enum devact act) +{ + struct ehci_softc *sc = (struct ehci_softc *)self; + int rv = 0; + + switch (act) { + case DVACT_ACTIVATE: + return (EOPNOTSUPP); + break; + + 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_dump_regs(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; +#if defined(__NetBSD__) + case PWR_SOFTSUSPEND: + case PWR_SOFTSTANDBY: + case PWR_SOFTRESUME: + break; +#endif + } + 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")); + EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */ + EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET); +} + +usbd_status +ehci_allocm(struct usbd_bus *bus, usb_dma_t *dma, u_int32_t size) +{ + struct ehci_softc *sc = (struct ehci_softc *)bus; + usbd_status err; + + err = usb_allocmem(&sc->sc_bus, size, 0, dma); +#ifdef EHCI_DEBUG + if (err) + printf("ehci_allocm: usb_allocmem()=%d\n", err); +#endif + return (err); +} + +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); +#ifdef DIAGNOSTIC + if (xfer->busy_free != XFER_FREE) { + printf("uhci_allocx: xfer=%p not free, 0x%08x\n", xfer, + xfer->busy_free); + } +#endif + } else { + xfer = malloc(sizeof(struct ehci_xfer), M_USB, M_NOWAIT); + } + if (xfer != NULL) { + memset(xfer, 0, sizeof (struct ehci_xfer)); +#ifdef DIAGNOSTIC + EXFER(xfer)->isdone = 1; + xfer->busy_free = XFER_BUSY; +#endif + } + return (xfer); +} + +void +ehci_freex(struct usbd_bus *bus, usbd_xfer_handle xfer) +{ + struct ehci_softc *sc = (struct ehci_softc *)bus; + +#ifdef DIAGNOSTIC + if (xfer->busy_free != XFER_BUSY) { + printf("ehci_freex: xfer=%p not busy, 0x%08x\n", xfer, + xfer->busy_free); + return; + } + xfer->busy_free = XFER_FREE; + if (!EXFER(xfer)->isdone) { + printf("ehci_freex: !isdone\n"); + return; + } +#endif + SIMPLEQ_INSERT_HEAD(&sc->sc_free_xfers, xfer, next); +} + +Static void +ehci_device_clear_toggle(usbd_pipe_handle pipe) +{ + struct ehci_pipe *epipe = (struct ehci_pipe *)pipe; + + DPRINTF(("ehci_device_clear_toggle: epipe=%p status=0x%x\n", + epipe, epipe->sqh->qh.qh_qtd.qtd_status)); +#ifdef USB_DEBUG + if (ehcidebug) + usbd_dump_pipe(pipe); +#endif + epipe->sqh->qh.qh_qtd.qtd_status &= htole32(~EHCI_QTD_TOGGLE); +} + +Static void +ehci_noop(usbd_pipe_handle pipe) +{ +} + +#ifdef EHCI_DEBUG +void +ehci_dump_regs(ehci_softc_t *sc) +{ + int i; + printf("cmd=0x%08x, sts=0x%08x, ien=0x%08x\n", + EOREAD4(sc, EHCI_USBCMD), + EOREAD4(sc, EHCI_USBSTS), + EOREAD4(sc, EHCI_USBINTR)); + printf("frindex=0x%08x ctrdsegm=0x%08x periodic=0x%08x async=0x%08x\n", + EOREAD4(sc, EHCI_FRINDEX), + EOREAD4(sc, EHCI_CTRLDSSEGMENT), + EOREAD4(sc, EHCI_PERIODICLISTBASE), + EOREAD4(sc, EHCI_ASYNCLISTADDR)); + for (i = 1; i <= sc->sc_noport; i++) + printf("port %d status=0x%08x\n", i, + EOREAD4(sc, EHCI_PORTSC(i))); +} + +void +ehci_dump() +{ + ehci_dump_regs(theehci); +} + +void +ehci_dump_link(ehci_link_t link, int type) +{ + link = le32toh(link); + printf("0x%08x", link); + if (link & EHCI_LINK_TERMINATE) + printf("<T>"); + else { + printf("<"); + if (type) { + switch (EHCI_LINK_TYPE(link)) { + case EHCI_LINK_ITD: printf("ITD"); break; + case EHCI_LINK_QH: printf("QH"); break; + case EHCI_LINK_SITD: printf("SITD"); break; + case EHCI_LINK_FSTN: printf("FSTN"); break; + } + } + printf(">"); + } +} + +void +ehci_dump_sqtds(ehci_soft_qtd_t *sqtd) +{ + int i; + u_int32_t stop; + + stop = 0; + for (i = 0; sqtd && i < 20 && !stop; sqtd = sqtd->nextqtd, i++) { + ehci_dump_sqtd(sqtd); + stop = sqtd->qtd.qtd_next & EHCI_LINK_TERMINATE; + } + if (sqtd) + printf("dump aborted, too many TDs\n"); +} + +void +ehci_dump_sqtd(ehci_soft_qtd_t *sqtd) +{ + printf("QTD(%p) at 0x%08x:\n", sqtd, sqtd->physaddr); + ehci_dump_qtd(&sqtd->qtd); +} + +void +ehci_dump_qtd(ehci_qtd_t *qtd) +{ + u_int32_t s; + char sbuf[128]; + + printf(" next="); ehci_dump_link(qtd->qtd_next, 0); + printf(" altnext="); ehci_dump_link(qtd->qtd_altnext, 0); + printf("\n"); + s = le32toh(qtd->qtd_status); + bitmask_snprintf(EHCI_QTD_GET_STATUS(s), + "\20\10ACTIVE\7HALTED\6BUFERR\5BABBLE\4XACTERR" + "\3MISSED\2SPLIT\1PING", sbuf, sizeof(sbuf)); + printf(" status=0x%08x: toggle=%d bytes=0x%x ioc=%d c_page=0x%x\n", + s, EHCI_QTD_GET_TOGGLE(s), EHCI_QTD_GET_BYTES(s), + EHCI_QTD_GET_IOC(s), EHCI_QTD_GET_C_PAGE(s)); + printf(" cerr=%d pid=%d stat=0x%s\n", EHCI_QTD_GET_CERR(s), + EHCI_QTD_GET_PID(s), sbuf); + for (s = 0; s < 5; s++) + printf(" buffer[%d]=0x%08x\n", s, le32toh(qtd->qtd_buffer[s])); +} + +void +ehci_dump_sqh(ehci_soft_qh_t *sqh) +{ + ehci_qh_t *qh = &sqh->qh; + u_int32_t endp, endphub; + + printf("QH(%p) at 0x%08x:\n", sqh, sqh->physaddr); + printf(" link="); ehci_dump_link(qh->qh_link, 1); printf("\n"); + endp = le32toh(qh->qh_endp); + printf(" endp=0x%08x\n", endp); + printf(" addr=0x%02x inact=%d endpt=%d eps=%d dtc=%d hrecl=%d\n", + EHCI_QH_GET_ADDR(endp), EHCI_QH_GET_INACT(endp), + EHCI_QH_GET_ENDPT(endp), EHCI_QH_GET_EPS(endp), + EHCI_QH_GET_DTC(endp), EHCI_QH_GET_HRECL(endp)); + printf(" mpl=0x%x ctl=%d nrl=%d\n", + EHCI_QH_GET_MPL(endp), EHCI_QH_GET_CTL(endp), + EHCI_QH_GET_NRL(endp)); + endphub = le32toh(qh->qh_endphub); + printf(" endphub=0x%08x\n", endphub); + printf(" smask=0x%02x cmask=0x%02x huba=0x%02x port=%d mult=%d\n", + EHCI_QH_GET_SMASK(endphub), EHCI_QH_GET_CMASK(endphub), + EHCI_QH_GET_HUBA(endphub), EHCI_QH_GET_PORT(endphub), + EHCI_QH_GET_MULT(endphub)); + printf(" curqtd="); ehci_dump_link(qh->qh_curqtd, 0); printf("\n"); + printf("Overlay qTD:\n"); + ehci_dump_qtd(&qh->qh_qtd); +} + +Static void +ehci_dump_exfer(struct ehci_xfer *ex) +{ + printf("ehci_dump_exfer: ex=%p\n", ex); +} +#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; + u_int8_t xfertype = ed->bmAttributes & UE_XFERTYPE; + struct ehci_pipe *epipe = (struct ehci_pipe *)pipe; + ehci_soft_qh_t *sqh; + usbd_status err; + int s; + int speed, naks; + + DPRINTFN(1, ("ehci_open: pipe=%p, addr=%d, endpt=%d (%d)\n", + pipe, addr, ed->bEndpointAddress, sc->sc_addr)); + + if (sc->sc_dying) + return (USBD_IOERROR); + + 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); + } + return (USBD_NORMAL_COMPLETION); + } + + /* XXX All this stuff is only valid for async. */ + switch (dev->speed) { + case USB_SPEED_LOW: speed = EHCI_QH_SPEED_LOW; break; + case USB_SPEED_FULL: speed = EHCI_QH_SPEED_FULL; break; + case USB_SPEED_HIGH: speed = EHCI_QH_SPEED_HIGH; break; + default: panic("ehci_open: bad device speed %d\n", dev->speed); + } + naks = 8; /* XXX */ + sqh = ehci_alloc_sqh(sc); + if (sqh == NULL) + goto bad0; + /* qh_link filled when the QH is added */ + sqh->qh.qh_endp = htole32( + EHCI_QH_SET_ADDR(addr) | + EHCI_QH_SET_ENDPT(ed->bEndpointAddress) | + EHCI_QH_SET_EPS(speed) | /* XXX */ + /* XXX EHCI_QH_DTC ? */ + EHCI_QH_SET_MPL(UGETW(ed->wMaxPacketSize)) | + (speed != EHCI_QH_SPEED_HIGH && xfertype == UE_CONTROL ? + EHCI_QH_CTL : 0) | + EHCI_QH_SET_NRL(naks) + ); + sqh->qh.qh_endphub = htole32( + EHCI_QH_SET_MULT(1) + /* XXX TT stuff */ + /* XXX interrupt mask */ + ); + sqh->qh.qh_curqtd = EHCI_NULL; + /* Fill the overlay qTD */ + sqh->qh.qh_qtd.qtd_next = EHCI_NULL; + sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL; + sqh->qh.qh_qtd.qtd_status = htole32(0); + + epipe->sqh = sqh; + + switch (xfertype) { + case UE_CONTROL: + err = usb_allocmem(&sc->sc_bus, sizeof(usb_device_request_t), + 0, &epipe->u.ctl.reqdma); +#ifdef EHCI_DEBUG + if (err) + printf("ehci_open: usb_allocmem()=%d\n", err); +#endif + if (err) + goto bad1; + pipe->methods = &ehci_device_ctrl_methods; + s = splusb(); + ehci_add_qh(sqh, sc->sc_async_head); + splx(s); + break; + case UE_BULK: + pipe->methods = &ehci_device_bulk_methods; + s = splusb(); + ehci_add_qh(sqh, sc->sc_async_head); + splx(s); + break; + case UE_INTERRUPT: + pipe->methods = &ehci_device_intr_methods; + return (USBD_INVAL); + case UE_ISOCHRONOUS: + pipe->methods = &ehci_device_isoc_methods; + return (USBD_INVAL); + default: + return (USBD_INVAL); + } + return (USBD_NORMAL_COMPLETION); + + bad1: + ehci_free_sqh(sc, sqh); + bad0: + return (USBD_NOMEM); +} + +/* + * Add an ED to the schedule. Called at splusb(). + */ +void +ehci_add_qh(ehci_soft_qh_t *sqh, ehci_soft_qh_t *head) +{ + SPLUSBCHECK; + + sqh->next = head->next; + sqh->qh.qh_link = head->qh.qh_link; + head->next = sqh; + head->qh.qh_link = htole32(sqh->physaddr | EHCI_LINK_QH); + +#ifdef EHCI_DEBUG + if (ehcidebug > 5) { + printf("ehci_add_qh:\n"); + ehci_dump_sqh(sqh); + } +#endif +} + +/* + * Remove an ED from the schedule. Called at splusb(). + */ +void +ehci_rem_qh(ehci_softc_t *sc, ehci_soft_qh_t *sqh, ehci_soft_qh_t *head) +{ + ehci_soft_qh_t *p; + + SPLUSBCHECK; + /* XXX */ + for (p = head; p == NULL && p->next != sqh; p = p->next) + ; + if (p == NULL) + panic("ehci_rem_qh: ED not found\n"); + p->next = sqh->next; + p->qh.qh_link = sqh->qh.qh_link; + + ehci_sync_hc(sc); +} + +void +ehci_set_qh_qtd(ehci_soft_qh_t *sqh, ehci_soft_qtd_t *sqtd) +{ + /* Halt while we are messing. */ + sqh->qh.qh_qtd.qtd_status |= htole32(EHCI_QTD_HALTED); + sqh->qh.qh_curqtd = 0; + sqh->qh.qh_qtd.qtd_next = htole32(sqtd->physaddr); + sqh->sqtd = sqtd; + /* Keep toggle, clear the rest, including length. */ + sqh->qh.qh_qtd.qtd_status &= htole32(EHCI_QTD_TOGGLE); +} + +/* + * Ensure that the HC has released all references to the QH. We do this + * by asking for a Async Advance Doorbell interrupt and then we wait for + * the interrupt. + * To make this easier we first obtain exclusive use of the doorbell. + */ +void +ehci_sync_hc(ehci_softc_t *sc) +{ + int s, error; + + if (sc->sc_dying) { + DPRINTFN(2,("ehci_sync_hc: dying\n")); + return; + } + DPRINTFN(2,("ehci_sync_hc: enter\n")); + + /* get doorbell */ + usb_lockmgr(&sc->sc_doorbell_lock, LK_EXCLUSIVE, NULL, curproc); + + s = splhardusb(); + /* ask for doorbell */ + EOWRITE4(sc, EHCI_USBCMD, EOREAD4(sc, EHCI_USBCMD) | EHCI_CMD_IAAD); + DPRINTFN(1,("ehci_sync_hc: cmd=0x%08x sts=0x%08x\n", + EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS))); + error = tsleep(&sc->sc_async_head, PZERO, "ehcidi", hz); /* bell wait */ + DPRINTFN(1,("ehci_sync_hc: cmd=0x%08x sts=0x%08x\n", + EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS))); + splx(s); + + /* release doorbell */ + usb_lockmgr(&sc->sc_doorbell_lock, LK_RELEASE, NULL, curproc); + +#ifdef DIAGNOSTIC + if (error) + printf("ehci_sync_hc: tsleep() = %d\n", error); +#endif + DPRINTFN(2,("ehci_sync_hc: exit\n")); +} + +/***********/ + +/* + * 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 */ + UDPROTO_HSHUBSTT, /* protocol */ + 64, /* max packet */ + {0},{0},{0x00,0x01}, /* device id */ + 1,2,0, /* string indicies */ + 1 /* # of configurations */ +}; + +Static usb_device_qualifier_t ehci_odevd = { + USB_DEVICE_DESCRIPTOR_SIZE, + UDESC_DEVICE_QUALIFIER, /* type */ + {0x00, 0x02}, /* USB version */ + UDCLASS_HUB, /* class */ + UDSUBCLASS_HUB, /* subclass */ + UDPROTO_FSHUB, /* protocol */ + 64, /* max packet */ + 1, /* # of configurations */ + 0 +}; + +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, + UIPROTO_HSHUBSTT, + 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; + /* + * We can't really operate at another speed, but the spec says + * we need this descriptor. + */ + case UDESC_DEVICE_QUALIFIER: + if ((value & 0xff) != 0) { + err = USBD_IOERROR; + goto ret; + } + totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE); + memcpy(buf, &ehci_odevd, l); + break; + /* + * We can't really operate at another speed, but the spec says + * we need this descriptor. + */ + case UDESC_OTHER_SPEED_CONFIGURATION: + 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); + ((usb_config_descriptor_t *)buf)->bDescriptorType = + value >> 8; + 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_PORT_TEST: + DPRINTFN(2,("ehci_root_ctrl_transfer: clear port test " + "%d\n", index)); + break; + case UHF_PORT_INDICATOR: + DPRINTFN(2,("ehci_root_ctrl_transfer: clear port ind " + "%d\n", index)); + EOWRITE4(sc, port, v &~ EHCI_PS_PIC); + 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: + sc->sc_isreset = 0; + 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_pcd_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 | + EHCI_HCS_P_INCICATOR(EREAD4(sc, EHCI_HCSPARAMS)) + ? UHD_PORT_IND : 0); + 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 = UPS_HIGH_SPEED; + 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; + if (sc->sc_isreset) i |= UPS_C_PORT_RESET; + 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)); + if (EHCI_PS_IS_LOWSPEED(v)) { + /* Low speed device, give up ownership. */ + ehci_disown(sc, index, 1); + break; + } + /* Start reset sequence. */ + v &= ~ (EHCI_PS_PE | EHCI_PS_PR); + EOWRITE4(sc, port, v | EHCI_PS_PR); + /* Wait for reset to complete. */ + usb_delay_ms(&sc->sc_bus, USB_PORT_ROOT_RESET_DELAY); + if (sc->sc_dying) { + err = USBD_IOERROR; + goto ret; + } + /* Terminate reset sequence. */ + EOWRITE4(sc, port, v); + /* Wait for HC to complete reset. */ + usb_delay_ms(&sc->sc_bus, EHCI_PORT_RESET_COMPLETE); + if (sc->sc_dying) { + err = USBD_IOERROR; + goto ret; + } + v = EOREAD4(sc, port); + DPRINTF(("ehci after reset, status=0x%08x\n", v)); + if (v & EHCI_PS_PR) { + printf("%s: port reset timeout\n", + USBDEVNAME(sc->sc_bus.bdev)); + return (USBD_TIMEOUT); + } + if (!(v & EHCI_PS_PE)) { + /* Not a high speed device, give up ownership.*/ + ehci_disown(sc, index, 0); + break; + } + sc->sc_isreset = 1; + DPRINTF(("ehci port %d reset, status = 0x%08x\n", + index, v)); + 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; + case UHF_PORT_TEST: + DPRINTFN(2,("ehci_root_ctrl_transfer: set port test " + "%d\n", index)); + break; + case UHF_PORT_INDICATOR: + DPRINTFN(2,("ehci_root_ctrl_transfer: set port ind " + "%d\n", index)); + EOWRITE4(sc, port, v | EHCI_PS_PIC); + break; + default: + err = USBD_IOERROR; + goto ret; + } + break; + case C(UR_CLEAR_TT_BUFFER, UT_WRITE_CLASS_OTHER): + case C(UR_RESET_TT, UT_WRITE_CLASS_OTHER): + case C(UR_GET_TT_STATE, UT_READ_CLASS_OTHER): + case C(UR_STOP_TT, UT_WRITE_CLASS_OTHER): + 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); +} + +void +ehci_disown(ehci_softc_t *sc, int index, int lowspeed) +{ + int port; + u_int32_t v; + + DPRINTF(("ehci_disown: index=%d lowspeed=%d\n", index, lowspeed)); +#ifdef DIAGNOSTIC + if (sc->sc_npcomp != 0) { + int i = (index-1) / sc->sc_npcomp; + if (i >= sc->sc_ncomp) + printf("%s: strange port\n", + USBDEVNAME(sc->sc_bus.bdev)); + else + printf("%s: handing over %s speed device on " + "port %d to %s\n", + USBDEVNAME(sc->sc_bus.bdev), + lowspeed ? "low" : "full", + index, USBDEVNAME(sc->sc_comps[i]->bdev)); + } else { + printf("%s: npcomp == 0\n", USBDEVNAME(sc->sc_bus.bdev)); + } +#endif + port = EHCI_PORTSC(index); + v = EOREAD4(sc, port) &~ EHCI_PS_CLEAR; + EOWRITE4(sc, port, v | EHCI_PS_PO); +} + +/* 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; +} + +/************************/ + +ehci_soft_qh_t * +ehci_alloc_sqh(ehci_softc_t *sc) +{ + ehci_soft_qh_t *sqh; + usbd_status err; + int i, offs; + usb_dma_t dma; + + if (sc->sc_freeqhs == NULL) { + DPRINTFN(2, ("ehci_alloc_sqh: allocating chunk\n")); + err = usb_allocmem(&sc->sc_bus, EHCI_SQH_SIZE * EHCI_SQH_CHUNK, + EHCI_PAGE_SIZE, &dma); +#ifdef EHCI_DEBUG + if (err) + printf("ehci_alloc_sqh: usb_allocmem()=%d\n", err); +#endif + if (err) + return (NULL); + for(i = 0; i < EHCI_SQH_CHUNK; i++) { + offs = i * EHCI_SQH_SIZE; + sqh = (ehci_soft_qh_t *)((char *)KERNADDR(&dma) + offs); + sqh->physaddr = DMAADDR(&dma) + offs; + sqh->next = sc->sc_freeqhs; + sc->sc_freeqhs = sqh; + } + } + sqh = sc->sc_freeqhs; + sc->sc_freeqhs = sqh->next; + memset(&sqh->qh, 0, sizeof(ehci_qh_t)); + sqh->next = NULL; + return (sqh); +} + +void +ehci_free_sqh(ehci_softc_t *sc, ehci_soft_qh_t *sqh) +{ + sqh->next = sc->sc_freeqhs; + sc->sc_freeqhs = sqh; +} + +ehci_soft_qtd_t * +ehci_alloc_sqtd(ehci_softc_t *sc) +{ + ehci_soft_qtd_t *sqtd; + usbd_status err; + int i, offs; + usb_dma_t dma; + int s; + + if (sc->sc_freeqtds == NULL) { + DPRINTFN(2, ("ehci_alloc_sqtd: allocating chunk\n")); + err = usb_allocmem(&sc->sc_bus, EHCI_SQTD_SIZE*EHCI_SQTD_CHUNK, + EHCI_PAGE_SIZE, &dma); +#ifdef EHCI_DEBUG + if (err) + printf("ehci_alloc_sqtd: usb_allocmem()=%d\n", err); +#endif + if (err) + return (NULL); + s = splusb(); + for(i = 0; i < EHCI_SQTD_CHUNK; i++) { + offs = i * EHCI_SQTD_SIZE; + sqtd = (ehci_soft_qtd_t *)((char *)KERNADDR(&dma)+offs); + sqtd->physaddr = DMAADDR(&dma) + offs; + sqtd->nextqtd = sc->sc_freeqtds; + sc->sc_freeqtds = sqtd; + } + splx(s); + } + + s = splusb(); + sqtd = sc->sc_freeqtds; + sc->sc_freeqtds = sqtd->nextqtd; + memset(&sqtd->qtd, 0, sizeof(ehci_qtd_t)); + sqtd->nextqtd = NULL; + sqtd->xfer = NULL; + splx(s); + + return (sqtd); +} + +void +ehci_free_sqtd(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd) +{ + int s; + + s = splusb(); + sqtd->nextqtd = sc->sc_freeqtds; + sc->sc_freeqtds = sqtd; + splx(s); +} + +usbd_status +ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc, + int alen, int rd, usbd_xfer_handle xfer, + ehci_soft_qtd_t **sp, ehci_soft_qtd_t **ep) +{ + ehci_soft_qtd_t *next, *cur; + ehci_physaddr_t dataphys, dataphyspage, dataphyslastpage, nextphys; + u_int32_t qtdstatus; + int len, curlen; + int i; + usb_dma_t *dma = &xfer->dmabuf; + + DPRINTFN(alen<4*4096,("ehci_alloc_sqtd_chain: start len=%d\n", alen)); + + len = alen; + dataphys = DMAADDR(dma); + dataphyslastpage = EHCI_PAGE(dataphys + len - 1); + qtdstatus = htole32( + EHCI_QTD_ACTIVE | + EHCI_QTD_SET_PID(rd ? EHCI_QTD_PID_IN : EHCI_QTD_PID_OUT) | + EHCI_QTD_SET_CERR(3) + /* IOC set below */ + /* BYTES set below */ + /* XXX Data toggle */ + ); + + cur = ehci_alloc_sqtd(sc); + *sp = cur; + if (cur == NULL) + goto nomem; + for (;;) { + dataphyspage = EHCI_PAGE(dataphys); + /* The EHCI hardware can handle at most 5 pages. */ + if (dataphyslastpage - dataphyspage < + EHCI_QTD_NBUFFERS * EHCI_PAGE_SIZE) { + /* we can handle it in this QTD */ + curlen = len; + } else { + /* must use multiple TDs, fill as much as possible. */ + curlen = EHCI_QTD_NBUFFERS * EHCI_PAGE_SIZE - + EHCI_PAGE_OFFSET(dataphys); +#ifdef DIAGNOSTIC + if (curlen > len) { + printf("ehci_alloc_sqtd_chain: curlen=0x%x " + "len=0x%x offs=0x%x\n", curlen, len, + EHCI_PAGE_OFFSET(dataphys)); + printf("lastpage=0x%x page=0x%x phys=0x%x\n", + dataphyslastpage, dataphyspage, + dataphys); + curlen = len; + } +#endif + + /* XXX true for EHCI? */ + /* the length must be a multiple of the max size */ + curlen -= curlen % UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize); + DPRINTFN(1,("ehci_alloc_sqtd_chain: multiple QTDs, " + "curlen=%d\n", curlen)); +#ifdef DIAGNOSTIC + if (curlen == 0) + panic("ehci_alloc_std: curlen == 0\n"); +#endif + } + DPRINTFN(4,("ehci_alloc_sqtd_chain: dataphys=0x%08x " + "dataphyslastpage=0x%08x len=%d curlen=%d\n", + dataphys, dataphyslastpage, + len, curlen)); + len -= curlen; + + if (len != 0) { + next = ehci_alloc_sqtd(sc); + if (next == NULL) + goto nomem; + nextphys = next->physaddr; + } else { + next = NULL; + nextphys = EHCI_NULL; + } + + for (i = 0; i * EHCI_PAGE_SIZE < curlen; i++) { + ehci_physaddr_t a = dataphys + i * EHCI_PAGE_SIZE; + if (i != 0) /* use offset only in first buffer */ + a = EHCI_PAGE(a); + cur->qtd.qtd_buffer[i] = htole32(a); +#ifdef DIAGNOSTIC + if (i >= EHCI_QTD_NBUFFERS) { + printf("ehci_alloc_sqtd_chain: i=%d\n", i); + goto nomem; + } +#endif + } + cur->nextqtd = next; + cur->qtd.qtd_next = cur->qtd.qtd_altnext = htole32(nextphys); + cur->qtd.qtd_status = + qtdstatus | htole32(EHCI_QTD_SET_BYTES(curlen)); + cur->xfer = xfer; + cur->len = curlen; + DPRINTFN(10,("ehci_alloc_sqtd_chain: cbp=0x%08x end=0x%08x\n", + dataphys, dataphys + curlen)); + if (len == 0) + break; + DPRINTFN(10,("ehci_alloc_sqtd_chain: extend chain\n")); + dataphys += curlen; + cur = next; + } + cur->qtd.qtd_status |= htole32(EHCI_QTD_IOC); + *ep = cur; + + DPRINTFN(10,("ehci_alloc_sqtd_chain: return sqtd=%p sqtdend=%p\n", + *sp, *ep)); + + return (USBD_NORMAL_COMPLETION); + + nomem: + /* XXX free chain */ + DPRINTFN(-1,("ehci_alloc_sqtd_chain: no memory\n")); + return (USBD_NOMEM); +} + +Static void +ehci_free_sqtd_chain(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd, + ehci_soft_qtd_t *sqtdend) +{ + ehci_soft_qtd_t *p; + int i; + + DPRINTFN(10,("ehci_free_sqtd_chain: sqtd=%p sqtdend=%p\n", + sqtd, sqtdend)); + + for (i = 0; sqtd != sqtdend; sqtd = p, i++) { + p = sqtd->nextqtd; + ehci_free_sqtd(sc, sqtd); + } +} + +/****************/ + +/* + * Close a reqular pipe. + * Assumes that there are no pending transactions. + */ +void +ehci_close_pipe(usbd_pipe_handle pipe, ehci_soft_qh_t *head) +{ + struct ehci_pipe *epipe = (struct ehci_pipe *)pipe; + ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus; + ehci_soft_qh_t *sqh = epipe->sqh; + int s; + + s = splusb(); + ehci_rem_qh(sc, sqh, head); + splx(s); + ehci_free_sqh(sc, epipe->sqh); +} + +/* + * Abort a device request. + * If this routine is called at splusb() it guarantees that the request + * will be removed from the hardware scheduling and that the callback + * for it will be called with USBD_CANCELLED status. + * It's impossible to guarantee that the requested transfer will not + * have happened since the hardware runs concurrently. + * If the transaction has already happened we rely on the ordinary + * interrupt processing to process it. + * XXX This is most probably wrong. + */ +void +ehci_abort_xfer(usbd_xfer_handle xfer, usbd_status status) +{ +#define exfer EXFER(xfer) + struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; + ehci_softc_t *sc = (ehci_softc_t *)epipe->pipe.device->bus; + ehci_soft_qh_t *sqh = epipe->sqh; + ehci_soft_qtd_t *sqtd; + ehci_physaddr_t cur; + u_int32_t qhstatus; + int s; + int hit; + + DPRINTF(("ehci_abort_xfer: xfer=%p pipe=%p\n", xfer, epipe)); + + if (sc->sc_dying) { + /* If we're dying, just do the software part. */ + s = splusb(); + xfer->status = status; /* make software ignore it */ + usb_uncallout(xfer->timeout_handle, ehci_timeout, xfer); + usb_transfer_complete(xfer); + splx(s); + return; + } + + if (xfer->device->bus->intr_context || !curproc) + panic("ehci_abort_xfer: not in process context\n"); + + /* + * Step 1: Make interrupt routine and hardware ignore xfer. + */ + s = splusb(); + xfer->status = status; /* make software ignore it */ + usb_uncallout(xfer->timeout_handle, ehci_timeout, xfer); + qhstatus = sqh->qh.qh_qtd.qtd_status; + sqh->qh.qh_qtd.qtd_status = qhstatus | htole32(EHCI_QTD_HALTED); + for (sqtd = exfer->sqtdstart; ; sqtd = sqtd->nextqtd) { + sqtd->qtd.qtd_status |= htole32(EHCI_QTD_HALTED); + if (sqtd == exfer->sqtdend) + break; + } + splx(s); + + /* + * Step 2: Wait until we know hardware has finished any possible + * use of the xfer. Also make sure the soft interrupt routine + * has run. + */ + ehci_sync_hc(sc); + s = splusb(); + sc->sc_softwake = 1; + usb_schedsoftintr(&sc->sc_bus); + tsleep(&sc->sc_softwake, PZERO, "ehciab", 0); + splx(s); + + /* + * Step 3: Remove any vestiges of the xfer from the hardware. + * The complication here is that the hardware may have executed + * beyond the xfer we're trying to abort. So as we're scanning + * the TDs of this xfer we check if the hardware points to + * any of them. + */ + s = splusb(); /* XXX why? */ + cur = EHCI_LINK_ADDR(le32toh(sqh->qh.qh_curqtd)); + hit = 0; + for (sqtd = exfer->sqtdstart; ; sqtd = sqtd->nextqtd) { + hit |= cur == sqtd->physaddr; + if (sqtd == exfer->sqtdend) + break; + } + sqtd = sqtd->nextqtd; + /* Zap curqtd register if hardware pointed inside the xfer. */ + if (hit && sqtd != NULL) { + DPRINTFN(1,("ehci_abort_xfer: cur=0x%08x\n", sqtd->physaddr)); + sqh->qh.qh_curqtd = htole32(sqtd->physaddr); /* unlink qTDs */ + sqh->qh.qh_qtd.qtd_status = qhstatus; + } else { + DPRINTFN(1,("ehci_abort_xfer: no hit\n")); + } + + /* + * Step 4: Execute callback. + */ +#ifdef DIAGNOSTIC + exfer->isdone = 1; +#endif + usb_transfer_complete(xfer); + + splx(s); +#undef exfer +} + +void +ehci_timeout(void *addr) +{ + struct ehci_xfer *exfer = addr; + struct ehci_pipe *epipe = (struct ehci_pipe *)exfer->xfer.pipe; + ehci_softc_t *sc = (ehci_softc_t *)epipe->pipe.device->bus; + + DPRINTF(("ehci_timeout: exfer=%p\n", exfer)); +#ifdef USB_DEBUG + if (ehcidebug > 1) + usbd_dump_pipe(exfer->xfer.pipe); +#endif + + if (sc->sc_dying) { + ehci_abort_xfer(&exfer->xfer, USBD_TIMEOUT); + return; + } + + /* Execute the abort in a process context. */ + usb_init_task(&exfer->abort_task, ehci_timeout_task, addr); + usb_add_task(exfer->xfer.pipe->device, &exfer->abort_task); +} + +void +ehci_timeout_task(void *addr) +{ + usbd_xfer_handle xfer = addr; + int s; + + DPRINTF(("ehci_timeout_task: xfer=%p\n", xfer)); + + s = splusb(); + ehci_abort_xfer(xfer, USBD_TIMEOUT); + splx(s); +} + +/************************/ + +Static usbd_status +ehci_device_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_device_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); +} + +Static usbd_status +ehci_device_ctrl_start(usbd_xfer_handle xfer) +{ + ehci_softc_t *sc = (ehci_softc_t *)xfer->pipe->device->bus; + usbd_status err; + + if (sc->sc_dying) + return (USBD_IOERROR); + +#ifdef DIAGNOSTIC + if (!(xfer->rqflags & URQ_REQUEST)) { + /* XXX panic */ + printf("ehci_device_ctrl_transfer: not a request\n"); + return (USBD_INVAL); + } +#endif + + err = ehci_device_request(xfer); + if (err) + return (err); + + if (sc->sc_bus.use_polling) + ehci_waitintr(sc, xfer); + return (USBD_IN_PROGRESS); +} + +void +ehci_device_ctrl_done(usbd_xfer_handle 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;*/ + + DPRINTFN(10,("ehci_ctrl_done: xfer=%p\n", xfer)); + +#ifdef DIAGNOSTIC + if (!(xfer->rqflags & URQ_REQUEST)) { + panic("ehci_ctrl_done: not a request\n"); + } +#endif + + if (xfer->status != USBD_NOMEM) { + ehci_del_intr_list(ex); /* remove from active list */ + ehci_free_sqtd_chain(sc, ex->sqtdstart, NULL); + } + + DPRINTFN(5, ("ehci_ctrl_done: length=%d\n", xfer->actlen)); +} + +/* Abort a device control request. */ +Static void +ehci_device_ctrl_abort(usbd_xfer_handle xfer) +{ + DPRINTF(("ehci_device_ctrl_abort: xfer=%p\n", xfer)); + ehci_abort_xfer(xfer, USBD_CANCELLED); +} + +/* Close a device control pipe. */ +Static void +ehci_device_ctrl_close(usbd_pipe_handle pipe) +{ + ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus; + /*struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;*/ + + DPRINTF(("ehci_device_ctrl_close: pipe=%p\n", pipe)); + ehci_close_pipe(pipe, sc->sc_async_head); +} + +usbd_status +ehci_device_request(usbd_xfer_handle xfer) +{ +#define exfer EXFER(xfer) + struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; + usb_device_request_t *req = &xfer->request; + usbd_device_handle dev = epipe->pipe.device; + ehci_softc_t *sc = (ehci_softc_t *)dev->bus; + int addr = dev->address; + ehci_soft_qtd_t *setup, *stat, *next; + ehci_soft_qh_t *sqh; + int isread; + int len; + usbd_status err; + int s; + + isread = req->bmRequestType & UT_READ; + len = UGETW(req->wLength); + + DPRINTFN(3,("ehci_device_control type=0x%02x, request=0x%02x, " + "wValue=0x%04x, wIndex=0x%04x len=%d, addr=%d, endpt=%d\n", + req->bmRequestType, req->bRequest, UGETW(req->wValue), + UGETW(req->wIndex), len, addr, + epipe->pipe.endpoint->edesc->bEndpointAddress)); + + setup = ehci_alloc_sqtd(sc); + if (setup == NULL) { + err = USBD_NOMEM; + goto bad1; + } + stat = ehci_alloc_sqtd(sc); + if (stat == NULL) { + err = USBD_NOMEM; + goto bad2; + } + + sqh = epipe->sqh; + epipe->u.ctl.length = len; + + /* XXX + * 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. */ + /* XXXX Should not touch ED here! */ + sqh->qh.qh_endp = + (sqh->qh.qh_endp & htole32(~(EHCI_QH_ADDRMASK | EHCI_QG_MPLMASK))) | + htole32( + EHCI_QH_SET_ADDR(addr) | + /* EHCI_QH_DTC | */ + 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 */ + if (len != 0) { + ehci_soft_qtd_t *end; + + err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, + &next, &end); + if (err) + goto bad3; + end->nextqtd = stat; + end->qtd.qtd_next = + end->qtd.qtd_altnext = htole32(stat->physaddr); + /* Start toggle at 1. */ + /*next->qtd.td_flags |= htole32(EHCI_QTD_TOGGLE);*/ + } else { + next = stat; + } + + memcpy(KERNADDR(&epipe->u.ctl.reqdma), req, sizeof *req); + + setup->qtd.qtd_status = htole32( + EHCI_QTD_ACTIVE | + EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) | + EHCI_QTD_SET_CERR(3) | + EHCI_QTD_SET_BYTES(sizeof *req) + ); + setup->qtd.qtd_buffer[0] = htole32(DMAADDR(&epipe->u.ctl.reqdma)); + setup->nextqtd = next; + setup->qtd.qtd_next = setup->qtd.qtd_altnext = htole32(next->physaddr); + setup->xfer = xfer; + setup->len = sizeof *req; + + stat->qtd.qtd_status = htole32( + EHCI_QTD_ACTIVE | + EHCI_QTD_SET_PID(isread ? EHCI_QTD_PID_OUT : EHCI_QTD_PID_IN) | + EHCI_QTD_SET_CERR(3) | + EHCI_QTD_IOC + ); + stat->qtd.qtd_buffer[0] = 0; /* XXX not needed? */ + stat->nextqtd = NULL; + stat->qtd.qtd_next = stat->qtd.qtd_altnext = EHCI_NULL; + stat->xfer = xfer; + stat->len = 0; + +#ifdef EHCI_DEBUG + if (ehcidebug > 5) { + DPRINTF(("ehci_device_request:\n")); + ehci_dump_sqh(sqh); + ehci_dump_sqtds(setup); + } +#endif + + exfer->sqtdstart = setup; + exfer->sqtdend = stat; +#ifdef DIAGNOSTIC + if (!exfer->isdone) { + printf("ehci_device_request: not done, exfer=%p\n", exfer); + } + exfer->isdone = 0; +#endif + + /* Insert qTD in QH list. */ + s = splusb(); + ehci_set_qh_qtd(sqh, setup); + if (xfer->timeout && !sc->sc_bus.use_polling) { + usb_callout(xfer->timeout_handle, MS_TO_TICKS(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_request: status=%x\n", + EOREAD4(sc, EHCI_USBSTS))); + delay(10000); + ehci_dump_regs(sc); + ehci_dump_sqh(sc->sc_async_head); + ehci_dump_sqh(sqh); + ehci_dump_sqtds(setup); + } +#endif + + return (USBD_NORMAL_COMPLETION); + + bad3: + ehci_free_sqtd(sc, stat); + bad2: + ehci_free_sqtd(sc, setup); + bad1: + DPRINTFN(-1,("ehci_device_request: no memory\n")); + xfer->status = err; + usb_transfer_complete(xfer); + return (err); +#undef exfer +} + +/************************/ + +Static usbd_status +ehci_device_bulk_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_device_bulk_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); +} + +usbd_status +ehci_device_bulk_start(usbd_xfer_handle xfer) +{ +#define exfer EXFER(xfer) + struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; + usbd_device_handle dev = epipe->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_bulk_transfer: 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_bulk_transfer: a request\n"); +#endif + + len = xfer->length; + endpt = epipe->pipe.endpoint->edesc->bEndpointAddress; + isread = UE_GET_DIR(endpt) == UE_DIR_IN; + sqh = epipe->sqh; + + epipe->u.bulk.length = len; + + err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, &data, + &dataend); + if (err) { + DPRINTFN(-1,("ehci_device_bulk_transfer: no memory\n")); + xfer->status = err; + usb_transfer_complete(xfer); + return (err); + } + +#ifdef EHCI_DEBUG + if (ehcidebug > 5) { + DPRINTF(("ehci_device_bulk_transfer: 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_bulk_transfer: 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, MS_TO_TICKS(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_bulk_transfer: data(2)\n")); + delay(10000); + DPRINTF(("ehci_device_bulk_transfer: data(3)\n")); + ehci_dump_regs(sc); +#if 0 + printf("async_head:\n"); + ehci_dump_sqh(sc->sc_async_head); +#endif + 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_bulk_abort(usbd_xfer_handle xfer) +{ + DPRINTF(("ehci_device_bulk_abort: xfer=%p\n", xfer)); + ehci_abort_xfer(xfer, USBD_CANCELLED); +} + +/* + * Close a device bulk pipe. + */ +Static void +ehci_device_bulk_close(usbd_pipe_handle pipe) +{ + ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus; + + DPRINTF(("ehci_device_bulk_close: pipe=%p\n", pipe)); + ehci_close_pipe(pipe, sc->sc_async_head); +} + +void +ehci_device_bulk_done(usbd_xfer_handle 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;*/ + + DPRINTFN(10,("ehci_bulk_done: xfer=%p, actlen=%d\n", + xfer, xfer->actlen)); + + if (xfer->status != USBD_NOMEM) { + ehci_del_intr_list(ex); /* remove from active list */ + ehci_free_sqtd_chain(sc, ex->sqtdstart, 0); + } + + DPRINTFN(5, ("ehci_bulk_done: length=%d\n", xfer->actlen)); +} + +/************************/ + +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 new file mode 100644 index 00000000000..341644210b1 --- /dev/null +++ b/sys/dev/usb/ehcireg.h @@ -0,0 +1,288 @@ +/* $OpenBSD: ehcireg.h,v 1.1 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: ehcireg.h,v 1.13 2001/11/23 01:16:27 augustss Exp $ */ + +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * The EHCI 0.96 spec can be found at + * http://developer.intel.com/technology/usb/download/ehci-r096.pdf + * and the USB 2.0 spec at + * http://www.usb.org/developers/data/usb_20.zip + */ + +#ifndef _DEV_PCI_EHCIREG_H_ +#define _DEV_PCI_EHCIREG_H_ + +/*** PCI config registers ***/ + +#define PCI_CBMEM 0x10 /* configuration base MEM */ + +#define PCI_INTERFACE_EHCI 0x20 + +#define PCI_USBREV 0x60 /* RO USB protocol revision */ +#define PCI_USBREV_MASK 0xff +#define PCI_USBREV_PRE_1_0 0x00 +#define PCI_USBREV_1_0 0x10 +#define PCI_USBREV_1_1 0x11 +#define PCI_USBREV_2_0 0x20 + +#define PCI_EHCI_FLADJ 0x61 /*RW Frame len adj, SOF=59488+6*fladj */ + +#define PCI_EHCI_PORTWAKECAP 0x62 /* RW Port wake caps (opt) */ + +/* Regs ar EECP + offset */ +#define PCI_EHCI_USBLEGSUP 0x00 +#define PCI_EHCI_USBLEGCTLSTS 0x04 + +/*** EHCI capability registers ***/ + +#define EHCI_CAPLENGTH 0x00 /*RO Capability register length field */ +/* reserved 0x01 */ +#define EHCI_HCIVERSION 0x02 /* RO Interface version number */ + +#define EHCI_HCSPARAMS 0x04 /* RO Structural parameters */ +#define EHCI_HCS_DEBUGPORT(x) (((x) >> 20) & 0xf) +#define EHCI_HCS_P_INCICATOR(x) ((x) & 0x10000) +#define EHCI_HCS_N_CC(x) (((x) >> 12) & 0xf) /* # of companion ctlrs */ +#define EHCI_HCS_N_PCC(x) (((x) >> 8) & 0xf) /* # of ports per comp. */ +#define EHCI_HCS_PPC(x) ((x) & 0x10) /* port power control */ +#define EHCI_HCS_N_PORTS(x) ((x) & 0xf) /* # of ports */ + +#define EHCI_HCCPARAMS 0x08 /* RO Capability parameters */ +#define EHCI_HCC_EECP(x) (((x) >> 8) & 0xff) /* extended ports caps */ +#define EHCI_HCC_IST(x) (((x) >> 4) & 0xf) /* isoc sched threshold */ +#define EHCI_HCC_ASPC(x) ((x) & 0x4) /* async sched park cap */ +#define EHCI_HCC_PFLF(x) ((x) & 0x2) /* prog frame list flag */ +#define EHCI_HCC_64BIT(x) ((x) & 0x1) /* 64 bit address cap */ + +#define EHCI_HCSP_PORTROUTE 0x0c /*RO Companion port route description */ + +/* EHCI operational registers. Offset given by EHCI_CAPLENGTH register */ +#define EHCI_USBCMD 0x00 /* RO, RW, WO Command register */ +#define EHCI_CMD_ITC_M 0x00ff0000 /* RW interrupt threshold ctrl */ +#define EHCI_CMD_ITC_1 0x00010000 +#define EHCI_CMD_ITC_2 0x00020000 +#define EHCI_CMD_ITC_4 0x00040000 +#define EHCI_CMD_ITC_8 0x00080000 +#define EHCI_CMD_ITC_16 0x00100000 +#define EHCI_CMD_ITC_32 0x00200000 +#define EHCI_CMD_ITC_64 0x00400000 +#define EHCI_CMD_ASPME 0x00000800 /* RW/RO async park enable */ +#define EHCI_CMD_ASPMC 0x00000300 /* RW/RO async park count */ +#define EHCI_CMD_LHCR 0x00000080 /* RW light host ctrl reset */ +#define EHCI_CMD_IAAD 0x00000040 /* RW intr on async adv door bell */ +#define EHCI_CMD_ASE 0x00000020 /* RW async sched enable */ +#define EHCI_CMD_PSE 0x00000010 /* RW periodic sched enable */ +#define EHCI_CMD_FLS_M 0x0000000c /* RW/RO frame list size */ +#define EHCI_CMD_FLS(x) (((x) >> 2) & 3) /* RW/RO frame list size */ +#define EHCI_CMD_HCRESET 0x00000002 /* RW reset */ +#define EHCI_CMD_RS 0x00000001 /* RW run/stop */ + +#define EHCI_USBSTS 0x04 /* RO, RW, RWC Status register */ +#define EHCI_STS_ASS 0x00008000 /* RO async sched status */ +#define EHCI_STS_PSS 0x00004000 /* RO periodic sched status */ +#define EHCI_STS_REC 0x00002000 /* RO reclamation */ +#define EHCI_STS_HCH 0x00001000 /* RO host controller halted */ +#define EHCI_STS_IAA 0x00000020 /* RWC interrupt on async adv */ +#define EHCI_STS_HSE 0x00000010 /* RWC host system error */ +#define EHCI_STS_FLR 0x00000008 /* RWC frame list rollover */ +#define EHCI_STS_PCD 0x00000004 /* RWC port change detect */ +#define EHCI_STS_ERRINT 0x00000002 /* RWC error interrupt */ +#define EHCI_STS_INT 0x00000001 /* RWC interrupt */ +#define EHCI_STS_INTRS(x) ((x) & 0x3f) + +#define EHCI_NORMAL_INTRS (EHCI_STS_IAA | EHCI_STS_HSE | EHCI_STS_PCD | EHCI_STS_ERRINT | EHCI_STS_INT) + +#define EHCI_USBINTR 0x08 /* RW Interrupt register */ +#define EHCI_INTR_IAAE 0x00000020 /* interrupt on async advance ena */ +#define EHCI_INTR_HSEE 0x00000010 /* host system error ena */ +#define EHCI_INTR_FLRE 0x00000008 /* frame list rollover ena */ +#define EHCI_INTR_PCIE 0x00000004 /* port change ena */ +#define EHCI_INTR_UEIE 0x00000002 /* USB error intr ena */ +#define EHCI_INTR_UIE 0x00000001 /* USB intr ena */ + +#define EHCI_FRINDEX 0x0c /* RW Frame Index register */ + +#define EHCI_CTRLDSSEGMENT 0x10 /* RW Control Data Structure Segment */ + +#define EHCI_PERIODICLISTBASE 0x14 /* RW Periodic List Base */ +#define EHCI_ASYNCLISTADDR 0x18 /* RW Async List Base */ + +#define EHCI_CONFIGFLAG 0x40 /* RW Configure Flag register */ +#define EHCI_CONF_CF 0x00000001 /* RW configure flag */ + +#define EHCI_PORTSC(n) (0x40+4*(n)) /* RO, RW, RWC Port Status reg */ +#define EHCI_PS_WKOC_E 0x00400000 /* RW wake on over current ena */ +#define EHCI_PS_WKDSCNNT_E 0x00200000 /* RW wake on disconnect ena */ +#define EHCI_PS_WKCNNT_E 0x00100000 /* RW wake on connect ena */ +#define EHCI_PS_PTC 0x000f0000 /* RW port test control */ +#define EHCI_PS_PIC 0x0000c000 /* RW port indicator control */ +#define EHCI_PS_PO 0x00002000 /* RW port owner */ +#define EHCI_PS_PP 0x00001000 /* RW,RO port power */ +#define EHCI_PS_LS 0x00000c00 /* RO line status */ +#define EHCI_PS_IS_LOWSPEED(x) (((x) & EHCI_PS_LS) == 0x00000400) +#define EHCI_PS_PR 0x00000100 /* RW port reset */ +#define EHCI_PS_SUSP 0x00000080 /* RW suspend */ +#define EHCI_PS_FPR 0x00000040 /* RW force port resume */ +#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 /* 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_PORT_RESET_COMPLETE 2 /* ms */ + +#define EHCI_FLALIGN_ALIGN 0x1000 + +/* No data structure may cross a page boundary. */ +#define EHCI_PAGE_SIZE 0x1000 +#define EHCI_PAGE(x) ((x) &~ 0xfff) +#define EHCI_PAGE_OFFSET(x) ((x) & 0xfff) + +typedef u_int32_t ehci_link_t; +#define EHCI_LINK_TERMINATE 0x00000001 +#define EHCI_LINK_TYPE(x) ((x) & 0x00000006) +#define EHCI_LINK_ITD 0x0 +#define EHCI_LINK_QH 0x2 +#define EHCI_LINK_SITD 0x4 +#define EHCI_LINK_FSTN 0x6 +#define EHCI_LINK_ADDR(x) ((x) &~ 0x1f) + +typedef u_int32_t ehci_physaddr_t; + +/* Isochronous Transfer Descriptor */ +typedef struct { + ehci_link_t itd_next; + /* XXX many more */ +} ehci_itd_t; +#define EHCI_ITD_ALIGN 32 + +/* Split Transaction Isochronous Transfer Descriptor */ +typedef struct { + ehci_link_t sitd_next; + /* XXX many more */ +} ehci_sitd_t; +#define EHCI_SITD_ALIGN 32 + +/* Queue Element Transfer Descriptor */ +#define EHCI_QTD_NBUFFERS 5 +typedef struct { + ehci_link_t qtd_next; + ehci_link_t qtd_altnext; + u_int32_t qtd_status; +#define EHCI_QTD_GET_STATUS(x) (((x) >> 0) & 0xff) +#define EHCI_QTD_ACTIVE 0x80 +#define EHCI_QTD_HALTED 0x40 +#define EHCI_QTD_BUFERR 0x20 +#define EHCI_QTD_BABBLE 0x10 +#define EHCI_QTD_XACTERR 0x08 +#define EHCI_QTD_MISSEDMICRO 0x04 +#define EHCI_QTD_SPLITXSTATE 0x02 +#define EHCI_QTD_PINGSTATE 0x01 +#define EHCI_QTD_STATERRS 0x7c +#define EHCI_QTD_GET_PID(x) (((x) >> 8) & 0x3) +#define EHCI_QTD_SET_PID(x) ((x) << 8) +#define EHCI_QTD_PID_OUT 0x0 +#define EHCI_QTD_PID_IN 0x1 +#define EHCI_QTD_PID_SETUP 0x2 +#define EHCI_QTD_GET_CERR(x) (((x) >> 10) & 0x3) +#define EHCI_QTD_SET_CERR(x) ((x) << 10) +#define EHCI_QTD_GET_C_PAGE(x) (((x) >> 12) & 0x7) +#define EHCI_QTD_SET_C_PAGE(x) ((x) << 12) +#define EHCI_QTD_GET_IOC(x) (((x) >> 15) & 0x1) +#define EHCI_QTD_IOC 0x00008000 +#define EHCI_QTD_GET_BYTES(x) (((x) >> 16) & 0x7fff) +#define EHCI_QTD_SET_BYTES(x) ((x) << 16) +#define EHCI_QTD_GET_TOGGLE(x) (((x) >> 31) & 0x1) +#define EHCI_QTD_TOGGLE 0x80000000 + ehci_physaddr_t qtd_buffer[EHCI_QTD_NBUFFERS]; +} ehci_qtd_t; +#define EHCI_QTD_ALIGN 32 + +/* Queue Head */ +typedef struct { + ehci_link_t qh_link; + u_int32_t qh_endp; +#define EHCI_QH_GET_ADDR(x) (((x) >> 0) & 0x7f) /* endpoint addr */ +#define EHCI_QH_SET_ADDR(x) (x) +#define EHCI_QH_ADDRMASK 0x0000007f +#define EHCI_QH_GET_INACT(x) (((x) >> 7) & 0x01) /* inactivate on next */ +#define EHCI_QH_INACT 0x00000080 +#define EHCI_QH_GET_ENDPT(x) (((x) >> 8) & 0x0f) /* endpoint no */ +#define EHCI_QH_SET_ENDPT(x) ((x) << 8) +#define EHCI_QH_GET_EPS(x) (((x) >> 12) & 0x03) /* endpoint speed */ +#define EHCI_QH_SET_EPS(x) ((x) << 12) +#define EHCI_QH_SPEED_FULL 0x0 +#define EHCI_QH_SPEED_LOW 0x1 +#define EHCI_QH_SPEED_HIGH 0x2 +#define EHCI_QH_GET_DTC(x) (((x) >> 14) & 0x01) /* data toggle control */ +#define EHCI_QH_DTC 0x00004000 +#define EHCI_QH_GET_HRECL(x) (((x) >> 15) & 0x01) /* head of reclamation */ +#define EHCI_QH_HRECL 0x00008000 +#define EHCI_QH_GET_MPL(x) (((x) >> 16) & 0x7ff) /* max packet len */ +#define EHCI_QH_SET_MPL(x) ((x) << 16) +#define EHCI_QG_MPLMASK 0x07ff0000 +#define EHCI_QH_GET_CTL(x) (((x) >> 26) & 0x01) /* control endpoint */ +#define EHCI_QH_CTL 0x08000000 +#define EHCI_QH_GET_NRL(x) (((x) >> 28) & 0x0f) /* NAK reload */ +#define EHCI_QH_SET_NRL(x) ((x) << 28) + u_int32_t qh_endphub; +#define EHCI_QH_GET_SMASK(x) (((x) >> 0) & 0xff) /* intr sched mask */ +#define EHCI_QH_SET_SMASK(x) ((x) << 0) +#define EHCI_QH_GET_CMASK(x) (((x) >> 8) & 0xff) /* split completion mask */ +#define EHCI_QH_SET_CMASK(x) ((x) << 8) +#define EHCI_QH_GET_HUBA(x) (((x) >> 16) & 0x7f) /* hub address */ +#define EHCI_QH_SET_HUBA(x) ((x) << 16) +#define EHCI_QH_GET_PORT(x) (((x) >> 23) & 0x7f) /* hub port */ +#define EHCI_QH_SET_PORT(x) ((x) << 23) +#define EHCI_QH_GET_MULT(x) (((x) >> 30) & 0x03) /* pipe multiplier */ +#define EHCI_QH_SET_MULT(x) ((x) << 30) + ehci_link_t qh_curqtd; + ehci_qtd_t qh_qtd; +} ehci_qh_t; +#define EHCI_QH_ALIGN 32 + +/* Periodic Frame Span Traversal Node */ +typedef struct { + ehci_link_t fstn_link; + ehci_link_t fstn_back; +} ehci_fstn_t; +#define EHCI_FSTN_ALIGN 32 + +#endif /* _DEV_PCI_EHCIREG_H_ */ diff --git a/sys/dev/usb/ehcivar.h b/sys/dev/usb/ehcivar.h new file mode 100644 index 00000000000..1aa7c866580 --- /dev/null +++ b/sys/dev/usb/ehcivar.h @@ -0,0 +1,138 @@ +/* $OpenBSD: ehcivar.h,v 1.1 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: ehcivar.h,v 1.12 2001/12/31 12:16:57 augustss Exp $ */ + +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +typedef struct ehci_soft_qtd { + ehci_qtd_t qtd; + struct ehci_soft_qtd *nextqtd; /* mirrors nextqtd in TD */ + ehci_physaddr_t physaddr; + usbd_xfer_handle xfer; + LIST_ENTRY(ehci_soft_qtd) hnext; + u_int16_t len; +} ehci_soft_qtd_t; +#define EHCI_SQTD_SIZE ((sizeof (struct ehci_soft_qtd) + EHCI_QTD_ALIGN - 1) / EHCI_QTD_ALIGN * EHCI_QTD_ALIGN) +#define EHCI_SQTD_CHUNK (EHCI_PAGE_SIZE / EHCI_SQTD_SIZE) + +typedef struct ehci_soft_qh { + ehci_qh_t qh; + struct ehci_soft_qh *next; + struct ehci_soft_qtd *sqtd; + ehci_physaddr_t physaddr; +} ehci_soft_qh_t; +#define EHCI_SQH_SIZE ((sizeof (struct ehci_soft_qh) + EHCI_QH_ALIGN - 1) / EHCI_QH_ALIGN * EHCI_QH_ALIGN) +#define EHCI_SQH_CHUNK (EHCI_PAGE_SIZE / EHCI_SQH_SIZE) + +struct ehci_xfer { + struct usbd_xfer xfer; + struct usb_task abort_task; + LIST_ENTRY(ehci_xfer) inext; /* list of active xfers */ + ehci_soft_qtd_t *sqtdstart; + ehci_soft_qtd_t *sqtdend; +#ifdef DIAGNOSTIC + int isdone; +#endif +}; +#define EXFER(xfer) ((struct ehci_xfer *)(xfer)) + + +#define EHCI_HASH_SIZE 128 +#define EHCI_COMPANION_MAX 8 + +typedef struct ehci_softc { + struct usbd_bus sc_bus; /* base device */ + bus_space_tag_t iot; + bus_space_handle_t ioh; + bus_size_t sc_size; + u_int sc_offs; /* offset to operational regs */ + + char sc_vendor[16]; /* vendor string for root hub */ + int sc_id_vendor; /* vendor ID for root hub */ + + void *sc_powerhook; /* cookie from power hook */ + void *sc_shutdownhook; /* cookie from shutdown hook */ + + u_int sc_ncomp; + u_int sc_npcomp; + struct usbd_bus *sc_comps[EHCI_COMPANION_MAX]; + + usb_dma_t sc_fldma; + u_int sc_flsize; + + LIST_HEAD(, ehci_xfer) sc_intrhead; + + ehci_soft_qh_t *sc_freeqhs; + ehci_soft_qtd_t *sc_freeqtds; + + int sc_noport; + u_int8_t sc_addr; /* device address */ + u_int8_t sc_conf; /* device configuration */ + usbd_xfer_handle sc_intrxfer; + char sc_isreset; + char sc_softwake; + + u_int32_t sc_eintrs; + ehci_soft_qh_t *sc_async_head; + + SIMPLEQ_HEAD(, usbd_xfer) sc_free_xfers; /* free xfers */ + + struct lock sc_doorbell_lock; + + usb_callout_t sc_tmo_pcd; + + 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)) +#define EREAD2(sc, a) bus_space_read_2((sc)->iot, (sc)->ioh, (a)) +#define EREAD4(sc, a) bus_space_read_4((sc)->iot, (sc)->ioh, (a)) +#define EWRITE1(sc, a, x) bus_space_write_1((sc)->iot, (sc)->ioh, (a), (x)) +#define EWRITE2(sc, a, x) bus_space_write_2((sc)->iot, (sc)->ioh, (a), (x)) +#define EWRITE4(sc, a, x) bus_space_write_4((sc)->iot, (sc)->ioh, (a), (x)) +#define EOREAD1(sc, a) bus_space_read_1((sc)->iot, (sc)->ioh, (sc)->sc_offs+(a)) +#define EOREAD2(sc, a) bus_space_read_2((sc)->iot, (sc)->ioh, (sc)->sc_offs+(a)) +#define EOREAD4(sc, a) bus_space_read_4((sc)->iot, (sc)->ioh, (sc)->sc_offs+(a)) +#define EOWRITE1(sc, a, x) bus_space_write_1((sc)->iot, (sc)->ioh, (sc)->sc_offs+(a), (x)) +#define EOWRITE2(sc, a, x) bus_space_write_2((sc)->iot, (sc)->ioh, (sc)->sc_offs+(a), (x)) +#define EOWRITE4(sc, a, x) bus_space_write_4((sc)->iot, (sc)->ioh, (sc)->sc_offs+(a), (x)) + +usbd_status ehci_init(ehci_softc_t *); +int ehci_intr(void *); +int ehci_detach(ehci_softc_t *, int); +int ehci_activate(device_ptr_t, enum devact); diff --git a/sys/dev/usb/files.usb b/sys/dev/usb/files.usb index c3132ad709b..77fa22bf4ab 100644 --- a/sys/dev/usb/files.usb +++ b/sys/dev/usb/files.usb @@ -1,5 +1,5 @@ -# $OpenBSD: files.usb,v 1.21 2001/10/02 01:37:36 millert Exp $ -# $NetBSD: files.usb,v 1.16 2000/02/14 20:29:54 augustss Exp $ +# $OpenBSD: files.usb,v 1.22 2002/05/07 18:08:04 nate Exp $ +# $NetBSD: files.usb,v 1.38 2002/01/02 03:21:36 augustss Exp $ # # Config file and device description for machine-independent USB code. # Included by ports that need it. Ports that use it must provide @@ -7,8 +7,7 @@ device usb { } attach usb at usbus -file dev/usb/hid.c usb -file dev/usb/usb.c usb needs-flag +file dev/usb/usb.c usb needs-flag file dev/usb/usbdi.c usb file dev/usb/usbdi_util.c usb file dev/usb/usb_mem.c usb @@ -35,62 +34,91 @@ device uaudio: audio, auconv, mulaw attach uaudio at uhub file dev/usb/uaudio.c uaudio +# MIDI devices +device umidi: midibus +attach umidi at uhub +file dev/usb/umidi.c umidi +file dev/usb/umidi_quirks.c umidi + # Modem and com serial port device ucom attach ucom at ucombus -file dev/usb/ucom.c ucom | ucombus needs-flag +file dev/usb/ucom.c ucom | ucombus needs-flag + # Generic devices device ugen attach ugen at uhub -file dev/usb/ugen.c ugen needs-flag +file dev/usb/ugen.c ugen needs-flag + + +# HID +# HID "bus" +define uhidbus {[ reportid = -1 ]} + +# HID processing +define hid +file dev/usb/hid.c hid + +# HID root device for multiple report IDs +device uhidev: hid, uhidbus +attach uhidev at uhub +file dev/usb/uhidev.c uhidev # Generic HID devices -device uhid -attach uhid at uhub -file dev/usb/uhid.c uhid needs-flag +device uhid: hid +attach uhid at uhidbus +file dev/usb/uhid.c uhid needs-flag # Keyboards -device ukbd: wskbddev -attach ukbd at uhub -file dev/usb/ukbd.c ukbd needs-flag -file dev/usb/ukbdmap.c ukbd +device ukbd: hid, wskbddev +attach ukbd at uhidbus +file dev/usb/ukbd.c ukbd needs-flag +file dev/usb/ukbdmap.c ukbd + +# Mice +device ums: hid, wsmousedev +attach ums at uhidbus +file dev/usb/ums.c ums + # Printers device ulpt attach ulpt at uhub -file dev/usb/ulpt.c ulpt needs-flag +file dev/usb/ulpt.c ulpt needs-flag + # Mass storage -device umass: scsi, atapi +device umass: scsi, atapi, ata attach umass at uhub file dev/usb/umass.c umass +#file dev/usb/umass_isdata.c umass & wd +file dev/usb/umass_quirks.c umass +file dev/usb/umass_scsi.c umass & (scsibus | atapiscsi) -# Modems -device umodem: ucombus -attach umodem at uhub -file dev/usb/umodem.c umodem - -# Mice -device ums: wsmousedev -attach ums at uhub -file dev/usb/ums.c ums +# Misc # Diamond Multimedia Rio 500 device urio attach urio at uhub -file dev/usb/urio.c urio needs-flag +file dev/usb/urio.c urio needs-flag # Handspring Visor device uvisor: ucombus attach uvisor at uhub file dev/usb/uvisor.c uvisor -# YAP firmware loader +# YAP phone firmware loader device uyap: ezload attach uyap at uhub file dev/usb/uyap.c uyap +# D-Link DSB-R100 FM radio +device udsbr: radio +attach udsbr at uhub +file dev/usb/udsbr.c udsbr + + # Ethernet adapters # ADMtek AN986 Pegasus device aue: ether, ifnet, mii, ifmedia @@ -108,25 +136,48 @@ attach kue at uhub file dev/usb/if_kue.c kue # Prolific PL2302 host-host -device upl: ifnet +device upl: ether, ifnet, ifmedia attach upl at uhub file dev/usb/if_upl.c upl +# Realtek RTL8150L(M) +device url: ether, ifnet, mii +attach url at uhub +file dev/usb/if_url.c url + + # Serial drivers +# Modems +device umodem: ucombus +attach umodem at uhub +file dev/usb/umodem.c umodem + # FTDI serial driver device uftdi: ucombus attach uftdi at uhub file dev/usb/uftdi.c uftdi # Prolific PL2303 serial driver -device uplcom: ucombus -attach uplcom at uhub -file dev/usb/uplcom.c uplcom +device uplcom: ucombus +attach uplcom at uhub +file dev/usb/uplcom.c uplcom + +# MCT USB-232 serial driver +device umct: ucombus +attach umct at uhub +file dev/usb/umct.c umct + +# SUNTAC Slipper U VS-10U driver +device uvscom: ucombus +attach uvscom at uhub +file dev/usb/uvscom.c uvscom + # Scanners +# Generic scanner support device uscanner attach uscanner at uhub -file dev/usb/uscanner.c uscanner needs-flag +file dev/usb/uscanner.c uscanner needs-flag # Avision SCSI over USB, HP5300 device usscanner: scsi diff --git a/sys/dev/usb/hid.c b/sys/dev/usb/hid.c index dcd656415cc..2a8a91a35cb 100644 --- a/sys/dev/usb/hid.c +++ b/sys/dev/usb/hid.c @@ -1,5 +1,5 @@ -/* $OpenBSD: hid.c,v 1.9 2000/11/08 18:10:37 aaron Exp $ */ -/* $NetBSD: hid.c,v 1.16 2000/06/01 14:28:57 augustss Exp $ */ +/* $OpenBSD: hid.c,v 1.10 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: hid.c,v 1.22 2002/01/12 17:11:03 tsutsui Exp $ */ /* $FreeBSD: src/sys/dev/usb/hid.c,v 1.11 1999/11/17 22:33:39 n_hibma Exp $ */ /* @@ -51,10 +51,10 @@ #include <dev/usb/hid.h> -#ifdef UHID_DEBUG -#define DPRINTF(x) if (usbdebug) logprintf x -#define DPRINTFN(n,x) if (usbdebug>(n)) logprintf x -extern int usbdebug; +#ifdef UHIDEV_DEBUG +#define DPRINTF(x) if (uhidevdebug) logprintf x +#define DPRINTFN(n,x) if (uhidevdebug>(n)) logprintf x +extern int uhidevdebug; #else #define DPRINTF(x) #define DPRINTFN(n,x) @@ -62,7 +62,7 @@ extern int usbdebug; Static void hid_clear_local(struct hid_item *); -#define MAXUSAGE 100 +#define MAXUSAGE 256 struct hid_data { u_char *start; u_char *end; @@ -73,13 +73,14 @@ struct hid_data { int minset; int multi; int multimax; - int kindset; + enum hid_kind kind; }; Static void hid_clear_local(struct hid_item *c) { + DPRINTFN(5,("hid_clear_local\n")); c->usage = 0; c->usage_minimum = 0; c->usage_maximum = 0; @@ -93,15 +94,18 @@ hid_clear_local(struct hid_item *c) } struct hid_data * -hid_start_parse(void *d, int len, int kindset) +hid_start_parse(void *d, int len, enum hid_kind kind) { struct hid_data *s; s = malloc(sizeof *s, M_TEMP, M_WAITOK); + if (s == NULL) + panic("hid_start_parse"); memset(s, 0, sizeof *s); + s->start = s->p = d; s->end = (char *)d + len; - s->kindset = kindset; + s->kind = kind; return (s); } @@ -128,15 +132,19 @@ hid_get_item(struct hid_data *s, struct hid_item *h) u_char *p; struct hid_item *hi; int i; + enum hid_kind retkind; top: + DPRINTFN(5,("hid_get_item: multi=%d multimax=%d\n", + s->multi, s->multimax)); if (s->multimax != 0) { if (s->multi < s->multimax) { c->usage = s->usages[min(s->multi, s->nu-1)]; s->multi++; *h = *c; c->loc.pos += c->loc.size; - h->next = 0; + h->next = NULL; + DPRINTFN(5,("return multi\n")); return (1); } else { c->loc.count = s->multimax; @@ -174,12 +182,12 @@ hid_get_item(struct hid_data *s, struct hid_item *h) dval = 0; break; case 1: - dval = (int8_t)*data++; + dval = /*(int8_t)*/ *data++; break; case 2: dval = *data++; dval |= *data++ << 8; - dval = (int16_t)dval; + dval = /*(int16_t)*/ dval; break; case 4: dval = *data++; @@ -192,15 +200,22 @@ hid_get_item(struct hid_data *s, struct hid_item *h) continue; } + DPRINTFN(5,("hid_get_item: bType=%d bTag=%d dval=%d\n", + bType, bTag, dval)); switch (bType) { case 0: /* Main */ switch (bTag) { case 8: /* Input */ - if (!(s->kindset & (1 << hid_input))) + retkind = hid_input; + ret: + if (s->kind != retkind) { + s->minset = 0; + s->nu = 0; + hid_clear_local(c); continue; - c->kind = hid_input; + } + c->kind = retkind; c->flags = dval; - ret: if (c->flags & HIO_VARIABLE) { s->multimax = c->loc.count; s->multi = 0; @@ -217,19 +232,18 @@ hid_get_item(struct hid_data *s, struct hid_item *h) } goto top; } else { + c->usage = c->_usage_page; /* XXX */ *h = *c; - h->next = 0; + h->next = NULL; c->loc.pos += - c->loc.size * c->loc.count; - hid_clear_local(c); + c->loc.size * c->loc.count; s->minset = 0; + s->nu = 0; + hid_clear_local(c); return (1); } case 9: /* Output */ - if (!(s->kindset & (1 << hid_output))) - continue; - c->kind = hid_output; - c->flags = dval; + retkind = hid_output; goto ret; case 10: /* Collection */ c->kind = hid_collection; @@ -240,16 +254,12 @@ hid_get_item(struct hid_data *s, struct hid_item *h) s->nu = 0; return (1); case 11: /* Feature */ - if (!(s->kindset & (1 << hid_feature))) - continue; - c->kind = hid_feature; - c->flags = dval; + retkind = hid_feature; goto ret; case 12: /* End collection */ c->kind = hid_endcollection; c->collevel--; *h = *c; - hid_clear_local(c); s->nu = 0; return (1); default: @@ -285,6 +295,7 @@ hid_get_item(struct hid_data *s, struct hid_item *h) break; case 8: c->report_ID = dval; + c->loc.pos = 0; break; case 9: c->loc.count = dval; @@ -367,35 +378,51 @@ hid_get_item(struct hid_data *s, struct hid_item *h) } int -hid_report_size(void *buf, int len, enum hid_kind k, u_int8_t *idp) +hid_report_size(void *buf, int len, enum hid_kind k, u_int8_t id) { struct hid_data *d; struct hid_item h; - int size, id; + int lo, hi; - id = 0; - for (d = hid_start_parse(buf, len, 1<<k); hid_get_item(d, &h); ) - if (h.report_ID != 0) - id = h.report_ID; + h.report_ID = 0; + lo = hi = -1; + DPRINTFN(2,("hid_report_size: kind=%d id=%d\n", k, id)); + for (d = hid_start_parse(buf, len, k); hid_get_item(d, &h); ) { + DPRINTFN(2,("hid_report_size: item kind=%d id=%d pos=%d " + "size=%d count=%d\n", + h.kind, h.report_ID, h.loc.pos, h.loc.size, + h.loc.count)); + if (h.report_ID == id && h.kind == k) { + if (lo < 0) { + lo = h.loc.pos; +#ifdef DIAGNOSTIC + if (lo != 0) { + printf("hid_report_size: lo != 0\n"); + } +#endif + } + hi = h.loc.pos + h.loc.size * h.loc.count; + DPRINTFN(2,("hid_report_size: lo=%d hi=%d\n", lo, hi)); + } + } hid_end_parse(d); - size = h.loc.pos; - if (id != 0) { - size += 8; - *idp = id; /* XXX wrong */ - } else - *idp = 0; - return ((size + 7) / 8); + return ((hi - lo + 7) / 8); } int -hid_locate(void *desc, int size, u_int32_t u, enum hid_kind k, +hid_locate(void *desc, int size, u_int32_t u, u_int8_t id, enum hid_kind k, struct hid_location *loc, u_int32_t *flags) { struct hid_data *d; struct hid_item h; - for (d = hid_start_parse(desc, size, 1<<k); hid_get_item(d, &h); ) { - if (h.kind == k && !(h.flags & HIO_CONST) && h.usage == u) { + h.report_ID = 0; + DPRINTFN(5,("hid_locate: enter usage=0x%x kind=%d id=%d\n", u, k, id)); + for (d = hid_start_parse(desc, size, k); hid_get_item(d, &h); ) { + DPRINTFN(5,("hid_locate: usage=0x%x kind=%d id=%d flags=0x%x\n", + h.usage, h.kind, h.report_ID, h.flags)); + if (h.kind == k && !(h.flags & HIO_CONST) && + h.usage == u && h.report_ID == id) { if (loc != NULL) *loc = h.loc; if (flags != NULL) @@ -437,19 +464,33 @@ hid_get_data(u_char *buf, struct hid_location *loc) } int -hid_is_collection(void *desc, int size, u_int32_t usage) +hid_is_collection(void *desc, int size, u_int8_t id, u_int32_t usage) { struct hid_data *hd; struct hid_item hi; - int err; + u_int32_t coll_usage = ~0; - hd = hid_start_parse(desc, size, hid_input); + hd = hid_start_parse(desc, size, hid_none); if (hd == NULL) return (0); - err = hid_get_item(hd, &hi) && - hi.kind == hid_collection && - hi.usage == usage; + DPRINTFN(2,("hid_is_collection: id=%d usage=0x%x\n", id, usage)); + while (hid_get_item(hd, &hi)) { + DPRINTFN(2,("hid_is_collection: kind=%d id=%d usage=0x%x" + "(0x%x)\n", + hi.kind, hi.report_ID, hi.usage, coll_usage)); + if (hi.kind == hid_collection && + hi.collection == HCOLL_APPLICATION) + coll_usage = hi.usage; + if (hi.kind == hid_endcollection && + coll_usage == usage && + hi.report_ID == id) { + DPRINTFN(2,("hid_is_collection: found\n")); + hid_end_parse(hd); + return (1); + } + } + DPRINTFN(2,("hid_is_collection: not found\n")); hid_end_parse(hd); - return (err); + return (0); } diff --git a/sys/dev/usb/hid.h b/sys/dev/usb/hid.h index 73608bbbaff..928a86ce337 100644 --- a/sys/dev/usb/hid.h +++ b/sys/dev/usb/hid.h @@ -1,5 +1,5 @@ -/* $OpenBSD: hid.h,v 1.4 2000/11/08 18:10:37 aaron Exp $ */ -/* $NetBSD: hid.h,v 1.6 2000/06/01 14:28:57 augustss Exp $ */ +/* $OpenBSD: hid.h,v 1.5 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: hid.h,v 1.7 2001/12/28 17:32:36 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/hid.h,v 1.7 1999/11/17 22:33:40 n_hibma Exp $ */ /* @@ -40,7 +40,12 @@ */ enum hid_kind { - hid_input, hid_output, hid_feature, hid_collection, hid_endcollection + hid_input, + hid_output, + hid_feature, + hid_collection, + hid_endcollection, + hid_none }; struct hid_location { @@ -81,11 +86,11 @@ struct hid_item { struct hid_item *next; }; -struct hid_data *hid_start_parse(void *d, int len, int kindset); +struct hid_data *hid_start_parse(void *d, int len, enum hid_kind kind); void hid_end_parse(struct hid_data *s); int hid_get_item(struct hid_data *s, struct hid_item *h); -int hid_report_size(void *buf, int len, enum hid_kind k, u_int8_t *id); -int hid_locate(void *desc, int size, u_int32_t usage, enum hid_kind kind, - struct hid_location *loc, u_int32_t *flags); +int hid_report_size(void *buf, int len, enum hid_kind k, u_int8_t id); +int hid_locate(void *desc, int size, u_int32_t usage, u_int8_t id, + enum hid_kind kind, struct hid_location *loc, u_int32_t *flags); u_long hid_get_data(u_char *buf, struct hid_location *loc); -int hid_is_collection(void *desc, int size, u_int32_t usage); +int hid_is_collection(void *desc, int size, u_int8_t id, u_int32_t usage); diff --git a/sys/dev/usb/if_upl.c b/sys/dev/usb/if_upl.c index 2684f9efddf..49e29bdce8b 100644 --- a/sys/dev/usb/if_upl.c +++ b/sys/dev/usb/if_upl.c @@ -1,5 +1,5 @@ -/* $OpenBSD: if_upl.c,v 1.5 2002/03/12 09:51:20 kjc Exp $ */ -/* $NetBSD: if_upl.c,v 1.15 2001/06/14 05:44:27 itojun Exp $ */ +/* $OpenBSD: if_upl.c,v 1.6 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: if_upl.c,v 1.17 2002/03/05 04:12:59 itojun Exp $ */ /* * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. @@ -78,27 +78,19 @@ #include <net/bpf.h> #endif -#if defined(__NetBSD__) #ifdef INET #include <netinet/in.h> #include <netinet/in_var.h> +#if defined(__NetBSD__) #include <netinet/if_inarp.h> -#else -#error upl without INET? -#endif -#endif - -#if defined(__OpenBSD__) -#ifdef INET -#include <netinet/in.h> +#elif defined(__OpenBSD__) #include <netinet/in_systm.h> -#include <netinet/in_var.h> #include <netinet/ip.h> #include <netinet/if_ether.h> +#endif #else #error upl without INET? #endif -#endif #ifdef NS #include <netns/ns.h> @@ -333,20 +325,22 @@ USB_ATTACH(upl) ifp->if_addrlen = 0; ifp->if_hdrlen = 0; ifp->if_output = upl_output; + ifp->if_baudrate = 12000000; #if defined(__NetBSD__) ifp->if_input = upl_input; + ifp->if_dlt = DLT_RAW; #endif - ifp->if_baudrate = 12000000; IFQ_SET_READY(&ifp->if_snd); /* Attach the interface. */ if_attach(ifp); +#if defined(__NetBSD__) + if_alloc_sadl(ifp); +#endif -#if NBPFILTER > 0 -#if defined(__NetBSD__) || defined(__FreeBSD__) +#if defined(__NetBSD__) && NBPFILTER > 0 bpfattach(ifp, DLT_RAW, 0); #endif -#endif #if NRND > 0 rnd_attach_source(&sc->sc_rnd_source, USBDEVNAME(sc->sc_dev), RND_TYPE_NET, 0); diff --git a/sys/dev/usb/if_url.c b/sys/dev/usb/if_url.c new file mode 100644 index 00000000000..d0d542a4171 --- /dev/null +++ b/sys/dev/usb/if_url.c @@ -0,0 +1,1609 @@ +/* $OpenBSD: if_url.c,v 1.1 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: if_url.c,v 1.2 2002/03/28 21:49:19 ichiro Exp $ */ +/* + * Copyright (c) 2001, 2002 + * Shingo WATANABE <nabe@nabechan.org>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Shingo WATANABE. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * The RTL8150L(Realtek USB to fast ethernet controller) spec can be found at + * ftp://ftp.realtek.com.tw/lancard/data_sheet/8150/8150v14.pdf + * ftp://152.104.125.40/lancard/data_sheet/8150/8150v14.pdf + */ + +/* + * TODO: + * Interrupt Endpoint support + * External PHYs + * powerhook() support? + */ + +#include "opt_inet.h" +#include "opt_ns.h" +#include "bpfilter.h" +#include "rnd.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/lock.h> +#include <sys/mbuf.h> +#include <sys/kernel.h> +#include <sys/socket.h> + +#include <sys/device.h> +#if NRND > 0 +#include <sys/rnd.h> +#endif + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_dl.h> +#include <net/if_media.h> + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif +#define BPF_MTAP(ifp, m) bpf_mtap((ifp)->if_bpf, (m)) + +#include <net/if_ether.h> +#ifdef INET +#include <netinet/in.h> +#include <netinet/if_inarp.h> +#endif +#ifdef NS +#include <netns/ns.h> +#include <netns/ns_if.h> +#endif + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> +#include <dev/mii/urlphyreg.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usbdevs.h> + +#include <dev/usb/if_urlreg.h> + + +/* Function declarations */ +USB_DECLARE_DRIVER(url); + +Static int url_openpipes(struct url_softc *); +Static int url_rx_list_init(struct url_softc *); +Static int url_tx_list_init(struct url_softc *); +Static int url_newbuf(struct url_softc *, struct url_chain *, struct mbuf *); +Static void url_start(struct ifnet *); +Static int url_send(struct url_softc *, struct mbuf *, int); +Static void url_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status); +Static void url_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status); +Static void url_tick(void *); +Static void url_tick_task(void *); +Static int url_ioctl(struct ifnet *, u_long, caddr_t); +Static void url_stop_task(struct url_softc *); +Static void url_stop(struct ifnet *, int); +Static void url_watchdog(struct ifnet *); +Static int url_ifmedia_change(struct ifnet *); +Static void url_ifmedia_status(struct ifnet *, struct ifmediareq *); +Static void url_lock_mii(struct url_softc *); +Static void url_unlock_mii(struct url_softc *); +Static int url_int_miibus_readreg(device_ptr_t, int, int); +Static void url_int_miibus_writereg(device_ptr_t, int, int, int); +Static void url_miibus_statchg(device_ptr_t); +Static int url_init(struct ifnet *); +Static void url_setmulti(struct url_softc *); +Static void url_reset(struct url_softc *); + +Static int url_csr_read_1(struct url_softc *, int); +Static int url_csr_read_2(struct url_softc *, int); +Static int url_csr_write_1(struct url_softc *, int, int); +Static int url_csr_write_2(struct url_softc *, int, int); +Static int url_csr_write_4(struct url_softc *, int, int); +Static int url_mem(struct url_softc *, int, int, void *, int); + +/* Macros */ +#ifdef URL_DEBUG +#define DPRINTF(x) if (urldebug) logprintf x +#define DPRINTFN(n,x) if (urldebug >= (n)) logprintf x +int urldebug = 0; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +#define URL_SETBIT(sc, reg, x) \ + url_csr_write_1(sc, reg, url_csr_read_1(sc, reg) | (x)) + +#define URL_SETBIT2(sc, reg, x) \ + url_csr_write_2(sc, reg, url_csr_read_2(sc, reg) | (x)) + +#define URL_CLRBIT(sc, reg, x) \ + url_csr_write_1(sc, reg, url_csr_read_1(sc, reg) & ~(x)) + +#define URL_CLRBIT2(sc, reg, x) \ + url_csr_write_2(sc, reg, url_csr_read_2(sc, reg) & ~(x)) + +static const struct url_type { + struct usb_devno url_dev; + u_int16_t url_flags; +#define URL_EXT_PHY 0x0001 +} url_devs [] = { + /* MELCO LUA-KTX */ + {{ USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAKTX }, 0}, + /* GREEN HOUSE USBKR100 */ + {{ USB_VENDOR_GREENHOUSE2, USB_PRODUCT_GREENHOUSE2_USBKR100}, 0} +}; +#define url_lookup(v, p) ((struct url_type *)usb_lookup(url_devs, v, p)) + + +/* Probe */ +USB_MATCH(url) +{ + USB_MATCH_START(url, uaa); + + if (uaa->iface != NULL) + return (UMATCH_NONE); + + return (url_lookup(uaa->vendor, uaa->product) != NULL ? + UMATCH_VENDOR_PRODUCT : UMATCH_NONE); +} +/* Attach */ +USB_ATTACH(url) +{ + USB_ATTACH_START(url, sc, uaa); + usbd_device_handle dev = uaa->device; + usbd_interface_handle iface; + usbd_status err; + usb_interface_descriptor_t *id; + usb_endpoint_descriptor_t *ed; + char devinfo[1024]; + char *devname = USBDEVNAME(sc->sc_dev); + struct ifnet *ifp; + struct mii_data *mii; + u_char eaddr[ETHER_ADDR_LEN]; + int i, s; + + usbd_devinfo(dev, 0, devinfo); + USB_ATTACH_SETUP; + printf("%s: %s\n", devname, devinfo); + + /* Move the device into the configured state. */ + err = usbd_set_config_no(dev, URL_CONFIG_NO, 1); + if (err) { + printf("%s: setting config no failed\n", devname); + goto bad; + } + + usb_init_task(&sc->sc_tick_task, url_tick_task, sc); + lockinit(&sc->sc_mii_lock, PZERO, "urlmii", 0, 0); + usb_init_task(&sc->sc_stop_task, (void (*)(void *)) url_stop_task, sc); + + /* get control interface */ + err = usbd_device2interface_handle(dev, URL_IFACE_INDEX, &iface); + if (err) { + printf("%s: failed to get interface, err=%s\n", devname, + usbd_errstr(err)); + goto bad; + } + + sc->sc_udev = dev; + sc->sc_ctl_iface = iface; + sc->sc_flags = url_lookup(uaa->vendor, uaa->product)->url_flags; + + /* get interface descriptor */ + id = usbd_get_interface_descriptor(sc->sc_ctl_iface); + + /* find endpoints */ + sc->sc_bulkin_no = sc->sc_bulkout_no = sc->sc_intrin_no = -1; + for (i = 0; i < id->bNumEndpoints; i++) { + ed = usbd_interface2endpoint_descriptor(sc->sc_ctl_iface, i); + if (ed == NULL) { + printf("%s: couldn't get endpoint %d\n", devname, i); + goto bad; + } + if ((ed->bmAttributes & UE_XFERTYPE) == UE_BULK && + UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN) + sc->sc_bulkin_no = ed->bEndpointAddress; /* RX */ + else if ((ed->bmAttributes & UE_XFERTYPE) == UE_BULK && + UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT) + sc->sc_bulkout_no = ed->bEndpointAddress; /* TX */ + else if ((ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT && + UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN) + sc->sc_intrin_no = ed->bEndpointAddress; /* Status */ + } + + if (sc->sc_bulkin_no == -1 || sc->sc_bulkout_no == -1 || + sc->sc_intrin_no == -1) { + printf("%s: missing endpoint\n", devname); + goto bad; + } + + s = splnet(); + + /* reset the adapter */ + url_reset(sc); + + /* Get Ethernet Address */ + err = url_mem(sc, URL_CMD_READMEM, URL_IDR0, (void *)eaddr, + ETHER_ADDR_LEN); + if (err) { + printf("%s: read MAC address faild\n", devname); + splx(s); + goto bad; + } + + /* Print Ethernet Address */ + printf("%s: Ethernet address %s\n", devname, ether_sprintf(eaddr)); + + /* initialize interface infomation */ + ifp = GET_IFP(sc); + ifp->if_softc = sc; + strncpy(ifp->if_xname, devname, IFNAMSIZ); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_start = url_start; + ifp->if_ioctl = url_ioctl; + ifp->if_watchdog = url_watchdog; + ifp->if_init = url_init; + ifp->if_stop = url_stop; + + IFQ_SET_READY(&ifp->if_snd); + + /* + * Do ifmedia setup. + */ + mii = &sc->sc_mii; + mii->mii_ifp = ifp; + mii->mii_readreg = url_int_miibus_readreg; + mii->mii_writereg = url_int_miibus_writereg; +#if 0 + if (sc->sc_flags & URL_EXT_PHY) { + mii->mii_readreg = url_ext_miibus_readreg; + mii->mii_writereg = url_ext_miibus_writereg; + } +#endif + mii->mii_statchg = url_miibus_statchg; + mii->mii_flags = MIIF_AUTOTSLEEP; + ifmedia_init(&mii->mii_media, 0, + url_ifmedia_change, url_ifmedia_status); + mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0); + if (LIST_FIRST(&mii->mii_phys) == NULL) { + ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL); + ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_NONE); + } else + ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO); + + /* attach the interface */ + if_attach(ifp); + Ether_ifattach(ifp, eaddr); + +#if NRND > 0 + rnd_attach_source(&sc->rnd_source, devname, RND_TYPE_NET, 0); +#endif + + usb_callout_init(sc->sc_stat_ch); + sc->sc_attached = 1; + splx(s); + + usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, dev, USBDEV(sc->sc_dev)); + + USB_ATTACH_SUCCESS_RETURN; + + bad: + sc->sc_dying = 1; + USB_ATTACH_ERROR_RETURN; +} + +/* detach */ +USB_DETACH(url) +{ + USB_DETACH_START(url, sc); + struct ifnet *ifp = GET_IFP(sc); + int s; + + DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __FUNCTION__)); + + /* Detached before attached finished */ + if (!sc->sc_attached) + return (0); + + usb_uncallout(sc->sc_stat_ch, url_tick, sc); + + /* Remove any pending tasks */ + usb_rem_task(sc->sc_udev, &sc->sc_tick_task); + usb_rem_task(sc->sc_udev, &sc->sc_stop_task); + + s = splusb(); + + if (--sc->sc_refcnt >= 0) { + /* Wait for processes to go away */ + usb_detach_wait(USBDEV(sc->sc_dev)); + } + + if (ifp->if_flags & IFF_RUNNING) + url_stop(GET_IFP(sc), 1); + +#if NRND > 0 + rnd_detach_source(&sc->rnd_source); +#endif + mii_detach(&sc->sc_mii, MII_PHY_ANY, MII_OFFSET_ANY); + ifmedia_delete_instance(&sc->sc_mii.mii_media, IFM_INST_ANY); + ether_ifdetach(ifp); + if_detach(ifp); + +#ifdef DIAGNOSTIC + if (sc->sc_pipe_tx != NULL) + printf("%s: detach has active tx endpoint.\n", + USBDEVNAME(sc->sc_dev)); + if (sc->sc_pipe_rx != NULL) + printf("%s: detach has active rx endpoint.\n", + USBDEVNAME(sc->sc_dev)); + if (sc->sc_pipe_intr != NULL) + printf("%s: detach has active intr endpoint.\n", + USBDEVNAME(sc->sc_dev)); +#endif + + sc->sc_attached = 0; + + splx(s); + + usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, + USBDEV(sc->sc_dev)); + + return (0); +} + +/* read/write memory */ +Static int +url_mem(struct url_softc *sc, int cmd, int offset, void *buf, int len) +{ + usb_device_request_t req; + usbd_status err; + + if (sc == NULL) + return (0); + + DPRINTFN(0x200, + ("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __FUNCTION__)); + + if (sc->sc_dying) + return (0); + + if (cmd == URL_CMD_READMEM) + req.bmRequestType = UT_READ_VENDOR_DEVICE; + else + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = URL_REQ_MEM; + USETW(req.wValue, offset); + USETW(req.wIndex, 0x0000); + USETW(req.wLength, len); + + sc->sc_refcnt++; + err = usbd_do_request(sc->sc_udev, &req, buf); + if (--sc->sc_refcnt < 0) + usb_detach_wakeup(USBDEV(sc->sc_dev)); + if (err) { + DPRINTF(("%s: url_mem(): %s failed. off=%04x, err=%d\n", + USBDEVNAME(sc->sc_dev), + cmd == URL_CMD_READMEM ? "read" : "write", + offset, err)); + } + + return (err); +} + +/* read 1byte from register */ +Static int +url_csr_read_1(struct url_softc *sc, int reg) +{ + u_int8_t val = 0; + + DPRINTFN(0x100, + ("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __FUNCTION__)); + + if (sc->sc_dying) + return (0); + + return (url_mem(sc, URL_CMD_READMEM, reg, &val, 1) ? 0 : val); +} + +/* read 2bytes from register */ +Static int +url_csr_read_2(struct url_softc *sc, int reg) +{ + uWord val; + + DPRINTFN(0x100, + ("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __FUNCTION__)); + + if (sc->sc_dying) + return (0); + + USETW(val, 0); + return (url_mem(sc, URL_CMD_READMEM, reg, &val, 2) ? 0 : UGETW(val)); +} + +/* write 1byte to register */ +Static int +url_csr_write_1(struct url_softc *sc, int reg, int aval) +{ + u_int8_t val = aval; + + DPRINTFN(0x100, + ("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __FUNCTION__)); + + if (sc->sc_dying) + return (0); + + return (url_mem(sc, URL_CMD_WRITEMEM, reg, &val, 1) ? -1 : 0); +} + +/* write 2bytes to register */ +Static int +url_csr_write_2(struct url_softc *sc, int reg, int aval) +{ + uWord val; + + DPRINTFN(0x100, + ("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __FUNCTION__)); + + USETW(val, aval); + + if (sc->sc_dying) + return (0); + + return (url_mem(sc, URL_CMD_WRITEMEM, reg, &val, 2) ? -1 : 0); +} + +/* write 4bytes to register */ +Static int +url_csr_write_4(struct url_softc *sc, int reg, int aval) +{ + uDWord val; + + DPRINTFN(0x100, + ("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __FUNCTION__)); + + USETDW(val, aval); + + if (sc->sc_dying) + return (0); + + return (url_mem(sc, URL_CMD_WRITEMEM, reg, &val, 4) ? -1 : 0); +} + +Static int +url_init(struct ifnet *ifp) +{ + struct url_softc *sc = ifp->if_softc; + struct mii_data *mii = GET_MII(sc); + u_char *eaddr; + int i, s; + + DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __FUNCTION__)); + + if (sc->sc_dying) + return (EIO); + + s = splnet(); + + /* Cancel pending I/O and free all TX/RX buffers */ + url_stop(ifp, 1); + + eaddr = LLADDR(ifp->if_sadl); + for (i = 0; i < ETHER_ADDR_LEN; i++) + url_csr_write_1(sc, URL_IDR0 + i, eaddr[i]); + + /* Init transmission control register */ + URL_CLRBIT(sc, URL_TCR, + URL_TCR_TXRR1 | URL_TCR_TXRR0 | + URL_TCR_IFG1 | URL_TCR_IFG0 | + URL_TCR_NOCRC); + + /* Init receive control register */ + URL_SETBIT2(sc, URL_RCR, URL_RCR_TAIL | URL_RCR_AD); + if (ifp->if_flags & IFF_BROADCAST) + URL_SETBIT2(sc, URL_RCR, URL_RCR_AB); + else + URL_CLRBIT2(sc, URL_RCR, URL_RCR_AB); + + /* If we want promiscuous mode, accept all physical frames. */ + if (ifp->if_flags & IFF_PROMISC) + URL_SETBIT2(sc, URL_RCR, URL_RCR_AAM|URL_RCR_AAP); + else + URL_CLRBIT2(sc, URL_RCR, URL_RCR_AAM|URL_RCR_AAP); + + + /* Initialize transmit ring */ + if (url_tx_list_init(sc) == ENOBUFS) { + printf("%s: tx list init failed\n", USBDEVNAME(sc->sc_dev)); + splx(s); + return (EIO); + } + + /* Initialize receive ring */ + if (url_rx_list_init(sc) == ENOBUFS) { + printf("%s: rx list init failed\n", USBDEVNAME(sc->sc_dev)); + splx(s); + return (EIO); + } + + /* Load the multicast filter */ + url_setmulti(sc); + + /* Enable RX and TX */ + URL_SETBIT(sc, URL_CR, URL_CR_TE | URL_CR_RE); + + mii_mediachg(mii); + + if (sc->sc_pipe_tx == NULL || sc->sc_pipe_rx == NULL) { + if (url_openpipes(sc)) { + splx(s); + return (EIO); + } + } + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + splx(s); + + usb_callout(sc->sc_stat_ch, hz, url_tick, sc); + + return (0); +} + +Static void +url_reset(struct url_softc *sc) +{ + int i; + + DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __FUNCTION__)); + + if (sc->sc_dying) + return; + + URL_SETBIT(sc, URL_CR, URL_CR_SOFT_RST); + + for (i = 0; i < URL_TX_TIMEOUT; i++) { + if (!(url_csr_read_1(sc, URL_CR) & URL_CR_SOFT_RST)) + break; + delay(10); /* XXX */ + } + + delay(10000); /* XXX */ +} + +int +url_activate(device_ptr_t self, enum devact act) +{ + struct url_softc *sc = (struct url_softc *)self; + + DPRINTF(("%s: %s: enter, act=%d\n", USBDEVNAME(sc->sc_dev), + __FUNCTION__, act)); + + switch (act) { + case DVACT_ACTIVATE: + return (EOPNOTSUPP); + break; + + case DVACT_DEACTIVATE: + if_deactivate(&sc->sc_ec.ec_if); + sc->sc_dying = 1; + break; + } + + return (0); +} + +#define url_calchash(addr) (ether_crc32_be((addr), ETHER_ADDR_LEN) >> 26) + + +Static void +url_setmulti(struct url_softc *sc) +{ + struct ifnet *ifp; + struct ether_multi *enm; + struct ether_multistep step; + u_int32_t hashes[2] = { 0, 0 }; + int h = 0; + int mcnt = 0; + + DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __FUNCTION__)); + + if (sc->sc_dying) + return; + + ifp = GET_IFP(sc); + + if (ifp->if_flags & IFF_PROMISC) { + URL_SETBIT2(sc, URL_RCR, URL_RCR_AAM|URL_RCR_AAP); + return; + } else if (ifp->if_flags & IFF_ALLMULTI) { + allmulti: + ifp->if_flags |= IFF_ALLMULTI; + URL_SETBIT2(sc, URL_RCR, URL_RCR_AAM); + URL_CLRBIT2(sc, URL_RCR, URL_RCR_AAP); + return; + } + + /* first, zot all the existing hash bits */ + url_csr_write_4(sc, URL_MAR0, 0); + url_csr_write_4(sc, URL_MAR4, 0); + + /* now program new ones */ + ETHER_FIRST_MULTI(step, &sc->sc_ec, enm); + while (enm != NULL) { + if (memcmp(enm->enm_addrlo, enm->enm_addrhi, + ETHER_ADDR_LEN) != 0) + goto allmulti; + + h = url_calchash(enm->enm_addrlo); + if (h < 32) + hashes[0] |= (1 << h); + else + hashes[1] |= (1 << (h -32)); + mcnt++; + ETHER_NEXT_MULTI(step, enm); + } + + ifp->if_flags &= ~IFF_ALLMULTI; + + URL_CLRBIT2(sc, URL_RCR, URL_RCR_AAM|URL_RCR_AAP); + + if (mcnt){ + URL_SETBIT2(sc, URL_RCR, URL_RCR_AM); + } else { + URL_CLRBIT2(sc, URL_RCR, URL_RCR_AM); + } + url_csr_write_4(sc, URL_MAR0, hashes[0]); + url_csr_write_4(sc, URL_MAR4, hashes[1]); +} + +Static int +url_openpipes(struct url_softc *sc) +{ + struct url_chain *c; + usbd_status err; + int i; + int error = 0; + + if (sc->sc_dying) + return (EIO); + + sc->sc_refcnt++; + + /* Open RX pipe */ + err = usbd_open_pipe(sc->sc_ctl_iface, sc->sc_bulkin_no, + USBD_EXCLUSIVE_USE, &sc->sc_pipe_rx); + if (err) { + printf("%s: open rx pipe failed: %s\n", + USBDEVNAME(sc->sc_dev), usbd_errstr(err)); + error = EIO; + goto done; + } + + /* Open TX pipe */ + err = usbd_open_pipe(sc->sc_ctl_iface, sc->sc_bulkout_no, + USBD_EXCLUSIVE_USE, &sc->sc_pipe_tx); + if (err) { + printf("%s: open tx pipe failed: %s\n", + USBDEVNAME(sc->sc_dev), usbd_errstr(err)); + error = EIO; + goto done; + } + +#if 0 + /* XXX: interrupt endpoint is not yet supported */ + /* Open Interrupt pipe */ + err = usbd_open_pipe_intr(sc->sc_ctl_iface, sc->sc_intrin_no, + USBD_EXCLUSIVE_USE, &sc->sc_pipe_intr, sc, + &sc->sc_cdata.url_ibuf, URL_INTR_PKGLEN, + url_intr, URL_INTR_INTERVAL); + if (err) { + printf("%s: open intr pipe failed: %s\n", + USBDEVNAME(sc->sc_dev), usbd_errstr(err)); + error = EIO; + goto done; + } +#endif + + + /* Start up the receive pipe. */ + for (i = 0; i < URL_RX_LIST_CNT; i++) { + c = &sc->sc_cdata.url_rx_chain[i]; + usbd_setup_xfer(c->url_xfer, sc->sc_pipe_rx, + c, c->url_buf, URL_BUFSZ, + USBD_SHORT_XFER_OK | USBD_NO_COPY, + USBD_NO_TIMEOUT, url_rxeof); + (void)usbd_transfer(c->url_xfer); + DPRINTF(("%s: %s: start read\n", USBDEVNAME(sc->sc_dev), + __FUNCTION__)); + } + + done: + if (--sc->sc_refcnt < 0) + usb_detach_wakeup(USBDEV(sc->sc_dev)); + + return (error); +} + +Static int +url_newbuf(struct url_softc *sc, struct url_chain *c, struct mbuf *m) +{ + struct mbuf *m_new = NULL; + + DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __FUNCTION__)); + + if (m == NULL) { + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) { + printf("%s: no memory for rx list " + "-- packet dropped!\n", USBDEVNAME(sc->sc_dev)); + return (ENOBUFS); + } + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + printf("%s: no memory for rx list " + "-- packet dropped!\n", USBDEVNAME(sc->sc_dev)); + m_freem(m_new); + return (ENOBUFS); + } + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + } else { + m_new = m; + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + m_new->m_data = m_new->m_ext.ext_buf; + } + + m_adj(m_new, ETHER_ALIGN); + c->url_mbuf = m_new; + + return (0); +} + + +Static int +url_rx_list_init(struct url_softc *sc) +{ + struct url_cdata *cd; + struct url_chain *c; + int i; + + DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __FUNCTION__)); + + cd = &sc->sc_cdata; + for (i = 0; i < URL_RX_LIST_CNT; i++) { + c = &cd->url_rx_chain[i]; + c->url_sc = sc; + c->url_idx = i; + if (url_newbuf(sc, c, NULL) == ENOBUFS) + return (ENOBUFS); + if (c->url_xfer == NULL) { + c->url_xfer = usbd_alloc_xfer(sc->sc_udev); + if (c->url_xfer == NULL) + return (ENOBUFS); + c->url_buf = usbd_alloc_buffer(c->url_xfer, URL_BUFSZ); + if (c->url_buf == NULL) { + usbd_free_xfer(c->url_xfer); + return (ENOBUFS); + } + } + } + + return (0); +} + +Static int +url_tx_list_init(struct url_softc *sc) +{ + struct url_cdata *cd; + struct url_chain *c; + int i; + + DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __FUNCTION__)); + + cd = &sc->sc_cdata; + for (i = 0; i < URL_TX_LIST_CNT; i++) { + c = &cd->url_tx_chain[i]; + c->url_sc = sc; + c->url_idx = i; + c->url_mbuf = NULL; + if (c->url_xfer == NULL) { + c->url_xfer = usbd_alloc_xfer(sc->sc_udev); + if (c->url_xfer == NULL) + return (ENOBUFS); + c->url_buf = usbd_alloc_buffer(c->url_xfer, URL_BUFSZ); + if (c->url_buf == NULL) { + usbd_free_xfer(c->url_xfer); + return (ENOBUFS); + } + } + } + + return (0); +} + +Static void +url_start(struct ifnet *ifp) +{ + struct url_softc *sc = ifp->if_softc; + struct mbuf *m_head = NULL; + + DPRINTF(("%s: %s: enter, link=%d\n", USBDEVNAME(sc->sc_dev), + __FUNCTION__, sc->sc_link)); + + if (sc->sc_dying) + return; + + if (!sc->sc_link) + return; + + if (ifp->if_flags & IFF_OACTIVE) + return; + + IFQ_POLL(&ifp->if_snd, m_head); + if (m_head == NULL) + return; + + if (url_send(sc, m_head, 0)) { + ifp->if_flags |= IFF_OACTIVE; + return; + } + + IFQ_DEQUEUE(&ifp->if_snd, m_head); + +#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m_head); +#endif + + ifp->if_flags |= IFF_OACTIVE; + + /* Set a timeout in case the chip goes out to lunch. */ + ifp->if_timer = 5; +} + +Static int +url_send(struct url_softc *sc, struct mbuf *m, int idx) +{ + int total_len; + struct url_chain *c; + usbd_status err; + + DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev),__FUNCTION__)); + + c = &sc->sc_cdata.url_tx_chain[idx]; + + /* Copy the mbuf data into a contiguous buffer */ + m_copydata(m, 0, m->m_pkthdr.len, c->url_buf); + c->url_mbuf = m; + total_len = m->m_pkthdr.len; + + if (total_len < URL_MIN_FRAME_LEN) + total_len = URL_MIN_FRAME_LEN; + usbd_setup_xfer(c->url_xfer, sc->sc_pipe_tx, c, c->url_buf, total_len, + USBD_FORCE_SHORT_XFER | USBD_NO_COPY, + URL_TX_TIMEOUT, url_txeof); + + /* Transmit */ + sc->sc_refcnt++; + err = usbd_transfer(c->url_xfer); + if (--sc->sc_refcnt < 0) + usb_detach_wakeup(USBDEV(sc->sc_dev)); + if (err != USBD_IN_PROGRESS) { + printf("%s: url_send error=%s\n", USBDEVNAME(sc->sc_dev), + usbd_errstr(err)); + /* Stop the interface */ + usb_add_task(sc->sc_udev, &sc->sc_stop_task); + return (EIO); + } + + DPRINTF(("%s: %s: send %d bytes\n", USBDEVNAME(sc->sc_dev), + __FUNCTION__, total_len)); + + sc->sc_cdata.url_tx_cnt++; + + return (0); +} + +Static void +url_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) +{ + struct url_chain *c = priv; + struct url_softc *sc = c->url_sc; + struct ifnet *ifp = GET_IFP(sc); + int s; + + if (sc->sc_dying) + return; + + s = splnet(); + + DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __FUNCTION__)); + + ifp->if_timer = 0; + ifp->if_flags &= ~IFF_OACTIVE; + + if (status != USBD_NORMAL_COMPLETION) { + if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { + splx(s); + return; + } + ifp->if_oerrors++; + printf("%s: usb error on tx: %s\n", USBDEVNAME(sc->sc_dev), + usbd_errstr(status)); + if (status == USBD_STALLED) { + sc->sc_refcnt++; + usbd_clear_endpoint_stall(sc->sc_pipe_tx); + if (--sc->sc_refcnt < 0) + usb_detach_wakeup(USBDEV(sc->sc_dev)); + } + splx(s); + return; + } + + ifp->if_opackets++; + + m_free(c->url_mbuf); + c->url_mbuf = NULL; + + if (IFQ_IS_EMPTY(&ifp->if_snd) == 0) + url_start(ifp); + + splx(s); +} + +Static void +url_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) +{ + struct url_chain *c = priv; + struct url_softc *sc = c->url_sc; + struct ifnet *ifp = GET_IFP(sc); + struct mbuf *m; + u_int32_t total_len; + url_rxhdr_t rxhdr; + int s; + + DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev),__FUNCTION__)); + + if (sc->sc_dying) + return; + + if (status != USBD_NORMAL_COMPLETION) { + if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) + return; + sc->sc_rx_errs++; + if (usbd_ratecheck(&sc->sc_rx_notice)) { + printf("%s: %u usb errors on rx: %s\n", + USBDEVNAME(sc->sc_dev), sc->sc_rx_errs, + usbd_errstr(status)); + sc->sc_rx_errs = 0; + } + if (status == USBD_STALLED) { + sc->sc_refcnt++; + usbd_clear_endpoint_stall(sc->sc_pipe_rx); + if (--sc->sc_refcnt < 0) + usb_detach_wakeup(USBDEV(sc->sc_dev)); + } + goto done; + } + + usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL); + + memcpy(mtod(c->url_mbuf, char *), c->url_buf, total_len); + + if (total_len <= ETHER_CRC_LEN) { + ifp->if_ierrors++; + goto done; + } + + memcpy(&rxhdr, c->url_buf + total_len - ETHER_CRC_LEN, sizeof(rxhdr)); + + DPRINTF(("%s: RX Status: %dbytes%s%s%s%s packets\n", + USBDEVNAME(sc->sc_dev), + UGETW(rxhdr) & URL_RXHDR_BYTEC_MASK, + UGETW(rxhdr) & URL_RXHDR_VALID_MASK ? ", Valid" : "", + UGETW(rxhdr) & URL_RXHDR_RUNTPKT_MASK ? ", Runt" : "", + UGETW(rxhdr) & URL_RXHDR_PHYPKT_MASK ? ", Physical match" : "", + UGETW(rxhdr) & URL_RXHDR_MCASTPKT_MASK ? ", Multicast" : "")); + + if ((UGETW(rxhdr) & URL_RXHDR_VALID_MASK) == 0) { + ifp->if_ierrors++; + goto done; + } + + ifp->if_ipackets++; + total_len -= ETHER_CRC_LEN; + + m = c->url_mbuf; + m->m_pkthdr.len = m->m_len = total_len; + m->m_pkthdr.rcvif = ifp; + + s = splnet(); + + if (url_newbuf(sc, c, NULL) == ENOBUFS) { + ifp->if_ierrors++; + goto done1; + } + +#if NBPFILTER > 0 + if (ifp->if_bpf) + BPF_MTAP(ifp, m); +#endif + + DPRINTF(("%s: %s: deliver %d\n", USBDEVNAME(sc->sc_dev), + __FUNCTION__, m->m_len)); + IF_INPUT(ifp, m); + + done1: + splx(s); + + done: + /* Setup new transfer */ + usbd_setup_xfer(xfer, sc->sc_pipe_rx, c, c->url_buf, URL_BUFSZ, + USBD_SHORT_XFER_OK | USBD_NO_COPY, + USBD_NO_TIMEOUT, url_rxeof); + sc->sc_refcnt++; + usbd_transfer(xfer); + if (--sc->sc_refcnt < 0) + usb_detach_wakeup(USBDEV(sc->sc_dev)); + + DPRINTF(("%s: %s: start rx\n", USBDEVNAME(sc->sc_dev), __FUNCTION__)); +} + +#if 0 +Static void url_intr() +{ +} +#endif + +Static int +url_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct url_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + struct mii_data *mii; + int s, error = 0; + + DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __FUNCTION__)); + + if (sc->sc_dying) + return (EIO); + + s = splnet(); + + switch (cmd) { + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + mii = GET_MII(sc); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); + break; + + default: + error = ether_ioctl(ifp, cmd, data); + if (error == ENETRESET) { + url_setmulti(sc); + error = 0; + } + break; + } + + splx(s); + + return (error); +} + +Static void +url_watchdog(struct ifnet *ifp) +{ + struct url_softc *sc = ifp->if_softc; + struct url_chain *c; + usbd_status stat; + int s; + + DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __FUNCTION__)); + + ifp->if_oerrors++; + printf("%s: watchdog timeout\n", USBDEVNAME(sc->sc_dev)); + + s = splusb(); + c = &sc->sc_cdata.url_tx_chain[0]; + usbd_get_xfer_status(c->url_xfer, NULL, NULL, NULL, &stat); + url_txeof(c->url_xfer, c, stat); + + if (IFQ_IS_EMPTY(&ifp->if_snd) == 0) + url_start(ifp); + splx(s); +} + +Static void +url_stop_task(struct url_softc *sc) +{ + url_stop(GET_IFP(sc), 1); +} + +/* Stop the adapter and free any mbufs allocated to the RX and TX lists. */ +Static void +url_stop(struct ifnet *ifp, int disable) +{ + struct url_softc *sc = ifp->if_softc; + usbd_status err; + int i; + + DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __FUNCTION__)); + + ifp->if_timer = 0; + + url_reset(sc); + + usb_uncallout(sc->sc_stat_ch, url_tick, sc); + + /* Stop transfers */ + /* RX endpoint */ + if (sc->sc_pipe_rx != NULL) { + err = usbd_abort_pipe(sc->sc_pipe_rx); + if (err) + printf("%s: abort rx pipe failed: %s\n", + USBDEVNAME(sc->sc_dev), usbd_errstr(err)); + err = usbd_close_pipe(sc->sc_pipe_rx); + if (err) + printf("%s: close rx pipe failed: %s\n", + USBDEVNAME(sc->sc_dev), usbd_errstr(err)); + sc->sc_pipe_rx = NULL; + } + + /* TX endpoint */ + if (sc->sc_pipe_tx != NULL) { + err = usbd_abort_pipe(sc->sc_pipe_tx); + if (err) + printf("%s: abort tx pipe failed: %s\n", + USBDEVNAME(sc->sc_dev), usbd_errstr(err)); + err = usbd_close_pipe(sc->sc_pipe_tx); + if (err) + printf("%s: close tx pipe failed: %s\n", + USBDEVNAME(sc->sc_dev), usbd_errstr(err)); + sc->sc_pipe_tx = NULL; + } + +#if 0 + /* XXX: Interrupt endpoint is not yet supported!! */ + /* Interrupt endpoint */ + if (sc->sc_pipe_intr != NULL) { + err = usbd_abort_pipe(sc->sc_pipe_intr); + if (err) + printf("%s: abort intr pipe failed: %s\n", + USBDEVNAME(sc->sc_dev), usbd_errstr(err)); + err = usbd_close_pipe(sc->sc_pipe_intr); + if (err) + printf("%s: close intr pipe failed: %s\n", + USBDEVNAME(sc->sc_dev), usbd_errstr(err)); + sc->sc_pipe_intr = NULL; + } +#endif + + /* Free RX resources. */ + for (i = 0; i < URL_RX_LIST_CNT; i++) { + if (sc->sc_cdata.url_rx_chain[i].url_mbuf != NULL) { + m_freem(sc->sc_cdata.url_rx_chain[i].url_mbuf); + sc->sc_cdata.url_rx_chain[i].url_mbuf = NULL; + } + if (sc->sc_cdata.url_rx_chain[i].url_xfer != NULL) { + usbd_free_xfer(sc->sc_cdata.url_rx_chain[i].url_xfer); + sc->sc_cdata.url_rx_chain[i].url_xfer = NULL; + } + } + + /* Free TX resources. */ + for (i = 0; i < URL_TX_LIST_CNT; i++) { + if (sc->sc_cdata.url_tx_chain[i].url_mbuf != NULL) { + m_freem(sc->sc_cdata.url_tx_chain[i].url_mbuf); + sc->sc_cdata.url_tx_chain[i].url_mbuf = NULL; + } + if (sc->sc_cdata.url_tx_chain[i].url_xfer != NULL) { + usbd_free_xfer(sc->sc_cdata.url_tx_chain[i].url_xfer); + sc->sc_cdata.url_tx_chain[i].url_xfer = NULL; + } + } + + sc->sc_link = 0; + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); +} + +/* Set media options */ +Static int +url_ifmedia_change(struct ifnet *ifp) +{ + struct url_softc *sc = ifp->if_softc; + struct mii_data *mii = GET_MII(sc); + + DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __FUNCTION__)); + + if (sc->sc_dying) + return (0); + + sc->sc_link = 0; + if (mii->mii_instance) { + struct mii_softc *miisc; + for (miisc = LIST_FIRST(&mii->mii_phys); miisc != NULL; + miisc = LIST_NEXT(miisc, mii_list)) + mii_phy_reset(miisc); + } + + return (mii_mediachg(mii)); +} + +/* Report current media status. */ +Static void +url_ifmedia_status(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct url_softc *sc = ifp->if_softc; + struct mii_data *mii = GET_MII(sc); + + DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __FUNCTION__)); + + if (sc->sc_dying) + return; + + if ((ifp->if_flags & IFF_RUNNING) == 0) { + ifmr->ifm_active = IFM_ETHER | IFM_NONE; + ifmr->ifm_status = 0; + return; + } + + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; +} + +Static void +url_tick(void *xsc) +{ + struct url_softc *sc = xsc; + + if (sc == NULL) + return; + + DPRINTFN(0xff, ("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), + __FUNCTION__)); + + if (sc->sc_dying) + return; + + /* Perform periodic stuff in process context */ + usb_add_task(sc->sc_udev, &sc->sc_tick_task); +} + +Static void +url_tick_task(void *xsc) +{ + struct url_softc *sc = xsc; + struct ifnet *ifp; + struct mii_data *mii; + int s; + + if (sc == NULL) + return; + + DPRINTFN(0xff, ("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), + __FUNCTION__)); + + if (sc->sc_dying) + return; + + ifp = GET_IFP(sc); + mii = GET_MII(sc); + + if (mii == NULL) + return; + + s = splnet(); + + mii_tick(mii); + if (!sc->sc_link) { + mii_pollstat(mii); + if (mii->mii_media_status & IFM_ACTIVE && + IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { + DPRINTF(("%s: %s: got link\n", + USBDEVNAME(sc->sc_dev), __FUNCTION__)); + sc->sc_link++; + if (IFQ_IS_EMPTY(&ifp->if_snd) == 0) + url_start(ifp); + } + } + + usb_callout(sc->sc_stat_ch, hz, url_tick, sc); + + splx(s); +} + +/* Get exclusive access to the MII registers */ +Static void +url_lock_mii(struct url_softc *sc) +{ + DPRINTFN(0xff, ("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), + __FUNCTION__)); + + sc->sc_refcnt++; + lockmgr(&sc->sc_mii_lock, LK_EXCLUSIVE, NULL); +} + +Static void +url_unlock_mii(struct url_softc *sc) +{ + DPRINTFN(0xff, ("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), + __FUNCTION__)); + + lockmgr(&sc->sc_mii_lock, LK_RELEASE, NULL); + if (--sc->sc_refcnt < 0) + usb_detach_wakeup(USBDEV(sc->sc_dev)); +} + +Static int +url_int_miibus_readreg(device_ptr_t dev, int phy, int reg) +{ + struct url_softc *sc; + u_int16_t val; + + if (dev == NULL) + return (0); + + sc = USBGETSOFTC(dev); + + DPRINTFN(0xff, ("%s: %s: enter, phy=%d reg=0x%04x\n", + USBDEVNAME(sc->sc_dev), __FUNCTION__, phy, reg)); + + if (sc->sc_dying) { +#ifdef DIAGNOSTIC + printf("%s: %s: dying\n", USBDEVNAME(sc->sc_dev), + __FUNCTION__); +#endif + return (0); + } + + /* XXX: one PHY only for the RTL8150 internal PHY */ + if (phy != 0) { + DPRINTFN(0xff, ("%s: %s: phy=%d is not supported\n", + USBDEVNAME(sc->sc_dev), __FUNCTION__, phy)); + return (0); + } + + url_lock_mii(sc); + + switch (reg) { + case MII_BMCR: /* Control Register */ + reg = URL_BMCR; + break; + case MII_BMSR: /* Status Register */ + reg = URL_BMSR; + break; + case MII_PHYIDR1: + case MII_PHYIDR2: + val = 0; + goto R_DONE; + break; + case MII_ANAR: /* Autonegotiation advertisement */ + reg = URL_ANAR; + break; + case MII_ANLPAR: /* Autonegotiation link partner abilities */ + reg = URL_ANLP; + break; + case URLPHY_MSR: /* Media Status Register */ + reg = URL_MSR; + break; + default: + printf("%s: %s: bad register %04x\n", + USBDEVNAME(sc->sc_dev), __FUNCTION__, reg); + val = 0; + goto R_DONE; + break; + } + + if (reg == URL_MSR) + val = url_csr_read_1(sc, reg); + else + val = url_csr_read_2(sc, reg); + + R_DONE: + DPRINTFN(0xff, ("%s: %s: phy=%d reg=0x%04x => 0x%04x\n", + USBDEVNAME(sc->sc_dev), __FUNCTION__, phy, reg, val)); + + url_unlock_mii(sc); + return (val); +} + +Static void +url_int_miibus_writereg(device_ptr_t dev, int phy, int reg, int data) +{ + struct url_softc *sc; + + if (dev == NULL) + return; + + sc = USBGETSOFTC(dev); + + DPRINTFN(0xff, ("%s: %s: enter, phy=%d reg=0x%04x data=0x%04x\n", + USBDEVNAME(sc->sc_dev), __FUNCTION__, phy, reg, data)); + + if (sc->sc_dying) { +#ifdef DIAGNOSTIC + printf("%s: %s: dying\n", USBDEVNAME(sc->sc_dev), + __FUNCTION__); +#endif + return; + } + + /* XXX: one PHY only for the RTL8150 internal PHY */ + if (phy != 0) { + DPRINTFN(0xff, ("%s: %s: phy=%d is not supported\n", + USBDEVNAME(sc->sc_dev), __FUNCTION__, phy)); + return; + } + + url_lock_mii(sc); + + switch (reg) { + case MII_BMCR: /* Control Register */ + reg = URL_BMCR; + break; + case MII_BMSR: /* Status Register */ + reg = URL_BMSR; + break; + case MII_PHYIDR1: + case MII_PHYIDR2: + goto W_DONE; + break; + case MII_ANAR: /* Autonegotiation advertisement */ + reg = URL_ANAR; + break; + case MII_ANLPAR: /* Autonegotiation link partner abilities */ + reg = URL_ANLP; + break; + case URLPHY_MSR: /* Media Status Register */ + reg = URL_MSR; + break; + default: + printf("%s: %s: bad register %04x\n", + USBDEVNAME(sc->sc_dev), __FUNCTION__, reg); + goto W_DONE; + break; + } + + if (reg == URL_MSR) + url_csr_write_1(sc, reg, data); + else + url_csr_write_2(sc, reg, data); + W_DONE: + + url_unlock_mii(sc); + return; +} + +Static void +url_miibus_statchg(device_ptr_t dev) +{ +#ifdef URL_DEBUG + struct url_softc *sc; + + if (dev == NULL) + return; + + sc = USBGETSOFTC(dev); + DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __FUNCTION__)); +#endif + /* Nothing to do */ +} + +#if 0 +/* + * external PHYs support, but not test. + */ +Static int +url_ext_miibus_redreg(device_ptr_t dev, int phy, int reg) +{ + struct url_softc *sc = USBGETSOFTC(dev); + u_int16_t val; + + DPRINTF(("%s: %s: enter, phy=%d reg=0x%04x\n", + USBDEVNAME(sc->sc_dev), __FUNCTION__, phy, reg)); + + if (sc->sc_dying) { +#ifdef DIAGNOSTIC + printf("%s: %s: dying\n", USBDEVNAME(sc->sc_dev), + __FUNCTION__); +#endif + return (0); + } + + url_lock_mii(sc); + + url_csr_write_1(sc, URL_PHYADD, phy & URL_PHYADD_MASK); + /* + * RTL8150L will initiate a MII management data transaction + * if PHYCNT_OWN bit is set 1 by software. After transaction, + * this bit is auto cleared by TRL8150L. + */ + url_csr_write_1(sc, URL_PHYCNT, + (reg | URL_PHYCNT_PHYOWN) & ~URL_PHYCNT_RWCR); + for (i = 0; i < URL_TIMEOUT; i++) { + if ((url_csr_read_1(sc, URL_PHYCNT) & URL_PHYCNT_PHYOWN) == 0) + break; + } + if (i == URL_TIMEOUT) { + printf("%s: MII read timed out\n", USBDEVNAME(sc->sc_dev)); + } + + val = url_csr_read_2(sc, URL_PHYDAT); + + DPRINTF(("%s: %s: phy=%d reg=0x%04x => 0x%04x\n", + USBDEVNAME(sc->sc_dev), __FUNCTION__, phy, reg, val)); + + url_unlock_mii(sc); + return (val); +} + +Static void +url_ext_miibus_writereg(device_ptr_t dev, int phy, int reg, int data) +{ + struct url_softc *sc = USBGETSOFTC(dev); + + DPRINTF(("%s: %s: enter, phy=%d reg=0x%04x data=0x%04x\n", + USBDEVNAME(sc->sc_dev), __FUNCTION__, phy, reg, data)); + + if (sc->sc_dying) { +#ifdef DIAGNOSTIC + printf("%s: %s: dying\n", USBDEVNAME(sc->sc_dev), + __FUNCTION__); +#endif + return; + } + + url_lock_mii(sc); + + url_csr_write_2(sc, URL_PHYDAT, data); + url_csr_write_1(sc, URL_PHYADD, phy); + url_csr_write_1(sc, URL_PHYCNT, reg | URL_PHYCNT_RWCR); /* Write */ + + for (i=0; i < URL_TIMEOUT; i++) { + if (url_csr_read_1(sc, URL_PHYCNT) & URL_PHYCNT_PHYOWN) + break; + } + + if (i == URL_TIMEOUT) { + printf("%s: MII write timed out\n", + USBDEVNAME(sc->sc_dev)); + } + + url_unlock_mii(sc); + return; +} +#endif + diff --git a/sys/dev/usb/if_urlreg.h b/sys/dev/usb/if_urlreg.h new file mode 100644 index 00000000000..f76bb380f00 --- /dev/null +++ b/sys/dev/usb/if_urlreg.h @@ -0,0 +1,195 @@ +/* $OpenBSD: if_urlreg.h,v 1.1 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: if_urlreg.h,v 1.1 2002/03/28 21:09:11 ichiro Exp $ */ +/* + * Copyright (c) 2001, 2002 + * Shingo WATANABE <nabe@nabechan.org>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Shingo WATANABE. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#define URL_IFACE_INDEX 0 +#define URL_CONFIG_NO 1 + +#define URL_TX_LIST_CNT 1 +#define URL_RX_LIST_CNT 1 + +#define URL_TX_TIMEOUT 1000 +#define URL_TIMEOUT 10000 + +#define ETHER_ALIGN 2 + + +/* Packet length */ +#define URL_MAX_MTU 1536 +#define URL_MIN_FRAME_LEN 60 +#define URL_BUFSZ URL_MAX_MTU + +/* Request */ +#define URL_REQ_MEM 0x05 + +#define URL_CMD_READMEM 1 +#define URL_CMD_WRITEMEM 2 + +/* Registers */ +#define URL_IDR0 0x0120 /* Ethernet Address, load from 93C46 */ +#define URL_IDR1 0x0121 /* Ethernet Address, load from 93C46 */ +#define URL_IDR2 0x0122 /* Ethernet Address, load from 93C46 */ +#define URL_IDR3 0x0123 /* Ethernet Address, load from 93C46 */ +#define URL_IDR4 0x0124 /* Ethernet Address, load from 93C46 */ +#define URL_IDR5 0x0125 /* Ethernet Address, load from 93C46 */ + +#define URL_MAR0 0x0126 /* Multicast register */ +#define URL_MAR1 0x0127 /* Multicast register */ +#define URL_MAR2 0x0128 /* Multicast register */ +#define URL_MAR3 0x0129 /* Multicast register */ +#define URL_MAR4 0x012a /* Multicast register */ +#define URL_MAR5 0x012b /* Multicast register */ +#define URL_MAR6 0x012c /* Multicast register */ +#define URL_MAR7 0x012d /* Multicast register */ +#define URL_MAR URL_MAR0 + +#define URL_CR 0x012e /* Command Register */ +#define URL_CR_WEPROM (1<<5) /* EEPROM Write Enable */ +#define URL_CR_SOFT_RST (1<<4) /* Software Reset */ +#define URL_CR_RE (1<<3) /* Ethernet Receive Enable */ +#define URL_CR_TE (1<<2) /* Ethernet Transmit Enable */ +#define URL_CR_EP3CLREN (1<<1) /* Enable clearing the performance counter */ +#define URL_CR_AUTOLOAD (1<<0) /* Auto-load the contents of 93C46 */ + +#define URL_TCR 0x012f /* Transmit Control Register */ +#define URL_TCR_TXRR1 (1<<7) /* TX Retry Count */ +#define URL_TCR_TXRR0 (1<<6) /* TX Retry Count */ +#define URL_TCR_IFG1 (1<<4) /* Interframe Gap Time */ +#define URL_TCR_IFG0 (1<<4) /* Interframe Gap Time */ +#define URL_TCR_NOCRC (1<<0) /* no CRC Append */ + +#define URL_RCR 0x0130 /* Receive Configuration Register */ +#define URL_RCR_TAIL (1<<7) +#define URL_RCR_AER (1<<6) +#define URL_RCR_AR (1<<5) +#define URL_RCR_AM (1<<4) +#define URL_RCR_AB (1<<3) +#define URL_RCR_AD (1<<2) +#define URL_RCR_AAM (1<<1) +#define URL_RCR_AAP (1<<0) + +#define URL_MSR 0x137 /* Media Status Register */ +#define URL_MSR_TXFCE (1<<7) +#define URL_MSR_RXFCE (1<<6) +#define URL_MSR_DUPLEX (1<<4) +#define URL_MSR_SPEED_100 (1<<3) +#define URL_MSR_LINK (1<<2) +#define URL_MSR_TXPF (1<<1) +#define URL_MSR_RXPF (1<<0) + +#define URL_PHYADD 0x138 /* MII PHY Address select */ +#define URL_PHYADD_MASK 0x1f /* MII PHY Address select */ + +#define URL_PHYDAT 0x139 /* MII PHY data */ + +#define URL_PHYCNT 0x13b /* MII PHY control */ +#define URL_PHYCNT_PHYOWN (1<<6) /* Own bit */ +#define URL_PHYCNT_RWCR (1<<5) /* MII management data R/W control */ +#define URL_PHY_PHYOFF_MASK 0x1f /* PHY register offset */ + +#define URL_BMCR 0x140 /* Basic mode control register */ +#define URL_BMSR 0x142 /* Basic mode status register */ +#define URL_ANAR 0x144 /* Auto-negotiation advertisement register */ +#define URL_ANLP 0x146 /* Auto-negotiation link partner ability register */ + + +typedef uWord url_rxhdr_t; /* Recive Header */ +#define URL_RXHDR_BYTEC_MASK (0x0fff) /* RX bytes count */ +#define URL_RXHDR_VALID_MASK (0x1000) /* Valid packet */ +#define URL_RXHDR_RUNTPKT_MASK (0x2000) /* Runt packet */ +#define URL_RXHDR_PHYPKT_MASK (0x4000) /* Physical match packet */ +#define URL_RXHDR_MCASTPKT_MASK (0x8000) /* Multicast packet */ + +#define GET_IFP(sc) (&(sc)->sc_ec.ec_if) +#define GET_MII(sc) (&(sc)->sc_mii) + +struct url_chain { + struct url_softc *url_sc; + usbd_xfer_handle url_xfer; + char *url_buf; + struct mbuf *url_mbuf; + int url_idx; +}; + +struct url_cdata { + struct url_chain url_tx_chain[URL_TX_LIST_CNT]; + struct url_chain url_rx_chain[URL_TX_LIST_CNT]; +#if 0 + /* XXX: Intrrupt Endpoint is not yet supported! */ + struct url_intrpkg url_ibuf; +#endif + int url_tx_prod; + int url_tx_cons; + int url_tx_cnt; + int url_rx_prod; +}; + +struct url_softc { + USBBASEDEVICE sc_dev; /* base device */ + usbd_device_handle sc_udev; + + /* USB */ + usbd_interface_handle sc_ctl_iface; + /* int sc_ctl_iface_no; */ + int sc_bulkin_no; /* bulk in endpoint */ + int sc_bulkout_no; /* bulk out endpoint */ + int sc_intrin_no; /* intr in endpoint */ + usbd_pipe_handle sc_pipe_rx; + usbd_pipe_handle sc_pipe_tx; + usbd_pipe_handle sc_pipe_intr; + usb_callout_t sc_stat_ch; + u_int sc_rx_errs; + /* u_int sc_intr_errs; */ + struct timeval sc_rx_notice; + + /* Ethernet */ + struct ethercom sc_ec; /* ethernet common */ + struct mii_data sc_mii; + struct lock sc_mii_lock; + int sc_link; +#define sc_media url_mii.mii_media +#if NRND > 0 + rndsource_element_t rnd_source; +#endif + struct url_cdata sc_cdata; + + int sc_attached; + int sc_dying; + int sc_refcnt; + + struct usb_task sc_tick_task; + struct usb_task sc_stop_task; + + u_int16_t sc_flags; +}; diff --git a/sys/dev/usb/ohci.c b/sys/dev/usb/ohci.c index c1e83997e64..4e1107f020f 100644 --- a/sys/dev/usb/ohci.c +++ b/sys/dev/usb/ohci.c @@ -1,5 +1,5 @@ -/* $OpenBSD: ohci.c,v 1.26 2002/05/02 20:08:04 nate Exp $ */ -/* $NetBSD: ohci.c,v 1.104 2001/09/28 23:57:21 augustss Exp $ */ +/* $OpenBSD: ohci.c,v 1.27 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: ohci.c,v 1.122 2002/03/17 18:02:52 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/ohci.c,v 1.22 1999/11/17 22:33:40 n_hibma Exp $ */ /* @@ -198,18 +198,18 @@ Static void ohci_device_isoc_abort(usbd_xfer_handle); Static void ohci_device_isoc_close(usbd_pipe_handle); Static void ohci_device_isoc_done(usbd_xfer_handle); -Static usbd_status ohci_device_setintr(ohci_softc_t *sc, +Static usbd_status ohci_device_setintr(ohci_softc_t *sc, struct ohci_pipe *pipe, int ival); -Static int ohci_str(usb_string_descriptor_t *, int, char *); +Static int ohci_str(usb_string_descriptor_t *, int, const char *); Static void ohci_timeout(void *); +Static void ohci_timeout_task(void *); Static void ohci_rhsc_able(ohci_softc_t *, int); -Static void ohci_rhsc_enable(void *sc); +Static void ohci_rhsc_enable(void *); Static void ohci_close_pipe(usbd_pipe_handle, ohci_soft_ed_t *); Static void ohci_abort_xfer(usbd_xfer_handle, usbd_status); -Static void ohci_abort_xfer_end(void *); Static void ohci_device_clear_toggle(usbd_pipe_handle pipe); Static void ohci_noop(usbd_pipe_handle pipe); @@ -236,7 +236,7 @@ Static void ohci_dump_itds(ohci_soft_itd_t *); #define OREAD4(sc, r) (OBARR(sc), bus_space_read_4((sc)->iot, (sc)->ioh, (r))) /* Reverse the bits in a value 0 .. 31 */ -Static u_int8_t revbits[OHCI_NO_INTRS] = +Static u_int8_t revbits[OHCI_NO_INTRS] = { 0x00, 0x10, 0x08, 0x18, 0x04, 0x14, 0x0c, 0x1c, 0x02, 0x12, 0x0a, 0x1a, 0x06, 0x16, 0x0e, 0x1e, 0x01, 0x11, 0x09, 0x19, 0x05, 0x15, 0x0d, 0x1d, @@ -286,7 +286,7 @@ Static struct usbd_bus_methods ohci_bus_methods = { ohci_freex, }; -Static struct usbd_pipe_methods ohci_root_ctrl_methods = { +Static struct usbd_pipe_methods ohci_root_ctrl_methods = { ohci_root_ctrl_transfer, ohci_root_ctrl_start, ohci_root_ctrl_abort, @@ -295,7 +295,7 @@ Static struct usbd_pipe_methods ohci_root_ctrl_methods = { ohci_root_ctrl_done, }; -Static struct usbd_pipe_methods ohci_root_intr_methods = { +Static struct usbd_pipe_methods ohci_root_intr_methods = { ohci_root_intr_transfer, ohci_root_intr_start, ohci_root_intr_abort, @@ -304,7 +304,7 @@ Static struct usbd_pipe_methods ohci_root_intr_methods = { ohci_root_intr_done, }; -Static struct usbd_pipe_methods ohci_device_ctrl_methods = { +Static struct usbd_pipe_methods ohci_device_ctrl_methods = { ohci_device_ctrl_transfer, ohci_device_ctrl_start, ohci_device_ctrl_abort, @@ -313,7 +313,7 @@ Static struct usbd_pipe_methods ohci_device_ctrl_methods = { ohci_device_ctrl_done, }; -Static struct usbd_pipe_methods ohci_device_intr_methods = { +Static struct usbd_pipe_methods ohci_device_intr_methods = { ohci_device_intr_transfer, ohci_device_intr_start, ohci_device_intr_abort, @@ -322,7 +322,7 @@ Static struct usbd_pipe_methods ohci_device_intr_methods = { ohci_device_intr_done, }; -Static struct usbd_pipe_methods ohci_device_bulk_methods = { +Static struct usbd_pipe_methods ohci_device_bulk_methods = { ohci_device_bulk_transfer, ohci_device_bulk_start, ohci_device_bulk_abort, @@ -368,15 +368,19 @@ ohci_detach(struct ohci_softc *sc, int flags) if (sc->sc_child != NULL) rv = config_detach(sc->sc_child, flags); - + if (rv != 0) return (rv); + usb_uncallout(sc->sc_tmo_rhsc, ohci_rhsc_enable, sc); + #if defined(__NetBSD__) || defined(__OpenBSD__) powerhook_disestablish(sc->sc_powerhook); shutdownhook_disestablish(sc->sc_shutdownhook); #endif + usb_delay_ms(&sc->sc_bus, 300); /* XXX let stray task complete */ + /* free data structures XXX */ return (rv); @@ -488,7 +492,7 @@ ohci_alloc_std_chain(struct ohci_pipe *opipe, ohci_softc_t *sc, dataphys = DMAADDR(dma); dataphysend = OHCI_PAGE(dataphys + len - 1); tdflags = htole32( - (rd ? OHCI_TD_IN : OHCI_TD_OUT) | + (rd ? OHCI_TD_IN : OHCI_TD_OUT) | (flags & USBD_SHORT_XFER_OK ? OHCI_TD_R : 0) | OHCI_TD_NOCC | OHCI_TD_TOGGLE_CARRY | OHCI_TD_NOINTR); @@ -504,7 +508,7 @@ ohci_alloc_std_chain(struct ohci_pipe *opipe, ohci_softc_t *sc, curlen = len; } else { /* must use multiple TDs, fill as much as possible. */ - curlen = 2 * OHCI_PAGE_SIZE - + curlen = 2 * OHCI_PAGE_SIZE - (dataphys & (OHCI_PAGE_SIZE-1)); /* the length must be a multiple of the max size */ curlen -= curlen % UGETW(opipe->pipe.endpoint->edesc->wMaxPacketSize); @@ -565,7 +569,7 @@ ohci_alloc_std_chain(struct ohci_pipe *opipe, ohci_softc_t *sc, #if 0 Static void -ohci_free_std_chain(ohci_softc_t *sc, ohci_soft_td_t *std, +ohci_free_std_chain(ohci_softc_t *sc, ohci_soft_td_t *std, ohci_soft_td_t *stdend) { ohci_soft_td_t *p; @@ -637,15 +641,6 @@ ohci_free_sitd(ohci_softc_t *sc, ohci_soft_itd_t *sitd) splx(s); } -void -ohci_reset(ohci_softc_t *sc) -{ - ohci_shutdown(sc); - /* disable all interrupts and then switch on all desired - interrupts */ - OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); -} - usbd_status ohci_init(ohci_softc_t *sc) { @@ -665,7 +660,7 @@ ohci_init(ohci_softc_t *sc) OHCI_REV_LEGACY(rev) ? ", legacy support" : ""); if (OHCI_REV_HI(rev) != 1 || OHCI_REV_LO(rev) != 0) { - printf("%s: unsupported OHCI revision\n", + printf("%s: unsupported OHCI revision\n", USBDEVNAME(sc->sc_bus.bdev)); sc->sc_bus.usbrev = USBREV_UNKNOWN; return (USBD_INVAL); @@ -681,7 +676,7 @@ ohci_init(ohci_softc_t *sc) /* XXX determine alignment by R/W */ /* Allocate the HCCA area. */ - err = usb_allocmem(&sc->sc_bus, OHCI_HCCA_SIZE, + err = usb_allocmem(&sc->sc_bus, OHCI_HCCA_SIZE, OHCI_HCCA_ALIGN, &sc->sc_hccadma); if (err) return (err); @@ -733,12 +728,12 @@ ohci_init(ohci_softc_t *sc) sed->next = psed; sed->ed.ed_nexted = htole32(psed->physaddr); } - /* + /* * Fill HCCA interrupt table. The bit reversal is to get * the tree set up properly to spread the interrupts. */ for (i = 0; i < OHCI_NO_INTRS; i++) - sc->sc_hcca->hcca_interrupt_table[revbits[i]] = + sc->sc_hcca->hcca_interrupt_table[revbits[i]] = htole32(sc->sc_eds[OHCI_NO_EDS-OHCI_NO_INTRS+i]->physaddr); #ifdef OHCI_DEBUG @@ -861,7 +856,7 @@ ohci_init(ohci_softc_t *sc) if (ohcidebug > 5) ohci_dumpregs(sc); #endif - + /* Set up the bus struct. */ sc->sc_bus.methods = &ohci_bus_methods; sc->sc_bus.pipe_size = sizeof(struct ohci_pipe); @@ -875,6 +870,8 @@ ohci_init(ohci_softc_t *sc) timeout_set(&sc->sc_tmo_rhsc, ohci_rhsc_enable, sc); #endif + usb_callout_init(sc->sc_tmo_rhsc); + return (USBD_NORMAL_COMPLETION); bad5: @@ -918,12 +915,23 @@ ohci_allocx(struct usbd_bus *bus) usbd_xfer_handle xfer; xfer = SIMPLEQ_FIRST(&sc->sc_free_xfers); - if (xfer != NULL) + 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); +#ifdef DIAGNOSTIC + if (xfer->busy_free != XFER_FREE) { + printf("ohci_allocx: xfer=%p not free, 0x%08x\n", xfer, + xfer->busy_free); + } +#endif + } else { + xfer = malloc(sizeof(struct ohci_xfer), M_USB, M_NOWAIT); + } + if (xfer != NULL) { + memset(xfer, 0, sizeof (struct ohci_xfer)); +#ifdef DIAGNOSTIC + xfer->busy_free = XFER_BUSY; +#endif + } return (xfer); } @@ -932,6 +940,14 @@ ohci_freex(struct usbd_bus *bus, usbd_xfer_handle xfer) { struct ohci_softc *sc = (struct ohci_softc *)bus; +#ifdef DIAGNOSTIC + if (xfer->busy_free != XFER_BUSY) { + printf("ohci_freex: xfer=%p not busy, 0x%08x\n", xfer, + xfer->busy_free); + return; + } + xfer->busy_free = XFER_FREE; +#endif SIMPLEQ_INSERT_HEAD(&sc->sc_free_xfers, xfer, next); } @@ -1065,15 +1081,22 @@ ohci_intr(void *p) { ohci_softc_t *sc = p; + if (sc == NULL || sc->sc_dying) + return (0); + /* If we get an interrupt while polling, then just ignore it. */ if (sc->sc_bus.use_polling) { #ifdef DIAGNOSTIC - printf("ohci_intr: ignored interrupt while polling\n"); + static int repeat = 0; + if (repeat < 10) { + printf("ohci_intr: ignored interrupt while polling\n"); + ++repeat; + } #endif return (0); } - return (ohci_intr1(sc)); + return (ohci_intr1(sc)); } Static int @@ -1082,6 +1105,8 @@ ohci_intr1(ohci_softc_t *sc) u_int32_t intrs, eintrs; ohci_physaddr_t done; + DPRINTFN(14,("ohci_intr1: enter\n")); + /* In case the interrupt occurs before initialization has completed. */ if (sc == NULL || sc->sc_hcca == NULL) { #ifdef DIAGNOSTIC @@ -1111,7 +1136,7 @@ ohci_intr1(ohci_softc_t *sc) sc->sc_bus.intr_context++; sc->sc_bus.no_intrs++; - DPRINTFN(7, ("ohci_intr: sc=%p intrs=0x%x(0x%x) eintrs=0x%x\n", + DPRINTFN(7, ("ohci_intr: sc=%p intrs=0x%x(0x%x) eintrs=0x%x\n", sc, (u_int)intrs, OREAD4(sc, OHCI_INTERRUPT_STATUS), (u_int)eintrs)); @@ -1123,13 +1148,13 @@ ohci_intr1(ohci_softc_t *sc) sc->sc_overrun_cnt = 0; } /* XXX do what */ - intrs &= ~OHCI_SO; + eintrs &= ~OHCI_SO; } if (eintrs & OHCI_WDH) { ohci_add_done(sc, done &~ OHCI_DONE_INTRS); sc->sc_hcca->hcca_done_head = 0; usb_schedsoftintr(&sc->sc_bus); - intrs &= ~OHCI_WDH; + eintrs &= ~OHCI_WDH; } if (eintrs & OHCI_RD) { printf("%s: resume detect\n", USBDEVNAME(sc->sc_bus.bdev)); @@ -1143,24 +1168,28 @@ ohci_intr1(ohci_softc_t *sc) } if (eintrs & OHCI_RHSC) { ohci_rhsc(sc, sc->sc_intrxfer); - intrs &= ~OHCI_RHSC; - - /* + /* * Disable RHSC interrupt for now, because it will be * on until the port has been reset. */ ohci_rhsc_able(sc, 0); -#if defined (__OpenBSD__) + DPRINTFN(2, ("%s: rhsc interrupt disabled\n", + USBDEVNAME(sc->sc_bus.bdev))); + /* Do not allow RHSC interrupts > 1 per second */ - timeout_add(&sc->sc_tmo_rhsc, hz); -#endif + usb_callout(sc->sc_tmo_rhsc, hz, ohci_rhsc_enable, sc); + eintrs &= ~OHCI_RHSC; } sc->sc_bus.intr_context--; - /* Block unprocessed interrupts. XXX */ - OWRITE4(sc, OHCI_INTERRUPT_DISABLE, intrs); - sc->sc_eintrs &= ~intrs; + if (eintrs != 0) { + /* Block unprocessed interrupts. XXX */ + OWRITE4(sc, OHCI_INTERRUPT_DISABLE, eintrs); + sc->sc_eintrs &= ~eintrs; + printf("%s: blocking intrs 0x%x\n", + USBDEVNAME(sc->sc_bus.bdev), eintrs); + } return (1); } @@ -1183,6 +1212,10 @@ ohci_rhsc_enable(void *v_sc) { ohci_softc_t *sc = v_sc; + ohci_rhsc(sc, sc->sc_intrxfer); + DPRINTFN(2, ("%s: rhsc interrupt enabled\n", + USBDEVNAME(sc->sc_bus.bdev))); + ohci_rhsc_able(sc, 1); } @@ -1253,6 +1286,8 @@ ohci_softintr(void *v) usbd_xfer_handle xfer; int len, cc, s; + DPRINTFN(10,("ohci_softintr: enter\n:")); + sc->sc_bus.intr_context++; s = splhardusb(); @@ -1262,7 +1297,7 @@ ohci_softintr(void *v) sc->sc_sidone = NULL; splx(s); - DPRINTFN(10,("ohci_process_done: sdone=%p sidone=%p\n", sdone, sidone)); + DPRINTFN(10,("ohci_softintr: sdone=%p sidone=%p\n", sdone, sidone)); #ifdef OHCI_DEBUG if (ohcidebug > 10) { @@ -1277,9 +1312,11 @@ ohci_softintr(void *v) DPRINTFN(10, ("ohci_process_done: std=%p xfer=%p hcpriv=%p\n", std, xfer, xfer ? xfer->hcpriv : 0)); if (xfer == NULL) { - /* xfer == NULL: There seems to be no xfer associated + /* + * xfer == NULL: There seems to be no xfer associated * with this TD. It is tailp that happened to end up on * the done queue. + * Shouldn't happen, but some chips are broken(?). */ continue; } @@ -1313,7 +1350,7 @@ ohci_softintr(void *v) * the endpoint. */ ohci_soft_td_t *p, *n; - struct ohci_pipe *opipe = + struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe; DPRINTFN(15,("ohci_process_done: error cc=%d (%s)\n", @@ -1340,7 +1377,7 @@ ohci_softintr(void *v) #ifdef OHCI_DEBUG if (ohcidebug > 10) { - DPRINTF(("ohci_process_done: ITD done:\n")); + DPRINTF(("ohci_softintr: ITD done:\n")); ohci_dump_itds(sidone); } #endif @@ -1367,7 +1404,7 @@ ohci_softintr(void *v) cc = OHCI_ITD_GET_CC(le32toh(sitd->itd.itd_flags)); if (cc == OHCI_CC_NO_ERROR) { /* XXX compute length for input */ - struct ohci_pipe *opipe = + struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe; if (sitd->flags & OHCI_CALL_DONE) { opipe->u.iso.inuse -= xfer->nframes; @@ -1383,7 +1420,13 @@ ohci_softintr(void *v) } } + if (sc->sc_softwake) { + sc->sc_softwake = 0; + wakeup(&sc->sc_softwake); + } + sc->sc_bus.intr_context--; + DPRINTFN(10,("ohci_softintr: done:\n")); } void @@ -1408,7 +1451,7 @@ ohci_device_intr_done(usbd_xfer_handle xfer) ohci_soft_td_t *data, *tail; - DPRINTFN(10,("ohci_intr_done: xfer=%p, actlen=%d\n", + DPRINTFN(10,("ohci_intr_done: xfer=%p, actlen=%d\n", xfer, xfer->actlen)); xfer->hcpriv = NULL; @@ -1421,9 +1464,9 @@ ohci_device_intr_done(usbd_xfer_handle xfer) return; } tail->xfer = NULL; - + data->td.td_flags = htole32( - OHCI_TD_IN | OHCI_TD_NOCC | + OHCI_TD_IN | OHCI_TD_NOCC | OHCI_TD_SET_DI(1) | OHCI_TD_TOGGLE_CARRY); if (xfer->flags & USBD_SHORT_XFER_OK) data->td.td_flags |= htole32(OHCI_TD_R); @@ -1446,7 +1489,7 @@ ohci_device_intr_done(usbd_xfer_handle xfer) void ohci_device_bulk_done(usbd_xfer_handle xfer) { - DPRINTFN(10,("ohci_bulk_done: xfer=%p, actlen=%d\n", + DPRINTFN(10,("ohci_bulk_done: xfer=%p, actlen=%d\n", xfer, xfer->actlen)); xfer->hcpriv = NULL; @@ -1462,7 +1505,7 @@ ohci_rhsc(ohci_softc_t *sc, usbd_xfer_handle xfer) int hstatus; hstatus = OREAD4(sc, OHCI_RH_STATUS); - DPRINTF(("ohci_rhsc: sc=%p xfer=%p hstatus=0x%08x\n", + DPRINTF(("ohci_rhsc: sc=%p xfer=%p hstatus=0x%08x\n", sc, xfer, hstatus)); if (xfer == NULL) { @@ -1515,6 +1558,8 @@ ohci_waitintr(ohci_softc_t *sc, usbd_xfer_handle xfer) xfer->status = USBD_IN_PROGRESS; for (usecs = timo * 1000000 / hz; usecs > 0; usecs -= 1000) { usb_delay_ms(&sc->sc_bus, 1); + if (sc->sc_dying) + break; intrs = OREAD4(sc, OHCI_INTERRUPT_STATUS) & sc->sc_eintrs; DPRINTFN(15,("ohci_waitintr: 0x%04x\n", intrs)); #ifdef OHCI_DEBUG @@ -1539,6 +1584,15 @@ void ohci_poll(struct usbd_bus *bus) { ohci_softc_t *sc = (ohci_softc_t *)bus; +#ifdef OHCI_DEBUG + static int last; + int new; + new = OREAD4(sc, OHCI_INTERRUPT_STATUS); + if (new != last) { + DPRINTFN(10,("ohci_poll: intrs=0x%04x\n", new)); + last = new; + } +#endif if (OREAD4(sc, OHCI_INTERRUPT_STATUS) & sc->sc_eintrs) ohci_intr1(sc); @@ -1565,7 +1619,7 @@ ohci_device_request(usbd_xfer_handle xfer) DPRINTFN(3,("ohci_device_control type=0x%02x, request=0x%02x, " "wValue=0x%04x, wIndex=0x%04x len=%d, addr=%d, endpt=%d\n", req->bmRequestType, req->bRequest, UGETW(req->wValue), - UGETW(req->wIndex), len, addr, + UGETW(req->wIndex), len, addr, opipe->pipe.endpoint->edesc->bEndpointAddress)); setup = opipe->tail.td; @@ -1651,11 +1705,15 @@ ohci_device_request(usbd_xfer_handle xfer) } splx(s); -#if 0 - if (ohcidebug > 10) { +#ifdef OHCI_DEBUG + if (ohcidebug > 20) { delay(10000); DPRINTF(("ohci_device_request: status=%x\n", OREAD4(sc, OHCI_COMMAND_STATUS))); + ohci_dumpregs(sc); + printf("ctrl head:\n"); + ohci_dump_ed(sc->sc_ctrl_head); + printf("sed:\n"); ohci_dump_ed(sed); ohci_dump_tds(setup); } @@ -1677,6 +1735,8 @@ ohci_device_request(usbd_xfer_handle xfer) void ohci_add_ed(ohci_soft_ed_t *sed, ohci_soft_ed_t *head) { + DPRINTFN(8,("ohci_add_ed: sed=%p head=%p\n", sed, head)); + SPLUSBCHECK; sed->next = head->next; sed->ed.ed_nexted = head->ed.ed_nexted; @@ -1690,7 +1750,7 @@ ohci_add_ed(ohci_soft_ed_t *sed, ohci_soft_ed_t *head) void ohci_rem_ed(ohci_soft_ed_t *sed, ohci_soft_ed_t *head) { - ohci_soft_ed_t *p; + ohci_soft_ed_t *p; SPLUSBCHECK; @@ -1740,7 +1800,7 @@ ohci_hash_find_td(ohci_softc_t *sc, ohci_physaddr_t a) int h = HASH(a); ohci_soft_td_t *std; - for (std = LIST_FIRST(&sc->sc_hash_tds[h]); + for (std = LIST_FIRST(&sc->sc_hash_tds[h]); std != NULL; std = LIST_NEXT(std, hnext)) if (std->physaddr == a) @@ -1756,7 +1816,7 @@ ohci_hash_add_itd(ohci_softc_t *sc, ohci_soft_itd_t *sitd) SPLUSBCHECK; - DPRINTFN(10,("ohci_hash_add_itd: sitd=%p physaddr=0x%08lx\n", + DPRINTFN(10,("ohci_hash_add_itd: sitd=%p physaddr=0x%08lx\n", sitd, (u_long)sitd->physaddr)); LIST_INSERT_HEAD(&sc->sc_hash_itds[h], sitd, hnext); @@ -1768,7 +1828,7 @@ ohci_hash_rem_itd(ohci_softc_t *sc, ohci_soft_itd_t *sitd) { SPLUSBCHECK; - DPRINTFN(10,("ohci_hash_rem_itd: sitd=%p physaddr=0x%08lx\n", + DPRINTFN(10,("ohci_hash_rem_itd: sitd=%p physaddr=0x%08lx\n", sitd, (u_long)sitd->physaddr)); LIST_REMOVE(sitd, hnext); @@ -1780,7 +1840,7 @@ ohci_hash_find_itd(ohci_softc_t *sc, ohci_physaddr_t a) int h = HASH(a); ohci_soft_itd_t *sitd; - for (sitd = LIST_FIRST(&sc->sc_hash_itds[h]); + for (sitd = LIST_FIRST(&sc->sc_hash_itds[h]); sitd != NULL; sitd = LIST_NEXT(sitd, hnext)) if (sitd->physaddr == a) @@ -1791,15 +1851,32 @@ ohci_hash_find_itd(ohci_softc_t *sc, ohci_physaddr_t a) void ohci_timeout(void *addr) { + struct ohci_xfer *oxfer = addr; + struct ohci_pipe *opipe = (struct ohci_pipe *)oxfer->xfer.pipe; + ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus; + + DPRINTF(("ohci_timeout: oxfer=%p\n", oxfer)); + + if (sc->sc_dying) { + ohci_abort_xfer(&oxfer->xfer, USBD_TIMEOUT); + return; + } + + /* Execute the abort in a process context. */ + usb_init_task(&oxfer->abort_task, ohci_timeout_task, addr); + usb_add_task(oxfer->xfer.pipe->device, &oxfer->abort_task); +} + +void +ohci_timeout_task(void *addr) +{ usbd_xfer_handle xfer = addr; int s; - DPRINTF(("ohci_timeout: xfer=%p\n", xfer)); + DPRINTF(("ohci_timeout_task: xfer=%p\n", xfer)); s = splusb(); - xfer->device->bus->intr_context++; ohci_abort_xfer(xfer, USBD_TIMEOUT); - xfer->device->bus->intr_context--; splx(s); } @@ -1816,19 +1893,19 @@ ohci_dump_td(ohci_soft_td_t *std) { char sbuf[128]; - bitmask_snprintf((int)le32toh(std->td.td_flags), + bitmask_snprintf((u_int32_t)le32toh(std->td.td_flags), "\20\23R\24OUT\25IN\31TOG1\32SETTOGGLE", sbuf, sizeof(sbuf)); - DPRINTF(("TD(%p) at %08lx: %s delay=%d ec=%d cc=%d\ncbp=0x%08lx " - "nexttd=0x%08lx be=0x%08lx\n", - std, (u_long)std->physaddr, sbuf, - OHCI_TD_GET_DI(le32toh(std->td.td_flags)), - OHCI_TD_GET_EC(le32toh(std->td.td_flags)), - OHCI_TD_GET_CC(le32toh(std->td.td_flags)), - (u_long)le32toh(std->td.td_cbp), - (u_long)le32toh(std->td.td_nexttd), - (u_long)le32toh(std->td.td_be))); + printf("TD(%p) at %08lx: %s delay=%d ec=%d cc=%d\ncbp=0x%08lx " + "nexttd=0x%08lx be=0x%08lx\n", + std, (u_long)std->physaddr, sbuf, + OHCI_TD_GET_DI(le32toh(std->td.td_flags)), + OHCI_TD_GET_EC(le32toh(std->td.td_flags)), + OHCI_TD_GET_CC(le32toh(std->td.td_flags)), + (u_long)le32toh(std->td.td_cbp), + (u_long)le32toh(std->td.td_nexttd), + (u_long)le32toh(std->td.td_be)); } void @@ -1836,20 +1913,20 @@ ohci_dump_itd(ohci_soft_itd_t *sitd) { int i; - DPRINTF(("ITD(%p) at %08lx: sf=%d di=%d fc=%d cc=%d\n" - "bp0=0x%08lx next=0x%08lx be=0x%08lx\n", - sitd, (u_long)sitd->physaddr, - OHCI_ITD_GET_SF(le32toh(sitd->itd.itd_flags)), - OHCI_ITD_GET_DI(le32toh(sitd->itd.itd_flags)), - OHCI_ITD_GET_FC(le32toh(sitd->itd.itd_flags)), - OHCI_ITD_GET_CC(le32toh(sitd->itd.itd_flags)), - (u_long)le32toh(sitd->itd.itd_bp0), - (u_long)le32toh(sitd->itd.itd_nextitd), - (u_long)le32toh(sitd->itd.itd_be))); + printf("ITD(%p) at %08lx: sf=%d di=%d fc=%d cc=%d\n" + "bp0=0x%08lx next=0x%08lx be=0x%08lx\n", + sitd, (u_long)sitd->physaddr, + OHCI_ITD_GET_SF(le32toh(sitd->itd.itd_flags)), + OHCI_ITD_GET_DI(le32toh(sitd->itd.itd_flags)), + OHCI_ITD_GET_FC(le32toh(sitd->itd.itd_flags)), + OHCI_ITD_GET_CC(le32toh(sitd->itd.itd_flags)), + (u_long)le32toh(sitd->itd.itd_bp0), + (u_long)le32toh(sitd->itd.itd_nextitd), + (u_long)le32toh(sitd->itd.itd_be)); for (i = 0; i < OHCI_ITD_NOFFSET; i++) - DPRINTF(("offs[%d]=0x%04x ", i, - (u_int)le16toh(sitd->itd.itd_offset[i]))); - DPRINTF(("\n")); + printf("offs[%d]=0x%04x ", i, + (u_int)le16toh(sitd->itd.itd_offset[i])); + printf("\n"); } void @@ -1864,21 +1941,21 @@ ohci_dump_ed(ohci_soft_ed_t *sed) { char sbuf[128], sbuf2[128]; - bitmask_snprintf((int)le32toh(sed->ed.ed_flags), + bitmask_snprintf((u_int32_t)le32toh(sed->ed.ed_flags), "\20\14OUT\15IN\16LOWSPEED\17SKIP\20ISO", sbuf, sizeof(sbuf)); - bitmask_snprintf((u_long)le32toh(sed->ed.ed_headp), + bitmask_snprintf((u_int32_t)le32toh(sed->ed.ed_headp), "\20\1HALT\2CARRY", sbuf2, sizeof(sbuf2)); - DPRINTF(("ED(%p) at 0x%08lx: addr=%d endpt=%d maxp=%d %s\ntailp=0x%08lx " - "headflags=%s headp=0x%08lx nexted=0x%08lx\n", - sed, (u_long)sed->physaddr, - OHCI_ED_GET_FA(le32toh(sed->ed.ed_flags)), - OHCI_ED_GET_EN(le32toh(sed->ed.ed_flags)), - OHCI_ED_GET_MAXP(le32toh(sed->ed.ed_flags)), sbuf, - (u_long)le32toh(sed->ed.ed_tailp), sbuf2, - (u_long)le32toh(sed->ed.ed_headp), - (u_long)le32toh(sed->ed.ed_nexted))); + printf("ED(%p) at 0x%08lx: addr=%d endpt=%d maxp=%d flags=%s\n" + "tailp=0x%08lx headflags=%s headp=0x%08lx nexted=0x%08lx\n", + sed, (u_long)sed->physaddr, + OHCI_ED_GET_FA(le32toh(sed->ed.ed_flags)), + OHCI_ED_GET_EN(le32toh(sed->ed.ed_flags)), + OHCI_ED_GET_MAXP(le32toh(sed->ed.ed_flags)), sbuf, + (u_long)le32toh(sed->ed.ed_tailp), sbuf2, + (u_long)le32toh(sed->ed.ed_headp), + (u_long)le32toh(sed->ed.ed_nexted)); } #endif @@ -1903,6 +1980,9 @@ ohci_open(usbd_pipe_handle pipe) DPRINTFN(1, ("ohci_open: pipe=%p, addr=%d, endpt=%d (%d)\n", pipe, addr, ed->bEndpointAddress, sc->sc_addr)); + if (sc->sc_dying) + return (USBD_IOERROR); + std = NULL; sed = NULL; @@ -1946,17 +2026,17 @@ ohci_open(usbd_pipe_handle pipe) fmt = OHCI_ED_FORMAT_GEN | OHCI_ED_DIR_TD; } sed->ed.ed_flags = htole32( - OHCI_ED_SET_FA(addr) | + OHCI_ED_SET_FA(addr) | OHCI_ED_SET_EN(ed->bEndpointAddress) | - (dev->lowspeed ? OHCI_ED_SPEED : 0) | fmt | - OHCI_ED_SET_MAXP(UGETW(ed->wMaxPacketSize))); + (dev->speed == USB_SPEED_LOW ? OHCI_ED_SPEED : 0) | + fmt | OHCI_ED_SET_MAXP(UGETW(ed->wMaxPacketSize))); sed->ed.ed_headp = sed->ed.ed_tailp = htole32(tdphys); switch (xfertype) { case UE_CONTROL: pipe->methods = &ohci_device_ctrl_methods; - err = usb_allocmem(&sc->sc_bus, - sizeof(usb_device_request_t), + err = usb_allocmem(&sc->sc_bus, + sizeof(usb_device_request_t), 0, &opipe->u.ctl.reqdma); if (err) goto bad; @@ -1991,7 +2071,7 @@ ohci_open(usbd_pipe_handle pipe) ohci_free_sed(sc, sed); bad0: return (USBD_NOMEM); - + } /* @@ -2009,22 +2089,25 @@ ohci_close_pipe(usbd_pipe_handle pipe, ohci_soft_ed_t *head) s = splusb(); #ifdef DIAGNOSTIC sed->ed.ed_flags |= htole32(OHCI_ED_SKIP); - if ((le32toh(sed->ed.ed_tailp) & OHCI_HEADMASK) != + if ((le32toh(sed->ed.ed_tailp) & OHCI_HEADMASK) != (le32toh(sed->ed.ed_headp) & OHCI_HEADMASK)) { - ohci_physaddr_t td = le32toh(sed->ed.ed_headp); ohci_soft_td_t *std; - for (std = LIST_FIRST(&sc->sc_hash_tds[HASH(td)]); - std != NULL; - std = LIST_NEXT(std, hnext)) - if (std->physaddr == td) - break; + std = ohci_hash_find_td(sc, le32toh(sed->ed.ed_headp)); printf("ohci_close_pipe: pipe not empty sed=%p hd=0x%x " "tl=0x%x pipe=%p, std=%p\n", sed, (int)le32toh(sed->ed.ed_headp), (int)le32toh(sed->ed.ed_tailp), pipe, std); +#ifdef USB_DEBUG + usbd_dump_pipe(&opipe->pipe); +#endif +#ifdef OHCI_DEBUG + ohci_dump_ed(sed); + if (std) + ohci_dump_td(std); +#endif usb_delay_ms(&sc->sc_bus, 2); - if ((le32toh(sed->ed.ed_tailp) & OHCI_HEADMASK) != + if ((le32toh(sed->ed.ed_tailp) & OHCI_HEADMASK) != (le32toh(sed->ed.ed_headp) & OHCI_HEADMASK)) printf("ohci_close_pipe: pipe still not empty\n"); } @@ -2034,7 +2117,7 @@ ohci_close_pipe(usbd_pipe_handle pipe, ohci_soft_ed_t *head) ohci_free_sed(sc, opipe->sed); } -/* +/* * Abort a device request. * If this routine is called at splusb() it guarantees that the request * will be removed from the hardware scheduling and that the callback @@ -2048,68 +2131,96 @@ void ohci_abort_xfer(usbd_xfer_handle xfer, usbd_status status) { struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe; - ohci_soft_ed_t *sed; + ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus; + ohci_soft_ed_t *sed = opipe->sed; + ohci_soft_td_t *p, *n; + ohci_physaddr_t headp; + int s, hit; - DPRINTF(("ohci_abort_xfer: xfer=%p pipe=%p\n", xfer, opipe)); + DPRINTF(("ohci_abort_xfer: xfer=%p pipe=%p sed=%p\n", xfer, opipe, + sed)); - xfer->status = status; + if (sc->sc_dying) { + /* If we're dying, just do the software part. */ + s = splusb(); + xfer->status = status; /* make software ignore it */ + usb_uncallout(xfer->timeout_handle, ohci_timeout, xfer); + usb_transfer_complete(xfer); + splx(s); + } - usb_uncallout(xfer->timeout_handle, ohci_timeout, xfer); + if (xfer->device->bus->intr_context || !curproc) + panic("ohci_abort_xfer: not in process context\n"); - sed = opipe->sed; + /* + * Step 1: Make interrupt routine and hardware ignore xfer. + */ + s = splusb(); + xfer->status = status; /* make software ignore it */ + usb_uncallout(xfer->timeout_handle, ohci_timeout, xfer); + splx(s); DPRINTFN(1,("ohci_abort_xfer: stop ed=%p\n", sed)); sed->ed.ed_flags |= htole32(OHCI_ED_SKIP); /* force hardware skip */ -#if 1 - if (xfer->device->bus->intr_context) { - /* We have no process context, so we can't use tsleep(). */ - usb_callout(xfer->pipe->abort_handle, - hz / USB_FRAMES_PER_SECOND, ohci_abort_xfer_end, xfer); - } else { -#if defined(DIAGNOSTIC) && defined(__i386__) && defined(__FreeBSD__) - KASSERT(intr_nesting_level == 0, - ("ohci_abort_req in interrupt context")); -#endif - usb_delay_ms(opipe->pipe.device->bus, 1); - ohci_abort_xfer_end(xfer); - } -#else - delay(1000); - ohci_abort_xfer_end(xfer); -#endif -} - -void -ohci_abort_xfer_end(void *v) -{ - usbd_xfer_handle xfer = v; - struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe; - ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus; - ohci_soft_ed_t *sed; - ohci_soft_td_t *p, *n; - int s; - + /* + * Step 2: Wait until we know hardware has finished any possible + * use of the xfer. Also make sure the soft interrupt routine + * has run. + */ + usb_delay_ms(opipe->pipe.device->bus, 20); /* Hardware finishes in 1ms */ s = splusb(); + sc->sc_softwake = 1; + usb_schedsoftintr(&sc->sc_bus); + tsleep(&sc->sc_softwake, PZERO, "ohciab", 0); + splx(s); + /* + * Step 3: Remove any vestiges of the xfer from the hardware. + * The complication here is that the hardware may have executed + * beyond the xfer we're trying to abort. So as we're scanning + * the TDs of this xfer we check if the hardware points to + * any of them. + */ + s = splusb(); /* XXX why? */ p = xfer->hcpriv; #ifdef DIAGNOSTIC if (p == NULL) { splx(s); - printf("ohci_abort_xfer: hcpriv==0\n"); + printf("ohci_abort_xfer: hcpriv is NULL\n"); return; } #endif +#ifdef OHCI_DEBUG + if (ohcidebug > 1) { + DPRINTF(("ohci_abort_xfer: sed=\n")); + ohci_dump_ed(sed); + ohci_dump_tds(p); + } +#endif + headp = le32toh(sed->ed.ed_headp) & OHCI_HEADMASK; + hit = 0; for (; p->xfer == xfer; p = n) { + hit |= headp == p->physaddr; n = p->nexttd; ohci_free_std(sc, p); } + /* Zap headp register if hardware pointed inside the xfer. */ + if (hit) { + DPRINTFN(1,("ohci_abort_xfer: set hd=0x08%x, tl=0x%08x\n", + (int)p->physaddr, (int)le32toh(sed->ed.ed_tailp))); + sed->ed.ed_headp = htole32(p->physaddr); /* unlink TDs */ + } else { + DPRINTFN(1,("ohci_abort_xfer: no hit\n")); + } - sed = opipe->sed; - DPRINTFN(2,("ohci_abort_xfer: set hd=%x, tl=%x\n", - (int)p->physaddr, (int)le32toh(sed->ed.ed_tailp))); - sed->ed.ed_headp = htole32(p->physaddr); /* unlink TDs */ + /* + * Step 4: Turn on hardware again. + */ sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP); /* remove hardware skip */ + /* + * Step 5: Execute callback. + */ usb_transfer_complete(xfer); splx(s); @@ -2124,7 +2235,7 @@ Static usb_device_descriptor_t ohci_devd = { {0x00, 0x01}, /* USB version */ UDCLASS_HUB, /* class */ UDSUBCLASS_HUB, /* subclass */ - 0, /* protocol */ + UDPROTO_FSHUB, 64, /* max packet */ {0},{0},{0x00,0x01}, /* device id */ 1,2,0, /* string indicies */ @@ -2152,7 +2263,7 @@ Static usb_interface_descriptor_t ohci_ifcd = { 1, UICLASS_HUB, UISUBCLASS_HUB, - 0, + UIPROTO_FSHUB, 0 }; @@ -2176,10 +2287,7 @@ Static usb_hub_descriptor_t ohci_hubd = { }; Static int -ohci_str(p, l, s) - usb_string_descriptor_t *p; - int l; - char *s; +ohci_str(usb_string_descriptor_t *p, int l, const char *s) { int i; @@ -2235,7 +2343,7 @@ ohci_root_ctrl_start(usbd_xfer_handle xfer) #endif req = &xfer->request; - DPRINTFN(4,("ohci_root_ctrl_control type=0x%02x request=%02x\n", + DPRINTFN(4,("ohci_root_ctrl_control type=0x%02x request=%02x\n", req->bmRequestType, req->bRequest)); len = UGETW(req->wLength); @@ -2250,7 +2358,7 @@ ohci_root_ctrl_start(usbd_xfer_handle xfer) 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. */ @@ -2419,13 +2527,13 @@ ohci_root_ctrl_start(usbd_xfer_handle xfer) hubd = ohci_hubd; hubd.bNbrPorts = sc->sc_noport; USETW(hubd.wHubCharacteristics, - (v & OHCI_NPS ? UHD_PWR_NO_SWITCH : + (v & OHCI_NPS ? UHD_PWR_NO_SWITCH : v & OHCI_PSM ? UHD_PWR_GANGED : UHD_PWR_INDIVIDUAL) /* XXX overcurrent */ ); hubd.bPwrOn2PwrGood = OHCI_GET_POTPGT(v); v = OREAD4(sc, OHCI_RH_DESCRIPTOR_B); - 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) hubd.DeviceRemovable[i++] = (u_int8_t)v; hubd.bDescLength = USB_HUB_DESCRIPTOR_SIZE + i; l = min(len, hubd.bDescLength); @@ -2482,8 +2590,13 @@ ohci_root_ctrl_start(usbd_xfer_handle xfer) DPRINTFN(5,("ohci_root_ctrl_transfer: reset port %d\n", index)); OWRITE4(sc, port, UPS_RESET); - for (i = 0; i < 10; i++) { - usb_delay_ms(&sc->sc_bus, 10); /* XXX */ + for (i = 0; i < 5; i++) { + usb_delay_ms(&sc->sc_bus, + USB_PORT_ROOT_RESET_DELAY); + if (sc->sc_dying) { + err = USBD_IOERROR; + goto ret; + } if ((OREAD4(sc, port) & UPS_RESET) == 0) break; } @@ -2578,7 +2691,7 @@ Static void ohci_root_intr_close(usbd_pipe_handle pipe) { ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus; - + DPRINTF(("ohci_root_intr_close\n")); sc->sc_intrxfer = NULL; @@ -2781,7 +2894,7 @@ ohci_device_bulk_abort(usbd_xfer_handle xfer) ohci_abort_xfer(xfer, USBD_CANCELLED); } -/* +/* * Close a device bulk pipe. */ Static void @@ -2843,7 +2956,7 @@ ohci_device_intr_start(usbd_xfer_handle xfer) tail->xfer = NULL; data->td.td_flags = htole32( - OHCI_TD_IN | OHCI_TD_NOCC | + OHCI_TD_IN | OHCI_TD_NOCC | OHCI_TD_SET_DI(1) | OHCI_TD_TOGGLE_CARRY); if (xfer->flags & USBD_SHORT_XFER_OK) data->td.td_flags |= htole32(OHCI_TD_R); @@ -2916,7 +3029,7 @@ ohci_device_intr_close(usbd_pipe_handle pipe) pipe, nslots, pos)); s = splusb(); sed->ed.ed_flags |= htole32(OHCI_ED_SKIP); - if ((le32toh(sed->ed.ed_tailp) & OHCI_HEADMASK) != + if ((le32toh(sed->ed.ed_tailp) & OHCI_HEADMASK) != (le32toh(sed->ed.ed_headp) & OHCI_HEADMASK)) usb_delay_ms(&sc->sc_bus, 2); @@ -2979,7 +3092,7 @@ ohci_device_setintr(ohci_softc_t *sc, struct ohci_pipe *opipe, int ival) bestbw = bw; } } - DPRINTFN(2, ("ohci_setintr: best=%d(%d..%d) bestbw=%d\n", + DPRINTFN(2, ("ohci_setintr: best=%d(%d..%d) bestbw=%d\n", best, slow, shigh, bestbw)); s = splusb(); @@ -3035,7 +3148,7 @@ ohci_device_isoc_enter(usbd_xfer_handle xfer) ohci_softc_t *sc = (ohci_softc_t *)dev->bus; ohci_soft_ed_t *sed = opipe->sed; struct iso *iso = &opipe->u.iso; - ohci_soft_itd_t *sitd, *nsitd; + ohci_soft_itd_t *sitd, *nsitd; ohci_physaddr_t buf, offs, noffs, bp0; int i, ncur, nframes; int s; @@ -3050,7 +3163,7 @@ ohci_device_isoc_enter(usbd_xfer_handle xfer) if (iso->next == -1) { /* Not in use yet, schedule it a few frames ahead. */ iso->next = le32toh(sc->sc_hcca->hcca_frame_number) + 5; - DPRINTFN(2,("ohci_device_isoc_enter: start next=%d\n", + DPRINTFN(2,("ohci_device_isoc_enter: start next=%d\n", iso->next)); } @@ -3064,7 +3177,7 @@ ohci_device_isoc_enter(usbd_xfer_handle xfer) noffs = offs + xfer->frlengths[i]; if (ncur == OHCI_ITD_NOFFSET || /* all offsets used */ OHCI_PAGE(buf + noffs) > bp0 + OHCI_PAGE_SIZE) { /* too many page crossings */ - + /* Allocate next ITD */ nsitd = ohci_alloc_sitd(sc); if (nsitd == NULL) { @@ -3076,7 +3189,7 @@ ohci_device_isoc_enter(usbd_xfer_handle xfer) /* Fill current ITD */ sitd->itd.itd_flags = htole32( - OHCI_ITD_NOCC | + OHCI_ITD_NOCC | OHCI_ITD_SET_SF(iso->next) | OHCI_ITD_SET_DI(6) | /* delay intr a little */ OHCI_ITD_SET_FC(ncur)); @@ -3088,7 +3201,7 @@ ohci_device_isoc_enter(usbd_xfer_handle xfer) sitd->flags = 0; sitd = nsitd; - iso->next = iso->next + ncur; + iso->next = iso->next + ncur; bp0 = OHCI_PAGE(buf + offs); ncur = 0; } @@ -3098,13 +3211,13 @@ ohci_device_isoc_enter(usbd_xfer_handle xfer) nsitd = ohci_alloc_sitd(sc); if (nsitd == NULL) { /* XXX what now? */ - printf("%s: isoc TD alloc failed\n", + printf("%s: isoc TD alloc failed\n", USBDEVNAME(sc->sc_bus.bdev)); return; } /* Fixup last used ITD */ sitd->itd.itd_flags = htole32( - OHCI_ITD_NOCC | + OHCI_ITD_NOCC | OHCI_ITD_SET_SF(iso->next) | OHCI_ITD_SET_DI(0) | OHCI_ITD_SET_FC(ncur)); @@ -3160,7 +3273,7 @@ ohci_device_isoc_start(usbd_xfer_handle xfer) #ifdef DIAGNOSTIC if (xfer->status != USBD_IN_PROGRESS) - printf("uhci_device_isoc_start: not in progress %p\n", xfer); + printf("ohci_device_isoc_start: not in progress %p\n", xfer); #endif /* XXX anything to do? */ @@ -3182,7 +3295,7 @@ ohci_device_isoc_abort(usbd_xfer_handle xfer) DPRINTFN(1,("ohci_device_isoc_abort: xfer=%p\n", xfer)); /* Transfer is already done. */ - if (xfer->status != USBD_NOT_STARTED && + if (xfer->status != USBD_NOT_STARTED && xfer->status != USBD_IN_PROGRESS) { splx(s); printf("ohci_device_isoc_abort: early return\n"); @@ -3230,7 +3343,7 @@ ohci_device_isoc_done(usbd_xfer_handle xfer) { struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe; ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus; - ohci_soft_itd_t *sitd, *nsitd; + ohci_soft_itd_t *sitd, *nsitd; DPRINTFN(1,("ohci_device_isoc_done: xfer=%p\n", xfer)); diff --git a/sys/dev/usb/ohcivar.h b/sys/dev/usb/ohcivar.h index 79b11e59168..19f4d228691 100644 --- a/sys/dev/usb/ohcivar.h +++ b/sys/dev/usb/ohcivar.h @@ -1,5 +1,5 @@ -/* $OpenBSD: ohcivar.h,v 1.15 2002/05/02 20:08:04 nate Exp $ */ -/* $NetBSD: ohcivar.h,v 1.28 2001/09/28 23:57:21 augustss Exp $ */ +/* $OpenBSD: ohcivar.h,v 1.16 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: ohcivar.h,v 1.30 2001/12/31 12:20:35 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/ohcivar.h,v 1.13 1999/11/17 22:33:41 n_hibma Exp $ */ /* @@ -106,6 +106,7 @@ typedef struct ohci_softc { int sc_noport; u_int8_t sc_addr; /* device address */ u_int8_t sc_conf; /* device configuration */ + char sc_softwake; ohci_soft_ed_t *sc_freeeds; ohci_soft_td_t *sc_freetds; @@ -138,13 +139,18 @@ typedef struct ohci_softc { char sc_dying; } ohci_softc_t; -void ohci_reset(ohci_softc_t *); +struct ohci_xfer { + struct usbd_xfer xfer; + struct usb_task abort_task; +}; + +#define OXFER(xfer) ((struct ehci_xfer *)(xfer)) + usbd_status ohci_init(ohci_softc_t *); int ohci_intr(void *); #if defined(__NetBSD__) || defined(__OpenBSD__) int ohci_detach(ohci_softc_t *, int); int ohci_activate(device_ptr_t, enum devact); #endif -Static void ohci_rhsc_enable(void *sc); #define MS_TO_TICKS(ms) ((ms) * hz / 1000) diff --git a/sys/dev/usb/ucom.c b/sys/dev/usb/ucom.c index 4145bc2242e..eb32bd3ad49 100644 --- a/sys/dev/usb/ucom.c +++ b/sys/dev/usb/ucom.c @@ -1,5 +1,5 @@ -/* $OpenBSD: ucom.c,v 1.9 2001/10/31 04:24:44 nate Exp $ */ -/* $NetBSD: ucom.c,v 1.39 2001/08/16 22:31:24 augustss Exp $ */ +/* $OpenBSD: ucom.c,v 1.10 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $ */ /* * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc. @@ -85,12 +85,16 @@ int ucomdebug = 0; #define UCOMUNIT_MASK 0x3ffff #define UCOMDIALOUT_MASK 0x80000 #define UCOMCALLUNIT_MASK 0x40000 + +#define LINESW(tp, func) ((tp)->t_linesw->func) #endif #if defined(__OpenBSD__) #define UCOMUNIT_MASK 0x3f #define UCOMDIALOUT_MASK 0x80 #define UCOMCALLUNIT_MASK 0x40 + +#define LINESW(tp, func) (linesw[(tp)->t_line].func) #endif #define UCOMUNIT(x) (minor(x) & UCOMUNIT_MASK) @@ -147,7 +151,7 @@ Static int ucomparam(struct tty *, struct termios *); Static void ucomstart(struct tty *); Static void ucom_shutdown(struct ucom_softc *); Static int ucom_do_ioctl(struct ucom_softc *, u_long, caddr_t, - int, struct proc *); + int, usb_proc_ptr); Static void ucom_dtr(struct ucom_softc *, int); Static void ucom_rts(struct ucom_softc *, int); Static void ucom_break(struct ucom_softc *, int); @@ -297,7 +301,7 @@ ucom_shutdown(struct ucom_softc *sc) } int -ucomopen(dev_t dev, int flag, int mode, struct proc *p) +ucomopen(dev_t dev, int flag, int mode, usb_proc_ptr p) { int unit = UCOMUNIT(dev); usbd_status err; @@ -463,7 +467,7 @@ ucomopen(dev_t dev, int flag, int mode, struct proc *p) if (error) goto bad; - error = (*linesw[tp->t_line].l_open)(dev, tp); + error = (*LINESW(tp, l_open))(dev, tp); if (error) goto bad; @@ -504,7 +508,7 @@ bad: } int -ucomclose(dev_t dev, int flag, int mode, struct proc *p) +ucomclose(dev_t dev, int flag, int mode, usb_proc_ptr p) { struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)]; struct tty *tp = sc->sc_tty; @@ -515,7 +519,7 @@ ucomclose(dev_t dev, int flag, int mode, struct proc *p) sc->sc_refcnt++; - (*linesw[tp->t_line].l_close)(tp, flag); + (*LINESW(tp, l_close))(tp, flag); ttyclose(tp); #if defined(__NetBSD__) @@ -551,7 +555,7 @@ ucomread(dev_t dev, struct uio *uio, int flag) return (EIO); sc->sc_refcnt++; - error = ((*linesw[tp->t_line].l_read)(tp, uio, flag)); + error = (*LINESW(tp, l_read))(tp, uio, flag); if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); return (error); @@ -568,18 +572,15 @@ ucomwrite(dev_t dev, struct uio *uio, int flag) return (EIO); sc->sc_refcnt++; - error = ((*linesw[tp->t_line].l_write)(tp, uio, flag)); + error = (*LINESW(tp, l_write))(tp, uio, flag); if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); return (error); } -#if 0 +#if defined(__NetBSD__) int -ucompoll(dev, events, p) - dev_t dev; - int events; - struct proc *p; +ucompoll(dev_t dev, int events, usb_proc_ptr p) { struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)]; struct tty *tp = sc->sc_tty; @@ -589,7 +590,7 @@ ucompoll(dev, events, p) return (EIO); sc->sc_refcnt++; - error = ((*linesw[tp->t_line].l_poll)(tp, events, p)); + error = (*LINESW(tp, l_poll))(tp, events, p); if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); return (error); @@ -606,7 +607,7 @@ ucomtty(dev_t dev) } int -ucomioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +ucomioctl(dev_t dev, u_long cmd, caddr_t data, int flag, usb_proc_ptr p) { struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)]; int error; @@ -620,7 +621,7 @@ ucomioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) Static int ucom_do_ioctl(struct ucom_softc *sc, u_long cmd, caddr_t data, - int flag, struct proc *p) + int flag, usb_proc_ptr p) { struct tty *tp = sc->sc_tty; int error; @@ -631,7 +632,7 @@ ucom_do_ioctl(struct ucom_softc *sc, u_long cmd, caddr_t data, DPRINTF(("ucomioctl: cmd=0x%08lx\n", cmd)); - error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); + error = (*LINESW(tp, l_ioctl))(tp, cmd, data, flag, p); if (error >= 0) return (error); @@ -805,7 +806,7 @@ ucom_status_change(struct ucom_softc *sc) sc->sc_methods->ucom_get_status(sc->sc_parent, sc->sc_portno, &sc->sc_lsr, &sc->sc_msr); if (ISSET((sc->sc_msr ^ old_msr), UMSR_DCD)) - (*linesw[tp->t_line].l_modem)(tp, + (*LINESW(tp, l_modem))(tp, ISSET(sc->sc_msr, UMSR_DCD)); } else { sc->sc_lsr = 0; @@ -866,7 +867,7 @@ ucomparam(struct tty *tp, struct termios *t) * explicit request. */ DPRINTF(("ucomparam: l_modem\n")); - (void) (*linesw[tp->t_line].l_modem)(tp, 1 /* XXX carrier */ ); + (void) (*LINESW(tp, l_modem))(tp, 1 /* XXX carrier */ ); #if 0 XXX what if the hardware is not open @@ -1033,7 +1034,7 @@ ucomwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status) CLR(tp->t_state, TS_FLUSH); else ndflush(&tp->t_outq, cc); - (*linesw[tp->t_line].l_start)(tp); + (*LINESW(tp, l_start))(tp); splx(s); return; @@ -1067,7 +1068,7 @@ ucomreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status) { struct ucom_softc *sc = (struct ucom_softc *)p; struct tty *tp = sc->sc_tty; - int (*rint)(int c, struct tty *tp) = linesw[tp->t_line].l_rint; + int (*rint)(int c, struct tty *tp) = LINESW(tp, l_rint); usbd_status err; u_int32_t cc; u_char *cp; @@ -1168,9 +1169,9 @@ ucomsubmatch(struct device *parent, void *match, void *aux) ucomsubmatch(struct device *parent, struct cfdata *cf, void *aux) #endif { - struct ucom_attach_args *uca = aux; + struct ucom_attach_args *uca = aux; #if defined(__OpenBSD__) - struct cfdata *cf = match; + struct cfdata *cf = match; #endif if (uca->portno != UCOM_UNK_PORTNO && diff --git a/sys/dev/usb/ucomvar.h b/sys/dev/usb/ucomvar.h index d3619824464..0e4e9b40a7a 100644 --- a/sys/dev/usb/ucomvar.h +++ b/sys/dev/usb/ucomvar.h @@ -1,5 +1,5 @@ -/* $OpenBSD: ucomvar.h,v 1.8 2002/01/30 20:45:34 nordin Exp $ */ -/* $NetBSD: ucomvar.h,v 1.9 2001/01/23 21:56:17 augustss Exp $ */ +/* $OpenBSD: ucomvar.h,v 1.9 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: ucomvar.h,v 1.10 2001/12/31 12:15:21 augustss Exp $ */ /* * Copyright (c) 1999 The NetBSD Foundation, Inc. @@ -56,7 +56,7 @@ struct ucom_methods { #define UCOM_SET_BREAK 3 int (*ucom_param)(void *sc, int portno, struct termios *); int (*ucom_ioctl)(void *sc, int portno, u_long cmd, - caddr_t data, int flag, struct proc *p); + caddr_t data, int flag, usb_proc_ptr p); int (*ucom_open)(void *sc, int portno); void (*ucom_close)(void *sc, int portno); void (*ucom_read)(void *sc, int portno, u_char **ptr, u_int32_t *count); @@ -105,10 +105,11 @@ struct ucom_attach_args { void *arg; }; -int ucomprint(void *aux, const char *pnp); -#if defined(__OpenBSD__) -int ucomsubmatch(struct device *parent, void *cf, void *aux); +#if defined(__NetBSD__) +int ucomsubmatch(struct device *, struct cfdata *, void *); #else -int ucomsubmatch(struct device *parent, struct cfdata *cf, void *aux); +int ucomsubmatch(struct device *, void *, void *); #endif + +int ucomprint(void *aux, const char *pnp); void ucom_status_change(struct ucom_softc *); diff --git a/sys/dev/usb/udsbr.c b/sys/dev/usb/udsbr.c new file mode 100644 index 00000000000..dc7a35abb09 --- /dev/null +++ b/sys/dev/usb/udsbr.c @@ -0,0 +1,279 @@ +/* $OpenBSD: udsbr.c,v 1.1 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: udsbr.c,v 1.6 2002/02/12 10:51:49 tron Exp $ */ + +/* + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Driver for the D-Link DSB-R100 FM radio. + * I apologize for the magic hex constants, but this is what happens + * when you have to reverse engineer the driver. + * Parts of the code borrowed from Linux and parts from Warner Losh's + * FreeBSD driver. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/device.h> + +#include <sys/radioio.h> +#include <dev/radio_if.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> + +#include <dev/usb/usbdevs.h> + +#ifdef UDSBR_DEBUG +#define DPRINTF(x) if (udsbrdebug) logprintf x +#define DPRINTFN(n,x) if (udsbrdebug>(n)) logprintf x +int udsbrdebug = 0; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +#define UDSBR_CONFIG_NO 1 + +Static int udsbr_get_info(void *, struct radio_info *); +Static int udsbr_set_info(void *, struct radio_info *); + +struct radio_hw_if udsbr_hw_if = { + NULL, /* open */ + NULL, /* close */ + udsbr_get_info, + udsbr_set_info, + NULL +}; + +struct udsbr_softc { + USBBASEDEVICE sc_dev; + usbd_device_handle sc_udev; + + char sc_mute; + char sc_vol; + u_int32_t sc_freq; + + struct device *sc_child; + + char sc_dying; +}; + +Static int udsbr_req(struct udsbr_softc *sc, int ureq, int value, + int index); +Static void udsbr_start(struct udsbr_softc *sc); +Static void udsbr_stop(struct udsbr_softc *sc); +Static void udsbr_setfreq(struct udsbr_softc *sc, int freq); +Static int udsbr_status(struct udsbr_softc *sc); + +USB_DECLARE_DRIVER(udsbr); + +USB_MATCH(udsbr) +{ + USB_MATCH_START(udsbr, uaa); + + DPRINTFN(50,("udsbr_match\n")); + + if (uaa->iface != NULL) + return (UMATCH_NONE); + + if (uaa->vendor != USB_VENDOR_CYPRESS || + uaa->product != USB_PRODUCT_CYPRESS_FMRADIO) + return (UMATCH_NONE); + return (UMATCH_VENDOR_PRODUCT); +} + +USB_ATTACH(udsbr) +{ + USB_ATTACH_START(udsbr, sc, uaa); + usbd_device_handle dev = uaa->device; + char devinfo[1024]; + usbd_status err; + + DPRINTFN(10,("udsbr_attach: sc=%p\n", sc)); + + usbd_devinfo(dev, 0, devinfo); + USB_ATTACH_SETUP; + printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo); + + err = usbd_set_config_no(dev, UDSBR_CONFIG_NO, 1); + if (err) { + printf("%s: setting config no failed\n", + USBDEVNAME(sc->sc_dev)); + USB_ATTACH_ERROR_RETURN; + } + + sc->sc_udev = dev; + + DPRINTFN(10, ("udsbr_attach: %p\n", sc->sc_udev)); + + usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, + USBDEV(sc->sc_dev)); + + sc->sc_child = radio_attach_mi(&udsbr_hw_if, sc, USBDEV(sc->sc_dev)); + + USB_ATTACH_SUCCESS_RETURN; +} + +USB_DETACH(udsbr) +{ + USB_DETACH_START(udsbr, sc); + int rv = 0; + + if (sc->sc_child != NULL) + rv = config_detach(sc->sc_child, flags); + + usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, + USBDEV(sc->sc_dev)); + + return (rv); +} + +int +udsbr_activate(device_ptr_t self, enum devact act) +{ + struct udsbr_softc *sc = (struct udsbr_softc *)self; + int rv = 0; + + switch (act) { + case DVACT_ACTIVATE: + return (EOPNOTSUPP); + break; + + case DVACT_DEACTIVATE: + sc->sc_dying = 1; + if (sc->sc_child != NULL) + rv = config_deactivate(sc->sc_child); + break; + } + return (rv); +} + +int +udsbr_req(struct udsbr_softc *sc, int ureq, int value, int index) +{ + usb_device_request_t req; + usbd_status err; + u_char data; + + DPRINTFN(1,("udsbr_req: ureq=0x%02x value=0x%04x index=0x%04x\n", + ureq, value, index)); + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = ureq; + USETW(req.wValue, value); + USETW(req.wIndex, index); + USETW(req.wLength, 1); + err = usbd_do_request(sc->sc_udev, &req, &data); + if (err) { + printf("%s: request failed err=%d\n", USBDEVNAME(sc->sc_dev), + err); + } + return !(data & 1); +} + +void +udsbr_start(struct udsbr_softc *sc) +{ + (void)udsbr_req(sc, 0x00, 0x0000, 0x00c7); + (void)udsbr_req(sc, 0x02, 0x0001, 0x0000); +} + +void +udsbr_stop(struct udsbr_softc *sc) +{ + (void)udsbr_req(sc, 0x00, 0x0016, 0x001c); + (void)udsbr_req(sc, 0x02, 0x0000, 0x0000); +} + +void +udsbr_setfreq(struct udsbr_softc *sc, int freq) +{ + DPRINTF(("udsbr_setfreq: setfreq=%d\n", freq)); + /* + * Freq now is in Hz. We need to convert it to the frequency + * that the radio wants. This frequency is 10.7MHz above + * the actual frequency. We then need to convert to + * units of 12.5kHz. We add one to the IFM to make rounding + * easier. + */ + freq = (freq * 1000 + 10700001) / 12500; + (void)udsbr_req(sc, 0x01, (freq >> 8) & 0xff, freq & 0xff); + (void)udsbr_req(sc, 0x00, 0x0096, 0x00b7); + usbd_delay_ms(sc->sc_udev, 240); /* wait for signal to settle */ +} + +int +udsbr_status(struct udsbr_softc *sc) +{ + return (udsbr_req(sc, 0x00, 0x0000, 0x0024)); +} + + +int +udsbr_get_info(void *v, struct radio_info *ri) +{ + struct udsbr_softc *sc = v; + + ri->mute = sc->sc_mute; + ri->volume = sc->sc_vol ? 255 : 0; + ri->caps = RADIO_CAPS_DETECT_STEREO; + ri->rfreq = 0; + ri->lock = 0; + ri->freq = sc->sc_freq; + ri->info = udsbr_status(sc) ? RADIO_INFO_STEREO : 0; + + return (0); +} + +int +udsbr_set_info(void *v, struct radio_info *ri) +{ + struct udsbr_softc *sc = v; + + sc->sc_mute = ri->mute != 0; + sc->sc_vol = ri->volume != 0; + sc->sc_freq = ri->freq; + udsbr_setfreq(sc, sc->sc_freq); + + if (sc->sc_mute || sc->sc_vol == 0) + udsbr_stop(sc); + else + udsbr_start(sc); + + return (0); +} diff --git a/sys/dev/usb/uftdi.c b/sys/dev/usb/uftdi.c index 9a9b94784e7..a86b3785fc4 100644 --- a/sys/dev/usb/uftdi.c +++ b/sys/dev/usb/uftdi.c @@ -1,5 +1,5 @@ -/* $OpenBSD: uftdi.c,v 1.3 2001/05/03 02:20:33 aaron Exp $ */ -/* $NetBSD: uftdi.c,v 1.6 2001/01/23 21:56:17 augustss Exp $ */ +/* $OpenBSD: uftdi.c,v 1.4 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: uftdi.c,v 1.9 2001/12/17 14:34:37 ichiro Exp $ */ /* * Copyright (c) 2000 The NetBSD Foundation, Inc. @@ -95,15 +95,19 @@ struct uftdi_softc { device_ptr_t sc_subdev; u_char sc_dying; + + u_int last_lcr; }; Static void uftdi_get_status(void *, int portno, u_char *lsr, u_char *msr); Static void uftdi_set(void *, int, int, int); Static int uftdi_param(void *, int, struct termios *); Static int uftdi_open(void *sc, int portno); -Static void uftdi_read(void *sc, int portno, u_char **ptr,u_int32_t *count); +Static void uftdi_read(void *sc, int portno, u_char **ptr, + u_int32_t *count); Static void uftdi_write(void *sc, int portno, u_char *to, u_char *from, u_int32_t *count); +Static void uftdi_break(void *sc, int portno, int onoff); struct ucom_methods uftdi_methods = { uftdi_get_status, @@ -376,7 +380,7 @@ uftdi_set(void *vsc, int portno, int reg, int onoff) ctl = onoff ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW; break; case UCOM_SET_BREAK: - /* XXX how do we set break? */ + uftdi_break(sc, portno, onoff); return; default: return; @@ -456,6 +460,8 @@ uftdi_param(void *vsc, int portno, struct termios *t) data |= FTDI_SIO_SET_DATA_BITS(8); break; } + sc->last_lcr = data; + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_SET_DATA; USETW(req.wValue, data); @@ -484,3 +490,27 @@ uftdi_get_status(void *vsc, int portno, u_char *lsr, u_char *msr) if (lsr != NULL) *lsr = sc->sc_lsr; } + +void +uftdi_break(void *vsc, int portno, int onoff) +{ + struct uftdi_softc *sc = vsc; + usb_device_request_t req; + int data; + + DPRINTF(("uftdi_break: sc=%p, port=%d onoff=%d\n", vsc, portno, + onoff)); + + if (onoff) { + data = sc->last_lcr | FTDI_SIO_SET_BREAK; + } else { + data = sc->last_lcr; + } + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = FTDI_SIO_SET_DATA; + USETW(req.wValue, data); + USETW(req.wIndex, portno); + USETW(req.wLength, 0); + (void)usbd_do_request(sc->sc_udev, &req, NULL); +} diff --git a/sys/dev/usb/uftdireg.h b/sys/dev/usb/uftdireg.h index 08019addafb..ce2dfb2ea78 100644 --- a/sys/dev/usb/uftdireg.h +++ b/sys/dev/usb/uftdireg.h @@ -1,5 +1,5 @@ -/* $OpenBSD: uftdireg.h,v 1.3 2001/10/31 04:24:44 nate Exp $ */ -/* $NetBSD: uftdireg.h,v 1.3 2001/06/12 14:59:28 wiz Exp $ */ +/* $OpenBSD: uftdireg.h,v 1.4 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: uftdireg.h,v 1.4 2001/12/17 14:31:02 ichiro Exp $ */ /* * Definitions for the FTDI USB Single Port Serial Converter - @@ -121,6 +121,7 @@ enum { #define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11) #define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11) #define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11) +#define FTDI_SIO_SET_BREAK (0x1 << 14) /* diff --git a/sys/dev/usb/ugen.c b/sys/dev/usb/ugen.c index 8052edd6834..6ba08f1ab6e 100644 --- a/sys/dev/usb/ugen.c +++ b/sys/dev/usb/ugen.c @@ -1,5 +1,5 @@ -/* $OpenBSD: ugen.c,v 1.18 2002/05/02 20:08:04 nate Exp $ */ -/* $NetBSD: ugen.c,v 1.49 2001/10/24 22:31:04 augustss Exp $ */ +/* $OpenBSD: ugen.c,v 1.19 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: ugen.c,v 1.58 2002/02/20 20:30:12 christos Exp $ */ /* $FreeBSD: src/sys/dev/usb/ugen.c,v 1.26 1999/11/17 22:33:41 n_hibma Exp $ */ /* @@ -20,8 +20,8 @@ * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. @@ -150,16 +150,16 @@ Static struct cdevsw ugen_cdevsw = { }; #endif -Static void ugenintr(usbd_xfer_handle xfer, usbd_private_handle addr, +Static void ugenintr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status); Static void ugen_isoc_rintr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status); Static int ugen_do_read(struct ugen_softc *, int, struct uio *, int); Static int ugen_do_write(struct ugen_softc *, int, struct uio *, int); -Static int ugen_do_ioctl(struct ugen_softc *, int, u_long, - caddr_t, int, struct proc *); +Static int ugen_do_ioctl(struct ugen_softc *, int, u_long, + caddr_t, int, usb_proc_ptr); Static int ugen_set_config(struct ugen_softc *sc, int configno); -Static usb_config_descriptor_t *ugen_get_cdesc(struct ugen_softc *sc, +Static usb_config_descriptor_t *ugen_get_cdesc(struct ugen_softc *sc, int index, int *lenp); Static usbd_status ugen_set_interface(struct ugen_softc *, int, int); Static int ugen_get_alt_index(struct ugen_softc *sc, int ifaceidx); @@ -201,7 +201,7 @@ USB_ATTACH(ugen) /* First set configuration index 0, the default one for ugen. */ err = usbd_set_config_index(udev, 0, 0); if (err) { - printf("%s: setting configuration index 0 failed\n", + printf("%s: setting configuration index 0 failed\n", USBDEVNAME(sc->sc_dev)); sc->sc_dying = 1; USB_ATTACH_ERROR_RETURN; @@ -211,7 +211,7 @@ USB_ATTACH(ugen) /* Set up all the local state for this configuration. */ err = ugen_set_config(sc, conf); if (err) { - printf("%s: setting configuration %d failed\n", + printf("%s: setting configuration %d failed\n", USBDEVNAME(sc->sc_dev), conf); sc->sc_dying = 1; USB_ATTACH_ERROR_RETURN; @@ -247,6 +247,19 @@ ugen_set_config(struct ugen_softc *sc, int configno) DPRINTFN(1,("ugen_set_config: %s to configno %d, sc=%p\n", USBDEVNAME(sc->sc_dev), configno, sc)); + + /* + * We start at 1, not 0, because we don't care whether the + * control endpoint is open or not. It is always present. + */ + for (endptno = 1; endptno < USB_MAX_ENDPOINTS; endptno++) + if (sc->sc_is_open[endptno]) { + DPRINTFN(1, + ("ugen_set_config: %s - endpoint %d is open\n", + USBDEVNAME(sc->sc_dev), endptno)); + return (USBD_IN_USE); + } + /* Avoid setting the current value. */ if (usbd_get_config_descriptor(dev)->bConfigurationValue != configno) { err = usbd_set_config_no(dev, configno, 1); @@ -272,7 +285,7 @@ ugen_set_config(struct ugen_softc *sc, int configno) dir = UE_GET_DIR(endpt) == UE_DIR_IN ? IN : OUT; sce = &sc->sc_endpoints[UE_GET_ADDR(endpt)][dir]; DPRINTFN(1,("ugen_set_config: endptno %d, endpt=0x%02x" - "(%d,%d), sce=%p\n", + "(%d,%d), sce=%p\n", endptno, endpt, UE_GET_ADDR(endpt), UE_GET_DIR(endpt), sce)); sce->sc = sc; @@ -284,7 +297,7 @@ ugen_set_config(struct ugen_softc *sc, int configno) } int -ugenopen(dev_t dev, int flag, int mode, struct proc *p) +ugenopen(dev_t dev, int flag, int mode, usb_proc_ptr p) { struct ugen_softc *sc; int unit = UGENUNIT(dev); @@ -299,7 +312,7 @@ ugenopen(dev_t dev, int flag, int mode, struct proc *p) USB_GET_SC_OPEN(ugen, unit, sc); - DPRINTFN(5, ("ugenopen: flag=%d, mode=%d, unit=%d endpt=%d\n", + DPRINTFN(5, ("ugenopen: flag=%d, mode=%d, unit=%d endpt=%d\n", flag, mode, unit, endpt)); if (sc == NULL || sc->sc_dying) @@ -330,7 +343,7 @@ ugenopen(dev_t dev, int flag, int mode, struct proc *p) sce = &sc->sc_endpoints[endpt][dir]; sce->state = 0; sce->timeout = USBD_NO_TIMEOUT; - DPRINTFN(5, ("ugenopen: sc=%p, endpt=%d, dir=%d, sce=%p\n", + DPRINTFN(5, ("ugenopen: sc=%p, endpt=%d, dir=%d, sce=%p\n", sc, endpt, dir, sce)); edesc = sce->edesc; switch (edesc->bmAttributes & UE_XFERTYPE) { @@ -339,14 +352,14 @@ ugenopen(dev_t dev, int flag, int mode, struct proc *p) if (isize == 0) /* shouldn't happen */ return (EINVAL); sce->ibuf = malloc(isize, M_USBDEV, M_WAITOK); - DPRINTFN(5, ("ugenopen: intr endpt=%d,isize=%d\n", + DPRINTFN(5, ("ugenopen: intr endpt=%d,isize=%d\n", endpt, isize)); - if (clalloc(&sce->q, UGEN_IBSIZE, 0) == -1) - return (ENOMEM); - err = usbd_open_pipe_intr(sce->iface, - edesc->bEndpointAddress, - USBD_SHORT_XFER_OK, &sce->pipeh, sce, - sce->ibuf, isize, ugenintr, + if (clalloc(&sce->q, UGEN_IBSIZE, 0) == -1) + return (ENOMEM); + err = usbd_open_pipe_intr(sce->iface, + edesc->bEndpointAddress, + USBD_SHORT_XFER_OK, &sce->pipeh, sce, + sce->ibuf, isize, ugenintr, USBD_DEFAULT_INTERVAL); if (err) { free(sce->ibuf, M_USBDEV); @@ -356,7 +369,7 @@ ugenopen(dev_t dev, int flag, int mode, struct proc *p) DPRINTFN(5, ("ugenopen: interrupt open done\n")); break; case UE_BULK: - err = usbd_open_pipe(sce->iface, + err = usbd_open_pipe(sce->iface, edesc->bEndpointAddress, 0, &sce->pipeh); if (err) return (EIO); @@ -371,7 +384,7 @@ ugenopen(dev_t dev, int flag, int mode, struct proc *p) M_USBDEV, M_WAITOK); sce->cur = sce->fill = sce->ibuf; sce->limit = sce->ibuf + isize * UGEN_NISOFRAMES; - DPRINTFN(5, ("ugenopen: isoc endpt=%d, isize=%d\n", + DPRINTFN(5, ("ugenopen: isoc endpt=%d, isize=%d\n", endpt, isize)); err = usbd_open_pipe(sce->iface, edesc->bEndpointAddress, 0, &sce->pipeh); @@ -408,6 +421,7 @@ ugenopen(dev_t dev, int flag, int mode, struct proc *p) usbd_free_xfer(sce->isoreqs[i].xfer); return (ENOMEM); case UE_CONTROL: + sce->timeout = USBD_DEFAULT_TIMEOUT; return (EINVAL); } } @@ -416,7 +430,7 @@ ugenopen(dev_t dev, int flag, int mode, struct proc *p) } int -ugenclose(dev_t dev, int flag, int mode, struct proc *p) +ugenclose(dev_t dev, int flag, int mode, usb_proc_ptr p) { int endpt = UGENENDPOINT(dev); struct ugen_softc *sc; @@ -448,31 +462,30 @@ ugenclose(dev_t dev, int flag, int mode, struct proc *p) sce = &sc->sc_endpoints[endpt][dir]; if (sce == NULL || sce->pipeh == NULL) continue; - DPRINTFN(5, ("ugenclose: endpt=%d dir=%d sce=%p\n", + DPRINTFN(5, ("ugenclose: endpt=%d dir=%d sce=%p\n", endpt, dir, sce)); usbd_abort_pipe(sce->pipeh); usbd_close_pipe(sce->pipeh); sce->pipeh = NULL; - switch (sce->edesc->bmAttributes & UE_XFERTYPE) { - case UE_INTERRUPT: - ndflush(&sce->q, sce->q.c_cc); - clfree(&sce->q); - break; - case UE_ISOCHRONOUS: - for (i = 0; i < UGEN_NISOREQS; ++i) - usbd_free_xfer(sce->isoreqs[i].xfer); - - default: - break; + switch (sce->edesc->bmAttributes & UE_XFERTYPE) { + case UE_INTERRUPT: + ndflush(&sce->q, sce->q.c_cc); + clfree(&sce->q); + break; + case UE_ISOCHRONOUS: + for (i = 0; i < UGEN_NISOREQS; ++i) + usbd_free_xfer(sce->isoreqs[i].xfer); + + default: + break; } if (sce->ibuf != NULL) { free(sce->ibuf, M_USBDEV); sce->ibuf = NULL; clfree(&sce->q); - } } sc->sc_is_open[endpt] = 0; @@ -558,8 +571,8 @@ ugen_do_read(struct ugen_softc *sc, int endpt, struct uio *uio, int flag) tn = n; err = usbd_bulk_transfer( xfer, sce->pipeh, - sce->state & UGEN_SHORT_OK ? - USBD_SHORT_XFER_OK : 0, + sce->state & UGEN_SHORT_OK ? + USBD_SHORT_XFER_OK : 0, sce->timeout, buf, &tn, "ugenrb"); if (err) { if (err == USBD_INTERRUPTED) @@ -677,7 +690,7 @@ ugen_do_write(struct ugen_softc *sc, int endpt, struct uio *uio, int flag) if (error) break; DPRINTFN(1, ("ugenwrite: transfer %d bytes\n", n)); - err = usbd_bulk_transfer(xfer, sce->pipeh, 0, + err = usbd_bulk_transfer(xfer, sce->pipeh, 0, sce->timeout, buf, &n,"ugenwb"); if (err) { if (err == USBD_INTERRUPTED) @@ -798,14 +811,15 @@ ugenintr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) if (status != USBD_NORMAL_COMPLETION) { DPRINTF(("ugenintr: status=%d\n", status)); - usbd_clear_endpoint_stall_async(sce->pipeh); + if (status == USBD_STALLED) + usbd_clear_endpoint_stall_async(sce->pipeh); return; } usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL); ibuf = sce->ibuf; - DPRINTFN(5, ("ugenintr: xfer=%p status=%d count=%d\n", + DPRINTFN(5, ("ugenintr: xfer=%p status=%d count=%d\n", xfer, status, count)); DPRINTFN(5, (" data = %02x %02x %02x\n", ibuf[0], ibuf[1], ibuf[2])); @@ -821,7 +835,7 @@ ugenintr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) } Static void -ugen_isoc_rintr(usbd_xfer_handle xfer, usbd_private_handle addr, +ugen_isoc_rintr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) { struct isoreq *req = addr; @@ -959,7 +973,8 @@ ugen_get_cdesc(struct ugen_softc *sc, int index, int *lenp) if (lenp) *lenp = len; cdesc = malloc(len, M_TEMP, M_WAITOK); - err = usbd_get_config_desc_full(sc->sc_udev, index, cdesc,len); + err = usbd_get_config_desc_full(sc->sc_udev, index, cdesc, + len); if (err) { free(cdesc, M_TEMP); return (0); @@ -982,7 +997,7 @@ ugen_get_alt_index(struct ugen_softc *sc, int ifaceidx) Static int ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd, - caddr_t addr, int flag, struct proc *p) + caddr_t addr, int flag, usb_proc_ptr p) { struct ugen_endpoint *sce; usbd_status err; @@ -1006,18 +1021,12 @@ ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd, /* All handled in the upper FS layer. */ return (0); case USB_SET_SHORT_XFER: - /* This flag only affects read */ if (endpt == USB_CONTROL_ENDPOINT) return (EINVAL); + /* This flag only affects read */ sce = &sc->sc_endpoints[endpt][IN]; - if (sce == NULL) + if (sce == NULL || sce->pipeh == NULL) return (EINVAL); -#ifdef DIAGNOSTIC - if (sce->pipeh == NULL) { - printf("ugenioctl: USB_SET_SHORT_XFER, no pipe\n"); - return (EIO); - } -#endif if (*(int *)addr) sce->state |= UGEN_SHORT_OK; else @@ -1025,14 +1034,12 @@ ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd, return (0); case USB_SET_TIMEOUT: sce = &sc->sc_endpoints[endpt][IN]; - if (sce == NULL) + if (sce == NULL + /* XXX this shouldn't happen, but the distinction between + input and output pipes isn't clear enough. + || sce->pipeh == NULL */ + ) return (EINVAL); -#ifdef DIAGNOSTIC - if (sce->pipeh == NULL) { - printf("ugenioctl: USB_SET_TIMEOUT, no pipe\n"); - return (EIO); - } -#endif sce->timeout = *(int *)addr; return (0); default: @@ -1058,13 +1065,19 @@ ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd, if (!(flag & FWRITE)) return (EPERM); err = ugen_set_config(sc, *(int *)addr); - if (err) + switch (err) { + case USBD_NORMAL_COMPLETION: + break; + case USBD_IN_USE: + return (EBUSY); + default: return (EIO); + } break; case USB_GET_ALTINTERFACE: ai = (struct usb_alt_interface *)addr; - err = usbd_device2interface_handle(sc->sc_udev, - ai->uai_interface_index, &iface); + err = usbd_device2interface_handle(sc->sc_udev, + ai->uai_interface_index, &iface); if (err) return (EINVAL); idesc = usbd_get_interface_descriptor(iface); @@ -1076,11 +1089,12 @@ ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd, if (!(flag & FWRITE)) return (EPERM); ai = (struct usb_alt_interface *)addr; - err = usbd_device2interface_handle(sc->sc_udev, - ai->uai_interface_index, &iface); + err = usbd_device2interface_handle(sc->sc_udev, + ai->uai_interface_index, &iface); if (err) return (EINVAL); - err = ugen_set_interface(sc, ai->uai_interface_index, ai->uai_alt_no); + err = ugen_set_interface(sc, ai->uai_interface_index, + ai->uai_alt_no); if (err) return (EINVAL); break; @@ -1094,7 +1108,8 @@ ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd, free(cdesc, M_TEMP); return (EINVAL); } - ai->uai_alt_no = usbd_get_no_alts(cdesc, idesc->bInterfaceNumber); + ai->uai_alt_no = usbd_get_no_alts(cdesc, + idesc->bInterfaceNumber); free(cdesc, M_TEMP); break; case USB_GET_DEVICE_DESC: @@ -1137,7 +1152,7 @@ ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd, alt = ugen_get_alt_index(sc, ed->ued_interface_index); else alt = ed->ued_alt_index; - edesc = usbd_find_edesc(cdesc, ed->ued_interface_index, + edesc = usbd_find_edesc(cdesc, ed->ued_interface_index, alt, ed->ued_endpoint_index); if (edesc == NULL) { free(cdesc, M_TEMP); @@ -1172,7 +1187,7 @@ ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd, } case USB_GET_STRING_DESC: si = (struct usb_string_desc *)addr; - err = usbd_get_string_desc(sc->sc_udev, si->usd_string_index, + err = usbd_get_string_desc(sc->sc_udev, si->usd_string_index, si->usd_language_id, &si->usd_desc); if (err) return (EINVAL); @@ -1209,7 +1224,7 @@ ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd, uio.uio_offset = 0; uio.uio_segflg = UIO_USERSPACE; uio.uio_rw = - ur->ucr_request.bmRequestType & UT_READ ? + ur->ucr_request.bmRequestType & UT_READ ? UIO_READ : UIO_WRITE; uio.uio_procp = p; ptr = malloc(len, M_TEMP, M_WAITOK); @@ -1219,8 +1234,9 @@ ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd, goto ret; } } - err = usbd_do_request_flags(sc->sc_udev, &ur->ucr_request, - ptr, ur->ucr_flags, &ur->ucr_actlen); + sce = &sc->sc_endpoints[endpt][IN]; + err = usbd_do_request_flags(sc->sc_udev, &ur->ucr_request, + ptr, ur->ucr_flags, &ur->ucr_actlen, sce->timeout); if (err) { error = EIO; goto ret; @@ -1248,7 +1264,7 @@ ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd, } int -ugenioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) +ugenioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p) { int endpt = UGENENDPOINT(dev); struct ugen_softc *sc; @@ -1264,7 +1280,7 @@ ugenioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) } int -ugenpoll(dev_t dev, int events, struct proc *p) +ugenpoll(dev_t dev, int events, usb_proc_ptr p) { struct ugen_softc *sc; struct ugen_endpoint *sce; @@ -1309,12 +1325,12 @@ ugenpoll(dev_t dev, int events, struct proc *p) } break; case UE_BULK: - /* + /* * We have no easy way of determining if a read will * yield any data or a write will happen. * Pretend they will. */ - revents |= events & + revents |= events & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM); break; default: diff --git a/sys/dev/usb/uhci.c b/sys/dev/usb/uhci.c index d6c71087c94..090f26f99f8 100644 --- a/sys/dev/usb/uhci.c +++ b/sys/dev/usb/uhci.c @@ -1,5 +1,5 @@ -/* $OpenBSD: uhci.c,v 1.23 2002/01/10 00:46:36 nordin Exp $ */ -/* $NetBSD: uhci.c,v 1.142 2001/10/25 02:08:13 augustss Exp $ */ +/* $OpenBSD: uhci.c,v 1.24 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: uhci.c,v 1.158 2002/03/17 18:02:53 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/uhci.c,v 1.33 1999/11/17 22:33:41 n_hibma Exp $ */ /* @@ -174,10 +174,10 @@ Static void uhci_enter_ctl_q(uhci_softc_t *, uhci_soft_qh_t *, Static void uhci_exit_ctl_q(uhci_softc_t *, uhci_soft_qh_t *); #endif -Static void uhci_free_std_chain(uhci_softc_t *, +Static void uhci_free_std_chain(uhci_softc_t *, uhci_soft_td_t *, uhci_soft_td_t *); Static usbd_status uhci_alloc_std_chain(struct uhci_pipe *, - uhci_softc_t *, int, int, u_int16_t, usb_dma_t *, + uhci_softc_t *, int, int, u_int16_t, usb_dma_t *, uhci_soft_td_t **, uhci_soft_td_t **); Static void uhci_poll_hub(void *); Static void uhci_waitintr(uhci_softc_t *, usbd_xfer_handle); @@ -187,6 +187,7 @@ Static void uhci_idone(uhci_intr_info_t *); Static void uhci_abort_xfer(usbd_xfer_handle, usbd_status status); Static void uhci_timeout(void *); +Static void uhci_timeout_task(void *); Static void uhci_add_ls_ctrl(uhci_softc_t *, uhci_soft_qh_t *); Static void uhci_add_hs_ctrl(uhci_softc_t *, uhci_soft_qh_t *); Static void uhci_add_bulk(uhci_softc_t *, uhci_soft_qh_t *); @@ -249,8 +250,8 @@ Static void uhci_softintr(void *); Static usbd_status uhci_device_request(usbd_xfer_handle xfer); Static void uhci_add_intr(uhci_softc_t *, uhci_soft_qh_t *); -Static void uhci_remove_intr(uhci_softc_t*, uhci_soft_qh_t*); -Static usbd_status uhci_device_setintr(uhci_softc_t *sc, +Static void uhci_remove_intr(uhci_softc_t *, uhci_soft_qh_t *); +Static usbd_status uhci_device_setintr(uhci_softc_t *sc, struct uhci_pipe *pipe, int ival); Static void uhci_device_clear_toggle(usbd_pipe_handle pipe); @@ -301,7 +302,7 @@ struct usbd_bus_methods uhci_bus_methods = { uhci_freex, }; -struct usbd_pipe_methods uhci_root_ctrl_methods = { +struct usbd_pipe_methods uhci_root_ctrl_methods = { uhci_root_ctrl_transfer, uhci_root_ctrl_start, uhci_root_ctrl_abort, @@ -310,7 +311,7 @@ struct usbd_pipe_methods uhci_root_ctrl_methods = { uhci_root_ctrl_done, }; -struct usbd_pipe_methods uhci_root_intr_methods = { +struct usbd_pipe_methods uhci_root_intr_methods = { uhci_root_intr_transfer, uhci_root_intr_start, uhci_root_intr_abort, @@ -366,7 +367,7 @@ uhci_find_prev_qh(uhci_soft_qh_t *pqh, uhci_soft_qh_t *sqh) DPRINTFN(15,("uhci_find_prev_qh: pqh=%p sqh=%p\n", pqh, sqh)); for (; pqh->hlink != sqh; pqh = pqh->hlink) { -#if defined(DIAGNOSTIC) || defined(UHCI_DEBUG) +#if defined(DIAGNOSTIC) || defined(UHCI_DEBUG) if (le32toh(pqh->qh.qh_hlink) & UHCI_PTR_T) { printf("uhci_find_prev_qh: QH not found\n"); return (NULL); @@ -401,13 +402,12 @@ uhci_init(uhci_softc_t *sc) uhci_dumpregs(sc); #endif - uhci_run(sc, 0); /* stop the controller */ UWRITE2(sc, UHCI_INTR, 0); /* disable interrupts */ uhci_globalreset(sc); /* reset the controller */ uhci_reset(sc); /* Allocate and initialize real frame array. */ - err = usb_allocmem(&sc->sc_bus, + err = usb_allocmem(&sc->sc_bus, UHCI_FRAMELIST_COUNT * sizeof(uhci_physaddr_t), UHCI_FRAMELIST_ALIGN, &sc->sc_dma); if (err) @@ -416,7 +416,7 @@ uhci_init(uhci_softc_t *sc) UWRITE2(sc, UHCI_FRNUM, 0); /* set frame number to 0 */ UWRITE4(sc, UHCI_FLBASEADDR, DMAADDR(&sc->sc_dma)); /* set frame list*/ - /* + /* * Allocate a TD, inactive, that hangs from the last QH. * This is to avoid a bug in the PIIX that makes it run berserk * otherwise. @@ -470,7 +470,7 @@ uhci_init(uhci_softc_t *sc) clsqh->qh.qh_elink = htole32(UHCI_PTR_T); sc->sc_lctl_start = sc->sc_lctl_end = clsqh; - /* + /* * Make all (virtual) frame list pointers point to the interrupt * queue heads and the interrupt queue heads at the control * queue head and point the physical frame list to the virtual. @@ -493,8 +493,8 @@ uhci_init(uhci_softc_t *sc) sc->sc_vframes[i].etd = std; sc->sc_vframes[i].hqh = sqh; sc->sc_vframes[i].eqh = sqh; - for (j = i; - j < UHCI_FRAMELIST_COUNT; + for (j = i; + j < UHCI_FRAMELIST_COUNT; j += UHCI_VFRAMELIST_COUNT) sc->sc_pframes[j] = htole32(std->physaddr); } @@ -516,7 +516,7 @@ uhci_init(uhci_softc_t *sc) #endif DPRINTFN(1,("uhci_init: enabling\n")); - UWRITE2(sc, UHCI_INTR, UHCI_INTR_TOCRCIE | UHCI_INTR_RIE | + UWRITE2(sc, UHCI_INTR, UHCI_INTR_TOCRCIE | UHCI_INTR_RIE | UHCI_INTR_IOCE | UHCI_INTR_SPIE); /* enable interrupts */ UHCICMD(sc, UHCI_CMD_MAXP); /* Assume 64 byte packets at frame end */ @@ -552,7 +552,7 @@ uhci_detach(struct uhci_softc *sc, int flags) if (sc->sc_child != NULL) rv = config_detach(sc->sc_child, flags); - + if (rv != 0) return (rv); @@ -568,7 +568,7 @@ uhci_detach(struct uhci_softc *sc, int flags) break; SIMPLEQ_REMOVE_HEAD(&sc->sc_free_xfers, xfer, next); free(xfer, M_USB); - } + } /* XXX free other data structures XXX */ @@ -582,10 +582,10 @@ uhci_allocm(struct usbd_bus *bus, usb_dma_t *dma, u_int32_t size) struct uhci_softc *sc = (struct uhci_softc *)bus; u_int32_t n; - /* + /* * XXX * Since we are allocating a buffer we can assume that we will - * need TDs for it. Since we don't want to alolocate those from + * need TDs for it. Since we don't want to allocate those from * an interrupt context, we allocate them here and free them again. * This is no guarantee that we'll get the TDs next time... */ @@ -594,7 +594,8 @@ uhci_allocm(struct usbd_bus *bus, usb_dma_t *dma, u_int32_t size) u_int32_t i; uhci_soft_td_t **stds; DPRINTF(("uhci_allocm: get %d TDs\n", n)); - stds = malloc(sizeof(uhci_soft_td_t *) * n, M_TEMP, M_NOWAIT); + stds = malloc(sizeof(uhci_soft_td_t *) * n, M_TEMP, + M_NOWAIT); if (stds == NULL) panic("uhci_allocm"); memset(stds, 0, sizeof(uhci_soft_td_t *) * n); @@ -693,7 +694,7 @@ uhci_power(int why, void *v) s = splhardusb(); cmd = UREAD2(sc, UHCI_CMD); - DPRINTF(("uhci_power: sc=%p, why=%d (was %d), cmd=0x%x\n", + DPRINTF(("uhci_power: sc=%p, why=%d (was %d), cmd=0x%x\n", sc, why, sc->sc_suspend, cmd)); switch (why) { @@ -739,7 +740,7 @@ uhci_power(int why, void *v) UHCICMD(sc, cmd | UHCI_CMD_FGR); /* force global resume */ usb_delay_ms(&sc->sc_bus, USB_RESUME_DELAY); UHCICMD(sc, cmd & ~UHCI_CMD_EGSM); /* back to normal */ - UWRITE2(sc, UHCI_INTR, UHCI_INTR_TOCRCIE | UHCI_INTR_RIE | + UWRITE2(sc, UHCI_INTR, UHCI_INTR_TOCRCIE | UHCI_INTR_RIE | UHCI_INTR_IOCE | UHCI_INTR_SPIE); /* re-enable intrs */ uhci_run(sc, 1); /* and start traffic again */ usb_delay_ms(&sc->sc_bus, USB_RESUME_RECOVERY); @@ -792,9 +793,9 @@ uhci_dump_td(uhci_soft_td_t *p) (long)le32toh(p->td.td_token), (long)le32toh(p->td.td_buffer))); - bitmask_snprintf((int)le32toh(p->td.td_link), "\20\1T\2Q\3VF", + bitmask_snprintf((u_int32_t)le32toh(p->td.td_link), "\20\1T\2Q\3VF", sbuf, sizeof(sbuf)); - bitmask_snprintf((int)le32toh(p->td.td_status), + bitmask_snprintf((u_int32_t)le32toh(p->td.td_status), "\20\22BITSTUFF\23CRCTO\24NAK\25BABBLE\26DBUFFER\27" "STALLED\30ACTIVE\31IOC\32ISO\33LS\36SPD", sbuf2, sizeof(sbuf2)); @@ -891,7 +892,7 @@ uhci_dump_ii(uhci_intr_info_t *ii) usbd_pipe_handle pipe; usb_endpoint_descriptor_t *ed; usbd_device_handle dev; - + #ifdef DIAGNOSTIC #define DONE ii->isdone #else @@ -924,8 +925,8 @@ uhci_dump_ii(uhci_intr_info_t *ii) } ed = pipe->endpoint->edesc; dev = pipe->device; - printf("ii %p: done=%d xfer=%p dev=%p vid=0x%04x pid=0x%04x addr=%d pipe=%p ep=0x%02x attr=0x%02x\n", - ii, DONE, ii->xfer, dev, + printf("ii %p: done=%d xfer=%p dev=%p vid=0x%04x pid=0x%04x addr=%d pipe=%p ep=0x%02x attr=0x%02x\n", + ii, DONE, ii->xfer, dev, UGETW(dev->ddesc.idVendor), UGETW(dev->ddesc.idProduct), dev->address, pipe, @@ -1010,7 +1011,7 @@ uhci_add_loop(uhci_softc_t *sc) { if (++sc->sc_loops == 1) { DPRINTFN(5,("uhci_start_loop: add\n")); /* Note, we don't loop back the soft pointer. */ - sc->sc_last_qh->qh.qh_hlink = + sc->sc_last_qh->qh.qh_hlink = htole32(sc->sc_hctl_start->physaddr | UHCI_PTR_QH); } } @@ -1073,7 +1074,7 @@ uhci_remove_hs_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh) } pqh = uhci_find_prev_qh(sc->sc_hctl_start, sqh); - pqh->hlink = sqh->hlink; + pqh->hlink = sqh->hlink; pqh->qh.qh_hlink = sqh->qh.qh_hlink; delay(UHCI_QH_REMOVE_DELAY); if (sc->sc_hctl_end == sqh) @@ -1090,9 +1091,9 @@ uhci_add_ls_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh) DPRINTFN(10, ("uhci_add_ls_ctrl: sqh=%p\n", sqh)); eqh = sc->sc_lctl_end; - sqh->hlink = eqh->hlink; + sqh->hlink = eqh->hlink; sqh->qh.qh_hlink = eqh->qh.qh_hlink; - eqh->hlink = sqh; + eqh->hlink = sqh; eqh->qh.qh_hlink = htole32(sqh->physaddr | UHCI_PTR_QH); sc->sc_lctl_end = sqh; } @@ -1112,7 +1113,7 @@ uhci_remove_ls_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh) delay(UHCI_QH_REMOVE_DELAY); } pqh = uhci_find_prev_qh(sc->sc_lctl_start, sqh); - pqh->hlink = sqh->hlink; + pqh->hlink = sqh->hlink; pqh->qh.qh_hlink = sqh->qh.qh_hlink; delay(UHCI_QH_REMOVE_DELAY); if (sc->sc_lctl_end == sqh) @@ -1129,9 +1130,9 @@ uhci_add_bulk(uhci_softc_t *sc, uhci_soft_qh_t *sqh) DPRINTFN(10, ("uhci_add_bulk: sqh=%p\n", sqh)); eqh = sc->sc_bulk_end; - sqh->hlink = eqh->hlink; + sqh->hlink = eqh->hlink; sqh->qh.qh_hlink = eqh->qh.qh_hlink; - eqh->hlink = sqh; + eqh->hlink = sqh; eqh->qh.qh_hlink = htole32(sqh->physaddr | UHCI_PTR_QH); sc->sc_bulk_end = sqh; uhci_add_loop(sc); @@ -1167,6 +1168,9 @@ uhci_intr(void *arg) { uhci_softc_t *sc = arg; + if (sc->sc_dying) + return (0); + DPRINTFN(15,("uhci_intr: real interrupt\n")); if (sc->sc_bus.use_polling) { #ifdef DIAGNOSTIC @@ -1190,15 +1194,10 @@ uhci_intr1(uhci_softc_t *sc) } #endif - status = UREAD2(sc, UHCI_STS); + status = UREAD2(sc, UHCI_STS) & UHCI_STS_ALLINTRS; if (status == 0) /* The interrupt was not for us. */ return (0); -#if defined(DIAGNOSTIC) && defined(__NetBSD__) - if (sc->sc_suspend != PWR_RESUME) - printf("uhci_intr: suspended sts=0x%x\n", status); -#endif - if (sc->sc_suspend != PWR_RESUME) { printf("%s: interrupt while not operating ignored\n", USBDEVNAME(sc->sc_bus.bdev)); @@ -1223,13 +1222,13 @@ uhci_intr1(uhci_softc_t *sc) } if (status & UHCI_STS_HCPE) { ack |= UHCI_STS_HCPE; - printf("%s: host controller process error\n", + printf("%s: host controller process error\n", USBDEVNAME(sc->sc_bus.bdev)); } if (status & UHCI_STS_HCH) { /* no acknowledge needed */ if (!sc->sc_dying) { - printf("%s: host controller halted\n", + printf("%s: host controller halted\n", USBDEVNAME(sc->sc_bus.bdev)); #ifdef UHCI_DEBUG uhci_dump_all(sc); @@ -1275,6 +1274,11 @@ uhci_softintr(void *v) for (ii = LIST_FIRST(&sc->sc_intrhead); ii; ii = LIST_NEXT(ii, list)) uhci_check_intr(sc, ii); + if (sc->sc_softwake) { + sc->sc_softwake = 0; + wakeup(&sc->sc_softwake); + } + sc->sc_bus.intr_context--; } @@ -1292,6 +1296,12 @@ uhci_check_intr(uhci_softc_t *sc, uhci_intr_info_t *ii) return; } #endif + if (ii->xfer->status == USBD_CANCELLED || + ii->xfer->status == USBD_TIMEOUT) { + DPRINTF(("uhci_check_intr: aborted xfer=%p\n", ii->xfer)); + return; + } + if (ii->stdstart == NULL) return; lstd = ii->stdend; @@ -1301,7 +1311,7 @@ uhci_check_intr(uhci_softc_t *sc, uhci_intr_info_t *ii) return; } #endif - /* + /* * If the last TD is still active we need to check whether there * is a an error somewhere in the middle, or whether there was a * short packet (SPD and not ACTIVE). @@ -1318,7 +1328,7 @@ uhci_check_intr(uhci_softc_t *sc, uhci_intr_info_t *ii) goto done; /* We want short packets, and it is short: it's done */ if ((status & UHCI_TD_SPD) && - UHCI_TD_GET_ACTLEN(status) < + UHCI_TD_GET_ACTLEN(status) < UHCI_TD_GET_MAXLEN(le32toh(std->td.td_token))) goto done; } @@ -1361,12 +1371,6 @@ uhci_idone(uhci_intr_info_t *ii) } #endif - if (xfer->status == USBD_CANCELLED || - xfer->status == USBD_TIMEOUT) { - DPRINTF(("uhci_idone: aborted xfer=%p\n", xfer)); - return; - } - if (xfer->nframes != 0) { /* Isoc transfer, do things differently. */ uhci_soft_td_t **stds = upipe->u.iso.stds; @@ -1422,14 +1426,15 @@ uhci_idone(uhci_intr_info_t *ii) upipe->nexttoggle = UHCI_TD_GET_DT(le32toh(std->td.td_token)); status &= UHCI_TD_ERROR; - DPRINTFN(10, ("uhci_idone: actlen=%d, status=0x%x\n", + DPRINTFN(10, ("uhci_idone: actlen=%d, status=0x%x\n", actlen, status)); xfer->actlen = actlen; if (status != 0) { #ifdef UHCI_DEBUG char sbuf[128]; - bitmask_snprintf((int)status, "\20\22BITSTUFF\23CRCTO\24NAK\25" + bitmask_snprintf((u_int32_t)status, + "\20\22BITSTUFF\23CRCTO\24NAK\25" "BABBLE\26DBUFFER\27STALLED\30ACTIVE", sbuf, sizeof(sbuf)); @@ -1461,17 +1466,33 @@ void uhci_timeout(void *addr) { uhci_intr_info_t *ii = addr; + struct uhci_xfer *uxfer = UXFER(ii->xfer); + struct uhci_pipe *upipe = (struct uhci_pipe *)uxfer->xfer.pipe; + uhci_softc_t *sc = (uhci_softc_t *)upipe->pipe.device->bus; - DPRINTF(("uhci_timeout: ii=%p\n", ii)); + DPRINTF(("uhci_timeout: uxfer=%p\n", uxfer)); -#ifdef UHCI_DEBUG - if (uhcidebug > 10) - uhci_dump_tds(ii->stdstart); -#endif + if (sc->sc_dying) { + uhci_abort_xfer(&uxfer->xfer, USBD_TIMEOUT); + return; + } - ii->xfer->device->bus->intr_context++; - uhci_abort_xfer(ii->xfer, USBD_TIMEOUT); - ii->xfer->device->bus->intr_context--; + /* Execute the abort in a process context. */ + usb_init_task(&uxfer->abort_task, uhci_timeout_task, ii->xfer); + usb_add_task(uxfer->xfer.pipe->device, &uxfer->abort_task); +} + +void +uhci_timeout_task(void *addr) +{ + usbd_xfer_handle xfer = addr; + int s; + + DPRINTF(("uhci_timeout_task: xfer=%p\n", xfer)); + + s = splusb(); + uhci_abort_xfer(xfer, USBD_TIMEOUT); + splx(s); } /* @@ -1502,7 +1523,7 @@ uhci_waitintr(uhci_softc_t *sc, usbd_xfer_handle xfer) /* Timeout */ DPRINTF(("uhci_waitintr: timeout\n")); for (ii = LIST_FIRST(&sc->sc_intrhead); - ii != NULL && ii->xfer != xfer; + ii != NULL && ii->xfer != xfer; ii = LIST_NEXT(ii, list)) ; #ifdef DIAGNOSTIC @@ -1528,11 +1549,11 @@ uhci_reset(uhci_softc_t *sc) UHCICMD(sc, UHCI_CMD_HCRESET); /* The reset bit goes low when the controller is done. */ - for (n = 0; n < UHCI_RESET_TIMEOUT && + for (n = 0; n < UHCI_RESET_TIMEOUT && (UREAD2(sc, UHCI_CMD) & UHCI_CMD_HCRESET); n++) usb_delay_ms(&sc->sc_bus, 1); if (n >= UHCI_RESET_TIMEOUT) - printf("%s: controller did not reset\n", + printf("%s: controller did not reset\n", USBDEVNAME(sc->sc_bus.bdev)); } @@ -1680,9 +1701,9 @@ uhci_alloc_std_chain(struct uhci_pipe *upipe, uhci_softc_t *sc, int len, int addr = upipe->pipe.device->address; int endpt = upipe->pipe.endpoint->edesc->bEndpointAddress; - DPRINTFN(8, ("uhci_alloc_std_chain: addr=%d endpt=%d len=%d ls=%d " - "flags=0x%x\n", addr, UE_GET_ADDR(endpt), len, - upipe->pipe.device->lowspeed, flags)); + DPRINTFN(8, ("uhci_alloc_std_chain: addr=%d endpt=%d len=%d speed=%d " + "flags=0x%x\n", addr, UE_GET_ADDR(endpt), len, + upipe->pipe.device->speed, flags)); maxp = UGETW(upipe->pipe.endpoint->edesc->wMaxPacketSize); if (maxp == 0) { printf("uhci_alloc_std_chain: maxp=0\n"); @@ -1705,14 +1726,14 @@ uhci_alloc_std_chain(struct uhci_pipe *upipe, uhci_softc_t *sc, int len, lastlink = UHCI_PTR_T; ntd--; status = UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(3) | UHCI_TD_ACTIVE); - if (upipe->pipe.device->lowspeed) + if (upipe->pipe.device->speed == USB_SPEED_LOW) status |= UHCI_TD_LS; if (flags & USBD_SHORT_XFER_OK) status |= UHCI_TD_SPD; for (i = ntd; i >= 0; i--) { p = uhci_alloc_std(sc); if (p == NULL) { - uhci_free_std_chain(sc, lastp, 0); + uhci_free_std_chain(sc, lastp, NULL); return (USBD_NOMEM); } p->link.std = lastp; @@ -1728,14 +1749,14 @@ uhci_alloc_std_chain(struct uhci_pipe *upipe, uhci_softc_t *sc, int len, *ep = p; } else l = maxp; - p->td.td_token = + p->td.td_token = htole32(rd ? UHCI_TD_IN (l, endpt, addr, tog) : UHCI_TD_OUT(l, endpt, addr, tog)); p->td.td_buffer = htole32(DMAADDR(dma) + i * maxp); tog ^= 1; } *sp = lastp; - DPRINTFN(10, ("uhci_alloc_std_chain: nexttog=%d\n", + DPRINTFN(10, ("uhci_alloc_std_chain: nexttog=%d\n", upipe->nexttoggle)); return (USBD_NORMAL_COMPLETION); } @@ -1762,7 +1783,7 @@ uhci_device_bulk_transfer(usbd_xfer_handle xfer) if (err) return (err); - /* + /* * Pipe isn't running (otherwise err would be USBD_INPROG), * so start it first. */ @@ -1861,44 +1882,68 @@ uhci_device_bulk_abort(usbd_xfer_handle xfer) } /* - * XXX This way of aborting is neither safe, nor good. - * But it will have to do until I figure out what to do. - * I apologize for the delay(). + * Abort a device request. + * If this routine is called at splusb() it guarantees that the request + * will be removed from the hardware scheduling and that the callback + * for it will be called with USBD_CANCELLED status. + * It's impossible to guarantee that the requested transfer will not + * have happened since the hardware runs concurrently. + * If the transaction has already happened we rely on the ordinary + * interrupt processing to process it. */ void uhci_abort_xfer(usbd_xfer_handle xfer, usbd_status status) { uhci_intr_info_t *ii = &UXFER(xfer)->iinfo; + struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe; + uhci_softc_t *sc = (uhci_softc_t *)upipe->pipe.device->bus; uhci_soft_td_t *std; int s; DPRINTFN(1,("uhci_abort_xfer: xfer=%p, status=%d\n", xfer, status)); - s = splusb(); - - /* Transfer is already done. */ - if (xfer->status != USBD_NOT_STARTED && - xfer->status != USBD_IN_PROGRESS) { + if (sc->sc_dying) { + /* If we're dying, just do the software part. */ + s = splusb(); + xfer->status = status; /* make software ignore it */ + usb_uncallout(xfer->timeout_handle, uhci_timeout, xfer); + usb_transfer_complete(xfer); splx(s); - return; } - /* Make interrupt routine ignore it, */ - xfer->status = status; + if (xfer->device->bus->intr_context || !curproc) + panic("uhci_abort_xfer: not in process context\n"); - /* don't timeout, */ + /* + * Step 1: Make interrupt routine and hardware ignore xfer. + */ + s = splusb(); + xfer->status = status; /* make software ignore it */ usb_uncallout(xfer->timeout_handle, uhci_timeout, ii); - - /* make hardware ignore it, */ + DPRINTFN(1,("uhci_abort_xfer: stop ii=%p\n", ii)); for (std = ii->stdstart; std != NULL; std = std->link.std) std->td.td_status &= htole32(~(UHCI_TD_ACTIVE | UHCI_TD_IOC)); - - xfer->hcpriv = ii; - splx(s); - delay(1000); + /* + * Step 2: Wait until we know hardware has finished any possible + * use of the xfer. Also make sure the soft interrupt routine + * has run. + */ + usb_delay_ms(upipe->pipe.device->bus, 2); /* Hardware finishes in 1ms */ + s = splusb(); + sc->sc_softwake = 1; + usb_schedsoftintr(&sc->sc_bus); + DPRINTFN(1,("uhci_abort_xfer: tsleep\n")); + tsleep(&sc->sc_softwake, PZERO, "uhciab", 0); + splx(s); + + /* + * Step 3: Execute callback. + */ + xfer->hcpriv = ii; + DPRINTFN(1,("uhci_abort_xfer: callback\n")); s = splusb(); #ifdef DIAGNOSTIC ii->isdone = 1; @@ -1928,7 +1973,7 @@ uhci_device_ctrl_transfer(usbd_xfer_handle xfer) if (err) return (err); - /* + /* * Pipe isn't running (otherwise err would be USBD_INPROG), * so start it first. */ @@ -1968,7 +2013,7 @@ uhci_device_intr_transfer(usbd_xfer_handle xfer) if (err) return (err); - /* + /* * Pipe isn't running (otherwise err would be USBD_INPROG), * so start it first. */ @@ -2024,7 +2069,7 @@ uhci_device_intr_start(usbd_xfer_handle xfer) ii->isdone = 0; #endif - DPRINTFN(10,("uhci_device_intr_transfer: qhs[0]=%p\n", + DPRINTFN(10,("uhci_device_intr_transfer: qhs[0]=%p\n", upipe->u.intr.qhs[0])); for (i = 0; i < upipe->u.intr.npoll; i++) { sqh = upipe->u.intr.qhs[i]; @@ -2067,7 +2112,7 @@ uhci_device_intr_abort(usbd_xfer_handle xfer) DPRINTFN(1,("uhci_device_intr_abort: xfer=%p\n", xfer)); if (xfer->pipe->intrxfer == xfer) { DPRINTFN(1,("uhci_device_intr_abort: remove\n")); - xfer->pipe->intrxfer = 0; + xfer->pipe->intrxfer = NULL; } uhci_abort_xfer(xfer, USBD_CANCELLED); } @@ -2088,7 +2133,7 @@ uhci_device_intr_close(usbd_pipe_handle pipe) uhci_remove_intr(sc, upipe->u.intr.qhs[i]); splx(s); - /* + /* * We now have to wait for any activity on the physical * descriptors to stop. */ @@ -2125,7 +2170,7 @@ uhci_device_request(usbd_xfer_handle xfer) UGETW(req->wIndex), UGETW(req->wLength), addr, endpt)); - ls = dev->lowspeed ? UHCI_TD_LS : 0; + ls = dev->speed == USB_SPEED_LOW ? UHCI_TD_LS : 0; isread = req->bmRequestType & UT_READ; len = UGETW(req->wLength); @@ -2159,9 +2204,9 @@ uhci_device_request(usbd_xfer_handle xfer) stat->link.std = NULL; stat->td.td_link = htole32(UHCI_PTR_T); - stat->td.td_status = htole32(UHCI_TD_SET_ERRCNT(3) | ls | + stat->td.td_status = htole32(UHCI_TD_SET_ERRCNT(3) | ls | UHCI_TD_ACTIVE | UHCI_TD_IOC); - stat->td.td_token = + stat->td.td_token = htole32(isread ? UHCI_TD_OUT(0, endpt, addr, 1) : UHCI_TD_IN (0, endpt, addr, 1)); stat->td.td_buffer = htole32(0); @@ -2188,7 +2233,7 @@ uhci_device_request(usbd_xfer_handle xfer) sqh->qh.qh_elink = htole32(setup->physaddr | UHCI_PTR_TD); s = splusb(); - if (dev->lowspeed) + if (dev->speed == USB_SPEED_LOW) uhci_add_ls_ctrl(sc, sqh); else uhci_add_hs_ctrl(sc, sqh); @@ -2211,7 +2256,7 @@ uhci_device_request(usbd_xfer_handle xfer) uhci_dump_qh(sxqh); for (xqh = sxqh; xqh != NULL; - xqh = (maxqh++ == 5 || xqh->hlink == sxqh || + xqh = (maxqh++ == 5 || xqh->hlink == sxqh || xqh->hlink == xqh ? NULL : xqh->hlink)) { uhci_dump_qh(xqh); } @@ -2263,7 +2308,7 @@ uhci_device_isoc_enter(usbd_xfer_handle xfer) usbd_device_handle dev = upipe->pipe.device; uhci_softc_t *sc = (uhci_softc_t *)dev->bus; struct iso *iso = &upipe->u.iso; - uhci_soft_td_t *std; + uhci_soft_td_t *std; u_int32_t buf, len, status; int s, i, next, nframes; @@ -2359,7 +2404,7 @@ uhci_device_isoc_start(usbd_xfer_handle xfer) #endif s = splusb(); - + /* Set up interrupt info. */ ii->xfer = xfer; ii->stdstart = end; @@ -2370,7 +2415,7 @@ uhci_device_isoc_start(usbd_xfer_handle xfer) ii->isdone = 0; #endif uhci_add_intr_info(sc, ii); - + splx(s); return (USBD_IN_PROGRESS); @@ -2387,7 +2432,7 @@ uhci_device_isoc_abort(usbd_xfer_handle xfer) s = splusb(); /* Transfer is already done. */ - if (xfer->status != USBD_NOT_STARTED && + if (xfer->status != USBD_NOT_STARTED && xfer->status != USBD_IN_PROGRESS) { splx(s); return; @@ -2571,7 +2616,7 @@ uhci_device_intr_done(usbd_xfer_handle xfer) sqh->elink = NULL; sqh->qh.qh_elink = htole32(UHCI_PTR_T); } - uhci_free_std_chain(sc, ii->stdstart, 0); + uhci_free_std_chain(sc, ii->stdstart, NULL); /* XXX Wasteful. */ if (xfer->pipe->repeat) { @@ -2628,7 +2673,7 @@ uhci_device_ctrl_done(usbd_xfer_handle xfer) uhci_del_intr_info(ii); /* remove from active list */ - if (upipe->pipe.device->lowspeed) + if (upipe->pipe.device->speed == USB_SPEED_LOW) uhci_remove_ls_ctrl(sc, upipe->u.ctl.sqh); else uhci_remove_hs_ctrl(sc, upipe->u.ctl.sqh); @@ -2651,7 +2696,7 @@ uhci_device_bulk_done(usbd_xfer_handle xfer) uhci_remove_bulk(sc, upipe->u.bulk.sqh); - uhci_free_std_chain(sc, ii->stdstart, 0); + uhci_free_std_chain(sc, ii->stdstart, NULL); DPRINTFN(5, ("uhci_bulk_done: length=%d\n", xfer->actlen)); } @@ -2717,10 +2762,10 @@ uhci_device_setintr(uhci_softc_t *sc, struct uhci_pipe *upipe, int ival) DPRINTFN(2, ("uhci_setintr: ival=%d npoll=%d\n", ival, npoll)); upipe->u.intr.npoll = npoll; - upipe->u.intr.qhs = + upipe->u.intr.qhs = malloc(npoll * sizeof(uhci_soft_qh_t *), M_USBHC, M_WAITOK); - /* + /* * Figure out which offset in the schedule that has most * bandwidth left over. */ @@ -2764,7 +2809,7 @@ uhci_open(usbd_pipe_handle pipe) int ival; DPRINTFN(1, ("uhci_open: pipe=%p, addr=%d, endpt=%d (%d)\n", - pipe, pipe->device->address, + pipe, pipe->device->address, ed->bEndpointAddress, sc->sc_addr)); upipe->aborting = 0; @@ -2799,8 +2844,8 @@ uhci_open(usbd_pipe_handle pipe) uhci_free_std(sc, upipe->u.ctl.setup); goto bad; } - err = usb_allocmem(&sc->sc_bus, - sizeof(usb_device_request_t), + err = usb_allocmem(&sc->sc_bus, + sizeof(usb_device_request_t), 0, &upipe->u.ctl.reqdma); if (err) { uhci_free_sqh(sc, upipe->u.ctl.sqh); @@ -2841,7 +2886,7 @@ usb_device_descriptor_t uhci_devd = { {0x00, 0x01}, /* USB version */ UDCLASS_HUB, /* class */ UDSUBCLASS_HUB, /* subclass */ - 0, /* protocol */ + UDPROTO_FSHUB, /* protocol */ 64, /* max packet */ {0},{0},{0x00,0x01}, /* device id */ 1,2,0, /* string indicies */ @@ -2869,7 +2914,7 @@ usb_interface_descriptor_t uhci_ifcd = { 1, UICLASS_HUB, UISUBCLASS_HUB, - 0, + UIPROTO_FSHUB, 0 }; @@ -2922,7 +2967,7 @@ uhci_root_ctrl_transfer(usbd_xfer_handle xfer) if (err) return (err); - /* + /* * Pipe isn't running (otherwise err would be USBD_INPROG), * so start it first. */ @@ -2949,7 +2994,7 @@ uhci_root_ctrl_start(usbd_xfer_handle xfer) #endif req = &xfer->request; - DPRINTFN(2,("uhci_root_ctrl_control type=0x%02x request=%02x\n", + DPRINTFN(2,("uhci_root_ctrl_control type=0x%02x request=%02x\n", req->bmRequestType, req->bRequest)); len = UGETW(req->wLength); @@ -2964,7 +3009,7 @@ uhci_root_ctrl_start(usbd_xfer_handle xfer) 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. */ @@ -3132,7 +3177,7 @@ uhci_root_ctrl_start(usbd_xfer_handle xfer) goto ret; } if (len > 0) { - *(u_int8_t *)buf = + *(u_int8_t *)buf = (UREAD2(sc, port) & UHCI_PORTSC_LS) >> UHCI_PORTSC_LS_SHIFT; totlen = 1; @@ -3172,19 +3217,19 @@ uhci_root_ctrl_start(usbd_xfer_handle xfer) status = change = 0; if (x & UHCI_PORTSC_CCS) status |= UPS_CURRENT_CONNECT_STATUS; - if (x & UHCI_PORTSC_CSC) + if (x & UHCI_PORTSC_CSC) change |= UPS_C_CONNECT_STATUS; - if (x & UHCI_PORTSC_PE) + if (x & UHCI_PORTSC_PE) status |= UPS_PORT_ENABLED; - if (x & UHCI_PORTSC_POEDC) + if (x & UHCI_PORTSC_POEDC) change |= UPS_C_PORT_ENABLED; - if (x & UHCI_PORTSC_OCI) + if (x & UHCI_PORTSC_OCI) status |= UPS_OVERCURRENT_INDICATOR; - if (x & UHCI_PORTSC_OCIC) + if (x & UHCI_PORTSC_OCIC) change |= UPS_C_OVERCURRENT_INDICATOR; - if (x & UHCI_PORTSC_SUSP) + if (x & UHCI_PORTSC_SUSP) status |= UPS_SUSPEND; - if (x & UHCI_PORTSC_LSDA) + if (x & UHCI_PORTSC_LSDA) status |= UPS_LOW_SPEED; status |= UPS_PORT_POWER; if (sc->sc_isreset) @@ -3221,7 +3266,7 @@ uhci_root_ctrl_start(usbd_xfer_handle xfer) case UHF_PORT_RESET: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_PR); - usb_delay_ms(&sc->sc_bus, 50); /*XXX USB v1.1 7.1.7.3 */ + usb_delay_ms(&sc->sc_bus, USB_PORT_ROOT_RESET_DELAY); UWRITE2(sc, port, x & ~UHCI_PORTSC_PR); delay(100); x = UREAD2(sc, port); diff --git a/sys/dev/usb/uhcireg.h b/sys/dev/usb/uhcireg.h index 4f71a231ba7..c0d73cedc4c 100644 --- a/sys/dev/usb/uhcireg.h +++ b/sys/dev/usb/uhcireg.h @@ -1,5 +1,5 @@ -/* $OpenBSD: uhcireg.h,v 1.9 2001/10/31 04:24:44 nate Exp $ */ -/* $NetBSD: uhcireg.h,v 1.14 2001/08/06 15:15:08 augustss Exp $ */ +/* $OpenBSD: uhcireg.h,v 1.10 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: uhcireg.h,v 1.15 2002/02/11 11:41:30 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/uhcireg.h,v 1.12 1999/11/17 22:33:42 n_hibma Exp $ */ /* @@ -76,6 +76,7 @@ #define UHCI_STS_HSE 0x0008 #define UHCI_STS_HCPE 0x0010 #define UHCI_STS_HCH 0x0020 +#define UHCI_STS_ALLINTRS 0x003f #define UHCI_INTR 0x04 #define UHCI_INTR_TOCRCIE 0x0001 diff --git a/sys/dev/usb/uhcivar.h b/sys/dev/usb/uhcivar.h index ef076c26dba..b8fdfeea482 100644 --- a/sys/dev/usb/uhcivar.h +++ b/sys/dev/usb/uhcivar.h @@ -1,5 +1,5 @@ -/* $OpenBSD: uhcivar.h,v 1.11 2001/06/12 19:11:59 mickey Exp $ */ -/* $NetBSD: uhcivar.h,v 1.32 2000/08/13 16:18:09 augustss Exp $ */ +/* $OpenBSD: uhcivar.h,v 1.12 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: uhcivar.h,v 1.33 2002/02/11 11:41:30 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/uhcivar.h,v 1.14 1999/11/17 22:33:42 n_hibma Exp $ */ /* @@ -84,6 +84,7 @@ typedef struct uhci_intr_info { struct uhci_xfer { struct usbd_xfer xfer; uhci_intr_info_t iinfo; + struct usb_task abort_task; int curframe; }; @@ -161,6 +162,7 @@ typedef struct uhci_softc { u_int8_t sc_saved_sof; u_int16_t sc_saved_frnum; + char sc_softwake; char sc_isreset; char sc_suspend; char sc_dying; diff --git a/sys/dev/usb/uhid.c b/sys/dev/usb/uhid.c index fca9cd5d787..48fb9f7d21d 100644 --- a/sys/dev/usb/uhid.c +++ b/sys/dev/usb/uhid.c @@ -1,6 +1,5 @@ -/* $OpenBSD: uhid.c,v 1.16 2002/05/02 20:08:04 nate Exp $ */ -/* $NetBSD: uhid.c,v 1.45 2001/10/26 17:58:21 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/uhid.c,v 1.22 1999/11/17 22:33:43 n_hibma Exp $ */ +/* $OpenBSD: uhid.c,v 1.17 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: uhid.c,v 1.51 2002/03/17 18:02:53 augustss Exp $ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -48,16 +47,8 @@ #include <sys/kernel.h> #include <sys/malloc.h> #include <sys/signalvar.h> -#if defined(__NetBSD__) || defined(__OpenBSD__) #include <sys/device.h> #include <sys/ioctl.h> -#elif defined(__FreeBSD__) -#include <sys/ioccom.h> -#include <sys/filio.h> -#include <sys/module.h> -#include <sys/bus.h> -#include <sys/ioccom.h> -#endif #include <sys/conf.h> #include <sys/tty.h> #include <sys/file.h> @@ -75,8 +66,7 @@ #include <dev/usb/hid.h> #include <dev/usb/usb_quirks.h> -/* Report descriptor for broken Wacom Graphire */ -#include <dev/usb/ugraphire_rdesc.h> +#include <dev/usb/uhidev.h> #ifdef UHID_DEBUG #define DPRINTF(x) if (uhiddebug) logprintf x @@ -88,33 +78,20 @@ int uhiddebug = 0; #endif struct uhid_softc { - USBBASEDEVICE sc_dev; /* base device */ - usbd_device_handle sc_udev; - usbd_interface_handle sc_iface; /* interface */ - usbd_pipe_handle sc_intrpipe; /* interrupt pipe */ - int sc_ep_addr; + struct uhidev sc_hdev; int sc_isize; int sc_osize; int sc_fsize; - u_int8_t sc_iid; - u_int8_t sc_oid; - u_int8_t sc_fid; - u_char *sc_ibuf; u_char *sc_obuf; - void *sc_repdesc; - int sc_repdesc_size; - struct clist sc_q; struct selinfo sc_rsel; - struct proc *sc_async; /* process that wants SIGIO */ + usb_proc_ptr sc_async; /* process that wants SIGIO */ u_char sc_state; /* driver state */ -#define UHID_OPEN 0x01 /* device is open */ -#define UHID_ASLP 0x02 /* waiting for device data */ -#define UHID_NEEDCLEAR 0x04 /* needs clearing endpoint stall */ -#define UHID_IMMED 0x08 /* return read data immediately */ +#define UHID_ASLP 0x01 /* waiting for device data */ +#define UHID_IMMED 0x02 /* return read data immediately */ int sc_refcnt; u_char sc_dying; @@ -124,153 +101,52 @@ struct uhid_softc { #define UHID_CHUNK 128 /* chunk size for read */ #define UHID_BSIZE 1020 /* buffer size */ -#if defined(__NetBSD__) || defined(__OpenBSD__) cdev_decl(uhid); -#elif defined(__FreeBSD__) -d_open_t uhidopen; -d_close_t uhidclose; -d_read_t uhidread; -d_write_t uhidwrite; -d_ioctl_t uhidioctl; -d_poll_t uhidpoll; - -#define UHID_CDEV_MAJOR 122 - -Static struct cdevsw uhid_cdevsw = { - /* open */ uhidopen, - /* close */ uhidclose, - /* read */ uhidread, - /* write */ uhidwrite, - /* ioctl */ uhidioctl, - /* poll */ uhidpoll, - /* mmap */ nommap, - /* strategy */ nostrategy, - /* name */ "uhid", - /* maj */ UHID_CDEV_MAJOR, - /* dump */ nodump, - /* psize */ nopsize, - /* flags */ 0, - /* bmaj */ -1 -}; -#endif -Static void uhid_intr(usbd_xfer_handle, usbd_private_handle, usbd_status); +Static void uhid_intr(struct uhidev *, void *, u_int len); Static int uhid_do_read(struct uhid_softc *, struct uio *uio, int); Static int uhid_do_write(struct uhid_softc *, struct uio *uio, int); -Static int uhid_do_ioctl(struct uhid_softc*, u_long, caddr_t, int,struct proc*); +Static int uhid_do_ioctl(struct uhid_softc*, u_long, caddr_t, int, + usb_proc_ptr); USB_DECLARE_DRIVER(uhid); USB_MATCH(uhid) { USB_MATCH_START(uhid, uaa); - usb_interface_descriptor_t *id; - - if (uaa->iface == NULL) - return (UMATCH_NONE); - id = usbd_get_interface_descriptor(uaa->iface); - if (id == NULL || id->bInterfaceClass != UICLASS_HID) - return (UMATCH_NONE); - if (uaa->matchlvl) - return (uaa->matchlvl); + struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa; + + DPRINTF(("uhid_match: report=%d\n", uha->reportid)); + + if (uha->matchlvl) + return (uha->matchlvl); return (UMATCH_IFACECLASS_GENERIC); } USB_ATTACH(uhid) { USB_ATTACH_START(uhid, sc, uaa); - usbd_interface_handle iface = uaa->iface; - usb_interface_descriptor_t *id; - usb_endpoint_descriptor_t *ed; - int size; + struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa; + int size, repid; void *desc; - usbd_status err; - char devinfo[1024]; - - sc->sc_udev = uaa->device; - sc->sc_iface = iface; - id = usbd_get_interface_descriptor(iface); - usbd_devinfo(uaa->device, 0, devinfo); - USB_ATTACH_SETUP; - printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev), - devinfo, id->bInterfaceClass, id->bInterfaceSubClass); - - ed = usbd_interface2endpoint_descriptor(iface, 0); - if (ed == NULL) { - printf("%s: could not read endpoint descriptor\n", - USBDEVNAME(sc->sc_dev)); - sc->sc_dying = 1; - USB_ATTACH_ERROR_RETURN; - } - - DPRINTFN(10,("uhid_attach: bLength=%d bDescriptorType=%d " - "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d" - " bInterval=%d\n", - ed->bLength, ed->bDescriptorType, - ed->bEndpointAddress & UE_ADDR, - UE_GET_DIR(ed->bEndpointAddress)==UE_DIR_IN? "in" : "out", - ed->bmAttributes & UE_XFERTYPE, - UGETW(ed->wMaxPacketSize), ed->bInterval)); - - if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN || - (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) { - printf("%s: unexpected endpoint\n", USBDEVNAME(sc->sc_dev)); - sc->sc_dying = 1; - USB_ATTACH_ERROR_RETURN; - } - - sc->sc_ep_addr = ed->bEndpointAddress; - - if (uaa->vendor == USB_VENDOR_WACOM && - uaa->product == USB_PRODUCT_WACOM_GRAPHIRE /* && - uaa->revision == 0x???? */) { /* XXX should use revision */ - /* The report descriptor for the Wacom Graphire is broken. */ - size = sizeof uhid_graphire_report_descr; - desc = malloc(size, M_USBDEV, M_NOWAIT); - if (desc == NULL) - err = USBD_NOMEM; - else { - err = USBD_NORMAL_COMPLETION; - memcpy(desc, uhid_graphire_report_descr, size); - } - } else { - desc = NULL; - err = usbd_read_report_desc(uaa->iface, &desc, &size,M_USBDEV); - } - if (err) { - printf("%s: no report descriptor\n", USBDEVNAME(sc->sc_dev)); - sc->sc_dying = 1; - USB_ATTACH_ERROR_RETURN; - } - (void)usbd_set_idle(iface, 0, 0); - - sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid); - sc->sc_osize = hid_report_size(desc, size, hid_output, &sc->sc_oid); - sc->sc_fsize = hid_report_size(desc, size, hid_feature, &sc->sc_fid); + sc->sc_hdev.sc_intr = uhid_intr; + sc->sc_hdev.sc_parent = uha->parent; + sc->sc_hdev.sc_report_id = uha->reportid; - sc->sc_repdesc = desc; - sc->sc_repdesc_size = size; + uhidev_get_report_desc(uha->parent, &desc, &size); + repid = uha->reportid; + sc->sc_isize = hid_report_size(desc, size, hid_input, repid); + sc->sc_osize = hid_report_size(desc, size, hid_output, repid); + sc->sc_fsize = hid_report_size(desc, size, hid_feature, repid); -#ifdef __FreeBSD__ - { - static int global_init_done = 0; - - if (!global_init_done) { - cdevsw_add(&uhid_cdevsw); - global_init_done = 1; - } - } -#endif - - usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, - USBDEV(sc->sc_dev)); + printf(": input=%d, output=%d, feature=%d\n", + sc->sc_isize, sc->sc_osize, sc->sc_fsize); USB_ATTACH_SUCCESS_RETURN; } -#if defined(__NetBSD__) || defined(__OpenBSD__) int uhid_activate(device_ptr_t self, enum devact act) { @@ -287,36 +163,28 @@ uhid_activate(device_ptr_t self, enum devact act) } return (0); } -#endif USB_DETACH(uhid) { USB_DETACH_START(uhid, sc); int s; -#if defined(__NetBSD__) || defined(__OpenBSD__) int maj, mn; DPRINTF(("uhid_detach: sc=%p flags=%d\n", sc, flags)); -#else - DPRINTF(("uhid_detach: sc=%p\n", sc)); -#endif sc->sc_dying = 1; - if (sc->sc_intrpipe != NULL) - usbd_abort_pipe(sc->sc_intrpipe); - if (sc->sc_state & UHID_OPEN) { + if (sc->sc_hdev.sc_state & UHIDEV_OPEN) { s = splusb(); if (--sc->sc_refcnt >= 0) { /* Wake everyone */ wakeup(&sc->sc_q); /* Wait for processes to go away. */ - usb_detach_wait(USBDEV(sc->sc_dev)); + usb_detach_wait(USBDEV(sc->sc_hdev.sc_dev)); } splx(s); } -#if defined(__NetBSD__) || defined(__OpenBSD__) /* locate the major number */ for (maj = 0; maj < nchrdev; maj++) if (cdevsw[maj].d_open == uhidopen) @@ -325,47 +193,33 @@ USB_DETACH(uhid) /* Nuke the vnodes for any open instances (calls close). */ mn = self->dv_unit; vdevgone(maj, mn, mn, VCHR); -#elif defined(__FreeBSD__) - /* XXX not implemented yet */ -#endif - if (sc->sc_repdesc) - free(sc->sc_repdesc, M_USBDEV); - - usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, - USBDEV(sc->sc_dev)); +#if 0 + usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, + sc->sc_hdev.sc_parent->sc_udev, + USBDEV(sc->sc_hdev.sc_dev)); +#endif return (0); } void -uhid_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) +uhid_intr(struct uhidev *addr, void *data, u_int len) { - struct uhid_softc *sc = addr; + struct uhid_softc *sc = (struct uhid_softc *)addr; #ifdef UHID_DEBUG if (uhiddebug > 5) { - u_int32_t cc, i; + u_int32_t i; - usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL); - DPRINTF(("uhid_intr: status=%d cc=%d\n", status, cc)); DPRINTF(("uhid_intr: data =")); - for (i = 0; i < cc; i++) - DPRINTF((" %02x", sc->sc_ibuf[i])); + for (i = 0; i < len; i++) + DPRINTF((" %02x", ((u_char *)data)[i])); DPRINTF(("\n")); } #endif - if (status == USBD_CANCELLED) - return; - - if (status != USBD_NORMAL_COMPLETION) { - DPRINTF(("uhid_intr: status=%d\n", status)); - sc->sc_state |= UHID_NEEDCLEAR; - return; - } - - (void) b_to_q(sc->sc_ibuf, sc->sc_isize, &sc->sc_q); + (void)b_to_q(data, len, &sc->sc_q); if (sc->sc_state & UHID_ASLP) { sc->sc_state &= ~UHID_ASLP; @@ -380,10 +234,10 @@ uhid_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) } int -uhidopen(dev_t dev, int flag, int mode, struct proc *p) +uhidopen(dev_t dev, int flag, int mode, usb_proc_ptr p) { struct uhid_softc *sc; - usbd_status err; + int error; USB_GET_SC_OPEN(uhid, UHIDUNIT(dev), sc); @@ -392,40 +246,23 @@ uhidopen(dev_t dev, int flag, int mode, struct proc *p) if (sc->sc_dying) return (ENXIO); - if (sc->sc_state & UHID_OPEN) - return (EBUSY); - sc->sc_state |= UHID_OPEN; + error = uhidev_open(&sc->sc_hdev); + if (error) + return (error); if (clalloc(&sc->sc_q, UHID_BSIZE, 0) == -1) { - sc->sc_state &= ~UHID_OPEN; + uhidev_close(&sc->sc_hdev); return (ENOMEM); } - - sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK); sc->sc_obuf = malloc(sc->sc_osize, M_USBDEV, M_WAITOK); - - /* Set up interrupt pipe. */ - err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr, - USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc, sc->sc_ibuf, - sc->sc_isize, uhid_intr, USBD_DEFAULT_INTERVAL); - if (err) { - DPRINTF(("uhidopen: usbd_open_pipe_intr failed, " - "error=%d\n",err)); - free(sc->sc_ibuf, M_USBDEV); - free(sc->sc_obuf, M_USBDEV); - sc->sc_state &= ~UHID_OPEN; - return (EIO); - } - sc->sc_state &= ~UHID_IMMED; - - sc->sc_async = 0; + sc->sc_async = NULL; return (0); } int -uhidclose(dev_t dev, int flag, int mode, struct proc *p) +uhidclose(dev_t dev, int flag, int mode, usb_proc_ptr p) { struct uhid_softc *sc; @@ -433,19 +270,10 @@ uhidclose(dev_t dev, int flag, int mode, struct proc *p) DPRINTF(("uhidclose: sc=%p\n", sc)); - /* Disable interrupts. */ - usbd_abort_pipe(sc->sc_intrpipe); - usbd_close_pipe(sc->sc_intrpipe); - sc->sc_intrpipe = 0; - clfree(&sc->sc_q); - - free(sc->sc_ibuf, M_USBDEV); free(sc->sc_obuf, M_USBDEV); - - sc->sc_state &= ~UHID_OPEN; - - sc->sc_async = 0; + sc->sc_async = NULL; + uhidev_close(&sc->sc_hdev); return (0); } @@ -455,6 +283,7 @@ uhid_do_read(struct uhid_softc *sc, struct uio *uio, int flag) { int s; int error = 0; + int extra; size_t length; u_char buffer[UHID_CHUNK]; usbd_status err; @@ -462,12 +291,12 @@ uhid_do_read(struct uhid_softc *sc, struct uio *uio, int flag) DPRINTFN(1, ("uhidread\n")); if (sc->sc_state & UHID_IMMED) { DPRINTFN(1, ("uhidread immed\n")); - - err = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT, - sc->sc_iid, buffer, sc->sc_isize); + extra = sc->sc_hdev.sc_report_id != 0; + err = uhidev_get_report(&sc->sc_hdev, UHID_INPUT_REPORT, + buffer, sc->sc_isize + extra); if (err) return (EIO); - return (uiomove(buffer, sc->sc_isize, uio)); + return (uiomove(buffer+extra, sc->sc_isize, uio)); } s = splusb(); @@ -486,11 +315,6 @@ uhid_do_read(struct uhid_softc *sc, struct uio *uio, int flag) sc->sc_state &= ~UHID_ASLP; break; } - if (sc->sc_state & UHID_NEEDCLEAR) { - DPRINTFN(-1,("uhidread: clearing stall\n")); - sc->sc_state &= ~UHID_NEEDCLEAR; - usbd_clear_endpoint_stall(sc->sc_intrpipe); - } } splx(s); @@ -523,7 +347,7 @@ uhidread(dev_t dev, struct uio *uio, int flag) sc->sc_refcnt++; error = uhid_do_read(sc, uio, flag); if (--sc->sc_refcnt < 0) - usb_detach_wakeup(USBDEV(sc->sc_dev)); + usb_detach_wakeup(USBDEV(sc->sc_hdev.sc_dev)); return (error); } @@ -545,12 +369,8 @@ uhid_do_write(struct uhid_softc *sc, struct uio *uio, int flag) return (EINVAL); error = uiomove(sc->sc_obuf, size, uio); if (!error) { - if (sc->sc_oid) - err = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT, - sc->sc_obuf[0], sc->sc_obuf+1, size-1); - else - err = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT, - 0, sc->sc_obuf, size); + err = uhidev_set_report(&sc->sc_hdev, UHID_OUTPUT_REPORT, + sc->sc_obuf, size); if (err) error = EIO; } @@ -569,18 +389,20 @@ uhidwrite(dev_t dev, struct uio *uio, int flag) sc->sc_refcnt++; error = uhid_do_write(sc, uio, flag); if (--sc->sc_refcnt < 0) - usb_detach_wakeup(USBDEV(sc->sc_dev)); + usb_detach_wakeup(USBDEV(sc->sc_hdev.sc_dev)); return (error); } int uhid_do_ioctl(struct uhid_softc *sc, u_long cmd, caddr_t addr, - int flag, struct proc *p) + int flag, usb_proc_ptr p) { struct usb_ctl_report_desc *rd; struct usb_ctl_report *re; - int size, id; + u_char buffer[UHID_CHUNK]; + int size, extra; usbd_status err; + void *desc; DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd)); @@ -611,17 +433,18 @@ uhid_do_ioctl(struct uhid_softc *sc, u_long cmd, caddr_t addr, break; case USB_GET_REPORT_DESC: + uhidev_get_report_desc(sc->sc_hdev.sc_parent, &desc, &size); rd = (struct usb_ctl_report_desc *)addr; - size = min(sc->sc_repdesc_size, sizeof rd->ucrd_data); + size = min(size, sizeof rd->ucrd_data); rd->ucrd_size = size; - memcpy(rd->ucrd_data, sc->sc_repdesc, size); + memcpy(rd->ucrd_data, desc, size); break; case USB_SET_IMMED: if (*(int *)addr) { - /* XXX should read into ibuf, but does it matter? */ - err = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT, - sc->sc_iid, sc->sc_ibuf, sc->sc_isize); + extra = sc->sc_hdev.sc_report_id != 0; + err = uhidev_get_report(&sc->sc_hdev, UHID_INPUT_REPORT, + buffer, sc->sc_isize + extra); if (err) return (EOPNOTSUPP); @@ -635,21 +458,21 @@ uhid_do_ioctl(struct uhid_softc *sc, u_long cmd, caddr_t addr, switch (re->ucr_report) { case UHID_INPUT_REPORT: size = sc->sc_isize; - id = sc->sc_iid; break; case UHID_OUTPUT_REPORT: size = sc->sc_osize; - id = sc->sc_oid; break; case UHID_FEATURE_REPORT: size = sc->sc_fsize; - id = sc->sc_fid; break; default: return (EINVAL); } - err = usbd_get_report(sc->sc_iface, re->ucr_report, id, - re->ucr_data, size); + extra = sc->sc_hdev.sc_report_id != 0; + err = uhidev_get_report(&sc->sc_hdev, re->ucr_report, + re->ucr_data, size + extra); + if (extra) + memcpy(re->ucr_data, re->ucr_data+1, size); if (err) return (EIO); break; @@ -659,26 +482,26 @@ uhid_do_ioctl(struct uhid_softc *sc, u_long cmd, caddr_t addr, switch (re->ucr_report) { case UHID_INPUT_REPORT: size = sc->sc_isize; - id = sc->sc_iid; break; case UHID_OUTPUT_REPORT: size = sc->sc_osize; - id = sc->sc_oid; break; case UHID_FEATURE_REPORT: size = sc->sc_fsize; - id = sc->sc_fid; break; default: return (EINVAL); } - err = usbd_set_report(sc->sc_iface, re->ucr_report, id, - re->ucr_data, - size); + err = uhidev_set_report(&sc->sc_hdev, re->ucr_report, + re->ucr_data, size); if (err) return (EIO); break; + case USB_GET_REPORT_ID: + *(int *)addr = sc->sc_hdev.sc_report_id; + break; + default: return (EINVAL); } @@ -686,7 +509,7 @@ uhid_do_ioctl(struct uhid_softc *sc, u_long cmd, caddr_t addr, } int -uhidioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) +uhidioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p) { struct uhid_softc *sc; int error; @@ -696,12 +519,12 @@ uhidioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) sc->sc_refcnt++; error = uhid_do_ioctl(sc, cmd, addr, flag, p); if (--sc->sc_refcnt < 0) - usb_detach_wakeup(USBDEV(sc->sc_dev)); + usb_detach_wakeup(USBDEV(sc->sc_hdev.sc_dev)); return (error); } int -uhidpoll(dev_t dev, int events, struct proc *p) +uhidpoll(dev_t dev, int events, usb_proc_ptr p) { struct uhid_softc *sc; int revents = 0; @@ -725,7 +548,3 @@ uhidpoll(dev_t dev, int events, struct proc *p) splx(s); return (revents); } - -#if defined(__FreeBSD__) -DRIVER_MODULE(uhid, uhub, uhid_driver, uhid_devclass, usbd_driver_load, 0); -#endif diff --git a/sys/dev/usb/uhidev.c b/sys/dev/usb/uhidev.c new file mode 100644 index 00000000000..263ab111bf1 --- /dev/null +++ b/sys/dev/usb/uhidev.c @@ -0,0 +1,513 @@ +/* $OpenBSD: uhidev.c,v 1.1 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: uhidev.c,v 1.5 2002/02/27 01:30:50 augustss Exp $ */ + +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net) at + * Carlstedt Research & Technology. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * HID spec: http://www.usb.org/developers/data/devclass/hid1_1.pdf + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/signalvar.h> +#include <sys/device.h> +#include <sys/ioctl.h> +#include <sys/conf.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbhid.h> + +#include <dev/usb/usbdevs.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/hid.h> +#include <dev/usb/usb_quirks.h> + +#include <dev/usb/uhidev.h> + +/* Report descriptor for broken Wacom Graphire */ +#include <dev/usb/ugraphire_rdesc.h> + +#ifdef UHIDEV_DEBUG +#define DPRINTF(x) if (uhidevdebug) logprintf x +#define DPRINTFN(n,x) if (uhidevdebug>(n)) logprintf x +int uhidevdebug = 0; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +Static void uhidev_intr(usbd_xfer_handle, usbd_private_handle, usbd_status); + +Static int uhidev_maxrepid(void *buf, int len); +Static int uhidevprint(void *aux, const char *pnp); +#if defined(__NetBSD__) +Static int uhidevsubmatch(struct device *parent, struct cfdata *cf, void *aux); +#else +Static int uhidevsubmatch(struct device *parent, void *cf, void *aux); +#endif + +USB_DECLARE_DRIVER(uhidev); + +USB_MATCH(uhidev) +{ + USB_MATCH_START(uhidev, uaa); + usb_interface_descriptor_t *id; + + if (uaa->iface == NULL) + return (UMATCH_NONE); + id = usbd_get_interface_descriptor(uaa->iface); + if (id == NULL || id->bInterfaceClass != UICLASS_HID) + return (UMATCH_NONE); + if (uaa->matchlvl) + return (uaa->matchlvl); + return (UMATCH_IFACECLASS_GENERIC); +} + +int repproto = 1; + +USB_ATTACH(uhidev) +{ + USB_ATTACH_START(uhidev, sc, uaa); + usbd_interface_handle iface = uaa->iface; + usb_interface_descriptor_t *id; + usb_endpoint_descriptor_t *ed; + struct uhidev_attach_arg uha; + struct uhidev *dev; + int size, nrepid, repid, repsz; + int repsizes[256]; + void *desc; + usbd_status err; + char devinfo[1024]; + + sc->sc_udev = uaa->device; + sc->sc_iface = iface; + id = usbd_get_interface_descriptor(iface); + usbd_devinfo(uaa->device, 0, devinfo); + USB_ATTACH_SETUP; + printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev), + devinfo, id->bInterfaceClass, id->bInterfaceSubClass); + + (void)usbd_set_idle(iface, 0, 0); +#if 0 + + qflags = usbd_get_quirks(sc->sc_udev)->uq_flags; + if ((qflags & UQ_NO_SET_PROTO) == 0 && + id->bInterfaceSubClass != UISUBCLASS_BOOT) + (void)usbd_set_protocol(iface, 1); +#endif + + ed = usbd_interface2endpoint_descriptor(iface, 0); + if (ed == NULL) { + printf("%s: could not read endpoint descriptor\n", + USBDEVNAME(sc->sc_dev)); + sc->sc_dying = 1; + USB_ATTACH_ERROR_RETURN; + } + + DPRINTFN(10,("uhidev_attach: bLength=%d bDescriptorType=%d " + "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d" + " bInterval=%d\n", + ed->bLength, ed->bDescriptorType, + ed->bEndpointAddress & UE_ADDR, + UE_GET_DIR(ed->bEndpointAddress)==UE_DIR_IN? "in" : "out", + ed->bmAttributes & UE_XFERTYPE, + UGETW(ed->wMaxPacketSize), ed->bInterval)); + + if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN || + (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) { + printf("%s: unexpected endpoint\n", USBDEVNAME(sc->sc_dev)); + sc->sc_dying = 1; + USB_ATTACH_ERROR_RETURN; + } + + sc->sc_ep_addr = ed->bEndpointAddress; + + /* XXX need to extend this */ + if (uaa->vendor == USB_VENDOR_WACOM && + uaa->product == USB_PRODUCT_WACOM_GRAPHIRE /* && + uaa->revision == 0x???? */) { /* XXX should use revision */ + /* The report descriptor for the Wacom Graphire is broken. */ + size = sizeof uhid_graphire_report_descr; + desc = malloc(size, M_USBDEV, M_NOWAIT); + if (desc == NULL) + err = USBD_NOMEM; + else { + err = USBD_NORMAL_COMPLETION; + memcpy(desc, uhid_graphire_report_descr, size); + } + } else { + desc = NULL; + err = usbd_read_report_desc(uaa->iface, &desc, &size, M_USBDEV); + } + if (err) { + printf("%s: no report descriptor\n", USBDEVNAME(sc->sc_dev)); + sc->sc_dying = 1; + USB_ATTACH_ERROR_RETURN; + } + + sc->sc_repdesc = desc; + sc->sc_repdesc_size = size; + + uha.uaa = uaa; + nrepid = uhidev_maxrepid(desc, size); + if (nrepid < 0) + USB_ATTACH_SUCCESS_RETURN; + if (nrepid > 0) + printf("%s: %d report ids\n", USBDEVNAME(sc->sc_dev), nrepid); + nrepid++; + sc->sc_subdevs = malloc(nrepid * sizeof(device_ptr_t), + M_USBDEV, M_NOWAIT); + bzero(sc->sc_subdevs, nrepid * sizeof(device_ptr_t)); + if (sc->sc_subdevs == NULL) { + printf("%s: no memory\n", USBDEVNAME(sc->sc_dev)); + USB_ATTACH_ERROR_RETURN; + } + sc->sc_nrepid = nrepid; + sc->sc_isize = 0; + + usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, + USBDEV(sc->sc_dev)); + + for (repid = 0; repid < nrepid; repid++) { + repsz = hid_report_size(desc, size, hid_input, repid); + DPRINTF(("uhidev_match: repid=%d, repsz=%d\n", repid, repsz)); + repsizes[repid] = repsz; + if (repsz > 0) { + if (repsz > sc->sc_isize) + sc->sc_isize = repsz; + } + } + sc->sc_isize += nrepid != 1; /* space for report ID */ + DPRINTF(("uhidev_attach: isize=%d\n", sc->sc_isize)); + + uha.parent = sc; + for (repid = 0; repid < nrepid; repid++) { + DPRINTF(("uhidev_match: try repid=%d\n", repid)); + if (hid_report_size(desc, size, hid_input, repid) == 0 && + hid_report_size(desc, size, hid_output, repid) == 0 && + hid_report_size(desc, size, hid_feature, repid) == 0) { + ; /* already NULL in sc->sc_subdevs[repid] */ + } else { + uha.reportid = repid; + dev = (struct uhidev *)config_found_sm(self, &uha, + uhidevprint, uhidevsubmatch); + sc->sc_subdevs[repid] = dev; + if (dev != NULL) { + dev->sc_in_rep_size = repsizes[repid]; +#ifdef DIAGNOSTIC + DPRINTF(("uhidev_match: repid=%d dev=%p\n", + repid, dev)); + if (dev->sc_intr == NULL) { + printf("%s: sc_intr == NULL\n", + USBDEVNAME(sc->sc_dev)); + USB_ATTACH_ERROR_RETURN; + } +#endif + } + } + } + + USB_ATTACH_SUCCESS_RETURN; +} + +int +uhidev_maxrepid(void *buf, int len) +{ + struct hid_data *d; + struct hid_item h; + int maxid; + + maxid = -1; + h.report_ID = 0; + for (d = hid_start_parse(buf, len, hid_none); hid_get_item(d, &h); ) + if (h.report_ID > maxid) + maxid = h.report_ID; + hid_end_parse(d); + return (maxid); +} + +int +uhidevprint(void *aux, const char *pnp) +{ + struct uhidev_attach_arg *uha = aux; + + if (pnp) + printf("uhid at %s", pnp); + if (uha->reportid != 0) + printf(" reportid %d", uha->reportid); + return (UNCONF); +} + +#if defined(__NetBSD__) +Static int uhidevsubmatch(struct device *parent, struct cfdata *cf, void *aux) +#else +Static int uhidevsubmatch(struct device *parent, void *match, void *aux) +#endif +{ + struct uhidev_attach_arg *uha = aux; +#if defined(__OpenBSD__) + struct cfdata *cf = match; +#endif + + if (cf->uhidevcf_reportid != UHIDEV_UNK_REPORTID && + cf->uhidevcf_reportid != uha->reportid) + return (0); + if (cf->uhidevcf_reportid == uha->reportid) + uha->matchlvl = UMATCH_VENDOR_PRODUCT; + else + uha->matchlvl = 0; + return ((*cf->cf_attach->ca_match)(parent, cf, aux)); +} + +int +uhidev_activate(device_ptr_t self, enum devact act) +{ + struct uhidev_softc *sc = (struct uhidev_softc *)self; + int i, rv; + + switch (act) { + case DVACT_ACTIVATE: + return (EOPNOTSUPP); + break; + + case DVACT_DEACTIVATE: + rv = 0; + for (i = 0; i < sc->sc_nrepid; i++) + if (sc->sc_subdevs[i] != NULL) + rv |= config_deactivate( + &sc->sc_subdevs[i]->sc_dev); + sc->sc_dying = 1; + break; + } + return (rv); +} + +USB_DETACH(uhidev) +{ + USB_DETACH_START(uhidev, sc); + int i, rv; + + DPRINTF(("uhidev_detach: sc=%p flags=%d\n", sc, flags)); + + sc->sc_dying = 1; + if (sc->sc_intrpipe != NULL) + usbd_abort_pipe(sc->sc_intrpipe); + + if (sc->sc_repdesc != NULL) + free(sc->sc_repdesc, M_USBDEV); + + rv = 0; + for (i = 0; i < sc->sc_nrepid; i++) { + if (sc->sc_subdevs[i] != NULL) { + rv |= config_detach(&sc->sc_subdevs[i]->sc_dev, flags); + sc->sc_subdevs[i] = NULL; + } + } + + usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, + USBDEV(sc->sc_dev)); + + return (rv); +} + +void +uhidev_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) +{ + struct uhidev_softc *sc = addr; + struct uhidev *scd; + u_char *p; + u_int rep; + u_int32_t cc; + + usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL); + +#ifdef UHIDEV_DEBUG + if (uhidevdebug > 5) { + u_int32_t i; + + DPRINTF(("uhidev_intr: status=%d cc=%d\n", status, cc)); + DPRINTF(("uhidev_intr: data =")); + for (i = 0; i < cc; i++) + DPRINTF((" %02x", sc->sc_ibuf[i])); + DPRINTF(("\n")); + } +#endif + + if (status == USBD_CANCELLED) + return; + + if (status != USBD_NORMAL_COMPLETION) { + DPRINTF(("%s: interrupt status=%d\n", USBDEVNAME(sc->sc_dev), + status)); + usbd_clear_endpoint_stall_async(sc->sc_intrpipe); + return; + } + + p = sc->sc_ibuf; + if (sc->sc_nrepid != 1) + rep = *p++, cc--; + else + rep = 0; + if (rep >= sc->sc_nrepid) { + printf("uhidev_intr: bad repid %d\n", rep); + return; + } + scd = sc->sc_subdevs[rep]; + DPRINTFN(5,("uhidev_intr: rep=%d, scd=%p state=0x%x\n", + rep, scd, scd ? scd->sc_state : 0)); + if (scd == NULL || !(scd->sc_state & UHIDEV_OPEN)) + return; +#ifdef DIAGNOSTIC + if (scd->sc_in_rep_size != cc) + printf("%s: bad input length %d != %d\n",USBDEVNAME(sc->sc_dev), + scd->sc_in_rep_size, cc); +#endif + scd->sc_intr(scd, p, cc); +} + +void +uhidev_get_report_desc(struct uhidev_softc *sc, void **desc, int *size) +{ + *desc = sc->sc_repdesc; + *size = sc->sc_repdesc_size; +} + +int +uhidev_open(struct uhidev *scd) +{ + struct uhidev_softc *sc = scd->sc_parent; + usbd_status err; + + DPRINTF(("uhidev_open: open pipe, state=%d refcnt=%d\n", + scd->sc_state, sc->sc_refcnt)); + + if (scd->sc_state & UHIDEV_OPEN) + return (EBUSY); + scd->sc_state |= UHIDEV_OPEN; + if (sc->sc_refcnt++) + return (0); + + if (sc->sc_isize == 0) + return (0); + + sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK); + + /* Set up interrupt pipe. */ + DPRINTF(("uhidev_open: isize=%d, ep=0x%02x\n", sc->sc_isize, + sc->sc_ep_addr)); + err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr, + USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc, sc->sc_ibuf, + sc->sc_isize, uhidev_intr, USBD_DEFAULT_INTERVAL); + if (err) { + DPRINTF(("uhidopen: usbd_open_pipe_intr failed, " + "error=%d\n",err)); + free(sc->sc_ibuf, M_USBDEV); + scd->sc_state &= ~UHIDEV_OPEN; + sc->sc_refcnt = 0; + sc->sc_intrpipe = NULL; + return (EIO); + } + return (0); +} + +void +uhidev_close(struct uhidev *scd) +{ + struct uhidev_softc *sc = scd->sc_parent; + + if (!(scd->sc_state & UHIDEV_OPEN)) + return; + scd->sc_state &= ~UHIDEV_OPEN; + if (--sc->sc_refcnt) + return; + DPRINTF(("uhidev_close: close pipe\n")); + + /* Disable interrupts. */ + if (sc->sc_intrpipe != NULL) { + usbd_abort_pipe(sc->sc_intrpipe); + usbd_close_pipe(sc->sc_intrpipe); + sc->sc_intrpipe = NULL; + } + + if (sc->sc_ibuf != NULL) { + free(sc->sc_ibuf, M_USBDEV); + sc->sc_ibuf = NULL; + } +} + +usbd_status +uhidev_set_report(struct uhidev *scd, int type, void *data, int len) +{ + /* XXX */ + char buf[100]; + if (scd->sc_report_id) { + buf[0] = scd->sc_report_id; + memcpy(buf+1, data, len); + len++; + data = buf; + } + + return usbd_set_report(scd->sc_parent->sc_iface, type, + scd->sc_report_id, data, len); +} + +void +uhidev_set_report_async(struct uhidev *scd, int type, void *data, int len) +{ + /* XXX */ + char buf[100]; + if (scd->sc_report_id) { + buf[0] = scd->sc_report_id; + memcpy(buf+1, data, len); + len++; + data = buf; + } + + usbd_set_report_async(scd->sc_parent->sc_iface, type, + scd->sc_report_id, data, len); +} + +usbd_status +uhidev_get_report(struct uhidev *scd, int type, void *data, int len) +{ + return usbd_get_report(scd->sc_parent->sc_iface, type, + scd->sc_report_id, data, len); +} diff --git a/sys/dev/usb/uhidev.h b/sys/dev/usb/uhidev.h new file mode 100644 index 00000000000..8d2700132cb --- /dev/null +++ b/sys/dev/usb/uhidev.h @@ -0,0 +1,91 @@ +/* $OpenBSD: uhidev.h,v 1.1 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: uhidev.h,v 1.2 2001/12/29 18:56:52 augustss Exp $ */ + +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net) at + * Carlstedt Research & Technology. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined(__NetBSD__) +#include "locators.h" +#endif + +#define uhidevcf_reportid cf_loc[UHIDBUSCF_REPORTID] +#define UHIDEV_UNK_REPORTID UHIDBUSCF_REPORTID_DEFAULT + +struct uhidev_softc { + USBBASEDEVICE sc_dev; /* base device */ + usbd_device_handle sc_udev; + usbd_interface_handle sc_iface; /* interface */ + usbd_pipe_handle sc_intrpipe; /* interrupt pipe */ + int sc_ep_addr; + + u_char *sc_ibuf; + u_int sc_isize; + + void *sc_repdesc; + int sc_repdesc_size; + + u_int sc_nrepid; + struct uhidev **sc_subdevs; + + int sc_refcnt; + u_char sc_dying; +}; + +struct uhidev { + USBBASEDEVICE sc_dev; /* base device */ + struct uhidev_softc *sc_parent; + uByte sc_report_id; + u_int8_t sc_state; + int sc_in_rep_size; +#define UHIDEV_OPEN 0x01 /* device is open */ + void (*sc_intr)(struct uhidev *, void *, u_int); +}; + +struct uhidev_attach_arg { + struct usb_attach_arg *uaa; + struct uhidev_softc *parent; + int reportid; + int reportsize; + int matchlvl; +}; + +void uhidev_get_report_desc(struct uhidev_softc *, void **, int *); +int uhidev_open(struct uhidev *); +void uhidev_close(struct uhidev *); +usbd_status uhidev_set_report(struct uhidev *scd, int type, void *data,int len); +void uhidev_set_report_async(struct uhidev *scd, int type, void *data, int len); +usbd_status uhidev_get_report(struct uhidev *scd, int type, void *data,int len); diff --git a/sys/dev/usb/uhub.c b/sys/dev/usb/uhub.c index a28b3886d1d..364de76d3f4 100644 --- a/sys/dev/usb/uhub.c +++ b/sys/dev/usb/uhub.c @@ -1,5 +1,5 @@ -/* $OpenBSD: uhub.c,v 1.14 2001/10/31 04:24:44 nate Exp $ */ -/* $NetBSD: uhub.c,v 1.52 2001/10/26 17:53:59 augustss Exp $ */ +/* $OpenBSD: uhub.c,v 1.15 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: uhub.c,v 1.57 2001/11/20 16:08:37 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/uhub.c,v 1.18 1999/11/17 22:33:43 n_hibma Exp $ */ /* @@ -268,6 +268,7 @@ USB_ATTACH(uhub) * For all ports * get port status * if device connected + * wait 100 ms * turn on reset * wait * clear C_PORT_RESET @@ -323,6 +324,7 @@ uhub_explore(usbd_device_handle dev) struct uhub_softc *sc = dev->hub->hubsoftc; struct usbd_port *up; usbd_status err; + int speed; int port; int change, status; @@ -423,15 +425,38 @@ uhub_explore(usbd_device_handle dev) /* Reset port, which implies enabling it. */ if (usbd_reset_port(dev, port, &up->status)) { - printf("uhub_explore: port=%d reset failed\n", - port); + printf("%s: port %d reset failed\n", + USBDEVNAME(sc->sc_dev), port); + continue; + } + /* Get port status again, it might have changed during reset */ + err = usbd_get_port_status(dev, port, &up->status); + if (err) { + DPRINTF(("uhub_explore: get port status failed, " + "error=%s\n", usbd_errstr(err))); + continue; + } + status = UGETW(up->status.wPortStatus); + change = UGETW(up->status.wPortChange); + if (!(status & UPS_CURRENT_CONNECT_STATUS)) { + /* Nothing connected, just ignore it. */ +#ifdef DIAGNOSTIC + printf("%s: port %d, device disappeared after reset\n", + USBDEVNAME(sc->sc_dev), port); +#endif continue; } + /* Figure out device speed */ + if (status & UPS_HIGH_SPEED) + speed = USB_SPEED_HIGH; + else if (status & UPS_LOW_SPEED) + speed = USB_SPEED_LOW; + else + speed = USB_SPEED_FULL; /* Get device info and set its address. */ err = usbd_new_device(USBDEV(sc->sc_dev), dev->bus, - dev->depth + 1, status & UPS_LOW_SPEED, - port, up); + dev->depth + 1, speed, port, up); /* XXX retry a few times? */ if (err) { DPRINTFN(-1,("uhub_explore: usb_new_device failed, " diff --git a/sys/dev/usb/ukbd.c b/sys/dev/usb/ukbd.c index 52a208b9c84..b57d0f4e37b 100644 --- a/sys/dev/usb/ukbd.c +++ b/sys/dev/usb/ukbd.c @@ -1,5 +1,5 @@ -/* $OpenBSD: ukbd.c,v 1.11 2002/04/03 17:27:58 jason Exp $ */ -/* $NetBSD: ukbd.c,v 1.69 2001/10/24 21:02:18 augustss Exp $ */ +/* $OpenBSD: ukbd.c,v 1.12 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: ukbd.c,v 1.79 2001/12/30 19:37:43 augustss Exp $ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -66,6 +66,7 @@ #include <dev/usb/usbdi_util.h> #include <dev/usb/usbdevs.h> #include <dev/usb/usb_quirks.h> +#include <dev/usb/uhidev.h> #include <dev/usb/hid.h> #include <dev/usb/ukbdvar.h> @@ -88,119 +89,96 @@ int ukbddebug = 0; #define DPRINTFN(n,x) #endif -#define NKEYCODE 6 - -#define NUM_LOCK 0x01 -#define CAPS_LOCK 0x02 -#define SCROLL_LOCK 0x04 +#define MAXKEYCODE 6 +#define MAXMOD 8 /* max 32 */ struct ukbd_data { - u_int8_t modifiers; -#define MOD_CONTROL_L 0x01 -#define MOD_CONTROL_R 0x10 -#define MOD_SHIFT_L 0x02 -#define MOD_SHIFT_R 0x20 -#define MOD_ALT_L 0x04 -#define MOD_ALT_R 0x40 -#define MOD_WIN_L 0x08 -#define MOD_WIN_R 0x80 - u_int8_t reserved; - u_int8_t keycode[NKEYCODE]; + u_int32_t modifiers; + u_int8_t keycode[MAXKEYCODE]; }; #define PRESS 0x000 #define RELEASE 0x100 #define CODEMASK 0x0ff -/* Translate USB bitmap to USB keycode. */ -#define NMOD 8 -Static const struct { - int mask, key; -} ukbd_mods[NMOD] = { - { MOD_CONTROL_L, 224 }, - { MOD_CONTROL_R, 228 }, - { MOD_SHIFT_L, 225 }, - { MOD_SHIFT_R, 229 }, - { MOD_ALT_L, 226 }, - { MOD_ALT_R, 230 }, - { MOD_WIN_L, 227 }, - { MOD_WIN_R, 231 }, -}; - #if defined(WSDISPLAY_COMPAT_RAWKBD) #define NN 0 /* no translation */ /* * Translate USB keycodes to US keyboard XT scancodes. - * Scancodes >= 128 represent EXTENDED keycodes. + * Scancodes >= 0x80 represent EXTENDED keycodes. + * + * See http://www.microsoft.com/HWDEV/TECH/input/Scancode.asp */ Static const u_int8_t ukbd_trtab[256] = { - NN, NN, NN, NN, 30, 48, 46, 32, /* 00 - 07 */ - 18, 33, 34, 35, 23, 36, 37, 38, /* 08 - 0F */ - 50, 49, 24, 25, 16, 19, 31, 20, /* 10 - 17 */ - 22, 47, 17, 45, 21, 44, 2, 3, /* 18 - 1F */ - 4, 5, 6, 7, 8, 9, 10, 11, /* 20 - 27 */ - 28, 1, 14, 15, 57, 12, 13, 26, /* 28 - 2F */ - 27, 43, 43, 39, 40, 41, 51, 52, /* 30 - 37 */ - 53, 58, 59, 60, 61, 62, 63, 64, /* 38 - 3F */ - 65, 66, 67, 68, 87, 88, 170, 70, /* 40 - 47 */ - 127, 210, 199, 201, 211, 207, 209, 205, /* 48 - 4F */ - 203, 208, 200, 69, 181, 55, 74, 78, /* 50 - 57 */ - 156, 79, 80, 81, 75, 76, 77, 71, /* 58 - 5F */ - 72, 73, 82, 83, 86, 221, NN, NN, /* 60 - 67 */ - NN, NN, NN, NN, NN, NN, NN, NN, /* 68 - 6F */ - NN, NN, NN, NN, NN, NN, NN, NN, /* 70 - 77 */ - NN, NN, NN, NN, NN, NN, NN, NN, /* 78 - 7F */ - NN, NN, NN, NN, NN, NN, NN, NN, /* 80 - 87 */ - NN, NN, NN, NN, NN, NN, NN, NN, /* 88 - 8F */ - NN, NN, NN, NN, NN, NN, NN, NN, /* 90 - 97 */ - NN, NN, NN, NN, NN, NN, NN, NN, /* 98 - 9F */ - NN, NN, NN, NN, NN, NN, NN, NN, /* A0 - A7 */ - NN, NN, NN, NN, NN, NN, NN, NN, /* A8 - AF */ - NN, NN, NN, NN, NN, NN, NN, NN, /* B0 - B7 */ - NN, NN, NN, NN, NN, NN, NN, NN, /* B8 - BF */ - NN, NN, NN, NN, NN, NN, NN, NN, /* C0 - C7 */ - NN, NN, NN, NN, NN, NN, NN, NN, /* C8 - CF */ - NN, NN, NN, NN, NN, NN, NN, NN, /* D0 - D7 */ - NN, NN, NN, NN, NN, NN, NN, NN, /* D8 - DF */ - 29, 42, 56, 219, 157, 54, 184,220, /* E0 - E7 */ - NN, NN, NN, NN, NN, NN, NN, NN, /* E8 - EF */ - NN, NN, NN, NN, NN, NN, NN, NN, /* F0 - F7 */ - NN, NN, NN, NN, NN, NN, NN, NN, /* F8 - FF */ + NN, NN, NN, NN, 0x1e, 0x30, 0x2e, 0x20, /* 00 - 07 */ + 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, /* 08 - 0f */ + 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1f, 0x14, /* 10 - 17 */ + 0x16, 0x2f, 0x11, 0x2d, 0x15, 0x2c, 0x02, 0x03, /* 18 - 1f */ + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, /* 20 - 27 */ + 0x1c, 0x01, 0x0e, 0x0f, 0x39, 0x0c, 0x0d, 0x1a, /* 28 - 2f */ + 0x1b, 0x2b, 0x2b, 0x27, 0x28, 0x29, 0x33, 0x34, /* 30 - 37 */ + 0x35, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, /* 38 - 3f */ + 0x41, 0x42, 0x43, 0x44, 0x57, 0x58, 0xaa, 0x46, /* 40 - 47 */ + 0x7f, 0xd2, 0xc7, 0xc9, 0xd3, 0xcf, 0xd1, 0xcd, /* 48 - 4f */ + 0xcb, 0xd0, 0xc8, 0x45, 0xb5, 0x37, 0x4a, 0x4e, /* 50 - 57 */ + 0x9c, 0x4f, 0x50, 0x51, 0x4b, 0x4c, 0x4d, 0x47, /* 58 - 5f */ + 0x48, 0x49, 0x52, 0x53, 0x56, 0xdd, NN, 0x59, /* 60 - 67 */ + 0x5d, 0x5e, 0x5f, NN, NN, NN, NN, NN, /* 68 - 6f */ + NN, NN, NN, NN, NN, NN, NN, NN, /* 70 - 77 */ + NN, NN, NN, NN, NN, NN, NN, NN, /* 78 - 7f */ + NN, NN, NN, NN, NN, 0x7e, NN, 0x73, /* 80 - 87 */ + 0x70, 0x7d, 0x79, 0x7b, 0x5c, NN, NN, NN, /* 88 - 8f */ + NN, NN, 0x78, 0x77, 0x76, NN, NN, NN, /* 90 - 97 */ + NN, NN, NN, NN, NN, NN, NN, NN, /* 98 - 9f */ + NN, NN, NN, NN, NN, NN, NN, NN, /* a0 - a7 */ + NN, NN, NN, NN, NN, NN, NN, NN, /* a8 - af */ + NN, NN, NN, NN, NN, NN, NN, NN, /* b0 - b7 */ + NN, NN, NN, NN, NN, NN, NN, NN, /* b8 - bf */ + NN, NN, NN, NN, NN, NN, NN, NN, /* c0 - c7 */ + NN, NN, NN, NN, NN, NN, NN, NN, /* c8 - cf */ + NN, NN, NN, NN, NN, NN, NN, NN, /* d0 - d7 */ + NN, NN, NN, NN, NN, NN, NN, NN, /* d8 - df */ + 0x1d, 0x2a, 0x38, 0xdb, 0x9d, 0x36, 0xb8, 0xdc, /* e0 - e7 */ + NN, NN, NN, NN, NN, NN, NN, NN, /* e8 - ef */ + NN, NN, NN, NN, NN, NN, NN, NN, /* f0 - f7 */ + NN, NN, NN, NN, NN, NN, NN, NN, /* f8 - ff */ }; #endif /* defined(WSDISPLAY_COMPAT_RAWKBD) */ #define KEY_ERROR 0x01 -#define MAXKEYS (NMOD+2*NKEYCODE) +#define MAXKEYS (MAXMOD+2*MAXKEYCODE) struct ukbd_softc { - USBBASEDEVICE sc_dev; /* base device */ - usbd_device_handle sc_udev; - usbd_interface_handle sc_iface; /* interface */ - usbd_pipe_handle sc_intrpipe; /* interrupt pipe */ - int sc_ep_addr; + struct uhidev sc_hdev; struct ukbd_data sc_ndata; struct ukbd_data sc_odata; + struct hid_location sc_modloc[MAXMOD]; + u_int sc_nmod; + struct { + u_int32_t mask; + u_int8_t key; + } sc_mods[MAXMOD]; + + struct hid_location sc_keycodeloc; + u_int sc_nkeycode; char sc_enabled; int sc_console_keyboard; /* we are the console keyboard */ char sc_debounce; /* for quirk handling */ + usb_callout_t sc_delay; /* for quirk handling */ struct ukbd_data sc_data; /* for quirk handling */ + struct hid_location sc_numloc; + struct hid_location sc_capsloc; + struct hid_location sc_scroloc; int sc_leds; -#if defined(__OpenBSD__) - struct timeout sc_delay; /* for quirk handling */ - struct timeout sc_rawrepeat_ch; -#else - struct callout sc_delay; /* for quirk handling */ - struct callout sc_rawrepeat_ch; -#endif + usb_callout_t sc_rawrepeat_ch; -#if defined(__NetBSD__) || defined(__OpenBSD__) struct device *sc_wskbddev; #if defined(WSDISPLAY_COMPAT_RAWKBD) #define REP_DELAY1 400 @@ -213,7 +191,6 @@ struct ukbd_softc { int sc_polling; int sc_npollchar; u_int16_t sc_pollchars[MAXKEYS]; -#endif u_char sc_dying; }; @@ -254,22 +231,21 @@ Static int ukbd_is_console; Static void ukbd_cngetc(void *, u_int *, int *); Static void ukbd_cnpollc(void *, int); -#if defined(__NetBSD__) || defined(__OpenBSD__) const struct wskbd_consops ukbd_consops = { ukbd_cngetc, ukbd_cnpollc, }; -#endif -Static void ukbd_intr(usbd_xfer_handle, usbd_private_handle, usbd_status); +Static const char *ukbd_parse_desc(struct ukbd_softc *sc); + +Static void ukbd_intr(struct uhidev *addr, void *ibuf, u_int len); Static void ukbd_decode(struct ukbd_softc *sc, struct ukbd_data *ud); Static void ukbd_delayed_decode(void *addr); Static int ukbd_enable(void *, int); Static void ukbd_set_leds(void *, int); -#if defined(__NetBSD__) || defined(__OpenBSD__) -Static int ukbd_ioctl(void *, u_long, caddr_t, int, struct proc *); +Static int ukbd_ioctl(void *, u_long, caddr_t, int, usb_proc_ptr ); #ifdef WSDISPLAY_COMPAT_RAWKBD Static void ukbd_rawrepeat(void *v); #endif @@ -290,90 +266,53 @@ const struct wskbd_mapdata ukbd_keymapdata = { KB_US, #endif }; -#endif USB_DECLARE_DRIVER(ukbd); USB_MATCH(ukbd) { USB_MATCH_START(ukbd, uaa); - usb_interface_descriptor_t *id; + struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa; + int size; + void *desc; - /* Check that this is a keyboard that speaks the boot protocol. */ - if (uaa->iface == NULL) - return (UMATCH_NONE); - id = usbd_get_interface_descriptor(uaa->iface); - if (id == NULL || - id->bInterfaceClass != UICLASS_HID || - id->bInterfaceSubClass != UISUBCLASS_BOOT || - id->bInterfaceProtocol != UIPROTO_BOOT_KEYBOARD) + uhidev_get_report_desc(uha->parent, &desc, &size); + if (!hid_is_collection(desc, size, uha->reportid, + HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD))) return (UMATCH_NONE); - return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO); + + return (UMATCH_IFACECLASS); } USB_ATTACH(ukbd) { USB_ATTACH_START(ukbd, sc, uaa); - usbd_interface_handle iface = uaa->iface; - usb_interface_descriptor_t *id; - usb_endpoint_descriptor_t *ed; - usbd_status err; + struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa; u_int32_t qflags; - char devinfo[1024]; -#if defined(__NetBSD__) || defined(__OpenBSD__) + const char *parseerr; struct wskbddev_attach_args a; -#else - int i; -#endif - sc->sc_udev = uaa->device; - sc->sc_iface = iface; - id = usbd_get_interface_descriptor(iface); - usbd_devinfo(uaa->device, 0, devinfo); - USB_ATTACH_SETUP; - printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev), - devinfo, id->bInterfaceClass, id->bInterfaceSubClass); - - ed = usbd_interface2endpoint_descriptor(iface, 0); - if (ed == NULL) { - printf("%s: could not read endpoint descriptor\n", - USBDEVNAME(sc->sc_dev)); + sc->sc_hdev.sc_intr = ukbd_intr; + sc->sc_hdev.sc_parent = uha->parent; + sc->sc_hdev.sc_report_id = uha->reportid; + + parseerr = ukbd_parse_desc(sc); + if (parseerr != NULL) { + printf("\n%s: attach failed, %s\n", + sc->sc_hdev.sc_dev.dv_xname, parseerr); USB_ATTACH_ERROR_RETURN; } + +#ifdef DIAGNOSTIC + printf(": %d modifier keys, %d key codes", sc->sc_nmod, + sc->sc_nkeycode); +#endif + printf("\n"); - DPRINTFN(10,("ukbd_attach: bLength=%d bDescriptorType=%d " - "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d" - " bInterval=%d\n", - ed->bLength, ed->bDescriptorType, - ed->bEndpointAddress & UE_ADDR, - UE_GET_DIR(ed->bEndpointAddress)==UE_DIR_IN? "in" : "out", - ed->bmAttributes & UE_XFERTYPE, - UGETW(ed->wMaxPacketSize), ed->bInterval)); - - if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN || - (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) { - printf("%s: unexpected endpoint\n", - USBDEVNAME(sc->sc_dev)); - USB_ATTACH_ERROR_RETURN; - } - qflags = usbd_get_quirks(uaa->device)->uq_flags; - if ((qflags & UQ_NO_SET_PROTO) == 0) { - err = usbd_set_protocol(iface, 0); - DPRINTFN(5, ("ukbd_attach: protocol set\n")); - if (err) { - printf("%s: set protocol failed\n", - USBDEVNAME(sc->sc_dev)); - USB_ATTACH_ERROR_RETURN; - } - } + qflags = usbd_get_quirks(uha->parent->sc_udev)->uq_flags; sc->sc_debounce = (qflags & UQ_SPUR_BUT_UP) != 0; - /* Ignore if SETIDLE fails since it is not crucial. */ - (void)usbd_set_idle(iface, 0, 0); - - sc->sc_ep_addr = ed->bEndpointAddress; - /* * Remember if we're the console keyboard. * @@ -398,26 +337,14 @@ USB_ATTACH(ukbd) a.accessops = &ukbd_accessops; a.accesscookie = sc; -#if defined(__OpenBSD__) -#ifdef WSDISPLAY_COMPAT_RAWKBD - timeout_set(&sc->sc_rawrepeat_ch, ukbd_rawrepeat, sc); -#endif - timeout_set(&sc->sc_delay, ukbd_delayed_decode, sc); -#endif - -#if defined(__NetBSD__) - callout_init(&sc->sc_rawrepeat_ch); - callout_init(&sc->sc_delay); -#endif + usb_callout_init(sc->sc_rawrepeat_ch); + usb_callout_init(sc->sc_delay); /* Flash the leds; no real purpose, just shows we're alive. */ ukbd_set_leds(sc, WSKBD_LED_SCROLL | WSKBD_LED_NUM | WSKBD_LED_CAPS); - usbd_delay_ms(uaa->device, 400); + usbd_delay_ms(uha->parent->sc_udev, 400); ukbd_set_leds(sc, 0); - usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, - USBDEV(sc->sc_dev)); - sc->sc_wskbddev = config_found(self, &a, wskbddevprint); USB_ATTACH_SUCCESS_RETURN; @@ -427,38 +354,27 @@ int ukbd_enable(void *v, int on) { struct ukbd_softc *sc = v; - usbd_status err; if (on && sc->sc_dying) return (EIO); /* Should only be called to change state */ if (sc->sc_enabled == on) { -#ifdef UKBD_DEBUG +#ifdef DIAGNOSTIC printf("ukbd_enable: %s: bad call on=%d\n", - USBDEVNAME(sc->sc_dev), on); + USBDEVNAME(sc->sc_hdev.sc_dev), on); #endif return (EBUSY); } DPRINTF(("ukbd_enable: sc=%p on=%d\n", sc, on)); + sc->sc_enabled = on; if (on) { - /* Set up interrupt pipe. */ - err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr, - USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc, - &sc->sc_ndata, sizeof(sc->sc_ndata), ukbd_intr, - USBD_DEFAULT_INTERVAL); - if (err) - return (EIO); + return (uhidev_open(&sc->sc_hdev)); } else { - /* Disable interrupts. */ - usbd_abort_pipe(sc->sc_intrpipe); - usbd_close_pipe(sc->sc_intrpipe); - sc->sc_intrpipe = NULL; + uhidev_close(&sc->sc_hdev); + return (0); } - sc->sc_enabled = on; - - return (0); } int @@ -507,7 +423,8 @@ USB_DETACH(ukbd) * XXX Should notify some other keyboard that it can be * XXX console, if there are any other keyboards. */ - printf("%s: was console keyboard\n", USBDEVNAME(sc->sc_dev)); + printf("%s: was console keyboard\n", + USBDEVNAME(sc->sc_hdev.sc_dev)); wskbd_cndetach(); ukbd_is_console = 1; #endif @@ -517,37 +434,34 @@ USB_DETACH(ukbd) rv = config_detach(sc->sc_wskbddev, flags); /* The console keyboard does not get a disable call, so check pipe. */ - if (sc->sc_intrpipe != NULL) { - usbd_abort_pipe(sc->sc_intrpipe); - usbd_close_pipe(sc->sc_intrpipe); - sc->sc_intrpipe = NULL; - } - - usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, - USBDEV(sc->sc_dev)); + if (sc->sc_hdev.sc_state & UHIDEV_OPEN) + uhidev_close(&sc->sc_hdev); return (rv); } void -ukbd_intr(xfer, addr, status) - usbd_xfer_handle xfer; - usbd_private_handle addr; - usbd_status status; +ukbd_intr(struct uhidev *addr, void *ibuf, u_int len) { - struct ukbd_softc *sc = addr; + struct ukbd_softc *sc = (struct ukbd_softc *)addr; struct ukbd_data *ud = &sc->sc_ndata; + int i; - DPRINTFN(5, ("ukbd_intr: status=%d\n", status)); - if (status == USBD_CANCELLED) - return; - - if (status) { - DPRINTF(("ukbd_intr: status=%d\n", status)); - if (status == USBD_STALLED) - usbd_clear_endpoint_stall_async(sc->sc_intrpipe); - return; +#ifdef UKBD_DEBUG + if (ukbddebug > 5) { + printf("ukbd_intr: data"); + for (i = 0; i < len; i++) + printf(" 0x%02x", ((u_char *)ibuf)[i]); + printf("\n"); } +#endif + + ud->modifiers = 0; + for (i = 0; i < sc->sc_nmod; i++) + if (hid_get_data(ibuf, &sc->sc_modloc[i])) + ud->modifiers |= sc->sc_mods[i].mask; + memcpy(ud->keycode, (char *)ibuf + sc->sc_keycodeloc.pos / 8, + sc->sc_nkeycode); if (sc->sc_debounce && !sc->sc_polling) { /* @@ -557,12 +471,8 @@ ukbd_intr(xfer, addr, status) * We avoid this bug by holding off decoding for 20 ms. */ sc->sc_data = *ud; -#if defined(__OpenBSD__) - timeout_add(&sc->sc_delay, hz / 50); -#else - callout_reset(&sc->sc_delay, hz / 50, ukbd_delayed_decode, sc); -#endif -#if DDB + usb_callout(sc->sc_delay, hz / 50, ukbd_delayed_decode, sc); +#ifdef DDB } else if (sc->sc_console_keyboard && !sc->sc_polling) { /* * For the console keyboard we can't deliver CTL-ALT-ESC @@ -571,11 +481,7 @@ ukbd_intr(xfer, addr, status) * loses bigtime. */ sc->sc_data = *ud; -#if defined(__OpenBSD__) - timeout_add(&sc->sc_delay, 1); /* NOT an immediate timeout */ -#else - callout_reset(&sc->sc_delay, 0, ukbd_delayed_decode, sc); -#endif + usb_callout(sc->sc_delay, 1, ukbd_delayed_decode, sc); #endif } else { ukbd_decode(sc, ud); @@ -607,7 +513,7 @@ ukbd_decode(struct ukbd_softc *sc, struct ukbd_data *ud) */ if (ukbdtrace) { struct ukbdtraceinfo *p = &ukbdtracedata[ukbdtraceindex]; - p->unit = sc->sc_dev.dv_unit; + p->unit = sc->sc_hdev.sc_dev.dv_unit; microtime(&p->tv); p->ud = *ud; if (++ukbdtraceindex >= UKBDTRACESIZE) @@ -632,19 +538,19 @@ ukbd_decode(struct ukbd_softc *sc, struct ukbd_data *ud) mod = ud->modifiers; omod = sc->sc_odata.modifiers; if (mod != omod) - for (i = 0; i < NMOD; i++) - if (( mod & ukbd_mods[i].mask) != - (omod & ukbd_mods[i].mask)) - ADDKEY(ukbd_mods[i].key | - (mod & ukbd_mods[i].mask + for (i = 0; i < sc->sc_nmod; i++) + if (( mod & sc->sc_mods[i].mask) != + (omod & sc->sc_mods[i].mask)) + ADDKEY(sc->sc_mods[i].key | + (mod & sc->sc_mods[i].mask ? PRESS : RELEASE)); - if (memcmp(ud->keycode, sc->sc_odata.keycode, NKEYCODE) != 0) { + if (memcmp(ud->keycode, sc->sc_odata.keycode, sc->sc_nkeycode) != 0) { /* Check for released keys. */ - for (i = 0; i < NKEYCODE; i++) { + for (i = 0; i < sc->sc_nkeycode; i++) { key = sc->sc_odata.keycode[i]; if (key == 0) continue; - for (j = 0; j < NKEYCODE; j++) + for (j = 0; j < sc->sc_nkeycode; j++) if (key == ud->keycode[j]) goto rfound; DPRINTFN(3,("ukbd_intr: relse key=0x%02x\n", key)); @@ -654,11 +560,11 @@ ukbd_decode(struct ukbd_softc *sc, struct ukbd_data *ud) } /* Check for pressed keys. */ - for (i = 0; i < NKEYCODE; i++) { + for (i = 0; i < sc->sc_nkeycode; i++) { key = ud->keycode[i]; if (key == 0) continue; - for (j = 0; j < NKEYCODE; j++) + for (j = 0; j < sc->sc_nkeycode; j++) if (key == sc->sc_odata.keycode[j]) goto pfound; DPRINTFN(2,("ukbd_intr: press key=0x%02x\n", key)); @@ -680,7 +586,7 @@ ukbd_decode(struct ukbd_softc *sc, struct ukbd_data *ud) } #ifdef WSDISPLAY_COMPAT_RAWKBD if (sc->sc_rawkbd) { - char cbuf[MAXKEYS * 2]; + u_char cbuf[MAXKEYS * 2]; int c; int npress; @@ -708,19 +614,11 @@ ukbd_decode(struct ukbd_softc *sc, struct ukbd_data *ud) s = spltty(); wskbd_rawinput(sc->sc_wskbddev, cbuf, j); splx(s); -#if defined(__OpenBSD__) - timeout_del(&sc->sc_rawrepeat_ch); -#else - callout_stop(&sc->sc_rawrepeat_ch); -#endif + usb_uncallout(sc->sc_rawrepeat_ch, ukbd_rawrepeat, sc); if (npress != 0) { sc->sc_nrep = npress; -#if defined(__OpenBSD__) - timeout_add(&sc->sc_rawrepeat_ch, hz * REP_DELAY1/1000); -#else - callout_reset(&sc->sc_rawrepeat_ch, + usb_callout(sc->sc_rawrepeat_ch, hz * REP_DELAY1 / 1000, ukbd_rawrepeat, sc); -#endif } return; } @@ -742,21 +640,24 @@ ukbd_set_leds(void *v, int leds) struct ukbd_softc *sc = v; u_int8_t res; - DPRINTF(("ukbd_set_leds: sc=%p leds=%d\n", sc, leds)); + DPRINTF(("ukbd_set_leds: sc=%p leds=%d, sc_leds=%d\n", + sc, leds, sc->sc_leds)); if (sc->sc_dying) return; + if (sc->sc_leds == leds) + return; sc->sc_leds = leds; res = 0; - if (leds & WSKBD_LED_SCROLL) - res |= SCROLL_LOCK; - if (leds & WSKBD_LED_NUM) - res |= NUM_LOCK; - if (leds & WSKBD_LED_CAPS) - res |= CAPS_LOCK; - res |= leds & 0xf8; - usbd_set_report_async(sc->sc_iface, UHID_OUTPUT_REPORT, 0, &res, 1); + /* XXX not really right */ + if ((leds & WSKBD_LED_SCROLL) && sc->sc_scroloc.size == 1) + res |= 1 << sc->sc_scroloc.pos; + if ((leds & WSKBD_LED_NUM) && sc->sc_numloc.size == 1) + res |= 1 << sc->sc_numloc.pos; + if ((leds & WSKBD_LED_CAPS) && sc->sc_capsloc.size == 1) + res |= 1 << sc->sc_capsloc.pos; + uhidev_set_report_async(&sc->sc_hdev, UHID_OUTPUT_REPORT, &res, 1); } #ifdef WSDISPLAY_COMPAT_RAWKBD @@ -769,17 +670,13 @@ ukbd_rawrepeat(void *v) s = spltty(); wskbd_rawinput(sc->sc_wskbddev, sc->sc_rep, sc->sc_nrep); splx(s); -#if defined(__OpenBSD__) - timeout_add(&sc->sc_rawrepeat_ch, hz * REP_DELAYN / 1000); -#else - callout_reset(&sc->sc_rawrepeat_ch, hz * REP_DELAYN / 1000, + usb_callout(sc->sc_rawrepeat_ch, hz * REP_DELAYN / 1000, ukbd_rawrepeat, sc); -#endif } #endif int -ukbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) +ukbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, usb_proc_ptr p) { struct ukbd_softc *sc = v; @@ -797,17 +694,19 @@ ukbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) case WSKBDIO_SETMODE: DPRINTF(("ukbd_ioctl: set raw = %d\n", *(int *)data)); sc->sc_rawkbd = *(int *)data == WSKBD_RAW; -#if defined(__OpenBSD__) - timeout_del(&sc->sc_rawrepeat_ch); -#else - callout_stop(&sc->sc_rawrepeat_ch); -#endif + usb_uncallout(sc->sc_rawrepeat_ch, ukbd_rawrepeat, sc); return (0); #endif } return (-1); } +/* + * This is a hack to work around some broken ports that don't call + * cnpollc() before cngetc(). + */ +static int pollenter, warned; + /* Console interface. */ void ukbd_cngetc(void *v, u_int *type, int *data) @@ -815,12 +714,25 @@ ukbd_cngetc(void *v, u_int *type, int *data) struct ukbd_softc *sc = v; int s; int c; + int broken; + + if (pollenter == 0) { + if (!warned) { + printf("\n" +"This port is broken, it does not call cnpollc() before calling cngetc().\n" +"This should be fixed, but it will work anyway (for now).\n"); + warned = 1; + } + broken = 1; + ukbd_cnpollc(v, 1); + } else + broken = 0; DPRINTFN(0,("ukbd_cngetc: enter\n")); s = splusb(); sc->sc_polling = 1; while(sc->sc_npollchar <= 0) - usbd_dopoll(sc->sc_iface); + usbd_dopoll(sc->sc_hdev.sc_parent->sc_iface); sc->sc_polling = 0; c = sc->sc_pollchars[0]; sc->sc_npollchar--; @@ -830,6 +742,8 @@ ukbd_cngetc(void *v, u_int *type, int *data) *data = c & CODEMASK; splx(s); DPRINTFN(0,("ukbd_cngetc: return 0x%02x\n", c)); + if (broken) + ukbd_cnpollc(v, 0); } void @@ -840,7 +754,8 @@ ukbd_cnpollc(void *v, int on) DPRINTFN(2,("ukbd_cnpollc: sc=%p on=%d\n", v, on)); - (void)usbd_interface2device_handle(sc->sc_iface,&dev); + usbd_interface2device_handle(sc->sc_hdev.sc_parent->sc_iface, &dev); + if (on) pollenter++; else pollenter--; usbd_set_polling(dev, on); } @@ -856,3 +771,64 @@ ukbd_cnattach(void) ukbd_is_console = 1; return (0); } + +const char * +ukbd_parse_desc(struct ukbd_softc *sc) +{ + struct hid_data *d; + struct hid_item h; + int size; + void *desc; + int imod; + + uhidev_get_report_desc(sc->sc_hdev.sc_parent, &desc, &size); + imod = 0; + sc->sc_nkeycode = 0; + d = hid_start_parse(desc, size, hid_input); + while (hid_get_item(d, &h)) { + /*printf("ukbd: id=%d kind=%d usage=0x%x flags=0x%x pos=%d size=%d cnt=%d\n", + h.report_ID, h.kind, h.usage, h.flags, h.loc.pos, h.loc.size, h.loc.count);*/ + if (h.kind != hid_input || (h.flags & HIO_CONST) || + HID_GET_USAGE_PAGE(h.usage) != HUP_KEYBOARD || + h.report_ID != sc->sc_hdev.sc_report_id) + continue; + DPRINTF(("ukbd: imod=%d usage=0x%x flags=0x%x pos=%d size=%d " + "cnt=%d\n", imod, + h.usage, h.flags, h.loc.pos, h.loc.size, h.loc.count)); + if (h.flags & HIO_VARIABLE) { + if (h.loc.size != 1) + return ("bad modifier size"); + /* Single item */ + if (imod < MAXMOD) { + sc->sc_modloc[imod] = h.loc; + sc->sc_mods[imod].mask = 1 << imod; + sc->sc_mods[imod].key = HID_GET_USAGE(h.usage); + imod++; + } else + return ("too many modifier keys"); + } else { + /* Array */ + if (h.loc.size != 8) + return ("key code size != 8"); + if (h.loc.count > MAXKEYCODE) + return ("too many key codes"); + if (h.loc.pos % 8 != 0) + return ("key codes not on byte boundary"); + if (sc->sc_nkeycode != 0) + return ("multiple key code arrays\n"); + sc->sc_keycodeloc = h.loc; + sc->sc_nkeycode = h.loc.count; + } + } + sc->sc_nmod = imod; + hid_end_parse(d); + + hid_locate(desc, size, HID_USAGE2(HUP_LEDS, HUD_LED_NUM_LOCK), + sc->sc_hdev.sc_report_id, hid_output, &sc->sc_numloc, NULL); + hid_locate(desc, size, HID_USAGE2(HUP_LEDS, HUD_LED_CAPS_LOCK), + sc->sc_hdev.sc_report_id, hid_output, &sc->sc_capsloc, NULL); + hid_locate(desc, size, HID_USAGE2(HUP_LEDS, HUD_LED_SCROLL_LOCK), + sc->sc_hdev.sc_report_id, hid_output, &sc->sc_scroloc, NULL); + + return (NULL); +} diff --git a/sys/dev/usb/ukbdmap.c b/sys/dev/usb/ukbdmap.c index 3c39bdc63b2..9d0ad9f25d8 100644 --- a/sys/dev/usb/ukbdmap.c +++ b/sys/dev/usb/ukbdmap.c @@ -1,8 +1,8 @@ -/* $OpenBSD: ukbdmap.c,v 1.9 2002/05/05 21:52:16 nate Exp $ */ -/* $NetBSD: ukbdmap.c,v 1.6 2001/04/04 05:31:57 toshii Exp $ */ +/* $OpenBSD: ukbdmap.c,v 1.10 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: ukbdmap.c,v 1.10 2002/03/17 18:01:07 augustss Exp $ */ /* - * Copyright (c) 1999 The NetBSD Foundation, Inc. + * Copyright (c) 1999,2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -94,7 +94,7 @@ Static const keysym_t ukbd_keydesc_us[] = { KC(47), KS_bracketleft, KS_braceleft, KC(48), KS_bracketright,KS_braceright, KC(49), KS_backslash, KS_bar, - KC(50), KS_numbersign, KS_asciitilde, + KC(50), KS_backslash, KS_bar, KC(51), KS_semicolon, KS_colon, KC(52), KS_apostrophe, KS_quotedbl, KC(53), KS_grave, KS_asciitilde, @@ -256,6 +256,7 @@ Static const keysym_t ukbd_keydesc_sv[] = { /* pos normal shifted altgr shift-altgr */ KC(45), KS_plus, KS_question, KS_backslash, KC(48), KS_dead_diaeresis, KS_dead_circumflex, KS_dead_tilde, + KC(50), KS_comma, KS_asterisk, KC(51), KS_odiaeresis, KC(52), KS_adiaeresis, KC(53), KS_paragraph, KS_onehalf, @@ -273,6 +274,7 @@ Static const keysym_t ukbd_keydesc_no[] = { /* pos normal shifted altgr shift-altgr */ KC(46), KS_backslash, KS_dead_grave, KS_dead_acute, KC(48), KS_dead_diaeresis, KS_dead_circumflex, KS_dead_tilde, + KC(50), KS_comma, KS_asterisk, KC(51), KS_oslash, KC(52), KS_ae, KC(53), KS_bar, KS_paragraph, @@ -358,6 +360,7 @@ Static const keysym_t ukbd_keydesc_uk[] = { KC(45), KS_minus, KS_underscore, KS_hyphen, KS_ssharp, KC(46), KS_equal, KS_plus, KS_onehalf, KS_guillemotleft, KC(49), KS_numbersign, KS_asciitilde, KS_sterling, KS_thorn, + KC(50), KS_numbersign, KS_asciitilde, KC(52), KS_apostrophe, KS_at, KS_section, KS_Agrave, KC(53), KS_grave, KS_grave, KS_agrave, KS_agrave, KC(100), KS_backslash, KS_bar, KS_Udiaeresis, diff --git a/sys/dev/usb/ulpt.c b/sys/dev/usb/ulpt.c index cde41db63f3..b10baefbd4c 100644 --- a/sys/dev/usb/ulpt.c +++ b/sys/dev/usb/ulpt.c @@ -1,5 +1,5 @@ -/* $OpenBSD: ulpt.c,v 1.9 2001/10/31 04:24:44 nate Exp $ */ -/* $NetBSD: ulpt.c,v 1.43 2001/10/19 15:30:25 nathanw Exp $ */ +/* $OpenBSD: ulpt.c,v 1.10 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: ulpt.c,v 1.49 2002/02/25 22:39:01 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/ulpt.c,v 1.24 1999/11/17 22:33:44 n_hibma Exp $ */ /* @@ -40,7 +40,8 @@ */ /* - * Printer Class spec: http://www.usb.org/developers/data/devclass/usbprint11.pdf + * Printer Class spec: + * http://www.usb.org/developers/data/devclass/usbprint11.pdf */ #include <sys/param.h> @@ -147,7 +148,9 @@ Static struct cdevsw ulpt_cdevsw = { /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, +#if !defined(__FreeBSD__) || (__FreeBSD__ < 5) /* bmaj */ -1 +#endif }; #endif @@ -307,8 +310,8 @@ USB_ATTACH(ulpt) USETW(req.wValue, cd->bConfigurationValue); USETW2(req.wIndex, id->bInterfaceNumber, id->bAlternateSetting); USETW(req.wLength, sizeof devinfo - 1); - err = usbd_do_request_flags(dev, &req, devinfo,USBD_SHORT_XFER_OK, - &alen); + err = usbd_do_request_flags(dev, &req, devinfo, USBD_SHORT_XFER_OK, + &alen, USBD_DEFAULT_TIMEOUT); if (err) { printf("%s: cannot get device id\n", USBDEVNAME(sc->sc_dev)); } else if (alen <= 2) { @@ -366,12 +369,12 @@ USB_DETACH(ulpt) int s; #if defined(__NetBSD__) || defined(__OpenBSD__) int maj, mn; - - DPRINTF(("ulpt_detach: sc=%p flags=%d\n", sc, flags)); #elif defined(__FreeBSD__) - DPRINTF(("ulpt_detach: sc=%p\n", sc)); + struct vnode *vp; #endif + DPRINTF(("ulpt_detach: sc=%p\n", sc)); + sc->sc_dying = 1; if (sc->sc_out_pipe != NULL) usbd_abort_pipe(sc->sc_out_pipe); @@ -396,7 +399,12 @@ USB_DETACH(ulpt) mn = self->dv_unit; vdevgone(maj, mn, mn, VCHR); #elif defined(__FreeBSD__) - /* XXX not implemented yet */ + vp = SLIST_FIRST(&sc->dev->si_hlist); + if (vp) + VOP_REVOKE(vp, REVOKEALL); + vp = SLIST_FIRST(&sc->dev_noprime->si_hlist); + if (vp) + VOP_REVOKE(vp, REVOKEALL); destroy_dev(sc->dev); destroy_dev(sc->dev_noprime); @@ -434,7 +442,6 @@ ulpt_reset(struct ulpt_softc *sc) usb_device_request_t req; DPRINTFN(1, ("ulpt_reset\n")); - req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_SOFT_RESET; USETW(req.wValue, 0); USETW(req.wIndex, sc->sc_ifaceno); @@ -446,6 +453,7 @@ ulpt_reset(struct ulpt_softc *sc) * UT_WRITE_CLASS_INTERFACE. Many printers use the old one, * so we try both. */ + req.bmRequestType = UT_WRITE_CLASS_OTHER; if (usbd_do_request(sc->sc_udev, &req, 0)) { /* 1.0 */ req.bmRequestType = UT_WRITE_CLASS_INTERFACE; (void)usbd_do_request(sc->sc_udev, &req, 0); /* 1.1 */ @@ -471,7 +479,7 @@ int ulptusein = 1; * Reset the printer, then wait until it's selected and not busy. */ int -ulptopen(dev_t dev, int flag, int mode, struct proc *p) +ulptopen(dev_t dev, int flag, int mode, usb_proc_ptr p) { u_char flags = ULPTFLAGS(dev); struct ulpt_softc *sc; @@ -546,8 +554,18 @@ ulptopen(dev_t dev, int flag, int mode, struct proc *p) sc->sc_in_xfer2 = usbd_alloc_xfer(sc->sc_udev); if (sc->sc_in_xfer1 == NULL || sc->sc_in_xfer2 == NULL) { error = ENOMEM; + if (sc->sc_in_xfer1 != NULL) { + usbd_free_xfer(sc->sc_in_xfer1); + sc->sc_in_xfer1 = NULL; + } + if (sc->sc_in_xfer2 != NULL) { + usbd_free_xfer(sc->sc_in_xfer2); + sc->sc_in_xfer2 = NULL; + } usbd_close_pipe(sc->sc_out_pipe); sc->sc_out_pipe = NULL; + usbd_close_pipe(sc->sc_in_pipe); + sc->sc_in_pipe = NULL; sc->sc_state = 0; goto done; } @@ -590,7 +608,7 @@ ulpt_statusmsg(u_char status, struct ulpt_softc *sc) } int -ulptclose(dev_t dev, int flag, int mode, struct proc *p) +ulptclose(dev_t dev, int flag, int mode, usb_proc_ptr p) { struct ulpt_softc *sc; @@ -680,7 +698,7 @@ ulptwrite(dev_t dev, struct uio *uio, int flags) } int -ulptioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +ulptioctl(dev_t dev, u_long cmd, caddr_t data, int flag, usb_proc_ptr p) { int error = 0; diff --git a/sys/dev/usb/umass.c b/sys/dev/usb/umass.c index 17e1d1bf45f..a47e31a0d27 100644 --- a/sys/dev/usb/umass.c +++ b/sys/dev/usb/umass.c @@ -1,5 +1,5 @@ -/* $OpenBSD: umass.c,v 1.14 2002/03/14 03:16:08 millert Exp $ */ -/* $NetBSD: umass.c,v 1.49 2001/01/21 18:56:38 augustss Exp $ */ +/* $OpenBSD: umass.c,v 1.15 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: umass.c,v 1.87 2002/03/17 18:02:53 augustss Exp $ */ /*- * Copyright (c) 1999 MAEKAWA Masahide <bishop@rr.iij4u.or.jp>, * Nick Hibma <n_hibma@freebsd.org> @@ -30,7 +30,7 @@ */ /* - * Universal Serial Bus Mass Storage Class Bulk-Only Transport + * Universal Serial Bus Mass Storage Class specs: * http://www.usb.org/developers/data/devclass/usbmassover_11.pdf * http://www.usb.org/developers/data/devclass/usbmassbulk_10.pdf * http://www.usb.org/developers/data/devclass/usbmass-cbi10.pdf @@ -51,13 +51,14 @@ * * Over these wire protocols it handles the following command protocols * - SCSI - * - UFI (floppy command set) - * - 8070 (ATA/ATAPI) + * - 8070 (ATA/ATAPI for rewritable removable media) + * - UFI (USB Floppy Interface) * - * UFI and 8070i are transformed versions of the SCSI command set. The - * sc->transform method is used to convert the commands into the appropriate - * format (if at all necessary). For example, UFI requires all commands to be - * 12 bytes in length amongst other things. + * 8070i is a transformed version of the SCSI command set. UFI is a transformed + * version of the 8070i command set. The sc->transform method is used to + * convert the commands into the appropriate format (if at all necessary). + * For example, ATAPI requires all commands to be 12 bytes in length amongst + * other things. * * The source code below is marked and can be split into a number of pieces * (in this order): @@ -93,15 +94,15 @@ * umass_cam_cb again to complete the CAM command. */ -/* XXX Should we split the driver into a number of files? umass.c, - * umass_scsi.c, umass_8070.c, umass_ufi.c, umass_bbb.c, umass_cbi.c or - * something similar? - */ - -#if !defined(__OpenBSD__) +#if defined(__NetBSD__) #include "atapibus.h" +#include "scsibus.h" +#elif defined(__OpenBSD__) +#include "atapiscsi.h" #endif +#include "wd.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> @@ -124,396 +125,15 @@ #include <dev/usb/usbdi_util.h> #include <dev/usb/usbdevs.h> -#if defined(__FreeBSD__) -#include <cam/cam.h> -#include <cam/cam_ccb.h> -#include <cam/cam_sim.h> -#include <cam/cam_xpt_sim.h> -#include <cam/scsi/scsi_all.h> -#include <cam/scsi/scsi_da.h> - -#ifdef UMASS_DO_CAM_RESCAN -#include <sys/devicestat.h> -#include <cam/cam_periph.h> -#endif +#include <dev/usb/umassvar.h> +#include <dev/usb/umass_quirks.h> +#include <dev/usb/umass_scsipi.h> +#include <dev/usb/umass_isdata.h> -#elif defined(__NetBSD__) -#include <sys/scsiio.h> -#include <dev/scsipi/scsi_all.h> -#include <dev/scsipi/scsipi_all.h> -#include <dev/scsipi/scsiconf.h> - -#include <dev/scsipi/atapiconf.h> - -#include <dev/scsipi/scsipi_disk.h> -#include <dev/scsipi/scsi_disk.h> -#include <dev/scsipi/scsi_changer.h> - -#include <dev/ata/atavar.h> /* XXX */ - -#define SCSI_LINK_TARGET(sc) ((sc)->scsipi_scsi.target) -#define SCSI_LINK_LUN(sc) ((sc)->scsipi_scsi.lun) -#elif defined(__OpenBSD__) -#include <scsi/scsi_all.h> -#include <scsi/scsiconf.h> -#include <scsi/scsi_disk.h> -#include <machine/bus.h> - -#define SCSI_LINK_TARGET(sc) ((sc)->target) -#define SCSI_LINK_LUN(sc) ((sc)->lun) -#define scsipi_generic scsi_generic - -#endif - -#define SHORT_INQUIRY_LENGTH 36 /* XXX */ #ifdef UMASS_DEBUG -#define DIF(m, x) if (umassdebug & (m)) do { x ; } while (0) -#define DPRINTF(m, x) if (umassdebug & (m)) logprintf x -#define UDMASS_UPPER 0x00008000 /* upper layer */ -#define UDMASS_GEN 0x00010000 /* general */ -#define UDMASS_SCSI 0x00020000 /* scsi */ -#define UDMASS_UFI 0x00040000 /* ufi command set */ -#define UDMASS_8070 0x00080000 /* 8070i command set */ -#define UDMASS_USB 0x00100000 /* USB general */ -#define UDMASS_BBB 0x00200000 /* Bulk-Only transfers */ -#define UDMASS_CBI 0x00400000 /* CBI transfers */ -#define UDMASS_ALL 0xffff0000 /* all of the above */ - -#define UDMASS_XFER 0x40000000 /* all transfers */ -#define UDMASS_CMD 0x80000000 - int umassdebug = 0; -#else -#define DIF(m, x) /* nop */ -#define DPRINTF(m, x) /* nop */ -#endif - - -/* Generic definitions */ - -#define UFI_COMMAND_LENGTH 12 -#define ATAPI_COMMAND_LENGTH 12 -/* Direction for umass_*_transfer */ -#define DIR_NONE 0 -#define DIR_IN 1 -#define DIR_OUT 2 - -/* The transfer speed determines the timeout value */ -#define UMASS_DEFAULT_TRANSFER_SPEED 150 /* in kb/s, conservative est. */ -#define UMASS_FLOPPY_TRANSFER_SPEED 20 -#define UMASS_ZIP100_TRANSFER_SPEED 650 - -#define UMASS_SPINUP_TIME 10000 /* ms */ - -#ifdef __FreeBSD__ -/* device name */ -#define DEVNAME "umass" -#define DEVNAME_SIM "umass-" - -#define UMASS_MAX_TRANSFER_SIZE 65536 - -/* CAM specific definitions */ - -/* The bus id, whatever that is */ -#define UMASS_SCSI_BUS 0 - -/* All USB drives are 'connected' to one SIM (SCSI controller). umass3 - * ends up being target 3 on that SIM. When a request for target 3 - * comes in we fetch the softc with devclass_get_softc(target_id). - * - * The SIM is the highest target number. This makes sure that umass0 corresponds - * to target 0 on the USB SCSI bus. - */ -#ifndef UMASS_DEBUG -#define UMASS_SCSIID_MAX 32 /* maximum number of drives expected */ -#else -/* while debugging avoid unnecessary clutter in the output at umass_cam_rescan - * (XPT_PATH_INQ) - */ -#define UMASS_SCSIID_MAX 3 /* maximum number of drives expected */ -#endif -#define UMASS_SCSIID_HOST UMASS_SCSIID_MAX -#endif - -#define MS_TO_TICKS(ms) ((ms) * hz / 1000) - - -/* Bulk-Only features */ - -#define UR_BBB_RESET 0xff /* Bulk-Only reset */ -#define UR_BBB_GET_MAX_LUN 0xfe - -/* Command Block Wrapper */ -typedef struct { - uDWord dCBWSignature; -# define CBWSIGNATURE 0x43425355 - uDWord dCBWTag; - uDWord dCBWDataTransferLength; - uByte bCBWFlags; -# define CBWFLAGS_OUT 0x00 -# define CBWFLAGS_IN 0x80 - uByte bCBWLUN; - uByte bCDBLength; -# define CBWCDBLENGTH 16 - uByte CBWCDB[CBWCDBLENGTH]; -} umass_bbb_cbw_t; -#define UMASS_BBB_CBW_SIZE 31 - -/* Command Status Wrapper */ -typedef struct { - uDWord dCSWSignature; -# define CSWSIGNATURE 0x53425355 - uDWord dCSWTag; - uDWord dCSWDataResidue; - uByte bCSWStatus; -# define CSWSTATUS_GOOD 0x0 -# define CSWSTATUS_FAILED 0x1 -# define CSWSTATUS_PHASE 0x2 -} umass_bbb_csw_t; -#define UMASS_BBB_CSW_SIZE 13 - -/* CBI features */ - -#define UR_CBI_ADSC 0x00 - -typedef unsigned char umass_cbi_cbl_t[16]; /* Command block */ - -typedef union { - struct { - unsigned char type; - #define IDB_TYPE_CCI 0x00 - unsigned char value; - #define IDB_VALUE_PASS 0x00 - #define IDB_VALUE_FAIL 0x01 - #define IDB_VALUE_PHASE 0x02 - #define IDB_VALUE_PERSISTENT 0x03 - #define IDB_VALUE_STATUS_MASK 0x03 - } common; - - struct { - unsigned char asc; - unsigned char ascq; - } ufi; -} umass_cbi_sbl_t; - - - -struct umass_softc; /* see below */ - -typedef void (*transfer_cb_f)(struct umass_softc *sc, void *priv, - int residue, int status); -#define STATUS_CMD_OK 0 /* everything ok */ -#define STATUS_CMD_UNKNOWN 1 /* will have to fetch sense */ -#define STATUS_CMD_FAILED 2 /* transfer was ok, command failed */ -#define STATUS_WIRE_FAILED 3 /* couldn't even get command across */ - -typedef void (*wire_reset_f)(struct umass_softc *sc, int status); -typedef void (*wire_transfer_f)(struct umass_softc *sc, int lun, - void *cmd, int cmdlen, void *data, int datalen, - int dir, transfer_cb_f cb, void *priv); -typedef void (*wire_state_f)(usbd_xfer_handle xfer, - usbd_private_handle priv, usbd_status err); - -#if defined(__FreeBSD__) -typedef int (*command_transform_f)(struct umass_softc *sc, - unsigned char *cmd, int cmdlen, - unsigned char **rcmd, int *rcmdlen); -#endif - - -/* the per device structure */ -struct umass_softc { - USBBASEDEVICE sc_dev; /* base device */ - usbd_device_handle sc_udev; /* device */ - - unsigned char drive; -# define DRIVE_GENERIC 0 /* use defaults for this one */ -# define ZIP_100 1 /* to be used for quirks */ -# define ZIP_250 2 -# define SHUTTLE_EUSB 3 -# define INSYSTEM_USBCABLE 4 - unsigned char quirks; - /* The drive does not support Test Unit Ready. Convert to - * Start Unit. - * Y-E Data - * ZIP 100 - */ -# define NO_TEST_UNIT_READY 0x01 - /* The drive does not reset the Unit Attention state after - * REQUEST SENSE has been sent. The INQUIRY command does not reset - * the UA either, and so CAM runs in circles trying to retrieve the - * initial INQUIRY data. - * Y-E Data - */ -# define RS_NO_CLEAR_UA 0x02 /* no REQUEST SENSE on INQUIRY*/ - /* The drive does not support START_STOP. - * Shuttle E-USB - */ -# define NO_START_STOP 0x04 - /* Don't ask for full inquiry data (255 bytes). - * Yano ATAPI-USB - */ -# define FORCE_SHORT_INQUIRY 0x08 - - unsigned int proto; -# define PROTO_UNKNOWN 0x0000 /* unknown protocol */ -# define PROTO_BBB 0x0001 /* USB wire protocol */ -# define PROTO_CBI 0x0002 -# define PROTO_CBI_I 0x0004 -# define PROTO_WIRE 0x00ff /* USB wire protocol mask */ -# define PROTO_SCSI 0x0100 /* command protocol */ -# define PROTO_ATAPI 0x0200 -# define PROTO_UFI 0x0400 -# define PROTO_RBC 0x0800 -# define PROTO_COMMAND 0xff00 /* command protocol mask */ - - u_char subclass; /* interface subclass */ - u_char protocol; /* interface protocol */ - - usbd_interface_handle iface; /* Mass Storage interface */ - int ifaceno; /* MS iface number */ - - u_int8_t bulkin; /* bulk-in Endpoint Address */ - u_int8_t bulkout; /* bulk-out Endpoint Address */ - u_int8_t intrin; /* intr-in Endp. (CBI) */ - usbd_pipe_handle bulkin_pipe; - usbd_pipe_handle bulkout_pipe; - usbd_pipe_handle intrin_pipe; - - /* Reset the device in a wire protocol specific way */ - wire_reset_f reset; - - /* The start of a wire transfer. It prepares the whole transfer (cmd, - * data, and status stage) and initiates it. It is up to the state - * machine (below) to handle the various stages and errors in these - */ - wire_transfer_f transfer; - - /* The state machine, handling the various states during a transfer */ - wire_state_f state; - -#if defined(__FreeBSD__) - /* The command transform function is used to conver the SCSI commands - * into their derivatives, like UFI, ATAPI, and friends. - */ - command_transform_f transform; /* command transform */ -#endif - - /* Bulk specific variables for transfers in progress */ - umass_bbb_cbw_t cbw; /* command block wrapper */ - umass_bbb_csw_t csw; /* command status wrapper*/ - /* CBI specific variables for transfers in progress */ - umass_cbi_cbl_t cbl; /* command block */ - umass_cbi_sbl_t sbl; /* status block */ - - /* generic variables for transfers in progress */ - /* ctrl transfer requests */ - usb_device_request_t request; - - /* xfer handles - * Most of our operations are initiated from interrupt context, so - * we need to avoid using the one that is in use. We want to avoid - * allocating them in the interrupt context as well. - */ - /* indices into array below */ -# define XFER_BBB_CBW 0 /* Bulk-Only */ -# define XFER_BBB_DATA 1 -# define XFER_BBB_DCLEAR 2 -# define XFER_BBB_CSW1 3 -# define XFER_BBB_CSW2 4 -# define XFER_BBB_SCLEAR 5 -# define XFER_BBB_RESET1 6 -# define XFER_BBB_RESET2 7 -# define XFER_BBB_RESET3 8 - -# define XFER_CBI_CB 0 /* CBI */ -# define XFER_CBI_DATA 1 -# define XFER_CBI_STATUS 2 -# define XFER_CBI_DCLEAR 3 -# define XFER_CBI_SCLEAR 4 -# define XFER_CBI_RESET1 5 -# define XFER_CBI_RESET2 6 -# define XFER_CBI_RESET3 7 - -# define XFER_NR 9 /* maximum number */ - - usbd_xfer_handle transfer_xfer[XFER_NR]; /* for ctrl xfers */ - - void *data_buffer; - - int transfer_dir; /* data direction */ - void *transfer_data; /* data buffer */ - int transfer_datalen; /* (maximum) length */ - int transfer_actlen; /* actual length */ - transfer_cb_f transfer_cb; /* callback */ - void *transfer_priv; /* for callback */ - int transfer_status; - - int transfer_state; -# define TSTATE_IDLE 0 -# define TSTATE_BBB_COMMAND 1 /* CBW transfer */ -# define TSTATE_BBB_DATA 2 /* Data transfer */ -# define TSTATE_BBB_DCLEAR 3 /* clear endpt stall */ -# define TSTATE_BBB_STATUS1 4 /* clear endpt stall */ -# define TSTATE_BBB_SCLEAR 5 /* clear endpt stall */ -# define TSTATE_BBB_STATUS2 6 /* CSW transfer */ -# define TSTATE_BBB_RESET1 7 /* reset command */ -# define TSTATE_BBB_RESET2 8 /* in clear stall */ -# define TSTATE_BBB_RESET3 9 /* out clear stall */ -# define TSTATE_CBI_COMMAND 10 /* command transfer */ -# define TSTATE_CBI_DATA 11 /* data transfer */ -# define TSTATE_CBI_STATUS 12 /* status transfer */ -# define TSTATE_CBI_DCLEAR 13 /* clear ep stall */ -# define TSTATE_CBI_SCLEAR 14 /* clear ep stall */ -# define TSTATE_CBI_RESET1 15 /* reset command */ -# define TSTATE_CBI_RESET2 16 /* in clear stall */ -# define TSTATE_CBI_RESET3 17 /* out clear stall */ -# define TSTATE_STATES 18 /* # of states above */ - - - int transfer_speed; /* in kb/s */ - int timeout; /* in msecs */ - - u_int8_t maxlun; /* max lun supported */ - -#ifdef UMASS_DEBUG - struct timeval tv; -#endif - -#if defined(__FreeBSD__) - /* SCSI/CAM specific variables */ - struct scsi_sense cam_scsi_sense; - -#elif defined(__NetBSD__) || defined(__OpenBSD__) - union { - struct scsipi_link sc_link; -#if defined(__NetBSD__) - struct { - struct ata_atapi_attach sc_aa; - struct ata_drive_datas sc_aa_drive; - } aa; -#endif - } u; -#if defined(__NetBSD__) - struct atapi_adapter sc_atapi_adapter; -#define sc_adapter sc_atapi_adapter._generic -#else - struct scsi_adapter sc_atapi_adapter; -#define sc_adapter sc_atapi_adapter -#endif - int sc_xfer_flags; - usbd_status sc_sync_status; - struct scsipi_sense sc_sense_cmd; - - device_ptr_t sc_child; /* child device, for detach */ - char sc_dying; - -#endif -}; - -#ifdef UMASS_DEBUG char *states[TSTATE_STATES+1] = { /* should be kept in sync with the list at transfer_state */ "Idle", @@ -538,17 +158,9 @@ char *states[TSTATE_STATES+1] = { }; #endif -struct cam_sim *umass_sim; /* SCSI Interface Module */ -struct cam_path *umass_path; /* and its path */ - - /* USB device probe/attach/detach functions */ USB_DECLARE_DRIVER(umass); Static void umass_disco(struct umass_softc *sc); -Static int umass_match_proto(struct umass_softc *sc, - usbd_interface_handle iface, - usbd_device_handle dev); -Static void umass_init_shuttle(struct umass_softc *sc); /* generic transfer functions */ Static usbd_status umass_setup_transfer(struct umass_softc *sc, @@ -556,113 +168,42 @@ Static usbd_status umass_setup_transfer(struct umass_softc *sc, void *buffer, int buflen, int flags, usbd_xfer_handle xfer); Static usbd_status umass_setup_ctrl_transfer(struct umass_softc *sc, - usbd_device_handle dev, usb_device_request_t *req, void *buffer, int buflen, int flags, usbd_xfer_handle xfer); -Static void umass_clear_endpoint_stall(struct umass_softc *sc, - u_int8_t endpt, usbd_pipe_handle pipe, - int state, usbd_xfer_handle xfer); +Static void umass_clear_endpoint_stall(struct umass_softc *sc, int endpt, + usbd_xfer_handle xfer); #if 0 -Static void umass_reset(struct umass_softc *sc, - transfer_cb_f cb, void *priv); +Static void umass_reset(struct umass_softc *sc, transfer_cb_f cb, void *priv); #endif /* Bulk-Only related functions */ -Static void umass_bbb_reset(struct umass_softc *sc, int status); -Static void umass_bbb_transfer(struct umass_softc *sc, int lun, - void *cmd, int cmdlen, - void *data, int datalen, int dir, - transfer_cb_f cb, void *priv); -Static void umass_bbb_state(usbd_xfer_handle xfer, - usbd_private_handle priv, - usbd_status err); -usbd_status umass_bbb_get_max_lun(struct umass_softc *sc, - u_int8_t *maxlun); +Static void umass_bbb_transfer(struct umass_softc *, int, void *, int, void *, + int, int, u_int, umass_callback, void *); +Static void umass_bbb_reset(struct umass_softc *, int); +Static void umass_bbb_state(usbd_xfer_handle, usbd_private_handle, usbd_status); +usbd_status umass_bbb_get_max_lun(struct umass_softc *, u_int8_t *); /* CBI related functions */ -Static int umass_cbi_adsc(struct umass_softc *sc, char *buffer,int buflen, - usbd_xfer_handle xfer); -Static void umass_cbi_reset(struct umass_softc *sc, int status); -Static void umass_cbi_transfer(struct umass_softc *sc, int lun, - void *cmd, int cmdlen, - void *data, int datalen, int dir, - transfer_cb_f cb, void *priv); -Static void umass_cbi_state(usbd_xfer_handle xfer, - usbd_private_handle priv, usbd_status err); - -#if defined(__FreeBSD__) -/* CAM related functions */ -Static void umass_cam_action(struct cam_sim *sim, union ccb *ccb); -Static void umass_cam_poll(struct cam_sim *sim); - -Static void umass_cam_cb(struct umass_softc *sc, void *priv, - int residue, int status); -Static void umass_cam_sense_cb(struct umass_softc *sc, void *priv, - int residue, int status); - -#ifdef UMASS_DO_CAM_RESCAN -Static void umass_cam_rescan(struct umass_softc *sc); -#endif - -Static int umass_cam_attach_sim(void); -Static int umass_cam_attach(struct umass_softc *sc); -Static int umass_cam_detach_sim(void); -Static int umass_cam_detach(struct umass_softc *sc); - -#elif defined(__NetBSD__) || defined(__OpenBSD__) +Static void umass_cbi_transfer(struct umass_softc *, int, void *, int, void *, + int, int, u_int, umass_callback, void *); +Static void umass_cbi_reset(struct umass_softc *, int); +Static void umass_cbi_state(usbd_xfer_handle, usbd_private_handle, usbd_status); -#define UMASS_SCSIID_HOST 0x00 -#define UMASS_SCSIID_DEVICE 0x01 +Static int umass_cbi_adsc(struct umass_softc *, char *, int, usbd_xfer_handle); -#define UMASS_ATAPI_DRIVE 0 - -#define UMASS_MAX_TRANSFER_SIZE MAXBSIZE - -struct scsipi_device umass_dev = -{ - NULL, /* Use default error handler */ - NULL, /* have a queue, served by this */ - NULL, /* have no async handler */ - NULL, /* Use default 'done' routine */ +const struct umass_wire_methods umass_bbb_methods = { + umass_bbb_transfer, + umass_bbb_reset, + umass_bbb_state }; -Static int umass_scsipi_cmd(struct scsipi_xfer *xs); -Static void umass_scsipi_minphys(struct buf *bp); -Static int umass_scsipi_ioctl(struct scsipi_link *, u_long, - caddr_t, int, struct proc *); -Static void umass_scsipi_cb(struct umass_softc *sc, void *priv, - int residue, int status); -Static void umass_scsipi_sense_cb(struct umass_softc *sc, void *priv, - int residue, int status); - -Static int scsipiprint(void *aux, const char *pnp); -Static int umass_ufi_transform(struct umass_softc *sc, - struct scsipi_generic *cmd, int cmdlen, - struct scsipi_generic *rcmd, int *rcmdlen); - -#if NATAPIBUS > 0 -Static void umass_atapi_probedev(struct atapibus_softc *, int); -#endif -#endif - -#if defined(__FreeBSD__) -/* SCSI specific functions */ -Static int umass_scsi_transform(struct umass_softc *sc, - unsigned char *cmd, int cmdlen, - unsigned char **rcmd, int *rcmdlen); - -/* UFI specific functions */ -Static int umass_ufi_transform(struct umass_softc *sc, - unsigned char *cmd, int cmdlen, - unsigned char **rcmd, int *rcmdlen); - -/* 8070 specific functions */ -Static int umass_8070_transform(struct umass_softc *sc, - unsigned char *cmd, int cmdlen, - unsigned char **rcmd, int *rcmdlen); -#endif +const struct umass_wire_methods umass_cbi_methods = { + umass_cbi_transfer, + umass_cbi_reset, + umass_cbi_state +}; #ifdef UMASS_DEBUG /* General debugging functions */ @@ -675,292 +216,180 @@ Static void umass_dump_buffer(struct umass_softc *sc, u_int8_t *buffer, #endif -void usbd_clear_endpoint_toggle(usbd_pipe_handle pipe); /* XXXXX */ - /* * USB device probe/attach/detach */ -/* - * Match the device we are seeing with the devices supported. Fill in the - * proto and drive fields in the softc accordingly. - * This function is called from both probe and attach. - */ - -Static int -umass_match_proto(sc, iface, dev) - struct umass_softc *sc; - usbd_interface_handle iface; - usbd_device_handle dev; +USB_MATCH(umass) { - usb_device_descriptor_t *dd; + USB_MATCH_START(umass, uaa); + const struct umass_quirk *quirk; usb_interface_descriptor_t *id; - u_int vendor, product; - /* - * Fill in sc->drive and sc->proto and return a match - * value if both are determined and 0 otherwise. - */ - - sc->drive = DRIVE_GENERIC; - sc->proto = PROTO_UNKNOWN; - sc->transfer_speed = UMASS_DEFAULT_TRANSFER_SPEED; - - sc->sc_udev = dev; - dd = usbd_get_device_descriptor(dev); - vendor = UGETW(dd->idVendor); - product = UGETW(dd->idProduct); - - if (vendor == USB_VENDOR_SHUTTLE && - product == USB_PRODUCT_SHUTTLE_EUSB) { - sc->drive = SHUTTLE_EUSB; -#if CBI_I - sc->proto = PROTO_ATAPI | PROTO_CBI_I; -#else - sc->proto = PROTO_ATAPI | PROTO_CBI; -#endif - sc->subclass = UISUBCLASS_SFF8020I; - sc->protocol = UIPROTO_MASS_CBI; - sc->quirks |= NO_TEST_UNIT_READY | NO_START_STOP; - return (UMATCH_VENDOR_PRODUCT); - } - - if (vendor == USB_VENDOR_YANO && - product == USB_PRODUCT_YANO_U640MO) { - sc->proto = PROTO_ATAPI | PROTO_CBI_I; - sc->quirks |= FORCE_SHORT_INQUIRY; - return (UMATCH_VENDOR_PRODUCT); - } - - if (vendor == USB_VENDOR_SONY && - product == USB_PRODUCT_SONY_MSC) { - printf ("XXX Sony MSC\n"); - sc->quirks |= FORCE_SHORT_INQUIRY; - } - - if (vendor == USB_VENDOR_YEDATA && - product == USB_PRODUCT_YEDATA_FLASHBUSTERU) { - - /* Revisions < 1.28 do not handle the interrupt endpoint - * very well. - */ - if (UGETW(dd->bcdDevice) < 0x128) - sc->proto = PROTO_UFI | PROTO_CBI; - else -#if CBI_I - sc->proto = PROTO_UFI | PROTO_CBI_I; -#else - sc->proto = PROTO_UFI | PROTO_CBI; -#endif - /* - * Revisions < 1.28 do not have the TEST UNIT READY command - * Revisions == 1.28 have a broken TEST UNIT READY - */ - if (UGETW(dd->bcdDevice) <= 0x128) - sc->quirks |= NO_TEST_UNIT_READY; - - sc->subclass = UISUBCLASS_UFI; - sc->protocol = UIPROTO_MASS_CBI; - - sc->quirks |= RS_NO_CLEAR_UA; - sc->transfer_speed = UMASS_FLOPPY_TRANSFER_SPEED; - return (UMATCH_VENDOR_PRODUCT_REV); - } + if (uaa->iface == NULL) + return (UMATCH_NONE); - if (vendor == USB_VENDOR_INSYSTEM && - product == USB_PRODUCT_INSYSTEM_USBCABLE) { - sc->drive = INSYSTEM_USBCABLE; - sc->proto = PROTO_ATAPI | PROTO_CBI; - sc->quirks |= NO_TEST_UNIT_READY | NO_START_STOP; - return (UMATCH_VENDOR_PRODUCT); - } + quirk = umass_lookup(uaa->vendor, uaa->product); + if (quirk != NULL) + return (quirk->uq_match); - id = usbd_get_interface_descriptor(iface); + id = usbd_get_interface_descriptor(uaa->iface); if (id == NULL || id->bInterfaceClass != UICLASS_MASS) return (UMATCH_NONE); - if (vendor == USB_VENDOR_SONY && id->bInterfaceSubClass == 0xff) { - /* - * Sony DSC devices set the sub class to 0xff - * instead of 1 (RBC). Fix that here. - */ - id->bInterfaceSubClass = UISUBCLASS_RBC; - /* They also should be able to do higher speed. */ - sc->transfer_speed = 500; - } - - if (vendor == USB_VENDOR_FUJIPHOTO && - product == USB_PRODUCT_FUJIPHOTO_MASS0100) - sc->quirks |= NO_TEST_UNIT_READY | NO_START_STOP; - - sc->subclass = id->bInterfaceSubClass; - sc->protocol = id->bInterfaceProtocol; - - switch (sc->subclass) { - case UISUBCLASS_SCSI: - sc->proto |= PROTO_SCSI; - break; - case UISUBCLASS_UFI: - sc->transfer_speed = UMASS_FLOPPY_TRANSFER_SPEED; - sc->proto |= PROTO_UFI; - break; + switch (id->bInterfaceSubClass) { + case UISUBCLASS_RBC: case UISUBCLASS_SFF8020I: - case UISUBCLASS_SFF8070I: case UISUBCLASS_QIC157: - sc->proto |= PROTO_ATAPI; - break; - case UISUBCLASS_RBC: - sc->proto |= PROTO_RBC; + case UISUBCLASS_UFI: + case UISUBCLASS_SFF8070I: + case UISUBCLASS_SCSI: break; default: - /* Assume that unsupported devices are ATAPI */ - DPRINTF(UDMASS_GEN, ("%s: Unsupported command protocol %d\n", - USBDEVNAME(sc->sc_dev), id->bInterfaceSubClass)); - - sc->proto |= PROTO_ATAPI; - break; + return (UMATCH_IFACECLASS); } - switch (sc->protocol) { - case UIPROTO_MASS_CBI: - sc->proto |= PROTO_CBI; - break; + switch (id->bInterfaceProtocol) { case UIPROTO_MASS_CBI_I: -#if CBI_I - sc->proto |= PROTO_CBI_I; -#else - sc->proto |= PROTO_CBI; -#endif - break; + case UIPROTO_MASS_CBI: + case UIPROTO_MASS_BBB_OLD: case UIPROTO_MASS_BBB: - sc->proto |= PROTO_BBB; - break; - case UIPROTO_MASS_BBB_P: - sc->drive = ZIP_100; - sc->proto |= PROTO_BBB; - sc->transfer_speed = UMASS_ZIP100_TRANSFER_SPEED; - sc->quirks |= NO_TEST_UNIT_READY; break; default: - DPRINTF(UDMASS_GEN, ("%s: Unsupported wire protocol %d\n", - USBDEVNAME(sc->sc_dev), id->bInterfaceProtocol)); - return (UMATCH_NONE); + return (UMATCH_IFACECLASS_IFACESUBCLASS); } - return (UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO); + return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO); } -USB_MATCH(umass) -{ - USB_MATCH_START(umass, uaa); -#if defined(__FreeBSD__) - struct umass_softc *sc = device_get_softc(self); -#elif defined(__NetBSD__) || defined(__OpenBSD__) - struct umass_softc scs, *sc = &scs; - memset(sc, 0, sizeof *sc); - strcpy(sc->sc_dev.dv_xname, "umass"); -#endif - - if (uaa->iface == NULL) - return(UMATCH_NONE); - - return (umass_match_proto(sc, uaa->iface, uaa->device)); -} - -void umass_delayed_attach(struct umass_softc *sc); - USB_ATTACH(umass) { USB_ATTACH_START(umass, sc, uaa); + const struct umass_quirk *quirk; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; - const char *sSubclass, *sProto; + const char *sWire, *sCommand; char devinfo[1024]; - int i, bno; - int err; - - /* - * the softc struct is bzero-ed in device_set_driver. We can safely - * call umass_detach without specifically initialising the struct. - */ + usbd_status err; + int i, bno, error; usbd_devinfo(uaa->device, 0, devinfo); USB_ATTACH_SETUP; - sc->iface = uaa->iface; - sc->ifaceno = uaa->ifaceno; + sc->sc_udev = uaa->device; + sc->sc_iface = uaa->iface; + sc->sc_ifaceno = uaa->ifaceno; + + quirk = umass_lookup(uaa->vendor, uaa->product); + if (quirk != NULL) { + sc->sc_wire = quirk->uq_wire; + sc->sc_cmd = quirk->uq_cmd; + sc->sc_quirks = quirk->uq_flags; + sc->sc_busquirks = quirk->uq_busquirks; + + if (quirk->uq_fixup != NULL) + (*quirk->uq_fixup)(sc); + } else { + sc->sc_wire = UMASS_WPROTO_UNSPEC; + sc->sc_cmd = UMASS_CPROTO_UNSPEC; + sc->sc_quirks = 0; + sc->sc_busquirks = 0; + } - /* initialise the proto and drive values in the umass_softc (again) */ - if (umass_match_proto(sc, sc->iface, uaa->device) == 0) { - printf("%s: match failed\n", USBDEVNAME(sc->sc_dev)); + id = usbd_get_interface_descriptor(sc->sc_iface); + if (id == NULL) USB_ATTACH_ERROR_RETURN; + + if (sc->sc_wire == UMASS_WPROTO_UNSPEC) { + switch (id->bInterfaceProtocol) { + case UIPROTO_MASS_CBI: + sc->sc_wire = UMASS_WPROTO_CBI; + break; + case UIPROTO_MASS_CBI_I: + sc->sc_wire = UMASS_WPROTO_CBI_I; + break; + case UIPROTO_MASS_BBB: + case UIPROTO_MASS_BBB_OLD: + sc->sc_wire = UMASS_WPROTO_BBB; + break; + default: + DPRINTF(UDMASS_GEN, + ("%s: Unsupported wire protocol %u\n", + USBDEVNAME(sc->sc_dev), + id->bInterfaceProtocol)); + USB_ATTACH_ERROR_RETURN; + } } - /* - * The timeout is based on the maximum expected transfer size - * divided by the expected transfer speed. - * We multiply by 4 to make sure a busy system doesn't make things - * fail. - */ - sc->timeout = 4 * UMASS_MAX_TRANSFER_SIZE / sc->transfer_speed; - sc->timeout += UMASS_SPINUP_TIME; /* allow for spinning up */ + /* XXX - Now unsupported CBI with CCI */ + if (sc->sc_wire == UMASS_WPROTO_CBI_I) + sc->sc_wire = UMASS_WPROTO_CBI; + + if (sc->sc_cmd == UMASS_CPROTO_UNSPEC) { + switch (id->bInterfaceSubClass) { + case UISUBCLASS_SCSI: + sc->sc_cmd = UMASS_CPROTO_SCSI; + break; + case UISUBCLASS_UFI: + sc->sc_cmd = UMASS_CPROTO_UFI; + break; + case UISUBCLASS_SFF8020I: + case UISUBCLASS_SFF8070I: + case UISUBCLASS_QIC157: + sc->sc_cmd = UMASS_CPROTO_ATAPI; + break; + case UISUBCLASS_RBC: + sc->sc_cmd = UMASS_CPROTO_RBC; + break; + default: + DPRINTF(UDMASS_GEN, + ("%s: Unsupported command protocol %u\n", + USBDEVNAME(sc->sc_dev), + id->bInterfaceSubClass)); + USB_ATTACH_ERROR_RETURN; + } + } - id = usbd_get_interface_descriptor(sc->iface); printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo); - switch (sc->subclass) { - case UISUBCLASS_RBC: - sSubclass = "RBC"; - break; - case UISUBCLASS_SCSI: - sSubclass = "SCSI"; - break; - case UISUBCLASS_UFI: - sSubclass = "UFI"; + switch (sc->sc_wire) { + case UMASS_WPROTO_CBI: + sWire = "CBI"; break; - case UISUBCLASS_SFF8020I: - sSubclass = "SFF8020i"; + case UMASS_WPROTO_CBI_I: + sWire = "CBI with CCI"; break; - case UISUBCLASS_SFF8070I: - sSubclass = "SFF8070i"; - break; - case UISUBCLASS_QIC157: - sSubclass = "QIC157"; + case UMASS_WPROTO_BBB: + sWire = "Bulk-Only"; break; default: - sSubclass = "unknown"; + sWire = "unknown"; break; } - switch (sc->protocol) { - case UIPROTO_MASS_CBI: - sProto = "CBI"; + + switch (sc->sc_cmd) { + case UMASS_CPROTO_RBC: + sCommand = "RBC"; break; - case UIPROTO_MASS_CBI_I: - sProto = "CBI-I"; + case UMASS_CPROTO_SCSI: + sCommand = "SCSI"; break; - case UIPROTO_MASS_BBB: - sProto = "BBB"; + case UMASS_CPROTO_UFI: + sCommand = "UFI"; + break; + case UMASS_CPROTO_ATAPI: + sCommand = "ATAPI"; break; - case UIPROTO_MASS_BBB_P: - sProto = "BBB-P"; + case UMASS_CPROTO_ISD_ATA: + sCommand = "ISD-ATA"; break; default: - sProto = "unknown"; + sCommand = "unknown"; break; } - printf("%s: using %s over %s\n", USBDEVNAME(sc->sc_dev), sSubclass, - sProto); - if (sc->drive == INSYSTEM_USBCABLE) { - err = usbd_set_interface(0, 1); - if (err) { - DPRINTF(UDMASS_USB, ("%s: could not switch to " - "Alt Interface %d\n", - USBDEVNAME(sc->sc_dev), 1)); - umass_disco(sc); - USB_ATTACH_ERROR_RETURN; - } - } + printf("%s: using %s over %s\n", USBDEVNAME(sc->sc_dev), sCommand, + sWire); /* * In addition to the Control endpoint the following endpoints @@ -974,22 +403,22 @@ USB_ATTACH(umass) * from the device descriptors of the current interface. */ for (i = 0 ; i < id->bNumEndpoints ; i++) { - ed = usbd_interface2endpoint_descriptor(sc->iface, i); - if (!ed) { + ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); + if (ed == NULL) { printf("%s: could not read endpoint descriptor\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { - sc->bulkin = ed->bEndpointAddress; + sc->sc_epaddr[UMASS_BULKIN] = ed->bEndpointAddress; } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { - sc->bulkout = ed->bEndpointAddress; - } else if (sc->proto & PROTO_CBI_I + sc->sc_epaddr[UMASS_BULKOUT] = ed->bEndpointAddress; + } else if (sc->sc_wire == UMASS_WPROTO_CBI_I && UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && (ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) { - sc->intrin = ed->bEndpointAddress; + sc->sc_epaddr[UMASS_INTRIN] = ed->bEndpointAddress; #ifdef UMASS_DEBUG if (UGETW(ed->wMaxPacketSize) > 2) { DPRINTF(UDMASS_CBI, ("%s: intr size is %d\n", @@ -1001,19 +430,21 @@ USB_ATTACH(umass) } /* check whether we found all the endpoints we need */ - if (!sc->bulkin || !sc->bulkout - || (sc->proto & PROTO_CBI_I && !sc->intrin) ) { - DPRINTF(UDMASS_USB, ("%s: endpoint not found %d/%d/%d\n", - USBDEVNAME(sc->sc_dev), - sc->bulkin, sc->bulkout, sc->intrin)); - umass_disco(sc); + if (!sc->sc_epaddr[UMASS_BULKIN] || !sc->sc_epaddr[UMASS_BULKOUT] || + (sc->sc_wire == UMASS_WPROTO_CBI_I && + !sc->sc_epaddr[UMASS_INTRIN])) { + DPRINTF(UDMASS_USB, ("%s: endpoint not found %u/%u/%u\n", + USBDEVNAME(sc->sc_dev), sc->sc_epaddr[UMASS_BULKIN], + sc->sc_epaddr[UMASS_BULKOUT], + sc->sc_epaddr[UMASS_INTRIN])); USB_ATTACH_ERROR_RETURN; } /* * Get the maximum LUN supported by the device. */ - if ((sc->proto & PROTO_WIRE) == PROTO_BBB) { + if (sc->sc_wire == UMASS_WPROTO_BBB && + !(sc->sc_quirks & UMASS_QUIRK_NO_MAX_LUN)) { err = umass_bbb_get_max_lun(sc, &sc->maxlun); if (err) { printf("%s: unable to get Max Lun: %s\n", @@ -1025,19 +456,20 @@ USB_ATTACH(umass) } /* Open the bulk-in and -out pipe */ - err = usbd_open_pipe(sc->iface, sc->bulkout, - USBD_EXCLUSIVE_USE, &sc->bulkout_pipe); + err = usbd_open_pipe(sc->sc_iface, sc->sc_epaddr[UMASS_BULKOUT], + USBD_EXCLUSIVE_USE, + &sc->sc_pipe[UMASS_BULKOUT]); if (err) { - DPRINTF(UDMASS_USB, ("%s: cannot open %d-out pipe (bulk)\n", - USBDEVNAME(sc->sc_dev), sc->bulkout)); + DPRINTF(UDMASS_USB, ("%s: cannot open %u-out pipe (bulk)\n", + USBDEVNAME(sc->sc_dev), sc->sc_epaddr[UMASS_BULKOUT])); umass_disco(sc); USB_ATTACH_ERROR_RETURN; } - err = usbd_open_pipe(sc->iface, sc->bulkin, - USBD_EXCLUSIVE_USE, &sc->bulkin_pipe); + err = usbd_open_pipe(sc->sc_iface, sc->sc_epaddr[UMASS_BULKIN], + USBD_EXCLUSIVE_USE, &sc->sc_pipe[UMASS_BULKIN]); if (err) { - DPRINTF(UDMASS_USB, ("%s: could not open %d-in pipe (bulk)\n", - USBDEVNAME(sc->sc_dev), sc->bulkin)); + DPRINTF(UDMASS_USB, ("%s: could not open %u-in pipe (bulk)\n", + USBDEVNAME(sc->sc_dev), sc->sc_epaddr[UMASS_BULKIN])); umass_disco(sc); USB_ATTACH_ERROR_RETURN; } @@ -1053,12 +485,13 @@ USB_ATTACH(umass) * code for handling the data on that endpoint simpler. No data * arriving concurrently. */ - if (sc->proto & PROTO_CBI_I) { - err = usbd_open_pipe(sc->iface, sc->intrin, - USBD_EXCLUSIVE_USE, &sc->intrin_pipe); + if (sc->sc_wire == UMASS_WPROTO_CBI_I) { + err = usbd_open_pipe(sc->sc_iface, sc->sc_epaddr[UMASS_INTRIN], + USBD_EXCLUSIVE_USE, &sc->sc_pipe[UMASS_INTRIN]); if (err) { - DPRINTF(UDMASS_USB, ("%s: couldn't open %d-in (intr)\n", - USBDEVNAME(sc->sc_dev), sc->intrin)); + DPRINTF(UDMASS_USB, ("%s: couldn't open %u-in (intr)\n", + USBDEVNAME(sc->sc_dev), + sc->sc_epaddr[UMASS_INTRIN])); umass_disco(sc); USB_ATTACH_ERROR_RETURN; } @@ -1078,14 +511,14 @@ USB_ATTACH(umass) } } /* Allocate buffer for data transfer (it's huge). */ - switch (sc->proto & PROTO_WIRE) { - case PROTO_BBB: + switch (sc->sc_wire) { + case UMASS_WPROTO_BBB: bno = XFER_BBB_DATA; goto dalloc; - case PROTO_CBI: + case UMASS_WPROTO_CBI: bno = XFER_CBI_DATA; goto dalloc; - case PROTO_CBI_I: + case UMASS_WPROTO_CBI_I: bno = XFER_CBI_DATA; dalloc: sc->data_buffer = usbd_alloc_buffer(sc->transfer_xfer[bno], @@ -1100,123 +533,89 @@ USB_ATTACH(umass) } /* Initialise the wire protocol specific methods */ - if (sc->proto & PROTO_BBB) { - sc->reset = umass_bbb_reset; - sc->transfer = umass_bbb_transfer; - sc->state = umass_bbb_state; - } else if ((sc->proto & PROTO_CBI) || (sc->proto & PROTO_CBI_I)) { - sc->reset = umass_cbi_reset; - sc->transfer = umass_cbi_transfer; - sc->state = umass_cbi_state; -#ifdef UMASS_DEBUG - } else { - panic("%s:%d: Unknown proto 0x%02x\n", - __FILE__, __LINE__, sc->proto); -#endif - } - - if (sc->drive == SHUTTLE_EUSB) - umass_init_shuttle(sc); - - /* - * Fill in the adapter. - */ - sc->sc_adapter.scsipi_cmd = umass_scsipi_cmd; - sc->sc_adapter.scsipi_minphys = umass_scsipi_minphys; - - /* - * fill in the prototype scsipi_link. - */ - switch (sc->proto & PROTO_COMMAND) { - case PROTO_SCSI: - case PROTO_UFI: - case PROTO_ATAPI: - case PROTO_RBC: - if ((sc->proto & PROTO_COMMAND) != PROTO_SCSI) - sc->u.sc_link.flags |= SDEV_ATAPI; - else - sc->u.sc_link.flags &= ~SDEV_ATAPI; - - sc->u.sc_link.adapter_buswidth = 2; - sc->u.sc_link.adapter_target = UMASS_SCSIID_HOST; - sc->u.sc_link.luns = sc->maxlun + 1; - - sc->u.sc_link.adapter_softc = sc; - sc->u.sc_link.adapter = &sc->sc_adapter; - sc->u.sc_link.device = &umass_dev; - sc->u.sc_link.openings = 1; - - if(sc->quirks & NO_TEST_UNIT_READY) - sc->u.sc_link.quirks |= ADEV_NOTUR; + switch (sc->sc_wire) { + case UMASS_WPROTO_BBB: + sc->sc_methods = &umass_bbb_methods; + break; + case UMASS_WPROTO_CBI: + case UMASS_WPROTO_CBI_I: + sc->sc_methods = &umass_cbi_methods; break; - - default: - printf("%s: proto=0x%x not supported yet\n", - USBDEVNAME(sc->sc_dev), sc->proto); umass_disco(sc); USB_ATTACH_ERROR_RETURN; } - if (cold) { - startuphook_establish((void (*)(void *))umass_delayed_attach, - sc); - } else { - /* hot plug, do it now */ - umass_delayed_attach(sc); + if (quirk != NULL && quirk->uq_init != NULL) { + err = (*quirk->uq_init)(sc); + if (err) { + umass_disco(sc); + USB_ATTACH_ERROR_RETURN; + } } - DPRINTF(UDMASS_GEN, ("%s: Attach finished\n", USBDEVNAME(sc->sc_dev))); + error = 0; + switch (sc->sc_cmd) { + case UMASS_CPROTO_RBC: + case UMASS_CPROTO_SCSI: +#if defined(__OpenBSD__) || NSCSIBUS > 0 + error = umass_scsi_attach(sc); +#else + printf("%s: scsibus not configured\n", USBDEVNAME(sc->sc_dev)); +#endif + break; - USB_ATTACH_SUCCESS_RETURN; -} + case UMASS_CPROTO_UFI: + case UMASS_CPROTO_ATAPI: +#if (NATAPIBUS > 0) || (NATAPISCSI > 0) + error = umass_atapi_attach(sc); +#else + printf("%s: "UMASS_ATAPISTR" not configured\n", + USBDEVNAME(sc->sc_dev)); +#endif + break; -void -umass_delayed_attach(struct umass_softc *sc) -{ - sc->sc_child = config_found(&sc->sc_dev, &sc->u, scsipiprint); - if (sc->sc_child == NULL) { + case UMASS_CPROTO_ISD_ATA: +#if defined (__NetBSD__) && NWD > 0 + error = umass_isdata_attach(sc); +#else + printf("%s: isdata not configured\n", USBDEVNAME(sc->sc_dev)); +#endif + break; + + default: + printf("%s: command protocol=0x%x not supported\n", + USBDEVNAME(sc->sc_dev), sc->sc_cmd); + umass_disco(sc); + USB_ATTACH_ERROR_RETURN; + } + if (error) { + printf("%s: bus attach failed\n", USBDEVNAME(sc->sc_dev)); umass_disco(sc); - /* Not an error, just not a complete success. */ - USB_ATTACH_SUCCESS_RETURN; + USB_ATTACH_ERROR_RETURN; } usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, USBDEV(sc->sc_dev)); -} -Static int -scsipiprint(aux, pnp) - void *aux; - const char *pnp; -{ -#if !defined(__OpenBSD__) - extern int atapi_print(void *aux, const char *pnp); - struct scsipi_link *l = aux; - - if (l->type == BUS_SCSI) - return (scsiprint(aux, pnp)); - else - return (atapi_print(aux, pnp)); -#else - return (scsiprint(aux, pnp)); -#endif + DPRINTF(UDMASS_GEN, ("%s: Attach finished\n", USBDEVNAME(sc->sc_dev))); + + USB_ATTACH_SUCCESS_RETURN; } USB_DETACH(umass) { USB_DETACH_START(umass, sc); - int rv = 0; + struct umassbus_softc *scbus = sc->bus; + int rv = 0, i; DPRINTF(UDMASS_USB, ("%s: detached\n", USBDEVNAME(sc->sc_dev))); /* Abort the pipes to wake up any waiting processes. */ - if (sc->bulkout_pipe != NULL) - usbd_abort_pipe(sc->bulkout_pipe); - if (sc->bulkin_pipe != NULL) - usbd_abort_pipe(sc->bulkin_pipe); - if (sc->intrin_pipe != NULL) - usbd_abort_pipe(sc->intrin_pipe); + for (i = 0 ; i < UMASS_NEP ; i++) { + if (sc->sc_pipe[i] != NULL) + usbd_abort_pipe(sc->sc_pipe[i]); + } #if 0 /* Do we really need reference counting? Perhaps in ioctl() */ @@ -1228,16 +627,13 @@ USB_DETACH(umass) splx(s); #endif -#if defined(__FreeBSD__) - if ((sc->proto & PROTO_SCSI) || - (sc->proto & PROTO_ATAPI) || - (sc->proto & PROTO_UFI)) - /* detach the device from the SCSI host controller (SIM) */ - rv = umass_cam_detach(sc); -#elif defined(__NetBSD__) || defined(__OpenBSD__) - if (sc->sc_child != NULL) - rv = config_detach(sc->sc_child, flags); -#endif + if (scbus != NULL) { + if (scbus->sc_child != NULL) + rv = config_detach(scbus->sc_child, flags); + free(scbus, M_DEVBUF); + sc->bus = NULL; + } + if (rv != 0) return (rv); @@ -1246,16 +642,14 @@ USB_DETACH(umass) usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, USBDEV(sc->sc_dev)); - return (0); + return (rv); } -#if defined(__NetBSD__) || defined(__OpenBSD__) int -umass_activate(self, act) - struct device *self; - enum devact act; +umass_activate(struct device *dev, enum devact act) { - struct umass_softc *sc = (struct umass_softc *) self; + struct umass_softc *sc = (struct umass_softc *)dev; + struct umassbus_softc *scbus = sc->bus; int rv = 0; DPRINTF(UDMASS_USB, ("%s: umass_activate: %d\n", @@ -1267,22 +661,19 @@ umass_activate(self, act) break; case DVACT_DEACTIVATE: - if (sc->sc_child == NULL) + sc->sc_dying = 1; + if (scbus == NULL || scbus->sc_child == NULL) break; - rv = config_deactivate(sc->sc_child); + rv = config_deactivate(scbus->sc_child); DPRINTF(UDMASS_USB, ("%s: umass_activate: child " "returned %d\n", USBDEVNAME(sc->sc_dev), rv)); - if (rv == 0) - sc->sc_dying = 1; break; } return (rv); } -#endif Static void -umass_disco(sc) - struct umass_softc *sc; +umass_disco(struct umass_softc *sc) { int i; @@ -1296,27 +687,10 @@ umass_disco(sc) } /* Remove all the pipes. */ - if (sc->bulkout_pipe != NULL) - usbd_close_pipe(sc->bulkout_pipe); - if (sc->bulkin_pipe != NULL) - usbd_close_pipe(sc->bulkin_pipe); - if (sc->intrin_pipe != NULL) - usbd_close_pipe(sc->intrin_pipe); -} - -Static void -umass_init_shuttle(struct umass_softc *sc) -{ - usb_device_request_t req; - u_char status[2]; - - /* The Linux driver does this */ - req.bmRequestType = UT_READ_VENDOR_DEVICE; - req.bRequest = 1; - USETW(req.wValue, 0); - USETW(req.wIndex, sc->ifaceno); - USETW(req.wLength, sizeof status); - (void)usbd_do_request(sc->sc_udev, &req, &status); + for (i = 0 ; i < UMASS_NEP ; i++) { + if (sc->sc_pipe[i] != NULL) + usbd_close_pipe(sc->sc_pipe[i]); + } } /* @@ -1336,7 +710,7 @@ umass_setup_transfer(struct umass_softc *sc, usbd_pipe_handle pipe, /* Initialiase a USB transfer and then schedule it */ usbd_setup_xfer(xfer, pipe, (void *)sc, buffer, buflen, - flags | sc->sc_xfer_flags, sc->timeout, sc->state); + flags | sc->sc_xfer_flags, sc->timeout, sc->sc_methods->wire_state); err = usbd_transfer(xfer); DPRINTF(UDMASS_XFER,("%s: start xfer buffer=%p buflen=%d flags=0x%x " @@ -1353,10 +727,8 @@ umass_setup_transfer(struct umass_softc *sc, usbd_pipe_handle pipe, Static usbd_status -umass_setup_ctrl_transfer(struct umass_softc *sc, usbd_device_handle dev, - usb_device_request_t *req, - void *buffer, int buflen, int flags, - usbd_xfer_handle xfer) +umass_setup_ctrl_transfer(struct umass_softc *sc, usb_device_request_t *req, + void *buffer, int buflen, int flags, usbd_xfer_handle xfer) { usbd_status err; @@ -1365,8 +737,8 @@ umass_setup_ctrl_transfer(struct umass_softc *sc, usbd_device_handle dev, /* Initialiase a USB control transfer and then schedule it */ - usbd_setup_default_xfer(xfer, dev, (void *) sc, - sc->timeout, req, buffer, buflen, flags, sc->state); + usbd_setup_default_xfer(xfer, sc->sc_udev, (void *) sc, sc->timeout, + req, buffer, buflen, flags, sc->sc_methods->wire_state); err = usbd_transfer(xfer); if (err && err != USBD_IN_PROGRESS) { @@ -1381,30 +753,23 @@ umass_setup_ctrl_transfer(struct umass_softc *sc, usbd_device_handle dev, } Static void -umass_clear_endpoint_stall(struct umass_softc *sc, - u_int8_t endpt, usbd_pipe_handle pipe, - int state, usbd_xfer_handle xfer) +umass_clear_endpoint_stall(struct umass_softc *sc, int endpt, + usbd_xfer_handle xfer) { - usbd_device_handle dev; - if (sc->sc_dying) return; DPRINTF(UDMASS_BBB, ("%s: Clear endpoint 0x%02x stall\n", - USBDEVNAME(sc->sc_dev), endpt)); - - usbd_interface2device_handle(sc->iface, &dev); + USBDEVNAME(sc->sc_dev), sc->sc_epaddr[endpt])); - sc->transfer_state = state; + usbd_clear_endpoint_toggle(sc->sc_pipe[endpt]); - usbd_clear_endpoint_toggle(pipe); - - sc->request.bmRequestType = UT_WRITE_ENDPOINT; - sc->request.bRequest = UR_CLEAR_FEATURE; - USETW(sc->request.wValue, UF_ENDPOINT_HALT); - USETW(sc->request.wIndex, endpt); - USETW(sc->request.wLength, 0); - umass_setup_ctrl_transfer(sc, dev, &sc->request, NULL, 0, 0, xfer); + sc->sc_req.bmRequestType = UT_WRITE_ENDPOINT; + sc->sc_req.bRequest = UR_CLEAR_FEATURE; + USETW(sc->sc_req.wValue, UF_ENDPOINT_HALT); + USETW(sc->sc_req.wIndex, sc->sc_epaddr[endpt]); + USETW(sc->sc_req.wLength, 0); + umass_setup_ctrl_transfer(sc, &sc->sc_req, NULL, 0, 0, xfer); } #if 0 @@ -1426,10 +791,9 @@ umass_reset(struct umass_softc *sc, transfer_cb_f cb, void *priv) Static void umass_bbb_reset(struct umass_softc *sc, int status) { - usbd_device_handle dev; - - KASSERT(sc->proto & PROTO_BBB, - ("sc->proto == 0x%02x wrong for umass_bbb_reset\n", sc->proto)); + KASSERT(sc->sc_wire & UMASS_WPROTO_BBB, + ("sc->sc_wire == 0x%02x wrong for umass_bbb_reset\n", + sc->sc_wire)); if (sc->sc_dying) return; @@ -1456,31 +820,32 @@ umass_bbb_reset(struct umass_softc *sc, int status) sc->transfer_state = TSTATE_BBB_RESET1; sc->transfer_status = status; - usbd_interface2device_handle(sc->iface, &dev); - /* reset is a class specific interface write */ - sc->request.bmRequestType = UT_WRITE_CLASS_INTERFACE; - sc->request.bRequest = UR_BBB_RESET; - USETW(sc->request.wValue, 0); - USETW(sc->request.wIndex, sc->ifaceno); - USETW(sc->request.wLength, 0); - umass_setup_ctrl_transfer(sc, dev, &sc->request, NULL, 0, 0, + sc->sc_req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + sc->sc_req.bRequest = UR_BBB_RESET; + USETW(sc->sc_req.wValue, 0); + USETW(sc->sc_req.wIndex, sc->sc_ifaceno); + USETW(sc->sc_req.wLength, 0); + umass_setup_ctrl_transfer(sc, &sc->sc_req, NULL, 0, 0, sc->transfer_xfer[XFER_BBB_RESET1]); } Static void umass_bbb_transfer(struct umass_softc *sc, int lun, void *cmd, int cmdlen, - void *data, int datalen, int dir, - transfer_cb_f cb, void *priv) + void *data, int datalen, int dir, u_int timeout, + umass_callback cb, void *priv) { static int dCBWtag = 42; /* unique for CBW of transfer */ DPRINTF(UDMASS_BBB,("%s: umass_bbb_transfer cmd=0x%02x\n", USBDEVNAME(sc->sc_dev), *(u_char *)cmd)); - KASSERT(sc->proto & PROTO_BBB, - ("sc->proto == 0x%02x wrong for umass_bbb_transfer\n", - sc->proto)); + KASSERT(sc->sc_wire & UMASS_WPROTO_BBB, + ("sc->sc_wire == 0x%02x wrong for umass_bbb_transfer\n", + sc->sc_wire)); + + /* Be a little generous. */ + sc->timeout = timeout + USBD_DEFAULT_TIMEOUT; /* * Do a Bulk-Only transfer with cmdlen bytes from cmd, possibly @@ -1552,7 +917,7 @@ umass_bbb_transfer(struct umass_softc *sc, int lun, void *cmd, int cmdlen, sc->cbw.bCBWFlags = (dir == DIR_IN? CBWFLAGS_IN:CBWFLAGS_OUT); sc->cbw.bCBWLUN = lun; sc->cbw.bCDBLength = cmdlen; - bcopy(cmd, sc->cbw.CBWCDB, cmdlen); + memcpy(sc->cbw.CBWCDB, cmd, cmdlen); DIF(UDMASS_BBB, umass_bbb_dump_cbw(sc, &sc->cbw)); @@ -1569,7 +934,7 @@ umass_bbb_transfer(struct umass_softc *sc, int lun, void *cmd, int cmdlen, sc->transfer_state = TSTATE_BBB_COMMAND; /* Send the CBW from host to device via bulk-out endpoint. */ - if (umass_setup_transfer(sc, sc->bulkout_pipe, + if (umass_setup_transfer(sc, sc->sc_pipe[UMASS_BULKOUT], &sc->cbw, UMASS_BBB_CBW_SIZE, 0, sc->transfer_xfer[XFER_BBB_CBW])) { umass_bbb_reset(sc, STATUS_WIRE_FAILED); @@ -1584,8 +949,9 @@ umass_bbb_state(usbd_xfer_handle xfer, usbd_private_handle priv, struct umass_softc *sc = (struct umass_softc *) priv; usbd_xfer_handle next_xfer; - KASSERT(sc->proto & PROTO_BBB, - ("sc->proto == 0x%02x wrong for umass_bbb_state\n",sc->proto)); + KASSERT(sc->sc_wire & UMASS_WPROTO_BBB, + ("sc->sc_wire == 0x%02x wrong for umass_bbb_state\n", + sc->sc_wire)); if (sc->sc_dying) return; @@ -1624,7 +990,7 @@ umass_bbb_state(usbd_xfer_handle xfer, usbd_private_handle priv, /* Data transport phase, setup transfer */ sc->transfer_state = TSTATE_BBB_DATA; if (sc->transfer_dir == DIR_IN) { - if (umass_setup_transfer(sc, sc->bulkin_pipe, + if (umass_setup_transfer(sc, sc->sc_pipe[UMASS_BULKIN], sc->data_buffer, sc->transfer_datalen, USBD_SHORT_XFER_OK | USBD_NO_COPY, sc->transfer_xfer[XFER_BBB_DATA])) @@ -1634,7 +1000,7 @@ umass_bbb_state(usbd_xfer_handle xfer, usbd_private_handle priv, } else if (sc->transfer_dir == DIR_OUT) { memcpy(sc->data_buffer, sc->transfer_data, sc->transfer_datalen); - if (umass_setup_transfer(sc, sc->bulkout_pipe, + if (umass_setup_transfer(sc, sc->sc_pipe[UMASS_BULKOUT], sc->data_buffer, sc->transfer_datalen, USBD_NO_COPY,/* fixed length transfer */ sc->transfer_xfer[XFER_BBB_DATA])) @@ -1656,18 +1022,16 @@ umass_bbb_state(usbd_xfer_handle xfer, usbd_private_handle priv, &sc->transfer_actlen, NULL); if (err) { - DPRINTF(UDMASS_BBB, ("%s: Data-%s %db failed, " + DPRINTF(UDMASS_BBB, ("%s: Data-%s %d failed, " "%s\n", USBDEVNAME(sc->sc_dev), (sc->transfer_dir == DIR_IN?"in":"out"), sc->transfer_datalen,usbd_errstr(err))); if (err == USBD_STALLED) { + sc->transfer_state = TSTATE_BBB_DCLEAR; umass_clear_endpoint_stall(sc, (sc->transfer_dir == DIR_IN? - sc->bulkin:sc->bulkout), - (sc->transfer_dir == DIR_IN? - sc->bulkin_pipe:sc->bulkout_pipe), - TSTATE_BBB_DCLEAR, + UMASS_BULKIN:UMASS_BULKOUT), sc->transfer_xfer[XFER_BBB_DCLEAR]); return; } else { @@ -1723,9 +1087,8 @@ umass_bbb_state(usbd_xfer_handle xfer, usbd_private_handle priv, } /* Read the Command Status Wrapper via bulk-in endpoint. */ - if (umass_setup_transfer(sc, sc->bulkin_pipe, - &sc->csw, UMASS_BBB_CSW_SIZE, 0, - next_xfer)) { + if (umass_setup_transfer(sc, sc->sc_pipe[UMASS_BULKIN], + &sc->csw, UMASS_BBB_CSW_SIZE, 0, next_xfer)) { umass_bbb_reset(sc, STATUS_WIRE_FAILED); return; } @@ -1744,10 +1107,9 @@ umass_bbb_state(usbd_xfer_handle xfer, usbd_private_handle priv, * retry it, otherwise fail. */ if (sc->transfer_state == TSTATE_BBB_STATUS1) { - umass_clear_endpoint_stall(sc, - sc->bulkin, sc->bulkin_pipe, - TSTATE_BBB_SCLEAR, - sc->transfer_xfer[XFER_BBB_SCLEAR]); + sc->transfer_state = TSTATE_BBB_SCLEAR; + umass_clear_endpoint_stall(sc, UMASS_BULKIN, + sc->transfer_xfer[XFER_BBB_SCLEAR]); return; } else { umass_bbb_reset(sc, STATUS_WIRE_FAILED); @@ -1757,6 +1119,11 @@ umass_bbb_state(usbd_xfer_handle xfer, usbd_private_handle priv, DIF(UDMASS_BBB, umass_bbb_dump_csw(sc, &sc->csw)); + /* Translate weird command-status signatures. */ + if ((sc->sc_quirks & UMASS_QUIRK_WRONG_CSWSIG) && + UGETDW(sc->csw.dCSWSignature) == CSWSIGNATURE_OLYMPUS_C1) + USETDW(sc->csw.dCSWSignature, CSWSIGNATURE); + /* Check CSW and handle any error */ if (UGETDW(sc->csw.dCSWSignature) != CSWSIGNATURE) { /* Invalid CSW: Wrong signature or wrong tag might @@ -1801,9 +1168,8 @@ umass_bbb_state(usbd_xfer_handle xfer, usbd_private_handle priv, panic("%s: transferred %d bytes instead of %d bytes\n", USBDEVNAME(sc->sc_dev), sc->transfer_actlen, sc->transfer_datalen); - } #if 0 - else if (sc->transfer_datalen - sc->transfer_actlen + } else if (sc->transfer_datalen - sc->transfer_actlen != UGETDW(sc->csw.dCSWDataResidue)) { DPRINTF(UDMASS_BBB, ("%s: actlen=%d != residue=%d\n", USBDEVNAME(sc->sc_dev), @@ -1812,10 +1178,8 @@ umass_bbb_state(usbd_xfer_handle xfer, usbd_private_handle priv, umass_bbb_reset(sc, STATUS_WIRE_FAILED); return; - - } #endif - else if (sc->csw.bCSWStatus == CSWSTATUS_FAILED) { + } else if (sc->csw.bCSWStatus == CSWSTATUS_FAILED) { DPRINTF(UDMASS_BBB, ("%s: Command Failed, res = %d\n", USBDEVNAME(sc->sc_dev), UGETDW(sc->csw.dCSWDataResidue))); @@ -1843,8 +1207,8 @@ umass_bbb_state(usbd_xfer_handle xfer, usbd_private_handle priv, printf("%s: BBB reset failed, %s\n", USBDEVNAME(sc->sc_dev), usbd_errstr(err)); - umass_clear_endpoint_stall(sc, - sc->bulkin, sc->bulkin_pipe, TSTATE_BBB_RESET2, + sc->transfer_state = TSTATE_BBB_RESET2; + umass_clear_endpoint_stall(sc, UMASS_BULKIN, sc->transfer_xfer[XFER_BBB_RESET2]); return; @@ -1854,8 +1218,8 @@ umass_bbb_state(usbd_xfer_handle xfer, usbd_private_handle priv, USBDEVNAME(sc->sc_dev), usbd_errstr(err)); /* no error recovery, otherwise we end up in a loop */ - umass_clear_endpoint_stall(sc, - sc->bulkout, sc->bulkout_pipe, TSTATE_BBB_RESET3, + sc->transfer_state = TSTATE_BBB_RESET3; + umass_clear_endpoint_stall(sc, UMASS_BULKOUT, sc->transfer_xfer[XFER_BBB_RESET3]); return; @@ -1889,19 +1253,16 @@ Static int umass_cbi_adsc(struct umass_softc *sc, char *buffer, int buflen, usbd_xfer_handle xfer) { - usbd_device_handle dev; - - KASSERT(sc->proto & (PROTO_CBI|PROTO_CBI_I), - ("sc->proto == 0x%02x wrong for umass_cbi_adsc\n",sc->proto)); - - usbd_interface2device_handle(sc->iface, &dev); - - sc->request.bmRequestType = UT_WRITE_CLASS_INTERFACE; - sc->request.bRequest = UR_CBI_ADSC; - USETW(sc->request.wValue, 0); - USETW(sc->request.wIndex, sc->ifaceno); - USETW(sc->request.wLength, buflen); - return umass_setup_ctrl_transfer(sc, dev, &sc->request, buffer, + KASSERT(sc->sc_wire & (UMASS_WPROTO_CBI|UMASS_WPROTO_CBI_I), + ("sc->sc_wire == 0x%02x wrong for umass_cbi_adsc\n", + sc->sc_wire)); + + sc->sc_req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + sc->sc_req.bRequest = UR_CBI_ADSC; + USETW(sc->sc_req.wValue, 0); + USETW(sc->sc_req.wIndex, sc->sc_ifaceno); + USETW(sc->sc_req.wLength, buflen); + return umass_setup_ctrl_transfer(sc, &sc->sc_req, buffer, buflen, 0, xfer); } @@ -1912,8 +1273,9 @@ umass_cbi_reset(struct umass_softc *sc, int status) int i; # define SEND_DIAGNOSTIC_CMDLEN 12 - KASSERT(sc->proto & (PROTO_CBI|PROTO_CBI_I), - ("sc->proto == 0x%02x wrong for umass_cbi_reset\n",sc->proto)); + KASSERT(sc->sc_wire & (UMASS_WPROTO_CBI|UMASS_WPROTO_CBI_I), + ("sc->sc_wire == 0x%02x wrong for umass_cbi_reset\n", + sc->sc_wire)); if (sc->sc_dying) return; @@ -1959,19 +1321,22 @@ umass_cbi_reset(struct umass_softc *sc, int status) Static void umass_cbi_transfer(struct umass_softc *sc, int lun, - void *cmd, int cmdlen, void *data, int datalen, int dir, - transfer_cb_f cb, void *priv) + void *cmd, int cmdlen, void *data, int datalen, int dir, + u_int timeout, umass_callback cb, void *priv) { DPRINTF(UDMASS_CBI,("%s: umass_cbi_transfer cmd=0x%02x, len=%d\n", USBDEVNAME(sc->sc_dev), *(u_char *)cmd, datalen)); - KASSERT(sc->proto & (PROTO_CBI|PROTO_CBI_I), - ("sc->proto == 0x%02x wrong for umass_cbi_transfer\n", - sc->proto)); + KASSERT(sc->sc_wire & (UMASS_WPROTO_CBI|UMASS_WPROTO_CBI_I), + ("sc->sc_wire == 0x%02x wrong for umass_cbi_transfer\n", + sc->sc_wire)); if (sc->sc_dying) return; + /* Be a little generous. */ + sc->timeout = timeout + USBD_DEFAULT_TIMEOUT; + /* * Do a CBI transfer with cmdlen bytes from cmd, possibly * a data phase of datalen bytes from/to the device and finally a @@ -2019,8 +1384,9 @@ umass_cbi_state(usbd_xfer_handle xfer, usbd_private_handle priv, { struct umass_softc *sc = (struct umass_softc *) priv; - KASSERT(sc->proto & (PROTO_CBI|PROTO_CBI_I), - ("sc->proto == 0x%02x wrong for umass_cbi_state\n", sc->proto)); + KASSERT(sc->sc_wire & (UMASS_WPROTO_CBI|UMASS_WPROTO_CBI_I), + ("sc->sc_wire == 0x%02x wrong for umass_cbi_state\n", + sc->sc_wire)); if (sc->sc_dying) return; @@ -2043,10 +1409,10 @@ umass_cbi_state(usbd_xfer_handle xfer, usbd_private_handle priv, /* Status transport by control pipe (section 2.3.2.1). * The command contained in the command block failed. * - * The control pipe has already been unstalled by the - * USB stack. - * Section 2.4.3.1.1 states that the bulk in endpoints - * should not stalled at this point. + * The control pipe has already been unstalled by the + * USB stack. + * Section 2.4.3.1.1 states that the bulk in endpoints + * should not stalled at this point. */ sc->transfer_state = TSTATE_IDLE; @@ -2065,7 +1431,7 @@ umass_cbi_state(usbd_xfer_handle xfer, usbd_private_handle priv, sc->transfer_state = TSTATE_CBI_DATA; if (sc->transfer_dir == DIR_IN) { - if (umass_setup_transfer(sc, sc->bulkin_pipe, + if (umass_setup_transfer(sc, sc->sc_pipe[UMASS_BULKIN], sc->transfer_data, sc->transfer_datalen, USBD_SHORT_XFER_OK | USBD_NO_COPY, sc->transfer_xfer[XFER_CBI_DATA])) @@ -2074,17 +1440,17 @@ umass_cbi_state(usbd_xfer_handle xfer, usbd_private_handle priv, } else if (sc->transfer_dir == DIR_OUT) { memcpy(sc->data_buffer, sc->transfer_data, sc->transfer_datalen); - if (umass_setup_transfer(sc, sc->bulkout_pipe, + if (umass_setup_transfer(sc, sc->sc_pipe[UMASS_BULKOUT], sc->transfer_data, sc->transfer_datalen, USBD_NO_COPY,/* fixed length transfer */ sc->transfer_xfer[XFER_CBI_DATA])) umass_cbi_reset(sc, STATUS_WIRE_FAILED); - } else if (sc->proto & PROTO_CBI_I) { + } else if (sc->sc_wire == UMASS_WPROTO_CBI_I) { DPRINTF(UDMASS_CBI, ("%s: no data phase\n", USBDEVNAME(sc->sc_dev))); sc->transfer_state = TSTATE_CBI_STATUS; - if (umass_setup_transfer(sc, sc->intrin_pipe, + if (umass_setup_transfer(sc, sc->sc_pipe[UMASS_INTRIN], &sc->sbl, sizeof(sc->sbl), 0, /* fixed length transfer */ sc->transfer_xfer[XFER_CBI_STATUS])){ @@ -2110,15 +1476,14 @@ umass_cbi_state(usbd_xfer_handle xfer, usbd_private_handle priv, USBDEVNAME(sc->sc_dev), sc->transfer_actlen)); if (err) { - DPRINTF(UDMASS_CBI, ("%s: Data-%s %db failed, " + DPRINTF(UDMASS_CBI, ("%s: Data-%s %d failed, " "%s\n", USBDEVNAME(sc->sc_dev), (sc->transfer_dir == DIR_IN?"in":"out"), sc->transfer_datalen,usbd_errstr(err))); if (err == USBD_STALLED) { - umass_clear_endpoint_stall(sc, - sc->bulkin, sc->bulkin_pipe, - TSTATE_CBI_DCLEAR, + sc->transfer_state = TSTATE_CBI_DCLEAR; + umass_clear_endpoint_stall(sc, UMASS_BULKIN, sc->transfer_xfer[XFER_CBI_DCLEAR]); } else { umass_cbi_reset(sc, STATUS_WIRE_FAILED); @@ -2134,10 +1499,10 @@ umass_cbi_state(usbd_xfer_handle xfer, usbd_private_handle priv, umass_dump_buffer(sc, sc->transfer_data, sc->transfer_actlen, 48)); - if (sc->proto & PROTO_CBI_I) { + if (sc->sc_wire == UMASS_WPROTO_CBI_I) { sc->transfer_state = TSTATE_CBI_STATUS; memset(&sc->sbl, 0, sizeof(sc->sbl)); - if (umass_setup_transfer(sc, sc->intrin_pipe, + if (umass_setup_transfer(sc, sc->sc_pipe[UMASS_INTRIN], &sc->sbl, sizeof(sc->sbl), 0, /* fixed length transfer */ sc->transfer_xfer[XFER_CBI_STATUS])){ @@ -2162,9 +1527,8 @@ umass_cbi_state(usbd_xfer_handle xfer, usbd_private_handle priv, */ if (err == USBD_STALLED) { - umass_clear_endpoint_stall(sc, - sc->intrin, sc->intrin_pipe, - TSTATE_CBI_SCLEAR, + sc->transfer_state = TSTATE_CBI_SCLEAR; + umass_clear_endpoint_stall(sc, UMASS_INTRIN, sc->transfer_xfer[XFER_CBI_SCLEAR]); } else { umass_cbi_reset(sc, STATUS_WIRE_FAILED); @@ -2174,7 +1538,7 @@ umass_cbi_state(usbd_xfer_handle xfer, usbd_private_handle priv, /* Dissect the information in the buffer */ - if (sc->proto & PROTO_UFI) { + if (sc->sc_cmd == UMASS_CPROTO_UFI) { int status; /* Section 3.4.3.1.3 specifies that the UFI command @@ -2250,8 +1614,8 @@ umass_cbi_state(usbd_xfer_handle xfer, usbd_private_handle priv, printf("%s: CBI reset failed, %s\n", USBDEVNAME(sc->sc_dev), usbd_errstr(err)); - umass_clear_endpoint_stall(sc, - sc->bulkin, sc->bulkin_pipe, TSTATE_CBI_RESET2, + sc->transfer_state = TSTATE_CBI_RESET2; + umass_clear_endpoint_stall(sc, UMASS_BULKIN, sc->transfer_xfer[XFER_CBI_RESET2]); return; @@ -2261,8 +1625,8 @@ umass_cbi_state(usbd_xfer_handle xfer, usbd_private_handle priv, USBDEVNAME(sc->sc_dev), usbd_errstr(err)); /* no error recovery, otherwise we end up in a loop */ - umass_clear_endpoint_stall(sc, - sc->bulkout, sc->bulkout_pipe, TSTATE_CBI_RESET3, + sc->transfer_state = TSTATE_CBI_RESET3; + umass_clear_endpoint_stall(sc, UMASS_BULKOUT, sc->transfer_xfer[XFER_CBI_RESET3]); return; @@ -2292,26 +1656,21 @@ umass_cbi_state(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status umass_bbb_get_max_lun(struct umass_softc *sc, u_int8_t *maxlun) { - usbd_device_handle dev; usb_device_request_t req; usbd_status err; - usb_interface_descriptor_t *id; *maxlun = 0; /* Default to 0. */ DPRINTF(UDMASS_BBB, ("%s: Get Max Lun\n", USBDEVNAME(sc->sc_dev))); - usbd_interface2device_handle(sc->iface, &dev); - id = usbd_get_interface_descriptor(sc->iface); - /* The Get Max Lun command is a class-specific request. */ req.bmRequestType = UT_READ_CLASS_INTERFACE; req.bRequest = UR_BBB_GET_MAX_LUN; USETW(req.wValue, 0); - USETW(req.wIndex, id->bInterfaceNumber); + USETW(req.wIndex, sc->sc_ifaceno); USETW(req.wLength, 1); - err = usbd_do_request(dev, &req, maxlun); + err = usbd_do_request(sc->sc_udev, &req, maxlun); switch (err) { case USBD_NORMAL_COMPLETION: DPRINTF(UDMASS_BBB, ("%s: Max Lun %d\n", @@ -2348,706 +1707,6 @@ umass_bbb_get_max_lun(struct umass_softc *sc, u_int8_t *maxlun) -#if defined(__FreeBSD__) -/* - * CAM specific functions (used by SCSI, UFI, 8070) - */ - -Static int -umass_cam_attach_sim() -{ - struct cam_devq *devq; /* Per device Queue */ - - /* A HBA is attached to the CAM layer. - * - * The CAM layer will then after a while start probing for - * devices on the bus. The number of devices is limitted to one. - */ - - /* SCSI transparent command set */ - - devq = cam_simq_alloc(1 /*maximum openings*/); - if (devq == NULL) - return(ENOMEM); - - umass_sim = cam_sim_alloc(umass_cam_action, umass_cam_poll, DEVNAME, - NULL /*priv*/, 0 /*unit number*/, - 1 /*maximum device openings*/, - 0 /*maximum tagged device openings*/, - devq); - if (umass_sim == NULL) { - cam_simq_free(devq); - return(ENOMEM); - } - - if(xpt_bus_register(umass_sim, 0) != CAM_SUCCESS) - return(ENOMEM); - - if (xpt_create_path(&umass_path, NULL, cam_sim_path(umass_sim), - UMASS_SCSIID_HOST, 0) - != CAM_REQ_CMP) - return(ENOMEM); - - return(0); -} - -#ifdef UMASS_DO_CAM_RESCAN -/* this function is only used from umass_cam_rescan, so mention - * prototype down here. - */ -Static void umass_cam_rescan_callback(struct cam_periph *periph,union ccb *ccb); - -Static void -umass_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb) -{ -#ifdef UMASS_DEBUG - struct umass_softc *sc = devclass_get_softc(umass_devclass, - ccb->ccb_h.target_id); - - if (ccb->ccb_h.status != CAM_REQ_CMP) { - DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d: Rescan failed, 0x%04x\n", - USBDEVNAME(sc->sc_dev), UMASS_SCSI_BUS, - ccb->ccb_h.target_id, ccb->ccb_h.target_lun, - ccb->ccb_h.status)); - } else { - DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d: Rescan succeeded, freeing resources.\n", - USBDEVNAME(sc->sc_dev), UMASS_SCSI_BUS, - ccb->ccb_h.target_id, ccb->ccb_h.target_lun)); - } -#endif - - xpt_free_path(ccb->ccb_h.path); - free(ccb, M_USBDEV); -} - -Static void -umass_cam_rescan(struct umass_softc *sc) -{ - struct cam_path *path; - union ccb *ccb = malloc(sizeof(union ccb), M_USBDEV, M_WAITOK); - - memset(ccb, 0, sizeof(union ccb)); - - DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d: scanning bus for new device %d\n", - USBDEVNAME(sc->sc_dev), cam_sim_path(umass_sim), - device_get_unit(sc->sc_dev), 0, - device_get_unit(sc->sc_dev))); - - if (xpt_create_path(&path, xpt_periph, cam_sim_path(umass_sim), - device_get_unit(sc->sc_dev), 0) - != CAM_REQ_CMP) - return; - - xpt_setup_ccb(&ccb->ccb_h, path, 5/*priority (low)*/); - ccb->ccb_h.func_code = XPT_SCAN_BUS; - ccb->ccb_h.cbfcnp = umass_cam_rescan_callback; - ccb->crcn.flags = CAM_FLAG_NONE; - xpt_action(ccb); - - /* The scan is in progress now. */ -} -#endif - -Static int -umass_cam_attach(struct umass_softc *sc) -{ - /* SIM already attached at module load. The device is a target on the - * one SIM we registered: target device_get_unit(self). - */ - - /* The artificial limit UMASS_SCSIID_MAX is there because CAM expects - * a limit to the number of targets that are present on a SIM. - */ - if (device_get_unit(sc->sc_dev) > UMASS_SCSIID_MAX) { - printf("%s: Increase UMASS_SCSIID_MAX (currently %d) in %s " - "and try again.\n", USBDEVNAME(sc->sc_dev), - UMASS_SCSIID_MAX, __FILE__); - return(1); - } - -#ifdef UMASS_DO_CAM_RESCAN - if (!cold) { - /* Notify CAM of the new device. Any failure is benign, as the - * user can still do it by hand (camcontrol rescan <busno>). - * Only do this if we are not booting, because CAM does a scan - * after booting has completed, when interrupts have been - * enabled. - */ - umass_cam_rescan(sc); - } -#endif - - return(0); /* always succesful */ -} - -/* umass_cam_detach - * detach from the CAM layer - */ - -Static int -umass_cam_detach_sim() -{ - if (umass_sim) - return(EBUSY); /* XXX CAM can't handle disappearing SIMs yet */ - - if (umass_path) { - /* XXX do we need to send an asynchroneous event for the SIM? - xpt_async(AC_LOST_DEVICE, umass_path, NULL); - */ - xpt_free_path(umass_path); - umass_path = NULL; - } - - if (umass_sim) { - if (xpt_bus_deregister(cam_sim_path(umass_sim))) - cam_sim_free(umass_sim, /*free_devq*/TRUE); - else - return(EBUSY); - - umass_sim = NULL; - } - - return(0); -} - -Static int -umass_cam_detach(struct umass_softc *sc) -{ - struct cam_path *path; - - /* detach of sim not done until module unload */ - DPRINTF(UDMASS_SCSI, ("%s: losing CAM device entry\n", - USBDEVNAME(sc->sc_dev))); - - if (xpt_create_path(&path, NULL, cam_sim_path(umass_sim), - device_get_unit(sc->sc_dev), CAM_LUN_WILDCARD) - != CAM_REQ_CMP) - return(ENOMEM); - xpt_async(AC_LOST_DEVICE, path, NULL); - xpt_free_path(path); - - return(0); -} - - - -/* umass_cam_action - * CAM requests for action come through here - */ - -Static void -umass_cam_action(struct cam_sim *sim, union ccb *ccb) -{ - struct umass_softc *sc = devclass_get_softc(umass_devclass, - ccb->ccb_h.target_id); - - /* The softc is still there, but marked as going away. umass_cam_detach - * has not yet notified CAM of the lost device however. - */ - if (sc && sc->sc_dying) { - DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:func_code 0x%04x: " - "Invalid target (gone)\n", - USBDEVNAME(sc->sc_dev), UMASS_SCSI_BUS, - ccb->ccb_h.target_id, ccb->ccb_h.target_lun, - ccb->ccb_h.func_code)); - ccb->ccb_h.status = CAM_TID_INVALID; - xpt_done(ccb); - return; - } - - /* Verify, depending on the operation to perform, that we either got a - * valid sc, because an existing target was referenced, or otherwise - * the SIM is addressed. - * - * This avoids bombing out at a printf and does give the CAM layer some - * sensible feedback on errors. - */ - switch (ccb->ccb_h.func_code) { - case XPT_SCSI_IO: - case XPT_RESET_DEV: - case XPT_GET_TRAN_SETTINGS: - case XPT_SET_TRAN_SETTINGS: - case XPT_CALC_GEOMETRY: - /* the opcodes requiring a target. These should never occur. */ - if (sc == NULL) { - printf("%s:%d:%d:%d:func_code 0x%04x: " - "Invalid target\n", - DEVNAME_SIM, UMASS_SCSI_BUS, - ccb->ccb_h.target_id, ccb->ccb_h.target_lun, - ccb->ccb_h.func_code); - - ccb->ccb_h.status = CAM_TID_INVALID; - xpt_done(ccb); - return; - } - break; - case XPT_PATH_INQ: - case XPT_NOOP: - /* The opcodes sometimes aimed at a target (sc is valid), - * sometimes aimed at the SIM (sc is invalid and target is - * CAM_TARGET_WILDCARD) - */ - if (sc == NULL && ccb->ccb_h.target_id != CAM_TARGET_WILDCARD) { - DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:func_code 0x%04x: " - "Invalid target\n", - DEVNAME_SIM, UMASS_SCSI_BUS, - ccb->ccb_h.target_id, ccb->ccb_h.target_lun, - ccb->ccb_h.func_code)); - - ccb->ccb_h.status = CAM_TID_INVALID; - xpt_done(ccb); - return; - } - break; - default: - /* XXX Hm, we should check the input parameters */ - } - - /* Perform the requested action */ - switch (ccb->ccb_h.func_code) { - case XPT_SCSI_IO: - { - struct ccb_scsiio *csio = &ccb->csio; /* deref union */ - int dir; - unsigned char *cmd; - int cmdlen; - - DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_SCSI_IO: " - "cmd: 0x%02x, flags: 0x%02x, " - "%db cmd/%db data/%db sense\n", - USBDEVNAME(sc->sc_dev), UMASS_SCSI_BUS, - ccb->ccb_h.target_id, ccb->ccb_h.target_lun, - csio->cdb_io.cdb_bytes[0], - ccb->ccb_h.flags & CAM_DIR_MASK, - csio->cdb_len, csio->dxfer_len, - csio->sense_len)); - - /* clear the end of the buffer to make sure we don't send out - * garbage. - */ - DIF(UDMASS_SCSI, if ((ccb->ccb_h.flags & CAM_DIR_MASK) - == CAM_DIR_OUT) - umass_dump_buffer(sc, csio->data_ptr, - csio->dxfer_len, 48)); - - if (sc->transfer_state != TSTATE_IDLE) { - DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_SCSI_IO: " - "I/O requested while busy (state %d, %s)\n", - USBDEVNAME(sc->sc_dev), UMASS_SCSI_BUS, - ccb->ccb_h.target_id, ccb->ccb_h.target_lun, - sc->transfer_state,states[sc->transfer_state])); - ccb->ccb_h.status = CAM_SCSI_BUSY; - xpt_done(ccb); - return; - } - - switch(ccb->ccb_h.flags&CAM_DIR_MASK) { - case CAM_DIR_IN: - dir = DIR_IN; - break; - case CAM_DIR_OUT: - dir = DIR_OUT; - break; - default: - dir = DIR_NONE; - } - - ccb->ccb_h.status = CAM_REQ_INPROG | CAM_SIM_QUEUED; - if (sc->transform(sc, csio->cdb_io.cdb_bytes, csio->cdb_len, - &cmd, &cmdlen)) { - sc->transfer(sc, ccb->ccb_h.target_lun, cmd, cmdlen, - csio->data_ptr, - csio->dxfer_len, dir, - umass_cam_cb, (void *) ccb); - } else { - ccb->ccb_h.status = CAM_REQ_INVALID; - xpt_done(ccb); - } - - break; - } - case XPT_PATH_INQ: - { - struct ccb_pathinq *cpi = &ccb->cpi; - - DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_PATH_INQ:.\n", - (sc == NULL? DEVNAME_SIM:USBDEVNAME(sc->sc_dev)), - UMASS_SCSI_BUS, - ccb->ccb_h.target_id, ccb->ccb_h.target_lun)); - - /* host specific information */ - cpi->version_num = 1; - cpi->hba_inquiry = 0; - cpi->target_sprt = 0; - cpi->hba_misc = 0; - cpi->hba_eng_cnt = 0; - cpi->max_target = UMASS_SCSIID_MAX; /* one target */ - cpi->max_lun = 0; /* no LUN's supported */ - cpi->initiator_id = UMASS_SCSIID_HOST; - strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); - strncpy(cpi->hba_vid, "USB SCSI", HBA_IDLEN); - strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); - cpi->unit_number = cam_sim_unit(sim); - cpi->bus_id = UMASS_SCSI_BUS; - if (sc) { - cpi->base_transfer_speed = sc->transfer_speed; - cpi->max_lun = sc->maxlun; - } - - cpi->ccb_h.status = CAM_REQ_CMP; - xpt_done(ccb); - break; - } - case XPT_RESET_DEV: - { - DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_RESET_DEV:.\n", - USBDEVNAME(sc->sc_dev), UMASS_SCSI_BUS, - ccb->ccb_h.target_id, ccb->ccb_h.target_lun)); - - ccb->ccb_h.status = CAM_REQ_INPROG; - umass_reset(sc, umass_cam_cb, (void *) ccb); - break; - } - case XPT_GET_TRAN_SETTINGS: - { - struct ccb_trans_settings *cts = &ccb->cts; - - DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_GET_TRAN_SETTINGS:.\n", - USBDEVNAME(sc->sc_dev), UMASS_SCSI_BUS, - ccb->ccb_h.target_id, ccb->ccb_h.target_lun)); - - cts->valid = 0; - cts->flags = 0; /* no disconnection, tagging */ - - ccb->ccb_h.status = CAM_REQ_CMP; - xpt_done(ccb); - break; - } - case XPT_SET_TRAN_SETTINGS: - { - DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_SET_TRAN_SETTINGS:.\n", - USBDEVNAME(sc->sc_dev), UMASS_SCSI_BUS, - ccb->ccb_h.target_id, ccb->ccb_h.target_lun)); - - ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; - xpt_done(ccb); - break; - } - case XPT_CALC_GEOMETRY: - { - struct ccb_calc_geometry *ccg = &ccb->ccg; - - DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_CALC_GEOMETRY: " - "Volume size = %d\n", - USBDEVNAME(sc->sc_dev), UMASS_SCSI_BUS, - ccb->ccb_h.target_id, ccb->ccb_h.target_lun, - ccg->volume_size)); - - /* XXX We should probably ask the drive for the details - * instead of cluching them up ourselves - */ - if (sc->drive == ZIP_100) { - ccg->heads = 64; - ccg->secs_per_track = 32; - ccg->cylinders = ccg->volume_size / ccg->heads - / ccg->secs_per_track; - ccb->ccb_h.status = CAM_REQ_CMP; - break; - } else if (sc->proto & PROTO_UFI) { - ccg->heads = 2; - if (ccg->volume_size == 2880) - ccg->secs_per_track = 18; - else - ccg->secs_per_track = 9; - ccg->cylinders = 80; - break; - } else { - ccb->ccb_h.status = CAM_REQ_CMP_ERR; - } - - xpt_done(ccb); - break; - } - case XPT_NOOP: - { - DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_NOOP:.\n", - (sc == NULL? DEVNAME_SIM:USBDEVNAME(sc->sc_dev)), - UMASS_SCSI_BUS, - ccb->ccb_h.target_id, ccb->ccb_h.target_lun)); - - ccb->ccb_h.status = CAM_REQ_CMP; - xpt_done(ccb); - break; - } - default: - DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:func_code 0x%04x: " - "Not implemented\n", - (sc == NULL? DEVNAME_SIM:USBDEVNAME(sc->sc_dev)), - UMASS_SCSI_BUS, - ccb->ccb_h.target_id, ccb->ccb_h.target_lun, - ccb->ccb_h.func_code)); - - ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; - xpt_done(ccb); - break; - } -} - -/* umass_cam_poll - * all requests are handled through umass_cam_action, requests - * are never pending. So, nothing to do here. - */ -Static void -umass_cam_poll(struct cam_sim *sim) -{ -#ifdef UMASS_DEBUG - struct umass_softc *sc = (struct umass_softc *) sim->softc; - - DPRINTF(UDMASS_SCSI, ("%s: CAM poll\n", - USBDEVNAME(sc->sc_dev))); -#endif - - /* nop */ -} - - -/* umass_cam_cb - * finalise a completed CAM command - */ - -Static void -umass_cam_cb(struct umass_softc *sc, void *priv, int residue, int status) -{ - union ccb *ccb = (union ccb *) priv; - struct ccb_scsiio *csio = &ccb->csio; /* deref union */ - - csio->resid = residue; - - switch (status) { - case STATUS_CMD_OK: - ccb->ccb_h.status = CAM_REQ_CMP; - xpt_done(ccb); - break; - - case STATUS_CMD_UNKNOWN: - case STATUS_CMD_FAILED: - switch (ccb->ccb_h.func_code) { - case XPT_SCSI_IO: - { - unsigned char *cmd; - int cmdlen; - - /* fetch sense data */ - DPRINTF(UDMASS_SCSI,("%s: Fetching %db sense data\n", - USBDEVNAME(sc->sc_dev), - sc->cam_scsi_sense.length)); - - sc->cam_scsi_sense.length = csio->sense_len; - - if (sc->transform(sc, (char *) &sc->cam_scsi_sense, - sizeof(sc->cam_scsi_sense), - &cmd, &cmdlen)) { - sc->transfer(sc, ccb->ccb_h.target_lun, - cmd, cmdlen, - &csio->sense_data, - csio->sense_len, DIR_IN, - umass_cam_sense_cb, (void *) ccb); - } else { -#ifdef UMASS_DEBUG - panic("transform(REQUEST_SENSE) failed\n"); -#else - csio->resid = sc->transfer_datalen; - ccb->ccb_h.status = CAM_REQ_CMP_ERR; - xpt_done(ccb); -#endif - } - break; - } - case XPT_RESET_DEV: /* Reset failed */ - ccb->ccb_h.status = CAM_REQ_CMP_ERR; - xpt_done(ccb); - break; - default: - panic("umass_cam_cb called for func_code %d\n", - ccb->ccb_h.func_code); - } - break; - - case STATUS_WIRE_FAILED: - /* the wire protocol failed and will have recovered - * (hopefully). We return an error to CAM and let CAM retry - * the command if necessary. - */ - ccb->ccb_h.status = CAM_REQ_CMP_ERR; - xpt_done(ccb); - break; - - default: - panic("%s: Unknown status %d in umass_cam_cb\n", - USBDEVNAME(sc->sc_dev), status); - } -} - -/* Finalise a completed autosense operation - */ -Static void -umass_cam_sense_cb(struct umass_softc *sc, void *priv, int residue, int status) -{ - union ccb *ccb = (union ccb *) priv; - struct ccb_scsiio *csio = &ccb->csio; /* deref union */ - - switch (status) { - case STATUS_CMD_OK: - case STATUS_CMD_UNKNOWN: - /* Getting sense data succeeded. The length of the sense data - * is not returned in any way. The sense data itself contains - * the length of the sense data that is valid. - */ - if (sc->quirks & RS_NO_CLEAR_UA - && csio->cdb_io.cdb_bytes[0] == INQUIRY - && (csio->sense_data.flags & SSD_KEY) - == SSD_KEY_UNIT_ATTENTION) { - /* Ignore unit attention errors in the case where - * the Unit Attention state is not cleared on - * REQUEST SENSE. They will appear again at the next - * command. - */ - ccb->ccb_h.status = CAM_REQ_CMP; - } else if ((csio->sense_data.flags & SSD_KEY) - == SSD_KEY_NO_SENSE) { - /* No problem after all (in the case of CBI without - * CCI) - */ - ccb->ccb_h.status = CAM_REQ_CMP; - } else { - ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR - | CAM_AUTOSNS_VALID; - csio->scsi_status = SCSI_STATUS_CHECK_COND; - } - xpt_done(ccb); - break; - - default: - DPRINTF(UDMASS_SCSI, ("%s: Autosense failed, status %d\n", - USBDEVNAME(sc->sc_dev), status)); - ccb->ccb_h.status = CAM_AUTOSENSE_FAIL; - xpt_done(ccb); - } -} - - -Static int -umass_driver_load(module_t mod, int what, void *arg) -{ - int err; - - switch (what) { - case MOD_UNLOAD: - err = umass_cam_detach_sim(); - if (err) - return(err); - return(usbd_driver_load(mod, what, arg)); - case MOD_LOAD: - /* We don't attach to CAM at this point, because it will try - * and malloc memory for it. This is not possible when the - * boot loader loads umass as a module before the kernel - * has been bootstrapped. - */ - default: - return(usbd_driver_load(mod, what, arg)); - } -} - - - -/* (even the comment is missing) */ - -DRIVER_MODULE(umass, uhub, umass_driver, umass_devclass, umass_driver_load, 0); - - -/* - * SCSI specific functions - */ - -Static int -umass_scsi_transform(struct umass_softc *sc, unsigned char *cmd, int cmdlen, - unsigned char **rcmd, int *rcmdlen) -{ - *rcmd = cmd; /* trivial copy */ - *rcmdlen = cmdlen; - - switch (cmd[0]) { - case TEST_UNIT_READY: - if (sc->quirks & NO_TEST_UNIT_READY) { - DPRINTF(UDMASS_SCSI, ("%s: Converted TEST_UNIT_READY " - "to START_UNIT\n", USBDEVNAME(sc->sc_dev))); - cmd[0] = START_STOP_UNIT; - cmd[4] = SSS_START; - } - break; - } - - return 1; /* success */ -} - -/* - * UFI specific functions - */ - -Static int -umass_ufi_transform(struct umass_softc *sc, unsigned char *cmd, int cmdlen, - unsigned char **rcmd, int *rcmdlen) -{ - *rcmd = cmd; - /* A UFI command is always 12 bytes in length */ - /* XXX cmd[(cmdlen+1)..12] contains garbage */ - *rcmdlen = 12; - - switch (cmd[0]) { - case TEST_UNIT_READY: - if (sc->quirks & NO_TEST_UNIT_READY) { - DPRINTF(UDMASS_UFI, ("%s: Converted TEST_UNIT_READY " - "to START_UNIT\n", USBDEVNAME(sc->sc_dev))); - cmd[0] = START_STOP_UNIT; - cmd[4] = SSS_START; - } - return 1; - case INQUIRY: - case START_STOP_UNIT: - case MODE_SENSE: - case PREVENT_ALLOW: - case READ_10: - case READ_12: - case READ_CAPACITY: - case REQUEST_SENSE: - case REZERO_UNIT: - case POSITION_TO_ELEMENT: /* SEEK_10 */ - case SEND_DIAGNOSTIC: - case WRITE_10: - case WRITE_12: - /* FORMAT_UNIT */ - /* MODE_SELECT */ - /* READ_FORMAT_CAPACITY */ - /* VERIFY */ - /* WRITE_AND_VERIFY */ - return 1; /* success */ - default: - return 0; /* success */ - } -} - -/* - * 8070 specific functions - */ -Static int -umass_8070_transform(struct umass_softc *sc, unsigned char *cmd, int cmdlen, - unsigned char **rcmd, int *rcmdlen) -{ - return 0; /* failure */ -} - -#endif /* __FreeBSD__ */ - #ifdef UMASS_DEBUG Static void @@ -3059,11 +1718,13 @@ umass_bbb_dump_cbw(struct umass_softc *sc, umass_bbb_cbw_t *cbw) int tag = UGETDW(cbw->dCBWTag); int flags = cbw->bCBWFlags; - DPRINTF(UDMASS_BBB, ("%s: CBW %d: cmd = %db " - "(0x%02x%02x%02x%02x%02x%02x%s), " + DPRINTF(UDMASS_BBB, ("%s: CBW %d: cmdlen=%d " + "(0x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%s), " "data = %d bytes, dir = %s\n", USBDEVNAME(sc->sc_dev), tag, clen, - c[0], c[1], c[2], c[3], c[4], c[5], (clen > 6? "...":""), + c[0], c[1], c[2], c[3], c[4], c[5], + c[6], c[7], c[8], c[9], + (clen > 10? "...":""), dlen, (flags == CBWFLAGS_IN? "in": (flags == CBWFLAGS_OUT? "out":"<invalid>")))); } @@ -3113,482 +1774,3 @@ umass_dump_buffer(struct umass_softc *sc, u_int8_t *buffer, int buflen, USBDEVNAME(sc->sc_dev), s1, s2, s3)); } #endif - - - - - - - - -#if defined(__NetBSD__) || defined(__OpenBSD__) -Static int -umass_scsipi_cmd(xs) - struct scsipi_xfer *xs; -{ - struct scsipi_link *sc_link = xs->sc_link; - struct umass_softc *sc = sc_link->adapter_softc; - struct scsipi_generic *cmd, trcmd; - int cmdlen; - int dir; -#ifdef UMASS_DEBUG - microtime(&sc->tv); -#endif - - memset(&trcmd, 0, sizeof(trcmd)); - -#if defined(__NetBSD__) - DIF(UDMASS_UPPER, sc_link->flags |= DEBUGLEVEL); -#endif -#if defined(__OpenBSD__) - DIF(UDMASS_UPPER, sc_link->flags |= SCSIDEBUG_LEVEL); -#endif - -#if defined(__NetBSD__) || defined(__OpenBSD__) - DPRINTF(UDMASS_CMD, ("%s: umass_scsi_cmd: %d:%d xs=%p cmd=0x%02x " - "(quirks=0x%x, poll=%d)\n", USBDEVNAME(sc->sc_dev), - SCSI_LINK_TARGET(sc_link), SCSI_LINK_LUN(sc_link), - xs, xs->cmd->opcode, sc_link->quirks, - xs->xs_control & XS_CTL_POLL)); -#endif - -#if defined(USB_DEBUG) && defined(SCSIDEBUG) - if (umassdebug & UDMASS_SCSI) - show_scsipi_xs(xs); - else if (umassdebug & ~UDMASS_CMD) - show_scsipi_cmd(xs); -#endif - - if (sc->sc_dying) { - xs->error = XS_DRIVER_STUFFUP; - goto done; - } - -#ifdef UMASS_DEBUG -#if defined(__NetBSD__) - if ((sc_link->type == BUS_ATAPI ? - sc_link->scsipi_atapi.drive : SCSI_LINK_TARGET(sc_link)) - != UMASS_SCSIID_DEVICE) { - DPRINTF(UDMASS_SCSI, ("%s: wrong SCSI ID %d\n", - USBDEVNAME(sc->sc_dev), - SCSI_LINK_TARGET(sc_link))); - xs->error = XS_DRIVER_STUFFUP; - goto done; - } -#endif -#if defined(__OpenBSD__) - if (sc_link->target != UMASS_SCSIID_DEVICE) { - DPRINTF(UDMASS_SCSI, ("%s: wrong SCSI ID %d\n", - USBDEVNAME(sc->sc_dev), - sc_link->target)); - xs->error = XS_DRIVER_STUFFUP; - goto done; - } -#endif -#endif - - cmd = xs->cmd; - - if (xs->cmd->opcode == SCSI_MODE_SENSE && - (sc_link->quirks & SDEV_NOMODESENSE)) { - /*printf("%s: SCSI_MODE_SENSE\n", USBDEVNAME(sc->sc_dev));*/ - xs->error = XS_TIMEOUT; - goto done; - } - - if (xs->cmd->opcode == START_STOP && - (sc->quirks & NO_START_STOP)) { - /*printf("%s: START_STOP\n", USBDEVNAME(sc->sc_dev));*/ - xs->error = XS_NOERROR; - goto done; - } - - if (xs->cmd->opcode == INQUIRY && - (sc->quirks & FORCE_SHORT_INQUIRY)) { - memcpy(&trcmd, cmd, sizeof trcmd); - trcmd.bytes[4] = SHORT_INQUIRY_LENGTH; - cmd = &trcmd; - } - - dir = DIR_NONE; - if (xs->datalen) { - switch (xs->xs_control & (XS_CTL_DATA_IN | XS_CTL_DATA_OUT)) { - case XS_CTL_DATA_IN: - dir = DIR_IN; - break; - case XS_CTL_DATA_OUT: - dir = DIR_OUT; - break; - } - } - - if (xs->datalen > UMASS_MAX_TRANSFER_SIZE) { - printf("umass_cmd: large datalen, %d\n", xs->datalen); - xs->error = XS_DRIVER_STUFFUP; - goto done; - } - - cmdlen = xs->cmdlen; - if (sc->proto & PROTO_UFI) { - if (!umass_ufi_transform(sc, cmd, cmdlen, &trcmd, &cmdlen)) { - xs->error = XS_DRIVER_STUFFUP; - goto done; - } - cmd= &trcmd; - } - - if (sc->proto & PROTO_ATAPI) { - bcopy(cmd, &trcmd, cmdlen); - cmd = &trcmd; - cmdlen = ATAPI_COMMAND_LENGTH; - } - - if (xs->xs_control & XS_CTL_POLL) { - /* Use sync transfer. XXX Broken! */ - DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: sync dir=%d\n", dir)); - sc->sc_xfer_flags = USBD_SYNCHRONOUS; - sc->sc_sync_status = USBD_INVAL; - sc->transfer(sc, SCSI_LINK_LUN(sc_link), cmd, cmdlen, - xs->data, xs->datalen, dir, 0, xs); - sc->sc_xfer_flags = 0; - DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: done err=%d\n", - sc->sc_sync_status)); - switch (sc->sc_sync_status) { - case USBD_NORMAL_COMPLETION: - xs->error = XS_NOERROR; - break; - case USBD_TIMEOUT: - xs->error = XS_TIMEOUT; - break; - default: - xs->error = XS_DRIVER_STUFFUP; - break; - } - goto done; - } else { - DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: async dir=%d, cmdlen=%d" - " datalen=%d\n", - dir, cmdlen, xs->datalen)); - sc->transfer(sc, SCSI_LINK_LUN(sc_link), cmd, cmdlen, - xs->data, xs->datalen, dir, umass_scsipi_cb, xs); - return (SUCCESSFULLY_QUEUED); - } - - /* Return if command finishes early. */ - done: -#if defined(__NetBSD__) - xs->xs_status |= XS_STS_DONE; -#endif -#if defined(__OpenBSD__) - xs->flags |= ITSDONE; -#endif - - scsipi_done(xs); - if (xs->xs_control & XS_CTL_POLL) - return (COMPLETE); - else - return (SUCCESSFULLY_QUEUED); -} - -Static void -umass_scsipi_minphys(bp) - struct buf *bp; -{ - if (bp->b_bcount > UMASS_MAX_TRANSFER_SIZE) - bp->b_bcount = UMASS_MAX_TRANSFER_SIZE; - minphys(bp); -} - -int -umass_scsipi_ioctl(link, cmd, arg, flag, p) - struct scsipi_link *link; - u_long cmd; - caddr_t arg; - int flag; - struct proc *p; -{ - /*struct umass_softc *sc = link->adapter_softc;*/ - - switch (cmd) { -#if 0 - case SCBUSIORESET: - ccb->ccb_h.status = CAM_REQ_INPROG; - umass_reset(sc, umass_cam_cb, (void *) ccb); - return (0); -#endif - default: - return (ENOTTY); - } -} - -Static void -umass_scsipi_cb(struct umass_softc *sc, void *priv, int residue, int status) -{ - struct scsipi_xfer *xs = priv; - struct scsipi_link *sc_link = xs->sc_link; - int cmdlen; - int s; -#ifdef UMASS_DEBUG - struct timeval tv; - u_int delta; - microtime(&tv); - delta = (tv.tv_sec - sc->tv.tv_sec) * 1000000 + tv.tv_usec - sc->tv.tv_usec; -#endif - - DPRINTF(UDMASS_CMD,("umass_scsipi_cb: at %lu.%06lu, delta=%u: xs=%p residue=%d" - " status=%d\n", tv.tv_sec, tv.tv_usec, delta, xs, residue, status)); - xs->resid = residue; - - switch (status) { - case STATUS_CMD_OK: - xs->error = XS_NOERROR; - break; - - case STATUS_CMD_UNKNOWN: - case STATUS_CMD_FAILED: - /* fetch sense data */ - memset(&sc->sc_sense_cmd, 0, sizeof(sc->sc_sense_cmd)); - sc->sc_sense_cmd.opcode = REQUEST_SENSE; - sc->sc_sense_cmd.byte2 = SCSI_LINK_LUN(sc_link) << - SCSI_CMD_LUN_SHIFT; - sc->sc_sense_cmd.length = sizeof(xs->sense); - - cmdlen = sizeof(sc->sc_sense_cmd); - if (sc->proto & PROTO_UFI) - cmdlen = UFI_COMMAND_LENGTH; - else if (sc->proto & PROTO_ATAPI) - cmdlen = ATAPI_COMMAND_LENGTH; - - sc->transfer(sc, SCSI_LINK_LUN(sc_link), - &sc->sc_sense_cmd, cmdlen, - &xs->sense, sizeof(xs->sense), DIR_IN, - umass_scsipi_sense_cb, xs); - return; - - case STATUS_WIRE_FAILED: - xs->error = XS_RESET; - break; - - default: - panic("%s: Unknown status %d in umass_scsipi_cb\n", - USBDEVNAME(sc->sc_dev), status); - } - -#if defined(__NetBSD__) - xs->xs_status |= XS_STS_DONE; -#endif -#if defined(__OpenBSD__) - xs->flags |= ITSDONE; -#endif - - DPRINTF(UDMASS_CMD,("umass_scsipi_cb: at %lu.%06lu: return xs->error=" - "%d, xs->xs_status=0x%x xs->resid=%d\n", - tv.tv_sec, tv.tv_usec, - xs->error, xs->xs_status, xs->resid)); - - s = splbio(); - scsipi_done(xs); - splx(s); -} - -/* - * Finalise a completed autosense operation - */ -Static void -umass_scsipi_sense_cb(struct umass_softc *sc, void *priv, int residue, - int status) -{ - struct scsipi_xfer *xs = priv; - int s; - int bytes_received; - - DPRINTF(UDMASS_CMD,("umass_scsipi_sense_cb: xs=%p residue=%d " - "status=%d\n", xs, residue, status)); - - switch (status) { - case STATUS_CMD_OK: - case STATUS_CMD_UNKNOWN: - /* getting sense data succeeded */ - if ((xs->cmd->opcode == INQUIRY) - && (xs->resid < xs->datalen)) { - /* Some drivers return SENSE errors even after INQUIRY - * The upper layer doesn't like that. - */ - xs->error = XS_NOERROR; - break; - } - - bytes_received = sizeof(xs->sense) - residue; - - if (bytes_received < 8 || - (bytes_received < xs->sense.extra_len + 8)) - xs->error = XS_SHORTSENSE; - else - xs->error = XS_SENSE; - -#if defined(__OpenBSD__) - /* Note that this test may need to be revised - with QIC-157a/SCSI tape drives that return - ILI, EOM in the high bits of flags. - */ - if ((xs->sense.error_code & SSD_ERRCODE) == 0x70 && - (xs->sense.flags == 0)) - xs->error = XS_NOERROR; -#endif - - break; - default: - DPRINTF(UDMASS_SCSI, ("%s: Autosense failed, status %d\n", - USBDEVNAME(sc->sc_dev), status)); - xs->error = XS_DRIVER_STUFFUP; - break; - } - -#if defined(__NetBSD__) - xs->xs_status |= XS_STS_DONE; -#endif -#if defined(__OpenBSD__) - xs->flags |= ITSDONE; -#endif - - DPRINTF(UDMASS_CMD,("umass_scsipi_sense_cb: return xs->error=%d, " - "xs->xs_status=0x%x xs->resid=%d\n", xs->error, xs->xs_status, - xs->resid)); - - s = splbio(); - scsipi_done(xs); - splx(s); -} - -/* - * UFI specific functions - */ - -Static int -umass_ufi_transform(struct umass_softc *sc, struct scsipi_generic *cmd, - int cmdlen, struct scsipi_generic *rcmd, int *rcmdlen) -{ - *rcmdlen = UFI_COMMAND_LENGTH; - memset(rcmd, 0, sizeof *rcmd); - - /* Handle any quirks */ - if (cmd->opcode == TEST_UNIT_READY - && (sc->quirks & NO_TEST_UNIT_READY)) { - /* - * Some devices do not support this command. - * Start Stop Unit should give the same results - */ - DPRINTF(UDMASS_UFI, ("%s: Converted TEST_UNIT_READY " - "to START_UNIT\n", USBDEVNAME(sc->sc_dev))); - rcmd->opcode = START_STOP; - rcmd->bytes[3] = SSS_START; - return 1; - } - - switch (cmd->opcode) { - /* Commands of which the format has been verified. They should work. */ - case TEST_UNIT_READY: - case SCSI_REZERO_UNIT: - case REQUEST_SENSE: - case INQUIRY: - case START_STOP: - /*case SEND_DIAGNOSTIC: ??*/ - case PREVENT_ALLOW: - case READ_CAPACITY: - case READ_BIG: - case WRITE_BIG: - case POSITION_TO_ELEMENT: /* SEEK_10 */ - case SCSI_MODE_SELECT_BIG: - case SCSI_MODE_SENSE_BIG: - default: - /* Copy the command into the (zeroed out) destination buffer */ - memcpy(rcmd, cmd, cmdlen); - return (1); /* success */ - - /* - * Other UFI commands: FORMAT_UNIT, MODE_SELECT, READ_FORMAT_CAPACITY, - * VERIFY, WRITE_AND_VERIFY. - * These should be checked whether they somehow can be made to fit. - */ - - /* These commands are known _not_ to work. They should be converted. */ - case SCSI_READ_COMMAND: - case SCSI_WRITE_COMMAND: - case SCSI_MODE_SENSE: - case SCSI_MODE_SELECT: - printf("%s: Unsupported UFI command 0x%02x", - USBDEVNAME(sc->sc_dev), cmd->opcode); - if (cmdlen == 6) - printf(", 6 byte command should have been converted"); - printf("\n"); - return (0); /* failure */ - } -} - - -#if NATAPIBUS > 0 -Static void -umass_atapi_probedev(atapi, target) - struct atapibus_softc *atapi; - int target; -{ - struct scsipi_link *sc_link; - struct scsipibus_attach_args sa; - struct ata_drive_datas *drvp = &atapi->sc_drvs[target]; - char vendor[33], product[65], revision[17]; - struct scsipi_inquiry_data inqbuf; - - DPRINTF(UDMASS_SCSI,("umass_atapi_probedev: atapi=%p target=%d\n", - atapi, target)); - - if (atapi->sc_link[target]) - return; - - sc_link = malloc(sizeof(*sc_link), M_DEVBUF, M_NOWAIT); - if (sc_link == NULL) { - printf("%s: can't allocate link for drive %d\n", - atapi->sc_dev.dv_xname, target); - return; - } - *sc_link = *atapi->adapter_link; - - DIF(UDMASS_UPPER, sc_link->flags |= DEBUGLEVEL); - - /* Fill generic parts of the link. */ - sc_link->active = 0; - sc_link->scsipi_atapi.drive = target; - sc_link->device = &umass_dev; - TAILQ_INIT(&sc_link->pending_xfers); - - DPRINTF(UDMASS_SCSI, ("umass_atapi_probedev: doing inquiry\n")); - /* Now go ask the device all about itself. */ - memset(&inqbuf, 0, sizeof(inqbuf)); - if (scsipi_inquire(sc_link, &inqbuf, XS_CTL_DISCOVERY) != 0) - goto bad; - - scsipi_strvis(vendor, 33, inqbuf.vendor, 8); - scsipi_strvis(product, 65, inqbuf.product, 16); - scsipi_strvis(revision, 17, inqbuf.revision, 4); - - sa.sa_sc_link = sc_link; - sa.sa_inqbuf.type = inqbuf.device; - sa.sa_inqbuf.removable = inqbuf.dev_qual2 & SID_REMOVABLE ? - T_REMOV : T_FIXED; - if (sa.sa_inqbuf.removable) - sc_link->flags |= SDEV_REMOVABLE; - /* XXX how? sc_link->scsipi_atapi.cap |= ACAP_LEN;*/ - sa.sa_inqbuf.vendor = vendor; - sa.sa_inqbuf.product = product; - sa.sa_inqbuf.revision = revision; - sa.sa_inqptr = NULL; - - drvp->drv_softc = atapi_probedev(atapi, target, sc_link, &sa); - /* atapi_probedev() frees the scsipi_link when there is no device. */ - return; - -bad: - free(sc_link, M_DEVBUF); - return; -} -#endif -#endif diff --git a/sys/dev/usb/umass_isdata.c b/sys/dev/usb/umass_isdata.c new file mode 100644 index 00000000000..bd5a73562dd --- /dev/null +++ b/sys/dev/usb/umass_isdata.c @@ -0,0 +1,589 @@ +/* $NetBSD: umass_isdata.c,v 1.1 2001/12/24 13:43:25 augustss Exp $ */ + +/* + * TODO: + * get ATA registers on any kind of error + * implement more commands (what is needed) + */ + +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net) at + * Carlstedt Research & Technology. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: umass_isdata.c,v 1.1 2001/12/24 13:43:25 augustss Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/conf.h> +#include <sys/buf.h> +#include <sys/device.h> +#include <sys/proc.h> +#include <sys/disklabel.h> +#include <sys/malloc.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> + +#include <dev/usb/umassvar.h> +#include <dev/usb/umass_isdata.h> + +int umass_wd_attach(struct umass_softc *); + +#include <dev/ata/atareg.h> +#include <dev/ata/atavar.h> +#include <dev/ata/wdvar.h> +#include <dev/ic/wdcreg.h> + +/* XXX move this */ +struct isd200_config { + uByte EventNotification; + uByte ExternalClock; + uByte ATAInitTimeout; + uByte ATAMisc1; +#define ATATiming 0x0f +#define ATAPIReset 0x10 +#define MasterSlaveSelection 0x20 +#define ATAPICommandBlockSize 0xc0 + uByte ATAMajorCommand; + uByte ATAMinorCommand; + uByte ATAMisc2; +#define LastLUNIdentifier 0x07 +#define DescriptOverride 0x08 +#define ATA3StateSuspend 0x10 +#define SkipDeviceBoot 0x20 +#define ConfigDescriptor2 0x40 +#define InitStatus 0x80 + uByte ATAMisc3; +#define SRSTEnable 0x01 +}; + +struct uisdata_softc { + struct umassbus_softc base; + + struct ata_drive_datas sc_drv_data; + struct isd200_config sc_isd_config; + void *sc_ata_bio; + u_long sc_skip; +}; + +#undef DPRINTF +#undef DPRINTFN +#ifdef UISDATA_DEBUG +#define DPRINTF(x) if (uisdatadebug) logprintf x +#define DPRINTFN(n,x) if (uisdatadebug>(n)) logprintf x +int uisdatadebug = 0; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +int uisdata_bio(struct ata_drive_datas *, struct ata_bio *); +int uisdata_bio1(struct ata_drive_datas *, struct ata_bio *); +void uisdata_reset_channel(struct ata_drive_datas *); +int uisdata_exec_command(struct ata_drive_datas *, struct wdc_command *); +int uisdata_get_params(struct ata_drive_datas *, u_int8_t, struct ataparams *); +int uisdata_addref(struct ata_drive_datas *); +void uisdata_delref(struct ata_drive_datas *); +void uisdata_kill_pending(struct ata_drive_datas *); + +void uisdata_bio_cb(struct umass_softc *, void *, int, int); +void uisdata_exec_cb(struct umass_softc *, void *, int, int); +int uwdprint(void *, const char *); + +const struct ata_bustype uisdata_bustype = { + SCSIPI_BUSTYPE_ATA, + uisdata_bio, + uisdata_reset_channel, + uisdata_exec_command, + uisdata_get_params, + uisdata_addref, + uisdata_delref, + uisdata_kill_pending, +}; + +struct ata_cmd { + u_int8_t ac_signature0; + u_int8_t ac_signature1; + + u_int8_t ac_action_select; +#define AC_ReadRegisterAccess 0x01 +#define AC_NoDeviceSelectionBit 0x02 +#define AC_NoBSYPollBit 0x04 +#define AC_IgnorePhaseErrorBit 0x08 +#define AC_IgnoreDeviceErrorBit 0x10 + + u_int8_t ac_register_select; +#define AC_SelectAlternateStatus 0x01 /* R */ +#define AC_SelectDeviceControl 0x01 /* W */ +#define AC_SelectError 0x02 /* R */ +#define AC_SelectFeatures 0x02 /* W */ +#define AC_SelectSectorCount 0x04 /* RW */ +#define AC_SelectSectorNumber 0x08 /* RW */ +#define AC_SelectCylinderLow 0x10 /* RW */ +#define AC_SelectCylinderHigh 0x20 /* RW */ +#define AC_SelectDeviceHead 0x40 /* RW */ +#define AC_SelectStatus 0x80 /* R */ +#define AC_SelectCommand 0x80 /* W */ + + u_int8_t ac_transfer_blocksize; + + u_int8_t ac_alternate_status; +#define ac_device_control ac_alternate_status + u_int8_t ac_error; +#define ac_features ac_error + + u_int8_t ac_sector_count; + u_int8_t ac_sector_number; + u_int8_t ac_cylinder_low; + u_int8_t ac_cylinder_high; + u_int8_t ac_device_head; + + u_int8_t ac_status; +#define ac_command ac_status + + u_int8_t ac_reserved[3]; +}; + +#define ATA_DELAY 10000 /* 10s for a drive I/O */ + +int +umass_isdata_attach(struct umass_softc *sc) +{ + usb_device_request_t req; + usbd_status err; + struct ata_device adev; + struct uisdata_softc *scbus; + struct isd200_config *cf; + + scbus = malloc(sizeof *scbus, M_DEVBUF, M_WAITOK | M_ZERO); + sc->bus = &scbus->base; + cf = &scbus->sc_isd_config; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = 0x02; + USETW(req.wValue, 0); + USETW(req.wIndex, 2); + USETW(req.wLength, sizeof *cf); + + err = usbd_do_request(sc->sc_udev, &req, cf); + if (err) + return (EIO); + DPRINTF(("umass_wd_attach info:\n EventNotification=0x%02x " + "ExternalClock=0x%02x ATAInitTimeout=0x%02x\n" + " ATAMisc1=0x%02x ATAMajorCommand=0x%02x " + "ATAMinorCommand=0x%02x\n" + " ATAMisc2=0x%02x ATAMisc3=0x%02x\n", + cf->EventNotification, cf->ExternalClock, cf->ATAInitTimeout, + cf->ATAMisc1, cf->ATAMajorCommand, cf->ATAMinorCommand, + cf->ATAMisc2, cf->ATAMisc3)); + + memset(&adev, 0, sizeof(struct ata_device)); + adev.adev_bustype = &uisdata_bustype; + adev.adev_channel = 1; /* XXX */ + adev.adev_openings = 1; + adev.adev_drv_data = &scbus->sc_drv_data; + scbus->sc_drv_data.drive_flags = DRIVE_ATA; + scbus->sc_drv_data.chnl_softc = sc; + scbus->base.sc_child = config_found(&sc->sc_dev, &adev, uwdprint); + + return (0); +} + + +void +uisdata_bio_cb(struct umass_softc *sc, void *priv, int residue, int status) +{ + struct uisdata_softc *scbus = (struct uisdata_softc *)sc->bus; + struct ata_bio *ata_bio = priv; + int s; + + DPRINTF(("%s: residue=%d status=%d\n", __FUNCTION__, residue, status)); + + s = splbio(); + scbus->sc_ata_bio = NULL; + if (status != STATUS_CMD_OK) + ata_bio->error = ERR_DF; /* ??? */ + else + ata_bio->error = NOERROR; + ata_bio->flags |= ATA_ITSDONE; + + ata_bio->blkdone += ata_bio->nblks; + ata_bio->blkno += ata_bio->nblks; + ata_bio->bcount -= ata_bio->nbytes; + scbus->sc_skip += ata_bio->nbytes; + if (residue != 0) { + ata_bio->bcount += residue; + } else if (ata_bio->bcount > 0) { + DPRINTF(("%s: continue\n", __FUNCTION__)); + (void)uisdata_bio1(&scbus->sc_drv_data, ata_bio); /*XXX save drv*/ + splx(s); + return; + } + + if (ata_bio->flags & ATA_POLL) { + DPRINTF(("%s: wakeup %p\n", __FUNCTION__, ata_bio)); + wakeup(ata_bio); + } else { + wddone(scbus->sc_drv_data.drv_softc); + } + splx(s); +} + +int +uisdata_bio(struct ata_drive_datas *drv, struct ata_bio *ata_bio) +{ + struct umass_softc *sc = drv->chnl_softc; + struct uisdata_softc *scbus = (struct uisdata_softc *)sc->bus; + + scbus->sc_skip = 0; + return (uisdata_bio1(drv, ata_bio)); +} + +int +uisdata_bio1(struct ata_drive_datas *drv, struct ata_bio *ata_bio) +{ + struct umass_softc *sc = drv->chnl_softc; + struct uisdata_softc *scbus = (struct uisdata_softc *)sc->bus; + struct isd200_config *cf = &scbus->sc_isd_config; + struct ata_cmd ata; + u_int16_t cyl; + u_int8_t head, sect; + int dir; + long nbytes; + u_int nblks; + + DPRINTF(("%s\n", __FUNCTION__)); + /* XXX */ + + if (ata_bio->flags & ATA_NOSLEEP) { + printf("%s: ATA_NOSLEEP not supported\n", __FUNCTION__); + ata_bio->error = TIMEOUT; + ata_bio->flags |= ATA_ITSDONE; + return (WDC_COMPLETE); + } + + if (scbus->sc_ata_bio != NULL) { + printf("%s: multiple uisdata_bio\n", __FUNCTION__); + return (WDC_TRY_AGAIN); + } else + scbus->sc_ata_bio = ata_bio; + + if (ata_bio->flags & ATA_LBA) { + sect = (ata_bio->blkno >> 0) & 0xff; + cyl = (ata_bio->blkno >> 8) & 0xffff; + head = (ata_bio->blkno >> 24) & 0x0f; + head |= WDSD_LBA; + } else { + int blkno = ata_bio->blkno; + sect = blkno % ata_bio->lp->d_nsectors; + sect++; /* Sectors begin with 1, not 0. */ + blkno /= ata_bio->lp->d_nsectors; + head = blkno % ata_bio->lp->d_ntracks; + blkno /= ata_bio->lp->d_ntracks; + cyl = blkno; + head |= WDSD_CHS; + } + + nbytes = ata_bio->bcount; + if (ata_bio->flags & ATA_SINGLE) + nblks = 1; + else + nblks = min(ata_bio->multi, nbytes / ata_bio->lp->d_secsize); + nbytes = nblks * ata_bio->lp->d_secsize; + ata_bio->nblks = nblks; + ata_bio->nbytes = nbytes; + + memset(&ata, 0, sizeof ata); + ata.ac_signature0 = cf->ATAMajorCommand; + ata.ac_signature1 = cf->ATAMinorCommand; + ata.ac_transfer_blocksize = 1; + ata.ac_sector_count = nblks; + ata.ac_sector_number = sect; + ata.ac_cylinder_high = cyl >> 8; + ata.ac_cylinder_low = cyl; + ata.ac_device_head = head; + ata.ac_register_select = AC_SelectSectorCount | AC_SelectSectorNumber | + AC_SelectCylinderLow | AC_SelectCylinderHigh | AC_SelectDeviceHead | + AC_SelectCommand; + + dir = DIR_NONE; + if (ata_bio->bcount != 0) { + if (ata_bio->flags & ATA_READ) + dir = DIR_IN; + else + dir = DIR_OUT; + } + + if (ata_bio->flags & ATA_READ) { + ata.ac_command = WDCC_READ; + } else { + ata.ac_command = WDCC_WRITE; + } + DPRINTF(("%s: bno=%d LBA=%d cyl=%d head=%d sect=%d count=%d multi=%d\n", + __FUNCTION__, ata_bio->blkno, + (ata_bio->flags & ATA_LBA) != 0, cyl, head, sect, + ata.ac_sector_count, ata_bio->multi)); + DPRINTF((" data=%p bcount=%ld, drive=%d\n", ata_bio->databuf, + ata_bio->bcount, drv->drive)); + sc->sc_methods->wire_xfer(sc, drv->drive, &ata, sizeof ata, + ata_bio->databuf + scbus->sc_skip, nbytes, + dir, ATA_DELAY, uisdata_bio_cb, ata_bio); + + while (ata_bio->flags & ATA_POLL) { + DPRINTF(("%s: tsleep %p\n", __FUNCTION__, ata_bio)); + if (tsleep(ata_bio, PZERO, "uisdatabl", 0)) { + ata_bio->error = TIMEOUT; + ata_bio->flags |= ATA_ITSDONE; + return (WDC_COMPLETE); + } + } + + return (ata_bio->flags & ATA_ITSDONE) ? WDC_COMPLETE : WDC_QUEUED; +} + +void +uisdata_reset_channel(struct ata_drive_datas *drv) +{ + DPRINTFN(-1,("%s\n", __FUNCTION__)); + /* XXX what? */ +} + +void +uisdata_exec_cb(struct umass_softc *sc, void *priv, int residue, int status) +{ + struct wdc_command *cmd = priv; + + DPRINTF(("%s: status=%d\n", __FUNCTION__, status)); + if (status != STATUS_CMD_OK) + cmd->flags |= AT_DF; /* XXX */ + cmd->flags |= AT_DONE; + if (cmd->flags & (AT_POLL | AT_WAIT)) { + DPRINTF(("%s: wakeup %p\n", __FUNCTION__, cmd)); + wakeup(cmd); + } +} + +int +uisdata_exec_command(struct ata_drive_datas *drv, struct wdc_command *cmd) +{ + struct umass_softc *sc = drv->chnl_softc; + struct uisdata_softc *scbus = (struct uisdata_softc *)sc->bus; + struct isd200_config *cf = &scbus->sc_isd_config; + int dir; + struct ata_cmd ata; + + DPRINTF(("%s\n", __FUNCTION__)); + DPRINTF((" r_command=0x%02x timeout=%d flags=0x%x bcount=%d\n", + cmd->r_command, cmd->timeout, cmd->flags, cmd->bcount)); + + dir = DIR_NONE; + if (cmd->bcount != 0) { + if (cmd->flags & AT_READ) + dir = DIR_IN; + else + dir = DIR_OUT; + } + + if (cmd->bcount > UMASS_MAX_TRANSFER_SIZE) { + printf("uisdata_exec_command: large datalen %d\n", cmd->bcount); + cmd->flags |= AT_ERROR; + goto done; + } + + memset(&ata, 0, sizeof ata); + ata.ac_signature0 = cf->ATAMajorCommand; + ata.ac_signature1 = cf->ATAMinorCommand; + ata.ac_transfer_blocksize = 1; + + switch (cmd->r_command) { + case WDCC_IDENTIFY: + ata.ac_register_select |= AC_SelectCommand; + ata.ac_command = WDCC_IDENTIFY; + break; + default: + printf("uisdata_exec_command: bad command 0x%02x\n", + cmd->r_command); + cmd->flags |= AT_ERROR; + goto done; + } + + DPRINTF(("%s: execute ATA command 0x%02x, drive=%d\n", __FUNCTION__, + ata.ac_command, drv->drive)); + sc->sc_methods->wire_xfer(sc, drv->drive, &ata, + sizeof ata, cmd->data, cmd->bcount, dir, + cmd->timeout, uisdata_exec_cb, cmd); + if (cmd->flags & (AT_POLL | AT_WAIT)) { +#if 0 + if (cmd->flags & AT_POLL) + printf("%s: AT_POLL not supported\n", __FUNCTION__); +#endif + DPRINTF(("%s: tsleep %p\n", __FUNCTION__, cmd)); + if (tsleep(cmd, PZERO, "uisdataex", 0)) { + cmd->flags |= AT_ERROR; + goto done; + } + } + +done: + return (WDC_COMPLETE); +} + +int +uisdata_addref(struct ata_drive_datas *drv) +{ + DPRINTF(("%s\n", __FUNCTION__)); + /* Nothing to do */ + return (0); +} + +void +uisdata_delref(struct ata_drive_datas *drv) +{ + DPRINTF(("%s\n", __FUNCTION__)); + /* Nothing to do */ +} + +void +uisdata_kill_pending(struct ata_drive_datas *drv) +{ + struct umass_softc *sc = drv->chnl_softc; + struct uisdata_softc *scbus = (struct uisdata_softc *)sc->bus; + struct ata_bio *ata_bio = scbus->sc_ata_bio; + + DPRINTFN(-1,("%s\n", __FUNCTION__)); + + if (ata_bio == NULL) + return; + scbus->sc_ata_bio = NULL; + ata_bio->flags |= ATA_ITSDONE; + ata_bio->error = ERR_NODEV; + ata_bio->r_error = WDCE_ABRT; + wddone(scbus->sc_drv_data.drv_softc); +} + +int +uisdata_get_params(struct ata_drive_datas *drvp, u_int8_t flags, + struct ataparams *prms) +{ + char tb[DEV_BSIZE]; + struct wdc_command wdc_c; + +#if BYTE_ORDER == LITTLE_ENDIAN + int i; + u_int16_t *p; +#endif + + DPRINTF(("%s\n", __FUNCTION__)); + + memset(tb, 0, DEV_BSIZE); + memset(prms, 0, sizeof(struct ataparams)); + memset(&wdc_c, 0, sizeof(struct wdc_command)); + + wdc_c.r_command = WDCC_IDENTIFY; + wdc_c.timeout = 1000; /* 1s */ + wdc_c.flags = AT_READ | flags; + wdc_c.data = tb; + wdc_c.bcount = DEV_BSIZE; + if (uisdata_exec_command(drvp, &wdc_c) != WDC_COMPLETE) { + DPRINTF(("uisdata_get_parms: wdc_exec_command failed\n")); + return (CMD_AGAIN); + } + if (wdc_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) { + DPRINTF(("uisdata_get_parms: wdc_c.flags=0x%x\n", + wdc_c.flags)); + return (CMD_ERR); + } else { + /* Read in parameter block. */ + memcpy(prms, tb, sizeof(struct ataparams)); +#if BYTE_ORDER == LITTLE_ENDIAN + /* XXX copied from ata.c */ + /* + * Shuffle string byte order. + * ATAPI Mitsumi and NEC drives don't need this. + */ + if ((prms->atap_config & WDC_CFG_ATAPI_MASK) == + WDC_CFG_ATAPI && + ((prms->atap_model[0] == 'N' && + prms->atap_model[1] == 'E') || + (prms->atap_model[0] == 'F' && + prms->atap_model[1] == 'X'))) + return 0; + for (i = 0; i < sizeof(prms->atap_model); i += 2) { + p = (u_short *)(prms->atap_model + i); + *p = ntohs(*p); + } + for (i = 0; i < sizeof(prms->atap_serial); i += 2) { + p = (u_short *)(prms->atap_serial + i); + *p = ntohs(*p); + } + for (i = 0; i < sizeof(prms->atap_revision); i += 2) { + p = (u_short *)(prms->atap_revision + i); + *p = ntohs(*p); + } +#endif + return CMD_OK; + } +} + + +/* XXX join with wdc.c routine? */ +int +uwdprint(void *aux, const char *pnp) +{ + //struct ata_device *adev = aux; + if (pnp) + printf("wd at %s", pnp); +#if 0 + printf(" channel %d drive %d", adev->adev_channel, + adev->adev_drv_data->drive); +#endif + return (UNCONF); +} + + +#if 0 + +int umass_wd_attach(struct umass_softc *); + +#if NWD > 0 + case UMASS_CPROTO_ISD_ATA: + return (umass_wd_attach(sc)); +#endif + +#endif diff --git a/sys/dev/usb/umass_isdata.h b/sys/dev/usb/umass_isdata.h new file mode 100644 index 00000000000..665922192a4 --- /dev/null +++ b/sys/dev/usb/umass_isdata.h @@ -0,0 +1,40 @@ +/* $NetBSD: umass_isdata.h,v 1.1 2001/12/24 13:43:25 augustss Exp $ */ + +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net) at + * Carlstedt Research & Technology. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +int umass_isdata_attach(struct umass_softc *sc); diff --git a/sys/dev/usb/umass_quirks.c b/sys/dev/usb/umass_quirks.c new file mode 100644 index 00000000000..5cf42653814 --- /dev/null +++ b/sys/dev/usb/umass_quirks.c @@ -0,0 +1,333 @@ +/* $OpenBSD: umass_quirks.c,v 1.1 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: umass_quirks.c,v 1.13 2002/04/22 12:48:40 augustss Exp $ */ + +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by MAEKAWA Masahide (gehenna@NetBSD.org). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/buf.h> + +#if defined(__NetBSD__) +#include <dev/scsipi/scsipi_all.h> /* for scsiconf.h below */ +#include <dev/scsipi/scsiconf.h> /* for quirks defines */ +#endif + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdevs.h> + +#include <dev/usb/umassvar.h> +#include <dev/usb/umass_quirks.h> + +Static usbd_status umass_init_insystem(struct umass_softc *); +Static usbd_status umass_init_shuttle(struct umass_softc *); + +Static void umass_fixup_sony(struct umass_softc *); +Static void umass_fixup_yedata(struct umass_softc *); + +Static const struct umass_quirk umass_quirks[] = { + { { USB_VENDOR_FUJIPHOTO, USB_PRODUCT_FUJIPHOTO_MASS0100 }, + UMASS_WPROTO_UNSPEC, UMASS_CPROTO_UNSPEC, + UMASS_QUIRK_NO_START_STOP, + PQUIRK_NOTUR | PQUIRK_NOSENSE, + UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO, + NULL, NULL + }, + + { { USB_VENDOR_INSYSTEM, USB_PRODUCT_INSYSTEM_USBCABLE }, + UMASS_WPROTO_CBI, UMASS_CPROTO_ATAPI, + UMASS_QUIRK_NO_START_STOP, + PQUIRK_NOTUR, + UMATCH_VENDOR_PRODUCT, + umass_init_insystem, NULL + }, + + { { USB_VENDOR_IOMEGA, USB_PRODUCT_IOMEGA_ZIP100 }, + UMASS_WPROTO_UNSPEC, UMASS_CPROTO_UNSPEC, + 0, + PQUIRK_NOTUR, + UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO, + NULL, NULL + }, + + { { USB_VENDOR_IOMEGA, USB_PRODUCT_IOMEGA_ZIP250 }, + UMASS_WPROTO_UNSPEC, UMASS_CPROTO_UNSPEC, + 0, + PQUIRK_NOTUR, + UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO, + NULL, NULL + }, + + { { USB_VENDOR_MICROTECH, USB_PRODUCT_MICROTECH_DPCM }, + UMASS_WPROTO_CBI, UMASS_CPROTO_ATAPI, + 0, + 0, + UMATCH_VENDOR_PRODUCT, + NULL, NULL + }, + + { { USB_VENDOR_MSYSTEMS, USB_PRODUCT_MSYSTEMS_DISKONKEY }, + UMASS_WPROTO_UNSPEC, UMASS_CPROTO_UNSPEC, + 0, + PQUIRK_NOMODESENSE | PQUIRK_NODOORLOCK | PQUIRK_NOBIGMODESENSE, + UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO, + NULL, NULL + }, + + { { USB_VENDOR_OLYMPUS, USB_PRODUCT_OLYMPUS_C1 }, + UMASS_WPROTO_UNSPEC, UMASS_CPROTO_UNSPEC, + UMASS_QUIRK_WRONG_CSWSIG, + 0, + UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO, + NULL, NULL + }, + + { { USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_MD2 }, + UMASS_WPROTO_UNSPEC, UMASS_CPROTO_UNSPEC, + 0, + PQUIRK_NOMODESENSE, + UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO, + NULL, NULL + }, + + { { USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_MD1II }, + UMASS_WPROTO_UNSPEC, UMASS_CPROTO_UNSPEC, + UMASS_QUIRK_NO_MAX_LUN | UMASS_QUIRK_NO_START_STOP, + PQUIRK_NOMODESENSE, + UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO, + NULL, NULL + }, + + { { USB_VENDOR_SCANLOGIC, USB_PRODUCT_SCANLOGIC_SL11R }, + UMASS_WPROTO_UNSPEC, UMASS_CPROTO_UFI, + 0, + 0, + UMATCH_VENDOR_PRODUCT, + NULL, NULL + }, + + { { USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_EUSB }, + UMASS_WPROTO_CBI_I, UMASS_CPROTO_ATAPI, + UMASS_QUIRK_NO_START_STOP, + PQUIRK_NOTUR, + UMATCH_VENDOR_PRODUCT, + umass_init_shuttle, NULL + }, + + { { USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_ZIOMMC }, + UMASS_WPROTO_CBI_I, UMASS_CPROTO_ATAPI, + UMASS_QUIRK_NO_START_STOP, + PQUIRK_NOTUR, + UMATCH_VENDOR_PRODUCT, + NULL, NULL + }, + + { { USB_VENDOR_SONY, USB_PRODUCT_SONY_MSC }, + UMASS_WPROTO_UNSPEC, UMASS_CPROTO_UNSPEC, + UMASS_QUIRK_FORCE_SHORT_INQUIRY, + 0, + UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO, + NULL, umass_fixup_sony + }, + + { { USB_VENDOR_SONY, USB_PRODUCT_ANY }, + UMASS_WPROTO_UNSPEC, UMASS_CPROTO_RBC, + 0, + 0, + UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO, + NULL, umass_fixup_sony + }, + + { { USB_VENDOR_TEAC, USB_PRODUCT_TEAC_FD05PUB }, + UMASS_WPROTO_UNSPEC, UMASS_CPROTO_UNSPEC, + 0, + PQUIRK_NOMODESENSE, + UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO, + NULL, NULL + }, + + { { USB_VENDOR_YANO, USB_PRODUCT_YANO_U640MO }, + UMASS_WPROTO_CBI_I, UMASS_CPROTO_ATAPI, + UMASS_QUIRK_FORCE_SHORT_INQUIRY, + 0, + UMATCH_VENDOR_PRODUCT, + NULL, NULL + }, + + { { USB_VENDOR_YEDATA, USB_PRODUCT_YEDATA_FLASHBUSTERU }, + UMASS_WPROTO_UNSPEC, UMASS_CPROTO_UFI, + UMASS_QUIRK_RS_NO_CLEAR_UA, + PQUIRK_NOMODESENSE, + UMATCH_VENDOR_PRODUCT_REV, + NULL, umass_fixup_yedata + }, + + { { USB_VENDOR_MINOLTA, USB_PRODUCT_MINOLTA_S304 }, + UMASS_WPROTO_UNSPEC, UMASS_CPROTO_UNSPEC, + UMASS_QUIRK_NO_MAX_LUN | UMASS_QUIRK_NO_START_STOP, + 0, + UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO, + NULL, NULL + }, + + { { USB_VENDOR_MINOLTA, USB_PRODUCT_MINOLTA_X }, + UMASS_WPROTO_UNSPEC, UMASS_CPROTO_UNSPEC, + UMASS_QUIRK_NO_MAX_LUN | UMASS_QUIRK_NO_START_STOP, + 0, + UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO, + NULL, NULL + }, + + { { USB_VENDOR_PEN, USB_PRODUCT_PEN_USBDISK }, + UMASS_WPROTO_UNSPEC, UMASS_CPROTO_UNSPEC, + UMASS_QUIRK_NO_MAX_LUN | UMASS_QUIRK_NO_START_STOP, + 0, + UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO, + NULL, NULL + }, + + /* InSystem Design ATA over USB devices */ + { { USB_VENDOR_ATI, USB_PRODUCT_ATI2_205 }, + UMASS_WPROTO_BBB, UMASS_CPROTO_ISD_ATA, + 0, + 0, + UMATCH_VENDOR_PRODUCT, + NULL, NULL + }, + { { USB_VENDOR_INSYSTEM, USB_PRODUCT_INSYSTEM_ATAPI }, + UMASS_WPROTO_BBB, UMASS_CPROTO_ISD_ATA, + 0, + 0, + UMATCH_VENDOR_PRODUCT, + NULL, NULL + }, + { { USB_VENDOR_INSYSTEM, USB_PRODUCT_INSYSTEM_DRIVEV2_5 }, + UMASS_WPROTO_BBB, UMASS_CPROTO_ISD_ATA, + 0, + 0, + UMATCH_VENDOR_PRODUCT, + NULL, NULL + }, + { { USB_VENDOR_INSYSTEM, USB_PRODUCT_INSYSTEM_ADAPTERV2 }, + UMASS_WPROTO_BBB, UMASS_CPROTO_ISD_ATA, + 0, + 0, + UMATCH_VENDOR_PRODUCT, + NULL, NULL + }, + { { USB_VENDOR_SONY, USB_PRODUCT_SONY_DRIVEV2 }, + UMASS_WPROTO_BBB, UMASS_CPROTO_ISD_ATA, + 0, + 0, + UMATCH_VENDOR_PRODUCT, + NULL, NULL + }, +}; + +const struct umass_quirk * +umass_lookup(u_int16_t vendor, u_int16_t product) +{ + return ((const struct umass_quirk *) + usb_lookup(umass_quirks, vendor, product)); +} + +Static usbd_status +umass_init_insystem(struct umass_softc *sc) +{ + usbd_status err; + + err = usbd_set_interface(sc->sc_iface, 1); + if (err) { + DPRINTF(UDMASS_USB, + ("%s: could not switch to Alt Interface 1\n", + USBDEVNAME(sc->sc_dev))); + return (err); + } + + return (USBD_NORMAL_COMPLETION); +} + +Static usbd_status +umass_init_shuttle(struct umass_softc *sc) +{ + usb_device_request_t req; + u_int8_t status[2]; + + /* The Linux driver does this */ + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = 1; + USETW(req.wValue, 0); + USETW(req.wIndex, sc->sc_ifaceno); + USETW(req.wLength, sizeof(status)); + + return (usbd_do_request(sc->sc_udev, &req, &status)); +} + +Static void +umass_fixup_sony(struct umass_softc *sc) +{ + usb_interface_descriptor_t *id; + + id = usbd_get_interface_descriptor(sc->sc_iface); + if (id->bInterfaceSubClass == 0xff) { + sc->sc_cmd = UMASS_CPROTO_RBC; + } +} + +Static void +umass_fixup_yedata(struct umass_softc *sc) +{ + usb_device_descriptor_t *dd; + + dd = usbd_get_device_descriptor(sc->sc_udev); + + /* + * Revisions < 1.28 do not handle the interrupt endpoint very well. + */ + if (UGETW(dd->bcdDevice) < 0x128) + sc->sc_wire = UMASS_WPROTO_CBI; + else + sc->sc_wire = UMASS_WPROTO_CBI_I; + + /* + * Revisions < 1.28 do not have the TEST UNIT READY command + * Revisions == 1.28 have a broken TEST UNIT READY + */ + if (UGETW(dd->bcdDevice) <= 0x128) + sc->sc_busquirks |= PQUIRK_NOTUR; +} diff --git a/sys/dev/usb/umass_quirks.h b/sys/dev/usb/umass_quirks.h new file mode 100644 index 00000000000..5348583ba34 --- /dev/null +++ b/sys/dev/usb/umass_quirks.h @@ -0,0 +1,62 @@ +/* $OpenBSD: umass_quirks.h,v 1.1 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: umass_quirks.h,v 1.3 2001/12/29 13:46:23 augustss Exp $ */ + +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by MAEKAWA Masahide (gehenna@NetBSD.org). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef _DEV_USB_UMASS_QUIRKS_H_ +#define _DEV_USB_UMASS_QUIRKS_H_ + +typedef usbd_status (*umass_init_quirk)(struct umass_softc *); +typedef void (*umass_fixup_quirk)(struct umass_softc *); + +struct umass_quirk { + struct usb_devno uq_dev; + + u_int8_t uq_wire; + u_int8_t uq_cmd; + u_int32_t uq_flags; + u_int32_t uq_busquirks; + int uq_match; + + umass_init_quirk uq_init; + umass_fixup_quirk uq_fixup; +}; + +const struct umass_quirk *umass_lookup(u_int16_t, u_int16_t); + +#endif /* _DEV_USB_UMASS_QUIRKS_H_ */ diff --git a/sys/dev/usb/umass_scsi.c b/sys/dev/usb/umass_scsi.c new file mode 100644 index 00000000000..2c1f1c2ba50 --- /dev/null +++ b/sys/dev/usb/umass_scsi.c @@ -0,0 +1,488 @@ +/* $OpenBSD: umass_scsi.c,v 1.1 2002/05/07 18:08:04 nate Exp $ */ +/* + * Copyright (c) 2001 Nathan L. Binkert + * All rights reserved. + * + * Permission to redistribute, use, copy, and modify this software + * without fee is hereby granted, provided that the following + * conditions are met: + * + * 1. This entire notice is included in all source code copies of any + * software which is or includes a copy or modification of this + * software. + * 2. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net) at + * Carlstedt Research & Technology. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "atapiscsi.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/conf.h> +#include <sys/buf.h> +#include <sys/device.h> +#include <sys/ioctl.h> +#include <sys/malloc.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usbdevs.h> + +#include <dev/usb/umassvar.h> +#include <dev/usb/umass_scsi.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> +#include <scsi/scsi_disk.h> +#include <machine/bus.h> + +struct umass_scsi_softc { + struct umassbus_softc base; + struct scsi_link sc_link; + struct scsi_adapter sc_adapter; + + usbd_status sc_sync_status; + struct scsi_sense sc_sense_cmd; +}; + + +#define SHORT_INQUIRY_LENGTH 36 /* XXX */ + +#define UMASS_SCSIID_HOST 0x00 +#define UMASS_SCSIID_DEVICE 0x01 + +#define UMASS_ATAPI_DRIVE 0 + +int umass_scsi_cmd(struct scsi_xfer *); +void umass_scsi_minphys(struct buf *); +int umass_scsi_ioctl(struct scsi_link *, u_long cmd, caddr_t addrp, int flag, + struct proc *p); + +void umass_scsi_cb(struct umass_softc *sc, void *priv, int residue, + int status); +void umass_scsi_sense_cb(struct umass_softc *sc, void *priv, int residue, + int status); +struct umass_scsi_softc *umass_scsi_setup(struct umass_softc *); + +struct scsi_device umass_scsi_dev = { NULL, NULL, NULL, NULL, }; + +int +umass_scsi_attach(struct umass_softc *sc) +{ + struct umass_scsi_softc *scbus; + + scbus = umass_scsi_setup(sc); + scbus->sc_link.adapter_target = UMASS_SCSIID_HOST; + scbus->sc_link.luns = sc->maxlun + 1; + + scbus->sc_adapter.scsi_cmd = umass_scsi_cmd; + scbus->sc_adapter.scsi_minphys = umass_scsi_minphys; + scbus->sc_adapter.ioctl = umass_scsi_ioctl; + + DPRINTF(UDMASS_USB, ("%s: umass_attach_bus: SCSI\n" + "sc = 0x%x, scbus = 0x%x\n", + USBDEVNAME(sc->sc_dev), sc, scbus)); + + scbus->base.sc_child = + config_found((struct device *)sc, &scbus->sc_link, scsiprint); + + return (0); +} + +#if NATAPISCSI > 0 +int +umass_atapi_attach(struct umass_softc *sc) +{ + struct umass_scsi_softc *scbus; + + scbus = umass_scsi_setup(sc); + scbus->sc_link.adapter_target = UMASS_SCSIID_HOST; + scbus->sc_link.luns = 1; + + scbus->sc_adapter.scsi_cmd = umass_scsi_cmd; + scbus->sc_adapter.scsi_minphys = umass_scsi_minphys; + scbus->sc_adapter.ioctl = umass_scsi_ioctl; + + DPRINTF(UDMASS_USB, ("%s: umass_attach_bus: ATAPI\n" + "sc = 0x%x, scbus = 0x%x\n", + USBDEVNAME(sc->sc_dev), sc, scbus)); + + scbus->base.sc_child = + config_found((struct device *)sc, &scbus->sc_link, scsiprint); + + return (0); +} +#endif + +struct umass_scsi_softc * +umass_scsi_setup(struct umass_softc *sc) +{ + struct umass_scsi_softc *scbus; + + scbus = malloc(sizeof(struct umass_scsi_softc), M_DEVBUF, M_WAITOK); + memset(&scbus->sc_link, 0, sizeof(struct scsi_link)); + memset(&scbus->sc_adapter, 0, sizeof(struct scsi_adapter)); + + sc->bus = (struct umassbus_softc *)scbus; + + scbus->sc_link.adapter_buswidth = 2; + scbus->sc_link.openings = 1; + scbus->sc_link.flags &= ~SDEV_ATAPI; + scbus->sc_link.device = &umass_scsi_dev; + scbus->sc_link.adapter = &scbus->sc_adapter; + scbus->sc_link.adapter_softc = sc; + scbus->sc_link.openings = 1; + + return (scbus); +} + +int +umass_scsi_cmd(struct scsi_xfer *xs) +{ + struct scsi_link *sc_link = xs->sc_link; + struct umass_softc *sc = sc_link->adapter_softc; + struct umass_scsi_softc *scbus = (struct umass_scsi_softc *)sc->bus; + + struct scsi_generic *cmd, trcmd; + int cmdlen, dir; + +#ifdef UMASS_DEBUG + microtime(&sc->tv); +#endif + + memset(&trcmd, 0, sizeof(trcmd)); + + DIF(UDMASS_UPPER, sc_link->flags |= SCSIDEBUG_LEVEL); + + DPRINTF(UDMASS_CMD, ("%s: umass_scsi_cmd: at %lu.%06lu: %d:%d " + "xs=%p cmd=0x%02x datalen=%d (quirks=0x%x, poll=%d)\n", + USBDEVNAME(sc->sc_dev), sc->tv.tv_sec, sc->tv.tv_usec, + sc_link->target, sc_link->lun, xs, xs->cmd->opcode, + xs->datalen, sc_link->quirks, xs->flags & SCSI_POLL)); + +#if defined(USB_DEBUG) && defined(SCSIDEBUG) + if (umassdebug & UDMASS_SCSI) + show_scsi_xs(xs); + else if (umassdebug & ~UDMASS_CMD) + show_scsi_cmd(xs); +#endif + + if (sc->sc_dying) { + xs->error = XS_DRIVER_STUFFUP; + goto done; + } + +#if defined(UMASS_DEBUG) + if (sc_link->target != UMASS_SCSIID_DEVICE) { + DPRINTF(UDMASS_SCSI, ("%s: wrong SCSI ID %d\n", + USBDEVNAME(sc->sc_dev), sc_link->target)); + xs->error = XS_DRIVER_STUFFUP; + goto done; + } +#endif + + cmd = xs->cmd; + cmdlen = xs->cmdlen; + + if (cmd->opcode == MODE_SENSE && + (sc_link->quirks & SDEV_NOMODESENSE)) { + xs->error = XS_TIMEOUT; + goto done; + } + + if (cmd->opcode == START_STOP && + (sc->sc_quirks & UMASS_QUIRK_NO_START_STOP)) { + xs->error = XS_NOERROR; + goto done; + } + + if (cmd->opcode == INQUIRY && + (sc->sc_quirks & UMASS_QUIRK_FORCE_SHORT_INQUIRY)) { + memcpy(&trcmd, cmd, sizeof(trcmd)); + trcmd.bytes[4] = SHORT_INQUIRY_LENGTH; + cmd = &trcmd; + } + + dir = DIR_NONE; + if (xs->datalen) { + switch (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) { + case SCSI_DATA_IN: + dir = DIR_IN; + break; + case SCSI_DATA_OUT: + dir = DIR_OUT; + break; + } + } + + if (xs->datalen > UMASS_MAX_TRANSFER_SIZE) { + printf("umass_cmd: large datalen, %d\n", xs->datalen); + xs->error = XS_DRIVER_STUFFUP; + goto done; + } + + if (xs->flags & SCSI_POLL) { + /* Use sync transfer. XXX Broken! */ + DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: sync dir=%d\n", dir)); + sc->sc_xfer_flags = USBD_SYNCHRONOUS; + scbus->sc_sync_status = USBD_INVAL; + sc->sc_methods->wire_xfer(sc, sc_link->lun, cmd, cmdlen, + xs->data, xs->datalen, dir, + xs->timeout, 0, xs); + sc->sc_xfer_flags = 0; + DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: done err=%d\n", + scbus->sc_sync_status)); + switch (scbus->sc_sync_status) { + case USBD_NORMAL_COMPLETION: + xs->error = XS_NOERROR; + break; + case USBD_TIMEOUT: + xs->error = XS_TIMEOUT; + break; + default: + xs->error = XS_DRIVER_STUFFUP; + break; + } + goto done; + } else { + DPRINTF(UDMASS_SCSI, + ("umass_scsi_cmd: async dir=%d, cmdlen=%d" + " datalen=%d\n", + dir, cmdlen, xs->datalen)); + sc->sc_methods->wire_xfer(sc, sc_link->lun, cmd, cmdlen, + xs->data, xs->datalen, dir, + xs->timeout, umass_scsi_cb, xs); + return (SUCCESSFULLY_QUEUED); + } + + /* Return if command finishes early. */ + done: + xs->flags |= ITSDONE; + + scsi_done(xs); + if (xs->flags & SCSI_POLL) + return (COMPLETE); + else + return (SUCCESSFULLY_QUEUED); +} + +void +umass_scsi_minphys(struct buf *bp) +{ + if (bp->b_bcount > UMASS_MAX_TRANSFER_SIZE) + bp->b_bcount = UMASS_MAX_TRANSFER_SIZE; + + minphys(bp); +} + +int +umass_scsi_ioctl(struct scsi_link *link, u_long cmd, caddr_t arg, int flag, + struct proc *p) +{ +#if 0 + struct umass_softc *sc = link->adapter_softc; +#endif + + switch (cmd) { +#if 0 + case SCBUSIORESET: + ccb->ccb_h.status = CAM_REQ_INPROG; + umass_reset(sc, umass_cam_cb, (void *) ccb); + return (0); +#endif + default: + return (ENOTTY); + } +} + +void +umass_scsi_cb(struct umass_softc *sc, void *priv, int residue, int status) +{ + struct umass_scsi_softc *scbus = (struct umass_scsi_softc *)sc->bus; + struct scsi_xfer *xs = priv; + struct scsi_link *link = xs->sc_link; + int cmdlen; + int s; +#ifdef UMASS_DEBUG + struct timeval tv; + u_int delta; + microtime(&tv); + delta = (tv.tv_sec - sc->tv.tv_sec) * 1000000 + + tv.tv_usec - sc->tv.tv_usec; +#endif + + DPRINTF(UDMASS_CMD, + ("umass_scsi_cb: at %lu.%06lu, delta=%u: xs=%p residue=%d" + " status=%d\n", tv.tv_sec, tv.tv_usec, delta, xs, residue, + status)); + + xs->resid = residue; + + switch (status) { + case STATUS_CMD_OK: + xs->error = XS_NOERROR; + break; + + case STATUS_CMD_UNKNOWN: + /* we can't issue REQUEST SENSE */ + if (xs->sc_link->quirks & PQUIRK_NOSENSE) { + /* + * If no residue and no other USB error, + * command succeeded. + */ + if (residue == 0) { + xs->error = XS_NOERROR; + break; + } + + /* + * Some devices return a short INQUIRY + * response, omitting response data from the + * "vendor specific data" on... + */ + if (xs->cmd->opcode == INQUIRY && + residue < xs->datalen) { + xs->error = XS_NOERROR; + break; + } + + xs->error = XS_DRIVER_STUFFUP; + break; + } + /* FALLTHROUGH */ + case STATUS_CMD_FAILED: + /* fetch sense data */ + memset(&scbus->sc_sense_cmd, 0, sizeof(scbus->sc_sense_cmd)); + scbus->sc_sense_cmd.opcode = REQUEST_SENSE; + scbus->sc_sense_cmd.byte2 = link->lun << SCSI_CMD_LUN_SHIFT; + scbus->sc_sense_cmd.length = sizeof(xs->sense); + + cmdlen = sizeof(scbus->sc_sense_cmd); + if (sc->sc_cmd == UMASS_CPROTO_UFI) /* XXX */ + cmdlen = UFI_COMMAND_LENGTH; + sc->sc_methods->wire_xfer(sc, link->lun, + &scbus->sc_sense_cmd, cmdlen, + &xs->sense, sizeof(xs->sense), + DIR_IN, xs->timeout, + umass_scsi_sense_cb, xs); + return; + + case STATUS_WIRE_FAILED: + xs->error = XS_RESET; + break; + + default: + panic("%s: Unknown status %d in umass_scsi_cb\n", + USBDEVNAME(sc->sc_dev), status); + } + + DPRINTF(UDMASS_CMD,("umass_scsi_cb: at %lu.%06lu: return error=%d, " + "status=0x%x resid=%d\n", + tv.tv_sec, tv.tv_usec, + xs->error, xs->status, xs->resid)); + + s = splbio(); + scsi_done(xs); + splx(s); +} + +/* + * Finalise a completed autosense operation + */ +void +umass_scsi_sense_cb(struct umass_softc *sc, void *priv, int residue, + int status) +{ + struct scsi_xfer *xs = priv; + int s; + + DPRINTF(UDMASS_CMD,("umass_scsi_sense_cb: xs=%p residue=%d " + "status=%d\n", xs, residue, status)); + + switch (status) { + case STATUS_CMD_OK: + case STATUS_CMD_UNKNOWN: + /* getting sense data succeeded */ + if (xs->cmd->opcode == INQUIRY && (xs->resid < xs->datalen || + (sc->sc_quirks & UMASS_QUIRK_RS_NO_CLEAR_UA /* XXX */))) { + /* + * Some drivers return SENSE errors even after INQUIRY. + * The upper layer doesn't like that. + */ + xs->error = XS_NOERROR; + break; + } + /* XXX look at residue */ + if (residue == 0 || residue == 14)/* XXX */ + xs->error = XS_SENSE; + else + xs->error = XS_SHORTSENSE; + break; + default: + DPRINTF(UDMASS_SCSI, ("%s: Autosense failed, status %d\n", + USBDEVNAME(sc->sc_dev), status)); + xs->error = XS_DRIVER_STUFFUP; + break; + } + + xs->status |= ITSDONE; + + DPRINTF(UDMASS_CMD,("umass_scsi_sense_cb: return xs->error=%d, " + "xs->status=0x%x xs->resid=%d\n", xs->error, xs->status, + xs->resid)); + + s = splbio(); + scsi_done(xs); + splx(s); +} diff --git a/sys/dev/usb/umass_scsi.h b/sys/dev/usb/umass_scsi.h new file mode 100644 index 00000000000..9237f5d364c --- /dev/null +++ b/sys/dev/usb/umass_scsi.h @@ -0,0 +1,31 @@ +/* $OpenBSD: umass_scsi.h,v 1.1 2002/05/07 18:08:04 nate Exp $ */ +/* + * Copyright (c) 2001 Nathan L. Binkert + * All rights reserved. + * + * Permission to redistribute, use, copy, and modify this software + * without fee is hereby granted, provided that the following + * conditions are met: + * + * 1. This entire notice is included in all source code copies of any + * software which is or includes a copy or modification of this + * software. + * 2. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +int umass_scsi_attach(struct umass_softc *sc); +int umass_atapi_attach(struct umass_softc *sc); diff --git a/sys/dev/usb/umass_scsipi.c b/sys/dev/usb/umass_scsipi.c new file mode 100644 index 00000000000..a37fb3cb425 --- /dev/null +++ b/sys/dev/usb/umass_scsipi.c @@ -0,0 +1,636 @@ +/* $NetBSD: umass_scsipi.c,v 1.4 2001/12/31 12:15:21 augustss Exp $ */ + +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net) at + * Carlstedt Research & Technology. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: umass_scsipi.c,v 1.4 2001/12/31 12:15:21 augustss Exp $"); + +#include "atapibus.h" +#include "scsibus.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/conf.h> +#include <sys/buf.h> +#include <sys/device.h> +#include <sys/ioctl.h> +#include <sys/malloc.h> + +/* SCSI & ATAPI */ +#include <sys/scsiio.h> +#include <dev/scsipi/scsi_all.h> +#include <dev/scsipi/scsipi_all.h> +#include <dev/scsipi/scsiconf.h> + +#include <dev/scsipi/atapiconf.h> + +#include <dev/scsipi/scsipi_disk.h> +#include <dev/scsipi/scsi_disk.h> +#include <dev/scsipi/scsi_changer.h> + +#include <dev/scsipi/atapi_disk.h> + +#include <sys/disk.h> /* XXX */ +#include <dev/scsipi/sdvar.h> /* XXX */ + +/* USB */ +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usbdevs.h> + +#include <dev/usb/umassvar.h> +#include <dev/usb/umass_scsipi.h> + +struct umass_scsipi_softc { + struct umassbus_softc base; + + struct atapi_adapter sc_atapi_adapter; +#define sc_adapter sc_atapi_adapter._generic + struct scsipi_channel sc_channel; + usbd_status sc_sync_status; + struct scsipi_sense sc_sense_cmd; +}; + + +#define SHORT_INQUIRY_LENGTH 36 /* XXX */ + +#define UMASS_SCSIID_HOST 0x00 +#define UMASS_SCSIID_DEVICE 0x01 + +#define UMASS_ATAPI_DRIVE 0 + +Static void umass_scsipi_request(struct scsipi_channel *, + scsipi_adapter_req_t, void *); +Static void umass_scsipi_minphys(struct buf *bp); +Static int umass_scsipi_ioctl(struct scsipi_channel *, u_long, + caddr_t, int, usb_proc_ptr); +Static int umass_scsipi_getgeom(struct scsipi_periph *periph, + struct disk_parms *, u_long sectors); + +Static void umass_scsipi_cb(struct umass_softc *sc, void *priv, + int residue, int status); +Static void umass_scsipi_sense_cb(struct umass_softc *sc, void *priv, + int residue, int status); + +Static struct umass_scsipi_softc *umass_scsipi_setup(struct umass_softc *sc); + +Static int scsipiprint(void *aux, const char *pnp); + +#if NATAPIBUS > 0 +Static void umass_atapi_probe_device(struct atapibus_softc *, int); + +const struct scsipi_bustype umass_atapi_bustype = { + SCSIPI_BUSTYPE_ATAPI, + atapi_scsipi_cmd, + atapi_interpret_sense, + atapi_print_addr, + scsi_kill_pending, +}; +#endif + + +#if NSCSIBUS > 0 +int +umass_scsi_attach(struct umass_softc *sc) +{ + struct umass_scsipi_softc *scbus; + + scbus = umass_scsipi_setup(sc); + + scbus->sc_channel.chan_bustype = &scsi_bustype; + scbus->sc_channel.chan_ntargets = UMASS_SCSIID_DEVICE + 1; + scbus->sc_channel.chan_nluns = sc->maxlun + 1; + scbus->sc_channel.chan_id = UMASS_SCSIID_HOST; + DPRINTF(UDMASS_USB, ("%s: umass_attach_bus: SCSI\n", + USBDEVNAME(sc->sc_dev))); + scbus->base.sc_child = + config_found(&sc->sc_dev, &scbus->sc_channel, scsipiprint); + + return (0); +} +#endif + +#if NATAPIBUS > 0 +int +umass_atapi_attach(struct umass_softc *sc) +{ + struct umass_scsipi_softc *scbus; + + scbus = umass_scsipi_setup(sc); + scbus->sc_atapi_adapter.atapi_probe_device = umass_atapi_probe_device; + + scbus->sc_channel.chan_bustype = &umass_atapi_bustype; + scbus->sc_channel.chan_ntargets = 2; + scbus->sc_channel.chan_nluns = 1; + + scbus->sc_channel.chan_defquirks |= sc->sc_busquirks; + DPRINTF(UDMASS_USB, ("%s: umass_attach_bus: ATAPI\n", + USBDEVNAME(sc->sc_dev))); + scbus->base.sc_child = + config_found(&sc->sc_dev, &scbus->sc_channel, scsipiprint); + + return (0); +} +#endif + +Static struct umass_scsipi_softc * +umass_scsipi_setup(struct umass_softc *sc) +{ + struct umass_scsipi_softc *scbus; + + scbus = malloc(sizeof *scbus, M_DEVBUF, M_WAITOK | M_ZERO); + sc->bus = &scbus->base; + + /* Only use big commands for USB SCSI devices. */ + sc->sc_busquirks |= PQUIRK_ONLYBIG; + + /* Fill in the adapter. */ + memset(&scbus->sc_adapter, 0, sizeof(scbus->sc_adapter)); + scbus->sc_adapter.adapt_dev = &sc->sc_dev; + scbus->sc_adapter.adapt_nchannels = 1; + scbus->sc_adapter.adapt_request = umass_scsipi_request; + scbus->sc_adapter.adapt_minphys = umass_scsipi_minphys; + scbus->sc_adapter.adapt_ioctl = umass_scsipi_ioctl; + scbus->sc_adapter.adapt_getgeom = umass_scsipi_getgeom; + + /* Fill in the channel. */ + memset(&scbus->sc_channel, 0, sizeof(scbus->sc_channel)); + scbus->sc_channel.chan_adapter = &scbus->sc_adapter; + scbus->sc_channel.chan_channel = 0; + scbus->sc_channel.chan_flags = SCSIPI_CHAN_OPENINGS; + scbus->sc_channel.chan_openings = 1; + scbus->sc_channel.chan_max_periph = 1; + scbus->sc_channel.chan_defquirks |= sc->sc_busquirks; + + return (scbus); +} + +Static int +scsipiprint(void *aux, const char *pnp) +{ + struct scsipi_channel *chan = aux; + + if (chan->chan_bustype->bustype_type == SCSIPI_BUSTYPE_SCSI) { +#if NSCSIBUS > 0 + return (scsiprint(aux, pnp)); +#else + if (pnp) + printf("scsibus at %s", pnp); + return (UNCONF); +#endif + } else { +#if NATAPIBUS > 0 + return (atapiprint(aux, pnp)); +#else + if (pnp) + printf("atapibus at %s", pnp); + return (UNCONF); +#endif + } +} + +Static void +umass_scsipi_request(struct scsipi_channel *chan, + scsipi_adapter_req_t req, void *arg) +{ + struct scsipi_adapter *adapt = chan->chan_adapter; + struct scsipi_periph *periph; + struct scsipi_xfer *xs; + struct umass_softc *sc = (void *)adapt->adapt_dev; + struct umass_scsipi_softc *scbus = (struct umass_scsipi_softc *)sc->bus; + struct scsipi_generic *cmd, trcmd; + int cmdlen; + int dir; +#ifdef UMASS_DEBUG + microtime(&sc->tv); +#endif + switch(req) { + case ADAPTER_REQ_RUN_XFER: + xs = arg; + periph = xs->xs_periph; + DIF(UDMASS_UPPER, periph->periph_dbflags |= SCSIPI_DEBUG_FLAGS); + + DPRINTF(UDMASS_CMD, ("%s: umass_scsi_cmd: at %lu.%06lu: %d:%d " + "xs=%p cmd=0x%02x datalen=%d (quirks=0x%x, poll=%d)\n", + USBDEVNAME(sc->sc_dev), sc->tv.tv_sec, sc->tv.tv_usec, + periph->periph_target, periph->periph_lun, + xs, xs->cmd->opcode, xs->datalen, + periph->periph_quirks, xs->xs_control & XS_CTL_POLL)); +#if defined(USB_DEBUG) && defined(SCSIPI_DEBUG) + if (umassdebug & UDMASS_SCSI) + show_scsipi_xs(xs); + else if (umassdebug & ~UDMASS_CMD) + show_scsipi_cmd(xs); +#endif + + if (sc->sc_dying) { + xs->error = XS_DRIVER_STUFFUP; + goto done; + } + +#ifdef UMASS_DEBUG + if (chan->chan_bustype->bustype_type == SCSIPI_BUSTYPE_ATAPI ? + periph->periph_target != UMASS_ATAPI_DRIVE : + periph->periph_target != UMASS_SCSIID_DEVICE) { + DPRINTF(UDMASS_SCSI, ("%s: wrong SCSI ID %d\n", + USBDEVNAME(sc->sc_dev), + periph->periph_target)); + xs->error = XS_DRIVER_STUFFUP; + goto done; + } +#endif + + cmd = xs->cmd; + cmdlen = xs->cmdlen; + + /* XXX should use transform */ + + if (cmd->opcode == START_STOP && + (sc->sc_quirks & UMASS_QUIRK_NO_START_STOP)) { + /*printf("%s: START_STOP\n", USBDEVNAME(sc->sc_dev));*/ + xs->error = XS_NOERROR; + goto done; + } + + if (cmd->opcode == INQUIRY && + (sc->sc_quirks & UMASS_QUIRK_FORCE_SHORT_INQUIRY)) { + /* + * some drives wedge when asked for full inquiry + * information. + */ + memcpy(&trcmd, cmd, sizeof trcmd); + trcmd.bytes[4] = SHORT_INQUIRY_LENGTH; + cmd = &trcmd; + } + + dir = DIR_NONE; + if (xs->datalen) { + switch (xs->xs_control & + (XS_CTL_DATA_IN | XS_CTL_DATA_OUT)) { + case XS_CTL_DATA_IN: + dir = DIR_IN; + break; + case XS_CTL_DATA_OUT: + dir = DIR_OUT; + break; + } + } + + if (xs->datalen > UMASS_MAX_TRANSFER_SIZE) { + printf("umass_cmd: large datalen, %d\n", xs->datalen); + xs->error = XS_DRIVER_STUFFUP; + goto done; + } + + if (xs->xs_control & XS_CTL_POLL) { + /* Use sync transfer. XXX Broken! */ + DPRINTF(UDMASS_SCSI, + ("umass_scsi_cmd: sync dir=%d\n", dir)); + sc->sc_xfer_flags = USBD_SYNCHRONOUS; + scbus->sc_sync_status = USBD_INVAL; + sc->sc_methods->wire_xfer(sc, periph->periph_lun, cmd, + cmdlen, xs->data, + xs->datalen, dir, + xs->timeout, 0, xs); + sc->sc_xfer_flags = 0; + DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: done err=%d\n", + scbus->sc_sync_status)); + switch (scbus->sc_sync_status) { + case USBD_NORMAL_COMPLETION: + xs->error = XS_NOERROR; + break; + case USBD_TIMEOUT: + xs->error = XS_TIMEOUT; + break; + default: + xs->error = XS_DRIVER_STUFFUP; + break; + } + goto done; + } else { + DPRINTF(UDMASS_SCSI, + ("umass_scsi_cmd: async dir=%d, cmdlen=%d" + " datalen=%d\n", + dir, cmdlen, xs->datalen)); + sc->sc_methods->wire_xfer(sc, periph->periph_lun, cmd, + cmdlen, xs->data, + xs->datalen, dir, + xs->timeout, + umass_scsipi_cb, xs); + return; + } + + /* Return if command finishes early. */ + done: + scsipi_done(xs); + return; + default: + /* Not supported, nothing to do. */ + ; + } +} + +Static void +umass_scsipi_minphys(struct buf *bp) +{ +#ifdef DIAGNOSTIC + if (bp->b_bcount <= 0) { + printf("umass_scsipi_minphys count(%ld) <= 0\n", + bp->b_bcount); + bp->b_bcount = UMASS_MAX_TRANSFER_SIZE; + } +#endif + if (bp->b_bcount > UMASS_MAX_TRANSFER_SIZE) + bp->b_bcount = UMASS_MAX_TRANSFER_SIZE; + minphys(bp); +} + +int +umass_scsipi_ioctl(struct scsipi_channel *chan, u_long cmd, caddr_t arg, + int flag, usb_proc_ptr p) +{ + /*struct umass_softc *sc = link->adapter_softc;*/ + /*struct umass_scsipi_softc *scbus = sc->bus;*/ + + switch (cmd) { +#if 0 + case SCBUSIORESET: + ccb->ccb_h.status = CAM_REQ_INPROG; + umass_reset(sc, umass_cam_cb, (void *) ccb); + return (0); +#endif + default: + return (ENOTTY); + } +} + +Static int +umass_scsipi_getgeom(struct scsipi_periph *periph, struct disk_parms *dp, + u_long sectors) +{ + struct umass_softc *sc = + (void *)periph->periph_channel->chan_adapter->adapt_dev; + + /* If it's not a floppy, we don't know what to do. */ + if (sc->sc_cmd != UMASS_CPROTO_UFI) + return (0); + + switch (sectors) { + case 1440: + /* Most likely a single density 3.5" floppy. */ + dp->heads = 2; + dp->sectors = 9; + dp->cyls = 80; + return (1); + case 2880: + /* Most likely a double density 3.5" floppy. */ + dp->heads = 2; + dp->sectors = 18; + dp->cyls = 80; + return (1); + default: + return (0); + } +} + +Static void +umass_scsipi_cb(struct umass_softc *sc, void *priv, int residue, int status) +{ + struct umass_scsipi_softc *scbus = (struct umass_scsipi_softc *)sc->bus; + struct scsipi_xfer *xs = priv; + struct scsipi_periph *periph = xs->xs_periph; + int cmdlen; + int s; +#ifdef UMASS_DEBUG + struct timeval tv; + u_int delta; + microtime(&tv); + delta = (tv.tv_sec - sc->tv.tv_sec) * 1000000 + tv.tv_usec - sc->tv.tv_usec; +#endif + + DPRINTF(UDMASS_CMD,("umass_scsipi_cb: at %lu.%06lu, delta=%u: xs=%p residue=%d" + " status=%d\n", tv.tv_sec, tv.tv_usec, delta, xs, residue, status)); + + xs->resid = residue; + + switch (status) { + case STATUS_CMD_OK: + xs->error = XS_NOERROR; + break; + + case STATUS_CMD_UNKNOWN: + /* we can't issue REQUEST SENSE */ + if (xs->xs_periph->periph_quirks & PQUIRK_NOSENSE) { + /* + * If no residue and no other USB error, + * command succeeded. + */ + if (residue == 0) { + xs->error = XS_NOERROR; + break; + } + + /* + * Some devices return a short INQUIRY + * response, omitting response data from the + * "vendor specific data" on... + */ + if (xs->cmd->opcode == INQUIRY && + residue < xs->datalen) { + xs->error = XS_NOERROR; + break; + } + + xs->error = XS_DRIVER_STUFFUP; + break; + } + /* FALLTHROUGH */ + case STATUS_CMD_FAILED: + /* fetch sense data */ + memset(&scbus->sc_sense_cmd, 0, sizeof(scbus->sc_sense_cmd)); + scbus->sc_sense_cmd.opcode = REQUEST_SENSE; + scbus->sc_sense_cmd.byte2 = periph->periph_lun << + SCSI_CMD_LUN_SHIFT; + scbus->sc_sense_cmd.length = sizeof(xs->sense); + + cmdlen = sizeof(scbus->sc_sense_cmd); + if (sc->sc_cmd == UMASS_CPROTO_UFI) /* XXX */ + cmdlen = UFI_COMMAND_LENGTH; + sc->sc_methods->wire_xfer(sc, periph->periph_lun, + &scbus->sc_sense_cmd, cmdlen, + &xs->sense, sizeof(xs->sense), + DIR_IN, xs->timeout, + umass_scsipi_sense_cb, xs); + return; + + case STATUS_WIRE_FAILED: + xs->error = XS_RESET; + break; + + default: + panic("%s: Unknown status %d in umass_scsipi_cb\n", + USBDEVNAME(sc->sc_dev), status); + } + + DPRINTF(UDMASS_CMD,("umass_scsipi_cb: at %lu.%06lu: return xs->error=" + "%d, xs->xs_status=0x%x xs->resid=%d\n", + tv.tv_sec, tv.tv_usec, + xs->error, xs->xs_status, xs->resid)); + + s = splbio(); + scsipi_done(xs); + splx(s); +} + +/* + * Finalise a completed autosense operation + */ +Static void +umass_scsipi_sense_cb(struct umass_softc *sc, void *priv, int residue, + int status) +{ + struct scsipi_xfer *xs = priv; + int s; + + DPRINTF(UDMASS_CMD,("umass_scsipi_sense_cb: xs=%p residue=%d " + "status=%d\n", xs, residue, status)); + + switch (status) { + case STATUS_CMD_OK: + case STATUS_CMD_UNKNOWN: + /* getting sense data succeeded */ + if (xs->cmd->opcode == INQUIRY && (xs->resid < xs->datalen || + (sc->sc_quirks & UMASS_QUIRK_RS_NO_CLEAR_UA /* XXX */))) { + /* + * Some drivers return SENSE errors even after INQUIRY. + * The upper layer doesn't like that. + */ + xs->error = XS_NOERROR; + break; + } + /* XXX look at residue */ + if (residue == 0 || residue == 14)/* XXX */ + xs->error = XS_SENSE; + else + xs->error = XS_SHORTSENSE; + break; + default: + DPRINTF(UDMASS_SCSI, ("%s: Autosense failed, status %d\n", + USBDEVNAME(sc->sc_dev), status)); + xs->error = XS_DRIVER_STUFFUP; + break; + } + + xs->xs_status |= XS_STS_DONE; + + DPRINTF(UDMASS_CMD,("umass_scsipi_sense_cb: return xs->error=%d, " + "xs->xs_status=0x%x xs->resid=%d\n", xs->error, xs->xs_status, + xs->resid)); + + s = splbio(); + scsipi_done(xs); + splx(s); +} + +#if NATAPIBUS > 0 +Static void +umass_atapi_probe_device(struct atapibus_softc *atapi, int target) +{ + struct scsipi_channel *chan = atapi->sc_channel; + struct scsipi_periph *periph; + struct scsipibus_attach_args sa; + char vendor[33], product[65], revision[17]; + struct scsipi_inquiry_data inqbuf; + + DPRINTF(UDMASS_SCSI,("umass_atapi_probe_device: atapi=%p target=%d\n", + atapi, target)); + + if (target != UMASS_ATAPI_DRIVE) /* only probe drive 0 */ + return; + + /* skip if already attached */ + if (scsipi_lookup_periph(chan, target, 0) != NULL) + return; + + periph = scsipi_alloc_periph(M_NOWAIT); + if (periph == NULL) { + printf("%s: can't allocate link for drive %d\n", + atapi->sc_dev.dv_xname, target); + return; + } + + DIF(UDMASS_UPPER, periph->periph_dbflags |= 1); /* XXX 1 */ + periph->periph_channel = chan; + periph->periph_switch = &atapi_probe_periphsw; + periph->periph_target = target; + periph->periph_quirks = chan->chan_defquirks; + + DPRINTF(UDMASS_SCSI, ("umass_atapi_probe_device: doing inquiry\n")); + /* Now go ask the device all about itself. */ + memset(&inqbuf, 0, sizeof(inqbuf)); + if (scsipi_inquire(periph, &inqbuf, + XS_CTL_DISCOVERY | XS_CTL_DATA_ONSTACK) != 0) { + DPRINTF(UDMASS_SCSI, ("umass_atapi_probe_device: " + "scsipi_inquire failed\n")); + free(periph, M_DEVBUF); + return; + } + + scsipi_strvis(vendor, 33, inqbuf.vendor, 8); + scsipi_strvis(product, 65, inqbuf.product, 16); + scsipi_strvis(revision, 17, inqbuf.revision, 4); + + sa.sa_periph = periph; + sa.sa_inqbuf.type = inqbuf.device; + sa.sa_inqbuf.removable = inqbuf.dev_qual2 & SID_REMOVABLE ? + T_REMOV : T_FIXED; + if (sa.sa_inqbuf.removable) + periph->periph_flags |= PERIPH_REMOVABLE; + sa.sa_inqbuf.vendor = vendor; + sa.sa_inqbuf.product = product; + sa.sa_inqbuf.revision = revision; + sa.sa_inqptr = NULL; + + DPRINTF(UDMASS_SCSI, ("umass_atapi_probedev: doing atapi_probedev on " + "'%s' '%s' '%s'\n", vendor, product, revision)); + atapi_probe_device(atapi, target, periph, &sa); + /* atapi_probe_device() frees the periph when there is no device.*/ +} +#endif diff --git a/sys/dev/usb/umass_scsipi.h b/sys/dev/usb/umass_scsipi.h new file mode 100644 index 00000000000..182b50f294b --- /dev/null +++ b/sys/dev/usb/umass_scsipi.h @@ -0,0 +1,41 @@ +/* $NetBSD: umass_scsipi.h,v 1.1 2001/12/24 13:25:53 augustss Exp $ */ + +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net) at + * Carlstedt Research & Technology. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +int umass_scsi_attach(struct umass_softc *sc); +int umass_atapi_attach(struct umass_softc *sc); diff --git a/sys/dev/usb/umassvar.h b/sys/dev/usb/umassvar.h new file mode 100644 index 00000000000..29e6b4a80cb --- /dev/null +++ b/sys/dev/usb/umassvar.h @@ -0,0 +1,271 @@ +/* $NetBSD: umassvar.h,v 1.15 2002/02/07 13:52:55 augustss Exp $ */ +/*- + * Copyright (c) 1999 MAEKAWA Masahide <bishop@rr.iij4u.or.jp>, + * Nick Hibma <n_hibma@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/sys/dev/usb/umass.c,v 1.13 2000/03/26 01:39:12 n_hibma Exp $ + */ + +#ifdef UMASS_DEBUG +#define DIF(m, x) if (umassdebug & (m)) do { x ; } while (0) +#define DPRINTF(m, x) if (umassdebug & (m)) logprintf x +#define UDMASS_UPPER 0x00008000 /* upper layer */ +#define UDMASS_GEN 0x00010000 /* general */ +#define UDMASS_SCSI 0x00020000 /* scsi */ +#define UDMASS_UFI 0x00040000 /* ufi command set */ +#define UDMASS_8070 0x00080000 /* 8070i command set */ +#define UDMASS_USB 0x00100000 /* USB general */ +#define UDMASS_BBB 0x00200000 /* Bulk-Only transfers */ +#define UDMASS_CBI 0x00400000 /* CBI transfers */ +#define UDMASS_ALL 0xffff0000 /* all of the above */ + +#define UDMASS_XFER 0x40000000 /* all transfers */ +#define UDMASS_CMD 0x80000000 + +extern int umassdebug; +#else +#define DIF(m, x) /* nop */ +#define DPRINTF(m, x) /* nop */ +#endif + +/* Generic definitions */ + +#define UFI_COMMAND_LENGTH 12 + +/* Direction for umass_*_transfer */ +#define DIR_NONE 0 +#define DIR_IN 1 +#define DIR_OUT 2 + +#define MS_TO_TICKS(ms) ((ms) * hz / 1000) + +/* Endpoints for umass */ +#define UMASS_BULKIN 0 +#define UMASS_BULKOUT 1 +#define UMASS_INTRIN 2 +#define UMASS_NEP 3 + +/* Bulk-Only features */ + +#define UR_BBB_RESET 0xff /* Bulk-Only reset */ +#define UR_BBB_GET_MAX_LUN 0xfe + +/* Command Block Wrapper */ +typedef struct { + uDWord dCBWSignature; +#define CBWSIGNATURE 0x43425355 + uDWord dCBWTag; + uDWord dCBWDataTransferLength; + uByte bCBWFlags; +#define CBWFLAGS_OUT 0x00 +#define CBWFLAGS_IN 0x80 + uByte bCBWLUN; + uByte bCDBLength; +#define CBWCDBLENGTH 16 + uByte CBWCDB[CBWCDBLENGTH]; +} umass_bbb_cbw_t; +#define UMASS_BBB_CBW_SIZE 31 + +/* Command Status Wrapper */ +typedef struct { + uDWord dCSWSignature; +#define CSWSIGNATURE 0x53425355 +#define CSWSIGNATURE_OLYMPUS_C1 0x55425355 + uDWord dCSWTag; + uDWord dCSWDataResidue; + uByte bCSWStatus; +#define CSWSTATUS_GOOD 0x0 +#define CSWSTATUS_FAILED 0x1 +#define CSWSTATUS_PHASE 0x2 +} umass_bbb_csw_t; +#define UMASS_BBB_CSW_SIZE 13 + +/* CBI features */ + +#define UR_CBI_ADSC 0x00 + +typedef unsigned char umass_cbi_cbl_t[16]; /* Command block */ + +typedef union { + struct { + uByte type; +#define IDB_TYPE_CCI 0x00 + uByte value; +#define IDB_VALUE_PASS 0x00 +#define IDB_VALUE_FAIL 0x01 +#define IDB_VALUE_PHASE 0x02 +#define IDB_VALUE_PERSISTENT 0x03 +#define IDB_VALUE_STATUS_MASK 0x03 + } common; + + struct { + uByte asc; + uByte ascq; + } ufi; +} umass_cbi_sbl_t; + +struct umass_softc; /* see below */ + +typedef void (*umass_callback)(struct umass_softc *, void *, int, int); +#define STATUS_CMD_OK 0 /* everything ok */ +#define STATUS_CMD_UNKNOWN 1 /* will have to fetch sense */ +#define STATUS_CMD_FAILED 2 /* transfer was ok, command failed */ +#define STATUS_WIRE_FAILED 3 /* couldn't even get command across */ + +typedef void (*umass_wire_xfer)(struct umass_softc *, int, void *, int, void *, + int, int, u_int, umass_callback, void *); +typedef void (*umass_wire_reset)(struct umass_softc *, int); +typedef void (*umass_wire_state)(usbd_xfer_handle, usbd_private_handle, + usbd_status); + +struct umass_wire_methods { + umass_wire_xfer wire_xfer; + umass_wire_reset wire_reset; + umass_wire_state wire_state; +}; + +struct umassbus_softc { + device_ptr_t sc_child; /* child device, for detach */ +}; + +/* the per device structure */ +struct umass_softc { + USBBASEDEVICE sc_dev; /* base device */ + usbd_device_handle sc_udev; /* device */ + usbd_interface_handle sc_iface; /* interface */ + int sc_ifaceno; /* interface number */ + + u_int8_t sc_epaddr[UMASS_NEP]; + usbd_pipe_handle sc_pipe[UMASS_NEP]; + usb_device_request_t sc_req; + + const struct umass_wire_methods *sc_methods; + + u_int8_t sc_wire; /* wire protocol */ +#define UMASS_WPROTO_UNSPEC 0 +#define UMASS_WPROTO_BBB 1 +#define UMASS_WPROTO_CBI 2 +#define UMASS_WPROTO_CBI_I 3 + + u_int8_t sc_cmd; /* command protocol */ +#define UMASS_CPROTO_UNSPEC 0 +#define UMASS_CPROTO_SCSI 1 +#define UMASS_CPROTO_ATAPI 2 +#define UMASS_CPROTO_UFI 3 +#define UMASS_CPROTO_RBC 4 +#define UMASS_CPROTO_ISD_ATA 5 + + u_int32_t sc_quirks; +#define UMASS_QUIRK_RS_NO_CLEAR_UA 0x00000002 +#define UMASS_QUIRK_NO_START_STOP 0x00000004 +#define UMASS_QUIRK_FORCE_SHORT_INQUIRY 0x00000008 +#define UMASS_QUIRK_WRONG_CSWSIG 0x00000010 +#define UMASS_QUIRK_NO_MAX_LUN 0x00000020 + + u_int32_t sc_busquirks; + + /* Bulk specific variables for transfers in progress */ + umass_bbb_cbw_t cbw; /* command block wrapper */ + umass_bbb_csw_t csw; /* command status wrapper*/ + /* CBI specific variables for transfers in progress */ + umass_cbi_cbl_t cbl; /* command block */ + umass_cbi_sbl_t sbl; /* status block */ + + /* xfer handles + * Most of our operations are initiated from interrupt context, so + * we need to avoid using the one that is in use. We want to avoid + * allocating them in the interrupt context as well. + */ + /* indices into array below */ +#define XFER_BBB_CBW 0 /* Bulk-Only */ +#define XFER_BBB_DATA 1 +#define XFER_BBB_DCLEAR 2 +#define XFER_BBB_CSW1 3 +#define XFER_BBB_CSW2 4 +#define XFER_BBB_SCLEAR 5 +#define XFER_BBB_RESET1 6 +#define XFER_BBB_RESET2 7 +#define XFER_BBB_RESET3 8 + +#define XFER_CBI_CB 0 /* CBI */ +#define XFER_CBI_DATA 1 +#define XFER_CBI_STATUS 2 +#define XFER_CBI_DCLEAR 3 +#define XFER_CBI_SCLEAR 4 +#define XFER_CBI_RESET1 5 +#define XFER_CBI_RESET2 6 +#define XFER_CBI_RESET3 7 + +#define XFER_NR 9 /* maximum number */ + + usbd_xfer_handle transfer_xfer[XFER_NR]; /* for ctrl xfers */ + + void *data_buffer; + + int transfer_dir; /* data direction */ + void *transfer_data; /* data buffer */ + int transfer_datalen; /* (maximum) length */ + int transfer_actlen; /* actual length */ + umass_callback transfer_cb; /* callback */ + void *transfer_priv; /* for callback */ + int transfer_status; + + int transfer_state; +#define TSTATE_IDLE 0 +#define TSTATE_BBB_COMMAND 1 /* CBW transfer */ +#define TSTATE_BBB_DATA 2 /* Data transfer */ +#define TSTATE_BBB_DCLEAR 3 /* clear endpt stall */ +#define TSTATE_BBB_STATUS1 4 /* clear endpt stall */ +#define TSTATE_BBB_SCLEAR 5 /* clear endpt stall */ +#define TSTATE_BBB_STATUS2 6 /* CSW transfer */ +#define TSTATE_BBB_RESET1 7 /* reset command */ +#define TSTATE_BBB_RESET2 8 /* in clear stall */ +#define TSTATE_BBB_RESET3 9 /* out clear stall */ +#define TSTATE_CBI_COMMAND 10 /* command transfer */ +#define TSTATE_CBI_DATA 11 /* data transfer */ +#define TSTATE_CBI_STATUS 12 /* status transfer */ +#define TSTATE_CBI_DCLEAR 13 /* clear ep stall */ +#define TSTATE_CBI_SCLEAR 14 /* clear ep stall */ +#define TSTATE_CBI_RESET1 15 /* reset command */ +#define TSTATE_CBI_RESET2 16 /* in clear stall */ +#define TSTATE_CBI_RESET3 17 /* out clear stall */ +#define TSTATE_STATES 18 /* # of states above */ + + + int timeout; /* in msecs */ + + u_int8_t maxlun; /* max lun supported */ + +#ifdef UMASS_DEBUG + struct timeval tv; +#endif + + int sc_xfer_flags; + char sc_dying; + + struct umassbus_softc *bus; /* bus dependent data */ +}; + +#define UMASS_MAX_TRANSFER_SIZE MAXBSIZE diff --git a/sys/dev/usb/umct.c b/sys/dev/usb/umct.c new file mode 100644 index 00000000000..954ce542c67 --- /dev/null +++ b/sys/dev/usb/umct.c @@ -0,0 +1,631 @@ +/* $OpenBSD: umct.c,v 1.1 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: umct.c,v 1.7 2001/12/17 14:19:39 ichiro Exp $ */ +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Ichiro FUKUHARA (ichiro@ichiro.org). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * MCT USB-RS232 Interface Controller + * http://www.mct.com.tw/p_u232.html + * http://www.dlink.com/products/usb/dsbs25 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/ioctl.h> +#include <sys/conf.h> +#include <sys/tty.h> +#include <sys/file.h> +#include <sys/select.h> +#include <sys/proc.h> +#include <sys/vnode.h> +#include <sys/device.h> +#include <sys/poll.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbcdc.h> + +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usbdevs.h> +#include <dev/usb/usb_quirks.h> + +#include <dev/usb/usbdevs.h> +#include <dev/usb/ucomvar.h> + +#include <dev/usb/umct.h> + +#ifdef UMCT_DEBUG +#define DPRINTFN(n, x) if (umctdebug > (n)) logprintf x +int umctdebug = 0; +#else +#define DPRINTFN(n, x) +#endif +#define DPRINTF(x) DPRINTFN(0, x) + +#define UMCT_CONFIG_INDEX 0 +#define UMCT_IFACE_INDEX 0 + +struct umct_softc { + USBBASEDEVICE sc_dev; /* base device */ + usbd_device_handle sc_udev; /* USB device */ + usbd_interface_handle sc_iface; /* interface */ + int sc_iface_number; /* interface number */ + u_int16_t sc_product; + + int sc_intr_number; /* interrupt number */ + usbd_pipe_handle sc_intr_pipe; /* interrupt pipe */ + u_char *sc_intr_buf; /* interrupt buffer */ + int sc_isize; + + usb_cdc_line_state_t sc_line_state; /* current line state */ + u_char sc_dtr; /* current DTR state */ + u_char sc_rts; /* current RTS state */ + u_char sc_break; /* set break */ + + u_char sc_status; + + device_ptr_t sc_subdev; /* ucom device */ + + u_char sc_dying; /* disconnecting */ + + u_char sc_lsr; /* Local status register */ + u_char sc_msr; /* umct status register */ + + u_int last_lcr; /* keep lcr register */ +}; + +/* + * These are the maximum number of bytes transferred per frame. + * The output buffer size cannot be increased due to the size encoding. + */ +#define UMCTIBUFSIZE 256 +#define UMCTOBUFSIZE 256 + +Static void umct_init(struct umct_softc *); +Static void umct_set_baudrate(struct umct_softc *, u_int); +Static void umct_set_lcr(struct umct_softc *, u_int); +Static void umct_intr(usbd_xfer_handle, usbd_private_handle, usbd_status); + +Static void umct_set(void *, int, int, int); +Static void umct_dtr(struct umct_softc *, int); +Static void umct_rts(struct umct_softc *, int); +Static void umct_break(struct umct_softc *, int); +Static void umct_set_line_state(struct umct_softc *); +Static void umct_get_status(void *, int portno, u_char *lsr, u_char *msr); +Static int umct_param(void *, int, struct termios *); +Static int umct_open(void *, int); +Static void umct_close(void *, int); + +struct ucom_methods umct_methods = { + umct_get_status, + umct_set, + umct_param, + NULL, + umct_open, + umct_close, + NULL, + NULL, +}; + +static const struct usb_devno umct_devs[] = { + /* MCT USB-232 Interface Products */ + { USB_VENDOR_MCT, USB_PRODUCT_MCT_USB232 }, + /* Sitecom USB-232 Products */ + { USB_VENDOR_MCT, USB_PRODUCT_MCT_SITECOM_USB232 }, + /* D-Link DU-H3SP USB BAY Hub Products */ + { USB_VENDOR_MCT, USB_PRODUCT_MCT_DU_H3SP_USB232 }, +}; +#define umct_lookup(v, p) usb_lookup(umct_devs, v, p) + +USB_DECLARE_DRIVER(umct); + +USB_MATCH(umct) +{ + USB_MATCH_START(umct, uaa); + + if (uaa->iface != NULL) + return (UMATCH_NONE); + + return (umct_lookup(uaa->vendor, uaa->product) != NULL ? + UMATCH_VENDOR_PRODUCT : UMATCH_NONE); +} + +USB_ATTACH(umct) +{ + USB_ATTACH_START(umct, sc, uaa); + usbd_device_handle dev = uaa->device; + usb_config_descriptor_t *cdesc; + usb_interface_descriptor_t *id; + usb_endpoint_descriptor_t *ed; + + char devinfo[1024]; + char *devname = USBDEVNAME(sc->sc_dev); + usbd_status err; + int i, found; + struct ucom_attach_args uca; + + usbd_devinfo(dev, 0, devinfo); + USB_ATTACH_SETUP; + printf("%s: %s\n", devname, devinfo); + + sc->sc_udev = dev; + sc->sc_product = uaa->product; + + DPRINTF(("\n\numct attach: sc=%p\n", sc)); + + /* initialize endpoints */ + uca.bulkin = uca.bulkout = -1; + sc->sc_intr_number = -1; + sc->sc_intr_pipe = NULL; + + /* Move the device into the configured state. */ + err = usbd_set_config_index(dev, UMCT_CONFIG_INDEX, 1); + if (err) { + printf("\n%s: failed to set configuration, err=%s\n", + devname, usbd_errstr(err)); + sc->sc_dying = 1; + USB_ATTACH_ERROR_RETURN; + } + + /* get the config descriptor */ + cdesc = usbd_get_config_descriptor(sc->sc_udev); + + if (cdesc == NULL) { + printf("%s: failed to get configuration descriptor\n", + USBDEVNAME(sc->sc_dev)); + sc->sc_dying = 1; + USB_ATTACH_ERROR_RETURN; + } + + /* get the interface */ + err = usbd_device2interface_handle(dev, UMCT_IFACE_INDEX, + &sc->sc_iface); + if (err) { + printf("\n%s: failed to get interface, err=%s\n", + devname, usbd_errstr(err)); + sc->sc_dying = 1; + USB_ATTACH_ERROR_RETURN; + } + + /* Find the bulk{in,out} and interrupt endpoints */ + + id = usbd_get_interface_descriptor(sc->sc_iface); + sc->sc_iface_number = id->bInterfaceNumber; + found = 0; + + for (i = 0; i < id->bNumEndpoints; i++) { + ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); + if (ed == NULL) { + printf("%s: no endpoint descriptor for %d\n", + USBDEVNAME(sc->sc_dev), i); + sc->sc_dying = 1; + USB_ATTACH_ERROR_RETURN; + } + + if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && + UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT && + found == 0) { + uca.bulkin = ed->bEndpointAddress; + found = 1; + } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && + UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { + uca.bulkout = ed->bEndpointAddress; + } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && + UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { + sc->sc_intr_number = ed->bEndpointAddress; + sc->sc_isize = UGETW(ed->wMaxPacketSize); + } + } + + if (uca.bulkin == -1) { + printf("%s: Could not find data bulk in\n", + USBDEVNAME(sc->sc_dev)); + sc->sc_dying = 1; + USB_ATTACH_ERROR_RETURN; + } + + if (uca.bulkout == -1) { + printf("%s: Could not find data bulk out\n", + USBDEVNAME(sc->sc_dev)); + sc->sc_dying = 1; + USB_ATTACH_ERROR_RETURN; + } + + if (sc->sc_intr_number== -1) { + printf("%s: Could not find interrupt in\n", + USBDEVNAME(sc->sc_dev)); + sc->sc_dying = 1; + USB_ATTACH_ERROR_RETURN; + } + + sc->sc_dtr = sc->sc_rts = 0; + uca.portno = UCOM_UNK_PORTNO; + /* bulkin, bulkout set above */ + uca.ibufsize = UMCTIBUFSIZE; + if (sc->sc_product == USB_PRODUCT_MCT_SITECOM_USB232) + uca.obufsize = 16; /* device is broken */ + else + uca.obufsize = UMCTOBUFSIZE; + uca.ibufsizepad = UMCTIBUFSIZE; + uca.opkthdrlen = 0; + uca.device = dev; + uca.iface = sc->sc_iface; + uca.methods = &umct_methods; + uca.arg = sc; + uca.info = NULL; + + umct_init(sc); + + usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, + USBDEV(sc->sc_dev)); + + DPRINTF(("umct: in=0x%x out=0x%x intr=0x%x\n", + uca.bulkin, uca.bulkout, sc->sc_intr_number )); + sc->sc_subdev = config_found_sm(self, &uca, ucomprint, ucomsubmatch); + + USB_ATTACH_SUCCESS_RETURN; +} + +USB_DETACH(umct) +{ + USB_DETACH_START(umct, sc); + int rv = 0; + + DPRINTF(("umct_detach: sc=%p flags=%d\n", sc, flags)); + + if (sc->sc_intr_pipe != NULL) { + usbd_abort_pipe(sc->sc_intr_pipe); + usbd_close_pipe(sc->sc_intr_pipe); + free(sc->sc_intr_buf, M_USBDEV); + sc->sc_intr_pipe = NULL; + } + + sc->sc_dying = 1; + if (sc->sc_subdev != NULL) { + rv = config_detach(sc->sc_subdev, flags); + sc->sc_subdev = NULL; + } + + usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, + USBDEV(sc->sc_dev)); + + return (rv); +} + +int +umct_activate(device_ptr_t self, enum devact act) +{ + struct umct_softc *sc = (struct umct_softc *)self; + int rv = 0; + + switch (act) { + case DVACT_ACTIVATE: + return (EOPNOTSUPP); + break; + + case DVACT_DEACTIVATE: + if (sc->sc_subdev != NULL) + rv = config_deactivate(sc->sc_subdev); + sc->sc_dying = 1; + break; + } + return (rv); +} + +void +umct_set_line_state(struct umct_softc *sc) +{ + usb_device_request_t req; + uByte ls; + + ls = (sc->sc_dtr ? MCR_DTR : 0) | + (sc->sc_rts ? MCR_RTS : 0); + + DPRINTF(("umct_set_line_state: DTR=%d,RTS=%d,ls=%02x\n", + sc->sc_dtr, sc->sc_rts, ls)); + + req.bmRequestType = UMCT_SET_REQUEST; + req.bRequest = REQ_SET_MCR; + USETW(req.wValue, 0); + USETW(req.wIndex, sc->sc_iface_number); + USETW(req.wLength, LENGTH_SET_MCR); + + (void)usbd_do_request(sc->sc_udev, &req, &ls); +} + +void +umct_set(void *addr, int portno, int reg, int onoff) +{ + struct umct_softc *sc = addr; + + switch (reg) { + case UCOM_SET_DTR: + umct_dtr(sc, onoff); + break; + case UCOM_SET_RTS: + umct_rts(sc, onoff); + break; + case UCOM_SET_BREAK: + umct_break(sc, onoff); + break; + default: + break; + } +} + +void +umct_dtr(struct umct_softc *sc, int onoff) +{ + + DPRINTF(("umct_dtr: onoff=%d\n", onoff)); + + if (sc->sc_dtr == onoff) + return; + sc->sc_dtr = onoff; + + umct_set_line_state(sc); +} + +void +umct_rts(struct umct_softc *sc, int onoff) +{ + DPRINTF(("umct_rts: onoff=%d\n", onoff)); + + if (sc->sc_rts == onoff) + return; + sc->sc_rts = onoff; + + umct_set_line_state(sc); +} + +void +umct_break(struct umct_softc *sc, int onoff) +{ + DPRINTF(("umct_break: onoff=%d\n", onoff)); + + umct_set_lcr(sc, onoff ? sc->last_lcr | LCR_SET_BREAK : + sc->last_lcr); +} + +void +umct_set_lcr(struct umct_softc *sc, u_int data) +{ + usb_device_request_t req; + uByte adata; + + adata = data; + req.bmRequestType = UMCT_SET_REQUEST; + req.bRequest = REQ_SET_LCR; + USETW(req.wValue, 0); + USETW(req.wIndex, sc->sc_iface_number); + USETW(req.wLength, LENGTH_SET_LCR); + + /* XXX should check */ + (void)usbd_do_request(sc->sc_udev, &req, &adata); +} + +void +umct_set_baudrate(struct umct_softc *sc, u_int rate) +{ + usb_device_request_t req; + uDWord arate; + u_int val; + + if (sc->sc_product == USB_PRODUCT_MCT_SITECOM_USB232) { + switch (rate) { + case 300: val = 0x01; break; + case 600: val = 0x02; break; + case 1200: val = 0x03; break; + case 2400: val = 0x04; break; + case 4800: val = 0x06; break; + case 9600: val = 0x08; break; + case 19200: val = 0x09; break; + case 38400: val = 0x0a; break; + case 57600: val = 0x0b; break; + case 115200: val = 0x0c; break; + default: val = -1; break; + } + } else { + val = UMCT_BAUD_RATE(rate); + } + USETDW(arate, val); + + req.bmRequestType = UMCT_SET_REQUEST; + req.bRequest = REQ_SET_BAUD_RATE; + USETW(req.wValue, 0); + USETW(req.wIndex, sc->sc_iface_number); + USETW(req.wLength, LENGTH_BAUD_RATE); + + /* XXX should check */ + (void)usbd_do_request(sc->sc_udev, &req, arate); +} + +void +umct_init(struct umct_softc *sc) +{ + umct_set_baudrate(sc, 9600); + umct_set_lcr(sc, LCR_DATA_BITS_8 | LCR_PARITY_NONE | LCR_STOP_BITS_1); +} + +int +umct_param(void *addr, int portno, struct termios *t) +{ + struct umct_softc *sc = addr; + u_int data = 0; + + DPRINTF(("umct_param: sc=%p\n", sc)); + + DPRINTF(("umct_param: BAUDRATE=%d\n", t->c_ospeed)); + + if (ISSET(t->c_cflag, CSTOPB)) + data |= LCR_STOP_BITS_2; + else + data |= LCR_STOP_BITS_1; + if (ISSET(t->c_cflag, PARENB)) { + if (ISSET(t->c_cflag, PARODD)) + data |= LCR_PARITY_ODD; + else + data |= LCR_PARITY_EVEN; + } else + data |= LCR_PARITY_NONE; + switch (ISSET(t->c_cflag, CSIZE)) { + case CS5: + data |= LCR_DATA_BITS_5; + break; + case CS6: + data |= LCR_DATA_BITS_6; + break; + case CS7: + data |= LCR_DATA_BITS_7; + break; + case CS8: + data |= LCR_DATA_BITS_8; + break; + } + + umct_set_baudrate(sc, t->c_ospeed); + + sc->last_lcr = data; + umct_set_lcr(sc, data); + + return (0); +} + +int +umct_open(void *addr, int portno) +{ + struct umct_softc *sc = addr; + int err, lcr_data; + + if (sc->sc_dying) + return (EIO); + + DPRINTF(("umct_open: sc=%p\n", sc)); + + /* initialize LCR */ + lcr_data = LCR_DATA_BITS_8 | LCR_PARITY_NONE | + LCR_STOP_BITS_1; + umct_set_lcr(sc, lcr_data); + + if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) { + sc->sc_status = 0; /* clear status bit */ + sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK); + err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_intr_number, + USBD_SHORT_XFER_OK, &sc->sc_intr_pipe, sc, + sc->sc_intr_buf, sc->sc_isize, + umct_intr, USBD_DEFAULT_INTERVAL); + if (err) { + DPRINTF(("%s: cannot open interrupt pipe (addr %d)\n", + USBDEVNAME(sc->sc_dev), sc->sc_intr_number)); + return (EIO); + } + } + + return (0); +} + +void +umct_close(void *addr, int portno) +{ + struct umct_softc *sc = addr; + int err; + + if (sc->sc_dying) + return; + + DPRINTF(("umct_close: close\n")); + + if (sc->sc_intr_pipe != NULL) { + err = usbd_abort_pipe(sc->sc_intr_pipe); + if (err) + printf("%s: abort interrupt pipe failed: %s\n", + USBDEVNAME(sc->sc_dev), usbd_errstr(err)); + err = usbd_close_pipe(sc->sc_intr_pipe); + if (err) + printf("%s: close interrupt pipe failed: %s\n", + USBDEVNAME(sc->sc_dev), usbd_errstr(err)); + free(sc->sc_intr_buf, M_USBDEV); + sc->sc_intr_pipe = NULL; + } +} + +void +umct_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) +{ + struct umct_softc *sc = priv; + u_char *buf = sc->sc_intr_buf; + u_char mstatus, lstatus; + + if (sc->sc_dying) + return; + + if (status != USBD_NORMAL_COMPLETION) { + if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) + return; + + DPRINTF(("%s: abnormal status: %s\n", USBDEVNAME(sc->sc_dev), + usbd_errstr(status))); + usbd_clear_endpoint_stall_async(sc->sc_intr_pipe); + return; + } + + DPRINTF(("%s: umct status = MSR:%02x, LSR:%02x\n", + USBDEVNAME(sc->sc_dev), buf[0],buf[1])); + + sc->sc_lsr = sc->sc_msr = 0; + mstatus = buf[0]; + lstatus = buf[1]; + if (ISSET(mstatus, MSR_DSR)) + sc->sc_msr |= UMSR_DSR; + if (ISSET(mstatus, MSR_DCD)) + sc->sc_msr |= UMSR_DCD; + ucom_status_change((struct ucom_softc *)sc->sc_subdev); +} + +void +umct_get_status(void *addr, int portno, u_char *lsr, u_char *msr) +{ + struct umct_softc *sc = addr; + + DPRINTF(("umct_get_status:\n")); + + if (lsr != NULL) + *lsr = sc->sc_lsr; + if (msr != NULL) + *msr = sc->sc_msr; +} diff --git a/sys/dev/usb/umct.h b/sys/dev/usb/umct.h new file mode 100644 index 00000000000..e162280d74c --- /dev/null +++ b/sys/dev/usb/umct.h @@ -0,0 +1,109 @@ +/* $OpenBSD: umct.h,v 1.1 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: umct.h,v 1.1 2001/03/28 18:42:13 ichiro Exp $ */ +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Ichiro FUKUHARA (ichiro@ichiro.org). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Vendor Request Interface + */ +#define UMCT_SET_REQUEST 0x40 +#define UMCT_GET_REQUEST 0xc0 + +#define REQ_SET_BAUD_RATE 5 /* Set Baud Rate Divisor */ +#define LENGTH_BAUD_RATE 4 + +#define REQ_GET_MSR 2 /* Get Modem Status Register (MSR) */ +#define LENGTH_GET_MSR 1 + +#define REQ_GET_LCR 6 /* Get Line Control Register (LCR) */ +#define LENGTH_GET_LCR 1 + +#define REQ_SET_LCR 7 /* Set Line Control Register (LCR) */ +#define LENGTH_SET_LCR 1 + +#define REQ_SET_MCR 10 /* Set Modem Control Register (MCR) */ +#define LENGTH_SET_MCR 1 + +/* + * Baud rate (divisor) + */ +#define UMCT_BAUD_RATE(b) (115200/b) + +/* + * Line Control Register (LCR) + */ +#define LCR_SET_BREAK 0x40 +#define LCR_PARITY_EVEN 0x18 +#define LCR_PARITY_ODD 0x08 +#define LCR_PARITY_NONE 0x00 +#define LCR_DATA_BITS_5 0x00 +#define LCR_DATA_BITS_6 0x01 +#define LCR_DATA_BITS_7 0x02 +#define LCR_DATA_BITS_8 0x03 +#define LCR_STOP_BITS_2 0x04 +#define LCR_STOP_BITS_1 0x00 + +/* + * Modem Control Register (MCR) + */ +#define MCR_NONE 0x8 +#define MCR_RTS 0xa +#define MCR_DTR 0x9 + +/* + * Modem Status Register (MSR) + */ +#define MSR_CD 0x80 /* Current CD */ +#define MSR_RI 0x40 /* Current RI */ +#define MSR_DSR 0x20 /* Current DSR */ +#define MSR_CTS 0x10 /* Current CTS */ +#define MSR_DCD 0x08 /* Delta CD */ +#define MSR_DRI 0x04 /* Delta RI */ +#define MSR_DDSR 0x02 /* Delta DSR */ +#define MSR_DCTS 0x01 /* Delta CTS */ + +/* + * Line Status Register (LSR) + */ +#define LSR_ERR 0x80 /* OE | PE | FE | BI */ +#define LSR_TEMT 0x40 /* transmit register empty */ +#define LSR_THRE 0x20 /* transmit holding register empty */ +#define LSR_BI 0x10 /* break indicator */ +#define LSR_FE 0x08 /* framing error */ +#define LSR_OE 0x02 /* overrun error */ +#define LSR_PE 0x04 /* parity error */ +#define LSR_OE 0x02 /* overrun error */ +#define LSR_DR 0x01 /* receive data ready */ diff --git a/sys/dev/usb/umidi.c b/sys/dev/usb/umidi.c new file mode 100644 index 00000000000..09b8df082a8 --- /dev/null +++ b/sys/dev/usb/umidi.c @@ -0,0 +1,1374 @@ +/* $OpenBSD: umidi.c,v 1.1 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: umidi.c,v 1.14 2002/03/08 17:24:06 kent Exp $ */ +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Takuya SHIOZAKI (tshiozak@netbsd.org). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/ioctl.h> +#include <sys/conf.h> +#include <sys/file.h> +#include <sys/select.h> +#include <sys/proc.h> +#include <sys/vnode.h> +#include <sys/poll.h> +#include <sys/lock.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> + +#include <dev/usb/usbdevs.h> +#include <dev/usb/uaudioreg.h> +#include <dev/usb/umidireg.h> +#include <dev/usb/umidivar.h> +#include <dev/usb/umidi_quirks.h> + +#include <dev/midi_if.h> + +#ifdef UMIDI_DEBUG +#define DPRINTF(x) if (umididebug) printf x +#define DPRINTFN(n,x) if (umididebug >= (n)) printf x +int umididebug = 0; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + + +static int umidi_open(void *, int, + void (*)(void *, int), void (*)(void *), void *); +static void umidi_close(void *); +static int umidi_output(void *, int); +static void umidi_getinfo(void *, struct midi_info *); + +static usbd_status alloc_pipe(struct umidi_endpoint *); +static void free_pipe(struct umidi_endpoint *); + +static usbd_status alloc_all_endpoints(struct umidi_softc *); +static void free_all_endpoints(struct umidi_softc *); + +static usbd_status alloc_all_jacks(struct umidi_softc *); +static void free_all_jacks(struct umidi_softc *); +static usbd_status bind_jacks_to_mididev(struct umidi_softc *, + struct umidi_jack *, + struct umidi_jack *, + struct umidi_mididev *); +static void unbind_jacks_from_mididev(struct umidi_mididev *); +static void unbind_all_jacks(struct umidi_softc *); +static usbd_status assign_all_jacks_automatically(struct umidi_softc *); +static usbd_status open_out_jack(struct umidi_jack *, void *, + void (*)(void *)); +static usbd_status open_in_jack(struct umidi_jack *, void *, + void (*)(void *, int)); +static void close_out_jack(struct umidi_jack *); +static void close_in_jack(struct umidi_jack *); + +static usbd_status attach_mididev(struct umidi_softc *, + struct umidi_mididev *); +static usbd_status detach_mididev(struct umidi_mididev *, int); +static usbd_status deactivate_mididev(struct umidi_mididev *); +static usbd_status alloc_all_mididevs(struct umidi_softc *, int); +static void free_all_mididevs(struct umidi_softc *); +static usbd_status attach_all_mididevs(struct umidi_softc *); +static usbd_status detach_all_mididevs(struct umidi_softc *, int); +static usbd_status deactivate_all_mididevs(struct umidi_softc *); + +#ifdef UMIDI_DEBUG +static void dump_sc(struct umidi_softc *); +static void dump_ep(struct umidi_endpoint *); +static void dump_jack(struct umidi_jack *); +#endif + +static void init_packet(struct umidi_packet *); + +static usbd_status start_input_transfer(struct umidi_endpoint *); +static usbd_status start_output_transfer(struct umidi_endpoint *); +static int out_jack_output(struct umidi_jack *, int); +static void in_intr(usbd_xfer_handle, usbd_private_handle, usbd_status); +static void out_intr(usbd_xfer_handle, usbd_private_handle, usbd_status); +static void out_build_packet(int, struct umidi_packet *, uByte); + + +struct midi_hw_if umidi_hw_if = { + umidi_open, + umidi_close, + umidi_output, + umidi_getinfo, + 0, /* ioctl */ +}; + +USB_DECLARE_DRIVER(umidi); + +USB_MATCH(umidi) +{ + USB_MATCH_START(umidi, uaa); + usb_interface_descriptor_t *id; + + DPRINTFN(1,("umidi_match\n")); + + if (uaa->iface == NULL) + return UMATCH_NONE; + + if (umidi_search_quirk(uaa->vendor, uaa->product, uaa->ifaceno)) + return UMATCH_IFACECLASS_IFACESUBCLASS; + + id = usbd_get_interface_descriptor(uaa->iface); + if (id!=NULL && + id->bInterfaceClass==UICLASS_AUDIO && + id->bInterfaceSubClass==UISUBCLASS_MIDISTREAM) + return UMATCH_IFACECLASS_IFACESUBCLASS; + + return UMATCH_NONE; +} + +USB_ATTACH(umidi) +{ + usbd_status err; + USB_ATTACH_START(umidi, sc, uaa); + char devinfo[1024]; + + DPRINTFN(1,("umidi_attach\n")); + + usbd_devinfo(uaa->device, 0, devinfo); + printf("\n%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo); + + sc->sc_iface = uaa->iface; + sc->sc_udev = uaa->device; + + sc->sc_quirk = + umidi_search_quirk(uaa->vendor, uaa->product, uaa->ifaceno); + printf("%s: ", USBDEVNAME(sc->sc_dev)); + umidi_print_quirk(sc->sc_quirk); + + + err = alloc_all_endpoints(sc); + if (err!=USBD_NORMAL_COMPLETION) { + printf("%s: alloc_all_endpoints failed. (err=%d)\n", + USBDEVNAME(sc->sc_dev), err); + goto error; + } + err = alloc_all_jacks(sc); + if (err!=USBD_NORMAL_COMPLETION) { + free_all_endpoints(sc); + printf("%s: alloc_all_jacks failed. (err=%d)\n", + USBDEVNAME(sc->sc_dev), err); + goto error; + } + printf("%s: out=%d, in=%d\n", + USBDEVNAME(sc->sc_dev), + sc->sc_out_num_jacks, sc->sc_in_num_jacks); + + err = assign_all_jacks_automatically(sc); + if (err!=USBD_NORMAL_COMPLETION) { + unbind_all_jacks(sc); + free_all_jacks(sc); + free_all_endpoints(sc); + printf("%s: assign_all_jacks_automatically failed. (err=%d)\n", + USBDEVNAME(sc->sc_dev), err); + goto error; + } + err = attach_all_mididevs(sc); + if (err!=USBD_NORMAL_COMPLETION) { + free_all_jacks(sc); + free_all_endpoints(sc); + printf("%s: attach_all_mididevs failed. (err=%d)\n", + USBDEVNAME(sc->sc_dev), err); + } + +#ifdef UMIDI_DEBUG + dump_sc(sc); +#endif + + usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, + sc->sc_udev, USBDEV(sc->sc_dev)); + + USB_ATTACH_SUCCESS_RETURN; +error: + printf("%s: disabled.\n", USBDEVNAME(sc->sc_dev)); + sc->sc_dying = 1; + USB_ATTACH_ERROR_RETURN; +} + +int +umidi_activate(device_ptr_t self, enum devact act) +{ + struct umidi_softc *sc = (struct umidi_softc *)self; + + switch (act) { + case DVACT_ACTIVATE: + DPRINTFN(1,("umidi_activate (activate)\n")); + + return EOPNOTSUPP; + break; + case DVACT_DEACTIVATE: + DPRINTFN(1,("umidi_activate (deactivate)\n")); + sc->sc_dying = 1; + deactivate_all_mididevs(sc); + break; + } + return 0; +} + +USB_DETACH(umidi) +{ + USB_DETACH_START(umidi, sc); + + DPRINTFN(1,("umidi_detach\n")); + + sc->sc_dying = 1; + detach_all_mididevs(sc, flags); + free_all_mididevs(sc); + free_all_jacks(sc); + free_all_endpoints(sc); + + usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, + USBDEV(sc->sc_dev)); + + return 0; +} + + +/* + * midi_if stuffs + */ +int +umidi_open(void *addr, + int flags, + void (*iintr)(void *, int), + void (*ointr)(void *), + void *arg) +{ + struct umidi_mididev *mididev = addr; + struct umidi_softc *sc = mididev->sc; + + DPRINTF(("umidi_open: sc=%p\n", sc)); + + if (!sc) + return ENXIO; + if (mididev->opened) + return EBUSY; + if (sc->sc_dying) + return EIO; + + mididev->opened = 1; + mididev->flags = flags; + if ((mididev->flags & FWRITE) && mididev->out_jack) + open_out_jack(mididev->out_jack, arg, ointr); + if ((mididev->flags & FREAD) && mididev->in_jack) { + open_in_jack(mididev->in_jack, arg, iintr); + } + + return 0; +} + +void +umidi_close(void *addr) +{ + int s; + struct umidi_mididev *mididev = addr; + + s = splusb(); + if ((mididev->flags & FWRITE) && mididev->out_jack) + close_out_jack(mididev->out_jack); + if ((mididev->flags & FREAD) && mididev->in_jack) + close_in_jack(mididev->in_jack); + mididev->opened = 0; + splx(s); +} + +int +umidi_output(void *addr, int d) +{ + struct umidi_mididev *mididev = addr; + + if (!mididev->out_jack || !mididev->opened) + return EIO; + + return out_jack_output(mididev->out_jack, d); +} + +void +umidi_getinfo(void *addr, struct midi_info *mi) +{ + struct umidi_mididev *mididev = addr; +/* struct umidi_softc *sc = mididev->sc; */ + + mi->name = "USB MIDI I/F"; /* XXX: model name */ + mi->props = MIDI_PROP_OUT_INTR; + if (mididev->in_jack) + mi->props |= MIDI_PROP_CAN_INPUT; +} + + +/* + * each endpoint stuffs + */ + +/* alloc/free pipe */ +static usbd_status +alloc_pipe(struct umidi_endpoint *ep) +{ + struct umidi_softc *sc = ep->sc; + usbd_status err; + + DPRINTF(("%s: alloc_pipe %p\n", USBDEVNAME(sc->sc_dev), ep)); + LIST_INIT(&ep->queue_head); + ep->xfer = usbd_alloc_xfer(sc->sc_udev); + if (ep->xfer == NULL) { + err = USBD_NOMEM; + goto quit; + } + ep->buffer = usbd_alloc_buffer(ep->xfer, UMIDI_PACKET_SIZE); + if (ep->buffer == NULL) { + usbd_free_xfer(ep->xfer); + err = USBD_NOMEM; + goto quit; + } + err = usbd_open_pipe(sc->sc_iface, ep->addr, 0, &ep->pipe); + if (err) + usbd_free_xfer(ep->xfer); +quit: + return err; +} + +static void +free_pipe(struct umidi_endpoint *ep) +{ + DPRINTF(("%s: free_pipe %p\n", USBDEVNAME(ep->sc->sc_dev), ep)); + usbd_abort_pipe(ep->pipe); + usbd_close_pipe(ep->pipe); + usbd_free_xfer(ep->xfer); +} + + +/* alloc/free the array of endpoint structures */ + +static usbd_status alloc_all_endpoints_fixed_ep(struct umidi_softc *); +static usbd_status alloc_all_endpoints_yamaha(struct umidi_softc *); +static usbd_status alloc_all_endpoints_genuine(struct umidi_softc *); + +static usbd_status +alloc_all_endpoints(struct umidi_softc *sc) +{ + usbd_status err; + struct umidi_endpoint *ep; + int i; + + if (UMQ_ISTYPE(sc, UMQ_TYPE_FIXED_EP)) { + err = alloc_all_endpoints_fixed_ep(sc); + } else if (UMQ_ISTYPE(sc, UMQ_TYPE_YAMAHA)) { + err = alloc_all_endpoints_yamaha(sc); + } else { + err = alloc_all_endpoints_genuine(sc); + } + if (err!=USBD_NORMAL_COMPLETION) + return err; + + ep = sc->sc_endpoints; + for (i=sc->sc_out_num_endpoints+sc->sc_in_num_endpoints; i>0; i--) { + err = alloc_pipe(ep++); + if (err!=USBD_NORMAL_COMPLETION) { + for (; ep!=sc->sc_endpoints; ep--) + free_pipe(ep-1); + free(sc->sc_endpoints, M_USBDEV); + sc->sc_endpoints = sc->sc_out_ep = sc->sc_in_ep = NULL; + break; + } + } + return err; +} + +static void +free_all_endpoints(struct umidi_softc *sc) +{ + int i; + for (i=0; i<sc->sc_in_num_endpoints+sc->sc_out_num_endpoints; i++) + free_pipe(&sc->sc_endpoints[i]); + if (sc->sc_endpoints != NULL) + free(sc->sc_endpoints, M_USBDEV); + sc->sc_endpoints = sc->sc_out_ep = sc->sc_in_ep = NULL; +} + +static usbd_status +alloc_all_endpoints_fixed_ep(struct umidi_softc *sc) +{ + usbd_status err; + struct umq_fixed_ep_desc *fp; + struct umidi_endpoint *ep; + usb_endpoint_descriptor_t *epd; + int i; + + fp = umidi_get_quirk_data_from_type(sc->sc_quirk, + UMQ_TYPE_FIXED_EP); + sc->sc_out_num_jacks = 0; + sc->sc_in_num_jacks = 0; + sc->sc_out_num_endpoints = fp->num_out_ep; + sc->sc_in_num_endpoints = fp->num_in_ep; + sc->sc_endpoints = malloc(sizeof(*sc->sc_out_ep)* + (sc->sc_out_num_endpoints+ + sc->sc_in_num_endpoints), + M_USBDEV, M_WAITOK); + if (!sc->sc_endpoints) { + return USBD_NOMEM; + } + sc->sc_out_ep = sc->sc_out_num_endpoints ? sc->sc_endpoints : NULL; + sc->sc_in_ep = + sc->sc_in_num_endpoints ? + sc->sc_endpoints+sc->sc_out_num_endpoints : NULL; + + ep = &sc->sc_out_ep[0]; + for (i=0; i<sc->sc_out_num_endpoints; i++) { + epd = usbd_interface2endpoint_descriptor( + sc->sc_iface, + fp->out_ep[i].ep); + if (!epd) { + printf("%s: cannot get endpoint descriptor(out:%d)\n", + USBDEVNAME(sc->sc_dev), fp->out_ep[i].ep); + err = USBD_INVAL; + goto error; + } + if (UE_GET_XFERTYPE(epd->bmAttributes)!=UE_BULK || + UE_GET_DIR(epd->bEndpointAddress)!=UE_DIR_OUT) { + printf("%s: illegal endpoint(out:%d)\n", + USBDEVNAME(sc->sc_dev), fp->out_ep[i].ep); + err = USBD_INVAL; + goto error; + } + ep->sc = sc; + ep->addr = epd->bEndpointAddress; + ep->num_jacks = fp->out_ep[i].num_jacks; + sc->sc_out_num_jacks += fp->out_ep[i].num_jacks; + ep->num_open = 0; + memset(ep->jacks, 0, sizeof(ep->jacks)); + LIST_INIT(&ep->queue_head); + ep++; + } + ep = &sc->sc_in_ep[0]; + for (i=0; i<sc->sc_in_num_endpoints; i++) { + epd = usbd_interface2endpoint_descriptor( + sc->sc_iface, + fp->in_ep[i].ep); + if (!epd) { + printf("%s: cannot get endpoint descriptor(in:%d)\n", + USBDEVNAME(sc->sc_dev), fp->in_ep[i].ep); + err = USBD_INVAL; + goto error; + } + if (UE_GET_XFERTYPE(epd->bmAttributes)!=UE_BULK || + UE_GET_DIR(epd->bEndpointAddress)!=UE_DIR_IN) { + printf("%s: illegal endpoint(in:%d)\n", + USBDEVNAME(sc->sc_dev), fp->in_ep[i].ep); + err = USBD_INVAL; + goto error; + } + ep->sc = sc; + ep->addr = epd->bEndpointAddress; + ep->num_jacks = fp->in_ep[i].num_jacks; + sc->sc_in_num_jacks += fp->in_ep[i].num_jacks; + ep->num_open = 0; + memset(ep->jacks, 0, sizeof(ep->jacks)); + ep++; + } + + return USBD_NORMAL_COMPLETION; +error: + free(sc->sc_endpoints, M_USBDEV); + sc->sc_endpoints = NULL; + return err; +} + +static usbd_status +alloc_all_endpoints_yamaha(struct umidi_softc *sc) +{ + /* This driver currently supports max 1in/1out bulk endpoints */ + usb_descriptor_t *desc; + usb_endpoint_descriptor_t *epd; + int out_addr, in_addr, i; + int dir; + size_t remain, descsize; + + sc->sc_out_num_jacks = sc->sc_in_num_jacks = 0; + out_addr = in_addr = 0; + + /* detect endpoints */ + desc = TO_D(usbd_get_interface_descriptor(sc->sc_iface)); + for (i=(int)TO_IFD(desc)->bNumEndpoints-1; i>=0; i--) { + epd = usbd_interface2endpoint_descriptor(sc->sc_iface, i); + if (UE_GET_XFERTYPE(epd->bmAttributes) == UE_BULK) { + dir = UE_GET_DIR(epd->bEndpointAddress); + if (dir==UE_DIR_OUT && !out_addr) + out_addr = epd->bEndpointAddress; + else if (dir==UE_DIR_IN && !in_addr) + in_addr = epd->bEndpointAddress; + } + } + desc = NEXT_D(desc); + + /* count jacks */ + if (!(desc->bDescriptorType==UDESC_CS_INTERFACE && + desc->bDescriptorSubtype==UMIDI_MS_HEADER)) + return USBD_INVAL; + remain = (size_t)UGETW(TO_CSIFD(desc)->wTotalLength) - + (size_t)desc->bLength; + desc = NEXT_D(desc); + + while (remain>=sizeof(usb_descriptor_t)) { + descsize = desc->bLength; + if (descsize>remain || descsize==0) + break; + if (desc->bDescriptorType==UDESC_CS_INTERFACE && + remain>=UMIDI_JACK_DESCRIPTOR_SIZE) { + if (desc->bDescriptorSubtype==UMIDI_OUT_JACK) + sc->sc_out_num_jacks++; + else if (desc->bDescriptorSubtype==UMIDI_IN_JACK) + sc->sc_in_num_jacks++; + } + desc = NEXT_D(desc); + remain-=descsize; + } + + /* validate some parameters */ + if (sc->sc_out_num_jacks>UMIDI_MAX_EPJACKS) + sc->sc_out_num_jacks = UMIDI_MAX_EPJACKS; + if (sc->sc_in_num_jacks>UMIDI_MAX_EPJACKS) + sc->sc_in_num_jacks = UMIDI_MAX_EPJACKS; + if (sc->sc_out_num_jacks && out_addr) { + sc->sc_out_num_endpoints = 1; + } else { + sc->sc_out_num_endpoints = 0; + sc->sc_out_num_jacks = 0; + } + if (sc->sc_in_num_jacks && in_addr) { + sc->sc_in_num_endpoints = 1; + } else { + sc->sc_in_num_endpoints = 0; + sc->sc_in_num_jacks = 0; + } + sc->sc_endpoints = malloc(sizeof(struct umidi_endpoint)* + (sc->sc_out_num_endpoints+ + sc->sc_in_num_endpoints), + M_USBDEV, M_WAITOK); + if (!sc->sc_endpoints) + return USBD_NOMEM; + if (sc->sc_out_num_endpoints) { + sc->sc_out_ep = sc->sc_endpoints; + sc->sc_out_ep->sc = sc; + sc->sc_out_ep->addr = out_addr; + sc->sc_out_ep->num_jacks = sc->sc_out_num_jacks; + sc->sc_out_ep->num_open = 0; + memset(sc->sc_out_ep->jacks, 0, sizeof(sc->sc_out_ep->jacks)); + } else + sc->sc_out_ep = NULL; + + if (sc->sc_in_num_endpoints) { + sc->sc_in_ep = sc->sc_endpoints+sc->sc_out_num_endpoints; + sc->sc_in_ep->sc = sc; + sc->sc_in_ep->addr = in_addr; + sc->sc_in_ep->num_jacks = sc->sc_in_num_jacks; + sc->sc_in_ep->num_open = 0; + memset(sc->sc_in_ep->jacks, 0, sizeof(sc->sc_in_ep->jacks)); + } else + sc->sc_in_ep = NULL; + + return USBD_NORMAL_COMPLETION; +} + +static usbd_status +alloc_all_endpoints_genuine(struct umidi_softc *sc) +{ + usb_descriptor_t *desc; + int num_ep; + size_t remain, descsize; + struct umidi_endpoint *p, *q, *lowest, *endep, tmpep; + int epaddr; + + desc = TO_D(usbd_get_interface_descriptor(sc->sc_iface)); + num_ep = TO_IFD(desc)->bNumEndpoints; + desc = NEXT_D(desc); /* ifd -> csifd */ + remain = ((size_t)UGETW(TO_CSIFD(desc)->wTotalLength) - + (size_t)desc->bLength); + desc = NEXT_D(desc); + + sc->sc_endpoints = p = malloc(sizeof(struct umidi_endpoint)*num_ep, + M_USBDEV, M_WAITOK); + if (!p) + return USBD_NOMEM; + + sc->sc_out_num_jacks = sc->sc_in_num_jacks = 0; + sc->sc_out_num_endpoints = sc->sc_in_num_endpoints = 0; + epaddr = -1; + + /* get the list of endpoints for midi stream */ + while (remain>=sizeof(usb_descriptor_t)) { + descsize = desc->bLength; + if (descsize>remain || descsize==0) + break; + if (desc->bDescriptorType==UDESC_ENDPOINT && + remain>=USB_ENDPOINT_DESCRIPTOR_SIZE && + UE_GET_XFERTYPE(TO_EPD(desc)->bmAttributes) == UE_BULK) { + epaddr = TO_EPD(desc)->bEndpointAddress; + } else if (desc->bDescriptorType==UDESC_CS_ENDPOINT && + remain>=UMIDI_CS_ENDPOINT_DESCRIPTOR_SIZE && + epaddr!=-1) { + if (num_ep>0) { + num_ep--; + p->sc = sc; + p->addr = epaddr; + p->num_jacks = TO_CSEPD(desc)->bNumEmbMIDIJack; + if (UE_GET_DIR(epaddr)==UE_DIR_OUT) { + sc->sc_out_num_endpoints++; + sc->sc_out_num_jacks += p->num_jacks; + } else { + sc->sc_in_num_endpoints++; + sc->sc_in_num_jacks += p->num_jacks; + } + p++; + } + } else + epaddr = -1; + desc = NEXT_D(desc); + remain-=descsize; + } + + /* sort endpoints */ + num_ep = sc->sc_out_num_endpoints + sc->sc_in_num_endpoints; + p = sc->sc_endpoints; + endep = p + num_ep; + while (p<endep) { + lowest = p; + for (q=p+1; q<endep; q++) { + if ((UE_GET_DIR(lowest->addr)==UE_DIR_IN && + UE_GET_DIR(q->addr)==UE_DIR_OUT) || + ((UE_GET_DIR(lowest->addr)== + UE_GET_DIR(q->addr)) && + (UE_GET_ADDR(lowest->addr)> + UE_GET_ADDR(q->addr)))) + lowest = q; + } + if (lowest != p) { + memcpy((void *)&tmpep, (void *)p, sizeof(tmpep)); + memcpy((void *)p, (void *)lowest, sizeof(tmpep)); + memcpy((void *)lowest, (void *)&tmpep, sizeof(tmpep)); + } + p->num_open = 0; + p++; + } + + sc->sc_out_ep = sc->sc_out_num_endpoints ? sc->sc_endpoints : NULL; + sc->sc_in_ep = + sc->sc_in_num_endpoints ? + sc->sc_endpoints+sc->sc_out_num_endpoints : NULL; + + return USBD_NORMAL_COMPLETION; +} + + +/* + * jack stuffs + */ + +static usbd_status +alloc_all_jacks(struct umidi_softc *sc) +{ + int i, j; + struct umidi_endpoint *ep; + struct umidi_jack *jack, **rjack; + + /* allocate/initialize structures */ + sc->sc_jacks = + malloc(sizeof(*sc->sc_out_jacks)*(sc->sc_in_num_jacks+ + sc->sc_out_num_jacks), + M_USBDEV, M_WAITOK); + if (!sc->sc_jacks) + return USBD_NOMEM; + sc->sc_out_jacks = + sc->sc_out_num_jacks ? sc->sc_jacks : NULL; + sc->sc_in_jacks = + sc->sc_in_num_jacks ? sc->sc_jacks+sc->sc_out_num_jacks : NULL; + + jack = &sc->sc_out_jacks[0]; + for (i=0; i<sc->sc_out_num_jacks; i++) { + jack->opened = 0; + jack->binded = 0; + jack->arg = NULL; + jack->u.out.intr = NULL; + jack->cable_number = i; + jack++; + } + jack = &sc->sc_in_jacks[0]; + for (i=0; i<sc->sc_in_num_jacks; i++) { + jack->opened = 0; + jack->binded = 0; + jack->arg = NULL; + jack->u.in.intr = NULL; + jack->cable_number = i; + jack++; + } + + /* assign each jacks to each endpoints */ + jack = &sc->sc_out_jacks[0]; + ep = &sc->sc_out_ep[0]; + for (i=0; i<sc->sc_out_num_endpoints; i++) { + rjack = &ep->jacks[0]; + for (j=0; j<ep->num_jacks; j++) { + *rjack = jack; + jack->endpoint = ep; + jack++; + rjack++; + } + ep++; + } + jack = &sc->sc_in_jacks[0]; + ep = &sc->sc_in_ep[0]; + for (i=0; i<sc->sc_in_num_endpoints; i++) { + rjack = &ep->jacks[0]; + for (j=0; j<ep->num_jacks; j++) { + *rjack = jack; + jack->endpoint = ep; + jack++; + rjack++; + } + ep++; + } + + return USBD_NORMAL_COMPLETION; +} + +static void +free_all_jacks(struct umidi_softc *sc) +{ + int s; + + s = splaudio(); + if (sc->sc_out_jacks) { + free(sc->sc_jacks, M_USBDEV); + sc->sc_jacks = sc->sc_in_jacks = sc->sc_out_jacks = NULL; + } + splx(s); +} + +static usbd_status +bind_jacks_to_mididev(struct umidi_softc *sc, + struct umidi_jack *out_jack, + struct umidi_jack *in_jack, + struct umidi_mididev *mididev) +{ + if ((out_jack && out_jack->binded) || (in_jack && in_jack->binded)) + return USBD_IN_USE; + if (mididev->out_jack || mididev->in_jack) + return USBD_IN_USE; + + if (out_jack) + out_jack->binded = 1; + if (in_jack) + in_jack->binded = 1; + mididev->in_jack = in_jack; + mididev->out_jack = out_jack; + + return USBD_NORMAL_COMPLETION; +} + +static void +unbind_jacks_from_mididev(struct umidi_mididev *mididev) +{ + if ((mididev->flags&FWRITE) && mididev->out_jack) + close_out_jack(mididev->out_jack); + if ((mididev->flags&FWRITE) && mididev->in_jack) + close_in_jack(mididev->in_jack); + + if (mididev->out_jack) + mididev->out_jack->binded = 0; + if (mididev->in_jack) + mididev->in_jack->binded = 0; + mididev->out_jack = mididev->in_jack = NULL; +} + +static void +unbind_all_jacks(struct umidi_softc *sc) +{ + int i; + + if (sc->sc_mididevs) + for (i=0; i<sc->sc_num_mididevs; i++) { + unbind_jacks_from_mididev(&sc->sc_mididevs[i]); + } +} + +static usbd_status +assign_all_jacks_automatically(struct umidi_softc *sc) +{ + usbd_status err; + int i; + struct umidi_jack *out, *in; + + err = + alloc_all_mididevs(sc, + max(sc->sc_out_num_jacks, sc->sc_in_num_jacks)); + if (err!=USBD_NORMAL_COMPLETION) + return err; + + for (i=0; i<sc->sc_num_mididevs; i++) { + out = (i<sc->sc_out_num_jacks) ? &sc->sc_out_jacks[i]:NULL; + in = (i<sc->sc_in_num_jacks) ? &sc->sc_in_jacks[i]:NULL; + err = bind_jacks_to_mididev(sc, out, in, &sc->sc_mididevs[i]); + if (err!=USBD_NORMAL_COMPLETION) { + free_all_mididevs(sc); + return err; + } + } + + return USBD_NORMAL_COMPLETION; +} + +static usbd_status +open_out_jack(struct umidi_jack *jack, void *arg, void (*intr)(void *)) +{ + struct umidi_endpoint *ep = jack->endpoint; + + if (jack->opened) + return USBD_IN_USE; + + jack->arg = arg; + jack->u.out.intr = intr; + init_packet(&jack->packet); + jack->opened = 1; + ep->num_open++; + + return USBD_NORMAL_COMPLETION; +} + +static usbd_status +open_in_jack(struct umidi_jack *jack, void *arg, void (*intr)(void *, int)) +{ + usbd_status err = USBD_NORMAL_COMPLETION; + struct umidi_endpoint *ep = jack->endpoint; + + if (jack->opened) + return USBD_IN_USE; + + jack->arg = arg; + jack->u.in.intr = intr; + jack->opened = 1; + if (ep->num_open++==0 && UE_GET_DIR(ep->addr)==UE_DIR_IN) { + err = start_input_transfer(ep); + if (err!=USBD_NORMAL_COMPLETION) { + ep->num_open--; + } + } + + return err; +} + +static void +close_out_jack(struct umidi_jack *jack) +{ + struct umidi_jack *tail; + int s; + + if (jack->opened) { + s = splusb(); + LIST_REMOVE(jack, u.out.queue_entry); + if (jack==jack->endpoint->queue_tail) { + /* find tail */ + LIST_FOREACH(tail, + &jack->endpoint->queue_head, + u.out.queue_entry) { + if (!LIST_NEXT(tail, u.out.queue_entry)) { + jack->endpoint->queue_tail = tail; + } + } + } + splx(s); + jack->opened = 0; + jack->endpoint->num_open--; + } +} + +static void +close_in_jack(struct umidi_jack *jack) +{ + if (jack->opened) { + jack->opened = 0; + jack->endpoint->num_open--; + } +} + +static usbd_status +attach_mididev(struct umidi_softc *sc, struct umidi_mididev *mididev) +{ + if (mididev->sc) + return USBD_IN_USE; + + mididev->sc = sc; + + mididev->mdev = midi_attach_mi(&umidi_hw_if, mididev, &sc->sc_dev); + + return USBD_NORMAL_COMPLETION; +} + +static usbd_status +detach_mididev(struct umidi_mididev *mididev, int flags) +{ + if (!mididev->sc) + return USBD_NO_ADDR; + + if (mididev->opened) { + umidi_close(mididev); + } + unbind_jacks_from_mididev(mididev); + + if (mididev->mdev) + config_detach(mididev->mdev, flags); + + mididev->sc = NULL; + + return USBD_NORMAL_COMPLETION; +} + +static usbd_status +deactivate_mididev(struct umidi_mididev *mididev) +{ + if (mididev->out_jack) + mididev->out_jack->binded = 0; + if (mididev->in_jack) + mididev->in_jack->binded = 0; + config_deactivate(mididev->mdev); + + return USBD_NORMAL_COMPLETION; +} + +static usbd_status +alloc_all_mididevs(struct umidi_softc *sc, int nmidi) +{ + sc->sc_num_mididevs = nmidi; + sc->sc_mididevs = malloc(sizeof(*sc->sc_mididevs)*nmidi, + M_USBDEV, M_WAITOK|M_ZERO); + if (!sc->sc_mididevs) + return USBD_NOMEM; + + return USBD_NORMAL_COMPLETION; +} + +static void +free_all_mididevs(struct umidi_softc *sc) +{ + sc->sc_num_mididevs = 0; + if (sc->sc_mididevs) + free(sc->sc_mididevs, M_USBDEV); +} + +static usbd_status +attach_all_mididevs(struct umidi_softc *sc) +{ + usbd_status err; + int i; + + if (sc->sc_mididevs) + for (i=0; i<sc->sc_num_mididevs; i++) { + err = attach_mididev(sc, &sc->sc_mididevs[i]); + if (err!=USBD_NORMAL_COMPLETION) + return err; + } + + return USBD_NORMAL_COMPLETION; +} + +static usbd_status +detach_all_mididevs(struct umidi_softc *sc, int flags) +{ + usbd_status err; + int i; + + if (sc->sc_mididevs) + for (i=0; i<sc->sc_num_mididevs; i++) { + err = detach_mididev(&sc->sc_mididevs[i], flags); + if (err!=USBD_NORMAL_COMPLETION) + return err; + } + + return USBD_NORMAL_COMPLETION; +} + +static usbd_status +deactivate_all_mididevs(struct umidi_softc *sc) +{ + usbd_status err; + int i; + + if (sc->sc_mididevs) + for (i=0; i<sc->sc_num_mididevs; i++) { + err = deactivate_mididev(&sc->sc_mididevs[i]); + if (err!=USBD_NORMAL_COMPLETION) + return err; + } + + return USBD_NORMAL_COMPLETION; +} + +#ifdef UMIDI_DEBUG +static void +dump_sc(struct umidi_softc *sc) +{ + int i; + + DPRINTFN(10, ("%s: dump_sc\n", USBDEVNAME(sc->sc_dev))); + for (i=0; i<sc->sc_out_num_endpoints; i++) { + DPRINTFN(10, ("\tout_ep(%p):\n", &sc->sc_out_ep[i])); + dump_ep(&sc->sc_out_ep[i]); + } + for (i=0; i<sc->sc_in_num_endpoints; i++) { + DPRINTFN(10, ("\tin_ep(%p):\n", &sc->sc_in_ep[i])); + dump_ep(&sc->sc_in_ep[i]); + } +} + +static void +dump_ep(struct umidi_endpoint *ep) +{ + int i; + for (i=0; i<ep->num_jacks; i++) { + DPRINTFN(10, ("\t\tjack(%p):\n", ep->jacks[i])); + dump_jack(ep->jacks[i]); + } +} +static void +dump_jack(struct umidi_jack *jack) +{ + DPRINTFN(10, ("\t\t\tep=%p, mididev=%p\n", + jack->endpoint, jack->mididev)); +} + +#endif /* UMIDI_DEBUG */ + + + +/* + * MUX MIDI PACKET + */ + +static const int packet_length[16] = { + /*0*/ -1, + /*1*/ -1, + /*2*/ 2, + /*3*/ 3, + /*4*/ 3, + /*5*/ 1, + /*6*/ 2, + /*7*/ 3, + /*8*/ 3, + /*9*/ 3, + /*A*/ 3, + /*B*/ 3, + /*C*/ 2, + /*D*/ 2, + /*E*/ 3, + /*F*/ 1, +}; + +static const struct { + int cin; + packet_state_t next; +} packet_0xFX[16] = { + /*F0: SysEx */ { 0x04, PS_EXCL_1 }, + /*F1: MTC */ { 0x02, PS_NORMAL_1OF2 }, + /*F2: S.POS */ { 0x03, PS_NORMAL_1OF3 }, + /*F3: S.SEL */ { 0x02, PS_NORMAL_1OF2 }, + /*F4: UNDEF */ { 0x00, PS_INITIAL }, + /*F5: UNDEF */ { 0x00, PS_INITIAL }, + /*F6: Tune */ { 0x0F, PS_END }, + /*F7: EofEx */ { 0x00, PS_INITIAL }, + /*F8: Timing */ { 0x0F, PS_END }, + /*F9: UNDEF */ { 0x00, PS_INITIAL }, + /*FA: Start */ { 0x0F, PS_END }, + /*FB: Cont */ { 0x0F, PS_END }, + /*FC: Stop */ { 0x0F, PS_END }, + /*FD: UNDEF */ { 0x00, PS_INITIAL }, + /*FE: ActS */ { 0x0F, PS_END }, + /*FF: Reset */ { 0x0F, PS_END }, +}; + +#define GET_CN(p) (((unsigned char)(p)>>4)&0x0F) +#define GET_CIN(p) ((unsigned char)(p)&0x0F) +#define MIX_CN_CIN(cn, cin) \ + ((unsigned char)((((unsigned char)(cn)&0x0F)<<4)| \ + ((unsigned char)(cin)&0x0F))) + +static void +init_packet(struct umidi_packet *packet) +{ + memset(packet->buffer, 0, UMIDI_PACKET_SIZE); + packet->state = PS_INITIAL; +} + +static usbd_status +start_input_transfer(struct umidi_endpoint *ep) +{ + usbd_setup_xfer(ep->xfer, ep->pipe, + (usbd_private_handle)ep, + ep->buffer, UMIDI_PACKET_SIZE, + USBD_NO_COPY, USBD_NO_TIMEOUT, in_intr); + return usbd_transfer(ep->xfer); +} + +static usbd_status +start_output_transfer(struct umidi_endpoint *ep) +{ + usbd_setup_xfer(ep->xfer, ep->pipe, + (usbd_private_handle)ep, + ep->buffer, UMIDI_PACKET_SIZE, + USBD_NO_COPY, USBD_NO_TIMEOUT, out_intr); + return usbd_transfer(ep->xfer); +} + +#ifdef UMIDI_DEBUG +#define DPR_PACKET(dir, sc, p) \ +if ((unsigned char)(p)->buffer[1]!=0xFE) \ + DPRINTFN(500, \ + ("%s: umidi packet(" #dir "): %02X %02X %02X %02X\n", \ + USBDEVNAME(sc->sc_dev), \ + (unsigned char)(p)->buffer[0], \ + (unsigned char)(p)->buffer[1], \ + (unsigned char)(p)->buffer[2], \ + (unsigned char)(p)->buffer[3])); +#else +#define DPR_PACKET(dir, sc, p) +#endif + +static int +out_jack_output(struct umidi_jack *out_jack, int d) +{ + struct umidi_endpoint *ep = out_jack->endpoint; + struct umidi_softc *sc = ep->sc; + int error; + int s; + + if (sc->sc_dying) + return EIO; + + error = 0; + if (out_jack->opened) { + DPRINTFN(1000, ("umidi_output: ep=%p 0x%02x\n", ep, d)); + out_build_packet(out_jack->cable_number, &out_jack->packet, d); + switch (out_jack->packet.state) { + case PS_EXCL_0: + case PS_END: + DPR_PACKET(out, sc, &out_jack->packet); + s = splusb(); + if (LIST_EMPTY(&ep->queue_head)) { + memcpy(ep->buffer, + out_jack->packet.buffer, + UMIDI_PACKET_SIZE); + start_output_transfer(ep); + } + if (LIST_EMPTY(&ep->queue_head)) + LIST_INSERT_HEAD(&ep->queue_head, + out_jack, u.out.queue_entry); + else + LIST_INSERT_AFTER(ep->queue_tail, + out_jack, u.out.queue_entry); + ep->queue_tail = out_jack; + splx(s); + break; + default: + error = EINPROGRESS; + } + } else + error = ENODEV; + + return error; +} + +static void +in_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) +{ + int cn, len, i; + struct umidi_endpoint *ep = (struct umidi_endpoint *)priv; + struct umidi_jack *jack; + + if (ep->sc->sc_dying || !ep->num_open) + return; + + cn = GET_CN(ep->buffer[0]); + len = packet_length[GET_CIN(ep->buffer[0])]; + jack = ep->jacks[cn]; + if (cn>=ep->num_jacks || !jack) { + DPRINTF(("%s: stray umidi packet (in): %02X %02X %02X %02X\n", + USBDEVNAME(ep->sc->sc_dev), + (unsigned)ep->buffer[0], + (unsigned)ep->buffer[1], + (unsigned)ep->buffer[2], + (unsigned)ep->buffer[3])); + return; + } + if (!jack->binded || !jack->opened) + return; + DPR_PACKET(in, ep->sc, &jack->buffer); + if (jack->u.in.intr) { + for (i=0; i<len; i++) { + (*jack->u.in.intr)(jack->arg, ep->buffer[i+1]); + } + } + + (void)start_input_transfer(ep); +} + +static void +out_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) +{ + struct umidi_endpoint *ep = (struct umidi_endpoint *)priv; + struct umidi_softc *sc = ep->sc; + struct umidi_jack *jack; + + if (sc->sc_dying || !ep->num_open) + return; + + jack = LIST_FIRST(&ep->queue_head); + if (jack && jack->opened) { + LIST_REMOVE(jack, u.out.queue_entry); + if (!LIST_EMPTY(&ep->queue_head)) { + memcpy(ep->buffer, + LIST_FIRST(&ep->queue_head)->packet.buffer, + UMIDI_PACKET_SIZE); + (void)start_output_transfer(ep); + } + if (jack->u.out.intr) { + (*jack->u.out.intr)(jack->arg); + } + } +} + +static void +out_build_packet(int cable_number, struct umidi_packet *packet, uByte in) +{ + int cin; + uByte prev; + +retry: + switch (packet->state) { + case PS_END: + case PS_INITIAL: + prev = packet->buffer[1]; + memset(packet->buffer, 0, UMIDI_PACKET_SIZE); + if (in<0x80) { + if (prev>=0x80 && prev<0xf0) { + /* running status */ + out_build_packet(cable_number, packet, prev); + goto retry; + } + /* ??? */ + break; + } + if (in>=0xf0) { + cin=packet_0xFX[in&0x0F].cin; + packet->state=packet_0xFX[in&0x0F].next; + } else { + cin=(unsigned char)in>>4; + switch (packet_length[cin]) { + case 2: + packet->state = PS_NORMAL_1OF2; + break; + case 3: + packet->state = PS_NORMAL_1OF3; + break; + default: + /* ??? */ + packet->state = PS_INITIAL; + } + } + packet->buffer[0] = MIX_CN_CIN(cable_number, cin); + packet->buffer[1] = in; + break; + case PS_NORMAL_1OF3: + if (in>=0x80) { /* ??? */ packet->state = PS_END; break; } + packet->buffer[2] = in; + packet->state = PS_NORMAL_2OF3; + break; + case PS_NORMAL_2OF3: + if (in>=0x80) { /* ??? */ packet->state = PS_END; break; } + packet->buffer[3] = in; + packet->state = PS_END; + break; + case PS_NORMAL_1OF2: + if (in>=0x80) { /* ??? */ packet->state = PS_END; break; } + packet->buffer[2] = in; + packet->state = PS_END; + break; + case PS_EXCL_0: + memset(packet->buffer, 0, UMIDI_PACKET_SIZE); + if (in==0xF7) { + packet->buffer[0] = MIX_CN_CIN(cable_number, 0x05); + packet->buffer[1] = 0xF7; + packet->state = PS_END; + break; + } + if (in>=0x80) { /* ??? */ packet->state = PS_END; break; } + packet->buffer[1] = in; + packet->state = PS_EXCL_1; + break; + case PS_EXCL_1: + if (in==0xF7) { + packet->buffer[0] = MIX_CN_CIN(cable_number, 0x06); + packet->buffer[2] = 0xF7; + packet->state = PS_END; + break; + } + if (in>=0x80) { /* ??? */ packet->state = PS_END; break; } + packet->buffer[2] = in; + packet->state = PS_EXCL_2; + break; + case PS_EXCL_2: + if (in==0xF7) { + packet->buffer[0] = MIX_CN_CIN(cable_number, 0x07); + packet->buffer[3] = 0xF7; + packet->state = PS_END; + break; + } + if (in>=0x80) { /* ??? */ packet->state = PS_END; break; } + packet->buffer[0] = MIX_CN_CIN(cable_number, 0x04); + packet->buffer[3] = in; + packet->state = PS_EXCL_0; + break; + default: + printf("umidi: ambiguous state.\n"); + packet->state = PS_INITIAL; + goto retry; + } +} + diff --git a/sys/dev/usb/umidi_quirks.c b/sys/dev/usb/umidi_quirks.c new file mode 100644 index 00000000000..a7ae1ee27f4 --- /dev/null +++ b/sys/dev/usb/umidi_quirks.c @@ -0,0 +1,215 @@ +/* $OpenBSD: umidi_quirks.c,v 1.1 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: umidi_quirks.c,v 1.3 2001/11/13 06:24:56 lukem Exp $ */ + +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Takuya SHIOZAKI (tshiozak@netbsd.org). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/ioctl.h> +#include <sys/conf.h> +#include <sys/file.h> +#include <sys/select.h> +#include <sys/proc.h> +#include <sys/vnode.h> +#include <sys/poll.h> +#include <sys/lock.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> + +#include <dev/usb/usbdevs.h> +#include <dev/usb/uaudioreg.h> +#include <dev/usb/umidireg.h> +#include <dev/usb/umidivar.h> +#include <dev/usb/umidi_quirks.h> + +/* + * quirk codes for UMIDI + */ + +#ifdef UMIDIQUIRK_DEBUG +#define DPRINTF(x) if (umidiquirkdebug) printf x +#define DPRINTFN(n,x) if (umidiquirkdebug >= (n)) printf x +int umidiquirkdebug = 1; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + + +/* + * YAMAHA UX-256 + * --- this is a typical yamaha device, but has a broken descriptor :-< + */ + +UMQ_FIXED_EP_DEF(YAMAHA, YAMAHA_UX256, ANYIFACE, 1, 1) = { + /* out */ + { 0, 16 }, + /* in */ + { 1, 8 } +}; + +UMQ_DEF(YAMAHA, YAMAHA_UX256, ANYIFACE) = { + UMQ_FIXED_EP_REG(YAMAHA, YAMAHA_UX256, ANYIFACE), +#if 0 + UMQ_YAMAHA_REG(YAMAHA, ANYPRODUCT, ANYIFACE), +#endif + UMQ_TERMINATOR +}; + + +/* + * YAMAHA generic + */ +UMQ_DEF(YAMAHA, ANYPRODUCT, ANYIFACE) = { + UMQ_YAMAHA_REG(YAMAHA, ANYPRODUCT, ANYIFACE), + UMQ_TERMINATOR +}; + + +/* + * ROLAND UM-1 + */ +UMQ_FIXED_EP_DEF(ROLAND, ROLAND_UM1, 2, 1, 1) = { + /* out */ + { 0, 1 }, + /* in */ + { 1, 1 } +}; + +UMQ_DEF(ROLAND, ROLAND_UM1, 2) = { + UMQ_FIXED_EP_REG(ROLAND, ROLAND_UM1, 2), + UMQ_TERMINATOR +}; + + +/* + * ROLAND UM-880 (native mode) + */ +UMQ_FIXED_EP_DEF(ROLAND, ROLAND_UM880N, 0, 1, 1) = { + /* out */ + { 0, 9 }, + /* in */ + { 1, 9 } +}; + +UMQ_DEF(ROLAND, ROLAND_UM880N, 0) = { + UMQ_FIXED_EP_REG(ROLAND, ROLAND_UM880N, 0), + UMQ_TERMINATOR +}; + + + +/* + * quirk list + */ +struct umidi_quirk umidi_quirklist[] = { + UMQ_REG(YAMAHA, YAMAHA_UX256, ANYIFACE), + UMQ_REG(YAMAHA, ANYPRODUCT, ANYIFACE), + UMQ_REG(ROLAND, ROLAND_UM1, 2), + UMQ_REG(ROLAND, ROLAND_UM880N, 0), + UMQ_TERMINATOR +}; + + +/* + * quirk utilities + */ + +struct umidi_quirk * +umidi_search_quirk(int vendor, int product, int ifaceno) +{ + struct umidi_quirk *p; + struct umq_data *q; + + DPRINTF(("umidi_search_quirk: v=%d, p=%d, i=%d\n", + vendor, product, ifaceno)); + + for (p=&umidi_quirklist[0]; p->vendor; p++) { + DPRINTFN(10, ("\tv=%d, p=%d, i=%d", + p->vendor, p->product, p->iface)); + if ((p->vendor==vendor || p->vendor==ANYVENDOR) && + (p->product==product || p->product==ANYPRODUCT) && + (p->iface==ifaceno || p->iface==ANYIFACE)) { + DPRINTFN(10, (" found\n")); + if (!p->type_mask) + /* make quirk mask */ + for (q=p->quirks; q->type; q++) + p->type_mask |= 1<<(q->type-1); + return p; + } + DPRINTFN(10, ("\n")); + } + + return NULL; +} + +static char *quirk_name[] = { + "NULL", + "Fixed Endpoint", + "Yamaha Specific", +}; + +void +umidi_print_quirk(struct umidi_quirk *q) +{ + struct umq_data *qd; + if (q) { + printf("("); + for (qd=q->quirks; qd->type; qd++) + printf("%s%s", quirk_name[qd->type], + (qd+1)->type?", ":")\n"); + } else { + printf("(genuine USB-MIDI)\n"); + } +} + +void * +umidi_get_quirk_data_from_type(struct umidi_quirk *q, u_int32_t type) +{ + struct umq_data *qd; + if (q) { + for (qd=q->quirks; qd->type; qd++) + if (qd->type == type) + return qd->data; + } + return NULL; +} diff --git a/sys/dev/usb/umidi_quirks.h b/sys/dev/usb/umidi_quirks.h new file mode 100644 index 00000000000..ea1f503d756 --- /dev/null +++ b/sys/dev/usb/umidi_quirks.h @@ -0,0 +1,120 @@ +/* $OpenBSD: umidi_quirks.h,v 1.1 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: umidi_quirks.h,v 1.2 2001/09/29 22:00:47 tshiozak Exp $ */ + +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Takuya SHIOZAKI (tshiozak@netbsd.org). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +/* + * quirk code for UMIDI + */ + +#ifndef _DEV_USB_UMIDI_QUIRKS_H_ +#define _DEV_USB_UMIDI_QUIRKS_H_ + +struct umq_data { + int type; +#define UMQ_TYPE_FIXED_EP 1 +#define UMQ_TYPE_YAMAHA 2 + void *data; +}; + +struct umidi_quirk { + int vendor; + int product; + int iface; + struct umq_data *quirks; + u_int32_t type_mask; +}; +#define UMQ_ISTYPE(q, type) \ + ((q)->sc_quirk && ((q)->sc_quirk->type_mask & (1<<((type)-1)))) + +#define UMQ_TERMINATOR { 0, } +#define UMQ_DEF(v, p, i) \ +static struct umq_data umq_##v##_##p##_##i[] +#define UMQ_REG(v, p, i) \ + { USB_VENDOR_##v, USB_PRODUCT_##p, i, \ + umq_##v##_##p##_##i, 0 } +#define ANYIFACE -1 +#define ANYVENDOR -1 +#define USB_VENDOR_ANYVENDOR ANYVENDOR +#define ANYPRODUCT -1 +#define USB_PRODUCT_ANYPRODUCT ANYPRODUCT + +/* + * quirk - fixed port + */ + +struct umq_fixed_ep_endpoint { + int ep; + int num_jacks; +}; +struct umq_fixed_ep_desc { + int num_out_ep; + int num_in_ep; + struct umq_fixed_ep_endpoint *out_ep; + struct umq_fixed_ep_endpoint *in_ep; +}; + +#define UMQ_FIXED_EP_DEF(v, p, i, noep, niep) \ +static struct umq_fixed_ep_endpoint \ +umq_##v##_##p##_##i##_fixed_ep_endpoints[noep+niep]; \ +static struct umq_fixed_ep_desc \ +umq_##v##_##p##_##i##_fixed_ep_desc = { \ + noep, niep, \ + &umq_##v##_##p##_##i##_fixed_ep_endpoints[0], \ + &umq_##v##_##p##_##i##_fixed_ep_endpoints[noep], \ +}; \ +static struct umq_fixed_ep_endpoint \ +umq_##v##_##p##_##i##_fixed_ep_endpoints[noep+niep] + +#define UMQ_FIXED_EP_REG(v, p, i) \ +{ UMQ_TYPE_FIXED_EP, (void *)&umq_##v##_##p##_##i##_fixed_ep_desc } + + +/* + * quirk - yamaha style midi I/F + */ +#define UMQ_YAMAHA_REG(v, p, i) \ +{ UMQ_TYPE_YAMAHA, NULL } + + +/* extern struct umidi_quirk umidi_quirklist[]; */ +struct umidi_quirk *umidi_search_quirk(int, int, int); +void umidi_print_quirk(struct umidi_quirk *); +void *umidi_get_quirk_data_from_type(struct umidi_quirk *, u_int32_t); + +#endif diff --git a/sys/dev/usb/umidireg.h b/sys/dev/usb/umidireg.h new file mode 100644 index 00000000000..f9fd240a1f6 --- /dev/null +++ b/sys/dev/usb/umidireg.h @@ -0,0 +1,80 @@ +/* $OpenBSD: umidireg.h,v 1.1 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: umidireg.h,v 1.2 2001/05/28 20:52:06 tshiozak Exp $ */ +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Takuya SHIOZAKI (tshiozak@netbsd.org). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* Jack Descriptor */ +#define UMIDI_MS_HEADER 0x01 +#define UMIDI_IN_JACK 0x02 +#define UMIDI_OUT_JACK 0x03 + +/* Jack Type */ +#define UMIDI_EMBEDDED 0x01 +#define UMIDI_EXTERNAL 0x02 + +typedef struct { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uWord bcdMSC; + uWord wTotalLength; +} UPACKED umidi_cs_interface_descriptor_t; +#define UMIDI_CS_INTERFACE_DESCRIPTOR_SIZE 7 + +typedef struct { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubType; + uByte bNumEmbMIDIJack; +} UPACKED umidi_cs_endpoint_descriptor_t; +#define UMIDI_CS_ENDPOINT_DESCRIPTOR_SIZE 4 + +typedef struct { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bJackType; + uByte bJackID; +} UPACKED umidi_jack_descriptor_t; +#define UMIDI_JACK_DESCRIPTOR_SIZE 5 + + +#define TO_D(p) ((usb_descriptor_t *)(p)) +#define NEXT_D(desc) TO_D((caddr_t)(desc)+(desc)->bLength) +#define TO_IFD(desc) ((usb_interface_descriptor_t *)(desc)) +#define TO_CSIFD(desc) ((umidi_cs_interface_descriptor_t *)(desc)) +#define TO_EPD(desc) ((usb_endpoint_descriptor_t *)(desc)) +#define TO_CSEPD(desc) ((umidi_cs_endpoint_descriptor_t *)(desc)) diff --git a/sys/dev/usb/umidivar.h b/sys/dev/usb/umidivar.h new file mode 100644 index 00000000000..d11d69b0303 --- /dev/null +++ b/sys/dev/usb/umidivar.h @@ -0,0 +1,138 @@ +/* $OpenBSD: umidivar.h,v 1.1 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: umidivar.h,v 1.3 2001/02/03 18:50:32 tshiozak Exp $ */ +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Takuya SHIOZAKI (tshiozak@netbsd.org). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* pending MUX-MIDI packet */ +typedef enum { + PS_EXCL_0=-2, /* put, and next state is PS_EXCL_0 */ + PS_END=-1, /* put, and next state is PS_INITIAL */ + PS_INITIAL=0, /* 0>= : not put, and state is keeped */ + PS_NORMAL_1OF3=1, + PS_NORMAL_2OF3=2, + PS_NORMAL_1OF2=3, + PS_EXCL_1=4, + PS_EXCL_2=5, +} packet_state_t; + +#define UMIDI_PACKET_SIZE 4 +struct umidi_packet { + char buffer[UMIDI_PACKET_SIZE]; + packet_state_t state; +}; + +/* + * hierarchie + * + * <-- parent child --> + * + * umidi(sc) -> endpoint -> jack <- (dynamically assignable) - mididev + * ^ | ^ | + * +-----+ +-----+ + */ + +/* midi device */ +struct umidi_mididev { + struct umidi_softc *sc; + struct device *mdev; + /* */ + struct umidi_jack *in_jack; + struct umidi_jack *out_jack; + /* */ + int opened; + int flags; +}; + +/* Jack Information */ +struct umidi_jack { + struct umidi_endpoint *endpoint; + /* */ + int cable_number; + struct umidi_packet packet; + void *arg; + int binded; + int opened; + union { + struct { + void (*intr)(void *); + LIST_ENTRY(umidi_jack) queue_entry; + } out; + struct { + void (*intr)(void *, int); + } in; + } u; +}; + +#define UMIDI_MAX_EPJACKS 16 +/* endpoint data */ +struct umidi_endpoint { + struct umidi_softc *sc; + /* */ + int addr; + usbd_pipe_handle pipe; + usbd_xfer_handle xfer; + char *buffer; + int num_open; + int num_jacks; + struct umidi_jack *jacks[UMIDI_MAX_EPJACKS]; + LIST_HEAD(, umidi_jack) queue_head; + struct umidi_jack *queue_tail; +}; + +/* software context */ +struct umidi_softc { + USBBASEDEVICE sc_dev; + usbd_device_handle sc_udev; + usbd_interface_handle sc_iface; + struct umidi_quirk *sc_quirk; + + int sc_dying; + + int sc_out_num_jacks; + struct umidi_jack *sc_out_jacks; + int sc_in_num_jacks; + struct umidi_jack *sc_in_jacks; + struct umidi_jack *sc_jacks; + + int sc_num_mididevs; + struct umidi_mididev *sc_mididevs; + + int sc_out_num_endpoints; + struct umidi_endpoint *sc_out_ep; + int sc_in_num_endpoints; + struct umidi_endpoint *sc_in_ep; + struct umidi_endpoint *sc_endpoints; +}; diff --git a/sys/dev/usb/umodem.c b/sys/dev/usb/umodem.c index df2c0ade0c2..9dbd15e2166 100644 --- a/sys/dev/usb/umodem.c +++ b/sys/dev/usb/umodem.c @@ -1,5 +1,5 @@ -/* $OpenBSD: umodem.c,v 1.7 2001/05/03 02:20:34 aaron Exp $ */ -/* $NetBSD: umodem.c,v 1.40 2001/03/25 23:02:34 augustss Exp $ */ +/* $OpenBSD: umodem.c,v 1.8 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: umodem.c,v 1.41 2001/11/13 06:24:56 lukem Exp $ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -137,7 +137,7 @@ Static void umodem_rts(struct umodem_softc *, int); Static void umodem_break(struct umodem_softc *, int); Static void umodem_set_line_state(struct umodem_softc *); Static int umodem_param(void *, int, struct termios *); -Static int umodem_ioctl(void *, int, u_long, caddr_t, int, struct proc *); +Static int umodem_ioctl(void *, int, u_long, caddr_t, int, usb_proc_ptr); Static int umodem_open(void *, int portno); Static void umodem_close(void *, int portno); Static void umodem_intr(usbd_xfer_handle, usbd_private_handle, usbd_status); @@ -524,7 +524,7 @@ umodem_param(void *addr, int portno, struct termios *t) int umodem_ioctl(void *addr, int portno, u_long cmd, caddr_t data, int flag, - struct proc *p) + usb_proc_ptr p) { struct umodem_softc *sc = addr; int error = 0; diff --git a/sys/dev/usb/ums.c b/sys/dev/usb/ums.c index 8505da52f67..d2b058ca11e 100644 --- a/sys/dev/usb/ums.c +++ b/sys/dev/usb/ums.c @@ -1,5 +1,5 @@ -/* $OpenBSD: ums.c,v 1.6 2001/10/31 04:24:44 nate Exp $ */ -/* $NetBSD: ums.c,v 1.49 2001/10/26 17:58:21 augustss Exp $ */ +/* $OpenBSD: ums.c,v 1.7 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: ums.c,v 1.55 2001/12/31 12:15:22 augustss Exp $ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -42,8 +42,6 @@ * HID spec: http://www.usb.org/developers/data/devclass/hid1_1.pdf */ -/* XXX complete SPUR_UP change */ - #include <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> @@ -64,6 +62,7 @@ #include <dev/usb/usbdi_util.h> #include <dev/usb/usbdevs.h> #include <dev/usb/usb_quirks.h> +#include <dev/usb/uhidev.h> #include <dev/usb/hid.h> #include <dev/wscons/wsconsio.h> @@ -87,18 +86,13 @@ int umsdebug = 0; #define PS2MBUTMASK x04 #define PS2BUTMASK 0x0f +#define MAX_BUTTONS 7 /* must not exceed size of sc_buttons */ + struct ums_softc { - USBBASEDEVICE sc_dev; /* base device */ - usbd_device_handle sc_udev; - usbd_interface_handle sc_iface; /* interface */ - usbd_pipe_handle sc_intrpipe; /* interrupt pipe */ - int sc_ep_addr; - - u_char *sc_ibuf; - u_int8_t sc_iid; - int sc_isize; + struct uhidev sc_hdev; + struct hid_location sc_loc_x, sc_loc_y, sc_loc_z; - struct hid_location *sc_loc_btn; + struct hid_location sc_loc_btn[MAX_BUTTONS]; int sc_enabled; @@ -108,7 +102,6 @@ struct ums_softc { #define UMS_REVZ 0x04 /* Z-axis is reversed */ int nbuttons; -#define MAX_BUTTONS 31 /* chosen because sc_buttons is u_int32_t */ u_int32_t sc_buttons; /* mouse button status */ struct device *sc_wsmousedev; @@ -119,11 +112,11 @@ struct ums_softc { #define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE) #define MOUSE_FLAGS (HIO_RELATIVE) -Static void ums_intr(usbd_xfer_handle, usbd_private_handle, usbd_status); +Static void ums_intr(struct uhidev *addr, void *ibuf, u_int len); Static int ums_enable(void *); Static void ums_disable(void *); -Static int ums_ioctl(void *, u_long, caddr_t, int, struct proc *); +Static int ums_ioctl(void *, u_long, caddr_t, int, usb_proc_ptr); const struct wsmouse_accessops ums_accessops = { ums_enable, @@ -136,115 +129,72 @@ USB_DECLARE_DRIVER(ums); USB_MATCH(ums) { USB_MATCH_START(ums, uaa); - usb_interface_descriptor_t *id; - int size, ret; + struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa; + int size; void *desc; - usbd_status err; - if (uaa->iface == NULL) - return (UMATCH_NONE); - id = usbd_get_interface_descriptor(uaa->iface); - if (id == NULL || id->bInterfaceClass != UICLASS_HID) + uhidev_get_report_desc(uha->parent, &desc, &size); + if (!hid_is_collection(desc, size, uha->reportid, + HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))) return (UMATCH_NONE); - err = usbd_read_report_desc(uaa->iface, &desc, &size, M_TEMP); - if (err) - return (UMATCH_NONE); - - if (hid_is_collection(desc, size, - HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))) - ret = UMATCH_IFACECLASS; - else - ret = UMATCH_NONE; - - free(desc, M_TEMP); - return (ret); + return (UMATCH_IFACECLASS); } USB_ATTACH(ums) { USB_ATTACH_START(ums, sc, uaa); - usbd_interface_handle iface = uaa->iface; - usb_interface_descriptor_t *id; - usb_endpoint_descriptor_t *ed; + struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa; struct wsmousedev_attach_args a; int size; void *desc; - usbd_status err; - char devinfo[1024]; u_int32_t flags, quirks; int i, wheel; struct hid_location loc_btn; - - sc->sc_udev = uaa->device; - sc->sc_iface = iface; - id = usbd_get_interface_descriptor(iface); - usbd_devinfo(uaa->device, 0, devinfo); - USB_ATTACH_SETUP; - printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev), - devinfo, id->bInterfaceClass, id->bInterfaceSubClass); - ed = usbd_interface2endpoint_descriptor(iface, 0); - if (ed == NULL) { - printf("%s: could not read endpoint descriptor\n", - USBDEVNAME(sc->sc_dev)); - USB_ATTACH_ERROR_RETURN; - } - DPRINTFN(10,("ums_attach: bLength=%d bDescriptorType=%d " - "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d" - " bInterval=%d\n", - ed->bLength, ed->bDescriptorType, - ed->bEndpointAddress & UE_ADDR, - UE_GET_DIR(ed->bEndpointAddress)==UE_DIR_IN? "in" : "out", - ed->bmAttributes & UE_XFERTYPE, - UGETW(ed->wMaxPacketSize), ed->bInterval)); - - if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN || - (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) { - printf("%s: unexpected endpoint\n", - USBDEVNAME(sc->sc_dev)); - USB_ATTACH_ERROR_RETURN; - } + sc->sc_hdev.sc_intr = ums_intr; + sc->sc_hdev.sc_parent = uha->parent; + sc->sc_hdev.sc_report_id = uha->reportid; - quirks = usbd_get_quirks(uaa->device)->uq_flags; + quirks = usbd_get_quirks(uha->parent->sc_udev)->uq_flags; if (quirks & UQ_MS_REVZ) sc->flags |= UMS_REVZ; if (quirks & UQ_SPUR_BUT_UP) sc->flags |= UMS_SPUR_BUT_UP; - err = usbd_read_report_desc(uaa->iface, &desc, &size, M_TEMP); - if (err) - USB_ATTACH_ERROR_RETURN; + uhidev_get_report_desc(uha->parent, &desc, &size); if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), - hid_input, &sc->sc_loc_x, &flags)) { - printf("%s: mouse has no X report\n", USBDEVNAME(sc->sc_dev)); + uha->reportid, hid_input, &sc->sc_loc_x, &flags)) { + printf("\n%s: mouse has no X report\n", + USBDEVNAME(sc->sc_hdev.sc_dev)); USB_ATTACH_ERROR_RETURN; } if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { - printf("%s: X report 0x%04x not supported\n", - USBDEVNAME(sc->sc_dev), flags); + printf("\n%s: X report 0x%04x not supported\n", + USBDEVNAME(sc->sc_hdev.sc_dev), flags); USB_ATTACH_ERROR_RETURN; } if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), - hid_input, &sc->sc_loc_y, &flags)) { - printf("%s: mouse has no Y report\n", USBDEVNAME(sc->sc_dev)); + uha->reportid, hid_input, &sc->sc_loc_y, &flags)) { + printf("\n%s: mouse has no Y report\n", + USBDEVNAME(sc->sc_hdev.sc_dev)); USB_ATTACH_ERROR_RETURN; } if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { - printf("%s: Y report 0x%04x not supported\n", - USBDEVNAME(sc->sc_dev), flags); + printf("\n%s: Y report 0x%04x not supported\n", + USBDEVNAME(sc->sc_hdev.sc_dev), flags); USB_ATTACH_ERROR_RETURN; } /* Try to guess the Z activator: first check Z, then WHEEL. */ wheel = 0; if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), - hid_input, &sc->sc_loc_z, &flags) || + uha->reportid, hid_input, &sc->sc_loc_z, &flags) || (wheel = hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL), - hid_input, &sc->sc_loc_z, &flags))) { + uha->reportid, hid_input, &sc->sc_loc_z, &flags))) { if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { sc->sc_loc_z.size = 0; /* Bad Z coord, ignore it */ } else { @@ -258,34 +208,18 @@ USB_ATTACH(ums) /* figure out the number of buttons */ for (i = 1; i <= MAX_BUTTONS; i++) if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i), - hid_input, &loc_btn, 0)) + uha->reportid, hid_input, &loc_btn, 0)) break; sc->nbuttons = i - 1; - sc->sc_loc_btn = malloc(sizeof(struct hid_location) * sc->nbuttons, - M_USBDEV, M_NOWAIT); - if (!sc->sc_loc_btn) { - printf("%s: no memory\n", USBDEVNAME(sc->sc_dev)); - USB_ATTACH_ERROR_RETURN; - } - printf("%s: %d button%s%s\n", USBDEVNAME(sc->sc_dev), + printf(": %d button%s%s\n", sc->nbuttons, sc->nbuttons == 1 ? "" : "s", sc->flags & UMS_Z ? " and Z dir." : ""); for (i = 1; i <= sc->nbuttons; i++) hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i), - hid_input, &sc->sc_loc_btn[i-1], 0); - - sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid); - sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV, M_NOWAIT); - if (sc->sc_ibuf == NULL) { - printf("%s: no memory\n", USBDEVNAME(sc->sc_dev)); - free(sc->sc_loc_btn, M_USBDEV); - USB_ATTACH_ERROR_RETURN; - } - - sc->sc_ep_addr = ed->bEndpointAddress; - free(desc, M_TEMP); + uha->reportid, hid_input, + &sc->sc_loc_btn[i-1], 0); #ifdef USB_DEBUG DPRINTF(("ums_attach: sc=%p\n", sc)); @@ -300,15 +234,11 @@ USB_ATTACH(ums) DPRINTF(("ums_attach: B%d\t%d/%d\n", i, sc->sc_loc_btn[i-1].pos,sc->sc_loc_btn[i-1].size)); } - DPRINTF(("ums_attach: size=%d, id=%d\n", sc->sc_isize, sc->sc_iid)); #endif a.accessops = &ums_accessops; a.accesscookie = sc; - usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, - USBDEV(sc->sc_dev)); - sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint); USB_ATTACH_SUCCESS_RETURN; @@ -344,45 +274,21 @@ USB_DETACH(ums) /* No need to do reference counting of ums, wsmouse has all the goo. */ if (sc->sc_wsmousedev != NULL) rv = config_detach(sc->sc_wsmousedev, flags); - if (rv == 0) { - free(sc->sc_loc_btn, M_USBDEV); - free(sc->sc_ibuf, M_USBDEV); - } - - usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, - USBDEV(sc->sc_dev)); return (rv); } void -ums_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) +ums_intr(struct uhidev *addr, void *ibuf, u_int len) { - struct ums_softc *sc = addr; - u_char *ibuf; + struct ums_softc *sc = (struct ums_softc *)addr; int dx, dy, dz; u_int32_t buttons = 0; int i; int s; - DPRINTFN(5, ("ums_intr: sc=%p status=%d\n", sc, status)); - DPRINTFN(5, ("ums_intr: data = %02x %02x %02x\n", - sc->sc_ibuf[0], sc->sc_ibuf[1], sc->sc_ibuf[2])); - - if (status == USBD_CANCELLED) - return; - - if (status) { - DPRINTF(("ums_intr: status=%d\n", status)); - usbd_clear_endpoint_stall_async(sc->sc_intrpipe); - return; - } + DPRINTFN(5,("ums_intr: len=%d\n", len)); - ibuf = sc->sc_ibuf; - if (sc->sc_iid != 0) { - if (*ibuf++ != sc->sc_iid) - return; - } dx = hid_get_data(ibuf, &sc->sc_loc_x); dy = -hid_get_data(ibuf, &sc->sc_loc_y); dz = hid_get_data(ibuf, &sc->sc_loc_z); @@ -410,8 +316,6 @@ ums_enable(void *v) { struct ums_softc *sc = v; - usbd_status err; - DPRINTFN(1,("ums_enable: sc=%p\n", sc)); if (sc->sc_dying) @@ -423,17 +327,7 @@ ums_enable(void *v) sc->sc_enabled = 1; sc->sc_buttons = 0; - /* Set up interrupt pipe. */ - err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr, - USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc, - sc->sc_ibuf, sc->sc_isize, ums_intr, USBD_DEFAULT_INTERVAL); - if (err) { - DPRINTF(("ums_enable: usbd_open_pipe_intr failed, error=%d\n", - err)); - sc->sc_enabled = 0; - return (EIO); - } - return (0); + return (uhidev_open(&sc->sc_hdev)); } Static void @@ -449,15 +343,12 @@ ums_disable(void *v) } #endif - /* Disable interrupts. */ - usbd_abort_pipe(sc->sc_intrpipe); - usbd_close_pipe(sc->sc_intrpipe); - sc->sc_enabled = 0; + return (uhidev_close(&sc->sc_hdev)); } Static int -ums_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) +ums_ioctl(void *v, u_long cmd, caddr_t data, int flag, usb_proc_ptr p) { switch (cmd) { diff --git a/sys/dev/usb/uplcom.c b/sys/dev/usb/uplcom.c index ffff1e0664c..25d26a8a871 100644 --- a/sys/dev/usb/uplcom.c +++ b/sys/dev/usb/uplcom.c @@ -1,5 +1,5 @@ -/* $OpenBSD: uplcom.c,v 1.2 2001/10/31 04:24:44 nate Exp $ */ -/* $NetBSD: uplcom.c,v 1.20 2001/07/31 12:33:11 ichiro Exp $ */ +/* $OpenBSD: uplcom.c,v 1.3 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: uplcom.c,v 1.27 2002/03/16 16:10:19 ichiro Exp $ */ /* * Copyright (c) 2001 The NetBSD Foundation, Inc. * All rights reserved. @@ -131,7 +131,7 @@ Static void uplcom_break(struct uplcom_softc *, int); Static void uplcom_set_line_state(struct uplcom_softc *); Static void uplcom_get_status(void *, int portno, u_char *lsr, u_char *msr); #if TODO -Static int uplcom_ioctl(void *, int, u_long, caddr_t, int, struct proc *); +Static int uplcom_ioctl(void *, int, u_long, caddr_t, int, usb_proc_ptr); #endif Static int uplcom_param(void *, int, struct termios *); Static int uplcom_open(void *, int); @@ -148,10 +148,7 @@ struct ucom_methods uplcom_methods = { NULL, }; -static const struct uplcom_product { - uint16_t vendor; - uint16_t product; -} uplcom_products [] = { +static const struct usb_devno uplcom_devs[] = { /* I/O DATA USB-RSAQ2 */ { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ2 }, /* I/O DATA USB-RSAQ */ @@ -160,26 +157,28 @@ static const struct uplcom_product { { USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC232A }, /* IOGEAR/ATEN UC-232A */ { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303 }, - { 0, 0 } + /* ELECOM UC-SGT */ + { USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT }, + /* RATOC REX-USB60 */ + { USB_VENDOR_RATOC, USB_PRODUCT_RATOC_REXUSB60 }, + /* TDK USB-PHS Adapter UHA6400 */ + { USB_VENDOR_TDK, USB_PRODUCT_TDK_UHA6400 }, + /* TDK USB-PDC Adapter UPA9664 */ + { USB_VENDOR_TDK, USB_PRODUCT_TDK_UPA9664 }, }; +#define uplcom_lookup(v, p) usb_lookup(uplcom_devs, v, p) USB_DECLARE_DRIVER(uplcom); USB_MATCH(uplcom) { USB_MATCH_START(uplcom, uaa); - int i; if (uaa->iface != NULL) return (UMATCH_NONE); - for (i = 0; uplcom_products[i].vendor != 0; i++) { - if (uplcom_products[i].vendor == uaa->vendor && - uplcom_products[i].product == uaa->product) { - return (UMATCH_VENDOR_PRODUCT); - } - } - return (UMATCH_NONE); + return (uplcom_lookup(uaa->vendor, uaa->product) != NULL ? + UMATCH_VENDOR_PRODUCT : UMATCH_NONE); } USB_ATTACH(uplcom) @@ -715,7 +714,7 @@ uplcom_get_status(void *addr, int portno, u_char *lsr, u_char *msr) #if TODO int uplcom_ioctl(void *addr, int portno, u_long cmd, caddr_t data, int flag, - struct proc *p) + usb_proc_ptr p) { struct uplcom_softc *sc = addr; int error = 0; diff --git a/sys/dev/usb/urio.c b/sys/dev/usb/urio.c index 08903978e71..89492aeb019 100644 --- a/sys/dev/usb/urio.c +++ b/sys/dev/usb/urio.c @@ -1,5 +1,5 @@ -/* $OpenBSD: urio.c,v 1.6 2001/10/31 04:24:44 nate Exp $ */ -/* $NetBSD: urio.c,v 1.6 2001/09/25 21:08:44 jdolecek Exp $ */ +/* $OpenBSD: urio.c,v 1.7 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: urio.c,v 1.11 2002/02/11 15:11:49 augustss Exp $ */ /* * Copyright (c) 2000 The NetBSD Foundation, Inc. @@ -126,6 +126,13 @@ struct urio_softc { #define URIO_RW_TIMEOUT 4000 /* ms */ +static const struct usb_devno urio_devs[] = { + { USB_VENDOR_DIAMOND, USB_PRODUCT_DIAMOND_RIO500USB}, + { USB_VENDOR_DIAMOND2, USB_PRODUCT_DIAMOND2_RIO600USB}, + { USB_VENDOR_DIAMOND2, USB_PRODUCT_DIAMOND2_RIO800USB}, +}; +#define urio_lookup(v, p) usb_lookup(urio_devs, v, p) + USB_DECLARE_DRIVER(urio); USB_MATCH(urio) @@ -137,15 +144,8 @@ USB_MATCH(urio) if (uaa->iface != NULL) return (UMATCH_NONE); - if ( ( uaa->vendor == USB_VENDOR_DIAMOND && - uaa->product == USB_PRODUCT_DIAMOND_RIO500USB ) || - ( uaa->vendor == USB_VENDOR_DIAMOND2 && - ( uaa->product == USB_PRODUCT_DIAMOND2_RIO600USB || - uaa->product == USB_PRODUCT_DIAMOND2_RIO800USB ) ) - ) - return (UMATCH_VENDOR_PRODUCT); - else - return (UMATCH_NONE); + return (urio_lookup(uaa->vendor, uaa->product) != NULL ? + UMATCH_VENDOR_PRODUCT : UMATCH_NONE); } USB_ATTACH(urio) @@ -293,7 +293,7 @@ urio_activate(device_ptr_t self, enum devact act) #endif int -urioopen(dev_t dev, int flag, int mode, struct proc *p) +urioopen(dev_t dev, int flag, int mode, usb_proc_ptr p) { struct urio_softc *sc; usbd_status err; @@ -326,7 +326,7 @@ urioopen(dev_t dev, int flag, int mode, struct proc *p) } int -urioclose(dev_t dev, int flag, int mode, struct proc *p) +urioclose(dev_t dev, int flag, int mode, usb_proc_ptr p) { struct urio_softc *sc; USB_GET_SC(urio, URIOUNIT(dev), sc); @@ -379,7 +379,7 @@ urioread(dev_t dev, struct uio *uio, int flag) while ((n = min(URIO_BSIZE, uio->uio_resid)) != 0) { DPRINTFN(1, ("urioread: start transfer %d bytes\n", n)); tn = n; - err = usbd_bulk_transfer(xfer, sc->sc_in_pipe, 0, + err = usbd_bulk_transfer(xfer, sc->sc_in_pipe, USBD_NO_COPY, URIO_RW_TIMEOUT, bufp, &tn, "uriors"); if (err) { if (err == USBD_INTERRUPTED) @@ -441,7 +441,7 @@ uriowrite(dev_t dev, struct uio *uio, int flag) DPRINTFN(1, ("uriowrite: transfer %d bytes\n", n)); - err = usbd_bulk_transfer(xfer, sc->sc_out_pipe, 0, + err = usbd_bulk_transfer(xfer, sc->sc_out_pipe, USBD_NO_COPY, URIO_RW_TIMEOUT, bufp, &n, "uriowr"); DPRINTFN(2, ("uriowrite: err=%d\n", err)); if (err) { @@ -468,7 +468,7 @@ uriowrite(dev_t dev, struct uio *uio, int flag) int -urioioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) +urioioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p) { struct urio_softc * sc; int unit = URIOUNIT(dev); @@ -544,7 +544,7 @@ urioioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) sc->sc_refcnt++; err = usbd_do_request_flags(sc->sc_udev, &req, ptr, req_flags, - &req_actlen); + &req_actlen, USBD_DEFAULT_TIMEOUT); if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); @@ -563,7 +563,7 @@ ret: } int -uriopoll(dev_t dev, int events, struct proc *p) +uriopoll(dev_t dev, int events, usb_proc_ptr p) { return (0); } diff --git a/sys/dev/usb/usb.c b/sys/dev/usb/usb.c index 03e769920d3..3cc61c6bb74 100644 --- a/sys/dev/usb/usb.c +++ b/sys/dev/usb/usb.c @@ -1,9 +1,8 @@ -/* $OpenBSD: usb.c,v 1.17 2002/05/02 20:08:04 nate Exp $ */ -/* $NetBSD: usb.c,v 1.53 2001/01/23 17:04:30 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/usb.c,v 1.20 1999/11/17 22:33:46 n_hibma Exp $ */ +/* $OpenBSD: usb.c,v 1.18 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: usb.c,v 1.69 2002/04/23 06:34:11 augustss Exp $ */ /* - * Copyright (c) 1998 The NetBSD Foundation, Inc. + * Copyright (c) 1998, 2002 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -53,6 +52,7 @@ #include <sys/kthread.h> #include <sys/proc.h> #include <sys/conf.h> +#include <sys/fcntl.h> #include <sys/poll.h> #include <sys/select.h> #include <sys/vnode.h> @@ -79,7 +79,7 @@ int uhcidebug; #ifdef OHCI_DEBUG int ohcidebug; #endif -/* +/* * 0 - do usual exploration * 1 - do not use timeout exploration * >1 - do no exploration @@ -95,30 +95,31 @@ struct usb_softc { usbd_bus_handle sc_bus; /* USB controller */ struct usbd_port sc_port; /* dummy port for root hub */ - TAILQ_HEAD(, usb_task) sc_tasks; - struct proc *sc_event_thread; - - struct usb_task sc_exp_task; + usb_proc_ptr sc_event_thread; char sc_dying; }; +TAILQ_HEAD(, usb_task) usb_all_tasks; + cdev_decl(usb); Static void usb_discover(void *); Static void usb_create_event_thread(void *); Static void usb_event_thread(void *); +Static void usb_task_thread(void *); +Static usb_proc_ptr usb_task_thread_proc = NULL; #define USB_MAX_EVENTS 100 struct usb_event_q { struct usb_event ue; SIMPLEQ_ENTRY(usb_event_q) next; }; -Static SIMPLEQ_HEAD(, usb_event_q) usb_events = +Static SIMPLEQ_HEAD(, usb_event_q) usb_events = SIMPLEQ_HEAD_INITIALIZER(usb_events); Static int usb_nevents = 0; Static struct selinfo usb_selevent; -Static struct proc *usb_async_proc; /* process that wants USB SIGIO */ +Static usb_proc_ptr usb_async_proc; /* process that wants USB SIGIO */ Static int usb_dev_open = 0; Static void usb_add_event(int, struct usb_event *); @@ -140,22 +141,29 @@ USB_ATTACH(usb) usbd_device_handle dev; usbd_status err; int usbrev; + int speed; struct usb_event ue; - + DPRINTF(("usbd_attach\n")); usbd_init(); sc->sc_bus = aux; sc->sc_bus->usbctl = sc; sc->sc_port.power = USB_MAX_POWER; - TAILQ_INIT(&sc->sc_tasks); - - usb_init_task(&sc->sc_exp_task, usb_discover, sc); usbrev = sc->sc_bus->usbrev; printf(": USB revision %s", usbrev_str[usbrev]); - if (usbrev != USBREV_1_0 && usbrev != USBREV_1_1) { + switch (usbrev) { + case USBREV_1_0: + case USBREV_1_1: + speed = USB_SPEED_FULL; + break; + case USBREV_2_0: + speed = USB_SPEED_HIGH; + break; + default: printf(", not supported\n"); + sc->sc_dying = 1; USB_ATTACH_ERROR_RETURN; } printf("\n"); @@ -167,19 +175,34 @@ USB_ATTACH(usb) ue.u.ue_ctrlr.ue_bus = USBDEVUNIT(sc->sc_dev); usb_add_event(USB_EVENT_CTRLR_ATTACH, &ue); - err = usbd_new_device(USBDEV(sc->sc_dev), sc->sc_bus, 0, 0, 0, +#ifdef USB_USE_SOFTINTR +#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS + /* XXX we should have our own level */ + sc->sc_bus->soft = softintr_establish(IPL_SOFTNET, + sc->sc_bus->methods->soft_intr, sc->sc_bus); + if (sc->sc_bus->soft == NULL) { + printf("%s: can't register softintr\n", USBDEVNAME(sc->sc_dev)); + sc->sc_dying = 1; + USB_ATTACH_ERROR_RETURN; + } +#else + usb_callout_init(&sc->sc_bus->softi); +#endif +#endif + + err = usbd_new_device(USBDEV(sc->sc_dev), sc->sc_bus, 0, speed, 0, &sc->sc_port); if (!err) { dev = sc->sc_port.device; if (dev->hub == NULL) { sc->sc_dying = 1; - printf("%s: root device is not a hub\n", + printf("%s: root device is not a hub\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } sc->sc_bus->root_hub = dev; #if 1 - /* + /* * Turning this code off will delay attachment of USB devices * until the USB event thread is running, which means that * the keyboard will not work until after cold boot. @@ -188,8 +211,8 @@ USB_ATTACH(usb) dev->hub->explore(sc->sc_bus->root_hub); #endif } else { - printf("%s: root hub problem, error=%d\n", - USBDEVNAME(sc->sc_dev), err); + printf("%s: root hub problem, error=%d\n", + USBDEVNAME(sc->sc_dev), err); sc->sc_dying = 1; } if (cold) @@ -206,6 +229,7 @@ void usb_create_event_thread(void *arg) { struct usb_softc *sc = arg; + static int created = 0; if (usb_kthread_create1(usb_event_thread, sc, &sc->sc_event_thread, "%s", sc->sc_dev.dv_xname)) { @@ -213,39 +237,47 @@ usb_create_event_thread(void *arg) sc->sc_dev.dv_xname); panic("usb_create_event_thread"); } + if (!created) { + created = 1; + TAILQ_INIT(&usb_all_tasks); + if (usb_kthread_create1(usb_task_thread, NULL, + &usb_task_thread_proc, "usbtask")) { + printf("unable to create task thread\n"); + panic("usb_create_event_thread task"); + } + } } /* - * Add a task to be performed by the event thread. This function can be + * Add a task to be performed by the task thread. This function can be * called from any context and the task will be executed in a process * context ASAP. */ void usb_add_task(usbd_device_handle dev, struct usb_task *task) { - struct usb_softc *sc = dev->bus->usbctl; int s; s = splusb(); if (!task->onqueue) { - DPRINTFN(2,("usb_add_task: sc=%p task=%p\n", sc, task)); - TAILQ_INSERT_TAIL(&sc->sc_tasks, task, next); + DPRINTFN(2,("usb_add_task: task=%p\n", task)); + TAILQ_INSERT_TAIL(&usb_all_tasks, task, next); task->onqueue = 1; - } else - DPRINTFN(3,("usb_add_task: sc=%p task=%p on q\n", sc, task)); - wakeup(&sc->sc_tasks); + } else { + DPRINTFN(3,("usb_add_task: task=%p on q\n", task)); + } + wakeup(&usb_all_tasks); splx(s); } void usb_rem_task(usbd_device_handle dev, struct usb_task *task) { - struct usb_softc *sc = dev->bus->usbctl; int s; s = splusb(); if (task->onqueue) { - TAILQ_REMOVE(&sc->sc_tasks, task, next); + TAILQ_REMOVE(&usb_all_tasks, task, next); task->onqueue = 0; } splx(s); @@ -255,31 +287,37 @@ void usb_event_thread(void *arg) { struct usb_softc *sc = arg; - struct usb_task *task; - int s; DPRINTF(("usb_event_thread: start\n")); + /* + * In case this controller is a companion controller to an + * EHCI controller we need to wait until the EHCI controller + * has grabbed the port. + * XXX It would be nicer to do this with a tsleep(), but I don't + * know how to synchronize the creation of the threads so it + * will work. + */ + usb_delay_ms(sc->sc_bus, 500); + /* Make sure first discover does something. */ sc->sc_bus->needs_explore = 1; usb_discover(sc); config_pending_decr(); while (!sc->sc_dying) { - s = splusb(); - task = TAILQ_FIRST(&sc->sc_tasks); - if (task == NULL) { - tsleep(&sc->sc_tasks, PWAIT, "usbevt", 0); - task = TAILQ_FIRST(&sc->sc_tasks); - } - DPRINTFN(2,("usb_event_thread: woke up task=%p\n", task)); - if (task != NULL && !sc->sc_dying) { - TAILQ_REMOVE(&sc->sc_tasks, task, next); - task->onqueue = 0; - splx(s); - task->fun(task->arg); - } else - splx(s); +#ifdef USB_DEBUG + if (usb_noexplore < 2) +#endif + usb_discover(sc); +#ifdef USB_DEBUG + (void)tsleep(&sc->sc_bus->needs_explore, PWAIT, "usbevt", + usb_noexplore ? 0 : hz * 60); +#else + (void)tsleep(&sc->sc_bus->needs_explore, PWAIT, "usbevt", + hz * 60); +#endif + DPRINTFN(2,("usb_event_thread: woke up\n")); } sc->sc_event_thread = NULL; @@ -290,6 +328,32 @@ usb_event_thread(void *arg) kthread_exit(0); } +void +usb_task_thread(void *arg) +{ + struct usb_task *task; + int s; + + DPRINTF(("usb_task_thread: start\n")); + + s = splusb(); + for (;;) { + task = TAILQ_FIRST(&usb_all_tasks); + if (task == NULL) { + tsleep(&usb_all_tasks, PWAIT, "usbtsk", 0); + task = TAILQ_FIRST(&usb_all_tasks); + } + DPRINTFN(2,("usb_task_thread: woke up task=%p\n", task)); + if (task != NULL) { + TAILQ_REMOVE(&usb_all_tasks, task, next); + task->onqueue = 0; + splx(s); + task->fun(task->arg); + s = splusb(); + } + } +} + int usbctlprint(void *aux, const char *pnp) { @@ -302,7 +366,7 @@ usbctlprint(void *aux, const char *pnp) #endif /* defined(__NetBSD__) || defined(__OpenBSD__) */ int -usbopen(dev_t dev, int flag, int mode, struct proc *p) +usbopen(dev_t dev, int flag, int mode, usb_proc_ptr p) { int unit = minor(dev); struct usb_softc *sc; @@ -357,7 +421,7 @@ usbread(dev_t dev, struct uio *uio, int flag) } int -usbclose(dev_t dev, int flag, int mode, struct proc *p) +usbclose(dev_t dev, int flag, int mode, usb_proc_ptr p) { int unit = minor(dev); @@ -370,7 +434,7 @@ usbclose(dev_t dev, int flag, int mode, struct proc *p) } int -usbioctl(dev_t devt, u_long cmd, caddr_t data, int flag, struct proc *p) +usbioctl(dev_t devt, u_long cmd, caddr_t data, int flag, usb_proc_ptr p) { struct usb_softc *sc; int unit = minor(devt); @@ -380,7 +444,7 @@ usbioctl(dev_t devt, u_long cmd, caddr_t data, int flag, struct proc *p) case FIONBIO: /* All handled in the upper FS layer. */ return (0); - + case FIOASYNC: if (*(int *)data) usb_async_proc = p; @@ -401,6 +465,8 @@ usbioctl(dev_t devt, u_long cmd, caddr_t data, int flag, struct proc *p) switch (cmd) { #ifdef USB_DEBUG case USB_SETDEBUG: + if (!(flag & FWRITE)) + return (EBADF); usbdebug = ((*(int *)data) & 0x000000ff); #ifdef UHCI_DEBUG uhcidebug = ((*(int *)data) & 0x0000ff00) >> 8; @@ -409,7 +475,7 @@ usbioctl(dev_t devt, u_long cmd, caddr_t data, int flag, struct proc *p) ohcidebug = ((*(int *)data) & 0x00ff0000) >> 16; #endif break; -#endif +#endif /* USB_DEBUG */ case USB_REQUEST: { struct usb_ctl_request *ur = (void *)data; @@ -421,10 +487,13 @@ usbioctl(dev_t devt, u_long cmd, caddr_t data, int flag, struct proc *p) usbd_status err; int error = 0; + if (!(flag & FWRITE)) + return (EBADF); + DPRINTF(("usbioctl: USB_REQUEST addr=%d len=%d\n", addr, len)); if (len < 0 || len > 32768) return (EINVAL); - if (addr < 0 || addr >= USB_MAX_DEVICES || + if (addr < 0 || addr >= USB_MAX_DEVICES || sc->sc_bus->devices[addr] == 0) return (EINVAL); if (len != 0) { @@ -436,7 +505,7 @@ usbioctl(dev_t devt, u_long cmd, caddr_t data, int flag, struct proc *p) uio.uio_offset = 0; uio.uio_segflg = UIO_USERSPACE; uio.uio_rw = - ur->ucr_request.bmRequestType & UT_READ ? + ur->ucr_request.bmRequestType & UT_READ ? UIO_READ : UIO_WRITE; uio.uio_procp = p; ptr = malloc(len, M_TEMP, M_WAITOK); @@ -447,8 +516,8 @@ usbioctl(dev_t devt, u_long cmd, caddr_t data, int flag, struct proc *p) } } err = usbd_do_request_flags(sc->sc_bus->devices[addr], - &ur->ucr_request, ptr, ur->ucr_flags, - &ur->ucr_actlen); + &ur->ucr_request, ptr, ur->ucr_flags, &ur->ucr_actlen, + USBD_DEFAULT_TIMEOUT); if (err) { error = EIO; goto ret; @@ -492,21 +561,21 @@ usbioctl(dev_t devt, u_long cmd, caddr_t data, int flag, struct proc *p) } int -usbpoll(dev_t dev, int events, struct proc *p) +usbpoll(dev_t dev, int events, usb_proc_ptr p) { int revents, mask, s; if (minor(dev) == USB_DEV_MINOR) { revents = 0; mask = POLLIN | POLLRDNORM; - + s = splusb(); if (events & mask && usb_nevents > 0) revents |= events & mask; if (revents == 0 && events & mask) selrecord(p, &usb_selevent); splx(s); - + return (revents); } else { return (ENXIO); @@ -524,7 +593,7 @@ usb_discover(void *v) if (usb_noexplore > 1) return; #endif - /* + /* * We need mutual exclusion while traversing the device tree, * but this is guaranteed since this function is only called * from the event thread for the controller. @@ -540,7 +609,7 @@ usb_needs_explore(usbd_device_handle dev) { DPRINTFN(2,("usb_needs_explore\n")); dev->bus->needs_explore = 1; - usb_add_task(dev, &dev->bus->usbctl->sc_exp_task); + wakeup(&dev->bus->needs_explore); } /* Called at splusb() */ @@ -552,6 +621,13 @@ usb_get_next_event(struct usb_event *ue) if (usb_nevents <= 0) return (0); ueq = SIMPLEQ_FIRST(&usb_events); +#ifdef DIAGNOSTIC + if (ueq == NULL) { + printf("usb: usb_nevents got out of sync! %d\n", usb_nevents); + usb_nevents = 0; + return (0); + } +#endif *ue = ueq->ue; SIMPLEQ_REMOVE_HEAD(&usb_events, ueq, next); free(ueq, M_USBDEV); @@ -574,7 +650,7 @@ usbd_add_drv_event(int type, usbd_device_handle udev, device_ptr_t dev) struct usb_event ue; ue.u.ue_driver.ue_cookie = udev->cookie; - strncpy(ue.u.ue_driver.ue_devname, USBDEVPTRNAME(dev), + strncpy(ue.u.ue_driver.ue_devname, USBDEVPTRNAME(dev), sizeof ue.u.ue_driver.ue_devname); usb_add_event(type, &ue); } @@ -607,10 +683,26 @@ usb_add_event(int type, struct usb_event *uep) psignal(usb_async_proc, SIGIO); splx(s); } + void -usb_schedsoftintr(struct usbd_bus *bus) +usb_schedsoftintr(usbd_bus_handle bus) { + DPRINTFN(10,("usb_schedsoftintr: polling=%d\n", bus->use_polling)); +#ifdef USB_USE_SOFTINTR + if (bus->use_polling) { + bus->methods->soft_intr(bus); + } else { +#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS + softintr_schedule(bus->soft); +#else + if (!usb_callout_pending(bus->softi)) + usb_callout(bus->softi, 0, bus->methods->soft_intr, + bus); +#endif /* __HAVE_GENERIC_SOFT_INTERRUPTS */ + } +#else bus->methods->soft_intr(bus); +#endif /* USB_USE_SOFTINTR */ } int @@ -627,7 +719,8 @@ usb_activate(device_ptr_t self, enum devact act) case DVACT_DEACTIVATE: sc->sc_dying = 1; - if (dev && dev->cdesc && dev->subdevs) { + if (dev != NULL && dev->cdesc != NULL && + dev->subdevs != NULL) { for (i = 0; dev->subdevs[i]; i++) rv |= config_deactivate(dev->subdevs[i]); } @@ -647,12 +740,12 @@ usb_detach(device_ptr_t self, int flags) sc->sc_dying = 1; /* Make all devices disconnect. */ - if (sc->sc_port.device) + if (sc->sc_port.device != NULL) usb_disconnect_port(&sc->sc_port, self); /* Kill off event thread. */ - if (sc->sc_event_thread) { - wakeup(&sc->sc_tasks); + if (sc->sc_event_thread != NULL) { + wakeup(&sc->sc_bus->needs_explore); if (tsleep(sc, PWAIT, "usbdet", hz * 60)) printf("%s: event thread didn't die\n", USBDEVNAME(sc->sc_dev)); @@ -661,6 +754,17 @@ usb_detach(device_ptr_t self, int flags) usbd_finish(); +#ifdef USB_USE_SOFTINTR +#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS + if (sc->sc_bus->soft != NULL) { + softintr_disestablish(sc->sc_bus->soft); + sc->sc_bus->soft = NULL; + } +#else + usb_uncallout(sc->sc_bus->softi, bus->methods->soft_intr, bus); +#endif +#endif + ue.u.ue_ctrlr.ue_bus = USBDEVUNIT(sc->sc_dev); usb_add_event(USB_EVENT_CTRLR_DETACH, &ue); diff --git a/sys/dev/usb/usb.h b/sys/dev/usb/usb.h index 0a3e39cfae3..c5dc2e8a72a 100644 --- a/sys/dev/usb/usb.h +++ b/sys/dev/usb/usb.h @@ -1,5 +1,5 @@ -/* $OpenBSD: usb.h,v 1.15 2002/05/02 20:08:04 nate Exp $ */ -/* $NetBSD: usb.h,v 1.52 2001/07/23 15:17:50 nathanw Exp $ */ +/* $OpenBSD: usb.h,v 1.16 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: usb.h,v 1.65 2002/02/26 10:27:49 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/usb.h,v 1.14 1999/11/17 22:33:46 n_hibma Exp $ */ /* @@ -48,23 +48,18 @@ #if defined(__NetBSD__) || defined(__OpenBSD__) #include <sys/ioctl.h> +#endif +#if defined(__FreeBSD__) +/* These two defines are used by usbd to autoload the usb kld */ +#define USB_KLD "usb" /* name of usb module */ +#define USB_UHUB "usb/uhub" /* root hub */ +#endif #if defined(_KERNEL) #include <dev/usb/usb_port.h> #endif /* _KERNEL */ -#elif defined(__FreeBSD__) -#if defined(KERNEL) -#include <sys/malloc.h> - -MALLOC_DECLARE(M_USB); -MALLOC_DECLARE(M_USBDEV); -MALLOC_DECLARE(M_USBHC); - -#include <dev/usb/usb_port.h> -#endif /* KERNEL */ -#endif /* __FreeBSD__ */ - +#define USB_STACK_VERSION 2 #define USB_MAX_DEVICES 128 #define USB_START_ADDR 0 @@ -159,6 +154,10 @@ typedef struct { #define UDESC_STRING 0x03 #define UDESC_INTERFACE 0x04 #define UDESC_ENDPOINT 0x05 +#define UDESC_DEVICE_QUALIFIER 0x06 +#define UDESC_OTHER_SPEED_CONFIGURATION 0x07 +#define UDESC_INTERFACE_POWER 0x08 +#define UDESC_OTG 0x09 #define UDESC_CS_DEVICE 0x21 /* class specific */ #define UDESC_CS_CONFIG 0x22 #define UDESC_CS_STRING 0x23 @@ -175,9 +174,13 @@ typedef struct { /* Feature numbers */ #define UF_ENDPOINT_HALT 0 #define UF_DEVICE_REMOTE_WAKEUP 1 +#define UF_TEST_MODE 2 #define USB_MAX_IPACKET 8 /* maximum size of the initial packet */ +#define USB_2_MAX_CTRL_PACKET 64 +#define USB_2_MAX_BULK_PACKET 512 + typedef struct { uByte bLength; uByte bDescriptorType; @@ -188,6 +191,8 @@ typedef struct { uByte bLength; uByte bDescriptorType; uWord bcdUSB; +#define UD_USB_2_0 0x0200 +#define UD_IS_USB2(d) (UGETW((d)->bcdUSB) >= UD_USB_2_0) uByte bDeviceClass; uByte bDeviceSubClass; uByte bDeviceProtocol; @@ -269,6 +274,10 @@ typedef struct { /* Hub specific request */ #define UR_GET_BUS_STATE 0x02 +#define UR_CLEAR_TT_BUFFER 0x08 +#define UR_RESET_TT 0x09 +#define UR_GET_TT_STATE 0x0a +#define UR_STOP_TT 0x0b /* Hub features */ #define UHF_C_HUB_LOCAL_POWER 0 @@ -285,21 +294,29 @@ typedef struct { #define UHF_C_PORT_SUSPEND 18 #define UHF_C_PORT_OVER_CURRENT 19 #define UHF_C_PORT_RESET 20 +#define UHF_PORT_TEST 21 +#define UHF_PORT_INDICATOR 22 typedef struct { uByte bDescLength; uByte bDescriptorType; uByte bNbrPorts; uWord wHubCharacteristics; -#define UHD_PWR 0x03 -#define UHD_PWR_GANGED 0x00 -#define UHD_PWR_INDIVIDUAL 0x01 -#define UHD_PWR_NO_SWITCH 0x02 -#define UHD_COMPOUND 0x04 -#define UHD_OC 0x18 -#define UHD_OC_GLOBAL 0x00 -#define UHD_OC_INDIVIDUAL 0x08 -#define UHD_OC_NONE 0x10 +#define UHD_PWR 0x0003 +#define UHD_PWR_GANGED 0x0000 +#define UHD_PWR_INDIVIDUAL 0x0001 +#define UHD_PWR_NO_SWITCH 0x0002 +#define UHD_COMPOUND 0x0004 +#define UHD_OC 0x0018 +#define UHD_OC_GLOBAL 0x0000 +#define UHD_OC_INDIVIDUAL 0x0008 +#define UHD_OC_NONE 0x0010 +#define UHD_TT_THINK 0x0060 +#define UHD_TT_THINK_8 0x0000 +#define UHD_TT_THINK_16 0x0020 +#define UHD_TT_THINK_24 0x0040 +#define UHD_TT_THINK_32 0x0060 +#define UHD_PORT_IND 0x0080 uByte bPwrOn2PwrGood; /* delay in 2 ms units */ #define UHD_PWRON_FACTOR 2 uByte bHubContrCurrent; @@ -311,6 +328,32 @@ typedef struct { #define USB_HUB_DESCRIPTOR_SIZE 9 /* includes deprecated PortPowerCtrlMask */ typedef struct { + uByte bLength; + uByte bDescriptorType; + uWord bcdUSB; + uByte bDeviceClass; + uByte bDeviceSubClass; + uByte bDeviceProtocol; + uByte bMaxPacketSize0; + uByte bNumConfigurations; + uByte bReserved; +} UPACKED usb_device_qualifier_t; +#define USB_DEVICE_QUALIFIER_SIZE 10 + +typedef struct { + uByte bLength; + uByte bDescriptorType; + uByte bmAttributes; +#define UOTG_SRP 0x01 +#define UOTG_HNP 0x02 +} UPACKED usb_otg_descriptor_t; + +/* OTG feature selectors */ +#define UOTG_B_HNP_ENABLE 3 +#define UOTG_A_HNP_SUPPORT 4 +#define UOTG_A_ALT_HNP_SUPPORT 5 + +typedef struct { uWord wStatus; /* Device status flags */ #define UDS_SELF_POWERED 0x0001 @@ -335,6 +378,9 @@ typedef struct { #define UPS_RESET 0x0010 #define UPS_PORT_POWER 0x0100 #define UPS_LOW_SPEED 0x0200 +#define UPS_HIGH_SPEED 0x0400 +#define UPS_PORT_TEST 0x0800 +#define UPS_PORT_INDICATOR 0x1000 uWord wPortChange; #define UPS_C_CONNECT_STATUS 0x0001 #define UPS_C_PORT_ENABLED 0x0002 @@ -349,6 +395,9 @@ typedef struct { #define UDCLASS_HID 0x00 #define UDCLASS_HUB 0x09 #define UDSUBCLASS_HUB 0 +#define UDPROTO_FSHUB 0 +#define UDPROTO_HSHUBSTT 1 +#define UDPROTO_HSHUBMTT 2 #define UDCLASS_MASS 0x00 /* Interface class codes */ @@ -390,11 +439,14 @@ typedef struct { #define UISUBCLASS_SCSI 6 #define UIPROTO_MASS_CBI_I 0 #define UIPROTO_MASS_CBI 1 -#define UIPROTO_MASS_BBB 2 -#define UIPROTO_MASS_BBB_P 80 /* 'P' for the Iomega Zip drive */ +#define UIPROTO_MASS_BBB_OLD 2 /* Not in the spec anymore */ +#define UIPROTO_MASS_BBB 80 /* 'P' for the Iomega Zip drive */ #define UICLASS_HUB 0x09 #define UISUBCLASS_HUB 0 +#define UIPROTO_FSHUB 0 +#define UIPROTO_HSHUBSTT 0 /* Yes, same as previous */ +#define UIPROTO_HSHUBMTT 1 #define UICLASS_CDC_DATA 0x0a #define UISUBCLASS_DATA 0 @@ -415,6 +467,10 @@ typedef struct { #define UICLASS_FIRM_UPD 0x0c #define UICLASS_APPL_SPEC 0xfe +#define UISUBCLASS_FIRMWARE_DOWNLOAD 1 +#define UISUBCLASS_IRDA 2 +#define UIPROTO_IRDA 0 + #define UICLASS_VENDOR 0xff @@ -430,6 +486,7 @@ typedef struct { #if 0 /* These are the values from the spec. */ #define USB_PORT_RESET_DELAY 10 /* ms */ +#define USB_PORT_ROOT_RESET_DELAY 50 /* ms */ #define USB_PORT_RESET_SETTLE 10 /* ms */ #define USB_PORT_POWERUP_DELAY 100 /* ms */ #define USB_SET_ADDRESS_SETTLE 2 /* ms */ @@ -440,8 +497,9 @@ typedef struct { #else /* Allow for marginal (i.e. non-conforming) devices. */ #define USB_PORT_RESET_DELAY 50 /* ms */ +#define USB_PORT_ROOT_RESET_DELAY 250 /* ms */ #define USB_PORT_RESET_RECOVERY 50 /* ms */ -#define USB_PORT_POWERUP_DELAY 200 /* ms */ +#define USB_PORT_POWERUP_DELAY 300 /* ms */ #define USB_SET_ADDRESS_SETTLE 10 /* ms */ #define USB_RESUME_DELAY (50*5) /* ms */ #define USB_RESUME_WAIT 50 /* ms */ @@ -533,7 +591,10 @@ struct usb_device_info { u_int8_t udi_subclass; u_int8_t udi_protocol; u_int8_t udi_config; - u_int8_t udi_lowspeed; + u_int8_t udi_speed; +#define USB_SPEED_LOW 1 +#define USB_SPEED_FULL 2 +#define USB_SPEED_HIGH 3 int udi_power; /* power consumption in mA, 0 if selfpowered */ int udi_nports; char udi_devnames[USB_MAX_DEVNAMES][USB_MAX_DEVNAMELEN]; @@ -563,6 +624,7 @@ struct usb_event { #define USB_EVENT_DRIVER_ATTACH 5 #define USB_EVENT_DRIVER_DETACH 6 #define USB_EVENT_IS_ATTACH(n) ((n) == USB_EVENT_CTRLR_ATTACH || (n) == USB_EVENT_DEVICE_ATTACH || (n) == USB_EVENT_DRIVER_ATTACH) +#define USB_EVENT_IS_DETACH(n) ((n) == USB_EVENT_CTRLR_DETACH || (n) == USB_EVENT_DEVICE_DETACH || (n) == USB_EVENT_DRIVER_DETACH) struct timespec ue_time; union { struct { @@ -588,6 +650,7 @@ struct usb_event { #define USB_SET_IMMED _IOW ('U', 22, int) #define USB_GET_REPORT _IOWR('U', 23, struct usb_ctl_report) #define USB_SET_REPORT _IOW ('U', 24, struct usb_ctl_report) +#define USB_GET_REPORT_ID _IOR ('U', 25, int) /* Generic USB device */ #define USB_GET_CONFIG _IOR ('U', 100, int) diff --git a/sys/dev/usb/usb_port.h b/sys/dev/usb/usb_port.h index 9f84d83ac07..66ceaddaf3f 100644 --- a/sys/dev/usb/usb_port.h +++ b/sys/dev/usb/usb_port.h @@ -1,5 +1,5 @@ -/* $OpenBSD: usb_port.h,v 1.35 2002/05/06 23:07:26 nate Exp $ */ -/* $NetBSD: usb_port.h,v 1.44 2001/05/14 20:35:29 bouyer Exp $ */ +/* $OpenBSD: usb_port.h,v 1.36 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: usb_port.h,v 1.54 2002/03/28 21:49:19 ichiro Exp $ */ /* $FreeBSD: src/sys/dev/usb/usb_port.h,v 1.21 1999/11/17 22:33:47 n_hibma Exp $ */ /* @@ -57,6 +57,7 @@ #ifdef USB_DEBUG #define UKBD_DEBUG 1 +#define UHIDEV_DEBUG 1 #define UHID_DEBUG 1 #define OHCI_DEBUG 1 #define UGEN_DEBUG 1 @@ -65,26 +66,36 @@ #define ULPT_DEBUG 1 #define UCOM_DEBUG 1 #define UPLCOM_DEBUG 1 +#define UMCT_DEBUG 1 #define UMODEM_DEBUG 1 #define UAUDIO_DEBUG 1 #define AUE_DEBUG 1 #define CUE_DEBUG 1 #define KUE_DEBUG 1 +#define URL_DEBUG 1 #define UMASS_DEBUG 1 #define UVISOR_DEBUG 1 #define UPL_DEBUG 1 #define UZCOM_DEBUG 1 #define URIO_DEBUG 1 #define UFTDI_DEBUG 1 -#define UMCT_DEBUG 1 #define USCANNER_DEBUG 1 #define USSCANNER_DEBUG 1 +#define EHCI_DEBUG 1 +#define UIRDA_DEBUG 1 +#define USTIR_DEBUG 1 +#define UISDATA_DEBUG 1 +#define UDSBR_DEBUG 1 #define Static #else #define Static static #endif -#define SCSI_MODE_SENSE MODE_SENSE +#define SCSI_MODE_SENSE MODE_SENSE + +#define UMASS_ATAPISTR "atapibus" + +typedef struct proc *usb_proc_ptr; typedef struct device *device_ptr_t; #define USBBASEDEVICE struct device @@ -104,8 +115,11 @@ typedef struct device *device_ptr_t; typedef struct callout usb_callout_t; #define usb_callout_init(h) callout_init(&(h)) #define usb_callout(h, t, f, d) callout_reset(&(h), (t), (f), (d)) +#define usb_callout_pending(h) callout_pending(&(h)) #define usb_uncallout(h, f, d) callout_stop(&(h)) +#define usb_lockmgr(l, f, sl, p) lockmgr((l), (f), (sl)) + #define usb_kthread_create1 kthread_create1 #define usb_kthread_create kthread_create @@ -133,21 +147,13 @@ struct cfattach __CONCAT(dname,_ca) = { \ } #define USB_MATCH(dname) \ -int \ -__CONCAT(dname,_match)(parent, match, aux) \ - struct device *parent; \ - struct cfdata *match; \ - void *aux; +int __CONCAT(dname,_match)(struct device *parent, struct cfdata *match, void *aux) #define USB_MATCH_START(dname, uaa) \ struct usb_attach_arg *uaa = aux #define USB_ATTACH(dname) \ -void \ -__CONCAT(dname,_attach)(parent, self, aux) \ - struct device *parent; \ - struct device *self; \ - void *aux; +void __CONCAT(dname,_attach)(struct device *parent, struct device *self, void *aux) #define USB_ATTACH_START(dname, sc, uaa) \ struct __CONCAT(dname,_softc) *sc = \ @@ -161,10 +167,7 @@ __CONCAT(dname,_attach)(parent, self, aux) \ #define USB_ATTACH_SETUP printf("\n") #define USB_DETACH(dname) \ -int \ -__CONCAT(dname,_detach)(self, flags) \ - struct device *self; \ - int flags; +int __CONCAT(dname,_detach)(struct device *self, int flags) #define USB_DETACH_START(dname, sc) \ struct __CONCAT(dname,_softc) *sc = \ @@ -184,12 +187,17 @@ __CONCAT(dname,_detach)(self, flags) \ (config_found_sm(parent, args, print, sub)) #elif defined(__OpenBSD__) -#include <sys/timeout.h> /* * OpenBSD */ +#include <sys/timeout.h> + +#define USB_USE_SOFTINTR + +#define USB_DEBUG #ifdef USB_DEBUG #define UKBD_DEBUG 1 +#define UHIDEV_DEBUG 1 #define UHID_DEBUG 1 #define OHCI_DEBUG 1 #define UGEN_DEBUG 1 @@ -197,6 +205,8 @@ __CONCAT(dname,_detach)(self, flags) \ #define UHUB_DEBUG 1 #define ULPT_DEBUG 1 #define UCOM_DEBUG 1 +#define UPLCOM_DEBUG 1 +#define UMCT_DEBUG 1 #define UMODEM_DEBUG 1 #define UAUDIO_DEBUG 1 #define AUE_DEBUG 1 @@ -208,33 +218,49 @@ __CONCAT(dname,_detach)(self, flags) \ #define UZCOM_DEBUG 1 #define URIO_DEBUG 1 #define UFTDI_DEBUG 1 -#define UMCT_DEBUG 1 #define USCANNER_DEBUG 1 #define USSCANNER_DEBUG 1 +#define EHCI_DEBUG 1 +#define UISDATA_DEBUG 1 +#define UDSBR_DEBUG 1 #endif #define Static +#define UMASS_ATAPISTR "atapiscsi" + +/* periph_quirks */ +#define PQUIRK_AUTOSAVE 0x00000001 /* do implicit SAVE POINTERS */ +#define PQUIRK_NOSYNC 0x00000002 /* does not grok SDTR */ +#define PQUIRK_NOWIDE 0x00000004 /* does not grok WDTR */ +#define PQUIRK_NOTAG 0x00000008 /* does not grok tagged cmds */ +#define PQUIRK_NOLUNS 0x00000010 /* DTWT with LUNs */ +#define PQUIRK_FORCELUNS 0x00000020 /* prehistoric device groks + LUNs */ +#define PQUIRK_NOMODESENSE 0x00000040 /* device doesn't do MODE SENSE + properly */ +#define PQUIRK_NOSTARTUNIT 0x00000080 /* do not issue START UNIT */ +#define PQUIRK_NOSYNCCACHE 0x00000100 /* do not issue SYNC CACHE */ +#define PQUIRK_CDROM 0x00000200 /* device is a CD-ROM, no + matter what else it claims */ +#define PQUIRK_LITTLETOC 0x00000400 /* audio TOC is little-endian */ +#define PQUIRK_NOCAPACITY 0x00000800 /* no READ CD CAPACITY */ +#define PQUIRK_NOTUR 0x00001000 /* no TEST UNIT READY */ +#define PQUIRK_NODOORLOCK 0x00002000 /* can't lock door */ +#define PQUIRK_NOSENSE 0x00004000 /* can't REQUEST SENSE */ +#define PQUIRK_ONLYBIG 0x00008000 /* only use SCSI_{R,W}_BIG */ +#define PQUIRK_BYTE5_ZERO 0x00010000 /* byte5 in capacity is wrong */ +#define PQUIRK_NO_FLEX_PAGE 0x00020000 /* does not support flex geom + page */ +#define PQUIRK_NOBIGMODESENSE 0x00040000 /* has no big mode-sense op */ +#define PQUIRK_CAP_SYNC 0x00080000 /* SCSI1 device with sync op */ + +typedef struct proc *usb_proc_ptr; + #define UCOMBUSCF_PORTNO -1 #define UCOMBUSCF_PORTNO_DEFAULT -1 - -#define SCSI_MODE_SENSE MODE_SENSE -#define XS_STS_DONE ITSDONE -#define XS_CTL_POLL SCSI_POLL -#define XS_CTL_DATA_IN SCSI_DATA_IN -#define XS_CTL_DATA_OUT SCSI_DATA_OUT -#define scsipi_adapter scsi_adapter -#define scsipi_cmd scsi_cmd -#define scsipi_device scsi_device -#define scsipi_done scsi_done -#define scsipi_link scsi_link -#define scsipi_minphys scsi_minphys -#define scsipi_sense scsi_sense -#define scsipi_xfer scsi_xfer -#define show_scsipi_xs show_scsi_xs -#define show_scsipi_cmd show_scsi_cmd -#define xs_control flags -#define xs_status status +#define UHIDBUSCF_REPORTID -1 +#define UHIDBUSCF_REPORTID_DEFAULT -1 #define bswap32(x) swap32(x) #define bswap16(x) swap16(x) @@ -301,6 +327,7 @@ typedef struct timeout usb_callout_t; #define usb_callout_init(h) #define usb_callout(h, t, f, d) \ { timeout_set(&(h), (f), (d)); timeout_add(&(h), (t)); } +#define usb_callout_pending(h) timeout_pending(&(h)) #define usb_uncallout(h, f, d) timeout_del(&(h)) #define usb_lockmgr(l, f, sl, p) lockmgr((l), (f), (sl), (p)) @@ -381,6 +408,15 @@ __CONCAT(dname,_detach)(self, flags) \ #include "opt_usb.h" +#if defined(_KERNEL) +#include <sys/malloc.h> + +MALLOC_DECLARE(M_USB); +MALLOC_DECLARE(M_USBDEV); +MALLOC_DECLARE(M_USBHC); + +#endif + #define Static #define USBVERBOSE @@ -395,14 +431,15 @@ __CONCAT(dname,_detach)(self, flags) \ #define DECLARE_USB_DMA_T typedef void * usb_dma_t +typedef struct proc *usb_proc_ptr; + /* XXX Change this when FreeBSD has memset */ #define memcpy(d, s, l) bcopy((s),(d),(l)) #define memset(d, v, l) bzero((d),(l)) #define bswap32(x) swap32(x) -#define usb_kthread_create1(function, sc, priv, string, name) -#define usb_kthread_create(create_function, sc) -#define usb_kthread_exit(err) +#define kthread_create1(f, s, p, a0, a1) \ + kthread_create((f), (s), (p), RFHIGHPID, (a0), (a1)) typedef struct callout_handle usb_callout_t; #define usb_callout_init(h) callout_handle_init(&(h)) @@ -416,6 +453,8 @@ typedef struct callout_handle usb_callout_t; #define powerhook_disestablish(hdl) #define PWR_RESUME 0 +#define config_detach(dev, flag) device_delete_child(device_get_parent(dev), dev) + typedef struct malloc_type *usb_malloc_type; #define USB_DECLARE_DRIVER_INIT(dname, init) \ diff --git a/sys/dev/usb/usb_quirks.c b/sys/dev/usb/usb_quirks.c index 31e003798b8..0abf050b1a6 100644 --- a/sys/dev/usb/usb_quirks.c +++ b/sys/dev/usb/usb_quirks.c @@ -1,5 +1,5 @@ -/* $OpenBSD: usb_quirks.c,v 1.10 2001/10/31 04:24:44 nate Exp $ */ -/* $NetBSD: usb_quirks.c,v 1.38 2001/04/15 10:26:36 augustss Exp $ */ +/* $OpenBSD: usb_quirks.c,v 1.11 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: usb_quirks.c,v 1.39 2001/11/13 06:24:56 lukem Exp $ */ /* $FreeBSD: src/sys/dev/usb/usb_quirks.c,v 1.13 1999/11/17 22:33:47 n_hibma Exp $ */ /* @@ -74,8 +74,6 @@ Static const struct usbd_quirk_entry { { USB_VENDOR_ALCOR2, USB_PRODUCT_ALCOR2_KBD_HUB, 0x001, { UQ_SPUR_BUT_UP }}, { USB_VENDOR_MCT, USB_PRODUCT_MCT_HUB0100, 0x102, { UQ_BUS_POWERED }}, { USB_VENDOR_MCT, USB_PRODUCT_MCT_USB232, 0x102, { UQ_BUS_POWERED }}, - { USB_VENDOR_MCT, USB_PRODUCT_MCT_SITECOM_USB232, 0x102, { UQ_BUS_POWERED }}, - { USB_VENDOR_MCT, USB_PRODUCT_MCT_DU_H3SP_USB232, 0x102, { UQ_BUS_POWERED }}, { USB_VENDOR_METRICOM, USB_PRODUCT_METRICOM_RICOCHET_GS, 0x100, { UQ_ASSUME_CM_OVER_DATA | UQ_NO_STRINGS }}, { USB_VENDOR_TI, USB_PRODUCT_TI_UTUSB41, 0x110, { UQ_POWER_CLAIM }}, diff --git a/sys/dev/usb/usb_subr.c b/sys/dev/usb/usb_subr.c index 4883a38aa17..66cb1fd6248 100644 --- a/sys/dev/usb/usb_subr.c +++ b/sys/dev/usb/usb_subr.c @@ -1,5 +1,5 @@ -/* $OpenBSD: usb_subr.c,v 1.19 2002/05/02 20:08:04 nate Exp $ */ -/* $NetBSD: usb_subr.c,v 1.87 2001/08/15 00:04:59 augustss Exp $ */ +/* $OpenBSD: usb_subr.c,v 1.20 2002/05/07 18:08:04 nate Exp $ */ +/* $NetBSD: usb_subr.c,v 1.98 2002/02/20 20:30:13 christos Exp $ */ /* $FreeBSD: src/sys/dev/usb/usb_subr.c,v 1.18 1999/11/17 22:33:47 n_hibma Exp $ */ /* @@ -352,6 +352,9 @@ usbd_reset_port(usbd_device_handle dev, int port, usb_port_status_t *ps) err)); return (err); } + /* If the device disappeared, just give up. */ + if (!(UGETW(ps->wPortStatus) & UPS_CURRENT_CONNECT_STATUS)) + return (USBD_NORMAL_COMPLETION); } while ((UGETW(ps->wPortChange) & UPS_C_PORT_RESET) == 0 && --n > 0); if (n == 0) return (USBD_TIMEOUT); @@ -475,13 +478,36 @@ usbd_fill_iface_data(usbd_device_handle dev, int ifaceidx, int altidx) break; } /* passed end, or bad desc */ - DPRINTF(("usbd_fill_iface_data: bad descriptor(s): %s\n", - ed->bLength == 0 ? "0 length" : - ed->bDescriptorType == UDESC_INTERFACE ? "iface desc": - "out of data")); + printf("usbd_fill_iface_data: bad descriptor(s): %s\n", + ed->bLength == 0 ? "0 length" : + ed->bDescriptorType == UDESC_INTERFACE ? "iface desc": + "out of data"); goto bad; found: ifc->endpoints[endpt].edesc = ed; + if (dev->speed == USB_SPEED_HIGH) { + u_int mps; + /* Control and bulk endpoints have max packet + limits. */ + switch (UE_GET_XFERTYPE(ed->bmAttributes)) { + case UE_CONTROL: + mps = USB_2_MAX_CTRL_PACKET; + goto check; + case UE_BULK: + mps = USB_2_MAX_BULK_PACKET; + check: + if (UGETW(ed->wMaxPacketSize) != mps) { + USETW(ed->wMaxPacketSize, mps); +#ifdef DIAGNOSTIC + printf("usbd_fill_iface_data: bad max " + "packet size\n"); +#endif + } + break; + default: + break; + } + } ifc->endpoints[endpt].refcnt = 0; p += ed->bLength; } @@ -725,7 +751,6 @@ usbd_setup_pipe(usbd_device_handle dev, usbd_interface_handle iface, p->repeat = 0; p->interval = ival; SIMPLEQ_INIT(&p->queue); - usb_callout_init(p->abort_handle); err = dev->bus->methods->open_pipe(p); if (err) { DPRINTFN(-1,("usbd_setup_pipe: endpoint=0x%x failed, error=" @@ -745,6 +770,7 @@ usbd_setup_pipe(usbd_device_handle dev, usbd_interface_handle iface, void usbd_kill_pipe(usbd_pipe_handle pipe) { + usbd_abort_pipe(pipe); pipe->methods->close(pipe); pipe->endpoint->refcnt--; free(pipe, M_USB); @@ -927,16 +953,17 @@ usbd_probe_and_attach(device_ptr_t parent, usbd_device_handle dev, */ usbd_status usbd_new_device(device_ptr_t parent, usbd_bus_handle bus, int depth, - int lowspeed, int port, struct usbd_port *up) + int speed, int port, struct usbd_port *up) { usbd_device_handle dev; + struct usbd_device *hub; usb_device_descriptor_t *dd; usbd_status err; int addr; int i; - DPRINTF(("usbd_new_device bus=%p port=%d depth=%d lowspeed=%d\n", - bus, port, depth, lowspeed)); + DPRINTF(("usbd_new_device bus=%p port=%d depth=%d speed=%d\n", + bus, port, depth, speed)); addr = usbd_getnewaddr(bus); if (addr < 0) { printf("%s: No free USB addresses, new device ignored.\n", @@ -947,7 +974,7 @@ usbd_new_device(device_ptr_t parent, usbd_bus_handle bus, int depth, dev = malloc(sizeof *dev, M_USB, M_NOWAIT); if (dev == NULL) return (USBD_NOMEM); - memset(dev, 0, sizeof(*dev)); + memset(dev, 0, sizeof *dev); dev->bus = bus; @@ -965,9 +992,15 @@ usbd_new_device(device_ptr_t parent, usbd_bus_handle bus, int depth, dev->quirks = &usbd_no_quirk; dev->address = USB_START_ADDR; dev->ddesc.bMaxPacketSize = 0; - dev->lowspeed = lowspeed != 0; dev->depth = depth; dev->powersrc = up; + dev->myhub = up->parent; + for (hub = up->parent; + hub != NULL && hub->speed != USB_SPEED_HIGH; + hub = hub->myhub) + ; + dev->myhighhub = hub; + dev->speed = speed; dev->langid = USBD_NOLANG; dev->cookie.cookie = ++usb_cookie_no; @@ -996,11 +1029,22 @@ usbd_new_device(device_ptr_t parent, usbd_bus_handle bus, int depth, return (err); } + if (speed == USB_SPEED_HIGH) { + /* Max packet size must be 64 (sec 5.5.3). */ + if (dd->bMaxPacketSize != USB_2_MAX_CTRL_PACKET) { +#ifdef DIAGNOSTIC + printf("usbd_new_device: addr=%d bad max packet size\n", + addr); +#endif + dd->bMaxPacketSize = USB_2_MAX_CTRL_PACKET; + } + } + DPRINTF(("usbd_new_device: adding unit addr=%d, rev=%02x, class=%d, " - "subclass=%d, protocol=%d, maxpacket=%d, len=%d, ls=%d\n", + "subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n", addr,UGETW(dd->bcdUSB), dd->bDeviceClass, dd->bDeviceSubClass, dd->bDeviceProtocol, dd->bMaxPacketSize, dd->bLength, - dev->lowspeed)); + dev->speed)); if (dd->bDescriptorType != UDESC_DEVICE) { /* Illegal device descriptor */ @@ -1203,7 +1247,7 @@ usbd_fill_deviceinfo(usbd_device_handle dev, struct usb_device_info *di, di->udi_protocol = dev->ddesc.bDeviceProtocol; di->udi_config = dev->config; di->udi_power = dev->self_powered ? 0 : dev->power; - di->udi_lowspeed = dev->lowspeed; + di->udi_speed = dev->speed; if (dev->subdevs != NULL) { for (i = 0; dev->subdevs[i] && @@ -1306,13 +1350,7 @@ usb_disconnect_port(struct usbd_port *up, device_ptr_t parent) if (up->portno != 0) printf(" port %d", up->portno); printf(" (addr %d) disconnected\n", dev->address); -#if defined(__NetBSD__) || defined(__OpenBSD__) config_detach(dev->subdevs[i], DETACH_FORCE); -#elif defined(__FreeBSD__) - device_delete_child(device_get_parent(dev->subdevs[i]), - dev->subdevs[i]); -#endif - } } diff --git a/sys/dev/usb/usbdevs.h b/sys/dev/usb/usbdevs.h index 8a81bd55e82..875ba4dfa7a 100644 --- a/sys/dev/usb/usbdevs.h +++ b/sys/dev/usb/usbdevs.h @@ -1,4 +1,4 @@ -/* $OpenBSD: usbdevs.h,v 1.43 2002/04/01 21:36:16 nate Exp $ */ +/* $OpenBSD: usbdevs.h,v 1.44 2002/05/07 18:08:05 nate Exp $ */ /* * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT. diff --git a/sys/dev/usb/usbdevs_data.h b/sys/dev/usb/usbdevs_data.h index 1e84ae19099..e7096569e4c 100644 --- a/sys/dev/usb/usbdevs_data.h +++ b/sys/dev/usb/usbdevs_data.h @@ -1,4 +1,4 @@ -/* $OpenBSD: usbdevs_data.h,v 1.43 2002/04/01 21:36:16 nate Exp $ */ +/* $OpenBSD: usbdevs_data.h,v 1.44 2002/05/07 18:08:05 nate Exp $ */ /* * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT. diff --git a/sys/dev/usb/usbdi.c b/sys/dev/usb/usbdi.c index 002fa3767f5..856a20cd905 100644 --- a/sys/dev/usb/usbdi.c +++ b/sys/dev/usb/usbdi.c @@ -1,5 +1,5 @@ -/* $OpenBSD: usbdi.c,v 1.16 2002/05/02 20:08:04 nate Exp $ */ -/* $NetBSD: usbdi.c,v 1.81 2001/04/17 00:05:33 augustss Exp $ */ +/* $OpenBSD: usbdi.c,v 1.17 2002/05/07 18:08:05 nate Exp $ */ +/* $NetBSD: usbdi.c,v 1.99 2002/02/28 04:49:16 thorpej Exp $ */ /* $FreeBSD: src/sys/dev/usb/usbdi.c,v 1.28 1999/11/17 22:33:49 n_hibma Exp $ */ /* @@ -79,10 +79,10 @@ extern int usbdebug; Static usbd_status usbd_ar_pipe(usbd_pipe_handle pipe); Static void usbd_do_request_async_cb -(usbd_xfer_handle, usbd_private_handle, usbd_status); + (usbd_xfer_handle, usbd_private_handle, usbd_status); Static void usbd_start_next(usbd_pipe_handle pipe); Static usbd_status usbd_open_pipe_ival -(usbd_interface_handle, u_int8_t, u_int8_t, usbd_pipe_handle *, int); + (usbd_interface_handle, u_int8_t, u_int8_t, usbd_pipe_handle *, int); Static int usbd_nbuses = 0; @@ -109,7 +109,42 @@ usbd_xfer_isread(usbd_xfer_handle xfer) } #ifdef USB_DEBUG -void usbd_dump_queue(usbd_pipe_handle pipe); +void +usbd_dump_iface(struct usbd_interface *iface) +{ + printf("usbd_dump_iface: iface=%p\n", iface); + if (iface == NULL) + return; + printf(" device=%p idesc=%p index=%d altindex=%d priv=%p\n", + iface->device, iface->idesc, iface->index, iface->altindex, + iface->priv); +} + +void +usbd_dump_device(struct usbd_device *dev) +{ + printf("usbd_dump_device: dev=%p\n", dev); + if (dev == NULL) + return; + printf(" bus=%p default_pipe=%p\n", dev->bus, dev->default_pipe); + printf(" address=%d config=%d depth=%d speed=%d self_powered=%d " + "power=%d langid=%d\n", + dev->address, dev->config, dev->depth, dev->speed, + dev->self_powered, dev->power, dev->langid); +} + +void +usbd_dump_endpoint(struct usbd_endpoint *endp) +{ + printf("usbd_dump_endpoint: endp=%p\n", endp); + if (endp == NULL) + return; + printf(" edesc=%p refcnt=%d\n", endp->edesc, endp->refcnt); + if (endp->edesc) + printf(" bEndpointAddress=0x%02x\n", + endp->edesc->bEndpointAddress); +} + void usbd_dump_queue(usbd_pipe_handle pipe) { @@ -122,6 +157,21 @@ usbd_dump_queue(usbd_pipe_handle pipe) printf(" xfer=%p\n", xfer); } } + +void +usbd_dump_pipe(usbd_pipe_handle pipe) +{ + printf("usbd_dump_pipe: pipe=%p\n", pipe); + if (pipe == NULL) + return; + usbd_dump_iface(pipe->iface); + usbd_dump_device(pipe->device); + usbd_dump_endpoint(pipe->endpoint); + printf(" (usbd_dump_pipe:)\n refcnt=%d running=%d aborting=%d\n", + pipe->refcnt, pipe->running, pipe->aborting); + printf(" intrxfer=%p, repeat=%d, interval=%d\n", + pipe->intrxfer, pipe->repeat, pipe->interval); +} #endif usbd_status @@ -221,12 +271,6 @@ usbd_close_pipe(usbd_pipe_handle pipe) LIST_REMOVE(pipe, next); pipe->endpoint->refcnt--; pipe->methods->close(pipe); -#if defined(__NetBSD__) && defined(DIAGNOSTIC) - if (callout_pending(&pipe->abort_handle)) { - callout_stop(&pipe->abort_handle); - printf("usbd_close_pipe: abort_handle pending"); - } -#endif if (pipe->intrxfer != NULL) usbd_free_xfer(pipe->intrxfer); free(pipe, M_USB); @@ -315,9 +359,13 @@ usbd_alloc_buffer(usbd_xfer_handle xfer, u_int32_t size) struct usbd_bus *bus = xfer->device->bus; usbd_status err; +#ifdef DIAGNOSTIC + if (xfer->rqflags & (URQ_DEV_DMABUF | URQ_AUTO_DMABUF)) + printf("usbd_alloc_buffer: xfer already has a buffer\n"); +#endif err = bus->methods->allocm(bus, &xfer->dmabuf, size); if (err) - return (0); + return (NULL); xfer->rqflags |= URQ_DEV_DMABUF; return (KERNADDR(&xfer->dmabuf)); } @@ -512,7 +560,7 @@ usbd_clear_endpoint_stall(usbd_pipe_handle pipe) DPRINTFN(8, ("usbd_clear_endpoint_stall\n")); /* - * Clearing en endpoint stall resets the enpoint toggle, so + * Clearing en endpoint stall resets the endpoint toggle, so * do the same to the HC toggle. */ pipe->methods->cleartoggle(pipe); @@ -551,7 +599,6 @@ usbd_clear_endpoint_stall_async(usbd_pipe_handle pipe) return (err); } -void usbd_clear_endpoint_toggle(usbd_pipe_handle pipe); /* XXXXX */ void usbd_clear_endpoint_toggle(usbd_pipe_handle pipe) { @@ -580,12 +627,11 @@ usbd_interface_count(usbd_device_handle dev, u_int8_t *count) return (USBD_NORMAL_COMPLETION); } -usbd_status +void usbd_interface2device_handle(usbd_interface_handle iface, usbd_device_handle *dev) { *dev = iface->device; - return (USBD_NORMAL_COMPLETION); } usbd_status @@ -719,6 +765,13 @@ usb_transfer_complete(usbd_xfer_handle xfer) DPRINTFN(5, ("usb_transfer_complete: pipe=%p xfer=%p status=%d " "actlen=%d\n", pipe, xfer, xfer->status, xfer->actlen)); +#ifdef DIAGNOSTIC + if (xfer->busy_free != XFER_ONQU) { + printf("usb_transfer_complete: xfer=%p not busy 0x%08x\n", + xfer, xfer->busy_free); + return; + } +#endif #ifdef DIAGNOSTIC if (pipe == NULL) { @@ -758,6 +811,7 @@ usb_transfer_complete(usbd_xfer_handle xfer) if (xfer != SIMPLEQ_FIRST(&pipe->queue)) printf("usb_transfer_complete: bad dequeue %p != %p\n", xfer, SIMPLEQ_FIRST(&pipe->queue)); + xfer->busy_free = XFER_BUSY; #endif SIMPLEQ_REMOVE_HEAD(&pipe->queue, xfer, next); } @@ -811,6 +865,14 @@ usb_insert_transfer(usbd_xfer_handle xfer) DPRINTFN(5,("usb_insert_transfer: pipe=%p running=%d timeout=%d\n", pipe, pipe->running, xfer->timeout)); +#ifdef DIAGNOSTIC + if (xfer->busy_free != XFER_BUSY) { + printf("usb_insert_transfer: xfer=%p not busy 0x%08x\n", + xfer, xfer->busy_free); + return (USBD_INVAL); + } + xfer->busy_free = XFER_ONQU; +#endif s = splusb(); SIMPLEQ_INSERT_TAIL(&pipe->queue, xfer, next); if (pipe->running) @@ -861,20 +923,22 @@ usbd_start_next(usbd_pipe_handle pipe) usbd_status usbd_do_request(usbd_device_handle dev, usb_device_request_t *req, void *data) { - return (usbd_do_request_flags(dev, req, data, 0, 0)); + return (usbd_do_request_flags(dev, req, data, 0, 0, + USBD_DEFAULT_TIMEOUT)); } usbd_status usbd_do_request_flags(usbd_device_handle dev, usb_device_request_t *req, - void *data, u_int16_t flags, int *actlen) + void *data, u_int16_t flags, int *actlen, u_int32_t timo) { return (usbd_do_request_flags_pipe(dev, dev->default_pipe, req, - data, flags, actlen)); + data, flags, actlen, timo)); } usbd_status usbd_do_request_flags_pipe(usbd_device_handle dev, usbd_pipe_handle pipe, - usb_device_request_t *req, void *data, u_int16_t flags, int *actlen) + usb_device_request_t *req, void *data, u_int16_t flags, int *actlen, + u_int32_t timeout) { usbd_xfer_handle xfer; usbd_status err; @@ -893,7 +957,7 @@ usbd_do_request_flags_pipe(usbd_device_handle dev, usbd_pipe_handle pipe, xfer = usbd_alloc_xfer(dev); if (xfer == NULL) return (USBD_NOMEM); - usbd_setup_default_xfer(xfer, dev, 0, USBD_DEFAULT_TIMEOUT, req, + usbd_setup_default_xfer(xfer, dev, 0, timeout, req, data, UGETW(req->wLength), flags, 0); xfer->pipe = pipe; err = usbd_sync_transfer(xfer); @@ -1024,6 +1088,9 @@ usbd_set_polling(usbd_device_handle dev, int on) dev->bus->use_polling++; else dev->bus->use_polling--; + /* When polling we need to make sure there is nothing pending to do. */ + if (dev->bus->use_polling) + dev->bus->methods->soft_intr(dev->bus); } @@ -1068,7 +1135,7 @@ usb_match_device(const struct usb_devno *tbl, u_int nentries, u_int sz, if (tbl->ud_vendor == vendor && (tproduct == product || tproduct == USB_PRODUCT_ANY)) return (tbl); - tbl = (struct usb_devno *)((char *)tbl + sz); + tbl = (const struct usb_devno *)((const char *)tbl + sz); } return (NULL); } diff --git a/sys/dev/usb/usbdi.h b/sys/dev/usb/usbdi.h index 517287ea75d..0a571b0bcee 100644 --- a/sys/dev/usb/usbdi.h +++ b/sys/dev/usb/usbdi.h @@ -1,5 +1,5 @@ -/* $OpenBSD: usbdi.h,v 1.14 2002/04/01 21:47:07 nate Exp $ */ -/* $NetBSD: usbdi.h,v 1.53 2001/08/15 00:04:59 augustss Exp $ */ +/* $OpenBSD: usbdi.h,v 1.15 2002/05/07 18:08:05 nate Exp $ */ +/* $NetBSD: usbdi.h,v 1.61 2002/02/11 15:20:23 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/usbdi.h,v 1.18 1999/11/17 22:33:49 n_hibma Exp $ */ /* @@ -48,25 +48,25 @@ typedef void *usbd_private_handle; typedef enum { /* keep in sync with usbd_status_msgs */ USBD_NORMAL_COMPLETION = 0, /* must be 0 */ - USBD_IN_PROGRESS, + USBD_IN_PROGRESS, /* 1 */ /* errors */ - USBD_PENDING_REQUESTS, - USBD_NOT_STARTED, - USBD_INVAL, - USBD_NOMEM, - USBD_CANCELLED, - USBD_BAD_ADDRESS, - USBD_IN_USE, - USBD_NO_ADDR, - USBD_SET_ADDR_FAILED, - USBD_NO_POWER, - USBD_TOO_DEEP, - USBD_IOERROR, - USBD_NOT_CONFIGURED, - USBD_TIMEOUT, - USBD_SHORT_XFER, - USBD_STALLED, - USBD_INTERRUPTED, + USBD_PENDING_REQUESTS, /* 2 */ + USBD_NOT_STARTED, /* 3 */ + USBD_INVAL, /* 4 */ + USBD_NOMEM, /* 5 */ + USBD_CANCELLED, /* 6 */ + USBD_BAD_ADDRESS, /* 7 */ + USBD_IN_USE, /* 8 */ + USBD_NO_ADDR, /* 9 */ + USBD_SET_ADDR_FAILED, /* 10 */ + USBD_NO_POWER, /* 11 */ + USBD_TOO_DEEP, /* 12 */ + USBD_IOERROR, /* 13 */ + USBD_NOT_CONFIGURED, /* 14 */ + USBD_TIMEOUT, /* 15 */ + USBD_SHORT_XFER, /* 16 */ + USBD_STALLED, /* 17 */ + USBD_INTERRUPTED, /* 18 */ USBD_ERROR_MAX /* must be last */ } usbd_status; @@ -117,16 +117,17 @@ usb_endpoint_descriptor_t *usbd_interface2endpoint_descriptor usbd_status usbd_abort_pipe(usbd_pipe_handle pipe); usbd_status usbd_clear_endpoint_stall(usbd_pipe_handle pipe); usbd_status usbd_clear_endpoint_stall_async(usbd_pipe_handle pipe); +void usbd_clear_endpoint_toggle(usbd_pipe_handle pipe); usbd_status usbd_endpoint_count(usbd_interface_handle dev, u_int8_t *count); usbd_status usbd_interface_count(usbd_device_handle dev, u_int8_t *count); -usbd_status usbd_interface2device_handle(usbd_interface_handle iface, - usbd_device_handle *dev); +void usbd_interface2device_handle(usbd_interface_handle iface, + usbd_device_handle *dev); usbd_status usbd_device2interface_handle(usbd_device_handle dev, u_int8_t ifaceno, usbd_interface_handle *iface); usbd_device_handle usbd_pipe2device_handle(usbd_pipe_handle); -void *usbd_alloc_buffer(usbd_xfer_handle req, u_int32_t size); -void usbd_free_buffer(usbd_xfer_handle req); +void *usbd_alloc_buffer(usbd_xfer_handle xfer, u_int32_t size); +void usbd_free_buffer(usbd_xfer_handle xfer); void *usbd_get_buffer(usbd_xfer_handle xfer); usbd_status usbd_sync_transfer(usbd_xfer_handle req); usbd_status usbd_open_pipe_intr(usbd_interface_handle iface, u_int8_t address, @@ -139,10 +140,11 @@ usbd_status usbd_do_request_async(usbd_device_handle pipe, usb_device_request_t *req, void *data); usbd_status usbd_do_request_flags(usbd_device_handle pipe, usb_device_request_t *req, - void *data, u_int16_t flags, int *); + void *data, u_int16_t flags, int*, u_int32_t); usbd_status usbd_do_request_flags_pipe( usbd_device_handle dev, usbd_pipe_handle pipe, - usb_device_request_t *req, void *data, u_int16_t flags, int *actlen); + usb_device_request_t *req, void *data, u_int16_t flags, int *actlen, + u_int32_t); usb_interface_descriptor_t *usbd_get_interface_descriptor (usbd_interface_handle iface); usb_config_descriptor_t *usbd_get_config_descriptor(usbd_device_handle dev); @@ -150,7 +152,7 @@ usb_device_descriptor_t *usbd_get_device_descriptor(usbd_device_handle dev); usbd_status usbd_set_interface(usbd_interface_handle, int); int usbd_get_no_alts(usb_config_descriptor_t *, int); usbd_status usbd_get_interface(usbd_interface_handle iface, u_int8_t *aiface); -void usbd_fill_deviceinfo(usbd_device_handle dev, struct usb_device_info *di, int); +void usbd_fill_deviceinfo(usbd_device_handle, struct usb_device_info *, int); int usbd_get_interface_altindex(usbd_interface_handle iface); usb_interface_descriptor_t *usbd_find_idesc(usb_config_descriptor_t *cd, @@ -267,14 +269,15 @@ struct usb_attach_arg { int usbd_driver_load(module_t mod, int what, void *arg); #endif -/* - * XXX - * splusb MUST be the lowest level interrupt so that within USB callbacks - * the level can be raised the appropriate level. - * XXX Should probably use a softsplusb. - */ -/* XXX */ +/* XXX Perhaps USB should have its own levels? */ +#ifdef USB_USE_SOFTINTR +#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS +#define splusb splsoftnet +#else +#define splusb splsoftclock +#endif /* __HAVE_GENERIC_SOFT_INTERRUPTS */ +#else #define splusb splbio +#endif /* USB_USE_SOFTINTR */ #define splhardusb splbio #define IPL_USB IPL_BIO -/* XXX */ diff --git a/sys/dev/usb/usbdi_util.c b/sys/dev/usb/usbdi_util.c index 3109bde4fac..cfe02dfea0f 100644 --- a/sys/dev/usb/usbdi_util.c +++ b/sys/dev/usb/usbdi_util.c @@ -1,5 +1,5 @@ -/* $OpenBSD: usbdi_util.c,v 1.11 2001/10/31 04:24:45 nate Exp $ */ -/* $NetBSD: usbdi_util.c,v 1.35 2001/10/26 17:58:21 augustss Exp $ */ +/* $OpenBSD: usbdi_util.c,v 1.12 2002/05/07 18:08:05 nate Exp $ */ +/* $NetBSD: usbdi_util.c,v 1.39 2001/12/27 11:24:42 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/usbdi_util.c,v 1.14 1999/11/17 22:33:50 n_hibma Exp $ */ /* @@ -220,6 +220,25 @@ usbd_set_port_feature(usbd_device_handle dev, int port, int sel) return (usbd_do_request(dev, &req, 0)); } +usbd_status +usbd_get_protocol(usbd_interface_handle iface, u_int8_t *report) +{ + usb_interface_descriptor_t *id = usbd_get_interface_descriptor(iface); + usbd_device_handle dev; + usb_device_request_t req; + + DPRINTFN(4, ("usbd_get_protocol: iface=%p, endpt=%d\n", + iface, id->bInterfaceNumber)); + if (id == NULL) + return (USBD_IOERROR); + usbd_interface2device_handle(iface, &dev); + req.bmRequestType = UT_READ_CLASS_INTERFACE; + req.bRequest = UR_GET_PROTOCOL; + USETW(req.wValue, 0); + USETW(req.wIndex, id->bInterfaceNumber); + USETW(req.wLength, 1); + return (usbd_do_request(dev, &req, report)); +} usbd_status usbd_set_protocol(usbd_interface_handle iface, int report) @@ -227,15 +246,12 @@ usbd_set_protocol(usbd_interface_handle iface, int report) usb_interface_descriptor_t *id = usbd_get_interface_descriptor(iface); usbd_device_handle dev; usb_device_request_t req; - usbd_status err; DPRINTFN(4, ("usbd_set_protocol: iface=%p, report=%d, endpt=%d\n", iface, report, id->bInterfaceNumber)); if (id == NULL) return (USBD_IOERROR); - err = usbd_interface2device_handle(iface, &dev); - if (err) - return (err); + usbd_interface2device_handle(iface, &dev); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_SET_PROTOCOL; USETW(req.wValue, report); @@ -251,14 +267,11 @@ usbd_set_report(usbd_interface_handle iface, int type, int id, void *data, usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface); usbd_device_handle dev; usb_device_request_t req; - usbd_status err; DPRINTFN(4, ("usbd_set_report: len=%d\n", len)); if (ifd == NULL) return (USBD_IOERROR); - err = usbd_interface2device_handle(iface, &dev); - if (err) - return (err); + usbd_interface2device_handle(iface, &dev); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_SET_REPORT; USETW2(req.wValue, type, id); @@ -268,20 +281,17 @@ usbd_set_report(usbd_interface_handle iface, int type, int id, void *data, } usbd_status -usbd_set_report_async(usbd_interface_handle iface, int type, int id, void *data, - int len) +usbd_set_report_async(usbd_interface_handle iface, int type, int id, + void *data, int len) { usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface); usbd_device_handle dev; usb_device_request_t req; - usbd_status err; DPRINTFN(4, ("usbd_set_report_async: len=%d\n", len)); if (ifd == NULL) return (USBD_IOERROR); - err = usbd_interface2device_handle(iface, &dev); - if (err) - return (err); + usbd_interface2device_handle(iface, &dev); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_SET_REPORT; USETW2(req.wValue, type, id); @@ -297,14 +307,11 @@ usbd_get_report(usbd_interface_handle iface, int type, int id, void *data, usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface); usbd_device_handle dev; usb_device_request_t req; - usbd_status err; DPRINTFN(4, ("usbd_get_report: len=%d\n", len)); if (ifd == NULL) return (USBD_IOERROR); - err = usbd_interface2device_handle(iface, &dev); - if (err) - return (err); + usbd_interface2device_handle(iface, &dev); req.bmRequestType = UT_READ_CLASS_INTERFACE; req.bRequest = UR_GET_REPORT; USETW2(req.wValue, type, id); @@ -319,14 +326,11 @@ usbd_set_idle(usbd_interface_handle iface, int duration, int id) usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface); usbd_device_handle dev; usb_device_request_t req; - usbd_status err; DPRINTFN(4, ("usbd_set_idle: %d %d\n", duration, id)); if (ifd == NULL) return (USBD_IOERROR); - err = usbd_interface2device_handle(iface, &dev); - if (err) - return (err); + usbd_interface2device_handle(iface, &dev); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_SET_IDLE; USETW2(req.wValue, duration, id); @@ -357,13 +361,10 @@ usbd_get_hid_descriptor(usbd_interface_handle ifc) usb_config_descriptor_t *cdesc; usb_hid_descriptor_t *hd; char *p, *end; - usbd_status err; if (idesc == NULL) return (0); - err = usbd_interface2device_handle(ifc, &dev); - if (err) - return (0); + usbd_interface2device_handle(ifc, &dev); cdesc = usbd_get_config_descriptor(dev); p = (char *)idesc + idesc->bLength; @@ -388,9 +389,7 @@ usbd_read_report_desc(usbd_interface_handle ifc, void **descp, int *sizep, usbd_device_handle dev; usbd_status err; - err = usbd_interface2device_handle(ifc, &dev); - if (err) - return (err); + usbd_interface2device_handle(ifc, &dev); id = usbd_get_interface_descriptor(ifc); if (id == NULL) return (USBD_INVAL); @@ -482,3 +481,21 @@ usb_detach_wakeup(device_ptr_t dv) DPRINTF(("usb_detach_wakeup: for %s\n", USBDEVPTRNAME(dv))); wakeup(dv); } + +usb_descriptor_t * +usb_find_desc(usbd_device_handle dev, int type) +{ + usb_descriptor_t *desc; + usb_config_descriptor_t *cd = usbd_get_config_descriptor(dev); + uByte *p = (uByte *)cd; + uByte *end = p + UGETW(cd->wTotalLength); + + while (p < end) { + desc = (usb_descriptor_t *)p; + if (desc->bDescriptorType == type) + return (desc); + p += desc->bLength; + } + + return (NULL); +} diff --git a/sys/dev/usb/usbdi_util.h b/sys/dev/usb/usbdi_util.h index 224cb714787..6bca3e54e7c 100644 --- a/sys/dev/usb/usbdi_util.h +++ b/sys/dev/usb/usbdi_util.h @@ -1,5 +1,5 @@ -/* $OpenBSD: usbdi_util.h,v 1.8 2001/10/31 04:24:45 nate Exp $ */ -/* $NetBSD: usbdi_util.h,v 1.23 2001/10/26 17:58:22 augustss Exp $ */ +/* $OpenBSD: usbdi_util.h,v 1.9 2002/05/07 18:08:05 nate Exp $ */ +/* $NetBSD: usbdi_util.h,v 1.27 2002/03/17 18:02:53 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/usbdi_util.h,v 1.9 1999/11/17 22:33:50 n_hibma Exp $ */ /* @@ -53,8 +53,9 @@ usbd_status usbd_set_hub_feature(usbd_device_handle dev, int); usbd_status usbd_clear_hub_feature(usbd_device_handle, int); usbd_status usbd_set_port_feature(usbd_device_handle dev, int, int); usbd_status usbd_clear_port_feature(usbd_device_handle, int, int); -usbd_status usbd_get_device_status(usbd_device_handle,usb_status_t*); +usbd_status usbd_get_device_status(usbd_device_handle, usb_status_t *); usbd_status usbd_get_hub_status(usbd_device_handle, usb_hub_status_t *); +usbd_status usbd_get_protocol(usbd_interface_handle dev, u_int8_t *report); usbd_status usbd_set_protocol(usbd_interface_handle dev, int report); usbd_status usbd_get_report_descriptor(usbd_device_handle dev, int ifcno, int size, void *d); @@ -84,3 +85,4 @@ usbd_status usbd_bulk_transfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe, void usb_detach_wait(device_ptr_t); void usb_detach_wakeup(device_ptr_t); +usb_descriptor_t *usb_find_desc(usbd_device_handle dev, int type); diff --git a/sys/dev/usb/usbdivar.h b/sys/dev/usb/usbdivar.h index 9d81200a6ff..d5e53c3e0f1 100644 --- a/sys/dev/usb/usbdivar.h +++ b/sys/dev/usb/usbdivar.h @@ -1,5 +1,5 @@ -/* $OpenBSD: usbdivar.h,v 1.14 2001/10/31 04:24:45 nate Exp $ */ -/* $NetBSD: usbdivar.h,v 1.63 2001/01/21 19:00:06 augustss Exp $ */ +/* $OpenBSD: usbdivar.h,v 1.15 2002/05/07 18:08:05 nate Exp $ */ +/* $NetBSD: usbdivar.h,v 1.69 2001/12/27 18:43:46 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/usbdivar.h,v 1.11 1999/11/17 22:33:51 n_hibma Exp $ */ /* @@ -80,7 +80,7 @@ struct usbd_port { u_int8_t portno; u_int8_t restartcnt; #define USBD_RESTART_MAX 5 - struct usbd_device *device; + struct usbd_device *device; /* Connected device */ struct usbd_device *parent; /* The ports hub */ }; @@ -117,13 +117,11 @@ struct usbd_bus { #define USBREV_2_0 4 #define USBREV_STR { "unknown", "pre 1.0", "1.0", "1.1", "2.0" } -#if 0 #ifdef USB_USE_SOFTINTR #ifdef __HAVE_GENERIC_SOFT_INTERRUPTS void *soft; /* soft interrupt cookie */ #else - struct callout softi; -#endif + usb_callout_t softi; #endif #endif @@ -138,13 +136,15 @@ struct usbd_device { u_int8_t address; /* device addess */ u_int8_t config; /* current configuration # */ u_int8_t depth; /* distance from root hub */ - u_int8_t lowspeed; /* lowspeed flag */ + u_int8_t speed; /* low/full/high speed */ u_int8_t self_powered; /* flag for self powered */ u_int16_t power; /* mA the device uses */ int16_t langid; /* language for strings */ #define USBD_NOLANG (-1) usb_event_cookie_t cookie; /* unique connection id */ struct usbd_port *powersrc; /* upstream hub port, or 0 */ + struct usbd_device *myhub; /* upstream hub */ + struct usbd_device *myhighhub; /* closest high speed hub */ struct usbd_endpoint def_ep; /* for pipe 0 */ usb_endpoint_descriptor_t def_ep_desc; /* for pipe 0 */ struct usbd_interface *ifaces; /* array of all interfaces */ @@ -179,8 +179,6 @@ struct usbd_pipe { char repeat; int interval; - usb_callout_t abort_handle; - /* Filled by HC driver. */ struct usbd_pipe_methods *methods; }; @@ -199,7 +197,8 @@ struct usbd_xfer { #ifdef DIAGNOSTIC u_int32_t busy_free; #define XFER_FREE 0x46524545 -#define XFER_BUSY 0x42555357 +#define XFER_BUSY 0x42555359 +#define XFER_ONQU 0x4f4e5155 #endif /* For control pipe */ @@ -228,6 +227,14 @@ struct usbd_xfer { void usbd_init(void); void usbd_finish(void); +#ifdef USB_DEBUG +void usbd_dump_iface(struct usbd_interface *iface); +void usbd_dump_device(struct usbd_device *dev); +void usbd_dump_endpoint(struct usbd_endpoint *endp); +void usbd_dump_queue(usbd_pipe_handle pipe); +void usbd_dump_pipe(usbd_pipe_handle pipe); +#endif + /* Routines from usb_subr.c */ int usbctlprint(void *, const char *); void usb_delay_ms(usbd_bus_handle, u_int); diff --git a/sys/dev/usb/usbhid.h b/sys/dev/usb/usbhid.h index 86c7948ccee..cd27fb55643 100644 --- a/sys/dev/usb/usbhid.h +++ b/sys/dev/usb/usbhid.h @@ -1,5 +1,5 @@ -/* $OpenBSD: usbhid.h,v 1.4 2000/11/08 18:10:39 aaron Exp $ */ -/* $NetBSD: usbhid.h,v 1.9 2000/09/03 19:09:14 augustss Exp $ */ +/* $OpenBSD: usbhid.h,v 1.5 2002/05/07 18:08:05 nate Exp $ */ +/* $NetBSD: usbhid.h,v 1.11 2001/12/28 00:20:24 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/usbhid.h,v 1.7 1999/11/17 22:33:51 n_hibma Exp $ */ /* @@ -165,12 +165,25 @@ typedef struct usb_hid_descriptor { #define HUD_ERASER 0x0045 #define HUD_TABLET_PICK 0x0046 -#define HID_USAGE2(p,u) (((p) << 16) | u) +/* Usages LEDs */ +#define HUD_LED_NUM_LOCK 0x0001 +#define HUD_LED_CAPS_LOCK 0x0002 +#define HUD_LED_SCROLL_LOCK 0x0003 +#define HUD_LED_COMPOSE 0x0004 +#define HUD_LED_KANA 0x0005 + +#define HID_USAGE2(p, u) (((p) << 16) | u) +#define HID_GET_USAGE(u) ((u) & 0xffff) +#define HID_GET_USAGE_PAGE(u) (((u) >> 16) & 0xffff) #define UHID_INPUT_REPORT 0x01 #define UHID_OUTPUT_REPORT 0x02 #define UHID_FEATURE_REPORT 0x03 +#define HCOLL_PHYSICAL 0 +#define HCOLL_APPLICATION 1 +#define HCOLL_LOGICAL 2 + /* Bits in the input/output/feature items */ #define HIO_CONST 0x001 #define HIO_VARIABLE 0x002 diff --git a/sys/dev/usb/uscanner.c b/sys/dev/usb/uscanner.c index a9973e43a53..dad8296523e 100644 --- a/sys/dev/usb/uscanner.c +++ b/sys/dev/usb/uscanner.c @@ -1,6 +1,5 @@ -/* $OpenBSD: uscanner.c,v 1.5 2002/01/03 22:23:43 deraadt Exp $ */ -/* $NetBSD: uscanner.c,v 1.18 2001/10/11 12:05:10 augustss Exp $ */ -/* $FreeBSD$ */ +/* $OpenBSD: uscanner.c,v 1.6 2002/05/07 18:08:05 nate Exp $ */ +/* $NetBSD: uscanner.c,v 1.27 2002/02/11 10:09:14 augustss Exp $ */ /* * Copyright (c) 2000 The NetBSD Foundation, Inc. @@ -56,7 +55,11 @@ #endif #include <sys/tty.h> #include <sys/file.h> +#if defined(__FreeBSD__) && __FreeBSD_version >= 500014 +#include <sys/selinfo.h> +#else #include <sys/select.h> +#endif #include <sys/proc.h> #include <sys/vnode.h> #include <sys/poll.h> @@ -77,104 +80,128 @@ int uscannerdebug = 0; #define DPRINTFN(n,x) #endif +struct uscan_info { + struct usb_devno devno; + u_int flags; +#define USC_KEEP_OPEN 1 +}; + /* Table of scanners that may work with this driver. */ -static const struct scanner_id { - uint16_t vendor; - uint16_t product; -} scanner_ids [] = { - /* Acer Peripherals */ - { USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_320U }, - { USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_640U }, - { USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_620U }, - { USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_C310U }, - - /* AGFA */ - { USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1212U }, - { USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1212U2 }, - { USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANTOUCH }, - - /* Kye */ - { USB_VENDOR_KYE, USB_PRODUCT_KYE_VIVIDPRO }, - - /* HP */ - { USB_VENDOR_HP, USB_PRODUCT_HP_3300C }, - { USB_VENDOR_HP, USB_PRODUCT_HP_3400CSE }, - { USB_VENDOR_HP, USB_PRODUCT_HP_4100C }, - { USB_VENDOR_HP, USB_PRODUCT_HP_4200C }, - { USB_VENDOR_HP, USB_PRODUCT_HP_S20 }, - { USB_VENDOR_HP, USB_PRODUCT_HP_5200C }, +static const struct uscan_info uscanner_devs[] = { + /* Acer Peripherals */ + {{ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_320U }, 0 }, + {{ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_640U }, 0 }, + {{ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_620U }, 0 }, + {{ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_C310U }, 0 }, + + /* AGFA */ + {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1236U }, 0 }, + {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1212U }, 0 }, + {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1212U2 }, 0 }, + {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANTOUCH }, 0 }, + {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE40 }, 0 }, + {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE50 }, 0 }, + {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE20 }, 0 }, + {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE25 }, 0 }, + {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE26 }, 0 }, + {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE52 }, 0 }, + + /* Avision */ + {{ USB_VENDOR_AVISION, USB_PRODUCT_AVISION_1200U }, 0 }, + + /* Canon */ + {{ USB_VENDOR_CANON, USB_PRODUCT_CANON_N656U }, 0 }, + + /* Kye */ + {{ USB_VENDOR_KYE, USB_PRODUCT_KYE_VIVIDPRO }, 0 }, + + /* HP */ + {{ USB_VENDOR_HP, USB_PRODUCT_HP_3300C }, 0 }, + {{ USB_VENDOR_HP, USB_PRODUCT_HP_3400CSE }, 0 }, + {{ USB_VENDOR_HP, USB_PRODUCT_HP_4100C }, 0 }, + {{ USB_VENDOR_HP, USB_PRODUCT_HP_4200C }, 0 }, + {{ USB_VENDOR_HP, USB_PRODUCT_HP_4300C }, 0 }, + {{ USB_VENDOR_HP, USB_PRODUCT_HP_S20 }, 0 }, + {{ USB_VENDOR_HP, USB_PRODUCT_HP_5200C }, 0 }, #if 0 - { USB_VENDOR_HP, USB_PRODUCT_HP_5300C }, + /* Handled by usscanner */ + {{ USB_VENDOR_HP, USB_PRODUCT_HP_5300C }, 0 }, #endif - { USB_VENDOR_HP, USB_PRODUCT_HP_6200C }, - { USB_VENDOR_HP, USB_PRODUCT_HP_6300C }, - - /* Avision */ - { USB_VENDOR_AVISION, USB_PRODUCT_AVISION_1200U }, + {{ USB_VENDOR_HP, USB_PRODUCT_HP_6200C }, 0 }, + {{ USB_VENDOR_HP, USB_PRODUCT_HP_6300C }, 0 }, #if 0 - /* Microtek */ - { USB_VENDOR_SCANLOGIC, USB_PRODUCT_SCANLOGIC_336CX }, - { USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_X6U }, - { USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_336CX }, - { USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_336CX2 }, - { USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_C6 }, - { USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6USL }, - { USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6USL2 }, - { USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6UL }, + /* XXX Should be handled by usscanner */ + /* Microtek */ + {{ USB_VENDOR_SCANLOGIC, USB_PRODUCT_SCANLOGIC_336CX }, 0 }, + {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_X6U }, 0 }, + {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_336CX }, 0 }, + {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_336CX2 }, 0 }, + {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_C6 }, 0 }, + {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6USL }, 0 }, + {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6USL2 }, 0 }, + {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6UL }, 0 }, #endif - /* Mustek */ - { USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200CU }, - { USB_VENDOR_NATIONAL, USB_PRODUCT_NATIONAL_BEARPAW1200 }, - { USB_VENDOR_NATIONAL, USB_PRODUCT_MUSTEK_600CU }, - { USB_VENDOR_NATIONAL, USB_PRODUCT_MUSTEK_1200USB }, - { USB_VENDOR_NATIONAL, USB_PRODUCT_MUSTEK_1200UB }, - - /* Primax */ - { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2X300 }, - { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E300 }, - { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2300 }, - { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E3002 }, - { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_9600 }, - { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_600U }, - { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_19200 }, - { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_1200U }, - { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G600 }, - { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_636I }, - { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2600 }, - { USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E600 }, - - /* Epson */ - { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_636 }, - { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_610 }, - { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1200 }, - { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1240 }, - { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1600 }, - { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1640 }, - { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_640U }, - { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1650 }, - - /* UMAX */ - { USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA1220U }, - { USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA1236U }, - { USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2000U }, - { USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2200U }, - { USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA3400 }, - - /* Visioneer */ - { USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_5300 }, - { USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_7600 }, - { USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_6100 }, - { USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_6200 }, - { USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_8100 }, - { USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_8600 }, - - /* Canon */ - { USB_VENDOR_CANON, USB_PRODUCT_CANON_N656U }, - - { 0, 0 } + /* Mustek */ + {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200CU }, 0 }, + {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_BEARPAW1200F }, 0 }, + {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_600USB }, 0 }, + {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_600CU }, 0 }, + {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200USB }, 0 }, + {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200UB }, 0 }, + {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200USBPLUS }, 0 }, + {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200CUPLUS }, 0 }, + + /* National */ + {{ USB_VENDOR_NATIONAL, USB_PRODUCT_NATIONAL_BEARPAW1200 }, 0 }, + {{ USB_VENDOR_NATIONAL, USB_PRODUCT_NATIONAL_BEARPAW2400 }, 0 }, + + /* Primax */ + {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2X300 }, 0 }, + {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E300 }, 0 }, + {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2300 }, 0 }, + {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E3002 }, 0 }, + {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_9600 }, 0 }, + {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_600U }, 0 }, + {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_19200 }, 0 }, + {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_1200U }, 0 }, + {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G600 }, 0 }, + {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_636I }, 0 }, + {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2600 }, 0 }, + {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E600 }, 0 }, + + /* Epson */ + {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_636 }, 0 }, + {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_610 }, 0 }, + {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1200 }, 0 }, + {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1240 }, 0 }, + {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1600 }, 0 }, + {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1640 }, 0 }, + {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_640U }, 0 }, + {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1650 }, 0 }, + {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_GT9700F }, USC_KEEP_OPEN }, + + /* UMAX */ + {{ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA1220U }, 0 }, + {{ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA1236U }, 0 }, + {{ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2000U }, 0 }, + {{ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2200U }, 0 }, + {{ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA3400 }, 0 }, + + /* Visioneer */ + {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_5300 }, 0 }, + {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_7600 }, 0 }, + {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_6100 }, 0 }, + {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_6200 }, 0 }, + {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_8100 }, 0 }, + {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_8600 }, 0 }, + + /* Ultima */ + {{ USB_VENDOR_ULTIMA, USB_PRODUCT_ULTIMA_1200UBPLUS }, 0 }, + }; +#define uscanner_lookup(v, p) ((const struct uscan_info *)usb_lookup(uscanner_devs, v, p)) #define USCANNER_BUFFERSIZE 1024 @@ -183,6 +210,8 @@ struct uscanner_softc { usbd_device_handle sc_udev; usbd_interface_handle sc_iface; + u_int sc_dev_flags; + usbd_pipe_handle sc_bulkin_pipe; int sc_bulkin; usbd_xfer_handle sc_bulkin_xfer; @@ -229,7 +258,9 @@ Static struct cdevsw uscanner_cdevsw = { /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, +#if !defined(__FreeBSD__) || (__FreeBSD__ < 5) /* bmaj */ -1 +#endif }; #endif @@ -244,19 +275,12 @@ USB_DECLARE_DRIVER(uscanner); USB_MATCH(uscanner) { USB_MATCH_START(uscanner, uaa); - int i; if (uaa->iface != NULL) return UMATCH_NONE; - for (i = 0; scanner_ids[i].vendor != 0; i++) { - if (scanner_ids[i].vendor == uaa->vendor && - scanner_ids[i].product == uaa->product) { - return (UMATCH_VENDOR_PRODUCT); - } - } - - return (UMATCH_NONE); + return (uscanner_lookup(uaa->vendor, uaa->product) != NULL ? + UMATCH_VENDOR_PRODUCT : UMATCH_NONE); } USB_ATTACH(uscanner) @@ -272,6 +296,8 @@ USB_ATTACH(uscanner) USB_ATTACH_SETUP; printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo); + sc->sc_dev_flags = uscanner_lookup(uaa->vendor, uaa->product)->flags; + sc->sc_udev = uaa->device; err = usbd_set_config_no(uaa->device, 1, 1); /* XXX */ @@ -335,11 +361,7 @@ USB_ATTACH(uscanner) } int -uscanneropen(dev, flag, mode, p) - dev_t dev; - int flag; - int mode; - struct proc *p; +uscanneropen(dev_t dev, int flag, int mode, usb_proc_ptr p) { struct uscanner_softc *sc; int unit = USCANNERUNIT(dev); @@ -366,21 +388,25 @@ uscanneropen(dev, flag, mode, p) sc->sc_bulkout_bufferlen = USCANNER_BUFFERSIZE; /* We have decided on which endpoints to use, now open the pipes */ - err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin, - USBD_EXCLUSIVE_USE, &sc->sc_bulkin_pipe); - if (err) { - printf("%s: cannot open bulk-in pipe (addr %d)\n", - USBDEVNAME(sc->sc_dev), sc->sc_bulkin); - uscanner_do_close(sc); - return (EIO); + if (sc->sc_bulkin_pipe == NULL) { + err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin, + USBD_EXCLUSIVE_USE, &sc->sc_bulkin_pipe); + if (err) { + printf("%s: cannot open bulk-in pipe (addr %d)\n", + USBDEVNAME(sc->sc_dev), sc->sc_bulkin); + uscanner_do_close(sc); + return (EIO); + } } - err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout, - USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe); - if (err) { - printf("%s: cannot open bulk-out pipe (addr %d)\n", - USBDEVNAME(sc->sc_dev), sc->sc_bulkout); - uscanner_do_close(sc); - return (EIO); + if (sc->sc_bulkout_pipe == NULL) { + err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout, + USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe); + if (err) { + printf("%s: cannot open bulk-out pipe (addr %d)\n", + USBDEVNAME(sc->sc_dev), sc->sc_bulkout); + uscanner_do_close(sc); + return (EIO); + } } sc->sc_bulkin_xfer = usbd_alloc_xfer(sc->sc_udev); @@ -398,11 +424,7 @@ uscanneropen(dev, flag, mode, p) } int -uscannerclose(dev, flag, mode, p) - dev_t dev; - int flag; - int mode; - struct proc *p; +uscannerclose(dev_t dev, int flag, int mode, usb_proc_ptr p) { struct uscanner_softc *sc; @@ -435,15 +457,17 @@ uscanner_do_close(struct uscanner_softc *sc) sc->sc_bulkout_xfer = NULL; } - if (sc->sc_bulkin_pipe) { - usbd_abort_pipe(sc->sc_bulkin_pipe); - usbd_close_pipe(sc->sc_bulkin_pipe); - sc->sc_bulkin_pipe = NULL; - } - if (sc->sc_bulkout_pipe) { - usbd_abort_pipe(sc->sc_bulkout_pipe); - usbd_close_pipe(sc->sc_bulkout_pipe); - sc->sc_bulkout_pipe = NULL; + if (!(sc->sc_dev_flags & USC_KEEP_OPEN)) { + if (sc->sc_bulkin_pipe != NULL) { + usbd_abort_pipe(sc->sc_bulkin_pipe); + usbd_close_pipe(sc->sc_bulkin_pipe); + sc->sc_bulkin_pipe = NULL; + } + if (sc->sc_bulkout_pipe != NULL) { + usbd_abort_pipe(sc->sc_bulkout_pipe); + usbd_close_pipe(sc->sc_bulkout_pipe); + sc->sc_bulkout_pipe = NULL; + } } if (sc->sc_bulkin_buffer) { @@ -459,10 +483,7 @@ uscanner_do_close(struct uscanner_softc *sc) } Static int -uscanner_do_read(sc, uio, flag) - struct uscanner_softc *sc; - struct uio *uio; - int flag; +uscanner_do_read(struct uscanner_softc *sc, struct uio *uio, int flag) { u_int32_t n, tn; usbd_status err; @@ -481,7 +502,7 @@ uscanner_do_read(sc, uio, flag) sc->sc_bulkin_xfer, sc->sc_bulkin_pipe, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, sc->sc_bulkin_buffer, &tn, - "uscannerrb"); + "uscnrb"); if (err) { if (err == USBD_INTERRUPTED) error = EINTR; @@ -501,10 +522,7 @@ uscanner_do_read(sc, uio, flag) } int -uscannerread(dev, uio, flag) - dev_t dev; - struct uio *uio; - int flag; +uscannerread(dev_t dev, struct uio *uio, int flag) { struct uscanner_softc *sc; int error; @@ -520,10 +538,7 @@ uscannerread(dev, uio, flag) } Static int -uscanner_do_write(sc, uio, flag) - struct uscanner_softc *sc; - struct uio *uio; - int flag; +uscanner_do_write(struct uscanner_softc *sc, struct uio *uio, int flag) { u_int32_t n; int error = 0; @@ -543,7 +558,7 @@ uscanner_do_write(sc, uio, flag) sc->sc_bulkout_xfer, sc->sc_bulkout_pipe, 0, USBD_NO_TIMEOUT, sc->sc_bulkout_buffer, &n, - "uscannerwb"); + "uscnwb"); if (err) { if (err == USBD_INTERRUPTED) error = EINTR; @@ -557,10 +572,7 @@ uscanner_do_write(sc, uio, flag) } int -uscannerwrite(dev, uio, flag) - dev_t dev; - struct uio *uio; - int flag; +uscannerwrite(dev_t dev, struct uio *uio, int flag) { struct uscanner_softc *sc; int error; @@ -576,9 +588,7 @@ uscannerwrite(dev, uio, flag) #if defined(__NetBSD__) || defined(__OpenBSD__) int -uscanner_activate(self, act) - device_ptr_t self; - enum devact act; +uscanner_activate(device_ptr_t self, enum devact act) { struct uscanner_softc *sc = (struct uscanner_softc *)self; @@ -613,11 +623,12 @@ USB_DETACH(uscanner) #endif sc->sc_dying = 1; + sc->sc_dev_flags = 0; /* make close really close device */ /* Abort all pipes. Causes processes waiting for transfer to wake. */ - if (sc->sc_bulkin_pipe) + if (sc->sc_bulkin_pipe != NULL) usbd_abort_pipe(sc->sc_bulkin_pipe); - if (sc->sc_bulkout_pipe) + if (sc->sc_bulkout_pipe != NULL) usbd_abort_pipe(sc->sc_bulkout_pipe); s = splusb(); @@ -652,10 +663,7 @@ USB_DETACH(uscanner) } int -uscannerpoll(dev, events, p) - dev_t dev; - int events; - struct proc *p; +uscannerpoll(dev_t dev, int events, usb_proc_ptr p) { struct uscanner_softc *sc; int revents = 0; @@ -677,7 +685,7 @@ uscannerpoll(dev, events, p) } int -uscannerioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) +uscannerioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p) { return (EINVAL); } diff --git a/sys/dev/usb/usscanner.c b/sys/dev/usb/usscanner.c index 68c1b7921f5..1d7a368d709 100644 --- a/sys/dev/usb/usscanner.c +++ b/sys/dev/usb/usscanner.c @@ -1,4 +1,4 @@ -/* $OpenBSD: usscanner.c,v 1.3 2001/05/03 02:20:35 aaron Exp $ */ +/* $OpenBSD: usscanner.c,v 1.4 2002/05/07 18:08:05 nate Exp $ */ /* $NetBSD: usscanner.c,v 1.6 2001/01/23 14:04:14 augustss Exp $ */ /* @@ -89,6 +89,22 @@ int usscannerdebug = 0; #define DPRINTFN(n,x) #endif +#define XS_CTL_DATA_IN SCSI_DATA_IN +#define XS_CTL_DATA_OUT SCSI_DATA_OUT +#define scsipi_adapter scsi_adapter +#define scsipi_cmd scsi_cmd +#define scsipi_device scsi_device +#define scsipi_done scsi_done +#define scsipi_link scsi_link +#define scsipi_minphys scsi_minphys +#define scsipi_sense scsi_sense +#define scsipi_xfer scsi_xfer +#define show_scsipi_xs show_scsi_xs +#define show_scsipi_cmd show_scsi_cmd +#define xs_control flags +#define xs_status status +#define XS_STS_DONE ITSDONE +#define XS_CTL_POLL SCSI_POLL #define USSCANNER_CONFIG_NO 1 #define USSCANNER_IFACE_IDX 0 diff --git a/sys/dev/usb/uvisor.c b/sys/dev/usb/uvisor.c index a0a45c54ddd..e44687355f9 100644 --- a/sys/dev/usb/uvisor.c +++ b/sys/dev/usb/uvisor.c @@ -1,5 +1,5 @@ -/* $OpenBSD: uvisor.c,v 1.3 2001/05/03 02:20:35 aaron Exp $ */ -/* $NetBSD: uvisor.c,v 1.11 2001/01/23 21:56:17 augustss Exp $ */ +/* $OpenBSD: uvisor.c,v 1.4 2002/05/07 18:08:05 nate Exp $ */ +/* $NetBSD: uvisor.c,v 1.14 2002/02/27 23:00:03 augustss Exp $ */ /* * Copyright (c) 2000 The NetBSD Foundation, Inc. @@ -104,7 +104,6 @@ struct uvisor_connection_info { }; #define UVISOR_CONNECTION_INFO_SIZE 18 - /* struct uvisor_connection_info.connection[x].port_function_id defines: */ #define UVISOR_FUNCTION_GENERIC 0x00 #define UVISOR_FUNCTION_DEBUGGER 0x01 @@ -112,6 +111,12 @@ struct uvisor_connection_info { #define UVISOR_FUNCTION_CONSOLE 0x03 #define UVISOR_FUNCTION_REMOTE_FILE_SYS 0x04 +/* + * Unknown PalmOS stuff. + */ +#define UVISOR_GET_PALM_INFORMATION 0x04 +#define UVISOR_GET_PALM_INFORMATION_LEN 0x14 + #define UVISORIBUFSIZE 1024 #define UVISOROBUFSIZE 1024 @@ -124,6 +129,8 @@ struct uvisor_softc { device_ptr_t sc_subdevs[UVISOR_MAX_CONN]; int sc_numcon; + u_int16_t sc_flags; + u_char sc_dying; }; @@ -144,6 +151,21 @@ struct ucom_methods uvisor_methods = { NULL, }; +struct uvisor_type { + struct usb_devno uv_dev; + u_int16_t uv_flags; +#define PALM4 0x0001 +}; +static const struct uvisor_type uvisor_devs[] = { + {{ USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_VISOR }, 0 }, + {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_M500 }, PALM4 }, + {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_M505 }, PALM4 }, + {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_M125 }, PALM4 }, + {{ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_40 }, PALM4 }, +/* {{ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_25 }, PALM4 },*/ +}; +#define uvisor_lookup(v, p) ((struct uvisor_type *)usb_lookup(uvisor_devs, v, p)) + USB_DECLARE_DRIVER(uvisor); USB_MATCH(uvisor) @@ -156,11 +178,8 @@ USB_MATCH(uvisor) DPRINTFN(20,("uvisor: vendor=0x%x, product=0x%x\n", uaa->vendor, uaa->product)); - if (uaa->vendor == USB_VENDOR_HANDSPRING && - uaa->product == USB_PRODUCT_HANDSPRING_VISOR) - return (UMATCH_VENDOR_PRODUCT); - - return (UMATCH_NONE); + return (uvisor_lookup(uaa->vendor, uaa->product) != NULL ? + UMATCH_VENDOR_PRODUCT : UMATCH_NONE); } USB_ATTACH(uvisor) @@ -198,6 +217,8 @@ USB_ATTACH(uvisor) USB_ATTACH_SETUP; printf("%s: %s\n", devname, devinfo); + sc->sc_flags = uvisor_lookup(uaa->vendor, uaa->product)->uv_flags; + id = usbd_get_interface_descriptor(iface); sc->sc_udev = dev; @@ -330,6 +351,7 @@ uvisor_init(struct uvisor_softc *sc, struct uvisor_connection_info *ci) usb_device_request_t req; int actlen; uWord avail; + char buffer[256]; DPRINTF(("uvisor_init: getting connection info\n")); req.bmRequestType = UT_READ_VENDOR_ENDPOINT; @@ -338,10 +360,30 @@ uvisor_init(struct uvisor_softc *sc, struct uvisor_connection_info *ci) USETW(req.wIndex, 0); USETW(req.wLength, UVISOR_CONNECTION_INFO_SIZE); err = usbd_do_request_flags(sc->sc_udev, &req, ci, - USBD_SHORT_XFER_OK, &actlen); + USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT); if (err) return (err); + if (sc->sc_flags & PALM4) { + /* Palm OS 4.0 Hack */ + req.bmRequestType = UT_READ_VENDOR_ENDPOINT; + req.bRequest = UVISOR_GET_PALM_INFORMATION; + USETW(req.wValue, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, UVISOR_GET_PALM_INFORMATION_LEN); + err = usbd_do_request(sc->sc_udev, &req, buffer); + if (err) + return (err); + req.bmRequestType = UT_READ_VENDOR_ENDPOINT; + req.bRequest = UVISOR_GET_PALM_INFORMATION; + USETW(req.wValue, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, UVISOR_GET_PALM_INFORMATION_LEN); + err = usbd_do_request(sc->sc_udev, &req, buffer); + if (err) + return (err); + } + DPRINTF(("uvisor_init: getting available bytes\n")); req.bmRequestType = UT_READ_VENDOR_ENDPOINT; req.bRequest = UVISOR_REQUEST_BYTES_AVAILABLE; @@ -374,5 +416,5 @@ uvisor_close(void *addr, int portno) USETW(req.wIndex, 0); USETW(req.wLength, UVISOR_CONNECTION_INFO_SIZE); (void)usbd_do_request_flags(sc->sc_udev, &req, &coninfo, - USBD_SHORT_XFER_OK, &actlen); + USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT); } |