hid_connection_mac.cc 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  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. #include "services/device/hid/hid_connection_mac.h"
  5. #include "base/bind.h"
  6. #include "base/location.h"
  7. #include "base/mac/foundation_util.h"
  8. #include "base/memory/ref_counted_memory.h"
  9. #include "base/numerics/safe_math.h"
  10. #include "base/strings/stringprintf.h"
  11. #include "base/task/single_thread_task_runner.h"
  12. #include "base/task/thread_pool.h"
  13. #include "base/threading/thread_task_runner_handle.h"
  14. #include "components/device_event_log/device_event_log.h"
  15. #include "services/device/hid/hid_connection_mac.h"
  16. #include "services/device/hid/hid_service.h"
  17. namespace device {
  18. namespace {
  19. std::string HexErrorCode(IOReturn error_code) {
  20. return base::StringPrintf("0x%04x", error_code);
  21. }
  22. } // namespace
  23. HidConnectionMac::HidConnectionMac(base::ScopedCFTypeRef<IOHIDDeviceRef> device,
  24. scoped_refptr<HidDeviceInfo> device_info,
  25. bool allow_protected_reports,
  26. bool allow_fido_reports)
  27. : HidConnection(device_info, allow_protected_reports, allow_fido_reports),
  28. device_(std::move(device)),
  29. task_runner_(base::ThreadTaskRunnerHandle::Get()),
  30. blocking_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
  31. HidService::kBlockingTaskTraits)) {
  32. IOHIDDeviceScheduleWithRunLoop(device_.get(), CFRunLoopGetMain(),
  33. kCFRunLoopDefaultMode);
  34. size_t expected_report_size = device_info->max_input_report_size();
  35. if (device_info->has_report_id())
  36. expected_report_size++;
  37. inbound_buffer_.resize(expected_report_size);
  38. if (inbound_buffer_.size() > 0) {
  39. AddRef(); // Hold a reference to this while this callback is registered.
  40. IOHIDDeviceRegisterInputReportCallback(
  41. device_.get(), &inbound_buffer_[0], inbound_buffer_.size(),
  42. &HidConnectionMac::InputReportCallback, this);
  43. }
  44. }
  45. HidConnectionMac::~HidConnectionMac() {}
  46. void HidConnectionMac::PlatformClose() {
  47. if (inbound_buffer_.size() > 0) {
  48. IOHIDDeviceRegisterInputReportCallback(device_.get(), &inbound_buffer_[0],
  49. inbound_buffer_.size(), NULL, this);
  50. // Release the reference taken when this callback was registered.
  51. Release();
  52. }
  53. IOHIDDeviceUnscheduleFromRunLoop(device_.get(), CFRunLoopGetMain(),
  54. kCFRunLoopDefaultMode);
  55. IOReturn result = IOHIDDeviceClose(device_.get(), 0);
  56. if (result != kIOReturnSuccess) {
  57. HID_LOG(EVENT) << "Failed to close HID device: " << HexErrorCode(result);
  58. }
  59. }
  60. void HidConnectionMac::PlatformWrite(
  61. scoped_refptr<base::RefCountedBytes> buffer,
  62. WriteCallback callback) {
  63. blocking_task_runner_->PostTask(
  64. FROM_HERE,
  65. base::BindOnce(&HidConnectionMac::SetReportAsync, this,
  66. kIOHIDReportTypeOutput, buffer, std::move(callback)));
  67. }
  68. void HidConnectionMac::PlatformGetFeatureReport(uint8_t report_id,
  69. ReadCallback callback) {
  70. blocking_task_runner_->PostTask(
  71. FROM_HERE, base::BindOnce(&HidConnectionMac::GetFeatureReportAsync, this,
  72. report_id, std::move(callback)));
  73. }
  74. void HidConnectionMac::PlatformSendFeatureReport(
  75. scoped_refptr<base::RefCountedBytes> buffer,
  76. WriteCallback callback) {
  77. blocking_task_runner_->PostTask(
  78. FROM_HERE,
  79. base::BindOnce(&HidConnectionMac::SetReportAsync, this,
  80. kIOHIDReportTypeFeature, buffer, std::move(callback)));
  81. }
  82. // static
  83. void HidConnectionMac::InputReportCallback(void* context,
  84. IOReturn result,
  85. void* sender,
  86. IOHIDReportType type,
  87. uint32_t report_id,
  88. uint8_t* report_bytes,
  89. CFIndex report_length) {
  90. HidConnectionMac* connection = static_cast<HidConnectionMac*>(context);
  91. if (result != kIOReturnSuccess) {
  92. HID_LOG(EVENT) << "Failed to read input report: " << HexErrorCode(result);
  93. return;
  94. }
  95. scoped_refptr<base::RefCountedBytes> buffer;
  96. if (connection->device_info()->has_report_id()) {
  97. // report_id is already contained in report_bytes
  98. buffer = base::MakeRefCounted<base::RefCountedBytes>(
  99. report_bytes, base::checked_cast<size_t>(report_length));
  100. } else {
  101. buffer = base::MakeRefCounted<base::RefCountedBytes>(
  102. (base::CheckedNumeric<size_t>(report_length) + 1).ValueOrDie());
  103. buffer->front()[0] = 0;
  104. memcpy(buffer->front() + 1, report_bytes, report_length);
  105. }
  106. connection->ProcessInputReport(buffer, buffer->size());
  107. }
  108. void HidConnectionMac::GetFeatureReportAsync(uint8_t report_id,
  109. ReadCallback callback) {
  110. auto buffer = base::MakeRefCounted<base::RefCountedBytes>(
  111. device_info()->max_feature_report_size() + 1);
  112. CFIndex report_size = buffer->size();
  113. // The IOHIDDevice object is shared with the UI thread and so this function
  114. // should probably be called there but it may block and the asynchronous
  115. // version is NOT IMPLEMENTED. I've examined the open source implementation
  116. // of this function and believe it is a simple enough wrapper around the
  117. // kernel API that this is safe.
  118. IOReturn result =
  119. IOHIDDeviceGetReport(device_.get(), kIOHIDReportTypeFeature, report_id,
  120. buffer->front(), &report_size);
  121. if (result == kIOReturnSuccess) {
  122. task_runner_->PostTask(
  123. FROM_HERE, base::BindOnce(&HidConnectionMac::ReturnAsyncResult, this,
  124. base::BindOnce(std::move(callback), true,
  125. buffer, report_size)));
  126. } else {
  127. HID_LOG(EVENT) << "Failed to get feature report: " << HexErrorCode(result);
  128. task_runner_->PostTask(
  129. FROM_HERE,
  130. base::BindOnce(&HidConnectionMac::ReturnAsyncResult, this,
  131. base::BindOnce(std::move(callback), false, nullptr, 0)));
  132. }
  133. }
  134. void HidConnectionMac::SetReportAsync(
  135. IOHIDReportType report_type,
  136. scoped_refptr<base::RefCountedBytes> buffer,
  137. WriteCallback callback) {
  138. uint8_t* data = buffer->front();
  139. size_t size = buffer->size();
  140. DCHECK_GE(size, 1u);
  141. uint8_t report_id = data[0];
  142. if (report_id == 0) {
  143. // OS X only expects the first byte of the buffer to be the report ID if the
  144. // report ID is non-zero.
  145. ++data;
  146. --size;
  147. }
  148. // The IOHIDDevice object is shared with the UI thread and so this function
  149. // should probably be called there but it may block and the asynchronous
  150. // version is NOT IMPLEMENTED. I've examined the open source implementation
  151. // of this function and believe it is a simple enough wrapper around the
  152. // kernel API that this is safe.
  153. IOReturn result =
  154. IOHIDDeviceSetReport(device_.get(), report_type, report_id, data, size);
  155. if (result == kIOReturnSuccess) {
  156. task_runner_->PostTask(
  157. FROM_HERE, base::BindOnce(&HidConnectionMac::ReturnAsyncResult, this,
  158. base::BindOnce(std::move(callback), true)));
  159. } else {
  160. HID_LOG(EVENT) << "Failed to set report: " << HexErrorCode(result);
  161. task_runner_->PostTask(
  162. FROM_HERE, base::BindOnce(&HidConnectionMac::ReturnAsyncResult, this,
  163. base::BindOnce(std::move(callback), false)));
  164. }
  165. }
  166. void HidConnectionMac::ReturnAsyncResult(base::OnceClosure callback) {
  167. // This function is used so that the last reference to |this| can be released
  168. // on the thread where it was created.
  169. std::move(callback).Run();
  170. }
  171. } // namespace device