上篇中,我们设备打开函数已经得到了我们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);
}