OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 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 "content/browser/device_monitor_mac.h" |
| 6 |
| 7 #include <CoreFoundation/CFNumber.h> |
| 8 #include <CoreFoundation/CoreFoundation.h> |
| 9 #include <IOKit/usb/IOUSBLib.h> |
| 10 |
| 11 #include "base/logging.h" |
| 12 #include "base/system_monitor/system_monitor.h" |
| 13 |
| 14 namespace { |
| 15 |
| 16 struct { |
| 17 base::SystemMonitor::DeviceType device_type; |
| 18 SInt32 interface_class_code; |
| 19 SInt32 interface_subclass_code; |
| 20 } kInterfaceTypeMap[] = { |
| 21 // Add new types here if needed. |
| 22 { base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE, kUSBAudioInterfaceClass, |
| 23 kUSBAudioControlSubClass }, |
| 24 { base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE, kUSBVideoInterfaceClass, |
| 25 kUSBVideoControlSubClass }, |
| 26 }; |
| 27 |
| 28 void AddValueToDictionary(CFMutableDictionaryRef dictionary, |
| 29 SInt32 code, |
| 30 const void *key) { |
| 31 CFNumberRef number_ref = CFNumberCreate(kCFAllocatorDefault, |
| 32 kCFNumberSInt32Type, |
| 33 &code); |
| 34 if (!number_ref) { |
| 35 NOTREACHED() << "failed to create CFNumberRef for " << code; |
| 36 return; |
| 37 } |
| 38 |
| 39 CFDictionaryAddValue(dictionary, key, number_ref); |
| 40 CFRelease(number_ref); |
| 41 } |
| 42 |
| 43 CFMutableDictionaryRef CreateMatchingDictionary( |
| 44 SInt32 interface_class_code, |
| 45 SInt32 interface_subclass_code) { |
| 46 CFMutableDictionaryRef matching_dictionary = IOServiceMatching( |
| 47 kIOUSBInterfaceClassName); |
| 48 AddValueToDictionary(matching_dictionary, interface_class_code, |
| 49 CFSTR(kUSBInterfaceClass)); |
| 50 AddValueToDictionary(matching_dictionary, interface_subclass_code, |
| 51 CFSTR(kUSBInterfaceSubClass)); |
| 52 |
| 53 return matching_dictionary; |
| 54 } |
| 55 |
| 56 void AddCallbackToIOService(IONotificationPortRef port, |
| 57 const io_name_t type, |
| 58 CFMutableDictionaryRef dictionary, |
| 59 IOServiceMatchingCallback callback, |
| 60 void* context) { |
| 61 // Retain additional dictionary references because each call to |
| 62 // IOServiceAddMatchingNotification consumes one reference. |
| 63 dictionary = (CFMutableDictionaryRef)(CFRetain(dictionary)); |
| 64 |
| 65 io_iterator_t devices_iterator = 0; |
| 66 kern_return_t err = IOServiceAddMatchingNotification(port, |
| 67 type, |
| 68 dictionary, |
| 69 callback, |
| 70 context, |
| 71 &devices_iterator); |
| 72 if (err) { |
| 73 NOTREACHED() << "Failed to register the IO matched notification for type " |
| 74 << type; |
| 75 return; |
| 76 } |
| 77 |
| 78 // Iterate over set of matching devices to access already-present devices |
| 79 // and to arm the notification. |
| 80 io_object_t this_object; |
| 81 while ((this_object = IOIteratorNext(devices_iterator))) |
| 82 IOObjectRelease(this_object); |
| 83 } |
| 84 |
| 85 // For now, context is a cast of device_type. If |this| is needed, |
| 86 // DeviceMonitorMac can keep an list of objects of |
| 87 // struct Context { |
| 88 // base::SystemMonitor::DeviceType device_type; |
| 89 // DeviceMonitorMac* instance; |
| 90 // }; |
| 91 void DevicesChangedCallback(void *context, io_iterator_t devices) { |
| 92 io_object_t this_object; |
| 93 while ((this_object = IOIteratorNext(devices))) { |
| 94 if (context) { |
| 95 base::SystemMonitor::DeviceType device_type = |
| 96 *(static_cast<base::SystemMonitor::DeviceType*>(context)); |
| 97 base::SystemMonitor::Get()->ProcessDevicesChanged(device_type); |
| 98 } |
| 99 IOObjectRelease(this_object); |
| 100 } |
| 101 } |
| 102 |
| 103 } // namespace |
| 104 |
| 105 namespace content { |
| 106 |
| 107 DeviceMonitorMac::DeviceMonitorMac() { |
| 108 CFRunLoopRef runloop = CFRunLoopGetCurrent(); |
| 109 CFRetain(runloop); |
| 110 |
| 111 // Add the notification port to the run loop. |
| 112 IONotificationPortRef notification_port = |
| 113 IONotificationPortCreate(kIOMasterPortDefault); |
| 114 CFRunLoopSourceRef notification_cfsource = |
| 115 IONotificationPortGetRunLoopSource(notification_port); |
| 116 |
| 117 RegisterCallbacks(notification_port); |
| 118 CFRunLoopAddSource(runloop, notification_cfsource, kCFRunLoopCommonModes); |
| 119 } |
| 120 |
| 121 DeviceMonitorMac::~DeviceMonitorMac() {} |
| 122 |
| 123 void DeviceMonitorMac::RegisterCallbacks(IONotificationPortRef port) { |
| 124 for (size_t i = 0; i < arraysize(kInterfaceTypeMap); i++) { |
| 125 CFMutableDictionaryRef matching_dictionary = CreateMatchingDictionary( |
| 126 kInterfaceTypeMap[i].interface_class_code, |
| 127 kInterfaceTypeMap[i].interface_subclass_code); |
| 128 |
| 129 // Add a callback which will be called when a device is plugged in. |
| 130 AddCallbackToIOService( |
| 131 port, |
| 132 kIOMatchedNotification, |
| 133 matching_dictionary, |
| 134 &DevicesChangedCallback, |
| 135 static_cast<void*>(&kInterfaceTypeMap[i].device_type)); |
| 136 |
| 137 // Add a callback which will be called when a video device is terminated. |
| 138 AddCallbackToIOService( |
| 139 port, |
| 140 kIOTerminatedNotification, |
| 141 matching_dictionary, |
| 142 &DevicesChangedCallback, |
| 143 static_cast<void*>(&kInterfaceTypeMap[i].device_type)); |
| 144 } |
| 145 } |
| 146 |
| 147 } // namespace content |
OLD | NEW |