Linux Device Driver
Apr 15, 2002
Yongguang Zhang
Today’s Lecture
- Q&A
- How are we doing with the guest lectures?
- Doing a Device Driver
- A 1K-buffer character device
Linux Device Driver Review
- Three types of device driver in Linux
- Character device, block device, network device
- Special device file: /dev
- The old way: need a major and a minor number
- The new way: devfs (device file system)
- You can support both
- Usually as loadable module
How To Write a Device Driver (1)
- Write the required functions for the device operation structure
- Character device: struct file_operations
- owner, llseek, read, write, readdir, poll, ioctl, mmap, open, flush, lock, release, fsync, fasync, lock, readv, writev, sendpage, get_unmapped_area
- Block device: struct block_device_operations
- open, release, ioctl, check_media_change, revalidate
- Not all functions are required if the operation isn't needed in the device driver
How To Write a Device Driver (2)
- Write an "init" function to register the device driver
- Register the device the "old" way: call either
- register_chrdev (unsigned int major, const char * name, struct file_operations *fops)
- register_blkdev (unsigned int major, const char * name, struct block_device_operations *bdops)
- Register the device the "new" (devfs) way: call
- devfs_register_chrdev() or devfs_register_blkdev() if you want support for the old device file format as well
- devfs_register (devfs_handle_t dir, const char *name, unsigned int flags, unsigned int major, unsigned int minor, umode_t mode, void *ops, void *info)
How To Write a Device Driver (3)
- Write an "exit" function to unregister the device driver
- Specify the "init" and "exit" functions
- module_init(xxx_init)
- module_exit(xxx_exit)
devfs_register()
- Parameters
- dir: handle to parent directory (NULL: root of /dev)
- name: name of entry
- flags: (usually DEVFS_FL_DEFAULT)
- major and minor: optional
- mode: access mode (rwx) and file type
- ops: device operation structure (e.g., struct file_operations)
- info: pointer to private data for open file structure
Skeleton "Init" Function
static int __init xxx_init(void)
{
/* probe the hardware, request irq, ... */
if (devfs_register_chrdev(XXX_MAJOR, "xxx", &xxx_fops) < 0) {
printk( ... ); return -ENODEV;
}
devfs_handle = devfs_register(NULL, "xxx", DEVFS_FL_DEFAULT,
XXX_MAJOR, 0,
S_IFCHR | S_IRUSR | S_IWUSR,
&xxx_fops, NULL);
/* rest of the initial setup */
printk( ... ); return 0;
}
xxx_init() Comments
- Define XXX_MAJOR to be the major number
- For block device
- replace _chrdev with _blkdev
- replace S_IFCHR with S_IFBLK
Skeleton "Exit" Function
static void __exit xxx_exit (void)
{
/* clean up */
devfs_unregister_chrdev(XXX_MAJOR, "xxx");
devfs_unregister(devfs_handle);
/* clean up */
}
module_init(xxx_init);
module_exit(xxx_exit);
Skeleton Operation Structure
static struct file_operations xxx_fops =
{
owner: THIS_MODULE,
read: xxx_read,
write: xxx_write,
open: xxx_open,
release: xxx_release
};
Skeleton "Open" Operation
static int xxx_open(struct inode * inode, struct file * filp)
{
/* check MINOR(inode->i_rdev) for minor number */
/* if you have different fops for different minor devices, you can
do: filp->f_op = &xxx_yyy_fops; */
/* you can have carry private data in filp->private_data */
MOD_INC_USE_COUNT;
}
Who calls xxx_open()?
- sys_open() creates the inode, the open file structure, then calls the special file's open operation
Example Open Function
struct xxx_dev {
char data[1024];
int len;
devfs_handle_t devfs_handle;
struct semaphore sem;
};
static struct xxx_dev xxx_dev;
static int xxx_open(struct inode * inode, struct file * filp)
{
if (! filp->private_data)
private_data = &xxx_dev;
MOD_INC_USE_COUNT;
}
Example Read Functions
static ssize_t xxx_read(struct file *filp, char *buf, size_t count, loff_t*ppos)
{
struct xxx_dev *d = filp->private_data;
if (down_interruptible(&d->sem))
return -ERESTARTSYS;
if (*ppos >= d->len) {
up(&d->sem); return 0;
}
if (*ppos + count > d->len)
count =d->len - *ppos;
if (copy_to_user(buf, d->data+*ppos, count)) {
up(&d->sem); return -EFAULT;
}
up(&d->sem);
*ppos += count;
return(count);
}
Example Write Functions
static ssize_t xxx_write(struct file *filp, const char *buf, size_t count, loff_t *ppos)
{
struct xxx_dev *d = filp->private_data;
if (down_interruptible(&d->sem))
return -ERESTARTSYS;
if (*ppos >= 1024) {
up(&d->sem); return 0;
}
if (*ppos + count > 1024)
count = 1024 - *ppos;
if (copy_from_user(d->data+*ppos, buf, count)) {
up(&d->sem); return -EFAULT;
}
up(&d->sem);
*ppos += count;
return(count);
}
Example Release Function
static ssize_t xxx_release(struct inode *inode, struct file *filp)
{
MOD_DEC_USE_COUNT;
}
Questions
- Why do you initialize the semaphore?
- sema_init(&xxx_dev.sem,1);
Next Lecture
- Network device driver
- Projects Q&A
- Evaluation Forms
© 2002 Yongguang Zhang