⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 usb-midi.c

📁 USB_MIDI_DRIVER(for linux)
💻 C
📖 第 1 页 / 共 4 页
字号:
/*
  usb-midi.c  --  USB-MIDI driver

  Copyright (C) 2001 
      NAGANO Daisuke <breeze.nagano@nifty.ne.jp>

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2, or (at your option)
  any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

  This driver is based on:
    - 'Universal Serial Bus Device Class Definition for MIDI Device'
    - linux/drivers/sound/es1371.c, linux/drivers/usb/audio.c
    - alsa/lowlevel/pci/cs64xx.c
    - umidi.c for NetBSD
 */

/* ------------------------------------------------------------------------- */


#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/poll.h>
#include <linux/sound.h>
#include <linux/init.h>
#include <asm/semaphore.h>

#include "usb-midi.h"

/* ------------------------------------------------------------------------- */

/* More verbose on syslog */
#undef MIDI_DEBUG

#define MIDI_IN_BUFSIZ 1024

#define HAVE_SUPPORT_USB_MIDI_CLASS

#undef HAVE_SUPPORT_ALSA

/* ------------------------------------------------------------------------- */

static int singlebyte = 0;
module_param(singlebyte, int, 0);
MODULE_PARM_DESC(singlebyte,"Enable sending MIDI messages with single message packet");

static int maxdevices = 4;
module_param(maxdevices, int, 0);
MODULE_PARM_DESC(maxdevices,"Max number of allocatable MIDI device");

static int uvendor     = -1;
module_param(uvendor, int, 0);
MODULE_PARM_DESC(uvendor, "The USB Vendor ID of a semi-compliant interface");

static int uproduct    = -1;
module_param(uproduct, int, 0);
MODULE_PARM_DESC(uproduct, "The USB Product ID of a semi-compliant interface");

static int uinterface  = -1;
module_param(uinterface, int, 0);
MODULE_PARM_DESC(uinterface, "The Interface number of a semi-compliant interface");

static int ualt        = -1;
module_param(ualt, int, 0);
MODULE_PARM_DESC(ualt, "The optional alternative setting of a semi-compliant interface");

static int umin        = -1;
module_param(umin, int, 0);
MODULE_PARM_DESC(umin, "The input endpoint of a semi-compliant interface");

static int umout       = -1;
module_param(umout, int, 0);
MODULE_PARM_DESC(umout, "The output endpoint of a semi-compliant interface");

static int ucable      = -1;
module_param(ucable, int, 0);
MODULE_PARM_DESC(ucable, "The cable number used for a semi-compliant interface");

/** Note -- the usb_string() returns only Latin-1 characters.
 * (unicode chars <= 255). To support Japanese, a unicode16LE-to-EUC or
 * unicode16LE-to-JIS routine is needed to wrap around usb_get_string().
 **/
static unsigned short ulangid      = 0x0409; /** 0x0411 for Japanese **/
module_param(ulangid, ushort, 0);
MODULE_PARM_DESC(ulangid, "The optional preferred USB Language ID for all devices");

MODULE_AUTHOR("NAGANO Daisuke <breeze.nagano@nifty.ne.jp>");
MODULE_DESCRIPTION("USB-MIDI driver");
MODULE_LICENSE("GPL");

/* ------------------------------------------------------------------------- */

/** MIDIStreaming Class-Specific Interface Descriptor Subtypes **/

#define MS_DESCRIPTOR_UNDEFINED	0
#define MS_HEADER		1
#define MIDI_IN_JACK		2
#define MIDI_OUT_JACK		3
/* Spec reads: ELEMENT */
#define ELEMENT_DESCRIPTOR   	4

#define MS_HEADER_LENGTH	7

/** MIDIStreaming Class-Specific Endpoint Descriptor Subtypes **/

#define DESCRIPTOR_UNDEFINED	0
/* Spec reads: MS_GENERAL */
#define MS_GENERAL_ENDPOINT	1

/** MIDIStreaming MIDI IN and OUT Jack Types **/

#define JACK_TYPE_UNDEFINED	0
/* Spec reads: EMBEDDED */
#define EMBEDDED_JACK		1
/* Spec reads: EXTERNAL */
#define EXTERNAL_JACK		2


/* structure summary
  
      usb_midi_state     usb_device
       |         |
      *|        *|       per ep
     in_ep     out_ep
       |         |
      *|        *|       per cable
      min       mout
       |         |       (cable to device pairing magic)
       |         |
       usb_midi_dev      dev_id (major,minor) == file->private_data

*/

/* usb_midi_state: corresponds to a USB-MIDI module */
struct usb_midi_state {
	struct list_head   mididev;
	
	struct usb_device *usbdev;
	
	struct list_head   midiDevList;
	struct list_head   inEndpointList;
	struct list_head   outEndpointList;
	
	spinlock_t         lock;
	
	unsigned int       count; /* usage counter */
};

/* midi_out_endpoint: corresponds to an output endpoint */
struct midi_out_endpoint {
	struct list_head  list;
	
	struct usb_device *usbdev;
	int                endpoint;
	spinlock_t         lock;
	wait_queue_head_t  wait;
	
	unsigned char     *buf;
	int                bufWrPtr;
	int                bufSize;
	
	struct urb       *urb;
};

/* midi_in_endpoint: corresponds to an input endpoint */
struct midi_in_endpoint {
	struct list_head   list;

	struct usb_device *usbdev;
	int                endpoint;
	spinlock_t         lock;
	wait_queue_head_t  wait;

	struct usb_mididev *cables[16];	// cables open for read
	int                 readers;	// number of cables open for read

	struct urb        *urb;
	unsigned char     *recvBuf;
	int                recvBufSize;
	int                urbSubmitted;	//FIXME: == readers > 0
};

/* usb_mididev: corresponds to a logical device */
struct usb_mididev {
	struct list_head       list;

	struct usb_midi_state *midi;
	int                    dev_midi;
	mode_t                 open_mode;

	struct {
		struct midi_in_endpoint *ep;
		int              cableId;
		
// as we are pushing data from usb_bulk_read to usb_midi_read,
// we need a larger, cyclic buffer here.
		unsigned char    buf[MIDI_IN_BUFSIZ];
		int              bufRdPtr;
		int              bufWrPtr;
		int              bufRemains;
	} min;

	struct {
		struct midi_out_endpoint *ep;
		int              cableId;
		
		unsigned char    buf[3];
		int              bufPtr;
		int              bufRemains;
		
		int              isInExclusive;
		unsigned char    lastEvent;
	} mout;

	int singlebyte;
};

/** Map the high nybble of MIDI voice messages to number of Message bytes.
 * High nyble ranges from 0x8 to 0xe
 */

static int remains_80e0[] = {
	3,	/** 0x8X Note Off **/
	3,	/** 0x9X Note On **/
	3,	/** 0xAX Poly-key pressure **/
	3,	/** 0xBX Control Change **/
	2,	/** 0xCX Program Change **/
	2,	/** 0xDX Channel pressure **/
	3 	/** 0xEX PitchBend Change **/
};

/** Map the messages to a number of Message bytes.
 *
 **/
static int remains_f0f6[] = {
	0,	/** 0xF0 **/
	2,	/** 0XF1 **/
	3,	/** 0XF2 **/
	2,	/** 0XF3 **/
	2,	/** 0XF4 (Undefined by MIDI Spec, and subject to change) **/
	2,	/** 0XF5 (Undefined by MIDI Spec, and subject to change) **/
	1	/** 0XF6 **/
};

/** Map the messages to a CIN (Code Index Number).
 *
 **/
static int cin_f0ff[] = {
	4,	/** 0xF0 System Exclusive Message Start (special cases may be 6 or 7) */
	2,	/** 0xF1 **/
	3,	/** 0xF2 **/
	2,	/** 0xF3 **/
	2,	/** 0xF4 **/
	2,	/** 0xF5 **/
	5,	/** 0xF6 **/
	5,	/** 0xF7 End of System Exclusive Message (May be 6 or 7) **/
	5,	/** 0xF8 **/
	5,	/** 0xF9 **/
	5,	/** 0xFA **/
	5,	/** 0xFB **/
	5,	/** 0xFC **/
	5,	/** 0xFD **/
	5,	/** 0xFE **/
	5	/** 0xFF **/
};

/** Map MIDIStreaming Event packet Code Index Number (low nybble of byte 0)
 * to the number of bytes of valid MIDI data.
 *
 * CIN of 0 and 1 are NOT USED in MIDIStreaming 1.0.
 *
 **/
static int cin_to_len[] = {
	0, 0, 2, 3,
	3, 1, 2, 3,
	3, 3, 3, 3,
	2, 2, 3, 1
};


/* ------------------------------------------------------------------------- */

static struct list_head mididevs = LIST_HEAD_INIT(mididevs);

static DECLARE_MUTEX(open_sem);
static DECLARE_WAIT_QUEUE_HEAD(open_wait);


/* ------------------------------------------------------------------------- */

static void usb_write_callback(struct urb *urb, struct pt_regs *regs)
{
	struct midi_out_endpoint *ep = (struct midi_out_endpoint *)urb->context;

	if ( waitqueue_active( &ep->wait ) )
		wake_up_interruptible( &ep->wait );
}


static int usb_write( struct midi_out_endpoint *ep, unsigned char *buf, int len )
{
	struct usb_device *d;
	int pipe;
	int ret = 0;
	int status;
	int maxretry = 50;
	
	DECLARE_WAITQUEUE(wait,current);
	init_waitqueue_head(&ep->wait);

	d = ep->usbdev;
	pipe = usb_sndbulkpipe(d, ep->endpoint);
	usb_fill_bulk_urb( ep->urb, d, pipe, (unsigned char*)buf, len,
		       usb_write_callback, ep );

	status = usb_submit_urb(ep->urb, GFP_KERNEL);
    
	if (status) {
		printk(KERN_ERR "usbmidi: Cannot submit urb (%d)\n",status);
		ret = -EIO;
		goto error;
	}

	add_wait_queue( &ep->wait, &wait );
	set_current_state( TASK_INTERRUPTIBLE );

	while( ep->urb->status == -EINPROGRESS ) {
		if ( maxretry-- < 0 ) {
			printk(KERN_ERR "usbmidi: usb_bulk_msg timed out\n");
			ret = -ETIME;
			break;
		}
		interruptible_sleep_on_timeout( &ep->wait, 10 );
	}
	set_current_state( TASK_RUNNING );
	remove_wait_queue( &ep->wait, &wait );

error:
	return ret;
}


/** Copy data from URB to In endpoint buf.
 * Discard if CIN == 0 or CIN = 1.
 *
 *
 **/

static void usb_bulk_read(struct urb *urb, struct pt_regs *regs)
{
	struct midi_in_endpoint *ep = (struct midi_in_endpoint *)(urb->context);
	unsigned char *data = urb->transfer_buffer;
	int i, j, wake;

	if ( !ep->urbSubmitted ) {
		return;
	}

	if ( (urb->status == 0) && (urb->actual_length > 0) ) {
		wake = 0;
		spin_lock( &ep->lock );

		for(j = 0; j < urb->actual_length; j += 4) {
			int cin = (data[j]>>0)&0xf;
			int cab = (data[j]>>4)&0xf;
			struct usb_mididev *cable = ep->cables[cab];
			if ( cable ) {
				int len = cin_to_len[cin]; /** length of MIDI data **/
				for (i = 0; i < len; i++) {
					cable->min.buf[cable->min.bufWrPtr] = data[1+i+j];
					cable->min.bufWrPtr = (cable->min.bufWrPtr+1)%MIDI_IN_BUFSIZ;
					if (cable->min.bufRemains < MIDI_IN_BUFSIZ)
						cable->min.bufRemains += 1;
					else /** need to drop data **/
						cable->min.bufRdPtr += (cable->min.bufRdPtr+1)%MIDI_IN_BUFSIZ;
					wake = 1;
				}
			}
		}

		spin_unlock ( &ep->lock );
		if ( wake ) {
			wake_up( &ep->wait );
		}
	}

	/* urb->dev must be reinitialized on 2.4.x kernels */
	urb->dev = ep->usbdev;

	urb->actual_length = 0;
	usb_submit_urb(urb, GFP_ATOMIC);
}



/* ------------------------------------------------------------------------- */

/* This routine must be called with spin_lock */

/** Wrapper around usb_write().
 *  This routine must be called with spin_lock held on ep.
 *  Called by midiWrite(), putOneMidiEvent(), and  usb_midi_write();
 **/
static int flush_midi_buffer( struct midi_out_endpoint *ep )
{
	int ret=0;

	if ( ep->bufWrPtr > 0 ) {
		ret = usb_write( ep, ep->buf, ep->bufWrPtr );
		ep->bufWrPtr = 0;
	}

	return ret;
}


/* ------------------------------------------------------------------------- */


/** Given a MIDI Event, determine size of data to be attached to 
 * USB-MIDI packet.
 * Returns 1, 2 or 3.
 * Called by midiWrite();
 * Uses remains_80e0 and remains_f0f6;
 **/
static int get_remains(int event)
{
	int ret;

	if ( event  < 0x80 ) {
		ret = 1;
	} else if ( event < 0xf0 ) {
		ret = remains_80e0[((event-0x80)>>4)&0x0f];
	} else if ( event < 0xf7 ) {
		ret = remains_f0f6[event-0xf0];
	} else {
		ret = 1;
	}

	return ret;
}

/** Given the output MIDI data in the output buffer, computes a reasonable 
 * CIN.
 * Called by putOneMidiEvent().
 **/
static int get_CIN( struct usb_mididev *m )
{
	int cin;

	if ( m->mout.buf[0] == 0xf7 ) {
		cin = 5;
	}
	else if ( m->mout.buf[1] == 0xf7 ) {
		cin = 6;
	}
	else if ( m->mout.buf[2] == 0xf7 ) {
		cin = 7;
	}
	else {
		if ( m->mout.isInExclusive == 1 ) {
			cin = 4;
		} else if ( m->mout.buf[0] < 0x80 ) {
			/** One byte that we know nothing about. **/
			cin = 0xF; 
		} else if ( m->mout.buf[0] < 0xf0 ) {
			/** MIDI Voice messages 0x8X to 0xEX map to cin 0x8 to 0xE. **/
			cin = (m->mout.buf[0]>>4)&0x0f; 
		}
		else {
			/** Special lookup table exists for real-time events. **/
			cin = cin_f0ff[m->mout.buf[0]-0xf0];
		}
	}

	return cin;
}


/* ------------------------------------------------------------------------- */



/** Move data to USB endpoint buffer.
 *
 **/
static int put_one_midi_event(struct usb_mididev *m)
{
	int cin;
	unsigned long flags;
	struct midi_out_endpoint *ep = m->mout.ep;
	int ret=0;

	cin = get_CIN( m );
	if ( cin > 0x0f || cin < 0 ) {
		return -EINVAL;
	}

	spin_lock_irqsave( &ep->lock, flags );
	ep->buf[ep->bufWrPtr++] = (m->mout.cableId<<4) | cin;
	ep->buf[ep->bufWrPtr++] = m->mout.buf[0];
	ep->buf[ep->bufWrPtr++] = m->mout.buf[1];
	ep->buf[ep->bufWrPtr++] = m->mout.buf[2];
	if ( ep->bufWrPtr >= ep->bufSize ) {
		ret = flush_midi_buffer( ep );
	}
	spin_unlock_irqrestore( &ep->lock, flags);

	m->mout.buf[0] = m->mout.buf[1] = m->mout.buf[2] = 0;
	m->mout.bufPtr = 0;

	return ret;
}

/** Write the MIDI message v on the midi device.
 *  Called by usb_midi_write();
 *  Responsible for packaging a MIDI data stream into USB-MIDI packets.
 **/

static int midi_write( struct usb_mididev *m, int v )
{
	unsigned long flags;
	struct midi_out_endpoint *ep = m->mout.ep;
	int ret=0;
	unsigned char c = (unsigned char)v;
	unsigned char sysrt_buf[4];

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -