Seeking a Device

The difficult part of the chapter is over; now we’ll quickly detail the llseek method, which is useful and easy to implement.

The llseek Implementation

The llseek method implements the lseek and llseek system calls. We have already stated that if the llseek method is missing from the device’s operations, the default implementation in the kernel performs seeks from the beginning of the file and from the current position by modifying filp->f_pos, the current reading/writing position within the file. Please note that for the lseek system call to work correctly, the read and write methods must cooperate by updating the offset item they receive as argument (the argument is usually a pointer to filp->f_pos).

You may need to provide your own llseek method if the seek operation corresponds to a physical operation on the device or if seeking from end-of-file, which is not implemented by the default method, makes sense. A simple example can be seen in the scull driver:

loff_t scull_llseek(struct file *filp, loff_t off, int whence)
{
  Scull_Dev *dev = filp->private_data;
  loff_t newpos;

  switch(whence) {
   case 0: /* SEEK_SET */
    newpos = off;
    break;

   case 1: /* SEEK_CUR */
    newpos = filp->f_pos + off;
    break;

   case 2: /* SEEK_END */
    newpos = dev->size + off;
    break;

   default: /* can't happen */
    return -EINVAL;
  }
  if (newpos<0) return -EINVAL;
  filp->f_pos = newpos;
  return newpos;
}

The only device-specific operation here is retrieving the file length from the device. In scull the read and write methods cooperate as needed, as shown in Section 3.8 in Chapter 3.

Although the implementation just shown makes sense for scull, which handles a well-defined data area, most devices offer a data flow rather than a data area (just think about the serial ports or the keyboard), and seeking those devices does not make sense. If this is the case, you can’t just refrain from declaring the llseek operation, because the default method allows seeking. Instead, you should use the following code:

loff_t scull_p_llseek(struct file *filp, loff_t off, int whence)
{
  return -ESPIPE; /* unseekable */
}

This function comes from the scullpipe device, which isn’t seekable; the error code is translated to “Illegal seek,” though the symbolic name means “is a pipe.” Because the position indicator is meaningless for nonseekable devices, neither read nor write needs to update it during data transfer.

It’s interesting to note that since pread and pwrite have been added to the set of supported system calls, the lseek device method is not the only way a user-space program can seek a file. A proper implementation of unseekable devices should allow normal read and write calls while preventing pread and pwrite. This is accomplished by the following line—the first in both the read and write methods of scullpipe—we didn’t explain when introducing those methods:

 if (f_pos != &filp->f_pos) return -ESPIPE;

Get Linux Device Drivers, Second Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.