How to set a non-standard baudrate on a serial port device on Linux?
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
B230400If 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 toB38400
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 howThis 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 toB38400
. This is unrelated to the speed you chose!Set the intended speed in
tty->alt_speed
. It gets ignored whenalt_speed==0
.
You can also an arbitrary speed rate by setting custom divisor as follows:
Set
tty->termios->c_cflag
speed toB38400
. 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 belowCall
TIOCSSERIAL
ioctl withstruct 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 callingTIOCGSERIAL
ioctl. Then modify it to indicate the new baudrate and set it withTIOCSSERIAL
:// 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;
}
Qt - Setting a custom baud rate
Relevant bug report here. Basically it means, that the exact baud rate is not available, because the actual thing configured to hardware is an integer divisor of some clock frequency, and there is no exact integer divisor for the requested baud rate. The warning message is pretty descriptive about this.
It shouldn't matter, baud rates have some tolerance, but if it does cause problems (discussion under bug suggests it may...), upgrading to Qt 5.6 is suggested in the bug report.
Linux set higher baud rate and associated settings
You may use setserial console utility:
1) Get baud_base value from setserial
output
setserial -a /dev/<...>
2) Calculate divisor = baud_base / desired_baud_rate
For example if baud_base = 3000000 (3MHz):
baud_rate = 115200 -> divisor = 26.04 (approximately 26)
baud_rate = 230400 -> divisor = 13.02 (approximately 13)
baud_rate = 921600 -> divisor = 3.26
3.26 is too much to set divisor=3 and too little to set divisor=4.
So, in this case you can't use baud_rate=921600 because of hardware limitations.
You may choose divisor=3 (baud_rate=1000000) or divisor=4 (baud_rate=750000). These baud rates are not standart, but possible.
stty -F /dev/<...> 9600 -icrnl -ixon -crtscts -parenb # desired UART settings
setserial /dev/<...> spd_cust # use custom value for divisor
setserial /dev/<...> divisor 3 # set custom value for divisor
stty -F /dev/<...> 38400 # activate setserial settings
# now baud_rate is (baud_base / divisor)
Uncommon baud rate on serial port - Linux
Finally i've found another topic on stackoverflow wich is complete and solve my problem : How to set baud rate to 307200 on Linux?
Here is my code with the modification :
static int rate_to_constant(int baudrate) {
#define B(x) case x: return B##x
switch(baudrate) {
B(50); B(75); B(110); B(134); B(150);
B(200); B(300); B(600); B(1200); B(1800);
B(2400); B(4800); B(9600); B(19200); B(38400);
B(57600); B(115200); B(230400); B(460800); B(500000);
B(576000); B(921600); B(1000000);B(1152000);B(1500000);
B(2000000);B(2500000);B(3000000);B(3500000);B(4000000);
default: return 0;
}
#undef B
}
int Custom_Baudrate(const char* Device, int rate)
{
/*Declaration of all the variables needed*/
struct termios2 options;
struct serial_struct serinfo;
int file=-1;
int speed = 0;
int r=rate;
/* Open and configure serial port */
file = open(Device,O_RDWR|O_NOCTTY);
if(file==-1){printf("\nERROR : Unable to open the serial port\n\n");return 1;}
speed = rate_to_constant(r);
/*Find best Baudrate*/
if (speed == 0) {
/* Custom divisor */
serinfo.reserved_char[0] = 0;
if (ioctl(file, TIOCGSERIAL, &serinfo) < 0) file=-1;
serinfo.flags &= ~ASYNC_SPD_MASK;
serinfo.flags |= ASYNC_SPD_CUST;
serinfo.custom_divisor = ((serinfo.baud_base + (r / 2)) / r);
if (serinfo.custom_divisor < 1)
serinfo.custom_divisor = 1;
if (ioctl(file, TIOCSSERIAL, &serinfo) < 0) file=-1;
if (ioctl(file, TIOCGSERIAL, &serinfo) < 0) file=-1;
if (serinfo.custom_divisor * r != serinfo.baud_base) {
warnx("actual baudrate is %d / %d = %f",
serinfo.baud_base, serinfo.custom_divisor,
(float)
serinfo.baud_base / serinfo.custom_divisor);
}
}
/*Set the best Baudrate (if desired baudrate is unvailable, it's set automatically at 38400)*/
fcntl(file, F_SETFL, 0);
tcgetattr(file, &options);
cfsetispeed(&options, speed ?: B38400);
cfsetospeed(&options, speed ?: B38400);
cfmakeraw(&options);
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~CRTSCTS;
if (tcsetattr(file, TCSANOW, &options) != 0) file=-1;
/*Read the serial port*/
communicate_Serial_Port(file);
close(file);
return 1;
}
In this code you just have to precise wich baudrate you want and it find the nearest value that you can use. This value is based on the "base baudrate" of your device and it search a divisor to set the best baudrate. However, some baudrate should always being unavailable so this program will put the 38400 as a base (it's a choice).
I've test it with several baudrate and it always work.
I'm new on stackoverflow, i hope this post will complete correctly the question.
Specifying non-standard baud rate for FTDI virtual serial port under Linux
You can't change baud base, I suppose it is hardware related. So messing with the module won't do you any good. In your third point you only talk about the first method proposed for setting a custom baudrate, where you need to access the tty->alt_speed
. It seems there is no interface to directly set tty struct from userspace, at least not with the ftdi_sio driver.
However, there is another method explained in the comments :
* 3. You can also set baud rate by setting custom divisor as follows
* - set tty->termios->c_cflag speed to B38400
* - call TIOCSSERIAL ioctl with (struct serial_struct) set as
* follows:
* o flags & ASYNC_SPD_MASK == ASYNC_SPD_CUST
* o custom_divisor set to baud_base / your_new_baudrate
Did you try it ?
Related Topics
Sorting on the Last Field of a Line
Search and Replace with Sed When Dots and Underscores Are Present
Shebang Line Limit in Bash and Linux Kernel
How to Find Directory of Some Command
How Find Out Which Process Is Using a File in Linux
How to Get Position of Cursor in Terminal
Linking 32-Bit Library to 64-Bit Program
How to Create a Device Node from the Init_Module Code of a Linux Kernel Module
Why Is the Page Size of Linux (X86) 4 Kb, How Is That Calculated
Docker-Compose Up and User Inputs on Stdin
Memory Limit to a 32-Bit Process Running on a 64-Bit Linux Os
How to Avoid Transparent_Hugepage/Defrag Warning from Mongodb
Using "$Random" to Generate a Random String in Bash
Why Is Rcx Not Used for Passing Parameters to System Calls, Being Replaced with R10
Using Grep and Sed to Find and Replace a String
How Clear and Invalidate Arm V7 Processor Cache from User Mode on Linux 2.6.35