[FreeBSD] update adapter detection
[deb_libcec.git] / src / lib / adapter / Pulse-Eight / USBCECAdapterDetection.cpp
index 9789bf70eb150376bf61ddbd463b5122e3da5ac2..58bf4a562abd17697a85c15e5fe6bf99e2d237e9 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the libCEC(R) library.
  *
- * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited.  All rights reserved.
+ * libCEC(R) is Copyright (C) 2011-2013 Pulse-Eight Limited.  All rights reserved.
  * libCEC(R) is an original work, containing original code.
  *
  * libCEC(R) is a trademark of Pulse-Eight Limited.
 #elif defined(__WINDOWS__)
 #pragma comment(lib, "advapi32.lib")
 #pragma comment(lib, "setupapi.lib")
+#pragma comment(lib, "cfgmgr32.lib")
 #include <setupapi.h>
+#include <cfgmgr32.h>
 
 // the virtual COM port only shows up when requesting devices with the raw device guid!
-static GUID USB_RAW_GUID =  { 0xA5DCBF10, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } };
+static GUID USB_RAW_GUID = { 0xA5DCBF10, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } };
+static GUID USB_CDC_GUID = { 0x4D36E978, 0xE325, 0x11CE, { 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18 } };
+
 #elif defined(HAVE_LIBUDEV)
 #include <dirent.h>
 #include <poll.h>
@@ -57,6 +61,8 @@ extern "C" {
 #include <libudev.h>
 }
 #elif defined(__FreeBSD__)
+#include <sys/param.h>
+#include <sys/sysctl.h>
 #include <stdio.h>
 #include <unistd.h>
 #endif
@@ -126,7 +132,79 @@ bool CUSBCECAdapterDetection::CanAutodetect(void)
 #endif
 }
 
-uint8_t CUSBCECAdapterDetection::FindAdapters(cec_adapter *deviceList, uint8_t iBufSize, const char *strDevicePath /* = NULL */)
+#if defined(__WINDOWS__)
+static bool GetComPortFromHandle(HDEVINFO hDevHandle, PSP_DEVINFO_DATA devInfoData, char* strPortName, unsigned int iSize)
+{
+  bool bReturn(false);
+  TCHAR strRegPortName[256];
+  strRegPortName[0] = _T('\0');
+  DWORD dwSize = sizeof(strRegPortName);
+  DWORD dwType = 0;
+
+  HKEY hDeviceKey = SetupDiOpenDevRegKey(hDevHandle, devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
+  if (!hDeviceKey)
+    return bReturn;
+
+  // locate the PortName
+  if ((RegQueryValueEx(hDeviceKey, _T("PortName"), NULL, &dwType, reinterpret_cast<LPBYTE>(strRegPortName), &dwSize) == ERROR_SUCCESS) &&
+      (dwType == REG_SZ) &&
+      _tcslen(strRegPortName) > 3 &&
+      _tcsnicmp(strRegPortName, _T("COM"), 3) == 0 &&
+      _ttoi(&(strRegPortName[3])) > 0)
+  {
+    // return the port name
+    snprintf(strPortName, iSize, "%s", strRegPortName);
+    bReturn = true;
+  }
+
+  RegCloseKey(hDeviceKey);
+
+  return bReturn;
+}
+
+static bool FindComPortForComposite(const char* strLocation, char* strPortName, unsigned int iSize)
+{
+  bool bReturn(false);
+
+  // find all devices of the CDC class
+  HDEVINFO hDevHandle = SetupDiGetClassDevs(&USB_CDC_GUID, NULL, NULL, DIGCF_PRESENT);
+  if (hDevHandle == INVALID_HANDLE_VALUE)
+    return bReturn;
+
+  // check all devices, whether they match the location or not
+  char strId[512];
+  bool bGetNext(true);
+  for (int iPtr = 0; !bReturn && bGetNext && iPtr < 1024 ; iPtr++)
+  {
+    strId[0] = 0;
+
+    SP_DEVINFO_DATA devInfoData;
+    devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
+
+    // no more devices
+    if (!SetupDiEnumDeviceInfo(hDevHandle, iPtr, &devInfoData))
+      bGetNext = false;
+    else
+    {
+      // check if the location of the _parent_ device matches
+      DEVINST parentDevInst;
+      if (CM_Get_Parent(&parentDevInst, devInfoData.DevInst, 0) == CR_SUCCESS)
+      {
+        CM_Get_Device_ID(parentDevInst, strId, 512, 0);
+
+        // match
+        if (!strncmp(strId, strLocation, strlen(strLocation)))
+          bReturn = GetComPortFromHandle(hDevHandle, &devInfoData, strPortName, iSize);
+      }
+    }
+  }
+
+  SetupDiDestroyDeviceInfoList(hDevHandle);
+  return bReturn;
+}
+#endif
+
+uint8_t CUSBCECAdapterDetection::FindAdapters(cec_adapter_descriptor *deviceList, uint8_t iBufSize, const char *strDevicePath /* = NULL */)
 {
   uint8_t iFound(0);
 
@@ -186,12 +264,19 @@ uint8_t CUSBCECAdapterDetection::FindAdapters(cec_adapter *deviceList, uint8_t i
             if (!strDevicePath || !strcmp(bsdPath, strDevicePath))
             {
               // on darwin, the device path is the same as the comm path.
-              snprintf(deviceList[iFound  ].path, sizeof(deviceList[iFound].path), "%s", bsdPath);
-              snprintf(deviceList[iFound++].comm, sizeof(deviceList[iFound].path), "%s", bsdPath);
+              if (iFound == 0 || strcmp(deviceList[iFound-1].strComName, bsdPath))
+              {
+                snprintf(deviceList[iFound].strComPath, sizeof(deviceList[iFound].strComPath), "%s", bsdPath);
+                snprintf(deviceList[iFound].strComName, sizeof(deviceList[iFound].strComName), "%s", bsdPath);
+                deviceList[iFound].iVendorId = iVendor;
+                deviceList[iFound].iProductId = iProduct;
+                deviceList[iFound].adapterType = ADAPTERTYPE_P8_EXTERNAL; // will be overridden when not doing a "quick scan" by the actual type
+                iFound++;
+              }
             }
           }
         }
-             IOObjectRelease(serialService);
+        IOObjectRelease(serialService);
       }
     }
     IOObjectRelease(serialPortIterator);
@@ -232,10 +317,14 @@ uint8_t CUSBCECAdapterDetection::FindAdapters(cec_adapter *deviceList, uint8_t i
       if (!strDevicePath || !strcmp(strPath.c_str(), strDevicePath))
       {
         CStdString strComm(strPath);
-        if (FindComPort(strComm))
+        if (FindComPort(strComm) && (iFound == 0 || strcmp(deviceList[iFound-1].strComName, strComm.c_str())))
         {
-          snprintf(deviceList[iFound  ].path, sizeof(deviceList[iFound].path), "%s", strPath.c_str());
-          snprintf(deviceList[iFound++].comm, sizeof(deviceList[iFound].path), "%s", strComm.c_str());
+          snprintf(deviceList[iFound].strComPath, sizeof(deviceList[iFound].strComPath), "%s", strPath.c_str());
+          snprintf(deviceList[iFound].strComName, sizeof(deviceList[iFound].strComName), "%s", strComm.c_str());
+          deviceList[iFound].iVendorId = iVendor;
+          deviceList[iFound].iProductId = iProduct;
+          deviceList[iFound].adapterType = ADAPTERTYPE_P8_EXTERNAL; // will be overridden when not doing a "quick scan" by the actual type
+          iFound++;
         }
       }
     }
@@ -255,6 +344,7 @@ uint8_t CUSBCECAdapterDetection::FindAdapters(cec_adapter *deviceList, uint8_t i
   SP_DEVINFO_DATA devInfoData;
   devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
 
+  // find all devices
   if ((hDevHandle = SetupDiGetClassDevs(&USB_RAW_GUID, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE)) == INVALID_HANDLE_VALUE)
     return iFound;
 
@@ -270,6 +360,7 @@ uint8_t CUSBCECAdapterDetection::FindAdapters(cec_adapter *deviceList, uint8_t i
 
     if(!bResult)
     {
+      // no (more) results
       SetupDiDestroyDeviceInfoList(hDevHandle);
       delete []buffer;
       buffer = NULL;
@@ -298,9 +389,11 @@ uint8_t CUSBCECAdapterDetection::FindAdapters(cec_adapter *deviceList, uint8_t i
     if(!bDetailResult)
       continue;
 
+    // check whether the path matches, if a path was given
     if (strDevicePath && strcmp(strDevicePath, devicedetailData->DevicePath) != 0)
       continue;
 
+    // get the vid and pid
     CStdString strVendorId;
     CStdString strProductId;
     CStdString strTmp(devicedetailData->DevicePath);
@@ -312,51 +405,116 @@ uint8_t CUSBCECAdapterDetection::FindAdapters(cec_adapter *deviceList, uint8_t i
     int iVendor, iProduct;
     sscanf(strVendorId, "%x", &iVendor);
     sscanf(strProductId, "%x", &iProduct);
-    if (iVendor != CEC_VID || (iProduct != CEC_PID && iProduct != CEC_PID2))
-      continue;
 
-    HKEY hDeviceKey = SetupDiOpenDevRegKey(hDevHandle, &devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
-    if (!hDeviceKey)
+    // no match
+    if (iVendor != CEC_VID || (iProduct != CEC_PID && iProduct != CEC_PID2))
       continue;
 
-    TCHAR strPortName[256];
-    strPortName[0] = _T('\0');
-    DWORD dwSize = sizeof(strPortName);
-    DWORD dwType = 0;
 
-    /* search the registry */
-    if ((RegQueryValueEx(hDeviceKey, _T("PortName"), NULL, &dwType, reinterpret_cast<LPBYTE>(strPortName), &dwSize) == ERROR_SUCCESS) && (dwType == REG_SZ))
+    if (iProduct == CEC_PID2)
     {
-      if (_tcslen(strPortName) > 3 && _tcsnicmp(strPortName, _T("COM"), 3) == 0 &&
-        _ttoi(&(strPortName[3])) > 0)
+      // the 1002 pid indicates a composite device, that needs special treatment
+      char strId[512];
+      CM_Get_Device_ID(devInfoData.DevInst, strId, 512, 0);
+      if (FindComPortForComposite(strId, deviceList[iFound].strComName, sizeof(deviceList[iFound].strComName)))
       {
-        snprintf(deviceList[iFound  ].path, sizeof(deviceList[iFound].path), "%s", devicedetailData->DevicePath);
-        snprintf(deviceList[iFound++].comm, sizeof(deviceList[iFound].path), "%s", strPortName);
+        snprintf(deviceList[iFound].strComPath, sizeof(deviceList[iFound].strComPath), "%s", devicedetailData->DevicePath);
+        deviceList[iFound].iVendorId = (uint16_t)iVendor;
+        deviceList[iFound].iProductId = (uint16_t)iProduct;
+        deviceList[iFound].adapterType = ADAPTERTYPE_P8_EXTERNAL; // will be overridden when not doing a "quick scan" by the actual type
+        iFound++;
       }
     }
-
-    RegCloseKey(hDeviceKey);
+    else if (GetComPortFromHandle(hDevHandle, &devInfoData, deviceList[iFound].strComName, sizeof(deviceList[iFound].strComName)))
+    {
+      snprintf(deviceList[iFound].strComPath, sizeof(deviceList[iFound].strComPath), "%s", devicedetailData->DevicePath);
+      deviceList[iFound].iVendorId = (uint16_t)iVendor;
+      deviceList[iFound].iProductId = (uint16_t)iProduct;
+      deviceList[iFound].adapterType = ADAPTERTYPE_P8_EXTERNAL; // will be overridden when not doing a "quick scan" by the actual type
+      iFound++;
+    }
   }
 #elif defined(__FreeBSD__)
   char devicePath[PATH_MAX + 1];
+  char infos[512];
+  char sysctlname[32];
+  char ttyname[8];
+  char *pos;
+  size_t infos_size = sizeof(infos);
   int i;
 
-  for (i = 0; i < 8; ++i)
+  for (i = 0; ; ++i)
   {
-    (void)snprintf(devicePath, sizeof(devicePath), "/dev/ttyU%d", i);
-    if (!access(devicePath, 0))
-    {
-      snprintf(deviceList[iFound  ].path, sizeof(deviceList[iFound].path), "%s", devicePath);
-      snprintf(deviceList[iFound++].comm, sizeof(deviceList[iFound].path), "%s", devicePath);
+    unsigned int iVendor, iProduct;
+    memset(infos, 0, sizeof(infos));
+    (void)snprintf(sysctlname, sizeof(sysctlname),
+      "dev.umodem.%d.%%pnpinfo", i);
+    if (sysctlbyname(sysctlname, infos, &infos_size,
+      NULL, 0) != 0)
+        break;
+    pos = strstr(infos, "vendor=");
+    if (pos == NULL)
+      continue;
+    sscanf(pos, "vendor=%x ", &iVendor);
+
+    pos = strstr(infos, "product=");
+    if (pos == NULL)
+      continue;
+    sscanf(pos, "product=%x ", &iProduct);
+
+    if (iVendor != CEC_VID || (iProduct != CEC_PID && iProduct != CEC_PID2))
+      continue;
+
+    pos = strstr(infos, "ttyname=");
+    if (pos == NULL)
+      continue;
+    sscanf(pos, "ttyname=%s ", ttyname);
+
+    (void)snprintf(devicePath, sizeof(devicePath),
+      "/dev/tty%s", ttyname);
+
+    if (strDevicePath) {
+      char currStrDevicePath[512];
+      int port = 0;
+      int devaddr = 0;
+      memset(currStrDevicePath, 0, sizeof(currStrDevicePath));
+      memset(infos, 0, sizeof(infos));
+      (void)snprintf(sysctlname, sizeof(sysctlname),
+        "dev.umodem.%d.%%location", i);
+      if (sysctlbyname(sysctlname, infos, &infos_size,
+        NULL, 0) != 0)
+          break;
+
+      pos = strstr(infos, "port=");
+      if (pos == NULL)
+        continue;
+      sscanf(pos, "port=%d ", &port);
+
+      pos = strstr(infos, "devaddr=");
+      if (pos == NULL)
+        continue;
+      sscanf(pos, "devaddr=%d ", &devaddr);
+
+      (void)snprintf(currStrDevicePath, sizeof(currStrDevicePath),
+        "/dev/ugen%d.%d", port, devaddr);
+
+      if (strcmp(currStrDevicePath, strDevicePath) != 0)
+        continue;
     }
+    snprintf(deviceList[iFound].strComPath, sizeof(deviceList[iFound].strComPath), "%s", devicePath);
+    snprintf(deviceList[iFound].strComName, sizeof(deviceList[iFound].strComName), "%s", devicePath);
+    deviceList[iFound].iVendorId = iVendor;
+    deviceList[iFound].iProductId = iProduct;
+    deviceList[iFound].adapterType = ADAPTERTYPE_P8_EXTERNAL; // will be overridden when not doing a "quick scan" by the actual type
+    iFound++;
   }
 #else
   //silence "unused" warnings
-  void *tmp = (void*)deviceList;
-  tmp = (void *)strDevicePath;
+  ((void)deviceList);
+  ((void) strDevicePath);
 #endif
 
-  iBufSize = 0; /* silence "unused" warning on linux/osx */
+  iBufSize = 0; if(!iBufSize){} /* silence "unused" warning on linux/osx */
 
   return iFound;
 }