块设备驱动的第一个任务就是将他们自己注册到内核中,其函数原型如下:
☆int register_blkdev(unsigned int major, const char* name);
major参数是块设备要使用的主设备号,name为设备名,它会在/proc/devices中被现实.如果major为0,内核会自动分配一个新的主设备号,并由该函数返回.如果返回值为负值,则说明设备号分派失败.
注销函数是unregister_blkdev(),原型如下:
☆int unreister_blkdev(unsigned int major, const char* name);
这里unreister_blkdev与register_blkdev的参数必须匹配,否则这个函数会返回-EINVAL. 在Linux2.6中,对register_blkdev的调用是可选的.register_blkdev这个调用在Linux2.6中只完成了两件事情:
①如果需要,分派一个主设备号;
②在/proc/devices中创建一个入口.
8、块设备驱动程序编写模板
块驱动中相关相关模块模板
1.块设备驱动的模块加载与卸载
1)块设备驱动的模块加载完成的工作如下:
☆ 分配,初始化请求队列,绑定请求队列和请求函数
☆ 分配,初始化gendisk,给gendisk的major,fops,queue等成员赋值,最后添加gendisk.
☆ 注册块设备驱动.
代码1:使用blk_alloc_queue函数完成块设备驱动的模块加载模板
static int __init xxx_init(void){
//分配gendisk
xxx_disks = alloc_disk(1);
if(!xxx_disks){
goto out;
}
//块设备驱动注册
if(register_blkdev(xxx_MAJOR, "xxx"){
err = -EIO;
goto out;
}
//"请求队列"分配
xxx_queue = blk_alloc_queue(GFP_KERNEL);
if(!xxx_queue){
goto out_queue;
}
blk_queue_make_request(xxx_queue, &xxx_make_request);//绑定"制造请求"函数
blk_queue_hardsect_size(xxx_queue,xxx_blocksize);//告知内核硬件扇区尺寸
//gendisk初始化
xxx_disks->major = xxx_MAJOR;
xxx_disks->first_minor = 0;
xxx_disks->fops = &xxx_fop;
xxx_disks->queue = xxx_queue;
sprintf(xxx_disks->disk_name, "xxx%d", i);
set_capacity(xxx_disks, xxx_size);//设置gendisk容量为xxx_size个扇区大小
add_disk(xxx_disks);
return 0;
out_queue:unregister_blkdev(xxx_MAJOR, "xxx");
out:put_disk(xxx_disks);
blk_cleanup_queue(xxx_queue);
return -ENOMEM;
}
代码2:使用blk_init_queue函数完成块设备驱动的模块加载模板
static int __init xxx_init(void){
//块设备驱动注册
if(register_blkdev(xxx_MAJOR, "xxx"){
err = -EIO;
goto out;
}
//请求队列初始化
xxx_queue = blk_init_queue(xxx_request, xxx_lock);
if(!xxx_queue){
goto out_queue;
}
blk_queue_hardsect_size(xxx_queue, xxx_blocksize);//告知内核硬件扇区大小
//gendisk初始化
xxx_disks->major = xxx_MAJOR;
xxx_disks->first_minor = 0;
xxx_disks->fops = &xxx_fop;
xxx_disks->queue = xxx_queue;
sprintf(xxx_disks->disk_name, "xxx%d", i);
set_capacity(xxx_disks, xxx_size*2);//设置gendisk容量为xxx_size个扇区大小
add_disk(xxx_disks);
return 0;
out_queue:unregister_blkdev(xxx_MAJOR, "xxx");
out:put_disk(xxx_disks);
blk_cleanup_queue(xxx_queue);
return -ENOMEM;
}
2)块设备驱动的模块卸载完成的工作如下:
☆ 清除请求队列.
☆ 删除gendisk和gendisk的引用
☆ 删除对块设备的引用,注销块设备驱动.
代码3:块设备驱动模块卸载函数模板
static void __exit xxx_exit(void){
if(bdev){
invalidate_bdev(xxx_bdev, 1);
blkdev_put(xxx_bdev);
}
del_gendisk(xxx_disks);//删除gendisk
put_disk(xxx_disks);
blk_cleanup_queue(xxx_queue[i]);//清除请求队列
unregister_blkdev(xxx_MAJOR, "xxx");
}
2.块设备驱动的打开与释放
块设备驱动的open()和release()函数不是必须的,一个简单的块设备驱动可以不提供open()和release()函数.