updated windows driver inf
[deb_libcec.git] / src / lib / adapter / Pulse-Eight / USBCECAdapterDetection.cpp
CommitLineData
abbca718
LOK
1/*
2 * This file is part of the libCEC(R) library.
3 *
b492c10e 4 * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
abbca718
LOK
5 * libCEC(R) is an original work, containing original code.
6 *
7 * libCEC(R) is a trademark of Pulse-Eight Limited.
8 *
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.
13 *
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.
18 *
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.
22 *
23 *
24 * Alternatively, you can license this library under a commercial license,
25 * please contact Pulse-Eight Licensing for more information.
26 *
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/
31 */
32
2b44051c 33#include "env.h"
7bb4ed43 34#include "USBCECAdapterDetection.h"
2b44051c 35#include "lib/platform/util/StdString.h"
a58d63d2 36
6df2c52f 37#if defined(__APPLE__)
38#include <dirent.h>
39#include <sys/param.h>
40#include <IOKit/IOKitLib.h>
41#include <IOKit/IOMessage.h>
42#include <IOKit/IOCFPlugIn.h>
43#include <IOKit/usb/IOUSBLib.h>
44#include <IOKit/serial/IOSerialKeys.h>
45#include <CoreFoundation/CoreFoundation.h>
42c02563 46#elif defined(__WINDOWS__)
4d760b54
LOK
47#pragma comment(lib, "advapi32.lib")
48#pragma comment(lib, "setupapi.lib")
a58d63d2
LOK
49#include <setupapi.h>
50
51// the virtual COM port only shows up when requesting devices with the raw device guid!
52static GUID USB_RAW_GUID = { 0xA5DCBF10, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } };
42c02563
LOK
53#elif defined(HAVE_LIBUDEV)
54#include <dirent.h>
42c02563 55#include <poll.h>
5fa3eb86
LOK
56extern "C" {
57#include <libudev.h>
58}
5458fd77
F
59#elif defined(__FreeBSD__)
60#include <stdio.h>
61#include <unistd.h>
abbca718 62#endif
abbca718 63
afee62c6
LOK
64#define CEC_VID 0x2548
65#define CEC_PID 0x1001
66#define CEC_PID2 0x1002
abbca718
LOK
67
68using namespace CEC;
69using namespace std;
70
42c02563 71#if defined(HAVE_LIBUDEV)
abbca718
LOK
72bool TranslateComPort(CStdString &strString)
73{
74 CStdString strTmp(strString);
75 strTmp.MakeReverse();
76 int iSlash = strTmp.Find('/');
77 if (iSlash >= 0)
78 {
79 strTmp = strTmp.Left(iSlash);
80 strTmp.MakeReverse();
81 strString.Format("%s/%s:1.0/tty", strString.c_str(), strTmp.c_str());
82 return true;
83 }
84
85 return false;
86}
87
88bool FindComPort(CStdString &strLocation)
89{
90 CStdString strPort = strLocation;
91 bool bReturn(!strPort.IsEmpty());
92 CStdString strConfigLocation(strLocation);
93 if (TranslateComPort(strConfigLocation))
94 {
95 DIR *dir;
96 struct dirent *dirent;
97 if((dir = opendir(strConfigLocation.c_str())) == NULL)
98 return bReturn;
99
100 while ((dirent = readdir(dir)) != NULL)
101 {
102 if(strcmp((char*)dirent->d_name, "." ) != 0 && strcmp((char*)dirent->d_name, ".." ) != 0)
103 {
104 strPort.Format("/dev/%s", dirent->d_name);
105 if (!strPort.IsEmpty())
106 {
107 strLocation = strPort;
108 bReturn = true;
109 break;
110 }
111 }
112 }
113 closedir(dir);
114 }
115
116 return bReturn;
117}
118#endif
119
6807ee55
LOK
120bool CUSBCECAdapterDetection::CanAutodetect(void)
121{
122#if defined(__APPLE__) || defined(HAVE_LIBUDEV) || defined(__WINDOWS__) || defined(__FreeBSD__)
123 return true;
124#else
125 return false;
126#endif
127}
128
7bb4ed43 129uint8_t CUSBCECAdapterDetection::FindAdapters(cec_adapter *deviceList, uint8_t iBufSize, const char *strDevicePath /* = NULL */)
abbca718 130{
25701fa6 131 uint8_t iFound(0);
abbca718 132
6df2c52f 133#if defined(__APPLE__)
134 kern_return_t kresult;
135 char bsdPath[MAXPATHLEN] = {0};
136 io_iterator_t serialPortIterator;
137
138 CFMutableDictionaryRef classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue);
139 if (classesToMatch)
140 {
141 CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDModemType));
142 kresult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, &serialPortIterator);
143 if (kresult == KERN_SUCCESS)
144 {
145 io_object_t serialService;
146 while ((serialService = IOIteratorNext(serialPortIterator)))
147 {
148 int iVendor = 0, iProduct = 0;
149 CFTypeRef bsdPathAsCFString;
150
151 // fetch the device path.
152 bsdPathAsCFString = IORegistryEntryCreateCFProperty(serialService,
153 CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0);
154 if (bsdPathAsCFString)
155 {
156 // convert the path from a CFString to a C (NUL-terminated) string.
157 CFStringGetCString((CFStringRef)bsdPathAsCFString, bsdPath, MAXPATHLEN - 1, kCFStringEncodingUTF8);
158 CFRelease(bsdPathAsCFString);
159
160 // now walk up the hierarchy until we find the entry with vendor/product IDs
161 io_registry_entry_t parent;
162 CFTypeRef vendorIdAsCFNumber = NULL;
163 CFTypeRef productIdAsCFNumber = NULL;
164 kern_return_t kresult = IORegistryEntryGetParentEntry(serialService, kIOServicePlane, &parent);
165 while (kresult == KERN_SUCCESS)
166 {
167 vendorIdAsCFNumber = IORegistryEntrySearchCFProperty(parent,
168 kIOServicePlane, CFSTR(kUSBVendorID), kCFAllocatorDefault, 0);
169 productIdAsCFNumber = IORegistryEntrySearchCFProperty(parent,
170 kIOServicePlane, CFSTR(kUSBProductID), kCFAllocatorDefault, 0);
171 if (vendorIdAsCFNumber && productIdAsCFNumber)
172 {
173 CFNumberGetValue((CFNumberRef)vendorIdAsCFNumber, kCFNumberIntType, &iVendor);
174 CFRelease(vendorIdAsCFNumber);
175 CFNumberGetValue((CFNumberRef)productIdAsCFNumber, kCFNumberIntType, &iProduct);
176 CFRelease(productIdAsCFNumber);
177 IOObjectRelease(parent);
178 break;
179 }
180 io_registry_entry_t oldparent = parent;
181 kresult = IORegistryEntryGetParentEntry(parent, kIOServicePlane, &parent);
182 IOObjectRelease(oldparent);
183 }
afee62c6 184 if (strlen(bsdPath) && iVendor == CEC_VID && (iProduct == CEC_PID || iProduct == CEC_PID2))
6df2c52f 185 {
186 if (!strDevicePath || !strcmp(bsdPath, strDevicePath))
187 {
188 // on darwin, the device path is the same as the comm path.
41b12347
LOK
189 if (iFound == 0 || strcmp(deviceList[iFound].comm, bsdPath))
190 {
191 snprintf(deviceList[iFound ].path, sizeof(deviceList[iFound].path), "%s", bsdPath);
192 snprintf(deviceList[iFound++].comm, sizeof(deviceList[iFound].path), "%s", bsdPath);
193 }
6df2c52f 194 }
195 }
196 }
41b12347 197 IOObjectRelease(serialService);
6df2c52f 198 }
199 }
200 IOObjectRelease(serialPortIterator);
201 }
42c02563 202#elif defined(HAVE_LIBUDEV)
abbca718
LOK
203 struct udev *udev;
204 if (!(udev = udev_new()))
205 return -1;
206
207 struct udev_enumerate *enumerate;
208 struct udev_list_entry *devices, *dev_list_entry;
25701fa6 209 struct udev_device *dev, *pdev;
abbca718
LOK
210 enumerate = udev_enumerate_new(udev);
211 udev_enumerate_scan_devices(enumerate);
212 devices = udev_enumerate_get_list_entry(enumerate);
213 udev_list_entry_foreach(dev_list_entry, devices)
214 {
215 const char *strPath;
216 strPath = udev_list_entry_get_name(dev_list_entry);
217
218 dev = udev_device_new_from_syspath(udev, strPath);
219 if (!dev)
220 continue;
221
25701fa6
LOK
222 pdev = udev_device_get_parent(udev_device_get_parent(dev));
223 if (!pdev || !udev_device_get_sysattr_value(pdev,"idVendor") || !udev_device_get_sysattr_value(pdev, "idProduct"))
abbca718
LOK
224 {
225 udev_device_unref(dev);
226 continue;
227 }
228
229 int iVendor, iProduct;
25701fa6
LOK
230 sscanf(udev_device_get_sysattr_value(pdev, "idVendor"), "%x", &iVendor);
231 sscanf(udev_device_get_sysattr_value(pdev, "idProduct"), "%x", &iProduct);
afee62c6 232 if (iVendor == CEC_VID && (iProduct == CEC_PID || iProduct == CEC_PID2))
abbca718 233 {
25701fa6
LOK
234 CStdString strPath(udev_device_get_syspath(pdev));
235 if (!strDevicePath || !strcmp(strPath.c_str(), strDevicePath))
abbca718 236 {
25701fa6 237 CStdString strComm(strPath);
41b12347 238 if (FindComPort(strComm) && (iFound == 0 || strcmp(deviceList[iFound].comm, strComm.c_str())))
25701fa6
LOK
239 {
240 snprintf(deviceList[iFound ].path, sizeof(deviceList[iFound].path), "%s", strPath.c_str());
241 snprintf(deviceList[iFound++].comm, sizeof(deviceList[iFound].path), "%s", strComm.c_str());
242 }
abbca718
LOK
243 }
244 }
245 udev_device_unref(dev);
246 }
247
248 udev_enumerate_unref(enumerate);
249 udev_unref(udev);
42c02563 250#elif defined(__WINDOWS__)
a58d63d2
LOK
251 HDEVINFO hDevHandle;
252 DWORD required = 0, iMemberIndex = 0;
253 int nBufferSize = 0;
254
255 SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
256 deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
257
258 SP_DEVINFO_DATA devInfoData;
259 devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
260
261 if ((hDevHandle = SetupDiGetClassDevs(&USB_RAW_GUID, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE)) == INVALID_HANDLE_VALUE)
262 return iFound;
263
264 BOOL bResult = true;
265 TCHAR *buffer = NULL;
266 PSP_DEVICE_INTERFACE_DETAIL_DATA devicedetailData;
25701fa6 267 while(bResult && iFound < iBufSize)
a58d63d2
LOK
268 {
269 bResult = SetupDiEnumDeviceInfo(hDevHandle, iMemberIndex, &devInfoData);
270
271 if (bResult)
272 bResult = SetupDiEnumDeviceInterfaces(hDevHandle, 0, &USB_RAW_GUID, iMemberIndex, &deviceInterfaceData);
273
274 if(!bResult)
275 {
276 SetupDiDestroyDeviceInfoList(hDevHandle);
277 delete []buffer;
278 buffer = NULL;
279 return iFound;
280 }
281
282 iMemberIndex++;
283 BOOL bDetailResult = false;
284 {
285 // As per MSDN, Get the required buffer size. Call SetupDiGetDeviceInterfaceDetail with a
286 // NULL DeviceInterfaceDetailData pointer, a DeviceInterfaceDetailDataSize of zero,
287 // and a valid RequiredSize variable. In response to such a call, this function returns
288 // the required buffer size at RequiredSize and fails with GetLastError returning
289 // ERROR_INSUFFICIENT_BUFFER.
290 // Allocate an appropriately sized buffer and call the function again to get the interface details.
291
292 SetupDiGetDeviceInterfaceDetail(hDevHandle, &deviceInterfaceData, NULL, 0, &required, NULL);
293
294 buffer = new TCHAR[required];
295 devicedetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA) buffer;
296 devicedetailData->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
297 nBufferSize = required;
298 }
299
300 bDetailResult = SetupDiGetDeviceInterfaceDetail(hDevHandle, &deviceInterfaceData, devicedetailData, nBufferSize , &required, NULL);
301 if(!bDetailResult)
302 continue;
303
b824eadc
LOK
304 if (strDevicePath && strcmp(strDevicePath, devicedetailData->DevicePath) != 0)
305 continue;
306
a58d63d2
LOK
307 CStdString strVendorId;
308 CStdString strProductId;
309 CStdString strTmp(devicedetailData->DevicePath);
25701fa6
LOK
310 strVendorId.assign(strTmp.substr(strTmp.Find("vid_") + 4, 4));
311 strProductId.assign(strTmp.substr(strTmp.Find("pid_") + 4, 4));
a58d63d2
LOK
312 if (strTmp.Find("&mi_") >= 0 && strTmp.Find("&mi_00") < 0)
313 continue;
314
315 int iVendor, iProduct;
316 sscanf(strVendorId, "%x", &iVendor);
317 sscanf(strProductId, "%x", &iProduct);
afee62c6 318 if (iVendor != CEC_VID || (iProduct != CEC_PID && iProduct != CEC_PID2))
a58d63d2
LOK
319 continue;
320
321 HKEY hDeviceKey = SetupDiOpenDevRegKey(hDevHandle, &devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
322 if (!hDeviceKey)
323 continue;
324
325 TCHAR strPortName[256];
326 strPortName[0] = _T('\0');
327 DWORD dwSize = sizeof(strPortName);
328 DWORD dwType = 0;
329
330 /* search the registry */
331 if ((RegQueryValueEx(hDeviceKey, _T("PortName"), NULL, &dwType, reinterpret_cast<LPBYTE>(strPortName), &dwSize) == ERROR_SUCCESS) && (dwType == REG_SZ))
332 {
333 if (_tcslen(strPortName) > 3 && _tcsnicmp(strPortName, _T("COM"), 3) == 0 &&
334 _ttoi(&(strPortName[3])) > 0)
335 {
25701fa6
LOK
336 snprintf(deviceList[iFound ].path, sizeof(deviceList[iFound].path), "%s", devicedetailData->DevicePath);
337 snprintf(deviceList[iFound++].comm, sizeof(deviceList[iFound].path), "%s", strPortName);
a58d63d2
LOK
338 }
339 }
340
341 RegCloseKey(hDeviceKey);
342 }
5458fd77
F
343#elif defined(__FreeBSD__)
344 char devicePath[PATH_MAX + 1];
345 int i;
346
347 for (i = 0; i < 8; ++i)
348 {
349 (void)snprintf(devicePath, sizeof(devicePath), "/dev/ttyU%d", i);
c7bee84a
F
350 if (!access(devicePath, 0))
351 {
352 snprintf(deviceList[iFound ].path, sizeof(deviceList[iFound].path), "%s", devicePath);
353 snprintf(deviceList[iFound++].comm, sizeof(deviceList[iFound].path), "%s", devicePath);
354 }
5458fd77 355 }
2b44051c
LOK
356#else
357 //silence "unused" warnings
358 void *tmp = (void*)deviceList;
359 tmp = (void *)strDevicePath;
abbca718
LOK
360#endif
361
1de6617c
LOK
362 iBufSize = 0; /* silence "unused" warning on linux/osx */
363
abbca718
LOK
364 return iFound;
365}