diff options
author | n_hibma <n_hibma@FreeBSD.org> | 1998-11-26 23:13:13 +0000 |
---|---|---|
committer | n_hibma <n_hibma@FreeBSD.org> | 1998-11-26 23:13:13 +0000 |
commit | 1f1ab4819c23e37e12cd77a9f862f0b56a026bd6 (patch) | |
tree | 34b8718ca7c243ba6fca181b98eb980de3d02aca /sys | |
parent | cb434691d915a69b39259cd29b94ec59672a7ca3 (diff) | |
download | FreeBSD-src-1f1ab4819c23e37e12cd77a9f862f0b56a026bd6.zip FreeBSD-src-1f1ab4819c23e37e12cd77a9f862f0b56a026bd6.tar.gz |
Initial commit of ported NetBSD USB stack
Diffstat (limited to 'sys')
35 files changed, 12155 insertions, 3 deletions
diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC index 930349a..f2c58b6 100644 --- a/sys/amd64/conf/GENERIC +++ b/sys/amd64/conf/GENERIC @@ -11,7 +11,7 @@ # device lines is present in the ./LINT configuration file. If you are # in doubt as to the purpose or necessity of a line, check first in LINT. # -# $Id: GENERIC,v 1.130 1998/11/03 22:01:21 des Exp $ +# $Id: GENERIC,v 1.131 1998/11/12 11:29:28 obrien Exp $ machine "i386" cpu "I386_CPU" @@ -179,3 +179,22 @@ options SYSVSHM # option. The number of devices determines the maximum number of # simultaneous BPF clients programs runnable. #pseudo-device bpfilter 4 #Berkeley packet filter + + +# USB support +#controller uhci0 +#controller usb0 +# +# for the moment we have to specify the priorities of the device +# drivers explicitly by the ordering in the list below. This will +# be changed in the future. +# +#device ums0 +#device ukbd0 +#device ulpt0 +#device uhub0 +#device hid0 +#device ugen0 +# +#options USB_DEBUG +#options USBVERBOSE diff --git a/sys/conf/files b/sys/conf/files index 2f95d46..7d18ce7 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -599,3 +599,34 @@ vm/vm_swap.c standard vm/vm_unix.c standard vm/vnode_pager.c standard vm/vm_zone.c standard +# +# USB support +dev/pci/uhci_pci.c optional uhci device-driver +usb_if.o optional uhci device-driver \ + dependency "usb_if.c" \ + compile-with "${NORMAL_C}" \ + no-implicit-rule local +usb_if.c optional uhci device-driver \ + dependency "$S/kern/makedevops.pl $S/dev/usb/usb_if.m" \ + compile-with "perl5 $S/kern/makedevops.pl -c $S/dev/usb/usb_if.m" \ + no-obj no-implicit-rule before-depend local \ + clean "usb_if.c" +usb_if.h optional uhci device-driver \ + dependency "$S/kern/makedevops.pl $S/dev/usb/usb_if.m" \ + compile-with "perl5 $S/kern/makedevops.pl -h $S/dev/usb/usb_if.m" \ + no-obj no-implicit-rule before-depend \ + clean "usb_if.h" +dev/usb/uhci.c optional uhci device-driver +dev/usb/usb.c optional usb device-driver +dev/usb/usbdi.c optional usb device-driver +dev/usb/usbdi_util.c optional usb device-driver +#dev/usb/usb_mem.c optional usb device-driver +dev/usb/usb_subr.c optional usb device-driver +dev/usb/usb_quirks.c optional usb device-driver +dev/usb/hid.c optional usb device-driver +dev/usb/uhub.c optional uhub device-driver +dev/usb/ukbd.c optional ukbd device-driver +dev/usb/ulpt.c optional ulpt device-driver +dev/usb/ums.c optional ums device-driver +dev/usb/ugen.c optional ugen device-driver +#dev/usb/uhid.c optional hid device-driver diff --git a/sys/conf/options b/sys/conf/options index 0094484..8cbd96f 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -1,4 +1,4 @@ -# $Id: options,v 1.107 1998/11/05 14:28:17 dg Exp $ +# $Id: options,v 1.108 1998/11/23 09:58:59 phk Exp $ # # On the handling of kernel options # @@ -305,3 +305,7 @@ SIMOS opt_simos.h # options for bus/device framework BUS_DEBUG opt_bus.h + +# options for USB support +USB_DEBUG opt_usb.h +USBVERBOSE opt_usb.h diff --git a/sys/dev/pci/uhci_pci.c b/sys/dev/pci/uhci_pci.c new file mode 100644 index 0000000..71bc228 --- /dev/null +++ b/sys/dev/pci/uhci_pci.c @@ -0,0 +1,338 @@ +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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 <dev/usb/usb_port.h> + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#if defined(__FreeBSD__) +#include <sys/module.h> +#include <sys/bus.h> +#endif +#include <sys/device.h> +#include <sys/proc.h> +#include <sys/queue.h> + +#if defined(__NetBSD__) +#include <machine/bus.h> +#include <dev/pci/pcivar.h> +#elif defined(__FreeBSD__) +#include <pci/pcivar.h> +#include <pci/pcireg.h> + +#define PCI_CLASS_SERIALBUS 0x0c000000 +#define PCI_SUBCLASS_COMMUNICATIONS_SERIAL 0x00000000 +#define PCI_SUBCLASS_SERIALBUS_FIREWIRE 0x00000000 +#define PCI_SUBCLASS_SERIALBUS_ACCESS 0x00010000 +#define PCI_SUBCLASS_SERIALBUS_SSA 0x00020000 +#define PCI_SUBCLASS_SERIALBUS_USB 0x00030000 +#define PCI_SUBCLASS_SERIALBUS_FIBER 0x00040000 +#endif + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdivar.h> +#include <dev/usb/usb_mem.h> + +#include <dev/usb/uhcireg.h> +#include <dev/usb/uhcivar.h> + +#if defined(__NetBSD__) +int uhci_pci_match __P((struct device *, struct cfdata *, void *)); +void uhci_pci_attach __P((struct device *, struct device *, void *)); + +struct cfattach uhci_pci_ca = { + sizeof(uhci_softc_t), uhci_pci_match, uhci_pci_attach +}; + +#elif defined(__FreeBSD__) + +#define PCI_INTERFACE_MASK 0x0000ff00 +#define PCI_INTERFACE_SHIFT 8 +#define PCI_INTERFACE(d) (((d)>>8)&PCI_INTERFACE_MASK) +#define PCI_SUBCLASS(d) ((d)&PCI_SUBCLASS_MASK) +#define PCI_CLASS(d) ((d)&PCI_CLASS_MASK) + +#define PCI_VENDOR(d) ((d)&0xffff) +#define PCI_DEVICE(d) (((d)>>8)&0xffff) + +#define PCI_UHCI_DEVICEID_PIIX3 0x70208086ul +#define PCI_UHCI_DEVICEID_PIIX4 0x71128086ul +#define PCI_UHCI_DEVICEID_PIIX4E 0x71128086ul /* no separate step */ + +#define PCI_UHCI_BASE_REG 0x20 + +static char *uhci_pci_probe __P((pcici_t, pcidi_t)); +static void uhci_pci_attach __P((pcici_t, int)); + +u_long uhci_count = 0; /* global counter for nr. of devices found */ + +static struct pci_device uhci_pci_device = { + "uhci", + uhci_pci_probe, + uhci_pci_attach, + &uhci_count, + NULL +}; + +DATA_SET(pcidevice_set, uhci_pci_device); +#endif + + +#if defined(__NetBSD__) +int +uhci_pci_match(parent, match, aux) + struct device *parent; + struct cfdata *match; + void *aux; +{ + struct pci_attach_args *pa = (struct pci_attach_args *) aux; + + if (PCI_CLASS(pa->pa_class) == PCI_CLASS_SERIALBUS && + PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_SERIALBUS_USB && + PCI_INTERFACE(pa->pa_class) == PCI_INTERFACE_UHCI) + return 1; + + return 0; +} + +#elif defined(__FreeBSD__) +static char * +uhci_pci_probe(pcici_t config_id, pcidi_t device_id) +{ + u_int32_t class; + + if (device_id == PCI_UHCI_DEVICEID_PIIX3) + return ("Intel 82371SB USB Host Controller"); + else if (device_id == PCI_UHCI_DEVICEID_PIIX4) + return ("Intel 82371AB/EB USB Host Controller"); + else { + class = pci_conf_read(config_id, PCI_CLASS_REG); + if ( PCI_CLASS(class) == PCI_CLASS_SERIALBUS + && PCI_SUBCLASS(class) == PCI_SUBCLASS_SERIALBUS_USB + && PCI_INTERFACE(class) == PCI_INTERFACE_UHCI) { + return ("UHCI Host Controller"); + } + } + + return NULL; /* dunno... */ +} +#endif + + +#if defined(__NetBSD__) +void +uhci_pci_attach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + uhci_softc_t *sc = (uhci_softc_t *)self; + struct pci_attach_args *pa = (struct pci_attach_args *)aux; + pci_chipset_tag_t pc = pa->pa_pc; + char const *intrstr; + pci_intr_handle_t ih; + pcireg_t csr; + char *typestr, *vendor; + char devinfo[256]; + usbd_status r; + + pci_devinfo(pa->pa_id, pa->pa_class, 0, devinfo); + printf(": %s (rev. 0x%02x)\n", devinfo, PCI_REVISION(pa->pa_class)); + + /* Map I/O registers */ + if (pci_mapreg_map(pa, PCI_CBIO, PCI_MAPREG_TYPE_IO, 0, + &sc->iot, &sc->ioh, NULL, NULL)) { + printf("%s: can't map i/o space\n", sc->sc_bus.bdev.dv_xname); + return; + } + + sc->sc_dmatag = pa->pa_dmat; + + + /* Enable the device. */ + csr = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); + pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, + csr | PCI_COMMAND_MASTER_ENABLE); + + /* Map and establish the interrupt. */ + return EFAULT; + + if (pci_intr_map(pc, pa->pa_intrtag, pa->pa_intrpin, + pa->pa_intrline, &ih)) { + printf("%s: couldn't map interrupt\n", + sc->sc_bus.bdev.dv_xname); + return; + } + intrstr = pci_intr_string(pc, ih); + sc->sc_ih = pci_intr_establish(pc, ih, IPL_USB, uhci_intr, sc); + if (sc->sc_ih == NULL) { + printf("%s: couldn't establish interrupt", + sc->sc_bus.bdev.dv_xname); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + return; + } + printf("%s: interrupting at %s\n", sc->sc_bus.bdev.dv_xname, intrstr); + + switch(pci_conf_read(pc, pa->pa_tag, PCI_USBREV) & PCI_USBREV_MASK) { + case PCI_USBREV_PRE_1_0: + typestr = "pre 1.0"; + break; + case PCI_USBREV_1_0: + typestr = "1.0"; + break; + default: + typestr = "unknown"; + break; + } + printf("%s: USB version %s\n", sc->sc_bus.bdev.dv_xname, typestr); + + /* Figure out vendor for root hub descriptor. */ + vendor = pci_findvendor(pa->pa_id); + if (vendor) + strncpy(sc->sc_vendor, vendor, sizeof(sc->sc_vendor)); + else + sprintf(sc->sc_vendor, "vendor 0x%04x", PCI_VENDOR(pa->pa_id)); + + r = uhci_init(sc); + if (r != USBD_NORMAL_COMPLETION) { + printf("%s: init failed, error=%d\n", + sc->sc_bus.bdev.dv_xname, r); + return; + } + + /* Attach usb device. */ + config_found((void *)sc, &sc->sc_bus, usbctlprint); +} + + +#elif defined(__FreeBSD__) + +static void +uhci_pci_attach(config_id, unit) + pcici_t config_id; + int unit; +{ + int irq; + int id; + char *typestr; + char devinfo[256]; + usbd_status r; + uhci_softc_t *sc = NULL; + int legsup; + + sc = malloc(sizeof(uhci_softc_t), M_DEVBUF, M_NOWAIT); + if ( sc == NULL ) { + printf("usb%d: could not allocate memory", unit); + return; + } + memset(sc, 0, sizeof(uhci_softc_t)); + + sc->sc_iobase = pci_conf_read(config_id,PCI_UHCI_BASE_REG) & 0xffe0; + sc->sc_int = pci_conf_read(config_id,PCI_INTERRUPT_REG) & 0xff; + sc->unit = unit; + + if ( !pci_map_int(config_id, (pci_inthand_t *)uhci_intr, + (void *) sc, &bio_imask)) { + printf("usb%d: Unable to map irq\n", unit); + return; + } + + if (bootverbose) { + printf("usb%d: interrupting at %d\n", unit, sc->sc_int); + switch(pci_conf_read(config_id, PCI_USBREV) & PCI_USBREV_MASK) { + case PCI_USBREV_PRE_1_0: + typestr = "pre 1.0"; + break; + case PCI_USBREV_1_0: + typestr = "1.0"; + break; + default: + typestr = "unknown"; + break; + } + printf("usb%d: USB version %s\n", unit, typestr); + } + + /* Figure out vendor for root hub descriptor. */ + id = pci_conf_read(config_id, PCI_ID_REG); + if (PCI_VENDOR(id) == 0x8086) + sprintf(sc->sc_vendor, "Intel"); + else + sprintf(sc->sc_vendor, "Vendor 0x%04x", PCI_VENDOR(id)); + + r = uhci_init(sc); + if (r != USBD_NORMAL_COMPLETION) { + printf("usb%d: init failed, error=%d\n", unit, r); + return; + } + + + /* We add a child to the root bus. After PCI configuration + * has completed the root bus will start to probe and + * attach all the devices attached to it, including our new + * kid. + * + * FIXME Sometime in the future the UHCI controller itself will + * become a kid of PCI device and this device add will no longer + * be necessary. + * + * See README for an elaborate description of the bus + * structure in spe. + */ + sc->sc_bus.bdev = device_add_child(root_bus, "usb", unit, sc); + if (!sc->sc_bus.bdev) + DEVICE_ERROR(sc->sc_bus.bdev, + ("unable to add USB device to root bus\n")); + + id = pci_conf_read(config_id, PCI_ID_REG); + switch (id) { + case PCI_UHCI_DEVICEID_PIIX3: + device_set_desc(sc->sc_bus.bdev, "Intel 82371SB USB Host Controller"); + break; + case PCI_UHCI_DEVICEID_PIIX4: + device_set_desc(sc->sc_bus.bdev, "Intel 82371AB/EB USB Host Controller"); + break; + default: + device_set_desc(sc->sc_bus.bdev, "UHCI Host Controller"); + } + + return; +} +#endif diff --git a/sys/dev/usb/hid.c b/sys/dev/usb/hid.c new file mode 100644 index 0000000..519e6d6 --- /dev/null +++ b/sys/dev/usb/hid.c @@ -0,0 +1,469 @@ +/* $NetBSD: hid.c,v 1.2 1998/07/24 20:57:46 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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 <dev/usb/usb_port.h> + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbhid.h> + +#include <dev/usb/hid.h> + +#ifdef USB_DEBUG +#define DPRINTF(x) if (usbdebug) printf x +#define DPRINTFN(n,x) if (usbdebug>(n)) printf x +extern int usbdebug; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +static void hid_clear_local __P((struct hid_item *)); + +#define MAXUSAGE 100 +struct hid_data { + u_char *start; + u_char *end; + u_char *p; + struct hid_item cur; + int32_t usages[MAXUSAGE]; + int nu; + int minset; + int multi; + int multimax; + int kindset; +}; + +static void +hid_clear_local(c) + struct hid_item *c; +{ + c->usage = 0; + c->usage_minimum = 0; + c->usage_maximum = 0; + c->designator_index = 0; + c->designator_minimum = 0; + c->designator_maximum = 0; + c->string_index = 0; + c->string_minimum = 0; + c->string_maximum = 0; + c->set_delimiter = 0; +} + +struct hid_data * +hid_start_parse(d, len, kindset) + void *d; + int len; + int kindset; +{ + struct hid_data *s; + u_char *p = d; + + s = malloc(sizeof *s, M_TEMP, M_WAITOK); + memset(s, 0, sizeof *s); + s->start = s->p = p; + s->end = p + len; + s->kindset = kindset; + return (s); +} + +void +hid_end_parse(s) + struct hid_data *s; +{ + while (s->cur.next) { + struct hid_item *hi = s->cur.next->next; + free(s->cur.next, M_TEMP); + s->cur.next = hi; + } + free(s, M_TEMP); +} + +int +hid_get_item(s, h) + struct hid_data *s; + struct hid_item *h; +{ + struct hid_item *c = &s->cur; + int bTag, bType, bSize; + u_char *data; + int32_t dval; + u_char *p; + struct hid_item *hi; + int i; + + top: + if (s->multimax) { + 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; + return (1); + } else { + c->loc.count = s->multimax; + s->multimax = 0; + s->nu = 0; + hid_clear_local(c); + } + } + for (;;) { + p = s->p; + if (p >= s->end) + return (0); + + bSize = *p++; + if (bSize == 0xfe) { + /* long item */ + bSize = *p++; + bSize |= *p++ << 8; + bTag = *p++; + data = p; + p += bSize; + bType = 0xff; /* XXX what should it be */ + } else { + /* short item */ + bTag = bSize >> 4; + bType = (bSize >> 2) & 3; + bSize &= 3; + if (bSize == 3) bSize = 4; + data = p; + p += bSize; + } + s->p = p; + switch(bSize) { + case 0: + dval = 0; + break; + case 1: + dval = (int8_t)*data++; + break; + case 2: + dval = *data++; + dval |= *data++ << 8; + dval = (int16_t)dval; + break; + case 4: + dval = *data++; + dval |= *data++ << 8; + dval |= *data++ << 16; + dval |= *data++ << 24; + break; + default: + printf("BAD LENGTH %d\n", bSize); + continue; + } + + switch (bType) { + case 0: /* Main */ + switch (bTag) { + case 8: /* Input */ + if (!(s->kindset & (1 << hid_input))) + continue; + c->kind = hid_input; + c->flags = dval; + ret: + if (c->flags & HIO_VARIABLE) { + s->multimax = c->loc.count; + s->multi = 0; + c->loc.count = 1; + if (s->minset) { + for (i = c->usage_minimum; + i <= c->usage_maximum; + i++) { + s->usages[s->nu] = i; + if (s->nu < MAXUSAGE-1) + s->nu++; + } + s->minset = 0; + } + goto top; + } else { + *h = *c; + h->next = 0; + c->loc.pos += + c->loc.size * c->loc.count; + hid_clear_local(c); + s->minset = 0; + return (1); + } + case 9: /* Output */ + if (!(s->kindset & (1 << hid_output))) + continue; + c->kind = hid_output; + c->flags = dval; + goto ret; + case 10: /* Collection */ + c->kind = hid_collection; + c->collection = dval; + c->collevel++; + *h = *c; + hid_clear_local(c); + s->nu = 0; + return (1); + case 11: /* Feature */ + if (!(s->kindset & (1 << hid_feature))) + continue; + c->kind = hid_feature; + c->flags = dval; + goto ret; + case 12: /* End collection */ + c->kind = hid_endcollection; + c->collevel--; + *h = *c; + hid_clear_local(c); + s->nu = 0; + return (1); + default: + printf("Main bTag=%d\n", bTag); + break; + } + break; + case 1: /* Global */ + switch (bTag) { + case 0: + c->_usage_page = dval << 16; + break; + case 1: + c->logical_minimum = dval; + break; + case 2: + c->logical_maximum = dval; + break; + case 3: + c->physical_maximum = dval; + break; + case 4: + c->physical_maximum = dval; + break; + case 5: + c->unit_exponent = dval; + break; + case 6: + c->unit = dval; + break; + case 7: + c->loc.size = dval; + break; + case 8: + c->report_ID = dval; + break; + case 9: + c->loc.count = dval; + break; + case 10: /* Push */ + hi = malloc(sizeof *hi, M_TEMP, M_WAITOK); + *hi = s->cur; + c->next = hi; + break; + case 11: /* Pop */ + hi = c->next; + s->cur = *hi; + free(hi, M_TEMP); + break; + default: + printf("Global bTag=%d\n", bTag); + break; + } + break; + case 2: /* Local */ + switch (bTag) { + case 0: + if (bSize == 1) + dval = c->_usage_page | (dval&0xff); + else if (bSize == 2) + dval = c->_usage_page | (dval&0xffff); + c->usage = dval; + if (s->nu < MAXUSAGE) + s->usages[s->nu++] = dval; + /* else XXX */ + break; + case 1: + s->minset = 1; + if (bSize == 1) + dval = c->_usage_page | (dval&0xff); + else if (bSize == 2) + dval = c->_usage_page | (dval&0xffff); + c->usage_minimum = dval; + break; + case 2: + if (bSize == 1) + dval = c->_usage_page | (dval&0xff); + else if (bSize == 2) + dval = c->_usage_page | (dval&0xffff); + c->usage_maximum = dval; + break; + case 3: + c->designator_index = dval; + break; + case 4: + c->designator_minimum = dval; + break; + case 5: + c->designator_maximum = dval; + break; + case 7: + c->string_index = dval; + break; + case 8: + c->string_minimum = dval; + break; + case 9: + c->string_maximum = dval; + break; + case 10: + c->set_delimiter = dval; + break; + default: + printf("Local bTag=%d\n", bTag); + break; + } + break; + default: + printf("default bType=%d\n", bType); + break; + } + } +} + +int +hid_report_size(buf, len, k, idp) + void *buf; + int len; + enum hid_kind k; + u_int8_t *idp; +{ + struct hid_data *d; + struct hid_item h; + int size, id; + + id = 0; + for (d = hid_start_parse(buf, len, 1<<k); hid_get_item(d, &h); ) + if (h.report_ID) + id = h.report_ID; + hid_end_parse(d); + size = h.loc.pos; + if (id) { + size += 8; + *idp = id; /* XXX wrong */ + } else + *idp = 0; + return ((size + 7) / 8); +} + +int +hid_locate(desc, size, u, k, loc, flags) + void *desc; + int size; + u_int32_t u; + 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) { + if (loc) + *loc = h.loc; + if (flags) + *flags = h.flags; + hid_end_parse(d); + return (1); + } + } + hid_end_parse(d); + loc->size = 0; + return (0); +} + +u_long +hid_get_data(buf, loc) + u_char *buf; + struct hid_location *loc; +{ + u_int hpos = loc->pos; + u_int hsize = loc->size; + u_int32_t data; + int i, s; + + DPRINTFN(10, ("hid_get_data: loc %d/%d\n", hpos, hsize)); + + if (hsize == 0) + return (0); + + data = 0; + s = hpos / 8; + for (i = hpos; i < hpos+hsize; i += 8) + data |= buf[i / 8] << ((i / 8 - s) * 8); + data >>= hpos % 8; + data &= (1 << hsize) - 1; + hsize = 32 - hsize; + /* Sign extend */ + data = ((int32_t)data << hsize) >> hsize; + DPRINTFN(10, ("hid_get_data: loc %d/%d = %lu\n", + loc->pos, loc->size, (long)data)); + return (data); +} + +int +hid_is_collection(desc, size, usage) + void *desc; + int size; + u_int32_t usage; +{ + struct hid_data *hd; + struct hid_item hi; + int r; + + hd = hid_start_parse(desc, size, hid_input); + if (!hd) + return (0); + + r = hid_get_item(hd, &hi) && + hi.kind == hid_collection && + hi.usage == usage; + hid_end_parse(hd); + return (r); +} diff --git a/sys/dev/usb/hid.h b/sys/dev/usb/hid.h new file mode 100644 index 0000000..875a18eb --- /dev/null +++ b/sys/dev/usb/hid.h @@ -0,0 +1,89 @@ +/* $NetBSD: hid.h,v 1.2 1998/07/24 20:57:46 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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. + */ + +enum hid_kind { + hid_input, hid_output, hid_feature, hid_collection, hid_endcollection +}; + +struct hid_location { + u_int32_t size; + u_int32_t count; + u_int32_t pos; +}; + +struct hid_item { + /* Global */ + int32_t _usage_page; + int32_t logical_minimum; + int32_t logical_maximum; + int32_t physical_minimum; + int32_t physical_maximum; + int32_t unit_exponent; + int32_t unit; + int32_t report_ID; + /* Local */ + int32_t usage; + int32_t usage_minimum; + int32_t usage_maximum; + int32_t designator_index; + int32_t designator_minimum; + int32_t designator_maximum; + int32_t string_index; + int32_t string_minimum; + int32_t string_maximum; + int32_t set_delimiter; + /* Misc */ + int32_t collection; + int collevel; + enum hid_kind kind; + u_int32_t flags; + /* Location */ + struct hid_location loc; + /* */ + struct hid_item *next; +}; + +struct hid_data *hid_start_parse __P((void *d, int len, int kindset)); +void hid_end_parse __P((struct hid_data *s)); +int hid_get_item __P((struct hid_data *s, struct hid_item *h)); +int hid_report_size __P((void *buf, int len, enum hid_kind k, u_int8_t *id)); +int hid_locate __P((void *desc, int size, u_int32_t usage, + enum hid_kind kind, struct hid_location *loc, + u_int32_t *flags)); +u_long hid_get_data __P((u_char *buf, struct hid_location *loc)); +int hid_is_collection __P((void *desc, int size, u_int32_t usage)); diff --git a/sys/dev/usb/ohcireg.h b/sys/dev/usb/ohcireg.h new file mode 100644 index 0000000..3f62ddf --- /dev/null +++ b/sys/dev/usb/ohcireg.h @@ -0,0 +1,204 @@ +/* $NetBSD: ohcireg.h,v 1.2 1998/07/26 00:40:59 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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. + */ + +#ifndef _DEV_PCI_OHCIREG_H_ +#define _DEV_PCI_OHCIREG_H_ + +/*** PCI config registers ***/ + +#define PCI_CBMEM 0x10 /* configuration base memory */ + +#define PCI_INTERFACE_OHCI 0x10 + +/*** OHCI registers */ + +#define OHCI_REVISION 0x00 /* OHCI revision # */ +#define OHCI_REV_LO(rev) ((rev)&0xf) +#define OHCI_REV_HI(rev) (((rev)>>4)&0xf) +#define OHCI_REV_LEGACY(rev) ((rev) & 0x100) + +#define OHCI_CONTROL 0x04 +#define OHCI_CBSR_MASK 0x00000003 /* Control/Bulk Service Ratio */ +#define OHCI_RATIO_1_1 0x00000000 +#define OHCI_RATIO_1_2 0x00000001 +#define OHCI_RATIO_1_3 0x00000002 +#define OHCI_RATIO_1_4 0x00000003 +#define OHCI_PLE 0x00000004 /* Periodic List Enable */ +#define OHCI_IE 0x00000008 /* Isochronous Enable */ +#define OHCI_CLE 0x00000010 /* Control List Enable */ +#define OHCI_BLE 0x00000020 /* Bulk List Enable */ +#define OHCI_HCFS_MASK 0x000000c0 /* HostControllerFunctionalState */ +#define OHCI_HCFS_RESET 0x00000000 +#define OHCI_HCFS_RESUME 0x00000040 +#define OHCI_HCFS_OPERATIONAL 0x00000080 +#define OHCI_HCFS_SUSPEND 0x000000c0 +#define OHCI_IR 0x00000100 /* Interrupt Routing */ +#define OHCI_RWC 0x00000200 /* Remote Wakeup Connected */ +#define OHCI_RWE 0x00000400 /* Remote Wakeup Enabled */ +#define OHCI_COMMAND_STATUS 0x08 +#define OHCI_HCR 0x00000001 /* Host Controller Reset */ +#define OHCI_CLF 0x00000002 /* Control List Filled */ +#define OHCI_BLF 0x00000004 /* Bulk List Filled */ +#define OHCI_OCR 0x00000008 /* Ownership Change Request */ +#define OHCI_SOC_MASK 0x00030000 /* Scheduling Overrun Count */ +#define OHCI_INTERRUPT_STATUS 0x0c +#define OHCI_SO 0x00000001 /* Scheduling Overrun */ +#define OHCI_WDH 0x00000002 /* Writeback Done Head */ +#define OHCI_SF 0x00000004 /* Start of Frame */ +#define OHCI_RD 0x00000008 /* Resume Detected */ +#define OHCI_UE 0x00000010 /* Unrecoverable Error */ +#define OHCI_FNO 0x00000020 /* Frame Number Overflow */ +#define OHCI_RHSC 0x00000040 /* Root Hub Status Change */ +#define OHCI_OC 0x40000000 /* Ownership Change */ +#define OHCI_MIE 0x80000000 /* Master Interrupt Enable */ +#define OHCI_INTERRUPT_ENABLE 0x10 +#define OHCI_INTERRUPT_DISABLE 0x14 +#define OHCI_HCCA 0x18 +#define OHCI_PERIOD_CURRENT_ED 0x1c +#define OHCI_CONTROL_HEAD_ED 0x20 +#define OHCI_CONTROL_CURRENT_ED 0x24 +#define OHCI_BULK_HEAD_ED 0x28 +#define OHCI_BULK_CURRENT_ED 0x2c +#define OHCI_DONE_HEAD 0x30 +#define OHCI_FM_INTERVAL 0x34 +#define OHCI_GET_IVAL(s) ((s) & 0x3fff) +#define OHCI_GET_FSMPS(s) (((s) >> 16) & 0x7fff) +#define OHCI_FIT 0x80000000 +#define OHCI_FM_REMAINING 0x38 +#define OHCI_FM_NUMBER 0x3c +#define OHCI_PERIODIC_START 0x40 +#define OHCI_LS_THRESHOLD 0x44 +#define OHCI_RH_DESCRIPTOR_A 0x48 +#define OHCI_GET_NDP(s) ((s) & 0xff) +#define OHCI_PSM 0x0100 /* Power Switching Mode */ +#define OHCI_NPS 0x0200 /* No Power Switching */ +#define OHCI_GET_POTPGT(s) ((s) >> 24) +#define OHCI_RH_DESCRIPTOR_B 0x4c +#define OHCI_RH_STATUS 0x50 +#define OHCI_LPS 0x00000001 /* Local Power Status */ +#define OHCI_OCI 0x00000002 /* OverCurrent Indicator */ +#define OHCI_DRWE 0x00008000 /* Device Remote Wakeup Enable */ +#define OHCI_LPSC 0x00010000 /* Local Power Status Change */ +#define OHCI_CCIC 0x00020000 /* OverCurrent Indicator Change */ +#define OHCI_CRWE 0x80000000 /* Clear Remote Wakeup Enable */ +#define OHCI_RH_PORT_STATUS(n) (0x50 + (n)*4) /* 1 based indexing */ + +#define OHCI_LES (OHCI_PLE | OHCI_IE | OHCI_CLE | OHCI_BLE) +#define OHCI_ALL_INTRS (OHCI_SO | OHCI_WDH | OHCI_SF | OHCI_RD | OHCI_UE | OHCI_FNO | OHCI_RHSC | OHCI_OC) +#define OHCI_NORMAL_INTRS (OHCI_SO | OHCI_WDH | OHCI_RD | OHCI_UE | OHCI_RHSC) + +#define OHCI_FSMPS(i) (((i-210)*6/7) << 16) +#define OHCI_PERIODIC(i) ((i)*9/10) + +typedef u_int32_t ohci_physaddr_t; + +#define OHCI_NO_INTRS 32 +struct ohci_hcca { + ohci_physaddr_t hcca_interrupt_table[OHCI_NO_INTRS]; + u_int32_t hcca_frame_number; + ohci_physaddr_t hcca_done_head; +#define OHCI_DONE_INTRS 1 +}; +#define OHCI_HCCA_SIZE 256 +#define OHCI_HCCA_ALIGN 256 + +typedef struct { + u_int32_t ed_flags; +#define OHCI_ED_GET_FA(s) ((s) & 0x7f) +#define OHCI_ED_ADDRMASK 0x0000007f +#define OHCI_ED_SET_FA(s) (s) +#define OHCI_ED_GET_EN(s) (((s) >> 7) & 0xf) +#define OHCI_ED_SET_EN(s) ((s) << 7) +#define OHCI_ED_DIR_MASK 0x00001800 +#define OHCI_ED_DIR_TD 0x00000000 +#define OHCI_ED_DIR_OUT 0x00000800 +#define OHCI_ED_DIR_IN 0x00001000 +#define OHCI_ED_SPEED 0x00002000 +#define OHCI_ED_SKIP 0x00004000 +#define OHCI_ED_FORMAT_GEN 0x00000000 +#define OHCI_ED_FORMAT_ISO 0x00008000 +#define OHCI_ED_GET_MAXP(s) (((s) >> 16) & 0x07ff) +#define OHCI_ED_SET_MAXP(s) ((s) << 16) + ohci_physaddr_t ed_tailp; +#define OHCI_HALTED 0x00000002 +#define OHCI_TOGGLECARRY 0x00000001 +#define OHCI_TAILMASK 0xfffffffc + ohci_physaddr_t ed_headp; + ohci_physaddr_t ed_nexted; +} ohci_ed_t; +#define OHCI_ED_SIZE 16 +#define OHCI_ED_ALIGN 16 + +typedef struct { + u_int32_t td_flags; +#define OHCI_TD_R 0x00040000 /* Buffer Rounding */ +#define OHCI_TD_DP_MASK 0x00180000 /* Direction / PID */ +#define OHCI_TD_SETUP 0x00000000 +#define OHCI_TD_OUT 0x00080000 +#define OHCI_TD_IN 0x00100000 +#define OHCI_TD_GET_DI(x) (((x) >> 21) & 7) /* Delay Interrupt */ +#define OHCI_TD_SET_DI(x) ((x) << 21) +#define OHCI_TD_NOINTR 0x00e00000 +#define OHCI_TD_TOGGLE_CARRY 0x00000000 +#define OHCI_TD_TOGGLE_0 0x02000000 +#define OHCI_TD_TOGGLE_1 0x03000000 +#define OHCI_TD_GET_EC(x) (((x) >> 26) & 3) /* Error Count */ +#define OHCI_TD_GET_CC(x) ((x) >> 28) /* Condition Code */ +#define OHCI_TD_NOCC 0xf0000000 + ohci_physaddr_t td_cbp; /* Current Buffer Pointer */ + ohci_physaddr_t td_nexttd; /* Next TD */ + ohci_physaddr_t td_be; /* Buffer End */ +} ohci_td_t; +#define OHCI_TD_SIZE 16 +#define OHCI_TD_ALIGN 16 + +#define OHCI_CC_NO_ERROR 0 +#define OHCI_CC_CRC 1 +#define OHCI_CC_BIT_STUFFING 2 +#define OHCI_CC_DATA_TOGGLE_MISMATCH 3 +#define OHCI_CC_STALL 4 +#define OHCI_CC_DEVICE_NOT_RESPONDING 5 +#define OHCI_CC_PID_CHECK_FAILURE 6 +#define OHCI_CC_UNEXPECTED_PID 7 +#define OHCI_CC_DATA_OVERRUN 8 +#define OHCI_CC_DATA_UNDERRUN 9 +#define OHCI_CC_BUFFER_OVERRUN 12 +#define OHCI_CC_BUFFER_UNDERRUN 13 +#define OHCI_CC_NOT_ACCESSED 15 + +#endif /* _DEV_PCI_OHCIREG_H_ */ diff --git a/sys/dev/usb/ohcivar.h b/sys/dev/usb/ohcivar.h new file mode 100644 index 0000000..dfafdb9 --- /dev/null +++ b/sys/dev/usb/ohcivar.h @@ -0,0 +1,107 @@ +/* $NetBSD: ohcivar.h,v 1.2 1998/07/24 21:09:07 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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. + */ + +typedef struct ohci_soft_ed { + ohci_ed_t *ed; + struct ohci_soft_ed *next; + ohci_physaddr_t physaddr; +} ohci_soft_ed_t; +#define OHCI_ED_CHUNK 256 + +typedef struct ohci_soft_td { + ohci_td_t *td; + struct ohci_soft_td *nexttd; /* mirrors nexttd in TD */ + struct ohci_soft_td *dnext; /* next in done list */ + ohci_physaddr_t physaddr; + LIST_ENTRY(ohci_soft_td) hnext; + /*ohci_soft_ed_t *sed;*/ + usbd_request_handle reqh; + u_int16_t len; +} ohci_soft_td_t; +#define OHCI_TD_CHUNK 256 + +#define OHCI_NO_EDS (2*OHCI_NO_INTRS-1) + +#define OHCI_HASH_SIZE 128 + +typedef struct ohci_softc { + struct usbd_bus sc_bus; /* base device */ + void *sc_ih; /* interrupt vectoring */ + bus_space_tag_t iot; + bus_space_handle_t ioh; + + bus_dma_tag_t sc_dmatag; /* DMA tag */ + /* XXX should keep track of all DMA memory */ + + usb_dma_t sc_hccadma; + struct ohci_hcca *sc_hcca; + ohci_soft_ed_t *sc_eds[OHCI_NO_EDS]; + u_int sc_bws[OHCI_NO_INTRS]; + + u_int32_t sc_eintrs; + ohci_soft_ed_t *sc_ctrl_head; + ohci_soft_ed_t *sc_bulk_head; + + LIST_HEAD(, ohci_soft_td) sc_hash_tds[OHCI_HASH_SIZE]; + + int sc_noport; + u_int8_t sc_addr; /* device address */ + u_int8_t sc_conf; /* device configuration */ + + ohci_soft_ed_t *sc_freeeds; + ohci_soft_td_t *sc_freetds; + + usbd_request_handle sc_intrreqh; + + int sc_intrs; + char sc_vendor[16]; +} ohci_softc_t; + +usbd_status ohci_init __P((ohci_softc_t *)); +int ohci_intr __P((void *)); + +#define MS_TO_TICKS(ms) ((ms) * hz / 1000) + +#ifdef USB_DEBUG +#define DPRINTF(x) if (ohcidebug) printf x +#define DPRINTFN(n,x) if (ohcidebug>(n)) printf x +int ohcidebug; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif diff --git a/sys/dev/usb/queue.addendum.h b/sys/dev/usb/queue.addendum.h new file mode 100644 index 0000000..9252342 --- /dev/null +++ b/sys/dev/usb/queue.addendum.h @@ -0,0 +1,29 @@ +/* These definitions are taken from the NetBSD /sys/sys/queue.h file + * The copyright as in /sys/sys/queue.h from FreeBSD applies (they are the same) + */ + +/* This was called SIMPLEQ + */ +#ifndef STAILQ_HEAD_INITIALIZER +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } +#endif + +/* This one was called SIMPLEQ_REMOVE_HEAD but removes not only the + * head element, but a whole queue of elements from the head. + */ +#ifndef STAILQ_REMOVE_HEAD_QUEUE +#define STAILQ_REMOVE_HEAD_QUEUE(head, elm, field) do { \ + if (((head)->stqh_first = (elm)->field.stqe_next) == NULL) \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (0) +#endif + + +/* This is called LIST and was called like that as well in the NetBSD version + */ +#ifndef LIST_HEAD_INITIALIZER +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } +#endif + diff --git a/sys/dev/usb/ugen.c b/sys/dev/usb/ugen.c new file mode 100644 index 0000000..6bcaa08 --- /dev/null +++ b/sys/dev/usb/ugen.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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 <dev/usb/usb_port.h> + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#if defined(__NetBSD__) +#include <sys/device.h> +#elif defined(__FreeBSD__) +#include <sys/module.h> +#include <sys/bus.h> +#endif +#include <sys/uio.h> +#include <sys/conf.h> +#include <sys/syslog.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usbdivar.h> +#include <dev/usb/usbdevs.h> +#include <dev/usb/usb_quirks.h> + +#ifdef USB_DEBUG +#define DPRINTF(x) if (usbdebug) printf x +#define DPRINTFN(n,x) if (usbdebug>(n)) printf x +extern int usbdebug; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +struct ugen_softc { + bdevice sc_dev; + usbd_device_handle sc_udev; /* device */ + usbd_interface_handle sc_iface; /* interface */ + int sc_ifaceno; + int sc_bulk; +}; + +#if defined(__NetBSD__) +int ugen_match __P((struct device *, struct cfdata *, void *)); +void ugen_attach __P((struct device *, struct device *, void *)); + +extern struct cfdriver ugen_cd; + +struct cfattach ugen_ca = { + sizeof(struct ugen_softc), ugen_match, ugen_attach +}; +#elif defined(__FreeBSD__) +static device_probe_t ugen_match; +static device_attach_t ugen_attach; +static device_detach_t ugen_detach; + +static devclass_t ugen_devclass; + +static device_method_t ugen_methods[] = { + DEVMETHOD(device_probe, ugen_match), + DEVMETHOD(device_attach, ugen_attach), + DEVMETHOD(device_detach, ugen_detach), + {0,0} +}; + +static driver_t ugen_driver = { + "ugen", + ugen_methods, + DRIVER_TYPE_MISC, + sizeof(struct ugen_softc) +}; +#endif + + +#if defined(__NetBSD__) +int +ugen_match(parent, match, aux) + struct device *parent; + struct cfdata *match; + void *aux; +{ + struct usb_attach_arg *uaa = aux; +#elif defined(__FreeBSD__) +static int +ugen_match(device_t device) +{ + struct usb_attach_arg *uaa = device_get_ivars(device); +#endif + usb_interface_descriptor_t *id; + + DPRINTFN(10,("ugen_match\n")); + if (uaa->usegeneric) + return UMATCH_GENERIC; + else + return UMATCH_NONE; +} + +#if defined(__NetBSD__) +void +ugen_attach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct ugen_softc *sc = (struct ugen_softc *)self; + struct usb_attach_arg *uaa = aux; +#elif defined(__FreeBSD__) +static int +ugen_attach(device_t self) +{ + struct ugen_softc *sc = device_get_softc(self); + struct usb_attach_arg *uaa = device_get_ivars(self); +#endif + usbd_device_handle dev = uaa->device; + usb_device_descriptor_t *udd = &dev->ddesc; + char devinfo[1024]; + + usbd_devinfo(dev, 0, devinfo); +#if defined(__FreeBSD__) + device_set_desc(self, devinfo); + printf("%s%d", device_get_name(self), device_get_unit(self)); +#endif + printf(": %s (device class %d/%d)\n", devinfo, + udd->bDeviceClass, udd->bDeviceSubClass); + sc->sc_dev = self; + + ATTACH_SUCCESS_RETURN; +} + +#if defined(__FreeBSD__) +static int +ugen_detach(device_t self) +{ + /* we need to cast away the const returned by + * device_get_desc + */ + char *devinfo = (char *) device_get_desc(self); + + if (devinfo) { + device_set_desc(self, NULL); + free(devinfo, M_USB); + } + + return 0; +} + +DRIVER_MODULE(ugen, usb, ugen_driver, ugen_devclass, usb_driver_load, 0); +#endif diff --git a/sys/dev/usb/uhci.c b/sys/dev/usb/uhci.c new file mode 100644 index 0000000..5065195 --- /dev/null +++ b/sys/dev/usb/uhci.c @@ -0,0 +1,2355 @@ +/* $NetBSD: uhci.c,v 1.10 1998/08/02 22:30:52 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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. + */ + +/* + * USB Universal Host Controller driver. + * Handles PIIX3 and PIIX4. + * + * Data sheets: ftp://download.intel.com/design/intarch/datashts/29055002.pdf + * ftp://download.intel.com/design/intarch/datashts/29056201.pdf + * UHCI spec: http://www.intel.com/design/usb/uhci11d.pdf + * USB spec: http://www.teleport.com/cgi-bin/mailmerge.cgi/~usb/cgiform.tpl + */ + +#include <dev/usb/usb_port.h> + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#if defined(__NetBSD__) +#include <sys/device.h> +#elif defined(__FreeBSD__) +#include <sys/module.h> +#include <sys/bus.h> +#endif +#include <sys/proc.h> +#include <sys/queue.h> +#include <sys/select.h> + +#include <machine/bus.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/uhcireg.h> +#include <dev/usb/uhcivar.h> + +#if defined(__FreeBSD__) +#include <machine/clock.h> +#include "dev/usb/queue.addendum.h" + +#define delay(d) DELAY(d) + +static struct callout_handle uhci_timeout_handle + = CALLOUT_HANDLE_INITIALIZER(&uhci_timeout_handle); +static struct callout_handle uhci_timo_handle + = CALLOUT_HANDLE_INITIALIZER(&uhci_timo_handle); +#endif + +#define MS_TO_TICKS(ms) ((ms) * hz / 1000) + +struct uhci_pipe { + struct usbd_pipe pipe; + uhci_intr_info_t *iinfo; + int newtoggle; + /* Info needed for different pipe kinds. */ + union { + /* Control pipe */ + struct { + uhci_soft_qh_t *sqh; + usb_dma_t reqdma; + usb_dma_t datadma; + uhci_soft_td_t *setup, *stat, *xferend; + u_int length; + } ctl; + /* Interrupt pipe */ + struct { + usb_dma_t datadma; + int npoll; + uhci_soft_qh_t **qhs; + } intr; + /* Bulk pipe */ + struct { + uhci_soft_qh_t *sqh; + usb_dma_t datadma; + u_int length; + int isread; + } bulk; + } u; +}; + +/* + * The uhci_intr_info free list can be global since they contain + * no dma specific data. The other free lists do. + */ +LIST_HEAD(, uhci_intr_info) uhci_ii_free; + +void uhci_busreset __P((uhci_softc_t *)); +void uhci_run __P((uhci_softc_t *, int run)); +uhci_soft_td_t *uhci_alloc_std __P((uhci_softc_t *)); +void uhci_free_std __P((uhci_softc_t *, uhci_soft_td_t *)); +uhci_soft_qh_t *uhci_alloc_sqh __P((uhci_softc_t *)); +void uhci_free_sqh __P((uhci_softc_t *, uhci_soft_qh_t *)); +uhci_intr_info_t *uhci_alloc_intr_info __P((uhci_softc_t *)); +void uhci_free_intr_info __P((uhci_intr_info_t *ii)); +void uhci_enter_ctl_q __P((uhci_softc_t *, uhci_soft_qh_t *, + uhci_intr_info_t *)); +void uhci_exit_ctl_q __P((uhci_softc_t *, uhci_soft_qh_t *)); + +void uhci_free_std_chain __P((uhci_softc_t *, + uhci_soft_td_t *, uhci_soft_td_t *)); +usbd_status uhci_alloc_std_chain __P((struct uhci_pipe *, uhci_softc_t *, + int, int, usb_dma_t *, + uhci_soft_td_t **, + uhci_soft_td_t **)); +void uhci_timo __P((void *)); +void uhci_waitintr __P((uhci_softc_t *, usbd_request_handle)); +void uhci_check_intr __P((uhci_softc_t *, uhci_intr_info_t *)); +void uhci_ii_done __P((uhci_intr_info_t *, int)); +void uhci_timeout __P((void *)); +void uhci_wakeup_ctrl __P((void *, int, int, void *, int)); +void uhci_lock_frames __P((uhci_softc_t *)); +void uhci_unlock_frames __P((uhci_softc_t *)); +void uhci_add_ctrl __P((uhci_softc_t *, uhci_soft_qh_t *)); +void uhci_add_bulk __P((uhci_softc_t *, uhci_soft_qh_t *)); +void uhci_remove_ctrl __P((uhci_softc_t *, uhci_soft_qh_t *)); +void uhci_remove_bulk __P((uhci_softc_t *, uhci_soft_qh_t *)); +int uhci_str __P((usb_string_descriptor_t *, int, char *)); + +void uhci_device_close __P((struct uhci_pipe *)); + +void uhci_wakeup_cb __P((usbd_request_handle reqh)); + +usbd_status uhci_device_ctrl_transfer __P((usbd_request_handle)); +void uhci_device_ctrl_abort __P((usbd_request_handle)); +void uhci_device_ctrl_close __P((usbd_pipe_handle)); +usbd_status uhci_device_intr_transfer __P((usbd_request_handle)); +void uhci_device_intr_abort __P((usbd_request_handle)); +void uhci_device_intr_close __P((usbd_pipe_handle)); +usbd_status uhci_device_bulk_transfer __P((usbd_request_handle)); +void uhci_device_bulk_abort __P((usbd_request_handle)); +void uhci_device_bulk_close __P((usbd_pipe_handle)); + +usbd_status uhci_root_ctrl_transfer __P((usbd_request_handle)); +void uhci_root_ctrl_abort __P((usbd_request_handle)); +void uhci_root_ctrl_close __P((usbd_pipe_handle)); +usbd_status uhci_root_intr_transfer __P((usbd_request_handle)); +void uhci_root_intr_abort __P((usbd_request_handle)); +void uhci_root_intr_close __P((usbd_pipe_handle)); + +usbd_status uhci_open __P((usbd_pipe_handle)); +void uhci_poll __P((struct usbd_bus *)); + +usbd_status uhci_device_request __P((usbd_request_handle reqh)); +void uhci_ctrl_done __P((uhci_intr_info_t *ii)); +void uhci_bulk_done __P((uhci_intr_info_t *ii)); + +void uhci_add_intr __P((uhci_softc_t *, int, uhci_soft_qh_t *)); +void uhci_remove_intr __P((uhci_softc_t *, int, uhci_soft_qh_t *)); +usbd_status uhci_device_setintr __P((uhci_softc_t *sc, + struct uhci_pipe *pipe, int ival)); +void uhci_intr_done __P((uhci_intr_info_t *ii)); + +#ifdef USB_DEBUG +static void uhci_dumpregs __P((uhci_softc_t *)); +void uhci_dump_tds __P((uhci_soft_td_t *)); +void uhci_dump_qh __P((uhci_soft_qh_t *)); +void uhci_dump __P((void)); +void uhci_dump_td __P((uhci_soft_td_t *)); +#endif + +#if defined(__NetBSD__) +#define UWRITE2(sc, r, x) bus_space_write_2((sc)->iot, (sc)->ioh, (r), (x)) +#define UWRITE4(sc, r, x) bus_space_write_4((sc)->iot, (sc)->ioh, (r), (x)) +#define UREAD2(sc, r) bus_space_read_2((sc)->iot, (sc)->ioh, (r)) +#define UREAD4(sc, r) bus_space_read_4((sc)->iot, (sc)->ioh, (r)) +#elif defined(__FreeBSD__) +#define UWRITE2(sc,r,x) outw((sc)->sc_iobase + (r), (x)) +#define UWRITE4(sc,r,x) outl((sc)->sc_iobase + (r), (x)) +#define UREAD2(sc,r) inw((sc)->sc_iobase + (r)) +#define UREAD4(sc,r) inl((sc)->sc_iobase + (r)) +#endif + +#define UHCICMD(sc, cmd) UWRITE2(sc, UHCI_CMD, cmd) +#define UHCISTS(sc) UREAD2(sc, UHCI_STS) + +#define UHCI_RESET_TIMEOUT 100 /* reset timeout */ +#define UHCI_CTRL_TIMEOUT 500 /* control transaction timeout */ +#define UHCI_ISO_DELAY 50 /* delay of start of iso */ + +#define UHCI_CURFRAME(sc) (UREAD2(sc, UHCI_FRNUM) & UHCI_FRNUM_MASK) + +#define UHCI_INTR_ENDPT 1 + +struct usbd_methods uhci_root_ctrl_methods = { + uhci_root_ctrl_transfer, + uhci_root_ctrl_abort, + uhci_root_ctrl_close, + 0, +}; + +struct usbd_methods uhci_root_intr_methods = { + uhci_root_intr_transfer, + uhci_root_intr_abort, + uhci_root_intr_close, + 0, +}; + +struct usbd_methods uhci_device_ctrl_methods = { + uhci_device_ctrl_transfer, + uhci_device_ctrl_abort, + uhci_device_ctrl_close, + 0, +}; + +struct usbd_methods uhci_device_intr_methods = { + uhci_device_intr_transfer, + uhci_device_intr_abort, + uhci_device_intr_close, + 0, +}; + +struct usbd_methods uhci_device_bulk_methods = { + uhci_device_bulk_transfer, + uhci_device_bulk_abort, + uhci_device_bulk_close, + 0, +}; + +void +uhci_busreset(sc) + uhci_softc_t *sc; +{ + UHCICMD(sc, UHCI_CMD_GRESET); /* global reset */ + usbd_delay_ms(&sc->sc_bus, USB_RESET_DELAY); /* wait at least 10ms */ + UHCICMD(sc, 0); /* do nothing */ +} + +usbd_status +uhci_init(sc) + uhci_softc_t *sc; +{ + usbd_status r; + int i, j; + uhci_soft_qh_t *csqh, *bsqh, *sqh; + uhci_soft_td_t *std; + usb_dma_t dma; + static int uhci_global_init_done = 0; + + DPRINTFN(1,("uhci_init: start\n")); + + if (!uhci_global_init_done) { + uhci_global_init_done = 1; + LIST_INIT(&uhci_ii_free); + } + +#if defined(USB_DEBUG) + if (uhcidebug > 2) + uhci_dumpregs(sc); +#endif + + uhci_run(sc, 0); /* stop the controller */ + UWRITE2(sc, UHCI_INTR, 0); /* disable interrupts */ + + /* Allocate and initialize real frame array. */ + r = usb_allocmem(sc->sc_dmatag, + UHCI_FRAMELIST_COUNT * sizeof(uhci_physaddr_t), + UHCI_FRAMELIST_ALIGN, &dma); + if (r != USBD_NORMAL_COMPLETION) + return (r); + sc->sc_pframes = KERNADDR(&dma); + UWRITE2(sc, UHCI_FRNUM, 0); /* set frame number to 0 */ + UWRITE4(sc, UHCI_FLBASEADDR, DMAADDR(&dma)); /* set frame list */ + + uhci_busreset(sc); + + /* Allocate the dummy QH where bulk traffic will be queued. */ + bsqh = uhci_alloc_sqh(sc); + if (!bsqh) + return (USBD_NOMEM); + bsqh->qh->qh_hlink = UHCI_PTR_T; /* end of QH chain */ + bsqh->qh->qh_elink = UHCI_PTR_T; + sc->sc_bulk_start = sc->sc_bulk_end = bsqh; + + /* Allocate the dummy QH where control traffic will be queued. */ + csqh = uhci_alloc_sqh(sc); + if (!csqh) + return (USBD_NOMEM); + csqh->qh->hlink = bsqh; + csqh->qh->qh_hlink = bsqh->physaddr | UHCI_PTR_Q; + csqh->qh->qh_elink = UHCI_PTR_T; + sc->sc_ctl_start = sc->sc_ctl_end = csqh; + + /* + * 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. + */ + for(i = 0; i < UHCI_VFRAMELIST_COUNT; i++) { + std = uhci_alloc_std(sc); + sqh = uhci_alloc_sqh(sc); + if (!std || !sqh) + return (USBD_NOMEM); + std->td->link.sqh = sqh; + std->td->td_link = sqh->physaddr | UHCI_PTR_Q; + std->td->td_status = UHCI_TD_IOS; /* iso, inactive */ + std->td->td_token = 0; + std->td->td_buffer = 0; + sqh->qh->hlink = csqh; + sqh->qh->qh_hlink = csqh->physaddr | UHCI_PTR_Q; + sqh->qh->elink = 0; + sqh->qh->qh_elink = UHCI_PTR_T; + sc->sc_vframes[i].htd = std; + 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; + j += UHCI_VFRAMELIST_COUNT) + sc->sc_pframes[j] = std->physaddr; + } + + LIST_INIT(&sc->sc_intrhead); + + /* Set up the bus struct. */ + sc->sc_bus.open_pipe = uhci_open; + sc->sc_bus.pipe_size = sizeof(struct uhci_pipe); + sc->sc_bus.do_poll = uhci_poll; + + DPRINTFN(1,("uhci_init: enabling\n")); + UWRITE2(sc, UHCI_INTR, UHCI_INTR_TOCRCIE | UHCI_INTR_RIE | + UHCI_INTR_IOCE | UHCI_INTR_SPIE); /* enable interrupts */ + + uhci_run(sc, 1); /* and here we go... */ + return (USBD_NORMAL_COMPLETION); +} + +#ifdef USB_DEBUG +static void +uhci_dumpregs(sc) + uhci_softc_t *sc; +{ + DEVICE_MSG(sc->sc_bus.bdev,("regs: cmd=%04x, sts=%04x, intr=%04x, frnum=%04x, flbase=%08x, sof=%04x, portsc1=%04x, portsc2=%04x\n", + UREAD2(sc, UHCI_CMD), + UREAD2(sc, UHCI_STS), + UREAD2(sc, UHCI_INTR), + UREAD2(sc, UHCI_FRNUM), + UREAD2(sc, UHCI_FLBASEADDR), + UREAD2(sc, UHCI_SOF), + UREAD2(sc, UHCI_PORTSC1), + UREAD2(sc, UHCI_PORTSC2))); +} + +int uhci_longtd = 1; + +void +uhci_dump_td(p) + uhci_soft_td_t *p; +{ + printf("TD(%p) at %08lx = 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", + p, (long)p->physaddr, + (long)p->td->td_link, + (long)p->td->td_status, + (long)p->td->td_token, + (long)p->td->td_buffer); + if (uhci_longtd) +#if defined(__NetBSD__) + printf(" %b %b,errcnt=%d,actlen=%d pid=%02x,addr=%d,endpt=%d,D=%d,maxlen=%d\n", + (long)p->td->td_link, + "\20\1T\2Q\3VF", + (long)p->td->td_status, + "\20\22BITSTUFF\23CRCTO\24NAK\25BABBLE\26DBUFFER\27STALLED\30ACTIVE\31IOC\32ISO\33LS\36SPD", + UHCI_TD_GET_ERRCNT(p->td->td_status), + UHCI_TD_GET_ACTLEN(p->td->td_status), + UHCI_TD_GET_PID(p->td->td_token), + UHCI_TD_GET_DEVADDR(p->td->td_token), + UHCI_TD_GET_ENDPT(p->td->td_token), + UHCI_TD_GET_DT(p->td->td_token), + UHCI_TD_GET_MAXLEN(p->td->td_token)); +#elif defined(__FreeBSD__) + printf(" Link=0x%08lx,Status=0x%08lx\n errcnt=%d,actlen=%d,pid=%02x,addr=%d,endpt=%d,D=%d,maxlen=%d\n", + (long)p->td->td_link, + (long)p->td->td_status, + UHCI_TD_GET_ERRCNT(p->td->td_status), + UHCI_TD_GET_ACTLEN(p->td->td_status), + UHCI_TD_GET_PID(p->td->td_token), + UHCI_TD_GET_DEVADDR(p->td->td_token), + UHCI_TD_GET_ENDPT(p->td->td_token), + UHCI_TD_GET_DT(p->td->td_token), + UHCI_TD_GET_MAXLEN(p->td->td_token)); +#endif + +} + +void +uhci_dump_qh(p) + uhci_soft_qh_t *p; +{ + printf("QH(%p) at %08x: hlink=%08x elink=%08x\n", p, (int)p->physaddr, + p->qh->qh_hlink, p->qh->qh_elink); +} + + +#if 0 +void +uhci_dump() +{ + uhci_softc_t *sc = uhci; + + uhci_dumpregs(sc); + printf("intrs=%d\n", sc->sc_intrs); + printf("framelist[i].link = %08x\n", sc->sc_framelist[0].link); + uhci_dump_qh(sc->sc_ctl_start->qh->hlink); +} +#endif + +void +uhci_dump_tds(std) + uhci_soft_td_t *std; +{ + uhci_soft_td_t *p; + + for(p = std; p; p = p->td->link.std) + uhci_dump_td(p); +} +#endif + +/* + * This routine is executed periodically and simulates interrupts + * from the root controller interrupt pipe for port status change. + */ +void +uhci_timo(addr) + void *addr; +{ + usbd_request_handle reqh = addr; + usbd_pipe_handle pipe = reqh->pipe; + uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus; + struct uhci_pipe *upipe = (struct uhci_pipe *)pipe; + int s; + u_char *p; + + DPRINTFN(15, ("uhci_timo\n")); + + p = KERNADDR(&upipe->u.intr.datadma); + p[0] = 0; + if (UREAD2(sc, UHCI_PORTSC1) & (UHCI_PORTSC_CSC|UHCI_PORTSC_OCIC)) + p[0] |= 1<<1; + if (UREAD2(sc, UHCI_PORTSC2) & (UHCI_PORTSC_CSC|UHCI_PORTSC_OCIC)) + p[0] |= 1<<2; + if (p[0] != 0) { + reqh->actlen = 1; + reqh->status = USBD_NORMAL_COMPLETION; + s = splusb(); + reqh->xfercb(reqh); + splx(s); + } + if (reqh->pipe->intrreqh == reqh) { +#if defined(__NetBSD__) + timeout(uhci_timo, reqh, sc->sc_ival); +#elif defined(__FreeBSD__) + /* To avoid race conditions we first initialise the struct + * before we use it. The timeout might happen between the + * setting of the timeout and the setting of callout_handler + */ + callout_handle_init(&reqh->callout_handler); + reqh->callout_handler = timeout(uhci_timo, reqh, sc->sc_ival); +#endif + } else { + usb_freemem(sc->sc_dmatag, &upipe->u.intr.datadma); + } +} + + +void +uhci_lock_frames(sc) + uhci_softc_t *sc; +{ + int s = splusb(); + while (sc->sc_vflock) { + sc->sc_vflock |= UHCI_WANT_LOCK; + tsleep(&sc->sc_vflock, PRIBIO, "uhcqhl", 0); + } + sc->sc_vflock = UHCI_HAS_LOCK; + splx(s); +} + +void +uhci_unlock_frames(sc) + uhci_softc_t *sc; +{ + int s = splusb(); + sc->sc_vflock &= ~UHCI_HAS_LOCK; + if (sc->sc_vflock & UHCI_WANT_LOCK) + wakeup(&sc->sc_vflock); + splx(s); +} + +/* + * Allocate an interrupt information struct. A free list is kept + * for fast allocation. + */ +uhci_intr_info_t * +uhci_alloc_intr_info(sc) + uhci_softc_t *sc; +{ + uhci_intr_info_t *ii; + + ii = LIST_FIRST(&uhci_ii_free); + if (ii) + LIST_REMOVE(ii, list); + else { + ii = malloc(sizeof(uhci_intr_info_t), M_USBDEV, M_NOWAIT); + } + ii->sc = sc; + return ii; +} + +void +uhci_free_intr_info(ii) + uhci_intr_info_t *ii; +{ + LIST_INSERT_HEAD(&uhci_ii_free, ii, list); /* and put on free list */ +} + +/* Add control QH, called at splusb(). */ +void +uhci_add_ctrl(sc, sqh) + uhci_softc_t *sc; + uhci_soft_qh_t *sqh; +{ + uhci_qh_t *eqh; + + DPRINTFN(10, ("uhci_add_ctrl: sqh=%p\n", sqh)); + eqh = sc->sc_ctl_end->qh; + sqh->qh->hlink = eqh->hlink; + sqh->qh->qh_hlink = eqh->qh_hlink; + eqh->hlink = sqh; + eqh->qh_hlink = sqh->physaddr | UHCI_PTR_Q; + sc->sc_ctl_end = sqh; +} + +/* Remove control QH, called at splusb(). */ +void +uhci_remove_ctrl(sc, sqh) + uhci_softc_t *sc; + uhci_soft_qh_t *sqh; +{ + uhci_soft_qh_t *pqh; + + DPRINTFN(10, ("uhci_remove_ctrl: sqh=%p\n", sqh)); + for (pqh = sc->sc_ctl_start; pqh->qh->hlink != sqh; pqh=pqh->qh->hlink) +#if defined(DIAGNOSTIC) || defined(USB_DEBUG) + if (pqh->qh->qh_hlink & UHCI_PTR_T) { + printf("uhci_remove_ctrl: QH not found\n"); + return; + } +#else + ; +#endif + pqh->qh->hlink = sqh->qh->hlink; + pqh->qh->qh_hlink = sqh->qh->qh_hlink; + if (sc->sc_ctl_end == sqh) + sc->sc_ctl_end = pqh; +} + +/* Add bulk QH, called at splusb(). */ +void +uhci_add_bulk(sc, sqh) + uhci_softc_t *sc; + uhci_soft_qh_t *sqh; +{ + uhci_qh_t *eqh; + + DPRINTFN(10, ("uhci_add_bulk: sqh=%p\n", sqh)); + eqh = sc->sc_bulk_end->qh; + sqh->qh->hlink = eqh->hlink; + sqh->qh->qh_hlink = eqh->qh_hlink; + eqh->hlink = sqh; + eqh->qh_hlink = sqh->physaddr | UHCI_PTR_Q; + sc->sc_bulk_end = sqh; +} + +/* Remove bulk QH, called at splusb(). */ +void +uhci_remove_bulk(sc, sqh) + uhci_softc_t *sc; + uhci_soft_qh_t *sqh; +{ + uhci_soft_qh_t *pqh; + + DPRINTFN(10, ("uhci_remove_bulk: sqh=%p\n", sqh)); + for (pqh = sc->sc_bulk_start; pqh->qh->hlink != sqh; pqh=pqh->qh->hlink) +#if defined(DIAGNOSTIC) || defined(USB_DEBUG) + if (pqh->qh->qh_hlink & UHCI_PTR_T) { + printf("uhci_remove_bulk: QH not found\n"); + return; + } +#else + ; +#endif + pqh->qh->hlink = sqh->qh->hlink; + pqh->qh->qh_hlink = sqh->qh->qh_hlink; + if (sc->sc_bulk_end == sqh) + sc->sc_bulk_end = pqh; +} + +int +uhci_intr(p) + void *p; +{ + uhci_softc_t *sc = p; + int status, ret; + uhci_intr_info_t *ii; + + sc->sc_intrs++; +#if defined(USB_DEBUG) + if (uhcidebug > 9) + uhci_dumpregs(sc); +#endif + status = UREAD2(sc, UHCI_STS); + ret = 0; + if (status & UHCI_STS_USBINT) { + UWRITE2(sc, UHCI_STS, UHCI_STS_USBINT); /* acknowledge */ + ret = 1; + } + if (status & UHCI_STS_USBEI) { + UWRITE2(sc, UHCI_STS, UHCI_STS_USBEI); /* acknowledge */ + ret = 1; + } + if (status & UHCI_STS_RD) { + UWRITE2(sc, UHCI_STS, UHCI_STS_RD); /* acknowledge */ + DEVICE_MSG(sc->sc_bus.bdev, ("resume detect\n")); + ret = 1; + } + if (status & UHCI_STS_HSE) { + UWRITE2(sc, UHCI_STS, UHCI_STS_HSE); /* acknowledge */ + DEVICE_MSG(sc->sc_bus.bdev, ("Host System Error\n")); + ret = 1; + } + if (status & UHCI_STS_HCPE) { + UWRITE2(sc, UHCI_STS, UHCI_STS_HCPE); /* acknowledge */ + DEVICE_MSG(sc->sc_bus.bdev, ("Host System Error\n")); + ret = 1; + } + if (status & UHCI_STS_HCH) + DEVICE_ERROR(sc->sc_bus.bdev, ("controller halted\n")); + if (!ret) + return 0; + + /* + * Interrupts on UHCI really suck. When the host controller + * interrupts because a transfer is completed there is no + * way of knowing which transfer it was. You can scan down + * the TDs and QHs of the previous frame to limit the search, + * but that assumes that the interrupt was not delayed by more + * than 1 ms, which may not always be true (e.g. after debug + * output on a slow console). + * We scan all interrupt descriptors to see if any have + * completed. + */ + for (ii = LIST_FIRST(&sc->sc_intrhead); ii; ii = LIST_NEXT(ii, list)) + uhci_check_intr(sc, ii); + + DPRINTFN(10, ("uhci_intr: exit\n")); + return 1; +} + +/* Check for an interrupt. */ +void +uhci_check_intr(sc, ii) + uhci_softc_t *sc; + uhci_intr_info_t *ii; +{ + struct uhci_pipe *upipe; + uhci_soft_td_t *std, *lstd; + + DPRINTFN(15, ("uhci_check_intr: ii=%p\n", ii)); +#ifdef DIAGNOSTIC + if (!ii) { + printf("uhci_check_intr: no ii? %p\n", ii); + return; + } +#endif + if (!ii->stdstart) + return; + lstd = ii->stdend; +#ifdef DIAGNOSTIC + if (!lstd) { + printf("uhci_check_intr: std==0\n"); + return; + } +#endif + /* If the last TD is still active the whole transfer probably is. */ + if (lstd->td->td_status & UHCI_TD_ACTIVE) { + DPRINTFN(15, ("uhci_check_intr: active ii=%p\n", ii)); + for (std = ii->stdstart; std != lstd; std = std->td->link.std) + if (std->td->td_status & UHCI_TD_STALLED) + goto done; + DPRINTFN(15, ("uhci_check_intr: ii=%p still active\n", ii)); + return; + } + done: + upipe = (struct uhci_pipe *)ii->reqh->pipe; + upipe->pipe.endpoint->toggle = upipe->newtoggle; + uhci_ii_done(ii, 0); +#if defined(__NetBSD__) + untimeout(uhci_timeout, ii); +#elif defined(__FreeBSD__) + untimeout(uhci_timeout, ii, ii->callout_handler); +#endif +} + +void +uhci_ii_done(ii, timo) + uhci_intr_info_t *ii; + int timo; +{ + usbd_request_handle reqh = ii->reqh; + uhci_soft_td_t *std; + u_int32_t tst; + int len, status; + + DPRINTFN(10, ("uhci_ii_done: ii=%p ready %d\n", ii, timo)); + +#ifdef DIAGNOSTIC + { + int s = splhigh(); + if (ii->isdone) { + printf("uhci_ii_done: is done!\n"); + splx(s); + return; + } + ii->isdone = 1; + splx(s); + } +#endif + + /* The transfer is done, compute length and status. */ + for (len = status = 0, std = ii->stdstart; + std != 0; + std = std->td->link.std) { + tst = std->td->td_status; + status |= tst; +#ifdef USB_DEBUG + if ((tst & UHCI_TD_ERROR) && uhcidebug) { + printf("uhci_ii_done: intr error TD:\n"); + uhci_dump_td(std); + } +#endif + if (UHCI_TD_GET_PID(std->td->td_token) != UHCI_TD_PID_SETUP) + len += UHCI_TD_GET_ACTLEN(tst); + } + status &= UHCI_TD_ERROR; + /* NWH wrong func name also below, 'uhci_intr' 3 times + DPRINTFN(10, ("uhci_check_intr: len=%d, status=0x%x\n", len, status)); + */ + DPRINTFN(10, ("uhci_ii_done: len=%d\n", len)); + if (status != 0) { +#if defined(__NetBSD__) + DPRINTFN(-1+(status==UHCI_TD_STALLED), + ("uhci_ii_done: error, status 0x%b\n", (long)status, + "\20\22BITSTUFF\23CRCTO\24NAK\25BABBLE\26DBUFFER\27STALLED\30ACTIVE")); +#elif defined(__FreeBSD__) + DPRINTFN(-1+(status==UHCI_TD_STALLED), + ("uhci_ii_done: error, status 0x%08lx\n", (long)status)); +#endif + if (status == UHCI_TD_STALLED) + reqh->status = USBD_STALLED; + else + reqh->status = USBD_IOERROR; /* more info XXX */ + reqh->actlen = 0; + } else { + reqh->status = USBD_NORMAL_COMPLETION; + reqh->actlen = len; + } + if (timo) { + /* We got a timeout. Make sure transaction is not active. */ + reqh->status = USBD_TIMEOUT; + for (std = ii->stdstart; std != 0; std = std->td->link.std) + std->td->td_status &= ~UHCI_TD_ACTIVE; + /* XXX should we wait 1 ms */ + } + DPRINTFN(5, ("uhci_ii_done: calling handler ii=%p\n", ii)); + + switch (reqh->pipe->endpoint->edesc->bmAttributes & UE_XFERTYPE) { + case UE_CONTROL: + uhci_ctrl_done(ii); + break; + case UE_ISOCHRONOUS: + printf("uhci_ii_done: ISO??\n"); + break; + case UE_BULK: + uhci_bulk_done(ii); + break; + case UE_INTERRUPT: + uhci_intr_done(ii); + break; + } + + /* And finally execute callback. */ + reqh->xfercb(reqh); +} + +void +uhci_timeout(addr) + void *addr; +{ + uhci_intr_info_t *ii = addr; + int s; + + DPRINTF(("uhci_timeout: ii=%p\n", ii)); + s = splusb(); + uhci_ii_done(ii, 1); + splx(s); +} + +/* + * Wait here until controller claims to have an interrupt. + * Then call uhci_intr and return. Use timeout to avoid waiting + * too long. + */ +void +uhci_waitintr(sc, reqh) + uhci_softc_t *sc; + usbd_request_handle reqh; +{ + int timo = reqh->timeout; + int usecs; + int hzs; + + DPRINTFN(10,("uhci_waitintr: timout = %ds\n", timo)); + + reqh->status = USBD_IN_PROGRESS; + for (usecs = timo * 1000000 / hz; usecs > 0; usecs -= 100000) { + /* NWH replaced by usbd_delay_ms + delay(1000); + NWH and descreased frequency from 1ms to 100ms, see also usecs -=... + */ + usbd_delay_ms(&(sc->sc_bus), 100); + /* NWH disabled + DPRINTFN(10,("uhci_waitintr: 0x%04x\n", UREAD2(sc, UHCI_STS))); + */ + if (UREAD2(sc, UHCI_STS) & UHCI_STS_USBINT) { + uhci_intr(sc); + if (reqh->status != USBD_IN_PROGRESS) + return; + } + } + reqh->status = USBD_TIMEOUT; + reqh->xfercb(reqh); +} + +void +uhci_poll(bus) + struct usbd_bus *bus; +{ + uhci_softc_t *sc = (uhci_softc_t *)bus; + + if (UREAD2(sc, UHCI_STS) & UHCI_STS_USBINT) + uhci_intr(sc); +} + +#if 0 +void +uhci_reset(p) + void *p; +{ + uhci_softc_t *sc = p; + int n; + + UHCICMD(sc, UHCI_CMD_HCRESET); + /* The reset bit goes low when the controller is done. */ + for (n = 0; n < UHCI_RESET_TIMEOUT && + (UREAD2(sc, UHCI_CMD) & UHCI_CMD_HCRESET); n++) + delay(100); + if (n >= UHCI_RESET_TIMEOUT) + DEVICE_ERROR(sc->sc_bus.bdev, ("controller did not reset\n")); +} +#endif + +void +uhci_run(sc, run) + uhci_softc_t *sc; + int run; +{ + int s, n, running; + + run = run != 0; + s = splusb(); /* XXX really? */ + running = !(UREAD2(sc, UHCI_STS) & UHCI_STS_HCH); + if (run == running) { + splx(s); + return; + } + UWRITE2(sc, UHCI_CMD, run ? UHCI_CMD_RS : 0); + for(n = 0; n < 100; n++) { + running = !(UREAD2(sc, UHCI_STS) & UHCI_STS_HCH); + /* return when we've entered the state we want */ + if (run == running) { + splx(s); + return; + } + } + splx(s); + DEVICE_ERROR(sc->sc_bus.bdev, ("cannot %s\n", (run ? "start" : "stop"))); +} + +/* + * Memory management routines. + * uhci_alloc_std allocates TDs + * uhci_alloc_sqh allocates QHs + * These two routines do their own free list management, + * partly for speed, partly because allocating DMAable memory + * has page size granularaity so much memory would be wasted if + * only one TD/QH (32 bytes) was placed in each alloacted chunk. + */ + +uhci_soft_td_t * +uhci_alloc_std(sc) + uhci_softc_t *sc; +{ + uhci_soft_td_t *std; + usbd_status r; + int i; + usb_dma_t dma; + + if (!sc->sc_freetds) { + DPRINTFN(2,("uhci_alloc_std: allocating chunk\n")); + std = malloc(sizeof(uhci_soft_td_t) * UHCI_TD_CHUNK, + M_USBDEV, M_NOWAIT); + if (!std) + return 0; + r = usb_allocmem(sc->sc_dmatag, UHCI_TD_SIZE * UHCI_TD_CHUNK, + UHCI_TD_ALIGN, &dma); + if (r != USBD_NORMAL_COMPLETION) { + free(std, M_USBDEV); + return 0; + } + for(i = 0; i < UHCI_TD_CHUNK; i++, std++) { + std->physaddr = DMAADDR(&dma) + + i * UHCI_TD_SIZE; + std->td = (uhci_td_t *) + ((char *)KERNADDR(&dma) + i * UHCI_TD_SIZE); + std->td->link.std = sc->sc_freetds; + sc->sc_freetds = std; + } + } + std = sc->sc_freetds; + sc->sc_freetds = std->td->link.std; + memset(std->td, 0, UHCI_TD_SIZE); + return std; +} + +void +uhci_free_std(sc, std) + uhci_softc_t *sc; + uhci_soft_td_t *std; +{ +#ifdef DIAGNOSTIC +#define TD_IS_FREE 0x12345678 + if (std->td->td_token == TD_IS_FREE) { + printf("uhci_free_std: freeing free TD %p\n", std); + return; + } + std->td->td_token = TD_IS_FREE; +#endif + std->td->link.std = sc->sc_freetds; + sc->sc_freetds = std; +} + +uhci_soft_qh_t * +uhci_alloc_sqh(sc) + uhci_softc_t *sc; +{ + uhci_soft_qh_t *sqh; + usbd_status r; + int i, offs; + usb_dma_t dma; + + if (!sc->sc_freeqhs) { + DPRINTFN(2, ("uhci_alloc_sqh: allocating chunk\n")); + sqh = malloc(sizeof(uhci_soft_qh_t) * UHCI_QH_CHUNK, + M_USBDEV, M_NOWAIT); + if (!sqh) + return 0; + r = usb_allocmem(sc->sc_dmatag, UHCI_QH_SIZE * UHCI_QH_CHUNK, + UHCI_QH_ALIGN, &dma); + if (r != USBD_NORMAL_COMPLETION) { + free(sqh, M_USBDEV); + return 0; + } + for(i = 0; i < UHCI_QH_CHUNK; i++, sqh++) { + offs = i * UHCI_QH_SIZE; + sqh->physaddr = DMAADDR(&dma) + offs; + sqh->qh = (uhci_qh_t *) + ((char *)KERNADDR(&dma) + offs); + sqh->qh->hlink = sc->sc_freeqhs; + sc->sc_freeqhs = sqh; + } + } + sqh = sc->sc_freeqhs; + sc->sc_freeqhs = sqh->qh->hlink; + memset(sqh->qh, 0, UHCI_QH_SIZE); + return sqh; +} + +void +uhci_free_sqh(sc, sqh) + uhci_softc_t *sc; + uhci_soft_qh_t *sqh; +{ + sqh->qh->hlink = sc->sc_freeqhs; + sc->sc_freeqhs = sqh; +} + +/* + * Enter a list of transfers onto a control queue. + * Called at splusb() + */ +void +uhci_enter_ctl_q(sc, sqh, ii) + uhci_softc_t *sc; + uhci_soft_qh_t *sqh; + uhci_intr_info_t *ii; +{ + DPRINTFN(5, ("uhci_enter_ctl_q: sqh=%p\n", sqh)); + +} + +void +uhci_free_std_chain(sc, std, stdend) + uhci_softc_t *sc; + uhci_soft_td_t *std; + uhci_soft_td_t *stdend; +{ + uhci_soft_td_t *p; + + for (; std != stdend; std = p) { + p = std->td->link.std; + uhci_free_std(sc, std); + } +} + +usbd_status +uhci_alloc_std_chain(upipe, sc, len, rd, dma, sp, ep) + struct uhci_pipe *upipe; + uhci_softc_t *sc; + int len, rd; + usb_dma_t *dma; + uhci_soft_td_t **sp, **ep; +{ + uhci_soft_td_t *p, *lastp; + uhci_physaddr_t lastlink; + u_int32_t ls; + int i, ntd, l, tog, maxp; + int addr = upipe->pipe.device->address; + int endpt = upipe->pipe.endpoint->edesc->bEndpointAddress; + + DPRINTFN(15, ("uhci_alloc_std_chain: len=%d\n", len)); + if (len == 0) { + *sp = *ep = 0; + printf("uhci_alloc_std_chain: len=0\n"); + return (USBD_NORMAL_COMPLETION); + } + ls = upipe->pipe.device->lowspeed ? UHCI_TD_LS : 0; + maxp = UGETW(upipe->pipe.endpoint->edesc->wMaxPacketSize); + if (maxp == 0) { + printf("uhci_alloc_std_chain: maxp=0\n"); + return (USBD_INVAL); + } + ntd = (len + maxp - 1) / maxp; + tog = upipe->pipe.endpoint->toggle; + if (ntd % 2 == 0) + tog ^= 1; + upipe->newtoggle = tog ^ 1; + lastp = 0; + lastlink = UHCI_PTR_T; + ntd--; + for (i = ntd; i >= 0; i--) { + p = uhci_alloc_std(sc); + if (!p) { + uhci_free_std_chain(sc, lastp, 0); + return (USBD_NOMEM); + } + p->td->link.std = lastp; + p->td->td_link = lastlink; + lastp = p; + lastlink = p->physaddr; + p->td->td_status = UHCI_TD_SET_ERRCNT(2) | ls | UHCI_TD_ACTIVE; + if (i == ntd) { + /* last TD */ + l = len % maxp; + if (l == 0) l = maxp; + *ep = p; + } else + l = maxp; + p->td->td_token = + rd ? UHCI_TD_IN (l, endpt, addr, tog) : + UHCI_TD_OUT(l, endpt, addr, tog); + p->td->td_buffer = DMAADDR(dma) + i * maxp; + tog ^= 1; + } + *sp = lastp; + /*upipe->pipe.endpoint->toggle = tog;*/ + DPRINTFN(10, ("uhci_alloc_std_chain: oldtog=%d newtog=%d\n", + upipe->pipe.endpoint->toggle, upipe->newtoggle)); + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +uhci_device_bulk_transfer(reqh) + usbd_request_handle reqh; +{ + struct uhci_pipe *upipe = (struct uhci_pipe *)reqh->pipe; + usbd_device_handle dev = upipe->pipe.device; + uhci_softc_t *sc = (uhci_softc_t *)dev->bus; + uhci_intr_info_t *ii = upipe->iinfo; + uhci_soft_td_t *xfer, *xferend; + uhci_soft_qh_t *sqh; + usb_dma_t *dmap; + usbd_status r; + int len, isread; + int s; + + DPRINTFN(3, ("uhci_device_bulk_transfer: reqh=%p buf=%p len=%d flags=%d\n", + reqh, reqh->buffer, reqh->length, reqh->flags)); + + if (reqh->isreq) + panic("uhci_device_bulk_transfer: a request\n"); + + len = reqh->length; + dmap = &upipe->u.bulk.datadma; + isread = reqh->pipe->endpoint->edesc->bEndpointAddress & UE_IN; + sqh = upipe->u.bulk.sqh; + + upipe->u.bulk.isread = isread; + upipe->u.bulk.length = len; + + r = usb_allocmem(sc->sc_dmatag, len, 0, dmap); + if (r != USBD_NORMAL_COMPLETION) + goto ret1; + r = uhci_alloc_std_chain(upipe, sc, len, isread, + dmap, &xfer, &xferend); + if (r != USBD_NORMAL_COMPLETION) + goto ret2; + xferend->td->td_status |= UHCI_TD_IOC; + + if (!isread && len != 0) + memcpy(KERNADDR(dmap), reqh->buffer, len); + +#ifdef USB_DEBUG + if (uhcidebug > 10) { + printf("uhci_device_bulk_transfer: xfer(1)\n"); + uhci_dump_tds(xfer); + } +#endif + + /* Set up interrupt info. */ + ii->reqh = reqh; + ii->stdstart = xfer; + ii->stdend = xferend; +#ifdef DIAGNOSTIC + ii->isdone = 0; +#endif + + sqh->qh->elink = xfer; + sqh->qh->qh_elink = xfer->physaddr; + sqh->intr_info = ii; + + s = splusb(); + uhci_add_bulk(sc, sqh); + LIST_INSERT_HEAD(&sc->sc_intrhead, ii, list); + + if (reqh->timeout && !sc->sc_bus.use_polling) + timeout(uhci_timeout, ii, MS_TO_TICKS(reqh->timeout)); + splx(s); + +#ifdef USB_DEBUG + if (uhcidebug > 10) { + printf("uhci_device_bulk_transfer: xfer(2)\n"); + uhci_dump_tds(xfer); + } +#endif + + return (USBD_IN_PROGRESS); + + ret2: + if (len != 0) + usb_freemem(sc->sc_dmatag, dmap); + ret1: + return (r); +} + +/* Abort a device bulk request. */ +void +uhci_device_bulk_abort(reqh) + usbd_request_handle reqh; +{ + /* XXX inactivate */ + usbd_delay_ms(reqh->pipe->device->bus, 1); /* make sure it is finished */ + /* XXX call done */ +} + +/* Close a device bulk pipe. */ +void +uhci_device_bulk_close(pipe) + usbd_pipe_handle pipe; +{ + struct uhci_pipe *upipe = (struct uhci_pipe *)pipe; + usbd_device_handle dev = upipe->pipe.device; + uhci_softc_t *sc = (uhci_softc_t *)dev->bus; + + uhci_free_sqh(sc, upipe->u.bulk.sqh); + uhci_free_intr_info(upipe->iinfo); + /* XXX free other resources */ +} + +usbd_status +uhci_device_ctrl_transfer(reqh) + usbd_request_handle reqh; +{ + uhci_softc_t *sc = (uhci_softc_t *)reqh->pipe->device->bus; + usbd_status r; + + if (!reqh->isreq) + panic("uhci_device_ctrl_transfer: not a request\n"); + + r = uhci_device_request(reqh); + if (r != USBD_NORMAL_COMPLETION) + return (r); + + if (sc->sc_bus.use_polling) + uhci_waitintr(sc, reqh); + return (USBD_IN_PROGRESS); +} + +usbd_status +uhci_device_intr_transfer(reqh) + usbd_request_handle reqh; +{ + struct uhci_pipe *upipe = (struct uhci_pipe *)reqh->pipe; + usbd_device_handle dev = upipe->pipe.device; + uhci_softc_t *sc = (uhci_softc_t *)dev->bus; + uhci_intr_info_t *ii = upipe->iinfo; + uhci_soft_td_t *xfer, *xferend; + uhci_soft_qh_t *sqh; + usb_dma_t *dmap; + usbd_status r; + int len, i; + int s; + + DPRINTFN(3, ("uhci_device_intr_transfer: reqh=%p buf=%p len=%d flags=%d\n", + reqh, reqh->buffer, reqh->length, reqh->flags)); + + if (reqh->isreq) + panic("uhci_device_intr_transfer: a request\n"); + + len = reqh->length; + dmap = &upipe->u.intr.datadma; + if (len == 0) + return (USBD_INVAL); /* XXX should it be? */ + + r = usb_allocmem(sc->sc_dmatag, len, 0, dmap); + if (r != USBD_NORMAL_COMPLETION) + goto ret1; + r = uhci_alloc_std_chain(upipe, sc, len, 1, dmap, &xfer, &xferend); + if (r != USBD_NORMAL_COMPLETION) + goto ret2; + xferend->td->td_status |= UHCI_TD_IOC; + +#ifdef USB_DEBUG + if (uhcidebug > 10) { + printf("uhci_device_intr_transfer: xfer(1)\n"); + uhci_dump_tds(xfer); + uhci_dump_qh(upipe->u.intr.qhs[0]); + } +#endif + + s = splusb(); + /* Set up interrupt info. */ + ii->reqh = reqh; + ii->stdstart = xfer; + ii->stdend = xferend; +#ifdef DIAGNOSTIC + ii->isdone = 0; +#endif + +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]; + sqh->qh->elink = xfer; + sqh->qh->qh_elink = xfer->physaddr; + } + splx(s); + +#ifdef USB_DEBUG + if (uhcidebug > 10) { + printf("uhci_device_intr_transfer: xfer(2)\n"); + uhci_dump_tds(xfer); + uhci_dump_qh(upipe->u.intr.qhs[0]); + } +#endif + + return (USBD_IN_PROGRESS); + + ret2: + if (len != 0) + usb_freemem(sc->sc_dmatag, dmap); + ret1: + return (r); +} + +/* Abort a device control request. */ +void +uhci_device_ctrl_abort(reqh) + usbd_request_handle reqh; +{ + /* XXX inactivate */ + usbd_delay_ms(reqh->pipe->device->bus, 1); /* make sure it is finished */ + /* XXX call done */ +} + +/* Close a device control pipe. */ +void +uhci_device_ctrl_close(pipe) + usbd_pipe_handle pipe; +{ + struct uhci_pipe *upipe = (struct uhci_pipe *)pipe; + + uhci_free_intr_info(upipe->iinfo); + /* XXX free other resources */ +} + +/* Abort a device interrupt request. */ +void +uhci_device_intr_abort(reqh) + usbd_request_handle reqh; +{ + struct uhci_pipe *upipe; + + DPRINTFN(1, ("uhci_device_intr_abort: reqh=%p\n", reqh)); + /* XXX inactivate */ + usbd_delay_ms(reqh->pipe->device->bus, 2); /* make sure it is finished */ + if (reqh->pipe->intrreqh == reqh) { + DPRINTF(("uhci_device_intr_abort: remove\n")); + reqh->pipe->intrreqh = 0; + upipe = (struct uhci_pipe *)reqh->pipe; + uhci_intr_done(upipe->u.intr.qhs[0]->intr_info); + } +} + +/* Close a device interrupt pipe. */ +void +uhci_device_intr_close(pipe) + usbd_pipe_handle pipe; +{ + struct uhci_pipe *upipe = (struct uhci_pipe *)pipe; + uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus; + int i, s, npoll; + + upipe->iinfo->stdstart = 0; /* inactive */ + + /* Unlink descriptors from controller data structures. */ + npoll = upipe->u.intr.npoll; + uhci_lock_frames(sc); + for (i = 0; i < npoll; i++) + uhci_remove_intr(sc, upipe->u.intr.qhs[i]->pos, + upipe->u.intr.qhs[i]); + uhci_unlock_frames(sc); + + /* + * We now have to wait for any activity on the physical + * descriptors to stop. + */ + usbd_delay_ms(&sc->sc_bus, 2); + + for(i = 0; i < npoll; i++) + uhci_free_sqh(sc, upipe->u.intr.qhs[i]); + free(upipe->u.intr.qhs, M_USB); + + s = splusb(); + LIST_REMOVE(upipe->iinfo, list); /* remove from active list */ + splx(s); + uhci_free_intr_info(upipe->iinfo); + + /* XXX free other resources */ +} + +usbd_status +uhci_device_request(reqh) + usbd_request_handle reqh; +{ + struct uhci_pipe *upipe = (struct uhci_pipe *)reqh->pipe; + usb_device_request_t *req = &reqh->request; + usbd_device_handle dev = upipe->pipe.device; + uhci_softc_t *sc = (uhci_softc_t *)dev->bus; + int addr = dev->address; + int endpt = upipe->pipe.endpoint->edesc->bEndpointAddress; + uhci_intr_info_t *ii = upipe->iinfo; + uhci_soft_td_t *setup, *xfer, *stat, *next, *xferend; + uhci_soft_qh_t *sqh; + usb_dma_t *dmap; + int len; + u_int32_t ls; + usbd_status r; + int isread; + int s; + + DPRINTFN(1,("uhci_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), UGETW(req->wLength), + addr, endpt)); + + ls = dev->lowspeed ? UHCI_TD_LS : 0; + isread = req->bmRequestType & UT_READ; + len = UGETW(req->wLength); + + setup = upipe->u.ctl.setup; + stat = upipe->u.ctl.stat; + sqh = upipe->u.ctl.sqh; + dmap = &upipe->u.ctl.datadma; + + /* Set up data transaction */ + if (len != 0) { + r = usb_allocmem(sc->sc_dmatag, len, 0, dmap); + if (r != USBD_NORMAL_COMPLETION) + goto ret1; + upipe->pipe.endpoint->toggle = 1; + r = uhci_alloc_std_chain(upipe, sc, len, isread, + dmap, &xfer, &xferend); + if (r != USBD_NORMAL_COMPLETION) + goto ret2; + next = xfer; + xferend->td->link.std = stat; + xferend->td->td_link = stat->physaddr; + } else { + xfer = 0; + next = stat; + } + upipe->u.ctl.length = len; + upipe->u.ctl.xferend = xferend; + + memcpy(KERNADDR(&upipe->u.ctl.reqdma), req, sizeof *req); + if (!isread && len != 0) + memcpy(KERNADDR(dmap), reqh->buffer, len); + + setup->td->link.std = next; + setup->td->td_link = next->physaddr; + setup->td->td_status = UHCI_TD_SET_ERRCNT(2) | ls | UHCI_TD_ACTIVE; + setup->td->td_token = UHCI_TD_SETUP(sizeof *req, endpt, addr); + setup->td->td_buffer = DMAADDR(&upipe->u.ctl.reqdma); + + stat->td->link.std = 0; + stat->td->td_link = UHCI_PTR_T; + stat->td->td_status = UHCI_TD_SET_ERRCNT(2) | ls | + UHCI_TD_ACTIVE | UHCI_TD_IOC; + stat->td->td_token = + isread ? UHCI_TD_OUT(0, endpt, addr, 1) : + UHCI_TD_IN (0, endpt, addr, 1); + stat->td->td_buffer = 0; + +#ifdef USB_DEBUG + if (uhcidebug > 20) { + printf("uhci_device_request: setup\n"); + uhci_dump_td(setup); + printf("uhci_device_request: stat\n"); + uhci_dump_td(stat); + } +#endif + + /* Set up interrupt info. */ + ii->reqh = reqh; + ii->stdstart = setup; + ii->stdend = stat; +#ifdef DIAGNOSTIC + ii->isdone = 0; +#endif + + sqh->qh->elink = setup; + sqh->qh->qh_elink = setup->physaddr; + sqh->intr_info = ii; + + s = splusb(); + uhci_add_ctrl(sc, sqh); + LIST_INSERT_HEAD(&sc->sc_intrhead, ii, list); +#ifdef USB_DEBUG + if (uhcidebug > 12) { + uhci_soft_td_t *std; + uhci_soft_qh_t *xqh; + uhci_soft_qh_t *sxqh; + int maxqh = 0; + uhci_physaddr_t link; + printf("uhci_enter_ctl_q: follow from [0]\n"); + for (std = sc->sc_vframes[0].htd, link = 0; + (link & UHCI_PTR_Q) == 0; + std = std->td->link.std) { + link = std->td->td_link; + uhci_dump_td(std); + } + for (sxqh = xqh = (uhci_soft_qh_t *)std; + xqh; + /* FIXME NWH seems to be a circular list ?? + * checking for beginning of list end of list + * and printing a maximum of 5 QH's ... + xqh = xqh->qh->hlink) + */ + xqh = (maxqh++ == 5 || xqh->qh->hlink==sxqh || xqh->qh->hlink==xqh? NULL : xqh->qh->hlink)) { + uhci_dump_qh(xqh); + uhci_dump_qh(sxqh); + } + printf("Enqueued QH:\n"); + uhci_dump_qh(sqh); + uhci_dump_tds(sqh->qh->elink); + } +#endif + if (reqh->timeout && !sc->sc_bus.use_polling) +#if defined(__NetBSD__) + timeout(uhci_timeout, ii, MS_TO_TICKS(reqh->timeout)); +#elif defined(__FreeBSD__) + /* To avoid race conditions we first initialise the struct + * before we use it. The timeout may happen between the setting + * of the timeout and the setting of callout_handle + */ + callout_handle_init(&ii->callout_handler); + ii->callout_handler = timeout(uhci_timeout, ii, MS_TO_TICKS(reqh->timeout)); +#endif + splx(s); + + return (USBD_NORMAL_COMPLETION); + + ret2: + if (len != 0) + usb_freemem(sc->sc_dmatag, dmap); + ret1: + return (r); +} + +void +uhci_intr_done(ii) + uhci_intr_info_t *ii; +{ + uhci_softc_t *sc = ii->sc; + usbd_request_handle reqh = ii->reqh; + struct uhci_pipe *upipe = (struct uhci_pipe *)reqh->pipe; + usb_dma_t *dma; + uhci_soft_qh_t *sqh; + int i, npoll; + + DPRINTFN(5, ("uhci_intr_done: length=%d\n", reqh->actlen)); + + dma = &upipe->u.intr.datadma; + memcpy(reqh->buffer, KERNADDR(dma), reqh->actlen); + npoll = upipe->u.intr.npoll; + for(i = 0; i < npoll; i++) { + sqh = upipe->u.intr.qhs[i]; + sqh->qh->elink = 0; + sqh->qh->qh_elink = UHCI_PTR_T; + } + uhci_free_std_chain(sc, ii->stdstart, 0); + + /* XXX Wasteful. */ + if (reqh->pipe->intrreqh == reqh) { + uhci_soft_td_t *xfer, *xferend; + + /* This alloc cannot fail since we freed the chain above. */ + uhci_alloc_std_chain(upipe, sc, reqh->length, 1, dma, + &xfer, &xferend); + xferend->td->td_status |= UHCI_TD_IOC; + +#ifdef USB_DEBUG + if (uhcidebug > 10) { + printf("uhci_device_intr_done: xfer(1)\n"); + uhci_dump_tds(xfer); + uhci_dump_qh(upipe->u.intr.qhs[0]); + } +#endif + + ii->stdstart = xfer; + ii->stdend = xferend; +#ifdef DIAGNOSTIC + ii->isdone = 0; +#endif + for (i = 0; i < npoll; i++) { + sqh = upipe->u.intr.qhs[i]; + sqh->qh->elink = xfer; + sqh->qh->qh_elink = xfer->physaddr; + } + } else { + usb_freemem(sc->sc_dmatag, dma); + ii->stdstart = 0; /* mark as inactive */ + } +} + +/* Deallocate request data structures */ +void +uhci_ctrl_done(ii) + uhci_intr_info_t *ii; +{ + uhci_softc_t *sc = ii->sc; + usbd_request_handle reqh = ii->reqh; + struct uhci_pipe *upipe = (struct uhci_pipe *)reqh->pipe; + u_int len = upipe->u.ctl.length; + usb_dma_t *dma; + uhci_td_t *htd = ii->stdstart->td; + +#ifdef DIAGNOSTIC + if (!reqh->isreq) + panic("uhci_ctrl_done: not a request\n"); +#endif + + LIST_REMOVE(ii, list); /* remove from active list */ + + uhci_remove_ctrl(sc, upipe->u.ctl.sqh); + + if (len != 0) { + dma = &upipe->u.ctl.datadma; + if (reqh->request.bmRequestType & UT_READ) + memcpy(reqh->buffer, KERNADDR(dma), len); + uhci_free_std_chain(sc, htd->link.std, ii->stdend); + usb_freemem(sc->sc_dmatag, dma); + } + DPRINTFN(5, ("uhci_ctrl_done: length=%d\n", reqh->actlen)); +} + +/* Deallocate request data structures */ +void +uhci_bulk_done(ii) + uhci_intr_info_t *ii; +{ + uhci_softc_t *sc = ii->sc; + usbd_request_handle reqh = ii->reqh; + struct uhci_pipe *upipe = (struct uhci_pipe *)reqh->pipe; + u_int len = upipe->u.bulk.length; + usb_dma_t *dma; + uhci_td_t *htd = ii->stdstart->td; + + LIST_REMOVE(ii, list); /* remove from active list */ + + uhci_remove_bulk(sc, upipe->u.bulk.sqh); + + if (len != 0) { + dma = &upipe->u.bulk.datadma; + if (upipe->u.bulk.isread && len != 0) + memcpy(reqh->buffer, KERNADDR(dma), len); + uhci_free_std_chain(sc, htd->link.std, 0); + usb_freemem(sc->sc_dmatag, dma); + } + DPRINTFN(4, ("uhci_bulk_done: length=%d\n", reqh->actlen)); + /* XXX compute new toggle */ +} + +/* Add interrupt QH, called with vflock. */ +void +uhci_add_intr(sc, n, sqh) + uhci_softc_t *sc; + int n; + uhci_soft_qh_t *sqh; +{ + struct uhci_vframe *vf = &sc->sc_vframes[n]; + uhci_qh_t *eqh; + + DPRINTFN(4, ("uhci_add_intr: n=%d sqh=%p\n", n, sqh)); + eqh = vf->eqh->qh; + sqh->qh->hlink = eqh->hlink; + sqh->qh->qh_hlink = eqh->qh_hlink; + eqh->hlink = sqh; + eqh->qh_hlink = sqh->physaddr | UHCI_PTR_Q; + vf->eqh = sqh; + vf->bandwidth++; +} + +/* Remove interrupt QH, called with vflock. */ +void +uhci_remove_intr(sc, n, sqh) + uhci_softc_t *sc; + int n; + uhci_soft_qh_t *sqh; +{ + struct uhci_vframe *vf = &sc->sc_vframes[n]; + uhci_soft_qh_t *pqh; + + DPRINTFN(4, ("uhci_remove_intr: n=%d sqh=%p\n", n, sqh)); + + for (pqh = vf->hqh; pqh->qh->hlink != sqh; pqh = pqh->qh->hlink) +#if defined(DIAGNOSTIC) || defined(USB_DEBUG) + if (pqh->qh->qh_hlink & UHCI_PTR_T) { + printf("uhci_remove_intr: QH not found\n"); + return; + } +#else + ; +#endif + pqh->qh->hlink = sqh->qh->hlink; + pqh->qh->qh_hlink = sqh->qh->qh_hlink; + if (vf->eqh == sqh) + vf->eqh = pqh; + vf->bandwidth--; +} + +usbd_status +uhci_device_setintr(sc, upipe, ival) + uhci_softc_t *sc; + struct uhci_pipe *upipe; + int ival; +{ + uhci_soft_qh_t *sqh; + int i, npoll, s; + u_int bestbw, bw, bestoffs, offs; + + DPRINTFN(2, ("uhci_setintr: pipe=%p\n", upipe)); + if (ival == 0) { + printf("uhci_setintr: 0 interval\n"); + return (USBD_INVAL); + } + + if (ival > UHCI_VFRAMELIST_COUNT) + ival = UHCI_VFRAMELIST_COUNT; + npoll = (UHCI_VFRAMELIST_COUNT + ival - 1) / ival; + DPRINTFN(2, ("uhci_setintr: ival=%d npoll=%d\n", ival, npoll)); + + upipe->u.intr.npoll = npoll; + upipe->u.intr.qhs = + malloc(npoll * sizeof(uhci_soft_qh_t *), M_USB, M_WAITOK); + + /* + * Figure out which offset in the schedule that has most + * bandwidth left over. + */ +#define MOD(i) ((i) & (UHCI_VFRAMELIST_COUNT-1)) + for (bestoffs = offs = 0, bestbw = ~0; offs < ival; offs++) { + for (bw = i = 0; i < npoll; i++) + bw += sc->sc_vframes[MOD(i * ival + offs)].bandwidth; + if (bw < bestbw) { + bestbw = bw; + bestoffs = offs; + } + } + DPRINTFN(1, ("uhci_setintr: bw=%d offs=%d\n", bestbw, bestoffs)); + + upipe->iinfo->stdstart = 0; + for(i = 0; i < npoll; i++) { + upipe->u.intr.qhs[i] = sqh = uhci_alloc_sqh(sc); + sqh->qh->elink = 0; + sqh->qh->qh_elink = UHCI_PTR_T; + sqh->pos = MOD(i * ival + bestoffs); + sqh->intr_info = upipe->iinfo; + } +#undef MOD + + s = splusb(); + LIST_INSERT_HEAD(&sc->sc_intrhead, upipe->iinfo, list); + splx(s); + + uhci_lock_frames(sc); + /* Enter QHs into the controller data structures. */ + for(i = 0; i < npoll; i++) + uhci_add_intr(sc, upipe->u.intr.qhs[i]->pos, + upipe->u.intr.qhs[i]); + uhci_unlock_frames(sc); + + DPRINTFN(5, ("uhci_setintr: returns %p\n", upipe)); + return (USBD_NORMAL_COMPLETION); +} + +/* Open a new pipe. */ +usbd_status +uhci_open(pipe) + usbd_pipe_handle pipe; +{ + uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus; + struct uhci_pipe *upipe = (struct uhci_pipe *)pipe; + usb_endpoint_descriptor_t *ed = pipe->endpoint->edesc; + usbd_status r; + + DPRINTFN(1, ("uhci_open: pipe=%p, addr=%d, endpt=%d (%d)\n", + pipe, pipe->device->address, + ed->bEndpointAddress, sc->sc_addr)); + if (pipe->device->address == sc->sc_addr) { + switch (ed->bEndpointAddress) { + case USB_CONTROL_ENDPOINT: + pipe->methods = &uhci_root_ctrl_methods; + break; + case UE_IN | UHCI_INTR_ENDPT: + pipe->methods = &uhci_root_intr_methods; + break; + default: + return (USBD_INVAL); + } + } else { + upipe->iinfo = uhci_alloc_intr_info(sc); + if (upipe->iinfo == 0) + return (USBD_NOMEM); + switch (ed->bmAttributes & UE_XFERTYPE) { + case UE_CONTROL: + pipe->methods = &uhci_device_ctrl_methods; + upipe->u.ctl.sqh = uhci_alloc_sqh(sc); + if (upipe->u.ctl.sqh == 0) + goto bad; + upipe->u.ctl.setup = uhci_alloc_std(sc); + if (upipe->u.ctl.setup == 0) { + uhci_free_sqh(sc, upipe->u.ctl.sqh); + goto bad; + } + upipe->u.ctl.stat = uhci_alloc_std(sc); + if (upipe->u.ctl.stat == 0) { + uhci_free_sqh(sc, upipe->u.ctl.sqh); + uhci_free_std(sc, upipe->u.ctl.setup); + goto bad; + } + r = usb_allocmem(sc->sc_dmatag, + sizeof(usb_device_request_t), + 0, &upipe->u.ctl.reqdma); + if (r != USBD_NORMAL_COMPLETION) { + uhci_free_sqh(sc, upipe->u.ctl.sqh); + uhci_free_std(sc, upipe->u.ctl.setup); + uhci_free_std(sc, upipe->u.ctl.stat); + goto bad; + } + break; + case UE_INTERRUPT: + pipe->methods = &uhci_device_intr_methods; + return (uhci_device_setintr(sc, upipe, ed->bInterval)); + case UE_ISOCHRONOUS: + printf("uhci_open: iso not implemented\n"); + return (USBD_XXX); + case UE_BULK: + pipe->methods = &uhci_device_bulk_methods; + upipe->u.bulk.sqh = uhci_alloc_sqh(sc); + if (upipe->u.bulk.sqh == 0) + goto bad; + break; + } + } + return (USBD_NORMAL_COMPLETION); + + bad: + uhci_free_intr_info(upipe->iinfo); + return (USBD_NOMEM); +} + +/* + * Data structures and routines to emulate the root hub. + */ +usb_device_descriptor_t uhci_devd = { + USB_DEVICE_DESCRIPTOR_SIZE, + UDESC_DEVICE, /* type */ + {0x00, 0x01}, /* USB version */ + UCLASS_HUB, /* class */ + USUBCLASS_HUB, /* subclass */ + 0, /* protocol */ + 64, /* max packet */ + {0},{0},{0x00,0x01}, /* device id */ + 1,2,0, /* string indicies */ + 1 /* # of configurations */ +}; + +usb_config_descriptor_t uhci_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 */ +}; + +usb_interface_descriptor_t uhci_ifcd = { + USB_INTERFACE_DESCRIPTOR_SIZE, + UDESC_INTERFACE, + 0, + 0, + 1, + UCLASS_HUB, + USUBCLASS_HUB, + 0, + 0 +}; + +usb_endpoint_descriptor_t uhci_endpd = { + USB_ENDPOINT_DESCRIPTOR_SIZE, + UDESC_ENDPOINT, + UE_IN | UHCI_INTR_ENDPT, + UE_INTERRUPT, + {8}, + 255 +}; + +usb_hub_descriptor_t uhci_hubd_piix = { + USB_HUB_DESCRIPTOR_SIZE, + UDESC_HUB, + 2, + { UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL, 0 }, + 50, /* power on to power good */ + 0, + { 0x00 }, /* both ports are removable */ + { 0x00 }, /* no ports can power down individually */ +}; + +int +uhci_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. + */ +usbd_status +uhci_root_ctrl_transfer(reqh) + usbd_request_handle reqh; +{ + uhci_softc_t *sc = (uhci_softc_t *)reqh->pipe->device->bus; + usb_device_request_t *req; + void *buf; + int port, x; + int len, value, index, status, change, l, totlen = 0; + usb_port_status_t ps; + usbd_status r; + + if (!reqh->isreq) + panic("uhci_root_ctrl_transfer: not a request\n"); + req = &reqh->request; + buf = reqh->buffer; + + DPRINTFN(2,("uhci_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); +#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_STALL 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(2,("uhci_root_ctrl_control wValue=0x%04x\n", value)); + switch(value >> 8) { + case UDESC_DEVICE: + if ((value & 0xff) != 0) { + r = USBD_IOERROR; + goto ret; + } + totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE); + memcpy(buf, &uhci_devd, l); + break; + case UDESC_CONFIG: + if ((value & 0xff) != 0) { + r = USBD_IOERROR; + goto ret; + } + totlen = l = min(len, USB_CONFIG_DESCRIPTOR_SIZE); + memcpy(buf, &uhci_confd, l); + buf = (char *)buf + l; + len -= l; + l = min(len, USB_INTERFACE_DESCRIPTOR_SIZE); + totlen += l; + memcpy(buf, &uhci_ifcd, l); + buf = (char *)buf + l; + len -= l; + l = min(len, USB_ENDPOINT_DESCRIPTOR_SIZE); + totlen += l; + memcpy(buf, &uhci_endpd, l); + break; + case UDESC_STRING: + if (len == 0) + break; + *(u_int8_t *)buf = 0; + totlen = 1; + switch (value & 0xff) { + case 1: /* Vendor */ + totlen = uhci_str(buf, len, sc->sc_vendor); + break; + case 2: /* Product */ + totlen = uhci_str(buf, len, "UHCI root hub"); + break; + } + break; + default: + r = 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) { + r = USBD_IOERROR; + goto ret; + } + sc->sc_addr = value; + break; + case C(UR_SET_CONFIG, UT_WRITE_DEVICE): + if (value != 0 && value != 1) { + r = 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): + r = 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(3, ("uhci_root_ctrl_control: UR_CLEAR_PORT_FEATURE port=%d feature=%d\n", + index, value)); + if (index == 1) + port = UHCI_PORTSC1; + else if (index == 2) + port = UHCI_PORTSC2; + else { + r = USBD_IOERROR; + goto ret; + } + switch(value) { + case UHF_PORT_ENABLE: + x = UREAD2(sc, port); + UWRITE2(sc, port, x & ~UHCI_PORTSC_PE); + break; + case UHF_PORT_SUSPEND: + x = UREAD2(sc, port); + UWRITE2(sc, port, x & ~UHCI_PORTSC_SUSP); + break; + case UHF_PORT_RESET: + x = UREAD2(sc, port); + UWRITE2(sc, port, x & ~UHCI_PORTSC_PR); + break; + case UHF_C_PORT_CONNECTION: + x = UREAD2(sc, port); + UWRITE2(sc, port, x | UHCI_PORTSC_CSC); + break; + case UHF_C_PORT_ENABLE: + x = UREAD2(sc, port); + UWRITE2(sc, port, x | UHCI_PORTSC_POEDC); + break; + case UHF_C_PORT_OVER_CURRENT: + x = UREAD2(sc, port); + UWRITE2(sc, port, x | UHCI_PORTSC_OCIC); + break; + case UHF_C_PORT_RESET: + sc->sc_isreset = 0; + r = USBD_NORMAL_COMPLETION; + goto ret; + case UHF_PORT_CONNECTION: + case UHF_PORT_OVER_CURRENT: + case UHF_PORT_POWER: + case UHF_PORT_LOW_SPEED: + case UHF_C_PORT_SUSPEND: + default: + r = USBD_IOERROR; + goto ret; + } + break; + case C(UR_GET_BUS_STATE, UT_READ_CLASS_OTHER): + if (index == 1) + port = UHCI_PORTSC1; + else if (index == 2) + port = UHCI_PORTSC2; + else { + r = USBD_IOERROR; + goto ret; + } + if (len > 0) { + *(u_int8_t *)buf = + (UREAD2(sc, port) & UHCI_PORTSC_LS) >> + UHCI_PORTSC_LS_SHIFT; + totlen = 1; + } + break; + case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): + if (value != 0) { + r = USBD_IOERROR; + goto ret; + } + l = min(len, USB_HUB_DESCRIPTOR_SIZE); + totlen = l; + memcpy(buf, &uhci_hubd_piix, l); + break; + case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): + if (len != 4) { + r = USBD_IOERROR; + goto ret; + } + memset(buf, 0, len); + totlen = len; + break; + case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): + if (index == 1) + port = UHCI_PORTSC1; + else if (index == 2) + port = UHCI_PORTSC2; + else { + r = USBD_IOERROR; + goto ret; + } + if (len != 4) { + r = USBD_IOERROR; + goto ret; + } + x = UREAD2(sc, port); + status = change = 0; + if (x & UHCI_PORTSC_CCS ) + status |= UPS_CURRENT_CONNECT_STATUS; + if (x & UHCI_PORTSC_CSC ) + change |= UPS_C_CONNECT_STATUS; + if (x & UHCI_PORTSC_PE ) + status |= UPS_PORT_ENABLED; + if (x & UHCI_PORTSC_POEDC) + change |= UPS_C_PORT_ENABLED; + if (x & UHCI_PORTSC_OCI ) + status |= UPS_OVERCURRENT_INDICATOR; + if (x & UHCI_PORTSC_OCIC ) + change |= UPS_C_OVERCURRENT_INDICATOR; + if (x & UHCI_PORTSC_SUSP ) + status |= UPS_SUSPEND; + if (x & UHCI_PORTSC_LSDA ) + status |= UPS_LOW_SPEED; + status |= UPS_PORT_POWER; + if (sc->sc_isreset) + change |= UPS_C_PORT_RESET; + USETW(ps.wPortStatus, status); + USETW(ps.wPortChange, change); + l = min(len, sizeof ps); + memcpy(buf, &ps, l); + totlen = l; + break; + case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): + r = 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) + port = UHCI_PORTSC1; + else if (index == 2) + port = UHCI_PORTSC2; + else { + r = USBD_IOERROR; + goto ret; + } + switch(value) { + case UHF_PORT_ENABLE: + x = UREAD2(sc, port); + UWRITE2(sc, port, x | UHCI_PORTSC_PE); + break; + case UHF_PORT_SUSPEND: + x = UREAD2(sc, port); + UWRITE2(sc, port, x | UHCI_PORTSC_SUSP); + break; + case UHF_PORT_RESET: + x = UREAD2(sc, port); + UWRITE2(sc, port, x | UHCI_PORTSC_PR); + usbd_delay_ms(&sc->sc_bus, 10); + UWRITE2(sc, port, x & ~UHCI_PORTSC_PR); + delay(100); + x = UREAD2(sc, port); + UWRITE2(sc, port, x | UHCI_PORTSC_PE); + delay(100); + DPRINTFN(3,("uhci port %d reset, status = 0x%04x\n", + index, UREAD2(sc, port))); + sc->sc_isreset = 1; + break; + case UHF_C_PORT_CONNECTION: + case UHF_C_PORT_ENABLE: + case UHF_C_PORT_OVER_CURRENT: + case UHF_PORT_CONNECTION: + case UHF_PORT_OVER_CURRENT: + case UHF_PORT_POWER: + case UHF_PORT_LOW_SPEED: + case UHF_C_PORT_SUSPEND: + case UHF_C_PORT_RESET: + default: + r = USBD_IOERROR; + goto ret; + } + break; + default: + r = USBD_IOERROR; + goto ret; + } + reqh->actlen = totlen; + r = USBD_NORMAL_COMPLETION; + ret: + reqh->status = r; + reqh->xfercb(reqh); + return (USBD_IN_PROGRESS); +} + +/* Abort a root control request. */ +void +uhci_root_ctrl_abort(reqh) + usbd_request_handle reqh; +{ + /* Nothing to do, all transfers are syncronous. */ +} + +/* Close the root pipe. */ +void +uhci_root_ctrl_close(pipe) + usbd_pipe_handle pipe; +{ +#if defined(__NetBSD__) + untimeout(uhci_timo, pipe->intrreqh); +#elif defined(__FreeBSD__) + untimeout(uhci_timo, pipe->intrreqh, pipe->intrreqh->callout_handler); +#endif + DPRINTF(("uhci_root_ctrl_close\n")); +} + +/* Abort a root interrupt request. */ +void +uhci_root_intr_abort(reqh) + usbd_request_handle reqh; +{ +#if defined(__NetBSD__) + untimeout(uhci_timo, reqh); +#elif defined(__FreeBSD__) + untimeout(uhci_timo, reqh, reqh->callout_handler); +#endif +} + +/* Start a transfer on the root interrupt pipe */ +usbd_status +uhci_root_intr_transfer(reqh) + usbd_request_handle reqh; +{ + usbd_pipe_handle pipe = reqh->pipe; + uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus; + struct uhci_pipe *upipe = (struct uhci_pipe *)pipe; + usb_dma_t *dmap; + usbd_status r; + int len; + + DPRINTFN(3, ("uhci_root_intr_transfer: reqh=%p buf=%p len=%d flags=%d\n", + reqh, reqh->buffer, reqh->length, reqh->flags)); + + len = reqh->length; + dmap = &upipe->u.intr.datadma; + if (len == 0) + return (USBD_INVAL); /* XXX should it be? */ + + r = usb_allocmem(sc->sc_dmatag, len, 0, dmap); + if (r != USBD_NORMAL_COMPLETION) + return (r); + + sc->sc_ival = MS_TO_TICKS(reqh->pipe->endpoint->edesc->bInterval); +#if defined(__NetBSD__) + timeout(uhci_timo, reqh, sc->sc_ival); +#elif defined(__FreeBSD__) + /* To avoid race conditions we first initialise the struct + * before we use it. The timeout happen between the setting + * of the timeout and the setting of callout_handle + */ + callout_handle_init(&reqh->callout_handler); + reqh->callout_handler = timeout(uhci_timo, reqh, sc->sc_ival); +#endif + return (USBD_IN_PROGRESS); +} + +/* Close the root interrupt pipe. */ +void +uhci_root_intr_close(pipe) + usbd_pipe_handle pipe; +{ +#if defined(__NetBSD__) + untimeout(uhci_timo, pipe->intrreqh); +#elif defined(__FreeBSD__) + untimeout(uhci_timo, pipe->intrreqh, pipe->intrreqh->callout_handler); +#endif + DPRINTF(("uhci_root_intr_close\n")); +} diff --git a/sys/dev/usb/uhcireg.h b/sys/dev/usb/uhcireg.h new file mode 100644 index 0000000..30c8700 --- /dev/null +++ b/sys/dev/usb/uhcireg.h @@ -0,0 +1,184 @@ +/* $NetBSD: uhcireg.h,v 1.2 1998/07/26 00:40:59 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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. + */ + +#ifndef _DEV_PCI_UHCIREG_H_ +#define _DEV_PCI_UHCIREG_H_ + +/*** PCI config registers ***/ + +#define PCI_USBREV 0x60 /* USB protocol revision */ +#define PCI_USBREV_MASK 0xff +#define PCI_USBREV_PRE_1_0 0x00 +#define PCI_USBREV_1_0 0x10 + +#define PCI_CBIO 0x20 /* configuration base IO */ + +#define PCI_INTERFACE_UHCI 0x00 + +/*** UHCI registers ***/ + +#define UHCI_CMD 0x00 +#define UHCI_CMD_RS 0x0001 +#define UHCI_CMD_HCRESET 0x0002 +#define UHCI_CMD_GRESET 0x0004 +#define UHCI_CMD_EGSM 0x0008 +#define UHCI_CMD_FGR 0x0010 +#define UHCI_CMD_SWDBG 0x0020 +#define UHCI_CMD_CF 0x0040 +#define UHCI_CMD_MAXP 0x0080 + +#define UHCI_STS 0x02 +#define UHCI_STS_USBINT 0x0001 +#define UHCI_STS_USBEI 0x0002 +#define UHCI_STS_RD 0x0004 +#define UHCI_STS_HSE 0x0008 +#define UHCI_STS_HCPE 0x0010 +#define UHCI_STS_HCH 0x0020 + +#define UHCI_INTR 0x04 +#define UHCI_INTR_TOCRCIE 0x0001 +#define UHCI_INTR_RIE 0x0002 +#define UHCI_INTR_IOCE 0x0004 +#define UHCI_INTR_SPIE 0x0008 + +#define UHCI_FRNUM 0x06 +#define UHCI_FRNUM_MASK 0x03ff + + +#define UHCI_FLBASEADDR 0x08 + +#define UHCI_SOF 0x0c +#define UHCI_SOF_MASK 0x7f + +#define UHCI_PORTSC1 0x010 +#define UHCI_PORTSC2 0x012 +#define UHCI_PORTSC_CCS 0x0001 +#define UHCI_PORTSC_CSC 0x0002 +#define UHCI_PORTSC_PE 0x0004 +#define UHCI_PORTSC_POEDC 0x0008 +#define UHCI_PORTSC_LS 0x0030 +#define UHCI_PORTSC_LS_SHIFT 4 +#define UHCI_PORTSC_RD 0x0040 +#define UHCI_PORTSC_LSDA 0x0100 +#define UHCI_PORTSC_PR 0x0200 +#define UHCI_PORTSC_OCI 0x0400 +#define UHCI_PORTSC_OCIC 0x0800 +#define UHCI_PORTSC_SUSP 0x1000 + +#define UHCI_FRAMELIST_COUNT 1024 +#define UHCI_FRAMELIST_ALIGN 4096 + +#define UHCI_TD_ALIGN 16 +#define UHCI_QH_ALIGN 16 + +typedef u_int32_t uhci_physaddr_t; +#define UHCI_PTR_T 0x00000001 +#define UHCI_PTR_Q 0x00000002 +#define UHCI_PTR_VF 0x00000004 + +typedef union { + struct uhci_soft_qh *sqh; + struct uhci_soft_td *std; +} uhci_soft_td_qh_t; + +/* + * The Queue Heads and Transfer Descriptors and accessed + * by both the CPU and the USB controller which runs + * concurrently. This means that they have to be accessed + * with great care. As long as the data structures are + * not linked into the controller's frame list they cannot + * be accessed by it and anything goes. As soon as a + * TD is accessible by the controller it "owns" the td_status + * field; it will not be written by the CPU. Similarly + * the controller "owns" the qh_elink field. + */ + +typedef struct { + uhci_physaddr_t td_link; + u_int32_t td_status; +#define UHCI_TD_GET_ACTLEN(s) (((s) + 1) & 0x3ff) +#define UHCI_TD_ZERO_ACTLEN(t) ((t) | 0x3ff) +#define UHCI_TD_BITSTUFF 0x00020000 +#define UHCI_TD_CRCTO 0x00040000 +#define UHCI_TD_NAK 0x00080000 +#define UHCI_TD_BABBLE 0x00100000 +#define UHCI_TD_DBUFFER 0x00200000 +#define UHCI_TD_STALLED 0x00400000 +#define UHCI_TD_ACTIVE 0x00800000 +#define UHCI_TD_IOC 0x01000000 +#define UHCI_TD_IOS 0x02000000 +#define UHCI_TD_LS 0x04000000 +#define UHCI_TD_GET_ERRCNT(s) (((s) >> 27) & 3) +#define UHCI_TD_SET_ERRCNT(n) ((n) << 27) +#define UHCI_TD_SPD 0x20000000 + u_int32_t td_token; +#define UHCI_TD_PID_IN 0x00000069 +#define UHCI_TD_PID_OUT 0x000000e1 +#define UHCI_TD_PID_SETUP 0x0000002d +#define UHCI_TD_GET_PID(s) ((s) & 0xff) +#define UHCI_TD_SET_DEVADDR(a) ((a) << 8) +#define UHCI_TD_GET_DEVADDR(s) (((s) >> 8) & 0x7f) +#define UHCI_TD_SET_ENDPT(e) ((e) << 15) +#define UHCI_TD_GET_ENDPT(s) (((s) >> 15) & 0xf) +#define UHCI_TD_SET_DT(t) ((t) << 19) +#define UHCI_TD_GET_DT(s) (((s) >> 19) & 1) +#define UHCI_TD_SET_MAXLEN(l) (((l)-1) << 21) +#define UHCI_TD_GET_MAXLEN(s) ((((s) >> 21) + 1) & 0x7ff) +#define UHCI_TD_MAXLEN_MASK 0xffe00000 + u_int32_t td_buffer; + uhci_soft_td_qh_t link; /* soft version of the td_link field */ + /* padding to 32 bytes */ +} uhci_td_t; +#define UHCI_TD_SIZE 32 + +#define UHCI_TD_ERROR (UHCI_TD_BITSTUFF|UHCI_TD_CRCTO|UHCI_TD_BABBLE|UHCI_TD_DBUFFER|UHCI_TD_STALLED) + +#define UHCI_TD_SETUP(len, endp, dev) (UHCI_TD_SET_MAXLEN(len) | UHCI_TD_SET_ENDPT(endp) | UHCI_TD_SET_DEVADDR(dev) | UHCI_TD_PID_SETUP) +#define UHCI_TD_OUT(len, endp, dev, dt) (UHCI_TD_SET_MAXLEN(len) | UHCI_TD_SET_ENDPT(endp) | UHCI_TD_SET_DEVADDR(dev) | UHCI_TD_PID_OUT | UHCI_TD_SET_DT(dt)) +#define UHCI_TD_IN(len, endp, dev, dt) (UHCI_TD_SET_MAXLEN(len) | UHCI_TD_SET_ENDPT(endp) | UHCI_TD_SET_DEVADDR(dev) | UHCI_TD_PID_IN | UHCI_TD_SET_DT(dt)) + +typedef struct { + uhci_physaddr_t qh_hlink; + uhci_physaddr_t qh_elink; + struct uhci_soft_qh *hlink; /* soft version of qh_hlink */ + struct uhci_soft_td *elink; /* soft version of qh_elink */ + /* padding to 32 bytes */ +} uhci_qh_t; +#define UHCI_QH_SIZE 32 + +#endif /* _DEV_PCI_UHCIREG_H_ */ diff --git a/sys/dev/usb/uhcivar.h b/sys/dev/usb/uhcivar.h new file mode 100644 index 0000000..b7102f3 --- /dev/null +++ b/sys/dev/usb/uhcivar.h @@ -0,0 +1,179 @@ +/* $NetBSD: uhcivar.h,v 1.3 1998/07/26 00:40:59 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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. + */ + +/* + * To avoid having 1024 TDs for each isochronous transfer we introduce + * a virtual frame list. Every UHCI_VFRAMELIST_COUNT entries in the real + * frame list points to a non-active TD. These, in turn, which form the + * starts of the virtual frame list. This also has the advantage that it + * simplifies linking in/out TD/QH in the schedule. + * Furthermore, initially each of the inactive TDs point to an inactive + * QH that forms the start of the interrupt traffic for that slot. + * Each of these QHs point to the same QH that is the start of control + * traffic. + * + * UHCI_VFRAMELIST_COUNT should be a power of 2 and <= UHCI_FRAMELIST_COUNT. + */ +#define UHCI_VFRAMELIST_COUNT 128 + +typedef struct uhci_soft_qh uhci_soft_qh_t; +typedef struct uhci_soft_td uhci_soft_td_t; + +/* + * An interrupt info struct contains the information needed to + * execute a requested routine when the controller generates an + * interrupt. Since we cannot know which transfer generated + * the interrupt all structs are linked together so they can be + * searched at interrupt time. + */ +typedef struct uhci_intr_info { +#if defined(__FreeBSD__) + struct callout_handle callout_handler; +#endif + struct uhci_softc *sc; + usbd_request_handle reqh; + uhci_soft_td_t *stdstart; + uhci_soft_td_t *stdend; + LIST_ENTRY(uhci_intr_info) list; +#ifdef DIAGNOSTIC + int isdone; +#endif +} uhci_intr_info_t; + +/* + * Extra information that we need for a TD. + */ +struct uhci_soft_td { + uhci_td_t *td; /* The real TD */ + uhci_physaddr_t physaddr; /* and its physical address. */ +}; +#define UHCI_TD_CHUNK 128 /*(PAGE_SIZE / UHCI_TD_SIZE)*/ + +/* + * Extra information that we need for a QH. + */ +struct uhci_soft_qh { + uhci_qh_t *qh; /* The real QH */ + uhci_physaddr_t physaddr; /* and its physical address. */ + int pos; /* Timeslot position */ + uhci_intr_info_t *intr_info; /* Who to call on completion. */ +}; +#define UHCI_QH_CHUNK 128 /*(PAGE_SIZE / UHCI_QH_SIZE)*/ + +/* Only used for buffer free list. */ +struct uhci_buffer { + struct uhci_buffer *next; +}; +#define UHCI_BUFFER_SIZE 64 +#define UHCI_BUFFER_CHUNK 64 /*(PAGE_SIZE / UHCI_BUFFER_SIZE)*/ + +/* + * Information about an entry in the virtial frame list. + */ +struct uhci_vframe { + uhci_soft_td_t *htd; /* pointer to dummy TD */ + uhci_soft_td_t *etd; /* pointer to last TD */ + uhci_soft_qh_t *hqh; /* pointer to dummy QH */ + uhci_soft_qh_t *eqh; /* pointer to last QH */ + u_int bandwidth; /* max bandwidth used by this frame */ +}; + +typedef struct uhci_softc { + struct usbd_bus sc_bus; /* base device */ +#if defined(__NetBSD__) + void *sc_ih; /* interrupt vectoring */ + bus_space_tag_t iot; + bus_space_handle_t ioh; + + bus_dma_tag_t sc_dmatag; /* DMA tag */ + /* XXX should keep track of all DMA memory */ +#elif defined(__FreeBSD__) + int sc_iobase; + int sc_int; + int unit; +#endif + + uhci_physaddr_t *sc_pframes; + struct uhci_vframe sc_vframes[UHCI_VFRAMELIST_COUNT]; + + uhci_soft_qh_t *sc_ctl_start; /* dummy QH for control */ + uhci_soft_qh_t *sc_ctl_end; /* last control QH */ + uhci_soft_qh_t *sc_bulk_start; /* dummy QH for bulk */ + uhci_soft_qh_t *sc_bulk_end; /* last bulk transfer */ + + uhci_soft_td_t *sc_freetds; + uhci_soft_qh_t *sc_freeqhs; + struct uhci_buffer *sc_freebuffers; + + u_int8_t sc_addr; /* device address */ + u_int8_t sc_conf; /* device configuration */ + + char sc_isreset; + + int sc_intrs; + LIST_HEAD(, uhci_intr_info) sc_intrhead; + + /* Info for the root hub interrupt channel. */ + int sc_ival; + + char sc_vflock; +#define UHCI_HAS_LOCK 1 +#define UHCI_WANT_LOCK 2 + +#if defined(__NetBSD__) + usb_dma_t *sc_mallocs; +#endif + + char sc_vendor[16]; +} uhci_softc_t; + +usbd_status uhci_init __P((uhci_softc_t *)); +int uhci_intr __P((void *)); +#if 0 +void uhci_reset __P((void *)); +#endif + +#ifdef USB_DEBUG +#define DPRINTF(x) if (uhcidebug) printf x +#define DPRINTFN(n,x) if (uhcidebug>(n)) printf x +extern int uhcidebug; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + diff --git a/sys/dev/usb/uhid.c b/sys/dev/usb/uhid.c new file mode 100644 index 0000000..34debf6 --- /dev/null +++ b/sys/dev/usb/uhid.c @@ -0,0 +1,632 @@ +/* $NetBSD: uhid.c,v 1.3 1998/08/01 20:52:45 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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 <dev/usb/usb_port.h> + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#if defined(__NetBSD__) +#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> +#endif +#include <sys/tty.h> +#include <sys/file.h> +#include <sys/select.h> +#include <sys/proc.h> +#include <sys/vnode.h> +#include <sys/poll.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbhid.h> + +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usbdivar.h> +#include <dev/usb/hid.h> +#include <dev/usb/usb_quirks.h> + +#ifdef USB_DEBUG +#define DPRINTF(x) if (uhiddebug) printf x +#define DPRINTFN(n,x) if (uhiddebug>(n)) printf x +int uhiddebug = 0; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +struct uhid_softc { + bdevice sc_dev; /* base device */ + usbd_interface_handle sc_iface; /* interface */ + usbd_pipe_handle sc_intrpipe; /* interrupt pipe */ + int sc_ep_addr; + + int sc_isize; + int sc_osize; + int sc_fsize; + u_int8_t sc_iid; + u_int8_t sc_oid; + u_int8_t sc_fid; + + char *sc_ibuf; + char *sc_obuf; + + void *sc_repdesc; + int sc_repdesc_size; + + struct clist sc_q; + struct selinfo sc_rsel; + u_char sc_state; /* driver state */ +#define UHID_OPEN 0x01 /* device is open */ +#define UHID_ASLP 0x02 /* waiting for mouse data */ +#define UHID_NEEDCLEAR 0x04 /* needs clearing endpoint stall */ +#define UHID_IMMED 0x08 /* return read data immediately */ + int sc_disconnected; /* device is gone */ +}; + +#define UHIDUNIT(dev) (minor(dev)) +#define UHID_CHUNK 128 /* chunk size for read */ +#define UHID_BSIZE 1020 /* buffer size */ + +#if defined(__NetBSD__) +int uhid_match __P((struct device *, struct cfdata *, void *)); +void uhid_attach __P((struct device *, struct device *, void *)); +#elif defined(__FreeBSD__) +static device_probe_t uhid_match; +static device_attach_t uhid_attach; +#endif + +int uhidopen __P((dev_t, int, int, struct proc *)); +int uhidclose __P((dev_t, int, int, struct proc *p)); +int uhidread __P((dev_t, struct uio *uio, int)); +int uhidwrite __P((dev_t, struct uio *uio, int)); +int uhidioctl __P((dev_t, u_long, caddr_t, int, struct proc *)); +int uhidpoll __P((dev_t, int, struct proc *)); +void uhid_intr __P((usbd_request_handle, usbd_private_handle, usbd_status)); +void uhid_disco __P((void *)); + +#if defined(__NetBSD__) +extern struct cfdriver uhid_cd; + +struct cfattach uhid_ca = { + sizeof(struct uhid_softc), uhid_match, uhid_attach +}; +#elif defined(__FreeBSD__) + +static devclass_t uhid_devclass; + +static device_method_t uhid_methods[] = { + DEVMETHOD(device_probe, uhid_match), + DEVMETHOD(device_attach, uhid_attach), + {0,0} +}; + +static driver_t uhid_driver = { + "uhid", + uhid_methods, + DRIVER_TYPE_MISC, + sizeof(struct uhid_softc) +}; +#endif + + +#if defined(__NetBSD__) +int +uhid_match(parent, match, aux) + struct device *parent; + struct cfdata *match; + void *aux; +{ + struct usb_attach_arg *uaa = aux; +#elif defined(__FreeBSD__) +static int +uhid_match(device_t device) +{ + struct usb_attach_arg *uaa = device_get_ivars(device); +#endif + usb_interface_descriptor_t *id; + + if (!uaa->iface) + return (UMATCH_NONE); + id = usbd_get_interface_descriptor(uaa->iface); + if (!id || id->bInterfaceClass != UCLASS_HID) + return (UMATCH_NONE); + return (UMATCH_IFACECLASS_GENERIC); +} + +#if defined(__NetBSD__) +void +uhid_attach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct uhid_softc *sc = (struct uhid_softc *)self; + struct usb_attach_arg *uaa = aux; +#elif defined(__FreeBSD__) +static int +uhid_attach(device_t self) +{ + struct uhid_softc *sc = device_get_softc(self); + struct usb_attach_arg *uaa = device_get_ivars(self); +#endif + usbd_interface_handle iface = uaa->iface; + usb_interface_descriptor_t *id; + usb_endpoint_descriptor_t *ed; + int size; + void *desc; + usbd_status r; + char devinfo[1024]; + + sc->sc_disconnected = 1; + sc->sc_iface = iface; + id = usbd_get_interface_descriptor(iface); + usbd_devinfo(uaa->device, 0, devinfo); +#if defined(__FreeBSD__) + usb_device_set_desc(self, devinfo); + printf("%s%d", device_get_name(self), device_get_unit(self)); +#endif + printf(": %s (interface class %d/%d)\n", devinfo, + id->bInterfaceClass, id->bInterfaceSubClass); + sc->sc_dev = self; + + ed = usbd_interface2endpoint_descriptor(iface, 0); + if (!ed) { + DEVICE_ERROR(sc->sc_dev, ("could not read endpoint descriptor\n")); + 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, + ed->bEndpointAddress & UE_IN ? "in" : "out", + ed->bmAttributes & UE_XFERTYPE, + UGETW(ed->wMaxPacketSize), ed->bInterval)); + + if ((ed->bEndpointAddress & UE_IN) != UE_IN || + (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) { + DEVICE_ERROR(sc->sc_dev, ("unexpected endpoint\n")); + ATTACH_ERROR_RETURN; + } + + sc->sc_ep_addr = ed->bEndpointAddress; + sc->sc_disconnected = 0; + + r = usbd_alloc_report_desc(uaa->iface, &desc, &size, M_USB); + if (r != USBD_NORMAL_COMPLETION) { + DEVICE_ERROR(sc->sc_dev, ("no report descriptor\n")); + 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_repdesc = desc; + sc->sc_repdesc_size = size; + + ATTACH_SUCCESS_RETURN; +} + +void +uhid_disco(p) + void *p; +{ + struct uhid_softc *sc = p; + + DPRINTF(("ums_hid: sc=%p\n", sc)); + usbd_abort_pipe(sc->sc_intrpipe); + sc->sc_disconnected = 1; +} + +void +uhid_intr(reqh, addr, status) + usbd_request_handle reqh; + usbd_private_handle addr; + usbd_status status; +{ + struct uhid_softc *sc = addr; + + DPRINTFN(5, ("uhid_intr: status=%d\n", status)); + DPRINTFN(5, ("uhid_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 != 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); + + if (sc->sc_state & UHID_ASLP) { + sc->sc_state &= ~UHID_ASLP; + DPRINTFN(5, ("uhid_intr: waking %p\n", sc)); + wakeup((caddr_t)sc); + } + selwakeup(&sc->sc_rsel); +} + +int +uhidopen(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + usbd_status r; +#if defined(__NetBSD__) + struct uhid_softc *sc; + int unit = UHIDUNIT(dev); + + if (unit >= uhid_cd.cd_ndevs) + return ENXIO; + sc = uhid_cd.cd_devs[unit]; +#elif defined(__FreeBSD__) + struct uhid_softc *sc = devclass_get_softc(uhid_devclass, UHIDUNIT(dev)); +#endif + if (!sc) + return ENXIO; + + DPRINTF(("uhidopen: sc=%p, disco=%d\n", sc, sc->sc_disconnected)); + + if (sc->sc_disconnected) + return (EIO); + + if (sc->sc_state & UHID_OPEN) + return EBUSY; + +#if defined(__NetBSD__) + if (clalloc(&sc->sc_q, UHID_BSIZE, 0) == -1) + return ENOMEM; +#elif defined(__FreeBSD__) + clist_alloc_cblocks(&sc->sc_q, UHID_BSIZE, 0); +#endif + + sc->sc_state |= UHID_OPEN; + sc->sc_state &= ~UHID_IMMED; + + sc->sc_ibuf = malloc(sc->sc_isize, M_USB, M_WAITOK); + sc->sc_obuf = malloc(sc->sc_osize, M_USB, M_WAITOK); + + /* Set up interrupt pipe. */ + r = 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); + if (r != USBD_NORMAL_COMPLETION) { + DPRINTF(("uhidopen: usbd_open_pipe_intr failed, error=%d\n",r)); + sc->sc_state &= ~UHID_OPEN; + return (EIO); + } + usbd_set_disco(sc->sc_intrpipe, uhid_disco, sc); + + return 0; +} + +int +uhidclose(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ +#if defined(__NetBSD__) + struct uhid_softc *sc; + int unit = UHIDUNIT(dev); + + if (unit >= uhid_cd.cd_ndevs) + return ENXIO; + sc = uhid_cd.cd_devs[unit]; +#elif defined(__FreeBSD__) + struct uhid_softc *sc = devclass_get_softc(uhid_devclass, UHIDUNIT(dev)); +#endif + + if (sc->sc_disconnected) + return (EIO); + + DPRINTF(("uhidclose: sc=%p\n", sc)); + + /* Disable interrupts. */ + usbd_abort_pipe(sc->sc_intrpipe); + usbd_close_pipe(sc->sc_intrpipe); + + sc->sc_state &= ~UHID_OPEN; + +#if defined(__NetBSD__) + clfree(&sc->sc_q); +#elif defined(__FreeBSD__) + clist_free_cblocks(&sc->sc_q); +#endif + + free(sc->sc_ibuf, M_USB); + free(sc->sc_obuf, M_USB); + + return 0; +} + +int +uhidread(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + int s; + int error = 0; + size_t length; + u_char buffer[UHID_CHUNK]; + usbd_status r; +#if defined(__NetBSD__) + struct uhid_softc *sc; + int unit = UHIDUNIT(dev); + + if (unit >= uhid_cd.cd_ndevs) + return ENXIO; + sc = uhid_cd.cd_devs[unit]; +#elif defined(__FreeBSD__) + struct uhid_softc *sc = devclass_get_softc(uhid_devclass, UHIDUNIT(dev)); +#endif + + if (sc->sc_disconnected) + return (EIO); + + DPRINTFN(1, ("uhidread\n")); + if (sc->sc_state & UHID_IMMED) { + DPRINTFN(1, ("uhidread immed\n")); + + r = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT, + sc->sc_iid, sc->sc_ibuf, sc->sc_isize); + if (r != USBD_NORMAL_COMPLETION) + return (EIO); + return (uiomove(buffer, sc->sc_isize, uio)); + } + + s = spltty(); + while (sc->sc_q.c_cc == 0) { + if (flag & IO_NDELAY) { + splx(s); + return EWOULDBLOCK; + } + sc->sc_state |= UHID_ASLP; + DPRINTFN(5, ("uhidread: sleep on %p\n", sc)); + error = tsleep((caddr_t)sc, PZERO | PCATCH, "uhidrea", 0); + DPRINTFN(5, ("uhidread: woke, error=%d\n", error)); + if (error) { + sc->sc_state &= ~UHID_ASLP; + splx(s); + return (error); + } + 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); + + /* Transfer as many chunks as possible. */ + while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0) { + length = min(sc->sc_q.c_cc, uio->uio_resid); + if (length > sizeof(buffer)) + length = sizeof(buffer); + + /* Remove a small chunk from the input queue. */ + (void) q_to_b(&sc->sc_q, buffer, length); + DPRINTFN(5, ("uhidread: got %d chars\n", length)); + + /* Copy the data to the user process. */ + if ((error = uiomove(buffer, length, uio)) != 0) + break; + } + + return (error); +} + +int +uhidwrite(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + int error; + int size; + usbd_status r; +#if defined(__NetBSD__) + struct uhid_softc *sc; + int unit = UHIDUNIT(dev); + + if (unit >= uhid_cd.cd_ndevs) + return ENXIO; + sc = uhid_cd.cd_devs[unit]; +#elif defined(__FreeBSD__) + struct uhid_softc *sc = devclass_get_softc(uhid_devclass, UHIDUNIT(dev)); +#endif + + if (sc->sc_disconnected) + return (EIO); + + DPRINTFN(1, ("uhidwrite\n")); + + size = sc->sc_osize; + error = 0; + while (uio->uio_resid > 0) { + if (uio->uio_resid != size) + return (0); + if ((error = uiomove(sc->sc_obuf, size, uio)) != 0) + break; + if (sc->sc_oid) + r = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT, + sc->sc_obuf[0], + sc->sc_obuf+1, size-1); + else + r = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT, + 0, sc->sc_obuf, size); + if (r != USBD_NORMAL_COMPLETION) { + error = EIO; + break; + } + } + return (error); +} + +int +uhidioctl(dev, cmd, addr, flag, p) + dev_t dev; + u_long cmd; + caddr_t addr; + int flag; + struct proc *p; +{ + struct usb_ctl_report_desc *rd; + struct usb_ctl_report *re; + int size, id; + usbd_status r; +#if defined(__NetBSD__) + struct uhid_softc *sc; + int unit = UHIDUNIT(dev); + + if (unit >= uhid_cd.cd_ndevs) + return ENXIO; + sc = uhid_cd.cd_devs[unit]; +#elif defined(__FreeBSD__) + struct uhid_softc *sc = devclass_get_softc(uhid_devclass, UHIDUNIT(dev)); +#endif + + if (sc->sc_disconnected) + return (EIO); + + DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd)); + switch (cmd) { + case FIONBIO: + /* All handled in the upper FS layer. */ + break; + + case USB_GET_REPORT_DESC: + rd = (struct usb_ctl_report_desc *)addr; + size = min(sc->sc_repdesc_size, sizeof rd->data); + rd->size = size; + memcpy(rd->data, sc->sc_repdesc, size); + break; + + case USB_SET_IMMED: + if (*(int *)addr) + sc->sc_state |= UHID_IMMED; + else + sc->sc_state &= ~UHID_IMMED; + break; + + case USB_GET_REPORT: + re = (struct usb_ctl_report *)addr; + switch (re->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); + } + r = usbd_get_report(sc->sc_iface, re->report, id, + re->data, size); + if (r != USBD_NORMAL_COMPLETION) + return (EIO); + break; + + default: + return (EINVAL); + } + return (0); +} + +int +uhidpoll(dev, events, p) + dev_t dev; + int events; + struct proc *p; +{ + int revents = 0; + int s; +#if defined(__NetBSD__) + struct uhid_softc *sc; + int unit = UHIDUNIT(dev); + + if (unit >= uhid_cd.cd_ndevs) + return ENXIO; + sc = uhid_cd.cd_devs[unit]; +#elif defined(__FreeBSD__) + struct uhid_softc *sc = devclass_get_softc(uhid_devclass, UHIDUNIT(dev)); +#endif + + if (sc->sc_disconnected) + return (EIO); + + s = spltty(); + if (events & (POLLOUT | POLLWRNORM)) + revents |= events & (POLLOUT | POLLWRNORM); + if (events & (POLLIN | POLLRDNORM)) { + if (sc->sc_q.c_cc > 0) + revents |= events & (POLLIN | POLLRDNORM); + else + selrecord(p, &sc->sc_rsel); + } + + splx(s); + return (revents); +} + +#if defined(__FreeBSD__) +DRIVER_MODULE(uhid, usb, uhid_driver, uhid_devclass, usb_driver_load, 0); +#endif diff --git a/sys/dev/usb/uhub.c b/sys/dev/usb/uhub.c new file mode 100644 index 0000000..a2f759f --- /dev/null +++ b/sys/dev/usb/uhub.c @@ -0,0 +1,490 @@ +/* $NetBSD: uhub.c,v 1.5 1998/08/02 22:30:52 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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 <dev/usb/usb_port.h> + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#if defined(__NetBSD__) +#include <sys/device.h> +#elif defined(__FreeBSD__) +#include <sys/module.h> +#include <sys/bus.h> +#endif +#include <sys/proc.h> + +#include <dev/usb/usb.h> + +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usbdivar.h> + +#ifdef USB_DEBUG +#define DPRINTF(x) if (usbdebug) printf x +#define DPRINTFN(n,x) if (usbdebug>(n)) printf x +extern int usbdebug; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +struct uhub_softc { + bdevice sc_dev; /* base device */ + usbd_device_handle sc_hub; /* USB device */ + usbd_pipe_handle sc_ipipe; /* interrupt pipe */ + u_int8_t sc_status[1]; /* XXX more ports */ + u_char sc_running; +}; + +#if defined(__NetBSD__) +int uhub_match __P((struct device *, struct cfdata *, void *)); +void uhub_attach __P((struct device *, struct device *, void *)); +#elif defined(__FreeBSD__) +static device_probe_t uhub_match; +static device_attach_t uhub_attach; +#endif + +usbd_status uhub_init_port __P((int, struct usbd_port *, usbd_device_handle)); +void uhub_disconnect __P((struct usbd_port *up, int portno)); +usbd_status uhub_explore __P((usbd_device_handle hub)); +void uhub_intr __P((usbd_request_handle, usbd_private_handle, usbd_status)); + +/*void uhub_disco __P((void *));*/ + +#if defined(__NetBSD__) +extern struct cfdriver uhub_cd; + +struct cfattach uhub_ca = { + sizeof(struct uhub_softc), uhub_match, uhub_attach +}; + +struct cfattach uhub_uhub_ca = { + sizeof(struct uhub_softc), uhub_match, uhub_attach +}; + +#elif defined(__FreeBSD__) +static devclass_t uhub_devclass; + +static device_method_t uhub_methods[] = { + DEVMETHOD(device_probe, uhub_match), + DEVMETHOD(device_attach, uhub_attach), + {0,0} +}; + +static driver_t uhub_driver = { + "usb", /* this is silly, but necessary. The uhub + * implements a usb bus on top of a usb bus, + * but the problem is that name of the driver + * is used a the name of the device class it + * implements. + */ + uhub_methods, + DRIVER_TYPE_MISC, + sizeof(struct uhub_softc) +}; +#endif + +#if defined(__NetBSD__) +int +uhub_match(parent, match, aux) + struct device *parent; + struct cfdata *match; + void *aux; +{ + struct usb_attach_arg *uaa = aux; +#elif defined(__FreeBSD__) +static int +uhub_match(device_t device) +{ + struct usb_attach_arg *uaa = device_get_ivars(device); +#endif + usb_device_descriptor_t *dd = usbd_get_device_descriptor(uaa->device); + + DPRINTFN(1,("uhub_match, dd=%p\n", dd)); + /* + * The subclass for hubs seems to be 0 for some and 1 for others, + * so we just ignore the subclass. + */ + if (uaa->iface == 0 && dd->bDeviceClass == UCLASS_HUB) + return (UMATCH_DEVCLASS_DEVSUBCLASS); + return (UMATCH_NONE); +} + +#if defined(__NetBSD__) +void +uhub_attach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct uhub_softc *sc = (struct uhub_softc *)self; + struct usb_attach_arg *uaa = aux; +#elif defined(__FreeBSD__) +static int +uhub_attach(device_t self) +{ + struct uhub_softc *sc = device_get_softc(self); + struct usb_attach_arg *uaa = device_get_ivars(self); +#endif + usbd_device_handle dev = uaa->device; + char devinfo[1024]; + usbd_status r; + struct usbd_hub *hub; + usb_device_request_t req; + usb_hub_descriptor_t hubdesc; + int port, nports; + usbd_interface_handle iface; + usb_endpoint_descriptor_t *ed; + + DPRINTFN(10,("uhub_attach\n")); + sc->sc_hub = dev; + usbd_devinfo(dev, 1, devinfo); +#if defined(__FreeBSD__) + usb_device_set_desc(self, devinfo); + printf("%s%d", device_get_name(self), device_get_unit(self)); +#endif + printf(": %s\n", devinfo); + sc->sc_dev = self; + + r = usbd_set_config_no(dev, 0, 1); + if (r != USBD_NORMAL_COMPLETION) { + DEVICE_ERROR(sc->sc_dev, ("configuration failed, error=%d\n", r)); + ATTACH_ERROR_RETURN; + } + + if (dev->depth > USB_HUB_MAX_DEPTH) { + DEVICE_ERROR(sc->sc_dev, ("hub depth (%d) exceeded, hub ignored\n", + USB_HUB_MAX_DEPTH)); + ATTACH_ERROR_RETURN; + } + + /* Get hub descriptor. */ + req.bmRequestType = UT_READ_CLASS_DEVICE; + req.bRequest = UR_GET_DESCRIPTOR; + USETW(req.wValue, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE); + DPRINTFN(1,("usb_init_hub: getting hub descriptor\n")); + /* XXX not correct for hubs with >7 ports */ + r = usbd_do_request(dev, &req, &hubdesc); + if (r != USBD_NORMAL_COMPLETION) { + DEVICE_ERROR(sc->sc_dev, ("getting hub descriptor failed, error=%d\n", r)); + ATTACH_ERROR_RETURN; + } + + /* XXX block should be moved down to avoid memory leaking (or an overdose of free()'s) */ + nports = hubdesc.bNbrPorts; + hub = malloc(sizeof(*hub) + (nports-1) * sizeof(struct usbd_port), + M_USB, M_NOWAIT); + if (hub == 0) + ATTACH_ERROR_RETURN; + dev->hub = hub; + dev->hub->hubdata = sc; + hub->explore = uhub_explore; + hub->hubdesc = hubdesc; + hub->nports = nports; + + DPRINTFN(1,("usbhub_init_hub: selfpowered=%d, parent=%p, parent->selfpowered=%d\n", + dev->self_powered, dev->powersrc->parent, + dev->powersrc->parent ? + dev->powersrc->parent->self_powered : 0)); + if (!dev->self_powered && dev->powersrc->parent && + !dev->powersrc->parent->self_powered) { + DEVICE_ERROR(sc->sc_dev, ("bus powered hub connected to bus powered hub, ignored\n")); + ATTACH_ERROR_RETURN; + } + + /* Set up interrupt pipe. */ + r = usbd_device2interface_handle(dev, 0, &iface); + if (r != USBD_NORMAL_COMPLETION) { + DEVICE_ERROR(sc->sc_dev, ("no interface handle\n")); + ATTACH_ERROR_RETURN; + } + ed = usbd_interface2endpoint_descriptor(iface, 0); + if (ed == 0) { + DEVICE_ERROR(sc->sc_dev, ("no endpoint descriptor\n")); + ATTACH_ERROR_RETURN; + } + if ((ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) { + DEVICE_ERROR(sc->sc_dev, ("bad interrupt endpoint\n")); + ATTACH_ERROR_RETURN; + } + + r = usbd_open_pipe_intr(iface, ed->bEndpointAddress,USBD_SHORT_XFER_OK, + &sc->sc_ipipe, sc, sc->sc_status, + sizeof(sc->sc_status), + uhub_intr); + if (r != USBD_NORMAL_COMPLETION) { + DEVICE_ERROR(sc->sc_dev, ("cannot open interrupt pipe\n")); + ATTACH_ERROR_RETURN; + } + + for (port = 1; port <= nports; port++) { + r = uhub_init_port(port, &hub->ports[port-1], dev); + if (r != USBD_NORMAL_COMPLETION) + DEVICE_ERROR(sc->sc_dev, ("init of port %d failed\n", port)); + } + sc->sc_running = 1; + + ATTACH_SUCCESS_RETURN; +} + +#if defined(__NetBSD__) +static int +uhub_detach(self) + struct device *self; +{ + struct uhub_softc *sc = (struct uhub_softc *)self; +#elif defined(__FreeBSD__) +static int +uhub_detach(device_t self) +{ + struct uhub_softc *sc = device_get_softc(self); +#endif + int nports = sc->sc_hub->hub->hubdesc.bNbrPorts; + int port; + + for (port = 1; port <= nports; port++) { + if (sc->sc_hub->hub->ports[port-1].device) + uhub_disconnect(&sc->sc_hub->hub->ports[port-1], port); + } + + free(sc->sc_hub->hub, M_USB); + + return 0; +} + +usbd_status +uhub_init_port(port, uport, dev) + int port; + struct usbd_port *uport; + usbd_device_handle dev; +{ + usbd_status r; + u_int16_t pstatus; + + uport->device = 0; + uport->parent = dev; + r = usbd_get_port_status(dev, port, &uport->status); + if (r != USBD_NORMAL_COMPLETION) + return r; + pstatus = UGETW(uport->status.wPortStatus); + DPRINTF(("usbd_init_port: adding hub port=%d status=0x%04x change=0x%04x\n", + port, pstatus, UGETW(uport->status.wPortChange))); + if ((pstatus & UPS_PORT_POWER) == 0) { + /* Port lacks power, turn it on */ + r = usbd_set_port_feature(dev, port, UHF_PORT_POWER); + if (r != USBD_NORMAL_COMPLETION) + return (r); + r = usbd_get_port_status(dev, port, &uport->status); + if (r != USBD_NORMAL_COMPLETION) + return (r); + DPRINTF(("usb_init_port: turn on port %d power status=0x%04x change=0x%04x\n", + port, UGETW(uport->status.wPortStatus), + UGETW(uport->status.wPortChange))); + /* Wait for stable power. */ + usbd_delay_ms(dev->bus, dev->hub->hubdesc.bPwrOn2PwrGood * + UHD_PWRON_FACTOR); + } + if (dev->self_powered) + /* Self powered hub, give ports maximum current. */ + uport->power = USB_MAX_POWER; + else + uport->power = USB_MIN_POWER; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +uhub_explore(dev) + usbd_device_handle dev; +{ + usb_hub_descriptor_t *hd = &dev->hub->hubdesc; + struct uhub_softc *sc = dev->hub->hubdata; + struct usbd_port *up; + usbd_status r; + int port; + int change, status; + + DPRINTFN(10, ("uhub_explore dev=%p addr=%d\n", dev, dev->address)); + + if (!sc->sc_running) + return (USBD_NOT_STARTED); + + /* Ignore hubs that are too deep. */ + if (dev->depth > USB_HUB_MAX_DEPTH) + return (USBD_TOO_DEEP); + + for(port = 1; port <= hd->bNbrPorts; port++) { + up = &dev->hub->ports[port-1]; + r = usbd_get_port_status(dev, port, &up->status); + if (r != USBD_NORMAL_COMPLETION) { + DPRINTF(("uhub_explore: get port status failed, error=%d\n", + r)); + continue; + } + status = UGETW(up->status.wPortStatus); + change = UGETW(up->status.wPortChange); + DPRINTFN(5, ("uhub_explore: port %d status 0x%04x 0x%04x\n", + port, status, change)); + if (!(change & UPS_CURRENT_CONNECT_STATUS)) { + /* No status change, just do recursive explore. */ + if (up->device && up->device->hub) + up->device->hub->explore(up->device); + continue; + } + DPRINTF(("uhub_explore: status change hub=%d port=%d\n", + dev->address, port)); + usbd_clear_port_feature(dev, port, UHF_C_PORT_CONNECTION); + usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE); + /* + * If there is already a device on the port the change status + * must mean that is has disconnected. Looking at the + * current connect status is not enough to figure this out + * since a new unit may have been connected before we handle + * the disconnect. + */ + if (up->device) { + /* Disconnected */ + DPRINTF(("uhub_explore: device %d disappeared on port %d\n", + up->device->address, port)); + uhub_disconnect(up, port); + usbd_clear_port_feature(dev, port, + UHF_C_PORT_CONNECTION); + } + if (!(status & UPS_CURRENT_CONNECT_STATUS)) + continue; + + /* Connected */ + /* Wait for maximum device power up time. */ + usbd_delay_ms(dev->bus, USB_PORT_POWERUP_DELAY); + /* Reset port, which implies enabling it. */ + if (usbd_reset_port(dev, port, &up->status) != + USBD_NORMAL_COMPLETION) + continue; + + /* Wait for power to settle in device. */ + usbd_delay_ms(dev->bus, USB_POWER_SETTLE); + + /* Get device info and set its address. */ + r = usbd_new_device(&sc->sc_dev, dev->bus, + dev->depth + 1, status & UPS_LOW_SPEED, + port, up); + /* XXX retry a few times? */ + if (r != USBD_NORMAL_COMPLETION) { + DPRINTFN(-1,("uhub_explore: usb_new_device failed, error=%d\n", r)); + /* Avoid addressing problems by disabling. */ + /* usbd_reset_port(dev, port, &up->status); */ +/* XXX + * What should we do. The device may or may not be at its + * assigned address. In any case we'd like to ignore it. + * Maybe the port should be disabled until the device is + * disconnected. + */ + if (r == USBD_SET_ADDR_FAILED || 1) {/* XXX */ + /* The unit refused to accept a new + * address, and since we cannot leave + * at 0 we have to disable the port + * instead. */ + /* + DEVICE_ERROR(*parent, ("device problem, disable port %d\n", + port)); + */ + usbd_clear_port_feature(dev, port, + UHF_PORT_ENABLE); + } + } else { + if (up->device->hub) + up->device->hub->explore(up->device); + } + } + return (USBD_NORMAL_COMPLETION); +} + +void +uhub_disconnect(up, portno) + struct usbd_port *up; + int portno; +{ + usbd_device_handle dev = up->device; + usbd_pipe_handle p, n; + usb_hub_descriptor_t *hd; + struct usbd_port *spi; + int i, port; + + DPRINTFN(3,("uhub_disconnect: up=%p dev=%p port=%d\n", + up, dev, portno)); + + DEVICE_MSG(dev->bdev, ("device addr %d%s on hub addr %d, port %d disconnected\n", + dev->address, dev->hub ? " (hub)" : "", up->parent->address, portno)); + + for (i = 0; i < dev->cdesc->bNumInterface; i++) { + for (p = LIST_FIRST(&dev->ifaces[i].pipes); p; p = n) { + n = LIST_NEXT(p, next); + if (p->disco) + p->disco(p->discoarg); + usbd_abort_pipe(p); + usbd_close_pipe(p); + } + } + + /* clean up the kindergarten, get rid of the kids */ + usbd_remove_device(dev, up); +} + +void +uhub_intr(reqh, addr, status) + usbd_request_handle reqh; + usbd_private_handle addr; + usbd_status status; +{ + struct uhub_softc *sc = addr; + + DPRINTFN(5,("uhub_intr: sc=%p\n", sc)); +#if 0 + if (status != USBD_NORMAL_COMPLETION) + usbd_clear_endpoint_stall(sc->sc_ipipe); + else +#endif + usb_needs_explore(sc->sc_hub->bus); +} + +#if defined(__FreeBSD__) +DRIVER_MODULE(uhub, usb, uhub_driver, uhub_devclass, usb_driver_load, 0); +#endif diff --git a/sys/dev/usb/ukbd.c b/sys/dev/usb/ukbd.c new file mode 100644 index 0000000..57501a9 --- /dev/null +++ b/sys/dev/usb/ukbd.c @@ -0,0 +1,651 @@ +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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 <dev/usb/usb_port.h> + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#if defined(__NetBSD__) +#include <sys/device.h> +#include <sys/ioctl.h> +#elif defined(__FreeBSD__) +#include <sys/ioccom.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <machine/clock.h> +#endif +#include <sys/tty.h> +#include <sys/file.h> +#include <sys/select.h> +#include <sys/proc.h> +#include <sys/vnode.h> +#include <sys/poll.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbhid.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdivar.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usbdevs.h> +#include <dev/usb/usb_quirks.h> +#include <dev/usb/hid.h> + +#if defined(__NetBSD__) +#include <dev/wscons/wsconsio.h> +#include <dev/wscons/wskbdvar.h> +#include <dev/wscons/wsksymdef.h> +#include <dev/wscons/wsksymvar.h> +#include <dev/wscons/wskbdmap_mfii.h> + +#include "opt_pckbd_layout.h" +#include "opt_wsdisplay_compat.h" +#endif + +#ifdef USB_DEBUG +#define DPRINTF(x) if (ukbddebug) printf x +#define DPRINTFN(n,x) if (ukbddebug>(n)) printf x +int ukbddebug = 0; +#elif defined(__FreeBSD__) +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +#define UPROTO_BOOT_KEYBOARD 1 + +#define NKEYCODE 6 + +#define NUM_LOCK 0x01 +#define CAPS_LOCK 0x02 +#define SCROLL_LOCK 0x04 + +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]; +}; + +#define PRESS 0 +#define RELEASE 0x100 + +#define NMOD 6 +static struct { + int mask, key; +} ukbd_mods[NMOD] = { + { MOD_CONTROL_L, 29 }, + { MOD_CONTROL_R, 58 }, + { MOD_SHIFT_L, 42 }, + { MOD_SHIFT_R, 54 }, + { MOD_ALT_L, 56 }, + { MOD_ALT_R, 184 }, +}; + +#define NN 0 /* no translation */ +/* Translate USB keycodes to US keyboard AT scancodes. */ +static u_int8_t ukbd_trtab[256] = { + 0, 0, 0, 0, 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, NN, 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, NN, NN, NN, NN, /* 60 - 67 */ + NN, NN, NN, NN, NN, NN, NN, NN, /* 68 - 6F */ + NN, NN, NN, NN, NN, NN, 221, 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 */ + NN, NN, NN, NN, NN, NN, NN, NN, /* E0 - E7 */ + NN, NN, NN, 219, NN, NN, NN, 220, /* E8 - EF */ + NN, NN, NN, NN, NN, NN, NN, NN, /* F0 - F7 */ + NN, NN, NN, NN, NN, NN, NN, NN, /* F8 - FF */ +}; + +#define KEY_ERROR 0x01 + +struct ukbd_softc { + bdevice sc_dev; /* base device */ + usbd_interface_handle sc_iface; /* interface */ + usbd_pipe_handle sc_intrpipe; /* interrupt pipe */ + int sc_ep_addr; + + struct ukbd_data sc_ndata; + struct ukbd_data sc_odata; + + char sc_enabled; + char sc_disconnected; /* device is gone */ + + int sc_leds; +#if defined(__NetBSD__) + struct device *sc_wskbddev; +#ifdef WSDISPLAY_COMPAT_RAWKBD + int sc_rawkbd; +#endif +#endif + + int sc_polling; + int sc_pollchar; +}; + +#define UKBDUNIT(dev) (minor(dev)) +#define UKBD_CHUNK 128 /* chunk size for read */ +#define UKBD_BSIZE 1020 /* buffer size */ + +#if defined(__NetBSD__) +int ukbd_match __P((struct device *, struct cfdata *, void *)); +void ukbd_attach __P((struct device *, struct device *, void *)); +#elif defined(__FreeBSD__) +static device_probe_t ukbd_match; +static device_attach_t ukbd_attach; +static device_detach_t ukbd_detach; +#endif + +void ukbd_cngetc __P((void *, u_int *, int *)); +void ukbd_cnpollc __P((void *, int)); + +#if defined(__NetBSD__) +const struct wskbd_consops ukbd_consops = { + ukbd_cngetc, + ukbd_cnpollc, +}; +#endif + +void ukbd_intr __P((usbd_request_handle, usbd_private_handle, usbd_status)); +void ukbd_disco __P((void *)); + +int ukbd_enable __P((void *, int)); +void ukbd_set_leds __P((void *, int)); +#if defined(__NetBSD__) +int ukbd_ioctl __P((void *, u_long, caddr_t, int, struct proc *)); + +const struct wskbd_accessops ukbd_accessops = { + ukbd_enable, + ukbd_set_leds, + ukbd_ioctl, +}; + +const struct wskbd_mapdata ukbd_keymapdata = { + pckbd_keydesctab, +#ifdef PCKBD_LAYOUT + PCKBD_LAYOUT, +#else + KB_US, +#endif +}; +#endif + +#if defined(__NetBSD__) +extern struct cfdriver ukbd_cd; + +struct cfattach ukbd_ca = { + sizeof(struct ukbd_softc), ukbd_match, ukbd_attach +}; +#elif defined(__FreeBSD__) +static devclass_t ukbd_devclass; + +static device_method_t ukbd_methods[] = { + DEVMETHOD(device_probe, ukbd_match), + DEVMETHOD(device_attach, ukbd_attach), + DEVMETHOD(device_detach, ukbd_detach), + {0,0} +}; + +static driver_t ukbd_driver = { + "ukbd", + ukbd_methods, + DRIVER_TYPE_MISC, + sizeof(struct ukbd_softc) +}; +#endif + +#if defined(__NetBSD__) +int +ukbd_match(parent, match, aux) + struct device *parent; + struct cfdata *match; + void *aux; +{ + struct usb_attach_arg *uaa = (struct usb_attach_arg *)aux; +#elif defined(__FreeBSD__) +static int +ukbd_match(device_t device) +{ + struct usb_attach_arg *uaa = device_get_ivars(device); +#endif + usb_interface_descriptor_t *id; + + /* Check that this is a keyboard that speaks the boot protocol. */ + if (!uaa->iface) + return (UMATCH_NONE); + id = usbd_get_interface_descriptor(uaa->iface); + if (id->bInterfaceClass != UCLASS_HID || + id->bInterfaceSubClass != USUBCLASS_BOOT || + id->bInterfaceProtocol != UPROTO_BOOT_KEYBOARD) + return (UMATCH_NONE); + return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO); +} + +#if defined(__NetBSD__) +void +ukbd_attach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct ukbd_softc *sc = (struct ukbd_softc *)self; + struct usb_attach_arg *uaa = aux; +#elif defined(__FreeBSD__) +static int +ukbd_attach(device_t self) +{ + struct ukbd_softc *sc = device_get_softc(self); + struct usb_attach_arg *uaa = device_get_ivars(self); +#endif + usbd_interface_handle iface = uaa->iface; + usb_interface_descriptor_t *id; + usb_endpoint_descriptor_t *ed; + usbd_status r; + char devinfo[1024]; +#if defined(__NetBSD__) + struct wskbddev_attach_args a; +#else + int i; +#endif + + sc->sc_disconnected = 1; + sc->sc_iface = iface; + id = usbd_get_interface_descriptor(iface); + usbd_devinfo(uaa->device, 0, devinfo); +#if defined(__FreeBSD__) + usb_device_set_desc(self, devinfo); + printf("%s%d", device_get_name(self), device_get_unit(self)); +#endif + printf(": %s (interface class %d/%d)\n", devinfo, + id->bInterfaceClass, id->bInterfaceSubClass); + sc->sc_dev = self; + ed = usbd_interface2endpoint_descriptor(iface, 0); + if (!ed) { + DEVICE_ERROR(sc->sc_dev, ("could not read endpoint descriptor\n")); + ATTACH_ERROR_RETURN; + } + + 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, + ed->bEndpointAddress & UE_IN ? "in" : "out", + ed->bmAttributes & UE_XFERTYPE, + UGETW(ed->wMaxPacketSize), ed->bInterval)); + + if ((ed->bEndpointAddress & UE_IN) != UE_IN || + (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) { + DEVICE_ERROR(sc->sc_dev, ("unexpected endpoint\n")); + ATTACH_ERROR_RETURN; + } + + if ((usbd_get_quirks(uaa->device)->uq_flags & UQ_NO_SET_PROTO) == 0) { + r = usbd_set_protocol(iface, 0); + DPRINTFN(5, ("ukbd_attach: protocol set\n")); + if (r != USBD_NORMAL_COMPLETION) { + DEVICE_ERROR(sc->sc_dev, ("set protocol failed\n")); + ATTACH_ERROR_RETURN; + } + } + /* Ignore if SETIDLE fails since it is not crucial. */ + usbd_set_idle(iface, 0, 0); + + sc->sc_ep_addr = ed->bEndpointAddress; + sc->sc_disconnected = 0; + +#if defined(__NetBSD__) + a.console = 0; /* XXX */ + + a.keymap = &ukbd_keymapdata; + + a.accessops = &ukbd_accessops; + a.accesscookie = sc; + + sc->sc_wskbddev = config_found(self, &a, wskbddevprint); + +#elif defined(__FreeBSD__) + /* it's alive! IT'S ALIVE! */ + ukbd_set_leds(sc, NUM_LOCK); + DELAY(15000); + ukbd_set_leds(sc, CAPS_LOCK); + DELAY(20000); + ukbd_set_leds(sc, SCROLL_LOCK); + DELAY(30000); + ukbd_set_leds(sc, CAPS_LOCK); + DELAY(50000); + ukbd_set_leds(sc, NUM_LOCK); + + ukbd_enable(sc, 1); +#endif + + ATTACH_SUCCESS_RETURN; +} + + +#if defined(__FreeBSD__) +int +ukbd_detach(device_t self) +{ + struct ukbd_softc *sc = device_get_softc(self); + char *devinfo = (char *) device_get_desc(self); + + if (sc->sc_enabled) + return ENXIO; + + if (devinfo) { + device_set_desc(self, NULL); + free(devinfo, M_USB); + } + + /* good bye, and thanks for all the fish */ + ukbd_set_leds(sc, NUM_LOCK); + DELAY(50000); + ukbd_set_leds(sc, CAPS_LOCK); + DELAY(30000); + ukbd_set_leds(sc, SCROLL_LOCK); + DELAY(20000); + ukbd_set_leds(sc, CAPS_LOCK); + DELAY(15000); + ukbd_set_leds(sc, NUM_LOCK); + + return 0; +} +#endif + + +void +ukbd_disco(p) + void *p; +{ + struct ukbd_softc *sc = p; + + DPRINTF(("ukbd_disco: sc=%p\n", sc)); + usbd_abort_pipe(sc->sc_intrpipe); + sc->sc_disconnected = 1; +} + +int +ukbd_enable(v, on) + void *v; + int on; +{ + struct ukbd_softc *sc = v; + usbd_status r; + + if (on) { + /* Set up interrupt pipe. */ + if (sc->sc_enabled) + return EBUSY; + + sc->sc_enabled = 1; + r = 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); + if (r != USBD_NORMAL_COMPLETION) + return (EIO); + usbd_set_disco(sc->sc_intrpipe, ukbd_disco, sc); + } else { + /* Disable interrupts. */ + usbd_abort_pipe(sc->sc_intrpipe); + usbd_close_pipe(sc->sc_intrpipe); + + sc->sc_enabled = 0; + } + + return (0); +} + +void +ukbd_intr(reqh, addr, status) + usbd_request_handle reqh; + usbd_private_handle addr; + usbd_status status; +{ + struct ukbd_softc *sc = addr; + struct ukbd_data *ud = &sc->sc_ndata; + int mod, omod; + int ibuf[NMOD+2*NKEYCODE]; /* chars events */ + int nkeys, i, j; + int key, c; +#define ADDKEY(c) ibuf[nkeys++] = (c) + + DPRINTFN(5, ("ukbd_intr: status=%d\n", status)); + if (status == USBD_CANCELLED) + return; + + if (status != USBD_NORMAL_COMPLETION) { + DPRINTF(("ukbd_intr: status=%d\n", status)); + usbd_clear_endpoint_stall_async(sc->sc_intrpipe); + return; + } + + DPRINTFN(5, (" mod=0x%02x key0=0x%02x key1=0x%02x\n", + ud->modifiers, ud->keycode[0], ud->keycode[1])); + + if (ud->keycode[0] == KEY_ERROR) + return; /* ignore */ + nkeys = 0; + 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 + ? PRESS : RELEASE)); + if (memcmp(ud->keycode, sc->sc_odata.keycode, NKEYCODE) != 0) { + /* Check for released keys. */ + for (i = 0; i < NKEYCODE; i++) { + key = sc->sc_odata.keycode[i]; + if (key == 0) + continue; + for (j = 0; j < NKEYCODE; j++) + if (key == ud->keycode[j]) + goto rfound; + c = ukbd_trtab[key]; + if (c) + ADDKEY(c | RELEASE); + rfound: + ; + } + + /* Check for pressed keys. */ + for (i = 0; i < NKEYCODE; i++) { + key = ud->keycode[i]; + if (key == 0) + continue; + for (j = 0; j < NKEYCODE; j++) + if (key == sc->sc_odata.keycode[j]) + goto pfound; + c = ukbd_trtab[key]; + DPRINTFN(2,("ukbd_intr: press key=0x%02x -> 0x%02x\n", + key, c)); + if (c) + ADDKEY(c | PRESS); + pfound: + ; + } + } + sc->sc_odata = *ud; + + if (sc->sc_polling) { + if (nkeys > 0) + sc->sc_pollchar = ibuf[0]; /* XXX lost keys? */ + return; + } + for (i = 0; i < nkeys; i++) { + c = ibuf[i]; +#if defined(__NetBSD__) + wskbd_input(sc->sc_wskbddev, + c & RELEASE ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN, + c & 0xff); +#elif defined(__FreeBSD__) + printf("%c (%d) %s\n", ((c&0xff) < 32 || (c&0xff) > 126? '.':(c&0xff)), c, + (c&RELEASE? "released":"pressed")); + if (ud->modifiers) + printf("0x%04x\n", ud->modifiers); + for (i = 0; i < NKEYCODE; i++) + if (ud->keycode[i]) + printf("%d ", ud->keycode[i]); + printf("\n"); +#endif + } +} + +void +ukbd_set_leds(v, leds) + void *v; + int leds; +{ + struct ukbd_softc *sc = v; + u_int8_t res = leds; + + DPRINTF(("ukbd_set_leds: sc=%p leds=%d\n", sc, leds)); + + sc->sc_leds = leds; +#if defined(__NetBSD__) + 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; +#endif + usbd_set_report_async(sc->sc_iface, UHID_OUTPUT_REPORT, 0, &res, 1); +} + +#if defined(__NetBSD__) +int +ukbd_ioctl(v, cmd, data, flag, p) + void *v; + u_long cmd; + caddr_t data; + int flag; + struct proc *p; +{ + struct ukbd_softc *sc = v; + + switch (cmd) { + case WSKBDIO_GTYPE: + *(int *)data = WSKBD_TYPE_PC_XT; + return 0; + case WSKBDIO_SETLEDS: + ukbd_set_leds(v, *(int *)data); + return 0; + case WSKBDIO_GETLEDS: + *(int *)data = sc->sc_leds; + return (0); +#ifdef WSDISPLAY_COMPAT_RAWKBD + case WSKBDIO_SETMODE: + sc->sc_rawkbd = *(int *)data == WSKBD_RAW; + return (0); +#endif + } + return -1; +} + +/* Console interface. */ +/* XXX does not work. */ +void +ukbd_cngetc(v, type, data) + void *v; + u_int *type; + int *data; +{ + struct ukbd_softc *sc = v; + usbd_lock_token s; + int c; + + DPRINTFN(1,("ukbd_cngetc: enter\n")); + s = usbd_lock(); + sc->sc_polling = 1; + sc->sc_pollchar = -1; + while(sc->sc_pollchar == -1) + usbd_dopoll(sc->sc_iface); + sc->sc_polling = 0; + c = sc->sc_pollchar; + *type = c & RELEASE ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN; + *data = c & 0xff; + usbd_unlock(s); + DPRINTFN(1,("ukbd_cngetc: return 0x%02x\n", c)); +} + +void +ukbd_cnpollc(v, on) + void *v; + int on; +{ + struct ukbd_softc *sc = v; + + DPRINTFN(1,("ukbd_cnpollc: sc=%p on=%d\n", v, on)); + + usbd_set_polling(sc->sc_iface, on); +} +#endif + +#if defined(__FreeBSD__) +DRIVER_MODULE(ukbd, usb, ukbd_driver, ukbd_devclass, usb_driver_load, 0); +#endif diff --git a/sys/dev/usb/ulpt.c b/sys/dev/usb/ulpt.c new file mode 100644 index 0000000..29d20c6 --- /dev/null +++ b/sys/dev/usb/ulpt.c @@ -0,0 +1,502 @@ +/* $NetBSD: ulpt.c,v 1.2 1998/07/25 15:19:09 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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 <dev/usb/usb_port.h> + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#if defined(__NetBSD__) +#include <sys/user.h> +#endif +#include <sys/malloc.h> +#include <sys/kernel.h> +#if defined(__NetBSD__) +#include <sys/device.h> +#include <sys/ioctl.h> +#elif defined(__FreeBSD__) +#include <sys/ioccom.h> +#include <sys/module.h> +#include <sys/bus.h> +#endif +#include <sys/uio.h> +#include <sys/conf.h> +#include <sys/syslog.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdivar.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usbdevs.h> +#include <dev/usb/usb_quirks.h> + +#define TIMEOUT hz*16 /* wait up to 16 seconds for a ready */ +#define STEP hz/4 + +#define LPTPRI (PZERO+8) +#define ULPT_BSIZE 1024 + +#ifdef USB_DEBUG +#define DPRINTF(x) if (ulptdebug) printf x +#define DPRINTFN(n,x) if (ulptdebug>(n)) printf x +int ulptdebug = 0; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +#define UR_GET_DEVICE_ID 0 +#define UR_GET_PORT_STATUS 1 +#define UR_SOFT_RESET 2 + +#define LPS_NERR 0x08 /* printer no error */ +#define LPS_SELECT 0x10 /* printer selected */ +#define LPS_NOPAPER 0x20 /* printer out of paper */ +#define LPS_INVERT (LPS_SELECT|LPS_NERR) +#define LPS_MASK (LPS_SELECT|LPS_NERR|LPS_NOPAPER) + +struct ulpt_softc { + bdevice sc_dev; + usbd_device_handle sc_udev; /* device */ + usbd_interface_handle sc_iface; /* interface */ + int sc_ifaceno; + usbd_pipe_handle sc_bulkpipe; /* bulk pipe */ + int sc_bulk; + + u_char sc_state; +#define ULPT_OPEN 0x01 /* device is open */ +#define ULPT_OBUSY 0x02 /* printer is busy doing output */ +#define ULPT_INIT 0x04 /* waiting to initialize for open */ + u_char sc_flags; +#define ULPT_NOPRIME 0x40 /* don't prime on open */ + u_char sc_laststatus; +}; + +#if defined(__NetBSD__) +int ulpt_match __P((struct device *, struct cfdata *, void *)); +void ulpt_attach __P((struct device *, struct device *, void *)); +#elif defined(__FreeBSD__) +static device_probe_t ulpt_match; +static device_attach_t ulpt_attach; +#endif + +int ulptopen __P((dev_t, int, int, struct proc *)); +int ulptclose __P((dev_t, int, int, struct proc *p)); +int ulptwrite __P((dev_t, struct uio *uio, int)); +int ulptioctl __P((dev_t, u_long, caddr_t, int, struct proc *)); +void ulpt_disco __P((void *)); + +int ulpt_status __P((struct ulpt_softc *)); +void ulpt_reset __P((struct ulpt_softc *)); +int ulpt_statusmsg __P((u_char, struct ulpt_softc *)); + +#define ULPTUNIT(s) (minor(s) & 0x1f) +#define ULPTFLAGS(s) (minor(s) & 0xe0) + +#if defined(__NetBSD__) +extern struct cfdriver ulpt_cd; + +struct cfattach ulpt_ca = { + sizeof(struct ulpt_softc), ulpt_match, ulpt_attach +}; +#elif defined(__FreeBSD__) +static devclass_t ulpt_devclass; + +static device_method_t ulpt_methods[] = { + DEVMETHOD(device_probe, ulpt_match), + DEVMETHOD(device_attach, ulpt_attach), + {0,0} +}; + +static driver_t ulpt_driver = { + "ulpt", + ulpt_methods, + DRIVER_TYPE_MISC, + sizeof(struct ulpt_softc) +}; +#endif + + +#if defined(__NetBSD__) +int +ulpt_match(parent, match, aux) + struct device *parent; + struct cfdata *match; + void *aux; +{ + struct usb_attach_arg *uaa = aux; +#elif defined(__FreeBSD__) +static int +ulpt_match(device_t device) +{ + struct usb_attach_arg *uaa = device_get_ivars(device); +#endif + usb_interface_descriptor_t *id; + + DPRINTFN(10,("ulpt_match\n")); + if (!uaa->iface) + return (UMATCH_NONE); + id = usbd_get_interface_descriptor(uaa->iface); + if (id->bInterfaceClass == UCLASS_PRINTER && + id->bInterfaceSubClass == USUBCLASS_PRINTER && + (id->bInterfaceProtocol == UPROTO_PRINTER_UNI || + id->bInterfaceProtocol == UPROTO_PRINTER_BI)) + return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO); + return (UMATCH_NONE); +} + +#if defined(__NetBSD__) +void +ulpt_attach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct ulpt_softc *sc = (struct ulpt_softc *)self; + struct usb_attach_arg *uaa = aux; +#elif defined(__FreeBSD__) +static int +ulpt_attach(device_t self) +{ + struct ulpt_softc *sc = device_get_softc(self); + struct usb_attach_arg *uaa = device_get_ivars(self); +#endif + usbd_device_handle dev = uaa->device; + usbd_interface_handle iface = uaa->iface; + usb_interface_descriptor_t *id = usbd_get_interface_descriptor(iface); +#if 0 + usb_config_descriptor_t *cd = usbd_get_config_descriptor(dev); + usb_device_request_t req; +#endif + char devinfo[1024]; + usb_endpoint_descriptor_t *ed; + usbd_status r; + + DPRINTFN(10,("ulpt_attach: sc=%p\n", sc)); + usbd_devinfo(dev, 0, devinfo); +#if defined(__FreeBSD__) + usb_device_set_desc(self, devinfo); + printf("%s%d", device_get_name(self), device_get_unit(self)); +#endif + printf(": %s (interface class %d/%d)\n", devinfo, + id->bInterfaceClass, id->bInterfaceSubClass); + sc->sc_dev = self; + + /* Figure out which endpoint is the bulk out endpoint. */ + ed = usbd_interface2endpoint_descriptor(iface, 0); + if (!ed) + goto nobulk; + if ((ed->bEndpointAddress & UE_IN) != UE_OUT || + (ed->bmAttributes & UE_XFERTYPE) != UE_BULK) { + /* In case we are using a bidir protocol... */ + ed = usbd_interface2endpoint_descriptor(iface, 1); + if (!ed) + goto nobulk; + if ((ed->bEndpointAddress & UE_IN) != UE_OUT || + (ed->bmAttributes & UE_XFERTYPE) != UE_BULK) + goto nobulk; + } + sc->sc_bulk = ed->bEndpointAddress; + DPRINTFN(10, ("ulpt_attach: bulk=%d\n", sc->sc_bulk)); + + sc->sc_iface = iface; + r = usbd_interface2device_handle(iface, &sc->sc_udev); + if (r != USBD_NORMAL_COMPLETION) + ATTACH_ERROR_RETURN; + sc->sc_ifaceno = id->bInterfaceNumber; + +#if 0 +XXX needs a different way to read the id string since the length +is unknown. usbd_do_request() returns error on a short transfer. + req.bmRequestType = UT_READ_CLASS_INTERFACE; + req.bRequest = UR_GET_DEVICE_ID; + USETW(req.wValue, cd->bConfigurationValue); + USETW2(req.wIndex, id->bInterfaceNumber, id->bAlternateSetting); + USETW(req.wLength, sizeof devinfo - 1); + r = usbd_do_request(dev, &req, devinfo); + if (r == USBD_NORMAL_COMPLETION) { + int len; + char *idstr; + len = (devinfo[0] << 8) | (devinfo[1] & 0xff); + /* devinfo now contains an IEEE-1284 device ID */ + idstr = devinfo+2; + idstr[len] = 0; + DEVICE_ERROR(sc->sc_dev, ("device id <%s>\n", idstr)); + } else { + printf("%s: \n", sc->sc_dev.dv_xname); + DEVICE_ERROR(sc->sc_dev, ("cannot get device id")); + } +#endif + + ATTACH_SUCCESS_RETURN; + + nobulk: + DEVICE_ERROR(sc->sc_dev, ("could not find bulk endpoint\n")); + ATTACH_ERROR_RETURN; +} + +int +ulpt_status(sc) + struct ulpt_softc *sc; +{ + usb_device_request_t req; + usbd_status r; + u_char status; + + req.bmRequestType = UT_READ_CLASS_INTERFACE; + req.bRequest = UR_GET_PORT_STATUS; + USETW(req.wValue, 0); + USETW(req.wIndex, sc->sc_ifaceno); + USETW(req.wLength, 1); + r = usbd_do_request(sc->sc_udev, &req, &status); + DPRINTFN(1, ("ulpt_status: status=0x%02x r=%d\n", status, r)); + if (r == USBD_NORMAL_COMPLETION) + return (status); + else + return (0); +} + +void +ulpt_reset(sc) + 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); + USETW(req.wLength, 0); + (void)usbd_do_request(sc->sc_udev, &req, 0); +} + +/* + * Reset the printer, then wait until it's selected and not busy. + */ +int +ulptopen(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + u_char flags = ULPTFLAGS(dev); + usbd_status r; + int spin, error; +#if defined(__NetBSD__) + int unit = ULPTUNIT(dev); + struct ulpt_softc *sc; + + if (unit >= ulpt_cd.cd_ndevs) + return ENXIO; + sc = ulpt_cd.cd_devs[unit]; +#elif defined(__FreeBSD__) + struct ulpt_softc *sc = devclass_get_softc(ulpt_devclass, ULPTUNIT(dev)); +#endif + + if (!sc || !sc->sc_iface) + return ENXIO; + + if (sc->sc_state) + return EBUSY; + + sc->sc_state = ULPT_INIT; + sc->sc_flags = flags; + DPRINTF(("ulptopen: flags=0x%x\n", (unsigned)flags)); + + if ((flags & ULPT_NOPRIME) == 0) + ulpt_reset(sc); + + for (spin = 0; (ulpt_status(sc) & LPS_SELECT) == 0; spin += STEP) { + if (spin >= TIMEOUT) { + sc->sc_state = 0; + return EBUSY; + } + + /* wait 1/4 second, give up if we get a signal */ + error = tsleep((caddr_t)sc, LPTPRI | PCATCH, "ulptop", STEP); + if (error != EWOULDBLOCK) { + sc->sc_state = 0; + return error; + } + } + + r = usbd_open_pipe(sc->sc_iface, sc->sc_bulk, 0, &sc->sc_bulkpipe); + if (r != USBD_NORMAL_COMPLETION) { + sc->sc_state = 0; + return (EIO); + } + + sc->sc_state = ULPT_OPEN; + + DPRINTF(("ulptopen: done\n")); + return (0); +} + +int +ulpt_statusmsg(status, sc) + u_char status; + struct ulpt_softc *sc; +{ + u_char new; + + status = (status ^ LPS_INVERT) & LPS_MASK; + new = status & ~sc->sc_laststatus; + sc->sc_laststatus = status; + +/* XXX should be tidied up into one block and a definition in usb_ports.h + */ +#if defined(__NetBSD__) + if (new & LPS_SELECT) + log(LOG_NOTICE, "%s: offline\n", sc->sc_dev.dv_xname); + else if (new & LPS_NOPAPER) + log(LOG_NOTICE, "%s: out of paper\n", sc->sc_dev.dv_xname); + else if (new & LPS_NERR) + log(LOG_NOTICE, "%s: output error\n", sc->sc_dev.dv_xname); +#elif defined(__FreeBSD__) + if (new & LPS_SELECT) + log(LOG_NOTICE, "%s%d: offline\n", + device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev)); + else if (new & LPS_NOPAPER) + log(LOG_NOTICE, "%s%d: out of paper\n", + device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev)); + else if (new & LPS_NERR) + log(LOG_NOTICE, "%s%d: output error\n", + device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev)); +#endif + + return status; +} + +int +ulptclose(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ +#if defined(__NetBSD__) + int unit = ULPTUNIT(dev); + struct ulpt_softc *sc; + + if (unit >= ulpt_cd.cd_ndevs) + return (ENXIO); + sc = ulpt_cd.cd_devs[unit]; +#elif defined(__FreeBSD__) + struct ulpt_softc *sc = devclass_get_softc(ulpt_devclass, ULPTUNIT(dev)); +#endif + + usbd_close_pipe(sc->sc_bulkpipe); + + sc->sc_state = 0; + + DPRINTF(("ulptclose: closed\n")); + return (0); +} + +int +ulptwrite(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ + size_t n; + int error = 0; + char buf[ULPT_BSIZE]; + usbd_request_handle reqh; + usbd_status r; +#if defined(__NetBSD__) + int unit = ULPTUNIT(dev); + struct ulpt_softc *sc; + + if (unit >= ulpt_cd.cd_ndevs) + return (ENXIO); + sc = ulpt_cd.cd_devs[unit]; +#elif defined(__FreeBSD__) + struct ulpt_softc *sc = devclass_get_softc(ulpt_devclass, ULPTUNIT(dev)); +#endif + + DPRINTF(("ulptwrite\n")); + reqh = usbd_alloc_request(); + if (reqh == 0) + return (EIO); + while ((n = min(ULPT_BSIZE, uio->uio_resid)) != 0) { + ulpt_statusmsg(ulpt_status(sc), sc); + uiomove(buf, n, uio); + /* XXX use callback to enable interrupt? */ + r = usbd_setup_request(reqh, sc->sc_bulkpipe, 0, buf, n, + 0, USBD_NO_TIMEOUT, 0); + if (r != USBD_NORMAL_COMPLETION) { + error = EIO; + break; + } + DPRINTFN(1, ("ulptwrite: transfer %d bytes\n", n)); + r = usbd_sync_transfer(reqh); + if (r != USBD_NORMAL_COMPLETION) { + DPRINTF(("ulptwrite: error=%d\n", r)); + usbd_clear_endpoint_stall(sc->sc_bulkpipe); + error = EIO; + break; + } + } + usbd_free_request(reqh); + return (error); +} + +int +ulptioctl(dev, cmd, data, flag, p) + dev_t dev; + u_long cmd; + caddr_t data; + int flag; + struct proc *p; +{ + int error = 0; + + switch (cmd) { + default: + error = ENODEV; + } + + return error; +} + +#if defined(__FreeBSD__) +DRIVER_MODULE(ulpt, usb, ulpt_driver, ulpt_devclass, usb_driver_load, 0); +#endif diff --git a/sys/dev/usb/ums.c b/sys/dev/usb/ums.c new file mode 100644 index 0000000..80dfa16 --- /dev/null +++ b/sys/dev/usb/ums.c @@ -0,0 +1,812 @@ +/* $NetBSD: ums.c,v 1.8 1998/08/01 20:11:39 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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 <dev/usb/usb_port.h> + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#if defined(__NetBSD__) +#include <sys/device.h> +#include <sys/ioctl.h> +#elif defined(__FreeBSD__) +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/ioccom.h> +#include <sys/conf.h> +#endif +#include <sys/tty.h> +#include <sys/file.h> +#include <sys/select.h> +#include <sys/proc.h> +#include <sys/vnode.h> +#include <sys/poll.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbhid.h> + +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdivar.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usbdevs.h> +#include <dev/usb/usb_quirks.h> +#include <dev/usb/hid.h> + +#if defined(__NetBSD__) +#include <dev/wscons/wsconsio.h> +#include <dev/wscons/wsmousevar.h> +#elif defined(__FreeBSD__) +#include <machine/mouse.h> +#endif + +#ifdef USB_DEBUG +#define DPRINTF(x) if (umsdebug) printf x +#define DPRINTFN(n,x) if (umsdebug>(n)) printf x +int umsdebug = 1; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +#define UMSUNIT(s) (minor(s)&0x1f) + +#define PS2LBUTMASK x01 +#define PS2RBUTMASK x02 +#define PS2MBUTMASK x04 +#define PS2BUTMASK 0x0f + +#define QUEUE_BUFSIZE 240 /* MUST be dividable by 3 _and_ 4 */ + +struct ums_softc { + bdevice sc_dev; /* base device */ + 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 hid_location sc_loc_x, sc_loc_y, sc_loc_z; + struct hid_location *sc_loc_btn; + + int sc_enabled; + int sc_disconnected; /* device is gone */ + + int flags; /* device configuration */ +# define UMS_Z 0x01 /* z direction available */ + int nbuttons; + +#if defined(__NetBSD__) + u_char sc_buttons; /* mouse button status */ + struct device *sc_wsmousedev; +#elif defined(__FreeBSD__) + u_char qbuf[QUEUE_BUFSIZE]; + u_char dummy[100]; /* just for safety and for now */ + int qcount, qhead, qtail; + mousehw_t hw; + mousemode_t mode; + mousestatus_t status; + + int state; +# define UMS_ASLEEP 0x01 /* readFromDevice is waiting */ +# define UMS_SELECT 0x02 /* select is waiting */ + struct selinfo rsel; /* process waiting in select */ +#endif +}; + +#define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE) +#define MOUSE_FLAGS (HIO_RELATIVE) + +#if defined(__NetBSD__) +int ums_match __P((struct device *, struct cfdata *, void *)); +void ums_attach __P((struct device *, struct device *, void *)); +#elif defined(__FreeBSD__) +static device_probe_t ums_match; +static device_attach_t ums_attach; +static device_detach_t ums_detach; +#endif + +void ums_intr __P((usbd_request_handle, usbd_private_handle, usbd_status)); +void ums_disco __P((void *)); + +static int ums_enable __P((void *)); +static void ums_disable __P((void *)); + +#if defined(__NetBSD__) +static int ums_ioctl __P((void *, u_long, caddr_t, int, struct proc *)); +#elif defined(__FreeBSD__) +static d_open_t ums_open; +static d_close_t ums_close; +static d_read_t ums_read; +static d_ioctl_t ums_ioctl; +static d_poll_t ums_poll; + +#define UMS_CDEV_MAJOR 138 /* XXX NWH should be requested */ + +static struct cdevsw ums_cdevsw = { + ums_open, ums_close, ums_read, nowrite, + ums_ioctl, nostop, nullreset, nodevtotty, + ums_poll, nommap, + NULL, "ums_", NULL, -1 +}; +#endif + +#if defined(__NetBSD__) +const struct wsmouse_accessops ums_accessops = { + ums_enable, + ums_ioctl, + ums_disable, +}; +#endif + +#if defined(__NetBSD__) +extern struct cfdriver ums_cd; + +struct cfattach ums_ca = { + sizeof(struct ums_softc), ums_match, ums_attach +}; +#elif defined(__FreeBSD__) +static devclass_t ums_devclass; + +static device_method_t ums_methods[] = { + DEVMETHOD(device_probe, ums_match), + DEVMETHOD(device_attach, ums_attach), + DEVMETHOD(device_detach, ums_detach), + {0,0} +}; + +static driver_t ums_driver = { + "ums", + ums_methods, + DRIVER_TYPE_MISC, + sizeof(struct ums_softc) +}; +#endif + + +#if defined(__NetBSD__) +int +ums_match(parent, match, aux) + struct device *parent; + struct cfdata *match; + void *aux; +{ + struct usb_attach_arg *uaa = aux; +#elif defined(__FreeBSD__) +static int +ums_match(device_t device) +{ + struct usb_attach_arg *uaa = device_get_ivars(device); +#endif + usb_interface_descriptor_t *id; + int size, ret; + void *desc; + usbd_status r; + + if (!uaa->iface) + return (UMATCH_NONE); + id = usbd_get_interface_descriptor(uaa->iface); + if (id->bInterfaceClass != UCLASS_HID) + return (UMATCH_NONE); + + r = usbd_alloc_report_desc(uaa->iface, &desc, &size, M_TEMP); + if (r != USBD_NORMAL_COMPLETION) + 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); +} + +#if defined(__NetBSD__) +void +ums_attach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct ums_softc *sc = (struct ums_softc *)self; + struct usb_attach_arg *uaa = aux; +#elif defined(__FreeBSD__) +static int +ums_attach(device_t self) +{ + struct ums_softc *sc = device_get_softc(self); + struct usb_attach_arg *uaa = device_get_ivars(self); +#endif + usbd_interface_handle iface = uaa->iface; + usb_interface_descriptor_t *id; + usb_endpoint_descriptor_t *ed; +#if defined(__NetBSD__) + struct wsmousedev_attach_args a; +#endif + char devinfo[1024]; + int size; + void *desc; + usbd_status r; + u_int32_t flags; + struct hid_location loc_btn; + int i; + + sc->sc_disconnected = 1; + sc->sc_iface = iface; + id = usbd_get_interface_descriptor(iface); + usbd_devinfo(uaa->device, 0, devinfo); +#if defined(__FreeBSD__) + usb_device_set_desc(self, devinfo); + printf("%s%d", device_get_name(self), device_get_unit(self)); +#endif + printf(": %s (interface class %d/%d)\n", devinfo, + id->bInterfaceClass, id->bInterfaceSubClass); + sc->sc_dev = self; + ed = usbd_interface2endpoint_descriptor(iface, 0); + if (!ed) { + DEVICE_ERROR(sc->sc_dev, ("could not read endpoint descriptor\n")); + 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, + ed->bEndpointAddress & UE_IN ? "in" : "out", + ed->bmAttributes & UE_XFERTYPE, + UGETW(ed->wMaxPacketSize), ed->bInterval)); + + if ((ed->bEndpointAddress & UE_IN) != UE_IN || + (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) { + DEVICE_ERROR(sc->sc_dev, ("unexpected endpoint\n")); + ATTACH_ERROR_RETURN; + } + + r = usbd_alloc_report_desc(uaa->iface, &desc, &size, M_TEMP); + if (r != USBD_NORMAL_COMPLETION) + ATTACH_ERROR_RETURN; + + if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), + hid_input, &sc->sc_loc_x, &flags)) { + DEVICE_ERROR(sc->sc_dev, ("mouse has no X report\n")); + ATTACH_ERROR_RETURN; + } + if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) + DEVICE_ERROR(sc->sc_dev, ("X report 0x%04x not supported\n", + flags)); + + if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), + hid_input, &sc->sc_loc_y, &flags)) { + DEVICE_ERROR(sc->sc_dev, ("mouse has no Y report\n")); + ATTACH_ERROR_RETURN; + } + if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) + DEVICE_ERROR(sc->sc_dev, ("Y report 0x%04x not supported\n", + flags)); + +#ifndef USBVERBOSE + if (bootverbose) +#endif + { + if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), + hid_input, &sc->sc_loc_z, &flags)) + DEVICE_MSG(sc->sc_dev, ("Device has Z axis\n")); + if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_SLIDER), + hid_input, &sc->sc_loc_z, &flags)) + DEVICE_MSG(sc->sc_dev, ("Device has Slider\n")); + if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_DIAL), + hid_input, &sc->sc_loc_z, &flags)) + DEVICE_MSG(sc->sc_dev, ("Device has Dial\n")); + if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL), + hid_input, &sc->sc_loc_z, &flags)) + DEVICE_MSG(sc->sc_dev, ("Device has Wheel\n")); + if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_HAT_SWITCH), + hid_input, &sc->sc_loc_z, &flags)) + DEVICE_MSG(sc->sc_dev, ("Device has Hat Switch\n")); + } + + /* try to guess the Z activator: first check Z, then WHEEL */ + if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), + hid_input, &sc->sc_loc_z, &flags) || + hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL), + 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 */ +#if !defined(__FreeBSD__) /* FIXME */ + /* IntelliMouse protocol is not properly implemented yet. + * Probably the wisest to use the sysmouse protocol + */ + } else { + sc->flags |= UMS_Z; +#endif + } + } + + /* figure out the number of buttons, 7 is an arbitrary limit */ + for (i = 1; i <= 7; i++) + if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i), + 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) + ATTACH_ERROR_RETURN; + +#ifndef USBVERBOSE + if (bootverbose) +#endif + DEVICE_MSG(sc->sc_dev, ("%d buttons%s\n", + sc->nbuttons, (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_USB, M_NOWAIT); + if (!sc->sc_ibuf) { + free(sc->sc_loc_btn, M_USB); + ATTACH_ERROR_RETURN; + } + + sc->sc_ep_addr = ed->bEndpointAddress; + sc->sc_disconnected = 0; + free(desc, M_TEMP); + +#ifdef USB_DEBUG + DPRINTF(("ums_attach: sc=%p\n", sc)); + DPRINTF(("ums_attach: X\t%d/%d\n", + sc->sc_loc_x.pos, sc->sc_loc_x.size)); + DPRINTF(("ums_attach: Y\t%d/%d\n", + sc->sc_loc_x.pos, sc->sc_loc_x.size)); + if (sc->flags & UMS_Z) + DPRINTF(("ums_attach: Z\t%d/%d\n", + sc->sc_loc_z.pos, sc->sc_loc_z.size)); + for (i = 1; i <= sc->nbuttons; i++) { + 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 + +#if defined(__NetBSD__) + a.accessops = &ums_accessops; + a.accesscookie = sc; + + sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint); +#elif defined(__FreeBSD__) + sc->hw.buttons = 2; /* XXX hw&mode values are bogus */ + sc->hw.iftype = MOUSE_IF_PS2; + sc->hw.type = MOUSE_MOUSE; + if (sc->flags & UMS_Z) + sc->hw.model = MOUSE_MODEL_INTELLI; + else + sc->hw.model = MOUSE_MODEL_GENERIC; + sc->hw.hwid = 0; + sc->mode.protocol = MOUSE_PROTO_PS2; + sc->mode.rate = -1; + sc->mode.resolution = MOUSE_RES_DEFAULT; + sc->mode.accelfactor = 1; + sc->mode.level = 0; + if (sc->flags & UMS_Z) { + sc->mode.packetsize = MOUSE_INTELLI_PACKETSIZE; + sc->mode.syncmask[0] = 0xc8; + } else { + sc->mode.packetsize = MOUSE_PS2_PACKETSIZE; + sc->mode.syncmask[0] = 0xc0; + } + sc->mode.syncmask[1] = 0; + + sc->status.flags = 0; + sc->status.button = sc->status.obutton = 0; + sc->status.dx = sc->status.dy = sc->status.dz = 0; + + sc->rsel.si_flags = 0; + sc->rsel.si_pid = 0; +#endif + + ATTACH_SUCCESS_RETURN; +} + + +#if defined(__FreeBSD__) +static int +ums_detach(device_t self) +{ + struct ums_softc *sc = device_get_softc(self); + char *devinfo = (char *) device_get_desc(self); + + if (devinfo) { + device_set_desc(self, NULL); + free(devinfo, M_USB); + } + free(sc->sc_loc_btn, M_USB); + free(sc->sc_ibuf, M_USB); + + return 0; +} +#endif + +void +ums_disco(p) + void *p; +{ + struct ums_softc *sc = p; + + DPRINTF(("ums_disco: sc=%p\n", sc)); + usbd_abort_pipe(sc->sc_intrpipe); + sc->sc_disconnected = 1; +} + +void +ums_intr(reqh, addr, status) + usbd_request_handle reqh; + usbd_private_handle addr; + usbd_status status; +{ + struct ums_softc *sc = addr; + u_char *ibuf; + int dx, dy, dz; + u_char buttons = 0; + int i; + + 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 != USBD_NORMAL_COMPLETION) { + DPRINTF(("ums_intr: status=%d\n", status)); + usbd_clear_endpoint_stall_async(sc->sc_intrpipe); + return; + } + + ibuf = sc->sc_ibuf; + if (sc->sc_iid) { + 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); + /* NWH Why are you modifying the button assignments here? + * That's the purpose of a high level mouse driver + */ + for (i = 1; i <= sc->nbuttons; i++) + if (hid_get_data(ibuf, &sc->sc_loc_btn[i-1])) + buttons |= (1 << (i-1)); + +#if defined(__NetBSD__) + if (dx || dy || buttons != sc->sc_buttons) { + DPRINTFN(10, ("ums_intr: x:%d y:%d z:%d buttons:0x%x\n", + dx, dy, dz, buttons)); + sc->sc_buttons = buttons; + if (sc->sc_wsmousedev) + wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, dz); +#elif defined(__FreeBSD__) + if (dx || dy || buttons != sc->status.button) { + DPRINTFN(10, ("ums_intr: x:%d y:%d z:%d buttons:0x%x\n", + dx, dy, dz, buttons)); + + sc->status.button = buttons; + sc->status.dx += dx; + sc->status.dy += dy; + sc->status.dz += dz; + + /* Discard data in case of full buffer */ + if (sc->qcount == sizeof(sc->qbuf)) { + DPRINTF(("Buffer full, discarded packet")); + return; + } + + sc->qbuf[sc->qhead] = MOUSE_PS2_SYNC; + if (dx < 0) + sc->qbuf[sc->qhead] |= MOUSE_PS2_XNEG; + if (dx > 255 || dx < -255) + sc->qbuf[sc->qhead] |= MOUSE_PS2_XOVERFLOW; + if (dy < 0) + sc->qbuf[sc->qhead] |= MOUSE_PS2_YNEG; + if (dy > 255 || dy < -255) + sc->qbuf[sc->qhead] |= MOUSE_PS2_YOVERFLOW; + sc->qbuf[sc->qhead++] |= buttons; + sc->qbuf[sc->qhead++] = dx; + sc->qbuf[sc->qhead++] = dy; + sc->qcount += 3; + if (sc->flags & UMS_Z) { + sc->qbuf[sc->qhead++] = dz; + sc->qcount++; + } +#ifdef USB_DEBUG + if (sc->qhead > sizeof(sc->qbuf)) + DPRINTF(("Buffer overrun! %d %d\n", sc->qhead, sizeof(sc->qbuf))); +#endif + if (sc->qhead >= sizeof(sc->qbuf)) /* wrap round at end of buffer */ + sc->qhead = 0; + + if (sc->state & UMS_ASLEEP) /* someone waiting for data */ + wakeup(sc); + selwakeup(&sc->rsel); /* wake up any pending selects */ + sc->state &= ~UMS_SELECT; +#endif + } +} + + +static int +ums_enable(v) + void *v; +{ + struct ums_softc *sc = v; + + usbd_status r; + + if (sc->sc_enabled) + return EBUSY; + + sc->sc_enabled = 1; +#if defined(__NetBSD__) + sc->sc_buttons = 0; +#elif defined(__FreeBSD__) + sc->qcount = 0; + sc->qhead = sc->qtail = 0; +#ifdef USB_DEBUG + if (sizeof(sc->qbuf) % 4 || sizeof(sc->qbuf) % 3) { + DPRINTF(("Buffer size not dividable by 3 or 4\n")); + return ENXIO; + } +#endif + sc->status.flags = 0; + sc->status.button = sc->status.obutton = 0; + sc->status.dx = sc->status.dy = sc->status.dz = 0; +#endif + + /* Set up interrupt pipe. */ + r = 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); + if (r != USBD_NORMAL_COMPLETION) { + DPRINTF(("ums_enable: usbd_open_pipe_intr failed, error=%d\n", + r)); + sc->sc_enabled = 0; + return (EIO); + } + usbd_set_disco(sc->sc_intrpipe, ums_disco, sc); + return (0); +} + +static void +ums_disable(v) + void *v; +{ + struct ums_softc *sc = v; + + /* Disable interrupts. */ + usbd_abort_pipe(sc->sc_intrpipe); + usbd_close_pipe(sc->sc_intrpipe); + + sc->sc_enabled = 0; + +#if defined(USBVERBOSE) && defined(__FreeBSD__) + if (sc->qcount != 0) + DPRINTF(("Discarded %d bytes in queue\n", sc->qcount)); +#endif +} + +#if defined(__NetBSD__) +static int +ums_ioctl(v, cmd, data, flag, p) + void *v; + u_long cmd; + caddr_t data; + int flag; + struct proc *p; + +{ + switch (cmd) { + case WSMOUSEIO_GTYPE: + *(u_int *)data = WSMOUSE_TYPE_PS2; + return (0); + } + + return (-1); /* NWH XXX ??? */ +} + +#elif defined(__FreeBSD__) +static int +ums_open(dev_t dev, int flag, int fmt, struct proc *p) +{ + struct ums_softc *sc = devclass_get_softc(ums_devclass, UMSUNIT(dev)); + + if (!sc) { + DPRINTF(("sc not found at open")); + return EINVAL; + } + + return ums_enable(sc); +} + +static int +ums_close(dev_t dev, int flag, int fmt, struct proc *p) +{ + struct ums_softc *sc = devclass_get_softc(ums_devclass, UMSUNIT(dev)); + + if (!sc) { + DPRINTF(("sc not found at close")); + return EINVAL; + } + + if (sc->sc_enabled) + ums_disable(sc); + return 0; +} + +static int +ums_read(dev_t dev, struct uio *uio, int flag) +{ + struct ums_softc *sc = devclass_get_softc(ums_devclass, UMSUNIT(dev)); + int s; + char buf[sizeof(sc->qbuf)]; + int l = 0; + int error; + + if (!sc || !sc->sc_enabled) { + DPRINTF(("sc not found at read")); + return EINVAL; + } + + s = splusb(); + while (sc->qcount == 0 ) { + /* NWH XXX non blocking I/O ?? + if (non blocking I/O ) { + splx(s); + return EWOULDBLOCK; + } else { + */ + sc->state |= UMS_ASLEEP; + error = tsleep(sc, PZERO | PCATCH, "umsrea", 0); + sc->state &= ~UMS_ASLEEP; + if (error) { + splx(s); + return error; + } + } + + while ((sc->qcount > 0) && (uio->uio_resid > 0)) { + l = (sc->qcount < uio->uio_resid? sc->qcount:uio->uio_resid); + if (l > sizeof(buf)) + l = sizeof(buf); + if (l > sizeof(sc->qbuf) - sc->qtail) /* transfer till end of buf */ + l = sizeof(sc->qbuf) - sc->qtail; + + splx(s); + uiomove(&sc->qbuf[sc->qtail], l, uio); + s = splusb(); + + if ( sc->qcount - l < 0 ) { + DPRINTF(("qcount below 0, count=%d l=%d\n", sc->qcount, l)); + sc->qcount = l; + } + sc->qcount -= l; /* remove the bytes from the buffer */ + sc->qtail = (sc->qtail + l) % sizeof(sc->qbuf); + } + splx(s); + + return 0; +} + +static int +ums_poll(dev_t dev, int events, struct proc *p) +{ + struct ums_softc *sc = devclass_get_softc(ums_devclass, UMSUNIT(dev)); + int revents = 0; + int s; + + if (!sc) { + DPRINTF(("sc not found at poll")); + return 0; /* just to make sure */ + } + + s = splusb(); + if (events & (POLLIN | POLLRDNORM)) + if (sc->qcount) { + revents = events & (POLLIN | POLLRDNORM); + } else { + sc->state |= UMS_SELECT; + selrecord(p, &sc->rsel); + } + splx(s); + + return revents; +} + +int +ums_ioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) +{ + struct ums_softc *sc = devclass_get_softc(ums_devclass, UMSUNIT(dev)); + int error = 0; + int s; + + if (!sc) { + DPRINTF(("sc not found at ioctl")); + return ENOENT; + } + + switch(cmd) { + case MOUSE_GETHWINFO: + *(mousehw_t *)addr = sc->hw; + break; + case MOUSE_GETMODE: + *(mousemode_t *)addr = sc->mode; + break; + case MOUSE_GETLEVEL: + *(int *)addr = sc->mode.level; + break; + case MOUSE_GETSTATUS: { + mousestatus_t *status = (mousestatus_t *) addr; + + s = splusb(); + *status = sc->status; + sc->status.obutton = sc->status.button; + sc->status.button = 0; + sc->status.dx = sc->status.dy = sc->status.dz = 0; + splx(s); + + if (status->dx || status->dy || status->dz) + status->flags |= MOUSE_POSCHANGED; + if (status->button != status->obutton) + status->flags |= MOUSE_BUTTONSCHANGED; + break; + } + default: + error = ENOTTY; + } + + return error; +} +#endif + +#if defined(__FreeBSD__) +CDEV_DRIVER_MODULE(ums, usb, ums_driver, ums_devclass, + UMS_CDEV_MAJOR, ums_cdevsw, usb_driver_load, 0); +#endif + diff --git a/sys/dev/usb/usb.c b/sys/dev/usb/usb.c new file mode 100644 index 0000000..3cea6ba --- /dev/null +++ b/sys/dev/usb/usb.c @@ -0,0 +1,603 @@ +/* $NetBSD: usb.c,v 1.3 1998/08/01 18:16:20 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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. + */ + +/* + * USB spec: http://www.teleport.com/cgi-bin/mailmerge.cgi/~usb/cgiform.tpl + * More USB specs at http://www.usb.org/developers/index.shtml + */ + +#include <dev/usb/usb_port.h> + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#if defined(__NetBSD__) +#include <sys/device.h> +#else +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/ioccom.h> +#include <sys/uio.h> +#include <sys/conf.h> +#endif +#include <sys/poll.h> +#include <sys/proc.h> +#include <sys/select.h> + +#include <dev/usb/usb.h> + +#if defined(__FreeBSD__) +MALLOC_DEFINE(M_USB, "USB", "USB"); +MALLOC_DEFINE(M_USBDEV, "USBdev", "USB device"); +#endif + +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdivar.h> +#include <dev/usb/usb_quirks.h> + +#if defined(__FreeBSD__) +#include "usb_if.h" +#endif + +#ifdef USB_DEBUG +#define DPRINTF(x) if (usbdebug) printf x +#define DPRINTFN(n,x) if (usbdebug>(n)) printf x +int usbdebug = 2; +int uhcidebug = 2; +int ohcidebug = 2; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +#define USBUNIT(dev) (minor(dev)) + +struct usb_softc { + bdevice sc_dev; /* base device */ + usbd_bus_handle sc_bus; /* USB controller */ + struct usbd_port sc_port; /* dummy port for root hub */ + char sc_running; + char sc_exploring; + struct selinfo sc_consel; /* waiting for connect change */ +}; + +#if defined(__NetBSD__) +int usb_match __P((struct device *, struct cfdata *, void *)); +void usb_attach __P((struct device *, struct device *, void *)); + +int usbopen __P((dev_t, int, int, struct proc *)); +int usbclose __P((dev_t, int, int, struct proc *)); +int usbioctl __P((dev_t, u_long, caddr_t, int, struct proc *)); +int usbpoll __P((dev_t, int, struct proc *)); + +#else +static device_probe_t usb_match; +static device_attach_t usb_attach; +static bus_print_child_t usb_print_child; + +d_open_t usbopen; +d_close_t usbclose; +d_ioctl_t usbioctl; +int usbpoll __P((dev_t, int, struct proc *)); + +struct cdevsw usb_cdevsw = { + usbopen, usbclose, noread, nowrite, + usbioctl, nullstop, nullreset, nodevtotty, + seltrue, nommap, nostrat, + "usb", NULL, -1 +}; +#endif + +usbd_status usb_discover __P((struct usb_softc *)); + +#if defined(__NetBSD__) +extern struct cfdriver usb_cd; + +struct cfattach usb_ca = { + sizeof(struct usb_softc), usb_match, usb_attach +}; +#else +static devclass_t usb_devclass = NULL; + +static device_method_t usb_methods[] = { + DEVMETHOD(device_probe, usb_match), + DEVMETHOD(device_attach, usb_attach), + + DEVMETHOD(bus_print_child, usb_print_child), + {0, 0} +}; + +static driver_t usb_driver = { + "usb", + usb_methods, + DRIVER_TYPE_MISC, + sizeof(struct usb_softc), +}; +#endif + +#if defined(__NetBSD__) +int +usb_match(parent, match, aux) + struct device *parent; + struct cfdata *match; + void *aux; +#else +static int +usb_match(device_t device) +#endif +{ + DPRINTF(("usbd_match\n")); +#if defined(__NetBSD__) + return (1); +#else + return (0); +#endif +} + +#if defined(__NetBSD__) +void +usb_attach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct usb_softc *sc = (struct usb_softc *)self; +#else +static int +usb_attach(device_t device) +{ + struct usb_softc *sc = device_get_softc(device); + void *aux = device_get_ivars(device); +#endif + usbd_device_handle dev; + usbd_status r; + +#if defined(__NetBSD__) + printf("\n"); +#endif + + DPRINTF(("usbd_attach\n")); + usbd_init(); + sc->sc_bus = aux; + sc->sc_bus->usbctl = sc; + sc->sc_running = 1; + sc->sc_bus->use_polling = 1; + sc->sc_port.power = USB_MAX_POWER; +#if defined(__FreeBSD__) + sc->sc_dev = device; +#endif + r = usbd_new_device(&sc->sc_dev, sc->sc_bus, 0, 0, 0, &sc->sc_port); + + if (r == USBD_NORMAL_COMPLETION) { + dev = sc->sc_port.device; + if (!dev->hub) { + sc->sc_running = 0; + DEVICE_ERROR(sc->sc_dev, ("root device is not a hub\n")); + ATTACH_ERROR_RETURN; + } + sc->sc_bus->root_hub = dev; + dev->hub->explore(sc->sc_bus->root_hub); + } else { + DEVICE_ERROR(sc->sc_dev, ("root hub problem, error=%d\n", r)); + sc->sc_running = 0; + } + sc->sc_bus->use_polling = 0; + + ATTACH_SUCCESS_RETURN; +} + +#if defined(__NetBSD__) +int +usbctlprint(aux, pnp) + void *aux; + const char *pnp; +{ + /* only "usb"es can attach to host controllers */ + if (pnp) + printf("usb at %s", pnp); + + return (UNCONF); +} + +#else +static void +usb_print_child(device_t parent, device_t child) +{ + struct usb_softc *sc = device_get_softc(child); + + printf(" at %s%d", device_get_name(parent), device_get_unit(parent)); + + /* How do we get to the usbd_device_handle??? + usbd_device_handle dev = invalidadosch; + + printf(" addr %d", dev->addr); + + if (bootverbose) { + if (dev->lowspeed) + printf(", lowspeed"); + if (dev->self_powered) + printf(", self powered"); + else + printf(", %dmA", dev->power); + printf(", config %d", dev->config); + } + */ +} + +/* Reconfigure all the USB busses in the system + */ + +int +usb_driver_load(module_t mod, modeventtype_t what, void *arg) +{ + /* subroutine is there but inactive at the moment + * the reconfiguration process has not been thought through yet. + */ + devclass_t ugen_devclass = devclass_find("ugen"); + device_t *devlist; + int devcount; + int error; + + switch (what) { + case MOD_LOAD: + case MOD_UNLOAD: + if (!usb_devclass) + return 0; /* just ignore call */ + + if (ugen_devclass) { + /* detach devices from generic driver if possible + */ + error = devclass_get_devices(ugen_devclass, &devlist, + &devcount); + if (!error) + for (devcount--; devcount >= 0; devcount--) + (void) DEVICE_DETACH(devlist[devcount]); + } + + error = devclass_get_devices(usb_devclass, &devlist, &devcount); + if (error) + return 0; /* XXX maybe transient, or error? */ + + for (devcount--; devcount >= 0; devcount--) + USB_RECONFIGURE(devlist[devcount]); + + free(devlist, M_TEMP); + return 0; + } + + return 0; /* nothing to do by us */ +} + +/* Set the description of the device including a malloc and copy + */ +void +usb_device_set_desc(device_t device, char *devinfo) +{ + size_t l; + char *desc; + + if ( devinfo ) { + l = strlen(devinfo); + desc = malloc(l+1, M_USB, M_NOWAIT); + if (desc) + memcpy(desc, devinfo, l+1); + } else + desc = NULL; + + device_set_desc(device, desc); +} +#endif + +int +usbopen(dev, flag, mode, p) + dev_t dev; + int flag, mode; + struct proc *p; +{ +#if defined(__NetBSD__) + int unit = USBUNIT(dev); + struct usb_softc *sc; + + if (unit >= usb_cd.cd_ndevs) + return (ENXIO); + sc = usb_cd.cd_devs[unit]; +#else + device_t device = devclass_get_device(usb_devclass, USBUNIT(dev)); + struct usb_softc *sc = device_get_softc(device); +#endif + + if (sc == 0 || !sc->sc_running) + return (ENXIO); + + return (0); +} + +int +usbclose(dev, flag, mode, p) + dev_t dev; + int flag, mode; + struct proc *p; +{ + return (0); +} + +int +usbioctl(dev, cmd, data, flag, p) + dev_t dev; + u_long cmd; + caddr_t data; + int flag; + struct proc *p; +{ +#if defined(__NetBSD__) + int unit = USBUNIT(dev); + struct usb_softc *sc; + + if (unit >= usb_cd.cd_ndevs) + return (ENXIO); + sc = usb_cd.cd_devs[unit]; +#else + device_t device = devclass_get_device(usb_devclass, USBUNIT(dev)); + struct usb_softc *sc = device_get_softc(device); +#endif + + if (sc == 0 || !sc->sc_running) + return (ENXIO); + switch (cmd) { +#ifdef USB_DEBUG + case USB_SETDEBUG: + usbdebug = uhcidebug = ohcidebug = *(int *)data; + break; +#endif + case USB_DISCOVER: + usb_discover(sc); + break; + case USB_REQUEST: + { + struct usb_ctl_request *ur = (void *)data; + int len = UGETW(ur->request.wLength); + struct iovec iov; + struct uio uio; + void *ptr = 0; + int addr = ur->addr; + usbd_status r; + int error = 0; + + if (len < 0 || len > 32768) + return EINVAL; + if (addr < 0 || addr >= USB_MAX_DEVICES || + sc->sc_bus->devices[addr] == 0) + return EINVAL; + if (len != 0) { + iov.iov_base = (caddr_t)ur->data; + iov.iov_len = len; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_resid = len; + uio.uio_offset = 0; + uio.uio_segflg = UIO_USERSPACE; + uio.uio_rw = + ur->request.bmRequestType & UT_READ ? + UIO_READ : UIO_WRITE; + uio.uio_procp = p; + ptr = malloc(len, M_TEMP, M_WAITOK); + if (uio.uio_rw == UIO_WRITE) { + error = uiomove(ptr, len, &uio); + if (error) + goto ret; + } + } + r = usbd_do_request(sc->sc_bus->devices[addr], + &ur->request, ptr); + if (r) { + error = EIO; + goto ret; + } + if (len != 0) { + if (uio.uio_rw == UIO_READ) { + error = uiomove(ptr, len, &uio); + if (error) + goto ret; + } + } + ret: + if (ptr) + free(ptr, M_TEMP); + return (error); + break; + } + + case USB_DEVICEINFO: + { + struct usb_device_info *di = (void *)data; + int addr = di->addr; + usbd_device_handle dev; + struct usbd_port *p; + int i, r, s; + + if (addr < 1 || addr >= USB_MAX_DEVICES) + return (EINVAL); + dev = sc->sc_bus->devices[addr]; + if (dev == 0) + return (ENXIO); + di->config = dev->config; + usbd_devinfo_vp(dev, di->product, di->vendor); + usbd_printBCD(di->revision, UGETW(dev->ddesc.bcdDevice)); + di->class = dev->ddesc.bDeviceClass; + di->power = dev->self_powered ? 0 : dev->power; + di->lowspeed = dev->lowspeed; + if (dev->hub) { + for (i = 0; + i < sizeof(di->ports) / sizeof(di->ports[0]) && + i < dev->hub->hubdesc.bNbrPorts; + i++) { + p = &dev->hub->ports[i]; + if (p->device) + r = p->device->address; + else { + s = UGETW(p->status.wPortStatus); + if (s & UPS_PORT_ENABLED) + r = USB_PORT_ENABLED; + else if (s & UPS_SUSPEND) + r = USB_PORT_SUSPENDED; + else if (s & UPS_PORT_POWER) + r = USB_PORT_POWERED; + else + r = USB_PORT_DISABLED; + } + di->ports[i] = r; + } + di->nports = dev->hub->hubdesc.bNbrPorts; + } else + di->nports = 0; + break; + } + + case USB_DEVICESTATS: + *(struct usb_device_stats *)data = sc->sc_bus->stats; + break; + + default: + return (ENXIO); + } + return (0); +} + +int +usbpoll(dev, events, p) + dev_t dev; + int events; + struct proc *p; +{ + int revents, s; +#if defined(__NetBSD__) + int unit = USBUNIT(dev); + struct usb_softc *sc; + + if (unit >= usb_cd.cd_ndevs) + return (ENXIO); + sc = usb_cd.cd_devs[unit]; +#else + device_t device = devclass_get_device(usb_devclass, USBUNIT(dev)); + struct usb_softc *sc = device_get_softc(device); +#endif + + DPRINTFN(2, ("usbpoll: sc=%p events=0x%x\n", sc, events)); + s = splusb(); + revents = 0; + if (events & (POLLOUT | POLLWRNORM)) + if (sc->sc_bus->needs_explore) + revents |= events & (POLLOUT | POLLWRNORM); + DPRINTFN(2, ("usbpoll: revents=0x%x\n", revents)); + if (revents == 0) { + if (events & (POLLOUT | POLLWRNORM)) { + DPRINTFN(2, ("usbpoll: selrecord\n")); + selrecord(p, &sc->sc_consel); + } + } + splx(s); + return (revents); +} + +#if defined(__NetBSD__) +/* See remarks on this in usbdi.c + */ +int +usb_bus_count() +{ + int i, n; + + for (i = n = 0; i < usb_cd.cd_ndevs; i++) + if (usb_cd.cd_devs[i]) + n++; + return (n); +} + +usbd_status +usb_get_bus_handle(n, h) + int n; + usbd_bus_handle *h; +{ + int i; + + for (i = 0; i < usb_cd.cd_ndevs; i++) + if (usb_cd.cd_devs[i] && n-- == 0) { + *h = usb_cd.cd_devs[i]; + return (USBD_NORMAL_COMPLETION); + } + return (USBD_INVAL); +} +#endif + +usbd_status +usb_discover(sc) + struct usb_softc *sc; +{ + int s; + + /* Explore device tree from the root */ + /* We need mutual exclusion while traversing the device tree. */ + s = splusb(); + while (sc->sc_exploring) + tsleep(&sc->sc_exploring, PRIBIO, "usbdis", 0); + sc->sc_exploring = 1; + sc->sc_bus->needs_explore = 0; + splx(s); + + sc->sc_bus->root_hub->hub->explore(sc->sc_bus->root_hub); + + s = splusb(); + sc->sc_exploring = 0; + wakeup(&sc->sc_exploring); + splx(s); + /* XXX should we start over if sc_needsexplore is set again? */ + return (0); +} + +void +usb_needs_explore(bus) + usbd_bus_handle bus; +{ + bus->needs_explore = 1; + selwakeup(&bus->usbctl->sc_consel); +} + +#if defined(__FreeBSD__) +DRIVER_MODULE(usb, root, usb_driver, usb_devclass, 0, 0); +#endif diff --git a/sys/dev/usb/usb.h b/sys/dev/usb/usb.h new file mode 100644 index 0000000..28695a3 --- /dev/null +++ b/sys/dev/usb/usb.h @@ -0,0 +1,387 @@ +/* $NetBSD: usb.h,v 1.3 1998/07/25 15:22:11 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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. + */ + + +#ifndef _USB_H_ +#define _USB_H_ + +#include <sys/types.h> +#if defined(__NetBSD__) +#include <sys/ioctl.h> +#endif + +#if defined(__FreeBSD__) +#include <sys/malloc.h> + +MALLOC_DECLARE(M_USB); +MALLOC_DECLARE(M_USBDEV); +#endif + +#define USB_MAX_DEVICES 128 +#define USB_START_ADDR 0 + +#define USB_CONTROL_ENDPOINT 0 +#define USB_MAX_ENDPOINTS 16 + +/* + * The USB records contain some unaligned little-endian word + * components. The U[SG]ETW macros take care of both the alignment + * and endian problem and should always be used to access 16 bit + * values. + */ +typedef u_int8_t uByte; +typedef u_int8_t uWord[2]; +#define UGETW(w) ((w)[0] | ((w)[1] << 8)) +#define USETW(w,v) ((w)[0] = (u_int8_t)(v), (w)[1] = (u_int8_t)((v) >> 8)) +#define USETW2(w,h,l) ((w)[0] = (u_int8_t)(l), (w)[1] = (u_int8_t)(h)) +/* + * On little-endian machines that can handle unanliged accesses + * (e.g. i386) these macros can be replaced by the following. + */ +#if 0 +#define UGETW(w) (*(u_int16_t *)(w)) +#define USETW(w,v) (*(u_int16_t *)(w) = (v)) +#endif + +typedef struct { + uByte bmRequestType; + uByte bRequest; + uWord wValue; + uWord wIndex; + uWord wLength; +} usb_device_request_t; + +#define UT_WRITE 0x00 +#define UT_READ 0x80 +#define UT_STANDARD 0x00 +#define UT_CLASS 0x20 +#define UT_VENDOR 0x40 +#define UT_DEVICE 0x00 +#define UT_INTERFACE 0x01 +#define UT_ENDPOINT 0x02 +#define UT_OTHER 0x03 + +#define UT_READ_DEVICE (UT_READ | UT_STANDARD | UT_DEVICE) +#define UT_READ_INTERFACE (UT_READ | UT_STANDARD | UT_INTERFACE) +#define UT_READ_ENDPOINT (UT_READ | UT_STANDARD | UT_ENDPOINT) +#define UT_WRITE_DEVICE (UT_WRITE | UT_STANDARD | UT_DEVICE) +#define UT_WRITE_INTERFACE (UT_WRITE | UT_STANDARD | UT_INTERFACE) +#define UT_WRITE_ENDPOINT (UT_WRITE | UT_STANDARD | UT_ENDPOINT) +#define UT_READ_CLASS_DEVICE (UT_READ | UT_CLASS | UT_DEVICE) +#define UT_READ_CLASS_INTERFACE (UT_READ | UT_CLASS | UT_INTERFACE) +#define UT_READ_CLASS_OTHER (UT_READ | UT_CLASS | UT_OTHER) +#define UT_WRITE_CLASS_DEVICE (UT_WRITE | UT_CLASS | UT_DEVICE) +#define UT_WRITE_CLASS_INTERFACE (UT_WRITE | UT_CLASS | UT_INTERFACE) +#define UT_WRITE_CLASS_OTHER (UT_WRITE | UT_CLASS | UT_OTHER) + +/* Requests */ +#define UR_GET_STATUS 0x00 +#define UR_CLEAR_FEATURE 0x01 +#define UR_SET_FEATURE 0x03 +#define UR_SET_ADDRESS 0x05 +#define UR_GET_DESCRIPTOR 0x06 +#define UDESC_DEVICE 1 +#define UDESC_CONFIG 2 +#define UDESC_STRING 3 +#define UDESC_INTERFACE 4 +#define UDESC_ENDPOINT 5 +#define UR_SET_DESCRIPTOR 0x07 +#define UR_GET_CONFIG 0x08 +#define UR_SET_CONFIG 0x09 +#define UR_GET_INTERFACE 0x0a +#define UR_SET_INTERFACE 0x0b +#define UR_SYNCH_FRAME 0x0c + +/* Feature numbers */ +#define UF_ENDPOINT_STALL 0 +#define UF_DEVICE_REMOTE_WAKEUP 1 + +#define USB_MAX_IPACKET 8 /* maximum size of the initial packet */ + +typedef struct { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; +} usb_descriptor_t; + +typedef struct { + uByte bLength; + uByte bDescriptorType; + uWord bcdUSB; + uByte bDeviceClass; + uByte bDeviceSubClass; + uByte bDeviceProtocol; + uByte bMaxPacketSize; + /* The fields below are not part of the initial descriptor. */ + uWord idVendor; + uWord idProduct; + uWord bcdDevice; + uByte iManufacturer; + uByte iProduct; + uByte iSerialNumber; + uByte bNumConfigurations; +} usb_device_descriptor_t; +#define USB_DEVICE_DESCRIPTOR_SIZE 18 + +typedef struct { + uByte bLength; + uByte bDescriptorType; + uWord wTotalLength; + uByte bNumInterface; + uByte bConfigurationValue; + uByte iConfiguration; + uByte bmAttributes; +#define UC_BUS_POWERED 0x80 +#define UC_SELF_POWERED 0x40 +#define UC_REMOTE_WAKEUP 0x20 + uByte bMaxPower; /* max current in 2 mA units */ +#define UC_POWER_FACTOR 2 +} usb_config_descriptor_t; +#define USB_CONFIG_DESCRIPTOR_SIZE 9 + +typedef struct { + uByte bLength; + uByte bDescriptorType; + uByte bInterfaceNumber; + uByte bAlternateSetting; + uByte bNumEndpoints; + uByte bInterfaceClass; + uByte bInterfaceSubClass; + uByte bInterfaceProtocol; + uByte iInterface; +} usb_interface_descriptor_t; +#define USB_INTERFACE_DESCRIPTOR_SIZE 9 + +typedef struct { + uByte bLength; + uByte bDescriptorType; + uByte bEndpointAddress; +#define UE_IN 0x80 +#define UE_OUT 0x00 +#define UE_ADDR 0x0f +#define UE_GET_IN(a) (((a) >> 7) & 1) + uByte bmAttributes; +#define UE_CONTROL 0x00 +#define UE_ISOCHRONOUS 0x01 +#define UE_BULK 0x02 +#define UE_INTERRUPT 0x03 +#define UE_XFERTYPE 0x03 + uWord wMaxPacketSize; + uByte bInterval; +} usb_endpoint_descriptor_t; +#define USB_ENDPOINT_DESCRIPTOR_SIZE 7 + +typedef struct { + uByte bLength; + uByte bDescriptorType; + uWord bString[127]; +} usb_string_descriptor_t; +#define USB_MAX_STRING_LEN 128 + +/* Hub specific request */ +#define UR_GET_BUS_STATE 0x02 + +/* Hub features */ +#define UHF_C_HUB_LOCAL_POWER 0 +#define UHF_C_HUB_OVER_CURRENT 1 +#define UHF_PORT_CONNECTION 0 +#define UHF_PORT_ENABLE 1 +#define UHF_PORT_SUSPEND 2 +#define UHF_PORT_OVER_CURRENT 3 +#define UHF_PORT_RESET 4 +#define UHF_PORT_POWER 8 +#define UHF_PORT_LOW_SPEED 9 +#define UHF_C_PORT_CONNECTION 16 +#define UHF_C_PORT_ENABLE 17 +#define UHF_C_PORT_SUSPEND 18 +#define UHF_C_PORT_OVER_CURRENT 19 +#define UHF_C_PORT_RESET 20 + +typedef struct { + uByte bDescLength; + uByte bDescriptorType; + uByte bNbrPorts; + uWord bHubCharacteristics; +#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 + uByte bPwrOn2PwrGood; /* delay in 2 ms units */ +#define UHD_PWRON_FACTOR 2 + uByte bHubContrCurrent; + uByte DeviceRemovable[1]; + /* this is only correct with 1-7 ports on the hub */ + uByte PortPowerCtrlMask[3]; +} usb_hub_descriptor_t; +#define USB_HUB_DESCRIPTOR_SIZE 9 + +typedef struct { + uWord wStatus; +/* Device status flags */ +#define UDS_SELF_POWERED 0x0001 +#define UDS_REMOTE_WAKEUP 0x0002 +} usb_status_t; + +typedef struct { + uWord wHubStatus; +#define UHS_LOCAL_POWER 0x0001 +#define UHS_OVER_CURRENT 0x0002 + uWord wHubChange; +} usb_hub_status_t; + +typedef struct { + uWord wPortStatus; +#define UPS_CURRENT_CONNECT_STATUS 0x0001 +#define UPS_PORT_ENABLED 0x0002 +#define UPS_SUSPEND 0x0004 +#define UPS_OVERCURRENT_INDICATOR 0x0008 +#define UPS_RESET 0x0010 +#define UPS_PORT_POWER 0x0100 +#define UPS_LOW_SPEED 0x0200 + uWord wPortChange; +#define UPS_C_CONNECT_STATUS 0x0001 +#define UPS_C_PORT_ENABLED 0x0002 +#define UPS_C_SUSPEND 0x0004 +#define UPS_C_OVERCURRENT_INDICATOR 0x0008 +#define UPS_C_PORT_RESET 0x0010 +} usb_port_status_t; + +#define UDESC_CS_DEVICE 0x21 +#define UDESC_CS_CONFIG 0x22 +#define UDESC_CS_STRING 0x23 +#define UDESC_CS_INTERFACE 0x24 +#define UDESC_CS_ENDPOINT 0x25 + +#define UDESC_HUB 0x29 + +#define UCLASS_UNSPEC 0 +#define UCLASS_AUDIO 1 +#define USUBCLASS_AUDIOCONTROL 1 +#define USUBCLASS_AUDIOSTREAM 2 +#define UCLASS_HID 3 +#define USUBCLASS_BOOT 1 +#define UCLASS_PRINTER 7 +#define USUBCLASS_PRINTER 1 +#define UPROTO_PRINTER_UNI 1 +#define UPROTO_PRINTER_BI 2 +#define UCLASS_HUB 9 +#define USUBCLASS_HUB 1 + +#define USB_HUB_MAX_DEPTH 5 + +#define USB_PORT_RESET_DELAY 10 /* ms */ +#define USB_PORT_POWERUP_DELAY 100 /* ms */ +#define USB_POWER_SETTLE 100 /* ms */ + +#define USB_MIN_POWER 100 /* mA */ +#define USB_MAX_POWER 500 /* mA */ + + +#define USB_RESET_DELAY 100 /* ms XXX?*/ +#define USB_RESUME_DELAY 10 /* ms XXX?*/ + +/*** ioctl() related stuff ***/ + +struct usb_ctl_request { + int addr; + usb_device_request_t request; + void *data; +}; + +struct usb_all_desc { + u_char data[1024]; /* filled data size will vary */ +}; + +struct usb_ctl_report_desc { + int size; + u_char data[1024]; /* filled data size will vary */ +}; + +struct usb_device_info { + uByte addr; /* device address */ + char product[USB_MAX_STRING_LEN]; + char vendor[USB_MAX_STRING_LEN]; + char revision[8]; + uByte class; + uByte config; + uByte lowspeed; + int power; /* power consumption in mA, 0 if selfpowered */ + int nports; + uByte ports[16]; /* hub only: addresses of devices on ports */ +#define USB_PORT_ENABLED 0xff +#define USB_PORT_SUSPENDED 0xfe +#define USB_PORT_POWERED 0xfd +#define USB_PORT_DISABLED 0xfc +}; + +struct usb_ctl_report { + int report; + u_char data[1024]; /* filled data size will vary */ +}; + +struct usb_device_stats { + u_long requests[4]; /* indexed by transfer type UE_* */ +}; + +/* USB controller */ +#define USB_REQUEST _IOWR('U', 1, struct usb_ctl_request) +#define USB_SETDEBUG _IOW ('U', 2, int) +#define USB_DISCOVER _IO ('U', 3) +#define USB_DEVICEINFO _IOWR('U', 4, struct usb_device_info) +#define USB_DEVICESTATS _IOR ('U', 5, struct usb_device_stats) + +/* Generic HID device */ +#define USB_GET_REPORT_DESC _IOR ('U', 21, struct usb_ctl_report_desc) +#define USB_SET_IMMED _IOW ('U', 22, int) +#define USB_GET_REPORT _IOWR('U', 23, struct usb_ctl_report) + +/* Generic USB device */ +#define USB_SET_CONFIG _IOW ('U', 100, int) +#define USB_SET_INTERFACE _IOW ('U', 101, int) +#define USB_GET_DEVICE_DESC _IOR ('U', 102, usb_device_descriptor_t) +#define USB_GET_CONFIG_DESC _IOR ('U', 103, usb_config_descriptor_t) +#define USB_GET_INTERFACE_DESC _IOR ('U', 104, usb_interface_descriptor_t) +#define USB_GET_ALL_DESC _IOR ('U', 105, struct usb_all_desc) + + +#endif /* _USB_H_ */ diff --git a/sys/dev/usb/usb_if.m b/sys/dev/usb/usb_if.m new file mode 100644 index 0000000..f9cb673 --- /dev/null +++ b/sys/dev/usb/usb_if.m @@ -0,0 +1,11 @@ +# USB interface description +# + +INTERFACE usb; + +# The device should start probing for new children again +# +METHOD int reconfigure { + device_t dev; +}; + diff --git a/sys/dev/usb/usb_mem.h b/sys/dev/usb/usb_mem.h new file mode 100644 index 0000000..7391d18 --- /dev/null +++ b/sys/dev/usb/usb_mem.h @@ -0,0 +1,91 @@ +/* $NetBSD: usb_mem.h,v 1.1 1998/07/24 21:09:08 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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__) +typedef struct usb_block_dma { + bus_dma_tag_t tag; + bus_dmamap_t map; + caddr_t kaddr; + bus_dma_segment_t segs[1]; + int nsegs; + size_t size; + size_t align; + int fullblock; + LIST_ENTRY(usb_block_dma) next; +} usb_dma_block_t; + +typedef struct { + usb_dma_block_t *block; + u_int offs; +} usb_dma_t; + +#define DMAADDR(dma) ((dma)->block->segs[0].ds_addr + (dma)->offs) +#define KERNADDR(dma) ((void *)((dma)->block->kaddr + (dma)->offs)) + +usbd_status usb_allocmem __P((bus_dma_tag_t, size_t, size_t, usb_dma_t *)); +void usb_freemem __P((bus_dma_tag_t, usb_dma_t *)); + +#else + +/* FreeBSD does not have special functions for dma memory, so let's keep it + * simple for now. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/queue.h> +#include <sys/proc.h> +#include <sys/buf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <vm/vm.h> +#include <vm/pmap.h> + +#include <machine/pmap.h> /* for vtophys */ + + + +typedef void * usb_dma_t; + +#define usb_allocmem(t,s,a,p) (*(p) = malloc(s, M_USB, M_NOWAIT), (*(p) == NULL? USBD_NOMEM: USBD_NORMAL_COMPLETION)) +#define usb_freemem(t,p) (free(*(p), M_USB)) + +#define DMAADDR(dma) (vtophys(*(dma))) +#define KERNADDR(dma) ((void *) *(dma)) +#endif + diff --git a/sys/dev/usb/usb_port.h b/sys/dev/usb/usb_port.h new file mode 100644 index 0000000..df66674 --- /dev/null +++ b/sys/dev/usb/usb_port.h @@ -0,0 +1,79 @@ +/* Macro's to cope with the differences between NetBSD and FreeBSD + */ + +/* + * NetBSD + * + */ + +#if defined(__NetBSD__) +#include "opt_usbverbose.h" + +#define DEVICE_NAME(bdev) \ + printf("%s: ", (bdev).dv_xname) +#define DEVICE_MSG(bdev + +typedef struct device bdevice; /* base device */ + + + +/* + * FreeBSD + * + */ + +#elif defined(__FreeBSD__) +#include "opt_usb.h" +#define DEVICE_NAME(bdev) \ + printf("%s%d: ", \ + device_get_name(bdev), device_get_unit(bdev)) + +/* XXX Change this when FreeBSD has memset + */ +#define memset(d, v, s) \ + do{ \ + if ((v) == 0) \ + bzero((d), (s)); \ + else \ + panic("Non zero filler for memset, cannot handle!"); \ + } while (0) + +/* XXX can't we put this somehow into a typedef? */ +#define bdevice device_t /* base device */ + +#define USB_MODULE(name, driver, devclass) \ + DRIVER_MODULE((name), "usb", (driver), (devclass), usb_driver_load, 0) +#endif + + + + +/* + * General + * + */ + +#define DEVICE_MSG(bdev, s) (DEVICE_NAME(bdev), printf s) +#define DEVICE_ERROR(bdev, s) DEVICE_MSG(bdev, s) + + +/* Returns from attach for NetBSD vs. FreeBSD + */ + +/* Error returns */ +#if defined(__NetBSD__) +#define ATTACH_ERROR_RETURN return +#define ATTACH_SUCCESS_RETURN return +#elif defined(__FreeBSD__) +#define ATTACH_ERROR_RETURN return ENXIO +#define ATTACH_SUCCESS_RETURN return 0 +#endif + + +/* + * The debugging subsystem + */ + +/* XXX to be filled in + */ + diff --git a/sys/dev/usb/usb_quirks.c b/sys/dev/usb/usb_quirks.c new file mode 100644 index 0000000..e707f56 --- /dev/null +++ b/sys/dev/usb/usb_quirks.c @@ -0,0 +1,89 @@ +/* $NetBSD: usb_quirks.c,v 1.1 1998/07/12 19:52:00 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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 <dev/usb/usb_port.h> + +#include <sys/param.h> +#include <sys/systm.h> +#if defined(__NetBSD__) +#include <sys/device.h> +#endif +#include <sys/select.h> + +#include <dev/usb/usb.h> + +#include <dev/usb/usbdevs.h> +#include <dev/usb/usb_quirks.h> + +struct usbd_quirk_entry { + u_int16_t idVendor; + u_int16_t idProduct; + u_int16_t bcdDevice; + struct usbd_quirks quirks; +} quirks[] = { + { USB_VENDOR_GENIUS, USB_PRODUCT_GENIUS_NICHE, 0x100, { UQ_NO_SET_PROTO}}, + { USB_VENDOR_INSIDEOUT,USB_PRODUCT_INSIDEOUT_EDGEPORT4, + 0x094, { UQ_SWAP_UNICODE}}, + { USB_VENDOR_UNIXTAR, USB_PRODUCT_UNIXTAR_UTUSB41, 0x100, { UQ_HUB_POWER }}, + { 0, 0, 0, { 0 } } +}; + +struct usbd_quirks usbd_no_quirk = { 0 }; + +struct usbd_quirks * +usbd_find_quirk(d) + usb_device_descriptor_t *d; +{ + struct usbd_quirk_entry *t; + + for (t = quirks; t->idVendor != 0; t++) { + if (t->idVendor == UGETW(d->idVendor) && + t->idProduct == UGETW(d->idProduct) && + t->bcdDevice == UGETW(d->bcdDevice)) + break; + } +#ifdef USB_DEBUG + { extern int usbdebug; + if (usbdebug && t->quirks.uq_flags) + printf("quirk %d/%d/%x: %d\n", + UGETW(d->idVendor), UGETW(d->idProduct), + UGETW(d->bcdDevice), t->quirks.uq_flags); + } +#endif + return (&t->quirks); +} diff --git a/sys/dev/usb/usb_quirks.h b/sys/dev/usb/usb_quirks.h new file mode 100644 index 0000000..6d9e822 --- /dev/null +++ b/sys/dev/usb/usb_quirks.h @@ -0,0 +1,49 @@ +/* $NetBSD: usb_quirks.h,v 1.1 1998/07/12 19:52:00 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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. + */ + +struct usbd_quirks { + u_int32_t uq_flags; /* Device problems: */ +#define UQ_NO_SET_PROTO 0x01 /* cannot handle SET PROTOCOL */ +#define UQ_SWAP_UNICODE 0x02 /* has some Unicode strings swapped. */ +#define UQ_HUB_POWER 0x04 /* does not respond correctly to get + device status; use get hub status. */ +}; + +extern struct usbd_quirks usbd_no_quirk; + +struct usbd_quirks *usbd_find_quirk __P((usb_device_descriptor_t *)); diff --git a/sys/dev/usb/usb_subr.c b/sys/dev/usb/usb_subr.c new file mode 100644 index 0000000..63fc7e4 --- /dev/null +++ b/sys/dev/usb/usb_subr.c @@ -0,0 +1,880 @@ +/* $NetBSD: usb_subr.c,v 1.7 1998/08/02 22:30:53 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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 <dev/usb/usb_port.h> + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#if defined(__NetBSD__) +#include <sys/device.h> +#elif defined(__FreeBSD__) +#include <sys/module.h> +#include <sys/bus.h> +#endif +#include <sys/proc.h> +#include <sys/select.h> + +#include <dev/usb/usb.h> + +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usbdivar.h> +#include <dev/usb/usbdevs.h> +#include <dev/usb/usb_quirks.h> + +#if defined(__FreeBSD__) +#include <machine/clock.h> +#define delay(d) DELAY(d) +#endif + +#ifdef USB_DEBUG +#define DPRINTF(x) if (usbdebug) printf x +#define DPRINTFN(n,x) if (usbdebug>(n)) printf x +extern int usbdebug; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +static usbd_status usbd_set_config __P((usbd_device_handle, int)); +char *usbd_get_string __P((usbd_device_handle, int, char *)); +int usbd_getnewaddr __P((usbd_bus_handle bus)); +int usbd_print __P((void *aux, const char *pnp)); +#if defined(__NetBSD__) +int usbd_submatch __P((struct device *, struct cfdata *cf, void *)); +#endif +usb_interface_descriptor_t *usbd_find_idesc __P((usb_config_descriptor_t *cd, + int ino, int ano)); +usbd_status usbd_fill_iface_data __P((usbd_device_handle dev, int i, int a)); +void usbd_free_iface_data __P((usbd_device_handle dev, int ifcno)); +void usbd_kill_pipe __P((usbd_pipe_handle)); +static usbd_status usbd_probe_and_attach(bdevice *parent, usbd_device_handle dev); + +#ifdef USBVERBOSE +typedef u_int16_t usb_vendor_id_t; +typedef u_int16_t usb_product_id_t; + +/* + * Descriptions of of known vendors and devices ("products"). + */ +struct usb_knowndev { + usb_vendor_id_t vendor; + usb_product_id_t product; + int flags; + char *vendorname, *productname; +}; +#define USB_KNOWNDEV_NOPROD 0x01 /* match on vendor only */ + +#include <dev/usb/usbdevs_data.h> +#endif /* USBVERBOSE */ + + +char * +usbd_get_string(dev, si, buf) + usbd_device_handle dev; + int si; + char *buf; +{ + int swap = dev->quirks->uq_flags & UQ_SWAP_UNICODE; + usb_device_request_t req; + usb_string_descriptor_t us; + char *s; + int i, n; + u_int16_t c; + usbd_status r; + int lang; /* NWH */ + + if (si == 0) + return 0; + + /* NWH added fetching of language + * See 9.6.5 (spec v1.0) + */ + req.bmRequestType = UT_READ_DEVICE; + req.bRequest = UR_GET_DESCRIPTOR; + USETW2(req.wValue, UDESC_STRING, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, 4); /* only first word in bString */ + r = usbd_do_request(dev, &req, &us); + if (r != USBD_NORMAL_COMPLETION) + return 0; + lang = UGETW(us.bString[0]); + /* NWH end */ + + req.bmRequestType = UT_READ_DEVICE; + req.bRequest = UR_GET_DESCRIPTOR; + USETW2(req.wValue, UDESC_STRING, si); + USETW(req.wIndex, lang); + USETW(req.wLength, 1); /* only size byte first */ + r = usbd_do_request(dev, &req, &us); + if (r != USBD_NORMAL_COMPLETION) + return 0; + USETW(req.wLength, us.bLength); /* the whole string */ + r = usbd_do_request(dev, &req, &us); + if (r != USBD_NORMAL_COMPLETION) + return 0; + s = buf; + n = us.bLength / 2 - 1; + for (i = 0; i < n; i++) { + c = UGETW(us.bString[i]); + /* Convert from Unicode, handle buggy strings. */ + if ((c & 0xff00) == 0) + *s++ = c; + else if ((c & 0x00ff) == 0 && swap) + *s++ = c >> 8; + else + *s++ = '?'; + } + *s++ = 0; + return buf; +} + +void +usbd_devinfo_vp(dev, v, p) + usbd_device_handle dev; + char *v, *p; +{ + usb_device_descriptor_t *udd = &dev->ddesc; + char *vendor = 0, *product = 0; +#ifdef USBVERBOSE + struct usb_knowndev *kdp; +#endif + + if (!dev) { + DPRINTF(("usbd_devinfo_vp: dev not set\n")); + return; + } + if (!v) { + DPRINTF(("usbd_devinfo_vp: v not set\n")); + return; + } + if (!p) { + DPRINTF(("usbd_devinfo_vp: p not set\n")); + return; + } + + vendor = usbd_get_string(dev, udd->iManufacturer, v); + product = usbd_get_string(dev, udd->iProduct, p); +#ifdef USBVERBOSE + if (!vendor) { + for(kdp = usb_knowndevs; + kdp->vendorname != NULL; + kdp++) { + if (kdp->vendor == UGETW(udd->idVendor) && + (kdp->product == UGETW(udd->idProduct) || + (kdp->flags & USB_KNOWNDEV_NOPROD) != 0)) + break; + } + if (kdp->vendorname == NULL) + vendor = product = NULL; + else { + vendor = kdp->vendorname; + product = (kdp->flags & USB_KNOWNDEV_NOPROD) == 0 ? + kdp->productname : NULL; + } + } +#endif + if (vendor) + strcpy(v, vendor); + else + sprintf(v, "vendor 0x%04x", UGETW(udd->idVendor)); + if (product) + strcpy(p, product); + else + sprintf(p, "product 0x%04x", UGETW(udd->idProduct)); +} + +int +usbd_printBCD(cp, bcd) + char *cp; + int bcd; +{ + return (sprintf(cp, "%x.%02x", bcd >> 8, bcd & 0xff)); +} + +void +usbd_devinfo(dev, showclass, cp) + usbd_device_handle dev; + int showclass; + char *cp; +{ + usb_device_descriptor_t *udd = &dev->ddesc; + char vendor[USB_MAX_STRING_LEN]; + char product[USB_MAX_STRING_LEN]; + int bcdDevice, bcdUSB; + + usbd_devinfo_vp(dev, vendor, product); + cp += sprintf(cp, "%s", vendor); + cp += sprintf(cp, " %s", product); + if (showclass) + cp += sprintf(cp, " (class %d/%d)", + udd->bDeviceClass, udd->bDeviceSubClass); + bcdUSB = UGETW(udd->bcdUSB); + bcdDevice = UGETW(udd->bcdDevice); + cp += sprintf(cp, " (rev "); + cp += usbd_printBCD(cp, bcdUSB); + *cp++ = '/'; + cp += usbd_printBCD(cp, bcdDevice); + *cp++ = ')'; + cp += sprintf(cp, " (addr %d)", dev->address); +} + +/* Delay for a certain number of ms */ +void +usbd_delay_ms(bus, ms) + usbd_bus_handle bus; + int ms; +{ + /* Wait at least two clock ticks so we know the time has passed. */ + if (bus->use_polling) + delay((ms+1) * 1000); + else + tsleep(&ms, PRIBIO, "usbdly", (ms*hz+999)/1000 + 1); +} + +usbd_status +usbd_reset_port(dev, port, ps) + usbd_device_handle dev; + int port; + usb_port_status_t *ps; +{ + usb_device_request_t req; + usbd_status r; + int n; + + req.bmRequestType = UT_WRITE_CLASS_OTHER; + req.bRequest = UR_SET_FEATURE; + USETW(req.wValue, UHF_PORT_RESET); + USETW(req.wIndex, port); + USETW(req.wLength, 0); + r = usbd_do_request(dev, &req, 0); + DPRINTFN(1,("usbd_reset_port: port %d reset done, error=%d\n", + port, r)); + if (r != USBD_NORMAL_COMPLETION) + return (r); + n = 10; + do { + /* Wait for device to recover from reset. */ + usbd_delay_ms(dev->bus, USB_PORT_RESET_DELAY); + r = usbd_get_port_status(dev, port, ps); + if (r != USBD_NORMAL_COMPLETION) { + DPRINTF(("usbd_reset_port: get status failed %d\n",r)); + return (r); + } + } while ((UGETW(ps->wPortChange) & UPS_C_PORT_RESET) == 0 && --n > 0); + if (n == 0) { + printf("usbd_reset_port: timeout\n"); + return (USBD_IOERROR); + } + r = usbd_clear_port_feature(dev, port, UHF_C_PORT_RESET); +#ifdef USB_DEBUG + if (r != USBD_NORMAL_COMPLETION) + DPRINTF(("usbd_reset_port: clear port feature failed %d\n",r)); +#endif + return (r); +} + +usb_interface_descriptor_t * +usbd_find_idesc(cd, ino, ano) + usb_config_descriptor_t *cd; + int ino; + int ano; +{ + char *p = (char *)cd; + char *end = p + UGETW(cd->wTotalLength); + usb_interface_descriptor_t *d; + + for (; p < end; p += d->bLength) { + d = (usb_interface_descriptor_t *)p; + if (p + d->bLength <= end && + d->bDescriptorType == UDESC_INTERFACE && + d->bInterfaceNumber == ino && d->bAlternateSetting == ano) + return (d); + } + return (0); +} + +usbd_status +usbd_fill_iface_data(dev, ino, ano) + usbd_device_handle dev; + int ino; + int ano; +{ + usbd_interface_handle ifc = &dev->ifaces[ino]; + usb_endpoint_descriptor_t *ed; + char *p, *end; + int endpt, nendpt; + usbd_status r; + + DPRINTFN(5,("usbd_fill_iface_data: ino=%d ano=%d\n", ino, ano)); + ifc->device = dev; + ifc->state = USBD_INTERFACE_ACTIVE; + ifc->idesc = usbd_find_idesc(dev->cdesc, ino, ano); + if (ifc->idesc == 0) + return (USBD_INVAL); + nendpt = ifc->idesc->bNumEndpoints; + DPRINTFN(10,("usbd_fill_iface_data: found idesc n=%d\n", nendpt)); + if (nendpt != 0) { + ifc->endpoints = malloc(nendpt * sizeof(struct usbd_endpoint), + M_USB, M_NOWAIT); + if (ifc->endpoints == 0) + return (USBD_NOMEM); + } else + ifc->endpoints = 0; + ifc->priv = 0; + p = (char *)ifc->idesc + ifc->idesc->bLength; + end = (char *)dev->cdesc + UGETW(dev->cdesc->wTotalLength); + for (endpt = 0; endpt < nendpt; endpt++) { + DPRINTFN(10,("usbd_fill_iface_data: endpt=%d\n", endpt)); + for (; p < end; p += ed->bLength) { + ed = (usb_endpoint_descriptor_t *)p; + DPRINTFN(10,("usbd_fill_iface_data: p=%p end=%p len=%d type=%d\n", + p, end, ed->bLength, ed->bDescriptorType)); + if (p + ed->bLength <= end && + ed->bDescriptorType == UDESC_ENDPOINT) + goto found; + if (ed->bDescriptorType == UDESC_INTERFACE) + break; + } + r = USBD_INVAL; + goto bad; + found: + ifc->endpoints[endpt].edesc = ed; + ifc->endpoints[endpt].state = USBD_ENDPOINT_ACTIVE; + ifc->endpoints[endpt].refcnt = 0; + ifc->endpoints[endpt].toggle = 0; + } + LIST_INIT(&ifc->pipes); + return (USBD_NORMAL_COMPLETION); + bad: + free(ifc->endpoints, M_USB); + return (r); +} + +void +usbd_free_iface_data(dev, ifcno) + usbd_device_handle dev; + int ifcno; +{ + usbd_interface_handle ifc = &dev->ifaces[ifcno]; + if (ifc->endpoints) + free(ifc->endpoints, M_USB); +} + +static usbd_status +usbd_set_config(dev, conf) + usbd_device_handle dev; + int conf; +{ + usb_device_request_t req; + + req.bmRequestType = UT_WRITE_DEVICE; + req.bRequest = UR_SET_CONFIG; + USETW(req.wValue, conf); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + return (usbd_do_request(dev, &req, 0)); +} + +usbd_status +usbd_set_config_no(dev, no, msg) + usbd_device_handle dev; + int no; + int msg; +{ + usb_status_t ds; + usb_hub_status_t hs; + usb_config_descriptor_t cd, *cdp; + usbd_status r; + int ifcno, nifc, len, selfpowered, power; + + DPRINTFN(5, ("usbd_set_config_no: dev=%p no=%d\n", dev, no)); + + /* XXX check that all interfaces are idle */ + if (dev->config != 0) { + DPRINTF(("usbd_set_config_no: free old config\n")); + /* Free all configuration data structures. */ + nifc = dev->cdesc->bNumInterface; + for (ifcno = 0; ifcno < nifc; ifcno++) + usbd_free_iface_data(dev, ifcno); + free(dev->ifaces, M_USB); + free(dev->cdesc, M_USB); + dev->ifaces = 0; + dev->cdesc = 0; + dev->config = 0; + dev->state = USBD_DEVICE_ADDRESSED; + } + + /* Figure out what config number to use. */ + r = usbd_get_config_desc(dev, no, &cd); + if (r != USBD_NORMAL_COMPLETION) + return (r); + len = UGETW(cd.wTotalLength); + cdp = malloc(len, M_USB, M_NOWAIT); + if (cdp == 0) + return (USBD_NOMEM); + r = usbd_get_desc(dev, UDESC_CONFIG, no, len, cdp); + if (r != USBD_NORMAL_COMPLETION) + goto bad; + selfpowered = 0; + if (cdp->bmAttributes & UC_SELF_POWERED) { + /* May be self powered. */ + if (cdp->bmAttributes & UC_BUS_POWERED) { + /* Must ask device. */ + if (dev->quirks->uq_flags & UQ_HUB_POWER) { + /* Buggy hub, use hub descriptor. */ + r = usbd_get_hub_status(dev, &hs); + if (r == USBD_NORMAL_COMPLETION && + !(UGETW(hs.wHubStatus) & UHS_LOCAL_POWER)) + selfpowered = 1; + } else { + r = usbd_get_device_status(dev, &ds); + if (r == USBD_NORMAL_COMPLETION && + (UGETW(ds.wStatus) & UDS_SELF_POWERED)) + selfpowered = 1; + } + DPRINTF(("usbd_set_config_no: status=0x%04x, error=%d\n", + UGETW(ds.wStatus), r)); + } else + selfpowered = 1; + } + DPRINTF(("usbd_set_config_no: (addr %d) attr=0x%02x, selfpowered=%d, power=%d, powerquirk=%x\n", + dev->address, cdp->bmAttributes, + selfpowered, cdp->bMaxPower * 2, + dev->quirks->uq_flags & UQ_HUB_POWER)); +#ifdef USB_DEBUG + if (!dev->powersrc) { + printf("usbd_set_config_no: No power source?\n"); + return (EIO); + } +#endif + power = cdp->bMaxPower * 2; + if (power > dev->powersrc->power) { + /* XXX print nicer message. */ + if (msg) + DEVICE_ERROR(dev->bus->bdev, + ("device addr %d (config %d) exceeds power budget, %d mA > %d mA\n", + dev->address, + cdp->bConfigurationValue, + power, dev->powersrc->power)); + r = USBD_NO_POWER; + goto bad; + } + dev->power = power; + dev->self_powered = selfpowered; + + r = usbd_set_config(dev, cdp->bConfigurationValue); + if (r != USBD_NORMAL_COMPLETION) { + DPRINTF(("usbd_set_config_no: setting config=%d failed, error=%d\n", + cdp->bConfigurationValue, r)); + goto bad; + } + DPRINTF(("usbd_set_config_no: setting new config %d\n", + cdp->bConfigurationValue)); + nifc = cdp->bNumInterface; + dev->ifaces = malloc(nifc * sizeof(struct usbd_interface), + M_USB, M_NOWAIT); + if (dev->ifaces == 0) { + r = USBD_NOMEM; + goto bad; + } + DPRINTFN(5,("usbd_set_config_no: dev=%p cdesc=%p\n", dev, cdp)); + dev->cdesc = cdp; + dev->config = cdp->bConfigurationValue; + dev->state = USBD_DEVICE_CONFIGURED; + for (ifcno = 0; ifcno < nifc; ifcno++) { + r = usbd_fill_iface_data(dev, ifcno, 0); + if (r != USBD_NORMAL_COMPLETION) { + while (--ifcno >= 0) + usbd_free_iface_data(dev, ifcno); + goto bad; + } + } + + return (USBD_NORMAL_COMPLETION); + + bad: + free(cdp, M_USB); + return (r); +} + +/* XXX add function for alternate settings */ + +usbd_status +usbd_setup_pipe(dev, iface, ep, pipe) + usbd_device_handle dev; + usbd_interface_handle iface; + struct usbd_endpoint *ep; + usbd_pipe_handle *pipe; +{ + usbd_pipe_handle p; + usbd_status r; + + *pipe = NULL; + + DPRINTFN(1,("usbd_setup_pipe: dev=%p iface=%p ep=%p pipe=%p\n", + dev, iface, ep, pipe)); + p = malloc(dev->bus->pipe_size, M_USB, M_NOWAIT); + if (p == 0) + return (USBD_NOMEM); + p->device = dev; + p->iface = iface; + p->state = USBD_PIPE_ACTIVE; + p->endpoint = ep; + ep->refcnt++; + p->refcnt = 1; + p->intrreqh = 0; + p->running = 0; + SIMPLEQ_INIT(&p->queue); + r = dev->bus->open_pipe(p); + if (r != USBD_NORMAL_COMPLETION) { + DPRINTF(("usbd_setup_pipe: endpoint=%d failed, error=%d\n", + ep->edesc->bEndpointAddress, r)); + free(p, M_USB); + return (r); + } + *pipe = p; + return (USBD_NORMAL_COMPLETION); +} + +/* Abort the device control pipe. */ +void +usbd_kill_pipe(pipe) + usbd_pipe_handle pipe; +{ + pipe->methods->close(pipe); + pipe->endpoint->refcnt--; + free(pipe, M_USB); +} + +int +usbd_getnewaddr(bus) + usbd_bus_handle bus; +{ + int addr; + + for (addr = 1; addr < USB_MAX_DEVICES; addr++) + if (bus->devices[addr] == 0) + return (addr); + return (-1); +} + + +/* NWH separated out the probe and attach code + */ +static usbd_status +usbd_probe_and_attach(parent, dev) + bdevice *parent; + usbd_device_handle dev; +{ + struct usb_attach_arg uaa; + usb_device_descriptor_t *dd = &dev->ddesc; + int r, found, i, confi; + +#if defined(__FreeBSD__) + dev->bdev = device_add_child(*parent, NULL, -1, &uaa); + if (!dev->bdev) { + DEVICE_ERROR(dev->bus->bdev, ("Device creation failed\n")); + return ENXIO; + } +#endif + + uaa.device = dev; + uaa.iface = 0; + uaa.usegeneric = 0; + + /* First try with device specific drivers. */ +#if defined(__NetBSD__) + if (config_found_sm(parent, &uaa, usbd_print, usbd_submatch) != 0) +#elif defined(__FreeBSD__) + if (device_probe_and_attach(dev->bdev) == 0) +#endif + return (USBD_NORMAL_COMPLETION); + + DPRINTF(("usbd_new_device: no device driver found\n")); + + /* Next try with interface drivers. */ + for (confi = 0; confi < dd->bNumConfigurations; confi++) { + r = usbd_set_config_no(dev, confi, 1); + if (r != USBD_NORMAL_COMPLETION) { + DEVICE_ERROR(*parent, ("set config failed, r=%d\n", r)); + return r; + } + for (found = i = 0; i < dev->cdesc->bNumInterface; i++) { + uaa.iface = &dev->ifaces[i]; +#if defined(__NetBSD__) + if (config_found_sm(parent, &uaa, usbd_print, + usbd_submatch)) +#elif defined(__FreeBSD__) + if (device_probe_and_attach(dev->bdev) == 0) +#endif + found++; + } + if (found != 0) + return (USBD_NORMAL_COMPLETION); + } + /* No interfaces were attach in any of the configurations. */ + if (dd->bNumConfigurations > 0) + usbd_set_config_no(dev, 0, 0); + + DPRINTF(("usbd_new_device: no interface drivers found\n")); + + /* Finally try the generic driver. */ + uaa.iface = 0; + uaa.usegeneric = 1; +#if defined(__NetBSD__) + if (config_found_sm(parent, &uaa, usbd_print, usbd_submatch) != 0) + return (USBD_NORMAL_COMPLETION); +#elif defined(__FreeBSD__) + if (device_probe_and_attach(dev->bdev) == 0) + return (USBD_NORMAL_COMPLETION); +#endif + + /* generic attach failed, but leave the device as it is + * we just did not find any drivers, that's all. the device is + * fully operational and not harming anyone + */ + DPRINTF(("usbd_new_device: generic attach failed\n")); + return USBD_NORMAL_COMPLETION; +} + + + +/* + * Called when a new device has been put in the powered state, + * but not yet in the addressed state. + * Get initial descriptor, set the address, get full descriptor, + * and attach a driver. + */ +usbd_status +usbd_new_device(parent, bus, depth, lowspeed, port, up) + bdevice *parent; + usbd_bus_handle bus; + int depth; + int lowspeed; + int port; + struct usbd_port *up; +{ + usbd_device_handle dev; + usb_device_descriptor_t *dd; + usbd_status r; + int addr; + int i; + + DPRINTF(("usbd_new_device bus=%p depth=%d lowspeed=%d\n", + bus, depth, lowspeed)); + addr = usbd_getnewaddr(bus); + if (addr < 0) { + DEVICE_ERROR(bus->bdev, ("No free USB addresses, new device ignored.\n")); + return (USBD_NO_ADDR); + } + + dev = malloc(sizeof *dev, M_USB, M_NOWAIT); + if (dev == 0) + return (USBD_NOMEM); + memset(dev, 0, sizeof(*dev)); + + dev->bus = bus; + + /* Set up default endpoint handle. */ + dev->def_ep.edesc = &dev->def_ep_desc; + dev->def_ep.state = USBD_ENDPOINT_ACTIVE; + dev->def_ep.refcnt = 0; + dev->def_ep.toggle = 0; /* XXX */ + + /* Set up default endpoint descriptor. */ + dev->def_ep_desc.bLength = USB_ENDPOINT_DESCRIPTOR_SIZE; + dev->def_ep_desc.bDescriptorType = UDESC_ENDPOINT; + dev->def_ep_desc.bEndpointAddress = USB_CONTROL_ENDPOINT; + dev->def_ep_desc.bmAttributes = UE_CONTROL; + USETW(dev->def_ep_desc.wMaxPacketSize, USB_MAX_IPACKET); + dev->def_ep_desc.bInterval = 0; + + dev->state = USBD_DEVICE_DEFAULT; + dev->quirks = &usbd_no_quirk; + dev->address = USB_START_ADDR; + dev->ddesc.bMaxPacketSize = 0; + dev->lowspeed = lowspeed != 0; + dev->depth = depth; + dev->powersrc = up; + +#if defined(__FreeBSD__) + dev->bdev = NULL; +#endif + + /* Establish the the default pipe. */ + r = usbd_setup_pipe(dev, 0, &dev->def_ep, &dev->default_pipe); + if (r != USBD_NORMAL_COMPLETION) { + usbd_remove_device(dev, up); + return r; + } + + up->device = dev; + dd = &dev->ddesc; + /* Try a few times in case the device is slow (i.e. outside specs.) */ + for (i = 0; i < 5; i++) { + /* Get the first 8 bytes of the device descriptor. */ + r = usbd_get_desc(dev, UDESC_DEVICE, 0, USB_MAX_IPACKET, dd); + if (r == USBD_NORMAL_COMPLETION) + break; + usbd_delay_ms(dev->bus, 200); + } + if (r != USBD_NORMAL_COMPLETION) { + DPRINTFN(-1, ("usbd_new_device: addr=%d, getting first desc failed\n", + addr)); + usbd_remove_device(dev, up); + return r; + } + + DPRINTF(("usbd_new_device: adding unit addr=%d, rev=%02x, class=%d, subclass=%d, protocol=%d, maxpacket=%d, ls=%d\n", + addr, UGETW(dd->bcdUSB), dd->bDeviceClass, dd->bDeviceSubClass, + dd->bDeviceProtocol, dd->bMaxPacketSize, dev->lowspeed)); + + USETW(dev->def_ep_desc.wMaxPacketSize, dd->bMaxPacketSize); + + /* Get the full device descriptor. */ + r = usbd_get_device_desc(dev, dd); + if (r != USBD_NORMAL_COMPLETION) { + DPRINTFN(-1, ("usbd_new_device: addr=%d, getting full desc failed\n", addr)); + usbd_remove_device(dev, up); + return r; + } + + /* Figure out what's wrong with this device. */ + dev->quirks = usbd_find_quirk(dd); + + /* Set the address */ + r = usbd_set_address(dev, addr); + if (r != USBD_NORMAL_COMPLETION) { + DPRINTFN(-1,("usbd_new_device: set address %d failed\n",addr)); + usbd_remove_device(dev, up); + return USBD_SET_ADDR_FAILED; + } + dev->address = addr; /* New device address now */ + dev->state = USBD_DEVICE_ADDRESSED; + bus->devices[addr] = dev; + + /* Assume 100mA bus powered for now. Changed when configured. */ + dev->power = USB_MIN_POWER; + dev->self_powered = 0; + + DPRINTF(("usbd_new_device: new dev (addr %d), dev=%p, parent=%p\n", + addr, dev, parent)); + + r = usbd_probe_and_attach(parent, dev); + if (r) { + usbd_remove_device(dev, up); + return r; + } + + return (USBD_NORMAL_COMPLETION); +} + +void +usbd_remove_device(dev, up) + usbd_device_handle dev; + struct usbd_port *up; +{ + DPRINTF(("usbd_remove_device: %p\n", dev)); + +#if defined(__NetBSD__) + /* XXX bit of a hack, only for hubs the detach is called + * the code should register a detach function and use that one + * to detach a device porperly + */ + if (dev->bdev && dev->hub) + uhub_detach(dev->hub->hubdata); +#elif defined(__FreeBSD__) + if (dev->bdev) + device_delete_child(device_get_parent(dev->bdev), dev->bdev); +#endif + + if (dev->default_pipe) + usbd_kill_pipe(dev->default_pipe); + up->device = 0; + dev->bus->devices[dev->address] = 0; + + free(dev, M_USB); +} + +#if defined(__NetBSD__) +int +usbd_print(aux, pnp) + void *aux; + const char *pnp; +{ + struct usb_attach_arg *uaa = aux; + char devinfo[1024]; + + DPRINTFN(15, ("usbd_print dev=%p\n", uaa->device)); + if (pnp) { + if (!uaa->usegeneric) + return (QUIET); + usbd_devinfo(uaa->device, 1, devinfo); + printf("%s at %s", devinfo, pnp); + } + if (uaa->port != 0) + printf(" port %d", uaa->port); + return (UNCONF); +} + +int +usbd_submatch(parent, cf, aux) + struct device *parent; + struct cfdata *cf; + void *aux; +{ + struct usb_attach_arg *uaa = aux; + + if (uaa->port != 0 && + cf->uhubcf_port != UHUB_UNK_PORT && + cf->uhubcf_port != uaa->port) + return 0; + return ((*cf->cf_attach->ca_match)(parent, cf, aux)); +} + +#elif defined(__FreeBSD__) +static void +usbd_bus_print_child(device_t bus, device_t dev) +{ + /* FIXME print the device address and the configuration used + */ +} +#endif diff --git a/sys/dev/usb/usbdevs.h b/sys/dev/usb/usbdevs.h new file mode 100644 index 0000000..94e2973 --- /dev/null +++ b/sys/dev/usb/usbdevs.h @@ -0,0 +1,121 @@ +/* $NetBSD: usbdevs.h,v 1.6 1998/10/05 02:31:13 mark Exp $ */ + +/* + * THIS FILE AUTOMATICALLY GENERATED. DO NOT EDIT. + * + * generated from: + * NetBSD: usbdevs,v 1.5 1998/10/05 02:30:17 mark Exp + */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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. + */ + +/* + * List of known USB vendors + */ + +#define USB_VENDOR_NEC 0x0409 /* NEC */ +#define USB_VENDOR_KODAK 0x040a /* Eastman Kodak */ +#define USB_VENDOR_NANAO 0x0440 /* Nanao */ +#define USB_VENDOR_UNIXTAR 0x0451 /* Unixtar */ +#define USB_VENDOR_GENIUS 0x0458 /* Genius */ +#define USB_VENDOR_CHERRY 0x046a /* Cherry */ +#define USB_VENDOR_PHILIPS 0x0471 /* Philips */ +#define USB_VENDOR_CONNECTIX 0x0478 /* Connectix */ +#define USB_VENDOR_CYPRESS 0x04b4 /* Cypress Semicondutor */ +#define USB_VENDOR_EIZO 0x056d /* EIZO */ +#define USB_VENDOR_BELKIN 0x05ab /* Belkin */ +#define USB_VENDOR_EIZONANAO 0x05e7 /* EIZO Nanao */ +#define USB_VENDOR_CHIC 0x05fe /* Chic Technology */ +#define USB_VENDOR_PLX 0x10b5 /* PLX */ +#define USB_VENDOR_INSIDEOUT 0x1608 /* Inside Out Networks */ +#define USB_VENDOR_INTEL 0x8086 /* Intel */ + +/* + * List of known products. Grouped by vendor. + */ + +/* NEC products */ +#define USB_PRODUCT_NEC_HUB 0x55aa /* hub */ + +/* Kodak products */ +#define USB_PRODUCT_KODAK_DC260 0x0110 /* Digital Science DC260 */ + +/* Nanao products */ +#define USB_PRODUCT_NANAO_HUB 0x0000 /* hub */ +#define USB_PRODUCT_NANAO_MONITOR 0x0001 /* monitor */ + +/* Unixtar products */ +#define USB_PRODUCT_UNIXTAR_UTUSB41 0x1446 /* UT-USB41 */ + +/* Genius products */ +#define USB_PRODUCT_GENIUS_NICHE 0x0001 /* Niche mouse */ +#define USB_PRODUCT_GENIUS_FLIGHT2000 0x1004 /* Flight 2000 joystick */ + +/* Cherry products */ +#define USB_PRODUCT_CHERRY_MY3000KBD 0x0001 /* My3000 keyboard */ +#define USB_PRODUCT_CHERRY_MY3000HUB 0x0003 /* My3000 hub */ + +/* Philips products */ +#define USB_PRODUCT_PHILIPS_DSS 0x0101 /* DSS 350 Digital Speaker System */ +#define USB_PRODUCT_PHILIPS_HUB 0x0201 /* hub */ + +/* Connectix products */ +#define USB_PRODUCT_CONNECTIX_QUICKCAM 0x0001 /* QuickCam */ + +/* Cypress Semiconduuctor products */ +#define USB_PRODUCT_CYPRESS_MOUSE 0x0001 /* mouse */ + +/* Belkin products */ +#define USB_PRODUCT_BELKIN_F5U002 0x0002 /* Parallel printer adapter */ + +/* EIZO Nanao products */ +#define USB_PRODUCT_EIZO_HUB 0x0000 /* hub */ +#define USB_PRODUCT_EIZO_MONITOR 0x0001 /* monitor */ +#define USB_PRODUCT_EIZONANAO_HUB 0x0000 /* hub */ +#define USB_PRODUCT_EIZONANAO_MONITOR 0x0001 /* monitor */ + +/* Chic Technology */ +#define USB_PRODUCT_CHIC_MOUSE1 0x0001 /* mouse */ + +/* PLX products */ +#define USB_PRODUCT_PLX_TESTBOARD 0x9060 /* test board */ + +/* Inside Out Networks products */ +#define USB_PRODUCT_INSIDEOUT_EDGEPORT4 0x0001 /* EdgePort/4 */ + +/* Intel products */ +#define USB_PRODUCT_INTEL_TESTBOARD 0x9890 /* 82930 test board */ diff --git a/sys/dev/usb/usbdevs_data.h b/sys/dev/usb/usbdevs_data.h new file mode 100644 index 0000000..d6b4133 --- /dev/null +++ b/sys/dev/usb/usbdevs_data.h @@ -0,0 +1,276 @@ +/* $NetBSD: usbdevs_data.h,v 1.6 1998/10/05 02:31:14 mark Exp $ */ + +/* + * THIS FILE AUTOMATICALLY GENERATED. DO NOT EDIT. + * + * generated from: + * NetBSD: usbdevs,v 1.5 1998/10/05 02:30:17 mark Exp + */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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. + */ + +struct usb_knowndev usb_knowndevs[] = { + { + USB_VENDOR_NEC, USB_PRODUCT_NEC_HUB, + 0, + "NEC", + "hub", + }, + { + USB_VENDOR_KODAK, USB_PRODUCT_KODAK_DC260, + 0, + "Eastman Kodak", + "Digital Science DC260", + }, + { + USB_VENDOR_NANAO, USB_PRODUCT_NANAO_HUB, + 0, + "Nanao", + "hub", + }, + { + USB_VENDOR_NANAO, USB_PRODUCT_NANAO_MONITOR, + 0, + "Nanao", + "monitor", + }, + { + USB_VENDOR_UNIXTAR, USB_PRODUCT_UNIXTAR_UTUSB41, + 0, + "Unixtar", + "UT-USB41", + }, + { + USB_VENDOR_GENIUS, USB_PRODUCT_GENIUS_NICHE, + 0, + "Genius", + "Niche mouse", + }, + { + USB_VENDOR_GENIUS, USB_PRODUCT_GENIUS_FLIGHT2000, + 0, + "Genius", + "Flight 2000 joystick", + }, + { + USB_VENDOR_CHERRY, USB_PRODUCT_CHERRY_MY3000KBD, + 0, + "Cherry", + "My3000 keyboard", + }, + { + USB_VENDOR_CHERRY, USB_PRODUCT_CHERRY_MY3000HUB, + 0, + "Cherry", + "My3000 hub", + }, + { + USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_DSS, + 0, + "Philips", + "DSS 350 Digital Speaker System", + }, + { + USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_HUB, + 0, + "Philips", + "hub", + }, + { + USB_VENDOR_CONNECTIX, USB_PRODUCT_CONNECTIX_QUICKCAM, + 0, + "Connectix", + "QuickCam", + }, + { + USB_VENDOR_CYPRESS, USB_PRODUCT_CYPRESS_MOUSE, + 0, + "Cypress Semicondutor", + "mouse", + }, + { + USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U002, + 0, + "Belkin", + "Parallel printer adapter", + }, + { + USB_VENDOR_EIZO, USB_PRODUCT_EIZO_HUB, + 0, + "EIZO", + "hub", + }, + { + USB_VENDOR_EIZO, USB_PRODUCT_EIZO_MONITOR, + 0, + "EIZO", + "monitor", + }, + { + USB_VENDOR_EIZONANAO, USB_PRODUCT_EIZONANAO_HUB, + 0, + "EIZO Nanao", + "hub", + }, + { + USB_VENDOR_EIZONANAO, USB_PRODUCT_EIZONANAO_MONITOR, + 0, + "EIZO Nanao", + "monitor", + }, + { + USB_VENDOR_CHIC, USB_PRODUCT_CHIC_MOUSE1, + 0, + "Chic Technology", + "mouse", + }, + { + USB_VENDOR_PLX, USB_PRODUCT_PLX_TESTBOARD, + 0, + "PLX", + "test board", + }, + { + USB_VENDOR_INSIDEOUT, USB_PRODUCT_INSIDEOUT_EDGEPORT4, + 0, + "Inside Out Networks", + "EdgePort/4", + }, + { + USB_VENDOR_INTEL, USB_PRODUCT_INTEL_TESTBOARD, + 0, + "Intel", + "82930 test board", + }, + { + USB_VENDOR_NEC, 0, + USB_KNOWNDEV_NOPROD, + "NEC", + NULL, + }, + { + USB_VENDOR_KODAK, 0, + USB_KNOWNDEV_NOPROD, + "Eastman Kodak", + NULL, + }, + { + USB_VENDOR_NANAO, 0, + USB_KNOWNDEV_NOPROD, + "Nanao", + NULL, + }, + { + USB_VENDOR_UNIXTAR, 0, + USB_KNOWNDEV_NOPROD, + "Unixtar", + NULL, + }, + { + USB_VENDOR_GENIUS, 0, + USB_KNOWNDEV_NOPROD, + "Genius", + NULL, + }, + { + USB_VENDOR_CHERRY, 0, + USB_KNOWNDEV_NOPROD, + "Cherry", + NULL, + }, + { + USB_VENDOR_PHILIPS, 0, + USB_KNOWNDEV_NOPROD, + "Philips", + NULL, + }, + { + USB_VENDOR_CONNECTIX, 0, + USB_KNOWNDEV_NOPROD, + "Connectix", + NULL, + }, + { + USB_VENDOR_CYPRESS, 0, + USB_KNOWNDEV_NOPROD, + "Cypress Semicondutor", + NULL, + }, + { + USB_VENDOR_EIZO, 0, + USB_KNOWNDEV_NOPROD, + "EIZO", + NULL, + }, + { + USB_VENDOR_BELKIN, 0, + USB_KNOWNDEV_NOPROD, + "Belkin", + NULL, + }, + { + USB_VENDOR_EIZONANAO, 0, + USB_KNOWNDEV_NOPROD, + "EIZO Nanao", + NULL, + }, + { + USB_VENDOR_CHIC, 0, + USB_KNOWNDEV_NOPROD, + "Chic Technology", + NULL, + }, + { + USB_VENDOR_PLX, 0, + USB_KNOWNDEV_NOPROD, + "PLX", + NULL, + }, + { + USB_VENDOR_INSIDEOUT, 0, + USB_KNOWNDEV_NOPROD, + "Inside Out Networks", + NULL, + }, + { + USB_VENDOR_INTEL, 0, + USB_KNOWNDEV_NOPROD, + "Intel", + NULL, + }, + { 0, 0, 0, NULL, NULL, } +}; diff --git a/sys/dev/usb/usbdi.c b/sys/dev/usb/usbdi.c new file mode 100644 index 0000000..cce795a --- /dev/null +++ b/sys/dev/usb/usbdi.c @@ -0,0 +1,1112 @@ +/* $NetBSD: usbdi.c,v 1.9 1998/08/02 22:30:53 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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 <dev/usb/usb_port.h> + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#if defined(__NetBSD__) +#include <sys/device.h> +#else +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/conf.h> +#endif +#include <sys/malloc.h> +#include <sys/proc.h> + +#include <sys/queue.h> +#if defined(__FreeBSD__) +#include "dev/usb/queue.addendum.h" +#endif + +#include <dev/usb/usb.h> + +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usbdivar.h> + +#ifdef USB_DEBUG +#define DPRINTF(x) if (usbdebug) printf x +#define DPRINTFN(n,x) if (usbdebug>(n)) printf x +extern int usbdebug; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +static usbd_status usbd_ar_pipe __P((usbd_pipe_handle pipe)); +static usbd_status usbd_ar_iface __P((usbd_interface_handle iface)); +static void usbd_transfer_cb __P((usbd_request_handle reqh)); +static void usbd_sync_transfer_cb __P((usbd_request_handle reqh)); +static usbd_status usbd_do_transfer __P((usbd_request_handle reqh)); +static usbd_status usbd_start __P((usbd_pipe_handle pipe)); +void usbd_do_request_async_cb + __P((usbd_request_handle, usbd_private_handle, usbd_status)); + +static SIMPLEQ_HEAD(, usbd_request) usbd_free_requests; + +#if defined(__FreeBSD__) +#define USB_CDEV_MAJOR 79 + +extern struct cdevsw usb_cdevsw; +#endif + +usbd_status +usbd_open_pipe(iface, address, flags, pipe) + usbd_interface_handle iface; + u_int8_t address; + u_int8_t flags; + usbd_pipe_handle *pipe; +{ + usbd_pipe_handle p; + struct usbd_endpoint *ep; + int i, r; + + if (iface->state != USBD_INTERFACE_ACTIVE) + return (USBD_INTERFACE_NOT_ACTIVE); + for (i = 0; i < iface->idesc->bNumEndpoints; i++) { + ep = &iface->endpoints[i]; + if (ep->edesc->bEndpointAddress == address) + goto found; + } + return (USBD_BAD_ADDRESS); + found: + if ((flags & USBD_EXCLUSIVE_USE) && + ep->refcnt != 0) + return (USBD_IN_USE); + r = usbd_setup_pipe(iface->device, iface, ep, &p); + if (r != USBD_NORMAL_COMPLETION) + return (r); + LIST_INSERT_HEAD(&iface->pipes, p, next); + *pipe = p; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_open_pipe_intr(iface, address, flags, pipe, priv, buffer, length, cb) + usbd_interface_handle iface; + u_int8_t address; + u_int8_t flags; + usbd_pipe_handle *pipe; + usbd_private_handle priv; + void *buffer; + u_int32_t length; + usbd_callback cb; +{ + usbd_status r; + usbd_request_handle reqh; + usbd_pipe_handle ipipe; + + reqh = usbd_alloc_request(); + if (reqh == 0) + return (USBD_NOMEM); + r = usbd_open_pipe(iface, address, USBD_EXCLUSIVE_USE, &ipipe); + if (r != USBD_NORMAL_COMPLETION) + goto bad1; + r = usbd_setup_request(reqh, ipipe, priv, buffer, length, + USBD_XFER_IN | flags, USBD_NO_TIMEOUT, cb); + if (r != USBD_NORMAL_COMPLETION) + goto bad2; + ipipe->intrreqh = reqh; + r = usbd_transfer(reqh); + *pipe = ipipe; + if (r != USBD_IN_PROGRESS) + goto bad3; + return (USBD_NORMAL_COMPLETION); + + bad3: + ipipe->intrreqh = 0; + bad2: + usbd_close_pipe(ipipe); + bad1: + usbd_free_request(reqh); + return r; +} + +usbd_status +usbd_open_pipe_iso(iface, address, flags, pipe, priv, bufsize, nbuf, cb) + usbd_interface_handle iface; + u_int8_t address; + u_int8_t flags; + usbd_pipe_handle *pipe; + usbd_private_handle priv; + u_int32_t bufsize; + u_int32_t nbuf; + usbd_callback cb; +{ + usbd_status r; + usbd_pipe_handle p; + + r = usbd_open_pipe(iface, address, USBD_EXCLUSIVE_USE, &p); + if (r != USBD_NORMAL_COMPLETION) + return (r); + if (!p->methods->isobuf) { + usbd_close_pipe(p); + return (USBD_INVAL); + } + r = p->methods->isobuf(p, bufsize, nbuf); + if (r != USBD_NORMAL_COMPLETION) { + usbd_close_pipe(p); + return (r); + } + *pipe = p; + return r; +} + +usbd_status +usbd_close_pipe(pipe) + usbd_pipe_handle pipe; +{ + if (pipe->iface->state != USBD_INTERFACE_ACTIVE) + return (USBD_INTERFACE_NOT_ACTIVE); + if (--pipe->refcnt != 0) + return (USBD_NORMAL_COMPLETION); + if (SIMPLEQ_FIRST(&pipe->queue) != 0) + return (USBD_PENDING_REQUESTS); + LIST_REMOVE(pipe, next); + pipe->endpoint->refcnt--; + pipe->methods->close(pipe); + if (pipe->intrreqh) + usbd_free_request(pipe->intrreqh); + free(pipe, M_USB); + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_transfer(reqh) + usbd_request_handle reqh; +{ + reqh->xfercb = usbd_transfer_cb; + return (usbd_do_transfer(reqh)); +} + +static usbd_status +usbd_do_transfer(reqh) + usbd_request_handle reqh; +{ + usbd_pipe_handle pipe = reqh->pipe; + usbd_interface_handle iface = pipe->iface; + usbd_status r; + int s; + + DPRINTFN(10,("usbd_do_transfer: reqh=%p\n", reqh)); + reqh->done = 0; + s = splusb(); + if (pipe->state == USBD_PIPE_IDLE || + (iface && iface->state == USBD_INTERFACE_IDLE)) { + splx(s); + return (USBD_IS_IDLE); + } + SIMPLEQ_INSERT_TAIL(&pipe->queue, reqh, next); + if (pipe->state == USBD_PIPE_ACTIVE && + (!iface || iface->state == USBD_INTERFACE_ACTIVE)) { + r = usbd_start(pipe); + } else + r = USBD_NOT_STARTED; + splx(s); + return (r); +} + +static usbd_status +usbd_start(pipe) + usbd_pipe_handle pipe; +{ + usbd_request_handle reqh; + + DPRINTFN(5, ("usbd_start: pipe=%p, running=%d\n", + pipe, pipe->running)); + if (pipe->running) + return (USBD_IN_PROGRESS); + reqh = SIMPLEQ_FIRST(&pipe->queue); + if (!reqh) { + /* XXX */ + printf("usbd_start: pipe empty!\n"); + pipe->running = 0; + return (USBD_XXX); + } + SIMPLEQ_REMOVE_HEAD(&pipe->queue, reqh, next); + pipe->running = 1; + pipe->curreqh = reqh; + return (pipe->methods->transfer(reqh)); +} + + +usbd_request_handle +usbd_alloc_request() +{ + usbd_request_handle reqh; + + reqh = SIMPLEQ_FIRST(&usbd_free_requests); + if (reqh) + SIMPLEQ_REMOVE_HEAD(&usbd_free_requests, reqh, next); + else + reqh = malloc(sizeof(*reqh), M_USB, M_NOWAIT); + if (!reqh) + return (0); + memset(reqh, 0, sizeof *reqh); + return (reqh); +} + +usbd_status +usbd_free_request(reqh) + usbd_request_handle reqh; +{ + SIMPLEQ_INSERT_HEAD(&usbd_free_requests, reqh, next); + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_setup_request(reqh, pipe, priv, buffer, length, flags, timeout, callback) + usbd_request_handle reqh; + usbd_pipe_handle pipe; + usbd_private_handle priv; + void *buffer; + u_int32_t length; + u_int16_t flags; + u_int32_t timeout; + void (*callback) __P((usbd_request_handle, + usbd_private_handle, + usbd_status)); +{ + reqh->pipe = pipe; + reqh->isreq = 0; + reqh->priv = priv; + reqh->buffer = buffer; + reqh->length = length; + reqh->actlen = 0; + reqh->flags = flags; + reqh->callback = callback; + reqh->status = USBD_NOT_STARTED; + reqh->retries = 1; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_setup_device_request(reqh, req) + usbd_request_handle reqh; + usb_device_request_t *req; +{ + reqh->isreq = 1; + reqh->request = *req; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_setup_default_request(reqh, dev, priv, timeout, req, buffer, + length, flags, callback) + usbd_request_handle reqh; + usbd_device_handle dev; + usbd_private_handle priv; + u_int32_t timeout; + usb_device_request_t *req; + void *buffer; + u_int32_t length; + u_int16_t flags; + void (*callback) __P((usbd_request_handle, + usbd_private_handle, + usbd_status)); +{ + reqh->pipe = dev->default_pipe; + reqh->priv = priv; + reqh->buffer = buffer; + reqh->length = length; + reqh->actlen = 0; + reqh->flags = flags; + reqh->timeout = timeout; + reqh->status = USBD_NOT_STARTED; + reqh->callback = callback; + reqh->request = *req; + reqh->isreq = 1; + reqh->retries = 1; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_set_request_timeout(reqh, timeout) + usbd_request_handle reqh; + u_int32_t timeout; +{ + reqh->timeout = timeout; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_get_request_status(reqh, priv, buffer, count, status) + usbd_request_handle reqh; + usbd_private_handle *priv; + void **buffer; + u_int32_t *count; + usbd_status *status; +{ + *priv = reqh->priv; + *buffer = reqh->buffer; + *count = reqh->actlen; + *status = reqh->status; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_request_device_data(reqh, req) + usbd_request_handle reqh; + usb_device_request_t *req; +{ + if (!reqh->isreq) + return (USBD_INVAL); + *req = reqh->request; + return (USBD_NORMAL_COMPLETION); +} + +#if 0 +usb_descriptor_t * +usbd_get_descriptor(iface, desc_type) + usbd_interface_handle *iface; + u_int8_t desc_type; +XX +#endif + +usb_config_descriptor_t * +usbd_get_config_descriptor(dev) + usbd_device_handle dev; +{ + return (dev->cdesc); +} + +usb_interface_descriptor_t * +usbd_get_interface_descriptor(iface) + usbd_interface_handle iface; +{ + return (iface->idesc); +} + +usb_device_descriptor_t * +usbd_get_device_descriptor(dev) + usbd_device_handle dev; +{ + return (&dev->ddesc); +} + +usb_endpoint_descriptor_t * +usbd_interface2endpoint_descriptor(iface, index) + usbd_interface_handle iface; + u_int8_t index; +{ + if (index >= iface->idesc->bNumEndpoints) + return 0; + return (iface->endpoints[index].edesc); +} + +usbd_status usbd_set_configuration(dev, conf) + usbd_device_handle dev; + u_int16_t conf; +{ + return usbd_set_config_no(dev, conf, 0); +} + +usbd_status +usbd_retry_request(reqh, retry_count) + usbd_request_handle reqh; + u_int32_t retry_count; +{ + usbd_status r; + + r = usbd_set_pipe_state(reqh->pipe, USBD_PIPE_ACTIVE); + if (r != USBD_NORMAL_COMPLETION) + return (r); + reqh->retries = retry_count; + return (usbd_transfer(reqh)); +} + +usbd_status +usbd_abort_pipe(pipe) + usbd_pipe_handle pipe; +{ + usbd_status r; + int s, st; + + if (pipe->iface->state != USBD_INTERFACE_ACTIVE) + return (USBD_INTERFACE_NOT_ACTIVE); + s = splusb(); + st = pipe->state; + r = usbd_ar_pipe(pipe); + pipe->state = st; + splx(s); + return (r); +} + +usbd_status +usbd_abort_interface(iface) + usbd_interface_handle iface; +{ + usbd_status r; + int s, st; + + s = splusb(); + st = iface->state; + r = usbd_ar_iface(iface); + iface->state = st; + splx(s); + return (r); +} + +usbd_status +usbd_reset_pipe(pipe) + usbd_pipe_handle pipe; +{ + usbd_status r; + int s; + + if (pipe->iface->state != USBD_INTERFACE_ACTIVE) + return (USBD_INTERFACE_NOT_ACTIVE); + s = splusb(); + r = usbd_ar_pipe(pipe); + /* XXX anything else */ + pipe->state = USBD_PIPE_ACTIVE; + splx(s); + return (r); +} + +usbd_status +usbd_reset_interface(iface) + usbd_interface_handle iface; +{ + usbd_status r; + int s; + + s = splusb(); + r = usbd_ar_iface(iface); + /* XXX anything else */ + iface->state = USBD_INTERFACE_ACTIVE; + splx(s); + return (r); +} + +usbd_status +usbd_clear_endpoint_stall(pipe) + usbd_pipe_handle pipe; +{ + usbd_device_handle dev = pipe->device; + usb_device_request_t req; + usbd_status r; + + req.bmRequestType = UT_WRITE_ENDPOINT; + req.bRequest = UR_CLEAR_FEATURE; + USETW(req.wValue, UF_ENDPOINT_STALL); + USETW(req.wIndex, pipe->endpoint->edesc->bEndpointAddress); + USETW(req.wLength, 0); + r = usbd_do_request(dev, &req, 0); +#if 0 +XXX should we do this? + if (r == USBD_NORMAL_COMPLETION) { + pipe->state = USBD_PIPE_ACTIVE; + /* XXX activate pipe */ + } +#endif + return (r); +} + +usbd_status +usbd_clear_endpoint_stall_async(pipe) + usbd_pipe_handle pipe; +{ + usbd_device_handle dev = pipe->device; + usb_device_request_t req; + usbd_status r; + + req.bmRequestType = UT_WRITE_ENDPOINT; + req.bRequest = UR_CLEAR_FEATURE; + USETW(req.wValue, UF_ENDPOINT_STALL); + USETW(req.wIndex, pipe->endpoint->edesc->bEndpointAddress); + USETW(req.wLength, 0); + r = usbd_do_request_async(dev, &req, 0); + return (r); +} + +usbd_status +usbd_set_pipe_state(pipe, state) + usbd_pipe_handle pipe; + usbd_pipe_state state; +{ + int s; + + if (pipe->iface->state != USBD_INTERFACE_ACTIVE) + return (USBD_INTERFACE_NOT_ACTIVE); + if (state != USBD_PIPE_ACTIVE && + state != USBD_PIPE_STALLED && + state != USBD_PIPE_IDLE) + return (USBD_INVAL); + pipe->state = state; + if (state == USBD_PIPE_ACTIVE) { + s = splusb(); + usbd_start(pipe); + splx(s); + } + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_get_pipe_state(pipe, state, endpoint_state, request_count) + usbd_pipe_handle pipe; + usbd_pipe_state *state; + u_int32_t *endpoint_state; + u_int32_t *request_count; +{ + int n; + usbd_request_handle r; + + *state = pipe->state; + *endpoint_state = pipe->endpoint->state; + for (r = SIMPLEQ_FIRST(&pipe->queue), n = 0; + r != 0; + r = SIMPLEQ_NEXT(r, next), n++) + ; + *request_count = n; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_set_interface_state(iface, state) + usbd_interface_handle iface; + usbd_interface_state state; +{ + int ps; + usbd_pipe_handle p; + + if (state == USBD_INTERFACE_ACTIVE) + ps = USBD_PIPE_ACTIVE; + else if (state == USBD_INTERFACE_STALLED) + ps = USBD_PIPE_STALLED; + else if (state == USBD_INTERFACE_IDLE) + ps = USBD_PIPE_IDLE; + else + return (USBD_INVAL); + iface->state = USBD_INTERFACE_ACTIVE; /* to allow setting the pipe */ + for (p = LIST_FIRST(&iface->pipes); p != 0; p = LIST_NEXT(p, next)) + usbd_set_pipe_state(p, ps); + iface->state = state; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_get_interface_state(iface, state) + usbd_interface_handle iface; + usbd_interface_state *state; +{ + *state = iface->state; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_get_device_state(dev, state) + usbd_device_handle dev; + usbd_device_state *state; +{ + *state = dev->state; + return (USBD_NORMAL_COMPLETION); +} + +#if 0 +usbd_status +usbd_set_device_state(dev, state) + usbd_device_handle dev; + usbd_device_state state; +X +#endif + +usbd_status +usbd_device_address(dev, address) + usbd_device_handle dev; + u_int8_t *address; +{ + *address = dev->address; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_endpoint_address(pipe, address) + usbd_pipe_handle pipe; + u_int8_t *address; +{ + *address = pipe->endpoint->edesc->bEndpointAddress; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_endpoint_count(iface, count) + usbd_interface_handle iface; + u_int8_t *count; +{ + *count = iface->idesc->bNumEndpoints; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_interface_count(dev, count) + usbd_device_handle dev; + u_int8_t *count; +{ + if (!dev->cdesc) + return (USBD_NOT_CONFIGURED); + *count = dev->cdesc->bNumInterface; + return (USBD_NORMAL_COMPLETION); +} + +#if defined(__NetBSD__) +/* Do we need these? + * Alternative: let people probe every single device node they can + * find for valid devices instead of counting through n of them. + */ +u_int8_t +usbd_bus_count() +{ + return (usb_bus_count()); +} + +usbd_status +usbd_get_bus_handle(index, bus) + u_int8_t index; + usbd_bus_handle *bus; +{ + return (usb_get_bus_handle(index, bus)); +} +#endif + +usbd_status +usbd_get_root_hub(bus, dev) + usbd_bus_handle bus; + usbd_device_handle *dev; +{ + *dev = bus->root_hub; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_port_count(dev, nports) + usbd_device_handle dev; + u_int8_t *nports; +{ + if (dev->hub == 0) + return (USBD_INVAL); + *nports = dev->hub->hubdesc.bNbrPorts; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_hub2device_handle(dev, port, devp) + usbd_device_handle dev; + u_int8_t port; + usbd_device_handle *devp; +{ + if (dev->hub == 0 || port >= dev->hub->hubdesc.bNbrPorts || + dev->hub->ports[port].device == 0) + return (USBD_INVAL); + *devp = dev->hub->ports[port].device; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_request2pipe_handle(reqh, pipe) + usbd_request_handle reqh; + usbd_pipe_handle *pipe; +{ + *pipe = reqh->pipe; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_pipe2interface_handle(pipe, iface) + usbd_pipe_handle pipe; + usbd_interface_handle *iface; +{ + *iface = pipe->iface; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_interface2device_handle(iface, dev) + usbd_interface_handle iface; + usbd_device_handle *dev; +{ + *dev = iface->device; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_device2bus_handle(dev, bus) + usbd_device_handle dev; + usbd_bus_handle *bus; +{ + *bus = dev->bus; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_device2interface_handle(dev, ifaceno, iface) + usbd_device_handle dev; + u_int8_t ifaceno; + usbd_interface_handle *iface; +{ + if (!dev->cdesc) + return (USBD_NOT_CONFIGURED); + if (ifaceno >= dev->cdesc->bNumInterface) + return (USBD_INVAL); + *iface = &dev->ifaces[ifaceno]; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_set_interface_private_handle(iface, priv) + usbd_interface_handle iface; + usbd_private_handle priv; +{ + iface->priv = priv; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_get_interface_private_handle(iface, priv) + usbd_interface_handle iface; + usbd_private_handle *priv; +{ + *priv = iface->priv; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_reference_pipe(pipe) + usbd_pipe_handle pipe; +{ + pipe->refcnt++; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +usbd_dereference_pipe(pipe) + usbd_pipe_handle pipe; +{ + pipe->refcnt--; + return (USBD_NORMAL_COMPLETION); +} + +usbd_lock_token +usbd_lock() +{ + return (splusb()); +} + +void +usbd_unlock(tok) + usbd_lock_token tok; +{ + splx(tok); +} + +/* XXX need to check that the interface is idle */ +usbd_status +usbd_set_interface(iface, aiface) + usbd_interface_handle iface; + int aiface; +{ + usb_device_request_t req; + + req.bmRequestType = UT_WRITE_INTERFACE; + req.bRequest = UR_SET_INTERFACE; + USETW(req.wValue, aiface); + USETW(req.wIndex, iface->idesc->iInterface); + USETW(req.wLength, 0); + return usbd_do_request(iface->device, &req, 0); +} + +/*** Internal routines ***/ + +/* Dequeue all pipe operations, called at splusb(). */ +static usbd_status +usbd_ar_pipe(pipe) + usbd_pipe_handle pipe; +{ + usbd_request_handle reqh; + + if (pipe->curreqh != 0) + pipe->methods->abort(pipe->curreqh); + + for (;;) { + reqh = SIMPLEQ_FIRST(&pipe->queue); + if (reqh == 0) + break; + SIMPLEQ_REMOVE_HEAD(&pipe->queue, reqh, next); + reqh->status = USBD_CANCELLED; + if (reqh->callback) + reqh->callback(reqh, reqh->priv, reqh->status); + } + return (USBD_NORMAL_COMPLETION); +} + +/* Dequeue all interface operations, called at splusb(). */ +static usbd_status +usbd_ar_iface(iface) + usbd_interface_handle iface; +{ + usbd_pipe_handle p; + usbd_status r, ret = USBD_NORMAL_COMPLETION; + + for (p = LIST_FIRST(&iface->pipes); p != 0; p = LIST_NEXT(p, next)) { + r = usbd_ar_pipe(p); + if (r != USBD_NORMAL_COMPLETION) + ret = r; + } + return (ret); +} + +static int usbd_global_init_done = 0; + +void +usbd_init() +{ +#if defined(__FreeBSD__) + dev_t dev; +#endif + + if (!usbd_global_init_done) { + usbd_global_init_done = 1; + SIMPLEQ_INIT(&usbd_free_requests); + +#if defined(__FreeBSD__) + dev = makedev(USB_CDEV_MAJOR, 0); + cdevsw_add(&dev, &usb_cdevsw, NULL); +#endif + } +} + +static void +usbd_transfer_cb(reqh) + usbd_request_handle reqh; +{ + usbd_pipe_handle pipe = reqh->pipe; + usbd_request_handle nreqh; + usbd_status r; + + /* Count completed transfers. */ + ++pipe->device->bus->stats.requests + [pipe->endpoint->edesc->bmAttributes & UE_XFERTYPE]; + + /* XXX check retry count */ + reqh->done = 1; + if (reqh->status == USBD_NORMAL_COMPLETION && + reqh->actlen < reqh->length && + !(reqh->flags & USBD_SHORT_XFER_OK)) { + DPRINTFN(-1, ("usbd_transfer_cb: short xfer %d < %d\n", + reqh->actlen, reqh->length)); + reqh->status = USBD_SHORT_XFER; + } + pipe->curreqh = 0; + if (reqh->callback) + reqh->callback(reqh, reqh->priv, reqh->status); + + if (pipe->state != USBD_PIPE_ACTIVE) { + pipe->running = 0; + return; + } + nreqh = SIMPLEQ_FIRST(&pipe->queue); + DPRINTFN(5, ("usbd_transfer_cb: nreqh=%p\n", nreqh)); + if (!nreqh) + pipe->running = 0; + else { + SIMPLEQ_REMOVE_HEAD(&pipe->queue, nreqh, next); + pipe->curreqh = reqh; + r = pipe->methods->transfer(nreqh); + if (r != USBD_IN_PROGRESS) + printf("usbd_transfer_cb: error=%d\n", r); + } +} + +static void +usbd_sync_transfer_cb(reqh) + usbd_request_handle reqh; +{ + usbd_transfer_cb(reqh); + if (!reqh->pipe->device->bus->use_polling) + wakeup(reqh); +} + +/* Like usbd_transfer(), but waits for completion. */ +usbd_status +usbd_sync_transfer(reqh) + usbd_request_handle reqh; +{ + usbd_status r; + int s; + + reqh->xfercb = usbd_sync_transfer_cb; + r = usbd_do_transfer(reqh); + if (r != USBD_IN_PROGRESS) + return (r); + s = splusb(); + if (!reqh->done) { + if (reqh->pipe->device->bus->use_polling) + panic("usbd_sync_transfer: not done\n"); + tsleep(reqh, PRIBIO, "usbsyn", 0); + } + splx(s); + return (reqh->status); +} + +usbd_status +usbd_do_request(dev, req, data) + usbd_device_handle dev; + usb_device_request_t *req; + void *data; +{ + usbd_request_handle reqh; + usbd_status r; + +#ifdef DIAGNOSTIC + if (!curproc) { + printf("usbd_do_request: not in process context\n"); + return (USBD_XXX); + } +#endif + + reqh = usbd_alloc_request(); + if (reqh == 0) + return (USBD_NOMEM); + r = usbd_setup_default_request( + reqh, dev, 0, USBD_DEFAULT_TIMEOUT, req, data, + UGETW(req->wLength), 0, 0); + if (r != USBD_NORMAL_COMPLETION) { + usbd_free_request(reqh); + return (r); + } + r = usbd_sync_transfer(reqh); +#if defined(USB_DEBUG) || defined(DIAGNOSTIC) + if (reqh->actlen > reqh->length) + printf("usbd_do_request: overrun addr=%d type=0x%02x req=0x%02x val=%d index=%d rlen=%d length=%d actlen=%d\n", + dev->address, reqh->request.bmRequestType, + reqh->request.bRequest, UGETW(reqh->request.wValue), + UGETW(reqh->request.wIndex), + UGETW(reqh->request.wLength), + reqh->length, reqh->actlen); +#endif + usbd_free_request(reqh); + return (r); +} + +void +usbd_do_request_async_cb(reqh, priv, status) + usbd_request_handle reqh; + usbd_private_handle priv; + usbd_status status; +{ +#if defined(USB_DEBUG) || defined(DIAGNOSTIC) + if (reqh->actlen > reqh->length) + printf("usbd_do_request: overrun addr=%d type=0x%02x req=0x%02x val=%d index=%d rlen=%d length=%d actlen=%d\n", + reqh->pipe->device->address, + reqh->request.bmRequestType, + reqh->request.bRequest, UGETW(reqh->request.wValue), + UGETW(reqh->request.wIndex), + UGETW(reqh->request.wLength), + reqh->length, reqh->actlen); +#endif + usbd_free_request(reqh); +} + +/* + * Execute a request without waiting for completion. + * Can be used from interrupt context. + */ +usbd_status +usbd_do_request_async(dev, req, data) + usbd_device_handle dev; + usb_device_request_t *req; + void *data; +{ + usbd_request_handle reqh; + usbd_status r; + + reqh = usbd_alloc_request(); + if (reqh == 0) + return (USBD_NOMEM); + r = usbd_setup_default_request( + reqh, dev, 0, USBD_DEFAULT_TIMEOUT, req, data, + UGETW(req->wLength), 0, usbd_do_request_async_cb); + if (r != USBD_NORMAL_COMPLETION) { + usbd_free_request(reqh); + return (r); + } + r = usbd_transfer(reqh); + if (r != USBD_IN_PROGRESS) + return (r); + return (USBD_NORMAL_COMPLETION); +} + +struct usbd_quirks * +usbd_get_quirks(dev) + usbd_device_handle dev; +{ + return (dev->quirks); +} + +void +usbd_set_disco(p, hdl, data) + usbd_pipe_handle p; + void (*hdl) __P((void *)); + void *data; +{ + p->disco = hdl; + p->discoarg = data; +} + +/* XXX do periodic free() of free list */ + +/* + * Called from keyboard driver when in polling mode. + */ +void +usbd_dopoll(iface) + usbd_interface_handle iface; +{ + iface->device->bus->do_poll(iface->device->bus); +} + +void +usbd_set_polling(iface, on) + usbd_interface_handle iface; + int on; +{ + iface->device->bus->use_polling = on; +} diff --git a/sys/dev/usb/usbdi.h b/sys/dev/usb/usbdi.h new file mode 100644 index 0000000..491376a --- /dev/null +++ b/sys/dev/usb/usbdi.h @@ -0,0 +1,303 @@ +/* $NetBSD: usbdi.h,v 1.6 1998/08/02 22:30:53 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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. + */ + +typedef struct usbd_bus *usbd_bus_handle; +typedef struct usbd_device *usbd_device_handle; +typedef struct usbd_interface *usbd_interface_handle; +typedef struct usbd_pipe *usbd_pipe_handle; +typedef struct usbd_request *usbd_request_handle; +typedef void *usbd_private_handle; + +typedef enum { + USBD_ENDPOINT_ACTIVE, + USBD_ENDPOINT_STALLED, +} usbd_endpoint_state; + +typedef enum { + USBD_PIPE_ACTIVE, + USBD_PIPE_STALLED, + USBD_PIPE_IDLE, +} usbd_pipe_state; + +typedef enum { + USBD_INTERFACE_ACTIVE, + USBD_INTERFACE_STALLED, + USBD_INTERFACE_IDLE, +} usbd_interface_state; + +typedef enum { + USBD_DEVICE_ATTACHED, + USBD_DEVICE_POWERED, + USBD_DEVICE_DEFAULT, + USBD_DEVICE_ADDRESSED, + USBD_DEVICE_CONFIGURED, + USBD_DEVICE_SUSPENDED, +} usbd_device_state; + +typedef enum { + USBD_NORMAL_COMPLETION = 0, + USBD_IN_PROGRESS, + /* errors */ + USBD_PENDING_REQUESTS, + USBD_NOT_STARTED, + USBD_INVAL, + USBD_IS_IDLE, + USBD_NOMEM, + USBD_CANCELLED, + USBD_BAD_ADDRESS, + USBD_IN_USE, + USBD_INTERFACE_NOT_ACTIVE, + 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_XXX, +} usbd_status; + +typedef int usbd_lock_token; + +typedef void (*usbd_callback) __P((usbd_request_handle, usbd_private_handle, + usbd_status)); + +/* Open flags */ +#define USBD_EXCLUSIVE_USE 0x01 + +/* Request flags */ +#define USBD_XFER_OUT 0x01 +#define USBD_XFER_IN 0x02 +#define USBD_SHORT_XFER_OK 0x04 + +#define USBD_NO_TIMEOUT 0 +#define USBD_DEFAULT_TIMEOUT 5000 /* ms = 5 s */ + +usbd_status usbd_open_pipe + __P((usbd_interface_handle iface, u_int8_t address, + u_int8_t flags, usbd_pipe_handle *pipe)); +usbd_status usbd_close_pipe __P((usbd_pipe_handle pipe)); +usbd_status usbd_transfer __P((usbd_request_handle req)); +usbd_request_handle usbd_alloc_request __P((void)); +usbd_status usbd_free_request __P((usbd_request_handle reqh)); +usbd_status usbd_setup_request + __P((usbd_request_handle reqh, usbd_pipe_handle pipe, + usbd_private_handle priv, void *buffer, + u_int32_t length, u_int16_t flags, u_int32_t timeout, + usbd_callback)); +usbd_status usbd_setup_device_request + __P((usbd_request_handle reqh, usb_device_request_t *req)); +usbd_status usbd_setup_default_request + __P((usbd_request_handle reqh, usbd_device_handle dev, + usbd_private_handle priv, u_int32_t timeout, + usb_device_request_t *req, void *buffer, + u_int32_t length, u_int16_t flags, usbd_callback)); +usbd_status usbd_set_request_timeout + __P((usbd_request_handle reqh, u_int32_t timeout)); +usbd_status usbd_get_request_status + __P((usbd_request_handle reqh, usbd_private_handle *priv, + void **buffer, u_int32_t *count, usbd_status *status)); +usbd_status usbd_request_device_data + __P((usbd_request_handle reqh, usb_device_request_t *req)); +usb_descriptor_t *usbd_get_descriptor + __P((usbd_interface_handle *iface, u_int8_t desc_type)); +usb_endpoint_descriptor_t *usbd_interface2endpoint_descriptor + __P((usbd_interface_handle iface, u_int8_t address)); +usbd_status usbd_set_configuration + __P((usbd_device_handle dev, u_int16_t conf)); +usbd_status usbd_retry_request + __P((usbd_request_handle reqh, u_int32_t retry_count)); +usbd_status usbd_abort_pipe __P((usbd_pipe_handle pipe)); +usbd_status usbd_abort_interface __P((usbd_interface_handle iface)); +usbd_status usbd_reset_pipe __P((usbd_pipe_handle pipe)); +usbd_status usbd_reset_interface __P((usbd_interface_handle iface)); +usbd_status usbd_clear_endpoint_stall __P((usbd_pipe_handle pipe)); +usbd_status usbd_clear_endpoint_stall_async __P((usbd_pipe_handle pipe)); +usbd_status usbd_set_pipe_state + __P((usbd_pipe_handle pipe, usbd_pipe_state state)); +usbd_status usbd_get_pipe_state + __P((usbd_pipe_handle pipe, usbd_pipe_state *state, + u_int32_t *endpoint_state, u_int32_t *request_count)); +usbd_status usbd_set_interface_state + __P((usbd_interface_handle iface, usbd_interface_state state)); +usbd_status usbd_get_interface_state + __P((usbd_interface_handle iface, usbd_interface_state *state)); +usbd_status usbd_get_device_state + __P((usbd_device_handle dev, usbd_device_state *state)); +usbd_status usbd_set_device_state + __P((usbd_device_handle dev, usbd_device_state state)); +usbd_status usbd_device_address + __P((usbd_device_handle dev, u_int8_t *address)); +usbd_status usbd_endpoint_address + __P((usbd_pipe_handle dev, u_int8_t *address)); +usbd_status usbd_endpoint_count + __P((usbd_interface_handle dev, u_int8_t *count)); +usbd_status usbd_interface_count + __P((usbd_device_handle dev, u_int8_t *count)); +u_int8_t usbd_bus_count __P((void)); +usbd_status usbd_get_bus_handle __P((u_int8_t index, usbd_bus_handle *bus)); +usbd_status usbd_get_root_hub + __P((usbd_bus_handle bus, usbd_device_handle *dev)); +usbd_status usbd_port_count __P((usbd_device_handle hub, u_int8_t *nports)); +usbd_status usbd_hub2device_handle + __P((usbd_device_handle hub, u_int8_t port, usbd_device_handle *dev)); +usbd_status usbd_request2pipe_handle + __P((usbd_request_handle reqh, usbd_pipe_handle *pipe)); +usbd_status usbd_pipe2interface_handle + __P((usbd_pipe_handle pipe, usbd_interface_handle *iface)); +usbd_status usbd_interface2device_handle + __P((usbd_interface_handle iface, usbd_device_handle *dev)); +usbd_status usbd_device2bus_handle + __P((usbd_device_handle dev, usbd_bus_handle *bus)); +usbd_status usbd_device2interface_handle + __P((usbd_device_handle dev, u_int8_t ifaceno, + usbd_interface_handle *iface)); +usbd_status usbd_set_interface_private_handle + __P((usbd_interface_handle iface, usbd_private_handle priv)); +usbd_status usbd_get_interface_private_handle + __P((usbd_interface_handle iface, usbd_private_handle *priv)); +usbd_status usbd_reference_pipe __P((usbd_pipe_handle pipe)); +usbd_status usbd_dereference_pipe __P((usbd_pipe_handle pipe)); +usbd_lock_token usbd_lock __P((void)); +void usbd_unlock __P((usbd_lock_token tok)); + +/* Non-standard */ +usbd_status usbd_sync_transfer __P((usbd_request_handle req)); +usbd_status usbd_open_pipe_intr + __P((usbd_interface_handle iface, u_int8_t address, + u_int8_t flags, usbd_pipe_handle *pipe, + usbd_private_handle priv, void *buffer, + u_int32_t length, usbd_callback)); +usbd_status usbd_open_pipe_iso + __P((usbd_interface_handle iface, u_int8_t address, + u_int8_t flags, usbd_pipe_handle *pipe, + usbd_private_handle priv, u_int32_t bufsize, u_int32_t nbuf, + usbd_callback)); +usbd_status usbd_do_request + __P((usbd_device_handle pipe, usb_device_request_t *req, void *data)); +usbd_status usbd_do_request_async + __P((usbd_device_handle pipe, usb_device_request_t *req, void *data)); +usb_interface_descriptor_t *usbd_get_interface_descriptor + __P((usbd_interface_handle iface)); +usb_config_descriptor_t *usbd_get_config_descriptor + __P((usbd_device_handle dev)); +usb_device_descriptor_t *usbd_get_device_descriptor + __P((usbd_device_handle dev)); +usbd_status usbd_set_interface __P((usbd_interface_handle, int)); + +void usbd_dopoll __P((usbd_interface_handle)); +void usbd_set_polling __P((usbd_interface_handle iface, int on)); + +/* NetBSD attachment information */ + +/* Attach data */ +struct usb_attach_arg { + struct usbd_device *device; + struct usbd_interface *iface; + int usegeneric; +}; + +#if defined(__NetBSD__) +/* Match codes. */ +/* First five codes is for a whole device. */ +#define UMATCH_VENDOR_PRODUCT_REV 14 +#define UMATCH_VENDOR_PRODUCT 13 +#define UMATCH_VENDOR_DEVCLASS_DEVPROTO 12 +#define UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO 11 +#define UMATCH_DEVCLASS_DEVSUBCLASS 10 +/* Next six codes are for interfaces. */ +#define UMATCH_VENDOR_PRODUCT_REV_CONF_IFACE 9 +#define UMATCH_VENDOR_PRODUCT_CONF_IFACE 8 +#define UMATCH_VENDOR_IFACESUBCLASS_IFACEPROTO 7 +#define UMATCH_VENDOR_IFACESUBCLASS 6 +#define UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO 5 +#define UMATCH_IFACECLASS_IFACESUBCLASS 4 +#define UMATCH_IFACECLASS 3 +#define UMATCH_IFACECLASS_GENERIC 2 +/* Generic driver */ +#define UMATCH_GENERIC 1 +/* No match */ +#define UMATCH_NONE 0 + +#elif defined(__FreeBSD__) +/* FreeBSD needs values less than zero */ +/* for the moment disabled +#define UMATCH_VENDOR_PRODUCT_REV -14 +#define UMATCH_VENDOR_PRODUCT -13 +#define UMATCH_VENDOR_DEVCLASS_DEVPROTO -12 +#define UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO -11 +#define UMATCH_DEVCLASS_DEVSUBCLASS -10 +#define UMATCH_VENDOR_PRODUCT_REV_CONF_IFACE -9 +#define UMATCH_VENDOR_PRODUCT_CONF_IFACE -8 +#define UMATCH_VENDOR_IFACESUBCLASS_IFACEPROTO -7 +#define UMATCH_VENDOR_IFACESUBCLASS -6 +#define UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO -5 +#define UMATCH_IFACECLASS_IFACESUBCLASS -4 +#define UMATCH_IFACECLASS -3 +#define UMATCH_IFACECLASS_GENERIC -2 +#define UMATCH_GENERIC -1 +#define UMATCH_NONE ENXIO + +* For the moment we use Yes/No answers with appropriate +* sorting in the config file +*/ +#define UMATCH_VENDOR_PRODUCT_REV 0 +#define UMATCH_VENDOR_PRODUCT 0 +#define UMATCH_VENDOR_DEVCLASS_DEVPROTO 0 +#define UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO 0 +#define UMATCH_DEVCLASS_DEVSUBCLASS 0 +#define UMATCH_VENDOR_PRODUCT_REV_CONF_IFACE 0 +#define UMATCH_VENDOR_PRODUCT_CONF_IFACE 0 +#define UMATCH_VENDOR_IFACESUBCLASS_IFACEPROTO 0 +#define UMATCH_VENDOR_IFACESUBCLASS 0 +#define UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO 0 +#define UMATCH_IFACECLASS_IFACESUBCLASS 0 +#define UMATCH_IFACECLASS 0 +#define UMATCH_IFACECLASS_GENERIC 0 +#define UMATCH_GENERIC 0 +#define UMATCH_NONE ENXIO + + +#endif + +void usbd_devinfo __P((usbd_device_handle, int, char *)); +struct usbd_quirks *usbd_get_quirks __P((usbd_device_handle)); +void usbd_set_disco __P((usbd_pipe_handle, void (*)(void *), void *)); diff --git a/sys/dev/usb/usbdi_util.c b/sys/dev/usb/usbdi_util.c new file mode 100644 index 0000000..a439655 --- /dev/null +++ b/sys/dev/usb/usbdi_util.c @@ -0,0 +1,416 @@ +/* $NetBSD: usbdi_util.c,v 1.4 1998/08/02 22:30:53 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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 <dev/usb/usb_port.h> + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#if defined(__NetBSD__) +#include <sys/device.h> +#endif +#include <sys/proc.h> +#include <sys/select.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbhid.h> + +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> + +#ifdef USB_DEBUG +#define DPRINTF(x) if (usbdebug) printf x +#define DPRINTFN(n,x) if (usbdebug>(n)) printf x +extern int usbdebug; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +usbd_status +usbd_get_desc(dev, type, index, len, desc) + usbd_device_handle dev; + int type, index; + int len; + void *desc; +{ + usb_device_request_t req; + + req.bmRequestType = UT_READ_DEVICE; + req.bRequest = UR_GET_DESCRIPTOR; + USETW2(req.wValue, type, index); + USETW(req.wIndex, 0); + USETW(req.wLength, len); + return (usbd_do_request(dev, &req, desc)); +} + +usbd_status +usbd_get_config_desc(dev, conf, d) + usbd_device_handle dev; + int conf; + usb_config_descriptor_t *d; +{ + DPRINTFN(3,("usbd_get_config_desc: conf=%d\n", conf)); + return (usbd_get_desc(dev, UDESC_CONFIG, + conf, USB_CONFIG_DESCRIPTOR_SIZE, d)); +} + +usbd_status +usbd_get_config_desc_full(dev, conf, d, size) + usbd_device_handle dev; + int conf; + void *d; + int size; +{ + DPRINTFN(3,("usbd_get_config_desc_full: conf=%d\n", conf)); + return (usbd_get_desc(dev, UDESC_CONFIG, conf, size, d)); +} + +usbd_status +usbd_get_device_desc(dev, d) + usbd_device_handle dev; + usb_device_descriptor_t *d; +{ + DPRINTFN(3,("usbd_get_device_desc:\n")); + return (usbd_get_desc(dev, UDESC_DEVICE, + 0, USB_DEVICE_DESCRIPTOR_SIZE, d)); +} + +usbd_status +usbd_get_device_status(dev, st) + usbd_device_handle dev; + usb_status_t *st; +{ + usb_device_request_t req; + + req.bmRequestType = UT_READ_DEVICE; + req.bRequest = UR_GET_STATUS; + USETW(req.wValue, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, sizeof(usb_status_t)); + return (usbd_do_request(dev, &req, st)); +} + +usbd_status +usbd_get_hub_status(dev, st) + usbd_device_handle dev; + usb_hub_status_t *st; +{ + usb_device_request_t req; + + req.bmRequestType = UT_READ_CLASS_DEVICE; + req.bRequest = UR_GET_STATUS; + USETW(req.wValue, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, sizeof(usb_hub_status_t)); + return (usbd_do_request(dev, &req, st)); +} + +usbd_status +usbd_set_address(dev, addr) + usbd_device_handle dev; + int addr; +{ + usb_device_request_t req; + + req.bmRequestType = UT_WRITE_DEVICE; + req.bRequest = UR_SET_ADDRESS; + USETW(req.wValue, addr); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + return usbd_do_request(dev, &req, 0); +} + +usbd_status +usbd_get_port_status(dev, port, ps) + usbd_device_handle dev; + int port; + usb_port_status_t *ps; +{ + usb_device_request_t req; + + req.bmRequestType = UT_READ_CLASS_OTHER; + req.bRequest = UR_GET_STATUS; + USETW(req.wValue, 0); + USETW(req.wIndex, port); + USETW(req.wLength, sizeof *ps); + return (usbd_do_request(dev, &req, ps)); +} + +usbd_status +usbd_clear_port_feature(dev, port, sel) + usbd_device_handle dev; + int port, sel; +{ + usb_device_request_t req; + + req.bmRequestType = UT_WRITE_CLASS_OTHER; + req.bRequest = UR_CLEAR_FEATURE; + USETW(req.wValue, sel); + USETW(req.wIndex, port); + USETW(req.wLength, 0); + return (usbd_do_request(dev, &req, 0)); +} + +usbd_status +usbd_set_port_feature(dev, port, sel) + usbd_device_handle dev; + int port, sel; +{ + usb_device_request_t req; + + req.bmRequestType = UT_WRITE_CLASS_OTHER; + req.bRequest = UR_SET_FEATURE; + USETW(req.wValue, sel); + USETW(req.wIndex, port); + USETW(req.wLength, 0); + return (usbd_do_request(dev, &req, 0)); +} + + +usbd_status +usbd_set_protocol(iface, report) + 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 r; + + DPRINTFN(4, ("usbd_set_protocol: iface=%p, report=%d, endpt=%d\n", + iface, report, id->bInterfaceNumber)); + r = usbd_interface2device_handle(iface, &dev); + if (r != USBD_NORMAL_COMPLETION) + return (r); + if (!id) + return (USBD_INVAL); + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UR_SET_PROTOCOL; + USETW(req.wValue, report); + USETW(req.wIndex, id->bInterfaceNumber); + USETW(req.wLength, 0); + return (usbd_do_request(dev, &req, 0)); +} + +usbd_status +usbd_set_report(iface, type, id, data, len) + 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 r; + + DPRINTFN(4, ("usbd_set_report: len=%d\n", len)); + r = usbd_interface2device_handle(iface, &dev); + if (r != USBD_NORMAL_COMPLETION) + return (r); + if (!ifd) + return (USBD_INVAL); + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UR_SET_REPORT; + USETW2(req.wValue, type, id); + USETW(req.wIndex, ifd->bInterfaceNumber); + USETW(req.wLength, len); + return (usbd_do_request(dev, &req, data)); +} + +usbd_status +usbd_set_report_async(iface, type, id, data, len) + 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 r; + + DPRINTFN(4, ("usbd_set_report_async: len=%d\n", len)); + r = usbd_interface2device_handle(iface, &dev); + if (r != USBD_NORMAL_COMPLETION) + return (r); + if (!ifd) + return (USBD_INVAL); + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UR_SET_REPORT; + USETW2(req.wValue, type, id); + USETW(req.wIndex, ifd->bInterfaceNumber); + USETW(req.wLength, len); + return (usbd_do_request_async(dev, &req, data)); +} + +usbd_status +usbd_get_report(iface, type, id, data, len) + 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 r; + + DPRINTFN(4, ("usbd_set_report: len=%d\n", len)); + r = usbd_interface2device_handle(iface, &dev); + if (r != USBD_NORMAL_COMPLETION) + return (r); + if (!ifd) + return (USBD_INVAL); + req.bmRequestType = UT_READ_CLASS_INTERFACE; + req.bRequest = UR_GET_REPORT; + USETW2(req.wValue, type, id); + USETW(req.wIndex, ifd->bInterfaceNumber); + USETW(req.wLength, len); + return (usbd_do_request(dev, &req, data)); +} + +usbd_status +usbd_set_idle(iface, duration, id) + 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 r; + + DPRINTFN(4, ("usbd_set_idle: %d %d\n", duration, id)); + r = usbd_interface2device_handle(iface, &dev); + if (r != USBD_NORMAL_COMPLETION) + return (r); + if (!ifd) + return (USBD_INVAL); + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UR_SET_IDLE; + USETW2(req.wValue, duration, id); + USETW(req.wIndex, ifd->bInterfaceNumber); + USETW(req.wLength, 0); + return (usbd_do_request(dev, &req, 0)); +} + +usbd_status +usbd_get_report_descriptor(dev, i, size, d) + usbd_device_handle dev; + int i; + int size; + void *d; +{ + usb_device_request_t req; + + req.bmRequestType = UT_READ_INTERFACE; + req.bRequest = UR_GET_DESCRIPTOR; + USETW2(req.wValue, UDESC_REPORT, 0); + USETW(req.wIndex, i); + USETW(req.wLength, size); + return (usbd_do_request(dev, &req, d)); +} + +usb_hid_descriptor_t * +usbd_get_hid_descriptor(ifc) + usbd_interface_handle ifc; +{ + usb_interface_descriptor_t *idesc = usbd_get_interface_descriptor(ifc); + usbd_device_handle dev; + usb_config_descriptor_t *cdesc; + usb_hid_descriptor_t *hd; + char *p, *end; + usbd_status r; + + r = usbd_interface2device_handle(ifc, &dev); + if (r != USBD_NORMAL_COMPLETION) + return (0); + cdesc = usbd_get_config_descriptor(dev); + + p = (char *)idesc + idesc->bLength; + end = (char *)cdesc + UGETW(cdesc->wTotalLength); + + for (; p < end; p += hd->bLength) { + hd = (usb_hid_descriptor_t *)p; + if (p + hd->bLength <= end && hd->bDescriptorType == UDESC_HID) + return (hd); + if (hd->bDescriptorType == UDESC_INTERFACE) + break; + } + return (0); +} + +usbd_status +usbd_alloc_report_desc(ifc, descp, sizep, mem) + usbd_interface_handle ifc; + void **descp; + int *sizep; +#if defined(__NetBSD__) + int mem; +#elif defined(__FreeBSD__) + struct malloc_type *mem; +#endif + +{ + usb_hid_descriptor_t *hid; + usbd_device_handle dev; + usbd_status r; + + r = usbd_interface2device_handle(ifc, &dev); + if (r != USBD_NORMAL_COMPLETION) + return (r); + hid = usbd_get_hid_descriptor(ifc); + if (!hid) + return (USBD_IOERROR); + *sizep = UGETW(hid->descrs[0].wDescriptorLength); + *descp = malloc(*sizep, mem, M_NOWAIT); + if (!*descp) + return (USBD_NOMEM); + r = usbd_get_report_descriptor(dev, 0, *sizep, *descp); + if (r != USBD_NORMAL_COMPLETION) { + free(*descp, mem); + return (r); + } + return (USBD_NORMAL_COMPLETION); +} diff --git a/sys/dev/usb/usbdi_util.h b/sys/dev/usb/usbdi_util.h new file mode 100644 index 0000000..2c21f50 --- /dev/null +++ b/sys/dev/usb/usbdi_util.h @@ -0,0 +1,74 @@ +/* $NetBSD: usbdi_util.h,v 1.4 1998/08/02 22:30:53 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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. + */ + +usbd_status usbd_get_desc __P((usbd_device_handle dev, int type, + int index, int len, void *desc)); +usbd_status usbd_get_config_desc __P((usbd_device_handle, int, + usb_config_descriptor_t *)); +usbd_status usbd_get_config_desc_full __P((usbd_device_handle, int, + void *, int)); +usbd_status usbd_get_device_desc __P((usbd_device_handle dev, + usb_device_descriptor_t *d)); +usbd_status usbd_set_address __P((usbd_device_handle dev, int addr)); +usbd_status usbd_get_port_status __P((usbd_device_handle, + int, usb_port_status_t *)); +usbd_status usbd_set_port_feature __P((usbd_device_handle dev, int, int)); +usbd_status usbd_clear_port_feature __P((usbd_device_handle, int, int)); +usbd_status usbd_get_device_status __P((usbd_device_handle,usb_status_t*)); +usbd_status usbd_get_hub_status __P((usbd_device_handle dev, + usb_hub_status_t *st)); +usbd_status usbd_set_protocol __P((usbd_interface_handle dev, int report)); +usbd_status usbd_get_report_descriptor + __P((usbd_device_handle dev, int i, int size, void *d)); +struct usb_hid_descriptor *usbd_get_hid_descriptor + __P((usbd_interface_handle ifc)); +usbd_status usbd_set_report + __P((usbd_interface_handle iface,int type,int id,void *data,int len)); +usbd_status usbd_set_report_async + __P((usbd_interface_handle iface,int type,int id,void *data,int len)); +usbd_status usbd_get_report + __P((usbd_interface_handle iface,int type,int id,void *data,int len)); +usbd_status usbd_set_idle + __P((usbd_interface_handle iface, int duration, int id)); +#if defined(__NetBSD__) +usbd_status usbd_alloc_report_desc + __P((usbd_interface_handle ifc, void **descp, int *sizep, int mem)); +#elif defined(__FreeBSD__) +usbd_status usbd_alloc_report_desc + __P((usbd_interface_handle ifc, void **descp, int *sizep, struct malloc_type * mem)); +#endif diff --git a/sys/dev/usb/usbdivar.h b/sys/dev/usb/usbdivar.h new file mode 100644 index 0000000..40e7947 --- /dev/null +++ b/sys/dev/usb/usbdivar.h @@ -0,0 +1,231 @@ +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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(__FreeBSD__) +/* conversiom from one type of queue to the other */ +#define SIMPLEQ_REMOVE_HEAD STAILQ_REMOVE_HEAD_QUEUE +#define SIMPLEQ_INSERT_HEAD STAILQ_INSERT_HEAD +#define SIMPLEQ_INSERT_TAIL STAILQ_INSERT_TAIL +#define SIMPLEQ_NEXT STAILQ_NEXT +#define SIMPLEQ_FIRST STAILQ_FIRST +#define SIMPLEQ_HEAD STAILQ_HEAD +#define SIMPLEQ_INIT STAILQ_INIT +#define SIMPLEQ_ENTRY STAILQ_ENTRY +#endif + +struct usbd_request; +struct usbd_pipe; + +struct usbd_endpoint { + usb_endpoint_descriptor_t *edesc; + usbd_endpoint_state state; + int refcnt; + int toggle; /* XXX */ +}; + +typedef void (*usbd_xfercb)__P((usbd_request_handle req)); + +struct usbd_methods { + usbd_status (*transfer)__P((usbd_request_handle reqh)); + void (*abort)__P((usbd_request_handle reqh)); + void (*close)__P((usbd_pipe_handle pipe)); + usbd_status (*isobuf)__P((usbd_pipe_handle pipe, + u_int32_t bufsize,u_int32_t nbuf)); +}; + +struct usbd_port { + usb_port_status_t status; + int power; /* mA of current on port */ + struct usbd_device *device; + struct usbd_device *parent; /* The ports hub */ +}; + +struct usbd_hub { + usbd_status (*explore)__P((usbd_device_handle hub)); + void *hubdata; + usb_hub_descriptor_t hubdesc; + int nports; + struct usbd_port ports[1]; +}; + +struct usb_softc; + +/*****/ + +struct usbd_bus { + /* Filled by HC driver */ + bdevice bdev; /* base device, host adapter */ + usbd_status (*open_pipe)__P((struct usbd_pipe *pipe)); + u_int32_t pipe_size; /* size of a pipe struct */ + void (*do_poll)__P((struct usbd_bus *)); + /* Filled by usb driver */ + struct usbd_device *root_hub; + usbd_device_handle devices[USB_MAX_DEVICES]; + char needs_explore;/* a hub a signalled a change */ + char use_polling; + struct usb_softc *usbctl; + struct usb_device_stats stats; +}; + +struct usbd_device { + struct usbd_bus *bus; + usbd_device_state state; + struct usbd_pipe *default_pipe; + u_int8_t address; + u_int8_t depth; + u_int8_t lowspeed; + u_int16_t power; + u_int8_t self_powered; + int config; + struct usbd_port *powersrc; + struct usbd_endpoint def_ep; /* for pipe 0 */ + usb_endpoint_descriptor_t def_ep_desc; /* for pipe 0 */ + struct usbd_interface *ifaces; + usb_device_descriptor_t ddesc; + usb_config_descriptor_t *cdesc; /* full config descr */ + struct usbd_quirks *quirks; + struct usbd_hub *hub; /* only if this is a hub */ +#if defined(__FreeBSD__) + bdevice bdev; /* base device */ +#endif +}; + +struct usbd_interface { + struct usbd_device *device; + usbd_interface_state state; + usb_interface_descriptor_t *idesc; + struct usbd_endpoint *endpoints; + void *priv; + LIST_HEAD(, usbd_pipe) pipes; +}; + +struct usbd_pipe { + struct usbd_interface *iface; + struct usbd_device *device; + struct usbd_endpoint *endpoint; + usbd_pipe_state state; + int32_t refcnt; + char running; + SIMPLEQ_HEAD(, usbd_request) queue; + LIST_ENTRY(usbd_pipe) next; + + void (*disco) __P((void *)); + void *discoarg; + + usbd_request_handle intrreqh; /* used for repeating requests */ + usbd_request_handle curreqh; /* currently running request */ + + /* Filled by HC driver. */ + struct usbd_methods *methods; +}; + +struct usbd_request { + struct usbd_pipe *pipe; + void *priv; + void *buffer; + u_int32_t length; + u_int32_t actlen; + u_int16_t flags; + u_int32_t timeout; + usbd_status status; + usbd_callback callback; + usbd_xfercb xfercb; + u_int32_t retries; + char done; + + usb_device_request_t request; + u_int8_t isreq; + + SIMPLEQ_ENTRY(usbd_request) next; + + void *hcpriv; /* XXX private use by the HC driver */ + +#if defined(__FreeBSD__) + struct callout_handle callout_handler; +#endif +}; + +void usbd_init __P((void)); + +/* Routines from usb_subr.c */ +int usbctlprint __P((void *, const char *)); +void usbd_delay_ms __P((usbd_bus_handle, int)); +void usbd_devinfo_vp __P((usbd_device_handle, char *, char *)); +usbd_status usbd_set_config_no __P((usbd_device_handle, int, int)); +usbd_status usbd_reset_port __P((usbd_device_handle dev, + int port, usb_port_status_t *ps)); +usbd_status usbd_setup_pipe __P((usbd_device_handle dev, + usbd_interface_handle iface, + struct usbd_endpoint *, + usbd_pipe_handle *pipe)); +usbd_status usbd_new_device __P((bdevice *parent, + usbd_bus_handle bus, int depth, + int lowspeed, int port, + struct usbd_port *)); +void usbd_remove_device __P((usbd_device_handle, + struct usbd_port *)); +int usbd_printBCD __P((char *cp, int bcd)); + +/* Routines from usb.c */ +int usb_bus_count __P((void)); +usbd_status usb_get_bus_handle __P((int, usbd_bus_handle *)); +void usb_needs_explore __P((usbd_bus_handle)); + +#if defined(__FreeBSD__) +int usb_driver_load __P((module_t mod, modeventtype_t what, + void *arg)); +void usb_device_set_desc __P((device_t device, char *devinfo)); +#endif + +extern int usbd_use_polling; + +/* Locator stuff. */ + +#if defined(__NetBSD__) +/* NWH File not found anywhere in NetBSD sources... */ +#include "locators.h" +#endif + +#define uhubcf_port cf_loc[UHUBCF_PORT] +#define UHUB_UNK_PORT UHUBCF_PORT_DEFAULT /* wildcarded 'port' */ + +/* Junk. */ + +/* XXX */ +#define splusb splbio +#define IPL_USB IPL_BIO +/* XXX */ + diff --git a/sys/dev/usb/usbhid.h b/sys/dev/usb/usbhid.h new file mode 100644 index 0000000..c4a55df --- /dev/null +++ b/sys/dev/usb/usbhid.h @@ -0,0 +1,133 @@ +/* $NetBSD: usbhid.h,v 1.1 1998/07/12 19:52:01 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * 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. + */ + + +#ifndef _USBHID_H_ +#define _USBHID_H_ + +#define UR_GET_HID_DESCRIPTOR 0x06 +#define UDESC_HID 0x21 +#define UDESC_REPORT 0x22 +#define UDESC_PHYSICAL 0x23 +#define UR_SET_HID_DESCRIPTOR 0x07 +#define UR_GET_REPORT 0x01 +#define UR_SET_REPORT 0x09 +#define UR_GET_IDLE 0x02 +#define UR_SET_IDLE 0x0a +#define UR_GET_PROTOCOL 0x03 +#define UR_SET_PROTOCOL 0x0b + +typedef struct usb_hid_descriptor { + uByte bLength; + uByte bDescriptorType; + uWord bcdHID; + uByte bCountryCode; + uByte bNumDescriptors; + struct { + uByte bDescriptorType; + uWord wDescriptorLength; + } descrs[1]; +} usb_hid_descriptor_t; +#define USB_HID_DESCRIPTOR_SIZE(n) (9+(n)*3) + +/* Usage pages */ +#define HUP_GENERIC_DESKTOP 0x0001 +#define HUP_SIMULATION 0x0002 +#define HUP_LEDS 0x0008 +#define HUP_BUTTON 0x0009 + +/* Usages, generic desktop */ +#define HUG_POINTER 0x0001 +#define HUG_MOUSE 0x0002 +#define HUG_JOYSTICK 0x0004 +#define HUG_GAME_PAD 0x0005 +#define HUG_KEYBOARD 0x0006 +#define HUG_KEYPAD 0x0007 +#define HUG_X 0x0030 +#define HUG_Y 0x0031 +#define HUG_Z 0x0032 +#define HUG_RX 0x0033 +#define HUG_RY 0x0034 +#define HUG_RZ 0x0035 +#define HUG_SLIDER 0x0036 +#define HUG_DIAL 0x0037 +#define HUG_WHEEL 0x0038 +#define HUG_HAT_SWITCH 0x0039 +#define HUG_COUNTED_BUFFER 0x003a +#define HUG_BYTE_COUNT 0x003b +#define HUG_MOTION_WAKEUP 0x003c +#define HUG_VX 0x0040 +#define HUG_VY 0x0041 +#define HUG_VZ 0x0042 +#define HUG_VBRX 0x0043 +#define HUG_VBRY 0x0044 +#define HUG_VBRZ 0x0045 +#define HUG_VNO 0x0046 +#define HUG_SYSTEM_CONTROL 0x0080 +#define HUG_SYSTEM_POWER_DOWN 0x0081 +#define HUG_SYSTEM_SLEEP 0x0082 +#define HUG_SYSTEM_WAKEUP 0x0083 +#define HUG_SYSTEM_CONTEXT_MENU 0x0084 +#define HUG_SYSTEM_MAIN_MENU 0x0085 +#define HUG_SYSTEM_APP_MENU 0x0086 +#define HUG_SYSTEM_MENU_HELP 0x0087 +#define HUG_SYSTEM_MENU_EXIT 0x0088 +#define HUG_SYSTEM_MENU_SELECT 0x0089 +#define HUG_SYSTEM_MENU_RIGHT 0x008a +#define HUG_SYSTEM_MENU_LEFT 0x008b +#define HUG_SYSTEM_MENU_UP 0x008c +#define HUG_SYSTEM_MENU_DOWN 0x008d + +#define HID_USAGE2(p,u) (((p) << 16) | u) + +#define UHID_INPUT_REPORT 0x01 +#define UHID_OUTPUT_REPORT 0x02 +#define UHID_FEATURE_REPORT 0x03 + +/* Bits in the input/output/feature items */ +#define HIO_CONST 0x001 +#define HIO_VARIABLE 0x002 +#define HIO_RELATIVE 0x004 +#define HIO_WRAP 0x008 +#define HIO_NONLINEAR 0x010 +#define HIO_NOPREF 0x020 +#define HIO_NULLSTATE 0x040 +#define HIO_VOLATILE 0x080 +#define HIO_BUFBYTES 0x100 + +#endif /* _USBHID_H_ */ diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC index 930349a..f2c58b6 100644 --- a/sys/i386/conf/GENERIC +++ b/sys/i386/conf/GENERIC @@ -11,7 +11,7 @@ # device lines is present in the ./LINT configuration file. If you are # in doubt as to the purpose or necessity of a line, check first in LINT. # -# $Id: GENERIC,v 1.130 1998/11/03 22:01:21 des Exp $ +# $Id: GENERIC,v 1.131 1998/11/12 11:29:28 obrien Exp $ machine "i386" cpu "I386_CPU" @@ -179,3 +179,22 @@ options SYSVSHM # option. The number of devices determines the maximum number of # simultaneous BPF clients programs runnable. #pseudo-device bpfilter 4 #Berkeley packet filter + + +# USB support +#controller uhci0 +#controller usb0 +# +# for the moment we have to specify the priorities of the device +# drivers explicitly by the ordering in the list below. This will +# be changed in the future. +# +#device ums0 +#device ukbd0 +#device ulpt0 +#device uhub0 +#device hid0 +#device ugen0 +# +#options USB_DEBUG +#options USBVERBOSE |