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

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: addressed comments Created 6 years, 10 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
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
13 #include "base/threading/thread_restrictions.h" 15 #include "base/threading/thread_restrictions.h"
14 #include "base/tuple.h" 16 #include "base/tuple.h"
15 #include "device/hid/hid_service.h" 17 #include "device/hid/hid_service.h"
16 #include "device/hid/hid_service_linux.h" 18 #include "device/hid/hid_service_linux.h"
17 19
20 // These are already defined in newer versions of linux/hidraw.h.
21 #ifndef HIDIOCSFEATURE
22 #define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE | _IOC_READ, 'H', 0x06, len)
23 #endif
24 #ifndef HIDIOCGFEATURE
25 #define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE | _IOC_READ, 'H', 0x07, len)
26 #endif
18 27
19 namespace device { 28 namespace device {
20 29
21 namespace { 30 namespace {
22 31
32 // Copies a buffer into a new one a report ID byte inserted at the front.
Mark Mentovai 2014/02/26 17:41:44 This comment is missing a word?
Ken Rockot(use gerrit already) 2014/02/26 22:36:39 Done.
33 scoped_refptr<net::IOBufferWithSize> CopyBufferWithReportId(
34 scoped_refptr<net::IOBufferWithSize> buffer,
35 uint8_t report_id) {
36 scoped_refptr<net::IOBufferWithSize> new_buffer(
37 new net::IOBufferWithSize(buffer->size() + 1));
38 new_buffer->data()[0] = report_id;
39 memcpy(new_buffer->data() + 1, buffer->data(), buffer->size());
40 return new_buffer;
41 }
42
23 const char kHidrawSubsystem[] = "hidraw"; 43 const char kHidrawSubsystem[] = "hidraw";
24 44
25 } // namespace 45 } // namespace
26 46
27 HidConnectionLinux::HidConnectionLinux(HidDeviceInfo device_info, 47 HidConnectionLinux::HidConnectionLinux(HidDeviceInfo device_info,
28 ScopedUdevDevicePtr udev_raw_device) 48 ScopedUdevDevicePtr udev_raw_device)
29 : HidConnection(device_info), 49 : HidConnection(device_info) {
30 initialized_(false) {
31 DCHECK(thread_checker_.CalledOnValidThread()); 50 DCHECK(thread_checker_.CalledOnValidThread());
32 51
33 udev_device* dev = udev_raw_device.get(); 52 udev_device* dev = udev_raw_device.get();
34 std::string dev_node; 53 std::string dev_node;
35 if (!FindHidrawDevNode(dev, &dev_node)) { 54 if (!FindHidrawDevNode(dev, &dev_node)) {
36 LOG(ERROR) << "Cannot open HID device as hidraw device."; 55 LOG(ERROR) << "Cannot open HID device as hidraw device.";
37 return; 56 return;
38 } 57 }
39 58
40 int flags = base::File::FLAG_OPEN | 59 int flags = base::File::FLAG_OPEN |
(...skipping 13 matching lines...) Expand all
54 return; 73 return;
55 } 74 }
56 device_file_ = device_file.Pass(); 75 device_file_ = device_file.Pass();
57 76
58 if (!base::MessageLoopForIO::current()->WatchFileDescriptor( 77 if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
59 device_file_.GetPlatformFile(), 78 device_file_.GetPlatformFile(),
60 true, 79 true,
61 base::MessageLoopForIO::WATCH_READ_WRITE, 80 base::MessageLoopForIO::WATCH_READ_WRITE,
62 &device_file_watcher_, 81 &device_file_watcher_,
63 this)) { 82 this)) {
64 LOG(ERROR) << "Cannot start watching file descriptor."; 83 LOG(ERROR) << "Failed to start watching device file.";
65 return;
66 } 84 }
67
68 initialized_ = true;
69 } 85 }
70 86
71 HidConnectionLinux::~HidConnectionLinux() { 87 HidConnectionLinux::~HidConnectionLinux() {
72 DCHECK(thread_checker_.CalledOnValidThread()); 88 DCHECK(thread_checker_.CalledOnValidThread());
73 Disconnect(); 89 Disconnect();
74 } 90 }
75 91
76 void HidConnectionLinux::OnFileCanReadWithoutBlocking(int fd) { 92 void HidConnectionLinux::OnFileCanReadWithoutBlocking(int fd) {
77 DCHECK(thread_checker_.CalledOnValidThread()); 93 DCHECK(thread_checker_.CalledOnValidThread());
78 DCHECK_EQ(fd, device_file_.GetPlatformFile()); 94 DCHECK_EQ(fd, device_file_.GetPlatformFile());
79 DCHECK(initialized_);
80 95
81 uint8 buffer[1024] = {0}; 96 uint8 buffer[1024] = {0};
82 int bytes = read(device_file_.GetPlatformFile(), buffer, 1024); 97 int bytes_read = read(device_file_.GetPlatformFile(), buffer, 1024);
83 if (bytes < 0) { 98 if (bytes_read < 0) {
84 if (errno == EAGAIN) { 99 if (errno == EAGAIN) {
85 return; 100 return;
86 } 101 }
87 Disconnect(); 102 Disconnect();
88 return; 103 return;
89 } 104 }
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 105
106 PendingHidReport report;
107 report.buffer = new net::IOBufferWithSize(bytes_read);
108 memcpy(report.buffer->data(), buffer, bytes_read);
109 pending_reports_.push(report);
94 ProcessReadQueue(); 110 ProcessReadQueue();
95 } 111 }
96 112
97 void HidConnectionLinux::OnFileCanWriteWithoutBlocking(int fd) {} 113 void HidConnectionLinux::OnFileCanWriteWithoutBlocking(int fd) {}
98 114
99 void HidConnectionLinux::Disconnect() { 115 void HidConnectionLinux::Disconnect() {
100 DCHECK(thread_checker_.CalledOnValidThread()); 116 DCHECK(thread_checker_.CalledOnValidThread());
101 if (!initialized_)
102 return;
103
104 initialized_ = false;
105 device_file_watcher_.StopWatchingFileDescriptor(); 117 device_file_watcher_.StopWatchingFileDescriptor();
106 device_file_.Close(); 118 device_file_.Close();
107 while (!read_queue_.empty()) { 119 while (!pending_reads_.empty()) {
108 PendingRequest callback = read_queue_.front(); 120 PendingHidRead pending_read = pending_reads_.front();
109 read_queue_.pop(); 121 pending_reads_.pop();
110 callback.c.Run(false, 0); 122 pending_read.callback.Run(false, 0);
111 } 123 }
112 } 124 }
113 125
114 void HidConnectionLinux::Read(scoped_refptr<net::IOBuffer> buffer, 126 void HidConnectionLinux::Read(scoped_refptr<net::IOBufferWithSize> buffer,
115 size_t size,
116 const IOCallback& callback) { 127 const IOCallback& callback) {
117 DCHECK(thread_checker_.CalledOnValidThread()); 128 DCHECK(thread_checker_.CalledOnValidThread());
118 if (!initialized_) { 129 PendingHidRead pending_read;
119 DCHECK(read_queue_.empty()); 130 pending_read.buffer = buffer;
120 // There might be unread reports. 131 pending_read.callback = callback;
121 if (!input_reports_.empty()){ 132 pending_reads_.push(pending_read);
122 read_queue_.push(MakeTuple(buffer, size, callback)); 133 ProcessReadQueue();
123 ProcessReadQueue(); 134 }
124 } 135
136 void HidConnectionLinux::Write(uint8_t report_id,
137 scoped_refptr<net::IOBufferWithSize> buffer,
138 const IOCallback& callback) {
139 DCHECK(thread_checker_.CalledOnValidThread());
140 // If report ID is non-zero, insert it into a new copy of the buffer.
141 if (report_id != 0)
142 buffer = CopyBufferWithReportId(buffer, report_id);
143 int bytes_written =
144 write(device_file_.GetPlatformFile(), buffer->data(), buffer->size());
Mark Mentovai 2014/02/26 17:41:44 Do you need to handle a short write or EINTR? Mayb
Ken Rockot(use gerrit already) 2014/02/26 22:36:39 I lean against treating short reads or writes as e
145 if (bytes_written < 0) {
146 Disconnect();
125 callback.Run(false, 0); 147 callback.Run(false, 0);
126 return;
127 } else { 148 } else {
128 read_queue_.push(MakeTuple(buffer, size, callback)); 149 callback.Run(true, bytes_written);
129 ProcessReadQueue();
130 } 150 }
131 } 151 }
132 152
133 void HidConnectionLinux::Write(scoped_refptr<net::IOBuffer> buffer, 153 void HidConnectionLinux::GetFeatureReport(
134 size_t size, 154 uint8_t report_id,
135 const IOCallback& callback) { 155 scoped_refptr<net::IOBufferWithSize> buffer,
156 const IOCallback& callback) {
136 DCHECK(thread_checker_.CalledOnValidThread()); 157 DCHECK(thread_checker_.CalledOnValidThread());
137 if (!initialized_) { 158
159 if (buffer->size() == 0) {
138 callback.Run(false, 0); 160 callback.Run(false, 0);
139 return; 161 return;
140 } else { 162 }
141 int bytes = write(device_file_.GetPlatformFile(), buffer->data(), size); 163
142 if (bytes < 0) { 164 // The first byte of the destination buffer is the report ID being requested.
143 Disconnect(); 165 buffer->data()[0] = report_id;
144 callback.Run(false, 0); 166 int result = ioctl(device_file_.GetPlatformFile(),
167 HIDIOCGFEATURE(buffer->size()),
168 buffer->data());
169 if (result < 0)
170 callback.Run(false, 0);
171 else
172 callback.Run(true, result);
173 }
174
175 void HidConnectionLinux::SendFeatureReport(
176 uint8_t report_id,
177 scoped_refptr<net::IOBufferWithSize> buffer,
178 const IOCallback& callback) {
179 DCHECK(thread_checker_.CalledOnValidThread());
180 if (report_id != 0)
181 buffer = CopyBufferWithReportId(buffer, report_id);
182 int result = ioctl(device_file_.GetPlatformFile(),
183 HIDIOCSFEATURE(buffer->size()),
184 buffer->data());
185 if (result < 0)
186 callback.Run(false, 0);
187 else
188 callback.Run(true, result);
189 }
190
191 void HidConnectionLinux::ProcessReadQueue() {
192 while (pending_reads_.size() && pending_reports_.size()) {
193 PendingHidRead read = pending_reads_.front();
194 pending_reads_.pop();
195 PendingHidReport report = pending_reports_.front();
196 if (report.buffer->size() > read.buffer->size()) {
197 read.callback.Run(false, report.buffer->size());
145 } else { 198 } else {
146 callback.Run(true, bytes); 199 memcpy(read.buffer->data(), report.buffer->data(), report.buffer->size());
200 pending_reports_.pop();
201 read.callback.Run(true, report.buffer->size());
147 } 202 }
148 } 203 }
149 } 204 }
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 205
188 bool HidConnectionLinux::FindHidrawDevNode(udev_device* parent, 206 bool HidConnectionLinux::FindHidrawDevNode(udev_device* parent,
189 std::string* result) { 207 std::string* result) {
190 udev* udev = udev_device_get_udev(parent); 208 udev* udev = udev_device_get_udev(parent);
191 if (!udev) 209 if (!udev)
192 return false; 210 return false;
193 211
194 ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev)); 212 ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev));
195 if (!enumerate) 213 if (!enumerate)
196 return false; 214 return false;
197 215
198 if (udev_enumerate_add_match_subsystem(enumerate.get(), kHidrawSubsystem)) { 216 if (udev_enumerate_add_match_subsystem(enumerate.get(), kHidrawSubsystem)) {
199 return false; 217 return false;
200 } 218 }
201 if (udev_enumerate_scan_devices(enumerate.get())) { 219 if (udev_enumerate_scan_devices(enumerate.get())) {
202 return false; 220 return false;
203 } 221 }
204 222
205 const char* parent_path = udev_device_get_devpath(parent); 223 const char* parent_path = udev_device_get_devpath(parent);
206 udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate.get()); 224 udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate.get());
207 for (udev_list_entry* i = devices; i != NULL; 225 for (udev_list_entry* i = devices; i != NULL;
208 i = udev_list_entry_get_next(i)) { 226 i = udev_list_entry_get_next(i)) {
209 ScopedUdevDevicePtr hid_dev( 227 ScopedUdevDevicePtr hid_dev(
210 udev_device_new_from_syspath(udev, udev_list_entry_get_name(i))); 228 udev_device_new_from_syspath(udev, udev_list_entry_get_name(i)));
211 const char* raw_path = udev_device_get_devnode(hid_dev.get()); 229 const char* raw_path = udev_device_get_devnode(hid_dev.get());
212 if (strncmp(parent_path, 230 if (strncmp(parent_path,
213 udev_device_get_devpath(hid_dev.get()), 231 udev_device_get_devpath(hid_dev.get()),
214 strlen(parent_path)) == 0 && 232 strlen(parent_path)) == 0 &&
Mark Mentovai 2014/02/26 17:41:44 Compute strlen(parent_path) outside this loop. Bu
Ken Rockot(use gerrit already) 2014/02/26 22:36:39 Done.
215 raw_path) { 233 raw_path) {
216 *result = raw_path; 234 *result = raw_path;
217 return true; 235 return true;
218 } 236 }
219 } 237 }
220 238
221 return false; 239 return false;
222 } 240 }
223 241
224 } // namespace device 242 } // namespace device
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698