Linux set higher baud rate and associated settings - linux

Input hardware: BeagleBone Black, with some GNU/Linux distro running on it.
What I want to achieve: I want to set some UART peripheral to 921600 baud value, and be able to set the other serial-associated settings (e.g. start/stop bits, parity, data bits, hw flow control, etc).
By far, in Linux, I have found at least three ways of configuring this parameters:
Using struct termios form termios.h header file.
Using struct termios2 from asm/termios.h header file.
Using the stty(1) GNU/Linux utility.
What's the problem:
With the first method I can't use the 921600 baud rate value (there's no define for such higher value, it only goes up to 230400 baud. So this method won't work.
The second method offers me one way to change custom baud rate values, but is also tricky because it doesn't offer some functions like tcgetattr(), tcsendbreak(), tcflush(), and so one. This functions are present in the first-described method, and I can't include both header files termios.h and asm/termios.h because of (1).
The last method also don't work, or at least it doesn't work for all the settings I want to make. This is the current method I'm using, I'm opening the targeted file, I get one file descriptor, to that file descriptor I set the communication parameters (baud (first i set one lower value), parity, start/stop bits, etc) using the first method, and then I use stty(1) utility to change (override) the baud rate value to 921600 (I make a system(...) function call to perform this).
This method won't work if I want to change the HW flow control for example (it won't override that setting, just like happens with the baud value).
What are the solutions?
Is it ok to mix two methods of setting parameters to a UART-communication link like this?

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)

IMHO, using system and stty from a C or C++ program is not the way to go (system is known as a poor security practice at first).
After reading you related questions, my advice would be to only declare in you main module, namespace or class some wrapper functions for all the features you want to use from both termios.h and asm/termios.h.
Then you define them in two different compilation units (c or c++ source files), first dealing with termios.h, the latter with asm/termios.h
If that does not work, the last way would be to carefully merge declarations from termios.h and asm/termios.h in a custom local_termios.h managed in your own sources and include it. This of course leads to non portable code, but anyway as soon as you use asm/termios.h you loose compatibility.

Related

How TTY "input speed" differs from "output speed"?

There are two commands for setting "speed" - cfsetospeed and cfsetispeed.
But why only one "speed" is shown by stty?
According to bits/termios.h, c_ispeed and c_ospeed are "input speed" and "output speed".
I tried to set B4800 to "input speed" and "B57600" to "output speed", and vice versa at the other
end of serial channel. But data is corrupted. Why there are two separate speeds if it is impossible to set
them separately?
stty shows the speed which was set by cfsetospeed or cfsetispeed - whichever was called last.
Besides, B0 setting takes effect only with cfsetospeed. Is it documented somewhere?
Moreover, while B0 is set, I can receive and transmit data with whatever speed was
active before B0 was set. Is it documented somewhere? Is it undefined behavior or it is in POSIX?
EDIT:
I conducted the same tests on ordinary serial port (i.e., no usb) and obtained the following curious difference with usb serial port:
Ordinary serial port uses 9600 "speed" if we set B0, whereas
usb serial port uses whatever speed was selected before B0 was set.
(OS: Linux)
Why there are two separate speeds if it is impossible to set them separately?
Some (older) UARTs (e.g. the ubiquitous 8250 and its successors) actually have a crystal input for the transmitter and another clock input for the receiver. Hence the input baudrate can be different from the output baudrate on such UARTs.
But most (if not all) boards feed the transmit clock to the receive clock to negate this feature (e.g. the typical 8250/165x0 datasheet will show the RCLK input driven by the BAUDOUT output).
The separate termios speed elements simply reflects this obscure hardware capability (that is rarely actually available).
Also most UARTs (that are not 8250-based) in SoCs have a common clock input for transmit and receive, so the baudrate setting has to apply to both.
So specifying "separate" baudrates is typically a useless configuration.
Besides, B0 setting takes effect only with cfsetospeed. Is it documented somewhere?
Code is the documentation.
Most serial port drivers call uart_get_baud_rate() in drivers/tty/serial/serial_core.c to decode the baudrate settings from the termios structure.
/**
* uart_get_baud_rate - return baud rate for a particular port
* #port: uart_port structure describing the port in question.
* #termios: desired termios settings.
* #old: old termios (or NULL)
* #min: minimum acceptable baud rate
* #max: maximum acceptable baud rate
*
* Decode the termios structure into a numeric baud rate,
* taking account of the magic 38400 baud rate (with spd_*
* flags), and mapping the %B0 rate to 9600 baud.
*
* If the new baud rate is invalid, try the old termios setting.
* If it's still invalid, we try 9600 baud.
*
* Update the #termios structure to reflect the baud rate
* we're actually going to be using. Don't do this for the case
* where B0 is requested ("hang up").
*/
Note the special handling mentioned for B0.
I conducted the same tests on ordinary serial port (i.e., no usb) and obtained the following curious difference with usb serial port:...
The list of drivers that use uart_get_baud_rate() does not seem to include any USB serial port adapters: https://elixir.free-electrons.com/linux/latest/ident/uart_get_baud_rate
You may have to inspect the driver for your specific USB serial port adapter for its handling of the termios baudrate specifers.

What's the maximum baud rate in MATLAB?

Ubuntu 16.04 & MATLAB R2017a.
I'm trying to set serial port like that:
s=serial_port('/dev/ttyUSB0','BaudRate',115200,'DataBits',8,'InputBufferSize',80000)
It's working fine, but when I try to change baud rate, say 1000000.
I got this message:
Open failed: BaudRate could not be set to the specified value.
So, I have 2 question:
1) Is it possible to set not common baud rates, say 2000000?
2) I found, that 1500000 and 3000000 is working for me.
Is there maximum speed?
** UPDATE**
I know how to change the baud rate in OS, in my case (Ubuntu 16.04)
setserial is not working, so I'm using sudo stty -F /dev/ttyUSB3 3500000 (not all speed is allowed) or via asm/termios.h> -- all speed is allowed.
So, I'm using second way.
After that, I can easily listen the port like that cu -l /dev/ttyUSB0
And at the same time I cant set the speed in matlab.. (Error above)
Although this link should provide you enough information about how to manage baud rates on Matlab side, as #Cris Luengo already stated in his command, I would like to elaborate a little bit on the hardware side of the problem.
Using the following command:
stty -F /dev/ttyUSB0
you should be able to retrieve the current baud rate of the targeted device. Alternatively, the following command also retrieves that value:
setserial -ag /dev/ttyUSB0
together with other important information:
/dev/ttyUSB0, Line ..., UART: ..., Port: ..., IRQ: ...
Baud_base: ..., close_delay: ..., divisor: ...
closing_wait: ..., closing_wait2: ...
Flags: ...
OS side, you can play with the baud rate of some devices but if you want to avoid problems, you always have to set a coherent value when establishing a connection. Generally speaking, devices have a tolerance level (of, I think, no more than ±5%) on overspeed and underspeed concerning baud rate deviance... so you can try to force an arbitrary baud rate different from the current one, but you don't want to go too far from it.

Baud rate of inb and outb commands - linux

I want to pass binary data from my PC to my micro-controller through the real serial port in my computer. At this time, I'm thinking of using INB and OUTB functions so I don't have to put up with linux tty character overrides.
Question is, how do I configure the baud rate of the serial port, if I use its address with the INB and OUTB functions? For example, if I want to write to COM1 (0x3F8), how will I be able to determine the baud rate data is being written at?
Does stty still have some sort of background control with INB and OUTB or setserial or what?
At this time, I'm thinking of using INB and OUTB functions so I don't have to put up with linux tty character overrides.
This is a terrible, no good, very bad idea, and it won't work. The existing serial drivers will get very confused if you try to change the port configuration from under them.
Use standard UNIX APIs to interact with the serial port. It's not hard.
Duskwuff is right, of course. Why go low-level and not use the API?
See http://www.tldp.org/HOWTO/text/IO-Port-Programming section 6.3:
If the device you're talking to supports something resembling RS-232,
you should be able to use the serial port to talk to it. The Linux
serial driver should be enough for almost all applications (you
shouldn't have to program the serial port directly, and you'd probably
have to write a kernel driver to do it); it is quite versatile, so
using non-standard bps rates and so on shouldn't be a problem.
To illustrate this, here is the answer to the original question "how to set the baud rate":
In addition to the port (e.g. 0x3F8 for COM1), the next few port numbers are used for various controlling purposes. For setting the baud rate, first calculate the divisor 115200 / [desired baud rate]. E.g. if you want 38400 baud, the divisor is 3. Then:
Set the highest bit of PORT+3
Send the least significant byte of the divisor to PORT
Send the most significant byte of the divisor to PORT+1
Clear the highest bit of PORT+3 (at the same time, the lower bits can be used to set the parity, stop bits etc.)
In code:
outb(PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
outb(PORT + 0, 0x03); // Set divisor to 3 (lo byte) 38400 baud
outb(PORT + 1, 0x00); // (hi byte)
outb(PORT + 3, 0x03); // 8 bits, no parity, one stop bit
Source is here: http://wiki.osdev.org/Serial_Ports

How can I set a custom baud rate on Linux?

I want to communicate over my serial port on Linux to a device with a non-standard-baud rate that is not defined in termios.h.
I tried the "baud rate aliasing"-method from this post, but when I execute my C-program (I’ve named it "testprogram"), Linux says "testprogram sets custom speed on ttyS0. This is deprecated."
I did some search on Google, and it seems that there is another (newer?) method to change the baud rate to a non-standard-value: On http://sourceware.org/ml/libc-help/2009-06/msg00016.html the author says that the c_flag of struct termios must be OR’d with BOTHER (=CBAUDEX | B0).
With this method the baud rates are set directly in the c_ispeed and c_ospeed-members of the struct termios. However, I don’t know how I use this method in my C program. Like the author said, there is no BOTHER defined/available when I include termios.h, so what should be done to set the baud rate this way?
How can I set the baud rate to a non-standard-value without changing the kernel?
I noticed the same thing about BOTHER not being defined. Like Jamey Sharp said, you can find it in <asm/termios.h>. Just a forewarning, I think I ran into problems including both it and the regular <termios.h> file at the same time.
Aside from that, I found with the glibc I have, it still didn't work because glibc's tcsetattr was doing the ioctl for the old-style version of struct termios which doesn't pay attention to the speed setting. I was able to set a custom speed by manually doing an ioctl with the new style termios2 struct, which should also be available by including <asm/termios.h>:
struct termios2 tio;
ioctl(fd, TCGETS2, &tio);
tio.c_cflag &= ~CBAUD;
tio.c_cflag |= BOTHER;
tio.c_ispeed = 12345;
tio.c_ospeed = 12345;
ioctl(fd, TCSETS2, &tio);
You can set a custom baud rate using the stty command on Linux. For example, to set a custom baud rate of 567890 on your serial port /dev/ttyX0, use the command:
stty -F /dev/ttyX0 567890
dougg3 has this pretty much (I can't comment there). The main additional thing you need to know is the headers which don't conflict with each other but do provide the correct prototypes. The answer is
#include <stropts.h>
#include <asm/termios.h>
After that you can use dougg3's code, preferably with error checking round the ioctl() calls. You will probably need to put this in a separate .c file to the rest of your serial port code which uses the normal termios to set other parameters. Doing POSIX manipulations first, then this to set the custom speed, works fine on the built-in UART of the Raspberry Pi to get a 250k baud rate.
BOTHER appears to be available from <asm/termios.h> on Linux. Pulling the definition from there is going to be wildly non-portable, but I assume this API is non-portable anyway, so it's probably no big loss.
For Mac users (possibly also for some Linux distributions)
stty ospeed 999999
stty ispeed 999999
You can just use the normal termios header and normal termios structure (it's the same as the termios2 when using header asm/termios).
So, you open the device using open() and get a file descriptor, then use it in tcgetattr() to fill your termios structure.
Then clear CBAUD and set CBAUDEX on c_cflag.
CBAUDEX has the same value as BOTHER.
After setting this, you can set a custom baud rate using normal functions, like cfsetspeed(), specifying the desired baud rate as an integer.
There is an serial I/O chip on your motherboard's CPU (16650 UART).
This chip uses 8-bit port as control and data bus, and thus you can issue a command to it through writing to this chip through the control and data bus.
Usually, an application did the following steps on the serial port
Set baud rate, parity, encoding, flow control, and starting / ending sequence length during program start. This setup can be done via ioctl to the serial device or 'stty' command. In fact, the stty command uses ioctl to that serial device.
Write characters of data to the serial device and the driver will be writing data charaters to the UART chip through its 8-bit data bus.
In short, you can specify the baud rate only in the STTY command, and then all other options would be kept as default, and it should enough to connect to ohter devices.

Input baudrate vs output baudrate

OS is Ubuntu 10.04 32-bit.
All these years I've unknowingly assumed that input and output baudrates were symmetrical, in == out. I just noticed, however, that the Linux termios structure contains fields for the input and output baudrate. And there are "in" and "out" versions of the buadrate setter/getter -- cfsetospeed/cfsetispeed, cfgetospeed/cfsgetispeed Are they actually separate controls and I can talk and listen at different speeds? Would I ever want to?
What little I could find on google says setting the input speed to 0 will tell the input speed to match the output speed. Correct? If I set the output speed to, say 9600, then set the input speed to zero, input speed should be 9600. What happens if I then change the output speed to 19200? Will input speed also change to 19200?
Sorry for the somewhat easier questions where I should just go try it. My serial-attached hardware is in an unknown state and I am unsure how it is behaving.
Not all serial ports support configuring these separately, but the termios API does give you separate controls to support the ones that do.
Cases where you'd want asymmetric rates will be completely obvious (the manual for the device you're talking to lists different rates for input and output, and you need both simultaneously).
There's no good reason to use the "zero means input and output are the same". Just set them the same explicitly, then you never have to worry about corner cases.

Resources