From: Martin Ling Date: Thu, 21 Nov 2013 00:35:51 +0000 (+0000) Subject: Support custom baudrates on Linux. X-Git-Tag: libserialport-0.1.0~91 X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=7a6d2196e043a7de86a5675f9fffa0ff0ec33e72;p=libserialport.git Support custom baudrates on Linux. --- diff --git a/Makefile.am b/Makefile.am index af4f1b3..0bf1453 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,7 +24,7 @@ AM_CPPFLAGS = -I$(top_srcdir) lib_LTLIBRARIES = libserialport.la -libserialport_la_SOURCES = serialport.c +libserialport_la_SOURCES = serialport.c linux_termios.c libserialport_la_LIBADD = $(LIBOBJS) diff --git a/configure.ac b/configure.ac index 91174fc..a9be457 100644 --- a/configure.ac +++ b/configure.ac @@ -107,6 +107,7 @@ AC_CHECK_HEADERS([errno.h fcntl.h stddef.h sys/ioctl.h termios.h]) AC_C_INLINE AC_TYPE_SIZE_T AC_TYPE_SSIZE_T +AC_CHECK_TYPE([struct termios2],[AC_DEFINE(HAVE_TERMIOS2, 1)],[],[[#include ]]) # Checks for library functions. AC_CHECK_FUNCS([strerror]) diff --git a/linux_termios.c b/linux_termios.c new file mode 100644 index 0000000..68c98e0 --- /dev/null +++ b/linux_termios.c @@ -0,0 +1,73 @@ +/* + * This file is part of the libserialport project. + * + * Copyright (C) 2013 Martin Ling + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include +#include "linux_termios.h" + +int get_termios_get_ioctl(void) +{ +#ifdef HAVE_TERMIOS2 + return TCGETS2; +#else + return TCGETS; +#endif +} + +int get_termios_set_ioctl(void) +{ +#ifdef HAVE_TERMIOS2 + return TCSETS2; +#else + return TCSETS; +#endif +} + +int get_termios_size(void) +{ +#ifdef HAVE_TERMIOS2 + return sizeof(struct termios2); +#else + return sizeof(struct termios); +#endif +} + +int get_termios_speed(void *data) +{ +#ifdef HAVE_TERMIOS2 + struct termios2 *term = (struct termios2 *) data; +#else + struct termios *term = (struct termios *) data; +#endif + if (term->c_ispeed != term->c_ospeed) + return -1; + else + return term->c_ispeed; +} + +void set_termios_speed(void *data, int speed) +{ +#ifdef HAVE_TERMIOS2 + struct termios2 *term = (struct termios2 *) data; +#else + struct termios *term = (struct termios *) data; +#endif + term->c_cflag &= ~CBAUD; + term->c_cflag |= BOTHER; + term->c_ispeed = term->c_ospeed = speed; +} diff --git a/linux_termios.h b/linux_termios.h new file mode 100644 index 0000000..8e6d202 --- /dev/null +++ b/linux_termios.h @@ -0,0 +1,24 @@ +/* + * This file is part of the libserialport project. + * + * Copyright (C) 2013 Martin Ling + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +int get_termios_get_ioctl(void); +int get_termios_set_ioctl(void); +int get_termios_size(void); +int get_termios_speed(void *data); +void set_termios_speed(void *data, int speed); diff --git a/serialport.c b/serialport.c index 8408ec7..a2cff49 100644 --- a/serialport.c +++ b/serialport.c @@ -44,6 +44,7 @@ #ifdef __linux__ #include "libudev.h" #include "linux/serial.h" +#include "linux_termios.h" #endif #include "libserialport.h" @@ -94,6 +95,8 @@ const struct std_baudrate std_baudrates[] = { #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) #define NUM_STD_BAUDRATES ARRAY_SIZE(std_baudrates) +#define TRY(x) do { int ret = x; if (ret != SP_OK) return ret; } while (0) + /* Helper functions. */ static enum sp_return validate_port(struct sp_port *port); static struct sp_port **list_append(struct sp_port **list, const char *portname); @@ -558,6 +561,41 @@ enum sp_return sp_read(struct sp_port *port, void *buf, size_t count) #endif } +#ifdef __linux__ +static enum sp_return get_baudrate(int fd, int *baudrate) +{ + void *data; + + if (!(data = malloc(get_termios_size()))) + return SP_ERR_MEM; + + if (ioctl(fd, get_termios_get_ioctl(), data) < 0) + return SP_ERR_FAIL; + + *baudrate = get_termios_speed(data); + + return SP_OK; +} + +static enum sp_return set_baudrate(int fd, int baudrate) +{ + void *data; + + if (!(data = malloc(get_termios_size()))) + return SP_ERR_MEM; + + if (ioctl(fd, get_termios_get_ioctl(), data) < 0) + return SP_ERR_FAIL; + + set_termios_speed(data, baudrate); + + if (ioctl(fd, get_termios_set_ioctl(), data) < 0) + return SP_ERR_FAIL; + + return SP_OK; +} +#endif + static enum sp_return get_config(struct sp_port *port, struct port_data *data, struct sp_port_config *config) { @@ -669,6 +707,8 @@ static enum sp_return get_config(struct sp_port *port, struct port_data *data, if (i == NUM_STD_BAUDRATES) { #ifdef __APPLE__ config->baudrate = (int)data->term.c_ispeed; +#elif defined(__linux__) + TRY(get_baudrate(port->fd, &config->baudrate)); #else config->baudrate = -1; #endif @@ -727,7 +767,7 @@ static enum sp_return get_config(struct sp_port *port, struct port_data *data, return SP_OK; } -static enum sp_return set_config(struct sp_port *port, struct port_data *data, +static enum sp_return set_config(struct sp_port *port, struct port_data *data, const struct sp_port_config *config) { unsigned int i; @@ -736,6 +776,9 @@ static enum sp_return set_config(struct sp_port *port, struct port_data *data, baud_nonstd = B0; #endif +#ifdef __linux__ + int baud_nonstd = 0; +#endif #ifdef _WIN32 if (config->baudrate >= 0) { @@ -870,6 +913,8 @@ static enum sp_return set_config(struct sp_port *port, struct port_data *data, #else /* !_WIN32 */ + int controlbits; + if (config->baudrate >= 0) { for (i = 0; i < NUM_STD_BAUDRATES; i++) { if (config->baudrate == std_baudrates[i].value) { @@ -889,6 +934,8 @@ static enum sp_return set_config(struct sp_port *port, struct port_data *data, if (cfsetspeed(&data->term, B9600) < 0) return SP_ERR_FAIL; baud_nonstd = config->baudrate; +#elif defined(__linux__) + baud_nonstd = 1; #else return SP_ERR_ARG; #endif @@ -971,7 +1018,7 @@ static enum sp_return set_config(struct sp_port *port, struct port_data *data, if (config->rts == SP_RTS_FLOW_CONTROL) { data->term.c_iflag |= CRTSCTS; } else { - int controlbits = TIOCM_RTS; + controlbits = TIOCM_RTS; if (ioctl(port->fd, config->rts == SP_RTS_ON ? TIOCMBIS : TIOCMBIC, &controlbits) < 0) return SP_ERR_FAIL; @@ -985,7 +1032,7 @@ static enum sp_return set_config(struct sp_port *port, struct port_data *data, return SP_ERR_ARG; if (config->dtr >= 0) { - int controlbits = TIOCM_DTR; + controlbits = TIOCM_DTR; if (ioctl(port->fd, config->dtr == SP_DTR_ON ? TIOCMBIS : TIOCMBIC, &controlbits) < 0) return SP_ERR_FAIL; @@ -1023,15 +1070,16 @@ static enum sp_return set_config(struct sp_port *port, struct port_data *data, if (cfsetspeed(&data->term, baud_nonstd) < 0) return SP_ERR_FAIL; } -#endif /* __APPLE__ */ +#elif defined(__linux__) + if (baud_nonstd) + TRY(set_baudrate(port->fd, config->baudrate)); +#endif #endif /* !_WIN32 */ return SP_OK; } -#define TRY(x) do { int ret = x; if (ret != SP_OK) return ret; } while (0) - enum sp_return sp_set_config(struct sp_port *port, const struct sp_port_config *config) { struct port_data data;