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