標籤:windows phone 8 acpi wdf windows驅動開發
今天給大家講解一下,在KMDF(Kernel-Mode Driver Framework)中是如何調用ACPI配置表中使用者定義的Method。在ACPI中,凡是以底線開頭的Method(如_STA)都系統預定義的Method,它們都由Windows OS自己調用,其他使用者自訂Method則由驅動程式調用。
ACPI Method
首先我們來看一段在ACPI中定義的Method:
//Global BufferName(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‘)
關於該結構體更詳細的資訊,請查閱MSDN文檔:
ACPI_EVAL_INPUT_BUFFER structure
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賦值,設定輸入參數
關於該結構體更詳細的資訊,請查閱MSDN文檔:
ACPI_EVAL_INPUT_BUFFER_COMPLEX structure
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
Windows Phone 8.1 驅動開發——如何調用ACPI Method