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卡设备相关结构关系图
MMC设备由控制器及插卡组成,对应的设备结构为mmc_host结构和mmc_card结构。MMC卡设备相关结构关系图如上图。下面分别说明设备相关结构:
每个卡的插槽对应一个块的数据结构mmc_blk_data,结构列出如下(在drivers/mmc/mmc_block.c中):
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中):
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中):
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列出如下:
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状态,列出如下:
mmc_ios
{
unsigned
int clock;
//时钟频率
unsigned
short vdd;
unsigned
char bus_mode;
//命令输出模式
unsigned
char power_mode;
//电源供应模式
}
;
结构mmc_driver是MMC设备驱动程序结构,列出如下:
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 *
) ;
}
;
图 MMC卡读写请求结构示意图
对于MMC卡的操作是通过MMC请求结构mmc_request的传递来完成的,来自系统块层的读写请求到达MMC设备抽象层时,用系统的读写请求填充初始化MMC卡的读写请求,经MMC协议分解后,然后把请求发给设备,再调用具体设备的请求处理函数来完成请求的处理。MMC卡读写请求结构示意图中上图。下面分析MMC卡读写请求相关结构:
结构mmc_request描述了读写MMC卡的请求,它包括命令、数据及请求完成后的回调函数。结构mmc_request列出如下(在include/linux/mmc/mmc.h中):
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中):
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中):
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卡操作相关命令及数据、状态信息等,结构列出如下:
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块设备驱动程序
函数mmc_blk_init注册一个MMC块设备驱动程序,它先将MMC块设备名注册到名称数组major_names中,然后,还把驱动程序注册到 sysfs文件系统中的总线和设备目录中。一方面,sysfs文件系统中可显示MMC块设备相关信息,另一方面,sysfs文件系统以树形结构管理着 MMC块设备驱动程序。
函数mmc_blk_init分析如下(在drivers/mmc/mmc_block.c中):
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驱动程序实例声明如下:
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中):
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)
;
}
图 函数mmc_blk_probe调用层次图
函数mmc_blk_probe是MMC控制器探测函数,它探测MMC控制器是否存在,并初始化控制器的结构,同时,还探测MMC卡的状态并初始化。函数mmc_blk_probe调用层次图如上图。
函数mmc_blk_probe列出如下:
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中):
#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
=