Lely core libraries  2.2.5
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 (fd == INVALID_HANDLE_VALUE) {
57  errc = get_errc();
58  goto error_CreateFile;
59  }
60 
61  if (!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 (!GetCommState(fd, &DCB)) {
70  errc = get_errc();
71  goto error_GetCommState;
72  }
73 
74  COMMTIMEOUTS CommTimeouts;
75  if (!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 (!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 (!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 (fd == -1 && errno == EINTR);
127  if (fd == -1) {
128  errc = get_errc();
129  goto error_open;
130  }
131 
132  struct termios ios;
133  if (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 (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 (!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 (handle == IO_HANDLE_ERROR) {
197  return -1;
198  }
199 
200  assert(handle->vtab);
201  if (!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 (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 (!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 (handle == IO_HANDLE_ERROR) {
244  return -1;
245  }
246 
247 #ifdef _WIN32
248  if (!SetCommState(handle->fd, io_attr_lpDCB(attr)))
249  return -1;
250 
251  if (!SetCommTimeouts(handle->fd, io_attr_lpCommTimeouts(attr)))
252  return -1;
253 
254  return 0;
255 #else
256  int result;
257  int errsv = errno;
258  do {
259  errno = errsv;
260  result = tcsetattr(handle->fd, TCSANOW,
261  (const struct termios *)attr);
262  } while (result == -1 && errno == EINTR);
263  return result;
264 #endif
265 }
266 
267 static int
268 serial_flush(struct io_handle *handle)
269 {
270  assert(handle);
271 
272 #ifdef _WIN32
273  return FlushFileBuffers(handle->fd) ? 0 : -1;
274 #else
275  int result;
276  int errsv = errno;
277  do {
278  errno = errsv;
279  result = tcdrain(handle->fd);
280  } while (result == -1 && errno == EINTR);
281  return result;
282 #endif
283 }
284 
285 static int
286 serial_purge(struct io_handle *handle, int flags)
287 {
288  assert(handle);
289 
290 #ifdef _WIN32
291  DWORD dwFlags = 0;
292  if (flags & IO_PURGE_RX)
293  dwFlags |= PURGE_RXABORT | PURGE_RXCLEAR;
294  if (flags & IO_PURGE_TX)
295  dwFlags |= PURGE_TXABORT | PURGE_TXCLEAR;
296 
297  return PurgeComm(handle->fd, dwFlags) ? 0 : -1;
298 #else
299  // clang-format off
300  return tcflush(handle->fd, (flags & IO_PURGE_RX)
301  ? (flags & IO_PURGE_TX) ? TCIOFLUSH : TCIFLUSH
302  : (flags & IO_PURGE_TX) ? TCOFLUSH : 0);
303  // clang-format on
304 #endif
305 }
306 
307 #endif // _WIN32 || _POSIX_C_SOURCE >= 200112L
io_open_serial
io_handle_t io_open_serial(const char *path, io_attr_t *attr)
Opens a serial I/O device.
Definition: serial.c:47
IO_PURGE_RX
@ IO_PURGE_RX
Purge the receive buffer of a serial I/O device.
Definition: serial.h:29
string.h
io_handle_vtab::type
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
io_serial_set_attr
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
get_errc
int get_errc(void)
Returns the last (thread-specific) native error code set by a system call or library function.
Definition: errnum.c:947
io_handle
An I/O device handle.
Definition: handle.h:41
io_handle_acquire
io_handle_t io_handle_acquire(io_handle_t handle)
Increments the reference count of an I/O device handle.
Definition: handle.c:32
ERRNUM_BADF
@ ERRNUM_BADF
Bad file descriptor.
Definition: errnum.h:90
IO_PURGE_TX
@ IO_PURGE_TX
Purge the transmit buffer of a serial I/O device.
Definition: serial.h:31
io_serial_get_attr
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
io_handle_alloc
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
set_errnum
void set_errnum(errnum_t errnum)
Sets the current (thread-specific) platform-independent error number to errnum.
Definition: errnum.h:375
set_errc
void set_errc(int errc)
Sets the current (thread-specific) native error code to errc.
Definition: errnum.c:957
ERRNUM_NOTTY
@ ERRNUM_NOTTY
Inappropriate I/O control operation.
Definition: errnum.h:195
io_purge
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.h
attr.h
IO_HANDLE_ERROR
#define IO_HANDLE_ERROR
The value of an invalid I/O device handle.
Definition: io.h:34
io_handle::vtab
const struct io_handle_vtab * vtab
A pointer to the virtual table.
Definition: handle.h:43
io_handle::flags
int flags
The I/O device flags (any combination of IO_FLAG_NO_CLOSE and IO_FLAG_NONBLOCK).
Definition: handle.h:62
serial.h
io_handle_vtab::purge
int(* purge)(struct io_handle *handle, int flags)
A pointer to the purge method.
Definition: handle.h:102
default.h
io_handle_vtab
The virtual table of an I/O device handle.
Definition: handle.h:74
io_handle::fd
int fd
The native file descriptor.
Definition: handle.h:56
IO_TYPE_SERIAL
@ IO_TYPE_SERIAL
A serial I/O device.
Definition: io.h:53
__io_attr
An opaque serial I/O device attributes type.
Definition: attr.h:34