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

📄 usb-midi.c

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

	ep->urb = usb_alloc_urb(0, GFP_KERNEL); /* no ISO */
	if ( !ep->urb ) {
		printk(KERN_ERR "usbmidi: no memory for midi out-endpoint urb\n");
		kfree(ep->buf);
		kfree(ep);
		return NULL;
	}

	ep->bufSize       = bufSize;
	/* ep->bufWrPtr      = 0; */

	init_waitqueue_head(&ep->wait);

	return ep;
}


static int remove_midi_out_endpoint( struct midi_out_endpoint *mout )
{
	usb_kill_urb( mout->urb );
	usb_free_urb( mout->urb );
	kfree( mout->buf );
	kfree( mout );

	return 0;
}


/** Returns a filled usb_mididev structure, registered as a Linux MIDI device.
 *
 * Returns null if memory is not available or the device cannot be registered.
 * Called by allocUsbMidiDev();
 *
 **/
static struct usb_mididev *allocMidiDev(
	struct usb_midi_state *s,
	struct midi_in_endpoint *min,
	struct midi_out_endpoint *mout,
	int inCableId,
	int outCableId )
{
	struct usb_mididev *m;

	m = (struct usb_mididev *)kmalloc(sizeof(struct usb_mididev), GFP_KERNEL);
	if (!m) {
		printk(KERN_ERR "usbmidi: no memory for midi device\n");
		return NULL;
	}

	memset(m, 0, sizeof(struct usb_mididev));

	if ((m->dev_midi = register_sound_midi(&usb_midi_fops, -1)) < 0) {
		printk(KERN_ERR "usbmidi: cannot register midi device\n");
		kfree(m);
		return NULL;
	}

	m->midi               = s;
	/* m->open_mode          = 0; */

	if ( min ) {
		m->min.ep             = min;
		m->min.ep->usbdev     = s->usbdev;
		m->min.cableId        = inCableId;
	}
	/* m->min.bufPtr         = 0; */
	/* m->min.bufRemains     = 0; */

	if ( mout ) {
		m->mout.ep            = mout;
		m->mout.ep->usbdev    = s->usbdev;
		m->mout.cableId       = outCableId;
	}
	/* m->mout.bufPtr        = 0; */
	/* m->mout.bufRemains    = 0; */
	/* m->mout.isInExclusive = 0; */
	/* m->mout.lastEvent     = 0; */

	m->singlebyte         = singlebyte;

	return m;
}


static void release_midi_device( struct usb_midi_state *s )
{
	struct usb_mididev *m;
	struct midi_in_endpoint *min;
	struct midi_out_endpoint *mout;

	if ( s->count > 0 ) {
		up(&open_sem);
		return;
	}
	up( &open_sem );
	wake_up( &open_wait );

	while(!list_empty(&s->inEndpointList)) {
		min = list_entry(s->inEndpointList.next, struct midi_in_endpoint, list);
		list_del(&min->list);
		remove_midi_in_endpoint(min);
	}

	while(!list_empty(&s->outEndpointList)) {
		mout = list_entry(s->outEndpointList.next, struct midi_out_endpoint, list);
		list_del(&mout->list);
		remove_midi_out_endpoint(mout);
	}

	while(!list_empty(&s->midiDevList)) {
		m = list_entry(s->midiDevList.next, struct usb_mididev, list);
		list_del(&m->list);
		kfree(m);
	}

	kfree(s);

	return;
}


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

/** Utility routine to find a descriptor in a dump of many descriptors.
 * Returns start of descriptor or NULL if not found. 
 * descStart pointer to list of interfaces.
 * descLength length (in bytes) of dump
 * after (ignored if NULL) this routine returns only descriptors after "after"
 * dtype (mandatory) The descriptor type.
 * iface (ignored if -1) returns descriptor at/following given interface
 * altSetting (ignored if -1) returns descriptor at/following given altSetting
 *
 *
 *  Called by parseDescriptor(), find_csinterface_descriptor();
 *
 */
static void *find_descriptor( void *descStart, unsigned int descLength, void *after, unsigned char dtype, int iface, int altSetting )
{
	unsigned char *p, *end, *next;
	int interfaceNumber = -1, altSet = -1;

	p = descStart;
	end = p + descLength;
	for( ; p < end; ) {
		if ( p[0] < 2 )
			return NULL;
		next = p + p[0];
		if ( next > end )
			return NULL;
		if ( p[1] == USB_DT_INTERFACE ) {
			if ( p[0] < USB_DT_INTERFACE_SIZE )
				return NULL;
			interfaceNumber = p[2];
			altSet = p[3];
		}
		if ( p[1] == dtype &&
		     ( !after || ( p > (unsigned char *)after) ) &&
		     ( ( iface == -1) || (iface == interfaceNumber) ) &&
		     ( (altSetting == -1) || (altSetting == altSet) )) {
			return p;
		}
		p = next;
	}
	return NULL;
}

/** Utility to find a class-specific interface descriptor.
 *  dsubtype is a descriptor subtype
 *  Called by parseDescriptor();
 **/
static void *find_csinterface_descriptor(void *descStart, unsigned int descLength, void *after, u8 dsubtype, int iface, int altSetting)
{
	unsigned char *p;
  
	p = find_descriptor( descStart, descLength, after, USB_DT_CS_INTERFACE, iface, altSetting );
	while ( p ) {
		if ( p[0] >= 3 && p[2] == dsubtype )
			return p;
		p = find_descriptor( descStart, descLength, p, USB_DT_CS_INTERFACE, 
				     iface, altSetting );
	}
	return NULL;
}


/** The magic of making a new usb_midi_device from config happens here.
 *
 * The caller is responsible for free-ing this return value (if not NULL).
 *
 **/
static struct usb_midi_device *parse_descriptor( struct usb_device *d, unsigned char *buffer, int bufSize, unsigned int ifnum , unsigned int altSetting, int quirks)
{
	struct usb_midi_device *u;
	unsigned char *p1;
	unsigned char *p2;
	unsigned char *next;
	int iep, oep;
	int length;
	unsigned long longBits;
	int pins, nbytes, offset, shift, jack;
#ifdef HAVE_JACK_STRINGS
	/** Jacks can have associated names.  **/
	unsigned char jack2string[256];
#endif

	u = NULL;
	/* find audiocontrol interface */
	p1 = find_csinterface_descriptor( buffer, bufSize, NULL,
					  MS_HEADER, ifnum, altSetting);

	if ( !p1 ) {
		goto error_end;
	}

	if ( p1[0] < MS_HEADER_LENGTH ) {
		goto error_end;
	}

	/* Assume success. Since the device corresponds to USB-MIDI spec, we assume
	   that the rest of the USB 2.0 spec is obeyed. */

	u = (struct usb_midi_device *)kmalloc( sizeof(struct usb_midi_device), GFP_KERNEL );
	if ( !u ) {
		return NULL;
	}
	u->deviceName = NULL;
	u->idVendor = le16_to_cpu(d->descriptor.idVendor);
	u->idProduct = le16_to_cpu(d->descriptor.idProduct);
	u->interface = ifnum;
	u->altSetting = altSetting;
	u->in[0].endpoint = -1;
	u->in[0].cableId = -1;
	u->out[0].endpoint = -1;
	u->out[0].cableId = -1;


	printk(KERN_INFO "usb-midi: Found MIDIStreaming device corresponding to Release %d.%02d of spec.\n",
	       (p1[4] >> 4) * 10 + (p1[4] & 0x0f ),
	       (p1[3] >> 4) * 10 + (p1[3] & 0x0f )
		);

	length = p1[5] | (p1[6] << 8);

#ifdef HAVE_JACK_STRINGS
	memset(jack2string, 0, sizeof(unsigned char) * 256);
#endif

	length -= p1[0];
	for (p2 = p1 + p1[0]; length > 0; p2 = next) {
		next = p2 + p2[0];
		length -= p2[0];

		if (p2[0] < 2 )
			break;
		if (p2[1] != USB_DT_CS_INTERFACE)
			break;
		if (p2[2] == MIDI_IN_JACK && p2[0] >= 6 ) {
			jack = p2[4];
#ifdef HAVE_JACK_STRINGS
			jack2string[jack] = p2[5];
#endif
			printk(KERN_INFO "usb-midi: Found IN Jack 0x%02x %s\n",
			       jack, (p2[3] == EMBEDDED_JACK)?"EMBEDDED":"EXTERNAL" );
		} else if ( p2[2] == MIDI_OUT_JACK && p2[0] >= 6) {
			pins = p2[5];
			if ( p2[0] < (6 + 2 * pins) )
				continue;
			jack = p2[4];
#ifdef HAVE_JACK_STRINGS
			jack2string[jack] = p2[5 + 2 * pins];
#endif
			printk(KERN_INFO "usb-midi: Found OUT Jack 0x%02x %s, %d pins\n",
			       jack, (p2[3] == EMBEDDED_JACK)?"EMBEDDED":"EXTERNAL", pins );
		} else if ( p2[2] == ELEMENT_DESCRIPTOR  && p2[0]  >= 10) {
			pins = p2[4];
			if ( p2[0] < (9 + 2 * pins ) )
				continue;
			nbytes = p2[8 + 2 * pins ];
			if ( p2[0] < (10 + 2 * pins + nbytes) )
				continue;
			longBits = 0L;
			for ( offset = 0, shift = 0; offset < nbytes && offset < 8; offset ++, shift += 8) {
				longBits |= ((long)(p2[9 + 2 * pins + offset])) << shift;
			}
			jack = p2[3];
#ifdef HAVE_JACK_STRINGS
			jack2string[jack] = p2[9 + 2 * pins + nbytes];
#endif
			printk(KERN_INFO "usb-midi: Found ELEMENT 0x%02x, %d/%d pins in/out, bits: 0x%016lx\n",
			       jack, pins, (int)(p2[5 + 2 * pins]), (long)longBits );
		} else {
		}
	}

	iep=0;
	oep=0;

	if (quirks==0) {
		/* MIDISTREAM */
		p2 = NULL;
		for (p1 = find_descriptor(buffer, bufSize, NULL, USB_DT_ENDPOINT,
					  ifnum, altSetting ); p1; p1 = next ) {
			next = find_descriptor(buffer, bufSize, p1, USB_DT_ENDPOINT,
					       ifnum, altSetting ); 
			p2 = find_descriptor(buffer, bufSize, p1, USB_DT_CS_ENDPOINT,
					     ifnum, altSetting ); 

			if ( p2 && next && ( p2 > next ) )
				p2 = NULL;

			if ( p1[0] < 9 || !p2 || p2[0] < 4 )
				continue;

			if ( (p1[2] & 0x80) == 0x80 ) {
				if ( iep < 15 ) {
					pins = p2[3]; /* not pins -- actually "cables" */
					if ( pins > 16 )
						pins = 16;
					u->in[iep].endpoint = p1[2];
					u->in[iep].cableId = ( 1 << pins ) - 1;
					if ( u->in[iep].cableId )
						iep ++;
					if ( iep < 15 ) {
						u->in[iep].endpoint = -1;
						u->in[iep].cableId = -1;
					}
				}
			} else {
				if ( oep < 15 ) {
					pins = p2[3]; /* not pins -- actually "cables" */
					if ( pins > 16 )
						pins = 16;
					u->out[oep].endpoint = p1[2];
					u->out[oep].cableId = ( 1 << pins ) - 1;
					if ( u->out[oep].cableId )
						oep ++;
					if ( oep < 15 ) {
						u->out[oep].endpoint = -1;
						u->out[oep].cableId = -1;
					}
				}
			}
	
		}
	} else if (quirks==1) {
		/* YAMAHA quirks */
		for (p1 = find_descriptor(buffer, bufSize, NULL, USB_DT_ENDPOINT,
					  ifnum, altSetting ); p1; p1 = next ) {
			next = find_descriptor(buffer, bufSize, p1, USB_DT_ENDPOINT,
					       ifnum, altSetting ); 
	
			if ( p1[0] < 7 )
				continue;

			if ( (p1[2] & 0x80) == 0x80 ) {
				if ( iep < 15 ) {
					pins = iep+1;
					if ( pins > 16 )
						pins = 16;
					u->in[iep].endpoint = p1[2];
					u->in[iep].cableId = ( 1 << pins ) - 1;
					if ( u->in[iep].cableId )
						iep ++;
					if ( iep < 15 ) {
						u->in[iep].endpoint = -1;
						u->in[iep].cableId = -1;
					}
				}
			} else {
				if ( oep < 15 ) {
					pins = oep+1;
					if ( pins > 16 )
						pins = 16;
					u->out[oep].endpoint = p1[2];
					u->out[oep].cableId = ( 1 << pins ) - 1;
					if ( u->out[oep].cableId )
						oep ++;
					if ( oep < 15 ) {
						u->out[oep].endpoint = -1;
						u->out[oep].cableId = -1;
					}
				}
			}
	
		}
	}

	if ( !iep && ! oep ) {
		goto error_end;
	}

	return u;

error_end:
	kfree(u);
	return NULL;
}

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

/** Returns number between 0 and 16.
 *
 **/
static int on_bits( unsigned short v )
{
	int i;
	int ret=0;

	for ( i=0 ; i<16 ; i++ ) {
		if ( v & (1<<i) )
			ret++;
	}

	return ret;
}


/** USB-device will be interrogated for altSetting.
 *
 * Returns negative on error.
 * Called by allocUsbMidiDev();
 *
 **/

static int get_alt_setting( struct usb_device *d, int ifnum )
{
	int alts, alt=0;
	struct usb_interface *iface;
	struct usb_host_interface *interface;
	struct usb_endpoint_descriptor *ep;
	int epin, epout;
	int i;

	iface = usb_ifnum_to_if( d, ifnum );
	alts = iface->num_altsetting;

	for ( alt=0 ; alt<alts ; alt++ ) {
		interface = &iface->altsetting[alt];
		epin = -1;
		epout = -1;

		for ( i=0 ; i<interface->desc.bNumEndpoints ; i++ ) {
			ep = &interface->endpoint[i].desc;
			if ( (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK ) {
				continue;
			}
			if ( (ep->bEndpointAddress & USB_DIR_IN) && epin < 0 ) {
				epin = i;
			} else if ( epout < 0 ) {
				epout = i;
			}
			if ( epin >= 0 && epout >= 0 ) {
				return interface->desc.bAlternateSetting;
			}
		}
	}

	return -ENODEV;
}


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


/** Returns 0 if successful in allocating and registering internal structures.
 * Returns negative on failure.
 * Calls allocMidiDev which additionally registers /dev/midiXX devices.
 * Writes messages on success to indicate which /dev/midiXX is which physical
 * endpoint.
 *
 **/
static int alloc_usb_midi_device( struct usb_device *d, struct usb_midi_state *s, struct usb_midi_device *u )
{
	struct usb_mididev **mdevs=NULL;
	struct midi_in_endpoint *mins[15], *min;
	struct midi_out_endpoint *mouts[15], *mout;
	int inDevs=0, outDevs=0;
	int inEndpoints=0, outEndpoints=0;
	int inEndpoint, outEndpoint;
	int inCableId, outCableId;
	int i;
	int devices = 0;
	int alt = 0;

	/* Obtain altSetting or die.. */
	alt = u->altSetting;
	if ( alt < 0 ) {
		alt = get_alt_setting( d, u->interface );
	}
	if ( alt < 0 )
		return -ENXIO;

	/* Configure interface */
	if ( usb_set_interface( d, u->interface, alt ) < 0 ) {
		return -ENXIO;
	}

	for ( i = 0 ; i < 15 ; i++ ) {
		mins[i] = NULL;
		mouts[i] = NULL;
	}

	/* Begin Allocation */
	while( inEndpoints < 15
	       && inDevs < maxdevices
	       && u->in[inEndpoints].cableId >= 0 ) {
		inDevs += on_bits((unsigned short)u->in[inEndpoints].cableId);
		mins[inEndpoints] = alloc_midi_in_endpoint( d, u->in[inEndpoints].endpoint );
		if ( mins[inEndpoints] == NULL )
			goto error_end;
		inEndpoints++;
	}

	while( outEndpoints < 15
	       && outDevs < maxdevices
	       && u->out[outEndpoints].cableId >= 0 ) {
		outDevs += on_bits((unsigned short)u->out[outEndpoints].cableId);
		mouts[outEndpoints] = alloc_midi_out_endpoint( d, u->out[outEndpoints].endpoint );
		if ( mouts[outEndpoints] == NULL )
			goto error_end;
		outEndpoints++;
	}

	devices = inDevs > outDevs ? inDevs : outDevs;
	devices = maxdevices > devices ? devices : maxdevices;

	/* obtain space for device name (iProduct) if not known. */
	if ( ! u->deviceName ) {
		mdevs = (struct usb_mididev **)
			kmalloc(sizeof(struct usb_mididevs *)*devices
				+ sizeof(char) * 256, GFP_KERNEL);
	} else {
		mdevs = (struct usb_mididev **)
			kmalloc(sizeof(struct usb_mididevs *)*devices, GFP_KERNEL);
	}

	if ( !mdevs ) {
		/* devices = 0; */

⌨️ 快捷键说明

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