《linux 设备驱动》---第三章:字符驱动 一
学习目的:编写一个完整的字符设备驱动。
学习成果:完成一个字符驱动模块。
scull( Simple Character Utility for Loading Localities). scull 是一个字符驱动, 操作
一块内存区域好像它是一个设备. 在本章, 因为 scull 的这个怪特性, 我们可互换地使用设备这个词和"scull 使用的内存区".
编写驱动的第一步是定义驱动将要提供给用户程序的能力(机制)。linux设备分为三种:字符,块,网络
scull实现的设备被引用成一种模型。
3.1主/次设备编号
设备号表示设备对应的驱动程序;次设备号由内核使用,用于正确确定设备文件所指的设备。
dev_t 类型(在 中定义)用来持有设备编号 -- 主次部分都包括.
dev_t 是 32 位的量, 12 位用作主编号, 20位用作次编号.
设备编号获得:
//中的一套宏定义. 为获得一个 dev_t的主或者次编号
MAJOR(dev_t dev);
MINOR(dev_t dev);
如果你有主次编号, 需要将其转换为一个 dev_t, 使用:
MKDEV(int major, int minor);
3.2分配和释放设备编号
我们关注几个函数:register_chrdev_region;int alloc_chrdev_region;void unregister_chrdev_region。
int register_chrdev_region(dev_t first, unsigned int count, char *name); //用于注册设备
//first 是你要分配的起始设备编号,通常为0
//count是你请求的连续设备编号总数.不要太大
//name 是应当连接到这个编号范围的名字。
/*如果分配成功进行, register_chrdev_region 的返回值 是 0. 出错的情况下, 返回一个负的错误码, 你不能存取请求的区域.*/
|
动态分配的方法:
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
|
使用这个函数, dev 是一个只输出的参数, 它在函数成功完成时持有你的分配
范围的第一个数. fisetminor 应当是请求的第一个要用的次编号; 它常常是 0.
count 和 name 参数如同给 request_chrdev_region 的一样.
释放设备:
void unregister_chrdev_region(dev_t first, unsigned int count);
|
调用 unregister_chrdev_region 的地方常常是你的模块的 cleanup 函数.
上面的函数分配设备编号给你的驱动使用, 但是它们不告诉内核你实际上会对
这些编号做什么. 在用户空间程序能够存取这些设备号中一个之前, 你的驱动
需要连接它们到它的实现设备操作的内部函数上. 我们将描述如何简短完成这
个连接, 但首先顾及一些必要的枝节问题.
PS:以上都是节选于《LDD》--第三章原文
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 %d\n", scull_major); return result; }
|
本书使用的几乎所有例子驱动使用类似的代码来分配它们的主编号.其中细节可以自己去看《ldd》。
struct file_operations是一个字符设备把驱动的操作集合在一起,与设备号进行互动的结构。文件在linux/fs.h中描述。
struct file_operations { struct module *owner; //设备拥有者
loff_t (*llseek) (); ssize_t (*read) (); ssize_t (*write) (); ssize_t (*aio_read) (); ssize_t (*aio_write) (); int (*readdir) (); unsigned int (*poll) (); int (*ioctl) (); long (*unlocked_ioctl) (); long (*compat_ioctl) (); int (*mmap) (); int (*open) (); int (*flush) (); int (*release) (); int (*fsync) (); int (*aio_fsync) (); int (*fasync) (); int (*lock) (); ssize_t (*sendfile) (); ssize_t (*sendpage) (); unsigned long (*get_unmapped_area) (); int (*check_flags) (); int (*dir_notify) (); int (*flock) (); ssize_t (*splice_write) (); ssize_t (*splice_read) (); };
|
阅读(1216) | 评论(0) | 转发(0) |