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_linux.h"
      6 
      7 #include <linux/hidraw.h>
      8 #include <sys/ioctl.h>
      9 #include <stdint.h>
     10 
     11 #include <string>
     12 
     13 #include "base/bind.h"
     14 #include "base/files/file.h"
     15 #include "base/files/file_path.h"
     16 #include "base/logging.h"
     17 #include "base/stl_util.h"
     18 #include "base/strings/string_number_conversions.h"
     19 #include "base/strings/string_piece.h"
     20 #include "base/strings/string_split.h"
     21 #include "base/threading/thread_restrictions.h"
     22 #include "device/hid/hid_connection_linux.h"
     23 #include "device/hid/hid_device_info.h"
     24 #include "device/hid/hid_report_descriptor.h"
     25 #include "device/udev_linux/udev.h"
     26 
     27 #if defined(OS_CHROMEOS)
     28 #include "base/sys_info.h"
     29 #include "chromeos/dbus/dbus_thread_manager.h"
     30 #include "chromeos/dbus/permission_broker_client.h"
     31 #endif  // defined(OS_CHROMEOS)
     32 
     33 namespace device {
     34 
     35 namespace {
     36 
     37 const char kHidrawSubsystem[] = "hidraw";
     38 const char kHIDID[] = "HID_ID";
     39 const char kHIDName[] = "HID_NAME";
     40 const char kHIDUnique[] = "HID_UNIQ";
     41 
     42 }  // namespace
     43 
     44 HidServiceLinux::HidServiceLinux(
     45     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
     46     : ui_task_runner_(ui_task_runner),
     47       weak_factory_(this) {
     48   base::ThreadRestrictions::AssertIOAllowed();
     49   DeviceMonitorLinux* monitor = DeviceMonitorLinux::GetInstance();
     50   monitor->AddObserver(this);
     51   monitor->Enumerate(
     52       base::Bind(&HidServiceLinux::OnDeviceAdded, weak_factory_.GetWeakPtr()));
     53 }
     54 
     55 scoped_refptr<HidConnection> HidServiceLinux::Connect(
     56     const HidDeviceId& device_id) {
     57   HidDeviceInfo device_info;
     58   if (!GetDeviceInfo(device_id, &device_info))
     59     return NULL;
     60 
     61   ScopedUdevDevicePtr device =
     62       DeviceMonitorLinux::GetInstance()->GetDeviceFromPath(
     63           device_info.device_id);
     64 
     65   if (device) {
     66     std::string dev_node = udev_device_get_devnode(device.get());
     67     return new HidConnectionLinux(device_info, dev_node);
     68   }
     69 
     70   return NULL;
     71 }
     72 
     73 HidServiceLinux::~HidServiceLinux() {
     74   if (DeviceMonitorLinux::HasInstance())
     75     DeviceMonitorLinux::GetInstance()->RemoveObserver(this);
     76 }
     77 
     78 void HidServiceLinux::OnDeviceAdded(udev_device* device) {
     79   if (!device)
     80     return;
     81 
     82   const char* device_path = udev_device_get_syspath(device);
     83   if (!device_path)
     84     return;
     85   const char* subsystem = udev_device_get_subsystem(device);
     86   if (!subsystem || strcmp(subsystem, kHidrawSubsystem) != 0)
     87     return;
     88 
     89   scoped_ptr<HidDeviceInfo> device_info(new HidDeviceInfo);
     90   device_info->device_id = device_path;
     91 
     92   uint32_t int_property = 0;
     93   const char* str_property = NULL;
     94 
     95   udev_device *parent = udev_device_get_parent(device);
     96   if (!parent) {
     97     return;
     98   }
     99 
    100   const char* hid_id = udev_device_get_property_value(parent, kHIDID);
    101   if (!hid_id)
    102     return;
    103 
    104   std::vector<std::string> parts;
    105   base::SplitString(hid_id, ':', &parts);
    106   if (parts.size() != 3) {
    107     return;
    108   }
    109 
    110   if (HexStringToUInt(base::StringPiece(parts[1]), &int_property)) {
    111     device_info->vendor_id = int_property;
    112   }
    113 
    114   if (HexStringToUInt(base::StringPiece(parts[2]), &int_property)) {
    115     device_info->product_id = int_property;
    116   }
    117 
    118   str_property = udev_device_get_property_value(parent, kHIDUnique);
    119   if (str_property != NULL)
    120     device_info->serial_number = str_property;
    121 
    122   str_property = udev_device_get_property_value(parent, kHIDName);
    123   if (str_property != NULL)
    124     device_info->product_name = str_property;
    125 
    126   const std::string dev_node = udev_device_get_devnode(device);
    127 #if defined(OS_CHROMEOS)
    128   // ChromeOS builds on non-ChromeOS machines (dev) should not attempt to
    129   // use permission broker.
    130   if (base::SysInfo::IsRunningOnChromeOS()) {
    131     chromeos::PermissionBrokerClient* client =
    132         chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient();
    133     DCHECK(client) << "Could not get permission broker client.";
    134     if (!client) {
    135       return;
    136     }
    137     ui_task_runner_->PostTask(
    138         FROM_HERE,
    139         base::Bind(&chromeos::PermissionBrokerClient::RequestPathAccess,
    140                    base::Unretained(client),
    141                    dev_node,
    142                    -1,
    143                    base::Bind(&HidServiceLinux::OnRequestAccessComplete,
    144                               weak_factory_.GetWeakPtr(),
    145                               dev_node,
    146                               base::Passed(&device_info))));
    147   } else {
    148     OnRequestAccessComplete(dev_node, device_info.Pass(), true);
    149   }
    150 #else
    151   OnRequestAccessComplete(dev_node, device_info.Pass(), true);
    152 #endif  // defined(OS_CHROMEOS)
    153 }
    154 
    155 void HidServiceLinux::OnDeviceRemoved(udev_device* device) {
    156   const char* device_path = udev_device_get_syspath(device);;
    157   if (device_path)
    158     RemoveDevice(device_path);
    159 }
    160 
    161 void HidServiceLinux::OnRequestAccessComplete(
    162     const std::string& path,
    163     scoped_ptr<HidDeviceInfo> device_info,
    164     bool success) {
    165   const int flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
    166   base::File device_file(base::FilePath(path), flags);
    167   if (!device_file.IsValid()) {
    168     LOG(ERROR) << "Cannot open '" << path << "': "
    169         << base::File::ErrorToString(device_file.error_details());
    170     return;
    171   }
    172 
    173   int desc_size = 0;
    174   int res = ioctl(device_file.GetPlatformFile(), HIDIOCGRDESCSIZE, &desc_size);
    175   if (res < 0) {
    176     PLOG(ERROR) << "Failed to get report descriptor size";
    177     device_file.Close();
    178     return;
    179   }
    180 
    181   hidraw_report_descriptor rpt_desc;
    182   rpt_desc.size = desc_size;
    183 
    184   res = ioctl(device_file.GetPlatformFile(), HIDIOCGRDESC, &rpt_desc);
    185   if (res < 0) {
    186     PLOG(ERROR) << "Failed to get report descriptor";
    187     device_file.Close();
    188     return;
    189   }
    190 
    191   device_file.Close();
    192 
    193   HidReportDescriptor report_descriptor(rpt_desc.value, rpt_desc.size);
    194   report_descriptor.GetDetails(&device_info->collections,
    195                                &device_info->has_report_id,
    196                                &device_info->max_input_report_size,
    197                                &device_info->max_output_report_size,
    198                                &device_info->max_feature_report_size);
    199 
    200   AddDevice(*device_info);
    201 }
    202 
    203 }  // namespace device
    204