塊設備驅動程序

/**
 * 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.
-----放入隊列的請求使用該函數(shù)處理
 * @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函數(shù),而是過一段時間才調用
 *    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.
 ------執(zhí)行成功,函數(shù)返回指向這個request queue,不成功的話,返回空指針
 * Note:
 *    blk_init_queue() must be paired with a blk_cleanup_queue() call
------------這兩個函數(shù)必須配對使用
 *    when the block device is deactivated (such as at module unload).
 **/

程序源碼,重點查看init函數(shù)有詳細講解


/* :
 * 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函數(shù)
        5.另外需要自己分配一塊內存空間用來當做塊設備,在request函數(shù)中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");

此時還不能進行創(chuàng)建分區(qū),使用fdisk命令的時候會有錯誤
需要在fp_ops中添加getgeo函數(shù)

# 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命令創(chuàng)建分區(qū),最終使用w保存分區(qū),保存分區(qū)表,最終可以單獨格式化或掛接其中一個分區(qū)

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,存在頁里的數(shù)據(jù)快速重寫,沒有了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.發(fā)出命令
mw.b 0x4e000008 0x90

3.發(fā)出地址
mw.b 0x4e00000c 0x0

4.讀取數(shù)據(jù),讀取數(shù)據(jù)操作必須是1個字節(jié),一個字節(jié)讀取才能讀取到正確數(shù)據(jù),連續(xù)讀取5個周期,不能一次性讀取5個字節(jié),這樣得出來的數(shù)據(jù)不對
md.b 0x4e000010 1

讀取數(shù)據(jù)操作,打開u-boot.bin(或者使用命令nand dump 0)對照獲取回來的數(shù)據(jù)是否準確,同樣發(fā)現(xiàn)獲取數(shù)據(jù)時只能1次,可能因為數(shù)據(jù)線只有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,實現(xiàn)操作底層硬件的函數(shù),設置
            1.1 芯片是8位的需要進行設置options為0
            1.2 select函數(shù),-1表示不選中,0表示選中
            1.3 cmd_ctrl函數(shù),寫命令的函數(shù)
            1.4 dev_ready,芯片是否正在忙
            1.5 讀寫函數(shù)的地址需要告訴內核
            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");

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末辆它,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子歉嗓,更是在濱河造成了極大的恐慌疚顷,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尘颓,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機虏辫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來锈拨,“玉大人砌庄,你說我怎么就攤上這事∞仁啵” “怎么了娄昆?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長缝彬。 經(jīng)常有香客問我萌焰,道長,這世上最難降的妖魔是什么谷浅? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任扒俯,我火速辦了婚禮,結果婚禮上一疯,老公的妹妹穿的比我還像新娘撼玄。我一直安慰自己,他們只是感情好墩邀,可當我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布掌猛。 她就那樣靜靜地躺著,像睡著了一般眉睹。 火紅的嫁衣襯著肌膚如雪留潦。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天辣往,我揣著相機與錄音兔院,去河邊找鬼。 笑死站削,一個胖子當著我的面吹牛坊萝,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼十偶,長吁一口氣:“原來是場噩夢啊……” “哼菩鲜!你這毒婦竟也來了?” 一聲冷哼從身側響起惦积,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤接校,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后狮崩,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蛛勉,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年睦柴,在試婚紗的時候發(fā)現(xiàn)自己被綠了诽凌。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡坦敌,死狀恐怖侣诵,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情狱窘,我是刑警寧澤杜顺,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站蘸炸,受9級特大地震影響躬络,放射性物質發(fā)生泄漏。R本人自食惡果不足惜幻馁,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一洗鸵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧仗嗦,春花似錦膘滨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至德撬,卻和暖如春铲咨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蜓洪。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工纤勒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人隆檀。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓摇天,卻偏偏與公主長得像粹湃,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子泉坐,可洞房花燭夜當晚...
    茶點故事閱讀 45,860評論 2 361

推薦閱讀更多精彩內容