From owner-svn-ports-all@freebsd.org Mon Dec 4 05:44:26 2017 Return-Path: Delivered-To: svn-ports-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 51408DEEC75; Mon, 4 Dec 2017 05:44:26 +0000 (UTC) (envelope-from gonzo@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id E8F9779990; Mon, 4 Dec 2017 05:44:25 +0000 (UTC) (envelope-from gonzo@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id vB45iPNX090279; Mon, 4 Dec 2017 05:44:25 GMT (envelope-from gonzo@FreeBSD.org) Received: (from gonzo@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id vB45iOYZ090269; Mon, 4 Dec 2017 05:44:24 GMT (envelope-from gonzo@FreeBSD.org) Message-Id: <201712040544.vB45iOYZ090269@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: gonzo set sender to gonzo@FreeBSD.org using -f From: Oleksandr Tymoshenko Date: Mon, 4 Dec 2017 05:44:24 +0000 (UTC) To: ports-committers@freebsd.org, svn-ports-all@freebsd.org, svn-ports-head@freebsd.org Subject: svn commit: r455495 - in head/www/chromium: . files X-SVN-Group: ports-head X-SVN-Commit-Author: gonzo X-SVN-Commit-Paths: in head/www/chromium: . files X-SVN-Commit-Revision: 455495 X-SVN-Commit-Repository: ports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-ports-all@freebsd.org X-Mailman-Version: 2.1.25 Precedence: list List-Id: SVN commit messages for the ports tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 04 Dec 2017 05:44:26 -0000 Author: gonzo (src committer) Date: Mon Dec 4 05:44:24 2017 New Revision: 455495 URL: https://svnweb.freebsd.org/changeset/ports/455495 Log: www/chromium: add support for USB U2F devices Implement enough of chrome.hid API to make U2F security keys work. Due to FreeBSD's HID API limitations it's not possible to consistently report vedor id or product id values for HID devices so if javascript library relies on this information to detect supported keys it won't work, only report descriptor based detection works. Also by default uhid(4) devices accessible only to root user, current solution is to change /dev/uhidX node permission using devd. For example create /usr/local/etc/devd/yubi.conf with following content and restart devd: attach 20 { match "vendor" "0x1050"; action "/bin/chmod 766 /dev/$device-name"; }; Work on u2f-devd port is in progress (see PR). When done it will take care of maintaining devd rulesets for U2F devices. PR: 196754 Approved by: cpm Added: head/www/chromium/files/patch-device_hid_hid__connection__freebsd.cc (contents, props changed) head/www/chromium/files/patch-device_hid_hid__connection__freebsd.h (contents, props changed) head/www/chromium/files/patch-device_hid_hid__device__info__freebsd.cc (contents, props changed) head/www/chromium/files/patch-device_hid_hid__device__info__freebsd.h (contents, props changed) head/www/chromium/files/patch-device_hid_hid__service.cc (contents, props changed) head/www/chromium/files/patch-device_hid_hid__service__freebsd.cc (contents, props changed) head/www/chromium/files/patch-device_hid_hid__service__freebsd.h (contents, props changed) Modified: head/www/chromium/Makefile head/www/chromium/files/patch-device_hid_BUILD.gn Modified: head/www/chromium/Makefile ============================================================================== --- head/www/chromium/Makefile Mon Dec 4 03:46:27 2017 (r455494) +++ head/www/chromium/Makefile Mon Dec 4 05:44:24 2017 (r455495) @@ -3,7 +3,7 @@ PORTNAME= chromium PORTVERSION= 61.0.3163.100 -PORTREVISION= 3 +PORTREVISION= 4 CATEGORIES?= www MASTER_SITES= https://commondatastorage.googleapis.com/chromium-browser-official/ DISTFILES= ${DISTNAME}${EXTRACT_SUFX} Modified: head/www/chromium/files/patch-device_hid_BUILD.gn ============================================================================== --- head/www/chromium/files/patch-device_hid_BUILD.gn Mon Dec 4 03:46:27 2017 (r455494) +++ head/www/chromium/files/patch-device_hid_BUILD.gn Mon Dec 4 05:44:24 2017 (r455495) @@ -1,13 +1,21 @@ ---- device/hid/BUILD.gn.orig 2017-04-19 19:06:34 UTC -+++ device/hid/BUILD.gn -@@ -56,6 +56,13 @@ source_set("hid") { - deps += [ "//device/udev_linux" ] +--- device/hid/BUILD.gn.orig 2017-12-03 15:37:09.320721000 -0800 ++++ device/hid/BUILD.gn 2017-12-03 15:37:32.146570000 -0800 +@@ -63,6 +63,21 @@ + ] } + 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", + ] + } + Added: head/www/chromium/files/patch-device_hid_hid__connection__freebsd.cc ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/www/chromium/files/patch-device_hid_hid__connection__freebsd.cc Mon Dec 4 05:44:24 2017 (r455495) @@ -0,0 +1,281 @@ +--- device/hid/hid_connection_freebsd.cc.orig 2017-12-03 15:37:32.146994000 -0800 ++++ device/hid/hid_connection_freebsd.cc 2017-12-03 15:37:32.154605000 -0800 +@@ -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 ++#include ++ ++#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 device_info, ++ base::WeakPtr 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 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(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 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 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 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 connection_; ++ const scoped_refptr origin_task_runner_; ++ std::unique_ptr file_watcher_; ++ ++ DISALLOW_COPY_AND_ASSIGN(BlockingTaskHelper); ++}; ++ ++HidConnectionFreeBSD::HidConnectionFreeBSD( ++ scoped_refptr device_info, ++ base::ScopedFD fd, ++ scoped_refptr blocking_task_runner) ++ : HidConnection(device_info), ++ blocking_task_runner_(std::move(blocking_task_runner)), ++ weak_factory_(this) { ++ helper_ = base::MakeUnique(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 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 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 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 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 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 Added: head/www/chromium/files/patch-device_hid_hid__connection__freebsd.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/www/chromium/files/patch-device_hid_hid__connection__freebsd.h Mon Dec 4 05:44:24 2017 (r455495) @@ -0,0 +1,79 @@ +--- device/hid/hid_connection_freebsd.h.orig 2017-12-03 15:37:32.155357000 -0800 ++++ device/hid/hid_connection_freebsd.h 2017-12-03 15:37:32.159062000 -0800 +@@ -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 ++#include ++ ++#include ++ ++#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 device_info, ++ base::ScopedFD fd, ++ scoped_refptr blocking_task_runner); ++ ++ private: ++ friend class base::RefCountedThreadSafe; ++ class BlockingTaskHelper; ++ ++ ~HidConnectionFreeBSD() override; ++ ++ // HidConnection implementation. ++ void PlatformClose() override; ++ void PlatformRead(const ReadCallback& callback) override; ++ void PlatformWrite(scoped_refptr buffer, ++ size_t size, ++ const WriteCallback& callback) override; ++ void PlatformGetFeatureReport(uint8_t report_id, ++ const ReadCallback& callback) override; ++ void PlatformSendFeatureReport(scoped_refptr buffer, ++ size_t size, ++ const WriteCallback& callback) override; ++ void ProcessInputReport(scoped_refptr 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 helper_; ++ ++ const scoped_refptr blocking_task_runner_; ++ ++ std::queue pending_reports_; ++ std::queue pending_reads_; ++ const scoped_refptr task_runner_; ++ ++ SEQUENCE_CHECKER(sequence_checker_); ++ ++ base::WeakPtrFactory weak_factory_; ++ ++ DISALLOW_COPY_AND_ASSIGN(HidConnectionFreeBSD); ++}; ++ ++} // namespace device ++ ++#endif // DEVICE_HID_HID_CONNECTION_FREEBSD_H_ Added: head/www/chromium/files/patch-device_hid_hid__device__info__freebsd.cc ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/www/chromium/files/patch-device_hid_hid__device__info__freebsd.cc Mon Dec 4 05:44:24 2017 (r455495) @@ -0,0 +1,33 @@ +--- device/hid/hid_device_info_freebsd.cc.orig 2017-12-03 15:37:32.159794000 -0800 ++++ device/hid/hid_device_info_freebsd.cc 2017-12-03 15:37:32.162092000 -0800 +@@ -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 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 Added: head/www/chromium/files/patch-device_hid_hid__device__info__freebsd.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/www/chromium/files/patch-device_hid_hid__device__info__freebsd.h Mon Dec 4 05:44:24 2017 (r455495) @@ -0,0 +1,38 @@ +--- device/hid/hid_device_info_freebsd.h.orig 2017-12-03 15:37:32.162846000 -0800 ++++ device/hid/hid_device_info_freebsd.h 2017-12-03 15:37:32.165247000 -0800 +@@ -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 ++ ++#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 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_ Added: head/www/chromium/files/patch-device_hid_hid__service.cc ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/www/chromium/files/patch-device_hid_hid__service.cc Mon Dec 4 05:44:24 2017 (r455495) @@ -0,0 +1,20 @@ +--- device/hid/hid_service.cc.orig 2017-09-21 15:04:58.000000000 -0700 ++++ device/hid/hid_service.cc 2017-12-03 15:37:32.167748000 -0800 +@@ -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 @@ + std::unique_ptr 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) Added: head/www/chromium/files/patch-device_hid_hid__service__freebsd.cc ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/www/chromium/files/patch-device_hid_hid__service__freebsd.cc Mon Dec 4 05:44:24 2017 (r455495) @@ -0,0 +1,373 @@ +--- device/hid/hid_service_freebsd.cc.orig 2017-12-03 15:37:32.168519000 -0800 ++++ device/hid/hid_service_freebsd.cc 2017-12-03 15:37:32.179514000 -0800 +@@ -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 ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#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 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 device_info; ++ ConnectCallback callback; ++ scoped_refptr task_runner; ++ scoped_refptr blocking_task_runner; ++ base::ScopedFD fd; ++}; ++ ++class HidServiceFreeBSD::BlockingTaskHelper { ++ public: ++ BlockingTaskHelper(base::WeakPtr 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 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 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::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 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(device_name, kMaxPermissionChecks)); ++ timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(1), ++ this, &BlockingTaskHelper::CheckPendingPermissionChange); ++ } ++ } ++ } ++ } ++ ++ if (base::StartsWith(data, "-uhid", base::CompareCase::SENSITIVE)) { ++ std::vector 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 service_; ++ scoped_refptr task_runner_; ++ std::unique_ptr file_watcher_; ++ std::unique_ptr timer_; ++ base::ScopedFD devd_fd_; ++ scoped_refptr devd_buffer_; ++ std::map 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(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 params) { ++ base::ThreadRestrictions::AssertIOAllowed(); ++ scoped_refptr 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 device_info = ++ static_cast(map_entry->second.get()); ++ ++ auto params = base::MakeUnique(device_info, callback); ++ ++ scoped_refptr 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 params) { ++ base::ThreadRestrictions::AssertIOAllowed(); ++ scoped_refptr task_runner = params->task_runner; ++ ++ task_runner->PostTask( ++ FROM_HERE, ++ base::Bind(&HidServiceFreeBSD::CreateConnection, base::Passed(¶ms))); ++} ++ ++// static ++void HidServiceFreeBSD::CreateConnection(std::unique_ptr params) { ++ DCHECK(params->fd.is_valid()); ++ params->callback.Run(base::MakeRefCounted( ++ std::move(params->device_info), std::move(params->fd), ++ std::move(params->blocking_task_runner))); ++} ++ ++} // namespace device Added: head/www/chromium/files/patch-device_hid_hid__service__freebsd.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/www/chromium/files/patch-device_hid_hid__service__freebsd.h Mon Dec 4 05:44:24 2017 (r455495) @@ -0,0 +1,50 @@ +--- device/hid/hid_service_freebsd.h.orig 2017-12-03 15:37:32.180261000 -0800 ++++ device/hid/hid_service_freebsd.h 2017-12-03 15:37:32.183647000 -0800 +@@ -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 ++ ++#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 params); ++ static void FinishOpen(std::unique_ptr params); ++ static void CreateConnection(std::unique_ptr params); ++ ++ const scoped_refptr task_runner_; ++ const scoped_refptr 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 helper_; ++ base::WeakPtrFactory weak_factory_; ++ ++ DISALLOW_COPY_AND_ASSIGN(HidServiceFreeBSD); ++}; ++ ++} // namespace device ++ ++#endif // DEVICE_HID_HID_SERVICE_FREEBSD_H_