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"
26 #if defined(OS_CHROMEOS)
27 #include "base/sys_info.h"
28 #include "chromeos/dbus/dbus_thread_manager.h"
29 #include "chromeos/dbus/permission_broker_client.h"
30 #endif // defined(OS_CHROMEOS)
36 const char kHidrawSubsystem
[] = "hidraw";
37 const char kHIDID
[] = "HID_ID";
38 const char kHIDName
[] = "HID_NAME";
39 const char kHIDUnique
[] = "HID_UNIQ";
43 HidServiceLinux::HidServiceLinux(
44 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner
)
45 : ui_task_runner_(ui_task_runner
),
47 DeviceMonitorLinux
* monitor
= DeviceMonitorLinux::GetInstance();
48 monitor
->AddObserver(this);
50 base::Bind(&HidServiceLinux::OnDeviceAdded
, weak_factory_
.GetWeakPtr()));
53 scoped_refptr
<HidConnection
> HidServiceLinux::Connect(
54 const HidDeviceId
& device_id
) {
55 HidDeviceInfo device_info
;
56 if (!GetDeviceInfo(device_id
, &device_info
))
59 ScopedUdevDevicePtr device
=
60 DeviceMonitorLinux::GetInstance()->GetDeviceFromPath(
61 device_info
.device_id
);
64 std::string dev_node
= udev_device_get_devnode(device
.get());
65 return new HidConnectionLinux(device_info
, dev_node
);
71 HidServiceLinux::~HidServiceLinux() {
72 if (DeviceMonitorLinux::HasInstance())
73 DeviceMonitorLinux::GetInstance()->RemoveObserver(this);
76 void HidServiceLinux::OnDeviceAdded(udev_device
* device
) {
80 const char* device_path
= udev_device_get_syspath(device
);
83 const char* subsystem
= udev_device_get_subsystem(device
);
84 if (!subsystem
|| strcmp(subsystem
, kHidrawSubsystem
) != 0)
87 scoped_ptr
<HidDeviceInfo
> device_info(new HidDeviceInfo
);
88 device_info
->device_id
= device_path
;
90 uint32_t int_property
= 0;
91 const char* str_property
= NULL
;
93 udev_device
*parent
= udev_device_get_parent(device
);
98 const char* hid_id
= udev_device_get_property_value(parent
, kHIDID
);
102 std::vector
<std::string
> parts
;
103 base::SplitString(hid_id
, ':', &parts
);
104 if (parts
.size() != 3) {
108 if (HexStringToUInt(base::StringPiece(parts
[1]), &int_property
)) {
109 device_info
->vendor_id
= int_property
;
112 if (HexStringToUInt(base::StringPiece(parts
[2]), &int_property
)) {
113 device_info
->product_id
= int_property
;
116 str_property
= udev_device_get_property_value(parent
, kHIDUnique
);
117 if (str_property
!= NULL
)
118 device_info
->serial_number
= str_property
;
120 str_property
= udev_device_get_property_value(parent
, kHIDName
);
121 if (str_property
!= NULL
)
122 device_info
->product_name
= str_property
;
124 const std::string dev_node
= udev_device_get_devnode(device
);
125 #if defined(OS_CHROMEOS)
126 // ChromeOS builds on non-ChromeOS machines (dev) should not attempt to
127 // use permission broker.
128 if (base::SysInfo::IsRunningOnChromeOS()) {
129 chromeos::PermissionBrokerClient
* client
=
130 chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient();
131 DCHECK(client
) << "Could not get permission broker client.";
135 ui_task_runner_
->PostTask(
137 base::Bind(&chromeos::PermissionBrokerClient::RequestPathAccess
,
138 base::Unretained(client
),
141 base::Bind(&HidServiceLinux::OnRequestAccessComplete
,
142 weak_factory_
.GetWeakPtr(),
144 base::Passed(&device_info
))));
146 OnRequestAccessComplete(dev_node
, device_info
.Pass(), true);
149 OnRequestAccessComplete(dev_node
, device_info
.Pass(), true);
150 #endif // defined(OS_CHROMEOS)
153 void HidServiceLinux::OnDeviceRemoved(udev_device
* device
) {
154 const char* device_path
= udev_device_get_syspath(device
);;
156 RemoveDevice(device_path
);
159 void HidServiceLinux::OnRequestAccessComplete(
160 const std::string
& path
,
161 scoped_ptr
<HidDeviceInfo
> device_info
,
163 const int flags
= base::File::FLAG_OPEN
| base::File::FLAG_READ
;
164 base::File
device_file(base::FilePath(path
), flags
);
165 if (!device_file
.IsValid()) {
166 LOG(ERROR
) << "Cannot open '" << path
<< "': "
167 << base::File::ErrorToString(device_file
.error_details());
172 int res
= ioctl(device_file
.GetPlatformFile(), HIDIOCGRDESCSIZE
, &desc_size
);
174 PLOG(ERROR
) << "Failed to get report descriptor size";
179 hidraw_report_descriptor rpt_desc
;
180 rpt_desc
.size
= desc_size
;
182 res
= ioctl(device_file
.GetPlatformFile(), HIDIOCGRDESC
, &rpt_desc
);
184 PLOG(ERROR
) << "Failed to get report descriptor";
191 HidReportDescriptor
report_descriptor(rpt_desc
.value
, rpt_desc
.size
);
192 report_descriptor
.GetDetails(&device_info
->collections
,
193 &device_info
->has_report_id
,
194 &device_info
->max_input_report_size
,
195 &device_info
->max_output_report_size
,
196 &device_info
->max_feature_report_size
);
198 AddDevice(*device_info
);
201 } // namespace device