What are the ways of setting custom baudrates on Linux?
An answer to this question must be at a level of userland low-level APIs (ioctl
, etc.) above the lev
Things are, unfortunately, driver-dependent. Good drivers will implement all of the methods below. Bad drivers will implement only some of the methods. Thus you need to try them all. All of the methods below are implemented in the helper functions in linux/drivers/tty/serial/serial_core.c.
The following 4 choices are available.
Standard baud rates are set in tty->termios->c_cflag
. You can choose from:
B0
B50
B75
B110
B134
B150
B200
B300
B600
B1200
B1800
B2400
B4800
B9600
B19200
B38400
B57600
B115200
B230400
If you need rates not listed above, e.g. 460800 (this is a deprecated hack that the kernel developers wish to die, per the source code comments):
set tty->termios->c_cflag
speed to B38400
call TIOCSSERIAL
ioctl with (struct serial_struct) set as follows:
serial->flags & ASYNC_SPD_MASK == ASYNC_SPD_[HI, VHI, SHI, WARP]
// this is an assertion, i.e. what your code must achieve, not how
This sets alternate speed to HI: 57600, VHI: 115200, SHI: 230400, WARP: 460800
You can set an arbitrary speed using alt_speed as follows:
Set tty->termios->c_cflag
speed to B38400
. This is unrelated to the speed you chose!
Set the intended speed in tty->alt_speed
. It gets ignored when alt_speed==0
.
You can also an arbitrary speed rate by setting custom divisor as follows:
Set tty->termios->c_cflag
speed to B38400
. This is unrelated to the speed you chose!
bool set_baudrate(int fd, long baudrate) {
struct termios term;
if (tcgetattr(fd, &term)) return false;
term.c_cflag &= ~(CBAUD | CBAUDEX);
term.c_cflag |= B38400;
if (tcsetattr(fd, TCSANOW, &term)) return false;
// cont'd below
Call TIOCSSERIAL
ioctl with struct serial_struct
set as follows:
serial->flags & ASYNC_SPD_MASK == ASYNC_SPD_CUST
serial->custom_divisor == serial->baud_base / your_new_baudrate
// these are assertions, i.e. what your code must achieve, not how
How to do it? First get the structure filled (including baud_base
you need) by calling TIOCGSERIAL
ioctl. Then modify it to indicate the new baudrate and set it with TIOCSSERIAL
:
// cont'd
struct serial_struct serial;
if (ioctl(fd, TIOCGSERIAL, &serial)) return false;
serial->flags &= ~ASYNC_SPD_MASK;
serial->flags |= ASYNC_SPD_CUST;
serial->custom_divisor = serial->baud_base / baudrate.
if (ioctl(fd, TIOCSSERIAL, &serial)) return false;
return true;
}