1 /*!
2  * \copyright Copyright (c) 2016-2021 Governikus GmbH & Co. KG, Germany
3  */
4 
5 #include "ReaderDetector.h"
6 
7 #include <CoreFoundation/CFNumber.h>
8 
9 #include <IOKit/IOKitKeys.h>
10 #include <IOKit/usb/IOUSBLib.h>
11 
12 #include <QLoggingCategory>
13 
14 Q_DECLARE_LOGGING_CATEGORY(card_drivers)
15 
16 using namespace governikus;
17 
18 
19 #define VENDOR_ID "idVendor"
20 #define PRODUCT_ID "idProduct"
21 
22 
deviceChanged(void * refCon,io_iterator_t iterator)23 static void deviceChanged(void* refCon, io_iterator_t iterator)
24 {
25 	while (IOIteratorNext(iterator))
26 	{
27 		// Clear the recognized changes
28 	}
29 
30 	qCDebug(card_drivers) << "System information: device changed";
31 
32 	ReaderDetector* readerDetector = static_cast<ReaderDetector*>(refCon);
33 	Q_EMIT readerDetector->fireReaderChangeDetected();
34 }
35 
36 
listenTo(const io_name_t notificationType,ReaderDetector * readerDetector,io_iterator_t * iterator)37 static bool listenTo(const io_name_t notificationType, ReaderDetector* readerDetector, io_iterator_t* iterator)
38 {
39 	//https://developer.apple.com/library/mac/documentation/DeviceDrivers/Conceptual/AccessingHardware/AH_Finding_Devices/AH_Finding_Devices.html
40 
41 	CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
42 	IONotificationPortRef notificationObject = IONotificationPortCreate(kIOMasterPortDefault);
43 	CFRunLoopSourceRef notificationRunLoopSource = IONotificationPortGetRunLoopSource(notificationObject);
44 	CFRunLoopAddSource(CFRunLoopGetCurrent(), notificationRunLoopSource, kCFRunLoopDefaultMode);
45 	kern_return_t kr = IOServiceAddMatchingNotification(
46 			notificationObject,
47 			notificationType,
48 			matchingDict,
49 			deviceChanged,
50 			readerDetector,
51 			iterator);
52 
53 	if (kr != KERN_SUCCESS)
54 	{
55 		qCDebug(card_drivers) << "IOServiceAddMatchingNotification returned" << kr;
56 		return false;
57 	}
58 
59 	while (IOIteratorNext(*iterator))
60 	{
61 		// Clear already known devices and arm the notification mechanism
62 	}
63 
64 	return true;
65 }
66 
67 
initNativeEvents()68 bool ReaderDetector::initNativeEvents()
69 {
70 	bool succeed = true;
71 	succeed &= listenTo(kIOPublishNotification, this, &mIteratorPublish);
72 	succeed &= listenTo(kIOTerminatedNotification, this, &mIteratorTerminated);
73 
74 	return succeed;
75 }
76 
77 
terminateNativeEvents()78 bool ReaderDetector::terminateNativeEvents()
79 {
80 	bool succeed = true;
81 	succeed &= (IOObjectRelease(mIteratorPublish) == KERN_SUCCESS);
82 	succeed &= (IOObjectRelease(mIteratorTerminated) == KERN_SUCCESS);
83 
84 	return succeed;
85 }
86 
87 
getUintValueFromDeviceRegistryEntry(io_object_t pDevice,CFStringRef pKey,uint * pResultValue)88 static bool getUintValueFromDeviceRegistryEntry(io_object_t pDevice, CFStringRef pKey, uint* pResultValue)
89 {
90 	static const CFTypeID NUMBER_TYPE_ID = CFNumberGetTypeID();
91 
92 	CFTypeRef property = IORegistryEntryCreateCFProperty(pDevice, pKey, kCFAllocatorDefault, /* options */ 0);
93 	if (property == NULL)
94 	{
95 		qCWarning(card_drivers) << "property" << pKey << "not found in registry entry";
96 
97 		return false;
98 	}
99 
100 	if (CFGetTypeID(property) != NUMBER_TYPE_ID)
101 	{
102 		qCWarning(card_drivers) << "property" << pKey << "is not of a numeric type";
103 
104 		return false;
105 	}
106 
107 	CFNumberRef number = static_cast<CFNumberRef>(property);
108 	if (CFNumberGetType(number) != kCFNumberSInt32Type)
109 	{
110 		qCWarning(card_drivers) << "property" << pKey << "has the wrong numeric type";
111 
112 		return false;
113 	}
114 
115 	const bool success = CFNumberGetValue(number, kCFNumberSInt32Type, pResultValue);
116 	CFRelease(property);
117 	if (!success)
118 	{
119 		qCWarning(card_drivers) << "error while reading numeric value from property" << pKey;
120 	}
121 
122 	return success;
123 }
124 
125 
getDeviceIds(io_object_t pDevice)126 static UsbId getDeviceIds(io_object_t pDevice)
127 {
128 	uint vendorId = 0x0;
129 	uint productId = 0x0;
130 
131 	if (!getUintValueFromDeviceRegistryEntry(pDevice, CFSTR(VENDOR_ID), &vendorId))
132 	{
133 		qCDebug(card_drivers) << "missing or invalid vendor id";
134 
135 		return UsbId(0x0, 0x0);
136 	}
137 
138 	if (!getUintValueFromDeviceRegistryEntry(pDevice, CFSTR(PRODUCT_ID), &productId))
139 	{
140 		qCDebug(card_drivers) << "missing or invalid product id";
141 
142 		return UsbId(0x0, 0x0);
143 	}
144 
145 	return UsbId(vendorId, productId);
146 }
147 
148 
attachedDevIds() const149 QVector<UsbId> ReaderDetector::attachedDevIds() const
150 {
151 	QVector<UsbId> result;
152 	mach_port_t myMasterPort = kIOMasterPortDefault;
153 	io_iterator_t deviceIterator = 0;
154 	io_object_t currentDevice = 0;
155 
156 	kern_return_t createIteratorResult = IORegistryCreateIterator(myMasterPort, kIOUSBPlane, kIORegistryIterateRecursively, &deviceIterator);
157 	if (createIteratorResult != kIOReturnSuccess)
158 	{
159 		qCWarning(card_drivers) << "creation of device iterator failed, exiting";
160 
161 		return QVector<UsbId>();
162 	}
163 
164 	currentDevice = IOIteratorNext(deviceIterator);
165 	while (currentDevice != 0)
166 	{
167 		UsbId ids = getDeviceIds(currentDevice);
168 		if (ids.getVendorId() != 0 && ids.getProductId() != 0)
169 		{
170 			result += ids;
171 		}
172 
173 		IOObjectRelease(currentDevice);
174 		currentDevice = IOIteratorNext(deviceIterator);
175 	}
176 
177 	IOObjectRelease(deviceIterator);
178 
179 	return result;
180 }
181