一个完整的驱动,需要提供如下的东西,
第一,用户空间/dev下面的设备节点。当然,如果该设备仅仅是内核的使用,例如I2C,则不需要在/dev下面建立设备节点。
第二,驱动程序,就是能到映射到/dev下面的fopen等系列操作。
中间有些负责,不过这些基本的东西都还是能够找到,具体细节上的联系,还需要后面认真分析。从简单的sd卡驱动来看这些内容都是可以找到的。
add_disk(md->disk)----》最终会向/dev下面添加节点。
md = mmc_blk_alloc(card); ----》会连接上实际的fopen等系列操作
-----------------------------------------------------华丽的分割线-------------------------------------------------------------------------
SD卡属于块设备,card驱动部分为了将SD卡驱动成为块设备。介绍该部分的内容时先介绍驱动结构体和接口函数结构体,然后介绍几个关键的驱动函数。
1.驱动的结构体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,
};
2.块设备操作结构体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.块设备探针函数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;
}
4.驱动的入口函数mmc_blk_init()
加载驱动时该函数被调用,该函数向内核申请注册一个块设备,然后进入核心层进行注册。
static int __init mmc_blk_init(void)
{
/*向内核申请注册一个块设备*/
res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");
/*进入核心层进行注册*/
mmc_register_driver(&mmc_driver);
return 0;
}
5.为块设备分配空间函数mmc_blk_alloc()
函数mmc_blk_alloc()为块设备分配空间,并初始化一个请求队列,设置设备队列的sector大小。
static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
{
struct mmc_blk_data *md;
int devidx, ret;
/*在内存中查找第一个被清理过的bit*/
devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS);
if (devidx >= MMC_NUM_MINORS)
return ERR_PTR(-ENOSPC);
/*从地址 dev_use开始设置bit,设置为devidx*/
__set_bit(devidx, dev_use);
/*分配结构体mmc_blk_data空间并初始化*/
md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL);
/*设置卡的状态为只读*/
md->read_only = mmc_blk_readonly(card);
/*分配设备的次设备号为8*/
md->disk = alloc_disk(1 << MMC_SHIFT);
}
spin_lock_init(&md->lock);
md->usage = 1;
/*初始化一个请求队列,并将该队列与卡关联*/
mmc_init_queue(&md->queue, card, &md->lock);
/*注册 mmc_blk_issue_rq到md->queue,当md->queue上
有request待处理时, mmc_blk_issue_rq就会被调用*/
md->queue.issue_fn = mmc_blk_issue_rq;
md->queue.data = md;
/*注册相关的mmc_blk _data包含的块设备区*/
md->disk->major = MMC_BLOCK_MAJOR;
md->disk->first_minor = devidx << MMC_SHIFT;
md->disk->fops = &mmc_bdops;
md->disk->private_data = md;
md->disk->queue = md->queue.queue;
md->disk->driverfs_dev = &card->dev;
sprintf(md->disk->disk_name, "mmcblk%d", devidx);
/*设置传输sector大小*/
blk_queue_hardsect_size(md->queue.queue, 512);
/*根据卡的类型设置容量*/
if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
/*
* The EXT_CSD sector count is in number or 512 byte
* sectors.
*/
set_capacity(md->disk, card->ext_csd.sectors);
} else {
/*
* The CSD capacity field is in units of read_blkbits.
* set_capacity takes units of 512 bytes.
*/
set_capacity(md->disk,
card->csd.capacity << (card->csd.read_blkbits - 9));
}
return md;
}
在该驱动部分还包括一些对块操作的函数,如mmc_blk_open()、mmc_blk_get()、mmc_blk_put()、mmc_blk_release()和mmc_blk_getgeo()
转于:http://edu.codepub.com/2010/0611/23407_2.php 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;
|