Open Sound System |
Do you have problems with sound/audio application development? Don't panic! Click here for help! |
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 "midiparser.h" typedef struct ymhusb_devc ymhusb_devc; #define MAX_INDEVS 1 #define MAX_OUTDEVS 1 #define TMPBUF_SIZE 64 #define TMPBUF_NSLOTS 24 typedef unsigned char tmpbuf_slot_t[TMPBUF_SIZE]; #define OUTBUF_SIZE (TMPBUF_NSLOTS*TMPBUF_SIZE) typedef struct { ymhusb_devc *devc; oss_device_t *osdev; udi_usb_devc *usbdev; oss_mutex_t mutex; int portnum; int midi_dev; int open_mode; void *endpoint_desc; udi_endpoint_handle_t *endpoint_handle; udi_usb_request_t *datapipe; oss_midi_inputbyte_t midi_input_intr; unsigned char *tmpbuf; oss_dma_handle_t tmpbuf_dma_handle; int tmp_len; midiparser_common_p parser; volatile int output_busy; /* Output buffer */ tmpbuf_slot_t *outbuf; oss_dma_handle_t outbuf_dma_handle; int buf_t, buf_h; } ymhusb_midic; struct ymhusb_devc { special_unload_t unload_func; oss_device_t *osdev; udi_usb_devc *usbdev; oss_mutex_t mutex; int n_inputs, n_outputs; ymhusb_midic indevs[MAX_INDEVS], outdevs[MAX_OUTDEVS]; }; static void ymhusb_unload (void *d) { ymhusb_devc *devc = (ymhusb_devc *) d; int i; for (i = 0; i < devc->n_inputs; i++) { MUTEX_CLEANUP (devc->indevs[i].mutex); } for (i = 0; i < devc->n_outputs; i++) { MUTEX_CLEANUP (devc->outdevs[i].mutex); } MUTEX_CLEANUP (devc->mutex); } static int ymhmidi_start_input (ymhusb_devc * devc, ymhusb_midic * midic); void ymhmidi_record_callback (udi_usb_request_t * request, void *arg) { ymhusb_midic *midic = arg; ymhusb_devc *devc = midic->devc; unsigned char *data; int l; l = udi_usb_request_actlen (request); data = udi_usb_request_actdata (request); if (l == 0) goto restart; if (midi_devs[midic->midi_dev]->event_input != NULL) midi_devs[midic->midi_dev]->event_input (midic->midi_dev, data, l); restart: if (midic->open_mode & OPEN_READ) ymhmidi_start_input (devc, midic); } static int ymhmidi_submit_output (ymhusb_devc * devc, ymhusb_midic * midic); /*ARGSUSED*/ void ymhmidi_play_callback (udi_usb_request_t * request, void *arg) { ymhusb_midic *midic = arg; ymhusb_devc *devc = midic->devc; midic->output_busy = 0; ymhmidi_submit_output (devc, midic); } /*ARGSUSED*/ static int ymhmidi_start_input (ymhusb_devc * devc, ymhusb_midic * midic) { int err; if ((err = udi_usb_submit_request (midic->datapipe, ymhmidi_record_callback, midic, midic->endpoint_handle, UDI_USBXFER_BULK_READ, midic->tmpbuf, TMPBUF_SIZE)) < 0) { return err; } return 0; } /*ARGSUSED*/ static int ymhmidi_submit_output (ymhusb_devc * devc, ymhusb_midic * midic) { int err; int n = midic->buf_t; if (midic->buf_h == midic->buf_t) return 0; if ((err = udi_usb_submit_request (midic->datapipe, ymhmidi_play_callback, midic, midic->endpoint_handle, UDI_USBXFER_BULK_WRITE, midic->outbuf[n], TMPBUF_SIZE)) < 0) { cmn_err (CE_WARN, "Submit USB MIDI request failed\n"); return err; } midic->buf_t = (n + 1) % TMPBUF_NSLOTS;; midic->output_busy = 1; return 0; } static int ymhmidi_put_output (ymhusb_devc * devc, ymhusb_midic * midic) { int n = midic->buf_h; midic->buf_h = (n + 1) % TMPBUF_NSLOTS; memcpy (midic->outbuf[n], midic->tmpbuf, TMPBUF_SIZE); midic->tmp_len = 0; memset (midic->tmpbuf, 0, TMPBUF_SIZE); if (midic->output_busy) return 0; ymhmidi_submit_output (devc, midic); return 0; } /*ARGSUSED*/ static void ymhusb_close_input (int dev, int mode) { oss_native_word flags; ymhusb_midic *midic; midic = midi_devs[dev]->devc; MUTEX_ENTER_IRQDISABLE (midic->mutex, flags); midic->open_mode = 0; midic->midi_input_intr = NULL; udi_usb_free_request (midic->datapipe); udi_close_endpoint (midic->endpoint_handle); if (midic->tmpbuf != NULL) CONTIG_FREE (midic->osdev, midic->tmpbuf, TMPBUF_SIZE, midic->tmpbuf_dma_handle); MUTEX_EXIT_IRQRESTORE (midic->mutex, flags); } /*ARGSUSED*/ static int ymhusb_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, phaddr; ymhusb_midic *midic; ymhusb_devc *devc; 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); midic->tmpbuf = CONTIG_MALLOC (midic->osdev, TMPBUF_SIZE, MEMLIMIT_32BITS, &phaddr, midic->tmpbuf_dma_handle); memset (midic->tmpbuf, 0, TMPBUF_SIZE); if ((midic->endpoint_handle = udi_open_endpoint (midic->usbdev, midic->endpoint_desc)) == NULL) { midic->open_mode = 0; midic->midi_input_intr = NULL; cmn_err (CE_WARN, "Cannot open audio pipe\n"); return OSS_ENOMEM; } if ((midic->datapipe = udi_usb_alloc_request (midic->usbdev, midic->endpoint_handle, 1, UDI_USBXFER_BULK_READ)) == NULL) { return OSS_EIO; } return ymhmidi_start_input (devc, midic); } static void ymhusb_flush_output (int dev) { oss_native_word flags; ymhusb_midic *midic; ymhusb_devc *devc; int next; midic = midi_devs[dev]->devc; devc = midic->devc; if (midic->tmp_len == 0) return; next = (midic->buf_t + 1) % TMPBUF_NSLOTS; if (next == midic->buf_h) /* Buffer full */ return; MUTEX_ENTER_IRQDISABLE (midic->mutex, flags); ymhmidi_put_output (devc, midic); MUTEX_EXIT_IRQRESTORE (midic->mutex, flags); } static int ymhusb_wait_output (int dev) { ymhusb_midic *midic; ymhusb_flush_output (dev); midic = midi_devs[dev]->devc; if (midic->output_busy) return 1; if (midic->buf_t == midic->buf_h) return 0; /* Not busy */ return 1; } /*ARGSUSED*/ static void ymhusb_close_output (int dev, int mode) { oss_native_word flags; ymhusb_midic *midic; midic = midi_devs[dev]->devc; ymhusb_flush_output (dev); MUTEX_ENTER_IRQDISABLE (midic->mutex, flags); midic->open_mode = 0; midic->midi_input_intr = NULL; udi_usb_free_request (midic->datapipe); udi_close_endpoint (midic->endpoint_handle); if (midic->outbuf != NULL) CONTIG_FREE (midic->osdev, midic->outbuf, OUTBUF_SIZE, midic->outbuf_dma_handle); if (midic->tmpbuf != NULL) KERNEL_FREE (midic->tmpbuf); midiparser_unalloc (midic->parser); midic->parser = NULL; MUTEX_EXIT_IRQRESTORE (midic->mutex, flags); } static void out_event (ymhusb_midic * midic, unsigned char a, unsigned char b, unsigned char c, unsigned char d) { unsigned char *e; ymhusb_devc *devc = midic->devc; if (midic->tmp_len > TMPBUF_SIZE - 4) ymhmidi_put_output (devc, midic); e = midic->tmpbuf + midic->tmp_len; *e++ = a; *e++ = b; *e++ = c; *e++ = d; midic->tmp_len += 4; if (midic->tmp_len >= TMPBUF_SIZE) ymhmidi_put_output (devc, midic); } void parser_cb (void *context, int category, unsigned char msg, unsigned char ch, unsigned char *parms, int len) { ymhusb_midic *midic = context; if (category == CAT_VOICE) { switch (msg) { case MIDI_NOTEON: out_event (midic, 0x9, msg | ch, parms[0], parms[1]); break; case MIDI_NOTEOFF: out_event (midic, 0x8, msg | ch, parms[0], parms[1]); break; case MIDI_KEY_PRESSURE: out_event (midic, 0xa, msg | ch, parms[0], parms[1]); break; } return; } if (category == CAT_CHN) { switch (msg) { case MIDI_CTL_CHANGE: out_event (midic, 0xb, msg | ch, parms[0], parms[1]); break; case MIDI_PGM_CHANGE: out_event (midic, 0xc, msg | ch, parms[0], 0); break; case MIDI_CHN_PRESSURE: out_event (midic, 0xd, msg | ch, parms[0], 0); break; case MIDI_PITCH_BEND: out_event (midic, 0xe, msg | ch, parms[0], parms[1]); break; } return; } if (category == CAT_REALTIME) { out_event (midic, 0xf, msg | ch, 0, 0); return; } if (category == CAT_MTC) { out_event (midic, 0x2, 0xf1, parms[0], 0); return; } if (category == CAT_SYSEX) { int l = len, n; unsigned char *d = parms; while (l > 0) { n = l; if (n > 3) n = 3; switch (n) { case 3: out_event (midic, 0x4, d[0], d[1], d[2]); break; case 2: out_event (midic, 0x6, d[0], d[1], 0); break; case 1: out_event (midic, 0x5, d[0], 0, 0); break; } l -= n; d += n; } return; } } /*ARGSUSED*/ static int ymhusb_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, phaddr; ymhusb_midic *midic; midic = midi_devs[dev]->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->output_busy = 0; MUTEX_EXIT_IRQRESTORE (midic->mutex, flags); midic->tmpbuf = KERNEL_MALLOC (TMPBUF_SIZE); memset (midic->tmpbuf, 0, TMPBUF_SIZE); midic->outbuf = CONTIG_MALLOC (midic->osdev, OUTBUF_SIZE, MEMLIMIT_32BITS, &phaddr, midic->outbuf_dma_handle); midic->tmp_len = 0; midic->buf_h = midic->buf_t = 0; /* Empty buffer */ memset (midic->outbuf, 0, OUTBUF_SIZE); if ((midic->parser = midiparser_create (parser_cb, midic)) == NULL) { midic->open_mode = 0; midic->midi_input_intr = NULL; cmn_err (CE_WARN, "Cannot create MIDI parser\n"); return OSS_ENOMEM; } if ((midic->endpoint_handle = udi_open_endpoint (midic->usbdev, midic->endpoint_desc)) == NULL) { midic->open_mode = 0; midic->midi_input_intr = NULL; cmn_err (CE_WARN, "Cannot open audio pipe\n"); return OSS_ENOMEM; } if ((midic->datapipe = udi_usb_alloc_request (midic->usbdev, midic->endpoint_handle, 1, UDI_USBXFER_BULK_WRITE)) == NULL) { return OSS_EIO; } return 0; } static int ymhusb_out (int dev, unsigned char midi_byte) { ymhusb_midic *midic = midi_devs[dev]->devc; oss_native_word flags; int next; next = (midic->buf_t + 1) % TMPBUF_NSLOTS; if (next == midic->buf_h) /* Buffer full */ return 0; /* Try again later */ MUTEX_ENTER_IRQDISABLE (midic->mutex, flags); midiparser_input (midic->parser, midi_byte); MUTEX_EXIT_IRQRESTORE (midic->mutex, flags); return 1; } /*ARGSUSED*/ static int ymhusb_ioctl (int dev, unsigned cmd, ioctl_arg arg) { return OSS_EINVAL; } static midi_driver_t ymhusb_input_driver = { ymhusb_open_input, ymhusb_close_input, ymhusb_ioctl, ymhusb_out, }; static void add_input_device (ymhusb_devc * devc, char *name, void *desc, int caps) { int n; ymhusb_midic *midic; char tmp[128]; if (devc->n_inputs >= MAX_INDEVS) { cmn_err (CE_WARN, "Yamaha MIDI: Too many inputs\n"); return; } n = devc->n_inputs++; midic = &devc->indevs[n]; midic->devc = devc; midic->osdev = devc->osdev; MUTEX_INIT (midic->osdev, midic->mutex, MH_DRV + 1); midic->endpoint_desc = desc; midic->portnum = n; midic->usbdev = devc->usbdev; sprintf (tmp, "%s input %d", name, n); if ((midic->midi_dev = oss_install_mididev (OSS_MIDI_DRIVER_VERSION, "YMHMIDI", tmp, &ymhusb_input_driver, sizeof (ymhusb_input_driver), MFLAG_INPUT, midic, midic->osdev)) < 0) { cmn_err (CE_CONT, "Failed to install MIDI device\n"); return; } midi_devs[midic->midi_dev]->caps |= caps; } static midi_driver_t ymhusb_output_driver = { ymhusb_open_output, ymhusb_close_output, ymhusb_ioctl, ymhusb_out, NULL, 0, ymhusb_flush_output, ymhusb_wait_output }; static void add_output_device (ymhusb_devc * devc, char *name, void *desc, int caps) { int n; ymhusb_midic *midic; char tmp[128]; if (devc->n_outputs >= MAX_OUTDEVS) { cmn_err (CE_WARN, "Yamaha MIDI: Too many outputs\n"); return; } n = devc->n_outputs++; midic = &devc->outdevs[n]; midic->devc = devc; midic->osdev = devc->osdev; MUTEX_INIT (midic->osdev, midic->mutex, MH_DRV + 1); midic->endpoint_desc = desc; midic->portnum = n; midic->usbdev = devc->usbdev; sprintf (tmp, "%s output %d", name, n); if ((midic->midi_dev = oss_install_mididev (OSS_MIDI_DRIVER_VERSION, "YMHMIDI", tmp, &ymhusb_output_driver, sizeof (ymhusb_output_driver), MFLAG_OUTPUT, midic, midic->osdev)) < 0) { cmn_err (CE_CONT, "Failed to install MIDI device\n"); return; } midi_devs[midic->midi_dev]->caps |= caps; } void * yamaha_usb_midi_driver (ossusb_devc * usb_devc) { ymhusb_devc *devc; int i; unsigned char *desc; int desc_len; int caps = MIDI_CAP_EXTERNAL; unsigned int devid; char *name = "Yamaha USB MIDI"; if (usb_devc->dev_name != NULL) name = usb_devc->dev_name; if ((devc = PMALLOC (usb_devc->osdev, sizeof (*devc))) == NULL) { cmn_err (CE_WARN, "Yamaha MIDI: Out of memory\n"); return NULL; } memset (devc, 0, sizeof (*devc)); devc->unload_func = ymhusb_unload; devc->osdev = usb_devc->osdev; devc->usbdev = usb_devc->last_usbdev; MUTEX_INIT (devc->osdev, devc->mutex, MH_DRV); devid = (usb_devc->vendor << 16) | usb_devc->product; DDB (cmn_err (CE_CONT, "Attaching Yamaha MIDI device %08x (%s)\n", devid, name)); switch (devid) { case 0x0499101e: /* PSR-K1 keyboard */ caps |= MIDI_CAP_PTOP; break; } for (i = 0; i < 32; i++) if ((desc = udi_usbdev_get_endpoint (devc->usbdev, 0, i, &desc_len)) != NULL) { if (desc[2] & 0x80) { add_input_device (devc, name, desc, caps); } else { add_output_device (devc, name, desc, caps); } } return devc; }