eMMC 应用在平板电脑上已经有一段时间了,本文给出了一个取得 eMMC CID 的例子。当然,你在其他资料上还会发现一些类似 MMC_INFO 之类的Protocol,但是根据我的实验,目前大多数系统只支持SD HOST IO 这一个 Protocol。此外,强烈建议一定在实体机上进行实验,避免出现费了很大力气编写代码,但是实际上第一步就无法完成的问题。
原理介绍:首先查找系统中的 SD_HOST_IO 的Protocol .目前看起来系统中只有一个这样的 Protocol ,所以我们用 LocateProtocol 就足够了。取得之后,通过这个 Protocol,对 eMMC 发送command。这个做法和 ATA 设备的 PassThrough Protocol很像。下面的代码是取得CID信息的,CID是用来识别eMMC一些基本信息的寄存器,比如 Serial Number,具体定义在 eMMC Specification中可以找到【参考1】 。
“8.2 CID register
The Card IDentification (CID) register is 128 bits wide. It contains the card identification information used during the card identification phase (MultiMediaCard protocol). Every individual flash or I/O card shall have an unique identification number. Every type of MultiMediaCard ROM cards (defined by content) shall have an unique identification number. Table 41 on page 112 lists these identifiers.The structure of the CID register is defined in the following sections”
代码:
#include <Uefi.h> #include <Library/UefiLib.h> #include <Library/ShellCEntryLib.h> #include <Library/MemoryAllocationLib.h> #include <Protocol/BlockIo.h> #include "SDHostIo.h" #include "mmc.h" EFI_GUID gEfiSdHostIoProtocolGuid = { 0xb63f8ec7, 0xa9c9, 0x4472, {0xa4, 0xc0, 0x4d, 0x8b, 0xf3, 0x65, 0xcc, 0x51}}; // // Command timeout will be max 100 ms // #define TIMEOUT_COMMAND 100 extern EFI_BOOT_SERVICES *gBS; EFI_STATUS SendCommand ( IN EFI_SD_HOST_IO_PROTOCOL *This, IN UINT16 CommandIndex, IN UINT32 Argument, IN TRANSFER_TYPE DataType, IN UINT8 *Buffer, OPTIONAL IN UINT32 BufferSize, IN RESPONSE_TYPE ResponseType, IN UINT32 TimeOut, OUT UINT32 *ResponseData ) /*++ Routine Description: Send command by using Host IO protocol Arguments: This - Pointer to EFI_SD_HOST_IO_PROTOCOL CommandIndex - The command index to set the command index field of command register Argument - Command argument to set the argument field of command register DataType - TRANSFER_TYPE, indicates no data, data in or data out Buffer - Contains the data read from / write to the device BufferSize - The size of the buffer ResponseType - RESPONSE_TYPE TimeOut - Time out value in 1 ms unit ResponseData - Depending on the ResponseType, such as CSD or card status Returns: EFI_INVALID_PARAMETER EFI_UNSUPPORTED EFI_DEVICE_ERROR EFI_SUCCESS --*/ { EFI_STATUS Status; Status = This->SendCommand ( This, CommandIndex, Argument, DataType, Buffer, BufferSize, ResponseType, TimeOut, ResponseData ); if (!EFI_ERROR (Status)) { if (ResponseType == ResponseR1 || ResponseType == ResponseR1b) { //ASSERT(ResponseData != NULL); Print(L"Error with code [%d]",(*ResponseData)); } } else { This->ResetSdHost (This, Reset_DAT_CMD); } return Status; } int EFIAPI main ( IN UINT32 Argc, IN CHAR16 **Argv ) { EFI_SD_HOST_IO_PROTOCOL *SdHostIo; EFI_STATUS Status; CID CIDReg; OCR OCRReg; UINT32 TimeOut=5000; Status = gBS->LocateProtocol ( &gEfiSdHostIoProtocolGuid, NULL, &SdHostIo ); if (EFI_ERROR (Status)) { Print(L"No SdHost driver, Application is exiting!\n"); return Status; } SdHostIo->EnableAutoStopCmd (SdHostIo, TRUE); SdHostIo->SetupDevice (SdHostIo); // // Go to Idle // SendCommand ( SdHostIo, GO_IDLE_STATE, 0, NoData, NULL, 0, ResponseNo, TIMEOUT_COMMAND, NULL ); gBS->Stall (100 * 1000); // // Check voltage support, first time we use 0x40FF8080 // SendCommand ( SdHostIo, SEND_OP_COND, 0x40FF8080, NoData, NULL, 0, ResponseR3, TIMEOUT_COMMAND, (UINT32*)&(OCRReg) ); while (OCRReg.Busy != 1) { OCRReg.AccessMode = 0x02; // sector mode; SendCommand ( SdHostIo, SEND_OP_COND, *(UINT32*)&(OCRReg), NoData, NULL, 0, ResponseR3, TIMEOUT_COMMAND, (UINT32*)&(OCRReg) ); gBS->Stall(100); TimeOut--; if (TimeOut == 0) { Print(L"Card is always busy\n"); Status = EFI_TIMEOUT; goto Exit; } } SendCommand( SdHostIo, ALL_SEND_CID, 0, NoData, NULL, 0, ResponseR2, TIMEOUT_COMMAND, (UINT32*)&(CIDReg) ); Print (L" Product serial number : %X\n",CIDReg.PSN); Print (L" Product revision : %X\n",CIDReg.PRV); Print (L" Product name : %c%c%c%c%c%c\n", CIDReg.PNM[0], CIDReg.PNM[1], CIDReg.PNM[2], CIDReg.PNM[3], CIDReg.PNM[4], CIDReg.PNM[5]); Print (L" Manufacturer ID : %X\n",CIDReg.MID); Exit: return EFI_SUCCESS; }
完整代码下载,其中还有代码生成的EFI程序,是 X64的。
getemmc
本文使用的头文件都是来自新版的 EDK2(比 UDK2015要新一些,我觉得UDK2017有可能会正式加入吧)【参考2】,有兴趣的读者可以自行查阅。
最后,既然有了CID,那么还可以读取一些关于 eMMC的其他信息,比如容量。请读者自己尝试完成。
最后的最后,推荐 Lenovo 出品的一款擦除 eMMC的工具,在 Shell 下运行,可以很快擦掉全部内容(应该使用 eMMC 的 Command 直接Erase的,所以能够达到很快的速度)。
gufd01ww
参考:
1. http://www.jedec.org/standards-documents/results/jesd84-b51 JESD84-A44.pdf
2. https://github.com/tianocore/edk2
3. http://support.lenovo.com/us/en/downloads/ds100934
作者你好,我看了你的uefi获取MMC信息的代码,我现在遇到个问题,就是在我执行你这段代码以后,再使用BlockIo读取硬盘会失败,这是什么原因
不好意思,最近忙刚看到。你是获取emmc的失败还是获取其他硬盘的操作失败?回头我试试。
我是获取eMMC信息成功了,但之后我想调用BlockIo进行读取硬盘操作却失败了,如果没有执行过这个程序,则用BlockIo读硬盘就没问题,一旦执行过这个程序,除非重启,否则BlockIo读硬盘一定失败
而且我只进行EnableAutoStopCmd 这个操作,SendCommand不执行,BlockIo读取硬盘也会失败,而且我用的是OpenProtocol打开SdHostIo,再用CloseProtocol关闭,这样也不行,很奇怪
你再试试 Init一下看看?
不论是初始化还是reset我都试了,我想问作者你先调用sdhostIo获取eMMC信息再调用BlockIo读硬盘可以读取成功吗
不好意思,没试验过
作者你好,我在shell执行完你这个获取eMMC信息的程序之后,我调用BlockIo读取硬盘就会失败,这是为什么
博主您好,下载了您的源文件,发现没有驱动的实际文件,只有一个Application调用。想问下您这边有源文件吗?
getemmc 这个里面有源代码的