开发者

C write() doesn't send data until close(fd) is called

So I have this test code to send "HELLO" over a USB serial port:

int fd;
struct termios tty;

if((fd = open("/dev/ttyUSB0", O_WRONLY|O_NONBLOCK|O_NOCTTY)) == -1){
err(1, "Cannot open write on /dev/ttyUSB0");
}

tcgetattr(fd, &tty);
tty.c_ifl开发者_C百科ag = 0;
tty.c_oflag = 0;
tty.c_lflag = 0;
tty.c_cflag = 0;
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 0;
cfsetospeed(&tty, B19200);
cfsetispeed(&tty, B19200);
tty.c_cflag |= CREAD|CRTSCTS|HUPCL|CS8;
tcsetattr(fd, TCSANOW, &tty);

printf("Write: %i\n", write(fd, "HELLO", 5));

sleep(5);

if(close(fd) != 0){
warn("Could not close write fd");
}

The program executes fine and "HELLO" is sent but there is one problem. "HELLO" doesn't seem to be sent when the write() function is called, but rather when the file descriptor is closed. I added the sleep(5) line above to test out this theory and sure enough, "HELLO" is sent ~5 seconds after the program is executed. How can I get "HELLO" to be sent immediately after the write() command instead of on close()?


The device is a tty device, so fsync isn't going to help, maybe not fflush either.

By default the device is working in canonical mode which means that data is packaged up into units of lines. You'll probably find that adding a cr/lf pair to your data will cause it to be sent.

You need to make sure canonical mode is off. Also, R's answer will be of use.

http://en.wikibooks.org/wiki/Serial_Programming/termios


From the man page of write():

A successful return from write() does not make any guarantee that data has been committed to disk. In fact, on some buggy implementations, it does not even guarantee that space has successfully been reserved for the data. The only way to be sure is to call fsync(2) after you are done writing all your data.

You need to call fsync() on your file descriptor to ensure the data is actually committed.


first of all, no idea why you first set all termios fields to 0, and then later, without any modification to that 0 preceding it, decide to set the usual rs232 flags on the cflag. (rather than doing that without the OR directly, where you now set it to 0, above).

what you might like -instead- of setting all those flags is just cfmakeraw() the termios fields.

also, sync(); without any parameters (NOT fsync! ;) seems to send all pending output to ALL filedescriptors, not just block devices. also tcp sockets and rs232..

and also open() has an option O_SYNC (O_SYNC and O_ASYNC have confusing names but have nothing to do with the serial line protocol being clocked or not, the one immediately commits write()'s and the other generates a signal for trapping when input becomes available (kinda like irq based rs232 on dos ;)

setting O_SYNC in the open() might already fix your issue.

also 'by reading the data on the other end'... there are these things called 'leds' and 'resistors' which you can just connect to TXD and SEE the data ;) also there are things called 'rs232 breakout box' or a scope that can make it -directly visible- ;) much easier that way than 'guessing' which side is not behaving properly.

WARNING: DID NOT TEST CODE. it compiles. but i have all my ttyUSB0 cables in another building. but i think your main issue is O_SYNC anyway. setting all termios crap to 0 is pretty much the same as cfmakeraw()... also why set CREAD if you are gonna open it write only? (why open it write only rather than readwrite anyway? - and also with write only you won't have to be scared of it becoming a controlling tty (O_NOCTTY ;) so in the case of write only, that's not exactly needed either...

just noticed the %i (same for %d btw) formatter also triggers a type mismatch warning the ssize_t return value of write() so casted that to (int)

#include<termios.h>
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>

void main(){
int fd;
struct termios tty;
fd=-1;
while(fd<0){fd=open("/dev/ttyUSB0",O_WRONLY|O_NONBLOCK|O_NOCTTY|O_SYNC);sleep(1);};
cfmakeraw(&tty);
tty.c_cflag=CREAD|CRTSCTS|HUPCL|CS8;
cfsetospeed(&tty,B19200);
cfsetispeed(&tty,B19200);
tcsetattr(fd,TCSANOW,&tty);
printf("Write: %i\n",(int)write(fd,"HELLO",5));
sync();//if all else fails, also try without, O_SYNC should already fix that.
close(fd);
};


Output ports are often buffered, so that there's a greater or lesser gap between you writing to an output stream, and the content actually being sent to the disk, line, or whatever. This is generally for efficiency.

See fflush(3) to force the buffer to be committed to the output.

You might also be able to open the output descriptor in a way which makes it non-buffered, but using fflush to say 'that's it, I'm done', is probably better.


Change this line:

tty.c_cc[VMIN] = 0;

to this:

tty.c_cc[VMIN] = 1;


the buffer isn't flushed. fflush.


Please see this question. BAsically you need to flush the file in order for IO to take place when you want to.


Try doing a

fflush( NULL );

after the write(). Maybe there is some internal buffer which is not flushed.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜