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

📄 usb-midi.c

📁 USB_MIDI_DRIVER(for linux)
💻 C
📖 第 1 页 / 共 4 页
字号:

	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 + -