精品博客
http://chxxxyg.blog.163.com/blog/#m=0
(1)SD卡驱动移植
在文件linux/arch/arm/mach-s3c2440/mach-smdk2440.c中做如下几处修改:
添加都文件:

填充如下结构:设备拔插检测管脚是GPG8,写保护管脚直接接地了,所以无写保护管脚。
添加如下函数:这个函数将上面添加结构体sd_cfg设为SD卡平台设备结构s3c_device_sdi的platform_data。
将内核编译下载到内存出现信息如下:
在/dev目录下产生设备节点/dev/mmcblk0p1,挂载并查看SD卡中的内容。
(二)SD卡驱动原理浅析
设备模型建立流程图




SD卡平台设备驱动在文件linux/drivers/mmc/host/s3cmci.c中实现。这里直接从SD卡平台设备驱动的探测函数开始说起吧。
tatic int __devinit s3cmci_probe(struct platform_device *pdev)
{
………………………………
//分配MMC驱动核心结构mmc_host,并为s3cmci_host分配内存而且将结构
//s3cmci_host置为mmc_host的private。还做了一个重要初始化
//INIT_DELAYED_WORK(&host->detect, mmc_rescan),后面将会遇到。
mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);
………………………………
//底半部机制,将在数据收发中断函数中触发,它的处理函数是pio_tasklet()
tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
………………………………
//申请MMC主控制器中断通道,中断处理函数s3cmci_irq()
if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) {
……………………
}
if (!host->pdata->no_detect) {
……………………
//申请设备插拔探测中断,中断处理函数s3cmci_irq_cd()
if (request_irq(host->irq_cd, s3cmci_irq_cd,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
DRIVER_NAME, host)) {
……………………
} else {
……………………
}
} else
host->irq_cd = -1;
……………………
//如果使用DMA进行数据传输,则请求一个DMA通道
if (s3cmci_host_usedma(host)) {
host->dma = s3c2410_dma_request(DMACH_SDI, &s3cmci_dma_client,
host);
…………………………
}
……………………
//结构体s3cmci_ops中包含几个操作函数,有管脚配置函数s3cmci_set_ios(),
//数据请求处理函数s3cmci_request()等
mmc->ops = &s3cmci_ops;
…………………………
//将设备host->class_dev添加到内核
ret = mmc_add_host(mmc);
}
当有卡插入卡槽将会触发设备探测中断,也就是自定义的一个外部中断。然后调用中断处理函数s3cmci_irq_cd()。
static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id)
{
mmc_detect_change(host->mmc, msecs_to_jiffies(500));
}
void mmc_detect_change(struct mmc_host *host, unsigned long delay)
{
…………………………
//调度延时work
mmc_schedule_delayed_work(&host->detect, delay);
}
当延时时间到将调用work处理函数mmc_rescan()。
void mmc_rescan(struct work_struct *work)
{
…………………………
//发送CMD55和CMD41识别卡的类型
err = mmc_send_app_op_cond(host, 0, &ocr);
if (!err) {//如果命令发送成功,即识别该卡为SD卡
//卡相关信息读取,和一些结构的初始化以及卡设备的建立与添加
if (mmc_attach_sd(host, ocr))
mmc_power_off(host);
goto out;
}
………………………………
}
int mmc_attach_sd(struct mmc_host *host, u32 ocr)
{
………………………………
//下面函数最终会调用函数host->ops->set_ios(host, ios),这就是上面所讲操作函
//数集中的管脚配置函数。
host->ocr = mmc_select_voltage(host, ocr);
………………………………
//下面函数将会分配一个 mmc_card结构,还会将SD卡设备card->dev的设备总
//线初始化为mmc_bus_type。card->dev.bus = &mmc_bus_type;
err = mmc_sd_init_card(host, host->ocr, NULL);
………………………………
//下面函数将会将SD卡设备添加到内核device_add(&card->dev)。由于拥有总线//的设备往往都有自己对应的驱动,一旦设备添加到内核就将去匹配这个驱动。
err = mmc_add_card(host->card);
………………………………
}
与上面添加到内核的SD卡驱动在文件linux/drivers/mmc/card/block.c中实现。
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,
};
当设备与驱动匹配成功将调用它的探测函数mmc_blk_probe()。
static int mmc_blk_probe(struct mmc_card *card)
{
………………………………
//这个函数完成了很多重要工作,如后面讲述
md = mmc_blk_alloc(card);
/*
向SD卡发送 CMD16命令,命令参数就是SD卡BLOCK的大小,这个命令参数就将被写入SD卡CSD寄存器的WRITE_BL_LEN中。读数据块大小不用设置,它是固定的,数据手册中有这么一句:块是数据传输的最小单位, 在CSD (READ_BL_LEN)中定义, SD卡为固定的512B.
*/
err = mmc_blk_set_blksize(md, card);
………………………………
//激活SD卡对应块设备
add_disk(md->disk);
………………………………
}
static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
{
………………………………
//分配SD卡对应的块设备结构gendisk
md->disk = alloc_disk(1 << MMC_SHIFT);
//下面函数做了很多重要工作后面讲述
ret = mmc_init_queue(&md->queue, card, &md->lock);
//函数mmc_blk_issue_rq()是实际进行的数据请求处理函数,将由内核线程处理函//数调用。
md->queue.issue_fn = mmc_blk_issue_rq;
md->queue.data = md;
…………………………
}
int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock)
{
………………………………
//初始化一个请求队列,请求处理函数mmc_request()主要工作就是
//唤醒内核线程mq->thread
mq->queue = blk_init_queue(mmc_request, lock);
mq->queue->queuedata = mq;
………………………………
#ifdef CONFIG_MMC_BLOCK_BOUNCE
if (host->max_hw_segs == 1) {
/*
如果上层传递来的数据段的段数和向下层传输的数据段的段数不一样,就需要进行数据缓冲,mq->bounce_buf和mq->bounce_sg就是为此而设的。真正向下层传输的数据是挂载在mq->sg上的。当上层有多端数据传来时,首先将这传来的多端数据挂到可以承载多个数据段的mq->bounce_sg上,然后将这些数据段中的数据拷贝到缓存mq->bounce_buf中,最后将缓存mq->bounce_buf作为一段数据挂载到mq->sg上向下传递。
*/
………………………………
mq->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
………………………………
mq->sg = kmalloc(sizeof(struct scatterlist),
GFP_KERNEL);
sg_init_table(mq->sg, 1);
mq->bounce_sg = kmalloc(sizeof(struct scatterlist) *
bouncesz / 512, GFP_KERNEL);
………………………………
sg_init_table(mq->bounce_sg, bouncesz / 512);
}
}
#endif
if (!mq->bounce_buf) {
………………………………
//如果允许多段数据向下层传输,就可以直接将上层传来的多段数据挂到//mq->sg上
mq->sg = kmalloc(sizeof(struct scatterlist) *
host->max_phys_segs, GFP_KERNEL);
………………………………
sg_init_table(mq->sg, host->max_phys_segs);
}
//创建内核线程,请求的处理最终都是通过该线程的处理函数
//mmc_queue_thread()调用函数mq->issue_fn(mq, req);来实现的。
mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd");
………………………………
}
………………………………
}
当有数据请求到来,请求队列的请求处理函数将被调用:
static void mmc_request(struct request_queue *q)
{
………………………………
if (!mq->req) //唤醒内核线程
wake_up_process(mq->thread);
}
内核线程一旦被唤醒就将调用它的线程处理函数
static int mmc_queue_thread(void *d)
{
………………………………
do {
………………………………
//如果是一个有效的请求将调用如下函数,下面函数就是函数mmc_blk_issue_rq()
mq->issue_fn(mq, req);
} while (1);
………………………………
}
static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
{
………………………………
return mmc_blk_issue_rw_rq(mq, req);
}
static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
{
………………………………
do {
………………………………
//如果该请求的数据方向是写,则把挂到反散列表mq->bounce_sg上的本次请求//的数据缓存考到缓存mq->bounce_buf上。mq->bounce_buf已被挂到了mq->sg
//上,通过mq->sg传递给下层函数
mmc_queue_bounce_pre(mq);
//请求发送并等待发送结束
mmc_wait_for_req(card->host, &brq.mrq);
//如果该请求的数据方向是读,则把mq->bounce_buf(已包含读到的数据)
//拷贝到mq->bounce_sg所挂载的请求缓存上
mmc_queue_bounce_post(mq);
………………………………
} while (ret);
………………………………
}
初始化一个完成量,等待请求发送结束
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);
}
static void mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
………………………………
//在平台设备驱动的探测函数s3cmci_probe()中有这样的初始化
//mmc->ops = &s3cmci_ops;,下面的函数就是操作函数集s3cmci_ops的数据请求处理函数 s3cmci_request()。这里的mmc和host结构都是mmc_host结构
//类型。
host->ops->request(host, mrq);
}