Vuo  2.3.2
VuoSerialIO.cc
Go to the documentation of this file.
1 
10 #include "VuoSerial.h"
11 #include "VuoPool.hh"
12 #include "VuoTriggerSet.hh"
13 
14 #include <sys/ioctl.h>
15 #include <termios.h>
16 #include <IOKit/serial/ioss.h>
17 #include <errno.h>
18 
19 
20 extern "C"
21 {
22 
23 #ifdef VUO_COMPILER
25  "title" : "VuoSerialIO",
26  "dependencies" : [
27  "VuoSerialDevice",
28  "VuoList_VuoSerialDevice"
29  ]
30  });
31 #endif
32 }
33 
34 
38 typedef struct _VuoSerial_internal
39 {
40  VuoText devicePath;
41  dispatch_queue_t queue;
42  VuoTriggerSet<VuoData> triggerSet;
43  dispatch_source_t source;
44  int fileHandle;
46 
47 
48 static dispatch_queue_t VuoSerial_pendingDevicesQueue;
49 static std::set<VuoSerial_internal> VuoSerial_pendingDevices;
50 
51 
55 static void __attribute__((constructor)) VuoSerialIO_init()
56 {
57  VuoSerial_pendingDevicesQueue = dispatch_queue_create("org.vuo.serial.pendingDevicesQueue", NULL);
58 }
59 
60 
67 {
68  ssize_t bytesRead = 0;
69  int fd;
70  int bytesAvailable = dispatch_source_get_data(si->source);
71  if (bytesAvailable == 0)
72  goto fail;
73 
74  fd = dispatch_source_get_handle(si->source);
75  VuoData data;
76  data.data = (char *)malloc(bytesAvailable);
77  VuoRegister(data.data, free);
78  bytesRead = read(fd, data.data, bytesAvailable);
79  if (bytesRead < 1)
80  {
81  VuoRetain(data.data);
82  VuoRelease(data.data);
83  goto fail;
84  }
85 
86  data.size = bytesRead;
87 
88 #if 0
89 {
90  char *hex = (char *)malloc(data.size * 4 + 2);
91  bzero(hex, data.size * 4 + 2);
92  for(int i = 0; i < data.size; ++i)
93  {
94  hex[i] = isprint(data.data[i]) ? data.data[i] : '_';
95  sprintf(hex + data.size + 1 + i*3, "%02x ", (unsigned char)data.data[i]);
96  }
97  hex[data.size] = '\t';
98  VUserLog("%4lld:\t%s", data.size, hex);
99  free(hex);
100 }
101 #endif
102 
103  si->triggerSet.fire(data);
104  return;
105 
106 fail:
107 // if (bytesRead == 0)
108 // VLog("Error: End of file");
109 // else
110 // VLog("Error: %s", strerror(errno));
111 
112  dispatch_source_cancel(si->source);
113  dispatch_release(si->source);
114  si->source = NULL;
115 
116  dispatch_async(VuoSerial_pendingDevicesQueue, ^{
117  VuoSerial_pendingDevices.insert(si);
118  });
119 }
120 
121 
126 {
127  si->source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, si->fileHandle, 0, si->queue);
128  dispatch_source_set_event_handler(si->source, ^{
129  VuoSerial_processPacket(si);
130  });
131  dispatch_source_set_cancel_handler(si->source, ^{
132  close(si->fileHandle);
133  });
134  dispatch_resume(si->source);
135 }
136 
140 static int VuoSerial_openDevice(const char *devicePath)
141 {
142  return open(devicePath,
143  O_RDWR // In case the composition author ever creates both a Send and Receive node for the same device.
144  | O_NOCTTY // Never change this process's controlling terminal.
145  | O_NONBLOCK // Avoid blocking while opening (if the device is exclusively locked by another process, that could take a long time, and VuoRunner might think the process has hung).
146  | O_EXLOCK // Prevent corruption if multiple processes try to read from the same serial device (each process only gets some of the data).
147  );
148 }
149 
156 {
157  dispatch_async(VuoSerial_pendingDevicesQueue, ^{
158  for (std::set<VuoSerial_internal>::iterator i = VuoSerial_pendingDevices.begin(); i != VuoSerial_pendingDevices.end();)
159  {
160  (*i)->fileHandle = VuoSerial_openDevice((*i)->devicePath);
161  if ((*i)->fileHandle < 0)
162  {
163  ++i;
164  continue;
165  }
166 
168  VuoSerial_pendingDevices.erase(i++);
169  }
170  });
171 }
172 
173 
176 static void VuoSerial_destroy(VuoSerial_internal si);
177 VuoSerial_internal VuoSerial_make(std::string devicePath)
178 {
179 // VLog("%s", devicePath.c_str());
180 
181  VuoSerial_use();
182 
183  VuoSerial_internal si = new _VuoSerial_internal;
185 
186  si->devicePath = VuoText_make(devicePath.c_str());
187  VuoRetain(si->devicePath);
188 
189  si->queue = dispatch_queue_create("org.vuo.serial", NULL);
190 
191  si->source = NULL;
192 
193  si->fileHandle = VuoSerial_openDevice(si->devicePath);
194  if (si->fileHandle >= 0)
196  else
197  {
198  VUserLog("Warning: Serial device '%s' isn't currently available (%s). I'll keep trying.", si->devicePath, strerror(errno));
199  dispatch_async(VuoSerial_pendingDevicesQueue, ^{
200  VuoSerial_pendingDevices.insert(si);
201  });
202  }
203 
204  return si;
205 }
207 {
208 // VLog("%s", si->devicePath);
209 
210  dispatch_sync(VuoSerial_pendingDevicesQueue, ^{
211  VuoSerial_pendingDevices.erase(si);
212  });
213 
214  if (si->source)
215  {
216  dispatch_sync(si->queue, ^{
217  dispatch_source_cancel(si->source);
218  dispatch_release(si->source);
219  });
220  }
221 
222  dispatch_release(si->queue);
223 
224  VuoSerial_internalPool->removeSharedInstance(si->devicePath);
225 
226  VuoRelease(si->devicePath);
227  delete si;
228 
230 }
234 
235 
240 {
241  return (VuoSerial)VuoSerial_internalPool->getSharedInstance(devicePath);
242 }
243 
244 
251 {
252  if (!device)
253  return;
254 
256 // VLog("%s", si->devicePath);
257  si->triggerSet.addTrigger(receivedData);
258 }
259 
260 
267 {
268  if (!device)
269  return;
270 
272 // VLog("%s", si->devicePath);
273  si->triggerSet.removeTrigger(receivedData);
274 }
275 
279 void VuoSerial_sendData(VuoSerial device, const VuoData data)
280 {
281  if (!device || !data.size || !data.data)
282  return;
283 
285  dispatch_async(si->queue, ^{
286  ssize_t ret = write(si->fileHandle, data.data, data.size);
287  if (ret == -1)
288  VUserLog("Error: %s", strerror(errno));
289  else if (ret != data.size)
290  VUserLog("Warning: Could only write %ld bytes (of %lld bytes total).", ret, data.size);
291  });
292 }
293 
297 void VuoSerial_configureDevice(VuoSerial device, VuoInteger baudRate, VuoInteger dataBits, VuoParity parity, VuoInteger stopBits)
298 {
299  if (!device)
300  return;
301 
303 // VLog("%s", si->devicePath);
304 
305 
306  // Configure baud rate.
307  if (ioctl(si->fileHandle, IOSSIOSPEED, &baudRate) == -1)
308  VUserLog("Couldn't set %s to baud rate %lld: %s", si->devicePath, baudRate, strerror(errno));
309 
310 
311  // Get the current options.
312  struct termios options;
313  if (tcgetattr(si->fileHandle, &options) == -1)
314  {
315  VUserLog("Couldn't get TTY attributes for %s: %s", si->devicePath, strerror(errno));
316  return;
317  }
318 
319 
320  // Configure data bits.
321  options.c_cflag &= ~CSIZE;
322  if (dataBits == 5)
323  options.c_cflag |= CS5;
324  else if (dataBits == 6)
325  options.c_cflag |= CS6;
326  else if (dataBits == 7)
327  options.c_cflag |= CS7;
328  else if (dataBits == 8)
329  options.c_cflag |= CS8;
330  else
331  VUserLog("Couldn't set %s dataBits to %lld.", si->devicePath, dataBits);
332 
333 
334  // Configure parity.
335  if (parity == VuoParity_None)
336  options.c_cflag &= ~PARENB; // Clear the Parity Enable bit.
337  else if (parity == VuoParity_Even)
338  {
339  options.c_cflag |= PARENB; // Set the Parity Enable bit.
340  options.c_cflag &= ~PARODD; // Clear the Parity Odd bit.
341  }
342  else if (parity == VuoParity_Odd)
343  options.c_cflag |= PARENB | PARODD; // Set the Parity Enable and Parity Odd bits.
344  else
345  VUserLog("Couldn't set %s parity to %d.", si->devicePath, parity);
346 
347 
348  // Configure stop bits.
349  if (stopBits == 1)
350  options.c_cflag &= ~CSTOPB;
351  else if (stopBits == 2)
352  options.c_cflag |= CSTOPB;
353  else
354  VUserLog("Couldn't set %s stopBits to %lld.", si->devicePath, stopBits);
355 
356 
357  // Apply configuration immediately.
358  if (tcsetattr(si->fileHandle, TCSANOW, &options) == -1)
359  VUserLog("Couldn't configure %s: %s", si->devicePath, strerror(errno));
360 }