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 
34 static int serial_flush(struct io_handle *handle);
35 static int serial_purge(struct io_handle *handle, int flags);
36 
37 static 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 
47 io_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 
171 error_alloc_handle:
172 #ifdef _WIN32
173 error_SetCommTimeouts:
174 error_SetCommState:
175 error_GetCommTimeouts:
176 error_GetCommState:
177 error_SetCommMask:
178  CloseHandle(fd);
179 error_CreateFile:
180 #else
181 error_tcsetattr:
182 error_tcgetattr:
183  close(fd);
184 error_open:
185 #endif
186  set_errc(errc);
187  return IO_HANDLE_ERROR;
188 }
189 
190 #endif // _WIN32 || _POSIX_C_SOURCE >= 200112L
191 
192 int
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 
211 int
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 
237 int
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 
270 static int
271 serial_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 
288 static int
289 serial_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
Inappropriate I/O control operation.
Definition: errnum.h:195
This header file is part of the C11 and POSIX compatibility library; it includes <string.h> and defines any missing functionality.
void set_errnum(errnum_t errnum)
Sets the current (thread-specific) platform-independent error number to errnum.
Definition: errnum.h:375
This is the internal header file of the serial I/O attributes declarations.
This is the internal header file of the default implementation of the I/O device handle methods...
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
A serial I/O device.
Definition: io.h:53
void set_errc(int errc)
Sets the current (thread-specific) native error code to errc.
Definition: errnum.c:957
io_handle_t io_handle_acquire(io_handle_t handle)
Increments the reference count of an I/O device handle.
Definition: handle.c:32
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
io_handle_t io_open_serial(const char *path, io_attr_t *attr)
Opens a serial I/O device.
Definition: serial.c:47
An opaque serial I/O device attributes type.
Definition: attr.h:34
#define IO_HANDLE_ERROR
The value of an invalid I/O device handle.
Definition: io.h:34
const struct io_handle_vtab * vtab
A pointer to the virtual table.
Definition: handle.h:43
int get_errc(void)
Returns the last (thread-specific) native error code set by a system call or library function...
Definition: errnum.c:947
The virtual table of an I/O device handle.
Definition: handle.h:74
This header file is part of the I/O library; it contains the serial I/O declarations.
Purge the receive buffer of a serial I/O device.
Definition: serial.h:29
int(* purge)(struct io_handle *handle, int flags)
A pointer to the purge method.
Definition: handle.h:102
#define __unlikely(x)
Indicates to the compiler that the expression is most-likely false.
Definition: features.h:286
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 is the internal header file of the I/O library.
int flags
The I/O device flags (any combination of IO_FLAG_NO_CLOSE and IO_FLAG_NONBLOCK).
Definition: handle.h:62
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
Purge the transmit buffer of a serial I/O device.
Definition: serial.h:31
Bad file descriptor.
Definition: errnum.h:90
int fd
The native file descriptor.
Definition: handle.h:56
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
An I/O device handle.
Definition: handle.h:41