tags:

views:

4428

answers:

6

It turns out this whole misunderstanding of the open() versus fopen() stems from a buggy I2C driver in the Linux 2.6.14 kernel on an ARM. Backporting a working bit bashed driver solved the root cause of the problem I was trying to address here.

I'm trying to figure out an issue with a serial device driver in Linux (I2C). It appears that by adding timed OS pauses (sleeps) between writes and reads on the device things work ... (much) better.

Aside: The nature of I2C is that each byte read or written by the master is acknowledged by the device on the other end of the wire (slave) - the pauses improving things encourage me to think of the driver as working asynchronously - something that I can't reconcile with how the bus works. Anyhoo ...

I'd either like to flush the write to be sure (rather than using fixed duration pause), or somehow test that the write/read transaction has completed in an multi-threaded friendly way.

The trouble with using fflush(fd); is that it requires 'fd' to be stream pointer (not a file descriptor) i.e.

FILE * fd = fopen("filename","r+");
... // do read and writes
fflush(fd);

My problem is that I require the use of the ioctl(), which doesn't use a stream pointer. i.e.

int fd = open("filename",0_RDWR);
ioctl(fd,...);

Suggestions?

+5  A: 

You have two choices:

  1. Use fileno() to obtain the file descriptor associated with the stdio stream pointer

  2. Don't use <stdio.h> at all, that way you don't need to worry about flush either - all writes will go to the device immediately, and in theory the write() call won't even return until the lower-level IO has completed.

For device-level IO I'd say it's pretty unusual to use stdio. I'd strongly recommend using the lower-level open(), read() and write() functions instead (based on your later reply):

int fd = open("/dev/i2c", O_RDWR);
ioctl(fd, IOCTL_COMMAND, args);
write(fd, buf, length);
Alnitak
Avoiding <stdio.h> is what I've been doing; the "Aside" in my original post led me to thinking that the file descriptors _might_ have been buffered. Thanks for confirming that they aren't and that I should be looking for another reason pummelling the I2C bus causes trouble.
Jamie
Let's make the terminology clear - a UNIX file descriptor is an integer index and is supposed to be unbuffered so that a call to write/read is immediate. It is supposed to be very efficient for "large" writes and inefficient for single byte writes. fopen gives you buffered I/O. Use open/read/write.
plinth
A: 

So Altniak; are you suggesting something like this might work? (ignoring errors ...)

...
FILE * fd = fopen("/dev/devicename","r+");

ioctl(fileno(fd),IOCTL_COMMAND,args);
fwrite(buf, sizeof (* buf), length, fd);
fflush(fd);
...

Is there an analog to 'go the other way' from file descriptor to file stream?

Jamie
I don't know the semantics of your i2c device, but that looks reasonable. However I'd prefer: fd = open("/dev/i2c", O_RDWR); ioctl(fd, IOCTL_COMMAND, args); write(fd, buf, length);
Alnitak
and yes, you can go from fd to stream using fdopen()
Alnitak
But you only do the mapping from fd to fp once...if you do it each time, you re going to leak horribly.
Jonathan Leffler
+2  A: 

fflush() only flushes the buffering added by the stdio fopen() layer, as managed by the FILE * object. The underlying file itself, as seen by the kernel, is not buffered at this level. This means that writes that bypass the FILE * layer, using fileno() and a raw write(), are also not buffered in a way that fflush() would flush.

As others have pointed out, try not mixing the two. If you need to use "raw" I/O functions such as ioctl(), then open() the file yourself directly, without using fopen<()/code> and friends from stdio.

unwind
A: 

If you want to go the other way round (associate FILE* with existing file descriptor), use fdopen() :

                                                                                                                                              FDOPEN(P)

NAME

   fdopen - associate a stream with a file descriptor

SYNOPSIS

   #include <stdio.h>

   FILE *fdopen(int fildes, const char *mode);
Arkadiy
A: 

It sounds like what you are looking for is the fsync() function (or fdatasync()?), or you could use the O_SYNC flag in your open() call.

jstedfast
+1  A: 

I think what you are looking for may be

int fsync(int fd);

int fdatasync(int fd);

fsync will flush the file from kernel buffer to the disk. fdatasync will also do except for the meta data.

Danke Xie