]> sigrok.org Git - libserialport.git/commitdiff
Support custom baudrates on Linux.
authorMartin Ling <redacted>
Thu, 21 Nov 2013 00:35:51 +0000 (00:35 +0000)
committerMartin Ling <redacted>
Thu, 21 Nov 2013 11:51:08 +0000 (11:51 +0000)
Makefile.am
configure.ac
linux_termios.c [new file with mode: 0644]
linux_termios.h [new file with mode: 0644]
serialport.c

index af4f1b3bfbba75afd71dd95941f17b3a3fb9fd26..0bf14538e2f662abd3a2fc6c6562f002856e5293 100644 (file)
@@ -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)
 
index 91174fc6a8fbde25ca686e33f0cf70b42fcf9f9d..a9be4576b4c2cbf3b78ef9ff012c30866e361428 100644 (file)
@@ -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 <linux/termios.h>]])
 
 # Checks for library functions.
 AC_CHECK_FUNCS([strerror])
diff --git a/linux_termios.c b/linux_termios.c
new file mode 100644 (file)
index 0000000..68c98e0
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * This file is part of the libserialport project.
+ *
+ * Copyright (C) 2013 Martin Ling <martin-libserialport@earth.li>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/termios.h>
+#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 (file)
index 0000000..8e6d202
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * This file is part of the libserialport project.
+ *
+ * Copyright (C) 2013 Martin Ling <martin-libserialport@earth.li>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+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);
index 8408ec7efb7d892d06399e329c9d00041fd7926f..a2cff495707146adb42e215d90899948c5e1cb1a 100644 (file)
@@ -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;