PCIe 장치 로밍 레지스터 읽기 편

10944 단어
전편에서, 우리 장치 열기 함수는 이미 우리 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: 반환된 데이터 크기(출력 버퍼에 저장)
LPOVERLAPPED lpOverlapped: 비동기식 또는 동기식 입출력을 지정할 수 있습니다.
이 예에서 우리의 읽기와 쓰기 제어 코드(상기 함수의 두 번째 매개 변수)는 다음과 같이 정의되었다.
/* 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 구조체는 입력 버퍼로 사용되며, 쓰기 작업은 데이터를 되찾는 문제와 관련이 없기 때문에, 출력 버퍼와 크기는 각각 NULL과 0을 생성하면 됩니다.다음은 PORTACCESS 구조체에 대한 자세한 정의:
/* 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 패키지는 기본 드라이버에 전달되며, 드라이버는 드라이버 엔트리 함수에 등록된 파견 함수인 DEMOPCi DevcieControl을 호출합니다.
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);
}

DEMOPCiDevcieControl 함수에서 서로 다른 장치 제어 코드에 따라 서로 다른 입출력 작업(예를 들어 DMA 버퍼 공간의 신청과 방출, 이벤트 생성과 닫기 등)을 수행하는 것을 보았습니다.이 예에서 우리는 레지스터 읽기 함수의 구체적인 실현만을 토론할 뿐이다.
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
");  if( ( (pBuffer->offs) + sizeof(ULONG) ) > ( pDevExt->base[bar].MemorySize ) ) { status=STATUS_INVALID_PARAMETER; } else {  DebugPrint("base[%d].MemoryMappedAddress+(pBuffer->offs): %x
",bar, ((PUCHAR)pDevExt->base[bar].MemoryMappedAddress+(pBuffer->offs))); DebugPrint("pBuffer->u.ldata: %x
",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
"); if( ( (pBuffer->offs) + sizeof(ULONG) ) > ( pDevExt->base[bar].IoPortSize ) ) { status=STATUS_INVALID_PARAMETER; } else { DebugPrint("base[%d].IoPortMappedAddress+(pBuffer->offs): %x
",bar, ((PUCHAR)pDevExt->base[bar].IoPortMappedAddress+(pBuffer->offs))); DebugPrint("pBuffer->u.ldata: %x
",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
"); if( ( (pBuffer->offs) + sizeof(ULONG) ) > ( pDevExt->base[bar].MemorySize ) ) { status=STATUS_INVALID_PARAMETER; } else { DebugPrint("base[%d].MemoryMappedAddress+(pBuffer->offs): %x
",bar, ((PUCHAR)pDevExt->base[bar].MemoryMappedAddress+(pBuffer->offs))); DebugPrint("pBuffer->u.ldata: %x
",pBuffer->u.ldata); DebugPrint("pDevExt->base[bar].MemorySize = %lx
", 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
"); if( ( (pBuffer->offs) + sizeof(ULONG) ) > ( pDevExt->base[bar].IoPortSize ) ) { status=STATUS_INVALID_PARAMETER; } else { DebugPrint("base[%d].IoPortMappedAddress+(pBuffer->offs): %x
",bar, ((PUCHAR)pDevExt->base[bar].IoPortMappedAddress+(pBuffer->offs))); DebugPrint("pBuffer->u.ldata: %x
",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); }

좋은 웹페이지 즐겨찾기