| Linux Kernel & Device Driver Programming↑ |
See scull module code on line.
Some examples of the "ls" output for special inodes.
crw------- 1 root root 4, 2 Mar 9 14:13 tty2 crw------- 1 root root 4, 6 Mar 9 14:13 tty6 crw------- 1 root root 10, 10 Feb 23 2004 adbmouse crw-r--r-- 1 root root 10, 175 Feb 23 2004 agpgart crw------- 1 root root 10, 134 Feb 23 2004 apm_bios crw------- 1 root root 14, 4 Feb 23 2004 audio crw------- 1 root root 14, 20 Feb 23 2004 audio1 crw------- 1 root root 14, 7 Feb 23 2004 audioctl brw-rw---- 1 root floppy 2, 0 Feb 23 2004 fd0 brw-rw---- 1 root floppy 2, 1 Feb 23 2004 fd1 brw-rw---- 1 root disk 3, 0 Feb 23 2004 hda brw-rw---- 1 root disk 3, 1 Feb 23 2004 hda1 brw-rw---- 1 root disk 3, 10 Feb 23 2004 hda10
The "c" indicates a char device and the "b" indicates a block device.
See example of usage in scull_init_module.
int register_chrdev_region(dev_t first, unsigned int count, char *name);
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
void unregister_chrdev_region(dev_t first, unsigned int count);
The above create /proc/devices/ entries, but do not create the device nodes in the filesystem. That must be done separately, and is not the responsibility of the driver.
Example content of /proc/devices:
Character devices: 1 mem 4 /dev/vc/0 4 tty 4 ttyS 5 /dev/tty 5 /dev/console 5 /dev/ptmx 7 vcs 10 misc 13 input 29 fb 36 netlink 128 ptm 136 pts 180 usb Block devices: 1 ramdisk 3 ide0 7 loop 9 md 22 ide1 253 device-mapper 254 mdp
After module initialization, dynamically assigned major device number can be determined from this pseudo-file, and used to create device nodes in the filesystem.
owner: THIS_MODULE; - OK in kernels 2.4 onSET_MODULE_OWNER (&fops); - backward-compatiblestruct file_operations {
struct module * owner;
loff_t (*lseek) (struct file *, loff_t, int);
...
ssize_t (*read) (struct file *, char *, size_t, loff_t *);
...
ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
...
}
Some of the "Method" Fields:
struct file_operations scull_fops = {
.owner = THIS_MODULE,
.llseek = scull_llseek,
.read = scull_read,
.write = scull_write,
.ioctl = scull_ioctl,
.open = scull_open,
.release = scull_release,
};
See the above example of use in context in the scull module.
We need one of the above for each minor type of device.
The above code uses a gcc-specific langauge extension to initialize a few fields of a structure, and get default values (zero) for the rest, without knowing the positions of the named fields or what other fields may be present. This allows code to be robust against reordering and addition of fields irrelevant to the current driver.
struct file {
struct list_head f_list;
struct dentry *f_dentry;
struct vfsmount *f_vfsmnt;
struct file_operations *f_op;
atomic_t f_count;
unsigned int f_flags;
mode_t f_mode;
int f_error;
loff_t f_pos;
struct fown_struct f_owner;
unsigned int f_uid, f_gid;
struct file_ra_state f_ra;
size_t f_maxcount;
unsigned long f_version;
void *f_security;
/* needed for tty driver, and maybe others */
void *private_data;
...
struct address_space *f_mapping;
}
filp->f_dentry->d_inode to access the inode
inode {
...
dev_t i_rdev;
...
struct cdev *i_cdev;
};
See code of scull_init_module for how driver registration and initialization of the scull_dev and cdev structures is done. Steps include:
Note that the older form of registration, using register_chrdev is still used by many drivers, but is now deprecated.
The call to kobj_map in cdev_add apparently is responsible for registering the new driver binding with the implementation of the sysfs virtual filesystem. This is undone later by cdev_dell which calls kobj_unmap.
See code of scull_cleanup_module for how driver unregistration and other finalization is done.
See code of scull_open for example.
Observe use of container_of macro, defined in kernel.h, to obtain a pointer to the struct scull_dev that contains the struct cdev referred to by inode->i_cdev.
Also observe use of kernel semaphore operations down_interruptible and up to protect a critical section.
See code of scull_release for a trivial example. Nothing needs to be done, because there is no hardware to shut down. More interesting examples will come up later.
Note there are two different kinds of "last close" here:
The kernel does not call the close method of the file object for every application-level close() call. It only calls for the "last close", i.e., when the reference count of the file object (f_count) goes to zero. That is the job of the kernel.
The job of the driver is to do the analogous thing for its own per-driver data structures, using device driver usage count.
struct scull_qset {
void **data; /* pointer to array of pointers to quanta */
struct scull_qset *next; /* pointer to next quantum set */
};
struct scull_dev {
struct scull_qset *data; /* pointer to first quantum set */
int quantum; /* the current quantum size */
int qset; /* the current array size */
unsigned long size;
unsigned int access_key; /* used by sculluid and scullpriv */
struct semaphore sem; /* mutual exclusion semaphore */
struct cdev cdev; /* char device structure */
} scull_dev;
This is not necessarily a data structure that anyone should emulate. The main point of this chapter is how to implement read and write. This structure is just an artifact of the example, not a feature of the Linux kernel. As the book says, a more efficient design would allocate memory in whole pages. It would also avoid the internal fragmentation problem this one has.
/*
* Empty out the scull device; must be called with the device
* semaphore held.
*/
int scull_trim(struct scull_dev *dev)
{
struct scull_qset *next, *dptr;
int qset = dev->qset; /* "dev" is not-null */
int i;
for (dptr = dev->data; dptr; dptr = next) { /* all the list items */
if (dptr->data) {
for (i = 0; i < qset; i++)
kfree(dptr->data[i]);
kfree(dptr->data);
dptr->data = NULL;
}
next = dptr->next;
kfree(dptr);
}
dev->size = 0;
dev->quantum = scull_quantum;
dev->qset = scull_qset;
dev->data = NULL;
return 0;
}
You should be already familiar with the danger of race conditions, and the use of locks to prevent them. This example illustrates a form of lock that works between processes, which may block while they are executing in the driver. You will later see other forms of lock that are used with interrupt handlers and other forms of code that are not allowed to block.
/* Initialize each device. */
for (i = 0; i < scull_nr_devs; i++) {
scull_devices[i].quantum = scull_quantum;
scull_devices[i].qset = scull_qset;
init_MUTEX(&scull_devices[i].sem);
scull_setup_cdev(&scull_devices[i], i);
}
See the example above in context.
if (down_interruptible(&dev->sem)) return -ERESTARTSYS; scull_trim(dev); /* ignore errors */ up(&dev->sem);
See the example above in context.
Semaphores are implemented by a set of macros defined in asm-i386/semaphore.h
The macros use wait queues, which we will see in more detail in Chapter 5.
Semaphores locking operations may block.
It is the calling process that is blocked until the lock is released.
Spinlock locking operations may spin (loop).
It is the calling processor that spins until the lock is released.
Never call the "down" operation unless it is OK for the current thread of control to block. Especially:
Learn to treat critical sections systematically, always following the same general usage patterns. That way, you will be less likely to make errors. Imitation/duplication of coding idioms is OK.
Always remember to release locks before exiting (whether via a return, break, or goto) a critical section.
Always look out for potential deadlocks. If you are thinking about holding two locks at once, make sure they are always locked in a well-known order, or else find a way to get by using only one lock at a a time.
The following examples of usage are from scull_read and scull_write:
if (copy_to_user(buf, dptr->data[s_pos]+q_pos, count)) {
ret = -EFAULT; goto out;
}
*f_pos += count;
...
if (copy_from_user(dptr->data[s_pos]+q_pos, buf, count)) {
ret = -EFAULT; goto out;
}
scull_read (struct file *filp, char __user *buf, size_t count, loff_t *ppos);
See example scull_write.
| © 2003, 2005 T. P. Baker. ($Id: ch3.html,v 1.1 2010/06/07 14:29:15 baker Exp baker $) |