Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / device / hid / hid_connection_mac.cc
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_mac.h"
6
7 #include "base/bind.h"
8 #include "base/mac/foundation_util.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/threading/thread_restrictions.h"
11 #include "device/hid/hid_connection_mac.h"
12
13 namespace device {
14
15 HidConnectionMac::HidConnectionMac(HidDeviceInfo device_info)
16     : HidConnection(device_info),
17       device_(device_info.device_id, base::scoped_policy::RETAIN) {
18   DCHECK(thread_checker_.CalledOnValidThread());
19
20   message_loop_ = base::MessageLoopProxy::current();
21
22   DCHECK(device_.get());
23   inbound_buffer_.reset((uint8_t*)malloc(device_info.input_report_size));
24   IOHIDDeviceRegisterInputReportCallback(device_.get(),
25                                          inbound_buffer_.get(),
26                                          device_info.input_report_size,
27                                          &HidConnectionMac::InputReportCallback,
28                                          this);
29   IOHIDDeviceOpen(device_, kIOHIDOptionsTypeNone);
30 }
31
32 HidConnectionMac::~HidConnectionMac() {
33   DCHECK(thread_checker_.CalledOnValidThread());
34
35   while (!pending_reads_.empty()) {
36     pending_reads_.front().callback.Run(false, 0);
37     pending_reads_.pop();
38   }
39
40   IOHIDDeviceClose(device_, kIOHIDOptionsTypeNone);
41 }
42
43 void HidConnectionMac::Read(scoped_refptr<net::IOBufferWithSize> buffer,
44                             const IOCallback& callback) {
45   DCHECK(thread_checker_.CalledOnValidThread());
46   if (!device_) {
47     callback.Run(false, 0);
48     return;
49   }
50   PendingHidRead read;
51   read.buffer = buffer;
52   read.callback = callback;
53   pending_reads_.push(read);
54   ProcessReadQueue();
55 }
56
57 void HidConnectionMac::Write(uint8_t report_id,
58                              scoped_refptr<net::IOBufferWithSize> buffer,
59                              const IOCallback& callback) {
60   DCHECK(thread_checker_.CalledOnValidThread());
61   WriteReport(kIOHIDReportTypeOutput, report_id, buffer, callback);
62 }
63
64 void HidConnectionMac::GetFeatureReport(
65     uint8_t report_id,
66     scoped_refptr<net::IOBufferWithSize> buffer,
67     const IOCallback& callback) {
68   DCHECK(thread_checker_.CalledOnValidThread());
69   if (device_info().feature_report_size == 0) {
70     callback.Run(false, 0);
71     return;
72   }
73
74   if (buffer->size() < device_info().feature_report_size) {
75     callback.Run(false, 0);
76     return;
77   }
78
79   uint8_t* feature_report_buffer = reinterpret_cast<uint8_t*>(buffer->data());
80   CFIndex feature_report_size = device_info().feature_report_size;
81   IOReturn result = IOHIDDeviceGetReport(device_,
82                                          kIOHIDReportTypeFeature,
83                                          report_id,
84                                          feature_report_buffer,
85                                          &feature_report_size);
86   if (result == kIOReturnSuccess)
87     callback.Run(true, feature_report_size);
88   else
89     callback.Run(false, 0);
90 }
91
92 void HidConnectionMac::SendFeatureReport(
93     uint8_t report_id,
94     scoped_refptr<net::IOBufferWithSize> buffer,
95     const IOCallback& callback) {
96   DCHECK(thread_checker_.CalledOnValidThread());
97   WriteReport(kIOHIDReportTypeFeature, report_id, buffer, callback);
98 }
99
100 void HidConnectionMac::InputReportCallback(void* context,
101                                            IOReturn result,
102                                            void* sender,
103                                            IOHIDReportType type,
104                                            uint32_t report_id,
105                                            uint8_t* report_bytes,
106                                            CFIndex report_length) {
107   HidConnectionMac* connection = static_cast<HidConnectionMac*>(context);
108   // report_id is already contained in report_bytes
109   scoped_refptr<net::IOBufferWithSize> buffer;
110   buffer = new net::IOBufferWithSize(report_length);
111   memcpy(buffer->data(), report_bytes, report_length);
112
113   connection->message_loop_->PostTask(
114       FROM_HERE,
115       base::Bind(
116           &HidConnectionMac::ProcessInputReport, connection, type, buffer));
117 }
118
119 void HidConnectionMac::ProcessReadQueue() {
120   DCHECK(thread_checker_.CalledOnValidThread());
121   while (pending_reads_.size() && pending_reports_.size()) {
122     PendingHidRead read = pending_reads_.front();
123     pending_reads_.pop();
124     PendingHidReport report = pending_reports_.front();
125     if (read.buffer->size() < report.buffer->size()) {
126       read.callback.Run(false, report.buffer->size());
127     } else {
128       memcpy(read.buffer->data(), report.buffer->data(), report.buffer->size());
129       pending_reports_.pop();
130       read.callback.Run(true, report.buffer->size());
131     }
132   }
133 }
134
135 void HidConnectionMac::ProcessInputReport(
136     IOHIDReportType type,
137     scoped_refptr<net::IOBufferWithSize> buffer) {
138   DCHECK(thread_checker_.CalledOnValidThread());
139   PendingHidReport report;
140   report.buffer = buffer;
141   pending_reports_.push(report);
142   ProcessReadQueue();
143 }
144
145 void HidConnectionMac::WriteReport(IOHIDReportType type,
146                                    uint8_t report_id,
147                                    scoped_refptr<net::IOBufferWithSize> buffer,
148                                    const IOCallback& callback) {
149   DCHECK(thread_checker_.CalledOnValidThread());
150   if (!device_) {
151     callback.Run(false, 0);
152     return;
153   }
154   scoped_refptr<net::IOBufferWithSize> output_buffer;
155   if (report_id != 0) {
156     output_buffer = new net::IOBufferWithSize(buffer->size() + 1);
157     output_buffer->data()[0] = static_cast<uint8_t>(report_id);
158     memcpy(output_buffer->data() + 1, buffer->data(), buffer->size());
159   } else {
160     output_buffer = new net::IOBufferWithSize(buffer->size());
161     memcpy(output_buffer->data(), buffer->data(), buffer->size());
162   }
163   IOReturn res =
164       IOHIDDeviceSetReport(device_.get(),
165                            type,
166                            report_id,
167                            reinterpret_cast<uint8_t*>(output_buffer->data()),
168                            output_buffer->size());
169   if (res != kIOReturnSuccess) {
170     callback.Run(false, 0);
171   } else {
172     callback.Run(true, output_buffer->size());
173   }
174 }
175
176 }  // namespace device