GPIO, I2C 및 SPI에 대한 사용자 모드 액세스를 사용하도록 설정하기

Windows 10 이상에서는 API를 사용자 모드에서 GPIO(범용 입력/출력), I2C(Inter-Integrated Circuit), SPI(직렬 주변 장치 인터페이스) 및 UART(범용 비동기 수신기-송신기)로 직접 액세스할 수 있습니다. Raspberry Pi 2와 같은 개발 보드는 사용자 지정 회로로 기본 컴퓨팅 모듈을 확장하여 특정 애플리케이션을 처리할 수 있도록 하는 이러한 연결 일부를 노출합니다. 이러한 낮은 수준의 버스는 일반적으로 헤더에 노출되는 GPIO 핀 및 버스의 하위 집합만 있는 다른 중요한 온보드 함수와 공유됩니다. 시스템의 안정성을 유지하기 위해서는 사용자 모드 애플리케이션에서 수정하기에 안전한 핀 및 버스를 지정해야 합니다.

이 문서는 ACPI(고급 구성 및 전원 인터페이스)에서 이 구성을 지정하는 방법을 설명하고 구성이 올바르게 지정되었는지 확인하기 위한 도구를 제공합니다.

중요

이 문서의 대상은 UEFI(통일 확장 펌웨어 인터페이스) 및 ACPI 개발자입니다. 이러한 개발자는 ACPI, ASL(ACPI 원본 언어) 제작 및 SpbCx/GpioClx에 대해 어느 정도 친숙한 것으로 간주됩니다.

Windows의 낮은 수준 버스에 대한 사용자 모드 액세스는 기존의 GpioClxSpbCx 프레임워크를 통해 연결됩니다. Windows IoT Core 및 Windows Enterprise에서 사용할 수 있는 RhProxy라는 새 드라이버는 GpioClxSpbCx 리소스를 사용자 모드에 공개합니다. 이 API를 사용하도록 설정하려면 사용자 모드에 공개해야 하는 각 GPIO 및 SPB 리소스를 사용하여 ACPI 테이블에서 rhproxy에 대한 디바이스 노드를 선언해야 합니다. 이 문서는 ASL 작성 및 확인을 안내합니다.

예시별 ASL

Raspberry Pi 2에서 rhproxy 디바이스 노드 선언을 살펴봅니다. 먼저 \_SB 범위에서 ACPI 디바이스 선언을 만듭니다.

Device(RHPX)
{
    Name(_HID, "MSFT8000")
    Name(_CID, "MSFT8000")
    Name(_UID, 1)
}
  • _HID – 하드웨어 ID. 공급업체 특정 하드웨어 ID로 설정합니다.
  • _CID – 호환 가능 ID. “MSFT8000”이어야 합니다.
  • _UID – 고유 ID. 1로 설정되어 있습니다.

다음으로 사용자 모드에 노출해야 하는 각 GPIO 및 SPB 리소스를 선언합니다. 리소스 인덱스는 리소스와 속성을 연결하는 데 사용되므로 리소스가 선언되는 순서가 중요합니다. 여러 I2C 또는 SPI 버스가 노출되는 경우 첫 번째 선언된 버스는 해당 유형의 '기본' 버스로 간주되며 Windows.Devices.I2c.I2cControllerWindows.Devices.Spi.SpiControllerGetDefaultAsync() 메서드에서 반환되는 인스턴스가 됩니다.

SPI

Raspberry Pi에는 두 대의 노출된 SPI 버스가 있습니다. SPI0에는 두 개의 하드웨어 칩 선택 라인이 있고 SPI1에는 하나의 하드웨어 칩 선택 라인이 있습니다. 각 버스의 각 칩 선택 라인에 대해 하나의 SPISerialBus() 리소스 선언이 필요합니다. 다음 두 SPISerialBus 리소스 선언은 SPI0의 두 칩 선택 라인에 대한 것입니다. DeviceSelection 필드는 드라이버가 하드웨어 칩 선택 줄 식별자로 해석하는 고유한 값을 포함합니다. DeviceSelection 필드에 입력한 정확한 값은 드라이버가 ACPI 연결 설명자의 이 필드를 해석하는 방법에 따라 달라집니다.

참고

이 문서는 Microsoft가 용납하지 않고 새 제품 및 설명서에서 사용을 중지한 용어인 슬레이브에 대한 참조를 포함합니다. 소프트웨어에서 용어가 제거되면 이 문서에서 해당 용어가 제거됩니다.

// Index 0
SPISerialBus(              // SCKL - GPIO 11 - Pin 23
                           // MOSI - GPIO 10 - Pin 19
                           // MISO - GPIO 9  - Pin 21
                           // CE0  - GPIO 8  - Pin 24
    0,                     // Device selection (CE0)
    PolarityLow,           // Device selection polarity
    FourWireMode,          // wiremode
    0,                     // databit len: placeholder
    ControllerInitiated,   // slave mode
    0,                     // connection speed: placeholder
    ClockPolarityLow,      // clock polarity: placeholder
    ClockPhaseFirst,       // clock phase: placeholder
    "\\_SB.SPI0",          // ResourceSource: SPI bus controller name
    0,                     // ResourceSourceIndex
                           // Resource usage
    )                      // Vendor Data

// Index 1
SPISerialBus(              // SCKL - GPIO 11 - Pin 23
                           // MOSI - GPIO 10 - Pin 19
                           // MISO - GPIO 9  - Pin 21
                           // CE1  - GPIO 7  - Pin 26
    1,                     // Device selection (CE1)
    PolarityLow,           // Device selection polarity
    FourWireMode,          // wiremode
    0,                     // databit len: placeholder
    ControllerInitiated,   // slave mode
    0,                     // connection speed: placeholder
    ClockPolarityLow,      // clock polarity: placeholder
    ClockPhaseFirst,       // clock phase: placeholder
    "\\_SB.SPI0",          // ResourceSource: SPI bus controller name
    0,                     // ResourceSourceIndex
                           // Resource usage
    )                      // Vendor Data

소프트웨어에서 이러한 두 리소스를 동일한 버스와 연결해야 한다는 것을 어떻게 알 수 있나요? DSD에 버스 이름 및 리소스 인덱스 간의 매핑이 지정됩니다.

Package(2) { "bus-SPI-SPI0", Package() { 0, 1 }},

이렇게 하면 리소스 인덱스 0과 1이라는 두 개의 칩 선택 라인이 있는 "SPI0"이라는 버스가 만들어집니다. SPI 버스의 기능을 선언하려면 몇 가지 속성이 추가로 필요합니다.

Package(2) { "SPI0-MinClockInHz", 7629 },
Package(2) { "SPI0-MaxClockInHz", 125000000 },

MinClockInHzMaxClockInHz 속성은 컨트롤러에서 지원하는 최소 및 최대 클록 속도를 지정합니다. API는 사용자가 이 범위를 벗어난 값을 지정하지 못하도록 합니다. 시계 속도는 연결 설명자의 _SPE 필드에 있는 SPB 드라이버에 전달됩니다(ACPI 섹션 6.4.3.8.2.2).

Package(2) { "SPI0-SupportedDataBitLengths", Package() { 8 }},

SupportedDataBitLengths 속성은 컨트롤러에서 지원하는 데이터 비트 길이를 나열합니다. 쉼표로 구분된 목록에 여러 값을 지정할 수 있습니다. API는 사용자가 이 목록을 벗어난 값을 지정하지 못하도록 합니다. 데이터 비트 길이는 연결 설명자의 _LEN 필드에 있는 SPB 드라이버에 전달됩니다(ACPI 섹션 6.4.3.8.2.2).

이러한 리소스 선언은 "템플릿"으로 간주될 수 있습니다. 일부 필드는 시스템 부팅 시 고정되며 다른 필드는 런타임에 동적으로 지정됩니다. SPISerialBus 설명자의 다음의 필드가 수정되었습니다.

  • DeviceSelection
  • DeviceSelectionPolarity
  • WireMode
  • SlaveMode
  • ResourceSource

다음의 필드는 런타임에 사용자가 지정한 값에 대한 자리 표시자입니다.

  • DataBitLength
  • ConnectionSpeed
  • ClockPolarity
  • ClockPhase

SPI1에는 단일 칩 선택 라인만 포함되므로 단일 SPISerialBus() 리소스가 선언됩니다.

// Index 2
SPISerialBus(              // SCKL - GPIO 21 - Pin 40
                           // MOSI - GPIO 20 - Pin 38
                           // MISO - GPIO 19 - Pin 35
                           // CE1  - GPIO 17 - Pin 11
    1,                     // Device selection (CE1)
    PolarityLow,           // Device selection polarity
    FourWireMode,          // wiremode
    0,                     // databit len: placeholder
    ControllerInitiated,   // slave mode
    0,                     // connection speed: placeholder
    ClockPolarityLow,      // clock polarity: placeholder
    ClockPhaseFirst,       // clock phase: placeholder
    "\\_SB.SPI1",          // ResourceSource: SPI bus controller name
    0,                     // ResourceSourceIndex
                           // Resource usage
    )                      // Vendor Data

함께 제공되는 이름 선언(필수)은 DSD에 지정되며 이 리소스 선언의 인덱스를 참조합니다.

Package(2) { "bus-SPI-SPI1", Package() { 2 }},

이렇게 하면 "SPI1"이라는 버스가 만들어지고 리소스 인덱스 2와 연결됩니다.

SPI 드라이버 요구 사항

  • SpbCx을(를) 사용하거나 SpbCx 호환이 가능해야 합니다
  • MITT SPI 테스트를 통과해야 합니다
  • 4Mhz 클록 속도를 지원해야 합니다
  • 8비트 데이터 길이를 지원해야 합니다
  • 모든 SPI 모드(0, 1, 2, 3)를 지원해야 합니다

I2C

다음으로, I2C 리소스를 선언합니다. Raspberry Pi는 핀 3과 5에 단일 I2C 버스를 노출합니다.

// Index 3
I2CSerialBus(              // Pin 3 (GPIO2, SDA1), 5 (GPIO3, SCL1)
    0xFFFF,                // SlaveAddress: placeholder
    ,                      // SlaveMode: default to ControllerInitiated
    0,                     // ConnectionSpeed: placeholder
    ,                      // Addressing Mode: placeholder
    "\\_SB.I2C1",          // ResourceSource: I2C bus controller name
    ,
    ,
    )                      // VendorData

함께 제공되는 이름 선언(필수)은 DSD에 지정됩니다.

Package(2) { "bus-I2C-I2C1", Package() { 3 }},

위에서 선언한 I2CSerialBus() 리소스의 인덱스인 리소스 인덱스 3을 참조하는 이름이 "I2C1"로 지정된 I2C 버스를 선언합니다.

I2CSerialBus() 설명자의 다음의 필드가 수정되었습니다.

  • SlaveMode
  • ResourceSource

다음의 필드는 런타임에 사용자가 지정한 값에 대한 자리 표시자입니다.

  • SlaveAddress
  • ConnectionSpeed
  • AddressingMode

I2C 드라이버 요구 사항

  • SpbCx를 사용하거나 SpbCx 호환이 가능해야 합니다
  • MITT I2C 테스트를 통과해야 합니다
  • 7비트 주소 지정을 지원해야 합니다.
  • 100kHz 클록 속도를 지원해야 합니다
  • 400kHz 클록 속도를 지원해야 합니다

GPIO

다음으로 사용자 모드에 노출되는 모든 GPIO 핀을 선언합니다. 노출할 핀을 결정하는 다음의 지침을 제공합니다.

  • 노출된 헤더에서 모든 핀을 선언합니다.
  • 버튼 및 LED와 같은 유용한 온보드 함수에 연결된 핀을 선언합니다.
  • 시스템 함수용으로 예약되거나 아무것도 연결되지 않은 핀을 선언하지 마세요.

다음의 ASL 블록은 GPIO4 및 GPIO5의 두 핀을 선언합니다. 다른 핀은 간결함을 위해 여기에 표시되지 않습니다. 부록 C에는 GPIO 리소스를 생성하는 데 사용할 수 있는 샘플 PowerShell 스크립트가 포함되어 있습니다.

// Index 4 – GPIO 4
GpioIO(Shared, PullUp, , , , “\\_SB.GPI0”, , , , ) { 4 }
GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, “\\_SB.GPI0”,) { 4 }

// Index 6 – GPIO 5
GpioIO(Shared, PullUp, , , , “\\_SB.GPI0”, , , , ) { 5 }
GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, “\\_SB.GPI0”,) { 5 }

GPIO 핀을 선언할 때 다음의 요구 사항을 준수해야 합니다.

  • 메모리 매핑된 GPIO 컨트롤러만 지원됩니다. I2C/SPI를 통해 인터페이스되는 GPIO 컨트롤러는 지원되지 않습니다. CLIENT_QueryControllerBasicInformation 콜백에 대한 응답으로 CLIENT_CONTROLLER_BASIC_INFORMATION 구조에서 MemoryMappedController 플래그를 설정하는 경우, 컨트롤러 드라이버는 메모리 매핑된 컨트롤러입니다.
  • 각 핀에는 GpioIO 및 GpioInt 리소스 모두가 필요합니다. GpioInt 리소스는 GpioIO 리소스의 바로 뒤에 있어야 하며 동일한 핀 번호를 참조해야 합니다.
  • GPIO 리소스는 핀 번호를 늘려 정렬해야 합니다.
  • 각 GpioIO 및 GpioInt 리소스는 핀 목록에 정확히 하나의 핀 번호를 포함해야 합니다.
  • 두 설명자의 ShareType 필드는 공유되어야 합니다.
  • GpioInt 설명자의 EdgeLevel 필드는 Edge여야 합니다.
  • GpioInt 설명자의 ActiveLevel 필드는 ActiveBoth여야 합니다.
  • PinConfig 필드
    • GpioIO 및 GpioInt 설명자 모두에서 동일해야 합니다.
    • PullUp, PullDown 또는 PullNone 중에서 하나여야 합니다. 이는 PullDefault일 수 없습니다.
    • 끌어오기 구성은 핀의 전원 켜기 상태와 일치해야 합니다. 핀의 상태는 전원 켜기 상태에서 지정된 끌어오기 모드로 핀을 배치해도 변경되지 않아야 합니다. 예를 들어 데이터시트에서 핀에 풀업이 제공되도록 지정하는 경우 PinConfig를 PullUp으로 지정합니다.

펌웨어, UEFI 및 드라이버 초기화 코드는 부팅 중에 핀의 상태를 전원 켜기 상태에서 변경해서는 안 됩니다. 사용자만 핀에 연결된 항목 및 따라서 어떤 상태 전환이 안전한지 알고 있습니다. 각 핀의 전원 켜기 상태를 문서화하여 사용자가 핀과 올바르게 인터페이스하는 하드웨어를 디자인할 수 있도록 해야 합니다. 부팅하는 동안 핀이 예기치 않게 상태를 변경해서는 안됩니다.

지원되는 드라이브 모드

GPIO 컨트롤러가 높은 임피댄스 입력 및 CMOS 출력 외에도 기본 제공 풀업 및 풀다운 저항기를 지원하는 경우 선택적 SupportedDriveModes 속성으로 이를 지정해야 합니다.

Package (2) { “GPIO-SupportedDriveModes”, 0xf },

SupportedDriveModes 속성은 GPIO 컨트롤러에서 지원되는 드라이브 모드를 나타냅니다. 위의 예시에서는 다음의 드라이브 모드가 모두 지원됩니다. 속성은 다음 값의 비트 마스크입니다.

플래그 값 드라이브 모드 설명
0x1 InputHighImpedance 핀이 ACPI의 "PullNone" 값에 해당하는 높은 임피던스 입력을 지원합니다.
0x2 InputPullUp 핀이 ACPI의 "PullUp" 값에 해당하는 기본 제공 풀업 저항기를 지원합니다.
0x4 InputPullDown 핀이 ACPI의 "PullDown" 값에 해당하는 기본 제공 풀다운 저항기를 지원합니다.
0x8 OutputCmos 핀은 (오픈 드레이닝과는 달리) 강한 최고 및 강한 최저값을 모두 생성하도록 지원합니다.

InputHighImpedance 및 OutputCmos는 거의 모든 GPIO 컨트롤러에서 지원됩니다. SupportedDriveModes 속성을 지정하지 않으면 기본값입니다.

노출된 헤더에 도달하기 전에 GPIO 신호가 수준 시프트를 통과하는 경우, 드라이브 모드가 외부 헤더에서 관찰할 수 없는 경우에도 SOC에서 지원하는 드라이브 모드를 선언합니다. 예를 들어 핀이 양방향 수준 시프터를 통과하여 핀이 저항적 풀업으로 열린 드레인으로 표시되는 경우, 핀이 높은 임피든스 입력으로 구성된 경우라도 노출된 헤더에서 높은 임피다스 상태를 관찰하지 않습니다. 핀이 높은 임피댄스 입력을 지원한다고 선언해야 합니다.

핀 번호 매기기

Windows는 다음과 같은 두 개의 핀 번호 매기기 체계를 지원합니다.

  • 순차 핀 번호 매기기 – 0, 1, 2...부터 노출된 최대 핀 수까지 번호가 표시됩니다. 0은 ASL에 선언된 첫 번째 GpioIo 리소스이고, 1은 ASL에 선언된 두 번째 GpioIo 리소스입니다.
  • 네이티브 핀 번호 매기기 – GpioIo 설명자에 지정된 핀 번호(예: 4, 5, 12, 13...)가 표시됩니다.
Package (2) { “GPIO-UseDescriptorPinNumbers”, 1 },

UseDescriptorPinNumbers 속성은 Windows에 순차 핀 번호 매기기 대신 네이티브 핀 번호 매기기를 사용하도록 지시합니다. UseDescriptorPinNumbers 속성이 지정되지 않았거나 값이 0이면 Windows는 기본적으로 순차 핀 번호 매기기로 설정됩니다.

네이티브 핀 번호 매기기를 사용하는 경우, PinCount 속성도 지정해야 합니다.

Package (2) { “GPIO-PinCount”, 54 },

PinCount 속성은 GpioClx 드라이버의 CLIENT_QueryControllerBasicInformation 콜백에서 TotalPins 속성을 통해 반환된 값과 일치해야 합니다.

보드에 대해 게시된 기존 설명서와 가장 호환되는 번호 매기기 체계를 선택합니다. 예를 들어 대부분의 기존 핀아웃 다이어그램은 BCM2835 핀 번호를 사용하기 때문에 Raspberry Pi는 네이티브 핀 번호 매기기를 사용합니다. MinnowBoardMax는 기존 핀아웃 다이어그램이 거의 없기 때문에 순차 핀 번호 매기기를 사용하며, 200개 이상의 핀에서 10개의 핀만 노출되므로 순차 핀 번호 매기기가 개발자 환경을 간소화합니다. 순차적 또는 네이티브 핀 번호 매기기를 사용하는 결정은 개발자의 혼란을 줄이는 것을 목표로 해야 합니다.

GPIO 드라이버 요구 사항

  • GpioClx을(를) 사용해야 합니다.
  • ON-SOC 메모리가 매핑되어야 합니다.
  • 에뮬레이트된 ActiveBoth 인터럽트 처리를 사용해야 합니다.

UART

UART 드라이버가 SerCx또는 SerCx2(을)를 사용하는 경우, rhproxy를 사용하여 사용자 모드에 드라이버를 노출할 수 있습니다. rhproxy는 GUID_DEVINTERFACE_COMPORT 형식의 디바이스 인터페이스를 만드는 UART 드라이버에서는 사용할 필요가 없습니다. 받은 편지함 Serial.sys 드라이버는 이러한 경우 중 하나입니다.

SerCx 스타일 UART를 사용자 모드에 노출하려면 다음과 같이 UARTSerialBus 리소스를 선언합니다.

// Index 2
UARTSerialBus(           // Pin 17, 19 of JP1, for SIO_UART2
    115200,                // InitialBaudRate: in bits ber second
    ,                      // BitsPerByte: default to 8 bits
    ,                      // StopBits: Defaults to one bit
    0xfc,                  // LinesInUse: 8 1-bit flags to declare line enabled
    ,                      // IsBigEndian: default to LittleEndian
    ,                      // Parity: Defaults to no parity
    ,                      // FlowControl: Defaults to no flow control
    32,                    // ReceiveBufferSize
    32,                    // TransmitBufferSize
    "\\_SB.URT2",          // ResourceSource: UART bus controller name
    ,
    ,
    ,
    )

ResourceSource 필드만 고정되지만 다른 모든 필드는 런타임에 사용자가 지정한 값의 자리 표시자입니다.

함께 제공되는 이름 선언은 다음과 같습니다.

Package(2) { "bus-UART-UART2", Package() { 2 }},

이 이름 선언은 사용자가 사용자 모드에서 버스에 액세스하는 데 사용하는 식별자에 해당하는 식별 이름 “UART2”를 컨트롤러에 할당합니다.

런타임 핀 먹싱

핀 먹싱은 다른 함수에 동일한 실제 핀을 사용하는 기능입니다. I2C 컨트롤러, SPI 컨트롤러 및 GPIO 컨트롤러와 같은 여러 다른 온칩 주변 장치는 SOC에서 동일한 물리적 핀으로 라우팅될 수 있습니다. 먹스 블록은 지정된 시간에 핀에서 활성 상태인 함수를 제어합니다. 일반적으로 펌웨어는 부팅 시 함수 할당을 설정해야 하며, 이 할당은 부팅 세션을 통해 정적으로 유지됩니다. 런타임 핀 먹싱은 런타임에 핀 함수 할당을 다시 구성하는 기능을 추가합니다. 사용자가 런타임에 핀의 기능을 선택할 수 있도록 하면 사용자가 보드의 핀을 신속하게 다시 구성할 수 있으므로 개발 속도가 빨라지고 하드웨어가 정적 구성보다 광범위한 애플리케이션을 지원할 수 있습니다.

사용자는 GPIO, I2C, SPI 및 UART에 대한 먹싱 지원을 추가 코드를 작성하지 않고 사용합니다. 사용자가 OpenPin() 또는 FromIdAsync()를 사용하여 GPIO 또는 버스를 열면 기본 물리적 핀이 요청된 함수에 자동으로 muxed됩니다. 핀이 다른 함수에서 이미 사용 중인 경우, OpenPin() 또는 FromIdAsync() 호출은 실패합니다. 사용자가 GpioPin, I2cDevice, SpiDevice 또는 SerialDevice 개체를 삭제하여 장치를 닫으면 핀이 해제되어 나중에 다른 함수에 대해 열 수 있습니다.

Windows는 GpioClx, SpbCxSerCx 프레임워크의 핀 먹싱에 대한 기본 제공 지원을 포함합니다. 이러한 프레임워크는 GPIO 핀 또는 버스에 액세스할 때 핀을 올바른 함수로 자동으로 전환하기 위해 함께 작동합니다. 핀에 대한 액세스는 여러 클라이언트 간의 충돌을 방지하기 위해 중재됩니다. 이 기본 제공 지원 외에도 핀 먹싱에 대한 인터페이스 및 프로토콜은 범용이며 추가 장치 및 시나리오를 지원하도록 확장할 수 있습니다.

이 문서는 먼저 핀 먹싱과 관련된 기본 인터페이스 및 프로토콜에 대해 설명한 다음 GpioClx, SpbCx 및 SerCx 컨트롤러 드라이버에 핀 먹싱에 대한 지원을 추가하는 방법을 설명합니다.

핀 먹싱 아키텍처

이 섹션은 핀 먹싱과 관련된 기본 인터페이스 및 프로토콜에 대해 설명합니다. 기본 프로토콜에 대한 지식은 GpioClx/SpbCx/SerCx 드라이버로 핀 먹싱을 지원하기 위해 반드시 필요한 것은 아닙니다. GpioCls/SpbCx/SerCx 드라이버로 핀 먹싱을 지원하는 방법에 대한 자세한 정보는 GpioClx 클라이언트 드라이버에서 핀 먹싱 지원 구현하기SpbCx 및 SerCx 컨트롤러 드라이버에서 먹싱 지원 사용하기를 참조하세요.

핀 먹싱은 여러 구성 요소의 협력을 통해 수행됩니다.

  • 핀 먹싱 서버 – 핀 먹싱 제어 블록을 제어하는 드라이버입니다. 핀 먹싱 서버는 먹싱 리소스에 대한 예약 요청(IRP_MJ_CREATE 요청을 통해)과 핀의 기능 전환 요청(*IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS 요청을 통해)을 통해 클라이언트에서 핀 먹싱 요청을 수신합니다. 핀 먹싱 서버는 먹싱 블록이 GPIO 블록의 일부일 때도 있기 때문에 일반적으로 GPIO 드라이버입니다. 먹싱 블록이 별도의 주변 장치인 경우에도 GPIO 드라이버는 먹싱 기능을 배치하는 논리적 위치입니다.
  • 핀 먹싱 클라이언트 – 핀 먹싱을 사용하는 드라이버입니다. 핀 먹싱 클라이언트는 ACPI 펌웨어에서 핀 먹싱 리소스를 받습니다. 핀 먹싱 리소스는 연결 리소스의 유형이며 리소스 허브에서 관리됩니다. 핀 먹싱 클라이언트는 핀 먹싱 리소스를 예약하기 위해 리소스에 대한 핸들을 엽니다. 하드웨어 변경에 영향을 주려면 클라이언트가 구성을 커밋하기 위해 IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS 요청을 전송해야 합니다. 클라이언트는 핸들을 닫아 핀 먹싱 리소스를 해제하며, 이때 먹싱 구성은 기본 상태로 되돌리기입니다.
  • ACPI 펌웨어 – MsftFunctionConfig() 리소스가 포함된 먹싱 구성을 지정합니다. MsftFunctionConfig 리소스는 클라이언트에서 먹싱 구성이 필요한 핀을 표현합니다. MsftFunctionConfig 리소스는 함수 번호, 끌어오기 구성 및 핀 번호 목록을 포함합니다. MsftFunctionConfig 리소스는 먹싱 클라이언트를 하드웨어 리소스로 고정하기 위해 제공되며, 이는 GPIO 및 SPB 연결 리소스와 유사하게 PrepareHardware 콜백의 드라이버에서 수신합니다. 클라이언트는 리소스에 대한 핸들을 여는 데 사용할 수 있는 리소스 허브 ID를 받습니다.

이러한 설명자는 현재 ACPI 작업 위원회의 검토를 받고 있으므로 MsftFunctionConfig() 설명자를 포함하는 ASL 파일을 컴파일하려면 /MsftInternal 명령줄 스위치를 asl.exe(으)로 전달해야 합니다. 예시: asl.exe /MsftInternal dsdt.asl

핀 먹싱과 관련된 작업 시퀀스는 다음과 같습니다.

Pin muxing client server interaction

  1. 클라이언트는 EvtDevicePrepareHardware() 콜백의 ACPI 펌웨어에서 MsftFunctionConfig 리소스를 받습니다.
  2. 클라이언트는 리소스 허브 도우미 함수인 RESOURCE_HUB_CREATE_PATH_FROM_ID()을(를) 사용하여 리소스 ID에서 경로를 만든 다음 경로에 대한 핸들을 엽니다(ZwCreateFile(), IoGetDeviceObjectPointer() 또는 WdfIoTargetOpen() 사용).
  3. 서버는 리소스 허브 도우미 함수 RESOURCE_HUB_ID_FROM_FILE_NAME()을(를) 사용하여 파일 경로에서 리소스 허브 ID를 추출한 다음, 리소스 허브를 쿼리하여 리소스 설명자를 가져옵니다.
  4. 서버는 설명자의 각 핀에 대해 공유 중재를 수행하고 IRP_MJ_CREATE 요청을 완료합니다.
  5. 클라이언트는 수신된 핸들에서 IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS 요청을 실행합니다.
  6. IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS 대한 응답으로 서버는 각 핀에서 지정된 함수를 활성화하여 하드웨어 먹싱 작업을 수행합니다.
  7. 클라이언트는 요청된 핀 먹싱 구성에 따라 작업을 진행합니다.
  8. 클라이언트가 더 이상 핀을 muxed할 필요가 없으면 핸들을 닫습니다.
  9. 핸들이 닫히는 것에 대한 응답으로 서버는 핀을 초기 상태로 다시 되돌립니다.

핀 먹싱 클라이언트에 대한 프로토콜 설명

이 섹션은 클라이언트가 핀 먹싱 기능을 사용하는 방법에 대해 설명합니다. 프레임워크는 컨트롤러 드라이버를 대신하여 이 프로토콜을 구현하므로 이 프로토콜은 SerCx 컨트롤러 드라이버 및 SpbCx 컨트롤러 드라이버에 적용되지 않습니다.

리소스 구문 분석

WDF 드라이버는 EvtDevicePrepareHardware() 루틴에서 MsftFunctionConfig() 리소스를 받습니다. MsftFunctionConfig 리소스는 다음의 필드로 식별할 수 있습니다.

CM_PARTIAL_RESOURCE_DESCRIPTOR::Type = CmResourceTypeConnection
CM_PARTIAL_RESOURCE_DESCRIPTOR::u.Connection.Class = CM_RESOURCE_CONNECTION_CLASS_FUNCTION_CONFIG
CM_PARTIAL_RESOURCE_DESCRIPTOR::u.Connection.Type = CM_RESOURCE_CONNECTION_TYPE_FUNCTION_CONFIG

EvtDevicePrepareHardware() 루틴은 다음과 같이 MsftFunctionConfig 리소스를 추출할 수 있습니다.

EVT_WDF_DEVICE_PREPARE_HARDWARE evtDevicePrepareHardware;

_Use_decl_annotations_
NTSTATUS
evtDevicePrepareHardware (
    WDFDEVICE WdfDevice,
    WDFCMRESLIST ResourcesTranslated
    )
{
    PAGED_CODE();

    LARGE_INTEGER connectionId;
    ULONG functionConfigCount = 0;

    const ULONG resourceCount = WdfCmResourceListGetCount(ResourcesTranslated);
    for (ULONG index = 0; index < resourceCount; ++index) {
        const CM_PARTIAL_RESOURCE_DESCRIPTOR* resDescPtr =
            WdfCmResourceListGetDescriptor(ResourcesTranslated, index);

        switch (resDescPtr->Type) {
        case CmResourceTypeConnection:
            switch (resDescPtr->u.Connection.Class) {
            case CM_RESOURCE_CONNECTION_CLASS_FUNCTION_CONFIG:
                switch (resDescPtr->u.Connection.Type) {
                case CM_RESOURCE_CONNECTION_TYPE_FUNCTION_CONFIG:
                    switch (functionConfigCount) {
                    case 0:
                        // save the connection ID
                        connectionId.LowPart = resDescPtr->u.Connection.IdLowPart;
                        connectionId.HighPart = resDescPtr->u.Connection.IdHighPart;
                        break;
                    } // switch (functionConfigCount)
                    ++functionConfigCount;
                    break; // CM_RESOURCE_CONNECTION_TYPE_FUNCTION_CONFIG

                } // switch (resDescPtr->u.Connection.Type)
                break; // CM_RESOURCE_CONNECTION_CLASS_FUNCTION_CONFIG
            } // switch (resDescPtr->u.Connection.Class)
            break;
        } // switch
    } // for (resource list)

    if (functionConfigCount < 1) {
        return STATUS_INVALID_DEVICE_CONFIGURATION;
    }
    // TODO: save connectionId in the device context for later use

    return STATUS_SUCCESS;
}

리소스 예약 및 커밋하기

클라이언트가 먹싱 핀을 사용하려는 경우, MsftFunctionConfig 리소스를 예약하고 커밋합니다. 다음의 예시는 클라이언트가 MsftFunctionConfig 리소스를 예약하고 커밋하는 방법을 보여 줍니다.

_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS AcquireFunctionConfigResource (
    WDFDEVICE WdfDevice,
    LARGE_INTEGER ConnectionId,
    _Out_ WDFIOTARGET* ResourceHandlePtr
    )
{
    PAGED_CODE();

    //
    // Form the resource path from the connection ID
    //
    DECLARE_UNICODE_STRING_SIZE(resourcePath, RESOURCE_HUB_PATH_CHARS);
    NTSTATUS status = RESOURCE_HUB_CREATE_PATH_FROM_ID(
            &resourcePath,
            ConnectionId.LowPart,
            ConnectionId.HighPart);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    //
    // Create a WDFIOTARGET
    //
    WDFIOTARGET resourceHandle;
    status = WdfIoTargetCreate(WdfDevice, WDF_NO_ATTRIBUTES, &resourceHandle);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    //
    // Reserve the resource by opening a WDFIOTARGET to the resource
    //
    WDF_IO_TARGET_OPEN_PARAMS openParams;
    WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(
        &openParams,
        &resourcePath,
        FILE_GENERIC_READ | FILE_GENERIC_WRITE);

    status = WdfIoTargetOpen(resourceHandle, &openParams);
    if (!NT_SUCCESS(status)) {
        return status;
    }
    //
    // Commit the resource
    //
    status = WdfIoTargetSendIoctlSynchronously(
            resourceHandle,
            WDF_NO_HANDLE,      // WdfRequest
            IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS,
            nullptr,            // InputBuffer
            nullptr,            // OutputBuffer
            nullptr,            // RequestOptions
            nullptr);           // BytesReturned

    if (!NT_SUCCESS(status)) {
        WdfIoTargetClose(resourceHandle);
        return status;
    }

    //
    // Pins were successfully muxed, return the handle to the caller
    //
    *ResourceHandlePtr = resourceHandle;
    return STATUS_SUCCESS;
}

드라이버는 나중에 닫을 수 있도록 WDFIOTARGET을 컨텍스트 영역 중 하나에 저장해야 합니다. 드라이버가 먹싱 구성을 해제할 준비가 되면 WDFIOTARGET을 다시 사용하려는 경우 WdfObjectDelete()또는 WdfIoTargetClose()를 호출하여 리소스 핸들을 닫아야 합니다.

    WdfObjectDelete(resourceHandle);

클라이언트가 리소스 핸들을 닫으면 핀이 초기 상태로 다시 muxed되고 다른 클라이언트에서 획득할 수 있습니다.

핀 먹싱 서버에 대한 프로토콜 설명

이 섹션은 핀 먹싱 서버가 해당 기능을 클라이언트에 노출하는 방법에 대해 설명합니다. 프레임워크는 클라이언트 드라이버를 대신하여 이 프로토콜을 구현하므로 GpioClx 미니포트 드라이버에는 적용되지 않습니다. GpioClx 클라이언트 드라이버에서 핀 먹싱을 지원하는 방법에 대해 자세히 알아보려면 GpioClx 클라이언트 드라이버에서 먹싱 지원 구현하기를 참조하세요.

IRP_MJ_CREATE 요청 처리하기

클라이언트는 핀 먹싱 리소스를 예약하려는 경우 리소스에 대한 핸들을 엽니다. 핀 먹싱 서버는 리소스 허브에서 재분석 작업을 통해 IRP_MJ_CREATE 요청을 받습니다. IRP_MJ_CREATE 요청의 후행 경로 구성 요소에는 16진수 형식의 64비트 정수인 리소스 허브 ID가 포함됩니다. 서버는 reshub.h에서 RESOURCE_HUB_ID_FROM_FILE_NAME()을(를) 사용하여 파일 이름에서 리소스 허브 ID를 추출하고 IOCTL_RH_QUERY_CONNECTION_PROPERTIES 리소스 허브로 보내 MsftFunctionConfig() 설명자를 가져와야 합니다.

서버는 설명자의 유효성을 검사하고 설명자에서 공유 모드 및 핀 목록을 추출해야 합니다. 그런 다음 핀에 대한 공유 중재를 수행해야 하며, 성공하면 요청을 완료하기 전에 핀을 예약된 것으로 표시합니다.

공유 중재는 핀 목록의 각 핀에 대해 공유 중재가 성공하면 전반적으로 성공합니다. 각 핀은 다음과 같이 중재되어야 합니다.

  • 핀이 아직 예약되지 않은 경우, 공유 중재가 성공합니다.
  • 핀이 이미 독점적으로 예약된 경우, 공유 중재는 실패합니다.
  • 핀이 이미 공유로 예약된 경우,
    • 들어오는 요청이 공유되고 공유 중재가 성공합니다.
    • 들어오는 요청은 독점적이고 공유 중재는 실패합니다.

공유 중재가 실패하면 STATUS_GPIO_INCOMPATIBLE_CONNECT_MODE 요청을 완료해야 합니다. 공유 중재가 성공하면 요청이 STATUS_SUCCESS로 완료되어야 합니다.

수신 요청의 공유 모드는 IrpSp->Parameters.Create.ShareAccess가 아닌 MsftFunctionConfig 설명자에서 가져옵니다.

IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS 요청 처리하기

클라이언트가 핸들을 열어 MsftFunctionConfig 리소스를 성공적으로 예약한 후에는 IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS 보내서 서버에 실제 하드웨어 먹싱 작업을 수행하도록 요청할 수 있습니다. 서버가 IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS를 받으면 핀 목록의 각 핀에 대해

  • PNP_FUNCTION_CONFIG_DESCRIPTOR 구조체의 PinConfiguration 멤버에 지정된 끌어오기 모드를 하드웨어로 설정해야 합니다.
  • PNP_FUNCTION_CONFIG_DESCRIPTOR 구조체의 FunctionNumber 멤버가 지정한 함수에 핀을 지정합니다.

그런 다음 서버는 STATUS_SUCCESS를 사용하여 요청을 완료해야 합니다.

FunctionNumber의 의미는 서버에 의해 정의되며, MsftFunctionConfig 설명자는 서버가 이 필드를 해석하는 방법에 대한 지식으로 작성된 것으로 이해됩니다.

핸들이 닫힌 경우 서버는 IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS를 받을 때 있던 구성에 핀을 되돌리기 해야 하므로 서버에서 핀의 상태를 저장해야 수정할 수 있습니다.

IRP_MJ_CLOSE 요청 처리하기

클라이언트에 더 이상 먹싱 리소스가 필요하지 않으면, 해당 핸들은 닫힙니다. 서버가 IRP_MJ_CLOSE 요청을 받으면 IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS가 수신되었을 때의 상태로 핀을 되돌리기 합니다. 클라이언트가 IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS를 보내지 않은 경우 아무 작업도 필요하지 않습니다. 그런 다음 서버는 공유 중재와 관련하여 핀을 사용 가능한 것으로 표시하고 STATUS_SUCCESS 요청을 완료해야 합니다. IRP_MJ_CLOSE 처리를 IRP_MJ_CREATE 처리와 올바르게 동기화해야 합니다.

ACPI 테이블에 대한 작성 지침

이 섹션은 클라이언트 드라이버에 먹싱 리소스를 제공하는 방법을 설명합니다. MsftFunctionConfig() 리소스가 포함된 테이블을 컴파일하려면 Microsoft ASL 컴파일러 빌드 14327 이상이 필요합니다. MsftFunctionConfig() 리소스는 핀 먹싱 클라이언트에 하드웨어 리소스로 제공됩니다. MsftFunctionConfig() 리소스는 일반적으로 핀 먹싱 변경이 필요한 드라이버(일반적으로 SPB 및 직렬 컨트롤러 드라이버)에 제공되어야 하며 SPB 및 직렬 주변 장치 드라이버의 경우 컨트롤러 드라이버에서 먹싱 구성을 처리하므로 제공될 필요가 없습니다. MsftFunctionConfig() ACPI 매크로는 다음과 같이 정의됩니다.

  MsftFunctionConfig(Shared/Exclusive
                PinPullConfig,
                FunctionNumber,
                ResourceSource,
                ResourceSourceIndex,
                ResourceConsumer/ResourceProducer,
                VendorData) { Pin List }

  • 공유/독점적 – 독점적인 경우 한 번에 하나의 클라이언트에서 이 핀을 획득할 수 있습니다. 공유되는 경우 여러 공유 클라이언트가 리소스를 획득할 수 있습니다. 조정되지 않은 여러 클라이언트가 변경 가능한 리소스에 액세스할 수 있도록 허용하면 데이터 경합이 발생하므로 예측할 수 없는 결과가 발생할 수 있으므로 항상 이 값을 독점으로 설정합니다.
  • PinPullConfig – 다음 중 하나
    • PullDefault – SOC 정의 전원 켜기 기본 끌어오기 구성 사용하기
    • PullUp – 풀업 저항기 사용하도록 설정하기
    • PullDown – 풀다운 저항기 사용하도록 설정하기
    • PullNone – 모든 끌어오기 저항기 사용하지 않도록 설정하기
  • FunctionNumber – mux로 프로그래밍할 함수 번호입니다.
  • ResourceSource – 핀 muxing 서버의 ACPI 네임스페이스 경로
  • ResourceSourceIndex – 이 항목을 0으로 설정
  • ResourceConsumer/ResourceProducer – 이 항목을 ResourceConsumer로 설정
  • VendorData – 핀 muxing 서버에서 의미를 정의하는 선택적 이진 데이터입니다. 일반적으로 비워 두어야 합니다
  • Pin List - 구성이 적용되는 핀 번호의 쉼표로 구분된 목록입니다. 핀 muxing 서버가 GpioClx 드라이버인 경우, GPIO 핀 번호이며 GpioIo 설명자의 핀 번호와 동일한 의미를 갖습니다.

다음의 예시는 MsftFunctionConfig() 리소스를 I2C 컨트롤러 드라이버에 제공하는 방법을 보여 줍니다.

Device(I2C1)
{
    Name(_HID, "BCM2841")
    Name(_CID, "BCMI2C")
    Name(_UID, 0x1)
    Method(_STA)
    {
        Return(0xf)
    }
    Method(_CRS, 0x0, NotSerialized)
    {
        Name(RBUF, ResourceTemplate()
        {
            Memory32Fixed(ReadWrite, 0x3F804000, 0x20)
            Interrupt(ResourceConsumer, Level, ActiveHigh, Shared) { 0x55 }
            MsftFunctionConfig(Exclusive, PullUp, 4, "\\_SB.GPI0", 0, ResourceConsumer, ) { 2, 3 }
        })
        Return(RBUF)
    }
}

일반적으로 컨트롤러 드라이버에 필요한 메모리 및 인터럽트 리소스 외에도 MsftFunctionConfig() 리소스도 지정됩니다. 이 리소스는 풀업 저항기가 활성화된 상태로 I2C 컨트롤러 드라이버에서 디바이스 노드에 의해 관리되는 핀 2 및 3을 기능 4의 \_SB.GPIO0에 추가할 수 있도록 합니다.

GpioClx 클라이언트 드라이버에서 muxing 지원하기

GpioClx에서는 기본적으로 핀 muxing을 지원합니다. GpioClx 미니포트 드라이버("GpioClx 클라이언트 드라이버"라고도 함)는 GPIO 컨트롤러 하드웨어를 구동합니다. Windows 10 빌드 14327을 기준으로 GpioClx 미니포트 드라이버는 두 개의 새 DDI를 구현하여 핀 muxing에 대한 지원을 추가할 수 있습니다.

  • CLIENT_ConnectFunctionConfigPins – GpioClx로 호출하여 지정된 muxing 구성을 적용하도록 미니포트 드라이버에 명령합니다.
  • CLIENT_DisconnectFunctionConfigPins – GpioClx로 호출하여 muxing 구성을 되돌리도록 미니포트 드라이버에 명령합니다.

이러한 루틴에 대한 설명은 GpioClx 이벤트 콜백 함수를 참조하세요.

이러한 두 가지 새로운 DDI 외에도 핀 muxing 호환성을 위해 기존의 DDI를 감사해야 합니다.

  • CLIENT_ConnectIoPins/CLIENT_ConnectInterrupt – CLIENT_ConnectIoPins는 GpioClx에 의해 호출되어 GPIO 입력 또는 출력에 대해 설정된 핀을 구성하도록 미니포트 드라이버에 명령합니다. GPIO는 MsftFunctionConfig와 함께 사용할 수 없습니다. 즉, GPIO 및 MsftFunctionConfig에 핀이 동시에 연결되지 않습니다. 핀의 기본 함수가 GPIO일 필요는 없으므로 커넥트IoPin이 호출될 때 핀이 GPIO에 muxed되지 않을 수도 있습니다. ConnectIoPins는 muxing 작업을 포함하여 GPIO IO에 대해 핀을 준비하는 데 필요한 모든 작업을 수행하는 데 필요합니다. CLIENT_ConnectInterrupt는 GPIO 입력의 특수한 사례로 간주될 수 있으므로 유사하게 동작해야 합니다.
  • CLIENT_DisconnectIoPins/CLIENT_DisconnectInterrupt – PreserveConfiguration 플래그를 지정하지 않는 한 이러한 루틴은 CLIENT_ConnectIoPins/CLIENT_ConnectInterrupt가 호출되었을 때의 상태로 핀을 반환해야 합니다. 핀 방향을 기본 상태로 되돌리기 것 외에도 미니포트는 각 핀의 muxing 상태를 _Connect 루틴이 호출될 때의 상태로 되돌리기 합니다.

예를 들어 핀의 기본 muxing 구성이 UART이고 핀을 GPIO로 사용할 수도 있다고 가정합니다. gpIO용 핀을 연결하기 위해 CLIENT_ConnectIoPins가 호출되면 GPIO에 핀을 mux하고 CLIENT_DisconnectIoPins 핀을 UART에 다시 mux해야 합니다. 일반적으로 Disconnect 루틴은 Connect 루틴에 의해 완료된 작업을 실행 취소합니다.

SpbCx 및 SerCx 컨트롤러 드라이버에서 먹싱 지원하기

Windows 10 빌드 14327을 기준으로, SpbCxSerCx 프레임워크에는 컨트롤러 드라이버 자체에 대한 코드 변경 없이 SpbCxSerCx 컨트롤러 드라이버가 muxing 클라이언트를 고정할 수 있도록 하는 핀 muxing에 대한 기본 제공 지원이 포함되어 있습니다. 더 나아가, muxing 사용 SpbCx/SerCx 컨트롤러 드라이버에 연결하는 SpbCx/SerCx 주변 장치 드라이버는 핀 muxing 작업을 트리거합니다.

다음의 다이어그램은 이러한 각 구성 요소 간의 종속성을 보여 줍니다. 여기에서 볼 수 있듯이 핀 muxing은 일반적으로 muxing을 담당하는 GPIO 드라이버에 SerCx 및 SpbCx 컨트롤러 드라이버의 종속성을 도입합니다.

Pin muxing dependency

디바이스 초기화 시 SpbCxSerCx 프레임워크는 하드웨어 리소스로 제공된 모든 MsftFunctionConfig() 리소스를 디바이스에 구문 분석합니다. 요청 시 SpbCx/SerCx는 핀 muxing 리소스를 획득하고 해제합니다.

SpbCx은(는) 클라이언트 드라이버의 EvtSpbTargetConnect() 콜백을 호출하기 바로 전에 해당 IRP_MJ_CREATE 처리기에 핀 muxing 구성을 적용합니다. muxing 구성을 적용할 수 없는 경우 컨트롤러 드라이버의 EvtSpbTargetConnect() 콜백이 호출되지 않습니다. 따라서 SPB 컨트롤러 드라이버는 EvtSpbTargetConnect()이(가) 호출될 때까지 핀이 SPB 함수에 muxed된다고 가정할 수 있습니다.

SpbCx은(는) 컨트롤러 드라이버의 EvtSpbTargetDisconnect() 콜백을 호출한 직후에 해당 IRP_MJ_CLOSE에서 핀 muxing 구성을 되돌립니다. 그 결과 주변 드라이버가 SPB 컨트롤러 드라이버에 대한 핸들을 열 때마다 핀이 SPB 함수에 muxed되고 주변 드라이버가 핸들을 닫을 때 muxed가 제거됩니다.

SerCx은(는) 비슷하게 동작합니다. SerCx은(는) 컨트롤러 드라이버의 EvtSerCx2FileOpen() 콜백을 호출하기 바로 전에 해당 IRP_MJ_CREATE 처리기의 모든 MsftFunctionConfig() 리소스를 획득하고, 컨트롤러 드라이버의 EvtSerCx2FileClose 콜백을 호출한 직후에 해당 IRP_MJ_CLOSE 처리기의 모든 리소스를 해제합니다.

SerCxSpbCx 컨트롤러 드라이버에 대한 동적 핀 muxing의 의미는 특정 시간에 SPB/UART 함수에서 멀리 muxed되는 핀을 허용할 수 있어야 한다는 것입니다. 컨트롤러 드라이버는 EvtSpbTargetConnect() 또는 EvtSerCx2FileOpen()이(가) 호출될 때까지 핀이 muxed되지 않는다고 가정해야 합니다. 핀은 다음 콜백 중에 SPB/UART 함수에 muxed할 필요가 없습니다. 다음은 전체 목록이 아니지만 컨트롤러 드라이버에서 구현하는 가장 일반적인 PNP 루틴을 나타냅니다.

  • DriverEntry
  • EvtDriverDeviceAdd
  • EvtDevicePrepareHardware/EvtDeviceReleaseHardware
  • EvtDeviceD0Entry/EvtDeviceD0Exit

확인

rhproxy 테스트가 준비되면 다음과 같이 단계별 절차를 사용하는 것이 좋습니다.

  1. 각각의 SpbCx, GpioClxSerCx 컨트롤러 드라이버가 올바르게 로드 및 작동 중인지 확인하기
  2. rhproxy이(가) 시스템에 존재하는지 확인합니다. Windows의 일부 버전 및 빌드에는 없습니다.
  3. ACPITABL.dat을(를) 사용하여 rhproxy 노드를 컴파일 및 로드하기
  4. rhproxy 디바이스 노드가 존재하는지 확인하기
  5. rhproxy이(가) 로드 및 시작 중인지 확인하기
  6. 예상되는 디바이스가 사용자 모드에 노출되는지 확인하기
  7. 명령줄에서 각 디바이스와 상호 작용이 가능한지 확인하기
  8. UWP 앱에서 각 디바이스와 상호 작용이 가능한지 확인하기
  9. HLK 테스트 실행하기

컨트롤러 드라이버 확인하기

rhproxy는 시스템의 다른 디바이스들을 사용자 모드에 노출하기 때문에 이러한 디바이스들이 이미 작동 중인 경우에만 작동합니다. 첫 번째 단계로 이러한 디바이스들, 즉 노출하고 싶은 I2C, SPI, GPIO 컨트롤러가 이미 작동 중인지 확인합니다.

명령 프롬프트에서 실행합니다

devcon status *

출력을 살펴보고 원하는 모든 디바이스들이 시작되었는지 확인합니다. 디바이스에 문제 코드가 있는 경우 해당 디바이스가 로드되지 않는 이유를 해결해야 합니다. 초기 플랫폼 가져오기 중에 모든 디바이스를 사용하도록 설정해야 합니다. SpbCx, GpioClx 또는 SerCx 컨트롤러 드라이버의 문제 해결 방법은 이 문서의 범위를 벗어납니다.

rhproxy가 시스템에 존재하는지 확인하기

rhproxy 서비스가 시스템에 존재하는지 확인합니다.

reg query HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\rhproxy

reg 키가 존재하지 않으면 시스템에 rhproxy가 존재하지 않습니다. 모든 IoT Core 빌드와 Windows Enterprise 빌드 15063 이상에는 rhproxy가 존재합니다.

ACPITABL.dat를 사용하여 ASL 컴파일 및 로드하기

rhproxy ASL 노드를 작성했다면 이제는 이를 컴파일하고 로드할 차례입니다. 시스템 ACPI 테이블에 추가할 수 있는 독립 실행형 AML 파일로 rhproxy 노드를 컴파일할 수 있습니다. 또는 시스템의 ACPI 소스에 액세스할 수 있는 경우 플랫폼의 ACPI 테이블에 직접 rhproxy 노드를 삽입할 수 있습니다. 그러나 초기 가져오기 중 ACPITABL.dat을(를) 사용하는 것이 더 쉬울 수 있습니다.

  1. yourboard.asl이라는 파일을 만들고 DEFINITIONBlock 내에 RHPX 디바이스 노드를 배치합니다.

    DefinitionBlock ("ACPITABL.dat", "SSDT", 1, "MSFT", "RHPROXY", 1)
    {
        Scope (\_SB)
        {
            Device(RHPX)
            {
            ...
            }
        }
    }
    
  2. WDK를 다운로드하고 C:\Program Files (x86)\Windows Kits\10\Tools\x64\ACPIVerify에서 asl.exe 찾기

  3. 다음의 명령을 실행하여 ACPITABL.dat를 생성합니다.

    asl.exe yourboard.asl
    
  4. 테스트 중인 시스템의 c:\windows\system32에 결과 ACPITABL.dat 파일을 복사합니다.

  5. 테스트 중인 시스템에서 테스트 서명 켜기:

    bcdedit /set testsigning on
    
  6. 테스트 중인 시스템을 다시 부팅합니다. 시스템은 ACPITABL.dat 정의된 ACPI 테이블을 시스템 펌웨어 테이블에 추가합니다.

rhproxy 디바이스 노드가 존재하는지 확인하기

다음의 명령을 실행하여 rhproxy 디바이스 노드를 열거합니다.

devcon status *msft8000

devcon의 출력에는 디바이스의 존재 여부가 표시되어야 합니다. 디바이스 노드가 존재하지 않으면 ACPI 테이블이 시스템에 제대로 추가되지 않습니다.

rhproxy가 로드 및 시작 중인지 확인하기

다음과 같이 rhproxy의 상태를 확인합니다.

devcon status *msft8000

rhproxy가 시작된 것으로 출력에 나타나면 rhproxy가 제대로 로드 및 시작된 것입니다. 문제 코드가 없으면 조사를 해야 합니다. 몇 가지 일반적인 문제 코드는 다음과 같습니다.

  • 문제 51 - CM_PROB_WAITING_ON_DEPENDENCY - 종속 프로그램 중 하나가 로드에 실패하였기 때문에 시스템에서 rhproxy가 시작되지 않고 있습니다. 이는 곧 리소스가 유효하지 않은 ACPI 노드로 rhproxy 지점을 전달했거나 대상 디바이스가 시작되지 않고 있다는 뜻입니다. 먼저, 모든 디바이스들이 제대로 실행되고 있는지 다시 확인합니다(위의 ‘컨트롤러 드라이버 확인’ 참조). 그런 다음, ASL을 다시 확인하여 모든 리소스 경로(예시: \_SB.I2C1)가 올바르고 DSDT의 유효한 노드를 가리키는지 확인합니다.
  • 문제 10 - CM_PROB_FAILED_START - 리소스 구문 분석 문제로 인해 rhproxy가 시작하지 못했을 가능성이 높습니다. ASL을 검토해서 DSD에서 리소스 인덱스를 다시 확인하고 GPIO 리소스의 핀 번호 순서가 증가하도록 지정되었는지 확인합니다.

예상되는 디바이스가 사용자 모드에 노출되는지 확인하기

rhproxy가 실행 중이면 사용자 모드에서 액세스가 가능한 디바이스 인터페이스가 생성된 것입니다. 몇 가지 명령줄 도구를 사용하여 디바이스를 열거하고 이들이 존재하는지 확인하게 됩니다.

https://github.com/ms-iot/samples 리포지토리를 복제하고 GpioTestTool, I2cTestTool, SpiTestToolMincomm 샘플을 빌드합니다. 테스트 동안 디바이스로 도구를 복사하고 다음 명령을 사용하여 디바이스를 열거합니다.

I2cTestTool.exe -list
SpiTestTool.exe -list
GpioTestTool.exe -list
MinComm.exe -list

디바이스와 식별 이름이 나열되어 보일 것입니다. 올바른 디바이스와 식별 이름이 보이지 않으면 ASL을 다시 확인합니다.

명령줄에서 각 디바이스 확인하기

다음 단계로 명령줄 도구를 사용하여 디바이스를 열고 디바이스와 상호 작용합니다.

I2CTestTool 예시:

I2cTestTool.exe 0x55 I2C1
> write {1 2 3}
> read 3
> writeread {1 2 3} 3

SpiTestTool 예시:

SpiTestTool.exe -n SPI1
> write {1 2 3}
> read 3

GpioTestTool 예시:

GpioTestTool.exe 12
> setdrivemode output
> write 0
> write 1
> setdrivemode input
> read
> interrupt on
> interrupt off

MinComm(직렬) 예시. 실행 전 Rx를 Tx에 연결합니다.

MinComm "\\?\ACPI#FSCL0007#3#{86e0d1e0-8089-11d0-9ce4-08003e301f73}\0000000000000006"
(type characters and see them echoed back)

UWP 앱에서 각 디바이스 확인하기

다음의 샘플을 사용하여 UWP에서 디바이스의 작동 여부를 확인합니다.

HLK 테스트 실행하기

하드웨어 랩 키트(HLK)를 다운로드합니다. 다음의 테스트를 사용할 수 있습니다.

HLK 관리자에서 rhproxy 디바이스 노드를 선택하면 해당 테스트가 자동으로 선택됩니다.

HLK 관리자에서 다음의 "리소스 허브 프록시 디바이스"를 선택합니다.

Screenshot of the Windows Hardware Lab Kit showing the Selection tab with the Resource Hub proxy device option selected.

그런 다음 테스트 탭을 클릭하고 I2C WinRT, Gpio WinRT 및 Spi WinRT 테스트를 선택합니다.

Screenshot of the Windows Hardware Lab Kit showing the Tests tab with the G P I O Win R T Functional and Stress Tests option selected.

선택 항목 실행을 클릭합니다. 각 테스트에 대한 추가 설명서는 테스트를 마우스 우클릭하고 "테스트 설명"을 클릭하여 사용할 수 있습니다.

리소스

부록

부록 A - Raspberry Pi ASL 목록

Raspberry Pi 2 & 3 Pin 매핑도 참조하세요.

DefinitionBlock ("ACPITABL.dat", "SSDT", 1, "MSFT", "RHPROXY", 1)
{

    Scope (\_SB)
    {
        //
        // RHProxy Device Node to enable WinRT API
        //
        Device(RHPX)
        {
            Name(_HID, "MSFT8000")
            Name(_CID, "MSFT8000")
            Name(_UID, 1)

            Name(_CRS, ResourceTemplate()
            {
                // Index 0
                SPISerialBus(              // SCKL - GPIO 11 - Pin 23
                                           // MOSI - GPIO 10 - Pin 19
                                           // MISO - GPIO 9  - Pin 21
                                           // CE0  - GPIO 8  - Pin 24
                    0,                     // Device selection (CE0)
                    PolarityLow,           // Device selection polarity
                    FourWireMode,          // wiremode
                    0,                     // databit len: placeholder
                    ControllerInitiated,   // slave mode
                    0,                     // connection speed: placeholder
                    ClockPolarityLow,      // clock polarity: placeholder
                    ClockPhaseFirst,       // clock phase: placeholder
                    "\\_SB.SPI0",          // ResourceSource: SPI bus controller name
                    0,                     // ResourceSourceIndex
                                           // Resource usage
                    )                      // Vendor Data

                // Index 1
                SPISerialBus(              // SCKL - GPIO 11 - Pin 23
                                           // MOSI - GPIO 10 - Pin 19
                                           // MISO - GPIO 9  - Pin 21
                                           // CE1  - GPIO 7  - Pin 26
                    1,                     // Device selection (CE1)
                    PolarityLow,           // Device selection polarity
                    FourWireMode,          // wiremode
                    0,                     // databit len: placeholder
                    ControllerInitiated,   // slave mode
                    0,                     // connection speed: placeholder
                    ClockPolarityLow,      // clock polarity: placeholder
                    ClockPhaseFirst,       // clock phase: placeholder
                    "\\_SB.SPI0",          // ResourceSource: SPI bus controller name
                    0,                     // ResourceSourceIndex
                                           // Resource usage
                    )                      // Vendor Data

                // Index 2
                SPISerialBus(              // SCKL - GPIO 21 - Pin 40
                                           // MOSI - GPIO 20 - Pin 38
                                           // MISO - GPIO 19 - Pin 35
                                           // CE1  - GPIO 17 - Pin 11
                    1,                     // Device selection (CE1)
                    PolarityLow,           // Device selection polarity
                    FourWireMode,          // wiremode
                    0,                     // databit len: placeholder
                    ControllerInitiated,   // slave mode
                    0,                     // connection speed: placeholder
                    ClockPolarityLow,      // clock polarity: placeholder
                    ClockPhaseFirst,       // clock phase: placeholder
                    "\\_SB.SPI1",          // ResourceSource: SPI bus controller name
                    0,                     // ResourceSourceIndex
                                           // Resource usage
                    )                      // Vendor Data
                // Index 3
                I2CSerialBus(              // Pin 3 (GPIO2, SDA1), 5 (GPIO3, SCL1)
                    0xFFFF,                // SlaveAddress: placeholder
                    ,                      // SlaveMode: default to ControllerInitiated
                    0,                     // ConnectionSpeed: placeholder
                    ,                      // Addressing Mode: placeholder
                    "\\_SB.I2C1",          // ResourceSource: I2C bus controller name
                    ,
                    ,
                    )                      // VendorData

                // Index 4 - GPIO 4 -
                GpioIO(Shared, PullUp, , , , "\\_SB.GPI0", , , , ) { 4 }
                GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",) { 4 }
                // Index 6 - GPIO 5 -
                GpioIO(Shared, PullUp, , , , "\\_SB.GPI0", , , , ) { 5 }
                GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",) { 5 }
                // Index 8 - GPIO 6 -
                GpioIO(Shared, PullUp, , , , "\\_SB.GPI0", , , , ) { 6 }
                GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",) { 6 }
                // Index 10 - GPIO 12 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 12 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 12 }
                // Index 12 - GPIO 13 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 13 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 13 }
                // Index 14 - GPIO 16 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 16 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 16 }
                // Index 16 - GPIO 18 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 18 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 18 }
                // Index 18 - GPIO 22 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 22 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 22 }
                // Index 20 - GPIO 23 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 23 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 23 }
                // Index 22 - GPIO 24 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 24 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 24 }
                // Index 24 - GPIO 25 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 25 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 25 }
                // Index 26 - GPIO 26 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 26 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 26 }
                // Index 28 - GPIO 27 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 27 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 27 }
                // Index 30 - GPIO 35 -
                GpioIO(Shared, PullUp, , , , "\\_SB.GPI0", , , , ) { 35 }
                GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",) { 35 }
                // Index 32 - GPIO 47 -
                GpioIO(Shared, PullUp, , , , "\\_SB.GPI0", , , , ) { 47 }
                GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",) { 47 }
            })

            Name(_DSD, Package()
            {
                ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
                Package()
                {
                    // Reference http://www.raspberrypi.org/documentation/hardware/raspberrypi/spi/README.md
                    // SPI 0
                    Package(2) { "bus-SPI-SPI0", Package() { 0, 1 }},                       // Index 0 & 1
                    Package(2) { "SPI0-MinClockInHz", 7629 },                               // 7629 Hz
                    Package(2) { "SPI0-MaxClockInHz", 125000000 },                          // 125 MHz
                    Package(2) { "SPI0-SupportedDataBitLengths", Package() { 8 }},          // Data Bit Length
                    // SPI 1
                    Package(2) { "bus-SPI-SPI1", Package() { 2 }},                          // Index 2
                    Package(2) { "SPI1-MinClockInHz", 30518 },                              // 30518 Hz
                    Package(2) { "SPI1-MaxClockInHz", 125000000 },                          // 125 MHz
                    Package(2) { "SPI1-SupportedDataBitLengths", Package() { 8 }},          // Data Bit Length
                    // I2C1
                    Package(2) { "bus-I2C-I2C1", Package() { 3 }},
                    // GPIO Pin Count and supported drive modes
                    Package (2) { "GPIO-PinCount", 54 },
                    Package (2) { "GPIO-UseDescriptorPinNumbers", 1 },
                    Package (2) { "GPIO-SupportedDriveModes", 0xf },                        // InputHighImpedance, InputPullUp, InputPullDown, OutputCmos
                }
            })
        }
    }
}

부록 B - MinnowBoardMax ASL 목록

MinnowBoard Max Pin Mappings도 참조하세요.

DefinitionBlock ("ACPITABL.dat", "SSDT", 1, "MSFT", "RHPROXY", 1)
{
    Scope (\_SB)
    {
        Device(RHPX)
        {
            Name(_HID, "MSFT8000")
            Name(_CID, "MSFT8000")
            Name(_UID, 1)

            Name(_CRS, ResourceTemplate()
            {
                // Index 0
                SPISerialBus(            // Pin 5, 7, 9 , 11 of JP1 for SIO_SPI
                    1,                     // Device selection
                    PolarityLow,           // Device selection polarity
                    FourWireMode,          // wiremode
                    8,                     // databit len
                    ControllerInitiated,   // slave mode
                    8000000,               // Connection speed
                    ClockPolarityLow,      // Clock polarity
                    ClockPhaseSecond,      // clock phase
                    "\\_SB.SPI1",          // ResourceSource: SPI bus controller name
                    0,                     // ResourceSourceIndex
                    ResourceConsumer,      // Resource usage
                    JSPI,                  // DescriptorName: creates name for offset of resource descriptor
                    )                      // Vendor Data

                // Index 1
                I2CSerialBus(            // Pin 13, 15 of JP1, for SIO_I2C5 (signal)
                    0xFF,                  // SlaveAddress: bus address
                    ,                      // SlaveMode: default to ControllerInitiated
                    400000,                // ConnectionSpeed: in Hz
                    ,                      // Addressing Mode: default to 7 bit
                    "\\_SB.I2C6",          // ResourceSource: I2C bus controller name (For MinnowBoard Max, hardware I2C5(0-based) is reported as ACPI I2C6(1-based))
                    ,
                    ,
                    JI2C,                  // Descriptor Name: creates name for offset of resource descriptor
                    )                      // VendorData

                // Index 2
                UARTSerialBus(           // Pin 17, 19 of JP1, for SIO_UART2
                    115200,                // InitialBaudRate: in bits ber second
                    ,                      // BitsPerByte: default to 8 bits
                    ,                      // StopBits: Defaults to one bit
                    0xfc,                  // LinesInUse: 8 1-bit flags to declare line enabled
                    ,                      // IsBigEndian: default to LittleEndian
                    ,                      // Parity: Defaults to no parity
                    ,                      // FlowControl: Defaults to no flow control
                    32,                    // ReceiveBufferSize
                    32,                    // TransmitBufferSize
                    "\\_SB.URT2",          // ResourceSource: UART bus controller name
                    ,
                    ,
                    UAR2,                  // DescriptorName: creates name for offset of resource descriptor
                    )

                // Index 3
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO2",) {0}  // Pin 21 of JP1 (GPIO_S5[00])
                // Index 4
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO2",) {0}

                // Index 5
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO2",) {1}  // Pin 23 of JP1 (GPIO_S5[01])
                // Index 6
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO2",) {1}

                // Index 7
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO2",) {2}  // Pin 25 of JP1 (GPIO_S5[02])
                // Index 8
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO2",) {2}

                // Index 9
                UARTSerialBus(           // Pin 6, 8, 10, 12 of JP1, for SIO_UART1
                    115200,                // InitialBaudRate: in bits ber second
                    ,                      // BitsPerByte: default to 8 bits
                    ,                      // StopBits: Defaults to one bit
                    0xfc,                  // LinesInUse: 8 1-bit flags to declare line enabled
                    ,                      // IsBigEndian: default to LittleEndian
                    ,                      // Parity: Defaults to no parity
                    FlowControlHardware,   // FlowControl: Defaults to no flow control
                    32,                    // ReceiveBufferSize
                    32,                    // TransmitBufferSize
                    "\\_SB.URT1",          // ResourceSource: UART bus controller name
                    ,
                    ,
                    UAR1,              // DescriptorName: creates name for offset of resource descriptor
                    )

                // Index 10
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {62}  // Pin 14 of JP1 (GPIO_SC[62])
                // Index 11
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {62}

                // Index 12
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {63}  // Pin 16 of JP1 (GPIO_SC[63])
                // Index 13
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {63}

                // Index 14
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {65}  // Pin 18 of JP1 (GPIO_SC[65])
                // Index 15
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {65}

                // Index 16
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {64}  // Pin 20 of JP1 (GPIO_SC[64])
                // Index 17
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {64}

                // Index 18
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {94}  // Pin 22 of JP1 (GPIO_SC[94])
                // Index 19
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {94}

                // Index 20
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {95}  // Pin 24 of JP1 (GPIO_SC[95])
                // Index 21
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {95}

                // Index 22
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {54}  // Pin 26 of JP1 (GPIO_SC[54])
                // Index 23
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {54}
            })

            Name(_DSD, Package()
            {
                ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
                Package()
                {
                    // SPI Mapping
                    Package(2) { "bus-SPI-SPI0", Package() { 0 }},

                    Package(2) { "SPI0-MinClockInHz", 100000 },
                    Package(2) { "SPI0-MaxClockInHz", 15000000 },
                    // SupportedDataBitLengths takes a list of support data bit length
                    // Example : Package(2) { "SPI0-SupportedDataBitLengths", Package() { 8, 7, 16 }},
                    Package(2) { "SPI0-SupportedDataBitLengths", Package() { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 }},
                     // I2C Mapping
                    Package(2) { "bus-I2C-I2C5", Package() { 1 }},
                    // UART Mapping
                    Package(2) { "bus-UART-UART2", Package() { 2 }},
                    Package(2) { "bus-UART-UART1", Package() { 9 }},
                }
            })
        }
    }
}

부록 C - GPIO 리소스를 생성하는 샘플 Powershell 스크립트

다음의 스크립트를 사용하여 Raspberry Pi에 대한 GPIO 리소스 선언을 생성할 수 있습니다.

$pins = @(
    @{PinNumber=4;PullConfig='PullUp'},
    @{PinNumber=5;PullConfig='PullUp'},
    @{PinNumber=6;PullConfig='PullUp'},
    @{PinNumber=12;PullConfig='PullDown'},
    @{PinNumber=13;PullConfig='PullDown'},
    @{PinNumber=16;PullConfig='PullDown'},
    @{PinNumber=18;PullConfig='PullDown'},
    @{PinNumber=22;PullConfig='PullDown'},
    @{PinNumber=23;PullConfig='PullDown'},
    @{PinNumber=24;PullConfig='PullDown'},
    @{PinNumber=25;PullConfig='PullDown'},
    @{PinNumber=26;PullConfig='PullDown'},
    @{PinNumber=27;PullConfig='PullDown'},
    @{PinNumber=35;PullConfig='PullUp'},
    @{PinNumber=47;PullConfig='PullUp'})

# generate the resources
$FIRST_RESOURCE_INDEX = 4
$resourceIndex = $FIRST_RESOURCE_INDEX
$pins | % {
    $a = @"
// Index $resourceIndex - GPIO $($_.PinNumber) - $($_.Name)
GpioIO(Shared, $($_.PullConfig), , , , "\\_SB.GPI0", , , , ) { $($_.PinNumber) }
GpioInt(Edge, ActiveBoth, Shared, $($_.PullConfig), 0, "\\_SB.GPI0",) { $($_.PinNumber) }
"@
    Write-Host $a
    $resourceIndex += 2;
}