USB Type-C 포트 컨트롤러 드라이버 작성

USB Type-C 하드웨어가 USB Type-C 또는 PD(Power Delivery) 물리적 계층을 구현하지만 전원 배달에 필요한 상태 컴퓨터를 구현하지 않는 경우 USB Type-C 포트 컨트롤러 드라이버를 작성해야 합니다.

Windows 10 버전 1703에서는 USB Type-C 또는 PD(Power Delivery) 물리적 계층을 구현하지만 해당 PD 정책 엔진 또는 프로토콜 계층 구현이 없는 하드웨어 디자인을 지원하도록 USB Type-C 아키텍처가 개선되었습니다. 이러한 디자인의 경우 Windows 10 버전 1703은 "USB 커넥터 관리자 Type-C 포트 컨트롤러 인터페이스 클래스 확장"(UcmTcpciCx)이라는 새 클래스 확장을 통해 소프트웨어 기반 PD 정책 엔진 및 디바이스 정책 관리자를 제공합니다. IHV 또는 OEM/ODM에서 작성한 클라이언트 드라이버는 UcmTcpciCx와 통신하여 PD 정책 엔진 및 UcmTcpciCx의 디바이스 정책 관리자가 작동하는 데 필요한 하드웨어 이벤트에 대한 정보를 제공합니다. 해당 통신은 이 문서 및 참조 섹션에 설명된 프로그래밍 인터페이스 집합을 통해 사용하도록 설정됩니다.

USB 커넥터 관리자 다이어그램

UcmTcpciCx 클래스 확장 자체는 UcmCx의 클라이언트 드라이버입니다. 전원 계약, 데이터 역할에 대한 정책 결정은 UcmCx에서 이루어지고 UcmTcpciCx로 전달됩니다. UcmTcpciCx는 UcmTcpciCx 클라이언트 드라이버에서 제공하는 포트 컨트롤러 인터페이스를 사용하여 이러한 정책을 구현하고 Type-C 및 PD 상태 컴퓨터를 관리합니다.

요약

  • UcmTcpci 클래스 확장에서 제공하는 서비스
  • 클라이언트 드라이버의 예상 동작

공식 사양

중요 API

USB Type-C 포트 컨트롤러 인터페이스 드라이버 클래스 확장 참조

UcmTcpciCx 클라이언트 드라이버 템플릿

UcmTcpciCx 클라이언트 드라이버 템플릿

시작하기 전에

  • 하드웨어 또는 펌웨어가 PD 상태 컴퓨터를 구현하는지 여부에 따라 작성해야 하는 드라이버 유형을 결정합니다. 자세한 내용은 USB Type-C 커넥터용 Windows 드라이버 개발을 참조하세요.

  • 대상 컴퓨터에 데스크톱 버전(Home, Pro, Enterprise 및 Education)용 Windows 10 설치하거나 USB Type-C 커넥터를 사용하여 Windows 10 Mobile 설치합니다.

  • 개발 컴퓨터에 최신 WDK(Windows 드라이버 키트)를 설치합니다. 이 키트에는 클라이언트 드라이버를 작성하는 데 필요한 헤더 파일 및 라이브러리가 있습니다. 특히 다음이 필요합니다.

    • 스텁 라이브러리(UcmTcpciCxStub.lib)입니다. 라이브러리는 클라이언트 드라이버의 호출을 변환하고 클래스 확장에 전달합니다.
    • 헤더 파일인 UcmTcpciCx.h입니다.

    클라이언트 드라이버는 커널 모드에서 실행되고 KMDF 1.15 라이브러리에 바인딩됩니다.

    UCM용 Visual Studio 구성 스크린샷

  • 클라이언트 드라이버가 경고를 지원하는지 여부를 결정합니다.

  • 포트 컨트롤러가 TCPCI 규격일 필요는 없습니다. 인터페이스는 모든 Type-C 포트 컨트롤러의 기능을 캡처합니다. TCPCI 규격이 아닌 하드웨어용 UcmTcpciCx 클라이언트 드라이버를 작성하려면 TCPCI 사양의 레지스터 및 명령의 의미를 하드웨어에 매핑해야 합니다.

  • 대부분의 TCPCI 컨트롤러는 I2C 연결입니다. 클라이언트 드라이버는 SPB(직렬 주변 버스) 연결 리소스와 인터럽트 라인을 사용하여 하드웨어와 통신합니다. 드라이버는 SPBCx(SpbCx) 프로그래밍 인터페이스를 사용합니다. 다음 문서를 읽어 SpbCx에 익숙해지세요.

    • [SPB(Simple Peripheral Bus) 드라이버 디자인 가이드]
    • [SPB 드라이버 프로그래밍 참조]
  • WDF(Windows 드라이버 파운데이션)에 대해 잘 알고 있습니다. 권장 읽기: 페니 오윅과 가이 스미스가 쓴 Windows 드라이버 파운데이션을 사용하여 드라이버 개발.

UcmTcpci 클래스 확장의 동작

  • 상태 머신 실행의 일부로 UcmTcpciCx는 포트 컨트롤러에 IOCTL 요청을 보냅니다. 예를 들어 PD 메시징에서 전송 버퍼를 설정하는 IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_TRANSMIT_BUFFER 요청을 보냅니다. 해당 요청(TRANSMIT_BUFFER)은 클라이언트 드라이버에 전달됩니다. 그런 다음 드라이버는 클래스 확장에서 제공하는 세부 정보를 사용하여 전송 버퍼를 설정합니다.

  • UcmTcpciCx는 전원 계약, 데이터 역할 등에 대한 정책을 구현합니다.

클라이언트 드라이버의 예상 동작

UcmTcpciCx에 대한 클라이언트 드라이버는 다음과 같습니다.

  • 전원 정책 소유자가 됩니다. UcmTcpciCx는 포트 컨트롤러의 전원 관리에 참여하지 않습니다.

  • UcmTcpciCx에서 받은 요청을 하드웨어 읽기 또는 쓰기 명령으로 변환합니다. DPM은 하드웨어 전송이 완료될 때까지 기다리는 것을 차단할 수 없으므로 명령은 비동기적이어야 합니다.

  • 프레임워크 요청 개체가 포함된 프레임워크 큐 개체를 제공합니다. UcmTcpci 클래스 확장이 클라이언트 드라이버에 보내려는 각 요청에 대해 확장은 드라이버의 큐 개체에 요청 개체를 추가합니다. 드라이버가 요청 처리를 완료하면 WdfRequestComplete를 호출합니다. 적시에 요청을 완료하는 것은 클라이언트 드라이버의 책임입니다.

  • 포트 컨트롤러의 기능을 검색하고 보고합니다. 이러한 기능에는 포트 컨트롤러가 작동할 수 있는 역할(예: 원본 전용, 싱크 전용, DRP)과 같은 정보가 포함됩니다. 그러나 USB Type-C 및 PD 정책을 제대로 구현하기 위해 DPM이 알아야 하는 커넥터(기능 저장소에 대한 참고 참조) 및 시스템 전체의 다른 기능이 있습니다. instance 경우 DPM은 시스템/커넥터의 원본 기능을 알고 포트 파트너에게 보급해야 합니다.

    기능 저장소

    클라이언트 드라이버 관련 기능 외에도 기능 저장소라고 하는 시스템 전역 위치에서 추가 정보가 제공됩니다. 이 시스템 전역 기능 저장소는 ACPI에 저장됩니다. DPM이 구현할 정책을 결정하는 데 사용하는 시스템 및 각 USB Type-C 커넥터의 기능에 대한 정적 설명입니다.

    포트 컨트롤러에 대한 클라이언트 드라이버에서 시스템 기능에 대한 설명을 분리하여 설계를 통해 다양한 기능의 다양한 시스템에서 드라이버를 사용할 수 있습니다. UcmTcpciCx가 아닌 UcmCx는 기능 저장소와 인터페이스합니다. UcmTcpciCx(또는 해당 클라이언트 드라이버)는 기능 저장소와 상호 작용하지 않습니다.

    해당하는 경우 기능 저장소의 정보는 포트 컨트롤러 클라이언트 드라이버에서 직접 제공되는 정보를 재정의합니다. instance 경우 포트 컨트롤러는 싱크 전용 작업을 수행할 수 있으며 클라이언트 드라이버는 해당 정보를 보고합니다. 그러나 나머지 시스템은 싱크 전용 작업에 대해 올바르게 구성되지 않을 수 있습니다. 이 경우 시스템 제조업체는 커넥터가 기능 저장소에서 원본 전용 작업을 수행할 수 있다고 보고할 수 있습니다. 기능 저장소의 설정이 드라이버 보고 정보보다 우선합니다.

  • 경고와 관련된 모든 관련 데이터를 UcmTcpciCx에 알립니다.

  • 선택 사항입니다. 대체 모드를 입력/종료한 후 추가 처리를 수행합니다. 드라이버는 IOCTL 요청을 통해 클래스 확장에 의해 해당 상태에 대해 알 수 있습니다.

UcmTcpciCx를 사용하여 클라이언트 드라이버 등록

샘플 참조: 에서 를 Device.cpp참조하세요EvtPrepareHardware.

  1. EVT_WDF_DRIVER_DEVICE_ADD 구현에서 UcmTcpciDeviceInitInitInitialize를 호출하여 WDFDEVICE_INIT 불투명 구조를 초기화합니다. 호출은 클라이언트 드라이버를 프레임워크와 연결합니다.

  2. WDFDEVICE(프레임워크 디바이스 개체)를 만든 후 UcmTcpciDeviceInitialize를 호출하여 UcmTcpciCx에 클라이언트 다이버를 등록합니다.

포트 컨트롤러 하드웨어에 대한 I2C 통신 채널 초기화

샘플 참조: 에서 를 Device.cpp참조하세요EvtCreateDevice.

EVT_WDF_DEVICE_PREPARE_HARDWARE 구현에서 하드웨어 리소스를 읽어 통신 채널을 엽니다. PD 기능을 검색하고 경고에 대한 알림을 받는 데 필요합니다.

대부분의 TCPCI 컨트롤러는 I2C 연결입니다. 참조 샘플에서 클라이언트 드라이버는 SPBCx(SpbCx) 프로그래밍 인터페이스를 사용하여I2 채널을 엽니다.

클라이언트 드라이버는 WdfCmResourceListGetDescriptor를 호출하여 하드웨어 리소스를 열거합니다.

경고는 인터럽트로 수신됩니다. 따라서 드라이버는 프레임워크 인터럽트 개체를 만들고 경고를 처리하는 ISR을 등록합니다. ISR은 하드웨어 액세스가 완료될 때까지 차단하는 하드웨어 읽기 및 쓰기 작업을 수행합니다. DIRQL에서 대기는 허용되지 않으므로 드라이버는 PASSIVE_LEVEL ISR을 수행합니다.

포트 컨트롤러의 Type-C 및 PD 기능 초기화

샘플 참조: 에서 를 Device.cpp참조하세요EvtDeviceD0Entry.

EVT_WDF_DEVICE_D0_EXIT 구현에서

  1. 포트 컨트롤러 하드웨어와 통신하고 다양한 레지스터를 읽어 디바이스 식별 및 기능을 검색합니다.

  2. 검색된 정보를 사용하여 UCMTCPCI_PORT_CONTROLLER_IDENTIFICATION 초기화하고 UCMTCPCI_PORT_CONTROLLER_CAPABILITIES.

  3. 초기화된 구조를 UCMTCPCI_PORT_CONTROLLER_CONFIG_INIT 전달하여 앞의 정보를 사용하여 UCMTCPCI_PORT_CONTROLLER_CONFIG 구조를 초기화합니다.

  4. UcmTcpciPortControllerCreate를 호출하여 포트 컨트롤러 개체를 만들고 UCMTCPCIPORTCONTROLLER 핸들을 검색합니다.

UcmTcpciCx에서 요청을 수신하기 위한 프레임워크 큐 개체 설정

샘플 참조: 의 및 HardwareRequestQueueInitializeDevice.cppQueue.cpp참조하세요EvtDeviceD0Entry.

  1. EVT_WDF_DEVICE_D0_EXIT 구현에서 WdfIoQueueCreate를 호출하여 프레임워크 큐 개체를 만듭니다. 이 호출에서는 UcmTpciCx에서 보낸 IOCTL 요청을 처리하기 위해 콜백 구현을 등록해야 합니다. 클라이언트 드라이버는 전원 관리 큐를 사용할 수 있습니다.

    Type-C 및 PD 상태 컴퓨터를 실행하는 동안 UcmTpciCx는 실행할 명령을 클라이언트 드라이버에 보냅니다. UcmTcpciCx는 지정된 시간에 최대 하나의 미해결 포트 컨트롤러 요청을 보장합니다.

  2. UcmTcpciPortControllerSetHardwareRequestQueue를 호출하여 UcmTpciCx에 새 프레임워크 큐 개체를 등록합니다. 해당 호출이 성공하면 UcmTcpciCx는 드라이버의 작업이 필요할 때 이 큐에 WDFREQUEST(프레임워크 큐 개체)를 배치합니다.

  3. EvtIoDeviceControl 콜백 함수를 구현하여 이러한 IOCTL을 처리합니다.

제어 코드 설명
IOCTL_UCMTCPCI_PORT_CONTROLLER_GET_STATUS 유니버설 직렬 버스 Type-C 포트 컨트롤러 인터페이스 사양에 따라 모든 상태 레지스터의 값을 가져옵니다. 클라이언트 드라이버는 CC_STATUS, POWER_STATUS 및 FAULT_STATUS 레지스터의 값을 검색해야 합니다.
IOCTL_UCMTCPCI_PORT_CONTROLLER_GET_CONTROL 유니버설 직렬 버스 Type-C 포트 컨트롤러 인터페이스 사양에 따라 정의된 모든 컨트롤 레지스터의 값을 가져옵니다.
IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_CONTROL 유니버설 직렬 버스 Type-C 포트 컨트롤러 인터페이스 사양에 따라 정의된 컨트롤 레지스터의 값을 설정합니다.
IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_TRANSMIT 유니버설 직렬 버스 Type-C 포트 컨트롤러 인터페이스 사양에 따라 정의된 전송 레지스터를 설정합니다.
IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_TRANSMIT_BUFFER 유니버설 직렬 버스 Type-C 포트 컨트롤러 인터페이스 사양에 따라 정의된 TRANSMIT_BUFER 레지스터를 설정합니다.
IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_RECEIVE_DETECT 유니버설 직렬 버스 Type-C 포트 컨트롤러 인터페이스 사양에 따라 정의된 RECEIVE_DETECT 레지스터를 설정합니다.
IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_CONFIG_STANDARD_OUTPUT 유니버설 직렬 버스 Type-C 포트 컨트롤러 인터페이스 사양에 따라 정의된 CONFIG_STANDARD_OUTPUT 레지스터를 설정합니다.
IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_COMMAND 유니버설 직렬 버스 Type-C 포트 컨트롤러 인터페이스 사양에 따라 정의된 명령 레지스터의 값을 설정합니다.
IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_MESSAGE_HEADER_INFO 유니버설 직렬 버스 Type-C 포트 컨트롤러 인터페이스 사양에 따라 정의된 MESSAGE_HEADER_INFO 레지스터의 값을 설정합니다.
IOCTL_UCMTCPCI_PORT_CONTROLLER_ALTERNATE_MODE_ENTERED 드라이버가 다른 작업을 수행할 수 있도록 클라이언트 드라이버에 대체 모드가 입력되었음을 알 수 있습니다.
IOCTL_UCMTCPCI_PORT_CONTROLLER_ALTERNATE_MODE_EXITED 드라이버가 다른 작업을 수행할 수 있도록 클라이언트 드라이버에 대체 모드가 종료되었음을 알 수 있습니다.
IOCTL_UCMTCPCI_PORT_CONTROLLER_DISPLAYPORT_CONFIGURED 클라이언트 드라이버가 다른 작업을 수행할 수 있도록 파트너 디바이스의 DisplayPort 대체 모드가 핀 할당으로 구성되었음을 알 수 있습니다.
IOCTL_UCMTCPCI_PORT_CONTROLLER_DISPLAYPORT_HPD_STATUS_CHANGED 드라이버가 다른 작업을 수행할 수 있도록 핫 플러그 검색 상태 DisplayPort 연결이 변경되었음을 클라이언트 드라이버에 알 수 있습니다.
  1. UcmTcpciPortControllerStart를 호출하여 UcmTcpciCx에 포트 컨트롤러를 시작하도록 지시합니다. UcmTcpciCx는 USB Type-C 및 전원 배달을 제어하는 것으로 가정합니다. 포트 컨트롤러가 시작된 후 UcmTcpciCx는 하드웨어 요청 큐에 요청을 배치하기 시작할 수 있습니다.

포트 컨트롤러 하드웨어에서 경고 처리

샘플 참조: 다음을 참조하세요 ProcessAndSendAlerts . Alert.cpp

클라이언트 드라이버는 포트 컨트롤러 하드웨어에서 받은 경고(또는 이벤트)를 처리하고 이벤트와 관련된 데이터를 사용하여 UcmTcpciCx로 보내야 합니다.

하드웨어 경고가 발생하면 포트 컨트롤러 하드웨어가 경고 핀을 높게 구동합니다. 이렇게 하면 클라이언트 드라이버의 ISR(2단계에 등록됨)이 호출됩니다. 루틴은 PASSIVE_LEVEL 하드웨어 인터럽트 서비스를 제공합니다. 루틴은 인터럽트 가 포트 컨트롤러 하드웨어의 경고인지 확인합니다. 그렇다면 경고 처리를 완료하고 UcmTcpciPortControllerAlert를 호출하여 UcmTcpciCx에 알릴 수 있습니다.

UcmTcpciPortControllerAlert를 호출하기 전에 클라이언트는 경고와 관련된 모든 관련 데이터를 UCMTCPCI_PORT_CONTROLLER_ALERT_DATA 구조에 포함해야 합니다. 클라이언트는 하드웨어가 여러 경고를 동시에 어설션할 수 있기 때문에 활성 상태인 모든 경고의 배열을 제공합니다.

CC 상태의 변경을 보고하는 작업의 예제 흐름은 다음과 같습니다.

  1. 클라이언트는 하드웨어 경고를 받습니다.

  2. 클라이언트는 ALERT 레지스터를 읽고 활성 상태인 유형 경고를 결정합니다.

  3. 클라이언트는 CC STATUS 레지스터를 읽고 UCMTCPCI_PORT_CONTROLLER_ALERT_DATA CC STATUS 레지스터의 내용을 설명합니다. 드라이버는 AlertType 멤버를 UcmTcpciPortControllerAlertCCStatus 및 레지스터의 CCStatus 멤버로 설정합니다.

  4. 클라이언트는 UcmPortControllerAlert를 호출하여 배열 하드웨어 경고를 UcmTcpciCx로 보냅니다.

  5. 클라이언트는 경고를 지웁니다(클라이언트가 경고 정보를 검색한 후 언제든지 발생할 수 있음).

UcmTcpciCx에서 받은 프로세스 요청

샘플 참조: 참조 PortControllerInterface.cpp

상태 머신 실행의 일부로 UcmTcpciCx는 포트 컨트롤러에 요청을 보내야 합니다. 예를 들어 TRANSMIT_BUFFER 설정해야 합니다. 이 요청은 클라이언트 드라이버에 전달됩니다. 드라이버는 UcmTcpciCx에서 제공하는 세부 정보를 사용하여 전송 버퍼를 설정합니다. 이러한 요청의 대부분은 클라이언트 드라이버가 읽거나 쓰는 하드웨어로 변환됩니다. DPM이 하드웨어 전송이 완료되기를 기다리는 것을 차단할 수 없으므로 명령은 비동기여야 합니다.

UcmTcpciCx는 클라이언트 드라이버에서 필요한 get/set 작업을 설명하는 I/O 제어 코드로 명령을 보냅니다. 클라이언트 드라이버의 큐 설정에서 드라이버는 UcmTcpciCx에 해당 큐를 등록했습니다. UcmTcpciCx는 드라이버에서 작업해야 하는 프레임워크 요청 개체를 큐에 배치하기 시작합니다. I/O 컨트롤 코드는 4단계의 표에 나열됩니다.

적시에 요청을 완료하는 것은 클라이언트 드라이버의 책임입니다.

클라이언트 드라이버는 요청된 작업을 완료할 때 완료 상태 프레임워크 요청 개체에서 WdfRequestComplete를 호출합니다.

클라이언트 드라이버는 하드웨어 작업을 수행하기 위해 다른 드라이버에 I/O 요청을 보내야 할 수 있습니다. 예를 들어 샘플에서 드라이버는 SPB 요청을 I2C 연결 포트 컨트롤러로 보냅니다. 이 경우 요청 개체에 WDM IRP의 스택 위치 수가 올바르지 않을 수 있으므로 드라이버가 UcmTcpciCx에서 받은 프레임워크 요청 개체를 전달할 수 없습니다. 클라이언트 드라이버는 다른 프레임워크 요청 개체를 만들고 다른 드라이버에 전달해야 합니다. 클라이언트 드라이버는 UcmTcpciCx에서 요청을 받을 때마다 만드는 대신 초기화 중에 필요한 요청 개체를 미리 할당할 수 있습니다. UcmTcpciCx는 지정된 시간에 미해결 요청이 하나만 있음을 보장하기 때문에 가능합니다.

참고 항목