糖尿病康复,内容丰富有趣,生活中的好帮手!
糖尿病康复 > Linux内核4.14版本——mmc core(10)——mmc core主模块(6)mmc请求相关

Linux内核4.14版本——mmc core(10)——mmc core主模块(6)mmc请求相关

时间:2021-12-10 01:30:45

相关推荐

Linux内核4.14版本——mmc core(10)——mmc core主模块(6)mmc请求相关

目录

1. 数据结构说明

1.1struct mmc_command

1.2struct mmc_data

1.3struct mmc_request

1.4struct mmc_async_req

2. 重要函数

2.1mmc_wait_for_req

2.1.1mmc_start_request

2.1.2mmc_mrq_prep

2.1.3__mmc_start_request

2.2mmc_request_done

2.3mmc_wait_for_cmd

2.4 mmc_start_areq(重要)

2.4.1mmc_finalize_areq

2.4.2__mmc_start_data_req

分成同步的mmc请求和异步的mmc请求。差别如下:

1、流程上的差别:(1)会阻塞的处理流程:mmc_wait_for_req——》__mmc_start_req // 发起请求————》init_completion(&mrq->completion); ————》mrq->done = mmc_wait_done————》mmc_start_request(host, mrq); // 实际发起请求的操作——》mmc_wait_for_req_done // 阻塞等待请求处理完成——》返回(2)不阻塞等待该命令的处理流程:(注意:并不是说调用这个接口并不会阻塞,而是不会为了等待当前请求处理完成而阻塞,但是可能会等待上一次请求处理完成而阻塞)mmc_start_areq——》mmc_finalize_areq// 阻塞等待上一次的请求处理——》__mmc_start_data_req // 发起异步请求————》mrq->done = mmc_wait_data_done————》mmc_start_request // 实际发起请求的操作——》返回

最后都是调用了mmc_start_request使host向MMC发起请求。

1. 数据结构说明

一个mmc请求分成两部分内容,分别是命令部分和数据部分。

1.1struct mmc_command

struct mmc_command {u32opcode;// 命令的操作码,如MMC_GO_IDLE_STATE、MMC_SEND_OP_COND等等u32arg; // 命令的参数#define MMC_CMD23_ARG_REL_WR(1 << 31)#define MMC_CMD23_ARG_PACKED((0 << 31) | (1 << 30))#define MMC_CMD23_ARG_TAG_REQ(1 << 29)u32resp[4]; // response值unsigned intflags;/* expected response type */// 期待的response的类型#define MMC_RSP_PRESENT(1 << 0)#define MMC_RSP_136(1 << 1)/* 136 bit response */#define MMC_RSP_CRC(1 << 2)/* expect valid crc */#define MMC_RSP_BUSY(1 << 3)/* card may send busy */#define MMC_RSP_OPCODE(1 << 4)/* response contains opcode */#define MMC_CMD_MASK(3 << 5)/* non-SPI command type */#define MMC_CMD_AC(0 << 5)#define MMC_CMD_ADTC(1 << 5)#define MMC_CMD_BC(2 << 5)#define MMC_CMD_BCR(3 << 5)#define MMC_RSP_SPI_S1(1 << 7)/* one status byte */#define MMC_RSP_SPI_S2(1 << 8)/* second byte */#define MMC_RSP_SPI_B4(1 << 9)/* four data bytes */#define MMC_RSP_SPI_BUSY (1 << 10)/* card may send busy *//** These are the native response types, and correspond to valid bit* patterns of the above flags. One additional valid pattern* is all zeros, which means we don't expect a response.*/#define MMC_RSP_NONE(0)#define MMC_RSP_R1(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)#define MMC_RSP_R1B(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY)#define MMC_RSP_R2(MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC)#define MMC_RSP_R3(MMC_RSP_PRESENT)#define MMC_RSP_R4(MMC_RSP_PRESENT)#define MMC_RSP_R5(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)#define MMC_RSP_R6(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)#define MMC_RSP_R7(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)/* Can be used by core to poll after switch to MMC HS mode */#define MMC_RSP_R1_NO_CRC(MMC_RSP_PRESENT|MMC_RSP_OPCODE)#define mmc_resp_type(cmd)((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE))/** These are the SPI response types for MMC, SD, and SDIO cards.* Commands return R1, with maybe more info. Zero is an error type;* callers must always provide the appropriate MMC_RSP_SPI_Rx flags.*/#define MMC_RSP_SPI_R1(MMC_RSP_SPI_S1)#define MMC_RSP_SPI_R1B(MMC_RSP_SPI_S1|MMC_RSP_SPI_BUSY)#define MMC_RSP_SPI_R2(MMC_RSP_SPI_S1|MMC_RSP_SPI_S2)#define MMC_RSP_SPI_R3(MMC_RSP_SPI_S1|MMC_RSP_SPI_B4)#define MMC_RSP_SPI_R4(MMC_RSP_SPI_S1|MMC_RSP_SPI_B4)#define MMC_RSP_SPI_R5(MMC_RSP_SPI_S1|MMC_RSP_SPI_S2)#define MMC_RSP_SPI_R7(MMC_RSP_SPI_S1|MMC_RSP_SPI_B4)#define mmc_spi_resp_type(cmd)((cmd)->flags & \(MMC_RSP_SPI_S1|MMC_RSP_SPI_BUSY|MMC_RSP_SPI_S2|MMC_RSP_SPI_B4))/** These are the command types.*/#define mmc_cmd_type(cmd)((cmd)->flags & MMC_CMD_MASK)unsigned intretries;/* max number of retries */// 失败时的重复尝试次数interror;/* command error */// 命令的错误码/** Standard errno values are used for errors, but some have specific* meaning in the MMC layer:** ETIMEDOUT Card took too long to respond* EILSEQ Basic format problem with the received or sent data* (e.g. CRC check failed, incorrect opcode in response* or bad end bit)* EINVAL Request cannot be performed because of restrictions* in hardware and/or the driver* ENOMEDIUM Host can determine that the slot is empty and is* actively failing requests*/unsigned intbusy_timeout;/* busy detect timeout in ms *//* Set this flag only for blocking sanitize request */boolsanitize_busy;struct mmc_data*data;/* data segment associated with cmd */ // 和该命令关联在一起的数据段struct mmc_request*mrq;/* associated request */// 该命令关联到哪个request};

1.2struct mmc_data

struct mmc_data {unsigned inttimeout_ns;/* data timeout (in ns, max 80ms) */// 超时时间,以ns为单位unsigned inttimeout_clks;/* data timeout (in clocks) */// 超时时间,以clock为单位unsigned intblksz;/* data block size */unsigned intblocks;/* number of blocks */unsigned intblk_addr;/* block address */interror;/* data error */unsigned intflags;#define MMC_DATA_WRITEBIT(8)#define MMC_DATA_READBIT(9)/* Extra flags used by CQE */#define MMC_DATA_QBRBIT(10)/* CQE queue barrier*/#define MMC_DATA_PRIOBIT(11)/* CQE high priority */#define MMC_DATA_REL_WRBIT(12)/* Reliable write */#define MMC_DATA_DAT_TAGBIT(13)/* Tag request */#define MMC_DATA_FORCED_PRGBIT(14)/* Forced programming */unsigned intbytes_xfered;struct mmc_command*stop;/* stop command */// 结束传输的命令struct mmc_request*mrq;/* associated request */// 该命令关联到哪个requestunsigned intsg_len;/* size of scatter list */intsg_count;/* mapped sg entries */struct scatterlist*sg;/* I/O scatter list */s32host_cookie;/* host private data */};

1.3struct mmc_request

struct mmc_request是mmc core向host controller发起命令请求的处理单位。

struct mmc_request {struct mmc_command*sbc;/* SET_BLOCK_COUNT for multiblock */// 设置块数量的命令,怎么用的后续再补充struct mmc_command*cmd; // 要传输的命令struct mmc_data*data; // 要传输的数据struct mmc_command*stop; // 结束命令,怎么用的后续再补充struct completioncompletion; // 完成量struct completioncmd_completion;void(*done)(struct mmc_request *);/* completion function *//** Notify uppers layers (e.g. mmc block driver) that recovery is needed* due to an error associated with the mmc_request. Currently used only* by CQE.*/void(*recovery_notifier)(struct mmc_request *);struct mmc_host*host; // 所属host/* Allow other commands during this ongoing data transfer or busy wait */boolcap_cmd_during_tfr;inttag;};

1.4struct mmc_async_req

struct mmc_async_req {/* active mmc request */struct mmc_request*mrq;/** Check error status of completed mmc request.* Returns 0 if success otherwise non zero.*/enum mmc_blk_status (*err_check)(struct mmc_card *, struct mmc_async_req *);};

2. 重要函数

2.1mmc_wait_for_req

mmc_wait_for_req->__mmc_start_req

发起mmc_request请求并且等待其处理完成。由其他需要发起mmc请求的模块调用。

可以结合后面的mmc_request_done来看。

static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq){int err;mmc_wait_ongoing_tfr_cmd(host);// 初始化完成量,在mmc_wait_for_req_done中会去等待这个完成量init_completion(&mrq->completion);mrq->done = mmc_wait_done;init_completion(&mrq->cmd_completion);/* 调用mmc_start_request发起mmc请求 */err = mmc_start_request(host, mrq); // 开始处理mmc_request请求if (err) {mrq->cmd->error = err;mmc_complete_cmd(mrq); complete(&mrq->completion);}return err;}

2.1.1mmc_start_request

static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq){int err;mmc_retune_hold(host);if (mmc_card_removed(host->card))return -ENOMEDIUM;mmc_mrq_pr_debug(host, mrq);WARN_ON(!host->claimed);err = mmc_mrq_prep(host, mrq);if (err)return err;led_trigger_event(host->led, LED_FULL);__mmc_start_request(host, mrq);return 0;}

2.1.2mmc_mrq_prep

static int mmc_mrq_prep(struct mmc_host *host, struct mmc_request *mrq){unsigned int i, sz = 0;struct scatterlist *sg;if (mrq->cmd) {mrq->cmd->error = 0;mrq->cmd->mrq = mrq;mrq->cmd->data = mrq->data;}if (mrq->sbc) {mrq->sbc->error = 0;mrq->sbc->mrq = mrq;}if (mrq->data) {if (mrq->data->blksz > host->max_blk_size ||mrq->data->blocks > host->max_blk_count ||mrq->data->blocks * mrq->data->blksz > host->max_req_size)return -EINVAL;for_each_sg(mrq->data->sg, sg, mrq->data->sg_len, i)sz += sg->length;if (sz != mrq->data->blocks * mrq->data->blksz)return -EINVAL;mrq->data->error = 0;mrq->data->mrq = mrq;if (mrq->stop) {mrq->data->stop = mrq->stop;mrq->stop->error = 0;mrq->stop->mrq = mrq;}}return 0;}

2.1.3__mmc_start_request

static void __mmc_start_request(struct mmc_host *host, struct mmc_request *mrq){int err;......host->ops->request(host, mrq);}

会调用host->ops->request来对mmc_request进行处理,对于sdhci类型的host,对应就是sdhci_request。

这个方法就是mmc_request实际被处理的核心。

2.2mmc_request_done

通知mmc core某个mmc_request已经处理完成,由host controller调用。

以sdhci类型的host为例,处理完一个mmc_request之后,会执行sdhci_tasklet_finish,而在sdhci_tasklet_finish中会调用mmc_request_done来通知host某个mmc_request已经处理完成了。

/***mmc_request_done - finish processing an MMC request*@host: MMC host which completed request*@mrq: MMC request which request**MMC drivers should call this function when they have completed*their processing of a request.*/void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq){struct mmc_command *cmd = mrq->cmd;int err = cmd->error;/* Flag re-tuning needed on CRC errors */if ((cmd->opcode != MMC_SEND_TUNING_BLOCK &&cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200) &&(err == -EILSEQ || (mrq->sbc && mrq->sbc->error == -EILSEQ) ||(mrq->data && mrq->data->error == -EILSEQ) ||(mrq->stop && mrq->stop->error == -EILSEQ)))mmc_retune_needed(host);if (err && cmd->retries && mmc_host_is_spi(host)) {if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)cmd->retries = 0;}if (host->ongoing_mrq == mrq)host->ongoing_mrq = NULL;mmc_complete_cmd(mrq);trace_mmc_request_done(host, mrq);/** We list various conditions for the command to be considered* properly done:** - There was no error, OK fine then* - We are not doing some kind of retry* - The card was removed (...so just complete everything no matter* if there are errors or retries)*/if (!err || !cmd->retries || mmc_card_removed(host->card)) {mmc_should_fail_request(host, mrq);if (!host->ongoing_mrq)led_trigger_event(host->led, LED_OFF);if (mrq->sbc) {pr_debug("%s: req done <CMD%u>: %d: %08x %08x %08x %08x\n",mmc_hostname(host), mrq->sbc->opcode,mrq->sbc->error,mrq->sbc->resp[0], mrq->sbc->resp[1],mrq->sbc->resp[2], mrq->sbc->resp[3]);}pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n",mmc_hostname(host), cmd->opcode, err,cmd->resp[0], cmd->resp[1],cmd->resp[2], cmd->resp[3]);if (mrq->data) {pr_debug("%s:%d bytes transferred: %d\n",mmc_hostname(host),mrq->data->bytes_xfered, mrq->data->error);}if (mrq->stop) {pr_debug("%s:(CMD%u): %d: %08x %08x %08x %08x\n",mmc_hostname(host), mrq->stop->opcode,mrq->stop->error,mrq->stop->resp[0], mrq->stop->resp[1],mrq->stop->resp[2], mrq->stop->resp[3]);}}/** Request starter must handle retries - see* mmc_wait_for_req_done().*/if (mrq->done)mrq->done(mrq);}

通过上述,mrq->done被调度,mmc_wait_done被执行,mrq->completion被设置。

然后等待mrq->completion的mmc_wait_for_req_done就会继续往下执行。

2.3mmc_wait_for_cmd

mmc_wait_for_cmd用于处理一个不带数据请求的命令。

会被封装到mmc_request中,通过调用mmc_wait_for_req来发起请求。

/***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 = {};WARN_ON(!host->claimed);memset(cmd->resp, 0, sizeof(cmd->resp)); // 清空command的responsecmd->retries = retries; // 失败时的重复尝试次数mrq.cmd = cmd; // 封装到mmc_request中cmd->data = NULL; // 不带数据包的命令,故清空datammc_wait_for_req(host, &mrq); // 调用mmc_wait_for_req发起mmc请求并且等待其处理完成return cmd->error;}

2.4 mmc_start_areq(重要)

机制说明如下:mmc_start_areq会先判断上一次的asycn_req是否处理完成,如果没有处理完成,则会等待其处理完成。

如果处理完成了,为当前要处理的asycn_req发起请求,但是并不会等待,而是直接返回。

注意:并不是说调用这个接口并不会阻塞,而是不会为了等待当前请求处理完成而阻塞,但是可能会等待上一次请求处理完成而阻塞。这样,可以利用等待的一部分时间来做其他操作。

为了方便理解这个函数,需要看一下其函数注释。

* 要注意,在函数里面有两个异步请求:

- areq:表示新的异步请求

- host->areq:表示上一次发起的、正在处理、等待完成的异步请求

/*** mmc_start_areq - start a non-blocking request // 该函数用来发起一个不阻塞的请求* @host: MMC host to start command // 要发起对应请求的host* @areq: async request to start // 要发起的异步请求* @error: out parameter returns 0 for success, otherwise non zero // 返回值,返回0表示成功,返回非零表示失败** Start a new MMC custom command request for a host. // 为host发起的一个新的mmc命令请求* If there is on ongoing async request wait for completion // 如果host已经有一个正在处理、等待完成的异步请求,那么会等待这个请求完成!!!* 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. // 会返回被完成的mmc请求(而不是新的mmc请求。)空表示没有mmc请求被完成。* 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.// 等待上一次发起的mmc请求完成,然后把这个mmc请求返回。如果没有mmc请求正在处理,那么就直接返回而不会等待。空并不是错误条件。*/struct mmc_async_req *mmc_start_areq(struct mmc_host *host,struct mmc_async_req *areq,enum mmc_blk_status *ret_stat){enum mmc_blk_status status;int start_err = 0;struct mmc_async_req *previous = host->areq;/* Prepare a new request *//* 为新的异步请求做准备处理 */if (areq)mmc_pre_req(host, areq->mrq);/* Finalize previous request *//* 对上一次发起的、正在处理、等待完成的异步请求进行处理、等待操作 */status = mmc_finalize_areq(host);if (ret_stat)*ret_stat = status;/* The previous request is still going on... */if (status == MMC_BLK_NEW_REQUEST)return NULL;/* Fine so far, start the new request! *//* 对新的异步请求进行发起操作 */if (status == MMC_BLK_SUCCESS && areq)// 调用__mmc_start_data_req发起新的异步请求start_err = __mmc_start_data_req(host, areq->mrq);/* Postprocess the old request at this point */if (host->areq)mmc_post_req(host, host->areq->mrq, 0);/* Cancel a prepared request if it was not started. */if ((status != MMC_BLK_SUCCESS || start_err) && areq)mmc_post_req(host, areq->mrq, -EINVAL);if (status != MMC_BLK_SUCCESS)host->areq = NULL;elsehost->areq = areq;return previous;}

2.4.1mmc_finalize_areq

mmc_finalize_areq处理上一次的请求。

/*** mmc_finalize_areq() - finalize an asynchronous request* @host: MMC host to finalize any ongoing request on** Returns the status of the ongoing asynchronous request, but* MMC_BLK_SUCCESS if no request was going on.*/static enum mmc_blk_status mmc_finalize_areq(struct mmc_host *host){struct mmc_context_info *context_info = &host->context_info;enum mmc_blk_status status;if (!host->areq)return MMC_BLK_SUCCESS;while (1) {wait_event_interruptible(context_info->wait,(context_info->is_done_rcv ||context_info->is_new_req));if (context_info->is_done_rcv) {struct mmc_command *cmd;context_info->is_done_rcv = false;cmd = host->areq->mrq->cmd;if (!cmd->error || !cmd->retries ||mmc_card_removed(host->card)) {status = host->areq->err_check(host->card,host->areq);break; /* return status */} else {mmc_retune_recheck(host);pr_info("%s: req failed (CMD%u): %d, retrying...\n",mmc_hostname(host),cmd->opcode, cmd->error);cmd->retries--;cmd->error = 0;__mmc_start_request(host, host->areq->mrq);continue; /* wait for done/new event again */}}return MMC_BLK_NEW_REQUEST;}mmc_retune_release(host);/** 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);}return status;}

2.4.2__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* If an ongoing transfer is already in progress, wait for the command line* to become available before sending another command.*/static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq){int err;mmc_wait_ongoing_tfr_cmd(host);mrq->done = mmc_wait_data_done;mrq->host = host;init_completion(&mrq->cmd_completion);err = mmc_start_request(host, mrq);if (err) {mrq->cmd->error = err;mmc_complete_cmd(mrq);mmc_wait_data_done(mrq);}return err;}

如果觉得《Linux内核4.14版本——mmc core(10)——mmc core主模块(6)mmc请求相关》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。