디바이스에 대한 연결 설정을 가져오는 방법

SPB 컨트롤러 드라이버가 EvtSpbTargetConnect 콜백 함수를 등록하는 경우 SPB 프레임워크 확장 (SpbCx)은 컨트롤러의 클라이언트(주변 드라이버)가 버스의 대상 디바이스에 대한 논리적 연결을 여는 IRP_MJ_CREATE 요청을 보낼 때 이 함수를 호출합니다. EvtSpbTargetConnect 콜백에 대한 응답으로 SPB 컨트롤러 드라이버는 SpbTargetGetConnectionParameters 메서드를 호출하여 대상 디바이스에 대한 연결 설정을 가져와야 합니다. SPB 컨트롤러 드라이버는 이러한 설정을 저장하고 나중에 클라이언트의 I/O 요청에 대한 응답으로 디바이스에 액세스하는 데 사용합니다.

예를 들어 I2C 버스의 대상 디바이스에 대한 연결 설정에는 디바이스의 버스 주소, 주소 너비(7비트 또는 10비트) 및 디바이스 액세스 중에 사용할 버스 시계 빈도가 포함됩니다. I2C 컨트롤러 드라이버는 이러한 설정을 사용하여 I2C 버스를 통해 디바이스에 액세스하도록 컨트롤러를 구성합니다.

SPB 컨트롤러 드라이버는 SpbTargetGetConnectionParameters 를 호출하여 I2C 또는 SPI 형식의 직렬 버스에 대상 디바이스의 연결을 설명하는 직렬 버스 연결 설명자에 대한 포인터를 가져옵니다. 이 설명자에는 두 직렬 버스 유형 모두에 공통적인 연결 정보가 포함되어 있으며 디바이스가 연결된 직렬 버스와 관련된 정보가 뒤따릅니다. 이 설명자의 형식에 대한 자세한 내용은 ACPI 5.0 사양을 참조하세요.

다음 코드 예제에서 I2C 컨트롤러 드라이버는 PNP_I2C_SERIAL_BUS_DESCRIPTOR 구조를 정의합니다. 이 구조체는 I2C 직렬 버스 연결 설명자를 나타냅니다. 이 용어는 ACPI 5.0 사양이 I2C 버스와 관련된 연결 설정 뒤에 오는 직렬 버스 연결 설명자를 설명하는 데 사용하는 용어입니다. PNP_I2C_SERIAL_BUS_DESCRIPTOR 구조체의 첫 번째 멤버인 SerialBusDescriptor는 직렬 버스 연결 설명자를 나타내는 PNP_SERIAL_BUS_DESCRIPTOR 구조체입니다. ConnectionSpeedSlaveAddress 멤버에는 I2C 관련 연결 설정이 포함됩니다.

#include <reshub.h>
#include <pshpack1.h>  

//
// See the ACPI 5.0 spec, section 6.4.3.8.2.1 (I2C Serial Bus Connection Descriptor).  
//
typedef struct _PNP_I2C_SERIAL_BUS_DESCRIPTOR {  
    PNP_SERIAL_BUS_DESCRIPTOR SerialBusDescriptor;  
    ULONG ConnectionSpeed;  
    USHORT SlaveAddress;  
    // Followed by optional vendor-specific data.
    // Followed by name of serial bus controller.
} PNP_I2C_SERIAL_BUS_DESCRIPTOR, *PPNP_I2C_SERIAL_BUS_DESCRIPTOR;  
  
#include <poppack.h>

reshub.h 헤더 파일은 PNP_SERIAL_BUS_DESCRIPTOR 구조를 정의합니다. pshpack1.h 및 poppack.h 헤더 파일은 컴파일러에서 사용하는 구조체 맞춤 모드를 제어합니다.

I2C 직렬 버스 연결 설명자는 인접 필드가 중간 간격 없이 가장 가까운 바이트 경계에 정렬되는 압축된 데이터 구조입니다. 따라서 이 설명자의 16비트 정수 값은 홀수 바이트 경계에서 시작될 수 있습니다. 앞의 코드 예제에서 pshpack1.h는 컴파일러에 인접한 구조체 멤버를 압축하도록 지시하기 위해 포함되며, poppack.h는 컴파일러에 기본 구조 맞춤을 다시 시작하도록 지시합니다.

PNP_I2C_SERIAL_BUS_DESCRIPTOR 구조체의 ConnectionSpeed 멤버는 대상 디바이스에 액세스하는 동안 I2C 버스를 클록할 빈도를 Hertz로 지정합니다. SlaveAddress 멤버는 대상 디바이스의 버스 주소입니다. 일부 I2C 컨트롤러 드라이버의 경우 SlaveAddress 멤버 뒤에 공급업체별 선택적 데이터가 뒤따를 수 있지만 이 데이터는 이 코드 예제의 드라이버에서 사용되지 않으므로 구조 정의의 일부가 아닙니다.

다음 코드 예제에서 이전 예제의 I2C 컨트롤러 드라이버는 SpbTargetGetConnectionParameters를 호출하여 I2C 버스의 대상 디바이스에 대한 연결 설정을 가져오는 루틴을 구현 GetTargetSettings 합니다. 이 루틴에 대한 대상 입력 매개 변수는 대상 디바이스에 대한 핸들입니다. 설정 출력 매개 변수는 루틴이 연결 매개 변수 집합을 작성하는 드라이버 할당 SPB_CONNECTION_PARAMETERS 구조체에 대한 포인터입니다. 이러한 매개 변수에는 요청된 연결 설정에 대한 포인터가 포함됩니다.

#define I2C_SERIAL_BUS_TYPE 0x01
#define I2C_SERIAL_BUS_SPECIFIC_FLAG_10BIT_ADDRESS 0x0001

typedef enum _I2C_ADDRESS_MODE
{
    AddressMode7Bit,
    AddressMode10Bit
} I2C_ADDRESS_MODE, *PI2C_ADDRESS_MODE;
  
typedef struct _I2C_TARGET_SETTINGS
{
    ULONG  ClockFrequency;
    ULONG  Address;
    I2C_ADDRESS_MODE  AddressMode;
} I2C_TARGET_SETTINGS, *PI2C_TARGET_SETTINGS;

NTSTATUS
GetTargetSettings(_In_ SPBTARGET Target, _Out_ PI2C_TARGET_SETTINGS Settings)
{
    PRH_QUERY_CONNECTION_PROPERTIES_OUTPUT_BUFFER Connection = NULL;
    SPB_CONNECTION_PARAMETERS Params;

    SPB_CONNECTION_PARAMETERS_INIT(&Params);
    SpbTargetGetConnectionParameters(Target, &Params);
    Connection = (PRH_QUERY_CONNECTION_PROPERTIES_OUTPUT_BUFFER)Params.ConnectionParameters;
    if (Connection->PropertiesLength < sizeof(PNP_SERIAL_BUS_DESCRIPTOR))
    {
        return STATUS_INVALID_PARAMETER;
    }

    PPNP_SERIAL_BUS_DESCRIPTOR Descriptor;

    Descriptor = (PPNP_SERIAL_BUS_DESCRIPTOR)Connection->ConnectionProperties;
    if (Descriptor->Tag != SERIAL_BUS_DESCRIPTOR ||
        Descriptor->SerialBusType != I2C_SERIAL_BUS_TYPE)
    {
        return STATUS_INVALID_PARAMETER;
    }

    PPNP_I2C_SERIAL_BUS_DESCRIPTOR I2CDescriptor;
    USHORT I2CFlags;

    I2CDescriptor = (PPNP_I2C_SERIAL_BUS_DESCRIPTOR)Connection->ConnectionProperties;
    Settings->Address = (ULONG)I2CDescriptor->SlaveAddress;
    I2CFlags = I2CDescriptor->SerialBusDescriptor.TypeSpecificFlags;
    Settings->AddressMode = 
                ((I2CFlags & I2C_SERIAL_BUS_SPECIFIC_FLAG_10BIT_ADDRESS) == 0) ? AddressMode7Bit : AddressMode10Bit;

    Settings->ClockFrequency = I2CDescriptor->ConnectionSpeed;

    return STATUS_SUCCESS;
}

앞의 코드 예제에서 SpbTargetGetConnectionParameters는 드라이버 할당 Params 구조에 연결 매개 변수를 씁니다. 의 ParamsConnectionParameters 멤버는 connectionProperties 멤버가 직렬 버스 연결 설명자의 첫 번째 바이트이고 이 설명자의 나머지 바이트가 ConnectionProperties 멤버 바로 뒤에 오는 RH_QUERY_CONNECTION_PROPERTIES_OUTPUT_BUFFER 구조체(reshub.h에 정의됨)를 가리킵니다.ParamsConnectionParameters 멤버가 가리키는 버퍼는 RH_QUERY_CONNECTION_PROPERTIES_OUTPUT_BUFFER 구조체와 이 구조체를 따르는 설명자 바이트를 포함할 만큼 충분히 큽니다.

앞의 코드 예제에서 드라이버 구현 GetTargetSettings 루틴은 SpbTargetGetConnectionParameters에서 받은 연결 매개 변수에 대해 다음 매개 변수 검사를 수행합니다.

  • RH_QUERY_CONNECTION_PROPERTIES_OUTPUT_BUFFER 구조에 포함된 직렬 버스 연결 설명자의 크기가 적어도 sizeof(PNP_SERIAL_BUS_DESCRIPTOR)인지 확인합니다.
  • ACPI 5.0 사양에 따라 직렬 버스 연결 설명자의 첫 번째 바이트가 SERIAL_BUS_DESCRIPTOR(상수 값 0x8e)로 설정되어 있는지 확인합니다.
  • 직렬 버스 연결 설명자의 직렬 버스 형식이 직렬 버스 유형을 I2C로 식별하는 I2C_SERIAL_BUS_TYPE(상수 값 0x01)로 설정되어 있는지 확인합니다.

이전 코드 예제의 끝에서 *Settings 구조에는 대상 디바이스에 대한 연결 설정(버스 주소, 주소 너비 및 버스 클록 빈도)이 포함됩니다. I2C 컨트롤러 드라이버는 이러한 연결 설정을 사용하여 디바이스에 액세스하도록 컨트롤러를 구성합니다.