• 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
  • 相关阅读:
    附近有什么?8款可以查周边的App
    实体店里充话费要怎么弄
    怎样买手机号?
    手机号是SIM卡的号呢,还是买手机时就带的
    网站SSL证书在线检测
    未来什么行业最赚钱
    陈安之-如何选择最赚钱的行业
    斗鱼宣布获C轮15亿融资 直播行业进入资本时代
    2016年Godaddy最新域名转出教程
    GoDaddy账户间域名转移PUSH以及ACCEPT接受域名过户方法
  • 原文地址:https://www.cnblogs.com/zpehome/p/3822654.html
Copyright © 2020-2023  润新知