SD/MMC卡块设备驱动程序[通俗易懂]

SD/MMC卡块设备驱动程序[通俗易懂]
SD/MMC卡组成的存储系统是许多嵌入设备的主要存储设备,相当于PC机的硬盘,在嵌入设备上的SD/MMC卡控制器通过MMC协议来解析命令控制SD/MMC卡的操作。SD/MMC卡上有一些寄存器来控制卡的状态及读写操作。MMC协议规定的寄存器有:CID寄存器,128位,是卡的鉴别寄存器,存有卡的鉴别信息;RCA寄存器是16位,存有卡的本地系统的相对地址,在初始化时由控制器动态指定。DSR寄存器是16位,是配置卡的驱动程序的寄存器,是可选的。CSD寄存器是卡特定数据信息描述寄存器,是可

大家好,又见面了,我是你们的朋友全栈君。

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账号...

(0)
blank

相关推荐

  • kafka批量删除topic_kafka自动创建topic

    kafka批量删除topic_kafka自动创建topic方法一:快速配置删除法1.kafka启动之前,在server.properties配置delete.topic.enable=true2.执行命令bin/kafka-topics.sh–delete–topictest–zookeeperzk:2181或者使用kafka-manager集群管理工具删除注意:如果kafka启动之前没有配置delete.topic.enab…

  • 「机械工程」力矩,转矩,扭矩的理解

    「机械工程」力矩,转矩,扭矩的理解1.力矩定义:力矩在物理学里是指作用力使物体绕着转动轴或支点转动的趋向。力矩的单位是牛顿-米。力矩希腊字母是tau。力矩的概念,起源于阿基米德对杠杆的研究。转动力矩又称为转矩或扭矩。力矩能够使物体改变其旋转运动。推挤或拖拉涉及到作用力,而扭转则涉及到力矩。力矩等于径向矢量与作用力的叉积。力矩在物理学里是指作用力使物体绕着转动轴或支点转动的趋向,是力对物体产生转动作用的物理量。可以分…

  • 【赠书】深入浅出Python量化交易实战

    【赠书】深入浅出Python量化交易实战‍‍本书主要以国内A股市场为例,借助第三方量化交易平台,讲述了KNN、线性模型、决策树、支持向量机、朴素贝叶斯等常见机器学习算法在交易策略中的应用,同时展示了如何对策略进行回测,以便让读者…

    2022年10月18日
  • stun client java实现_stun 协议客户端实现

    stun client java实现_stun 协议客户端实现/**Spider–AnopensourceClanguagetoolkit.**Copyright(C)2011,Inc.**lidp**Thisprogramisfreesoftware,distributedunderthetermsof*theGNUGeneralPublicLicenseVersion2.Seethe…

  • 卷积及理解图像卷积操作的意义

    卷积及理解图像卷积操作的意义转载:http://blog.csdn.net/chaipp0607/article/details/72236892     https://www.zhihu.com/question/22298352   在图像处理领域,我们经常能听到滤波,卷积之类的词,其实他们都可以看做一种图像的卷积操作,相对应的卷积核,卷积模板,滤波器,滤波模板,扫描窗其实也都是同一个东西。下面我们进一步讨论…

  • Nginx负载均衡失效「建议收藏」

    Nginx负载均衡失效「建议收藏」1.配置upstreamtest_api_backend{server192.168.0.1:8080;server192.168.0.2:8080;server192.168.0.3:8080;session_stickycookie=test_web_route;}2.现象负载均衡失效,通过域名访问后请求总是打到同一台机器。3.原因使用了session_st

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号