Linux下的eMMC

常見存儲介質

NAND Flash
NOR Flash
HDD 機械硬盤
SSD 固態硬盤(包含NAND Flash)
UFS(包含NAND Flash)
eMMC(Embedded Multi Media Card) eMMC接口+NAND Flash
eMCP eMMC和DDR進行了封裝

先看下硬件連接(除電源)

 

Linux下的eMMC架構

eMMC的註冊過程,先註冊host(eMMC通信的接口),然後註冊塊設備,在add_disk的過程中會掃描分區,添加分區,Android通過vold掛載各分區,流程如下

mmc_alloc_host   INIT_DELAYED_WORK(&host->detect, mmc_rescan);
mmc_add_host
	mmc_start_host
		_mmc_detect_change-->mmc_schedule_delayed_work(&host->detect, delay);  

註冊了設備
mmc_rescan
	mmc_rescan_try_freq
	mmc_attach_mmc/mmc_attach_sdio/mmc_attach_sdio
	mmc_init_card mmc_alloc_card-->card->dev.bus = &mmc_bus_type;
	mmc_add_card  device_add bus_probe_device device_initial_probe __device_attach __device_attach_driver really_probe dev->bus->probe


內核默認註冊了驅動
static int mmc_bus_probe(struct device *dev)
{
	struct mmc_driver *drv = to_mmc_driver(dev->driver);
	struct mmc_card *card = mmc_dev_to_card(dev);

	return drv->probe(card);
}

static int mmc_bus_match(struct device *dev, struct device_driver *drv)
{
	return 1;
}
static struct bus_type mmc_bus_type = {
	.match		= mmc_bus_match,
	.probe		= mmc_bus_probe,
};
kernel/driver/mmc/card.c
int mmc_register_driver(struct mmc_driver *drv)
{
	drv->drv.bus = &mmc_bus_type;
	return driver_register(&drv->drv);
}

static struct mmc_driver mmc_driver = {
	.drv		= {
		.name	= "mmcblk",
		.pm	= &mmc_blk_pm_ops,
	},
	.probe		= mmc_blk_probe,
	.remove		= mmc_blk_remove,
	.shutdown	= mmc_blk_shutdown,
};
static int __init mmc_blk_init(void)
{
	mmc_register_driver(&mmc_driver);
}


static int mmc_blk_probe(struct mmc_card *card)
{
	struct mmc_blk_data *md;
	md=mmc_blk_alloc(card);
	mmc_add_disk(md)
}

static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
{
	mmc_blk_alloc_req(card, &card->dev, size, false, NULL,MMC_BLK_DATA_AREA_MAIN);
}

static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,struct device *parent,sector_t size,bool default_ro,const char *subname,int area_type)
{
    alloc_disk();
    mmc_init_queue-->blk_init_queue(mmc_request_fn, lock);
}

static int mmc_add_disk(struct mmc_blk_data *md)
{
	add_disk(md->disk);-->register_disk(disk);-->blkdev_get-->__blkdev_get-->rescan_partitions-->check_partition-->check_part-->efi_partition-->put_partition
												     add_partition
}

虛擬內存塊設備driver,用於理解塊設備的運行。 

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>
#include <linux/blkpg.h>
#include <linux/bio.h>
#include <linux/io.h>
#include <linux/spinlock.h>
#include <asm/uaccess.h>

#define SECTOR_SIZE (512)
static int BLK_SIZE=10*1024*512;
struct vmem_device *vdev=NULL;
static int vmem_major;

struct vmem_device {
	struct gendisk *disk;
	struct request_queue *que;
	void *buf;
	spinlock_t lock;
	ssize_t size;
};

static int vmem_open(struct block_device *bdev, fmode_t mode)
{
	pr_info("vmem_open\n");
	return 0;
}

static void vmem_release(struct gendisk *disk, fmode_t mode)
{
	pr_info("vmem_release\n");
}

static int vmem_ioctl(struct block_device *bdev, fmode_t mode,unsigned command, unsigned long argument)
{
	pr_info("vmem_ioctl\n");
	return 0;
}

static int vmem_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
	geo->cylinders=1;
	geo->heads=1;
	geo->sectors=BLK_SIZE/SECTOR_SIZE;
	pr_info("vmem_getgeo\n");
	return 0;
}

static struct block_device_operations vmem_fops={
	.owner=THIS_MODULE,
	.getgeo=vmem_getgeo,
	.ioctl=vmem_ioctl,
	.open=vmem_open,
	.release=vmem_release,
};

static int vmem_transfer(struct vmem_device *vdev, uint64_t pos, ssize_t size, void *buffer, int write)
{
	if(write)
		memcpy(vdev->buf+pos, buffer, size);
	else
		memcpy(buffer, vdev->buf+pos, size);
	return 0;
}

static void vmem_request(struct request_queue *q){
	struct request *req;
	uint64_t pos=0;
	ssize_t size=0;
	struct bio_vec bvec;
	int rv=0;
	struct req_iterator iter;
	void *kaddr=NULL;

	while((req=blk_fetch_request(q)) != NULL){
		spin_unlock_irq(q->queue_lock);
		pos=blk_rq_pos(req)*SECTOR_SIZE;
		size=blk_rq_bytes(req);
		if(pos+size>vdev->size){
			printk(KERN_WARNING "beyond addr\n");
			rv=-EIO;
			goto skip;
		}
		rq_for_each_segment(bvec, req, iter){
		kaddr=kmap(bvec.bv_page);
		rv=vmem_transfer(vdev, pos, bvec.bv_len, kaddr+bvec.bv_offset, rq_data_dir(req));
		if(rv<0)
			goto skip;
		pos+=bvec.bv_len;
		kunmap(bvec.bv_page);
		}
skip:
		blk_end_request_all(req, rv);
		spin_lock_irq(q->queue_lock);
	}
}

static int __init vmem_init(void)
{
	struct gendisk *disk;
	vmem_major=register_blkdev(0, "VMEM");
	vdev=kzalloc(sizeof(struct vmem_device), GFP_KERNEL);
	if(!vdev){
		printk(KERN_WARNING "vmem_device: unable to allocate mem\n");
		goto out;
	}
	vdev->size=BLK_SIZE;
	vdev->buf=vmalloc(vdev->size);
	if(vdev->buf==NULL){
		printk(KERN_WARNING "failed to vmalloc vdev->buf\n");
		goto out_dev;
	}
	spin_lock_init(&vdev->lock);
	vdev->que=blk_init_queue(vmem_request, &vdev->lock);
	if(vdev->que==NULL){
		printk(KERN_WARNING "failed to init queue\n");
		goto out_buf;
	}
	disk=alloc_disk(1);
	if(disk==NULL){
		printk(KERN_WARNING "failed to alloc disk\n");
		goto out_que;
	}
	vdev->disk=disk;
	disk->major=vmem_major;
	disk->first_minor=1;
	disk->fops=&vmem_fops;
	disk->queue=vdev->que;
	disk->private_data=vdev;
	sprintf(disk->disk_name, "VMEM");
	set_capacity(disk, BLK_SIZE/SECTOR_SIZE);
	add_disk(disk);
	printk(KERN_INFO "succeed to init\n");
	return 0;
out_que:
	blk_cleanup_queue(vdev->que);
out_buf:
	vfree(vdev->buf);
out_dev:
	kfree(vdev);
out:
	unregister_blkdev(vmem_major, "VMEM");
	return -1;
}

static void __exit vmem_exit(void)
{
	del_gendisk(vdev->disk);
	blk_cleanup_queue(vdev->que);
	put_disk(vdev->disk);
	vfree(vdev->buf);
	kfree(vdev);
	unregister_blkdev(vmem_major, "VMEM");
	printk(KERN_INFO "module exit\n");
}

module_init(vmem_init);
module_exit(vmem_exit);
MODULE_AUTHOR("Pingbo An");
MODULE_LICENSE("GPL v2");
mkdir  /home/w/disk
mkfs.ext4 /dev/VMEM
mount /dev/VMEM /home/w/disk

mkfs.ext4格式硬盤爲ext4格式,linux下還有很多格式硬件的命令,如mkfs.ext3,mkfs.msdos.mkfs.vfat等

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章