Why does read not terminate for my USB device? - c

I have an USB-device connected with my RaspberryPi 3 B+ (Raspbian Buster Lite 2019-07-10). I also write an small program to read data from the USB Device. The device has an custom, CDC conform firmware, so the device is detected correctly by the OS and and tty is attached.
But when I call 'read' for the device, the syscall never terminates. The odd thing is, when I access the device with MiniCom, CuteCom or even H-Term, it works correct.
I already tried the answers from the following questions:
Reading and writing to serial port in C on Linux
How to open, read, and write from serial port in C?
C program to read data from USB device connected to the system
None of them worked.
I also tried to flush the tty with tcflush and tcdrain.
int dev = open("/dev/ttyACM0", O_RWDR | O_NOCTTY);
// I tried to adapt the device parameters with termios, see above
uint8_t req[] = {0x21, 0x42, 0x00, 0x12}; // the actual request
write(dev, req, 4);
uint8_t resp[12];
read(dev, resp, 8); // does not terminate
I expect a result, 12 bytes long, but read just blocks. If I try the O_NONBLOCK/O_NDELAY option, read terminates with the EAGAIN error.
Of course I checked the result values from every syscall and library function, they all returned/terminated as expected. And as I mentioned above, when connected with a terminal program the device works how it should, so it can't be the firmware.
Also I traced with strace what minicom and cutecom did, but they also did nothing more than open, write and read and of course I tried everything with sudo, so the rights are not the problem.

Seems like the device has never taken the request, so its never processing and getting blocked. so, something prob in the way the request is written. Did you try setting the speed of the port/interface?

I found the Answer myself: the ICANON bit in the termios struct, c_lflag field musst be cleared and the VMIN field in the c_cc array should be set to 1.
Further explanations are found here:
https://www.gnu.org/software/libc/manual/html_node/Local-Modes.html#Local-Modes
https://www.gnu.org/software/libc/manual/html_node/Mode-Data-Types.html
(be carefull, some parts of this documentary are a little bit outdated, for example the CCTS_OFLOW Flag does not exist on Raspbian-Buster)

Related

Problems with read() of a Serial Port (USB-RS232) using C language

Good day to everyone,
I am just starting to program for my research which needs to use a traverse mechanism (similar to a robotic arm which can only move in 3 axis X-Y-Z) connected by Serial Port (USB to RS232, using /dev/ttyUSB0) to the Computer. The OS of the computer is Linux 14.04 and for kernel reasons I can't use a more advance version, also I am using C language on the program for this traverse arm.
This code that I have been using has been developed since the year 2000, but on recent years this program hasn't been used. Ever since I started with this project I have encounter several issues, but in the end I always crash with the same wall, that is an eternal loop caused inside the program because the read() function of C can't proceed properly to read the data coming from the Serial Port.
/* For 232cOUT() and 232cIN() */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include "traverse.h"
rs232cIN(mojiin,status)
unsigned char *mojiin;
unsigned char *status;{
int fdi,c,res,j,w;
struct termios oldtio,newtio;
struct termios oldscr;
printf("ok0i\n");
fdi = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
if (fdi <0) {perror(MODEMDEVICE); exit(-1); }
tcgetattr(fdi,&oldtio); /* save current port settings --> For tty device */
tcgetattr(1,&oldscr); /* save current port settings --> For display device */
printf("fdi=%d\n",fdi);
printf("ok1i\n");
bzero(&newtio, sizeof(newtio));
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD | CSTOPB;
newtio.c_iflag = IGNPAR | ICRNL;
newtio.c_oflag = 0;
printf("ok2i\n");
/* set input mode (non-canonical, no echo,...) */
newtio.c_lflag = 0;
newtio.c_cc[VTIME] = 5; /* inter-character timer unused */
newtio.c_cc[VMIN] = 0; /* blocking read until 5 chars received */
tcflush(fdi, TCIFLUSH);
tcsetattr(fdi,TCSANOW,&newtio);
tcsetattr(1,TCSANOW,&newtio); /* stdout settings like modem settings */
printf("ok3i\n");
res = read(fdi,mojiin,256); /* return after 5 chars have been input */
printf("res=%d\n",res);
printf("ok4i\n");
mojiin[res]=0; /* so we can printf... */
#if 1
printf("Data import Good!!\n\r");
printf("Imported-MOJIRETSU:%c\n\r",mojiin[0]);
printf("Imported-MOJISU:%d\n\r", res);
#endif
status[0]=mojiin[0];
//sleep(1); /* this command is very important */
usleep(SLEEPUSEC2);
tcsetattr(fdi,TCSANOW,&oldtio);
tcsetattr(1,TCSANOW,&oldscr);
close(fdi);
}
Maybe this code is similar to others I have found on the web, the Open Port is also similar to this one but instead of read() function is with write().
The problem comes here at the read function, where using the current code I can't read any value coming from the RS232. The computer can send an ASCII code to the RS232 but when it need to receive, there is no return value, as an example of the program running is this code, The program runs several loops but the program gets stuck in loopin1.
The results of the read are res, MOJIRETSU and MOJISU. If you check the fdi=3 which means that it is reading something from the RS232 port but when it get to res, the value is 0 and MOJIRETSU doesn't show any value and MOJISU it is equal to 0. From MOJISU it creates the Status that later generates the Error on the Status flag.
========== [Y_AXIS] Traversing START!! ==========
Delta(Traverse) -->0.000000
loopin1ok0o
ok1o
ok2o
MOJISU=4
ok3o
ZR
MOJIOUT=ZR
Sending OK
ok4o
ok0i
fdi=3
ok1i
ok2i
ok3i
res=0
ok4i
Data import Good!!
Imported-MOJIRETSU:
Imported-MOJISU:0
status=0
STATUS ERROR 0x0 !!!!
STATUS FLAG=-4
ok0o
ok1o
ok2o
MOJISU=4
ok3o
ZR
MOJIOUT=ZR
Sending OK
ok4o
ok0i
fdi=3
ok1i
ok2i
ok3i
res=0
ok4i
Data import Good!!
Imported-MOJIRETSU:
Imported-MOJISU:0
status=0
STATUS ERROR 0x0 !!!!
STATUS FLAG=-4
^C
As you can see from the Loop, there is an error message that is the one causing that the same section repeats and repeats itself all the time. The weirdest thing is that if I nullify this error, it means that I just ignore the loop the error generates modifying other codes:The signal continues to other loops until it finally gets to move the traverse mechanism! Even though it still sends the exact same error as the loop is showing.
You may ask then, work without the loop, but the problem is that the error messages are the flags that indicate if the traverse mechanism is reaching a limit on the rail that it stands, and if it doesn't have the warning I might break the traverse mechanism! So definitely I need this feedback loop for its operation.
The system works in Non-canonical way and I have adjusted the VTIME to several values and doesn't change the status of the out come. If I change the VMIN then the program get stuck and I can only stop it if I close the terminal.
It is really strange that sending the error even without the loop, the traverse mechanism works. So really need advice if someone know how to operate the read() function so I can actually get values for res, MOJIRETSU y MOJISU and so operate the traverse mechanism without removing this feedback loop.
Your serial terminal initialization uses poor coding practices. Zeroing out the termios struct is not POSIX compliant.
what do you refer with Zeroing the termios structure?
Simply search for "zero" in your code, and you'll find the offending statement:
bzero(&newtio, sizeof(newtio));
Refer to Setting Terminal Modes Properly
and Serial Programming Guide for POSIX Operating Systems for proper methods of initializing a serial terminal.
Also the code use to work some years ago, because the traverse mechanism was used in previous research, more recently it hasn't been able to move with this code.
That's the problem with poorly written code. It's not portable, and may not work on a different system or a different day.
The problem comes here at the read function, where using the current code I can't read any value coming from the RS232.
Analysis is somewhat hampered by code that does not match the comments or your text descriptions.
But three problems with your code are obvious.
As already mentioned, the serial terminal initialization is not POSIX compliant.
The log indicates that you're cycling between some mystery output routine(s) and the input routine that you did post for review.
Each execution of the input routine performs an open(), initialization, and close() sequence on the serial terminal.
Presumably the output routine (which has not been posted) performs a similar open(), initialization, and close() sequence on the serial terminal.
This repeated sequence of opening, initializing, and closing of the serial terminal is horribly inefficient and completely unnecessary with proper program design.
That's the second problem.
The third problem is a direct consequence of the previous problem.
As part of the initialization for reading, you perform an explicit discarding of any data that may be in the receive buffers:
tcflush(fdi, TCIFLUSH);
Your subsequent read() (regardless how you configure VMIN and VTIME) can only return data that is received after the flush operation.
Lost to your program is any and all data that is received while the serial terminal is not open and/or before the flush operation.
If your program opened and initialized the serial terminal just once at its start for both reading and writing, this third problem would disappear.

creating a serial loopback under VxWorks

I'm fairly new to VxWorks OS and hence wouldn't mind explanations in case I differ in my understanding of things under the hood when they differ from more traditional OSes like Linux and the likes. With that out of the way, let me begin my actual question.
I am trying to create a loop-back test for testing changes I made to the serial UART driver on the board. Since I do not want to use a cross cable to actually short two UART ports externally, I've connected both of those ports to my dev machine. One configured as an output port from the dev machine's perspective (and consequently as an Input port on the board) and the other an input port (an output port on the board). I am actually doing the loopback using a shared memory buffer which I am protecting using a mutex. So there are two threads on the board, one of which reads from the input port, copies data to the shared memory and the other reads from the memory and sends it over the output port.
And I am using regular open, read and write calls in my VxWorks application (by the way I think it is part of the application code as I call the functions from usrAppInit.c not withstanding the fact that I can even call driver routines from here! (Is it because of a flat memory model vis-a-vis Linux?? Anyhow).
Now I these ports on VxWorks have been opened in a non blocking mode and here's the code snippet which configures one of the ports:
if( (fdIn = open(portstrIn, O_RDONLY | O_NOCTTY, 0) ) == ERROR) {
return 1;
}
if(((status = ioctl(fdIn, FIOSETOPTIONS, OPT_RAW))) == ERROR)
{
return 1;
}
/* set the baud rate to 115200
*/
if((status = ioctl(fdIn, FIOBAUDRATE, 115200)) == ERROR)
{
return 1;
}
/* set the HW options
*/
if((status = ioctl(fdIn, SIO_HW_OPTS_SET, (CS8 | 0 | 0))) == ERROR)
{
return 1;
}
And similarly the output port is also configured. These two are part of two separate tasks spawned using taskSpawn and have the same priority of 100. However what I am annoyed by, is that when I write to the in port from my dev machine (using a python script), the read call on the board get's sort of staggered (I wonder if that's the right way to refer to it). It is most likely due to the short availability of hardware buffer space on the UART input buffer (or some such). This is usually not much of a problem if that is all I am doing.
However, as explained earlier, I am trying to copy the whole received character stream into a common memory buffer (guarded by a mutex of course) which is then read by another task and then re-transmitted over another serial port (sort of an in memory loopback if you will)
In lieu of that aforementioned staggering of the read calls, I thought of holding the mutex as long as there are characters to be read from the Inport and once there are no chars to be read, release the mutex and since this is VxWorks, do an explicit taskDelay(0) to schedule the next ready task (my other task). However since this is a blocking read, I am (as expected) stuck on the read call due to which my other thread never gets a chance to execute.
I did think of checking if the buffer was full and then doing the explicit task switch however if any of you have a better idea, I'm all ears.
Also just to see how this staggered read thing works from the perspective of the kernel, I timed it using a time(NULL) call just before and right after the read. So surprisingly, the very first chunk shows up a number, every other chunk after that (if it's a part of the same data block coming from the outside) shows 0. Could anyone explain that as well?
Keen to hear
I don't have 50 rep points for commenting, but without a loopback cable attached, the only way you can arrive at testing serial loopback behavior is to switch the uart into loopback mode. This often means making changes to the specific hardware part driver.

Forcing raw serial mode in C - linux

Not sure how to word the title, but what I'm trying to do is test my micro controller with my Linux PC to ensure data is correct. After hours of searching, I found out that the stty command can change how data is managed through the serial port and it turned out that by default if xon or xoff characters are received from the port, they don't get displayed. At first I thought my computer was too slow that I was losing characters at 57.6Kbps but that wasn't the case.
Back in the day when I was playing with the serial mouse in QuickBasic for DOS, I could use this command to start the serial port:
OPEN "com1:1200,n,7,1,op0" for binary as #1
So what I want to do now is create something simple in C that would allow me to open up the serial port in the rawest mode possible. I want it so that whatever data I give to it is sent to it unmodified. I also want to receive data unmodified. so if the controller decides to send a character the PC would recognize as a special control code, I still want to see the character, not have the PC go funny just because a character matches a control code.
One idea I thought of is to create a fork to the stty program and use nearly every (50+?) parameters added to the program making the requirement of program stack space a bit high.
Another idea I thought of is to do direct I/O with the port address itself (using inb and outb) but I'm not sure if the kernel would run those commands through anything else before the data reaches the port, but I'd rather use that as a last option in case I ever replace my computer and the serial port value changes (or becomes a serial port made through USB to serial converter hardware).
so rather than inb and outb and those variants (like inw), and without executing stty with specifying 50+ parameters in my program, is there a function in C I can use (without requiring a special library not included with a standard linux distribution) to force the serial port device as a raw device so I can do any I/O on it without the kernel modifying or dropping data?

Init a serial communication with c library open() causes TX to send one bit on RPi

I'm trying to set up a serial communication between the RPI and an FPGA. However, there is an issue when using the standard C library open() to init the serial interface: I'm using a scope to monitor what is sent and received via the RX and TX lines. A call to open causes the TX line of the RPI to go low for the length of one bit. I do not see this behavior with other computers/linux PCs. The point is, the FPGA assumes a valid transmission, since he thinks it's a start bit, but it's not.
I checked with minicom installed on the RPI. Same thing. Starting minicom causes the TX line sending one bit. Once minicom has started, the communication runs as expected and all bytes have the correct frame size. Is there any way to suppress the TX line going low upon the open call to init the serial communication? Is this an expected behavior?
This is a super far-fetched hunch, but this code seems a bit suspicious, from the pl011_startup() function in the PL011 serial port driver:
/*
* Provoke TX FIFO interrupt into asserting.
*/
It seems as if it's twiddling the TX line when starting up the port, which would explain the pulse you're seeing. More investigation would surely be needed before concluding this is what happens, of course.
So, I guess my "answer" boils down to: that sounds weird, perhaps it's something with the driver?
Of course, one way of working around this is to apply some care in the FPGA end, assuming you have more control over it. "Proper" framing would take care of this, and make it clear that the spurious send can be discarded.
UPDATE: I meant that if "proper" messages were to be always framed by some sequence of bytes, the FPGA might be able to discard invalid ("unframed") data anyway, and thus become immune to the random pulse. For instance, messages could be defined to always start with SOH (start of header) or SOT (start of text) symbols (bytes with the values 0x01 and 0x02, respectively).

USB serial port garbage when the device resets

When I reset a USB device that is connected via USB serial it starts printing garbage.
If I close out the serial monitor (using Arduino's serial console but should not matter) and reopen, clean text starts printing. Judging by the rate of transmission, the garbage is clearly caused by my device doing normal serial printing -- that is, it is not random garbage.
This is a homebrew USB serial device so the problem could very well be in there. In fact, I can very easily change the code in USB serial device, but cannot do the same for the serial terminal program :-), so would prefer if the solution was there.
No, this is not an incorrect baud or parity setting so please don't suggest it! :-)
Thanks for any help/ideas!
Is it printing garbage even when nothing is being (intentionally) transmitted? If so, there is a hardware problem. Either it resets into a "break" state, or it is jabbering.
If it happens only when data is written through it, then, when the device resets, what serial settings does it default to? Almost certainly this is caused by a speed/data bits/stop bits problem.

Resources