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