svn commit: r455495 - in head/www/chromium: . files

Oleksandr Tymoshenko gonzo at FreeBSD.org
Mon Dec 4 05:44:26 UTC 2017


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 <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

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 <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_

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<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

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 <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_

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> 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 <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(&params)));
++}
++
++// 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(&params)));
++}
++
++// 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

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 <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_


More information about the svn-ports-head mailing list