summaryrefslogtreecommitdiff
path: root/kernel/drv/oss_usb/ossusb_midisport.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drv/oss_usb/ossusb_midisport.c')
-rw-r--r--kernel/drv/oss_usb/ossusb_midisport.c957
1 files changed, 957 insertions, 0 deletions
diff --git a/kernel/drv/oss_usb/ossusb_midisport.c b/kernel/drv/oss_usb/ossusb_midisport.c
new file mode 100644
index 0000000..bbeee14
--- /dev/null
+++ b/kernel/drv/oss_usb/ossusb_midisport.c
@@ -0,0 +1,957 @@
+/*
+ * Purpose: Dedicated driver for M-Audio/Midiman MIDISPORT USB MIDI family
+ */
+
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+#include "oss_config.h"
+#include "midi_core.h"
+#include "ossusb.h"
+#include "midisport2x2_fw.h"
+#include "midisport1x1_fw.h"
+#include "oxygen8_fw.h"
+
+/*
+ * Audiosport Quattro also use the same packet protocol (no FW load).
+ * Out EP 2 is the output and in EP 1 is the input (check this).
+ */
+#define RECBUF_SIZE (10*4)
+#define DLBUF_SIZE 63
+#define MAX_PACK 32
+#define PLAYBUF_SIZE (MAX_PACK*4)
+#define QUEUE_ENTRIES 17
+#define QUEUE_SIZE (QUEUE_ENTRIES*PLAYBUF_SIZE)
+#define MAX_PORTS 9
+
+static int instance_num = 0;
+
+typedef struct midisport_devc midisport_devc;
+
+typedef unsigned char playbuf_t[PLAYBUF_SIZE];
+
+typedef struct
+{
+ oss_mutex_t mutex;
+ midisport_devc *devc;
+ int open_count;
+ int busy;
+ int max_out_blocks; /* Num of 4(3) byte blocks allowed in single message */
+
+ playbuf_t *playbuf; /* QUEUE_SIZE */
+ oss_dma_handle_t playbuf_dma_handle;
+ int playbuf_h, playbuf_t;
+
+ unsigned char *out_ep_desc;
+ udi_usb_request_t *output_pipe;
+ udi_endpoint_handle_t *output_endpoint_handle;
+} midisport_outqueue_t;
+
+typedef struct
+{
+ midisport_devc *devc;
+ oss_device_t *osdev;
+ oss_mutex_t mutex;
+
+ int portnum;
+ int midi_dev;
+ int open_mode;
+ oss_midi_inputbyte_t midi_input_intr;
+ playbuf_t output_buf;
+ int outbuf_p;
+
+ int outqueue_ix; /* Output queue 0 or 1 */
+ oss_midi_outputintr_t outputintr;
+} midisport_midic;
+
+static int alphabethic_numbering = 1;
+
+struct midisport_devc
+{
+ special_unload_t unload_func;
+ int is_dummy;
+ oss_device_t *osdev;
+ oss_mutex_t mutex;
+ ossusb_devc *usb_devc;
+ udi_usb_devc *usbdev;
+
+ int instance_num;
+
+ unsigned char *in_ep_desc;
+
+ int num_inputs, num_outputs;
+ int open_inputs;
+ midisport_midic in_midic[MAX_PORTS];
+ midisport_midic out_midic[MAX_PORTS];
+
+ unsigned char *recbuf;
+ oss_dma_handle_t recbuf_dma_handle;
+ udi_usb_request_t *input_pipe;
+ udi_endpoint_handle_t *input_endpoint_handle;
+
+ int num_queues;
+ midisport_outqueue_t out_queues[2];
+};
+
+static void
+load_firmware (midisport_devc * devc, const struct setup_request *setup,
+ char *name)
+{
+ int err;
+
+ if (setup == NULL)
+ {
+ cmn_err (CE_WARN, "midisport: No firmware available\n");
+ return;
+ }
+
+ while (setup->pData != NULL)
+ {
+#if 0
+ cmn_err (CE_CONT, "Load(%x, Rq=%x, Rqt=%x, Val=%x, Ix=%x, data=%x, len=%d\n", devc->usbdev, setup->bRequest, /* Request */
+ setup->bmRequestType, /* Rqtype */
+ setup->wValue, /* Value */
+ setup->wIndex, /* Index */
+ setup->pData, /* Data */
+ setup->wLength); /* Len */
+#endif
+
+ err = udi_usb_snd_control_msg (devc->usbdev, 0, /* Endpoint */
+ setup->bRequest, /* Request */
+ setup->bmRequestType, /* Rqtype */
+ setup->wValue, /* Value */
+ setup->wIndex, /* Index */
+ (void *) setup->pData, /* Data */
+ setup->wLength, /* Len */
+ OSS_HZ);
+ if (err < 0)
+ {
+ cmn_err (CE_WARN, "%s: Firmware download failed (%d)\n", name, err);
+ return;
+ }
+
+ setup++;
+ }
+}
+
+static void
+midisport_unload (void *d)
+{
+ midisport_devc *devc = d;
+
+ if (devc->is_dummy)
+ {
+ return;
+ }
+
+ MUTEX_CLEANUP (devc->mutex);
+}
+
+void *
+midisport_init (ossusb_devc * usb_devc)
+{
+ midisport_devc *devc;
+ unsigned int devid;
+ char *name = "Unknown device";
+ const struct setup_request *setup = NULL;
+
+ devid = (usb_devc->vendor << 16) | usb_devc->product;
+
+ switch (devid)
+ {
+ case 0x07631001:
+ name = "MIDISPORT 2x2";
+ setup = midisport2x2_setupRequest;
+ break;
+ case 0x07631014:
+ name = "Oxygen8";
+ setup = oxygen8_setupRequest;
+ break;
+ case 0x07631010:
+ name = "MIDISPORT 1x1";
+ setup = midisport1x1_setupRequest;
+ break;
+ }
+
+ cmn_err (CE_CONT, "%s firmware load started\n", name);
+
+ if ((devc = PMALLOC (usb_devc->osdev, sizeof (*devc))) == NULL)
+ {
+ cmn_err (CE_WARN, "midisport: Out of memory\n");
+ return NULL;
+ }
+
+ memset (devc, 0, sizeof (*devc));
+
+ devc->unload_func = midisport_unload;
+ devc->is_dummy = 1;
+ devc->osdev = usb_devc->osdev;
+ devc->usb_devc = usb_devc;
+ devc->usbdev = usb_devc->last_usbdev;
+
+ load_firmware (devc, setup, name);
+
+ cmn_err (CE_CONT, "%s firmware load completed\n", name);
+ return devc;
+}
+
+static int midisport_start_input (midisport_devc * devc);
+
+static void
+record_callback (udi_usb_request_t * request, void *arg)
+{
+ midisport_devc *devc = arg;
+ int i, l, p;
+ unsigned char *data;
+ data = udi_usb_request_actdata (request);
+
+ l = udi_usb_request_actlen (request);
+ if (l == 0)
+ goto restart;
+
+ for (p = 0; p < l - 3; p += 4)
+ {
+ unsigned char *buf, cmd;
+ int nbytes, src;
+ midisport_midic *midic;
+
+ buf = data + p;
+ cmd = buf[3];
+ nbytes = cmd & 0x0f;
+ src = (cmd >> 4) & 0x0f;
+
+ if (nbytes == 0) /* End of data */
+ break;
+
+ if (nbytes > 3 || src >= devc->num_inputs)
+ continue; /* No data or error */
+
+ midic = &devc->in_midic[src];
+
+ if (!(midic->open_mode & OPEN_READ) || midic->midi_input_intr == NULL)
+ continue; /* This device is not recording */
+
+ for (i = 0; i < nbytes; i++)
+ {
+ midic->midi_input_intr (midic->midi_dev, buf[i]);
+ }
+ }
+
+restart:
+ midisport_start_input (devc);
+}
+
+static int
+midisport_start_input (midisport_devc * devc)
+{
+ oss_native_word flags;
+ int err = 0;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+
+ if ((err =
+ udi_usb_submit_request (devc->input_pipe, record_callback, devc,
+ devc->input_endpoint_handle,
+ UDI_USBXFER_INTR_READ, devc->recbuf,
+ RECBUF_SIZE)) < 0)
+ {
+ cmn_err (CE_WARN, "udi_usb_submit_request failed, err=%d\n", err);
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ return err;
+}
+
+static void submit_output (midisport_devc * devc, midisport_outqueue_t * q);
+
+ /*ARGSUSED*/ static void
+play_callback (udi_usb_request_t * request, void *arg)
+{
+ midisport_outqueue_t *q = arg;
+ midisport_devc *devc = q->devc;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (q->mutex, flags);
+ q->busy = 0;
+ submit_output (devc, q);
+ MUTEX_EXIT_IRQRESTORE (q->mutex, flags);
+}
+
+ /*ARGSUSED*/ static void
+submit_output (midisport_devc * devc, midisport_outqueue_t * q)
+{
+ int err;
+ int max_bytes;
+ unsigned char *qbuf;
+
+ if (q->busy)
+ return;
+
+ if (q->playbuf_h == q->playbuf_t) /* Queue empty */
+ return;
+
+ qbuf = q->playbuf[q->playbuf_h];
+ max_bytes = q->max_out_blocks * 4;
+
+ q->playbuf_h = (q->playbuf_h + 1) % QUEUE_ENTRIES;
+ q->busy = 1;
+
+ if ((err =
+ udi_usb_submit_request (q->output_pipe, play_callback, q,
+ q->output_endpoint_handle,
+ UDI_USBXFER_BULK_WRITE, qbuf, max_bytes)) < 0)
+ {
+ cmn_err (CE_WARN, "udi_usb_submit_request (play) failed, err=%d\n",
+ err);
+ }
+}
+
+ /*ARGSUSED*/ static void
+midisport_close_input (int dev, int mode)
+{
+ oss_native_word flags;
+ midisport_midic *midic;
+ midisport_devc *devc;
+ int do_stop = 0;
+
+ midic = midi_devs[dev]->devc;
+ devc = midic->devc;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ devc->open_inputs--;
+ if (devc->open_inputs == 0)
+ do_stop = 1;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ if (do_stop)
+ {
+ udi_usb_free_request (devc->input_pipe);
+ udi_close_endpoint (devc->input_endpoint_handle);
+ if (devc->recbuf != NULL)
+ CONTIG_FREE (midic->osdev, devc->recbuf, RECBUF_SIZE, devc->recbuf_dma_handle);
+ }
+
+ MUTEX_ENTER_IRQDISABLE (midic->mutex, flags);
+ midic->open_mode = 0;
+ midic->midi_input_intr = NULL;
+ MUTEX_EXIT_IRQRESTORE (midic->mutex, flags);
+}
+
+ /*ARGSUSED*/ static int
+midisport_open_input (int dev, int mode, oss_midi_inputbyte_t inputbyte,
+ oss_midi_inputbuf_t inputbuf,
+ oss_midi_outputintr_t outputintr)
+{
+ oss_native_word flags;
+ midisport_midic *midic;
+ midisport_devc *devc;
+ oss_native_word phaddr;
+ int do_start = 0;
+
+ midic = midi_devs[dev]->devc;
+ devc = midic->devc;
+
+ MUTEX_ENTER_IRQDISABLE (midic->mutex, flags);
+ if (midic->open_mode)
+ {
+ MUTEX_EXIT_IRQRESTORE (midic->mutex, flags);
+ return OSS_EBUSY;
+ }
+
+ midic->open_mode = mode;
+ midic->midi_input_intr = inputbyte;
+ MUTEX_EXIT_IRQRESTORE (midic->mutex, flags);
+
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ devc->open_inputs++;
+ if (devc->open_inputs == 1)
+ do_start = 1;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ if (do_start)
+ {
+ int err;
+
+ devc->recbuf =
+ CONTIG_MALLOC (devc->osdev, RECBUF_SIZE, MEMLIMIT_32BITS, &phaddr, devc->recbuf_dma_handle);
+ if (devc->recbuf == NULL)
+ {
+ cmn_err (CE_CONT, "Failed to allocate the recording buffer\n");
+ return OSS_ENOMEM;
+ }
+
+ if ((devc->input_endpoint_handle =
+ udi_open_endpoint (devc->usbdev, devc->in_ep_desc)) == NULL)
+ {
+ midic->open_mode = 0;
+ midic->midi_input_intr = NULL;
+ cmn_err (CE_WARN, "Cannot open audio pipe\n");
+ return OSS_ENOMEM;
+ }
+ if ((devc->input_pipe =
+ udi_usb_alloc_request (devc->usbdev, devc->input_endpoint_handle,
+ 1, UDI_USBXFER_INTR_READ)) == NULL)
+ {
+ return OSS_EIO;
+ }
+
+ if ((err = midisport_start_input (devc)) < 0)
+ {
+ cmn_err (CE_WARN, "midisport: Input error %d\n", err);
+ midisport_close_input (dev, mode);
+ return OSS_EIO;
+ }
+ }
+
+ return 0;
+}
+
+static int
+open_output_queue (midisport_devc * devc, int queue_ix)
+{
+ oss_native_word flags;
+ midisport_outqueue_t *q;
+ int open_count;
+ oss_native_word phaddr;
+
+ if (queue_ix < 0 || queue_ix >= devc->num_queues)
+ {
+ cmn_err (CE_WARN, "Bad output queue index %d\n", queue_ix);
+ return OSS_EIO;
+ }
+
+ q = &devc->out_queues[queue_ix];
+
+ MUTEX_ENTER_IRQDISABLE (q->mutex, flags);
+ open_count = q->open_count++;
+
+ if (open_count == 0) /* First open */
+ {
+ q->playbuf_h = q->playbuf_t = 0;
+ q->busy = 0;
+ }
+ MUTEX_EXIT_IRQRESTORE (q->mutex, flags);
+
+ if (open_count == 0) /* First open */
+ {
+ if ((q->playbuf =
+ CONTIG_MALLOC (devc->osdev, QUEUE_SIZE, MEMLIMIT_32BITS,
+ &phaddr, q->playbuf_dma_handle)) == NULL)
+ {
+ cmn_err (CE_WARN, "Failed to allocate output buffer (%d bytes)\n",
+ QUEUE_SIZE);
+ q->open_count--;
+ return OSS_ENOMEM;
+ }
+
+ if ((q->output_endpoint_handle =
+ udi_open_endpoint (devc->usbdev, q->out_ep_desc)) == NULL)
+ {
+ cmn_err (CE_WARN, "Failed to open output endpoint\n");
+ q->open_count--;
+ return OSS_EIO;
+ }
+
+ if ((q->output_pipe =
+ udi_usb_alloc_request (devc->usbdev, q->output_endpoint_handle, 1,
+ UDI_USBXFER_BULK_WRITE)) == NULL)
+ {
+ cmn_err (CE_WARN, "Failed to allocate output request\n");
+ q->open_count--;
+ return OSS_EIO;
+ }
+
+ }
+ return 0;
+}
+
+static void
+close_output_queue (midisport_devc * devc, int queue_ix)
+{
+ oss_native_word flags;
+ midisport_outqueue_t *q;
+ int open_count;
+
+ if (queue_ix < 0 || queue_ix >= devc->num_queues)
+ {
+ cmn_err (CE_WARN, "Bad output queue index %d\n", queue_ix);
+ return;
+ }
+
+ q = &devc->out_queues[queue_ix];
+
+ if (q->open_count <= 0) /* Was not opened at all */
+ return;
+
+ MUTEX_ENTER_IRQDISABLE (q->mutex, flags);
+ open_count = q->open_count--;
+ MUTEX_EXIT_IRQRESTORE (q->mutex, flags);
+
+ if (open_count <= 1) /* Queue not needed any more */
+ {
+ udi_usb_free_request (q->output_pipe);
+ udi_close_endpoint (q->output_endpoint_handle);
+ if (q->playbuf != NULL)
+ CONTIG_FREE (devc->osdev, q->playbuf, QUEUE_SIZE, q->playbuf_dma_handle);
+ }
+}
+
+ /*ARGSUSED*/ static void
+midisport_close_output (int dev, int mode)
+{
+ oss_native_word flags;
+ midisport_midic *midic;
+ midisport_devc *devc;
+
+ midic = midi_devs[dev]->devc;
+ devc = midic->devc;
+
+ close_output_queue (devc, midic->outqueue_ix);
+
+ MUTEX_ENTER_IRQDISABLE (midic->mutex, flags);
+ midic->open_mode = 0;
+ midic->outputintr = NULL;
+ MUTEX_EXIT_IRQRESTORE (midic->mutex, flags);
+}
+
+ /*ARGSUSED*/ static int
+midisport_open_output (int dev, int mode, oss_midi_inputbyte_t inputbyte,
+ oss_midi_inputbuf_t inputbuf,
+ oss_midi_outputintr_t outputintr)
+{
+ oss_native_word flags;
+ midisport_midic *midic;
+ midisport_devc *devc;
+ int err;
+
+ midic = midi_devs[dev]->devc;
+ devc = midic->devc;
+
+ MUTEX_ENTER_IRQDISABLE (midic->mutex, flags);
+ if (midic->open_mode)
+ {
+ MUTEX_EXIT_IRQRESTORE (midic->mutex, flags);
+ return OSS_EBUSY;
+ }
+
+ midic->open_mode = mode;
+ midic->midi_input_intr = NULL, midic->outputintr = outputintr;
+ midic->outbuf_p = 0;
+ memset (midic->output_buf, 0, sizeof (midic->output_buf));
+ MUTEX_EXIT_IRQRESTORE (midic->mutex, flags);
+
+ if ((err = open_output_queue (devc, midic->outqueue_ix)) < 0)
+ {
+ cmn_err (CE_WARN, "Failed to open the output queue (%d)\n", err);
+ midisport_close_output (dev, mode);
+ return err;
+ }
+
+ return 0;
+}
+
+static int
+do_flush (midisport_devc * devc, midisport_midic * midic,
+ midisport_outqueue_t * q)
+{
+ int next;
+ unsigned char *qbuf;
+ int max_bytes;
+
+ /*
+ * Move stuff from the intermediate buffer to the EP queue
+ */
+ max_bytes = q->max_out_blocks * 4;
+ next = (q->playbuf_t + 1) % QUEUE_ENTRIES;
+ if (next == q->playbuf_h) /* No more space in any of the buffers */
+ {
+ return 0;
+ }
+
+ qbuf = q->playbuf[q->playbuf_t];
+ memcpy (qbuf, midic->output_buf, max_bytes);
+ memset (midic->output_buf, 0, max_bytes);
+ midic->outbuf_p = 0;
+ q->playbuf_t = next;
+
+ submit_output (devc, q);
+
+ return 1;
+}
+
+#if 0
+static void
+midisport_flush_output (int dev)
+{
+ midisport_midic *midic = midi_devs[dev]->devc;
+ midisport_devc *devc = midic->devc;
+ midisport_outqueue_t *q;
+ oss_native_word flags;
+ oss_native_word qflags;
+
+ q = &devc->out_queues[midic->outqueue_ix];
+
+ MUTEX_ENTER_IRQDISABLE (midic->mutex, flags);
+ MUTEX_ENTER_IRQDISABLE (q->mutex, qflags);
+ do_flush (devc, midic, q);
+ MUTEX_EXIT_IRQRESTORE (q->mutex, qflags);
+ MUTEX_EXIT_IRQRESTORE (midic->mutex, flags);
+}
+#endif
+
+static int
+midisport_bulk_write (int dev, unsigned char *buf, int len)
+{
+ midisport_midic *midic = midi_devs[dev]->devc;
+ midisport_devc *devc = midic->devc;
+ oss_native_word flags;
+ oss_native_word qflags;
+ int i, l = 0, n = 0, p;
+ //int max_bytes;
+ midisport_outqueue_t *q;
+ unsigned char *outbuf;
+
+ if (midic->outqueue_ix < 0 || midic->outqueue_ix >= devc->num_queues)
+ {
+ cmn_err (CE_WARN, "Bad output queue index %d\n", midic->outqueue_ix);
+ return OSS_EIO;
+ }
+
+ q = &devc->out_queues[midic->outqueue_ix];
+
+ MUTEX_ENTER_IRQDISABLE (midic->mutex, flags);
+
+ //max_bytes = q->max_out_blocks * 4;
+
+ for (i = 0; i < len; i += 3)
+ {
+ l = len - i;
+ if (l > 3)
+ l = 3;
+ p = midic->outbuf_p;
+
+ if ((p + 4) >= q->max_out_blocks)
+ {
+ int next;
+ MUTEX_ENTER_IRQDISABLE (q->mutex, qflags);
+
+ next = (q->playbuf_t + 1) % QUEUE_ENTRIES;
+ if (next == q->playbuf_h) /* No more space in any of the buffers */
+ {
+ MUTEX_EXIT_IRQRESTORE (q->mutex, qflags);
+ MUTEX_EXIT_IRQRESTORE (midic->mutex, flags);
+ return n;
+ }
+
+ if (!do_flush (devc, midic, q)) /* Output FIFO full */
+ {
+ MUTEX_EXIT_IRQRESTORE (q->mutex, qflags);
+ return n;
+ }
+ p = midic->outbuf_p;
+ MUTEX_EXIT_IRQRESTORE (q->mutex, qflags);
+ }
+
+ outbuf = midic->output_buf + p;
+ memcpy (outbuf, buf, l);
+ outbuf[3] = (midic->portnum << 4) | l;
+ midic->outbuf_p += 4;
+ if (midic->outbuf_p >= q->max_out_blocks)
+ do_flush (devc, midic, q);
+ n += l;
+ }
+
+ MUTEX_EXIT_IRQRESTORE (midic->mutex, flags);
+
+ return n;
+}
+
+ /*ARGSUSED*/ static int
+midisport_ioctl (int dev, unsigned cmd, ioctl_arg arg)
+{
+ return OSS_EINVAL;
+}
+
+static midi_driver_t midisport_midi_input_driver = {
+ midisport_open_input,
+ midisport_close_input,
+ midisport_ioctl
+};
+
+static midi_driver_t midisport_midi_output_driver = {
+ midisport_open_output,
+ midisport_close_output,
+ midisport_ioctl,
+ NULL,
+ midisport_bulk_write,
+ 8 * 3 /* 8 packets of 3 bytes */
+};
+
+static void
+create_inputs (midisport_devc * devc, char *name, int ep, int ninputs)
+{
+ int desc_len;
+ int i, ix = ep;
+ char portid = 'A';
+ unsigned char *desc;
+ int flags = MFLAG_INPUT;
+
+ for (i = 0; i < 32; i++)
+ if ((desc =
+ udi_usbdev_get_endpoint (devc->usbdev, 0, i, &desc_len)) != NULL)
+ {
+ if (desc[2] == (ep | 0x80))
+ ix = i;
+ }
+
+ if (!alphabethic_numbering)
+ portid = '1';
+
+ if (ninputs > MAX_PORTS)
+ {
+ cmn_err (CE_WARN, "Too many input ports %d\n", ninputs);
+ return;
+ }
+
+ if ((devc->in_ep_desc =
+ udi_usbdev_get_endpoint (devc->usbdev, 0, ix, &desc_len)) == NULL)
+ {
+ cmn_err (CE_WARN, "Bad endpoint %d\n", ep);
+ return;
+ }
+
+ if (!(devc->in_ep_desc[2] & 0x80))
+ {
+ cmn_err (CE_WARN, "Bad endpoint %d - not input\n", ep);
+ }
+
+ for (i = 0; i < ninputs; i++)
+ {
+ midisport_midic *midic = midic = &devc->in_midic[i];
+ char tmp[128];
+
+ midic->devc = devc;
+ midic->osdev = devc->osdev;
+ MUTEX_INIT (devc->osdev, midic->mutex, MH_DRV + 1);
+ midic->portnum = i;
+ devc->num_inputs++;
+
+ if (i == 8)
+ {
+ sprintf (tmp, "%s SMPTE status", name);
+ flags = MFLAG_MTC;
+ }
+ else
+ sprintf (tmp, "%s input %c", name, portid + i);
+
+ midic->midi_dev =
+ oss_install_mididev (OSS_MIDI_DRIVER_VERSION, "MIDISPORT", tmp,
+ &midisport_midi_input_driver,
+ sizeof (midi_driver_t),
+ flags, midic, midic->osdev);
+ }
+}
+
+static void
+create_output (midisport_devc * devc, char *name, int queue_ix)
+{
+ int n;
+ midisport_midic *midic = NULL;
+ char tmp[128];
+ char portid = 'A';
+ int flags = MFLAG_OUTPUT;
+
+ if (!alphabethic_numbering)
+ portid = '1';
+
+ n = devc->num_outputs++;
+ midic = &devc->out_midic[n];
+ midic->devc = devc;
+ MUTEX_INIT (devc->osdev, midic->mutex, MH_DRV + 1);
+ midic->osdev = devc->osdev;
+ midic->portnum = n;
+ midic->outqueue_ix = queue_ix;
+
+ if (n == 8)
+ {
+ sprintf (tmp, "%s SMPTE control", name);
+ flags = MFLAG_MTC;
+ }
+ else
+ sprintf (tmp, "%s output %c", name, portid + n);
+
+ midic->midi_dev =
+ oss_install_mididev (OSS_MIDI_DRIVER_VERSION, "MIDISPORT", tmp,
+ &midisport_midi_output_driver,
+ sizeof (midi_driver_t),
+ flags, midic, midic->osdev);
+}
+
+static void
+init_outqueue (midisport_devc * devc, int ix, int ep, int max_blocks)
+{
+ int desc_len;
+ midisport_outqueue_t *q;
+ int i, epix = ep;
+
+ unsigned char *desc;
+
+ for (i = 0; i < 32; i++)
+ if ((desc =
+ udi_usbdev_get_endpoint (devc->usbdev, 0, i, &desc_len)) != NULL)
+ {
+ if (desc[2] == ep)
+ epix = i;
+ }
+
+ if (ix < 0 || ix > devc->num_queues)
+ {
+ cmn_err (CE_WARN, "Endpoint index outside bounds\n");
+ return;
+ }
+
+ q = &devc->out_queues[ix];
+ MUTEX_INIT (devc->osdev, q->mutex, MH_DRV + 2);
+ q->max_out_blocks = max_blocks;
+ q->devc = devc;
+
+ if ((q->out_ep_desc =
+ udi_usbdev_get_endpoint (devc->usbdev, 0, epix, &desc_len)) == NULL)
+ {
+ cmn_err (CE_WARN, "Bad endpoint %d\n", ep);
+ return;
+ }
+
+ if (q->out_ep_desc[2] & 0x80)
+ {
+ cmn_err (CE_WARN, "Bad endpoint %d - not output\n", ep);
+ }
+ cmn_err (CE_CONT, "Attaching output endpoint %d=%02x\n", ep,
+ q->out_ep_desc[2]);
+}
+
+void *
+midisport_driver (ossusb_devc * usb_devc)
+{
+ midisport_devc *devc;
+ char *name;
+ unsigned int devid;
+ int i;
+ unsigned char *desc;
+ int desc_len;
+
+ devid = (usb_devc->vendor << 16) | usb_devc->product;
+
+ if ((devc = PMALLOC (usb_devc->osdev, sizeof (*devc))) == NULL)
+ {
+ cmn_err (CE_WARN, "midisport: Out of memory\n");
+ return NULL;
+ }
+
+ memset (devc, 0, sizeof (*devc));
+
+ devc->unload_func = midisport_unload;
+ devc->osdev = usb_devc->osdev;
+ MUTEX_INIT (devc->osdev, devc->mutex, MH_DRV);
+
+ devc->usb_devc = usb_devc;
+ devc->usbdev = usb_devc->last_usbdev;
+ devc->instance_num = instance_num++;
+
+ for (i = 0; i < 32; i++)
+ if ((desc =
+ udi_usbdev_get_endpoint (devc->usbdev, 0, i, &desc_len)) != NULL)
+ {
+ if (desc[2] & 0x80)
+ {
+ cmn_err (CE_CONT, "Input endpoint %02x\n", desc[2]);
+ }
+ else
+ {
+ cmn_err (CE_CONT, "Output endpoint %02x\n", desc[2]);
+ }
+ }
+
+ alphabethic_numbering = 1;
+
+ devc->num_queues = 2;
+
+ switch (devid)
+ {
+ case 0x07631002:
+ name = "Midisport 2x2";
+ create_inputs (devc, name, 1, 2);
+ init_outqueue (devc, 0, 2, 8);
+ init_outqueue (devc, 1, 4, 8);
+ create_output (devc, name, 0);
+ create_output (devc, name, 1);
+ break;
+
+ case 0x07631011:
+ name = "Midisport 1x1";
+ devc->num_queues = 1;
+ init_outqueue (devc, 0, 2, 8);
+ create_inputs (devc, name, 1, 1);
+ create_output (devc, name, 0);
+ break;
+
+ case 0x07631015:
+ name = "Oxygen8";
+ devc->num_queues = 1;
+ create_inputs (devc, name, 1, 1);
+ init_outqueue (devc, 0, 2, 8);
+ create_output (devc, name, 0);
+ break;
+
+ case 0x07632001:
+ name = "Quattro";
+ devc->num_queues = 1;
+ create_inputs (devc, name, 1, 1);
+ init_outqueue (devc, 0, 2, 8);
+ create_output (devc, name, 0);
+ break;
+
+ case 0x07631031:
+ name = "Midisport 8x8";
+ alphabethic_numbering = 0;
+ create_inputs (devc, name, 2, 9);
+ init_outqueue (devc, 0, 2, 10);
+ init_outqueue (devc, 1, 4, 8);
+ create_output (devc, name, 0);
+ create_output (devc, name, 1);
+ create_output (devc, name, 0);
+ create_output (devc, name, 1);
+ create_output (devc, name, 0);
+ create_output (devc, name, 1);
+ create_output (devc, name, 0);
+ create_output (devc, name, 1);
+ create_output (devc, name, 0); /* SMPTE control */
+ break;
+
+ case 0x07631021:
+ name = "Midisport 4x4";
+ create_inputs (devc, name, 2, 4);
+ init_outqueue (devc, 0, 2, 16);
+ init_outqueue (devc, 1, 4, 16);
+ create_output (devc, name, 0);
+ create_output (devc, name, 1);
+ create_output (devc, name, 0);
+ create_output (devc, name, 1);
+ break;
+
+ default:
+ cmn_err (CE_WARN, "Unrecognized MIDI device %08x\n", devid);
+ }
+
+ return devc;
+}