Linux驱动程序开发之字符设备驱动——基础篇(二)
2012-09-14 23:07
826 查看
Linux驱动程序开发之字符设备驱动——基础篇(二)
转自:http://www.cnblogs.com/LakeFollow/archive/2012/07/30/2614475.htmlLinux下的大部分驱动程序都是字符设备驱动程序,通过下面的学习我们将 会了解到字符设备是如何注册到系统中的,应用程序是如何访问驱动程序的数据的,及字符驱动程序是如何工作的。
设备号
通过前面的 学习我们知道应用程序是通过设备节点来访问驱动程序及设备的,其根本是通过设备节点的设备号(主设备号及从设备号)来关联驱动程序及设备的,字符设备也不 例外(其实字符设备只能这样访问)。这里我们详细讨论Linux内部如何管 理设备号的。
设备号类型
Linux内核里用“dev_t”来表示设备 号,它是一个32位的无符号 数,其高12位用来表示主
设备号,低20位用来表示从 设备号。它被定义在<linux/types.h>头文件里。 内核里提供了操作“dev_t”的函数,驱动 程序中通过这些函数(其实是宏,定义在<linux/kdev_t.h>文件中)来
操作设备号。
#define MINORBITS 20 #define MINORMASK ((1U << MINORBITS) - 1) #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi)) |
另一点需要 说明的是,dev_t数据类型支持2^12个主设备号,每个主设备号(通常是一个设备驱动)可以支持2^20个设备,目前来说这已经足够大了,但谁又能说将来还能满足要求 呢?一个良好的编程习惯是不要依赖dev_t这个数据类
型,切记必须使用内核提供的操作设备号的函数。
字符设备号注册
内核提供了字符设备号管理的函数接口,作为一个良好的编程习惯,字符设备驱动程 序应该通过这些函数向系统注册或注销字符设备号。
int register_chrdev_region(dev_t from, unsigned count, const char *name) int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name) void unregister_chrdev_region(dev_t from, unsigned count) |
这些静态分配的设备号。
alloc_chrdev_region用于动态分 配的设备号并注册到内核中,分配的设备号通过dev参数返回。 作为一个良好的内核开发习惯,我们推荐你使用动态分配的方式来生成设备号。
unregister_chrdev_region用于注销一 个不用的设备号区域,通常这个函数在驱动程序卸载时被调用。
字符设备
Linux2.6内核使用“struct cdev”来记录字符设 备的信息,内核也提供了相关的函数来操作“struct cdev”对象,他们 定义在<linux/cdev.h>头文件中。
可见字符设备及其操作函数接口定义的很简单。
struct cdev { struct kobject kobj; struct module *owner; const struct file_operations *ops; struct list_head list; dev_t dev; unsigned int count; }; void cdev_init(struct cdev *, const struct file_operations *); struct cdev *cdev_alloc(void); void cdev_put(struct cdev *p); int cdev_add(struct cdev *, dev_t, unsigned); void cdev_del(struct cdev *); |
kobj:struct kobject对象数据,用 来描述设备的引用计数,是Linux设备模型的 基础结构。我们在后面的“Linux设备模型”在做详细的介绍。
owner:struct module对象数据,描 述了模块的属主,指向拥有这个结构的模块的指针,显然它只有对编译为模块方式的驱动才由意义。一般赋值位“THIS_MODULE”。
ops:struct file_operations对象数据,描 述了字符设备的操作函数指针。对于设备驱动来说,这是一个很重要的数据成员,几乎所有的驱动都要用到这个对象,我们会在下面做详细介绍。
dev:dev_t对象数据,描述了字符设备的设备号。
内核提供了操作字符设备对象“struct cdev”的函数,我们 只能通过这些函数来操作字符设备,例如:初始化、注册、添加、移除字符设备。
cdev_alloc:用于动态 分配一个新的字符设备 cdev 对象,并对其 进行初始化。采用cdev_alloc分配的cdev对象需要显示的初始化owner和ops对象。
// 参考drivers/scsi/st.c:st_probe 函数 struct cdev *cdev = NULL; cdev = cdev_alloc(); // Error Processing cdev->owner = THIS_MODULE; cdev->ops = &st_fops; |
cdev_init:用于初始 化一个静态分配的cdev对象,一般这 个对象会嵌入到其他的对象中。cdev_init会自动初始 化ops数据,因此应
用程序只需要显示的给owner对象赋值。cdev_init的功能与cdev_alloc基本相同,唯 一的区别是cdev_init初始化一个
已经存在的cdev对象,并且这 个初始化会影响到字符设备删除函数(cdev_del)的行为, 请参考cdev_del函数。
cdev_add:向内核系 统中添加一个新的字符设备cdev,并且使它立 即可用。
cdev_del:从内核系 统中移除cdev字符设备。如 果字符设备是由cdev_alloc动态分配的, 则会释放分配的内存。
cdev_put:减少模块 的引用计数,一般很少会有驱动程序直接调用这个函数。
文件操作对象
Linux中的所有设备都是文件,内核中用“struct file”结构来表示一 个文件。尽管我们的驱动不会直接使用这个结构中的大部分对象,其中的一些数据成员还是很重要的,我们有必要在这里做一些介绍,具体的内容请参考内核源代码 树<linux/fs.h>头文件。
// struct file 中的一些重要数据成员 const struct file_operations *f_op; unsigned int f_flags; mode_t f_mode; loff_t f_pos; struct address_space *f_mapping; |
file_operations结构。Linux里的struct file_operations结构描述了一 个文件操作需要的所有函数,它定义在<linux/fs.h>头文件中。
struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff c6b8 _t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *, int datasync); int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); int (*dir_notify)(struct file *filp, unsigned long arg); int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); int (*setlease)(struct file *, long, struct file_lock **); }; |
这里需要指 出的是,open和release函数的第一个参数是一个struct inode对象。这是一个内核文件系统索引节点对象,它包含 了内核在操作文件或目录是需要的全部信息。对于字符设备驱动来说,我们关心的是从struct
inode对象中获取设备号(inode的i_rdev成员)内核提供了两个函数来做这件事。
static inline unsigned iminor(const struct inode *inode) { return MINOR(inode->i_rdev); } static inline unsigned imajor(const struct inode *inode) { return MAJOR(inode->i_rdev); } |
字符设备驱 动可以参考Linux 设备驱动程序 第三版和linux设备驱动开发 详解,其中linux设备驱动程序 第三版中讲的:
主次编号
一些重要数据结构
字符设备注册
Open和release
读和写
一些头文件和结构体;
都非常经典, 都理解字符驱动设备很重要,很值得参考!
相关文章推荐
- Linux驱动程序开发 - 字符设备驱动
- Linux驱动程序开发(4) - 字符设备驱动(3)-LED设备驱动和应用程序
- Linux字符设备驱动程序开发(1)-使用字符设备驱动
- Linux驱动程序开发 004- 字符设备驱动
- 嵌入式Linux设备驱动开发之:按键驱动程序实例
- linux4.10.8 内核移植(四)---字符设备驱动_led驱动程序
- Linux下的硬件驱动——USB设备(下)&& Linux下PCI设备驱动程序开发
- Linux驱动程序开发007 - 设备驱动模型初探
- Linux设备驱动程序学习(基于2440的GPIO字符设备驱动)
- Linux字符设备驱动程序开发
- Linux 字符设备驱动开发基础(一)—— 编写简单 LED 设备驱动
- linux驱动开发--字符设备:原子操作
- Linux驱动程序开发 - 设备驱动模型初探
- linux驱动开发--字符设备:设备轮询操作select/poll
- linux设备驱动程序之简单字符设备驱动
- linux驱动开发--字符设备:简单的file_operations示例
- linux中秒字符设备驱动(宋宝华设备驱动开发详解第10章)
- 字符设备驱动 - linux驱动开发
- 基于mini6410的linux驱动学习总结(四 设计字符设备驱动程序)
- 07-S3C2440驱动学习(一)嵌入式linux字符设备驱动-查询+中断+引入poll机制的按键驱动程序