I\'m designing a device driver that simply reads and writes to a character buffer. My question is however regarding the two functions in the file_operations
str
"loff_t" is a "long offset", i.e., a seek position that unifies the crazy diversity of off_t
, off64_t
, and so on, so that drivers can just use loff_t and not worry about it.
The pointer itself, at the time you get into the driver, points to the offset provided by the user (assuming it's user code doing the driver access—technically the kernel can provide its own, but the user case is the one to think about) via lseek
or llseek
or lseek64
, etc., and then by ordinary read and write operations. Consider the case of a regular on-disk file: when you first open
the file, you (as a user) get the kernel to provide a data structure that keeps track of your current position in the file, so that if you read
or write
some bytes, the next read
or write
picks up from where you left off.
Furthermore, if you dup
the file descriptor, or do the equivalent by (e.g.) fork
and exec
in terms of running a sequence of commands, that seek-position is shared by all the inheriting processes. Hence, at the shell prompt, the command:
(prog1; prog2; prog3) > outputfile
creates an output file, then dup
s the descriptor to the three programs, so that output that prog2
writes goes into the file immediately after the output from prog1
, and output from prog3
follows the other two—all because all three separate processes share the same underlying kernel data structure with the same internal loff_t
.
The same applies to device driver files. When your read and write functions are called, you receive the "current offset" as provided by the user, and you can (and should) update it as needed ... assuming there is any need (e.g., you want to provide users with the appearance of a regular file, including the fact that seek offsets move as you read and write). If the device has some logical application of the seek offset, you can use that here.
Of course, there's a lot more to device drivers, which is why there are entire book-chapters on this stuff (q.v.). :-)
Torek's answer is excellent. Just adding a bit extra detail/context... From an earlier Linux kernel (2.6.28), here is an example of offset in use in a system call... it copies the offset from user space to a temporary variable before getting into the kernel driver-invocation mechanism, and then copies it back out to the user file. This is how the offset the driver sees is decoupled from the user view of it, and facilitates the situations where offset is NULL in the system call, so no SEGVIO occurs.
SYSCALL_DEFINE4(sendfile64, int, out_fd, int, in_fd, loff_t __user *, offset, size_t, count)
{
loff_t pos;
ssize_t ret;
if (offset) {
if (unlikely(copy_from_user(&pos, offset, sizeof(loff_t))))
return -EFAULT;
ret = do_sendfile(out_fd, in_fd, &pos, count, 0);
if (unlikely(put_user(pos, offset)))
return -EFAULT;
return ret;
}
return do_sendfile(out_fd, in_fd, NULL, count, 0);
}