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"
10 #include "base/files/file.h"
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/logging.h"
14 #include "base/stl_util.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_piece.h"
17 #include "base/strings/string_split.h"
18 #include "base/thread_task_runner_handle.h"
19 #include "base/threading/thread_restrictions.h"
20 #include "device/hid/hid_connection_linux.h"
21 #include "device/hid/hid_device_info.h"
22 #include "device/hid/hid_report_descriptor.h"
23 #include "device/udev_linux/scoped_udev.h"
25 #if defined(OS_CHROMEOS)
26 #include "base/sys_info.h"
27 #include "chromeos/dbus/dbus_thread_manager.h"
28 #include "chromeos/dbus/permission_broker_client.h"
29 #endif // defined(OS_CHROMEOS)
35 const char kHidrawSubsystem
[] = "hidraw";
36 const char kHIDID
[] = "HID_ID";
37 const char kHIDName
[] = "HID_NAME";
38 const char kHIDUnique
[] = "HID_UNIQ";
39 const char kSysfsReportDescriptorKey
[] = "report_descriptor";
41 #if defined(OS_CHROMEOS)
42 void OnRequestAccessComplete(
43 scoped_refptr
<base::SingleThreadTaskRunner
> reply_task_runner
,
44 const base::Callback
<void(bool success
)>& callback
,
46 reply_task_runner
->PostTask(FROM_HERE
, base::Bind(callback
, success
));
50 const std::string
& device_node
,
51 scoped_refptr
<base::SingleThreadTaskRunner
> reply_task_runner
,
52 const base::Callback
<void(bool success
)>& callback
) {
55 if (base::SysInfo::IsRunningOnChromeOS()) {
56 chromeos::PermissionBrokerClient
* client
=
57 chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient();
58 DCHECK(client
) << "Could not get permission broker client.";
60 client
->RequestPathAccess(
63 base::Bind(OnRequestAccessComplete
, reply_task_runner
, callback
));
67 // Not really running on Chrome OS, declare success.
71 reply_task_runner
->PostTask(FROM_HERE
, base::Bind(callback
, success
));
77 HidServiceLinux::HidServiceLinux(
78 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner
)
79 : ui_task_runner_(ui_task_runner
),
81 base::ThreadRestrictions::AssertIOAllowed();
82 task_runner_
= base::ThreadTaskRunnerHandle::Get();
83 DeviceMonitorLinux
* monitor
= DeviceMonitorLinux::GetInstance();
84 monitor
->AddObserver(this);
86 base::Bind(&HidServiceLinux::OnDeviceAdded
, weak_factory_
.GetWeakPtr()));
89 void HidServiceLinux::Connect(const HidDeviceId
& device_id
,
90 const ConnectCallback
& callback
) {
91 DCHECK(thread_checker_
.CalledOnValidThread());
93 ScopedUdevDevicePtr device
=
94 DeviceMonitorLinux::GetInstance()->GetDeviceFromPath(
97 task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, nullptr));
101 const char* device_node
= udev_device_get_devnode(device
.get());
103 task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, nullptr));
107 base::Callback
<void(bool success
)> finish_connect
=
108 base::Bind(&HidServiceLinux::FinishConnect
,
109 weak_factory_
.GetWeakPtr(),
111 std::string(device_node
),
114 #if defined(OS_CHROMEOS)
115 ui_task_runner_
->PostTask(FROM_HERE
,
116 base::Bind(RequestAccess
,
117 std::string(device_node
),
121 // Use the task runner to preserve the asynchronous behavior of this call on
122 // non-Chrome OS platforms.
123 task_runner_
->PostTask(FROM_HERE
, base::Bind(finish_connect
, true));
127 HidServiceLinux::~HidServiceLinux() {
128 if (DeviceMonitorLinux::HasInstance())
129 DeviceMonitorLinux::GetInstance()->RemoveObserver(this);
132 void HidServiceLinux::OnDeviceAdded(udev_device
* device
) {
136 const char* device_path
= udev_device_get_syspath(device
);
139 const char* subsystem
= udev_device_get_subsystem(device
);
140 if (!subsystem
|| strcmp(subsystem
, kHidrawSubsystem
) != 0)
143 HidDeviceInfo device_info
;
144 device_info
.device_id
= device_path
;
146 uint32_t int_property
= 0;
147 const char* str_property
= NULL
;
149 udev_device
* parent
= udev_device_get_parent(device
);
154 const char* hid_id
= udev_device_get_property_value(parent
, kHIDID
);
159 std::vector
<std::string
> parts
;
160 base::SplitString(hid_id
, ':', &parts
);
161 if (parts
.size() != 3) {
165 if (HexStringToUInt(base::StringPiece(parts
[1]), &int_property
)) {
166 device_info
.vendor_id
= int_property
;
169 if (HexStringToUInt(base::StringPiece(parts
[2]), &int_property
)) {
170 device_info
.product_id
= int_property
;
173 str_property
= udev_device_get_property_value(parent
, kHIDUnique
);
174 if (str_property
!= NULL
) {
175 device_info
.serial_number
= str_property
;
178 str_property
= udev_device_get_property_value(parent
, kHIDName
);
179 if (str_property
!= NULL
) {
180 device_info
.product_name
= str_property
;
183 const char* parent_sysfs_path
= udev_device_get_syspath(parent
);
184 if (!parent_sysfs_path
) {
187 base::FilePath report_descriptor_path
=
188 base::FilePath(parent_sysfs_path
).Append(kSysfsReportDescriptorKey
);
189 std::string report_descriptor_str
;
190 if (!base::ReadFileToString(report_descriptor_path
, &report_descriptor_str
)) {
194 HidReportDescriptor
report_descriptor(
195 reinterpret_cast<uint8_t*>(&report_descriptor_str
[0]),
196 report_descriptor_str
.length());
197 report_descriptor
.GetDetails(&device_info
.collections
,
198 &device_info
.has_report_id
,
199 &device_info
.max_input_report_size
,
200 &device_info
.max_output_report_size
,
201 &device_info
.max_feature_report_size
);
203 AddDevice(device_info
);
206 void HidServiceLinux::OnDeviceRemoved(udev_device
* device
) {
207 const char* device_path
= udev_device_get_syspath(device
);;
209 RemoveDevice(device_path
);
213 void HidServiceLinux::FinishConnect(
214 const HidDeviceId
& device_id
,
215 const std::string device_node
,
216 const base::Callback
<void(scoped_refptr
<HidConnection
>)>& callback
,
218 DCHECK(thread_checker_
.CalledOnValidThread());
220 callback
.Run(nullptr);
223 const auto& map_entry
= devices().find(device_id
);
224 if (map_entry
== devices().end()) {
225 callback
.Run(nullptr);
228 callback
.Run(new HidConnectionLinux(map_entry
->second
, device_node
));
231 } // namespace device