linux sd卡驱动分析,基于mini2440,sdio mmc sd卡驱动编写

linux device driver之sdio驱动编程 先谈谈如何写linux驱动: - 在驱动模块初始化函数中调用register_chrdev(),将驱动向系统注册为一个字符设备,伪装成一个文件,上层的应用可以通过访问这个文件(字符设备),来操作驱动模块。 - 驱动模块注册为字符设备后,还需要用户在命令行中敲mknod命令来创建一个对应的字符文件,上层应用就是用open, close, read, write该文件这样的方式来访问驱动模块。 阅读详情
 

By JeefJiang July ,25th ,2009

  Email:Jeefjiang1983@163.com

                      ( 本文仅做学习交流用,转载或引用请标明)

参考资料:

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 的核心层。

 

三,Linux SDIO驱动框架之扫卡流程  本文以kernel-4.4,以MTK平台分析host端初始化的扫卡流程  mtk平台host驱动代码在drivers/mmc/host/mtk-sdio.c,sd card,mmc和sdio card的相关核心代码都存在drivers/mmc/目录中,本文只关心host是如果扫描到card的,下面分析host扫卡流程。 1,host驱动初始化  平台驱动代码中注册platform_driver,匹配到device调用到probe函数int msdc_drv_probe(struct platform_de 阅读详情

相关推荐

MINI2440的UCOS+SD卡驱动,基于Eclipse环境

工程由友善之臂提供的原始UCOS和裸机代码修改完善而来,环境为Eclipse前端+yagarto gcc,可以参照OPEN JTAG的环境搭建教程。工程中提供了基于OPEN JTAG和JLINK GDB sever的外部SRAM初始化脚本,可根据实际情况选择。

Linux 下wifi 驱动开发(三)—— SDIO接口WiFi驱动浅析

1sdio结构图定义以及初始化 2创建sdio工作队列 3申请host 4使能sdio功能 5申请sdio中断 6添加网络结构体分配设备并注册 7分配ethernet 8设置物理地址 9初始化ops 原文:http://blog.csdn.net/zqixiao_09/article/details/51105877 SDIO-Wifi模块是基于SDI...

849

Redis简介和项目应用

内存优先架构简介Redis的读写操作都是在内存中执行的,计算机内存的读写速度极快,比机械硬盘的读写快很多,因此也会比MySql采用机械读写的方式性能更高。但是这样会产生一个问题,当Redis重启之后,内存中的数据就会丢失,为了保证数据不会因为服务器重启而丢失,Redis实现了数据持久化的机制,这个机制会把数据存储到磁盘中,当服务器重启的时候,能够从磁盘中恢复原有的数据。

人工智能、机器学习 758

linux sdio device描述查看

1.sdio card插入以后,可以查看device信息。 存放在cd /sys/bus/sdio/

cat_lover的专栏 3120

【2024保研经验帖】中科院信工所夏令营经验分享

中科院信工所夏令营经验分享,信工所夏令营据说只要报了就能进,这个是不是真的尚不清楚,不过qq群里有八九百个人,也属实可怕。招生老师说我们这届夏令营招200-300个左右,于是又开始惶惶不安。 夏令营时期都是线上进行,先是每个科室进行线上招生宣讲,然后报名第一志愿部门和第二志愿部门,不同部门的考核是不同的。有的部门只要求英语面试和综合面试,有的部门除了面试还需要机试,而有的部门除了这些还需要笔试。

m0_63002183的博客 4489

Python爬虫实战:用Selenium+ChromeDriver批量抓取政府网站政策文件(附完整代码)

本文详细介绍了使用Python的Selenium和ChromeDriver进行政府网站政策文件批量抓取的实战技巧。从环境配置、动态页面元素定位到分页机制破解和反爬虫策略,提供了完整的解决方案和代码示例,帮助开发者高效获取结构化政策数据。

weixin_42531710的博客 265

Linux SDIO总线驱动

http://blog.sina.com.cn/s/blog_4b4b54da0102vivr.html Linux SDIO总线驱动 SDIO卡        SDIO卡是在SD内存卡接口的基础上发展起来的接口,SDIO接口兼容以前的SD内存卡,并且可以连接SDIO接口的设备,目前根据SDIO协议的SPEC,SDIO接口支持的设备总类有蓝牙,网卡,电视卡

one shot,one kill. 1438

CPU系统级验证——测试激励——imperas公司riscvOVPsimPlus文件分析

本分析重点关注该repo向量扩展部分。 1. repo简介 该repo是指令集验证的仿真环境包括,大部分是指令集的测试用例,也包含了一些模拟器,如riscvOVPsimPlus。 主要介绍的是riscvOVPsimPlus指令集模拟器,riscvOVPsimPlus(下面统称ISS)是imperas公司在riscvOVPsim基础上扩充了向量扩展的指令集模拟器,该repo于今年9月份部分开源 什么叫部分开源呢? 首先该repo必须在imperas官网注册下载,而且必须是企业用户(免费邮箱人家瞧不上.

知识,既贵,又便宜。 4459

linux sd卡驱动分析,基于mini2440,sdio mmc sd卡驱动

linux sd卡驱动分析,基于mini2440,sdio mmc sd卡驱动<br />1. 硬件基础:<br />SD/MMC/SDIO 概念区分概要<br />SD (Secure Digital )与 MMC (Multimedia Card )<br />SD 是一种 flash memory card 的标准,也就是一般常见的 SD 记忆卡,而 MMC 则是较早的一种记忆卡标准,目前已经被 SD 标准所取代。<br />SDIO 是目前我们比较关心的技术,SDIO 故名思义,就是 SD 的 I/

zhenwenxian的专栏 3152

AUTOSAR进阶图解==>AUTOSAR_EXP_VFB

AUTOSAR虚拟功能总线(VFB)摘要 AUTOSAR虚拟功能总线(VFB)是汽车开放系统架构中的核心抽象概念,为分布式电子系统提供统一通信模型。VFB允许软件组件在不了解底层物理通信机制的情况下交互,通过运行时环境(RTE)实现组件间的数据交换。系统配置过程将抽象的VFB视图转换为具体实现,包括组件到ECU的映射和通信机制选择。VFB采用"last-is-best"语义进行数据通信,并支持模式管理机制,使组件能根据系统状态调整行为。此外,VFB还支持与非AUTOSAR系统的交互,通常

一名留德华的,康德的信徒的,专注于汽车电子领域的博客 1117

【YOLO多模态检测顶刊】TGRS 2025范文解读,RGFNet 可见光-红外图像融合目标检测,2026年基于YOLO多模态融合属于发文热点,小论文和大论文通用,通读顶刊了解做实验和论文写作的全流程

🔥本文提出了一种反射率引导的渐进特征对齐网络(RGFNet)用于全天候无人机目标检测。该方法利用Retinex理论从可见光图像中提取光照不变的反射率特征,设计了反射率引导协同对齐模块(RCAM)实现跨模态双向特征对齐,并通过光照感知选择性融合模块(LSFM)在共享状态空间中进行高效特征交互。在DroneVehicle和DVTOD数据集上的实验表明,RGFNet分别达到81.4%和88.5%的mAP,显著优于现有方法。该方法有效解决了低光照条件下可见光-红外图像特征错位问题,为全天候无人机检测提供了新思路。

Ai缝合怪、助力小伙伴高效发论文 1077

linux sd卡驱动分析,基于mini2440,sdio mmc sd卡驱动编写(2),一些初始化流程

最近要让s3c2440在linux2.6.18上 支持4G的SD卡.原文地址:http://linux4u.wikidot.com/mmc-controller-driver==============================================================linux-2.6.2x的mmc驱动与linux-2.6.1x的mmc驱动

LYJ个人博客,记录技术、八卦、生活,分享各种心得,期待与朋友们一同进步 9229

问答式AI vs 智能体(Agent):它们有什么不同?

你肯定用过 ChatGPT 或文心一言——问它一个问题,它给你一个答案。AI Agent(AI智能体)。很多人搞不清楚:它们不都是"AI"吗?有什么区别?这篇文章用一个最简单的类比,彻底讲清楚这两者的本质差异。框架特点适合人群AutoGPT完全自主执行任务,链式思考开发者MetaGPT模拟团队协作,多角色分工开发者LangChain最流行的Agent开发框架开发者Dify可视化无代码搭建Agent非技术用户Coze(扣子)字节跳动出品,国内最易用普通用户WorkBuddy。

Johnny2004的专栏 446

linux中sd卡驱动(2)

linux sd卡驱动分析 1. 硬件基础: SD/MMC/SDIO 概念区分概要 SD (Secure Digital )与 MMC (Multimedia Card ) SD 是一种 flash memory card 的标准,也就是一般常见的 SD 记忆卡,而 MMC 则是较早的一种记忆卡标准,目前已经被 SD 标准所取代。 SDIO 是目前我们比较关心的技术,SDIO 故名思义,

iosxiaoming的博客 448

【NVMEM子系统】二、NVMEM驱动框架

该子系统整体架构不算太大,还是比较容易去理解的,下面我们一起去一探究竟!,该子系统主要用于实现EEPROMEfuse等非易失存储器的统一管理。在早期,像EEPROM驱动是存放于目录下,由于没有做到好的抽象,每次需要去访问相应内存空间,都需要去复制几乎一样的代码,去注册sysfs,这是一个相当大的抽象泄露。NVMEM子系统就是为了解决以往的抽象泄露问题。

Donge's Blog 1714

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Mini2440 SD卡驱动程序分析

 Mini2440 SD卡驱动程序分析参考资料:1.SD Memory Card Specifications / Part 1. Physical Layer Specification; Version 1.0  2.LDD3 CHAPTER-16 BLOCK DEVICE3. http://www.sdcard.org引言:前几天把mini2440的sd卡驱动程序移植到了

zhenwenxian的专栏 4198

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

linux SD卡驱动分析

1. 硬件基础: SD/MMC/SDIO 概念区分概要 SD (Secure Digital )与 MMC (Multimedia Card ) SD 是一种 flash memory card 的标准,也就是一般常见的 SD 记忆卡,而 MMC 则是较早的一种记忆卡标准,目前已经被 SD 标准所取代。  SDIO 是目前我们比较关心的技术,SDIO 故名思义,就是 SD

╰☆╮EvilCode的专栏╭☆╯ 4273

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

mini2440 sd卡支持

内核版本:linux-2.6.32.2        实验平台:mini2440

Tracy Mcgrady的专栏 2120

SD卡驱动参考blog

http://blog.csdn.net/lanmanck/archive/2009/12/03/4930089.aspx //SD卡驱动流程 http://hi.baidu.com/396954504/blog/item/99aaac0ecfe97fc77bcbe1d5.html //sd 卡驱动在2.6内核的编写.sd/mmc/sdio kernel,sd/mmc/sdio 内核 ht

ccx_john的专栏 699
上一篇: 楼市泡沫雪崩为何十倍“猛于”A股大跌
下一篇: linux sd卡驱动分析,基于mini2440,sdio mmc sd卡驱动编写(2),一些初始化流程
#Page#
博客等级 码龄23年 1691粉丝 360原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值