diff --git a/ChangeLog b/ChangeLog index 42a6795d..b229f2e1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2014-11-25 Joerg Wunsch + + patch #8380: adds 500k 1M 2M baud to ser_posix.c + * ser_posix.c: Add a hack to allow for arbitrary baud rates on + Linux + 2014-11-25 Joerg Wunsch patch #8437: [PATCH] Serial-over-ethernet for Win32 diff --git a/NEWS b/NEWS index d2be83fd..32453c2b 100644 --- a/NEWS +++ b/NEWS @@ -28,6 +28,9 @@ Current: - The "-P net:" syntax (forwarding of serial data over TCP) is now also implemented for Win32 systems. + - Allow for arbitrary serial baudrates under Linux (OSX and *BSD + could already handle it). + * New devices supported: - AT90PWM216 (bug #42310: New part description for AT90PWM216) @@ -57,6 +60,7 @@ Current: - bug #40870: config nitpick: ATtiny25/45/85 have 1 calibration byte not 2 - bug #42908: no external reset at JTAGICE3 - patch #8437: [PATCH] Serial-over-ethernet for Win32 + - patch #8380: adds 500k 1M 2M baud to ser_posix.c * Internals: - Removing exit calls from config parser diff --git a/ser_posix.c b/ser_posix.c index beeb9bdc..20230f9b 100644 --- a/ser_posix.c +++ b/ser_posix.c @@ -37,6 +37,9 @@ #include #include #include +#ifdef __linux__ +#include +#endif #include #include @@ -52,8 +55,13 @@ struct baud_mapping { speed_t speed; }; -/* There are a lot more baud rates we could handle, but what's the point? */ +static struct termios original_termios; +static int saved_original_termios; +#if !defined __linux__ +/* For linux this mapping is no longer needed. + * (OSX and *BSD do not need this mapping either because for them, + * Bxxx is the same as xxx.) */ static struct baud_mapping baud_lookup_table [] = { { 1200, B1200 }, { 2400, B2400 }, @@ -73,8 +81,6 @@ static struct baud_mapping baud_lookup_table [] = { { 0, 0 } /* Terminator. */ }; -static struct termios original_termios; -static int saved_original_termios; static speed_t serial_baud_lookup(long baud) { @@ -95,12 +101,19 @@ static speed_t serial_baud_lookup(long baud) return baud; } +#endif static int ser_setspeed(union filedescriptor *fd, long baud) { int rc; struct termios termios; +#if defined __linux__ + /* for linux no conversion is needed*/ + speed_t speed = baud; +#else + /* converting the baud rate to the bit set needed by posix way*/ speed_t speed = serial_baud_lookup (baud); +#endif if (!isatty(fd->ifd)) return -ENOTTY; @@ -128,16 +141,79 @@ static int ser_setspeed(union filedescriptor *fd, long baud) termios.c_cflag = (CS8 | CREAD | CLOCAL); termios.c_cc[VMIN] = 1; termios.c_cc[VTIME] = 0; +#ifdef __linux__ + /* Support for custom baud rate for linux is implemented by setting + * a dummy baud rate of 38400 and manupulating the custom divider of + * the serial interface*/ + struct serial_struct ss; + int ioret = ioctl(fd->ifd, TIOCGSERIAL, &ss); + if (ioret < 0){ + avrdude_message(MSG_INFO, + "%s: Cannot get serial port settings. ioctl returned %d\n", + progname, ioret); + return -errno; + } + ss.flags = (ss.flags & ~ASYNC_SPD_MASK) | ASYNC_SPD_CUST; + ss.custom_divisor = (ss.baud_base + (speed / 2)) / speed; + unsigned int closestSpeed = ss.baud_base / ss.custom_divisor; - cfsetospeed(&termios, speed); - cfsetispeed(&termios, speed); - + if (closestSpeed < speed * 98 / 100 || closestSpeed > speed * 102 / 100) { + avrdude_message(MSG_INFO, + "%s: Cannot set serial port speed to %d. Closest possible is %d\n", + progname, speed, closestSpeed); + return -errno; + } + ioret= ioctl(fd->ifd, TIOCSSERIAL, &ss); + if (ioret < 0){ + avrdude_message(MSG_INFO, + "%s: Cannot set serial port speed to %d. ioctl returned %d\n", + progname, speed, ioret); + return -errno; + } + if (cfsetispeed(&termios, B38400) < 0){ + avrdude_message(MSG_INFO, + "%s: cfsetispeed: failed to set dummy baud\n", + progname); + return -errno; + } + if (cfsetospeed(&termios, B38400) < 0){ + avrdude_message(MSG_INFO, + "%s: cfsetospeed: failed to set dummy baud\n", + progname); + return -errno; + } +#else /* !linux */ + if (cfsetospeed(&termios, speed) < 0){ + avrdude_message(MSG_INFO, + "%s: cfsetospeed: failed to set speed: %d\n", + progname, speed); + return -errno; + } + if (cfsetispeed(&termios, speed) < 0){ + avrdude_message(MSG_INFO, + "%s: cfsetispeed: failed to set speed: %d\n", + progname, speed); + return -errno; + } +#endif /* linux */ rc = tcsetattr(fd->ifd, TCSANOW, &termios); if (rc < 0) { avrdude_message(MSG_INFO, "%s: ser_setspeed(): tcsetattr() failed\n", progname); return -errno; } +#ifdef __linux__ + /* a bit more linux specific stuff to set custom baud rates*/ + if (ioctl(fd->ifd, TIOCGSERIAL, &ss) < 0){ + avrdude_message(MSG_INFO, "%s: ioctl: failed to get port settins\n", progname); + return -errno; + } + ss.flags &= ~ASYNC_SPD_MASK; + if (ioctl(fd->ifd, TIOCSSERIAL, &ss) < 0){ + avrdude_message(MSG_INFO, "%s: ioctl: failed to set port settins\n", progname); + return -errno; + } +#endif /* * Everything is now set up for a local line without modem control