본문 바로가기

Hello CE,Mobile

버스드라이버

윈도우 CE 버스 드라이버 작동원리

How Windows CE Bus Drivers Work

작성자: 데이비드 랴오 (David Liao)

요약                                                                                              

   

버스 드라이버는 특정 버스를 제어하고 설정하도록 설계되었다. 또한 버스 드라이버는 버스상의 하드웨어를 제어·설정하고 클라이언트 드라이버라고 지칭되는 하드웨어 드라이버를 로드·언로드 한다. 버스 드라이버는 소프트웨어적인 관점에서 기본적으로 가지의 기능을 수행한다. 하나는 클라이언트 드라이버를 서브하는 것이고 나머지 하나는 클라이언트 드라이버를 설정, 로딩, 제어하는 기능이다. 마이크로소프트는 PCI, PCCARD, Root 버스와 같이 가장 일반적인 버스를 지원하는 버스 드라이버를 제공한다.

   

   

버스 드라이버의 역할

   

버스 드라이버는 하드웨어 설정, 하드웨어 파워 콘트롤, 버스 주소 번역과 상위 클라이언트 드라이버의 로딩·언로딩과 같은 역할을 수행한다.

   

상위 클라이언트 드라이버의 로딩 언로딩

   

대부분의 상위 클라이언트는 버스 드라이버에 의해서 로딩되고 이때 버스 드라이버는 ActivateDevice(Ex) 호출한다. ActivateDevice(Ex) 콜러가 전달한 레지스트리 엔트리(registry entry) 내용에 따라 클라이언트 드라이버를 로딩한다.

   

따라서 드라이버를 로딩할 때는 레지스트리가 필요한데 버스 드라이버는 기존의 레지스트리를 사용하거나 템플릿(template) 따라 인스턴스(instance)  레지스트리 엔트리를 생성한다.

   

플러그앤플러그 (PNP) 드라이버의 경우 버스 드라이버는 플러그앤플러그 설정에 따라 하드웨어를 설정하고 인스턴스 레지스트리 엔트리를 설치한다. 다음에는 ActivateDevice(Ex) 호출하고 새로운 인스턴스 레지스트리 엔트리를 지정하여 클라이언트 드라이버를 로딩한다.

   

플러그앤플러그 드라이버가 아니거나 중개 드라이버(intermediate driver) 경우 레지스트리는 수동으로 설치된다. 버스 드라이버는 마이크로소프트사 혹은 OEM, 타회사가 공급하며 일반적으로 레지스트리를 이용하여 드라이버들을 로딩한다.

   

또한 버스 드라이버는 버스의 트리(tree) 생성함으로써 '페어런트'(parent) 버스 드라이버에 의해 로딩될 수도 있다. 루트 버스 드라이버나 트리의 트렁크(trunk) 예외이다. 루트 버스 드라이버는 장치관리자에 의해 로딩되며 장치관리자는 다음과 같은 특정 위치에서의 레지스트리 경로 값을 이용하여 이를 수행한다.

[HKEY_LOCAL_MACHINE\Drivers]

"RootKey"="Drivers\BuiltIn"

   

초기 설정값은 마이크로소프트사가 설정하지만 OEM 루트 버스 드라이버가 다른 레지스트리 경로에서 드라이버들을 열거할 있도록 이를 변경하기도 한다.

   

위의 예시에서는 루트 버스 드라이버는 다음의 경로에 위치한다.

[HKEY_LOCAL_MACHINE\Drivers\Builtin]

장치관리자는 초기화 과정에서 루트 버스드라이버를 로딩한다.

중요: 장치관리자는 시스템에서 2단계 부트과정을 통해 2 실행하도록 제어한다.

   

번째 부트과정에서 장치관리자는 만료된 드라이버 인스턴스(: HKLM\drivers\Active) 보유하고 있는 드라이버 '액티브' 키를 삭제하고 'BuiltInPhase1'라는 고정 버스이름으로 루트 버스드라이버를 로딩한다. 번째 부트 신호를 받으면 장치관리자는 레지스트리에서 발견된 버스 이름을 이용하여 다시 한번 루트 버스 드라이버를 로딩한다.

   

드라이버가 2단계 부트 시스템에서 2 로딩되는 것이 가능한가? 물론이다.

   

만일 드라이버가 HIVE 레지스트리에 포함되어 있고 엔트리에 DEVFLAGS_BOOTPHASE_1 (0x00001000) 지정하는 DWORD "플래그(Flags)" 엔트리가 포함되어 있지 않다면 드라이버는 2 로딩된다.  이는 버스 드라이버의 레지스트리 엔트리를 지정할 매우 중요하다.

   

이론적으로 어떤 버스 드라이버는 반드시 2 로딩되어야 한다.

   

버스 드라이버 액세스 CEDDK 버스 기능

   

클라이언트 드라이버는 버스 드라이버를 이용하여 다음과 같은 기능을 수행 있다.

  • 하위 버스와 부모(parent) 버스간의 버스 주소 번역
  • 디바이스 파워 상태(Power State) 변경
  • 설정 데이터 접근

   

버스 드라이버를 사용해야 하는 이유는 다양하다. 하나로 클라이언트 드라이버를 버스 아그노스틱(bus-agnostic)으로 만들어 주는 기능이 있다. 드라이버가 사용자 모드로 변경될 있거나 동일한 2진수(binary) 다른 버스 구조에 작동할 있는 것을 의미한다. 버스 아그노스틱 드라이버의 예로는 COM16550.dll NE2000.dll 있다. 이들은 동일한 2진수로 PCI 버스, 네이티브 버스(Native Bus), PCMCIA 버스의 클라이언트 드라이버로 기능을 수행할 있다.

   

버스 액세스 기능

   

부모 버스 드라이버에 액세스하기 위해서 클라이언트 드라이버는 CreateBusAccessHandle() 기능을 호출하여 버스 드라이버 액세스 핸들을 확보해야 합니다. 기능은 일반적으로 클라이언트 드라이버가 초기화 과정(XXX_Init)에서 호출하는 기능으로 클라이언트 드라이버가 활성 레지스트리 경로 (Active Registry Path) 인수로서 전달해야 한다. CloseBusAccessHandle() 호출되어 액세스 핸들을 닫는 역할을 수행한다. 보통 CloseBusAccessHandle() 기능은 XXX_Deinit 빠져나가기 이전에 호출된다. 보다 자세한 정보는 MSDN 온라인 문서를 참조하면 있다.

   

버스 액세스 핸들이 생성된 클라이언트는 핸들을 인수로 사용하는 모든 기능을 사용할 있다.

   

버스 드라이버 작동에 있어 CEDDK 기능은 일종의 포장(wrapped) 기능을 제공한다. 해당 기능의 매개변수를 포장하여 특정 동작체계의 입출력 제어 코드 (IOCTL) 호출한다. 마이크로소프트사는 드라이버 라이터(Driver Writer) 입출력 제어 코드를 이용하여 버스 드라이버를 직접 호출하는 대신에 CEDDK 기능을 이용할 것을 권장한다. 버스 입출력 제어 코드의 매개 변수는 전달 변경될 있지만 CEDDK 기능의 경우 API형식으로 시그니처가 변경되지 않는다.

   

아래는 CEDDK 기능과 해당 입출력 제어 코드이다.

CreateBusAccessHandle

다른 CEDDK 버스 기능에 대해 액세스 핸들을 생성한다. 결과 부모 bus?Open() 엔트리가 호출된다.

CloseBusAccessHandle

CreateBusAccessHandle() 이용하여 생성된 핸들을 닫는다. 결과 부모 bus?Open() 엔트리가 호출된다.

SetDevicePowerState

IOCTL_BUS_SET_POWER_STATE

GetDevicePowerState

IOCTL_BUS_GET_POWER_STATE

TranslateBusAddr

IOCTL_BUS_TRANSLATE_BUS_ADDRESS

TranslateSystemAddr

IOCTL_BUS_TRANSLATE_SYSTEM_ADDRESS

SetDeviceConfigurationData

IOCTL_BUS_SET_CONFIGURE_DATA

GetDeviceConfigurationData

IOCTL_BUS_GET_CONFIGURE_DATA

GetChildDeviceRemoveState

IOCTL_BUS_IS_CHILD_REMOVED

   

클라이언트 드라이버는 SetDevicePowerState() GetDevicePowerState() 이용하여 부모 버스 드라이버가 클라이언트를 특정 파워 상태로 설정되도록 요청한다. 어떤 클라이언트 드라이버는 스스로 파워 상태 변환을 수행하기도 하지만 클라이언트 드라이버가 효과적인 파워 상태로 설정되도록 하는 것은 버스 드라이버의 책임이다. 버스 드라이버가 클라이언트 드라이버를 로딩하기 전에 클라이언트 드라이버의 하드웨어를 파워상태 (Power State) D0으로 설정해야 한다. 버스 드라이버가 클라이언트 드라이버를 언로딩한 해당 하드웨어를 파워상태 D4 설정해야 한다.

   

TranslateBusAddr() TranslateSystemAddr() 물리 CPU 주소와 하위 버스 주소간에 번역기능을 수행한다. 그러기 위해서는 TranslateBusAddr() 대상 주소를 하위 버스에서 부모 버스로 번역해야 한다. 이러한 호출은 루트 버스 드라이버에 까지 전해지고 루트 버스 드라이버는 OAL 기능 (HalTranslateBusAddress()) 호출하여 CPU 해당 물리 주소를 최종적으로 획득한다. TranslateSystemAddr() 이와 유사하지만 다른 방향으로 진행된다. OAL HalTranslateSystemAddress() 이용하여 CPU 주소를 버스 주소로 번역하기 때문에 다층(multi-layer) 버스 주소는 TranslateBusAddr() TranslateSystemAddr() 의해서만 번역될 있다.

   

드라이버에 가상 또는 정적 시스템 메모리 맵핑 기능을 수행하기 앞서 BusTransBusAddrToVirtual() BusTransBusAddrToStatic() TranslateBusAddr() 이용하여 버스 주소를 CPU (시스템) 주소로 번역한다.

   

CEDDK 지원하지 않는 입출력 제어코드

   

CEDDK BusIoControl() BusChildIoControl() 기능을 클라이언트 드라이버에 제공한다. 클라이언트 드라이버는 기능을 이용하여 버스 입출력 제어 호출을 직접 수행한다. 기능의 차이점은 BusChildIoControl() 경우 호출을 직접 수행하는 클라이언트 드라이버와 연관된 입출력 제어 호출을 발신하는데 이용된다는 점이다.

   

IOCTL_BUS_ACTIVATE_CHILD IOCTL_BUS_DEACTIVATE_CHILD 제어코드는 표준 입출력 제어코드로서 CEDDK 포장 기능이 설정되어 있지 않다. 이들 입출력 제어코드는 어떠한 어플리케이션에 의해서도 호출이 가능하며 이때 어플리케이션은 해당 버스 드라이버에 핸들을 개방하여야 한다. 입출력 제어코드는 클라이언트 드라이버를 활성 또는 비활성화 시키는 이용된다. 확인: 레거시(legacy) 이유로 인해 모든 클라이언트 드라이버가 비활성화 있는 것은 아니다.

   

   

드라이버 버스이름 지정 원리

   

클라이언트 드라이버의 버스 이름은 부모 버스 드라이버에 의해 지정된다.

   

버스 드라이버가 지정하는 버스 이름은 다음과 같은 형태를 가진다.

busname _ bus# _ device# _ function#

버스이름           버스번호          디바이스번호 기능번호

   

일반적으로 버스이름은 버스 드라이버 디바이스의 레지스트리 키상의 BusName 값에 따라 정해진다. 값은 마이크로소프트사나 버스 드라이버를 사용하는 OEM 에서 사전에 지정한 값이다.

   

"bus#", "device#", "function#" 클라이언트 드라이버의 디바이스 상의 레지스트리 엔트리 값인 "BusNumber", "DeviceNumber", "FunctionNumber" 설정된다. 이들 레지스트리 값은 플러그 플러그(PnP) 버스 드라이버에 의해서 생성된다. 루트 버스 드라이버와 같이 PnP 드라이버가 아닌 드라이버의 bus# 값은 버스 드라이버의 디바이스 레지스트리 키값에 의해 지정된다. device# function# 버스 드라이버에 의해 자동으로 부여된다.   

   

시스템상의 버스이름은 고유하게 정해져야 하기 때문에 시스템이 다수의 버스 드라이버를 가지고 있을 경우 적절히 선택되어야 한다. 다음은 일반적으로 사용되는 버스 드라이버 이름이다.

  • 루트 버스 드라이버 (Root Bus Driver): "BuiltIn"
  • PCI 버스 드라이버 (PCI Bus Driver): "PCI"
  • PC 카드 버스 드라이버 (PC Card Bus Driver): "PCCARD"

   

파워 제어 드라이버(Power Manageable Drivers) 버스 드라이버의 파워 입출력 제어코드

   

파워 제어 드라이버에는 가지 유형이 있다. 하나는 시스템의 파워 메니저(PM) 의해 제어되는 드라이버 이고 다른 하나는 스스로 파워를 제어하는 드라이버이다. 파워메니저의 제어를 받는 드라이버는 다음의 입출력 제어 코드를 지원한다.

   

  • IOCTL_POWER_CAPABILITIES 파워 메니저는 입출력 제어 코드를 이용하여 드라이버의 지원디바이스 파워상태를 쿼리(query)하고 드라이버가 이에 대해 (D0->D4) 지원하는 파워상태와 지원 가능한 디바이스의 파워상태를 지정하는 IOCTL_POWER_SET 커맨드(아래참조) 전송한다. 만일 디바이스가 이와 같은 파워상태 변경 커맨드를 거부할 경우 파워메니저는 이를 치명적인 오류로 간주한다.
  • IOCTL_POWER_QUERY -- (선택사양) 기능은 드라이버의 현재 파워상태를 전송한다.
  • IOCTL_POWER_SET 파워 메니저는 입출력 제어 코드를 이용하여 드라이버의 파워 상태를 변경한다. 파워 메니저는 드라이버가 IOCTL_POWER_CAPABILITIES로부터 보고받은 유효 파워상태만을 지정한다.

   

클라이언트 드라이버의 파워 관리 버스 드라이버의 역할

   

앞에서 설명한 바와 같이 클라이언트의 버스 드라이버 하드웨어의 파워 상태를 변경하는 책임을 진다. 따라서 이론적으로 파워메니저는 버스 드라이버를 직접 호출하여 파워를 제어하는 것이 마땅하지만 실제로는 그렇지 않다. 이유는 다음과 같다.

  • 클라이언트 드라이버는 기능을 올바르게 수행하기 위해서 현재의 파워 상태를 인식하고 있어야 한다. 예를 들어 클라이언트 드라이버는 버스 드라이버가 하드웨어 전원을 오프하기 전에 하드웨어상에 진행중인 프로세스는 없는지 또는 향후 기능 수행을(기능요청 대기 ) 방해하는 요소가 없는 확인해야 한다.
  • 윈도우 CE 5.0 이전의 드라이버의 경우 파워 제어를 자체적으로 수행하였다. 프로세스에 역호환성(backwards compatible) 부여하려면 파워메니저가 클라이언트 드라이버를 직접 그리고 지속적으로 호출해야 한다.

   

시스템 파워 메니저의 파워 제어 커맨드는 클라이언트 드라이버로 전달되고 버스 입출력 제어 코드를 통해 클라이언트의 버스 드라이버에 반영된다. 처리과정은 다음과 같다.

  • 파워메니저가 클라이언트 상의 DeviceIoControl (IOCTL_POWER_SET) 호출한다.
  • 클라이언트 드라이버는 IOCTL_BUS_SET_POWER_STATE 버스 드라이버에 번역해주는 SetDevicePowerState() CEDDK 기능을 호출한다.
  • 버스 드라이버는 클라이언트 드라이버의 호출에 따라 다음 과정을 결정한다.

   

드라이버가 입출력 기능 (IoControl/Read/Write) 파워 메니저의 입출력 제어 코드로부터 동시에 명령을 받기 때문에 혼란과 우려가 제기될 있다. 동시적인 요청이 발생하면 클라이언트 드라이버가 기능을 올바로 수행할 있도록 조치가 필요한데 다음과 같이 CEDDK 기능 가지를 사용하면 문제를 해결할 있다.

Init()

{

hPwrHandle = DDKPwr_Initialize(__in PFN_SETPOWERLEVEL pSetPowerLevelFn,

__in DWORD dwContext,

__in BOOL fAbortOnPMRequests,

__in DWORD dwTimeout );

};

   

Deinit

{

DDKPwr_Deinitialize(hPwrHandle);

}

   

Function_Request()

{

       HANDLE hLevelHandle = DDKPwr_RequestLevel(hPwrHandle,

__in CEDEVICE_POWER_STATE dx );

       ?// Do some work.

       DDKPwr_ReleaseLevel(hPwrHandle, hLevelHandle);

}

   

PowerMgr_Request(Dx)

{       // From Power IO Control (파워 입출력 제어로부터)

DDKPwr_SetDeviceLevel(hPwrHandle ,Dx , __in PFN_SETPOWERLEVELCALLBACK pCallbackFn );

}

   

의사코드(pseudocode) 참조하면 일반적인 처리 과정을 확인할 있다. CEDDK 기능의 사용법 예시는 public\common\oak\drivers\serial\serpddcm\cserpdd.cpp 에서 확인할 있다.

   

버스 드라이버 라이브러리

   

마이크로소프트사는 사용자가 버스 드라이버를 있도록 버스드라이버 라이브러리를 제공한다. CEDDK 기능은 버스 입출력 제어 코드를 버스 드라이버에 전달하는데 입출력 제어 코드에는 가지가 있다. 하나는 버스 드라이버를 대상으로 다른 하나는 클라이언트 드라이버를 대상으로 설정 파워 상태, 버스 주소 번역을 제어한다.

   

Class DefaultBusDriver {

   

Public:

      // Constructor  (생성자)

      // Client Driver Specific function. (클라이언트 드라이버 지정기능)

      // Bus Driver Function. (버스 드라이버 기능)

   

Protected:

      // Container for all Folders of Child Client Driver.

         (자식 클라이언트 드라이버의 모든 폴더를 포함)

      // Child Folder Class Manufacture. };

         (자식 폴더 클래스 제조)

   

class DeviceFolder {

   

public:

      // Constructor (생성자)

      // Device Power Function  (디바이스 파워 기능)

      // Device Configuration Function  (디바이스 설정 기능)

      // Device Bus Address Translation Function 

         (디바이스 버스주소 번역기능)

      // Device Driver Load and Unload Function.

         (디바이스 드라이버 로딩 언로딩 기능)

};

   

DefaultBusDriver DeviceFolder 클래스는 버스 드라이버 구현에 있어 추상적인 템플릿이다. 버스 드라이버는 기본 클래스로부터 특정 필요에 따라 가상 기능을 전달받아 이를 수정한다.

   

버스 라이브러리는 인스턴스가 가능한(instantiable) DefaultBusDriver DeviceFolder 클래스를 제공하지 않는다. 따라서 개발자는 이러한 추상적인 베이스를 하위 분류하여 세부 정보를 제공해야 한다. DefaultBusDriver 구현에 있어 DeviceFolder기반 클래스를 관리하는 기본 설정된(default) 방법들이 있다.

   

다음은 버스 라이브러리가 제공하는 가지 기능이다.

   

DefaultBusDriver Class

  • 폴더에 특정 디바이스의 요청 전달
  • DeviceFolder 컨테이너 관리
  • IOCTL_BUS_TRANSLATE_SYSTEM_ADDRESS IOCTL_BUS_TRANSLATE_BUS_ADDRESS IO Control Codes. 디폴트 버스 번역(Default Bus Translations) 수행. 부모 버스 드라이버를 호출하여 주소를 번역하거나 루트 버스 드라이버의 경우 HalTranslateBusAddress 또는 HalTranslateSystemAddress 호출.
  • 기본 IOCTL_BUS_IS_CHILD_REMOVED, IOCTL_BUS_NAME_PREFIX 기타 입출력 제어코드 처리.
  • IOCTL_BUS_POSTINIT 의해 호출되는 PostInit() 더미(dummy)기능 수행

   

DeviceFolder

  • 드라이버 로딩 언로딩 사용되는 디폴트 버스이름 생성 논리 포함.
  • IOCTL_BUS_GET_CONFIGURE_DATA IOCTL_BUS_SET_CONFIGURE_DATA 통하여 DefaultBusDriver 클래스가 전달하는 디폴트 설정 기능 수행. PCI 클라이언트 디바이스 드라이버에 요청 발생 HalGetBusDataByOffset 또는 HalSetBusDataByOffset 호출.
  • DefaultBusDriver IOCTL_BUS_SET_POWER_STATE 통해 호출하는 SetPowerState 더미(dummy) 기능 수행. 제공된 PCI 버스 드라이버 구현과정은 특정 하드웨어에서 SetPowerState 어떻게 수행하는 보여주는 좋은 예시이다.

   

PCI 버스 드라이버

   

PCI 버스 드라이버는 PCI 버스 설정 자원 할당, 템플릿에 따른 드라이버 검색, 인스턴트 디바이스 로딩 레지스트리 설정을 담당한다. 구현과정은 여기에서 다뤄지지 않지만 외부 클라이언트에서 버스 입출력제어 호출을 지원하는 기능을 설명한다.

   

클라이언트 드라이버에서 버스 입출력 제어 호출을 지원하기 위해서 PCI 버스 드라이버는 다음과 같이 버스 드라이버 라이브러리에 명시된 서브 클래스를 구현한다.

class PciDeviceFolder : public DeviceFolder{

    virtual BOOL PostInit() ;

...

};

class PciBusEnum : public  DefaultBusDriver {

...

};

이들 클래스는 다음과 같이 베이스를 오버라이드(override)한다.

  • PciBusEnum PostInit() 기능을 구현하여 부모 클래스상의 더미(dummy) 구현을 덮어쓰기(overwrite)한다. 새로운 구현은 다음의 가지 하부 기능을 호출한다.
    • 각각의 디바이스 인스턴스에 PciDeviceFolder 하나씩 생성하는 AssignChildDriver() 호출하고 디바이스 폴더 컨테이너에 폴더를 삽입한다.
    • DeviceFolder::LoadDevice() 호출하여 각각의 클라이언트 드라이버를 차례로 로딩하는 ActivateAllChildDriver() 호출한다.

       

  • PciDeviceFolder -- 부모 클래스의 더미(dummy) 구현을 덮어 쓰기 위하여 SetPowerState() 구현한다. 새로운 방법은 PCI PM 1.1 사양에 따라 적절한 조치를 구현한다.

   

루트 버스 드라이버

   

디폴트 루트 버스 드라이버는 'BusEnum'이라 지칭된다. 디폴트는 오직 단순한 BusEnum 클래스만을 구현하는데 BusEnum 클래스는 버스 드라이버 라이브러리의 DefaultBusDriver 클래스에서 작동양식을 이어받는다. 것은 디폴트 DeviceFolder 구현을 수정하지 않는다. 따라서 SetPowerState() 디바이스 폴더 호출을 지원하지 않는다. (디폴트 구현은 더미(dummy) 불과하다.).

class BusEnum : public  DefaultBusDriver {

virtual BOOL PostInit() ;

...

};

  • BusEnum BusEnum PostInit() 기능을 구현하여 부모 클래스의 더미 구현을 덮어쓰기 한다. 새로운 구현은 다음의 가지 하부 기능을 호출한다.
    • 하부 레지스트리 키에 의해 조회된 각각의 디바이스 인스턴스에 대한 DeviceFolder 생성하고 디바이스 폴더 컨테이너에 폴더를 삽입한다.
    • DeviceFolder::LoadDevice() 호출하여 드라이버들을 차례로 로딩하는 ActivateAllChildDriver() 호출한다.

   

루트 버스가 클라이언트 드라이버 요청 파워 상태 설정을 지원하기 위해서는 SetPowerState() 드라이버 폴더 기능이 구현되어야 한다. F-샘플(OMAP850) 이것이 어떻게 구현되는지 보여주는 특정 플랫폼 루트 버스드라이버의 구현 예를 제공한다.

   

원본 위치 <http://kr.blog.yahoo.com/jaesu_lee/1021.html?p=1&pm=l&tc=20&tt=1288167178>

   

'Hello CE,Mobile' 카테고리의 다른 글

[Wndows Mobile] Programming Camera Driver #2  (0) 2010.10.28
[Windows Mobile] Programming Camera Driver #1  (0) 2010.10.28
NAND  (1) 2010.10.19
Windows Mobile 6.5 기대반 실망반 by 키온  (0) 2010.10.13
CeGetUserNotification()  (0) 2010.05.28