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