• <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>

            string

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

            UEFI實戰(5) driver

            Posted on 2012-04-22 08:36 djx_zh 閱讀(16843) 評論(10)  編輯 收藏 引用
            UEFI driver框架
            上一節我們講了服務型驅動,它有如下幾個特點:
            1. 在Image的入口函數中執行安裝。
            2. 服務型驅動不需要驅動特定硬件,可以安裝到任意controller上。
            3. 沒有提供卸載函數。
            一個真正的驅動程序,在安裝時首先要找到對應的硬件設備(在UEFI中是要找到對應的Controller), 然后執行安裝操作,將驅動程序安裝到硬件設備的controller上。 有時候我們還需要卸載驅動,更新驅動(先卸載舊的驅動,然后安裝新的驅動)。 有時候安裝操作可能需要執行多次,例如:第一次安裝時發現設備沒有準備好,或者所依賴的某個Protocol沒有安裝,就需要退出安裝,執行其他操作,然后進行第二次安裝。
            那么我們可以總結出一個完整的驅動程序框架需要三部分:
            1.  Findout() 找出對應的硬件設備
            2.  Install()  安裝驅動到指定的硬件設備 或者說 Start() 啟動硬件設備
            3. Uninstall()從硬件設備中卸載驅動  或者說 Stop() 停止硬件設備。
            另外很重要的一點是框架必須支持多次安裝。 上一節我們實現的驅動是不能多次安裝的(在入口函數中執行安裝),如果首次安裝失敗例如InstallProtocolInterface(...)返回錯誤,我們只能unload 驅動文件(image),從新loadImage。
            我們來看UEFI驅動框架是如何實現這幾點的。
            在UEFI驅動的入口函數中,安裝EFI Driver Binding Protocol到某個Handle(大部分情況下是自身即ImageHandle, 有時也會安裝到其它Handle上), 這個Driver Binding Protocol實例會常駐內存,用于驅動的安裝和卸載。使用DriverBindingProtocol使得我們可以多次操作(查找設備,安裝卸載)驅動.
            在Driver Binding Protocol中實現了框架的三個部分的接口。下面是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三個成員函數,對應我們總結的三個部分。
            首先看Support函數,簡單來講,如果ControllerHandle是我們要找的Controoler,該函數返回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 忽略該參數,
                                                                                                       //Bus Driver(例如MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.c)才會使用該參數。
            );
            再看Start函數,Start()用來啟動硬件設備,在函數中最重要的事情是調用InstallProtocolInterface()或者InstallMultipleProtocolInterfaces()在ControllerHandle上安裝驅動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函數,用于卸載驅動(調用UninstallProtocolInterface()或UninstallMultipleProtocolInterfaces()從ControllerHandle卸載驅動協議),并停止硬件設備。
            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
            );
            對Device Driver來講,NumberOfChildren 為0, ChildHandleBuffer 為NULL。 對Bus Driver來講,如果NumberOfChildren不為零,那么ChildHandleBuffer中的子節點都要被釋放。
            我們分別研究了驅動框架的三個部分,這三個部分是如何聯系起來的呢?看下面的代碼(MdeModulePkg/Core/Dxe/Hand/DriverSupport.c:CoreConnectSingleController),可以大致了解UEFI驅動程序框架是如何工作的。
            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安裝驅動。如果 ContextDriverImageHandles為空, 則遍歷系統中的所有DriverBindingProtocol,否則就只遍歷指定的DriverBindingProtocol。SortedDriverBindingProtocols[]存放了需要測試的DriverBindingProtocol, 對于每一個需要測試的DriverBindingProtocol,首先調用DriverBinding->Supported(...)測試該DriverBindingProtocol是否支持ControllerHandle, 如果Supported函數返回EFI_SUCCESS,則調用DriverBinding->Start(...)向ControllerHandle安裝驅動,啟動設備。
            CoreDisconnectController(
              IN  EFI_HANDLE  ControllerHandle,
              IN  EFI_HANDLE  DriverImageHandle  OPTIONAL,
              IN  EFI_HANDLE  ChildHandle        OPTIONAL
              ) 用于卸載掉ControllerHandle上指定的驅動(若DriverImageHandle 則卸載掉ControllerHandle上的所有驅動)。
            看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
            ++;
                    }
                  }

            了解了各個部分的細節后,我們在看一遍加載Driver的整個過程。
            首先在Shell中使用命令Load 將Driver文件加載到內存, Load后UEFI會調用gBS->StartImage(...) 執行DriverImage的入口函數, 在入口函數里Driver Binding Protocol被加載到Handle上(Driver Image handle 或者其它的Controller handle),然后UEFI會遍歷所有的Controller,調用Driver Binding Protocol的Supported 函數測試這個Driver是否支持該Controller,如果支持則調用Start()安裝驅動。

            EFI Component Name Protocol

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

            下面我們以AC97 控制器驅動為例,介紹如何編寫Device driver。南橋芯片中集成的AC97控制器是一種PCI設備,在開始寫驅動之前讓我們補充一點PCI設備驅動及AC97聲卡的背景知識。
            首先讓我們建立實驗環境, 在QEMU中選擇開啟 Audio ,類型選擇為Intel AC97.
            PCI設備及PciIo
            每個PCI設備都有三種地址空間:配置空間,IO空間和內存空間。
            系統初始化時系統會初始化每個PCI設備的配置空間寄存器。配置地址空間大小為256字節,前64字節是標準的,后面的寄存器由設備自定義用途。

             

            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的配置空間內容如下
            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設備中的IO和內存空間被劃分為1~6個互補重疊的子空間,每個子空間用于完成一組相對獨立的子功能。Base Address 0 ~5 表示子空間的基地址(物理地址)。
            對設備的操作主要是通過對子空間的讀寫來實現的。 UEFI提供了EFI_PCI_IO_PROTOCOL來操作PCI設備。 
            ///
            /// 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; 這兩個子功能, 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,     // 每個元素的大小 
              IN     UINT8                                      BarIndex,       // 0~5中的一個
              IN     UINT64                                   Offset,            // 偏移 
              IN     UINTN                                     Count,           //  元素個數
              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個bar里偏移為4的寄存器,
            UINT8 Data;
            Status = PciIo->Io.Read(PciIo,
                                     EfiPciIoWidthUint8 ,  //讀一個字節
                                    0,    // Bar 0
                                    4,   // 偏移4字節
                                    1,   // 一個元素
                                    &Data);

            AC97
            Intel芯片組南橋中一般有AC97控制器芯片,AC97控制器通過AC-LINK與AC97 Codec進行通信,我們的音頻驅動其實是AC97控制器驅動。
            如何操作AC97控制器可以參考Intel® I/O Controller Hub 6 (ICH6) High Definition Audio / AC ’97, 這兒只做簡單介紹。
            AC97 IO空間有兩個部分:Native Audio Bus Master Control Registers and Native Mixer Registers。
            Bar0 表示 Native Mixer Registers(NAMBAR) , 訪問以16-bits為一個單位(與AC-LINK單位數據大小一致)。 用于配置AC97參數,如音量,采樣率等。
            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個通道
            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.
            每個通道有一個16-bit DMA引擎,用于傳輸音頻數據(PCM格式)。 DMA引擎使用Buffer Descriptor List 數據結構獲得數據緩沖區地址。Buffer Descriptor List 最多允許32項,Buffer Descriptor 聲明如下:
            typedef struct {
                UINT32 addr;
                UINT16 len;
                unsigned short reserved:14;
                unsigned short BUP:1;
                unsigned short IOC:1;
            } BufferDescriptor;
            addr的第0個bit必須是0。 List中最后一個BufferDescriptor 的BUP需為1。每個buffer最多有65536個采樣。
            啟動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(對PCM Out而言是 MBBAR + 10h (POBAR))
            3. 填充buffer descriptor list 中的buffer descriptor,并設置好數據緩存。
            4. 設置 Last Valid Index(LVI)寄存器 (PCM OUT: MBBAR + 15h (POLVI))。LVI是BDL中最后一個準備好緩沖區的buffer descriptor的下標。
            5. 設置Control register中的run bit,啟動DMA傳輸。
            這時就可以聽到聲音了。

            AC97驅動

            現在我們開始設計AC97驅動。時候還記得我們講過驅動分兩個部分,硬件相關部分和框架部分。首先來設計硬件相關部分,也就是驅動硬件并提供給用戶使用的協議。
            EFI_AUDIO_PROTOCOL
            我們把要提供的服務命名為EFI_AUDIO_PROTOCOL,在EFI_AUDIO_PROTOCOL, 我們提供播放音頻的服務,首先我們要提供硬件初始化服務,還要提供PCM音頻播放服務,音量調節服務,還要提供一個Event用來通知用戶音頻播放結束。
            在audio.h中定義EFI_AUDIO_PROTOCOL相關數據結構

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

            在accdriver.c中還要定義用于標示音頻播放上下文的數據結構,一般命名為X_PRIVATE_DATA。在上下文中我們要包含EFI_AUDIO_PROTOCOL實例,設備的EFI_PCI_IO_PROTOCOL實例,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函數中初始化EFI_AUDIO_PROTOCOL實例
            //
            // 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}
            };
            下面要實現 EFI_AUDIO_PROTOCOL 中定義的三個服務,每個成員函數(服務)的第一個參數是This指針,在函數里首先要根據This指針獲得上下文Private,然后根據上下文執行相應操作。
            首先是Reset,主要是設置Mixer Register里德Powerdown Ctrl/Stat和Reset寄存器, 可以參考WinDDK里AC97驅動示例。
             
            /**
              @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函數, 首先設置Buffer Descriptor List,然后設置POBAR和PO_LVI  ,     最后啟動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);    //啟動DMA
                (
            void) Status;
                
            return EFI_SUCCESS;
            }
            設置音量的函數
            /**
              @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;
            }

            驅動的框架部分
            驅動的框架部分主要是實現EFI_DRIVER_BINDING_PROTOCOL及Image的初始化函數
            //
            // Driver binding protocol implementation for AC97 driver.
            //
            EFI_DRIVER_BINDING_PROTOCOL gAudioDriverBinding = {
              AC97DriverBindingSupported,
              AC97DriverBindingStart,
              AC97DriverBindingStop,
              
            0xa,
              NULL,
              NULL
            };
            (1)Supported(AC97DriverBindingSupported)函數用來檢測設備是否AC97驅動器。分兩步:1。 判斷Controller時候有EFI_PCI_IO_PROTOCOL  , 沒有則返回錯誤。2。  有EFI_PCI_IO_PROTOCOL 則讀取PCI配置空間,判斷設備時候AC97驅動器。         
            /**
              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) 啟動設備,安裝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)函數

            /**
              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的入口函數安裝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;
            }
            自此,驅動模型就介紹完了。 

            Feedback

            # re: UEFI實戰(5) driver  回復  更多評論   

            2012-04-23 08:49 by tb
            學習了

            # re: UEFI實戰(5) driver  回復  更多評論   

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

            # re: UEFI實戰(5) driver[未登錄]  回復  更多評論   

            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實戰(5) driver  回復  更多評論   

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

            # re: UEFI實戰(5) driver[未登錄]  回復  更多評論   

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

            # re: UEFI實戰(5) driver  回復  更多評論   

            2014-05-16 11:00 by 何龍
            請問UEFI驅動怎么實現在PCI掃描完之后所有的掛載的設備再加載你上面的驅動?

            # re: UEFI實戰(5) driver[未登錄]  回復  更多評論   

            2014-05-27 01:51 by djx_zh
            把UEFI驅動(必須遵循UEFI驅動規范)燒到rom里,系統會自動加載這個驅動

            # re: UEFI實戰(5) driver  回復  更多評論   

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

            # re: UEFI實戰(5) driver  回復  更多評論   

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

            # re: UEFI實戰(5) driver  回復  更多評論   

            2014-09-26 09:08 by djx_zh
            勘誤:
            設置 Last Valid Index(LVI)寄存器 (PCM OUT: MBBAR + 15h (POLVI))。LVI是BDL中最后一個準備好緩沖區的buffer descriptor的下標。
            久久久无码精品亚洲日韩软件| 伊人久久免费视频| 国产A级毛片久久久精品毛片| 少妇人妻综合久久中文字幕| 亚洲精品成人久久久| 99久久精品免费看国产一区二区三区| 久久亚洲AV成人无码国产| 久久91综合国产91久久精品| 久久人人爽人人澡人人高潮AV | 国产精品久久久久jk制服| 久久精品一区二区| 久久久久亚洲精品男人的天堂| 狠狠综合久久AV一区二区三区| 久久国产劲爆AV内射—百度| 精品综合久久久久久888蜜芽| 国产精品久久久久久福利69堂| 狠狠精品干练久久久无码中文字幕 | 国产精品美女久久久m| 久久久久无码精品| 久久久久久亚洲精品成人| 久久www免费人成看国产片| 日韩精品久久久久久久电影蜜臀| 国产精品美女久久久免费| 中文国产成人精品久久不卡| 91秦先生久久久久久久| 亚洲色欲久久久综合网东京热| 精品国产乱码久久久久久浪潮| 人妻久久久一区二区三区| 色婷婷噜噜久久国产精品12p| 久久99精品久久久久久动态图 | 99久久国产免费福利| 新狼窝色AV性久久久久久| 污污内射久久一区二区欧美日韩| 精品久久久久久中文字幕| 一本色道久久88—综合亚洲精品| 精品国产一区二区三区久久蜜臀| 久久精品国产亚洲AV无码娇色| 成人午夜精品无码区久久| 一本久久a久久精品综合香蕉| 国产精品99久久久久久宅男| AV色综合久久天堂AV色综合在|