summaryrefslogtreecommitdiffstats
path: root/device/hid/hid_connection_linux.cc
diff options
context:
space:
mode:
authorikarienator@chromium.org <ikarienator@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-30 10:41:05 +0000
committerikarienator@chromium.org <ikarienator@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-30 10:41:05 +0000
commita2c724ab72ccebb6d78e228caefd36b100e8308a (patch)
treef023f4383558055b3a41f6183983da7e9183e1ee /device/hid/hid_connection_linux.cc
parent441e25cdf56a650df64a17416314aeee1598ccff (diff)
downloadchromium_src-a2c724ab72ccebb6d78e228caefd36b100e8308a.zip
chromium_src-a2c724ab72ccebb6d78e228caefd36b100e8308a.tar.gz
chromium_src-a2c724ab72ccebb6d78e228caefd36b100e8308a.tar.bz2
HID backend
BUG=290428 Review URL: https://codereview.chromium.org/143883005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@247926 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'device/hid/hid_connection_linux.cc')
-rw-r--r--device/hid/hid_connection_linux.cc231
1 files changed, 231 insertions, 0 deletions
diff --git a/device/hid/hid_connection_linux.cc b/device/hid/hid_connection_linux.cc
new file mode 100644
index 0000000..7b9285c
--- /dev/null
+++ b/device/hid/hid_connection_linux.cc
@@ -0,0 +1,231 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/hid/hid_connection_linux.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <libudev.h>
+#include <linux/hidraw.h>
+#include <string>
+
+#include "base/threading/thread_restrictions.h"
+#include "base/tuple.h"
+#include "device/hid/hid_service.h"
+#include "device/hid/hid_service_linux.h"
+
+
+namespace device {
+
+namespace {
+
+const char kHidrawSubsystem[] = "hidraw";
+
+} // namespace
+
+HidConnectionLinux::HidConnectionLinux(HidDeviceInfo device_info,
+ ScopedUdevDevicePtr udev_raw_device)
+ : HidConnection(device_info),
+ initialized_(false) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ udev_device* dev = udev_raw_device.get();
+ std::string dev_node;
+ if (!FindHidrawDevNode(dev, &dev_node)) {
+ LOG(ERROR) << "Cannot open HID device as hidraw device.";
+ return;
+ }
+
+ base::PlatformFileError error;
+
+ int flags = base::PLATFORM_FILE_OPEN |
+ base::PLATFORM_FILE_READ |
+ base::PLATFORM_FILE_WRITE |
+ base::PLATFORM_FILE_EXCLUSIVE_READ |
+ base::PLATFORM_FILE_EXCLUSIVE_WRITE;
+
+ base::PlatformFile device_file = base::CreatePlatformFile(
+ base::FilePath(dev_node),
+ flags,
+ NULL,
+ &error);
+ if (error || device_file <= 0) {
+ LOG(ERROR) << error;
+ if (device_file)
+ base::ClosePlatformFile(device_file);
+ return;
+ }
+ if (fcntl(device_file, F_SETFL, fcntl(device_file, F_GETFL) | O_NONBLOCK)) {
+ PLOG(ERROR) << "Failed to set non-blocking flag to device file.";
+ return;
+ }
+ device_file_ = device_file;
+
+ if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
+ device_file_,
+ true,
+ base::MessageLoopForIO::WATCH_READ_WRITE,
+ &device_file_watcher_,
+ this)) {
+ LOG(ERROR) << "Cannot start watching file descriptor.";
+ return;
+ }
+
+ initialized_ = true;
+}
+
+HidConnectionLinux::~HidConnectionLinux() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ Disconnect();
+}
+
+void HidConnectionLinux::OnFileCanReadWithoutBlocking(int fd) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_EQ(fd, device_file_);
+ DCHECK(initialized_);
+
+ uint8 buffer[1024] = {0};
+ int bytes = read(device_file_, buffer, 1024);
+ if (bytes < 0) {
+ if (errno == EAGAIN) {
+ return;
+ }
+ Disconnect();
+ return;
+ }
+ scoped_refptr<net::IOBuffer> io_buffer(new net::IOBuffer(bytes));
+ memcpy(io_buffer->data(), buffer, bytes);
+ input_reports_.push(std::make_pair(io_buffer, bytes));
+
+ ProcessReadQueue();
+}
+
+void HidConnectionLinux::OnFileCanWriteWithoutBlocking(int fd) {}
+
+void HidConnectionLinux::Disconnect() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!initialized_)
+ return;
+
+ initialized_ = false;
+ device_file_watcher_.StopWatchingFileDescriptor();
+ close(device_file_);
+ while (!read_queue_.empty()) {
+ PendingRequest callback = read_queue_.front();
+ read_queue_.pop();
+ callback.c.Run(false, 0);
+ }
+}
+
+void HidConnectionLinux::Read(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!initialized_) {
+ DCHECK(read_queue_.empty());
+ // There might be unread reports.
+ if (!input_reports_.empty()){
+ read_queue_.push(MakeTuple(buffer, size, callback));
+ ProcessReadQueue();
+ }
+ callback.Run(false, 0);
+ return;
+ } else {
+ read_queue_.push(MakeTuple(buffer, size, callback));
+ ProcessReadQueue();
+ }
+}
+
+void HidConnectionLinux::Write(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!initialized_) {
+ callback.Run(false, 0);
+ return;
+ } else {
+ int bytes = write(device_file_, buffer->data(), size);
+ if (bytes < 0) {
+ Disconnect();
+ callback.Run(false, 0);
+ } else {
+ callback.Run(true, bytes);
+ }
+ }
+}
+
+void HidConnectionLinux::GetFeatureReport(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!initialized_) {
+ callback.Run(false, 0);
+ return;
+ }
+ NOTIMPLEMENTED();
+}
+
+void HidConnectionLinux::SendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!initialized_) {
+ callback.Run(false, 0);
+ return;
+ }
+ NOTIMPLEMENTED();
+}
+
+void HidConnectionLinux::ProcessReadQueue() {
+ while(read_queue_.size() && input_reports_.size()) {
+ PendingRequest request = read_queue_.front();
+ read_queue_.pop();
+ PendingReport report = input_reports_.front();
+ if (report.second > request.b) {
+ request.c.Run(false, report.second);
+ } else {
+ memcpy(request.a->data(), report.first->data(), report.second);
+ input_reports_.pop();
+ request.c.Run(true, report.second);
+ }
+ }
+}
+
+bool HidConnectionLinux::FindHidrawDevNode(udev_device* parent,
+ std::string* result) {
+ udev* udev = udev_device_get_udev(parent);
+ if (!udev)
+ return false;
+
+ ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev));
+ if (!enumerate)
+ return false;
+
+ if (udev_enumerate_add_match_parent(enumerate.get(), parent)) {
+ return false;
+ }
+
+ if (udev_enumerate_add_match_subsystem(enumerate.get(), kHidrawSubsystem)) {
+ return false;
+ }
+ if (udev_enumerate_scan_devices(enumerate.get())) {
+ return false;
+ }
+
+ udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate.get());
+ for (udev_list_entry* i = devices; i != NULL;
+ i = udev_list_entry_get_next(i)) {
+ ScopedUdevDevicePtr hid_dev(
+ udev_device_new_from_syspath(udev, udev_list_entry_get_name(i)));
+ const char* raw_path = udev_device_get_devnode(hid_dev.get());
+ if (raw_path) {
+ *result = raw_path;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // namespace device