串口是非常有效和廉价的Debug手段,在开发中,几乎所有的UEFI 主板都会支持串口,本文介绍如何在Shell下面实现 串口通讯。
与之相关的是 EFI_SERIAL_IO_PROTOCOL,这个 Protocol 的定义可以在 UEFI Spec【参考1】中看到:
代码如下:
#include <Uefi.h> #include <Library/UefiLib.h> #include <Library/ShellCEntryLib.h> #include <Protocol/SerialIo.h> extern EFI_BOOT_SERVICES *gBS; extern EFI_SYSTEM_TABLE *gST; extern EFI_RUNTIME_SERVICES *gRT; EFI_GUID gEfiSerialIoProtocolGuid = { 0xBB25CF6F, 0xF1D4, 0x11D2, { 0x9A, 0x0C, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0xFD }}; EFI_STATUS DumpSetting( IN UINT64 BaudRate, IN UINT32 ReceiveFifoDepth, IN UINT32 Timeout, IN EFI_PARITY_TYPE Parity, IN UINT8 DataBits, IN EFI_STOP_BITS_TYPE StopBits) { Print(L" Timeout: [%d]\n",Timeout); Print(L" BaudRate:[%ld]\n",BaudRate); Print(L" DataBits:[%d]\n",DataBits); Print(L" Parity: [%d]\n",Parity); Print(L" StopBits:[%d]\n",StopBits); Print(L" ReceiveFifoDepth:[%d]\n",ReceiveFifoDepth); return EFI_SUCCESS; } int EFIAPI main ( IN int Argc, IN char **Argv ) { EFI_STATUS Status; EFI_SERIAL_IO_PROTOCOL *Serial; CHAR8 *Textbuf1 = "www.lab-z.com waiting.........\n"; CHAR8 *Textbuf2 = "Continue............\n"; CHAR8 *Textbuf3 = "12345678"; CHAR16 *Textbuf4 = L" "; UINTN BufferSize; EFI_TIME TimeStart,TimeEnd; Status = gBS->LocateProtocol( &gEfiSerialIoProtocolGuid, NULL, (VOID **)&Serial); if (EFI_ERROR(Status)) { Print(L"Cannot find EFI_SERIAL_IO_PROTOCOL \r\n"); return Status; } Print(L"Current Settings:\n"); DumpSetting( Serial->Mode->BaudRate, Serial->Mode->ReceiveFifoDepth, Serial->Mode->Timeout, Serial->Mode->Parity, Serial->Mode->DataBits & 0xFF, Serial->Mode->StopBits); // Baudrate 115200,Data Bits=8,Parity=None,Stop Bits=1,Flow Type= None Status=Serial->SetAttributes( Serial, 115200, Serial->Mode->ReceiveFifoDepth, Serial->Mode->Timeout, NoParity, 8, OneStopBit); if (Status!=EFI_SUCCESS) { Print(L"[%d], %r",Status,Status); } Print(L"New Settings:\n"); DumpSetting( Serial->Mode->BaudRate, Serial->Mode->ReceiveFifoDepth, Serial->Mode->Timeout, Serial->Mode->Parity, Serial->Mode->DataBits & 0xFF, Serial->Mode->StopBits); BufferSize=AsciiStrLen(Textbuf1); Serial->Write(Serial,&BufferSize,Textbuf1); gRT->GetTime(&TimeStart,NULL); TimeEnd=TimeStart; while ((TimeEnd.Hour - TimeStart.Hour) * 60 * 60 + (TimeEnd.Minute - TimeStart.Minute)*60 + (TimeEnd.Second - TimeStart.Second) < 30) { BufferSize=AsciiStrLen(Textbuf3); Status=Serial->Read(Serial,&BufferSize,Textbuf3); if ((Status==EFI_SUCCESS) && (BufferSize!=0)) { Print(L"read [%d] %s\n",BufferSize,AsciiStrToUnicodeStr(Textbuf3,Textbuf4)); } gRT->GetTime(&TimeEnd,NULL); } BufferSize=AsciiStrLen(Textbuf2); Serial->Write(Serial,&BufferSize,Textbuf2); return EFI_SUCCESS; }
上面程序的基本流程:首先检查一下串口设置,打印在屏幕上,然后设置为我们通常使用的 115200。最后测试用 Write从 Shell 下发送数据出来,再尝试用Read接收数据。
程序运行结果:
完整代码下载:
特别注意:Shell 使用 Read 只能接收固定长度的数据。比如: Read(Serial,8,Textbuf) 那么只能接收8个字符,如果你只输入了7个bytes,不会有反应;如果输入了9个bytes,那么只能收到前面8个。目前不清楚为什么有这样的限制。
另外,对于普通的串口,使用GetControl 获得的当前的状态中并没有当前串口的发送接收状态。定义的 EFI_SERIAL_INPUT_BUFFER_EMPTY和EFI_SERIAL_OUTPUT_BUFFER_EMPTY 应该是给存在对应线路的串口使用的,是一种硬件线路的标志。如果你只用了 TX RX GND , 这里的状态是没有意义的。
参考:
1. UEFI Spec 2.4 P476
read無法耶,我用print status來看是12號錯誤
就是找不到這個device medium
Nt32模拟环境下不工作,你要在实体机上试试。
请问,对于一台机器有多个串口,如何指定从哪个串口通讯?
每个串口上都有一个 SERIAL_IO_PROTOCOL, 所以你要先确定是哪个 SERIAL_IO_PROTOCOL ,然后用它就好了