View | Details | Raw Unified | Return to bug 196754 | Differences between
and this patch

Collapse All | Expand All

(-)b/device/hid/BUILD.gn (+15 lines)
Lines 56-61 source_set("hid") { Link Here
56
    deps += [ "//device/udev_linux" ]
56
    deps += [ "//device/udev_linux" ]
57
  }
57
  }
58
58
59
  if (is_bsd) {
60
    sources -= [
61
      "hid_connection_linux.cc",
62
      "hid_connection_linux.h",
63
    ]
64
    sources += [
65
      "hid_connection_freebsd.cc",
66
      "hid_connection_freebsd.h",
67
      "hid_device_info_freebsd.cc",
68
      "hid_device_info_freebsd.h",
69
      "hid_service_freebsd.cc",
70
      "hid_service_freebsd.h",
71
    ]
72
  }
73
59
  if (is_chromeos) {
74
  if (is_chromeos) {
60
    deps += [ "//chromeos" ]
75
    deps += [ "//chromeos" ]
61
  }
76
  }
(-)b/device/hid/hid_connection_freebsd.cc (+278 lines)
Added Link Here
1
// Copyright (c) 2014 The Chromium Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
#include "device/hid/hid_connection_freebsd.h"
6
7
#include <dev/usb/usbhid.h>
8
#include <dev/usb/usb_ioctl.h>
9
10
#include "base/bind.h"
11
#include "base/files/file_descriptor_watcher_posix.h"
12
#include "base/location.h"
13
#include "base/numerics/safe_math.h"
14
#include "base/posix/eintr_wrapper.h"
15
#include "base/single_thread_task_runner.h"
16
#include "base/strings/stringprintf.h"
17
#include "base/task_scheduler/post_task.h"
18
#include "base/threading/thread_restrictions.h"
19
#include "base/threading/thread_task_runner_handle.h"
20
#include "components/device_event_log/device_event_log.h"
21
#include "device/hid/hid_service.h"
22
23
namespace device {
24
25
class HidConnectionFreeBSD::BlockingTaskHelper {
26
 public:
27
  BlockingTaskHelper(base::ScopedFD fd,
28
                     scoped_refptr<HidDeviceInfo> device_info,
29
                     base::WeakPtr<HidConnectionFreeBSD> connection)
30
      : fd_(std::move(fd)),
31
        connection_(connection),
32
        origin_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
33
    DETACH_FROM_SEQUENCE(sequence_checker_);
34
    // Report buffers must always have room for the report ID.
35
    report_buffer_size_ = device_info->max_input_report_size() + 1;
36
    has_report_id_ = device_info->has_report_id();
37
  }
38
39
  ~BlockingTaskHelper() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); }
40
41
  // Starts the FileDescriptorWatcher that reads input events from the device.
42
  // Must be called on a thread that has a base::MessageLoopForIO.
43
  void Start() {
44
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
45
    base::ThreadRestrictions::AssertIOAllowed();
46
47
    file_watcher_ = base::FileDescriptorWatcher::WatchReadable(
48
        fd_.get(), base::Bind(&BlockingTaskHelper::OnFileCanReadWithoutBlocking,
49
                              base::Unretained(this)));
50
  }
51
52
  void Write(scoped_refptr<net::IOBuffer> buffer,
53
             size_t size,
54
             const WriteCallback& callback) {
55
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
56
    char *data = buffer->data();
57
    // if report id is 0, it shouldn't be included
58
    if (data[0] == 0) {
59
      data++;
60
      size--;
61
    }
62
63
    ssize_t result = HANDLE_EINTR(write(fd_.get(), data, size));
64
    if (result < 0) {
65
      HID_PLOG(EVENT) << "Write failed";
66
      origin_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
67
    } else {
68
      if (static_cast<size_t>(result) != size)
69
        HID_LOG(EVENT) << "Incomplete HID write: " << result << " != " << size;
70
      origin_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
71
    }
72
  }
73
74
  void GetFeatureReport(uint8_t report_id,
75
                        scoped_refptr<net::IOBufferWithSize> buffer,
76
                        const ReadCallback& callback) {
77
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
78
    struct usb_gen_descriptor ugd;
79
    ugd.ugd_report_type = UHID_FEATURE_REPORT;  
80
    ugd.ugd_data = buffer->data();
81
    ugd.ugd_maxlen = buffer->size();
82
    int result = HANDLE_EINTR(
83
        ioctl(fd_.get(), USB_GET_REPORT, &ugd));
84
    if (result < 0) {
85
      HID_PLOG(EVENT) << "Failed to get feature report";
86
      origin_task_runner_->PostTask(FROM_HERE,
87
                                    base::Bind(callback, false, nullptr, 0));
88
    } else if (result == 0) {
89
      HID_LOG(EVENT) << "Get feature result too short.";
90
      origin_task_runner_->PostTask(FROM_HERE,
91
                                    base::Bind(callback, false, nullptr, 0));
92
    } else {
93
      origin_task_runner_->PostTask(FROM_HERE,
94
                                    base::Bind(callback, true, buffer, result));
95
    }
96
  }
97
98
  void SendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
99
                         size_t size,
100
                         const WriteCallback& callback) {
101
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
102
    struct usb_gen_descriptor ugd;
103
    ugd.ugd_report_type = UHID_FEATURE_REPORT;  
104
    ugd.ugd_data = buffer->data();
105
    ugd.ugd_maxlen = size;
106
    // FreeBSD does not require report id if it's not used
107
    if (buffer->data()[0] == 0) {
108
      ugd.ugd_data = buffer->data() + 1;
109
      ugd.ugd_maxlen = size - 1;
110
    } else {
111
      ugd.ugd_data = buffer->data();
112
      ugd.ugd_maxlen = size;
113
    }
114
    int result = HANDLE_EINTR(
115
        ioctl(fd_.get(), USB_SET_REPORT, &ugd));
116
    if (result < 0) {
117
      HID_PLOG(EVENT) << "Failed to send feature report";
118
      origin_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
119
    } else {
120
      origin_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
121
    }
122
  }
123
124
 private:
125
  void OnFileCanReadWithoutBlocking() {
126
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
127
128
    scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(report_buffer_size_));
129
    char* data = buffer->data();
130
    size_t length = report_buffer_size_;
131
    if (!has_report_id_) {
132
      // FreeBSD will not prefix the buffer with a report ID if report IDs are not
133
      // used by the device. Prefix the buffer with 0.
134
      *data++ = 0;
135
      length--;
136
    }
137
138
    ssize_t bytes_read = HANDLE_EINTR(read(fd_.get(), data, length));
139
    if (bytes_read < 0) {
140
      if (errno != EAGAIN) {
141
        HID_PLOG(EVENT) << "Read failed";
142
        // This assumes that the error is unrecoverable and disables reading
143
        // from the device until it has been re-opened.
144
        // TODO(reillyg): Investigate starting and stopping the file descriptor
145
        // watcher in response to pending read requests so that per-request
146
        // errors can be returned to the client.
147
        file_watcher_.reset();
148
      }
149
      return;
150
    }
151
    if (!has_report_id_) {
152
      // Behave as if the byte prefixed above as the the report ID was read.
153
      bytes_read++;
154
    }
155
156
    origin_task_runner_->PostTask(
157
        FROM_HERE, base::Bind(&HidConnectionFreeBSD::ProcessInputReport,
158
                              connection_, buffer, bytes_read));
159
  }
160
161
  SEQUENCE_CHECKER(sequence_checker_);
162
  base::ScopedFD fd_;
163
  size_t report_buffer_size_;
164
  bool has_report_id_;
165
  base::WeakPtr<HidConnectionFreeBSD> connection_;
166
  const scoped_refptr<base::SequencedTaskRunner> origin_task_runner_;
167
  std::unique_ptr<base::FileDescriptorWatcher::Controller> file_watcher_;
168
169
  DISALLOW_COPY_AND_ASSIGN(BlockingTaskHelper);
170
};
171
172
HidConnectionFreeBSD::HidConnectionFreeBSD(
173
    scoped_refptr<HidDeviceInfo> device_info,
174
    base::ScopedFD fd,
175
    scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
176
    : HidConnection(device_info),
177
      blocking_task_runner_(std::move(blocking_task_runner)),
178
      weak_factory_(this) {
179
  helper_ = base::MakeUnique<BlockingTaskHelper>(std::move(fd), device_info,
180
                                                 weak_factory_.GetWeakPtr());
181
  blocking_task_runner_->PostTask(
182
      FROM_HERE,
183
      base::Bind(&BlockingTaskHelper::Start, base::Unretained(helper_.get())));
184
}
185
186
HidConnectionFreeBSD::~HidConnectionFreeBSD() {}
187
188
void HidConnectionFreeBSD::PlatformClose() {
189
  // By closing the device on the blocking task runner 1) the requirement that
190
  // base::ScopedFD is destroyed on a thread where I/O is allowed is satisfied
191
  // and 2) any tasks posted to this task runner that refer to this file will
192
  // complete before it is closed.
193
  blocking_task_runner_->DeleteSoon(FROM_HERE, helper_.release());
194
195
  while (!pending_reads_.empty()) {
196
    pending_reads_.front().callback.Run(false, NULL, 0);
197
    pending_reads_.pop();
198
  }
199
}
200
201
void HidConnectionFreeBSD::PlatformRead(const ReadCallback& callback) {
202
  DCHECK(thread_checker().CalledOnValidThread());
203
  PendingHidRead pending_read;
204
  pending_read.callback = callback;
205
  pending_reads_.push(pending_read);
206
  ProcessReadQueue();
207
}
208
209
void HidConnectionFreeBSD::PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
210
                                     size_t size,
211
                                     const WriteCallback& callback) {
212
213
  blocking_task_runner_->PostTask(
214
      FROM_HERE,
215
      base::Bind(&BlockingTaskHelper::Write, base::Unretained(helper_.get()),
216
                 buffer, size, callback));
217
}
218
219
void HidConnectionFreeBSD::PlatformGetFeatureReport(uint8_t report_id,
220
                                                const ReadCallback& callback) {
221
  // The first byte of the destination buffer is the report ID being requested
222
  // and is overwritten by the feature report.
223
  DCHECK_GT(device_info()->max_feature_report_size(), 0u);
224
  scoped_refptr<net::IOBufferWithSize> buffer(
225
      new net::IOBufferWithSize(device_info()->max_feature_report_size() + 1));
226
  if (report_id != 0)
227
    buffer->data()[0] = report_id;
228
229
  blocking_task_runner_->PostTask(
230
      FROM_HERE,
231
      base::Bind(&BlockingTaskHelper::GetFeatureReport,
232
                 base::Unretained(helper_.get()), report_id, buffer, callback));
233
}
234
235
void HidConnectionFreeBSD::PlatformSendFeatureReport(
236
    scoped_refptr<net::IOBuffer> buffer,
237
    size_t size,
238
    const WriteCallback& callback) {
239
  blocking_task_runner_->PostTask(
240
      FROM_HERE,
241
      base::Bind(&BlockingTaskHelper::SendFeatureReport,
242
                 base::Unretained(helper_.get()), buffer, size, callback));
243
}
244
245
void HidConnectionFreeBSD::ProcessInputReport(
246
    scoped_refptr<net::IOBuffer> buffer,
247
    size_t size) {
248
  DCHECK(thread_checker().CalledOnValidThread());
249
  DCHECK_GE(size, 1u);
250
251
  uint8_t report_id = buffer->data()[0];
252
  if (IsReportIdProtected(report_id))
253
    return;
254
255
  PendingHidReport report;
256
  report.buffer = buffer;
257
  report.size = size;
258
  pending_reports_.push(report);
259
  ProcessReadQueue();
260
}
261
262
void HidConnectionFreeBSD::ProcessReadQueue() {
263
  DCHECK(thread_checker().CalledOnValidThread());
264
265
  // Hold a reference to |this| to prevent a callback from freeing this object
266
  // during the loop.
267
  scoped_refptr<HidConnectionFreeBSD> self(this);
268
  while (pending_reads_.size() && pending_reports_.size()) {
269
    PendingHidRead read = pending_reads_.front();
270
    PendingHidReport report = pending_reports_.front();
271
272
    pending_reads_.pop();
273
    pending_reports_.pop();
274
    read.callback.Run(true, report.buffer, report.size);
275
  }
276
}
277
278
}  // namespace device
(-)b/device/hid/hid_connection_freebsd.h (+76 lines)
Added Link Here
1
// Copyright (c) 2014 The Chromium Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
#ifndef DEVICE_HID_HID_CONNECTION_FREEBSD_H_
6
#define DEVICE_HID_HID_CONNECTION_FREEBSD_H_
7
8
#include <stddef.h>
9
#include <stdint.h>
10
11
#include <queue>
12
13
#include "base/files/scoped_file.h"
14
#include "base/macros.h"
15
#include "base/memory/weak_ptr.h"
16
#include "base/sequence_checker.h"
17
#include "device/hid/hid_connection.h"
18
19
namespace base {
20
class SequencedTaskRunner;
21
}
22
23
namespace net {
24
class IOBuffer;
25
}
26
27
namespace device {
28
29
class HidConnectionFreeBSD : public HidConnection {
30
 public:
31
  HidConnectionFreeBSD(
32
      scoped_refptr<HidDeviceInfo> device_info,
33
      base::ScopedFD fd,
34
      scoped_refptr<base::SequencedTaskRunner> blocking_task_runner);
35
36
 private:
37
  friend class base::RefCountedThreadSafe<HidConnectionFreeBSD>;
38
  class BlockingTaskHelper;
39
40
  ~HidConnectionFreeBSD() override;
41
42
  // HidConnection implementation.
43
  void PlatformClose() override;
44
  void PlatformRead(const ReadCallback& callback) override;
45
  void PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
46
                     size_t size,
47
                     const WriteCallback& callback) override;
48
  void PlatformGetFeatureReport(uint8_t report_id,
49
                                const ReadCallback& callback) override;
50
  void PlatformSendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
51
                                 size_t size,
52
                                 const WriteCallback& callback) override;
53
  void ProcessInputReport(scoped_refptr<net::IOBuffer> buffer, size_t size);
54
  void ProcessReadQueue();
55
56
  // |helper_| lives on the sequence to which |blocking_task_runner_| posts
57
  // tasks so all calls must be posted there including this object's
58
  // destruction.
59
  std::unique_ptr<BlockingTaskHelper> helper_;
60
61
  const scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
62
63
  std::queue<PendingHidReport> pending_reports_;
64
  std::queue<PendingHidRead> pending_reads_;
65
  const scoped_refptr<base::SequencedTaskRunner> task_runner_;
66
67
  SEQUENCE_CHECKER(sequence_checker_);
68
69
  base::WeakPtrFactory<HidConnectionFreeBSD> weak_factory_;
70
71
  DISALLOW_COPY_AND_ASSIGN(HidConnectionFreeBSD);
72
};
73
74
}  // namespace device
75
76
#endif  // DEVICE_HID_HID_CONNECTION_FREEBSD_H_
(-)b/device/hid/hid_device_info_freebsd.cc (+30 lines)
Added Link Here
1
// Copyright 2015 The Chromium Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
#include "hid_device_info_freebsd.h"
6
7
namespace device {
8
9
HidDeviceInfoFreeBSD::HidDeviceInfoFreeBSD(
10
    const HidDeviceId& device_id,
11
    const std::string& device_node,
12
    uint16_t vendor_id,
13
    uint16_t product_id,
14
    const std::string& product_name,
15
    const std::string& serial_number,
16
    HidBusType bus_type,
17
    const std::vector<uint8_t> report_descriptor)
18
    : HidDeviceInfo(device_id,
19
                    vendor_id,
20
                    product_id,
21
                    product_name,
22
                    serial_number,
23
                    bus_type,
24
                    report_descriptor),
25
      device_node_(device_node) {}
26
27
HidDeviceInfoFreeBSD::~HidDeviceInfoFreeBSD() {
28
}
29
30
}  // namespace device
(-)b/device/hid/hid_device_info_freebsd.h (+35 lines)
Added Link Here
1
// Copyright 2015 The Chromium Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
#ifndef DEVICE_HID_HID_DEVICE_INFO_LINUX_H_
6
#define DEVICE_HID_HID_DEVICE_INFO_LINUX_H_
7
8
#include <stdint.h>
9
10
#include "device/hid/hid_device_info.h"
11
12
namespace device {
13
14
class HidDeviceInfoFreeBSD : public HidDeviceInfo {
15
 public:
16
  HidDeviceInfoFreeBSD(const HidDeviceId& device_id,
17
                     const std::string& device_node,
18
                     uint16_t vendor_id,
19
                     uint16_t product_id,
20
                     const std::string& product_name,
21
                     const std::string& serial_number,
22
                     HidBusType bus_type,
23
                     const std::vector<uint8_t> report_descriptor);
24
25
  const std::string& device_node() const { return device_node_; }
26
27
 private:
28
  ~HidDeviceInfoFreeBSD() override;
29
30
  std::string device_node_;
31
};
32
33
}  // namespace device
34
35
#endif  // DEVICE_HID_HID_DEVICE_INFO_LINUX_H_
(-)b/device/hid/hid_service.cc (+4 lines)
Lines 16-21 Link Here
16
16
17
#if defined(OS_LINUX) && defined(USE_UDEV)
17
#if defined(OS_LINUX) && defined(USE_UDEV)
18
#include "device/hid/hid_service_linux.h"
18
#include "device/hid/hid_service_linux.h"
19
#elif defined(OS_BSD)
20
#include "device/hid/hid_service_freebsd.h"
19
#elif defined(OS_MACOSX)
21
#elif defined(OS_MACOSX)
20
#include "device/hid/hid_service_mac.h"
22
#include "device/hid/hid_service_mac.h"
21
#elif defined(OS_WIN)
23
#elif defined(OS_WIN)
Lines 42-47 constexpr base::TaskTraits HidService::kBlockingTaskTraits; Link Here
42
std::unique_ptr<HidService> HidService::Create() {
44
std::unique_ptr<HidService> HidService::Create() {
43
#if defined(OS_LINUX) && defined(USE_UDEV)
45
#if defined(OS_LINUX) && defined(USE_UDEV)
44
  return base::WrapUnique(new HidServiceLinux());
46
  return base::WrapUnique(new HidServiceLinux());
47
#elif defined(OS_BSD)
48
  return base::WrapUnique(new HidServiceFreeBSD());
45
#elif defined(OS_MACOSX)
49
#elif defined(OS_MACOSX)
46
  return base::WrapUnique(new HidServiceMac());
50
  return base::WrapUnique(new HidServiceMac());
47
#elif defined(OS_WIN)
51
#elif defined(OS_WIN)
(-)b/device/hid/hid_service_freebsd.cc (+370 lines)
Added Link Here
1
// Copyright 2014 The Chromium Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
#include "device/hid/hid_service_freebsd.h"
6
7
#include <dev/usb/usb_ioctl.h>
8
#include <stdint.h>
9
#include <sys/socket.h>
10
#include <sys/un.h>
11
12
#include <set>
13
#include <string>
14
#include <vector>
15
16
#include "base/bind.h"
17
#include "base/files/file_descriptor_watcher_posix.h"
18
#include "base/files/file_enumerator.h"
19
#include "base/location.h"
20
#include "base/logging.h"
21
#include "base/posix/eintr_wrapper.h"
22
#include "base/single_thread_task_runner.h"
23
#include "base/stl_util.h"
24
#include "base/strings/pattern.h"
25
#include "base/strings/stringprintf.h"
26
#include "base/strings/sys_string_conversions.h"
27
#include "base/strings/string_util.h"
28
#include "base/strings/string_split.h"
29
#include "base/task_scheduler/post_task.h"
30
#include "base/threading/thread_restrictions.h"
31
#include "base/threading/thread_task_runner_handle.h"
32
#include "components/device_event_log/device_event_log.h"
33
#include "device/hid/hid_connection_freebsd.h"
34
#include "device/hid/hid_device_info_freebsd.h"
35
36
const int kMaxPermissionChecks = 5;
37
38
namespace device {
39
40
struct HidServiceFreeBSD::ConnectParams {
41
  ConnectParams(scoped_refptr<HidDeviceInfoFreeBSD> device_info,
42
                const ConnectCallback& callback)
43
      : device_info(std::move(device_info)),
44
        callback(callback),
45
        task_runner(base::ThreadTaskRunnerHandle::Get()),
46
        blocking_task_runner(
47
            base::CreateSequencedTaskRunnerWithTraits(kBlockingTaskTraits)) {}
48
  ~ConnectParams() {}
49
50
  scoped_refptr<HidDeviceInfoFreeBSD> device_info;
51
  ConnectCallback callback;
52
  scoped_refptr<base::SequencedTaskRunner> task_runner;
53
  scoped_refptr<base::SequencedTaskRunner> blocking_task_runner;
54
  base::ScopedFD fd;
55
};
56
57
class HidServiceFreeBSD::BlockingTaskHelper {
58
 public:
59
  BlockingTaskHelper(base::WeakPtr<HidServiceFreeBSD> service)
60
      : service_(std::move(service)),
61
        task_runner_(base::ThreadTaskRunnerHandle::Get()) {
62
    DETACH_FROM_SEQUENCE(sequence_checker_);
63
64
    timer_.reset(new base::RepeatingTimer());
65
    devd_buffer_ = new net::IOBufferWithSize(1024);
66
  }
67
68
  ~BlockingTaskHelper() {
69
  }
70
71
  void Start() {
72
    base::ThreadRestrictions::AssertIOAllowed();
73
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
74
75
    const base::FilePath kDevRoot("/dev");
76
    const std::string kUHIDPattern("/dev/uhid*");
77
78
    base::FileEnumerator enumerator(kDevRoot, false, base::FileEnumerator::FILES);
79
    do {
80
      const base::FilePath next_device_path(enumerator.Next());
81
      const std::string next_device = next_device_path.value();
82
      if (next_device.empty())
83
        break;
84
85
      if (base::MatchPattern(next_device, kUHIDPattern))
86
        OnDeviceAdded(next_device.substr(5));
87
    } while (true);
88
89
    SetupDevdMonitor();
90
91
    task_runner_->PostTask(
92
        FROM_HERE,
93
        base::Bind(&HidServiceFreeBSD::FirstEnumerationComplete, service_));
94
  }
95
96
  bool HaveReadWritePermissions(std::string device_id) {
97
    std::string device_node = "/dev/" + device_id;
98
    base::ThreadRestrictions::AssertIOAllowed();
99
100
    base::FilePath device_path(device_node);
101
    base::File device_file;
102
    int flags =
103
        base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE;
104
    device_file.Initialize(device_path, flags);
105
    if (!device_file.IsValid())
106
      return false;
107
108
    return true;
109
  }
110
111
  void OnDeviceAdded(std::string device_id) {
112
    std::string device_node = "/dev/" + device_id;
113
    uint16_t vendor_id = 0xffff;
114
    uint16_t product_id = 0xffff;
115
    std::string product_name = "";
116
    std::string serial_number = "";
117
118
    std::vector<uint8_t> report_descriptor;
119
120
    base::ThreadRestrictions::AssertIOAllowed();
121
122
    base::FilePath device_path(device_node);
123
    base::File device_file;
124
    int flags =
125
        base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE;
126
    device_file.Initialize(device_path, flags);
127
    if (!device_file.IsValid()) {
128
      HID_LOG(ERROR) << "Failed to open '" << device_node
129
                     << "': "
130
                     << base::File::ErrorToString(device_file.error_details());
131
      return;
132
    }
133
134
    base::ScopedFD fd;
135
    fd.reset(device_file.TakePlatformFile());
136
137
    struct usb_gen_descriptor ugd;
138
    ugd.ugd_data = NULL;
139
    ugd.ugd_maxlen = 0xffff;
140
    int result = HANDLE_EINTR(
141
        ioctl(fd.get(), USB_GET_REPORT_DESC, &ugd));
142
143
    if (result < 0) {
144
      HID_LOG(ERROR) << "Failed to get report descriptor size";
145
      return;
146
    }
147
148
    report_descriptor.resize(ugd.ugd_actlen);
149
150
    ugd.ugd_data = report_descriptor.data();
151
    ugd.ugd_maxlen = ugd.ugd_actlen;
152
    result = HANDLE_EINTR(
153
        ioctl(fd.get(), USB_GET_REPORT_DESC, &ugd));
154
155
    if (result < 0) {
156
      HID_LOG(ERROR) << "Failed to get report descriptor";
157
      return;
158
    }
159
160
    scoped_refptr<HidDeviceInfoFreeBSD> device_info(new HidDeviceInfoFreeBSD(
161
        device_id, device_node, vendor_id, product_id, product_name,
162
        serial_number,
163
        kHIDBusTypeUSB,
164
        report_descriptor));
165
166
    task_runner_->PostTask(FROM_HERE, base::Bind(&HidServiceFreeBSD::AddDevice,
167
                                                 service_, device_info));
168
  }
169
170
  void OnDeviceRemoved(std::string device_id) {
171
    task_runner_->PostTask(
172
        FROM_HERE, base::Bind(&HidServiceFreeBSD::RemoveDevice, service_,
173
                              device_id));
174
  }
175
176
 private:
177
178
  void CheckPendingPermissionChange() {
179
    base::ThreadRestrictions::AssertIOAllowed();
180
    std::map<std::string, int>::iterator it;
181
    for (it = permissions_checks_attempts_.begin(); it != permissions_checks_attempts_.end();) {
182
      std::string device_name = it->first;
183
      bool keep = true;
184
      if (HaveReadWritePermissions(device_name)) {
185
        OnDeviceAdded(device_name);
186
        keep = false;
187
      }
188
      else if (it->second-- <= 0) {
189
        HID_LOG(ERROR) << "Still don't have write permissions to '" << device_name
190
                       << "' after " << kMaxPermissionChecks << " attempts";
191
        keep = false;
192
      }
193
194
      if (keep)
195
        ++it;
196
      else
197
        permissions_checks_attempts_.erase(it++);
198
    }
199
200
    if (permissions_checks_attempts_.empty())
201
      timer_->Stop();
202
  }
203
204
  void SetupDevdMonitor() {
205
    base::ThreadRestrictions::AssertIOAllowed();
206
207
    int devd_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
208
    if (devd_fd < 0)
209
      return;
210
211
    struct sockaddr_un sa;
212
213
    sa.sun_family = AF_UNIX;
214
    strlcpy(sa.sun_path, "/var/run/devd.seqpacket.pipe", sizeof(sa.sun_path));
215
    if (connect(devd_fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
216
      close(devd_fd);
217
      return;
218
    } 
219
220
    devd_fd_.reset(devd_fd);
221
    file_watcher_ = base::FileDescriptorWatcher::WatchReadable(
222
        devd_fd_.get(), base::Bind(&BlockingTaskHelper::OnDevdMessageCanBeRead,
223
                                   base::Unretained(this)));
224
  }
225
226
  void OnDevdMessageCanBeRead() {
227
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
228
    ssize_t bytes_read = HANDLE_EINTR(recv(devd_fd_.get(), devd_buffer_->data(),
229
                                      devd_buffer_->size() - 1, MSG_WAITALL));
230
    if (bytes_read < 0) {
231
      if (errno != EAGAIN) {
232
        HID_LOG(ERROR) << "Read failed";
233
        file_watcher_.reset();
234
      }
235
      return;
236
    }
237
238
    devd_buffer_->data()[bytes_read] = 0;
239
    char *data = devd_buffer_->data();
240
    // It may take some time for devd to change permissions
241
    // on /dev/uhidX node. So do not fail immediately if 
242
    // open fail. Retry each second for kMaxPermissionChecks
243
    // times before giving up entirely
244
    if (base::StartsWith(data, "+uhid", base::CompareCase::SENSITIVE)) {
245
      std::vector<std::string> parts = base::SplitString(
246
        data, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
247
      if (!parts.empty()) {
248
        std::string device_name = parts[0].substr(1); // skip '+'
249
        if (HaveReadWritePermissions(device_name))
250
          OnDeviceAdded(parts[0].substr(1));
251
        else {
252
          // Do not re-add to checks 
253
          if (permissions_checks_attempts_.find(device_name) == permissions_checks_attempts_.end()) {
254
            permissions_checks_attempts_.insert(std::pair<std::string, int>(device_name, kMaxPermissionChecks));
255
            timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(1),
256
                          this, &BlockingTaskHelper::CheckPendingPermissionChange);
257
          }
258
        }
259
      }
260
    }
261
262
    if (base::StartsWith(data, "-uhid", base::CompareCase::SENSITIVE)) {
263
      std::vector<std::string> parts = base::SplitString(
264
        data, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
265
      if (!parts.empty()) {
266
        std::string device_name = parts[0].substr(1); // skip '-'
267
        auto it = permissions_checks_attempts_.find(device_name);
268
        if (it != permissions_checks_attempts_.end()) {
269
          permissions_checks_attempts_.erase(it);
270
          if (permissions_checks_attempts_.empty())
271
            timer_->Stop();
272
        }
273
        OnDeviceRemoved(parts[0].substr(1));
274
      }
275
    }
276
  }
277
278
  SEQUENCE_CHECKER(sequence_checker_);
279
280
  // This weak pointer is only valid when checked on this task runner.
281
  base::WeakPtr<HidServiceFreeBSD> service_;
282
  scoped_refptr<base::SequencedTaskRunner> task_runner_;
283
  std::unique_ptr<base::FileDescriptorWatcher::Controller> file_watcher_;
284
  std::unique_ptr<base::RepeatingTimer> timer_;
285
  base::ScopedFD devd_fd_;
286
  scoped_refptr<net::IOBufferWithSize> devd_buffer_;
287
  std::map<std::string, int> permissions_checks_attempts_;
288
289
  DISALLOW_COPY_AND_ASSIGN(BlockingTaskHelper);
290
};
291
292
HidServiceFreeBSD::HidServiceFreeBSD()
293
    : task_runner_(base::ThreadTaskRunnerHandle::Get()),
294
      blocking_task_runner_(
295
          base::CreateSequencedTaskRunnerWithTraits(kBlockingTaskTraits)),
296
      weak_factory_(this) {
297
  helper_ = base::MakeUnique<BlockingTaskHelper>(weak_factory_.GetWeakPtr());
298
  blocking_task_runner_->PostTask(
299
      FROM_HERE,
300
      base::Bind(&BlockingTaskHelper::Start, base::Unretained(helper_.get())));
301
}
302
303
HidServiceFreeBSD::~HidServiceFreeBSD() {
304
  blocking_task_runner_->DeleteSoon(FROM_HERE, helper_.release());
305
}
306
307
// static
308
void HidServiceFreeBSD::OpenOnBlockingThread(
309
    std::unique_ptr<ConnectParams> params) {
310
  base::ThreadRestrictions::AssertIOAllowed();
311
  scoped_refptr<base::SequencedTaskRunner> task_runner = params->task_runner;
312
313
  base::FilePath device_path(params->device_info->device_node());
314
  base::File device_file;
315
  int flags =
316
      base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE;
317
  device_file.Initialize(device_path, flags);
318
  if (!device_file.IsValid()) {
319
    HID_LOG(EVENT) << "Failed to open '" << params->device_info->device_node()
320
                   << "': "
321
                   << base::File::ErrorToString(device_file.error_details());
322
    task_runner->PostTask(FROM_HERE, base::Bind(params->callback, nullptr));
323
    return;
324
  }
325
  params->fd.reset(device_file.TakePlatformFile());
326
  FinishOpen(std::move(params));
327
}
328
329
void HidServiceFreeBSD::Connect(const HidDeviceId& device_id,
330
                            const ConnectCallback& callback) {
331
  DCHECK(thread_checker_.CalledOnValidThread());
332
333
  const auto& map_entry = devices().find(device_id);
334
  if (map_entry == devices().end()) {
335
    base::ThreadTaskRunnerHandle::Get()->PostTask(
336
        FROM_HERE, base::Bind(callback, nullptr));
337
    return;
338
  }
339
340
  scoped_refptr<HidDeviceInfoFreeBSD> device_info =
341
      static_cast<HidDeviceInfoFreeBSD*>(map_entry->second.get());
342
343
  auto params = base::MakeUnique<ConnectParams>(device_info, callback);
344
345
  scoped_refptr<base::SequencedTaskRunner> blocking_task_runner =
346
      params->blocking_task_runner;
347
  blocking_task_runner->PostTask(
348
      FROM_HERE, base::Bind(&HidServiceFreeBSD::OpenOnBlockingThread,
349
                            base::Passed(&params)));
350
}
351
352
// static
353
void HidServiceFreeBSD::FinishOpen(std::unique_ptr<ConnectParams> params) {
354
  base::ThreadRestrictions::AssertIOAllowed();
355
  scoped_refptr<base::SequencedTaskRunner> task_runner = params->task_runner;
356
357
  task_runner->PostTask(
358
      FROM_HERE,
359
      base::Bind(&HidServiceFreeBSD::CreateConnection, base::Passed(&params)));
360
}
361
362
// static
363
void HidServiceFreeBSD::CreateConnection(std::unique_ptr<ConnectParams> params) {
364
  DCHECK(params->fd.is_valid());
365
  params->callback.Run(base::MakeRefCounted<HidConnectionFreeBSD>(
366
      std::move(params->device_info), std::move(params->fd),
367
      std::move(params->blocking_task_runner)));
368
}
369
370
}  // namespace device
(-)b/device/hid/hid_service_freebsd.h (+47 lines)
Added Link Here
1
// Copyright 2014 The Chromium Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
#ifndef DEVICE_HID_HID_SERVICE_FREEBSD_H_
6
#define DEVICE_HID_HID_SERVICE_FREEBSD_H_
7
8
#include <string>
9
10
#include "base/macros.h"
11
#include "base/memory/ref_counted.h"
12
#include "base/memory/weak_ptr.h"
13
#include "base/timer/timer.h"
14
#include "device/hid/hid_service.h"
15
#include "net/base/io_buffer.h"
16
17
namespace device {
18
19
class HidServiceFreeBSD : public HidService {
20
 public:
21
  HidServiceFreeBSD();
22
  ~HidServiceFreeBSD() override;
23
24
  void Connect(const HidDeviceId& device_id,
25
               const ConnectCallback& connect) override;
26
27
 private:
28
  struct ConnectParams;
29
  class BlockingTaskHelper;
30
31
  static void OpenOnBlockingThread(std::unique_ptr<ConnectParams> params);
32
  static void FinishOpen(std::unique_ptr<ConnectParams> params);
33
  static void CreateConnection(std::unique_ptr<ConnectParams> params);
34
35
  const scoped_refptr<base::SequencedTaskRunner> task_runner_;
36
  const scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
37
  // |helper_| lives on the sequence |blocking_task_runner_| posts to and holds
38
  // a weak reference back to the service that owns it.
39
  std::unique_ptr<BlockingTaskHelper> helper_;
40
  base::WeakPtrFactory<HidServiceFreeBSD> weak_factory_;
41
42
  DISALLOW_COPY_AND_ASSIGN(HidServiceFreeBSD);
43
};
44
45
}  // namespace device
46
47
#endif  // DEVICE_HID_HID_SERVICE_FREEBSD_H_

Return to bug 196754