【嵌入式Linux驱动开发】三、字符设备驱动(一)

2019-07-13 00:33发布

1. 基本步骤 (1)确定主设备号和次设备号 (2)实现字符驱动程序
  • 实现file_operations结构体;
  • 实现初始化函数,注册字符设备;
  • 实现销毁函数,释放字符设备;
  • 实现字符设备其他基本成员函数。
(3)创建设备文件节点 2. 什么是主设备号/次设备号 主设备号是内核识别一个设备的标识。它是一个整数(占12位),通常使用1~255。 次设备号由内核使用,用于正确确定设备文件所指的设备。它也是一个整数(占20位),通常使用0~255。 注:同一类设备的主设备号相同,不同的是次设备号。如多个串口的主设备号是相同的,次设备号不同。 3. 设备编号的内部表达 (1)dev_t类型(32位):用来保存设备编号 (2)从dev_t获得主、次设备号:
  • MAJOR(dev_t):    //主
  • MINOR(dev_t);    //次
(3)将主、次设备号转换成dev_t类型:
  • MKDEV(int major, int minor);
4. 分配设备号 通常在模块加载函数中调用。
(1)手工分配:找一个内核没有使用的主设备号来使用 #include int register_chrdev_region(dev_t first, unsigned int count, char *name); //first是设备号,次设备号通常为0;count是要分配设备号的个数,即次设备号个数;name是此类设备的名字。
(2)动态分配: #include int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name); //dev是输出的设备号;firstminor是要申请第一个次设备号;count是要申请的设备号个数;name是此类设备的名字。
5. 释放设备号
通常在模块清理函数中调用。 #include void unregister_chrdev_region(dev_t dev, unsigned int count);
6.  重要结构体 (1)cdev结构体 struct cdev { struct kobject kobj; //内嵌的kobject对象 struct module *owner; //所属模块 struct file_operations *ops; //文件操作结构体 struct list_head list; dev_t dev; //设备号 unsigned int count; };操作cdev结构体的函数: void cdev_init(struct cdev *cdev, struct file_operations *ops); //用于初始化已分配的cdev结构,并建立cdev和file_operations之间的连接 struct cdev *cdev_alloc(void); //用于动态申请一个cdev内存 int cdev_add(struct cdev *cdev, dev_t num, unsigned int count); //向内核添加一个cdev,完成字符设备的注册,通常在模块加载函数中调用 void cdev_del(struct cdev *cdev); //删除一个cdev,完成字符设备的注销,通常在模块卸载函数中调用
(2)file_operations结构体
  • 是字符驱动和内核的接口,在include/linux/fs.h中定义;
  • 字符驱动都要实现一个file_operations结构体;
file_operations的主要成员: struct module *owner:指向模块自身(THIS_MODULE)
open:打开设备 release:关闭设备 read:从设备上读数据 write:向设备上写数据 ioctl:I/O控制函数 llseek:定位当前读写位置指针 mmap:映射设备空间到进程的地址空间 (3)file结构体
  • file_operations结构相关的一个结构体,描述一个正在打开的设备文件。
file的成员: loff_t  f_pos:当前读写位置 unsigned  int  f_flags:标识文件打开时是否可读或可写,O_RDONLY、O_NONBLOCK、O_SYNC struct  file_operations *f_op:文件相关的操作,指向所实现的struct  file_operations void  *private_data:私有数据指针,驱动程序可以将这个字段用于任何目的或者忽略(设为NULL)这个字段 (4)inode结构体
  • 内核用inode结构在内部表示文件;
  • inode与file的区别:file表示打开的文件描述符,多个表示打开的文件描述符的file结构可以指向一个inode结构。
inode的重要成员: dev_t  i_rdev:对表示设备文件的inode结构,该字段包含了真正的设备号 struct  cdev  *i_cdev:表示字符设备在内核中的内部结构 7. 字符设备驱动程序模版 //字符设备驱动模块加载函数 static int __init xxx_init(void) { ... cdev_init(&xxx_dev.cdev, &xxx_fops); //初始化cdev xxx_dev.cdev.owner = THIS_MODULE; //获取字符设备编号 if(xxx_major) { register_chrdev_region(xxx_dev_no, 1, DEV_NAME); } else { alloc_chrdev_region(&xxx_dev_no, 0, 1, DEV_NAME); } ret = cdev_add(&xxx_dev.cdev, xxx_dev_no, 1); //注册设备 ... }
//字符设备驱动模块卸载函数 static void __exit xxx_exit(void) { unregister_chrdev_region(xxx_dev_no, 1); //释放占用的设备号 cdev_del(&xxx_dev.cdev); //注销设备 ... }