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"
8 #include "base/callback.h"
9 #include "base/mac/foundation_util.h"
10 #include "base/threading/thread_restrictions.h"
11 #include "base/tuple.h"
12 #include "device/hid/hid_service.h"
13 #include "device/hid/hid_service_mac.h"
14 #include "net/base/io_buffer.h"
16 #include <CoreFoundation/CoreFoundation.h>
17 #include <IOKit/hid/IOHIDManager.h>
21 HidConnectionMac::HidConnectionMac(HidServiceMac
* service
,
22 HidDeviceInfo device_info
,
23 IOHIDDeviceRef device
)
24 : HidConnection(device_info
),
27 disconnected_(false) {
28 DCHECK(thread_checker_
.CalledOnValidThread());
30 message_loop_
= base::MessageLoopProxy::current();
33 inbound_buffer_
.reset((uint8_t*) malloc(device_info
.input_report_size
+ 1));
34 IOHIDDeviceRegisterInputReportCallback(
36 inbound_buffer_
.get(),
37 device_info
.input_report_size
+ 1,
38 &HidConnectionMac::InputReportCallback
,
40 IOHIDDeviceOpen(device_
, kIOHIDOptionsTypeNone
);
42 HidConnectionMac::~HidConnectionMac() {
43 DCHECK(thread_checker_
.CalledOnValidThread());
45 while (read_queue_
.size()) {
46 read_queue_
.front().c
.Run(false, 0);
50 IOHIDDeviceClose(device_
, kIOHIDOptionsTypeNone
);
53 void HidConnectionMac::InputReportCallback(void * context
,
59 CFIndex reportLength
) {
60 HidConnectionMac
* connection
= reinterpret_cast<HidConnectionMac
*>(context
);
61 size_t length
= reportLength
+ (reportID
!= 0);
62 scoped_refptr
<net::IOBuffer
> buffer(new net::IOBuffer(length
));
64 buffer
->data()[0] = reportID
;
65 memcpy(buffer
->data() + 1, report
, reportLength
);
67 memcpy(buffer
->data(), report
, reportLength
);
69 connection
->message_loop_
->PostTask(
71 base::Bind(&HidConnectionMac::ProcessInputReport
,
78 void HidConnectionMac::ProcessReadQueue() {
79 DCHECK(thread_checker_
.CalledOnValidThread());
81 while(read_queue_
.size() && input_reports_
.size()) {
82 PendingRead read
= read_queue_
.front();
84 PendingReport report
= input_reports_
.front();
86 if (read
.b
< report
.second
) {
87 read
.c
.Run(false, report
.second
);
89 memcpy(read
.a
->data(), report
.first
->data(), report
.second
);
91 read
.c
.Run(true, report
.second
);
96 void HidConnectionMac::ProcessInputReport(IOHIDReportType type
,
97 scoped_refptr
<net::IOBuffer
> report
,
98 CFIndex reportLength
) {
99 DCHECK(thread_checker_
.CalledOnValidThread());
101 input_reports_
.push(std::make_pair(report
, reportLength
));
105 void HidConnectionMac::WriteReport(IOHIDReportType type
,
106 scoped_refptr
<net::IOBuffer
> buffer
,
108 const IOCallback
& callback
) {
109 DCHECK(thread_checker_
.CalledOnValidThread());
110 if (disconnected_
|| !device_
) {
111 callback
.Run(false, 0);
114 const unsigned char* data_to_send
=
115 reinterpret_cast<const unsigned char*>(buffer
->data());
116 size_t length_to_send
= size
;
117 if (data_to_send
[0] == 0x0) {
118 /* Not using numbered Reports.
119 Don't send the report number. */
123 IOReturn res
= IOHIDDeviceSetReport(device_
.get(),
125 buffer
->data()[0], /* Report ID*/
128 if (res
!= kIOReturnSuccess
) {
129 callback
.Run(false, 0);
131 callback
.Run(true, size
);
135 void HidConnectionMac::Read(scoped_refptr
<net::IOBuffer
> buffer
,
137 const IOCallback
& callback
) {
138 DCHECK(thread_checker_
.CalledOnValidThread());
139 if (disconnected_
|| !device_
) {
140 callback
.Run(false, 0);
143 read_queue_
.push(MakeTuple(buffer
, size
, callback
));
147 void HidConnectionMac::Write(scoped_refptr
<net::IOBuffer
> buffer
,
149 const IOCallback
& callback
) {
150 DCHECK(thread_checker_
.CalledOnValidThread());
151 WriteReport(kIOHIDReportTypeOutput
, buffer
, size
, callback
);
154 void HidConnectionMac::SendFeatureReport(scoped_refptr
<net::IOBuffer
> buffer
,
156 const IOCallback
& callback
) {
157 DCHECK(thread_checker_
.CalledOnValidThread());
158 WriteReport(kIOHIDReportTypeFeature
, buffer
, size
, callback
);
161 void HidConnectionMac::GetFeatureReport(scoped_refptr
<net::IOBuffer
> buffer
,
163 const IOCallback
& callback
) {
164 DCHECK(thread_checker_
.CalledOnValidThread());
165 if (disconnected_
|| !device_
|| device_info_
.feature_report_size
== 0) {
166 callback
.Run(false, 0);
170 if (device_info_
.feature_report_size
!= 0 &&
171 device_info_
.feature_report_size
!= size
) {
172 callback
.Run(false, 0);
176 CFIndex len
= device_info_
.feature_report_size
;
177 IOReturn res
= IOHIDDeviceGetReport(device_
,
178 kIOHIDReportTypeFeature
,
180 (uint8_t*) buffer
->data(),
182 if (res
== kIOReturnSuccess
)
183 callback
.Run(true, len
);
185 callback
.Run(false, 0);
188 } // namespace device