Chinaunix首页 | 论坛 | 博客
  • 博客访问: 192170
  • 博文数量: 71
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 210
  • 用 户 组: 普通用户
  • 注册时间: 2013-01-13 14:49
文章分类
文章存档

2017年(1)

2015年(5)

2014年(10)

2013年(55)

我的朋友

分类: 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期

阅读(2713) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~