分类: LINUX
2013-10-15 17:09:52
以下是一个最简单的块设备驱动,写完可以对编写块设备驱动的框架有初步了解。
环境:
Linux 2.6.32+mini2440+arm-linux-gcc 4.3.2
源码:
ramblock_drv.c
/* 参考:
* drivers\block\xd.c
* drivers\block\z2ram.c
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static struct gendisk *ramblock_disk;
static struct request_queue * ramblock_queue;
static int major;
static DEFINE_SPINLOCK(ramblock_lock);
#define RAMBLOCK_SIZE (1024*1024)
static unsigned char *ramblock_buf;
static int ramblock_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
/* 容量=heads*cylinders*sectors*512 */
geo->heads = 2;
geo->cylinders = 32;
geo->sectors = RAMBLOCK_SIZE/2/32/512;
return 0;
}
static struct block_device_operations ramblock_fops = {
.owner = THIS_MODULE,
.getgeo = ramblock_getgeo,
};
static void do_ramblock_request(struct request_queue * q)
{
int err = 0;
struct request *req;
req = blk_fetch_request(q);
while (req) {
/* 数据传输三要素: 源,目的,长度 */
/* 源/目的: */
unsigned long offset = blk_rq_pos(req) * 512;
/* 目的/源: */
// req->buffer
/* 长度: */
unsigned long len = blk_rq_cur_sectors(req) * 512;
if (rq_data_dir(req) == READ)
memcpy(req->buffer, ramblock_buf+offset, len);
else
memcpy(ramblock_buf+offset, req->buffer, len);
if (!__blk_end_request_cur(req, err))
req = blk_fetch_request(q);
}
}
static int ramblock_init(void)
{
/* 1. 分配一个gendisk结构体 */
ramblock_disk = alloc_disk(16); /* 次设备号个数: 分区个数+1 */
/* 2. 设置 */
/* 2.1 分配/设置队列: 提供读写能力 */
ramblock_queue = blk_init_queue(do_ramblock_request, &ramblock_lock);
ramblock_disk->queue = ramblock_queue;
/* 2.2 设置其他属性: 比如容量 */
major = register_blkdev(0, "ramblock"); /* cat /proc/devices */
ramblock_disk->major = major;
ramblock_disk->first_minor = 0;
sprintf(ramblock_disk->disk_name, "ramblock");
ramblock_disk->fops = &ramblock_fops;
set_capacity(ramblock_disk, RAMBLOCK_SIZE / 512);
/* 3. 硬件相关操作 */
ramblock_buf = kzalloc(RAMBLOCK_SIZE, GFP_KERNEL);
/* 4. 注册 */
add_disk(ramblock_disk);
return 0;
}
static void ramblock_exit(void)
{
unregister_blkdev(major, "ramblock");
del_gendisk(ramblock_disk);
put_disk(ramblock_disk);
blk_cleanup_queue(ramblock_queue);
kfree(ramblock_buf);
}
module_init(ramblock_init);
module_exit(ramblock_exit);
MODULE_LICENSE("GPL");
使用块设备:
1. insmod ramblock.ko
2. ls /dev/ramblock*
3. fdisk /dev/ramblock
剩下的看提示应该能搞定
收获:
1. 要让一个最简单的块设备驱动可用,必须实现的关键结构为gendisk和request_queue。gendisk结构描述一个磁盘,包括主从设备号、设备操作函数、容量等信息,它通过gendisk->queue和request_queue联系起来,request_queue初始化时又向内核块设备层注册了处理request的函数。
2. 该版本适用于2.6.32内核,参照了韦东山的2.6.24内核上写的,一些api发生变化(见linux/include/blkdev.h)。在2.6.32内核中,
request -> sectors 变为 blk_rq_pos(request)
request -> nr_sectors 变为 blk_rq_nr_sectors(request)
elev_next_request(request) 变为 blk_fetch_request(request)
end_request(request, error) 变为 if (!__blk_end_request_cur(req, err)) req = blk_fetch_request(q);
我在2.6.32内核下对照上述源码修改api时遇到一些问题。假如使用blk_end_request_all(request, error),insmod直接死机;假如使用__blk_end_request_all(request, error),可以insmod,但是不能够mkfs和mount,参考drivers\block\z2ram.c把 if (!__blk_end_request_cur(req, err)) req = blk_fetch_request(q);替换了end_request(request, error)即可。err初始化的时候是0,
参考:
韦东山2期