[来 源]:http://blog168.chinaunix.net/space.php?uid=25678596&do=blog&id=2102933

参考资料:

1.SD Memory Card Specifications / Part 1. Physical Layer Specification; Version 1.0

2.LDD3 CHAPTER-16 BLOCK DEVICE

3. http://www.sdcard.org

引言: 前几天把mini2440 的sd 卡驱动程序移植到了Android 平台,当时对SD 卡以及内核的MMC 子系统不是很了解,浏览了四天的代码,终于理清了一些头绪,尽管很多细节的实现还不是很清楚,不过先把知道的记录下来,细节部分由时间在慢慢挖掘。本文先介绍了一下MMC 的基本框架结构,然后采用自底向上的方法来分析整个MMC 层是如何共同作用的。阅读时请结合参考资料1 和2.

1. 硬件基础:

http://blog.ednchina.com/yelov/198217/message.aspx

SD/MMC/SDIO 概念区分概要

SD (Secure Digital )与 MMC (Multimedia Card )

SD 是一种 flash memory card 的标准,也就是一般常见的 SD 记忆卡,而 MMC 则是较早的一种记忆卡标准,目前已经被 SD 标准所取代。在维基百科上有相当详细的 SD/MMC 规格说明: [ http://zh.wikipedia.org/wiki/Secure_Digital ] 。

SDIO 是目前我们比较关心的技术,SDIO 故名思义,就是 SD 的 I/O 接口(interface )的意思,不过这样解释可能还有点抽像。更具体的说明,SD 本来是记忆卡的标准,但是现在也可以把 SD 拿来插上一些外围接口使用,这样的技术便是 SDIO 。

所以 SDIO 本身是一种相当单纯的技术,透过 SD 的 I/O 接脚来连接外部外围,并且透过 SD 上的 I/O 数据接位与这些外围传输数据,而且 SD 协会会员也推出很完整的 SDIO stack 驱动程序,使得 SDIO 外围(我们称为 SDIO 卡)的开发与应用变得相当热门。

现在已经有非常多的手机或是手持装置都支持 SDIO 的功能(SD 标准原本就是针对 mobile device 而制定),而且许多 SDIO 外围也都被开发出来,让手机外接外围更加容易,并且开发上更有弹性(不需要内建外围)。目前常见的 SDIO 外围(SDIO 卡)有:

·                                 Wi-Fi card (无线网络卡)

·                                 CMOS sensor card (照相模块)

·                                 GPS card

·                                 GSM/GPRS modem card

·                                 Bluetooth card

·                                 Radio/TV card (很好玩)

SDIO 的应用将是未来嵌入式系统最重要的接口技术之一,并且也会取代目前 GPIO 式的 SPI 接口。

SD/SDIO 的传输模式

SD 传输模式有以下 3 种:

·                                 SPI mode (required )

·                                 1-bit mode

·                                 4-bit mode

SDIO 同样也支持以上 3 种传输模式。依据 SD 标准,所有的 SD (记忆卡)与 SDIO (外围)都必须支持 SPI mode ,因此 SPI mode 是「required 」。此外,早期的 MMC 卡(使用 SPI 传输)也能接到 SD 插糟(SD slot ),并且使用 SPI mode 或 1-bit mode 来读取。

SD 的 MMC Mode

SD 也能读取 MMC 内存,虽然 MMC 标准上提到,MMC 内存不见得要支持 SPI mode (但是一定要支持 1-bit mode ),但是市面上能看到的 MMC 卡其实都有支持 SPI mode 。因此,我们可以把 SD 设定成 SPI mode 的传输方式来读取 MMC 记忆卡。

SD 的 MMC Mode 就是用来读取 MMC 卡的一种传输模式。不过,SD 的 MMC Mode 虽然也是使用 SPI mode ,但其物理特性仍是有差异的:

·                                 MMC 的 SPI mode 最大传输速率为 20 Mbit/s ;

·                                 SD 的 SPI mode 最大传输速率为 25 Mbit/s 。

为避免混淆,有时也用 SPI/MMC mode 与 SPI/SD mode 的写法来做清楚区别。

 

2.MMC 子系统的基本框架结构:

很遗憾,内核没有为我们提供关于MMC 子系统的文档,在谷歌上搜索了很多,也没有找到相关文章。只能自己看代码分析了,可能有很多理解不对的地方,希望研究过这方面的朋友多邮件交流一下。

MMC 子系统的代码在kernel/driver/MMC 下,目前的MMC 子系统支持一些形式的记忆卡:SD,SDIO,MMC. 由于笔者对SDIO 的规范不是很清楚,后面的分析中不会涉及。MMC 子系统范围三个部分:

HOST 部分是针对不同主机的驱动程序,这一部是驱动程序工程师需要根据自己的特点平台来完成的。

CORE 部分: 这是整个MMC 的核心存,这部分完成了不同协议和规范的实现,并为HOST 层的驱动提供了接口函数。

CARD 部分:因为这些记忆卡都是块设备,当然需要提供块设备的驱动程序,这部分就是实现了将你的SD 卡如何实现为块设备的。

3.HOST 层分析:

HOST 层实现的就是我们针对特定主机的驱动程序,这里以mini2440 的s3cmci.c 为例子进行分析,我们先采用platform_driver_register(&s3cmci_2440_driver) 注册了一个平台设备,接下来重点关注probe 函数。在这个函数总,我们与CORE 的联系是通过下面三句实现的。首先分配一个mmc_host 结构体,注意sizeof(struct s3cmci_host) ,这样就能在mmc_host 中找到了s3cmci_host ,嵌入结构和被嵌入的结构体能够找到对方在Linux 内核代码中的常用技术了。接下来为mmc->pos 赋值, s3cmci_ops 结构实现了几个很重要的函数,待会我一一介绍。中间还对mmc 结构的很多成员进行了赋值,最后将mmc 结构加入到MMC 子系统,mmc_alloc_host ,以及mmc_add_host 的具体做了什么事情,我们在下节再分析,这三句是些MMC 层驱动必须包含的。

mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);

mmc->ops = &s3cmci_ops;

……………

s3cmci_ops 中包含了四个函数:

static struct mmc_host_ops s3cmci_ops = {

       .request  = s3cmci_request,

       .set_ios   = s3cmci_set_ios,

       .get_ro          = s3cmci_get_ro,

       .get_cd          = s3cmci_card_present,

};

我们从简单的开始分析 , 这些函数都会在 core 部分被调用:

s3cmci_get_ro:这个函数通过从 GPIO读取,来判断我们的卡是否是写保护的

s3cmci_card_present: 这个函数通过从 GPIO读取来判断卡是否存在

s3cmci_set_ios: s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)

依据核心层传递过来的 ios,来设置硬件 IO,包括引脚配置,使能时钟,和配置总线带宽。

s3cmci_request: 这个 函数是最主要,也最复杂的函数,实现了命令和数据的发送和接收,

当 CORE部分需要发送命令或者传输数据时,都会调用这个函数,并传递 mrq请求。

static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)

{

struct s3cmci_host *host = mmc_priv(mmc);

host->status = "mmc request";

host->cmd_is_stop = 0;

host->mrq = mrq;

if (s3cmci_card_present(mmc) == 0) {

dbg(host, dbg_err, "%s: no medium present/n", __func__);

host->mrq->cmd->error = -ENOMEDIUM;

mmc_request_done(mmc, mrq);//如果卡不存在,就终止请求

} else

s3cmci_send_request(mmc);

}

接下来看 s3cmci_send_request(mmc):

这个函数先判断一下请求时传输数据还是命令, 如果是数据的话:

先调用 s3cmci_setup_data来对 S3C2410_SDIDCON寄存器进行设置,然后设置 SDITIMER寄存器这就设置好了总线宽度,是否使用 DMA,,并启动了数据传输模式,并且使能了下面这些中断:

imsk = S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC |

S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH;

解析来判断是否是采用 DMA进行数据传输还是采用 FIFO进行数据传输

if (host->dodma)

/      because host->dodma           = 0,so we don't use it

res = s3cmci_prepare_dma(host, cmd->data);// 准备 DMA传输,

else

res = s3cmci_prepare_pio(host, cmd->data);.// 准备 FIFO传输

如果是命令的话: 则调用 s3cmci_send_command()这个函数是命令发送的函数,和 datesheet上描述的过程差不多 ,关于 SD规范中命令的格式,请参考参考资料 1.

writel(cmd->arg, host->base + S3C2410_SDICMDARG);/*先写参数寄存器

ccon  = cmd->opcode & S3C2410_SDICMDCON_INDEX;//确定命令种类

ccon |= S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART;

/*with start 2bits*/

if (cmd->flags & MMC_RSP_PRESENT)

ccon |= S3C2410_SDICMDCON_WAITRSP;

/*wait rsp*/

if (cmd->flags & MMC_RSP_136)

ccon |= S3C2410_SDICMDCON_LONGRSP;

//确定 respose的种类

writel(ccon, host->base + S3C2410_SDICMDCON);

命令通道分析完了,我们分析数据通道,先分析采用 FIFO方式传输是怎么样实现的。

先分析 s3cmci_prepare_pio(host, cmd->data)

根据 rw来判断是读还是写

if (rw) {

do_pio_write(host);

/* Determines SDI generate an interrupt if Tx FIFO fills half*/

enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);

} else {

enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF

| S3C2410_SDIIMSK_RXFIFOLAST);

}

如果是写数据到 SD的话,会调用 do_pio_write,往 FIFO中填充数据。当 64字节的 FIFO少于 33字节时就会产生中断。如果是从 SD读数据,则先使能中断,当 FIFO多于 31字节时时,则会调用中断服务程序,中断服务程序中将会调用 do_pio_read FIFO的数据读出。

接下来分析 do_pio_write:

to_ptr = host->base + host->sdidata;

fifo_free(host)用来检测 fifo剩余空间

while ((fifo = fifo_free(host)) > 3) {

if (!host->pio_bytes) {

res = get_data_buffer(host, &host->pio_bytes,

/* If we have reached the end of the block, we have to

* write exactly the remaining number of bytes.  If we

* in the middle of the block, we have to write full

* words, so round down to an even multiple of 4. */

if (fifo >= host->pio_bytes)//fifo的空间比 pio_bytes大,表明这是读这个块的最后一次

fifo = host->pio_bytes;

/* because the volume of FIFO can contain the remaning block*/

else

fifo -= fifo & 3;/*round down to an even multiple of 4*/

host->pio_bytes -= fifo;//更新还剩余的没有写完的字

host->pio_count += fifo;/*chang the value of pio_bytes*/

fifo = (fifo + 3) >> 2;//将字节数转化为字数

/*how many words fifo contain,every time we just writ one word*/

ptr = host->pio_ptr;

while (fifo--)

writel(*ptr++, to_ptr);//写往 FIFO.

host->pio_ptr = ptr;

}

注释一:注意, MMC核心为 mrq->data成员分配了一个 struct scatterlist的表,用来支持分散聚集,使用这种方法,这样使物理上不一致的内存页,被组装成一个连续的数组,避免了分配大的缓冲区的问题

我们看代码

if (host->pio_sgptr >= host->mrq->data->sg_len) {

dbg(host, dbg_debug, "no more buffers (%i/%i)/n",

host->pio_sgptr, host->mrq->data->sg_len);

return -EBUSY;

}

sg = &host->mrq->data->sg[host->pio_sgptr];

*bytes = sg->length;//页缓冲区中的长度

* pointer = sg_virt(sg); 将页地址映射为虚拟地址

host->pio_sgptr++;这里表明我们的程序又完成了一次映射

这样,每一个 mmc请求,我们只能处理 scatterlist表中的一个页(块)。因此,完成一次完整的请求需要映射 sg_len次

再来总结一下一个 mmc写设备请求的过程:

在 s3cmci_prepare_pio中我们第一次先调用 do_pio_write,如果 FIFO空间大于 3,且能够获取到 scatterlist,则我们就开始往 FIFO写数据,当 FIFO空间小于 3,则使能 TXFIFOHALF中断,在中断服务程序中,如果检测到 TFDET表明又有 FIFO空间了,则关闭 TXFIFOHALF中断,并调用 do_pio_write进行写。

数据流向如下: scatterlist-------->fifo---------->sdcard

一个 mmc读设备请求的过程 数据流向如下 : sdcard --------> fifo ---------->scatterlist,

????关于读数据的过程,中断的触发不是很清楚, s3cmci_prepare_pio中 enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF, S3C2410_SDIIMSK_RXFIFOLAST);但如果没从 SD卡中读数据,怎么会引发这个中断呢?是由 S3C2410_SDIIMSK_RXFIFOLAST引起的吗

接下来我们分析一下中断服务程序:

static irqreturn_t s3cmci_irq(int irq, void *dev_id)

该程序先获取所有的状态寄存器:

mci_csta = readl(host->base + S3C2410_SDICMDSTAT);

mci_dsta = readl(host->base + S3C2410_SDIDSTA);

mci_dcnt = readl(host->base + S3C2410_SDIDCNT);

mci_fsta = readl(host->base + S3C2410_SDIFSTA);

mci_imsk = readl(host->base + host->sdiimsk);

这些将作为中断处理的依据。

如果不是 DMA模式,则处理数据的收发

if (!host->dodma) {

if ((host->pio_active == XFER_WRITE) &&

(mci_fsta & S3C2410_SDIFSTA_TFDET)) {

/*This bit indicates that FIFO data is available for transmit when

DatMode is data transmit mode. If DMA mode is enable, sd

host requests DMA operation.*/

disable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);

tasklet_schedule(&host->pio_tasklet);

注意我们采用 tasklet这种延时机制来减少中断服务的时间,延时函数 pio_tasklet中调用了 do_pio_write和 了 do_pio_read

host->status = "pio tx";

}

if ((host->pio_active == XFER_READ) &&

(mci_fsta & S3C2410_SDIFSTA_RFDET)) {

disable_imask(host,

S3C2410_SDIIMSK_RXFIFOHALF |

S3C2410_SDIIMSK_RXFIFOLAST);

tasklet_schedule(&host->pio_tasklet);

host->status = "pio rx";

}

接下来的很多代码是对其他的一些类型中断的处理。

最后来分析 DMA模式:这种模式下不需要 CPU的干预。 S3C2440的 DMA有 4个通道,我们选择了通道 0

static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)

{

int dma_len, i;

int rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0;

BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR);

s3cmci_dma_setup(host, rw ? S3C2410_DMASRC_MEM : S3C2410_DMASRC_HW); // 注一

s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);

dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,

(rw) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); // 注二

if (dma_len == 0)

return -ENOMEM;

host->dma_complete = 0;

host->dmatogo = dma_len;

for (i = 0; i < dma_len; i++) {

int res;

dbg(host, dbg_dma, "enqueue %i:%u@%u/n", i,

sg_dma_address(&data->sg[i]),

sg_dma_len(&data->sg[i]));

res = s3c2410_dma_enqueue(host->dma, (void *) host,

sg_dma_address(&data->sg[i]),

sg_dma_len(&data->sg[i]));

if (res) {

s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);

return -EBUSY;

}

}

s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_START);

return 0;

}

注一 :这个函数先调用 s3c2410_dma_devconfig来配置 DMA 源 /目的的意见类型和地址,注意我们这里的设备地址 host->mem->start + host->sdidata实际上就是 SDIDATA寄存器的地址值,如果是写 SD卡,则为目的地址,否则为源地址。然后调用 s3c2410_dma_set_buffdone_fn(host->dma, s3cmci_dma_done_callback);

设置 dma通道 0的回调函数。

注二:

dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,

(rw) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);

这里进行分散 /聚集映射( P444,LDD3) ,返回值是传送的 DMA缓冲区数,可能会小于 sg_len,也就是说 sg_len与 dma_len可能是不同的。

sg_dma_address(&data->sg[i]),返回的是总线( DMA)地址

sg_dma_len(&data->sg[i])); 返回的是缓冲区的长度。

最后调用 s3c2410_dma_enqueue(host->dma, (void *) host,

sg_dma_address(&data->sg[i]),

sg_dma_len(&data->sg[i]));

对每个 DMA缓冲区进行排队,等待处理。

s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_START);启动 DMA

这样 DMA缓冲区就和 scatterlist联系起来,当写数据时, scatterlist中的数据由于上面的映射关系会直接“拷贝”到 DMA缓冲区,当读数据时则反之。整个过程不需要 CPU干预,自动完成。

以上就是针对 mini2440 HOST部分的内容。

 

4 、 CORE 层分析:

CORE 层完成了不同协议和规范的实现,并为 HOST 层的驱动提供了接口函数,在 HOST 层我们曾经调用的两个函数:

mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);

mmc_add_host(mmc);

我们就从这两个函数入手,来分析 CORE层与 HOST层是如何交互的。

先看 mmc_alloc_host函数:

    dev_set_name(&host->class_dev, "mmc%d", host->index);

         host->parent = dev;

         host->class_dev.parent = dev;

         host->class_dev.class = &mmc_host_class;

         device_initialize(&host->class_dev);

这几句是将导致在 /SYS/CLASS/mmc_host下出现 mmc0目录,添加类设备,在 2.6.21后的版本中,类设备的 class_device已近被 device所取代, LDD3P387的内容有点 OUT了

       INIT_DELAYED_WORK(&host->detect, mmc_rescan);

初始化了一个工作队列,延时函数为 mmc_rescan,这个延时函数很重要,下午要详细分析

最后对 host做一些默认配置,不过这些配置在 probe函数的后面都被重置了。

分析 mmc_add_host(mmc);

device_add(&host->class_dev);这里才真正的添加了类设备。

其中调用了 mmc_start_host

void mmc_start_host(struct mmc_host *host)

{

mmc_power_off(host);

mmc_detect_change(host, 0);

}

mmc_power_off中对 ios进行了设置,然后调用 mmc_set_ios(host);

host->ios.power_mode = MMC_POWER_OFF;

host->ios.bus_width = MMC_BUS_WIDTH_1;

host->ios.timing = MMC_TIMING_LEGACY;

mmc_set_ios(host);

mmc_set_ios(host)中的关键语句 host->ops->set_ios(host, ios);这里的 set_ios实际上就是我们前面所提到的 .set_ios  = s3cmci_set_ios,

再看 mmc_detect_change(host, 0);最后一句是

       mmc_schedule_delayed_work(&host->detect, delay);

实际上就是调用我们前面说的延时函数 mmc_rescan

mmc_power_up(host);//这个函数实际上与前面的 mmc_power_off类似,不过设置了启动时需要的 ios

mmc_go_idle(host);

//CMD0 , from inactive to idle

mmc_send_if_cond(host, host->ocr_avail);//发送 SD_SEND_IF_COND,是使用 SD2.0卡才需要设置的命令

/*suppot for 2.0 card*/

* ...then normal SD...

*/

err = mmc_send_app_op_cond(host, 0, &ocr);

if (!err) {

if (mmc_attach_sd(host, ocr))

mmc_power_off(host);

goto out;

}

蓝色部分是遵照 SD卡协议的 SD卡启动过程,包括了非激活模式、卡识别模式和数据传输模式三种模式共九种状态的转换,你需要参照相关规范来理解。可以先参考下面三章图对模式和状态,以及状态转换有个初步了解。

我们最初的 SD卡的状态时 inactive状态调用 mmc_go_idle(host)后,发送命令 CMD0是其处于 IDLE状态。

我们详细分析一下 mmc_go_idle

memset(&cmd, 0, sizeof(struct mmc_command));

cmd.opcode = MMC_GO_IDLE_STATE; MMC_GO_IDLE_STATE就是命令 CMD0

         cmd.arg = 0;此命令无参数

         cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;

err = mmc_wait_for_cmd(host, &cmd, 0);//见注 1

mmc_delay(1);

注 1: mmc_wait_for_cmd(host, &cmd, 0)是用来发送命令的,我们揭开它的神秘面纱吧。

memset(&mrq, 0, sizeof(struct mmc_request));

         memset(cmd->resp, 0, sizeof(cmd->resp));

         cmd->retries = retries;

         mrq.cmd = cmd; 将命令嵌入到一个 mmc请求中

         cmd->data = NULL;mmc命令的 data部分设置为 NULL,这样表示我们要传输的是命令而不是数据

         mmc_wait_for_req(host, &mrq);//关键部分

在该函数中调用了mmc_start_request ,而这个函数调用了host->ops->request(host, mrq) ,这个request 函数就是我们在前面分析的s3cmci_request ,这样MMC 核心第二次核HOST 层握手了

 

我们再看看:        err = mmc_send_app_op_cond(host, 0, &ocr);//注一

if (!err) {

if (mmc_attach_sd(host, ocr))//注二

mmc_power_off(host);

goto out;

注一:实际上是要发送 ACMD41 命令,这条命令可以用来获取 SDcard 的允许电压范围值,由于这是一条应用命令,所有发送它之前需要发送 CMD_55 命令。执行完后 card 状态变为 READY 获取的电压范围保存在 ocr 中,再调用 mmc_attach_sd(host, ocr) 看这个电压范围是否满足主机的要求,不满足,则 power_off 主机。

注二: mmc_attach_sd 完成匹配,和初始化卡的功能

host->ocr = mmc_select_voltage(host, ocr); 看是否匹配,如果匹配则做下面初始化工作

mmc_sd_init_card(host, host->ocr, NULL); 我们分析该函数

( 1 ) mmc_all_send_cid ()这个函数发生 CMD2 ,获取卡的身份信息,进入到身份状态

(2)card = mmc_alloc_card(host, &sd_type); 分配一张 SD 类型的 card 结构

(3) 接着调用 mmc_send_relative_add, 获取卡的相对地址,注意一前卡和主机通信都采用默认地址,现在有了自己的地址了,进入到 stand_by 状态

( 4 )通过发送 SEND_CSD (CMD9) 获取 CSD 寄存器的信息,包括 block 长度,卡容量等信息

(5) mmc_select_card(card) 发送 CMD7, 选中目前 RADD 地址上的卡,任何时候总线上只有一张卡被选中,进入了传输状态

( 6 )调用 mmc_app_send_scr 发送命令 ACMD51 获取 SRC 寄存器的内容,进入到 SENDING-DATA 状态

在函数中还将获得的各个卡寄存器的内容解码,并保存到 cmd 结构的相应成员中。

( 7 ) if (host->ops->get_ro(host) > 0 )

mmc_card_set_readonly(card);

通过调用 get_ro(host)函数,实际上就是 s3cmci_get_ro函数了。 我们判断是否写保护,如果是的,将 card状态设置为只读状态

最后再 mmc_attach_sd里,我们将 card结构添加进去

mmc_add_card(host->card);

dev_set_name(&card->dev, "%s:%04x", mmc_hostname(card->host), card->rca);这里我们以 host名 +rca地址来命名卡我们可以看到在 /sys/devices/platform/s3c2440-sdi/mmc_host:mmc0/下出现 mmc0: 0002的目录,这个 0002就是 rca地址

到这里我们分析完了 MMC的核心层。

SD/MMC/SDIO 驱动分析相关推荐

  1. cmd52命令发送 mmc_Linux SD/MMC/SDIO驱动分析(新)

    一.SD/MMC/SDIO概念区分 SD(SecureDigital)与 MMC(MultimediaCard) SD 是一种 flash memory card 的标准,也就是一般常见的 SD 记忆 ...

  2. cmd52命令发送 mmc_乾坤合一~Linux SD/MMC/SDIO驱动分析(上)

    一.SD/MMC/SDIO概念区分 SD(SecureDigital)与 MMC(MultimediaCard) SD 是一种 flash memory card 的标准,也就是一般常见的 SD 记忆 ...

  3. cmd的发送 mmc_Linux SD/MMC/SDIO驱动分析

    一.SD/MMC/SDIO概念区分 SD(SecureDigital)与 MMC(MultimediaCard) SD 是一种 flash memory card 的标准,也就是一般常见的 SD 记忆 ...

  4. (linux)MMC 卡驱动分析

    最近花时间研究了一下 MMC 卡驱动程序,开始在网上找了很多关于 MMC 卡驱动的分析文章,但大都是在描述各个层,这对于初学者来讲帮助并不大,所以我就打算把自己的理解写下来,希望对大家有用.个人觉得理 ...

  5. cmd52命令发送 mmc_[MMC]Linux MMC/SD/SDIO驱动分析

    1.SD卡命令组成 SD卡的指令由6字节(Byte)组成,如下: Byte1:0 1 x x x x x x(命令号,由指令标志定义,如CMD39为100111即16进制0x27,那么完整的CMD39 ...

  6. Linux SD卡/SDIO驱动开发0-基本知识

    文章目录 Linux驱动子系统-sdio子系统 sdio系统概述 判断sd卡是否识别 判断sdio wifi是否识别 sd协议 SD BUS SPI BUS 硬件接口: 调试的问题 sdmmc接口使用 ...

  7. linux mmc分区_【经验记录】如何给嵌入式Linux的SD/MMC卡驱动中添加多个分区

    How to add multi partition for SD/MMC card in Linux Driver 之所以写这个,是因为,在这个过程中,自己明显感觉到了,做事情一定要有一定的方法,方 ...

  8. sd 卡驱动在2.6内核的编写.sd/mmc/sdio kernel,sd/mmc/sdio 内核

    [转帖请注明出处:blog.csdn.net/lanmanck] sd卡驱动主要参照已有的文件即可,2410,9260都挺好.其实写驱动主要是搞清楚工作流程即可.我这里写一些心得与大家分享下,基于2. ...

  9. Linux SD卡/SDIO驱动开发-dw_mci_probe

    以瑞芯微(rk)平台的代码解析,其他平台也类似,供其他同学参考学习. 参考:http://blog.chinaunix.net/uid-25445243-id-3885164.html int dw_ ...

最新文章

  1. insert into 多条数据_最全总结 | 聊聊 Python 数据处理全家桶(Sqlite篇)
  2. php动态引入js文件路径问题,JavaScript_动态加载外部css或js文件,原理解析:第一步:使用dom创 - phpStudy...
  3. 信息系统项目管理师需要准备多久?备考技巧分享
  4. spring boot aop 记录方法执行时间
  5. JAVA设计模式之【单例模式】
  6. Android ViewRoot、DecorViewWindow浅析
  7. 黑龙江对口学计算机上机,2009年黑龙江省职高对口升学计算机应用专业技能试卷十.doc...
  8. 终于有人把A/B测试讲明白了
  9. mysql数据库group by_MySQL数据库对GROUP BY子句的功能扩展(1)
  10. Vijos 1092 全排列
  11. 在python中用os模块实现批量移动文件
  12. 耳机插入电脑插孔后只有一边有声音
  13. ipa文件怎么安装到iPhone iPhone怎么安装ipa
  14. Windows+chrome中关闭自动更新
  15. Mysql占用CPU过高排查过程及可能优化方案
  16. Spring源码浅析之AOP、Aspect、Advice
  17. Dell服务器raid5扩容及相关技术mark
  18. 视频教程-微信小程序系统教程Java版[3/3阶段]_微信小程序电商系统-微信开发
  19. 如何打造一款游戏外挂
  20. 刘海洋《LaTex入门》学习笔记2

热门文章

  1. Centos7 无法停止mysql服务(郁闷了好半天)
  2. Ubuntu安装英伟达驱动CUDA,CUDNN,PyTorch保姆级教程
  3. 仿生算法_糖尿病患者:说出仿生胰腺很有趣,但是现实检查怎么样?
  4. 2019-2020 ICPC, NERC, Southern and Volga Russian Regional Contest J. The Parade(二分+贪心)
  5. Spring Data ElasticSearch 常用API调用
  6. MongoDB——牛X的索引操作
  7. whmcs不依赖index.php的方法,自定义WHMCS以获得无缝的用户体验
  8. canvas绘图色块区域+多边形区域色块显示功能
  9. GIS原理篇 TIN 不规则三角网
  10. OCR技术学习,智能文档图像处理技术应用与实践