
#include "RBC9-SpaceNav.h"
#pragma warning(disable : 4996)
#pragma PAGEDCODE

UNICODE_STRING			InitRegPath;

NTSTATUS DriverEntry(
					 IN OUT PDRIVER_OBJECT   DriverObject,
					 IN PUNICODE_STRING      RegistryPath
					 )
{	


	NTSTATUS Status = STATUS_UNSUCCESSFUL;
	HID_MINIDRIVER_REGISTRATION hidMinidriverRegistration;

	DBG_ENTER(("RBC9-SpaceNav"));


	DriverObject->MajorFunction[IRP_MJ_CREATE]					=	RBC9_DispatchCreateClose;
	DriverObject->MajorFunction[IRP_MJ_CLOSE]					=	RBC9_DispatchCreateClose;
	DriverObject->MajorFunction[IRP_MJ_PNP]						=	RBC9_DispatchPnp;
	DriverObject->MajorFunction[IRP_MJ_POWER]					=	RBC9_DispatchPower;
	DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]			=	RBC9_DispatchDevice;
	DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL]			=	RBC9_DispatchSystem;
	DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] =	RBC9_DispatchIntDevice;
	DriverObject->DriverUnload									=	RBC9_DriverUnload;
	DriverObject->DriverExtension->AddDevice					=	RBC9_AddDevice;


	/* register hid minidriver */
	RtlZeroMemory(&hidMinidriverRegistration, sizeof(hidMinidriverRegistration));
	hidMinidriverRegistration.Revision				=	HID_REVISION;
	hidMinidriverRegistration.DriverObject			=	DriverObject;
	hidMinidriverRegistration.RegistryPath			=	RegistryPath;
	hidMinidriverRegistration.DevicesArePolled		=	FALSE;
	hidMinidriverRegistration.DeviceExtensionSize	=	sizeof(DEVICE_EXTENSION);

	Status = HidRegisterMinidriver(&hidMinidriverRegistration);

	
	InitRegPath.Buffer = (PWSTR) ExAllocatePool(PagedPool, RegistryPath->Length + sizeof(WCHAR));
	InitRegPath.MaximumLength = RegistryPath->Length + sizeof(WCHAR);
	RtlCopyUnicodeString(&InitRegPath, RegistryPath);
	InitRegPath.Buffer[RegistryPath->Length/sizeof(WCHAR)] = 0;

	//	DBG_OUT(("Minidriver Registration %ws", RegistryPath->Buffer));

//	DBG_OUT(("Minidriver Registration BIS  %ws", InitRegPath.Buffer));


	DBG_LEAVE(("%X",Status));

	return Status;
}

#pragma PAGEDCODE
NTSTATUS RBC9_DispatchCreateClose(
								  IN PDEVICE_OBJECT		DeviceObject,
								  IN PIRP					Irp
								  )
{
	//DBG_ENTER(("RBC9_DispatchCreateClose"));
	RBC9_COMPLETE_REQUEST(Irp,0,STATUS_SUCCESS,IO_NO_INCREMENT);
	return STATUS_SUCCESS;
}

#pragma PAGEDCODE
VOID RBC9_DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
	//RBC9_Stop_Notify();

	DBG_ENTER((" ... and leave"));

	return;
}


#pragma PAGEDCODE
NTSTATUS RBC9_AddDevice(IN PDRIVER_OBJECT DriverObject,IN PDEVICE_OBJECT func_dev_obj)
{
	NTSTATUS			Status=STATUS_SUCCESS;
    PDEVICE_OBJECT		pDev_obj;
    PDEVICE_EXTENSION	pDevExt;

	DBG_ENTER(("%p, %p", DriverObject, func_dev_obj));	

    /* device object */
    pDev_obj = func_dev_obj;
    pDevExt = GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj);
    RtlZeroMemory(pDevExt, sizeof(DEVICE_EXTENSION));

    /* init devxce extension */
	pDevExt->RequestCount = 1 ;

	KeInitializeEvent(&pDevExt->Remove_Event, NotificationEvent, FALSE);

    KeInitializeSpinLock(&pDevExt->Pending_Read_irp_Lock);
    InitializeListHead(&pDevExt->Pending_Read_irp);

    pDevExt->Pnp_State = DEVICE_STATE_STARTING;
	pDevExt->Profil_First = NULL;
	pDevExt->Led = NULL;
	pDevExt->Reset_Input = TRUE;
	pDevExt->ModeConfig = FALSE;

	pDevExt->RegPath.Buffer = (PWSTR) ExAllocatePool(PagedPool, InitRegPath.Length + sizeof(WCHAR));
	pDevExt->RegPath.MaximumLength = InitRegPath.Length + sizeof(WCHAR);
	RtlCopyUnicodeString(&pDevExt->RegPath, &InitRegPath);
	pDevExt->RegPath.Buffer[InitRegPath.Length/sizeof(WCHAR)] = 0;

    pDev_obj->Flags &= ~DO_DEVICE_INITIALIZING;

//	DBG_OUT(("regpath %ws", pDevExt->RegPath.Buffer));

	DBG_LEAVE(("%x", Status));
    return Status;
}

#pragma PAGEDCODE
NTSTATUS RBC9_DispatchPnp(IN PDEVICE_OBJECT pDev_obj, IN PIRP pIrp)
{
	NTSTATUS			Status;
	PDEVICE_EXTENSION	pDevExt;
	PIO_STACK_LOCATION	pStack;
	UCHAR				minor_func;

	DBG_ENTER(("%p, %p", pDev_obj, pIrp));

	Status = STATUS_SUCCESS;

	pDevExt = GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj);
	pStack = IoGetCurrentIrpStackLocation(pIrp);
	minor_func = pStack->MinorFunction;

	Status = RBC9_IncRequestCount(pDevExt);
	if(! NT_SUCCESS(Status)) {
		RBC9_COMPLETE_REQUEST(pIrp, 0, Status, IO_NO_INCREMENT);
		DBG_LEAVE(("Error %x", Status));
		return Status;
	}


	//DBG_OUT(("pnp: minor func: %02x", minor_func));

	switch (minor_func)
	{
	case IRP_MN_START_DEVICE:
		{
			Status=RBC9_pnp_start(pDev_obj,pIrp);
			break;
		}

	case IRP_MN_STOP_DEVICE:
		{

			Status=RBC9_pnp_stop(pDev_obj,pIrp);
			break;
		}
	case IRP_MN_SURPRISE_REMOVAL:
		{
			Status=RBC9_pnp_surprise_remove(pDev_obj,pIrp);
			break;
		}
	case IRP_MN_REMOVE_DEVICE:
		{
			Status=RBC9_pnp_remove(pDev_obj,pIrp);
			break;
		}
	case IRP_MN_QUERY_CAPABILITIES:
		{
			Status=RBC9_pnp_capabilities(pDev_obj,pIrp);
			break;
		}
	default:
		{
			//DBG_OUT(("Irp %d %x not supported", minor_func,minor_func));

			IoSkipCurrentIrpStackLocation (pIrp);
			Status = IoCallDriver(GET_LOWER_DEVICE_OBJECT(pDev_obj), pIrp);
			break;
		}
	}

	if(minor_func != IRP_MN_REMOVE_DEVICE) {
		RBC9_DecRequestCount(pDevExt);
	}

	DBG_LEAVE(("%x", Status));
	return Status;
}



#pragma PAGEDCODE
NTSTATUS RBC9_DispatchPower(IN PDEVICE_OBJECT pDev_obj, IN PIRP pIrp)
{
	NTSTATUS			Status;
	PDEVICE_EXTENSION	pDevExt;
	PIO_STACK_LOCATION	pStack;

	//DBG_ENTER(("%p, %p", pDev_obj, pIrp));

	pDevExt = GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj);
	pStack = IoGetCurrentIrpStackLocation(pIrp);

	Status = RBC9_IncRequestCount(pDevExt);
	if(! NT_SUCCESS(Status)) {
		RBC9_COMPLETE_REQUEST(pIrp, 0, Status, IO_NO_INCREMENT);
		DBG_LEAVE(("Error %x", Status));
		return Status;
	}


/*	DBG_OUT(("power: minor func: %02x, %u, %u, %u, %u",
		pStack->MinorFunction,
		pStack->Parameters.Power.Type,
		pStack->Parameters.Power.State,
		pStack->Parameters.Power.ShutdownType,
		pStack->Parameters.Power.SystemContext));
*/
	if(pStack->MinorFunction == IRP_MN_SET_POWER &&
		pStack->Parameters.Power.Type == DevicePowerState) {
			if(pStack->Parameters.Power.State.DeviceState != PowerDeviceD0) {
				pDevExt->Pnp_State = DEVICE_STATE_STOPPED;
			}
			
			PoSetPowerState(pDev_obj, DevicePowerState,
				pStack->Parameters.Power.State);
	}

	IoSkipCurrentIrpStackLocation(pIrp);
	PoStartNextPowerIrp(pIrp);
	Status = PoCallDriver(GET_LOWER_DEVICE_OBJECT(pDev_obj), pIrp);

	RBC9_DecRequestCount(pDevExt);

	//DBG_LEAVE(("%x", Status));
	return Status;
}

#pragma PAGEDCODE
NTSTATUS RBC9_DispatchIntDevice(IN PDEVICE_OBJECT pDev_obj, IN PIRP pIrp)
{
	NTSTATUS			Status;
	PDEVICE_EXTENSION	pDevExt;

	DBG_ENTER(("%p, %p", pDev_obj, pIrp));

	pDevExt = GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj);

	pIrp->IoStatus.Information = 0;

	Status = RBC9_IncRequestCount(pDevExt);
	if(NT_SUCCESS(Status)) 
	{
		PIO_STACK_LOCATION pStack;
		ULONG ioctl_code;

		pStack = IoGetCurrentIrpStackLocation(pIrp);
		ioctl_code = pStack->Parameters.DeviceIoControl.IoControlCode;

		//DBG_OUT(("inter ioctl: code: %08x", ioctl_code));
		
		switch(ioctl_code)
		{
		case IOCTL_HID_GET_DEVICE_DESCRIPTOR:
			{
				//000b0003
				Status=RBC9_ioctl_get_dev_desc(pDev_obj,pIrp);
				break;
			}
		case IOCTL_HID_GET_DEVICE_ATTRIBUTES:
			{
				//000b0027
				Status=RBC9_ioctl_get_dev_attr(pDev_obj,pIrp);
				break;
			}
		case IOCTL_HID_GET_REPORT_DESCRIPTOR:
			{
				//000b0007
				Status=RBC9_ioctl_get_rep_desc(pDev_obj,pIrp);
				break;
			}
		case IOCTL_HID_READ_REPORT:
			{
				//000b000b
				Status=RBC9_ioctl_read_rep(pDev_obj,pIrp);
				break;
			}
		case IOCTL_HID_WRITE_REPORT:
			{
				
				Status=RBC9_ioctl_write_rep(pDev_obj,pIrp);
				break;
			}

		case IOCTL_HID_SET_OUTPUT_REPORT:
			{
			//	DBG_OUT(("IOCTL_HID_SET_OUTPUT_REPORT %x",ioctl_code));
				Status=RBC9_ioctl_write_rep(pDev_obj,pIrp);
				break;
			}
		case IOCTL_HID_SET_FEATURE:
			{
				//DBG_OUT(("IOCTL_HID_SET_FEATURE %x",ioctl_code));
				Status=RBC9_ioctl_write_rep(pDev_obj,pIrp);
				break;
			}
		default:
			{
			//	DBG_OUT(("inter ioctl: not supported %x",ioctl_code));
				Status=STATUS_NOT_SUPPORTED;
				RBC9_COMPLETE_REQUEST(pIrp, 0, Status, IO_NO_INCREMENT);
				break;
			}
		}

		RBC9_DecRequestCount(pDevExt);
	}

	DBG_LEAVE(("%x", Status));
	return Status;
}
#pragma PAGEDCODE
NTSTATUS RBC9_DispatchDevice(IN PDEVICE_OBJECT pDev_obj, IN PIRP pIrp)
{
	PDEVICE_EXTENSION pDevExt = GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj);
	NTSTATUS Status;
	PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
	//DBG_OUT(("RBC9_DispatchDevice - - - -"));
	IoSkipCurrentIrpStackLocation(pIrp);
	Status = IoCallDriver(GET_LOWER_DEVICE_OBJECT(pDev_obj), pIrp);
	return Status;
}

#pragma PAGEDCODE
NTSTATUS RBC9_DispatchSystem(IN PDEVICE_OBJECT pDev_obj, IN PIRP pIrp)
{
	PDEVICE_EXTENSION pDevExt = GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj);
	NTSTATUS Status;
	PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
	//DBG_OUT(("RBC9_DispatchSystem - - - -"));
	IoSkipCurrentIrpStackLocation(pIrp);
	Status = IoCallDriver(GET_LOWER_DEVICE_OBJECT(pDev_obj), pIrp);
	return Status;
}

#pragma LOCKEDCODE
NTSTATUS RBC9_IncRequestCount(IN PDEVICE_EXTENSION pDevExt)
{
	NTSTATUS Status;
	LONG cnt;

	//DBG_ENTER(("%p", pDevExt));

	cnt = InterlockedIncrement(&pDevExt->RequestCount);
	//DBG_OUT(("increment ref: %d", cnt));

	if(pDevExt->Pnp_State == DEVICE_STATE_REMOVED) {
		//DBG_OUT(("already removed"));

		cnt = InterlockedDecrement(&pDevExt->RequestCount);
		//DBG_OUT(("decrement ref: %d", cnt));
		if(cnt == 0) {
			KeSetEvent(&pDevExt->Remove_Event, IO_NO_INCREMENT, FALSE);
		}

		Status = STATUS_DELETE_PENDING;
	} else {
		Status = STATUS_SUCCESS;
	}

	//DBG_LEAVE(("%x", Status));
	return Status;
}


VOID RBC9_DecRequestCount(IN PDEVICE_EXTENSION pDevExt)
{
	LONG cnt;

	//DBG_ENTER(("%p", pDevExt));

	cnt = InterlockedDecrement(&pDevExt->RequestCount);
	//DBG_OUT(("decrement ref: %d", cnt));
	if(cnt == 0) {
		KeSetEvent(&pDevExt->Remove_Event, IO_NO_INCREMENT, FALSE);
	}
	//DBG_LEAVE(("void"));
}

