软件世界网 购物 网址 三丰软件 | 小说 美女秀 图库大全 游戏 笑话 | 下载 开发知识库 新闻 开发 图片素材
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
移动开发 架构设计 编程语言 Web前端 互联网
开发杂谈 系统运维 研发管理 数据库 云计算 Android开发资料
  软件世界网 -> 移动开发 -> LinuxSD卡驱动开发(三)——SD卡驱动分析CORE篇 -> 正文阅读

[移动开发]LinuxSD卡驱动开发(三)——SD卡驱动分析CORE篇


       废话不多说,直接切进主题:
       Linux在内核源码的drivers/mmc/core文件夹下为我们的提供了一系列SD卡的接口服务函数。可以查看Makefile如下
[img]http://img.blog.csdn.net/20160402092639852?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
可见,core文件夹下有针对总线的服务bus.c,针对主控制器的服务host.c,针对SD卡的服务sd.c, sd_ops.c等等。
其中,最为核心的一个函数便是之前提到的位于core.c的mmc_rescan,概括来讲,主要完成两项任务,即
扫描SD总线,插入SD卡
扫描SD总线,拔出SD卡

一、 插入SD卡
        前面HOST篇最后的中断篇中讲到,插入SD卡,主控制器产生中断,进入中断处理函数s3cmci_irq_cd,其中调用的函数 mmc_detect_change,它将最终调用queue_delayed_work执行工作队列里的mmc_rescan函数
下面来看看 mmc_rescan
void mmc_rescan(struct work_struct *work)
{
	struct mmc_host *host =
		container_of(work, struct mmc_host, detect.work);
	int i;

	if (host->rescan_disable)
		return;

	/* If there is a non-removable card registered, only scan once */
	if ((host->caps & MMC_CAP_NONREMOVABLE) && host->rescan_entered)
		return;
	host->rescan_entered = 1;

	mmc_bus_get(host);

	/*
	 * if there is a _removable_ card registered, check whether it is
	 * still present
	 */
	if (host->bus_ops && host->bus_ops->detect && !host->bus_dead
	    && !(host->caps & MMC_CAP_NONREMOVABLE))
		host->bus_ops->detect(host);

	host->detect_change = 0;

	/*
	 * Let mmc_bus_put() free the bus/bus_ops if we've found that
	 * the card is no longer present.
	 */
	mmc_bus_put(host);
	mmc_bus_get(host);

	/* if there still is a card present, stop here */
	if (host->bus_ops != NULL) {
		mmc_bus_put(host);
		goto out;
	}

	/*
	 * Only we can add a new handler, so it's safe to
	 * release the lock here.
	 */
	mmc_bus_put(host);

	if (!(host->caps & MMC_CAP_NONREMOVABLE) && host->ops->get_cd &&
			host->ops->get_cd(host) == 0) {
		mmc_claim_host(host);
		mmc_power_off(host);
		mmc_release_host(host);
		goto out;
	}

	mmc_claim_host(host);
	for (i = 0; i < ARRAY_SIZE(freqs); i++) {
		if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
			break;
		if (freqs[i] <= host->f_min)
			break;
	}
	mmc_release_host(host);

 out:
	if (host->caps & MMC_CAP_NEEDS_POLL)
		mmc_schedule_delayed_work(&host->detect, HZ);
}

      插入SD卡,mmc_rescan扫描SD总线上是否存在SD卡,具体的实现方法就是通过向SD卡上电,看是否能成功,以普通SD卡为例,为普通SD卡上电的函数mmc_send_app_op_cond(host, 0, &ocr);
如果上电成功,则返回0,即执行if()里的mmc_attach_sd()进行总线与SD卡的绑定
如果上电失败,则返回非0值,跳过if(),尝试其他上电的方法。
     那么,上电方法究竟有何不同呢?具体看看mmc_send_app_op_cond()的实现过程
int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{
	struct mmc_command cmd;
	cmd.opcode = SD_APP_OP_COND;    /* #define SD_APP_OP_COND   41   */
	mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES);

	... ...

}
int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card, struct mmc_command *cmd, int retries)
{

  mmc_app_cmd(host, card);   /* #define MMC_APP_CMD   55   */
  mrq.cmd = cmd;
  cmd->data = NULL;

  mmc_wait_for_req(host, &mrq);

  ... ...

}

这里的指令SD_APP_OP_COND只有SD2.0的协议支持,也就是说,只有普通SD卡支持,所以也只有普通SD卡能够成功上电。 [img]http://img.blog.csdn.net/20160402093350792?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
             [img]http://img.blog.csdn.net/20160402093424105?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
如果上电成功,就开始进行总线与SD卡的绑定,通过mmc_attach_sd(),绑定过程可分为四步,
注册SD总线上的操作函数 - struct mmc_bus_ops mmc_sd_ops
设置主控制器的时钟和总线方式 - 通过回调函数host->ops->set_ios();
启动SD卡 - 根据协议,完成SD卡启动的各个步骤
注册SD卡设备驱动


二、注册总线上的操作函数
int mmc_attach_sd(struct mmc_host *host, u32 ocr)
{
	mmc_sd_attach_bus_ops(host);

 	... ...

}

static void mmc_sd_attach_bus_ops(struct mmc_host *host)
{
	const struct mmc_bus_ops *bus_ops;

	bus_ops = &mmc_sd_ops;
	mmc_attach_bus(host, bus_ops);
}
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
{
	host->bus_ops = ops;
	host->bus_refs = 1;
	host->bus_dead = 0;
}
static const struct mmc_bus_ops mmc_sd_ops = {
	.remove = mmc_sd_remove,  // 拔出SD卡的操作函数
	.detect = mmc_sd_detect,      // 探测SD卡的操作函数
	.suspend = NULL,
	.resume = NULL,
	.power_restore = mmc_sd_power_restore,  // 重新启动SD卡的操作函数
};

     这里的mmc_sd_detect和mmc_sd_remove就是拔出SD卡所需要用到的函数,下文将详细讨论。这里需要注意的是,插入SD卡的时候,并不执行mmc_sd_detect和mmc_sd_remove这两个函数,但是会注册它们,也就是说,这两个函数的功能已经实现,将来可以使用。
三、设置时钟和总线
int mmc_attach_sd(struct mmc_host *host, u32 ocr)
{
	host->ocr = mmc_select_voltage(host, ocr);

	... ...

}

u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
{
	mmc_set_ios(host);

	... ...
}

static inline void mmc_set_ios(struct mmc_host *host)
{
	struct mmc_ios *ios = &host->ios;

	host->ops->set_ios(host, ios);  // 设置主控制器时钟和总线的回调函数,具体实现由主控制器驱动完成
}

    从这里可以体会到回调函数的精髓:协议层里利用回调函数为所有满足该协议的设备提供统一的接口,而具体实现由底层不同的设备驱动各自完成。注意到,之所以要定义一些放之四海而皆准的公用的类,比如struct mmc_host,就是需要通过struct mmc_host *host指针作为形参传到协议层所提供的接口函数中,从而得以调用。


四、启动SD卡
int mmc_attach_sd(struct mmc_host *host, u32 ocr)
{

	mmc_sd_init_card(host, host->ocr, NULL);

	... ...

}

 mmc_sd_init_card主要完成以下任务,
SD卡的启动过程
得到寄存器CID, CSD, SCR, RCA的数据
其他操作比如切换到高速模式,初始化card


static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, struct mmc_card *oldcard)
{
	
	/* SD卡的启动过程 */
	mmc_go_idle(host);
	mmc_send_if_cond(host, ocr);
	mmc_send_app_op_cond(host, ocr, NULL);
	mmc_all_send_cid(host, cid);
	mmc_send_relative_addr(host, &card->rca);
	
	/* 得到寄存器CID, CSD, SCR的数据 */
	mmc_send_csd(card, card->raw_csd);
	mmc_decode_csd(card);
	mmc_decode_cid(card);
	mmc_app_send_scr(card, card->raw_scr);
	mmc_decode_scr(card);

	/* 其它操作 */
	mmc_alloc_card(host, &sd_type);
	mmc_select_card(card); 
	mmc_read_switch(card);
	mmc_switch_hs(card);
	... ...

}



1) SD卡的启动过程
    根据SD2.0协议,SD卡的状态可分为两种模式:卡识别模式(card-identification mode)和数据传输模式(data-transfer mode)。这里,我们关注启动SD卡的卡识别模式。
[img]http://img.blog.csdn.net/20160402095313628?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

综合代码:
  mmc_go_idle(host);                     CMD0
  Idle State
  mmc_send_if_cond(host, ocr);     CMD8
  mmc_send_app_op_cond(host, ocr, NULL);       ACMD41
  Ready State
  mmc_all_send_cid(host, cid);       CMD2
  Identification State
  mmc_send_relative_addr(host, &card->rca);     CMD3
  Stand-by State

2) 寄存器CID, CSD, SCR, RCA
-> 发送指令并得到寄存器的值
   当主控制器向SD卡发送cmd指令,比如mmc_send_cid(card, card->raw_cid),请求得到SD卡CID寄存器的值,当主控制器发送cmd完成后,芯片产生一个内部中断,处理结束cmd的中断函数,之后得到来自SD卡的response,即CID寄存器的值,存放于host->cmd->resp[i]中。关于内部中断处理,参看上文的中断一节里的 mmc_wait_for_cmd()
   mmc_send_cid(card, card->raw_cid);这个函数发送了接收CSD寄存器的请求,并且得到了来自SD卡的CSD寄存器的值。
int mmc_send_cid(struct mmc_card *card, u32 *cid)
{
   return mmc_send_cxd_native(card->host, card->rca << 16, cid, MMC_SEND_CID);

}

static int mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode)
{
 cmd.opcode = opcode;
 cmd.arg = arg;
 cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;

 mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);

 memcpy(cxd, cmd.resp, sizeof(u32) * 4);  // 得到response赋给cxd,即card->raw_cid

 ... ...
}

-> 解析寄存器的值
    为什么要解析?先来看看寄存器CID在SD卡协议里的定义,它是一个128位的寄存器,存放了关于这块SD卡的基本信息,就像自己的身份证。通过mmc_send_cid()将这个寄存器的数值赋给了card->raw_cid (定义 u32 raw_cid[4];) ,为了方便得到具体某一个信息,协议层为我们解析了寄存器里的域,并赋给card->cid,比如厂商名称,就可以通过card->cid.manfid直接读取到。
[img]http://img.blog.csdn.net/20160402095445879?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
static int mmc_decode_cid(struct mmc_card *card)
{
  u32 *resp = card->raw_cid;

  card->cid.manfid = UNSTUFF_BITS(resp, 120, 8);
  card->cid.oemid  = UNSTUFF_BITS(resp, 104, 16);
  card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8);
  card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8);
  card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8);
  card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8);
  card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8);
  card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8);
  card->cid.serial = UNSTUFF_BITS(resp, 16, 32);
  card->cid.month  = UNSTUFF_BITS(resp, 12, 4);
  card->cid.year  = UNSTUFF_BITS(resp, 8, 4) + 1997;
  return 0;
}

五、 注册SD卡设备驱动
 int mmc_attach_sd(struct mmc_host *host, u32 ocr)
{

  /* mmc_alloc_card(host, &sd_type); 在mmc_sd_init_card()已完成 */

  mmc_add_card(host->card);

  ... ...

}

上文已经提到,设备驱动程序都会通过alloc_xxx()和add_xxx()两步来注册驱动,其实质是调用/drivers/base/core.c里的device_initialize()和device_add(),device_add()完成建立kobject,sys文件,发送uevent,等工作。
六、拔出SD卡
 void mmc_rescan(struct work_struct *work)
{
 struct mmc_host *host = container_of(work, struct mmc_host, detect.work);
 mmc_bus_get(host);

 /* if there is a card registered, check whether it is still present */
 if ((host->bus_ops != NULL) && host->bus_ops->detect && !host->bus_dead)
  host->bus_ops->detect(host);

 mmc_bus_put(host);

 ... ...

}

这里的mmc_bus_get/put(),为SD总线加上一个自旋锁,规定同时只能有一个线程在SD总线上操作。
1、 bus_ops->detect()
       mmc_rescan()扫描SD总线,如果发现host->ops上赋了值,即之前已有SD卡注册过,就执行bus_ops->detect()操作去探测SD总线上是否还存在SD卡,如果不存在了,就执行bus_ops->remove()拔出SD卡。之前已经提到,这个bus_ops->detect()已在mmc_attach_sd()注册完成了。
static void mmc_sd_detect(struct mmc_host *host)
{
 mmc_claim_host(host);

 /*
  * Just check if our card has been removed.
  */
 err = mmc_send_status(host->card, NULL);

 mmc_release_host(host);

 if (err) {
  mmc_sd_remove(host);

  mmc_claim_host(host);
  mmc_detach_bus(host);
  mmc_release_host(host);
 }
}

这里的mmc_claim_host(host)通过set_current_state(TASK_RUNNING);将当前进程设置为正在运行进程。
mmc_send_status()发送得到SD卡状态的请求,如果未能得到状态数据,则执行mmc_sd_remove(host)拔出SD卡。
int mmc_send_status(struct mmc_card *card, u32 *status)
{
 struct mmc_command cmd;

 cmd.opcode = MMC_SEND_STATUS;    /* #define MMC_SEND_STATUS   13 */
 cmd.arg = card->rca << 16;
 cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;

 err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);

 if (err)               
  return err;           // 接收来自SD卡的response失败,即没有发现SD卡
 if (status)
  *status = cmd.resp[0];

 return 0;

}

2、bus_ops->remove()
      拔出SD卡,其实就是注册SD卡驱动的反操作,实质就是执行device_del()和device_put()
static void mmc_sd_remove(struct mmc_host *host)
{
 mmc_remove_card(host->card);
 host->card = NULL;
}
void mmc_remove_card(struct mmc_card *card)
{
 if (mmc_card_present(card))
  device_del(&card->dev);

 put_device(&card->dev);
}

......显示全文...
    点击查看全文


上一篇文章           查看所有文章
2016-04-03 20:41:06  
移动开发 最新文章
深入了解android中的消息机制Handler
Android
Libgdx之BitmapFont字体
AndroidApp发布到应用市场的流程
Android开发找工作之前先看看这些知识点吧
View的事件分发机制解析
简单介绍了解白鹭引擎Egret
Cocos2d
android获取本地图片(二)
动画特效七:碰撞动画
360图书馆 软件开发资料 文字转语音 购物精选 软件下载 美食菜谱 新闻资讯 电影视频 小游戏 Chinese Culture 股票 租车
生肖星座 三丰软件 视频 开发 短信 中国文化 网文精选 搜图网 美图 阅读网 多播 租车 短信 看图 日历 万年历 2018年1日历
2018-1-16 17:34:08
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  软件世界网 --