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.
5 #include "device/hid/hid_service_linux.h"
7 #include <linux/hidraw.h>
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 "device/hid/hid_connection_linux.h"
22 #include "device/hid/hid_device_info.h"
23 #include "device/hid/hid_report_descriptor.h"
24 #include "device/udev_linux/udev.h"
30 const char kHIDSubSystem
[] = "hid";
31 const char kHidrawSubsystem
[] = "hidraw";
32 const char kHIDID
[] = "HID_ID";
33 const char kHIDName
[] = "HID_NAME";
34 const char kHIDUnique
[] = "HID_UNIQ";
38 HidServiceLinux::HidServiceLinux() {
39 DeviceMonitorLinux
* monitor
= DeviceMonitorLinux::GetInstance();
40 monitor
->AddObserver(this);
42 base::Bind(&HidServiceLinux::OnDeviceAdded
, base::Unretained(this)));
45 scoped_refptr
<HidConnection
> HidServiceLinux::Connect(
46 const HidDeviceId
& device_id
) {
47 HidDeviceInfo device_info
;
48 if (!GetDeviceInfo(device_id
, &device_info
))
51 ScopedUdevDevicePtr device
=
52 DeviceMonitorLinux::GetInstance()->GetDeviceFromPath(
53 device_info
.device_id
);
57 if (!FindHidrawDevNode(device
.get(), &dev_node
)) {
58 LOG(ERROR
) << "Cannot open HID device as hidraw device.";
61 return new HidConnectionLinux(device_info
, dev_node
);
67 HidServiceLinux::~HidServiceLinux() {
68 if (DeviceMonitorLinux::HasInstance())
69 DeviceMonitorLinux::GetInstance()->RemoveObserver(this);
72 void HidServiceLinux::OnDeviceAdded(udev_device
* device
) {
76 const char* device_path
= udev_device_get_syspath(device
);
79 const char* subsystem
= udev_device_get_subsystem(device
);
80 if (!subsystem
|| strcmp(subsystem
, kHIDSubSystem
) != 0)
83 HidDeviceInfo device_info
;
84 device_info
.device_id
= device_path
;
86 uint32_t int_property
= 0;
87 const char* str_property
= NULL
;
89 const char* hid_id
= udev_device_get_property_value(device
, kHIDID
);
93 std::vector
<std::string
> parts
;
94 base::SplitString(hid_id
, ':', &parts
);
95 if (parts
.size() != 3) {
99 if (HexStringToUInt(base::StringPiece(parts
[1]), &int_property
)) {
100 device_info
.vendor_id
= int_property
;
103 if (HexStringToUInt(base::StringPiece(parts
[2]), &int_property
)) {
104 device_info
.product_id
= int_property
;
107 str_property
= udev_device_get_property_value(device
, kHIDUnique
);
108 if (str_property
!= NULL
)
109 device_info
.serial_number
= str_property
;
111 str_property
= udev_device_get_property_value(device
, kHIDName
);
112 if (str_property
!= NULL
)
113 device_info
.product_name
= str_property
;
115 std::string dev_node
;
116 if (!FindHidrawDevNode(device
, &dev_node
)) {
117 LOG(ERROR
) << "Cannot find device node for HID device.";
121 int flags
= base::File::FLAG_OPEN
| base::File::FLAG_READ
;
123 base::File
device_file(base::FilePath(dev_node
), flags
);
124 if (!device_file
.IsValid()) {
125 LOG(ERROR
) << "Cannot open '" << dev_node
<< "': "
126 << base::File::ErrorToString(device_file
.error_details());
131 int res
= ioctl(device_file
.GetPlatformFile(), HIDIOCGRDESCSIZE
, &desc_size
);
133 PLOG(ERROR
) << "Failed to get report descriptor size";
138 hidraw_report_descriptor rpt_desc
;
139 rpt_desc
.size
= desc_size
;
141 res
= ioctl(device_file
.GetPlatformFile(), HIDIOCGRDESC
, &rpt_desc
);
143 PLOG(ERROR
) << "Failed to get report descriptor";
150 HidReportDescriptor
report_descriptor(rpt_desc
.value
, rpt_desc
.size
);
151 report_descriptor
.GetDetails(&device_info
.collections
,
152 &device_info
.max_input_report_size
,
153 &device_info
.max_output_report_size
,
154 &device_info
.max_feature_report_size
);
156 AddDevice(device_info
);
159 void HidServiceLinux::OnDeviceRemoved(udev_device
* device
) {
160 const char* device_path
= udev_device_get_syspath(device
);;
162 RemoveDevice(device_path
);
165 bool HidServiceLinux::FindHidrawDevNode(udev_device
* parent
,
166 std::string
* result
) {
167 udev
* udev
= udev_device_get_udev(parent
);
171 ScopedUdevEnumeratePtr
enumerate(udev_enumerate_new(udev
));
175 if (udev_enumerate_add_match_subsystem(enumerate
.get(), kHidrawSubsystem
)) {
178 if (udev_enumerate_scan_devices(enumerate
.get())) {
181 std::string
parent_path(udev_device_get_devpath(parent
));
182 if (parent_path
.length() == 0 || *parent_path
.rbegin() != '/')
184 udev_list_entry
* devices
= udev_enumerate_get_list_entry(enumerate
.get());
185 for (udev_list_entry
* i
= devices
; i
!= NULL
;
186 i
= udev_list_entry_get_next(i
)) {
187 ScopedUdevDevicePtr
hid_dev(
188 udev_device_new_from_syspath(udev
, udev_list_entry_get_name(i
)));
189 const char* raw_path
= udev_device_get_devnode(hid_dev
.get());
190 std::string device_path
= udev_device_get_devpath(hid_dev
.get());
192 !device_path
.compare(0, parent_path
.length(), parent_path
)) {
193 std::string sub_path
= device_path
.substr(parent_path
.length());
194 if (sub_path
.substr(0, sizeof(kHidrawSubsystem
) - 1) ==
205 } // namespace device