diff options
author | ikarienator@chromium.org <ikarienator@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-30 10:41:05 +0000 |
---|---|---|
committer | ikarienator@chromium.org <ikarienator@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-30 10:41:05 +0000 |
commit | a2c724ab72ccebb6d78e228caefd36b100e8308a (patch) | |
tree | f023f4383558055b3a41f6183983da7e9183e1ee /device/hid/hid_connection_linux.cc | |
parent | 441e25cdf56a650df64a17416314aeee1598ccff (diff) | |
download | chromium_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.cc | 231 |
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 |