nix icon indicating copy to clipboard operation
nix copied to clipboard

Non-Standard TTY Baud Rates on Linux

Open rrichardson opened this issue 4 years ago • 3 comments

On Linux, non-standard baud rates are often required, but not "supported" by traditional termios APIs. They must be set with an IOCTL but this is still needed.

The DMX512 Lighting protocol, for example, is set at 250,000 BAUD.

The recommended approach on Linux is to the TCGETS2 and TCSETS2 ioctls.

e.g. (pseudorust) to set the baud rate for a given tty device

    let tio = termios2::new();
    libc::ioctl(fd, TCGETS2, &tio);
    tio.c_cflag &= !CBAUD;
    tio.termios.c_cflag |= BOTHER;
    tio.termios.c_ispeed = rate as c_uint;
    tio.termios.c_ospeed = rate as c_uint;
    libc::ioctl(fd, TCSETS2, &tio);

We could keep the interfaces basically the same if we broaden the ability of the cfsetspeed functions to take a u32 on Linux. Then we could make a non-standard implementation of cfsetspeed on Linuxes which handles the non-standard baud rate.

I've thought of 3 options for implementation:

  1. Create an alternative set of functions to cfset*speed to handle non-standard Baud rates.
  2. Update the cfsetspeed functions to switch between the inner cfsetspeed functions for standard baud and then ioctls for non-standard baud.
  3. Replace the implementations of the cfset*speed functions to always use the ioctl no matter what speed is passed.

Option 1 Would be a bit confusing to those who are trying to use nix on a tty with non-standard baud. Option 2 Would be the most complicated implementation, but less confusing for all. I think this would be the preferred approach if there were performance ramifications for using cfsetspeed vs ioctl (I haven't looked to see if either would cause a context switch) Option 3 Is probably the easiest to implement, but it may cause unintended side effects, that I haven't considered.

Thoughts?

I'm happy to implement this, but I'd like to know what would be the preferred approach. Thanks!

rrichardson avatar Jan 18 '21 18:01 rrichardson

I went ahead and implemented option 3, since that makes it match the BSD implementation. I haven't really tested it, but it should work :)
When I get a chance, I'll test some DMX code against it.

rrichardson avatar Jan 18 '21 23:01 rrichardson

Have you checked what cfsetspeed does in glibc? It might just use the TCSETS2 ioctl. Option 3 does sound convenient. My only worry is that it might not work in all cases. Examining the source of cfsetspeed should help to clear that up.

asomers avatar Jan 31 '21 23:01 asomers

It looks like the cfset*speed functions just update the termios struct. Later, tcsetattr is called, which invokes either the TCSETS or TCGETS ioctal.
So it is uniform across the board, cfsetspeed -> termios (1) -> tcsetattr -> TCSETS

Unfortunately there isn't an officital cfset*speed2/tcsetattr2 set of functions yet. (but there are some unofficial ones: https://github.com/linux4sam/9bit/blob/master/custom_termios2.h)

rrichardson avatar Feb 02 '21 17:02 rrichardson