FreeBSD Bugzilla – Attachment 188231 Details for
Bug 196754
www/chromium: segfaults during login with security key
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
chrome.hid implementation for FreeBSD
chromium-freebsd-uhid.diff (text/plain), 31.19 KB, created by
Oleksandr Tymoshenko
on 2017-11-24 00:32:37 UTC
(
hide
)
Description:
chrome.hid implementation for FreeBSD
Filename:
MIME Type:
Creator:
Oleksandr Tymoshenko
Created:
2017-11-24 00:32:37 UTC
Size:
31.19 KB
patch
obsolete
>diff --git a/device/hid/BUILD.gn b/device/hid/BUILD.gn >index 6a95817..37e6033 100644 >--- a/device/hid/BUILD.gn >+++ b/device/hid/BUILD.gn >@@ -56,6 +56,21 @@ source_set("hid") { > deps += [ "//device/udev_linux" ] > } > >+ if (is_bsd) { >+ sources -= [ >+ "hid_connection_linux.cc", >+ "hid_connection_linux.h", >+ ] >+ sources += [ >+ "hid_connection_freebsd.cc", >+ "hid_connection_freebsd.h", >+ "hid_device_info_freebsd.cc", >+ "hid_device_info_freebsd.h", >+ "hid_service_freebsd.cc", >+ "hid_service_freebsd.h", >+ ] >+ } >+ > if (is_chromeos) { > deps += [ "//chromeos" ] > } >diff --git a/device/hid/hid_connection_freebsd.cc b/device/hid/hid_connection_freebsd.cc >new file mode 100644 >index 0000000..8552281 >--- /dev/null >+++ b/device/hid/hid_connection_freebsd.cc >@@ -0,0 +1,278 @@ >+// Copyright (c) 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_freebsd.h" >+ >+#include <dev/usb/usbhid.h> >+#include <dev/usb/usb_ioctl.h> >+ >+#include "base/bind.h" >+#include "base/files/file_descriptor_watcher_posix.h" >+#include "base/location.h" >+#include "base/numerics/safe_math.h" >+#include "base/posix/eintr_wrapper.h" >+#include "base/single_thread_task_runner.h" >+#include "base/strings/stringprintf.h" >+#include "base/task_scheduler/post_task.h" >+#include "base/threading/thread_restrictions.h" >+#include "base/threading/thread_task_runner_handle.h" >+#include "components/device_event_log/device_event_log.h" >+#include "device/hid/hid_service.h" >+ >+namespace device { >+ >+class HidConnectionFreeBSD::BlockingTaskHelper { >+ public: >+ BlockingTaskHelper(base::ScopedFD fd, >+ scoped_refptr<HidDeviceInfo> device_info, >+ base::WeakPtr<HidConnectionFreeBSD> connection) >+ : fd_(std::move(fd)), >+ connection_(connection), >+ origin_task_runner_(base::ThreadTaskRunnerHandle::Get()) { >+ DETACH_FROM_SEQUENCE(sequence_checker_); >+ // 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(); >+ } >+ >+ ~BlockingTaskHelper() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); } >+ >+ // Starts the FileDescriptorWatcher that reads input events from the device. >+ // Must be called on a thread that has a base::MessageLoopForIO. >+ void Start() { >+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); >+ base::ThreadRestrictions::AssertIOAllowed(); >+ >+ file_watcher_ = base::FileDescriptorWatcher::WatchReadable( >+ fd_.get(), base::Bind(&BlockingTaskHelper::OnFileCanReadWithoutBlocking, >+ base::Unretained(this))); >+ } >+ >+ void Write(scoped_refptr<net::IOBuffer> buffer, >+ size_t size, >+ const WriteCallback& callback) { >+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); >+ char *data = buffer->data(); >+ // if report id is 0, it shouldn't be included >+ if (data[0] == 0) { >+ data++; >+ size--; >+ } >+ >+ ssize_t result = HANDLE_EINTR(write(fd_.get(), data, size)); >+ if (result < 0) { >+ HID_PLOG(EVENT) << "Write failed"; >+ origin_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false)); >+ } else { >+ if (static_cast<size_t>(result) != size) >+ HID_LOG(EVENT) << "Incomplete HID write: " << result << " != " << size; >+ origin_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true)); >+ } >+ } >+ >+ void GetFeatureReport(uint8_t report_id, >+ scoped_refptr<net::IOBufferWithSize> buffer, >+ const ReadCallback& callback) { >+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); >+ struct usb_gen_descriptor ugd; >+ ugd.ugd_report_type = UHID_FEATURE_REPORT; >+ ugd.ugd_data = buffer->data(); >+ ugd.ugd_maxlen = buffer->size(); >+ int result = HANDLE_EINTR( >+ ioctl(fd_.get(), USB_GET_REPORT, &ugd)); >+ if (result < 0) { >+ HID_PLOG(EVENT) << "Failed to get feature report"; >+ origin_task_runner_->PostTask(FROM_HERE, >+ base::Bind(callback, false, nullptr, 0)); >+ } else if (result == 0) { >+ HID_LOG(EVENT) << "Get feature result too short."; >+ origin_task_runner_->PostTask(FROM_HERE, >+ base::Bind(callback, false, nullptr, 0)); >+ } else { >+ origin_task_runner_->PostTask(FROM_HERE, >+ base::Bind(callback, true, buffer, result)); >+ } >+ } >+ >+ void SendFeatureReport(scoped_refptr<net::IOBuffer> buffer, >+ size_t size, >+ const WriteCallback& callback) { >+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); >+ struct usb_gen_descriptor ugd; >+ ugd.ugd_report_type = UHID_FEATURE_REPORT; >+ ugd.ugd_data = buffer->data(); >+ ugd.ugd_maxlen = size; >+ // FreeBSD does not require report id if it's not used >+ if (buffer->data()[0] == 0) { >+ ugd.ugd_data = buffer->data() + 1; >+ ugd.ugd_maxlen = size - 1; >+ } else { >+ ugd.ugd_data = buffer->data(); >+ ugd.ugd_maxlen = size; >+ } >+ int result = HANDLE_EINTR( >+ ioctl(fd_.get(), USB_SET_REPORT, &ugd)); >+ if (result < 0) { >+ HID_PLOG(EVENT) << "Failed to send feature report"; >+ origin_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false)); >+ } else { >+ origin_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true)); >+ } >+ } >+ >+ private: >+ void OnFileCanReadWithoutBlocking() { >+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); >+ >+ 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_) { >+ // FreeBSD 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--; >+ } >+ >+ ssize_t bytes_read = HANDLE_EINTR(read(fd_.get(), data, length)); >+ if (bytes_read < 0) { >+ if (errno != EAGAIN) { >+ HID_PLOG(EVENT) << "Read failed"; >+ // This assumes that the error is unrecoverable and disables reading >+ // from the device until it has been re-opened. >+ // TODO(reillyg): Investigate starting and stopping the file descriptor >+ // watcher in response to pending read requests so that per-request >+ // errors can be returned to the client. >+ file_watcher_.reset(); >+ } >+ return; >+ } >+ if (!has_report_id_) { >+ // Behave as if the byte prefixed above as the the report ID was read. >+ bytes_read++; >+ } >+ >+ origin_task_runner_->PostTask( >+ FROM_HERE, base::Bind(&HidConnectionFreeBSD::ProcessInputReport, >+ connection_, buffer, bytes_read)); >+ } >+ >+ SEQUENCE_CHECKER(sequence_checker_); >+ base::ScopedFD fd_; >+ size_t report_buffer_size_; >+ bool has_report_id_; >+ base::WeakPtr<HidConnectionFreeBSD> connection_; >+ const scoped_refptr<base::SequencedTaskRunner> origin_task_runner_; >+ std::unique_ptr<base::FileDescriptorWatcher::Controller> file_watcher_; >+ >+ DISALLOW_COPY_AND_ASSIGN(BlockingTaskHelper); >+}; >+ >+HidConnectionFreeBSD::HidConnectionFreeBSD( >+ scoped_refptr<HidDeviceInfo> device_info, >+ base::ScopedFD fd, >+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) >+ : HidConnection(device_info), >+ blocking_task_runner_(std::move(blocking_task_runner)), >+ weak_factory_(this) { >+ helper_ = base::MakeUnique<BlockingTaskHelper>(std::move(fd), device_info, >+ weak_factory_.GetWeakPtr()); >+ blocking_task_runner_->PostTask( >+ FROM_HERE, >+ base::Bind(&BlockingTaskHelper::Start, base::Unretained(helper_.get()))); >+} >+ >+HidConnectionFreeBSD::~HidConnectionFreeBSD() {} >+ >+void HidConnectionFreeBSD::PlatformClose() { >+ // By closing the device on the blocking task runner 1) the requirement that >+ // base::ScopedFD is destroyed 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. >+ blocking_task_runner_->DeleteSoon(FROM_HERE, helper_.release()); >+ >+ while (!pending_reads_.empty()) { >+ pending_reads_.front().callback.Run(false, NULL, 0); >+ pending_reads_.pop(); >+ } >+} >+ >+void HidConnectionFreeBSD::PlatformRead(const ReadCallback& callback) { >+ DCHECK(thread_checker().CalledOnValidThread()); >+ PendingHidRead pending_read; >+ pending_read.callback = callback; >+ pending_reads_.push(pending_read); >+ ProcessReadQueue(); >+} >+ >+void HidConnectionFreeBSD::PlatformWrite(scoped_refptr<net::IOBuffer> buffer, >+ size_t size, >+ const WriteCallback& callback) { >+ >+ blocking_task_runner_->PostTask( >+ FROM_HERE, >+ base::Bind(&BlockingTaskHelper::Write, base::Unretained(helper_.get()), >+ buffer, size, callback)); >+} >+ >+void HidConnectionFreeBSD::PlatformGetFeatureReport(uint8_t report_id, >+ const ReadCallback& callback) { >+ // The first byte of the destination buffer is the report ID being requested >+ // and is overwritten by the feature report. >+ DCHECK_GT(device_info()->max_feature_report_size(), 0u); >+ scoped_refptr<net::IOBufferWithSize> buffer( >+ new net::IOBufferWithSize(device_info()->max_feature_report_size() + 1)); >+ if (report_id != 0) >+ buffer->data()[0] = report_id; >+ >+ blocking_task_runner_->PostTask( >+ FROM_HERE, >+ base::Bind(&BlockingTaskHelper::GetFeatureReport, >+ base::Unretained(helper_.get()), report_id, buffer, callback)); >+} >+ >+void HidConnectionFreeBSD::PlatformSendFeatureReport( >+ scoped_refptr<net::IOBuffer> buffer, >+ size_t size, >+ const WriteCallback& callback) { >+ blocking_task_runner_->PostTask( >+ FROM_HERE, >+ base::Bind(&BlockingTaskHelper::SendFeatureReport, >+ base::Unretained(helper_.get()), buffer, size, callback)); >+} >+ >+void HidConnectionFreeBSD::ProcessInputReport( >+ scoped_refptr<net::IOBuffer> buffer, >+ size_t size) { >+ DCHECK(thread_checker().CalledOnValidThread()); >+ DCHECK_GE(size, 1u); >+ >+ uint8_t report_id = buffer->data()[0]; >+ if (IsReportIdProtected(report_id)) >+ return; >+ >+ PendingHidReport report; >+ report.buffer = buffer; >+ report.size = size; >+ pending_reports_.push(report); >+ ProcessReadQueue(); >+} >+ >+void HidConnectionFreeBSD::ProcessReadQueue() { >+ DCHECK(thread_checker().CalledOnValidThread()); >+ >+ // Hold a reference to |this| to prevent a callback from freeing this object >+ // during the loop. >+ scoped_refptr<HidConnectionFreeBSD> self(this); >+ while (pending_reads_.size() && pending_reports_.size()) { >+ PendingHidRead read = pending_reads_.front(); >+ PendingHidReport report = pending_reports_.front(); >+ >+ pending_reads_.pop(); >+ pending_reports_.pop(); >+ read.callback.Run(true, report.buffer, report.size); >+ } >+} >+ >+} // namespace device >diff --git a/device/hid/hid_connection_freebsd.h b/device/hid/hid_connection_freebsd.h >new file mode 100644 >index 0000000..1735107 >--- /dev/null >+++ b/device/hid/hid_connection_freebsd.h >@@ -0,0 +1,76 @@ >+// Copyright (c) 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. >+ >+#ifndef DEVICE_HID_HID_CONNECTION_FREEBSD_H_ >+#define DEVICE_HID_HID_CONNECTION_FREEBSD_H_ >+ >+#include <stddef.h> >+#include <stdint.h> >+ >+#include <queue> >+ >+#include "base/files/scoped_file.h" >+#include "base/macros.h" >+#include "base/memory/weak_ptr.h" >+#include "base/sequence_checker.h" >+#include "device/hid/hid_connection.h" >+ >+namespace base { >+class SequencedTaskRunner; >+} >+ >+namespace net { >+class IOBuffer; >+} >+ >+namespace device { >+ >+class HidConnectionFreeBSD : public HidConnection { >+ public: >+ HidConnectionFreeBSD( >+ scoped_refptr<HidDeviceInfo> device_info, >+ base::ScopedFD fd, >+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner); >+ >+ private: >+ friend class base::RefCountedThreadSafe<HidConnectionFreeBSD>; >+ class BlockingTaskHelper; >+ >+ ~HidConnectionFreeBSD() override; >+ >+ // HidConnection implementation. >+ void PlatformClose() override; >+ void PlatformRead(const ReadCallback& callback) override; >+ void PlatformWrite(scoped_refptr<net::IOBuffer> buffer, >+ size_t size, >+ const WriteCallback& callback) override; >+ void PlatformGetFeatureReport(uint8_t report_id, >+ const ReadCallback& callback) override; >+ void PlatformSendFeatureReport(scoped_refptr<net::IOBuffer> buffer, >+ size_t size, >+ const WriteCallback& callback) override; >+ void ProcessInputReport(scoped_refptr<net::IOBuffer> buffer, size_t size); >+ void ProcessReadQueue(); >+ >+ // |helper_| lives on the sequence to which |blocking_task_runner_| posts >+ // tasks so all calls must be posted there including this object's >+ // destruction. >+ std::unique_ptr<BlockingTaskHelper> helper_; >+ >+ const scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_; >+ >+ std::queue<PendingHidReport> pending_reports_; >+ std::queue<PendingHidRead> pending_reads_; >+ const scoped_refptr<base::SequencedTaskRunner> task_runner_; >+ >+ SEQUENCE_CHECKER(sequence_checker_); >+ >+ base::WeakPtrFactory<HidConnectionFreeBSD> weak_factory_; >+ >+ DISALLOW_COPY_AND_ASSIGN(HidConnectionFreeBSD); >+}; >+ >+} // namespace device >+ >+#endif // DEVICE_HID_HID_CONNECTION_FREEBSD_H_ >diff --git a/device/hid/hid_device_info_freebsd.cc b/device/hid/hid_device_info_freebsd.cc >new file mode 100644 >index 0000000..adbd1cf >--- /dev/null >+++ b/device/hid/hid_device_info_freebsd.cc >@@ -0,0 +1,30 @@ >+// Copyright 2015 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 "hid_device_info_freebsd.h" >+ >+namespace device { >+ >+HidDeviceInfoFreeBSD::HidDeviceInfoFreeBSD( >+ const HidDeviceId& device_id, >+ const std::string& device_node, >+ uint16_t vendor_id, >+ uint16_t product_id, >+ const std::string& product_name, >+ const std::string& serial_number, >+ HidBusType bus_type, >+ const std::vector<uint8_t> report_descriptor) >+ : HidDeviceInfo(device_id, >+ vendor_id, >+ product_id, >+ product_name, >+ serial_number, >+ bus_type, >+ report_descriptor), >+ device_node_(device_node) {} >+ >+HidDeviceInfoFreeBSD::~HidDeviceInfoFreeBSD() { >+} >+ >+} // namespace device >diff --git a/device/hid/hid_device_info_freebsd.h b/device/hid/hid_device_info_freebsd.h >new file mode 100644 >index 0000000..d41cca8 >--- /dev/null >+++ b/device/hid/hid_device_info_freebsd.h >@@ -0,0 +1,35 @@ >+// Copyright 2015 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. >+ >+#ifndef DEVICE_HID_HID_DEVICE_INFO_LINUX_H_ >+#define DEVICE_HID_HID_DEVICE_INFO_LINUX_H_ >+ >+#include <stdint.h> >+ >+#include "device/hid/hid_device_info.h" >+ >+namespace device { >+ >+class HidDeviceInfoFreeBSD : public HidDeviceInfo { >+ public: >+ HidDeviceInfoFreeBSD(const HidDeviceId& device_id, >+ const std::string& device_node, >+ uint16_t vendor_id, >+ uint16_t product_id, >+ const std::string& product_name, >+ const std::string& serial_number, >+ HidBusType bus_type, >+ const std::vector<uint8_t> report_descriptor); >+ >+ const std::string& device_node() const { return device_node_; } >+ >+ private: >+ ~HidDeviceInfoFreeBSD() override; >+ >+ std::string device_node_; >+}; >+ >+} // namespace device >+ >+#endif // DEVICE_HID_HID_DEVICE_INFO_LINUX_H_ >diff --git a/device/hid/hid_service.cc b/device/hid/hid_service.cc >index f129a97..7e4ee71 100644 >--- a/device/hid/hid_service.cc >+++ b/device/hid/hid_service.cc >@@ -16,6 +16,8 @@ > > #if defined(OS_LINUX) && defined(USE_UDEV) > #include "device/hid/hid_service_linux.h" >+#elif defined(OS_BSD) >+#include "device/hid/hid_service_freebsd.h" > #elif defined(OS_MACOSX) > #include "device/hid/hid_service_mac.h" > #elif defined(OS_WIN) >@@ -42,6 +44,8 @@ constexpr base::TaskTraits HidService::kBlockingTaskTraits; > std::unique_ptr<HidService> HidService::Create() { > #if defined(OS_LINUX) && defined(USE_UDEV) > return base::WrapUnique(new HidServiceLinux()); >+#elif defined(OS_BSD) >+ return base::WrapUnique(new HidServiceFreeBSD()); > #elif defined(OS_MACOSX) > return base::WrapUnique(new HidServiceMac()); > #elif defined(OS_WIN) >diff --git a/device/hid/hid_service_freebsd.cc b/device/hid/hid_service_freebsd.cc >new file mode 100644 >index 0000000..da795ed >--- /dev/null >+++ b/device/hid/hid_service_freebsd.cc >@@ -0,0 +1,370 @@ >+// 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_service_freebsd.h" >+ >+#include <dev/usb/usb_ioctl.h> >+#include <stdint.h> >+#include <sys/socket.h> >+#include <sys/un.h> >+ >+#include <set> >+#include <string> >+#include <vector> >+ >+#include "base/bind.h" >+#include "base/files/file_descriptor_watcher_posix.h" >+#include "base/files/file_enumerator.h" >+#include "base/location.h" >+#include "base/logging.h" >+#include "base/posix/eintr_wrapper.h" >+#include "base/single_thread_task_runner.h" >+#include "base/stl_util.h" >+#include "base/strings/pattern.h" >+#include "base/strings/stringprintf.h" >+#include "base/strings/sys_string_conversions.h" >+#include "base/strings/string_util.h" >+#include "base/strings/string_split.h" >+#include "base/task_scheduler/post_task.h" >+#include "base/threading/thread_restrictions.h" >+#include "base/threading/thread_task_runner_handle.h" >+#include "components/device_event_log/device_event_log.h" >+#include "device/hid/hid_connection_freebsd.h" >+#include "device/hid/hid_device_info_freebsd.h" >+ >+const int kMaxPermissionChecks = 5; >+ >+namespace device { >+ >+struct HidServiceFreeBSD::ConnectParams { >+ ConnectParams(scoped_refptr<HidDeviceInfoFreeBSD> device_info, >+ const ConnectCallback& callback) >+ : device_info(std::move(device_info)), >+ callback(callback), >+ task_runner(base::ThreadTaskRunnerHandle::Get()), >+ blocking_task_runner( >+ base::CreateSequencedTaskRunnerWithTraits(kBlockingTaskTraits)) {} >+ ~ConnectParams() {} >+ >+ scoped_refptr<HidDeviceInfoFreeBSD> device_info; >+ ConnectCallback callback; >+ scoped_refptr<base::SequencedTaskRunner> task_runner; >+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner; >+ base::ScopedFD fd; >+}; >+ >+class HidServiceFreeBSD::BlockingTaskHelper { >+ public: >+ BlockingTaskHelper(base::WeakPtr<HidServiceFreeBSD> service) >+ : service_(std::move(service)), >+ task_runner_(base::ThreadTaskRunnerHandle::Get()) { >+ DETACH_FROM_SEQUENCE(sequence_checker_); >+ >+ timer_.reset(new base::RepeatingTimer()); >+ devd_buffer_ = new net::IOBufferWithSize(1024); >+ } >+ >+ ~BlockingTaskHelper() { >+ } >+ >+ void Start() { >+ base::ThreadRestrictions::AssertIOAllowed(); >+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); >+ >+ const base::FilePath kDevRoot("/dev"); >+ const std::string kUHIDPattern("/dev/uhid*"); >+ >+ base::FileEnumerator enumerator(kDevRoot, false, base::FileEnumerator::FILES); >+ do { >+ const base::FilePath next_device_path(enumerator.Next()); >+ const std::string next_device = next_device_path.value(); >+ if (next_device.empty()) >+ break; >+ >+ if (base::MatchPattern(next_device, kUHIDPattern)) >+ OnDeviceAdded(next_device.substr(5)); >+ } while (true); >+ >+ SetupDevdMonitor(); >+ >+ task_runner_->PostTask( >+ FROM_HERE, >+ base::Bind(&HidServiceFreeBSD::FirstEnumerationComplete, service_)); >+ } >+ >+ bool HaveReadWritePermissions(std::string device_id) { >+ std::string device_node = "/dev/" + device_id; >+ base::ThreadRestrictions::AssertIOAllowed(); >+ >+ base::FilePath device_path(device_node); >+ base::File device_file; >+ int flags = >+ base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE; >+ device_file.Initialize(device_path, flags); >+ if (!device_file.IsValid()) >+ return false; >+ >+ return true; >+ } >+ >+ void OnDeviceAdded(std::string device_id) { >+ std::string device_node = "/dev/" + device_id; >+ uint16_t vendor_id = 0xffff; >+ uint16_t product_id = 0xffff; >+ std::string product_name = ""; >+ std::string serial_number = ""; >+ >+ std::vector<uint8_t> report_descriptor; >+ >+ base::ThreadRestrictions::AssertIOAllowed(); >+ >+ base::FilePath device_path(device_node); >+ base::File device_file; >+ int flags = >+ base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE; >+ device_file.Initialize(device_path, flags); >+ if (!device_file.IsValid()) { >+ HID_LOG(ERROR) << "Failed to open '" << device_node >+ << "': " >+ << base::File::ErrorToString(device_file.error_details()); >+ return; >+ } >+ >+ base::ScopedFD fd; >+ fd.reset(device_file.TakePlatformFile()); >+ >+ struct usb_gen_descriptor ugd; >+ ugd.ugd_data = NULL; >+ ugd.ugd_maxlen = 0xffff; >+ int result = HANDLE_EINTR( >+ ioctl(fd.get(), USB_GET_REPORT_DESC, &ugd)); >+ >+ if (result < 0) { >+ HID_LOG(ERROR) << "Failed to get report descriptor size"; >+ return; >+ } >+ >+ report_descriptor.resize(ugd.ugd_actlen); >+ >+ ugd.ugd_data = report_descriptor.data(); >+ ugd.ugd_maxlen = ugd.ugd_actlen; >+ result = HANDLE_EINTR( >+ ioctl(fd.get(), USB_GET_REPORT_DESC, &ugd)); >+ >+ if (result < 0) { >+ HID_LOG(ERROR) << "Failed to get report descriptor"; >+ return; >+ } >+ >+ scoped_refptr<HidDeviceInfoFreeBSD> device_info(new HidDeviceInfoFreeBSD( >+ device_id, device_node, vendor_id, product_id, product_name, >+ serial_number, >+ kHIDBusTypeUSB, >+ report_descriptor)); >+ >+ task_runner_->PostTask(FROM_HERE, base::Bind(&HidServiceFreeBSD::AddDevice, >+ service_, device_info)); >+ } >+ >+ void OnDeviceRemoved(std::string device_id) { >+ task_runner_->PostTask( >+ FROM_HERE, base::Bind(&HidServiceFreeBSD::RemoveDevice, service_, >+ device_id)); >+ } >+ >+ private: >+ >+ void CheckPendingPermissionChange() { >+ base::ThreadRestrictions::AssertIOAllowed(); >+ std::map<std::string, int>::iterator it; >+ for (it = permissions_checks_attempts_.begin(); it != permissions_checks_attempts_.end();) { >+ std::string device_name = it->first; >+ bool keep = true; >+ if (HaveReadWritePermissions(device_name)) { >+ OnDeviceAdded(device_name); >+ keep = false; >+ } >+ else if (it->second-- <= 0) { >+ HID_LOG(ERROR) << "Still don't have write permissions to '" << device_name >+ << "' after " << kMaxPermissionChecks << " attempts"; >+ keep = false; >+ } >+ >+ if (keep) >+ ++it; >+ else >+ permissions_checks_attempts_.erase(it++); >+ } >+ >+ if (permissions_checks_attempts_.empty()) >+ timer_->Stop(); >+ } >+ >+ void SetupDevdMonitor() { >+ base::ThreadRestrictions::AssertIOAllowed(); >+ >+ int devd_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); >+ if (devd_fd < 0) >+ return; >+ >+ struct sockaddr_un sa; >+ >+ sa.sun_family = AF_UNIX; >+ strlcpy(sa.sun_path, "/var/run/devd.seqpacket.pipe", sizeof(sa.sun_path)); >+ if (connect(devd_fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { >+ close(devd_fd); >+ return; >+ } >+ >+ devd_fd_.reset(devd_fd); >+ file_watcher_ = base::FileDescriptorWatcher::WatchReadable( >+ devd_fd_.get(), base::Bind(&BlockingTaskHelper::OnDevdMessageCanBeRead, >+ base::Unretained(this))); >+ } >+ >+ void OnDevdMessageCanBeRead() { >+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); >+ ssize_t bytes_read = HANDLE_EINTR(recv(devd_fd_.get(), devd_buffer_->data(), >+ devd_buffer_->size() - 1, MSG_WAITALL)); >+ if (bytes_read < 0) { >+ if (errno != EAGAIN) { >+ HID_LOG(ERROR) << "Read failed"; >+ file_watcher_.reset(); >+ } >+ return; >+ } >+ >+ devd_buffer_->data()[bytes_read] = 0; >+ char *data = devd_buffer_->data(); >+ // It may take some time for devd to change permissions >+ // on /dev/uhidX node. So do not fail immediately if >+ // open fail. Retry each second for kMaxPermissionChecks >+ // times before giving up entirely >+ if (base::StartsWith(data, "+uhid", base::CompareCase::SENSITIVE)) { >+ std::vector<std::string> parts = base::SplitString( >+ data, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); >+ if (!parts.empty()) { >+ std::string device_name = parts[0].substr(1); // skip '+' >+ if (HaveReadWritePermissions(device_name)) >+ OnDeviceAdded(parts[0].substr(1)); >+ else { >+ // Do not re-add to checks >+ if (permissions_checks_attempts_.find(device_name) == permissions_checks_attempts_.end()) { >+ permissions_checks_attempts_.insert(std::pair<std::string, int>(device_name, kMaxPermissionChecks)); >+ timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(1), >+ this, &BlockingTaskHelper::CheckPendingPermissionChange); >+ } >+ } >+ } >+ } >+ >+ if (base::StartsWith(data, "-uhid", base::CompareCase::SENSITIVE)) { >+ std::vector<std::string> parts = base::SplitString( >+ data, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); >+ if (!parts.empty()) { >+ std::string device_name = parts[0].substr(1); // skip '-' >+ auto it = permissions_checks_attempts_.find(device_name); >+ if (it != permissions_checks_attempts_.end()) { >+ permissions_checks_attempts_.erase(it); >+ if (permissions_checks_attempts_.empty()) >+ timer_->Stop(); >+ } >+ OnDeviceRemoved(parts[0].substr(1)); >+ } >+ } >+ } >+ >+ SEQUENCE_CHECKER(sequence_checker_); >+ >+ // This weak pointer is only valid when checked on this task runner. >+ base::WeakPtr<HidServiceFreeBSD> service_; >+ scoped_refptr<base::SequencedTaskRunner> task_runner_; >+ std::unique_ptr<base::FileDescriptorWatcher::Controller> file_watcher_; >+ std::unique_ptr<base::RepeatingTimer> timer_; >+ base::ScopedFD devd_fd_; >+ scoped_refptr<net::IOBufferWithSize> devd_buffer_; >+ std::map<std::string, int> permissions_checks_attempts_; >+ >+ DISALLOW_COPY_AND_ASSIGN(BlockingTaskHelper); >+}; >+ >+HidServiceFreeBSD::HidServiceFreeBSD() >+ : task_runner_(base::ThreadTaskRunnerHandle::Get()), >+ blocking_task_runner_( >+ base::CreateSequencedTaskRunnerWithTraits(kBlockingTaskTraits)), >+ weak_factory_(this) { >+ helper_ = base::MakeUnique<BlockingTaskHelper>(weak_factory_.GetWeakPtr()); >+ blocking_task_runner_->PostTask( >+ FROM_HERE, >+ base::Bind(&BlockingTaskHelper::Start, base::Unretained(helper_.get()))); >+} >+ >+HidServiceFreeBSD::~HidServiceFreeBSD() { >+ blocking_task_runner_->DeleteSoon(FROM_HERE, helper_.release()); >+} >+ >+// static >+void HidServiceFreeBSD::OpenOnBlockingThread( >+ std::unique_ptr<ConnectParams> params) { >+ base::ThreadRestrictions::AssertIOAllowed(); >+ scoped_refptr<base::SequencedTaskRunner> task_runner = params->task_runner; >+ >+ base::FilePath device_path(params->device_info->device_node()); >+ base::File device_file; >+ int flags = >+ base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE; >+ device_file.Initialize(device_path, flags); >+ if (!device_file.IsValid()) { >+ HID_LOG(EVENT) << "Failed to open '" << params->device_info->device_node() >+ << "': " >+ << base::File::ErrorToString(device_file.error_details()); >+ task_runner->PostTask(FROM_HERE, base::Bind(params->callback, nullptr)); >+ return; >+ } >+ params->fd.reset(device_file.TakePlatformFile()); >+ FinishOpen(std::move(params)); >+} >+ >+void HidServiceFreeBSD::Connect(const HidDeviceId& device_id, >+ const ConnectCallback& callback) { >+ DCHECK(thread_checker_.CalledOnValidThread()); >+ >+ const auto& map_entry = devices().find(device_id); >+ if (map_entry == devices().end()) { >+ base::ThreadTaskRunnerHandle::Get()->PostTask( >+ FROM_HERE, base::Bind(callback, nullptr)); >+ return; >+ } >+ >+ scoped_refptr<HidDeviceInfoFreeBSD> device_info = >+ static_cast<HidDeviceInfoFreeBSD*>(map_entry->second.get()); >+ >+ auto params = base::MakeUnique<ConnectParams>(device_info, callback); >+ >+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner = >+ params->blocking_task_runner; >+ blocking_task_runner->PostTask( >+ FROM_HERE, base::Bind(&HidServiceFreeBSD::OpenOnBlockingThread, >+ base::Passed(¶ms))); >+} >+ >+// static >+void HidServiceFreeBSD::FinishOpen(std::unique_ptr<ConnectParams> params) { >+ base::ThreadRestrictions::AssertIOAllowed(); >+ scoped_refptr<base::SequencedTaskRunner> task_runner = params->task_runner; >+ >+ task_runner->PostTask( >+ FROM_HERE, >+ base::Bind(&HidServiceFreeBSD::CreateConnection, base::Passed(¶ms))); >+} >+ >+// static >+void HidServiceFreeBSD::CreateConnection(std::unique_ptr<ConnectParams> params) { >+ DCHECK(params->fd.is_valid()); >+ params->callback.Run(base::MakeRefCounted<HidConnectionFreeBSD>( >+ std::move(params->device_info), std::move(params->fd), >+ std::move(params->blocking_task_runner))); >+} >+ >+} // namespace device >diff --git a/device/hid/hid_service_freebsd.h b/device/hid/hid_service_freebsd.h >new file mode 100644 >index 0000000..863715f >--- /dev/null >+++ b/device/hid/hid_service_freebsd.h >@@ -0,0 +1,47 @@ >+// 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. >+ >+#ifndef DEVICE_HID_HID_SERVICE_FREEBSD_H_ >+#define DEVICE_HID_HID_SERVICE_FREEBSD_H_ >+ >+#include <string> >+ >+#include "base/macros.h" >+#include "base/memory/ref_counted.h" >+#include "base/memory/weak_ptr.h" >+#include "base/timer/timer.h" >+#include "device/hid/hid_service.h" >+#include "net/base/io_buffer.h" >+ >+namespace device { >+ >+class HidServiceFreeBSD : public HidService { >+ public: >+ HidServiceFreeBSD(); >+ ~HidServiceFreeBSD() override; >+ >+ void Connect(const HidDeviceId& device_id, >+ const ConnectCallback& connect) override; >+ >+ private: >+ struct ConnectParams; >+ class BlockingTaskHelper; >+ >+ static void OpenOnBlockingThread(std::unique_ptr<ConnectParams> params); >+ static void FinishOpen(std::unique_ptr<ConnectParams> params); >+ static void CreateConnection(std::unique_ptr<ConnectParams> params); >+ >+ const scoped_refptr<base::SequencedTaskRunner> task_runner_; >+ const scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_; >+ // |helper_| lives on the sequence |blocking_task_runner_| posts to and holds >+ // a weak reference back to the service that owns it. >+ std::unique_ptr<BlockingTaskHelper> helper_; >+ base::WeakPtrFactory<HidServiceFreeBSD> weak_factory_; >+ >+ DISALLOW_COPY_AND_ASSIGN(HidServiceFreeBSD); >+}; >+ >+} // namespace device >+ >+#endif // DEVICE_HID_HID_SERVICE_FREEBSD_H_
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 196754
:
183157
|
187829
| 188231 |
188273
|
188626