Program 1 illustrated how a driver could return data to a client using the read callback. As you might expect, there is a corresponding write callback that allows the driver to receive data from a client. write takes four arguments, similar to the read callback:
The semantics of write's return value are the same as in a kernel callback:
Program 2, echo.c, is an example implementation of a device
(/dev/echo) that uses both read() and write()
callbacks. A client that tries to read() from this device will
get the contents of the most recent write(). For example:
% echo Hello there > /dev/echo % cat /dev/echo Hello there % echo Device drivers are fun > /dev/echo % cat /dev/echo Device drivers are fun |
The implementation of /dev/echo keeps a global variable, data, which serves as a cache for the data most recently written to the driver by a client program. The driver does not assume the data is null-terminated, so it also keeps track of the number of bytes of data available. (These two variables appear on lines 1-2.)
The driver's write callback first frees any data which might have been allocated by a previous call to write (lines 26-29). Next, on line 33, it attempts to allocate new memory for the new data arriving. If the allocation fails, -ENOMEM is returned to the client. If the allocation is successful, the driver copies the data into its local buffer and stores its length (lines 37-38). Finally, the driver tells the user that the entire buffer was consumed by returning a value equal to the number of bytes the user tried to write (user_length).
The read callback has some extra features that we did not see in Program 1's read() callback. The most important is that it allows the driver to read the available data incrementally, instead of requiring that the first read() executed by the client has enough space for all the data the driver has available. In other words, a client can do two 50-byte reads, and expect the same effect as if it had done a single 100-byte read.
This is implemented using *offset, the user's file pointer. If the user is trying to read past the amount of data we have available, the driver returns EOF (lines 8-9). Normally, this happens after the client has finished reading data. However, in this driver, it might happen on a client's first read if nothing has been written to the driver yet or if the most recent write's memory allocation failed.
If there is data to return, the driver computes the number of bytes that should be copied back to the client--the minimum of the number of bytes the user asked for, and the number of bytes of data that this client hasn't seen yet (line 12). This data is copied back to the user's buffer (line 15), and the user's file pointer is advanced accordingly (line 16). Finally, on line 19, the client is told how many bytes were copied to its buffer.