大家好,又见面了,我是你们的朋友全栈君。
SD/MMC 卡组成的存储系统是许多嵌入设备的主要存储设备,相当于PC机的硬盘,在嵌入设备上的SD/MMC卡控制器通过MMC协议来解析命令控制SD/MMC卡的 操作。SD/MMC卡上有一些寄存器来控制卡的状态及读写操作。MMC协议规定的寄存器有:CID寄存器,128位,是卡的鉴别寄存器,存有卡的鉴别信 息;RCA寄存器是16位,存有卡的本地系统的相对地址,在初始化时由控制器动态指定。DSR寄存器是16位,是配置卡的驱动程序的寄存器,是可选的。 CSD寄存器是卡特定数据信息描述寄存器,是可选的。OCR寄存器是操作控制寄存器。MMC卡的系统定义及相关协议请查询《MMC卡系统定义3.1版 本》。
MMC驱动程序以分通用设备层、 MMC抽象设备层、MMC协议层和具体设备层四层来构建,上一层抽象出下一层的共有特性,每一层以相应的结构来描述。通用设备层对于块设备来说,主要负责 设备内核对象在sysfs文件系统中的管理、请求队列管理、及与文件系统的接口,MMC抽象设备层抽出MMC卡的共有特性,如: MMC卡的请求管理、电源管理等。MMC协议层将MMC操作分解成标准的MMC协议,具体设备层则负责具体物理设备的寄存器控制等。这种分层结构层次分 明,管理有效。MMC驱动程序的层次结构如下图。
MMC驱动程序主要处理两部分的内容,一是创建通用硬盘结构向系统注册,以便系统对MMC设备的管理。另一方面,要完成系统分发过来的读写请求的处理。
图 MMC驱动程序的层次结构
MMC抽象设备层相关结构
(1)设备描述结构
图 MMC卡设备相关结构关系图
MMC设备由控制器及插卡组成,对应的设备结构为mmc_host结构和mmc_card结构。MMC卡设备相关结构关系图如上图。下面分别说明设备相关结构:
每个卡的插槽对应一个块的数据结构mmc_blk_data,结构列出如下(在drivers/mmc/mmc_block.c中):
struct mmc_blk_data {
spinlock_t lock;
struct gendisk * disk; //通用硬盘结构
struct mmc_queue queue; //MMC请求队列结构
unsigned int usage;
unsigned int block_bits; //卡每一块大小所占的bit位
} ;
结构mmc_card是一个插卡的特性描述结构,它代有了一个插卡。列出如下(在include/linux/mmc/card.h中):
struct mmc_card {
struct list_head node; //在主设备链表中的节点
struct mmc_host * host; // 卡所属的控制器
struct device dev; //通用设备结构
unsigned int rca; //设备的相对本地系统的地址
unsigned int state; //卡的状态
#define MMC_STATE_PRESENT (1<<0) //卡出现在sysfs文件系统中
#define MMC_STATE_DEAD (1<<1) //卡不在工作状态
#define MMC_STATE_BAD (1<<2) //不认识的设备
u32 raw_cid[ 4 ] ; /* raw card CID */
u32 raw_csd[ 4 ] ; /* raw card CSD */
struct mmc_cid cid; //卡的身份鉴别,值来自卡的CID寄存器
struct mmc_csd csd; //卡特定信息,值来自卡的CSD寄存器
} ;
结构mmc_host描述了一个MMC卡控制器的特性及操作等,结构mmc_host列出如下(在include/linux/mmc/host.h中):
struct mmc_host {
struct device * dev; //通用设备结构
struct mmc_host_ops * ops; //控制器操作函数集结构
unsigned int f_min;
unsigned int f_max;
u32 ocr_avail; //卡可用的OCR寄存器值
char host_name[ 8 ] ; //控制器名字
//主控制器中与块层请求队列相关数据
unsigned int max_seg_size; //最大片断的尺寸
unsigned short max_hw_segs; //最大硬件片断数
unsigned short max_phys_segs; //最大物理片断数
unsigned short max_sectors; //最大扇区数
unsigned short unused;
//私有数据
struct mmc_ios ios; //当前i/o总线设置
u32 ocr; //当前的OCR设置
struct list_head cards; //接在这个主控制器上的设备
wait_queue_head_t wq; //等待队列
spinlock_t lock; //卡忙时的锁
struct mmc_card * card_busy; //正与主控制器通信的卡
struct mmc_card * card_selected; //选择的MMC卡
struct work_struct detect; //工作结构
} ;
结构mmc_host_ops是控制器的操作函数集,它包括请求处理函数指针和控制器对卡I/O的状态的设置函数指针,结构mmc_host_ops列出如下:
struct mmc_host_ops {
void ( * request) ( struct mmc_host * host, struct mmc_request * req) ;
void ( * set_ios) ( struct mmc_host * host, struct mmc_ios * ios) ;
}
结构mmc_ios描述了控制器对卡的I/O状态,列出如下:
struct mmc_ios {
unsigned int clock; //时钟频率
unsigned short vdd;
unsigned char bus_mode; //命令输出模式
unsigned char power_mode; //电源供应模式
} ;
结构mmc_driver是MMC设备驱动程序结构,列出如下:
struct mmc_driver {
struct device_driver drv;
int ( * probe) ( struct mmc_card * ) ;
void ( * remove) ( struct mmc_card * ) ;
int ( * suspend) ( struct mmc_card *, u32) ;
int ( * resume) ( struct mmc_card * ) ;
} ;
(2) 读写请求相关结构
图 MMC卡读写请求结构示意图
对 于MMC卡的操作是通过MMC请求结构mmc_request的传递来完成的,来自系统块层的读写请求到达MMC设备抽象层时,用系统的读写请求填充初始 化MMC卡的读写请求,经MMC协议分解后,然后把请求发给设备,再调用具体设备的请求处理函数来完成请求的处理。MMC卡读写请求结构示意图中上图。下 面分析MMC卡读写请求相关结构:
结构mmc_request描述了读写MMC卡的请求,它包括命令、数据及请求完成后的回调函数。结构mmc_request列出如下(在include/linux/mmc/mmc.h中):
struct mmc_request {
struct mmc_command * cmd;
struct mmc_data * data;
struct mmc_command * stop;
void * done_data; //回调函数的参数
void ( * done) ( struct mmc_request * ) ; //请求完成的回调函数
} ;
结构mmc_queue是MMC的请求队列结构,它封装了通用请求队列结构,加入了MMC卡相关结构,结构mmc_queue列出如下(在drivers/mmc/mmc_queue.h中):
struct mmc_queue {
struct mmc_card * card; //MMC卡结构
struct completion thread_complete; //线程完成结构
wait_queue_head_t thread_wq; //等待队列
struct semaphore thread_sem;
unsigned int flags;
struct request * req; //通用请求结构
int ( * prep_fn) ( struct mmc_queue *, struct request * ) ;
//发出读写请求函数
int ( * issue_fn) ( struct mmc_queue *, struct request * ) ;
void * data;
struct request_queue * queue; //块层通用请求队列
struct scatterlist * sg; //碎片链表
} ;
结构mmc_data描述了MMC卡读写的数据相关信息,如:请求、操作命令、数据及状态等。结构mmc_data列出如下(在include/linuc/mmc/mmc.h中):
struct mmc_data {
unsigned int timeout_ns; //数据超时( ns,最大80ms)
unsigned int timeout_clks; //数据超时(以时钟计数)
unsigned int blksz_bits; //数据块大小的bit位
unsigned int blocks; //块数
unsigned int 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; //停止命令
struct mmc_request * mrq; //相关的请求
unsigned int sg_len; //碎片链表的长度
struct scatterlist * sg; // I/O碎片链表指针
} ;
结构mmc_command描述了MMC卡操作相关命令及数据、状态信息等,结构列出如下:
struct mmc_command {
u32 opcode;
u32 arg;
u32 resp[ 4 ] ;
unsigned int flags; //期望的反应类型
#define MMC_RSP_NONE (0 << 0)
#define MMC_RSP_SHORT (1 << 0)
#define MMC_RSP_LONG (2 << 0)
#define MMC_RSP_MASK (3 << 0)
#define MMC_RSP_CRC (1 << 3) /* expect valid crc */
#define MMC_RSP_BUSY (1 << 4) /* card may send busy */
/*
* These are the response types, and correspond to valid bit
* patterns of the above flags. One additional valid pattern
* is all zeros, which means we don't expect a response.
*/
#define MMC_RSP_R1 (MMC_RSP_SHORT|MMC_RSP_CRC)
#define MMC_RSP_R1B (MMC_RSP_SHORT|MMC_RSP_CRC|MMC_RSP_BUSY)
#define MMC_RSP_R2 (MMC_RSP_LONG|MMC_RSP_CRC)
#define MMC_RSP_R3 (MMC_RSP_SHORT)
unsigned int retries; /* max number of retries */
unsigned int error; /* command error */
#define MMC_ERR_NONE 0
#define MMC_ERR_TIMEOUT 1
#define MMC_ERR_BADCRC 2
#define MMC_ERR_FIFO 3
#define MMC_ERR_FAILED 4
#define MMC_ERR_INVALID 5
struct mmc_data * data; //与命令相关的数据片断
struct mmc_request * mrq; //与命令相关的请求
} ;
MMC抽象设备层MMC块设备驱动程序
(1)MMC块设备驱动程序初始化
函 数mmc_blk_init注册一个MMC块设备驱动程序,它先将MMC块设备名注册到名称数组major_names中,然后,还把驱动程序注册到 sysfs文件系统中的总线和设备目录中。一方面,sysfs文件系统中可显示MMC块设备相关信息,另一方面,sysfs文件系统以树形结构管理着 MMC块设备驱动程序。
函数mmc_blk_init分析如下(在drivers/mmc/mmc_block.c中):
static int __init mmc_blk_init( void )
{
int res = - ENOMEM;
//将卡名字mmc和major注册到块设备的名称数组major_names中
res = register_blkdev( major, "mmc" ) ;
if ( res < 0 ) {
printk( KERN_WARNING "Unable to get major %d for MMC media: %d/n " ,
major, res) ;
goto out;
}
if ( major == 0 )
major = res;
//在devfs文件系统中创建mmc目录
devfs_mk_dir( "mmc" ) ;
return mmc_register_driver( & mmc_driver) ;
out:
return res;
}
mmc_driver驱动程序实例声明如下:
static struct mmc_driver mmc_driver = {
.drv = {
.name = "mmcblk" ,
} ,
.probe = mmc_blk_probe, // MMC块设备驱动程序探测函数
.remove = mmc_blk_remove,
.suspend = mmc_blk_suspend,
.resume = mmc_blk_resume,
} ;
函数mmc_register_driver 注册一个媒介层驱动程序。其中参数drv是MMC媒介层驱动程序结构。
函数mmc_register_driver分析如下(在drivers/mmc/mmc_sysfs.c中):
int mmc_register_driver( struct mmc_driver * drv)
{
drv-> drv.bus = & mmc_bus_type;
drv-> drv.probe = mmc_drv_probe;
drv-> drv.remove = mmc_drv_remove;
//把块设备注册到sysfs文件系统中对应的总线及设备目录下
return driver_register( & drv-> drv) ;
}
(2)MMC块设备驱动程序探测函数
图 函数mmc_blk_probe调用层次图
函数mmc_blk_probe是MMC控制器探测函数,它探测MMC控制器是否存在,并初始化控制器的结构,同时,还探测MMC卡的状态并初始化。函数mmc_blk_probe调用层次图如上图。
函数mmc_blk_probe列出如下:
static int mmc_blk_probe( struct mmc_card * card)
{
struct mmc_blk_data * md; //每个插槽一个结构mmc_blk_data
int err;
if ( card-> csd.cmdclass & ~0x1ff )
return - ENODEV;
if ( card-> csd.read_blkbits < 9 ) { //所读的块小于1扇区
printk( KERN_WARNING "%s: read blocksize too small (%u)/n " ,
mmc_card_id( card) , 1 << card-> csd.read_blkbits ) ;
return - ENODEV;
}
//分配每个插槽的卡的块数据结构,初始化了通用硬盘及请求队列
md = mmc_blk_alloc( card) ;
if ( IS_ERR( md) )
return PTR_ERR( md) ;
//设置块大小,发命令设置卡为选中状态
err = mmc_blk_set_blksize( md, card) ;
if ( err)
goto out;
printk( KERN_INFO "%s: %s %s %dKiB/n " ,
md-> disk-> disk_name, mmc_card_id( card) , mmc_card_name( card) ,
( card-> csd.capacity << card-> csd.read_blkbits ) / 1024 ) ;
mmc_set_drvdata( card, md) ; //即card ->driver_data = md
add_disk( md-> disk) ; //向系统注册通用硬盘结构,它包括每分区信息
return 0 ;
out:
mmc_blk_put( md) ;
return err;
}
函数*mmc_blk_alloc给每一插槽分配一个结构mmc_blk_data,并分配设置通用硬盘结构和初始了请求队列结构。
函数*mmc_blk_alloc分析如下(在drivers/mmc/mmc_block.c中):
//最大支持8个控制器,每个控制器可控制4个卡,每个卡最大8个分区。
#define MMC_SHIFT 3 //表示每个卡最大支持8个分区
#define MMC_NUM_MINORS (256 >> MMC_SHIFT) //为256/8=32
//即定义dev_use[32/(8*4)] = devuse[1],一个控制器用32位表示使用情况
static unsigned long dev_use[ MMC_NUM_MINORS/ ( 8 * sizeof ( unsigned long ) ) ] ;
static struct mmc_blk_data * mmc_blk_alloc( struct mmc_card * card)
{
struct mmc_blk_data * md;
int devidx, ret;
//查找dev_use中第一个bit为0的位序号(在MMC_NUM_MINORS位以内)
//找到第个空闲的分区
devidx = find_first_zero_bit( dev_use, MMC_NUM_MINORS) ;
if ( devidx >= MMC_NUM_MINORS)
return ERR_PTR( - ENOSPC) ;
__set_bit( devidx, dev_use) ; //将分区对应的位设置为1,表示使用。
md = kmalloc( sizeof ( struct mmc_blk_data) , GFP_KERNEL) ; //分配对象空间
if ( md) {
memset( md, 0 , sizeof ( struct mmc_blk_data) ) ;
//分配gendisk结构及通用硬盘的分区hd_struct结构,并初始化内核对象
md-> disk = alloc_disk( 1 << MMC_SHIFT) ;
if ( md-> disk == NULL) {
kfree( md) ;
md = ERR_PTR( - ENOMEM) ;
goto out;
}
spin_lock_init( & md-> lock) ;
md-> usage = 1 ;
//初始化请求队列
ret = mmc_init_queue( & md-> queue, card, & md-> lock) ;
if ( ret) {
put_disk( md-> disk) ;
kfree( md) ;
md = ERR_PTR( ret) ;
goto out;
}
//赋上各种请求队列处理函数
md-> queue.prep_fn = mmc_blk_prep_rq; //准备请求
md-> queue.issue_fn = mmc_blk_issue_rq; //发出请求让设备开始处理
md-> queue.data = md;
//初始化通用硬盘
md-> disk-> major = 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;
/*带有永久的块设备可移去的介质应被设置GENHD_FL_REMOVABLE标识,对于永久的介质可移去的块设备不应设置GENHD_FL_REMOVABLE。MMC块设备属于永久介质可移去的块设备,不能设置GENHD_FL_REMOVABLE。用户空间应使用块设备创建/销毁热插拔消息来告诉何时卡存在。*/
sprintf( md-> disk-> disk_name, "mmcblk%d" , devidx) ;
sprintf( md-> disk-> devfs_name, "mmc/blk%d" , devidx) ;
md-> block_bits = card-> csd.read_blkbits ;
//为请求队列设置硬件扇区大小
blk_queue_hardsect_size( md-> queue.queue , 1 << md-> block_bits) ;
set_capacity( md-> disk, card-> csd.capacity ) ; //设置卡的容量
}
out:
return md;
}
(3)MMC卡请求的处理
图 函数mmc_init_queue调用层次图
函数mmc_init_queue初始化一个MMC卡请求队列结构,其中参数mq是mmc请求队列,参数card是加在这个队列里的mmc卡,参数lock是队列锁。函数mmc_init_queue调用层次图如上图。
函数mmc_init_queue分析如下(在drivers/mmc/mmc_queue.c中):
int mmc_init_queue( struct mmc_queue * mq, struct mmc_card * card,
spinlock_t * lock)
{
struct mmc_host * host = card-> host;
u64 limit = BLK_BOUNCE_HIGH;
int ret;
if ( host-> dev-> dma_mask && * host-> dev-> dma_mask)
limit = * host-> dev-> dma_mask;
mq-> card = card;
//初始化块层的请求队列,请求合并策略,赋上请求处理函数mmc_request。
mq-> queue = blk_init_queue( mmc_request, lock) ;
if ( ! mq-> queue)
return - ENOMEM;
//初始化请求队列的扇区及片断限制
blk_queue_prep_rq( mq-> queue, mmc_prep_request) ; //赋上准备请求函数
blk_queue_bounce_limit( mq-> queue, limit) ;
blk_queue_max_sectors( mq-> queue, host-> max_sectors) ;
blk_queue_max_phys_segments( mq-> queue, host-> max_phys_segs) ;
blk_queue_max_hw_segments( mq-> queue, host-> max_hw_segs) ;
blk_queue_max_segment_size( mq-> queue, host-> max_seg_size) ;
mq-> queue-> queuedata = mq;
mq-> req = NULL;
mq-> sg = kmalloc( sizeof ( struct scatterlist) * host-> max_phys_segs,
GFP_KERNEL) ;
if ( ! mq-> sg) {
ret = - ENOMEM;
goto cleanup;
}
init_completion( & mq-> thread_complete) ;
init_waitqueue_head( & mq-> thread_wq) ;
init_MUTEX( & mq-> thread_sem) ;
//创建请求队列处理线程mmc_queue_thread
ret = kernel_thread( mmc_queue_thread, mq, CLONE_KERNEL) ;
if ( ret >= 0 ) {
wait_for_completion( & mq-> thread_complete) ;
init_completion( & mq-> thread_complete) ;
ret = 0 ;
goto out;
}
cleanup:
kfree( mq-> sg) ;
mq-> sg = NULL;
blk_cleanup_queue( mq-> queue) ;
out:
return ret;
}
函数mmc_prep_request 在准备一个MMC请求时做一些状态转移及保护操作,函数列出如下(在drivers/mmc/ mmc_queue.c中):
static int mmc_prep_request( struct request_queue * q, struct request * req)
{
struct mmc_queue * mq = q-> queuedata;
int ret = BLKPREP_KILL;
if ( req-> flags & REQ_SPECIAL) {
//在req->special 中已建立命令块,表示请求已准备好
BUG_ON( ! req-> special) ;
ret = BLKPREP_OK;
} else if ( req-> flags & ( REQ_CMD | REQ_BLOCK_PC) ) {
//块I/O请求需要按照协议进行翻译
ret = mq-> prep_fn( mq, req) ;
} else {
//无效的请求
blk_dump_rq_flags( req, "MMC bad request" ) ;
}
if ( ret == BLKPREP_OK) //请求已准备好,不需再准备了
req-> flags |= REQ_DONTPREP;
return ret;
}
函数mmc_blk_prep_rq是准备请求时调用的函数,这里仅做了简单的保护处理,列出如下(在drivers/mmc/mmc_block.c中):
static int mmc_blk_prep_rq( struct mmc_queue * mq, struct request * req)
{
struct mmc_blk_data * md = mq-> data;
int stat = BLKPREP_OK;
//如果没有设备,没法完成初始化
if ( ! md || ! mq-> card) {
printk( KERN_ERR "%s: killing request - no device/host/n " ,
req-> rq_disk-> disk_name) ;
stat = BLKPREP_KILL;
}
return stat;
}
函数mmc_request是通用MMC请求处理函数,它唤醒请求队列处理线程。它在特定的主控制器上被任何请求队列调用。当主控制器不忙时,我们查找在这个主控制器上的任何一个队列中的请求,并且尝试发出这个请求进行处理。
函数mmc_request分析如下(在driver/mmd/mmc_queue.c中):
static void mmc_request( request_queue_t * q)
{
struct mmc_queue * mq = q-> queuedata;
if ( ! mq-> req) //如果有请求,唤醒请求队列处理线程
wake_up( & mq-> thread_wq) ;
}
线程函数mmc_queue_thread调用了具体设备的请求处理函数,利用线程机制来处理请求。函数mmc_queue_thread分析如下:
static int mmc_queue_thread( void * d)
{
struct mmc_queue * mq = d;
struct request_queue * q = mq-> queue;
DECLARE_WAITQUEUE( wait, current) ; //声明一个当前进程的等待队列
//设置当前进程状态,来让线程自己来处理挂起
current-> flags |= PF_MEMALLOC| PF_NOFREEZE;
//让线程继承init进程,从而不会使用用户进程资源
daemonize( "mmcqd" ) ;
complete( & mq-> thread_complete) ; //设置线程完成时的回调函数
down( & mq-> thread_sem) ;
add_wait_queue( & mq-> thread_wq, & wait) ; //加线程到等待队列
do {
struct request * req = NULL;
spin_lock_irq( q-> queue_lock) ;
set_current_state( TASK_INTERRUPTIBLE) ;
if ( ! blk_queue_plugged( q) ) //如果队列是非堵塞状态,得到下一个请求
mq-> req = req = elv_next_request( q) ;
spin_unlock_irq( q-> queue_lock) ;
if ( ! req) { //如果请求为空
if ( mq-> flags & MMC_QUEUE_EXIT)
break ;
up( & mq-> thread_sem) ;
schedule( ) ;
down( & mq-> thread_sem) ;
continue ;
}
set_current_state( TASK_RUNNING) ;
//这里调用了mmc_blk_issue_rq开始处理请求
mq-> issue_fn( mq, req) ;
} while ( 1 ) ;
remove_wait_queue( & mq-> thread_wq, & wait) ;
up( & mq-> thread_sem) ;
//调用请求处理完后的回调函数
complete_and_exit( & mq-> thread_complete, 0 ) ;
return 0 ;
}
函数mmc_blk_issue_rq初始化MMC块请求结构后,向卡发出请求命令,并等待请求的完成,函数分析如下:
static int mmc_blk_issue_rq( struct mmc_queue * mq, struct request * req)
{
struct mmc_blk_data * md = mq-> data;
struct mmc_card * card = md-> queue.card ;
int ret;
//认领控制器,发命令到卡设置card为选中状态
if ( mmc_card_claim_host( card) )
goto cmd_err;
do {
struct mmc_blk_request brq;
struct mmc_command cmd;
//初始化MMC块请求结构
memset( & brq, 0 , sizeof ( struct mmc_blk_request) ) ;
brq.mrq .cmd = & brq.cmd ;
brq.mrq .data = & brq.data ;
brq.cmd .arg = req-> sector << 9 ;
brq.cmd .flags = MMC_RSP_R1;
brq.data .timeout_ns = card-> csd.tacc_ns * 10 ;
brq.data .timeout_clks = card-> csd.tacc_clks * 10 ;
brq.data .blksz_bits = md-> block_bits;
brq.data .blocks = req-> nr_sectors >> ( md-> block_bits - 9 ) ;
brq.stop .opcode = MMC_STOP_TRANSMISSION;
brq.stop .arg = 0 ;
brq.stop .flags = MMC_RSP_R1B;
if ( rq_data_dir( req) == READ) { //读请求
brq.cmd .opcode = brq.data .blocks > 1 ? MMC_READ_MULTIPLE_BLOCK : MMC_READ_SINGLE_BLOCK;
brq.data .flags |= MMC_DATA_READ;
} else { //写
brq.cmd .opcode = MMC_WRITE_BLOCK;
brq.cmd .flags = MMC_RSP_R1B;
brq.data .flags |= MMC_DATA_WRITE;
brq.data .blocks = 1 ;
}
brq.mrq .stop = brq.data .blocks > 1 ? & brq.stop : NULL;
brq.data .sg = mq-> sg;
brq.data .sg_len = blk_rq_map_sg( req-> q, req, brq.data .sg ) ;
//等待请求完成
mmc_wait_for_req( card-> host, & brq.mrq ) ;
……
do {
int err;
cmd.opcode = MMC_SEND_STATUS;
cmd.arg = card-> rca << 16 ;
cmd.flags = MMC_RSP_R1;
err = mmc_wait_for_cmd( card-> host, & cmd, 5 ) ;
if ( err) {
printk( KERN_ERR "%s: error %d requesting status/n " ,
req-> rq_disk-> disk_name, err) ;
goto cmd_err;
}
} while ( ! ( cmd.resp [ 0 ] & R1_READY_FOR_DATA) ) ;
//一个块被成功传输
spin_lock_irq( & md-> lock) ;
ret = end_that_request_chunk( req, 1 , brq.data .bytes_xfered ) ;
if ( ! ret) {
//整个请求完全成功完成
add_disk_randomness( req-> rq_disk) ;
blkdev_dequeue_request( req) ; //从队列中删除请求
end_that_request_last( req) ; //写一些更新信息
}
spin_unlock_irq( & md-> lock) ;
} while ( ret) ;
mmc_card_release_host( card) ;
return 1 ;
cmd_err:
mmc_card_release_host( card) ;
spin_lock_irq( & md-> lock) ;
do {
//结束请求req上的I/O,操作成功时返回0
ret = end_that_request_chunk( req, 0 ,
req-> current_nr_sectors << 9 ) ;
} while ( ret) ;
add_disk_randomness( req-> rq_disk) ;
blkdev_dequeue_request( req) ;
end_that_request_last( req) ;
spin_unlock_irq( & md-> lock) ;
return 0 ;
}
函数mmc_card_claim_host发出命令选择这个卡card,函数列出如下(在 include/linuc/mmc/card.h中):
static inline int mmc_card_claim_host( struct mmc_card * card)
{
return __mmc_claim_host( card-> host, card) ;
}
函 数__mmc_claim_host专有地认领一个控制器,参数host是认领的mmc控制器,参数card是去认领控制器的卡。函数 __mmc_claim_host为一套操作认领一个控制器,如果card是被传递的一个有效的卡,并且它不是上次被选择的卡,那么在函数返回之前发出命 令选择这个卡card。
函数__mmc_claim_host分析如下(在drivers/mmc/mmc.c中):
int __mmc_claim_host( struct mmc_host * host, struct mmc_card * card)
{
DECLARE_WAITQUEUE( wait, current) ; //给当前进程声明一个等待队列
unsigned long flags;
int err = 0 ;
add_wait_queue( & host-> wq, & wait) ; //加host->wq到等待队列中
spin_lock_irqsave( & host-> lock, flags) ;
while ( 1 ) {
set_current_state( TASK_UNINTERRUPTIBLE) ; //设置当前进程不可中断状态
if ( host-> card_busy == NULL) //如果没有忙的卡,跳出循环
break ;
spin_unlock_irqrestore( & host-> lock, flags) ;
schedule( ) ; //如果有忙的卡,去调度执行
spin_lock_irqsave( & host-> lock, flags) ;
}
set_current_state( TASK_RUNNING) ; //设置当前进程为运行状态
host-> card_busy = card; //指定当前忙的卡
spin_unlock_irqrestore( & host-> lock, flags) ;
remove_wait_queue( & host-> wq, & wait) ; //从等待队列中移去host->wq
//如果卡不是选择状态,发出命令到卡设置为选择状态
if ( card != ( void * ) - 1 && host-> card_selected != card) {
struct mmc_command cmd;
host-> card_selected = card;
cmd.opcode = MMC_SELECT_CARD;
cmd.arg = card-> rca << 16 ;
cmd.flags = MMC_RSP_R1;
//等待命令完成
err = mmc_wait_for_cmd( host, & cmd, CMD_RETRIES) ;
}
return err;
}
函数mmc_wait_for_req开始执行一个请求并等待请求完成,函数分析如下(在drivers/mmc/mmc.c中):
int mmc_wait_for_req( struct mmc_host * host, struct mmc_request * mrq)
{
DECLARE_COMPLETION( complete) ;
mrq-> done_data = & complete;
mrq-> done = mmc_wait_done;
mmc_start_request( host, mrq) ;
wait_for_completion( & complete) ;
return 0 ;
}
函数mmc_start_request开始排队执行一个在控制器上的命令,参数host是执行命令的控制器,参数mrq是将要开始执行的请求。调用者应持有锁并且关中断。
函数mmc_start_request分析如下(在drivers/mmc/mmc.c中):
void mmc_start_request( struct mmc_host * host, struct mmc_request * mrq)
{
DBG( "MMC: starting cmd %02x arg %08x flags %08x/n " ,
mrq-> cmd-> opcode, mrq-> cmd-> arg, mrq-> cmd-> flags) ;
WARN_ON( host-> card_busy == NULL) ;
mrq-> cmd-> error = 0 ;
mrq-> cmd-> mrq = mrq;
if ( mrq-> data) {
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;
}
}
//调用请求处理函数,对于amba主控制器来说就是mmci_request函数
host-> ops-> request( host, mrq) ;
}
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/133240.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...