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

Unified Diff: device/hid/hid_connection_linux.cc

Issue 771393002: Migrate HidServiceLinux and HidConnectionLinux to BrowserThread::UI. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix HID receive buffer size. Created 6 years 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 side-by-side diff with in-line comments
Download patch
Index: device/hid/hid_connection_linux.cc
diff --git a/device/hid/hid_connection_linux.cc b/device/hid/hid_connection_linux.cc
index bec4de7d26d6499aa18c137cdc3cd76c3384d269..27d4e38786eb2b88fd1cfdfa536e4b817a1938cc 100644
--- a/device/hid/hid_connection_linux.cc
+++ b/device/hid/hid_connection_linux.cc
@@ -5,15 +5,18 @@
#include "device/hid/hid_connection_linux.h"
#include <errno.h>
-#include <fcntl.h>
#include <linux/hidraw.h>
#include <sys/ioctl.h>
#include <string>
+#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_pump_libevent.h"
#include "base/posix/eintr_wrapper.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/threading/non_thread_safe.h"
#include "base/threading/thread_restrictions.h"
#include "base/tuple.h"
#include "device/hid/hid_service.h"
@@ -28,54 +31,110 @@
namespace device {
-HidConnectionLinux::HidConnectionLinux(HidDeviceInfo device_info,
- std::string dev_node)
- : HidConnection(device_info) {
- int flags = base::File::FLAG_OPEN |
- base::File::FLAG_READ |
- base::File::FLAG_WRITE;
-
- base::File device_file(base::FilePath(dev_node), flags);
- if (!device_file.IsValid()) {
- base::File::Error file_error = device_file.error_details();
+class HidConnectionLinux::Helper : public base::MessagePumpLibevent::Watcher,
+ public base::NonThreadSafe {
Ken Rockot(use gerrit already) 2014/12/03 22:35:09 nit: I think you can compose a base::ThreadChecker
Reilly Grant (use Gerrit) 2014/12/03 23:10:28 Done.
+ public:
+ Helper(base::PlatformFile platform_file,
+ const HidDeviceInfo& device_info,
+ base::WeakPtr<HidConnectionLinux> connection,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+ : platform_file_(platform_file),
+ connection_(connection),
+ task_runner_(task_runner) {
+ if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
+ platform_file_, true, base::MessageLoopForIO::WATCH_READ,
+ &file_watcher_, this)) {
+ LOG(ERROR) << "Failed to start watching device file.";
+ }
- if (file_error == base::File::FILE_ERROR_ACCESS_DENIED) {
- VLOG(1) << "Access denied opening device read-write, trying read-only.";
+ // Report buffers must always have room for the report ID.
+ report_buffer_size_ = device_info.max_input_report_size + 1;
+ has_report_id_ = device_info.has_report_id;
+ }
- flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
+ virtual ~Helper() {}
+
+ private:
+ // base::MessagePumpLibevent::Watcher implementation.
+ void OnFileCanReadWithoutBlocking(int fd) override {
+ DCHECK(CalledOnValidThread());
+ DCHECK_EQ(fd, platform_file_);
+
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(report_buffer_size_));
+ char* data = buffer->data();
+ size_t length = report_buffer_size_;
+ if (!has_report_id_) {
+ // Linux will not prefix the buffer with a report ID if report IDs are not
+ // used by the device. Prefix the buffer with 0.
+ *data++ = 0;
+ length--;
+ }
- device_file = base::File(base::FilePath(dev_node), flags);
+ ssize_t bytes_read = HANDLE_EINTR(read(platform_file_, data, length));
+ if (bytes_read < 0) {
+ if (errno == EAGAIN) {
+ return;
+ }
+ VPLOG(1) << "Read failed";
+ return;
}
- }
- if (!device_file.IsValid()) {
- LOG(ERROR) << "Failed to open '" << dev_node << "': "
- << base::File::ErrorToString(device_file.error_details());
- return;
+ if (!has_report_id_) {
+ // Behave as if the byte prefixed above as the the report ID was read.
+ bytes_read++;
+ }
+
+ task_runner_->PostTask(FROM_HERE,
+ base::Bind(&HidConnectionLinux::ProcessInputReport,
+ connection_, buffer, bytes_read));
}
- if (fcntl(device_file.GetPlatformFile(), F_SETFL,
- fcntl(device_file.GetPlatformFile(), F_GETFL) | O_NONBLOCK)) {
- PLOG(ERROR) << "Failed to set non-blocking flag to device file";
- return;
+ void OnFileCanWriteWithoutBlocking(int fd) override {
+ NOTREACHED(); // Only listening for reads.
}
+
+ base::PlatformFile platform_file_;
+ size_t report_buffer_size_;
+ bool has_report_id_;
+ base::WeakPtr<HidConnectionLinux> connection_;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ base::MessagePumpLibevent::FileDescriptorWatcher file_watcher_;
+};
+
+HidConnectionLinux::HidConnectionLinux(
+ HidDeviceInfo device_info,
+ base::File device_file,
+ scoped_refptr<base::SingleThreadTaskRunner> file_task_runner)
+ : HidConnection(device_info),
+ file_task_runner_(file_task_runner),
+ weak_factory_(this) {
+ task_runner_ = base::ThreadTaskRunnerHandle::Get();
device_file_ = device_file.Pass();
- if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
- device_file_.GetPlatformFile(),
- true,
- base::MessageLoopForIO::WATCH_READ_WRITE,
- &device_file_watcher_,
- this)) {
- LOG(ERROR) << "Failed to start watching device file.";
- }
+ // The helper is passed a weak pointer to this connection so that it can be
+ // cleaned up after the connection is closed. The weak pointer must be
+ // constructed on this thread.
+ file_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&HidConnectionLinux::StartHelper, this,
+ weak_factory_.GetWeakPtr()));
}
HidConnectionLinux::~HidConnectionLinux() {
}
void HidConnectionLinux::PlatformClose() {
- Disconnect();
- Flush();
+ // By closing the device file on the FILE thread (1) the requirement that
+ // base::File::Close is called on a thread where I/O is allowed is satisfied
+ // and (2) any tasks posted to this task runner that refer to this file will
+ // complete before it is closed.
+ file_task_runner_->DeleteSoon(FROM_HERE, helper_.release());
+ file_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&HidConnectionLinux::CloseDevice,
+ base::Passed(&device_file_)));
+
+ while (!pending_reads_.empty()) {
+ pending_reads_.front().callback.Run(false, NULL, 0);
+ pending_reads_.pop();
+ }
}
void HidConnectionLinux::PlatformRead(const ReadCallback& callback) {
@@ -90,19 +149,13 @@ void HidConnectionLinux::PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
const WriteCallback& callback) {
// Linux expects the first byte of the buffer to always be a report ID so the
// buffer can be used directly.
- const ssize_t bytes_written =
- HANDLE_EINTR(write(device_file_.GetPlatformFile(), buffer->data(), size));
- if (bytes_written < 0) {
- VPLOG(1) << "Write failed";
- Disconnect();
- callback.Run(false);
- } else {
- if (static_cast<size_t>(bytes_written) != size) {
- LOG(WARNING) << "Incomplete HID write: " << bytes_written
- << " != " << size;
- }
- callback.Run(true);
- }
+ file_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&HidConnectionLinux::BlockingWrite,
+ device_file_.GetPlatformFile(), buffer, size,
+ base::Bind(&HidConnectionLinux::FinishWrite,
+ weak_factory_.GetWeakPtr(), size, callback),
+ task_runner_));
}
void HidConnectionLinux::PlatformGetFeatureReport(
@@ -115,9 +168,51 @@ void HidConnectionLinux::PlatformGetFeatureReport(
new net::IOBufferWithSize(device_info().max_feature_report_size + 1));
buffer->data()[0] = report_id;
- int result = ioctl(device_file_.GetPlatformFile(),
- HIDIOCGFEATURE(buffer->size()),
- buffer->data());
+ file_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &HidConnectionLinux::BlockingIoctl, device_file_.GetPlatformFile(),
+ HIDIOCGFEATURE(buffer->size()), buffer,
+ base::Bind(&HidConnectionLinux::FinishGetFeatureReport,
+ weak_factory_.GetWeakPtr(), report_id, buffer, callback),
+ task_runner_));
+}
+
+void HidConnectionLinux::PlatformSendFeatureReport(
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const WriteCallback& callback) {
+ // Linux expects the first byte of the buffer to always be a report ID so the
+ // buffer can be used directly.
+ file_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&HidConnectionLinux::BlockingIoctl,
+ device_file_.GetPlatformFile(), HIDIOCSFEATURE(size), buffer,
+ base::Bind(&HidConnectionLinux::FinishSendFeatureReport,
+ weak_factory_.GetWeakPtr(), callback),
+ task_runner_));
+}
+
+void HidConnectionLinux::FinishWrite(size_t expected_size,
+ const WriteCallback& callback,
+ ssize_t result) {
+ if (result < 0) {
+ VPLOG(1) << "Write failed";
+ callback.Run(false);
+ } else {
+ if (static_cast<size_t>(result) != expected_size) {
+ LOG(WARNING) << "Incomplete HID write: " << result
+ << " != " << expected_size;
+ }
+ callback.Run(true);
+ }
+}
+
+void HidConnectionLinux::FinishGetFeatureReport(
+ uint8_t report_id,
+ scoped_refptr<net::IOBuffer> buffer,
+ const ReadCallback& callback,
+ int result) {
if (result < 0) {
VPLOG(1) << "Failed to get feature report";
callback.Run(false, NULL, 0);
@@ -134,14 +229,8 @@ void HidConnectionLinux::PlatformGetFeatureReport(
}
}
-void HidConnectionLinux::PlatformSendFeatureReport(
- scoped_refptr<net::IOBuffer> buffer,
- size_t size,
- const WriteCallback& callback) {
- // Linux expects the first byte of the buffer to always be a report ID so the
- // buffer can be used directly.
- int result = ioctl(
- device_file_.GetPlatformFile(), HIDIOCSFEATURE(size), buffer->data());
+void HidConnectionLinux::FinishSendFeatureReport(const WriteCallback& callback,
+ int result) {
if (result < 0) {
VPLOG(1) << "Failed to send feature report";
callback.Run(false);
@@ -150,55 +239,41 @@ void HidConnectionLinux::PlatformSendFeatureReport(
}
}
-void HidConnectionLinux::OnFileCanReadWithoutBlocking(int fd) {
- DCHECK(thread_checker().CalledOnValidThread());
- DCHECK_EQ(fd, device_file_.GetPlatformFile());
-
- size_t expected_report_size = device_info().max_input_report_size + 1;
- scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(expected_report_size));
- char* data = buffer->data();
- if (!device_info().has_report_id) {
- // Linux will not prefix the buffer with a report ID if they are not used
- // by the device.
- data[0] = 0;
- data++;
- expected_report_size--;
- }
-
- ssize_t bytes_read = HANDLE_EINTR(
- read(device_file_.GetPlatformFile(), data, expected_report_size));
- if (bytes_read < 0) {
- if (errno == EAGAIN) {
- return;
- }
- VPLOG(1) << "Read failed";
- Disconnect();
- return;
- }
- if (!device_info().has_report_id) {
- // Include the byte prepended earlier.
- bytes_read++;
- }
+void HidConnectionLinux::StartHelper(
+ base::WeakPtr<HidConnectionLinux> weak_ptr) {
+ base::ThreadRestrictions::AssertIOAllowed();
- ProcessInputReport(buffer, bytes_read);
+ helper_.reset(new Helper(device_file_.GetPlatformFile(), device_info(),
+ weak_ptr, task_runner_));
}
-void HidConnectionLinux::OnFileCanWriteWithoutBlocking(int fd) {
+// static
+void HidConnectionLinux::BlockingWrite(
+ base::PlatformFile platform_file,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const InternalWriteCallback& callback,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ ssize_t result = HANDLE_EINTR(write(platform_file, buffer->data(), size));
+ task_runner->PostTask(FROM_HERE, base::Bind(callback, result));
}
-void HidConnectionLinux::Disconnect() {
- DCHECK(thread_checker().CalledOnValidThread());
- device_file_watcher_.StopWatchingFileDescriptor();
- device_file_.Close();
-
- Flush();
+// static
+void HidConnectionLinux::BlockingIoctl(
+ base::PlatformFile platform_file,
+ int request,
+ scoped_refptr<net::IOBuffer> buffer,
+ const IoctlCallback& callback,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ int result = ioctl(platform_file, request, buffer->data());
+ task_runner->PostTask(FROM_HERE, base::Bind(callback, result));
}
-void HidConnectionLinux::Flush() {
- while (!pending_reads_.empty()) {
- pending_reads_.front().callback.Run(false, NULL, 0);
- pending_reads_.pop();
- }
+// static
+void HidConnectionLinux::CloseDevice(base::File device_file) {
+ device_file.Close();
}
void HidConnectionLinux::ProcessInputReport(scoped_refptr<net::IOBuffer> buffer,

Powered by Google App Engine
This is Rietveld 408576698