上篇中,我们设备打开函数已经得到了我们PCIe设备的句柄了。接下来我们来看看,设备打开之后,上层软件是怎样利用该句柄实现对设备上具体寄存器的访问的。
1:寄存器写操作
上层应用程序写操作函数代码:
/********************************************************************/ /* Write register 32bits */ /********************************************************************/ DLLEXP int CCONV ClLib_RegWrite32( HANDLE hHandle, unsigned char bar, unsigned long offset, unsigned long data ) { PORT_ACCESS port; DWORD dwBytes; int status = RTN_OK; if( (hHandle == NULL) || (hHandle == INVALID_HANDLE_VALUE) || (bar >= MAX_PCI_BAR) ) { return (RTN_PRM_ERR); } memset(&port,0,sizeof(PORT_ACCESS)); port.bar = bar; port.offs = offset; port.u.ldata = data; if(!DeviceIoControl(hHandle, IOCTL_WRITE_BASE_ULONG, &port, sizeof(PORT_ACCESS), NULL, 0, &dwBytes, NULL)) { status = RTN_ERR; } return (status); }
BOOL WINAPI DeviceIoControl( __in HANDLE hDevice, __in DWORD dwIoControlCode, __in_opt LPVOID lpInBuffer, __in DWORD nInBufferSize, __out_opt LPVOID lpOutBuffer, __in DWORD nOutBufferSize, __out_opt LPDWORD lpBytesReturned, __inout_opt LPOVERLAPPED lpOverlapped );HANDLE hDevice: 设备句柄
DWORD dwIoControlCode: 控制代码
LPVOID lpInBuffer: 输入Buffer
DWORD nInBufferSize: 输入Buffer大小
LPVOID lpOutBuffer: 输出Buffer
DWORD nOutBufferSize:输出Buffer大小
LPDWORD lpBytesReturned: 返回的数据大小(存放于输出Buffer中)
LPOVERLAPPED lpOverlapped: 用于指定该I/O操作是异步还是同步。
本例中,我们的读写控制代码(上述函数的第二个参数)定义如下。
/* Used 32768-65535 */ #define FILE_DEVICE_DEMOPCI 530710 #define CODE_BASE 0x0A00 /* Control code definition */ #define IOCTL_WRITE_BASE_ULONG CTL_CODE(FILE_DEVICE_DEMOPCI, CODE_BASE+ 1 , METHOD_BUFFERED, FILE_WRITE_ACCESS) #define IOCTL_READ_BASE_ULONG CTL_CODE(FILE_DEVICE_DEMOPCI, CODE_BASE+ 2 , METHOD_BUFFERED, FILE_READ_ACCESS)
另外。我们还定义了一个PORT_ACCESS结构体来当做输入Buffer,由于写操作不涉及取回数据的问题,我们将输出Buffer和其大小分别生成NULL和0即可。下面是PORT_ACCESS结构体的详细定义:
/* For Single Access */ typedef struct _tagPortARG { unsigned char bar; /* Pci BaseAddress number */ unsigned long offs; /* offset */ union { unsigned long ldata; /* send/recv data buffer */ unsigned short sdata; /* send/recv data buffer */ unsigned char cdata; /* send/recv data buffer */ }u; } PORT_ACCESS ,*PPORT_ACCESS;该结构体中,我们把要操作的寄存器对应BAR空间编号以及偏移地址还有要写入的数据填入该结构中,然后调用DeviceIoControl函数,I/O管理器将创建一个主功能号为IRP_MJ_DEVICE_CONTROL的IRP包传递给我们底层的驱动程序,而驱动将会调用我们之前在DriverEntry函数里面已经注册好的派遣函数DEMOPciDevcieControl。
NTSTATUS DEMOPciDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP pIrp) { NTSTATUS status = STATUS_SUCCESS; PTSTDPCI_DEVICE_EXT pDevExt; PIO_STACK_LOCATION pIrpStack; void * pBuffer; ULONG Size; LARGE_INTEGER StartTime, EndTime, Freq; LONGLONG IntervelTime; pDevExt = (PDEMOPCI_DEVICE_EXT)DeviceObject->DeviceExtension; /* Flag setting when driver is being used */ DEMOPciRequestIncrement(pDevExt); if (!pDevExt->Started) { status = STATUS_DEVICE_NOT_READY; pIrp->IoStatus.Status = STATUS_DEVICE_NOT_READY; IoCompleteRequest( pIrp, IO_NO_INCREMENT ); } pIrpStack = IoGetCurrentIrpStackLocation( pIrp ); switch (pIrpStack->MajorFunction) { case IRP_MJ_DEVICE_CONTROL: switch (pIrpStack->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_WRITE_BASE_ULONG: status = DEMOPciWriteBaseUlong(pDevExt, pIrp); break; case IOCTL_READ_BASE_ULONG: status = DEMOPciReadBaseUlong(pDevExt, pIrp); break; case IOCTL_CMN_BUFF_ALLOC: status = DEMOPciCommonBufferAlloc(pDevExt, pIrp); break; case IOCTL_CMN_BUFF_FREE: status = DEMOPciCommonBufferFree(pDevExt, pIrp); break; .............. .............. .............. case IOCTL_CREATE_EVENT: status= DEMOPciCreateEvent(pDevExt, pIrp); break; case IOCTL_CLOSE_EVENT: status= DEMOPciCloseEvent(pDevExt, pIrp); break; default: status = STATUS_INVALID_PARAMETER; break; } break; default: status = STATUS_NOT_IMPLEMENTED; break; } break; default: status = STATUS_NOT_IMPLEMENTED; break; } pIrp->IoStatus.Status = status; if (status != STATUS_PENDING) { IoCompleteRequest(pIrp, IO_NO_INCREMENT); /* Flag release when driver is being used */ DEMOPciRequestDecrement(pDevExt); } return (status); }
NTSTATUS DEMOPciWriteBaseUlong( PDEMOPCI_DEVICE_EXT pDevExt, PIRP pIrp ) { UCHAR bar; NTSTATUS status = STATUS_SUCCESS; PIO_STACK_LOCATION pIrpStack; PORT_ACCESS *pBuffer; ULONG ulInBufferSize, ulOutBufferSize; PULONG pulIoAddr; pIrpStack = IoGetCurrentIrpStackLocation(pIrp); ulInBufferSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength; pBuffer = (PORT_ACCESS *)pIrp->AssociatedIrp.SystemBuffer; bar = pBuffer->bar; if(bar < 6) { if( pDevExt->base[bar].WhichMapped == TYPE_MEM ) { DebugPrint("TYPE_MEM\n"); if( ( (pBuffer->offs) + sizeof(ULONG) ) > ( pDevExt->base[bar].MemorySize ) ) { status=STATUS_INVALID_PARAMETER; } else { DebugPrint("base[%d].MemoryMappedAddress+(pBuffer->offs): %x\n",bar, ((PUCHAR)pDevExt->base[bar].MemoryMappedAddress+(pBuffer->offs))); DebugPrint("pBuffer->u.ldata: %x\n",pBuffer->u.ldata); WRITE_REGISTER_ULONG((PULONG)((PUCHAR)pDevExt->base[bar].MemoryMappedAddress+(pBuffer->offs)), pBuffer->u.ldata); } } else if( pDevExt->base[bar].WhichMapped == TYPE_IO ) { DebugPrint("TYPE_IO\n"); if( ( (pBuffer->offs) + sizeof(ULONG) ) > ( pDevExt->base[bar].IoPortSize ) ) { status=STATUS_INVALID_PARAMETER; } else { DebugPrint("base[%d].IoPortMappedAddress+(pBuffer->offs): %x\n",bar, ((PUCHAR)pDevExt->base[bar].IoPortMappedAddress+(pBuffer->offs))); DebugPrint("pBuffer->u.ldata: %x\n",pBuffer->u.ldata); WRITE_PORT_ULONG((PULONG)((PUCHAR)pDevExt->base[bar].IoPortMappedAddress+(pBuffer->offs)), pBuffer->u.ldata); } } else { status=STATUS_UNSUCCESSFUL; } } else { status=STATUS_INVALID_PARAMETER; } if(status == STATUS_SUCCESS) { pIrp->IoStatus.Information = 4; } else { pIrp->IoStatus.Information = 0; } return (status); }
上述代码中,函数通过解析IRP包中的信息,得到要操作的寄存器的bar空间编号,偏移地址,以及要写入的数据等信息,然后调用系统函数WRITE_REGISTER_ULONG完成对寄存器的写操作,操作成功之后,该函数通过设置pIrp->IoStatus.Information = 4 来表明此次IRP操作的字节数,然后返回状态给DEMOPciDevcieControl函数,由DEMOPciDevcieControl函数调用IoCompleteRequest函数来完成本次IRP操作,最后I/O控制器将结果返回给上层函数,至此,寄存器的写操作已经成功完成了。
2:寄存器读操作
知道了写操作的流程,读操作就依葫芦画瓢,非常简单了。在此我们就不详细叙述。代码就是最好的老师,我们直接展示。
上层寄存器读操作函数如下:
/********************************************************************/ /* Read register 32bits */ /********************************************************************/ DLLEXP int CCONV ClLib_RegRead32( HANDLE hHandle, unsigned char bar, unsigned long offset, unsigned long *data ) { PORT_ACCESS port; DWORD dwBytes; int status = RTN_OK; if( (hHandle == NULL) || (hHandle == INVALID_HANDLE_VALUE) || (bar >= MAX_PCI_BAR) ) { return (RTN_PRM_ERR); } memset(&port,0,sizeof(PORT_ACCESS)); port.bar = bar; port.offs = offset; if(!DeviceIoControl(hHandle, IOCTL_READ_BASE_ULONG, &port, sizeof(PORT_ACCESS), &port, sizeof(PORT_ACCESS), &dwBytes, NULL)) { status = RTN_ERR; } *data = port.u.ldata; return (status); }
NTSTATUS DEMOPciReadBaseUlong( PDEMOPCI_DEVICE_EXT pDevExt, PIRP pIrp ) { NTSTATUS status = STATUS_SUCCESS; PIO_STACK_LOCATION pIrpStack; PORT_ACCESS *pBuffer; ULONG ulInBufferSize, ulOutBufferSize; pIrpStack = IoGetCurrentIrpStackLocation(pIrp); ulInBufferSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength; ulOutBufferSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength; pBuffer = (PORT_ACCESS *)pIrp->AssociatedIrp.SystemBuffer; bar = pBuffer->bar; if(bar < 6) { pBuffer->u.ldata=0; if( pDevExt->base[bar].WhichMapped == TYPE_MEM ) { DebugPrint("TYPE_MEM\n"); if( ( (pBuffer->offs) + sizeof(ULONG) ) > ( pDevExt->base[bar].MemorySize ) ) { status=STATUS_INVALID_PARAMETER; } else { DebugPrint("base[%d].MemoryMappedAddress+(pBuffer->offs): %x\n",bar, ((PUCHAR)pDevExt->base[bar].MemoryMappedAddress+(pBuffer->offs))); DebugPrint("pBuffer->u.ldata: %x\n",pBuffer->u.ldata); DebugPrint("pDevExt->base[bar].MemorySize = %lx\n", pDevExt->base[bar].MemorySize); pBuffer->u.ldata = READ_REGISTER_ULONG( (PULONG)((PUCHAR)pDevExt->base[bar].MemoryMappedAddress+(pBuffer->offs)) ); } } else if( pDevExt->base[bar].WhichMapped == TYPE_IO ) { DebugPrint("TYPE_IO\n"); if( ( (pBuffer->offs) + sizeof(ULONG) ) > ( pDevExt->base[bar].IoPortSize ) ) { status=STATUS_INVALID_PARAMETER; } else { DebugPrint("base[%d].IoPortMappedAddress+(pBuffer->offs): %x\n",bar, ((PUCHAR)pDevExt->base[bar].IoPortMappedAddress+(pBuffer->offs))); DebugPrint("pBuffer->u.ldata: %x\n",pBuffer->u.ldata); pBuffer->u.ldata = READ_PORT_ULONG( (PULONG)((PUCHAR)pDevExt->base[bar].IoPortMappedAddress+(pBuffer->offs)) ); } } else { status=STATUS_UNSUCCESSFUL; } } else { status=STATUS_INVALID_PARAMETER; } if(status == STATUS_SUCCESS) pIrp->IoStatus.Information = sizeof(PORT_ACCESS); else pIrp->IoStatus.Information = 0; return (status); }