目录
6、申请设备号:register_chrdev_region()或alloc_chrdev_region()
1、使用register_chrdev注册驱动程序
//内核中register_chrdev实现
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
{
return __register_chrdev(major, 0, 256, name, fops);
}
解析一下各个参数
major:主设备号,如果写0可以动态申请
name:申请设备的设备名
fops:file_operations结构体
返回值:返回申请到的设备号
//实际使用示范
#define NAME "mycdev"
major = register_chrdev(0, NAME, &fops)
2、解析__register_chrdev
int __register_chrdev(unsigned int major, unsigned int baseminor,
unsigned int count, const char *name,
const struct file_operations *fops)
各个参数
major:主设备号
baseminor:次设备号的起始值
count:申请的个数
name:设备的名字
fops:file_operations结构体
返回值:char_device_struct结构体
3、为什么不用register_chrdevr
根据register_chrdev我们可以看到,如果用的是registe_chrdev的形式,会一次性申请256个次设备号,这对于项目开发非常的不友好,所以在实际项目开发中,一般不会用到register_chrdev,根据__register_chrdev,我们可以自己写出注册的方法。
所以,接下来我们来根据__register_chrdev写注册的方法
4、分配对象:cdev_alloc()
struct cdev *cdev;
cdev = cdev_alloc();
//下面为cdev_alloc的实现
struct cdev *cdev_alloc(void)
{
//使用结构体前为结构体分配一块内存空间
struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
if (p) {
INIT_LIST_HEAD(&p->list);
//kobj是啥,我也不懂,先放在这里,后面再查
//2023.8.10注:已经知道是什么了,具体看后面的文章
kobject_init(&p->kobj, &ktype_cdev_dynamic);
}
return p;
}
//下面为INIT_LIST_HEAD的实现
static inline void INIT_LIST_HEAD(struct list_head *list)
{
//实际上就是让链表全部指向自己,意思就是为了创建这个链表
list->next = list;
list->prev = list;
}
5、初始化对象:cdev_init()
const struct file_operations fops = {
.open = mycdev_open,
.read = mycdev_read,
.write = mycdev_write,
.release = mycdev_release,
};
cdev_init(cdev, &fops);
//下面为cdev_init函数
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
//初始化内存空间,清空结构体
memset(cdev, 0, sizeof *cdev);
//在前面创建的时候已经初始化过了,不知道为啥这里还要再来一遍
INIT_LIST_HEAD(&cdev->list);
//这里也是同理,不知道为啥还要再来一遍
//那么应该可以自己把两个结合一下,把上面先创建cdev拿下来,然后再调用这个函数
//具体看后面的文章
kobject_init(&cdev->kobj, &ktype_cdev_default);
//初始化file_operations结构体,就是填入进去
cdev->ops = fops;
}
6、申请设备号:register_chrdev_region()或alloc_chrdev_region()
在此先说明两者的区别,一种是静态的申请(直接写入major的值),一种是动态的申请(major是0,自动分配主设备号),两者看似有区别,但是进入到内部一看,都是调用了__register_chrdev_region,所以就直接看__register_chrdev_region吧
//先解释一下参数
//major:主设备号,写0动态申请
//baseminor:次设备号的起始值
//minorct:个数
//name:设备的名字
__register_chrdev_region(unsigned int major, unsigned int baseminor,
int minorct, const char *name)
//申请一块内存
struct char_device_struct *cd;
cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
//然后就是一大段的初始化,例如
cd->major = major;
cd->baseminor = baseminor;
cd->minorct = minorct;
strlcpy(cd->name, name, sizeof(cd->name));
//这个地方有点意思,放在下个代码块中解释一下
i = major_to_index(major);
//这是上面代码中的一部分
i = major_to_index(major);
//定义这个大小为255,意味着char_device_struct最长为256
//如果要放下比256更大的设备号说明要进行哈希查找,这就是名字中带HASH的原因
#define BLKDEV_MAJOR_HASH_SIZE 255
//这个是一个单向链表
static struct char_device_struct {
struct char_device_struct *next;
unsigned int major;
unsigned int baseminor;
int minorct;
char name[64];
struct cdev *cdev; /* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
//此处就是为哈希查找的原理
static inline int major_to_index(unsigned major)
{
return major % BLKDEV_MAJOR_HASH_SIZE;
}
7、字符设备注册 cdev_add()
ret = cdev_add(cdev, MKDEV(major, minor), count);
//下面为cdev_add函数,函数功能就是注册字符设备驱动
//下面有关kobj的东西我都没有太留意,后面再说吧
//参数说明
//p:cdev的结构体指针
//dev:申请到的设备号
//count:设备的个数
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
int error;
p->dev = dev;
p->count = count;
error = kobj_map(cdev_map, dev, count, NULL,
exact_match, exact_lock, p);
if (error)
return error;
kobject_get(p->kobj.parent);
return 0;
}
8、动态创建设备的逻辑类:class_create()
struct class *cls;
#define NAME "mycdev"
cls = class_create(THIS_MODULE, NAME);
if (IS_ERR(cls)) {
printk("class create error\n");
return PTR_ERR(cls);
}
9、创建设备:device_create()
struct device *dev;
for(i=0; i<count; i++) {
dev = device_create(cls,NULL,MKDEV(major, i),NULL,"mycdev%d",i);
if(IS_ERR(cls)) {
printk("device create error\n");
goto ERR5;
}
}
10、移除驱动
//设备销毁
int i = 0;
for(i=0;i<count;i++){
device_destroy(cls,MKDEV(major, i));
}
//销毁目录
class_destroy(cls);
//注销字符设备驱动
cdev_del(cdev);
//释放设备号
unregister_chrdev_region(MKDEV(major, minor), count);
//释放cdev_alloc申请的内存
kfree(cdev);