Vuo  2.0.0
VuoSerialDevices.cc
Go to the documentation of this file.
1 
10 #include "VuoSerial.h"
11 #include "VuoTriggerSet.hh"
12 
13 
14 #include <IOKit/IOKitLib.h>
15 #include <IOKit/serial/IOSerialKeys.h>
16 #include <IOKit/usb/USBSpec.h>
17 
18 extern "C"
19 {
20 
21 #ifdef VUO_COMPILER
23  "title" : "VuoSerialDevices",
24  "dependencies" : [
25  "CoreFoundation.framework",
26  "IOKit.framework",
27  "VuoSerialDevice",
28  "VuoSerialDevices",
29  "VuoSerialIO",
30  "VuoList_VuoSerialDevice",
31  "VuoList_VuoInteger"
32  ]
33  });
34 #endif
35 }
36 
38 
42 static VuoText VuoSerial_getPropertyFromObjectOrAncestry(io_object_t o, CFStringRef property)
43 {
44  CFStringRef valueCF = (CFStringRef)IORegistryEntryCreateCFProperty(o, property, NULL, 0);
45  if (valueCF)
46  {
47  VuoText value = VuoText_makeFromCFString(valueCF);
48  CFRelease(valueCF);
49  return value;
50  }
51 
52  io_iterator_t it;
53  kern_return_t ret = IORegistryEntryGetParentIterator(o, kIOServicePlane, &it);
54  if (ret != KERN_SUCCESS)
55  {
56  VUserLog("Error: Couldn't get parent iterator: %d", ret);
57  return NULL;
58  }
59 
60  io_object_t po;
61  while ( (po = IOIteratorNext(it)) )
62  {
64  IOObjectRelease(po);
65  if (value)
66  {
67  IOObjectRelease(it);
68  return value;
69  }
70  }
71  IOObjectRelease(it);
72 
73  return NULL;
74 }
75 
76 
81 {
82  CFMutableDictionaryRef match = IOServiceMatching(kIOSerialBSDServiceValue);
83  if (!match)
84  {
85  VUserLog("Error: Couldn't create serial matching dictionary.");
86  return NULL;
87  }
88  CFDictionarySetValue(match, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes));
89 
90  io_iterator_t it;
91  kern_return_t ret = IOServiceGetMatchingServices(kIOMasterPortDefault, match, &it);
92  if (ret != KERN_SUCCESS)
93  {
94  VUserLog("Error: Couldn't get serial device list: %d", ret);
95  return NULL;
96  }
97 
99  io_object_t o;
100  while ( (o = IOIteratorNext(it)) )
101  {
102  VuoSerialDevice device;
103  device.matchType = VuoSerialDevice_MatchPath;
104 
105  device.path = VuoSerial_getPropertyFromObjectOrAncestry(o, CFSTR(kIOCalloutDeviceKey));
106  if (!device.path)
107  {
108  VUserLog("Error: Device %d has no path.", o);
109  continue;
110  }
111 
112  // Omit devices we can't open.
113  if (strstr(device.path, "-Wireless") // iPhone?
114  || strstr(device.path, "Bluetooth-"))
115  {
116  VuoRetain(device.path);
117  VuoRelease(device.path);
118  continue;
119  }
120 
121  VuoText vendorName = VuoSerial_getPropertyFromObjectOrAncestry(o, CFSTR(kUSBVendorString));
122  VuoText productName = VuoSerial_getPropertyFromObjectOrAncestry(o, CFSTR(kUSBProductString));
123  VuoText serialNumber = VuoSerial_getPropertyFromObjectOrAncestry(o, CFSTR(kUSBSerialNumberString));
124 
125  VuoRetain(vendorName);
126  VuoRetain(productName);
127  VuoRetain(serialNumber);
128 
129  if (vendorName && productName && serialNumber)
130  device.name = VuoText_make(VuoText_format("%s %s (%s)", vendorName, productName, serialNumber));
131  else if (vendorName && productName)
132  device.name = VuoText_make(VuoText_format("%s %s", vendorName, productName));
133  else if (vendorName && serialNumber)
134  device.name = VuoText_make(VuoText_format("%s (%s)", vendorName, serialNumber));
135  else if (productName && serialNumber)
136  device.name = VuoText_make(VuoText_format("%s (%s)", productName, serialNumber));
137  else if (vendorName)
138  device.name = VuoText_make(vendorName);
139  else if (productName)
140  device.name = VuoText_make(productName);
141  else
142  {
143  VuoText ttyDevice = VuoSerial_getPropertyFromObjectOrAncestry(o, CFSTR(kIOTTYDeviceKey));
144  if (ttyDevice)
145  device.name = ttyDevice;
146  else
147  device.name = VuoText_make(device.path);
148  }
149 
150 
151  VuoRelease(vendorName);
152  VuoRelease(productName);
153  VuoRelease(serialNumber);
154  IOObjectRelease(o);
155 
156  VuoListAppendValue_VuoSerialDevice(devices, device);
157  }
158 
159  return devices;
160 }
161 
162 unsigned int VuoSerial_useCount = 0;
163 IONotificationPortRef VuoSerial_notificationPort;
164 
168 static void VuoSerial_servicesChanged(void *refcon, io_iterator_t it)
169 {
170  // The notification won't fire again until we've looked at all of the existing items once.
171  while (IOIteratorNext(it));
172 
174 
176 }
177 
183 void VuoSerial_use(void)
184 {
185  if (__sync_add_and_fetch(&VuoSerial_useCount, 1) == 1)
186  {
187  VuoSerial_notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
189  {
190  VUserLog("Error: Couldn't create serial notification port.");
191  return;
192  }
193 
194  CFRunLoopSourceRef notificationSource = IONotificationPortGetRunLoopSource(VuoSerial_notificationPort);
195  if (!notificationSource)
196  {
197  VUserLog("Error: Couldn't get serial notification source.");
198  return;
199  }
200 
201  CFRunLoopAddSource(CFRunLoopGetMain(), notificationSource, kCFRunLoopCommonModes);
202 
203 
204  CFMutableDictionaryRef match = IOServiceMatching(kIOSerialBSDServiceValue);
205  if (!match)
206  {
207  VUserLog("Error: Couldn't create serial matching dictionary.");
208  return;
209  }
210  CFDictionarySetValue(match, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes));
211 
212  // Since each IOServiceAddMatchingNotification() call releases the matching dictionary, retain it in order to make it through both calls.
213  CFRetain(match);
214 
215 
216  io_iterator_t it;
217  kern_return_t ret = IOServiceAddMatchingNotification(VuoSerial_notificationPort, kIOMatchedNotification, match, VuoSerial_servicesChanged, NULL, &it);
218  if (ret != KERN_SUCCESS)
219  {
220  VUserLog("Error: Couldn't create serial-add notification: %d", ret);
221  return;
222  }
223  // The notification doesn't start firing until we've looked at all of the existing items once.
224  while (IOIteratorNext(it));
225 
226 
227  ret = IOServiceAddMatchingNotification(VuoSerial_notificationPort, kIOTerminatedNotification, match, VuoSerial_servicesChanged, NULL, &it);
228  if (ret != KERN_SUCCESS)
229  {
230  VUserLog("Error: Couldn't create serial-remove notification: %d", ret);
231  return;
232  }
233  // The notification doesn't start firing until we've looked at all of the existing items once.
234  while (IOIteratorNext(it));
235  }
236 }
237 
244 {
245  if (VuoSerial_useCount <= 0)
246  {
247  VUserLog("Error: Unbalanced VuoSerial_use() / _disuse() calls.");
248  return;
249  }
250 
251  if (__sync_sub_and_fetch(&VuoSerial_useCount, 1) == 0)
252  IONotificationPortDestroy(VuoSerial_notificationPort);
253 }
254 
263 {
264  VuoSerial_deviceCallbacks.addTrigger(devices);
265  devices(VuoSerial_getDeviceList());
266 }
267 
274 {
275  VuoSerial_deviceCallbacks.removeTrigger(devices);
276 }