Linux termios parameter interpretation - c

I have been trying to set up a serial port on an Olimex A13 machine with the Linux (Debian Wheezy) operating system. To set up the parameters to set up the UART I am using the termios structure. In my case I am simply setting a parameter = value like below...
options.c_cflag = (CLOCAL | CREAD);
I have also seen example code on the internet that looks like the following...
tcgetattr(fd, &options);
cfsetispeed(&options, B115200);
cfsetospeed(&options, B115200);
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
options.c_cflag &= ~( ICANON | ECHO | ECHOE |ISIG );
options.c_iflag &= ~(IXON | IXOFF | IXANY );
options.c_oflag &= ~OPOST;
tcsetattr(fd, TCSANOW, &options);
In the above case it looks like the parameter assignments are using bit-wise operators to set the parameters.
My question is, how are the above assignments interpreted?
For example:
How is...
options.c_cflag |= (CLOCAL | CREAD);
interpreted compared to...
options.c_cflag = (CLOCAL | CREAD);
???
And the same for:
How is...
options.c_cflag &= ~PARENB;
interpreted Compared to...
options.c_cflag = ~PARENB;
???
Are the termios flags really a set of bits where the parameters correspond to a particular bit location in the flag?
Since these values are being set by parameters (i.e. CLOCAL, CREAD) are the bit wise operators redundant when setting the flag = to the parameters?
If someone can clarify this I would greatly appreciate it.

The termios bits are indeed bits set on an unsigned int wiithin a struct termios (at least on Linux). They are defined in /usr/include/<platform>/bits/termios.h.
How is... options.c_cflag |= (CLOCAL | CREAD); ...interpreted compared to... options.c_cflag = (CLOCAL | CREAD);
|= (CLOCAL | CREAD) will set the requested termios bits additionally to what's already there, while = (CLOCAL | CREAD) will set only the bits you requested resetting everything else to zero (which is plain wrong most likely since it will set e.g. the character size to 5 bits (CS5).
The same with c_cflag &= ~PARENB; against options.c_cflag = ~PARENB. While the former will set only the PARENB flag to zero, the latter will set all bits to 1 except the PARENB flag which will be set to zero - I don't think that's the desired result.

options.c_cflag = ~PARENB;
options.c_cflag |= ~PARENB; // So it will be the only true

Related

Can't communicate with certain baud rates

My application is able to communicate with baud rates like 4800, 9600 and 115200 but can't with 14400 or 38400. I have to include asm/termios because I need struct termios2 since I'm going to use c_ispeed and c_ospeed members for any buad rate.
Also the second problem I encounter is that read function doesn't return afterVTIME. Do you know why this happens? Any help is appreciated. Thanks.
#include <asm/termios.h>
int serialDevice = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_SYNC);
serialSettings.baudRate = 4800;
serialSettings.dataBits = 8;
serialSettings.hardwareFlowControl = 0;
serialSettings.parity = 0;
serialSettings.parityOdd = 0;
serialSettings.stopBits = 1;
serialSettings.xonxoff = 1;
setSerialSettings(serialDevice, &serialSettings);
//-------------------------------------------------------
int8_t setSerialSettings(int serialDevice, Serial_Params_t *settings)
{
struct termios2 tty;
memset(&tty, 0, sizeof tty);
// get current serial settings
if (ioctl(serialDevice, TCGETS2, &tty) == -1)
{
sendLog("Can't get serial attributes | setSerialSettings", LOG_TYPE_ERROR);
return FALSE;
}
// baudrate
tty.c_cflag &= ~CBAUD;
tty.c_cflag |= BOTHER;
tty.c_ispeed = MAX(110, MIN(settings->baudRate, MAX_BAUDRATE));
tty.c_ospeed = MAX(110, MIN(settings->baudRate, MAX_BAUDRATE));
// enable input parity check
tty.c_iflag |= INPCK;
// data bits: CS5, CS6, CS7, CS8
tty.c_cflag &= ~CSIZE;
switch (settings->dataBits)
{
case 5:
tty.c_cflag |= CS5;
break;
case 6:
tty.c_cflag |= CS6;
break;
case 7:
tty.c_cflag |= CS7;
break;
case 8:
default:
tty.c_cflag |= CS8;
break;
}
// stop bit
switch (settings->stopBits)
{
case 1:
default:
tty.c_cflag &= ~CSTOPB;
break;
case 2:
tty.c_cflag |= CSTOPB;
}
// parity
if (settings->parity == 1)
tty.c_cflag |= PARENB;
else
tty.c_cflag &= ~PARENB;
// odd/even parity
if (settings->parityOdd == 1)
tty.c_cflag |= PARODD;
else
tty.c_cflag &= ~PARODD;
// flow control
// XON/XOFF
if (settings->xonxoff == 1)
tty.c_iflag |= (IXON | IXOFF | IXANY);
else
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
// enable RTS/CTS
if (settings->hardwareFlowControl == 1)
tty.c_cflag |= CRTSCTS;
else
tty.c_cflag &= ~CRTSCTS;
tty.c_cc[VMIN] = 1; // return read function when receive 1 byte
tty.c_cc[VTIME] = 10; // 1 seconds read timeout (deciseconds)
tty.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
// non-canonical mode
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL);
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
tty.c_oflag &= ~OPOST;
// flush port & apply attributes
tcflush(serialDevice, TCIFLUSH);
if (ioctl(serialDevice, TCSETS2, &tty) == -1)
{
sendLog("Can't set serial attributes | setSerialSettings", LOG_TYPE_ERROR);
return FALSE;
}
return TRUE;
}
My application is able to communicate with baud rates like 4800, 9600 and 115200 but can't with 14400 or 38400.
There is a pretty nice writeup for how custom serial speed setting works here: https://github.com/npat-efault/picocom/blob/master/termios2.txt.
In brief, given a struct termios2 identified by tty, to set both input and output speed to custom values, you must
ensure that tty.c_cflag & CBAUD == BOTHER. You appear to do this correctly.
set the desired output speed in tty.c_ospeed. You do this, too.
either
ensure that (tty.c_cflag >> IBSHIFT) & CBAUD == B0, in which case the output speed will also be used as the input speed, or
ensure that (tty.c_cflag >> IBSHIFT) & CBAUD == BOTHER, in which case tty.c_ispeed will be used as the input speed.
You do not do either of those. I'm uncertain why this would cause incorrect communication for some speeds and not others, but the driver is reputed to play some interesting games with speed settings, and maybe you've stumbled across one.
As for
read function doesn't return after VTIME
I think you have incorrect expectations. You are setting VMIN and VTIME both to nonzero values. In this case, VTIME is the maximum inter-character time, not an overall read timeout. With these settings, a blocking read will wait indefinitely for the first character, then will keep reading subsequent characters, up to the requested number, as long as each one arrives within VTIME deciseconds of the previous one.
If you want an overall timeout on every read() call, then set VMIN to 0, and be prepared for some read() calls to read 0 bytes. As always, read() may also read a positive number of bytes but fewer than requested. That may be more likely to happen in this configuration than in the one you're presently using, depending on your choice of VTIME and the behavior of the peer.

serial aplication writtes "0D" as "0A"

I have a serial app on C that receives data and writes it onto a binary file. The problem is that all the data is the same but when i have 0A on sending side, i have 0D on receiving side. I have set the serial port on raw mode and opened the file with wb option. Any clue how to avoid this? If some code is needed, i'll post it.
thanks
EDIT--------------------------
File opening:
FILE *fout;
fout = fopen(file,"wb");
Serial options:
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
/* To disable software flow control simply mask those bits: */
options.c_iflag &= ~(IXON | IXOFF | IXANY);
options.c_lflag &= ~(ICANON|ECHO|ECHOE|ISIG);
tcsetattr(fd, TCSANOW, &options);
success = 1;
return success;
Writting on the file:
fwrite(buffer,1,n,fout);
----------FIX----------------------
Setting this option fixes the problem :
options.c_oflag &= ~OPOST;
You need to mask off the ICRNL mode which translates the enter (carriage return) key to the newline character. This is on the receiving end, not in your program. There's also a corresponding output mode that might be set on your end, but less likely.

What does this operator do?

I am writing a Linux driver for my chinese arduino. At one moment I need to change the baud rate. I looked for examples and found that listing:
Listing 2 - Setting the baud rate.
struct termios options;
/*
* Get the current options for the port...
*/
tcgetattr(fd, &options);
/*
* Set the baud rates to 19200...
*/
cfsetispeed(&options, B19200);
cfsetospeed(&options, B19200);
/*
* Enable the receiver and set local mode...
*/
options.c_cflag |= (CLOCAL | CREAD);
/*
* Set the new options for the port...
*/
tcsetattr(fd, TCSANOW, &options);
The next to last line of code has the |= operator. What does it do? I've never seen it before.
options.c_cflag |= (CLOCAL | CREAD);
is generally equivalent to
options.c_cflag = options.c_cflag | (CLOCAL | CREAD);
except options.c_cflag is evaluated only once, which doesn't matter in the above expression, but it would matter if options.c_cflag had any side effects (for example, if it were *options.c_cflag++)

Can't set serial port configuration in Linux in C

I have writen some Linux program to comunicate my device. I have "same" my program for Windows ("same" because of it's same logic).
I'm using 8N2 data frame format # 9600 bps, with neither software (XOn/XOff) nor hardware (RTS/CTS) flow control. I don't use DTR, DCD, RI, DSR pins of RS-232 9-pin D-sub. I use only RX and TX pins to communicate with my device.
In Linux I have this part of code:
struct termios PortOpts, result;
int fd; /* File descriptor for the port */
/* Configure Port */
tcgetattr(fd, &PortOpts);
// BaudRate - 9600
cfsetispeed(&PortOpts, B9600);
cfsetospeed(&PortOpts, B9600);
// enable reciever and set local mode, frame format - 8N2, no H/W flow control
PortOpts.c_cflag &= (~(CLOCAL | CREAD | CSIZE | CSTOPB));
PortOpts.c_cflag |= ((CLOCAL | CREAD | CS8 | CSTOPB) & (~PARENB));
PortOpts.c_cflag &= (~CRTSCTS);
// PortOpts.c_cflag &= ~PARENB
// PortOpts.c_cflag |= CSTOPB
// PortOpts.c_cflag &= ~CSIZE;
// PortOpts.c_cflag |= CS8;
// no parity check, no software flow control on input
PortOpts.c_iflag |= IGNPAR;
PortOpts.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY | INPCK);
// raw data input mode
PortOpts.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
// raw data output
PortOpts.c_oflag &= ~OPOST;
// setting timeouts
PortOpts.c_cc[VMIN] = 1; // minimum number of chars to read in noncanonical (raw mode)
PortOpts.c_cc[VTIME] = 5; // time in deciseconds to wait for data in noncanonical mode (raw mode)
if (tcsetattr(fd, TCSANOW, &PortOpts) == 0) {
tcgetattr(fd, &result);
if ( (result.c_cflag != PortOpts.c_cflag) ||
(result.c_oflag != PortOpts.c_oflag) ||
(result.c_iflag != PortOpts.c_iflag) ||
(result.c_cc[VMIN] != PortOpts.c_cc[VMIN]) ||
(result.c_cc[VTIME] != PortOpts.c_cc[VTIME]) ) {
perror("While configuring port1");
close(fd);
return 1;
}
} else {
perror("While configuring port2");
close(fd);
return 1;
};
File descriptor 'fd' is for '/dev/ttyS0' device.
I get that message: While configuring port2: Input/output error
I have a laptop, though I don't have any serial ports except for USB. Is this a reason?
I run program as 'root'.
Sorry for my broken English.
Can you run strace on the program; that will give more specifics of just where the IO error is occurring.
One thing to keep in mind - errno doesn't get reset, so the actual error could be from any system call before the perror.

Reading raw bytes from a serial port

I'm trying to read raw bytes from a serial port sent by a IEC 870-5-101 win32 protocol simulator with a program written in C running on Linux 32bit.
It's working fine for byte values like 0x00 - 0x7F. But for values beginning from 0x80 to 0xAF the high bit is wrong, e.g.:
0x7F -> 0x7F //correct
0x18 -> 0x18 //correct
0x79 -> 0x79 //correct
0x80 -> 0x00 //wrong
0xAF -> 0x2F //wrong
0xFF -> 0x7F //wrong
After digging around for two days now, I have no idea, what's causing this.
This is my config of the serial port:
cfsetispeed(&config, B9600);
cfsetospeed(&config, B9600);
config.c_cflag |= (CLOCAL | CREAD);
config.c_cflag &= ~CSIZE; /* Mask the character size bits */
config.c_cflag |= (PARENB | CS8); /* Parity bit Select 8 data bits */
config.c_cflag &= ~(PARODD | CSTOPB); /* even parity, 1 stop bit */
config.c_cflag |= CRTSCTS; /*enable RTS/CTS flow control - linux only supports rts/cts*/
config.c_iflag &= ~(IXON | IXOFF | IXANY); /*disable software flow control*/
config.c_oflag &= ~OPOST; /* enable raw output */
config.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /* enable raw input */
config.c_iflag &= ~(INPCK | PARMRK); /* DANGEROUS no parity check*/
config.c_iflag |= ISTRIP; /* strip parity bits */
config.c_iflag |= IGNPAR; /* DANGEROUS ignore parity errors*/
config.c_cc[VTIME] = 1; /*timeout to read a character in tenth of a second*/
I'm reading from the serial port with:
*bytesread = read((int) fd, in_buf, BytesToRead);
Right after this operation "in_buf" contains the wrong byte, so I guess there's something wrong with my config, which is a port from a win32 DCB structure.
Thanks for any ideas!
Based on your examples, only the 8th bit (the high bit) is wrong, and it's wrong by being always 0. You are setting ISTRIP in your line discipline on the Linux side, and that would cause this. ISTRIP does not, as the comment in the C code claims, strip parity bits. It strips the 8th data bit.
If ISTRIP is set, valid input bytes shall first be stripped to seven bits; otherwise, all eight bits shall be processed. IEEE Std 1003.1, 2004 Edition, chapter 11, General Terminal Interface

Resources