MSDN says: "The baud rate at which the communications device operates. This member can be an actual baud rate value, or one of the following indexes." But it is not specified what happens for different values, so we round down to valid ones (or to the minimum of 9600) by reusing the existing struct baudentry sp_baudtable[] and the new function round_baud().
Do similarly on non-Windows where arbitrary values are not allowed anyway.
Signed-off-by: Stefan Tauner stefan.tauner@student.tuwien.ac.at --- serial.c | 97 ++++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 57 insertions(+), 40 deletions(-)
diff --git a/serial.c b/serial.c index 18562be..dd3471d 100644 --- a/serial.c +++ b/serial.c @@ -47,62 +47,89 @@ void __attribute__((noreturn)) sp_die(char *msg) exit(1); }
-#ifndef _WIN32 +#ifdef _WIN32 +struct baudentry { + DWORD flag; + unsigned int baud; +}; +#define BAUDENTRY(baud) { CBR_##baud, baud }, +#else struct baudentry { int flag; unsigned int baud; }; - -/* I'd like if the C preprocessor could have directives in macros */ #define BAUDENTRY(baud) { B##baud, baud }, +#endif + +/* I'd like if the C preprocessor could have directives in macros. + * See TERMIOS(3) and http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214(v=vs.85).as... and + * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=blob;f=includ... */ static const struct baudentry sp_baudtable[] = { - BAUDENTRY(9600) + BAUDENTRY(9600) /* unconditional default */ +#if defined(B19200) || defined(CBR_19200) BAUDENTRY(19200) +#endif +#if defined(B38400) || defined(CBR_38400) BAUDENTRY(38400) +#endif +#if defined(B57600) || defined(CBR_57600) BAUDENTRY(57600) +#endif +#if defined(B115200) || defined(CBR_115200) BAUDENTRY(115200) -#ifdef B230400 +#endif +#if defined(B230400) || defined(CBR_230400) BAUDENTRY(230400) #endif -#ifdef B460800 +#if defined(B460800) || defined(CBR_460800) BAUDENTRY(460800) #endif -#ifdef B500000 +#if defined(B500000) || defined(CBR_500000) BAUDENTRY(500000) #endif -#ifdef B576000 +#if defined(B576000) || defined(CBR_576000) BAUDENTRY(576000) #endif -#ifdef B921600 +#if defined(B921600) || defined(CBR_921600) BAUDENTRY(921600) #endif -#ifdef B1000000 +#if defined(B1000000) || defined(CBR_1000000) BAUDENTRY(1000000) #endif -#ifdef B1152000 +#if defined(B1152000) || defined(CBR_1152000) BAUDENTRY(1152000) #endif -#ifdef B1500000 +#if defined(B1500000) || defined(CBR_1500000) BAUDENTRY(1500000) #endif -#ifdef B2000000 +#if defined(B2000000) || defined(CBR_2000000) BAUDENTRY(2000000) #endif -#ifdef B2500000 +#if defined(B2500000) || defined(CBR_2500000) BAUDENTRY(2500000) #endif -#ifdef B3000000 +#if defined(B3000000) || defined(CBR_3000000) BAUDENTRY(3000000) #endif -#ifdef B3500000 +#if defined(B3500000) || defined(CBR_3500000) BAUDENTRY(3500000) #endif -#ifdef B4000000 +#if defined(B4000000) || defined(CBR_4000000) BAUDENTRY(4000000) #endif {0, 0} /* Terminator */ }; -#endif + +const struct baudentry *round_baud(unsigned int baud) +{ + int i; + /* Round baud rate to next lower entry in sp_baudtable if it exists, else use the lowest entry. */ + for (i = ARRAY_SIZE(sp_baudtable) - 2; i >= 0 ; i--) { + if (sp_baudtable[i].baud <= baud) + return &sp_baudtable[i]; + } + return &sp_baudtable[0]; +}
/* Uses msg_perr to print the last system error. * Prints "Error: " followed first by \c msg and then by the description of the last error retrieved via @@ -155,15 +182,8 @@ fdtype sp_openserport(char *dev, unsigned int baud) msg_perr_strerror("Could not fetch serial port configuration: "); return SER_INV_FD; } - switch (baud) { - case 9600: dcb.BaudRate = CBR_9600; break; - case 19200: dcb.BaudRate = CBR_19200; break; - case 38400: dcb.BaudRate = CBR_38400; break; - case 57600: dcb.BaudRate = CBR_57600; break; - case 115200: dcb.BaudRate = CBR_115200; break; - default: msg_perr("Error: Could not set baud rate: %s\n", strerror(errno)); - return SER_INV_FD; - } + const struct baudentry *entry = round_baud(baud); + dcb.BaudRate = entry->baud; dcb.ByteSize = 8; dcb.Parity = NOPARITY; dcb.StopBits = ONESTOPBIT; @@ -171,10 +191,15 @@ fdtype sp_openserport(char *dev, unsigned int baud) msg_perr_strerror("Could not change serial port configuration: "); return SER_INV_FD; } + if (!GetCommState(fd, &dcb)) { + msg_perr_strerror("Could not fetch serial port configuration: "); + return SER_INV_FD; + } + msg_pdbg("Baud rate is %ld.\n", dcb.BaudRate); return fd; #else struct termios options; - int fd, i; + int fd; fd = open(dev, O_RDWR | O_NOCTTY | O_NDELAY); if (fd < 0) { msg_perr_strerror("Cannot open serial port: "); @@ -182,18 +207,10 @@ fdtype sp_openserport(char *dev, unsigned int baud) } fcntl(fd, F_SETFL, 0); tcgetattr(fd, &options); - for (i = 0;; i++) { - if (sp_baudtable[i].baud == 0) { - close(fd); - msg_perr("Error: cannot configure for baudrate %d\n", baud); - return SER_INV_FD; - } - if (sp_baudtable[i].baud == baud) { - cfsetispeed(&options, sp_baudtable[i].flag); - cfsetospeed(&options, sp_baudtable[i].flag); - break; - } - } + const struct baudentry *entry = round_baud(baud); + cfsetispeed(&options, entry->flag); + cfsetospeed(&options, entry->flag); + msg_pdbg("Setting baud rate to %d.\n", entry->baud); options.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS); options.c_cflag |= (CS8 | CLOCAL | CREAD); options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);