| OLD | NEW |
| 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 <linux/hidraw.h> | 8 #include <linux/hidraw.h> |
| 9 #include <sys/ioctl.h> | 9 #include <sys/ioctl.h> |
| 10 | 10 |
| 11 #include <memory> | 11 #include <memory> |
| 12 #include <string> | 12 #include <string> |
| 13 #include <utility> | 13 #include <utility> |
| 14 | 14 |
| 15 #include "base/bind.h" | 15 #include "base/bind.h" |
| 16 #include "base/files/file_descriptor_watcher_posix.h" | 16 #include "base/files/file_descriptor_watcher_posix.h" |
| 17 #include "base/files/file_path.h" | |
| 18 #include "base/macros.h" | 17 #include "base/macros.h" |
| 19 #include "base/message_loop/message_loop.h" | 18 #include "base/memory/ptr_util.h" |
| 20 #include "base/posix/eintr_wrapper.h" | 19 #include "base/posix/eintr_wrapper.h" |
| 21 #include "base/threading/thread_restrictions.h" | 20 #include "base/threading/thread_restrictions.h" |
| 22 #include "base/threading/thread_task_runner_handle.h" | 21 #include "base/threading/thread_task_runner_handle.h" |
| 23 #include "components/device_event_log/device_event_log.h" | 22 #include "components/device_event_log/device_event_log.h" |
| 24 #include "device/hid/hid_service.h" | 23 #include "device/hid/hid_service.h" |
| 25 | 24 |
| 26 // These are already defined in newer versions of linux/hidraw.h. | 25 // These are already defined in newer versions of linux/hidraw.h. |
| 27 #ifndef HIDIOCSFEATURE | 26 #ifndef HIDIOCSFEATURE |
| 28 #define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE | _IOC_READ, 'H', 0x06, len) | 27 #define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE | _IOC_READ, 'H', 0x06, len) |
| 29 #endif | 28 #endif |
| 30 #ifndef HIDIOCGFEATURE | 29 #ifndef HIDIOCGFEATURE |
| 31 #define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE | _IOC_READ, 'H', 0x07, len) | 30 #define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE | _IOC_READ, 'H', 0x07, len) |
| 32 #endif | 31 #endif |
| 33 | 32 |
| 34 namespace device { | 33 namespace device { |
| 35 | 34 |
| 36 class HidConnectionLinux::FileThreadHelper | 35 class HidConnectionLinux::FileThreadHelper { |
| 37 : public base::MessageLoop::DestructionObserver { | |
| 38 public: | 36 public: |
| 39 FileThreadHelper(base::PlatformFile platform_file, | 37 FileThreadHelper(base::ScopedFD fd, |
| 40 scoped_refptr<HidDeviceInfo> device_info, | 38 scoped_refptr<HidDeviceInfo> device_info, |
| 41 base::WeakPtr<HidConnectionLinux> connection, | 39 base::WeakPtr<HidConnectionLinux> connection) |
| 42 scoped_refptr<base::SingleThreadTaskRunner> task_runner) | 40 : fd_(std::move(fd)), |
| 43 : platform_file_(platform_file), | |
| 44 connection_(connection), | 41 connection_(connection), |
| 45 task_runner_(task_runner) { | 42 origin_task_runner_(base::ThreadTaskRunnerHandle::Get()) { |
| 43 sequence_checker_.DetachFromSequence(); |
| 46 // Report buffers must always have room for the report ID. | 44 // Report buffers must always have room for the report ID. |
| 47 report_buffer_size_ = device_info->max_input_report_size() + 1; | 45 report_buffer_size_ = device_info->max_input_report_size() + 1; |
| 48 has_report_id_ = device_info->has_report_id(); | 46 has_report_id_ = device_info->has_report_id(); |
| 49 } | 47 } |
| 50 | 48 |
| 51 ~FileThreadHelper() override { | 49 ~FileThreadHelper() { DCHECK(sequence_checker_.CalledOnValidSequence()); } |
| 52 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 53 base::MessageLoop::current()->RemoveDestructionObserver(this); | |
| 54 } | |
| 55 | 50 |
| 56 // Starts the FileDescriptorWatcher that reads input events from the device. | 51 // Starts the FileDescriptorWatcher that reads input events from the device. |
| 57 // Must be called on a thread that has a base::MessageLoopForIO. | 52 // Must be called on a thread that has a base::MessageLoopForIO. |
| 58 static void Start(std::unique_ptr<FileThreadHelper> self) { | 53 void Start() { |
| 54 DCHECK(sequence_checker_.CalledOnValidSequence()); |
| 59 base::ThreadRestrictions::AssertIOAllowed(); | 55 base::ThreadRestrictions::AssertIOAllowed(); |
| 60 self->thread_checker_.DetachFromThread(); | |
| 61 | 56 |
| 62 self->file_watcher_ = base::FileDescriptorWatcher::WatchReadable( | 57 file_watcher_ = base::FileDescriptorWatcher::WatchReadable( |
| 63 self->platform_file_, | 58 fd_.get(), base::Bind(&FileThreadHelper::OnFileCanReadWithoutBlocking, |
| 64 base::Bind(&FileThreadHelper::OnFileCanReadWithoutBlocking, | 59 base::Unretained(this))); |
| 65 base::Unretained(self.get()))); | 60 } |
| 66 | 61 |
| 67 // |self| is now owned by the current message loop. | 62 void Write(scoped_refptr<net::IOBuffer> buffer, |
| 68 base::MessageLoop::current()->AddDestructionObserver(self.release()); | 63 size_t size, |
| 64 const WriteCallback& callback) { |
| 65 DCHECK(sequence_checker_.CalledOnValidSequence()); |
| 66 ssize_t result = HANDLE_EINTR(write(fd_.get(), buffer->data(), size)); |
| 67 if (result < 0) { |
| 68 HID_PLOG(EVENT) << "Write failed"; |
| 69 origin_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false)); |
| 70 } else { |
| 71 if (static_cast<size_t>(result) != size) |
| 72 HID_LOG(EVENT) << "Incomplete HID write: " << result << " != " << size; |
| 73 origin_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true)); |
| 74 } |
| 75 } |
| 76 |
| 77 void GetFeatureReport(uint8_t report_id, |
| 78 scoped_refptr<net::IOBufferWithSize> buffer, |
| 79 const ReadCallback& callback) { |
| 80 DCHECK(sequence_checker_.CalledOnValidSequence()); |
| 81 int result = HANDLE_EINTR( |
| 82 ioctl(fd_.get(), HIDIOCGFEATURE(buffer->size()), buffer->data())); |
| 83 if (result < 0) { |
| 84 HID_PLOG(EVENT) << "Failed to get feature report"; |
| 85 origin_task_runner_->PostTask(FROM_HERE, |
| 86 base::Bind(callback, false, nullptr, 0)); |
| 87 } else if (result == 0) { |
| 88 HID_LOG(EVENT) << "Get feature result too short."; |
| 89 origin_task_runner_->PostTask(FROM_HERE, |
| 90 base::Bind(callback, false, nullptr, 0)); |
| 91 } else if (report_id == 0) { |
| 92 // Linux adds a 0 to the beginning of the data received from the device. |
| 93 scoped_refptr<net::IOBuffer> copied_buffer(new net::IOBuffer(result - 1)); |
| 94 memcpy(copied_buffer->data(), buffer->data() + 1, result - 1); |
| 95 origin_task_runner_->PostTask( |
| 96 FROM_HERE, base::Bind(callback, true, copied_buffer, result - 1)); |
| 97 } else { |
| 98 origin_task_runner_->PostTask(FROM_HERE, |
| 99 base::Bind(callback, true, buffer, result)); |
| 100 } |
| 101 } |
| 102 |
| 103 void SendFeatureReport(scoped_refptr<net::IOBuffer> buffer, |
| 104 size_t size, |
| 105 const WriteCallback& callback) { |
| 106 DCHECK(sequence_checker_.CalledOnValidSequence()); |
| 107 int result = |
| 108 HANDLE_EINTR(ioctl(fd_.get(), HIDIOCSFEATURE(size), buffer->data())); |
| 109 if (result < 0) { |
| 110 HID_PLOG(EVENT) << "Failed to send feature report"; |
| 111 origin_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false)); |
| 112 } else { |
| 113 origin_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true)); |
| 114 } |
| 69 } | 115 } |
| 70 | 116 |
| 71 private: | 117 private: |
| 72 void OnFileCanReadWithoutBlocking() { | 118 void OnFileCanReadWithoutBlocking() { |
| 73 DCHECK(thread_checker_.CalledOnValidThread()); | 119 DCHECK(sequence_checker_.CalledOnValidSequence()); |
| 74 | 120 |
| 75 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(report_buffer_size_)); | 121 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(report_buffer_size_)); |
| 76 char* data = buffer->data(); | 122 char* data = buffer->data(); |
| 77 size_t length = report_buffer_size_; | 123 size_t length = report_buffer_size_; |
| 78 if (!has_report_id_) { | 124 if (!has_report_id_) { |
| 79 // Linux will not prefix the buffer with a report ID if report IDs are not | 125 // Linux will not prefix the buffer with a report ID if report IDs are not |
| 80 // used by the device. Prefix the buffer with 0. | 126 // used by the device. Prefix the buffer with 0. |
| 81 *data++ = 0; | 127 *data++ = 0; |
| 82 length--; | 128 length--; |
| 83 } | 129 } |
| 84 | 130 |
| 85 ssize_t bytes_read = HANDLE_EINTR(read(platform_file_, data, length)); | 131 ssize_t bytes_read = HANDLE_EINTR(read(fd_.get(), data, length)); |
| 86 if (bytes_read < 0) { | 132 if (bytes_read < 0) { |
| 87 if (errno != EAGAIN) { | 133 if (errno != EAGAIN) { |
| 88 HID_PLOG(EVENT) << "Read failed"; | 134 HID_PLOG(EVENT) << "Read failed"; |
| 89 // This assumes that the error is unrecoverable and disables reading | 135 // This assumes that the error is unrecoverable and disables reading |
| 90 // from the device until it has been re-opened. | 136 // from the device until it has been re-opened. |
| 91 // TODO(reillyg): Investigate starting and stopping the file descriptor | 137 // TODO(reillyg): Investigate starting and stopping the file descriptor |
| 92 // watcher in response to pending read requests so that per-request | 138 // watcher in response to pending read requests so that per-request |
| 93 // errors can be returned to the client. | 139 // errors can be returned to the client. |
| 94 file_watcher_.reset(); | 140 file_watcher_.reset(); |
| 95 } | 141 } |
| 96 return; | 142 return; |
| 97 } | 143 } |
| 98 if (!has_report_id_) { | 144 if (!has_report_id_) { |
| 99 // Behave as if the byte prefixed above as the the report ID was read. | 145 // Behave as if the byte prefixed above as the the report ID was read. |
| 100 bytes_read++; | 146 bytes_read++; |
| 101 } | 147 } |
| 102 | 148 |
| 103 task_runner_->PostTask(FROM_HERE, | 149 origin_task_runner_->PostTask( |
| 104 base::Bind(&HidConnectionLinux::ProcessInputReport, | 150 FROM_HERE, base::Bind(&HidConnectionLinux::ProcessInputReport, |
| 105 connection_, buffer, bytes_read)); | 151 connection_, buffer, bytes_read)); |
| 106 } | 152 } |
| 107 | 153 |
| 108 // base::MessageLoop::DestructionObserver: | 154 base::SequenceChecker sequence_checker_; |
| 109 void WillDestroyCurrentMessageLoop() override { | 155 base::ScopedFD fd_; |
| 110 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 111 delete this; | |
| 112 } | |
| 113 | |
| 114 base::ThreadChecker thread_checker_; | |
| 115 base::PlatformFile platform_file_; | |
| 116 size_t report_buffer_size_; | 156 size_t report_buffer_size_; |
| 117 bool has_report_id_; | 157 bool has_report_id_; |
| 118 base::WeakPtr<HidConnectionLinux> connection_; | 158 base::WeakPtr<HidConnectionLinux> connection_; |
| 119 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; | 159 const scoped_refptr<base::SequencedTaskRunner> origin_task_runner_; |
| 120 std::unique_ptr<base::FileDescriptorWatcher::Controller> file_watcher_; | 160 std::unique_ptr<base::FileDescriptorWatcher::Controller> file_watcher_; |
| 121 | 161 |
| 122 DISALLOW_COPY_AND_ASSIGN(FileThreadHelper); | 162 DISALLOW_COPY_AND_ASSIGN(FileThreadHelper); |
| 123 }; | 163 }; |
| 124 | 164 |
| 125 HidConnectionLinux::HidConnectionLinux( | 165 HidConnectionLinux::HidConnectionLinux( |
| 126 scoped_refptr<HidDeviceInfo> device_info, | 166 scoped_refptr<HidDeviceInfo> device_info, |
| 127 base::File device_file, | 167 base::ScopedFD fd, |
| 128 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner) | 168 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) |
| 129 : HidConnection(device_info), | 169 : HidConnection(device_info), |
| 130 file_task_runner_(file_task_runner), | 170 blocking_task_runner_(std::move(blocking_task_runner)), |
| 131 weak_factory_(this) { | 171 weak_factory_(this) { |
| 132 task_runner_ = base::ThreadTaskRunnerHandle::Get(); | 172 helper_ = base::MakeUnique<FileThreadHelper>(std::move(fd), device_info, |
| 133 device_file_ = std::move(device_file); | 173 weak_factory_.GetWeakPtr()); |
| 134 | 174 blocking_task_runner_->PostTask( |
| 135 // The helper is passed a weak pointer to this connection so that it can be | 175 FROM_HERE, |
| 136 // cleaned up after the connection is closed. | 176 base::Bind(&FileThreadHelper::Start, base::Unretained(helper_.get()))); |
| 137 std::unique_ptr<FileThreadHelper> helper( | |
| 138 new FileThreadHelper(device_file_.GetPlatformFile(), device_info, | |
| 139 weak_factory_.GetWeakPtr(), task_runner_)); | |
| 140 helper_ = helper.get(); | |
| 141 file_task_runner_->PostTask( | |
| 142 FROM_HERE, base::Bind(&FileThreadHelper::Start, base::Passed(&helper))); | |
| 143 } | 177 } |
| 144 | 178 |
| 145 HidConnectionLinux::~HidConnectionLinux() { | 179 HidConnectionLinux::~HidConnectionLinux() { |
| 146 DCHECK(helper_ == nullptr); | 180 DCHECK(sequence_checker_.CalledOnValidSequence()); |
| 147 } | 181 } |
| 148 | 182 |
| 149 void HidConnectionLinux::PlatformClose() { | 183 void HidConnectionLinux::PlatformClose() { |
| 150 // By closing the device file on the FILE thread (1) the requirement that | 184 // By closing the device on the blocking task runner 1) the requirement that |
| 151 // base::File::Close is called on a thread where I/O is allowed is satisfied | 185 // base::ScopedFD is destroyed on a thread where I/O is allowed is satisfied |
| 152 // and (2) any tasks posted to this task runner that refer to this file will | 186 // and 2) any tasks posted to this task runner that refer to this file will |
| 153 // complete before it is closed. | 187 // complete before it is closed. |
| 154 file_task_runner_->DeleteSoon(FROM_HERE, helper_); | 188 blocking_task_runner_->DeleteSoon(FROM_HERE, helper_.release()); |
| 155 helper_ = nullptr; | |
| 156 file_task_runner_->PostTask(FROM_HERE, | |
| 157 base::Bind(&HidConnectionLinux::CloseDevice, | |
| 158 base::Passed(&device_file_))); | |
| 159 | 189 |
| 160 while (!pending_reads_.empty()) { | 190 while (!pending_reads_.empty()) { |
| 161 pending_reads_.front().callback.Run(false, NULL, 0); | 191 pending_reads_.front().callback.Run(false, NULL, 0); |
| 162 pending_reads_.pop(); | 192 pending_reads_.pop(); |
| 163 } | 193 } |
| 164 } | 194 } |
| 165 | 195 |
| 166 void HidConnectionLinux::PlatformRead(const ReadCallback& callback) { | 196 void HidConnectionLinux::PlatformRead(const ReadCallback& callback) { |
| 167 PendingHidRead pending_read; | 197 PendingHidRead pending_read; |
| 168 pending_read.callback = callback; | 198 pending_read.callback = callback; |
| 169 pending_reads_.push(pending_read); | 199 pending_reads_.push(pending_read); |
| 170 ProcessReadQueue(); | 200 ProcessReadQueue(); |
| 171 } | 201 } |
| 172 | 202 |
| 173 void HidConnectionLinux::PlatformWrite(scoped_refptr<net::IOBuffer> buffer, | 203 void HidConnectionLinux::PlatformWrite(scoped_refptr<net::IOBuffer> buffer, |
| 174 size_t size, | 204 size_t size, |
| 175 const WriteCallback& callback) { | 205 const WriteCallback& callback) { |
| 176 // Linux expects the first byte of the buffer to always be a report ID so the | 206 // Linux expects the first byte of the buffer to always be a report ID so the |
| 177 // buffer can be used directly. | 207 // buffer can be used directly. |
| 178 file_task_runner_->PostTask( | 208 blocking_task_runner_->PostTask( |
| 179 FROM_HERE, | 209 FROM_HERE, |
| 180 base::Bind(&HidConnectionLinux::BlockingWrite, | 210 base::Bind(&FileThreadHelper::Write, base::Unretained(helper_.get()), |
| 181 device_file_.GetPlatformFile(), buffer, size, | 211 buffer, size, callback)); |
| 182 base::Bind(&HidConnectionLinux::FinishWrite, | |
| 183 weak_factory_.GetWeakPtr(), size, callback), | |
| 184 task_runner_)); | |
| 185 } | 212 } |
| 186 | 213 |
| 187 void HidConnectionLinux::PlatformGetFeatureReport( | 214 void HidConnectionLinux::PlatformGetFeatureReport( |
| 188 uint8_t report_id, | 215 uint8_t report_id, |
| 189 const ReadCallback& callback) { | 216 const ReadCallback& callback) { |
| 190 // The first byte of the destination buffer is the report ID being requested | 217 // The first byte of the destination buffer is the report ID being requested |
| 191 // and is overwritten by the feature report. | 218 // and is overwritten by the feature report. |
| 192 DCHECK_GT(device_info()->max_feature_report_size(), 0u); | 219 DCHECK_GT(device_info()->max_feature_report_size(), 0u); |
| 193 scoped_refptr<net::IOBufferWithSize> buffer( | 220 scoped_refptr<net::IOBufferWithSize> buffer( |
| 194 new net::IOBufferWithSize(device_info()->max_feature_report_size() + 1)); | 221 new net::IOBufferWithSize(device_info()->max_feature_report_size() + 1)); |
| 195 buffer->data()[0] = report_id; | 222 buffer->data()[0] = report_id; |
| 196 | 223 |
| 197 file_task_runner_->PostTask( | 224 blocking_task_runner_->PostTask( |
| 198 FROM_HERE, | 225 FROM_HERE, |
| 199 base::Bind( | 226 base::Bind(&FileThreadHelper::GetFeatureReport, |
| 200 &HidConnectionLinux::BlockingIoctl, device_file_.GetPlatformFile(), | 227 base::Unretained(helper_.get()), report_id, buffer, callback)); |
| 201 HIDIOCGFEATURE(buffer->size()), buffer, | |
| 202 base::Bind(&HidConnectionLinux::FinishGetFeatureReport, | |
| 203 weak_factory_.GetWeakPtr(), report_id, buffer, callback), | |
| 204 task_runner_)); | |
| 205 } | 228 } |
| 206 | 229 |
| 207 void HidConnectionLinux::PlatformSendFeatureReport( | 230 void HidConnectionLinux::PlatformSendFeatureReport( |
| 208 scoped_refptr<net::IOBuffer> buffer, | 231 scoped_refptr<net::IOBuffer> buffer, |
| 209 size_t size, | 232 size_t size, |
| 210 const WriteCallback& callback) { | 233 const WriteCallback& callback) { |
| 211 // Linux expects the first byte of the buffer to always be a report ID so the | 234 // Linux expects the first byte of the buffer to always be a report ID so the |
| 212 // buffer can be used directly. | 235 // buffer can be used directly. |
| 213 file_task_runner_->PostTask( | 236 blocking_task_runner_->PostTask( |
| 214 FROM_HERE, | 237 FROM_HERE, |
| 215 base::Bind(&HidConnectionLinux::BlockingIoctl, | 238 base::Bind(&FileThreadHelper::SendFeatureReport, |
| 216 device_file_.GetPlatformFile(), HIDIOCSFEATURE(size), buffer, | 239 base::Unretained(helper_.get()), buffer, size, callback)); |
| 217 base::Bind(&HidConnectionLinux::FinishSendFeatureReport, | |
| 218 weak_factory_.GetWeakPtr(), callback), | |
| 219 task_runner_)); | |
| 220 } | |
| 221 | |
| 222 void HidConnectionLinux::FinishWrite(size_t expected_size, | |
| 223 const WriteCallback& callback, | |
| 224 ssize_t result) { | |
| 225 if (result < 0) { | |
| 226 HID_PLOG(EVENT) << "Write failed"; | |
| 227 callback.Run(false); | |
| 228 } else { | |
| 229 if (static_cast<size_t>(result) != expected_size) { | |
| 230 HID_LOG(EVENT) << "Incomplete HID write: " << result | |
| 231 << " != " << expected_size; | |
| 232 } | |
| 233 callback.Run(true); | |
| 234 } | |
| 235 } | |
| 236 | |
| 237 void HidConnectionLinux::FinishGetFeatureReport( | |
| 238 uint8_t report_id, | |
| 239 scoped_refptr<net::IOBuffer> buffer, | |
| 240 const ReadCallback& callback, | |
| 241 int result) { | |
| 242 if (result < 0) { | |
| 243 HID_PLOG(EVENT) << "Failed to get feature report"; | |
| 244 callback.Run(false, NULL, 0); | |
| 245 } else if (result == 0) { | |
| 246 HID_LOG(EVENT) << "Get feature result too short."; | |
| 247 callback.Run(false, NULL, 0); | |
| 248 } else if (report_id == 0) { | |
| 249 // Linux adds a 0 to the beginning of the data received from the device. | |
| 250 scoped_refptr<net::IOBuffer> copied_buffer(new net::IOBuffer(result - 1)); | |
| 251 memcpy(copied_buffer->data(), buffer->data() + 1, result - 1); | |
| 252 callback.Run(true, copied_buffer, result - 1); | |
| 253 } else { | |
| 254 callback.Run(true, buffer, result); | |
| 255 } | |
| 256 } | |
| 257 | |
| 258 void HidConnectionLinux::FinishSendFeatureReport(const WriteCallback& callback, | |
| 259 int result) { | |
| 260 if (result < 0) { | |
| 261 HID_PLOG(EVENT) << "Failed to send feature report"; | |
| 262 callback.Run(false); | |
| 263 } else { | |
| 264 callback.Run(true); | |
| 265 } | |
| 266 } | |
| 267 | |
| 268 // static | |
| 269 void HidConnectionLinux::BlockingWrite( | |
| 270 base::PlatformFile platform_file, | |
| 271 scoped_refptr<net::IOBuffer> buffer, | |
| 272 size_t size, | |
| 273 const InternalWriteCallback& callback, | |
| 274 scoped_refptr<base::SingleThreadTaskRunner> task_runner) { | |
| 275 base::ThreadRestrictions::AssertIOAllowed(); | |
| 276 ssize_t result = HANDLE_EINTR(write(platform_file, buffer->data(), size)); | |
| 277 task_runner->PostTask(FROM_HERE, base::Bind(callback, result)); | |
| 278 } | |
| 279 | |
| 280 // static | |
| 281 void HidConnectionLinux::BlockingIoctl( | |
| 282 base::PlatformFile platform_file, | |
| 283 int request, | |
| 284 scoped_refptr<net::IOBuffer> buffer, | |
| 285 const IoctlCallback& callback, | |
| 286 scoped_refptr<base::SingleThreadTaskRunner> task_runner) { | |
| 287 base::ThreadRestrictions::AssertIOAllowed(); | |
| 288 int result = ioctl(platform_file, request, buffer->data()); | |
| 289 task_runner->PostTask(FROM_HERE, base::Bind(callback, result)); | |
| 290 } | |
| 291 | |
| 292 // static | |
| 293 void HidConnectionLinux::CloseDevice(base::File device_file) { | |
| 294 device_file.Close(); | |
| 295 } | 240 } |
| 296 | 241 |
| 297 void HidConnectionLinux::ProcessInputReport(scoped_refptr<net::IOBuffer> buffer, | 242 void HidConnectionLinux::ProcessInputReport(scoped_refptr<net::IOBuffer> buffer, |
| 298 size_t size) { | 243 size_t size) { |
| 299 DCHECK(thread_checker().CalledOnValidThread()); | 244 DCHECK(thread_checker().CalledOnValidThread()); |
| 300 PendingHidReport report; | 245 PendingHidReport report; |
| 301 report.buffer = buffer; | 246 report.buffer = buffer; |
| 302 report.size = size; | 247 report.size = size; |
| 303 pending_reports_.push(report); | 248 pending_reports_.push(report); |
| 304 ProcessReadQueue(); | 249 ProcessReadQueue(); |
| 305 } | 250 } |
| 306 | 251 |
| 307 void HidConnectionLinux::ProcessReadQueue() { | 252 void HidConnectionLinux::ProcessReadQueue() { |
| 308 DCHECK(thread_checker().CalledOnValidThread()); | 253 DCHECK(thread_checker().CalledOnValidThread()); |
| 309 while (pending_reads_.size() && pending_reports_.size()) { | 254 while (pending_reads_.size() && pending_reports_.size()) { |
| 310 PendingHidRead read = pending_reads_.front(); | 255 PendingHidRead read = pending_reads_.front(); |
| 311 PendingHidReport report = pending_reports_.front(); | 256 PendingHidReport report = pending_reports_.front(); |
| 312 | 257 |
| 313 pending_reports_.pop(); | 258 pending_reports_.pop(); |
| 314 if (CompleteRead(report.buffer, report.size, read.callback)) { | 259 if (CompleteRead(report.buffer, report.size, read.callback)) |
| 315 pending_reads_.pop(); | 260 pending_reads_.pop(); |
| 316 } | |
| 317 } | 261 } |
| 318 } | 262 } |
| 319 | 263 |
| 320 } // namespace device | 264 } // namespace device |
| OLD | NEW |