emmc/sd 核心层解析

来源:互联网 发布:手机淘宝如何上架宝贝 编辑:程序博客网 时间:2024/06/14 05:44

通过前一篇文章emmc/sd 区块层解析,我们大概知道了emmc块设备驱动层的内容,记得还有一些未解的问题。

不管怎样,我们还是从croe.c入手。

一、mmc_init

如果您看过emmc/sd驱动总线简析这篇文章,我估计您看到这个标题,应该会有印象,总线就是在这个函数里面加载到内核的。

现在我们关注的是核心层的东西,既然前面已经讲过总线的注册,那么我们就来分析下其它的部分。

上代码:

static int __init mmc_init(void){int ret;workqueue = alloc_ordered_workqueue("kmmcd", 0);if (!workqueue)return -ENOMEM;ret = mmc_register_bus();if (ret)goto destroy_workqueue;ret = mmc_register_host_class();if (ret)goto unregister_bus;ret = sdio_register_bus();if (ret)goto unregister_host_class;return 0;unregister_host_class:mmc_unregister_host_class();unregister_bus:mmc_unregister_bus();destroy_workqueue:destroy_workqueue(workqueue);return ret;}

第5行,创建了一个工作队列,主要用来处理中断的下半部工作,在《linux内核设计与实现》的8.4节有详细的介绍。
第9--12行,注册总线,之前已经讲过;
第13--16行,向注册了一个名为“mmc_host”的类,暂时没搞懂。
第17--19行,注册了一个sdio的东西,sdio是sd的I/O扩展,可以接外围设备。
后面的就是相关错误处理。

二、解答在card模块中的疑问

欠债是要还的,我们来还一下在emmc/sd 区块层解析中欠下的债。
2.1、mmc_claim_host

/** *mmc_claim_host - exclusively claim a host *@host: mmc host to claim * *Claim a host for a set of operations. */static inline void mmc_claim_host(struct mmc_host *host){__mmc_claim_host(host, NULL);}
通过注释可以知道,该函数为操作获取一个host。再来看__mmc_claim_host

int __mmc_claim_host(struct mmc_host *host, atomic_t *abort){DECLARE_WAITQUEUE(wait, current);unsigned long flags;int stop;might_sleep();add_wait_queue(&host->wq, &wait);spin_lock_irqsave(&host->lock, flags);while (1) {set_current_state(TASK_UNINTERRUPTIBLE);stop = abort ? atomic_read(abort) : 0;if (stop || !host->claimed || host->claimer == current)break;spin_unlock_irqrestore(&host->lock, flags);schedule();spin_lock_irqsave(&host->lock, flags);}set_current_state(TASK_RUNNING);if (!stop) {host->claimed = 1;host->claimer = current;host->claim_cnt += 1;} elsewake_up(&host->wq);spin_unlock_irqrestore(&host->lock, flags);remove_wait_queue(&host->wq, &wait);if (host->ops->enable && !stop && host->claim_cnt == 1)host->ops->enable(host);return stop;}

第3行:内核宏,在当前进程创建一个等待队列;
第7行:内核函数,是否需要休眠;
第9行:将host的请求加入等待队列;
第12行:设置当前进程等待队列不可以被信号唤醒,只可以背wake_up唤醒;
第14--18行:判断当前进程是否休眠
由mmc_claim_host才进来的参数可知,abort为NULL,所以stop为0
host的claimed表示host是否被占用
host的claimer表示被占用的进程
由此可知,如果host没被占用,或者被本进程占用就会跳出while循环,否则调用内核函数schedule,设置本进程休眠;
第20行:设置进程为运行态;
第21行到24行设置当前host被占用,且是被本进程占用;
如果不执行第21行的if,则进入第26行;
第26行:唤醒本进程的等待队列;
第28行:干掉等待队列;
第29--30行:如果当前host可用,且只被当前进程占用,则使能host;
host->ops->enable(host);具体是干什么呢?看一下ops是什么?
ops定义在mmc_host中:const struct mmc_host_ops *ops;
那么mmc_host_ops 是什么?

struct mmc_host_ops {/* * 'enable' is called when the host is claimed and 'disable' is called * when the host is released. 'enable' and 'disable' are deprecated. */int (*enable)(struct mmc_host *host);int (*disable)(struct mmc_host *host);/* * It is optional for the host to implement pre_req and post_req in * order to support double buffering of requests (prepare one * request while another request is active). * pre_req() must always be followed by a post_req(). * To undo a call made to pre_req(), call post_req() with * a nonzero err condition. */void(*post_req)(struct mmc_host *host, struct mmc_request *req,    int err);void(*pre_req)(struct mmc_host *host, struct mmc_request *req,   bool is_first_req);void(*request)(struct mmc_host *host, struct mmc_request *req);/* * Avoid calling these three functions too often or in a "fast path", * since underlaying controller might implement them in an expensive * and/or slow way. * * Also note that these functions might sleep, so don't call them * in the atomic contexts! * * Return values for the get_ro callback should be: *   0 for a read/write card *   1 for a read-only card *   -ENOSYS when not supported (equal to NULL callback) *   or a negative errno value when something bad happened * * Return values for the get_cd callback should be: *   0 for a absent card *   1 for a present card *   -ENOSYS when not supported (equal to NULL callback) *   or a negative errno value when something bad happened */void(*set_ios)(struct mmc_host *host, struct mmc_ios *ios);int(*get_ro)(struct mmc_host *host);int(*get_cd)(struct mmc_host *host);void(*enable_sdio_irq)(struct mmc_host *host, int enable);/* optional callback for HC quirks */void(*init_card)(struct mmc_host *host, struct mmc_card *card);int(*start_signal_voltage_switch)(struct mmc_host *host, struct mmc_ios *ios);/* Check if the card is pulling dat[0:3] low */int(*card_busy)(struct mmc_host *host);/* The tuning command opcode value is different for SD and eMMC cards */int(*execute_tuning)(struct mmc_host *host, u32 opcode);int(*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv);void(*hw_reset)(struct mmc_host *host);void(*card_event)(struct mmc_host *host);};
根据名称和里面的内容,估计就是host的处理函数。先不谈这个,在讲host的时候会知道每个具体的函数指针所指向的处理函数。
又欠一份债:1@mmc_host的ops的具体实现
mmc_claim_host分析完毕。

2.2、mmc_release_host

/** *mmc_release_host - release a host *@host: mmc host to release * *Release a MMC host, allowing others to claim the host *for their operations. */void mmc_release_host(struct mmc_host *host){unsigned long flags;WARN_ON(!host->claimed);if (host->ops->disable && host->claim_cnt == 1)host->ops->disable(host);spin_lock_irqsave(&host->lock, flags);if (--host->claim_cnt) {/* Release for nested claim */spin_unlock_irqrestore(&host->lock, flags);} else {host->claimed = 0;host->claimer = NULL;spin_unlock_irqrestore(&host->lock, flags);wake_up(&host->wq);}}
这个是和mmc_claim_host反着来的。

2.3、mmc_erase

/** * mmc_erase - erase sectors. * @card: card to erase * @from: first sector to erase * @nr: number of sectors to erase * @arg: erase command argument (SD supports only %MMC_ERASE_ARG) * * Caller must claim host before calling this function. */int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,      unsigned int arg){unsigned int rem, to = from + nr;if (!(card->host->caps & MMC_CAP_ERASE) ||    !(card->csd.cmdclass & CCC_ERASE))return -EOPNOTSUPP;if (!card->erase_size)return -EOPNOTSUPP;if (mmc_card_sd(card) && arg != MMC_ERASE_ARG)return -EOPNOTSUPP;if ((arg & MMC_SECURE_ARGS) &&    !(card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN))return -EOPNOTSUPP;if ((arg & MMC_TRIM_ARGS) &&    !(card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN))return -EOPNOTSUPP;if (arg == MMC_SECURE_ERASE_ARG) {if (from % card->erase_size || nr % card->erase_size)return -EINVAL;}if (arg == MMC_ERASE_ARG) {rem = from % card->erase_size;if (rem) {rem = card->erase_size - rem;from += rem;if (nr > rem)nr -= rem;elsereturn 0;}rem = nr % card->erase_size;if (rem)nr -= rem;}if (nr == 0)return 0;to = from + nr;if (to <= from)return -EINVAL;/* 'from' and 'to' are inclusive */to -= 1;return mmc_do_erase(card, from, to, arg);}

功能:通过注释可以知道,该函数根据参数完成指定位置指定大小的擦除,或者根据arg命令进行操作。

第15--17行:判断是否支持容量擦除和命令擦除,关于MMC_CAP_ERASE,在代码中找到以下宏,具体什么意思看注释,再深层次的我暂时解释不了

#define MMC_CAP_4_BIT_DATA(1 << 0)/* Can the host do 4 bit transfers */#define MMC_CAP_MMC_HIGHSPEED(1 << 1)/* Can do MMC high-speed timing */#define MMC_CAP_SD_HIGHSPEED(1 << 2)/* Can do SD high-speed timing */#define MMC_CAP_SDIO_IRQ(1 << 3)/* Can signal pending SDIO IRQs */#define MMC_CAP_SPI(1 << 4)/* Talks only SPI protocols */#define MMC_CAP_NEEDS_POLL(1 << 5)/* Needs polling for card-detection */#define MMC_CAP_8_BIT_DATA(1 << 6)/* Can the host do 8 bit transfers */#define MMC_CAP_AGGRESSIVE_PM(1 << 7)/* Suspend (e)MMC/SD at idle  */#define MMC_CAP_NONREMOVABLE(1 << 8)/* Nonremovable e.g. eMMC */#define MMC_CAP_WAIT_WHILE_BUSY(1 << 9)/* Waits while card is busy */#define MMC_CAP_ERASE(1 << 10)/* Allow erase/trim commands */#define MMC_CAP_1_8V_DDR(1 << 11)/* can support *//* DDR mode at 1.8V */#define MMC_CAP_1_2V_DDR(1 << 12)/* can support *//* DDR mode at 1.2V */#define MMC_CAP_POWER_OFF_CARD(1 << 13)/* Can power off after boot */#define MMC_CAP_BUS_WIDTH_TEST(1 << 14)/* CMD14/CMD19 bus width ok */#define MMC_CAP_UHS_SDR12(1 << 15)/* Host supports UHS SDR12 mode */#define MMC_CAP_UHS_SDR25(1 << 16)/* Host supports UHS SDR25 mode */#define MMC_CAP_UHS_SDR50(1 << 17)/* Host supports UHS SDR50 mode */#define MMC_CAP_UHS_SDR104(1 << 18)/* Host supports UHS SDR104 mode */#define MMC_CAP_UHS_DDR50(1 << 19)/* Host supports UHS DDR50 mode */#define MMC_CAP_RUNTIME_RESUME(1 << 20)/* Resume at runtime_resume. */#define MMC_CAP_DRIVER_TYPE_A(1 << 23)/* Host supports Driver Type A */#define MMC_CAP_DRIVER_TYPE_C(1 << 24)/* Host supports Driver Type C */#define MMC_CAP_DRIVER_TYPE_D(1 << 25)/* Host supports Driver Type D */#define MMC_CAP_CMD23(1 << 30)/* CMD23 supported. */#define MMC_CAP_HW_RESET(1 << 31)/* Hardware reset */u32caps2;/* More host capabilities */#define MMC_CAP2_BOOTPART_NOACC(1 << 0)/* Boot partition no access */#define MMC_CAP2_CACHE_CTRL(1 << 1)/* Allow cache control */#define MMC_CAP2_FULL_PWR_CYCLE(1 << 2)/* Can do full power cycle */#define MMC_CAP2_NO_MULTI_READ(1 << 3)/* Multiblock reads don't work */#define MMC_CAP2_NO_SLEEP_CMD(1 << 4)/* Don't allow sleep command */#define MMC_CAP2_HS200_1_8V_SDR(1 << 5)        /* can support */#define MMC_CAP2_HS200_1_2V_SDR(1 << 6)        /* can support */#define MMC_CAP2_HS200(MMC_CAP2_HS200_1_8V_SDR | \ MMC_CAP2_HS200_1_2V_SDR)#define MMC_CAP2_BROKEN_VOLTAGE(1 << 7)/* Use the broken voltage */#define MMC_CAP2_HC_ERASE_SZ(1 << 9)/* High-capacity erase size */#define MMC_CAP2_CD_ACTIVE_HIGH(1 << 10)/* Card-detect signal active high */#define MMC_CAP2_RO_ACTIVE_HIGH(1 << 11)/* Write-protect signal active high */#define MMC_CAP2_PACKED_RD(1 << 12)/* Allow packed read */#define MMC_CAP2_PACKED_WR(1 << 13)/* Allow packed write */#define MMC_CAP2_PACKED_CMD(MMC_CAP2_PACKED_RD | \ MMC_CAP2_PACKED_WR)#define MMC_CAP2_NO_PRESCAN_POWERUP (1 << 14)/* Don't power up before scan */#define MMC_CAP2_SANITIZE(1 << 15)/* Support Sanitize */
而关于CCC_相关的命令,也有一些宏:

/* * Card Command Classes (CCC) */#define CCC_BASIC(1<<0)/* (0) Basic protocol functions *//* (CMD0,1,2,3,4,7,9,10,12,13,15) *//* (and for SPI, CMD58,59) */#define CCC_STREAM_READ(1<<1)/* (1) Stream read commands *//* (CMD11) */#define CCC_BLOCK_READ(1<<2)/* (2) Block read commands *//* (CMD16,17,18) */#define CCC_STREAM_WRITE(1<<3)/* (3) Stream write commands *//* (CMD20) */#define CCC_BLOCK_WRITE(1<<4)/* (4) Block write commands *//* (CMD16,24,25,26,27) */#define CCC_ERASE(1<<5)/* (5) Ability to erase blocks *//* (CMD32,33,34,35,36,37,38,39) */#define CCC_WRITE_PROT(1<<6)/* (6) Able to write protect blocks *//* (CMD28,29,30) */#define CCC_LOCK_CARD(1<<7)/* (7) Able to lock down card *//* (CMD16,CMD42) */#define CCC_APP_SPEC(1<<8)/* (8) Application specific *//* (CMD55,56,57,ACMD*) */#define CCC_IO_MODE(1<<9)/* (9) I/O mode *//* (CMD5,39,40,52,53) */#define CCC_SWITCH(1<<10)/* (10) High speed switch *//* (CMD6,34,35,36,37,50) *//* (11) Reserved *//* (CMD?) */
再回到mmc_earse:
第19--20行:如果card配置的每次擦除的sector数为0,直接返回;
第22--23行:前面函数在解释arg时,已经说明,如果card为sd卡,则arg必须为MMC_ERASE_ARG
第25--36行:检查参数和card的配置是否匹配;
第38--51行:如果是MMC_ERASE_ARG,则特殊处理,下面举个例子:
当from=2,card->erase_size=3,nr=4时,此时擦除第3,4,5个索引的sector;
第58--59行:还是上面的参数,则擦除索引为2,3,4,5的sector。
第62行,表示索引从0开始,我们上面的操作已经是这么算的。
第64行,执行擦除,咱们来看下源码:

static int mmc_do_erase(struct mmc_card *card, unsigned int from,unsigned int to, unsigned int arg){struct mmc_command cmd = {0};unsigned int qty = 0;unsigned long timeout;int err;/* * qty is used to calculate the erase timeout which depends on how many * erase groups (or allocation units in SD terminology) are affected. * We count erasing part of an erase group as one erase group. * For SD, the allocation units are always a power of 2.  For MMC, the * erase group size is almost certainly also power of 2, but it does not * seem to insist on that in the JEDEC standard, so we fall back to * division in that case.  SD may not specify an allocation unit size, * in which case the timeout is based on the number of write blocks. * * Note that the timeout for secure trim 2 will only be correct if the * number of erase groups specified is the same as the total of all * preceding secure trim 1 commands.  Since the power may have been * lost since the secure trim 1 commands occurred, it is generally * impossible to calculate the secure trim 2 timeout correctly. */if (card->erase_shift)qty += ((to >> card->erase_shift) -(from >> card->erase_shift)) + 1;else if (mmc_card_sd(card))qty += to - from + 1;elseqty += ((to / card->erase_size) -(from / card->erase_size)) + 1;if (!mmc_card_blockaddr(card)) {from <<= 9;to <<= 9;}if (mmc_card_sd(card))cmd.opcode = SD_ERASE_WR_BLK_START;elsecmd.opcode = MMC_ERASE_GROUP_START;cmd.arg = from;cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;err = mmc_wait_for_cmd(card->host, &cmd, 0);if (err) {pr_err("mmc_erase: group start error %d, "       "status %#x\n", err, cmd.resp[0]);err = -EIO;goto out;}memset(&cmd, 0, sizeof(struct mmc_command));if (mmc_card_sd(card))cmd.opcode = SD_ERASE_WR_BLK_END;elsecmd.opcode = MMC_ERASE_GROUP_END;cmd.arg = to;cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;err = mmc_wait_for_cmd(card->host, &cmd, 0);if (err) {pr_err("mmc_erase: group end error %d, status %#x\n",       err, cmd.resp[0]);err = -EIO;goto out;}memset(&cmd, 0, sizeof(struct mmc_command));cmd.opcode = MMC_ERASE;cmd.arg = arg;cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;cmd.cmd_timeout_ms = mmc_erase_timeout(card, arg, qty);err = mmc_wait_for_cmd(card->host, &cmd, 0);if (err) {pr_err("mmc_erase: erase error %d, status %#x\n",       err, cmd.resp[0]);err = -EIO;goto out;}if (mmc_host_is_spi(card->host))goto out;timeout = jiffies + msecs_to_jiffies(MMC_CORE_TIMEOUT_MS);do {memset(&cmd, 0, sizeof(struct mmc_command));cmd.opcode = MMC_SEND_STATUS;cmd.arg = card->rca << 16;cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;/* Do not retry else we can't see errors */err = mmc_wait_for_cmd(card->host, &cmd, 0);if (err || (cmd.resp[0] & 0xFDF92000)) {pr_err("error %d requesting status %#x\n",err, cmd.resp[0]);err = -EIO;goto out;}/* Timeout if the device never becomes ready for data and * never leaves the program state. */if (time_after(jiffies, timeout)) {pr_err("%s: Card stuck in programming state! %s\n",mmc_hostname(card->host), __func__);err =  -EIO;goto out;}} while (!(cmd.resp[0] & R1_READY_FOR_DATA) || (R1_CURRENT_STATE(cmd.resp[0]) == R1_STATE_PRG));out:return err;}

第25行前的注释,就是为了解释第25行--37行的代码,计算擦除超时时间;
第39行--42行:根据是否为sd卡来选择不同的命令,该命令用于设置擦除的起始块地址;
第43行--51行:设置擦除的起始块地址,这里面有用到一个函数:mmc_wait_for_cmd,毋庸置疑这个函数很重要,我们等下再看;
第52行--67行:设置擦除的结束块地址,这里也用到了这个函数:mmc_wait_for_cmd;
第68行--79行:擦除设定范围的块;用到函数mmc_wait_for_cmd;
第81行--82行:如果host是spi(串行外围接口),则直接返回;
第84行:计算超时时间;
第86行--97行:获取擦除操作的状态,用到函数mmc_wait_for_cmd;
第99--107行:超时处理;
mmc_do_erase函数分析完毕。

上面有个mmc_wait_for_cmd函数用到了四次,现在我们来看一下实现:

2.3.1、mmc_wait_for_cmd

/** *mmc_wait_for_cmd - start a command and wait for completion *@host: MMC host to start command *@cmd: MMC command to start *@retries: maximum number of retries * *Start a new MMC command for a host, and wait for the command *to complete.  Return any error that occurred while the command *was executing.  Do not attempt to parse the response. */int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries){struct mmc_request mrq = {NULL};WARN_ON(!host->claimed);memset(cmd->resp, 0, sizeof(cmd->resp));cmd->retries = retries;mrq.cmd = cmd;cmd->data = NULL;mmc_wait_for_req(host, &mrq);return cmd->error;}
函数很短,大概完成什么功能在注释中已经表达出来了,但是实际怎么做的又封装到了mmc_wait_for_req函数中:

/** *mmc_wait_for_req - start a request and wait for completion *@host: MMC host to start command *@mrq: MMC request to start * *Start a new MMC custom command request for a host, and wait *for the command to complete. Does not attempt to parse the *response. */void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq){__mmc_start_req(host, mrq);mmc_wait_for_req_done(host, mrq);}
函数还是很短,里面又有封装两个函数,先来看下第一个:

static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq){init_completion(&mrq->completion);mrq->done = mmc_wait_done;if (mmc_card_removed(host->card)) {mrq->cmd->error = -ENOMEDIUM;complete(&mrq->completion);return -ENOMEDIUM;}mmc_start_request(host, mrq);return 0;}

第3行:用到了linux内核同步机制中的完成变量,这里初始化了一个完成变量;
第4行:将设置等待的操作传递给done函数指针;
第5--9行:如果卡被移除,则返回错误;
第10行:发送请求命令:

static voidmmc_start_request(struct mmc_host *host, struct mmc_request *mrq){#ifdef CONFIG_MMC_DEBUGunsigned int i, sz;struct scatterlist *sg;#endifif (mrq->sbc) {pr_debug("<%s: starting CMD%u arg %08x flags %08x>\n", mmc_hostname(host), mrq->sbc->opcode, mrq->sbc->arg, mrq->sbc->flags);}pr_debug("%s: starting CMD%u arg %08x flags %08x\n", mmc_hostname(host), mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags);if (mrq->data) {pr_debug("%s:     blksz %d blocks %d flags %08x ""tsac %d ms nsac %d\n",mmc_hostname(host), mrq->data->blksz,mrq->data->blocks, mrq->data->flags,mrq->data->timeout_ns / 1000000,mrq->data->timeout_clks);}if (mrq->stop) {pr_debug("%s:     CMD%u arg %08x flags %08x\n", mmc_hostname(host), mrq->stop->opcode, mrq->stop->arg, mrq->stop->flags);}WARN_ON(!host->claimed);mrq->cmd->error = 0;mrq->cmd->mrq = mrq;if (mrq->data) {BUG_ON(mrq->data->blksz > host->max_blk_size);BUG_ON(mrq->data->blocks > host->max_blk_count);BUG_ON(mrq->data->blocks * mrq->data->blksz >host->max_req_size);#ifdef CONFIG_MMC_DEBUGsz = 0;for_each_sg(mrq->data->sg, sg, mrq->data->sg_len, i)sz += sg->length;BUG_ON(sz != mrq->data->blocks * mrq->data->blksz);#endifmrq->cmd->data = mrq->data;mrq->data->error = 0;mrq->data->mrq = mrq;if (mrq->stop) {mrq->data->stop = mrq->stop;mrq->stop->error = 0;mrq->stop->mrq = mrq;}}mmc_host_clk_hold(host);led_trigger_event(host->led, LED_FULL);host->ops->request(host, mrq);}

代码虽多,但是第62行之前多是打印调试信息和一些简单配置。
第62行:时钟使用计数加一;
第63行:设置led灯状态;
第64行:再次用到host的ops,记得在前面我已经用红色粗体字标记出来了,还说那是我在本文欠的第一笔账。
mmc_start_request分析完毕,再次来到mmc_wait_for_req,并且分析它的第二个函数:
static void mmc_wait_for_req_done(struct mmc_host *host,  struct mmc_request *mrq){struct mmc_command *cmd;while (1) {wait_for_completion(&mrq->completion);cmd = mrq->cmd;/* * If host has timed out waiting for the sanitize * to complete, card might be still in programming state * so let's try to bring the card out of programming * state. */if (cmd->sanitize_busy && cmd->error == -ETIMEDOUT) {if (!mmc_interrupt_hpi(host->card)) {pr_warning("%s: %s: Interrupted sanitize\n",   mmc_hostname(host), __func__);cmd->error = 0;break;} else {pr_err("%s: %s: Failed to interrupt sanitize\n",       mmc_hostname(host), __func__);}}if (!cmd->error || !cmd->retries ||    mmc_card_removed(host->card))break;pr_debug("%s: req failed (CMD%u): %d, retrying...\n", mmc_hostname(host), cmd->opcode, cmd->error);cmd->retries--;cmd->error = 0;host->ops->request(host, mrq);}}
通过函数名可以看回来,它是给__mmc_start_req做善后的。
第7行:完成机制等待函数,当请求函数完成请求后,就会置mrq->completion为完成状态,这样本函数的第7行以后的代码就得以执行;
第17--27行:如果前面请求发生了超时,而且系统设置了块请求优化选项,则使用中断的方式向主机发送优化请求;
第28--30行:如果前面请求失败了,或者是重试次数达到了上线,或者是卡被拔出了,则退出该函数;
第32--36行:重试前面失败的请求;
mmc_wait_for_req_done函数讲完了,那么mmc_wait_for_req函数也讲完了,那么mmc_wait_for_cmd函数也讲完了,那么mmc_do_erase也分析完了。

小结:mmc_do_erase最终调用了host->ops->request(host, mrq);来处理请求。

2.4、mmc_flush_cache

/* * Flush the cache to the non-volatile storage. */int mmc_flush_cache(struct mmc_card *card){struct mmc_host *host = card->host;int err = 0;if (!(host->caps2 & MMC_CAP2_CACHE_CTRL))return err;if (mmc_card_mmc(card) &&(card->ext_csd.cache_size > 0) &&(card->ext_csd.cache_ctrl & 1)) {err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,EXT_CSD_FLUSH_CACHE, 1, 0);if (err)pr_err("%s: cache flush error %d\n",mmc_hostname(card->host), err);}return err;}
注释已经写得很清楚:将缓存区的数据写到非易失存储;
第9行:如果容量缓存开关没打开,则直接返回;
第12--14行:如果是emmc且缓存区的容量大小大于0且缓存开关打开了,则执行mmc_switch:
参数EXT_CSD_CMD_SET_NORMAL:一个普通的设置命令;
参数EXT_CSD_FLUSH_CACHE:该命令是刷缓存的;
mmc_switch调用了__mmc_switch,其代码如下:


/** *__mmc_switch - modify EXT_CSD register *@card: the MMC card associated with the data transfer *@set: cmd set values *@index: EXT_CSD register index *@value: value to program into EXT_CSD register *@timeout_ms: timeout (ms) for operation performed by register write, *                   timeout of zero implies maximum possible timeout *@use_busy_signal: use the busy signal as response type *@send_status: send status cmd to poll for busy * *Modifies the EXT_CSD register for selected card. */int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,unsigned int timeout_ms, bool use_busy_signal, bool send_status){int err;struct mmc_command cmd = {0};unsigned long timeout;u32 status = 0;bool ignore_crc = false;BUG_ON(!card);BUG_ON(!card->host);cmd.opcode = MMC_SWITCH;cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |  (index << 16) |  (value << 8) |  set;cmd.flags = MMC_CMD_AC;if (use_busy_signal)cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B;elsecmd.flags |= MMC_RSP_SPI_R1 | MMC_RSP_R1;cmd.cmd_timeout_ms = timeout_ms;if (index == EXT_CSD_SANITIZE_START)cmd.sanitize_busy = true;err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);if (err)return err;/* No need to check card status in case of unblocking command */if (!use_busy_signal)return 0;/* * Must check status to be sure of no errors * If CMD13 is to check the busy completion of the timing change, * disable the check of CRC error. */if (index == EXT_CSD_HS_TIMING &&    !(card->host->caps & MMC_CAP_WAIT_WHILE_BUSY))ignore_crc = true;timeout = jiffies + msecs_to_jiffies(MMC_OPS_TIMEOUT_MS);do {if (send_status) {err = __mmc_send_status(card, &status, ignore_crc);if (err)return err;}if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)break;if (mmc_host_is_spi(card->host))break;/* * We are not allowed to issue a status command and the host * does'nt support MMC_CAP_WAIT_WHILE_BUSY, then we can only * rely on waiting for the stated timeout to be sufficient. */if (!send_status) {mmc_delay(timeout_ms);return 0;}/* Timeout if the device never leaves the program state. */if (time_after(jiffies, timeout)) {pr_err("%s: Card stuck in programming state! %s\n",mmc_hostname(card->host), __func__);return -ETIMEDOUT;}} while (R1_CURRENT_STATE(status) == R1_STATE_PRG);if (mmc_host_is_spi(card->host)) {if (status & R1_SPI_ILLEGAL_COMMAND)return -EBADMSG;} else {if (status & 0xFDFFA000)pr_warning("%s: unexpected status %#x after "       "switch", mmc_hostname(card->host), status);if (status & R1_SWITCH_ERROR)return -EBADMSG;}return 0;}

注释:修改EXT_CSD寄存器。
第17--41行:根据传入的参数配置命令结构;
第42行,调用mmc_wait_for_cmd执行具体操作,前面已经讲过;
后面的基本都是做错误处理了。那么mmc_flush_cache讲完了。
小结:mmc_flush_cache最终也是调用了host->ops->request(host, mrq);来处理请求。

2.5、mmc_start_req

/** *mmc_start_req - start a non-blocking request *@host: MMC host to start command *@areq: async request to start *@error: out parameter returns 0 for success, otherwise non zero * *Start a new MMC custom command request for a host. *If there is on ongoing async request wait for completion *of that request and start the new one and return. *Does not wait for the new request to complete. * *      Returns the completed request, NULL in case of none completed. *Wait for the an ongoing request (previoulsy started) to complete and *return the completed request. If there is no ongoing request, NULL *is returned without waiting. NULL is not an error condition. */struct mmc_async_req *mmc_start_req(struct mmc_host *host,    struct mmc_async_req *areq, int *error){int err = 0;int start_err = 0;struct mmc_async_req *data = host->areq;/* Prepare a new request */if (areq)mmc_pre_req(host, areq->mrq, !host->areq);if (host->areq) {err = mmc_wait_for_data_req_done(host, host->areq->mrq,areq);if (err == MMC_BLK_NEW_REQUEST) {if (error)*error = err;/* * The previous request was not completed, * nothing to return */return NULL;}/* * Check BKOPS urgency for each R1 response */if (host->card && mmc_card_mmc(host->card) &&    ((mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1) ||     (mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1B)) &&    (host->areq->mrq->cmd->resp[0] & R1_EXCEPTION_EVENT))mmc_start_bkops(host->card, true);}if (!err && areq)start_err = __mmc_start_data_req(host, areq->mrq);if (host->areq)mmc_post_req(host, host->areq->mrq, 0); /* Cancel a prepared request if it was not started. */if ((err || start_err) && areq)mmc_post_req(host, areq->mrq, -EINVAL);if (err)host->areq = NULL;elsehost->areq = areq;if (error)*error = err;return data;}
这个函数和前面我们看过的一个函数及其相似,咱们对比下函数名,顺便分析下区别:
__mmc_start_req:第2.3.1节中可以找到该函数的实现,该函数结合mmc_wait_for_req_done函数一起使用完成变量来实现同步;
mmc_start_req:注释很明确的说明,该函数为异步函数。

现在我们来分析下mmc_start_req函数:

第25--26:请求预处理,看一下其实现函数:

/** *mmc_pre_req - Prepare for a new request *@host: MMC host to prepare command *@mrq: MMC request to prepare for *@is_first_req: true if there is no previous started request *                     that may run in parellel to this call, otherwise false * *mmc_pre_req() is called in prior to mmc_start_req() to let *host prepare for the new request. Preparation of a request may be *performed while another request is running on the host. */static void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq, bool is_first_req){if (host->ops->pre_req) {mmc_host_clk_hold(host);host->ops->pre_req(host, mrq, is_first_req);mmc_host_clk_release(host);}}

该函数用于在执行真正的请求前做一些预处理的工作,实际调用2@host->ops->pre_req实现。
回到mmc_start_req的第29行
第29--50行:首先是看得出来,mmc_start_req()的目的其实是要处理本次areq。 所以一开始对areq做mmc_pre_req(host, areq->mrq, !host->areq); 预准备,但接下来是一个if (host->areq) { err = mmc_wait_for_data_req_done (host, host->areq->mrq, 一个很明显的等待操作,从函数名就可以得知这是一个同步等待,会放弃处理器等待命令完成。但从上文可以看到,一直还未正在处理命令,也没往host发送命令,此时就开始等待命令完成显然是毫无道理。但有时候代码容易看漏,该等待是针对的host->areq,并不是参数传递的areq。本次等待的是上一次传递未完成的动作。如果上次的传输以及完成,则该等待函数会很快返回,并把处理情况反馈到外面。
看一下mmc_wait_for_data_req_done的实现:

/* * mmc_wait_for_data_req_done() - wait for request completed * @host: MMC host to prepare the command. * @mrq: MMC request to wait for * * Blocks MMC context till host controller will ack end of data request * execution or new request notification arrives from the block layer. * Handles command retries. * * Returns enum mmc_blk_status after checking errors. */static int mmc_wait_for_data_req_done(struct mmc_host *host,      struct mmc_request *mrq,      struct mmc_async_req *next_req){struct mmc_command *cmd;struct mmc_context_info *context_info = &host->context_info;int err;unsigned long flags;while (1) {wait_event_interruptible(context_info->wait,(context_info->is_done_rcv || context_info->is_new_req));spin_lock_irqsave(&context_info->lock, flags);context_info->is_waiting_last_req = false;spin_unlock_irqrestore(&context_info->lock, flags);if (context_info->is_done_rcv) {context_info->is_done_rcv = false;context_info->is_new_req = false;cmd = mrq->cmd;if (!cmd->error || !cmd->retries ||    mmc_card_removed(host->card)) {err = host->areq->err_check(host->card,    host->areq);break; /* return err */} else {pr_info("%s: req failed (CMD%u): %d, retrying...\n",mmc_hostname(host),cmd->opcode, cmd->error);cmd->retries--;cmd->error = 0;host->ops->request(host, mrq);continue; /* wait for done/new event again */}} else if (context_info->is_new_req) {context_info->is_new_req = false;if (!next_req) {err = MMC_BLK_NEW_REQUEST;break; /* return err */}}}return err;}

通过第22行可以发现
通过wait_event_interruptible来实现同步功能,当等待条件不满足时,该函数会休眠,腾出cpu时间。
如果条件达成,则检查请求执行的状态,
再回到mmc_start_req,看__mmc_start_data_req的实现:
/* *__mmc_start_data_req() - starts data request * @host: MMC host to start the request * @mrq: data request to start * * Sets the done callback to be called when request is completed by the card. * Starts data mmc request execution */static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq){mrq->done = mmc_wait_data_done;mrq->host = host;if (mmc_card_removed(host->card)) {mrq->cmd->error = -ENOMEDIUM;mmc_wait_data_done(mrq);return -ENOMEDIUM;}mmc_start_request(host, mrq);return 0;}

里面调用了前面已经在2.3.1分析过的mmc_start_request函数。
现在已经分析完mmc_start_req函数的第50行了;

第52--53行:如果请求不为空,则执行mmc_post_req,该函数实现如下:

/** *mmc_post_req - Post process a completed request *@host: MMC host to post process command *@mrq: MMC request to post process for *@err: Error, if non zero, clean up any resources made in pre_req * *Let the host post process a completed request. Post processing of *a request may be performed while another reuqest is running. */static void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq, int err){if (host->ops->post_req) {mmc_host_clk_hold(host);host->ops->post_req(host, mrq, err);mmc_host_clk_release(host);}}

该函数是和本节前面分析的mmc_pre_req函数对应的,你如果发完请求后还想干点啥,可以在这里实现。
本函数最终调用了3@host->ops->post_req
好,我们还是回到mmc_start_req;
第56--57行,如果前面的请求发生错误,或者请求处理后做的收尾工作发生了错误,则取消即将发送的请求。(看注释)
第59到60行,如果请求发送(不一定执行完)成功,则将当前请求变量设置给host,否则置为null。

mmc_start_req函数分析完毕;

小结:
上面我们分析了mmc_start_req,它里面虽然用了wait_event_interruptible同步机制,但是用它是为了等待上一个任务执行完成,当上一个任务执行完成后,向host发送的请求就不是同步的了,也就是说,它只负责向host发送请求,而不管该请求什么时候结束。
但是mmc_wait_for_req却需要等到请求结束。

三、总结

根据上面的分析,在emmc/sd 区块层解析这篇文章中欠的债,最终全部都转向了host->ops,那么我们现在该将精力集中到这个东西是怎么实现的了。

请听下回分解。










0 0