Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / device / hid / hid_service_linux.cc
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 <linux/hidraw.h>
6 #include <sys/ioctl.h>
7
8 #include <stdint.h>
9
10 #include <string>
11
12 #include "base/bind.h"
13 #include "base/files/file_path.h"
14 #include "base/logging.h"
15 #include "base/platform_file.h"
16 #include "base/stl_util.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/string_piece.h"
19 #include "base/strings/string_split.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/hid/hid_service_linux.h"
24 #include "device/hid/udev_common.h"
25
26 namespace device {
27
28 namespace {
29
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";
35
36 } // namespace
37
38 HidServiceLinux::HidServiceLinux() {
39   DeviceMonitorLinux* monitor = DeviceMonitorLinux::GetInstance();
40   monitor->AddObserver(this);
41   monitor->Enumerate(
42       base::Bind(&HidServiceLinux::OnDeviceAdded, base::Unretained(this)));
43 }
44
45 scoped_refptr<HidConnection> HidServiceLinux::Connect(
46     const HidDeviceId& device_id) {
47   HidDeviceInfo device_info;
48   if (!GetDeviceInfo(device_id, &device_info))
49     return NULL;
50
51   ScopedUdevDevicePtr device =
52       DeviceMonitorLinux::GetInstance()->GetDeviceFromPath(
53           device_info.device_id);
54
55   if (device) {
56     std::string dev_node;
57     if (!FindHidrawDevNode(device.get(), &dev_node)) {
58       LOG(ERROR) << "Cannot open HID device as hidraw device.";
59       return NULL;
60     }
61     return new HidConnectionLinux(device_info, dev_node);
62   }
63
64   return NULL;
65 }
66
67 HidServiceLinux::~HidServiceLinux() {
68   if (DeviceMonitorLinux::HasInstance())
69     DeviceMonitorLinux::GetInstance()->RemoveObserver(this);
70 }
71
72 void HidServiceLinux::OnDeviceAdded(udev_device* device) {
73   if (!device)
74     return;
75
76   const char* device_path = udev_device_get_syspath(device);
77   if (!device_path)
78     return;
79   const char* subsystem = udev_device_get_subsystem(device);
80   if (!subsystem || strcmp(subsystem, kHIDSubSystem) != 0)
81     return;
82
83   HidDeviceInfo device_info;
84   device_info.device_id = device_path;
85
86   uint32_t int_property = 0;
87   const char* str_property = NULL;
88
89   const char* hid_id = udev_device_get_property_value(device, kHIDID);
90   if (!hid_id)
91     return;
92
93   std::vector<std::string> parts;
94   base::SplitString(hid_id, ':', &parts);
95   if (parts.size() != 3) {
96     return;
97   }
98
99   if (HexStringToUInt(base::StringPiece(parts[1]), &int_property)) {
100     device_info.vendor_id = int_property;
101   }
102
103   if (HexStringToUInt(base::StringPiece(parts[2]), &int_property)) {
104     device_info.product_id = int_property;
105   }
106
107   str_property = udev_device_get_property_value(device, kHIDUnique);
108   if (str_property != NULL)
109     device_info.serial_number = str_property;
110
111   str_property = udev_device_get_property_value(device, kHIDName);
112   if (str_property != NULL)
113     device_info.product_name = str_property;
114
115   std::string dev_node;
116   if (!FindHidrawDevNode(device, &dev_node)) {
117     LOG(ERROR) << "Cannot open HID device as hidraw device.";
118     return;
119   }
120
121   int flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
122
123   base::File device_file(base::FilePath(dev_node), flags);
124   if (!device_file.IsValid()) {
125     LOG(ERROR) << device_file.error_details();
126     return;
127   }
128
129   int desc_size = 0;
130   int res = ioctl(device_file.GetPlatformFile(), HIDIOCGRDESCSIZE, &desc_size);
131   if (res < 0) {
132     LOG(ERROR) << "HIDIOCGRDESCSIZE failed.";
133     device_file.Close();
134     return;
135   }
136
137   hidraw_report_descriptor rpt_desc;
138   rpt_desc.size = desc_size;
139
140   res = ioctl(device_file.GetPlatformFile(), HIDIOCGRDESC, &rpt_desc);
141   if (res < 0) {
142     LOG(ERROR) << "HIDIOCGRDESC failed.";
143     device_file.Close();
144     return;
145   }
146
147   device_file.Close();
148
149   HidReportDescriptor report_descriptor(rpt_desc.value, rpt_desc.size);
150   report_descriptor.GetTopLevelCollections(&device_info.usages);
151
152   AddDevice(device_info);
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 bool HidServiceLinux::FindHidrawDevNode(udev_device* parent,
162                                         std::string* result) {
163   udev* udev = udev_device_get_udev(parent);
164   if (!udev) {
165     return false;
166   }
167   ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev));
168   if (!enumerate) {
169     return false;
170   }
171   if (udev_enumerate_add_match_subsystem(enumerate.get(), kHidrawSubsystem)) {
172     return false;
173   }
174   if (udev_enumerate_scan_devices(enumerate.get())) {
175     return false;
176   }
177   std::string parent_path(udev_device_get_devpath(parent));
178   if (parent_path.length() == 0 || *parent_path.rbegin() != '/')
179     parent_path += '/';
180   udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate.get());
181   for (udev_list_entry* i = devices; i != NULL;
182        i = udev_list_entry_get_next(i)) {
183     ScopedUdevDevicePtr hid_dev(
184         udev_device_new_from_syspath(udev, udev_list_entry_get_name(i)));
185     const char* raw_path = udev_device_get_devnode(hid_dev.get());
186     std::string device_path = udev_device_get_devpath(hid_dev.get());
187     if (raw_path &&
188         !device_path.compare(0, parent_path.length(), parent_path)) {
189       std::string sub_path = device_path.substr(parent_path.length());
190       if (sub_path.substr(0, sizeof(kHidrawSubsystem) - 1) ==
191           kHidrawSubsystem) {
192         *result = raw_path;
193         return true;
194       }
195     }
196   }
197
198   return false;
199 }
200
201 } // namespace dev