(dev_t)-->主设备号、次设备号 |
MAJOR(dev_t dev) |
主设备号、次设备号-->(dev_t) |
MKDEV(int major,int minor) |
在建立一个字符驱动时你的驱动需要做的第一件事是获取一个或多个设备编号来使用。并且应当在不再使用它们时释放它。
int register_chrdev_region(dev_t first, unsigned int count,char *name); //指定设备编号 |
安排主编号最好的方式, 我们认为, 是缺省使用动态分配, 而留给自己在加载时或者甚至在编译时指定主编号的选项权.
以下是在scull.c中用来获取主设备好的代码:
if (scull_major) { |
动态分配的缺点是你无法提前创建设备节点, 因为分配给你的模块的主编号会变化. 对于驱动的正常使用, 这不是问题, 因为一旦编号分配了, 你可从 /proc/devices 中读取它.
2、一些重要数据结构
大部分的基础性的驱动操作包括 4 个重要的内核数据结构cdev,file_operations, file 和 inode.
file_operations结构定义在 <linux/fs.h>
struct file_operations scull_fops = { |
struct file,定义于 <linux/fs.h>与用户空间程序的 FILE 指针没有任何关系。文件结构代表一个打开的文件.它由内核在 open 时创建, 并传递给在文件上操作的任何函数, 直到最后的关闭. 在文件的所有实例都关闭后, 内核释放这个数据结构.
struct inode ,是在内核内部用来表示文件的。因此, 它和代表打开文件描述符的文件结构是不同的. 可能有代表单个文件的多个打开描述符的许多文件结构, 但是它们都指向一个单个 inode 结构.
struct cdev ,Linux2.6内核与2.4内核不同2.6内核采用了。cdev结构体来描述管理字符设备
struct kobject kobj; //嵌在cdev结构中的kobject对象
struct module *owner;
struct file_operations *ops;/*file_operation 结构体,最终与硬件打交道的函数都注册在这里*/
struct list_head list;
dev_t dev;
unsigned int count;
};
- 为struct cdev 分配空间(如果已经将struct cdev 嵌入到自己的设备的特定结构体中,并分配了空间,这步略过!)
struct cdev *my_cdev = cdev_alloc();
- 初始化struct cdev
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
- 初始化cdev.owner
cdev.owner = THIS_MODULE;
- cdev设置完成,通知内核struct cdev的信息(在执行这步之前必须确定你对struct cdev的以上设置已经完成!)
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
从系统中移除一个字符设备:void cdev_del(struct cdev *p)
以下是scull中的初始化代码(之前已经为struct scull_dev 分配了空间):
/* |
open 方法提供给驱动来做任何的初始化来准备后续的操作. 在大部分驱动中, open 应当进行下面的工作:
●检查设备特定的错误(例如设备没准备好, 或者类似的硬件错误)
●如果它第一次打开, 初始化设备
●如果需要, 更新 f_op 指针.
●分配并填充要放进 filp->private_data 的任何数据结构
但是, 事情的第一步常常是确定打开哪个设备. 记住 open 方法的原型是:
int (*open)(struct inode *inode, struct file *filp); |
inode 参数有我们需要的信息,以它的 i_cdev 成员的形式, 里面包含我们之前建立的 cdev 结构.
container_of(pointer, container_type, container_field); |
这个宏使用一个指向 container_field 类型的成员的指针, 它在一个 container_type 类型的结构中, 并且返回一个指针指向包含结构. 在 scull_open, 这个宏用来找到适当的设备结构:
struct scull_dev *dev; /* device information */ |
识别打开的设备的另外的方法是查看×××存储在 inode 结构的次编号. 如果你使用 register_chrdev 注册你的设备, 你必须使用这个技术. 确认使用 iminor 从 inode 结构中获取次编号, 并且确定它对应一个你的驱动真正准备好处理的设备.
int scull_open(struct inode *inode, struct file *filp) |
release 方法
●释放 open 分配在 filp->private_data 中的任何东西
●在最后的 close 关闭设备
scull 的基本形式没有硬件去关闭, 因此需要的代码是最少的:
int scull_release(struct inode *inode, struct file *filp) |