1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "device/hid/hid_service_win.h" 6 7 #include <cstdlib> 8 9 #include "base/files/file.h" 10 #include "base/stl_util.h" 11 #include "base/strings/sys_string_conversions.h" 12 #include "base/threading/thread_restrictions.h" 13 #include "device/hid/hid_connection_win.h" 14 #include "device/hid/hid_device_info.h" 15 #include "net/base/io_buffer.h" 16 17 #if defined(OS_WIN) 18 19 #define INITGUID 20 21 #include <setupapi.h> 22 #include <winioctl.h> 23 #include "base/win/scoped_handle.h" 24 25 #endif // defined(OS_WIN) 26 27 // Setup API is required to enumerate HID devices. 28 #pragma comment(lib, "setupapi.lib") 29 #pragma comment(lib, "hid.lib") 30 31 namespace device { 32 namespace { 33 34 const char kHIDClass[] = "HIDClass"; 35 36 } // namespace 37 38 HidServiceWin::HidServiceWin() { 39 base::ThreadRestrictions::AssertIOAllowed(); 40 Enumerate(); 41 } 42 43 HidServiceWin::~HidServiceWin() {} 44 45 void HidServiceWin::Enumerate() { 46 BOOL res; 47 HDEVINFO device_info_set; 48 SP_DEVINFO_DATA devinfo_data; 49 SP_DEVICE_INTERFACE_DATA device_interface_data; 50 51 memset(&devinfo_data, 0, sizeof(SP_DEVINFO_DATA)); 52 devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA); 53 device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); 54 55 device_info_set = SetupDiGetClassDevs( 56 &GUID_DEVINTERFACE_HID, 57 NULL, 58 NULL, 59 DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); 60 61 std::set<std::string> connected_devices; 62 63 if (device_info_set != INVALID_HANDLE_VALUE) { 64 for (int device_index = 0; 65 SetupDiEnumDeviceInterfaces(device_info_set, 66 NULL, 67 &GUID_DEVINTERFACE_HID, 68 device_index, 69 &device_interface_data); 70 ++device_index) { 71 DWORD required_size = 0; 72 73 // Determime the required size of detail struct. 74 SetupDiGetDeviceInterfaceDetailA(device_info_set, 75 &device_interface_data, 76 NULL, 77 0, 78 &required_size, 79 NULL); 80 81 scoped_ptr<SP_DEVICE_INTERFACE_DETAIL_DATA_A, base::FreeDeleter> 82 device_interface_detail_data( 83 static_cast<SP_DEVICE_INTERFACE_DETAIL_DATA_A*>( 84 malloc(required_size))); 85 device_interface_detail_data->cbSize = 86 sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); 87 88 // Get the detailed data for this device. 89 res = SetupDiGetDeviceInterfaceDetailA(device_info_set, 90 &device_interface_data, 91 device_interface_detail_data.get(), 92 required_size, 93 NULL, 94 NULL); 95 if (!res) 96 continue; 97 98 // Enumerate device info. Looking for Setup Class "HIDClass". 99 for (DWORD i = 0; 100 SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data); 101 i++) { 102 char class_name[256] = {0}; 103 res = SetupDiGetDeviceRegistryPropertyA(device_info_set, 104 &devinfo_data, 105 SPDRP_CLASS, 106 NULL, 107 (PBYTE) class_name, 108 sizeof(class_name) - 1, 109 NULL); 110 if (!res) 111 break; 112 if (memcmp(class_name, kHIDClass, sizeof(kHIDClass)) == 0) { 113 char driver_name[256] = {0}; 114 // Get bounded driver. 115 res = SetupDiGetDeviceRegistryPropertyA(device_info_set, 116 &devinfo_data, 117 SPDRP_DRIVER, 118 NULL, 119 (PBYTE) driver_name, 120 sizeof(driver_name) - 1, 121 NULL); 122 if (res) { 123 // Found the driver. 124 break; 125 } 126 } 127 } 128 129 if (!res) 130 continue; 131 132 PlatformAddDevice(device_interface_detail_data->DevicePath); 133 connected_devices.insert(device_interface_detail_data->DevicePath); 134 } 135 } 136 137 // Find disconnected devices. 138 std::vector<std::string> disconnected_devices; 139 for (DeviceMap::const_iterator it = devices().begin(); it != devices().end(); 140 ++it) { 141 if (!ContainsKey(connected_devices, it->first)) { 142 disconnected_devices.push_back(it->first); 143 } 144 } 145 146 // Remove disconnected devices. 147 for (size_t i = 0; i < disconnected_devices.size(); ++i) { 148 PlatformRemoveDevice(disconnected_devices[i]); 149 } 150 } 151 152 void HidServiceWin::CollectInfoFromButtonCaps( 153 PHIDP_PREPARSED_DATA preparsed_data, 154 HIDP_REPORT_TYPE report_type, 155 USHORT button_caps_length, 156 HidCollectionInfo* collection_info) { 157 if (button_caps_length > 0) { 158 scoped_ptr<HIDP_BUTTON_CAPS[]> button_caps( 159 new HIDP_BUTTON_CAPS[button_caps_length]); 160 if (HidP_GetButtonCaps(report_type, 161 &button_caps[0], 162 &button_caps_length, 163 preparsed_data) == HIDP_STATUS_SUCCESS) { 164 for (size_t i = 0; i < button_caps_length; i++) { 165 int report_id = button_caps[i].ReportID; 166 if (report_id != 0) { 167 collection_info->report_ids.insert(report_id); 168 } 169 } 170 } 171 } 172 } 173 174 void HidServiceWin::CollectInfoFromValueCaps( 175 PHIDP_PREPARSED_DATA preparsed_data, 176 HIDP_REPORT_TYPE report_type, 177 USHORT value_caps_length, 178 HidCollectionInfo* collection_info) { 179 if (value_caps_length > 0) { 180 scoped_ptr<HIDP_VALUE_CAPS[]> value_caps( 181 new HIDP_VALUE_CAPS[value_caps_length]); 182 if (HidP_GetValueCaps( 183 report_type, &value_caps[0], &value_caps_length, preparsed_data) == 184 HIDP_STATUS_SUCCESS) { 185 for (size_t i = 0; i < value_caps_length; i++) { 186 int report_id = value_caps[i].ReportID; 187 if (report_id != 0) { 188 collection_info->report_ids.insert(report_id); 189 } 190 } 191 } 192 } 193 } 194 195 void HidServiceWin::PlatformAddDevice(const std::string& device_path) { 196 HidDeviceInfo device_info; 197 device_info.device_id = device_path; 198 199 // Try to open the device. 200 base::win::ScopedHandle device_handle( 201 CreateFileA(device_path.c_str(), 202 GENERIC_WRITE | GENERIC_READ, 203 FILE_SHARE_READ | FILE_SHARE_WRITE, 204 NULL, 205 OPEN_EXISTING, 206 FILE_FLAG_OVERLAPPED, 207 0)); 208 209 if (!device_handle.IsValid() && 210 GetLastError() == base::File::FILE_ERROR_ACCESS_DENIED) { 211 base::win::ScopedHandle device_handle( 212 CreateFileA(device_path.c_str(), 213 GENERIC_READ, 214 FILE_SHARE_READ, 215 NULL, 216 OPEN_EXISTING, 217 FILE_FLAG_OVERLAPPED, 218 0)); 219 220 if (!device_handle.IsValid()) 221 return; 222 } 223 224 // Get VID/PID pair. 225 HIDD_ATTRIBUTES attrib = {0}; 226 attrib.Size = sizeof(HIDD_ATTRIBUTES); 227 if (!HidD_GetAttributes(device_handle.Get(), &attrib)) 228 return; 229 230 device_info.vendor_id = attrib.VendorID; 231 device_info.product_id = attrib.ProductID; 232 233 for (ULONG i = 32; 234 HidD_SetNumInputBuffers(device_handle.Get(), i); 235 i <<= 1); 236 237 // Get usage and usage page (optional). 238 PHIDP_PREPARSED_DATA preparsed_data; 239 if (HidD_GetPreparsedData(device_handle.Get(), &preparsed_data) && 240 preparsed_data) { 241 HIDP_CAPS capabilities = {0}; 242 if (HidP_GetCaps(preparsed_data, &capabilities) == HIDP_STATUS_SUCCESS) { 243 device_info.max_input_report_size = capabilities.InputReportByteLength; 244 device_info.max_output_report_size = capabilities.OutputReportByteLength; 245 device_info.max_feature_report_size = 246 capabilities.FeatureReportByteLength; 247 HidCollectionInfo collection_info; 248 collection_info.usage = HidUsageAndPage( 249 capabilities.Usage, 250 static_cast<HidUsageAndPage::Page>(capabilities.UsagePage)); 251 CollectInfoFromButtonCaps(preparsed_data, 252 HidP_Input, 253 capabilities.NumberInputButtonCaps, 254 &collection_info); 255 CollectInfoFromButtonCaps(preparsed_data, 256 HidP_Output, 257 capabilities.NumberOutputButtonCaps, 258 &collection_info); 259 CollectInfoFromButtonCaps(preparsed_data, 260 HidP_Feature, 261 capabilities.NumberFeatureButtonCaps, 262 &collection_info); 263 CollectInfoFromValueCaps(preparsed_data, 264 HidP_Input, 265 capabilities.NumberInputValueCaps, 266 &collection_info); 267 CollectInfoFromValueCaps(preparsed_data, 268 HidP_Output, 269 capabilities.NumberOutputValueCaps, 270 &collection_info); 271 CollectInfoFromValueCaps(preparsed_data, 272 HidP_Feature, 273 capabilities.NumberFeatureValueCaps, 274 &collection_info); 275 if (!collection_info.report_ids.empty()) { 276 device_info.has_report_id = true; 277 } 278 device_info.collections.push_back(collection_info); 279 } 280 // Whether or not the device includes report IDs in its reports the size 281 // of the report ID is included in the value provided by Windows. This 282 // appears contrary to the MSDN documentation. 283 if (device_info.max_input_report_size > 0) { 284 device_info.max_input_report_size--; 285 } 286 if (device_info.max_output_report_size > 0) { 287 device_info.max_output_report_size--; 288 } 289 if (device_info.max_feature_report_size > 0) { 290 device_info.max_feature_report_size--; 291 } 292 HidD_FreePreparsedData(preparsed_data); 293 } 294 295 AddDevice(device_info); 296 } 297 298 void HidServiceWin::PlatformRemoveDevice(const std::string& device_path) { 299 RemoveDevice(device_path); 300 } 301 302 void HidServiceWin::GetDevices(std::vector<HidDeviceInfo>* devices) { 303 Enumerate(); 304 HidService::GetDevices(devices); 305 } 306 307 scoped_refptr<HidConnection> HidServiceWin::Connect( 308 const HidDeviceId& device_id) { 309 HidDeviceInfo device_info; 310 if (!GetDeviceInfo(device_id, &device_info)) 311 return NULL; 312 scoped_refptr<HidConnectionWin> connection(new HidConnectionWin(device_info)); 313 if (!connection->available()) { 314 PLOG(ERROR) << "Failed to open device."; 315 return NULL; 316 } 317 return connection; 318 } 319 320 } // namespace device 321