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 "device/hid/hid_connection_win.h"
     13 #include "device/hid/hid_device_info.h"
     14 #include "net/base/io_buffer.h"
     15 
     16 #if defined(OS_WIN)
     17 
     18 #define INITGUID
     19 
     20 #include <windows.h>
     21 #include <hidclass.h>
     22 
     23 extern "C" {
     24 
     25 #include <hidsdi.h>
     26 #include <hidpi.h>
     27 
     28 }
     29 
     30 #include <setupapi.h>
     31 #include <winioctl.h>
     32 #include "base/win/scoped_handle.h"
     33 
     34 #endif  // defined(OS_WIN)
     35 
     36 // Setup API is required to enumerate HID devices.
     37 #pragma comment(lib, "setupapi.lib")
     38 #pragma comment(lib, "hid.lib")
     39 
     40 namespace device {
     41 namespace {
     42 
     43 const char kHIDClass[] = "HIDClass";
     44 
     45 }  // namespace
     46 
     47 HidServiceWin::HidServiceWin() {
     48   Enumerate();
     49 }
     50 
     51 HidServiceWin::~HidServiceWin() {}
     52 
     53 void HidServiceWin::Enumerate() {
     54   BOOL res;
     55   HDEVINFO device_info_set;
     56   SP_DEVINFO_DATA devinfo_data;
     57   SP_DEVICE_INTERFACE_DATA device_interface_data;
     58 
     59   memset(&devinfo_data, 0, sizeof(SP_DEVINFO_DATA));
     60   devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA);
     61   device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
     62 
     63   device_info_set = SetupDiGetClassDevs(
     64       &GUID_DEVINTERFACE_HID,
     65       NULL,
     66       NULL,
     67       DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
     68 
     69   if (device_info_set == INVALID_HANDLE_VALUE)
     70     return;
     71 
     72   for (int device_index = 0;
     73        SetupDiEnumDeviceInterfaces(device_info_set,
     74                                    NULL,
     75                                    &GUID_DEVINTERFACE_HID,
     76                                    device_index,
     77                                    &device_interface_data);
     78        ++device_index) {
     79     DWORD required_size = 0;
     80 
     81     // Determime the required size of detail struct.
     82     SetupDiGetDeviceInterfaceDetailA(device_info_set,
     83                                      &device_interface_data,
     84                                      NULL,
     85                                      0,
     86                                      &required_size,
     87                                      NULL);
     88 
     89     scoped_ptr<SP_DEVICE_INTERFACE_DETAIL_DATA_A, base::FreeDeleter>
     90         device_interface_detail_data(
     91             static_cast<SP_DEVICE_INTERFACE_DETAIL_DATA_A*>(
     92                 malloc(required_size)));
     93     device_interface_detail_data->cbSize =
     94         sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
     95 
     96     // Get the detailed data for this device.
     97     res = SetupDiGetDeviceInterfaceDetailA(device_info_set,
     98                                            &device_interface_data,
     99                                            device_interface_detail_data.get(),
    100                                            required_size,
    101                                            NULL,
    102                                            NULL);
    103     if (!res)
    104       continue;
    105 
    106     // Enumerate device info. Looking for Setup Class "HIDClass".
    107     for (DWORD i = 0;
    108         SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data);
    109         i++) {
    110       char class_name[256] = {0};
    111       res = SetupDiGetDeviceRegistryPropertyA(device_info_set,
    112                                               &devinfo_data,
    113                                               SPDRP_CLASS,
    114                                               NULL,
    115                                               (PBYTE) class_name,
    116                                               sizeof(class_name) - 1,
    117                                               NULL);
    118       if (!res)
    119         break;
    120       if (memcmp(class_name, kHIDClass, sizeof(kHIDClass)) == 0) {
    121         char driver_name[256] = {0};
    122         // Get bounded driver.
    123         res = SetupDiGetDeviceRegistryPropertyA(device_info_set,
    124                                                 &devinfo_data,
    125                                                 SPDRP_DRIVER,
    126                                                 NULL,
    127                                                 (PBYTE) driver_name,
    128                                                 sizeof(driver_name) - 1,
    129                                                 NULL);
    130         if (res) {
    131           // Found the driver.
    132           break;
    133         }
    134       }
    135     }
    136 
    137     if (!res)
    138       continue;
    139 
    140     PlatformAddDevice(device_interface_detail_data->DevicePath);
    141   }
    142 }
    143 
    144 void HidServiceWin::PlatformAddDevice(const std::string& device_path) {
    145   HidDeviceInfo device_info;
    146   device_info.device_id = device_path;
    147 
    148   // Try to open the device.
    149   base::win::ScopedHandle device_handle(
    150       CreateFileA(device_path.c_str(),
    151                   GENERIC_WRITE | GENERIC_READ,
    152                   FILE_SHARE_READ | FILE_SHARE_WRITE,
    153                   NULL,
    154                   OPEN_EXISTING,
    155                   FILE_FLAG_OVERLAPPED,
    156                   0));
    157 
    158   if (!device_handle.IsValid() &&
    159       GetLastError() == base::File::FILE_ERROR_ACCESS_DENIED) {
    160     base::win::ScopedHandle device_handle(
    161       CreateFileA(device_path.c_str(),
    162       GENERIC_READ,
    163       FILE_SHARE_READ,
    164       NULL,
    165       OPEN_EXISTING,
    166       FILE_FLAG_OVERLAPPED,
    167       0));
    168 
    169     if (!device_handle.IsValid())
    170       return;
    171   }
    172 
    173   // Get VID/PID pair.
    174   HIDD_ATTRIBUTES attrib = {0};
    175   attrib.Size = sizeof(HIDD_ATTRIBUTES);
    176   if (!HidD_GetAttributes(device_handle.Get(), &attrib))
    177     return;
    178 
    179   device_info.vendor_id = attrib.VendorID;
    180   device_info.product_id = attrib.ProductID;
    181 
    182   for (ULONG i = 32;
    183       HidD_SetNumInputBuffers(device_handle.Get(), i);
    184       i <<= 1);
    185 
    186   // Get usage and usage page (optional).
    187   PHIDP_PREPARSED_DATA preparsed_data;
    188   if (HidD_GetPreparsedData(device_handle.Get(), &preparsed_data) &&
    189       preparsed_data) {
    190     HIDP_CAPS capabilities;
    191     if (HidP_GetCaps(preparsed_data, &capabilities) == HIDP_STATUS_SUCCESS) {
    192       device_info.input_report_size = capabilities.InputReportByteLength;
    193       device_info.output_report_size = capabilities.OutputReportByteLength;
    194       device_info.feature_report_size = capabilities.FeatureReportByteLength;
    195       device_info.usages.push_back(HidUsageAndPage(
    196         capabilities.Usage,
    197         static_cast<HidUsageAndPage::Page>(capabilities.UsagePage)));
    198     }
    199     // Detect if the device supports report ids.
    200     if (capabilities.NumberInputValueCaps > 0) {
    201       scoped_ptr<HIDP_VALUE_CAPS[]> value_caps(
    202           new HIDP_VALUE_CAPS[capabilities.NumberInputValueCaps]);
    203       USHORT value_caps_length = capabilities.NumberInputValueCaps;
    204       if (HidP_GetValueCaps(HidP_Input, &value_caps[0], &value_caps_length,
    205                             preparsed_data) == HIDP_STATUS_SUCCESS) {
    206         device_info.has_report_id = (value_caps[0].ReportID != 0);
    207       }
    208     }
    209     if (!device_info.has_report_id && capabilities.NumberInputButtonCaps > 0)
    210     {
    211       scoped_ptr<HIDP_BUTTON_CAPS[]> button_caps(
    212         new HIDP_BUTTON_CAPS[capabilities.NumberInputButtonCaps]);
    213       USHORT button_caps_length = capabilities.NumberInputButtonCaps;
    214       if (HidP_GetButtonCaps(HidP_Input,
    215                              &button_caps[0],
    216                              &button_caps_length,
    217                              preparsed_data) == HIDP_STATUS_SUCCESS) {
    218         device_info.has_report_id = (button_caps[0].ReportID != 0);
    219       }
    220     }
    221 
    222     HidD_FreePreparsedData(preparsed_data);
    223   }
    224 
    225   AddDevice(device_info);
    226 }
    227 
    228 void HidServiceWin::PlatformRemoveDevice(const std::string& device_path) {
    229   RemoveDevice(device_path);
    230 }
    231 
    232 void HidServiceWin::GetDevices(std::vector<HidDeviceInfo>* devices) {
    233   Enumerate();
    234   HidService::GetDevices(devices);
    235 }
    236 
    237 scoped_refptr<HidConnection> HidServiceWin::Connect(
    238     const HidDeviceId& device_id) {
    239   HidDeviceInfo device_info;
    240   if (!GetDeviceInfo(device_id, &device_info))
    241     return NULL;
    242   scoped_refptr<HidConnectionWin> connection(new HidConnectionWin(device_info));
    243   if (!connection->available()) {
    244     PLOG(ERROR) << "Failed to open device.";
    245     return NULL;
    246   }
    247   return connection;
    248 }
    249 
    250 }  // namespace device
    251