Setting custom baud rate on serial port

custom baud rate

Serial ports are really important in Industry Automation, in fact they are widely used to communicate between computers and their peripherals (or between computers too). Usually these serial peripherals use standard baud rates but sometimes they not! So how we can force our serial controller to use these non standard speeds?

Looking around into the driver

The Linux kernel is an open-source project so to better answering to the above question we can start by directly looking into the source, in fact if we take a look into file linux/drivers/tty/tty_ioctl.c  we can see the following code for the set_termios()  function:

/* If old style Bfoo values are used then load c_ispeed/c_ospeed
 * with the real speed so its unconditionally usable */
tmp_termios.c_ispeed = tty_termios_input_baud_rate(&tmp_termios);
tmp_termios.c_ospeed = tty_termios_baud_rate(&tmp_termios);

Here we can see that input and output speed is read from the termios  variables by using specific functions. Then if we take a look at one of these functions, for example tty_termios_input_baud_rate() , we see the following code:

speed_t tty_termios_input_baud_rate(struct ktermios *termios)
{
#ifdef IBSHIFT
        unsigned int cbaud = (termios->c_cflag >> IBSHIFT) & CBAUD;
        if (cbaud == B0)
                return tty_termios_baud_rate(termios);
        /* Magic token for arbitrary speed via c_ispeed*/
        if (cbaud == BOTHER)
                return termios->c_ispeed;

Here we notice that the BOTHER  define can be used to specify a generic value for baud rate instead of the classic fixed speed defines like B115200  & friends. In this scenario we can go further searching where BOTHER  is defined and we can discover that everything is into file linux/include/uapi/asm-generic/termbits.h , below is what we can found:

struct termios2 {
        tcflag_t c_iflag;               /* input mode flags */
        tcflag_t c_oflag;               /* output mode flags */
        tcflag_t c_cflag;               /* control mode flags */
        tcflag_t c_lflag;               /* local mode flags */
        cc_t c_line;                    /* line discipline */
        cc_t c_cc[NCCS];                /* control characters */
        speed_t c_ispeed;               /* input speed */
        speed_t c_ospeed;               /* output speed */
};
...
#define    BOTHER 0010000

So we discovered that struct termios2  can be used to specify serial communication parameters from the userland with non standard speeds. Let’s see how.

A practical example

Below is a simple code in C that we can use to set custom baud rate. It’s quite obvious that this code can work if and only if the underlying driver is supporting these advanced settings.

        struct termios2 term2;
        ioctl(fd, TCGETS2, &term2);
        term2.c_cflag &= ~CBAUD;  /* Remove current BAUD rate */
        term2.c_cflag |= BOTHER;  /* Allow custom BAUD rate using int input */
        term2.c_ispeed = speed;   /* Set the input BAUD rate */
        term2.c_ospeed = speed;   /* Set the output BAUD rate */
        ioctl(fd, TCSETS2, &term2);

By using the ioctl()  command TCGETS2  we retrieve from the kernel the current serial port configuration where we remove the (old and) current baud rate and then we enable custom baud rate with BOTHER .

As final note, don’t forget that the above code is just an example so we should add return values checks especially for systems calls!