ACPI Method
首先我们来看一段在ACPI中定义的Method:
//Global Buffer
Name(DATA, Buffer(0x4) { 0x00, 0x00, 0x00, 0x00})
Device (TEST)
{
Name (_HID, "TEST001")
Name (_UID, 1)
...
Method(GETD, 0x0, NotSerialized)
{
return (DATA)
}
Method(SETD, 0x1, NotSerialized)
{
Store(Arg0, DATA)
}
...
}
从代码中可以看到,该TEST设备提供了2个Method:GETD()和SETD()。全局变量DATA为4字节数组,GETD()方法用于读取DATA的数据并传给驱动程序,SETD()则用于将驱动程序传过来的数据写入DATA中去。用C语言的表现形式来描述它们的话,就可以写成这样:uchar* GETD(void); 和 void SETD(uchar* data);
IOCTL_ACPI_EVAL_METHOD 请求
驱动程序可以通过调用WdfIoTargetSendIoctlSynchronously()函数发送IOCTL_ACPI_EVAL_METHOD请求给ACPI驱动来调用ACPI Method。关于该IOCTL更详细的信息,请查阅MSDN文档:IOCTL_ACPI_EVAL_METHOD control code
在编写KMDF驱动之前,我们需要先了解一下以下4个结构体:
ACPI_EVAL_INPUT_BUFFER
ACPI_EVAL_INPUT_BUFFER_COMPLEX
ACPI_EVAL_OUTPUT_BUFFER
ACPI_METHOD_ARGUMENT
ACPI_EVAL_INPUT_BUFFER结构体
结构体定义如下:
typedef struct _ACPI_EVAL_INPUT_BUFFER {
ULONG Signature;
union {
UCHAR MethodName[4];
ULONG MethodNameAsUlong;
};
} ACPI_EVAL_INPUT_BUFFER, *PACPI_EVAL_INPUT_BUFFER;
该结构体用于调用一个
不带输入参数的ACPI Method,假设要访问的Method为GETD(),在发送IOCTL_ACPI_EVAL_METHOD请求之前,需要对其成员变量进行如下设置:
- 设置Signature为ACPI_EVAL_INPUT_BUFFER_SIGNATURE
- 设置MethodName为 'GETD' 或设置MethodNameAsUlong为 (ULONG)('DTEG')
ACPI_EVAL_INPUT_BUFFER_COMPLEX结构体
结构体定义如下:
typedef struct _ACPI_EVAL_INPUT_BUFFER_COMPLEX {
ULONG Signature;
union {
UCHAR MethodName[4];
ULONG MethodNameAsUlong;
};
ULONG Size;
ULONG ArgumentCount;
ACPI_METHOD_ARGUMENT Argument[ANYSIZE_ARRAY];
} ACPI_EVAL_INPUT_BUFFER_COMPLEX, *PACPI_EVAL_INPUT_BUFFER_COMPLEX;
该结构体用于调用一个
带输入参数的ACPI Method,用来传递输入参数,假设要访问的Method为SETD(),在发送IOCTL_ACPI_EVAL_METHOD请求之前,需要对其成员变量进行如下设置:
- 设置Signature为ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE
- 设置MethodName为 'SETD' 或设置MethodNameAsUlong为 (ULONG)('DTES')
- 设置Size的值,该值表示Argument[ANYSIZE_ARRAY]整个数组的字节大小
- 设置ArgumentCount的值,这里为1
- 给结构体成员Argument赋值,设置输入参数
ACPI_EVAL_OUTPUT_BUFFER结构体
结构体定义如下:
typedef struct _ACPI_EVAL_OUTPUT_BUFFER {
ULONG Signature;
ULONG Length;
ULONG Count;
ACPI_METHOD_ARGUMENT Argument[ANYSIZE_ARRAY];
} ACPI_EVAL_OUTPUT_BUFFER;
该结构体用于返回ACPI Method执行后的输出参数,输出参数将被保存在Argument成员变量中,Signature的值必须为ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE,Length表示整个ACPI_EVAL_OUTPUT_BUFFER结构体的字节大小,Count记录了有多少个Argument成员。关于该结构体更详细的信息,请查阅MSDN文档:ACPI_EVAL_OUTPUT_BUFFER structure
ACPI_METHOD_ARGUMENT结构体
结构体定义如下:
typedef struct _ACPI_METHOD_ARGUMENT
USHORT Type;
USHORT DataLength;
union {
ULONG Argument;
UCHAR Data[ANYSIZE_ARRAY];
};
} ACPI_METHOD_ARGUMENT;
该结构体才是真正存储输入、输出参数的地方。其中
Type定义了参数的类型,它的值有以下几种:
ACPI_METHOD_ARGUMENT_INTEGER
ACPI_METHOD_ARGUMENT_STRING
ACPI_METHOD_ARGUMENT_BUFFER
ACPI_METHOD_ARGUMENT_PACKAGE
DataLength为数组Data的字节大小。
关于该结构体更详细的信息,请查阅MSDN文档:ACPI_METHOD_ARGUMENT structure
Sample Code
访问不带输入参数的ACPI Method:NTSTATUS ACPIGetData(WDFDEVICE FxDevice, void *pBuffer)
{
NTSTATUS Status;
WDF_MEMORY_DESCRIPTOR InputDescriptor;
WDF_MEMORY_DESCRIPTOR OutputDescriptor;
ACPI_EVAL_INPUT_BUFFER InputBuffer;
ACPI_EVAL_OUTPUT_BUFFER OutputBuffer;
WDFIOTARGET IoTarget;
ULONG SizeReturned;
USHORT DataLength;
PAGED_CODE();
//
// Signature and Method name in reverse
//
InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
InputBuffer.MethodNameAsUlong = (ULONG) ('DTEG');
//
// Use following WDF method to initialize memory descriptor
// The memory descriptor is initialized with the input buffer we have defined.
//
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&InputDescriptor,
(PVOID)&InputBuffer,
sizeof(ACPI_EVAL_INPUT_BUFFER));
RtlZeroMemory(&OutputBuffer, sizeof(OutputBuffer));
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&OutputDescriptor,
(PVOID)&OutputBuffer,
sizeof(ACPI_EVAL_OUTPUT_BUFFER));
//
// Get handle for underlying ACPI layer
//
IoTarget = WdfDeviceGetIoTarget(FxDevice);
//
// Send synchronous request
//
Status = WdfIoTargetSendIoctlSynchronously(IoTarget,
NULL,
IOCTL_ACPI_EVAL_METHOD,
&InputDescriptor,
&OutputDescriptor,
NULL,
&SizeReturned);
if (!NT_SUCCESS(Status)) {
return Status;
}
//
// Verify output signature and length
//
if ((OutputBuffer.Signature == ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE)
&& (OutputBuffer.Argument[0].Type == ACPI_METHOD_ARGUMENT_BUFFER))
{
//
// Extract data from buffer
//
DataLength = OutputBuffer.Argument[0].DataLength;
memcpy_s((UINT8*)pBuffer,
(DataLength * sizeof(UINT8)),
(UINT8*)OutputBuffer.Argument[0].Data,
(DataLength * sizeof(UINT8)));
Status = STATUS_SUCCESS;
}
else
{
Status = STATUS_ACPI_INVALID_DATA;
}
exit:
return Status;
}
访问带输入参数的ACPI Method:
NTSTATUS ACPISetData(WDFDEVICE FxDevice, void *pBuffer)
{
NTSTATUS Status;
WDF_MEMORY_DESCRIPTOR InputDescriptor;
WDF_MEMORY_DESCRIPTOR OutputDescriptor;
ACPI_EVAL_INPUT_BUFFER_COMPLEX InputBuffer;
ACPI_EVAL_OUTPUT_BUFFER OutputBuffer;
WDFIOTARGET IoTarget;
ULONG SizeReturned;
PAGED_CODE();
//
// Signature and Method name in reverse
//
InputBuffer.MethodNameAsUlong = (ULONG)('DTES');
InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE;
InputBuffer.ArgumentCount = 1;
InputBuffer.Size = InputBuffer.ArgumentCount * sizeof(ACPI_METHOD_ARGUMENT);
InputBuffer.Argument[0].Type = ACPI_METHOD_ARGUMENT_BUFFER;
InputBuffer.Argument[0].DataLength = 4;
memcpy_s(InputBuffer.Argument[0].Data, InputBuffer.Argument[0].DataLength,
pBuffer, InputBuffer.Argument[0].DataLength);
//
// Use following WDF method to initialize memory descriptor
// The memory descriptor is initialized with the input buffer we have defined.
//
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&InputDescriptor,
(PVOID)&InputBuffer,
sizeof(ACPI_EVAL_INPUT_BUFFER_COMPLEX));
RtlZeroMemory(&OutputBuffer, sizeof(OutputBuffer));
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&OutputDescriptor,
(PVOID)&OutputBuffer,
sizeof(ACPI_EVAL_OUTPUT_BUFFER));
//
// Get handle for underlying ACPI layer
//
IoTarget = WdfDeviceGetIoTarget(FxDevice);
//
// Send synchronous request
//
Status = WdfIoTargetSendIoctlSynchronously(IoTarget,
NULL,
IOCTL_ACPI_EVAL_METHOD,
&InputDescriptor,
&OutputDescriptor,
NULL,
&SizeReturned);
if (!NT_SUCCESS(Status))
{
goto exit;
}
else
{
//
// Verify output signature and length
//
if ( (OutputBuffer.Signature == ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE)
&& (OutputBuffer.Argument[0].Type == ACPI_METHOD_ARGUMENT_BUFFER))
{
Status = STATUS_SUCCESS;
}
else
{
Status = STATUS_ACPI_INVALID_DATA;
}
}
exit:
return Status;
}
关于ACPI Control Method更详细的用法,请查阅MSDN官方文档:Evaluating ACPI Control Methods Synchronously