Use bucket parameter in GetIfChanged for support binaries.
[chromium-blink-merge.git] / device / hid / hid_connection_mac.cc
blob5f8ef9776d4dad9d8b3ae7c3ee754348f2159a3e
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.
5 #include "device/hid/hid_connection_mac.h"
7 #include "base/bind.h"
8 #include "base/location.h"
9 #include "base/mac/foundation_util.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "device/hid/hid_connection_mac.h"
15 namespace device {
17 HidConnectionMac::HidConnectionMac(
18 IOHIDDeviceRef device,
19 HidDeviceInfo device_info,
20 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner)
21 : HidConnection(device_info),
22 device_(device, base::scoped_policy::RETAIN),
23 file_task_runner_(file_task_runner) {
24 task_runner_ = base::ThreadTaskRunnerHandle::Get();
25 DCHECK(task_runner_.get());
27 IOHIDDeviceScheduleWithRunLoop(
28 device_.get(), CFRunLoopGetMain(), kCFRunLoopDefaultMode);
30 size_t expected_report_size = device_info.max_input_report_size;
31 if (device_info.has_report_id) {
32 expected_report_size++;
34 inbound_buffer_.resize(expected_report_size);
36 if (inbound_buffer_.size() > 0) {
37 AddRef(); // Hold a reference to this while this callback is registered.
38 IOHIDDeviceRegisterInputReportCallback(
39 device_.get(),
40 &inbound_buffer_[0],
41 inbound_buffer_.size(),
42 &HidConnectionMac::InputReportCallback,
43 this);
47 HidConnectionMac::~HidConnectionMac() {
50 void HidConnectionMac::PlatformClose() {
51 if (inbound_buffer_.size() > 0) {
52 IOHIDDeviceRegisterInputReportCallback(
53 device_.get(), &inbound_buffer_[0], inbound_buffer_.size(), NULL, this);
54 // Release the reference taken when this callback was registered.
55 Release();
58 IOHIDDeviceUnscheduleFromRunLoop(
59 device_.get(), CFRunLoopGetMain(), kCFRunLoopDefaultMode);
60 IOReturn result = IOHIDDeviceClose(device_.get(), 0);
61 if (result != kIOReturnSuccess) {
62 VLOG(1) << "Failed to close HID device: "
63 << base::StringPrintf("0x%04x", result);
66 while (!pending_reads_.empty()) {
67 pending_reads_.front().callback.Run(false, NULL, 0);
68 pending_reads_.pop();
72 void HidConnectionMac::PlatformRead(const ReadCallback& callback) {
73 DCHECK(thread_checker().CalledOnValidThread());
74 PendingHidRead pending_read;
75 pending_read.callback = callback;
76 pending_reads_.push(pending_read);
77 ProcessReadQueue();
80 void HidConnectionMac::PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
81 size_t size,
82 const WriteCallback& callback) {
83 file_task_runner_->PostTask(FROM_HERE,
84 base::Bind(&HidConnectionMac::SetReportAsync,
85 this,
86 kIOHIDReportTypeOutput,
87 buffer,
88 size,
89 callback));
92 void HidConnectionMac::PlatformGetFeatureReport(uint8_t report_id,
93 const ReadCallback& callback) {
94 file_task_runner_->PostTask(
95 FROM_HERE,
96 base::Bind(
97 &HidConnectionMac::GetFeatureReportAsync, this, report_id, callback));
100 void HidConnectionMac::PlatformSendFeatureReport(
101 scoped_refptr<net::IOBuffer> buffer,
102 size_t size,
103 const WriteCallback& callback) {
104 file_task_runner_->PostTask(FROM_HERE,
105 base::Bind(&HidConnectionMac::SetReportAsync,
106 this,
107 kIOHIDReportTypeFeature,
108 buffer,
109 size,
110 callback));
113 // static
114 void HidConnectionMac::InputReportCallback(void* context,
115 IOReturn result,
116 void* sender,
117 IOHIDReportType type,
118 uint32_t report_id,
119 uint8_t* report_bytes,
120 CFIndex report_length) {
121 HidConnectionMac* connection = static_cast<HidConnectionMac*>(context);
122 if (result != kIOReturnSuccess) {
123 VLOG(1) << "Failed to read input report: "
124 << base::StringPrintf("0x%08x", result);
125 return;
128 scoped_refptr<net::IOBufferWithSize> buffer;
129 if (connection->device_info().has_report_id) {
130 // report_id is already contained in report_bytes
131 buffer = new net::IOBufferWithSize(report_length);
132 memcpy(buffer->data(), report_bytes, report_length);
133 } else {
134 buffer = new net::IOBufferWithSize(report_length + 1);
135 buffer->data()[0] = 0;
136 memcpy(buffer->data() + 1, report_bytes, report_length);
139 connection->ProcessInputReport(buffer);
142 void HidConnectionMac::ProcessInputReport(
143 scoped_refptr<net::IOBufferWithSize> buffer) {
144 DCHECK(thread_checker().CalledOnValidThread());
145 PendingHidReport report;
146 report.buffer = buffer;
147 report.size = buffer->size();
148 pending_reports_.push(report);
149 ProcessReadQueue();
152 void HidConnectionMac::ProcessReadQueue() {
153 DCHECK(thread_checker().CalledOnValidThread());
154 while (pending_reads_.size() && pending_reports_.size()) {
155 PendingHidRead read = pending_reads_.front();
156 PendingHidReport report = pending_reports_.front();
158 pending_reports_.pop();
159 if (CompleteRead(report.buffer, report.size, read.callback)) {
160 pending_reads_.pop();
165 void HidConnectionMac::GetFeatureReportAsync(uint8_t report_id,
166 const ReadCallback& callback) {
167 scoped_refptr<net::IOBufferWithSize> buffer(
168 new net::IOBufferWithSize(device_info().max_feature_report_size + 1));
169 CFIndex report_size = buffer->size();
171 // The IOHIDDevice object is shared with the UI thread and so this function
172 // should probably be called there but it may block and the asynchronous
173 // version is NOT IMPLEMENTED. I've examined the open source implementation
174 // of this function and believe it is a simple enough wrapper around the
175 // kernel API that this is safe.
176 IOReturn result =
177 IOHIDDeviceGetReport(device_.get(),
178 kIOHIDReportTypeFeature,
179 report_id,
180 reinterpret_cast<uint8_t*>(buffer->data()),
181 &report_size);
182 if (result == kIOReturnSuccess) {
183 task_runner_->PostTask(
184 FROM_HERE,
185 base::Bind(&HidConnectionMac::ReturnAsyncResult,
186 this,
187 base::Bind(callback, true, buffer, report_size)));
188 } else {
189 VLOG(1) << "Failed to get feature report: "
190 << base::StringPrintf("0x%08x", result);
191 task_runner_->PostTask(FROM_HERE,
192 base::Bind(&HidConnectionMac::ReturnAsyncResult,
193 this,
194 base::Bind(callback, false, nullptr, 0)));
198 void HidConnectionMac::SetReportAsync(IOHIDReportType report_type,
199 scoped_refptr<net::IOBuffer> buffer,
200 size_t size,
201 const WriteCallback& callback) {
202 uint8_t* data = reinterpret_cast<uint8_t*>(buffer->data());
203 DCHECK_GE(size, 1u);
204 uint8_t report_id = data[0];
205 if (report_id == 0) {
206 // OS X only expects the first byte of the buffer to be the report ID if the
207 // report ID is non-zero.
208 ++data;
209 --size;
212 // The IOHIDDevice object is shared with the UI thread and so this function
213 // should probably be called there but it may block and the asynchronous
214 // version is NOT IMPLEMENTED. I've examined the open source implementation
215 // of this function and believe it is a simple enough wrapper around the
216 // kernel API that this is safe.
217 IOReturn result =
218 IOHIDDeviceSetReport(device_.get(), report_type, report_id, data, size);
219 if (result == kIOReturnSuccess) {
220 task_runner_->PostTask(FROM_HERE,
221 base::Bind(&HidConnectionMac::ReturnAsyncResult,
222 this,
223 base::Bind(callback, true)));
224 } else {
225 VLOG(1) << "Failed to set report: " << base::StringPrintf("0x%08x", result);
226 task_runner_->PostTask(FROM_HERE,
227 base::Bind(&HidConnectionMac::ReturnAsyncResult,
228 this,
229 base::Bind(callback, false)));
233 void HidConnectionMac::ReturnAsyncResult(const base::Closure& callback) {
234 callback.Run();
237 } // namespace device