| 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 <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 Loading... |
| 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 |
| OLD | NEW |