【经验记录】如何给嵌入式Linux的SD/MMC卡驱动中添加多个分区
How to add multi partition for SD/MMC card in Linux Driver
之所以写这个,是因为,在这个过程中,自己明显感觉到了,做事情一定要有一定的方法,方法对了,
事情做的才有效率,否则就是事倍功半。
【过程】
当前,Linux下sd/mmc卡的驱动已经实现。需要在此基础上实现,给这个mmc/sd块设备加多个分区。
有人会问,那等系统启动后,通过fdisk工具去分区不也可以吗?回答是,除了我对此fdisk工具不熟悉之外,本身sd/mmc驱动加载后,只有一个区,而我tootfs就是放在sd卡中,然后kernel 通过sd挂载rootfs的,所以,系统启动后,sd卡处于使用中,好像fdisk对于已经使用的sd卡进行分区,估计也会有些问题的。
所以,就需要在驱动加载过程中,加入多个分区。然后我后期向卡的不同的分区去写东西,就很方便了。
言归正传。此处,如果通过NFS启动kernel和rootfs,然后启动后,插入卡,可以显示类似如下信息(4GB的SD卡):
mmc0: new SDHC card at address d555
mmcblk0: mmc0:d555 SD04G 3.79 GiB
mmcblk0: p1
如果里面的分区被破坏,就会这样(这个是1G的SD卡):
mmc0: new high speed SD card at address b368
mmcblk0: mmc0:b368 UD 952 MiB
mmcblk0: unknown partition table
现在要做的,就是对其支持多分区,但是,关于如何对于sd卡里面加多分区,去百度google了,结果是除了这个帖子里:
http://en.gentoo-wiki.com/wiki/SD_and_MMC_card_readers
“Check Setup
After rebooting and inserting a SD card you should have a device file like:
/dev/mmcblk0
And a partition on it:
/dev/mmcblk0p1 ”
让人明白,mmcblk0就是mmc block 0和mmcblk0p1就是mmc block 0 partition 1之外,其他的可以说是一无所获,所以,只能去通过上面的信息,搞清楚到底何时去加的/dev/mmcblk0p1,然后在对应的地方
加上自己要的多分区。
不过,具体是什么地方输出这些信息的,自己去mmc驱动里面,一直无法找到。而且mmc驱动只是针对host controller而言的,而这些信息输出是在卡插上的时候,识别了卡的之后,才能显示具体信息的。
不过,在mmc驱动至少能知道,大概信息,就是在这些函数里面:
mmc_rescan -> mmc_attach_sd - -> mmc_add_card() ->device_add() ->
然后在device_add里面,太多相关的系统函数kobject_add,blocking_notifier_call_chain,device_create_file,device_create_file,device_create_sys_dev_entry,device_add_class_symlinks,device_add_attrs,bus_add_device,device_pm_add,bus_attach_device,。。。。。
所以,实现没法搞清楚,具体是哪个函数具体去实现的底层加了/dev/mmcblk0p1这个节点的。
继续debug,加打印信息,在插入卡后,出现:
# mmc0: new SDHC card at address d555
--SD-- 1
--SD-- 2
--SD-- 3
--SD-- 4
--SD-- 6
--SD-- 7
--SD-- 8
--SD-- 1
--SD-- 9
--SD-- 1
--SD-- 10
mmcblk0: mmc0:d555 SD04G 3.79 GiB
--SD-- 1
--SD-- 2
--SD-- 3
--SD-- 4
--SD-- 5
--SD-- 6
--SD-- 7
--SD-- 8
--SD-- 1
--SD-- 9
--SD-- 1
--SD-- 10
--SD-- 11
--SD-- 12
mmcblk0: p1
--SD-- 1
--SD-- 2
--SD-- 3
--SD-- 4
--SD-- 5
--SD-- 6
--SD-- 7
--SD-- 8
--SD-- 1
--SD-- 9
--SD-- 1
--SD-- 10
--SD-- 11
--SD-- 12
--SD-- 1
--SD-- 2
--SD-- 3
--SD-- 4
--SD-- 6
--SD-- 7
--SD-- 8
--SD-- 1
--SD-- 9
--SD-- 1
--SD-- 10
--SD-- 11
--SD-- 12
--SD-- 11
--SD-- 12
所以,由:
--SD-- 10
mmcblk0: mmc0:d555 SD04G 3.79 GiB
判断是
bus_attach_device(dev);
if (parent)
klist_add_tail(&dev->knode_parent, &parent->klist_children);
去实现的/dev/mmcblk0p1,但是去看了这bus_attach_device和klist_add_tail,也仍然是看不懂具体,不知道哪里去具体实现的。
不过,在看代码过程中,发现 kobject_uevent(&dev->kobj, KOBJ_ADD);
中有个dev->kobj,这个kobj是驱动的一个核心数据结构,其中有个name变量,所以,就想,打印这些kobj的name看看,都是哪些名称所以去掉其他打印,加上这个:
printk("--SD-- 10, obj_name:%s\n", dev->kobj.name);
然后debug结果输出是:
# mmc0: new SDHC card at address d555
--SD-- 1
--SD-- 10, obj_name:mmc0:d555
mmcblk0: mmc0:d555 SD04G 3.79 GiB
--SD-- 1
--SD-- 10, obj_name:mmcblk0
--SD-- 11
--SD-- 12
mmcblk0: p1
--SD-- 1
--SD-- 10, obj_name:mmcblk0p1
--SD-- 11
--SD-- 12
--SD-- 1
--SD-- 10, obj_name:179:0
--SD-- 11
--SD-- 12
--SD-- 11
--SD-- 12
因此,知道了mmcblk0p1是从这里:
mmcblk0: p1
--SD-- 1
--SD-- 10, obj_name:mmcblk0p1
实现的,但是仍旧不知道是哪些函数调用到这里的device_add,
此时,由于以前遇到过,dump_stack()函数,知道这个函数可以打印堆栈信息,也就是当前栈上的函数调用信息,所以,此时可以派上用场了。同时,也知道“179:0”是mmc的主次设备号,所以继续debug,加上这些:
if((0==strcmp("mmcblk0p1", dev->kobj.name)) ||
(0==strcmp("mmcblk0", dev->kobj.name)) ||
(0==strcmp("179:0", dev->kobj.name)) ||
(0==strcmp("179:1", dev->kobj.name)) )
{
printk("--SD-- 10, obj_name:%s\n", dev->kobj.name);
dump_stack();
}
最后打印出来我想要的函数调用信息:
# mmc0: new SDHC card at address d555
mmcblk0: mmc0:d555 SD04G 3.79 GiB
--SD-- 10, obj_name:mmcblk0
[<c025deac>] (dump_stack+0x0/0x14) from [<c0184cd8>] (device_add+0x4c4/0x688)
[<c0184814>] (device_add+0x0/0x688) from [<c00c9360>] (register_disk+0x5c/0x154)
[<c00c9304>] (register_disk+0x0/0x154) from [<c013a204>] (add_disk+0xe8/0x150)
r7:00000000 r6:c3987200 r5:c3987a00 r4:c388ddde
[<c013a11c>] (add_disk+0x0/0x150) from [<c01a4f54>] (mmc_blk_probe+0x224/0x2ec)
r5:c3a9a120 r4:c388ddde
[<c01a4d30>] (mmc_blk_probe+0x0/0x2ec) from [<c019f868>] (mmc_bus_probe+0x20/0x24)
[<c019f848>] (mmc_bus_probe+0x0/0x24) from [<c0186eb0>] (driver_probe_device+0xa8/0x1bc)
[<c0186e08>] (driver_probe_device+0x0/0x1bc) from [<c0187064>] (__device_attach+0x10/0x14)
[<c0187054>] (__device_attach+0x0/0x14) from [<c0186448>] (bus_for_each_drv+0x68/0x94)
[<c01863e0>] (bus_for_each_drv+0x0/0x94) from [<c01870f8>] (device_attach+0x64/0x7c)
r7:c3987290 r6:c3a5ef60 r5:c39872b0 r4:c3987204
[<c0187094>] (device_attach+0x0/0x7c) from [<c0186224>] (bus_attach_device+0x40/0x60)
r5:c3987204 r4:c031c274
[<c01861e4>] (bus_attach_device+0x0/0x60) from [<c0184ce0>] (device_add+0x4cc/0x688)
r5:c3987204 r4:00000000
[<c0184814>] (device_add+0x0/0x688) from [<c019fa00>] (mmc_add_card+0x88/0x134)
[<c019f978>] (mmc_add_card+0x0/0x134) from [<c01a1548>] (mmc_attach_sd+0x15c/0x858)
r5:00000000 r4:c3bfb960
[<c01a13ec>] (mmc_attach_sd+0x0/0x858) from [<c019f610>] (mmc_rescan+0x228/0x29c)
[<c019f3e8>] (mmc_rescan+0x0/0x29c) from [<c004842c>] (run_workqueue+0xe8/0x198)
r6:c388c000 r5:c019f3e8 r4:c3885720
[<c0048344>] (run_workqueue+0x0/0x198) from [<c0048ac8>] (worker_thread+0x5c/0xb8)
r7:c03291d0 r6:c388dfa8 r5:c3885728 r4:c3885720
[<c0048a6c>] (worker_thread+0x0/0xb8) from [<c004c428>] (kthread+0x58/0x8c)
r6:c0048a6c r5:c3885720 r4:c388c000
[<c004c3d0>] (kthread+0x0/0x8c) from [<c003abec>] (do_exit+0x0/0x738)
r7:00000000 r6:00000000 r5:00000000 r4:00000000
mmcblk0: p1
--SD-- 10, obj_name:mmcblk0p1
[<c025deac>] (dump_stack+0x0/0x14) from [<c0184cd8>] (device_add+0x4c4/0x688)
[<c0184814>] (device_add+0x0/0x688) from [<c00c9648>] (add_partition+0x11c/0x21c)
[<c00c952c>] (add_partition+0x0/0x21c) from [<c00c9ad0>] (rescan_partitions+0x234/0x338)
[<c00c989c>] (rescan_partitions+0x0/0x338) from [<c00aee3c>] (__blkdev_get+0x15c/0x2a8)
[<c00aece0>] (__blkdev_get+0x0/0x2a8) from [<c00aef9c>] (blkdev_get+0x14/0x18)
[<c00aef88>] (blkdev_get+0x0/0x18) from [<c00c9438>] (register_disk+0x134/0x154)
[<c00c9304>] (register_disk+0x0/0x154) from [<c013a204>] (add_disk+0xe8/0x150)
r7:00000000 r6:c3987200 r5:c3987a00 r4:c388ddde
[<c013a11c>] (add_disk+0x0/0x150) from [<c01a4f54>] (mmc_blk_probe+0x224/0x2ec)
r5:c3a9a120 r4:c388ddde
[<c01a4d30>] (mmc_blk_probe+0x0/0x2ec) from [<c019f868>] (mmc_bus_probe+0x20/0x24)
[<c019f848>] (mmc_bus_probe+0x0/0x24) from [<c0186eb0>] (driver_probe_device+0xa8/0x1bc)
[<c0186e08>] (driver_probe_device+0x0/0x1bc) from [<c0187064>] (__device_attach+0x10/0x14)
[<c0187054>] (__device_attach+0x0/0x14) from [<c0186448>] (bus_for_each_drv+0x68/0x94)
[<c01863e0>] (bus_for_each_drv+0x0/0x94) from [<c01870f8>] (device_attach+0x64/0x7c)
r7:c3987290 r6:c3a5ef60 r5:c39872b0 r4:c3987204
[<c0187094>] (device_attach+0x0/0x7c) from [<c0186224>] (bus_attach_device+0x40/0x60)
r5:c3987204 r4:c031c274
[<c01861e4>] (bus_attach_device+0x0/0x60) from [<c0184ce0>] (device_add+0x4cc/0x688)
r5:c3987204 r4:00000000
[<c0184814>] (device_add+0x0/0x688) from [<c019fa00>] (mmc_add_card+0x88/0x134)
[<c019f978>] (mmc_add_card+0x0/0x134) from [<c01a1548>] (mmc_attach_sd+0x15c/0x858)
r5:00000000 r4:c3bfb960
[<c01a13ec>] (mmc_attach_sd+0x0/0x858) from [<c019f610>] (mmc_rescan+0x228/0x29c)
[<c019f3e8>] (mmc_rescan+0x0/0x29c) from [<c004842c>] (run_workqueue+0xe8/0x198)
r6:c388c000 r5:c019f3e8 r4:c3885720
[<c0048344>] (run_workqueue+0x0/0x198) from [<c0048ac8>] (worker_thread+0x5c/0xb8)
r7:c03291d0 r6:c388dfa8 r5:c3885728 r4:c3885720
[<c0048a6c>] (worker_thread+0x0/0xb8) from [<c004c428>] (kthread+0x58/0x8c)
r6:c0048a6c r5:c3885720 r4:c388c000
[<c004c3d0>] (kthread+0x0/0x8c) from [<c003abec>] (do_exit+0x0/0x738)
r7:00000000 r6:00000000 r5:00000000 r4:00000000
--SD-- 10, obj_name:179:0
[<c025deac>] (dump_stack+0x0/0x14) from [<c0184cd8>] (device_add+0x4c4/0x688)
[<c0184814>] (device_add+0x0/0x688) from [<c0184eb8>] (device_register+0x1c/0x20)
[<c0184e9c>] (device_register+0x0/0x20) from [<c0184f4c>] (device_create_vargs+0x90/0xac)
r4:c3a79400
[<c0184ebc>] (device_create_vargs+0x0/0xac) from [<c00702a0>] (bdi_register+0x54/0xac)
r8:00000176 r7:00000000 r6:c3987200 r5:00000000 r4:c39ba204
[<c007024c>] (bdi_register+0x0/0xac) from [<c0070324>] (bdi_register_dev+0x2c/0x34)
r3:000000b3 r2:c02c0f24
r5:c3987a00 r4:c39ba168
[<c00702f8>] (bdi_register_dev+0x0/0x34) from [<c013a21c>] (add_disk+0x100/0x150)
[<c013a11c>] (add_disk+0x0/0x150) from [<c01a4f54>] (mmc_blk_probe+0x224/0x2ec)
r5:c3a9a120 r4:c388ddde
[<c01a4d30>] (mmc_blk_probe+0x0/0x2ec) from [<c019f868>] (mmc_bus_probe+0x20/0x24)
[<c019f848>] (mmc_bus_probe+0x0/0x24) from [<c0186eb0>] (driver_probe_device+0xa8/0x1bc)
[<c0186e08>] (driver_probe_device+0x0/0x1bc) from [<c0187064>] (__device_attach+0x10/0x14)
[<c0187054>] (__device_attach+0x0/0x14) from [<c0186448>] (bus_for_each_drv+0x68/0x94)
[<c01863e0>] (bus_for_each_drv+0x0/0x94) from [<c01870f8>] (device_attach+0x64/0x7c)
r7:c3987290 r6:c3a5ef60 r5:c39872b0 r4:c3987204
[<c0187094>] (device_attach+0x0/0x7c) from [<c0186224>] (bus_attach_device+0x40/0x60)
r5:c3987204 r4:c031c274
[<c01861e4>] (bus_attach_device+0x0/0x60) from [<c0184ce0>] (device_add+0x4cc/0x688)
r5:c3987204 r4:00000000
[<c0184814>] (device_add+0x0/0x688) from [<c019fa00>] (mmc_add_card+0x88/0x134)
[<c019f978>] (mmc_add_card+0x0/0x134) from [<c01a1548>] (mmc_attach_sd+0x15c/0x858)
r5:00000000 r4:c3bfb960
[<c01a13ec>] (mmc_attach_sd+0x0/0x858) from [<c019f610>] (mmc_rescan+0x228/0x29c)
[<c019f3e8>] (mmc_rescan+0x0/0x29c) from [<c004842c>] (run_workqueue+0xe8/0x198)
r6:c388c000 r5:c019f3e8 r4:c3885720
[<c0048344>] (run_workqueue+0x0/0x198) from [<c0048ac8>] (worker_thread+0x5c/0xb8)
r7:c03291d0 r6:c388dfa8 r5:c3885728 r4:c3885720
[<c0048a6c>] (worker_thread+0x0/0xb8) from [<c004c428>] (kthread+0x58/0x8c)
r6:c0048a6c r5:c3885720 r4:c388c000
[<c004c3d0>] (kthread+0x0/0x8c) from [<c003abec>] (do_exit+0x0/0x738)
r7:00000000 r6:00000000 r5:00000000 r4:00000000
从上面可以看出:mmc_bus_probe ->mmc_blk_probe->add_disk,就是具体实现分区的地方了。
知道哪些函数了,剩下的找到对应C文件。
本来觉得很简单的,去百度一下mmc_blk_probe应该就能找到对应C文件。结果百度找的是drivers/mmc/mmc_block.c,但是,我去我的2.6.28的kernel,发现没有这个文件,所以很是无语,只能继续找。。。
最后,在drivers/mmc/card/block.c中找到了这些相关的函数:
mmc_blk_probe,add_disk,其中,“mmcblk0: mmc0:d555 SD04G 3.79 GiB ”就是mmc_blk_probe中的
printk(KERN_INFO "%s: %s %s %s %s\n",
md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),
cap_str, md->read_only ? "(ro)" : "");
打印的。
找到这里,要做的事情,其实才刚开始,但是也是只是时间问题了。因为我之前自己做了LBA的块设备,所以,知道怎么添加多分区。此处,只是要添加一些代码即可。
【具体代码】
在drivers/mmc/card/block.c添加如下代码:
#define AS353X_MMC_SUPPORT_MULTI_PART 1
#if AS353X_MMC_SUPPORT_MULTI_PART
typedef struct
{
int32_t start_sec;
int32_t sec_num;
}as353x_blk_part_info;
/*
PatitionName FsType PartitionSize Rage(From-To)
(1)uImage None (4M) 0 - (4M)
(2)Rootfs FAT/Ext2 (28M or 124M) (4M) - (32M or 128M)
(3)Data Ext2/Ext3 (CardSize -496KB - (28M or 124M)) (32M or 128M) - (CardSize -496KB)
(4)uboot None (496KB) (CardSize -496KB) - (CardSize )
A.not support multi partitions for <=32MB, it is too small for rootfs+uImage+uboot
B.for rootfs, if mmc/sd size > 128MB, reserved 128MB, or reserved 32MB
C.for uboot, 496KB = 0x7C000, which if fixed by BootCode
*/
#ifndef SZ_1M
#define SZ_1M 0x00100000
#endif
#ifndef SZ_1K
#define SZ_1K 0x00000400
#endif
#define SDMMC_BLOCK_SIZE 512
#define SDMMC_CARDSIZE_128MB (128 *SZ_1M)
#define SDMMC_CARDSIZE_TOO_SMALL (32 *SZ_1M)
#define SDMMC_ROOTFS_SIZE_128MB (128*SZ_1M) /* actual is 128-4=124MB*/
#define SDMMC_ROOTFS_SIZE_32MB (32*SZ_1M) /* actual is 32-4= 28MB*/
#define SDMMC_KERNEL_SIZE (4*SZ_1M)
#define SDMMC_UBOOT_SIZE (496*SZ_1K) /* 0x7C000=496KB */
#define SDMMC_ROOTFS_128MB_BLK_NUM (SDMMC_ROOTFS_SIZE_128MB/SDMMC_BLOCK_SIZE)
#define SDMMC_ROOTFS_32MB_BLK_NUM (SDMMC_ROOTFS_SIZE_32MB/SDMMC_BLOCK_SIZE)
#define SDMMC_KERNEL_BLK_NUM (SDMMC_KERNEL_SIZE/SDMMC_BLOCK_SIZE)
#define SDMMC_UBOOT_BLK_NUM (SDMMC_UBOOT_SIZE/SDMMC_BLOCK_SIZE)
/* 0 is the first partion for whole disk */
#define AS353X_SDMMC_MAX_PARTITIONS 5
static as353x_blk_part_info as353x_mmc_partitions[AS353X_SDMMC_MAX_PARTITIONS];
/* 1 block=512B */
static int as353x_init_sdmmc_parition(uint32_t card_size_in_blocks)
{
uint32_t rootfs_blocks;
uint64_t card_size_bytes = card_size_in_blocks * SDMMC_BLOCK_SIZE;
if(card_size_bytes <= SDMMC_CARDSIZE_TOO_SMALL)
return -1; /* not support multi partitions here */
/* 0 is for whole disk, not used here */
as353x_mmc_partitions[0].start_sec = 0;
as353x_mmc_partitions[0].sec_num =0;
if(card_size_bytes > SDMMC_CARDSIZE_128MB)
{
/* > 128MB, is 128MB */
rootfs_blocks = SDMMC_ROOTFS_128MB_BLK_NUM;
}
else
{
/* <=128MB is 32MB */
rootfs_blocks = SDMMC_ROOTFS_32MB_BLK_NUM;
}
/* 4M uImage */
as353x_mmc_partitions[1].start_sec = 0;
as353x_mmc_partitions[1].sec_num =SDMMC_KERNEL_BLK_NUM;
/* 28 or 124 MB rootfs */
as353x_mmc_partitions[2].start_sec = SDMMC_KERNEL_BLK_NUM;
as353x_mmc_partitions[2].sec_num = rootfs_blocks - SDMMC_KERNEL_BLK_NUM;
/* carsize - 496KB- (28 or 124) MB */
as353x_mmc_partitions[3].start_sec = rootfs_blocks;
as353x_mmc_partitions[3].sec_num = card_size_in_blocks - rootfs_blocks - SDMMC_UBOOT_BLK_NUM;
/* 496KB uboot */
as353x_mmc_partitions[4].start_sec = card_size_in_blocks - SDMMC_UBOOT_BLK_NUM;
as353x_mmc_partitions[4].sec_num = SDMMC_UBOOT_BLK_NUM;
return 0;
}
#endif
static int mmc_blk_probe(struct mmc_card *card)
{
。。。。。
char cap_str[10];
#if AS353X_MMC_SUPPORT_MULTI_PART
int i;
struct hd_struct * hd;
#endif
/*
* Check that the card supports the command class(es) we need.
*/
if (!(card->csd.cmdclass & CCC_BLOCK_READ))
return -ENODEV;
。。。。。。
mmc_set_drvdata(card, md);
add_disk(md->disk);
#if AS353X_MMC_SUPPORT_MULTI_PART
if(0==as353x_init_sdmmc_parition(get_capacity(md->disk)))
{
for(i = 1; i < AS353X_SDMMC_MAX_PARTITIONS; i++)
{
hd = add_partition(md->disk, i, as353x_mmc_partitions[i].start_sec, as353x_mmc_partitions[i].sec_num, ADDPART_FLAG_NONE);
}
}
#endif
return 0;
。。。。
}
【总结】
1.还是应了那句话,只要有源码,就不怕解决不了问题。
2.调试的时候,很多时候是看起来没有办法了,但是只要细心,额外看到的信息,可以充分利用,比如上面的dev->kobj有个name参数,就可以拿来打印出来看看到底是哪些名字,以便可能继续提供更多的信息,便于找到新的办法或线索。
3.做事情,还是要有耐力和找到有效的办法,没有办法,也要尽可能地想办法。如果具备了一定的基础知识,加上细心观察,充分利用信息,很多时候,即使