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 
38 static int serial_flush(struct io_handle *handle);
39 static int serial_purge(struct io_handle *handle, int flags);
40 
41 static 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 
51 io_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 
175 error_alloc_handle:
176 #if _WIN32
177 error_SetCommTimeouts:
178 error_SetCommState:
179 error_GetCommTimeouts:
180 error_GetCommState:
181 error_SetCommMask:
182  CloseHandle(fd);
183 error_CreateFile:
184 #else
185 error_tcsetattr:
186 error_tcgetattr:
187  close(fd);
188 error_open:
189 #endif
190  set_errc(errc);
191  return IO_HANDLE_ERROR;
192 }
193 
194 #endif // _WIN32 || _POSIX_C_SOURCE >= 200112L
195 
196 int
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 
215 int
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 
241 int
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 
271 static int
272 serial_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 
289 static int
290 serial_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
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:51
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:71
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:242
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:932
io_handle
An I/O device handle.
Definition: handle.h:33
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:36
ERRNUM_BADF
@ ERRNUM_BADF
Bad file descriptor.
Definition: errnum.h:93
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:216
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:81
set_errnum
void set_errnum(errnum_t errnum)
Sets the current (thread-specific) platform-independent error number to errnum.
Definition: errnum.h:424
set_errc
void set_errc(int errc)
Sets the current (thread-specific) native error code to errc.
Definition: errnum.c:944
ERRNUM_NOTTY
@ ERRNUM_NOTTY
Inappropriate I/O control operation.
Definition: errnum.h:198
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:197
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:35
io_handle::flags
int flags
The I/O device flags (any combination of IO_FLAG_NO_CLOSE and IO_FLAG_NONBLOCK).
Definition: handle.h:54
serial.h
io_handle_vtab::purge
int(* purge)(struct io_handle *handle, int flags)
A pointer to the purge method.
Definition: handle.h:94
default.h
io_handle_vtab
The virtual table of an I/O device handle.
Definition: handle.h:66
io_handle::fd
int fd
The native file descriptor.
Definition: handle.h:48
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