05-emmc的识别模式

、协议流程
1、整体框图
在这里插入图片描述
2、框图描述
从上图可以看出:
(1)设备上电或者设备接收到参数为0的CMD0命令,便会进入idle状态
(2)当设备进入idle状态后,通过CMD1(SEND_OP_COND)命令来判断设备是否处于忙状态,而且可以获取到OCR寄存器的值,即可以获取到emmc支持的电压范围,和控制进行比较,判断是否有匹配的电压
在这里插入图片描述
CMD1需要重复发送直到返回不忙状态,设备进入ready状态
(3)进入ready状态后,发送广播命令CMD2(ALL_SEND_CID),该命令请求所有设备发送其唯一设备标识(CID)号,所有
未识别的(即处于Ready状态的)设备同时开始串行地发送其CID号,成功后设备进入Identification状态
在这里插入图片描述
(4)主机发送CMD3(SET_RELATIVE_ADDR),给改设备一个相对设备地址(RCA),该地址用于数据传输模式,设备一旦接收到RCA,设备就变为Stand-by状态。设备将其输出驱动器从开漏切换到推拉模式。
RCA寄存器模式是0x0001

二、裸驱配置流程
1、SRS11.SRFA ( Software Reset For All) 复位整个芯片,循环等待该bit为0,为0代表复位成功
2、SRS10.BP=1 SRS10.BVS=101(1.8V)
3、SRS13 使能或者SRS14关闭各种中断
4、phy初始化
5、SRS10.EDTW=0 SRS10.DTW=0 配置位宽为1bit
6、SRS11.SDCFSL=(200M/400KHz)配置clk为400Hz
7、开始发送CMD0,延时2ms
8、发送CMD1,根据应答判断OCR的bit31是否busy,1代表空闲,0代表忙,如果忙,再次发送,连续尝试3次
9、发送CMD2,根据应答获取到CID
10、发送CMD3,分配一个rca值,根据应答获取到rca

三、linux的流程

drivers/mmc/host/sdhci-xxxx.c
sdhci_add_host
	__sdhci_add_host
		host->complete_wq = alloc_workqueue("sdhci", flags, 0); 
		timer_setup(&host->timer, sdhci_timeout_timer, 0);
		timer_setup(&host->data_timer, sdhci_timeout_data_timer, 0);
		sdhci_init(host, 0);
			sdhci_do_reset(host, SDHCI_RESET_ALL);
			sdhci_set_default_irqs(host);
		ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq,IRQF_SHARED,>mmc_hostname(mmc), host);
		ret = mmc_add_host(mmc);
			err = device_add(&host->class_dev);
			mmc_start_host(host);
				mmc_gpiod_request_cd_irq(host)
				_mmc_detect_change(host, 0, false);
					host->detect_change = 1;
					mmc_schedule_delayed_work(&host->detect, delay); // 触发mmc_rescan
driver/mmc/core/core.c
subsys_initcall(mmc_init);
	__init mmc_init(void) 
		ret = mmc_register_bus();
			bus_register(&mmc_bus_type);
		ret = mmc_register_host_class();
			class_register(&mmc_host_class);
		ret = sdio_register_bus(); 
			bus_register(&sdio_bus_type); 
			
drivers/mmc/core/host.c
drivers/mmc/host/mmci.c
module_amba_driver(mmci_driver);
#define module_amba_driver(__amba_drv) \
	module_driver(__amba_drv, amba_driver_register, amba_driver_unregister) 

static struct amba_driver mmci_driver = {
	...
	.probe	= mmci_probe,
	...
}

static struct mmc_host_ops mmci_ops = {
	.request	= mmci_request,
	.pre_req	= mmci_pre_request,
	.post_req	= mmci_post_request,
	.set_ios	= mmci_set_ios,
	.get_ro		= mmc_gpio_get_ro,
	.get_cd		= mmci_get_cd,
	.start_signal_voltage_switch = mmci_sig_volt_switch,
};


mmci_probe
	mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev);
	ret = mmci_of_parse(np, mmc);  
		alias_id = of_alias_get_id(dev->of_node, "mmc");
		dev_set_name(&host->class_dev, "mmc%d", host->index); 
		INIT_DELAYED_WORK(&host->detect, mmc_rescan);
		INIT_DELAYED_WORK(&host->sdio_irq_work, sdio_irq_work);
		timer_setup(&host->retune_timer, mmc_retune_timer, 0);
	host->mmc_ops = &mmci_ops;
	mmc->ops = &mmci_ops;
	host->clk = devm_clk_get(&dev->dev, NULL);
	ret = clk_prepare_enable(host->clk);
	host->mclk = clk_get_rate(host->clk)
	host->base = devm_ioremap_resource(&dev->dev, &dev->res);
	host->rst = devm_reset_control_get_optional_exclusive(&dev->dev, NULL);
	ret = mmc_regulator_get_supply(mmc);
	ret = devm_request_threaded_irq(&dev->dev, dev->irq[0], mmci_irq,mmci_irq_thread, IRQF_SHARED,DRIVER_NAME " (cmd)", host);
	mmci_dma_setup(host);
	mmc_add_host(mmc); 
mmc_rescan // 在sdhci_add_host中被触发
	if (!mmc_card_is_removable(host) && host->rescan_entered) return
	if (mmc_card_is_removable(host) && host->ops->get_cd &&  host->ops->get_cd(host) == 0) {
		mmc_power_off(host);
		mmc_release_host(host); 
	}
	for (i = 0; i < ARRAY_SIZE(freqs); i++) {
		unsigned int freq = freqs[i];
		if (freq > host->f_max) {
			if (i + 1 < ARRAY_SIZE(freqs))
				continue;
			freq = host->f_max;
		}
		if (!mmc_rescan_try_freq(host, max(freq, host->f_min)))
			break;
		if (freqs[i] <= host->f_min)
			break;
	}
	mmc_release_host(host);
	if (!(host->caps2 & MMC_CAP2_NO_SD)) 
		mmc_send_if_cond(host, host->ocr_avail);
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
	host->f_init = freq;
	mmc_power_up(host, host->ocr_avail);
		mmc_pwrseq_pre_power_on(host); 
		mmc_delay(host->ios.power_delay_ms);
		mmc_pwrseq_post_power_on(host);
		host->ios.power_mode = MMC_POWER_ON;
			mmc_set_ios(host);
		mmc_delay(host->ios.power_delay_ms); // This delay must be at least 74 clock sizes, or 1 ms,
	mmc_hw_reset_for_init(host);
		mmc_pwrseq_reset(host);
	if (!(host->caps2 & MMC_CAP2_NO_SDIO))
		sdio_reset(host);

	mmc_go_idle(host);
		cmd.opcode = MMC_GO_IDLE_STATE;
		cmd.arg = 0
		err = mmc_wait_for_cmd(host, &cmd, 0)

	if (!(host->caps2 & MMC_CAP2_NO_SD))
		mmc_send_if_cond(host, host->ocr_avail);
			cmd.opcode = SD_SEND_IF_COND; 
			cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern;
			err = mmc_wait_for_cmd(host, &cmd, 0);
			result_pattern = cmd.resp[0] & 0xFF;

	/* Order's important: probe SDIO, then SD, then MMC */
	if (!(host->caps2 & MMC_CAP2_NO_SDIO))
		if (!mmc_attach_sdio(host))
			return 0;

	if (!(host->caps2 & MMC_CAP2_NO_SD))
		if (!mmc_attach_sd(host))
			return 0;

	if (!(host->caps2 & MMC_CAP2_NO_MMC))
		if (!mmc_attach_mmc(host)) // 见下
			return 0;
	mmc_power_off(host);
	return -EIO;
}
static const struct mmc_bus_ops mmc_ops = {
	.remove = mmc_remove,
	.detect = mmc_detect,
	.suspend = mmc_suspend,
	.resume = mmc_resume,
	.runtime_suspend = mmc_runtime_suspend,
	.runtime_resume = mmc_runtime_resume,
	.alive = mmc_alive,
	.shutdown = mmc_shutdown,
	.hw_reset = _mmc_hw_reset,
};

mmc_attach_mmc
	mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN); 
		host->ios.bus_mode = mode;
		mmc_set_ios(host);
	err = mmc_send_op_cond(host, 0, &ocr);
		cmd.opcode = MMC_SEND_OP_COND;
		cmd.arg = mmc_host_is_spi(host) ? 0 : ocr; 
		for (i = 100; i; i--) {
			err = mmc_wait_for_cmd(host, &cmd, 0); 
			if (cmd.resp[0] & MMC_CARD_BUSY)
				break; 
			mmc_delay(10); // ms
			cmd.arg = cmd.resp[0] | BIT(30);
		}
		*rocr = cmd.resp[0];
	mmc_attach_bus(host, &mmc_ops);
		host->bus_ops = ops;
	rocr = mmc_select_voltage(host, ocr);
	err = mmc_init_card(host, rocr, NULL); 
		mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN); 
		mmc_go_idle(host);
		err = mmc_send_op_cond(host, ocr | (1 << 30), &rocr);
		err = mmc_send_cid(host, cid);
		card = mmc_alloc_card(host, &mmc_type);
		card->ocr = ocr;
		card->type = MMC_TYPE_MMC;
		card->rca = 1; 
		memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); 
		err = mmc_set_relative_addr(card);
			cmd.opcode = MMC_SET_RELATIVE_ADDR;
			cmd.arg = card->rca << 16; 
			mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES)
		mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
		err = mmc_send_csd(card, card->raw_csd);
			mmc_send_cxd_native(card->host, card->rca << 16,>csd,MMC_SEND_CSD);
		err = mmc_decode_csd(card);
		err = mmc_decode_cid(card); 
		
		err = mmc_select_card(card);
			_mmc_select_card(card->host, card);
				cmd.opcode = MMC_SELECT_CARD;
				cmd.arg = card->rca << 16; 
				mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES)
		err = mmc_read_ext_csd(card);
		
		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_ERASE_GROUP_DEF, 1,
						card->ext_csd.generic_cmd6_time); 
		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONFIG,	
						card->ext_csd.part_config, 
						card->ext_csd.part_time);

		err = mmc_select_timing(card);
			if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES)
				err = mmc_select_hs400es(card);
					err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180)
					err = mmc_select_bus_width(card); 
					err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
									EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS,
									card->ext_csd.generic_cmd6_time, 0,
									false, true); 
					mmc_set_timing(host, MMC_TIMING_MMC_HS);
					err = mmc_switch_status(card, true);
					mmc_set_clock(host, card->ext_csd.hs_max_dtr);
					val = EXT_CSD_DDR_BUS_WIDTH_8 | EXT_CSD_BUS_WIDTH_STROBE;
					err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
									EXT_CSD_BUS_WIDTH,
									val,
									card->ext_csd.generic_cmd6_time);
					mmc_select_driver_type(card);
					val = EXT_CSD_TIMING_HS400 | card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
					err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, 
									EXT_CSD_HS_TIMING, val,
									card->ext_csd.generic_cmd6_time, 0, 
									false, true);
					mmc_set_timing(host, MMC_TIMING_MMC_HS400);
					err = mmc_switch_status(card, true);
			else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200)
				err = mmc_select_hs200(card);
					mmc_select_driver_type(card);
					err = mmc_select_bus_width(card);
					val = EXT_CSD_TIMING_HS200 | card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
					err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
									EXT_CSD_HS_TIMING, val,
									card->ext_csd.generic_cmd6_time, 0,
									false, true);
					mmc_set_timing(host, MMC_TIMING_MMC_HS200);
					err = mmc_switch_status(card, false);
			else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS)
				err = mmc_select_hs(card);
					err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
									EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS,
									card->ext_csd.generic_cmd6_time, MMC_TIMING_MMC_HS,
									true, true); 
						
		
			if (mmc_card_hs200(card)) {  // card->host->ios.timing == MMC_TIMING_MMC_HS200;
				err = mmc_hs200_tuning(card);
					err = host->ops->execute_tuning(host, opcode);
				err = mmc_select_hs400(card);
			} else if (!mmc_card_hs400es(card)) {  // card->host->ios.enhanced_strobe;
				err = mmc_select_bus_width(card); 
				if (err > 0 && mmc_card_hs(card)) { // card->host->ios.timing == MMC_TIMING_SD_HS || card->host->ios.timing == MMC_TIMING_MMC_HS;
					err = mmc_select_hs_ddr(card);
						bus_width = host->ios.bus_width;
						ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? EXT_CSD_DDR_BUS_WIDTH_8 : EXT_CSD_DDR_BUS_WIDTH_4;
						err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, 
									EXT_CSD_BUS_WIDTH,
									ext_csd_bits,
									card->ext_csd.generic_cmd6_time,
									MMC_TIMING_MMC_DDR52,
									true, true);
						err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
	
			}
			if (card->ext_csd.cmdq_support && host->caps2 & MMC_CAP2_CQE) { 
				err = mmc_cmdq_enable(card);
					mmc_cmdq_switch(card, true);
			}
			
		
	mmc_release_host(host);
	err = mmc_add_card(host->card);
	mmc_claim_host(host);
	
<think>好的,用户现在想了解如何使用emmcbin工具将EMMC数据转存到FAT格式的USB磁盘,并恢复数据。首先,我需要确认emmcbin工具的具体用法,因为之前用户的问题是关于使用dd命令的,而这里提到了emmcbin,可能是一个特定的工具或者用户可能有拼写错误。不过,根据上下文,用户可能指的是某种类似dd的工具,或者是专门用于EMMC操作的二进制工具。 首先,我需要检查用户提供的引用内容。引用1提到fdisk的分区功能,引用2关于文件系统类型和挂载,引用3涉及MBR和GPT分区表。这些引用可能帮助用户理解分区和文件系统的基础知识,但用户现在的问题是关于emmcbin工具的具体使用,所以可能需要结合这些知识来指导操作步骤。 用户之前的问题是关于使用dd命令操作mmcblk0,现在转向emmcbin工具,可能需要比较两者的异同。假设emmcbin是一个特定的工具,我需要查找相关资料或手册,但考虑到可能拼写错误,也可能是mmc-utils或其他工具。如果找不到相关信息,可能需要基于已有的知识进行推断,或者建议用户确认工具名称的正确性。 接下来,用户希望将数据转存到FAT格式的USB磁盘。FAT文件系统有大小限制,比如FAT32不支持单个文件超过4GB,所以需要注意镜像文件的大小。可能需要分割镜像文件,或者建议使用exFAT格式,但用户明确提到FAT,所以需要处理这个问题。 步骤方面,首先需要确认USB设备已正确格式化为FAT,并且有足够的空间。然后使用emmcbin工具进行转储,假设其命令语法类似dd,可能需要指定输入输出文件。恢复时同样需要确保USB中的镜像正确。 此外,用户可能需要权限问题,如使用sudo,或者挂载USB设备。引用中提到fdisk和分区表,可能需要指导用户如何确认USB设备的分区是否正确,避免覆盖错误的分区。 需要验证步骤的正确性,比如检查镜像完整性,使用sync命令确保数据写入。同时,注意风险提示,如覆盖数据会导致数据丢失,确保用户备份重要信息。 可能用户遇到的问题包括工具不存在、权限不足、镜像文件过大等,需要在回答中提前给出解决方案,如安装工具、使用sudo、分割文件等。 最后,生成相关问题,帮助用户进一步了解相关操作,比如处理大文件、修复损坏镜像、权限问题等。</think>### 分步解决方案:使用 `emmcbin` 工具操作 EMMC 与 FAT 格式 USB 设备 (注:根据上下文推测,"emmcbin" 可能为特定工具或脚本,以下操作基于类 `dd` 工具逻辑,结合 FAT 文件系统特性实现) --- #### **一、准备工作** 1. **确认设备节点** - 通过 `lsblk` 或 `fdisk -l` 确认 EMMC 设备节点(如 `/dev/mmcblk0`)和 USB 设备节点(如 `/dev/sdX`)[^1]: ```bash sudo fdisk -l # 示例输出:Disk /dev/mmcblk0: 14.5 GB, 14547566592 bytes ``` 2. **格式化 USB 为 FAT32** - 若 USB 设备未格式化为 FAT32,使用 `mkfs.vfat` 格式化(*注意:此操作会清除 USB 所有数据*): ```bash sudo mkfs.vfat -F 32 /dev/sdX1 # -F 32 指定 FAT32 格式 ``` --- #### **二、转储 EMMC 数据到 FAT32 USB** 1. **创建 FAT32 兼容镜像** - 由于 FAT32 不支持单文件超过 4GB,需分割 EMMC 镜像(假设 EMMC 容量为 16GB): ```bash sudo emmcbin --dump /dev/mmcblk0 | split -d -b 4000M - emmc_backup_ # 生成 emmc_backup_00, emmc_backup_01 等分割文件 ``` 2. **复制到 USB 设备** - 挂载 USB 并拷贝分割文件: ```bash sudo mount /dev/sdX1 /mnt/usb sudo cp emmc_backup_* /mnt/usb/ sudo sync && sudo umount /mnt/usb ``` --- #### **三、从 FAT32 USB 恢复数据到 EMMC** 1. **合并分割镜像** - 挂载 USB 后合并文件: ```bash sudo mount /dev/sdX1 /mnt/usb cat /mnt/usb/emmc_backup_* > full_image.img ``` 2. **写入 EMMC** ```bash sudo emmcbin --restore full_image.img /dev/mmcblk0 # 或通过管道直接写入: cat /mnt/usb/emmc_backup_* | sudo emmcbin --restore - /dev/mmcblk0 ``` --- #### **四、关键参数说明** | 参数/命令 | 作用 | |------------------------|----------------------------------------------------------------------| | `--dump` | 指定转储模式,后接源设备(如 `/dev/mmcblk0`) | | `--restore` | 指定恢复模式,后接镜像文件或 `-`(表示从标准输入读取) | | `split -b 4000M` | 按 4000MB 分割文件(预留 FAT32 4GB 限制的冗余) | | `mkfs.vfat -F 32` | 强制格式化为 FAT32 文件系统 | --- #### **五、风险控制** 1. **文件系统限制规避** -EMMC 镜像超过 4GB,必须分割文件(如使用 `split`)[^2] - 或改用 exFAT 格式(需内核支持): ```bash sudo mkfs.exfat /dev/sdX1 ``` 2. **数据完整性验证** ```bash sha256sum /dev/mmcblk0 > emmc_checksum.txt # 转储前生成校验码 sha256sum full_image.img # 恢复前比对校验码 ``` --- ### 相关问题 1. 如何绕过 FAT32 的 4GB 文件限制直接备份大容量 EMMC 镜像? 2. 使用 `emmcbin` 工具时如何跳过坏块或损坏扇区? 3. 在 Windows 系统下如何合并分割的 `emmc_backup_*` 文件? --- [^1]: 通过 `fdisk` 或 `lsblk` 可以明确识别存储设备的物理分区结构,避免误操作覆盖错误设备。 [^2]: FAT32 文件系统的单文件限制源于其 32 位文件寻址设计,需通过分割或升级文件系统规避。 [^3]: 若 USB 设备使用 GPT 分区表,需注意保护性 MBR 对兼容性的影响。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值