Example #1
0
//////////////////////////// 
//  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 );
}
Example #2
0
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);
}
Example #3
0
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;
      }
    }
  }
}
Example #4
0
//////////////////////////// 
// 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);

}
Example #5
0
//////////////////////////// 
// 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);

}