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);}
- PCIe设备漫游记----寄存器读写篇
- PCIe设备漫游记----BIOS篇
- PCIe设备漫游记----驱动加载篇
- PCIe设备漫游记----BIOS篇
- PCIe设备漫游记----BIOS篇
- PCIe设备漫游记----BIOS篇
- PCIe设备漫游记----驱动加载篇
- PCIe设备漫游记----设备打开/关闭篇
- PCIe驱动开发-寄存器读写
- 应用程序实现读写PCIE设备配置空间
- PCIe配置空间和PCI设备中的寄存器
- PCIe配置空间和PCI设备中的寄存器
- 应用层读写i2c从设备寄存器
- PCI Express设备驱动 (4,PCIe配置空间和PCI设备中的寄存器)
- 字符设备驱动高级篇6——内核提供的读写寄存器接口
- 读写某个设备上的寄存器的过程
- WDM驱动程序开发之读写设备寄存器:KIoRange类
- PCIe设备,功能,总线
- Android应用开发中如何进行单元测试
- malloc()与 alloc()等内存分配方式
- POJ 3233
- ubuntu安装到u盘
- JavaScript 写一段最短的代码,用上js所有关键字
- PCIe设备漫游记----寄存器读写篇
- equals()方法必须具备三个著名的属性
- Activity四种启动模式(launchMode)
- (引用)类方法与实例方法的区别
- Android 中各种权限深入体验及详解
- 关于JNI回调JAVA方法及其注意事项
- 又一开源操作系统 -----Tizen
- 你不知道的手动变速箱:换挡杆下的玄机
- [javase] java中值传递还是引用传递的较好说明