Setting custom baud rate on serial port
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:
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); 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; tcflag_t c_oflag; tcflag_t c_cflag; tcflag_t c_lflag; cc_t c_line; cc_t c_cc[NCCS]; speed_t c_ispeed; speed_t c_ospeed; }; ... #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; term2.c_cflag |= BOTHER; term2.c_ispeed = speed; term2.c_ospeed = speed; 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!