#include "RBC9-SpaceNav.h"

static UCHAR My_hid_report_descriptor_base[] = {


	/* joystick */
	0x05, 0x01,                 /* Usage Page(Generic Desktop) */
	0x09, 0x04,                 /* Usage(Joystick) */
	0xa1, 0x01,                 /* Collection(Application) */

	0x85, REPORT_ID_JOY,                 /*   Report ID() */

	/* x, y */
	0x05, 0x01,                 /*   Usage Page(Generic Desktop) */
	0x09, 0x01,                 /*   Usage(Pointer) */

	0xa1, 0x00,                 /*   Collection(Physical) */
	0x15, 0x00,                 /*     Logical Minimum(00) */
	//	0x26, 0x20, 0x03,           /*     Logical Maximum(400) */
	0x26, 0xD0, 0x02,           /*     Logical Maximum(2*360) */
	0x75, 0x10,                 /*     Report Size(16) */
	0x95, 0x02,                 /*     Report Count(2) */
	0x05, 0x01,                 /*     Usage Page(Generic Desktop) */
	0x09, 0x30,                 /*     Usage(X) */
	0x09, 0x31,                 /*     Usage(Y) */
	0x81, 0x02,                 /*     Input(Variable, Absolute) */
	0xc0,                       /*   End Collection */

	/* z */
	0x15, 0x00,                 /*     Logical Minimum(00) */
	0x26, 0xD0, 0x02,           /*     Logical Maximum(2*360) */
	0x75, 0x10,                 /*   Report Size(16) */
	0x95, 0x01,                 /*   Report Count(1) */
	0x05, 0x01,                 /*   Usage Page(Generic Desktop) */
	0x09, 0x32,                 /*   Usage(Z) */
	0x81, 0x02,                 /*   Input(Variable, Absolute) */

	/* rx, ry */
	0x05, 0x01,                 /*   Usage Page(Generic Desktop) */
	0x09, 0x01,                 /*   Usage(Pointer) */
	0xa1, 0x00,                 /*   Collection(Physical) */
	0x15, 0x00,                 /*     Logical Minimum(00) */
	0x26, 0xD0, 0x02,           /*     Logical Maximum(2*360) */
	0x75, 0x10,                 /*     Report Size(16) */
	0x95, 0x02,                 /*     Report Count(2) */
	0x05, 0x01,                 /*     Usage Page(Generic Desktop) */
	0x09, 0x33,                 /*     Usage(Rx) */
	0x09, 0x34,                 /*     Usage(Ry) */
	0x81, 0x02,                 /*     Input(Variable, Absolute) */
	0xc0,                       /*   End Collection */

	/* rz */
	0x15, 0x00,                 /*     Logical Minimum(00) */
	0x26, 0xD0, 0x02,           /*     Logical Maximum(2*360) */
	0x75, 0x10,                 /*   Report Size(16) */
	0x95, 0x01,                 /*   Report Count(1) */
	0x05, 0x01,                 /*   Usage Page(Generic Desktop) */
	0x09, 0x35,                 /*   Usage(Rz) */
	0x81, 0x02,                 /*   Input(Variable, Absolute) */

	/* slider */
	0x75, 0x10,                 /*   Report Size(16) */
	0x95, 0x01,                 /*   Report Count(1) */
	0x15, 0x00,                 /*     Logical Minimum(00) */
	0x26, 0xD0, 0x02,           /*     Logical Maximum(2*360) */
	0x05, 0x01,                 /*   Usage Page(Generic Desktop) */
	0x09, 0x36,                 /*   Usage(Slider) */
	0x81, 0x02,                 /*   Input(Variable, Absolute) */

	/* slider Bis */
	0x75, 0x10,                 /*   Report Size(16) */
	0x95, 0x01,                 /*   Report Count(1) */
	0x15, 0x00,                 /*     Logical Minimum(00) */
	0x26, 0xD0, 0x02,           /*     Logical Maximum(2*360) */
	0x05, 0x01,                 /*   Usage Page(Generic Desktop) */
	0x09, 0x36,                 /*   Usage(Slider) */
	0x81, 0x02,                 /*   Input(Variable, Absolute) */

	/* throttle */
	//0x75, 0x10,                 /*   Report Size(16) */
//	0x95, 0x01,                 /*   Report Count(1) */
//	0x15, 0x00,                 /*     Logical Minimum(00) */
//	0x26, 0xD0, 0x02,           /*     Logical Maximum(2*360) */
//	0x05, 0x02,                 /*   Usage Page(Simulation) */
//	0x09, 0xbb,                 /*   Usage(Throttle) */
//	0x81, 0x02,                 /*   Input(Variable, Absolute) */



	/* hat switch 1 */
	0x75, 0x04,                 /*   Report Size(4) */
	0x95, 0x01,                 /*   Report Count(1) */
	0x15, 0x00,                 /*   Logical Minimum(0x00) */
	0x25, 0x07,                 /*   Logical Maximum(0x07) */
	0x35, 0x00,                 /*   Physical Minimum(0x00) */
	0x46, 0x3b, 0x01,           /*   Physical Maximum(0x013b) */
	0x65, 0x14,                 /*   Unit(Degree) */
	0x05, 0x01,                 /*   Usage Page(Generic Desktop) */
	0x09, 0x39,                 /*   Usage(Hat Switch) */
	0x81, 0x42,                 /*   Input(Variable, Absolute, Null State) */

	/*!
	4 bits pour aligner
	*/
	0x75, 0x04,					//     REPORT_SIZE (4)
	0x95, 0x01,					//     REPORT_COUNT (1)
	0x81, 0x01,					//     INPUT (Cnst,Ary,Abs)


	/* button x 8 */
	0x75, 0x01,                 /*   Report Size(1) */
	0x95, 0x08,                 /*   Report Count(8) */
	0x15, 0x00,                 /*   Logical Minimum(0x00) */
	0x25, 0x01,                 /*   Logical Maximum(0x01) */
	0x35, 0x00,                 /*   Physical Minimum(0x00) */
	0x45, 0x01,                 /*   Physical Maximum(0x01) */
	0x05, 0x09,                 /*   Usage Page(Button) */
	0x19, 0x01,                 /*   Usage Minimum(Button 1) */
	0x29, 0x08,                 /*   Usage Maximum(Button 8) */
	0x81, 0x02,                 /*   Input(Variable, Absolute) */
	0xc0,                       /* End Collection */

	/* mouse */
	0x05, 0x01,                 /* Usage Page(Generic Desktop) */
	0x09, 0x02,                 /* Usage(Mouse) */
	0xa1, 0x01,                 /* Collection(Application) */

	0x85, REPORT_ID_MOU,                 /*   Report ID() */

	/* x, y */
	0x05, 0x01,                 /*   Usage Page(Generic Desktop) */
	0x09, 0x01,                 /*   Usage(Pointer) */
	0xa1, 0x00,                 /*   Collection(Physical) */
	0x15, 0x81,                 /*     Logical Minimum(-0x7f) */
	0x25, 0x7f,                 /*     Logical Maximum(+0x7f) */
	//0x35, 0xc0,                 /*     Physical Minimum(-0x40) */
	//0x45, 0x40,                 /*     Physical Maximum(+0x40) */
	0x35, 0x00,                 /*   Physical Minimum(0x00) */
	0x45, 0x00,                 /*   Physical Maximum(0x00) */

	0x75, 0x08,                 /*     Report Size(8) */
	0x95, 0x02,                 /*     Report Count(2) */
	0x05, 0x01,                 /*     Usage Page(Generic Desktop) */
	0x09, 0x30,                 /*     Usage(X) */
	0x09, 0x31,                 /*     Usage(Y) */
	0x81, 0x06,                 /*     Input(Variable, Relative) */
	0xc0,                       /*   End Collection */

	/* wheel */
	0x15, 0x81,                 /*   Logical Minimum(-0x7f) */
	0x25, 0x7f,                 /*   Logical Maximum(+0x7f) */
	0x35, 0x00,                 /*   Physical Minimum(0x00) */
	0x45, 0x00,                 /*   Physical Maximum(0x00) */
	0x75, 0x08,                 /*   Report Size(8) */
	0x95, 0x01,                 /*   Report Count(1) */
	0x05, 0x01,                 /*   Usage Page(Generic Desktop) */
	0x09, 0x38,                 /*   Usage(Wheel) */
	0x81, 0x06,                 /*   Input(Variable, Relative) */

	/* button x 5 */
	0x75, 0x01,                 /*   Report Size(1) */
	0x95, 0x05,                 /*   Report Count(5) */
	0x15, 0x00,                 /*   Logical Minimum(0x00) */
	0x25, 0x01,                 /*   Logical Maximum(0x01) */
	0x35, 0x00,                 /*   Physical Minimum(0x00) */
	0x45, 0x01,                 /*   Physical Maximum(0x01) */
	0x05, 0x09,                 /*   Usage Page(Button) */
	0x19, 0x01,                 /*   Usage Minimum(Button 1) */
	0x29, 0x05,                 /*   Usage Maximum(Button 5) */
	0x81, 0x02,                 /*   Input(Variable, Absolute) */

	/* padding (3bits) */
	0x75, 0x01,                 /*   Report Size(1) */
	0x95, 0x03,                 /*   Report Count(3) */
	0x81, 0x01,                 /*   Input(Constant) */

	0xc0 ,                      /* End Collection */



	// keyboard 
	0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x06,                    // USAGE (Keyboard)
    0xa1, 0x01,                    // COLLECTION (Application)

	0x85, REPORT_ID_KEY,		   //   REPORT_ID 

	0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
    0x19, 0xe0,                    //   USAGE_MINIMUM (Keyboard LeftControl)
    0x29, 0xe7,                    //   USAGE_MAXIMUM (Keyboard Right GUI)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x75, 0x01,                    //   REPORT_SIZE (1)
    0x95, 0x08,                    //   REPORT_COUNT (8)
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)

    0x95, 0x06,                    //   REPORT_COUNT (6)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x65,                    //   LOGICAL_MAXIMUM (101)
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
    0x19, 0x00,                    //   USAGE_MINIMUM (Reserved (no event indicated))
    0x29, 0x65,                    //   USAGE_MAXIMUM (Keyboard Application)
    0x81, 0x00,                    //   INPUT (Data,Ary,Abs)

	0xc0,                          // END_COLLECTION


// Key Special Key

	0x05, 0x0c,                    // USAGE_PAGE (Consumer Devices)
    0x09, 0x01,                    // USAGE (Consumer Control)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x85, REPORT_ID_WWWKEY,        //   REPORT_ID ()
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x75, 0x01,                    //   REPORT_SIZE (1)
    0x95, 0x10,                    //   REPORT_COUNT (16)
    0x09, 0xe2,                    //   USAGE (Mute) 0x01
    0x09, 0xe9,                    //   USAGE (Volume Up) 0x02
    0x09, 0xea,                    //   USAGE (Volume Down) 0x03
    0x09, 0xcd,                    //   USAGE (Play/Pause) 0x04
    0x09, 0xb7,                    //   USAGE (Stop) 0x05
    0x09, 0xb6,                    //   USAGE (Scan Previous Track) 0x06 
    0x09, 0xb5,                    //   USAGE (Scan Next Track) 0x07
    0x0a, 0x8a, 0x01,              //   USAGE (Mail) 0x08 
    0x0a, 0x92, 0x01,              //   USAGE (Calculator) 0x09 
    0x0a, 0x21, 0x02,              //   USAGE (www search) 0x0a
    0x0a, 0x23, 0x02,              //   USAGE (www home) 0x0b
    0x0a, 0x2a, 0x02,              //   USAGE (www favorites) 0x0c
	0x0a, 0x27, 0x02,              //   USAGE (www refresh) 0x0d
	0x0a, 0x26, 0x02,              //   USAGE (www stop) 0x0e
	0x0a, 0x25, 0x02,              //   USAGE (www forward) 0x0f 
	0x0a, 0x24, 0x02,              //   USAGE (www back) 0x10
    0x81, 0x62,                    //   INPUT (Data,Var,Abs,NPrf,Null)

// Ajout pour comm
	/* Output Report Command */
	0xa1, 0x02,                    //   COLLECTION (Logical)
    0x85, REPORT_ID_CMD,           //   REPORT_ID ()
	0x75, 0x08,						/*   Report Size(8) */
	0x95, 0x03,						/*   Report Count(3) */
	0x15, 0x00,						/*   Logical Minimum(0x00) */
	0x26, 0xff, 0x00,				/*   Logical Maximum(0x00ff) */
	0x06, 0x00, 0xff,				/*   Usage Page(Vendor defined) */
	0x09, 0x01,						/*   Usage(Vendor defined 1) */
	0x91, 0x02,						/*   Output(Variable, Absolute) */
    0xc0,                           //   END_COLLECTION

	/* Input Report */
    0xa1, 0x02,						//   COLLECTION (Logical)
    0x85, REPORT_ID_CMD,			//     REPORT_ID ()
  	0x05, 0x01,						//     USAGE_PAGE (Generic Desktop)
	0x09, 0x3A,						//     USAGE (Counted Buffer)
	0x75, 0x08,						//     REPORT_SIZE (8)
	0x95, 0x03,						//     REPORT_COUNT ()
	0x81, 0x02,						//     INPUT (Data,Var,Abs)
	0xc0,							//   END_COLLECTION

    0xc0,                           //               END_COLLECTION

};

static UCHAR My_hid_report_descriptor_Navigator[] = {
		/* Space Nav */

	0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x08,                    // USAGE (Undefined)
    0xa1, 0x01,                    // COLLECTION (Application)

    0xa1, 0x00,                    //   COLLECTION (Physical)
    0x85, 0x01,                    //     REPORT_ID (1)
    0x16, 0x0c, 0xfe,              //     LOGICAL_MINIMUM (-500)
    0x26, 0xf4, 0x01,              //     LOGICAL_MAXIMUM (500)
    0x36, 0x00, 0x80,              //     PHYSICAL_MINIMUM (-32768)
    0x46, 0xff, 0x7f,              //     PHYSICAL_MAXIMUM (32767)
   // 0x09, 0x30,                    //     USAGE (X)
   // 0x09, 0x31,                    //     USAGE (Y)
    0x09, 0x32,                    //     USAGE (Z) ----> tricherie pour ne pas voir ce "joystick"
	0x09, 0x32,                    //     USAGE (Z) ----> tricherie pour ne pas voir ce "joystick"
	0x09, 0x32,                    //     USAGE (Z)
    0x75, 0x10,                    //     REPORT_SIZE (16)
    0x95, 0x03,                    //     REPORT_COUNT (3)
    0x81, 0x06,                    //     INPUT (Data,Var,Rel)
    0xc0,                          //   END_COLLECTION

    0xa1, 0x00,                    //   COLLECTION (Physical)
    0x85, 0x02,                    //     REPORT_ID (2)
    0x09, 0x33,                    //     USAGE (Rx)
    0x09, 0x34,                    //     USAGE (Ry)
    0x09, 0x35,                    //     USAGE (Rz)
    0x75, 0x10,                    //     REPORT_SIZE (16)
    0x95, 0x03,                    //     REPORT_COUNT (3)
    0x81, 0x06,                    //     INPUT (Data,Var,Rel)
    0xc0,                          //   END_COLLECTION

    0xa1, 0x02,                    //   COLLECTION (Logical)
    0x85, 0x03,                    //     REPORT_ID (3)
    0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
    0x05, 0x09,                    //     USAGE_PAGE (Button)
    0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
    0x29, 0x15,                    //     USAGE_MAXIMUM (Button 2)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
    0x35, 0x00,                    //     PHYSICAL_MINIMUM (0)
    0x45, 0x01,                    //     PHYSICAL_MAXIMUM (1)
    0x75, 0x01,                    //     REPORT_SIZE (1)
    0x95, 0x15,                    //     REPORT_COUNT (21)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0x95, 0x03,                    //     REPORT_COUNT (3)
    0x81, 0x03,                    //     INPUT (Cnst,Var,Abs)

    0xc0,                          //   END_COLLECTION

    0xa1, 0x02,                    //   COLLECTION (Logical)
    0x85, 0x04,                    //     REPORT_ID (4)
    0x05, 0x08,                    //     USAGE_PAGE (LEDs)
    0x09, 0x4b,                    //     USAGE (Generic Indicator)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
    0x95, 0x01,                    //     REPORT_COUNT (1)
    0x75, 0x01,                    //     REPORT_SIZE (1)
    0x91, 0x02,                    //     OUTPUT (Data,Var,Abs)
    0x95, 0x01,                    //     REPORT_COUNT (1)
    0x75, 0x07,                    //     REPORT_SIZE (7)
    0x91, 0x03,                    //     OUTPUT (Cnst,Var,Abs)
    0xc0,                          //   END_COLLECTION


	/*Dump SpaceNAv */
0x06, 0x00, 0xff, 0x09, 0x01, 0xa1, 0x02, 0x15, 0x80, 0x25, 0x7f, 0x75, 
0x08, 0x09, 0x3a, 0xa1, 0x02, 0x85, 0x05, 0x09, 0x20, 0x95, 0x01, 0xb1, 
0x02, 0xc0, 0xa1, 0x02, 0x85, 0x06, 0x09, 0x21, 0x95, 0x01, 0xb1, 0x02, 
0xc0, 0xa1, 0x02, 0x85, 0x07, 0x09, 0x22, 0x95, 0x01, 0xb1, 0x02, 0xc0, 
0xa1, 0x02, 0x85, 0x08, 0x09, 0x23, 0x95, 0x07, 0xb1, 0x02, 0xc0, 0xa1, 
0x02, 0x85, 0x09, 0x09, 0x24, 0x95, 0x07, 0xb1, 0x02, 0xc0, 0xa1, 0x02, 
0x85, 0x0a, 0x09, 0x25, 0x95, 0x07, 0xb1, 0x02, 0xc0, 0xa1, 0x02, 0x85, 
0x0b, 0x09, 0x26, 0x95, 0x01, 0xb1, 0x02, 0xc0, 0xc0, 0xc0,

};



NTSTATUS RBC9_hid_start(IN PDEVICE_OBJECT pDev_obj)
{
	PDEVICE_EXTENSION	pDevExt = GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj);


	DBG_ENTER(("%p %d", pDev_obj,sizeof(My_hid_report_descriptor_base)));

	/* initialize hid part */

	pDevExt->Report_Descriptor_Size = sizeof(My_hid_report_descriptor_base)+pDevExt->Size_HID_Report_Space;
	pDevExt->Reset_Input = TRUE;

	RBC9_nav_init(pDev_obj);
	RBC9_nav_led(pDev_obj,TRUE);

	/* start hid worker */
	{
		HANDLE thread;
		NTSTATUS Status;

		KeInitializeEvent(&pDevExt->hid_worker_wakeup_event,SynchronizationEvent, FALSE);

		/* create thread and start */
		pDevExt->hid_worker_thread_obj = NULL;
		Status = PsCreateSystemThread(&thread,
			THREAD_ALL_ACCESS, NULL, NULL, NULL,
			RBC9_hid_worker, pDev_obj);
		if(! NT_SUCCESS(Status)) {
			DBG_LEAVE(("%x: PsCreateSystemThread failed", Status));
			return Status;
		}

		ObReferenceObjectByHandle(thread, THREAD_ALL_ACCESS, NULL, KernelMode,
			&pDevExt->hid_worker_thread_obj, NULL);
	}




	DBG_LEAVE(("STATUS_SUCCESS"));
	return STATUS_SUCCESS;
}

NTSTATUS RBC9_hid_remove(IN PDEVICE_OBJECT pDev_obj)
{
	PDEVICE_EXTENSION	pDevExt = GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj);

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

	/* wait for hid worker stop */
	if(pDevExt->hid_worker_thread_obj != NULL) {
		KeSetEvent(&pDevExt->hid_worker_wakeup_event, IO_NO_INCREMENT, FALSE);
		KeWaitForSingleObject(pDevExt->hid_worker_thread_obj,
			Executive, KernelMode, FALSE, NULL);

		ObDereferenceObject(pDevExt->hid_worker_thread_obj);
		pDevExt->hid_worker_thread_obj = NULL;
	}

	DBG_LEAVE(("STATUS_SUCCESS"));
	return STATUS_SUCCESS;
}

NTSTATUS RBC9_hid_get_rep_desc(IN PDEVICE_OBJECT pDev_obj, OUT UCHAR *buf, OUT ULONG *size)
{
	
	PDEVICE_EXTENSION	pDevExt = GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj);

	DBG_ENTER(("%p, %p, %d, %d", pDev_obj, buf, *size,sizeof(My_hid_report_descriptor_base)));

	if(*size < pDevExt->Report_Descriptor_Size ) {
		DBG_LEAVE(("STATUS_BUFFER_TOO_SMALL"));
		return STATUS_BUFFER_TOO_SMALL;
	}

	RtlCopyMemory(buf, My_hid_report_descriptor_base, sizeof(My_hid_report_descriptor_base) );
	RtlCopyMemory(buf +sizeof(My_hid_report_descriptor_base) , pDevExt->HID_Report_Space, pDevExt->Size_HID_Report_Space );
	*size = pDevExt->Report_Descriptor_Size ;

	DBG_LEAVE(("STATUS_SUCCESS: %u", *size));
	return STATUS_SUCCESS;
}

NTSTATUS RBC9_hid_read_rep(IN PDEVICE_OBJECT pDev_obj, OUT UCHAR *buf, OUT ULONG *size)
{

	PDEVICE_EXTENSION	pDevExt;
	void				*rep_buf;
	ULONG				rep_size, in_type;

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

	pDevExt = GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj);

	if(pDevExt->hid_input_changed & INPUT_TYPE_INFO) {
		in_type = INPUT_TYPE_INFO;
		rep_buf = &pDevExt->input_state.Info;
		rep_size = sizeof(pDevExt->input_state.Info);
	} 
	else if(pDevExt->hid_input_changed & INPUT_TYPE_NAV_ID1) {
		in_type = INPUT_TYPE_NAV_ID1;
		rep_buf = &pDevExt->input_state.SpaceNav.Trame[0];
		rep_size = sizeof(pDevExt->input_state.SpaceNav.Trame[0]);
	} 
	else if(pDevExt->hid_input_changed & INPUT_TYPE_NAV_ID2) {
		in_type = INPUT_TYPE_NAV_ID2;
		rep_buf = &pDevExt->input_state.SpaceNav.Trame[1];
		rep_size = sizeof(pDevExt->input_state.SpaceNav.Trame[1]);
	}
	else if(pDevExt->hid_input_changed & INPUT_TYPE_NAV_ID3) {
		in_type = INPUT_TYPE_NAV_ID3;
		rep_buf = &pDevExt->input_state.SpaceNav.Trame[2];
		rep_size = sizeof(pDevExt->input_state.SpaceNav.Trame[2]);
	} 
	else if(pDevExt->hid_input_changed & INPUT_TYPE_JOYSTICK) {
		/* joystick */
		in_type = INPUT_TYPE_JOYSTICK;
		rep_buf = &pDevExt->input_state.joy;
		rep_size = sizeof(pDevExt->input_state.joy);
	} 
	else if(pDevExt->hid_input_changed & INPUT_TYPE_KEYBOARD) {
		/* keyboard */
		in_type = INPUT_TYPE_KEYBOARD;
		rep_buf = &pDevExt->input_state.kbd;
		rep_size = sizeof(pDevExt->input_state.kbd);
	} else if(pDevExt->hid_input_changed & INPUT_TYPE_WWW) {
		/* wwww */
	//	DBG_OUT(("Balance www"));
		in_type = INPUT_TYPE_WWW;
		rep_buf = &pDevExt->input_state.www;
		rep_size = sizeof(pDevExt->input_state.www);
	} else if(pDevExt->hid_input_changed & INPUT_TYPE_MOUSE) {
		/* mouse */
		in_type = INPUT_TYPE_MOUSE;
		rep_buf = &pDevExt->input_state.mou;
		rep_size = sizeof(pDevExt->input_state.mou);
	} else {
		DBG_LEAVE(("STATUS_UNSUCCESSFUL: not changed: %x",
			pDevExt->hid_input_changed));
		return STATUS_UNSUCCESSFUL;
	}
	//DBG_OUT(("input type: %x/%x ",in_type, pDevExt->hid_input_changed));

	if(*size < rep_size) {
		DBG_LEAVE(("STATUS_BUFFER_TOO_SMALL: %d", *size));
		return STATUS_BUFFER_TOO_SMALL;
	}

	/* copy data */
	RtlCopyMemory(buf, rep_buf, rep_size);
	*size = rep_size;

	/* drop changed bit */
	pDevExt->hid_input_changed &= ~in_type;
	//  DBG_OUT(("input type (dropped): %x", pDevExt->hid_input_changed));

	//  DBG_LEAVE(("STATUS_SUCCESS"));
	return STATUS_SUCCESS;
}


NTSTATUS RBC9_hid_write_control(IN PDEVICE_OBJECT pDev_obj, PCONTROLLER_OUTPUT_REPORT out_state,ULONG *raw_size)
{
	NTSTATUS					Status;

	//DBG_ENTER(("%x %x",ReportId,InfoByte));

	switch(out_state->report_id) 
	{
	case 0x04:
		*raw_size=2;
		Status = RBC9_usb_put_control_transfer(pDev_obj, out_state, raw_size,NAV_PACKET_LED);
		break;
	default:
		NAV_PACKET_INIT[2] = out_state->report_id;
		Status = RBC9_usb_put_control_transfer(pDev_obj, out_state, raw_size,NAV_PACKET_INIT);
	}


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

	return Status;
}

NTSTATUS RBC9_hid_write_rep(IN PDEVICE_OBJECT pDev_obj,IN UCHAR *buf, IN ULONG *size, IN UCHAR id)
{
	PDEVICE_EXTENSION			pDevExt;
	CONTROLLER_OUTPUT_REPORT	out_state;
	NTSTATUS					Status;

//	DBG_ENTER(("%p, %p, %u, %u", pDev_obj, buf, *size, id));

	pDevExt = GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj);
	Status = STATUS_SUCCESS;

	if(*size > sizeof(out_state)) {
		*size = sizeof(out_state);
	}

	RtlZeroMemory(&out_state, sizeof(out_state));
	RtlCopyMemory(&out_state, buf, *size);

	DBG_OUT(("report_id 0x%x 0x%x", out_state.report_id,out_state.Cmd.command));

	if (out_state.report_id != REPORT_ID_CMD)
	{
		Status = RBC9_hid_write_control(pDev_obj,&out_state,size);
	}
	else
	{
		switch(out_state.Cmd.command) {
		
		case 0x01:
			// ask nav report
			DBG_OUT(("hid_input_changed"));
			pDevExt->hid_input_changed |= (INPUT_TYPE_NAV_ALL | INPUT_TYPE_INFO);
			break;
		case 0x02:
			// Reload layout
			DBG_OUT(("Reset_Input"));
			pDevExt->Reset_Input = TRUE;
			break;

		case 0x04:
			// Led
			DBG_OUT(("RBC9_nav_led"));
			Status = RBC9_nav_led(pDev_obj,(out_state.Cmd.InfoByte !=0) ? TRUE :FALSE);
			break;

		case 0x06:
			// Init
			DBG_OUT(("RBC9_nav_init"));
			Status = RBC9_nav_init(pDev_obj);
			break;

		case 0x07:
			// Init
			DBG_OUT(("RBC9_nav_calibrate"));
			Status = RBC9_nav_calibrate(pDev_obj);
			break;
		
		case 0x08:
			// Switch to profil num
			DBG_OUT(("Profil Num : %d", out_state.Cmd.ProfilNum));
			pDevExt->Profil_Actif = RBC9_Select_Profil(pDev_obj,out_state.Cmd.ProfilNum);
			break;

		case 0xFF:
			// Mode Config
			DBG_OUT(("ModeConfig  : %d",out_state.Cmd.InfoByte));
			pDevExt->ModeConfig = (out_state.Cmd.InfoByte !=0) ? TRUE :FALSE;
			if(!pDevExt->ModeConfig)
			{
				RBC9_SEQ_Special_Action_Fin(pDev_obj);
			}
			break;

		default:
			DBG_OUT(("unknown write command: 0x%x", out_state.Cmd.command));
			break;
		}
	
	}
	//DBG_LEAVE(("%x", Status));
	return Status;

}

NTSTATUS RBC9_nav_led(IN PDEVICE_OBJECT pDev_obj,BOOLEAN Led)
{
	//DBG_ENTER((" LED : %x", Led));
	CONTROLLER_OUTPUT_REPORT	out_state;
	ULONG size;

	out_state.report_id = 4;
	out_state.OriByte[0]=Led ? 1 : 0;
	size = 2;

	return RBC9_hid_write_control(pDev_obj,&out_state,&size);
}

NTSTATUS RBC9_nav_calibrate(IN PDEVICE_OBJECT pDev_obj)
{
	CONTROLLER_OUTPUT_REPORT	out_state;
	ULONG size;

	out_state.report_id = 7;
	out_state.OriByte[0]= 0;
	size = 2;

	return RBC9_hid_write_control(pDev_obj,&out_state,&size);
}

NTSTATUS RBC9_nav_init(IN PDEVICE_OBJECT pDev_obj)
{

	CONTROLLER_OUTPUT_REPORT	out_state;
	ULONG size;

	out_state.report_id = 6;
	out_state.OriByte[0]= 3;
	size = 2;

	return RBC9_hid_write_control(pDev_obj,&out_state,&size);
}


static VOID RBC9_hid_update_from_raw_input(IN PDEVICE_OBJECT pDev_obj)
{
	PDEVICE_EXTENSION		pDevExt;
	CONTROLLER_INPUT_REPORT in_state;
	ULONG					changed_flag;
	ULONGLONG				cur_time;

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

	pDevExt = GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj);

	in_state = pDevExt->input_state;

	/* process input data */
	RBC9_process_raw_input(pDev_obj);

	if (pDevExt->ModeConfig)
	{
		if (pDevExt->Led == NULL)
				{
					RBC9_SEQ_Special_Action(pDev_obj);
				}	
	}
	
	if (pDevExt->Led) RBC9_Led_Clignote(pDev_obj);

	/* check state change */
	changed_flag = 0;

	/* Info state */
	if(RtlCompareMemory(&pDevExt->input_state.Info, &in_state.Info,sizeof(in_state.Info)) != sizeof(in_state.Info)) 
	{
		changed_flag |= INPUT_TYPE_INFO;
	}

	/* NAVID1 state */
	if(RtlCompareMemory(&pDevExt->input_state.SpaceNav.Trame[0], &in_state.SpaceNav.Trame[0],sizeof(in_state.SpaceNav.Trame[0])) != sizeof(in_state.SpaceNav.Trame[0])) 
	{
		changed_flag |= INPUT_TYPE_NAV_ID1;
	}

	/* NAVID2 state */
	if(RtlCompareMemory(&pDevExt->input_state.SpaceNav.Trame[1], &in_state.SpaceNav.Trame[1],sizeof(in_state.SpaceNav.Trame[1])) != sizeof(in_state.SpaceNav.Trame[1])) 
	{
		changed_flag |= INPUT_TYPE_NAV_ID2;
	}

	/* NAVID3 state */
	if(RtlCompareMemory(&pDevExt->input_state.SpaceNav.Trame[2], &in_state.SpaceNav.Trame[2],sizeof(in_state.SpaceNav.Trame[2])) != sizeof(in_state.SpaceNav.Trame[2])) 
	{
		changed_flag |= INPUT_TYPE_NAV_ID3;
	}

	/* joystick state */
	if(RtlCompareMemory(&pDevExt->input_state.joy, &in_state.joy,sizeof(in_state.joy)) != sizeof(in_state.joy)) 
	{
		//DBG_OUT(("joystick changed"));
		changed_flag |= INPUT_TYPE_JOYSTICK;
	}

	/* keyboard state */
	if(RtlCompareMemory(&pDevExt->input_state.kbd, &in_state.kbd, sizeof(in_state.kbd)) != sizeof(in_state.kbd)) 
	{
		//DBG_OUT(("keyboard changed"));
		changed_flag |= INPUT_TYPE_KEYBOARD;
	}

	/* www state */
	if(RtlCompareMemory(&pDevExt->input_state.www, &in_state.www, sizeof(in_state.www)) != sizeof(in_state.www)) 
	{
	//	DBG_OUT(("www changed"));
		changed_flag |= INPUT_TYPE_WWW;
	}


	/* mouse state */
	cur_time = KeQueryInterruptTime();

	if(cur_time >= (pDevExt->Mouse.wheel_time  + (pDevExt->Mouse.wheel_wait * 10 * 1000)))
	{
		pDevExt->input_state.mou.wheel = pDevExt->Mouse.wheel_val;
	}
	else
	{
		pDevExt->input_state.mou.wheel=0;
	}

	if(pDevExt->input_state.mou.x != 0 ||
		pDevExt->input_state.mou.y != 0 ||
		pDevExt->input_state.mou.wheel != 0 ||
		RtlCompareMemory(&pDevExt->input_state.mou.button, &in_state.mou.button,sizeof(in_state.mou.button)) != sizeof(in_state.mou.button)) 
	{
		if(cur_time >= pDevExt->Mouse.next_time) { /* only for each 10ms */
			//DBG_OUT(("mouse changed"));
			changed_flag |= INPUT_TYPE_MOUSE;
			pDevExt->Mouse.next_time = cur_time + 10 * 10 * 1000;
			if (pDevExt->input_state.mou.wheel != 0)
			{
				pDevExt->Mouse.wheel_time = cur_time;
			}
		}
	}

	/* save current state */
	pDevExt->hid_input_changed |= changed_flag;

	/* notify to ioctl layer */
	if(pDevExt->hid_input_changed) {
		RBC9_notify_input_report_change(pDev_obj);
	}


	//DBG_LEAVE(("void"));
}


static VOID RBC9_hid_generate_raw_output(PCONTROLLER_OUTPUT_REPORT out_state,PRAW_OUTPUT raw_out)
{
	int i;

	DBG_ENTER(("%p, %p", out_state, raw_out));

	//raw_out->ReportId = 4;
	//raw_out->OutPut[0] = out_state->Value[0];

	DBG_LEAVE(("void"));
}

#pragma warning(disable : 4996)
#pragma PAGEDCODE
static VOID RBC9_hid_worker(IN PVOID ctxt)
{
	PDEVICE_OBJECT		pDev_obj;
	PDEVICE_EXTENSION	pDevExt;
	LARGE_INTEGER		time;

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

	pDev_obj = (PDEVICE_OBJECT)ctxt;
	pDevExt = GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj);

	KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY);

	while(pDevExt->Pnp_State == DEVICE_STATE_STARTED) {
		if(pDevExt->Reset_Input) 
		{
			/* reload setting */
			RBC9_InitState(pDev_obj);
			pDevExt->Reset_Input = FALSE;
		}

		KeResetEvent(&pDevExt->hid_worker_wakeup_event);

		/* update input report data */
		RBC9_hid_update_from_raw_input(pDev_obj);

		/* wait for wakeup or 10ms */
		time = RtlConvertLongToLargeInteger(-10 * 1000 * 10);
		KeWaitForSingleObject(&pDevExt->hid_worker_wakeup_event, Executive,	KernelMode, FALSE, &time);

		//DBG_OUT(("RBC9_hid_worker ReportId %x",pDevExt->hwInData.ReportId));
	}

	DBG_OUT(("already stop/removed"));
	RBC9_notify_input_report_stop(pDev_obj);

	DBG_LEAVE(("STATUS_SUCCESS"));
	PsTerminateSystemThread(STATUS_SUCCESS);
}
