ae1a14261df13ac440cc3aa2188dd2e27bc104d1
[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(const ReadCallback& callback) {
46   if (!device_) {
47     callback.Run(false, NULL, 0);
48     return;
49   }
50
51   PendingHidRead pending_read;
52   pending_read.callback = callback;
53   pending_reads_.push(pending_read);
54   ProcessReadQueue();
55 }
56
57 void HidConnectionMac::PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
58                                      size_t size,
59                                      const WriteCallback& callback) {
60   WriteReport(kIOHIDReportTypeOutput, buffer, size, callback);
61 }
62
63 void HidConnectionMac::PlatformGetFeatureReport(uint8_t report_id,
64                                                 const ReadCallback& callback) {
65   if (!device_) {
66     callback.Run(false, NULL, 0);
67     return;
68   }
69
70   scoped_refptr<net::IOBufferWithSize> buffer(
71       new net::IOBufferWithSize(device_info().max_feature_report_size));
72   CFIndex report_size = buffer->size();
73   IOReturn result =
74       IOHIDDeviceGetReport(device_,
75                            kIOHIDReportTypeFeature,
76                            report_id,
77                            reinterpret_cast<uint8_t*>(buffer->data()),
78                            &report_size);
79   if (result == kIOReturnSuccess) {
80     callback.Run(true, buffer, report_size);
81   } else {
82     VLOG(1) << "Failed to get feature report: " << result;
83     callback.Run(false, NULL, 0);
84   }
85 }
86
87 void HidConnectionMac::PlatformSendFeatureReport(
88     scoped_refptr<net::IOBuffer> buffer,
89     size_t size,
90     const WriteCallback& callback) {
91   WriteReport(kIOHIDReportTypeFeature, buffer, size, callback);
92 }
93
94 void HidConnectionMac::InputReportCallback(void* context,
95                                            IOReturn result,
96                                            void* sender,
97                                            IOHIDReportType type,
98                                            uint32_t report_id,
99                                            uint8_t* report_bytes,
100                                            CFIndex report_length) {
101   if (result != kIOReturnSuccess) {
102     VLOG(1) << "Failed to read input report: " << result;
103     return;
104   }
105
106   HidConnectionMac* connection = static_cast<HidConnectionMac*>(context);
107   scoped_refptr<net::IOBufferWithSize> buffer;
108   if (connection->device_info().has_report_id) {
109     // report_id is already contained in report_bytes
110     buffer = new net::IOBufferWithSize(report_length);
111     memcpy(buffer->data(), report_bytes, report_length);
112   } else {
113     buffer = new net::IOBufferWithSize(report_length + 1);
114     buffer->data()[0] = 0;
115     memcpy(buffer->data() + 1, report_bytes, report_length);
116   }
117
118   connection->message_loop_->PostTask(
119       FROM_HERE,
120       base::Bind(&HidConnectionMac::ProcessInputReport, connection, buffer));
121 }
122
123 void HidConnectionMac::WriteReport(IOHIDReportType type,
124                                    scoped_refptr<net::IOBuffer> buffer,
125                                    size_t size,
126                                    const WriteCallback& callback) {
127   if (!device_) {
128     callback.Run(false);
129     return;
130   }
131
132   uint8_t* data = reinterpret_cast<uint8_t*>(buffer->data());
133   DCHECK(size >= 1);
134   uint8_t report_id = data[0];
135   if (report_id == 0) {
136     // OS X only expects the first byte of the buffer to be the report ID if the
137     // report ID is non-zero.
138     ++data;
139     --size;
140   }
141
142   IOReturn res =
143       IOHIDDeviceSetReport(device_.get(), type, report_id, data, size);
144   if (res == kIOReturnSuccess) {
145     callback.Run(true);
146   } else {
147     VLOG(1) << "Failed to set report: " << res;
148     callback.Run(false);
149   }
150 }
151
152 void HidConnectionMac::Flush() {
153   while (!pending_reads_.empty()) {
154     pending_reads_.front().callback.Run(false, NULL, 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   report.size = buffer->size();
165   pending_reports_.push(report);
166   ProcessReadQueue();
167 }
168
169 void HidConnectionMac::ProcessReadQueue() {
170   DCHECK(thread_checker().CalledOnValidThread());
171   while (pending_reads_.size() && pending_reports_.size()) {
172     PendingHidRead read = pending_reads_.front();
173     PendingHidReport report = pending_reports_.front();
174
175     pending_reports_.pop();
176     if (CompleteRead(report.buffer, report.size, read.callback)) {
177       pending_reads_.pop();
178     }
179   }
180 }
181
182 }  // namespace device