1、主设备号和此设备号
主编号标识设备相连的驱动,次编号被内核用来决定引用哪个设备.
在内核中, dev_t 类型(在 <linux/types.h>中定义)用来持有设备编号.对于 2.6.0 内核, dev_t 是 32 位的量, 12 位用作主编号, 20 位用作次编号.
应当利用在 <linux/kdev_t.h>中的一套宏定义. 为获得一个 dev_t 的主或者次编号, 使用:
(dev_t)-->主设备号、次设备号
|
MAJOR(dev_t dev)
MINOR(dev_t dev)
|
主设备号、次设备号-->(dev_t)
|
MKDEV(int major,int minor)
|
在建立一个字符驱动时你的驱动需要做的第一件事是获取一个或多个设备编号来使用.并且应当在不再使用它们时释放它.
int register_chrdev_region(dev_t first, unsigned int count,char *name); //指定设备编号
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,
unsigned int count, char *name); //动态生成设备编号
void unregister_chrdev_region(dev_t first, unsigned int count); //释放设备编号
|
安排主编号最好的方式, 我们认为, 是缺省使用动态分配, 而留给自己在加载时或者甚至在编译时指定主编号的选项权.
以下是在scull.c中用来获取主设备好的代码:
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 %dn", scull_major);
return result;
}
|
动态分配的缺点是你无法提前创建设备节点, 分配给你的模块的主编号会变化. 对于驱动的正常使用, 这不是问题, 一旦编号分配了, 你可从 /proc/devices 中读取它.
2、一些重要数据结构
大部分的基础性的驱动操作包括 4 个重要的内核数据结构cdev,file_operations, file 和 inode.
file_operations结构定义在 <linux/fs.h>
struct file_operations scull_fops = {
.owner = THIS_MODULE,
.llseek = scull_llseek,
.read = scull_read,
.write = scull_write,
.ioctl = scull_ioctl,
.open = scull_open,
.release = scull_release,
};
|
struct file,定义于 <linux/fs.h>与用户空间程序的 FILE 指针没有任何关系.文件结构代表一个打开的文件.它由内核在 open 时创建, 并传递给在文件上操作的任何函数, 直到 的关闭. 在文件的所有实例都关闭后, 内核释放这个数据结构 |