summaryrefslogtreecommitdiff
path: root/sys/dev/usb
diff options
context:
space:
mode:
authorNathan Binkert <nate@cvs.openbsd.org>2002-06-11 07:49:58 +0000
committerNathan Binkert <nate@cvs.openbsd.org>2002-06-11 07:49:58 +0000
commit98e4ab5b7e693843e950e4646d2cf2ceb6f0ec6d (patch)
treed0c28e54fafb2246687ab66af63ff8d4fb2f1f7c /sys/dev/usb
parent6d7b8f565e84481bd01959fb74f45e5e45d86dac (diff)
add usb midi support.
untested From NetBSD
Diffstat (limited to 'sys/dev/usb')
-rw-r--r--sys/dev/usb/files.usb8
-rw-r--r--sys/dev/usb/umidi.c1376
-rw-r--r--sys/dev/usb/umidi_quirks.c215
-rw-r--r--sys/dev/usb/umidi_quirks.h120
-rw-r--r--sys/dev/usb/umidireg.h80
-rw-r--r--sys/dev/usb/umidivar.h138
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;
+};