#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#ifdef HAVE_LINUX_SERIAL_H
#include <linux/serial.h>
#endif
#ifdef HAVE_IOKIT_SERIAL_IOSS_H
#include <IOKit/serial/ioss.h>
#endif
#include <sys/types.h>
#include <dirent.h>
#include <fnmatch.h>
#ifndef TIOCINQ
#define TIOCINQ FIONREAD
#endif
#ifdef ENABLE_PTY
#define NOPTY (errno != EINVAL && errno != ENOTTY)
#else
#define NOPTY 1
#endif
#include <libdivecomputer/serial.h>
#include "common-private.h"
#include "context-private.h"
#include "iostream-private.h"
#include "iterator-private.h"
#include "descriptor-private.h"
#include "platform.h"
#include "timer.h"
#define DIRNAME "/dev"
static dc_status_t dc_serial_iterator_next (dc_iterator_t *iterator, void *item);
static dc_status_t dc_serial_iterator_free (dc_iterator_t *iterator);
static dc_status_t dc_serial_set_timeout (dc_iostream_t *iostream, int timeout);
static dc_status_t dc_serial_set_break (dc_iostream_t *iostream, unsigned int value);
static dc_status_t dc_serial_set_dtr (dc_iostream_t *iostream, unsigned int value);
static dc_status_t dc_serial_set_rts (dc_iostream_t *iostream, unsigned int value);
static dc_status_t dc_serial_get_lines (dc_iostream_t *iostream, unsigned int *value);
static dc_status_t dc_serial_get_available (dc_iostream_t *iostream, size_t *value);
static dc_status_t dc_serial_configure (dc_iostream_t *iostream, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol);
static dc_status_t dc_serial_poll (dc_iostream_t *iostream, int timeout);
static dc_status_t dc_serial_read (dc_iostream_t *iostream, void *data, size_t size, size_t *actual);
static dc_status_t dc_serial_write (dc_iostream_t *iostream, const void *data, size_t size, size_t *actual);
static dc_status_t dc_serial_ioctl (dc_iostream_t *iostream, unsigned int request, void *data, size_t size);
static dc_status_t dc_serial_flush (dc_iostream_t *iostream);
static dc_status_t dc_serial_purge (dc_iostream_t *iostream, dc_direction_t direction);
static dc_status_t dc_serial_sleep (dc_iostream_t *iostream, unsigned int milliseconds);
static dc_status_t dc_serial_close (dc_iostream_t *iostream);
struct dc_serial_device_t {
char name[256];
};
typedef struct dc_serial_iterator_t {
dc_iterator_t base;
dc_descriptor_t *descriptor;
DIR *dp;
} dc_serial_iterator_t;
typedef struct dc_serial_t {
dc_iostream_t base;
int fd;
int timeout;
dc_timer_t *timer;
struct termios tty;
} dc_serial_t;
static const dc_iterator_vtable_t dc_serial_iterator_vtable = {
sizeof(dc_serial_iterator_t),
dc_serial_iterator_next,
dc_serial_iterator_free,
};
static const dc_iostream_vtable_t dc_serial_vtable = {
sizeof(dc_serial_t),
dc_serial_set_timeout,
dc_serial_set_break,
dc_serial_set_dtr,
dc_serial_set_rts,
dc_serial_get_lines,
dc_serial_get_available,
dc_serial_configure,
dc_serial_poll,
dc_serial_read,
dc_serial_write,
dc_serial_ioctl,
dc_serial_flush,
dc_serial_purge,
dc_serial_sleep,
dc_serial_close,
};
static dc_status_t
syserror(int errcode)
{
switch (errcode) {
case EINVAL:
return DC_STATUS_INVALIDARGS;
case ENOMEM:
return DC_STATUS_NOMEMORY;
case ENOENT:
return DC_STATUS_NODEVICE;
case EACCES:
case EBUSY:
return DC_STATUS_NOACCESS;
default:
return DC_STATUS_IO;
}
}
const char *
dc_serial_device_get_name (dc_serial_device_t *device)
{
if (device == NULL || device->name[0] == '\0')
return NULL;
return device->name;
}
void
dc_serial_device_free (dc_serial_device_t *device)
{
free (device);
}
dc_status_t
dc_serial_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descriptor_t *descriptor)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_serial_iterator_t *iterator = NULL;
if (out == NULL)
return DC_STATUS_INVALIDARGS;
iterator = (dc_serial_iterator_t *) dc_iterator_allocate (context, &dc_serial_iterator_vtable);
if (iterator == NULL) {
SYSERROR (context, ENOMEM);
return DC_STATUS_NOMEMORY;
}
iterator->dp = opendir (DIRNAME);
if (iterator->dp == NULL) {
int errcode = errno;
SYSERROR (context, errcode);
status = syserror (errcode);
goto error_free;
}
iterator->descriptor = descriptor;
*out = (dc_iterator_t *) iterator;
return DC_STATUS_SUCCESS;
error_free:
dc_iterator_deallocate ((dc_iterator_t *) iterator);
return status;
}
static dc_status_t
dc_serial_iterator_next (dc_iterator_t *abstract, void *out)
{
dc_serial_iterator_t *iterator = (dc_serial_iterator_t *) abstract;
dc_serial_device_t *device = NULL;
struct dirent *ep = NULL;
const char *patterns[] = {
#if defined (__APPLE__)
"tty.*",
#else
"ttyS*",
"ttyUSB*",
"ttyACM*",
"rfcomm*",
#endif
NULL
};
while ((ep = readdir (iterator->dp)) != NULL) {
for (size_t i = 0; patterns[i] != NULL; ++i) {
if (fnmatch (patterns[i], ep->d_name, 0) != 0)
continue;
char filename[sizeof(device->name)];
int n = dc_platform_snprintf (filename, sizeof (filename), "%s/%s", DIRNAME, ep->d_name);
if (n < 0 || (size_t) n >= sizeof (filename)) {
return DC_STATUS_NOMEMORY;
}
if (!dc_descriptor_filter (iterator->descriptor, DC_TRANSPORT_SERIAL, filename, NULL)) {
continue;
}
device = (dc_serial_device_t *) malloc (sizeof(dc_serial_device_t));
if (device == NULL) {
SYSERROR (abstract->context, ENOMEM);
return DC_STATUS_NOMEMORY;
}
strncpy(device->name, filename, sizeof(device->name));
*(dc_serial_device_t **) out = device;
return DC_STATUS_SUCCESS;
}
}
return DC_STATUS_DONE;
}
static dc_status_t
dc_serial_iterator_free (dc_iterator_t *abstract)
{
dc_serial_iterator_t *iterator = (dc_serial_iterator_t *) abstract;
closedir (iterator->dp);
return DC_STATUS_SUCCESS;
}
dc_status_t
dc_serial_open (dc_iostream_t **out, dc_context_t *context, const char *name)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_serial_t *device = NULL;
if (out == NULL || name == NULL)
return DC_STATUS_INVALIDARGS;
INFO (context, "Open: name=%s", name);
device = (dc_serial_t *) dc_iostream_allocate (context, &dc_serial_vtable, DC_TRANSPORT_SERIAL);
if (device == NULL) {
SYSERROR (context, ENOMEM);
return DC_STATUS_NOMEMORY;
}
device->timeout = -1;
status = dc_timer_new (&device->timer);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to create a high resolution timer.");
goto error_free;
}
device->fd = open (name, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (device->fd == -1) {
int errcode = errno;
SYSERROR (context, errcode);
status = syserror (errcode);
goto error_timer_free;
}
#ifndef ENABLE_PTY
if (ioctl (device->fd, TIOCEXCL, NULL) != 0) {
int errcode = errno;
SYSERROR (context, errcode);
status = syserror (errcode);
goto error_close;
}
#endif
if (tcgetattr (device->fd, &device->tty) != 0) {
int errcode = errno;
SYSERROR (context, errcode);
status = syserror (errcode);
goto error_close;
}
*out = (dc_iostream_t *) device;
return DC_STATUS_SUCCESS;
error_close:
close (device->fd);
error_timer_free:
dc_timer_free (device->timer);
error_free:
dc_iostream_deallocate ((dc_iostream_t *) device);
return status;
}
static dc_status_t
dc_serial_close (dc_iostream_t *abstract)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_serial_t *device = (dc_serial_t *) abstract;
if (tcsetattr (device->fd, TCSANOW, &device->tty) != 0) {
int errcode = errno;
SYSERROR (abstract->context, errcode);
dc_status_set_error(&status, syserror (errcode));
}
#ifndef ENABLE_PTY
if (ioctl (device->fd, TIOCNXCL, NULL)) {
int errcode = errno;
SYSERROR (abstract->context, errcode);
dc_status_set_error(&status, syserror (errcode));
}
#endif
if (close (device->fd) != 0) {
int errcode = errno;
SYSERROR (abstract->context, errcode);
dc_status_set_error(&status, syserror (errcode));
}
dc_timer_free (device->timer);
return status;
}
static dc_status_t
dc_serial_configure (dc_iostream_t *abstract, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol)
{
dc_serial_t *device = (dc_serial_t *) abstract;
struct termios tty;
memset (&tty, 0, sizeof (tty));
if (tcgetattr (device->fd, &tty) != 0) {
int errcode = errno;
SYSERROR (abstract->context, errcode);
return syserror (errcode);
}
tty.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | ICRNL);
tty.c_oflag &= ~(OPOST);
tty.c_lflag &= ~(ICANON | ECHO | ISIG | IEXTEN);
tty.c_cflag |= (CLOCAL | CREAD);
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 0;
int custom = 0;
speed_t baud = 0;
switch (baudrate) {
case 0: baud = B0; break;
case 50: baud = B50; break;
case 75: baud = B75; break;
case 110: baud = B110; break;
case 134: baud = B134; break;
case 150: baud = B150; break;
case 200: baud = B200; break;
case 300: baud = B300; break;
case 600: baud = B600; break;
case 1200: baud = B1200; break;
case 1800: baud = B1800; break;
case 2400: baud = B2400; break;
case 4800: baud = B4800; break;
case 9600: baud = B9600; break;
case 19200: baud = B19200; break;
case 38400: baud = B38400; break;
#ifdef B57600
case 57600: baud = B57600; break;
#endif
#ifdef B115200
case 115200: baud = B115200; break;
#endif
#ifdef B230400
case 230400: baud = B230400; break;
#endif
#ifdef B460800
case 460800: baud = B460800; break;
#endif
#ifdef B500000
case 500000: baud = B500000; break;
#endif
#ifdef B576000
case 576000: baud = B576000; break;
#endif
#ifdef B921600
case 921600: baud = B921600; break;
#endif
#ifdef B1000000
case 1000000: baud = B1000000; break;
#endif
#ifdef B1152000
case 1152000: baud = B1152000; break;
#endif
#ifdef B1500000
case 1500000: baud = B1500000; break;
#endif
#ifdef B2000000
case 2000000: baud = B2000000; break;
#endif
#ifdef B2500000
case 2500000: baud = B2500000; break;
#endif
#ifdef B3000000
case 3000000: baud = B3000000; break;
#endif
#ifdef B3500000
case 3500000: baud = B3500000; break;
#endif
#ifdef B4000000
case 4000000: baud = B4000000; break;
#endif
default:
baud = B38400;
custom = 1;
break;
}
if (cfsetispeed (&tty, baud) != 0 ||
cfsetospeed (&tty, baud) != 0) {
int errcode = errno;
SYSERROR (abstract->context, errcode);
return syserror (errcode);
}
tty.c_cflag &= ~CSIZE;
switch (databits) {
case 5:
tty.c_cflag |= CS5;
break;
case 6:
tty.c_cflag |= CS6;
break;
case 7:
tty.c_cflag |= CS7;
break;
case 8:
tty.c_cflag |= CS8;
break;
default:
return DC_STATUS_INVALIDARGS;
}
#ifdef CMSPAR
tty.c_cflag &= ~(PARENB | PARODD | CMSPAR);
#else
tty.c_cflag &= ~(PARENB | PARODD);
#endif
tty.c_iflag &= ~(IGNPAR | PARMRK | INPCK);
switch (parity) {
case DC_PARITY_NONE:
tty.c_iflag |= IGNPAR;
break;
case DC_PARITY_EVEN:
tty.c_cflag |= PARENB;
tty.c_iflag |= INPCK;
break;
case DC_PARITY_ODD:
tty.c_cflag |= (PARENB | PARODD);
tty.c_iflag |= INPCK;
break;
#ifdef CMSPAR
case DC_PARITY_MARK:
tty.c_cflag |= (PARENB | PARODD | CMSPAR);
tty.c_iflag |= INPCK;
break;
case DC_PARITY_SPACE:
tty.c_cflag |= (PARENB | CMSPAR);
tty.c_iflag |= INPCK;
break;
#endif
default:
return DC_STATUS_INVALIDARGS;
}
switch (stopbits) {
case DC_STOPBITS_ONE:
tty.c_cflag &= ~CSTOPB;
break;
case DC_STOPBITS_TWO:
tty.c_cflag |= CSTOPB;
break;
default:
return DC_STATUS_INVALIDARGS;
}
switch (flowcontrol) {
case DC_FLOWCONTROL_NONE:
#ifdef CRTSCTS
tty.c_cflag &= ~CRTSCTS;
#endif
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
break;
case DC_FLOWCONTROL_HARDWARE:
#ifdef CRTSCTS
tty.c_cflag |= CRTSCTS;
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
break;
#else
return DC_STATUS_UNSUPPORTED;
#endif
case DC_FLOWCONTROL_SOFTWARE:
#ifdef CRTSCTS
tty.c_cflag &= ~CRTSCTS;
#endif
tty.c_iflag |= (IXON | IXOFF);
break;
default:
return DC_STATUS_INVALIDARGS;
}
if (tcsetattr (device->fd, TCSANOW, &tty) != 0 && NOPTY) {
int errcode = errno;
SYSERROR (abstract->context, errcode);
return syserror (errcode);
}
if (custom) {
#if defined(TIOCGSERIAL) && defined(TIOCSSERIAL) && !defined(__ANDROID__)
struct serial_struct ss;
if (ioctl (device->fd, TIOCGSERIAL, &ss) != 0 && NOPTY) {
int errcode = errno;
SYSERROR (abstract->context, errcode);
return syserror (errcode);
}
ss.custom_divisor = ss.baud_base / baudrate;
ss.flags &= ~ASYNC_SPD_MASK;
ss.flags |= ASYNC_SPD_CUST;
if (ioctl (device->fd, TIOCSSERIAL, &ss) != 0 && NOPTY) {
int errcode = errno;
SYSERROR (abstract->context, errcode);
return syserror (errcode);
}
#elif defined(IOSSIOSPEED)
speed_t speed = baudrate;
if (ioctl (device->fd, IOSSIOSPEED, &speed) != 0 && NOPTY) {
int errcode = errno;
SYSERROR (abstract->context, errcode);
return syserror (errcode);
}
#else
return DC_STATUS_UNSUPPORTED;
#endif
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
dc_serial_set_timeout (dc_iostream_t *abstract, int timeout)
{
dc_serial_t *device = (dc_serial_t *) abstract;
device->timeout = timeout;
return DC_STATUS_SUCCESS;
}
static dc_status_t
dc_serial_set_latency (dc_iostream_t *abstract, unsigned int milliseconds)
{
dc_serial_t *device = (dc_serial_t *) abstract;
#if defined(TIOCGSERIAL) && defined(TIOCSSERIAL) && !defined(__ANDROID__)
struct serial_struct ss;
if (ioctl (device->fd, TIOCGSERIAL, &ss) != 0 && NOPTY) {
int errcode = errno;
SYSERROR (abstract->context, errcode);
return syserror (errcode);
}
if (milliseconds == 0) {
ss.flags |= ASYNC_LOW_LATENCY;
} else {
ss.flags &= ~ASYNC_LOW_LATENCY;
}
if (ioctl (device->fd, TIOCSSERIAL, &ss) != 0 && NOPTY) {
int errcode = errno;
SYSERROR (abstract->context, errcode);
return syserror (errcode);
}
#elif defined(IOSSDATALAT)
unsigned long usec = (milliseconds == 0 ? 1 : milliseconds * 1000);
if (ioctl (device->fd, IOSSDATALAT, &usec) != 0 && NOPTY) {
int errcode = errno;
SYSERROR (abstract->context, errcode);
return syserror (errcode);
}
#endif
return DC_STATUS_SUCCESS;
}
static dc_status_t
dc_serial_poll (dc_iostream_t *abstract, int timeout)
{
dc_serial_t *device = (dc_serial_t *) abstract;
int rc = 0;
do {
fd_set fds;
FD_ZERO (&fds);
FD_SET (device->fd, &fds);
struct timeval tv, *ptv = NULL;
if (timeout > 0) {
tv.tv_sec = (timeout / 1000);
tv.tv_usec = (timeout % 1000) * 1000;
ptv = &tv;
} else if (timeout == 0) {
tv.tv_sec = 0;
tv.tv_usec = 0;
ptv = &tv;
}
rc = select (device->fd + 1, &fds, NULL, NULL, ptv);
} while (rc < 0 && errno == EINTR);
if (rc < 0) {
int errcode = errno;
SYSERROR (abstract->context, errcode);
return syserror (errcode);
} else if (rc == 0) {
return DC_STATUS_TIMEOUT;
} else {
return DC_STATUS_SUCCESS;
}
}
static dc_status_t
dc_serial_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_serial_t *device = (dc_serial_t *) abstract;
size_t nbytes = 0;
dc_usecs_t target = 0;
int init = 1;
while (nbytes < size) {
fd_set fds;
FD_ZERO (&fds);
FD_SET (device->fd, &fds);
struct timeval tv, *ptv = NULL;
if (device->timeout > 0) {
dc_usecs_t timeout = 0;
dc_usecs_t now = 0;
status = dc_timer_now (device->timer, &now);
if (status != DC_STATUS_SUCCESS) {
goto out;
}
if (init) {
timeout = (dc_usecs_t) device->timeout * 1000;
target = now + timeout;
init = 0;
} else {
if (now < target) {
timeout = target - now;
} else {
timeout = 0;
}
}
tv.tv_sec = timeout / 1000000;
tv.tv_usec = timeout % 1000000;
ptv = &tv;
} else if (device->timeout == 0) {
tv.tv_sec = 0;
tv.tv_usec = 0;
ptv = &tv;
}
int rc = select (device->fd + 1, &fds, NULL, NULL, ptv);
if (rc < 0) {
int errcode = errno;
if (errcode == EINTR)
continue; SYSERROR (abstract->context, errcode);
status = syserror (errcode);
goto out;
} else if (rc == 0) {
break; }
ssize_t n = read (device->fd, (char *) data + nbytes, size - nbytes);
if (n < 0) {
int errcode = errno;
if (errcode == EINTR || errcode == EAGAIN)
continue; SYSERROR (abstract->context, errcode);
status = syserror (errcode);
goto out;
} else if (n == 0) {
break; }
nbytes += n;
}
if (nbytes != size) {
status = DC_STATUS_TIMEOUT;
}
out:
if (actual)
*actual = nbytes;
return status;
}
static dc_status_t
dc_serial_write (dc_iostream_t *abstract, const void *data, size_t size, size_t *actual)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_serial_t *device = (dc_serial_t *) abstract;
size_t nbytes = 0;
while (nbytes < size) {
fd_set fds;
FD_ZERO (&fds);
FD_SET (device->fd, &fds);
int rc = select (device->fd + 1, NULL, &fds, NULL, NULL);
if (rc < 0) {
int errcode = errno;
if (errcode == EINTR)
continue; SYSERROR (abstract->context, errcode);
status = syserror (errcode);
goto out;
} else if (rc == 0) {
break; }
ssize_t n = write (device->fd, (const char *) data + nbytes, size - nbytes);
if (n < 0) {
int errcode = errno;
if (errcode == EINTR || errcode == EAGAIN)
continue; SYSERROR (abstract->context, errcode);
status = syserror (errcode);
goto out;
} else if (n == 0) {
break; }
nbytes += n;
}
#ifdef __ANDROID__
while (ioctl (device->fd, TCSBRK, 1) != 0) {
#else
while (tcdrain (device->fd) != 0) {
#endif
int errcode = errno;
if (errcode != EINTR ) {
SYSERROR (abstract->context, errcode);
status = syserror (errcode);
goto out;
}
}
out:
if (actual)
*actual = nbytes;
return status;
}
static dc_status_t
dc_serial_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size)
{
switch (request) {
case DC_IOCTL_SERIAL_SET_LATENCY:
return dc_serial_set_latency (abstract, *(unsigned int *) data);
default:
return DC_STATUS_UNSUPPORTED;
}
}
static dc_status_t
dc_serial_purge (dc_iostream_t *abstract, dc_direction_t direction)
{
dc_serial_t *device = (dc_serial_t *) abstract;
int flags = 0;
switch (direction) {
case DC_DIRECTION_INPUT:
flags = TCIFLUSH;
break;
case DC_DIRECTION_OUTPUT:
flags = TCOFLUSH;
break;
case DC_DIRECTION_ALL:
flags = TCIOFLUSH;
break;
default:
return DC_STATUS_INVALIDARGS;
}
if (tcflush (device->fd, flags) != 0) {
int errcode = errno;
SYSERROR (abstract->context, errcode);
return syserror (errcode);
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
dc_serial_flush (dc_iostream_t *abstract)
{
return DC_STATUS_SUCCESS;
}
static dc_status_t
dc_serial_set_break (dc_iostream_t *abstract, unsigned int level)
{
dc_serial_t *device = (dc_serial_t *) abstract;
unsigned long action = (level ? TIOCSBRK : TIOCCBRK);
if (ioctl (device->fd, action, NULL) != 0 && NOPTY) {
int errcode = errno;
SYSERROR (abstract->context, errcode);
return syserror (errcode);
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
dc_serial_set_dtr (dc_iostream_t *abstract, unsigned int level)
{
dc_serial_t *device = (dc_serial_t *) abstract;
unsigned long action = (level ? TIOCMBIS : TIOCMBIC);
int value = TIOCM_DTR;
if (ioctl (device->fd, action, &value) != 0 && NOPTY) {
int errcode = errno;
SYSERROR (abstract->context, errcode);
return syserror (errcode);
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
dc_serial_set_rts (dc_iostream_t *abstract, unsigned int level)
{
dc_serial_t *device = (dc_serial_t *) abstract;
unsigned long action = (level ? TIOCMBIS : TIOCMBIC);
int value = TIOCM_RTS;
if (ioctl (device->fd, action, &value) != 0 && NOPTY) {
int errcode = errno;
SYSERROR (abstract->context, errcode);
return syserror (errcode);
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
dc_serial_get_available (dc_iostream_t *abstract, size_t *value)
{
dc_serial_t *device = (dc_serial_t *) abstract;
int bytes = 0;
if (ioctl (device->fd, TIOCINQ, &bytes) != 0) {
int errcode = errno;
SYSERROR (abstract->context, errcode);
return syserror (errcode);
}
if (value)
*value = bytes;
return DC_STATUS_SUCCESS;
}
static dc_status_t
dc_serial_get_lines (dc_iostream_t *abstract, unsigned int *value)
{
dc_serial_t *device = (dc_serial_t *) abstract;
unsigned int lines = 0;
int status = 0;
if (ioctl (device->fd, TIOCMGET, &status) != 0) {
int errcode = errno;
SYSERROR (abstract->context, errcode);
return syserror (errcode);
}
if (status & TIOCM_CAR)
lines |= DC_LINE_DCD;
if (status & TIOCM_CTS)
lines |= DC_LINE_CTS;
if (status & TIOCM_DSR)
lines |= DC_LINE_DSR;
if (status & TIOCM_RNG)
lines |= DC_LINE_RNG;
if (value)
*value = lines;
return DC_STATUS_SUCCESS;
}
static dc_status_t
dc_serial_sleep (dc_iostream_t *abstract, unsigned int timeout)
{
if (dc_platform_sleep (timeout) != 0) {
int errcode = errno;
SYSERROR (abstract->context, errcode);
return syserror (errcode);
}
return DC_STATUS_SUCCESS;
}