cec: the CEC adapter will now also be autodetected on wjndows
[deb_libcec.git] / src / lib / CECDetect.cpp
CommitLineData
abbca718
LOK
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 "CECDetect.h"
34#include "libPlatform/os-dependent.h"
35#include "util/StdString.h"
a58d63d2
LOK
36#include <string.h>
37
abbca718
LOK
38#if !defined(__WINDOWS__)
39#include <dirent.h>
40#include <libudev.h>
41#include <poll.h>
a58d63d2
LOK
42#else
43#include <setupapi.h>
44
45// the virtual COM port only shows up when requesting devices with the raw device guid!
46static GUID USB_RAW_GUID = { 0xA5DCBF10, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } };
abbca718 47#endif
abbca718
LOK
48
49#define CEC_VID 0x2548
50#define CEC_PID 0x1001
51
52using namespace CEC;
53using namespace std;
54
55#if !defined(__WINDOWS__)
56bool TranslateComPort(CStdString &strString)
57{
58 CStdString strTmp(strString);
59 strTmp.MakeReverse();
60 int iSlash = strTmp.Find('/');
61 if (iSlash >= 0)
62 {
63 strTmp = strTmp.Left(iSlash);
64 strTmp.MakeReverse();
65 strString.Format("%s/%s:1.0/tty", strString.c_str(), strTmp.c_str());
66 return true;
67 }
68
69 return false;
70}
71
72bool FindComPort(CStdString &strLocation)
73{
74 CStdString strPort = strLocation;
75 bool bReturn(!strPort.IsEmpty());
76 CStdString strConfigLocation(strLocation);
77 if (TranslateComPort(strConfigLocation))
78 {
79 DIR *dir;
80 struct dirent *dirent;
81 if((dir = opendir(strConfigLocation.c_str())) == NULL)
82 return bReturn;
83
84 while ((dirent = readdir(dir)) != NULL)
85 {
86 if(strcmp((char*)dirent->d_name, "." ) != 0 && strcmp((char*)dirent->d_name, ".." ) != 0)
87 {
88 strPort.Format("/dev/%s", dirent->d_name);
89 if (!strPort.IsEmpty())
90 {
91 strLocation = strPort;
92 bReturn = true;
93 break;
94 }
95 }
96 }
97 closedir(dir);
98 }
99
100 return bReturn;
101}
102#endif
103
104int CCECDetect::FindDevices(vector<cec_device> &deviceList, const char *strDevicePath /* = NULL */)
105{
106 int iFound(0);
107
108#if !defined(__WINDOWS__)
109 struct udev *udev;
110 if (!(udev = udev_new()))
111 return -1;
112
113 struct udev_enumerate *enumerate;
114 struct udev_list_entry *devices, *dev_list_entry;
115 struct udev_device *dev;
116 enumerate = udev_enumerate_new(udev);
117 udev_enumerate_scan_devices(enumerate);
118 devices = udev_enumerate_get_list_entry(enumerate);
119 udev_list_entry_foreach(dev_list_entry, devices)
120 {
121 const char *strPath;
122 strPath = udev_list_entry_get_name(dev_list_entry);
123
124 dev = udev_device_new_from_syspath(udev, strPath);
125 if (!dev)
126 continue;
127
128 dev = udev_device_get_parent(udev_device_get_parent(dev));
129 if (!dev)
130 continue;
131 if (!udev_device_get_sysattr_value(dev,"idVendor") || !udev_device_get_sysattr_value(dev, "idProduct"))
132 {
133 udev_device_unref(dev);
134 continue;
135 }
136
137 int iVendor, iProduct;
138 sscanf(udev_device_get_sysattr_value(dev, "idVendor"), "%x", &iVendor);
139 sscanf(udev_device_get_sysattr_value(dev, "idProduct"), "%x", &iProduct);
140 if (iVendor == CEC_VID && iProduct == CEC_PID)
141 {
142 CStdString strPath(udev_device_get_syspath(dev));
143 if (strDevicePath && strcmp(strPath.c_str(), strDevicePath))
144 continue;
145
146 CStdString strComm(strPath);
147 if (FindComPort(strComm))
148 {
149 cec_device foundDev;
150 foundDev.path = strPath;
151 foundDev.comm = strComm;
152 deviceList.push_back(foundDev);
153 ++iFound;
154 }
155 }
156 udev_device_unref(dev);
157 }
158
159 udev_enumerate_unref(enumerate);
160 udev_unref(udev);
a58d63d2
LOK
161#else
162 HDEVINFO hDevHandle;
163 DWORD required = 0, iMemberIndex = 0;
164 int nBufferSize = 0;
165
166 SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
167 deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
168
169 SP_DEVINFO_DATA devInfoData;
170 devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
171
172 if ((hDevHandle = SetupDiGetClassDevs(&USB_RAW_GUID, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE)) == INVALID_HANDLE_VALUE)
173 return iFound;
174
175 BOOL bResult = true;
176 TCHAR *buffer = NULL;
177 PSP_DEVICE_INTERFACE_DETAIL_DATA devicedetailData;
178 while(bResult)
179 {
180 bResult = SetupDiEnumDeviceInfo(hDevHandle, iMemberIndex, &devInfoData);
181
182 if (bResult)
183 bResult = SetupDiEnumDeviceInterfaces(hDevHandle, 0, &USB_RAW_GUID, iMemberIndex, &deviceInterfaceData);
184
185 if(!bResult)
186 {
187 SetupDiDestroyDeviceInfoList(hDevHandle);
188 delete []buffer;
189 buffer = NULL;
190 return iFound;
191 }
192
193 iMemberIndex++;
194 BOOL bDetailResult = false;
195 {
196 // As per MSDN, Get the required buffer size. Call SetupDiGetDeviceInterfaceDetail with a
197 // NULL DeviceInterfaceDetailData pointer, a DeviceInterfaceDetailDataSize of zero,
198 // and a valid RequiredSize variable. In response to such a call, this function returns
199 // the required buffer size at RequiredSize and fails with GetLastError returning
200 // ERROR_INSUFFICIENT_BUFFER.
201 // Allocate an appropriately sized buffer and call the function again to get the interface details.
202
203 SetupDiGetDeviceInterfaceDetail(hDevHandle, &deviceInterfaceData, NULL, 0, &required, NULL);
204
205 buffer = new TCHAR[required];
206 devicedetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA) buffer;
207 devicedetailData->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
208 nBufferSize = required;
209 }
210
211 bDetailResult = SetupDiGetDeviceInterfaceDetail(hDevHandle, &deviceInterfaceData, devicedetailData, nBufferSize , &required, NULL);
212 if(!bDetailResult)
213 continue;
214
215 CStdString strVendorId;
216 CStdString strProductId;
217 CStdString strTmp(devicedetailData->DevicePath);
218 strVendorId = strTmp.substr(strTmp.Find("vid_") + 4, 4);
219 strProductId = strTmp.substr(strTmp.Find("pid_") + 4, 4);
220 if (strTmp.Find("&mi_") >= 0 && strTmp.Find("&mi_00") < 0)
221 continue;
222
223 int iVendor, iProduct;
224 sscanf(strVendorId, "%x", &iVendor);
225 sscanf(strProductId, "%x", &iProduct);
226 if (iVendor != CEC_VID || iProduct != CEC_PID)
227 continue;
228
229 HKEY hDeviceKey = SetupDiOpenDevRegKey(hDevHandle, &devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
230 if (!hDeviceKey)
231 continue;
232
233 TCHAR strPortName[256];
234 strPortName[0] = _T('\0');
235 DWORD dwSize = sizeof(strPortName);
236 DWORD dwType = 0;
237
238 /* search the registry */
239 if ((RegQueryValueEx(hDeviceKey, _T("PortName"), NULL, &dwType, reinterpret_cast<LPBYTE>(strPortName), &dwSize) == ERROR_SUCCESS) && (dwType == REG_SZ))
240 {
241 if (_tcslen(strPortName) > 3 && _tcsnicmp(strPortName, _T("COM"), 3) == 0 &&
242 _ttoi(&(strPortName[3])) > 0)
243 {
244 cec_device foundDev;
245 foundDev.path = devicedetailData->DevicePath;
246 foundDev.comm = strPortName;
247 deviceList.push_back(foundDev);
248 ++iFound;
249 }
250 }
251
252 RegCloseKey(hDeviceKey);
253 }
abbca718
LOK
254#endif
255
256 return iFound;
257}