File Operations on Character Devices (Slideshow)#
File Operations: Interface Definition (“vtable”) (1)#
Character devices are interfaces
Driver writer supplies methods (read, write, …)
Semantics are up to the implementor
Good Unix citizenship encouraged (but not enforced)
struct file_operations {
     struct module *owner;
     int (*open) (struct inode *, struct file *);
     ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
     ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
     long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
     /*...*/
};
#include <linux/fs.h>
const struct file_operations my_ops = {
    .owner = THIS_MODULE,
    .open = my_open,
    .read = my_read,
    .write = my_write,
    .unlocked_ioctl = my_ioctl,
    /*...*/
};
Available Methods#
More methods “overloadable” …
All methods receive
struct fileas “this” parameteropen(): implementsman -s 2 open-inodealready loaded,struct fileallocated ⟶ “constructor”
read(): implementsman -s 2 readwrite(): implementsman -s 2 writeunlocked_ioctl(): implementsman -s 2 ioctlflush(): reference count decrementedrelease(): reference count reached zero ⟶struct filefreed
Note
flush()/release(): see fork()/dup()
open(): Userspace#
man -s 2 open#int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
Opens and/or creates a file
Many flags/parameters
Permissions
Driver not concerned with all that
⟶ Virtual File System layer
open(): Kernelspace#
int my_open(struct inode* inode, struct file* filp) {...}
All complicated stuff (permissions etc.) done by VFS layer
Hook for driver to associate driver data with
struct fileLooks weird
Is simple
⟶ Later by example
ioctl(): Userspace#
Swiss army knife …
Used to communicate with drivers
All that doesn’t fit in
read(),write()
man -s 2 ioctl##include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);
fd: handle to open device noderequest: device specific request code...: (if any) a single parameterUsually a pointer
Can be integer, but should be of pointer size
Type depends on value of
request
ioctl(): Kernelspace#
static long my_ioctl(
    struct file *file,
    unsigned int request,
    unsigned long arg) {...}
file: (as always) in-kernel pendant to userspace file descriptorrequest: userspacerequestarg: the...parameter from userspace. Cast arbitrarily, depending onrequest
ioctl(): Requests#
Ideally request is a simple number; e.g.
enum my_ioctl_requests
{
    MY_REQUEST_SUCH,
    MY_REQUEST_SUCH_AND_SUCH,
    /*...*/
};
Things are not so simple though
History-laden
Historically, hardcoding major an minor number led to conflicts between devices (so they say)
Safety measure:
ioctlrequest numbers need to be endodedType information of 3rd argument
Direction
_IO*() Macros#
/*
 * Used to create numbers.
 *
 * NOTE: _IOW means userland is writing and kernel is reading. _IOR
 * means userland is reading and kernel is writing.
 */
#define _IO(type,nr)         _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size)   _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOW(type,nr,size)   _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOWR(type,nr,size)  _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
type: some (arbitrary?) “magic number”nr: actualioctlrequestsize: the C type, not the size (OMG)
_IO*() Macros: Usage#
enum my_ioctl_requests
{
    MY_REQUEST_SUCH          = _IO(666, 0), /* no argument */,
    MY_REQUEST_SUCH_AND_SUCH = _IOW(666, 1, int), /* user to kernel, int argument */,
    /*...*/
};