Home | History | Annotate | Download | only in hid
      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