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 file
as “this” parameteropen()
: implementsman -s 2 open
-inode
already loaded,struct file
allocated ⟶ “constructor”
read()
: implementsman -s 2 read
write()
: implementsman -s 2 write
unlocked_ioctl()
: implementsman -s 2 ioctl
flush()
: reference count decrementedrelease()
: reference count reached zero ⟶struct file
freed
Note
flush()
/release()
: see fork()/dup()
open()
: Userspace¶
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 file
Looks weird
Is simple
⟶ Later by example
ioctl()
: Userspace¶
Swiss army knife …
Used to communicate with drivers
All that doesn’t fit in
read()
,write()
#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
: userspacerequest
arg
: 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:
ioctl
request 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
: actualioctl
requestsize
: 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 */,
/*...*/
};