Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(607)

Side by Side Diff: device/hid/hid_connection_linux.cc

Issue 161823002: Clean up HID backend and API. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « device/hid/hid_connection_linux.h ('k') | device/hid/hid_connection_mac.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "device/hid/hid_connection_linux.h" 5 #include "device/hid/hid_connection_linux.h"
6 6
7 #include <errno.h> 7 #include <errno.h>
8 #include <fcntl.h> 8 #include <fcntl.h>
9 #include <libudev.h> 9 #include <libudev.h>
10 #include <linux/hidraw.h> 10 #include <linux/hidraw.h>
11 #include <sys/ioctl.h>
12
11 #include <string> 13 #include <string>
12 14
15 #include "base/posix/eintr_wrapper.h"
13 #include "base/threading/thread_restrictions.h" 16 #include "base/threading/thread_restrictions.h"
14 #include "base/tuple.h" 17 #include "base/tuple.h"
15 #include "device/hid/hid_service.h" 18 #include "device/hid/hid_service.h"
16 #include "device/hid/hid_service_linux.h" 19 #include "device/hid/hid_service_linux.h"
17 20
21 // These are already defined in newer versions of linux/hidraw.h.
22 #ifndef HIDIOCSFEATURE
23 #define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE | _IOC_READ, 'H', 0x06, len)
24 #endif
25 #ifndef HIDIOCGFEATURE
26 #define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE | _IOC_READ, 'H', 0x07, len)
27 #endif
18 28
19 namespace device { 29 namespace device {
20 30
21 namespace { 31 namespace {
22 32
33 // Copies a buffer into a new one with a report ID byte inserted at the front.
34 scoped_refptr<net::IOBufferWithSize> CopyBufferWithReportId(
35 scoped_refptr<net::IOBufferWithSize> buffer,
36 uint8_t report_id) {
37 scoped_refptr<net::IOBufferWithSize> new_buffer(
38 new net::IOBufferWithSize(buffer->size() + 1));
39 new_buffer->data()[0] = report_id;
40 memcpy(new_buffer->data() + 1, buffer->data(), buffer->size());
41 return new_buffer;
42 }
43
23 const char kHidrawSubsystem[] = "hidraw"; 44 const char kHidrawSubsystem[] = "hidraw";
24 45
25 } // namespace 46 } // namespace
26 47
27 HidConnectionLinux::HidConnectionLinux(HidDeviceInfo device_info, 48 HidConnectionLinux::HidConnectionLinux(HidDeviceInfo device_info,
28 ScopedUdevDevicePtr udev_raw_device) 49 ScopedUdevDevicePtr udev_raw_device)
29 : HidConnection(device_info), 50 : HidConnection(device_info) {
30 initialized_(false) {
31 DCHECK(thread_checker_.CalledOnValidThread()); 51 DCHECK(thread_checker_.CalledOnValidThread());
32 52
33 udev_device* dev = udev_raw_device.get(); 53 udev_device* dev = udev_raw_device.get();
34 std::string dev_node; 54 std::string dev_node;
35 if (!FindHidrawDevNode(dev, &dev_node)) { 55 if (!FindHidrawDevNode(dev, &dev_node)) {
36 LOG(ERROR) << "Cannot open HID device as hidraw device."; 56 LOG(ERROR) << "Cannot open HID device as hidraw device.";
37 return; 57 return;
38 } 58 }
39 59
40 int flags = base::File::FLAG_OPEN | 60 int flags = base::File::FLAG_OPEN |
(...skipping 13 matching lines...) Expand all
54 return; 74 return;
55 } 75 }
56 device_file_ = device_file.Pass(); 76 device_file_ = device_file.Pass();
57 77
58 if (!base::MessageLoopForIO::current()->WatchFileDescriptor( 78 if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
59 device_file_.GetPlatformFile(), 79 device_file_.GetPlatformFile(),
60 true, 80 true,
61 base::MessageLoopForIO::WATCH_READ_WRITE, 81 base::MessageLoopForIO::WATCH_READ_WRITE,
62 &device_file_watcher_, 82 &device_file_watcher_,
63 this)) { 83 this)) {
64 LOG(ERROR) << "Cannot start watching file descriptor."; 84 LOG(ERROR) << "Failed to start watching device file.";
65 return;
66 } 85 }
67
68 initialized_ = true;
69 } 86 }
70 87
71 HidConnectionLinux::~HidConnectionLinux() { 88 HidConnectionLinux::~HidConnectionLinux() {
72 DCHECK(thread_checker_.CalledOnValidThread()); 89 DCHECK(thread_checker_.CalledOnValidThread());
73 Disconnect(); 90 Disconnect();
74 } 91 }
75 92
76 void HidConnectionLinux::OnFileCanReadWithoutBlocking(int fd) { 93 void HidConnectionLinux::OnFileCanReadWithoutBlocking(int fd) {
77 DCHECK(thread_checker_.CalledOnValidThread()); 94 DCHECK(thread_checker_.CalledOnValidThread());
78 DCHECK_EQ(fd, device_file_.GetPlatformFile()); 95 DCHECK_EQ(fd, device_file_.GetPlatformFile());
79 DCHECK(initialized_);
80 96
81 uint8 buffer[1024] = {0}; 97 uint8 buffer[1024] = {0};
82 int bytes = read(device_file_.GetPlatformFile(), buffer, 1024); 98 int bytes_read =
83 if (bytes < 0) { 99 HANDLE_EINTR(read(device_file_.GetPlatformFile(), buffer, 1024));
100 if (bytes_read < 0) {
84 if (errno == EAGAIN) { 101 if (errno == EAGAIN) {
85 return; 102 return;
86 } 103 }
87 Disconnect(); 104 Disconnect();
88 return; 105 return;
89 } 106 }
90 scoped_refptr<net::IOBuffer> io_buffer(new net::IOBuffer(bytes));
91 memcpy(io_buffer->data(), buffer, bytes);
92 input_reports_.push(std::make_pair(io_buffer, bytes));
93 107
108 PendingHidReport report;
109 report.buffer = new net::IOBufferWithSize(bytes_read);
110 memcpy(report.buffer->data(), buffer, bytes_read);
111 pending_reports_.push(report);
94 ProcessReadQueue(); 112 ProcessReadQueue();
95 } 113 }
96 114
97 void HidConnectionLinux::OnFileCanWriteWithoutBlocking(int fd) {} 115 void HidConnectionLinux::OnFileCanWriteWithoutBlocking(int fd) {}
98 116
99 void HidConnectionLinux::Disconnect() { 117 void HidConnectionLinux::Disconnect() {
100 DCHECK(thread_checker_.CalledOnValidThread()); 118 DCHECK(thread_checker_.CalledOnValidThread());
101 if (!initialized_)
102 return;
103
104 initialized_ = false;
105 device_file_watcher_.StopWatchingFileDescriptor(); 119 device_file_watcher_.StopWatchingFileDescriptor();
106 device_file_.Close(); 120 device_file_.Close();
107 while (!read_queue_.empty()) { 121 while (!pending_reads_.empty()) {
108 PendingRequest callback = read_queue_.front(); 122 PendingHidRead pending_read = pending_reads_.front();
109 read_queue_.pop(); 123 pending_reads_.pop();
110 callback.c.Run(false, 0); 124 pending_read.callback.Run(false, 0);
111 } 125 }
112 } 126 }
113 127
114 void HidConnectionLinux::Read(scoped_refptr<net::IOBuffer> buffer, 128 void HidConnectionLinux::Read(scoped_refptr<net::IOBufferWithSize> buffer,
115 size_t size,
116 const IOCallback& callback) { 129 const IOCallback& callback) {
117 DCHECK(thread_checker_.CalledOnValidThread()); 130 DCHECK(thread_checker_.CalledOnValidThread());
118 if (!initialized_) { 131 PendingHidRead pending_read;
119 DCHECK(read_queue_.empty()); 132 pending_read.buffer = buffer;
120 // There might be unread reports. 133 pending_read.callback = callback;
121 if (!input_reports_.empty()){ 134 pending_reads_.push(pending_read);
122 read_queue_.push(MakeTuple(buffer, size, callback)); 135 ProcessReadQueue();
123 ProcessReadQueue(); 136 }
124 } 137
138 void HidConnectionLinux::Write(uint8_t report_id,
139 scoped_refptr<net::IOBufferWithSize> buffer,
140 const IOCallback& callback) {
141 DCHECK(thread_checker_.CalledOnValidThread());
142 // If report ID is non-zero, insert it into a new copy of the buffer.
143 if (report_id != 0)
144 buffer = CopyBufferWithReportId(buffer, report_id);
145 int bytes_written = HANDLE_EINTR(
146 write(device_file_.GetPlatformFile(), buffer->data(), buffer->size()));
147 if (bytes_written < 0) {
148 Disconnect();
125 callback.Run(false, 0); 149 callback.Run(false, 0);
126 return;
127 } else { 150 } else {
128 read_queue_.push(MakeTuple(buffer, size, callback)); 151 callback.Run(true, bytes_written);
129 ProcessReadQueue();
130 } 152 }
131 } 153 }
132 154
133 void HidConnectionLinux::Write(scoped_refptr<net::IOBuffer> buffer, 155 void HidConnectionLinux::GetFeatureReport(
134 size_t size, 156 uint8_t report_id,
135 const IOCallback& callback) { 157 scoped_refptr<net::IOBufferWithSize> buffer,
158 const IOCallback& callback) {
136 DCHECK(thread_checker_.CalledOnValidThread()); 159 DCHECK(thread_checker_.CalledOnValidThread());
137 if (!initialized_) { 160
161 if (buffer->size() == 0) {
138 callback.Run(false, 0); 162 callback.Run(false, 0);
139 return; 163 return;
140 } else { 164 }
141 int bytes = write(device_file_.GetPlatformFile(), buffer->data(), size); 165
142 if (bytes < 0) { 166 // The first byte of the destination buffer is the report ID being requested.
143 Disconnect(); 167 buffer->data()[0] = report_id;
144 callback.Run(false, 0); 168 int result = ioctl(device_file_.GetPlatformFile(),
169 HIDIOCGFEATURE(buffer->size()),
170 buffer->data());
171 if (result < 0)
172 callback.Run(false, 0);
173 else
174 callback.Run(true, result);
175 }
176
177 void HidConnectionLinux::SendFeatureReport(
178 uint8_t report_id,
179 scoped_refptr<net::IOBufferWithSize> buffer,
180 const IOCallback& callback) {
181 DCHECK(thread_checker_.CalledOnValidThread());
182 if (report_id != 0)
183 buffer = CopyBufferWithReportId(buffer, report_id);
184 int result = ioctl(device_file_.GetPlatformFile(),
185 HIDIOCSFEATURE(buffer->size()),
186 buffer->data());
187 if (result < 0)
188 callback.Run(false, 0);
189 else
190 callback.Run(true, result);
191 }
192
193 void HidConnectionLinux::ProcessReadQueue() {
194 while (pending_reads_.size() && pending_reports_.size()) {
195 PendingHidRead read = pending_reads_.front();
196 pending_reads_.pop();
197 PendingHidReport report = pending_reports_.front();
198 if (report.buffer->size() > read.buffer->size()) {
199 read.callback.Run(false, report.buffer->size());
145 } else { 200 } else {
146 callback.Run(true, bytes); 201 memcpy(read.buffer->data(), report.buffer->data(), report.buffer->size());
202 pending_reports_.pop();
203 read.callback.Run(true, report.buffer->size());
147 } 204 }
148 } 205 }
149 } 206 }
150
151 void HidConnectionLinux::GetFeatureReport(scoped_refptr<net::IOBuffer> buffer,
152 size_t size,
153 const IOCallback& callback) {
154 DCHECK(thread_checker_.CalledOnValidThread());
155 if (!initialized_) {
156 callback.Run(false, 0);
157 return;
158 }
159 NOTIMPLEMENTED();
160 }
161
162 void HidConnectionLinux::SendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
163 size_t size,
164 const IOCallback& callback) {
165 DCHECK(thread_checker_.CalledOnValidThread());
166 if (!initialized_) {
167 callback.Run(false, 0);
168 return;
169 }
170 NOTIMPLEMENTED();
171 }
172
173 void HidConnectionLinux::ProcessReadQueue() {
174 while(read_queue_.size() && input_reports_.size()) {
175 PendingRequest request = read_queue_.front();
176 read_queue_.pop();
177 PendingReport report = input_reports_.front();
178 if (report.second > request.b) {
179 request.c.Run(false, report.second);
180 } else {
181 memcpy(request.a->data(), report.first->data(), report.second);
182 input_reports_.pop();
183 request.c.Run(true, report.second);
184 }
185 }
186 }
187 207
188 bool HidConnectionLinux::FindHidrawDevNode(udev_device* parent, 208 bool HidConnectionLinux::FindHidrawDevNode(udev_device* parent,
189 std::string* result) { 209 std::string* result) {
190 udev* udev = udev_device_get_udev(parent); 210 udev* udev = udev_device_get_udev(parent);
191 if (!udev) 211 if (!udev) {
192 return false; 212 return false;
193 213 }
194 ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev)); 214 ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev));
195 if (!enumerate) 215 if (!enumerate) {
196 return false; 216 return false;
197 217 }
198 if (udev_enumerate_add_match_subsystem(enumerate.get(), kHidrawSubsystem)) { 218 if (udev_enumerate_add_match_subsystem(enumerate.get(), kHidrawSubsystem)) {
199 return false; 219 return false;
200 } 220 }
201 if (udev_enumerate_scan_devices(enumerate.get())) { 221 if (udev_enumerate_scan_devices(enumerate.get())) {
202 return false; 222 return false;
203 } 223 }
204 224 std::string parent_path(udev_device_get_devpath(parent));
205 const char* parent_path = udev_device_get_devpath(parent); 225 if (parent_path.length() == 0 || *parent_path.rbegin() != '/')
226 parent_path += '/';
206 udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate.get()); 227 udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate.get());
207 for (udev_list_entry* i = devices; i != NULL; 228 for (udev_list_entry* i = devices; i != NULL;
208 i = udev_list_entry_get_next(i)) { 229 i = udev_list_entry_get_next(i)) {
209 ScopedUdevDevicePtr hid_dev( 230 ScopedUdevDevicePtr hid_dev(
210 udev_device_new_from_syspath(udev, udev_list_entry_get_name(i))); 231 udev_device_new_from_syspath(udev, udev_list_entry_get_name(i)));
211 const char* raw_path = udev_device_get_devnode(hid_dev.get()); 232 const char* raw_path = udev_device_get_devnode(hid_dev.get());
212 if (strncmp(parent_path, 233 std::string device_path = udev_device_get_devpath(hid_dev.get());
213 udev_device_get_devpath(hid_dev.get()), 234 if (raw_path &&
214 strlen(parent_path)) == 0 && 235 !device_path.compare(0, parent_path.length(), parent_path)) {
215 raw_path) {
216 *result = raw_path; 236 *result = raw_path;
217 return true; 237 return true;
218 } 238 }
219 } 239 }
220 240
221 return false; 241 return false;
222 } 242 }
223 243
224 } // namespace device 244 } // namespace device
OLDNEW
« no previous file with comments | « device/hid/hid_connection_linux.h ('k') | device/hid/hid_connection_mac.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698