-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathUSBNotifier.m
149 lines (123 loc) · 5.16 KB
/
USBNotifier.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/*
Shamlessly taken from the (BSD-licensed) HardwareGrowler Extra of the Growl project.
*/
#include "USBNotifier.h"
#import "FFHelperApp.h"
#include <IOKit/IOKitLib.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/usb/IOUSBLib.h>
#include <IOKit/usb/USB.h>
//extern void NSLog(CFStringRef format, ...);
static IONotificationPortRef ioKitNotificationPort;
static CFRunLoopSourceRef notificationRunLoopSource;
static Boolean notificationsArePrimed = false;
#pragma mark C Callbacks
static void usbDeviceAdded(void *refCon, io_iterator_t iterator) {
#pragma unused(refCon)
// NSLog(@"USB Device Added Notification.");
io_object_t thisObject;
while ((thisObject = IOIteratorNext(iterator))) {
if (notificationsArePrimed) {
kern_return_t nameResult;
io_name_t deviceNameChars;
// This works with USB devices...
// but apparently not firewire
nameResult = IORegistryEntryGetName(thisObject, deviceNameChars);
CFStringRef deviceName = CFStringCreateWithCString(kCFAllocatorDefault,
deviceNameChars,
kCFStringEncodingASCII);
if ((CFStringCompare(deviceName, CFSTR("OHCI Root Hub Simulation"), 0) == kCFCompareEqualTo) ||
(CFStringCompare(deviceName, CFSTR("UHCI Root Hub Simulation"), 0) == kCFCompareEqualTo)) {
CFRelease(deviceName);
deviceName = CFCopyLocalizedString(CFSTR("USB Bus"), "");
} else if (CFStringCompare(deviceName, CFSTR("EHCI Root Hub Simulation"), 0) == kCFCompareEqualTo) {
CFRelease(deviceName);
deviceName = CFCopyLocalizedString(CFSTR("USB 2.0 Bus"), "");
}
// NSLog(@"USB Device Attached: %@" , deviceName);
[(FFHelperApp *)refCon keyboardListChanged];
CFRelease(deviceName);
}
IOObjectRelease(thisObject);
}
}
static void usbDeviceRemoved(void *refCon, io_iterator_t iterator) {
#pragma unused(refCon)
// NSLog(@"USB Device Removed Notification.");
io_object_t thisObject;
while ((thisObject = IOIteratorNext(iterator))) {
kern_return_t nameResult;
io_name_t deviceNameChars;
// This works with USB devices...
// but apparently not firewire
nameResult = IORegistryEntryGetName(thisObject, deviceNameChars);
CFStringRef deviceName = CFStringCreateWithCString(kCFAllocatorDefault,
deviceNameChars,
kCFStringEncodingASCII);
if (CFStringCompare(deviceName, CFSTR("OHCI Root Hub Simulation"), 0) == kCFCompareEqualTo)
deviceName = CFCopyLocalizedString(CFSTR("USB Bus"), "");
else if (CFStringCompare(deviceName, CFSTR("EHCI Root Hub Simulation"), 0) == kCFCompareEqualTo)
deviceName = CFCopyLocalizedString(CFSTR("USB 2.0 Bus"), "");
// NSLog(@"USB Device Detached: %@" , deviceName);
[(FFHelperApp *)refCon keyboardListChanged];
CFRelease(deviceName);
IOObjectRelease(thisObject);
}
}
#pragma mark -
static void registerForUSBNotifications(void *refcon) {
//http://developer.apple.com/documentation/DeviceDrivers/Conceptual/AccessingHardware/AH_Finding_Devices/chapter_4_section_2.html#//apple_ref/doc/uid/TP30000379/BABEACCJ
kern_return_t matchingResult;
kern_return_t removeNoteResult;
io_iterator_t addedIterator;
io_iterator_t removedIterator;
// NSLog(@"registerForUSBNotifications");
// Setup a matching Dictionary.
CFDictionaryRef myMatchDictionary;
myMatchDictionary = IOServiceMatching(kIOUSBDeviceClassName);
// Register our notification
matchingResult = IOServiceAddMatchingNotification(ioKitNotificationPort,
kIOPublishNotification,
myMatchDictionary,
usbDeviceAdded,
refcon,
&addedIterator);
if (matchingResult)
NSLog(@"matching notification registration failed: %d", matchingResult);
// Prime the Notifications (And Deal with the existing devices)...
usbDeviceAdded(NULL, addedIterator);
// Register for removal notifications.
// It seems we have to make a new dictionary... reusing the old one didn't work.
myMatchDictionary = IOServiceMatching(kIOUSBDeviceClassName);
removeNoteResult = IOServiceAddMatchingNotification(ioKitNotificationPort,
kIOTerminatedNotification,
myMatchDictionary,
usbDeviceRemoved,
refcon,
&removedIterator);
// Matching notification must be "primed" by iterating over the
// iterator returned from IOServiceAddMatchingNotification(), so
// we call our device removed method here...
//
if (kIOReturnSuccess != removeNoteResult)
NSLog(@"Couldn't add device removal notification");
else
usbDeviceRemoved(NULL, removedIterator);
notificationsArePrimed = true;
}
void USBNotifier_init(FFHelperApp *delegate) {
notificationsArePrimed = false;
//#warning kIOMasterPortDefault is only available on 10.2 and above...
ioKitNotificationPort = IONotificationPortCreate(kIOMasterPortDefault);
notificationRunLoopSource = IONotificationPortGetRunLoopSource(ioKitNotificationPort);
CFRunLoopAddSource(CFRunLoopGetCurrent(),
notificationRunLoopSource,
kCFRunLoopDefaultMode);
registerForUSBNotifications((void *)delegate);
}
void USBNotifier_dealloc(void) {
if (ioKitNotificationPort) {
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), notificationRunLoopSource, kCFRunLoopDefaultMode);
IONotificationPortDestroy(ioKitNotificationPort);
}
}