linux系统将设备分为3类:字符设备、块设备、网络设备。可以把一个系统划分为应用、文件系统、设备驱动结构示意图如下:
设备分层示意图
字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据。字符设备是面向流的设备,常见的字符设备有鼠标、键盘、串口、控制台和LED设备等。
块设备:是指可以从设备的任意位置读取一定长度数据的设备。块设备包括硬盘、磁盘、U盘和SD卡等。
每一个字符设备或块设备都在/dev目录下对应一个设备文件。linux用户程序通过设备文件(或称设备节点)来使用驱动程序操作字符设备和块设备
字符设备驱动模型
int register_chrdev_region(dev_t from, unsigned count, const char *name);
功能:申请使用从from开始的count 个设备号(主设备号不变,次设备号增加)
静态申请相对较简单,但是一旦驱动被广泛使用,这个随机选定的主设备号可能会导致设备号冲突,而使驱动程序无法注册。
动态方法如下
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name); 功能:请求内核动态分配count个设备号,且次设备号从baseminor开始。 动态申请简单,易于驱动推广,但是无法在安装驱动前创建设备文件(因为安装前还没有分配到主设备号)。 静态申请是已知起始设备号的情况,如先使用cat /proc/devices 命令查得哪个设备号未事先使用(不推荐使用静态申请);动态申请是由系统自动分配,只需设置major = 0即可 2 初始化cdev void cdev_init(struct cdev *, const struct file_operations *); 该函数用于初始化 cdev 的成员,并建立 cdev 和 file_operations 之间的连接。 struct cdev *cdev_alloc(void); 用于动态分配一个cdev结构 int cdev_add(struct cdev *, dev_t, unsigned); 向内核注册一个cdev结构 void cdev_del(struct cdev *); 向内核注销一个cdev结构 3 注册cdev int cdev_add(struct cdev *, dev_t, unsigned); 用于向系统添加一个 cdev,完成字符设备的注册。 (二)设备实现操作 用户空间的程序以访问文件的形式访问字符设备,通常进行open、read、write、close等系统调用。而这些系统调用的最终落实则是file_operations结构体中成员函数,它们是字符设备驱动与内核的接口 这个结构体是字符设备当中最重要的结构体之一,file_operations 结构体中的成员函数指针是字符设备驱动程序设计的主体内容。 static struct file_operations globalmem_fops = { .owner = THIS_MODULE, .open = globalmem_open, .release = globalmem_close, .read = globalmem_read }; 代码中的globalmem_open、globalmem_close、globalmem_read是要在具体的驱动中自己实现的。 (三)驱动注销 1 删除 cdev 在字符设备驱动模块卸载函数中通过cdev_del()函数向系统删除一个cdev,完成字符设备的注销。 void cdev_del(struct cdev *); 2 释放设备号 在调用cdev_del()函数从系统注销字符设备之后,unregister_chrdev_region()应该被调用以释放原先申请的设备号 void unregister_chrdev_region(dev_t from, unsigned count); |