Vuo 2.4.2
Loading...
Searching...
No Matches
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
20extern "C"
21{
22
23#ifdef VUO_COMPILER
25 "title" : "VuoSerialIO",
26 "dependencies" : [
27 "VuoSerialDevice",
28 "VuoList_VuoSerialDevice"
29 ]
30 });
31#endif
32}
33
34
38typedef 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
48static dispatch_queue_t VuoSerial_pendingDevicesQueue;
49static std::set<VuoSerial_internal> VuoSerial_pendingDevices;
50
51
55static 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
106fail:
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
140static 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
177VuoSerial_internal VuoSerial_make(std::string devicePath)
178{
179// VLog("%s", devicePath.c_str());
180
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
279void 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
297void 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}