Windows Phone 8.1 驱动开发——GPIO Device

来源:互联网 发布:济南网站搜索优化 编辑:程序博客网 时间:2024/06/05 21:11

在上一节 Windows Phone 8.1 驱动开发——GPIO 简介 中,我们了解了Windows 8系统中GPIO驱动的大体架构,由于在工作中手机驱动开发人员很少涉及到GPIO Controller驱动的开发,该部分都由平台厂商开发完成,所以这里给大家讲解一下GPIO Peripheral Device Driver的开发步骤。


本文以微软官方提供的GPIO Sample为例进行讲解,你也可以到MSDN官网进行源码下载:GPIO Sample Drivers


电路示意图

根据该Sample Code,本人画了一张电路示意图,如下:



ACPI资源配置

编写驱动之前,我们首先看一下在ACPI配置表中,GPIO I/O Resource的分配:

       //       // Sample peripheral device       //       Device (DEV1) {         Name (_HID, "TEST0001")         Name (_CID, "TEST0001")         Name (_UID, 1)         Method (_CRS, 0x0, NotSerialized) {           Name (RBUF, ResourceTemplate () {             // GPIO Interrupt Resources             GpioInt(Edge, ActiveHigh, Shared, PullUp, 0, "\\_SB.GPIO", 0, ResourceConsumer,, RawDataBuffer() {1}) {1}                          // GPIO IO Resources             GpioIo(Exclusive, PullUp, 0, 0,, "\\_SB.GPIO",0, ResourceConsumer, , RawDataBuffer() {1}) {10}             GpioIo(Exclusive, PullUp, 0, 0,, "\\_SB.GPIO",0, ResourceConsumer, , RawDataBuffer() {1}) {11}           })           Return (RBUF)         }         Method (_STA, 0x0, NotSerialized) {             Return(0xf)         }       }

从资源表中可以看到,该设备DEV1使用了GPIO10、GPIO11和GPIO1三个引脚,其中GPIO10/11用作普通IO口,而GPIO1则用作中断输入引脚。至于这里对GPIO10/11的引脚配置到底作输入还是输出,是否上拉等,其实大家并不用关心,因为在电源管理中,也就是加载PEP模块的时候,会重新根据电源管理中的配置文件(pep_common.asl)进行相应设置,这里的GpioIo方法只是告诉Peripheral Driver该设备使用到了哪些I/O引脚。同样,GpioInt方法也只是告诉驱动当前设备使用到那个GPIO作外部中断引脚,至于具体的配置细节也是在电源管理配置文件中进行描述的。


获取I/O资源

当PnP Manager加载设备时,会在EvtDevicePrepareHardware回调函数中,去读取I/O资源:

NTSTATUSSampleDrvEvtDevicePrepareHardware (    _In_ WDFDEVICE Device,    _In_ WDFCMRESLIST ResourcesRaw,    _In_ WDFCMRESLIST ResourcesTranslated    ){    PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor;    PSAMPLE_DRV_DEVICE_EXTENSION SampleDrvExtension;    ULONG Index;    ULONG ResourceCount;    ...    SampleDrvExtension = SampleDrvGetDeviceExtension(Device);    //    // Walk through the resource list and map all the resources. Only two    // I/O resource and one interrupt is expected.    //    ResourceCount = WdfCmResourceListGetCount(ResourcesTranslated);    for (Index = 0; Index < ResourceCount; Index += 1) {        Descriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, Index);        switch(Descriptor->Type) {        //        // GPIO I/O descriptors        //        case CmResourceTypeConnection:            //            //  Check against expected connection type            //            if ((Descriptor->u.Connection.Class == CM_RESOURCE_CONNECTION_CLASS_GPIO) &&                (Descriptor->u.Connection.Type == CM_RESOURCE_CONNECTION_TYPE_GPIO_IO)) {                SampleDrvExtension->ConnectionIds[IoResourceIndex].LowPart = Descriptor->u.Connection.IdLowPart;                SampleDrvExtension->ConnectionIds[IoResourceIndex].HighPart = Descriptor->u.Connection.IdHighPart;            }            break;        //        //  Interrupt resource        //        case CmResourceTypeInterrupt:            SampleDrvExtension->InterruptCount++;        default:            break;        }    }        ...}

在以上代码中需要注意的是,函数WdfCmResourceListGetDescriptor()是按照ACPI资源表中的顺序依次读取的,所以这里SampleDrvExtension->ConnectionIds[0]对应GPIO10,SampleDrvExtension->ConnectionIds[1]对应GPIO11,顺序不能弄错了。


创建IO Target

当获取到I/O资源后,就可以使用该资源ID号创建一个IO Target来操作对应的GPIO口。这里对原始Sample Code做了点修改,增加了结构体SAMPLE_DRV_DEVICE_EXTENSION成员变量ReadIoTarget和WriteIoTarget,分别用于保存读、写IO TARGET的句柄。

创建一个读操作的IO Target:

NTSTATUS ReadIoTargetInitialize(WDFDEVICE Device){    NTSTATUS Status;    PSAMPLE_DRV_DEVICE_EXTENSION SampleDrvExtension;    UNICODE_STRING ReadString;    WCHAR ReadStringBuffer[100];    WDF_OBJECT_ATTRIBUTES ObjectAttributes;    WDF_IO_TARGET_OPEN_PARAMS OpenParams    ...    SampleDrvExtension = SampleDrvGetDeviceExtension(Device);    WDF_OBJECT_ATTRIBUTES_INIT(&ObjectAttributes);    ObjectAttributes.ParentObject = Device;    WdfIoTargetCreate(Device, &ObjectAttributes, &SampleDrvExtension->ReadIoTarget);    RtlInitEmptyUnicodeString(&ReadString, ReadStringBuffer, sizeof(ReadStringBuffer));    RESOURCE_HUB_CREATE_PATH_FROM_ID(&ReadString,                                     SampleDrvExtension->ConnectionIds[0].LowPart,                                     SampleDrvExtension->ConnectionIds[0].HighPart);    WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(&OpenParams, ReadString, FILE_GENERIC_READ);    WdfIoTargetOpen(SampleDrvExtension->ReadIoTarget, &OpenParams);    ...}



创建一个写操作的IO Target:

NTSTATUS WriteIoTargetInitialize(WDFDEVICE Device){    NTSTATUS Status;    PSAMPLE_DRV_DEVICE_EXTENSION SampleDrvExtension;    UNICODE_STRING WriteString;    WCHAR WriteStringBuffer[100];    WDF_OBJECT_ATTRIBUTES ObjectAttributes;    WDF_IO_TARGET_OPEN_PARAMS OpenParams    ...    SampleDrvExtension = SampleDrvGetDeviceExtension(Device);    WDF_OBJECT_ATTRIBUTES_INIT(&ObjectAttributes);    ObjectAttributes.ParentObject = Device;    WdfIoTargetCreate(Device, &ObjectAttributes, &SampleDrvExtension->WriteIoTarget);    RtlInitEmptyUnicodeString(&WriteString, WriteStringBuffer, sizeof(WriteStringBuffer));    RESOURCE_HUB_CREATE_PATH_FROM_ID(&WriteString,                                     SampleDrvExtension->ConnectionIds[1].LowPart,                                     SampleDrvExtension->ConnectionIds[1].HighPart);    WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(&OpenParams, WriteString, FILE_GENERIC_WRITE);    WdfIoTargetOpen(SampleDrvExtension->WriteIoTarget, &OpenParams);    ...}

发送IO请求

当创建并打开了一个IO TARGET后,就可以通过对该IO TARGET发送IOCTL请求来实现I/O引脚的读写操作。发送IOCTL_GPIO_READ_PINS进行读操作,发送IOCTL_GPIO_WRITE_PINS进行写操作。

NTSTATUSReadWriteGPIO (    _In_ WDFDEVICE Device,    _In_ BOOLEAN ReadOperation,    _Inout_ PUCHAR Data,    _In_ _In_range_(>, 0) ULONG Size,    ){    WDF_OBJECT_ATTRIBUTES RequestAttributes;    WDF_OBJECT_ATTRIBUTES Attributes;    WDFIOTARGET IoTarget;    WDFREQUEST IoctlRequest;    WDFMEMORY WdfMemory;    WDF_REQUEST_SEND_OPTIONS SendOptions;    PSAMPLE_DRV_DEVICE_EXTENSION SampleDrvExtension;    NTSTATUS Status;    SampleDrvExtension = SampleDrvGetDeviceExtension(Device);    if(ReadOperation != FALSE)         IoTarget = SampleDrvExtension->ReadIoTarget;    else        IoTarget = SampleDrvExtension->WriteIoTarget;    WDF_OBJECT_ATTRIBUTES_INIT(&RequestAttributes);        WdfRequestCreate(&RequestAttributes, IoTarget, &IoctlRequest);    WDF_OBJECT_ATTRIBUTES_INIT(&Attributes);        Attributes.ParentObject = IoctlRequest;        WdfMemoryCreatePreallocated(&Attributes, Data, Size, &WdfMemory);        if (ReadOperation != FALSE) {                WdfIoTargetFormatRequestForIoctl(IoTarget,                                                                                  IoctlRequest,                                                                                  IOCTL_GPIO_READ_PINS,                                                                                  NULL,                                                                                  0,                                                                                  WdfMemory,                                                                                  0);        } else {                WdfIoTargetFormatRequestForIoctl(IoTarget,                                                                                  IoctlRequest,                                         IOCTL_GPIO_WRITE_PINS,                                                                              WdfMemory,                                                                                  0,                                                                                  WdfMemory,                                                                                  0);        }        WDF_REQUEST_SEND_OPTIONS_INIT(&SendOptions, WDF_REQUEST_SEND_OPTION_SYNCHRONOUS);        WdfRequestSend(IoctlRequest, IoTarget, &SendOptions);            ...}

设备初始化

实现以上函数后,就可以对设备进行初始化了。一般设备的初始化操作在PnP Manager的回调函数EvtDeviceD0Entry中完成:

NTSTATUSSampleDrvEvtDeviceD0Entry (    _In_ WDFDEVICE Device,    _In_ WDF_POWER_DEVICE_STATE PreviousPowerState    ){    BYTE Data;    NTSTATUS Status;    ReadIoTargetInitialize(Device);    Data = 0x0;    Status = ReadWriteGPIO(Device, TRUE, &Data, sizeof(Data));    if (!NT_SUCCESS(Status)) {        goto Cleanup;    }    WriteIoTargetInitialize(Device);    Data = 0x1;    Status = ReadWriteGPIO(Device, FALSE, &Data, sizeof(Data));    if (!NT_SUCCESS(Status)) {        goto Cleanup;    }    ...}
至此,GPIO外设驱动就完成了。


关于GPIO外设驱动更多详细信息,请参考MSDN官方文档:Connecting a KMDF Driver to GPIO I/O Pins



0 0