• Uncategorized

About linux : How-to-set-a-non-standard-baudrate-on-a-serial-port-device-on-Linux

Question Detail

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 level of a syscall. It should be useful in these circumstances at least:

Writing low-level C-based userland code that uses serial ports,
Writing libraries that abstract the serial port functionality,
Writing kernel serial port drivers.

Question Answer

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;
}

You may also like...

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.