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