块设备驱动程序

/**
 * blk_init_queue  - prepare a request queue for use with a block device
-----给块设备提供一个请求队列使用
 * @rfn:  The function to be called to process requests that have been
 *        placed on the queue.
-----放入队列的请求使用该函数处理
 * @lock: Request queue spin lock
 *
-----请求队列的自旋锁
 * Description:
 *    If a block device wishes to use the standard request handling procedures,
 *    which sorts requests and coalesces adjacent requests, then it must
 *    call blk_init_queue().  
-------块设备需要使用标准的请求处理流程时(排序请求和合并相邻请求)需要调用blk_init_queue
The function @rfn will be called when there
 *    are requests on the queue that need to be processed.
  
If the device
 *    supports plugging, then @rfn may not be called immediately when requests are available on the queue, but may be called at some time later instead.
------如果设备支持插拔,当队列中的请求可以使用的时候,并不会立即就调用rfn函数,而是过一段时间才调用
 *    Plugged queues are generally unplugged when a buffer belonging to one
 *    of the requests on the queue is needed, or due to memory pressure.
 *
 *    @rfn is not required, or even expected, to remove all requests off the
 *    queue, but only as many as it can handle at a time.  If it does leave
 *    requests on the queue, it is responsible for arranging that the requests
 *    get dealt with eventually.
 *
 *    The queue spin lock must be held while manipulating the requests on the
 *    request queue; this lock will be taken also from interrupt context, so irq
 *    disabling is needed for it.
 *
 *    Function returns a pointer to the initialized request queue, or NULL if
 *    it didn't succeed.
 ------执行成功,函数返回指向这个request queue,不成功的话,返回空指针
 * Note:
 *    blk_init_queue() must be paired with a blk_cleanup_queue() call
------------这两个函数必须配对使用
 *    when the block device is deactivated (such as at module unload).
 **/

程序源码,重点查看init函数有详细讲解


/* :
 * drivers\block\xd.c
 * drivers\block\z2ram.c
 */

#include <linux/module.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/genhd.h>
#include <linux/hdreg.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/blkdev.h>
#include <linux/blkpg.h>
#include <linux/delay.h>
#include <linux/io.h>

#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/dma.h>


#define SIZE (1024)
static struct gendisk *ram_gendisk = NULL;
static struct request_queue *ram_queue = NULL;
static DEFINE_SPINLOCK(ram_lock);
static int major;
static char *buf = NULL;
static void do_queue_request(request_queue_t *q)
{
    
    struct request *req;
    static int cnt = 0;
    while ((req = elv_next_request(q)) != NULL) {
        unsigned long start = req->sector << 9;
        unsigned long len  = req->current_nr_sectors << 9;

        if (start + len > (SIZE * 512)) {
            printk( KERN_ERR  ": bad access: block=%lu, count=%u\n",
                req->sector, req->current_nr_sectors);
            end_request(req, 0);
            continue;
        }
        
        if (READ == rq_data_dir(req)) {
            memcpy(req->buffer, (const void *)(buf+start), len);
        } else {
            memcpy(buf+start, (const void *)req->buffer, len);
        }
        
        end_request(req, 1);
        printk("func:%s, line:%d, cnt:%d, R(0)W(1):%d, start:%x, len:%x, sector:%x, nr:%x\n",
            __func__, __LINE__, cnt++, rq_data_dir(req), start, len, req->sector, 
            req->current_nr_sectors);
    }

        
}

static struct block_device_operations ram_fops =
{
    .owner      = THIS_MODULE,
};

static int ramblock_init(void)
{
    /*
        1.分配一个gendisk结构体
        2.设置一个队列,将我们的请求放到队列里去
        3.设置这个gnedisk结构体的属性,如容量等
        4.add_gendisk函数
        5.另外需要自己分配一块内存空间用来当做块设备,在request函数中memcpy访问
    他,模仿块设备读写

    --------怎么进行测试
    1. insmod ramblock.ko
    2. 格式化: mkdosfs /dev/ramblock
    3. 挂接: mount /dev/ramblock /tmp/
    4. 读写文件: cd /tmp, 在里面vi文件
    5. cd /; umount /tmp/
    6. cat /dev/ramblock > /mnt/ramblock.bin
    7. 在PC上查看ramblock.bin
    sudo mount -o loop ramblock.bin /mnt
    */
    major = register_blkdev(0, "ramblock");

    if (major < 0) {
        printk("fail register block device!\n");
    }
    ram_gendisk =  alloc_disk(16);
    if (!ram_gendisk){
        printk("alloc disk fail!\n");
        return -1;
    }

    ram_queue = blk_init_queue(do_queue_request, &ram_lock);
    if (!ram_queue) {
        printk("queue is init fail!\n");
        return -1;
    }

    buf = kzalloc(SIZE * 512, GFP_KERNEL);
    if (!buf) {
        printk("alloc failed!\n");
        return -1;
    }
    ram_gendisk->major = major;
    ram_gendisk->first_minor = 0;
    ram_gendisk->fops = &ram_fops;
    sprintf(ram_gendisk->disk_name, "ramblock");

    ram_gendisk->queue = ram_queue;
    set_capacity(ram_gendisk, SIZE);
    add_disk(ram_gendisk);

    return 0;
}

static void ramblock_exit(void)
{
    if (unregister_blkdev(major, "ramblock")) {
        printk( KERN_ERR ": unregister of device failed\n");
    }
    del_gendisk(ram_gendisk);
    put_disk(ram_gendisk);

    blk_cleanup_queue(ram_queue);
}

module_init(ramblock_init);
module_exit(ramblock_exit);

MODULE_LICENSE("GPL");

此时还不能进行创建分区,使用fdisk命令的时候会有错误
需要在fp_ops中添加getgeo函数

# fdisk /dev/ramblock 
func:do_queue_request, line:60, cnt:182, R(0)W(1):0, start:0, len:1000, sector:8, nr:8
func:do_queue_request, line:60, cnt:183, R(0)W(1):0, start:1000, len:1000, sector:10, nr:8
func:do_queue_request, line:60, cnt:184, R(0)W(1):0, start:2000, len:1000, sector:18, nr:8
func:do_queue_request, line:60, cnt:185, R(0)W(1):0, start:3000, len:1000, sector:18, nr:8
Unknown value(s) for: cylinders (settable in the extra functions menu)

使用n命令创建分区,最终使用w保存分区,保存分区表,最终可以单独格式化或挂接其中一个分区

nand falsh设备驱动 K9F2G08U0C

NFCONF  0x4E000000
NFCMMD 0x4E000008
NFADDR  0x4E00000C
NFDATA  0x4E000010

  • 命令集
命令集

5.3 Copy-back Program
Copy-Back program with Read for Copy-Back is configured to quickly and efficiently rewrite data stored in one page without data re-loading when the bit error is not in data stored.
the bit error is not in data stored,存在页里的数据快速重写,没有了re-loading的时间,
Since the time-consuming re-loading cycles are removed, the system performance is improved. The benefit is especially obvious when a portion of a block is updated and the rest of the block also needs to be copied to the newly assigned free block. Copy-Back operation is a sequential execution of Read for Copy-Back and of copy-back program with the destination page address. A read operation with "35h" command and the
address of the source page moves the whole 2,112-byte data into the internal data buffer. A bit error is checked by sequential reading the data output. In
the case where there is no bit error, the data do not need to be reloaded. Therefore Copy-Back program operation is initiated by issuing Page-Copy DataInput command (85h) with destination page address. Actual programming operation begins after Program Confirm command (10h) is issued. Once the
program process starts, the Read Status Register command (70h) may be entered to read the status register. The system controller can detect the completion of a program cycle by monitoring the R/B output, or the Status bit(I/O 6) of the Status Register. When the Copy-Back Program is complete, the
Write Status Bit(I/O 0) may be checked(Figure 8 & Figure 9). The command register remains in Read Status command mode until another valid command is written to the command register.
During copy-back program, data modification is possible using random data input command (85h) as shown in Figure 9

1.选中芯片
读取NFCONT,修改使能那几位
OpenJTAG> md.b 0x4e000004 1
4e000004: 03
修改为0x1,使能nand
mw.b 0x4e000004 0x1

2.发出命令
mw.b 0x4e000008 0x90

3.发出地址
mw.b 0x4e00000c 0x0

4.读取数据,读取数据操作必须是1个字节,一个字节读取才能读取到正确数据,连续读取5个周期,不能一次性读取5个字节,这样得出来的数据不对
md.b 0x4e000010 1

读取数据操作,打开u-boot.bin(或者使用命令nand dump 0)对照获取回来的数据是否准确,同样发现获取数据时只能1次,可能因为数据线只有8根的原因
mw.b 0x4E000004 1
mw.b 0x4E000008 0x00
mw.b 0x4E00000C 0x00
mw.b 0x4E00000C 0x00
mw.b 0x4E00000C 0x00
mw.b 0x4E00000C 0x00
mw.b 0x4E00000C 0x00
mw.b 0x4E000008 0x30
md.b 0x4E000010 1

写好的源代码


/* 
 * drivers\mtd\nand\s3c2410.c
 * drivers\mtd\nand\at91_nand.c
 */

#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/clk.h>
 
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
 
#include <asm/io.h>
 
#include <asm/arch/regs-nand.h>
#include <asm/arch/nand.h>

static struct nand_chip *s3c_nand_chip = NULL;
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  ;
};

#define S3C2410_NFSTAT_BUSY        (1<<0)
#define NAND_SELECT                 (0x1)
#define NAND_DISELECT               (0x3)

#define TACLS_MASK    (0x3 << 12)
#define TWRPH0_MASK   (0x7 << 8)
#define TWRPH1_MASK   (0x7 << 4)

#define TACLS     (0x1 << 12)
#define TWRPH0    (0x1 << 8)
#define TWRPH1    (0x1 << 4)

static struct s3c_nand_regs *nand_reg = NULL;
static struct mtd_info *s3c_mtd_info = NULL;
static struct clk *nand_clk = NULL;


static struct mtd_partition my_nand_part[] = {
    [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,
    }
};

static int mynand_ready(struct mtd_info *mtd)
{
    return (nand_reg->nfstat & S3C2410_NFSTAT_BUSY);
}

static void mynand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
    if (NAND_CMD_NONE == cmd)
        return;

    if (ctrl & NAND_CLE) {
        writeb(cmd, &nand_reg->nfcmd);
    } else if (ctrl & NAND_ALE) {
        writeb(cmd, &nand_reg->nfaddr);
    }
}
static void mynand_select_chip(struct mtd_info *mtd, int chip)
{
    
    switch (chip) {
    case -1:
        nand_reg->nfcont &= NAND_DISELECT; 
        break;
    case 0:
        nand_reg->nfcont &= NAND_SELECT;
        break;

    default:
        BUG();
    }
}

static int __init nand_init(void)
{
    /*
        1.分配一个nand_chip结构体,select_chip,实现操作底层硬件的函数,设置
            1.1 芯片是8位的需要进行设置options为0
            1.2 select函数,-1表示不选中,0表示选中
            1.3 cmd_ctrl函数,写命令的函数
            1.4 dev_ready,芯片是否正在忙
            1.5 读写函数的地址需要告诉内核
            1.6 开启ECC
        2.分配设置mtd
        3.硬件时序上的设置,设置硬件的时序
        4.add_mtd_partitions
    */
    s3c_nand_chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
    if (!s3c_nand_chip) {
        printk(KERN_ERR "nand: failed to allocate chip structure.\n");
        return -ENOMEM;
    }
#if 0   
nand_scan
    nand_scan_ident
    nand_set_defaults
        nand_select_chip
        nand_command
    nand_get_flash_type
#endif

    nand_reg =  ioremap(0x4E000000, sizeof(struct s3c_nand_regs));
    if (!nand_reg) {
        printk("func:%s, line:%d failed to ioremap!\n", 
            __func__, __LINE__);
    }
    s3c_nand_chip->options = 0;
    s3c_nand_chip->select_chip = mynand_select_chip;
    

    s3c_nand_chip->cmd_ctrl = mynand_cmd_ctrl;
    s3c_nand_chip->dev_ready = mynand_ready;
    s3c_nand_chip->ecc.mode = NAND_ECC_SOFT;    /* enable ECC */
    s3c_nand_chip->IO_ADDR_R = &nand_reg->nfdata;
    s3c_nand_chip->IO_ADDR_W = &nand_reg->nfdata;
    
    s3c_mtd_info = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
    if (!s3c_mtd_info) {
        printk(KERN_ERR "nand: failed to allocate mtd_info structure.\n");
        return -ENOMEM;
    }
    s3c_mtd_info->priv = s3c_nand_chip;
    s3c_mtd_info->owner = THIS_MODULE;

    nand_reg->nfconf &= ~(TACLS_MASK | TWRPH0_MASK | TWRPH1_MASK);
    nand_reg->nfconf |= (TACLS | TWRPH0 | TWRPH1);
#if 0
    nand_clk = clk_get(NULL, "nand");
    
    if (IS_ERR(nand_clk) ) {
        printk(KERN_ERR "S3C2440: Failed to get parent clocks\n");
        return -EINVAL;
    }
    if (clk_enable(nand_clk)) {
        printk("func:%s, line:%d\n", __func__, __LINE__);
    }
#endif
    nand_scan(s3c_mtd_info, 1);

    if (add_mtd_partitions(s3c_mtd_info, my_nand_part, 4)) {
        printk("func:%s, line:%d, can't add mtd partitions\n", __func__, __LINE__);
        return -ENOMEM;
    }

    
    return 0;
}

static void __exit nand_exit(void)
{
    kfree(s3c_mtd_info);
    kfree(s3c_nand_chip);
    iounmap(nand_reg);
    del_mtd_partitions(s3c_mtd_info);
}

module_init(nand_init);
module_exit(nand_exit);

MODULE_LICENSE("GPL");

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容