📄 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 list_head *devs, *mdevs; 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); for (devs = mididevs.next; devs != &mididevs; devs = devs->next) { s = list_entry(devs, struct usb_midi_state, mididev); for (mdevs = s->midiDevList.next; mdevs != &s->midiDevList; mdevs = mdevs->next) { m = list_entry(mdevs, struct usb_mididev, 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_unlink_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_unlink_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_unlink_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 ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -