@jason2866/serialport-bindings-cpp 0.0.0-development

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,482 @@
1
+ #include "serialport_unix.h"
2
+ #include "serialport.h"
3
+
4
+ #include <sys/file.h>
5
+ #include <unistd.h>
6
+ #include <fcntl.h>
7
+ #include <errno.h>
8
+ #include <termios.h>
9
+
10
+ #ifdef __APPLE__
11
+ #include <AvailabilityMacros.h>
12
+ #include <sys/param.h>
13
+ #endif
14
+
15
+ #if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4)
16
+ #include <sys/ioctl.h>
17
+ #include <IOKit/serial/ioss.h>
18
+
19
+ #elif defined(__NetBSD__)
20
+ #include <sys/ioctl.h>
21
+
22
+ #elif defined(__OpenBSD__)
23
+ #include <sys/ioctl.h>
24
+
25
+ #elif defined(__linux__)
26
+ #include <sys/ioctl.h>
27
+ #include <linux/serial.h>
28
+ #include "serialport_linux.h"
29
+ #endif
30
+
31
+ int ToStopBitsConstant(SerialPortStopBits stopBits);
32
+
33
+ int ToBaudConstant(int baudRate) {
34
+ switch (baudRate) {
35
+ case 0: return B0;
36
+ case 50: return B50;
37
+ case 75: return B75;
38
+ case 110: return B110;
39
+ case 134: return B134;
40
+ case 150: return B150;
41
+ case 200: return B200;
42
+ case 300: return B300;
43
+ case 600: return B600;
44
+ case 1200: return B1200;
45
+ case 1800: return B1800;
46
+ case 2400: return B2400;
47
+ case 4800: return B4800;
48
+ case 9600: return B9600;
49
+ case 19200: return B19200;
50
+ case 38400: return B38400;
51
+ case 57600: return B57600;
52
+ case 115200: return B115200;
53
+ case 230400: return B230400;
54
+ #if defined(__linux__)
55
+ case 460800: return B460800;
56
+ case 500000: return B500000;
57
+ case 576000: return B576000;
58
+ case 921600: return B921600;
59
+ case 1000000: return B1000000;
60
+ case 1152000: return B1152000;
61
+ case 1500000: return B1500000;
62
+ case 2000000: return B2000000;
63
+ case 2500000: return B2500000;
64
+ case 3000000: return B3000000;
65
+ case 3500000: return B3500000;
66
+ case 4000000: return B4000000;
67
+ #endif
68
+ }
69
+ return -1;
70
+ }
71
+
72
+ int ToDataBitsConstant(int dataBits) {
73
+ switch (dataBits) {
74
+ case 8: default: return CS8;
75
+ case 7: return CS7;
76
+ case 6: return CS6;
77
+ case 5: return CS5;
78
+ }
79
+ return -1;
80
+ }
81
+
82
+ void OpenBaton::Execute() {
83
+
84
+ int flags = (O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC | O_SYNC);
85
+ int fd = open(path, flags);
86
+
87
+ if (-1 == fd) {
88
+ snprintf(errorString, sizeof(errorString), "Error: %s, cannot open %s", strerror(errno), path);
89
+ this->SetError(errorString);
90
+ return;
91
+ }
92
+
93
+ if (-1 == setup(fd, this)) {
94
+ this->SetError(errorString);
95
+ close(fd);
96
+ return;
97
+ }
98
+
99
+ result = fd;
100
+ }
101
+
102
+ void ConnectionOptionsBaton::Execute() {
103
+ // lookup the standard baudrates from the table
104
+ int baudRate = ToBaudConstant(this->baudRate);
105
+
106
+ // get port options
107
+ struct termios options;
108
+ if (-1 == tcgetattr(fd, &options)) {
109
+ snprintf(errorString, sizeof(errorString), "Error: %s setting custom baud rate of %d", strerror(errno), baudRate);
110
+ this->SetError(errorString);
111
+ return;
112
+ }
113
+
114
+ // If there is a custom baud rate on linux you can do the following trick with B38400
115
+ #if defined(__linux__) && defined(ASYNC_SPD_CUST)
116
+ if (baudRate == -1) {
117
+ int err = linuxSetCustomBaudRate(fd, this->baudRate);
118
+
119
+ if (err == -1) {
120
+ snprintf(errorString, sizeof(errorString), "Error: %s || while retrieving termios2 info", strerror(errno));
121
+ this->SetError(errorString);
122
+ return;
123
+ } else if (err == -2) {
124
+ snprintf(errorString, sizeof(errorString), "Error: %s || while setting custom baud rate of %d", strerror(errno), this->baudRate);
125
+ this->SetError(errorString);
126
+ return;
127
+ }
128
+
129
+ return;
130
+ }
131
+ #endif
132
+
133
+ // On OS X, starting with Tiger, we can set a custom baud rate with ioctl
134
+ #if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4)
135
+ if (-1 == baudRate) {
136
+ speed_t speed = this->baudRate;
137
+ if (-1 == ioctl(fd, IOSSIOSPEED, &speed)) {
138
+ snprintf(errorString, sizeof(errorString), "Error: %s calling ioctl(.., IOSSIOSPEED, %ld )", strerror(errno), speed);
139
+ this->SetError(errorString);
140
+ return;
141
+ } else {
142
+ tcflush(fd, TCIOFLUSH);
143
+ return;
144
+ }
145
+ }
146
+ #endif
147
+
148
+ if (-1 == baudRate) {
149
+ snprintf(errorString, sizeof(errorString), "Error baud rate of %d is not supported on your platform", baudRate);
150
+ this->SetError(errorString);
151
+ return;
152
+ }
153
+
154
+ // If we have a good baud rate set it and lets go
155
+ cfsetospeed(&options, baudRate);
156
+ cfsetispeed(&options, baudRate);
157
+ // throw away all the buffered data
158
+ tcflush(fd, TCIOFLUSH);
159
+ // make the changes now
160
+ tcsetattr(fd, TCSANOW, &options);
161
+ return;
162
+ }
163
+
164
+ int setup(int fd, OpenBaton *data) {
165
+ int dataBits = ToDataBitsConstant(data->dataBits);
166
+ if (-1 == dataBits) {
167
+ snprintf(data->errorString, sizeof(data->errorString),"Invalid data bits setting %d", data->dataBits);
168
+ return -1;
169
+ }
170
+
171
+ // Snow Leopard doesn't have O_CLOEXEC
172
+ if (-1 == fcntl(fd, F_SETFD, FD_CLOEXEC)) {
173
+ snprintf(data->errorString, sizeof(data->errorString), "Error %s Cannot open %s", strerror(errno), data->path);
174
+ return -1;
175
+ }
176
+
177
+ // Get port configuration for modification
178
+ struct termios options;
179
+ tcgetattr(fd, &options);
180
+
181
+ // IGNPAR: ignore bytes with parity errors
182
+ options.c_iflag = IGNPAR;
183
+
184
+ // ICRNL: map CR to NL (otherwise a CR input on the other computer will not terminate input)
185
+ // Future potential option
186
+ // options.c_iflag = ICRNL;
187
+ // otherwise make device raw (no other input processing)
188
+
189
+ // Specify data bits
190
+ options.c_cflag &= ~CSIZE;
191
+ options.c_cflag |= dataBits;
192
+
193
+ options.c_cflag &= ~(CRTSCTS);
194
+
195
+ if (data->rtscts) {
196
+ options.c_cflag |= CRTSCTS;
197
+ // evaluate specific flow control options
198
+ }
199
+
200
+ options.c_iflag &= ~(IXON | IXOFF | IXANY);
201
+
202
+ if (data->xon) {
203
+ options.c_iflag |= IXON;
204
+ }
205
+
206
+ if (data->xoff) {
207
+ options.c_iflag |= IXOFF;
208
+ }
209
+
210
+ if (data->xany) {
211
+ options.c_iflag |= IXANY;
212
+ }
213
+
214
+ switch (data->parity) {
215
+ case SERIALPORT_PARITY_NONE:
216
+ options.c_cflag &= ~PARENB;
217
+ // options.c_cflag &= ~CSTOPB;
218
+ // options.c_cflag &= ~CSIZE;
219
+ // options.c_cflag |= CS8;
220
+ break;
221
+ case SERIALPORT_PARITY_ODD:
222
+ options.c_cflag |= PARENB;
223
+ options.c_cflag |= PARODD;
224
+ // options.c_cflag &= ~CSTOPB;
225
+ // options.c_cflag &= ~CSIZE;
226
+ // options.c_cflag |= CS7;
227
+ break;
228
+ case SERIALPORT_PARITY_EVEN:
229
+ options.c_cflag |= PARENB;
230
+ options.c_cflag &= ~PARODD;
231
+ // options.c_cflag &= ~CSTOPB;
232
+ // options.c_cflag &= ~CSIZE;
233
+ // options.c_cflag |= CS7;
234
+ break;
235
+ default:
236
+ snprintf(data->errorString, sizeof(data->errorString), "Invalid parity setting %d", data->parity);
237
+ return -1;
238
+ }
239
+
240
+ switch (data->stopBits) {
241
+ case SERIALPORT_STOPBITS_ONE:
242
+ options.c_cflag &= ~CSTOPB;
243
+ break;
244
+ case SERIALPORT_STOPBITS_TWO:
245
+ options.c_cflag |= CSTOPB;
246
+ break;
247
+ default:
248
+ snprintf(data->errorString, sizeof(data->errorString), "Invalid stop bits setting %d", data->stopBits);
249
+ return -1;
250
+ }
251
+
252
+ options.c_cflag |= CLOCAL; // ignore status lines
253
+ options.c_cflag |= CREAD; // enable receiver
254
+ if (data->hupcl) {
255
+ options.c_cflag |= HUPCL; // drop DTR (i.e. hangup) on close
256
+ }
257
+
258
+ // Raw output
259
+ options.c_oflag = 0;
260
+
261
+ // ICANON makes partial lines not readable. It should be optional.
262
+ // It works with ICRNL.
263
+ options.c_lflag = 0; // ICANON;
264
+ options.c_cc[VMIN]= data->vmin;
265
+ options.c_cc[VTIME]= data->vtime;
266
+
267
+ // Note that tcsetattr() returns success if any of the requested changes could be successfully carried out.
268
+ // Therefore, when making multiple changes it may be necessary to follow this call with a further call to
269
+ // tcgetattr() to check that all changes have been performed successfully.
270
+ // This also fails on OSX
271
+ tcsetattr(fd, TCSANOW, &options);
272
+
273
+ if (data->lock) {
274
+ if (-1 == flock(fd, LOCK_EX | LOCK_NB)) {
275
+ snprintf(data->errorString, sizeof(data->errorString), "Error %s Cannot lock port", strerror(errno));
276
+ return -1;
277
+ }
278
+ }
279
+
280
+ // Copy the connection options into the ConnectionOptionsBaton to set the baud rate
281
+ ConnectionOptions* connectionOptions = new ConnectionOptions();
282
+ connectionOptions->fd = fd;
283
+ connectionOptions->baudRate = data->baudRate;
284
+
285
+ if (-1 == setBaudRate(connectionOptions)) {
286
+ strncpy(data->errorString, connectionOptions->errorString, sizeof(data->errorString));
287
+ delete(connectionOptions);
288
+ return -1;
289
+ }
290
+ delete(connectionOptions);
291
+
292
+ // flush all unread and wrote data up to this point because it could have been received or sent with bad settings
293
+ // Not needed since setBaudRate does this for us
294
+ // tcflush(fd, TCIOFLUSH);
295
+
296
+ return 1;
297
+ }
298
+
299
+ int setBaudRate(ConnectionOptions *data) {
300
+ // lookup the standard baudrates from the table
301
+ int baudRate = ToBaudConstant(data->baudRate);
302
+ int fd = data->fd;
303
+
304
+ // get port options
305
+ struct termios options;
306
+ if (-1 == tcgetattr(fd, &options)) {
307
+ snprintf(data->errorString, sizeof(data->errorString),
308
+ "Error: %s setting custom baud rate of %d", strerror(errno), data->baudRate);
309
+ return -1;
310
+ }
311
+
312
+ // If there is a custom baud rate on linux you can do the following trick with B38400
313
+ #if defined(__linux__) && defined(ASYNC_SPD_CUST)
314
+ if (baudRate == -1) {
315
+ int err = linuxSetCustomBaudRate(fd, data->baudRate);
316
+
317
+ if (err == -1) {
318
+ snprintf(data->errorString, sizeof(data->errorString),
319
+ "Error: %s || while retrieving termios2 info", strerror(errno));
320
+ return -1;
321
+ } else if (err == -2) {
322
+ snprintf(data->errorString, sizeof(data->errorString),
323
+ "Error: %s || while setting custom baud rate of %d", strerror(errno), data->baudRate);
324
+ return -1;
325
+ }
326
+
327
+ return 1;
328
+ }
329
+ #endif
330
+
331
+ // On OS X, starting with Tiger, we can set a custom baud rate with ioctl
332
+ #if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4)
333
+ if (-1 == baudRate) {
334
+ speed_t speed = data->baudRate;
335
+ if (-1 == ioctl(fd, IOSSIOSPEED, &speed)) {
336
+ snprintf(data->errorString, sizeof(data->errorString),
337
+ "Error: %s calling ioctl(.., IOSSIOSPEED, %ld )", strerror(errno), speed);
338
+ return -1;
339
+ } else {
340
+ tcflush(fd, TCIOFLUSH);
341
+ return 1;
342
+ }
343
+ }
344
+ #endif
345
+
346
+ if (-1 == baudRate) {
347
+ snprintf(data->errorString, sizeof(data->errorString), "Error baud rate of %d is not supported on your platform", data->baudRate);
348
+ return -1;
349
+ }
350
+
351
+ // If we have a good baud rate set it and lets go
352
+ cfsetospeed(&options, baudRate);
353
+ cfsetispeed(&options, baudRate);
354
+ // throw away all the buffered data
355
+ tcflush(fd, TCIOFLUSH);
356
+ // make the changes now
357
+ tcsetattr(fd, TCSANOW, &options);
358
+ return 1;
359
+ }
360
+
361
+ void CloseBaton::Execute() {
362
+
363
+ if (-1 == close(fd)) {
364
+ snprintf(errorString, sizeof(errorString), "Error: %s, unable to close fd %d", strerror(errno), fd);
365
+ this->SetError(errorString);
366
+ }
367
+ }
368
+
369
+ void SetBaton::Execute() {
370
+
371
+ int bits;
372
+ ioctl(fd, TIOCMGET, &bits);
373
+
374
+ bits &= ~(TIOCM_RTS | TIOCM_CTS | TIOCM_DTR | TIOCM_DSR);
375
+
376
+ if (rts) {
377
+ bits |= TIOCM_RTS;
378
+ }
379
+
380
+ if (cts) {
381
+ bits |= TIOCM_CTS;
382
+ }
383
+
384
+ if (dtr) {
385
+ bits |= TIOCM_DTR;
386
+ }
387
+
388
+ if (dsr) {
389
+ bits |= TIOCM_DSR;
390
+ }
391
+
392
+ int result = 0;
393
+ if (brk) {
394
+ result = ioctl(fd, TIOCSBRK, NULL);
395
+ } else {
396
+ result = ioctl(fd, TIOCCBRK, NULL);
397
+ }
398
+
399
+ if (-1 == result) {
400
+ snprintf(errorString, sizeof(errorString), "Error: %s, cannot set", strerror(errno));
401
+ this->SetError(errorString);
402
+ return;
403
+ }
404
+
405
+ if (-1 == ioctl(fd, TIOCMSET, &bits)) {
406
+ snprintf(errorString, sizeof(errorString), "Error: %s, cannot set", strerror(errno));
407
+ this->SetError(errorString);
408
+ return;
409
+ }
410
+
411
+ #if defined(__linux__)
412
+ int err = linuxSetLowLatencyMode(fd, lowLatency);
413
+ // Only report errors when the lowLatency is being set to true. Attempting to set as false can error, since the default is false
414
+ if (lowLatency) {
415
+ if (err == -1) {
416
+ snprintf(errorString, sizeof(errorString), "Error: %s, cannot get low latency", strerror(errno));
417
+ return;
418
+ } else if(err == -2) {
419
+ snprintf(errorString, sizeof(errorString), "Error: %s, cannot set low latency", strerror(errno));
420
+ return;
421
+ }
422
+ }
423
+ #endif
424
+ }
425
+
426
+ void GetBaton::Execute() {
427
+ int bits;
428
+ if (-1 == ioctl(fd, TIOCMGET, &bits)) {
429
+ snprintf(errorString, sizeof(errorString), "Error: %s, cannot get", strerror(errno));
430
+ this->SetError(errorString);
431
+ return;
432
+ }
433
+
434
+ cts = bits & TIOCM_CTS;
435
+ dsr = bits & TIOCM_DSR;
436
+ dcd = bits & TIOCM_CD;
437
+
438
+ #if defined(__linux__) && defined(ASYNC_LOW_LATENCY)
439
+ bool lowlatency = false;
440
+ // Try to get low latency info, but we don't care if fails (a failure state will still return lowlatency = false)
441
+ linuxGetLowLatencyMode(fd, &lowlatency);
442
+ lowLatency = lowlatency;
443
+ #else
444
+ lowLatency = false;
445
+ #endif
446
+ }
447
+
448
+ void GetBaudRateBaton::Execute() {
449
+ int outbaud = -1;
450
+
451
+ #if defined(__linux__) && defined(ASYNC_SPD_CUST)
452
+ if (-1 == linuxGetSystemBaudRate(fd, &outbaud)) {
453
+ snprintf(errorString, sizeof(errorString), "Error: %s, cannot get baud rate", strerror(errno));
454
+ this->SetError(errorString);
455
+ return;
456
+ }
457
+ #else
458
+ snprintf(errorString, sizeof(errorString), "Error: System baud rate check not implemented on this platform");
459
+ this->SetError(errorString);
460
+ return;
461
+ #endif
462
+
463
+ baudRate = outbaud;
464
+ }
465
+
466
+ void FlushBaton::Execute() {
467
+
468
+ if (-1 == tcflush(fd, TCIOFLUSH)) {
469
+ snprintf(errorString, sizeof(errorString), "Error: %s, cannot flush", strerror(errno));
470
+ this->SetError(errorString);
471
+ return;
472
+ }
473
+ }
474
+
475
+ void DrainBaton::Execute() {
476
+
477
+ if (-1 == tcdrain(fd)) {
478
+ snprintf(errorString, sizeof(errorString), "Error: %s, cannot drain", strerror(errno));
479
+ this->SetError(errorString);
480
+ return;
481
+ }
482
+ }
@@ -0,0 +1,7 @@
1
+ #ifndef PACKAGES_SERIALPORT_SRC_SERIALPORT_UNIX_H_
2
+ #define PACKAGES_SERIALPORT_SRC_SERIALPORT_UNIX_H_
3
+
4
+ int ToBaudConstant(int baudRate);
5
+ int ToDataBitsConstant(int dataBits);
6
+
7
+ #endif // PACKAGES_SERIALPORT_SRC_SERIALPORT_UNIX_H_