`
yzd
  • 浏览: 1805914 次
  • 性别: Icon_minigender_2
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Linux NAND FLASH驱动程序框架分析

 
阅读更多
<div id="content">
<p>1.Linux-MTD Subsystem </p>
<p>FLASH在嵌入式系统中是必不可少的,它是bootloader、linux内核和文件系统的最佳载体。在Linux内核中引入了MTD子系统为NOR FLASH和NAND FLASH设备提供统一的接口,从而使得FLASH驱动的设计大为简化。</p>
<p>在引入MTD后Linux系统中FLASH设备驱动可分为四层,如图:</p>
<img src="http://www.linuxidc.com/upload/2011_01/11011418276979.png" alt="Linux NAND FLASH驱动程序框架分析" align="middle"><table style="width: 97%;" border="0" align="center"><tbody><tr>
<td colspan="3"><br></td>
</tr></tbody></table>
<p>1. 硬件驱动层</p>
<p>FLASH硬件驱动层负责FLASH硬件设备的读、写、擦出,LINUX MTD设备的NOR FLASH驱动位于/driver/mtd/chips子目录下,NAND FLASH驱动则位于/driver/mtd/nand子目录下。</p>
<p>2. MTD原始设备层:MTD原始设备层由两部分构成,一部分是MTD原始设备的通用代码(mtdcore.c、mtdpart.c),另一部分是各个特定的FLASH的数据,例如分区。</p>
<p>3. MTD设备层:基于MTD原始设备,LINUX系统可以定义出MTD的块设备(主设备号31)www.linuxidc.com和字符设备(设备号90),构成设备层。MTD字符设备在mtdchar.c实现,MTD块设备在mtdblock.c实现。</p>
<p>4. 设备节点:通过mknod在/dev子目录下建立MTD字符设备节点(主设备号为90)和块设备节点(主设备号为31),用户通过访问此设备节点即可访问MTD字符设备和块设备。</p>
<p>也可通过下图理解:</p>
<img src="http://www.linuxidc.com/upload/2011_01/11011418273428.png" alt="Linux NAND FLASH驱动程序框架分析" align="middle"><img src="http://www.linuxidc.com/upload/2011_01/11011418272183.jpg" alt="Linux NAND FLASH驱动程序框架分析" align="middle"><p>从上图可以看出,MTD设备层与原始设备层打交道。通过分析源代码我们可以知道当上层要求对FLASH进行读写时,它会像设备层发出请求,设备层的
读写函数会调用原始设备层中的读写函数,即mtd_info结构体(mtd原始设备层中描述设备的专用结构体)中的读写函数,而mtd_info中的函数
会调用nand_chip(nand硬件驱动层中描述设备的结构体,其中包含了针对特定设备的基本参数和设备操作函数)中的读写函数。所以当我们写一个
flash硬件驱动程序时,有以下步骤:</p>
<p>1. 如果FLASH要分区,则定义mtd_partition数组,将FLASH分区信息记录其中。</p>
<p>2. 在模块加载时为每一个chip(主分区)分配mtd_info和nand_chip的内存,根据目标板nand
控制器的特殊情况初始化nand_chip中的实现对FLASH操作的成员函数,如hwcontrol()、dev_ready()、
read_byte()、write_byte()等。填充mtd_info,并将其priv成员指向nand_chip。</p>
<p>3. 以mtd_info为参数调用nand_scan()函数探测NAND FLASH的存在。nand_scan()函数会从FLASH芯片中读取其参数,填充相应nand_chip成员。</p>
<p>4. 如果要分区,则以mtd_info和mtd_partition为参数调用add_mtd_partions(),添加分区信息。在这个函数里面会为每一个分区(不包含主分区)分配一个mtd_info结构体,www.linuxidc.com填充,并注册。</p>
<div id="content">
<p>2.nand flash驱动程序实例分析</p>
<p>我们以2.6.26内核中s3c2410的nand flash驱动程序为例来分析一下这个过程,这里的flash驱动被写成了platform驱动的形式。我们下面分析其过程:</p>
<p>1. 注册nand flash设备</p>
<p>nand flash分区:</p>
<p>linux2.6.26.8/arch/arm/plat-s3c24xx/common-smdk.c:</p>
<table style="width: 97%;" border="0" align="center"><tbody><tr>
<td colspan="3"><br></td>
</tr></tbody></table>
<p>static struct mtd_partition smdk_default_nand_part[] = {</p>
<p>[0] = {</p>
<p>name: "bootloader",</p>
<p>size: 0x00100000,</p>
<p>offset: 0x0,</p>
<p>},</p>
<p>[1] = {</p>
<p>name: "kernel",</p>
<p>size: 0x00300000,</p>
<p>offset: 0x00100000,</p>
<p>},</p>
<p>[2] = {</p>
<p>name: "root",</p>
<p>size: 0x02800000,</p>
<p>offset: 0x00400000,</p>
<p>},</p>
<p>};</p>
<p>static struct s3c2410_nand_set smdk_nand_sets[] = { //该数组为chip集合,这里我们只有一片chip</p>
<p>[0] = {</p>
<p>.name = "NAND",</p>
<p>.nr_chips = 1,</p>
<p>.nr_partitions = ARRAY_SIZE(smdk_default_nand_part),</p>
<p>.partitions = smdk_default_nand_part,</p>
<p>},</p>
<p>};</p>
<p>static struct s3c2410_platform_nand smdk_nand_info = { //这里将许多数据作为platform_data传入包括chip数组</p>
<p>.tacls = 20,</p>
<p>.twrph0 = 60,</p>
<p>.twrph1 = 20,</p>
<p>.nr_sets = ARRAY_SIZE(smdk_nand_sets),</p>
<p>.sets = smdk_nand_sets,</p>
<p>};</p>
<p>nand控制器资源:</p>
<p>linux2.6.26.8/arch/arm/plat-s3c24xx/devs.c</p>
<p>static struct resource s3c_nand_resource[] = {</p>
<p>[0] = {</p>
<p>.start = S3C2410_PA_NAND,</p>
<p>.end = S3C2410_PA_NAND + S3C24XX_SZ_NAND - 1,</p>
<p>.flags = IORESOURCE_MEM,</p>
<p>}</p>
<p>};</p>
<p>struct platform_device s3c_device_nand = {</p>
<p>.name = "s3c2410-nand",</p>
<p>.id = -1,</p>
<p>.num_resources = ARRAY_SIZE(s3c_nand_resource),</p>
<p>.resource = s3c_nand_resource,</p>
<p>};</p>
<p>注册nand flash作为platform device:</p>
<p>linux2.6.26.8/arch/arm/plat-s3c24xx/common-smdk.c:</p>
<p>static struct platform_device __initdata *smdk_devs[] = {</p>
<p>&amp;s3c_device_nand,</p>
<p>…</p>
<p>};</p>
<p>void __init smdk_machine_init(void)</p>
<p>{</p>
<p>…</p>
<p>s3c_device_nand.dev.platform_data = &amp;smdk_nand_info; //注意这里的赋值,在nand flash驱动程序的probe函数里面利用了这里赋值的数据</p>
<p>platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs));</p>
<p>s3c2410_pm_init();</p>
<p>}</p>
<div id="content">
<p>2. 注册nand flash driver<br>
linux/drivers/mtd/nand/s3c2410.c:</p>
<p>static struct platform_driver s3c2410_nand_driver = {</p>
<p>.probe = s3c2410_nand_probe,</p>
<p>.remove = s3c2410_nand_remove,</p>
<table style="width: 97%;" border="0" align="center"><tbody><tr>
<td colspan="3"><br></td>
</tr></tbody></table>
<p>.suspend = s3c24xx_nand_suspend,</p>
<p>.resume = s3c24xx_nand_resume,</p>
<p>.driver = {</p>
<p>.name = "s3c2410-nand",</p>
<p>.owner = THIS_MODULE,</p>
<p>},</p>
<p>};</p>
<p>static int __init s3c2410_nand_init(void)</p>
<p>{</p>
<p>printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics/n");</p>
<p></p>
<p>platform_driver_register(&amp;s3c2412_nand_driver);</p>
<p>platform_driver_register(&amp;s3c2440_nand_driver);</p>
<p>return platform_driver_register(&amp;s3c2410_nand_driver);</p>
<p>}</p>
<p>module_init(s3c2410_nand_init);</p>
<p>当platform_driver驱动被加载时或者是当platform_device被注册时,总线驱动程序</p>
<p>会查找与设备匹配的驱动程序,找到时设备驱动程序的probe函数会被调用,下面我们来分析一下在我们驱动程序中的probe函数:</p>
<p>static int s3c2410_nand_probe(struct platform_device *dev)</p>
<p>{</p>
<p>return s3c24xx_nand_probe(dev, TYPE_S3C2410);</p>
<p>}</p>
<p>static int s3c24xx_nand_probe(struct platform_device *pdev,</p>
<p> enum s3c_cpu_type cpu_type)</p>
<p>{</p>
<p>struct s3c2410_platform_nand *plat = to_nand_plat(pdev);</p>
<p>struct s3c2410_nand_info *info;</p>
<p>struct s3c2410_nand_mtd *nmtd;</p>
<p>struct s3c2410_nand_set *sets;</p>
<p>struct resource *res;</p>
<p>int err = 0;</p>
<p>int size;</p>
<p>int nr_sets;</p>
<p>int setno;</p>
<p></p>
<p>pr_debug("s3c2410_nand_probe(%p)/n", pdev);</p>
<p></p>
<p>info = kmalloc(sizeof(*info), GFP_KERNEL); //分配s3c2410_nand_info内存</p>
<p>if (info == NULL) {</p>
<p>dev_err(&amp;pdev-&gt;dev, "no memory for flash info/n");</p>
<p>err = -ENOMEM;</p>
<p>goto exit_error;</p>
<p>}</p>
<p></p>
<p>memzero(info, sizeof(*info)); //将s3c2410_nand_info清零</p>
<p>platform_set_drvdata(pdev, info); //pdev-&gt;dev-&gt;driver_data = info</p>
<p></p>
<p>spin_lock_init(&amp;info-&gt;controller.lock);</p>
<p>init_waitqueue_head(&amp;info-&gt;controller.wq);</p>
<p></p>
<p></p>
<p></p>
<p>info-&gt;clk = clk_get(&amp;pdev-&gt;dev, "nand");</p>
<p>if (IS_ERR(info-&gt;clk)) {</p>
<p>dev_err(&amp;pdev-&gt;dev, "failed to get clock/n");</p>
<p>err = -ENOENT;</p>
<p>goto exit_error;</p>
<p>}</p>
<p></p>
<p>clk_enable(info-&gt;clk);</p>
<p></p>
<p></p>
<p></p>
<p></p>
<p>res = pdev-&gt;resource;</p>
<p>size = res-&gt;end - res-&gt;start + 1;</p>
<p></p>
<p>info-&gt;area = request_mem_region(res-&gt;start, size, pdev-&gt;name);</p>
<p></p>
<p>if (info-&gt;area == NULL) {</p>
<p>dev_err(&amp;pdev-&gt;dev, "cannot reserve register region/n");</p>
<p>err = -ENOENT;</p>
<p>goto exit_error;</p>
<p>}</p>
<p></p>
<p>info-&gt;device = &amp;pdev-&gt;dev;</p>
<p>info-&gt;platform = plat;</p>
<p>info-&gt;regs = ioremap(res-&gt;start, size); //存储nand控制器寄存器虚拟地</p>
<p>址</p>
<p>info-&gt;cpu_type = cpu_type;</p>
<p></p>
<p>if (info-&gt;regs == NULL) {</p>
<p>dev_err(&amp;pdev-&gt;dev, "cannot reserve register region/n");</p>
<p>err = -EIO;</p>
<p>goto exit_error;</p>
<p>}</p>
<p></p>
<p>dev_dbg(&amp;pdev-&gt;dev, "mapped registers at %p/n", info-&gt;regs);</p>
<p></p>
<p></p>
<p></p>
<p>err = s3c2410_nand_inithw(info, pdev); //设置TACLS TWRPH0 TWRPH1</p>
<p>if (err != 0)</p>
<p>goto exit_error;</p>
<p></p>
<p>sets = (plat != NULL) ? plat-&gt;sets : NULL; //sets指向plat-&gt;sets数组的首地址</p>
<p>nr_sets = (plat != NULL) ? plat-&gt;nr_sets : 1; //plat-&gt;sets中的chips数目</p>
<p></p>
<p>info-&gt;mtd_count = nr_sets;</p>
<p>size = nr_sets * sizeof(*info-&gt;mtds); </p>
<p>info-&gt;mtds = kmalloc(size, GFP_KERNEL);</p>
<p>if (info-&gt;mtds == NULL) {</p>
<p>dev_err(&amp;pdev-&gt;dev, "failed to allocate mtd storage/n");</p>
<p>err = -ENOMEM;</p>
<p>goto exit_error;</p>
<p>}</p>
<p>memzero(info-&gt;mtds, size); //将申请的s3c2410_nand_mtd结构体数组清零</p>
<p>nmtd = info-&gt;mtds;</p>
<p>for (setno = 0; setno &lt; nr_sets; setno++, nmtd++) { </p>
<p>pr_debug("initialising set %d (%p, info %p)/n", setno, nmtd, info);</p>
<p></p>
<p>s3c2410_nand_init_chip(info, nmtd, sets); //初始化s3c2410_nand_mtd结构</p>
<p>体中的chip成员和mtd成员,且mtd.priv = chip</p>
<p></p>
<p>nmtd-&gt;scan_res = nand_scan_ident(&amp;nmtd-&gt;mtd,</p>
<p>(sets) ? sets-&gt;nr_chips : 1); //设置nand_chip一些成员</p>
<p>的默认值并探测FLASH,并读出FLASH参数,填入nand_chip</p>
<p></p>
<p>if (nmtd-&gt;scan_res == 0) {</p>
<p>s3c2410_nand_update_chip(info, nmtd); //</p>
<p>nand_scan_tail(&amp;nmtd-&gt;mtd); //设置nand_chip中所有未被设置的</p>
<p>函数指针的值,并填充相关mtd_info成员,若需要建立bad block table</p>
<p>s3c2410_nand_add_partition(info, nmtd, sets); //添加分区</p>
<p>}</p>
<p></p>
<p>if (sets != NULL)</p>
<p>sets++; //注意这里sets++,指向下一个plat-&gt;sets里的set</p>
<p>}</p>
<p></p>
<p>if (allow_clk_stop(info)) {</p>
<p>dev_info(&amp;pdev-&gt;dev, "clock idle support enabled/n");</p>
<p>clk_disable(info-&gt;clk);</p>
<p>}</p>
<p></p>
<p>pr_debug("initialised ok/n");</p>
<p>return 0;</p>
<p></p>
<p>exit_error:</p>
<p>s3c2410_nand_remove(pdev);</p>
<p></p>
<p>if (err == 0)</p>
<p>err = -EINVAL;</p>
<p>return err;</p>
<p>}</p>
<p></p>
<p></p>
<p>附:几个机构体</p>
<img src="http://www.linuxidc.com/upload/2011_01/11011418309540.jpg" alt="Linux NAND FLASH驱动程序框架分析" align="middle"><img src="http://www.linuxidc.com/upload/2011_01/11011418301662.jpg" alt="Linux NAND FLASH驱动程序框架分析" align="middle"><img src="http://www.linuxidc.com/upload/2011_01/11011418309306.jpg" alt="Linux NAND FLASH驱动程序框架分析" align="middle"><img src="http://www.linuxidc.com/upload/2011_01/11011418309580.jpg" alt="Linux NAND FLASH驱动程序框架分析" align="middle"><br><br><br><textarea cols="50" rows="15" name="code" class="c-sharp">struct mtd_info {
u_char type;
u_int32_t flags;
u_int32_t size;  // Total size of the MTD


u_int32_t erasesize;

u_int32_t writesize;

u_int32_t oobsize;   // Amount of OOB data per block (e.g. 16)
u_int32_t oobavail;  // Available OOB bytes per block

// Kernel-only stuff starts here.
char *name;
int index;


struct nand_ecclayout *ecclayout;


int numeraseregions;
struct mtd_erase_region_info *eraseregions;


int (*erase) (struct mtd_info *mtd, struct erase_info *instr);



int (*point) (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, void **virt, resource_size_t *phys);


void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);


int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);



int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);

int (*read_oob) (struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops);
int (*write_oob) (struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops);


int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);


int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);


void (*sync) (struct mtd_info *mtd);


int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len);
int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);


int (*suspend) (struct mtd_info *mtd);
void (*resume) (struct mtd_info *mtd);


int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);

struct notifier_block reboot_notifier;


struct mtd_ecc_stats ecc_stats;

int subpage_sft;

void *priv;

struct module *owner;
int usecount;


int (*get_device) (struct mtd_info *mtd);
void (*put_device) (struct mtd_info *mtd);
};

struct nand_chip {
void  __iomem *IO_ADDR_R;
void  __iomem *IO_ADDR_W;

uint8_t (*read_byte)(struct mtd_info *mtd);
u16 (*read_word)(struct mtd_info *mtd);
void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
void (*select_chip)(struct mtd_info *mtd, int chip);
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
void (*cmd_ctrl)(struct mtd_info *mtd, int dat,
    unsigned int ctrl);
int (*dev_ready)(struct mtd_info *mtd);
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
void (*erase_cmd)(struct mtd_info *mtd, int page);
int (*scan_bbt)(struct mtd_info *mtd);
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
      const uint8_t *buf, int page, int cached, int raw);

int chip_delay;
unsigned int options;

int page_shift;
int phys_erase_shift;
int bbt_erase_shift;
int chip_shift;
int numchips;
unsigned long chipsize;
int pagemask;
int pagebuf;
int subpagesize;
uint8_t cellinfo;
int badblockpos;

nand_state_t state;

uint8_t *oob_poi;
struct nand_hw_control  *controller;
struct nand_ecclayout *ecclayout;

struct nand_ecc_ctrl ecc;
struct nand_buffers *buffers;
struct nand_hw_control hwcontrol;

struct mtd_oob_ops ops;

uint8_t *bbt;
struct nand_bbt_descr *bbt_td;
struct nand_bbt_descr *bbt_md;

struct nand_bbt_descr *badblock_pattern;

void *priv;
}; </textarea><br><br>
</div>
</div>
</div>
分享到:
评论

相关推荐

    【详解】如何编写Linux下Nand Flash驱动 v2.1

    本文主要介绍了Nand Flash的各种硬件背景知识,以及Linux框架下的MTD等软件背景知识,最后介绍了在Linux的MTD驱动框架下,如何实现Nand Flash的驱动。

    史上最强的嵌入式底层驱动开发课程 Linux系统开发+Linux高级程序+主板开发+ARM等

    │ ├08 - Linux基础及操作系统框架1.mp4 │ ├09 - Linux基础及操作系统框架2.mp4 │ ├10 - Linux基础及操作系统框架3.mp4 │ ├11 - Linux基础及操作系统框架4.mp4 │ ├12 - Shell命令机制1.mp4 │ ├13 - Shell...

    Android底层开发技术实战详解--内核、移植和驱动.(电子工业.王振丽).part3

    5.8.5 nandflash驱动程序162 5.8.6 mmc驱动程序162 5.8.7 电池驱动程序162 第6章 msm内核和驱动解析164 6.1 msm基础164 6.1.1 常见msm处理器产品164 6.1.2 snapdragon内核介绍165 6.2 移植msm内核简介...

    Android底层开发技术实战详解--内核、移植和驱动.(电子工业.王振丽).part1

    5.8.5 nandflash驱动程序162 5.8.6 mmc驱动程序162 5.8.7 电池驱动程序162 第6章 msm内核和驱动解析164 6.1 msm基础164 6.1.1 常见msm处理器产品164 6.1.2 snapdragon内核介绍165 6.2 移植msm内核简介...

    Android底层开发技术实战详解--内核、移植和驱动.(电子工业.王振丽).part2

    5.8.5 nandflash驱动程序162 5.8.6 mmc驱动程序162 5.8.7 电池驱动程序162 第6章 msm内核和驱动解析164 6.1 msm基础164 6.1.1 常见msm处理器产品164 6.1.2 snapdragon内核介绍165 6.2 移植msm内核简介...

    郭天祥ARM9视频教程(第13和20讲均可观看).docx

    3. 字符设备驱动程序设计实例 4. 中断和同步的处理 第二十讲 其他类型设备驱动 1. Linux块设备驱动框架 2. MTD设备驱动分析 3. LCD驱动,音频驱动简介 4. 网络设备驱动分析 第九部分 QT图形界面开发 第二十一讲 QT及...

    学ARM和学单片机一样简单12

    5、GPIO字符驱动程序(130分钟)(第八讲) (1)、驱动程序编写 (2)、快照的使用 (3)、内核配置 (4)、内核编译 (5)、演示实验 6、中断字符驱动程序(52分钟)(第八讲) (1)、驱动程序编写 (2...

    学ARM和学单片机一样简单15

    5、GPIO字符驱动程序(130分钟)(第八讲) (1)、驱动程序编写 (2)、快照的使用 (3)、内核配置 (4)、内核编译 (5)、演示实验 6、中断字符驱动程序(52分钟)(第八讲) (1)、驱动程序编写 (2...

    学ARM和学单片机一样简单4

    5、GPIO字符驱动程序(130分钟)(第八讲) (1)、驱动程序编写 (2)、快照的使用 (3)、内核配置 (4)、内核编译 (5)、演示实验 6、中断字符驱动程序(52分钟)(第八讲) (1)、驱动程序编写 (2...

    学ARM和学单片机一样简单3

    5、GPIO字符驱动程序(130分钟)(第八讲) (1)、驱动程序编写 (2)、快照的使用 (3)、内核配置 (4)、内核编译 (5)、演示实验 6、中断字符驱动程序(52分钟)(第八讲) (1)、驱动程序编写 (2...

    学ARM和学单片机一样简单9

    5、GPIO字符驱动程序(130分钟)(第八讲) (1)、驱动程序编写 (2)、快照的使用 (3)、内核配置 (4)、内核编译 (5)、演示实验 6、中断字符驱动程序(52分钟)(第八讲) (1)、驱动程序编写 (2...

    学ARM和学单片机一样简单2

    5、GPIO字符驱动程序(130分钟)(第八讲) (1)、驱动程序编写 (2)、快照的使用 (3)、内核配置 (4)、内核编译 (5)、演示实验 6、中断字符驱动程序(52分钟)(第八讲) (1)、驱动程序编写 (2...

    学ARM和学单片机一样简单11

    5、GPIO字符驱动程序(130分钟)(第八讲) (1)、驱动程序编写 (2)、快照的使用 (3)、内核配置 (4)、内核编译 (5)、演示实验 6、中断字符驱动程序(52分钟)(第八讲) (1)、驱动程序编写 (2...

    学ARM和学单片机一样简单7

    5、GPIO字符驱动程序(130分钟)(第八讲) (1)、驱动程序编写 (2)、快照的使用 (3)、内核配置 (4)、内核编译 (5)、演示实验 6、中断字符驱动程序(52分钟)(第八讲) (1)、驱动程序编写 (2...

    学ARM和学单片机一样简单5

    5、GPIO字符驱动程序(130分钟)(第八讲) (1)、驱动程序编写 (2)、快照的使用 (3)、内核配置 (4)、内核编译 (5)、演示实验 6、中断字符驱动程序(52分钟)(第八讲) (1)、驱动程序编写 (2...

    学ARM和学单片机一样简单13

    5、GPIO字符驱动程序(130分钟)(第八讲) (1)、驱动程序编写 (2)、快照的使用 (3)、内核配置 (4)、内核编译 (5)、演示实验 6、中断字符驱动程序(52分钟)(第八讲) (1)、驱动程序编写 (2...

    学ARM和学单片机一样简单6

    5、GPIO字符驱动程序(130分钟)(第八讲) (1)、驱动程序编写 (2)、快照的使用 (3)、内核配置 (4)、内核编译 (5)、演示实验 6、中断字符驱动程序(52分钟)(第八讲) (1)、驱动程序编写 (2...

    学ARM和学单片机一样简单14

    5、GPIO字符驱动程序(130分钟)(第八讲) (1)、驱动程序编写 (2)、快照的使用 (3)、内核配置 (4)、内核编译 (5)、演示实验 6、中断字符驱动程序(52分钟)(第八讲) (1)、驱动程序编写 (2...

    学ARM和学单片机一样简单8

    5、GPIO字符驱动程序(130分钟)(第八讲) (1)、驱动程序编写 (2)、快照的使用 (3)、内核配置 (4)、内核编译 (5)、演示实验 6、中断字符驱动程序(52分钟)(第八讲) (1)、驱动程序编写 (2...

    SPDK开发手册中文版.docx

    2.1. 用户空间驱动程序** 6 2.2. 来自用户空间的DMA** 7 2.3. 消息传递和并发** 9 2.4. NAND Flash SSD内部 13 2.5. 将I / O提交到NVMe设备** 15 2.5.1 NVMe规范 15 2.5.2 SPDK NVMe驱动程序I / O路径 15 2.6. 使用...

Global site tag (gtag.js) - Google Analytics