Add ICU message format support
[chromium-blink-merge.git] / device / hid / hid_connection_mac.cc
blob57f56af4a390bbf5de46bd9126bcd4f1e7983eed
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/numerics/safe_math.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "components/device_event_log/device_event_log.h"
15 #include "device/hid/hid_connection_mac.h"
17 namespace device {
19 namespace {
21 std::string HexErrorCode(IOReturn error_code) {
22 return base::StringPrintf("0x%04x", error_code);
25 } // namespace
27 HidConnectionMac::HidConnectionMac(
28 IOHIDDeviceRef device,
29 scoped_refptr<HidDeviceInfo> device_info,
30 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner)
31 : HidConnection(device_info),
32 device_(device, base::scoped_policy::RETAIN),
33 file_task_runner_(file_task_runner) {
34 task_runner_ = base::ThreadTaskRunnerHandle::Get();
35 DCHECK(task_runner_.get());
37 IOHIDDeviceScheduleWithRunLoop(
38 device_.get(), CFRunLoopGetMain(), kCFRunLoopDefaultMode);
40 size_t expected_report_size = device_info->max_input_report_size();
41 if (device_info->has_report_id()) {
42 expected_report_size++;
44 inbound_buffer_.resize(expected_report_size);
46 if (inbound_buffer_.size() > 0) {
47 AddRef(); // Hold a reference to this while this callback is registered.
48 IOHIDDeviceRegisterInputReportCallback(
49 device_.get(),
50 &inbound_buffer_[0],
51 inbound_buffer_.size(),
52 &HidConnectionMac::InputReportCallback,
53 this);
57 HidConnectionMac::~HidConnectionMac() {
60 void HidConnectionMac::PlatformClose() {
61 if (inbound_buffer_.size() > 0) {
62 IOHIDDeviceRegisterInputReportCallback(
63 device_.get(), &inbound_buffer_[0], inbound_buffer_.size(), NULL, this);
64 // Release the reference taken when this callback was registered.
65 Release();
68 IOHIDDeviceUnscheduleFromRunLoop(
69 device_.get(), CFRunLoopGetMain(), kCFRunLoopDefaultMode);
70 IOReturn result = IOHIDDeviceClose(device_.get(), 0);
71 if (result != kIOReturnSuccess) {
72 HID_LOG(EVENT) << "Failed to close HID device: " << HexErrorCode(result);
75 while (!pending_reads_.empty()) {
76 pending_reads_.front().callback.Run(false, NULL, 0);
77 pending_reads_.pop();
81 void HidConnectionMac::PlatformRead(const ReadCallback& callback) {
82 DCHECK(thread_checker().CalledOnValidThread());
83 PendingHidRead pending_read;
84 pending_read.callback = callback;
85 pending_reads_.push(pending_read);
86 ProcessReadQueue();
89 void HidConnectionMac::PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
90 size_t size,
91 const WriteCallback& callback) {
92 file_task_runner_->PostTask(FROM_HERE,
93 base::Bind(&HidConnectionMac::SetReportAsync,
94 this,
95 kIOHIDReportTypeOutput,
96 buffer,
97 size,
98 callback));
101 void HidConnectionMac::PlatformGetFeatureReport(uint8_t report_id,
102 const ReadCallback& callback) {
103 file_task_runner_->PostTask(
104 FROM_HERE,
105 base::Bind(
106 &HidConnectionMac::GetFeatureReportAsync, this, report_id, callback));
109 void HidConnectionMac::PlatformSendFeatureReport(
110 scoped_refptr<net::IOBuffer> buffer,
111 size_t size,
112 const WriteCallback& callback) {
113 file_task_runner_->PostTask(FROM_HERE,
114 base::Bind(&HidConnectionMac::SetReportAsync,
115 this,
116 kIOHIDReportTypeFeature,
117 buffer,
118 size,
119 callback));
122 // static
123 void HidConnectionMac::InputReportCallback(void* context,
124 IOReturn result,
125 void* sender,
126 IOHIDReportType type,
127 uint32_t report_id,
128 uint8_t* report_bytes,
129 CFIndex report_length) {
130 HidConnectionMac* connection = static_cast<HidConnectionMac*>(context);
131 if (result != kIOReturnSuccess) {
132 HID_LOG(EVENT) << "Failed to read input report: " << HexErrorCode(result);
133 return;
136 scoped_refptr<net::IOBufferWithSize> buffer;
137 if (connection->device_info()->has_report_id()) {
138 // report_id is already contained in report_bytes
139 buffer = new net::IOBufferWithSize(
140 base::CheckedNumeric<size_t>(report_length).ValueOrDie());
141 memcpy(buffer->data(), report_bytes, report_length);
142 } else {
143 buffer = new net::IOBufferWithSize(
144 (base::CheckedNumeric<size_t>(report_length) + 1).ValueOrDie());
145 buffer->data()[0] = 0;
146 memcpy(buffer->data() + 1, report_bytes, report_length);
149 connection->ProcessInputReport(buffer);
152 void HidConnectionMac::ProcessInputReport(
153 scoped_refptr<net::IOBufferWithSize> buffer) {
154 DCHECK(thread_checker().CalledOnValidThread());
155 PendingHidReport report;
156 report.buffer = buffer;
157 report.size = buffer->size();
158 pending_reports_.push(report);
159 ProcessReadQueue();
162 void HidConnectionMac::ProcessReadQueue() {
163 DCHECK(thread_checker().CalledOnValidThread());
164 while (pending_reads_.size() && pending_reports_.size()) {
165 PendingHidRead read = pending_reads_.front();
166 PendingHidReport report = pending_reports_.front();
168 pending_reports_.pop();
169 if (CompleteRead(report.buffer, report.size, read.callback)) {
170 pending_reads_.pop();
175 void HidConnectionMac::GetFeatureReportAsync(uint8_t report_id,
176 const ReadCallback& callback) {
177 scoped_refptr<net::IOBufferWithSize> buffer(
178 new net::IOBufferWithSize(device_info()->max_feature_report_size() + 1));
179 CFIndex report_size = buffer->size();
181 // The IOHIDDevice object is shared with the UI thread and so this function
182 // should probably be called there but it may block and the asynchronous
183 // version is NOT IMPLEMENTED. I've examined the open source implementation
184 // of this function and believe it is a simple enough wrapper around the
185 // kernel API that this is safe.
186 IOReturn result =
187 IOHIDDeviceGetReport(device_.get(),
188 kIOHIDReportTypeFeature,
189 report_id,
190 reinterpret_cast<uint8_t*>(buffer->data()),
191 &report_size);
192 if (result == kIOReturnSuccess) {
193 task_runner_->PostTask(
194 FROM_HERE,
195 base::Bind(&HidConnectionMac::ReturnAsyncResult,
196 this,
197 base::Bind(callback, true, buffer, report_size)));
198 } else {
199 HID_LOG(EVENT) << "Failed to get feature report: " << HexErrorCode(result);
200 task_runner_->PostTask(FROM_HERE,
201 base::Bind(&HidConnectionMac::ReturnAsyncResult,
202 this,
203 base::Bind(callback, false, nullptr, 0)));
207 void HidConnectionMac::SetReportAsync(IOHIDReportType report_type,
208 scoped_refptr<net::IOBuffer> buffer,
209 size_t size,
210 const WriteCallback& callback) {
211 uint8_t* data = reinterpret_cast<uint8_t*>(buffer->data());
212 DCHECK_GE(size, 1u);
213 uint8_t report_id = data[0];
214 if (report_id == 0) {
215 // OS X only expects the first byte of the buffer to be the report ID if the
216 // report ID is non-zero.
217 ++data;
218 --size;
221 // The IOHIDDevice object is shared with the UI thread and so this function
222 // should probably be called there but it may block and the asynchronous
223 // version is NOT IMPLEMENTED. I've examined the open source implementation
224 // of this function and believe it is a simple enough wrapper around the
225 // kernel API that this is safe.
226 IOReturn result =
227 IOHIDDeviceSetReport(device_.get(), report_type, report_id, data, size);
228 if (result == kIOReturnSuccess) {
229 task_runner_->PostTask(FROM_HERE,
230 base::Bind(&HidConnectionMac::ReturnAsyncResult,
231 this,
232 base::Bind(callback, true)));
233 } else {
234 HID_LOG(EVENT) << "Failed to set report: " << HexErrorCode(result);
235 task_runner_->PostTask(FROM_HERE,
236 base::Bind(&HidConnectionMac::ReturnAsyncResult,
237 this,
238 base::Bind(callback, false)));
242 void HidConnectionMac::ReturnAsyncResult(const base::Closure& callback) {
243 callback.Run();
246 } // namespace device