| OLD | NEW |
| 1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "device/hid/hid_connection_mac.h" | 5 #include "device/hid/hid_connection_mac.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/mac/foundation_util.h" | 8 #include "base/mac/foundation_util.h" |
| 9 #include "base/message_loop/message_loop.h" | 9 #include "base/message_loop/message_loop.h" |
| 10 #include "device/hid/hid_connection_mac.h" | 10 #include "device/hid/hid_connection_mac.h" |
| 11 | 11 |
| 12 namespace device { | 12 namespace device { |
| 13 | 13 |
| 14 HidConnectionMac::HidConnectionMac(HidDeviceInfo device_info) | 14 HidConnectionMac::HidConnectionMac(HidDeviceInfo device_info) |
| 15 : HidConnection(device_info), | 15 : HidConnection(device_info), |
| 16 device_(device_info.device_id, base::scoped_policy::RETAIN) { | 16 device_(device_info.device_id, base::scoped_policy::RETAIN) { |
| 17 message_loop_ = base::MessageLoopProxy::current(); | 17 message_loop_ = base::MessageLoopProxy::current(); |
| 18 | 18 |
| 19 DCHECK(device_.get()); | 19 DCHECK(device_.get()); |
| 20 size_t expected_report_size = device_info.max_input_report_size; | 20 size_t expected_report_size = device_info.max_input_report_size; |
| 21 if (device_info.has_report_id) { | 21 if (device_info.has_report_id) { |
| 22 expected_report_size++; | 22 expected_report_size++; |
| 23 } | 23 } |
| 24 inbound_buffer_.reset((uint8_t*)malloc(expected_report_size)); | 24 inbound_buffer_.reset(new uint8_t[expected_report_size]); |
| 25 IOHIDDeviceRegisterInputReportCallback(device_.get(), | 25 IOHIDDeviceRegisterInputReportCallback(device_.get(), |
| 26 inbound_buffer_.get(), | 26 inbound_buffer_.get(), |
| 27 expected_report_size, | 27 expected_report_size, |
| 28 &HidConnectionMac::InputReportCallback, | 28 &HidConnectionMac::InputReportCallback, |
| 29 this); | 29 this); |
| 30 IOHIDDeviceOpen(device_, kIOHIDOptionsTypeNone); | 30 IOHIDDeviceOpen(device_, kIOHIDOptionsTypeNone); |
| 31 } | 31 } |
| 32 | 32 |
| 33 HidConnectionMac::~HidConnectionMac() { | 33 HidConnectionMac::~HidConnectionMac() { |
| 34 IOHIDDeviceClose(device_, kIOHIDOptionsTypeNone); | 34 IOHIDDeviceClose(device_, kIOHIDOptionsTypeNone); |
| 35 Flush(); | 35 Flush(); |
| 36 } | 36 } |
| 37 | 37 |
| 38 void HidConnectionMac::PlatformRead(scoped_refptr<net::IOBufferWithSize> buffer, | 38 void HidConnectionMac::PlatformRead(const ReadCallback& callback) { |
| 39 const IOCallback& callback) { | |
| 40 if (!device_) { | 39 if (!device_) { |
| 41 callback.Run(false, 0); | 40 callback.Run(false, NULL, 0); |
| 42 return; | 41 return; |
| 43 } | 42 } |
| 44 | 43 |
| 45 PendingHidRead pending_read; | 44 PendingHidRead pending_read; |
| 46 pending_read.buffer = buffer; | |
| 47 pending_read.callback = callback; | 45 pending_read.callback = callback; |
| 48 pending_reads_.push(pending_read); | 46 pending_reads_.push(pending_read); |
| 49 ProcessReadQueue(); | 47 ProcessReadQueue(); |
| 50 } | 48 } |
| 51 | 49 |
| 52 void HidConnectionMac::PlatformWrite( | 50 void HidConnectionMac::PlatformWrite(scoped_refptr<net::IOBuffer> buffer, |
| 53 uint8_t report_id, | 51 size_t size, |
| 54 scoped_refptr<net::IOBufferWithSize> buffer, | 52 const WriteCallback& callback) { |
| 55 const IOCallback& callback) { | 53 WriteReport(kIOHIDReportTypeOutput, buffer, size, callback); |
| 56 WriteReport(kIOHIDReportTypeOutput, report_id, buffer, callback); | |
| 57 } | 54 } |
| 58 | 55 |
| 59 void HidConnectionMac::PlatformGetFeatureReport( | 56 void HidConnectionMac::PlatformGetFeatureReport(uint8_t report_id, |
| 60 uint8_t report_id, | 57 const ReadCallback& callback) { |
| 61 scoped_refptr<net::IOBufferWithSize> buffer, | |
| 62 const IOCallback& callback) { | |
| 63 if (!device_) { | 58 if (!device_) { |
| 64 callback.Run(false, 0); | 59 callback.Run(false, NULL, 0); |
| 65 return; | 60 return; |
| 66 } | 61 } |
| 67 | 62 |
| 68 uint8_t* feature_report_buffer = reinterpret_cast<uint8_t*>(buffer->data()); | 63 scoped_refptr<net::IOBufferWithSize> buffer( |
| 64 new net::IOBufferWithSize(device_info().max_feature_report_size)); |
| 69 CFIndex report_size = buffer->size(); | 65 CFIndex report_size = buffer->size(); |
| 70 IOReturn result = IOHIDDeviceGetReport(device_, | 66 IOReturn result = |
| 71 kIOHIDReportTypeFeature, | 67 IOHIDDeviceGetReport(device_, |
| 72 report_id, | 68 kIOHIDReportTypeFeature, |
| 73 feature_report_buffer, | 69 report_id, |
| 74 &report_size); | 70 reinterpret_cast<uint8_t*>(buffer->data()), |
| 75 if (result == kIOReturnSuccess) | 71 &report_size); |
| 76 callback.Run(true, report_size); | 72 if (result == kIOReturnSuccess) { |
| 77 else | 73 callback.Run(true, buffer, report_size); |
| 78 callback.Run(false, 0); | 74 } else { |
| 75 callback.Run(false, NULL, 0); |
| 76 } |
| 79 } | 77 } |
| 80 | 78 |
| 81 void HidConnectionMac::PlatformSendFeatureReport( | 79 void HidConnectionMac::PlatformSendFeatureReport( |
| 82 uint8_t report_id, | 80 scoped_refptr<net::IOBuffer> buffer, |
| 83 scoped_refptr<net::IOBufferWithSize> buffer, | 81 size_t size, |
| 84 const IOCallback& callback) { | 82 const WriteCallback& callback) { |
| 85 WriteReport(kIOHIDReportTypeFeature, report_id, buffer, callback); | 83 WriteReport(kIOHIDReportTypeFeature, buffer, size, callback); |
| 86 } | 84 } |
| 87 | 85 |
| 88 void HidConnectionMac::InputReportCallback(void* context, | 86 void HidConnectionMac::InputReportCallback(void* context, |
| 89 IOReturn result, | 87 IOReturn result, |
| 90 void* sender, | 88 void* sender, |
| 91 IOHIDReportType type, | 89 IOHIDReportType type, |
| 92 uint32_t report_id, | 90 uint32_t report_id, |
| 93 uint8_t* report_bytes, | 91 uint8_t* report_bytes, |
| 94 CFIndex report_length) { | 92 CFIndex report_length) { |
| 95 HidConnectionMac* connection = static_cast<HidConnectionMac*>(context); | 93 HidConnectionMac* connection = static_cast<HidConnectionMac*>(context); |
| 96 // report_id is already contained in report_bytes | |
| 97 scoped_refptr<net::IOBufferWithSize> buffer; | 94 scoped_refptr<net::IOBufferWithSize> buffer; |
| 98 buffer = new net::IOBufferWithSize(report_length); | 95 if (connection->device_info().has_report_id) { |
| 99 memcpy(buffer->data(), report_bytes, report_length); | 96 // report_id is already contained in report_bytes |
| 97 buffer = new net::IOBufferWithSize(report_length); |
| 98 memcpy(buffer->data(), report_bytes, report_length); |
| 99 } else { |
| 100 buffer = new net::IOBufferWithSize(report_length + 1); |
| 101 buffer->data()[0] = 0; |
| 102 memcpy(buffer->data() + 1, report_bytes, report_length); |
| 103 } |
| 100 | 104 |
| 101 connection->message_loop_->PostTask( | 105 connection->message_loop_->PostTask( |
| 102 FROM_HERE, | 106 FROM_HERE, |
| 103 base::Bind(&HidConnectionMac::ProcessInputReport, connection, buffer)); | 107 base::Bind(&HidConnectionMac::ProcessInputReport, connection, buffer)); |
| 104 } | 108 } |
| 105 | 109 |
| 106 void HidConnectionMac::WriteReport(IOHIDReportType type, | 110 void HidConnectionMac::WriteReport(IOHIDReportType type, |
| 107 uint8_t report_id, | 111 scoped_refptr<net::IOBuffer> buffer, |
| 108 scoped_refptr<net::IOBufferWithSize> buffer, | 112 size_t size, |
| 109 const IOCallback& callback) { | 113 const WriteCallback& callback) { |
| 110 if (!device_) { | 114 if (!device_) { |
| 111 callback.Run(false, 0); | 115 callback.Run(false); |
| 112 return; | 116 return; |
| 113 } | 117 } |
| 114 | 118 |
| 115 scoped_refptr<net::IOBufferWithSize> output_buffer; | 119 uint8_t* data = reinterpret_cast<uint8_t*>(buffer->data()); |
| 116 if (report_id != 0) { | 120 DCHECK(size >= 1); |
| 117 output_buffer = new net::IOBufferWithSize(buffer->size() + 1); | 121 uint8_t report_id = data[0]; |
| 118 output_buffer->data()[0] = static_cast<uint8_t>(report_id); | 122 if (report_id == 0) { |
| 119 memcpy(output_buffer->data() + 1, buffer->data(), buffer->size()); | 123 // OS X only expects the first byte of the buffer to be the report ID if the |
| 124 // report ID is non-zero. |
| 125 ++data; |
| 126 --size; |
| 127 } |
| 128 |
| 129 IOReturn res = |
| 130 IOHIDDeviceSetReport(device_.get(), type, report_id, data, size); |
| 131 if (res == kIOReturnSuccess) { |
| 132 callback.Run(true); |
| 120 } else { | 133 } else { |
| 121 output_buffer = new net::IOBufferWithSize(buffer->size()); | 134 callback.Run(false); |
| 122 memcpy(output_buffer->data(), buffer->data(), buffer->size()); | |
| 123 } | |
| 124 IOReturn res = | |
| 125 IOHIDDeviceSetReport(device_.get(), | |
| 126 type, | |
| 127 report_id, | |
| 128 reinterpret_cast<uint8_t*>(output_buffer->data()), | |
| 129 output_buffer->size()); | |
| 130 if (res != kIOReturnSuccess) { | |
| 131 callback.Run(false, 0); | |
| 132 } else { | |
| 133 callback.Run(true, output_buffer->size()); | |
| 134 } | 135 } |
| 135 } | 136 } |
| 136 | 137 |
| 137 void HidConnectionMac::Flush() { | 138 void HidConnectionMac::Flush() { |
| 138 while (!pending_reads_.empty()) { | 139 while (!pending_reads_.empty()) { |
| 139 pending_reads_.front().callback.Run(false, 0); | 140 pending_reads_.front().callback.Run(false, NULL, 0); |
| 140 pending_reads_.pop(); | 141 pending_reads_.pop(); |
| 141 } | 142 } |
| 142 } | 143 } |
| 143 | 144 |
| 144 void HidConnectionMac::ProcessInputReport( | 145 void HidConnectionMac::ProcessInputReport( |
| 145 scoped_refptr<net::IOBufferWithSize> buffer) { | 146 scoped_refptr<net::IOBufferWithSize> buffer) { |
| 146 DCHECK(thread_checker().CalledOnValidThread()); | 147 DCHECK(thread_checker().CalledOnValidThread()); |
| 147 PendingHidReport report; | 148 PendingHidReport report; |
| 148 report.buffer = buffer; | 149 report.buffer = buffer; |
| 150 report.size = buffer->size(); |
| 149 pending_reports_.push(report); | 151 pending_reports_.push(report); |
| 150 ProcessReadQueue(); | 152 ProcessReadQueue(); |
| 151 } | 153 } |
| 152 | 154 |
| 153 void HidConnectionMac::ProcessReadQueue() { | 155 void HidConnectionMac::ProcessReadQueue() { |
| 154 DCHECK(thread_checker().CalledOnValidThread()); | 156 DCHECK(thread_checker().CalledOnValidThread()); |
| 155 while (pending_reads_.size() && pending_reports_.size()) { | 157 while (pending_reads_.size() && pending_reports_.size()) { |
| 156 PendingHidRead read = pending_reads_.front(); | 158 PendingHidRead read = pending_reads_.front(); |
| 157 PendingHidReport report = pending_reports_.front(); | 159 PendingHidReport report = pending_reports_.front(); |
| 158 | 160 |
| 159 if (read.buffer->size() < report.buffer->size()) { | 161 pending_reports_.pop(); |
| 160 read.callback.Run(false, 0); | 162 if (CompleteRead(report.buffer, report.size, read.callback)) { |
| 161 pending_reads_.pop(); | 163 pending_reads_.pop(); |
| 162 } else { | |
| 163 memcpy(read.buffer->data(), report.buffer->data(), report.buffer->size()); | |
| 164 pending_reports_.pop(); | |
| 165 | |
| 166 if (CompleteRead(read.buffer, report.buffer->size(), read.callback)) { | |
| 167 pending_reads_.pop(); | |
| 168 } | |
| 169 } | 164 } |
| 170 } | 165 } |
| 171 } | 166 } |
| 172 | 167 |
| 173 } // namespace device | 168 } // namespace device |
| OLD | NEW |