Lely core libraries 1.9.2
serial.c
Go to the documentation of this file.
1
24#include "attr.h"
25#include "default.h"
26#include "io.h"
27#include <lely/io/serial.h>
28
29#include <assert.h>
30#include <string.h>
31
32#if defined(_WIN32) || _POSIX_C_SOURCE >= 200112L
33
34static int serial_flush(struct io_handle *handle);
35static int serial_purge(struct io_handle *handle, int flags);
36
37static const struct io_handle_vtab serial_vtab = { .type = IO_TYPE_SERIAL,
38 .size = sizeof(struct io_handle),
39 .fini = &default_fini,
40 .flags = &default_flags,
41 .read = &default_read,
42 .write = &default_write,
43 .flush = &serial_flush,
44 .purge = &serial_purge };
45
47io_open_serial(const char *path, io_attr_t *attr)
48{
49 assert(path);
50
51 int errc = 0;
52
53#ifdef _WIN32
54 HANDLE fd = CreateFileA(path, GENERIC_READ | GENERIC_WRITE, 0, NULL,
55 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
56 if (__unlikely(fd == INVALID_HANDLE_VALUE)) {
57 errc = get_errc();
58 goto error_CreateFile;
59 }
60
61 if (__unlikely(!SetCommMask(fd, EV_RXCHAR))) {
62 errc = get_errc();
63 goto error_SetCommMask;
64 }
65
66 DCB DCB;
67 memset(&DCB, 0, sizeof(DCB));
68 DCB.DCBlength = sizeof(DCB);
69 if (__unlikely(!GetCommState(fd, &DCB))) {
70 errc = get_errc();
71 goto error_GetCommState;
72 }
73
74 COMMTIMEOUTS CommTimeouts;
75 if (__unlikely(!GetCommTimeouts(fd, &CommTimeouts))) {
76 errc = get_errc();
77 goto error_GetCommTimeouts;
78 }
79
80 if (attr) {
81 *io_attr_lpDCB(attr) = DCB;
82 *io_attr_lpCommTimeouts(attr) = CommTimeouts;
83 }
84
85 DCB.fBinary = TRUE;
86 DCB.fParity = FALSE;
87 DCB.fOutxCtsFlow = FALSE;
88 DCB.fOutxDsrFlow = FALSE;
89 DCB.fDtrControl = DTR_CONTROL_ENABLE;
90 DCB.fDsrSensitivity = FALSE;
91 DCB.fTXContinueOnXoff = TRUE;
92 DCB.fOutX = FALSE;
93 DCB.fInX = FALSE;
94 DCB.fErrorChar = FALSE;
95 DCB.fNull = FALSE;
96 DCB.fRtsControl = RTS_CONTROL_ENABLE;
97 DCB.fAbortOnError = TRUE;
98 DCB.ByteSize = 8;
99 DCB.Parity = NOPARITY;
100
101 if (__unlikely(!SetCommState(fd, &DCB))) {
102 errc = get_errc();
103 goto error_SetCommState;
104 }
105
106 // Block on reading by waiting as long as possible (MAXDWORD - 1
107 // milliseconds) until at least one bytes arrives. serial_read()
108 // contains a loop to make this time infinite.
109 CommTimeouts.ReadIntervalTimeout = MAXDWORD;
110 CommTimeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
111 CommTimeouts.ReadTotalTimeoutConstant = MAXDWORD - 1;
112 // Do not use timeouts for write operations.
113 CommTimeouts.WriteTotalTimeoutMultiplier = 0;
114 CommTimeouts.WriteTotalTimeoutConstant = 0;
115
116 if (__unlikely(!SetCommTimeouts(fd, &CommTimeouts))) {
117 errc = get_errc();
118 goto error_SetCommTimeouts;
119 }
120#else
121 int fd;
122 int errsv = errno;
123 do {
124 errno = errsv;
125 fd = open(path, O_RDWR | O_NOCTTY | O_CLOEXEC);
126 } while (__unlikely(fd == -1 && errno == EINTR));
127 if (__unlikely(fd == -1)) {
128 errc = get_errc();
129 goto error_open;
130 }
131
132 struct termios ios;
133 if (__unlikely(tcgetattr(fd, &ios) == -1)) {
134 errc = get_errc();
135 goto error_tcgetattr;
136 }
137
138 if (attr)
139 *(struct termios *)attr = ios;
140
141 // These options are taken from cfmakeraw() on BSD.
142 ios.c_iflag &= ~(BRKINT | ICRNL | IGNBRK | IGNCR | INLCR | ISTRIP | IXON
143 | PARMRK);
144 ios.c_oflag &= ~OPOST;
145 ios.c_cflag &= ~(CSIZE | PARENB);
146 ios.c_cflag |= CS8;
147 ios.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
148
149 ios.c_iflag |= IGNPAR;
150 ios.c_cflag |= CREAD | CLOCAL;
151
152 ios.c_cc[VMIN] = 1;
153 ios.c_cc[VTIME] = 0;
154
155 if (__unlikely(tcsetattr(fd, TCSANOW, &ios) == -1)) {
156 errc = get_errc();
157 goto error_tcsetattr;
158 }
159#endif
160
161 struct io_handle *handle = io_handle_alloc(&serial_vtab);
162 if (__unlikely(!handle)) {
163 errc = get_errc();
164 goto error_alloc_handle;
165 }
166
167 handle->fd = fd;
168
169 return io_handle_acquire(handle);
170
171error_alloc_handle:
172#ifdef _WIN32
173error_SetCommTimeouts:
174error_SetCommState:
175error_GetCommTimeouts:
176error_GetCommState:
177error_SetCommMask:
178 CloseHandle(fd);
179error_CreateFile:
180#else
181error_tcsetattr:
182error_tcgetattr:
183 close(fd);
184error_open:
185#endif
186 set_errc(errc);
187 return IO_HANDLE_ERROR;
188}
189
190#endif // _WIN32 || _POSIX_C_SOURCE >= 200112L
191
192int
194{
195 if (__unlikely(handle == IO_HANDLE_ERROR)) {
197 return -1;
198 }
199
200 assert(handle->vtab);
201 if (__unlikely(!handle->vtab->purge)) {
203 return -1;
204 }
205
206 return handle->vtab->purge(handle, flags);
207}
208
209#if defined(_WIN32) || _POSIX_C_SOURCE >= 200112L
210
211int
213{
214 assert(attr);
215
216 if (__unlikely(handle == IO_HANDLE_ERROR)) {
218 return -1;
219 }
220
221#ifdef _WIN32
222 LPDCB lpDCB = io_attr_lpDCB(attr);
223 memset(lpDCB, 0, sizeof(*lpDCB));
224 lpDCB->DCBlength = sizeof(*lpDCB);
225 if (__unlikely(!GetCommState(handle->fd, lpDCB)))
226 return -1;
227
228 // clang-format off
229 return GetCommTimeouts(handle->fd, io_attr_lpCommTimeouts(attr))
230 ? 0 : -1;
231 // clang-format on
232#else
233 return tcgetattr(handle->fd, (struct termios *)attr);
234#endif
235}
236
237int
239{
240 assert(attr);
241
242 if (__unlikely(handle == IO_HANDLE_ERROR)) {
244 return -1;
245 }
246
247#ifdef _WIN32
248 if (__unlikely(!SetCommState(handle->fd, io_attr_lpDCB(attr))))
249 return -1;
250
251 // clang-format off
252 if (__unlikely(!SetCommTimeouts(
253 handle->fd, io_attr_lpCommTimeouts(attr))))
254 // clang-format on
255 return -1;
256
257 return 0;
258#else
259 int result;
260 int errsv = errno;
261 do {
262 errno = errsv;
263 result = tcsetattr(handle->fd, TCSANOW,
264 (const struct termios *)attr);
265 } while (__unlikely(result == -1 && errno == EINTR));
266 return result;
267#endif
268}
269
270static int
271serial_flush(struct io_handle *handle)
272{
273 assert(handle);
274
275#ifdef _WIN32
276 return FlushFileBuffers(handle->fd) ? 0 : -1;
277#else
278 int result;
279 int errsv = errno;
280 do {
281 errno = errsv;
282 result = tcdrain(handle->fd);
283 } while (__unlikely(result == -1 && errno == EINTR));
284 return result;
285#endif
286}
287
288static int
289serial_purge(struct io_handle *handle, int flags)
290{
291 assert(handle);
292
293#ifdef _WIN32
294 DWORD dwFlags = 0;
295 if (flags & IO_PURGE_RX)
296 dwFlags |= PURGE_RXABORT | PURGE_RXCLEAR;
297 if (flags & IO_PURGE_TX)
298 dwFlags |= PURGE_TXABORT | PURGE_TXCLEAR;
299
300 return PurgeComm(handle->fd, dwFlags) ? 0 : -1;
301#else
302 // clang-format off
303 return tcflush(handle->fd, (flags & IO_PURGE_RX)
304 ? (flags & IO_PURGE_TX) ? TCIOFLUSH : TCIFLUSH
305 : (flags & IO_PURGE_TX) ? TCOFLUSH : 0);
306 // clang-format on
307#endif
308}
309
310#endif // _WIN32 || _POSIX_C_SOURCE >= 200112L
This is the internal header file of the default implementation of the I/O device handle methods.
@ ERRNUM_BADF
Bad file descriptor.
Definition: errnum.h:90
@ ERRNUM_NOTTY
Inappropriate I/O control operation.
Definition: errnum.h:195
int get_errc(void)
Returns the last (thread-specific) native error code set by a system call or library function.
Definition: errnum.c:947
void set_errc(int errc)
Sets the current (thread-specific) native error code to errc.
Definition: errnum.c:957
void set_errnum(errnum_t errnum)
Sets the current (thread-specific) platform-independent error number to errnum.
Definition: errnum.h:375
#define __unlikely(x)
Indicates to the compiler that the expression is most-likely false.
Definition: features.h:286
struct io_handle * io_handle_alloc(const struct io_handle_vtab *vtab)
Allocates a new I/O device handle from a virtual table.
Definition: handle.c:77
@ IO_TYPE_SERIAL
A serial I/O device.
Definition: io.h:53
io_handle_t io_handle_acquire(io_handle_t handle)
Increments the reference count of an I/O device handle.
Definition: handle.c:32
#define IO_HANDLE_ERROR
The value of an invalid I/O device handle.
Definition: io.h:34
int io_serial_set_attr(io_handle_t handle, const io_attr_t *attr)
Sets the attributes of a serial I/O device to those in *attr.
Definition: serial.c:238
io_handle_t io_open_serial(const char *path, io_attr_t *attr)
Opens a serial I/O device.
Definition: serial.c:47
int io_purge(io_handle_t handle, int flags)
Purges the receive and/or transmit buffers of a serial I/O device.
Definition: serial.c:193
int io_serial_get_attr(io_handle_t handle, io_attr_t *attr)
Retrieves the current attributes of a serial I/O device and stores them in *attr.
Definition: serial.c:212
This header file is part of the I/O library; it contains the serial I/O declarations.
@ IO_PURGE_TX
Purge the transmit buffer of a serial I/O device.
Definition: serial.h:31
@ IO_PURGE_RX
Purge the receive buffer of a serial I/O device.
Definition: serial.h:29
This is the internal header file of the serial I/O attributes declarations.
This is the internal header file of the I/O library.
This header file is part of the C11 and POSIX compatibility library; it includes <string....
The virtual table of an I/O device handle.
Definition: handle.h:74
int(* purge)(struct io_handle *handle, int flags)
A pointer to the purge method.
Definition: handle.h:102
int type
The type of the device (one of IO_TYPE_CAN, IO_TYPE_FILE, IO_TYPE_PIPE, IO_TYPE_SERIAL or IO_TYPE_SOC...
Definition: handle.h:79
An I/O device handle.
Definition: handle.h:41
int fd
The native file descriptor.
Definition: handle.h:56
int flags
The I/O device flags (any combination of IO_FLAG_NO_CLOSE and IO_FLAG_NONBLOCK).
Definition: handle.h:62
const struct io_handle_vtab * vtab
A pointer to the virtual table.
Definition: handle.h:43
An opaque serial I/O device attributes type.
Definition: attr.h:34