Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2087801
  • 博文数量: 361
  • 博客积分: 10828
  • 博客等级: 上将
  • 技术积分: 4161
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-20 14:34
文章分类

全部博文(361)

文章存档

2011年(132)

2010年(229)

分类: LINUX

2010-01-30 08:48:06

 块设备是驱动一个重要的部分,需要和字符型加以区分,字符型设备不带有缓冲,操作直接与实际的设备相连,直接进行操作,而对于常见的块设备,通常都带有一个缓冲区,也就是块,数据的操作单元是块,数据的输入输出都是通过这个缓冲区来完成的。通常意义的块设备主要指的是硬盘、SD卡、flash等, 早期的块设备主要指的是硬盘, 后来很多的块设备的驱动都是从早期硬盘驱动衍化而来的。
 
  块设备驱动程序框架:
  驱动程序初始化函数
  int register_blkdev(unsigned int, const char *, struct block_device_operations *);
驱动程序清除函数
   int unregister_blkdev(unsigned int, const char *);
块设备驱动数据结构
   struct block_device_operations   下边是一个典型的块设备驱动:Ø 

#ifndef __KERNEL__
    #define __KERNEL__
#endif
#ifndef MODULE
    #define MODULE
#endif

#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>    /* printk() */
#include <linux/init.h>    /* __init __exit */

#include <linux/types.h>    /* size_t */
#include <linux/fs.h>    /* file_operation */
#include <linux/blk.h>
#include <linux/hdreg.h>    /* geo */
//#include     /* Error number */

//#include     /* udelay */

#include <asm/uaccess.h>    /* copy_to_user, copy_from_user */

#define DRIVER_NAME    "Ram Disk"
#define PRINTK(fmt, arg...)    printk(KERN_NOTICE fmt, ##arg)
static int myBlockDriver_Major = 0;        /* Driver Major Number */

/* Vitual Driver Buffer */
#define NUM_RAMDISKS        1
#define DISK_SIZE            (1UL*1024UL*1024UL)
#define BYTE_PER_SECTOR        (512)
static unsigned char rd_buffer[NUM_RAMDISKS][DISK_SIZE];/* Vitual Buffer */
static unsigned long rd_length[NUM_RAMDISKS];            /* Size of RAM disks in bytes */
static int rd_hardsec[NUM_RAMDISKS];                    /* Size of real blocks in bytes */
static int rd_blocksizes[NUM_RAMDISKS];                    /* Size of 1024 byte blocks :) */
static int rd_kbsize[NUM_RAMDISKS];                        /* Size in blocks of 1024 bytes */
//static devfs_handle_t devfs_handle;

//static struct block_device *rd_bdev[NUM_RAMDISKS];        /* Protected device data */


/* Block Driver Operation Functions */
static int myBlockDriver_open(struct inode *inode, struct file *filp)
{
    int Minor = MINOR(inode->i_rdev);
//这里的从设备号是有意义的,和字符设备要区分,

    if (Minor >= NUM_RAMDISKS)
//这里从设备号可以用来区别具体的盘符。

        return -ENXIO;
    MOD_INC_USE_COUNT;
    PRINTK("myBlockDriver open called!\n");
    return 0;
}

static int myBlockDriver_release(struct inode *inode, struct file *filp)
{
    int Minor = MINOR(inode->i_rdev);
//这里的从设备号是有意义的,和字符设备要区分,

    if (Minor >= NUM_RAMDISKS)
        return -ENXIO;
    MOD_DEC_USE_COUNT;
    PRINTK("myBlockDriver release called!\n");
    return 0;
}

static int myBlockDriver_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
    int error = -EINVAL;
    int Minor;
//对一些命令的响应

//    long size;

    struct hd_geometry geo;

    PRINTK("myBlockDriver ioctl called(%d)!\n", cmd);
    if (!inode || !inode->i_rdev)     
        return error;

    Minor = MINOR(inode->i_rdev);
    switch(cmd)
    {
        case BLKGETSIZE: /* Return device size */
            if (!arg)
                break;
            error = put_user(rd_kbsize[Minor] << 1, (unsigned long *) arg);
            error = 0;
            break;
         case BLKGETSIZE64:
            error = put_user((u64)rd_kbsize[Minor]<<10, (u64*)arg);
            break;
     case HDIO_GETGEO:
         PRINTK("HDIO_GETGEO called!\n");
            geo.cylinders = rd_length[Minor] >> 6;
// 除(4*16)相当于右移6位

            geo.heads = 4;
            geo.sectors = 16;
            geo.start = 0;
            copy_to_user((void *)arg, &geo, sizeof(geo));
            break;
     default:
            return blk_ioctl(inode->i_rdev, cmd, arg);
    }
    return error;
}

static int myBlockDriver_check_media_change(kdev_t dev)
//对于热插拔的设备需要在这里填充代码

{
    return 0;
}

static int myBlockDriver_revalidate(kdev_t dev)
//通常的磁盘的大小、具体数据信息的更新在这里完成

{
    return 0;
}

/* Block Driver Operation structure */
static struct block_device_operations myBlockDriver_fops = {
    owner:        THIS_MODULE,
    open:        myBlockDriver_open,
    release:    myBlockDriver_release,
    ioctl:        myBlockDriver_ioctl,
    check_media_change:myBlockDriver_check_media_change,
    revalidate:    myBlockDriver_revalidate,
};

/* Block Driver Data Transfer Function */
static int myBlockDriver_make_request(request_queue_t *queue, int rw, struct buffer_head *bh)
{
    unsigned long flag;
    int Minor = MINOR(bh->b_rdev);
    long offset = bh->b_rsector * 512;    /* 0 means the start sector of the disk*/
    unsigned char *start_addr = bh->b_data;
    size_t len = bh->b_size;

    switch(rw)
    {
    case READA:
    case READ:
        local_irq_save(flag);
//进入临界区

        memcpy(start_addr, &rd_buffer[Minor][offset], len);
        local_irq_restore(flag);
//推出临界区

     bh->b_end_io(bh, 1);
//调用回调函数,通知内核成功完成操作

     break;
    case WRITE:
        local_irq_save(flag);
        memcpy(&rd_buffer[Minor][offset], start_addr, len);
        local_irq_restore(flag);
     bh->b_end_io(bh, 1);
     break;
    default:
     bh->b_end_io(bh,0);
     break;
    }
    return 0;
}

/* Module Init & Exit function */
#ifdef CONFIG_DEVFS_FS
devfs_handle_t devfs_myDriver_dir;
#endif
static int __init myModule_init(void)
{
    int i;
    /* Module init code */
    PRINTK("myModule_init\n");
    /* Init device structure*/
    for(i = 0; i < NUM_RAMDISKS; i++)
    {
        rd_length[i] = DISK_SIZE;                    /* Size of RAM disks in bytes */
        rd_hardsec[i] = BYTE_PER_SECTOR;            /* Size of real blocks in bytes */
        rd_blocksizes[i] = 1024;                    /* Size of 1024 byte blocks :) */
        rd_kbsize[i] = DISK_SIZE/1024;                /* Size in blocks of 1024 bytes */
    }
    /* Driver register */
    myBlockDriver_Major = register_blkdev(0, DRIVER_NAME, &myBlockDriver_fops);
    if(myBlockDriver_Major < 0)
    {
        PRINTK("register char device fail!\n");
        return myBlockDriver_Major;
    }
    PRINTK("register myDriver OK! Major = %d\n", myBlockDriver_Major);
    
//一下的部分是字符型设备与块设备最主要区别的地方,真正的数据传输在这里发生。

    
//调用blk_queue_make_request来替换内核的make_request

    blk_queue_make_request(BLK_DEFAULT_QUEUE(myBlockDriver_Major), myBlockDriver_make_request);
    PRINTK("register make request function OK\n");
#ifdef CONFIG_DEVFS_FS
    devfs_myDriver_dir = devfs_mk_dir(NULL, "blkdrv", NULL);
    devfs_register_series(devfs_myDriver_dir, "%u", NUM_RAMDISKS,
             DEVFS_FL_DEFAULT, myBlockDriver_Major, 0,
             S_IFBLK | S_IRUSR | S_IWUSR,
             &myBlockDriver_fops, NULL);
    PRINTK("add dev file OK!\n");
#endif
    return 0;
}

static void __exit myModule_exit(void)
{
    /* Module exit code */
    PRINTK("myModule_exit\n");
    /* Driver unregister */
    if(myBlockDriver_Major > 0)
    {
#ifdef CONFIG_DEVFS_FS
        devfs_unregister(devfs_myDriver_dir);
#endif
        unregister_blkdev(myBlockDriver_Major, DRIVER_NAME);
    }
    return;
}

MODULE_AUTHOR("xxxx");
MODULE_LICENSE("Dual BSD/GPL");
module_init(myModule_init);
module_exit(myModule_exit);

  上边的这个驱动完成的功能是在kernel中模拟一块硬盘出来,并没有实际的意义。块设备通过check_media_change、revalidate来支持可移动存储设备。
check_media_change  用于检查自从上次检查以来设备是否发生变化
revalidate    在检查之后重新初始化驱动状态。

   在块设备中一个不同于字符型设备驱动的地方是需要自己写一个xx_make_request()函数,这需要追溯到块设备的鼻祖,早期硬盘的驱动,最初块设备使用于基于机械寻址的硬盘
磁头在不同轨道移动是最耗时的操作,移动距离越长,需要的时间越长,是整个系统的性能瓶颈,Linux优化代码以便减少这个时间消耗,将对设备的读写请求重新排列,尽量按照自然顺序对磁盘进行读写,使用请求队列来缓存和排序上层的访问请求,块I/O请求使用make_request()完成,由于linux默认的make_request会有队列操作,而以后的块设备,比如SD卡是没有机械动作的,那么队列操作是不必须的,就需要去掉这些,那么必须自己构造一个make_request

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