Upstream version 10.38.208.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 "device/hid/hid_connection_mac.h"
11
12 namespace device {
13
14 HidConnectionMac::HidConnectionMac(HidDeviceInfo device_info)
15     : HidConnection(device_info),
16       device_(device_info.device_id, base::scoped_policy::RETAIN) {
17   message_loop_ = base::MessageLoopProxy::current();
18
19   DCHECK(device_.get());
20
21   size_t expected_report_size = device_info.max_input_report_size;
22   if (device_info.has_report_id) {
23     expected_report_size++;
24   }
25   inbound_buffer_.resize(expected_report_size);
26   if (inbound_buffer_.size() > 0) {
27     IOHIDDeviceRegisterInputReportCallback(
28         device_.get(),
29         &inbound_buffer_[0],
30         inbound_buffer_.size(),
31         &HidConnectionMac::InputReportCallback,
32         this);
33   }
34 }
35
36 HidConnectionMac::~HidConnectionMac() {
37   if (inbound_buffer_.size() > 0) {
38     // Unregister the input report callback before this object is freed.
39     IOHIDDeviceRegisterInputReportCallback(
40         device_.get(), &inbound_buffer_[0], inbound_buffer_.size(), NULL, this);
41   }
42   Flush();
43 }
44
45 void HidConnectionMac::PlatformRead(scoped_refptr<net::IOBufferWithSize> buffer,
46                                     const IOCallback& callback) {
47   if (!device_) {
48     callback.Run(false, 0);
49     return;
50   }
51
52   PendingHidRead pending_read;
53   pending_read.buffer = buffer;
54   pending_read.callback = callback;
55   pending_reads_.push(pending_read);
56   ProcessReadQueue();
57 }
58
59 void HidConnectionMac::PlatformWrite(
60     uint8_t report_id,
61     scoped_refptr<net::IOBufferWithSize> buffer,
62     const IOCallback& callback) {
63   WriteReport(kIOHIDReportTypeOutput, report_id, buffer, callback);
64 }
65
66 void HidConnectionMac::PlatformGetFeatureReport(
67     uint8_t report_id,
68     scoped_refptr<net::IOBufferWithSize> buffer,
69     const IOCallback& callback) {
70   if (!device_) {
71     callback.Run(false, 0);
72     return;
73   }
74
75   uint8_t* feature_report_buffer = reinterpret_cast<uint8_t*>(buffer->data());
76   CFIndex report_size = buffer->size();
77   IOReturn result = IOHIDDeviceGetReport(device_,
78                                          kIOHIDReportTypeFeature,
79                                          report_id,
80                                          feature_report_buffer,
81                                          &report_size);
82   if (result == kIOReturnSuccess)
83     callback.Run(true, report_size);
84   else {
85     VLOG(1) << "Failed to get feature report: " << result;
86     callback.Run(false, 0);
87   }
88 }
89
90 void HidConnectionMac::PlatformSendFeatureReport(
91     uint8_t report_id,
92     scoped_refptr<net::IOBufferWithSize> buffer,
93     const IOCallback& callback) {
94   WriteReport(kIOHIDReportTypeFeature, report_id, buffer, callback);
95 }
96
97 void HidConnectionMac::InputReportCallback(void* context,
98                                            IOReturn result,
99                                            void* sender,
100                                            IOHIDReportType type,
101                                            uint32_t report_id,
102                                            uint8_t* report_bytes,
103                                            CFIndex report_length) {
104   if (result != kIOReturnSuccess) {
105     VLOG(1) << "Failed to read input report: " << result;
106     return;
107   }
108
109   HidConnectionMac* connection = static_cast<HidConnectionMac*>(context);
110   // report_id is already contained in report_bytes
111   scoped_refptr<net::IOBufferWithSize> buffer;
112   buffer = new net::IOBufferWithSize(report_length);
113   memcpy(buffer->data(), report_bytes, report_length);
114
115   connection->message_loop_->PostTask(
116       FROM_HERE,
117       base::Bind(&HidConnectionMac::ProcessInputReport, connection, buffer));
118 }
119
120 void HidConnectionMac::WriteReport(IOHIDReportType type,
121                                    uint8_t report_id,
122                                    scoped_refptr<net::IOBufferWithSize> buffer,
123                                    const IOCallback& callback) {
124   if (!device_) {
125     callback.Run(false, 0);
126     return;
127   }
128
129   scoped_refptr<net::IOBufferWithSize> output_buffer;
130   if (report_id != 0) {
131     output_buffer = new net::IOBufferWithSize(buffer->size() + 1);
132     output_buffer->data()[0] = static_cast<uint8_t>(report_id);
133     memcpy(output_buffer->data() + 1, buffer->data(), buffer->size());
134   } else {
135     output_buffer = new net::IOBufferWithSize(buffer->size());
136     memcpy(output_buffer->data(), buffer->data(), buffer->size());
137   }
138   IOReturn res =
139       IOHIDDeviceSetReport(device_.get(),
140                            type,
141                            report_id,
142                            reinterpret_cast<uint8_t*>(output_buffer->data()),
143                            output_buffer->size());
144   if (res != kIOReturnSuccess) {
145     callback.Run(false, 0);
146   } else {
147     VLOG(1) << "Failed to set report: " << res;
148     callback.Run(true, output_buffer->size());
149   }
150 }
151
152 void HidConnectionMac::Flush() {
153   while (!pending_reads_.empty()) {
154     pending_reads_.front().callback.Run(false, 0);
155     pending_reads_.pop();
156   }
157 }
158
159 void HidConnectionMac::ProcessInputReport(
160     scoped_refptr<net::IOBufferWithSize> buffer) {
161   DCHECK(thread_checker().CalledOnValidThread());
162   PendingHidReport report;
163   report.buffer = buffer;
164   pending_reports_.push(report);
165   ProcessReadQueue();
166 }
167
168 void HidConnectionMac::ProcessReadQueue() {
169   DCHECK(thread_checker().CalledOnValidThread());
170   while (pending_reads_.size() && pending_reports_.size()) {
171     PendingHidRead read = pending_reads_.front();
172     PendingHidReport report = pending_reports_.front();
173
174     if (read.buffer->size() < report.buffer->size()) {
175       read.callback.Run(false, 0);
176       pending_reads_.pop();
177     } else {
178       memcpy(read.buffer->data(), report.buffer->data(), report.buffer->size());
179       pending_reports_.pop();
180
181       if (CompleteRead(read.buffer, report.buffer->size(), read.callback)) {
182         pending_reads_.pop();
183       }
184     }
185   }
186 }
187
188 }  // namespace device