PCIe设备漫游记----寄存器读写篇

来源:互联网 发布:小米电视接网络机顶盒 编辑:程序博客网 时间:2024/05/17 03:50

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

函数中,第一个参数hHandle就是我们上篇中通过设备打开函数得到的设备句柄,参数bar 和 offset 分别指定寄存器所处的BAR空间编号以及对应的偏移地址。代码中重点就是DeviceIoControl函数。下面我们来详细探讨。
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_ULONGCTL_CODE(FILE_DEVICE_DEMOPCI, CODE_BASE+ 1 , METHOD_BUFFERED, FILE_WRITE_ACCESS)#define IOCTL_READ_BASE_ULONGCTL_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){NTSTATUSstatus = STATUS_SUCCESS;PTSTDPCI_DEVICE_EXTpDevExt;PIO_STACK_LOCATIONpIrpStack;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);}

我们看到在DEMOPciDevcieControl函数中,根据不同的设备控制码,将执行不同的I/O操作(如DMA Buffer空间的申请与释放,事件创建与关闭等等)。本例中我们只讨论寄存器读写函数的具体实现。
NTSTATUS DEMOPciWriteBaseUlong( PDEMOPCI_DEVICE_EXTpDevExt, PIRP pIrp ) {UCHAR                 bar;NTSTATUS        status = STATUS_SUCCESS;PIO_STACK_LOCATIONpIrpStack;PORT_ACCESS*pBuffer;ULONG        ulInBufferSize, ulOutBufferSize;PULONGpulIoAddr;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 ) {NTSTATUSstatus = STATUS_SUCCESS;PIO_STACK_LOCATIONpIrpStack;PORT_ACCESS*pBuffer;ULONGulInBufferSize, 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);elsepIrp->IoStatus.Information = 0;return (status);}


原创粉丝点击