Google
 

Open Sound System
The Hitchhiker's Guide to OSS 4.1 Internals

Do you have problems with sound/audio application development? Don't panic! Click here for help!

oss_usb/ossusb_ymhmidi.c

Dedicated driver for Yamaha USB MIDI devices

Description




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;
}

Copyright (C) 4Front Technologies, 2007. All rights reserved.

Back to index OSS web site


Copyright (C) 4Front Technologies, 2007. All rights reserved.
Back to index OSS web site