SDIO驱动(6)命令的构建和发送

xiaoxiao2021-02-28  60

Linux 2.6.38

1、command的构建

MMC子系统是主从式的架构,数据的传输由host通过命令进行请求和发送。kernel中命令由结构体mmc_command表示:

struct mmc_command { u32 opcode; // Command的操作码 u32 arg; // Command携带的参数 u32 resp[4]; // Command发出后,如果需要应答,结果保存在resp数组中,最多可以保存128bits的应答 unsigned int flags; // 保存该命令所期望的应答类型 unsigned int retries; // 指明最多可重发的次数 unsigned int error; // 如果最终还是出错,通过该字段返回错误的原因,例如ETIMEDOUT、EILSEQ、EINVAL、ENOMEDIUM等 unsigned int erase_timeout; /* in milliseconds */ struct mmc_data *data; /* data segment associated with cmd */ struct mmc_request *mrq; /* associated request */ };

struct mmc_data结构定义传输数据内容:

struct mmc_data { unsigned int timeout_ns; /* data timeout (in ns, max 80ms) */ unsigned int timeout_clks; /* data timeout (in clocks) */ unsigned int blksz; /* data block size */ unsigned int blocks; /* number of blocks */ unsigned int error; /* data error */ unsigned int flags; #define MMC_DATA_WRITE (1 << 8) #define MMC_DATA_READ (1 << 9) #define MMC_DATA_STREAM (1 << 10) unsigned int bytes_xfered; struct mmc_command *stop; /* stop command */ struct mmc_request *mrq; /* associated request */ unsigned int sg_len; /* size of scatter list */ struct scatterlist *sg; /* I/O scatter list */ }

error,如果数据传输出错,错误值保存在该字段。

flags,指明数据传输方向,可采用下面定义的宏。

sg,struct scatterlist类型的数组,保存需要传输的数据。

sg_len,sg数组的大小。

我们知道,数据传输有两种方式,一种使用内存存放收发数据,另一种就是DMA。使用DMA的时候涉及数据的scatter/gather,就需要一个scatterlist数组。

传输请求mmc_request:

struct mmc_request { struct mmc_command *cmd; struct mmc_data *data; struct mmc_command *stop; void *done_data; /* completion data */ void (*done)(struct mmc_request *);/* completion function */ }; cmd,start command,传输操作必须设置

data,传输的数据,非必须。

done,传输完成时的回调函数,用于通知传输请求的发起者。

发送数据前,构建command,以CMD52为例:

static int mmc_io_rw_direct_host(struct mmc_host *host, int write, unsigned fn, unsigned addr, u8 in, u8 *out) { struct mmc_command cmd; int err; BUG_ON(!host); BUG_ON(fn > 7); /* sanity check */ if (addr & ~0x1FFFF) return -EINVAL; memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = SD_IO_RW_DIRECT; // #define SD_IO_RW_DIRECT 52 cmd.arg = write ? 0x80000000 : 0x00000000; cmd.arg |= fn << 28; cmd.arg |= (write && out) ? 0x08000000 : 0x00000000; cmd.arg |= addr << 9; cmd.arg |= in; cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC;     ............下半部省略............  }BUG_ON是一个宏:

#ifndef HAVE_ARCH_BUG_ON #define BUG_ON(condition) do { if (unlikely(condition)) BUG(); } while(0) #endif #define BUG() do { \ printk("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); \ panic("BUG!"); \ } while (0)可见,异常时导致系统panic并打印相应出错的位置,panic还可以提供更加详细的信息,比如寄存器、堆栈等。

第8行,function number占3bits,所以fn>7参数非法,11、12行的判断同理

16~22行,根据CMD52的格式构建cmd参数,arg是32bits无符号整形值,而SDIO命令是48bits,对应关系就是取cmd[8:39],arg即是:

2、command的发送

command构建好之后,mmc_io_rw_direct_host函数下半部执行:

static int mmc_io_rw_direct_host(struct mmc_host *host, int write, unsigned fn, unsigned addr, u8 in, u8 *out) { .........上半部省略............ err = mmc_wait_for_cmd(host, &cmd, 0); if (err) return err; if (mmc_host_is_spi(host)) { /* host driver already reported errors */ } else { if (cmd.resp[0] & R5_ERROR) return -EIO; if (cmd.resp[0] & R5_FUNCTION_NUMBER) return -EINVAL; if (cmd.resp[0] & R5_OUT_OF_RANGE) return -ERANGE; } if (out) { if (mmc_host_is_spi(host)) *out = (cmd.resp[0] >> 8) & 0xFF; else *out = cmd.resp[0] & 0xFF; } return 0; }

调用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; WARN_ON(!host->claimed); memset(&mrq, 0, sizeof(struct mmc_request)); 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; }WARN_ON(!host->claimed),判断目前host是否由当前进程所有,我们在 SDIO驱动(5)sdio总线上的probe分析过。 17~23行构建一个mmc_request,接力棒传递给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) { DECLARE_COMPLETION_ONSTACK(complete); mrq->done_data = &complete; mrq->done = mmc_wait_done; mmc_start_request(host, mrq); wait_for_completion(&complete); }这里开始一次请求,然后等待请求完成,实现方式用的是completion同步机制:

struct completion { unsigned int done; wait_queue_head_t wait; };

done的值指示等待的事件是否完成。初始化时为0表示等待的事件未完成。大于0表示等待的事件已经完成。

wait存放等待该事件完成的队列。

DECLARE_COMPLETION_ONSTACK 用于定义并初始化名为"completion"的completion,然后把它赋值给request的done_data成员,之后进行异步调用。 17行,mmc_start_request发起传输,然后调用wait_for_completion(&complete)等待事情的完成。mmc_start_request:

static void mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) { #ifdef CONFIG_MMC_DEBUG unsigned int i, sz; struct scatterlist *sg; #endif pr_debug("%s: starting CMD%u arg x flags x\n", mmc_hostname(host), mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags); if (mrq->data) { pr_debug("%s: blksz %d blocks %d flags x " "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 x flags x\n", mmc_hostname(host), mrq->stop->opcode, mrq->stop->arg, mrq->stop->flags); } WARN_ON(!host->claimed); led_trigger_event(host->led, LED_FULL); 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_DEBUG sz = 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); #endif mrq->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_ungate(host); host->ops->request(host, mrq); }3~25行,打印调试信息。 29行,点亮指示灯,一般不会启用。

33~54,对于CMD52命令mrq->data成员为NULL,所以这部份代码不会执行;对于数据传输,给mmc_request成员赋值,55行开启clock,然后调用host的request函数发送数据。host对应的是集成在SOC上的sdio控制器,这部分由host驱动负责实现。

一次数据传输结果有3种:OK/fail/timeout,然后填充cmd->error成员,由mmc_wait_for_cmd函数返回。 mmc_io_rw_direct_host函数的11~18行判断传输是否发生出错,错误返回错误码。

20~25行,如果我们关心card的应答,则取出应答值。

CMD52的Response结构:

所以24行,*out = cmd.resp[0] & 0xFF获取的是SDIO卡的状态,对应上图的Response Flags,为8 bit的数据。

转载请注明原文地址: https://www.6miu.com/read-81310.html

最新回复(0)