现在的位置: 首页 > 综合 > 正文

块设备驱动程序

2017年08月25日 ⁄ 综合 ⁄ 共 4778字 ⁄ 字号 评论关闭

设备描述---

定义于linux/genhd.h

struct gendisk

{

int major;主设备号

int first_minor;次设备号

int minors;

char disk_name[DISK_NAME_LEN];驱动名

struct block_device_operations *fops;

struct request_queue *queue;请求队列

....................................................

int node_id;

};

设备注册---

void add_disk(struct gendisk *gd)

设备操作---

字符设备通过file_operations结构来定义它所支持的操作。块设备通过struct block_device_operations结构来定义它所支持的操作。

无读写------使用I/O请求来实现。

struct block_device_operations

{

int (*open) (struct block_device *,fmode_t);

int (*release) (struct gendisk *,fmode_t);

int (*ioctl) (struct block_device *,fmode_t,unsigned,unsigned long);

      ...............................................

};

I/O请求---

struct request表示等待处理的块设备I/O请求

struct request

{

struct list_head queuelist;链表结构

sector_t sector;要操作的首个扇区

unsigned long nr_sectors;要操作的扇区数目

struct bio *bio;请求的bio结构体的链表

struct bio *biotail;请求的bio结构体的链表

...............................

};

请求队列----

简单的讲,请求队列就是IO请求request所形成的队列,在linux内核中struct request_queue描述。

内核提供了一系列函数用来操作请求队列:

struct request_queue *blk_init_queue(request_fn_proc *rfn,spinlock_t *lock)

rfn 函数指针------当请求队列上有request过来之后,通过自己定义的这个函数指针指向的函数,实现真正的数据访问。

lock 自旋锁

初始化请求队列,一般在块设备驱动的模块加载函数中调用。


void blk_cleanup_queue(request_queue_t *q)

清除请求队列,这个函数完成将请求队列返回给系统的任务,一般在块设备驱动模块卸载函数中调用。


struct request *elv_next_request(request_queue_t *queue)

返回一个要处理的请求(由IO调度器决定),如果没有请求则返回NULL。elv_next_request()不会清除请求,它仍然将这个请求保留在队列上,因此连续调用它2次,2次会返回同一个请求结构体。


void blkdev_dequeue_request(struct request *req)

从队列中删除一个请求。


#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/kernel.h>    /* printk() */
#include <linux/slab.h>        /* kmalloc() */
#include <linux/fs.h>        /* everything... */
#include <linux/errno.h>    /* error codes */
#include <linux/timer.h>
#include <linux/types.h>    /* size_t */
#include <linux/fcntl.h>    /* O_ACCMODE */
#include <linux/hdreg.h>    /* HDIO_GETGEO */
#include <linux/kdev_t.h>
#include <linux/vmalloc.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/buffer_head.h>    /* invalidate_bdev */
#include <linux/bio.h>

#define SIMP_BLKDEV_DEVICEMAJOR        COMPAQ_SMART2_MAJOR
#define SIMP_BLKDEV_DISKNAME        "simp_blkdev"
#define SIMP_BLKDEV_BYTES        (16*1024*1024)

static struct request_queue *simp_blkdev_queue;
static struct gendisk *simp_blkdev_disk;
unsigned char simp_blkdev_data[SIMP_BLKDEV_BYTES];
名字能变,参数类型,返回值不能变。
static void simp_blkdev_do_request(struct request_queue *q)
{
        struct request *req;
        while ((req = elv_next_request(q)) != NULL) {
                if ((req->sector + req->current_nr_sectors) << 9
                        > SIMP_BLKDEV_BYTES)

(要操作的首个扇区+要操作的扇区数目)*512>16*1024*1024

               {
                        printk(KERN_ERR SIMP_BLKDEV_DISKNAME
                                ": bad request: block=%llu, count=%u\n",
                                (unsigned long long)req->sector,
                                req->current_nr_sectors);
                        end_request(req, 0);
                        continue;
                }

                switch (rq_data_dir(req))

             {//rq_data_dir(req)判断请求类型


                case READ:

这里使用memcpy不好,因为是从内核空间向用户空间传数据,应该采用映射。由于这里是内存模拟,故可以这样使用。

                        memcpy(req->buffer,
                                simp_blkdev_data + (req->sector << 9),
                                req->current_nr_sectors << 9);
                        end_request(req, 1);//结束这个请求
                        break;
                case WRITE:
                        memcpy(simp_blkdev_data + (req->sector << 9),
                                req->buffer, req->current_nr_sectors << 9);
                        end_request(req, 1);
                        break;
                default:
                        /* No default because rq_data_dir(req) is 1 bit */
                        break;
                }
        }
}

struct block_device_operations simp_blkdev_fops = {
        .owner                = THIS_MODULE,
};

static int __init simp_blkdev_init(void)
{
        int ret;

        simp_blkdev_queue = blk_init_queue(simp_blkdev_do_request, NULL);

                  初始化请求等待队列

        if (!simp_blkdev_queue) {
                ret = -ENOMEM;
                goto err_init_queue;
        }

        simp_blkdev_disk = alloc_disk(1);
    分配gendisk结构

    if (!simp_blkdev_disk) {
                ret = -ENOMEM;
                goto err_alloc_disk;
        }

        strcpy(simp_blkdev_disk->disk_name, SIMP_BLKDEV_DISKNAME);
        simp_blkdev_disk->major = SIMP_BLKDEV_DEVICEMAJOR;
        simp_blkdev_disk->first_minor = 0;
        simp_blkdev_disk->fops = &simp_blkdev_fops;
        simp_blkdev_disk->queue = simp_blkdev_queue;
        set_capacity(simp_blkdev_disk, SIMP_BLKDEV_BYTES>>9);
        add_disk(simp_blkdev_disk);

        return 0;

err_alloc_disk:
        blk_cleanup_queue(simp_blkdev_queue);
err_init_queue:
        return ret;
}

static void __exit simp_blkdev_exit(void)
{
        del_gendisk(simp_blkdev_disk);
        put_disk(simp_blkdev_disk);
        blk_cleanup_queue(simp_blkdev_queue);
}

module_init(simp_blkdev_init);
module_exit(simp_blkdev_exit);

抱歉!评论已关闭.