📄 usb-midi.c
字号:
if ( m->singlebyte != 0 ) {
/** Simple code to handle the single-byte USB-MIDI protocol. */
spin_lock_irqsave( &ep->lock, flags );
if ( ep->bufWrPtr+4 > ep->bufSize ) {
ret = flush_midi_buffer( ep );
if ( !ret ) {
spin_unlock_irqrestore( &ep->lock, flags );
return ret;
}
}
ep->buf[ep->bufWrPtr++] = (m->mout.cableId<<4) | 0x0f; /* single byte */
ep->buf[ep->bufWrPtr++] = c;
ep->buf[ep->bufWrPtr++] = 0;
ep->buf[ep->bufWrPtr++] = 0;
if ( ep->bufWrPtr >= ep->bufSize ) {
ret = flush_midi_buffer( ep );
}
spin_unlock_irqrestore( &ep->lock, flags );
return ret;
}
/** Normal USB-MIDI protocol begins here. */
if ( c > 0xf7 ) { /* system: Realtime messages */
/** Realtime messages are written IMMEDIATELY. */
sysrt_buf[0] = (m->mout.cableId<<4) | 0x0f;
sysrt_buf[1] = c;
sysrt_buf[2] = 0;
sysrt_buf[3] = 0;
spin_lock_irqsave( &ep->lock, flags );
ret = usb_write( ep, sysrt_buf, 4 );
spin_unlock_irqrestore( &ep->lock, flags );
/* m->mout.lastEvent = 0; */
return ret;
}
if ( c >= 0x80 ) {
if ( c < 0xf0 ) {
m->mout.lastEvent = c;
m->mout.isInExclusive = 0;
m->mout.bufRemains = get_remains(c);
} else if ( c == 0xf0 ) {
/* m->mout.lastEvent = 0; */
m->mout.isInExclusive = 1;
m->mout.bufRemains = get_remains(c);
} else if ( c == 0xf7 && m->mout.isInExclusive == 1 ) {
/* m->mout.lastEvent = 0; */
m->mout.isInExclusive = 0;
m->mout.bufRemains = 1;
} else if ( c > 0xf0 ) {
/* m->mout.lastEvent = 0; */
m->mout.isInExclusive = 0;
m->mout.bufRemains = get_remains(c);
}
} else if ( m->mout.bufRemains == 0 && m->mout.isInExclusive == 0 ) {
if ( m->mout.lastEvent == 0 ) {
return 0; /* discard, waiting for the first event */
}
/** track status **/
m->mout.buf[0] = m->mout.lastEvent;
m->mout.bufPtr = 1;
m->mout.bufRemains = get_remains(m->mout.lastEvent)-1;
}
m->mout.buf[m->mout.bufPtr++] = c;
m->mout.bufRemains--;
if ( m->mout.bufRemains == 0 || m->mout.bufPtr >= 3) {
ret = put_one_midi_event(m);
}
return ret;
}
/* ------------------------------------------------------------------------- */
/** Basic operation on /dev/midiXX as registered through struct file_operations.
*
* Basic contract: Used to change the current read/write position in a file.
* On success, the non-negative position is reported.
* On failure, the negative of an error code is reported.
*
* Because a MIDIStream is not a file, all seek operations are doomed to fail.
*
**/
static loff_t usb_midi_llseek(struct file *file, loff_t offset, int origin)
{
/** Tell user you cannot seek on a PIPE-like device. **/
return -ESPIPE;
}
/** Basic operation on /dev/midiXX as registered through struct file_operations.
*
* Basic contract: Block until count bytes have been read or an error occurs.
*
**/
static ssize_t usb_midi_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
struct usb_mididev *m = (struct usb_mididev *)file->private_data;
struct midi_in_endpoint *ep = m->min.ep;
ssize_t ret;
DECLARE_WAITQUEUE(wait, current);
if ( !access_ok(VERIFY_READ, buffer, count) ) {
return -EFAULT;
}
if ( count == 0 ) {
return 0;
}
add_wait_queue( &ep->wait, &wait );
ret = 0;
while( count > 0 ) {
int cnt;
int d = (int)count;
cnt = m->min.bufRemains;
if ( cnt > d ) {
cnt = d;
}
if ( cnt <= 0 ) {
if ( file->f_flags & O_NONBLOCK ) {
if (!ret)
ret = -EAGAIN;
break;
}
__set_current_state(TASK_INTERRUPTIBLE);
schedule();
if (signal_pending(current)) {
if(!ret)
ret=-ERESTARTSYS;
break;
}
continue;
}
{
int i;
unsigned long flags; /* used to synchronize access to the endpoint */
spin_lock_irqsave( &ep->lock, flags );
for (i = 0; i < cnt; i++) {
if ( copy_to_user( buffer+i, m->min.buf+m->min.bufRdPtr, 1 ) ) {
if ( !ret )
ret = -EFAULT;
break;
}
m->min.bufRdPtr = (m->min.bufRdPtr+1)%MIDI_IN_BUFSIZ;
m->min.bufRemains -= 1;
}
spin_unlock_irqrestore( &ep->lock, flags );
}
count-=cnt;
buffer+=cnt;
ret+=cnt;
break;
}
remove_wait_queue( &ep->wait, &wait );
set_current_state(TASK_RUNNING);
return ret;
}
/** Basic operation on /dev/midiXX as registered through struct file_operations.
*
* Basic Contract: Take MIDI data byte-by-byte and pass it to
* writeMidi() which packages MIDI data into USB-MIDI stream.
* Then flushMidiData() is called to ensure all bytes have been written
* in a timely fashion.
*
**/
static ssize_t usb_midi_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
struct usb_mididev *m = (struct usb_mididev *)file->private_data;
ssize_t ret;
unsigned long int flags;
if ( !access_ok(VERIFY_READ, buffer, count) ) {
return -EFAULT;
}
if ( count == 0 ) {
return 0;
}
ret = 0;
while( count > 0 ) {
unsigned char c;
if (copy_from_user((unsigned char *)&c, buffer, 1)) {
if ( ret == 0 )
ret = -EFAULT;
break;
}
if( midi_write(m, (int)c) ) {
if ( ret == 0 )
ret = -EFAULT;
break;
}
count--;
buffer++;
ret++;
}
spin_lock_irqsave( &m->mout.ep->lock, flags );
if ( flush_midi_buffer(m->mout.ep) < 0 ) {
ret = -EFAULT;
}
spin_unlock_irqrestore( &m->mout.ep->lock, flags );
return ret;
}
/** Basic operation on /dev/midiXX as registered through struct file_operations.
*
* Basic contract: Wait (spin) until ready to read or write on the file.
*
**/
static unsigned int usb_midi_poll(struct file *file, struct poll_table_struct *wait)
{
struct usb_mididev *m = (struct usb_mididev *)file->private_data;
struct midi_in_endpoint *iep = m->min.ep;
struct midi_out_endpoint *oep = m->mout.ep;
unsigned long flags;
unsigned int mask = 0;
if ( file->f_mode & FMODE_READ ) {
poll_wait( file, &iep->wait, wait );
spin_lock_irqsave( &iep->lock, flags );
if ( m->min.bufRemains > 0 )
mask |= POLLIN | POLLRDNORM;
spin_unlock_irqrestore( &iep->lock, flags );
}
if ( file->f_mode & FMODE_WRITE ) {
poll_wait( file, &oep->wait, wait );
spin_lock_irqsave( &oep->lock, flags );
if ( oep->bufWrPtr < oep->bufSize )
mask |= POLLOUT | POLLWRNORM;
spin_unlock_irqrestore( &oep->lock, flags );
}
return mask;
}
/** Basic operation on /dev/midiXX as registered through struct file_operations.
*
* Basic contract: This is always the first operation performed on the
* device node. If no method is defined, the open succeeds without any
* notification given to the module.
*
**/
static int usb_midi_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
DECLARE_WAITQUEUE(wait, current);
struct usb_midi_state *s;
struct usb_mididev *m;
unsigned long flags;
int succeed = 0;
#if 0
printk(KERN_INFO "usb-midi: Open minor= %d.\n", minor);
#endif
for(;;) {
down(&open_sem);
list_for_each_entry(s, &mididevs, mididev) {
list_for_each_entry(m, &s->midiDevList, list) {
if ( !((m->dev_midi ^ minor) & ~0xf) )
goto device_found;
}
}
up(&open_sem);
return -ENODEV;
device_found:
if ( !s->usbdev ) {
up(&open_sem);
return -EIO;
}
if ( !(m->open_mode & file->f_mode) ) {
break;
}
if ( file->f_flags & O_NONBLOCK ) {
up(&open_sem);
return -EBUSY;
}
__set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue( &open_wait, &wait );
up(&open_sem);
schedule();
remove_wait_queue( &open_wait, &wait );
if ( signal_pending(current) ) {
return -ERESTARTSYS;
}
}
file->private_data = m;
spin_lock_irqsave( &s->lock, flags );
if ( !(m->open_mode & (FMODE_READ | FMODE_WRITE)) ) {
//FIXME: intented semantics unclear here
m->min.bufRdPtr = 0;
m->min.bufWrPtr = 0;
m->min.bufRemains = 0;
spin_lock_init(&m->min.ep->lock);
m->mout.bufPtr = 0;
m->mout.bufRemains = 0;
m->mout.isInExclusive = 0;
m->mout.lastEvent = 0;
spin_lock_init(&m->mout.ep->lock);
}
if ( (file->f_mode & FMODE_READ) && m->min.ep != NULL ) {
unsigned long int flagsep;
spin_lock_irqsave( &m->min.ep->lock, flagsep );
m->min.ep->cables[m->min.cableId] = m;
m->min.ep->readers += 1;
m->min.bufRdPtr = 0;
m->min.bufWrPtr = 0;
m->min.bufRemains = 0;
spin_unlock_irqrestore( &m->min.ep->lock, flagsep );
if ( !(m->min.ep->urbSubmitted)) {
/* urb->dev must be reinitialized on 2.4.x kernels */
m->min.ep->urb->dev = m->min.ep->usbdev;
if ( usb_submit_urb(m->min.ep->urb, GFP_ATOMIC) ) {
printk(KERN_ERR "usbmidi: Cannot submit urb for MIDI-IN\n");
}
m->min.ep->urbSubmitted = 1;
}
m->open_mode |= FMODE_READ;
succeed = 1;
}
if ( (file->f_mode & FMODE_WRITE) && m->mout.ep != NULL ) {
m->mout.bufPtr = 0;
m->mout.bufRemains = 0;
m->mout.isInExclusive = 0;
m->mout.lastEvent = 0;
m->open_mode |= FMODE_WRITE;
succeed = 1;
}
spin_unlock_irqrestore( &s->lock, flags );
s->count++;
up(&open_sem);
/** Changed to prevent extra increments to USE_COUNT. **/
if (!succeed) {
return -EBUSY;
}
#if 0
printk(KERN_INFO "usb-midi: Open Succeeded. minor= %d.\n", minor);
#endif
return nonseekable_open(inode, file); /** Success. **/
}
/** Basic operation on /dev/midiXX as registered through struct file_operations.
*
* Basic contract: Close an opened file and deallocate anything we allocated.
* Like open(), this can be missing. If open set file->private_data,
* release() must clear it.
*
**/
static int usb_midi_release(struct inode *inode, struct file *file)
{
struct usb_mididev *m = (struct usb_mididev *)file->private_data;
struct usb_midi_state *s = (struct usb_midi_state *)m->midi;
#if 0
printk(KERN_INFO "usb-midi: Close.\n");
#endif
down(&open_sem);
if ( m->open_mode & FMODE_WRITE ) {
m->open_mode &= ~FMODE_WRITE;
usb_kill_urb( m->mout.ep->urb );
}
if ( m->open_mode & FMODE_READ ) {
unsigned long int flagsep;
spin_lock_irqsave( &m->min.ep->lock, flagsep );
m->min.ep->cables[m->min.cableId] = NULL; // discard cable
m->min.ep->readers -= 1;
m->open_mode &= ~FMODE_READ;
if ( m->min.ep->readers == 0 &&
m->min.ep->urbSubmitted ) {
m->min.ep->urbSubmitted = 0;
usb_kill_urb(m->min.ep->urb);
}
spin_unlock_irqrestore( &m->min.ep->lock, flagsep );
}
s->count--;
up(&open_sem);
wake_up(&open_wait);
file->private_data = NULL;
return 0;
}
static struct file_operations usb_midi_fops = {
.owner = THIS_MODULE,
.llseek = usb_midi_llseek,
.read = usb_midi_read,
.write = usb_midi_write,
.poll = usb_midi_poll,
.open = usb_midi_open,
.release = usb_midi_release,
};
/* ------------------------------------------------------------------------- */
/** Returns filled midi_in_endpoint structure or null on failure.
*
* Parameters:
* d - a usb_device
* endPoint - An usb endpoint in the range 0 to 15.
* Called by allocUsbMidiDev();
*
**/
static struct midi_in_endpoint *alloc_midi_in_endpoint( struct usb_device *d, int endPoint )
{
struct midi_in_endpoint *ep;
int bufSize;
int pipe;
endPoint &= 0x0f; /* Silently force endPoint to lie in range 0 to 15. */
pipe = usb_rcvbulkpipe( d, endPoint );
bufSize = usb_maxpacket( d, pipe, usb_pipein(pipe) );
/* usb_pipein() = ! usb_pipeout() = true for an in Endpoint */
ep = (struct midi_in_endpoint *)kmalloc(sizeof(struct midi_in_endpoint), GFP_KERNEL);
if ( !ep ) {
printk(KERN_ERR "usbmidi: no memory for midi in-endpoint\n");
return NULL;
}
memset( ep, 0, sizeof(struct midi_in_endpoint) );
// this sets cables[] and readers to 0, too.
// for (i=0; i<16; i++) ep->cables[i] = 0; // discard cable
// ep->readers = 0;
ep->endpoint = endPoint;
ep->recvBuf = (unsigned char *)kmalloc(sizeof(unsigned char)*(bufSize), GFP_KERNEL);
if ( !ep->recvBuf ) {
printk(KERN_ERR "usbmidi: no memory for midi in-endpoint buffer\n");
kfree(ep);
return NULL;
}
ep->urb = usb_alloc_urb(0, GFP_KERNEL); /* no ISO */
if ( !ep->urb ) {
printk(KERN_ERR "usbmidi: no memory for midi in-endpoint urb\n");
kfree(ep->recvBuf);
kfree(ep);
return NULL;
}
usb_fill_bulk_urb( ep->urb, d,
usb_rcvbulkpipe(d, endPoint),
(unsigned char *)ep->recvBuf, bufSize,
usb_bulk_read, ep );
/* ep->bufRdPtr = 0; */
/* ep->bufWrPtr = 0; */
/* ep->bufRemains = 0; */
/* ep->urbSubmitted = 0; */
ep->recvBufSize = bufSize;
init_waitqueue_head(&ep->wait);
return ep;
}
static int remove_midi_in_endpoint( struct midi_in_endpoint *min )
{
usb_kill_urb( min->urb );
usb_free_urb( min->urb );
kfree( min->recvBuf );
kfree( min );
return 0;
}
/** Returns filled midi_out_endpoint structure or null on failure.
*
* Parameters:
* d - a usb_device
* endPoint - An usb endpoint in the range 0 to 15.
* Called by allocUsbMidiDev();
*
**/
static struct midi_out_endpoint *alloc_midi_out_endpoint( struct usb_device *d, int endPoint )
{
struct midi_out_endpoint *ep = NULL;
int pipe;
int bufSize;
endPoint &= 0x0f;
pipe = usb_sndbulkpipe( d, endPoint );
bufSize = usb_maxpacket( d, pipe, usb_pipeout(pipe) );
ep = (struct midi_out_endpoint *)kmalloc(sizeof(struct midi_out_endpoint), GFP_KERNEL);
if ( !ep ) {
printk(KERN_ERR "usbmidi: no memory for midi out-endpoint\n");
return NULL;
}
memset( ep, 0, sizeof(struct midi_out_endpoint) );
ep->endpoint = endPoint;
ep->buf = (unsigned char *)kmalloc(sizeof(unsigned char)*bufSize, GFP_KERNEL);
if ( !ep->buf ) {
printk(KERN_ERR "usbmidi: no memory for midi out-endpoint buffer\n");
kfree(ep);
return NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -