#include "RBC9-SpaceNav.h"

#pragma PAGEDCODE
NTSTATUS RBC9_pnp_complete(IN PDEVICE_OBJECT pDev_obj, IN PIRP pIrp,IN PVOID ctxt)
{
	NTSTATUS			Status=STATUS_MORE_PROCESSING_REQUIRED;
	//DBG_ENTER(("%p, %p, %p", pDev_obj, pIrp, ctxt));
	KeSetEvent((PKEVENT)ctxt, IO_NO_INCREMENT, FALSE);

	if(pIrp->PendingReturned) {
		IoMarkIrpPending(pIrp);
	}

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

#pragma LOCKEDCODE
NTSTATUS RBC9_pnp_start(IN PDEVICE_OBJECT pDev_obj,	IN PIRP pIrp)
{
	NTSTATUS			Status;
	PDEVICE_EXTENSION	pDevExt;
	KEVENT				start_event;

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

	pDevExt = GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj);

	/* start lower device */
	KeInitializeEvent(&start_event, NotificationEvent, FALSE);

	IoCopyCurrentIrpStackLocationToNext(pIrp);
	IoSetCompletionRoutine(pIrp, RBC9_pnp_complete, &start_event, TRUE, TRUE, TRUE);

	Status = IoCallDriver(GET_LOWER_DEVICE_OBJECT(pDev_obj), pIrp);
	if(! NT_SUCCESS(Status)) 
	{
		DBG_LEAVE(("%x: IoCallDriver failed", Status));
		RBC9_COMPLETE_REQUEST(pIrp, 0, Status, IO_NO_INCREMENT);
		return Status;
	}

//	DBG_OUT(("waiting start event: %p", &start_event));
	KeWaitForSingleObject(&start_event, Executive, KernelMode, FALSE, NULL);

	Status = pIrp->IoStatus.Status;
	if(! NT_SUCCESS(Status)) 
	{
		DBG_LEAVE(("%x: calling next device failed", Status));
		RBC9_COMPLETE_REQUEST(pIrp, 0, Status, IO_NO_INCREMENT);
		return Status;
	}

	/* start this device */
	Status = RBC9_device_start(pDev_obj);
	if(! NT_SUCCESS(Status)) 
	{
		DBG_LEAVE(("%x: device_start failed", Status));
		RBC9_COMPLETE_REQUEST(pIrp, 0, Status, IO_NO_INCREMENT);
		return Status;
	}

	RBC9_COMPLETE_REQUEST(pIrp, 0, Status, IO_NO_INCREMENT);

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

NTSTATUS RBC9_device_start(IN PDEVICE_OBJECT pDev_obj)
{
	NTSTATUS			Status;
	PDEVICE_EXTENSION	pDevExt;

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


	pDevExt = GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj);

	/* initialize usb */
	Status = RBC9_usb_start(pDev_obj);
	if(! NT_SUCCESS(Status)) {
		DBG_LEAVE(("%x: usb_start failed", Status));
		return Status;
	}

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

NTSTATUS RBC9_pnp_stop(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);

	/* stop this device */
	pDevExt->Pnp_State = DEVICE_STATE_STOPPED;

	/* stop lower device */
	IoSkipCurrentIrpStackLocation(pIrp);
	Status = IoCallDriver(GET_LOWER_DEVICE_OBJECT(pDev_obj), pIrp);

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

	return Status;
}
NTSTATUS RBC9_pnp_surprise_remove(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);

	/* remove this device */
	pDevExt->Pnp_State = DEVICE_STATE_REMOVED;
	RBC9_device_remove(pDev_obj);

	/* remove lower device */
	pIrp->IoStatus.Status = STATUS_SUCCESS;
	IoSkipCurrentIrpStackLocation(pIrp);
	Status = IoCallDriver(GET_LOWER_DEVICE_OBJECT(pDev_obj), pIrp);

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

	return Status;
}
NTSTATUS RBC9_pnp_remove(IN PDEVICE_OBJECT pDev_obj,	IN PIRP pIrp)
{
	NTSTATUS			Status=STATUS_SUCCESS;
	PDEVICE_EXTENSION	pDevExt;

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

	pDevExt = GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj);

	/* remove this device */
	pDevExt->Pnp_State = DEVICE_STATE_REMOVED;
	RBC9_device_remove(pDev_obj);

	{
		LONG cnt;

		InterlockedDecrement(&pDevExt->RequestCount); /* irp_pnp's req */
		cnt = InterlockedDecrement(&pDevExt->RequestCount); /* device_add's req */
		if(cnt > 0) {
			KeWaitForSingleObject(&pDevExt->Remove_Event,Executive, KernelMode, FALSE, NULL);
		}
	}

	/* remove lower device */
	IoSkipCurrentIrpStackLocation(pIrp);
	IoCallDriver(GET_LOWER_DEVICE_OBJECT(pDev_obj), pIrp);

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

	return Status;
}

NTSTATUS RBC9_device_remove(IN PDEVICE_OBJECT pDev_obj)
{
	NTSTATUS			Status=STATUS_SUCCESS;
	PDEVICE_EXTENSION	pDevExt;

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

	pDevExt = GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj);

	/* finalize usb */
	RBC9_usb_remove(pDev_obj);

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

NTSTATUS RBC9_pnp_capabilities(IN PDEVICE_OBJECT pDev_obj,	IN PIRP pIrp)
{
	NTSTATUS				Status=STATUS_SUCCESS;
	PIO_STACK_LOCATION		pStack;
	PDEVICE_CAPABILITIES	dev_caps;

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

	pStack = IoGetCurrentIrpStackLocation(pIrp);

	dev_caps = pStack->Parameters.DeviceCapabilities.Capabilities;
	dev_caps->LockSupported = FALSE;
	dev_caps->EjectSupported = FALSE;
	dev_caps->Removable = TRUE;
	dev_caps->DockDevice = FALSE;
	dev_caps->UniqueID = FALSE;
	dev_caps->SurpriseRemovalOK = TRUE;
	dev_caps->SystemWake = PowerSystemUnspecified;
	dev_caps->DeviceWake = PowerDeviceUnspecified;

	RBC9_COMPLETE_REQUEST(pIrp, 0, STATUS_SUCCESS, IO_NO_INCREMENT);

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