You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

751 lines
29 KiB

/*
Calf Box, an open source musical instrument.
Copyright (C) 2010-2013 Krzysztof Foltman
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#if USE_LIBUSB
#include "usbio_impl.h"
#include <errno.h>
////////////////////////////////////////////////////////////////////////////////
static const struct libusb_endpoint_descriptor *get_midi_endpoint(const struct libusb_interface_descriptor *asdescr, gboolean is_output)
{
for (int epi = 0; epi < asdescr->bNumEndpoints; epi++)
{
const struct libusb_endpoint_descriptor *ep = &asdescr->endpoint[epi];
if ((ep->bEndpointAddress >= 0x80) == !is_output)
return ep;
}
return NULL;
}
static const struct libusb_endpoint_descriptor *get_audio_output_endpoint(const struct libusb_interface_descriptor *asdescr, const struct libusb_endpoint_descriptor **sync_endpoint)
{
for (int epi = 0; epi < asdescr->bNumEndpoints; epi++)
{
const struct libusb_endpoint_descriptor *ep = &asdescr->endpoint[epi];
uint8_t ep_type = (ep->bmAttributes & 0xF);
if (ep->bEndpointAddress < 0x80 && (ep_type == 9 || ep_type == 13)) // output, isochronous, adaptive or synchronous
{
if (sync_endpoint)
*sync_endpoint = NULL;
return ep;
}
if (ep->bEndpointAddress < 0x80 && ep_type == 5) // output, isochronous, asynchronous
{
// Look for corresponding synch endpoint. It must be placed after the original output endpoint.
if (sync_endpoint && ep->bSynchAddress >= 0x81)
{
*sync_endpoint = NULL;
for(int epi2 = epi + 1; epi2 < asdescr->bNumEndpoints; epi2++)
{
const struct libusb_endpoint_descriptor *ep2 = &asdescr->endpoint[epi2];
if (ep2->bEndpointAddress == ep->bSynchAddress && (ep2->bmAttributes & 0xF) == 1) // input, isochronous, no sync
{
*sync_endpoint = ep2;
break;
}
}
}
return ep;
}
}
return NULL;
}
static void fill_endpoint_desc(struct usbio_endpoint_descriptor *epdesc, const struct libusb_endpoint_descriptor *ep)
{
epdesc->found = TRUE;
epdesc->interrupt = FALSE;
epdesc->bEndpointAddress = ep->bEndpointAddress;
epdesc->wMaxPacketSize = ep->wMaxPacketSize;
}
static gboolean fill_endpoint_by_address(const struct libusb_interface_descriptor *asdescr, uint8_t addr, struct usbio_endpoint_descriptor *epdesc)
{
epdesc->found = FALSE;
epdesc->interrupt = FALSE;
for (int epi = 0; epi < asdescr->bNumEndpoints; epi++)
{
const struct libusb_endpoint_descriptor *ep = &asdescr->endpoint[epi];
if (ep->bEndpointAddress == addr)
{
fill_endpoint_desc(epdesc, ep);
return TRUE;
}
}
return FALSE;
}
////////////////////////////////////////////////////////////////////////////////
struct usb_ut_descriptor_header
{
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
};
struct usb_class_specific_header
{
struct usb_ut_descriptor_header hdr;
uint8_t bcdADC[2];
uint8_t wTotalLengthLo, wTotalLengthHi;
uint8_t bInCollection;
uint8_t baInterfaceNr[0];
};
struct usbaudio_input_terminal_descriptor
{
uint8_t bTerminalID;
uint8_t wTerminalTypeLo, wTerminalTypeHi;
uint8_t bAssocTerminal;
uint8_t bNrChannels;
uint8_t wChannelConfigLo, wChannelConfigHi;
uint8_t iChannelNames;
uint8_t iTerminal;
};
struct usbaudio_output_terminal_descriptor
{
uint8_t bTerminalID;
uint8_t wTerminalTypeLo, wTerminalTypeHi;
uint8_t bAssocTerminal;
uint8_t bSourceID;
uint8_t iTerminal;
};
struct usbaudio_mixer_unit_descriptor1
{
uint8_t bUnitID;
uint8_t bNrInPins;
uint8_t baSourceID[0];
};
struct usbaudio_mixer_unit_descriptor2
{
uint8_t bNrChannels;
uint8_t wChannelConfigLo, wChannelConfigHi;
uint8_t bmControls[0];
// after the last index, 8-bit iMixer
};
struct usbaudio_selector_unit_descriptor1
{
uint8_t bUnitID;
uint8_t bNrInPins;
uint8_t baSourceID[0];
// after the last index, 8-bit iSelector
};
struct usbaudio_feature_unit_descriptor1
{
uint8_t bUnitID;
uint8_t bSourceID;
uint8_t bControlSize;
uint8_t bmaControls[0];
// after the last index, 8-bit iFeature
};
// Unit/Terminal Descriptor header from the spec
#include <stdio.h>
#if 0
// termt10.pdf
static const char *get_terminal_type_name(int type)
{
switch(type)
{
case 0x101: return "USB Streaming";
case 0x1FF: return "USB vendor specific";
case 0x201: return "Microphone";
case 0x202: return "Desktop microphone";
case 0x203: return "Personal microphone";
case 0x204: return "Omni-directional microphone";
case 0x205: return "Microphone array";
case 0x206: return "Processing microphone array";
case 0x301: return "Speaker";
case 0x302: return "Headphones";
case 0x303: return "Head-mounted display audio";
case 0x304: return "Desktop speaker";
case 0x305: return "Room speaker";
case 0x306: return "Communication speaker";
case 0x307: return "Low-frequency effects speaker";
case 0x400: return "Bi-directional Undefined";
case 0x401: return "Handset (handheld)";
case 0x402: return "Handset (head-mounted)";
case 0x403: return "Speakerphone";
case 0x404: return "Echo-suppressing speakerphone";
case 0x405: return "Echo-cancelling speakerphone";
case 0x501: return "Phone line";
case 0x502: return "Telephone";
case 0x503: return "Down line phone";
case 0x600: return "External Undefined";
case 0x601: return "Analog connector";
case 0x602: return "Digital audio interface";
case 0x603: return "Line connector";
case 0x604: return "Legacy audio";
case 0x605: return "S/PDIF";
case 0x606: return "1394 D/A Stream";
case 0x607: return "1394 D/V Stream Soundtrack";
case 0x700: return "Embedded undefined";
case 0x701: return "Level calibration noise source";
case 0x702: return "Equalization noise";
case 0x703: return "CD player";
case 0x704: return "DAT";
case 0x705: return "DCC";
case 0x706: return "MiniDisc";
case 0x707: return "Analog tape";
case 0x708: return "Phono";
case 0x709: return "VCR Audio";
case 0x70A: return "VideoDisc Audio";
case 0x70B: return "DVD Audio";
case 0x70C: return "TV Tuner Audio";
case 0x70D: return "Satellite Receiver Audio";
case 0x70E: return "Cable Tuner Audio";
case 0x70F: return "DSS Audio";
case 0x710: return "Radio Receiver";
case 0x711: return "Radio Transmitter";
case 0x712: return "Multitrack Recorder";
case 0x713: return "Synthesizer";
default:
return "Unknown";
}
}
#endif
static gboolean parse_audio_control_class(struct cbox_usb_audio_info *uai, const struct libusb_interface_descriptor *asdescr, gboolean other_config)
{
#if 0
if (asdescr->extra_length < 8)
{
g_warning("AudioControl interface descriptor length is %d, should be at least 8", (int)asdescr->extra_length);
return FALSE;
}
const struct usb_class_specific_header *extra = (const struct usb_class_specific_header *)asdescr->extra;
uint16_t wTotalLength = extra->wTotalLengthLo + 256 * extra->wTotalLengthHi;
if (wTotalLength > asdescr->extra_length)
{
g_warning("AudioControl interface descriptor total length is %d, but libusb value is %d", (int)wTotalLength, (int)asdescr->extra_length);
return FALSE;
}
if (extra->hdr.bDescriptorType != 36)
{
g_warning("AudioControl interface descriptor type is %d, but expected 36 (CS_INTERFACE)", (int)extra->hdr.bDescriptorType);
return FALSE;
}
if (extra->hdr.bDescriptorSubtype != 1)
{
g_warning("AudioControl interface descriptor type is %d, but expected 1 (HEADER)", (int)extra->hdr.bDescriptorSubtype);
return FALSE;
}
printf("Device %04x:%04x\n", uai->udi->vid, uai->udi->pid);
printf("hdrlen=%d dt=%d dst=%d ver=%02x%02x total len=%d\n", extra->hdr.bLength, extra->hdr.bDescriptorType, extra->hdr.bDescriptorSubtype, extra->bcdADC[0], extra->bcdADC[1], wTotalLength);
for(int i = 0; i < extra->bInCollection; i++)
{
printf("interface nr %d = %d\n", i, extra->baInterfaceNr[i]);
}
for(uint32_t pos = extra->hdr.bLength; pos < wTotalLength; pos += asdescr->extra[pos])
{
const struct usb_ut_descriptor_header *hdr = (const struct usb_ut_descriptor_header *)(asdescr->extra + pos);
if (hdr->bDescriptorType != 36)
{
g_warning("Skipping unit/terminal descriptor type %d,%d", (int)hdr->bDescriptorType, (int)hdr->bDescriptorSubtype);
continue;
}
printf("hdr %d,%d len %d\n", hdr->bDescriptorType, hdr->bDescriptorSubtype, hdr->bLength);
switch(hdr->bDescriptorSubtype)
{
case 2: // INPUT_TERMINAL
{
const struct usbaudio_input_terminal_descriptor *itd = (const struct usbaudio_input_terminal_descriptor *)(hdr + 1);
int wTerminalType = itd->wTerminalTypeHi * 256 + itd->wTerminalTypeLo;
printf("INPUT TERMINAL %d: type %04x (%s)\n", (int)itd->bTerminalID, wTerminalType, get_terminal_type_name(wTerminalType));
break;
}
case 3: // OUTPUT_TERMINAL
{
const struct usbaudio_output_terminal_descriptor *otd = (const struct usbaudio_output_terminal_descriptor *)(hdr + 1);
int wTerminalType = otd->wTerminalTypeHi * 256 + otd->wTerminalTypeLo;
printf("OUTPUT TERMINAL %d: type %04x (%s) source %d\n", (int)otd->bTerminalID, wTerminalType, get_terminal_type_name(wTerminalType), otd->bSourceID);
break;
}
case 4: // MIXER_UNIT
{
const struct usbaudio_mixer_unit_descriptor1 *mud = (const struct usbaudio_mixer_unit_descriptor1 *)(hdr + 1);
printf("MIXER UNIT %d\n", (int)mud->bUnitID);
for (int i = 0; i < mud->bNrInPins; i++)
{
printf("Input[%d] = %d\n", i, mud->baSourceID[i]);
}
break;
}
case 5: // SELECTOR_UNIT
{
const struct usbaudio_selector_unit_descriptor1 *sud = (const struct usbaudio_selector_unit_descriptor1 *)(hdr + 1);
printf("SELECTOR UNIT %d (%d pins)\n", (int)sud->bUnitID, sud->bNrInPins);
for (int i = 0; i < sud->bNrInPins; i++)
{
printf("Input[%d] = %d\n", i, sud->baSourceID[i]);
}
break;
}
case 6: // FEATURE_UNIT
{
static const char *features[] = {"Mute", "Volume", "Bass", "Mid", "Treble", "EQ", "AGC", "Delay", "Bass Boost", "Loudness" };
const struct usbaudio_feature_unit_descriptor1 *fud = (const struct usbaudio_feature_unit_descriptor1 *)(hdr + 1);
printf("FEATURE UNIT %d (source = %d, control size %d)\n", (int)fud->bUnitID, fud->bSourceID, fud->bControlSize);
for (int i = 0; i < fud->bControlSize * 8; i++)
{
if (i >= sizeof(features) / sizeof(features[0]))
break;
if (fud->bmaControls[i >> 3] & (1 << (i & 7)))
printf("Master %s\n", features[i]);
}
for (int ch = 1; ch < (hdr->bLength - 7) / fud->bControlSize; ch++)
{
int chofs = ch * fud->bControlSize;
for (int i = 0; i < fud->bControlSize * 8; i++)
{
if (i >= sizeof(features) / sizeof(features[0]))
break;
if (fud->bmaControls[(i >> 3) + chofs] & (1 << (i & 7)))
printf("Channel %d %s\n", ch, features[i]);
}
}
break;
}
default:
printf("Unsupported unit type %d\n", hdr->bDescriptorSubtype);
break;
}
}
#endif
return FALSE;
}
struct usbaudio_streaming_interface_descriptor_general
{
struct usb_ut_descriptor_header hdr; // 7, 36, 1
uint8_t bTerminalLink;
uint8_t bDelay;
uint8_t wFormatTagLo, wFormatTagHi;
};
struct usbaudio_streaming_interface_descriptor_format_type_pcm
{
struct usb_ut_descriptor_header hdr; // 7, 36, 2
uint8_t bFormatType;
uint8_t bNrChannels;
uint8_t bSubframeSize;
uint8_t bBitResolution;
uint8_t bSamFreqType;
uint8_t taSamFreq[0][3];
};
static gboolean parse_audio_class(struct cbox_usb_io_impl *uii, struct cbox_usb_audio_info *uai, const struct libusb_interface_descriptor *asdescr, gboolean other_config)
{
// omit alternate setting 0, as it's used to describe a 'standby' setting of the interface (no bandwidth)
if (asdescr->bAlternateSetting == 0)
return FALSE;
if (asdescr->extra_length < 7)
{
g_warning("AudioStreaming interface descriptor length is %d, should be at least 7", (int)asdescr->extra_length);
return FALSE;
}
const struct usbaudio_streaming_interface_descriptor_general *extra = (struct usbaudio_streaming_interface_descriptor_general *)asdescr->extra;
if (extra->hdr.bLength != 7 || extra->hdr.bDescriptorType != 36 || extra->hdr.bDescriptorSubtype != 1)
{
g_warning("The AudioStreaming descriptor does not start with the general descriptor (len %d, type %d, subtype %d)", (int)extra->hdr.bLength, (int)extra->hdr.bDescriptorType, (int)extra->hdr.bDescriptorSubtype);
return FALSE;
}
if (extra->wFormatTagLo != 1 || extra->wFormatTagHi != 0)
{
g_warning("The AudioStreaming descriptor does not describe a PCM device");
return FALSE;
}
const struct usbaudio_streaming_interface_descriptor_format_type_pcm *fmt = (struct usbaudio_streaming_interface_descriptor_format_type_pcm *)(asdescr->extra + extra->hdr.bLength);
if (fmt->hdr.bLength < 11 || ((fmt->hdr.bLength - 11) % 3) || fmt->hdr.bDescriptorType != 36 || fmt->hdr.bDescriptorSubtype != 2)
{
g_warning("The AudioStreaming descriptor does not have a format type descriptor after general descriptor (len %d, type %d, subtype %d)", (int)fmt->hdr.bLength, (int)fmt->hdr.bDescriptorType, (int)fmt->hdr.bDescriptorSubtype);
return FALSE;
}
// We need this to tell inputs from outputs - until I implement reasonable
// Audio Control support
const struct libusb_endpoint_descriptor *sync_ep = NULL;
const struct libusb_endpoint_descriptor *ep = get_audio_output_endpoint(asdescr, &sync_ep);
if (ep)
{
if (fmt->bBitResolution != uii->output_resolution * 8)
{
g_warning("Interface %d alternate setting %d does not support %d bit resolution, only %d", asdescr->bInterfaceNumber, asdescr->bAlternateSetting, uii->output_resolution * 8, fmt->bBitResolution);
return FALSE;
}
if (fmt->bSamFreqType)
{
gboolean found = FALSE;
for (int i = 0; i < fmt->bSamFreqType; i++)
{
int sf = fmt->taSamFreq[i][0] + 256 * fmt->taSamFreq[i][1] + 65536 * fmt->taSamFreq[i][2];
if (sf == uii->sample_rate)
{
found = TRUE;
break;
}
}
if (!found)
{
g_warning("Interface %d alternate setting %d does not support sample rate of %d Hz", asdescr->bInterfaceNumber, asdescr->bAlternateSetting, uii->sample_rate);
for (int i = 0; i < fmt->bSamFreqType; i++)
{
int sf = fmt->taSamFreq[i][0] + 256 * fmt->taSamFreq[i][1] + 65536 * fmt->taSamFreq[i][2];
g_warning("Sample rate[%d] = %d Hz", i, sf);
}
return FALSE;
}
}
// XXXKF in case of continuous sample rate, check the limits... assuming
// that there are any devices with continuous sample rate, that is.
if (other_config)
return TRUE;
if (uai->epdesc.found)
return FALSE;
if (sync_ep)
g_warning("Interface %d alt-setting %d endpoint %02x (synched via %02x) looks promising", asdescr->bInterfaceNumber, asdescr->bAlternateSetting, ep->bEndpointAddress, sync_ep->bEndpointAddress);
else
g_warning("Interface %d alt-setting %d endpoint %02x looks promising", asdescr->bInterfaceNumber, asdescr->bAlternateSetting, ep->bEndpointAddress);
uai->intf = asdescr->bInterfaceNumber;
uai->alt_setting = asdescr->bAlternateSetting;
fill_endpoint_desc(&uai->epdesc, ep);
uai->sync_protocol = (sync_ep != NULL) ? USBAUDIOSYNC_PROTOCOL_CLASS : USBAUDIOSYNC_PROTOCOL_NONE;
if (sync_ep)
fill_endpoint_desc(&uai->sync_epdesc, sync_ep);
else
uai->sync_epdesc.found = FALSE;
return TRUE;
}
return FALSE;
}
static gboolean parse_midi_class(struct cbox_usb_midi_info *umi, const struct libusb_interface_descriptor *asdescr, gboolean other_config, gboolean is_output)
{
const struct libusb_endpoint_descriptor *ep = get_midi_endpoint(asdescr, is_output);
if (!ep)
return FALSE;
if (other_config)
return TRUE;
struct usbio_endpoint_descriptor *epd = is_output ? &umi->epdesc_out : &umi->epdesc_in;
if (epd->found)
return FALSE;
umi->intf = asdescr->bInterfaceNumber;
umi->alt_setting = asdescr->bAlternateSetting;
fill_endpoint_desc(epd, ep);
return TRUE;
}
static gboolean inspect_device(struct cbox_usb_io_impl *uii, struct libusb_device *dev, uint16_t busdevadr, gboolean probe_only)
{
struct libusb_device_descriptor dev_descr;
int bus = busdevadr >> 8;
int devadr = busdevadr & 255;
if (0 != libusb_get_device_descriptor(dev, &dev_descr))
{
g_warning("USB device %03d:%03d - cannot get device descriptor (will retry)", bus, devadr);
return FALSE;
}
struct cbox_usb_device_info *udi = g_hash_table_lookup(uii->device_table, GINT_TO_POINTER(busdevadr));
if (!udi)
{
struct libusb_config_descriptor *cfg_descr = NULL;
if (0 != libusb_get_active_config_descriptor(dev, &cfg_descr))
return FALSE;
udi = malloc(sizeof(struct cbox_usb_device_info));
udi->dev = dev;
udi->handle = NULL;
udi->status = CBOX_DEVICE_STATUS_PROBING;
udi->active_config = cfg_descr->bConfigurationValue;
udi->bus = bus;
udi->devadr = devadr;
udi->busdevadr = busdevadr;
udi->vid = dev_descr.idVendor;
udi->pid = dev_descr.idProduct;
udi->configs_with_midi = 0;
udi->configs_with_audio = 0;
udi->is_midi = FALSE;
udi->is_audio = FALSE;
udi->last_probe_time = time(NULL);
udi->failures = 0;
g_hash_table_insert(uii->device_table, GINT_TO_POINTER(busdevadr), udi);
libusb_free_config_descriptor(cfg_descr);
}
else
if (udi->vid == dev_descr.idVendor && udi->pid == dev_descr.idProduct)
{
// device already open or determined to be
if (udi->status == CBOX_DEVICE_STATUS_OPENED ||
udi->status == CBOX_DEVICE_STATUS_UNSUPPORTED)
return FALSE;
// give up after 10 attempts to query or open the device
if (udi->failures > 10)
return FALSE;
// only do ~1 attempt per second
if (probe_only && time(NULL) == udi->last_probe_time)
return FALSE;
udi->last_probe_time = time(NULL);
}
struct cbox_usb_audio_info uainf;
cbox_usb_audio_info_init(&uainf, udi);
struct cbox_usb_midi_info uminf;
cbox_usb_midi_info_init(&uminf, udi);
gboolean is_audio = FALSE;
// printf("%03d:%03d Device %04X:%04X\n", bus, devadr, dev_descr.idVendor, dev_descr.idProduct);
for (int ci = 0; ci < (int)dev_descr.bNumConfigurations; ci++)
{
struct libusb_config_descriptor *cfg_descr = NULL;
// if this is not the current config, and another config with MIDI input
// has already been found, do not look any further
if (0 != libusb_get_config_descriptor(dev, ci, &cfg_descr))
{
udi->failures++;
g_warning("%03d:%03d - cannot get configuration descriptor (try %d)", bus, devadr, udi->failures);
return FALSE;
}
int cur_config = cfg_descr->bConfigurationValue;
gboolean other_config = cur_config != udi->active_config;
uint32_t config_mask = 0;
// XXXKF not sure about legal range for bConfigurationValue
if(cfg_descr->bConfigurationValue >= 0 && cfg_descr->bConfigurationValue < 32)
config_mask = 1 << cfg_descr->bConfigurationValue;
else
g_warning("Unexpected configuration value %d", cfg_descr->bConfigurationValue);
for (int ii = 0; ii < cfg_descr->bNumInterfaces; ii++)
{
const struct libusb_interface *idescr = &cfg_descr->interface[ii];
for (int as = 0; as < idescr->num_altsetting; as++)
{
const struct libusb_interface_descriptor *asdescr = &idescr->altsetting[as];
if (asdescr->bInterfaceClass == LIBUSB_CLASS_AUDIO && asdescr->bInterfaceSubClass == 1) // Audio control
{
if (parse_audio_control_class(&uainf, asdescr, other_config) && other_config)
udi->configs_with_audio |= config_mask;
}
else if (asdescr->bInterfaceClass == LIBUSB_CLASS_AUDIO && asdescr->bInterfaceSubClass == 2) // Audio streaming
{
if (parse_audio_class(uii, &uainf, asdescr, other_config) && other_config)
udi->configs_with_audio |= config_mask;
}
else if (asdescr->bInterfaceClass == LIBUSB_CLASS_AUDIO && asdescr->bInterfaceSubClass == 3) // MIDI streaming
{
if (parse_midi_class(&uminf, asdescr, other_config, FALSE) && other_config)
udi->configs_with_midi |= config_mask;
if (parse_midi_class(&uminf, asdescr, other_config, TRUE) && other_config)
udi->configs_with_midi |= config_mask;
uminf.protocol = USBMIDI_PROTOCOL_CLASS;
}
else if (udi->vid == 0x09e8 && udi->pid == 0x0062) // Akai MPD16
{
uminf.intf = asdescr->bInterfaceNumber;
uminf.alt_setting = asdescr->bAlternateSetting;
uminf.protocol = USBMIDI_PROTOCOL_MPD16;
fill_endpoint_by_address(asdescr, 0x82, &uminf.epdesc_in);
}
else if (udi->vid == 0x1235 && udi->pid == 0x000a) // Novation Nocturn
{
uminf.intf = asdescr->bInterfaceNumber;
uminf.alt_setting = asdescr->bAlternateSetting;
fill_endpoint_by_address(asdescr, 0x81, &uminf.epdesc_in);
fill_endpoint_by_address(asdescr, 0x02, &uminf.epdesc_out);
uminf.epdesc_in.interrupt = TRUE;
uminf.epdesc_out.interrupt = TRUE;
uminf.protocol = USBMIDI_PROTOCOL_NOCTURN;
}
}
}
libusb_free_config_descriptor(cfg_descr);
}
if (!uminf.epdesc_in.found && !uminf.epdesc_out.found && udi->configs_with_midi)
g_warning("%03d:%03d - MIDI port available on different configs: mask=0x%x", bus, devadr, udi->configs_with_midi);
if (uainf.epdesc.found) // Class-compliant USB audio device
is_audio = TRUE;
if (udi->vid == 0x13b2 && udi->pid == 0x0030) // Alesis Multimix 8
{
uainf.sync_protocol = USBAUDIOSYNC_PROTOCOL_MULTIMIX8; // not used later
is_audio = TRUE;
}
// All configs/interfaces/alts scanned, nothing interesting found -> mark as unsupported
udi->is_midi = uminf.epdesc_in.found || uminf.epdesc_out.found;
udi->is_audio = is_audio;
if (!udi->is_midi && !udi->is_audio)
{
udi->status = CBOX_DEVICE_STATUS_UNSUPPORTED;
return FALSE;
}
gboolean opened = FALSE;
struct libusb_device_handle *handle = NULL;
int err = libusb_open(dev, &handle);
if (0 != err)
{
g_warning("Cannot open device %03d:%03d: %s; errno = %s", bus, devadr, libusb_error_name(err), strerror(errno));
udi->failures++;
return FALSE;
}
if (probe_only)
{
libusb_close(handle);
// Make sure that the reconnection code doesn't bail out due to
// last_probe_time == now.
udi->last_probe_time = 0;
return udi->is_midi || udi->is_audio;
}
if (uminf.epdesc_in.found || uminf.epdesc_out.found)
{
g_debug("Found MIDI device %03d:%03d, trying to open", bus, devadr);
if (0 != usbio_open_midi_interface(uii, &uminf, handle))
opened = TRUE;
}
if (uainf.epdesc.found)
{
GError *error = NULL;
if (usbio_open_audio_interface(uii, &uainf, handle, &error))
{
// should have already been marked as opened by the MIDI code, but
// I might add the ability to disable some MIDI interfaces at some point
udi->status = CBOX_DEVICE_STATUS_OPENED;
opened = TRUE;
}
else
{
g_warning("Cannot open class-compliant USB audio output: %s", error->message);
g_error_free(error);
}
}
else if (udi->vid == 0x13b2 && udi->pid == 0x0030)
{
GError *error = NULL;
if (usbio_open_audio_interface_multimix(uii, bus, devadr, handle, &error))
{
// should have already been marked as opened by the MIDI code, but
// I might add the ability to disable some MIDI interfaces at some point
udi->status = CBOX_DEVICE_STATUS_OPENED;
opened = TRUE;
}
else
{
g_warning("Cannot open Alesis Multimix audio output: %s", error->message);
g_error_free(error);
}
}
if (!opened)
{
udi->failures++;
libusb_close(handle);
}
else
udi->handle = handle;
return opened;
}
gboolean usbio_scan_devices(struct cbox_usb_io_impl *uii, gboolean probe_only)
{
struct libusb_device **dev_list;
size_t i, num_devices;
gboolean added = FALSE;
gboolean removed = FALSE;
num_devices = libusb_get_device_list(probe_only ? uii->usbctx_probe : uii->usbctx, &dev_list);
uint16_t *busdevadrs = malloc(sizeof(uint16_t) * num_devices);
for (i = 0; i < num_devices; i++)
{
struct libusb_device *dev = dev_list[i];
int bus = libusb_get_bus_number(dev);
int devadr = libusb_get_device_address(dev);
busdevadrs[i] = (bus << 8) | devadr;
}
GList *prev_keys = g_hash_table_get_values(uii->device_table);
for (GList *p = prev_keys; p; p = p->next)
{
gboolean found = FALSE;
struct cbox_usb_device_info *udi = p->data;
for (i = 0; !found && i < num_devices; i++)
found = busdevadrs[i] == udi->busdevadr;
if (!found)
{
// Only specifically trigger removal if the device is ours
if (udi->status == CBOX_DEVICE_STATUS_OPENED)
{
g_message("Disconnected: %03d:%03d (%s)", udi->bus, udi->devadr, probe_only ? "probe" : "reconfigure");
removed = TRUE;
}
if (!probe_only)
usbio_forget_device(uii, udi);
}
}
g_list_free(prev_keys);
for (i = 0; i < num_devices; i++)
added = inspect_device(uii, dev_list[i], busdevadrs[i], probe_only) || added;
free(busdevadrs);
libusb_free_device_list(dev_list, 0);
return added || removed;
}
void usbio_forget_device(struct cbox_usb_io_impl *uii, struct cbox_usb_device_info *devinfo)
{
g_hash_table_remove(uii->device_table, GINT_TO_POINTER(devinfo->busdevadr));
for (GList *p = uii->midi_ports, *pNext = NULL; p; p = pNext)
{
pNext = p->next;
struct cbox_usb_midi_interface *umi = p->data;
if (umi->busdevadr == devinfo->busdevadr)
{
uii->midi_ports = g_list_delete_link(uii->midi_ports, p);
free(umi);
}
}
if (uii->handle_audiodev == devinfo->handle)
uii->handle_audiodev = NULL;
libusb_close(devinfo->handle);
free(devinfo);
}
#endif