2 * This file is part of the libCEC(R) library.
4 * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
5 * libCEC(R) is an original work, containing original code.
7 * libCEC(R) is a trademark of Pulse-Eight Limited.
9 * This program is dual-licensed; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 * Alternatively, you can license this library under a commercial license,
25 * please contact Pulse-Eight Licensing for more information.
27 * For more information contact:
28 * Pulse-Eight Licensing <license@pulse-eight.com>
29 * http://www.pulse-eight.com/
30 * http://www.pulse-eight.net/
33 #include "USBCECAdapterDetection.h"
34 #include "../platform/util/StdString.h"
36 #if defined(__APPLE__)
38 #include <sys/param.h>
39 #include <IOKit/IOKitLib.h>
40 #include <IOKit/IOMessage.h>
41 #include <IOKit/IOCFPlugIn.h>
42 #include <IOKit/usb/IOUSBLib.h>
43 #include <IOKit/serial/IOSerialKeys.h>
44 #include <CoreFoundation/CoreFoundation.h>
45 #elif defined(__WINDOWS__)
46 #pragma comment(lib, "advapi32.lib")
47 #pragma comment(lib, "setupapi.lib")
50 // the virtual COM port only shows up when requesting devices with the raw device guid!
51 static GUID USB_RAW_GUID
= { 0xA5DCBF10, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } };
52 #elif defined(HAVE_LIBUDEV)
58 #elif defined(__FreeBSD__)
63 #define CEC_VID 0x2548
64 #define CEC_PID 0x1001
69 #if defined(HAVE_LIBUDEV)
70 bool TranslateComPort(CStdString
&strString
)
72 CStdString
strTmp(strString
);
74 int iSlash
= strTmp
.Find('/');
77 strTmp
= strTmp
.Left(iSlash
);
79 strString
.Format("%s/%s:1.0/tty", strString
.c_str(), strTmp
.c_str());
86 bool FindComPort(CStdString
&strLocation
)
88 CStdString strPort
= strLocation
;
89 bool bReturn(!strPort
.IsEmpty());
90 CStdString
strConfigLocation(strLocation
);
91 if (TranslateComPort(strConfigLocation
))
94 struct dirent
*dirent
;
95 if((dir
= opendir(strConfigLocation
.c_str())) == NULL
)
98 while ((dirent
= readdir(dir
)) != NULL
)
100 if(strcmp((char*)dirent
->d_name
, "." ) != 0 && strcmp((char*)dirent
->d_name
, ".." ) != 0)
102 strPort
.Format("/dev/%s", dirent
->d_name
);
103 if (!strPort
.IsEmpty())
105 strLocation
= strPort
;
118 bool CUSBCECAdapterDetection::CanAutodetect(void)
120 #if defined(__APPLE__) || defined(HAVE_LIBUDEV) || defined(__WINDOWS__) || defined(__FreeBSD__)
127 uint8_t CUSBCECAdapterDetection::FindAdapters(cec_adapter
*deviceList
, uint8_t iBufSize
, const char *strDevicePath
/* = NULL */)
131 #if defined(__APPLE__)
132 kern_return_t kresult
;
133 char bsdPath
[MAXPATHLEN
] = {0};
134 io_iterator_t serialPortIterator
;
136 CFMutableDictionaryRef classesToMatch
= IOServiceMatching(kIOSerialBSDServiceValue
);
139 CFDictionarySetValue(classesToMatch
, CFSTR(kIOSerialBSDTypeKey
), CFSTR(kIOSerialBSDModemType
));
140 kresult
= IOServiceGetMatchingServices(kIOMasterPortDefault
, classesToMatch
, &serialPortIterator
);
141 if (kresult
== KERN_SUCCESS
)
143 io_object_t serialService
;
144 while ((serialService
= IOIteratorNext(serialPortIterator
)))
146 int iVendor
= 0, iProduct
= 0;
147 CFTypeRef bsdPathAsCFString
;
149 // fetch the device path.
150 bsdPathAsCFString
= IORegistryEntryCreateCFProperty(serialService
,
151 CFSTR(kIOCalloutDeviceKey
), kCFAllocatorDefault
, 0);
152 if (bsdPathAsCFString
)
154 // convert the path from a CFString to a C (NUL-terminated) string.
155 CFStringGetCString((CFStringRef
)bsdPathAsCFString
, bsdPath
, MAXPATHLEN
- 1, kCFStringEncodingUTF8
);
156 CFRelease(bsdPathAsCFString
);
158 // now walk up the hierarchy until we find the entry with vendor/product IDs
159 io_registry_entry_t parent
;
160 CFTypeRef vendorIdAsCFNumber
= NULL
;
161 CFTypeRef productIdAsCFNumber
= NULL
;
162 kern_return_t kresult
= IORegistryEntryGetParentEntry(serialService
, kIOServicePlane
, &parent
);
163 while (kresult
== KERN_SUCCESS
)
165 vendorIdAsCFNumber
= IORegistryEntrySearchCFProperty(parent
,
166 kIOServicePlane
, CFSTR(kUSBVendorID
), kCFAllocatorDefault
, 0);
167 productIdAsCFNumber
= IORegistryEntrySearchCFProperty(parent
,
168 kIOServicePlane
, CFSTR(kUSBProductID
), kCFAllocatorDefault
, 0);
169 if (vendorIdAsCFNumber
&& productIdAsCFNumber
)
171 CFNumberGetValue((CFNumberRef
)vendorIdAsCFNumber
, kCFNumberIntType
, &iVendor
);
172 CFRelease(vendorIdAsCFNumber
);
173 CFNumberGetValue((CFNumberRef
)productIdAsCFNumber
, kCFNumberIntType
, &iProduct
);
174 CFRelease(productIdAsCFNumber
);
175 IOObjectRelease(parent
);
178 io_registry_entry_t oldparent
= parent
;
179 kresult
= IORegistryEntryGetParentEntry(parent
, kIOServicePlane
, &parent
);
180 IOObjectRelease(oldparent
);
182 if (strlen(bsdPath
) && iVendor
== CEC_VID
&& iProduct
== CEC_PID
)
184 if (!strDevicePath
|| !strcmp(bsdPath
, strDevicePath
))
186 // on darwin, the device path is the same as the comm path.
187 snprintf(deviceList
[iFound
].path
, sizeof(deviceList
[iFound
].path
), "%s", bsdPath
);
188 snprintf(deviceList
[iFound
++].comm
, sizeof(deviceList
[iFound
].path
), "%s", bsdPath
);
192 IOObjectRelease(serialService
);
195 IOObjectRelease(serialPortIterator
);
197 #elif defined(HAVE_LIBUDEV)
199 if (!(udev
= udev_new()))
202 struct udev_enumerate
*enumerate
;
203 struct udev_list_entry
*devices
, *dev_list_entry
;
204 struct udev_device
*dev
, *pdev
;
205 enumerate
= udev_enumerate_new(udev
);
206 udev_enumerate_scan_devices(enumerate
);
207 devices
= udev_enumerate_get_list_entry(enumerate
);
208 udev_list_entry_foreach(dev_list_entry
, devices
)
211 strPath
= udev_list_entry_get_name(dev_list_entry
);
213 dev
= udev_device_new_from_syspath(udev
, strPath
);
217 pdev
= udev_device_get_parent(udev_device_get_parent(dev
));
218 if (!pdev
|| !udev_device_get_sysattr_value(pdev
,"idVendor") || !udev_device_get_sysattr_value(pdev
, "idProduct"))
220 udev_device_unref(dev
);
224 int iVendor
, iProduct
;
225 sscanf(udev_device_get_sysattr_value(pdev
, "idVendor"), "%x", &iVendor
);
226 sscanf(udev_device_get_sysattr_value(pdev
, "idProduct"), "%x", &iProduct
);
227 if (iVendor
== CEC_VID
&& iProduct
== CEC_PID
)
229 CStdString
strPath(udev_device_get_syspath(pdev
));
230 if (!strDevicePath
|| !strcmp(strPath
.c_str(), strDevicePath
))
232 CStdString
strComm(strPath
);
233 if (FindComPort(strComm
))
235 snprintf(deviceList
[iFound
].path
, sizeof(deviceList
[iFound
].path
), "%s", strPath
.c_str());
236 snprintf(deviceList
[iFound
++].comm
, sizeof(deviceList
[iFound
].path
), "%s", strComm
.c_str());
240 udev_device_unref(dev
);
243 udev_enumerate_unref(enumerate
);
245 #elif defined(__WINDOWS__)
247 DWORD required
= 0, iMemberIndex
= 0;
250 SP_DEVICE_INTERFACE_DATA deviceInterfaceData
;
251 deviceInterfaceData
.cbSize
= sizeof(SP_DEVICE_INTERFACE_DATA
);
253 SP_DEVINFO_DATA devInfoData
;
254 devInfoData
.cbSize
= sizeof(SP_DEVINFO_DATA
);
256 if ((hDevHandle
= SetupDiGetClassDevs(&USB_RAW_GUID
, 0, 0, DIGCF_PRESENT
| DIGCF_DEVICEINTERFACE
)) == INVALID_HANDLE_VALUE
)
260 TCHAR
*buffer
= NULL
;
261 PSP_DEVICE_INTERFACE_DETAIL_DATA devicedetailData
;
262 while(bResult
&& iFound
< iBufSize
)
264 bResult
= SetupDiEnumDeviceInfo(hDevHandle
, iMemberIndex
, &devInfoData
);
267 bResult
= SetupDiEnumDeviceInterfaces(hDevHandle
, 0, &USB_RAW_GUID
, iMemberIndex
, &deviceInterfaceData
);
271 SetupDiDestroyDeviceInfoList(hDevHandle
);
278 BOOL bDetailResult
= false;
280 // As per MSDN, Get the required buffer size. Call SetupDiGetDeviceInterfaceDetail with a
281 // NULL DeviceInterfaceDetailData pointer, a DeviceInterfaceDetailDataSize of zero,
282 // and a valid RequiredSize variable. In response to such a call, this function returns
283 // the required buffer size at RequiredSize and fails with GetLastError returning
284 // ERROR_INSUFFICIENT_BUFFER.
285 // Allocate an appropriately sized buffer and call the function again to get the interface details.
287 SetupDiGetDeviceInterfaceDetail(hDevHandle
, &deviceInterfaceData
, NULL
, 0, &required
, NULL
);
289 buffer
= new TCHAR
[required
];
290 devicedetailData
= (PSP_DEVICE_INTERFACE_DETAIL_DATA
) buffer
;
291 devicedetailData
->cbSize
= sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA
);
292 nBufferSize
= required
;
295 bDetailResult
= SetupDiGetDeviceInterfaceDetail(hDevHandle
, &deviceInterfaceData
, devicedetailData
, nBufferSize
, &required
, NULL
);
299 if (strDevicePath
&& strcmp(strDevicePath
, devicedetailData
->DevicePath
) != 0)
302 CStdString strVendorId
;
303 CStdString strProductId
;
304 CStdString
strTmp(devicedetailData
->DevicePath
);
305 strVendorId
.assign(strTmp
.substr(strTmp
.Find("vid_") + 4, 4));
306 strProductId
.assign(strTmp
.substr(strTmp
.Find("pid_") + 4, 4));
307 if (strTmp
.Find("&mi_") >= 0 && strTmp
.Find("&mi_00") < 0)
310 int iVendor
, iProduct
;
311 sscanf(strVendorId
, "%x", &iVendor
);
312 sscanf(strProductId
, "%x", &iProduct
);
313 if (iVendor
!= CEC_VID
|| iProduct
!= CEC_PID
)
316 HKEY hDeviceKey
= SetupDiOpenDevRegKey(hDevHandle
, &devInfoData
, DICS_FLAG_GLOBAL
, 0, DIREG_DEV
, KEY_QUERY_VALUE
);
320 TCHAR strPortName
[256];
321 strPortName
[0] = _T('\0');
322 DWORD dwSize
= sizeof(strPortName
);
325 /* search the registry */
326 if ((RegQueryValueEx(hDeviceKey
, _T("PortName"), NULL
, &dwType
, reinterpret_cast<LPBYTE
>(strPortName
), &dwSize
) == ERROR_SUCCESS
) && (dwType
== REG_SZ
))
328 if (_tcslen(strPortName
) > 3 && _tcsnicmp(strPortName
, _T("COM"), 3) == 0 &&
329 _ttoi(&(strPortName
[3])) > 0)
331 snprintf(deviceList
[iFound
].path
, sizeof(deviceList
[iFound
].path
), "%s", devicedetailData
->DevicePath
);
332 snprintf(deviceList
[iFound
++].comm
, sizeof(deviceList
[iFound
].path
), "%s", strPortName
);
336 RegCloseKey(hDeviceKey
);
338 #elif defined(__FreeBSD__)
339 char devicePath
[PATH_MAX
+ 1];
342 for (i
= 0; i
< 8; ++i
)
344 (void)snprintf(devicePath
, sizeof(devicePath
), "/dev/ttyU%d", i
);
345 if (!access(devicePath
, 0))
347 snprintf(deviceList
[iFound
].path
, sizeof(deviceList
[iFound
].path
), "%s", devicePath
);
348 snprintf(deviceList
[iFound
++].comm
, sizeof(deviceList
[iFound
].path
), "%s", devicePath
);
353 iBufSize
= 0; /* silence "unused" warning on linux/osx */