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

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

Issue 499713002: Don't pass buffers to HidConnection::Read because it knows the size. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 4 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
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>
(...skipping 11 matching lines...) Expand all
22 // These are already defined in newer versions of linux/hidraw.h. 22 // These are already defined in newer versions of linux/hidraw.h.
23 #ifndef HIDIOCSFEATURE 23 #ifndef HIDIOCSFEATURE
24 #define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE | _IOC_READ, 'H', 0x06, len) 24 #define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE | _IOC_READ, 'H', 0x06, len)
25 #endif 25 #endif
26 #ifndef HIDIOCGFEATURE 26 #ifndef HIDIOCGFEATURE
27 #define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE | _IOC_READ, 'H', 0x07, len) 27 #define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE | _IOC_READ, 'H', 0x07, len)
28 #endif 28 #endif
29 29
30 namespace device { 30 namespace device {
31 31
32 namespace {
33
34 // Copies a buffer into a new one with a report ID byte inserted at the front.
35 scoped_refptr<net::IOBufferWithSize> CopyBufferWithReportId(
36 scoped_refptr<net::IOBufferWithSize> buffer,
37 uint8_t report_id) {
38 scoped_refptr<net::IOBufferWithSize> new_buffer(
39 new net::IOBufferWithSize(buffer->size() + 1));
40 new_buffer->data()[0] = report_id;
41 memcpy(new_buffer->data() + 1, buffer->data(), buffer->size());
42 return new_buffer;
43 }
44
45 } // namespace
46
47 HidConnectionLinux::HidConnectionLinux(HidDeviceInfo device_info, 32 HidConnectionLinux::HidConnectionLinux(HidDeviceInfo device_info,
48 std::string dev_node) 33 std::string dev_node)
49 : HidConnection(device_info) { 34 : HidConnection(device_info) {
50 int flags = base::File::FLAG_OPEN | 35 int flags = base::File::FLAG_OPEN |
51 base::File::FLAG_READ | 36 base::File::FLAG_READ |
52 base::File::FLAG_WRITE; 37 base::File::FLAG_WRITE;
53 38
54 base::File device_file(base::FilePath(dev_node), flags); 39 base::File device_file(base::FilePath(dev_node), flags);
55 if (!device_file.IsValid()) { 40 if (!device_file.IsValid()) {
56 base::File::Error file_error = device_file.error_details(); 41 base::File::Error file_error = device_file.error_details();
(...skipping 27 matching lines...) Expand all
84 this)) { 69 this)) {
85 LOG(ERROR) << "Failed to start watching device file."; 70 LOG(ERROR) << "Failed to start watching device file.";
86 } 71 }
87 } 72 }
88 73
89 HidConnectionLinux::~HidConnectionLinux() { 74 HidConnectionLinux::~HidConnectionLinux() {
90 Disconnect(); 75 Disconnect();
91 Flush(); 76 Flush();
92 } 77 }
93 78
94 void HidConnectionLinux::PlatformRead( 79 void HidConnectionLinux::PlatformRead(const ReadCallback& callback) {
95 scoped_refptr<net::IOBufferWithSize> buffer,
96 const IOCallback& callback) {
97 PendingHidRead pending_read; 80 PendingHidRead pending_read;
98 pending_read.buffer = buffer;
99 pending_read.callback = callback; 81 pending_read.callback = callback;
100 pending_reads_.push(pending_read); 82 pending_reads_.push(pending_read);
101 ProcessReadQueue(); 83 ProcessReadQueue();
102 } 84 }
103 85
104 void HidConnectionLinux::PlatformWrite( 86 void HidConnectionLinux::PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
105 uint8_t report_id, 87 size_t size,
106 scoped_refptr<net::IOBufferWithSize> buffer, 88 const WriteCallback& callback) {
107 const IOCallback& callback) { 89 // Linux expects the first byte of the buffer to always be a report ID so the
108 // Linux always expects the first byte of the buffer to be the report ID. 90 // buffer can be used directly.
109 buffer = CopyBufferWithReportId(buffer, report_id); 91 const ssize_t bytes_written =
110 const int bytes_written = HANDLE_EINTR( 92 HANDLE_EINTR(write(device_file_.GetPlatformFile(), buffer->data(), size));
111 write(device_file_.GetPlatformFile(), buffer->data(), buffer->size()));
112 if (bytes_written < 0) { 93 if (bytes_written < 0) {
113 VPLOG(1) << "Write failed"; 94 VPLOG(1) << "Write failed";
114 Disconnect(); 95 Disconnect();
115 callback.Run(false, 0); 96 callback.Run(false);
116 } else { 97 } else {
117 if (bytes_written != buffer->size()) { 98 if (static_cast<size_t>(bytes_written) != size) {
118 LOG(WARNING) << "Incomplete HID write: " 99 LOG(WARNING) << "Incomplete HID write: " << bytes_written
119 << bytes_written << " != " << buffer->size(); 100 << " != " << size;
120 } 101 }
121 callback.Run(true, bytes_written == 0 ? 0 : bytes_written - 1); 102 callback.Run(true);
122 } 103 }
123 } 104 }
124 105
125 void HidConnectionLinux::PlatformGetFeatureReport( 106 void HidConnectionLinux::PlatformGetFeatureReport(
126 uint8_t report_id, 107 uint8_t report_id,
127 scoped_refptr<net::IOBufferWithSize> buffer, 108 const ReadCallback& callback) {
128 const IOCallback& callback) { 109 // The first byte of the destination buffer is the report ID being requested
129 if (buffer->size() == 0) { 110 // and is overwritten by the feature report.
130 callback.Run(false, 0); 111 scoped_refptr<net::IOBufferWithSize> buffer(
131 return; 112 new net::IOBufferWithSize(device_info().max_feature_report_size + 1));
Ken Rockot(use gerrit already) 2014/08/26 16:32:04 Just for sanity checking: This seems to imply that
Reilly Grant (use Gerrit) 2014/08/26 17:17:22 The ioctl will overwrite the first byte with the f
132 } 113 buffer->data()[0] = report_id;
133 114
134 // The first byte of the destination buffer is the report ID being requested.
135 buffer->data()[0] = report_id;
136 int result = ioctl(device_file_.GetPlatformFile(), 115 int result = ioctl(device_file_.GetPlatformFile(),
137 HIDIOCGFEATURE(buffer->size()), 116 HIDIOCGFEATURE(buffer->size()),
138 buffer->data()); 117 buffer->data());
139 if (result < 0) { 118 if (result < 0) {
140 VPLOG(1) << "Failed to get feature report"; 119 VPLOG(1) << "Failed to get feature report";
141 callback.Run(false, 0); 120 callback.Run(false, NULL, 0);
142 } else { 121 } else {
143 callback.Run(true, result); 122 callback.Run(true, buffer, result);
144 } 123 }
145 } 124 }
146 125
147 void HidConnectionLinux::PlatformSendFeatureReport( 126 void HidConnectionLinux::PlatformSendFeatureReport(
148 uint8_t report_id, 127 scoped_refptr<net::IOBuffer> buffer,
149 scoped_refptr<net::IOBufferWithSize> buffer, 128 size_t size,
150 const IOCallback& callback) { 129 const WriteCallback& callback) {
151 if (report_id != 0) 130 // Linux expects the first byte of the buffer to always be a report ID so the
152 buffer = CopyBufferWithReportId(buffer, report_id); 131 // buffer can be used directly.
153 int result = ioctl(device_file_.GetPlatformFile(), 132 int result = ioctl(
154 HIDIOCSFEATURE(buffer->size()), 133 device_file_.GetPlatformFile(), HIDIOCSFEATURE(size), buffer->data());
155 buffer->data());
156 if (result < 0) { 134 if (result < 0) {
157 VPLOG(1) << "Failed to send feature report"; 135 VPLOG(1) << "Failed to send feature report";
158 callback.Run(false, 0); 136 callback.Run(false);
159 } else { 137 } else {
160 callback.Run(true, result); 138 callback.Run(true);
161 } 139 }
162 } 140 }
163 141
164 void HidConnectionLinux::OnFileCanReadWithoutBlocking(int fd) { 142 void HidConnectionLinux::OnFileCanReadWithoutBlocking(int fd) {
165 DCHECK(thread_checker().CalledOnValidThread()); 143 DCHECK(thread_checker().CalledOnValidThread());
166 DCHECK_EQ(fd, device_file_.GetPlatformFile()); 144 DCHECK_EQ(fd, device_file_.GetPlatformFile());
167 145
168 uint8 raw_buffer[1024] = {0}; 146 size_t expected_report_size = device_info().max_input_report_size + 1;
Ken Rockot(use gerrit already) 2014/08/26 16:32:04 Same deal with the +1 here.
Reilly Grant (use Gerrit) 2014/08/26 17:17:22 Here I do because the read may or may not include
169 int bytes_read = 147 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(expected_report_size));
170 HANDLE_EINTR(read(device_file_.GetPlatformFile(), raw_buffer, 1024)); 148 char* data = buffer->data();
149 if (!device_info().has_report_id) {
150 // Linux will not prefix the buffer with a report ID if they are not used
151 // by the device.
152 data[0] = 0;
153 data++;
154 expected_report_size--;
155 }
156
157 ssize_t bytes_read = HANDLE_EINTR(
158 read(device_file_.GetPlatformFile(), data, expected_report_size));
171 if (bytes_read < 0) { 159 if (bytes_read < 0) {
172 if (errno == EAGAIN) { 160 if (errno == EAGAIN) {
173 return; 161 return;
174 } 162 }
175 VPLOG(1) << "Read failed"; 163 VPLOG(1) << "Read failed";
176 Disconnect(); 164 Disconnect();
177 return; 165 return;
178 } 166 }
167 if (!device_info().has_report_id) {
168 // Include the byte prepended earlier.
169 bytes_read++;
170 }
179 171
180 scoped_refptr<net::IOBufferWithSize> buffer = 172 ProcessInputReport(buffer, bytes_read);
181 new net::IOBufferWithSize(bytes_read);
182 memcpy(buffer->data(), raw_buffer, bytes_read);
183
184 ProcessInputReport(buffer);
185 } 173 }
186 174
187 void HidConnectionLinux::OnFileCanWriteWithoutBlocking(int fd) { 175 void HidConnectionLinux::OnFileCanWriteWithoutBlocking(int fd) {
188 } 176 }
189 177
190 void HidConnectionLinux::Disconnect() { 178 void HidConnectionLinux::Disconnect() {
191 DCHECK(thread_checker().CalledOnValidThread()); 179 DCHECK(thread_checker().CalledOnValidThread());
192 device_file_watcher_.StopWatchingFileDescriptor(); 180 device_file_watcher_.StopWatchingFileDescriptor();
193 device_file_.Close(); 181 device_file_.Close();
194 182
195 Flush(); 183 Flush();
196 } 184 }
197 185
198 void HidConnectionLinux::Flush() { 186 void HidConnectionLinux::Flush() {
199 while (!pending_reads_.empty()) { 187 while (!pending_reads_.empty()) {
200 pending_reads_.front().callback.Run(false, 0); 188 pending_reads_.front().callback.Run(false, NULL, 0);
201 pending_reads_.pop(); 189 pending_reads_.pop();
202 } 190 }
203 } 191 }
204 192
205 void HidConnectionLinux::ProcessInputReport( 193 void HidConnectionLinux::ProcessInputReport(scoped_refptr<net::IOBuffer> buffer,
206 scoped_refptr<net::IOBufferWithSize> buffer) { 194 size_t size) {
207 DCHECK(thread_checker().CalledOnValidThread()); 195 DCHECK(thread_checker().CalledOnValidThread());
208 PendingHidReport report; 196 PendingHidReport report;
209 report.buffer = buffer; 197 report.buffer = buffer;
198 report.size = size;
210 pending_reports_.push(report); 199 pending_reports_.push(report);
211 ProcessReadQueue(); 200 ProcessReadQueue();
212 } 201 }
213 202
214 void HidConnectionLinux::ProcessReadQueue() { 203 void HidConnectionLinux::ProcessReadQueue() {
215 DCHECK(thread_checker().CalledOnValidThread()); 204 DCHECK(thread_checker().CalledOnValidThread());
216 while (pending_reads_.size() && pending_reports_.size()) { 205 while (pending_reads_.size() && pending_reports_.size()) {
217 PendingHidRead read = pending_reads_.front(); 206 PendingHidRead read = pending_reads_.front();
218 PendingHidReport report = pending_reports_.front(); 207 PendingHidReport report = pending_reports_.front();
219 208
220 if (read.buffer->size() < report.buffer->size()) { 209 pending_reports_.pop();
221 read.callback.Run(false, 0); 210 if (CompleteRead(report.buffer, report.size, read.callback)) {
Ken Rockot(use gerrit already) 2014/08/26 16:32:04 Much nicer.
Reilly Grant (use Gerrit) 2014/08/26 17:17:22 Acknowledged.
222 pending_reads_.pop(); 211 pending_reads_.pop();
223 } else {
224 memcpy(read.buffer->data(), report.buffer->data(), report.buffer->size());
225 pending_reports_.pop();
226
227 if (CompleteRead(read.buffer, report.buffer->size(), read.callback)) {
228 pending_reads_.pop();
229 }
230 } 212 }
231 } 213 }
232 } 214 }
233 215
234 } // namespace device 216 } // namespace device
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698