Add common GLFence::IsSupported() check
[chromium-blink-merge.git] / device / hid / hid_connection_linux.cc
blob425d88fa35cd83ac0c29ddeb39b62f59727c314a
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
3 // found in the LICENSE file.
5 #include "device/hid/hid_connection_linux.h"
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <libudev.h>
10 #include <linux/hidraw.h>
11 #include <sys/ioctl.h>
13 #include <string>
15 #include "base/files/file_path.h"
16 #include "base/posix/eintr_wrapper.h"
17 #include "base/threading/thread_restrictions.h"
18 #include "base/tuple.h"
19 #include "device/hid/hid_service.h"
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
29 namespace device {
31 namespace {
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;
44 } // namespace
46 HidConnectionLinux::HidConnectionLinux(HidDeviceInfo device_info,
47 std::string dev_node)
48 : HidConnection(device_info) {
49 DCHECK(thread_checker_.CalledOnValidThread());
51 int flags = base::File::FLAG_OPEN |
52 base::File::FLAG_READ |
53 base::File::FLAG_WRITE;
55 base::File device_file(base::FilePath(dev_node), flags);
56 if (!device_file.IsValid()) {
57 base::File::Error file_error = device_file.error_details();
59 if (file_error == base::File::FILE_ERROR_ACCESS_DENIED) {
60 VLOG(1) << "Access denied opening device read-write, trying read-only.";
62 flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
64 device_file = base::File(base::FilePath(dev_node), flags);
67 if (!device_file.IsValid()) {
68 LOG(ERROR) << "Failed to open '" << dev_node << "': "
69 << base::File::ErrorToString(device_file.error_details());
70 return;
73 if (fcntl(device_file.GetPlatformFile(), F_SETFL,
74 fcntl(device_file.GetPlatformFile(), F_GETFL) | O_NONBLOCK)) {
75 PLOG(ERROR) << "Failed to set non-blocking flag to device file.";
76 return;
78 device_file_ = device_file.Pass();
80 if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
81 device_file_.GetPlatformFile(),
82 true,
83 base::MessageLoopForIO::WATCH_READ_WRITE,
84 &device_file_watcher_,
85 this)) {
86 LOG(ERROR) << "Failed to start watching device file.";
90 HidConnectionLinux::~HidConnectionLinux() {
91 DCHECK(thread_checker_.CalledOnValidThread());
92 Disconnect();
95 void HidConnectionLinux::OnFileCanReadWithoutBlocking(int fd) {
96 DCHECK(thread_checker_.CalledOnValidThread());
97 DCHECK_EQ(fd, device_file_.GetPlatformFile());
99 uint8 buffer[1024] = {0};
100 int bytes_read =
101 HANDLE_EINTR(read(device_file_.GetPlatformFile(), buffer, 1024));
102 if (bytes_read < 0) {
103 if (errno == EAGAIN) {
104 return;
106 Disconnect();
107 return;
110 PendingHidReport report;
111 report.buffer = new net::IOBufferWithSize(bytes_read);
112 memcpy(report.buffer->data(), buffer, bytes_read);
113 pending_reports_.push(report);
114 ProcessReadQueue();
117 void HidConnectionLinux::OnFileCanWriteWithoutBlocking(int fd) {}
119 void HidConnectionLinux::Disconnect() {
120 DCHECK(thread_checker_.CalledOnValidThread());
121 device_file_watcher_.StopWatchingFileDescriptor();
122 device_file_.Close();
123 while (!pending_reads_.empty()) {
124 PendingHidRead pending_read = pending_reads_.front();
125 pending_reads_.pop();
126 pending_read.callback.Run(false, 0);
130 void HidConnectionLinux::Read(scoped_refptr<net::IOBufferWithSize> buffer,
131 const IOCallback& callback) {
132 DCHECK(thread_checker_.CalledOnValidThread());
133 PendingHidRead pending_read;
134 pending_read.buffer = buffer;
135 pending_read.callback = callback;
136 pending_reads_.push(pending_read);
137 ProcessReadQueue();
140 void HidConnectionLinux::Write(uint8_t report_id,
141 scoped_refptr<net::IOBufferWithSize> buffer,
142 const IOCallback& callback) {
143 DCHECK(thread_checker_.CalledOnValidThread());
144 // If report ID is non-zero, insert it into a new copy of the buffer.
145 if (report_id != 0)
146 buffer = CopyBufferWithReportId(buffer, report_id);
147 int bytes_written = HANDLE_EINTR(
148 write(device_file_.GetPlatformFile(), buffer->data(), buffer->size()));
149 if (bytes_written < 0) {
150 Disconnect();
151 callback.Run(false, 0);
152 } else {
153 callback.Run(true, bytes_written);
157 void HidConnectionLinux::GetFeatureReport(
158 uint8_t report_id,
159 scoped_refptr<net::IOBufferWithSize> buffer,
160 const IOCallback& callback) {
161 DCHECK(thread_checker_.CalledOnValidThread());
163 if (buffer->size() == 0) {
164 callback.Run(false, 0);
165 return;
168 // The first byte of the destination buffer is the report ID being requested.
169 buffer->data()[0] = report_id;
170 int result = ioctl(device_file_.GetPlatformFile(),
171 HIDIOCGFEATURE(buffer->size()),
172 buffer->data());
173 if (result < 0)
174 callback.Run(false, 0);
175 else
176 callback.Run(true, result);
179 void HidConnectionLinux::SendFeatureReport(
180 uint8_t report_id,
181 scoped_refptr<net::IOBufferWithSize> buffer,
182 const IOCallback& callback) {
183 DCHECK(thread_checker_.CalledOnValidThread());
184 if (report_id != 0)
185 buffer = CopyBufferWithReportId(buffer, report_id);
186 int result = ioctl(device_file_.GetPlatformFile(),
187 HIDIOCSFEATURE(buffer->size()),
188 buffer->data());
189 if (result < 0)
190 callback.Run(false, 0);
191 else
192 callback.Run(true, result);
195 void HidConnectionLinux::ProcessReadQueue() {
196 while (pending_reads_.size() && pending_reports_.size()) {
197 PendingHidRead read = pending_reads_.front();
198 pending_reads_.pop();
199 PendingHidReport report = pending_reports_.front();
200 if (report.buffer->size() > read.buffer->size()) {
201 read.callback.Run(false, report.buffer->size());
202 } else {
203 memcpy(read.buffer->data(), report.buffer->data(), report.buffer->size());
204 pending_reports_.pop();
205 read.callback.Run(true, report.buffer->size());
210 } // namespace device