TIOCEXCL in Solaris - c++

I have a little problem. I work with serial port, for example /dev/term/0 and I need to lock multiple access to this device. For that I use this code:
int hComm;
hComm = open(portName, O_RDWR | O_NOCTTY | O_NDELAY);
if(hComm != -1){
ioctl(hComm, TIOCEXCL, NULL);
int flags = fcntl(hComm, F_GETFL, 0);
flags &= ~O_NDELAY;
fcntl(hComm, F_SETFL, flags);
}
All works fine. Then I run another application and try to open this port, I have error EBUSY - and it's OK, but in this moment my first application stop working. I can't read/write and close this port, I always have only one error ENXIO (No such device or address).
I have tested this code in Linux and Mac OS X and all works without any problems, but in Solaris...
I don't know what to do.

Related

Non blocking pseudo terminal, recovery after POLLHUP

I create a new pseudo-terminal by opening /dev/ptmx with open() function and O_RDWR | O_NOCTTY | O_NONBLOCK flags. Then I use poll() function to wait for incoming data from the remote end:
struct pollfd pollFileDescriptors[numberOfTerminals];
for (unsigned terminalIndex = 0; terminalIndex < numberOfTerminals; terminalIndex++) {
pollFileDescriptors[terminalIndex].fd = terminals[terminalIndex].getFileDescriptor();
pollFileDescriptors[terminalIndex].events = POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND;
}
int ready = poll(pollFileDescriptors, terminals.getNumberOfTerminals(), timeoutMSec);
Everything works like a dream until the remote end closes connection. In such a case the poll() function returns all the time with POLLHUP revents flag. This is by design, however what could I do to make it operating as before i.e. waiting for another process to open and use the pseudo-terminal. I mean it waits, but returns immediately with POLLHUP set. On the other hand, if I close the file descriptor I have no guarantee of receiving the same pseudo-terminal-id that after reopening the /dev/ptmx. Is there any way to remove the POLLHUP revents flag?
I found a similar question: Poll() on Named Pipe returns with POLLHUP constantly and immediately , however I am using already O_RDWR as described there but it doesn't help as in case of named pipes.
The issue can be easily solved by reopening the pseudo-terminal right after it is created. The POLLHUP won't appear as long as at least one writer exists so we can do this by ourselves with open() and ptsname():
// Create a new pseudo terminal
int fileDescriptor = open("/dev/ptmx", O_RDWR | O_NOCTTY | O_NONBLOCK);
grantpt(fileDescriptor);
unlockpt(fileDescriptor);
// Reopen it for write
const char *targetPath = ptsname(fileDescriptor);
int dummyWriterFileDescriptor = open(fileName.c_str(), O_WRONLY | O_NOCTTY | O_NONBLOCK);

QSerialPort is in use before application starts

I am developing an application for Apalis iMX6 with Qt C++ in Linux and I've added this application into the startup by means of profile.d
this code must set ttymxc1 into RS485 mode like below:
int enableRS485(){
struct serial_rs485 rs485conf;
int fd = open ("/dev/ttymxc1", O_RDWR);
if (fd < 0) {
printf("Error: Can't open: /dev/ttymxc1 %d\n",fd);
return fd;
}
rs485conf.flags |= SER_RS485_ENABLED;
rs485conf.flags &= ~(SER_RS485_RTS_ON_SEND);
rs485conf.flags |= SER_RS485_RTS_AFTER_SEND;
rs485conf.flags |= SER_RS485_RX_DURING_TX;
err = ioctl (fd, TIOCSRS485, &rs485conf);
if (err < 0) {
printf("Error: TIOCSRS485 ioctl not supported.\n");
return err;
}
err = close (fd);
if (err < 0) {
printf("Error: Can't close: /dev/ttyLP1 %d\n",err);
return err;
}
return 0;
}
this function is working as well and has no problem. but when I try to open the serial port sometimes during startup application couldn't get this serial port and QSerialPortInfo shows me ttymxc1 is in use. my initiation of the serial port is like below:
void SerialClass::initSerial()
{
m_serialPort = new QSerialPort();
enableRS485();
sleep(1);
m_serialPort->setPortName("/dev/ttymxc1");
m_serialPort->setFlowControl(QSerialPort::NoFlowControl);
m_serialPort->setBaudRate(9600);
m_serialPort->setRequestToSend(false);
bool res = m_serialPort->open(QIODevice::ReadWrite);
}
"QSerialPort::errorOccured" signal will emit with value "PermissionError"
but mysteriously "ls -l /proc/[0-9]/fd/ |grep /dev/ttymxc1" shows me ttymxc1 is in used by my application.
there is no application that works with serial port and my application is just for one time run.
Is there any idea?
Thanks
You need to call close() on m_serialPort when you close your application. Otherwise your serial port will keep in-use state.
I've found a new remedy, so I want to share it here.
in SerialClass constructor I've added initSerial(), in this way application at the beginning of starting, will open the ttymxc1.
There is no more problem with such a solution.
I don't have such an issue with Raspberry Pi (Raspberry Pi3 + Qt 5.6 + Raspbian). But Toradex Apalis is in another way.
Would you please tell me why?

Linux C++ reading UART device not working consistently

I am using a BeagleBone Black to read data coming from a microcontroller(s) via a UART port(s). I need the reading of the UART port to be a blocking call. Additionally, for the usage of this software there will be some non-standard baud rates in use (aka not provided by termios). Additionally, the UART should follow 8-N-1 (8 data bits, no parity, 1 stop bit).
The code I have for the opening the UART port is as follows:
int UART::UART_open(unsigned int baudRate)
{
mFd = open(mPath.c_str(), O_RDWR | O_NOCTTY);
if(mFd < 0)
{
return -1;
}
struct termios2 tty;
if(ioctl(mFd, TCGETS2, &tty) == -1)
{
return -1;
}
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 5;
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
tty.c_cflag &= ~CBAUD;
tty.c_cflag |= (BOTHER | CREAD | CLOCAL);
tty.c_ispeed = baudRate;
tty.c_ospeed = baudRate;
if(ioctl(mFd, TCSETS2, &tty) == -1)
{
return -1;
}
return 0;
}
The code I have for reading the UART port is as follows:
int UART::UART_read(unsigned char* buf, int length)
{
if(mFd < 0)
{
return -1;
}
if(read(mFd, buf, length) != length)
{
return -1;
}
return length;
}
There is some odd behavior going on. What happens is, the reading is inconsistent. Sometimes when I test it with an Mbed microcontroller sending data continuously (with small delays in between) via UART to the right port, and a test program to continuously read the UART port on the BeagleBone Black, and print out the data it gets, it works fine, and I am able to print out the data sent and everything works as expected. However, what happens often is the very first read simply blocks forever. No errors occur from the functions, the UART_read function simply hangs. So, to debug the error the first thing I do is I use 'screen' to monitor the /dev/ttyO* port I am trying to read from. What I find is that data is being sent to that port just fine. Then, the odd thing is, after I use screen, if I run my test program to continuously read the UART port, it works fine. This happens consistently too, if I do a quick 'screen' of the port when it is not working, I see data being sent, then my test program works. I have tried changing some of the opening termios2 struct options, to no avail. Any help would be appreciated!
Your termios initialization is obviously incomplete. There's a partial configuration for raw mode, e.g.
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 5;
yet you never actually enable noncanonical mode.
The "blocks forever" behavior of read() is symptomatic of a serial terminal (mis)configured for canonical mode when only binary data is received (i.e. there is no line terminator to "complete" the request).
Another possible cause of "blocks forever" behavior is failure to disable hardware flow-control when it is not used.
The insertion of the following statements would enable noncanonical mode and disable the HW handshake:
cfmakeraw(&tty);
tty.c_cflag &= ~CRTSCTS;
Refer to this answer for a complete example.
if(read(mFd, buf, length) != length)
{
return -1;
}
There is no requirement that a read() of a serial terminal will fill the buffer. Depending on the configuration, a "successful" read can return zero or more bytes up to the number requested.
Therefore a short read is not actually an error condition.

Hardware flow control with termios (CRTSCTS) for UART Device

right now I'm communicating with a device over UART in C++ on a Udoo Neo. For this I'm using termios to set up the connection and write data to the device.
For this purpose I want to use hardware flow control and have set the flag (CRTSCTS) with termios.
For the hardaware flow control I've connected the device RTS line to the boards CTS and I've also checked via oscilloscope, that the device is giving me an active high, if it is not ready to read.
The problem is that I still loose bytes in the following example of just spamming the device with numbers, but the boards output says that everything was written correctly.
I thought the UART would be blocked when using HW flow control, so that no information is lost. Am I not understanding this correctly - or is there an error in the code?
Thanks for the help
const char dev[] = "/dev/ttymxc4";
int main(int argc, char **argv) {
int fd;
struct termios t; ///< control structure for a general asynchronous interface
// edited code
tcgetattr(fd, &t);
t.c_iflag &= ~(IGNBRK | BRKINT | ICRNL |
INLCR | PARMRK | INPCK | ISTRIP | IXON);
t.c_oflag = 0;
t.c_cflag &= ~(CSIZE | PARENB);
t.c_cflag |= (CS8 | CRTSCTS);
// edited code
t.c_cflag |= (CLOCAL | CREAD);
t.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
t.c_cc[VMIN] = 0;
t.c_cc[VTIME] = 0;
cfsetispeed(&t,B57600); /* normal shall be: B115200 Baud */
fd = ::open(dev, O_RDWR);
if (fd==-1) {
printf("UART: cannot open file: %s\n",dev);
return -1;
}
tcsetattr(fd,TCSANOW, &t);
// edited code
fcntl(fd, F_SETFL, 0);
int count = 0;
while (true) {
count++;
std::stringstream output;
output << count << ",";
::write(fd, output.str().c_str(), output.str().length());
printf("%d, writing: %s\n", fd, output.str().c_str());
usleep(10000);
}
return 0;
}
Referring to the links by #sawdust, the HW flow control is manipulated via
CCTS_OFLOW and CRTS_IFLOW via libc-doc
Macro: tcflag_t CCTS_OFLOW
If this bit is set, enable flow control of output based on the CTS wire (RS232 protocol).
Macro: tcflag_t CRTS_IFLOW
If this bit is set, enable flow control of input based on the RTS wire (RS232 protocol).
CNEW_RTSCTS and CRTSCTS via SerProgGuide
Some versions of UNIX support hardware flow control using the CTS
(Clear To Send) and RTS (Request To Send) signal lines. If the
CNEW_RTSCTS or CRTSCTS constants are defined on your system then
hardware flow control is probably supported. Do the following to
enable hardware flow control:
options.c_cflag |= CNEW_RTSCTS; /* Also called CRTSCTS */
Note the "Some versions..." and "...is probably supported."
On my particular cross compilation toolchain (Linaro GCC 6.5-2018.12) if I grep for these values, CRTSCTS is not documented, but defined, CCTS_OFLOW is in a lot of info files, but in no header files...
libc/usr/include/bits/termios.h:
174:# define CRTSCTS 020000000000 /* flow control */
As you have said in your comment
... I just thought this would be handled by the kernel?
I am seeing the phenomenon, that even if I add the relevant rts/cts properties in the device-tree ({rts,cts}-gpios or uart-has-rtscts), the command stty -a -F /dev/ttyS still reports back -crtscts meaning that the RTS/CTS handshake is disabled, so even without the userspace application this doesn't seem to be a trivial config. (Kernel 5.4)

Serial port is stuck at wait(4,

I have this implementation in C++ for linux for initializing a serial port:
void setupSerialPort()
{
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
memset(&tio, 0, sizeof(tio));
tio.c_iflag = 0;
tio.c_oflag = 0;
tio.c_cflag = CS8|CREAD|CLOCAL;
tio.c_lflag = 0;
tio.c_cc[VMIN] = 0;
tio.c_cc[VTIME] = 0;
fd = open(SERIALPORT, O_RDWR | O_NONBLOCK) ;
fcntl(fd , F_SETFL, 0); // read data in the buffer per chunk
cfsetospeed(&tio, B4800); // 4800 baud
cfsetispeed(&tio, B4800); // 4800 baud
tcsetattr(fd , TCSANOW, &tio);
}
Sometimes, the serial port reading is stuck and I use 'strace' to see what is going on. the problem is:
strace -p 9454
Process 9454 attached - interrupt to quit
wait4(-1, ^C <unfinished ...>
Process 9454 detached
How can I avoid the problem (it does not happen all the time)?
The problem occurring randomly indicates that the call to open is failing. So check the return values from open call and other remaining calls.
if (fd < 0) return;
Also ensure clean close of the serial port.
Syscalls involving IO could get stuck specially with network IO. Since you are using NON_BLOCKING mode that shouldnt be the case , The complete code would have been helpful here. Personally i had this situation before and I used timer to generate SIG_USR which used to end the blocking call with an error. Pseudo code for that algo is as follows:
initiateTimer ( timeout)
myfunctionCall(){
someSysCall();
}
if(!signalled){
disableTimer();
}else{
checkState();
if(fatal)
die;
}
My suggestions... Open the device with:
int fd = TEMP_FAILURE_RETRY(open(ttyName, O_RDWR | O_NOCTTY | O_NONBLOCK));
The macro TEMP_FAILURE_RETRY is needed to protect open, close, read and select from signal interruptions. Read about the macro if you use libc. Include O_NOCTTY flag to avoid the device becoming the controlling port of the process. Check return value for valid fd.
C.