usb_service_mac.cc 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. // Copyright 2020 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/usb/usb_service_mac.h"
  5. #include <CoreFoundation/CFBase.h>
  6. #include <CoreFoundation/CoreFoundation.h>
  7. #include <IOKit/IOCFPlugIn.h>
  8. #include <IOKit/IOReturn.h>
  9. #include <memory>
  10. #include <string>
  11. #include <utility>
  12. #include <vector>
  13. #include "base/mac/foundation_util.h"
  14. #include "base/mac/scoped_ioplugininterface.h"
  15. #include "base/strings/sys_string_conversions.h"
  16. #include "base/strings/utf_string_conversions.h"
  17. #include "components/device_event_log/device_event_log.h"
  18. #include "services/device/usb/usb_descriptors.h"
  19. #include "services/device/usb/usb_device_mac.h"
  20. #include "services/device/utils/mac_utils.h"
  21. #include "third_party/abseil-cpp/absl/types/optional.h"
  22. namespace device {
  23. namespace {
  24. // USB class codes are defined by the USB specification.
  25. // https://www.usb.org/defined-class-codes
  26. constexpr uint8_t kDeviceClassHub = 0x09;
  27. } // namespace
  28. UsbServiceMac::UsbServiceMac() {
  29. notify_port_.reset(IONotificationPortCreate(kIOMasterPortDefault));
  30. CFRunLoopAddSource(CFRunLoopGetMain(),
  31. IONotificationPortGetRunLoopSource(notify_port_.get()),
  32. kCFRunLoopDefaultMode);
  33. IOReturn result = IOServiceAddMatchingNotification(
  34. notify_port_.get(), kIOFirstMatchNotification,
  35. IOServiceMatching(kIOUSBDeviceClassName), FirstMatchCallback, this,
  36. devices_added_iterator_.InitializeInto());
  37. if (result != kIOReturnSuccess) {
  38. USB_LOG(ERROR) << "Failed to listen for device arrival: " << std::hex
  39. << result << ".";
  40. return;
  41. }
  42. // Drain |devices_added_iterator_| to arm the notification.
  43. AddDevices();
  44. result = IOServiceAddMatchingNotification(
  45. notify_port_.get(), kIOTerminatedNotification,
  46. IOServiceMatching(kIOUSBDeviceClassName), TerminatedCallback, this,
  47. devices_removed_iterator_.InitializeInto());
  48. if (result != kIOReturnSuccess) {
  49. USB_LOG(ERROR) << "Failed to listen for device removal: " << std::hex
  50. << result << ".";
  51. return;
  52. }
  53. // Drain |devices_removed_iterator_| to arm the notification.
  54. RemoveDevices();
  55. }
  56. UsbServiceMac::~UsbServiceMac() = default;
  57. // static
  58. void UsbServiceMac::FirstMatchCallback(void* context, io_iterator_t iterator) {
  59. DCHECK_EQ(CFRunLoopGetMain(), CFRunLoopGetCurrent());
  60. UsbServiceMac* service = reinterpret_cast<UsbServiceMac*>(context);
  61. DCHECK_EQ(service->devices_added_iterator_, iterator);
  62. service->AddDevices();
  63. }
  64. // static
  65. void UsbServiceMac::TerminatedCallback(void* context, io_iterator_t iterator) {
  66. DCHECK_EQ(CFRunLoopGetMain(), CFRunLoopGetCurrent());
  67. UsbServiceMac* service = reinterpret_cast<UsbServiceMac*>(context);
  68. DCHECK_EQ(service->devices_removed_iterator_, iterator);
  69. service->RemoveDevices();
  70. }
  71. void UsbServiceMac::AddDevices() {
  72. base::mac::ScopedIOObject<io_service_t> device;
  73. while (device.reset(IOIteratorNext(devices_added_iterator_)), device) {
  74. AddDevice(device);
  75. }
  76. }
  77. void UsbServiceMac::AddDevice(io_service_t device) {
  78. base::mac::ScopedIOPluginInterface<IOCFPlugInInterface> plugin_interface;
  79. int32_t score;
  80. // This call fails sometimes due to a resource shortage.
  81. // TODO(richardmachado): Figure out what is causing this failure.
  82. IOReturn kr = IOCreatePlugInInterfaceForService(
  83. device, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID,
  84. plugin_interface.InitializeInto(), &score);
  85. if ((kr != kIOReturnSuccess) || !plugin_interface.get()) {
  86. USB_LOG(ERROR) << "Unable to create a plug-in: " << std::hex << kr << ".";
  87. return;
  88. }
  89. base::mac::ScopedIOPluginInterface<IOUSBDeviceInterface182> device_interface;
  90. kr = (*plugin_interface)
  91. ->QueryInterface(
  92. plugin_interface.get(),
  93. CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
  94. reinterpret_cast<LPVOID*>(device_interface.InitializeInto()));
  95. if (kr != kIOReturnSuccess || !device_interface) {
  96. USB_LOG(ERROR) << "Couldn’t create a device interface.";
  97. return;
  98. }
  99. uint8_t device_class;
  100. if ((*device_interface)->GetDeviceClass(device_interface, &device_class) !=
  101. kIOReturnSuccess) {
  102. return;
  103. }
  104. // We don't want to enumerate hubs.
  105. if (device_class == kDeviceClassHub)
  106. return;
  107. uint16_t vendor_id;
  108. if ((*device_interface)->GetDeviceVendor(device_interface, &vendor_id) !=
  109. kIOReturnSuccess) {
  110. return;
  111. }
  112. uint16_t product_id;
  113. if ((*device_interface)->GetDeviceProduct(device_interface, &product_id) !=
  114. kIOReturnSuccess) {
  115. return;
  116. }
  117. uint8_t device_protocol;
  118. if ((*device_interface)
  119. ->GetDeviceProtocol(device_interface, &device_protocol) !=
  120. kIOReturnSuccess) {
  121. return;
  122. }
  123. uint8_t device_subclass;
  124. if ((*device_interface)
  125. ->GetDeviceSubClass(device_interface, &device_subclass) !=
  126. kIOReturnSuccess) {
  127. return;
  128. }
  129. uint16_t device_version;
  130. if ((*device_interface)
  131. ->GetDeviceReleaseNumber(device_interface, &device_version) !=
  132. kIOReturnSuccess) {
  133. return;
  134. }
  135. uint32_t location_id;
  136. if ((*device_interface)->GetLocationID(device_interface, &location_id) !=
  137. kIOReturnSuccess) {
  138. return;
  139. }
  140. uint64_t entry_id;
  141. if (IORegistryEntryGetRegistryEntryID(device, &entry_id) != kIOReturnSuccess)
  142. return;
  143. absl::optional<uint8_t> property_uint8 =
  144. GetIntegerProperty<uint8_t>(device, CFSTR("PortNum"));
  145. if (!property_uint8.has_value())
  146. return;
  147. uint8_t port_number = property_uint8.value();
  148. absl::optional<uint16_t> property_uint16 =
  149. GetIntegerProperty<uint16_t>(device, CFSTR("bcdUSB"));
  150. uint16_t usb_version;
  151. if (!property_uint16.has_value())
  152. return;
  153. usb_version = property_uint16.value();
  154. absl::optional<std::u16string> property_string16 =
  155. GetStringProperty<std::u16string>(device, CFSTR(kUSBVendorString));
  156. std::u16string manufacturer_string;
  157. if (property_string16.has_value())
  158. manufacturer_string = property_string16.value();
  159. property_string16 =
  160. GetStringProperty<std::u16string>(device, CFSTR(kUSBSerialNumberString));
  161. std::u16string serial_number_string;
  162. if (property_string16.has_value())
  163. serial_number_string = property_string16.value();
  164. property_string16 =
  165. GetStringProperty<std::u16string>(device, CFSTR(kUSBProductString));
  166. std::u16string product_string;
  167. if (property_string16.has_value())
  168. product_string = property_string16.value();
  169. uint8_t num_config;
  170. if ((*device_interface)
  171. ->GetNumberOfConfigurations(device_interface, &num_config) !=
  172. kIOReturnSuccess) {
  173. return;
  174. }
  175. // Populate device descriptor with all necessary configuration info.
  176. auto descriptor = std::make_unique<UsbDeviceDescriptor>();
  177. IOUSBConfigurationDescriptorPtr desc;
  178. for (uint8_t i = 0; i < num_config; i++) {
  179. if ((*device_interface)
  180. ->GetConfigurationDescriptorPtr(device_interface, i, &desc) !=
  181. kIOReturnSuccess) {
  182. return;
  183. }
  184. if (!descriptor->Parse(base::make_span(reinterpret_cast<uint8_t*>(desc),
  185. desc->wTotalLength))) {
  186. return;
  187. }
  188. }
  189. descriptor->device_info->usb_version_major = usb_version >> 8;
  190. descriptor->device_info->usb_version_minor = usb_version >> 4 & 0xf;
  191. descriptor->device_info->usb_version_subminor = usb_version & 0xf;
  192. descriptor->device_info->class_code = device_class;
  193. descriptor->device_info->subclass_code = device_subclass;
  194. descriptor->device_info->protocol_code = device_protocol;
  195. descriptor->device_info->vendor_id = vendor_id;
  196. descriptor->device_info->product_id = product_id;
  197. descriptor->device_info->device_version_major = device_version >> 8;
  198. descriptor->device_info->device_version_minor = device_version >> 4 & 0xf;
  199. descriptor->device_info->device_version_subminor = device_version & 0xf;
  200. descriptor->device_info->manufacturer_name = manufacturer_string;
  201. descriptor->device_info->product_name = product_string;
  202. descriptor->device_info->serial_number = serial_number_string;
  203. descriptor->device_info->bus_number = location_id >> 24;
  204. descriptor->device_info->port_number = port_number;
  205. scoped_refptr<UsbDeviceMac> mac_device =
  206. new UsbDeviceMac(entry_id, std::move(descriptor->device_info));
  207. device_map_[entry_id] = mac_device;
  208. devices()[mac_device->guid()] = mac_device;
  209. NotifyDeviceAdded(mac_device);
  210. }
  211. void UsbServiceMac::RemoveDevices() {
  212. base::mac::ScopedIOObject<io_service_t> device;
  213. while (device.reset(IOIteratorNext(devices_removed_iterator_)), device) {
  214. uint64_t entry_id;
  215. if (kIOReturnSuccess !=
  216. IORegistryEntryGetRegistryEntryID(device, &entry_id)) {
  217. continue;
  218. }
  219. auto it = device_map_.find(entry_id);
  220. if (it == device_map_.end())
  221. continue;
  222. auto mac_device = it->second;
  223. device_map_.erase(it);
  224. auto by_guid_it = devices().find(mac_device->guid());
  225. devices().erase(by_guid_it);
  226. NotifyDeviceRemoved(mac_device);
  227. mac_device->OnDisconnect();
  228. }
  229. }
  230. } // namespace device