青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

string

string
posts - 27, comments - 177, trackbacks - 0, articles - 0
  C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

UEFI實(shí)戰(zhàn)(5) driver

Posted on 2012-04-22 08:36 djx_zh 閱讀(17002) 評(píng)論(10)  編輯 收藏 引用
UEFI driver框架
上一節(jié)我們講了服務(wù)型驅(qū)動(dòng),它有如下幾個(gè)特點(diǎn):
1. 在Image的入口函數(shù)中執(zhí)行安裝。
2. 服務(wù)型驅(qū)動(dòng)不需要驅(qū)動(dòng)特定硬件,可以安裝到任意controller上。
3. 沒有提供卸載函數(shù)。
一個(gè)真正的驅(qū)動(dòng)程序,在安裝時(shí)首先要找到對(duì)應(yīng)的硬件設(shè)備(在UEFI中是要找到對(duì)應(yīng)的Controller), 然后執(zhí)行安裝操作,將驅(qū)動(dòng)程序安裝到硬件設(shè)備的controller上。 有時(shí)候我們還需要卸載驅(qū)動(dòng),更新驅(qū)動(dòng)(先卸載舊的驅(qū)動(dòng),然后安裝新的驅(qū)動(dòng))。 有時(shí)候安裝操作可能需要執(zhí)行多次,例如:第一次安裝時(shí)發(fā)現(xiàn)設(shè)備沒有準(zhǔn)備好,或者所依賴的某個(gè)Protocol沒有安裝,就需要退出安裝,執(zhí)行其他操作,然后進(jìn)行第二次安裝。
那么我們可以總結(jié)出一個(gè)完整的驅(qū)動(dòng)程序框架需要三部分:
1.  Findout() 找出對(duì)應(yīng)的硬件設(shè)備
2.  Install()  安裝驅(qū)動(dòng)到指定的硬件設(shè)備 或者說 Start() 啟動(dòng)硬件設(shè)備
3. Uninstall()從硬件設(shè)備中卸載驅(qū)動(dòng)  或者說 Stop() 停止硬件設(shè)備。
另外很重要的一點(diǎn)是框架必須支持多次安裝。 上一節(jié)我們實(shí)現(xiàn)的驅(qū)動(dòng)是不能多次安裝的(在入口函數(shù)中執(zhí)行安裝),如果首次安裝失敗例如InstallProtocolInterface(...)返回錯(cuò)誤,我們只能unload 驅(qū)動(dòng)文件(image),從新loadImage。
我們來看UEFI驅(qū)動(dòng)框架是如何實(shí)現(xiàn)這幾點(diǎn)的。
在UEFI驅(qū)動(dòng)的入口函數(shù)中,安裝EFI Driver Binding Protocol到某個(gè)Handle(大部分情況下是自身即ImageHandle, 有時(shí)也會(huì)安裝到其它Handle上), 這個(gè)Driver Binding Protocol實(shí)例會(huì)常駐內(nèi)存,用于驅(qū)動(dòng)的安裝和卸載。使用DriverBindingProtocol使得我們可以多次操作(查找設(shè)備,安裝卸載)驅(qū)動(dòng).
在Driver Binding Protocol中實(shí)現(xiàn)了框架的三個(gè)部分的接口。下面是DriverBindingProtocol的聲明:
//
/// This protocol provides the services required to determine if a driver supports a given controller. 
/// If a controller is supported, then it also provides routines to start and stop the controller.
///
struct _EFI_DRIVER_BINDING_PROTOCOL {
  EFI_DRIVER_BINDING_SUPPORTED  Supported;
  EFI_DRIVER_BINDING_START      Start;
  EFI_DRIVER_BINDING_STOP       Stop;

  
///
  
/// The version number of the UEFI driver that produced the
  
/// EFI_DRIVER_BINDING_PROTOCOL. This field is used by
  
/// the EFI boot service ConnectController() to determine
  
/// the order that driver's Supported() service will be used when
  
/// a controller needs to be started. EFI Driver Binding Protocol
  
/// instances with higher Version values will be used before ones
  
/// with lower Version values. The Version values of 0x0-
  
/// 0x0f and 0xfffffff0-0xffffffff are reserved for
  
/// platform/OEM specific drivers. The Version values of 0x10-
  
/// 0xffffffef are reserved for IHV-developed drivers.
  
///
  UINT32                        Version;


  
///
  
/// The image handle of the UEFI driver that produced this instance
  
/// of the EFI_DRIVER_BINDING_PROTOCOL.
  
///
  EFI_HANDLE                    ImageHandle;

  
///
  
/// The handle on which this instance of the
  
/// EFI_DRIVER_BINDING_PROTOCOL is installed. In most
  
/// cases, this is the same handle as ImageHandle. However, for
  
/// UEFI drivers that produce more than one instance of the
  
/// EFI_DRIVER_BINDING_PROTOCOL, this value may not be
  
/// the same as ImageHandle.  
  
///
  EFI_HANDLE                    DriverBindingHandle;
};
核心是Support,Start,Stop三個(gè)成員函數(shù),對(duì)應(yīng)我們總結(jié)的三個(gè)部分。
首先看Support函數(shù),簡(jiǎn)單來講,如果ControllerHandle是我們要找的Controoler,該函數(shù)返回EFI_SUCCESS, 否則返回EFI_UNSUPPORTED、 EFI_ACCESS_DENIED或EFI_ALREADY_STARTED等等。
typedef
EFI_STATUS
(EFIAPI 
*EFI_DRIVER_BINDING_PROTOCOL_SUPPORTED) (
IN EFI_DRIVER_BINDING_PROTOCOL 
*This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL 
*RemainingDevicePath OPTIONAL  // Device driver 忽略該參數(shù),
                                                                                           //Bus Driver(例如MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.c)才會(huì)使用該參數(shù)。
);
再看Start函數(shù),Start()用來啟動(dòng)硬件設(shè)備,在函數(shù)中最重要的事情是調(diào)用InstallProtocolInterface()或者InstallMultipleProtocolInterfaces()在ControllerHandle上安裝驅(qū)動(dòng)Protocol。
typedef
EFI_STATUS
(EFIAPI 
*EFI_DRIVER_BINDING_PROTOCOL_START) (
IN EFI_DRIVER_BINDING_PROTOCOL 
*This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL 
*RemainingDevicePath OPTIONAL
);
Stop函數(shù),用于卸載驅(qū)動(dòng)(調(diào)用UninstallProtocolInterface()或UninstallMultipleProtocolInterfaces()從ControllerHandle卸載驅(qū)動(dòng)協(xié)議),并停止硬件設(shè)備。
typedef
EFI_STATUS
(EFIAPI 
*EFI_DRIVER_BINDING_PROTOCOL_STOP) (
IN EFI_DRIVER_BINDING_PROTOCOL 
*This,
IN EFI_HANDLE ControllerHandle,
IN UINTN NumberOfChildren,
IN EFI_HANDLE 
*ChildHandleBuffer OPTIONAL
);
對(duì)Device Driver來講,NumberOfChildren 為0, ChildHandleBuffer 為NULL。 對(duì)Bus Driver來講,如果NumberOfChildren不為零,那么ChildHandleBuffer中的子節(jié)點(diǎn)都要被釋放。
我們分別研究了驅(qū)動(dòng)框架的三個(gè)部分,這三個(gè)部分是如何聯(lián)系起來的呢?看下面的代碼(MdeModulePkg/Core/Dxe/Hand/DriverSupport.c:CoreConnectSingleController),可以大致了解UEFI驅(qū)動(dòng)程序框架是如何工作的。
do {

    //
    
// Loop through the sorted Driver Binding Protocol Instances in order, and see if
    
// any of the Driver Binding Protocols support the controller specified by
    
// ControllerHandle.
    
//
    DriverBinding = NULL;
    DriverFound = FALSE;
    for (Index = 0; (Index < NumberOfSortedDriverBindingProtocols) && !DriverFound; Index++) {
      if (SortedDriverBindingProtocols[Index] != NULL) {
        DriverBinding = SortedDriverBindingProtocols[Index];
        PERF_START (DriverBinding->DriverBindingHandle, "DB:Support:", NULL, 0);
        Status = DriverBinding->Supported(
                                  DriverBinding,
                                  ControllerHandle,
                                  RemainingDevicePath
                                  );
        PERF_END (DriverBinding->DriverBindingHandle, "DB:Support:", NULL, 0);
        if (!EFI_ERROR (Status)) {
          SortedDriverBindingProtocols[Index] = NULL;
          DriverFound = TRUE;

          //
          
// A driver was found that supports ControllerHandle, so attempt to start the driver
          
// on ControllerHandle.
          
//
          PERF_START (DriverBinding->DriverBindingHandle, "DB:Start:", NULL, 0);
          Status = DriverBinding->Start (
                                    DriverBinding,
                                    ControllerHandle,
                                    RemainingDevicePath
                                    );
          PERF_END (DriverBinding->DriverBindingHandle, "DB:Start:", NULL, 0);

          if (!EFI_ERROR (Status)) {
            //
            
// The driver was successfully started on ControllerHandle, so set a flag
            
//
            OneStarted = TRUE;
          }
        }
      }
    }
  } while (DriverFound);
CoreConnectSingleController(IN  EFI_HANDLE ControllerHandle,  IN  EFI_HANDLE *ContextDriverImageHandles OPTIONAL,  IN  EFI_DEVICE_PATH_PROTOCOL  *RemainingDevicePath OPTIONAL)用于為ControllerHandle安裝驅(qū)動(dòng)。如果 ContextDriverImageHandles為空, 則遍歷系統(tǒng)中的所有DriverBindingProtocol,否則就只遍歷指定的DriverBindingProtocol。SortedDriverBindingProtocols[]存放了需要測(cè)試的DriverBindingProtocol, 對(duì)于每一個(gè)需要測(cè)試的DriverBindingProtocol,首先調(diào)用DriverBinding->Supported(...)測(cè)試該DriverBindingProtocol是否支持ControllerHandle, 如果Supported函數(shù)返回EFI_SUCCESS,則調(diào)用DriverBinding->Start(...)向ControllerHandle安裝驅(qū)動(dòng),啟動(dòng)設(shè)備。
CoreDisconnectController(
  IN  EFI_HANDLE  ControllerHandle,
  IN  EFI_HANDLE  DriverImageHandle  OPTIONAL,
  IN  EFI_HANDLE  ChildHandle        OPTIONAL
  ) 用于卸載掉ControllerHandle上指定的驅(qū)動(dòng)(若DriverImageHandle 則卸載掉ControllerHandle上的所有驅(qū)動(dòng))。
看MdeModulePkg/Core/Dxe/Hand/DriverSupport.c:876
 if (ChildHandle == NULL || ChildHandleValid) {
        ChildrenToStop 
= 0;
        Status 
= EFI_SUCCESS;
        
if (ChildBufferCount > 0) {
          
if (ChildHandle != NULL) {
            ChildrenToStop 
= 1;
            Status 
= DriverBinding->Stop (DriverBinding, ControllerHandle, ChildrenToStop, &ChildHandle);
          } 
else {
            ChildrenToStop 
= ChildBufferCount;
            Status 
= DriverBinding->Stop (DriverBinding, ControllerHandle, ChildrenToStop, ChildBuffer);
          }
        }
        
if (!EFI_ERROR (Status) && ((ChildHandle == NULL) || (ChildBufferCount == ChildrenToStop))) {
          Status 
= DriverBinding->Stop (DriverBinding, ControllerHandle, 0, NULL);
        }
        
if (!EFI_ERROR (Status)) {
          StopCount
++;
        }
      }

了解了各個(gè)部分的細(xì)節(jié)后,我們?cè)诳匆槐榧虞dDriver的整個(gè)過程。
首先在Shell中使用命令Load 將Driver文件加載到內(nèi)存, Load后UEFI會(huì)調(diào)用gBS->StartImage(...) 執(zhí)行DriverImage的入口函數(shù), 在入口函數(shù)里Driver Binding Protocol被加載到Handle上(Driver Image handle 或者其它的Controller handle),然后UEFI會(huì)遍歷所有的Controller,調(diào)用Driver Binding Protocol的Supported 函數(shù)測(cè)試這個(gè)Driver是否支持該Controller,如果支持則調(diào)用Start()安裝驅(qū)動(dòng)。

EFI Component Name Protocol

編寫Driver
驅(qū)動(dòng)分為兩部分,一部分是硬件相關(guān)的部分,這部分是驅(qū)動(dòng)的內(nèi)容,用于驅(qū)動(dòng)硬件設(shè)備,為用戶提供服務(wù),以協(xié)議的形式出現(xiàn),例如DiskIo,BlockIo。 另一部分驅(qū)動(dòng)的框架部分,需要實(shí)現(xiàn)Driver Binding Protocol,主要是其三個(gè)接口(Supported, Start, Stop),這部分用于驅(qū)動(dòng)的安裝與卸載。硬件相關(guān)部分不是本節(jié)講述的重點(diǎn)。 
本節(jié)主要講述Device Driver如何實(shí)現(xiàn)框架部分。
Spec中詳細(xì)描述了如何編寫Supported, Start, Stop 三個(gè)函數(shù),下面的斜體內(nèi)容翻譯至Spec2.3
Supported函數(shù)要點(diǎn)(Spec2.3:339)
1. 忽略參數(shù) RemainingDevicePath
2. 使用函數(shù)OpenProtocol()打開所有需要的Protocols。 標(biāo)準(zhǔn)的驅(qū)動(dòng)要用EFI_OPEN_PROTOCOL_BY_DRIVER屬性打開Protocol。 如果要獨(dú)占某個(gè)Protocol,首先要關(guān)閉所有使用該P(yáng)rotocol的其它驅(qū)動(dòng),然后用屬性EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE打開Protocol。
3. 如果(2)中OpenProtocol()返回錯(cuò)誤,調(diào)用CloseProtcol()關(guān)閉所有已經(jīng)打開的Protocol并返回錯(cuò)誤代碼。
4. 所需的所有Protocols成功打開,則測(cè)試這個(gè)Driver是否支持此Controller。有時(shí)使用這些Protocols足以完成測(cè)試,有時(shí)還需要此Controller的其它特征。如果任意一項(xiàng)測(cè)試失敗,則用CloseProtocol()關(guān)閉所有打開的Protocol,返回EFI_UNSUPPORTED.
5. 測(cè)試成功,調(diào)用CloseProtocol()關(guān)閉所有已經(jīng)打開的Protocols。
6. 返回 EFI_SUCCESS。
Start函數(shù)要點(diǎn)(spec2.3:345)
1. 忽略參數(shù) RemainingDevicePath
2. 使用函數(shù)OpenProtocol()打開所有需要的Protocols。 標(biāo)準(zhǔn)的驅(qū)動(dòng)要用EFI_OPEN_PROTOCOL_BY_DRIVER屬性打開Protocol。 如果要獨(dú)占某個(gè)Protocol,首先要關(guān)閉所有使用該P(yáng)rotocol的其它驅(qū)動(dòng),然后用屬性EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE打開Protocol。
3. 如果(2)中OpenProtocol()返回錯(cuò)誤,調(diào)用CloseProtcol()關(guān)閉所有已經(jīng)打開的Protocol并返回錯(cuò)誤代碼。
4. 初始化ControllerHandle所指的設(shè)備。如果有錯(cuò)誤,則關(guān)閉所有已打開的Protocols并返回 EFI_DEVICE_ERROR。
5. 分配并初始化要用到的數(shù)據(jù)結(jié)構(gòu),這些數(shù)據(jù)結(jié)構(gòu)包括驅(qū)動(dòng)Protocols及其它相關(guān)的私有數(shù)據(jù)結(jié)構(gòu)。如果分配資源時(shí)發(fā)生錯(cuò)誤,則關(guān)閉所有已打開的Protocols,釋放已經(jīng)得到的資源,返回EFI_OUT_OF_RESOURCES。
6. 用InstallMultipleProtocolInterfaces()安裝驅(qū)動(dòng)協(xié)議到ControllerHandle。如果有錯(cuò)誤發(fā)生,則關(guān)閉所有已打開的Protocols,并返回錯(cuò)誤代碼。
7. 返回EFI_SUCESS。
Stop函數(shù)要點(diǎn)(spec2.3:)
1. 用UninstallMultipleProtocolInterfaces() Uninstall 所安裝的Protocols。
2. 關(guān)閉所有已打開的Protocols。
3. 釋放所有已申請(qǐng)的資源。

下面我們以AC97 控制器驅(qū)動(dòng)為例,介紹如何編寫Device driver。南橋芯片中集成的AC97控制器是一種PCI設(shè)備,在開始寫驅(qū)動(dòng)之前讓我們補(bǔ)充一點(diǎn)PCI設(shè)備驅(qū)動(dòng)及AC97聲卡的背景知識(shí)。
首先讓我們建立實(shí)驗(yàn)環(huán)境, 在QEMU中選擇開啟 Audio ,類型選擇為Intel AC97.
PCI設(shè)備及PciIo
每個(gè)PCI設(shè)備都有三種地址空間:配置空間,IO空間和內(nèi)存空間。
系統(tǒng)初始化時(shí)系統(tǒng)會(huì)初始化每個(gè)PCI設(shè)備的配置空間寄存器。配置地址空間大小為256字節(jié),前64字節(jié)是標(biāo)準(zhǔn)的,后面的寄存器由設(shè)備自定義用途。

 

0x0

0x1

0x2

0x3

0x4

0x5

0x6

0x7

0x8

0x9

0xa

0xb

0xc

0xd

0xe

0xf

0x00

Vendor ID

Device ID

Command Reg.

Status Reg.

Revision ID

Class Code

Cache

Line

Latency

Timer

Header

Type

BIST

0x10

Base Address 0

Base Address 1

Base Address 2

Base Address 3

0x20

Base Address 4

Base Address 5

Card Bus CIS pointer

Subsytem Vendor ID

Subsystem Device ID

0x30

Expansion ROM Base Address

 

 

 

 

 

 

 

 

IRQ Line

IRQ Pin

Min_Gnt

Max_lat


例如Qemu中AC97的配置空間內(nèi)容如下
PciRoot(0x0)/Pci(0x4,0x0) 
UINT16  VendorId :8086 
UINT16  DeviceId :2415
UINT16  Command  :7 
UINT16  Status   :280
UINT8   RevisionID : 1
UINT8   ClassCode[2] : 4
UINT8   ClassCode[1] : 1
UINT8   ClassCode[0] : 0
UINT8   CacheLineSize :0
UINT8   LatencyTimer : 0
UINT8   HeaderType :   0
UINT8   BIST :         0
Bar[0] :  C001      
Bar[1] :  C401      
Bar[2] :  0  
Bar[3] :  0      
Bar[4] :  0      
Bar[5] :  0    

PCI設(shè)備中的IO和內(nèi)存空間被劃分為1~6個(gè)互補(bǔ)重疊的子空間,每個(gè)子空間用于完成一組相對(duì)獨(dú)立的子功能。Base Address 0 ~5 表示子空間的基地址(物理地址)。
對(duì)設(shè)備的操作主要是通過對(duì)子空間的讀寫來實(shí)現(xiàn)的。 UEFI提供了EFI_PCI_IO_PROTOCOL來操作PCI設(shè)備。 
///
/// The EFI_PCI_IO_PROTOCOL provides the basic Memory, I/O, PCI configuration,
/// and DMA interfaces used to abstract accesses to PCI controllers.
/// There is one EFI_PCI_IO_PROTOCOL instance for each PCI controller on a PCI bus.
/// A device driver that wishes to manage a PCI controller in a system will have to
/// retrieve the EFI_PCI_IO_PROTOCOL instance that is associated with the PCI controller.
///
struct _EFI_PCI_IO_PROTOCOL {
  EFI_PCI_IO_PROTOCOL_POLL_IO_MEM         PollMem;
  EFI_PCI_IO_PROTOCOL_POLL_IO_MEM         PollIo;
  EFI_PCI_IO_PROTOCOL_ACCESS              Mem;
  EFI_PCI_IO_PROTOCOL_ACCESS              Io;
  EFI_PCI_IO_PROTOCOL_CONFIG_ACCESS       Pci;
  EFI_PCI_IO_PROTOCOL_COPY_MEM            CopyMem;
  EFI_PCI_IO_PROTOCOL_MAP                 Map;
  EFI_PCI_IO_PROTOCOL_UNMAP               Unmap;
  EFI_PCI_IO_PROTOCOL_ALLOCATE_BUFFER     AllocateBuffer;
  EFI_PCI_IO_PROTOCOL_FREE_BUFFER         FreeBuffer;
  EFI_PCI_IO_PROTOCOL_FLUSH               Flush;
  EFI_PCI_IO_PROTOCOL_GET_LOCATION        GetLocation;
  EFI_PCI_IO_PROTOCOL_ATTRIBUTES          Attributes;
  EFI_PCI_IO_PROTOCOL_GET_BAR_ATTRIBUTES  GetBarAttributes;
  EFI_PCI_IO_PROTOCOL_SET_BAR_ATTRIBUTES  SetBarAttributes;

  ///
  
/// The size, in bytes, of the ROM image.
  
///
  UINT64                                  RomSize;

  ///
  
/// A pointer to the in memory copy of the ROM image. The PCI Bus Driver is responsible
  
/// for allocating memory for the ROM image, and copying the contents of the ROM to memory.
  
/// The contents of this buffer are either from the PCI option ROM that can be accessed
  
/// through the ROM BAR of the PCI controller, or it is from a platform-specific location.
  
/// The Attributes() function can be used to determine from which of these two sources
  
/// the RomImage buffer was initialized.
  
///
  VOID                                    *RomImage;
};

今天我們只用到 EFI_PCI_IO_PROTOCOL_ACCESS              Io; 和 EFI_PCI_IO_PROTOCOL_CONFIG_ACCESS       Pci; 這兩個(gè)子功能, EFI_PCI_IO_PROTOCOL_CONFIG_ACCESS 用于讀寫配置空間, EFI_PCI_IO_PROTOCOL_ACCESS用于讀寫Io空間。
EFI_PCI_IO_PROTOCOL_CONFIG_ACCESS :
 /** 
  Enable a PCI driver to access PCI controller registers in PCI configuration space.


  @param  This                  A pointer to the EFI_PCI_IO_PROTOCOL instance.
  @param  Width                 Signifies the width of the memory operations.
  @param  Offset                The offset within the PCI configuration space for the PCI controller.
  @param  Count                 The number of PCI configuration operations to perform.
  @param  Buffer                For read operations, the destination buffer to store the results. For write
                                operations, the source buffer to write data from.


  @retval EFI_SUCCESS           The data was read from or written to the PCI controller.
  @retval EFI_UNSUPPORTED       The address range specified by Offset, Width, and Count is not
                                valid for the PCI configuration header of the PCI controller.
  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.
  @retval EFI_INVALID_PARAMETER Buffer is NULL or Width is invalid.

**/
typedef
EFI_STATUS
(EFIAPI *EFI_PCI_IO_PROTOCOL_CONFIG)(
  IN EFI_PCI_IO_PROTOCOL              *This,
  IN     EFI_PCI_IO_PROTOCOL_WIDTH    Width,
  IN     UINT32                       Offset,
  IN     UINTN                        Count,
  IN OUT VOID                         *Buffer
  );

typedef struct {
  ///
  
/// Read PCI controller registers in PCI configuration space.
  
///
  EFI_PCI_IO_PROTOCOL_CONFIG  Read;
  ///
  
/// Write PCI controller registers in PCI configuration space.
  
///
  EFI_PCI_IO_PROTOCOL_CONFIG  Write;
} EFI_PCI_IO_PROTOCOL_CONFIG_ACCESS;

EFI_PCI_IO_PROTOCOL_ACCESS:
typedef enum {
  EfiPciIoWidthUint8      = 0,
  EfiPciIoWidthUint16,
  EfiPciIoWidthUint32,
  EfiPciIoWidthUint64,
  EfiPciIoWidthFifoUint8,
  EfiPciIoWidthFifoUint16,
  EfiPciIoWidthFifoUint32,
  EfiPciIoWidthFifoUint64,
  EfiPciIoWidthFillUint8,
  EfiPciIoWidthFillUint16,
  EfiPciIoWidthFillUint32,
  EfiPciIoWidthFillUint64,
  EfiPciIoWidthMaximum
} EFI_PCI_IO_PROTOCOL_WIDTH;
/**
  Enable a PCI driver to access PCI controller registers in the PCI memory or I/O space.

  @param  This                  A pointer to the EFI_PCI_IO_PROTOCOL instance.
  @param  Width                 Signifies the width of the memory or I/O operations.
  @param  BarIndex              The BAR index of the standard PCI Configuration header to use as the
                                base address for the memory or I/O operation to perform.
  @param  Offset                The offset within the selected BAR to start the memory or I/O operation.
  @param  Count                 The number of memory or I/O operations to perform.
  @param  Buffer                For read operations, the destination buffer to store the results. For write
                                operations, the source buffer to write data from.

  @retval EFI_SUCCESS           The data was read from or written to the PCI controller.
  @retval EFI_UNSUPPORTED       BarIndex not valid for this PCI controller.
  @retval EFI_UNSUPPORTED       The address range specified by Offset, Width, and Count is not
                                valid for the PCI BAR specified by BarIndex.
  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.
  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.

*
*/
typedef
EFI_STATUS
(EFIAPI *EFI_PCI_IO_PROTOCOL_IO_MEM)(
  IN EFI_PCI_IO_PROTOCOL              *This,
  IN     EFI_PCI_IO_PROTOCOL_WIDTH    Width,     // 每個(gè)元素的大小 
  IN     UINT8                                      BarIndex,       // 0~5中的一個(gè)
  IN     UINT64                                   Offset,            // 偏移 
  IN     UINTN                                     Count,           //  元素個(gè)數(shù)
  IN OUT VOID                                   *Buffer
  );

typedef struct {
  ///
  
/// Read PCI controller registers in the PCI memory or I/O space.
  
///
  EFI_PCI_IO_PROTOCOL_IO_MEM  Read;
  ///
  
/// Write PCI controller registers in the PCI memory or I/O space.
  
///
  EFI_PCI_IO_PROTOCOL_IO_MEM  Write;
} EFI_PCI_IO_PROTOCOL_ACCESS;
例如讀第0個(gè)bar里偏移為4的寄存器,
UINT8 Data;
Status = PciIo->Io.Read(PciIo,
                         EfiPciIoWidthUint8 ,  //讀一個(gè)字節(jié)
                        0,    // Bar 0
                        4,   // 偏移4字節(jié)
                        1,   // 一個(gè)元素
                        &Data);

AC97
Intel芯片組南橋中一般有AC97控制器芯片,AC97控制器通過AC-LINK與AC97 Codec進(jìn)行通信,我們的音頻驅(qū)動(dòng)其實(shí)是AC97控制器驅(qū)動(dòng)。
如何操作AC97控制器可以參考Intel® I/O Controller Hub 6 (ICH6) High Definition Audio / AC ’97, 這兒只做簡(jiǎn)單介紹。
AC97 IO空間有兩個(gè)部分:Native Audio Bus Master Control Registers and Native Mixer Registers。
Bar0 表示 Native Mixer Registers(NAMBAR) , 訪問以16-bits為一個(gè)單位(與AC-LINK單位數(shù)據(jù)大小一致)。 用于配置AC97參數(shù),如音量,采樣率等。
Bar1 表示Bus Master Control Registers 。 用于控制AC97
Native Mixer Registers 功能表
Primary Offset    NAMBAR Exposed Registers
(Codec ID =00)
00h                    Reset
02h                    Master Volume
04h                    Aux Out Volume
06h                    Mono Volume
08h                    Master Tone (R & L)
0Ah                    PC_BEEP Volume
0Ch                    Phone Volume
0Eh                    Mic Volume
10h                    Line In Volume
12h                    CD Volume
14h                    Video Volume
16h                    Aux In Volume
18h                    PCM Out Volume
1Ah                    Record Select
1Ch                    Record Gain
1Eh                    Record Gain Mic
20h                    General Purpose
22h                    3D Control
24h                    AC ’97 RESERVED
26h                    Powerdown Ctrl/Stat
28h                    Extended Audio
2Ah                    Extended Audio Ctrl/Stat
2Ch                    PCM Front DAC Rate
2Eh                    PCM Surround DAC Rate
30h                    PCM LFE DAC Rate
32h                    PCM LR ADC Rate
34h                    MIC ADC Rate
36h                    6Ch Vol: C, LFE
38h                    6Ch Vol: L, R Surround
3Ah                    S/PDIF Control
3C~56h                    Intel RESERVED
58h                    AC ’97 Reserved
5Ah                    Vendor Reserved
7Ch                    Vendor ID1
7Eh                    Vendor ID2

Bus Master Control Registers 功能表

Offset

Mnemonic

Name

Default

Access

00h

PI_BDBAR

PCM In Buffer Descriptor list Base Address

00000000h

R/W

04h

PI_CIV

PCM In Current Index Value

00h

RO

05h

PI_LVI

PCM In Last Valid Index

00h

R/W

06h

PI_SR

PCM In Status

0001h

R/WC, RO

08h

PI_PICB

PCM In Position in Current Buffer

0000h

RO

0Ah

PI_PIV

PCM In Prefetched Index Value

00h

RO

0Bh

PI_CR

PCM In Control

00h

R/W, R/W (special)

10h

PO_BDBAR

PCM Out Buffer Descriptor list Base Address

00000000h

R/W

14h

PO_CIV

PCM Out Current Index Value

00h

RO

15h

PO_LVI

PCM Out Last Valid Index

00h

R/W

16h

PO_SR

PCM Out Status

0001h

R/WC, RO

18h

PO_PICB

PCM In Position In Current Buffer

0000h

RO

1Ah

PO_PIV

PCM Out Prefetched Index Value

00h

RO

1Bh

PO_CR

PCM Out Control

00h

R/W, R/W (special)

20h

MC_BDBAR

Mic. In Buffer Descriptor List Base Address

00000000h

R/W

24h

MC_CIV

Mic. In Current Index Value

00h

RO

25h

MC_LVI

Mic. In Last Valid Index

00h

R/W

26h

MC_SR

Mic. In Status

0001h

R/WC, RO

28h

MC_PICB

Mic. In Position In Current Buffer

0000h

RO

2Ah

MC_PIV

Mic. In Prefetched Index Value

00h

RO

2Bh

MC_CR

Mic. In Control

00h

R/W, R/W (special)

2Ch

GLOB_CNT

Global Control

00000000h

R/W, R/W (special)

30h

GLOB_STA

Global Status

See register description

R/W, R/WC, RO

34h

CAS

Codec Access Semaphore

00h

R/W (special)

40h

MC2_BDBAR

Mic. 2 Buffer Descriptor List Base Address

00000000h

R/W

44h

MC2_CIV

Mic. 2 Current Index Value

00h

RO

45h

MC2_LVI

Mic. 2 Last Valid Index

00h

R/W

46h

MC2_SR

Mic. 2 Status

0001h

RO, R/WC

48h

MC2_PICB

Mic 2 Position In Current Buffer

0000h

RO

4Ah

MC2_PIV

Mic. 2 Prefetched Index Value

00h

RO

4Bh

MC2_CR

Mic. 2 Control

00h

R/W, R/W (special)

50h

PI2_BDBAR

PCM In 2 Buffer Descriptor List Base Address

00000000h

R/W

54h

PI2_CIV

PCM In 2 Current Index Value

00h

RO

55h

PI2_LVI

PCM In 2 Last Valid Index

00h

R/W

56h

PI2_SR

PCM In 2 Status

0001h

R/WC, RO

58h

PI2_PICB

PCM In 2 Position in Current Buffer

0000h

RO

5Ah

PI2_PIV

PCM In 2 Prefetched Index Value

00h

RO

5Bh

PI2_CR

PCM In 2 Control

00h

R/W, R/W (special)

60h

SPBAR

S/PDIF Buffer Descriptor List Base Address

00000000h

R/W

64h

SPCIV

S/PDIF Current Index Value

00h

RO

65h

SPLVI

S/PDIF Last Valid Index

00h

R/W

66h

SPSR

S/PDIF Status

0001h

R/WC, RO

68h

SPPICB

S/PDIF Position In Current Buffer

0000h

RO

6Ah

SPPIV

S/PDIF Prefetched Index Value

00h

RO

6Bh

SPCR

S/PDIF Control

00h

R/W, R/W (special)

80h

SDM

SData_IN Map

00h

R/W, RO

表中有5個(gè)通道
PI = PCM in channel
PO = PCM out channel
MC = Mic in channel
MC2 = Mic 2 channel
PI2 = PCM in 2 channel
SP = S/PDIF out channel.
每個(gè)通道有一個(gè)16-bit DMA引擎,用于傳輸音頻數(shù)據(jù)(PCM格式)。 DMA引擎使用Buffer Descriptor List 數(shù)據(jù)結(jié)構(gòu)獲得數(shù)據(jù)緩沖區(qū)地址。Buffer Descriptor List 最多允許32項(xiàng),Buffer Descriptor 聲明如下:
typedef struct {
    UINT32 addr;
    UINT16 len;
    unsigned short reserved:14;
    unsigned short BUP:1;
    unsigned short IOC:1;
} BufferDescriptor;
addr的第0個(gè)bit必須是0。 List中最后一個(gè)BufferDescriptor 的BUP需為1。每個(gè)buffer最多有65536個(gè)采樣。
啟動(dòng)DMA的過程如下(翻譯自:Intel® I/O Controller Hub 6 (ICH6) High Definition Audio / AC ’97 Programmer’s Reference Manual (PRM) :30頁):
1. 建立buffer descriptor list。
2. 將buffer descriptor list 的基地址寫入Buffer Descriptor List Base Address register(對(duì)PCM Out而言是 MBBAR + 10h (POBAR))
3. 填充buffer descriptor list 中的buffer descriptor,并設(shè)置好數(shù)據(jù)緩存。
4. 設(shè)置 Last Valid Index(LVI)寄存器 (PCM OUT: MBBAR + 15h (POLVI))。LVI是BDL中最后一個(gè)準(zhǔn)備好緩沖區(qū)的buffer descriptor的下標(biāo)。
5. 設(shè)置Control register中的run bit,啟動(dòng)DMA傳輸。
這時(shí)就可以聽到聲音了。

AC97驅(qū)動(dòng)

現(xiàn)在我們開始設(shè)計(jì)AC97驅(qū)動(dòng)。時(shí)候還記得我們講過驅(qū)動(dòng)分兩個(gè)部分,硬件相關(guān)部分和框架部分。首先來設(shè)計(jì)硬件相關(guān)部分,也就是驅(qū)動(dòng)硬件并提供給用戶使用的協(xié)議。
EFI_AUDIO_PROTOCOL
我們把要提供的服務(wù)命名為EFI_AUDIO_PROTOCOL,在EFI_AUDIO_PROTOCOL, 我們提供播放音頻的服務(wù),首先我們要提供硬件初始化服務(wù),還要提供PCM音頻播放服務(wù),音量調(diào)節(jié)服務(wù),還要提供一個(gè)Event用來通知用戶音頻播放結(jié)束。
在audio.h中定義EFI_AUDIO_PROTOCOL相關(guān)數(shù)據(jù)結(jié)構(gòu)

struct _EFI_AUDIO_PROTOCOL{
 UINT64          Revision;
 EFI_AC97_RESET  Reset;
 EFI_AC97_PLAY   Play;
 EFI_AC97_VOLUME Volume;
 EFI_EVENT       WaitForEndEvent;
}
設(shè)計(jì)EFI_AUDIO_PROTOCOL的GUID
#define EFI_AUDIO_PROTOCOL_GUID \
{ \
    
0xce3451710xabcd0x11d2, {0x8e0x4f0x00xa00xc90x690x720x3b } \
}

在accdriver.c中還要定義用于標(biāo)示音頻播放上下文的數(shù)據(jù)結(jié)構(gòu),一般命名為X_PRIVATE_DATA。在上下文中我們要包含EFI_AUDIO_PROTOCOL實(shí)例,設(shè)備的EFI_PCI_IO_PROTOCOL實(shí)例,BufferDescriptor。
typedef struct {
    UINTN                 Signature;
    EFI_AUDIO_PROTOCOL  Audio; 
    EFI_PCI_IO_PROTOCOL   
*PciIo;
    BufferDescriptor Bdes[
32]; 
} AUDIO_PRIVATE_DATA;
定義EFI_AUDIO_PROTOCOL Template,用于在Start函數(shù)中初始化EFI_AUDIO_PROTOCOL實(shí)例
//
// Template for Audio private data structure.
// The pointer to Audio protocol interface is assigned dynamically.
//
AUDIO_PRIVATE_DATA        gDiskIoPrivateDataTemplate = {
  AUDIO_PRIVATE_DATA_SIGNATURE,
  {
    EFI_AUDIO_PROTOCOL_REVISION,
    AC97Reset,
    AC97Play,
    AC97Volume,
    
0
  },
  NULL,
  {
0}
};
下面要實(shí)現(xiàn) EFI_AUDIO_PROTOCOL 中定義的三個(gè)服務(wù),每個(gè)成員函數(shù)(服務(wù))的第一個(gè)參數(shù)是This指針,在函數(shù)里首先要根據(jù)This指針獲得上下文Private,然后根據(jù)上下文執(zhí)行相應(yīng)操作。
首先是Reset,主要是設(shè)置Mixer Register里德Powerdown Ctrl/Stat和Reset寄存器, 可以參考WinDDK里AC97驅(qū)動(dòng)示例。
 
/**
  @param  This       Indicates a pointer to the calling context.

  @retval EFI_SUCCESS          . 
  @retval EFI_DEVICE_ERROR         . 

*
*/
EFI_STATUS
EFIAPI AC97Reset(
        IN  EFI_AUDIO_PROTOCOL  
*This
        )
{
    NTSTATUS InitAC97 (
void);
    EFI_STATUS            Status;
    AUDIO_PRIVATE_DATA
* Private = AUDIO_PRIVATE_DATA_FROM_THIS(This);
    gAudioPciIo 
= Private->PciIo; 

    Status 
= InitAC97 ();
    
return Status;
}
然后是Play函數(shù), 首先設(shè)置Buffer Descriptor List,然后設(shè)置POBAR和PO_LVI  ,     最后啟動(dòng)DMA。
/**
  @param  This       Indicates a pointer to the calling context.
  @param  PcmData    Pointer to PCM Data
  @param  Format     PCM Data Format
  @param  Size       How many Samples in PCM Data

  @retval EFI_SUCCESS          . 
  @retval EFI_DEVICE_ERROR         . 

*
*/
EFI_STATUS
EFIAPI AC97Play(
        IN  EFI_AUDIO_PROTOCOL  
*This,
    IN  UINT8
* PcmData,
    IN  UINT32 Format,
    IN  UINTN Size
        )
{
    EFI_STATUS            Status;
    UINTN i
=0, LenLeft = Size;
    AUDIO_PRIVATE_DATA
* Private = AUDIO_PRIVATE_DATA_FROM_THIS(This);
    gAudioPciIo 
= Private->PciIo; 

    
for( i=0; i< 32 && LenLeft > 0; i++, LenLeft-=65536){
        Private
->Bdes[0].addr = (u32)(PcmData + 65536 * 4 * i);
        Private
->Bdes[0].len = (u16)(LenLeft <= 65536 ? LenLeft:LenLeft-65536);
    }
    Private
->Bdes[i-1].BUP = 1;
    Private
->Bdes[i-1].IOC = 0;

    WriteBMControlRegister32(PO_BDBAR       ,  (u32)Private
->Bdes);
    WriteBMControlRegister(PO_LVI         ,  
0);
    WriteBMControlRegisterMask(PO_CR           , 1,1);    //啟動(dòng)DMA
    (
void) Status;
    
return EFI_SUCCESS;
}
設(shè)置音量的函數(shù)
/**
  @param  This       Indicates a pointer to the calling context.
  @param  Increase   How much should the volume change, 
                     +Number increase; -Number Decrease.
  @param  NewVolume   if *NewVolume >=0 , It will set the volume as *NewVolume;
                      if *NewVolume <0, the Volume will be changed by Increase, 
              and *Newvolume returns the current Volume.

  @retval EFI_SUCCESS          . 
  @retval EFI_DEVICE_ERROR         . 

*
*/
EFI_STATUS
EFIAPI AC97Volume(
        IN  EFI_AUDIO_PROTOCOL  
*This,
    IN  INT32 Increase,
    IN OUT INT32 
* NewVolume
    )
{
    AUDIO_PRIVATE_DATA
* Private = AUDIO_PRIVATE_DATA_FROM_THIS(This);
    gAudioPciIo 
= Private->PciIo; 

    
if(*NewVolume < 0){
        WORD data
= (WORD) (long ) NewVolume;
        WriteCodecRegister (AC97REG_PCM_OUT_VOLUME, data, 
0xFFFF);
    }
else{
        WORD data
= 0;
        ReadCodecRegister(AC97REG_PCM_OUT_VOLUME, 
&data); 
        data 
+= (INT16) Increase;
        WriteCodecRegister (AC97REG_PCM_OUT_VOLUME, data, 
0xFFFF);
        
*NewVolume = (INT32)data;
    }
    
return EFI_SUCCESS;
}

驅(qū)動(dòng)的框架部分
驅(qū)動(dòng)的框架部分主要是實(shí)現(xiàn)EFI_DRIVER_BINDING_PROTOCOL及Image的初始化函數(shù)
//
// Driver binding protocol implementation for AC97 driver.
//
EFI_DRIVER_BINDING_PROTOCOL gAudioDriverBinding = {
  AC97DriverBindingSupported,
  AC97DriverBindingStart,
  AC97DriverBindingStop,
  
0xa,
  NULL,
  NULL
};
(1)Supported(AC97DriverBindingSupported)函數(shù)用來檢測(cè)設(shè)備是否AC97驅(qū)動(dòng)器。分兩步:1。 判斷Controller時(shí)候有EFI_PCI_IO_PROTOCOL  , 沒有則返回錯(cuò)誤。2。  有EFI_PCI_IO_PROTOCOL 則讀取PCI配置空間,判斷設(shè)備時(shí)候AC97驅(qū)動(dòng)器。         
/**
  Test to see if this driver supports ControllerHandle.

  @param  This                Protocol instance pointer.
  @param  ControllerHandle    Handle of device to test
  @param  RemainingDevicePath Optional parameter use to pick a specific child
                              device to start.

  @retval EFI_SUCCESS         This driver supports this device
  @retval EFI_ALREADY_STARTED This driver is already running on this device
  @retval other               This driver does not support this device

*
*/
EFI_STATUS
EFIAPI
AC97DriverBindingSupported (
  IN EFI_DRIVER_BINDING_PROTOCOL  
*This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     
*RemainingDevicePath OPTIONAL
  )
{
  EFI_STATUS            Status;
  PCI_TYPE00                        PciData;
  EFI_PCI_IO_PROTOCOL       
*PciIo;
  Status 
= gBS->OpenProtocol(
          ControllerHandle,
          
&gEfiPciIoProtocolGuid, 
          (VOID
**)&PciIo,
          This
->DriverBindingHandle,
          ControllerHandle,
          EFI_OPEN_PROTOCOL_BY_DRIVER
            );
   
if (EFI_ERROR (Status)) {
       
return Status;
   }

   Status 
= PciIo->Pci.Read (
           PciIo,
           EfiPciIoWidthUint32,
           
0,
           
sizeof (PciData) / sizeof (UINT32),
           
&PciData
           );

   gBS
->CloseProtocol (
        ControllerHandle,
        
&gEfiPciIoProtocolGuid,
        This
->DriverBindingHandle,
        ControllerHandle
        );
   
   
if (EFI_ERROR (Status)) {
       
return Status;
   }
   
if (!(PciData.Hdr.ClassCode[2== PCI_CLASS_MEDIA && PciData.Hdr.ClassCode[1== PCI_CLASS_MEDIA_AUDIO && PciData.Hdr.ClassCode[0== 0x00) ) {
       
return EFI_UNSUPPORTED;
   }
  
return EFI_SUCCESS;
}

(2) Start(AC97DriverBindingStart) 啟動(dòng)設(shè)備,安裝EFI_AUDIO_PROTOCOL。 
/**
  Start this driver on ControllerHandle by opening a PCI IO protocol and
  installing a Audio IO protocol on ControllerHandle.

  @param  This                 Protocol instance pointer.
  @param  ControllerHandle     Handle of device to bind driver to
  @param  RemainingDevicePath  Optional parameter use to pick a specific child
                               device to start.

  @retval EFI_SUCCESS          This driver is added to ControllerHandle
  @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle
  @retval other                This driver does not support this device

*
*/
EFI_STATUS
EFIAPI
AC97DriverBindingStart (
  IN EFI_DRIVER_BINDING_PROTOCOL  
*This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     
*RemainingDevicePath OPTIONAL
  )
{
  EFI_STATUS            Status;
  EFI_PCI_IO_PROTOCOL   
*PciIo;
  AUDIO_PRIVATE_DATA    
*Private; 

  Status 
= gBS->OpenProtocol(
          ControllerHandle,
          
&gEfiPciIoProtocolGuid, 
          (VOID
**)&PciIo,
          This
->DriverBindingHandle,
          ControllerHandle,
          EFI_OPEN_PROTOCOL_BY_DRIVER
            );
   
if (EFI_ERROR (Status)) {
       
return Status;
   }
   
//
   
// Allocate a buffer to store the ATA_ATAPI_PASS_THRU_INSTANCE data structure
   
//
   Private = AllocateCopyPool (sizeof (AUDIO_PRIVATE_DATA),        &gDiskIoPrivateDataTemplate );
   
if (Private == NULL) {
       
goto ErrorExit;
   }

   Private
->PciIo = PciIo;
   Status 
= gBS->CreateEvent(EVT_NOTIFY_WAIT, TPL_NOTIFY, (EFI_EVENT_NOTIFY)PlayEndEventNoify, (VOID*)Private, &Private->Audio.WaitForEndEvent);

   Status 
= gBS->InstallProtocolInterface (
                  
&ControllerHandle,
                  
&gEfiAudioProtocolGUID,
                  EFI_NATIVE_INTERFACE,
                  
&Private->Audio
                  );


ErrorExit:
   
if (EFI_ERROR (Status)) {

    
if (Private != NULL) {
      FreePool (Private);
    }

    gBS
->CloseProtocol (
          ControllerHandle,
          
&gEfiPciIoProtocolGuid,
          This
->DriverBindingHandle,
          ControllerHandle
          );
  }
else{
   
// Init Ac97
   AC97Reset(&Private->Audio);
  }
   
return Status;
}
(3)Stop(AC97DriverBindingStop)函數(shù)

/**
  Stop this driver on ControllerHandle by removing Audio IO protocol and closing
  the PCI IO protocol on ControllerHandle.

  @param  This              Protocol instance pointer.
  @param  ControllerHandle  Handle of device to stop driver on
  @param  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of
                            children is zero stop the entire bus driver.
  @param  ChildHandleBuffer List of Child Handles to Stop.

  @retval EFI_SUCCESS       This driver is removed ControllerHandle
  @retval other             This driver was not removed from this device

*
*/
EFI_STATUS
EFIAPI
AC97DriverBindingStop (
  IN  EFI_DRIVER_BINDING_PROTOCOL    
*This,
  IN  EFI_HANDLE                     ControllerHandle,
  IN  UINTN                          NumberOfChildren,
  IN  EFI_HANDLE                     
*ChildHandleBuffer
  )
{
  EFI_STATUS            Status;
  AUDIO_PRIVATE_DATA    
*Private; 
  EFI_AUDIO_PROTOCOL    
*Audio;

  
//
  
// Get our context back.
  
//
  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  
&gEfiAudioProtocolGUID,
                  (VOID 
**&Audio,
                  This
->DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
                  );
  
if (EFI_ERROR (Status)) {
    
return EFI_UNSUPPORTED;
  }

  Private 
= AUDIO_PRIVATE_DATA_FROM_THIS(This);

  Status 
= gBS->UninstallProtocolInterface (
                  ControllerHandle,
                  
&gEfiAudioProtocolGUID,
                  
&Private->Audio
                  );
  
if (!EFI_ERROR (Status)) {
    Status 
= gBS->CloseProtocol (
                    ControllerHandle,
                    
&gEfiPciIoProtocolGuid,
                    This
->DriverBindingHandle,
                    ControllerHandle
                    );
  }

  
if (!EFI_ERROR (Status)) {
    FreePool (Private);
  }

  
return Status;
}

最后要在Image的入口函數(shù)安裝EFI_DRIVER_BINDING_PROTOCOL

EFI_STATUS
EFIAPI
InitializeACC(
        IN EFI_HANDLE        ImageHandle,
        IN EFI_SYSTEM_TABLE  
*SystemTable
        )
{
    EFI_STATUS Status;
    
//
    
// Install driver model protocol(s).
    
//
    Status = EfiLibInstallDriverBindingComponentName2 (
            ImageHandle,
            SystemTable,
            
&gAudioDriverBinding,
            ImageHandle,
            
&gAudioComponentName,
            
&gAudioComponentName2
            );
    
//ASSERT_EFI_ERROR (Status);
    return Status;
}
自此,驅(qū)動(dòng)模型就介紹完了。 

Feedback

# re: UEFI實(shí)戰(zhàn)(5) driver  回復(fù)  更多評(píng)論   

2012-04-23 08:49 by tb
學(xué)習(xí)了

# re: UEFI實(shí)戰(zhàn)(5) driver  回復(fù)  更多評(píng)論   

2012-04-23 11:41 by ningle
你好!請(qǐng)教:EfiLibInstallDriverBindingComponentName2()中實(shí)參, &gAudioComponentName, &gAudioComponentName2是在什么時(shí)候初始化的,哪個(gè)文件中被初始化的,被誰調(diào)用后初始化的?

# re: UEFI實(shí)戰(zhàn)(5) driver[未登錄]  回復(fù)  更多評(píng)論   

2012-04-24 01:41 by djx_zh
@ningle
EFI_COMPONENT_NAME_PROTOCOL 和EFI_COMPONENT_NAME2_PROTOCOL 一般放在CompontName.c中
GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gAudioComponentName = {
AudioComponentNameGetDriverName,
AudioComponentNameGetControllerName,
"eng"
};

GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gAudioComponentName2 = {
(EFI_COMPONENT_NAME2_GET_DRIVER_NAME) AudioComponentNameGetDriverName,
(EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) AudioComponentNameGetControllerName,
"en"
};

GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mAudioDriverNameTable[] = {
{
"eng;en",
(CHAR16 *)L"Generic AC97 Driver"
},
{
NULL,
NULL
}
};

EFI_STATUS
EFIAPI
AudioComponentNameGetDriverName (
IN EFI_COMPONENT_NAME_PROTOCOL *This,
IN CHAR8 *Language,
OUT CHAR16 **DriverName
)
{
return LookupUnicodeString2 (
Language,
This->SupportedLanguages,
mAudioDriverNameTable,
DriverName,
(BOOLEAN)(This == &gAudioComponentName)
);
}


EFI_STATUS
EFIAPI
AudioComponentNameGetControllerName (
IN EFI_COMPONENT_NAME_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_HANDLE ChildHandle OPTIONAL,
IN CHAR8 *Language,
OUT CHAR16 **ControllerName
)
{
return EFI_UNSUPPORTED;
}

# re: UEFI實(shí)戰(zhàn)(5) driver  回復(fù)  更多評(píng)論   

2013-12-19 11:07 by lingming
學(xué)習(xí)了,期望有更多的好文章,或者編本書。

# re: UEFI實(shí)戰(zhàn)(5) driver[未登錄]  回復(fù)  更多評(píng)論   

2013-12-23 22:13 by djx_zh
多謝關(guān)注。 如果一切順利的話,新書將于4月份發(fā)行。到時(shí)將有ffdecoder, ffplayer, CppPkg, GuiPkg 等源碼隨書發(fā)行。

# re: UEFI實(shí)戰(zhàn)(5) driver  回復(fù)  更多評(píng)論   

2014-05-16 11:00 by 何龍
請(qǐng)問UEFI驅(qū)動(dòng)怎么實(shí)現(xiàn)在PCI掃描完之后所有的掛載的設(shè)備再加載你上面的驅(qū)動(dòng)?

# re: UEFI實(shí)戰(zhàn)(5) driver[未登錄]  回復(fù)  更多評(píng)論   

2014-05-27 01:51 by djx_zh
把UEFI驅(qū)動(dòng)(必須遵循UEFI驅(qū)動(dòng)規(guī)范)燒到rom里,系統(tǒng)會(huì)自動(dòng)加載這個(gè)驅(qū)動(dòng)

# re: UEFI實(shí)戰(zhàn)(5) driver  回復(fù)  更多評(píng)論   

2014-07-14 15:36 by 趙崗
你好,能不能發(fā)我一份源碼,謝謝
我的郵箱是867368106@qq.com

# re: UEFI實(shí)戰(zhàn)(5) driver  回復(fù)  更多評(píng)論   

2014-07-17 16:58 by P_WU
你好,也能不能發(fā)我一份源碼,急需,謝謝
Email: stylewarp@hotmail.com

# re: UEFI實(shí)戰(zhàn)(5) driver  回復(fù)  更多評(píng)論   

2014-09-26 09:08 by djx_zh
勘誤:
設(shè)置 Last Valid Index(LVI)寄存器 (PCM OUT: MBBAR + 15h (POLVI))。LVI是BDL中最后一個(gè)準(zhǔn)備好緩沖區(qū)的buffer descriptor的下標(biāo)。

只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            一本色道久久综合亚洲精品不卡| 国产精品一区二区在线观看不卡 | a91a精品视频在线观看| 欧美中文字幕视频在线观看| 亚洲特黄一级片| 国产日韩专区| 久久久久成人精品| 香蕉成人伊视频在线观看 | 美脚丝袜一区二区三区在线观看| 久久精品视频va| av不卡在线| 国产精品美女久久久久av超清 | 欧美在线观看www| 国产精品久久777777毛茸茸| 亚洲欧美在线aaa| 欧美h视频在线| 狠狠狠色丁香婷婷综合激情| 亚洲国产日韩一区二区| 久久亚洲精品视频| 日韩视频在线免费观看| 欧美伊人久久大香线蕉综合69| 久久精品91| 久久精品人人做人人综合| 国产精品久久久久久久久免费樱桃| 欧美成人精品激情在线观看 | 欧美日韩一区综合| 久久久一二三| 欧美一级黄色网| 久久久精品一区二区三区| 国产精品免费网站| 老司机一区二区三区| 美女啪啪无遮挡免费久久网站| 亚洲二区精品| 久久国产精品免费一区| 国产亚洲亚洲| 激情久久久久久久| 免费欧美在线视频| 久久综合九九| 亚洲日韩中文字幕在线播放| 久久国产精品第一页| 欧美日韩在线精品| 免费观看在线综合| 中文国产成人精品| 国产精品久久久久秋霞鲁丝| 另类激情亚洲| 99精品视频免费观看| 免费日韩av电影| 免费观看一级特黄欧美大片| 亚洲欧美精品中文字幕在线| 欧美激情亚洲视频| 亚洲毛片一区二区| 欧美福利视频在线| 欧美高清视频一区二区| 亚洲一区三区电影在线观看| 欧美日韩国产综合视频在线观看中文| 久久久福利视频| 亚洲一区二区四区| 欧美日韩一区二区三区在线观看免| 亚洲国产视频a| 久久久久亚洲综合| 亚洲国产91| 国产亚洲永久域名| 欧美电影免费观看| 久久久精品国产99久久精品芒果| 亚洲精品一区二区三区99| 国产亚洲精品久久久久婷婷瑜伽| 亚洲精品在线观看免费| 久久狠狠婷婷| 亚洲毛片在线看| 亚洲视频碰碰| 在线一区日本视频| 伊人成人开心激情综合网| 欧美精品乱码久久久久久按摩| 欧美日本在线观看| 亚洲乱亚洲高清| 亚洲国产精品久久精品怡红院 | 亚洲精品欧洲精品| 亚洲一区亚洲二区| 亚洲影视九九影院在线观看| 亚洲精品视频免费| 午夜精品区一区二区三| 国产精品永久免费观看| 欧美一区二区免费| 这里是久久伊人| 欧美成人综合网站| 91久久精品国产91久久性色| 亚洲精品在线免费| 能在线观看的日韩av| 国产精品亚洲一区| 欧美高清视频一区二区| 欧美性大战久久久久久久| 国产精品青草久久| 亚洲看片免费| 一区二区三区高清不卡| 国产日韩欧美二区| 久久综合九色欧美综合狠狠| 欧美高清视频一区| 亚洲一区二区三区在线看| 宅男噜噜噜66一区二区| 欧美sm重口味系列视频在线观看| 亚洲激情图片小说视频| 久久天堂国产精品| 亚洲在线观看视频网站| 亚洲伦理在线| 久久综合九九| 久久婷婷久久| 91久久精品美女| 日韩一级不卡| 亚洲免费伊人电影在线观看av| 久久中文字幕导航| 欧美精品日韩精品| 欧美日本在线看| 欧美三级电影一区| 国产在线不卡| 亚洲综合色网站| 欧美大片在线观看一区二区| 一区二区免费在线观看| 久久在线免费观看视频| 国产丝袜美腿一区二区三区| 亚洲自拍电影| 一区二区三区**美女毛片| 亚洲精品日韩久久| 99人久久精品视频最新地址| 久久亚洲春色中文字幕久久久| 欧美特黄a级高清免费大片a级| 国产一区二区三区在线观看免费| 国产一级久久| 一区二区欧美亚洲| 亚洲狠狠婷婷| 亚洲欧美成人| 亚洲精品久久久久久下一站 | 欧美在线免费观看| 欧美中文字幕久久| 国产精品乱码妇女bbbb| 一本久久综合亚洲鲁鲁| 亚洲一区免费看| 欧美国产日韩xxxxx| 亚洲视频免费观看| 欧美乱在线观看| 亚洲精品系列| 国产色视频一区| 一本色道久久99精品综合| 亚洲专区国产精品| 亚洲欧美日韩国产另类专区| 国产专区综合网| 91久久国产综合久久蜜月精品| 亚洲激情综合| 亚洲电影观看| 国产精品成人av性教育| 久久国产精品亚洲77777| 欧美在线999| 91久久中文| 午夜国产精品视频免费体验区| 久久亚洲影院| 国产精品99久久久久久www| 国产精品久久7| 欧美日韩精品免费在线观看视频| 中日韩美女免费视频网址在线观看| 亚洲制服av| 亚洲日本va午夜在线电影| 亚洲视频在线免费观看| 欧美在线免费观看| 亚洲高清视频在线| 亚洲精品在线电影| 狠狠操狠狠色综合网| 免费高清在线一区| 香蕉久久一区二区不卡无毒影院 | 国产视频在线观看一区二区| 欧美激情视频一区二区三区免费| 欧美午夜不卡视频| 久久天天狠狠| 欧美剧在线免费观看网站| 欧美一区二区免费视频| 欧美久久在线| 亚洲手机视频| 国产精品欧美风情| 欧美成人一区二免费视频软件| 欧美大片免费观看| 久久伊人精品天天| 国产精品性做久久久久久| 亚洲精品中文字幕女同| 黄色亚洲网站| 欧美主播一区二区三区美女 久久精品人 | 亚洲经典在线看| 国产日韩欧美精品| 日韩手机在线导航| 久久综合久色欧美综合狠狠 | 亚洲毛片在线| 亚洲国产一区在线观看| 一本到高清视频免费精品| 在线播放中文字幕一区| 久久偷看各类wc女厕嘘嘘偷窃| 国产精品久久久久一区| 亚洲精品无人区| 国产精品99久久久久久久女警 | 欧美成人激情在线| 激情校园亚洲| 亚洲小说欧美另类社区| 久久久久久久综合| 久久国产精品高清|