1. 设备号
主,次:
crw-rw---- 1 root tty 7, 134 2013-06-16 09:45 vcsa6
crw------- 1 root root 10, 63 2013-06-16 09:45 vga_arbiter
crw-rw-rw- 1 root root 1, 5 2013-06-16 09:45 zero
其中,主设备号标识设备对应的驱动程序,次设备内核使用直接指向设备本身。
内核中,用dev_t保存设备号。以下宏用来在dev_t和主次设备号之间转换。
MAJOR(dev_t dev);
MINOR(dev_t dev);MKDEV(int major, int minor);
分配:
可用动态或静态两种方法来分配设备号,对应方法为:
-
if (scull_major) {
-
dev = MKDEV(scull_major, scull_minor);
-
result = register_chrdev_region(dev, scull_nr_devs, "scull");
-
} else {
-
result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull");
-
scull_major = MAJOR(dev);
-
}
-
-
if (result < 0) {
-
printk(KERN_WARNING "scull: can't get major %dn", scull_major);
-
return result;
-
}
2. 设备相关的数据结构
struct file_operations:指定了一些操作,如open, read, write。应该如下初始化:
-
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,
-
};
struct file:内核结构,与用户空间的FILE不同,它为每个打开的文件创建一个,简称flip。
struct
inode:此结构表示真正的文件,以下两字段重要:dev_t i_rdev; 指出对应设备文件的设备号; struct cdev
*i_cdev;若inode指向字符设备,则这指针指向struct cdev。可用宏从inode结构中取得主次设备号。
3. 字符设备的注册
注册就是获取一个struct cdev结构,有以下两种方法:
独立获取cdev结构应该这样:
-
struct cdev *my_cdev = cdev_alloc();
-
my_cdev->ops = &my_fops;
否则如果在自己的结构体中嵌入cdev时应该这样:
-
static void scull_setup_cdev(struct scull_dev *dev, int index)
-
{
-
int err, devno = MKDEV(scull_major, scull_minor + index);
-
-
cdev_init(&dev->cdev, &scull_fops);
-
dev->cdev.owner = THIS_MODULE;
-
dev->cdev.ops = &scull_fops;
-
err = cdev_add (&dev->cdev, devno, 1);
-
}
4. 驱动简单实现:
open:
-
int (*open)(struct inode *inode, struct file *filp)
输入为inode,可从中取得cdev指针,进而用container_of宏取得自定结构体,最终将自定结构体传递给filp的private_data结构。如有需要,处理自带结构中的内存。
release:做适当的清理。
read: write:
就是从自带结构中相关数据与用户空间数据读写。
注意的是,
内核中不可直接引用用户空间的指针。因为可能页被换出,会导致出错。必须用copy_to_user, copy_from_user宏来完成数据传送。这两个宏是cpu级别的汇编代码实现的。
用这两个宏时如果用户空间的内存被换出,那么此进程会睡眠。所以
必须保证任何访问用户空间的函数必须可重入
阅读(514) | 评论(0) | 转发(0) |