diff options
-rw-r--r-- | sys/dev/usb/files.usb | 8 | ||||
-rw-r--r-- | sys/dev/usb/umidi.c | 1376 | ||||
-rw-r--r-- | sys/dev/usb/umidi_quirks.c | 215 | ||||
-rw-r--r-- | sys/dev/usb/umidi_quirks.h | 120 | ||||
-rw-r--r-- | sys/dev/usb/umidireg.h | 80 | ||||
-rw-r--r-- | sys/dev/usb/umidivar.h | 138 |
6 files changed, 1936 insertions, 1 deletions
diff --git a/sys/dev/usb/files.usb b/sys/dev/usb/files.usb index 77cb637854b..9bdc4909c9b 100644 --- a/sys/dev/usb/files.usb +++ b/sys/dev/usb/files.usb @@ -1,4 +1,4 @@ -# $OpenBSD: files.usb,v 1.26 2002/06/11 06:42:17 nate Exp $ +# $OpenBSD: files.usb,v 1.27 2002/06/11 07:49:56 nate Exp $ # $NetBSD: files.usb,v 1.16 2000/02/14 20:29:54 augustss Exp $ # # Config file and device description for machine-independent USB code. @@ -34,6 +34,12 @@ device uaudio: audio, auconv, mulaw attach uaudio at uhub file dev/usb/uaudio.c uaudio +# MIDI devices +device umidi: midibus +attach umidi at uhub +file dev/usb/umidi.c umidi +file dev/usb/umidi_quirks.c umidi + # Modem and com serial port device ucom attach ucom at ucombus diff --git a/sys/dev/usb/umidi.c b/sys/dev/usb/umidi.c new file mode 100644 index 00000000000..315a31bf902 --- /dev/null +++ b/sys/dev/usb/umidi.c @@ -0,0 +1,1376 @@ +/* $OpenBSD: umidi.c,v 1.3 2002/06/11 07:49:56 nate Exp $ */ +/* $NetBSD: umidi.c,v 1.14 2002/03/08 17:24:06 kent Exp $ */ +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Takuya SHIOZAKI (tshiozak@netbsd.org). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/ioctl.h> +#include <sys/conf.h> +#include <sys/file.h> +#include <sys/select.h> +#include <sys/proc.h> +#include <sys/vnode.h> +#include <sys/poll.h> +#include <sys/lock.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> + +#include <dev/usb/usbdevs.h> +#include <dev/usb/uaudioreg.h> +#include <dev/usb/umidireg.h> +#include <dev/usb/umidivar.h> +#include <dev/usb/umidi_quirks.h> + +#include <dev/midi_if.h> + +#ifdef UMIDI_DEBUG +#define DPRINTF(x) if (umididebug) printf x +#define DPRINTFN(n,x) if (umididebug >= (n)) printf x +int umididebug = 0; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + + +static int umidi_open(void *, int, + void (*)(void *, int), void (*)(void *), void *); +static void umidi_close(void *); +static int umidi_output(void *, int); +static void umidi_getinfo(void *, struct midi_info *); + +static usbd_status alloc_pipe(struct umidi_endpoint *); +static void free_pipe(struct umidi_endpoint *); + +static usbd_status alloc_all_endpoints(struct umidi_softc *); +static void free_all_endpoints(struct umidi_softc *); + +static usbd_status alloc_all_jacks(struct umidi_softc *); +static void free_all_jacks(struct umidi_softc *); +static usbd_status bind_jacks_to_mididev(struct umidi_softc *, + struct umidi_jack *, + struct umidi_jack *, + struct umidi_mididev *); +static void unbind_jacks_from_mididev(struct umidi_mididev *); +static void unbind_all_jacks(struct umidi_softc *); +static usbd_status assign_all_jacks_automatically(struct umidi_softc *); +static usbd_status open_out_jack(struct umidi_jack *, void *, + void (*)(void *)); +static usbd_status open_in_jack(struct umidi_jack *, void *, + void (*)(void *, int)); +static void close_out_jack(struct umidi_jack *); +static void close_in_jack(struct umidi_jack *); + +static usbd_status attach_mididev(struct umidi_softc *, + struct umidi_mididev *); +static usbd_status detach_mididev(struct umidi_mididev *, int); +static usbd_status deactivate_mididev(struct umidi_mididev *); +static usbd_status alloc_all_mididevs(struct umidi_softc *, int); +static void free_all_mididevs(struct umidi_softc *); +static usbd_status attach_all_mididevs(struct umidi_softc *); +static usbd_status detach_all_mididevs(struct umidi_softc *, int); +static usbd_status deactivate_all_mididevs(struct umidi_softc *); + +#ifdef UMIDI_DEBUG +static void dump_sc(struct umidi_softc *); +static void dump_ep(struct umidi_endpoint *); +static void dump_jack(struct umidi_jack *); +#endif + +static void init_packet(struct umidi_packet *); + +static usbd_status start_input_transfer(struct umidi_endpoint *); +static usbd_status start_output_transfer(struct umidi_endpoint *); +static int out_jack_output(struct umidi_jack *, int); +static void in_intr(usbd_xfer_handle, usbd_private_handle, usbd_status); +static void out_intr(usbd_xfer_handle, usbd_private_handle, usbd_status); +static void out_build_packet(int, struct umidi_packet *, uByte); + + +struct midi_hw_if umidi_hw_if = { + umidi_open, + umidi_close, + umidi_output, + umidi_getinfo, + 0, /* ioctl */ +}; + +USB_DECLARE_DRIVER(umidi); + +USB_MATCH(umidi) +{ + USB_MATCH_START(umidi, uaa); + usb_interface_descriptor_t *id; + + DPRINTFN(1,("umidi_match\n")); + + if (uaa->iface == NULL) + return UMATCH_NONE; + + if (umidi_search_quirk(uaa->vendor, uaa->product, uaa->ifaceno)) + return UMATCH_IFACECLASS_IFACESUBCLASS; + + id = usbd_get_interface_descriptor(uaa->iface); + if (id!=NULL && + id->bInterfaceClass==UICLASS_AUDIO && + id->bInterfaceSubClass==UISUBCLASS_MIDISTREAM) + return UMATCH_IFACECLASS_IFACESUBCLASS; + + return UMATCH_NONE; +} + +USB_ATTACH(umidi) +{ + usbd_status err; + USB_ATTACH_START(umidi, sc, uaa); + char devinfo[1024]; + + DPRINTFN(1,("umidi_attach\n")); + + usbd_devinfo(uaa->device, 0, devinfo); + printf("\n%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo); + + sc->sc_iface = uaa->iface; + sc->sc_udev = uaa->device; + + sc->sc_quirk = + umidi_search_quirk(uaa->vendor, uaa->product, uaa->ifaceno); + printf("%s: ", USBDEVNAME(sc->sc_dev)); + umidi_print_quirk(sc->sc_quirk); + + + err = alloc_all_endpoints(sc); + if (err!=USBD_NORMAL_COMPLETION) { + printf("%s: alloc_all_endpoints failed. (err=%d)\n", + USBDEVNAME(sc->sc_dev), err); + goto error; + } + err = alloc_all_jacks(sc); + if (err!=USBD_NORMAL_COMPLETION) { + free_all_endpoints(sc); + printf("%s: alloc_all_jacks failed. (err=%d)\n", + USBDEVNAME(sc->sc_dev), err); + goto error; + } + printf("%s: out=%d, in=%d\n", + USBDEVNAME(sc->sc_dev), + sc->sc_out_num_jacks, sc->sc_in_num_jacks); + + err = assign_all_jacks_automatically(sc); + if (err!=USBD_NORMAL_COMPLETION) { + unbind_all_jacks(sc); + free_all_jacks(sc); + free_all_endpoints(sc); + printf("%s: assign_all_jacks_automatically failed. (err=%d)\n", + USBDEVNAME(sc->sc_dev), err); + goto error; + } + err = attach_all_mididevs(sc); + if (err!=USBD_NORMAL_COMPLETION) { + free_all_jacks(sc); + free_all_endpoints(sc); + printf("%s: attach_all_mididevs failed. (err=%d)\n", + USBDEVNAME(sc->sc_dev), err); + } + +#ifdef UMIDI_DEBUG + dump_sc(sc); +#endif + + usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, + sc->sc_udev, USBDEV(sc->sc_dev)); + + USB_ATTACH_SUCCESS_RETURN; +error: + printf("%s: disabled.\n", USBDEVNAME(sc->sc_dev)); + sc->sc_dying = 1; + USB_ATTACH_ERROR_RETURN; +} + +int +umidi_activate(device_ptr_t self, enum devact act) +{ + struct umidi_softc *sc = (struct umidi_softc *)self; + + switch (act) { + case DVACT_ACTIVATE: + DPRINTFN(1,("umidi_activate (activate)\n")); + + return EOPNOTSUPP; + break; + case DVACT_DEACTIVATE: + DPRINTFN(1,("umidi_activate (deactivate)\n")); + sc->sc_dying = 1; + deactivate_all_mididevs(sc); + break; + } + return 0; +} + +USB_DETACH(umidi) +{ + USB_DETACH_START(umidi, sc); + + DPRINTFN(1,("umidi_detach\n")); + + sc->sc_dying = 1; + detach_all_mididevs(sc, flags); + free_all_mididevs(sc); + free_all_jacks(sc); + free_all_endpoints(sc); + + usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, + USBDEV(sc->sc_dev)); + + return 0; +} + + +/* + * midi_if stuffs + */ +int +umidi_open(void *addr, + int flags, + void (*iintr)(void *, int), + void (*ointr)(void *), + void *arg) +{ + struct umidi_mididev *mididev = addr; + struct umidi_softc *sc = mididev->sc; + + DPRINTF(("umidi_open: sc=%p\n", sc)); + + if (!sc) + return ENXIO; + if (mididev->opened) + return EBUSY; + if (sc->sc_dying) + return EIO; + + mididev->opened = 1; + mididev->flags = flags; + if ((mididev->flags & FWRITE) && mididev->out_jack) + open_out_jack(mididev->out_jack, arg, ointr); + if ((mididev->flags & FREAD) && mididev->in_jack) { + open_in_jack(mididev->in_jack, arg, iintr); + } + + return 0; +} + +void +umidi_close(void *addr) +{ + int s; + struct umidi_mididev *mididev = addr; + + s = splusb(); + if ((mididev->flags & FWRITE) && mididev->out_jack) + close_out_jack(mididev->out_jack); + if ((mididev->flags & FREAD) && mididev->in_jack) + close_in_jack(mididev->in_jack); + mididev->opened = 0; + splx(s); +} + +int +umidi_output(void *addr, int d) +{ + struct umidi_mididev *mididev = addr; + + if (!mididev->out_jack || !mididev->opened) + return EIO; + + return out_jack_output(mididev->out_jack, d); +} + +void +umidi_getinfo(void *addr, struct midi_info *mi) +{ + struct umidi_mididev *mididev = addr; +/* struct umidi_softc *sc = mididev->sc; */ + + mi->name = "USB MIDI I/F"; /* XXX: model name */ + mi->props = MIDI_PROP_OUT_INTR; + if (mididev->in_jack) + mi->props |= MIDI_PROP_CAN_INPUT; +} + + +/* + * each endpoint stuffs + */ + +/* alloc/free pipe */ +static usbd_status +alloc_pipe(struct umidi_endpoint *ep) +{ + struct umidi_softc *sc = ep->sc; + usbd_status err; + + DPRINTF(("%s: alloc_pipe %p\n", USBDEVNAME(sc->sc_dev), ep)); + LIST_INIT(&ep->queue_head); + ep->xfer = usbd_alloc_xfer(sc->sc_udev); + if (ep->xfer == NULL) { + err = USBD_NOMEM; + goto quit; + } + ep->buffer = usbd_alloc_buffer(ep->xfer, UMIDI_PACKET_SIZE); + if (ep->buffer == NULL) { + usbd_free_xfer(ep->xfer); + err = USBD_NOMEM; + goto quit; + } + err = usbd_open_pipe(sc->sc_iface, ep->addr, 0, &ep->pipe); + if (err) + usbd_free_xfer(ep->xfer); +quit: + return err; +} + +static void +free_pipe(struct umidi_endpoint *ep) +{ + DPRINTF(("%s: free_pipe %p\n", USBDEVNAME(ep->sc->sc_dev), ep)); + usbd_abort_pipe(ep->pipe); + usbd_close_pipe(ep->pipe); + usbd_free_xfer(ep->xfer); +} + + +/* alloc/free the array of endpoint structures */ + +static usbd_status alloc_all_endpoints_fixed_ep(struct umidi_softc *); +static usbd_status alloc_all_endpoints_yamaha(struct umidi_softc *); +static usbd_status alloc_all_endpoints_genuine(struct umidi_softc *); + +static usbd_status +alloc_all_endpoints(struct umidi_softc *sc) +{ + usbd_status err; + struct umidi_endpoint *ep; + int i; + + if (UMQ_ISTYPE(sc, UMQ_TYPE_FIXED_EP)) { + err = alloc_all_endpoints_fixed_ep(sc); + } else if (UMQ_ISTYPE(sc, UMQ_TYPE_YAMAHA)) { + err = alloc_all_endpoints_yamaha(sc); + } else { + err = alloc_all_endpoints_genuine(sc); + } + if (err!=USBD_NORMAL_COMPLETION) + return err; + + ep = sc->sc_endpoints; + for (i=sc->sc_out_num_endpoints+sc->sc_in_num_endpoints; i>0; i--) { + err = alloc_pipe(ep++); + if (err!=USBD_NORMAL_COMPLETION) { + for (; ep!=sc->sc_endpoints; ep--) + free_pipe(ep-1); + free(sc->sc_endpoints, M_USBDEV); + sc->sc_endpoints = sc->sc_out_ep = sc->sc_in_ep = NULL; + break; + } + } + return err; +} + +static void +free_all_endpoints(struct umidi_softc *sc) +{ + int i; + for (i=0; i<sc->sc_in_num_endpoints+sc->sc_out_num_endpoints; i++) + free_pipe(&sc->sc_endpoints[i]); + if (sc->sc_endpoints != NULL) + free(sc->sc_endpoints, M_USBDEV); + sc->sc_endpoints = sc->sc_out_ep = sc->sc_in_ep = NULL; +} + +static usbd_status +alloc_all_endpoints_fixed_ep(struct umidi_softc *sc) +{ + usbd_status err; + struct umq_fixed_ep_desc *fp; + struct umidi_endpoint *ep; + usb_endpoint_descriptor_t *epd; + int i; + + fp = umidi_get_quirk_data_from_type(sc->sc_quirk, + UMQ_TYPE_FIXED_EP); + sc->sc_out_num_jacks = 0; + sc->sc_in_num_jacks = 0; + sc->sc_out_num_endpoints = fp->num_out_ep; + sc->sc_in_num_endpoints = fp->num_in_ep; + sc->sc_endpoints = malloc(sizeof(*sc->sc_out_ep)* + (sc->sc_out_num_endpoints+ + sc->sc_in_num_endpoints), + M_USBDEV, M_WAITOK); + if (!sc->sc_endpoints) { + return USBD_NOMEM; + } + sc->sc_out_ep = sc->sc_out_num_endpoints ? sc->sc_endpoints : NULL; + sc->sc_in_ep = + sc->sc_in_num_endpoints ? + sc->sc_endpoints+sc->sc_out_num_endpoints : NULL; + + ep = &sc->sc_out_ep[0]; + for (i=0; i<sc->sc_out_num_endpoints; i++) { + epd = usbd_interface2endpoint_descriptor( + sc->sc_iface, + fp->out_ep[i].ep); + if (!epd) { + printf("%s: cannot get endpoint descriptor(out:%d)\n", + USBDEVNAME(sc->sc_dev), fp->out_ep[i].ep); + err = USBD_INVAL; + goto error; + } + if (UE_GET_XFERTYPE(epd->bmAttributes)!=UE_BULK || + UE_GET_DIR(epd->bEndpointAddress)!=UE_DIR_OUT) { + printf("%s: illegal endpoint(out:%d)\n", + USBDEVNAME(sc->sc_dev), fp->out_ep[i].ep); + err = USBD_INVAL; + goto error; + } + ep->sc = sc; + ep->addr = epd->bEndpointAddress; + ep->num_jacks = fp->out_ep[i].num_jacks; + sc->sc_out_num_jacks += fp->out_ep[i].num_jacks; + ep->num_open = 0; + memset(ep->jacks, 0, sizeof(ep->jacks)); + LIST_INIT(&ep->queue_head); + ep++; + } + ep = &sc->sc_in_ep[0]; + for (i=0; i<sc->sc_in_num_endpoints; i++) { + epd = usbd_interface2endpoint_descriptor( + sc->sc_iface, + fp->in_ep[i].ep); + if (!epd) { + printf("%s: cannot get endpoint descriptor(in:%d)\n", + USBDEVNAME(sc->sc_dev), fp->in_ep[i].ep); + err = USBD_INVAL; + goto error; + } + if (UE_GET_XFERTYPE(epd->bmAttributes)!=UE_BULK || + UE_GET_DIR(epd->bEndpointAddress)!=UE_DIR_IN) { + printf("%s: illegal endpoint(in:%d)\n", + USBDEVNAME(sc->sc_dev), fp->in_ep[i].ep); + err = USBD_INVAL; + goto error; + } + ep->sc = sc; + ep->addr = epd->bEndpointAddress; + ep->num_jacks = fp->in_ep[i].num_jacks; + sc->sc_in_num_jacks += fp->in_ep[i].num_jacks; + ep->num_open = 0; + memset(ep->jacks, 0, sizeof(ep->jacks)); + ep++; + } + + return USBD_NORMAL_COMPLETION; +error: + free(sc->sc_endpoints, M_USBDEV); + sc->sc_endpoints = NULL; + return err; +} + +static usbd_status +alloc_all_endpoints_yamaha(struct umidi_softc *sc) +{ + /* This driver currently supports max 1in/1out bulk endpoints */ + usb_descriptor_t *desc; + usb_endpoint_descriptor_t *epd; + int out_addr, in_addr, i; + int dir; + size_t remain, descsize; + + sc->sc_out_num_jacks = sc->sc_in_num_jacks = 0; + out_addr = in_addr = 0; + + /* detect endpoints */ + desc = TO_D(usbd_get_interface_descriptor(sc->sc_iface)); + for (i=(int)TO_IFD(desc)->bNumEndpoints-1; i>=0; i--) { + epd = usbd_interface2endpoint_descriptor(sc->sc_iface, i); + if (UE_GET_XFERTYPE(epd->bmAttributes) == UE_BULK) { + dir = UE_GET_DIR(epd->bEndpointAddress); + if (dir==UE_DIR_OUT && !out_addr) + out_addr = epd->bEndpointAddress; + else if (dir==UE_DIR_IN && !in_addr) + in_addr = epd->bEndpointAddress; + } + } + desc = NEXT_D(desc); + + /* count jacks */ + if (!(desc->bDescriptorType==UDESC_CS_INTERFACE && + desc->bDescriptorSubtype==UMIDI_MS_HEADER)) + return USBD_INVAL; + remain = (size_t)UGETW(TO_CSIFD(desc)->wTotalLength) - + (size_t)desc->bLength; + desc = NEXT_D(desc); + + while (remain>=sizeof(usb_descriptor_t)) { + descsize = desc->bLength; + if (descsize>remain || descsize==0) + break; + if (desc->bDescriptorType==UDESC_CS_INTERFACE && + remain>=UMIDI_JACK_DESCRIPTOR_SIZE) { + if (desc->bDescriptorSubtype==UMIDI_OUT_JACK) + sc->sc_out_num_jacks++; + else if (desc->bDescriptorSubtype==UMIDI_IN_JACK) + sc->sc_in_num_jacks++; + } + desc = NEXT_D(desc); + remain-=descsize; + } + + /* validate some parameters */ + if (sc->sc_out_num_jacks>UMIDI_MAX_EPJACKS) + sc->sc_out_num_jacks = UMIDI_MAX_EPJACKS; + if (sc->sc_in_num_jacks>UMIDI_MAX_EPJACKS) + sc->sc_in_num_jacks = UMIDI_MAX_EPJACKS; + if (sc->sc_out_num_jacks && out_addr) { + sc->sc_out_num_endpoints = 1; + } else { + sc->sc_out_num_endpoints = 0; + sc->sc_out_num_jacks = 0; + } + if (sc->sc_in_num_jacks && in_addr) { + sc->sc_in_num_endpoints = 1; + } else { + sc->sc_in_num_endpoints = 0; + sc->sc_in_num_jacks = 0; + } + sc->sc_endpoints = malloc(sizeof(struct umidi_endpoint)* + (sc->sc_out_num_endpoints+ + sc->sc_in_num_endpoints), + M_USBDEV, M_WAITOK); + if (!sc->sc_endpoints) + return USBD_NOMEM; + if (sc->sc_out_num_endpoints) { + sc->sc_out_ep = sc->sc_endpoints; + sc->sc_out_ep->sc = sc; + sc->sc_out_ep->addr = out_addr; + sc->sc_out_ep->num_jacks = sc->sc_out_num_jacks; + sc->sc_out_ep->num_open = 0; + memset(sc->sc_out_ep->jacks, 0, sizeof(sc->sc_out_ep->jacks)); + } else + sc->sc_out_ep = NULL; + + if (sc->sc_in_num_endpoints) { + sc->sc_in_ep = sc->sc_endpoints+sc->sc_out_num_endpoints; + sc->sc_in_ep->sc = sc; + sc->sc_in_ep->addr = in_addr; + sc->sc_in_ep->num_jacks = sc->sc_in_num_jacks; + sc->sc_in_ep->num_open = 0; + memset(sc->sc_in_ep->jacks, 0, sizeof(sc->sc_in_ep->jacks)); + } else + sc->sc_in_ep = NULL; + + return USBD_NORMAL_COMPLETION; +} + +static usbd_status +alloc_all_endpoints_genuine(struct umidi_softc *sc) +{ + usb_descriptor_t *desc; + int num_ep; + size_t remain, descsize; + struct umidi_endpoint *p, *q, *lowest, *endep, tmpep; + int epaddr; + + desc = TO_D(usbd_get_interface_descriptor(sc->sc_iface)); + num_ep = TO_IFD(desc)->bNumEndpoints; + desc = NEXT_D(desc); /* ifd -> csifd */ + remain = ((size_t)UGETW(TO_CSIFD(desc)->wTotalLength) - + (size_t)desc->bLength); + desc = NEXT_D(desc); + + sc->sc_endpoints = p = malloc(sizeof(struct umidi_endpoint)*num_ep, + M_USBDEV, M_WAITOK); + if (!p) + return USBD_NOMEM; + + sc->sc_out_num_jacks = sc->sc_in_num_jacks = 0; + sc->sc_out_num_endpoints = sc->sc_in_num_endpoints = 0; + epaddr = -1; + + /* get the list of endpoints for midi stream */ + while (remain>=sizeof(usb_descriptor_t)) { + descsize = desc->bLength; + if (descsize>remain || descsize==0) + break; + if (desc->bDescriptorType==UDESC_ENDPOINT && + remain>=USB_ENDPOINT_DESCRIPTOR_SIZE && + UE_GET_XFERTYPE(TO_EPD(desc)->bmAttributes) == UE_BULK) { + epaddr = TO_EPD(desc)->bEndpointAddress; + } else if (desc->bDescriptorType==UDESC_CS_ENDPOINT && + remain>=UMIDI_CS_ENDPOINT_DESCRIPTOR_SIZE && + epaddr!=-1) { + if (num_ep>0) { + num_ep--; + p->sc = sc; + p->addr = epaddr; + p->num_jacks = TO_CSEPD(desc)->bNumEmbMIDIJack; + if (UE_GET_DIR(epaddr)==UE_DIR_OUT) { + sc->sc_out_num_endpoints++; + sc->sc_out_num_jacks += p->num_jacks; + } else { + sc->sc_in_num_endpoints++; + sc->sc_in_num_jacks += p->num_jacks; + } + p++; + } + } else + epaddr = -1; + desc = NEXT_D(desc); + remain-=descsize; + } + + /* sort endpoints */ + num_ep = sc->sc_out_num_endpoints + sc->sc_in_num_endpoints; + p = sc->sc_endpoints; + endep = p + num_ep; + while (p<endep) { + lowest = p; + for (q=p+1; q<endep; q++) { + if ((UE_GET_DIR(lowest->addr)==UE_DIR_IN && + UE_GET_DIR(q->addr)==UE_DIR_OUT) || + ((UE_GET_DIR(lowest->addr)== + UE_GET_DIR(q->addr)) && + (UE_GET_ADDR(lowest->addr)> + UE_GET_ADDR(q->addr)))) + lowest = q; + } + if (lowest != p) { + memcpy((void *)&tmpep, (void *)p, sizeof(tmpep)); + memcpy((void *)p, (void *)lowest, sizeof(tmpep)); + memcpy((void *)lowest, (void *)&tmpep, sizeof(tmpep)); + } + p->num_open = 0; + p++; + } + + sc->sc_out_ep = sc->sc_out_num_endpoints ? sc->sc_endpoints : NULL; + sc->sc_in_ep = + sc->sc_in_num_endpoints ? + sc->sc_endpoints+sc->sc_out_num_endpoints : NULL; + + return USBD_NORMAL_COMPLETION; +} + + +/* + * jack stuffs + */ + +static usbd_status +alloc_all_jacks(struct umidi_softc *sc) +{ + int i, j; + struct umidi_endpoint *ep; + struct umidi_jack *jack, **rjack; + + /* allocate/initialize structures */ + sc->sc_jacks = + malloc(sizeof(*sc->sc_out_jacks)*(sc->sc_in_num_jacks+ + sc->sc_out_num_jacks), + M_USBDEV, M_WAITOK); + if (!sc->sc_jacks) + return USBD_NOMEM; + sc->sc_out_jacks = + sc->sc_out_num_jacks ? sc->sc_jacks : NULL; + sc->sc_in_jacks = + sc->sc_in_num_jacks ? sc->sc_jacks+sc->sc_out_num_jacks : NULL; + + jack = &sc->sc_out_jacks[0]; + for (i=0; i<sc->sc_out_num_jacks; i++) { + jack->opened = 0; + jack->binded = 0; + jack->arg = NULL; + jack->u.out.intr = NULL; + jack->cable_number = i; + jack++; + } + jack = &sc->sc_in_jacks[0]; + for (i=0; i<sc->sc_in_num_jacks; i++) { + jack->opened = 0; + jack->binded = 0; + jack->arg = NULL; + jack->u.in.intr = NULL; + jack->cable_number = i; + jack++; + } + + /* assign each jacks to each endpoints */ + jack = &sc->sc_out_jacks[0]; + ep = &sc->sc_out_ep[0]; + for (i=0; i<sc->sc_out_num_endpoints; i++) { + rjack = &ep->jacks[0]; + for (j=0; j<ep->num_jacks; j++) { + *rjack = jack; + jack->endpoint = ep; + jack++; + rjack++; + } + ep++; + } + jack = &sc->sc_in_jacks[0]; + ep = &sc->sc_in_ep[0]; + for (i=0; i<sc->sc_in_num_endpoints; i++) { + rjack = &ep->jacks[0]; + for (j=0; j<ep->num_jacks; j++) { + *rjack = jack; + jack->endpoint = ep; + jack++; + rjack++; + } + ep++; + } + + return USBD_NORMAL_COMPLETION; +} + +static void +free_all_jacks(struct umidi_softc *sc) +{ + int s; + + s = splaudio(); + if (sc->sc_out_jacks) { + free(sc->sc_jacks, M_USBDEV); + sc->sc_jacks = sc->sc_in_jacks = sc->sc_out_jacks = NULL; + } + splx(s); +} + +static usbd_status +bind_jacks_to_mididev(struct umidi_softc *sc, + struct umidi_jack *out_jack, + struct umidi_jack *in_jack, + struct umidi_mididev *mididev) +{ + if ((out_jack && out_jack->binded) || (in_jack && in_jack->binded)) + return USBD_IN_USE; + if (mididev->out_jack || mididev->in_jack) + return USBD_IN_USE; + + if (out_jack) + out_jack->binded = 1; + if (in_jack) + in_jack->binded = 1; + mididev->in_jack = in_jack; + mididev->out_jack = out_jack; + + return USBD_NORMAL_COMPLETION; +} + +static void +unbind_jacks_from_mididev(struct umidi_mididev *mididev) +{ + if ((mididev->flags&FWRITE) && mididev->out_jack) + close_out_jack(mididev->out_jack); + if ((mididev->flags&FWRITE) && mididev->in_jack) + close_in_jack(mididev->in_jack); + + if (mididev->out_jack) + mididev->out_jack->binded = 0; + if (mididev->in_jack) + mididev->in_jack->binded = 0; + mididev->out_jack = mididev->in_jack = NULL; +} + +static void +unbind_all_jacks(struct umidi_softc *sc) +{ + int i; + + if (sc->sc_mididevs) + for (i=0; i<sc->sc_num_mididevs; i++) { + unbind_jacks_from_mididev(&sc->sc_mididevs[i]); + } +} + +static usbd_status +assign_all_jacks_automatically(struct umidi_softc *sc) +{ + usbd_status err; + int i; + struct umidi_jack *out, *in; + + err = + alloc_all_mididevs(sc, + max(sc->sc_out_num_jacks, sc->sc_in_num_jacks)); + if (err!=USBD_NORMAL_COMPLETION) + return err; + + for (i=0; i<sc->sc_num_mididevs; i++) { + out = (i<sc->sc_out_num_jacks) ? &sc->sc_out_jacks[i]:NULL; + in = (i<sc->sc_in_num_jacks) ? &sc->sc_in_jacks[i]:NULL; + err = bind_jacks_to_mididev(sc, out, in, &sc->sc_mididevs[i]); + if (err!=USBD_NORMAL_COMPLETION) { + free_all_mididevs(sc); + return err; + } + } + + return USBD_NORMAL_COMPLETION; +} + +static usbd_status +open_out_jack(struct umidi_jack *jack, void *arg, void (*intr)(void *)) +{ + struct umidi_endpoint *ep = jack->endpoint; + + if (jack->opened) + return USBD_IN_USE; + + jack->arg = arg; + jack->u.out.intr = intr; + init_packet(&jack->packet); + jack->opened = 1; + ep->num_open++; + + return USBD_NORMAL_COMPLETION; +} + +static usbd_status +open_in_jack(struct umidi_jack *jack, void *arg, void (*intr)(void *, int)) +{ + usbd_status err = USBD_NORMAL_COMPLETION; + struct umidi_endpoint *ep = jack->endpoint; + + if (jack->opened) + return USBD_IN_USE; + + jack->arg = arg; + jack->u.in.intr = intr; + jack->opened = 1; + if (ep->num_open++==0 && UE_GET_DIR(ep->addr)==UE_DIR_IN) { + err = start_input_transfer(ep); + if (err!=USBD_NORMAL_COMPLETION) { + ep->num_open--; + } + } + + return err; +} + +static void +close_out_jack(struct umidi_jack *jack) +{ + struct umidi_jack *tail; + int s; + + if (jack->opened) { + s = splusb(); + LIST_REMOVE(jack, u.out.queue_entry); + if (jack==jack->endpoint->queue_tail) { + /* find tail */ + LIST_FOREACH(tail, + &jack->endpoint->queue_head, + u.out.queue_entry) { + if (!LIST_NEXT(tail, u.out.queue_entry)) { + jack->endpoint->queue_tail = tail; + } + } + } + splx(s); + jack->opened = 0; + jack->endpoint->num_open--; + } +} + +static void +close_in_jack(struct umidi_jack *jack) +{ + if (jack->opened) { + jack->opened = 0; + jack->endpoint->num_open--; + } +} + +static usbd_status +attach_mididev(struct umidi_softc *sc, struct umidi_mididev *mididev) +{ + if (mididev->sc) + return USBD_IN_USE; + + mididev->sc = sc; + + mididev->mdev = midi_attach_mi(&umidi_hw_if, mididev, &sc->sc_dev); + + return USBD_NORMAL_COMPLETION; +} + +static usbd_status +detach_mididev(struct umidi_mididev *mididev, int flags) +{ + if (!mididev->sc) + return USBD_NO_ADDR; + + if (mididev->opened) { + umidi_close(mididev); + } + unbind_jacks_from_mididev(mididev); + + if (mididev->mdev) + config_detach(mididev->mdev, flags); + + mididev->sc = NULL; + + return USBD_NORMAL_COMPLETION; +} + +static usbd_status +deactivate_mididev(struct umidi_mididev *mididev) +{ + if (mididev->out_jack) + mididev->out_jack->binded = 0; + if (mididev->in_jack) + mididev->in_jack->binded = 0; + config_deactivate(mididev->mdev); + + return USBD_NORMAL_COMPLETION; +} + +static usbd_status +alloc_all_mididevs(struct umidi_softc *sc, int nmidi) +{ + sc->sc_num_mididevs = nmidi; + sc->sc_mididevs = malloc(sizeof(*sc->sc_mididevs)*nmidi, + M_USBDEV, M_WAITOK); + memset(sc->sc_mididevs, 0, sizeof(*sc->sc_mididevs)*nmidi); + + if (!sc->sc_mididevs) + return USBD_NOMEM; + + return USBD_NORMAL_COMPLETION; +} + +static void +free_all_mididevs(struct umidi_softc *sc) +{ + sc->sc_num_mididevs = 0; + if (sc->sc_mididevs) + free(sc->sc_mididevs, M_USBDEV); +} + +static usbd_status +attach_all_mididevs(struct umidi_softc *sc) +{ + usbd_status err; + int i; + + if (sc->sc_mididevs) + for (i=0; i<sc->sc_num_mididevs; i++) { + err = attach_mididev(sc, &sc->sc_mididevs[i]); + if (err!=USBD_NORMAL_COMPLETION) + return err; + } + + return USBD_NORMAL_COMPLETION; +} + +static usbd_status +detach_all_mididevs(struct umidi_softc *sc, int flags) +{ + usbd_status err; + int i; + + if (sc->sc_mididevs) + for (i=0; i<sc->sc_num_mididevs; i++) { + err = detach_mididev(&sc->sc_mididevs[i], flags); + if (err!=USBD_NORMAL_COMPLETION) + return err; + } + + return USBD_NORMAL_COMPLETION; +} + +static usbd_status +deactivate_all_mididevs(struct umidi_softc *sc) +{ + usbd_status err; + int i; + + if (sc->sc_mididevs) + for (i=0; i<sc->sc_num_mididevs; i++) { + err = deactivate_mididev(&sc->sc_mididevs[i]); + if (err!=USBD_NORMAL_COMPLETION) + return err; + } + + return USBD_NORMAL_COMPLETION; +} + +#ifdef UMIDI_DEBUG +static void +dump_sc(struct umidi_softc *sc) +{ + int i; + + DPRINTFN(10, ("%s: dump_sc\n", USBDEVNAME(sc->sc_dev))); + for (i=0; i<sc->sc_out_num_endpoints; i++) { + DPRINTFN(10, ("\tout_ep(%p):\n", &sc->sc_out_ep[i])); + dump_ep(&sc->sc_out_ep[i]); + } + for (i=0; i<sc->sc_in_num_endpoints; i++) { + DPRINTFN(10, ("\tin_ep(%p):\n", &sc->sc_in_ep[i])); + dump_ep(&sc->sc_in_ep[i]); + } +} + +static void +dump_ep(struct umidi_endpoint *ep) +{ + int i; + for (i=0; i<ep->num_jacks; i++) { + DPRINTFN(10, ("\t\tjack(%p):\n", ep->jacks[i])); + dump_jack(ep->jacks[i]); + } +} +static void +dump_jack(struct umidi_jack *jack) +{ + DPRINTFN(10, ("\t\t\tep=%p, mididev=%p\n", + jack->endpoint, jack->mididev)); +} + +#endif /* UMIDI_DEBUG */ + + + +/* + * MUX MIDI PACKET + */ + +static const int packet_length[16] = { + /*0*/ -1, + /*1*/ -1, + /*2*/ 2, + /*3*/ 3, + /*4*/ 3, + /*5*/ 1, + /*6*/ 2, + /*7*/ 3, + /*8*/ 3, + /*9*/ 3, + /*A*/ 3, + /*B*/ 3, + /*C*/ 2, + /*D*/ 2, + /*E*/ 3, + /*F*/ 1, +}; + +static const struct { + int cin; + packet_state_t next; +} packet_0xFX[16] = { + /*F0: SysEx */ { 0x04, PS_EXCL_1 }, + /*F1: MTC */ { 0x02, PS_NORMAL_1OF2 }, + /*F2: S.POS */ { 0x03, PS_NORMAL_1OF3 }, + /*F3: S.SEL */ { 0x02, PS_NORMAL_1OF2 }, + /*F4: UNDEF */ { 0x00, PS_INITIAL }, + /*F5: UNDEF */ { 0x00, PS_INITIAL }, + /*F6: Tune */ { 0x0F, PS_END }, + /*F7: EofEx */ { 0x00, PS_INITIAL }, + /*F8: Timing */ { 0x0F, PS_END }, + /*F9: UNDEF */ { 0x00, PS_INITIAL }, + /*FA: Start */ { 0x0F, PS_END }, + /*FB: Cont */ { 0x0F, PS_END }, + /*FC: Stop */ { 0x0F, PS_END }, + /*FD: UNDEF */ { 0x00, PS_INITIAL }, + /*FE: ActS */ { 0x0F, PS_END }, + /*FF: Reset */ { 0x0F, PS_END }, +}; + +#define GET_CN(p) (((unsigned char)(p)>>4)&0x0F) +#define GET_CIN(p) ((unsigned char)(p)&0x0F) +#define MIX_CN_CIN(cn, cin) \ + ((unsigned char)((((unsigned char)(cn)&0x0F)<<4)| \ + ((unsigned char)(cin)&0x0F))) + +static void +init_packet(struct umidi_packet *packet) +{ + memset(packet->buffer, 0, UMIDI_PACKET_SIZE); + packet->state = PS_INITIAL; +} + +static usbd_status +start_input_transfer(struct umidi_endpoint *ep) +{ + usbd_setup_xfer(ep->xfer, ep->pipe, + (usbd_private_handle)ep, + ep->buffer, UMIDI_PACKET_SIZE, + USBD_NO_COPY, USBD_NO_TIMEOUT, in_intr); + return usbd_transfer(ep->xfer); +} + +static usbd_status +start_output_transfer(struct umidi_endpoint *ep) +{ + usbd_setup_xfer(ep->xfer, ep->pipe, + (usbd_private_handle)ep, + ep->buffer, UMIDI_PACKET_SIZE, + USBD_NO_COPY, USBD_NO_TIMEOUT, out_intr); + return usbd_transfer(ep->xfer); +} + +#ifdef UMIDI_DEBUG +#define DPR_PACKET(dir, sc, p) \ +if ((unsigned char)(p)->buffer[1]!=0xFE) \ + DPRINTFN(500, \ + ("%s: umidi packet(" #dir "): %02X %02X %02X %02X\n", \ + USBDEVNAME(sc->sc_dev), \ + (unsigned char)(p)->buffer[0], \ + (unsigned char)(p)->buffer[1], \ + (unsigned char)(p)->buffer[2], \ + (unsigned char)(p)->buffer[3])); +#else +#define DPR_PACKET(dir, sc, p) +#endif + +static int +out_jack_output(struct umidi_jack *out_jack, int d) +{ + struct umidi_endpoint *ep = out_jack->endpoint; + struct umidi_softc *sc = ep->sc; + int error; + int s; + + if (sc->sc_dying) + return EIO; + + error = 0; + if (out_jack->opened) { + DPRINTFN(1000, ("umidi_output: ep=%p 0x%02x\n", ep, d)); + out_build_packet(out_jack->cable_number, &out_jack->packet, d); + switch (out_jack->packet.state) { + case PS_EXCL_0: + case PS_END: + DPR_PACKET(out, sc, &out_jack->packet); + s = splusb(); + if (LIST_EMPTY(&ep->queue_head)) { + memcpy(ep->buffer, + out_jack->packet.buffer, + UMIDI_PACKET_SIZE); + start_output_transfer(ep); + } + if (LIST_EMPTY(&ep->queue_head)) + LIST_INSERT_HEAD(&ep->queue_head, + out_jack, u.out.queue_entry); + else + LIST_INSERT_AFTER(ep->queue_tail, + out_jack, u.out.queue_entry); + ep->queue_tail = out_jack; + splx(s); + break; + default: + error = EINPROGRESS; + } + } else + error = ENODEV; + + return error; +} + +static void +in_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) +{ + int cn, len, i; + struct umidi_endpoint *ep = (struct umidi_endpoint *)priv; + struct umidi_jack *jack; + + if (ep->sc->sc_dying || !ep->num_open) + return; + + cn = GET_CN(ep->buffer[0]); + len = packet_length[GET_CIN(ep->buffer[0])]; + jack = ep->jacks[cn]; + if (cn>=ep->num_jacks || !jack) { + DPRINTF(("%s: stray umidi packet (in): %02X %02X %02X %02X\n", + USBDEVNAME(ep->sc->sc_dev), + (unsigned)ep->buffer[0], + (unsigned)ep->buffer[1], + (unsigned)ep->buffer[2], + (unsigned)ep->buffer[3])); + return; + } + if (!jack->binded || !jack->opened) + return; + DPR_PACKET(in, ep->sc, &jack->buffer); + if (jack->u.in.intr) { + for (i=0; i<len; i++) { + (*jack->u.in.intr)(jack->arg, ep->buffer[i+1]); + } + } + + (void)start_input_transfer(ep); +} + +static void +out_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) +{ + struct umidi_endpoint *ep = (struct umidi_endpoint *)priv; + struct umidi_softc *sc = ep->sc; + struct umidi_jack *jack; + + if (sc->sc_dying || !ep->num_open) + return; + + jack = LIST_FIRST(&ep->queue_head); + if (jack && jack->opened) { + LIST_REMOVE(jack, u.out.queue_entry); + if (!LIST_EMPTY(&ep->queue_head)) { + memcpy(ep->buffer, + LIST_FIRST(&ep->queue_head)->packet.buffer, + UMIDI_PACKET_SIZE); + (void)start_output_transfer(ep); + } + if (jack->u.out.intr) { + (*jack->u.out.intr)(jack->arg); + } + } +} + +static void +out_build_packet(int cable_number, struct umidi_packet *packet, uByte in) +{ + int cin; + uByte prev; + +retry: + switch (packet->state) { + case PS_END: + case PS_INITIAL: + prev = packet->buffer[1]; + memset(packet->buffer, 0, UMIDI_PACKET_SIZE); + if (in<0x80) { + if (prev>=0x80 && prev<0xf0) { + /* running status */ + out_build_packet(cable_number, packet, prev); + goto retry; + } + /* ??? */ + break; + } + if (in>=0xf0) { + cin=packet_0xFX[in&0x0F].cin; + packet->state=packet_0xFX[in&0x0F].next; + } else { + cin=(unsigned char)in>>4; + switch (packet_length[cin]) { + case 2: + packet->state = PS_NORMAL_1OF2; + break; + case 3: + packet->state = PS_NORMAL_1OF3; + break; + default: + /* ??? */ + packet->state = PS_INITIAL; + } + } + packet->buffer[0] = MIX_CN_CIN(cable_number, cin); + packet->buffer[1] = in; + break; + case PS_NORMAL_1OF3: + if (in>=0x80) { /* ??? */ packet->state = PS_END; break; } + packet->buffer[2] = in; + packet->state = PS_NORMAL_2OF3; + break; + case PS_NORMAL_2OF3: + if (in>=0x80) { /* ??? */ packet->state = PS_END; break; } + packet->buffer[3] = in; + packet->state = PS_END; + break; + case PS_NORMAL_1OF2: + if (in>=0x80) { /* ??? */ packet->state = PS_END; break; } + packet->buffer[2] = in; + packet->state = PS_END; + break; + case PS_EXCL_0: + memset(packet->buffer, 0, UMIDI_PACKET_SIZE); + if (in==0xF7) { + packet->buffer[0] = MIX_CN_CIN(cable_number, 0x05); + packet->buffer[1] = 0xF7; + packet->state = PS_END; + break; + } + if (in>=0x80) { /* ??? */ packet->state = PS_END; break; } + packet->buffer[1] = in; + packet->state = PS_EXCL_1; + break; + case PS_EXCL_1: + if (in==0xF7) { + packet->buffer[0] = MIX_CN_CIN(cable_number, 0x06); + packet->buffer[2] = 0xF7; + packet->state = PS_END; + break; + } + if (in>=0x80) { /* ??? */ packet->state = PS_END; break; } + packet->buffer[2] = in; + packet->state = PS_EXCL_2; + break; + case PS_EXCL_2: + if (in==0xF7) { + packet->buffer[0] = MIX_CN_CIN(cable_number, 0x07); + packet->buffer[3] = 0xF7; + packet->state = PS_END; + break; + } + if (in>=0x80) { /* ??? */ packet->state = PS_END; break; } + packet->buffer[0] = MIX_CN_CIN(cable_number, 0x04); + packet->buffer[3] = in; + packet->state = PS_EXCL_0; + break; + default: + printf("umidi: ambiguous state.\n"); + packet->state = PS_INITIAL; + goto retry; + } +} + diff --git a/sys/dev/usb/umidi_quirks.c b/sys/dev/usb/umidi_quirks.c new file mode 100644 index 00000000000..e5b47503246 --- /dev/null +++ b/sys/dev/usb/umidi_quirks.c @@ -0,0 +1,215 @@ +/* $OpenBSD: umidi_quirks.c,v 1.3 2002/06/11 07:49:57 nate Exp $ */ +/* $NetBSD: umidi_quirks.c,v 1.3 2001/11/13 06:24:56 lukem Exp $ */ + +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Takuya SHIOZAKI (tshiozak@netbsd.org). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/ioctl.h> +#include <sys/conf.h> +#include <sys/file.h> +#include <sys/select.h> +#include <sys/proc.h> +#include <sys/vnode.h> +#include <sys/poll.h> +#include <sys/lock.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> + +#include <dev/usb/usbdevs.h> +#include <dev/usb/uaudioreg.h> +#include <dev/usb/umidireg.h> +#include <dev/usb/umidivar.h> +#include <dev/usb/umidi_quirks.h> + +/* + * quirk codes for UMIDI + */ + +#ifdef UMIDIQUIRK_DEBUG +#define DPRINTF(x) if (umidiquirkdebug) printf x +#define DPRINTFN(n,x) if (umidiquirkdebug >= (n)) printf x +int umidiquirkdebug = 1; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + + +/* + * YAMAHA UX-256 + * --- this is a typical yamaha device, but has a broken descriptor :-< + */ + +UMQ_FIXED_EP_DEF(YAMAHA, YAMAHA_UX256, ANYIFACE, 1, 1) = { + /* out */ + { 0, 16 }, + /* in */ + { 1, 8 } +}; + +UMQ_DEF(YAMAHA, YAMAHA_UX256, ANYIFACE) = { + UMQ_FIXED_EP_REG(YAMAHA, YAMAHA_UX256, ANYIFACE), +#if 0 + UMQ_YAMAHA_REG(YAMAHA, ANYPRODUCT, ANYIFACE), +#endif + UMQ_TERMINATOR +}; + + +/* + * YAMAHA generic + */ +UMQ_DEF(YAMAHA, ANYPRODUCT, ANYIFACE) = { + UMQ_YAMAHA_REG(YAMAHA, ANYPRODUCT, ANYIFACE), + UMQ_TERMINATOR +}; + + +/* + * ROLAND UM-1 + */ +UMQ_FIXED_EP_DEF(ROLAND, ROLAND_UM1, 2, 1, 1) = { + /* out */ + { 0, 1 }, + /* in */ + { 1, 1 } +}; + +UMQ_DEF(ROLAND, ROLAND_UM1, 2) = { + UMQ_FIXED_EP_REG(ROLAND, ROLAND_UM1, 2), + UMQ_TERMINATOR +}; + + +/* + * ROLAND UM-880 (native mode) + */ +UMQ_FIXED_EP_DEF(ROLAND, ROLAND_UM880N, 0, 1, 1) = { + /* out */ + { 0, 9 }, + /* in */ + { 1, 9 } +}; + +UMQ_DEF(ROLAND, ROLAND_UM880N, 0) = { + UMQ_FIXED_EP_REG(ROLAND, ROLAND_UM880N, 0), + UMQ_TERMINATOR +}; + + + +/* + * quirk list + */ +struct umidi_quirk umidi_quirklist[] = { + UMQ_REG(YAMAHA, YAMAHA_UX256, ANYIFACE), + UMQ_REG(YAMAHA, ANYPRODUCT, ANYIFACE), + UMQ_REG(ROLAND, ROLAND_UM1, 2), + UMQ_REG(ROLAND, ROLAND_UM880N, 0), + UMQ_TERMINATOR +}; + + +/* + * quirk utilities + */ + +struct umidi_quirk * +umidi_search_quirk(int vendor, int product, int ifaceno) +{ + struct umidi_quirk *p; + struct umq_data *q; + + DPRINTF(("umidi_search_quirk: v=%d, p=%d, i=%d\n", + vendor, product, ifaceno)); + + for (p=&umidi_quirklist[0]; p->vendor; p++) { + DPRINTFN(10, ("\tv=%d, p=%d, i=%d", + p->vendor, p->product, p->iface)); + if ((p->vendor==vendor || p->vendor==ANYVENDOR) && + (p->product==product || p->product==ANYPRODUCT) && + (p->iface==ifaceno || p->iface==ANYIFACE)) { + DPRINTFN(10, (" found\n")); + if (!p->type_mask) + /* make quirk mask */ + for (q=p->quirks; q->type; q++) + p->type_mask |= 1<<(q->type-1); + return p; + } + DPRINTFN(10, ("\n")); + } + + return NULL; +} + +static char *quirk_name[] = { + "NULL", + "Fixed Endpoint", + "Yamaha Specific", +}; + +void +umidi_print_quirk(struct umidi_quirk *q) +{ + struct umq_data *qd; + if (q) { + printf("("); + for (qd=q->quirks; qd->type; qd++) + printf("%s%s", quirk_name[qd->type], + (qd+1)->type?", ":")\n"); + } else { + printf("(genuine USB-MIDI)\n"); + } +} + +void * +umidi_get_quirk_data_from_type(struct umidi_quirk *q, u_int32_t type) +{ + struct umq_data *qd; + if (q) { + for (qd=q->quirks; qd->type; qd++) + if (qd->type == type) + return qd->data; + } + return NULL; +} diff --git a/sys/dev/usb/umidi_quirks.h b/sys/dev/usb/umidi_quirks.h new file mode 100644 index 00000000000..ec676703cef --- /dev/null +++ b/sys/dev/usb/umidi_quirks.h @@ -0,0 +1,120 @@ +/* $OpenBSD: umidi_quirks.h,v 1.3 2002/06/11 07:49:57 nate Exp $ */ +/* $NetBSD: umidi_quirks.h,v 1.2 2001/09/29 22:00:47 tshiozak Exp $ */ + +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Takuya SHIOZAKI (tshiozak@netbsd.org). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +/* + * quirk code for UMIDI + */ + +#ifndef _DEV_USB_UMIDI_QUIRKS_H_ +#define _DEV_USB_UMIDI_QUIRKS_H_ + +struct umq_data { + int type; +#define UMQ_TYPE_FIXED_EP 1 +#define UMQ_TYPE_YAMAHA 2 + void *data; +}; + +struct umidi_quirk { + int vendor; + int product; + int iface; + struct umq_data *quirks; + u_int32_t type_mask; +}; +#define UMQ_ISTYPE(q, type) \ + ((q)->sc_quirk && ((q)->sc_quirk->type_mask & (1<<((type)-1)))) + +#define UMQ_TERMINATOR { 0, } +#define UMQ_DEF(v, p, i) \ +static struct umq_data umq_##v##_##p##_##i[] +#define UMQ_REG(v, p, i) \ + { USB_VENDOR_##v, USB_PRODUCT_##p, i, \ + umq_##v##_##p##_##i, 0 } +#define ANYIFACE -1 +#define ANYVENDOR -1 +#define USB_VENDOR_ANYVENDOR ANYVENDOR +#define ANYPRODUCT -1 +#define USB_PRODUCT_ANYPRODUCT ANYPRODUCT + +/* + * quirk - fixed port + */ + +struct umq_fixed_ep_endpoint { + int ep; + int num_jacks; +}; +struct umq_fixed_ep_desc { + int num_out_ep; + int num_in_ep; + struct umq_fixed_ep_endpoint *out_ep; + struct umq_fixed_ep_endpoint *in_ep; +}; + +#define UMQ_FIXED_EP_DEF(v, p, i, noep, niep) \ +static struct umq_fixed_ep_endpoint \ +umq_##v##_##p##_##i##_fixed_ep_endpoints[noep+niep]; \ +static struct umq_fixed_ep_desc \ +umq_##v##_##p##_##i##_fixed_ep_desc = { \ + noep, niep, \ + &umq_##v##_##p##_##i##_fixed_ep_endpoints[0], \ + &umq_##v##_##p##_##i##_fixed_ep_endpoints[noep], \ +}; \ +static struct umq_fixed_ep_endpoint \ +umq_##v##_##p##_##i##_fixed_ep_endpoints[noep+niep] + +#define UMQ_FIXED_EP_REG(v, p, i) \ +{ UMQ_TYPE_FIXED_EP, (void *)&umq_##v##_##p##_##i##_fixed_ep_desc } + + +/* + * quirk - yamaha style midi I/F + */ +#define UMQ_YAMAHA_REG(v, p, i) \ +{ UMQ_TYPE_YAMAHA, NULL } + + +/* extern struct umidi_quirk umidi_quirklist[]; */ +struct umidi_quirk *umidi_search_quirk(int, int, int); +void umidi_print_quirk(struct umidi_quirk *); +void *umidi_get_quirk_data_from_type(struct umidi_quirk *, u_int32_t); + +#endif diff --git a/sys/dev/usb/umidireg.h b/sys/dev/usb/umidireg.h new file mode 100644 index 00000000000..77bc91ad544 --- /dev/null +++ b/sys/dev/usb/umidireg.h @@ -0,0 +1,80 @@ +/* $OpenBSD: umidireg.h,v 1.3 2002/06/11 07:49:57 nate Exp $ */ +/* $NetBSD: umidireg.h,v 1.2 2001/05/28 20:52:06 tshiozak Exp $ */ +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Takuya SHIOZAKI (tshiozak@netbsd.org). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* Jack Descriptor */ +#define UMIDI_MS_HEADER 0x01 +#define UMIDI_IN_JACK 0x02 +#define UMIDI_OUT_JACK 0x03 + +/* Jack Type */ +#define UMIDI_EMBEDDED 0x01 +#define UMIDI_EXTERNAL 0x02 + +typedef struct { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uWord bcdMSC; + uWord wTotalLength; +} UPACKED umidi_cs_interface_descriptor_t; +#define UMIDI_CS_INTERFACE_DESCRIPTOR_SIZE 7 + +typedef struct { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubType; + uByte bNumEmbMIDIJack; +} UPACKED umidi_cs_endpoint_descriptor_t; +#define UMIDI_CS_ENDPOINT_DESCRIPTOR_SIZE 4 + +typedef struct { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bJackType; + uByte bJackID; +} UPACKED umidi_jack_descriptor_t; +#define UMIDI_JACK_DESCRIPTOR_SIZE 5 + + +#define TO_D(p) ((usb_descriptor_t *)(p)) +#define NEXT_D(desc) TO_D((caddr_t)(desc)+(desc)->bLength) +#define TO_IFD(desc) ((usb_interface_descriptor_t *)(desc)) +#define TO_CSIFD(desc) ((umidi_cs_interface_descriptor_t *)(desc)) +#define TO_EPD(desc) ((usb_endpoint_descriptor_t *)(desc)) +#define TO_CSEPD(desc) ((umidi_cs_endpoint_descriptor_t *)(desc)) diff --git a/sys/dev/usb/umidivar.h b/sys/dev/usb/umidivar.h new file mode 100644 index 00000000000..7d471b20b5c --- /dev/null +++ b/sys/dev/usb/umidivar.h @@ -0,0 +1,138 @@ +/* $OpenBSD: umidivar.h,v 1.3 2002/06/11 07:49:57 nate Exp $ */ +/* $NetBSD: umidivar.h,v 1.3 2001/02/03 18:50:32 tshiozak Exp $ */ +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Takuya SHIOZAKI (tshiozak@netbsd.org). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* pending MUX-MIDI packet */ +typedef enum { + PS_EXCL_0=-2, /* put, and next state is PS_EXCL_0 */ + PS_END=-1, /* put, and next state is PS_INITIAL */ + PS_INITIAL=0, /* 0>= : not put, and state is keeped */ + PS_NORMAL_1OF3=1, + PS_NORMAL_2OF3=2, + PS_NORMAL_1OF2=3, + PS_EXCL_1=4, + PS_EXCL_2=5, +} packet_state_t; + +#define UMIDI_PACKET_SIZE 4 +struct umidi_packet { + char buffer[UMIDI_PACKET_SIZE]; + packet_state_t state; +}; + +/* + * hierarchie + * + * <-- parent child --> + * + * umidi(sc) -> endpoint -> jack <- (dynamically assignable) - mididev + * ^ | ^ | + * +-----+ +-----+ + */ + +/* midi device */ +struct umidi_mididev { + struct umidi_softc *sc; + struct device *mdev; + /* */ + struct umidi_jack *in_jack; + struct umidi_jack *out_jack; + /* */ + int opened; + int flags; +}; + +/* Jack Information */ +struct umidi_jack { + struct umidi_endpoint *endpoint; + /* */ + int cable_number; + struct umidi_packet packet; + void *arg; + int binded; + int opened; + union { + struct { + void (*intr)(void *); + LIST_ENTRY(umidi_jack) queue_entry; + } out; + struct { + void (*intr)(void *, int); + } in; + } u; +}; + +#define UMIDI_MAX_EPJACKS 16 +/* endpoint data */ +struct umidi_endpoint { + struct umidi_softc *sc; + /* */ + int addr; + usbd_pipe_handle pipe; + usbd_xfer_handle xfer; + char *buffer; + int num_open; + int num_jacks; + struct umidi_jack *jacks[UMIDI_MAX_EPJACKS]; + LIST_HEAD(, umidi_jack) queue_head; + struct umidi_jack *queue_tail; +}; + +/* software context */ +struct umidi_softc { + USBBASEDEVICE sc_dev; + usbd_device_handle sc_udev; + usbd_interface_handle sc_iface; + struct umidi_quirk *sc_quirk; + + int sc_dying; + + int sc_out_num_jacks; + struct umidi_jack *sc_out_jacks; + int sc_in_num_jacks; + struct umidi_jack *sc_in_jacks; + struct umidi_jack *sc_jacks; + + int sc_num_mididevs; + struct umidi_mididev *sc_mididevs; + + int sc_out_num_endpoints; + struct umidi_endpoint *sc_out_ep; + int sc_in_num_endpoints; + struct umidi_endpoint *sc_in_ep; + struct umidi_endpoint *sc_endpoints; +}; |