linux下块设备驱动程序

块设备不能向字符设备那样访问,而是要先将请求放入队列,优化调整顺序后再执行,这种访问方式称为"电梯调度算法"。

本篇文章通过ramdisk、nand flash、nor flash来讲解如何写块设备驱动程序。

一、ramdisk

1.因为块设备驱动程序是将请求放入队列然后调整顺序后执行,所以我们需要先定义请求队列:

static unsigned char *ramblock_buf;
ramblock_buf = kzalloc(RAMBLOCK_SIZE, GFP_KERNEL);
static DEFINE_SPINLOCK(ramblock_lock);
struct request_queue_t *ramblock_queue = blk_init_queue(do_ramblock_request,&ramblock_lock);//提供读写函数和自旋锁


static void do_ramblock_request(request_queue_t *q)
{
     struct request *req;
     while ((req == elv_next_request(q) != NULL)) {
          unsigned long offset = req->sector * 512;
          unsigned long len = req->current_nr_sectors * 512;
          if (rq_data_dir(req) == READ) {
               memcpy(req->buffer,ramblock_buf+offset, len);
          } else {
               memcpy(ramblock_buf+offset, req->buffer, len);
          }
          end_request(req, 1);
     }
}

 

request结构体的主要成员包括:
sector_t sector;第一个尚未传输的扇区
unsigned long nr_sectors;尚待完成的扇区数
unsigned int current_nr_sectors;当前I/O操作中待完成的扇区数
驱动中会经常与这3个成员打交道,这3个成员在内核和驱动交互中发挥着重大作用。它们以512字节大小为一个扇区,如果硬件的扇区大小不是512字节,则需要进行相应的调整。例如,如果硬件的扇区大小是2048字节,则在进行硬件操作之前,需要用4来除起始扇区号。

2.注册块设备,并让系统为我们自动分配主设备号:

static int major;
major = register_blkdev(0,"ramblock");

3.构造block_device_operations结构体:

#define RAMBLOCK_SIZE (1024*1024)
struct blcok_device_operations = rameblcok_fops = {
     .owner = THIS_MODULE,
     .getgeo = ramblock_getgeo,
};
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;
}

4.分配设置gendisk结构体:

struct gendisk *ramblock_disk = alloc_disk(16);/* 次设备号个数:分区个数+1 */
ramblock_disk->major = major
ramblock_disk->first_minor = 0;
sprintf(ramblcok_disk->disk_name, "ramblock");
ramblcok_disk->fops = &ramblcok_fops;
set_capacity(ramblock_disk, RAMBLOCK_SIZE/512);
add_disk(ramblock_disk);

 5.测试:

(1) insmod ramblock.ko
(2) ls /dev/ramblock*   信息如下:brw-rw----    1 0        0        254,   0 Jan  1 02:26 /dev/ramblock
(3)分区:fdisk /dev/ramblock
输入:m 可以查看帮助信息
输入:n 用于创建新的分区
输入:p   用于建立主分区
然后根据提示可以创建分区
二、Nand flash
1.定义S3C2440的Nand flash控制寄存器结构体:
struct s3c_nand_regs {
    unsigned long nfconf  ;
    unsigned long nfcont  ;
    unsigned long nfcmd   ;
    unsigned long nfaddr  ;
    unsigned long nfdata  ;
    unsigned long nfeccd0 ;
    unsigned long nfeccd1 ;
    unsigned long nfeccd  ;
    unsigned long nfstat  ;
    unsigned long nfestat0;
    unsigned long nfestat1;
    unsigned long nfmecc0 ;
    unsigned long nfmecc1 ;
    unsigned long nfsecc  ;
    unsigned long nfsblk  ;
    unsigned long nfeblk  ;
};
static struct s3c_nand_regs *s3c_nand_regs;
s3c_nand_regs = ioremap(0x4E000000, sizeof(struct s3c_nand_regs));

2.手动定义分区:

static struct mtd_partition s3c_nand_parts[] = {
    [0] = {
        .name   = "bootloader",
        .size   = 0x00040000,
        .offset    = 0,
    },
    [1] = {
        .name   = "params",
        .offset = MTDPART_OFS_APPEND,
        .size   = 0x00020000,
    },
    [2] = {
        .name   = "kernel",
        .offset = MTDPART_OFS_APPEND,
        .size   = 0x00200000,
    },
    [3] = {
        .name   = "root",
        .offset = MTDPART_OFS_APPEND,
        .size   = MTDPART_SIZ_FULL,
    }
};

3.使能S3C2440的Nand flash控制器的时钟:

struct clk *clk = clk_get(NULL, "nand");
clk_enable(clk);
/* HCLK=100MHz
     * TACLS:  发出CLE/ALE之后多长时间才发出nWE信号, 从NAND手册可知CLE/ALE与nWE可以同时发出,所以TACLS=0
     * TWRPH0: nWE的脉冲宽度, HCLK x ( TWRPH0 + 1 ), 从NAND手册可知它要>=12ns, 所以TWRPH0>=1
     * TWRPH1: nWE变为高电平后多长时间CLE/ALE才能变为低电平, 从NAND手册可知它要>=5ns, 所以TWRPH1>=0
     */
#define TACLS    0
#define TWRPH0   1
#define TWRPH1   0
    s3c_nand_regs->nfconf = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);

    /* NFCONT: 
     * BIT1-设为1, 取消片选 
     * BIT0-设为1, 使能NAND FLASH控制器
     */
    s3c_nand_regs->nfcont = (1<<1) | (1<<0);

 

4.分配nand_chip和mtd_info结构体,并初始化:

struct nand_chip *s3c_nand = kzmalloc(sizeof(struct nand_chip), GFP_KERNEL);
struct mtd_info *s3c_mtd = kzmalloc(sizeof(struct mtd_info), GFP_KERNEL);

s3c_nand->select_chip = s3c2440_select_chip;
s3c_nand->cmd_ctrl = s3c2440_cmd_ctrl;
s3c_nand->IO_ADDR_R = &s3c_nand_regs->nfdata;
s3c_nand->IO_ADDR_W = &s3c_nand_regs->nfdata;
s3c_nand->dev_ready = s3c2440_dev_ready;
s3c_nand->ecc.mode = NAND_ECC_SOFT;
s3c_mtd->owner = THIS_MODULE;
s3c_mtd->priv = s3c_nand;
nand_scan(s3c_mtd,1);//识别Nand flash
add_mtd_partitions(s3c_mtd, s3c_nand_parts, 4);

static void s3c2440_select_chip(struct mtd_info *mtd, int chipnr)
{
     if (chipnr == -1)
     {
          /* 取消选中: NFCONT[1]设为1 */
          s3c_nand_regs->nfcont |= (1<<1);         
     }
     else
     {
          /* 选中: NFCONT[1]设为0 */
          s3c_nand_regs->nfcont &= ~(1<<1);
     }
}
/*
 *当ALE为高电平时传输的是地址,
 *当CLE为高电平时传输的是命令
 *当ALE和CLE都为低电平时传输的是数据
 */
static void s3c2440_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
     if (ctrl & NAND_CLE)
     {
          /* 发命令: NFCMMD=dat */
          s3c_nand_regs->nfcmd = dat;
     }
     else
     {
          /* 发地址: NFADDR=dat */
          s3c_nand_regs->nfaddr = dat;
     }
}

static int s3c2440_dev_ready(struct mtd_info *mtd)
{
     return (s3c_nand_regs->nfstat & (1<<0));
}

5.测试:

使用mtd-utils-05.07.23.tar.bz2

 三、Nor flash

1.分配map_info和mtd_info结构体并初始化:

static struct map_info *s3c_nor_map;
static struct mtd_info *s3c_nor_mtd;
s3c_nor_map = kzalloc(sizeof(struct map_info), GFP_KERNEL);
/* 设置:位宽、物理基地址、虚拟基地址、大小 */

s3c_nor_map->name = "s3c_nor";
s3c_nor_map->phys = 0;
s3c_nor_map->size = 0x1000000; /* >= NOR的真正大小 */
s3c_nor_map->bankwidth = 2;
s3c_nor_map->virt = ioremap(s3c_nor_map->phys, s3c_nor_map->size);

simple_map_init(s3c_nor_map);//这里面设置了读写和擦除等函数

2.手动分区:

static struct mtd_partition s3c_nor_parts[] = {
    [0] = {
        .name   = "bootloader_nor",
        .size   = 0x00040000,
        .offset    = 0,
    },
    [1] = {
        .name   = "root_nor",
        .offset = MTDPART_OFS_APPEND,
        .size   = MTDPART_SIZ_FULL,
    }
};

3.探测Nor flash:

/* 使用cfi协议探测NOR Flash */
s3c_nor_mtd = do_map_probe("cfi_probe", s3c_nor_map);
if (!s3c_nor_mtd) {
    /* 使用jedec协议探测NOR Flash */
     s3c_nor_mtd = do_map_probe("jedec_probe", s3c_nor_map);
}
if (!s3c_nor_mtd) {        
    iounmap(s3c_nor_map->virt);
    kfree(s3c_nor_map);
    return -EIO;
}
add_mtd_partitions(s3c_nor_mtd, s3c_nor_parts, 2);

4.测试:

使用mtd-utils-05.07.23.tar.bz2

 

 

 

posted @ 2014-07-03 17:17  zpehome  阅读(280)  评论(0编辑  收藏  举报