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
- Windows Phone 8.1 驱动开发——GPIO Device
- Windows Phone 8.1 驱动开发——GPIO 简介
- Windows Phone 8.1 驱动开发——注册表read/write
- Windows Phone 8.1 驱动开发——SPB 简介
- Windows Phone 8.1 驱动开发——如何调用ACPI Method
- Windows Phone 8开发历程——Windows Phone简介
- linux驱动开发(一)—GPIO驱动框架
- linux驱动开发(一)—GPIO驱动框架
- NRF52832开发:GPIO驱动
- Windows Phone 8初学者开发—第3部分:编写第一个Windows Phone 8应用程序
- Windows驱动开发——WDM驱动
- Windows驱动开发——WDM驱动
- Windows CE GPIO/LED驱动
- Windows Phone 7 - 取得Device的NetworkInformation
- Windows Phone 7 开发探索笔记9——菜单栏
- Windows Phone 7 开发——独立存储
- Windows Phone开发学习之四——仿真器介绍
- Windows Phone开发学习之二——环境搭建
- fatal error LNK1169: 找到一个或多个多重定义的符号
- 链接库(sqlserver—>mysql)
- 使用常量数组简化算法
- DiskView:图形化显示每个文件在硬盘上的物理位置
- Apache之.htaccess备忘录(二)
- Windows Phone 8.1 驱动开发——GPIO Device
- 如何在JM8.6编码端提取QDCT?
- js 小数四舍五入保留位数
- POJ 1179 Polygon(环形区间DP)
- WeCenter 与 UCenter 对接
- 跨域访问
- PHP 中「自增、自减」运算引发的奇怪问题
- 黑莓硌手的Passport变圆了
- 常见加密算法浅析