第三章 三个数据结构以及字符设备的注册
上一篇讲到了注册设备编号,显然,注册设备编号只是编写驱动代码的第一个步骤。为了完成以后的一些步骤,先介绍三个重要的内核数据结构。
file_operation囊括了所有设备操作到设备号的链接。在linux中驱动以文件的方式存在,而file_operation则是对文件(这里是是字符设备)的所有操作的集合。由于file_operation的成员太多,而当前scull项目仅仅用到了其中的几个。 scull项目的file_operation初始化如下:
1 struct file_operations scull_fops = {
2 .owner = THIS_MODULE,
3 .llseek = scull_llseek,
4 .read = scull_read,
5 .write = scull_write,
6 .ioctl = scull_ioctl,
7 .open = scull_open,
8 .release = scull_release,
9 };
owner指向拥有这个结构的模块,几乎所有情况下,它的值都是THIS_MODULE,这是一个定义在<linux/module.h>中的宏;llseek 方法用作改变文件中的当前读/写位置, 并且新位置作为(正的)返回值;read用来从设备中获取数据;write用来发送数据给设备;.ioctl 用来发出设备特定命令(非read或write);open为打开设备文件,显然这应该是对设备文件进行的第一个操作;.release 释放文件结构。
file数据结构代表一个打开的文件(记住设备被当做了文件)。如同file_operation指定了操作的集合,file就是这些操作的目标对象了。
inode数据结构也表示文件,不过他跟file数据结构不同(估计这个区别以后才能了解)。对于编写驱动来说,这个数据结构就两个字段有用。dev_t i_rdev代表设备编号,struct cdev *i_cdev;代表一个字符设备。详细的信息到使用的时候再深入研究。
要说明的是,linux源代码在初始化这些数据结构的时候一般都是用c语言的标记式结构初始化语法。
linux内核在内部使用类型struct cdev来代表字符设备。而在scull中,使用一个自定义的struct scull_dev类型的
结构来表示每个设备,这个结构包括了一个struct cdev结构:
1 struct scull_dev {
2 struct scull_qset *data; /* Pointer to first quantum set */
3 int quantum; /* the current quantum size */
4 int qset; /* the current array size */
5 unsigned long size; /* amount of data stored here */
6 unsigned int access_key; /* used by sculluid and scullpriv */
7 struct semaphore sem; /* mutual exclusion semaphore */
8 struct cdev cdev; /* Char device structure */
9 };
初始化这一结构并注册的函数如下:
1 static void scull_setup_cdev(struct scull_dev *dev, int index)
2 {
3 int err, devno = MKDEV(scull_major, scull_minor + index);
4 cdev_init(&dev->cdev, &scull_fops);
5 dev->cdev.owner = THIS_MODULE;
6 dev->cdev.ops = &scull_fops;
7 err = cdev_add (&dev->cdev, devno, 1);
8 /* Fail gracefully if need be */
9 if (err)
10 printk(KERN_NOTICE "Error %d adding scull%d", err, index);
11 }
这样就完成了一个字符设备的初始化和注册。