//////////////////////////// // SEARCH 4 USB DEVICE bool windozeHelpers::Search4UsbDevice( const uint16_t DeviceNum ) { HDEVINFO deviceInfo = 0; SP_DEVICE_INTERFACE_DATA interfaceData; std::string errMsg; const bool result = EnumerateDevice(DeviceNum, deviceInfo, interfaceData, errMsg); return( result ); }
void CAESinkALSA::EnumerateDevice(AEDeviceInfoList &list, const std::string &device, const std::string &description, snd_config_t *config) { snd_pcm_t *pcmhandle = NULL; if (!OpenPCMDevice(device, "", ALSA_MAX_CHANNELS, &pcmhandle, config)) return; snd_pcm_info_t *pcminfo; snd_pcm_info_alloca(&pcminfo); memset(pcminfo, 0, snd_pcm_info_sizeof()); int err = snd_pcm_info(pcmhandle, pcminfo); if (err < 0) { CLog::Log(LOGINFO, "CAESinkALSA - Unable to get pcm_info for \"%s\"", device.c_str()); snd_pcm_close(pcmhandle); } int cardNr = snd_pcm_info_get_card(pcminfo); CAEDeviceInfo info; info.m_deviceName = device; info.m_deviceType = AEDeviceTypeFromName(device); if (cardNr >= 0) { /* "HDA NVidia", "HDA Intel", "HDA ATI HDMI", "SB Live! 24-bit External", ... */ char *cardName; if (snd_card_get_name(cardNr, &cardName) == 0) info.m_displayName = cardName; if (info.m_deviceType == AE_DEVTYPE_HDMI && info.m_displayName.size() > 5 && info.m_displayName.substr(info.m_displayName.size()-5) == " HDMI") { /* We already know this is HDMI, strip it */ info.m_displayName.erase(info.m_displayName.size()-5); } /* "CONEXANT Analog", "USB Audio", "HDMI 0", "ALC889 Digital" ... */ std::string pcminfoName = snd_pcm_info_get_name(pcminfo); /* * Filter "USB Audio", in those cases snd_card_get_name() is more * meaningful already */ if (pcminfoName != "USB Audio") info.m_displayNameExtra = pcminfoName; if (info.m_deviceType == AE_DEVTYPE_HDMI) { /* replace, this was likely "HDMI 0" */ info.m_displayNameExtra = "HDMI"; int dev = snd_pcm_info_get_device(pcminfo); if (dev >= 0) { /* lets see if we can get ELD info */ snd_ctl_t *ctlhandle; std::stringstream sstr; sstr << "hw:" << cardNr; std::string strHwName = sstr.str(); if (snd_ctl_open_lconf(&ctlhandle, strHwName.c_str(), 0, config) == 0) { snd_hctl_t *hctl; if (snd_hctl_open_ctl(&hctl, ctlhandle) == 0) { snd_hctl_load(hctl); bool badHDMI = false; if (!GetELD(hctl, dev, info, badHDMI)) CLog::Log(LOGDEBUG, "CAESinkALSA - Unable to obtain ELD information for device \"%s\" (not supported by device, or kernel older than 3.2)", device.c_str()); /* snd_hctl_close also closes ctlhandle */ snd_hctl_close(hctl); if (badHDMI) { /* * Warn about disconnected devices, but keep them enabled * Detection can go wrong on Intel, Nvidia and on all * AMD (fglrx) hardware, so it is not safe to close those * handles */ CLog::Log(LOGDEBUG, "CAESinkALSA - HDMI device \"%s\" may be unconnected (no ELD data)", device.c_str()); } } else { snd_ctl_close(ctlhandle); } } } } else if (info.m_deviceType == AE_DEVTYPE_IEC958) { /* append instead of replace, pcminfoName is useful for S/PDIF */ if (!info.m_displayNameExtra.empty()) info.m_displayNameExtra += ' '; info.m_displayNameExtra += "S/PDIF"; } else if (info.m_displayNameExtra.empty()) { /* for USB audio, it gets a bit confusing as there is * - "SB Live! 24-bit External" * - "SB Live! 24-bit External, S/PDIF" * so add "Analog" qualifier to the first one */ info.m_displayNameExtra = "Analog"; } /* "default" is a device that will be used for all inputs, while * "@" will be mangled to front/default/surroundXX as necessary */ if (device == "@" || device == "default") { /* Make it "Default (whatever)" */ info.m_displayName = "Default (" + info.m_displayName + (info.m_displayNameExtra.empty() ? "" : " " + info.m_displayNameExtra + ")"); info.m_displayNameExtra = ""; } } else { /* virtual devices: "default", "pulse", ... */ /* description can be e.g. "PulseAudio Sound Server" - for hw devices it is * normally uninteresting, like "HDMI Audio Output" or "Default Audio Device", * so we only use it for virtual devices that have no better display name */ info.m_displayName = description; } snd_pcm_hw_params_t *hwparams; snd_pcm_hw_params_alloca(&hwparams); memset(hwparams, 0, snd_pcm_hw_params_sizeof()); /* ensure we can get a playback configuration for the device */ if (snd_pcm_hw_params_any(pcmhandle, hwparams) < 0) { CLog::Log(LOGINFO, "CAESinkALSA - No playback configurations available for device \"%s\"", device.c_str()); snd_pcm_close(pcmhandle); return; } /* detect the available sample rates */ for (unsigned int *rate = ALSASampleRateList; *rate != 0; ++rate) if (snd_pcm_hw_params_test_rate(pcmhandle, hwparams, *rate, 0) >= 0) info.m_sampleRates.push_back(*rate); /* detect the channels available */ int channels = 0; for (int i = ALSA_MAX_CHANNELS; i >= 1; --i) { /* Reopen the device if needed on the special "surroundXX" cases */ if (info.m_deviceType == AE_DEVTYPE_PCM && (i == 8 || i == 6 || i == 4)) OpenPCMDevice(device, "", i, &pcmhandle, config); if (snd_pcm_hw_params_test_channels(pcmhandle, hwparams, i) >= 0) { channels = i; break; } } if (device == "default" && channels == 2) { /* This looks like the ALSA standard default stereo dmix device, we * probably want to use "@" instead to get surroundXX. */ snd_pcm_close(pcmhandle); EnumerateDevice(list, "@", description, config); return; } CAEChannelInfo alsaChannels; for (int i = 0; i < channels; ++i) { if (!info.m_channels.HasChannel(ALSAChannelMap[i])) info.m_channels += ALSAChannelMap[i]; alsaChannels += ALSAChannelMap[i]; } /* remove the channels from m_channels that we cant use */ info.m_channels.ResolveChannels(alsaChannels); /* detect the PCM sample formats that are available */ for (enum AEDataFormat i = AE_FMT_MAX; i > AE_FMT_INVALID; i = (enum AEDataFormat)((int)i - 1)) { if (AE_IS_RAW(i) || i == AE_FMT_MAX) continue; snd_pcm_format_t fmt = AEFormatToALSAFormat(i); if (fmt == SND_PCM_FORMAT_UNKNOWN) continue; if (snd_pcm_hw_params_test_format(pcmhandle, hwparams, fmt) >= 0) info.m_dataFormats.push_back(i); } snd_pcm_close(pcmhandle); list.push_back(info); }
void CAESinkALSA::EnumerateDevicesEx(AEDeviceInfoList &list, bool force) { /* ensure that ALSA has been initialized */ snd_lib_error_set_handler(sndLibErrorHandler); if(!snd_config || force) { if(force) snd_config_update_free_global(); snd_config_update(); } snd_config_t *config; snd_config_copy(&config, snd_config); /* Always enumerate the default device. * Note: If "default" is a stereo device, EnumerateDevice() * will automatically add "@" instead to enable surroundXX mangling. * We don't want to do that if "default" can handle multichannel * itself (e.g. in case of a pulseaudio server). */ EnumerateDevice(list, "default", "", config); void **hints; if (snd_device_name_hint(-1, "pcm", &hints) < 0) { CLog::Log(LOGINFO, "CAESinkALSA - Unable to get a list of devices"); return; } std::string defaultDescription; for (void** hint = hints; *hint != NULL; ++hint) { char *io = snd_device_name_get_hint(*hint, "IOID"); char *name = snd_device_name_get_hint(*hint, "NAME"); char *desc = snd_device_name_get_hint(*hint, "DESC"); if ((!io || strcmp(io, "Output") == 0) && name && strcmp(name, "null") != 0) { std::string baseName = std::string(name); baseName = baseName.substr(0, baseName.find(':')); if (strcmp(name, "default") == 0) { /* added already, but lets get the description if we have one */ if (desc) defaultDescription = desc; } else if (baseName == "front") { /* Enumerate using the surroundXX mangling */ /* do not enumerate basic "front", it is already handled * by the default "@" entry added in the very beginning */ if (strcmp(name, "front") != 0) EnumerateDevice(list, std::string("@") + (name+5), desc ? desc : name, config); } /* Do not enumerate "default", it is already enumerated above. */ /* Do not enumerate the sysdefault or surroundXX devices, those are * always accompanied with a "front" device and it is handled above * as "@". The below devices will be automatically used if available * for a "@" device. */ /* Ubuntu has patched their alsa-lib so that "defaults.namehint.extended" * defaults to "on" instead of upstream "off", causing lots of unwanted * extra devices (many of which are not actually routed properly) to be * found by the enumeration process. Skip them as well ("hw", "dmix", * "plughw", "dsnoop"). */ else if (baseName != "default" && baseName != "sysdefault" && baseName != "surround40" && baseName != "surround41" && baseName != "surround50" && baseName != "surround51" && baseName != "surround71" && baseName != "hw" && baseName != "dmix" && baseName != "plughw" && baseName != "dsnoop") { EnumerateDevice(list, name, desc ? desc : name, config); } } free(io); free(name); free(desc); } snd_device_name_free_hint(hints); /* set the displayname for default device */ if (!list.empty() && list[0].m_deviceName == "default") { /* If we have one from a hint (DESC), use it */ if (!defaultDescription.empty()) list[0].m_displayName = defaultDescription; /* Otherwise use the discovered name or (unlikely) "Default" */ else if (list[0].m_displayName.empty()) list[0].m_displayName = "Default"; } /* lets check uniqueness, we may need to append DEV or CARD to DisplayName */ /* If even a single device of card/dev X clashes with Y, add suffixes to * all devices of both them, for clarity. */ /* clashing card names, e.g. "NVidia", "NVidia_2" */ std::set<std::string> cardsToAppend; /* clashing basename + cardname combinations, e.g. ("hdmi","Nvidia") */ std::set<std::pair<std::string, std::string> > devsToAppend; for (AEDeviceInfoList::iterator it1 = list.begin(); it1 != list.end(); ++it1) { for (AEDeviceInfoList::iterator it2 = it1+1; it2 != list.end(); ++it2) { if (it1->m_displayName == it2->m_displayName && it1->m_displayNameExtra == it2->m_displayNameExtra) { /* something needs to be done */ std::string cardString1 = GetParamFromName(it1->m_deviceName, "CARD"); std::string cardString2 = GetParamFromName(it2->m_deviceName, "CARD"); if (cardString1 != cardString2) { /* card name differs, add identifiers to all devices */ cardsToAppend.insert(cardString1); cardsToAppend.insert(cardString2); continue; } std::string devString1 = GetParamFromName(it1->m_deviceName, "DEV"); std::string devString2 = GetParamFromName(it2->m_deviceName, "DEV"); if (devString1 != devString2) { /* device number differs, add identifiers to all such devices */ devsToAppend.insert(std::make_pair(it1->m_deviceName.substr(0, it1->m_deviceName.find(':')), cardString1)); devsToAppend.insert(std::make_pair(it2->m_deviceName.substr(0, it2->m_deviceName.find(':')), cardString2)); continue; } /* if we got here, the configuration is really weird, just append the whole device string */ it1->m_displayName += " (" + it1->m_deviceName + ")"; it2->m_displayName += " (" + it2->m_deviceName + ")"; } } } for (std::set<std::string>::iterator it = cardsToAppend.begin(); it != cardsToAppend.end(); ++it) { for (AEDeviceInfoList::iterator itl = list.begin(); itl != list.end(); ++itl) { std::string cardString = GetParamFromName(itl->m_deviceName, "CARD"); if (cardString == *it) /* "HDA NVidia (NVidia)", "HDA NVidia (NVidia_2)", ... */ itl->m_displayName += " (" + cardString + ")"; } } for (std::set<std::pair<std::string, std::string> >::iterator it = devsToAppend.begin(); it != devsToAppend.end(); ++it) { for (AEDeviceInfoList::iterator itl = list.begin(); itl != list.end(); ++itl) { std::string baseName = itl->m_deviceName.substr(0, itl->m_deviceName.find(':')); std::string cardString = GetParamFromName(itl->m_deviceName, "CARD"); if (baseName == it->first && cardString == it->second) { std::string devString = GetParamFromName(itl->m_deviceName, "DEV"); /* "HDMI #0", "HDMI #1" ... */ itl->m_displayNameExtra += " #" + devString; } } } }
//////////////////////////// // FETCH USB DRIVER VERSION void windozeHelpers::FetchUsbDriverVersion( const uint16_t DeviceNum, std::string & version ) { HDEVINFO deviceInfo = 0; SP_DEVICE_INTERFACE_DATA interfaceData; std::string msg; if( !EnumerateDevice( DeviceNum, deviceInfo, interfaceData, msg ) ) { apgHelper::throwRuntimeException( __FILE__, msg, __LINE__, Apg::ErrorType_Connection ); } SP_DEVINFO_DATA deviceData; deviceData.cbSize = sizeof(SP_DEVINFO_DATA); if( !SetupDiEnumDeviceInfo( deviceInfo, DeviceNum, &deviceData ) ) { //getting the windows error message b4 the next winapi calls //nukes it out. then throw the error std::string errMsg = "SetupDiEnumDeviceInfo failed " + windozeHelpers::GetLastWinError(); SetupDiDestroyDeviceInfoList(deviceInfo); apgHelper::throwRuntimeException( __FILE__, errMsg , __LINE__,Apg::ErrorType_Connection ); } //from BOOL DumpDeviceDriverNodes in dump.cpp in //C:\WinDDK\6001.18002\src\setup\devcon SP_DEVINSTALL_PARAMS deviceInstallParams; SP_DRVINFO_DATA driverInfoData; ZeroMemory(&deviceInstallParams, sizeof(deviceInstallParams)); ZeroMemory(&driverInfoData, sizeof(driverInfoData)); driverInfoData.cbSize = sizeof(SP_DRVINFO_DATA); deviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS); if( !SetupDiGetDeviceInstallParams(deviceInfo, &deviceData, &deviceInstallParams) ) { std::string errMsg = "SetupDiGetDeviceInstallParams failed " + windozeHelpers::GetLastWinError(); SetupDiDestroyDeviceInfoList(deviceInfo); apgHelper::throwRuntimeException( __FILE__, errMsg , __LINE__,Apg::ErrorType_Connection ); } // // Set the flags that tell SetupDiBuildDriverInfoList to allow excluded drivers. // deviceInstallParams.FlagsEx |= (DI_FLAGSEX_INSTALLEDDRIVER | DI_FLAGSEX_ALLOWEXCLUDEDDRVS); if( !SetupDiSetDeviceInstallParams(deviceInfo, &deviceData, &deviceInstallParams) ) { std::string errMsg = "SetupDiGetDeviceInstallParams failed " + windozeHelpers::GetLastWinError(); SetupDiDestroyDeviceInfoList(deviceInfo); apgHelper::throwRuntimeException( __FILE__, errMsg , __LINE__,Apg::ErrorType_Connection ); } if( !SetupDiBuildDriverInfoList(deviceInfo, &deviceData, SPDIT_COMPATDRIVER) ) { std::string errMsg = "SetupDiBuildDriverInfoList failed " + windozeHelpers::GetLastWinError(); SetupDiDestroyDeviceInfoList(deviceInfo); apgHelper::throwRuntimeException( __FILE__, errMsg , __LINE__,Apg::ErrorType_Connection ); } // believe the member index should be set to 0 for all // devices...not 100% sure what this parameter is referring // to, the number of devices, the number of driver interfaces, // number of device interfaces???? // http://msdn.microsoft.com/en-us/library/windows/hardware/ff551018(v=vs.85).aspx if( !SetupDiEnumDriverInfo(deviceInfo, &deviceData, SPDIT_COMPATDRIVER, 0, &driverInfoData) ) { std::string errMsg = "SetupDiEnumDriverInfo failed " + windozeHelpers::GetLastWinError(); SetupDiDestroyDriverInfoList(deviceInfo, &deviceData, SPDIT_COMPATDRIVER); SetupDiDestroyDeviceInfoList(deviceInfo); apgHelper::throwRuntimeException( __FILE__, errMsg , __LINE__,Apg::ErrorType_Connection ); } ULARGE_INTEGER Version; Version.QuadPart = driverInfoData.DriverVersion; std::stringstream ss; ss << HIWORD(Version.HighPart) << "."; ss << LOWORD(Version.HighPart) << "."; ss << HIWORD(Version.LowPart) << "."; ss << LOWORD(Version.LowPart); version = ss.str(); SetupDiDestroyDriverInfoList(deviceInfo, &deviceData, SPDIT_COMPATDRIVER); SetupDiDestroyDeviceInfoList(deviceInfo); }
//////////////////////////// // FETCH USB DEVICE PATH void windozeHelpers::FetchUsbDevicePath( const uint16_t DeviceNum, CString & path ) { //most of the code for this function came from //http://www.osronline.com/article.cfm?id=532 HDEVINFO deviceInfo = 0; SP_DEVICE_INTERFACE_DATA interfaceData; std::string msg; if( !EnumerateDevice(DeviceNum, deviceInfo, interfaceData, msg) ) { apgHelper::throwRuntimeException( __FILE__, msg, __LINE__, Apg::ErrorType_Connection ); } // // Get information about the found device interface. We don't // know how much memory to allocate to get this information, so // we will ask by passing in a null buffer and location to // receive the size of the buffer needed. // ULONG requiredLength=0; BOOL result = SetupDiGetDeviceInterfaceDetail(deviceInfo, &interfaceData, NULL, 0, &requiredLength, NULL); if(!requiredLength) { SetupDiDestroyDeviceInfoList(deviceInfo); std::string errMsg = "Failed to get device memory size. Last error = " + windozeHelpers::GetLastWinError(); apgHelper::throwRuntimeException( __FILE__, errMsg, __LINE__, Apg::ErrorType_Connection ); } // // Okay, we got a size back, so let's allocate memory // for the interface detail information we want. // PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA) LocalAlloc(LMEM_FIXED,requiredLength); if( detailData == 0) { SetupDiDestroyDeviceInfoList(deviceInfo); std::string errMsg = "LocalAlloc failed. Last error = " + windozeHelpers::GetLastWinError(); apgHelper::throwRuntimeException( __FILE__, errMsg, __LINE__, Apg::ErrorType_Connection ); } detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); ULONG length = requiredLength; result = SetupDiGetDeviceInterfaceDetail(deviceInfo, &interfaceData, detailData, length, &requiredLength, NULL); if( !result ) { SetupDiDestroyDeviceInfoList(deviceInfo); LocalFree(detailData); std::string errMsg = "SetupDiGetDeviceInterfaceDetail error " + windozeHelpers::GetLastWinError(); apgHelper::throwRuntimeException( __FILE__, errMsg, __LINE__, Apg::ErrorType_Connection ); } //store the path path = detailData->DevicePath; SetupDiDestroyDeviceInfoList(deviceInfo); LocalFree(detailData); }