📄 usb-midi.c
字号:
/*
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 + -