- 了解linux mmc card driver.
1.概述
mmc core/block.c中module_init(mmc_blk_init)调用mmc_register_driver函数创建mmcblk driver,并将之挂载到mmc_bus_type总线的driver list链表上。
Note:
mmc core提供了mmc_test.c作为mmc driver的测试文件。mmc_test.c中,module_init(mmc_test_init)函数中,调用mmc_register_driver函数创建了mmc_test driver,并且将之挂载在mmc_bus_type总线的driver list链表上。
mmc模块如果需要使用mmc_test功能,需要把CONFIG_MMC_BLOKC宏关闭,然后把CONFIG_MMC_BLOCK=y。否则,card将会匹配blockdreiver,不会再次匹配mmc_test driver,具体见下图。
2.相关结构体
2.1.struct mmc_card
struct mmc_card {
struct mmc_host *host; /* the host this device belongs to */ // 该mmc_card所属host
struct device dev; /* the device */ // 对应的device
unsigned int rca; /* relative card address of device */ // 该card的RCA地址
unsigned int type; /* card type */ // card类型,后面说明
unsigned int state; /* (our) card state */ // card的当前状态,后面说明
unsigned int quirks; /* card quirks */ // 该card的一些特点
unsigned int erase_size; /* erase size in sectors */
unsigned int erase_shift; /* if erase unit is power 2 */
unsigned int pref_erase; /* in sectors */
u8 erased_byte; /* value of erased bytes */
u32 raw_cid[4]; /* raw card CID */ // 原始的cid寄存器的值
u32 raw_csd[4]; /* raw card CSD */ // 原始的csd寄存器的值
u32 raw_scr[2]; /* raw card SCR */ // 原始的scr寄存器的值
struct mmc_cid cid; /* card identification */ // 从cid寄存器的值解析出来的信息
struct mmc_csd csd; /* card specific */ // 从csd寄存器的值解析出来的信息
struct mmc_ext_csd ext_csd; /* mmc v4 extended card specific */ // 从ext_csd寄存器的值解析出来的信息
struct sd_scr scr; /* extra SD information */ // 外部sdcard的信息
struct sd_ssr ssr; /* yet more SD information */ // 更多关于sd card的信息
struct sd_switch_caps sw_caps; /* switch (CMD6) caps */ // sd的切换属性
unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */
struct dentry *debugfs_root; // 对应debug目录的结构体
struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */ // 物理分区
unsigned int nr_parts; // 分区数量
unsigned int part_curr; // 当前分区
struct mmc_wr_pack_stats wr_pack_stats; /* packed commands stats*/
struct mmc_bkops_info bkops_info;
struct device_attribute rpm_attrib; // rpm属性
unsigned int idle_timeout;
struct notifier_block reboot_notify;
bool issue_long_pon;
u8 *cached_ext_csd;
};
mmc card类型(mmc_card->type)如下:
#define MMC_TYPE_MMC 0 /* MMC card */
#define MMC_TYPE_SD 1 /* SD card */
#define MMC_TYPE_SDIO 2 /* SDIO card */
#define MMC_TYPE_SD_COMBO 3 /* SD combo (IO+mem) card */
mmc card状态(mmc_card->state)如下:
#define MMC_STATE_PRESENT (1<<0) /* present in sysfs */
#define MMC_STATE_READONLY (1<<1) /* card is read-only */
#define MMC_STATE_HIGHSPEED (1<<2) /* card is in high speed mode */
#define MMC_STATE_BLOCKADDR (1<<3) /* card uses block-addressing */
#define MMC_STATE_HIGHSPEED_DDR (1<<4) /* card is in high speed mode */
#define MMC_STATE_ULTRAHIGHSPEED (1<<5) /* card is in ultra high speed mode */
#define MMC_CARD_SDXC (1<<6) /* card is SDXC */
#define MMC_CARD_REMOVED (1<<7) /* card has been removed */
#define MMC_STATE_HIGHSPEED_200 (1<<8) /* card is in HS200 mode */
#define MMC_STATE_HIGHSPEED_400 (1<<9) /* card is in HS400 mode */
#define MMC_STATE_DOING_BKOPS (1<<10) /* card is doing BKOPS */
#define MMC_STATE_NEED_BKOPS (1<<11) /* card needs to do BKOPS */
mmc card的一些特写标识(mmc_card->quirks)如下:
#define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */
#define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */
/* for byte mode */
#define MMC_QUIRK_NONSTD_SDIO (1<<2) /* non-standard SDIO card attached */
/* (missing CIA registers) */
#define MMC_QUIRK_BROKEN_CLK_GATING (1<<3) /* clock gating the sdio bus will make card fail */
#define MMC_QUIRK_NONSTD_FUNC_IF (1<<4) /* SDIO card has nonstd function interfaces */
#define MMC_QUIRK_DISABLE_CD (1<<5) /* disconnect CD/DAT[3] resistor */
#define MMC_QUIRK_INAND_CMD38 (1<<6) /* iNAND devices have broken CMD38 */
#define MMC_QUIRK_BLK_NO_CMD23 (1<<7) /* Avoid CMD23 for regular multiblock */
#define MMC_QUIRK_BROKEN_BYTE_MODE_512 (1<<8) /* Avoid sending 512 bytes in */
#define MMC_QUIRK_LONG_READ_TIME (1<<9) /* Data read time > CSD says */
#define MMC_QUIRK_SEC_ERASE_TRIM_BROKEN (1<<10) /* Skip secure for erase/trim */
/* byte mode */
#define MMC_QUIRK_INAND_DATA_TIMEOUT (1<<11) /* For incorrect data timeout */
/* To avoid eMMC device getting broken permanently due to HPI feature */
#define MMC_QUIRK_BROKEN_HPI (1 << 12)
/* Skip data-timeout advertised by card */
#define MMC_QUIRK_BROKEN_DATA_TIMEOUT (1<<13)
#define MMC_QUIRK_CACHE_DISABLE (1 << 14) /* prevent cache enable */
2.2.struct mmc_part
225 /*
226 * MMC Physical partitions
227 */
228 struct mmc_part {
229 unsigned int size; /* partition size (in bytes) */
230 unsigned int part_cfg; /* partition type */
231 char name[MAX_MMC_PART_NAME_LEN];
232 bool force_ro; /* to make boot parts RO by default */
233 unsigned int area_type;
234 #define MMC_BLK_DATA_AREA_MAIN (1<<0)
235 #define MMC_BLK_DATA_AREA_BOOT (1<<1)
236 #define MMC_BLK_DATA_AREA_GP (1<<2)
237 #define MMC_BLK_DATA_AREA_RPMB (1<<3)
238 };
2.3.struct mmc_bus_ops
host的mmc总线的操作集,由host插入的card决定。不同类型的card对mmc总线的操作有所不同。
struct mmc_bus_ops {
int (*awake)(struct mmc_host *); // 唤醒mmc总线上的card
int (*sleep)(struct mmc_host *); // 休眠mmc总线上的card
void (*remove)(struct mmc_host *); // 从软件上注销mmc总线上的card
void (*detect)(struct mmc_host *); // 检测mmc总线上的card是否被移除
int (*suspend)(struct mmc_host *); // 对应mmc总线的suspend操作
int (*resume)(struct mmc_host *); // 对应mmc总线的resume操作
int (*power_save)(struct mmc_host *); // 存储电源状态
int (*power_restore)(struct mmc_host *); // 恢复电源状态
int (*alive)(struct mmc_host *); // 检测mmc总线上的card的激活状态
int (*change_bus_speed)(struct mmc_host *, unsigned long *); // 修改mmc总线的工作时钟
};
2.4.struct mmc_ios
struct mmc_ios 由mmc core定义的规范的结构,用来维护mmc总线相关的一些io setting。
struct mmc_ios {
unsigned int clock; /* clock rate */ // 当前工作频率
unsigned int old_rate; /* saved clock rate */ // 上一次的工作频率
unsigned long clk_ts; /* time stamp of last updated clock */ // 上一次更新工作频率的时间戳
unsigned short vdd;/* vdd stores the bit number of the selected voltage range from below. */ // 支持的电压表
unsigned char bus_mode; /* command output mode */ // 总线输出模式,包括开漏模式和上拉模式
unsigned char chip_select; /* SPI chip select */ // spi片选
unsigned char power_mode; /* power supply mode */ // 电源状态模式
unsigned char bus_width; /* data bus width */ // 总线宽度
unsigned char timing; /* timing specification used */ // 时序类型
unsigned char signal_voltage; /* signalling voltage (1.8V or 3.3V) */ // 信号的工作电压
unsigned char drv_type; /* driver type (A, B, C, D) */ // 驱动类型
};
操作函数:
- mmc_set_ios //调用sdhci_set_ios设置ios
- mmc_set_timing
- mmc_set_driver_type
- mmc_set_bus_mode
- mmc_set_chip_select
3.驱动代码分析
3.1.mmc_bus_type
- mmc_bus_type代表了mmc虚拟总线。其内容如下:
static struct bus_type mmc_bus_type = {
.name = "mmc", // 相应会在/sys/bus下生成mmc目录
.dev_attrs = mmc_dev_attrs, // bus下的device下继承的属性,可以看到/sys/bus/mmc/devices/mmc0:0001/type属性就是这里来的
.match = mmc_bus_match, // 用于mmc bus上device和driver的匹配
.uevent = mmc_bus_uevent,
.probe = mmc_bus_probe, // 当match成功的时候,执行的probe操作
.remove = mmc_bus_remove,
.shutdown = mmc_bus_shutdown,
.pm = &mmc_bus_pm_ops, // 挂在mmc bus上的device的电源管理操作集合
};
3.2.驱动和设备匹配
static int mmc_bus_match(struct device *dev, struct device_driver *drv)
{
return 1; // 无条件返回1,说明挂载mmc bus上的device(mmc_card)和driver(mmc_driver)是无条件匹配的。
}
static int mmc_bus_probe(struct device *dev)
{
struct mmc_driver *drv = to_mmc_driver(dev->driver);
struct mmc_card *card = mmc_dev_to_card(dev);
return drv->probe(card); // 直接调用mmc_driver中的probe操作,对于block.c来说就是mmc_blk_probe
}
3.3.驱动入口函数mmc_blk_init()
- 该函数向内核申请注册一个块设备,然后进入核心层进行注册。
drivers/mmc/core/block.c:
static int __init mmc_blk_init(void)
{
/*向内核申请注册一个块设备*/
res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");
/*进入核心层进行注册*/
mmc_register_driver(&mmc_driver); //注册mmc_driver
return 0;
}
int mmc_register_driver(struct mmc_driver *drv)
{
drv->drv.bus = &mmc_bus_type; //mmc设备是挂载在mmc总线上的
return driver_register(&drv->drv); //注册mmc驱动
}
3.4.驱动结构体mmc_driver
- 该结构体定义驱动的名字,驱动探针函数、驱动移除函数、驱动阻塞和驱动重启等函数。
static struct mmc_driver mmc_driver = {
.drv = {
.name = "mmcblk",
},
.probe = mmc_blk_probe,
.remove = mmc_blk_remove,
.suspend = mmc_blk_suspend,
.resume = mmc_blk_resume,
};
3.5.块设备操作结构体mmc_bdops
- 结构体mmc_bdops中定义了块设备操作的接口函数。
static struct block_device_operations mmc_bdops = {
.open = mmc_blk_open,
.release = mmc_blk_release,
.getgeo = mmc_blk_getgeo,
.owner = THIS_MODULE,
};
3.6.块设备探针函数mmc_blk_probe()
- 该函数主要完成检验卡支持的命令,分配mmc_blk_data结构体空间,设置块的大小,设置card的driver_data 字段,并注册mmc信息到系统。
static int mmc_blk_probe(struct mmc_card *card)
{
struct mmc_blk_data *md;
int err;
char cap_str[10];
/*检查卡支持的命令*/
if (!(card->csd.cmdclass & CCC_BLOCK_READ))
return -ENODEV;
/*为card分配mmc_blk_data结构体空间*/
md = mmc_blk_alloc(card);
/*设置块的大小*/
mmc_blk_set_blksize(md, card);
/*将md设置为card的driver_data 字段*/
mmc_set_drvdata(card, md);
/*把mmc包含的信息向系统进行注册,注册成功后就可以在文
件系统对应目录下找到 mmc_card对应的结点设备*/
add_disk(md->disk);
return 0;
}
3.6.1.mmc_blk_alloc():
mmc_blk_alloc:
-> md = mmc_blk_alloc_req(card, &card->dev, size, false, NULL);
-> md->disk = alloc_disk(perdev_minors);
-> ret = mmc_init_queue(&md->queue, card, &md->lock, subname); //初始化blk队列,然后建立一个线程mmc_queue_thread().
-> mq->queue = blk_init_queue(mmc_request, lock);
-> set_capacity(md->disk, size);
- mmc_init_queue->mmc_queue_thread->mmc_blk_issue_rq
其调用关系如下:
mmc_blk_issue_rq
-> mmc_blk_issue_secdiscard_rq(mq, req);
-> mmc_blk_issue_discard_rq(mq, req);
-> mmc_blk_issue_flush(mq, req);
-> mmc_blk_issue_rw_rq(mq, req); /* 上面四个函数选一个执行 */
-> mmc_wait_for_req(card->host, &brq.mrq);
-> mmc_start_request(host, mrq);
-> host->ops->request(host, mrq); /* s3cmci.c中host->requset = s3cmci_request */
请求处理过程:
mmc_queue_thread----->mmc_blk_issue_rq---->mmc_wait_for_req—>mmc_start_request---->host->ops->request(s3cmci.c里边的s3cmci_request被调用)
3.6.2. mmc_add_disk():
mmc_add_disk(md)
-> add_disk(md->disk);
-> device_create_file(disk_to_dev(md->disk), &md->force_ro);
4.APIs
4.1.mmc_alloc_card
- 用于分配一个struct mmc_card结构体,创建其于mmc host以及mmc bus之间的关联。
struct mmc_card *mmc_alloc_card(struct mmc_host *host, struct device_type *type)
{
struct mmc_card *card;
card = kzalloc(sizeof(struct mmc_card), GFP_KERNEL); // 分配一个mmc_card
if (!card)
return ERR_PTR(-ENOMEM);
card->host = host; // 关联mmc_card与mmc_host
device_initialize(&card->dev);
card->dev.parent = mmc_classdev(host);
// 设置card的device的parent device为mmc_host的classdev,
// 注册到设备驱动模型中之后,会在/sys/class/mmc_host/mmc0目录下生成相应card的节点,如mmc0:0001
card->dev.bus = &mmc_bus_type;
// 设置card的bus为mmc_bus_type,这样,mmc_card注册到设备驱动模型中之后就会挂在mmc_bus下。
// 会在/sys/bus/mmc/devices/目录下生成相应card的节点,如mmc0:0001
card->dev.release = mmc_release_card;
card->dev.type = type; // 设置device type
spin_lock_init(&card->bkops_info.bkops_stats.lock); // 初始化spin_lock
spin_lock_init(&card->wr_pack_stats.lock); // 初始化spin_lock
return card;
}
4.2.mmc_add_card
- 用于注册struct mmc_card到mmc_bus上。
int mmc_add_card(struct mmc_card *card)
{
int ret;
/* 以下用于打印card的注册信息 */
const char *type;
const char *uhs_bus_speed_mode = "";
if (mmc_host_is_spi(card->host)) {
pr_info("%s: new %s%s%s card on SPI\n",
mmc_hostname(card->host),
mmc_card_highspeed(card) ? "high speed " : "",
mmc_card_ddr_mode(card) ? "DDR " : "",
type);
} else {
pr_info("%s: new %s%s%s%s%s%s card at address %04x\n",
mmc_hostname(card->host),
mmc_card_uhs(card) ? "ultra high speed " :
(mmc_card_highspeed(card) ? "high speed " : ""),
(mmc_card_hs400(card) ? "HS400 " : ""),
(mmc_card_hs200(card) ? "HS200 " : ""),
mmc_card_ddr_mode(card) ? "DDR " : "",
uhs_bus_speed_mode, type, card->rca);
}
// 在这里会打印出card信息的字符串
// eg:mmc0: new HS200 MMC card at address 0001
/* 设置card的debug节点 */
#ifdef CONFIG_DEBUG_FS
mmc_add_card_debugfs(card);
// 创建card对应的debug节点,对应路径例如:/sys/kernel/debug/mmc0/mmc0:0001
#endif
mmc_init_context_info(card->host); // 初始化同步的文本信息
/* 以下使能card device的pm runtime的功能 */
ret = pm_runtime_set_active(&card->dev);
if (ret)
pr_err("%s: %s: failed setting runtime active: ret: %d\n",
mmc_hostname(card->host), __func__, ret);
else if (!mmc_card_sdio(card) && mmc_use_core_runtime_pm(card->host))
pm_runtime_enable(&card->dev);
/* 添加到设备驱动模型中 */
ret = device_add(&card->dev);
// 会创建/sys/bus/mmc/devices/mmc0:0001节点和/sys/class/mmc_host/mmc0/mmc0:0001节点
/* 使能异步device suspend,初始化runtime_pm_timeout属性 */
device_enable_async_suspend(&card->dev);
if (mmc_use_core_runtime_pm(card->host) && !mmc_card_sdio(card)) {
card->rpm_attrib.show = show_rpm_delay;
card->rpm_attrib.store = store_rpm_delay;
sysfs_attr_init(&card->rpm_attrib.attr);
card->rpm_attrib.attr.name = "runtime_pm_timeout";
card->rpm_attrib.attr.mode = S_IRUGO | S_IWUSR;
ret = device_create_file(&card->dev, &card->rpm_attrib);
if (ret)
pr_err("%s: %s: creating runtime pm sysfs entry: failed: %d\n",
mmc_hostname(card->host), __func__, ret);
/* Default timeout is 10 seconds */
card->idle_timeout = RUNTIME_SUSPEND_DELAY_MS;
}
/* 设置mmc card的state标识 */
mmc_card_set_present(card);
// 设置card的MMC_STATE_PRESENT状态
// #define MMC_STATE_PRESENT (1<<0) /* present in sysfs */
// 表示card已经合入到sysfs中了
return 0;
}
如上所示:device_add会调用probe函数,即mmc_blk_probe。